diff --git a/Common/modbus_master_csharp/Base/Files/ProgramLogCSVFileClass.cs b/Common/modbus_master_csharp/Base/Files/ProgramLogCSVFileClass.cs index 9f9c84c6..3a573247 100644 --- a/Common/modbus_master_csharp/Base/Files/ProgramLogCSVFileClass.cs +++ b/Common/modbus_master_csharp/Base/Files/ProgramLogCSVFileClass.cs @@ -77,6 +77,26 @@ namespace XYLEM.Base.Files return nameTxt; } + /// + /// Automated get function name for the function calling this + /// Used for automating getting a function name to write in a log + /// + /// Add extra message to log with function name + /// "[CALLING_FUNCTION_NAME]() -> [inclMsg]" + [MethodImpl(MethodImplOptions.NoInlining)] + public static string GetCurrentStaticMethod(string inclMsg = null) + { + var st = new StackTrace(); + var sf = st.GetFrame(1); + + var nameTxt = $"{sf.GetMethod().Name}()"; + if (!String.IsNullOrEmpty(inclMsg)) + { + nameTxt += " -> " + inclMsg; + } + return nameTxt; + } + internal void AddToLogFile(bool isError, string nameSingleLine, string infoMultibleLines) { AddToLogFile(isError, StdLogFilePath, nameSingleLine, infoMultibleLines); diff --git a/Common/modbus_master_csharp/Base/StringConvert.cs b/Common/modbus_master_csharp/Base/StringConvert.cs index 538f1a63..27bc320f 100644 --- a/Common/modbus_master_csharp/Base/StringConvert.cs +++ b/Common/modbus_master_csharp/Base/StringConvert.cs @@ -356,7 +356,7 @@ namespace XYLEM.Base { /// internal static string ToSingleString(this string[] array, string joinWithText) { string ret = ""; - if (array != null) { + if ((array != null) && (array.Length > 0)) { ret = array.Select( item => (String.IsNullOrEmpty( item ) ? "" : item) ).Aggregate( (current, next) => current + ((joinWithText != null) ? joinWithText : "") + next ); } return ret; diff --git a/Common/modbus_master_csharp/Base/XML/XMLWriteFuncClass.cs b/Common/modbus_master_csharp/Base/XML/XMLWriteFuncClass.cs index 415ba697..4f7d60be 100644 --- a/Common/modbus_master_csharp/Base/XML/XMLWriteFuncClass.cs +++ b/Common/modbus_master_csharp/Base/XML/XMLWriteFuncClass.cs @@ -6,6 +6,8 @@ using System.Text; using System.Threading.Tasks; using System.Xml.Serialization; using System.Xml; +using System.Net.Sockets; +using XYLEM.Base.Files; namespace XYLEM.Base.XML { @@ -27,9 +29,13 @@ namespace XYLEM.Base.XML TextWriter writer = null; try { - var serializer = new XmlSerializer(typeof(T)); writer = new StreamWriter(filePath, append); - serializer.Serialize(writer, objectToWrite); + XmlSerializer xmlSerializer = new XmlSerializer(typeof(T)); + xmlSerializer.Serialize(writer, objectToWrite); + } + catch (Exception ex) + { + throw new Exception(ex.ToFuncErrorText(typeof(XMLWriteFuncClass).ToString(), ProgramLogCSVFileClass.GetCurrentStaticMethod())); } finally { @@ -52,9 +58,13 @@ namespace XYLEM.Base.XML TextReader reader = null; try { - var serializer = new XmlSerializer(typeof(T)); + XmlSerializer xmlSerializer = new XmlSerializer(typeof(T)); reader = new StreamReader(filePath); - return (T)serializer.Deserialize(reader); + return (T)xmlSerializer.Deserialize(reader); + } + catch (Exception ex) + { + throw new Exception(ex.ToFuncErrorText(typeof(XMLWriteFuncClass).ToString(), ProgramLogCSVFileClass.GetCurrentStaticMethod())); } finally { diff --git a/Common/modbus_master_csharp/Device/CalibrationPoint.cs b/Common/modbus_master_csharp/Device/CalibrationPoint.cs index 8ce0041d..8048e942 100644 --- a/Common/modbus_master_csharp/Device/CalibrationPoint.cs +++ b/Common/modbus_master_csharp/Device/CalibrationPoint.cs @@ -39,8 +39,9 @@ namespace XYLEM.Device 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)"; + const int w1 = 11; + return $"Ref={ReferenceFlowRate_lps*3.6,w1:F6} m3/h DUT={ReportedFlowRate_lps*3.6,w1:F6} m3/h " + + $"(Ref={ReferenceFlowRate_lps,w1:F6} l/s DUT={ReportedFlowRate_lps,w1:F6} l/s)"; } public int CompareTo(CalibrationPoint other) diff --git a/Common/modbus_master_csharp/Device/IMagFluxRequestProtocol.cs b/Common/modbus_master_csharp/Device/IMagFluxRequestProtocol.cs index 06f0d34a..0e91b68b 100644 --- a/Common/modbus_master_csharp/Device/IMagFluxRequestProtocol.cs +++ b/Common/modbus_master_csharp/Device/IMagFluxRequestProtocol.cs @@ -56,12 +56,20 @@ namespace XYLEM.Device void OpenLogFile(); /// - /// MagFlux Sensor _serial number + /// MagFlux Sensor serial number /// /// a decimal serial number ex. 12345678 or NULL at error /// true on okay or false on error Boolean GetSensorSerialNo(out string value_out); + + /// + /// Set MagFlux Sensor serial number + /// + /// + /// true on okay or false on error + Boolean SetSensorSerialNo(UInt32 new_serial_number); + /// /// Unique-ID for the MagFlux electronics /// @@ -100,9 +108,16 @@ namespace XYLEM.Device /// /// MagFlux Sensor nominal DN size in millimeters /// - /// DN size in [mm] is -1 on error + /// DN size in [mm] is -1 on error /// true on okay or false on error - Boolean GetDn_mm(out Int32 value_out); + Boolean GetDn_mm(out Int32 dn_mm_out); + + /// + /// Set Sensor nominal DN size in millimeters + /// + /// DN size in [mm] ex 50 is 50mm or DN50 + /// true on okay or false on error + Boolean SetDn_mm(UInt16 new_dn_mm); /// /// Check if there is problem with the DUT diff --git a/Common/modbus_master_csharp/Device/MagFlux6200.cs b/Common/modbus_master_csharp/Device/MagFlux6200.cs index d0db7286..f5ae46ea 100644 --- a/Common/modbus_master_csharp/Device/MagFlux6200.cs +++ b/Common/modbus_master_csharp/Device/MagFlux6200.cs @@ -27,12 +27,20 @@ namespace XYLEM.Device Mock } + /// + /// MagFlux 6200 "Metrology" device API. + /// + /// Handling serial communication with a single device during sensor Calibration and verification. + /// + /// Note: All Modbus register used needs to be documented in confluence, to show Modbus dependency and insure future support and comparability. + /// Confluence page https://xyleminc.atlassian.net/wiki/spaces/MJKMF/pages/7337054026/Modbus+register+-+Used+for+Sensor+calibration+and+verification+interface + /// public class MagFlux6200 : ModbusDeviceCom, IMagFluxRequestProtocol { RUN_TYPE _runAs = RUN_TYPE.Normal; public MagFlux6200(RUN_TYPE runAs = RUN_TYPE.Normal) : base() - { + { this._runAs = runAs; } @@ -83,16 +91,21 @@ namespace XYLEM.Device 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}"); + { // Check register list version + var version = int.Parse(device_reglist_ver); + if (program_reglist_ver != version) + { + // Warning to user that the register version is not the same as MagFlux has + var message = $"Warning communicate!\nProgram use {program_reglist_ver} and may not supporting MagFlux {version}!"; + Console.WriteLine(message); + _logger.AddFuncLog(true, this.ToString(), _logger.GetCurrentMethod(), message); + isOkay = true; + } + else + { + Console.WriteLine($"Communication with Modbus register list ver. {program_reglist_ver}"); + } } if (!isOkay) { @@ -117,7 +130,7 @@ namespace XYLEM.Device /// /// Device Healthy status that is periodically updated in background task /// - private bool _DeviceHealthy = false; + private bool? _DeviceHealthy; /// /// List with info about what errors is detected @@ -610,15 +623,26 @@ namespace XYLEM.Device { return true; // is okay } - return _DeviceHealthy; + var wait_max_s = 5; + var wait_pull_ms = 10; + var start_wait_count = wait_max_s*(1000.0/wait_pull_ms); + while (_DeviceHealthy == null) + { + Thread.Sleep(wait_pull_ms); + if (start_wait_count-- <=0) + { + throw new Exception("Fail getting a updated DeviceHealthy"); + } + } + return _DeviceHealthy.Value; } - public Boolean GetDn_mm(out Int32 value_out) + public Boolean GetDn_mm(out Int32 dn_mm_out) { // Is just running as a Mock if (_runAs == RUN_TYPE.Mock) { - value_out = 50; // is okay + dn_mm_out = 50; // is okay return true; // is okay } {// Normal @@ -629,7 +653,7 @@ namespace XYLEM.Device // Is okay if (value != null) { - value_out = (int)value.Value; + dn_mm_out = (int)value.Value; return true; // is okay } } @@ -637,11 +661,33 @@ namespace XYLEM.Device { _logger.AddFuncLog(true, this.ToString(), _logger.GetCurrentMethod(valueInfo.Name), "Fail to read value", ex); } - value_out = -1; // ERROR: Did not receive a value + dn_mm_out = -1; // ERROR: Did not receive a value return false; // failed } } + public bool SetDn_mm(UInt16 new_dn_mm) + { + var isOkay = false; + try + { + isOkay = GetDn_mm(out var current); + // Only write DN if different + if(current != new_dn_mm) + { + isOkay &= write_U16(reg_list.Met_U16PipeDiameterConfig_mm, (UInt16)new_dn_mm); + } + } + catch (Exception ex) + { +#if (DEBUG) + var message = $"Fail to write DN {new_dn_mm}"; + AppConst.Logger.AddFuncLog(true, this.ToString(), _logger.GetCurrentMethod(), message, ex); +#endif // #if (DEBUG) + } + return isOkay; + } + public Boolean GetFirmwareVersion(out string value_out) { // Is just running as a Mock @@ -830,6 +876,26 @@ namespace XYLEM.Device } } + + public bool SetSensorSerialNo(UInt32 new_serial_number) + { + var isOkay = false; + try + { + isOkay = write_U32(reg_list.Met_U32SensorSerialNo, new_serial_number); + } + catch (Exception ex) + { +#if (DEBUG) + var message = $"Fail to write Sensor SN {new_serial_number}"; + AppConst.Logger.AddFuncLog(true, this.ToString(), _logger.GetCurrentMethod(), message, ex); +#endif // #if (DEBUG) + } + return isOkay; + } + + + public Boolean GetUniqueId(out string value_out) { // Is just running as a Mock @@ -857,7 +923,7 @@ namespace XYLEM.Device value_out = "?"; // ERROR: Did not receive a value return false; // failed } - } + } #region Calibration points handling public Boolean GetCalibrationPoints(out CalibrationPoints values_read) @@ -1119,7 +1185,6 @@ namespace XYLEM.Device } } - #endregion //#region Calibration points } } \ No newline at end of file diff --git a/Common/modbus_master_csharp/Device/MagFlux6200_metrology_modbus_dependency_documentation.url b/Common/modbus_master_csharp/Device/MagFlux6200_metrology_modbus_dependency_documentation.url new file mode 100644 index 00000000..595941e5 --- /dev/null +++ b/Common/modbus_master_csharp/Device/MagFlux6200_metrology_modbus_dependency_documentation.url @@ -0,0 +1,5 @@ +[{000214A0-0000-0000-C000-000000000046}] +Prop3=19,11 +[InternetShortcut] +IDList= +URL=https://xyleminc.atlassian.net/wiki/spaces/MJKMF/pages/7337054026/Modbus+register+-+Used+for+Sensor+calibration+and+verification+interface diff --git a/Common/modbus_master_csharp/Device/MagFlux6200_metrology_reg_list.cs b/Common/modbus_master_csharp/Device/MagFlux6200_metrology_reg_list.cs index d523dc66..2f31d878 100644 --- a/Common/modbus_master_csharp/Device/MagFlux6200_metrology_reg_list.cs +++ b/Common/modbus_master_csharp/Device/MagFlux6200_metrology_reg_list.cs @@ -1,5 +1,5 @@ /* @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" +* AUTO GENERATED DON'T EDIT, IS GENERATED WITH "MagFlux6200_metrology_reg_listvX.py @ 2023_08_11__08_35" * From: https://bitbucket.org/xyleminc/magflux_modbus_registers/src/main/Registers/ */ @@ -35,7 +35,7 @@ namespace MagFlux6200_metrology_reg_list /// @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; } } + public int Version { get { return 43; } } /// /// Variable name @@ -958,6 +958,24 @@ namespace MagFlux6200_metrology_reg_list /*------------------------------ block number 21 ------------------------------*/ + /// + /// @brief Updated with 1_WIRE operation. + /// + internal static readonly Device_value Met_U16_OneWIRE_Status = new Device_value("Met_U16_OneWIRE_Status", 6100, ValueDataType.U16, ValueRW_Type.ReadWrite, "Updated with 1_WIRE operation."); + + /// + /// @brief Updated with 1_WIRE operation. + /// + internal static readonly Device_value Met_U16_OneWIRE_Error = new Device_value("Met_U16_OneWIRE_Error", 6101, ValueDataType.U16, ValueRW_Type.ReadWrite, "Updated with 1_WIRE operation."); + + /// + /// @brief Issue commands to control 1_WIRE operation + /// + internal static readonly Device_value Met_U16_OneWIRE_Ctrl = new Device_value("Met_U16_OneWIRE_Ctrl", 6102, ValueDataType.U16, ValueRW_Type.ReadWrite, "Issue commands to control 1_WIRE operation"); + + +/*------------------------------ block number 22 ------------------------------*/ + /// /// @brief Measured resistance between electrodes, in ohms, 0deg phase. /// @@ -999,7 +1017,7 @@ namespace MagFlux6200_metrology_reg_list 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 ------------------------------*/ +/*------------------------------ block number 23 ------------------------------*/ /// /// @brief EPD state @@ -1017,7 +1035,7 @@ namespace MagFlux6200_metrology_reg_list 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 ------------------------------*/ +/*------------------------------ block number 24 ------------------------------*/ /// /// @brief Low flow can be set on or off etc. @@ -1045,7 +1063,7 @@ namespace MagFlux6200_metrology_reg_list 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 ------------------------------*/ +/*------------------------------ block number 25 ------------------------------*/ /// /// @brief Rev flow can be set on or off etc. @@ -1073,7 +1091,7 @@ namespace MagFlux6200_metrology_reg_list 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 ------------------------------*/ +/*------------------------------ block number 26 ------------------------------*/ /// /// @brief Rev flow can be set on or off etc. diff --git a/Common/modbus_master_csharp/Device/ModbusDeviceCom.cs b/Common/modbus_master_csharp/Device/ModbusDeviceCom.cs index a91e3be5..30729db6 100644 --- a/Common/modbus_master_csharp/Device/ModbusDeviceCom.cs +++ b/Common/modbus_master_csharp/Device/ModbusDeviceCom.cs @@ -290,6 +290,21 @@ namespace XYLEM.Device return isOkay; // Error in reading } + protected bool write_U32(Device_value dv, UInt32 value) + { + var isOkay = _md_com.Write(_device_id, dv, value); + if (!isOkay) + { +#if (DEBUG) + if (DoComDebugPrint) + { + Console.WriteLine($"Failed Write={dv,ch_lng_dv}: Reg_Ver={dv.Version,ch_lng_Version} Adr={dv.MD_adr,ch_lng_MD_adr} value={value,dig_value}"); + } +#endif // #if (DEBUG) + } + return isOkay; // Error in reading + } + /// /// Write multiple values in a optimized number of telegrams /// diff --git a/Common/modbus_master_csharp/Program.cs b/Common/modbus_master_csharp/Program.cs index 0e8acb21..5457ac31 100644 --- a/Common/modbus_master_csharp/Program.cs +++ b/Common/modbus_master_csharp/Program.cs @@ -23,27 +23,71 @@ 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 + { + /*For testing directly to metrology */ + static string[] argsDefault1 = new string[] { "TEST", "COM41", "115200", "N" }; + /*For metrology RS485 when transparent to metrology*/ + static string[] argsDefault2 = new string[] { "SAVE_CAL", "COM38" }; + + static string[] argsDefault2b = new string[] { "SAVE_CAL", "COM41", "115200", "N" }; + + static string[] argsDefault2c = new string[] { "SAVE_CAL", "COM41", "COM42", "115200", "N" }; + + // Having more ports + static string[] argsDefault3 = new string[] { "TEST", "COM38", "COM41" }; + + /*For metrology RS485 when transparent to metrology*/ + static string[] argsDefault4 = new string[] { "LOAD_CAL=\"0_sensor_cal_for_testing.xml\"", "COM38" }; + + /*For metrology RS485 when transparent to metrology*/ + static string[] argsDefault5 = new string[] { "SET_DN_SN=25,999999", "COM38" }; + + /*For metrology RS485 when transparent to metrology*/ + static string[] argsDebugLiveRun = new string[] { "TEST", "COM41" }; + + /*For metrology RS485 when transparent to metrology*/ + static string[] args_ExAbortCal = new string[] { "ABORT_CAL", "COM41" }; + +#if (DEBUG) + static bool LIVE_RUN = true; // Default = "true" to run with DUT communication or "false" to run without any DUT for communication and mock values + + /* Preload with program argument used for debugging*/ + static bool MAIN_ARGS_OVERWRITE = true; // true to overwrite run with "argsDebugLiveRun" [Default = false] + static string[] MAIN_ARGS_OVERWRITE_WITH = argsDebugLiveRun; //testing arguments in debug Default = "argsDebugLiveRun" +#else + static bool LIVE_RUN = true; // #endif - /*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" }; + private static string GetArgHelpMessage(bool isArgErrorMessage, string[] args) + { + var name_exe = System.AppDomain.CurrentDomain.FriendlyName; + var message = $@" +Example of from command line: +Save calibrations only include COMx to use standard baudrate and parity setup: + ""{name_exe} {argsDefault2.ToSingleString(false)}"" + +or run functional test for 2 MagFlux + ""{name_exe} {argsDefault3.ToSingleString(false)}"" - /*For metrology RS485 when transparent to metrology*/ - static string[] argsDefault4 = new string[] { "LOAD_CAL=\"0_sensor_cal_for_testing.xml\"", "COM2" }; +To run functional test and configure both COMx, baudrate, parity. (Used for com directly to Metrology) + ""{name_exe} {argsDefault1.ToSingleString(false)}"" - /*For metrology RS485 when transparent to metrology*/ - static string[] argsDefault5 = new string[] { "TEST", "COM2" }; +To Load calibrations only include COMx to use standard baudrate and parity setup: + ""{name_exe} {argsDefault4.ToSingleString(false)}"" +To Set DN [mm] and Sensor Serial number, only include COMx to use standard baudrate and parity setup: + ""{name_exe} {argsDefault5.ToSingleString(false)}"" + +To Abort calibrations only include COMx to use standard baudrate and parity setup: + ""{name_exe} {args_ExAbortCal.ToSingleString(false)}"" +"; + if (isArgErrorMessage) + { + var argsTxt = args != null ? args.ToSingleString(" ") : ""; + message = $"{new string('!', 50)}\nThere is errors in the Arguments list!\n{new string('!', 50)}\nWas: {name_exe} {argsTxt}\n" + message; + } + return message; + } #region Reused functions for testing private static bool GetDeviceHealthy(IMagFluxRequestProtocol dut) @@ -65,6 +109,91 @@ namespace modbus_master_csharp return isOkay; } + private static void GetBasicDeviceInfo(IMagFluxRequestProtocol dut) + { + { + var name = nameof(dut.GetFwGitHash); + var isOkay = dut.GetFwGitHash(out var value_out); + var message = $"{name}: {(isOkay ? value_out : "!Read failed!")}"; + Console.WriteLine(message); + if (!isOkay) + { + throw new Exception(message); + } + } + { + var name = nameof(dut.GetSensorSerialNo); + var isOkay = dut.GetSensorSerialNo(out var value_out); + var message = $"{name}: {(isOkay ? value_out : "!Read failed!")}"; + Console.WriteLine(message); + if (!isOkay) + { + throw new Exception(message); + } + } + { + var name = nameof(dut.GetUniqueId); + var isOkay = dut.GetUniqueId(out var value_out); + var message = $"{name}: {(isOkay ? value_out : "!Read failed!")}"; + Console.WriteLine(message); + if (!isOkay) + { + throw new Exception(message); + } + } + { + var name = nameof(dut.GetFirmwareVersion); + var isOkay = dut.GetFirmwareVersion(out var value_out); + var message = $"{name}: {(isOkay ? value_out : "!Read failed!")}"; + Console.WriteLine(message); + if (!isOkay) + { + throw new Exception(message); + } + } + { + var name = nameof(dut.GetFwBuildDate); + var isOkay = dut.GetFwBuildDate(out var value_out); + var message = $"{name}: {(isOkay ? value_out : "!Read failed!")}"; + Console.WriteLine(message); + if (!isOkay) + { + throw new Exception(message); + } + } + } + + private static void GetBasicSensorInfo(IMagFluxRequestProtocol dut) + { + { + var name = nameof(dut.GetDn_mm); + var isOkay = dut.GetDn_mm(out var dn_mm_out); + var message = $"{name}: {(isOkay ? $"{dn_mm_out}mm" : "!Read failed!")}"; + Console.WriteLine(message); + if (!isOkay) + { + throw new Exception(message); + } + } + { + GetDeviceHealthy(dut); + } + } + + private static void AbortDeviceCalibration(IMagFluxRequestProtocol dut) + { + { + var name = nameof(dut.AbortDeviceForCalibration); + var isOkay = dut.AbortDeviceForCalibration(); + var message = $"{name}: {(isOkay ? "Done" : "!Abort failed!")}"; + Console.WriteLine(message); + if (!isOkay) + { + throw new Exception(message); + } + } + } + private static bool GetCalibrationPoints(IMagFluxRequestProtocol dut, out CalibrationPoints value_out, bool printoutPoints) { var name = nameof(dut.GetCalibrationPoints); @@ -87,6 +216,21 @@ namespace modbus_master_csharp return isOkay; } + private static bool PrepareDeviceForCalibration(IMagFluxRequestProtocol dut) + { + { // --- Test setting the device ready for the first measurement used for calibrations --- + { + var isOkay = dut.PrepareDeviceForCalibration(); + Console.WriteLine($"{nameof(dut.PrepareDeviceForCalibration)}: {(isOkay ? "Okay" : "Failed")}"); + if (!isOkay || !GetCalibrationPoints(dut, out var value_out, true/*printout points*/)) + { + GetDeviceHealthy(dut); + throw new Exception("Failed Setting device up for calibration"); + } + return isOkay; + } + } + } private static void RunTestCalibrationPoints(IMagFluxRequestProtocol dut, CalibrationPoints new_calibration_points) { @@ -109,6 +253,106 @@ namespace modbus_master_csharp } } + /// + /// Read Sensor serial number + /// + /// + /// Return the Serial number + /// Will throw an exception if failing + private static UInt32 GetSensorSerialNo(IMagFluxRequestProtocol dut) + { + var isOkay = dut.GetSensorSerialNo(out var read_out); + UInt32 ret = 0; + if (isOkay) + { + isOkay = UInt32.TryParse(read_out, out var passed); + ret = passed; + } + { // Handle result + var name = nameof(dut.GetSensorSerialNo); + var message = $"Read {name}: {(isOkay ? ret.ToString() : "!failed!")}"; + Console.WriteLine(message); + if (!isOkay) + { + throw new Exception(message); + } + } + return ret; + } + + /// + /// Write Sensor serial number (Also check with a read back) + /// + /// what dut to write to + /// New serial number + /// Will throw an exception if failing + private static void SetSensorSerialNo(IMagFluxRequestProtocol dut, UInt32 new_SN) + { + var name = nameof(dut.SetSensorSerialNo); + Console.WriteLine($"Write of {name}: {new_SN.ToString()}"); + var isOkay = dut.SetSensorSerialNo(new_SN); + var read = GetSensorSerialNo(dut); + isOkay &= (read == new_SN); // Is the same as read ? + // Handle result + if (!isOkay) + { + var message = $"Write of {name}:!failed!"; + Console.WriteLine(message); + throw new Exception(message); + } + } + + /// + /// Read Sensor DN + /// + /// + /// Return DN [mm] + /// Will throw an exception if failing + private static UInt16 GetDn_mm(IMagFluxRequestProtocol dut) + { + var isOkay = dut.GetDn_mm(out var read_out); + UInt16 ret = 0; + if (isOkay) + { + isOkay = UInt16.TryParse(read_out.ToString(), out var passed); + ret = passed; + } + { // Handle result + var name = nameof(dut.GetDn_mm); + var message = $"Read {name}: {(isOkay ? ret.ToString() : "!failed!")}"; + Console.WriteLine(message); + if (!isOkay) + { + throw new Exception(message); + } + } + return ret; + } + + + /// + /// Write Sensor DN size (Also check with a read back) + /// + /// what dut to write to + /// New DN [mm] + /// Will throw an exception if failing + private static void SetDn_mm(IMagFluxRequestProtocol dut, UInt16 new_DN_mm) + { + var name = nameof(dut.SetDn_mm); + Console.WriteLine($"Write of {name}: {new_DN_mm.ToString()}"); + var isOkay = dut.SetDn_mm(new_DN_mm); + var read = GetDn_mm(dut); + isOkay &= (read == new_DN_mm); // Is the same as read ? + // Handle result + if (!isOkay) + { + var message = $"Write of {name}:!failed!"; + Console.WriteLine(message); + throw new Exception(message); + } + } + + /// /// save and load what is read from device /// @@ -136,6 +380,8 @@ namespace modbus_master_csharp } } + + private static void SAVE_CAL(List duts) { { // Check Save all calibrations one DUT at a time @@ -145,6 +391,8 @@ namespace modbus_master_csharp Console.WriteLine($"------------------- DUT{dutIdx+1} --------------------"); try { + GetBasicDeviceInfo(dut); + GetBasicSensorInfo(dut); if (!GetCalibrationPoints(dut, out var value_out, true/*printout points*/)) { GetDeviceHealthy(dut); @@ -164,7 +412,36 @@ namespace modbus_master_csharp } } } + private static void ABORT_CAL(List duts) + { + { // Check Save all calibrations one DUT at a time + for (var dutIdx = 0; dutIdx < duts.Count(); dutIdx++) + { + var dut = duts[dutIdx]; + Console.WriteLine($"------------------- DUT{dutIdx+1} --------------------"); + try + { + GetBasicDeviceInfo(dut); + AbortDeviceCalibration(dut); + GetBasicSensorInfo(dut); + if (!GetCalibrationPoints(dut, out var value_out, true/*printout points*/)) + { + if (!PrepareDeviceForCalibration(dut)) + { + throw new Exception("Failed abort of device calibration"); + } + } + } + catch (Exception ex) + { + dut.OpenLogFile(); + throw ex; + } + } + } + } + /// /// Load Calibrations /// @@ -202,6 +479,39 @@ namespace modbus_master_csharp } } + + /// + /// Set DN and SN in device + /// + /// Device to write it to + /// DN size in [mm] + /// Sensor serial number + static void SET_DN_SN(IMagFluxRequestProtocol dut, UInt16 DN_mm, UInt32 sensor_sn) + { + Console.WriteLine($"------------------- Read current DUT Sensor DN and SN --------------------"); + try + { + GetDn_mm(dut); + GetSensorSerialNo(dut); + } + catch (Exception ex) + { + dut.OpenLogFile(); + throw ex; + } + + Console.WriteLine($"------------------- Set DUT Sensor DN and SN --------------------"); + try + { + SetDn_mm(dut, DN_mm); + SetSensorSerialNo(dut, sensor_sn); + } + catch (Exception ex) + { + dut.OpenLogFile(); + throw ex; + } + } static void TEST(List duts) { @@ -215,56 +525,7 @@ namespace modbus_master_csharp //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); - } - } + GetBasicDeviceInfo(dut); { var name = nameof(dut.GetFlowRate_lps); var isOkay = dut.GetFlowRate_lps(out var value_out); @@ -275,21 +536,9 @@ namespace modbus_master_csharp 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); - } - { + GetBasicSensorInfo(dut); + { // Always check that GetDeviceErrorMessages() even when there is not error, to test function + // Is only run in GetBasicSensorInfo() when there is an error var name = nameof(dut.GetDeviceErrorMessages); var isOkay = dut.GetDeviceErrorMessages(out var value_out); var message = $"{name}: \n{(isOkay ? String.Join("\n", value_out) : "!Read failed!")}"; @@ -299,16 +548,7 @@ namespace modbus_master_csharp 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); - } - } + AbortDeviceCalibration(dut); { if (!GetCalibrationPoints(dut, out var value_out, true/*printout points*/)) { @@ -324,17 +564,7 @@ namespace modbus_master_csharp //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"); - } - } - } + PrepareDeviceForCalibration(dut); { // --- Do a simple 2 point calibration with zero --- var new_calibration_points = new CalibrationPoints( new List { @@ -406,6 +636,41 @@ namespace modbus_master_csharp } } } + { // --- Test Sensor new_SN test --- + Console.WriteLine($"--- Test Write a changed Sensor SN ---"); + // Read Sensor new_SN + Console.WriteLine($"1)Read current Sensor SN"); + UInt32 current = GetSensorSerialNo(dut); + // Create a new Sensor new_SN + var write_new = current + 1; + // Write function also check with a read and throw exception if failed + Console.WriteLine($"2)Change Sensor SN to {write_new}"); + SetSensorSerialNo(dut, write_new); + // Restore Sensor new_SN + Console.WriteLine($"3)Restore Sensor SN to {write_new}"); + SetSensorSerialNo(dut, current); + Console.WriteLine($"--- Finish Test ---"); + } + { // --- Test Sensor DN test --- + Console.WriteLine($"--- Test Write a changed Sensor DN Size ---"); + // SetDn_mm(dut, 20); // Insure a valid DN + + UInt16[] tests_dn_mm = { 50, 100 }; + + // Read Sensor DN + Console.WriteLine($"1)Read current Sensor DN Size"); + UInt16 current = GetDn_mm(dut); + + // Create a new Sensor DN + var write_new = tests_dn_mm.FirstOrDefault(dn => dn != current); + // Write function also check with a read and throw exception if failed + Console.WriteLine($"2)Change Sensor DN Size as {write_new}"); + SetDn_mm(dut, write_new); + // Restore Sensor DN + Console.WriteLine($"3)Restore original Sensor DN Size as {current}"); + SetDn_mm(dut, current); + Console.WriteLine($"--- Finish Test ---"); + } } } catch (Exception ex) @@ -417,7 +682,6 @@ namespace modbus_master_csharp } } - #endregion enum RUN_ACTION { @@ -425,8 +689,194 @@ namespace modbus_master_csharp TEST, SAVE_CAL, LOAD_CAL, + ABORT_CAL, + SET_DN_SN, + SHOW_HELP, } + class args_inputs + { + public List comports; + public int? baudrate; + public Parity? parity; + public List loadedCalibrations; + public UInt16? DN_mm; + public UInt32? sensor_sn; + + public args_inputs() + { + comports = new List(); + baudrate = null; + parity = null; + loadedCalibrations = new List(); + sensor_sn = null; + } + } + + + #region Handle if arguments + static RUN_ACTION parse_arguments(string[] args, ref args_inputs ret_args_input) + { + bool gotArgs = (args.Length >= 1); + var run_task = RUN_ACTION.UNKNOWN; + var argErrors = gotArgs ? false : true; + // Use Default connection if nothing is provided by arguments + var arg_offset = 0; + if (args.Length > arg_offset) + { + { /*--------- Parse What task to do ---------*/ + if (args.Length > arg_offset) + { + if ("-H" == args[arg_offset].ToUpper()) + { + run_task = RUN_ACTION.SHOW_HELP; + } + else if ("TEST" == args[arg_offset].ToUpper()) + { + run_task = RUN_ACTION.TEST; + } + else if ("SAVE_CAL" == args[arg_offset].ToUpper()) + { + run_task = RUN_ACTION.SAVE_CAL; + } + else if ("ABORT_CAL" == args[arg_offset].ToUpper()) + { + run_task = RUN_ACTION.ABORT_CAL; + } + else if (args[arg_offset].ToUpper().StartsWith("SET_DN_SN=")) + { + run_task = RUN_ACTION.SET_DN_SN; + var values_as_txt = args[arg_offset].Split('=').Last().Split(','); + int value_offset = 0; + { // parse DN + var dn_mm_txt = values_as_txt[value_offset++]; + if (!UInt16.TryParse(dn_mm_txt, out var dn_mm)) + { + throw new InvalidDataException($"Error parsing DN \n{dn_mm_txt} as [mm]\n{GetArgHelpMessage(true, args)}"); + } + if ((dn_mm < 3) || (dn_mm > 3000)) + { + throw new InvalidDataException($"Error parsing DN \n{dn_mm}[mm] only 3 - 3000mm is allowed\n{GetArgHelpMessage(true, args)}"); + } + // Is okay + ret_args_input.DN_mm = dn_mm; + } + { // parse Sensor SN + var sn_txt = values_as_txt[value_offset++]; + if (!UInt32.TryParse(sn_txt, out var SN)) + { + throw new InvalidDataException($"Error parsing Sensor SN \n{sn_txt}\n{GetArgHelpMessage(true, args)}"); + } + // Is okay + ret_args_input.sensor_sn = SN; + } + + } + else if (args[arg_offset].ToUpper().StartsWith("LOAD_CAL=")) + { + run_task = RUN_ACTION.LOAD_CAL; + var calFilePath = args[arg_offset].Split('=').Last(); + var cal = new CalibrationPoints(); + if (!File.Exists(calFilePath) || !cal.LoadFromFile(calFilePath)) + { + throw new InvalidDataException($"Error load calibrations from\n {calFilePath}\n{GetArgHelpMessage(true, args)}"); + } + ret_args_input.loadedCalibrations.Add(cal); + } + else + { + throw new NotImplementedException($"Missing support for parameter {args[arg_offset]}\n{GetArgHelpMessage(true, args)}"); + } + } + } + arg_offset++; // Check next argument + { /*--------- Parse com port / ports ---------*/ + var exit = false; + var com_check_arg_offset = arg_offset; + while (!exit) + { + if (args.Length > com_check_arg_offset) + { + var arg = args[com_check_arg_offset].ToUpper(); + if ("COM" == arg.Remove(3)) + { + ret_args_input.comports.Add(arg); + arg_offset = com_check_arg_offset; // Save ret location for last added com + } + else if (ret_args_input.comports.Count <= 0) + { + Console.WriteLine($"Com port {arg} not understood."); + argErrors = true; + exit = true; + } + else + { + exit = true; // No more com to connect to + } + com_check_arg_offset++; // Check next argument + } + else + { + exit = true; // No more com to connect to + } + } + } + arg_offset++; // Check next argument + { /*--------- Parse Baud rate ---------*/ + if (args.Length > arg_offset) + { + var arg = args[arg_offset].ToUpper(); + var allowed_baudrate = new string[] { "9600", "19200", "38400", "57600", "115200", "230400" }; + if (allowed_baudrate.Contains(arg)) + { + ret_args_input.baudrate = int.Parse(arg); + } + else + { + Console.WriteLine($"Baud rate {arg} not known."); + ret_args_input.baudrate = -1; + argErrors = true; + } + } + } + arg_offset++; // Check next argument + { /*--------- Parse Parity ---------*/ + if (args.Length > arg_offset) + { + var arg = args[arg_offset].ToUpper(); + if (arg == "E") + { + ret_args_input.parity = Parity.Even; + } + else if (arg == "N") + { + ret_args_input.parity = Parity.None; + } + else if (arg == "O") + { + ret_args_input.parity = Parity.Odd; + } + else + { + Console.WriteLine($"Parity {arg} not known."); + ret_args_input.parity = Parity.None; + argErrors = true; + } + } + } + } + if (argErrors) + { + string message = GetArgHelpMessage(argErrors, args); + Console.WriteLine(message); + if (argErrors) + { + throw new Exception(message); // exit main + } + } + return run_task; + } + #endregion /// /// For unit testing setup @@ -448,159 +898,34 @@ namespace modbus_master_csharp #if (DEBUG) if (args.Length <= 0) { - if(LIVE_RUN) - { - args = argsDefault5; //metrology RS485 when transparent to metrology - } - else + if (MAIN_ARGS_OVERWRITE) { - args = argsDefault5; // Run the functional test as mock - } - if (LIVE_RUN_DIRECT_MET) - { - args = argsDefault1; //testing directly to metrology + args = MAIN_ARGS_OVERWRITE_WITH; //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) + var run_task = RUN_ACTION.UNKNOWN; + bool gotArgs = ((args != null) && (args.Length >= 1)); + var inputs = new args_inputs(); + if (gotArgs) { - // 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; - } - } + run_task = parse_arguments(args, ref inputs); } - if (argHelpNeeded) + else { - 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)}"" -"; + run_task = RUN_ACTION.SHOW_HELP; + } + if (run_task == RUN_ACTION.SHOW_HELP) + { + string message = GetArgHelpMessage(false, null); Console.WriteLine(message); - return; // exit main + return; } - #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) + foreach (var comport in inputs.comports) { if (runMode != RUN_TYPE.Normal) { @@ -616,13 +941,13 @@ To Load calibrations only include COMx to use standard baudrate and parity setup isOkay &=gotArgs; if (isOkay) { // Use standard and just setup as port - if (baudrate == null) + if (inputs.baudrate == null) { isOkay &=dut.Connect(comport); } - else if ((baudrate != null) && (parity != null)) + else if ((inputs.baudrate != null) && (inputs.parity != null)) { - isOkay &=dut.Connect(comport, baudrate.Value, parity.Value); + isOkay &=dut.Connect(comport, inputs.baudrate.Value, inputs.parity.Value); } else { @@ -654,7 +979,7 @@ To Load calibrations only include COMx to use standard baudrate and parity setup } } #endregion //#region Setup Communication to dut's - switch (runtask) + switch (run_task) { case RUN_ACTION.TEST: TEST(duts); @@ -664,14 +989,31 @@ To Load calibrations only include COMx to use standard baudrate and parity setup SAVE_CAL(duts); } break; + case RUN_ACTION.SET_DN_SN: + { + if(duts.Count() > 1) + { + throw new InvalidDataException($"Error support only a single device for setting DN and SN \n{GetArgHelpMessage(true, args)}"); + } + SET_DN_SN(duts.First(), inputs.DN_mm.Value, inputs.sensor_sn.Value); + int wait_ms = 15000; + Console.WriteLine($"Wait {wait_ms/1000} for device to reset after a possible DN change"); + Task.Delay(15000).GetAwaiter().GetResult(); + } + break; case RUN_ACTION.LOAD_CAL: { - LOAD_CAL(duts, loadedCalibrations); + LOAD_CAL(duts, inputs.loadedCalibrations); + } + break; + case RUN_ACTION.ABORT_CAL: + { + ABORT_CAL(duts); } break; default: { - throw new NotImplementedException($"Missing support run mode {runtask}"); + throw new NotImplementedException($"Missing support run mode {run_task}"); } } Console.WriteLine("finished functional testing");