10-19-2020 10:59 PM
Hi,
I have the following simple SMO structure:
The SMO.RT_CTRL launches SMO.LCD.16x2 and SMO.TCP processes using the LaunchDependency.vi. Both dependencies are launched in the OnStarted.vi (SMO.RT_CTRL).
I’m able to pull the SMO.TCP reference without any problems using the FindDependency(by key).vi, after that, I apply a ToMoreSpecific.vi to register the SMO into RT_CTRL. No problems at this point.
My issue is with SMO.LCD16x2. I’m following the exact same procedure as for SMO.TCP. I call LCD.GetPublicEvents.vi (changed inputs to dynamic dispatch) after the class typecast since LCD16x2 is a child of SMO.LCD. See below:
I get an error telling that it couldn’t find the reference to LCD16x2. Sorry, I forgot to write down the error number but my main question is if this the correct way to handle child classes from dependencies?
Any help will be appreciated.
Thanks.
Solved! Go to Solution.
10-20-2020 07:47 AM - edited 10-20-2020 07:48 AM
Hi Luis,
The way you are using the LaunchDependency and FindDependency(byKey) is fine. You correctly wait for the SMO to be started before you create your dynamic dependencies.
There are three things that could be wrong here though.
The first thing I notice is that you discard the 1556 error after your cast. That tells me that the SMO reference is invalid or you are storing your public events by reference in a byValue SMO, so it could be that your object is not created in the same process thread as it should to work.
The second thing is that the onStarted() method is a callback run from the Process thread, so any byValue child object you instantiate will not be accessible outside the process thread. This is an effective encapsulation method if this is by design, but it might not be what you are aiming for.
The third thing to make sure is the name of your LCD object is unique. When you use the LaunchDependency.vi method, check the output name in case the registry appended a namespace to prevent name duplication. This is probably not the case, but just mentioning to be rigorous.
If you launch both dependencies from the onStarted() callback, I'm going to make a guess that your TCP object is a byReference SMO and your LCD object is byValue.
If so, you might want to make both be byRef objects.
***
However, I think you might also want to look into another pattern for launching your dependencies. If you are using the onStarted method, that's because your dependencies are really "static" (aka. always need to be there for your RT_CTRL class to function properly.
In a situation like this, I'd use the "enumerateStaticDependencies.vi" override and define them as static dependencies.
You can configure your objects (either by type, or even by creating them before injection, as you wish) using the following template:
The advantage of Static Dependencies is that they will get created and started by the framework before your RT Controller object is officially started, so that any calls in the Process or non-Process thread that occur after being started (onStarted) will have the byValue object in the same state, in both threads.
You will also see them show up in the "ListThisLevelDependencies" method in the order you declared them.
Knowing this, you can simplify your life with this snippet to extract the class references of your statically-defined dependencies:
Oh, and it's faster than the (byKey) search...
Hope this helps!
Francois
10-20-2020 08:43 AM
Adding this useful reference to the discussion:
https://forums.ni.com/t5/JKI-State-Machine-Objects/SMO-Process-Lifecycle/
10-20-2020 01:22 PM
François, thanks for your reply. I was expecting you to jump in and provide useful information 😊.
The first thing I notice is that you discard the 1556 error after your cast. That tells me that the SMO reference is invalid or you are storing your public events by reference in a byValue SMO, so it could be that your object is not created in the same process thread as it should to work.
Answer: Error 1556 only happens at the 1st time when I register events inside SMO.RT_CTRL Process.vi, I don’t wait for the dependency to be available I just simply do: Events: Unregister, Events: Register.vi and Process: Sync again. This sequence executes after the last dependency is added. Another option to avoid this error is wait for the dependency to complete launch as you mention.
The second thing is that the onStarted() method is a callback run from the Process thread, so any byValue child object you instantiate will not be accessible outside the process thread. This is an effective encapsulation method if this is by design, but it might not be what you are aiming for.
Answer: All my SMOs are byRef objects. This is how I’m initializing my SMOs inside OnStarted():
The third thing to make sure is the name of your LCD object is unique. When you use the LaunchDependency.vi method, check the output name in case the registry appended a namespace to prevent name duplication. This is probably not the case, but just mentioning to be rigorous.
Answer: Names are unique in this case but will add that additional name check that you mention if I had to add more SMOs to my RT_CTRLR. Good practice.
If you are using the onStarted method, that's because your dependencies are really "static" (aka. always need to be there for your RT_CTRL class to function properly.
Answer: Yes, my dependencies are Static. RT_CTRLR is composed by TCP, LCD and ErrorHandler (not yet created) objects.
In a situation like this, I'd use the "enumerateStaticDependencies.vi" override and define them as static dependencies.
Answer: I agree. I think its better to use enumerateStaticDependencies(). That will ensure my dependencies’ processes are started before RT_CTRLR gets started.
--- Code modification:
I simplified my life by changing my implementation from dynamic dependencies to static dependencies. I no longer use OnStarted() method but enumerateStaticDependencies(). No need to run unregister and register events in RT_CTRLR.
With this pattern I’m now able to have my abstract SMO.LCD object and create specific implementations for 16x2 LCDs, TFT LCDs etc. Planning to have a HAL approach for this.
François, thank you very much for your help!
10-21-2020 08:32 AM
UPDATE:
After additional testing and moving the pattern from dynamic to static dependencies my original issue was not solved but resulted in a faster dependency launch.
The original issue was I was trying to override a byRef method (LCD.GetPublicEvents.vi). That cannot be done if the goal is to have access to more specific dependency data (child’s data). The solution is much simpler, create a protected method in the parent’s object (in my case SMO.LCD) and then override it in the child’s object (SMO.LCD16x2) so when the child’s Process.vi runs it’ll have access to its parent method.
Minute 13:30 in the HAL example from François shows this implementation.