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 } } } }