Custom Mcode message queue
-
The macro (below) implemets a "private" message queue. It's a multi-message buffer, not a FIFO queue.
Hopefully others find this post useful.
Background
In one of my programs / plugin (DuetLapse3), I originally started using M117 and later M291 / M292. The semantics were not right for my use-case and could cause undesirable side effects / interactions. This macro resolves those issues.Operation
The operation is as follows (assumes the macro is
/sys/M3291
) :- An array
global.DL3msg
stores the messages. The first element [0] is a sequence counter. - A string array
global.DL3Del
lists the indexes of messages that are to be deleted. - There are two reserved messages
M3291 B"Del"
andM291 B"Clear"
Adding a message
- The macro is called with a single parameter that is the message. e.g.
M3291 B"A Message"
- The message is added to the first empty(nul) element in
global.DL3msg
- The message sequence counter
global.DL3.msg[0]
is incremented
Reading Messages (typical use-case)
- The reciever reads
global.DL3msg
- If the sequence number
global.DL3msg[0]
has been incremented- Copies any non-null elements noting the index
- Place indexes into an array
- use the array to update the delete list e.g.
set global.DL3del = {2,5,6,}
- calls
M3291 B"Del"
to delete the now, read messages.
Deleting Messages
- The macro is called with
M3291 B"Del
- For each element in
global.DL3Del
the corresponding index inglobal.DL3msg
is set to null i.e. deletes the message global.DL3Del
is set to null i.e. no messages to be deleted
- For each element in
Clearing Messages
- The macro is called with `M3291 B"Clear"
- All messages in
global.DL3msg
are cleared. - The sequence number
global.DL3msg[0]
is retained.
- All messages in
M3291 macro
; A message passing queue for use by DuetLapse3 ; This is not used as a FIFO queue - its a multi-message buffer. ; The macro should be placed in /sys folder and given a name like ; M3921 (the default) ; ; If the name is changed e.g. to avoid a custom M code conflict (should always be M<something>) ; the -M3291 option of DuetLapse3 needs to be set to the new name ; ; Useage: ; M3921 B"<DuetLapse3 command message>" ; e.g. M3921 B"Duetlapse3.start" ; Can only take one message per call ; ; Special Useage: ; M3921 B"Clear" (Case Sensitive) ; Clears the queue and leaves the sequence number at last value. May be useful between print jobs ; ; M3921 B"Del" (Case Sensitive) ; Deletes items from the queue based on indexes held in global.DL3del ; ; DL3msg queue uses the following structures ; global.DL3msg[0] int - sequence number of last message added. ; global.DL3msg[x] string - the message to be processed ; ; global.DL3del string array - indexes in DL3msg to be deleted ; e.g. set global.DL3del = {1,3,7,} ; ; At the processing end, if global.DL3msg[0] is > last time checked: all messages are extracted in one go ; global.DL3del is set with a list of extracted messge indexes ; and then M321 P"Del" called. i.e. the recently read entries are cleared ; ; ################# MACRO STARTS HERE ################################# ; Version number of this macro var version = "Version 1.0" ; ; Make sure queue is initialized ; number of messages that can be held. 15 should be good for most cases var len_DL3msg = 15 if !exists(global.DL3msg) || global.DL3msg=null ;initialize the message queue global DL3msg = vector(var.len_DL3msg,null) ;beginning sequence number set global.DL3msg[0] = 0 if !exists(global.DL3del) || global.DL3del=null ; initialize the delete array global DL3del = null echo "DL3msg queue: initialized" echo var.version ; check if B parameter sent if exists(param.B) var Bparam = param.B if var.Bparam = "Clear" ; Clear the queue ; sets all message slots to null but leaves the sequence number as-is ; clear the delete array var sequence = global.DL3msg[0] set global.DL3msg = vector(var.len_DL3msg,null) set global.DL3msg[0] = var.sequence set global.DL3del = null echo "DL3msg queue: Cleared" elif var.Bparam = "Del" ; Delete (make null) selected messages from DL3msg queue ; This allows bulk deletes ; Items to be deleted are held in an array in global.DL3del if global.DL3del != null ; there are items to delete while true if iterations >= #global.DL3del break set global.DL3msg[global.DL3del[iterations]] = null echo "DL3msg queue: Items deleted - "^global.DL3del set global.DL3del = null else echo "DL3msg queue: Nothing to delete" else ; Add new message to the queue ; Iterate through and place message in any emply (null) slot ; If all the slots are full -- no more can be added var success = false while true if iterations >= var.len_DL3msg break if global.DL3msg[iterations] == null set global.DL3msg[0] = global.DL3msg[0] + 1 ; increment message count set global.DL3msg[iterations] = var.Bparam set var.success = true echo "DL3msg queue: Added - "^var.Bparam^" as item "^iterations break ; Check if message added OK if !var.success var errormsg = "DL3msg queue: Full" echo var.errormsg M291 P{var.errormsg} S0 else echo var.version echo "DL3msg queue: No B param passed"
- An array
-
@stuartofmt cool!, what are you using this for?
-
DuetLapse3 has the ability to accept commands at runtime and modify its behavior. For example, change certain options, start / stop the timelapse recording, invoke a local programs / scripts.
It periodically checks to see if there is some command that needs action. The M3291 macro provides a fairly failsafe and non-blocking way to communicate. It also does not have any side effects if DuetLapse3 is not running (blocking M291 messages were a bit of a pain).
For example (you'll be sorry you asked):
I usually have DuetLapse3 in standby mode (doing nothing). I also usually have it set to capture images on layer change and also every 'n' seconds.In my slicer's start gcode, after heating homing etc I issue a M3291 message that tells DuetLapse3 to start the timelapse. This avoids capturing images during the preample.
In my slicer's end gcode, I issue a M3291 message that tells DuetLapse3 to stop recording every 'n' seconds and to stop the timelapse. This avoids capturing images of non-printing moves (e.g. dropping the bed).
Another use might be to have different types of timelapse settings in macros that call M3291 messages. This way DuetLapse3 can be modified by calling the relevent macro (at the start of a print job). This avoids the need to modify the configuration file and restart DuetLapse3.
-
@stuartofmt thanks for the detail!
-
@stuartofmt said in Custom Mcode message queue:
DuetLapse3 has the ability to accept commands at runtime and modify its behavior. For example, change certain options, start / stop the timelapse recording, invoke a local programs / scripts.
It periodically checks to see if there is some command that needs action. The M3291 macro provides a fairly failsafe and non-blocking way to communicate. It also does not have any side effects if DuetLapse3 is not running (blocking M291 messages were a bit of a pain).
For example (you'll be sorry you asked):
I usually have DuetLapse3 in standby mode (doing nothing). I also usually have it set to capture images on layer change and also every 'n' seconds.In my slicer's start gcode, after heating homing etc I issue a M3291 message that tells DuetLapse3 to start the timelapse. This avoids capturing images during the preample.
In my slicer's end gcode, I issue a M3291 message that tells DuetLapse3 to stop recording every 'n' seconds and to stop the timelapse. This avoids capturing images of non-printing moves (e.g. dropping the bed).
Another use might be to have different types of timelapse settings in macros that call M3291 messages. This way DuetLapse3 can be modified by calling the relevent macro (at the start of a print job). This avoids the need to modify the configuration file and restart DuetLapse3.
hi @stuartofmt , am getting this error
-
@sankafola
You have cross posted this question. Lets keep the discussion in the other post since its not clear at this point if you even have a clean install.