From ead01f9c921713adb6352498665c239c95dafc13 Mon Sep 17 00:00:00 2001 From: Thomas Wiedebusch Date: Mon, 12 Aug 2024 11:53:05 +0200 Subject: [PATCH 01/57] Certificates refreshed --- Common/CommonCore.Configuration/ServiceUrls.cs | 2 +- Common/PendingEmails/PendingEmails.csproj | 9 ++++++++- Common/Ui/Common.UI.VFM/Common.UI.VFM.csproj | 5 +++-- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/Common/CommonCore.Configuration/ServiceUrls.cs b/Common/CommonCore.Configuration/ServiceUrls.cs index df18db27..9a1f2de4 100644 --- a/Common/CommonCore.Configuration/ServiceUrls.cs +++ b/Common/CommonCore.Configuration/ServiceUrls.cs @@ -37,7 +37,7 @@ namespace Xylem.Common.CommonCore.Configuration "http://sla12iis01.emea.sensus.net/MeterProcessState/api/ProccesState/GetProductionSkipAndLifeTimeInfo?PcbId="; private const String DefaultGetCordonelAppVersionServiceUrl = - //"http://localhost:56011/api/ProccesState/GetCordonelAppVersion?PcbId="; + //"http://localhost:56011/api/ProcessState/GetCordonelAppVersion?PcbId="; "http://sla12iis01.emea.sensus.net/MeterProcessState/api/ProccesState/GetCordonelAppVersion?PcbId="; private const String DefaultListAllCordonelCustomersWithFilterServiceUrl = diff --git a/Common/PendingEmails/PendingEmails.csproj b/Common/PendingEmails/PendingEmails.csproj index dd0e6dd6..04af8a90 100644 --- a/Common/PendingEmails/PendingEmails.csproj +++ b/Common/PendingEmails/PendingEmails.csproj @@ -48,7 +48,7 @@ 4 - 4F225C82652E1EBABD8B58C34175D7B260B3A052 + 857BDC9424CA995BBB5640EE4C4D03DB5F1F2369 PendingEmails_TemporaryKey.pfx @@ -59,6 +59,13 @@ true + + true + + + + + diff --git a/Common/Ui/Common.UI.VFM/Common.UI.VFM.csproj b/Common/Ui/Common.UI.VFM/Common.UI.VFM.csproj index d5fe69e2..be0ed99c 100644 --- a/Common/Ui/Common.UI.VFM/Common.UI.VFM.csproj +++ b/Common/Ui/Common.UI.VFM/Common.UI.VFM.csproj @@ -57,10 +57,10 @@ false - 0F55F7C5256A0679A793CF846EE16C762E09517E + 2AAD7D379BFB7F46CA9B0F35907CFDA28F27D632 - Common.UI.VFM_TemporaryKey.pfx + Common.UI.VFM_1_TemporaryKey.pfx true @@ -151,6 +151,7 @@ Resources.Designer.cs + SettingsSingleFileGenerator From 6c5d24d872a33bdbee8dedef81601fef7dfcf2cd Mon Sep 17 00:00:00 2001 From: Thomas Wiedebusch Date: Mon, 12 Aug 2024 12:34:25 +0200 Subject: [PATCH 02/57] MagFlux: - 1. rebased to main, - 2. Assembly versions changed --- Common/.shared/SharedAssemblyInfo.cs | 1 - .../MagFlux/DataPackages/DataPackages.csproj | 3 --- .../MagFlux/MagFluxConfig/CommunicationConfig.cs | 2 ++ .../MagFlux/MagFluxConfig/MagFluxConfig.csproj | 10 ++++++++++ .../MagFlux/MagFluxConfig/Properties/AssemblyInfo.cs | 4 +--- .../MagFlux/MagFluxCore/Properties/AssemblyInfo.cs | 3 +-- .../RequestProtocol/Properties/AssemblyInfo.cs | 3 +-- .../StreamingProtocol/Properties/AssemblyInfo.cs | 3 +-- Common/Ui/MagFluxToolBox/MagFluxToolBox.csproj | 3 ++- Common/Ui/MagFluxToolBox/Properties/AssemblyInfo.cs | 7 +++---- Common/Ui/MagFluxToolBox/frmMainForm.cs | 2 +- 11 files changed, 22 insertions(+), 19 deletions(-) diff --git a/Common/.shared/SharedAssemblyInfo.cs b/Common/.shared/SharedAssemblyInfo.cs index 2c53cf85..ae1a5838 100644 --- a/Common/.shared/SharedAssemblyInfo.cs +++ b/Common/.shared/SharedAssemblyInfo.cs @@ -15,4 +15,3 @@ using System.Reflection; [assembly: AssemblyVersion("2.6.10.*")] -[assembly: AssemblyFileVersion("2.6.10.0")] \ No newline at end of file diff --git a/Common/Hardware/WaterMeter/MagFlux/DataPackages/DataPackages.csproj b/Common/Hardware/WaterMeter/MagFlux/DataPackages/DataPackages.csproj index d6cac487..b1eddd98 100644 --- a/Common/Hardware/WaterMeter/MagFlux/DataPackages/DataPackages.csproj +++ b/Common/Hardware/WaterMeter/MagFlux/DataPackages/DataPackages.csproj @@ -212,9 +212,6 @@ MinimumRecommendedRules.ruleset - - Properties\SharedAssemblyInfo.cs - diff --git a/Common/Hardware/WaterMeter/MagFlux/MagFluxConfig/CommunicationConfig.cs b/Common/Hardware/WaterMeter/MagFlux/MagFluxConfig/CommunicationConfig.cs index 11ae3929..2dd69031 100644 --- a/Common/Hardware/WaterMeter/MagFlux/MagFluxConfig/CommunicationConfig.cs +++ b/Common/Hardware/WaterMeter/MagFlux/MagFluxConfig/CommunicationConfig.cs @@ -1,4 +1,6 @@ using System; +using Xylem.Common.Hardware.Interfaces.Ports.PortCore; +using Xylem.Common.Hardware.Interfaces.Protocols.TransmitProtocol; namespace Xylem.Common.Hardware.WaterMeter.MagFlux.MagFluxConfig { diff --git a/Common/Hardware/WaterMeter/MagFlux/MagFluxConfig/MagFluxConfig.csproj b/Common/Hardware/WaterMeter/MagFlux/MagFluxConfig/MagFluxConfig.csproj index f51296f0..7df7d4b8 100644 --- a/Common/Hardware/WaterMeter/MagFlux/MagFluxConfig/MagFluxConfig.csproj +++ b/Common/Hardware/WaterMeter/MagFlux/MagFluxConfig/MagFluxConfig.csproj @@ -57,5 +57,15 @@ + + + {2E139F63-B6FE-4EA9-A098-85364FE9B26C} + PortCore + + + {63E0AC5E-B7C8-4363-A2FE-BE489E648FF0} + TransmitProtocol + + \ No newline at end of file diff --git a/Common/Hardware/WaterMeter/MagFlux/MagFluxConfig/Properties/AssemblyInfo.cs b/Common/Hardware/WaterMeter/MagFlux/MagFluxConfig/Properties/AssemblyInfo.cs index 04865a04..4c287db8 100644 --- a/Common/Hardware/WaterMeter/MagFlux/MagFluxConfig/Properties/AssemblyInfo.cs +++ b/Common/Hardware/WaterMeter/MagFlux/MagFluxConfig/Properties/AssemblyInfo.cs @@ -1,5 +1,4 @@ using System.Reflection; -using System.Runtime.CompilerServices; using System.Runtime.InteropServices; // General Information about an assembly is controlled through the following @@ -10,7 +9,7 @@ using System.Runtime.InteropServices; [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("Xylem")] [assembly: AssemblyProduct("MagFluxConfig")] -[assembly: AssemblyCopyright("Copyright © Xylem 2023")] +[assembly: AssemblyCopyright("Copyright © Xylem 2023..2024")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] @@ -33,4 +32,3 @@ using System.Runtime.InteropServices; // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] [assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/Common/Hardware/WaterMeter/MagFlux/MagFluxCore/Properties/AssemblyInfo.cs b/Common/Hardware/WaterMeter/MagFlux/MagFluxCore/Properties/AssemblyInfo.cs index 4155a811..ecda56d2 100644 --- a/Common/Hardware/WaterMeter/MagFlux/MagFluxCore/Properties/AssemblyInfo.cs +++ b/Common/Hardware/WaterMeter/MagFlux/MagFluxCore/Properties/AssemblyInfo.cs @@ -10,7 +10,7 @@ using System.Runtime.InteropServices; [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("Xylem")] [assembly: AssemblyProduct("MagFluxCore")] -[assembly: AssemblyCopyright("Copyright © Xylem 2023")] +[assembly: AssemblyCopyright("Copyright © Xylem 2023..2024")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] @@ -33,4 +33,3 @@ using System.Runtime.InteropServices; // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] [assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/Common/Hardware/WaterMeter/MagFlux/Protocols/RequestProtocol/Properties/AssemblyInfo.cs b/Common/Hardware/WaterMeter/MagFlux/Protocols/RequestProtocol/Properties/AssemblyInfo.cs index d4f9d6ab..c23e017c 100644 --- a/Common/Hardware/WaterMeter/MagFlux/Protocols/RequestProtocol/Properties/AssemblyInfo.cs +++ b/Common/Hardware/WaterMeter/MagFlux/Protocols/RequestProtocol/Properties/AssemblyInfo.cs @@ -10,7 +10,7 @@ using System.Runtime.InteropServices; [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("Xylem")] [assembly: AssemblyProduct("")] -[assembly: AssemblyCopyright("Copyright © Xylem 2023")] +[assembly: AssemblyCopyright("Copyright © Xylem 2023..2024")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] @@ -33,4 +33,3 @@ using System.Runtime.InteropServices; // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] [assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/Common/Hardware/WaterMeter/MagFlux/Protocols/StreamingProtocol/Properties/AssemblyInfo.cs b/Common/Hardware/WaterMeter/MagFlux/Protocols/StreamingProtocol/Properties/AssemblyInfo.cs index 41621a5d..066a6f1c 100644 --- a/Common/Hardware/WaterMeter/MagFlux/Protocols/StreamingProtocol/Properties/AssemblyInfo.cs +++ b/Common/Hardware/WaterMeter/MagFlux/Protocols/StreamingProtocol/Properties/AssemblyInfo.cs @@ -10,7 +10,7 @@ using System.Runtime.InteropServices; [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("Xylem")] [assembly: AssemblyProduct("")] -[assembly: AssemblyCopyright("Copyright © Xylem 2023")] +[assembly: AssemblyCopyright("Copyright © Xylem 2023..2024")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] @@ -33,4 +33,3 @@ using System.Runtime.InteropServices; // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] [assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/Common/Ui/MagFluxToolBox/MagFluxToolBox.csproj b/Common/Ui/MagFluxToolBox/MagFluxToolBox.csproj index e7aa59c9..f0b3b7dc 100644 --- a/Common/Ui/MagFluxToolBox/MagFluxToolBox.csproj +++ b/Common/Ui/MagFluxToolBox/MagFluxToolBox.csproj @@ -11,7 +11,7 @@ v4.8 512 true - true + false @@ -24,6 +24,7 @@ prompt 4 false + bin\Debug\MagFluxToolBox.xml AnyCPU diff --git a/Common/Ui/MagFluxToolBox/Properties/AssemblyInfo.cs b/Common/Ui/MagFluxToolBox/Properties/AssemblyInfo.cs index 3d114edb..ec58b559 100644 --- a/Common/Ui/MagFluxToolBox/Properties/AssemblyInfo.cs +++ b/Common/Ui/MagFluxToolBox/Properties/AssemblyInfo.cs @@ -9,14 +9,14 @@ using System.Runtime.InteropServices; [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("Xylem")] [assembly: AssemblyProduct("MagFluxToolBox")] -[assembly: AssemblyCopyright("Copyright © Xylem 2023")] +[assembly: AssemblyCopyright("Copyright © Xylem 2023..2024")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] // Setting ComVisible to false makes the types in this assembly not visible // to COM components. If you need to access a type in this assembly from // COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] +[assembly: ComVisible(true)] // The following GUID is for the ID of the typelib if this project is exposed to COM [assembly: Guid("a5e7ce52-3101-4e9b-b896-c2c0fade4812")] @@ -31,5 +31,4 @@ using System.Runtime.InteropServices; // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("2.0.0.0")] -[assembly: AssemblyFileVersion("2.0.0.0")] +[assembly: AssemblyVersion("2.1.0.*")] diff --git a/Common/Ui/MagFluxToolBox/frmMainForm.cs b/Common/Ui/MagFluxToolBox/frmMainForm.cs index fac0c46b..4d99232d 100644 --- a/Common/Ui/MagFluxToolBox/frmMainForm.cs +++ b/Common/Ui/MagFluxToolBox/frmMainForm.cs @@ -22,7 +22,7 @@ namespace Xylem.Common.Ui.MagFluxToolBox InitializeComponent(); var logger = NLogHelper.CreateOrGetLogger("MagFluxToolBox"); var version = Assembly.GetExecutingAssembly().GetName().Version; - logger.Info($"MagFluxToolBox Version: {version.Major}.{version.Minor}.{version.Build}"); + logger.Info($"MagFluxToolBox Version: {version.Major}.{version.Minor}.{version.Build}.{version.Revision}"); lblVersionNumber.Text = $@"Version: {version.Major}.{version.Minor}.{version.Build}"; } From 587c0ed25eb1fee30eda421359d8a0a1ded5d3df Mon Sep 17 00:00:00 2001 From: Thomas Wiedebusch Date: Mon, 12 Aug 2024 15:01:14 +0200 Subject: [PATCH 03/57] MagFlux: - changed from modbus_master_csharp to magflux_api --- Common/Common.sln | 70 +- .../DataPackages/DataTypes/Calibration.cs | 3 - .../RequestProtocol/RequestProtocol.cs | 77 +- .../RequestProtocol/RequestProtocol.csproj | 7 +- Common/MagFluxApi/magflux_api.dll | Bin 0 -> 267264 bytes Common/modbus_master_csharp/.gitignore | 450 ------ .../.vscode/extensions.json | 11 - Common/modbus_master_csharp/App.config | 6 - Common/modbus_master_csharp/Base/AppConst.cs | 111 -- .../Base/ByteArrayExtensionClass.cs | 314 ----- .../Base/Convert1970SecToTime.cs | 153 --- Common/modbus_master_csharp/Base/DataUnion.cs | 459 ------- .../Base/Files/PathTools.cs | 115 -- .../Base/Files/ProgramLogCSVFileClass.cs | 232 ---- .../Base/LogMessageTool.cs | 102 -- .../Base/NumberConvert.cs | 602 -------- .../Base/StringConvert.cs | 474 ------- .../Base/XML/XMLStringClass.cs | 127 -- .../Base/XML/XMLWriteFuncClass.cs | 76 -- .../Communication/DeviceExceptionData.cs | 67 - .../Communication/FuncTypes.cs | 22 - .../Communication/ModbusCom.cs | 634 --------- .../ProtocolTranslateModbusRTUClass.cs | 572 -------- .../Communication/ProtocolType.cs | 11 - .../ValueEncoders/ArrayManipulator.cs | 22 - .../ValueEncoders/DeviceValueEncoder.cs | 952 ------------- .../Communication/ValueEncoders/LayoutType.cs | 12 - .../ValueEncoders/StringEncoder.cs | 92 -- .../ValueEncoders/StringEncoderEmail.cs | 95 -- .../StringEncoderEmail_Q_Types.cs | 175 --- .../ValueEncoders/StringEncoderUnicode.cs | 41 - .../ValueEncoders/StringEncoder_W1252.cs | 39 - .../ValueEncoders/U32_IP_Encoder.cs | 69 - .../Device/CalibrationPoint.cs | 121 -- .../Device/CalibrationPoints.cs | 198 --- .../Device/IMagFluxRequestProtocol.cs | 171 --- .../Device/MagFlux6200.cs | 1190 ---------------- ...rology_modbus_dependency_documentation.url | 5 - .../Device/MagFlux6200_metrology_reg_list.cs | 1211 ----------------- .../Device/Met_6200v046.html | 1154 ---------------- .../Device/ModbusDeviceCom.cs | 334 ----- .../Device/Value/ValueDataType.cs | 93 -- .../Device/Value/ValueDescriptionFunc.cs | 146 -- Common/modbus_master_csharp/Device/md_test.cs | 155 --- Common/modbus_master_csharp/Program.cs | 1127 --------------- .../Properties/AssemblyInfo.cs | 36 - Common/modbus_master_csharp/README.md | 22 - .../2023-10-10 support Met reg v6200045.zip | Bin 61228 -> 0 bytes .../0_sensor_cal_for_testing.xml | 27 - ...0_sensor_cal_set_ready_for_calibration.xml | 27 - .../run_ABORT_CAL.cmd | 6 - .../run_LOAD_CAL.cmd | 19 - ...run_LOAD_CAL_set_ready_for_calibration.cmd | 19 - .../run_SAVE_CAL.cmd | 12 - .../run_SET_DN_SN.cmd | 19 - .../run_TEST.cmd | 17 - .../run_help.cmd | 4 - .../update_log.txt | 17 - ...itbucket link for modbus_master_csharp.url | 5 - .../modbus_master_csharp/docs/dummy_file.txt | 0 .../modbus_master_csharp.csproj | 89 -- .../scripts/dummy_file.txt | 0 .../src/modbus_master_csharp.sln | 25 - .../src/modbus_master_csharp/App.config | 6 - .../src/modbus_master_csharp/Base/AppConst.cs | 111 -- .../Base/ByteArrayExtensionClass.cs | 314 ----- .../Base/Convert1970SecToTime.cs | 153 --- .../modbus_master_csharp/Base/DataUnion.cs | 459 ------- .../Base/Files/PathTools.cs | 115 -- .../Base/Files/ProgramLogCSVFileClass.cs | 232 ---- .../Base/LogMessageTool.cs | 102 -- .../Base/NumberConvert.cs | 602 -------- .../Base/StringConvert.cs | 474 ------- .../Base/XML/XMLStringClass.cs | 127 -- .../Base/XML/XMLWriteFuncClass.cs | 76 -- .../Communication/DeviceExceptionData.cs | 67 - .../Communication/FuncTypes.cs | 22 - .../Communication/ModbusCom.cs | 634 --------- .../ProtocolTranslateModbusRTUClass.cs | 572 -------- .../Communication/ProtocolType.cs | 11 - .../ValueEncoders/ArrayManipulator.cs | 22 - .../ValueEncoders/DeviceValueEncoder.cs | 952 ------------- .../Communication/ValueEncoders/LayoutType.cs | 12 - .../ValueEncoders/StringEncoder.cs | 92 -- .../ValueEncoders/StringEncoderEmail.cs | 95 -- .../StringEncoderEmail_Q_Types.cs | 175 --- .../ValueEncoders/StringEncoderUnicode.cs | 41 - .../ValueEncoders/StringEncoder_W1252.cs | 39 - .../ValueEncoders/U32_IP_Encoder.cs | 69 - .../Device/CalibrationPoint.cs | 121 -- .../Device/CalibrationPoints.cs | 198 --- .../Device/IMagFluxRequestProtocol.cs | 171 --- .../Device/MagFlux6200.cs | 1190 ---------------- ...rology_modbus_dependency_documentation.url | 5 - .../Device/MagFlux6200_metrology_reg_list.cs | 1123 --------------- .../Device/ModbusDeviceCom.cs | 334 ----- .../Device/Value/ValueDataType.cs | 93 -- .../Device/Value/ValueDescriptionFunc.cs | 146 -- .../modbus_master_csharp/Device/md_test.cs | 155 --- .../src/modbus_master_csharp/Program.cs | 1127 --------------- .../Properties/AssemblyInfo.cs | 36 - .../modbus_master_csharp.csproj | 89 -- .../src/run_debug_modbus_master_csharp.cmd | 6 - .../modbus_master_csharp/test/dummy_file.txt | 0 .../modbus_master_csharp/tools/dummy_file.txt | 0 .../modbus_master_csharp/xptp/dummy_file.txt | 0 106 files changed, 52 insertions(+), 22757 deletions(-) create mode 100644 Common/MagFluxApi/magflux_api.dll delete mode 100644 Common/modbus_master_csharp/.gitignore delete mode 100644 Common/modbus_master_csharp/.vscode/extensions.json delete mode 100644 Common/modbus_master_csharp/App.config delete mode 100644 Common/modbus_master_csharp/Base/AppConst.cs delete mode 100644 Common/modbus_master_csharp/Base/ByteArrayExtensionClass.cs delete mode 100644 Common/modbus_master_csharp/Base/Convert1970SecToTime.cs delete mode 100644 Common/modbus_master_csharp/Base/DataUnion.cs delete mode 100644 Common/modbus_master_csharp/Base/Files/PathTools.cs delete mode 100644 Common/modbus_master_csharp/Base/Files/ProgramLogCSVFileClass.cs delete mode 100644 Common/modbus_master_csharp/Base/LogMessageTool.cs delete mode 100644 Common/modbus_master_csharp/Base/NumberConvert.cs delete mode 100644 Common/modbus_master_csharp/Base/StringConvert.cs delete mode 100644 Common/modbus_master_csharp/Base/XML/XMLStringClass.cs delete mode 100644 Common/modbus_master_csharp/Base/XML/XMLWriteFuncClass.cs delete mode 100644 Common/modbus_master_csharp/Communication/DeviceExceptionData.cs delete mode 100644 Common/modbus_master_csharp/Communication/FuncTypes.cs delete mode 100644 Common/modbus_master_csharp/Communication/ModbusCom.cs delete mode 100644 Common/modbus_master_csharp/Communication/ProtocolTranslateModbusRTUClass.cs delete mode 100644 Common/modbus_master_csharp/Communication/ProtocolType.cs delete mode 100644 Common/modbus_master_csharp/Communication/ValueEncoders/ArrayManipulator.cs delete mode 100644 Common/modbus_master_csharp/Communication/ValueEncoders/DeviceValueEncoder.cs delete mode 100644 Common/modbus_master_csharp/Communication/ValueEncoders/LayoutType.cs delete mode 100644 Common/modbus_master_csharp/Communication/ValueEncoders/StringEncoder.cs delete mode 100644 Common/modbus_master_csharp/Communication/ValueEncoders/StringEncoderEmail.cs delete mode 100644 Common/modbus_master_csharp/Communication/ValueEncoders/StringEncoderEmail_Q_Types.cs delete mode 100644 Common/modbus_master_csharp/Communication/ValueEncoders/StringEncoderUnicode.cs delete mode 100644 Common/modbus_master_csharp/Communication/ValueEncoders/StringEncoder_W1252.cs delete mode 100644 Common/modbus_master_csharp/Communication/ValueEncoders/U32_IP_Encoder.cs delete mode 100644 Common/modbus_master_csharp/Device/CalibrationPoint.cs delete mode 100644 Common/modbus_master_csharp/Device/CalibrationPoints.cs delete mode 100644 Common/modbus_master_csharp/Device/IMagFluxRequestProtocol.cs delete mode 100644 Common/modbus_master_csharp/Device/MagFlux6200.cs delete mode 100644 Common/modbus_master_csharp/Device/MagFlux6200_metrology_modbus_dependency_documentation.url delete mode 100644 Common/modbus_master_csharp/Device/MagFlux6200_metrology_reg_list.cs delete mode 100644 Common/modbus_master_csharp/Device/Met_6200v046.html delete mode 100644 Common/modbus_master_csharp/Device/ModbusDeviceCom.cs delete mode 100644 Common/modbus_master_csharp/Device/Value/ValueDataType.cs delete mode 100644 Common/modbus_master_csharp/Device/Value/ValueDescriptionFunc.cs delete mode 100644 Common/modbus_master_csharp/Device/md_test.cs delete mode 100644 Common/modbus_master_csharp/Program.cs delete mode 100644 Common/modbus_master_csharp/Properties/AssemblyInfo.cs delete mode 100644 Common/modbus_master_csharp/README.md delete mode 100644 Common/modbus_master_csharp/compiled_exe/2023-10-10 support Met reg v6200045.zip delete mode 100644 Common/modbus_master_csharp/compiled_exe/202x-xx-xx support Met reg v6200xxx BETA/0_sensor_cal_for_testing.xml delete mode 100644 Common/modbus_master_csharp/compiled_exe/202x-xx-xx support Met reg v6200xxx BETA/0_sensor_cal_set_ready_for_calibration.xml delete mode 100644 Common/modbus_master_csharp/compiled_exe/202x-xx-xx support Met reg v6200xxx BETA/run_ABORT_CAL.cmd delete mode 100644 Common/modbus_master_csharp/compiled_exe/202x-xx-xx support Met reg v6200xxx BETA/run_LOAD_CAL.cmd delete mode 100644 Common/modbus_master_csharp/compiled_exe/202x-xx-xx support Met reg v6200xxx BETA/run_LOAD_CAL_set_ready_for_calibration.cmd delete mode 100644 Common/modbus_master_csharp/compiled_exe/202x-xx-xx support Met reg v6200xxx BETA/run_SAVE_CAL.cmd delete mode 100644 Common/modbus_master_csharp/compiled_exe/202x-xx-xx support Met reg v6200xxx BETA/run_SET_DN_SN.cmd delete mode 100644 Common/modbus_master_csharp/compiled_exe/202x-xx-xx support Met reg v6200xxx BETA/run_TEST.cmd delete mode 100644 Common/modbus_master_csharp/compiled_exe/202x-xx-xx support Met reg v6200xxx BETA/run_help.cmd delete mode 100644 Common/modbus_master_csharp/compiled_exe/202x-xx-xx support Met reg v6200xxx BETA/update_log.txt delete mode 100644 Common/modbus_master_csharp/compiled_exe/bitbucket link for modbus_master_csharp.url delete mode 100644 Common/modbus_master_csharp/docs/dummy_file.txt delete mode 100644 Common/modbus_master_csharp/modbus_master_csharp.csproj delete mode 100644 Common/modbus_master_csharp/scripts/dummy_file.txt delete mode 100644 Common/modbus_master_csharp/src/modbus_master_csharp.sln delete mode 100644 Common/modbus_master_csharp/src/modbus_master_csharp/App.config delete mode 100644 Common/modbus_master_csharp/src/modbus_master_csharp/Base/AppConst.cs delete mode 100644 Common/modbus_master_csharp/src/modbus_master_csharp/Base/ByteArrayExtensionClass.cs delete mode 100644 Common/modbus_master_csharp/src/modbus_master_csharp/Base/Convert1970SecToTime.cs delete mode 100644 Common/modbus_master_csharp/src/modbus_master_csharp/Base/DataUnion.cs delete mode 100644 Common/modbus_master_csharp/src/modbus_master_csharp/Base/Files/PathTools.cs delete mode 100644 Common/modbus_master_csharp/src/modbus_master_csharp/Base/Files/ProgramLogCSVFileClass.cs delete mode 100644 Common/modbus_master_csharp/src/modbus_master_csharp/Base/LogMessageTool.cs delete mode 100644 Common/modbus_master_csharp/src/modbus_master_csharp/Base/NumberConvert.cs delete mode 100644 Common/modbus_master_csharp/src/modbus_master_csharp/Base/StringConvert.cs delete mode 100644 Common/modbus_master_csharp/src/modbus_master_csharp/Base/XML/XMLStringClass.cs delete mode 100644 Common/modbus_master_csharp/src/modbus_master_csharp/Base/XML/XMLWriteFuncClass.cs delete mode 100644 Common/modbus_master_csharp/src/modbus_master_csharp/Communication/DeviceExceptionData.cs delete mode 100644 Common/modbus_master_csharp/src/modbus_master_csharp/Communication/FuncTypes.cs delete mode 100644 Common/modbus_master_csharp/src/modbus_master_csharp/Communication/ModbusCom.cs delete mode 100644 Common/modbus_master_csharp/src/modbus_master_csharp/Communication/ProtocolTranslateModbusRTUClass.cs delete mode 100644 Common/modbus_master_csharp/src/modbus_master_csharp/Communication/ProtocolType.cs delete mode 100644 Common/modbus_master_csharp/src/modbus_master_csharp/Communication/ValueEncoders/ArrayManipulator.cs delete mode 100644 Common/modbus_master_csharp/src/modbus_master_csharp/Communication/ValueEncoders/DeviceValueEncoder.cs delete mode 100644 Common/modbus_master_csharp/src/modbus_master_csharp/Communication/ValueEncoders/LayoutType.cs delete mode 100644 Common/modbus_master_csharp/src/modbus_master_csharp/Communication/ValueEncoders/StringEncoder.cs delete mode 100644 Common/modbus_master_csharp/src/modbus_master_csharp/Communication/ValueEncoders/StringEncoderEmail.cs delete mode 100644 Common/modbus_master_csharp/src/modbus_master_csharp/Communication/ValueEncoders/StringEncoderEmail_Q_Types.cs delete mode 100644 Common/modbus_master_csharp/src/modbus_master_csharp/Communication/ValueEncoders/StringEncoderUnicode.cs delete mode 100644 Common/modbus_master_csharp/src/modbus_master_csharp/Communication/ValueEncoders/StringEncoder_W1252.cs delete mode 100644 Common/modbus_master_csharp/src/modbus_master_csharp/Communication/ValueEncoders/U32_IP_Encoder.cs delete mode 100644 Common/modbus_master_csharp/src/modbus_master_csharp/Device/CalibrationPoint.cs delete mode 100644 Common/modbus_master_csharp/src/modbus_master_csharp/Device/CalibrationPoints.cs delete mode 100644 Common/modbus_master_csharp/src/modbus_master_csharp/Device/IMagFluxRequestProtocol.cs delete mode 100644 Common/modbus_master_csharp/src/modbus_master_csharp/Device/MagFlux6200.cs delete mode 100644 Common/modbus_master_csharp/src/modbus_master_csharp/Device/MagFlux6200_metrology_modbus_dependency_documentation.url delete mode 100644 Common/modbus_master_csharp/src/modbus_master_csharp/Device/MagFlux6200_metrology_reg_list.cs delete mode 100644 Common/modbus_master_csharp/src/modbus_master_csharp/Device/ModbusDeviceCom.cs delete mode 100644 Common/modbus_master_csharp/src/modbus_master_csharp/Device/Value/ValueDataType.cs delete mode 100644 Common/modbus_master_csharp/src/modbus_master_csharp/Device/Value/ValueDescriptionFunc.cs delete mode 100644 Common/modbus_master_csharp/src/modbus_master_csharp/Device/md_test.cs delete mode 100644 Common/modbus_master_csharp/src/modbus_master_csharp/Program.cs delete mode 100644 Common/modbus_master_csharp/src/modbus_master_csharp/Properties/AssemblyInfo.cs delete mode 100644 Common/modbus_master_csharp/src/modbus_master_csharp/modbus_master_csharp.csproj delete mode 100644 Common/modbus_master_csharp/src/run_debug_modbus_master_csharp.cmd delete mode 100644 Common/modbus_master_csharp/test/dummy_file.txt delete mode 100644 Common/modbus_master_csharp/tools/dummy_file.txt delete mode 100644 Common/modbus_master_csharp/xptp/dummy_file.txt diff --git a/Common/Common.sln b/Common/Common.sln index d0f714ff..aaf2a991 100644 --- a/Common/Common.sln +++ b/Common/Common.sln @@ -150,6 +150,9 @@ EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Protocols", "Protocols", "{94579693-7AC6-4FF0-B540-1F3780E1BCFA}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "MjkControlledCodeRef", "MjkControlledCodeRef", "{B4F8545B-F379-42A5-BDE9-763FD7B79598}" + ProjectSection(SolutionItems) = preProject + MagFluxApi\magflux_api.dll = MagFluxApi\magflux_api.dll + EndProjectSection EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MagFluxToolBox", "Ui\MagFluxToolBox\MagFluxToolBox.csproj", "{A5E7CE52-3101-4E9B-B896-C2C0FADE4812}" EndProject @@ -169,8 +172,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LaaProduction", "LaaProduct EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CommonConsole", "CommonConsole\CommonConsole.csproj", "{AFC428F5-0141-4A3F-BEF2-470A89D972F6}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "modbus_master_csharp", "modbus_master_csharp\modbus_master_csharp.csproj", "{F14940F8-3C86-4BB7-A915-B021E4B3052D}" -EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "OmniPlus", "OmniPlus", "{68323534-4337-4E3A-89BC-11D8BB55343A}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OmniPlus", "OmniPlus\OmniPlus\OmniPlus.csproj", "{F8D84B9A-0FCF-4083-B067-8C39B7314152}" @@ -3913,70 +3914,6 @@ Global {AFC428F5-0141-4A3F-BEF2-470A89D972F6}.XP32bit|x64.Build.0 = Release|Any CPU {AFC428F5-0141-4A3F-BEF2-470A89D972F6}.XP32bit|x86.ActiveCfg = Release|Any CPU {AFC428F5-0141-4A3F-BEF2-470A89D972F6}.XP32bit|x86.Build.0 = Release|Any CPU - {F14940F8-3C86-4BB7-A915-B021E4B3052D}._MANUAL_CONTROL|Any CPU.ActiveCfg = Release|Any CPU - {F14940F8-3C86-4BB7-A915-B021E4B3052D}._MANUAL_CONTROL|Any CPU.Build.0 = Release|Any CPU - {F14940F8-3C86-4BB7-A915-B021E4B3052D}._MANUAL_CONTROL|Mixed Platforms.ActiveCfg = Release|Any CPU - {F14940F8-3C86-4BB7-A915-B021E4B3052D}._MANUAL_CONTROL|Mixed Platforms.Build.0 = Release|Any CPU - {F14940F8-3C86-4BB7-A915-B021E4B3052D}._MANUAL_CONTROL|x64.ActiveCfg = Release|Any CPU - {F14940F8-3C86-4BB7-A915-B021E4B3052D}._MANUAL_CONTROL|x64.Build.0 = Release|Any CPU - {F14940F8-3C86-4BB7-A915-B021E4B3052D}._MANUAL_CONTROL|x86.ActiveCfg = Release|Any CPU - {F14940F8-3C86-4BB7-A915-B021E4B3052D}._MANUAL_CONTROL|x86.Build.0 = Release|Any CPU - {F14940F8-3C86-4BB7-A915-B021E4B3052D}.COM|Any CPU.ActiveCfg = Release|Any CPU - {F14940F8-3C86-4BB7-A915-B021E4B3052D}.COM|Any CPU.Build.0 = Release|Any CPU - {F14940F8-3C86-4BB7-A915-B021E4B3052D}.COM|Mixed Platforms.ActiveCfg = Release|Any CPU - {F14940F8-3C86-4BB7-A915-B021E4B3052D}.COM|Mixed Platforms.Build.0 = Release|Any CPU - {F14940F8-3C86-4BB7-A915-B021E4B3052D}.COM|x64.ActiveCfg = Release|Any CPU - {F14940F8-3C86-4BB7-A915-B021E4B3052D}.COM|x64.Build.0 = Release|Any CPU - {F14940F8-3C86-4BB7-A915-B021E4B3052D}.COM|x86.ActiveCfg = Release|Any CPU - {F14940F8-3C86-4BB7-A915-B021E4B3052D}.COM|x86.Build.0 = Release|Any CPU - {F14940F8-3C86-4BB7-A915-B021E4B3052D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F14940F8-3C86-4BB7-A915-B021E4B3052D}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F14940F8-3C86-4BB7-A915-B021E4B3052D}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU - {F14940F8-3C86-4BB7-A915-B021E4B3052D}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU - {F14940F8-3C86-4BB7-A915-B021E4B3052D}.Debug|x64.ActiveCfg = Debug|Any CPU - {F14940F8-3C86-4BB7-A915-B021E4B3052D}.Debug|x64.Build.0 = Debug|Any CPU - {F14940F8-3C86-4BB7-A915-B021E4B3052D}.Debug|x86.ActiveCfg = Debug|Any CPU - {F14940F8-3C86-4BB7-A915-B021E4B3052D}.Debug|x86.Build.0 = Debug|Any CPU - {F14940F8-3C86-4BB7-A915-B021E4B3052D}.GenesisDisplayTool_V01|Any CPU.ActiveCfg = Release|Any CPU - {F14940F8-3C86-4BB7-A915-B021E4B3052D}.GenesisDisplayTool_V01|Any CPU.Build.0 = Release|Any CPU - {F14940F8-3C86-4BB7-A915-B021E4B3052D}.GenesisDisplayTool_V01|Mixed Platforms.ActiveCfg = Release|Any CPU - {F14940F8-3C86-4BB7-A915-B021E4B3052D}.GenesisDisplayTool_V01|Mixed Platforms.Build.0 = Release|Any CPU - {F14940F8-3C86-4BB7-A915-B021E4B3052D}.GenesisDisplayTool_V01|x64.ActiveCfg = Release|Any CPU - {F14940F8-3C86-4BB7-A915-B021E4B3052D}.GenesisDisplayTool_V01|x64.Build.0 = Release|Any CPU - {F14940F8-3C86-4BB7-A915-B021E4B3052D}.GenesisDisplayTool_V01|x86.ActiveCfg = Release|Any CPU - {F14940F8-3C86-4BB7-A915-B021E4B3052D}.GenesisDisplayTool_V01|x86.Build.0 = Release|Any CPU - {F14940F8-3C86-4BB7-A915-B021E4B3052D}.PerformTest|Any CPU.ActiveCfg = Release|Any CPU - {F14940F8-3C86-4BB7-A915-B021E4B3052D}.PerformTest|Any CPU.Build.0 = Release|Any CPU - {F14940F8-3C86-4BB7-A915-B021E4B3052D}.PerformTest|Mixed Platforms.ActiveCfg = Release|Any CPU - {F14940F8-3C86-4BB7-A915-B021E4B3052D}.PerformTest|Mixed Platforms.Build.0 = Release|Any CPU - {F14940F8-3C86-4BB7-A915-B021E4B3052D}.PerformTest|x64.ActiveCfg = Release|Any CPU - {F14940F8-3C86-4BB7-A915-B021E4B3052D}.PerformTest|x64.Build.0 = Release|Any CPU - {F14940F8-3C86-4BB7-A915-B021E4B3052D}.PerformTest|x86.ActiveCfg = Release|Any CPU - {F14940F8-3C86-4BB7-A915-B021E4B3052D}.PerformTest|x86.Build.0 = Release|Any CPU - {F14940F8-3C86-4BB7-A915-B021E4B3052D}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F14940F8-3C86-4BB7-A915-B021E4B3052D}.Release|Any CPU.Build.0 = Release|Any CPU - {F14940F8-3C86-4BB7-A915-B021E4B3052D}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU - {F14940F8-3C86-4BB7-A915-B021E4B3052D}.Release|Mixed Platforms.Build.0 = Release|Any CPU - {F14940F8-3C86-4BB7-A915-B021E4B3052D}.Release|x64.ActiveCfg = Release|Any CPU - {F14940F8-3C86-4BB7-A915-B021E4B3052D}.Release|x64.Build.0 = Release|Any CPU - {F14940F8-3C86-4BB7-A915-B021E4B3052D}.Release|x86.ActiveCfg = Release|Any CPU - {F14940F8-3C86-4BB7-A915-B021E4B3052D}.Release|x86.Build.0 = Release|Any CPU - {F14940F8-3C86-4BB7-A915-B021E4B3052D}.ReleaseTest|Any CPU.ActiveCfg = Release|Any CPU - {F14940F8-3C86-4BB7-A915-B021E4B3052D}.ReleaseTest|Any CPU.Build.0 = Release|Any CPU - {F14940F8-3C86-4BB7-A915-B021E4B3052D}.ReleaseTest|Mixed Platforms.ActiveCfg = Release|Any CPU - {F14940F8-3C86-4BB7-A915-B021E4B3052D}.ReleaseTest|Mixed Platforms.Build.0 = Release|Any CPU - {F14940F8-3C86-4BB7-A915-B021E4B3052D}.ReleaseTest|x64.ActiveCfg = Release|Any CPU - {F14940F8-3C86-4BB7-A915-B021E4B3052D}.ReleaseTest|x64.Build.0 = Release|Any CPU - {F14940F8-3C86-4BB7-A915-B021E4B3052D}.ReleaseTest|x86.ActiveCfg = Release|Any CPU - {F14940F8-3C86-4BB7-A915-B021E4B3052D}.ReleaseTest|x86.Build.0 = Release|Any CPU - {F14940F8-3C86-4BB7-A915-B021E4B3052D}.XP32bit|Any CPU.ActiveCfg = Release|Any CPU - {F14940F8-3C86-4BB7-A915-B021E4B3052D}.XP32bit|Any CPU.Build.0 = Release|Any CPU - {F14940F8-3C86-4BB7-A915-B021E4B3052D}.XP32bit|Mixed Platforms.ActiveCfg = Release|Any CPU - {F14940F8-3C86-4BB7-A915-B021E4B3052D}.XP32bit|Mixed Platforms.Build.0 = Release|Any CPU - {F14940F8-3C86-4BB7-A915-B021E4B3052D}.XP32bit|x64.ActiveCfg = Release|Any CPU - {F14940F8-3C86-4BB7-A915-B021E4B3052D}.XP32bit|x64.Build.0 = Release|Any CPU - {F14940F8-3C86-4BB7-A915-B021E4B3052D}.XP32bit|x86.ActiveCfg = Release|Any CPU - {F14940F8-3C86-4BB7-A915-B021E4B3052D}.XP32bit|x86.Build.0 = Release|Any CPU {F8D84B9A-0FCF-4083-B067-8C39B7314152}._MANUAL_CONTROL|Any CPU.ActiveCfg = Release|Any CPU {F8D84B9A-0FCF-4083-B067-8C39B7314152}._MANUAL_CONTROL|Any CPU.Build.0 = Release|Any CPU {F8D84B9A-0FCF-4083-B067-8C39B7314152}._MANUAL_CONTROL|Mixed Platforms.ActiveCfg = Release|Any CPU @@ -5717,7 +5654,6 @@ Global {8BDD2789-96D4-43EA-AAD7-0F49D0347998} = {EC10DAFE-88CF-42D5-99E7-D247938B6430} {FD30EB7A-7809-4E62-9374-791D31877D6D} = {696A94BE-7237-4AE2-9E36-6059DE519971} {AFC428F5-0141-4A3F-BEF2-470A89D972F6} = {C17363DF-A043-4F25-8EBA-2B3B078227DC} - {F14940F8-3C86-4BB7-A915-B021E4B3052D} = {B4F8545B-F379-42A5-BDE9-763FD7B79598} {F8D84B9A-0FCF-4083-B067-8C39B7314152} = {68323534-4337-4E3A-89BC-11D8BB55343A} {01A28A70-D5E6-4898-8C22-E64400A3B80F} = {68323534-4337-4E3A-89BC-11D8BB55343A} {4806D89A-FBFB-41BC-AAA7-2242444BF55A} = {FA3B769A-E83E-40F0-923A-DFA124AF0C2C} diff --git a/Common/Hardware/WaterMeter/MagFlux/DataPackages/DataTypes/Calibration.cs b/Common/Hardware/WaterMeter/MagFlux/DataPackages/DataTypes/Calibration.cs index 7a982ea8..af42c27a 100644 --- a/Common/Hardware/WaterMeter/MagFlux/DataPackages/DataTypes/Calibration.cs +++ b/Common/Hardware/WaterMeter/MagFlux/DataPackages/DataTypes/Calibration.cs @@ -1,7 +1,4 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; namespace Xylem.Common.Hardware.WaterMeter.MagFlux.DataPackages.DataTypes { diff --git a/Common/Hardware/WaterMeter/MagFlux/Protocols/RequestProtocol/RequestProtocol.cs b/Common/Hardware/WaterMeter/MagFlux/Protocols/RequestProtocol/RequestProtocol.cs index 0bf07ae7..4d7c7c64 100644 --- a/Common/Hardware/WaterMeter/MagFlux/Protocols/RequestProtocol/RequestProtocol.cs +++ b/Common/Hardware/WaterMeter/MagFlux/Protocols/RequestProtocol/RequestProtocol.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Linq; using System.Threading; using NLog; +using XYLEM; using Xylem.Common.Hardware.Interfaces.Ports.PortCore.EventArguments; using Xylem.Common.Hardware.Interfaces.Protocols.ProtocolCore; using Xylem.Common.Hardware.Interfaces.Protocols.ProtocolCore.EventArguments; @@ -18,28 +19,37 @@ namespace Xylem.Common.Hardware.WaterMeter.MagFlux.Protocols.RequestProtocol public class RequestProtocol : BaseProtocol { private readonly String _ident; - private readonly ILogger _logger; + private readonly ILogger _logger; private IMagFluxRequestProtocol _magFluxRequestProtocol; + private IMagFluxAPI _magFluxApi; // calibration point in cubic meters per hour private List _calibPointsCmPerH; /// + /// + /// - MagFluxApi + /// public RequestProtocol(String ident) : base(ident) { _ident = ident; - _magFluxRequestProtocol = new MagFlux6200(); + _magFluxApi = new MagFluxAPI(); _calibPointsCmPerH = new List(); - _logger = NLogHelper.CreateOrGetMultiLogger(_ident, "", "RequestProtocol", "MeterBase", "MeterBase"); + _logger = NLogHelper.CreateOrGetMultiLogger(_ident, "", "RequestProtocol", "MeterBase", + "MeterBase"); } /// > + /// + /// - MagFluxApi + /// public override void Dispose() { _calibPointsCmPerH.Clear(); _calibPointsCmPerH = null; - _magFluxRequestProtocol?.CloseConnection(); + CloseConnection(); _magFluxRequestProtocol = null; + _magFluxApi = null; base.Dispose(); } @@ -54,21 +64,6 @@ namespace Xylem.Common.Hardware.WaterMeter.MagFlux.Protocols.RequestProtocol throw new NotImplementedException(); } - /// - /// Wrapper to connect MagFlux - /// - /// - /// - /// - Initial - /// - public Boolean IsConnected() - { - if (_magFluxRequestProtocol == null) return false; - var retVal = _magFluxRequestProtocol.IsConnected(); - _logger.Debug(retVal ? $"{_ident} Connection established: true" : $"{_ident} Connection check request failed!"); - return retVal; - } - /// /// Wrapper to connect MagFlux /// @@ -77,13 +72,19 @@ namespace Xylem.Common.Hardware.WaterMeter.MagFlux.Protocols.RequestProtocol /// /// - Initial /// + /// + /// - MagFluxApi + /// public Boolean Connect(String portName) { + if (_magFluxApi == null) return false; + _magFluxRequestProtocol = _magFluxApi.Connect(portName); if (_magFluxRequestProtocol == null) return false; - var retVal = _magFluxRequestProtocol.Connect(portName); - _logger.Debug(retVal ? $"{_ident} Connection established to {portName}" : - $"{_ident} Connection to {portName} failed!"); + Thread.Sleep(100); + var retVal = _magFluxRequestProtocol.IsConnected(); + _logger.Debug(retVal ? $"{_ident} Connection established to {portName}" : + $"{_ident} Connection to {portName} failed!"); return retVal; } @@ -106,7 +107,8 @@ namespace Xylem.Common.Hardware.WaterMeter.MagFlux.Protocols.RequestProtocol if (_magFluxRequestProtocol == null) return false; var retVal = _magFluxRequestProtocol.SetLogLocation(path, fileName); if (string.IsNullOrEmpty(fileName)) fileName = "?"; - _logger.Debug(retVal ? $"{_ident} Logging set to: {path}\\{fileName}" : $"{_ident} Logging path setup failed!"); + _logger.Debug(retVal ? $"{_ident} Logging set to: {path}\\{fileName}" : + $"{_ident} Logging path setup failed!"); return retVal; } @@ -132,6 +134,7 @@ namespace Xylem.Common.Hardware.WaterMeter.MagFlux.Protocols.RequestProtocol $"{_ident} PcbId (SensorSerialNo) request failed!"); return retVal; } + /// /// Wrapper to set the PcbId which equals the sensor serial number /// @@ -148,6 +151,7 @@ namespace Xylem.Common.Hardware.WaterMeter.MagFlux.Protocols.RequestProtocol $"{_ident} Set PcbId (SensorSerialNo) request failed!"); return retVal; } + /// /// Wrapper to get unique id referenced as PCBID /// @@ -161,7 +165,8 @@ namespace Xylem.Common.Hardware.WaterMeter.MagFlux.Protocols.RequestProtocol pcbId = "?"; if (_magFluxRequestProtocol == null) return false; var retVal = _magFluxRequestProtocol.GetUniqueId(out pcbId); - _logger.Debug(retVal ? $"{_ident} UniqueId: {pcbId}" : $"{_ident} UniqueId request failed!"); + _logger.Debug(retVal ? $"{_ident} UniqueId: {pcbId}" : + $"{_ident} UniqueId request failed!"); return retVal; } @@ -178,9 +183,11 @@ namespace Xylem.Common.Hardware.WaterMeter.MagFlux.Protocols.RequestProtocol fwVersion = "?"; if (_magFluxRequestProtocol == null) return false; var retVal = _magFluxRequestProtocol.GetFirmwareVersion(out fwVersion); - _logger.Debug(retVal ? $"{_ident} FW Version: {fwVersion}" : $"{_ident} FW Version request failed!"); + _logger.Debug(retVal ? $"{_ident} FW Version: {fwVersion}" : + $"{_ident} FW Version request failed!"); return retVal; } + /// /// Wrapper to get FW Build Date /// @@ -194,7 +201,8 @@ namespace Xylem.Common.Hardware.WaterMeter.MagFlux.Protocols.RequestProtocol fwBuildDate = "?"; if (_magFluxRequestProtocol == null) return false; var retVal = _magFluxRequestProtocol.GetFwBuildDate(out fwBuildDate); - _logger.Debug(retVal ? $"{_ident} FW Build Date: {fwBuildDate}" : $"{_ident} FW Build Date request failed!"); + _logger.Debug(retVal ? $"{_ident} FW Build Date: {fwBuildDate}" : + $"{_ident} FW Build Date request failed!"); return retVal; } @@ -211,7 +219,8 @@ namespace Xylem.Common.Hardware.WaterMeter.MagFlux.Protocols.RequestProtocol fwGitHash = "?"; if (_magFluxRequestProtocol == null) return false; var retVal = _magFluxRequestProtocol.GetFwGitHash(out fwGitHash); - _logger.Debug(retVal ? $"{_ident} FW GIT Hash: {fwGitHash}" : $"{_ident} FW GIT Hash request failed!"); + _logger.Debug(retVal ? $"{_ident} FW GIT Hash: {fwGitHash}" : + $"{_ident} FW GIT Hash request failed!"); return retVal; } @@ -233,7 +242,8 @@ namespace Xylem.Common.Hardware.WaterMeter.MagFlux.Protocols.RequestProtocol meterSize = 0; if (_magFluxRequestProtocol == null) return false; var retVal = _magFluxRequestProtocol.GetDn_mm(out meterSize); - _logger.Debug(retVal ? $"{_ident} MeterSize DN: {meterSize}" : $"{_ident} MeterSize request failed!"); + _logger.Debug(retVal ? $"{_ident} MeterSize DN: {meterSize}" : + $"{_ident} MeterSize request failed!"); return retVal; } @@ -249,7 +259,8 @@ namespace Xylem.Common.Hardware.WaterMeter.MagFlux.Protocols.RequestProtocol { if (_magFluxRequestProtocol == null) return false; var retVal = _magFluxRequestProtocol.SetDn_mm(meterSize); - _logger.Debug(retVal ? $"{_ident} Set MeterSize DN: {meterSize}" : $"{_ident} Set MeterSize failed!"); + _logger.Debug(retVal ? $"{_ident} Set MeterSize DN: {meterSize}" : + $"{_ident} Set MeterSize failed!"); return retVal; } @@ -389,8 +400,12 @@ namespace Xylem.Common.Hardware.WaterMeter.MagFlux.Protocols.RequestProtocol public Boolean CloseConnection() { if (_magFluxRequestProtocol == null) return false; - var retVal = _magFluxRequestProtocol.CloseConnection(); - _logger.Debug(retVal ? $"{_ident} Connection closed" : $"{_ident} Close connection request failed!"); + MagFluxAPI.CloseAllConnections(); + + Thread.Sleep(100); + var retVal = _magFluxRequestProtocol.IsConnected(); + _logger.Debug(retVal ? $"{_ident} Connection closed" : + $"{_ident} Close connection request failed!"); return retVal; } } diff --git a/Common/Hardware/WaterMeter/MagFlux/Protocols/RequestProtocol/RequestProtocol.csproj b/Common/Hardware/WaterMeter/MagFlux/Protocols/RequestProtocol/RequestProtocol.csproj index 6255094c..3d6bc376 100644 --- a/Common/Hardware/WaterMeter/MagFlux/Protocols/RequestProtocol/RequestProtocol.csproj +++ b/Common/Hardware/WaterMeter/MagFlux/Protocols/RequestProtocol/RequestProtocol.csproj @@ -31,6 +31,9 @@ 4 + + ..\..\..\..\..\MagFluxApi\magflux_api.dll + ..\..\..\..\..\packages\NLog.5.2.2\lib\net46\NLog.dll @@ -53,10 +56,6 @@ - - {F14940F8-3C86-4BB7-A915-B021E4B3052D} - modbus_master_csharp - {4B88B0D3-E791-4774-9521-EABEACDC420E} Logging diff --git a/Common/MagFluxApi/magflux_api.dll b/Common/MagFluxApi/magflux_api.dll new file mode 100644 index 0000000000000000000000000000000000000000..e0addceee53e82df639bcca608fd9a1af6dbf42e GIT binary patch literal 267264 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 - - - - - \ No newline at end of file diff --git a/Common/modbus_master_csharp/Base/AppConst.cs b/Common/modbus_master_csharp/Base/AppConst.cs deleted file mode 100644 index 9d974cff..00000000 --- a/Common/modbus_master_csharp/Base/AppConst.cs +++ /dev/null @@ -1,111 +0,0 @@ -using XYLEM.Base.Files; -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Reflection; - -namespace XYLEM.Base -{ - internal static class AppConst - { - static ProgramLogCSVFileClass _Logger = new ProgramLogCSVFileClass(); - - internal static ProgramLogCSVFileClass Logger { get { return _Logger; } } - - internal static bool IsBeta - { - get - { - return true; - //return false; - } - } - - // - // Summary: - // Gets the product version associated with this application. - // - // Returns: - // The product version. - internal static string ProductVersion - { - get { return "1.0.0.1"; } - } - - - /// - /// Return application MJK No. - /// Have to be manuel changed by programmer for every application to fit install! - /// - /// return ex. "840126" - internal static string ProductMainMJKNo() - { - return "MJK_MAGFLUX_MODBUS"; - } - - /// - /// Return MJK Programs standard folder in My documents. - /// - /// return ex. "c:\...\My Documents\MJK Automation\" - internal static string ProgramsUserFolderRootPath() - { - return Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "MJK Automation\\"); - } - - - /// - /// Return application folder in My documents. - /// Have to be manuel changed by programmer for every application to fit install! - /// - /// return ex. "c:\...\My Documents\MJK Automation\840126\" - internal static string ProgramUserFolderRootPath() - { - return Path.Combine(ProgramsUserFolderRootPath(), ProductMainMJKNo() + "\\"); - } - - /// - /// Return application folder to use for user files. - /// Have to be manuel changed by programmer for every application to fit install! - /// - /// return ex. "c:\...\My Documents\MJK Automation\- UserFileSave\" - internal static string UserFileSavePath() - { - return Path.Combine(ProgramsUserFolderRootPath(), "- UserFileSave\\"); - } - - /// - /// Return application folder to use for program user settings . - /// Have to be manual changed by programmer for every application to fit install! - /// - /// return ex. "c:\...\My Documents\MJK Automation\840126\UserSettings\" - internal static string UserFileSettingsPath() - { - return Path.Combine(ProgramUserFolderRootPath(), "UserSettings\\"); - } - - /// - /// Try Create UserFileSavePath and SettingPath - /// - internal static void CreateStandardUserFolders() - { - try - { - if (!Directory.Exists(UserFileSavePath())) - { - Directory.CreateDirectory(UserFileSavePath()); - } - if (!Directory.Exists(ProgramUserFolderRootPath())) - { - Directory.CreateDirectory(ProgramUserFolderRootPath()); - } - } - catch (Exception ex) - { - Logger.AddToProgramLogFile(true, "Error creating default user folders ", ex.Message); - } - } - } -} diff --git a/Common/modbus_master_csharp/Base/ByteArrayExtensionClass.cs b/Common/modbus_master_csharp/Base/ByteArrayExtensionClass.cs deleted file mode 100644 index ce2c4631..00000000 --- a/Common/modbus_master_csharp/Base/ByteArrayExtensionClass.cs +++ /dev/null @@ -1,314 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace XYLEM.Base -{ - internal static class ByteArrayExtensionClass - { - /// - /// Compare if values in to array's is equal - /// - /// - /// - /// - public static bool Compare(this byte[] b1, byte[] b2) - { - // Validate buffers are the same length. - // This also ensures that the count does not exceed the length of either buffer. - return b1.SequenceEqual(b2); - } - - /// - /// Copy a chunk of bytes from start index to a specific LongLength - /// - /// - /// start offset 0..x - /// LongLength of returned byte data array - /// Return copy of byte data - public static byte[] Copy(this byte[] from, long startIndex, long LongLength) - { - try - { - byte[] bArray = new byte[LongLength]; - Array.Copy(from, startIndex, bArray, 0, LongLength); - return bArray; - } - catch (Exception ex) - { - throw new Exception(ex.ToFuncErrorText(typeof(ByteArrayExtensionClass).ToString(), "Copy()")); - } - } - - /// - /// Copy a chunk of bytes to the end of array, starting from start offset - /// - /// - /// start offset 0..x - /// Return copy of byte data - public static byte[] Copy(this byte[] from, long startIndex) - { - try - { - long LongLength = from.LongLength - startIndex; - byte[] bArray = new byte[LongLength]; - Array.Copy(from, startIndex, bArray, 0, LongLength); - return bArray; - } - catch (Exception ex) - { - throw new Exception(ex.ToFuncErrorText(typeof(ByteArrayExtensionClass).ToString(), "Copy()")); - } - } - - /// - /// Remove a chunk of bytes at the end of array, staring from offset index - /// - /// - /// offset 0..x - /// Return copy of byte data - public static byte[] Remove(this byte[] from, long fromIndex) - { - try - { - byte[] bArray = new byte[fromIndex]; - if (bArray.LongLength < fromIndex) - { - return from; - } - Array.Copy(from, 0, bArray, 0, fromIndex); - return bArray; - } - catch (Exception ex) - { - throw new Exception(ex.ToFuncErrorText(typeof(ByteArrayExtensionClass).ToString(), "Remove()")); - } - } - - - /// - /// Copy bytes until this character - /// - /// bytes to find character in - /// detection character - /// - internal static IEnumerable CopyUntil(this IEnumerable toInSeach, char untilThisCharacter) - { - try - { - if (toInSeach != null) - { - var withCharacter = toInSeach.TakeWhile(ch => ch != untilThisCharacter); - // has any data after this - if (withCharacter.Count() > 1) - { - return withCharacter; - } - } - return null; // nothing to return - } - catch (Exception ex) - { - throw new Exception(ex.ToFuncErrorText(typeof(ByteArrayExtensionClass).ToString(), "CopyUntil()")); - } - } - - /// - /// Copy bytes after this character - /// - /// bytes to find character in - /// detection character - /// - internal static IEnumerable CopyAfterThis(this IEnumerable toInSeach, char afterFirstCharacterOfThis) - { - try - { - if (toInSeach != null) - { - var withCharacter = toInSeach.SkipWhile(ch => ch != afterFirstCharacterOfThis); - // has any data after this - if (withCharacter.Count() > 1) - { - return withCharacter.Skip(1); - } - } - return null; // nothing to return - } - catch (Exception ex) - { - throw new Exception(ex.ToFuncErrorText(typeof(ByteArrayExtensionClass).ToString(), "CopyAfterThis()")); - } - } - - /// - /// Copy bytes after this sequence of bytes - /// - /// bytes to find character in - /// detection sequence - /// - internal static IEnumerable CopyAfterThis(this IEnumerable toSeachIn, IEnumerable afterSequenceThis) - { - try - { - var toSeachNow = toSeachIn; - while ((toSeachNow != null) && (toSeachNow.Count() > 0) && (afterSequenceThis != null)) - { - var startCh = afterSequenceThis.First(); - var sequenceLng = afterSequenceThis.Count(); - toSeachNow = toSeachNow.SkipWhile(ch => ch != startCh); - // If there is data after a possible sequence.. check if it have sequnece - if ((toSeachNow.Count() > sequenceLng) && afterSequenceThis.SequenceEqual(toSeachNow.Take(sequenceLng))) - { - if (toSeachNow.Count() > sequenceLng) - { - return toSeachNow.Skip(afterSequenceThis.Count()); - } - return null; // nothing after sequence to return - } - // Not found - if ((toSeachNow == null) || (toSeachNow.Count() == 0)) - { - return null; // nothing to return - } - // increase to next to find next start - toSeachNow = toSeachNow.Skip(1); - } - return null; // nothing to return - } - catch (Exception ex) - { - throw new Exception(ex.ToFuncErrorText(typeof(ByteArrayExtensionClass).ToString(), "CopyAfterThis()")); - } - } - - /// - /// Swap high and low byte in a word(16bit) for all words in a byte array (use ex. to a line data correct for conversion to computer U16 from modbus u16) - /// - /// Data to swap (SWAP IS NOT DONE ON A COPY, BUT ON THE ARRAY GIVEN AS INPUT - /// return swapped input array reference (done to help supporting single line code use ex. "var data = byteArrayNeedingSwap.SwapByteInWord();") - public static byte[] SwapByteInWord(this byte[] data) - { - try - { - if (data != null) - { - for (long i = 0; i < data.LongLength; i += 2) - { - byte b = data[i]; - data[i] = data[i + 1]; - data[i + 1] = b; - } - } - return data; - } - catch (Exception ex) - { - throw new Exception(ex.ToFuncErrorText(typeof(ByteArrayExtensionClass).ToString(), "SwapByteInWord()")); - } - } - - - /// - /// Compare if to array's has the same content (like array1 == array2) - /// - /// - /// - /// return true if the same - public static bool IsEqual(this byte[] array1, byte[] array2) - { - try - { - if ((array1 != null) && (array2 != null) && (array1.LongLength == array2.LongLength)) - { - // To speed up - var result = array1.SequenceEqual(array2); - //{ - //#if (!DEBUG) - //#error Test code!!! - //#endif - //if (!result) { - // var test = "Not the same!!"; - // } - //} - return result; - } - return false;// not the same - } - catch (Exception ex) - { - throw new Exception(ex.ToFuncErrorText(typeof(ByteArrayExtensionClass).ToString(), "IsEqual()")); - } - } - - /// - /// Slice an array of bytes in the same sizes - /// - /// Data to slice an array of bytes from - /// start in data for beginning slice of bytes - /// The length of each slice of bytes - /// List of byte slices with the same size - public static List SliceSameSize(this byte[] source, long startIndex, long eachSliceLength) - { - try - { - if (source == null) - { - return null; - } - var LongLength = source.LongLength; - var slices = new List(); - for (long offset = startIndex; offset < LongLength; offset += eachSliceLength) - { - slices.Add(Copy(source, offset, eachSliceLength)); - } - return slices; - } - catch (Exception ex) - { - throw new Exception(ex.ToFuncErrorText(typeof(ByteArrayExtensionClass).ToString(), "SliceSameSize()")); - } - } - - /// - /// Slice a single array with a specific length - /// - /// Data to slice an array of bytes from - /// start in data for beginning slice of bytes - /// how long is the slice from start index - /// - public static byte[] Slice(this byte[] source, long startIndex, long length) - { - try - { - var returnValue = new byte[length]; - Array.Copy(source, startIndex, returnValue, 0, length); - return returnValue; - } - catch (Exception ex) - { - throw new InvalidOperationException(ex.ToFuncErrorText(typeof(ByteArrayExtensionClass).ToString(), "Slice()")); - } - } - - - /// - /// Fill or pad array with a specific value - /// - /// - /// - /// - public static byte[] Fill(this byte[] data, byte valueForFill) - { - if (data != null) - { - for (long i = 0; i < data.LongLength; i++) - { - data[i] = valueForFill; - } - } - return data; - } - } -} diff --git a/Common/modbus_master_csharp/Base/Convert1970SecToTime.cs b/Common/modbus_master_csharp/Base/Convert1970SecToTime.cs deleted file mode 100644 index 6dfa2ad8..00000000 --- a/Common/modbus_master_csharp/Base/Convert1970SecToTime.cs +++ /dev/null @@ -1,153 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace XYLEM.Base -{ - internal enum TimeType - { - UTC, - Local - }; - - internal static class TimeConvertClass - { - /// - /// convert to a text showing file time ex. [20151201-135917] - /// - /// - /// - internal static string ToFileTimeTxt(this DateTime time) - { - return String.Format("[{0}{1:00}{2:00}-{3:00}{4:00}{5:00}]", time.Year, time.Month, time.Day, time.Hour, time.Minute, time.Second); - } - - /// - /// Convert 1970Sec to 32-bit time to Time - /// - /// - /// - internal static TimeSpan ConvertTimeSpan(this UInt32 timeSec) - { - return new TimeSpan((int)(timeSec/3600), (int)(timeSec%3600/60), (int)(timeSec%60)); - } - - internal static string ConvertSecondsToTimeSpanTxt(this Int32 timeSec) - { - return (timeSec >= 0 ? "" : "-") + ((UInt32)Math.Abs(timeSec)).ConvertSecondsToTimeSpanTxt(); - } - - internal static string ConvertToTimeSpanTxt(this TimeSpan time) - { - if (time.Days > 0) - { - return string.Format((time.Days > 1 ? "{0} days " : "1 day ") + "{1:00}:{2:00}:{3:00}", time.Days, time.Hours, time.Minutes, time.Seconds); - } - return string.Format("{0:00}:{1:00}:{2:00}", time.Hours, time.Minutes, time.Seconds); - } - - internal static string ConvertSecondsToTimeSpanTxt(this UInt32 timeSec) - { - TimeSpan time = new TimeSpan((int)(timeSec / 3600), (int)(timeSec % 3600 / 60), (int)(timeSec % 60)); - return time.ConvertToTimeSpanTxt(); - } - - internal static string ConvertSecondsToTimeSpanTxt(this Int16 timeSec) - { - return ConvertSecondsToTimeSpanTxt((Int32)timeSec); - } - - internal static string ConvertSecondsToTimeSpanTxt(this UInt16 timeSec) - { - return ConvertSecondsToTimeSpanTxt((UInt32)timeSec); - } - - internal static string ConvertMinuteToTimeSpanTxt(this Int16 timeMin) - { - return ConvertSecondsToTimeSpanTxt((Int32)timeMin * 60); - } - - internal static string ConvertMinuteToTimeSpanTxt(this UInt16 timeMin) - { - return ConvertSecondsToTimeSpanTxt((UInt32)timeMin * 60); - } - - internal static string ConvertHourToTimeSpanTxt(this Int16 timeHour) - { - return ConvertSecondsToTimeSpanTxt((Int32)timeHour * 3600); - } - - internal static string ConvertHourToTimeSpanTxt(this UInt16 timeHour) - { - return ConvertSecondsToTimeSpanTxt((UInt32)timeHour * 3600); - } - - - - /// - /// Convert 1970Sec to 32-bit time to Time - /// - /// - /// - internal static DateTime Convert1970SecToTime(this UInt32 timeSec1970, TimeType timetype) - { - return timetype == TimeType.Local ? - (new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Local)).AddSeconds(timeSec1970) - : (new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)).AddSeconds(timeSec1970); - } - - /// - /// Convert Time to 1970Sec - /// - /// - /// - internal static UInt32 ConvertTimeTo1970Sec(this DateTime time, TimeType timetype) - { - TimeSpan ts = timetype == TimeType.Local ? - (time - new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Local)) - : (time.ToUniversalTime() - new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)); - return Convert.ToUInt32(ts.TotalSeconds); - } - - /// - /// Convert 2000Sec to 32-bit time to Time - /// - /// - /// - internal static DateTime Convert2000SecToTime(this UInt32 timeSec2000) - { - return (new DateTime(2000, 1, 1, 0, 0, 0, DateTimeKind.Utc)).AddSeconds(timeSec2000); - } - - /// - /// Convert Time to 2000Sec - /// - /// - /// - internal static UInt32 ConvertTimeTo2000Sec(this DateTime time, TimeType timetype) - { - TimeSpan ts = timetype == TimeType.Local ? - (time - new DateTime(2000, 1, 1, 0, 0, 0, DateTimeKind.Local)) - : (time.ToUniversalTime() - new DateTime(2000, 1, 1, 0, 0, 0, DateTimeKind.Utc)); - return Convert.ToUInt32(ts.TotalSeconds); - } - - /// - /// Convert time to a String - /// - /// - /// - public static String ToLocalDateTimeString(this DateTime time) - { - DateTime localTime; - localTime = (time.Kind == DateTimeKind.Utc) ? time.ToLocalTime() : time; - return (localTime.Year.ToString().PadLeft(4, '0') + "/" + localTime.Month.ToString().PadLeft(2, '0') + "/" + localTime.Day.ToString().PadLeft(2, '0') + " " - + localTime.Hour.ToString().PadLeft(2, '0') + ":" + localTime.Minute.ToString().PadLeft(2, '0') + ":" + localTime.Second.ToString().PadLeft(2, '0')); - } - - public static readonly bool IsLocalHourFormatAM_PM = new DateTime(2000, 1, 1, 13, 0, 0).ToLongTimeString().ToLower().Contains("pm"); - } -} - diff --git a/Common/modbus_master_csharp/Base/DataUnion.cs b/Common/modbus_master_csharp/Base/DataUnion.cs deleted file mode 100644 index e4c7c0f1..00000000 --- a/Common/modbus_master_csharp/Base/DataUnion.cs +++ /dev/null @@ -1,459 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Runtime.InteropServices; -using System.Text; -using System.Threading.Tasks; - -namespace XYLEM.Base -{ - public static class DataUnionTools - { - public static byte[] Reverse(this byte[] data) - { - Array.Reverse(data); - return data; - } - } - - /// - /// Convert DataObjClass.DataType to Byte - /// - [StructLayout(LayoutKind.Explicit)] - public struct DataUnion - { - //----------------------------- Data String to Convert Rx to ---------------------- - #region ---- Inset Data Bytes in union -------- - public enum DataByteLayoutType - { - Normal, - SwapAll, - SwapWords // KellerType - } - - internal UInt32 Conv32BitToLowAdrHighValue(UInt32 input) - { - return (input >> 16) | (input << 16); - } - - /// - /// Convert input byte data to union data - /// - /// - /// - public DataUnion PutByteArray(Byte[] bInputArray, DataByteLayoutType byteType) - { - if (byteType == DataByteLayoutType.SwapAll) - { - Array.Reverse(bInputArray); - } - //dataRxText = ""; - if ((bInputArray == null) || (bInputArray.Length == 0)) - { - u64Data = 0; - return this; - } - bData = bInputArray[0]; - if (bInputArray.Length == 1) - { - return this; - } - - bData1 = bInputArray[1]; - if (bInputArray.Length == 2) - { // 16bit / 2 x 8bit - return this; - } - - bData2 = bInputArray[2]; - if (bInputArray.Length == 3) - { - if (byteType == DataByteLayoutType.SwapWords) - { - throw new Exception("SwapWords Not Supported!"); - } - return this; - } - - bData3 = bInputArray[3]; - if (bInputArray.Length == 4) - {// 32bit / 4 x 8bit - if (byteType == DataByteLayoutType.SwapWords) - { - u32Data = Conv32BitToLowAdrHighValue(u32Data); - } - return this; - } - - bData4 = bInputArray[4]; - if (bInputArray.Length == 5) - { - if (byteType == DataByteLayoutType.SwapWords) - { - throw new Exception("SwapWords Not Supported!"); - } - return this; - } - - bData5 = bInputArray[5]; - if (bInputArray.Length == 6) - { - if (byteType == DataByteLayoutType.SwapWords) - { - throw new Exception("SwapWords Not Supported!"); - } - return this; - } - - bData6 = bInputArray[6]; - if (bInputArray.Length == 7) - { - if (byteType == DataByteLayoutType.SwapWords) - { - throw new Exception("SwapWords Not Supported!"); - } - return this; - } - - bData7 = bInputArray[7]; - if (byteType == DataByteLayoutType.SwapWords) - { // 64bit / 8 x 8bit - throw new Exception("SwapWords Not Supported!"); - } - return this; - } - - #endregion - - #region ---- Put data as Value in to union ----- - public void PutValue(UInt32 u32DataIn) - { - u32Data = u32DataIn; - } - - public void PutValue(Int32 s32DataIn) - { - s32Data = s32DataIn; - } - - public void PutValue(Single f32DataIn) - { - f32Data = f32DataIn; - } - - public void PutValue(UInt64 u64DataIn) - { - u64Data = u64DataIn; - } - - public void PutValue(Int64 s64DataIn) - { - s64Data = s64DataIn; - } - - public void PutValue(double f64DataIn) - { - f64Data = f64DataIn; - } - #endregion - - #region ---- Get ByteArray from input as a value ---- - public byte[] GetByteArray() - { - return new byte[] { bData, bData1, bData2, bData3, bData4, bData5, bData6, bData7 }; - } - - - void DoTxByteLayout(ref byte[] txData, DataByteLayoutType type) - { - try - { - if ((txData == null) || (txData.Length == 0)) - { - throw new Exception("Has no txData"); - } - switch (type) - { - case DataByteLayoutType.SwapAll: Array.Reverse(txData); break; - case DataByteLayoutType.SwapWords: - if (txData.Length == 4) - { // 32bit / 2 x word - byte[] firstWord = new byte[2]; - Array.Copy(txData, firstWord, 2); - Array.Copy(txData, 2, txData, 0, 2); - Array.Copy(firstWord, 0, txData, 2, 2); - } - else if (txData.Length == 8) - { // 64bit / 2 x word - throw new Exception("SwapWords Not Supported!"); - } - break; - case DataByteLayoutType.Normal: break; // Do nothing - default: throw new Exception("Unkown type = " + type.ToString()); - } - } - catch (Exception ex) - { - throw new Exception("Error in " + this + " in func. DoTxByteLayout()! message = " + ex.Message); - } - } - - - /// - /// Convert to Tx data to unsigned 16bit - /// - /// - /// - public byte[] GetByteArray(UInt16 u16DataIn, DataByteLayoutType byteType) - { - byte[] bDataRet = new byte[2]; - u16Data = u16DataIn; - bDataRet[0] = bData; - bDataRet[1] = bData1; - DoTxByteLayout(ref bDataRet, byteType); - return bDataRet; - } - - /// - /// Convert to Tx data to signed 16bit - /// - /// - /// - public byte[] GetByteArray(Int16 s16DataIn, DataByteLayoutType byteType) - { - byte[] bDataRet = new byte[2]; - s16Data = s16DataIn; - bDataRet[0] = bData; - bDataRet[1] = bData1; - DoTxByteLayout(ref bDataRet, byteType); - return bDataRet; - } - - /// - /// Convert to Tx data to unsigned 32bit - /// - /// - /// - public byte[] GetByteArray(UInt32 u32DataIn, DataByteLayoutType byteType) - { - PutValue(u32DataIn); - byte[] bDataRet = new byte[4]; - bDataRet[0] = bData; - bDataRet[1] = bData1; - bDataRet[2] = bData2; - bDataRet[3] = bData3; - DoTxByteLayout(ref bDataRet, byteType); - return bDataRet; - } - - /// - /// Convert to Tx data to sign 32bit - /// - /// - /// - public byte[] GetByteArray(Int32 s32DataIn, DataByteLayoutType byteType) - { - PutValue(s32DataIn); - byte[] bDataRet = new byte[4]; - bDataRet[0] = bData; - bDataRet[1] = bData1; - bDataRet[2] = bData2; - bDataRet[3] = bData3; - DoTxByteLayout(ref bDataRet, byteType); - return bDataRet; - } - - /// - /// Convert to Tx data to Single 32bit - /// - /// - /// - public byte[] GetByteArray(Single f32DataIn, DataByteLayoutType byteType) - { - PutValue(f32DataIn); - byte[] bDataRet = new byte[4]; - bDataRet[0] = bData; - bDataRet[1] = bData1; - bDataRet[2] = bData2; - bDataRet[3] = bData3; - DoTxByteLayout(ref bDataRet, byteType); - return bDataRet; - } - - /// - /// Convert to Tx data to unsigned 64bit - /// - /// - /// - public byte[] GetByteArray(UInt64 u64DataIn, DataByteLayoutType byteType) - { - PutValue(u64DataIn); - byte[] bDataRet = new byte[8]; - bDataRet[0] = bData; - bDataRet[1] = bData1; - bDataRet[2] = bData2; - bDataRet[3] = bData3; - bDataRet[4] = bData4; - bDataRet[5] = bData5; - bDataRet[6] = bData6; - bDataRet[7] = bData7; - DoTxByteLayout(ref bDataRet, byteType); - return bDataRet; - } - - /// - /// Convert to Tx data to double (F64bit) - /// - /// - /// - public byte[] GetByteArray(Int64 s64DataIn, DataByteLayoutType byteType) - { - PutValue(s64DataIn); - byte[] bDataRet = new byte[8]; - bDataRet[0] = bData; - bDataRet[1] = bData1; - bDataRet[2] = bData2; - bDataRet[3] = bData3; - bDataRet[4] = bData4; - bDataRet[5] = bData5; - bDataRet[6] = bData6; - bDataRet[7] = bData7; - DoTxByteLayout(ref bDataRet, byteType); - return bDataRet; - } - - /// - /// Convert to Tx data to unsigned 64bit - /// - /// - /// - public byte[] GetByteArray(double f64DataIn, DataByteLayoutType byteType) - { - PutValue(f64DataIn); - byte[] bDataRet = new byte[8]; - bDataRet[0] = bData; - bDataRet[1] = bData1; - bDataRet[2] = bData2; - bDataRet[3] = bData3; - bDataRet[4] = bData4; - bDataRet[5] = bData5; - bDataRet[6] = bData6; - bDataRet[7] = bData7; - DoTxByteLayout(ref bDataRet, byteType); - return bDataRet; - } - - #endregion - - #region ---- Return Union data as a value ---- - public byte U8Data { get { return bData; } set { u16Data = bData; } } - public byte U8Data0 { get { return bData; } } - public byte U8Data1 { get { return bData1; } } - public byte U8Data2 { get { return bData2; } } - public byte U8Data3 { get { return bData3; } } - public byte U8Data4 { get { return bData4; } } - public byte U8Data5 { get { return bData5; } } - public byte U8Data6 { get { return bData6; } } - public byte U8Data7 { get { return bData7; } } - - public UInt16 U16Data { get { return u16Data; } set { u16Data = value; } } - public UInt16 U16Data0 { get { return u16Data; } } - public UInt16 U16Data1 { get { return u16Data1; } } - public UInt16 U16Data2 { get { return u16Data2; } } - public UInt16 U16Data3 { get { return u16Data3; } } - - public Int16 S16Data { get { return s16Data; } } - public Int16 S16Data0 { get { return s16Data; } } - public Int16 S16Data1 { get { return s16Data1; } } - public Int16 S16Data2 { get { return s16Data2; } } - public Int16 S16Data3 { get { return s16Data3; } } - - public UInt32 U32Data { get { return u32Data; } set { u32Data = value; } } - public UInt32 U32Data0 { get { return u32Data; } } - public UInt32 U32Data1 { get { return u32Data1; } } - - public Int32 S32Data { get { return s32Data; } set { s32Data = value; } } - public Int32 S32Data0 { get { return s32Data; } } - public Int32 S32Data1 { get { return s32Data1; } } - - public Single F32Data { get { return f32Data; } set { f32Data = value; } } - - public UInt64 U64Data { get { return u64Data; } } - public Int64 S64Data { get { return s64Data; } } - public double F64Data { get { return f64Data; } } - #endregion - - /// - /// Clear all data and return emptyunion - /// - /// - public DataUnion ClearData() - { - u64Data = 0; - return this; - } - - #region ---- Field Union Array ---- - //----------------------------- Field Union array ----------------------------- - [FieldOffset(0)] - private byte bData; - [FieldOffset(1)] - private byte bData1; - [FieldOffset(2)] - private byte bData2; - [FieldOffset(3)] - private byte bData3; - [FieldOffset(4)] - private byte bData4; - [FieldOffset(5)] - private byte bData5; - [FieldOffset(6)] - private byte bData6; - [FieldOffset(7)] - private byte bData7; - - [FieldOffset(0)] - private UInt16 u16Data; - [FieldOffset(2)] - private UInt16 u16Data1; - [FieldOffset(4)] - private UInt16 u16Data2; - [FieldOffset(6)] - private UInt16 u16Data3; - - [FieldOffset(0)] - private Int16 s16Data; - [FieldOffset(2)] - private Int16 s16Data1; - [FieldOffset(4)] - private Int16 s16Data2; - [FieldOffset(6)] - private Int16 s16Data3; - - [FieldOffset(0)] - private UInt32 u32Data; - [FieldOffset(4)] - private UInt32 u32Data1; - - [FieldOffset(0)] - private Int32 s32Data; - [FieldOffset(4)] - private Int32 s32Data1; - - [FieldOffset(0)] - private Single f32Data; - [FieldOffset(4)] - private Single f32Data1; - - [FieldOffset(0)] - private UInt64 u64Data; - - [FieldOffset(0)] - private Int64 s64Data; - - [FieldOffset(0)] - private double f64Data; - #endregion - - }; -} diff --git a/Common/modbus_master_csharp/Base/Files/PathTools.cs b/Common/modbus_master_csharp/Base/Files/PathTools.cs deleted file mode 100644 index 912c9e0b..00000000 --- a/Common/modbus_master_csharp/Base/Files/PathTools.cs +++ /dev/null @@ -1,115 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace XYLEM.Base.Files -{ - internal class PathTools - { - /// - /// File path trunk - /// - /// ex. c:\mainDir\subDir\text.text - /// ex. 12 char - /// ex. will return "...\subDir\text.text" because "subDir\text.text" is > 12 - public static string FilePathStartTrunk(string dirPath, int trunkLength) - { - try - { - if (!String.IsNullOrEmpty(dirPath) && (dirPath.Length > 40)) - { // if to big .. then trunk it - var pathSplits = dirPath.Split(new char[] { '\\' }, StringSplitOptions.RemoveEmptyEntries); - if (pathSplits.Length > 0) - { // are able to trunk - dirPath = ""; - foreach (var item in pathSplits.Reverse()) - { - dirPath = CombineDirAndFileSubPath(item, dirPath); - if (dirPath.Length > 40) - { - dirPath = CombineDirAndFileSubPath("...", dirPath); - break; // max length is now then just exit loop - } - } - } - } - return dirPath; - } - catch (Exception ex) - { - AppConst.Logger.AddFuncLog(true, "PathTools", "FilePathStartTrunk()", ex); - } - return "?"; - } - - /// - /// Create the directory path - /// - /// - /// - public static Boolean CreateDirPath(string dirPath) - { - try - { - // Determine whether the directory exists. - if (Directory.Exists(dirPath)) - { - //That path exists already - return false; - } - - // Try to create the directory. - DirectoryInfo di = Directory.CreateDirectory(dirPath); - return false; - } - catch - { - return true; - } - } - - /// - /// Create the directory for a file path - /// - /// - /// return true on error - public static bool CreateDirPathForFile(string pathForFile) - { - try - { - return CreateDirPath(GetDirPath(pathForFile)); - } - catch - { - return true; - } - } - - /// - /// Combine a directory path and sub path to a compleate path - /// - /// - /// - /// - public static string CombineDirAndFileSubPath(string dirPath, string subFilePath) - { - dirPath = dirPath.Trim().EndsWith("\\") ? - dirPath.Trim().Remove(dirPath.LastIndexOf("\\")) : - dirPath.Trim(); - return Path.Combine(dirPath, subFilePath.Trim()); - } - - /// - /// Get directory path - /// - /// - /// return true on error - public static string GetDirPath(string filePath) - { - return filePath.Remove(filePath.Trim().LastIndexOf("\\")); - } - } -} diff --git a/Common/modbus_master_csharp/Base/Files/ProgramLogCSVFileClass.cs b/Common/modbus_master_csharp/Base/Files/ProgramLogCSVFileClass.cs deleted file mode 100644 index 3a573247..00000000 --- a/Common/modbus_master_csharp/Base/Files/ProgramLogCSVFileClass.cs +++ /dev/null @@ -1,232 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.Linq; -using System.Runtime.CompilerServices; -using System.Text; -using System.Threading.Tasks; - - -namespace XYLEM.Base.Files -{ - public class ProgramLogCSVFileClass - { - private int _LogCountErrors = -1; - private int _LogCountTotal = -1; - - public int LogCountErrors { get { return _LogCountErrors; } } - public int LogCountTotal { get { return _LogCountTotal; } } - - public string StdLogFilePath = null; - - public event EventHandler OnErrorIsAdded; - -#if (!DEBUG) - public bool DoTimeStamp = false; -#else - /*#warning DoTimeStamp is true!!!!!!!!!!!!!!!! - public bool DoTimeStamp = true;*/ - public bool DoTimeStamp = false; -#endif - /// - /// init standard program logger - /// - /// use a custom log folder or null if use default - /// set a custom log file name without extension (ex. "UserCustomLogFile") or null if use default - public ProgramLogCSVFileClass(string stdLogFolderPath = null, string filename = null) - { - if (String.IsNullOrEmpty(filename)) - { - filename = $"{AppConst.ProductVersion}_program_log"; - } - if (!string.IsNullOrEmpty(stdLogFolderPath)) - { - if (!Directory.Exists(stdLogFolderPath)) - { - PathTools.CreateDirPath(stdLogFolderPath); - } - StdLogFilePath=Path.Combine(stdLogFolderPath, filename + ".txt"); - } - else - { - this.StdLogFilePath = Path.Combine(AppConst.ProgramUserFolderRootPath(), filename + ".txt"); -#if (DEBUG) - Console.WriteLine($"Using standard program log path:\n{this.StdLogFilePath}"); -#endif - } - } - - /// - /// Automated get function name for the function calling this - /// Used for automating getting a function name to write in a log - /// - /// Add extra message to log with function name - /// "[CALLING_FUNCTION_NAME]() -> [inclMsg]" - [MethodImpl(MethodImplOptions.NoInlining)] - public string GetCurrentMethod(string inclMsg = null) - { - var st = new StackTrace(); - var sf = st.GetFrame(1); - - var nameTxt = $"{sf.GetMethod().Name}()"; - if (!String.IsNullOrEmpty(inclMsg)) - { - nameTxt += " -> " + inclMsg; - } - return nameTxt; - } - - /// - /// Automated get function name for the function calling this - /// Used for automating getting a function name to write in a log - /// - /// Add extra message to log with function name - /// "[CALLING_FUNCTION_NAME]() -> [inclMsg]" - [MethodImpl(MethodImplOptions.NoInlining)] - public static string GetCurrentStaticMethod(string inclMsg = null) - { - var st = new StackTrace(); - var sf = st.GetFrame(1); - - var nameTxt = $"{sf.GetMethod().Name}()"; - if (!String.IsNullOrEmpty(inclMsg)) - { - nameTxt += " -> " + inclMsg; - } - return nameTxt; - } - - internal void AddToLogFile(bool isError, string nameSingleLine, string infoMultibleLines) - { - AddToLogFile(isError, StdLogFilePath, nameSingleLine, infoMultibleLines); - } - - internal void AddToLogFile(bool isError, string LogFilePath, string nameSingleLine, string infoMultibleLines) - { - try - { - if (isError) - { - _LogCountErrors++; - try - { - if (OnErrorIsAdded != null) - { - OnErrorIsAdded(null, null); - } - } - catch { } // Don't handle this if something breaks down - } - _LogCountTotal++; - if (System.IO.File.Exists(LogFilePath)) - { - FileInfo f = new FileInfo(LogFilePath); - if (f.Length > (DoTimeStamp ? 1000000 : 10000)) - { - f.Delete(); - } - } - else - { - PathTools.CreateDirPathForFile(LogFilePath); - } - var message = infoMultibleLines; - var length = message.Length; - File.AppendAllText(LogFilePath, "¤¤¤ " + nameSingleLine.Trim() + " \"" + DateTime.Now.ToString() + "\" ¤¤¤ " + Environment.NewLine - + message + Environment.NewLine); -#if (DEBUG) - System.Diagnostics.Debug.WriteLine(DateTime.Now.ToString("HH:mm:ss.fff ") + - " ¤¤¤ " + nameSingleLine.Trim() + " ¤¤¤ " + Environment.NewLine - + message - ); -#endif - } - catch (Exception ex) - { // Ups just to make sure that this don't result in program is closed - string test = ex.ToMessageText(); - } - } - - /// - /// Add a time stamp in program log file.. used to time differend code / controls load time - /// - /// class or window / control obkject to make time stamp for - /// description text - /// Force making log and ignore DoTimeStamp setting - internal void AddToTimestampLog(object processObj, string desc, bool forceLog = false) - { - try - { - if (DoTimeStamp || forceLog) - { - AddToLogFile(false, StdLogFilePath, processObj.ToString() + DateTime.Now.ToString(" [HH:mm:ss fff]"), desc); - } - } - catch { } - } - - internal void AddToProgramLogFile(bool isError, string nameSingleLine, string infoMultibleLines) - { - AddToLogFile(isError, StdLogFilePath, nameSingleLine, infoMultibleLines); - } - - /// - /// Add to program log - /// - /// Exception to log - /// Is an error or just as log - /// what class or where function is located in - /// what function it was in like "myFunc()" and null if not used - /// Additional information also to include like "while doing x and y..." - internal void ToLog(Exception ex, bool isError, string className, string functionName = null, string dataInfo = null) - { - AddToProgramLogFile(isError, LogMessageTool.ToFuncText(isError, className, functionName), - (String.IsNullOrEmpty(dataInfo) ? "" : dataInfo + ((ex != null) ? Environment.NewLine : "")) // Add data info if used - + ((ex != null) ? ex.ToMessageText() : "")); - } - - internal void AddFuncLog(bool isError, string className, string functionName, string dataInfo, Exception ex) - { - ToLog(ex, isError, className, functionName, dataInfo); - } - - internal void AddFuncLog(bool isError, string className, string functionName, Exception ex) - { - AddFuncLog(isError, className, functionName, null, ex); - } - - internal void AddFuncLog(bool isError, string className, string functionName, string dataInfo) - { - AddFuncLog(isError, className, functionName, dataInfo, null); - } - - internal void AddClassInitLog(bool isError, string className, Exception ex) - { - AddToProgramLogFile(isError, LogMessageTool.ToInitText(isError, className), ((ex != null) ? ex.ToMessageText() : "")); - } - - internal void AddClassInitLog(bool isError, string className, string dataInfo, Exception ex) - { - AddToProgramLogFile(isError, LogMessageTool.ToInitText(isError, className, dataInfo), ((ex != null) ? ex.ToMessageText() : "")); - } - - internal void OpenProgramLogFile() - { - OpenLogFile(StdLogFilePath); - } - - - internal void OpenLogFile(string LogFilePath) - { - try - { - Process.Start(LogFilePath); - } - catch (Exception ex) - { - Console.WriteLine("Error Open Log " + Environment.NewLine + ex.ToMessageText()); - } - } - } -} \ No newline at end of file diff --git a/Common/modbus_master_csharp/Base/LogMessageTool.cs b/Common/modbus_master_csharp/Base/LogMessageTool.cs deleted file mode 100644 index 5229e356..00000000 --- a/Common/modbus_master_csharp/Base/LogMessageTool.cs +++ /dev/null @@ -1,102 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace XYLEM.Base -{ - public static class LogMessageTool - { - /// - /// Return Type Name as a string - /// - /// - /// - /// - static public string TypeName(this T obj) - { - Type t; - if (obj == null) - { - t = typeof(T); - } - else - { - t = obj.GetType(); - } - return t.Name; - } - - public static string ToMessageText(this Exception ex) - { - if (ex != null) - { - var message = ""; - var innerMessage = ""; - if (ex.InnerException != null) - { - innerMessage = ToMessageText(ex.InnerException); - } - if ((ex.Message != null) && !String.IsNullOrEmpty(ex.Message)) - { - message = ex.Message; - } - if (!String.IsNullOrEmpty(innerMessage)) - { - message += (!String.IsNullOrEmpty(message) ? Environment.NewLine : "") + innerMessage; - } - return message; - } - return ""; - } - - public static string ToFuncText(bool isError, string className, string functionName) - { - var message = (isError ? "Error in class" : "Class ") + className + " and func. " + functionName; -#if(DEBUG) - System.Diagnostics.Debug.WriteLine(DateTime.Now.ToString("HH:mm:ss.fff ") + message); -#endif - return message; - } - - public static string ToInitText(bool isError, string className) - { - var message = (isError ? "Error in init of " : "Init of ") + className; -#if(DEBUG) - System.Diagnostics.Debug.WriteLine(DateTime.Now.ToString("HH:mm:ss.fff ") + message); -#endif - return message; - } - - public static string ToInitText(bool isError, string className, string dataInfo) - { - return ToInitText(isError, className + " info = " + dataInfo); - } - - public static string ToFuncErrorText(this Exception ex, string className, string functionName) - { - return ToFuncText(true, className, functionName + ((ex == null) ? "" : Environment.NewLine + "Message = " + ex.ToMessageText())); - } - - public static string ToFuncErrorText(this Exception ex, string className, string functionName, string dataInfo) - { - return ToFuncText(true, className, functionName + Environment.NewLine + dataInfo + ((ex == null) ? "" : Environment.NewLine + "Message = " + ex.ToMessageText())); - } - - public static string ToFuncErrorText(string className, string functionName, string dataInfo) - { - return ToFuncText(true, className, functionName + Environment.NewLine + dataInfo); - } - - public static string ToFuncErrorText(string className, string functionName) - { - return ToFuncText(true, className, functionName); - } - - public static string ToInitErrorText(this Exception ex, string className) - { - return ToInitText(true, className + ((ex == null) ? "" : Environment.NewLine + "Message = " + ex.ToMessageText())); - } - } -} diff --git a/Common/modbus_master_csharp/Base/NumberConvert.cs b/Common/modbus_master_csharp/Base/NumberConvert.cs deleted file mode 100644 index 6d8b975d..00000000 --- a/Common/modbus_master_csharp/Base/NumberConvert.cs +++ /dev/null @@ -1,602 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Text.RegularExpressions; -using System.Threading.Tasks; - -namespace XYLEM.Base -{ - public static class NumberConvert - { - public static readonly string ColumnSeperatorCSV = ((2.3).ToString().Contains(".") ? "," : ";"); - private static readonly int base10 = 10; - private static readonly char[] cHexa = new char[] { 'A', 'B', 'C', 'D', 'E', 'F' }; - private static readonly int[] iHexaNumeric = new int[] { 10, 11, 12, 13, 14, 15 }; - private static readonly int[] iHexaIndices = new int[] { 0, 1, 2, 3, 4, 5 }; - private static readonly int asciiDiff = 48; - - public static string PadLeft(this String text, int minLength) - { - int iCount = minLength - text.Length; - String textRet = ""; - if (iCount > 0) - { - textRet = new String('0', iCount); - } - return textRet + text; - } - - /// - /// Tag for values input is done as "decimal" and is placed ex. in front or behinde the value that needs to be converted - /// - public static readonly string ValueInputAsDecimalTag = "decimal"; - - /// - /// Tag for values input is done as "binary" and is placed ex. in front or behinde the value that needs to be converted - /// - public static readonly string ValueInputAsBinaryTag = "binary"; - - - /// - /// Tag for values input is done as "octal" and is placed ex. in front or behinde the value that needs to be converted - /// - public static readonly string ValueInputAsOctalEndTag = "octal"; - - /// - /// Tag for values input is done as "0o" and is placed ex. in front the value that needs to be converted - /// - public static readonly string ValueInputAsOctalFrontTag = "0o"; - - /// - /// Tag for values input is done as "hex" and is placed ex. in front or behinde the value that needs to be converted - /// - public static readonly string ValueInputAsHexEndTag = "hex"; - - /// - /// Tag for values input is done as "0x" and is placed ex. in front the value that needs to be converted - /// - public static readonly string ValueInputAsHexFrontTag = "0x"; - - - /// - /// Tag for values input is done as "bin" and is placed ex. in front or behinde the value that needs to be converted - /// - public static readonly string ValueInputAsBinaryEndTag = "bin"; - - /// - /// Tag for values input is done as "0b" and is placed ex. in front the value that needs to be converted - /// - public static readonly string ValueInputAsBinaryFrontTag = "0b"; - - /// - /// Convert byte values as in a string byte array - /// - /// must be like "0x12, 0x12, 0x12" or "1010bin; 1010bin; 1010bin" or "decimal 123, 123, 123" - /// - public static byte[] ValuesToBytes(this string byteValues) - { - try - { - List ret = new List(); - var isHexadecimal = false; - var isDecimal = false; - var isBinary = false; - byteValues = byteValues.Trim().ToLower(); - bool isBinaryMatch = (new Regex(@"^[01 ]+$")).IsMatch(byteValues); - bool isHexMatch = (new Regex(@"^[0-9a-f ]+$")).IsMatch(byteValues); - if (!string.IsNullOrEmpty(byteValues) && (byteValues.Contains(ValueInputAsHexEndTag) || byteValues.Contains("0x") || (isHexMatch && !isBinaryMatch))) - { - byteValues = byteValues.ToLower().Replace(ValueInputAsHexEndTag, ""); - isHexadecimal = true; - } - else if (!string.IsNullOrEmpty(byteValues) && (byteValues.Contains(ValueInputAsDecimalTag) || isBinaryMatch)) - { - byteValues = byteValues.Replace(ValueInputAsDecimalTag, ""); - isDecimal = true; - } - else if (!string.IsNullOrEmpty(byteValues) && byteValues.Contains(ValueInputAsBinaryTag)) - { - byteValues = byteValues.Replace(ValueInputAsBinaryTag, ""); - isBinary = true; - } - var chunks = byteValues.Trim(new char[] { ' ' }).Split(new string[] { ",", ";", " ", Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries); - foreach (var value in chunks) - { - var toConvert = value.Trim(); - if (!string.IsNullOrEmpty(toConvert)) - { - if (isHexadecimal || !isBinary && !isDecimal && (toConvert.StartsWith(ValueInputAsHexFrontTag) || toConvert.ToLower().Contains(ValueInputAsHexEndTag))) - { - ret.Add(Convert.ToByte(toConvert.Replace(ValueInputAsHexFrontTag, "").ToLower().Replace(ValueInputAsHexEndTag, ""), 16)); - } - else if (isBinary || !isDecimal && (toConvert.StartsWith(ValueInputAsBinaryFrontTag) || toConvert.ToLower().Contains(ValueInputAsBinaryEndTag))) - { - ret.Add(Convert.ToByte(toConvert.Replace(ValueInputAsBinaryFrontTag, "").ToLower().Replace(ValueInputAsBinaryEndTag, ""), 2)); - } - else - { - ret.Add(Convert.ToByte(toConvert, 10)); - } - } - } - return ret.ToArray(); - } - catch - { - return null; // Error - } - } - - public static string UnsignedHexToDecimal(this string Hex) - { - try - { - return Convert.ToUInt64(Hex, 16).ToString(); - } - catch - { - return "?"; - } - } - - public static string UnsignedDecimalToHex(this string iDec, int padLeft, bool Add_0x) - { - try - { - return UnsignedDecimalToHex(Convert.ToUInt64(iDec), padLeft, Add_0x); - } - catch - { - return "?"; - } - } - - public static string UnsignedDecimalToHex(this UInt32 iDec, int padLeft, bool Add_0x) - { - try - { - return ((Add_0x) ? ValueInputAsHexFrontTag : "") + String.Format("{0:X}", iDec).PadLeft(padLeft, '0'); - } - catch - { - return "?"; - } - } - - public static string DecimalToHex(this Int32 iDec, int padLeft, bool Add_0x) - { - try - { - return ((Add_0x) ? ValueInputAsHexFrontTag : "") + String.Format("{0:X}", iDec).PadLeft(padLeft, '0'); - } - catch - { - return "?"; - } - } - - public static string UnsignedDecimalToHex(this UInt64 iDec, int padLeft, bool Add_0x) - { - try - { - return ((Add_0x) ? ValueInputAsHexFrontTag : "") + String.Format("{0:X}", iDec).PadLeft(padLeft, '0'); - } - catch - { - return "?"; - } - } - - public static string DecimalToHex(this Int64 iDec, int padLeft, bool Add_0x) - { - try - { - return ((Add_0x) ? ValueInputAsHexFrontTag : "") + String.Format("{0:X}", iDec).PadLeft(padLeft, '0'); - } - catch - { - return "?"; - } - } - - public static string UnsignedDecimalToBin(this string dec, bool Add_0b) - { - try - { - return ((Add_0b) ? ValueInputAsBinaryFrontTag : "") + UnsignedDecimalToBase(Convert.ToUInt64(dec), 2); - } - catch - { - return "?"; - } - } - - static string DoAutoPadLeft(this string valueAsStr) - { - var numberToPad = (valueAsStr.Length <= 8) ? 8 : (valueAsStr.Length <= 16) ? 16 : (valueAsStr.Length <= 32) ? 32 : 64; - return valueAsStr.PadLeft(numberToPad, '0'); - } - - static string DoAutoSpcaePadEvery4Char(this string ValueAsStr) - { - if (String.IsNullOrEmpty(ValueAsStr)) - { - return ValueAsStr; - } - else - { - ValueAsStr = ValueAsStr.Trim(); - var MissingChar = ValueAsStr.Length % 4; - ValueAsStr = ValueAsStr.PadLeft(ValueAsStr.Length + 4 - MissingChar, '0'); - return Regex.Replace(ValueAsStr.Trim(), @"(.{4})", "$1 ").Trim(); - } - } - - public static string ToUnsignedBin(this int value, bool Add_0b) - { - try - { - return ((Add_0b) ? ValueInputAsBinaryFrontTag : "") + UnsignedDecimalToBase((UInt32)value, 2).DoAutoPadLeft(); - } - catch - { - return "?"; - } - } - - public static string ToUnsignedBin(this uint value, bool Add_0b) - { - try - { - return ((Add_0b) ? ValueInputAsBinaryFrontTag : "") + UnsignedDecimalToBase(value, 2).DoAutoPadLeft(); - } - catch - { - return "?"; - } - } - - public static string ToUnsignedBin(this Int64 value) - { - try - { - return UnsignedDecimalToBase((UInt32)value, 2).DoAutoPadLeft(); - } - catch - { - return "?"; - } - } - - /// - /// To string like 0bxxxxxxx depending of selection - /// - /// what value to show - /// add 0b in front to indicate it is binary - /// "true" if pad left front with all 0 for the actual value type else "false" only pad with up to 4 bits 0 to easy read - /// - public static string ToUnsignedBin(this UInt64 value, bool Add_0b, bool DoAutoPadLeft = true) - { - try - { - var ret = UnsignedDecimalToBase(value, 2); - if (ret.Length == 0) - { - ret += "0"; // Always show 0 if no 1 is set - } - if (DoAutoPadLeft) - { - ret = ret.DoAutoPadLeft(); - } - else - { - ret = ret.DoAutoSpcaePadEvery4Char(); - } - return ((Add_0b) ? ValueInputAsBinaryFrontTag : "") + ret; - } - catch - { - return "?"; - } - } - - public static string ToBin(this byte value, bool Add_0b) - { - try - { - return ((Add_0b) ? ValueInputAsBinaryFrontTag : "") + UnsignedDecimalToBase(value, 2).DoAutoPadLeft(); - } - catch - { - return "?"; - } - } - - public static string ToDec(this byte value) - { - return value.ToString("D3"); - } - - public static string ToHex(this byte value, bool Add_0x) - { - return DecimalToHex(value, 2, Add_0x); - } - - public static string ToHex(this byte[] values, bool Add_0x, bool seperatorForCSV) - { - if (values == null) { return "Array is empty!"; } - string ret = ""; - foreach (var value in values) - { - ret += (!String.IsNullOrEmpty(ret) ? (seperatorForCSV ? ColumnSeperatorCSV + " " : ", ") : "") + value.ToHex(Add_0x); - } - return ret; - } - - public static string ToDec(this byte[] values) - { - if (values == null) { return "Array is empty!"; } - string ret = ""; - foreach (var value in values) - { - ret += (!String.IsNullOrEmpty(ret) ? ", " : "") + value.ToDec(); - } - return ret; - } - - public static string ToBin(this byte[] values, bool Add_0b) - { - if (values == null) { return "Array is empty!"; } - string ret = ""; - foreach (var value in values) - { - ret += (!String.IsNullOrEmpty(ret) ? ", " : "") + value.ToBin(Add_0b); - } - return ret; - } - - - public static string ToHex(this Int16 value, bool Add_0x) - { - return DecimalToHex(value, 4, Add_0x); - } - - public static string ToHex(this UInt16 value, bool Add_0x) - { - return UnsignedDecimalToHex(value, 4, Add_0x); - } - - public static string ToHex(this Int32 value, bool Add_0x) - { - return DecimalToHex(value, 8, Add_0x); - } - - public static string ToHex(this UInt32 value, bool Add_0x) - { - return UnsignedDecimalToHex(value, 8, Add_0x); - } - - public static string ToHex(this Int64 value, bool Add_0x) - { - return DecimalToHex(value, 16, Add_0x); - } - - public static string ToHex(this UInt64 value, bool Add_0x) - { - return UnsignedDecimalToHex(value, 16, Add_0x); - } - - public static string UnsignedDecimalToBase(this string iDec, int numbase) - { - try - { - return UnsignedDecimalToBase(Convert.ToUInt64(iDec), numbase); - } - catch - { - return "?"; - } - } - - public static string UnsignedDecimalToBase(this UInt64 iDec, int numbase) - { - try - { - String strBin = ""; - int[] result = new int[64]; - int MaxBit = 64; - for (; iDec > 0; iDec /= (UInt64)numbase) - { - int rem = (int)(iDec % Convert.ToUInt64(numbase)); - result[--MaxBit] = rem; - } - for (int i = 0; i < result.Length; i++) - { - if ((int)result.GetValue(i) >= base10) - { - strBin += cHexa[(int)result.GetValue(i) % base10]; - } - else - { - strBin += result.GetValue(i); - } - } - strBin = strBin.TrimStart(new char[] { '0' }); - return strBin; - } - catch - { - return "?"; - } - } - - /// - /// Converter string to a value (0x or 0b is not legal) - /// - /// - /// - /// - public static UInt64 UnsignedBaseToDecimal(this String sBase, int numbase) - { - UInt64 result = 0; - UInt64 b; - UInt64 iProduct = 1; - String sHexa = ""; - if (numbase > base10) - { - for (int i = 0; i < cHexa.Length; i++) - { - sHexa += cHexa.GetValue(i).ToString(); - } - } - for (int i = sBase.Length - 1; i >= 0; i--, iProduct *= (UInt64)numbase) - { - String sValue = sBase[i].ToString(); - if (sValue.IndexOfAny(cHexa) >= 0) - { - b = (UInt64)iHexaNumeric[sHexa.IndexOf(sBase[i])]; - } - else - { - b = (UInt64)sBase[i] - (UInt64)asciiDiff; - } - result += (b * iProduct); - } - return result; - } - - /// - /// Converter string to a value use 0x or 0b - /// - /// - /// - /// - public static UInt64 ToUInt64(this String sBase) - { - UInt64 result = 0; - UInt64 b; - UInt64 iProduct = 1; - String sHexa = ""; - int numbase; - sBase = sBase.Trim().ToLower(); - if (sBase.StartsWith(ValueInputAsOctalFrontTag)) - { - numbase = 8; - sBase = sBase.Replace(ValueInputAsOctalFrontTag, ""); - } - else if (sBase.StartsWith(ValueInputAsHexFrontTag)) - { - numbase = 16; - sBase = sBase.Replace(ValueInputAsHexFrontTag, ""); - } - else if (sBase.StartsWith(ValueInputAsBinaryFrontTag)) - { - numbase = 2; - sBase = sBase.Replace(ValueInputAsBinaryFrontTag, ""); - } - else - { - return Convert.ToUInt64(sBase); - } - if (numbase > base10) - { - for (int i = 0; i < cHexa.Length; i++) - { - sHexa += cHexa.GetValue(i).ToString(); - } - } - for (int i = sBase.Length - 1; i >= 0; i--, iProduct *= (UInt64)numbase) - { - String sValue = sBase[i].ToString(); - if (sValue.IndexOfAny(cHexa) >= 0) - { - b = (UInt64)iHexaNumeric[sHexa.IndexOf(sBase[i])]; - } - else - { - b = (UInt64)sBase[i] - (UInt64)asciiDiff; - } - result += (b * iProduct); - } - return result; - } - - /// - /// Converter string to a value use 0x or 0b - /// - /// - /// - /// - public static Int64 ToInt64(this String sBase) - { - Int64 result = 0; - Int64 b; - Int64 iProduct = 1; - String sHexa = ""; - int numbase; - bool isNegative = false; - sBase = sBase.Trim().ToLower(); - if (sBase.StartsWith("-")) - { - sBase = sBase.TrimStart('-'); - isNegative = true; - } - else if (sBase.StartsWith("+")) - { - sBase = sBase.TrimStart('+'); - } - if (sBase.StartsWith(ValueInputAsOctalFrontTag)) - { - numbase = 8; - sBase = sBase.Replace(ValueInputAsOctalFrontTag, ""); - } - else if (sBase.StartsWith(ValueInputAsHexFrontTag)) - { - numbase = 16; - sBase = sBase.Replace(ValueInputAsHexFrontTag, ""); - } - else if (sBase.StartsWith(ValueInputAsBinaryFrontTag)) - { - numbase = 2; - sBase = sBase.Replace(ValueInputAsBinaryFrontTag, ""); - } - else - { - return isNegative ? -Convert.ToInt64(sBase) : Convert.ToInt64(sBase); - } - if (numbase > base10) - { - for (int i = 0; i < cHexa.Length; i++) - { - sHexa += cHexa.GetValue(i).ToString(); - } - } - for (int i = sBase.Length - 1; i >= 0; i--, iProduct *= (Int64)numbase) - { - String sValue = sBase[i].ToString(); - if (sValue.IndexOfAny(cHexa) >= 0) - { - b = (Int64)iHexaNumeric[sHexa.IndexOf(sBase[i])]; - } - else - { - b = (Int64)sBase[i] - (Int64)asciiDiff; - } - result += (b * iProduct); - } - return isNegative ? -result : result; - } - - public static UInt16 SwapByte(this UInt16 value) - { - return (UInt16)(value >> 8 | (value & 0xFF) << 8); - } - - public static UInt64 SwapWord(this UInt64 value) - { - return (UInt64)(value >> 16 | (value & 0xFFFF) << 16); - } - - } -} diff --git a/Common/modbus_master_csharp/Base/StringConvert.cs b/Common/modbus_master_csharp/Base/StringConvert.cs deleted file mode 100644 index 27bc320f..00000000 --- a/Common/modbus_master_csharp/Base/StringConvert.cs +++ /dev/null @@ -1,474 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Text.RegularExpressions; - -namespace XYLEM.Base { - #region ---- SMS Char Convert ---- - - internal class UniCode { - internal UInt32 Value { get; set; } - internal char Ch { - get { return (char)Value; } - set { Value = (UInt32)value; } - } - - internal string InfoTxt { get; set; } - public UniCode(UInt32 value, string infoTxt) { - Value = value; - InfoTxt = infoTxt; - } - - public override string ToString( ) { - return String.Format( "\"{0}\"=0x{1x} ({2})", (char)Value, Value, InfoTxt ); - } - } - - // Look at /DOC/Siemens MC55i GSM-charset.pdf - // And http://www.unicode.org/Public/MAPPINGS/ETSI/GSM0338.TXT - static class StringConvertToSMSClass { - readonly static Dictionary ConvTable = new Dictionary( ){ - //{ new Byte[]{0x00}, new UniCode( 0x0040 , "COMMERCIAL AT") }, TMO NOT IN USE - { new Byte[]{0x40}, new UniCode( 0x0040, "@ Converted to \"INVERTED EXCLAMATION MARK\", Device then TX \"0x00\"") }, - //{ new Byte[]{0x40}, new UniCode( 0x00A1 , "INVERTED EXCLAMATION MARK") }, TMO NOT IN USE - { new Byte[]{0x21,0x2A}, new UniCode( 0x00A1 , "INVERTED EXCLAMATION MARK to \"!*\"") }, - { new Byte[]{0x00}, new UniCode( 0x0000 , "NULL (see note above)") }, - { new Byte[]{0x01}, new UniCode( 0x00A3 , "POUND SIGN") }, - { new Byte[]{0x02}, new UniCode( 0x0024 , "DOLLAR SIGN") }, - { new Byte[]{0x03}, new UniCode( 0x00A5 , "YEN SIGN") }, - { new Byte[]{0x04}, new UniCode( 0x00E8 , "LATIN SMALL LETTER E WITH GRAVE") }, - { new Byte[]{0x05}, new UniCode( 0x00E9 , "LATIN SMALL LETTER E WITH ACUTE") }, - { new Byte[]{0x06}, new UniCode( 0x00F9 , "LATIN SMALL LETTER U WITH GRAVE") }, - { new Byte[]{0x07}, new UniCode( 0x00EC , "LATIN SMALL LETTER I WITH GRAVE") }, - { new Byte[]{0x08}, new UniCode( 0x00F2 , "LATIN SMALL LETTER O WITH GRAVE") }, - { new Byte[]{0x09}, new UniCode( 0x00E7 , "LATIN SMALL LETTER C WITH CEDILLA") }, - { new Byte[]{0x09}, new UniCode( 0x00C7 , "LATIN CAPITAL LETTER C WITH CEDILLA (see note above)") }, - { new Byte[]{0x0A}, new UniCode( 0x000A , "LINE FEED") }, - { new Byte[]{0x0B}, new UniCode( 0x00D8 , "LATIN CAPITAL LETTER O WITH STROKE") }, - { new Byte[]{0x0C}, new UniCode( 0x00F8 , "LATIN SMALL LETTER O WITH STROKE") }, - { new Byte[]{0x0D}, new UniCode( 0x000D , "CARRIAGE RETURN") }, - { new Byte[]{0x0E}, new UniCode( 0x00C5 , "LATIN CAPITAL LETTER A WITH RING ABOVE") }, - { new Byte[]{0x0F}, new UniCode( 0x00E5 , "LATIN SMALL LETTER A WITH RING ABOVE") }, - { new Byte[]{0x10}, new UniCode( 0x0394 , "GREEK CAPITAL LETTER DELTA") }, - { new Byte[]{0x11}, new UniCode( 0x005F , "LOW LINE") }, - { new Byte[]{0x12}, new UniCode( 0x03A6 , "GREEK CAPITAL LETTER PHI") }, - { new Byte[]{0x13}, new UniCode( 0x0393 , "GREEK CAPITAL LETTER GAMMA") }, - { new Byte[]{0x14}, new UniCode( 0x039B , "GREEK CAPITAL LETTER LAMDA") }, - { new Byte[]{0x15}, new UniCode( 0x03A9 , "GREEK CAPITAL LETTER OMEGA") }, - { new Byte[]{0x16}, new UniCode( 0x03A0 , "GREEK CAPITAL LETTER PI") }, - { new Byte[]{0x17}, new UniCode( 0x03A8 , "GREEK CAPITAL LETTER PSI") }, - { new Byte[]{0x18}, new UniCode( 0x03A3 , "GREEK CAPITAL LETTER SIGMA") }, - { new Byte[]{0x19}, new UniCode( 0x0398 , "GREEK CAPITAL LETTER THETA") }, - { new Byte[]{0x1A}, new UniCode( 0x039E , "GREEK CAPITAL LETTER XI") }, - { new Byte[]{0x1B}, new UniCode( 0x00A0 , "ESCAPE TO EXTENSION TABLE (or displayed as NBSP, see note above)") }, - { new Byte[]{0x1B,0x0A}, new UniCode( 0x000C , "FORM FEED") }, - { new Byte[]{0x1B,0x14}, new UniCode( 0x005E , "CIRCUMFLEX ACCENT") }, - { new Byte[]{0x1B,0x28}, new UniCode( 0x007B , "LEFT CURLY BRACKET") }, - { new Byte[]{0x1B,0x29}, new UniCode( 0x007D , "RIGHT CURLY BRACKET") }, - { new Byte[]{0x1B,0x2F}, new UniCode( 0x005C , "REVERSE SOLIDUS") }, - { new Byte[]{0x1B,0x3C}, new UniCode( 0x005B , "LEFT SQUARE BRACKET") }, - { new Byte[]{0x1B,0x3D}, new UniCode( 0x007E , "TILDE") }, - { new Byte[]{0x1B,0x3E}, new UniCode( 0x005D , "RIGHT SQUARE BRACKET") }, - { new Byte[]{0x1B,0x40}, new UniCode( 0x007C , "VERTICAL LINE") }, - { new Byte[]{0x1B,0x65}, new UniCode( 0x20AC , "EURO SIGN") }, - { new Byte[]{0x1C}, new UniCode( 0x00C6 , "LATIN CAPITAL LETTER AE") }, - { new Byte[]{0x1D}, new UniCode( 0x00E6 , "LATIN SMALL LETTER AE") }, - { new Byte[]{0x1E}, new UniCode( 0x00DF , "LATIN SMALL LETTER SHARP S (German)") }, - { new Byte[]{0x1F}, new UniCode( 0x00C9 , "LATIN CAPITAL LETTER E WITH ACUTE") }, - { new Byte[]{0x20}, new UniCode( 0x0020 , "SPACE") }, - { new Byte[]{0x21}, new UniCode( 0x0021 , "EXCLAMATION MARK") }, - { new Byte[]{0x22}, new UniCode( 0x0022 , "QUOTATION MARK") }, - { new Byte[]{0x23}, new UniCode( 0x0023 , "NUMBER SIGN") }, - { new Byte[]{0x24}, new UniCode( 0x00A4 , "CURRENCY SIGN") }, - { new Byte[]{0x25}, new UniCode( 0x0025 , "PERCENT SIGN") }, - { new Byte[]{0x26}, new UniCode( 0x0026 , "AMPERSAND") }, - { new Byte[]{0x27}, new UniCode( 0x0027 , "APOSTROPHE") }, - { new Byte[]{0x28}, new UniCode( 0x0028 , "LEFT PARENTHESIS") }, - { new Byte[]{0x29}, new UniCode( 0x0029 , "RIGHT PARENTHESIS") }, - { new Byte[]{0x2A}, new UniCode( 0x002A , "ASTERISK") }, - { new Byte[]{0x2B}, new UniCode( 0x002B , "PLUS SIGN") }, - { new Byte[]{0x2C}, new UniCode( 0x002C , "COMMA") }, - { new Byte[]{0x2D}, new UniCode( 0x002D , "HYPHEN-MINUS") }, - { new Byte[]{0x2E}, new UniCode( 0x002E , "FULL STOP") }, - { new Byte[]{0x2F}, new UniCode( 0x002F , "SOLIDUS") }, - { new Byte[]{0x30}, new UniCode( 0x0030 , "DIGIT ZERO") }, - { new Byte[]{0x31}, new UniCode( 0x0031 , "DIGIT ONE") }, - { new Byte[]{0x32}, new UniCode( 0x0032 , "DIGIT TWO") }, - { new Byte[]{0x33}, new UniCode( 0x0033 , "DIGIT THREE") }, - { new Byte[]{0x34}, new UniCode( 0x0034 , "DIGIT FOUR") }, - { new Byte[]{0x35}, new UniCode( 0x0035 , "DIGIT FIVE") }, - { new Byte[]{0x36}, new UniCode( 0x0036 , "DIGIT SIX") }, - { new Byte[]{0x37}, new UniCode( 0x0037 , "DIGIT SEVEN") }, - { new Byte[]{0x38}, new UniCode( 0x0038 , "DIGIT EIGHT") }, - { new Byte[]{0x39}, new UniCode( 0x0039 , "DIGIT NINE") }, - { new Byte[]{0x3A}, new UniCode( 0x003A , "COLON") }, - { new Byte[]{0x3B}, new UniCode( 0x003B , "SEMICOLON") }, - { new Byte[]{0x3C}, new UniCode( 0x003C , "LESS-THAN SIGN") }, - { new Byte[]{0x3D}, new UniCode( 0x003D , "EQUALS SIGN") }, - { new Byte[]{0x3E}, new UniCode( 0x003E , "GREATER-THAN SIGN") }, - { new Byte[]{0x3F}, new UniCode( 0x003F , "QUESTION MARK") }, - - { new Byte[]{0x41}, new UniCode( 0x0041 , "LATIN CAPITAL LETTER A") }, - { new Byte[]{0x41}, new UniCode( 0x0391 , "GREEK CAPITAL LETTER ALPHA") }, - { new Byte[]{0x42}, new UniCode( 0x0042 , "LATIN CAPITAL LETTER B") }, - { new Byte[]{0x42}, new UniCode( 0x0392 , "GREEK CAPITAL LETTER BETA") }, - { new Byte[]{0x43}, new UniCode( 0x0043 , "LATIN CAPITAL LETTER C") }, - { new Byte[]{0x44}, new UniCode( 0x0044 , "LATIN CAPITAL LETTER D") }, - { new Byte[]{0x45}, new UniCode( 0x0045 , "LATIN CAPITAL LETTER E") }, - { new Byte[]{0x45}, new UniCode( 0x0395 , "GREEK CAPITAL LETTER EPSILON") }, - { new Byte[]{0x46}, new UniCode( 0x0046 , "LATIN CAPITAL LETTER F") }, - { new Byte[]{0x47}, new UniCode( 0x0047 , "LATIN CAPITAL LETTER G") }, - { new Byte[]{0x48}, new UniCode( 0x0048 , "LATIN CAPITAL LETTER H") }, - { new Byte[]{0x48}, new UniCode( 0x0397 , "GREEK CAPITAL LETTER ETA") }, - { new Byte[]{0x49}, new UniCode( 0x0049 , "LATIN CAPITAL LETTER I") }, - { new Byte[]{0x49}, new UniCode( 0x0399 , "GREEK CAPITAL LETTER IOTA") }, - { new Byte[]{0x4A}, new UniCode( 0x004A , "LATIN CAPITAL LETTER J") }, - { new Byte[]{0x4B}, new UniCode( 0x004B , "LATIN CAPITAL LETTER K") }, - { new Byte[]{0x4B}, new UniCode( 0x039A , "GREEK CAPITAL LETTER KAPPA") }, - { new Byte[]{0x4C}, new UniCode( 0x004C , "LATIN CAPITAL LETTER L") }, - { new Byte[]{0x4D}, new UniCode( 0x004D , "LATIN CAPITAL LETTER M") }, - { new Byte[]{0x4D}, new UniCode( 0x039C , "GREEK CAPITAL LETTER MU") }, - { new Byte[]{0x4E}, new UniCode( 0x004E , "LATIN CAPITAL LETTER N") }, - { new Byte[]{0x4E}, new UniCode( 0x039D , "GREEK CAPITAL LETTER NU") }, - { new Byte[]{0x4F}, new UniCode( 0x004F , "LATIN CAPITAL LETTER O") }, - { new Byte[]{0x4F}, new UniCode( 0x039F , "GREEK CAPITAL LETTER OMICRON") }, - { new Byte[]{0x50}, new UniCode( 0x0050 , "LATIN CAPITAL LETTER P") }, - { new Byte[]{0x50}, new UniCode( 0x03A1 , "GREEK CAPITAL LETTER RHO") }, - { new Byte[]{0x51}, new UniCode( 0x0051 , "LATIN CAPITAL LETTER Q") }, - { new Byte[]{0x52}, new UniCode( 0x0052 , "LATIN CAPITAL LETTER R") }, - { new Byte[]{0x53}, new UniCode( 0x0053 , "LATIN CAPITAL LETTER S") }, - { new Byte[]{0x54}, new UniCode( 0x0054 , "LATIN CAPITAL LETTER T") }, - { new Byte[]{0x54}, new UniCode( 0x03A4 , "GREEK CAPITAL LETTER TAU") }, - { new Byte[]{0x55}, new UniCode( 0x0055 , "LATIN CAPITAL LETTER U") }, - { new Byte[]{0x56}, new UniCode( 0x0056 , "LATIN CAPITAL LETTER V") }, - { new Byte[]{0x57}, new UniCode( 0x0057 , "LATIN CAPITAL LETTER W") }, - { new Byte[]{0x58}, new UniCode( 0x0058 , "LATIN CAPITAL LETTER X") }, - { new Byte[]{0x58}, new UniCode( 0x03A7 , "GREEK CAPITAL LETTER CHI") }, - { new Byte[]{0x59}, new UniCode( 0x0059 , "LATIN CAPITAL LETTER Y") }, - { new Byte[]{0x59}, new UniCode( 0x03A5 , "GREEK CAPITAL LETTER UPSILON") }, - { new Byte[]{0x5A}, new UniCode( 0x005A , "LATIN CAPITAL LETTER Z") }, - { new Byte[]{0x5A}, new UniCode( 0x0396 , "GREEK CAPITAL LETTER ZETA") }, - { new Byte[]{0x5B}, new UniCode( 0x00C4 , "LATIN CAPITAL LETTER A WITH DIAERESIS") }, - { new Byte[]{0x5C}, new UniCode( 0x00D6 , "LATIN CAPITAL LETTER O WITH DIAERESIS") }, - { new Byte[]{0x5D}, new UniCode( 0x00D1 , "LATIN CAPITAL LETTER N WITH TILDE") }, - { new Byte[]{0x5E}, new UniCode( 0x00DC , "LATIN CAPITAL LETTER U WITH DIAERESIS") }, - { new Byte[]{0x5F}, new UniCode( 0x00A7 , "SECTION SIGN") }, - { new Byte[]{0x60}, new UniCode( 0x00BF , "INVERTED QUESTION MARK") }, - { new Byte[]{0x61}, new UniCode( 0x0061 , "LATIN SMALL LETTER A") }, - { new Byte[]{0x62}, new UniCode( 0x0062 , "LATIN SMALL LETTER B") }, - { new Byte[]{0x63}, new UniCode( 0x0063 , "LATIN SMALL LETTER C") }, - { new Byte[]{0x64}, new UniCode( 0x0064 , "LATIN SMALL LETTER D") }, - { new Byte[]{0x65}, new UniCode( 0x0065 , "LATIN SMALL LETTER E") }, - { new Byte[]{0x66}, new UniCode( 0x0066 , "LATIN SMALL LETTER F") }, - { new Byte[]{0x67}, new UniCode( 0x0067 , "LATIN SMALL LETTER G") }, - { new Byte[]{0x68}, new UniCode( 0x0068 , "LATIN SMALL LETTER H") }, - { new Byte[]{0x69}, new UniCode( 0x0069 , "LATIN SMALL LETTER I") }, - { new Byte[]{0x6A}, new UniCode( 0x006A , "LATIN SMALL LETTER J") }, - { new Byte[]{0x6B}, new UniCode( 0x006B , "LATIN SMALL LETTER K") }, - { new Byte[]{0x6C}, new UniCode( 0x006C , "LATIN SMALL LETTER L") }, - { new Byte[]{0x6D}, new UniCode( 0x006D , "LATIN SMALL LETTER M") }, - { new Byte[]{0x6E}, new UniCode( 0x006E , "LATIN SMALL LETTER N") }, - { new Byte[]{0x6F}, new UniCode( 0x006F , "LATIN SMALL LETTER O") }, - { new Byte[]{0x70}, new UniCode( 0x0070 , "LATIN SMALL LETTER P") }, - { new Byte[]{0x71}, new UniCode( 0x0071 , "LATIN SMALL LETTER Q") }, - { new Byte[]{0x72}, new UniCode( 0x0072 , "LATIN SMALL LETTER R") }, - { new Byte[]{0x73}, new UniCode( 0x0073 , "LATIN SMALL LETTER S") }, - { new Byte[]{0x74}, new UniCode( 0x0074 , "LATIN SMALL LETTER T") }, - { new Byte[]{0x75}, new UniCode( 0x0075 , "LATIN SMALL LETTER U") }, - { new Byte[]{0x76}, new UniCode( 0x0076 , "LATIN SMALL LETTER V") }, - { new Byte[]{0x77}, new UniCode( 0x0077 , "LATIN SMALL LETTER W") }, - { new Byte[]{0x78}, new UniCode( 0x0078 , "LATIN SMALL LETTER X") }, - { new Byte[]{0x79}, new UniCode( 0x0079 , "LATIN SMALL LETTER Y") }, - { new Byte[]{0x7A}, new UniCode( 0x007A , "LATIN SMALL LETTER Z") }, - { new Byte[]{0x7B}, new UniCode( 0x00E4 , "LATIN SMALL LETTER A WITH DIAERESIS") }, - { new Byte[]{0x7C}, new UniCode( 0x00F6 , "LATIN SMALL LETTER O WITH DIAERESIS") }, - { new Byte[]{0x7D}, new UniCode( 0x00F1 , "LATIN SMALL LETTER N WITH TILDE") }, - { new Byte[]{0x7E}, new UniCode( 0x00FC , "LATIN SMALL LETTER U WITH DIAERESIS") }, - { new Byte[]{0x7F}, new UniCode( 0x00E0 , "LATIN SMALL LETTER A WITH GRAVE") }, - }; - - readonly static string[][] ConvertCh = new string[][]{ - new string[]{ "\r", ""}, - new string[]{ Environment.NewLine, "\n"}, - }; - - internal static string RemoveIllegalATCh(this string value) { - string ret = value; - foreach (string[] replaceTxt in ConvertCh) { - if (!String.IsNullOrEmpty( replaceTxt[ 0 ] )) { - ret = ret.Replace( replaceTxt[ 0 ], "" ); - } - } - return ret; - } - - internal static string ReplaceIllegalATCh(this string value) { - string ret = value; - foreach (string[] replaceTxt in ConvertCh) { - if (!String.IsNullOrEmpty( replaceTxt[ 0 ] )) { - ret = ret.Replace( replaceTxt[ 0 ], replaceTxt[ 1 ] ); - } - } - return ret; - } - - internal static UniCode[] GetUniCodes(this byte[] data) { - try { - List list = new List( ); - for (int offset = 0; offset < data.Length; offset++) { - bool found = false; - // check if byte sequens is in convert table - foreach (var code in ConvTable) { - if (data[ offset ] == code.Key.First( )) { - if (code.Key.Length == 1) { - list.Add( code.Value ); - found = true; - break; - // Check if it is a combination of to bytes - } else if ((code.Key.Length == 2) && (offset + 1 < data.Length) && (data[ offset + 1 ] == code.Key[ 1 ])) { - offset++; - list.Add( code.Value ); - found = true; - break; - } - } - } - if (!found) { - list.Add( new UniCode( data[ offset ], "(The Same)" ) ); - } - } - return list.ToArray( ); - } catch (Exception ex) { - throw new Exception( "Unable to convert data to " + typeof( UniCode ).ToString( ), ex ); - } - } - - - internal static string ToPcChFromSmsTxt(this byte[] data) { - string txt = ""; - try { - for (int offset = 0; offset < data.Length; offset++) { - bool found = false; - // check if byte sequens is in convert table - foreach (var code in ConvTable) { - if (data[ offset ] == code.Key.First( )) { - if (code.Key.Length == 1) { - txt += code.Value.Ch; - found = true; - break; - // Check if it is a combination of to bytes - } else if ((code.Key.Length == 2) && (offset + 1 < data.Length) && (data[ offset + 1 ] == code.Key[ 1 ])) { - offset++; - txt += code.Value.Ch; - found = true; - break; - } - } - } - if (!found) { - txt += (char)data[ offset ]; - } - } - return txt.Replace( "\n", Environment.NewLine ); - } catch (Exception ex) { - throw new Exception( "Unable to convert char after this \"" + txt + "\"", ex ); - } - } - - internal static byte[] ToByteFromPcChSMS(this String Txt) { - List ret = new List( ); - try { - foreach (var ch in Txt.ReplaceIllegalATCh( )) { - bool found = false; - // check if byte sequens is in convert table - foreach (var code in ConvTable) { - if (ch == code.Value.Ch) { - ret.AddRange( code.Key ); - found = true; - break; - } - } - if (!found) { - ret.AddRange( ASCIIEncoding.Default.GetBytes( ch.ToString( ) ) ); - } - } - return ret.ToArray( ); - } catch (Exception ex) { - throw new Exception( "Unable to convert char after this \"" + (ret.Count > 0 ? ret.ToArray( ).ToPcChFromSmsTxt( ) : "None, Is Empty!!") + "\"", ex ); - } - } - - internal static string ToStringFromPcChSMS(this String Txt) { - string ret = ""; - try { - foreach (var ch in Txt.ReplaceIllegalATCh( )) { - bool found = false; - // check if byte sequens is in convert table - foreach (var code in ConvTable) { - if (ch == code.Value.Ch) { - foreach (var cdByte in code.Key) { - ret += (char)cdByte; - } - found = true; - break; - } - } - if (!found) { - foreach (var cdByte in ASCIIEncoding.Default.GetBytes( ch.ToString( ) )) { - ret += (char)cdByte; - } - } - } - return ret; - } catch (Exception ex) { - throw new Exception( "Unable to convert char after this \"" + ret + "\"", ex ); - } - } - - - #region ---- To String ---- - private static string ToInfoTxt(byte[] data, UniCode unicode) { - return string.Format( "SMS Ch = 0x{0x}{1} \t\t unicode Ch = {2}", - data.First( ), // 0 - ((data.Length > 1) ? string.Format( "0x{0x}", data[ 0 ] ) : ""), //1 - unicode.ToString( ) ); // 2 - } - - public static string GetInfoTable( ) { - string txt = ""; - foreach (var data in ConvTable) { - txt += ToInfoTxt( data.Key, data.Value ) + Environment.NewLine; - } - return txt; - } - #endregion - } - - #endregion - - internal static class StringConvert { - /// - /// Combine string array to a single text - /// - /// items that is null or empty will be removed - /// inset string to join between to items (set to null if none) - /// - internal static string ToSingleString(this string[] array, string joinWithText) { - string ret = ""; - if ((array != null) && (array.Length > 0)) { - ret = array.Select( item => (String.IsNullOrEmpty( item ) ? "" : item) ).Aggregate( (current, next) => current + ((joinWithText != null) ? joinWithText : "") + next ); - } - return ret; - } - - - /// - /// Combine string array to a single text - /// - /// items that is null or empty will be removed - /// new line or a single space is use to join between to item - /// - internal static string ToSingleString(this string[] array, bool addNewLine) { - return ToSingleString( array, (addNewLine ? Environment.NewLine : " ") ); - } - - - internal static string ToSingleString(this List list, bool addNewLine) { - return ToSingleString( list.ToArray( ), addNewLine ); - } - - internal static string ToSingleString(this IEnumerable list, bool addNewLine) { - return ToSingleString( list.ToArray( ), addNewLine ); - } - - internal static byte[] ToBytes(this string value) { - return ASCIIEncoding.Default.GetBytes( value ); - } - - /// - /// Convert PC string to at AT OK string (No new line) - /// - /// - /// - internal static byte[] ToBytesOkATCh(this string value) { - return ASCIIEncoding.Default.GetBytes( value.RemoveIllegalATCh( ) ); - } - - internal static string ToPcCh(this byte[] value) { - return ASCIIEncoding.Default.GetString( value ); - } - - /// - /// Convert and remove all none printable char - /// - /// - /// - internal static string ToPcPrintableCh(this byte[] value) { - return ToPcPrintableCh( value as IEnumerable ); - } - - /// - /// Convert and remove all none printable char - /// - /// - /// - internal static string ToPcPrintableCh(this IEnumerable value) { - string ret = ""; - if (value != null) { - foreach (var b in value) { - if (b >= 32) { - ret += ASCIIEncoding.Default.GetString( new byte[] { b } ); - } else if (b == '\r') { - ret += Environment.NewLine; - } - } - } - return ret; - } - - internal static string ToPcChFromATCh(this byte[] value) { - return ASCIIEncoding.Default.GetString( value ).Replace( "\r", "" ).Replace( "\n", Environment.NewLine ); - } - - /// - /// Get all digit sequence in a string and return it as strings - /// - /// - /// - internal static string[] GetDecValues(this string values) { - //http://regexhero.net/tester/ - //COM8.45 78.463 -676,67 6.454.54.45,4545,4545 (Selecting only the values) - // - MatchCollection matches = Regex.Matches( values, @"[-]|[.]|[-.]|[0-9][0-9]*[.]|[,]*[0-9]+" ); // ([0,1]|([0,1]?\.[0-9]+)) or "^[0-9]*[.]?[0-9]+$" - if (matches.Count != 0) { - List ret = new List( ); - foreach (Match value in matches) { - ret.Add( value.Value ); - } - return ret.ToArray( ); - } - return null; - } - - /// - /// Get first digit sequence in a string and return it as strings - /// - /// - /// - internal static string GetDecValue(this string value) { - string[] values = GetDecValues( value ); - if (values != null) { - return values.First( ); - } - return null; - } - - internal static string RemoveZeroTerm(this string value) { - if (!string.IsNullOrEmpty( value ) && value.Contains( '\0' )) { - return value.Remove( value.IndexOf( '\0' ) ); - } - return value; - } - } -} diff --git a/Common/modbus_master_csharp/Base/XML/XMLStringClass.cs b/Common/modbus_master_csharp/Base/XML/XMLStringClass.cs deleted file mode 100644 index 69fb1e0a..00000000 --- a/Common/modbus_master_csharp/Base/XML/XMLStringClass.cs +++ /dev/null @@ -1,127 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Text.RegularExpressions; -using System.Threading.Tasks; - -namespace XYLEM.Base.XML -{ - public static class XMLStringClass - { - public enum XMLVersion - { - V1_0, - V1_1 - } - - /// - /// Strips non-printable ascii characters - /// Refer to http://www.w3.org/TR/xml11/#charsets for XML 1.1 - /// - /// - /// - public static string StripIllegalXMLChars(this string txtToClean) - { - return StripIllegalXMLChars(txtToClean, XMLVersion.V1_1); - } - - /// - /// Strips non-printable ascii characters - /// Refer to http://www.w3.org/TR/xml11/#charsets for XML 1.1 - /// Refer to http://www.w3.org/TR/2006/REC-xml-20060816/#charsets for XML 1.0 - /// - /// - /// - /// - public static string StripIllegalXMLChars(this string txtToClean, XMLVersion version) - { - try - { - string pattern = String.Empty; - txtToClean = txtToClean. - Replace("&", "&"). - Replace("<", "<"). - Replace(">", ">"). - Replace("\"", """). - Replace("'", "'"). - Replace("\r", " "). - Replace("\n", " "); - switch (version) - { - case XMLVersion.V1_0: - pattern = @"#x((10?|[2-F])FFF[EF]|FDD[0-9A-F]|7F|8[0-46-9A-F]9[0-9A-F])"; - break; - case XMLVersion.V1_1: - pattern = @"#x((10?|[2-F])FFF[EF]|FDD[0-9A-F]|[19][0-9A-F]|7F|8[0-46-9A-F]|0?[1-8BCEF])"; - break; - } - - Regex regex = new Regex(pattern, RegexOptions.IgnoreCase); - if (regex.IsMatch(txtToClean)) - { - txtToClean = regex.Replace(txtToClean, String.Empty); - } - if (txtToClean.Contains('\0')) - { - txtToClean = txtToClean.Remove(txtToClean.IndexOf('\0')); - } - if (txtToClean.Contains((char)0x1E)) - { - txtToClean = txtToClean.Replace((char)0x1E, ' '); - } - { - // Replace any character that has survived this done before - // Replace illegal character in XML documents with blank - // See here for reference http://www.w3.org/TR/xml/#charsets - txtToClean = Regex.Replace(txtToClean, "[\x00-\x08\x0B\x0C\x0E-\x1F]", String.Empty, RegexOptions.Compiled); - } - return txtToClean; - } - catch (Exception ex) - { - throw new Exception("Error in XMLFileIOClass and StripIllegalXMLChars() " + (txtToClean != null ? " txtToClean = " + txtToClean.ToString() : "txtToClean is null"), ex); - } - } - - /// - /// reinset non-printable ascii characters - /// Refer to http://www.w3.org/TR/xml11/#charsets for XML 1.1 - /// - /// - /// - public static string ReinsetIllegalXMLChars(this string txtToClean) - { - return ReinsetIllegalXMLChars(txtToClean, XMLVersion.V1_1); - } - - /// - /// reinset non-printable ascii characters - /// Refer to http://www.w3.org/TR/xml11/#charsets for XML 1.1 - /// Refer to http://www.w3.org/TR/2006/REC-xml-20060816/#charsets for XML 1.0 - /// - /// - /// - /// - public static string ReinsetIllegalXMLChars(string txtToReinset, XMLVersion version) - { - try - { - string pattern = String.Empty; - txtToReinset = txtToReinset. - Replace("&", "&"). - Replace("<", "<"). - Replace(">", ">"). - Replace(""", "\""). - Replace("'", "'"). - Replace(" ", "\r"). - Replace(" ", "\n"); - return txtToReinset; - } - catch (Exception ex) - { - throw new Exception("Error in XMLFileIOClass and StripIllegalXMLChars() " + (txtToReinset != null ? " txtToClean = " + txtToReinset.ToString() : "txtToClean is null"), ex); - } - } - } -} diff --git a/Common/modbus_master_csharp/Base/XML/XMLWriteFuncClass.cs b/Common/modbus_master_csharp/Base/XML/XMLWriteFuncClass.cs deleted file mode 100644 index 4f7d60be..00000000 --- a/Common/modbus_master_csharp/Base/XML/XMLWriteFuncClass.cs +++ /dev/null @@ -1,76 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Xml.Serialization; -using System.Xml; -using System.Net.Sockets; -using XYLEM.Base.Files; - -namespace XYLEM.Base.XML -{ - internal class XMLWriteFuncClass - { - /// - /// Writes the given object instance to an XML file. - /// Only Public properties and variables will be written to the file. These can be any type though, even other classes. - /// If there are public properties/variables that you do not want written to the file, decorate them with the [XmlIgnore] attribute. - /// Object type must have a parameterless constructor. - /// - /// The type of object being written to the file. - /// The file path to write the object instance to. - /// The object instance to write to the file. - /// If false the file will be overwritten if it already exists. If true the contents will be appended to the file. - public static void WriteToXmlFile(string filePath, T objectToWrite, bool append = false) where T : new() - { - // from https://stackoverflow.com/questions/6115721/how-to-save-restore-serializable-object-to-from-file - TextWriter writer = null; - try - { - writer = new StreamWriter(filePath, append); - XmlSerializer xmlSerializer = new XmlSerializer(typeof(T)); - xmlSerializer.Serialize(writer, objectToWrite); - } - catch (Exception ex) - { - throw new Exception(ex.ToFuncErrorText(typeof(XMLWriteFuncClass).ToString(), ProgramLogCSVFileClass.GetCurrentStaticMethod())); - } - finally - { - if (writer != null) - writer.Close(); - } - } - - /// - /// Reads an object instance from an XML file. - /// Object type must have a parameterless constructor. - /// - /// The type of object to read from the file. - /// The file path to read the object instance from. - /// Returns a new instance of the object read from the XML file. - public static T ReadFromXmlFile(string filePath) where T : new() - { - // from https://stackoverflow.com/questions/6115721/how-to-save-restore-serializable-object-to-from-file - - TextReader reader = null; - try - { - XmlSerializer xmlSerializer = new XmlSerializer(typeof(T)); - reader = new StreamReader(filePath); - return (T)xmlSerializer.Deserialize(reader); - } - catch (Exception ex) - { - throw new Exception(ex.ToFuncErrorText(typeof(XMLWriteFuncClass).ToString(), ProgramLogCSVFileClass.GetCurrentStaticMethod())); - } - finally - { - if (reader != null) - reader.Close(); - } - } - } -} diff --git a/Common/modbus_master_csharp/Communication/DeviceExceptionData.cs b/Common/modbus_master_csharp/Communication/DeviceExceptionData.cs deleted file mode 100644 index e8895bc2..00000000 --- a/Common/modbus_master_csharp/Communication/DeviceExceptionData.cs +++ /dev/null @@ -1,67 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace XYLEM.Communication -{ - /// - /// Class to contain data for a exception - /// - public struct DeviceExceptionData - { - /// - /// Special function has to be done when this com - /// https://mjksvn.world.fluidtechnology.net:4443/svn/development/- MJK Standards/- Communication protocols/Modbus/Modbus Specifications.doc - /// "2.2.1 MODBUS Exception Responses:" - /// - public enum SpecialType - { - DriverError = -1, - WaitForReset = 14, // Wait x sec befor next tx - WaitForFinishMax5s = 252, // Wait Exception OK (max 5s) - OnlyAnInforead = 253, // Used internal to only make an info read about data to read / write (Not intentet for normal TX) - WaitForFinishMax60s = 254, // Wait Exception OK (max 60s) - WaitForFinishMax5Min = 255, // Wait Exception OK (max 5min) - } - - /// - /// Exception No. - /// - private int _ExcNo; - - /// - /// Exception Info - /// - private string _Info; - /// - /// Preload All data.. used when init data array - /// - /// - /// - public DeviceExceptionData(int excNo, string info) - { - this._ExcNo = excNo; - this._Info = info; - } - - /// - /// Exception code number - /// - public int ExcNo - { - get { return _ExcNo; } - set { _ExcNo = value; } - } - - /// - /// Info text to exception no. - /// - public String Info - { - get { return _Info; } - set { _Info = value; } - } - } -} diff --git a/Common/modbus_master_csharp/Communication/FuncTypes.cs b/Common/modbus_master_csharp/Communication/FuncTypes.cs deleted file mode 100644 index 00e940f5..00000000 --- a/Common/modbus_master_csharp/Communication/FuncTypes.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace XYLEM.Communication -{ - public enum Func_RD : int - { - NoSupport = -1, - No3 = 3, - No4 = 4, - }; - - public enum Func_WR : int - { - NoSupport = -1, - No15 = 15, - No16 = 16, - }; -} diff --git a/Common/modbus_master_csharp/Communication/ModbusCom.cs b/Common/modbus_master_csharp/Communication/ModbusCom.cs deleted file mode 100644 index e33716aa..00000000 --- a/Common/modbus_master_csharp/Communication/ModbusCom.cs +++ /dev/null @@ -1,634 +0,0 @@ -using MagFlux6200_metrology_reg_list; -using XYLEM.Device; -using XYLEM.Communication.ValueEncoders; -using System; -using System.Collections.Generic; -using System.IO.Ports; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Numerics; -using System.Xml.Linq; -using static System.Net.Mime.MediaTypeNames; -using XYLEM.Base; -using System.Text.RegularExpressions; -using System.Data; -using System.Security.Policy; -using System.Threading; - -namespace XYLEM.Communication -{ - /// - /// Modbus read and write return data type - /// - /// - public class ModbusDataResult where T : IComparable, IFormattable, IComparable, IEquatable/*, IConvertible is not compatible with BigInteger*/ - { - /// - /// Has the task succeded - /// - internal bool IsOkay; - - /// - /// Data readed from or written to device - /// - internal T Data; - - /// - /// What device value info for Data - /// - internal Device_value Info; - - public ModbusDataResult(bool IsOkay, T DataRW, Device_value Info) - { - this.IsOkay = IsOkay; - this.Data = DataRW; - this.Info = Info; - } - } - - public class ModbusDataRequest - { - /// - /// Has the task succeded - /// - internal bool IsOkay; - - /// - /// Data for return read in from device - /// - internal object Data; - - /// - /// What device value info for Data - /// - internal Device_value Info; - - public ModbusDataRequest(bool IsOkay, object returnReadIn, Device_value Info) - { - this.IsOkay = IsOkay; - this.Data = returnReadIn; - this.Info = Info; - } - } - - - public class ModbusCom - { - const int ch_lng_dv = -15; - const int ch_lng_Version = -3; - const int ch_lng_MD_adr = -7; - const int dig_value = 10; - - /// - /// Min delay before a DUT message is expected faild - // Is 1s for testing out that default modbus settings com don't fail - /// - private readonly int _MaxWaitForComRX_ms = 1000; - - /// - /// Message ended detection delay - /// Min delay before bytes count received and haven't changed result in modbus message ended - /// The 50ms is found to be needed when using DUT RS485 and modbus messages is transparent to Metrology - /// - private readonly int _DelayBetweenRxByteCheck_ms = 50; - SerialPort _serial; - int _retry; - - - public int MsgErrorCount { get; private set; } - public int MsgCount { get; private set; } - - public void ResetMsgCounters() - { - MsgCount = MsgErrorCount = 0; - } - - public ModbusCom(string serial_port, int baudRate, Parity parity, int retry = 3) - { - ResetMsgCounters(); - this._serial = new SerialPort(); - _serial.PortName = serial_port; - _serial.BaudRate = baudRate; - _serial.Parity= parity; - _serial.DtrEnable = _serial.RtsEnable = true; // Need to be on for Application USB Serial com to detect a connection - //_serial.Handshake = Handshake.RequestToSendXOnXOff; - _serial.ReadTimeout = 100; - if (!_serial.IsOpen) - { - _serial.Open(); - } - _retry=retry; - Console.WriteLine($"Serial connection {_serial.PortName}, {_serial.BaudRate}, {_serial.Parity}, retry = {_retry}"); - } - - - /// - /// Is communication port connected - /// - /// - public bool IsConnected() - { - return _serial.IsOpen; - } - - /// - /// Generic value Read of a modbus value ex. "var ret = await Read(_serial, _device_id, req, new Single());" - /// - /// UInt16, Single ... - /// device ID to use for read - /// device value to read - /// a new value of the type to read, to use for init and return - /// as ModbusDataResult where Item1 is true if okay and Item2 is the value read - public ModbusDataResult Read(int device_id, Device_value dv, T retValue) where T : IComparable, IFormattable, IComparable, IEquatable/*, IConvertible is not compatible with BigInteger*/ - { - ModbusDataResult ret = null; - try - { - byte[] answer = Read_bytes(device_id, dv.MD_adr, dv.Datatype.Length()); - retValue = (T)DeviceValueEncoder.Decode(answer, dv.Datatype); - ret = new ModbusDataResult(true, retValue, dv); // Okay - } - catch (Exception ex) - { - var message = $"Read error! from ID={device_id} name={dv.Name} adr={dv.MD_adr} with {dv.Datatype.Length()}\n\r" + ex.Message + "\n\r"; - Console.WriteLine(message); - AppConst.Logger.AddFuncLog(true, this.ToString(), "Read()", message); - ret = new ModbusDataResult(false, retValue, dv); // Error - } - return ret; - } - - /// - /// Read multiple value optimized in one or more telegrams from device - /// - /// Device ID to use for read - /// A list of values to read - /// List with read okay or failed values - public List Read(int device_id, List dvList) - { - List ret = null; - try - { - // ----------- Generate blocks to read ----------- - dvList.Sort((dvA, dvB) => - { - return dvA.MD_adr.CompareTo(dvB.MD_adr); - }); - List> blocks = new List>(); - foreach (var dv in dvList) - { - // if first item, just add it - if ((blocks.Count == 0) || (blocks.Last().Count == 0)) - { - if (blocks.Count == 0) - { - blocks.Add(new List()); - } - blocks.Last().Add(new ModbusDataRequest(false, null, dv)); - } - else - { - var dvLast = blocks.Last().Last().Info; - // Only add it if a new value not already to be read - if (dvLast.Name != dv.Name) - { - var nextAdr = dvLast.MD_adr + (dvLast.Datatype.Length()/2); - // If not next to read in current block - if (dv.MD_adr != nextAdr) - { - // then add it to the next read block - blocks.Add(new List()); - } - blocks.Last().Add(new ModbusDataRequest(false, null, dv)); - } - } - } - // ----------- Read blocks ----------- - foreach (var dv_group in blocks) - { - try - { - var dvi_first = dv_group.First().Info; - var dvi_last = dv_group.Last().Info; - var startAdr = dvi_first.MD_adr; - var LenBytes = (dvi_last.MD_adr+(dvi_last.Datatype.Length()/2)-startAdr)*2; - if ((LenBytes < 0) || (LenBytes > 250)) - { - throw new Exception($"Block length limit Error is {LenBytes}"); - } - byte[] answer = Read_bytes(device_id, startAdr, LenBytes); - if (answer == null) - { - throw new Exception($"Read block error! from ID={device_id} from {dvi_first} to {dvi_last}"); - } - foreach (var dv_data in dv_group) - { - var dv = dv_data.Info; - var start_idx = (dv.MD_adr-startAdr)*2; - var value_bytes = answer.Copy(start_idx, dv.Datatype.Length()); - var value = DeviceValueEncoder.Decode(value_bytes, dv.Datatype); - if (ret == null) - { - ret = new List(); - } - ret.Add(new ModbusDataRequest(true, value, dv)); // Okay - } - } - //catch (Exception ex) - catch - { - // Retry read it as single values - foreach (var dv_data in dv_group) - { - var dv = dv_data.Info; - byte[] answer = Read_bytes(device_id, dv.MD_adr, dv.Datatype.Length()); - var value = DeviceValueEncoder.Decode(answer, dv.Datatype); - if (ret == null) - { - ret = new List(); - } - ret.Add(new ModbusDataRequest(true, value, dv)); // Okay - } - } - } - return ret; - } - catch (Exception ex) - { - if ((dvList != null) && (dvList.Count() > 0)) - { - foreach (var dv in dvList) - { - var message = $"Read error! from ID={device_id} name={dv.Name} adr={dv.MD_adr}\n\r" + ex.Message + "\n\r"; - Console.WriteLine(message); - AppConst.Logger.AddFuncLog(true, this.ToString(), "Read()", message); - } - } - else - { - var message = $"Read error! from ID={device_id}\n\r" + ex.Message + "\n\r"; - Console.WriteLine(message); - AppConst.Logger.AddFuncLog(true, this.ToString(), "Read()", message); - } - ret = null; // Error - } - return ret; - } - - - /// - /// Generic value Write of a modbus value ex. "await Write(_serial, _device_id, req, value)" - /// - /// UInt16, Single .. - /// device ID to use for write - /// device value to write - /// the value to write - /// - public bool Write(int device_id, Device_value dv, T value) where T : IComparable, IFormattable, IConvertible, IComparable, IEquatable - { - bool isOkay = true; - try - { - byte[] tx_data = DeviceValueEncoder.Encode(value, dv.Datatype); - if (!Write_bytes(device_id, dv.MD_adr, tx_data)) - { - var message = $"Write failed={dv,ch_lng_dv}: Reg_Ver={dv.Version,ch_lng_Version} Adr={dv.MD_adr,-7} value={value,dig_value}"; - Console.WriteLine(message); - AppConst.Logger.AddFuncLog(true, this.ToString(), "Write()", message); - isOkay = false; - } - } - catch (Exception ex) - { - var message = $"Write error! to ID={device_id} name={dv.Name} adr={dv.MD_adr} with {dv.Datatype.Length()}\n\r" + ex.Message + "\n\r"; - Console.WriteLine(message); - AppConst.Logger.AddFuncLog(true, this.ToString(), "Write()", message); - isOkay = false; - } - return isOkay; - } - - /// - /// Write multiple value optimized in one or more telegrams to device - /// - /// device ID to use for write - /// a list of values to write - /// A list of values written okay or failed to device - public List Write(int device_id, List reqList) - { - List ret = null; - try - { - // ----------- Generate blocks to write ----------- - reqList.Sort((dvA, dvB) => - { - return dvA.Info.MD_adr.CompareTo(dvB.Info.MD_adr); - }); - List> blocks = new List>(); - foreach (var req in reqList) - { - // if first item, just add it - if ((blocks.Count == 0) || (blocks.Last().Count == 0)) - { - if (blocks.Count == 0) - { - blocks.Add(new List()); - } - blocks.Last().Add(req); - } - else - { - var req_Last = blocks.Last().Last(); - // Only add it if a new value not already written - if (req_Last.Info.Name != req.Info.Name) - { - var nextAdr = req_Last.Info.MD_adr + (req_Last.Info.Datatype.Length()/2); - // If not next to read in current block - if (req.Info.MD_adr != nextAdr) - { - // then add it to the next read block - blocks.Add(new List()); - } - blocks.Last().Add(req); - } - else - { - throw new Exception($"Only a single Write object to {req_Last.Info.Name} is allowed!"); - } - } - } - // ----------- Write blocks ----------- - foreach (var req_group in blocks) - { - try - { - var dvi_first = req_group.First().Info; - var dvi_last = req_group.Last().Info; - var startAdr = dvi_first.MD_adr; - var LenBytes = (dvi_last.MD_adr+(dvi_last.Datatype.Length()/2)-startAdr)*2; - if ((LenBytes < 0) || (LenBytes > 250)) - { - throw new Exception($"Block length limit Error is {LenBytes}"); - } - var tx_group_data = new List(); - foreach (var wr in req_group) - { - byte[] tx_data = DeviceValueEncoder.Encode(wr.Data, wr.Info.Datatype); - if (tx_data == null) - { - throw new Exception($"Fail getting write byte data for {wr.Info.Name}"); - } - tx_group_data.AddRange(tx_data); - } - if (LenBytes != tx_group_data.Count) - { - throw new Exception($"Group write from {dvi_first.Name} to {dvi_last.Name} expected={LenBytes} bytes, encoded was={tx_group_data.Count} bytes"); - } - var isOkay = Write_bytes(device_id, startAdr, tx_group_data.ToArray()); - if (!isOkay) - { - throw new Exception($"Write block error! from ID={device_id} from {dvi_first} to {dvi_last}"); - } - ret = new List(); - foreach (var req in req_group) - { - req.IsOkay = true; - ret.Add(req); // add request that succeed - } - } - //catch (Exception ex) //For debugging - catch - { - ret = new List(); - // Retry write as single values - foreach (var req in req_group) - { - var dv = req.Info; - byte[] tx_data = DeviceValueEncoder.Encode(req.Data, req.Info.Datatype); - req.IsOkay = Write_bytes(device_id, dv.MD_adr, tx_data); - ret.Add(req); // - } - } - } - return ret; - } - catch (Exception ex) - { - if ((reqList != null) && (reqList.Count() > 0)) - { - foreach (var dv in reqList) - { - var message = $"Write error! from ID={device_id} name={dv.Info.Name} adr={dv.Info.MD_adr}\n\r" + ex.Message + "\n\r"; - Console.WriteLine(message); - AppConst.Logger.AddFuncLog(true, this.ToString(), "Write()", message); - } - } - else - { - var message = $"Write error! from ID={device_id}\n\r" + ex.Message + "\n\r"; - Console.WriteLine(message); - AppConst.Logger.AddFuncLog(true, this.ToString(), "Write()", message); - } - ret = null; // Error - } - return ret; - } - - /// - /// A general modbus read function for just a bunch of bytes - /// - /// device ID to use for modbus command - /// Start address to data accessed - /// Number of bytes to request - /// byte data read from device and is null if none / error - public byte[] Read_bytes(int device_id, int md_adr, int num_of_bytes) - { - var is_write = false; - try - { - // Read address 0 device - var p_transate = new ProtocolTranslateModbusRTUClass(); - byte[] tx_data = p_transate.CreateTxData(device_id, (int)Func_RD.No3, md_adr, num_of_bytes, null, is_write); - return modbus_com(md_adr, tx_data, is_write, p_transate); - } - catch (Exception ex) - { - var message = $"!error No data received from ID={device_id} adr={md_adr} with {num_of_bytes} to {(is_write ? "Write" : "Read")}\n\r" + ex.Message + "\n\r"; - Console.WriteLine(message); - AppConst.Logger.AddFuncLog(true, this.ToString(), "Read_bytes()", message); - return null; - } - } - - /// - /// A general modbus write function for just a bunch of bytes - /// - /// device ID to use for modbus command - /// Start address to data accessed - /// Byte data to Write - /// Is true on write is okay - public bool Write_bytes(int device_id, int md_adr, byte[] write_data) - { - var is_write = true; - try - { - // Read address 0 device - var p_transate = new ProtocolTranslateModbusRTUClass(); - byte[] tx_data = p_transate.CreateTxData(device_id, (int)Func_WR.No16, md_adr, write_data.Length, write_data, is_write); - var result = modbus_com(md_adr, tx_data, is_write, p_transate); - if (result != null) - { - return true; // Okay - } - } - catch (Exception ex) - { - var message = $"!error writting data to ID={device_id} adr={md_adr} to {(is_write ? "Write" : "Read")}\n\r" + ex.Message + "\n\r"; - Console.WriteLine(message); - AppConst.Logger.AddFuncLog(true, this.ToString(), "Write_bytes()", message); - } - return false; // Error - } - - /// - /// A general modbus read / write function for just a bunch of bytes - /// - /// Start address to data accessed - /// a complet modbus telegram byte data to transmit - /// is the modbus telegram a write or read type to use for eval received result - /// Modbus protocol translater for eval received result - /// will return null on error or received message on okay - public byte[] modbus_com(int md_adr, byte[] tx_data, bool is_write, ProtocolTranslateModbusRTUClass p_transate) - { - lock (_serial) - { - try - { - byte[] answer = null; - var retry = 0; - do - { - MsgCount++; - if (!_serial.IsOpen) - { - _serial.Open(); - } - _serial.DiscardInBuffer(); - _serial.Write(tx_data, 0, tx_data.Length); - - int received = 0; - int max_count = _MaxWaitForComRX_ms/_DelayBetweenRxByteCheck_ms; - do - { - received = _serial.BytesToRead; - Thread.Sleep(_DelayBetweenRxByteCheck_ms); - } while (((received == 0) || (received != _serial.BytesToRead)) && (--max_count>0)); - if (_serial.BytesToRead <= 0) - { - MsgErrorCount++; - } - } while ((_serial.BytesToRead <= 0) && (++retry < _retry)); - if (_serial.BytesToRead <= 0) - { - throw new Exception("!error No data received from device"); - } -#if (DEBUG) - // TODO just testing - if (retry > 1) - { - Console.WriteLine($"Warning modbus com {retry-1} retry needed for ID={md_adr} to {(is_write ? "Write" : "Read")}"); - } -#endif //#if (DEBUG) - var bytesToRead = _serial.BytesToRead; - var rx_data = new byte[bytesToRead]; - var rx_data_len = _serial.Read(rx_data, 0, bytesToRead); - bool isRxRW = p_transate.IsDataRW(rx_data, true); - // Check data func is Read or write - if (isRxRW) - { - // Read - if (!is_write) - { - if (p_transate.IsRead(rx_data, true)) - { - answer = p_transate.GetReadData(rx_data, true); - //buf.CallBackOK(bDataTemp, txData, CallBackOK_Single); - } - else - { - throw new Exception("Data Rx Wrong"); - } - } - else // Write - { - bool isWrite = p_transate.IsWrite(rx_data, true); - int adr = p_transate.GetRxAdr(rx_data, true); - if (isWrite && (!p_transate.IsAdrPossible(rx_data, true) ? true : (adr == md_adr))) - { - //buf.CallBackOK(null, txData, CallBackOK_Single); - return rx_data; // Is okay return received message - } - else - { - throw new Exception("Data Rx Wrong"); - } - } - - } - else // Exception received - { - // Normal - if (!p_transate.IsRxWaitForCommand(rx_data, true)) - { - int exception = p_transate.GetRxAcknowledgeCode(rx_data, true); - //buf.CallBackError(exception, txData, ""); - // If read .. try again after wait - } - else - { - if (!is_write) - { - //buf.Add(txData); - // if Write.. then all must be okay and put event on delay - } - else - { - //buf.CallBackOK(null, txData, CallBackOK_Single); - } - if (p_transate.IsRxWaitForFinishMax60S(rx_data, true)) - { - Thread.Sleep((int)(60 * 1000)); - } - else if (p_transate.IsRxWaitForFinishMax5Min(rx_data, true)) - { - Thread.Sleep((int)(5 * 60 * 1000)); - } - else if (p_transate.IsRxWaitForFinishMax5S(rx_data, true)) - { - Thread.Sleep((int)(5 * 1000)); - } - else - { - int waitTime = 5 * 1000; // default - Thread.Sleep(waitTime); // Wait for reset - } - } - } - return answer; - } - catch (Exception ex) - { - var message = "Error: \n\r" + ex.Message + "\n\r"; - Console.WriteLine(message); - AppConst.Logger.AddFuncLog(true, this.ToString(), "modbus_com()", message); - return null; - } - finally - { - _serial.Close(); - } - } - } - } -} diff --git a/Common/modbus_master_csharp/Communication/ProtocolTranslateModbusRTUClass.cs b/Common/modbus_master_csharp/Communication/ProtocolTranslateModbusRTUClass.cs deleted file mode 100644 index 8f6f4ed2..00000000 --- a/Common/modbus_master_csharp/Communication/ProtocolTranslateModbusRTUClass.cs +++ /dev/null @@ -1,572 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Net.Sockets; -using XYLEM.Base; - -namespace XYLEM.Communication -{ - public class ProtocolTranslateModbusRTUClass - { - public enum FunctionNumber : byte - { - NoSupport = 0, - Read_No3 = 3, - Read_No4 = 4, - Write_No15 = 15, - Write_No16 = 16, - - }; - - /// - /// Modbus Byte Offset for write data telegram - /// - private enum BaseOffset - { - ID = 0, - FuncCode - } - - /// - /// Modbus Byte Offset for write data telegram - /// - private enum TxOffset_W - { - AdrHigh = (int)BaseOffset.FuncCode + 1, - AdrLow, - RegCount_High, - RegCount_Low, - ByteCount, - Data_Start - }; - - /// - /// Modbus Byte Offset for read data telegram - /// - private enum TxOffset_R - { - AdrHigh = (int)BaseOffset.FuncCode + 1, - AdrLow, - RegCount_High, RegCount_Low - }; - - /// - /// Modbus Byte Offset for read data telegram - /// - private enum RxOffset_R - { - ByteCount = (int)BaseOffset.FuncCode + 1, - Data_Start - }; - - /// - /// Modbus Byte Offset for read data telegram - /// - private enum RxOffset_W - { - AdrHigh = (int)BaseOffset.FuncCode + 1, - AdrLow, - RegCount_High, RegCount_Low - }; - - /// - /// Modbus Byte Offset for exception data telegram - /// - private enum TxRxOffset_Exc - { - ExceptionCode = (int)BaseOffset.FuncCode + 1 - }; - - /// - /// Calc CRC - /// - /// - /// - /// - private UInt16 CalcCrc(Byte[] bByteData, int iDataCount) - { - int u8CountByte; - byte u8Temp, u8CountBit; - UInt16 u16RetCRCvalue; - u16RetCRCvalue = 0xffff; - for (u8CountByte = 0; u8CountByte < iDataCount; u8CountByte++) - { // Udregn CRC værdien - u16RetCRCvalue ^= bByteData[u8CountByte]; - for (u8CountBit = 0; u8CountBit < 8; u8CountBit++) - { - u8Temp = (Byte)(u16RetCRCvalue & 1); - u16RetCRCvalue >>= 1; - if (u8Temp == 1) - { - u16RetCRCvalue ^= 0xa001; - } - } - } - return u16RetCRCvalue; - } - - - #region IProtocolTranslateClass Members - public int MaxDataReadBSize() - { - return 250; ////(252-1"Number of bytes to read")/2=125 MODBUS over _serial line specification and implementation guide V1.02 (Page 13) + Func 4/3 - } - - public int MaxDataWriteBSize() - { - return 230; // - } - - public void StampReset() { } - - public byte StampNow - { - get { return 0; } // Off - } - - /// - /// Create a tx modbus message - /// - /// - /// - /// - /// - /// - /// - /// - public byte[] CreateTxData(int slaveID, int rwFuncNo, int adr, int dataByteSize, Byte[] writeData, bool IsDeviceResponse) - { - try - { - Byte bTemp; - Byte[] txData = null; - int count = 0, iCRC; - // Convert to modb us func number - FunctionNumber funcNo; - switch (rwFuncNo) - { - case (int)Func_RD.No3: funcNo = FunctionNumber.Read_No3; break; - case (int)Func_RD.No4: funcNo = FunctionNumber.Read_No4; break; - case (int)Func_WR.No15: funcNo = FunctionNumber.Write_No15; break; - case (int)Func_WR.No16: funcNo = FunctionNumber.Write_No16; break; - default: throw new Exception(LogMessageTool.ToFuncErrorText(this.ToString(), "CreateTxData()", "Incorrect func = " + rwFuncNo.ToString())); - } - #region ---- Read ---- - if ((funcNo == FunctionNumber.Read_No3) || (funcNo == FunctionNumber.Read_No4)) - { - if (IsDeviceResponse) - { - if ((writeData == null) && (dataByteSize != writeData.Length)) - { - var dataBLng = (writeData == null ? 0 : writeData.Length); - throw new Exception("Data length is not the same expected " + dataByteSize + " but is " + dataBLng); - } - count = (int)RxOffset_R.Data_Start + dataByteSize; - txData = new Byte[count + 2/*to crc*/ ]; - txData[(int)BaseOffset.ID] = (byte)slaveID; - txData[(int)BaseOffset.FuncCode] = (byte)funcNo; - txData[(int)RxOffset_R.ByteCount] = (byte)dataByteSize; - // Copy and Swap High And Low byte in word - for (int offset = 0; offset < dataByteSize; offset += 2) - { - bTemp = writeData[offset]; - txData[(int)RxOffset_R.Data_Start + offset] = writeData[offset + 1]; - txData[(int)RxOffset_R.Data_Start + offset + 1] = bTemp; - } - } - else - { - dataByteSize = (dataByteSize == 1) ? 2 : dataByteSize; - txData = new Byte[(int)TxOffset_R.RegCount_Low + 3]; - txData[(int)BaseOffset.ID] = (byte)slaveID; - txData[(int)BaseOffset.FuncCode] = (byte)funcNo; - txData[(int)TxOffset_R.AdrHigh] = (byte)(adr >> 8); - txData[(int)TxOffset_R.AdrLow] = (byte)adr; - txData[(int)TxOffset_R.RegCount_High] = (byte)0; - txData[(int)TxOffset_R.RegCount_Low] = (byte)(dataByteSize >> 1); - count = (int)TxOffset_R.RegCount_Low + 1; - } - } - #endregion// Read - #region ---- Write ---- - if ((funcNo == FunctionNumber.Write_No16) || (funcNo == FunctionNumber.Write_No15)) - { - dataByteSize = writeData.Length; - dataByteSize = (dataByteSize == 1) ? 2 : dataByteSize; - txData = new Byte[(int)TxOffset_W.Data_Start + dataByteSize + 2]; - txData[(int)BaseOffset.ID] = (byte)slaveID; - txData[(int)BaseOffset.FuncCode] = (byte)funcNo; - txData[(int)TxOffset_W.AdrHigh] = (byte)(adr >> 8); - txData[(int)TxOffset_W.AdrLow] = (byte)adr; - txData[(int)TxOffset_W.RegCount_High] = (byte)0; - txData[(int)TxOffset_W.RegCount_Low] = (byte)(dataByteSize >> 1); - txData[(int)TxOffset_W.ByteCount] = (byte)dataByteSize; - // Copy and Swap High And Low byte in word - for (count = 0; count < dataByteSize; count += 2) - { - bTemp = writeData[count]; - txData[(int)TxOffset_W.Data_Start + count] = writeData[count + 1]; - txData[(int)TxOffset_W.Data_Start + count + 1] = bTemp; - } - count = dataByteSize + (int)TxOffset_W.Data_Start; - } - #endregion// Write - iCRC = CalcCrc(txData, count); - txData[count] = (byte)iCRC; - txData[count + 1] = (byte)(iCRC >> 8); - return txData; // OK - } - catch - { - return null; // Error - } - } - - public bool IsDataOkay(byte[] data, bool IsDeviceResponse) - { - int iCRC; - int length = (data != null) ? data.Length : 0; - iCRC = data[length - 1]; - iCRC = data[length - 2] + (iCRC << 8); - return (CalcCrc(data, length - 2) == iCRC) || (CalcCrc(data, length - 2) == (((iCRC & 0xff) << 8) | (iCRC >> 8))); // Keller Fix - } - - public bool IsRxDataOkay(byte[] data) - { - return IsDataOkay(data, true); - } - - - public bool IsDataRW(byte[] data, bool IsDeviceResponse) - { - return (data[(int)BaseOffset.FuncCode] < 0x80); - } - - public bool IsRead(byte[] data, bool IsDeviceResponse) - { - return data[(int)BaseOffset.FuncCode] == (int)FunctionNumber.Read_No3 - || data[(int)BaseOffset.FuncCode] == (int)FunctionNumber.Read_No4; - } - - public byte[] GetReadData(byte[] data, bool IsDeviceResponse) - { - try - { - Byte[] retData = new Byte[(int)data[(int)RxOffset_R.ByteCount]]; - int offset = 0, length = 0, func = data[(int)BaseOffset.FuncCode]; - if ((func == (int)FunctionNumber.Read_No3) || (func == (int)FunctionNumber.Read_No4)) - { - offset = (int)RxOffset_R.Data_Start; - length = retData.Length; - if (data.Count() < length) - { - throw new Exception($"Data lng ={data.Count()} is expecting min. {length}"); - } - if (length == 0) - { - return null; // Error - } - for (int iTemp = 0; (iTemp < length); iTemp += 2) - { - retData[iTemp + 1] = data[offset + iTemp]; - retData[iTemp] = data[offset + iTemp + 1]; - } - } - else - { - return null; // Error - } - return retData; - } - catch (Exception ex) - { - var dataAsTxt = (data==null) ? "Data is null" : data.ToHex(true, false); - var message = $"Error: data={dataAsTxt}\n\r" + ex.Message + "\n\r"; - Console.WriteLine(message); - AppConst.Logger.AddFuncLog(true, this.ToString(), "GetReadData()", message); - throw new Exception(message); - } - } - - public bool IsWrite(byte[] data, bool IsDeviceResponse) - { - return (data[(int)BaseOffset.FuncCode] == (int)FunctionNumber.Write_No16); - } - - public bool IsAdrPossible(byte[] data, bool IsDeviceResponse) - { - return false; - } - - public int GetRxAdr(byte[] data, bool IsDeviceResponse) - { - return ((int)data[(int)RxOffset_W.AdrHigh] << 8) | (int)data[(int)RxOffset_W.AdrLow]; - } - - public bool IsRxWaitForCommand(byte[] data, bool IsDeviceResponse) - { - return - (data[(int)TxRxOffset_Exc.ExceptionCode] == (int)DeviceExceptionData.SpecialType.WaitForReset) - || IsRxWaitForFinishMax5S(data, IsDeviceResponse) - || IsRxWaitForFinishMax60S(data, IsDeviceResponse) - || IsRxWaitForFinishMax5Min(data, IsDeviceResponse); - } - - public bool IsRxWaitForFinishMax5S(byte[] data, bool IsDeviceResponse) - { - return data[(int)TxRxOffset_Exc.ExceptionCode] == (int)DeviceExceptionData.SpecialType.WaitForFinishMax5s; - } - - public bool IsRxWaitForFinishMax60S(byte[] data, bool IsDeviceResponse) - { - return data[(int)TxRxOffset_Exc.ExceptionCode] == (int)DeviceExceptionData.SpecialType.WaitForFinishMax60s; - } - - public bool IsRxWaitForFinishMax5Min(byte[] data, bool IsDeviceResponse) - { - return data[(int)TxRxOffset_Exc.ExceptionCode] == (int)DeviceExceptionData.SpecialType.WaitForFinishMax5Min; - } - - public bool IsAcknowledge(byte[] data, bool IsDeviceResponse) - { - return data[(int)BaseOffset.FuncCode] >= 0x80; - } - - public int GetRxAcknowledgeCode(byte[] data, bool IsDeviceResponse) - { - return data[(int)TxRxOffset_Exc.ExceptionCode]; - } - - public ProtocolType Protocol - { - get { return ProtocolType.ModbusRTU; } - } - - public int GetFuncNo(byte[] rx_data, bool IsDeviceResponse) - { - return (int)rx_data[(int)BaseOffset.FuncCode]; - } - - public int GetStartAdr(byte[] rx_data, bool IsDeviceResponse) - { - if (IsWrite(rx_data, IsDeviceResponse)) - { - return GetRxAdr(rx_data, IsDeviceResponse); - } - else if (IsRead(rx_data, IsDeviceResponse)) - { - return GetRxAdr(rx_data, IsDeviceResponse); // Is master read - } - throw new Exception("Unknown protocol type and location for start adr."); - } - - public int GetNumberOfDataBytes(byte[] rx_data, bool IsDeviceResponse) - { - if (IsWrite(rx_data, IsDeviceResponse)) - { - return rx_data[(int)RxOffset_W.RegCount_Low] * 2; - } - else if (IsRead(rx_data, IsDeviceResponse)) - { - if (IsDeviceResponse) - { - return rx_data[(int)RxOffset_R.ByteCount]; - } - else - { - return rx_data[(int)RxOffset_W.RegCount_Low] * 2; - } - } - throw new Exception("Unknown protocol type and location for number of bytes"); - } - - public int GetComID(byte[] rx_data, bool IsDeviceResponse) - { - return rx_data[(int)BaseOffset.ID]; - } - - public byte[] GetWriteData(byte[] rx_data, bool IsDeviceResponse) - { - var bytesToWrite = GetNumberOfDataBytes(rx_data, IsDeviceResponse); - try - { - var data = Base.ByteArrayExtensionClass.SwapByteInWord(rx_data.Copy((int)TxOffset_W.Data_Start, bytesToWrite)); - return data; - } - catch (Exception) - { - throw new Exception("Fail to copy write data! data byte size = " + bytesToWrite); - } - } - - public byte[] CreateTxData(int slaveID, int rwFuncNo, int adr, int writeResponse, bool IsDeviceResponse) - { - throw new NotImplementedException(); - } - - public string ToInfo(byte[] input, bool IsDeviceResponse) - { - string ret = ""; - try - { - bool isOkay = IsDataOkay(input, IsDeviceResponse); - if (isOkay) - { - int id = GetComID(input, IsDeviceResponse); - int func = GetFuncNo(input, IsDeviceResponse); - ret += string.Format("Type {0}" + Environment.NewLine, IsDeviceResponse ? "Response" : "Request"); - ret += string.Format("ID {0}" + Environment.NewLine, id); - bool isExeption = !IsDataRW(input, IsDeviceResponse); - try - { - var func2Test = func; - if (isExeption) - { - func2Test -= 0x80; // Get function from exeption code - ret += "Exception "; - } - ret += string.Format("Func. {0} / {1} hex", func2Test, func2Test.ToString("x")); - switch (func2Test) - { - case (int)FunctionNumber.Read_No3: - case (int)FunctionNumber.Read_No4: - ret += " is Read"; - break; - case (int)FunctionNumber.Write_No15: - case (int)FunctionNumber.Write_No16: - if (IsDeviceResponse) - { - ret += " Incorrect write replay from slave!!!!"; - } - else - { - ret += " is Write"; - } - break; - default: ret += " Unknown func. type!"; break; - } - } - catch { ret += string.Format(" Unknown func. type!"); } - ret += Environment.NewLine; - if (isExeption) - { - var exeptioncode = (int)input[(int)BaseOffset.FuncCode + 1]; - ret += string.Format("Exception code {0} / {1} hex", exeptioncode, exeptioncode.ToString("x")); - } - else - { - ret += string.Format("Number of Bytes {0}" + Environment.NewLine, GetNumberOfDataBytes(input, IsDeviceResponse)); - if (IsDeviceResponse) - { - byte[] data = null; - if (IsRead(input, IsDeviceResponse)) - { - data = GetReadData(input, IsDeviceResponse); - } - if (data != null) - { - data = Base.ByteArrayExtensionClass.SwapByteInWord(data); - ret += string.Format("Data is = {0}", Base.NumberConvert.ToHex(data, true, false)); - } - } - else - { - byte[] data = null; - ret += string.Format("Start Address {0}" + Environment.NewLine, GetStartAdr(input, IsDeviceResponse)); - if (IsWrite(input, IsDeviceResponse)) - { - data = GetWriteData(input, IsDeviceResponse); - } - if (data != null) - { - data = Base.ByteArrayExtensionClass.SwapByteInWord(data); - ret += string.Format("Data is = {0}\n", Base.NumberConvert.ToHex(data, true, false)); - } - } - } - //No44, // or 68 as a Special myconnect IO module telegram for https://mjksvn.world.fluidtechnology.net:4443/svn/development/8440XX Connect and myConnect/myConnect/trunk/docs/IO modules communication.docx - } - else if ((input != null) && (input.Length >= (16 + 2))) - { // Check if it is - var telegrams = input.SliceSameSize(0, (16 + 2)); - foreach (var telegram in telegrams) - { - if (IsDataOkay(telegram, IsDeviceResponse)) - { - ret += "¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤" + Environment.NewLine; - int offset = 0; - ret += "ID[" + telegram[offset++] + "]"; - ret += " func[" + telegram[offset++] + "]"; - int Quantity = telegram[offset++]; - int[] Descriptors = new int[] { telegram[offset++], 0 }; - { // print out Status - ret += Environment.NewLine; - int Status = telegram[offset++]; - switch (Status) - { - case 0x11: ret += "Both recognized"; break; - case 0x01: ret += "Lower OK, upper ERROR"; break; - case 0x10: ret += "Lower ERROR, upper OK"; break; - case 0x00: ret += "Both ERROR"; break; - default: ret += "Unknown status" + Status.ToHex(true); break; - } - } - {// Print out Descriptor - ret += Environment.NewLine; - Descriptors[1] = telegram[offset++]; - foreach (var Descriptor in Descriptors) - { - ret += " Type["; - switch (Descriptor) - { - case 0: ret += "none"; break; - case 1: ret += "6DI"; break; - case 2: ret += "6DO"; break; - case 3: ret += "3AI"; break; - case 4: ret += "3AO"; break; - default: ret += "Unknown" + Descriptor.ToHex(true); break; - } - ret += "]"; - } - } - {// Print out data - ret += Environment.NewLine + "Raw[" + telegram.Slice(offset, telegram.Length - offset).ToHex(true, false) + "]"; - - var u32Values = telegram.SliceSameSize(offset, 4); - foreach (var u32Value in u32Values) - { - ret += Environment.NewLine; - ret += " DI&DO[" + u32Value[0].ToBin(true) + "]/AI&AO[" + (new DataUnion()).PutByteArray(u32Value, DataUnion.DataByteLayoutType.SwapWords).F32Data + "]"; - } - } - } - else - { - ret += string.Format("Message is {0} is wrong!" + Environment.NewLine, telegram.ToHex(true, false)); - } - ret += Environment.NewLine; - } - } - else - { - ret += string.Format("Message is {0}" + Environment.NewLine, isOkay ? "okay" : "CRC is wrong!"); - } - } - catch (Exception ex) - { - ret += "Error = " + ex.Message; - } - return ret; - } - - /// - /// Added to IProtocolTranslateClass to use in Comli - /// - public byte[] CreateTxData(int slaveID, int rwFuncNo, int adr, int dataByteSize, byte[] writeData, bool IsDeviceResponse, ref int pageNumberNow) - { - throw new NotImplementedException(); - } - - #endregion - } -} \ No newline at end of file diff --git a/Common/modbus_master_csharp/Communication/ProtocolType.cs b/Common/modbus_master_csharp/Communication/ProtocolType.cs deleted file mode 100644 index 9917f686..00000000 --- a/Common/modbus_master_csharp/Communication/ProtocolType.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System; - -namespace XYLEM.Communication -{ - //------------------------------------- Define ------------------------------------------------ - public enum ProtocolType : int - { - ModbusRTU = 0, - None, - }; -} diff --git a/Common/modbus_master_csharp/Communication/ValueEncoders/ArrayManipulator.cs b/Common/modbus_master_csharp/Communication/ValueEncoders/ArrayManipulator.cs deleted file mode 100644 index 437b8573..00000000 --- a/Common/modbus_master_csharp/Communication/ValueEncoders/ArrayManipulator.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace XYLEM.Communication.ValueEncoders -{ - static class ArrayManipulator - { - public static byte[] SwapPairWise(byte[] input) - { - byte[] swapped = new byte[input.Length]; - for (int i = 0; i < input.Length; i = i+2) - { - swapped[i + 1] = input[i]; - swapped[i] = input[i + 1]; - } - - return swapped; - } - } -} diff --git a/Common/modbus_master_csharp/Communication/ValueEncoders/DeviceValueEncoder.cs b/Common/modbus_master_csharp/Communication/ValueEncoders/DeviceValueEncoder.cs deleted file mode 100644 index b485232c..00000000 --- a/Common/modbus_master_csharp/Communication/ValueEncoders/DeviceValueEncoder.cs +++ /dev/null @@ -1,952 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using XYLEM.Communication; -using System.Xml; -using System.Xml.Linq; -using XYLEM.Base; -using XYLEM.Base.Files; -using XYLEM.Device.Value; -using System.Numerics; -using XYLEM.Communication.ValueEncoders; -using XYLEM.Base.XML; - -namespace XYLEM.Communication.ValueEncoders -{ - /// - /// Return used character set used for email or empty if not Loaded from setup - /// - /// ex. "iso-8859-1" - public delegate string GetEmailCharacterSetToUse(); - - public static class DeviceValueEncoder - { - public static object Default(this ValueDataType dataType) - { - try - { - switch (dataType) - { - case ValueDataType.U8: - case ValueDataType.B8: return (byte)0; - case ValueDataType.S8: return (sbyte)0; - - case ValueDataType.B16: - case ValueDataType.U16: - case ValueDataType.U16MJK_SN: - return (UInt16)0; - case ValueDataType.U32TIME_Y1970: - case ValueDataType.U32TIME_Y1970_UTC: return ((UInt32)0).Convert1970SecToTime(TimeType.UTC); - case ValueDataType.U32TIME_Y2000: return ((UInt32)0).Convert2000SecToTime(); - case ValueDataType.U32MJK_SN: return ((UInt32)0); - //case DeviceValueDataType.U24DateHH_MM_SS: - //case DeviceValueDataType.U48DateYY_MM_DD_HH_MM_SS: return ((UInt32)0); - //case DeviceValueDataType.U24: - //case DeviceValueDataType.U32TIMESPAN_MIDNIGHT_UTC: - case ValueDataType.B32: - case ValueDataType.U32: return (UInt32)0; - case ValueDataType.B64: - case ValueDataType.U64: return (UInt64)0; - case ValueDataType.F64: return (double)0; - case ValueDataType.U128: return (BigInteger)0; - case ValueDataType.S16: return (Int16)0; - case ValueDataType.F32: return (Single)0; - //case DeviceValueDataType.U32IPV4: return "0.0.0.0"; // handled as a string - default: - if (dataType.IsText()) - { // Do this as the last check to save CPU power - return ""; - } - throw new Exception(dataType.ToString() + "has no default value)"); - } - } - catch (Exception ex) - { - throw new Exception(ex.ToFuncErrorText(typeof(DeviceValueEncoder).ToString(), "Default()", "dataType = " + dataType.ToString())); - } - } - - public static string EncodeXML(this object value, ValueDataType type) - { - try - { - string valueAsTxt = value.ToString(); - switch (type) - { - case ValueDataType.U32TIME_Y1970: - case ValueDataType.U32TIME_Y1970_UTC: - if (value is DateTime) - { - var value32bit = ((DateTime)value).ConvertTimeTo1970Sec(TimeType.Local); - return XmlConvert.ToString(value32bit); - } - return XmlConvert.ToString((Double)value); - case ValueDataType.U32TIME_Y2000: - if (value is DateTime) - { - var value32bit = ((DateTime)value).ConvertTimeTo2000Sec(TimeType.Local); - return XmlConvert.ToString(value32bit); - } - return XmlConvert.ToString((Double)value); - case ValueDataType.U8: - case ValueDataType.B8: - case ValueDataType.S8: - //case DeviceValueDataType.U32TIMESPAN_MIDNIGHT_UTC: - case ValueDataType.B16: - case ValueDataType.U16: - case ValueDataType.U16MJK_SN: - //case DeviceValueDataType.U48DateYY_MM_DD_HH_MM_SS: - //case DeviceValueDataType.U24DateHH_MM_SS: - //case DeviceValueDataType.U24: - case ValueDataType.B32: - case ValueDataType.U32: - case ValueDataType.B64: - case ValueDataType.U64: - case ValueDataType.S16: - case ValueDataType.S32: - case ValueDataType.S64: - case ValueDataType.F32: - case ValueDataType.F64: - case ValueDataType.U32MJK_SN: - { - if (typeof(Double) == value.GetType()) - { - valueAsTxt = XmlConvert.ToString((Double)value); - } - else if (typeof(Single) == value.GetType()) - { - valueAsTxt = XmlConvert.ToString((Single)value); - } - return valueAsTxt; - } - case ValueDataType.U128: - if (typeof(BigInteger) == value.GetType()) - { - valueAsTxt = ((BigInteger)value).ToString("X"); // enclode as hex - } - else - { - throw new Exception($"{value.GetType()} is not supported for U128 xml convert"); - } - return valueAsTxt; - - //case DeviceValueDataType.U32IPV4: return valueAsTxt.Trim( ).StripIllegalXMLChars( );// handled as a string - default: - if (type.IsText()) - { // Do this as the last check to save CPU power - return valueAsTxt.Trim().StripIllegalXMLChars(); - } - throw new NotImplementedException("DataType '" + type + "' not recognized."); - } - } - catch (Exception ex) - { - throw new Exception(ex.ToFuncErrorText(typeof(DeviceValueEncoder).ToString(), "EncodeXML()", "dataType = " + type.ToString() + " Value is = " + value.ToString())); - } - } - - public static Object DecodeXML(this string valueInXml, ValueDataType type) - { - try - { - switch (type) - { - case ValueDataType.U32TIME_Y1970: - case ValueDataType.U32TIME_Y1970_UTC: - return XmlConvert.ToUInt32(valueInXml).Convert1970SecToTime(TimeType.Local); - case ValueDataType.U32TIME_Y2000: - return XmlConvert.ToUInt32(valueInXml).Convert2000SecToTime(); - //case DeviceValueDataType.U32TIMESPAN_MIDNIGHT_UTC: return XmlConvert.ToUInt32( valueInXml ); - case ValueDataType.B8: - case ValueDataType.U8: - return XmlConvert.ToByte(valueInXml); - case ValueDataType.S8: - return XmlConvert.ToSByte(valueInXml); - case ValueDataType.B16: - case ValueDataType.U16: - case ValueDataType.U16MJK_SN: - return XmlConvert.ToUInt16(valueInXml); - //case DeviceValueDataType.U24DateHH_MM_SS: - //case DeviceValueDataType.U48DateYY_MM_DD_HH_MM_SS: - //case DeviceValueDataType.U24: - case ValueDataType.B32: - case ValueDataType.U32: - case ValueDataType.U32MJK_SN: - return XmlConvert.ToUInt32(valueInXml); - case ValueDataType.B64: - case ValueDataType.U64: return XmlConvert.ToUInt64(valueInXml); - case ValueDataType.S16: return XmlConvert.ToInt16(valueInXml); - case ValueDataType.S32: return XmlConvert.ToInt32(valueInXml); - case ValueDataType.S64: return XmlConvert.ToInt64(valueInXml); - case ValueDataType.F32: return XmlConvert.ToSingle(valueInXml); - case ValueDataType.F64: return XmlConvert.ToDouble(valueInXml); - case ValueDataType.U128: return BigInteger.Parse(valueInXml, System.Globalization.NumberStyles.HexNumber); - //case DeviceValueDataType.U32IPV4: return valueInXml.ReinsetIllegalXMLChars( );// handled as a string - default: - if (type.IsText()) - { // Do this as the last check to save CPU power - return valueInXml.ReinsetIllegalXMLChars(); - } - throw new NotImplementedException("DataType '" + type + "' not recognized."); - } - } - catch (Exception ex) - { - throw new Exception(ex.ToFuncErrorText(typeof(DeviceValueEncoder).ToString(), "DecodeXML()", "dataType = " + type.ToString() + " Value is = " + valueInXml)); - } - } - - /// - /// do layout changes on byte stream - /// - /// - /// - /// - static byte[] DoLayout(this byte[] bytes, LayoutType layout = LayoutType.Normal) - { - try - { - switch (layout) - { - case LayoutType.Normal: return bytes; ; // Do nothing - case LayoutType.SwapAll: return bytes.Reverse(); - default: - throw new NotSupportedException(layout + " is not supported"); - } - } - catch (Exception ex) - { - throw new Exception(ex.ToFuncErrorText(typeof(DeviceValueEncoder).ToString(), "DoLayout", "layout = " + layout)); - } - } - - - /// - /// Encode data with normal data layout - /// - /// - /// - /// Null if not used - /// - public static byte[] Encode(this object value, ValueDataType dataType, GetEmailCharacterSetToUse emailCharacterSet = null) - { - try - { - if (value == null) - { - throw new Exception("Value is null"); - } - switch (dataType) - { - case ValueDataType.B8: - case ValueDataType.U8: - { - value = CorrectTypeIfNotTheSame(value, dataType, typeof(byte));// Try set type if not the correct one - return BitConverter.GetBytes((byte)value); - } - case ValueDataType.S8: - { - value = CorrectTypeIfNotTheSame(value, dataType, typeof(sbyte));// Try set type if not the correct one - return BitConverter.GetBytes((sbyte)value); - } - case ValueDataType.B16: - case ValueDataType.U16: - case ValueDataType.U16MJK_SN: - return BitConverter.GetBytes((UInt16)value); - case ValueDataType.U32TIME_Y1970: - if (value is DateTime) - { - return BitConverter.GetBytes(((DateTime)value).ConvertTimeTo1970Sec(TimeType.Local)); - } - return BitConverter.GetBytes((UInt32)value); // Work around on olde types - case ValueDataType.U32TIME_Y1970_UTC: - if (value is DateTime) - { - return BitConverter.GetBytes(((DateTime)value).ConvertTimeTo1970Sec(TimeType.UTC)); - } - return BitConverter.GetBytes((UInt32)value); // Work around on olde types - case ValueDataType.U32TIME_Y2000: - if (value is DateTime) - { - return BitConverter.GetBytes(((DateTime)value).ConvertTimeTo2000Sec(TimeType.Local)); - } - return BitConverter.GetBytes((UInt32)value); - // Work around on olde types - //case DeviceValueDataType.U32TIMESPAN_MIDNIGHT_UTC: - // return BitConverter.GetBytes( TimeConvertClass.ToMidnightSecOffsetUTC( (UInt32)value ) ); - //case DeviceValueDataType.U24DateHH_MM_SS: { - // var timeSpan = TimeConvertClass.ConvertTimeSpan( (UInt32)value ); - // return new byte[] { (byte)timeSpan.Seconds, (byte)timeSpan.Minutes, (byte)timeSpan.Hours }; - // } - //case DeviceValueDataType.U48DateYY_MM_DD_HH_MM_SS: { - // var date = ((UInt32)value).Convert1970SecToTime( TimeType.Local ); - // return new byte[] { - // (byte)date.Second, - // (byte)date.Minute, - // (byte)date.Hour, - // (byte)date.Day, - // (byte)date.Month, - // (byte)(date.Year - 2000) - // }; - // } - //case DeviceValueDataType.U24: { - // value = CorrectTypeIfNotTheSame( value, dataType, typeof( UInt32 ) );// Try set type if not the correct one - // return BitConverter.GetBytes( (UInt32)value ).Remove( 3 ); - // } - case ValueDataType.B32: - case ValueDataType.U32: - case ValueDataType.U32MJK_SN: - { - value = CorrectTypeIfNotTheSame(value, dataType, typeof(UInt32));// Try set type if not the correct one - return BitConverter.GetBytes((UInt32)value); - } - case ValueDataType.B64: - case ValueDataType.U64: - { - value = CorrectTypeIfNotTheSame(value, dataType, typeof(UInt64));// Try set type if not the correct one - return BitConverter.GetBytes((UInt64)value); - } - case ValueDataType.S16: - { - value = CorrectTypeIfNotTheSame(value, dataType, typeof(Int16));// Try set type if not the correct one - return BitConverter.GetBytes((Int16)value); - } - case ValueDataType.S32: - { - value = CorrectTypeIfNotTheSame(value, dataType, typeof(Int32));// Try set type if not the correct one - return BitConverter.GetBytes((Int32)value); - } - case ValueDataType.F32: - { - value = CorrectTypeIfNotTheSame(value, dataType, typeof(Single));// Try set type if not the correct one - return BitConverter.GetBytes((Single)value); - } - case ValueDataType.F64: - { - value = CorrectTypeIfNotTheSame(value, dataType, typeof(double));// Try set type if not the correct one - return BitConverter.GetBytes((double)value); - } - case ValueDataType.U128: - { - value = CorrectTypeIfNotTheSame(value, dataType, typeof(BigInteger));// Try set type if not the correct one - return ((BigInteger)value).ToByteArray(); // TODO: Is this okay or need always to be the 16 byte in size? - } - //case DeviceValueDataType.U32IPV4: return U32_IP_Encoder.Encode( value.ToString( ) ); - default: - if (dataType.IsText()) - { // Do this as the last check to save CPU power - var typeAsText = dataType.ToString(); - // is Email - if (dataType.IsEmailText()) - { - return StringEncoderEmail.Encode(value.ToString(), Length(dataType), ((emailCharacterSet == null) ? "" : emailCharacterSet())); - } - // is SMS - if (dataType.IsSMSText()) - { - return StringEncoderUnicode.Encode(value.ToString(), Length(dataType)); - } - // is Windows 1252 - if (dataType.IsWindows1252Text()) - { - return StringEncoder_W1252.Encode(value.ToString(), Length(dataType)); - } - // is Windows 1252, but needs to have "<>" in frond and end - // Ex. end result is "," or "" - if (dataType.IsWindows1252TextRecipient()) - { - var valueAsText = value.ToString().Trim(); - if (!String.IsNullOrEmpty(valueAsText)) - { - // Remove manual added brackets or " if user has done it in the past - if (valueAsText.Contains("\"")) - { - valueAsText = valueAsText.Replace("\"", ""); - } - //// work arround if user has already used "," as separation - //if (valueAsText.Contains( "," )) { - // valueAsText = valueAsText.Replace( ",", ">,<" ); //old modem firmware V1.003 and TDC NO - //} - // Replace ";" with correct "," as separation and add bracked ">" "<" to support RCF821 - if (valueAsText.Contains(";")) - { - //valueAsText = valueAsText.Replace( ";", ">,<" ); old modem firmware V1.003 and TDC NO - valueAsText = valueAsText.Replace(";", ",");//new modem firmware V1.3 using mail.mjk-link.dk - } - // Trim - valueAsText = valueAsText.Trim(); - // Add brackets "<>" around address / addresses and end with " to tell modem where it starts and end in MC55I at command - //Ex. end result is "," or "" - if (!String.IsNullOrEmpty(valueAsText)) - { - //valueAsText = "\"<" + valueAsText + "\">"; old modem firmware V1.003 and TDC NO - valueAsText = "\"" + valueAsText + "\""; //new modem firmware V1.3 using mail.mjk-link.dk - } - } - return StringEncoder_W1252.Encode(valueAsText, Length(dataType)); - } - // is Quoted Printable Text - if (dataType.IsEmailQuotedPrintableText()) - { - return StringEncoderEmail_Q_Types.Encode(value.ToString(), Length(dataType), ((emailCharacterSet == null) ? "" : emailCharacterSet())); - } - // Is is normal utf8 - return StringEncoder.Encode(value.ToString(), Length(dataType)); - } - throw new NotImplementedException("DataType '" + dataType + "' not recognized."); - } - } - catch (Exception ex) - { - throw new Exception(ex.ToFuncErrorText(typeof(DeviceValueEncoder).ToString(), "Encode", "dataType = " + dataType.ToString())); - } - } - - private static object CorrectTypeIfNotTheSame(object value, ValueDataType correctTypeTag, Type typeUsedForCast) - { - try - { - if (value.GetType() == typeUsedForCast) - { - return value; // Nothing to change - } - { - AppConst.Logger.AddFuncLog( -#if DEBUG // - true // log as error -#else - false // Log silent in log -#endif //#if DEBUG - , typeof(DeviceValueEncoder).ToString(), "CorrectTypeIfNotTheSame()", - "Warning! correction was needed. Is this a older setting else not okay? " + MakeCorrectTypeValueInfoText(value, correctTypeTag, typeUsedForCast) - ); - } - return ToValue(value.ToString(), correctTypeTag); - } - catch (Exception ex) - { - var valueInfo = MakeCorrectTypeValueInfoText(value, correctTypeTag, typeUsedForCast); - throw new Exception(ex.ToFuncErrorText(typeof(DeviceValueEncoder).ToString(), "CorrectTypeIfNotTheSame", valueInfo)); - } - } - - /// - /// Generate the value info possible to get for a value to convert - /// - /// Value to correct - /// correct value type tag - /// type used for cast - /// Text to show in log - private static string MakeCorrectTypeValueInfoText(object value, ValueDataType correctTypeTag, Type typeUsedForCast) - { - var ValueInfo = ""; - try - { - if (ValueInfo == null) - { - ValueInfo += " Value= IS NULL and not possible to correct!"; - } - else - { - ValueInfo += " Value=\"" + value.ToString() + "\""; - ValueInfo += " From Type=\"" + value.GetType() + "\""; - } - ValueInfo += " To Type=\"" + correctTypeTag.ToString() + "\""; - } - catch { ValueInfo += " [Fail to get info ?] "; } - return ValueInfo; - } - - /// - /// Encode data with data layout setting - /// - /// - /// - /// - /// Null if not used - /// - public static byte[] Encode(this object value, ValueDataType dataType, LayoutType layout, GetEmailCharacterSetToUse emailCharacterSet = null) - { - try - { - var bytes = Encode(value, dataType, emailCharacterSet); - if (bytes == null) - { - return bytes; - } - return bytes.DoLayout(layout); - } - catch (Exception ex) - { - throw new Exception(ex.ToFuncErrorText(typeof(DeviceValueEncoder).ToString(), "Encode", "dataType = " + dataType.ToString())); - } - } - - - /// - /// Decode data with normal data layout - /// - /// - /// - /// Null if not used - /// - public static object Decode(this byte[] value, ValueDataType dataType, GetEmailCharacterSetToUse emailCharacterSet = null) - { - try - { - if (value == null) - { - throw new Exception("Value is null"); - } - if (value.Count() == 0) - { - throw new Exception("Value Has no values in array"); - } - switch (dataType) - { - case ValueDataType.B8: - case ValueDataType.U8: - return (byte)value.First(); - case ValueDataType.S8: - return (sbyte)value.First(); - case ValueDataType.B16: - case ValueDataType.U16: - case ValueDataType.U16MJK_SN: - return BitConverter.ToUInt16(value, 0); - case ValueDataType.U32TIME_Y1970: - return BitConverter.ToUInt32(value, 0).Convert1970SecToTime(TimeType.Local); - case ValueDataType.U32TIME_Y1970_UTC: - return BitConverter.ToUInt32(value, 0).Convert1970SecToTime(TimeType.UTC).ToLocalTime(); - case ValueDataType.U32TIME_Y2000: - return BitConverter.ToUInt32(value, 0).Convert2000SecToTime(); - //case DeviceValueDataType.U32TIMESPAN_MIDNIGHT_UTC: - // return TimeConvertClass.FromMidnightSecOffsetUTC( BitConverter.ToUInt32( value, 0 ) ); - //case DeviceValueDataType.U24DateHH_MM_SS: { - // return (UInt32)((UInt32)value[ 2 ] * 3600) + ((UInt32)value[ 1 ] * 60 + (UInt32)value[ 0 ]); - // } - //case DeviceValueDataType.U48DateYY_MM_DD_HH_MM_SS: { - // var date = new DateTime( (int)value[ 5 ] + 2000, value[ 4 ], value[ 3 ], value[ 2 ], value[ 1 ], value[ 0 ] ); - // return date.ConvertTimeTo1970Sec( TimeType.Local ); - // } - //case DeviceValueDataType.U24: { - // var as32bitValue = new byte[] { value[ 0 ], value[ 1 ], value[ 2 ], 0 }; - // return BitConverter.ToUInt32( as32bitValue, 0 ); - // } - case ValueDataType.B32: - case ValueDataType.U32: - case ValueDataType.U32MJK_SN: - return BitConverter.ToUInt32(value, 0); - case ValueDataType.B64: - case ValueDataType.U64: return BitConverter.ToUInt64(value, 0); - case ValueDataType.S16: return BitConverter.ToInt16(value, 0); - case ValueDataType.S32: return BitConverter.ToInt32(value, 0); - case ValueDataType.F32: return BitConverter.ToSingle(value, 0); - case ValueDataType.F64: return BitConverter.ToDouble(value, 0); - case ValueDataType.U128: - { - // Need a zero in msb to avoid value is handled as signed - var valueConverted = new BigInteger((value).Concat(new byte[1]).ToArray()); - return valueConverted; - } - - case ValueDataType.Array6B: - case ValueDataType.Array10B: - case ValueDataType.Array12B: - case ValueDataType.Array64B: - case ValueDataType.Array128B: - case ValueDataType.Array136B: - case ValueDataType.Array250B: return value; - //case DeviceValueDataType.U32IPV4: return U32_IP_Encoder.Decode( value ); - default: - if (dataType.IsText()) - { // Do this as the last check to save CPU power - var typeAsText = dataType.ToString(); - if (dataType.IsEmailText()) - { // is Email - return StringEncoderEmail.Decode(value, Length(dataType), ((emailCharacterSet == null) ? "" : emailCharacterSet())); - } - if (dataType.IsSMSText()) - { // is SMS - return StringEncoderUnicode.Decode(value, Length(dataType)); - } - if (dataType.IsWindows1252Text()) - { // is Windows 1252 - return StringEncoder_W1252.Decode(value, Length(dataType)); - } - if (dataType.IsWindows1252TextRecipient()) - { // is Windows 1252, but needs to have "<>" in frond and end - var valueAsText = StringEncoder_W1252.Decode(value, Length(dataType)); - if (!String.IsNullOrEmpty(valueAsText)) - { - // Remove added brackets or " nessesarry that is only to be - // Ex. end result is "," or "" - if (valueAsText.Contains("\"")) - { - valueAsText = valueAsText.Replace("\"", ""); - } - if (valueAsText.Contains("<")) - { - valueAsText = valueAsText.Replace("<", ""); - } - if (valueAsText.Contains(">")) - { - valueAsText = valueAsText.Replace(">", ""); - } - // Replace email seperation and replace it with correct the standard ";" - if (valueAsText.Contains(",")) - { - valueAsText = valueAsText.Replace(",", ";"); - } - } - return valueAsText; - } - if (dataType.IsEmailQuotedPrintableText()) - { - return StringEncoderEmail_Q_Types.Decode(value, Length(dataType), ((emailCharacterSet == null) ? "" : emailCharacterSet())); - } - // Is allowed to have a variable length on a string / text - var maxLng = dataType.Length(); - if (value.Length > maxLng) - { - throw new Exception(String.Format("Size of text is {0} and more than max {1}", value.Length, maxLng)); - } - // Is is normal utf8 - return StringEncoder.Decode(value, value.Length); - } - throw new NotImplementedException("DataType '" + dataType + "' not recognized."); - } - } - catch (Exception ex) - { - throw new Exception(ex.ToFuncErrorText(typeof(DeviceValueEncoder).ToString(), "Decode", "dataType = " + dataType.ToString())); - } - } - - /// - /// Decode data with data layout setting - /// - /// - /// - /// Null if not used - /// - /// - public static object Decode(this byte[] value, ValueDataType dataType, LayoutType layout, GetEmailCharacterSetToUse emailCharacterSet = null) - { - try - { - var bytes = value.DoLayout(layout); - return Decode(value, dataType, emailCharacterSet); - } - catch (Exception ex) - { - throw new Exception(ex.ToFuncErrorText(typeof(DeviceValueEncoder).ToString(), "Encode", "dataType = " + dataType.ToString())); - } - } - - /// - /// Convert a local defined string value to the correct value object according to dataType ("DeviceValueType") - /// - /// Value as string (floating point must use local setting for "," or ".") and DateTime as local time - /// - /// - public static object ToValue(this string value, ValueDataType dataType) - { - try - { - if (value == null) - { - throw new Exception("Value is null"); - } - switch (dataType) - { - case ValueDataType.B8: - case ValueDataType.U8: - return (byte)Convert.ToUInt16(value); - case ValueDataType.S8: - return (sbyte)Convert.ToInt16(value); - case ValueDataType.B16: - case ValueDataType.U16: - case ValueDataType.U16MJK_SN: - return Convert.ToUInt16(value); - case ValueDataType.U32TIME_Y1970: - case ValueDataType.U32TIME_Y1970_UTC: - case ValueDataType.U32TIME_Y2000: - // Always expect this as local time - return new DateTime(Convert.ToDateTime(value).Ticks, DateTimeKind.Local); - //case DeviceValueDataType.U32TIMESPAN_MIDNIGHT_UTC: - //case DeviceValueDataType.U24DateHH_MM_SS: - //case DeviceValueDataType.U24: - case ValueDataType.B32: - case ValueDataType.U32: - case ValueDataType.U32MJK_SN: - return Convert.ToUInt32(value); - case ValueDataType.B64: - case ValueDataType.U64: return Convert.ToUInt64(value); - case ValueDataType.S16: return Convert.ToInt16(value); - case ValueDataType.S32: return Convert.ToInt32(value); - case ValueDataType.F32: return Convert.ToSingle(value); - case ValueDataType.F64: return Convert.ToDouble(value); - case ValueDataType.U128: - // Need a zero in msb to avoid value is handled as signed - return BigInteger.Parse("0"+value, System.Globalization.NumberStyles.HexNumber); - //case DeviceValueDataType.U32IPV4: { - // if (dataType.IsText( )) { - // return value.ToString( ); - // } - // throw new NotImplementedException( "DataType '" + dataType + "' not recognized." ); - // } - default: - if (dataType.IsText()) - { // Do this as the last check to save CPU power - var typeAsText = dataType.ToString(); - if (dataType.IsEmailText()) - { // is Email - return value; - } - if (dataType.IsSMSText()) - { // is SMS - return value; - } - if (dataType.IsWindows1252Text()) - { // is Windows 1252 - return value; - } - if (dataType.IsWindows1252TextRecipient()) - { // is Windows 1252, but needs to have "<>" in frond and end - return value; - } - if (dataType.IsEmailQuotedPrintableText()) - { // is Special Email Quoted Printable - return value; - } - // Is is normal utf8 - return value; //TODO: No converting is done. may have to be converted to utf8? - } - throw new NotImplementedException("DataType '" + dataType + "' not recognized."); - } - } - catch (Exception ex) - { - throw new Exception(ex.ToFuncErrorText(typeof(DeviceValueEncoder).ToString(), "ToValue()", "dataType = " + dataType.ToString())); - } - } - - /// - /// Return data Byte Length (1x8bit) - /// - /// - public static int Length(this ValueDataType DataType) - { - try - { - switch (DataType) - { - case ValueDataType._Empty: return 0; - #region ---- 8 Bit ---- - case ValueDataType.Bool: - case ValueDataType.B8: - case ValueDataType.S8: - case ValueDataType.U8: return 1; - #endregion // #region ---- 8 Bit ---- - //case DeviceValueDataType.U24DateHH_MM_SS: - //case DeviceValueDataType.U24: return 3; - //case DeviceValueDataType.U48DateYY_MM_DD_HH_MM_SS: return 6; - default: - return WLength(DataType) << 1; - } - } - catch (Exception ex) - { - throw new Exception(ex.ToFuncErrorText(typeof(DeviceValueEncoder).ToString(), "Length")); - } - } - - /// - /// Return data Word Length (2x8bit) - /// - /// - public static int WLength(this ValueDataType DataType) - { - try - { - switch (DataType) - { - case ValueDataType._Empty: return 0; - #region ---- 8 / 16 Bit ---- - case ValueDataType.Bool: - case ValueDataType.B8: - case ValueDataType.S8: - case ValueDataType.U8: - case ValueDataType.U16: - case ValueDataType.U16MJK_SN: - case ValueDataType.B16: - case ValueDataType.S16: - case ValueDataType.U16TEXTID: - case ValueDataType.U16UNIT: - case ValueDataType.U16UNITGROUP: return 1; - #endregion - //case DeviceValueDataType.U48DateYY_MM_DD_HH_MM_SS: return 3; - //case DeviceValueDataType.U24DateHH_MM_SS: - //case DeviceValueDataType.U24: return 2; - #region ---- 32 Bit ---- - case ValueDataType.B32: - case ValueDataType.U32: - case ValueDataType.U32MJK_SN: - case ValueDataType.S32: - case ValueDataType.F32: - //case DeviceValueDataType.U32TIMESPAN_MIDNIGHT_UTC: - case ValueDataType.U32TIME_Y2000: - case ValueDataType.U32TIME_Y1970: - case ValueDataType.U32TIME_Y1970_UTC: - //case DeviceValueDataType.U32IPV4: - return 2; - #endregion - #region ---- 12 x 8 Bit Time Types ---- - case ValueDataType.TIME_ASCII_12B: - case ValueDataType.TIME_ymdhms: - return 6; - #endregion - #region ---- 64 Bit ---- - case ValueDataType.B64: - case ValueDataType.U64: - case ValueDataType.S64: - case ValueDataType.F64: - return 4; - #endregion - #region ---- 128 Bit ---- - case ValueDataType.U128: - return 8; - #endregion - #region ---- Bit Arrays ---- - case ValueDataType.Array6B: return (6 / 2); - case ValueDataType.Array10B: return (10 / 2); - case ValueDataType.Array12B: return (12 / 2); - case ValueDataType.Array64B: return (64 / 2); - case ValueDataType.Array128B: return (128 / 2); - case ValueDataType.Array136B: return (136 / 2); - case ValueDataType.Array250B: return (250 / 2); - #endregion - #region ---- Strings / Text ---- - //6bytes - case ValueDataType.STR6E: - case ValueDataType.STR6S: - //case DeviceValueDataType.STR6Q: - //case DeviceValueDataType.STR6W1252: - case ValueDataType.STR6: return 3; - //8bytes - case ValueDataType.STR8E: - case ValueDataType.STR8S: - //case DeviceValueDataType.STR8Q: - //case DeviceValueDataType.STR8W1252: - case ValueDataType.STR8: return 4; - //10bytes - case ValueDataType.STR10E: - case ValueDataType.STR10S: - //case DeviceValueDataType.STR10Q: - //case DeviceValueDataType.STR10W1252: - case ValueDataType.STR10: return 5; - //16bytes - case ValueDataType.STR16E: - case ValueDataType.STR16S: - //case DeviceValueDataType.STR16Q: - //case DeviceValueDataType.STR16W1252: - case ValueDataType.STR16: return 8; - //18bytes - case ValueDataType.STR18E: - case ValueDataType.STR18S: - //case DeviceValueDataType.STR18Q: - //case DeviceValueDataType.STR18W1252: - case ValueDataType.STR18: return 9; - //20bytes - case ValueDataType.STR20E: - case ValueDataType.STR20S: - //case DeviceValueDataType.STR20Q: - //case DeviceValueDataType.STR20W1252: - case ValueDataType.STR20: return 10; - //22bytes - case ValueDataType.STR22E: - case ValueDataType.STR22S: - //case DeviceValueDataType.STR22Q: - //case DeviceValueDataType.STR22W1252: - case ValueDataType.STR22: return 11; - //30bytes - case ValueDataType.STR30E: - case ValueDataType.STR30S: - //case DeviceValueDataType.STR30Q: - //case DeviceValueDataType.STR30W1252: - case ValueDataType.STR30: return 15; - //32bytes - case ValueDataType.STR32E: - case ValueDataType.STR32S: - //case DeviceValueDataType.STR32Q: - //case DeviceValueDataType.STR32W1252: - case ValueDataType.STR32: return 16; - //64bytes - case ValueDataType.STR64E: - case ValueDataType.STR64S: - //case DeviceValueDataType.STR64Q: - //case DeviceValueDataType.STR64W1252: - case ValueDataType.STR64: return 32; - //100bytes - case ValueDataType.STR100E: - case ValueDataType.STR100S: - //case DeviceValueDataType.STR100Q: - //case DeviceValueDataType.STR100W1252: - case ValueDataType.STR100: return 50; - //128bytes - case ValueDataType.STR128E: - case ValueDataType.STR128S: - //case DeviceValueDataType.STR128Q: - //case DeviceValueDataType.STR128W1252: - //case DeviceValueDataType.STR128W1252rec: - case ValueDataType.STR128: return 64; - //160bytes - //case DeviceValueDataType.STR160E: - //case DeviceValueDataType.STR160S: - //case DeviceValueDataType.STR160Q: - //case DeviceValueDataType.STR160W1252: - //case DeviceValueDataType.STR160: return 80; - //240bytes - case ValueDataType.STR240E: - case ValueDataType.STR240S: - //case DeviceValueDataType.STR240Q: - //case DeviceValueDataType.STR240W1252: - case ValueDataType.STR240: return 240; - #endregion - default: - throw (new Exception("No known length for tag = " + DataType.ToString())); - } - } - catch (Exception ex) - { - throw new Exception(ex.ToFuncErrorText(typeof(DeviceValueEncoder).ToString(), "WLength")); - } - } - - /// - /// Return the text length for value type if it is a text - /// - /// - public static int StringLength(this ValueDataType DataType) - { - try - { - switch (DataType) - { - //case DeviceValueDataType.U32IPV4: return (4 * 3 + 3); - default: - if (DataType.IsSMSText()) - { // is SMS - return DataType.Length() / 2; // is always 16bit - } - return Length(DataType); - } - } - catch (Exception ex) - { - throw new Exception(ex.ToFuncErrorText(typeof(DeviceValueEncoder).ToString(), "StringLength")); - } - } - } -} diff --git a/Common/modbus_master_csharp/Communication/ValueEncoders/LayoutType.cs b/Common/modbus_master_csharp/Communication/ValueEncoders/LayoutType.cs deleted file mode 100644 index e1cad01c..00000000 --- a/Common/modbus_master_csharp/Communication/ValueEncoders/LayoutType.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace XYLEM.Communication.ValueEncoders { - public enum LayoutType { - Normal, - SwapAll, - } -} diff --git a/Common/modbus_master_csharp/Communication/ValueEncoders/StringEncoder.cs b/Common/modbus_master_csharp/Communication/ValueEncoders/StringEncoder.cs deleted file mode 100644 index b9654183..00000000 --- a/Common/modbus_master_csharp/Communication/ValueEncoders/StringEncoder.cs +++ /dev/null @@ -1,92 +0,0 @@ -using System; -using System.Text; -using XYLEM.Communication; - -namespace XYLEM.Communication.ValueEncoders { - public static class StringEncoder { - public static Encoding GetDefaultEncoding( ) { - try { - return Encoding.GetEncoding( "Windows-1252"/*Strings._CONFIG_STD_String_DefaultCharacterEncoding*/ ); - } catch (Exception ex) { - throw new Exception( typeof( StringEncoder ).ToString( )+ "\n\rGetDefaultEncoding"+ "\n\rError when making default encoding with = "+ "\n\rWindows-1252"/*Strings._CONFIG_STD_String_DefaultCharacterEncoding*/, ex ); - } - //Legacy code return Encoding.GetEncoding( 1252 ); - } - - /// - /// Standard Windows iso-8859-1-encoding page. - /// - static Encoding s_Encoder = GetDefaultEncoding( ); // Default Fallback - - /// - /// - /// - /// - /// - /// ex. "iso-8859-1" - /// - public static byte[] Encode(string value, int lenght, string CharacterSet) { - if (value.Length > lenght) { - throw new ArgumentException( "value.Length is too long", "value" ); - } - var result = new byte[ lenght ]; - try { - if (!String.IsNullOrEmpty( CharacterSet )) { - Encoding.GetEncoding( CharacterSet ).GetBytes( value ).CopyTo( result, 0 ); - return result; - } - } catch (Exception ex) { - throw new Exception( typeof( StringEncoder ).ToString( )+ "\n\rEncode"+ "\n\rWith = " + "iso-8859-1"/*Strings._CONFIG_STD_Email_DefaultCharacterEncoding*/ + Environment.NewLine + ex.Message ); - } - // Use defalt - s_Encoder.GetBytes( value ).CopyTo( result, 0 ); - return result; - } - - /// - /// Use default characterSet - /// - /// - /// - /// - public static byte[] Encode(string value, int lenght) { return Encode( value, lenght, null ); } - - /// - /// - /// - /// - /// - /// ex. "iso-8859-1" - /// - public static string Decode(byte[] value, int lenght, string CharacterSet) { - if (value.Length != lenght) { - throw new ArgumentException( "value.Length wrong lenght", "value" ); - } - string result = null; - try { - if (!String.IsNullOrEmpty( CharacterSet )) { - result = Encoding.GetEncoding( CharacterSet ).GetString( value ); - } - } catch (Exception ex) { - throw new Exception( typeof( StringEncoder ).ToString( )+ "\n\rDecode()"+ "\n\rWith = " + "iso-8859-1"/*Strings._CONFIG_STD_Email_DefaultCharacterEncoding*/ + Environment.NewLine + ex.Message ); - } - // Use defalt - if (result == null) { - result = s_Encoder.GetString( value ); - } - var index = result.IndexOf( '\0' ); - if (index >= 0) - return result.Substring( 0, result.IndexOf( '\0' ) ); - else - return result; - } - - /// - /// Use default characterSet - /// - /// - /// - /// - public static string Decode(byte[] value, int lenght) { return Decode( value, lenght, null ); } - } -} diff --git a/Common/modbus_master_csharp/Communication/ValueEncoders/StringEncoderEmail.cs b/Common/modbus_master_csharp/Communication/ValueEncoders/StringEncoderEmail.cs deleted file mode 100644 index 3d0850ce..00000000 --- a/Common/modbus_master_csharp/Communication/ValueEncoders/StringEncoderEmail.cs +++ /dev/null @@ -1,95 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace XYLEM.Communication.ValueEncoders { - public static class StringEncoderEmail { - public static Encoding GetDefaultEncoding( ) { - try { - return Encoding.GetEncoding( "iso-8859-1"/*Strings._CONFIG_STD_Email_DefaultCharacterEncoding*/ ); - } catch (Exception ex) { - throw new Exception( typeof( StringEncoderEmail ).ToString( )+ "\n\rGetDefaultEncoding"+ "\n\rWith = " + "iso-8859-1"/*Strings._CONFIG_STD_Email_DefaultCharacterEncoding*/, ex ); - } - // Legacy code return Encoding.GetEncoding( "iso-8859-1" ); - } - - - /// - /// Standard Windows iso-8859-1-encoding page. - /// - static Encoding s_Encoder = GetDefaultEncoding( ); // Default Fallback - - /// - /// - /// - /// - /// - /// ex. "iso-8859-1" - /// - public static byte[] Encode(string value, int lenght, string CharacterSet) { - if (value.Length > lenght) { - throw new ArgumentException( "value.Length is too long", "value" ); - } - var result = new byte[ lenght ]; - try { - if (!String.IsNullOrEmpty( CharacterSet )) { - Encoding.GetEncoding( CharacterSet ).GetBytes( value ).CopyTo( result, 0 ); - return result; - } - } catch (Exception ex) { - throw new Exception( typeof( StringEncoderEmail ).ToString( )+ "\n\rEncode()"+ "\n\rWith = " + "iso-8859-1"/*Strings._CONFIG_STD_Email_DefaultCharacterEncoding*/,ex ); - } - // Use defalt - s_Encoder.GetBytes( value ).CopyTo( result, 0 ); - return result; - } - - /// - /// Use default characterSet - /// - /// - /// - /// - public static byte[] Encode(string value, int lenght) { return Encode( value, lenght, null ); } - - /// - /// - /// - /// - /// - /// ex. "iso-8859-1" - /// - public static string Decode(byte[] value, int lenght, string CharacterSet) { - if (value.Length != lenght) { - throw new ArgumentException( "value.Length wrong lenght", "value" ); - } - string result = null; - try { - if (!String.IsNullOrEmpty( CharacterSet )) { - result = Encoding.GetEncoding( CharacterSet ).GetString( value ); - } - } catch (Exception ex) { - throw new Exception( typeof( StringEncoderEmail ).ToString()+ "\n\rDecode()"+ "\n\rWith = " + "iso-8859-1"/*Strings._CONFIG_STD_Email_DefaultCharacterEncoding*/, ex ); - } - // Use defalt - if (result == null) { - result = s_Encoder.GetString( value ); - } - var index = result.IndexOf( '\0' ); - if (index >= 0) { - - return result.Substring( 0, result.IndexOf( '\0' ) ); - } - return result; - } - - /// - /// Use default characterSet - /// - /// - /// - /// - public static string Decode(byte[] value, int lenght) { return Decode( value, lenght, null ); } - } -} \ No newline at end of file diff --git a/Common/modbus_master_csharp/Communication/ValueEncoders/StringEncoderEmail_Q_Types.cs b/Common/modbus_master_csharp/Communication/ValueEncoders/StringEncoderEmail_Q_Types.cs deleted file mode 100644 index 37dcb471..00000000 --- a/Common/modbus_master_csharp/Communication/ValueEncoders/StringEncoderEmail_Q_Types.cs +++ /dev/null @@ -1,175 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Linq.Expressions; -using System.Text; -using System.Text.RegularExpressions; -using System.Globalization; - -namespace XYLEM.Communication.ValueEncoders { - public static class StringEncoderEmail_Q_Types { - static string DefaultEncoding = "iso-8859-1"; - - public static Encoding GetDefaultEncoding( ) { - try { - return Encoding.GetEncoding( "iso-8859-1"/*Strings._CONFIG_STD_Email_DefaultCharacterEncoding*/ ); - } catch (Exception ex) { - throw new Exception( typeof( StringEncoderEmail ).ToString( ) + "\n\rGetDefaultEncoding" + "\n\rError when making default encoding with = " + "iso-8859-1"/*Strings._CONFIG_STD_Email_DefaultCharacterEncoding*/, ex ); - } - // Legacy code return Encoding.GetEncoding( DefaultEncoding ); - } - - #region Converting to Quoted Printable - //http://stackoverflow.com/questions/11793734/code-for-encode-decode-quotedprintable - - public static byte[] EncodeQ(byte[] value, int lenght) { - if (value.First( ) == 0) { - return value; - } - List ret = new List( ); - foreach (byte v in value) { - // The following are not required to be encoded: - // - Tab (ASCII 9) - // - Space (ASCII 32) - // - Characters 33 to 126, except for the equal sign (61). - if ((v == 9) || ((v >= 32) && (v <= 60)) || ((v >= 62) && (v <= 126))) { - if (ret.Count + 1 > lenght) { // Check for length is not over max - break; // Stop converting - } - ret.Add( (byte)Convert.ToChar( v ) ); - } else { - if (ret.Count + 3 > lenght) { // Check for length is not over max - break; // Stop converting - } - if (v == 0) { - ret.Add( 0 ); - break; // Stop terminate string - } - ret.AddRange( Encoding.UTF8.GetBytes( "=" + v.ToString( "X2" ) ) ); - } - } - char lastChar = (char)ret.Last( ); - if (char.IsWhiteSpace( lastChar )) { - ret.RemoveAt( ret.Count( ) - 1 ); - if (!(ret.Count + 3 > lenght)) { // Check for length is not over max - ret.AddRange( Encoding.UTF8.GetBytes( "=" + ((byte)lastChar).ToString( "X2" ) ) ); - } - } - if (ret.Count < lenght) { // Make sure length is correct - ret.AddRange( new byte[ lenght - ret.Count ] ); - } - return ret.ToArray( ); - } - - public static string DecodeQ(byte[] inputBytes, string charSet) { - var input = (new UTF8Encoding( )).GetString( inputBytes ); - Encoding enc; - try { - enc = Encoding.GetEncoding( charSet ); - } catch { - enc = new UTF8Encoding( ); - } - var occurences = new Regex( @"(=[0-9A-Z]{2}){1,}", RegexOptions.Multiline ); - var matches = occurences.Matches( input ); - - foreach (Match match in matches) { - try { - byte[] b = new byte[ match.Groups[ 0 ].Value.Length / 3 ]; - for (int i = 0; i < match.Groups[ 0 ].Value.Length / 3; i++) { - b[ i ] = byte.Parse( match.Groups[ 0 ].Value.Substring( i * 3 + 1, 2 ), System.Globalization.NumberStyles.AllowHexSpecifier ); - } - char[] hexChar = enc.GetChars( b ); - input = input.Replace( match.Groups[ 0 ].Value, new String( hexChar ) ); - } catch { ;} - } - input = input.Replace( "?=", "" ).Replace( "\r\n", "" ); - return input; - } - #endregion - - /// - /// Standard Windows iso-8859-1-encoding page. - /// - static Encoding s_Encoder = GetDefaultEncoding( ); // Default Fallback - - - /// - /// - /// - /// - /// - /// ex. "iso-8859-1" - /// - public static byte[] Encode(string value, int lenght, string CharacterSet) { - byte[] ret = null; - if (value.Length > lenght) { - throw new ArgumentException( "value.Length is too long", "value" ); - } - var result = new byte[ lenght ]; - try { - if (!String.IsNullOrEmpty( CharacterSet )) { - Encoding.GetEncoding( CharacterSet ).GetBytes( value ).CopyTo( result, 0 ); - ret = EncodeQ( result, lenght ); - } - } catch (Exception ex) { - throw new Exception( typeof( StringEncoderEmail ).ToString( )+ "\n\rEncode()" + "\n\rWith = " + "\n\riso-8859-1"/*Strings._CONFIG_STD_Email_DefaultCharacterEncoding*/, ex ); - } - if (ret == null) { - // Use defalt - s_Encoder.GetBytes( value ).CopyTo( result, 0 ); - ret = EncodeQ( result, lenght ); - } - /*#warning "!!!!!!!!!!!!!!!!! Testing !!!!!!!!!!!!!!!!!!!!!!!" - string test = new UTF8Encoding( ).GetString( ret );*/ - return ret; - } - - /// - /// Use default characterSet - /// - /// - /// - /// - public static byte[] Encode(string value, int lenght) { return Encode( value, lenght, null ); } - - /// - /// - /// - /// - /// - /// ex. "iso-8859-1" - /// - public static string Decode(byte[] value, int lenght, string CharacterSet) { - if (value.Length != lenght) { - throw new ArgumentException( "value.Length wrong lenght", "value" ); - } - /*#warning "!!!!!!!!!!!!!!!!! Testing !!!!!!!!!!!!!!!!!!!!!!!" - string test = new UTF8Encoding( ).GetString( value );*/ - string result = null; - try { - if (!String.IsNullOrEmpty( CharacterSet )) { - result = DecodeQ( value, CharacterSet ); - } - } catch (Exception ex) { - throw new Exception( typeof( StringEncoderEmail ).ToString() + "\n\rDecode()" + "\n\rWith = " + "iso-8859-1"/*Strings._CONFIG_STD_Email_DefaultCharacterEncoding*/, ex ); - } - // Use defalt - if (result == null) { - result = DecodeQ( value, DefaultEncoding ); - } - var index = result.IndexOf( '\0' ); - if (index >= 0) { - return result.Substring( 0, result.IndexOf( '\0' ) ); - } - return result; - } - - /// - /// Use default characterSet - /// - /// - /// - /// - public static string Decode(byte[] value, int lenght) { return Decode( value, lenght, null ); } - } -} \ No newline at end of file diff --git a/Common/modbus_master_csharp/Communication/ValueEncoders/StringEncoderUnicode.cs b/Common/modbus_master_csharp/Communication/ValueEncoders/StringEncoderUnicode.cs deleted file mode 100644 index 0ac0ec5d..00000000 --- a/Common/modbus_master_csharp/Communication/ValueEncoders/StringEncoderUnicode.cs +++ /dev/null @@ -1,41 +0,0 @@ -using System; -using System.Text; -using XYLEM.Communication; - -namespace XYLEM.Communication.ValueEncoders -{ - public static class StringEncoderUnicode - { - /// - /// Standard Windows 1252-encoding page. - /// - static Encoding s_Unicode = Encoding.BigEndianUnicode; - - public static byte[] Encode(string value, int lenght) - { - if (value.Length > lenght) - { - throw new ArgumentException("value.Length is too long", "value"); - } - - var result = new byte[lenght]; - s_Unicode.GetBytes(value).CopyTo(result, 0); - return result; - } - - public static string Decode(byte[] value, int lenght) - { - if (value.Length > lenght) - { - throw new ArgumentException("value.Length wrong lenght", "value"); - } - - var result = s_Unicode.GetString(value); - var index = result.IndexOf('\0'); - if (index >= 0) - return result.Substring(0, result.IndexOf('\0')); - else - return result; - } - } -} diff --git a/Common/modbus_master_csharp/Communication/ValueEncoders/StringEncoder_W1252.cs b/Common/modbus_master_csharp/Communication/ValueEncoders/StringEncoder_W1252.cs deleted file mode 100644 index 25f0a58f..00000000 --- a/Common/modbus_master_csharp/Communication/ValueEncoders/StringEncoder_W1252.cs +++ /dev/null @@ -1,39 +0,0 @@ -using System; -using System.Text; -using XYLEM.Communication; - -namespace XYLEM.Communication.ValueEncoders { - public static class StringEncoder_W1252 { - public static Encoding GetDefaultEncoding( ) { - return Encoding.GetEncoding( 1252 ); - } - - /// - /// Standard Windows 1252-encoding page. - /// - static Encoding s_Win1252 = GetDefaultEncoding( ); - - public static byte[] Encode(string value, int lenght) { - if (value.Length > lenght) { - throw new ArgumentException( "value.Length is too long", "value" ); - } - - var result = new byte[ lenght ]; - s_Win1252.GetBytes( value ).CopyTo( result, 0 ); - return result; - } - - public static string Decode(byte[] value, int lenght) { - if (value.Length > lenght) { - throw new ArgumentException( "value.Length wrong lenght", "value" ); - } - - var result = s_Win1252.GetString( value ); - var index = result.IndexOf( '\0' ); - if (index >= 0) - return result.Substring( 0, result.IndexOf( '\0' ) ); - else - return result; - } - } -} diff --git a/Common/modbus_master_csharp/Communication/ValueEncoders/U32_IP_Encoder.cs b/Common/modbus_master_csharp/Communication/ValueEncoders/U32_IP_Encoder.cs deleted file mode 100644 index 2f1770ea..00000000 --- a/Common/modbus_master_csharp/Communication/ValueEncoders/U32_IP_Encoder.cs +++ /dev/null @@ -1,69 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using XYLEM.Base; - -namespace XYLEM.Communication.ValueEncoders { - /// - /// IP Address encoder when it has to be converted to a u32 value - /// - internal static class U32_IP_Encoder { - /// - /// Check if string is okay and possible to encode - /// - /// - /// - internal static bool IsOkay(string stringForCheck) { - try { - if (string.IsNullOrEmpty( stringForCheck )) { - return false; // if string empty, then this is off and there is nothing to check - } - try { - var test = Encode( stringForCheck ); - } catch { - return false; // Error is not possible to convert - } - return true; // Okay to convert - } catch (Exception ex) { - throw new Exception( ex.ToFuncErrorText( typeof( U32_IP_Encoder ).ToString( ), "IsOkay" ) ); - } - } - - /// - /// Encode xxx.xxx.xxx.xxx to bytes - /// - /// - /// - static internal byte[] Encode(string ip_XXX_XXX_XXX_XXX) { - try { - var splited = ip_XXX_XXX_XXX_XXX.Split( '.' ); - if (splited.Count( ) != 4) { - throw new Exception( "IP must be formated xxx.xxx.xxx.xxx but is " + ip_XXX_XXX_XXX_XXX.ToString( ) ); - } - var values = splited.Select( value => byte.Parse( value ) ); - return values.ToArray( ); - } catch (Exception ex) { - throw new Exception( ex.ToFuncErrorText( typeof( U32_IP_Encoder ).ToString( ), "Encode" ) ); - } - } - - /// - /// decode bytes - /// - /// - /// xxx.xxx.xxx.xxx - static internal string Decode(byte[] data) { - try { - if ((data == null) || (data.Count( ) != 4)) { - throw new Exception( "Must be 4 bytes" ); - } - return String.Format( "{0}.{1}.{2}.{3}", data[ 0 ], data[ 1 ], data[ 2 ], data[ 3 ] ); - } catch (Exception ex) { - throw new Exception( ex.ToFuncErrorText( typeof( U32_IP_Encoder ).ToString( ), "Decode" ) ); - } - } - - } -} diff --git a/Common/modbus_master_csharp/Device/CalibrationPoint.cs b/Common/modbus_master_csharp/Device/CalibrationPoint.cs deleted file mode 100644 index 8048e942..00000000 --- a/Common/modbus_master_csharp/Device/CalibrationPoint.cs +++ /dev/null @@ -1,121 +0,0 @@ -using System; -using System.Xml; - -namespace XYLEM.Device -{ - /// - /// A single calibration point - /// - [Serializable] - public class CalibrationPoint : IComparable, IEquatable - { - /// - /// Reference flow in liter / second - /// https://xyleminc.atlassian.net/wiki/spaces/MJKMF/pages/6661802342 - /// - public Single ReferenceFlowRate_lps { get; set; } - - /// - /// DUT Reported flow in liter / second - /// https://xyleminc.atlassian.net/wiki/spaces/MJKMF/pages/6661802342 - /// - public Single ReportedFlowRate_lps { get; set; } - - public CalibrationPoint() - { - ReferenceFlowRate_lps = ReportedFlowRate_lps = 0; - } - - /// - /// Ctor - /// - /// reference flow rate [l/s] given by the flow-test-bench - /// reported flow rate [l/s] by the DUT - public CalibrationPoint(Single referenceFlowRate_lps, Single reportedFlowRate_lps) - { - ReferenceFlowRate_lps = referenceFlowRate_lps; - ReportedFlowRate_lps = reportedFlowRate_lps; - } - - public override string ToString() - { - const int w1 = 11; - return $"Ref={ReferenceFlowRate_lps*3.6,w1:F6} m3/h DUT={ReportedFlowRate_lps*3.6,w1:F6} m3/h " + - $"(Ref={ReferenceFlowRate_lps,w1:F6} l/s DUT={ReportedFlowRate_lps,w1:F6} l/s)"; - } - - public int CompareTo(CalibrationPoint other) - { - // If null, it can't be the same - if (System.Object.ReferenceEquals(other, null)) - { - return -1; - } - // Any of them is not okay - if ((this.ReferenceFlowRate_lps!= other.ReferenceFlowRate_lps) || (this.ReportedFlowRate_lps!= other.ReportedFlowRate_lps)) - { - return -1; // Not the same - } - return 0; // is the same - } - - public bool Equals(CalibrationPoint other) - { - return this.CompareTo(other) == 0; - } - - #region Operator == > < ! .. - public static bool operator ==(CalibrationPoint a, CalibrationPoint b) - { - // If both are null, or both are same instance, return true. - if (System.Object.ReferenceEquals(a, b)) - { - return true; - } - - // If one is null, but not both, return false. - // from https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/statements-expressions-operators/how-to-define-value-equality-for-a-type?redirectedfrom=MSDN - if (a is null) - { - // null == null = true. - if (b is null) - { - return true; - } - // Only the left side is null. - return false; - } - - // Return true if the fields match: - return (a.Equals(b)); - } - - public static bool operator !=(CalibrationPoint a, CalibrationPoint b) - { - return !(a == b); - } - - public static bool operator <(CalibrationPoint a, CalibrationPoint b) - { - return a.CompareTo(b) < 0; - } - - public static bool operator >(CalibrationPoint a, CalibrationPoint b) - { - return a.CompareTo(b) > 0; - } - - public static bool operator >=(CalibrationPoint a, CalibrationPoint b) - { - return a.CompareTo(b) >= 0; - } - - public static bool operator <=(CalibrationPoint a, CalibrationPoint b) - { - return a.CompareTo(b) <= 0; - } - - #endregion - } - -} \ No newline at end of file diff --git a/Common/modbus_master_csharp/Device/CalibrationPoints.cs b/Common/modbus_master_csharp/Device/CalibrationPoints.cs deleted file mode 100644 index fa33eb6e..00000000 --- a/Common/modbus_master_csharp/Device/CalibrationPoints.cs +++ /dev/null @@ -1,198 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Runtime.CompilerServices; -using System.Threading; -using System.Xml; -using XYLEM.Base.XML; - -namespace XYLEM.Device -{ - [Serializable] - public class CalibrationPoints : IComparable, IEquatable - { - /// - /// calibrations to or from DUT - /// - public List calibrations { get; private set; } - - public void SaveToFile(string filePath) - { - XMLWriteFuncClass.WriteToXmlFile(filePath, calibrations, false); - } - - /// - /// - /// - /// - /// Is a valid calibration for DUT - public bool LoadFromFile(string filePath) - { - var pointsSaved = XMLWriteFuncClass.ReadFromXmlFile>(filePath); - calibrations = pointsSaved; - return IsOkay(); - } - - /// - /// Constructor for A empty list of calibrations, to use for loading calibrations from file - /// - public CalibrationPoints() - { - calibrations = new List(); - } - - - /// - /// - /// - /// - /// Maximum numbers of calibration able to write to device - public CalibrationPoints(List cal_points, int maxTotalPoints) - { - if (cal_points == null) - { - throw new ArgumentNullException("Calibrations points can't be zero"); - } - if (cal_points.Count > maxTotalPoints) - { - throw new Exception($"Has {cal_points.Count} calibrations points and only support {maxTotalPoints}"); - } - // Pad with zero if not the full size. Done to make a working writing and comparing two calibrations. - if (cal_points.Count < maxTotalPoints) - { - var missing = maxTotalPoints-cal_points.Count; - cal_points.AddRange(Enumerable.Range(0, missing).Select(_ => new CalibrationPoint(0, 0))); - } - calibrations = cal_points; - } - - /// - /// Is a valid calibration to or from DUT - /// - /// - public Boolean IsOkay() - { - if ((null == calibrations) || (0 > calibrations.Count)) - { - return false; // Not okay - } - return true; // Is okay - } - - /// - /// Number of calibrations to or from DUT - /// - /// is 0 or more if okay and -1 on error / not okay - public int Get_number_of_calibrations() - { - if (!IsOkay()) - { - return -1; - } - return calibrations.Count; - } - - public override string ToString() - { - if (!IsOkay()) - { - return "No valide calibration to print out"; - } - var ret_txt = ""; - var idx = 0; - foreach (var cal in calibrations) - { - ret_txt += $"{idx++}: {cal}\n"; - } - return ret_txt; - } - - public int CompareTo(CalibrationPoints other) - { - // If null, it can't be the same - if (System.Object.ReferenceEquals(other, null)) - { - return -1; - } - - // Any of them is not okay - if (!this.IsOkay() || !other.IsOkay()) - { - return -1; // Not the same - } - // if number of items is not the same - if (this.Get_number_of_calibrations() != other.Get_number_of_calibrations()) - { - return -1; // Not the same - } - var num_of_cals = this.calibrations.Count; - var my_cals = this.calibrations; - var others_cals = other.calibrations; - for (int i = 0; i < num_of_cals; i++) - { - if (my_cals[i] != others_cals[i]) - { - return -1; // Not the same - } - } - return 0; // is the same - } - - public bool Equals(CalibrationPoints other) - { - return this.CompareTo(other) == 0; - } - - #region Operator == > < ! .. - public static bool operator ==(CalibrationPoints a, CalibrationPoints b) - { - // If both are null, or both are same instance, return true. - if (System.Object.ReferenceEquals(a, b)) - { - return true; - } - - // If one is null, but not both, return false. - // from https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/statements-expressions-operators/how-to-define-value-equality-for-a-type?redirectedfrom=MSDN - if (a is null) - { - // null == null = true. - if (b is null) - { - return true; - } - // Only the left side is null. - return false; - } - - // Return true if the fields match: - return (a.Equals(b)); - } - - public static bool operator !=(CalibrationPoints a, CalibrationPoints b) - { - return !(a == b); - } - - public static bool operator <(CalibrationPoints a, CalibrationPoints b) - { - return a.CompareTo(b) < 0; - } - - public static bool operator >(CalibrationPoints a, CalibrationPoints b) - { - return a.CompareTo(b) > 0; - } - - public static bool operator >=(CalibrationPoints a, CalibrationPoints b) - { - return a.CompareTo(b) >= 0; - } - - public static bool operator <=(CalibrationPoints a, CalibrationPoints b) - { - return a.CompareTo(b) <= 0; - } - #endregion - } -} \ No newline at end of file diff --git a/Common/modbus_master_csharp/Device/IMagFluxRequestProtocol.cs b/Common/modbus_master_csharp/Device/IMagFluxRequestProtocol.cs deleted file mode 100644 index 0e91b68b..00000000 --- a/Common/modbus_master_csharp/Device/IMagFluxRequestProtocol.cs +++ /dev/null @@ -1,171 +0,0 @@ -using MagFlux6200_metrology_reg_list; -using XYLEM.Communication; -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO.Ports; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace XYLEM.Device -{ - /// - /// Command interface for MagFlux 6200 - /// https://xyleminc.atlassian.net/wiki/spaces/MJKMF/pages/7112855120/Command+Interface+Draft - /// - public interface IMagFluxRequestProtocol - { - /// - /// Is connected - /// - /// true if yes - Boolean IsConnected(); - - /// - /// Make communication connection to Meter - /// !!NOTE!! Only one meter is supported for each com port - /// - /// Like "COM1" - /// true on okay or false on error - Boolean Connect(string comport); - - /// - /// Close communication connection to Meter - /// - /// true on okay or false on error - Boolean CloseConnection(); - - /// - /// Get printout of communication counters - /// - /// - string GetComCounters(); - - /// - /// Set location for where log data can saved. - /// - /// path for where to log file should be saved - /// set a custom log file name without extension (ex. "UserCustomLogFile") or null if use default - /// true on okay or false on error - Boolean SetLogLocation(String path, string filename = null); - - /// - /// Open saved log file - /// - void OpenLogFile(); - - /// - /// MagFlux Sensor serial number - /// - /// a decimal serial number ex. 12345678 or NULL at error - /// true on okay or false on error - Boolean GetSensorSerialNo(out string value_out); - - - /// - /// Set MagFlux Sensor serial number - /// - /// - /// true on okay or false on error - Boolean SetSensorSerialNo(UInt32 new_serial_number); - - /// - /// Unique-ID for the MagFlux electronics - /// - /// a hex decimal string 0x1234ABCDEF or NULL at error - /// true on okay or false on error - Boolean GetUniqueId(out string value_out); - - /// - /// Get the firmware version - /// - /// like 1.2.4 (MAJOR.MINOR.REVISION) - /// true on okay or false on error - Boolean GetFirmwareVersion(out string value_out); - - /// - /// Get the firmware build date - /// - /// like 2022/12/24 13:00:00 or 2022-12-24 13:45:10 - /// true on okay or false on error - Boolean GetFwBuildDate(out string value_out); - - /// - /// Git Hash to Unique identify firmware - /// - /// like 0xABCDE123 - /// true on okay or false on error - Boolean GetFwGitHash(out string value_out); - - /// - /// Flow rate calibrated - /// - /// Actual flow rate [l/s] - /// true on okay or false on error - Boolean GetFlowRate_lps(out Single value_out); - - /// - /// MagFlux Sensor nominal DN size in millimeters - /// - /// DN size in [mm] is -1 on error - /// true on okay or false on error - Boolean GetDn_mm(out Int32 dn_mm_out); - - /// - /// Set Sensor nominal DN size in millimeters - /// - /// DN size in [mm] ex 50 is 50mm or DN50 - /// true on okay or false on error - Boolean SetDn_mm(UInt16 new_dn_mm); - - /// - /// Check if there is problem with the DUT - /// use " to get a list of error messages for debug problems - /// - /// >true if device operates correctly - Boolean GetDeviceHealthy(); - - /// - /// get a list of error messages for debug problem - /// - /// Will return a empty list if there is no messages to return - /// true on okay or false on error - Boolean GetDeviceErrorMessages(out List status_list_out); - - /// - /// This function prepares the DUT for the calibration. - /// - /// ATTENTION: - /// This function needs to be called before the calibration process starts any flow of water. - /// - /// true on okay or false on error - Boolean PrepareDeviceForCalibration(); - - /// - /// This function will abort started or failed calibration to bring DUT back to normal calibration state - /// - /// true on okay or false on error - Boolean AbortDeviceForCalibration(); - - /// - /// The maximum calibration points that is supported by the device - /// - int CalibrationPointsMax { get; } - - /// - /// Get list of calibration points saved in DUT - /// - /// The calibrations points from device - /// true on okay or false on error - Boolean GetCalibrationPoints(out CalibrationPoints values_read); - - /// - /// set calibration in DUT. - /// Remember to run before the calibration process is started. - /// - /// A list of calibration points to write to DUT - /// true on okay or false on error - Boolean SetCalibrationPoints(CalibrationPoints new_calibration_points); - } -} diff --git a/Common/modbus_master_csharp/Device/MagFlux6200.cs b/Common/modbus_master_csharp/Device/MagFlux6200.cs deleted file mode 100644 index f5ae46ea..00000000 --- a/Common/modbus_master_csharp/Device/MagFlux6200.cs +++ /dev/null @@ -1,1190 +0,0 @@ -using MagFlux6200_metrology_reg_list; -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO.Ports; -using System.Linq; -using System.Numerics; -using System.Runtime.CompilerServices; -using System.Threading; -using System.Threading.Tasks; -using XYLEM.Base; -using XYLEM.Base.Files; -using XYLEM.Communication; - -namespace XYLEM.Device -{ - public enum RUN_TYPE - { - /// - /// Make real connections to a device - /// - Normal, - - /// - /// Mock that just return static values and need not _serial connection or connected MagFlux hardware - /// - Mock - } - - /// - /// MagFlux 6200 "Metrology" device API. - /// - /// Handling serial communication with a single device during sensor Calibration and verification. - /// - /// Note: All Modbus register used needs to be documented in confluence, to show Modbus dependency and insure future support and comparability. - /// Confluence page https://xyleminc.atlassian.net/wiki/spaces/MJKMF/pages/7337054026/Modbus+register+-+Used+for+Sensor+calibration+and+verification+interface - /// - public class MagFlux6200 : ModbusDeviceCom, IMagFluxRequestProtocol - { - RUN_TYPE _runAs = RUN_TYPE.Normal; - - public MagFlux6200(RUN_TYPE runAs = RUN_TYPE.Normal) : base() - { - this._runAs = runAs; - } - - public Boolean Connect(string comport) - { - try - { - /*metrology RS485 when transparent to metrology*/ - int baudRate = 9600; - Parity parity = Parity.Even; - // Is just running as a Mock - if (_runAs == RUN_TYPE.Mock) - { - return true; // is okay - } - var result = Connect(comport, baudRate, parity); - return result; - } - catch (Exception ex) - { - _logger.AddFuncLog(true, this.ToString(), _logger.GetCurrentMethod(), "Fail to open connection", ex); - } - return false; // failed - } - - public override Boolean Connect(string comport, int baudRate, Parity parity) - { - try - { - // Is just running as a Mock - if (_runAs == RUN_TYPE.Mock) - { - return true; // is okay - } - {// Normal - if (IsConnected()) - { - CloseConnection(); - } - var isOkay = base.Connect(comport, baudRate, parity); - if (isOkay) - { - isOkay = Get_MdbsRegsVer(out var device_reglist_ver); - var program_reglist_ver = (6200000 + reg_list.Met_U16DeviceType.Version); - if (!isOkay) - { - var message = $"Device Modbus register list version is {(isOkay ? device_reglist_ver : "!Read failed!")}"; - Console.WriteLine(message); - _logger.AddFuncLog(true, this.ToString(), _logger.GetCurrentMethod(), message); - } - else - { // Check register list version - var version = int.Parse(device_reglist_ver); - if (program_reglist_ver != version) - { - // Warning to user that the register version is not the same as MagFlux has - var message = $"Warning communicate!\nProgram use {program_reglist_ver} and may not supporting MagFlux {version}!"; - Console.WriteLine(message); - _logger.AddFuncLog(true, this.ToString(), _logger.GetCurrentMethod(), message); - isOkay = true; - } - else - { - Console.WriteLine($"Communication with Modbus register list ver. {program_reglist_ver}"); - } - } - if (!isOkay) - { - CloseConnection(); - } - else - { - StartBackgroundChecking(); - } - } - return isOkay; // return result - } - } - catch (Exception ex) - { - _logger.AddFuncLog(true, this.ToString(), _logger.GetCurrentMethod(), "Fail to open connection", ex); - } - return false; // failed - } - - #region Background service - /// - /// Device Healthy status that is periodically updated in background task - /// - private bool? _DeviceHealthy; - - /// - /// List with info about what errors is detected - /// - private List _DeviceHealthyMsgList = new List(); - - private UInt16 _Met_U16EmptyPipeStatus = 0; - private UInt32 _Met_U32Errors = 0; - private UInt16 _Met_U16Flow_CalibStatus = 0; - private int _HandlerBackgroundReadStageNow = 0; - - /// - /// Handle checking DUT and background reading device values - /// - /// - protected override void RunBackgroundReadServiceFunction() - { - try - { - // Is just running as a Mock - //if (true) - if (_runAs == RUN_TYPE.Mock) - { - _DeviceHealthy = true; // is okay - } - else - {// Normal - // Run one value for every background task call - switch (_HandlerBackgroundReadStageNow++) - { - case 0: - { // Read errors - var valueInfo = reg_list.Met_U32Errors; - try - { - var value = read_U32(valueInfo); - // Is okay - if (value != null) - { - _Met_U32Errors = value.Value; - } - } - catch (Exception ex) - { - _logger.AddFuncLog(true, this.ToString(), _logger.GetCurrentMethod(valueInfo.Name), "Fail to read value", ex); - _Met_U32Errors = UInt32.MaxValue; // ERROR: Did not receive a value - } - } - break; - case 1: - { // Read Empty pipe - var valueInfo = reg_list.Met_U16EmptyPipeStatus; - try - { - var value = read_U16(valueInfo); - // Is okay - if (value != null) - { - _Met_U16EmptyPipeStatus = value.Value; - } - } - catch (Exception ex) - { - _logger.AddFuncLog(true, this.ToString(), _logger.GetCurrentMethod(valueInfo.Name), "Fail to read value", ex); - _Met_U16EmptyPipeStatus = UInt16.MaxValue; // ERROR: Did not receive a value - } - } - break; - case 2: - { // Read calibration state - var valueInfo = reg_list.Met_U16Flow_CalibStatus; - try - { - var value = read_U16(valueInfo); - // Is okay - if (value != null) - { - _Met_U16Flow_CalibStatus = value.Value; - } - } - catch (Exception ex) - { - _logger.AddFuncLog(true, this.ToString(), _logger.GetCurrentMethod(valueInfo.Name), "Fail to read value", ex); - _Met_U16Flow_CalibStatus = UInt16.MaxValue; // ERROR: Did not receive a value - } - } - break; - default: - // Return to start - _HandlerBackgroundReadStageNow=0; - break; - - } - UpdateDeviceHealthy(); - } - } - catch (Exception ex) - { -#if (DEBUG) - var message = $"Fail background service handling"; - Console.WriteLine(message); - AppConst.Logger.AddFuncLog(true, this.ToString(), _logger.GetCurrentMethod(), message, ex); -#endif // #if (DEBUG) - _DeviceHealthy = false; // ERROR: Did failing updating value, then just put it as not Healthy - _DeviceHealthyMsgList = new List() - { - "Unknown failing for updating device healthy" - }; - } - } - - private void UpdateDeviceHealthy() - { - try - { - var newHealthy = true; - var newMsgList = new List(); - { // Check Errors - if (_Met_U32Errors != 0) - { - newHealthy = false; - if (_Met_U32Errors == UInt32.MaxValue) - { - newMsgList.Add("System error has unknown error"); - } - else - { - newMsgList.Add($"System error 0x{_Met_U32Errors:X}"); - } - } - } - { // Check empty pipe - // https://xyleminc.atlassian.net/wiki/spaces/MJKMF/pages/6659966841/Empty+Pipe+Detection+EPD#Empty-pipe-status - if ((_Met_U16EmptyPipeStatus&1) != 0) // If empty pipe active - { - newHealthy = false; - // Bit for empty pipe used for show pipe level. - switch ((_Met_U16EmptyPipeStatus >> 1) & 7) - { - case 0x01: // Below empty - newMsgList.Add("Pipe Empty"); - break; - case 0x02: // Between empty & full - newMsgList.Add("Pipe Below Half"); - break; - case 0x04: // Above full - newMsgList.Add("Pipe Above full"); - break; - default: - if (_Met_U16EmptyPipeStatus == UInt16.MaxValue) - { - newMsgList.Add("Pipe Empty has unknown error"); - } - break; - } - } - } - { // Check Calibration status - var calstatus = GetCalErrStatus(_Met_U16Flow_CalibStatus); - if (calstatus != CalibrationErrorStatus.AllOK) - { - newHealthy = false; - newMsgList.Add(GetCalErrText(calstatus)); - } - } - // Update to the new status - _DeviceHealthy = newHealthy; - _DeviceHealthyMsgList = newMsgList; - } - catch (Exception ex) - { - _logger.AddFuncLog(true, this.ToString(), _logger.GetCurrentMethod(), "Fail updating device healthy", ex); - _DeviceHealthy = false; // ERROR: indicate error - } - } - #endregion // Background service - - class CalPointsRegInfo - { - internal Device_value Reference { get; } - internal Device_value Reported { get; } - - public CalPointsRegInfo(Device_value reference, Device_value reported) - { - Reference = reference; - Reported = reported; - } - - public override string ToString() - { - return $"Cal point ref={Reference.Name} rep={Reported.Name}"; - } - - } - - List NewCalRegInfoList = new List { - new CalPointsRegInfo(reg_list.Met_F32Flow_NewReferencePt0_lps, reg_list.Met_F32Flow_NewReportedPt0_lps), - new CalPointsRegInfo(reg_list.Met_F32Flow_NewReferencePt1_lps, reg_list.Met_F32Flow_NewReportedPt1_lps), - new CalPointsRegInfo(reg_list.Met_F32Flow_NewReferencePt2_lps, reg_list.Met_F32Flow_NewReportedPt2_lps), - new CalPointsRegInfo(reg_list.Met_F32Flow_NewReferencePt3_lps, reg_list.Met_F32Flow_NewReportedPt3_lps), - new CalPointsRegInfo(reg_list.Met_F32Flow_NewReferencePt4_lps, reg_list.Met_F32Flow_NewReportedPt4_lps), - new CalPointsRegInfo(reg_list.Met_F32Flow_NewReferencePt5_lps, reg_list.Met_F32Flow_NewReportedPt5_lps), - }; - - /// - /// Present accepted Calibration (Note is read only) - /// - List PresentCalRegInfoList = new List { - new CalPointsRegInfo(reg_list.Met_F32FlowPresentPoint0_reference_lps, reg_list.Met_F32FlowPresentPoint0_reported_lps), - new CalPointsRegInfo(reg_list.Met_F32FlowPresentPoint1_reference_lps, reg_list.Met_F32FlowPresentPoint1_reported_lps), - new CalPointsRegInfo(reg_list.Met_F32FlowPresentPoint2_reference_lps, reg_list.Met_F32FlowPresentPoint2_reported_lps), - new CalPointsRegInfo(reg_list.Met_F32FlowPresentPoint3_reference_lps, reg_list.Met_F32FlowPresentPoint3_reported_lps), - new CalPointsRegInfo(reg_list.Met_F32FlowPresentPoint4_reference_lps, reg_list.Met_F32FlowPresentPoint4_reported_lps), - new CalPointsRegInfo(reg_list.Met_F32FlowPresentPoint5_reference_lps, reg_list.Met_F32FlowPresentPoint5_reported_lps), - }; - - public int CalibrationPointsMax { get { return NewCalRegInfoList.Count; } } - - - #region Calibration Error flags - /// - /// Calibration Error flags - /// https://xyleminc.atlassian.net/wiki/spaces/MJKMF/pages/6661802342/Calibration+guide#Calibration-Status-Register - /// - [Flags] - enum CalibrationErrorStatus - { - /// - /// Bits 0000: All OK - /// - AllOK = 0, - - /// - /// Bits 0001: Bad point - /// - ErrorInPoint = 0b0001, - - /// - /// Bits 0010: Bad gradient - /// - ErrorInGradient = 0b0010, - - /// - /// Bits 1111: Bad other - /// - GeneralError = 0b1111, - - /// - /// Haven't any knowledge on it you - /// - UnkownError = 0xFFFF, - } - - /// - /// Get the cal error status from Calibration status register - /// https://xyleminc.atlassian.net/wiki/spaces/MJKMF/pages/6661802342/Calibration+guide#Calibration-Status-Register - /// - /// Read from "Met_U16Flow_CalibCtrl" - /// - CalibrationErrorStatus GetCalErrStatus(UInt16 CalibStatus) - { - var status = (CalibStatus&0xF00)>>8; - try - { - return (CalibrationErrorStatus)status; - } - catch (Exception ex) - { -#if (DEBUG) - var message = $"Fail to convert {status} to {typeof(CalibrationErrorStatus)}"; - AppConst.Logger.AddFuncLog(true, this.ToString(), _logger.GetCurrentMethod(), message, ex); -#endif // #if (DEBUG) - return CalibrationErrorStatus.UnkownError; - } - - } - - static string AppendErrorTxt(ref string to, string append) - { - to = $"{to}[{append}]"; - return to; - } - - /// - /// Convert the calibration error to a string - /// - /// - /// - string GetCalErrText(CalibrationErrorStatus status) - { - var ret = ""; - switch (status) - { - case CalibrationErrorStatus.AllOK: - AppendErrorTxt(ref ret, "All Cal OK"); - break; - case CalibrationErrorStatus.ErrorInPoint: - AppendErrorTxt(ref ret, "Error in point"); - break; - case CalibrationErrorStatus.ErrorInGradient: - AppendErrorTxt(ref ret, "Error in gradient"); - break; - case CalibrationErrorStatus.UnkownError: - AppendErrorTxt(ref ret, "Unknown error"); - break; - default: - AppendErrorTxt(ref ret, $"Unknown error 0x{status:X}"); - break; - } - return ret; - } - #endregion //#region Calibration Error flags - - /// - /// Calibration state - /// https://xyleminc.atlassian.net/wiki/spaces/MJKMF/pages/6661802342/Calibration+guide#Calibration-Status-Register - /// - enum CalibrationState - { - Normal = 0, - CalibrationMode = 1, - UnkownError, - } - - /// - /// Convert the Calibration State to a string - /// - /// - /// - string GetCalStateText(CalibrationState state) - { - var ret = ""; - switch (state) - { - case CalibrationState.Normal: - AppendErrorTxt(ref ret, "Normal"); - break; - case CalibrationState.CalibrationMode: - AppendErrorTxt(ref ret, "Calibration Mode"); - break; - case CalibrationState.UnkownError: - AppendErrorTxt(ref ret, "Unknown error"); - break; - default: - AppendErrorTxt(ref ret, $"Unknown state 0x{state:X}"); - break; - } - return ret; - } - - /// - /// Get current calibration state from Calibration status register - /// https://xyleminc.atlassian.net/wiki/spaces/MJKMF/pages/6661802342/Calibration+guide#Calibration-Status-Register - /// - /// Read from "Met_U16Flow_CalibCtrl" - /// - CalibrationState GetCurrentCalState(UInt16 CalibStatus) - { - var status = (CalibStatus&0x1); - try - { - return (CalibrationState)status; - } - catch (Exception ex) - { -#if (DEBUG) - var message = $"Fail to convert {status} to {typeof(CalibrationState)}"; - AppConst.Logger.AddFuncLog(true, this.ToString(), _logger.GetCurrentMethod(), message, ex); -#endif // #if (DEBUG) - return CalibrationState.UnkownError; - } - } - - /// - /// Get current calibration state from Calibration status register - /// https://xyleminc.atlassian.net/wiki/spaces/MJKMF/pages/6661802342/Calibration+guide#Calibration-Status-Register - /// - /// Read from "Met_U16Flow_CalibCtrl" - /// - CalibrationState GetPreviousCalState(UInt16 CalibStatus) - { - var status = ((CalibStatus&0xC)>>2); - try - { - return (CalibrationState)status; - } - catch (Exception ex) - { -#if (DEBUG) - var message = $"Fail to convert {status} to {typeof(CalibrationState)}"; - AppConst.Logger.AddFuncLog(true, this.ToString(), _logger.GetCurrentMethod(), message, ex); -#endif // #if (DEBUG) - return CalibrationState.UnkownError; - } - } - - enum CalibrationCommand - - { - /// - /// 00: Unused - /// - Unused = 0b00, - - /// - /// 01: Enter - /// - Enter = 0b01, - - /// - /// 10: Confirm - /// - Confirm = 0b10, - - /// - /// 11: Abort - /// - Abort = 0b11, - - /// - /// Unknown error - /// - UnkownError = 0xFFFF - } - - /// - /// Get current calibration state from Calibration status register - /// https://xyleminc.atlassian.net/wiki/spaces/MJKMF/pages/6661802342/Calibration+guide#Calibration-Status-Register - /// - /// Read from "Met_U16Flow_CalibCtrl" - /// - CalibrationCommand GetPreviousCalCommand(UInt16 CalibStatus) - { - var status = ((CalibStatus&0x20)>>5); - try - { - return (CalibrationCommand)status; - } - catch (Exception ex) - { -#if (DEBUG) - var message = $"Fail to convert {status} to {typeof(CalibrationCommand)}"; - AppConst.Logger.AddFuncLog(true, this.ToString(), _logger.GetCurrentMethod(), message, ex); -#endif // #if (DEBUG) - return CalibrationCommand.UnkownError; - } - } - - /// - /// Set a calibration command in device - /// - /// - /// - bool SetCalCommand(CalibrationCommand command) - { - var isOkay = false; - try - { - isOkay = write_U16(reg_list.Met_U16Flow_CalibCtrl, (UInt16)command); - } - catch (Exception ex) - { -#if (DEBUG) - var message = $"Fail to write {command}"; - AppConst.Logger.AddFuncLog(true, this.ToString(), _logger.GetCurrentMethod(), message, ex); -#endif // #if (DEBUG) - } - return isOkay; - } - - public Boolean GetDeviceErrorMessages(out List status_list_out) - { - // Is just running as a Mock - if (_runAs == RUN_TYPE.Mock) - { - status_list_out = new List(); // is okay - return true; // is okay - } - {// Normal - //TODO: There is not errors to convert yet throw new System.NotImplementedException(); - status_list_out = _DeviceHealthyMsgList; // is okay - return true; // is okay - } - } - - public Boolean GetDeviceHealthy() - { - // Is just running as a Mock - if (_runAs == RUN_TYPE.Mock) - { - return true; // is okay - } - var wait_max_s = 5; - var wait_pull_ms = 10; - var start_wait_count = wait_max_s*(1000.0/wait_pull_ms); - while (_DeviceHealthy == null) - { - Thread.Sleep(wait_pull_ms); - if (start_wait_count-- <=0) - { - throw new Exception("Fail getting a updated DeviceHealthy"); - } - } - return _DeviceHealthy.Value; - } - - public Boolean GetDn_mm(out Int32 dn_mm_out) - { - // Is just running as a Mock - if (_runAs == RUN_TYPE.Mock) - { - dn_mm_out = 50; // is okay - return true; // is okay - } - {// Normal - var valueInfo = reg_list.Met_U16PipeDiameterConfig_mm; - try - { - var value = read_U16(valueInfo); - // Is okay - if (value != null) - { - dn_mm_out = (int)value.Value; - return true; // is okay - } - } - catch (Exception ex) - { - _logger.AddFuncLog(true, this.ToString(), _logger.GetCurrentMethod(valueInfo.Name), "Fail to read value", ex); - } - dn_mm_out = -1; // ERROR: Did not receive a value - return false; // failed - } - } - - public bool SetDn_mm(UInt16 new_dn_mm) - { - var isOkay = false; - try - { - isOkay = GetDn_mm(out var current); - // Only write DN if different - if(current != new_dn_mm) - { - isOkay &= write_U16(reg_list.Met_U16PipeDiameterConfig_mm, (UInt16)new_dn_mm); - } - } - catch (Exception ex) - { -#if (DEBUG) - var message = $"Fail to write DN {new_dn_mm}"; - AppConst.Logger.AddFuncLog(true, this.ToString(), _logger.GetCurrentMethod(), message, ex); -#endif // #if (DEBUG) - } - return isOkay; - } - - public Boolean GetFirmwareVersion(out string value_out) - { - // Is just running as a Mock - if (_runAs == RUN_TYPE.Mock) - { - value_out="1.2.3"; // is okay - return true; // is okay - } - {// Normal - var valueInfo = reg_list.Met_U32FirmwareVersion; - try - { - var value = read_U32(valueInfo); - // Is okay - if (value != null) - { - var GIT_VERSION_MAJOR = value>>24; - var GIT_VERSION_MINOR = (value>>16) & ((1<<16)-1); - var GIT_VERSION_REVISION = (value & ((1<<16)-1)); - value_out = $"{GIT_VERSION_MAJOR}.{GIT_VERSION_MINOR}.{GIT_VERSION_REVISION}"; - return true; // is okay - } - } - catch (Exception ex) - { - _logger.AddFuncLog(true, this.ToString(), _logger.GetCurrentMethod(valueInfo.Name), "Fail to read value", ex); - } - value_out = "?"; // ERROR: Did not receive a value - return false; // failed - } - } - - public Boolean GetFlowRate_lps(out Single value_out) - { - // Is just running as a Mock - if (_runAs == RUN_TYPE.Mock) - { - value_out = 1.2345F; // is okay - return true; // is okay - } - {// Normal - var valueInfo = reg_list.Met_F32FlowRate_lps; - try - { - var value = read_F32(valueInfo); - // Is okay - if (value != null) - { - value_out = value.Value; - return true; // is okay - } - throw new Exception("value returned was null"); - } - catch (Exception ex) - { - _logger.AddFuncLog(true, this.ToString(), _logger.GetCurrentMethod(valueInfo.Name), "Fail to read value", ex); - } - value_out = 0; // ERROR: Did not receive a value - return false; // failed - } - } - - public Boolean GetFwBuildDate(out string value_out) - { - // Is just running as a Mock - if (_runAs == RUN_TYPE.Mock) - { - value_out = TimeConvertClass.ToLocalDateTimeString(DateTime.Now); // is okay - return true; // is okay - } - {// Normal - var valueInfo = reg_list.Met_U32FmwrBuildDate; - try - { - var value = read_U32(valueInfo); - // Is okay - if (value != null) - { - int year = (int)(value.Value/1000000); - int Month = (int)((value.Value%1000000)/10000); - int day = (int)((value.Value%10000)/100); - int hour = (int)(value.Value%100); - DateTime time = new DateTime(year, Month, day, hour, 0, 0); - value_out = TimeConvertClass.ToLocalDateTimeString(time); - return true; // is okay - } - throw new Exception("value returned was null"); - } - catch (Exception ex) - { - _logger.AddFuncLog(true, this.ToString(), _logger.GetCurrentMethod(valueInfo.Name), "Fail to read value", ex); - } - value_out = "?"; // ERROR: Did not receive a value - return false; // failed - } - } - - - public Boolean Get_MdbsRegsVer(out string value_out) - { - // Is just running as a Mock - if (_runAs == RUN_TYPE.Mock) - { - value_out = (6200000 + reg_list.Met_U16DeviceType.Version).ToString(); // is okay - return true; // is okay - } - {// Normal - var valueInfo = reg_list.Met_U32MdbsRegsVer; - try - { - var value = read_U32(valueInfo); - // Is okay - if (value != null) - { - value_out = value.ToString(); - return true; // is okay - } - } - catch (Exception ex) - { - _logger.AddFuncLog(true, this.ToString(), _logger.GetCurrentMethod(valueInfo.Name), "Fail to read value", ex); - } - value_out = "?"; // ERROR: Did not receive a value - return false; // failed - } - } - - - public Boolean GetFwGitHash(out string value_out) - { - // Is just running as a Mock - if (_runAs == RUN_TYPE.Mock) - { - value_out = "0xF3573B9"; - return true; // is okay - } - {// Normal - var valueInfo = reg_list.Met_U32FmwrGITHash; - try - { - var value = read_U32(valueInfo); - // Is okay - if (value != null) - { - value_out = $"0x{value:X}"; - return true; // is okay - } - } - catch (Exception ex) - { - _logger.AddFuncLog(true, this.ToString(), _logger.GetCurrentMethod(valueInfo.Name), "Fail to read value", ex); - } - value_out = "?"; // ERROR: Did not receive a value - return false; // failed - } - } - - - public Boolean GetSensorSerialNo(out string value_out) - { - // Is just running as a Mock - if (_runAs == RUN_TYPE.Mock) - { - value_out = "0"; - return true; // is okay - } - {// Normal - var valueInfo = reg_list.Met_U32SensorSerialNo; - try - { - // TODO: is this to be converted to new format maybe like yyyy/mm/day - var value = read_U32(valueInfo); - // Is okay - if (value != null) - { - value_out = $"{value}"; - return true; // is okay - } - } - catch (Exception ex) - { - _logger.AddFuncLog(true, this.ToString(), _logger.GetCurrentMethod(valueInfo.Name), "Fail to read value", ex); - } - value_out = "?"; // ERROR: Did not receive a value - return false; // failed - } - } - - - public bool SetSensorSerialNo(UInt32 new_serial_number) - { - var isOkay = false; - try - { - isOkay = write_U32(reg_list.Met_U32SensorSerialNo, new_serial_number); - } - catch (Exception ex) - { -#if (DEBUG) - var message = $"Fail to write Sensor SN {new_serial_number}"; - AppConst.Logger.AddFuncLog(true, this.ToString(), _logger.GetCurrentMethod(), message, ex); -#endif // #if (DEBUG) - } - return isOkay; - } - - - - public Boolean GetUniqueId(out string value_out) - { - // Is just running as a Mock - if (_runAs == RUN_TYPE.Mock) - { - value_out = "0"; - return true; // is okay - } - {// Normal - var valueInfo = reg_list.Met_U128_CPU_ID; - try - { - var value = read_U128(valueInfo); - // Is okay - if (value != null) - { - value_out = $"0x{value:x}"; - return true; // is okay - } - } - catch (Exception ex) - { - _logger.AddFuncLog(true, this.ToString(), _logger.GetCurrentMethod(valueInfo.Name), "Fail to read value", ex); - } - value_out = "?"; // ERROR: Did not receive a value - return false; // failed - } - } - - #region Calibration points handling - public Boolean GetCalibrationPoints(out CalibrationPoints values_read) - { - // Is just running as a Mock - if (_runAs == RUN_TYPE.Mock) - { - values_read = new CalibrationPoints( - new List { - new CalibrationPoint(0,0), // 0 - new CalibrationPoint(1,1), // 1 - new CalibrationPoint(5,5), // 2 - }, - CalibrationPointsMax - ); - return true; // is okay - } - {// Normal - try - { - var cals = new List(); - var calP_dvis = PresentCalRegInfoList; - // ---------- Do read ---------- - var dvList = new List(); - // Add status - var u16CalStatusReg = reg_list.Met_U16Flow_CalibStatus; - dvList.Add(u16CalStatusReg); - // Add cal points - foreach (var point in calP_dvis) - { - dvList.Add(point.Reference); - dvList.Add(point.Reported); - } - var reads = read(dvList); - if ((reads == null) || (reads.Count == 0)) - { - throw new Exception("No read data returned"); - } - // ---------- Handle read respond ---------- - // Check that calibration needs to be in normal mode and accepted before getting the calibration. - var u16CalStatusValue = reads.Find(data => data.Info == u16CalStatusReg); - if ((u16CalStatusValue != null) && (u16CalStatusValue != null)) - { - var calstatus = GetCalErrStatus((UInt16)u16CalStatusValue.Data); - if (calstatus != CalibrationErrorStatus.AllOK) - { - throw new Exception($"Calibration status must be {GetCalErrText(CalibrationErrorStatus.AllOK)} and not {GetCalErrText(calstatus)}"); - } - var calState = GetCurrentCalState((UInt16)u16CalStatusValue.Data); - if (calState != CalibrationState.Normal) - { - throw new Exception($"Calibration state must be {GetCalStateText(CalibrationState.Normal)} and not {GetCalStateText(calState)}"); - } - } - - // Get calibrations - foreach (var point in PresentCalRegInfoList) - { - try - { - var ref_value_lps = reads.Find(data => data.Info == point.Reference); - var rep_value_lps = reads.Find(data => data.Info == point.Reported); - // Is okay - if ((ref_value_lps != null) && (rep_value_lps != null)) - { - cals.Add(new CalibrationPoint(Convert.ToSingle(ref_value_lps.Data), Convert.ToSingle(rep_value_lps.Data))); - } - else - { - throw new Exception("value returned was null"); - } - } - catch (Exception ex) - { - _logger.AddFuncLog(true, this.ToString(), _logger.GetCurrentMethod(point.ToString()), "Fail to read values", ex); - throw new Exception($"Faild cal point read of {point} "); - } - } - values_read = new CalibrationPoints(cals, CalibrationPointsMax); - return true; // is okay - } - catch (Exception ex) - { - _logger.AddFuncLog(true, this.ToString(), _logger.GetCurrentMethod(), "Fail to read value", ex); - } - values_read = null; - return false; // failed - } - } - - public Boolean AbortDeviceForCalibration() - { - - // Is just running as a Mock - if (_runAs == RUN_TYPE.Mock) - { - return true; // is okay - } - {// Normal - try - { - // Abort any calibration that have been started and possible failed. - if (!SetCalCommand(CalibrationCommand.Abort)) - { - throw new Exception("Was not able to abort calibration"); - } - return true; // is okay - } - catch (Exception ex) - { - _logger.AddFuncLog(true, this.ToString(), _logger.GetCurrentMethod(), "Fail preparing for calibrations", ex); - } - return false; // failed - } - } - - - public Boolean PrepareDeviceForCalibration() - { - // Is just running as a Mock - if (_runAs == RUN_TYPE.Mock) - { - return true; // is okay - } - {// Normal - try - { - // Generate default calibrations points to use for calibration - var cals = new List(); - for (int i = 0; i < NewCalRegInfoList.Count; i++) - { - cals.Add(new CalibrationPoint(0, 0)); - } - var calPoints = new CalibrationPoints(cals, CalibrationPointsMax); - if (!SetCalibrationPoints(calPoints)) - { - throw new Exception("Fail writing default 0 calibration to DUT"); - } - return true; // is okay - } - catch (Exception ex) - { - _logger.AddFuncLog(true, this.ToString(), _logger.GetCurrentMethod(), "Fail preparing for calibrations", ex); - } - return false; // failed - } - } - - public Boolean SetCalibrationPoints(CalibrationPoints new_calibration_points) - { - // Is just running as a Mock - if (_runAs == RUN_TYPE.Mock) - { - return true; // is okay - } - {// Normal - try - { - if ((new_calibration_points == null) || (new_calibration_points.Get_number_of_calibrations() <= 0)) - { - throw new Exception("Calibrations is not valid to write"); - } - if (!new_calibration_points.IsOkay()) - { - var pointsTxt = "?"; - if (new_calibration_points.calibrations != null) - { - var points = new_calibration_points.calibrations.Select(point => point.ToString()); - pointsTxt = $"{String.Join("|", points)}"; - } - throw new Exception($"Calibrations to write is not okay\n {pointsTxt}"); - } - // Set calibration to default 0 before beginning. - if (!SetCalCommand(CalibrationCommand.Enter)) - { - throw new Exception("Was not able to enter calibration mode"); - } - var cals = new List(); - var calP_dvis = NewCalRegInfoList; - // Check if calibrations to write is valid - if (calP_dvis.Count() < new_calibration_points.Get_number_of_calibrations()) - { - throw new Exception($"Fail writing calibrations! Support {calP_dvis.Count()} points, given {new_calibration_points.Get_number_of_calibrations()} to write"); - } - // ---------- Do read ---------- - var reqList = new List(); - - { // Add cal points - var idx = 0; - var calPoints = new_calibration_points.calibrations; - foreach (var point in calP_dvis) - { - var calWr = new CalibrationPoint(0, 0); // Set default up, if points haven't been entered - if (idx < calPoints.Count) - { - calWr = calPoints[idx]; - } - reqList.Add(new ModbusDataRequest(false, calWr.ReferenceFlowRate_lps, point.Reference)); - reqList.Add(new ModbusDataRequest(false, calWr.ReportedFlowRate_lps, point.Reported)); - idx++; - } - } - var results = write(reqList); - if ((results == null) || (results.Count != reqList.Count)) - { - throw new Exception("Unknown Write failing of calibration"); - } - { // Check if any has failed - var failedOnes = results.Where(result => !result.IsOkay); - if (failedOnes.Count() > 0) - { - var names = failedOnes.Select(result => result.Info.Name); - throw new Exception($"Write failing of calibration {String.Join("|", names)}"); - } - } - // Finish and setting calibration in use - if (!SetCalCommand(CalibrationCommand.Confirm)) - { - throw new Exception("Was not able to enter calibration"); - } - {// Check calibrations is written correct - if (!GetCalibrationPoints(out var calRead) || (calRead == null) || (calRead.Get_number_of_calibrations() != calP_dvis.Count)) - { - throw new Exception("Failed reading default calibration written"); - } - var calPoints = new_calibration_points.calibrations; - var failedOnes = new List(); - var idx = 0; - foreach (var read in calRead.calibrations) - { - var calWr = new CalibrationPoint(0, 0); // Set default up, if points haven't been entered - if (idx < calPoints.Count) - { - calWr = calPoints[idx]; - } - if ((read.ReferenceFlowRate_lps != calWr.ReferenceFlowRate_lps) || (read.ReportedFlowRate_lps != calWr.ReportedFlowRate_lps)) - { - failedOnes.Add(calWr); - var msg = $"Write failing of calibration idx {idx}\n As {calWr}\n Was {read}"; - _logger.AddFuncLog(true, this.ToString(), _logger.GetCurrentMethod(), msg); - } - idx++; - } - if (failedOnes.Count() > 0) - { - var values = failedOnes.Select(point => point.ToString()); - throw new Exception($"Write failing of calibration {String.Join("|", values)}"); - } - } - return true; // is okay - } - catch (Exception ex) - { - _logger.AddFuncLog(true, this.ToString(), _logger.GetCurrentMethod(), "Fail writing calibrations", ex); - // Make sure that calibration is off - AbortDeviceForCalibration(); - } - return false; // failed - } - } - - #endregion //#region Calibration points - } -} \ No newline at end of file diff --git a/Common/modbus_master_csharp/Device/MagFlux6200_metrology_modbus_dependency_documentation.url b/Common/modbus_master_csharp/Device/MagFlux6200_metrology_modbus_dependency_documentation.url deleted file mode 100644 index 595941e5..00000000 --- a/Common/modbus_master_csharp/Device/MagFlux6200_metrology_modbus_dependency_documentation.url +++ /dev/null @@ -1,5 +0,0 @@ -[{000214A0-0000-0000-C000-000000000046}] -Prop3=19,11 -[InternetShortcut] -IDList= -URL=https://xyleminc.atlassian.net/wiki/spaces/MJKMF/pages/7337054026/Modbus+register+-+Used+for+Sensor+calibration+and+verification+interface diff --git a/Common/modbus_master_csharp/Device/MagFlux6200_metrology_reg_list.cs b/Common/modbus_master_csharp/Device/MagFlux6200_metrology_reg_list.cs deleted file mode 100644 index 6c750776..00000000 --- a/Common/modbus_master_csharp/Device/MagFlux6200_metrology_reg_list.cs +++ /dev/null @@ -1,1211 +0,0 @@ -/* @page Declaration for "Gluing" Metrology Modbus API in to Application -* AUTO GENERATED DON'T EDIT, IS GENERATED WITH "MagFlux6200_metrology_reg_listvX.py @ 2023_10_19__08_34" -* From: https://bitbucket.org/xyleminc/magflux_modbus_registers/src/main/Registers/ -*/ - -using XYLEM.Device.Value; -using XYLEM.Communication; -using System; - -/*---------------------------------------------------------------------------*/ - -namespace MagFlux6200_metrology_reg_list -{ - -/*---------------------------------------------------------------------------*/ - - /// - /// Value support - /// - public enum ValueRW_Type - { - ReadOnly, - WriteOnly, - ReadWrite, - // Alway last - Unknown, - }; - - /// - /// Used for containing the information for access metrology values - /// - public class Device_value - { - /// - /// @brief Version number of Metrology register list. - /// The md_master_csharp_6200_reg_list.cs files were created from this version - /// - public int Version { get { return 46; } } - - /// - /// Variable name - /// - public string Name { get; private set; } - - /// - /// Modbus address to access data on (0 - 65535) - /// - public int MD_adr { get; private set; } - - /// - /// Data type to expect data to be returned as - /// - public ValueDataType Datatype { get; private set; } - - /// - /// Read Write support - /// - public ValueRW_Type RW { get; private set; } - - /// - /// The value information - /// - public string Info { get; private set; } - - public Device_value(string name, int md_adr, ValueDataType datatype, ValueRW_Type rw, string info) - { - this.Name = name; - this.MD_adr = md_adr; - this.Datatype = datatype; - this.RW = rw; - this.Info = info; - } - - public override string ToString() - { - return Name; - } - } -/*---------------------------------------------------------------------------*/ -/*---------------------------------------------------------------------------*/ - - internal class reg_list - { - - -/*------------------------------ block number 0 ------------------------------*/ - - /// - /// @brief Device type used at MJK. Metrology = 0x003C - /// - internal static readonly Device_value Met_U16DeviceType = new Device_value("Met_U16DeviceType", 0, ValueDataType.U16, ValueRW_Type.ReadWrite, "Device type used at MJK. Metrology = 0x003C "); - - /// - /// @brief MAGFLUX NEXTGEN - MJKNXTGN == 0x4D4A4B4E5854474E, const with JTAG, big end? little end ? TBD. - /// - internal static readonly Device_value Met_U64DeviceFamily = new Device_value("Met_U64DeviceFamily", 1, ValueDataType.U64, ValueRW_Type.ReadWrite, "MAGFLUX NEXTGEN - MJKNXTGN == 0x4D4A4B4E5854474E, const with JTAG, big end? little end ? TBD."); - - /// - /// @brief Type version / model detail - A0 in hex 0x41300000 for Alpha, big end? little end ? const with JTAG, TBD - /// - internal static readonly Device_value Met_U32DeviceTypeDetail = new Device_value("Met_U32DeviceTypeDetail", 5, ValueDataType.U32, ValueRW_Type.ReadWrite, "Type version / model detail - A0 in hex 0x41300000 for Alpha, big end? little end ? const with JTAG, TBD"); - - /// - /// @brief Programmed during production. Const after production. Days since ...01.01.2020. - /// - internal static readonly Device_value Met_U32ProdDate = new Device_value("Met_U32ProdDate", 7, ValueDataType.U32, ValueRW_Type.ReadWrite, "Programmed during production. Const after production. Days since ...01.01.2020."); - - /// - /// @brief Programmed during production of entire meter assembly. Const after production. - /// - internal static readonly Device_value Met_U32ProdSerialNo = new Device_value("Met_U32ProdSerialNo", 9, ValueDataType.U32, ValueRW_Type.ReadWrite, "Programmed during production of entire meter assembly. Const after production."); - - /// - /// @brief Programmed during JTAG with firmware. Const after production. - /// - internal static readonly Device_value Met_U32FirmwareVersion = new Device_value("Met_U32FirmwareVersion", 11, ValueDataType.U32, ValueRW_Type.ReadWrite, "Programmed during JTAG with firmware. Const after production."); - - /// - /// @brief Programmed during JTAG with firmware. Const after production. - /// - internal static readonly Device_value Met_U32FmwrBuildDate = new Device_value("Met_U32FmwrBuildDate", 13, ValueDataType.U32, ValueRW_Type.ReadWrite, "Programmed during JTAG with firmware. Const after production."); - - /// - /// @brief Programmed during JTAG with firmware. Const after production. - /// - internal static readonly Device_value Met_U32FmwrGITHash = new Device_value("Met_U32FmwrGITHash", 15, ValueDataType.U32, ValueRW_Type.ReadWrite, "Programmed during JTAG with firmware. Const after production."); - - /// - /// @brief Programmed during production ? or maybe programmed during JTAG with firmware. Const after production. - /// - internal static readonly Device_value Met_U32HdwrVersion = new Device_value("Met_U32HdwrVersion", 17, ValueDataType.U32, ValueRW_Type.ReadWrite, "Programmed during production ? or maybe programmed during JTAG with firmware. Const after production."); - - /// - /// @brief Programmed during JTAG with firmware. Const after production. - /// - internal static readonly Device_value Met_U32MdbsRegsVer = new Device_value("Met_U32MdbsRegsVer", 19, ValueDataType.U32, ValueRW_Type.ReadWrite, "Programmed during JTAG with firmware. Const after production."); - - /// - /// @brief Tells you what calibration method was used. 0 is default, for calibration on flow rig for 5 min per point - /// - internal static readonly Device_value Met_U16CalibrationVersion = new Device_value("Met_U16CalibrationVersion", 21, ValueDataType.U16, ValueRW_Type.ReadWrite, "Tells you what calibration method was used. 0 is default, for calibration on flow rig for 5 min per point"); - - -/*------------------------------ block number 1 ------------------------------*/ - - /// - /// @brief CAUSES RESTART! 10s - Coil driver frequency configuration, 0 = 1.25Hz, 1 = 2.5Hz, 2 = 5Hz, 3 = 10Hz, - /// - internal static readonly Device_value Met_U16CoilDriverFrequencyConfig = new Device_value("Met_U16CoilDriverFrequencyConfig", 25, ValueDataType.U16, ValueRW_Type.ReadWrite, "CAUSES RESTART! 10s - Coil driver frequency configuration, 0 = 1.25Hz, 1 = 2.5Hz, 2 = 5Hz, 3 = 10Hz,"); - - /// - /// @brief CAUSES RESTART! 10s - Pipe Diameter configuration, 50 for DN50, 100 for DN100 etc. - /// - internal static readonly Device_value Met_U16PipeDiameterConfig_mm = new Device_value("Met_U16PipeDiameterConfig_mm", 26, ValueDataType.U16, ValueRW_Type.ReadWrite, "CAUSES RESTART! 10s - Pipe Diameter configuration, 50 for DN50, 100 for DN100 etc."); - - /// - /// @brief Programmed during connection to metrology. Const after production. See label on sensor - /// - internal static readonly Device_value Met_U32SensorSerialNo = new Device_value("Met_U32SensorSerialNo", 27, ValueDataType.U32, ValueRW_Type.ReadWrite, "Programmed during connection to metrology. Const after production. See label on sensor"); - - -/*------------------------------ block number 2 ------------------------------*/ - - /// - /// @brief Const pulled from inside Met CPU - /// - internal static readonly Device_value Met_U128_CPU_ID = new Device_value("Met_U128_CPU_ID", 80, ValueDataType.U128, ValueRW_Type.ReadWrite, "Const pulled from inside Met CPU"); - - -/*------------------------------ block number 3 ------------------------------*/ - - /// - /// @brief Signature is only calculated when the Modbus reads the signature and it ALWAYS covers the Volume information block. If you don't know the hash seed then you won't be able to check the signature. :-) Still TBD. - /// - internal static readonly Device_value Met_U128Signature = new Device_value("Met_U128Signature", 100, ValueDataType.U128, ValueRW_Type.ReadWrite, "Signature is only calculated when the Modbus reads the signature and it ALWAYS covers the Volume information block. If you don't know the hash seed then you won't be able to check the signature. :-) Still TBD."); - - /// - /// @brief Status and other registers up to the age are covered by the signature - /// - internal static readonly Device_value Met_U32Status = new Device_value("Met_U32Status", 108, ValueDataType.U32, ValueRW_Type.ReadWrite, "Status and other registers up to the age are covered by the signature"); - - /// - /// @brief Errors come and go. Flags for them are here. - /// - internal static readonly Device_value Met_U32Errors = new Device_value("Met_U32Errors", 110, ValueDataType.U32, ValueRW_Type.ReadWrite, "Errors come and go. Flags for them are here."); - - /// - /// @brief We set alarms from the slave when errors happen, only the master can clear them. - /// - internal static readonly Device_value Met_U32Alarms = new Device_value("Met_U32Alarms", 112, ValueDataType.U32, ValueRW_Type.ReadWrite, "We set alarms from the slave when errors happen, only the master can clear them."); - - /// - /// @brief Volume register names should speak for themselves. - /// - internal static readonly Device_value Met_U64ForwardNormalVolume_l = new Device_value("Met_U64ForwardNormalVolume_l", 114, ValueDataType.U64, ValueRW_Type.ReadWrite, "Volume register names should speak for themselves."); - - /// - /// @brief Volume of water measured goingbackwards! - /// - internal static readonly Device_value Met_U64ReverseVolume_l = new Device_value("Met_U64ReverseVolume_l", 118, ValueDataType.U64, ValueRW_Type.ReadWrite, "Volume of water measured goingbackwards!"); - - /// - /// @brief Volume of water measured going faster than we can measure accurately - /// - internal static readonly Device_value Met_U64ForwardTooFastVolume_l = new Device_value("Met_U64ForwardTooFastVolume_l", 122, ValueDataType.U64, ValueRW_Type.ReadWrite, "Volume of water measured going faster than we can measure accurately"); - - /// - /// @brief Speed of flow in m/s, calibrated, floating point number so units are easy. - /// - internal static readonly Device_value Met_F32FlowSpeed_mps = new Device_value("Met_F32FlowSpeed_mps", 126, ValueDataType.F32, ValueRW_Type.ReadWrite, "Speed of flow in m/s, calibrated, floating point number so units are easy."); - - /// - /// @brief Forward +1, Reverse -1 - /// - internal static readonly Device_value Met_S16FlowDirection = new Device_value("Met_S16FlowDirection", 128, ValueDataType.S16, ValueRW_Type.ReadWrite, "Forward +1, Reverse -1"); - - /// - /// @brief Rate of flow in L/s, calibrated - /// - internal static readonly Device_value Met_F32FlowRate_lps = new Device_value("Met_F32FlowRate_lps", 129, ValueDataType.F32, ValueRW_Type.ReadWrite, "Rate of flow in L/s, calibrated"); - - /// - /// @brief Updated with the second tick, maybe a little less often if we are in low power mode. Atomic with the signature, total updates etc. - /// - internal static readonly Device_value Met_U16AgeTotals_s = new Device_value("Met_U16AgeTotals_s", 131, ValueDataType.U16, ValueRW_Type.ReadWrite, "Updated with the second tick, maybe a little less often if we are in low power mode. Atomic with the signature, total updates etc."); - - -/*------------------------------ block number 4 ------------------------------*/ - - /// - /// @brief Logged in as secure = 1. Not logged in as secure 0 - /// - internal static readonly Device_value Met_U16SecurityLoginStatus = new Device_value("Met_U16SecurityLoginStatus", 300, ValueDataType.U16, ValueRW_Type.ReadWrite, "Logged in as secure = 1. Not logged in as secure 0"); - - /// - /// @brief Security Login will timeout... when we get to 0... Updated with the second tick - /// - internal static readonly Device_value Met_U16SecurityLoginTimeleft_s = new Device_value("Met_U16SecurityLoginTimeleft_s", 301, ValueDataType.U16, ValueRW_Type.ReadWrite, "Security Login will timeout... when we get to 0... Updated with the second tick"); - - /// - /// @brief Part of the security login games etc. TBD. - /// - internal static readonly Device_value Met_U32SecurityLoginChallenge = new Device_value("Met_U32SecurityLoginChallenge", 302, ValueDataType.U32, ValueRW_Type.ReadWrite, " Part of the security login games etc. TBD."); - - /// - /// @brief This will change - but we play a game here just to start a conversation, enter code, read challenge, enter reply to code... all good... or bad... - /// - internal static readonly Device_value Met_U32SecurityLoginCode = new Device_value("Met_U32SecurityLoginCode", 304, ValueDataType.U32, ValueRW_Type.ReadWrite, "This will change - but we play a game here just to start a conversation, enter code, read challenge, enter reply to code... all good... or bad..."); - - -/*------------------------------ block number 5 ------------------------------*/ - - /// - /// @brief No one is allowed to change bootcount, but can be zero'd after JTAG ? TBD. - /// - internal static readonly Device_value Met_U16BootCount = new Device_value("Met_U16BootCount", 500, ValueDataType.U16, ValueRW_Type.ReadWrite, "No one is allowed to change bootcount, but can be zero'd after JTAG ? TBD."); - - /// - /// @brief How long has the meter been running (inc. sleep time). - /// - internal static readonly Device_value Met_U32LifeTime_min = new Device_value("Met_U32LifeTime_min", 501, ValueDataType.U32, ValueRW_Type.ReadWrite, "How long has the meter been running (inc. sleep time)."); - - /// - /// @brief Allows us to watch the clock, since last power on - /// - internal static readonly Device_value Met_U64TimeUpTicks = new Device_value("Met_U64TimeUpTicks", 503, ValueDataType.U64, ValueRW_Type.ReadWrite, "Allows us to watch the clock, since last power on"); - - /// - /// @brief Number of good packets recieved. This value can be zero'd by writing anything to it. - /// - internal static readonly Device_value Met_U32ComGoodPktCounter = new Device_value("Met_U32ComGoodPktCounter", 507, ValueDataType.U32, ValueRW_Type.ReadWrite, "Number of good packets recieved. This value can be zero'd by writing anything to it."); - - /// - /// @brief Number of transmissions. This value can be zero'd by writing anything to it. - /// - internal static readonly Device_value Met_U32ComTxCounter = new Device_value("Met_U32ComTxCounter", 509, ValueDataType.U32, ValueRW_Type.ReadWrite, "Number of transmissions. This value can be zero'd by writing anything to it."); - - /// - /// @brief Number of packets that have been dropped. This value can be zero'd by writing anything to it. - /// - internal static readonly Device_value Met_U32ComDroppedPktCounter = new Device_value("Met_U32ComDroppedPktCounter", 511, ValueDataType.U32, ValueRW_Type.ReadWrite, "Number of packets that have been dropped. This value can be zero'd by writing anything to it."); - - /// - /// @brief Number of bad CRC's that have been recieved. This value can be zero'd by writing anything to it. - /// - internal static readonly Device_value Met_U32ComBadCRCCounter = new Device_value("Met_U32ComBadCRCCounter", 513, ValueDataType.U32, ValueRW_Type.ReadWrite, "Number of bad CRC's that have been recieved. This value can be zero'd by writing anything to it."); - - /// - /// @brief Number of good bytes recieved. This sort of number is used to allow a measure of reliability to be calculated. This value can be zero'd by writing anything to it. - /// - internal static readonly Device_value Met_U32ComGoodByteCounter = new Device_value("Met_U32ComGoodByteCounter", 515, ValueDataType.U32, ValueRW_Type.ReadWrite, "Number of good bytes recieved. This sort of number is used to allow a measure of reliability to be calculated. This value can be zero'd by writing anything to it."); - - /// - /// @brief Number of bad bytes recieved, meaning those from non valid packets. This value can be zero'd by writing anything to it. - /// - internal static readonly Device_value Met_U32ComBadByteCounter = new Device_value("Met_U32ComBadByteCounter", 517, ValueDataType.U32, ValueRW_Type.ReadWrite, "Number of bad bytes recieved, meaning those from non valid packets. This value can be zero'd by writing anything to it."); - - -/*------------------------------ block number 6 ------------------------------*/ - - /// - /// @brief Register to set pulse-on time in ms, for pulse output 1. Write 0 will set digital output to fastest possible Ton_ms (0.5ms) - /// - internal static readonly Device_value Met_U16ComPktPulseOutput1_Ton_ms = new Device_value("Met_U16ComPktPulseOutput1_Ton_ms", 640, ValueDataType.U16, ValueRW_Type.ReadWrite, "Register to set pulse-on time in ms, for pulse output 1. Write 0 will set digital output to fastest possible Ton_ms (0.5ms)"); - - /// - /// @brief Register to get minimum value of pulse-on time in ms, for pulse output 1. The Ton_Min_ms is updated by FW at Init. True min is 0.5ms when Ton_ms = 0 - /// - internal static readonly Device_value Met_U16ComPktPulseOutput1_Ton_min_ms = new Device_value("Met_U16ComPktPulseOutput1_Ton_min_ms", 641, ValueDataType.U16, ValueRW_Type.ReadWrite, "Register to get minimum value of pulse-on time in ms, for pulse output 1. The Ton_Min_ms is updated by FW at Init. True min is 0.5ms when Ton_ms = 0"); - - /// - /// @brief Register to get maximum value of pulse-on time in ms, for pulse output 1. The Ton_Max_ms is updated by FW at Init - /// - internal static readonly Device_value Met_U16ComPktPulseOutput1_Ton_max_ms = new Device_value("Met_U16ComPktPulseOutput1_Ton_max_ms", 642, ValueDataType.U16, ValueRW_Type.ReadWrite, "Register to get maximum value of pulse-on time in ms, for pulse output 1. The Ton_Max_ms is updated by FW at Init"); - - /// - /// @brief Register to set volume per pulse for pulse output 1. - /// - internal static readonly Device_value Met_U32VolumePerPulse_PulseOutput1_mL = new Device_value("Met_U32VolumePerPulse_PulseOutput1_mL", 643, ValueDataType.U32, ValueRW_Type.ReadWrite, "Register to set volume per pulse for pulse output 1."); - - /// - /// @brief Register to get the minimum value used by volume per pulse register for pulse output 1. - /// - internal static readonly Device_value Met_U32VolumePerPulse_PulseOutput1_min_mL = new Device_value("Met_U32VolumePerPulse_PulseOutput1_min_mL", 645, ValueDataType.U32, ValueRW_Type.ReadWrite, "Register to get the minimum value used by volume per pulse register for pulse output 1."); - - /// - /// @brief Register to get the maximum value used by volume per pulse register for pulse output 1. - /// - internal static readonly Device_value Met_U32VolumePerPulse_PulseOutput1_max_mL = new Device_value("Met_U32VolumePerPulse_PulseOutput1_max_mL", 647, ValueDataType.U32, ValueRW_Type.ReadWrite, "Register to get the maximum value used by volume per pulse register for pulse output 1."); - - /// - /// @brief Register to set pulse-on time in ms, for pulse output 2. Write 0 will set digital output to fastest possible Ton_ms (0.5ms) - /// - internal static readonly Device_value Met_U16ComPktPulseOutput2_Ton_ms = new Device_value("Met_U16ComPktPulseOutput2_Ton_ms", 649, ValueDataType.U16, ValueRW_Type.ReadWrite, "Register to set pulse-on time in ms, for pulse output 2. Write 0 will set digital output to fastest possible Ton_ms (0.5ms)"); - - /// - /// @brief Register to get minimum value of pulse-on time in ms, for pulse output 2. The Ton_Min_ms is updated by FW at Init. True min is 0.5ms when Ton_ms = 0 - /// - internal static readonly Device_value Met_U16ComPktPulseOutput2_Ton_min_ms = new Device_value("Met_U16ComPktPulseOutput2_Ton_min_ms", 650, ValueDataType.U16, ValueRW_Type.ReadWrite, "Register to get minimum value of pulse-on time in ms, for pulse output 2. The Ton_Min_ms is updated by FW at Init. True min is 0.5ms when Ton_ms = 0"); - - /// - /// @brief Register to get maximum value of pulse-on time in ms, for pulse output 2. The Ton_Max_ms is updated by FW at Init. - /// - internal static readonly Device_value Met_U16ComPktPulseOutput2_Ton_max_ms = new Device_value("Met_U16ComPktPulseOutput2_Ton_max_ms", 651, ValueDataType.U16, ValueRW_Type.ReadWrite, "Register to get maximum value of pulse-on time in ms, for pulse output 2. The Ton_Max_ms is updated by FW at Init."); - - /// - /// @brief Register to set volume per pulse for pulse output 2. - /// - internal static readonly Device_value Met_U32VolumePerPulse_PulseOutput2_mL = new Device_value("Met_U32VolumePerPulse_PulseOutput2_mL", 652, ValueDataType.U32, ValueRW_Type.ReadWrite, "Register to set volume per pulse for pulse output 2."); - - /// - /// @brief Register to get the minimum value used by volume per pulse register for pulse output 2. - /// - internal static readonly Device_value Met_U32VolumePerPulse_PulseOutput2_min_mL = new Device_value("Met_U32VolumePerPulse_PulseOutput2_min_mL", 654, ValueDataType.U32, ValueRW_Type.ReadWrite, "Register to get the minimum value used by volume per pulse register for pulse output 2."); - - /// - /// @brief Register to get the maximum value used by volume per pulse register for pulse output 2 - /// - internal static readonly Device_value Met_U32VolumePerPulse_PulseOutput2_max_mL = new Device_value("Met_U32VolumePerPulse_PulseOutput2_max_mL", 656, ValueDataType.U32, ValueRW_Type.ReadWrite, "Register to get the maximum value used by volume per pulse register for pulse output 2"); - - -/*------------------------------ block number 7 ------------------------------*/ - - /// - /// @brief Write to this register to control ADC calibration - /// - internal static readonly Device_value Met_U16ExtAdc_CalibCtrl = new Device_value("Met_U16ExtAdc_CalibCtrl", 700, ValueDataType.U16, ValueRW_Type.ReadWrite, "Write to this register to control ADC calibration"); - - /// - /// @brief Read this register to understand ADC calibration states and errors etc. - /// - internal static readonly Device_value Met_U16ExtAdc_CalibStatus = new Device_value("Met_U16ExtAdc_CalibStatus", 701, ValueDataType.U16, ValueRW_Type.ReadWrite, "Read this register to understand ADC calibration states and errors etc."); - - /// - /// @brief External ADC current measuring Electrode inputs, see CalibStatus - /// - internal static readonly Device_value Met_F32ExtADC_Electrode_mV = new Device_value("Met_F32ExtADC_Electrode_mV", 702, ValueDataType.F32, ValueRW_Type.ReadWrite, "External ADC current measuring Electrode inputs, see CalibStatus"); - - /// - /// @brief External ADC current measuring Coil inputs, see CalibStatus - /// - internal static readonly Device_value Met_F32ExtADC_Coil_mV = new Device_value("Met_F32ExtADC_Coil_mV", 704, ValueDataType.F32, ValueRW_Type.ReadWrite, "External ADC current measuring Coil inputs, see CalibStatus"); - - -/*------------------------------ block number 8 ------------------------------*/ - - /// - /// @brief External ADC calibration point0 measuring electrodes - known mV - /// - internal static readonly Device_value Met_F32ExtADC_Electrode_referencePt0_mV = new Device_value("Met_F32ExtADC_Electrode_referencePt0_mV", 740, ValueDataType.F32, ValueRW_Type.ReadWrite, "External ADC calibration point0 measuring electrodes - known mV"); - - /// - /// @brief External ADC calibration point0 measuring electrodes - read mV - /// - internal static readonly Device_value Met_F32ExtADC_Electrode_reportPt0_mV = new Device_value("Met_F32ExtADC_Electrode_reportPt0_mV", 742, ValueDataType.F32, ValueRW_Type.ReadWrite, "External ADC calibration point0 measuring electrodes - read mV"); - - -/*------------------------------ block number 9 ------------------------------*/ - - /// - /// @brief External ADC calibration point0 measuring coil - known mV - /// - internal static readonly Device_value Met_F32ExtADC_Coil_referencePt0_mV = new Device_value("Met_F32ExtADC_Coil_referencePt0_mV", 760, ValueDataType.F32, ValueRW_Type.ReadWrite, "External ADC calibration point0 measuring coil - known mV"); - - /// - /// @brief External ADC calibration point0 measuring coil - read mV - /// - internal static readonly Device_value Met_F32ExtADC_Coil_reportPt0_mV = new Device_value("Met_F32ExtADC_Coil_reportPt0_mV", 762, ValueDataType.F32, ValueRW_Type.ReadWrite, "External ADC calibration point0 measuring coil - read mV"); - - -/*------------------------------ block number 10 ------------------------------*/ - - /// - /// @brief Write to this register to control Flow calibration - /// - internal static readonly Device_value Met_U16Flow_CalibCtrl = new Device_value("Met_U16Flow_CalibCtrl", 800, ValueDataType.U16, ValueRW_Type.ReadWrite, "Write to this register to control Flow calibration"); - - /// - /// @brief Read this register to understand Flow calibration states and errors etc. - /// - internal static readonly Device_value Met_U16Flow_CalibStatus = new Device_value("Met_U16Flow_CalibStatus", 801, ValueDataType.U16, ValueRW_Type.ReadWrite, "Read this register to understand Flow calibration states and errors etc."); - - /// - /// @brief Raw flow speed in m/s, before calibration - /// - internal static readonly Device_value Met_F32Flow_RawSpeed_mps = new Device_value("Met_F32Flow_RawSpeed_mps", 802, ValueDataType.F32, ValueRW_Type.ReadWrite, "Raw flow speed in m/s, before calibration"); - - /// - /// @brief Raw flow rate in L/s, before calibration - /// - internal static readonly Device_value Met_F32Flow_RawRate_lps = new Device_value("Met_F32Flow_RawRate_lps", 804, ValueDataType.F32, ValueRW_Type.ReadWrite, "Raw flow rate in L/s, before calibration"); - - /// - /// @brief Flow point0, ZERO flow cal. From reference test harness, known L/s - Cannot read present cal point! - /// - internal static readonly Device_value Met_F32Flow_NewReferencePt0_lps = new Device_value("Met_F32Flow_NewReferencePt0_lps", 806, ValueDataType.F32, ValueRW_Type.ReadWrite, "Flow point0, ZERO flow cal. From reference test harness, known L/s - Cannot read present cal point!"); - - /// - /// @brief Flow point0, ZERO flow cal. calculated from this meter, L/s - Cannot read present cal point! - /// - internal static readonly Device_value Met_F32Flow_NewReportedPt0_lps = new Device_value("Met_F32Flow_NewReportedPt0_lps", 808, ValueDataType.F32, ValueRW_Type.ReadWrite, "Flow point0, ZERO flow cal. calculated from this meter, L/s - Cannot read present cal point!"); - - /// - /// @brief Flow point1, from reference test harness, known L/s - Cannot read present cal point! - /// - internal static readonly Device_value Met_F32Flow_NewReferencePt1_lps = new Device_value("Met_F32Flow_NewReferencePt1_lps", 810, ValueDataType.F32, ValueRW_Type.ReadWrite, "Flow point1, from reference test harness, known L/s - Cannot read present cal point!"); - - /// - /// @brief Flow point1, calculated from this meter, L/s - Cannot read present cal point! - /// - internal static readonly Device_value Met_F32Flow_NewReportedPt1_lps = new Device_value("Met_F32Flow_NewReportedPt1_lps", 812, ValueDataType.F32, ValueRW_Type.ReadWrite, "Flow point1, calculated from this meter, L/s - Cannot read present cal point!"); - - /// - /// @brief Flow point2, from reference test harness, known L/s - Cannot read present cal point! - /// - internal static readonly Device_value Met_F32Flow_NewReferencePt2_lps = new Device_value("Met_F32Flow_NewReferencePt2_lps", 814, ValueDataType.F32, ValueRW_Type.ReadWrite, "Flow point2, from reference test harness, known L/s - Cannot read present cal point!"); - - /// - /// @brief Flow point2, calculated from this meter, L/s - Cannot read present cal point! - /// - internal static readonly Device_value Met_F32Flow_NewReportedPt2_lps = new Device_value("Met_F32Flow_NewReportedPt2_lps", 816, ValueDataType.F32, ValueRW_Type.ReadWrite, "Flow point2, calculated from this meter, L/s - Cannot read present cal point!"); - - /// - /// @brief Flow point3, from reference test harness, known L/s - Cannot read present cal point! - /// - internal static readonly Device_value Met_F32Flow_NewReferencePt3_lps = new Device_value("Met_F32Flow_NewReferencePt3_lps", 818, ValueDataType.F32, ValueRW_Type.ReadWrite, "Flow point3, from reference test harness, known L/s - Cannot read present cal point!"); - - /// - /// @brief Flow point3, calculated from this meter, L/s - Cannot read present cal point! - /// - internal static readonly Device_value Met_F32Flow_NewReportedPt3_lps = new Device_value("Met_F32Flow_NewReportedPt3_lps", 820, ValueDataType.F32, ValueRW_Type.ReadWrite, "Flow point3, calculated from this meter, L/s - Cannot read present cal point!"); - - /// - /// @brief Flow point4, from reference test harness, known L/s - Cannot read present cal point! - /// - internal static readonly Device_value Met_F32Flow_NewReferencePt4_lps = new Device_value("Met_F32Flow_NewReferencePt4_lps", 822, ValueDataType.F32, ValueRW_Type.ReadWrite, "Flow point4, from reference test harness, known L/s - Cannot read present cal point!"); - - /// - /// @brief Flow point4, calculated from this meter, L/s - Cannot read present cal point! - /// - internal static readonly Device_value Met_F32Flow_NewReportedPt4_lps = new Device_value("Met_F32Flow_NewReportedPt4_lps", 824, ValueDataType.F32, ValueRW_Type.ReadWrite, "Flow point4, calculated from this meter, L/s - Cannot read present cal point!"); - - /// - /// @brief Flow point5, from reference test harness, known L/s - Cannot read present cal point! - /// - internal static readonly Device_value Met_F32Flow_NewReferencePt5_lps = new Device_value("Met_F32Flow_NewReferencePt5_lps", 826, ValueDataType.F32, ValueRW_Type.ReadWrite, "Flow point5, from reference test harness, known L/s - Cannot read present cal point!"); - - /// - /// @brief Flow point5, calculated from this meter, L/s - Cannot read present cal point! - /// - internal static readonly Device_value Met_F32Flow_NewReportedPt5_lps = new Device_value("Met_F32Flow_NewReportedPt5_lps", 828, ValueDataType.F32, ValueRW_Type.ReadWrite, "Flow point5, calculated from this meter, L/s - Cannot read present cal point!"); - - -/*------------------------------ block number 11 ------------------------------*/ - - /// - /// @brief Number of current points used for calibration. Set by FW at init and at a successful calibration - /// - internal static readonly Device_value Met_U16PresentPointsCount = new Device_value("Met_U16PresentPointsCount", 849, ValueDataType.U16, ValueRW_Type.ReadWrite, "Number of current points used for calibration. Set by FW at init and at a successful calibration"); - - /// - /// @brief ZERO flow cal. Current boundary position between Zero flow and 1st region, post-calibration, L/s. - Only saved after a successful calibration - /// - internal static readonly Device_value Met_F32FlowPresentPoint0_reference_lps = new Device_value("Met_F32FlowPresentPoint0_reference_lps", 850, ValueDataType.F32, ValueRW_Type.ReadWrite, "ZERO flow cal. Current boundary position between Zero flow and 1st region, post-calibration, L/s. - Only saved after a successful calibration"); - - /// - /// @brief ZERO flow cal. Current boundary position between Zero flow and 1st region, pre-calibration, L/s. - Only saved after a successful calibration - /// - internal static readonly Device_value Met_F32FlowPresentPoint0_reported_lps = new Device_value("Met_F32FlowPresentPoint0_reported_lps", 852, ValueDataType.F32, ValueRW_Type.ReadWrite, "ZERO flow cal. Current boundary position between Zero flow and 1st region, pre-calibration, L/s. - Only saved after a successful calibration"); - - /// - /// @brief Current boundary position between region 1 and 2, post-calibration, L/s. - Only saved after a successful calibration - /// - internal static readonly Device_value Met_F32FlowPresentPoint1_reference_lps = new Device_value("Met_F32FlowPresentPoint1_reference_lps", 854, ValueDataType.F32, ValueRW_Type.ReadWrite, "Current boundary position between region 1 and 2, post-calibration, L/s. - Only saved after a successful calibration"); - - /// - /// @brief Current boundary position between region 1 and 2, pre-calibration, L/s. - Only saved after a successful calibration - /// - internal static readonly Device_value Met_F32FlowPresentPoint1_reported_lps = new Device_value("Met_F32FlowPresentPoint1_reported_lps", 856, ValueDataType.F32, ValueRW_Type.ReadWrite, "Current boundary position between region 1 and 2, pre-calibration, L/s. - Only saved after a successful calibration"); - - /// - /// @brief Current boundary position between region 2 and 3, post-calibration, L/s. - Only saved after a successful calibration - /// - internal static readonly Device_value Met_F32FlowPresentPoint2_reference_lps = new Device_value("Met_F32FlowPresentPoint2_reference_lps", 858, ValueDataType.F32, ValueRW_Type.ReadWrite, "Current boundary position between region 2 and 3, post-calibration, L/s. - Only saved after a successful calibration"); - - /// - /// @brief Current boundary position between region 2 and 3, pre-calibration, L/s. - Only saved after a successful calibration - /// - internal static readonly Device_value Met_F32FlowPresentPoint2_reported_lps = new Device_value("Met_F32FlowPresentPoint2_reported_lps", 860, ValueDataType.F32, ValueRW_Type.ReadWrite, "Current boundary position between region 2 and 3, pre-calibration, L/s. - Only saved after a successful calibration"); - - /// - /// @brief Current boundary position between region 3 and 4, post-calibration, L/s. - Only saved after a successful calibration - /// - internal static readonly Device_value Met_F32FlowPresentPoint3_reference_lps = new Device_value("Met_F32FlowPresentPoint3_reference_lps", 862, ValueDataType.F32, ValueRW_Type.ReadWrite, "Current boundary position between region 3 and 4, post-calibration, L/s. - Only saved after a successful calibration"); - - /// - /// @brief Current boundary position between region 3 and 4, pre-calibration, L/s. - Only saved after a successful calibration - /// - internal static readonly Device_value Met_F32FlowPresentPoint3_reported_lps = new Device_value("Met_F32FlowPresentPoint3_reported_lps", 864, ValueDataType.F32, ValueRW_Type.ReadWrite, "Current boundary position between region 3 and 4, pre-calibration, L/s. - Only saved after a successful calibration"); - - /// - /// @brief Current boundary position between region 4 and 5, post-calibration, L/s. - Only saved after a successful calibration - /// - internal static readonly Device_value Met_F32FlowPresentPoint4_reference_lps = new Device_value("Met_F32FlowPresentPoint4_reference_lps", 866, ValueDataType.F32, ValueRW_Type.ReadWrite, "Current boundary position between region 4 and 5, post-calibration, L/s. - Only saved after a successful calibration"); - - /// - /// @brief Current boundary position between region 4 and 5, pre-calibration, L/s. - Only saved after a successful calibration - /// - internal static readonly Device_value Met_F32FlowPresentPoint4_reported_lps = new Device_value("Met_F32FlowPresentPoint4_reported_lps", 868, ValueDataType.F32, ValueRW_Type.ReadWrite, "Current boundary position between region 4 and 5, pre-calibration, L/s. - Only saved after a successful calibration"); - - /// - /// @brief Current boundary position between region 5 and above, post-calibration, L/s. - Only saved after a successful calibration - /// - internal static readonly Device_value Met_F32FlowPresentPoint5_reference_lps = new Device_value("Met_F32FlowPresentPoint5_reference_lps", 870, ValueDataType.F32, ValueRW_Type.ReadWrite, "Current boundary position between region 5 and above, post-calibration, L/s. - Only saved after a successful calibration"); - - /// - /// @brief Current boundary position between region 5 and above, pre-calibration, L/s. - Only saved after a successful calibration - /// - internal static readonly Device_value Met_F32FlowPresentPoint5_reported_lps = new Device_value("Met_F32FlowPresentPoint5_reported_lps", 872, ValueDataType.F32, ValueRW_Type.ReadWrite, "Current boundary position between region 5 and above, pre-calibration, L/s. - Only saved after a successful calibration"); - - -/*------------------------------ block number 12 ------------------------------*/ - - /// - /// @brief Flow calibration gradient m in region 0. y = mx+c - /// - internal static readonly Device_value Met_F32FlowRegion0_gradient_m = new Device_value("Met_F32FlowRegion0_gradient_m", 880, ValueDataType.F32, ValueRW_Type.ReadWrite, "Flow calibration gradient m in region 0. y = mx+c"); - - /// - /// @brief Flow calibration offset c in region 0. y = mx+c - /// - internal static readonly Device_value Met_F32FlowRegion0_offset_c = new Device_value("Met_F32FlowRegion0_offset_c", 882, ValueDataType.F32, ValueRW_Type.ReadWrite, "Flow calibration offset c in region 0. y = mx+c"); - - /// - /// @brief Flow calibration gradient m in region 1. y = mx+c - /// - internal static readonly Device_value Met_F32FlowRegion1_gradient_m = new Device_value("Met_F32FlowRegion1_gradient_m", 884, ValueDataType.F32, ValueRW_Type.ReadWrite, "Flow calibration gradient m in region 1. y = mx+c"); - - /// - /// @brief Flow calibration offset c in region 1. y = mx+c - /// - internal static readonly Device_value Met_F32FlowRegion1_offset_c = new Device_value("Met_F32FlowRegion1_offset_c", 886, ValueDataType.F32, ValueRW_Type.ReadWrite, "Flow calibration offset c in region 1. y = mx+c"); - - /// - /// @brief Flow calibration gradient m in region 2. y = mx+c - /// - internal static readonly Device_value Met_F32FlowRegion2_gradient_m = new Device_value("Met_F32FlowRegion2_gradient_m", 888, ValueDataType.F32, ValueRW_Type.ReadWrite, "Flow calibration gradient m in region 2. y = mx+c"); - - /// - /// @brief Flow calibration offset c in region 2. - /// - internal static readonly Device_value Met_F32FlowRegion2_offset_c = new Device_value("Met_F32FlowRegion2_offset_c", 890, ValueDataType.F32, ValueRW_Type.ReadWrite, "Flow calibration offset c in region 2."); - - /// - /// @brief Flow calibration gradient m in region 3. y = mx+c - /// - internal static readonly Device_value Met_F32FlowRegion3_gradient_m = new Device_value("Met_F32FlowRegion3_gradient_m", 892, ValueDataType.F32, ValueRW_Type.ReadWrite, "Flow calibration gradient m in region 3. y = mx+c"); - - /// - /// @brief Flow calibration offset c in region 3. - /// - internal static readonly Device_value Met_F32FlowRegion3_offset_c = new Device_value("Met_F32FlowRegion3_offset_c", 894, ValueDataType.F32, ValueRW_Type.ReadWrite, "Flow calibration offset c in region 3."); - - /// - /// @brief Flow calibration gradient m in region 4. y = mx+c - /// - internal static readonly Device_value Met_F32FlowRegion4_gradient_m = new Device_value("Met_F32FlowRegion4_gradient_m", 896, ValueDataType.F32, ValueRW_Type.ReadWrite, "Flow calibration gradient m in region 4. y = mx+c"); - - /// - /// @brief Flow calibration offset c in region 4. - /// - internal static readonly Device_value Met_F32FlowRegion4_offset_c = new Device_value("Met_F32FlowRegion4_offset_c", 898, ValueDataType.F32, ValueRW_Type.ReadWrite, "Flow calibration offset c in region 4."); - - /// - /// @brief Flow calibration gradient m in region 5. y = mx+c - /// - internal static readonly Device_value Met_F32FlowRegion5_gradient_m = new Device_value("Met_F32FlowRegion5_gradient_m", 900, ValueDataType.F32, ValueRW_Type.ReadWrite, "Flow calibration gradient m in region 5. y = mx+c"); - - /// - /// @brief Flow calibration offset c in region 5. - /// - internal static readonly Device_value Met_F32FlowRegion5_offset_c = new Device_value("Met_F32FlowRegion5_offset_c", 902, ValueDataType.F32, ValueRW_Type.ReadWrite, "Flow calibration offset c in region 5."); - - /// - /// @brief Flow calibration gradient m in region 6. y = mx+c - /// - internal static readonly Device_value Met_F32FlowRegion6_gradient_m = new Device_value("Met_F32FlowRegion6_gradient_m", 904, ValueDataType.F32, ValueRW_Type.ReadWrite, "Flow calibration gradient m in region 6. y = mx+c"); - - /// - /// @brief Flow calibration offset c in region 6. - /// - internal static readonly Device_value Met_F32FlowRegion6_offset_c = new Device_value("Met_F32FlowRegion6_offset_c", 906, ValueDataType.F32, ValueRW_Type.ReadWrite, "Flow calibration offset c in region 6."); - - -/*------------------------------ block number 13 ------------------------------*/ - - /// - /// @brief CAUSES RESTART! 10s - You need to be logged to force this... also incs boot count. - /// - internal static readonly Device_value Met_U16ForceResetOfFirmware = new Device_value("Met_U16ForceResetOfFirmware", 1000, ValueDataType.U16, ValueRW_Type.ReadWrite, "CAUSES RESTART! 10s - You need to be logged to force this... also incs boot count."); - - -/*------------------------------ block number 14 ------------------------------*/ - - /// - /// @brief You need to be able to login to force this... See also status register. Setting the flags on means that mock values are used. Mock registers are implemented to support test an error flag in the status register will indicate mock registers in use. - /// - internal static readonly Device_value Met_U32MockFlags = new Device_value("Met_U32MockFlags", 1100, ValueDataType.U32, ValueRW_Type.ReadWrite, "You need to be able to login to force this... See also status register. Setting the flags on means that mock values are used. Mock registers are implemented to support test an error flag in the status register will indicate mock registers in use."); - - /// - /// @brief Mock water pressure value. Mock registers are implemented to support test an error flag in the status register will indicate mock registers in use. - /// - internal static readonly Device_value Met_F32MockFEPressure_MPa = new Device_value("Met_F32MockFEPressure_MPa", 1102, ValueDataType.F32, ValueRW_Type.ReadWrite, "Mock water pressure value. Mock registers are implemented to support test an error flag in the status register will indicate mock registers in use."); - - /// - /// @brief Mock temperature, degrees C. Mock registers are implemented to support test an error flag in the status register will indicate mock registers in use. - /// - internal static readonly Device_value Met_F32MockFETemperature_C = new Device_value("Met_F32MockFETemperature_C", 1104, ValueDataType.F32, ValueRW_Type.ReadWrite, "Mock temperature, degrees C. Mock registers are implemented to support test an error flag in the status register will indicate mock registers in use."); - - /// - /// @brief Mock speed of flow. Mock registers are implemented to support test an error flag in the status register will indicate mock registers in use. - /// - internal static readonly Device_value Met_F32MockFlowSpeed_mps = new Device_value("Met_F32MockFlowSpeed_mps", 1106, ValueDataType.F32, ValueRW_Type.ReadWrite, "Mock speed of flow. Mock registers are implemented to support test an error flag in the status register will indicate mock registers in use."); - - /// - /// @brief Mock flow rate for testing calibration. - /// - internal static readonly Device_value Met_S64MockFlowRate_ulps = new Device_value("Met_S64MockFlowRate_ulps", 1108, ValueDataType.S64, ValueRW_Type.ReadWrite, "Mock flow rate for testing calibration."); - - /// - /// @brief Mock value for direction of flow. Mock registers are implemented to support test an error flag in the status register will indicate mock registers in use. - /// - internal static readonly Device_value Met_S16MockFlowDirection = new Device_value("Met_S16MockFlowDirection", 1112, ValueDataType.S16, ValueRW_Type.ReadWrite, "Mock value for direction of flow. Mock registers are implemented to support test an error flag in the status register will indicate mock registers in use."); - - /// - /// @brief Mock time between current sample and previous one. Mock registers are implemented to support test an error flag in the status register will indicate mock registers in use. - /// - internal static readonly Device_value Met_F32MockTimeSampleInterval_s = new Device_value("Met_F32MockTimeSampleInterval_s", 1113, ValueDataType.F32, ValueRW_Type.ReadWrite, "Mock time between current sample and previous one. Mock registers are implemented to support test an error flag in the status register will indicate mock registers in use."); - - /// - /// @brief Scratch register - just somewhere to play write and read. - /// - internal static readonly Device_value Met_U16Scratch0 = new Device_value("Met_U16Scratch0", 1115, ValueDataType.U16, ValueRW_Type.ReadWrite, "Scratch register - just somewhere to play write and read."); - - -/*------------------------------ block number 15 ------------------------------*/ - - /// - /// @brief Scratch register - just somewhere to play write and read. - /// - internal static readonly Device_value Met_S16Scratch_S16 = new Device_value("Met_S16Scratch_S16", 1118, ValueDataType.S16, ValueRW_Type.ReadWrite, "Scratch register - just somewhere to play write and read."); - - /// - /// @brief Scratch register - just somewhere to play write and read. - /// - internal static readonly Device_value Met_U32Scratch_U32 = new Device_value("Met_U32Scratch_U32", 1119, ValueDataType.U32, ValueRW_Type.ReadWrite, "Scratch register - just somewhere to play write and read."); - - /// - /// @brief Scratch register - just somewhere to play write and read. - /// - internal static readonly Device_value Met_S32Scratch_S32 = new Device_value("Met_S32Scratch_S32", 1121, ValueDataType.S32, ValueRW_Type.ReadWrite, "Scratch register - just somewhere to play write and read."); - - /// - /// @brief Scratch register - just somewhere to play write and read. - /// - internal static readonly Device_value Met_U64Scratch_U64 = new Device_value("Met_U64Scratch_U64", 1123, ValueDataType.U64, ValueRW_Type.ReadWrite, "Scratch register - just somewhere to play write and read."); - - /// - /// @brief Scratch register - just somewhere to play write and read. - /// - internal static readonly Device_value Met_S64Scratch_S64 = new Device_value("Met_S64Scratch_S64", 1127, ValueDataType.S64, ValueRW_Type.ReadWrite, "Scratch register - just somewhere to play write and read."); - - /// - /// @brief Scratch register - just somewhere to play write and read. - /// - internal static readonly Device_value Met_U128Scratch_U128 = new Device_value("Met_U128Scratch_U128", 1131, ValueDataType.U128, ValueRW_Type.ReadWrite, "Scratch register - just somewhere to play write and read."); - - /// - /// @brief Scratch register - just somewhere to play write and read. - /// - internal static readonly Device_value Met_F32Scratch_F32 = new Device_value("Met_F32Scratch_F32", 1139, ValueDataType.F32, ValueRW_Type.ReadWrite, "Scratch register - just somewhere to play write and read."); - - /// - /// @brief Scratch register, min and max - just somewhere to play write and read. - /// - internal static readonly Device_value Met_F32Scratch_MinMax = new Device_value("Met_F32Scratch_MinMax", 1141, ValueDataType.F32, ValueRW_Type.ReadWrite, "Scratch register, min and max - just somewhere to play write and read."); - - /// - /// @brief Scratch register, Enum - just somewhere to play write and read. - /// - internal static readonly Device_value Met_U16Scratch_Enum = new Device_value("Met_U16Scratch_Enum", 1143, ValueDataType.U16, ValueRW_Type.ReadWrite, "Scratch register, Enum - just somewhere to play write and read."); - - /// - /// @brief Scratch register, const - just somewhere to play write and read. - /// - internal static readonly Device_value Met_U16Scratch_Const = new Device_value("Met_U16Scratch_Const", 1144, ValueDataType.U16, ValueRW_Type.ReadWrite, "Scratch register, const - just somewhere to play write and read."); - - -/*------------------------------ block number 16 ------------------------------*/ - - /// - /// @brief Int. ADC value: batt_adc as Voltage - /// - internal static readonly Device_value Met_F32IntADCSample_battADC_V = new Device_value("Met_F32IntADCSample_battADC_V", 3200, ValueDataType.F32, ValueRW_Type.ReadWrite, "Int. ADC value: batt_adc as Voltage"); - - /// - /// @brief Tick count, for ADC data sample - /// - internal static readonly Device_value Met_U32IntADCSample_battADC_tick = new Device_value("Met_U32IntADCSample_battADC_tick", 3202, ValueDataType.U32, ValueRW_Type.ReadWrite, "Tick count, for ADC data sample"); - - /// - /// @brief Int. ADC value: diag_p_adc as Voltage - /// - internal static readonly Device_value Met_F32IntADCSample_diagP_adc_V = new Device_value("Met_F32IntADCSample_diagP_adc_V", 3204, ValueDataType.F32, ValueRW_Type.ReadWrite, "Int. ADC value: diag_p_adc as Voltage"); - - /// - /// @brief Tick count, for ADC data sample - /// - internal static readonly Device_value Met_U32IntADCSample_diagP_adc_tick = new Device_value("Met_U32IntADCSample_diagP_adc_tick", 3206, ValueDataType.U32, ValueRW_Type.ReadWrite, "Tick count, for ADC data sample"); - - /// - /// @brief Int. ADC value: diag_n_adc as Voltage - /// - internal static readonly Device_value Met_F32IntADCSample_diagN_adc_V = new Device_value("Met_F32IntADCSample_diagN_adc_V", 3208, ValueDataType.F32, ValueRW_Type.ReadWrite, "Int. ADC value: diag_n_adc as Voltage"); - - /// - /// @brief Tick count, for ADC data sample - /// - internal static readonly Device_value Met_U32IntADCSample_diagN_adc_tick = new Device_value("Met_U32IntADCSample_diagN_adc_tick", 3210, ValueDataType.U32, ValueRW_Type.ReadWrite, "Tick count, for ADC data sample"); - - /// - /// @brief Int. ADC value: diag_c_adc as Voltage - /// - internal static readonly Device_value Met_F32IntADCSample_diagC_adc_V = new Device_value("Met_F32IntADCSample_diagC_adc_V", 3212, ValueDataType.F32, ValueRW_Type.ReadWrite, "Int. ADC value: diag_c_adc as Voltage"); - - /// - /// @brief Tick count, for ADC data sample - /// - internal static readonly Device_value Met_U32IntADCSample_diagC_adc_tick = new Device_value("Met_U32IntADCSample_diagC_adc_tick", 3214, ValueDataType.U32, ValueRW_Type.ReadWrite, "Tick count, for ADC data sample"); - - /// - /// @brief Int. ADC value: vref_common_adc as Voltage - /// - internal static readonly Device_value Met_F32IntADCSample_vrefCommonADC_V = new Device_value("Met_F32IntADCSample_vrefCommonADC_V", 3216, ValueDataType.F32, ValueRW_Type.ReadWrite, "Int. ADC value: vref_common_adc as Voltage"); - - /// - /// @brief Tick count, for ADC data sample - /// - internal static readonly Device_value Met_U32IntADCSample_vrefCommonADC_tick = new Device_value("Met_U32IntADCSample_vrefCommonADC_tick", 3218, ValueDataType.U32, ValueRW_Type.ReadWrite, "Tick count, for ADC data sample"); - - /// - /// @brief Int. ADC value: vref_adc as Voltage - /// - internal static readonly Device_value Met_F32IntADCSample_vrefADC_V = new Device_value("Met_F32IntADCSample_vrefADC_V", 3220, ValueDataType.F32, ValueRW_Type.ReadWrite, "Int. ADC value: vref_adc as Voltage"); - - /// - /// @brief Tick count, for ADC data sample - /// - internal static readonly Device_value Met_U32IntADCSample_vrefADC_tick = new Device_value("Met_U32IntADCSample_vrefADC_tick", 3222, ValueDataType.U32, ValueRW_Type.ReadWrite, "Tick count, for ADC data sample"); - - /// - /// @brief Int. ADC value: temperature_Cdiv10_adc as Voltage - /// - internal static readonly Device_value Met_F32IntADCSample_temperature_C = new Device_value("Met_F32IntADCSample_temperature_C", 3224, ValueDataType.F32, ValueRW_Type.ReadWrite, "Int. ADC value: temperature_Cdiv10_adc as Voltage"); - - /// - /// @brief Tick count, for ADC data sample - /// - internal static readonly Device_value Met_U32IntADCSample_temperature_C_tick = new Device_value("Met_U32IntADCSample_temperature_C_tick", 3226, ValueDataType.U32, ValueRW_Type.ReadWrite, "Tick count, for ADC data sample"); - - /// - /// @brief Int. ADC value: int_batt_adc as Voltage - /// - internal static readonly Device_value Met_F32IntADCSample_intBattADC_V = new Device_value("Met_F32IntADCSample_intBattADC_V", 3228, ValueDataType.F32, ValueRW_Type.ReadWrite, "Int. ADC value: int_batt_adc as Voltage"); - - /// - /// @brief Tick count, for ADC data sample - /// - internal static readonly Device_value Met_U32IntADCSample_intBattADC_tick = new Device_value("Met_U32IntADCSample_intBattADC_tick", 3230, ValueDataType.U32, ValueRW_Type.ReadWrite, "Tick count, for ADC data sample"); - - -/*------------------------------ block number 17 ------------------------------*/ - - /// - /// @brief Extern ADC, Electrode diff as Voltage - /// - internal static readonly Device_value Met_F32ExtADC_ElectrodeDiff_V = new Device_value("Met_F32ExtADC_ElectrodeDiff_V", 3250, ValueDataType.F32, ValueRW_Type.ReadWrite, "Extern ADC, Electrode diff as Voltage"); - - /// - /// @brief Tick count, for Extern ADC, Electrode diff - /// - internal static readonly Device_value Met_S32ExtADC_ElectrodeDiff_tics = new Device_value("Met_S32ExtADC_ElectrodeDiff_tics", 3252, ValueDataType.S32, ValueRW_Type.ReadWrite, "Tick count, for Extern ADC, Electrode diff"); - - /// - /// @brief Extern ADC, Coil diff as Current - /// - internal static readonly Device_value Met_F32ExtADC_CoilDiff_A = new Device_value("Met_F32ExtADC_CoilDiff_A", 3254, ValueDataType.F32, ValueRW_Type.ReadWrite, "Extern ADC, Coil diff as Current"); - - /// - /// @brief Tick count, for Extern ADC, Coil diff - /// - internal static readonly Device_value Met_S32ExtADC_CoilDiff_tics = new Device_value("Met_S32ExtADC_CoilDiff_tics", 3256, ValueDataType.S32, ValueRW_Type.ReadWrite, "Tick count, for Extern ADC, Coil diff"); - - -/*------------------------------ block number 18 ------------------------------*/ - - /// - /// @brief Int. ADC value: batt_adc as Voltage - /// - internal static readonly Device_value Met_F32IntADCSample_battADC_offset = new Device_value("Met_F32IntADCSample_battADC_offset", 3300, ValueDataType.F32, ValueRW_Type.ReadWrite, "Int. ADC value: batt_adc as Voltage"); - - /// - /// @brief Tick count, for ADC data sample - /// - internal static readonly Device_value Met_F32IntADCSample_battADC_scale = new Device_value("Met_F32IntADCSample_battADC_scale", 3302, ValueDataType.F32, ValueRW_Type.ReadWrite, "Tick count, for ADC data sample"); - - /// - /// @brief Int. ADC value: diag_p_adc as Voltage - /// - internal static readonly Device_value Met_F32IntADCSample_diagP_adc_offset = new Device_value("Met_F32IntADCSample_diagP_adc_offset", 3304, ValueDataType.F32, ValueRW_Type.ReadWrite, "Int. ADC value: diag_p_adc as Voltage"); - - /// - /// @brief Tick count, for ADC data sample - /// - internal static readonly Device_value Met_F32IntADCSample_diagP_adc_scale = new Device_value("Met_F32IntADCSample_diagP_adc_scale", 3306, ValueDataType.F32, ValueRW_Type.ReadWrite, "Tick count, for ADC data sample"); - - /// - /// @brief Int. ADC value: diag_n_adc as Voltage - /// - internal static readonly Device_value Met_F32IntADCSample_diagN_adc_offset = new Device_value("Met_F32IntADCSample_diagN_adc_offset", 3308, ValueDataType.F32, ValueRW_Type.ReadWrite, "Int. ADC value: diag_n_adc as Voltage"); - - /// - /// @brief Tick count, for ADC data sample - /// - internal static readonly Device_value Met_F32IntADCSample_diagN_adc_scale = new Device_value("Met_F32IntADCSample_diagN_adc_scale", 3310, ValueDataType.F32, ValueRW_Type.ReadWrite, "Tick count, for ADC data sample"); - - /// - /// @brief Int. ADC value: diag_c_adc as Voltage - /// - internal static readonly Device_value Met_F32IntADCSample_diagC_adc_offset = new Device_value("Met_F32IntADCSample_diagC_adc_offset", 3312, ValueDataType.F32, ValueRW_Type.ReadWrite, "Int. ADC value: diag_c_adc as Voltage"); - - /// - /// @brief Tick count, for ADC data sample - /// - internal static readonly Device_value Met_F32IntADCSample_diagC_adc_scale = new Device_value("Met_F32IntADCSample_diagC_adc_scale", 3314, ValueDataType.F32, ValueRW_Type.ReadWrite, "Tick count, for ADC data sample"); - - /// - /// @brief Int. ADC value: vref_common_adc as Voltage - /// - internal static readonly Device_value Met_F32IntADCSample_vrefCommonADC_offset = new Device_value("Met_F32IntADCSample_vrefCommonADC_offset", 3316, ValueDataType.F32, ValueRW_Type.ReadWrite, "Int. ADC value: vref_common_adc as Voltage"); - - /// - /// @brief Tick count, for ADC data sample - /// - internal static readonly Device_value Met_F32IntADCSample_vrefCommonADC_scale = new Device_value("Met_F32IntADCSample_vrefCommonADC_scale", 3318, ValueDataType.F32, ValueRW_Type.ReadWrite, "Tick count, for ADC data sample"); - - /// - /// @brief Int. ADC value: vref_adc as Voltage - /// - internal static readonly Device_value Met_F32IntADCSample_vrefADC_offset = new Device_value("Met_F32IntADCSample_vrefADC_offset", 3320, ValueDataType.F32, ValueRW_Type.ReadWrite, "Int. ADC value: vref_adc as Voltage"); - - /// - /// @brief Tick count, for ADC data sample - /// - internal static readonly Device_value Met_F32IntADCSample_vrefADC_scale = new Device_value("Met_F32IntADCSample_vrefADC_scale", 3322, ValueDataType.F32, ValueRW_Type.ReadWrite, "Tick count, for ADC data sample"); - - /// - /// @brief Int. ADC value: temperature_Cdiv10_adc as Voltage - /// - internal static readonly Device_value Met_F32IntADCSample_temperature_offset = new Device_value("Met_F32IntADCSample_temperature_offset", 3324, ValueDataType.F32, ValueRW_Type.ReadWrite, "Int. ADC value: temperature_Cdiv10_adc as Voltage"); - - /// - /// @brief Tick count, for ADC data sample - /// - internal static readonly Device_value Met_F32IntADCSample_temperature_C_scale = new Device_value("Met_F32IntADCSample_temperature_C_scale", 3326, ValueDataType.F32, ValueRW_Type.ReadWrite, "Tick count, for ADC data sample"); - - /// - /// @brief Int. ADC value: int_batt_adc as Voltage - /// - internal static readonly Device_value Met_F32IntADCSample_intBattADC_offset = new Device_value("Met_F32IntADCSample_intBattADC_offset", 3328, ValueDataType.F32, ValueRW_Type.ReadWrite, "Int. ADC value: int_batt_adc as Voltage"); - - /// - /// @brief Tick count, for ADC data sample - /// - internal static readonly Device_value Met_F32IntADCSample_intBattADC_scale = new Device_value("Met_F32IntADCSample_intBattADC_scale", 3330, ValueDataType.F32, ValueRW_Type.ReadWrite, "Tick count, for ADC data sample"); - - -/*------------------------------ block number 19 ------------------------------*/ - - /// - /// @brief Extern ADC, Electrode diff offset - /// - internal static readonly Device_value Met_F32ExtADC_ElectrodeDiff_offset = new Device_value("Met_F32ExtADC_ElectrodeDiff_offset", 3350, ValueDataType.F32, ValueRW_Type.ReadWrite, "Extern ADC, Electrode diff offset"); - - /// - /// @brief Extern ADC, Electrode diff scale, ref/adc bits, 2.5/2^23 - /// - internal static readonly Device_value Met_F32ExtADC_ElectrodeDiff_scale = new Device_value("Met_F32ExtADC_ElectrodeDiff_scale", 3352, ValueDataType.F32, ValueRW_Type.ReadWrite, "Extern ADC, Electrode diff scale, ref/adc bits, 2.5/2^23"); - - /// - /// @brief Extern ADC, Coil diff offset as Voltage - /// - internal static readonly Device_value Met_F32ExtADC_CoilDiff_offset = new Device_value("Met_F32ExtADC_CoilDiff_offset", 3354, ValueDataType.F32, ValueRW_Type.ReadWrite, "Extern ADC, Coil diff offset as Voltage"); - - /// - /// @brief Extern ADC, Coil diff scale, ref/adc bits, 2.5/2^23 - /// - internal static readonly Device_value Met_F32ExtADC_CoilDiff_scale = new Device_value("Met_F32ExtADC_CoilDiff_scale", 3356, ValueDataType.F32, ValueRW_Type.ReadWrite, "Extern ADC, Coil diff scale, ref/adc bits, 2.5/2^23"); - - -/*------------------------------ block number 20 ------------------------------*/ - - /// - /// @brief Updated with EEPROM operation. - /// - internal static readonly Device_value Met_U16EEPROM_Status = new Device_value("Met_U16EEPROM_Status", 6000, ValueDataType.U16, ValueRW_Type.ReadWrite, "Updated with EEPROM operation."); - - /// - /// @brief Updated with EEPROM operation. - /// - internal static readonly Device_value Met_U16EEPROM_LastErrorCode = new Device_value("Met_U16EEPROM_LastErrorCode", 6001, ValueDataType.U16, ValueRW_Type.ReadWrite, "Updated with EEPROM operation."); - - -/*------------------------------ block number 21 ------------------------------*/ - - /// - /// @brief Updated with 1_WIRE operation. - /// - internal static readonly Device_value Met_U16_OneWIRE_Status = new Device_value("Met_U16_OneWIRE_Status", 6100, ValueDataType.U16, ValueRW_Type.ReadWrite, "Updated with 1_WIRE operation."); - - /// - /// @brief Updated with 1_WIRE operation. - /// - internal static readonly Device_value Met_U16_OneWIRE_Error = new Device_value("Met_U16_OneWIRE_Error", 6101, ValueDataType.U16, ValueRW_Type.ReadWrite, "Updated with 1_WIRE operation."); - - /// - /// @brief Issue commands to control 1_WIRE operation - /// - internal static readonly Device_value Met_U16_OneWIRE_Ctrl = new Device_value("Met_U16_OneWIRE_Ctrl", 6102, ValueDataType.U16, ValueRW_Type.ReadWrite, "Issue commands to control 1_WIRE operation"); - - -/*------------------------------ block number 22 ------------------------------*/ - - /// - /// @brief Measured resistance between electrodes, in ohms, 0deg phase. - /// - internal static readonly Device_value Met_F32_Zi_ohms = new Device_value("Met_F32_Zi_ohms", 7000, ValueDataType.F32, ValueRW_Type.ReadWrite, "Measured resistance between electrodes, in ohms, 0deg phase."); - - /// - /// @brief Measured resistance between electrodes, in ohms, 90deg phase. - /// - internal static readonly Device_value Met_F32_Zj_ohms = new Device_value("Met_F32_Zj_ohms", 7002, ValueDataType.F32, ValueRW_Type.ReadWrite, "Measured resistance between electrodes, in ohms, 90deg phase."); - - /// - /// @brief Electrode resistance max, (mod = sqrt(Zi^2+Zj^2)), for full decision. - /// - internal static readonly Device_value Met_F32_FullLevelThreshold_ohms = new Device_value("Met_F32_FullLevelThreshold_ohms", 7004, ValueDataType.F32, ValueRW_Type.ReadWrite, "Electrode resistance max, (mod = sqrt(Zi^2+Zj^2)), for full decision."); - - /// - /// @brief Electrode resistance min, (mod = sqrt(Zi^2+Zj^2)), for empty decision. - /// - internal static readonly Device_value Met_F32_EmptyLevelThreshold_ohms = new Device_value("Met_F32_EmptyLevelThreshold_ohms", 7006, ValueDataType.F32, ValueRW_Type.ReadWrite, "Electrode resistance min, (mod = sqrt(Zi^2+Zj^2)), for empty decision."); - - /// - /// @brief Electrode resistance min, (arg = atan(Zq/Zi)), for empty decision. - /// - internal static readonly Device_value Met_S16_EmptyLevelThreshold_minPhs_degs = new Device_value("Met_S16_EmptyLevelThreshold_minPhs_degs", 7008, ValueDataType.S16, ValueRW_Type.ReadWrite, "Electrode resistance min, (arg = atan(Zq/Zi)), for empty decision."); - - /// - /// @brief Electrode resistance min, (arg = atan(Zq/Zi)), for empty decision. - /// - internal static readonly Device_value Met_S16_EmptyLevelThreshold_maxPhs_degs = new Device_value("Met_S16_EmptyLevelThreshold_maxPhs_degs", 7009, ValueDataType.S16, ValueRW_Type.ReadWrite, "Electrode resistance min, (arg = atan(Zq/Zi)), for empty decision."); - - /// - /// @brief Filter coef for EPD decision. - /// - internal static readonly Device_value Met_F32_ZiZj_FilterA = new Device_value("Met_F32_ZiZj_FilterA", 7010, ValueDataType.F32, ValueRW_Type.ReadWrite, "Filter coef for EPD decision."); - - /// - /// @brief Filter coef for EPD decision. - /// - internal static readonly Device_value Met_F32_ZiZj_FilterB = new Device_value("Met_F32_ZiZj_FilterB", 7012, ValueDataType.F32, ValueRW_Type.ReadWrite, "Filter coef for EPD decision."); - - -/*------------------------------ block number 23 ------------------------------*/ - - /// - /// @brief EPD state - /// - internal static readonly Device_value Met_U16EmptyPipeControl = new Device_value("Met_U16EmptyPipeControl", 7050, ValueDataType.U16, ValueRW_Type.ReadWrite, "EPD state"); - - /// - /// @brief EPD state - /// - internal static readonly Device_value Met_U16EmptyPipeStatus = new Device_value("Met_U16EmptyPipeStatus", 7051, ValueDataType.U16, ValueRW_Type.ReadWrite, "EPD state"); - - /// - /// @brief EPD flags, including decison - /// - internal static readonly Device_value Met_U16EmptyPipeFlags = new Device_value("Met_U16EmptyPipeFlags", 7052, ValueDataType.U16, ValueRW_Type.ReadWrite, "EPD flags, including decison"); - - -/*------------------------------ block number 24 ------------------------------*/ - - /// - /// @brief Low flow can be set on or off etc. - /// - internal static readonly Device_value Met_U16LowFlowControl = new Device_value("Met_U16LowFlowControl", 7100, ValueDataType.U16, ValueRW_Type.ReadWrite, "Low flow can be set on or off etc."); - - /// - /// @brief How is it going? Low flow - or not. - /// - internal static readonly Device_value Met_U16LowFlowStatus = new Device_value("Met_U16LowFlowStatus", 7101, ValueDataType.U16, ValueRW_Type.ReadWrite, "How is it going? Low flow - or not."); - - /// - /// @brief Can be reset by master. Does not wrap. - /// - internal static readonly Device_value Met_S32LowFlow_ResettableTotal_l = new Device_value("Met_S32LowFlow_ResettableTotal_l", 7102, ValueDataType.S32, ValueRW_Type.ReadWrite, "Can be reset by master. Does not wrap."); - - /// - /// @brief micro-litres per second below which we let it go. - /// - internal static readonly Device_value Met_U32LowFlowVolThresh_ulps = new Device_value("Met_U32LowFlowVolThresh_ulps", 7104, ValueDataType.U32, ValueRW_Type.ReadWrite, "micro-litres per second below which we let it go."); - - /// - /// @brief micro-litres per second below which we let it go. - /// - internal static readonly Device_value Met_U16LowFlowTimeThresh_s = new Device_value("Met_U16LowFlowTimeThresh_s", 7106, ValueDataType.U16, ValueRW_Type.ReadWrite, "micro-litres per second below which we let it go."); - - -/*------------------------------ block number 25 ------------------------------*/ - - /// - /// @brief Rev flow can be set on or off etc. - /// - internal static readonly Device_value Met_U16RevFlowControl = new Device_value("Met_U16RevFlowControl", 7150, ValueDataType.U16, ValueRW_Type.ReadWrite, "Rev flow can be set on or off etc."); - - /// - /// @brief How is it going? Rev flow - or not. - /// - internal static readonly Device_value Met_U16RevFlowStatus = new Device_value("Met_U16RevFlowStatus", 7151, ValueDataType.U16, ValueRW_Type.ReadWrite, "How is it going? Rev flow - or not."); - - /// - /// @brief Can be reset by master. Does not wrap. - /// - internal static readonly Device_value Met_S32RevFlow_ResettableTotal_l = new Device_value("Met_S32RevFlow_ResettableTotal_l", 7152, ValueDataType.S32, ValueRW_Type.ReadWrite, "Can be reset by master. Does not wrap."); - - /// - /// @brief micro-litres per second below which we let it go. - /// - internal static readonly Device_value Met_U32RevFlowVolThresh_ulps = new Device_value("Met_U32RevFlowVolThresh_ulps", 7154, ValueDataType.U32, ValueRW_Type.ReadWrite, "micro-litres per second below which we let it go."); - - /// - /// @brief micro-litres per second below which we let it go. - /// - internal static readonly Device_value Met_U16RevFlowTimeThresh_s = new Device_value("Met_U16RevFlowTimeThresh_s", 7156, ValueDataType.U16, ValueRW_Type.ReadWrite, "micro-litres per second below which we let it go."); - - -/*------------------------------ block number 26 ------------------------------*/ - - /// - /// @brief Rev flow can be set on or off etc. - /// - internal static readonly Device_value Met_U16ExcessFlowControl = new Device_value("Met_U16ExcessFlowControl", 7200, ValueDataType.U16, ValueRW_Type.ReadWrite, "Rev flow can be set on or off etc."); - - /// - /// @brief How is it going? Rev flow - or not. - /// - internal static readonly Device_value Met_U16ExcessFlowStatus = new Device_value("Met_U16ExcessFlowStatus", 7201, ValueDataType.U16, ValueRW_Type.ReadWrite, "How is it going? Rev flow - or not."); - - /// - /// @brief Can be reset by master. Does not wrap. - /// - internal static readonly Device_value Met_S32ExcessFlow_ResettableTotal_l = new Device_value("Met_S32ExcessFlow_ResettableTotal_l", 7202, ValueDataType.S32, ValueRW_Type.ReadWrite, "Can be reset by master. Does not wrap."); - - /// - /// @brief micro-litres per second below which we let it go. - /// - internal static readonly Device_value Met_U32ExcessFlowVolThresh_ulps = new Device_value("Met_U32ExcessFlowVolThresh_ulps", 7204, ValueDataType.U32, ValueRW_Type.ReadWrite, "micro-litres per second below which we let it go."); - - /// - /// @brief micro-litres per second below which we let it go. - /// - internal static readonly Device_value Met_U16ExcessFlowTimeThresh_s = new Device_value("Met_U16ExcessFlowTimeThresh_s", 7206, ValueDataType.U16, ValueRW_Type.ReadWrite, "micro-litres per second below which we let it go."); - - -/*------------------------------ block number 27 ------------------------------*/ - - /// - /// @brief Only uneven number allowed Changes will dynamic change Met_U16_...OnOffAvr_Max and Met_U16_...OnOffAvr - /// - internal static readonly Device_value Met_U16_FlowMedian_NoTapsReq = new Device_value("Met_U16_FlowMedian_NoTapsReq", 7220, ValueDataType.U16, ValueRW_Type.ReadWrite, "Only uneven number allowed Changes will dynamic change Met_U16_...OnOffAvr_Max and Met_U16_...OnOffAvr"); - - /// - /// @brief Minimum value limit for Met_U16...NoTapsReq - /// - internal static readonly Device_value Met_U16_FlowMedian_NoTapsReq_Min = new Device_value("Met_U16_FlowMedian_NoTapsReq_Min", 7221, ValueDataType.U16, ValueRW_Type.ReadWrite, "Minimum value limit for Met_U16...NoTapsReq"); - - /// - /// @brief Maximum value limit for Met_U16...NoTapsReq - /// - internal static readonly Device_value Met_U16_FlowMedian_NoTapsReq_Max = new Device_value("Met_U16_FlowMedian_NoTapsReq_Max", 7222, ValueDataType.U16, ValueRW_Type.ReadWrite, "Maximum value limit for Met_U16...NoTapsReq"); - - /// - /// @brief 0:Passthrough, n: No. Central values. (default: 1) Will dynamic change, if Met_U16...OnOffAvr_Max changes to lower value. - /// - internal static readonly Device_value Met_U16_FlowMedian_OnOffAvr = new Device_value("Met_U16_FlowMedian_OnOffAvr", 7223, ValueDataType.U16, ValueRW_Type.ReadWrite, "0:Passthrough, n: No. Central values. (default: 1) Will dynamic change, if Met_U16...OnOffAvr_Max changes to lower value."); - - /// - /// @brief Minimum value limit for Met_U16...OnOffAvr - /// - internal static readonly Device_value Met_U16_FlowMedian_OnOffAvr_Min = new Device_value("Met_U16_FlowMedian_OnOffAvr_Min", 7224, ValueDataType.U16, ValueRW_Type.ReadWrite, "Minimum value limit for Met_U16...OnOffAvr"); - - /// - /// @brief Maximum value limit for Met_U16...OnOffAvr Max is Dynamic changed and defined as (Met_U16...NoTapsReq - 2) - /// - internal static readonly Device_value Met_U16_FlowMedian_OnOffAvr_Max = new Device_value("Met_U16_FlowMedian_OnOffAvr_Max", 7225, ValueDataType.U16, ValueRW_Type.ReadWrite, "Maximum value limit for Met_U16...OnOffAvr Max is Dynamic changed and defined as (Met_U16...NoTapsReq - 2)"); - - /// - /// @brief 0:No options, 1:max-min based, 2:Running stdev based - /// - internal static readonly Device_value Met_U16_FlowMedian_Options = new Device_value("Met_U16_FlowMedian_Options", 7226, ValueDataType.U16, ValueRW_Type.ReadWrite, "0:No options, 1:max-min based, 2:Running stdev based"); - - /// - /// @brief Minimum limit allowed for Met_U16...Options - /// - internal static readonly Device_value Met_U16_FlowMedian_Options_Min = new Device_value("Met_U16_FlowMedian_Options_Min", 7227, ValueDataType.U16, ValueRW_Type.ReadWrite, "Minimum limit allowed for Met_U16...Options"); - - /// - /// @brief Maximum limit allowed for Met_U16...Options - /// - internal static readonly Device_value Met_U16_FlowMedian_Options_Max = new Device_value("Met_U16_FlowMedian_Options_Max", 7228, ValueDataType.U16, ValueRW_Type.ReadWrite, "Maximum limit allowed for Met_U16...Options"); - - /// - /// @brief Version of the median filter implementation - /// - internal static readonly Device_value Met_U16_FlowMedian_Version = new Device_value("Met_U16_FlowMedian_Version", 7229, ValueDataType.U16, ValueRW_Type.ReadWrite, "Version of the median filter implementation"); - - /// - /// @brief 0 is ok, 1 is interesting like starting up etc., 2 is interesting - like EPD blanking ... all TBD - /// - internal static readonly Device_value Met_U16_FlowMedian_Status = new Device_value("Met_U16_FlowMedian_Status", 7230, ValueDataType.U16, ValueRW_Type.ReadWrite, "0 is ok, 1 is interesting like starting up etc., 2 is interesting - like EPD blanking ... all TBD"); - - /// - /// @brief Time in seconds in the filter. Easily calculated from the current flip rate * number of taps, but may be useful. - /// - internal static readonly Device_value Met_F32_FlowMedian_TimeInFilter_s = new Device_value("Met_F32_FlowMedian_TimeInFilter_s", 7231, ValueDataType.F32, ValueRW_Type.ReadWrite, "Time in seconds in the filter. Easily calculated from the current flip rate * number of taps, but may be useful."); - - /// - /// @brief Central Filter value - /// - internal static readonly Device_value Met_F32_FlowMedian_CentralValue = new Device_value("Met_F32_FlowMedian_CentralValue", 7233, ValueDataType.F32, ValueRW_Type.ReadWrite, "Central Filter value"); - - /// - /// @brief Maximum Filter value - /// - internal static readonly Device_value Met_F32_FlowMedian_MaxValue = new Device_value("Met_F32_FlowMedian_MaxValue", 7235, ValueDataType.F32, ValueRW_Type.ReadWrite, "Maximum Filter value"); - - /// - /// @brief Minimum Filter value - /// - internal static readonly Device_value Met_F32_FlowMedian_MinValue = new Device_value("Met_F32_FlowMedian_MinValue", 7237, ValueDataType.F32, ValueRW_Type.ReadWrite, "Minimum Filter value"); - - /// - /// @brief Usually something like ((max-min)/(0.001*Q3)) but may be something else TBD. - /// - internal static readonly Device_value Met_F32_FlowMedian_NoiseMeasure = new Device_value("Met_F32_FlowMedian_NoiseMeasure", 7239, ValueDataType.F32, ValueRW_Type.ReadWrite, "Usually something like ((max-min)/(0.001*Q3)) but may be something else TBD."); - - } // End of reg_list - /*---------------------------------------------------------------------------*/ -} /* MagFlux6200_metrology_reg_list */ diff --git a/Common/modbus_master_csharp/Device/Met_6200v046.html b/Common/modbus_master_csharp/Device/Met_6200v046.html deleted file mode 100644 index 517770f9..00000000 --- a/Common/modbus_master_csharp/Device/Met_6200v046.html +++ /dev/null @@ -1,1154 +0,0 @@ - - - - - - - - -
-

Met_6200v046 from Fri Oct 27 10:21:57 2023

-
-
- -
-
- - - - diff --git a/Common/modbus_master_csharp/Device/ModbusDeviceCom.cs b/Common/modbus_master_csharp/Device/ModbusDeviceCom.cs deleted file mode 100644 index 30729db6..00000000 --- a/Common/modbus_master_csharp/Device/ModbusDeviceCom.cs +++ /dev/null @@ -1,334 +0,0 @@ -using MagFlux6200_metrology_reg_list; -using System.Collections.Generic; -using System.Numerics; -using System; -using XYLEM.Communication; -using System.IO.Ports; -using XYLEM.Base.Files; -using System.Threading.Tasks; -using XYLEM.Base; - -namespace XYLEM.Device -{ - public abstract class ModbusDeviceCom - { - protected ProgramLogCSVFileClass _logger = null; - - UInt16 _device_id = 1; - ModbusCom _md_com = null; - -#if (!DEBUG) - public Boolean DoComDebugPrint = false; -#else - //public Boolean DoComDebugPrint = true; - public Boolean DoComDebugPrint = false; -#if (DoComDebugPrint) -#warning DoTxValuePrint is true!!!!!!!!!!!!!!!! -#endif - const int ch_lng_dv = -15; - const int ch_lng_Version = -3; - const int ch_lng_MD_adr = -7; - const int dig_value = 10; -#endif - public ModbusDeviceCom() - { - - } - - public Boolean SetLogLocation(string path, string filename) - { - try - { - // If already created a _logger then - if (_logger != null) - { - // TODO: nothing to do right now - throw new Exception("Logger path can only be set one time"); - } - _logger = new ProgramLogCSVFileClass(path, filename); - return true; // Okay - } - catch (Exception ex) - { - Console.WriteLine($"Fail to setup program log path!\nPath:{path}\nMessage:{ex.Message}"); - } - return false; // failed - } - - - public void OpenLogFile() - { - // If already created a _logger then - if (_logger != null) - { - _logger.OpenProgramLogFile(); - } - } - - - public string GetComCounters() - { - if (_md_com == null) - { - return "T=? E=?"; - } - return $"T={_md_com.MsgCount} E={_md_com.MsgErrorCount}"; - - } - - - public Boolean IsConnected() - { - if (_md_com == null) - { - return false; - } - return _md_com.IsConnected(); - } - - public Boolean CloseConnection() - { - try - { - StopBackgroundChecking(); - if (_md_com != null) - { - /*There is nothing to close right now*/ - } - return true; // is okay - } - catch (Exception ex) - { - _logger.AddFuncLog(true, this.ToString(), "CloseConnection", "Fail to close connection", ex); - } - return false; // failed - } - - - - public virtual Boolean Connect(string comport, int baudRate, Parity parity) - { - try - { - {// Normal - if (IsConnected()) - { - CloseConnection(); - } - _md_com = new ModbusCom(comport, baudRate, parity); - return _md_com.IsConnected(); // return result - } - } - catch (Exception ex) - { - _logger.AddFuncLog(true, this.ToString(), "Connect", "Fail to open connection", ex); - } - return false; // failed - } - - #region Background service - System.Timers.Timer _BackgroundCheckingTimer = null; - - protected void StartBackgroundChecking() - { - StopBackgroundChecking(); // Make sure that a already running one is stopped - _BackgroundCheckingTimer = new System.Timers.Timer(1000); - _BackgroundCheckingTimer.Elapsed += (sender, e) => HandleBackgroundChecking(); - _BackgroundCheckingTimer.Start(); - } - - protected void StopBackgroundChecking() - { - if (_BackgroundCheckingTimer != null) - { - _BackgroundCheckingTimer.Stop(); - _BackgroundCheckingTimer.Close(); - _BackgroundCheckingTimer = null; - } - } - - /// - /// Handle checking DUT and background reading device values - /// - /// - private void HandleBackgroundChecking() - { - // Don't continue background service before the first has been serviced - if (_BackgroundCheckingTimer != null) - { - _BackgroundCheckingTimer.Stop(); - } - try - { - RunBackgroundReadServiceFunction(); - } - catch (Exception ex) - { -#if (DEBUG) - var message = $"Fail background service handling"; - Console.WriteLine(message); - AppConst.Logger.AddFuncLog(true, this.ToString(), "HandleBackgroundChecking()", message, ex); -#endif // #if (DEBUG) - } - // Don't continue background service before the first has been serviced - if (_BackgroundCheckingTimer != null) - { - _BackgroundCheckingTimer.Start(); - } - } - - /// - /// Need to be overwritten with a function for polling periodically read information from device for checking health etc. - /// - protected abstract void RunBackgroundReadServiceFunction(); - #endregion // Background service - - #region ---------------- Generic modbus communications --------------------- - protected UInt16? read_U16(Device_value dv) - { - var ret = _md_com.Read(_device_id, dv, new UInt16()); - if (ret.IsOkay) - { - var value = ret.Data; -#if (DEBUG) - if (DoComDebugPrint) - { - Console.WriteLine($"Read={dv,ch_lng_dv}: Reg_Ver={dv.Version,ch_lng_Version} Adr={dv.MD_adr,ch_lng_MD_adr} value={value,dig_value}"); - } -#endif // #if (DEBUG) - return value; - } - return null; // Error in reading - } - - protected UInt32? read_U32(Device_value dv) - { - var ret = _md_com.Read(_device_id, dv, new UInt32()); - if (ret.IsOkay) - { - var value = ret.Data; -#if (DEBUG) - if (DoComDebugPrint) - { - Console.WriteLine($"Read={dv,ch_lng_dv}: Reg_Ver={dv.Version,ch_lng_Version} Adr={dv.MD_adr,ch_lng_MD_adr} value={value,dig_value}"); - } -#endif // #if (DEBUG) - return value; - } - return null; // Error in reading - } - - - protected Single? read_F32(Device_value dv) - { - var ret = _md_com.Read(_device_id, dv, new Single()); - if (ret.IsOkay) - { - var value = ret.Data; -#if (DEBUG) - if (DoComDebugPrint) - { - Console.WriteLine($"Read={dv,ch_lng_dv}: Reg_Ver={dv.Version,ch_lng_Version} Adr={dv.MD_adr,ch_lng_MD_adr} value={value,dig_value}"); - } -#endif // #if (DEBUG) - return value; - } - return null; // Error in reading - } - - protected BigInteger? read_U128(Device_value dv) - { - var ret = _md_com.Read(_device_id, dv, new BigInteger()); - if (ret.IsOkay) - { - var value = ret.Data; -#if (DEBUG) - if (DoComDebugPrint) - { - Console.WriteLine($"Read={dv,ch_lng_dv}: Reg_Ver={dv.Version,ch_lng_Version} Adr={dv.MD_adr,ch_lng_MD_adr} value={value,dig_value}"); - } -#endif // #if (DEBUG) - return value; - } - return null; // Error in reading - } - - protected List read(List dvList) - { - var ret = _md_com.Read(_device_id, dvList); - if (ret.Count > 0) - { -#if (DEBUG) - foreach (var data in ret) - { - if (DoComDebugPrint) - { - var dv = data.Info; - Console.WriteLine($"Read={dv,ch_lng_dv}: Reg_Ver={dv.Version,ch_lng_Version} Adr={dv.MD_adr,ch_lng_MD_adr} value={data.Data,dig_value}"); - } - } -#endif // #if (DEBUG) - return ret; - } - return null; // Error in reading - } - - - - protected bool write_U16(Device_value dv, UInt16 value) - { - var isOkay = _md_com.Write(_device_id, dv, value); - if (!isOkay) - { -#if (DEBUG) - if (DoComDebugPrint) - { - Console.WriteLine($"Failed Write={dv,ch_lng_dv}: Reg_Ver={dv.Version,ch_lng_Version} Adr={dv.MD_adr,ch_lng_MD_adr} value={value,dig_value}"); - } -#endif // #if (DEBUG) - } - return isOkay; // Error in reading - } - - protected bool write_U32(Device_value dv, UInt32 value) - { - var isOkay = _md_com.Write(_device_id, dv, value); - if (!isOkay) - { -#if (DEBUG) - if (DoComDebugPrint) - { - Console.WriteLine($"Failed Write={dv,ch_lng_dv}: Reg_Ver={dv.Version,ch_lng_Version} Adr={dv.MD_adr,ch_lng_MD_adr} value={value,dig_value}"); - } -#endif // #if (DEBUG) - } - return isOkay; // Error in reading - } - - /// - /// Write multiple values in a optimized number of telegrams - /// - /// - /// - protected List write(List reqList) - { - var ret = _md_com.Write(_device_id, reqList); - if ((ret!=null) && (ret.Count > 0)) - { -#if (DEBUG) - foreach (var data in ret) - { - if (DoComDebugPrint) - { - var dv = data.Info; - Console.WriteLine($"Write={dv,ch_lng_dv}: Reg_Ver={dv.Version,ch_lng_Version} Adr={dv.MD_adr,ch_lng_MD_adr} value={data.Data,dig_value} {data.IsOkay}"); - } - } -#endif // #if (DEBUG) - return ret; - } - return null; // Error in reading - } - #endregion // #region ---------------- Generic modbus communications --------------------- - } -} \ No newline at end of file diff --git a/Common/modbus_master_csharp/Device/Value/ValueDataType.cs b/Common/modbus_master_csharp/Device/Value/ValueDataType.cs deleted file mode 100644 index 82b07575..00000000 --- a/Common/modbus_master_csharp/Device/Value/ValueDataType.cs +++ /dev/null @@ -1,93 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace XYLEM.Device.Value -{ - /// - /// Possible Type to use - /// - public enum ValueDataType - { - _Empty, - B8, - S8, - U8, - Bool, - U16, - U16MJK_SN, - U16MJK_BRAND_ID, - B16, - S16, - B32, - U32, - U32MJK_SN, - S32, - F32, - B64, - U64, - S64, - F64, - U128, - Array6B, - Array10B, - Array12B, - Array64B, - Array128B, - Array136B, - Array250B, - STR6E, - STR6S, - STR6, - STR8E, - STR8S, - STR8, - STR10E, - STR10S, - STR10, - STR16E, - STR16S, - STR16, - STR18E, - STR18S, - STR18, - STR20E, - STR20S, - STR20, - STR22E, - STR22S, - STR22, - STR30E, - STR30S, - STR30, - STR32E, - STR32S, - STR32, - STR34E, - STR40E, - STR40S, - STR40, - STR64E, - STR64S, - STR64, - STR100E, - STR100S, - STR100, - STR128E, - STR128S, - STR128, - STR240E, - STR240S, - STR240, - U16TEXTID, - U16UNIT, - U16UNITGROUP, - U32TIME_Y2000, - U32TIME_Y1970, - U32TIME_Y1970_UTC, - TIME_ymdhms, - TIME_ASCII_12B - } -} diff --git a/Common/modbus_master_csharp/Device/Value/ValueDescriptionFunc.cs b/Common/modbus_master_csharp/Device/Value/ValueDescriptionFunc.cs deleted file mode 100644 index b04fb890..00000000 --- a/Common/modbus_master_csharp/Device/Value/ValueDescriptionFunc.cs +++ /dev/null @@ -1,146 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Xml; - -namespace XYLEM.Device.Value -{ - public static class ValueDescriptionFunc - { - /// - /// Is data a text - /// - /// - public static Boolean IsText(this ValueDataType DataType) - { - switch (DataType) - { - case ValueDataType._Empty: - case ValueDataType.B8: - case ValueDataType.S8: - case ValueDataType.U8: - case ValueDataType.Bool: - case ValueDataType.U16: - case ValueDataType.U16MJK_SN: - case ValueDataType.U16MJK_BRAND_ID: - case ValueDataType.B16: - case ValueDataType.S16: - case ValueDataType.B32: - case ValueDataType.U32: - case ValueDataType.U32MJK_SN: - case ValueDataType.S32: - case ValueDataType.F32: - case ValueDataType.B64: - case ValueDataType.U64: - case ValueDataType.S64: - case ValueDataType.F64: - case ValueDataType.U128: - case ValueDataType.U16TEXTID: - case ValueDataType.U16UNIT: - case ValueDataType.U16UNITGROUP: - case ValueDataType.U32TIME_Y2000: - case ValueDataType.U32TIME_Y1970: - case ValueDataType.U32TIME_Y1970_UTC: - case ValueDataType.TIME_ASCII_12B: - case ValueDataType.TIME_ymdhms: - case ValueDataType.Array6B: - case ValueDataType.Array10B: - case ValueDataType.Array12B: - case ValueDataType.Array64B: - case ValueDataType.Array128B: - case ValueDataType.Array136B: - case ValueDataType.Array250B: - return false; - default: - if (DataType.ToString().StartsWith("STR")) - { - return true; // Is text - } - throw (new Exception("No known tag = " + DataType.ToString())); - } - } - - public static bool IsSMSText(this ValueDataType DataType) - { - if (DataType.IsText()) - { - var typeAsText = DataType.ToString(); - if (typeAsText.EndsWith("S")) - { // is Email - return true; - } - } - return false; - } - - public static bool IsEmailText(this ValueDataType DataType) - { - if (DataType.IsText()) - { - var typeAsText = DataType.ToString(); - if (typeAsText.EndsWith("E")) - { // is Email - return true; - } - } - return false; - } - - /// - /// Conversion has to be for string Quoted Printable Text - /// - /// - /// - public static bool IsEmailQuotedPrintableText(this ValueDataType DataType) - { - if (DataType.IsText()) - { - var typeAsText = DataType.ToString(); - if (typeAsText.EndsWith("Q")) - { // is Email - return true; - } - } - return false; - } - - /// - /// Conversion type for string is Windows 1252 - /// - /// - /// - public static bool IsWindows1252Text(this ValueDataType DataType) - { - if (DataType.IsText()) - { - var typeAsText = DataType.ToString(); - if (typeAsText.EndsWith("W1252")) - { // is Email - return true; - } - } - return false; - } - - /// - /// Conversion type for string is Windows 1252 and for the email recipient address that needs special handling - /// Needs to have "<>" in frond and end - /// - /// - /// - public static bool IsWindows1252TextRecipient(this ValueDataType DataType) - { - if (DataType.IsText()) - { - var typeAsText = DataType.ToString(); - if (typeAsText.EndsWith("W1252rec")) - { // is Email - return true; - } - } - return false; - } - } -} diff --git a/Common/modbus_master_csharp/Device/md_test.cs b/Common/modbus_master_csharp/Device/md_test.cs deleted file mode 100644 index cfb9b435..00000000 --- a/Common/modbus_master_csharp/Device/md_test.cs +++ /dev/null @@ -1,155 +0,0 @@ -using XYLEM.Device.Value; -using MagFlux6200_metrology_reg_list; -using XYLEM.Communication; -using System; -using System.IO.Ports; -using System.Threading.Tasks; - -namespace XYLEM.Device -{ - public enum MD_Reg_list - { - APP, - MET - } - - public class md_test - { - #region Access data from normal Application RS485 - internal static readonly Device_value App_F32FlowRate_m3ps = new Device_value("App_F32FlowRate_m3ps", 600, ValueDataType.F32, ValueRW_Type.ReadOnly, "App read flow"); - #endregion - - - const int ch_lng_dv = -15; - const int ch_lng_Version = -3; - const int ch_lng_MD_adr = -7; - const int dig_value = 10; - - public void Go(MD_Reg_list md_reg_list_selection, string serial_port, int baudRate, Parity parity) - { - ModbusCom md = new ModbusCom(serial_port, baudRate, parity); - ReadTests(md_reg_list_selection, md); - - } - - public void ReadTests(MD_Reg_list md_reg_list_selection, ModbusCom md) - { - var device_id = 1; - //{ // Testing read of specific registers - // var test_rw_items = new[] { reg_list.Met_U32MdbsRegsVer, reg_list.Met_U16DeviceType, reg_list.Met_U32DeviceTypeDetail }; - - // foreach (var testRead in test_rw_items) - // {// Testing - // Device_value dv = null; - // if (md_reg_list_selection == MD_Reg_list.MET) // Metrology - // { - // dv = testRead; - // var ret = md.Read(device_id, dv, new UInt32()); - // if (ret.IsOkay) - // { - // var value = ret.Data; - // Console.WriteLine($"Read={dv,ch_lng_dv}: Reg_Ver={dv.Version,ch_lng_Version} Adr={dv.MD_adr,-7} value={value,dig_value}"); - // } - // } - // else // To application - // { - // dv = null; // Not yet supported - // } - // } - // return; - //} - - - { // Read ID - Device_value dv = reg_list.Met_U16DeviceType; - var ret = md.Read(device_id, dv, new UInt16()); - if (ret.IsOkay) - { - var value = ret.Data; - Console.WriteLine($"Read={dv,ch_lng_dv}: Reg_Ver={dv.Version,ch_lng_Version} Adr={dv.MD_adr,ch_lng_MD_adr} value={value,dig_value}"); - } - } - {// Read Met_U32MdbsRegsVer - Device_value dv = null; - if (md_reg_list_selection == MD_Reg_list.MET) // Metrology - { - dv = reg_list.Met_U32MdbsRegsVer; - var ret = md.Read(device_id, dv, new UInt32()); - if (ret.IsOkay) - { - var value = ret.Data; - Console.WriteLine($"Read={dv,ch_lng_dv}: Reg_Ver={dv.Version,ch_lng_Version} Adr={dv.MD_adr,-7} value={value,dig_value}"); - } - } - else // To application - { - dv = null; // Not yet supported - } - } - {// Read flow - Device_value dv = null; - string unit = "m3/h"; - Single scale = 1.0f; - if (md_reg_list_selection == MD_Reg_list.MET) // Metrology - { - dv = reg_list.Met_F32FlowRate_lps; - scale = 3.6f; // From l/s to m3/h - } - else // To application - { - dv = App_F32FlowRate_m3ps; - scale = 3600f; // From m3/s to m3/h - } - var ret = md.Read(device_id, dv, new Single()); - if (ret.IsOkay) - { - var value = ret.Data; - Console.WriteLine($"Read={dv,ch_lng_dv}: Reg_Ver={dv.Version,ch_lng_Version} Adr={dv.MD_adr,-7} value={(value*scale),dig_value} {unit}"); - } - } - - {// Read / Write scratch - Device_value dv = null; - string unit = ""; - Single scale = 1.0f; - if (md_reg_list_selection == MD_Reg_list.MET) // Metrology - { - dv = reg_list.Met_F32Scratch_F32; - - Single value = Single.NaN; - { // read - var ret = md.Read(device_id, dv, new Single()); - if (ret.IsOkay) - { - value = ret.Data; - Console.WriteLine($"Read={dv,ch_lng_dv}: Reg_Ver={dv.Version,ch_lng_Version} Adr={dv.MD_adr,-7} value={(value*scale),dig_value} {unit}"); - } - } - { // write - value += 10; - if (!md.Write(device_id, dv, value)) - { - Console.WriteLine($"Write failed={dv,ch_lng_dv}: Reg_Ver={dv.Version,ch_lng_Version} Adr={dv.MD_adr,-7} value={value,dig_value} {unit}"); - } - else - { - Console.WriteLine($"Write={dv,ch_lng_dv}: Reg_Ver={dv.Version,ch_lng_Version} Adr={dv.MD_adr,-7} value={value,dig_value} {unit}"); - } - } - { // read back - var ret = md.Read(device_id, dv, new Single()); - if (ret.IsOkay) - { - value = ret.Data; ; - Console.WriteLine($"Read={dv,ch_lng_dv}: Reg_Ver={dv.Version,ch_lng_Version} Adr={dv.MD_adr,-7} value={(value*scale),dig_value} {unit}"); - } - } - } - else // To application - { - dv = null; // Not yet supported - } - } - } - } -} diff --git a/Common/modbus_master_csharp/Program.cs b/Common/modbus_master_csharp/Program.cs deleted file mode 100644 index 69fe52c5..00000000 --- a/Common/modbus_master_csharp/Program.cs +++ /dev/null @@ -1,1127 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.IO.Ports; -using XYLEM.Communication; -using System.Xml.Linq; -using XYLEM.Base; -using XYLEM.Communication.ValueEncoders; -using System.Linq.Expressions; -using System.Threading; -using MagFlux6200_metrology_reg_list; -using XYLEM.Device; -using static modbus_master_csharp.Program; -using System.Diagnostics.Contracts; -using System.Security.Policy; -using System.IO; -using System.Diagnostics; -using System.Xml.Schema; -using System.Net.NetworkInformation; - -namespace modbus_master_csharp -{ - internal class Program - { - /*For testing directly to metrology */ - static string[] argsDefault1 = new string[] { "TEST", "COM200", "115200", "N" }; - /*For metrology RS485 when transparent to metrology*/ - static string[] argsDefault2 = new string[] { "SAVE_CAL", "COM38" }; - - static string[] argsDefault2b = new string[] { "SAVE_CAL", "COM200", "115200", "N" }; - - static string[] argsDefault2c = new string[] { "SAVE_CAL", "COM200", "COM200", "115200", "N" }; - - // Having more ports - static string[] argsDefault3 = new string[] { "TEST", "COM38", "COM200" }; - - /*For metrology RS485 when transparent to metrology*/ - static string[] argsDefault4 = new string[] { "LOAD_CAL=\"0_sensor_cal_for_testing.xml\"", "COM38" }; - - /*For metrology RS485 when transparent to metrology*/ - static string[] argsDefault5 = new string[] { "SET_DN_SN=25,999999", "COM38" }; - - /*For metrology RS485 when transparent to metrology*/ - static string[] argsDebugLiveRun = new string[] { "TEST", "COM200" }; - - /*For metrology RS485 when transparent to metrology*/ - static string[] args_ExAbortCal = new string[] { "ABORT_CAL", "COM200" }; - -#if (DEBUG) - static bool LIVE_RUN = true; // Default = "true" to run with DUT communication or "false" to run without any DUT for communication and mock values - - /* Preload with program argument used for debugging*/ - static bool MAIN_ARGS_OVERWRITE = true; // true to overwrite run with "argsDebugLiveRun" [Default = false] - static string[] MAIN_ARGS_OVERWRITE_WITH = argsDebugLiveRun; //testing arguments in debug Default = "argsDebugLiveRun" -#else - static bool LIVE_RUN = true; // -#endif - - private static string GetArgHelpMessage(bool isArgErrorMessage, string[] args) - { - var name_exe = System.AppDomain.CurrentDomain.FriendlyName; - var message = $@" -Example of from command line: -Save calibrations only include COMx to use standard baudrate and parity setup: - ""{name_exe} {argsDefault2.ToSingleString(false)}"" - -or run functional test for 2 MagFlux - ""{name_exe} {argsDefault3.ToSingleString(false)}"" - -To run functional test and configure both COMx, baudrate, parity. (Used for com directly to Metrology) - ""{name_exe} {argsDefault1.ToSingleString(false)}"" - -To Load calibrations only include COMx to use standard baudrate and parity setup: - ""{name_exe} {argsDefault4.ToSingleString(false)}"" - -To Set DN [mm] and Sensor Serial number, only include COMx to use standard baudrate and parity setup: - ""{name_exe} {argsDefault5.ToSingleString(false)}"" - -To Abort calibrations only include COMx to use standard baudrate and parity setup: - ""{name_exe} {args_ExAbortCal.ToSingleString(false)}"" -"; - if (isArgErrorMessage) - { - var argsTxt = args != null ? args.ToSingleString(" ") : ""; - message = $"{new string('!', 50)}\nThere is errors in the Arguments list!\n{new string('!', 50)}\nWas: {name_exe} {argsTxt}\n" + message; - } - return message; - } - - #region Reused functions for testing - private static bool GetDeviceHealthy(IMagFluxRequestProtocol dut) - { - var name = nameof(dut.GetDeviceHealthy); - var isOkay = dut.GetDeviceHealthy(); - var message = $"{name}: {(isOkay ? "Is okay" : "Has errors")}"; - if (!isOkay) - { - if (dut.GetDeviceErrorMessages(out var errorList)) - { - Console.WriteLine(message + "\nError List:\n" + errorList.ToSingleString(false)); - } - else - { - Console.WriteLine(message); - } - } - return isOkay; - } - - private static void GetBasicDeviceInfo(IMagFluxRequestProtocol dut) - { - { - var name = nameof(dut.GetFwGitHash); - var isOkay = dut.GetFwGitHash(out var value_out); - var message = $"{name}: {(isOkay ? value_out : "!Read failed!")}"; - Console.WriteLine(message); - if (!isOkay) - { - throw new Exception(message); - } - } - { - var name = nameof(dut.GetSensorSerialNo); - var isOkay = dut.GetSensorSerialNo(out var value_out); - var message = $"{name}: {(isOkay ? value_out : "!Read failed!")}"; - Console.WriteLine(message); - if (!isOkay) - { - throw new Exception(message); - } - } - { - var name = nameof(dut.GetUniqueId); - var isOkay = dut.GetUniqueId(out var value_out); - var message = $"{name}: {(isOkay ? value_out : "!Read failed!")}"; - Console.WriteLine(message); - if (!isOkay) - { - throw new Exception(message); - } - } - { - var name = nameof(dut.GetFirmwareVersion); - var isOkay = dut.GetFirmwareVersion(out var value_out); - var message = $"{name}: {(isOkay ? value_out : "!Read failed!")}"; - Console.WriteLine(message); - if (!isOkay) - { - throw new Exception(message); - } - } - { - var name = nameof(dut.GetFwBuildDate); - var isOkay = dut.GetFwBuildDate(out var value_out); - var message = $"{name}: {(isOkay ? value_out : "!Read failed!")}"; - Console.WriteLine(message); - if (!isOkay) - { - throw new Exception(message); - } - } - } - - private static void GetBasicSensorInfo(IMagFluxRequestProtocol dut) - { - { - var name = nameof(dut.GetDn_mm); - var isOkay = dut.GetDn_mm(out var dn_mm_out); - var message = $"{name}: {(isOkay ? $"{dn_mm_out}mm" : "!Read failed!")}"; - Console.WriteLine(message); - if (!isOkay) - { - throw new Exception(message); - } - } - { - GetDeviceHealthy(dut); - } - } - - private static void AbortDeviceCalibration(IMagFluxRequestProtocol dut) - { - { - var name = nameof(dut.AbortDeviceForCalibration); - var isOkay = dut.AbortDeviceForCalibration(); - var message = $"{name}: {(isOkay ? "Done" : "!Abort failed!")}"; - Console.WriteLine(message); - if (!isOkay) - { - throw new Exception(message); - } - } - } - - private static bool GetCalibrationPoints(IMagFluxRequestProtocol dut, out CalibrationPoints value_out, bool printoutPoints) - { - var name = nameof(dut.GetCalibrationPoints); - var isOkay = dut.GetCalibrationPoints(out value_out); - if (printoutPoints) - { - Console.WriteLine($"{name}: Read points-> {(isOkay ? "\n"+value_out.ToString() : "!Read failed!")}"); - } - else - { - Console.WriteLine($"{name}: Reading calibration points {(isOkay ? "Okay" : "!Read failed!")}"); - } - return isOkay; - } - - private static bool SetCalibrationPoints(IMagFluxRequestProtocol dut, CalibrationPoints new_calibration_points) - { - var isOkay = dut.SetCalibrationPoints(new_calibration_points); - Console.WriteLine($"{nameof(dut.SetCalibrationPoints)}: {(isOkay ? "Okay" : "Failed")}\n{new_calibration_points}"); - return isOkay; - } - - private static bool PrepareDeviceForCalibration(IMagFluxRequestProtocol dut) - { - { // --- Test setting the device ready for the first measurement used for calibrations --- - { - var isOkay = dut.PrepareDeviceForCalibration(); - Console.WriteLine($"{nameof(dut.PrepareDeviceForCalibration)}: {(isOkay ? "Okay" : "Failed")}"); - if (!isOkay || !GetCalibrationPoints(dut, out var value_out, true/*printout points*/)) - { - GetDeviceHealthy(dut); - throw new Exception("Failed Setting device up for calibration"); - } - return isOkay; - } - } - } - - private static void RunTestCalibrationPoints(IMagFluxRequestProtocol dut, CalibrationPoints new_calibration_points) - { - { // Write calibrations - var isOkay = SetCalibrationPoints(dut, new_calibration_points); - if (!isOkay) - { - GetDeviceHealthy(dut); - throw new Exception("Failed writing the calibration to device"); - } - } - { // Get calibrations and check it - if (!GetCalibrationPoints(dut, out var value_out, false/*Don't printout*/) || (new_calibration_points != value_out)) - { - GetDeviceHealthy(dut); - var msg = $"Failed! Calibration is not the same read as written to device\nWritten:\n{new_calibration_points}\nRead:\n{value_out}"; - Console.WriteLine(msg); - throw new Exception(msg); - } - } - } - - /// - /// Read Sensor serial number - /// - /// - /// Return the Serial number - /// Will throw an exception if failing - private static UInt32 GetSensorSerialNo(IMagFluxRequestProtocol dut) - { - var isOkay = dut.GetSensorSerialNo(out var read_out); - UInt32 ret = 0; - if (isOkay) - { - isOkay = UInt32.TryParse(read_out, out var passed); - ret = passed; - } - { // Handle result - var name = nameof(dut.GetSensorSerialNo); - var message = $"Read {name}: {(isOkay ? ret.ToString() : "!failed!")}"; - Console.WriteLine(message); - if (!isOkay) - { - throw new Exception(message); - } - } - return ret; - } - - /// - /// Write Sensor serial number (Also check with a read back) - /// - /// what dut to write to - /// New serial number - /// Will throw an exception if failing - private static void SetSensorSerialNo(IMagFluxRequestProtocol dut, UInt32 new_SN) - { - var name = nameof(dut.SetSensorSerialNo); - Console.WriteLine($"Write of {name}: {new_SN.ToString()}"); - var isOkay = dut.SetSensorSerialNo(new_SN); - var read = GetSensorSerialNo(dut); - isOkay &= (read == new_SN); // Is the same as read ? - // Handle result - if (!isOkay) - { - var message = $"Write of {name}:!failed!"; - Console.WriteLine(message); - throw new Exception(message); - } - } - - /// - /// Read Sensor DN - /// - /// - /// Return DN [mm] - /// Will throw an exception if failing - private static UInt16 GetDn_mm(IMagFluxRequestProtocol dut) - { - var isOkay = dut.GetDn_mm(out var read_out); - UInt16 ret = 0; - if (isOkay) - { - isOkay = UInt16.TryParse(read_out.ToString(), out var passed); - ret = passed; - } - { // Handle result - var name = nameof(dut.GetDn_mm); - var message = $"Read {name}: {(isOkay ? ret.ToString() : "!failed!")}"; - Console.WriteLine(message); - if (!isOkay) - { - throw new Exception(message); - } - } - return ret; - } - - - /// - /// Write Sensor DN size (Also check with a read back) - /// - /// what dut to write to - /// New DN [mm] - /// Will throw an exception if failing - private static void SetDn_mm(IMagFluxRequestProtocol dut, UInt16 new_DN_mm) - { - var name = nameof(dut.SetDn_mm); - Console.WriteLine($"Write of {name}: {new_DN_mm.ToString()}"); - var isOkay = dut.SetDn_mm(new_DN_mm); - var read = GetDn_mm(dut); - isOkay &= (read == new_DN_mm); // Is the same as read ? - // Handle result - if (!isOkay) - { - var message = $"Write of {name}:!failed!"; - Console.WriteLine(message); - throw new Exception(message); - } - } - - - /// - /// save and load what is read from device - /// - /// - /// - private static void SaveCalPointsToTempFolder(IMagFluxRequestProtocol dut, CalibrationPoints value_out) - { - var tempSaveFolder = Environment.GetEnvironmentVariable("USERPROFILE")+@"\Downloads\"; - if (dut.GetSensorSerialNo(out var sensorSerial)) - { - dut.GetUniqueId(out var id); - var sensor_cal_file = tempSaveFolder+$"{sensorSerial}_sensor_cal_id_{id}_{DateTime.Now.ToFileTimeTxt()}.xml"; - Console.WriteLine($"Save to:\n{sensor_cal_file}"); - value_out.SaveToFile(sensor_cal_file); - // Check saved data - var to_saved_points = value_out.calibrations.ToArray(); - value_out.LoadFromFile(sensor_cal_file); - var loaded_points = value_out.calibrations.ToArray(); - if (!to_saved_points.SequenceEqual(loaded_points)) - { - var msg = $"Failed save of calibrations\nSaved:\n{to_saved_points}\nLoaded:\n{loaded_points}"; - Console.WriteLine(msg); - throw new Exception(msg); - } - } - } - - - - private static void SAVE_CAL(List duts) - { - { // Check Save all calibrations one DUT at a time - for (var dutIdx = 0; dutIdx < duts.Count(); dutIdx++) - { - var dut = duts[dutIdx]; - Console.WriteLine($"------------------- DUT{dutIdx+1} --------------------"); - try - { - GetBasicDeviceInfo(dut); - GetBasicSensorInfo(dut); - if (!GetCalibrationPoints(dut, out var value_out, true/*printout points*/)) - { - GetDeviceHealthy(dut); - throw new Exception("Failed saving device calibration"); - } - else - { - // save and load what is read from device - SaveCalPointsToTempFolder(dut, value_out); - } - } - catch (Exception ex) - { - dut.OpenLogFile(); - throw ex; - } - } - } - } - private static void ABORT_CAL(List duts) - { - { // Check Save all calibrations one DUT at a time - for (var dutIdx = 0; dutIdx < duts.Count(); dutIdx++) - { - var dut = duts[dutIdx]; - Console.WriteLine($"------------------- DUT{dutIdx+1} --------------------"); - try - { - GetBasicDeviceInfo(dut); - AbortDeviceCalibration(dut); - GetBasicSensorInfo(dut); - if (!GetCalibrationPoints(dut, out var value_out, true/*printout points*/)) - { - if (!PrepareDeviceForCalibration(dut)) - { - throw new Exception("Failed abort of device calibration"); - } - } - } - catch (Exception ex) - { - dut.OpenLogFile(); - throw ex; - } - } - } - } - - - /// - /// Load Calibrations - /// - /// - /// - private static void LOAD_CAL(List duts, List loadedCalibrations) - { - { // Check all Values one by one a DUT at a time - if (duts.Count() != loadedCalibrations.Count()) - { - throw new Exception("The number of calibrations is not the same as DUT's to load it in"); - } - Console.WriteLine("\nSave Calibrations before starting writing new calibrations\n"); - SAVE_CAL(duts); - Console.WriteLine("\nStarting writing new calibrations\n"); - for (var dutIdx = 0; dutIdx < duts.Count(); dutIdx++) - { - var dut = duts[dutIdx]; - Console.WriteLine($"------------------- DUT{dutIdx+1} --------------------"); - try - { - var new_calibration_points = loadedCalibrations[dutIdx]; - var isOkay = SetCalibrationPoints(dut, new_calibration_points); - if (!isOkay) - { - throw new Exception($"Writing calibration Fail!\n{new_calibration_points}"); - } - } - catch (Exception ex) - { - dut.OpenLogFile(); - throw ex; - } - } - } - } - - - /// - /// Set DN and SN in device - /// - /// Device to write it to - /// DN size in [mm] - /// Sensor serial number - static void SET_DN_SN(IMagFluxRequestProtocol dut, UInt16 DN_mm, UInt32 sensor_sn) - { - Console.WriteLine($"------------------- Read current DUT Sensor DN and SN --------------------"); - try - { - GetDn_mm(dut); - GetSensorSerialNo(dut); - } - catch (Exception ex) - { - dut.OpenLogFile(); - throw ex; - } - - Console.WriteLine($"------------------- Set DUT Sensor DN and SN --------------------"); - try - { - SetDn_mm(dut, DN_mm); - SetSensorSerialNo(dut, sensor_sn); - } - catch (Exception ex) - { - dut.OpenLogFile(); - throw ex; - } - } - - static void TEST(List duts) - { - { // Check all Values one by one a DUT at a time - for (var dutIdx = 0; dutIdx < duts.Count(); dutIdx++) - { - Console.WriteLine($"------------------- DUT{dutIdx+1} --------------------"); - var dut = duts[dutIdx]; - try - { - //if (false) // Don't do basic reads - if (true) // Do basic read test - { - GetBasicDeviceInfo(dut); - { - var name = nameof(dut.GetFlowRate_lps); - var isOkay = dut.GetFlowRate_lps(out var value_out); - var message = $"{name}: {(isOkay ? $"{value_out} l/s or {value_out*3.6} m3/h" : "!Read failed!")}"; - Console.WriteLine(message); - if (!isOkay) - { - throw new Exception(message); - } - } - GetBasicSensorInfo(dut); - { // Always check that GetDeviceErrorMessages() even when there is not error, to test function - // Is only run in GetBasicSensorInfo() when there is an error - var name = nameof(dut.GetDeviceErrorMessages); - var isOkay = dut.GetDeviceErrorMessages(out var value_out); - var message = $"{name}: \n{(isOkay ? String.Join("\n", value_out) : "!Read failed!")}"; - Console.WriteLine(message); - if (!isOkay) - { - throw new Exception(message); - } - } - AbortDeviceCalibration(dut); - { - if (!GetCalibrationPoints(dut, out var value_out, true/*printout points*/)) - { - GetDeviceHealthy(dut); - } - else - { - // save and load what is read from device - SaveCalPointsToTempFolder(dut, value_out); - } - } - } - //if (false) // Do not execute calibration test - if (LIVE_RUN && true) // execute calibration test - { // A single calibration Test - PrepareDeviceForCalibration(dut); - { // --- Do a simple 2 point calibration with zero --- - var new_calibration_points = new CalibrationPoints( - new List { - new CalibrationPoint(0,0.001f), // 0 - new CalibrationPoint(1,1.002f), // 1 - new CalibrationPoint(2,2.003f), // 2 - }, - dut.CalibrationPointsMax // Extend to - ); - RunTestCalibrationPoints(dut, new_calibration_points); - } - { // --- Do a 6 point calibration with negative zero --- - var new_calibration_points = new CalibrationPoints( - new List { - new CalibrationPoint(0,-0.001f), // 0 - new CalibrationPoint(1,1.002f), // 1 - new CalibrationPoint(2,2.003f), // 2 - new CalibrationPoint(3,3.004f), // 3 - new CalibrationPoint(4,4.005f), // 4 - new CalibrationPoint(5,5.006f), // 5 - }, - dut.CalibrationPointsMax // Extend to - ); - RunTestCalibrationPoints(dut, new_calibration_points); - // save and load what is read from device - SaveCalPointsToTempFolder(dut, new_calibration_points); - } - { // --- Do a check of write of calibrations points that is not ordered correct --- - Console.WriteLine($"Test writing calibration in wrong order is failing and Background check service is running"); - { - var new_calibration_points = new CalibrationPoints( - new List { - new CalibrationPoint(0,-0.001f), // 0 - new CalibrationPoint(3,3.002f), // 1 Error this an error in order and need to brake calibrations - new CalibrationPoint(2,2.003f), // 2 - }, - dut.CalibrationPointsMax // Extend to - ); - var isOkay = SetCalibrationPoints(dut, new_calibration_points); - if (isOkay) - { - throw new Exception($"Writing calibration in wrong order needs to Fail!\n{new_calibration_points}"); - } - int maxWait_s = 5; - while (maxWait_s-- > 0) - { - Console.Write($"\rWait timer out in {maxWait_s}s for getting Device Healthy is returning showing error: "); - if (!dut.GetFlowRate_lps(out var value_out)) - { - throw new Exception($"read of flow failed during polling Device Healthy!"); - } - if (!GetDeviceHealthy(dut)) - { - Console.WriteLine($"\nGot correct replay that is Device healthy is \"false\""); - break; // Exit - } - Task.Delay(1000).GetAwaiter().GetResult(); - } - if (maxWait_s <= 0) - { - throw new Exception($"Device Healthy was returning \"false\" when calibration was failing!\n{new_calibration_points}"); - } - } - {// Cleanup by returning calibration to default - var isOkay = dut.PrepareDeviceForCalibration(); - if (!isOkay) - { - throw new Exception("Failing returning calibration to default!"); - } - } - } - { // --- Test Sensor new_SN test --- - Console.WriteLine($"--- Test Write a changed Sensor SN ---"); - // Read Sensor new_SN - Console.WriteLine($"1)Read current Sensor SN"); - UInt32 current = GetSensorSerialNo(dut); - // Create a new Sensor new_SN - var write_new = current + 1; - // Write function also check with a read and throw exception if failed - Console.WriteLine($"2)Change Sensor SN to {write_new}"); - SetSensorSerialNo(dut, write_new); - // Restore Sensor new_SN - Console.WriteLine($"3)Restore Sensor SN to {write_new}"); - SetSensorSerialNo(dut, current); - Console.WriteLine($"--- Finish Test ---"); - } - { // --- Test Sensor DN test --- - Console.WriteLine($"--- Test Write a changed Sensor DN Size ---"); - // SetDn_mm(dut, 20); // Insure a valid DN - - UInt16[] tests_dn_mm = { 50, 100 }; - - // Read Sensor DN - Console.WriteLine($"1)Read current Sensor DN Size"); - UInt16 current = GetDn_mm(dut); - - // Create a new Sensor DN - var write_new = tests_dn_mm.FirstOrDefault(dn => dn != current); - // Write function also check with a read and throw exception if failed - Console.WriteLine($"2)Change Sensor DN Size as {write_new}"); - SetDn_mm(dut, write_new); - // Restore Sensor DN - Console.WriteLine($"3)Restore original Sensor DN Size as {current}"); - SetDn_mm(dut, current); - Console.WriteLine($"--- Finish Test ---"); - } - } - } - catch (Exception ex) - { - dut.OpenLogFile(); - throw ex; - } - } - } - } - - #endregion - enum RUN_ACTION - { - UNKNOWN, - TEST, - SAVE_CAL, - LOAD_CAL, - ABORT_CAL, - SET_DN_SN, - SHOW_HELP, - } - - class args_inputs - { - public List comports; - public int? baudrate; - public Parity? parity; - public List loadedCalibrations; - public UInt16? DN_mm; - public UInt32? sensor_sn; - - public args_inputs() - { - comports = new List(); - baudrate = null; - parity = null; - loadedCalibrations = new List(); - sensor_sn = null; - } - } - - - #region Handle if arguments - static RUN_ACTION parse_arguments(string[] args, ref args_inputs ret_args_input) - { - bool gotArgs = (args.Length >= 1); - var run_task = RUN_ACTION.UNKNOWN; - var argErrors = gotArgs ? false : true; - // Use Default connection if nothing is provided by arguments - var arg_offset = 0; - if (args.Length > arg_offset) - { - { /*--------- Parse What task to do ---------*/ - if (args.Length > arg_offset) - { - if ("-H" == args[arg_offset].ToUpper()) - { - run_task = RUN_ACTION.SHOW_HELP; - } - else if ("TEST" == args[arg_offset].ToUpper()) - { - run_task = RUN_ACTION.TEST; - } - else if ("SAVE_CAL" == args[arg_offset].ToUpper()) - { - run_task = RUN_ACTION.SAVE_CAL; - } - else if ("ABORT_CAL" == args[arg_offset].ToUpper()) - { - run_task = RUN_ACTION.ABORT_CAL; - } - else if (args[arg_offset].ToUpper().StartsWith("SET_DN_SN=")) - { - run_task = RUN_ACTION.SET_DN_SN; - var values_as_txt = args[arg_offset].Split('=').Last().Split(','); - int value_offset = 0; - { // parse DN - var dn_mm_txt = values_as_txt[value_offset++]; - if (!UInt16.TryParse(dn_mm_txt, out var dn_mm)) - { - throw new InvalidDataException($"Error parsing DN \n{dn_mm_txt} as [mm]\n{GetArgHelpMessage(true, args)}"); - } - if ((dn_mm < 3) || (dn_mm > 3000)) - { - throw new InvalidDataException($"Error parsing DN \n{dn_mm}[mm] only 3 - 3000mm is allowed\n{GetArgHelpMessage(true, args)}"); - } - // Is okay - ret_args_input.DN_mm = dn_mm; - } - { // parse Sensor SN - var sn_txt = values_as_txt[value_offset++]; - if (!UInt32.TryParse(sn_txt, out var SN)) - { - throw new InvalidDataException($"Error parsing Sensor SN \n{sn_txt}\n{GetArgHelpMessage(true, args)}"); - } - // Is okay - ret_args_input.sensor_sn = SN; - } - - } - else if (args[arg_offset].ToUpper().StartsWith("LOAD_CAL=")) - { - run_task = RUN_ACTION.LOAD_CAL; - var calFilePath = args[arg_offset].Split('=').Last(); - var cal = new CalibrationPoints(); - if (!File.Exists(calFilePath) || !cal.LoadFromFile(calFilePath)) - { - throw new InvalidDataException($"Error load calibrations from\n {calFilePath}\n{GetArgHelpMessage(true, args)}"); - } - ret_args_input.loadedCalibrations.Add(cal); - } - else - { - throw new NotImplementedException($"Missing support for parameter {args[arg_offset]}\n{GetArgHelpMessage(true, args)}"); - } - } - } - arg_offset++; // Check next argument - { /*--------- Parse com port / ports ---------*/ - var exit = false; - var com_check_arg_offset = arg_offset; - while (!exit) - { - if (args.Length > com_check_arg_offset) - { - var arg = args[com_check_arg_offset].ToUpper(); - if ("COM" == arg.Remove(3)) - { - ret_args_input.comports.Add(arg); - arg_offset = com_check_arg_offset; // Save ret location for last added com - } - else if (ret_args_input.comports.Count <= 0) - { - Console.WriteLine($"Com port {arg} not understood."); - argErrors = true; - exit = true; - } - else - { - exit = true; // No more com to connect to - } - com_check_arg_offset++; // Check next argument - } - else - { - exit = true; // No more com to connect to - } - } - } - arg_offset++; // Check next argument - { /*--------- Parse Baud rate ---------*/ - if (args.Length > arg_offset) - { - var arg = args[arg_offset].ToUpper(); - var allowed_baudrate = new string[] { "9600", "19200", "38400", "57600", "115200", "230400" }; - if (allowed_baudrate.Contains(arg)) - { - ret_args_input.baudrate = int.Parse(arg); - } - else - { - Console.WriteLine($"Baud rate {arg} not known."); - ret_args_input.baudrate = -1; - argErrors = true; - } - } - } - arg_offset++; // Check next argument - { /*--------- Parse Parity ---------*/ - if (args.Length > arg_offset) - { - var arg = args[arg_offset].ToUpper(); - if (arg == "E") - { - ret_args_input.parity = Parity.Even; - } - else if (arg == "N") - { - ret_args_input.parity = Parity.None; - } - else if (arg == "O") - { - ret_args_input.parity = Parity.Odd; - } - else - { - Console.WriteLine($"Parity {arg} not known."); - ret_args_input.parity = Parity.None; - argErrors = true; - } - } - } - } - if (argErrors) - { - string message = GetArgHelpMessage(argErrors, args); - Console.WriteLine(message); - if (argErrors) - { - throw new Exception(message); // exit main - } - } - return run_task; - } - #endregion - - /// - /// For unit testing setup - /// - /// - static void Main(string[] args) - { - int ret_error = 0; // Is okay - List duts = new List(); - try - { - var runMode = RUN_TYPE.Mock; - if (LIVE_RUN) - { - runMode = RUN_TYPE.Normal; - } - - // Just for internal testing -#if (DEBUG) - if (args.Length <= 0) - { - if (MAIN_ARGS_OVERWRITE) - { - args = MAIN_ARGS_OVERWRITE_WITH; //testing directly to metrology - } - } -#endif - var run_task = RUN_ACTION.UNKNOWN; - bool gotArgs = ((args != null) && (args.Length >= 1)); - var inputs = new args_inputs(); - if (gotArgs) - { - run_task = parse_arguments(args, ref inputs); - } - else - { - run_task = RUN_ACTION.SHOW_HELP; - } - if (run_task == RUN_ACTION.SHOW_HELP) - { - string message = GetArgHelpMessage(false, null); - Console.WriteLine(message); - return; - } - - // Set window to max size to better fit content - // Not needed for now! Console.SetWindowSize(Console.LargestWindowWidth, Console.LargestWindowHeight); - #region Setup Communication to dut's - foreach (var comport in inputs.comports) - { - if (runMode != RUN_TYPE.Normal) - { - Console.WriteLine($"{new string('!', 50)}\n Warning is in running in {runMode} mode\n{new string('!', 50)}"); - } - MagFlux6200 dut = null; - try - { // Set up on DUT - dut = new MagFlux6200(runMode); - var log_path = Environment.GetEnvironmentVariable("USERPROFILE")+@"\Downloads\"; - var isOkay = dut.SetLogLocation(log_path, "logs_"+comport); - Console.WriteLine($"Using program log path:\n{log_path}"); - isOkay &=gotArgs; - if (isOkay) - { // Use standard and just setup as port - if (inputs.baudrate == null) - { - isOkay &=dut.Connect(comport); - } - else if ((inputs.baudrate != null) && (inputs.parity != null)) - { - isOkay &=dut.Connect(comport, inputs.baudrate.Value, inputs.parity.Value); - } - else - { - isOkay = false; // Error in settings - } - } - - if (isOkay) - { - duts.Add(dut); - } - else - { - var port = comport; - if (String.IsNullOrEmpty(port)) - { - port = "COM ?"; - } - throw new Exception($"Fail to setup device for {port}"); - } - } - catch (Exception ex) - { - if (dut!=null) - { - dut.OpenLogFile(); - } - throw ex; - } - } - #endregion //#region Setup Communication to dut's - switch (run_task) - { - case RUN_ACTION.TEST: - TEST(duts); - break; - case RUN_ACTION.SAVE_CAL: - { - SAVE_CAL(duts); - } - break; - case RUN_ACTION.SET_DN_SN: - { - if(duts.Count() > 1) - { - throw new InvalidDataException($"Error support only a single device for setting DN and SN \n{GetArgHelpMessage(true, args)}"); - } - SET_DN_SN(duts.First(), inputs.DN_mm.Value, inputs.sensor_sn.Value); - int wait_ms = 15000; - Console.WriteLine($"Wait {wait_ms/1000} for device to reset after a possible DN change"); - Task.Delay(15000).GetAwaiter().GetResult(); - } - break; - case RUN_ACTION.LOAD_CAL: - { - LOAD_CAL(duts, inputs.loadedCalibrations); - } - break; - case RUN_ACTION.ABORT_CAL: - { - ABORT_CAL(duts); - } - break; - default: - { - throw new NotImplementedException($"Missing support run mode {run_task}"); - } - } - Console.WriteLine("finished functional testing"); - // Is set to true when the monitoring values also needs to stop - bool exitMonitoringLoop = false, pauseMonitoringLoop = false, _LastPauseMonitoringLoop = false; - // Start a task for monitoring a key for exit - Console.WriteLine("Press key 'x' exit, 'p' for pause read, 'c' continue read"); - Task.Run(() => - { - ConsoleKeyInfo keyinfo; - do - { - keyinfo = Console.ReadKey(); - if (keyinfo.Key == ConsoleKey.P) - { - pauseMonitoringLoop = true; - } - else if (keyinfo.Key == ConsoleKey.C) - { - pauseMonitoringLoop = false; - } - else - { - Console.WriteLine($"\n{keyinfo.Key} was pressed. Press key 'x' exit"); - } - } - while (keyinfo.Key != ConsoleKey.X); - exitMonitoringLoop = true; - }); - // Just read flow and other values for testing general measurement - int count = 0; - // "Structure of an interpolated string" - // https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/tokens/interpolated - while (!exitMonitoringLoop) - { - if (!pauseMonitoringLoop) - { - var message = "\r"; - var dutNum = 1; - foreach (var dut in duts) - { - { // Read Flow - var isOkay = dut.GetFlowRate_lps(out var value_out); - if (dutNum > 1) - { - message += " | "; - } - message += $"DUT{dutNum++}: "; - if (!dut.GetDeviceHealthy()) - { - message += $"Error:"; - if (dut.GetDeviceErrorMessages(out var errorList)) - { - message += $"{errorList.ToSingleString(false)} "; - } - else - { - message += $"No Text to display!"; - } - } - else - { - const int w = 9; - message += $"{(isOkay ? $"{value_out*3.6,w:F3} m3/h" : $"!Read failed!")} {dut.GetComCounters()}\t"; - if (!isOkay) - { - throw new Exception(message); - } - } - } - } - Console.Write(message + $" msg={++count}\t"); - } - { // Print out if paused read - if (_LastPauseMonitoringLoop != pauseMonitoringLoop) - { - _LastPauseMonitoringLoop = pauseMonitoringLoop; - if (pauseMonitoringLoop) - { - Console.Write("\rRead is Paused!\t\t\t\t\t\t\t\t"); - } - } - } - } - } - catch (Exception ex) - { - var name_exe = System.AppDomain.CurrentDomain.FriendlyName; - AppConst.Logger.AddFuncLog(true, name_exe, "main()", ex); - AppConst.Logger.OpenProgramLogFile(); - ret_error = -1; //https://learn.microsoft.com/en-us/windows/win32/debug/system-error-codes - } - finally - { - // Close connection before exit - try - { - if (duts.Count != 0) - { - foreach (var dut in duts) - { - dut.CloseConnection(); - } - } - } - catch { } - Environment.Exit(ret_error); //https://learn.microsoft.com/en-us/windows/win32/debug/system-error-codes - } - } - } -} diff --git a/Common/modbus_master_csharp/Properties/AssemblyInfo.cs b/Common/modbus_master_csharp/Properties/AssemblyInfo.cs deleted file mode 100644 index de710f69..00000000 --- a/Common/modbus_master_csharp/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("modbus_master_csharp")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("Xylem Inc.")] -[assembly: AssemblyProduct("modbus_master_csharp")] -[assembly: AssemblyCopyright("Copyright © Xylem Inc. 2023")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("f14940f8-3c86-4bb7-a915-b021e4b3052d")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -// [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/Common/modbus_master_csharp/README.md b/Common/modbus_master_csharp/README.md deleted file mode 100644 index fedab5ba..00000000 --- a/Common/modbus_master_csharp/README.md +++ /dev/null @@ -1,22 +0,0 @@ -# README # - -### What is this repository for? ### - -* A simple basic Modbus Master console project that support read and write from MagFlux Next Gen. -* "md_master_csharp_6200_reg_list.cs" is using the register list generated by [magflux_modbus_registers](https://bitbucket.org/xyleminc/magflux_modbus_registers/src/main/) - -### How do I get set up? ### - -* Open project (modbus_master_csharp.sln) in Visual studio (developed in Ver 2022) -* In "Program.cs" Main() Set what com port MagFlux Next Gen is connected to* - -### Contribution guidelines ### - -* Writing tests if you can -* Code review with minimum one from MJK team -* Make sure that "md_master_csharp_6200_reg_list.cs" is updated - - -### Who do I talk to? ### - -* Please contact MJK R & D team diff --git a/Common/modbus_master_csharp/compiled_exe/2023-10-10 support Met reg v6200045.zip b/Common/modbus_master_csharp/compiled_exe/2023-10-10 support Met reg v6200045.zip deleted file mode 100644 index ca112651a2ed45a3c266b174bc69fa47034cb430..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 61228 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%0D|{8gXOdY)>zZZQM~uT3+Mrs{%L|PO8sX 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