/// /// Copyright (c) 2015 Sensus Metering Systems /// using System; using System.Collections.Generic; using System.Drawing; using System.Globalization; using System.Runtime.InteropServices; using System.Text; using System.Threading; using System.Windows.Forms; using log4net; using Config.Entities; using TBF.Resources; using TBF.BenchControl.Sequences; using TBF.BenchControl.WaterMeters.iPerl; namespace TBF.BenchControl.TestMethods.iPerlCommunication { public enum CommErr { None = 0, CommFailed, OpenPort, Read, Write, CmdActive, CmdTest, Verify, } public partial class iPerlCommunicationForm : Form, GenericDevices.IHasCompleted { private static readonly ILog log = LogManager.GetLogger(typeof(iPerlCommunicationForm)); protected static readonly ILog rfidDataLogger = LogManager.GetLogger("RfidData"); #region DLL_Interface [DllImport("libRfid1.dll", EntryPoint = "getDllVersion")] static extern unsafe int getDllVersion1(); [DllImport("libRfid1.dll", EntryPoint = "openPort")] static extern unsafe int openPort1(int comPort); [DllImport("libRfid1.dll", EntryPoint = "closePort")] static extern unsafe int closePort1(); [DllImport("libRfid1.dll", EntryPoint = "readRequestPort")] static extern unsafe int readRequestPort1(MessageID messageId, int offset, int lenght, byte* ptr, int timeout_ms); [DllImport("libRfid1.dll", EntryPoint = "writeRequestPort")] static extern unsafe int writeRequestPort1(MessageID messageId, int offset, int lenght, byte* ptr, int timeout_ms); [DllImport("libRfid2.dll", EntryPoint = "getDllVersion")] public static extern unsafe int getDllVersion2(); [DllImport("libRfid2.dll", EntryPoint = "openPort")] static extern unsafe int openPort2(int comPort); [DllImport("libRfid2.dll", EntryPoint = "closePort")] static extern unsafe int closePort2(); [DllImport("libRfid2.dll", EntryPoint = "readRequestPort")] static extern unsafe int readRequestPort2(MessageID messageId, int offset, int lenght, byte* ptr, int timeout_ms); [DllImport("libRfid2.dll", EntryPoint = "writeRequestPort")] static extern unsafe int writeRequestPort2(MessageID messageId, int offset, int lenght, byte* ptr, int timeout_ms); [DllImport("libRfid3.dll", EntryPoint = "getDllVersion")] public static extern unsafe int getDllVersion3(); [DllImport("libRfid3.dll", EntryPoint = "openPort")] static extern unsafe int openPort3(int comPort); [DllImport("libRfid3.dll", EntryPoint = "closePort")] static extern unsafe int closePort3(); [DllImport("libRfid3.dll", EntryPoint = "readRequestPort")] static extern unsafe int readRequestPort3(MessageID messageId, int offset, int lenght, byte* ptr, int timeout_ms); [DllImport("libRfid3.dll", EntryPoint = "writeRequestPort")] static extern unsafe int writeRequestPort3(MessageID messageId, int offset, int lenght, byte* ptr, int timeout_ms); [DllImport("libRfid4.dll", EntryPoint = "getDllVersion")] public static extern unsafe int getDllVersion4(); [DllImport("libRfid4.dll", EntryPoint = "openPort")] static extern unsafe int openPort4(int comPort); [DllImport("libRfid4.dll", EntryPoint = "closePort")] static extern unsafe int closePort4(); [DllImport("libRfid4.dll", EntryPoint = "readRequestPort")] static extern unsafe int readRequestPort4(MessageID messageId, int offset, int lenght, byte* ptr, int timeout_ms); [DllImport("libRfid4.dll", EntryPoint = "writeRequestPort")] static extern unsafe int writeRequestPort4(MessageID messageId, int offset, int lenght, byte* ptr, int timeout_ms); /// /// Wrapper function with safe interface and unsafe body /// /// 0..3 /// Com port number /// Value returned by openPort(...) public static unsafe int OpenPort(WaterMeters.iPerl.WaterMeter wm) { if (wm.DebugLevel != DebugMode.Normal) return 0; /// No function in debug mode if (wm.RfidComPortNr > 0) { return openPort1(wm.RfidComPortNr); } else { switch (wm.MuxBoardNr) { default: case 1: return openPort1(cfg.RfidPortNrBoard1); case 2: return openPort2(cfg.RfidPortNrBoard2); case 3: return openPort3(cfg.RfidPortNrBoard3); case 4: return openPort4(cfg.RfidPortNrBoard4); } } } /// /// Wrapper function with safe interface and unsafe body /// /// 0..3 /// Com port number /// Value returned by openPort(...) public static unsafe int ClosePort(WaterMeters.iPerl.WaterMeter wm) { if (wm.DebugLevel != DebugMode.Normal) return 0; /// No function in debug mode if (wm.RfidComPortNr > 0) { return closePort1(); } else { switch (wm.MuxBoardNr) { default: case 1: return closePort1(); case 2: return closePort2(); case 3: return closePort3(); case 4: return closePort4(); } } } /// /// Wrapper function with safe interface and unsafe body /// /// Value returned by readRequestPort(...) public static unsafe int ReadRequestPort(WaterMeters.iPerl.WaterMeter wm, MessageID messageID, int offset, int lenght, out byte[] buffer, int timeout) { byte[] buf = new byte[200]; int retv; fixed (byte* pBuf = buf) { if (wm.DebugLevel == DebugMode.Normal) { if (wm.RfidComPortNr > 0) { retv = readRequestPort1(messageID, offset, lenght, pBuf, timeout); } else { switch (wm.MuxBoardNr) { default: case 1: retv = readRequestPort1(messageID, offset, lenght, pBuf, timeout); break; case 2: retv = readRequestPort2(messageID, offset, lenght, pBuf, timeout); break; case 3: retv = readRequestPort3(messageID, offset, lenght, pBuf, timeout); break; case 4: retv = readRequestPort4(messageID, offset, lenght, pBuf, timeout); break; } } } else { /// Simulation if (messageID == MessageID.Configuration) { } else if (messageID == MessageID.Calibration) { } else if (messageID == MessageID.MetrologyMemory) { } if (wm.Name.Equals("iPerl13")) retv = 2; else retv = 0; } buffer = new byte[lenght]; for (int i = 0; i < lenght; i++) buffer[i] = buf[i]; string name = string.Format("{0}({1})", wm.Name, wm.SerialNr); /// if ((messageID == MessageID.Configuration) && (offset == 0) && (lenght == WaterMeters.iPerl.ConfigStruct.Length)) { rfidDataLogger.InfoFormat("{0}: ReadRequestPort({1},...) returned {2} {3}", name, messageID, retv, (retv != 0) ? "!" : WaterMeters.iPerl.ConfigStruct.FromByteArray(buffer).ToString()); } else if ((messageID == MessageID.Calibration) && (offset == 0) && (lenght == WaterMeters.iPerl.CalibrationStruct.Length)) { rfidDataLogger.InfoFormat("{0}: ReadRequestPort({1},...) returned {2} {3}", name, messageID, retv, (retv != 0) ? "!" : WaterMeters.iPerl.CalibrationStruct.FromByteArray(buffer).ToString()); } else { rfidDataLogger.InfoFormat("{0}: ReadRequestPort({1}, {2}, {3}, ...) returned {4} {5}", name, messageID, offset, lenght, retv, (retv != 0) ? "!" : ""); } } return retv; } /// /// Wrapper function with safe interface adn unsafe body /// /// Value returned by writeRequestPort(...) public static unsafe int WriteRequestPort(WaterMeters.iPerl.WaterMeter wm, MessageID messageID, int offset, int lenght, byte[] buffer, int timeout) { int retv; fixed (byte* pBuf = buffer) { if (wm.DebugLevel == DebugMode.Normal) { if (wm.RfidComPortNr > 0) { retv = writeRequestPort1(messageID, offset, lenght, pBuf, timeout); } else { switch (wm.MuxBoardNr) { default: case 1: retv = writeRequestPort1(messageID, offset, lenght, pBuf, timeout); break; case 2: retv = writeRequestPort2(messageID, offset, lenght, pBuf, timeout); break; case 3: retv = writeRequestPort3(messageID, offset, lenght, pBuf, timeout); break; case 4: retv = writeRequestPort4(messageID, offset, lenght, pBuf, timeout); break; } } } else { /// Simulation if (messageID == MessageID.Configuration) { } else if (messageID == MessageID.Calibration) { } else if (messageID == MessageID.MetrologyMemory) { } retv = 0; } string name = string.Format("{0}({1})", wm.Name, wm.SerialNr); /// if (lenght == 1) { rfidDataLogger.InfoFormat("{0}: WriteRequestPort({2}, {3}, {4}, {5}) returned {1} {6}", name, retv, messageID, offset, lenght, pBuf[0].ToString("X2"), (retv != 0) ? "!" : ""); } else if (lenght == 2) { if (messageID == MessageID.Calibration && offset == 2) { UInt16 calibFactor = (UInt16)(pBuf[0] + 256 * pBuf[1]); rfidDataLogger.InfoFormat("{0}: WriteRequestPort(calibFactor={1}) returned {2} {3}", name, calibFactor, retv, (retv != 0) ? "!" : ""); } else { rfidDataLogger.InfoFormat("{0}: WriteRequestPort({2}, {3}, {4}, {5} {6}) returned {1} {7}", name, retv, messageID, offset, lenght, pBuf[0].ToString("X2"), pBuf[1].ToString("X2"), (retv != 0) ? "!" : ""); } } else { rfidDataLogger.InfoFormat("{0}: WriteRequestPort({2}, {3}, {4}, {5} {6} ...) returned {1} {7}", name, retv, messageID, offset, lenght, pBuf[0].ToString("X2"), pBuf[1].ToString("X2"), (retv != 0) ? "!" : ""); } } return retv; } #endregion DLL_Interface const string ReadConfigurationStr = "Read configuration"; /// Example: "Read configuration" or "Read configuration if enabled" const string SetTestModeStr = "Set Test mode"; /// Example: "Set Test mode" or "Set Test mode A0" (hexadecimal number is the required 'testModeConfig' const string SetActiveModeStr = "Set Active mode"; const string ReadCalibrationStr = "Read calibration"; const string WriteCalibrationFactorStr = "Write calibration factor"; const string ResetQ2CorrectionStr = "Reset Q2 correction"; const string WriteQ2CorrectionStr = "Write Q2 correction"; const string Reset2HzCorrectionStr = "Reset 2Hz correction"; const string Write2HzCorrectionStr = "Write 2Hz correction"; DateTime startTime; int startTimeSec; // Set to 'true' when the form closes public bool Completed { get { return formCompleted; } } bool formCompleted; /// Number of text boxes for serial numbers public int WaterMetersCount; int textBoxesCount; Label[] labels; TextBox[] counters; TextBox[] messages; static IList waterMeters; Modbus.QuidoRS.QuidoRS quido; /// /// RFID multiplexer PCB / RFID serial port and worker thread related variables /// static TestMethodCfg cfg; static IList tests; static IList multiTestParams; static int currentActivityStep; static int currentGroup; /// form -> worker thread (0 = none) static int lastGroup; static int completedCommCount; /// Number of completed communication steps static IList workerThreads; static IList rfidPortNrs; static bool stopWorkerThreads; /// form -> worker thread /// Parameterless constructor (without watermeters, threads) public iPerlCommunicationForm() { InitializeComponent(); textBoxesCount = 48; labels = new Label[48] { wmLabel1, wmLabel2, wmLabel3, wmLabel4, wmLabel5, wmLabel6, wmLabel7, wmLabel8, wmLabel9, wmLabel10, wmLabel11, wmLabel12, wmLabel13, wmLabel14, wmLabel15, wmLabel16, wmLabel17, wmLabel18, wmLabel19, wmLabel20, wmLabel21, wmLabel22, wmLabel23, wmLabel24, wmLabel25, wmLabel26, wmLabel27, wmLabel28, wmLabel29, wmLabel30, wmLabel31, wmLabel32, wmLabel33, wmLabel34, wmLabel35, wmLabel36, wmLabel37, wmLabel38, wmLabel39, wmLabel40, wmLabel41, wmLabel42, wmLabel43, wmLabel44, wmLabel45, wmLabel46, wmLabel47, wmLabel48, }; counters = new TextBox[48] { textBox1, textBox2, textBox3, textBox4, textBox5, textBox6, textBox7, textBox8, textBox9, textBox10, textBox11, textBox12, textBox13, textBox14, textBox15, textBox16, textBox17, textBox18, textBox19, textBox20, textBox21, textBox22, textBox23, textBox24, textBox25, textBox26, textBox27, textBox28, textBox29, textBox30, textBox31, textBox32, textBox33, textBox34, textBox35, textBox36, textBox37, textBox38, textBox39, textBox40, textBox41, textBox42, textBox43, textBox44, textBox45, textBox46, textBox47, textBox48, }; messages = new TextBox[48] { wmTextBox1, wmTextBox2, wmTextBox3, wmTextBox4, wmTextBox5, wmTextBox6, wmTextBox7, wmTextBox8, wmTextBox9, wmTextBox10, wmTextBox11, wmTextBox12, wmTextBox13, wmTextBox14, wmTextBox15, wmTextBox16, wmTextBox17, wmTextBox18, wmTextBox19, wmTextBox20, wmTextBox21, wmTextBox22, wmTextBox23, wmTextBox24, wmTextBox25, wmTextBox26, wmTextBox27, wmTextBox28, wmTextBox29, wmTextBox30, wmTextBox31, wmTextBox32, wmTextBox33, wmTextBox34, wmTextBox35, wmTextBox36, wmTextBox37, wmTextBox38, wmTextBox39, wmTextBox40, wmTextBox41, wmTextBox42, wmTextBox43, wmTextBox44, wmTextBox45, wmTextBox46, wmTextBox47, wmTextBox48, }; formCompleted = false; startTime = DateTime.Now; startTimeSec = StateMachine.Time; /// Find QuidoRS foreach (var comp in StateMachine.Components) { if (comp is Modbus.QuidoRS.QuidoRS) { quido = comp as Modbus.QuidoRS.QuidoRS; break; } } /// Attach to 'CommCompleted' handler CommCompletedHandler += delegate(object sender, CommCompletedEventArgs args) { if (InvokeRequired) { Invoke(new EventHandler(DoOnCommCompleted), sender, args); } else DoOnCommCompleted(sender, args); }; /// Attach to 'AllCompleted' handler AllCompletedHandler += delegate(object sender, AllCompletedEventArgs args) { if (InvokeRequired) { Invoke(new EventHandler(DoOnAllCompleted), sender, args); } else DoOnAllCompleted(sender, args); }; StartForceCloseHandler(); } /// /// Constructor for one iPerlCommunication 'test' /// /// Number of text boxes for serial numbers public iPerlCommunicationForm(TestMethodCfg cfg, Test test, iPerlCommunicationParams testParams) : this() { iPerlCommunicationForm.cfg = cfg; iPerlCommunicationForm.tests = new List(); iPerlCommunicationForm.tests.Add(test); iPerlCommunicationForm.multiTestParams = new List(); iPerlCommunicationForm.multiTestParams.Add(testParams); ProcessData.RegisterReaders = StateMachine.GetMetersPath(test).RegisterReaders; activityLabel.Text = testParams.Activity; ConstructorCommon(); } /// /// Constructor for multiple iPerlCommunication 'tests' /// /// Number of text boxes for serial numbers public iPerlCommunicationForm(TestMethodCfg cfg, IList tests, IList multiTestParams) : this() { iPerlCommunicationForm.cfg = cfg; iPerlCommunicationForm.tests = tests; iPerlCommunicationForm.multiTestParams = multiTestParams; if (multiTestParams.Count > 0) { ProcessData.RegisterReaders = StateMachine.GetMetersPath(tests[0]).RegisterReaders; activityLabel.Text = multiTestParams[0].Activity; } ConstructorCommon(); } void ConstructorCommon() { waterMeters = new List(); foreach (var rr in ProcessData.RegisterReaders) { if (rr is WaterMeters.iPerl.WaterMeter) { waterMeters.Add(rr as WaterMeters.iPerl.WaterMeter); } } WaterMetersCount = waterMeters.Count; ShuffleTextBoxes(WaterMetersCount, Config.Data.LineSize); /// /// Prepare worker threads, 'rfidPortNrs', 'lastGroup', etc.. /// workerThreads = new List(); rfidPortNrs = new List(); currentActivityStep = 0; currentGroup = 0; lastGroup = 0; /// group numbers are >=1, lastGroup == 0 means no group completedCommCount = 0; stopWorkerThreads = false; foreach (var iPerl in iPerlCommunicationForm.waterMeters) { if (!rfidPortNrs.Contains(iPerl.MuxBoardNr)) { rfidPortNrs.Add(iPerl.MuxBoardNr); if (workerThreads.Count < cfg.NrThreads) { Thread thread = new Thread(iPerlCommunicationForm.Worker); workerThreads.Add(thread); } } if (iPerl.Group > lastGroup) lastGroup = iPerl.Group; } StringBuilder sb = new StringBuilder(); sb.Append("rfidPortNrs = {"); foreach (var v in rfidPortNrs) sb.Append(string.Format(" {0}", v)); sb.Append(string.Format(" }, nrThreads = {0}", workerThreads.Count)); log.WarnFormat(sb.ToString()); } /// /// Make sure the layout of labels/text boxes on the screen /// corresponds to the layout of watermeters of the test bench. /// /// Number of watermeters /// Number of watermeters in one line void ShuffleTextBoxes(int wmsCount, int lineSize) { if (wmsCount < textBoxesCount && lineSize > 0) { int nrLines = (wmsCount + lineSize - 1) / lineSize; int gap = (textBoxesCount - wmsCount) / nrLines; int dest = 0; for (int l = 0; l < nrLines; l++) { for (int i = 0; i < lineSize; i++) { labels[dest] = labels[l * lineSize + l * gap + i]; counters[dest] = counters[l * lineSize + l * gap + i]; messages[dest] = messages[l * lineSize + l * gap + i]; dest++; } } textBoxesCount = wmsCount; } } void Localize() { Text = Strings.Water_Meter_States; for (int i = 0; i < textBoxesCount; i++) { labels[i].Text = string.Format("iPerl{0}", i + 1); } } private void iPerlCommunicationForm_Load(object sender, EventArgs e) { Localize(); //Height = 50 + 90 * WaterMetersCount; for (int i = 0; i < textBoxesCount; i++) { //if (disabled != null && i < disabled.Length && disabled[i]) //{ // labels[i].Visible = messages[i].Visible = false; //} //else { labels[i].Visible = counters[i].Visible = messages[i].Visible = true; messages[i].Text = "---"; } } TBF.LocalSettings ls = Program.LocalSettings; Left = (ls.iPerlCommunicationsFormLeft != 0) ? ls.iPerlCommunicationsFormLeft : 150; Top = (ls.iPerlCommunicationsFormTop != 0) ? ls.iPerlCommunicationsFormTop : 150; /// Reset opto-data indication for (int i = 0; i < waterMeters.Count; i++) { waterMeters[i].FlushedDataCount = 0; counters[i].Text = "0"; } /// /// Start worker threads, etc. /// if (workerThreads != null) { int wtId = 0; foreach (var wt in workerThreads) wt.Start(new Boxes.IntBox(wtId++)); /// /// Set QuidoRS outputs and start the whole communication process /// by incrementing 'currentGroup'. /// if (quido != null) { quido.SetOutputs((ushort)(16 - currentGroup - 1)); Thread.Sleep(200); } currentGroup++; } } private void NormalClose() { CommCompletedHandler = null; AllCompletedHandler = null; if (multiTestParams != null && multiTestParams.Count > 0 && multiTestParams[0].SimultWithPrevious) { UpdateRfidCommResult(tests); /// TODO: Pass the test info in a correct way } formCompleted = true; Program.LocalSettings.iPerlCommunicationsFormLeft = Location.X; Program.LocalSettings.iPerlCommunicationsFormTop = Location.Y; Program.LocalSettings.Save(); DialogResult = DialogResult.OK; Close(); } #region Forced close handling public void StartForceCloseHandler() { UiBridge.Bridge.CloseModelessFormHandler += delegate(object sender, EventArgs args) { if (InvokeRequired) { Invoke(new EventHandler(OnForceClose), sender, args); } else OnForceClose(sender, args); }; } void OnForceClose(object sender, EventArgs args) { CommCompletedHandler = null; AllCompletedHandler = null; stopWorkerThreads = true; DialogResult = DialogResult.Cancel; Close(); } #endregion /// /// Worker thread /// /// Thread ID (integer) wrapped into IntBox static void Worker(object threadData) { int threadId = (threadData as Boxes.IntBox).Val; int activityStep = 0; /// activity step > 0 in case multiTestParams are used for (int i = 0; i < multiTestParams.Count; i++ ) { iPerlCommunicationParams testParams = multiTestParams[i]; TBF.UiBridge.TestProgressEventArgs.SetEstimatedTimes(new int[] { 0, 0, 10, 140, 0, 0, 0 }); TBF.UiBridge.Bridge.OnTestProgress(null, new TBF.UiBridge.TestProgressEventArgs(tests[i], Config.Entities.Progress.JustStarted)); string activity = testParams.Activity; /// Current activity if (threadId == 0) { rfidDataLogger.InfoFormat(""); /// Makes the log more readable when there is a lot of data rfidDataLogger.WarnFormat("Activity = {0}", activity); rfidDataLogger.InfoFormat(""); /// Makes the log more readable when there is a lot of data } for (int group = 1; group <= lastGroup; group++) { /// Synchronize with QuidoRS and other threads while (((group != currentGroup) || (activityStep != currentActivityStep)) && !stopWorkerThreads) { Thread.Sleep(50); } if (stopWorkerThreads) break; for (int rfidPortIx = threadId; rfidPortIx < threadId + 4; rfidPortIx += cfg.NrThreads) { if (rfidPortIx >= rfidPortNrs.Count) break; // quit, all RFID ports of all water meters done int rfidPortNr = rfidPortNrs[rfidPortIx]; int wmNr = 0; bool wmFound = false; foreach (var wm in waterMeters) { if ((wm.MuxBoardNr == rfidPortNr) && (wm.Group == group)) { wmFound = true; CommErr error; string resultStr = string.Empty; if (activity.ToLower().Contains(ReadConfigurationStr.ToLower())) error = ReadConfiguration(wm, ref resultStr); else if (activity.ToLower().Contains(SetTestModeStr.ToLower())) error = SetTestMode(wm, ref resultStr); else if (activity.ToLower().Equals(SetActiveModeStr.ToLower())) error = SetActiveMode(wm, ref resultStr); else if (activity.ToLower().Equals(ReadCalibrationStr.ToLower())) error = ReadCalibration(wm, ref resultStr); else if (activity.ToLower().Contains(WriteCalibrationFactorStr.ToLower())) error = WriteCalibrationFactor(wm, ref resultStr); else if (activity.ToLower().Equals(ResetQ2CorrectionStr.ToLower())) error = ResetQ2Correction(wm, ref resultStr); else if (activity.ToLower().Equals(WriteQ2CorrectionStr.ToLower())) error = WriteQ2Correction(wm, ref resultStr); else if (activity.ToLower().Equals(Reset2HzCorrectionStr.ToLower())) error = Reset2HzCorrection(wm, ref resultStr); else if (activity.ToLower().Equals(Write2HzCorrectionStr.ToLower())) error = Write2HzCorrection(wm, ref resultStr); else { error = CommErr.None; resultStr = "Invalid activity"; } if (error == CommErr.None) { OnCommCompleted(null, new CommCompletedEventArgs(threadId, wmNr, resultStr)); } else if (wm.CommFailed || error == CommErr.CommFailed) { OnCommCompleted(null, new CommCompletedEventArgs(threadId, wmNr, "Watermeter is disabled")); } else { OnCommCompleted(null, new CommCompletedEventArgs(threadId, wmNr, string.Format("{0} failed ({1}) !!!", activity, error))); rfidDataLogger.ErrorFormat("Group={0}, Board={1}, {2} failed ({3}) !!!", currentGroup, wm.MuxBoardNr, activity, error); wm.CommFailed = true; } break; } wmNr++; TBF.UiBridge.Bridge.OnTestProgress(null, new TBF.UiBridge.TestProgressEventArgs(tests[i], Config.Entities.Progress.FlowSetting)); } if (!wmFound) { OnCommCompleted(null, new CommCompletedEventArgs(threadId, -1, string.Empty)); /// Send negative wmNr } if (stopWorkerThreads) break; } if (stopWorkerThreads) break; } /// for (int group TBF.UiBridge.Bridge.OnTestProgress(null, new TBF.UiBridge.TestProgressEventArgs(tests[i], Config.Entities.Progress.Completed)); activityStep++; if (stopWorkerThreads) break; } } /// /// Read a complete configuration structure of the watermeter /// /// Water meter object /// String passed to caller /// true on success static CommErr ReadConfiguration(WaterMeters.iPerl.WaterMeter wm, ref string resultStr) { /// /// The activity is "Read configuration" (this enables the watermeter, resets error flag) /// or "Read configuration if enabled" (this keeps th error flag). /// if (!multiTestParams[currentActivityStep].Activity.ToLower().Contains(" if enabled")) { wm.CommFailed = false; } if (wm.CommFailed) return CommErr.CommFailed; if (OpenPort(wm) != 0) return CommErr.OpenPort; /// Open RFID port CommErr error = CommErr.Read; /// Read configuration byte[] config = null; for (int j = 0; j < cfg.MaxCommRetries; j++) { if (0 == ReadRequestPort(wm, MessageID.Configuration, 0, ConfigStruct.Length, out config, cfg.CommTimeout)) { error = CommErr.None; wm.ConfigStruct = ConfigStruct.FromByteArray(config); resultStr = wm.ConfigStruct.ToString(1); break; } } ClosePort(wm); /// Close RFID port return error; } /// /// Set the watermeter to the test mode. /// If testModeConfig is specified as a hexadeximal number appended to "set test mode ", it is verified /// whether the testModeConfig is correctly set, if necessary it is changed to the specified value. /// Read a part of configuration afterwards to verify the mode was set correctly. /// /// Water meter object /// String passed to caller /// true on success static CommErr SetTestMode(WaterMeters.iPerl.WaterMeter wm, ref string resultStr) { if (wm.CommFailed) return CommErr.CommFailed; Byte testModeConfig = 0xA0; /// Default value /// if (multiTestParams[currentActivityStep].Activity.Length > SetTestModeStr.Length) { string testModeConfigStr = multiTestParams[currentActivityStep].Activity.Substring(SetTestModeStr.Length + 1); UInt16 byteVal; if (UInt16.TryParse(testModeConfigStr, NumberStyles.HexNumber, CultureInfo.CurrentCulture, out byteVal) && byteVal <= 255) { testModeConfig = (Byte)byteVal; /// Update with specified value } } if (wm.ConfigStruct.MeterState == MeterState.Test && wm.ConfigStruct.TestModeConfig == testModeConfig) { /// Already in the correct test mode resultStr = "Already " + wm.ConfigStruct.ToString(1); return CommErr.None; } /// Communication necessary if (OpenPort(wm) != 0) return CommErr.OpenPort; /// Open RFID port CommErr error = CommErr.None; if (error==CommErr.None && (wm.ConfigStruct.TestModeConfig != testModeConfig) && (wm.ConfigStruct.MeterState != MeterState.Active)) { /// Switch to Active mode in order to change TestModeConfig error = CommErr.CmdActive; byte[] cmd = new byte[1] { (byte)6 }; for (int j = 0; j < cfg.MaxCommRetries; j++) { if (0 == WriteRequestPort(wm, MessageID.Command, 0, 1, cmd, cfg.CommTimeout)) { error = CommErr.None; break; } } Thread.Sleep(250); } if (error == CommErr.None && (wm.ConfigStruct.TestModeConfig != testModeConfig)) { /// Change the TestModeConfig if necessary error = CommErr.Write; byte[] tstMdCfg = new byte[1] { testModeConfig }; for (int j = 0; j < cfg.MaxCommRetries; j++) { if (0 == WriteRequestPort(wm, MessageID.Configuration, 21, 1, tstMdCfg, cfg.CommTimeout)) { wm.ConfigStruct.Update(21, tstMdCfg); error = CommErr.None; break; } } Thread.Sleep(250); } if (error == CommErr.None) { /// Switch to test mode error = CommErr.CmdTest; byte[] cmd = new byte[1] { (byte)7 }; for (int j = 0; j < cfg.MaxCommRetries; j++) { if (0 == WriteRequestPort(wm, MessageID.Command, 0, 1, cmd, cfg.CommTimeout)) { error = CommErr.None; break; } } Thread.Sleep(250); } /// Now the meter should be in the Test mode ... verify if (error == CommErr.None) { /// Verify the configuration error = CommErr.Verify; byte[] cfg_0_3 = null; for (int j = 0; j < cfg.MaxCommRetries; j++) { if (0 == ReadRequestPort(wm, MessageID.Configuration, 0, 4, out cfg_0_3, cfg.CommTimeout)) { wm.ConfigStruct.Update(0, cfg_0_3); if (wm.ConfigStruct.MeterState == MeterState.Test) { error = CommErr.None; resultStr = wm.ConfigStruct.ToString(1); break; } } } } ClosePort(wm); /// Close RFID port return error; } /// /// Set the watermeter to the active mode. /// Read a part of configuration afterwards to verify the mode was set correctly. /// /// Water meter object /// String passed to caller /// true on success static CommErr SetActiveMode(WaterMeters.iPerl.WaterMeter wm, ref string resultStr) { if (OpenPort(wm) != 0) return CommErr.OpenPort; /// Open RFID port CommErr error = CommErr.CmdActive; /// Switch to active mode byte[] cmd = new byte[1] { (byte)6 }; for (int j = 0; j < cfg.MaxCommRetries; j++) { if (0 == WriteRequestPort(wm, MessageID.Command, 0, 1, cmd, cfg.CommTimeout)) { error = CommErr.None; break; } } #if false if (error == CommErr.None) { error = CommErr.Verify; /// Read configuration byte[] cfg_0_3 = null; for (int j = 0; j < cfg.MaxCommRetries; j++) { if (wm.ConfigStruct == null) { if (0 == ReadRequestPort(wm, MessageID.Configuration, 0, 4, out cfg_0_3, cfg.CommTimeout)) { wm.ConfigStruct.Update(0, cfg_0_3); if (wm.ConfigStruct.MeterState == MeterState.Active) { error = CommErr.None; resultStr = wm.ConfigStruct.ToString(1); break; } } } } } #else if (error == CommErr.None) { if (wm.ConfigStruct == null) resultStr = "OK (Config not available)"; else resultStr = wm.ConfigStruct.ToString(1); } #endif ClosePort(wm); /// Close RFID port return error; } /// /// Read a complete calibration structure from the watermeter /// /// Water meter object /// String passed to caller /// true on success static CommErr ReadCalibration(WaterMeters.iPerl.WaterMeter wm, ref string resultStr) { if (wm.CommFailed) return CommErr.CommFailed; if (OpenPort(wm) != 0) return CommErr.OpenPort; /// Open RFID port CommErr error = CommErr.Read; /// Read calibration byte[] calib = null; for (int j = 0; j < cfg.MaxCommRetries; j++) { if (0 == ReadRequestPort(wm, MessageID.Calibration, 0, CalibrationStruct.Length, out calib, cfg.CommTimeout)) { error = CommErr.None; wm.CalibrationStruct = CalibrationStruct.FromByteArray(calib); resultStr = wm.CalibrationStruct.ToString(); break; } } ClosePort(wm); /// Close RFID port return error; } /// /// Write the calculated calibration factor to the water meter. /// Read a part of CalibrationStruct afterwards to verify factor was written correctly. /// /// Water meter object /// String passed to caller /// true on success static CommErr WriteCalibrationFactor(WaterMeters.iPerl.WaterMeter wm, ref string resultStr) { if (wm.CommFailed) return CommErr.CommFailed; if (OpenPort(wm) != 0) return CommErr.OpenPort; /// Open RFID port CommErr error = CommErr.Write; /// Parse calibration factor (1 argument) or calibration factor limits (2 arguments) UInt16 newCalibFactor = 0; if (multiTestParams[currentActivityStep].Activity.Length > WriteCalibrationFactorStr.Length) { UInt16 factorLimitLo; UInt16 factorLimitHi; UInt16 val; string calibFactrorStr = multiTestParams[currentActivityStep].Activity.Substring(WriteCalibrationFactorStr.Length + 1); string[] arguments = calibFactrorStr.Split(new char[] { ' ' }); if (arguments.Length >= 2 && UInt16.TryParse(arguments[0], out factorLimitLo) && factorLimitLo > 0 && UInt16.TryParse(arguments[1], out factorLimitHi) && factorLimitHi > 0) { newCalibFactor = wm.CalculateNewCalibFactor(wm.CalibrationFactor, factorLimitLo, factorLimitHi); } else if (arguments.Length == 1 && UInt16.TryParse(arguments[0], out val) && val > 0) { newCalibFactor = val; /// Update with specified value } else { newCalibFactor = 2710; /// Default calibration factor for DN15 } } else { newCalibFactor = wm.CalculateNewCalibFactor(wm.CalibrationFactor, 2400, 2700); /// Default factor limits for DN15 } byte[] data = new byte[2] { (byte)(newCalibFactor & 0x00FF), (byte)((newCalibFactor >> 8) & 0x00FF) }; for (int j = 0; j < cfg.MaxCommRetries; j++) { if (0 == WriteRequestPort(wm, MessageID.Calibration, 2, 2, data, cfg.CommTimeout)) { error = CommErr.None; wm.CalibrationStruct.Update(data, 2); break; } } if (error == CommErr.None) { error = CommErr.Verify; /// Read calibration byte[] calib_2_3 = null; for (int j = 0; j < cfg.MaxCommRetries; j++) { if (0 == ReadRequestPort(wm, MessageID.Calibration, 2, 2, out calib_2_3, cfg.CommTimeout)) { error = CommErr.None; wm.CalibrationStruct.Update(calib_2_3, 2); resultStr = wm.CalibrationStruct.ToString(); break; } } } ClosePort(wm); /// Close RFID port return error; } const int Hz2CorrFactorsAddr = 0x1875; /// Used by Reset2HzCorrection(...) and Write2HzCorrection(...) const int Q2CorrFactorsAddr = 0x1878; /// Used by ResetQ2Correction(...) and WriteQ2Correction(...) /// /// Reset both Q2 correction factors in the memory to 0. /// Read them back to verify factors were written correctly. /// /// Water meter object /// String passed to caller /// true on success static CommErr ResetQ2Correction(WaterMeters.iPerl.WaterMeter wm, ref string resultStr) { if (wm.CommFailed) return CommErr.CommFailed; if (OpenPort(wm) != 0) return CommErr.OpenPort; /// Open RFID port CommErr error = CommErr.Write; /// Write zero Q2 correction byte[] wrData = new byte[2] { 0, 0 }; /// for (int j = 0; j < cfg.MaxCommRetries; j++) { if (0 == WriteRequestPort(wm, MessageID.MetrologyMemory, Q2CorrFactorsAddr, wrData.Length, wrData, cfg.CommTimeout)) { error = CommErr.None; break; } } /// /// Verification disabled on 16.02.2016 /// #if false /// Verify the correction factors if (error == CommErr.None) { error = CommErr.Verify; /// Read calibration byte[] rdData = null; for (int j = 0; j < cfg.MaxCommRetries; j++) { if ((0 == ReadRequestPort(wm, MessageID.MetrologyMemory, Q2CorrFactorsAddr, 2, out rdData, cfg.CommTimeout)) && (rdData != null) && (rdData.Length == 2) && (rdData[0] == 0) && (rdData[1] == 0)) { error = CommErr.None; resultStr = "Q2 correction factors reset to 0"; wm.Q2CorrectionFactor = 0; break; } } } #else if (error == CommErr.None) { resultStr = "Q2 corrections reset to 0"; wm.Q2CorrectionFactor = 0; } #endif ClosePort(wm); /// Close RFID port return error; } /// /// Reset 2Hz correction factor in the memory to 0. /// /// Water meter object /// String passed to caller /// true on success static CommErr Reset2HzCorrection(WaterMeters.iPerl.WaterMeter wm, ref string resultStr) { if (wm.CommFailed) return CommErr.CommFailed; if (OpenPort(wm) != 0) return CommErr.OpenPort; /// Open RFID port CommErr error = CommErr.Write; /// Write zero Q2 correction byte[] wrData = new byte[1] { 0 }; /// for (int j = 0; j < cfg.MaxCommRetries; j++) { if (0 == WriteRequestPort(wm, MessageID.MetrologyMemory, Hz2CorrFactorsAddr, wrData.Length, wrData, cfg.CommTimeout)) { error = CommErr.None; break; } } /// /// Verification disabled on 16.02.2016 /// #if false /// Verify the correction factors if (error == CommErr.None) { error = CommErr.Verify; /// Read calibration byte[] rdData = null; for (int j = 0; j < cfg.MaxCommRetries; j++) { if ((0 == ReadRequestPort(wm, MessageID.MetrologyMemory, Hz2CorrFactorsAddr, 1, out rdData, cfg.CommTimeout)) && (rdData != null) && (rdData.Length == 2) && (rdData[0] == 0) && (rdData[1] == 0)) { error = CommErr.None; resultStr = "2Hz correction reset to 0"; wm.Q2CorrectionFactor = 0; break; } } } #else if (error == CommErr.None) { resultStr = "2Hz correction reset to 0"; wm.Hz2CorrectionFactor = 0; } #endif ClosePort(wm); /// Close RFID port return error; } /// /// Write the calculated Q2 correction factors to the memory. /// Read them back to verify factors were written correctly. /// /// Water meter object /// String passed to caller /// true on success static CommErr WriteQ2Correction(WaterMeters.iPerl.WaterMeter wm, ref string resultStr) { if (wm.CommFailed) return CommErr.CommFailed; wm.Q2ErrorWOCorrection = wm.LastTestResult.Error; wm.Q2CorrectionDone = false; double q2CorrectionFactor; bool wmOK = wm.CalculateQ2CorrectionFactor(wm.LastTestResult, out q2CorrectionFactor); if (q2CorrectionFactor == 0) { resultStr = string.Format("Q2 correction = 0 (writing bypassed)"); return CommErr.None; } if (OpenPort(wm) != 0) return CommErr.OpenPort; /// Open RFID port CommErr error = CommErr.Write; Byte q2CorrRFlow = (byte)((int)Math.Round(q2CorrectionFactor / 2) & 0x000000FF); Byte q2CorrLFlow = (byte)((int)Math.Round(q2CorrectionFactor) & 0x000000FF); byte[] wrData = new byte[2] { q2CorrRFlow, q2CorrLFlow }; /// for (int j = 0; j < cfg.MaxCommRetries; j++) { if (0 == WriteRequestPort(wm, MessageID.MetrologyMemory, Q2CorrFactorsAddr, wrData.Length, wrData, cfg.CommTimeout)) { error = CommErr.None; break; } } /// /// Verification disabled on 16.02.2016 /// #if false if (error == CommErr.None) { error = CommErr.Verify; /// Read calibration byte[] rdData = null; for (int j = 0; j < cfg.MaxCommRetries; j++) { if ((0 == ReadRequestPort(wm, MessageID.MetrologyMemory, Q2CorrFactorsAddr, 2, out rdData, cfg.CommTimeout)) && (rdData != null) && (rdData.Length == 2) && (rdData[0] == q2CorrRFlow) && (rdData[1] == q2CorrLFlow)) { error = CommErr.None; resultStr = string.Format("Q2 factors: R-flow={0}, L-flow={1}", (SByte)q2CorrRFlow, (SByte)q2CorrLFlow); rfidDataLogger.Warn(wm.Name + ": " + resultStr); wm.Q2CorrectionDone = true; wm.Q2CorrectionFactor = q2CorrectionFactor; break; } } } #else if (error == CommErr.None) { resultStr = string.Format("Q2 correction: R-flow={0}, L-flow={1}", (SByte)q2CorrRFlow, (SByte)q2CorrLFlow); rfidDataLogger.Warn(wm.Name + ": " + resultStr); wm.Q2CorrectionDone = true; wm.Q2CorrectionFactor = q2CorrectionFactor; } #endif ClosePort(wm); /// Close RFID port return error; } /// /// Write the calculated 2Hz correction factor to the memory. /// /// Water meter object /// String passed to caller /// true on success static CommErr Write2HzCorrection(WaterMeters.iPerl.WaterMeter wm, ref string resultStr) { if (wm.CommFailed) return CommErr.CommFailed; /// Calculate the correction wm.Hz2CorrectionDone = false; int hz2CorrectionFactor; bool wmOK = wm.Calculate2HzCorrectionFactor(wm.LastTestResult2, wm.LastTestResult, out wm.Diff2Hz8Hz, out hz2CorrectionFactor); if (hz2CorrectionFactor == 0) { resultStr = string.Format("2Hz correction = 0 (writing bypassed)"); return CommErr.None; } if (OpenPort(wm) != 0) return CommErr.OpenPort; /// Open RFID port CommErr error = CommErr.Write; Byte hz2CorrectionByte = (byte)(hz2CorrectionFactor & 0x000000FF); byte[] wrData = new byte[1] { hz2CorrectionByte }; /// for (int j = 0; j < cfg.MaxCommRetries; j++) { if (0 == WriteRequestPort(wm, MessageID.MetrologyMemory, Hz2CorrFactorsAddr, wrData.Length, wrData, cfg.CommTimeout)) { error = CommErr.None; break; } } /// /// Verification disabled on 16.02.2016 /// #if false if (error == CommErr.None) { error = CommErr.Verify; /// Read calibration byte[] rdData = null; for (int j = 0; j < cfg.MaxCommRetries; j++) { if ((0 == ReadRequestPort(wm, MessageID.MetrologyMemory, Hz2CorrFactorsAddr, 1, out rdData, cfg.CommTimeout)) && (rdData != null) && (rdData.Length == 2) && (rdData[0] == hz2CorrectionByte)) { error = CommErr.None; resultStr = string.Format("2Hz factor = {0}", (SByte)hz2CorrectionByte); rfidDataLogger.Warn(wm.Name + ": " + resultStr); wm.Hz2CorrectionFactor = hz2CorrectionFactor; break; } } } #else if (error == CommErr.None) { resultStr = string.Format("2Hz correction = {0}", (SByte)hz2CorrectionByte); rfidDataLogger.Warn(wm.Name + ": " + resultStr); wm.Hz2CorrectionDone = true; wm.Hz2CorrectionFactor = hz2CorrectionFactor; } #endif ClosePort(wm); /// Close RFID port return error; } /// /// Called when communication with one watermeter is completed /// public static void OnCommCompleted(object sender, CommCompletedEventArgs data) { if (CommCompletedHandler == null) return; try { CommCompletedHandler(sender, data); } catch (Exception e) { log.Error("CommCompletedHandler(...) failed", e); } } public static event EventHandler CommCompletedHandler; void DoOnCommCompleted(object sender, CommCompletedEventArgs data) { if (data.WMNr >= 0) messages[data.WMNr].Text = data.CommMessage; for (int i = 0; i < waterMeters.Count; i++) { counters[i].Text = (waterMeters[i].FlushedDataCount % 1000).ToString(); } lock (this) { if (++completedCommCount < rfidPortNrs.Count) return; completedCommCount = 0; } if (currentGroup < lastGroup) { /// Go to the next step / next group if (quido != null) { quido.SetOutputs((ushort)(16 - currentGroup - 1)); Thread.Sleep(100); } currentGroup++; } else if (currentActivityStep + 1 < multiTestParams.Count) { currentGroup = 0; currentActivityStep++; activityLabel.Text = multiTestParams[currentActivityStep].Activity; if (quido != null) { quido.SetOutputs((ushort)(16 - currentGroup - 1)); Thread.Sleep(100); } currentGroup++; } else { /// Wait until all threads are finished workerThreads[data.ThreadId].Join(2000); NormalClose(); } } /// /// Called when communication with all watermeters is completed /// public static void OnAllCompleted(object sender, AllCompletedEventArgs data) { if (AllCompletedHandler == null) return; try { AllCompletedHandler(sender, data); } catch (Exception e) { log.Error("AllCompletedHandler(...) failed", e); } } public static event EventHandler AllCompletedHandler; void DoOnAllCompleted(object sender, AllCompletedEventArgs data) { Text = data.CommMessage; } /// /// Update test result representing RFID communication success/failure /// /// void UpdateRfidCommResult(IList tests) { DateTime endTime = DateTime.Now; int testTime = StateMachine.Time - startTimeSec; if (!tests.Contains(StateMachine.Tests[0])) { tests.Insert(0, StateMachine.Tests[0]); /// Add RFID test as the 1st item } foreach (var test in tests) { Results.Entities.TestRslt tstRslt = ProcessData.BatchRslts.GetTestRslt(test.Name, test.Part); if (tstRslt != null) { /// Auxiliary results ... not required /// Main results tstRslt.StartTime = tstRslt.Batch.StartTime; tstRslt.EndTime = endTime; tstRslt.FlowSetTime = 0; tstRslt.TestTime += testTime; /// [s] total communication time of all tests for (int i = 0; i < Math.Min(ProcessData.BatchRslts.WMPositionsCount, waterMeters.Count); i++) { Results.Entities.MeterTestRslt meterRslt = ProcessData.BatchRslts.GetMeterTestRslt(test.Name, i, Config.Entities.CompoundMeterId.Single); WaterMeters.iPerl.WaterMeter iPerl = waterMeters[i] as WaterMeters.iPerl.WaterMeter; if (meterRslt != null && iPerl != null) { meterRslt.WaterMeter.SerialNr = iPerl.SerialNr; meterRslt.Passed = (!iPerl.CommFailed && !iPerl.Disabled); meterRslt.TestDone = true; } } } } } } }