LabVIEW

cancel
Showing results for 
Search instead for 
Did you mean: 

Visualise and log data at the same time using TDMS?

Solved!
Go to solution

@rwunderl wrote:

I don't like state machines because of the incessant polling for no reason.


State machines have nothing to do with polling. I create and use state machines all the time that change states based on user events, queue elements received, Actor Framework messages, etc. No polling required.

0 Kudos
Message 11 of 18
(673 Views)

Of course there are mechanisms to change states based on some asynchronous event without polling. I suppose I mean that most state machines I have seen other people code involve polling and delays. When I use state machines, they are usually an outer layer of an application to drive startup/shutdown/reset behaviors. The business logic is usually based on queues and events. If the task is very well defined and finite, then a state machine is a good option to keep things predictable, reliable, and well documented.

_______________________________________________________________
"Computers are useless. They can only give you answers." - Pablo Picasso
0 Kudos
Message 12 of 18
(661 Views)

@Kevin_Price wrote:

I had something a little different in mind.  I took your snippet and ran with it a bit to illustrate what I was suggesting with actual code.   I use the actual Dequeue timeout value to pace the loop once it's time to start the regular readings.  And I show how to recalculate the timeout after completing each read query in case these queries consume a measurable amount of execution time.

 

 

-Kevin P


Ah I see ! I haven't thought of using the timeout option of the dequeue function!

Why are you doing this though?

VinnyLaTaupe_0-1595921190619.png

I mean the "Calc target for next read" and "Then calc msec until next read".

I've ran it for test, it works great but I don't get it 😅

 

Thanks for the tips!

0 Kudos
Message 13 of 18
(642 Views)

Hi rwunderl.

 


@rwunderl wrote:

Vinny,

 

Ok, I now understand better what you are trying to do. I still do not understand how you are correlating the current measurement with the velocity response from the motor controller. I think Kevin has explained a scheme to more accurately time the velocity readings, but now on to how to get the current reading.

For now I'm not yet ^^ That's still on my to do list.

Saddly this project is on hold for now until the next few weeks (I don't have access to the device) but I'm still building knowledge and prepping the software 🙂

 


@rwunderl wrote:

We can only presume that the velocity reported by the motor controller is "instantaneous" as of receiving the request, which we will also presume is sent instantaneously. It is at the moment of sending the request that you should log the current, as it is at this moment that the controller is copying the velocity value into its response message. The delays here are probably negligible since you are using SPI, but we'll try to be accurate anyway.


Yep, so the OBC is uploading the motor speed every 100ms (do not know the accuracy of this) in a register that I'm requesting to read via spi (every 100ms as well logically.). Given the communication frequency vs the speed update, the "read" section can be considered instantaneous.

 

So technically, if we want to be extra accurate (Which is not needed) I should be recording one sample of the Current consumption when the obc is loading the speed in the register, and not when I'm reading it, which could be anytime in the 100ms refresh rate frame. Here is a picture to illustrate what I mean:

VinnyLaTaupe_1-1595932807852.png

 


@rwunderl wrote:

One concept that comes to mind as we are talking about synchronization is events. Have you ever tried User Events? You could have 3 loops in your application: the user interface loop, the motor controller communications loop, and the data acquisition loop. The user interface loop would monitor buttons and other user interface items. The motor controller loop would be much like you and Kevin have outlined, except driven by an Event Structure with User Event handlers. Then the data acquisition loop would just be an Event Structure as well.

 

The motor controller and data loops would each handle only 2 user events: Stop and Acquire. The main loop could send the first Acquire user event when the user starts the run. From then on, you could use a scheme like Kevin's to send all subsequent Acquire events from the motor controller loop (handled by itself and the data loop). The timing could be a simple Wait Until Next Multiple in parallel with the comms; the Wait Until Next Multiple will hold the event case until the next 100-ms multiple to avoid drifting too much over time (a simple Wait would cause the acquisition to take longer than the saved data points would suggest over time). If comms take longer than 100 ms, it's probably an error anyway. The Acquire event would be fired after the Wait in the Acquire handler case (if there's no error). This will re-trigger the Acquire event handler in both loops simultaneously. The nice thing is there is no polling; every loop is completely idle until something of interest happens.

 

This is just off the top of my head, maybe it's a bad idea. Things can get much more complicated, but I like Event Structures because they keep things relatively simple. There are other synchronization methods too, like Occurrences, Notifiers, and the dreaded local variable polling. I don't like state machines because of the incessant polling for no reason. Just my 2¢.


So I'm using the QMH template for now (Have you seen the code I've uploaded above?), if I'm not wrong, the User events you're referring to is the same than the one in the template Firing the "Quit program" ?

This one:

VinnyLaTaupe_2-1595933292894.png

 

If that's so, do you mean that I should create another thread to fire an "acquire" event every 100ms ?

 

Best,

Vinny.

0 Kudos
Message 14 of 18
(628 Views)

Vinny,

 

Yes, that's a typical use of user events: using an event to stop parallel loops versus using "stop" local variable polling for example. This is nice because you can propagate that event to subVIs as well.

 

As a general concept, your data acquisition should be in a loop with nothing else. Then you use a queue to transport the data out of that acquisition loop in a lossless and ordered manner. The data can go to a processing loop or data saving loop (or both). So I'm generally suggesting having a separate loop per instrument or function of your application. This keeps things modular and flexible. What if you needed to add a noise meter to the setup? You could just add a loop for it and send the data sample to a new channel in your TDMS file.

 

You can add all of this to one QMH, but then you lose some synchronicity. It's always a trade-off. If you use events to synchronize things, you can even make the loops subVIs since you can send the stop event into them (as I noted above). Of course the onion requires one more layer if you want this to be a re-settable acquisition versus a one-shot application that quits when stopped.

 

I'm sorry I don't have the time to review your design more thoroughly, I thought it might be helpful to just share some thoughts on various architectures to help you get a sense of the possibilities.

 

I'm sure Kevin can expand on this if necessary, but to your question about what he is doing with the timing, he is taking the time of the previous sample, adding 0.1 s (100 ms) to it to set a "target" time for the next sample, then subtracting the time it took to actually get the current data sample. The remainder is the time that needs to be waited before acquiring the next sample. This is used as the timeout value, which delays the acquisition of the next sample unless a stop (or other) command is received first. The math looks a bit funny the way he did it; you could have just done some less-than-zero testing and limit checking in other ways, but his method is quite compact if a bit cryptic.

_______________________________________________________________
"Computers are useless. They can only give you answers." - Pablo Picasso
0 Kudos
Message 15 of 18
(623 Views)

@rwunderl wrote:

I'm sorry I don't have the time to review your design more thoroughly, I thought it might be helpful to just share some thoughts on various architectures to help you get a sense of the possibilities.

 


It is ! Thanks a lot for your time !

 


@rwunderl wrote:

I'm sure Kevin can expand on this if necessary, but to your question about what he is doing with the timing, he is taking the time of the previous sample, adding 0.1 s (100 ms) to it to set a "target" time for the next sample, then subtracting the time it took to actually get the current data sample. The remainder is the time that needs to be waited before acquiring the next sample. This is used as the timeout value, which delays the acquisition of the next sample unless a stop (or other) command is received first. The math looks a bit funny the way he did it; you could have just done some less-than-zero testing and limit checking in other ways, but his method is quite compact if a bit cryptic.


Ahhh is it because the Timestamp sent in, is actually the time stamp before the timeout happened, which is supposed to be 100ms, thus adding 100ms before acquiring?

0 Kudos
Message 16 of 18
(611 Views)

rwunderl already explained my thinking well, but just for clarity:

 


Ahhh is it because the Timestamp sent in, is actually the time stamp before the timeout happened, which is supposed to be 100ms, thus adding 100ms before acquiring?

No, the dataflow enforces a different sequence than that.  The first timestamp is taken right *after* Dequeue returns -- either with a valid message or a timeout.

 

Then in the "Read message" case, the first sequence frame has a comment to suggest acquiring/reading immediately, and then after that all finishes, get a new timestamp to see how much of the desired interval is left.  That becomes the new timeout value.

 

It's really all just an elaborate workaround that tries to enforce a periodic timeout rate.  This kind of behavior is exactly what "Wait Until Next ms Multiple" does naturally, which is why rwunderl has been recommending it.

 

His other suggestions about User Events could be a nice way to go, once you get used to them.  Two tips to help you along the learning curve:

 

1. Event Registration Refs are distinct from the Event Refs themselves.  You should have a separate instance of "Register for Events" before each Event Structure that's going to subscribe to an event.  (And a similar Unregister after leaving the loop it's in).

    You'll only create 1 "Shutdown" event for your app as a whole, but many different parallel loops will make separate and distinct registration refs for it.

 

2. You need to make sure sure you do all your event unregistration before destroying the User Event.

 

 

-Kevin P

CAUTION! New LabVIEW adopters -- it's too late for me, but you *can* save yourself. The new subscription policy for LabVIEW puts NI's hand in your wallet for the rest of your working life. Are you sure you're *that* dedicated to LabVIEW? (Summary of my reasons in this post, part of a voluminous thread of mostly complaints starting here).
0 Kudos
Message 17 of 18
(601 Views)

Thanks a lot for your help both of you.

 

I'll try to use the User Events more in the future as it sounds super interesting.

 

We've drifted a little bit from the initial subject and I have all the answers I could hope for, so thanks again 🙂

 

See you around.

Vinny.

0 Kudos
Message 18 of 18
(580 Views)