LabVIEW

cancel
Showing results for 
Search instead for 
Did you mean: 

Analysing a rats-nest

Solved!
Go to solution

Hi

 

I have been given the wonderful task of understanding what looks like a rats-nest of real-time code, and making changes to it. It works (almost) and builds, but I need to fix and change it. Its target is a PXI based NI Linux RT (and another app which is CRIO NI Linux RT), so I can't run it and trace it. And to-be-honest, I don't think running it is going to be helpful. Rewriting it is not an option (unfortunately).

 

What I am finding difficult is actually manually tracing through the code (statically) with the LabVIEW IDE.

 

1) I'd like to find out where all instances of a built-in VI are actually being made e.g. UDP Read - Its not in dependencies and not in the project, so how can I perform a  Right-Click > Find > Callers?

 

2) User-event and Notifier references are being set as properties in Actor Framework classes all over the place. I think they are limited to the classes themselves, but instances of the classes are being used all over the project.

How can I trace where they are being created, registered and actually handled by event structures? I am hoping something like the "Show Class Hierarchy" tool facility exists for events and notifiers? Its a long-shot but I'm hoping.

 

3) Are their any tools which would reverse engineer a UML diagram? Though I'm not sure how event/notifier passing of info actually is represented in UML?! But something that could even just reveal class instance relationships would be useful.

 

4) Are their any tools/features to LabVIEW that can provide complexity figures?

 

5) Are there any articles which cover LabVIEW code analysis which is really difficult to pull apart and breakdown into sub-artifacts. Are there some hot-key tricks maybe?

 

If it were a script application I could do plain-text searches for certain key-words treating the code as just text. But in LabVIEW you can't do simple things like that, which makes pulling the code apart to understand it that bit more difficult i.e. the G and structure of the application gets in the way.

 

Thanks for any suggestions anyone can provide.

 

 

0 Kudos
Message 1 of 13
(2,590 Views)

@skol45uk wrote:

1) I'd like to find out where all instances of a built-in VI are actually being made e.g. UDP Read - Its not in dependencies and not in the project, so how can I perform a  Right-Click > Find > Callers?


Those are functions.

 

Select one, CTRL+F. This will find the instances of the function in the VIs that are in memory.

 


@skol45uk wrote:

3) Are their any tools which would reverse engineer a UML diagram? Though I'm not sure how event/notifier passing of info actually is represented in UML?! But something that could even just reveal class instance relationships would be useful.


I think OpenGDS, but it's not AF specific.

 

There are probably a few initiatives, but I don't use AF so I can't really advice.

 


@skol45uk wrote:

4) Are their any tools/features to LabVIEW that can provide complexity figures?


Do you mean metrics?

 

VIs have a metric properties. The Metrics.Advanced property is only available with hidden scripting options on, but it provides a lot:

wiebeCARYA_0-1654698701843.png

 

0 Kudos
Message 2 of 13
(2,548 Views)

VI Hierarchy view. It will be your new favorite tool.  Search from there to find and or replace any items in the Hierarchy.   You can even launch a search for text in things like block diagram objects (useful for finding where some element is bundled/unbundled etc...and one reason I default to naming queues, events, notifiers and other things that break dataflow)

The Class Hierarchy browser will help too.

 

When I see Spaghetti code I always start with a Hierarchy view to grasp the depth and structure.   And usually run the VI Annalyzer to identify likely problem areas. 

 

Make use of Bookmarks to jot down your thoughts on what the code is doing or supposed to be doing BONUS: you can even use foul language. 😉  

ADD vi documentation as you go.

 

Do create Sandbox code to test subvis or APIs that might have unexpected results (zero iteration for loops without SRs, dead ended error clusters, poor error handling...)

 

 


"Should be" isn't "Is" -Jay
0 Kudos
Message 3 of 13
(2,543 Views)

One thing to keep in mind with Actor Framework is that it's intended to be used as the ONLY communication method between actors. Inside an actor, you can communicate however you want.

 

If the programmer of this decided to circumvent the actor tree, it's going to be MUCH harder to debug. If they followed the "only use AF enqueuers to talk to other actors" then you can likely ignore most of your user events and notifiers.

 

User events and notifiers should, in a properly done AF system, only be used between helper loops inside a single actor. For example, you might have user events signaling between a helper loop in Actor Core and certain methods. A method may use a User Event to send a signal back to the UI loop in Actor Core to do some work.

 

Being able to ignore those would let you "black box" an Actor and just assume it works unless something gets sent to its Enqueuer. When you have multiple communication paths to an Actor it gets really hairy and you get to deal with a bunch of race conditions. Hopefully the notifiers and user events are just used internally with the Actors that use them (since that's a very common use case) and not between actors (which is a bad practice 99.9% of the time).

0 Kudos
Message 4 of 13
(2,531 Views)

I hear you loud and clear on this point. But I can't find an easy way to confirm a Notification type or User-Event type is actually being contained within the Actor. And there are a number of Actors!

 

And just to be clear, this "rule of thumb" that in clean code a notifier/user-event is only within the Actor that creates it, is this true even if there are nested Actors? What I have seen so far, is message-handler methods in the Actor/Nest-Actor that just exist to raise the message content as an event/notification. Which, kind-of breaks the above "cleanliness" that can maybe be assumed about events/notifications. And still entangles the relationship between Actors.

 

I've personally experimented with abstract messages and Interfaces, and neither are being used from Nested-to-Parent messaging in this code. It looks like a reference to the parent-actor enqueuer is being passed to a launched nested actors, who use it to send a message back to the parent. I think.

 

Sorry, don't want to drag you into this 'mare.

 

While trying to trace user-event/notifier sources/sinks, I've tried "VI Hierarchy" and CTRL-F and they don't seem to show or find a "Send Notification" or "Generate User Event" call. And Ctrl-F never seems to find anything! This may be just my use of it as this feature is quite new to me.

 

And as for tracing a type specific "Send Notification" or "Generate User Event" call, I think I'm just out of luck.

 

I've found a web-page describing the LabVIEW hidden scripting options! Wow! Need to dig into this at some point. Thanks very much for that detail. Metrics would be really useful if I can get that going.

 

 

0 Kudos
Message 5 of 13
(2,524 Views)

The general idea is that Actors communicate via the actor tree. In other words, only to their caller and to the ones they specifically call.

 

The Caller enqueuer is obtained with "Read caller enqueuer", then you can send messages up the chain. Nested actors do require you to store the Enqueuers when you launch them, then send them messages later on.

 

Figuring out if the Notifiers are going up or down is more architectural than "ctrl-F"-able. Find where the Notifier is created, and see if there are any methods that send that Notification reference to someone else. Maybe it's created and added to the object before being launched, letting the previous programmer leak that notifier reference. Otherwise, it'd have to be sent along a regular Actor message. Or, maybe the calling parent creates a Notifier reference and sends it to a callee actor. Basically you just have to trace where the Notifier references (and UE's) get routed.

 

Typically if you have Parent actor that launches Child A which launches Child B, and you want Child B to give info to the Parent, then B should send a message to A which sends a message to Parent. This does result in "message forwarding" code that just receives a message and resends it, but it's the proper way to do things.

 

(As an aside- a better way to look at it is that each "forwarding" step should recontextualize the data. For instance, B might be an analog input actor that just sends voltage data to A. A might scale that data and send "pressure" data to Parent. Yes, it's just forwarding the same underlying info, but each Actor has a different responsibility and "understands" the data differently. The exception to this is logging actors, which generally just need "forward the data straight through" methods.)

0 Kudos
Message 6 of 13
(2,516 Views)

Last paragraph very clarifying. Thanks.

 

I'm just still "stuck" in tracing features across the code. Its a LabVIEW IDE issue more than specifically Actor Framework. AF just makes it a bit more challenging, but probably better than if there was no Actor decomposition.

 

Maybe I need to raise a feature-request to NI. It just feels unnecessary challenging to have to manually go through each VI to find a "Send Notification" or "Generate User Event" to trace the source/sink of events. Maybe I'm just spoilt.

 

The main problem is that the data-type of the created User-event or Notifier is not type-def'd. I think it was, but as I have experienced, if you drag a type-def onto a block-diagram in a certain way, the link is lost. So I can't trace their usage using it either.

 

Is there a way to re-link a constant to a data-type, instead of selecting "Make Type Def." which creates a whole new data-type?

 

Maybe I can hand-code something to pull out sub-VI instance entries from a list of VI's? But then I would have thought Right-Click > Find > SubVIs would list them anyway. And its doesn't. Just .ctl files which actually are not linked to any data-type. This aspect of LabVIEW is really messed up in my opinion. Unless there is genuine reason why disconnection of a constant from a data-type is a useful option?!

 

Rambling a bit. But analysing others code always ends up finding problems in everything! Including LabVIEW itself. Which does not seem to be designed for "reading" code. Just creating it.

 

0 Kudos
Message 7 of 13
(2,501 Views)

Are you operating within a Project? Ctrl F, then select Objects, and select "Generate User Event" with Search Scope to All VI's in Instance should find everything.

 

And clicking View- VI Hierarchy should get you a list of subVI's. If you're using a lot of PPL's or something then I could see it being a pain, but I'm not sure why you're having such trouble with your Find tools. Maybe something isn't loaded into memory that should be (because it only searches in memory).

0 Kudos
Message 8 of 13
(2,484 Views)

As sad as this may actually sound, None of the LabVIEW Core training material actually covers the VI Hierarchy view.   

 

It is covered in the help file.

 

There are no presentations on it use as far as I know. 

 

But, Trust me when I tell you that you will MULTIPLY every second you spend exploring each of its menu options and tool bar icons.

 

Open main.vi and show items in vilib

Search for ACBRs and open those vis

 

Show controls ( pray that the communication types are typedef clusters of [command, data] and have non default icons) give your typedefs meaningful Icons when they don't have one.  

 

I usually use either a  FN Command Queue.ctl of cluster of [ enum <FN Q Command.clt>,Data]  or [ Message as String, Data as Variant] or even Qname.ctl[Qhandler case, Data]  

 

Enums stink for localization.  Strings stink when the developer has dyslexia. 

 

"LYSDEXICTS UNTIE!"


"Should be" isn't "Is" -Jay
0 Kudos
Message 9 of 13
(2,473 Views)

One of my first "tasks" in learning LabVIEW was to be giving a LabVIEW RT "project", developed in LabVIEW 7.0 (so before the "LabVIEW Project" as a "thing" with an extension of .lvproj) that ran on a PXI system using Pharlap (well before RT Linux).  Hundreds of VIs and Type defs, no more than 1-2% of the code having a "Description" giving a hint as to inputs, outputs, and function, many not even having a "Text" Icon.

 

My mentor showed me a trick that I call a VI Tree -- a VI that consists of nothing more than a subset of VIs (and TypeDefs) that exist in the (lower-case "p") project.  The idea is that you can load the "Tree" (which, if the Folder holding your project has sub-folders, can have a structure that mimics the folder "tree") and thereby know that all of your VIs are in memory, and can be found using the ^F Find function.  

 

It's late, and I'm too tired to find the reference, but there's something called the "SOLID" (I think that's the acronym) Principles of organizing code that can also help you to refactor poorly-designed code.  As I recall, "S" stands for "Single Responsibility", the idea that a sub-VI should do one thing (and do it well), hiding messy details and making the routines in which it appears more transparent (particularly if the sub-VI has a recognizable Icon that gives a clue to its purpose).  

 

And for your own sanity, be sure that all your code development and refactoring is within the context of some form of Version Control System!!

 

Bob Schor

Message 10 of 13
(2,437 Views)