It seems like a simple thing, but I’m pretty stumped on this. Let’s say you define two types in your model XML, Type1 and Type2. The *instance* of Type1 has multiple children of Type2. From what I understand (possibly incorrectly), the predefined uanodes won’t be built correctly unless the type definition hierarchy matches the instance definition hierarchy (see my conclusion in this thread: https://opcfoundation.org/foru…..ler/#p3065). What is the correct way to specify the type definition for Type1 so that it will allow a variable number of Type2 children? Just specifying a single Type2 child in Type1’s type definition does not seem to do the trick, nor does leaving Type2 out entirely.
05/30/2017
When a type is instantiated only the mandatory components are created.
If you want optional components to appear you need to replicate the tree down to each optional component that you want to appear.
However, you only need the BrowseName and ReferenceTypes. All other information is copied from the TypeDefinition.
This design is because the tool can only instantiate optional components if it has been instructed to do so (the optional designation would be meaningless if it did not behave this way). Specifying the tree in the ModelDesign is how a user instructs the tool to create optional components.
This design also means optional components need to be explicitly specified for every instance where you want them to appear.
I’m finding that when I mark a sub-type as Optional in my type hierarchy, like this:
<ObjectType SymbolicName="COMPANY:ThingCategoryType" BaseType="ua:FolderType"> <BrowseName>Thing Category</BrowseName> <Children> <Object SymbolicName="COMPANY:Thing" ModellingRule="Optional" TypeDefinition="COMPANY:ThingType" SupportsEvents="true"> <BrowseName>Thing</BrowseName> </Object> </Children> </ObjectType>
What happens is, when initializing the children of my ThingCategoryType instance, NodeState::Initialize does not give the proper type to the ThingType instance. NodeState::Initialize calls FindChild, but the generated ThingCategoryType override of FindChild does not include a case for ThingType, which means the resulting object is of type BaseInstanceState rather than a ThingTypeState.
All I’m really trying to do is allow myself to create an instance of ThingCategoryType that has zero or more ThingType instances as children.
I should have included the instance portion of my XML, for reference:
<Object> <BrowseName>THINGCATEGORY1</BrowseName> <DisplayName>Thing Category 1</DisplayName> <Children> <Object> <BrowseName>THING1</BrowseName> <DisplayName>Thing 1</DisplayName> <Description>Thing 1</Description> <References> <Reference> <ReferenceType>ua:HasTypeDefinition</ReferenceType> <TargetId>COMPANY:ThingType</TargetId> </Reference> </References> </Object> <Object> <BrowseName>THING2</BrowseName> <DisplayName>Thing 2</DisplayName> <Description>Thing 2</Description> <References> <Reference> <ReferenceType>ua:HasTypeDefinition</ReferenceType> <TargetId>COMPANY:ThingType</TargetId> </Reference> </References> </Object> </Children> <References> <Reference> <ReferenceType>ua:HasTypeDefinition</ReferenceType> <TargetId>COMPANY:ThingCategoryType</TargetId> </Reference> </References> </Object>
05/30/2017
Unfortunately, there is not an example for this case.
When you instantiate an instance in code you must manually create members for all optional types you need for the same reason you need to create the tree in the design file (i.e. the code can’t know which optional components to create unless it is told).
e.g.:
BoilerState boiler = new BoilerState(null);
boiler.OptionalProperty = new PropertyState(boiler);
string name = Utils.Format(“Boiler #{0}”, unitNumber);
boiler.Create(
context,
null,
new QualifiedName(name, m_namespaceIndex),
null,
true);
I want to make sure I understand correctly. Do you mean that there’s no way, in a predefined nodes file, to create a custom type that has an indefinite number of children? Like, the “Optional” rule just says “this child can exist or not”, but there’s no way to say “there will be one or more of these children in the object instance”?
I can’t quite wrap my head around how the code approach you’ve posted could work in my case, but I’ve had another thought. Perhaps what I need to do is create a *different* type for each instance that has a different number of Things as children? So there would be a ThingCategory1Type that has one Thing child, and a ThingCategory2Type that has two Thing children, and so on? It would be cumbersome to look at, as the XML could get very large if there are a lot of different objects with different numbers of children…but since we’re generating the model XML programmatically, it would at least be do-able.
05/30/2017
Like, the “Optional” rule just says “this child can exist or not”, but there’s no way to say “there will be one or more of these children in the object instance”?
Use the MandatoryPlaceholder or OptionalPlaceholder ModellingRules.
This will create an information model that specifies what you want.
But any instances need to be manually added in code.
If I use OptionalPlaceholder or MandatoryPlaceholder as the ModellingRule of my Thing child in the ThingCategoryType definition, the generated ThingCategoryState class does *not* override FindChild, which means that the original problem still occurs (NodeState initialize does not apply the correct type to the Thing instances):
<ObjectType SymbolicName="COMPANY:ThingCategoryType" BaseType="ua:FolderType"> <BrowseName>Thing Category</BrowseName> <Children> <Object SymbolicName="COMPANY:Thing" ModellingRule="MandatoryPlaceholder" TypeDefinition="COMPANY:ThingType" SupportsEvents="true"> <BrowseName>Thing</BrowseName> </Object> </Children> </ObjectType>
The only way I can get the FindChild override to include a case (in the switch statement) for Thing is to use the Mandatory ModellingRule…but of course, that rule doesn’t allow me to specify an indefinite number of Thing instances within a ThingCategory.
Maybe the piece I’m still missing is related to your suggestion about manually adding instances in code. I still can’t wrap my head around that one, however. Our customers will be supplying their own instances; they can’t be hard-coded. We have no way to know how many Things their server will have, or what their display names are, etc.
05/30/2017
Yes, using MandatoryPlaceholder or OptionalPlaceholder ModellingRules means you get no help from the code generator.
You will have to manually add the additional fields you need (the partial class declarations allow you to put this handcrafted code in a separate file).
But that is the unavoidable consequence of the model you have.
Mandatory/Optional are only for use with a single child with a BrowseName specified in the Type.
1 Guest(s)