Summary
When a script is run in Periodically mode, OpenModSim re-executes the entire script file at every interval — not just a designated update function. As a result, any state held in top-level var declarations is reinitialized every cycle. Only code registered via Script.onInit(callback) is guaranteed to run exactly once; everything else (e.g. logic in a tick()-style function called at the bottom of the script) re-runs from scratch each period.
This is technically consistent with the documented behavior ("In Periodically mode, the script will start after a certain period of time until the user stops it or Script.stop() is called"), but the practical consequence — that local/global JS state does not persist across cycles — is not made explicit, and is easy to miss when writing stateful simulation logic (e.g. ramps, counters, latched flags).
Steps to reproduce
Load the bundled demo_plc_simulator example script (Help → Examples, or wherever it ships in the OpenModSim distribution).
Set the script's Run Mode to Periodically (e.g. 1000 ms).
Set the motor "Start" coil to trigger motorRunning = true and drive actualRpm toward a setpoint via incremental ramping inside the tick() function.
Observe actualRpm and the console log via a register read / log output.
Expected behavior
actualRpm ramps up incrementally across consecutive cycles toward the setpoint, since the variable is expected to retain its value between executions. The "Motor started" log line should print once, on the start transition.
Actual behavior
actualRpm resets to 0 at the start of every cycle, so it never progresses past the first increment step (stays at 50). The "Motor started" log line fires on every single cycle for as long as the Start coil is set, because the motorRunning flag itself is reset to its initial value before the gate is checked each cycle.
Root cause
The whole script body — including demo_plc_simulator's tick() function and its top-level var declarations (motorRunning, actualRpm, etc.) — is re-executed each period, so all initializers run again. Only callbacks passed to Script.onInit(...) are excluded from this re-execution (confirmed empirically: an init-only console.log in the same script fired exactly once across many periodic cycles).
Workaround used
Persist state in the Modbus registers themselves (server-side, not reset by script re-execution) and read it back via Server.readCoil / Server.readDiscrete / Server.readHolding / Server.readInput at the top of each tick() call, instead of relying on top-level JS variables.
Suggestion
Either: (a) clarify in the README/docs that only Script.onInit code is guaranteed to persist across Periodically-mode cycles, that all other top-level state — including in the shipped demo_plc_simulator example — is reinitialized each run, with a recommended register-readback pattern for stateful scripts; or (b) provide a documented in-engine mechanism for persisting arbitrary JS state across periodic cycles.
Environment
OpenModSim (sanny32/OpenModSim), bundled demo_plc_simulator example script
Scripting engine: Qt QJSEngine (per README)
Summary
When a script is run in Periodically mode, OpenModSim re-executes the entire script file at every interval — not just a designated update function. As a result, any state held in top-level var declarations is reinitialized every cycle. Only code registered via Script.onInit(callback) is guaranteed to run exactly once; everything else (e.g. logic in a tick()-style function called at the bottom of the script) re-runs from scratch each period.
This is technically consistent with the documented behavior ("In Periodically mode, the script will start after a certain period of time until the user stops it or Script.stop() is called"), but the practical consequence — that local/global JS state does not persist across cycles — is not made explicit, and is easy to miss when writing stateful simulation logic (e.g. ramps, counters, latched flags).
Steps to reproduce
Load the bundled demo_plc_simulator example script (Help → Examples, or wherever it ships in the OpenModSim distribution).
Set the script's Run Mode to Periodically (e.g. 1000 ms).
Set the motor "Start" coil to trigger motorRunning = true and drive actualRpm toward a setpoint via incremental ramping inside the tick() function.
Observe actualRpm and the console log via a register read / log output.
Expected behavior
actualRpm ramps up incrementally across consecutive cycles toward the setpoint, since the variable is expected to retain its value between executions. The "Motor started" log line should print once, on the start transition.
Actual behavior
actualRpm resets to 0 at the start of every cycle, so it never progresses past the first increment step (stays at 50). The "Motor started" log line fires on every single cycle for as long as the Start coil is set, because the motorRunning flag itself is reset to its initial value before the gate is checked each cycle.
Root cause
The whole script body — including demo_plc_simulator's tick() function and its top-level var declarations (motorRunning, actualRpm, etc.) — is re-executed each period, so all initializers run again. Only callbacks passed to Script.onInit(...) are excluded from this re-execution (confirmed empirically: an init-only console.log in the same script fired exactly once across many periodic cycles).
Workaround used
Persist state in the Modbus registers themselves (server-side, not reset by script re-execution) and read it back via Server.readCoil / Server.readDiscrete / Server.readHolding / Server.readInput at the top of each tick() call, instead of relying on top-level JS variables.
Suggestion
Either: (a) clarify in the README/docs that only Script.onInit code is guaranteed to persist across Periodically-mode cycles, that all other top-level state — including in the shipped demo_plc_simulator example — is reinitialized each run, with a recommended register-readback pattern for stateful scripts; or (b) provide a documented in-engine mechanism for persisting arbitrary JS state across periodic cycles.
Environment
OpenModSim (sanny32/OpenModSim), bundled demo_plc_simulator example script
Scripting engine: Qt QJSEngine (per README)