diff --git a/EDSEditorGUI/.gitignore b/EDSEditorGUI/.gitignore index a16e7a5..a0ac84a 100644 --- a/EDSEditorGUI/.gitignore +++ b/EDSEditorGUI/.gitignore @@ -1 +1,2 @@ -version.txt \ No newline at end of file +version.txt +EDSEditorGUI.csproj.user diff --git a/EDSEditorGUI/Form1.cs b/EDSEditorGUI/Form1.cs index 1d051a9..f273ae0 100644 --- a/EDSEditorGUI/Form1.cs +++ b/EDSEditorGUI/Form1.cs @@ -278,12 +278,13 @@ private void openToolStripMenuItem_Click(object sender, EventArgs e) Warnings.warning_list.Clear(); OpenFileDialog odf = new OpenFileDialog(); - odf.Filter = "All supported files (*.xdd;*.xdc;*.xpd;*.eds;*.dcf)|*.xdd;*.xdc;*.xpd;*.eds;*.dcf|" + odf.Filter = "All supported files (*.xdd;*.xdc;*.xpd;*.eds;*.dcf;*.binpb;*.json)|*.xdd;*.xdc;*.xpd;*.eds;*.dcf;*.binpb;*.json|" + "CANopen XDD (*.xdd)|*.xdd|" + "CANopen XDC (*.xdc)|*.xdc|" + "CANopen XPD (*.xpd)|*.xpd|" + "Electronic Data Sheet (*.eds)|*.eds|" - + "Device Configuration File (*.dcf)|*.dcf"; + + "Device Configuration File (*.dcf)|*.dcf|" + + "CANopen Protobuffer (*.binpb;*.json)|*.binpb;*.json"; if (odf.ShowDialog() == DialogResult.OK) { @@ -304,6 +305,14 @@ private void openToolStripMenuItem_Click(object sender, EventArgs e) openEDSfile(odf.FileName, InfoSection.Filetype.File_DCF); break; + case ".binpb": + OpenProtobufferfile(odf.FileName, false); + break; + + case ".json": + OpenProtobufferfile(odf.FileName, true); + break; + default: return; @@ -369,6 +378,49 @@ private void openXDDfile(string path) } + private void OpenProtobufferfile(string path, bool json) + { + Warnings.warning_list.Clear(); + + try + { + EDSsharp eds; + + CanOpenXDD_1_1 coxml_1_1 = new CanOpenXDD_1_1(); + eds = coxml_1_1.ReadProtobuf(path, json); + + if (eds == null) + { + return; + } + + eds.projectFilename = path; + + DeviceView device = new DeviceView(eds, network); + + device.UpdateODViewForEDS += Device_UpdateODViewForEDS; + eds.OnDataDirty += Eds_onDataDirty; + + tabControl1.TabPages.Add(eds.di.ProductName); + tabControl1.TabPages[tabControl1.TabPages.Count - 1].Controls.Add(device); + + device.Dock = DockStyle.Fill; + device.dispatch_updateOD(); + + network.Add(eds); + } + catch (Exception ex) + { + Warnings.warning_list.Add(ex.ToString()); + } + + if (Warnings.warning_list.Count != 0) + { + WarningsFrm frm = new WarningsFrm(); + frm.Show(); + } + } + private void Device_UpdateODViewForEDS(object sender, UpdateODViewEventArgs e) { foreach (TabPage page in tabControl1.TabPages) @@ -484,6 +536,8 @@ private void exportDeviceFileToolStripMenuItem_Click(object sender, EventArgs e) sfd.Filter = "CANopen XDD v1.1 stripped (*.xdd)|*.xdd|" //must be first or change condition below + "Electronic Data Sheet (*.eds)|*.eds|" + "Device Configuration File (*.dcf)|*.dcf|" + + "Protobuffer binary, experimental (*.binpb)|*.binpb|" + + "Protobuffer JSON, experimental (*.json)|*.json|" + "Documentation (*.md)|*.md|" + "CANopen XDD v1.0, old (*.xdd)|*.xdd"; @@ -605,6 +659,20 @@ void dosave(DeviceView dv, string FileName, bool xddfileVersion_1_1, bool stripp dv.eds.xddfilename_1_0 = FileName; } break; + + case ".binpb": + case ".json": + Warnings.warning_list.Clear(); + + CanOpenXDD_1_1 copb = new CanOpenXDD_1_1(); + copb.WriteProtobuf(FileName, dv.eds, Path.GetExtension(FileName) == ".json"); + + if (Warnings.warning_list.Count != 0) + { + WarningsFrm frm = new WarningsFrm(); + frm.Show(); + } + break; } dv.dispatch_updateOD(); @@ -683,6 +751,12 @@ void OpenRecentFile(object sender, EventArgs e) if (ext == ".xdd" || ext == ".xdc" || ext == ".xpd") openXDDfile(filepath); + + else if (ext == ".binpb") + OpenProtobufferfile(filepath, false); + else if (ext == ".json") + OpenProtobufferfile(filepath, true); + if ( ext == ".eds" ) openEDSfile(filepath, InfoSection.Filetype.File_EDS); if (ext == ".dcf") @@ -1203,6 +1277,14 @@ private void ODEditor_MainForm_DragDrop(object sender, DragEventArgs e) openXDDNetworkfile(fileName); break; + case ".binpb": + OpenProtobufferfile(fileName, false); + break; + + case ".json": + OpenProtobufferfile(fileName, true); + break; + default: break; diff --git a/libEDSsharp/CanOpenXDD_1_1.cs b/libEDSsharp/CanOpenXDD_1_1.cs index 31f20d9..1e83122 100644 --- a/libEDSsharp/CanOpenXDD_1_1.cs +++ b/libEDSsharp/CanOpenXDD_1_1.cs @@ -26,6 +26,10 @@ You should have received a copy of the GNU General Public License using System.Xml; using System.Xml.Serialization; using CanOpenXSD_1_1; +using LibCanOpen; +using Google.Protobuf; +using Google.Protobuf.WellKnownTypes; +using System.Linq; namespace libEDSsharp { @@ -153,6 +157,66 @@ public void WriteXML(string file, EDSsharp eds, string gitVersion, bool deviceCo stream.Close(); } + /// + /// Read protobuffer file into EDSsharp object + /// + /// Name of the protobuffer file + /// read as JSON string or binary wireformat + /// EDSsharp object + public EDSsharp ReadProtobuf(string file, bool json) + { + CanOpenDevice devCanOpen; + + // read the protobuffer message in json format or binary wireformat + if (json) + { + var parserConfig = new JsonParser.Settings(100); + var parser = new JsonParser(parserConfig); + devCanOpen = parser.Parse(File.ReadAllText(file)); + } + else + { + using (var input = File.OpenRead(file)) + { + devCanOpen = CanOpenDevice.Parser.ParseFrom(input); + } + } + + /* first convert to XDD, then to EDSsharp (for now) */ + ISO15745ProfileContainer devXdd = ConvertFromProtobuf(devCanOpen, file, true, false); + + return Convert(devXdd); + } + + /// + /// Write protobuffer file from EDSsharp object + /// + /// Name of the protobuffer file + /// EDSsharp object + /// write as JSON string or binary wireformat + public void WriteProtobuf(string file, EDSsharp eds, bool json) + { + /* first convert to XDD, then to protobuffer (for now) */ + ISO15745ProfileContainer devXdd = Convert(eds, Path.GetFileName(file), true, false); + CanOpenDevice dev = ConvertToProtobuf(devXdd); + + // write the protobuffer message in json format or binary wireformat + if (json) + { + var formatterConfig = new JsonFormatter.Settings(true).WithIndentation().WithFormatDefaultValues(true); + var formatter = new JsonFormatter(formatterConfig); + var rawJsonString = formatter.Format(dev); + File.WriteAllText(file, rawJsonString); + } + else + { + using (var output = File.Create(file)) + { + dev.WriteTo(output); + } + } + } + private parameterTemplateAccess ConvertAccessType(EDSsharp.AccessType edsAccessType) { switch (edsAccessType) @@ -167,6 +231,23 @@ private parameterTemplateAccess ConvertAccessType(EDSsharp.AccessType edsAccessT } } + private parameterTemplateAccess ConvertAccessType(OdSubObject subEntry) + { + switch (subEntry.Sdo) + { + case LibCanOpen.OdSubObject.Types.AccessSDO.Ro: return parameterTemplateAccess.read; + case LibCanOpen.OdSubObject.Types.AccessSDO.Wo: return parameterTemplateAccess.write; + case LibCanOpen.OdSubObject.Types.AccessSDO.Rw: + switch (subEntry.Pdo) + { + case LibCanOpen.OdSubObject.Types.AccessPDO.R: return parameterTemplateAccess.readWriteInput; + case LibCanOpen.OdSubObject.Types.AccessPDO.T: return parameterTemplateAccess.readWriteOutput; + default: return parameterTemplateAccess.readWrite; + } + default: return parameterTemplateAccess.noAccess; + } + } + private EDSsharp.AccessType ConvertAccessType(parameterTemplateAccess xddAccessType) { switch (xddAccessType) @@ -181,6 +262,34 @@ private EDSsharp.AccessType ConvertAccessType(parameterTemplateAccess xddAccessT } } + private void ConvertAccessType(parameterTemplateAccess xddAccessType, OdSubObject subEntry) + { + switch (xddAccessType) + { + case parameterTemplateAccess.@const: + case parameterTemplateAccess.read: + subEntry.Sdo = LibCanOpen.OdSubObject.Types.AccessSDO.Ro; + break; + case parameterTemplateAccess.readWrite: + subEntry.Sdo = LibCanOpen.OdSubObject.Types.AccessSDO.Rw; + break; + case parameterTemplateAccess.readWriteInput: + subEntry.Sdo = LibCanOpen.OdSubObject.Types.AccessSDO.Rw; + subEntry.Pdo = LibCanOpen.OdSubObject.Types.AccessPDO.T; + break; + case parameterTemplateAccess.readWriteOutput: + subEntry.Sdo = LibCanOpen.OdSubObject.Types.AccessSDO.Rw; + subEntry.Pdo = LibCanOpen.OdSubObject.Types.AccessPDO.R; + break; + case parameterTemplateAccess.write: + subEntry.Sdo = LibCanOpen.OdSubObject.Types.AccessSDO.Wo; + break; + default: + subEntry.Sdo = LibCanOpen.OdSubObject.Types.AccessSDO.No; + break; + } + } + private Items1ChoiceType ConvertDataType (ODentry od) { UInt32 byteLength; @@ -280,6 +389,131 @@ private DataType ConvertDataType(Items1ChoiceType choiceType, string defaultValu } } + private Items1ChoiceType ConvertDataType(OdSubObject subEntry) + { + UInt32 byteLength; + bool signed = false; + var dt = subEntry.Type; + + switch (dt) + { + case LibCanOpen.OdSubObject.Types.DataType.Boolean: return Items1ChoiceType.BOOL; + case LibCanOpen.OdSubObject.Types.DataType.Integer8: return Items1ChoiceType.SINT; + case LibCanOpen.OdSubObject.Types.DataType.Integer16: return Items1ChoiceType.INT; + case LibCanOpen.OdSubObject.Types.DataType.Integer32: return Items1ChoiceType.DINT; + case LibCanOpen.OdSubObject.Types.DataType.Integer64: return Items1ChoiceType.LINT; + case LibCanOpen.OdSubObject.Types.DataType.Unsigned8: return Items1ChoiceType.USINT; + case LibCanOpen.OdSubObject.Types.DataType.Unsigned16: return Items1ChoiceType.UINT; + case LibCanOpen.OdSubObject.Types.DataType.Unsigned32: return Items1ChoiceType.UDINT; + case LibCanOpen.OdSubObject.Types.DataType.Unsigned64: return Items1ChoiceType.ULINT; + case LibCanOpen.OdSubObject.Types.DataType.Real32: return Items1ChoiceType.REAL; + case LibCanOpen.OdSubObject.Types.DataType.Real64: return Items1ChoiceType.LREAL; + case LibCanOpen.OdSubObject.Types.DataType.VisibleString: return Items1ChoiceType.STRING; + case LibCanOpen.OdSubObject.Types.DataType.OctetString: return Items1ChoiceType.BITSTRING; + case LibCanOpen.OdSubObject.Types.DataType.UnicodeString: return Items1ChoiceType.WSTRING; + + case LibCanOpen.OdSubObject.Types.DataType.Domain: + subEntry.DefaultValue = ""; + return Items1ChoiceType.BITSTRING; + + default: + subEntry.Type = LibCanOpen.OdSubObject.Types.DataType.Integer32; + return Items1ChoiceType.DINT; + + // transform other non standard values to OCTET_STRING + case LibCanOpen.OdSubObject.Types.DataType.Integer24: byteLength = 3; signed = true; break; + case LibCanOpen.OdSubObject.Types.DataType.Integer40: byteLength = 5; signed = true; break; + case LibCanOpen.OdSubObject.Types.DataType.Integer48: byteLength = 6; signed = true; break; + case LibCanOpen.OdSubObject.Types.DataType.Integer56: byteLength = 7; signed = true; break; + case LibCanOpen.OdSubObject.Types.DataType.Unsigned24: byteLength = 3; break; + case LibCanOpen.OdSubObject.Types.DataType.Unsigned40: byteLength = 5; break; + case LibCanOpen.OdSubObject.Types.DataType.Unsigned48: + case LibCanOpen.OdSubObject.Types.DataType.TimeOfDay: + case LibCanOpen.OdSubObject.Types.DataType.TimeDifference: byteLength = 6; break; + case LibCanOpen.OdSubObject.Types.DataType.Unsigned56: byteLength = 7; break; + } + + // set datatype OCTET_STRING and write default value as a sequence of bytes, little endian, like "56 34 12" + UInt64 value; + try + { + value = signed ? (UInt64)((Int64)new System.ComponentModel.Int64Converter().ConvertFromString(subEntry.DefaultValue)) + : (UInt64)new System.ComponentModel.UInt64Converter().ConvertFromString(subEntry.DefaultValue); + } + catch (Exception) + { + value = 0; + } + + List bytes = new List(); + for (UInt32 i = 0; i < byteLength; i++) + { + bytes.Add(String.Format("{0:X2}", value & 0xFF)); + value >>= 8; + } + subEntry.Type = LibCanOpen.OdSubObject.Types.DataType.OctetString; + subEntry.DefaultValue = string.Join(" ", bytes); + + return Items1ChoiceType.BITSTRING; + } + + private void ConvertDataType(Items1ChoiceType choiceType, OdSubObject OdSubObject) + { + switch (choiceType) + { + case Items1ChoiceType.BOOL: + OdSubObject.Type = LibCanOpen.OdSubObject.Types.DataType.Boolean; + break; + case Items1ChoiceType.CHAR: + case Items1ChoiceType.SINT: + OdSubObject.Type = LibCanOpen.OdSubObject.Types.DataType.Integer8; + break; + case Items1ChoiceType.INT: + OdSubObject.Type = LibCanOpen.OdSubObject.Types.DataType.Integer16; + break; + case Items1ChoiceType.DINT: + OdSubObject.Type = LibCanOpen.OdSubObject.Types.DataType.Integer32; + break; + case Items1ChoiceType.LINT: + OdSubObject.Type = LibCanOpen.OdSubObject.Types.DataType.Integer64; + break; + case Items1ChoiceType.BYTE: + case Items1ChoiceType.USINT: + OdSubObject.Type = LibCanOpen.OdSubObject.Types.DataType.Unsigned8; + break; + case Items1ChoiceType.WORD: + case Items1ChoiceType.UINT: + OdSubObject.Type = LibCanOpen.OdSubObject.Types.DataType.Unsigned16; + break; + case Items1ChoiceType.DWORD: + case Items1ChoiceType.UDINT: + OdSubObject.Type = LibCanOpen.OdSubObject.Types.DataType.Unsigned32; + break; + case Items1ChoiceType.LWORD: + case Items1ChoiceType.ULINT: + OdSubObject.Type = LibCanOpen.OdSubObject.Types.DataType.Unsigned64; + break; + case Items1ChoiceType.REAL: + OdSubObject.Type = LibCanOpen.OdSubObject.Types.DataType.Real32; + break; + case Items1ChoiceType.LREAL: + OdSubObject.Type = LibCanOpen.OdSubObject.Types.DataType.Real64; + break; + case Items1ChoiceType.STRING: + OdSubObject.Type = LibCanOpen.OdSubObject.Types.DataType.VisibleString; + break; + case Items1ChoiceType.WSTRING: + OdSubObject.Type = LibCanOpen.OdSubObject.Types.DataType.UnicodeString; + break; + case Items1ChoiceType.BITSTRING: + OdSubObject.Type = OdSubObject.DefaultValue == "" ? LibCanOpen.OdSubObject.Types.DataType.Domain : LibCanOpen.OdSubObject.Types.DataType.OctetString; + break; + default: + OdSubObject.Type = LibCanOpen.OdSubObject.Types.DataType.Integer32; + break; + } + } + private void WriteVar(parameter devPar, ODentry od) { if (od.accesstype == EDSsharp.AccessType.UNKNOWN && od.parent != null && od.parent.objecttype == ObjectType.ARRAY) @@ -309,6 +543,99 @@ private void WriteVar(parameter devPar, ODentry od) } } + private void WriteVar(parameter devPar, OdSubObject subEntry) + { + devPar.access = ConvertAccessType(subEntry); + + devPar.Items1 = new object[] { new object() }; + devPar.Items1ElementName = new Items1ChoiceType[] { ConvertDataType(subEntry) }; + + if (subEntry.DefaultValue != null && subEntry.DefaultValue != "") + devPar.defaultValue = new defaultValue { value = subEntry.DefaultValue }; + + if (subEntry.LowLimit != null && subEntry.LowLimit != "" && subEntry.HighLimit != null && subEntry.HighLimit != "") + { + devPar.allowedValues = new allowedValues + { + range = new range[] + { + new range + { + minValue = new rangeMinValue { value = subEntry.LowLimit }, + maxValue = new rangeMaxValue { value = subEntry.HighLimit } + } + } + }; + } + } + + private void ConvertXddProperties(property[] properties, OdObject entry, OdSubObject subEntry) + { + if (properties != null) + { + foreach (property prop in properties) + { + switch (prop.name) + { + case "CO_disabled": entry.Disabled = prop.value == "true"; break; + case "CO_countLabel": entry.CountLabel = prop.value ?? ""; break; + case "CO_storageGroup": entry.StorageGroup = prop.value ?? ""; break; + case "CO_flagsPDO": entry.FlagsPDO = prop.value == "true"; break; + case "CO_accessSRDO": + if (prop.value != null) + switch (prop.value) + { + case "rx": subEntry.Srdo = LibCanOpen.OdSubObject.Types.AccessSRDO.Rx; break; + case "tx": subEntry.Srdo = LibCanOpen.OdSubObject.Types.AccessSRDO.Tx; break; + case "trx": subEntry.Srdo = LibCanOpen.OdSubObject.Types.AccessSRDO.Trx; break; + case "no": subEntry.Srdo = LibCanOpen.OdSubObject.Types.AccessSRDO.No; break; + } + break; + case "CO_stringLengthMin": + try { subEntry.StringLengthMin = System.Convert.ToUInt16(prop.value); } + catch (Exception) { subEntry.StringLengthMin = 0; } + break; + } + } + } + } + + private property[] ConvertXddProperties(OdObject entry) + { + var props = new List(); + + if (entry.Disabled) + props.Add(new property { name = "CO_disabled", value = "true" }); + if (entry.CountLabel != "") + props.Add(new property { name = "CO_countLabel", value = entry.CountLabel }); + if (entry.StorageGroup != "RAM" && entry.StorageGroup != "") + props.Add(new property { name = "CO_storageGroup", value = entry.StorageGroup }); + if (entry.FlagsPDO) + props.Add(new property { name = "CO_flagsPDO", value = "true" }); + + return props.ToArray(); + } + + private property[] ConvertXddProperties(OdSubObject subEntry) + { + var props = new List(); + string val; + + switch (subEntry.Srdo) + { + case LibCanOpen.OdSubObject.Types.AccessSRDO.Rx: val = "rx"; break; + case LibCanOpen.OdSubObject.Types.AccessSRDO.Tx: val = "tx"; break; + case LibCanOpen.OdSubObject.Types.AccessSRDO.Trx: val = "trx"; break; + default: val = "no"; break; + } + props.Add(new property { name = "CO_accessSRDO", value = val }); + + if (subEntry.StringLengthMin != 0) + props.Add(new property { name = "CO_stringLengthMin", value = subEntry.StringLengthMin.ToString() }); + + return props.ToArray(); + } + private ISO15745ProfileContainer Convert(EDSsharp eds, string fileName, bool deviceCommissioning, bool stripped) { // Get xdd template from eds (if memorized on xdd file open) @@ -434,7 +761,7 @@ private ISO15745ProfileContainer Convert(EDSsharp eds, string fileName, bool dev if (od.objecttype == ObjectType.VAR) { - try { netObj.PDOmapping = (CANopenObjectListCANopenObjectPDOmapping)Enum.Parse(typeof(CANopenObjectListCANopenObjectPDOmapping), od.PDOtype.ToString()); } + try { netObj.PDOmapping = (CANopenObjectListCANopenObjectPDOmapping)System.Enum.Parse(typeof(CANopenObjectListCANopenObjectPDOmapping), od.PDOtype.ToString()); } catch (Exception) { netObj.PDOmapping = CANopenObjectListCANopenObjectPDOmapping.no; } netObj.PDOmappingSpecified = true; @@ -477,7 +804,7 @@ private ISO15745ProfileContainer Convert(EDSsharp eds, string fileName, bool dev PDOmappingSpecified = true, uniqueIDRef = "UID_SUB_" + subUid }; - try { netSubObj.PDOmapping = (CANopenObjectListCANopenObjectCANopenSubObjectPDOmapping)Enum.Parse(typeof(CANopenObjectListCANopenObjectCANopenSubObjectPDOmapping), subod.PDOtype.ToString()); } + try { netSubObj.PDOmapping = (CANopenObjectListCANopenObjectCANopenSubObjectPDOmapping)System.Enum.Parse(typeof(CANopenObjectListCANopenObjectCANopenSubObjectPDOmapping), subod.PDOtype.ToString()); } catch (Exception) { netSubObj.PDOmapping = CANopenObjectListCANopenObjectCANopenSubObjectPDOmapping.no; } var devSubPar = new parameter { @@ -1007,7 +1334,7 @@ private EDSsharp Convert(ISO15745ProfileContainer container) EDSsharp.AccessType accessType; if (netObj.accessTypeSpecified) { - try { accessType = (EDSsharp.AccessType)Enum.Parse(typeof(EDSsharp.AccessType), netObj.accessType.ToString()); } + try { accessType = (EDSsharp.AccessType)System.Enum.Parse(typeof(EDSsharp.AccessType), netObj.accessType.ToString()); } catch (Exception) { accessType = EDSsharp.AccessType.ro; } } else { @@ -1017,7 +1344,7 @@ private EDSsharp Convert(ISO15745ProfileContainer container) PDOMappingType PDOtype; if (netObj.PDOmappingSpecified) { - try { PDOtype = (PDOMappingType)Enum.Parse(typeof(PDOMappingType), netObj.PDOmapping.ToString()); } + try { PDOtype = (PDOMappingType)System.Enum.Parse(typeof(PDOMappingType), netObj.PDOmapping.ToString()); } catch (Exception) { PDOtype = PDOMappingType.no; } } else @@ -1093,7 +1420,7 @@ private EDSsharp Convert(ISO15745ProfileContainer container) EDSsharp.AccessType subAccessType; if (netSubObj.accessTypeSpecified) { - try { subAccessType = (EDSsharp.AccessType)Enum.Parse(typeof(EDSsharp.AccessType), netSubObj.accessType.ToString()); } + try { subAccessType = (EDSsharp.AccessType)System.Enum.Parse(typeof(EDSsharp.AccessType), netSubObj.accessType.ToString()); } catch (Exception) { subAccessType = EDSsharp.AccessType.ro; } } else @@ -1104,7 +1431,7 @@ private EDSsharp Convert(ISO15745ProfileContainer container) PDOMappingType subPDOtype; if (netSubObj.PDOmappingSpecified) { - try { subPDOtype = (PDOMappingType)Enum.Parse(typeof(PDOMappingType), netSubObj.PDOmapping.ToString()); } + try { subPDOtype = (PDOMappingType)System.Enum.Parse(typeof(PDOMappingType), netSubObj.PDOmapping.ToString()); } catch (Exception) { subPDOtype = PDOMappingType.no; } } else @@ -1199,6 +1526,818 @@ private EDSsharp Convert(ISO15745ProfileContainer container) return eds; } + + private CanOpenDevice ConvertToProtobuf(ISO15745ProfileContainer container) + { + CanOpenDevice dev = new CanOpenDevice(); + + dev.FileInfo = new CanOpen_FileInfo(); + dev.DeviceInfo = new CanOpen_DeviceInfo(); + dev.DeviceCommissioning = new CanOpen_DeviceCommissioning(); + + ProfileBody_Device_CANopen body_device = null; + ProfileBody_CommunicationNetwork_CANopen body_network = null; + ProfileBody_CommunicationNetwork_CANopenApplicationLayers ApplicationLayers = null; + var parameters = new Dictionary(); + + foreach (ISO15745Profile item in container.ISO15745Profile) + { + if (item.ProfileBody.GetType() == typeof(ProfileBody_Device_CANopen)) + body_device = (ProfileBody_Device_CANopen)item.ProfileBody; + else if (item.ProfileBody.GetType() == typeof(ProfileBody_CommunicationNetwork_CANopen)) + body_network = (ProfileBody_CommunicationNetwork_CANopen)item.ProfileBody; + } + + if (body_device != null) + { + //eds.fi.FileName = body_device.fileName ?? ""; + dev.FileInfo.FileVersion = body_device.fileVersion ?? ""; + dev.FileInfo.CreatedBy = body_device.fileCreator ?? ""; + dev.FileInfo.ModifiedBy = body_device.fileModifiedBy ?? ""; + + if (body_device.fileCreationTimeSpecified) + { + dev.FileInfo.CreationTime = Timestamp.FromDateTime(body_device.fileCreationDate.Add(body_device.fileCreationTime.TimeOfDay).ToUniversalTime()); + + } + if (body_device.fileModificationDateSpecified) + { + dev.FileInfo.ModificationTime = Timestamp.FromDateTime(body_device.fileModificationDate.Add(body_device.fileModificationTime.TimeOfDay).ToUniversalTime()); + } + + if (body_device.DeviceIdentity != null) + { + if (body_device.DeviceIdentity.productText != null) + dev.FileInfo.Description = G_label_getDescription(body_device.DeviceIdentity.productText.Items); + if (body_device.DeviceIdentity.vendorName != null) + dev.DeviceInfo.VendorName = body_device.DeviceIdentity.vendorName.Value ?? ""; + if (body_device.DeviceIdentity.productName != null) + dev.DeviceInfo.ProductName = body_device.DeviceIdentity.productName.Value ?? ""; + } + + if (body_device.ApplicationProcess != null + && body_device.ApplicationProcess[0] != null + && body_device.ApplicationProcess[0].parameterList != null) + { + foreach (parameter param in body_device.ApplicationProcess[0].parameterList) + { + if (!parameters.ContainsKey(param.uniqueID)) + parameters.Add(param.uniqueID, param); + } + } + } + + if (body_network != null) + { + ProfileBody_CommunicationNetwork_CANopenTransportLayers TransportLayers = null; + ProfileBody_CommunicationNetwork_CANopenNetworkManagement NetworkManagement = null; + + foreach (object item in body_network.Items) + { + if (item.GetType() == typeof(ProfileBody_CommunicationNetwork_CANopenApplicationLayers)) + ApplicationLayers = (ProfileBody_CommunicationNetwork_CANopenApplicationLayers)item; + else if (item.GetType() == typeof(ProfileBody_CommunicationNetwork_CANopenTransportLayers)) + TransportLayers = (ProfileBody_CommunicationNetwork_CANopenTransportLayers)item; + else if (item.GetType() == typeof(ProfileBody_CommunicationNetwork_CANopenNetworkManagement)) + NetworkManagement = (ProfileBody_CommunicationNetwork_CANopenNetworkManagement)item; + } + + if (TransportLayers != null && TransportLayers.PhysicalLayer != null + && TransportLayers.PhysicalLayer.baudRate != null && TransportLayers.PhysicalLayer.baudRate.supportedBaudRate != null) + { + foreach (ProfileBody_CommunicationNetwork_CANopenTransportLayersPhysicalLayerBaudRateSupportedBaudRate baud in TransportLayers.PhysicalLayer.baudRate.supportedBaudRate) + { + switch (baud.value.ToString()) + { + case "Item10Kbps": dev.DeviceInfo.BaudRate10 = true; break; + case "Item20Kbps": dev.DeviceInfo.BaudRate20 = true; break; + case "Item50Kbps": dev.DeviceInfo.BaudRate50 = true; break; + case "Item125Kbps": dev.DeviceInfo.BaudRate125 = true; break; + case "Item250Kbps": dev.DeviceInfo.BaudRate250 = true; break; + case "Item500Kbps": dev.DeviceInfo.BaudRate500 = true; break; + case "Item800Kbps": dev.DeviceInfo.BaudRate800 = true; break; + case "Item1000Kbps": dev.DeviceInfo.BaudRate1000 = true; break; + case "autobaudRate": dev.DeviceInfo.BaudRateAuto = true; break; + } + } + } + + if (NetworkManagement != null) + { + if (NetworkManagement.CANopenGeneralFeatures != null) + { + dev.DeviceInfo.LssSlave = NetworkManagement.CANopenGeneralFeatures.layerSettingServiceSlave; + } + + if (NetworkManagement.CANopenMasterFeatures != null) + { + dev.DeviceInfo.LssMaster = NetworkManagement.CANopenMasterFeatures.layerSettingServiceMaster; + } + + if (NetworkManagement.deviceCommissioning != null) + { + dev.DeviceCommissioning.NodeId = NetworkManagement.deviceCommissioning.NodeID; + dev.DeviceCommissioning.NodeName = NetworkManagement.deviceCommissioning.nodeName; + try { dev.DeviceCommissioning.Baudrate = System.Convert.ToUInt32(NetworkManagement.deviceCommissioning.actualBaudRate); } + catch (Exception) { dev.DeviceCommissioning.Baudrate = 0; } + } + } + + if (ApplicationLayers != null) + { + if (ApplicationLayers.CANopenObjectList != null && ApplicationLayers.CANopenObjectList.CANopenObject != null) + { + foreach (CANopenObjectListCANopenObject netObj in ApplicationLayers.CANopenObjectList.CANopenObject) + { + if (netObj.index == null || netObj.index.Length != 2) + continue; + + string index = netObj.index[0].ToString("X2") + netObj.index[1].ToString("X2"); + + // some properties for object, set to default, changed by netObj + LibCanOpen.OdObject.Types.ObjectType objectType = LibCanOpen.OdObject.Types.ObjectType.Unspecified; + + // some properties for sub objects, set to default, changed by netObj or netSubObj + LibCanOpen.OdSubObject.Types.DataType dataType = LibCanOpen.OdSubObject.Types.DataType.Unspecified; + LibCanOpen.OdSubObject.Types.AccessSDO accessSDO = LibCanOpen.OdSubObject.Types.AccessSDO.No; + LibCanOpen.OdSubObject.Types.AccessPDO accessPDO = LibCanOpen.OdSubObject.Types.AccessPDO.No; + + if (System.Enum.IsDefined(typeof(LibCanOpen.OdObject.Types.ObjectType), (Int32)netObj.objectType)) + { + objectType = (LibCanOpen.OdObject.Types.ObjectType)netObj.objectType; + } + + if (netObj.dataType != null && netObj.dataType.Length == 1) + { + if (System.Enum.IsDefined(typeof(LibCanOpen.OdSubObject.Types.DataType), netObj.dataType[0])) + { + dataType = (LibCanOpen.OdSubObject.Types.DataType)netObj.dataType[0]; + } + } + + // accessType in xdd may be specified by netObj.accessType or inside netObj.uniqueIDRef ?? + if (netObj.accessTypeSpecified) + { + switch (netObj.accessType) + { + case CANopenObjectListCANopenObjectAccessType.@const: + case CANopenObjectListCANopenObjectAccessType.ro: accessSDO = LibCanOpen.OdSubObject.Types.AccessSDO.Ro; break; + case CANopenObjectListCANopenObjectAccessType.wo: accessSDO = LibCanOpen.OdSubObject.Types.AccessSDO.Wo; break; + case CANopenObjectListCANopenObjectAccessType.rw: accessSDO = LibCanOpen.OdSubObject.Types.AccessSDO.Rw; break; + } + } + + if (netObj.PDOmappingSpecified) + { + switch (netObj.PDOmapping) + { + case CANopenObjectListCANopenObjectPDOmapping.RPDO: accessPDO = LibCanOpen.OdSubObject.Types.AccessPDO.R; break; + case CANopenObjectListCANopenObjectPDOmapping.TPDO: accessPDO = LibCanOpen.OdSubObject.Types.AccessPDO.T; break; + case CANopenObjectListCANopenObjectPDOmapping.optional: accessPDO = LibCanOpen.OdSubObject.Types.AccessPDO.Tr; break; + } + } + + OdObject entry = new OdObject() + { + Name = netObj.name ?? "", + Alias = netObj.denotation ?? "", + Type = objectType + }; + + OdSubObject subEntry0 = new OdSubObject() + { + Type = dataType, + Sdo = accessSDO, + Pdo = accessPDO, + DefaultValue = netObj.defaultValue ?? "", + ActualValue = netObj.actualValue ?? "", + LowLimit = netObj.lowLimit ?? "", + HighLimit = netObj.highLimit ?? "" + }; + + if (netObj.uniqueIDRef != null && netObj.uniqueIDRef != "" && parameters.ContainsKey(netObj.uniqueIDRef)) + { + parameter devPar = parameters[netObj.uniqueIDRef]; + + entry.Description = G_label_getDescription(devPar.Items); + + ConvertAccessType(devPar.access, subEntry0); + + if (devPar.defaultValue != null && devPar.defaultValue.value != null) + subEntry0.DefaultValue = devPar.defaultValue.value; + + if (devPar.Items1 != null && devPar.Items1ElementName != null) + ConvertDataType(devPar.Items1ElementName[0], subEntry0); + + if (devPar.actualValue != null && devPar.actualValue.value != null) + subEntry0.ActualValue = devPar.actualValue.value; + + if (devPar.denotation != null) + subEntry0.Alias = G_label_getDescription(devPar.denotation.Items); + + if (devPar.allowedValues != null && devPar.allowedValues.range != null && devPar.allowedValues.range[0] != null) + { + range r = devPar.allowedValues.range[0]; + if (r.minValue != null) subEntry0.LowLimit = r.minValue.value ?? ""; + if (r.maxValue != null) subEntry0.HighLimit = r.maxValue.value ?? ""; + } + + ConvertXddProperties(devPar.property, entry, subEntry0); + } + + if (netObj.CANopenSubObject == null) + { + entry.SubObjects.Add("00", subEntry0); + } + else + { + foreach (CANopenObjectListCANopenObjectCANopenSubObject netSubObj in netObj.CANopenSubObject) + { + if (netSubObj.subIndex == null || netSubObj.subIndex.Length != 1) + continue; + + string subIndex = netSubObj.subIndex[0].ToString("X2"); + LibCanOpen.OdSubObject.Types.DataType subDataType = LibCanOpen.OdSubObject.Types.DataType.Unspecified; + LibCanOpen.OdSubObject.Types.AccessSDO subAccessSDO = LibCanOpen.OdSubObject.Types.AccessSDO.No; + LibCanOpen.OdSubObject.Types.AccessPDO subAccessPDO = LibCanOpen.OdSubObject.Types.AccessPDO.No; + + if (netSubObj.dataType != null && netSubObj.dataType.Length == 1) + { + if (System.Enum.IsDefined(typeof(LibCanOpen.OdSubObject.Types.DataType), netSubObj.dataType[0])) + { + subDataType = (LibCanOpen.OdSubObject.Types.DataType)netSubObj.dataType[0]; + } + } + + if (netSubObj.accessTypeSpecified) + { + switch (netSubObj.accessType) + { + case CANopenObjectListCANopenObjectCANopenSubObjectAccessType.@const: + case CANopenObjectListCANopenObjectCANopenSubObjectAccessType.ro: subAccessSDO = LibCanOpen.OdSubObject.Types.AccessSDO.Ro; break; + case CANopenObjectListCANopenObjectCANopenSubObjectAccessType.wo: subAccessSDO = LibCanOpen.OdSubObject.Types.AccessSDO.Wo; break; + case CANopenObjectListCANopenObjectCANopenSubObjectAccessType.rw: subAccessSDO = LibCanOpen.OdSubObject.Types.AccessSDO.Rw; break; + } + } + + if (netSubObj.PDOmappingSpecified) + { + switch (netSubObj.PDOmapping) + { + case CANopenObjectListCANopenObjectCANopenSubObjectPDOmapping.RPDO: subAccessPDO = LibCanOpen.OdSubObject.Types.AccessPDO.R; break; + case CANopenObjectListCANopenObjectCANopenSubObjectPDOmapping.TPDO: subAccessPDO = LibCanOpen.OdSubObject.Types.AccessPDO.T; break; + case CANopenObjectListCANopenObjectCANopenSubObjectPDOmapping.optional: subAccessPDO = LibCanOpen.OdSubObject.Types.AccessPDO.Tr; break; + } + } + + OdSubObject subEntry = new OdSubObject() + { + Name = netSubObj.name ?? "", + Alias = netSubObj.denotation ?? "", + Type = subDataType, + Sdo = subAccessSDO, + Pdo = subAccessPDO, + DefaultValue = netSubObj.defaultValue ?? "", + ActualValue = netSubObj.actualValue ?? "", + LowLimit = netSubObj.lowLimit ?? "", + HighLimit = netSubObj.highLimit ?? "" + }; + + if (netSubObj.uniqueIDRef != null && netSubObj.uniqueIDRef != "" && parameters.ContainsKey(netSubObj.uniqueIDRef)) + { + parameter devSubPar = parameters[netSubObj.uniqueIDRef]; + + entry.Description += G_label_getDescription(devSubPar.Items); + + ConvertAccessType(devSubPar.access, subEntry); + + if (devSubPar.defaultValue != null && devSubPar.defaultValue.value != null) + subEntry.DefaultValue = devSubPar.defaultValue.value; + + if (devSubPar.Items1 != null && devSubPar.Items1ElementName != null) + ConvertDataType(devSubPar.Items1ElementName[0], subEntry); + + if (devSubPar.actualValue != null && devSubPar.actualValue.value != null) + subEntry.ActualValue = devSubPar.actualValue.value; + + if (devSubPar.denotation != null) + subEntry.Alias = G_label_getDescription(devSubPar.denotation.Items); + + if (devSubPar.allowedValues != null && devSubPar.allowedValues.range != null && devSubPar.allowedValues.range[0] != null) + { + range r = devSubPar.allowedValues.range[0]; + if (r.minValue != null) subEntry.LowLimit = r.minValue.value ?? ""; + if (r.maxValue != null) subEntry.HighLimit = r.maxValue.value ?? ""; + } + + ConvertXddProperties(devSubPar.property, entry, subEntry); + } + + if (!entry.SubObjects.ContainsKey(subIndex)) + entry.SubObjects.Add(subIndex, subEntry); + } + } + + if (!dev.Objects.ContainsKey(index)) + dev.Objects.Add(index, entry); + } + } + } + } + + return dev; + } + + private ISO15745ProfileContainer ConvertFromProtobuf(CanOpenDevice dev, string fileName, bool deviceCommissioning, bool stripped) + { + #region base_elements + ISO15745ProfileContainer container = new ISO15745ProfileContainer(); + + container.ISO15745Profile = new ISO15745Profile[] + { + new ISO15745Profile(), + new ISO15745Profile(), + }; + + container.ISO15745Profile[0].ProfileHeader = new ProfileHeader_DataType + { + ProfileIdentification = "CANopen device profile", + ProfileRevision = "1.1", + ProfileName = "", + ProfileSource = "", + ProfileClassID = ProfileClassID_DataType.Device, + ISO15745Reference = new ISO15745Reference_DataType + { + ISO15745Part = "1", + ISO15745Edition = "1", + ProfileTechnology = "CANopen" + } + }; + container.ISO15745Profile[0].ProfileBody = new ProfileBody_Device_CANopen(); + var body_device = (ProfileBody_Device_CANopen)container.ISO15745Profile[0].ProfileBody; + + container.ISO15745Profile[1].ProfileHeader = new ProfileHeader_DataType + { + ProfileIdentification = "CANopen communication network profile", + ProfileRevision = "1.1", + ProfileName = "", + ProfileSource = "", + ProfileClassID = ProfileClassID_DataType.CommunicationNetwork, + ISO15745Reference = new ISO15745Reference_DataType + { + ISO15745Part = "1", + ISO15745Edition = "1", + ProfileTechnology = "CANopen" + } + }; + container.ISO15745Profile[1].ProfileBody = new ProfileBody_CommunicationNetwork_CANopen(); + var body_network = (ProfileBody_CommunicationNetwork_CANopen)container.ISO15745Profile[1].ProfileBody; + + #endregion + + #region ObjectDictionary + var body_network_objectList = new List(); + var body_device_parameterList = new List(); + var body_device_arrayList = new List(); + var body_device_structList = new List<@struct>(); + + foreach (var kvp in dev.Objects) + { + string index = kvp.Key; + Int16 indexInt; + OdObject entry = kvp.Value; + + + try + { + indexInt = System.Convert.ToInt16(index, 16); + } + catch (Exception) + { + Warnings.AddWarning($"Error in Object ({index}), wrong index", Warnings.warning_class.WARNING_GENERIC); + continue; + } + + if (stripped && entry.Disabled) + continue; + + var netObj = new CANopenObjectListCANopenObject + { + index = new byte[] { (byte)(indexInt >> 8), (byte)(indexInt & 0xFF) }, + name = entry.Name, + objectType = (byte)entry.Type, + uniqueIDRef = "UID_OBJ_" + index + }; + body_network_objectList.Add(netObj); + + var devPar = new parameter { uniqueID = "UID_OBJ_" + index }; + if (entry.Description != null && entry.Description != "") + { + devPar.Items = new object[] { new vendorTextDescription { lang = "en", Value = entry.Description } }; + } + else + { + // Add at least label made from parameter name, because g_labels is required by schema + devPar.Items = new object[] { new vendorTextLabel { lang = "en", Value = entry.Name } }; + } + if (deviceCommissioning && entry.Alias != null && entry.Alias != "") + { + devPar.denotation = new denotation + { + Items = new object[] { new vendorTextLabel { lang = "en", Value = entry.Alias } } + }; + } + body_device_parameterList.Add(devPar); + + if (entry.Type == LibCanOpen.OdObject.Types.ObjectType.Var) + { + if (entry.SubObjects.Count != 1) + { + Warnings.AddWarning($"Error in Object ({index}), VAR must have one subobject", Warnings.warning_class.WARNING_GENERIC); + continue; + } + var subEntry0 = entry.SubObjects[entry.SubObjects.Keys.First()]; + switch (subEntry0.Pdo) + { + case LibCanOpen.OdSubObject.Types.AccessPDO.R: netObj.PDOmapping = CANopenObjectListCANopenObjectPDOmapping.RPDO; break; + case LibCanOpen.OdSubObject.Types.AccessPDO.T: netObj.PDOmapping = CANopenObjectListCANopenObjectPDOmapping.TPDO; break; + case LibCanOpen.OdSubObject.Types.AccessPDO.Tr: netObj.PDOmapping = CANopenObjectListCANopenObjectPDOmapping.optional; break; + default: netObj.PDOmapping = CANopenObjectListCANopenObjectPDOmapping.no; break; + } + + netObj.PDOmappingSpecified = true; + + if (!stripped) + { + var propOd = ConvertXddProperties(entry); + var propSub = ConvertXddProperties(subEntry0); + devPar.property = new property[propOd.Length + propSub.Length]; + propOd.CopyTo(devPar.property, 0); + propSub.CopyTo(devPar.property, propOd.Length); + } + + WriteVar(devPar, subEntry0); + if (deviceCommissioning && subEntry0.ActualValue != null && subEntry0.ActualValue != "") + devPar.actualValue = new actualValue { value = subEntry0.ActualValue }; + } + else if ((entry.Type == LibCanOpen.OdObject.Types.ObjectType.Array || entry.Type == LibCanOpen.OdObject.Types.ObjectType.Record) && entry.SubObjects != null && entry.SubObjects.Count > 0) + { + netObj.subNumber = (byte)entry.SubObjects.Count; + netObj.subNumberSpecified = true; + + if (!stripped) + devPar.property = ConvertXddProperties(entry); + + var netSubObjList = new List(); + var devStructSubList = new List(); + ItemChoiceType ArrayItemElementName = ItemChoiceType.SINT; + + foreach (var subKvp in entry.SubObjects) + { + string subIndex = subKvp.Key; + byte subIndexInt; + OdSubObject subEntry = subKvp.Value; + + try + { + subIndexInt = System.Convert.ToByte(subIndex, 16); + } + catch (Exception) + { + Warnings.AddWarning($"Error in Object ({index}), wrong SubIndex", Warnings.warning_class.WARNING_GENERIC); + continue; + } + + var netSubObj = new CANopenObjectListCANopenObjectCANopenSubObject + { + subIndex = new byte[] { subIndexInt }, + name = subEntry.Name, + objectType = (byte)LibCanOpen.OdObject.Types.ObjectType.Var, + PDOmappingSpecified = true, + uniqueIDRef = "UID_SUB_" + index + subIndex + }; + switch (subEntry.Pdo) + { + case LibCanOpen.OdSubObject.Types.AccessPDO.R: netSubObj.PDOmapping = CANopenObjectListCANopenObjectCANopenSubObjectPDOmapping.RPDO; break; + case LibCanOpen.OdSubObject.Types.AccessPDO.T: netSubObj.PDOmapping = CANopenObjectListCANopenObjectCANopenSubObjectPDOmapping.TPDO; break; + case LibCanOpen.OdSubObject.Types.AccessPDO.Tr: netSubObj.PDOmapping = CANopenObjectListCANopenObjectCANopenSubObjectPDOmapping.optional; break; + default: netSubObj.PDOmapping = CANopenObjectListCANopenObjectCANopenSubObjectPDOmapping.no; break; + } + + var devSubPar = new parameter + { + uniqueID = "UID_SUB_" + index + subIndex + }; + // Add at least label made from parameter name, because g_labels is required by schema + devSubPar.Items = new object[] { new vendorTextLabel { lang = "en", Value = subEntry.Name } }; + + if (!stripped) + devSubPar.property = ConvertXddProperties(subEntry); + + if (deviceCommissioning && subEntry.Alias != null && subEntry.Alias != "") + { + devPar.denotation = new denotation + { + Items = new object[] { new vendorTextLabel { lang = "en", Value = subEntry.Alias } } + }; + } + WriteVar(devSubPar, subEntry); + + if (deviceCommissioning && subEntry.ActualValue != null && subEntry.ActualValue != "") + devPar.actualValue = new actualValue { value = subEntry.ActualValue }; + + if (entry.Type == LibCanOpen.OdObject.Types.ObjectType.Record) + { + devStructSubList.Add(new varDeclaration + { + name = subEntry.Name, + uniqueID = "UID_RECSUB_" + index + subIndex, + Item = new object(), + ItemElementName = (ItemChoiceType1)ConvertDataType(subEntry) + }); + } + else + { + ArrayItemElementName = (ItemChoiceType)ConvertDataType(subEntry); + } + + body_device_parameterList.Add(devSubPar); + netSubObjList.Add(netSubObj); + } + + // add refference to data type definition and definition of array or struct data type (schema requirement) + if (entry.Type == LibCanOpen.OdObject.Types.ObjectType.Array) + { + devPar.Items1 = new object[] { new dataTypeIDRef { uniqueIDRef = "UID_ARR_" + index } }; + devPar.Items1ElementName = new Items1ChoiceType[] { Items1ChoiceType.dataTypeIDRef }; + body_device_arrayList.Add(new array + { + uniqueID = "UID_ARR_" + index, + name = entry.Name, + Item = new object(), + ItemElementName = ArrayItemElementName, + subrange = new subrange[] { new subrange { lowerLimit = 0, upperLimit = entry.SubObjects.Count - 1 } } + }); + } + else + { + devPar.Items1 = new object[] { new dataTypeIDRef { uniqueIDRef = "UID_REC_" + index } }; + devPar.Items1ElementName = new Items1ChoiceType[] { Items1ChoiceType.dataTypeIDRef }; + body_device_structList.Add(new @struct + { + uniqueID = "UID_REC_" + index, + name = entry.Name, + varDeclaration = devStructSubList.ToArray() + }); + } + + netObj.CANopenSubObject = netSubObjList.ToArray(); + } + } + #endregion + + #region body_device + body_device.fileName = fileName; + body_device.fileCreator = dev.FileInfo.CreatedBy; + body_device.fileCreationDate = dev.FileInfo.CreationTime.ToDateTime(); + body_device.fileCreationTime = dev.FileInfo.CreationTime.ToDateTime(); + body_device.fileCreationTimeSpecified = true; + body_device.fileVersion = dev.FileInfo.FileVersion; + body_device.fileModifiedBy = dev.FileInfo.ModifiedBy; + body_device.fileModificationDate = dev.FileInfo.ModificationTime.ToDateTime(); + body_device.fileModificationTime = dev.FileInfo.ModificationTime.ToDateTime(); + body_device.fileModificationDateSpecified = true; + body_device.fileModificationTimeSpecified = true; + body_device.supportedLanguages = "en"; + + // Device identity + if (body_device.DeviceIdentity == null) + body_device.DeviceIdentity = new DeviceIdentity(); + body_device.DeviceIdentity.vendorName = new vendorName { Value = dev.DeviceInfo.VendorName }; + body_device.DeviceIdentity.vendorID = new vendorID { Value = "0" }; + body_device.DeviceIdentity.productName = new productName { Value = dev.DeviceInfo.ProductName }; + body_device.DeviceIdentity.productID = new productID { Value = "0" }; + if (dev.FileInfo.Description != null && dev.FileInfo.Description != "") + { + body_device.DeviceIdentity.productText = new productText + { + Items = new object[] + { + new vendorTextDescription { lang = "en", Value = dev.FileInfo.Description } + } + }; + } + + // version is optional element, make a template if empty + if (body_device.DeviceIdentity.version == null) + { + body_device.DeviceIdentity.version = new version[] + { + new version { versionType = versionVersionType.SW, Value = "0" }, + new version { versionType = versionVersionType.FW, Value = "0" }, + new version { versionType = versionVersionType.HW, Value = "0" } + }; + } + + // DeviceFunction is required by schema, make a template if empty. + if (body_device.DeviceFunction == null) + { + // This is just a template for somehow complex xml structure. Users can edit the + // xdd file directly to write characteristics of own devices or use generic xml + // editing tool. External editing will be preserved anyway, if xdd file will be + // later opened and saved back in EDSEditor. + // EDSEditor curerently does not have interface for editing the characteristics. + body_device.DeviceFunction = new DeviceFunction[] + { + new DeviceFunction + { + capabilities = new capabilities + { + characteristicsList = new characteristicsList[] + { + new characteristicsList + { + characteristic = new characteristic[] + { + new characteristic + { + characteristicName = new characteristicName + { + Items = new object[] + { + new vendorTextLabel { lang = "en", Value = "SW library" } + } + }, + characteristicContent = new characteristicContent[] + { + new characteristicContent { + Items = new object[] + { + new vendorTextLabel { lang = "en", Value = "libedssharp" } + } + }, + new characteristicContent { + Items = new object[] + { + new vendorTextLabel { lang = "en", Value = "CANopenNode" } + } + } + } + } + } + } + } + } + } + }; + } + + // ApplicationProcess (insert object dictionary) + if (body_device.ApplicationProcess == null) + body_device.ApplicationProcess = new ApplicationProcess[1]; + if (body_device.ApplicationProcess[0] == null) + body_device.ApplicationProcess[0] = new ApplicationProcess(); + body_device.ApplicationProcess[0].dataTypeList = new dataTypeList + { + array = body_device_arrayList.ToArray(), + @struct = body_device_structList.ToArray() + }; + body_device.ApplicationProcess[0].parameterList = body_device_parameterList.ToArray(); + #endregion + + #region body_network + body_network.fileName = fileName; + body_network.fileCreator = dev.FileInfo.CreatedBy; + body_network.fileCreationDate = dev.FileInfo.CreationTime.ToDateTime(); + body_network.fileCreationTime = dev.FileInfo.CreationTime.ToDateTime(); + body_network.fileCreationTimeSpecified = true; + body_network.fileVersion = dev.FileInfo.FileVersion; + body_network.fileModificationDate = dev.FileInfo.ModificationTime.ToDateTime(); + body_network.fileModificationTime = dev.FileInfo.ModificationTime.ToDateTime(); + body_network.fileModificationDateSpecified = true; + body_network.fileModificationTimeSpecified = true; + body_network.supportedLanguages = "en"; + + // base elements + ProfileBody_CommunicationNetwork_CANopenApplicationLayers ApplicationLayers = null; + ProfileBody_CommunicationNetwork_CANopenTransportLayers TransportLayers = null; + ProfileBody_CommunicationNetwork_CANopenNetworkManagement NetworkManagement = null; + if (body_network.Items != null && body_network.Items.Length >= 3) + { + foreach (object item in body_network.Items) + { + if (item.GetType() == typeof(ProfileBody_CommunicationNetwork_CANopenApplicationLayers)) + ApplicationLayers = (ProfileBody_CommunicationNetwork_CANopenApplicationLayers)item; + else if (item.GetType() == typeof(ProfileBody_CommunicationNetwork_CANopenApplicationLayers)) + TransportLayers = (ProfileBody_CommunicationNetwork_CANopenTransportLayers)item; + else if (item.GetType() == typeof(ProfileBody_CommunicationNetwork_CANopenApplicationLayers)) + NetworkManagement = (ProfileBody_CommunicationNetwork_CANopenNetworkManagement)item; + } + } + if (ApplicationLayers == null || TransportLayers == null || NetworkManagement == null) + { + body_network.Items = new object[3]; + body_network.Items[0] = new ProfileBody_CommunicationNetwork_CANopenApplicationLayers(); + ApplicationLayers = (ProfileBody_CommunicationNetwork_CANopenApplicationLayers)body_network.Items[0]; + body_network.Items[1] = new ProfileBody_CommunicationNetwork_CANopenTransportLayers(); + TransportLayers = (ProfileBody_CommunicationNetwork_CANopenTransportLayers)body_network.Items[1]; + body_network.Items[2] = new ProfileBody_CommunicationNetwork_CANopenNetworkManagement(); + NetworkManagement = (ProfileBody_CommunicationNetwork_CANopenNetworkManagement)body_network.Items[2]; + } + + // ApplicationLayers -> dummyUsage + ApplicationLayers.dummyUsage = new ProfileBody_CommunicationNetwork_CANopenApplicationLayersDummy[7]; + for (int x = 0; x < 7; x++) + ApplicationLayers.dummyUsage[x] = new ProfileBody_CommunicationNetwork_CANopenApplicationLayersDummy(); + + ApplicationLayers.dummyUsage[0].entry = ProfileBody_CommunicationNetwork_CANopenApplicationLayersDummyEntry.Dummy00010; + ApplicationLayers.dummyUsage[1].entry = ProfileBody_CommunicationNetwork_CANopenApplicationLayersDummyEntry.Dummy00021; + ApplicationLayers.dummyUsage[2].entry = ProfileBody_CommunicationNetwork_CANopenApplicationLayersDummyEntry.Dummy00031; + ApplicationLayers.dummyUsage[3].entry = ProfileBody_CommunicationNetwork_CANopenApplicationLayersDummyEntry.Dummy00041; + ApplicationLayers.dummyUsage[4].entry = ProfileBody_CommunicationNetwork_CANopenApplicationLayersDummyEntry.Dummy00051; + ApplicationLayers.dummyUsage[5].entry = ProfileBody_CommunicationNetwork_CANopenApplicationLayersDummyEntry.Dummy00061; + ApplicationLayers.dummyUsage[6].entry = ProfileBody_CommunicationNetwork_CANopenApplicationLayersDummyEntry.Dummy00071; + + // ApplicationLayers -> CANopenObjectList (insert object dictionary) + ApplicationLayers.CANopenObjectList = new CANopenObjectList + { + CANopenObject = body_network_objectList.ToArray() + }; + + // TransportLayers -> supportedBaudRate + List bauds; + bauds = new List(); + if (dev.DeviceInfo.BaudRate10) + bauds.Add(ProfileBody_CommunicationNetwork_CANopenTransportLayersPhysicalLayerBaudRateSupportedBaudRateValue.Item10Kbps); + if (dev.DeviceInfo.BaudRate20) + bauds.Add(ProfileBody_CommunicationNetwork_CANopenTransportLayersPhysicalLayerBaudRateSupportedBaudRateValue.Item20Kbps); + if (dev.DeviceInfo.BaudRate50) + bauds.Add(ProfileBody_CommunicationNetwork_CANopenTransportLayersPhysicalLayerBaudRateSupportedBaudRateValue.Item50Kbps); + if (dev.DeviceInfo.BaudRate125) + bauds.Add(ProfileBody_CommunicationNetwork_CANopenTransportLayersPhysicalLayerBaudRateSupportedBaudRateValue.Item125Kbps); + if (dev.DeviceInfo.BaudRate250) + bauds.Add(ProfileBody_CommunicationNetwork_CANopenTransportLayersPhysicalLayerBaudRateSupportedBaudRateValue.Item250Kbps); + if (dev.DeviceInfo.BaudRate500) + bauds.Add(ProfileBody_CommunicationNetwork_CANopenTransportLayersPhysicalLayerBaudRateSupportedBaudRateValue.Item500Kbps); + if (dev.DeviceInfo.BaudRate800) + bauds.Add(ProfileBody_CommunicationNetwork_CANopenTransportLayersPhysicalLayerBaudRateSupportedBaudRateValue.Item800Kbps); + if (dev.DeviceInfo.BaudRate1000) + bauds.Add(ProfileBody_CommunicationNetwork_CANopenTransportLayersPhysicalLayerBaudRateSupportedBaudRateValue.Item1000Kbps); + if (dev.DeviceInfo.BaudRateAuto) + bauds.Add(ProfileBody_CommunicationNetwork_CANopenTransportLayersPhysicalLayerBaudRateSupportedBaudRateValue.autobaudRate); + TransportLayers.PhysicalLayer = new ProfileBody_CommunicationNetwork_CANopenTransportLayersPhysicalLayer + { + baudRate = new ProfileBody_CommunicationNetwork_CANopenTransportLayersPhysicalLayerBaudRate + { + supportedBaudRate = new ProfileBody_CommunicationNetwork_CANopenTransportLayersPhysicalLayerBaudRateSupportedBaudRate[bauds.Count] + } + }; + for (int i = 0; i < bauds.Count; i++) + { + TransportLayers.PhysicalLayer.baudRate.supportedBaudRate[i] = new ProfileBody_CommunicationNetwork_CANopenTransportLayersPhysicalLayerBaudRateSupportedBaudRate + { + value = bauds[i] + }; + } + + // NetworkManagement + if (NetworkManagement.CANopenGeneralFeatures == null) + NetworkManagement.CANopenGeneralFeatures = new ProfileBody_CommunicationNetwork_CANopenNetworkManagementCANopenGeneralFeatures(); + NetworkManagement.CANopenGeneralFeatures.granularity = 8; // required parameter + NetworkManagement.CANopenGeneralFeatures.nrOfRxPDO = 4; //extract from OD + NetworkManagement.CANopenGeneralFeatures.nrOfTxPDO = 4; //extract from OD + + NetworkManagement.CANopenGeneralFeatures.ngSlave = false; + NetworkManagement.CANopenGeneralFeatures.ngMaster = false; + NetworkManagement.CANopenGeneralFeatures.NrOfNG_MonitoredNodes = 0; + + NetworkManagement.CANopenGeneralFeatures.layerSettingServiceSlave = dev.DeviceInfo.LssSlave; + NetworkManagement.CANopenGeneralFeatures.groupMessaging = false; + NetworkManagement.CANopenGeneralFeatures.dynamicChannels = 0; + + if (NetworkManagement.CANopenMasterFeatures == null) + NetworkManagement.CANopenMasterFeatures = new ProfileBody_CommunicationNetwork_CANopenNetworkManagementCANopenMasterFeatures(); + NetworkManagement.CANopenMasterFeatures.layerSettingServiceMaster = dev.DeviceInfo.LssMaster; + NetworkManagement.CANopenMasterFeatures.bootUpMaster = false; + + if (deviceCommissioning) + { + NetworkManagement.deviceCommissioning = new ProfileBody_CommunicationNetwork_CANopenNetworkManagementDeviceCommissioning + { + NodeID = (byte)dev.DeviceCommissioning.NodeId, + nodeName = dev.DeviceCommissioning.NodeName, + actualBaudRate = dev.DeviceCommissioning.Baudrate.ToString() + }; + } + else + { + NetworkManagement.deviceCommissioning = null; + } + #endregion + + return container; + } + } } diff --git a/libEDSsharp/libEDSsharp.csproj b/libEDSsharp/libEDSsharp.csproj index 3bd702c..a5c4252 100644 --- a/libEDSsharp/libEDSsharp.csproj +++ b/libEDSsharp/libEDSsharp.csproj @@ -23,6 +23,21 @@ - + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/libEDSsharp/proto/.gitignore b/libEDSsharp/proto/.gitignore new file mode 100644 index 0000000..8143e15 --- /dev/null +++ b/libEDSsharp/proto/.gitignore @@ -0,0 +1 @@ +*.cs diff --git a/libEDSsharp/proto/CanOpen.proto b/libEDSsharp/proto/CanOpen.proto new file mode 100644 index 0000000..bff4c33 --- /dev/null +++ b/libEDSsharp/proto/CanOpen.proto @@ -0,0 +1,168 @@ +syntax = "proto3"; + +package LibCanOpen; + +import "google/protobuf/timestamp.proto"; + +/* File information related properties. */ +message CanOpen_FileInfo +{ + string fileVersion = 1; // Actual file version (as string). + string description = 2; // File description. + google.protobuf.Timestamp creationTime = 3; // File creation time. + string createdBy = 4; // Name or a description of the file creator. + google.protobuf.Timestamp modificationTime = 5; // Time of the last modification. + string modifiedBy = 6; // Name or a description of the creator. +} + +/* CANopen device information related properties. */ +message CanOpen_DeviceInfo +{ + string vendorName = 1; // Vendor name. + string productName = 3; // Product name. + bool baudRate10 = 100; // Support of the baud rate 10 kbit/s. + bool baudRate20 = 101; // Support of the baud rate 20 kbit/s. + bool baudRate50 = 102; // Support of the baud rate 50 kbit/s. + bool baudRate125 = 103; // Support of the baud rate 125 kbit/s. + bool baudRate250 = 104; // Support of the baud rate 250 kbit/s. + bool baudRate500 = 105; // Support of the baud rate 500 kbit/s. + bool baudRate800 = 106; // Support of the baud rate 800 kbit/s. + bool baudRate1000 = 107; // Support of the baud rate 1000 kbit/s. + bool baudRateAuto = 108; // Support of the auto baud rate. + bool lssSlave = 150; // Support of the LSS slave functionality. + bool lssMaster = 151; // Support of the LSS master functionality. +} + +/* Actual CANopen device properties. */ +message CanOpen_DeviceCommissioning +{ + uint32 nodeId = 1; // CANopen device’s address. + string nodeName = 2; // Node name. + uint32 baudrate = 3; // CANopen device’s baudrate. +} + +/* Object Dictionary SubEntry on specific Subindex. Sorted dictionary of them is part of the OdEntry. If OdEntry + * ObjectType is "record", then each SubEntry in the dictionary may be unique. If OdEntry ObjectType is "array", + * then some properties of all SubEntries must be equal. If OdEntry ObjectType is "var", then one SubEntry exists. */ +message OdSubObject +{ + /* Object dictionary basic data types from CiA 301. */ + enum DataType + { + UNSPECIFIED = 0x00; // Unspecified, should not be used. + BOOLEAN = 0x01; // Boolean. + INTEGER8 = 0x02; // 8-bit signed integer. + INTEGER16 = 0x03; // 16-bit signed integer. + INTEGER32 = 0x04; // 32-bit signed integer. + UNSIGNED8 = 0x05; // 8-bit unsigned integer. + UNSIGNED16 = 0x06; // 16-bit unsigned integer. + UNSIGNED32 = 0x07; // 32-bit unsigned integer. + REAL32 = 0x08; // 32-bit floating point number. + VISIBLE_STRING = 0x09; /* Null terminated string (8-bit chars). It may contain control characters and UTF-8 + * characters. StringLengthMin specifies minimum buffer length for the string. */ + OCTET_STRING = 0x0A; // Fixed length array of bytes. + UNICODE_STRING = 0x0B; /* Null terminated string (16-bit chars). Not recommended to use. + * StringLengthMin specifies minimum buffer length for the string. */ + TIME_OF_DAY = 0x0C; // 48 bit long structure. + TIME_DIFFERENCE = 0x0D; // 48 bit long structure. + DOMAIN = 0x0F; // Data block of any length, don't have default value. + INTEGER24 = 0x10; // 24-bit signed integer, should not be used. + REAL64 = 0x11; // 64-bit floating point number. + INTEGER40 = 0x12; // 40-bit signed integer, should not be used. + INTEGER48 = 0x13; // 48-bit signed integer, should not be used. + INTEGER56 = 0x14; // 56-bit signed integer, should not be used. + INTEGER64 = 0x15; // 64-bit signed integer. + UNSIGNED24 = 0x16; // 24-bit unsigned integer, should not be used. + UNSIGNED40 = 0x18; // 40-bit unsigned integer, should not be used. + UNSIGNED48 = 0x19; // 48-bit unsigned integer, should not be used. + UNSIGNED56 = 0x1A; // 56-bit unsigned integer, should not be used. + UNSIGNED64 = 0x1B; // 64-bit unsigned integer. + } + + /* CANopen SDO access permissions. */ + enum AccessSDO + { + ACCESS_SDO_NO = 0; // No access. + ACCESS_SDO_RO = 1; // Read only access. + ACCESS_SDO_WO = 2; // Write only access. + ACCESS_SDO_RW = 3; // Read and write access. + } + + /* CANopen PDO access permissions. */ + enum AccessPDO + { + ACCESS_PDO_NO = 0; // No access. + ACCESS_PDO_T = 1; // TPDO access. + ACCESS_PDO_R = 2; // RPDO access. + ACCESS_PDO_TR = 3; // TPDO and RPDO access. + } + + /* CANopen SRDO access permissions. */ + enum AccessSRDO + { + ACCESS_SRDO_NO = 0; // no access. + ACCESS_SRDO_TX = 1; // SRDO TX access. + ACCESS_SRDO_RX = 2; // SRDO RX access. + ACCESS_SRDO_TRX = 3; // SRDO TX or RX access. + } + + string name = 1; // Name of the sub entry. If OdEntry is "VAR", this property is not relevant. + string alias = 2; // Additonal sub parameter name, for the actual device. If OdEntry is "VAR", this property is not relevant. + DataType type = 3; // CANopen data type + AccessSDO sdo = 4; // CANopen SDO access permissions + AccessPDO pdo = 5; // CANopen PDO access permissions + AccessSRDO srdo = 6; // CANopen SRDO access permissions. + string defaultValue = 7; // Default value of the sub object. + string actualValue = 8; // Actual value of the sub object, for the actual device. + string lowLimit = 9; // Low limit for the value. + string highLimit = 10; // High limit for the value. + uint32 stringLengthMin = 101; // CanOpenNode OD exporter V4: Minimum length of a string that can be stored. +} + +/* Object Dictionary Entry on specific Index. Sorted dictionary of them is part of CanOpenDevice - CANopen Object Dictionary. */ +message OdObject +{ + /* Type of Object Dictionary entry, similar as Object Code from CiA 301. */ + enum ObjectType + { + // Not defined, default + OBJECT_TYPE_UNSPECIFIED = 0; + // A single value such as an UNSIGNED8, BOOLEAN, FLOAT, INTEGER16, VISIBLE STRING etc. + OBJECT_TYPE_VAR = 7; + // A multiple data field object where each data field is a simple variable of the SAME basic data type e.g. array + // of UNSIGNED16 etc. Sub-index 0 is of UNSIGNED8. + OBJECT_TYPE_ARRAY = 8; + // A multiple data field object where the data fields may be any combination of + // simple variables. Sub-index 0 is of UNSIGNED8 and sub-index 255 is of UNSIGNED32. + OBJECT_TYPE_RECORD = 9; + } + + // If true, object is completelly skipped by CANopenNode exporters, etc. + bool disabled = 1; + // Name of the entry. + string name = 2; + // Additonal parameter name, for the actual device. + string alias = 3; + // Description of the Entry. + string description = 4; + // CANopen Object Type. + ObjectType type = 5; + // CanOpenNode OD exporter V4: it will generate a macro for each different CO_countLabel. For example, if four + // OD objects have "CO_countLabel" set to "TPDO", then macro "#define ODxyz_CNT_TPDO 4" will be generated by the OD exporter. + string countLabel = 101; + // CanOpenNode OD exporter V4: storage group into which the C variable will belong. If not defined, it will default to "RAM". + string storageGroup = 102; + // CanOpenNode OD exporter V1.3: Flags for the PDO. + bool flagsPDO = 103; + // Sorted dictionary of sub entries + map subObjects = 200; +} + +/* CANopen Device description object. */ +message CanOpenDevice +{ + CanOpen_FileInfo fileInfo = 1; // File information related properties. + CanOpen_DeviceInfo deviceInfo = 2; //CANopen device information related properties. + CanOpen_DeviceCommissioning deviceCommissioning = 3; // Parameters of the actual CANopen device. + map objects = 200; // CANopen Object Dictionary as sorted dictionary. +}