Jump to content

[LVTN] Messenger Library


Recommended Posts

On 1/30/2020 at 10:00 AM, Maksim Kuznetsov said:

What do you think of using typedefs for message data?

I don't tend to use them unless they have meaning beyond the message itself (ie. they are a natural grouping of data, rather than something that just exists for the message).  Most of my messages contain only one piece of data, so no typedef needed (unless that data is naturally a typedef).  Also, it is possible to go more than just a typedef: have an API of subVIs to send and receive the messages in a common library.  This can be a lot more powerful than just a typedef.  An example would be having any message starting with "Config:..." being passed to a Config subVI, with multiple possible messages being handled by that subVI ("Config: Get as JSON","Config: Set from JSON", etc.).  Another option is to send an Object that has multiple methods that the receiving actor can use.  I view a typedef as a poor cost-benefit trade-off, since you have the coupling issue without the maximum possible benefits.

  • Thanks 1
Link to comment
  • 3 weeks later...

Thank you once again Dr. Powell for sharing this powerful approach. I can see it being used in "Metronome" actor (e.g. Set Period).
And I already started to use it in my projects.

If you don't mind I would like to ask you another question: I have an actor (A) that launches a sub-actor (B) to delegate some tasks to it.
When sub-actor (B) publishes an event, actor (A) gets a message. However I would like to publish that message beyond actor (A). So that actors subscribed
to actor (A) would also receive events from sub-actor (B).

Currently my approach is to send Observer Registry of actor (A) to sub-actor (B) during initialisation. Then sub-actor (B) publishes its events into Observer Registry of actor (A) directly.
Another idea was to republish the event from actor (A) when it gets the message from sub-actor (B). Neither of these methods feel right to me. The first one shares internal reference
of the actor (A). The second method is not as efficient (receives, then sends again).

How would you approach this?

  • Like 1
Link to comment

I have always done the second method, and just accepted the extra overhead of one extra message pass. Beware of premature optimization.  It is generally rare for me to want to just forward a message like this without the forwarding actor needing to change or react to the message in some way.

An alternate design is to accept that you don't have an actor-subactor relationship, but that your subactor should really be a helper loop of the actor.  A dedicated helper loop can share references no problem.  Your "actors sharing references" is a potentially suboptimal mix of "actors are highly independent but follow restrictive rules" and "helper loops have no restrictions but are completely coupled to their owner"

  • Thanks 1
Link to comment
  • 1 month later...
2 hours ago, Thoric said:

My code is not broken. It runs from source perfectly fine.

It's a common problem in LabVIEW.

Link to comment
55 minutes ago, JKSH said:

It's a common problem in LabVIEW.

Thanks JKSH:

  • Closed LabVIEW and cleared the compile cache - same error
  • Altered the Build Spec to not "Remove unused polymorphic VI instances" - same error
  • Altered the Build Spec to not "Remove unused members of project libraries" - it built
  • Rechecked "Remove unused polymorphic VI instances" - it still built.

In Short: The key is disable "Remove unused members of project libraries". Can't recall having to do this before, but hey ho. Whatever works!

Link to comment

Unfortunately, "Get Dynamic Launch Shell" gets a reference to a VI that calls the "Actor.vi" Dynamic-Dispatch method that is overridden by all your actors; i.e. practically ALL your application code!   It could have been anything breaking that rendered Get Dynamic Launch Shell" broken.  So it is near impossible to tell what was wrong.   If only that error message reported which subVI was the problem...

Link to comment

Hello James,

There is a concept I am struggling to get my head around. For a long time I am following a method of not having typedefs in messages. Messages can contain various data and to make sure that the message is constructed correctly I have to consult the actor and copy the structure from it. Same applies for messages received. I believe this is called "zero-coupling".

1. If I am adding the actor reference to the VI, it brings all the dependencies of the actor to project, so I am becoming coupled to that actor, isn't it? (it is not like some generic reference)
2. Why in this case it is not advised to use actor's typedefs in messages (this could save time and less errors would appear during development)?
3. There is another method - to have a SubVI instead of typedef that will do the job of constructing (deconstructing) the message. But how is this different from using a typedef? SubVIs will also create more coupling.

I have a feeling that my understanding of coupling is not full. It would be great to hear your thoughts about these points.

Thank you! Kind regards,
Max

Link to comment

Thank you for your feedback. The honest reason why I am asking is a hope that someone will tell me which approach from many is the better one.
Every now and then I am having a dispute over this with my colleagues and I am always saying that typedefs create additional coupling, but I realised that I never
understood what it actually means, it was like a rule I used throughout my projects. Now I am not sure if I was right or wrong, clearly I have a lack of knowledge.

Maybe someone can share their approach and reasons behind it? Thank you!

Link to comment
  • 2 weeks later...
On 4/20/2020 at 7:59 PM, drjdpowell said:

Unfortunately, "Get Dynamic Launch Shell" gets a reference to a VI that calls the "Actor.vi" Dynamic-Dispatch method that is overridden by all your actors; i.e. practically ALL your application code!   It could have been anything breaking that rendered Get Dynamic Launch Shell" broken.  So it is near impossible to tell what was wrong.   If only that error message reported which subVI was the problem...

I wanted to get back to this thread because you've effectively said that by using dynamic-despatch the actor launcher is pulling in virtually all my code and therefore it is near impossible to tell what's wrong; implying that there is a broken VI somewhere in my code that prevents the app builder from completing. But I'm certain there isn't anything broken. The code runs in the IDE. If I open all VIs there are none broken. None. So I don't see how your argument stands. I need to discuss further because I'm encountering annoyances associated with the workaround to disable "Remove unused members of project libraries" option and would like to go back to getting this properly sorted.

Link to comment

Have you tried building your actors individually into EXEs?  To see which one breaks.  Make a new build and make the top level vi only one of your actors and see if it builds.  Then swap in a different actor.  Hopefully, they will all build but one, which narrows down where the problem is.

Link to comment
3 hours ago, drjdpowell said:

Have you tried building your actors individually into EXEs?  To see which one breaks.  Make a new build and make the top level vi only one of your actors and see if it builds.  Then swap in a different actor.  Hopefully, they will all build but one, which narrows down where the problem is.

OK, good idea. So I painstakingly built each actor independently and 11 built fine, 10 failed. I think some of those that failed are only failing because they call one of the other failing child actors, so of the 10 failures only 5 are at the bottom of the tree hierarchy and I think these are the culprits.

So now, the question is "What about these 5 failing actors is causing a build failure?". Do they exclusively have something in common?

Their roles are:

  1. One is Dialogue style GUI Editor for populating an Array - a relatively simple actor.
  2. Another is a Dialogue style GUI Editor for a more complex file management need. Manages remotely (FTP) stored files.
  3. A third Dialogue style GUI Editor for managing another Array - a relatively simple actor, like (1).
  4. A local file management actor for receiving configurations and collating them into a JSON file - a simple decoupled actor with the ability to gather JSON objects from any other actor for storage and retrieval.
  5. A simple actor that adds log entries to a local text file, and uploads the local file to a remote FTP folder.

So they don't all have one common dependency that the other actors don't also use. The first three present their FPs, but so do many other actors. Some depend on FTP interaction, using a separate internal library we have, but not exclusively so.

I think I'll try cloning one of these 5 actors, trial build it then strip out 1 feature, and repeat until it builds successfully to determine which component is the problem...

Link to comment
1 hour ago, Thoric said:

Their roles are:

  1. One is Dialogue style GUI Editor for populating an Array - a relatively simple actor.
  2. Another is a Dialogue style GUI Editor for a more complex file management need. Manages remotely (FTP) stored files.
  3. A third Dialogue style GUI Editor for managing another Array - a relatively simple actor, like (1).
  4. A local file management actor for receiving configurations and collating them into a JSON file - a simple decoupled actor with the ability to gather JSON objects from any other actor for storage and retrieval.
  5. A simple actor that adds log entries to a local text file, and uploads the local file to a remote FTP folder.

So they don't all have one common dependency that the other actors don't also use. The first three present their FPs, but so do many other actors. Some depend on FTP interaction, using a separate internal library we have, but not exclusively so.

I think I'll try cloning one of these 5 actors, trial build it then strip out 1 feature, and repeat until it builds successfully to determine which component is the problem...

This is a long shot here, but do they all need to run in the UI thread but their preferred execution system is set to "same as caller" and then something gets screwed up when they are called by dynamic dispatch in the runtime?  What happens if you set their preferred execution system to "user interface" and retry.

 

Link to comment

@Thoric

I have seen similar behaviour before. There are two scenarios I have seen.

1. It turned out that some code I had naughtily left in a diagram disable structure was "out of date". If I recall it was a class method that was broken or perhaps the class had "stale" data in its history. Resetting the class mutation history sometimes fixed this.

2. Are you sure you don't have any broken methods (like perhaps a test or prototype VI in a class somewhere that you never got around to updating) in the project? This kind of error is hard to track down as everything will run fine in LabVIEW.

It might be quicker to roll back to when it last worked and then look at your next commit to see what changed.

Edited by Neil Pate
Link to comment

So I started by cloning the first actor that has a problem and creating a build spec for it, then removing content bit by bit until it built OK. It was only happy when I removed the last reference to a typedef cluster that belongs to another library (which I'll call LibraryA).

I started again with the same actor, cloned it, checked it still doesn't build, removed only references to LibraryA's typedef by "disconnecting from typedef" wherever it appears in the actor and it then built ok straight away. The fix has nothing to do with all the other things I'd removed, just typedef linkages to other libraries.

I tried the same with the second failing actor. Check it still fails to build. This actor has typedefs from two other libraries in it (LibraryA and LibraryB). Removing links to these wasn't enough. I also had to remove links to all typedefs, including one that belonged to a toolkit. Then it finally built.

Thoughts anybody? Every actor I've created has typedefs from libraries and toolkits in it. But only these five actors appear to have a problem with incoroprating them. 

@Neil PateThanks for the idea, but these actors have no stale or unforgotten disabled code in them. They're really clean and some a even super simple. The classes also have no bad VIs in them.

@bbeanThanks for the idea, but I haven't changed their thread preferences. I'm not sure actually can without breaking the dynamic dispatch rules.

Edited by Thoric
Link to comment
8 hours ago, Thoric said:

Thoughts anybody? Every actor I've created has typedefs from libraries and toolkits in it. But only these five actors appear to have a problem with incoroprating them. 

My gut feeling says that a Mass Compile could make this problem go away.

Link to comment

Question: if you take an actor that has the problem, and remove it from its class (making it an ordinary non-dd vi) does it still not build?  I want to know if this is a problem that requires DD and your library typedefs together, or just the typedefs.

Edited by drjdpowell
Link to comment
15 minutes ago, drjdpowell said:

Question: if you take an actor that has the problem, and remove it from its class (making it an ordinary non-dd vi) does it still not build?  I want to know if this is a problem that requires DD and your library typedefs together, or just the typedefs.

OK, so I made a Source Distribution out of the failing actor, which included all it's references/dependencies. Wrapped this one actor and the copied dependencies into a new project, created a build spec and it fails. Good. So I have a test environment. Checked into SCC.

Remove ActorNR.vi from Class
I removed ActorNR.vi from the class (with the rest of the class content to maintain accessibility) changed the DD terminal to Optional. It still fails. If I run ActorNR.vi it works fine. So this problem isn't necessarily related to the DD requirement.

Mass Compile
I don't think this is necessary given I've created a fresh Source Distribution, but tried for completeness. No reported errors. The build still fails.

Note: The build error (Cannot save a bad VI without its block diagram) appear to suggest the problem is with: vi.lib\drjdpowell\Messenging\Parallel Process\Parallel Process Library\Startup Handshaking.vi. Clearly this VI is not broken in source.

Remove Identity from ActorNR.vi
I realised the ActorNR.vi was still referencing actortype2 as it had the identity class control. Deleted it. Build still fails, same error but now it points to vi.lib\drjdpowell\Messenging\Parallel Process\Parallel Process Library\Startup type 2.vi

Investigating the dependency libraries
One of the three libraries this actor calls upon actually contains a Messenger actor. An actor that's not required in this case, but exists within that library. When removed from the library, the build process succeeds.

Rewind
I rewound my source code branch, so this problematic actor is now an actor again, and tried the last step of removing the other unused actor from the dependency library. Build succeeds.

So this is my situation (see image attached).
 

 

buildfail1.png

Link to comment

My fix for now is to move the "IGOR_Sync.lvclass" actor out of the containing library "IGOR Sync.lvlib". This ensures the actor isn't present and therefore not "removed as an unused item from the library" during build.

But I think I need to get to the bottom of why this perfectly valid situation is creating a build failure. Any thoughts? Is the class structure/tree being broken in the builder when the unneeded actor is removed from the library? That would be a LabVIEW builder bug.

Link to comment

I've narrowed this down to this probable cause:

If an actor in a library that's drawn in as an unneeded dependency uses "TCP Actor" to make it remotely accessible then the build of the structure fails when "Remove unused elements from Libraries" is set in the app builder, as is the default for LabVIEW.

I've attached code that demonstrates the build error (LV2017) and if you remove the TCP Service subVI from the actor UnusedClassWithinLibrary.lvclass the build then succeeds.

@drjdpowell Any clues as to why using the TCP service features on an unused actor within a library could cause a build to fail?

Needing to keep "Remove unused elements from libraries" set in the builder inflates my executable by over 100% because it then draws in masses of unnecessary code.

messengeractortcpcreateserroronbuild.png

MessengerActorBuildFailDemo.7z

Link to comment

I'm guessing this is an edge-case builder bug.  I realize, though, why I have not seen it; I am hesitant to use LabVIEW Libraries.  I don't put typedefs in libraries at all if they are going to be used by a component independently of the other stuff in the library, and I rarely if ever put a Class inside a Library.   Libraries should be a good thing, but the "load everything in the dev environment" is a real negative.  Here, the bug you are suffering is in the builder code to strip out the useless connection to the unused library elements.   That builder code shouldn't even need to exist, as there is no reason to load code that is not used.  

The fact that your IGOR Library has "Utilities" and "Common Typedefs" tells me you are using LabVIEW Libraries the way they SHOULD work: convenient collections of things that you might use some of.  But Libraries are too flawed to be used as they intuitively should be.  Hopefully, they will address this in NXG.

Link to comment

I'll have a look at your Demo when I get the chance, to see if I can find a work around (other than not putting actors in Libraries).  Unfortunately, "Messenger  Library" is built on the principle of doing asynchronous things without being blocked by "Root Loop" (a little known but critical flaw of async calls in LabVIEW), and that restricts how I "launch" actors.  The NI "Actor Framework" uses the same/similar method; I wonder if it has a similar problem.

Link to comment
  • 2 months later...

Hello everybody,

I am currently cleaning up my projects and thought of the way that could potentially reduce the amount of things loaded in the development environment.

This is the architecture used at the moment:
Actor named "Main" launches actors A, B, C, D, E. The last actor E is launched and requires addresses of C, D.
I pass these addresses (C, D) as specific actor references.

That means that if I want to debug just actor E, the development environment will load actors C, D as well.
And if one of them is broken, actor E will appear broken as well (even if I don't want to test functions related to C, D).

Idea:
Instead of passing specific actor references to actor E,  I can use generic references for C, D (Actor type 2\Actor type2.lvclass) in actor E.
Then if I want to work on actor E, actors C, D won't be loaded.
_______________________________________________________________

Before implementing this idea for my project which will take substantial time, I would like to hear your opinion on this.
Maybe I am missing something. Thank you in advance!

Link to comment

Join the conversation

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

Guest
Unfortunately, your content contains terms that we do not allow. Please edit your content to remove the highlighted words below.
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.