Topic RSS20:26, EEST
February 21, 2014
OfflineHi,
I created a couple of Alarms (subtype of AlarmConditionType) using UaModeler. I am only using objects/variables/properties with ModelingRule==Mandatory.
All alarm instances are created in UaModeler as part of custom object definitions as instance declarations, not directly in the instance tree underneath the objects folder. Only an instance of the custom root application objectType is added to the objects folder.
Code has been generated and the model has been registered and nodesets have been loaded. SDK version is 5.8.0
1. when calling alarm.setActive(true) in the server code, the SDK throws an exception
After setting the ActiveState/Id value directly once (or, in UaModeler, uncheck the “null” checkbox for the value), subsequent calls succeed. This indicates, that the ActiveState/Id property is created only after writing a value directly to it
2. upon calling alarm.disable(), the toolkit triggers an event on its own. This came unexpected, because, if unaware of this behaviour, other alarm properties need to be adjusted before calling disable(). It looks like method calls are routed through the event manager. A little note in the Server tutorial could help to make this behaviour more explicit.
Is the recommendation to use the methods enable(), disable(); setActive() etc or to set the properties manually and then trigger in code?
I’d appreciate your comments on these observations.
Thanks, HU
16:29, EEST
April 3, 2012
OfflineHi,
Sorry, answers are very long..
1.
Based on the error and our code, the AlarmConditionTypeNodeBase.getActiveStateNode() did return TwoStateVariableTypeNodeBase whose getIdNode is non-null UaProperty, but the Value of the property itself is null. In general this scenario should not happen. SDK assumes there is always a non-null Value in the Id node, because AlarmConditionTypeNode.afterCreate() forces there to be false. So, probably the afterCreate() was never run by the SDK. Thus SDK fails when it tries to use ‘null’ as ‘boolean’ (and not ‘Boolean’). We could maybe treat null as false, but similar things exists elsewhere thus it is more important to know why afterCreate never ran as it does other things as well.
Each generated XXXTypeNodeBase.afterCreate has logic that first does ‘super.afterCreate();’ Then it calls internal .callAfterCreateIfExists(UaNode) for all of the components (that are defined in this typedefinition i.e. all for which it has a generated getXXXNode()). Thus an .afterCreate() on a “top-lvl-node” propagates to all of the components. Generated XXXTypeNode.afterCreate() also contain call ‘super.afterCreate();’ (so it calls the XXXTypeNodeBase.afterCreate() of that type, which then calls the XXXTypeNode of the super type) , thus one possibility is that this call would have been removed if ‘XXXTypeNode’ was edited manually (no ‘super.afterCreate();’). Another is that our heuristic for determining “top-lvl-node” failed during model loading.
Whenever NodeManagerUaNode.createInstance/NodeBuilder makes instances, it knows the top-lvl node as it creates it (it is the one returned and it just makes one top-lvl at a time). However, when loading an information model we use a heuristic: nodes that have 0 inverse Aggregate (or subtypes) references are considered as top-lvl nodes and .afterCreate is called for each of them. It is important that it is only called for the top-lvl ones, since the logic in XXXTypeNode(s) might rely on the order, with effectively the top-lvl instance having the last say in the XXXTypeNode.afterCreate that is designed to be edited (i.e. after ‘super.afterCreate();’ SDK has finalized initializing all components and custom logic can be added so that the SDK does not override it).
Maybe this heuristic fails for your model? For example, if the model attaches instances with HasComponent instead of Organizes to some folder structure then it would fail.
2.
There is potential confusion, as some of the methods are from Codegen and others are what we have added manually (or existed from the era before we had Codegen). Also, some of the Codegen methods are for doing an UA Method call (or partially simulating one in the server) or setting the data of a component.
For example, the ConditionTypeNodeBase.disable() is related to the interface, ConditionType.disable(), which is actually used to Call the ‘Disable’ Method of the ConditionType. It exist for both sides, client and server. It might not make that much sense here for the server side, but this is general logic for all generated types (and we do Codegen the core information model in the SDK). Note that this ConditionTypeNodeBase.disable() specifically calls the implementation of ConditionTypeNode.onDisable, it bypasses the EventManager logic as it is done directly in the nodes, i.e. no CallRequest is made).
The AlarmConditionTypeNode.setActive(boolean) on the other hand is a manually created helper (or probably this existed before we had Codegen), since doing interaction alarmNode.getActiveStateNode().setId(..). Plus in addition it has custom logic if the Optional ShelvingState exists for the instance.
And as an example AcknowledgeableConditionTypeNodeBase.setEnabledState(LocalizedText) is a Codegen setter for setting the Value of the EnabledState component (just as an example, in practice this should not be used directly, but instead ConditionTypeNode.setEnabled(boolean) as it properly sets the Id to the given value and then that interaction flows to overriden logic in TwoStateVariableTypeNode.setId(Boolean) (not TwoStateVariableTypeNodeBase) which sets the value of the EnabledState to the proper LocalizedText which are set by ConditionTypeNode.afterCreate())
In general I would say it is best to call methods named setXXX and then trigger manually (yes, the above kinda conflicts with this one, for the ones having LocalizedText, see if we have a related method with boolean and use that instead). This could probably use a lot of improvements … some day.
The ConditionTypeNode Disable Method implementation does send the event because https://reference.opcfoundatio…..00-9/5.5.2 “When the Condition instance enters the Disabled state, the Retain Property of this Condition shall be set to False by the Server to indicate to the Client that the Condition instance is currently not of interest to Clients.”
in addition it says “In normal processing when a Client receives an Event with the Retain flag set to False, the Client should consider this as a ConditionBranch that is no longer of interest, in the case of a “current Alarm display” the ConditionBranch would be removed from the display.” Thus when a Client calls Disable this information must be relayed to all subscribers of the event. Though this logic is … very old.
P.S.
I am aware that best would be if the Codegen versions had some specific qualifier e.g. callXXX instead of just xXX as the method name so they would be easier to differentiate. Wont happen in SDK 5.x, but not sure can we make this in 6.0.0 either, but we will have to discuss internally (it would resolve some other issues as well with naming conflicts, but the API breakage might be too much).
1 Guest(s)

Log In
Register