LabVIEW

cancel
Showing results for 
Search instead for 
Did you mean: 

Tips to remember when using Queued State machine

Solved!
Go to solution

@johnsold wrote:

VeeJay,

 

The timing functions tend to run independently.  So short times may do nothing.  The Wait Until Next ms Multiple is sometimes misunderstood with results which are not what the programmer intended.

 

Let me discuss your upper loop (with serial port read) as an example.  The Read takes about 2.1 ms per byte at 4800 baud.  The function is smart enough to wait for that byte to arrive without tying up the CPU. If a byte a happens to already be there when the Read is called it will return immediately.  The delay here may be negligible or up to 2.1 ms.  The Dequeue has a 2 ms timeout. Like the read it will return as soon as an element is ready to dequeue or after the timeout: 0 to 2 ms.  TDML_split_control_and_data.vi reads 4 bytes in some cases and does nothing in others.  Delay: 0 to 8.4 ms.  Wait Until Next ms Multiple with 10 ms wait. This will wait until the Tick count is a multiple of the input value.  The Wait on Notification has no timeout so it uses the default (-1) or wait forever (= never timeout). The way you had the middle loop written a notification will be received at intervals no longer than 10 ms.

 

I guess my assumption on Wait until 10ms multiple in every loop was that I wanted each loop to run iterations at 10ms per iteration no matter what. I understand why my loops tie up and your suggestion on noticing value on"i" terminal is surely an indicator to show which loop is stuck and wwhich one keeps moving.

 

Question? How did you come up with 2.1 ms per byte at 4800 baud? I get 4800 baud = 4800 bits/s = 600 bytes/sec or 1byte in 1.6ms. Just curious of the Math.

 

I guess what I took from the timing function especially the 10ms Wait multiple is that no matter what timeout deQ,notifier,READ etc has it should be below 10ms to enable the loop to keep iterating. Is this assumption correct? I think you have stated that, I am just confirming my understanding. In addition to your question few posts above as to why I have so many shift registers, this is precisely the point. I did not want to keep the WRITE VISA, enQ and Notify (in 2nd loop) waiting, hence,having a shift register will make sure every iteration there is a value for the function to run. Now I guess I will just try your suggestion coz it makes total sense.

 

Functionend time

Read 10 .. 2.1

Read 20 .. 8.4+2.1*

Dequeue0 .. 2

Wait Multiple0 .. 10**

Wait Notifiy0 .. 10

 

* Read 2 always runs after Read 1 so the maximum Read 1 end time is added.

** Actual time depends on tick count when called on the first iteration.

How long does the loop take? If data is available at the serial port before the loop starts and the notifier and queue have already been written and the tick counter is at a multiple of 10, the first iteration could complete in a few microseconds. Later iterations will be at least 10 ms long and may be >10.5 ms if 5 bytes are received after the loop starts. If the middle loop is delayed or blocked so that the Notification is not sent, the upper loop will also wait forever.

 

The above para answers my question regarding loop timing be limited to a max of 10ms which was what I was going for. Also, I think I have made sure that my second loop with the event structure too runs all the time without waiting forever. The 10ms timeout takes care of that. So I have clocked that loop too to run at 10ms.

 

Now, what can you do about this confused situation?  First, you cannot make this loop always run in 10 ms because the 5-byte reads may take longer than that. What is the probability that the remote device will stop sending data on the serial port while this program is running?  What are the consequencies when that happens? If this is unlikely and you do not care if the loop blocks until the VISA Read times out (default = 10 seconds), then use the Reads as the loop timing. Put zero timeouts on Dequeue and on Wait on Notification and remove the Wait Until Next ms Multiple.  This will make the loop spin as fast as data is recieved on the serial port. If you want all iterations to be closer to 10-11 ms, put an 8 ms Wait (ms) in the no-read case in the subVI.

 

A little background of the device that sends data through serial port and also expects feedback/ACKs. During startup, it pings a control code to establish. I am not sure how long it waits for an ACK because it is propreitery software, All I reverse engineered were the codes and the data that it sends. here is a pattern from start to establish connection and then for me to be able to enter into the software. If there is no communication at start, I get an error message from the device.During Startup, it sends control code and expects ACK. And then no bytes are sent.

 

When wwe press start, that's when there is constant communication. Start starts off with a default Speed and angle value. eg. A0 A3 30303137 A4 30313030 C0 C1 C2 C0 C1 C2 C0 C1 C2 etc..... A0 A3 A4 and C's are all control codes. Others are data. data just follows A3 and A4. All in Hexadecimal mode for simplicity. If I choose next stage, control A3 30303235 A4 30313230 C0 C1 C2 C0 C1 C2 etc.....

 

For the C's I send D's with feedback values in the same format as the device expects to display on its front panel.

 

The point I am trying to make is that whether the device sends control and data, it still pings for feedback, so there is always communication hence I can never have Loop 1 stuck at a particular iteration.

 

The VISA Read timeout sets an upper limit on how long the read will wait for the specified number of bytes or the termination character.

 

 (Oh!) You have termination character enabled and you are specifying the number of bytes to read!!! This may be part of the reason for the timeouts. Generally you use one method or the other to terminate a read but not both.  If your remote device sends a one byte control code followed by a line feed and then sends a 4-byte data set followed by a line feed, your code does this:

Reads 1 byte (command code). The subVI is set to read 4 bytes. The first byte it reads is the line feed following the command code. Because Termination Characters are Enabled, the read stops with no data available to be converted to angles and speeds.  On the next iteration of the loop ther Read 1 reads the first byte of the 4-byte data set.  Since this is not intended to be a control code, it will be misinterpretted by the subVI.  If the remote device is sending termination characters, set the Bytes to Read inputs to a larger number than the longest message and let the termination characters do their jobs.

 

Back to the timeout.  The timeout is intended to allow the program to recover gracefully if the remote device fails to send the expected data or the data is corrupted by noise.  The default timeout is 10 seconds.  If you know your device will always send data in much less time than that, you can set a shorter timeout.

 

I am not sure I understand Line feed. I will take the termination character out. Since, the device just sends data for 2 of the 5 control codes, I want to make sure that data is not misinterpretted as control. I would like to make sure READ VISA times out rather than define a default timeout of 10sec. My device sends control/feedback control everytime after I press start Test on the device. Before that, when I open the devicce, it pings for communication and just waits for START.

 

The problem that I have been having so far is that I haven't really thought these functions as exhibiting parallelism, hence not really programmed based on time. Step by step with your valued assistance, the timing concept which is actually really important is becoming more evident. I must confess at this time though that I am better than yesterday, but still have some lingering questions. Can't wait to get back tomorrow to try out these suggestions. Ever been so excited to go to wwork? 🙂 I so am 🙂

 

VJ


 

I may not be perfect, but I'm all I got!
0 Kudos
Message 31 of 75
(1,153 Views)

@VeeJay wrote:

@johnsold wrote:

VeeJay,

 

 

 

Question? How did you come up with 2.1 ms per byte at 4800 baud? I get 4800 baud = 4800 bits/s = 600 bytes/sec or 1byte in 1.6ms. Just curious of the Math.

 

In serial communication, a byte is not equal to 8 bits.  You have to factor in the start and stop bits.  Potentially a parity bit as well.  So depending on your serial settings, a byte is usually about 10 bits.  So 480 bytes/sec is a little over 2 msec per byte.

 

 In addition to your question few posts above as to why I have so many shift registers, this is precisely the point. I did not want to keep the WRITE VISA, enQ and Notify (in 2nd loop) waiting, hence, having a shift register will make sure every iteration there is a value for the function to run. Now I guess I will just try your suggestion because it makes total sense.

 

This statement does not make sense to me "having a shift register will make sure every iteration there is a value for the function to run".  A shift register returns values to the beginning of the next iteration.  I has nothing to to with creating values.

 

 

VJ


 

0 Kudos
Message 32 of 75
(1,144 Views)

@Ravens Fan wrote:

@VeeJay wrote:


 In addition to your question few posts above as to why I have so many shift registers, this is precisely the point. I did not want to keep the WRITE VISA, enQ and Notify (in 2nd loop) waiting, hence, having a shift register will make sure every iteration there is a value for the function to run. Now I guess I will just try your suggestion because it makes total sense.

 

This statement does not make sense to me "having a shift register will make sure every iteration there is a value for the function to run".  A shift register returns values to the beginning of the next iteration.  I has nothing to to with creating values.

 

 

 Thanks for clarifying the Math Raven!!

 

Regarding shift registers, maybe I will try explaining better. I will go from what you said. I want values for every iteration, be it new values or past values. Since I am "Q"ing data I want to make sure that the there is data to Q, so that my thrid loop has data to function correctly. Let me explain and please tell me where I am wrong.

 

for eg. to provide an analogy, what I am doing is Pressing a button constantly rather than fliping a switch ON and hope that it remains ON. When I do want to turn OFF , I stop pressing a button. I know this is an archaic means to do it but I couldn't figure out a better way so far.

 

Maybe I don't need a shift register for the STOP loops (EXIT event) the way I have programmed it but for the ACKnowledgement codes for my serial communication, I need to enQ ACK codes back to Loop 1 to WRITE on serial port, else my device shows (** **) since it didnt receive any feedback. Other than that, I just have shift registers for Q's and errors.

 

V

 

I may not be perfect, but I'm all I got!
0 Kudos
Message 33 of 75
(1,141 Views)


Looking at your program (without fully understanding it), I would probably make significant changes to the middle loop.  A loop with an event structure set up to handle user inputs does not need to run every 10 ms.  Unless your users can push buttons 100 times per second (and the OS can keep up)! I would probably iterate that loop only when an event occurs.  BUT this means that your Send Notification and Enqueues will no longer be happening every 10 ms. So that means that the other loops need changes as well.  For example I would only send the Stop notification when the Stop button was pressed.  The Wait on Notification functions in the other loops would need appropriate timeouts and the "timed out?" outputs would need to be tested to see if a valid notification had been received.

 

Lynn


Hi Lynn,

 

I was reading through all your comments and compiling your suggestions. I believe I did not answer your above comment/question.

 

Since my event structure is a combination of User events and events generated by the program itself, I set it to run at 10ms so that my device would not err. If you take a look at the code, most of the events generated are from "Control code" control because my device constantly pings for information and supplies information when needed. I will thusly name my "control code" control as "command code" for ease in understanding. Most user events are like minimizing window to tray or choosing some run time panel options to run some procedures. Predominantly, I expect control code to change and generate events and that I use to run my 3rd loop

 

V

I may not be perfect, but I'm all I got!
0 Kudos
Message 34 of 75
(1,137 Views)

@tbob wrote:

@VeeJay wrote:

Also, I would like some guidance in using shift registers for queues. Sometimes, I see code where shift registers are used for queue name and error, in the enQ loop and not  in the deQ loop. Why is it? Is that the right convention?


Whenever using a loop, especially a For Loop, there is a chance that the loop will iterate 0 times.  Lets say there is an array going into the loop with indexing enabled.  Lets say the array accidentally is empty.  The loop will iterate 0 times.  Whenever this happens, if you use regular tunnels for the queue name, the queue name goes in, but since the loop did not execute, some default value (probably NULL) comes out.  So when you try to close the queue name, you will get an error.

 

If you don't use an array to trigger the number of loops, this point is pointless.  But it is a good habit to make to always use shift registers for such things as queue names, VISA handles, etc.  As for error in and out, if you use shift registers, any errors that occur in one iteration carries over to the next.  With tunnels, this won't happen.  Whatever comes into the loop will always be used for the next iteration.  You will lose any errors that occur inside the loop, except for the last iteration.

 

I hope this clears up the shift registery mystery.  I always use shift registers, even for While Loops, just to form and keep a good habit.


Hi tbob,

 

When using deQ element in Consumer loop, should that also be shift registered? 🙂 Or having shift registers only in producer would suffice? I prefer to terminate/release loops at deQ end, hence would it make sense to use shift registers there or would shift registers at both place be redundant but helpful?

 

V

I may not be perfect, but I'm all I got!
0 Kudos
Message 35 of 75
(1,137 Views)

VeeJay,

 

Raven's Fan cleared up the data rate math.

 

Timing: Yes. You want to have only one thing in each loop controlling the timing.  If there are other fucntions with waits or timeouts they should be significantly shorter than the controlling timer.  Zero is a permitted value for timeouts but may not be appropriate in some cases.

 

You seem to have cleaned up the shift registers.  My earlier comment was about data which never changed inside the loop.  For such data a regular tunnel is sufficient.  The same data will always be there for use inside the loop on each iteration.

 

You need to send an ACK code within some unknown by relatively small number of milliseconds after receiving a control code starting with C.  Is that correct?  It looks like you send the previously received data back (one command cycle later). Do you ever send anything to the remote device which you generate either manually or automatically other than echoing its data? Given the requirements of the remote device for a prompt response, I would probably develop a communication module which receives the data stream and sends the automatic responses.  It would also decode the data received from the device and pass that information to the main program via the queue.  If you do need to send arbitrary data, pass it to the module via a second queue.  All reads and writes to the serial port would be in this module.  In some ways it is similar to the upper loop you now have.  The differences are that the automatic responses would be generated locally, that it operates as fast as data is received, that it does write any data to front panel indicators, and that it cannot be blocked waiting for anything other than the serial port.

 

The part about the line feed probably confused you because your device apparently does not send them. The example was to show how it would work if the data looked like \A0\n\A3\30\30\31\37\n...  Since you have disabled the tremination character, you cna disregard all that.

 

It is fun to be excited to go to work!

 

Lynn

0 Kudos
Message 36 of 75
(1,125 Views)

You need to send an ACK code within some unknown by relatively small number of milliseconds after receiving a control code starting with C.  Is that correct?  It looks like you send the previously received data back (one command cycle later). Do you ever send anything to the remote device which you generate either manually or automatically other than echoing its data? Given the requirements of the remote device for a prompt response, I would probably develop a communication module which receives the data stream and sends the automatic responses.  It would also decode the data received from the device and pass that information to the main program via the queue.  If you do need to send arbitrary data, pass it to the module via a second queue.  All reads and writes to the serial port would be in this module.  In some ways it is similar to the upper loop you now have.  The differences are that the automatic responses would be generated locally, that it operates as fast as data is received, that it does write any data to front panel indicators, and that it cannot be blocked waiting for anything other than the serial port.

 

IT is such a coincidence that I receive your response just when I decided to do exactly that :). Having the commands and their responses in one loop like the upper loop will help immensely with the plethora of timing issues I have been facing since the morning. I think trying to do this one stop layover technique making the 2nd loop the middle man in both directions is causing too many problems. Here is what I intend to do. I will post the code tonight.

 

Top loop: RS232 read and write. The whole control code CASE structure will be in the upper loop and I will have a Q for Start,Stop,TestEnd send values directly to Lower loop rather than through the Middle loop

 

Middle loop: This will now primarily be used for USER interface stuff as shown except that it will no longer generate events for COMMAND Codes

 

Bottom loop: The CONSUMER SM. This will receive data from Top and middle loops to run the way I need it to be.

 

Effectively, it has become a PC architecture and a classic one in that. I see in my head clearly how to lay things out. 🙂 I would still need help sorting out timing issues because that is HURTING my brain so much 🙂

 

With respect to COMMAND CODES. A's and C's are command codes and B's and D's are ACK codes. A3 and A4 send data with them (4 bytes) similarly C1 and C2 expect ACKs D1 and D2 along with 4 bytes of feedback. B's are ACK for A's (what I call control codes because their receipt means an action is performed) and D's are ACKs for C's (what I call Feedback status codes because they just need status od my machine)

 

The part about the line feed probably confused you because your device apparently does not send them. The example was to show how it would work if the data looked like \A0\n\A3\30\30\31\37\n...  Since you have disabled the tremination character, you cna disregard all that.

 

This clarifies it for me. effective line feed means bytes are sent column wise?

 

It is fun to be excited to go to work!

 

Lynn


VJ

I may not be perfect, but I'm all I got!
0 Kudos
Message 37 of 75
(1,118 Views)

I will be away from the Forum tonight.

 

What you are describing now sounds much cleaner.

 

In  most situations I prefer to separate the communications from the analysis.  However in your case the analysis (decoding the commands and determining the appropriate ACK code) is simple and directly relevant to the communications so including it there makes sense.

 

Lynn

0 Kudos
Message 38 of 75
(1,115 Views)

Quick question before we head for the weekend! Without shift registers, how do I maintain values until they are changed ? For Eg. If START=TRUE in Case 160, it has to be TRUE until Case 162 is encountered. But what's happening is that when other cases are being run, START goes back to FALSE and I have erratic behavior. Even for Speed and angle values, I get 0's in between rather than it remaining at say 2 and 10 until the device changes it.

 

When I use shift registers and monitor values with probe in producer, the values remain the same until the device changes it.

In consumer SM, when I deQ , there are 0s in between i.e. what I enQ is not deQing the same values

I may not be perfect, but I'm all I got!
0 Kudos
Message 39 of 75
(1,112 Views)

@VeeJay wrote:


Hi tbob,

 

When using deQ element in Consumer loop, should that also be shift registered? 🙂 Or having shift registers only in producer would suffice? I prefer to terminate/release loops at deQ end, hence would it make sense to use shift registers there or would shift registers at both place be redundant but helpful?

 

V


The main purpose of shift registers is to propogate data that has or might change from one loop iteration to the next.  However, a side use of shift registers is to pass the first input to the output if the loop iterates zero times.  I've explained this before.  If there is a chance that the loop will not run even one iteration, use a shift register to pass references and errors.  If in doubt, it doesn't hurt to use the shift register. 

 

I use them regularly just to form a habit.  I have run across a situation where an array input with indexing enabled determines how many times the loop will iterate.  Because of a programming error, the array was empty.  Had I not used a shift register for the queue reference, the output would have been "NULL", causing the subsequent Queue Destroy to fail.  Because I used a shift register, the Destroy worked. 

 

It is really up to you if you want to form a good habit.  Many people don't use them because they deem it unnecessary.  I would rather be safe than sorry, so I use them for every reference going into a For Loop.  While loops usually iterate at least once, so they are not necessary there.  Also not necessary for For Loops that don't have an array input with indexing enabled or have a constant wired to the N terminal.  Even a variable wired to the N has a potential of being zero, so use the shift registers here. 

 

In short, it is a good habit to always use shift registers with For Loops.


 

- tbob

Inventor of the WORM Global
0 Kudos
Message 40 of 75
(1,105 Views)