I'm not sure I understand the blinkingLight example in the simulations folder. The line
self.blinkingLight.mark (not self.blinkingLight, not self.blinkingTimer) seems to mean: flip the state of the marker if the timer equals zero (= False). However, can we be sure that the timer equals exactly zero after resetting? The evaluation of intervening statements is bound to take some time, too. The HowTo document talks about "the necessity to use a oneshot if you want a timer to trigger certain events" (p 28). So, the code should perhaps be like this:
self.pulse.trigger (self.blinkingTimer > 3) self.blinkingLight.mark (not self.blinkingLight, self.pulse) Is that correct? However, this does not work, at least not on my system (Windows 7 Enterprise, Python 3.6.0, SimPyLC 3.5.1). The light stays dark. This does work:
self.blinkingLight.mark (self.blinkingLight == False, self.pulse) This flips the marker every time the pulse is triggered. So, the not operator seems to be broken. Can anyone confirm this?
Lastly, if I understand correctly, the simplest way of getting a blinking light would be:
self.blinkingLight.mark (True, self.timer > 1, False) self.timer.reset (self.timer > 2) Is that correct? But perhaps the example has been designed to showcase most of the elements available in SimPyLC.
SimPyLC, or in fact any PLC works with discrete time steps. So the time will be the exactly the same in the whole sweep (cycle). This means that if the timer is reset, it will stay zero from that point to exactly the end of the sweep, which in this case suits the purpose.
While it's not needed here, a oneshot can be useful if the reaction on the timer being 0 has to take place EARLIER in the sweep. This is because a oneshot stays True exactly one cycle, so not until the end of the sweep, but exactly upto the point where it was triggered. If say it was triggered in line 20 of the code of your sweep, it will stay True exactly until execution is again at line 20. So every circuit is guaranteed to see the oneshot being True EXACTLY ONCE.
When running your code on an actual Arduino rather than only on the simulator, the use of oneshots is more often required, due to limititations on the Arduino's timer resolution. From the SimPyLCHowTo that's in the distro:
Edge triggering for buttons and timers
Less clear is the necessity to use a oneshot if you want a timer to trigger certain events. Suppose you want to increment a counter every 3 seconds. The simplest thing would be to use a timer, reset it to 0 after 3 seconds and increment the counter whenever the timer is 0. On the simulator this would work fine. However, SimPyLC timers running on the Arduino have a resolution of 1 ms, which is more than the sweeptime. This means that a timer can remain 0 for multiple sweeps, e.g. incrementing your counter several times. Using a oneshot to edge trigger on your counter becoming 0 will solve this problem.
Remark: The resolution of timers on the Arduino has been improved, but the problem may still occur, so using oneshots in this case remains a good idea.
Concerning the light staying dark: Turns out that there's a subtle difference between Python 2.7 and 3.5, namely the __nonzero__ magic word being replaced by the __bool__ magic word. I've adapted this in engine.py so the blinkinglight works fine now. This fix is in SymPyLC 3.5.3.