Jump to content
dmurray

CRIO-style architecture for Raspberry Pi

Recommended Posts

I've been looking at implementing a CRio-style architecture for a Raspberry Pi 3, and I'd appreciate some feedback on what I currently have, and how I can improve it. My main aim is to get a solid architecture using mid-level techniques (say, CLD level) because most RPi users won't have much LabVIEW experience. Even CLD-level may be too much for a lot of people, but I think CLD level techniques are a minimum requirement for a decent architecture. 

I'm using the NI LabVIEW for CompactRIO Developer's Guide as a reference, although I don't have any embedded experience. Also, I use the Makerhib LINX library to interface with the RPi (available on VIPM). LINX running on the RPi is an embedded Linux application, although it's not real-time. Also, it's basically a headless application (you can't view FP's on a monitor connected to the RPi), although you can develop and run VIs in interactive mode i.e. view your front-panels on the development PC. The headless mode is ultimately how I want to use it anyway, with a TCP server on the RPi, and a TCP client on a PC/Laptop/etc. Another thing is that RPi running LINX only works on LabVIEW 2014. I'm using the Home edition as it gives access to the Control and Simulation package, plus MathScript (neither used in this architecture).

The system in this example is very simple as shown below. The current architecture I have is also shown. The RPi simply reads the temperature and Alert signal of a PmodTMP3 sensor in a Main Processing Loop, updates the UI via another loop using CVT data, and turns an LED on if the temperature is above a set limit. 

RPi Architecture 1.png

Front Panels.png

Some points on the server/RPi side:

  1. The 'Main Processing Loop' is a sequencer which reads and writes the signals as mentioned above, but it also has a queue so commands can be sent to the RPi. Commands in the example are 'Set Temp Limit' and 'Set Temp Hysteresis'. These commands are sent by the client and processed by the TCP Receiver, where events are triggered for the Event Handler to add the commands to the Main Loop queue.
  2.  I use CVT to pass relevant values between loops i.e. 'Main Processing Loop' -> 'TCP Send', and 'Main Processing Loop' -> 'UI Update'.
  3.  I use STM for TCP functionality, as I find the Connection Manager makes life a lot easier.
  4. The TCP Sender sends data to any connected clients, the data being the temperature and alert values.
  5. Because of the embedded nature, standard button press functionality in events is not available on the RPi side, so to shut down the code in interactive mode, I use a Stop button and event in the Main Processing Loop.
  6. I use a global Stop value to stop loops when I want to close the program. This seems to be used a lot in CRIO applications. Any better solutions?
  7.  I'd like to have the Main Processing Loop as a sub-vi so that I can tidy up the block diagram, but I can't work out how to do this (because of the Stop button used in the loop). As mentioned, standard events can't be used, and neither can references to FP controls (unless I'm missing something). Any suggestions on how to solve this? Same question for the UI Update loop. (Note it wouldn't be a problem in a headless application as I would have it free-running, and use commands from the client to shut-down the program). 

Server Architecture 2.png

Server Architecture 1.png

There's not much going on on the Client side:

  1.  I have a TCP Receiver which updates the temperature and alert signal values on the client UI.
  2.  I have an event handler which sends Temperature Limit and Hysteresis commands back to the client.

Client Architecture.png

So my general questions are, does anything really suck, any suggestions for improvement, etc. I also have questions on sharing the RPi connection resource so that I can have more than one processing loop (e.g. one for collecting data from the RPi, another for writing commands to the RPi), but I'll leave that for another thread.

I've attached the code, but there may be an issue with dependencies. As mentioned, the code uses STM, CVT and LINX add-ons. Anything else missing should not be a problem in looking at the architecture.
 

RPi Temperature System 1.zip

PmodTMP3.lvlib

Edited by dmurray
sp.

Share this post


Link to post
Share on other sites

Okay, so I cleaned things up a bit for RPi side. Basically just put the Stop functionality in its own loop and then was able to have the main loop as a sub-vi. I also wrapped the TCP functionality in a class so that I can re-use it on other projects. The BD is still a bit messy though.

A question on using a Global for stopping loops: in this case I assume that because the global is only written in one location (i.e. the event loop when I'm shutting down), and read in several loops, that this is a safe use case for a global? I think my other options are sing a LV2 style global (not much point as it's effectively the same thing?), or using a CVT value seeing as I already have that functionality in the code anyway.

Somewhat Cleaned-up BD.png

 

Share this post


Link to post
Share on other sites
1 hour ago, dmurray said:

A question on using a Global for stopping loops: in this case I assume that because the global is only written in one location (i.e. the event loop when I'm shutting down), and read in several loops, that this is a safe use case for a global? I think my other options are sing a LV2 style global (not much point as it's effectively the same thing?), or using a CVT value seeing as I already have that functionality in the code anyway.

I would in general prefer to use the event to every loop. For example you have a QMH and en event structure -- I would pick one. The issue with the global is now every function is coupled to that one instance of the stop global. Using an event (or a queue message or a notifier) means you can pass in any queue or event. Is it a big deal? No, I'm just saying what I'd prefer. 

The other issue with the global rather than a queue, notifier, or event is that the global is sort of implied to be an abort rather than a request to stop. The natural implementation is what you have in your polling loop -- wire the global directly to the stop. For the polling loop thats not a big deal, but for a more complex situation (talking to an FPGA for example) maybe you want to step through a few states first before you actually exit, which means you need to either (a) put all that logic outside of the loop you just aborted with the global or (b) check the state of the global, than trigger some internal state change which eventually stops the loop.

  • Like 1

Share this post


Link to post
Share on other sites

I know the title of your post is "CRIO-style architecture for Raspberry Pi" and briefly looking over the architecture I think it looks fine.  But have you or anyone tried this architecture on the Rpi:

Front Panel Publisher

What are the limitations of the Rpi LabVIEW runtime?  When I get some time I'd like to investigate more.

Share this post


Link to post
Share on other sites
1 hour ago, smithd said:

I would in general prefer to use the event to every loop. For example you have a QMH and en event structure -- I would pick one. The issue with the global is now every function is coupled to that one instance of the stop global. Using an event (or a queue message or a notifier) means you can pass in any queue or event. Is it a big deal? No, I'm just saying what I'd prefer. 

The other issue with the global rather than a queue, notifier, or event is that the global is sort of implied to be an abort rather than a request to stop. The natural implementation is what you have in your polling loop -- wire the global directly to the stop. For the polling loop thats not a big deal, but for a more complex situation (talking to an FPGA for example) maybe you want to step through a few states first before you actually exit, which means you need to either (a) put all that logic outside of the loop you just aborted with the global or (b) check the state of the global, than trigger some internal state change which eventually stops the loop.

So just to clarify, for a queue I just need to do this:

(1) Add another message to the QMH:

Queue1.png

(2) Handle the message in a loop as follows:

Queue2.png

 

Now on to events: I'm having some troubling implementing the event-style stop. I assumed I could use one event trigger ('Stop Main Loop' in my example) and have more than one event structure be registered for that event i.e. a one-to-many relationship. I did that in my code (one generated event, two event structures registered for the event) but sometimes only one event structure fires when the event is generated. Am I right that events are a one-to-many relationship?

 

 

Share this post


Link to post
Share on other sites
1 hour ago, bbean said:

I know the title of your post is "CRIO-style architecture for Raspberry Pi" and briefly looking over the architecture I think it looks fine.  But have you or anyone tried this architecture on the Rpi:

Front Panel Publisher

What are the limitations of the Rpi LabVIEW runtime?  When I get some time I'd like to investigate more.

I haven't tried that particular architecture, but I will now that I know about it (thanks!). Web Services support has been added to LINX-RPi; there's a basic example on MakerHub, so I see no reason that the code in your link shouldn't work. The only thing to watch out for is that LINX-RPi doesn't support Shared Variables, in case they (Network Shared Variables) are used in the code you linked to. I need to have a proper look through the code.

Regarding RPi and LabVIEW there are two approaches that I know of: MakerHub (LINX) and TSXperts. What I know about MakerHub-Linx:

  1. Doesn't support front panels running on a monitor attached to the RPi. You can either run code interactively with the front panel on your development PC, or use a deployed application and use TCP/Web Services/(other??) to interface with the RPi.
  2. Shared Variables aren't yet supported as mentioned. Not sure if that's much of an issue.
  3. GPIO support is somewhat limited. No PWM, can't use dedicated CS pins for SPI, can't programatically set pull-ups/downs, events, etc. I'm currently wrapping this library to improve the GPIO functionality, although I guess the MakerHub guys will do something similar eventually anyway.
  4. As I understand it, LINX is not meant to be used in commercial applications. It's really meant for hobbyists/makers/academia/etc. 
  5. The RPi won't have the determinism of a RT system, but again for the types of applications it's meant for that probably doesn't matter.
  6. RPi currently only runs on LV2014. But you can use the Home version which is very cheap (compared to the standard/pro versions), and you get the Control and Simulation package, and MathScript functionality as well.

Regarding the TSXperts version, I don't know much about it. There's a LAVA thread here. Main differences are it allows the front panel to be displayed on a monitor attached to the RPi, and it is a commercial application (both non-free and can be used in commercial apps, if it follows their Arduino model). I think it's almost ready to be released. I'll probably buy the cheaper version to evaluate it.

Share this post


Link to post
Share on other sites
6 hours ago, dmurray said:

Now on to events: I'm having some troubling implementing the event-style stop. I assumed I could use one event trigger ('Stop Main Loop' in my example) and have more than one event structure be registered for that event i.e. a one-to-many relationship. I did that in my code (one generated event, two event structures registered for the event) but sometimes only one event structure fires when the event is generated. Am I right that events are a one-to-many relationship?

Okay, had a re-watch of the Jack Dunaway presentation from NI Week a few years back, and realized I was splitting the event reg refnum wire incorrectly i.e. I was just branching it and wiring it to the two event handlers. Having two Reg Events functions solved the problem. The fine details in LabVIEW are always my downfall.... 

Edited by dmurray

Share this post


Link to post
Share on other sites
5 hours ago, dmurray said:

Okay, had a re-watch of the Jack Dunaway presentation from NI Week a few years back, and realized I was splitting the event reg refnum wire incorrectly i.e. I was just branching it and wiring it to the two event handlers. Having two Reg Events functions solved the problem. The fine details in LabVIEW are always my downfall.... 

Yep, register for events is basically "create receive queue" while create user even is "create send queue".

To clarify my point, it was more to say that while labview has a bunch of different communication mechanisms, I'd just pick one. User events are nice, you can find a decent number of threads after the improvements in 2013 where people say they've totally switched from queues, and I've done that on a few programs so far as well. So in general my suggestion would be to either change the user events out entirely for queues, or change the queues out entirely for events.

While there is absolutely nothing wrong with also using a stop message for the polling loop, for something simple like that I have zero objection to globals or locals. That loop is obviously not something you ever plan to reuse, so worrying that its tightly coupled to a global variable is...well not worth worrying about. That having been said, if you have a global stop event it does make things cleaner to use it.

  • Like 1

Share this post


Link to post
Share on other sites
6 hours ago, smithd said:

Yep, register for events is basically "create receive queue" while create user even is "create send queue".

To clarify my point, it was more to say that while labview has a bunch of different communication mechanisms, I'd just pick one. User events are nice, you can find a decent number of threads after the improvements in 2013 where people say they've totally switched from queues, and I've done that on a few programs so far as well. So in general my suggestion would be to either change the user events out entirely for queues, or change the queues out entirely for events.

While there is absolutely nothing wrong with also using a stop message for the polling loop, for something simple like that I have zero objection to globals or locals. That loop is obviously not something you ever plan to reuse, so worrying that its tightly coupled to a global variable is...well not worth worrying about. That having been said, if you have a global stop event it does make things cleaner to use it.

Yes, I was getting caught up a bit in stopping the loops cleanly, and wanted to experiment a bit anyway. As an exercise my plan now is to do one version completely using queues and another using events. That will push me along the learning curve. 

My worry is that things will get too complex though.  In the maker tradition, my plan would be to share this code with other users who generally won't have a lot of LabVIEW experience. Even implementing the TCP server as a class like I did here would be too much for most people. But also it's important that I don't do anything really bad in terms of coding. No point in passing bad habits along to others. 

Thanks for your help, it's much appreciated. I'm sure I'll have more questions as I go along.

Edited by dmurray

Share this post


Link to post
Share on other sites

Related to this architecture, I have a question on the best way to pass around Event Refnums and User events to different sub-vi's. Currently what I'm doing is passing the Reg Events around as an array, and the User Events as a cluster (Method 1 below). Is a better solution to make all the events as the same type (variant in a cluster) and pass around an array (Method 2 below)? Or is there something else I need to be doing, or watch out for? I'd like to iron out these basics with this small project before I try to build something substantial and run into unforeseen issues.

Method 1:

User events in cluster.png

Method 2:

User events - arrays.png

Share this post


Link to post
Share on other sites

You only want to pass around the events themselves, not the registration refnums. You should register the very last thing before you get to the event structure and unregister the very first thing after the event structure. Otherwise you have this queue floating in the ether that isn't being read from.

 

I personally make a "create" function for any process which generates a cluster of input events and a "subscribe" function which takes any clusters from any other processes and copies the events into a local 'state' cluster. That is, I move the events around only during initialization. This probably doesn't work well for something more dynamic, but...

  • Like 1

Share this post


Link to post
Share on other sites
2 hours ago, smithd said:

You only want to pass around the events themselves, not the registration refnums. You should register the very last thing before you get to the event structure and unregister the very first thing after the event structure. Otherwise you have this queue floating in the ether that isn't being read from.

I personally make a "create" function for any process which generates a cluster of input events and a "subscribe" function which takes any clusters from any other processes and copies the events into a local 'state' cluster. That is, I move the events around only during initialization. This probably doesn't work well for something more dynamic, but...

Register at destination... of course, seems obvious now that you say it. 

Your second point, I can't quite visualize. Can you post a simple screen shot? 

 

Share this post


Link to post
Share on other sites

An issue I've run into: I've been messing around with the events in this project, and at edit-time I've lost some information about the events. An example is shown below, where the event is <New Temperature Hysteresis>, but in the event I get the data for 'New Temperature Limit'. What have I screwed up, and how do I fix it?

Events issue.png 

Share this post


Link to post
Share on other sites

I expect New Temperature Hysteresis contains a numeric with a label of "New Temperature Limit", at least in the User Events control.

Share this post


Link to post
Share on other sites
3 hours ago, dmurray said:

An issue I've run into: I've been messing around with the events in this project, and at edit-time I've lost some information about the events. An example is shown below, where the event is <New Temperature Hysteresis>, but in the event I get the data for 'New Temperature Limit'. What have I screwed up, and how do I fix it?

This is the downside of the event system. I still like it, you just have to learn the fiddly bits to make it display nicely. I may be slightly off with these but:
-User event registrations, in the event structure, show the label of the event refnum as passed into the register for events node. Therefore, your cluster label is "New Temp..." and your event case is the same.
-Control registrations use the same principle, but with arrays of control refnums it totally goes bonkers. I've used a for loop and To More Generic with a VI server constant (this is automatically performed for you when you use a Build Array with different refnums, so throwing this in there is basically a no-op). In this case, the event case shows the label of the VI server constant used with the to more generic class function.
-The inner data node where it says "New Temperature Limit" is based on the label of the element inside the user event refnum as passed into the register for events. That is, your wire "New Temperature Hysteresis" contains a numeric called "New Temperature Limit", presumably because you copy-pasted. You can fix this in one of three ways:

  1. Right click on the user event refnum in your cluster and select show control. Then you can relabel the existing data.
  2. Open your cluster type def (because you absolutely typedef'd it) and drop down a new control of the type you want, in this case a numeric. Relabel this new control to "Blah". Drag and drop "blah" into your user event refnum and it will replace the prior contents, meaning you've just relabeled "New..." to "Blah".
  3. Go back to where you created the event, right click on the constant you used to create the event, change the label to "Blah". Then create a new constant/control off the create user event node. This new control should have the correct label.

I haven't personally used this, but the discussion is pertinent: 

 

Since I'm already here, its worth mentioning that if you rearrange the order of event registration or the label of the user event (not the contained data), you **must** verify the order of your event cases. The same is true if you bundle up multiple registrations. Changing the order or name of the event registration requires a quick code review. This is the one killer negative feature of the user event system. I still like user events despite these flaws, and I think as long as you know about them you're fine, but...

 

By the way, one neat thing I don't know a good use for is that you can unregister for selected events on a temporary basis:

http://zone.ni.com/reference/en-XX/help/371361K-01/lvhowto/dynamic_modifying_reg/

One thing I wanted to use it for would be as part of a state machine, you can literally stop listening for some events when you're in a state where they don't matter. But I decided it was easier and more readable to just put a case structure in the right places.

 

7 hours ago, dmurray said:

Your second point, I can't quite visualize. Can you post a simple screen shot? 

I don't have labview here so I'll try again. Its worth mentioning to be clear that this is just how I personally organize the code. I've never really swapped notes with anyone on this.

Imagine you have two loops. One reads the data from the sensor (I'm assuming this is what the loop you showed above will become -- lets call it "HW") and one that interacts with the user (lets call it "UI).

"HW" generates a temperature reading event and an alarm event. "UI" generates a new limit and stop event.

"HW" consumes the new limit and stop events. "UI" consumes the temp reading and alarm events.

So my init sequence would be:

  1. "HW" creates the temp reading event and alarm event and stores this in a cluster called "HW.Pub"
  2. At the same time, "UI" creates the new limit and stop event, storing these in a cluster called "UI.Pub"
  3. On the higher level diagram, you feed "HW.Pub" to a function called "UI.Subscribe". This function pulls the events it cares about out of "HW.Pub" and bundles everything up into a cluster called "UI.Sub"
  4. At the same time, on the higher level diagram, you feed "UI.Pub" to a function called "HW.Subscribe". This function pulls the events it cares about out of "UI.Pub" and bundles everything up into a cluster called "HW.Sub".
  5. You pass "HW.Pub" and "HW.Sub" to a function ("HW.Main"), and you pass "UI.Pub" and "UI.Sub" to a function ("UI.Main").

I like this because it makes it clear just by looking at the code exactly what each loop is subscribing to and publishing. This doesn't scale for more dynamic situations.

 

  • Like 1

Share this post


Link to post
Share on other sites

Just to throw some more wood on the fire of experimenting.....

Queues are a Many-to-One architecture (aggregation). You can have many providers and they can post to a single queue. They also have a very specific access order and this is critical. Events are a One-To-Many architecture (broadcast) they have a single provider but many "listeners". Events are not guaranteed to be acted upon in any particular order across equivalent listeners-by that I mean if you send an event to two listeners, you cannot guarantee that one will execute before the other... 

For control, queues tend to be more precise and efficient since you usually want a single device or subsystem do do something that only it can do and usually in a specific order. As a trivial example, think about reading a file. You need to open it,, read and close it. If each operation was event driven, you could not necessarily guarantee the order without some very specific knowledge about the underlying mechanisms. Queus you can  poke the instructions on the queue and be guaranteed they will be acted upon in that order. This feature is why we have Queued Message Handlers.

Now. That leads us to a bit of a dichotomy.We like queues because of the ordering but we also like Events because they are broadcast. So what would a hybrid system give us?

This is my now standard architecture for complex systems. A control queue with event responses in a service oriented architecture where the services can be orchestrated via their queues and listeners can react to events in the system. If you want to see what that looks like. Take a look at the VIM HAL Demo. It is a message based, service oriented architecture with self contained services that "plug-in"  (not dynamically, but at design time).

Edited by ShaunR

Share this post


Link to post
Share on other sites
4 hours ago, ShaunR said:

(1)If you send an event to two listeners, you cannot guarantee that one will execute before the other... 

(2)For control, queues tend to be more precise and efficient since you usually want a single device or subsystem do do something that only it can do and usually in a specific order. (3) As a trivial example, think about reading a file. You need to open it,, read and close it. If each operation was event driven, you could not necessarily guarantee the order without some very specific knowledge about the underlying mechanisms. (4)Queus you can  poke the instructions on the queue and be guaranteed they will be acted upon in that order. This feature is why we have Queued Message Handlers.

1-Can you guarantee this with two queues? In any message-based environment sync between two processes is difficult.On the receiving side, events are still received in the order they are generated...

2-This part is very true. To avoid making a billion events I'll have some events for critical messages that I consider important to the behavior of the system (shutdown, configure alarms) and a generic string message for minor things.

3-Again, events get there in the expected order (if you send open first it opens first, unless you mark "read" as higher priority than "open"). However, I don't think its appropriate to send three commands to any subsystem like that. There should be a single atomic message ("hey read me my data and send it back") and the receiver should figure out what to do with it.

4-It would be nice if there were better tools for events. It would also be good if there were better tools for queues, like a true priority system, the ability to listen on multiple queues at the same time, and the ability to selectively flush certain elements from the queue (in a safe way). Events should have better preview mechanisms (well actually I don't care about this but it seems like you did), the ability to limit the size of the queue, and better type propagation. I prefer the tradeoffs of events to those of queues, but both have issues. It would be great if we could bundle an event registration with a queue and treat N queues as an event registration. That would totally fix the issues, wouldn't it?

Share this post


Link to post
Share on other sites
14 hours ago, Tim_S said:

I expect New Temperature Hysteresis contains a numeric with a label of "New Temperature Limit", at least in the User Events control.

 

13 hours ago, smithd said:

Loads of good stuff...

 

Okay, so the reason I messed up the events was that I rearranged and renamed some of the events in the events cluster. Using method 2 (smithd) I fixed the issue, thanks for the info. Also smithd, thanks for the comprehensive post. Pretty much everything in it is useful to me. I'm going to crack on with some more coding...

 

 

Share this post


Link to post
Share on other sites
5 hours ago, ShaunR said:

Just to throw some more wood on the fire of experimenting.....

Queues are a Many-to-One architecture (aggregation). You can have many providers and they can post to a single queue. They also have a very specific access order and this is critical. Events are a One-To-Many architecture (broadcast) they have a single provider but many "listeners". Events are not guaranteed to be acted upon in any particular order across equivalent listeners-by that I mean if you send an event to two listeners, you cannot guarantee that one will execute before the other... 

For control, queues tend to be more precise and efficient since you usually want a single device or subsystem do do something that only it can do and usually in a specific order. As a trivial example, think about reading a file. You need to open it,, read and close it. If each operation was event driven, you could not necessarily guarantee the order without some very specific knowledge about the underlying mechanisms. Queus you can  poke the instructions on the queue and be guaranteed they will be acted upon in that order. This feature is why we have Queued Message Handlers.

Now. That leads us to a bit of a dichotomy.We like queues because of the ordering but we also like Events because they are broadcast. So what would a hybrid system give us?

This is my now standard architecture for complex systems. A control queue with event responses in a service oriented architecture where the services can be orchestrated via their queues and listeners can react to events in the system. If you want to see what that looks like. Take a look at the VIM HAL Demo. It is a message based, service oriented architecture with self contained services that "plug-in"  (not dynamically, but at design time).

There's quite a bit going on in the demo code, more than I can take in quickly. It appeals to me more as something I would use in my every-day LabVIEW coding in work, rather than what I'm trying to do with RPi, which is to build a system that intermediate programmers can easily understand.But again, I need to look at the code more closely to try and grasp the basic concepts in it, and see what I can use. For RPi, I can see useful services being a re-usable data logger, or TCP server, or maybe even reusable sensor modules. As I type this, I'm warming to the idea. And, another plus, I now know that VI macros exist. :)

Share this post


Link to post
Share on other sites
2 hours ago, smithd said:

1-Can you guarantee this with two queues? In any message-based environment sync between two processes is difficult.On the receiving side, events are still received in the order they are generated...

2-This part is very true. To avoid making a billion events I'll have some events for critical messages that I consider important to the behavior of the system (shutdown, configure alarms) and a generic string message for minor things.

3-(a)Again, events get there in the expected order (if you send open first it opens first, unless you mark "read" as higher priority than "open"). (b) However, I don't think its appropriate to send three commands to any subsystem like that. There should be a single atomic message ("hey read me my data and send it back") and the receiver should figure out what to do with it.

4-It would be nice if there were better tools for events. It would also be good if there were better tools for queues, like a true priority system, the ability to listen on multiple queues at the same time, and the ability to selectively flush certain elements from the queue (in a safe way). Events should have better preview mechanisms (well actually I don't care about this but it seems like you did), the ability to limit the size of the queue, and better type propagation. I prefer the tradeoffs of events to those of queues, but both have issues. It would be great if we could bundle an event registration with a queue and treat N queues as an event registration. That would totally fix the issues, wouldn't it?

1- Yes. If you have two queues the state and order of operation is external to the sub systems. This means you can place something on one queue, wait for a confirmation (if it is important to do so) and then place the copy on the other. You cannot do this with events.

2- A key point here is message filtering and, consequently, local state and system state.

You can consider it like this.

When using a queue you filter events on the sender side. You can decide which queue gets which messages and you can "crank" the system through it's various states by being selective in the messages you send and to whom.So you can move the slide, adjust the camera focus and then take a picture with each subsytem responsible for it's little bit. This means you don't flood messages and have strict control over the interaction between sub systems..

With Events, you filter messages on the receiver side. This means that state is local to the sub system and it is oblivious to the "system state". All messages are received by those registered and they immediately start to do their thing. There are two schools here. Those that have a single or shared event and each subsystem filters for relevant messages to it or a separate registration specific to that subsystem which is the same as a queue but you can't reference by name  (hence me wanting VIMS to encapsulate events :cool:)

3-(a) The point I was trying (and possibly failing) to make is that with a single event to read the file and 3 separate event cases to Open, read and write. you don't know which one will happen first. With a queue you can send to each queue separately, in order.

3- (b) Agreed. But tell the JKI state machine that :lol: 

4-I'm not sure I get what you are saying here and I think you might have me confused with someone else :P 

I don't need any more tools for queues. I'm sure I could find a use for queue priorities but I do fine without it. I don't use queues that way. I also don't need event flushing or previewing on events because I don't design systems that message flood.Each message is sent when it is required and only when it is required. I do have things like heartbeats and maybe the odd burst but my systems are not 1000 balls tossed into the air all emitting and consuming messages just to stay in the air. Mine are all sat on a shelf and I poke each one when I need it to do something :D

Edited by ShaunR

Share this post


Link to post
Share on other sites
2 hours ago, dmurray said:

There's quite a bit going on in the demo code, more than I can take in quickly. It appeals to me more as something I would use in my every-day LabVIEW coding in work, rather than what I'm trying to do with RPi, which is to build a system that intermediate programmers can easily understand.But again, I need to look at the code more closely to try and grasp the basic concepts in it, and see what I can use. For RPi, I can see useful services being a re-usable data logger, or TCP server, or maybe even reusable sensor modules. As I type this, I'm warming to the idea. And, another plus, I now know that VI macros exist. :)

It's actually very straight forward. The main VI pokes the servers to do things and the listeners react to the responses from the servers. Each server and listener is an API with a pre-defined set of actions and commands. They all follow the same template.Keep adding APIs (services and listeners).and keep poking them to do things :D Each subsytem is standalone (modular) so you can take one; analyse it then run it and test it in isolation meaning you can break down even really complex systems in easily digestible chunks..

Don;t forget to run the TCPIP client first so you can see the Telemetry that hooks all the messages  ;)

Edited by ShaunR
  • Like 1

Share this post


Link to post
Share on other sites
7 hours ago, ShaunR said:

1- Yes. If you have two queues the state and order of operation is external to the sub systems. This means you can place something on one queue, wait for a confirmation (if it is important to do so) and then place the copy on the other. You cannot do this with events.

2- A key point here is message filtering and, consequently, local state and system state.

Ah, yes, waiting for a response in between would do that.

I believe a key part of our disagreement is you seem to care that events can be 1:N. I use them quite regularly as 1:1. The important thing for me is that I can take N different message sources in 1 event structure and still have it be type safe. If that ends up with a bunch of processes all "broadcasting" to one specific listener, thats absolutely fine by me. Its also nice to know that if I want to, I can always reroute data to another source easily enough (although you can say the same for queues).

7 hours ago, ShaunR said:

3- (b) Agreed. But tell the JKI state machine that :lol: 

Seeing the command Macro::DoSomething makes me :( on the inside

7 hours ago, ShaunR said:

4-I'm not sure I get what you are saying here and I think you might have me confused with someone else :P 

No, I read "Queus you can poke the instructions on the queue and be guaranteed they will be acted upon in that order" and interpreted as "peek and poke" and jumped over to the "peek" part -> "you want to be able to see whats on the queue". It totally makes sense, I promise.

Share this post


Link to post
Share on other sites
On 8/6/2016 at 2:33 AM, smithd said:

I believe a key part of our disagreement is you seem to care that events can be 1:N. I use them quite regularly as 1:1. The important thing for me is that I can take N different message sources in 1 event structure and still have it be type safe. If that ends up with a bunch of processes all "broadcasting" to one specific listener, thats absolutely fine by me. Its also nice to know that if I want to, I can always reroute data to another source easily enough (although you can say the same for queues).

Right. So you are filtering on the receiver.

There is nothing wrong with anything you are saying but there are three four things that make a clear case for one or the other in the 1:1 cases you describe (in my architectures):

  1. Events cannot be referenced by name. For me this is a problem since my messaging system is string based (Like SCPI). The routing is based on module names so this rules our events for command/control queues. This is why I invented named events when I found out about the VIM.:thumbup1:
  2. I can have more control. I can make the queue lossy (rate limit for comms) or flush them (abort) and I can insert exit messages at the front. You will probably come back and say that's what you want from Events too.but you already have that if you are just going to use an event as a queue with a case statement.
  3. You can't "lazy load" events. With a queue I can start up the main and poke some values on  to it. Whenever the sub modules start up, they are still there when they come to read their queue. Events don't do this so you end up having to detect when a module is loaded and then send it a message. (External state again)
  4. It's not portable.It is using tribal knowledge of a specific implementation of events. It is more of an abuse of events knowing they have a queue in the LabVIEW implementation so the architecture will not work in the other, true event driven language,s that I also program in (Free Pascal for example).
Edited by ShaunR

Share this post


Link to post
Share on other sites

Just an aside, but I almost never use User Events as 1:N.   I use both Events and Queues as N:1 message carriers.  Each receiver creates the communication method it prefers, preloads any initial messages, and then passes it to the sender(s).  For 1:N I use arrays of these Events/Queues.

Edited by drjdpowell

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.


×
×
  • Create New...

Important Information

By using this site, you agree to our Terms of Use.