From 2adc8756bc7ffbf0fdcd1ef28cde7fbd35d39fdc Mon Sep 17 00:00:00 2001 From: Stoyan Zlatev Date: Mon, 4 Sep 2023 13:38:00 +0200 Subject: [PATCH] mudbus_master_csharp project added directly to the solution --- Common/Common.sln | 134 +- .../MagFlux/MagFluxCore/MagFluxCore.csproj | 4 - .../RequestProtocol/RequestProtocol.csproj | 4 +- ...erRegisters.csproj.CoreCompileInputs.cache | 2 +- ...ToXlsHelper.csproj.CoreCompileInputs.cache | 2 +- .../Ui/MagFluxToolBox/MagFluxToolBox.csproj | 4 - 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 | 212 ++++ .../Base/LogMessageTool.cs | 102 ++ .../Base/NumberConvert.cs | 602 +++++++++ .../Base/StringConvert.cs | 474 +++++++ .../Base/XML/XMLStringClass.cs | 127 ++ .../Base/XML/XMLWriteFuncClass.cs | 66 + .../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 | 120 ++ .../Device/CalibrationPoints.cs | 198 +++ .../Device/IMagFluxRequestProtocol.cs | 156 +++ .../Device/MagFlux6200.cs | 1125 +++++++++++++++++ .../Device/MagFlux6200_metrology_reg_list.cs | 1105 ++++++++++++++++ .../Device/ModbusDeviceCom.cs | 319 +++++ .../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 | 785 ++++++++++++ .../Properties/AssemblyInfo.cs | 36 + .../modbus_master_csharp.csproj | 89 ++ 44 files changed, 9942 insertions(+), 79 deletions(-) create mode 100644 Common/modbus_master_csharp/App.config create mode 100644 Common/modbus_master_csharp/Base/AppConst.cs create mode 100644 Common/modbus_master_csharp/Base/ByteArrayExtensionClass.cs create mode 100644 Common/modbus_master_csharp/Base/Convert1970SecToTime.cs create mode 100644 Common/modbus_master_csharp/Base/DataUnion.cs create mode 100644 Common/modbus_master_csharp/Base/Files/PathTools.cs create mode 100644 Common/modbus_master_csharp/Base/Files/ProgramLogCSVFileClass.cs create mode 100644 Common/modbus_master_csharp/Base/LogMessageTool.cs create mode 100644 Common/modbus_master_csharp/Base/NumberConvert.cs create mode 100644 Common/modbus_master_csharp/Base/StringConvert.cs create mode 100644 Common/modbus_master_csharp/Base/XML/XMLStringClass.cs create mode 100644 Common/modbus_master_csharp/Base/XML/XMLWriteFuncClass.cs create mode 100644 Common/modbus_master_csharp/Communication/DeviceExceptionData.cs create mode 100644 Common/modbus_master_csharp/Communication/FuncTypes.cs create mode 100644 Common/modbus_master_csharp/Communication/ModbusCom.cs create mode 100644 Common/modbus_master_csharp/Communication/ProtocolTranslateModbusRTUClass.cs create mode 100644 Common/modbus_master_csharp/Communication/ProtocolType.cs create mode 100644 Common/modbus_master_csharp/Communication/ValueEncoders/ArrayManipulator.cs create mode 100644 Common/modbus_master_csharp/Communication/ValueEncoders/DeviceValueEncoder.cs create mode 100644 Common/modbus_master_csharp/Communication/ValueEncoders/LayoutType.cs create mode 100644 Common/modbus_master_csharp/Communication/ValueEncoders/StringEncoder.cs create mode 100644 Common/modbus_master_csharp/Communication/ValueEncoders/StringEncoderEmail.cs create mode 100644 Common/modbus_master_csharp/Communication/ValueEncoders/StringEncoderEmail_Q_Types.cs create mode 100644 Common/modbus_master_csharp/Communication/ValueEncoders/StringEncoderUnicode.cs create mode 100644 Common/modbus_master_csharp/Communication/ValueEncoders/StringEncoder_W1252.cs create mode 100644 Common/modbus_master_csharp/Communication/ValueEncoders/U32_IP_Encoder.cs create mode 100644 Common/modbus_master_csharp/Device/CalibrationPoint.cs create mode 100644 Common/modbus_master_csharp/Device/CalibrationPoints.cs create mode 100644 Common/modbus_master_csharp/Device/IMagFluxRequestProtocol.cs create mode 100644 Common/modbus_master_csharp/Device/MagFlux6200.cs create mode 100644 Common/modbus_master_csharp/Device/MagFlux6200_metrology_reg_list.cs create mode 100644 Common/modbus_master_csharp/Device/ModbusDeviceCom.cs create mode 100644 Common/modbus_master_csharp/Device/Value/ValueDataType.cs create mode 100644 Common/modbus_master_csharp/Device/Value/ValueDescriptionFunc.cs create mode 100644 Common/modbus_master_csharp/Device/md_test.cs create mode 100644 Common/modbus_master_csharp/Program.cs create mode 100644 Common/modbus_master_csharp/Properties/AssemblyInfo.cs create mode 100644 Common/modbus_master_csharp/modbus_master_csharp.csproj diff --git a/Common/Common.sln b/Common/Common.sln index 008730d2..4f5ae3eb 100644 --- a/Common/Common.sln +++ b/Common/Common.sln @@ -157,8 +157,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Protocols", "Protocols", "{ EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "MjkControlledCodeRef", "MjkControlledCodeRef", "{B4F8545B-F379-42A5-BDE9-763FD7B79598}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "modbus_master_csharp", "..\..\..\MJK\Code\C#\modbus_master_csharp\src\modbus_master_csharp\modbus_master_csharp.csproj", "{F14940F8-3C86-4BB7-A915-B021E4B3052D}" -EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MagFluxToolBox", "Ui\MagFluxToolBox\MagFluxToolBox.csproj", "{A5E7CE52-3101-4E9B-B896-C2C0FADE4812}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MagFluxConfig", "Hardware\WaterMeter\MagFlux\MagFluxConfig\MagFluxConfig.csproj", "{70398B99-B134-4BBD-968B-7399B70C3CE2}" @@ -189,6 +187,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CommonMessurementUnits", "C 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 Global GlobalSection(SharedMSBuildProjectFiles) = preSolution Utils\DateTimeServerShared\DateTimeServerShared.projitems*{4806d89a-fbfb-41bc-aaa7-2242444bf55a}*SharedItemsImports = 4 @@ -3487,70 +3487,6 @@ Global {35726BCF-975C-4B49-BEFA-C00D90394F1D}.XP32bit|x64.Build.0 = Release|Any CPU {35726BCF-975C-4B49-BEFA-C00D90394F1D}.XP32bit|x86.ActiveCfg = Release|Any CPU {35726BCF-975C-4B49-BEFA-C00D90394F1D}.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 {A5E7CE52-3101-4E9B-B896-C2C0FADE4812}._MANUAL_CONTROL|Any CPU.ActiveCfg = Release|Any CPU {A5E7CE52-3101-4E9B-B896-C2C0FADE4812}._MANUAL_CONTROL|Any CPU.Build.0 = Release|Any CPU {A5E7CE52-3101-4E9B-B896-C2C0FADE4812}._MANUAL_CONTROL|Mixed Platforms.ActiveCfg = Release|Any CPU @@ -4447,6 +4383,70 @@ 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 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -4517,7 +4517,6 @@ Global {C9393A97-C0BB-4D59-A879-B572BE04CE15} = {5563DD3B-2287-4BED-97BF-F0389FF505AF} {94579693-7AC6-4FF0-B540-1F3780E1BCFA} = {C9393A97-C0BB-4D59-A879-B572BE04CE15} {B4F8545B-F379-42A5-BDE9-763FD7B79598} = {C9393A97-C0BB-4D59-A879-B572BE04CE15} - {F14940F8-3C86-4BB7-A915-B021E4B3052D} = {B4F8545B-F379-42A5-BDE9-763FD7B79598} {A5E7CE52-3101-4E9B-B896-C2C0FADE4812} = {03D4F23E-7E4A-4091-BC96-036C996D351A} {70398B99-B134-4BBD-968B-7399B70C3CE2} = {C9393A97-C0BB-4D59-A879-B572BE04CE15} {B5DA260C-71CC-45CA-B79E-D1B376AF718C} = {C9393A97-C0BB-4D59-A879-B572BE04CE15} @@ -4533,6 +4532,7 @@ Global {FD30EB7A-7809-4E62-9374-791D31877D6D} = {696A94BE-7237-4AE2-9E36-6059DE519971} {17C15B79-9A9E-4079-8FE2-0E563F18CA01} = {C17363DF-A043-4F25-8EBA-2B3B078227DC} {AFC428F5-0141-4A3F-BEF2-470A89D972F6} = {C17363DF-A043-4F25-8EBA-2B3B078227DC} + {F14940F8-3C86-4BB7-A915-B021E4B3052D} = {B4F8545B-F379-42A5-BDE9-763FD7B79598} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {4BCB7B69-696C-436A-86F7-C91EA5588164} diff --git a/Common/Hardware/WaterMeter/MagFlux/MagFluxCore/MagFluxCore.csproj b/Common/Hardware/WaterMeter/MagFlux/MagFluxCore/MagFluxCore.csproj index 5368c7fc..74ec0df8 100644 --- a/Common/Hardware/WaterMeter/MagFlux/MagFluxCore/MagFluxCore.csproj +++ b/Common/Hardware/WaterMeter/MagFlux/MagFluxCore/MagFluxCore.csproj @@ -59,10 +59,6 @@ - - {f14940f8-3c86-4bb7-a915-b021e4b3052d} - modbus_master_csharp - {9eb1f659-6f73-4f65-bcab-4114fd94f5fc} CommonCore.Configuration diff --git a/Common/Hardware/WaterMeter/MagFlux/Protocols/RequestProtocol/RequestProtocol.csproj b/Common/Hardware/WaterMeter/MagFlux/Protocols/RequestProtocol/RequestProtocol.csproj index 28ff6b99..6255094c 100644 --- a/Common/Hardware/WaterMeter/MagFlux/Protocols/RequestProtocol/RequestProtocol.csproj +++ b/Common/Hardware/WaterMeter/MagFlux/Protocols/RequestProtocol/RequestProtocol.csproj @@ -53,8 +53,8 @@ - - {f14940f8-3c86-4bb7-a915-b021e4b3052d} + + {F14940F8-3C86-4BB7-A915-B021E4B3052D} modbus_master_csharp diff --git a/Common/Hardware/WaterMeter/WaterMeterCore/WaterMeterRegisters/obj/Debug/WaterMeterRegisters.csproj.CoreCompileInputs.cache b/Common/Hardware/WaterMeter/WaterMeterCore/WaterMeterRegisters/obj/Debug/WaterMeterRegisters.csproj.CoreCompileInputs.cache index 82bc2c16..8865d283 100644 --- a/Common/Hardware/WaterMeter/WaterMeterCore/WaterMeterRegisters/obj/Debug/WaterMeterRegisters.csproj.CoreCompileInputs.cache +++ b/Common/Hardware/WaterMeter/WaterMeterCore/WaterMeterRegisters/obj/Debug/WaterMeterRegisters.csproj.CoreCompileInputs.cache @@ -1 +1 @@ -bf6bb8fceb3db5a9977dadc407cf576684bf7127 +89102b0142e574de38a9f47d370f3334e5e6c268 diff --git a/Common/Tools/Tools.JsonToXlsHelper/obj/Debug/Tools.JsonToXlsHelper.csproj.CoreCompileInputs.cache b/Common/Tools/Tools.JsonToXlsHelper/obj/Debug/Tools.JsonToXlsHelper.csproj.CoreCompileInputs.cache index 8dda2975..dd8c285d 100644 --- a/Common/Tools/Tools.JsonToXlsHelper/obj/Debug/Tools.JsonToXlsHelper.csproj.CoreCompileInputs.cache +++ b/Common/Tools/Tools.JsonToXlsHelper/obj/Debug/Tools.JsonToXlsHelper.csproj.CoreCompileInputs.cache @@ -1 +1 @@ -247396738e2c5d26ac00f9df05c0cc5c61d7f33a +338bdd8d71c148c9df0972f903098eb23eacf0c9 diff --git a/Common/Ui/MagFluxToolBox/MagFluxToolBox.csproj b/Common/Ui/MagFluxToolBox/MagFluxToolBox.csproj index 8a642bd3..e7aa59c9 100644 --- a/Common/Ui/MagFluxToolBox/MagFluxToolBox.csproj +++ b/Common/Ui/MagFluxToolBox/MagFluxToolBox.csproj @@ -117,10 +117,6 @@ - - {F14940F8-3C86-4BB7-A915-B021E4B3052D} - modbus_master_csharp - {1B02C79E-0B19-43E2-8F6B-71EF0C786C97} CommonCore diff --git a/Common/modbus_master_csharp/App.config b/Common/modbus_master_csharp/App.config new file mode 100644 index 00000000..193aecc6 --- /dev/null +++ b/Common/modbus_master_csharp/App.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/Common/modbus_master_csharp/Base/AppConst.cs b/Common/modbus_master_csharp/Base/AppConst.cs new file mode 100644 index 00000000..9d974cff --- /dev/null +++ b/Common/modbus_master_csharp/Base/AppConst.cs @@ -0,0 +1,111 @@ +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 new file mode 100644 index 00000000..ce2c4631 --- /dev/null +++ b/Common/modbus_master_csharp/Base/ByteArrayExtensionClass.cs @@ -0,0 +1,314 @@ +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 new file mode 100644 index 00000000..6dfa2ad8 --- /dev/null +++ b/Common/modbus_master_csharp/Base/Convert1970SecToTime.cs @@ -0,0 +1,153 @@ +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 new file mode 100644 index 00000000..e4c7c0f1 --- /dev/null +++ b/Common/modbus_master_csharp/Base/DataUnion.cs @@ -0,0 +1,459 @@ +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 new file mode 100644 index 00000000..912c9e0b --- /dev/null +++ b/Common/modbus_master_csharp/Base/Files/PathTools.cs @@ -0,0 +1,115 @@ +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 new file mode 100644 index 00000000..9f9c84c6 --- /dev/null +++ b/Common/modbus_master_csharp/Base/Files/ProgramLogCSVFileClass.cs @@ -0,0 +1,212 @@ +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; + } + + 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 new file mode 100644 index 00000000..5229e356 --- /dev/null +++ b/Common/modbus_master_csharp/Base/LogMessageTool.cs @@ -0,0 +1,102 @@ +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 new file mode 100644 index 00000000..6d8b975d --- /dev/null +++ b/Common/modbus_master_csharp/Base/NumberConvert.cs @@ -0,0 +1,602 @@ +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 new file mode 100644 index 00000000..538f1a63 --- /dev/null +++ b/Common/modbus_master_csharp/Base/StringConvert.cs @@ -0,0 +1,474 @@ +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) { + 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 new file mode 100644 index 00000000..69fb1e0a --- /dev/null +++ b/Common/modbus_master_csharp/Base/XML/XMLStringClass.cs @@ -0,0 +1,127 @@ +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 new file mode 100644 index 00000000..415ba697 --- /dev/null +++ b/Common/modbus_master_csharp/Base/XML/XMLWriteFuncClass.cs @@ -0,0 +1,66 @@ +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; + +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 + { + var serializer = new XmlSerializer(typeof(T)); + writer = new StreamWriter(filePath, append); + serializer.Serialize(writer, objectToWrite); + } + 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 + { + var serializer = new XmlSerializer(typeof(T)); + reader = new StreamReader(filePath); + return (T)serializer.Deserialize(reader); + } + finally + { + if (reader != null) + reader.Close(); + } + } + } +} diff --git a/Common/modbus_master_csharp/Communication/DeviceExceptionData.cs b/Common/modbus_master_csharp/Communication/DeviceExceptionData.cs new file mode 100644 index 00000000..e8895bc2 --- /dev/null +++ b/Common/modbus_master_csharp/Communication/DeviceExceptionData.cs @@ -0,0 +1,67 @@ +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 new file mode 100644 index 00000000..00e940f5 --- /dev/null +++ b/Common/modbus_master_csharp/Communication/FuncTypes.cs @@ -0,0 +1,22 @@ +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 new file mode 100644 index 00000000..e33716aa --- /dev/null +++ b/Common/modbus_master_csharp/Communication/ModbusCom.cs @@ -0,0 +1,634 @@ +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 new file mode 100644 index 00000000..8f6f4ed2 --- /dev/null +++ b/Common/modbus_master_csharp/Communication/ProtocolTranslateModbusRTUClass.cs @@ -0,0 +1,572 @@ +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 new file mode 100644 index 00000000..9917f686 --- /dev/null +++ b/Common/modbus_master_csharp/Communication/ProtocolType.cs @@ -0,0 +1,11 @@ +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 new file mode 100644 index 00000000..437b8573 --- /dev/null +++ b/Common/modbus_master_csharp/Communication/ValueEncoders/ArrayManipulator.cs @@ -0,0 +1,22 @@ +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 new file mode 100644 index 00000000..b485232c --- /dev/null +++ b/Common/modbus_master_csharp/Communication/ValueEncoders/DeviceValueEncoder.cs @@ -0,0 +1,952 @@ +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 new file mode 100644 index 00000000..e1cad01c --- /dev/null +++ b/Common/modbus_master_csharp/Communication/ValueEncoders/LayoutType.cs @@ -0,0 +1,12 @@ +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 new file mode 100644 index 00000000..b9654183 --- /dev/null +++ b/Common/modbus_master_csharp/Communication/ValueEncoders/StringEncoder.cs @@ -0,0 +1,92 @@ +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 new file mode 100644 index 00000000..3d0850ce --- /dev/null +++ b/Common/modbus_master_csharp/Communication/ValueEncoders/StringEncoderEmail.cs @@ -0,0 +1,95 @@ +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 new file mode 100644 index 00000000..37dcb471 --- /dev/null +++ b/Common/modbus_master_csharp/Communication/ValueEncoders/StringEncoderEmail_Q_Types.cs @@ -0,0 +1,175 @@ +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 new file mode 100644 index 00000000..0ac0ec5d --- /dev/null +++ b/Common/modbus_master_csharp/Communication/ValueEncoders/StringEncoderUnicode.cs @@ -0,0 +1,41 @@ +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 new file mode 100644 index 00000000..25f0a58f --- /dev/null +++ b/Common/modbus_master_csharp/Communication/ValueEncoders/StringEncoder_W1252.cs @@ -0,0 +1,39 @@ +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 new file mode 100644 index 00000000..2f1770ea --- /dev/null +++ b/Common/modbus_master_csharp/Communication/ValueEncoders/U32_IP_Encoder.cs @@ -0,0 +1,69 @@ +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 new file mode 100644 index 00000000..8ce0041d --- /dev/null +++ b/Common/modbus_master_csharp/Device/CalibrationPoint.cs @@ -0,0 +1,120 @@ +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 = 10; + return $"Ref={ReferenceFlowRate_lps*3.6,w1:F3}m3/h DUT={ReportedFlowRate_lps*3.6,w1:F3}m3/h (Ref={ReferenceFlowRate_lps}L/s DUT={ReportedFlowRate_lps}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 new file mode 100644 index 00000000..fa33eb6e --- /dev/null +++ b/Common/modbus_master_csharp/Device/CalibrationPoints.cs @@ -0,0 +1,198 @@ +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 new file mode 100644 index 00000000..06f0d34a --- /dev/null +++ b/Common/modbus_master_csharp/Device/IMagFluxRequestProtocol.cs @@ -0,0 +1,156 @@ +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); + + /// + /// 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 value_out); + + /// + /// 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 new file mode 100644 index 00000000..d0db7286 --- /dev/null +++ b/Common/modbus_master_csharp/Device/MagFlux6200.cs @@ -0,0 +1,1125 @@ +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 + } + + 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 if (program_reglist_ver != int.Parse(device_reglist_ver)) + { + var message = $"Error can not communicate!\nProgram {program_reglist_ver} is not supporting MagFlux {device_reglist_ver}"; + Console.WriteLine(message); + _logger.AddFuncLog(true, this.ToString(), _logger.GetCurrentMethod(), message); + isOkay = false; + } + 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 = false; + + /// + /// 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 + } + return _DeviceHealthy; + } + + public Boolean GetDn_mm(out Int32 value_out) + { + // Is just running as a Mock + if (_runAs == RUN_TYPE.Mock) + { + value_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) + { + value_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); + } + value_out = -1; // ERROR: Did not receive a value + return false; // failed + } + } + + 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 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_reg_list.cs b/Common/modbus_master_csharp/Device/MagFlux6200_metrology_reg_list.cs new file mode 100644 index 00000000..d523dc66 --- /dev/null +++ b/Common/modbus_master_csharp/Device/MagFlux6200_metrology_reg_list.cs @@ -0,0 +1,1105 @@ +/* @page Declaration for "Gluing" Metrology Modbus API in to Application +* AUTO GENERATED DON'T EDIT, IS GENERATED WITH "MagFlux6200_metrology_reg_listvX.py @ 2023_04_26__14_58" +* 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 41; } } + + /// + /// 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."); + + +/*------------------------------ 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_U32IntADCSample_battADC_scale = new Device_value("Met_U32IntADCSample_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_U32IntADCSample_diagP_adc_scale = new Device_value("Met_U32IntADCSample_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_U32IntADCSample_diagN_adc_scale = new Device_value("Met_U32IntADCSample_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_U32IntADCSample_diagC_adc_scale = new Device_value("Met_U32IntADCSample_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_U32IntADCSample_vrefCommonADC_scale = new Device_value("Met_U32IntADCSample_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_U32IntADCSample_vrefADC_scale = new Device_value("Met_U32IntADCSample_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_U32IntADCSample_temperature_C_scale = new Device_value("Met_U32IntADCSample_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_U32IntADCSample_intBattADC_scale = new Device_value("Met_U32IntADCSample_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 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 22 ------------------------------*/ + + /// + /// @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 23 ------------------------------*/ + + /// + /// @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 24 ------------------------------*/ + + /// + /// @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 25 ------------------------------*/ + + /// + /// @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."); + + } // End of reg_list + /*---------------------------------------------------------------------------*/ +} /* MagFlux6200_metrology_reg_list */ diff --git a/Common/modbus_master_csharp/Device/ModbusDeviceCom.cs b/Common/modbus_master_csharp/Device/ModbusDeviceCom.cs new file mode 100644 index 00000000..a91e3be5 --- /dev/null +++ b/Common/modbus_master_csharp/Device/ModbusDeviceCom.cs @@ -0,0 +1,319 @@ +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 + } + + /// + /// 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 new file mode 100644 index 00000000..82b07575 --- /dev/null +++ b/Common/modbus_master_csharp/Device/Value/ValueDataType.cs @@ -0,0 +1,93 @@ +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 new file mode 100644 index 00000000..b04fb890 --- /dev/null +++ b/Common/modbus_master_csharp/Device/Value/ValueDescriptionFunc.cs @@ -0,0 +1,146 @@ +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 new file mode 100644 index 00000000..cfb9b435 --- /dev/null +++ b/Common/modbus_master_csharp/Device/md_test.cs @@ -0,0 +1,155 @@ +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 new file mode 100644 index 00000000..0e8acb21 --- /dev/null +++ b/Common/modbus_master_csharp/Program.cs @@ -0,0 +1,785 @@ +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 + { + static bool LIVE_RUN = true; +#if (DEBUG) + //static bool LIVE_RUN = false; // overwrite default + static bool LIVE_RUN_DIRECT_MET = false; // Run using RS485 9600,8,E setup as default + //static bool LIVE_RUN_DIRECT_MET = LIVE_RUN & true; // overwrite default to Run using 115200,8,N setup as default +#endif + + /*For testing directly to metrology */ + static string[] argsDefault1 = new string[] { "TEST", "COM11", "115200", "N" }; + /*For metrology RS485 when transparent to metrology*/ + static string[] argsDefault2 = new string[] { "SAVE_CAL", "COM2" }; + // Having more ports + static string[] argsDefault3 = new string[] { "TEST", "COM2", "COM6" }; + + /*For metrology RS485 when transparent to metrology*/ + static string[] argsDefault4 = new string[] { "LOAD_CAL=\"0_sensor_cal_for_testing.xml\"", "COM2" }; + + /*For metrology RS485 when transparent to metrology*/ + static string[] argsDefault5 = new string[] { "TEST", "COM2" }; + + + #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 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 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); + } + } + } + + /// + /// 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 + { + 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; + } + } + } + } + + /// + /// 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; + } + } + } + } + + + 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 + { + { + 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); + } + } + { + 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); + } + } + { + var name = nameof(dut.GetDn_mm); + var isOkay = dut.GetDn_mm(out var value_out); + var message = $"{name}: {(isOkay ? $"{value_out}mm" : "!Read failed!")}"; + Console.WriteLine(message); + if (!isOkay) + { + throw new Exception(message); + } + } + + { + GetDeviceHealthy(dut); + } + { + 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); + } + } + { + 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); + } + } + { + 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 + { // --- 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"); + } + } + } + { // --- 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!"); + } + } + } + } + } + catch (Exception ex) + { + dut.OpenLogFile(); + throw ex; + } + } + } + } + + + #endregion + enum RUN_ACTION + { + UNKNOWN, + TEST, + SAVE_CAL, + LOAD_CAL, + } + + + /// + /// 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(LIVE_RUN) + { + args = argsDefault5; //metrology RS485 when transparent to metrology + } + else + { + args = argsDefault5; // Run the functional test as mock + } + if (LIVE_RUN_DIRECT_MET) + { + args = argsDefault1; //testing directly to metrology + } + } +#endif + + #region Handle if arguments + bool gotArgs = (args.Length >= 1); + List comports = new List(); + int? baudrate = null; + Parity? parity = null; + var runtask = RUN_ACTION.UNKNOWN; + var loadedCalibrations = new List(); + var argHelpNeeded = gotArgs ? false : true; + // Use Default connection if nothing is provided by arguments + var arg_offset = 0; + if (args.Length > arg_offset) + { + // What task to do + if (args.Length > arg_offset) + { + if ("-H" == args[arg_offset].ToUpper()) + { + argHelpNeeded = true; + } + else if ("TEST" == args[arg_offset].ToUpper()) + { + runtask = RUN_ACTION.TEST; + } + else if ("SAVE_CAL" == args[arg_offset].ToUpper()) + { + runtask = RUN_ACTION.SAVE_CAL; + } + else if (args[arg_offset].ToUpper().StartsWith("LOAD_CAL=")) + { + runtask = 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}"); + } + loadedCalibrations.Add(cal); + } + else + { + throw new NotImplementedException($"Missing support for parameter {args[arg_offset]}"); + } + } + arg_offset++; + { // Parse com port / ports + var exit = false; + while (!exit) + { + if (args.Length > arg_offset) + { + var arg = args[arg_offset].ToUpper(); + if ("COM" == arg.Remove(3)) + { + comports.Add(arg); + } + else if (comports.Count <= 0) + { + Console.WriteLine($"Com port {arg} not understood."); + argHelpNeeded = true; + exit = true; + } + else + { + exit = true; // No more com to connect to + } + arg_offset++; + } + else + { + exit = true; // No more com to connect to + } + } + } + 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)) + { + baudrate = int.Parse(arg); + } + else + { + Console.WriteLine($"Baud rate {arg} not known."); + baudrate = -1; + argHelpNeeded = true; + } + } + arg_offset++; + if (args.Length > arg_offset) + { + var arg = args[arg_offset].ToUpper(); + if (arg == "E") + { + parity = Parity.Even; + } + else if (arg == "N") + { + parity = Parity.None; + } + else if (arg == "O") + { + parity = Parity.Odd; + } + else + { + Console.WriteLine($"Parity {arg} not known."); + parity = Parity.None; + argHelpNeeded = true; + } + } + } + if (argHelpNeeded) + { + var name_exe = System.AppDomain.CurrentDomain.FriendlyName; + var message = $@" +Arglist indicates help needed. See stdout. +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)}"" +"; + Console.WriteLine(message); + return; // exit main + } + #endregion + // 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 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 (baudrate == null) + { + isOkay &=dut.Connect(comport); + } + else if ((baudrate != null) && (parity != null)) + { + isOkay &=dut.Connect(comport, baudrate.Value, 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 (runtask) + { + case RUN_ACTION.TEST: + TEST(duts); + break; + case RUN_ACTION.SAVE_CAL: + { + SAVE_CAL(duts); + } + break; + case RUN_ACTION.LOAD_CAL: + { + LOAD_CAL(duts, loadedCalibrations); + } + break; + default: + { + throw new NotImplementedException($"Missing support run mode {runtask}"); + } + } + 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 new file mode 100644 index 00000000..de710f69 --- /dev/null +++ b/Common/modbus_master_csharp/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +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/modbus_master_csharp.csproj b/Common/modbus_master_csharp/modbus_master_csharp.csproj new file mode 100644 index 00000000..02a2573a --- /dev/null +++ b/Common/modbus_master_csharp/modbus_master_csharp.csproj @@ -0,0 +1,89 @@ + + + + + Debug + AnyCPU + {F14940F8-3C86-4BB7-A915-B021E4B3052D} + Exe + XYLEM + modbus_master_csharp + v4.8 + 512 + true + true + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file