I have seen so many flows/solutions/subflows that aim to bring semaphore features to a flow, but.... they all have 1 or more things in common.
They are no longer maintained (and therefore have undesirable bugs)
Involves using nodes that are not really designed for managing access to a resource (therefore become messy)
don't have some form of fail-safe in mind.
I needed all of this!
Introducing node-red-semaphore-plus
Uh... what is it
This is a set of Nodes for Node RED, that allows messages to be queued, and signalled to pass, once a message has reached the end of the flow.
But at the same time, allowing an optional fail-safe, that can be controlled dynamically.
Node
What's it for?
Semaphore Take
Queues and passes a message through when signalled (or when the optional fail-safe triggers)
Semaphore Release
Raises the signal to allow a message to pass
Semaphore FS Reset
Alters the current fail-safe time
The fail-safe time is set at Node level (Semaphore Take, Semaphore FS Reset),
But! You can also pass in a msg property of sp_timeout and the nodes will ignore what is set at Node level, and use this value instead
Setting the fail-safe to 0 (zero) will disable the fail-safe, meaning Semaphore Release will be the only node that can release the lock
If nothing has already taken the lock, the message will of course pass through, and subsequently start queuing other messages, where each one is allowed to pass on the signal or fail-safe trigger
Messages are released with a property of sp_isFailsafe, and this lets you know if the release was due to a fail-safe condition
Thank you for this. I expected a greater response to your post, but I think many of us will find your nodes very useful.
Just a few suggestions for possible improvements:
Improve the documentation. The .html file should include text that appears when one of the nodes is selected in the palette and the help pane in the sidebar. In particular, the node status should be described. The meanings of GQ and LQ are not obvious. LFS presumably refers to the fail-safe, but what does the L mean?
Provide a reset command that deletes the queue and restores the semaphore. This may not be important in operation but could help in development and debugging.
I think there should be an option to limit the size of the queue. This could be done in the node configuration or by having the node use the nodeMessageBufferMaxLength parameter defined in settings.js.
I fully agree with the documentation, I developed this to solve some specific problems in work, that needed addressing quickly, so may have cut corners on the docs - I’ll get them improved on the next update.
LFS - Last Fail Safe time that was used QG - Global Queue size LQ - Local Queue size
I also like the suggestions with the queue size and purge commands, Watch this space
The nodes do indeed employ done() so should work with the complete node, as for catch - nothing is actively handled presently.
It's extremely funny you mentioned a 2nd output, as I have been teasing this idea, and how best to utilise a 2nd output - some room for some handy processing here.
As for a limit - this will be implemented, and may Evan employ the 2nd output pin somehow, almost like an overflow drain
Based on the feedback thus far.
I think I have come up with something that could work really well.
Option will be:
msg.smp_avoidQueue
Never
Threshold
Example: If the flow is busy (lock engaged), but you need to allow a message to pass, you would set the Avoidance Mode accordingly, and attach a message part of msg.smp_avoidQueue: true, the message will then pass through Pin 2 avoiding the semaphore queue.
With Threshold its the same, but only if the queue is at the Threshold (no need for smp_avoidQueue)
For control type messaging - the 2 options I explore before resorting to a magic prop is
Use a topic and payload arrangement for control messages
e.g. { topic: 'smp:avoid-queue', payload: 'never' }
this has the benefit of being both explicit AND everyone (the programmer, we the user) know and expect a control message to NOT be considered a real message that needs to be processed / passed on.
TypeInput - this makes it immediately clear to the user which msg prop is used (and have the freedom to change it)
I might play with using typed inputs, as using a single message to alter its behaviour, will alter it for all messages, I want to allow 'priority' messages to be given a free pass, hence the magic prop - but I will move that to a user definable typed input.