03/06/2024
Hello,
I want to create a c# OpcUa server to simulate the presence of a Siemens 1500 PLC Opc Ua server to test our application. I've managed to create the server with simple directories and variables, but I can't manage to create an ExtensionObject variable with a given structure. I'd like to do it directly in the code and not go through xml and OpcUa Compiler.
This is how I created my slot object:
public class Slot : IEncodeable
{
public int ProductId { get; set; }
public string ProductName { get; set; }
public int[] Location { get; set; }
public int[] Rotation { get; set; }
public int[] Dimension { get; set; }public Slot()
{
Location = new int[3] { 0, 0, 0 };
Rotation = new int[3] { 0, 0, 0 };
Dimension = new int[3] { 0, 0, 0 };
}// Ajoutez ici les implémentations de Clone(), Encode() et Decode()...
public bool IsEqual(IEncodeable encodeable)
{
if (encodeable == null || !(encodeable is Slot))
{
return false;
}Slot other = (Slot)encodeable;
if (ProductId != other.ProductId)
return false;
if (ProductName != other.ProductName)
return false;
if (!CompareArrays(Location, other.Location))
return false;
if (!CompareArrays(Rotation, other.Rotation))
return false;
if (!CompareArrays(Dimension, other.Dimension))
return false;return true;
}public object Clone()
{
// Créez une copie superficielle avec MemberwiseClone()
return this.MemberwiseClone();
}private bool CompareArrays(T[] array1, T[] array2)
{
if (array1 == null && array2 == null)
return true;
if (array1 == null || array2 == null)
return false;
if (array1.Length != array2.Length)
return false;for (int i = 0; i < array1.Length; i++)
{
if (!Equals(array1[i], array2[i]))
return false;
}
return true;
}
public void Encode(IEncoder encoder)
{
encoder.WriteInt32("ProductId", ProductId);
encoder.WriteString("ProductName", ProductName);
encoder.WriteInt32Array("Location", Location);
encoder.WriteInt32Array("Rotation", Rotation);
encoder.WriteInt32Array("Dimension", Dimension);
}public void Decode(IDecoder decoder)
{
ProductId = decoder.ReadInt32("ProductId");
ProductName = decoder.ReadString("ProductName");
Location = decoder.ReadInt32Array("Location").ToArray();
Rotation = decoder.ReadInt32Array("Rotation").ToArray();
Dimension = decoder.ReadInt32Array("Dimension").ToArray();
}public ExpandedNodeId TypeId => new ExpandedNodeId("ns=2;n=SlotDataType", 2);
public ExpandedNodeId BinaryEncodingId => new ExpandedNodeId("ns=2;n=SlotDataType.BinaryEncoding", 2);
public ExpandedNodeId XmlEncodingId => new ExpandedNodeId("ns=2;n=SlotDataType.XmlEncoding", 2);}
Write the type in CustomNodeManager2 this way:
protected void CreateSlotDataTypeState()
{
var dataTypeId = new NodeId("SlotDataType", NamespaceIndex);
var binaryEncodingId = new NodeId("SlotDataType.BinaryEncoding", NamespaceIndex);// Créer les champs pour le type de données Slot
List fields = new List
{
new StructureField
{
Name = "ProductId",
DataType = DataTypeIds.Int32,
ValueRank = ValueRanks.Scalar,
IsOptional = false
},
new StructureField
{
Name = "ProductName",
DataType = DataTypeIds.String,
ValueRank = ValueRanks.Scalar,
IsOptional = false
},
new StructureField
{
Name = "Location",
DataType = DataTypeIds.Int32,
ValueRank = ValueRanks.OneDimension,
ArrayDimensions = new uint[] { 3 },
IsOptional = false
},
new StructureField
{
Name = "Rotation",
DataType = DataTypeIds.Int32,
ValueRank = ValueRanks.OneDimension,
ArrayDimensions = new uint[] { 3 },
IsOptional = false
},
new StructureField
{
Name = "Dimension",
DataType = DataTypeIds.Int32,
ValueRank = ValueRanks.OneDimension,
ArrayDimensions = new uint[] { 3 },
IsOptional = false
}
};// Créer la définition de la structure pour Slot
StructureDefinition structureDefinition = new StructureDefinition
{
BaseDataType = DataTypeIds.Structure,
StructureType = StructureType.Structure,
Fields = fields.ToArray()
};// Enregistrer le type de données Slot
var slotDataType = new DataTypeState
{
NodeId = dataTypeId,
BrowseName = new QualifiedName("SlotDataType", NamespaceIndex),
DisplayName = "Slot",
Description = "A custom Slot data type.",
IsAbstract = false,
WriteMask = AttributeWriteMask.None,
UserWriteMask = AttributeWriteMask.None,
DataTypeDefinition = new ExtensionObject(structureDefinition)};
var binaryEncoding = new BaseObjectState(slotDataType)
{
NodeId = binaryEncodingId,
BrowseName = new QualifiedName("BinaryEncoding", NamespaceIndex),
DisplayName = new LocalizedText("Binary Encoding"),
// Autres propriétés
};// Lier l'encodage binaire au type de données
slotDataType.AddReference(ReferenceTypes.HasEncoding, false, binaryEncoding.NodeId);
binaryEncoding.AddReference(ReferenceTypes.HasEncoding, true, slotDataType.NodeId);// Enregistrement du type Slot.
// EncodeableFactory.GlobalFactory.AddEncodeableType(typeof(Slot));
AddPredefinedNode(SystemContext, binaryEncoding);
// Ajoutez le DataTypeNode à l'espace d'adressage du serveur
AddPredefinedNode(SystemContext, slotDataType);
}
and add my variable:
// Production Unit
FolderState pus = CreateFolder(null, "Pus");
pus.AddReference(ReferenceTypes.Organizes, true, ObjectIds.ObjectsFolder);
references.Add(new NodeStateReference(ReferenceTypes.Organizes, false, pus.NodeId));
pus.EventNotifier = EventNotifiers.SubscribeToEvents;
AddRootNotifier(pus);// Create a variable node using the Slot data type
for (int i = 0; i < 2; i++)
{
FolderState currentPu = CreateFolder(pus, $"Pu_{i}");
currentPu.AddReference(ReferenceTypes.Organizes, true, ObjectIds.ObjectsFolder);
currentPu.EventNotifier = EventNotifiers.SubscribeToEvents;NodeId slotDataTypeId = new NodeId("SlotDataType", NamespaceIndex);
Slot slot = new Slot
{
ProductId = 1,
ProductName = "Product A",
Location = new int[3] { 0, 0, 0 },
Rotation = new int[3] { 0, 0, 0 },
Dimension = new int[3] { 0, 0, 0 }
};ExtensionObject extensionObject = new ExtensionObject(slot.BinaryEncodingId,slot);
// Créez ici la variable en utilisant le type de données 'Slot'.
var slotVariable = new BaseDataVariableState(currentPu)
{
NodeId = new NodeId($"SlotVariable_{i}", NamespaceIndex),
BrowseName = new QualifiedName($"SlotVariable_{i}", NamespaceIndex),
DisplayName = $"Slot Variable {i}",
TypeDefinitionId = VariableTypeIds.BaseDataVariableType,
DataType = slotDataTypeId, // Utilisez l'NodeId du type de données 'Slot'.
ValueRank = ValueRanks.Scalar,
AccessLevel = AccessLevels.CurrentReadOrWrite,
UserAccessLevel = AccessLevels.CurrentReadOrWrite,
Historizing = false,
Value = extensionObject};
// Ajoutez la variable au modèle d'adresse
currentPu.AddChild(slotVariable);
}
AddPredefinedNode(SystemContext, pus);
The object is created, but the OpcUa client can't decode the Byte[] object.
Thank you in advance for your help.
Best regards.
2 Guest(s)