/// /// Copyright (c) 2015-2019 Sensus Slovensko a.s. /// //#define VERIFY_ACTIVE_MODE //#define VERIFY_Q2_CORR_RESET 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; using Config.Entities; using TBF.Resources; using TBF.BenchControl.Sequences; using TBF.BenchControl.TestMethods.iPerlCommunication.iPerlHead; namespace TBF.BenchControl.TestMethods.iPerlCommunication { public enum CommErr { None = 0, CommFailed, /// 1 OpenPort, /// 2 Read, /// 3 Read1, /// 4 Read2, /// 5 Read3, /// 6 Read4, /// 7 Write, /// 8 ReadAfterWrite, /// 9 CmdActive, /// A = 10 CmdTest, /// B = 11 Verify, /// C = 12 WrongIPerlType, /// D = 13 OutOfRange, /// E = 14 Q2OutOfRange, /// F = 15 MissingTest, /// 10H = 16 RFPowerRecordMissing, /// 11H = 17 HeadDisabledByUser, /// 12H = 18 WrongArguments, /// 13H = 19 } public partial class iPerlCommunicationForm : Form, GenericDevices.IHasCompleted { private static readonly ILog log = LogManager.GetLogger(typeof(iPerlCommunicationForm)); protected static readonly ILog rfidDataLogger = LogManager.GetLogger("RfidData"); const int Hz2CorrFactorsAddr = 0x1875; /// Used by Reset2HzCorrection(...) and Write2HzCorrection(...) const int Q2CorrFactorsAddr = 0x1878; /// Used by ResetQ2Correction(...) and WriteQ2Correction(...) const int Q2CorrFactorsAddrLR = 0x1878; const int Q2CorrFactorsAddrRL = 0x1879; public const string ReadConfigurationStr = "Read configuration"; /// Example: "Read configuration" or "Read configuration if enabled" public const string SetTestModeStr = "Set Test mode"; /// Example: "Set Test mode" or "Set Test mode A0" (hexadecimal number is the required 'testModeConfig' public const string SetActiveModeStr = "Set Active mode"; public const string ReadCalibrationStr = "Read calibration"; public const string ReadCalibrationV4Str = "Read calibration_V4"; public const string WriteCalibrationFactorStr = "Write calibration factor"; public const string WriteCalibrationV4FactorsStr = "Write calibration_V4"; public const string NormalizeCalibrationFactorStr = "Normalize calibration factor"; public const string NormalizeCalibrationV4FactorsStr = "Normalize calibration_V4"; public const string ReadQ2CorrectionStr = "Read Q2 corrections"; public const string ResetQ2CorrectionStr = "Reset Q2 correction"; public const string InitOrReadQ2CorrectionStr = "Init or read Q2 corrections"; public const string WriteQ2CorrectionStr = "Write Q2 correction"; /// No arguments public const string WriteQ2CorrectionAltStr = "Write Q2 correction Alt"; /// No arguments public const string WriteQ2CorrectionGreeceStr = "Write Q2 correction Greece"; /// Arguments: test_name public const string WriteQ2CorrectionRLStr = "Write Q2 correction R-L"; /// Arguments: test_name public const string WriteQ2CorrectionLRStr = "Write Q2 correction L-R"; /// Arguments: test_name public const string UpdateQ2CorrectionsStr = "Update Q2 corrections"; /// Arguments: R-L_test_name L-R_test_name public const string ConditnlUpdateQ2CorrectionsStr = "Conditional update of Q2 corrections"; /// Arguments: R-L_test_name L-R_test_name public const string Reset2HzCorrectionStr = "Reset 2Hz correction"; public const string Write2HzCorrectionStr = "Write 2Hz correction"; public static Color DisabledColor = Color.DarkGray; public static Color OptoAndDirOKColor = Color.Green; public static Color OptoNokColor = Color.Orange; public static Color DirNokColor = Color.Red; #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 /// Water meter (iPerlHead) object /// Value returned by openPort(...) public static unsafe int OpenPort(int threadID, IperlHead iperlHead) { if (iperlHead.DebugLevel != DebugMode.Normal) return 0; /// No function in debug mode int rfidPortNr; if (cfg.UseMuxBoards) { switch (iperlHead.MuxBoardNrOrGroup14) { default: case 1: rfidPortNr = cfg.RfidPortNrBoard1; break; case 2: rfidPortNr = cfg.RfidPortNrBoard2; break; case 3: rfidPortNr = cfg.RfidPortNrBoard3; break; case 4: rfidPortNr = cfg.RfidPortNrBoard4; break; } } else { rfidPortNr = iperlHead.RfidComPortNr; } switch (threadID) { default: case 0: return openPort1(rfidPortNr); case 1: return openPort2(rfidPortNr); case 2: return openPort3(rfidPortNr); case 3: return openPort4(rfidPortNr); } } /// /// Wrapper function with safe interface and unsafe body /// /// 0..3 /// Water meter (iPerlHead) object /// Value returned by openPort(...) public static unsafe int ClosePort(int threadID, IperlHead iperlHead) { if (iperlHead.DebugLevel != DebugMode.Normal) return 0; /// No function in debug mode switch (threadID) { default: case 0: return closePort1(); case 1: return closePort2(); case 2: return closePort3(); case 3: return closePort4(); } } /// /// Wrapper function with safe interface and unsafe body /// /// 0..3 /// Water meter (iPerlHead) object /// Value returned by readRequestPort(...) public static unsafe int ReadRequestPort(int threadID, IperlHead iperlHead, MessageID messageID, int offset, int lenght, out byte[] buffer, int timeout) { buffer = new byte[lenght]; if (iperlHead.DebugLevel != DebugMode.Normal) return iperlHead.Name.Equals("iPerl13") ? 2 : 0; /// Simulates an error on position 13 byte[] buf = new byte[200]; /// fixed (byte* pBuf = buf) { int retv; switch (threadID) { default: case 0: retv = readRequestPort1(messageID, offset, lenght, pBuf, timeout); break; case 1: retv = readRequestPort2(messageID, offset, lenght, pBuf, timeout); break; case 2: retv = readRequestPort3(messageID, offset, lenght, pBuf, timeout); break; case 3: retv = readRequestPort4(messageID, offset, lenght, pBuf, timeout); break; } for (int i = 0; i < lenght; i++) buffer[i] = buf[i]; /// /// Logging /// string name = string.Format("{0}({1})", iperlHead.Name, iperlHead.SerialNr); if ((messageID == MessageID.Configuration) && (offset == 0) && (lenght == ConfigStruct.Length)) rfidDataLogger.InfoFormat("{0}: ReadRequestPort({1},...) returned {2} {3}", name, messageID, retv, (retv != 0) ? "!" : ConfigStruct.FromByteArray(buffer).ToString()); else if ((messageID == MessageID.Calibration) && (offset == 0) && (lenght == CalibrationStruct.Length)) rfidDataLogger.InfoFormat("{0}: ReadRequestPort({1},...) returned {2} {3}", name, messageID, retv, (retv != 0) ? "!" : CalibrationStruct.FromByteArray(buffer).ToString()); else if ((messageID == MessageID.Calibration) && (offset == 0) && (lenght == CalibrationStructV4.Length)) rfidDataLogger.InfoFormat("{0}: ReadRequestPort({1},...) returned {2} {3}", name, messageID, retv, (retv != 0) ? "!" : CalibrationStructV4.FromByteArray(buffer).ToString()); else if ((messageID == MessageID.Calibration) && (offset == 2) && (lenght == 2)) rfidDataLogger.InfoFormat("{0}: ReadRequestPort({1},2,2,...) returned {2} {3}", name, messageID, retv, (retv != 0) ? "!" : string.Format("Cal={0}", buf[0] + 256 * buf[1])); else if ((messageID == MessageID.Calibration) && (offset == 34) && (lenght == 2)) rfidDataLogger.InfoFormat("{0}: ReadRequestPort({1},34,2,...) returned {2} {3}", name, messageID, retv, (retv != 0) ? "!" : string.Format("CalLNA={0}", buf[0] + 256 * buf[1])); else if ((messageID == MessageID.MetrologyMemory) && (offset == Q2CorrFactorsAddr) && (lenght == 2)) rfidDataLogger.InfoFormat("{0}: ReadRequestPort({1},6264,2,...) returned {2} {3}", name, messageID, retv, (retv != 0) ? "!" : string.Format("LR={0} RL={1}", (int)buf[0], (int)buf[1])); 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 /// /// 0..3 /// Water meter (iPerlHead) object /// Value returned by writeRequestPort(...) public static unsafe int WriteRequestPort(int threadID, IperlHead iperlHead, MessageID messageID, int offset, int lenght, byte[] buffer, int timeout) { if (iperlHead.DebugLevel != DebugMode.Normal) return 0; fixed (byte* pBuf = buffer) { int retv; switch (threadID) { default: case 0: retv = writeRequestPort1(messageID, offset, lenght, pBuf, timeout); break; case 1: retv = writeRequestPort2(messageID, offset, lenght, pBuf, timeout); break; case 2: retv = writeRequestPort3(messageID, offset, lenght, pBuf, timeout); break; case 3: retv = writeRequestPort4(messageID, offset, lenght, pBuf, timeout); break; } Thread.Sleep(250); /// /// Logging /// string name = string.Format("{0}({1})", iperlHead.Name, iperlHead.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(Cal={1}) returned {2} {3}", name, calibFactor, retv, (retv != 0) ? "!" : ""); } else if (messageID == MessageID.Calibration && offset == 34) { UInt16 calibFactor = (UInt16)(pBuf[0] + 256 * pBuf[1]); rfidDataLogger.InfoFormat("{0}: WriteRequestPort(CalLNA={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 readonly bool checkBoxesEditMode; DateTime startTime; int startTimeSec; // Set to 'true' when the form closes public bool Completed { get { return formCompleted; } } bool formCompleted; bool forcedClose; /// Set in the forced close handler /// Number of text boxes for serial numbers public int WaterMetersCount; const int MaxTextBoxesCount = 48; int textBoxesCount; Label[] labels; PictureBox[] counters; TextBox[] messages; CheckBoxImage[] checkBoxes; static int[] ckbIndex; static bool[] ckbState; static IList iperlHeads; static IList waterMeterPositions0; /// keeps original 0-based indices in RegisterReaders list 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 muxBrdOrGroup14Nrs; static bool stopWorkerThreads; /// form -> worker thread /// Parameterless constructor (without watermeters, threads) public iPerlCommunicationForm() { InitializeComponent(); } /// /// Constructor for checkBox states (active/inactive iPerl head) editing. /// /// Initial check box states public iPerlCommunicationForm(bool isCheckBoxesEditMode) : this() { if (isCheckBoxesEditMode) { checkBoxesEditMode = true; saveButton.Visible = true; activityLabel.Text = Strings.Activate_Deactivate_heads; ShuffleTextBoxes(Config.Data.WMsCount, Config.Data.LineSize); } } /// /// Constructor for one iPerlCommunication 'test' /// /// Number of text boxes for serial numbers public iPerlCommunicationForm(TestMethodCfg cfg, Test test, iPerlCommunicationParams testParams) : this(cfg, new List { test }, new List { testParams }) { } /// /// Constructor for multiple iPerlCommunication 'tests' /// /// Number of text boxes for serial numbers public iPerlCommunicationForm(TestMethodCfg cfg, IList tests, IList multiTestParams) : this() { checkBoxesEditMode = false; 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() { startTime = DateTime.Now; startTimeSec = StateMachine.Time; /// Find QuidoRS quido = null; if (StateMachine.Components != null) { foreach (var comp in StateMachine.Components) { Modbus.QuidoRS.QuidoRS q = comp as Modbus.QuidoRS.QuidoRS; if ((q != null) && (q.Variant == Modbus.QuidoRS.QuidoVariant.QuidoRS_2_16)) { quido = q; 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); }; formCompleted = false; StartForceCloseHandler(); iperlHeads = new List(); waterMeterPositions0 = new List(); /// for (int wmPos = 0; wmPos < ProcessData.RegisterReaders.Length; wmPos++) { IperlHead iperlHead = ProcessData.RegisterReaders[wmPos] as IperlHead; if (iperlHead != null) { iperlHeads.Add(iperlHead); waterMeterPositions0.Add(wmPos); } } WaterMetersCount = iperlHeads.Count; ShuffleTextBoxes(WaterMetersCount, Config.Data.LineSize); /// /// Prepare worker threads, 'rfidPortNrs', 'lastGroup', etc.. /// currentActivityStep = 0; currentGroup = 0; completedCommCount = 0; stopWorkerThreads = false; /// group numbers are >=1, lastGroup == 0 means there is no group lastGroup = 0; foreach (var iPerl in iPerlCommunicationForm.iperlHeads) { if (iPerl.Group > lastGroup) lastGroup = iPerl.Group; } workerThreads = new List(); for (int i = 0; i < cfg.NrThreads; i++) { Thread thread = new Thread(iPerlCommunicationForm.Worker); thread.CurrentCulture = CultureInfo.CurrentCulture; thread.CurrentUICulture = CultureInfo.CurrentUICulture; workerThreads.Add(thread); } muxBrdOrGroup14Nrs = new List(); foreach (var iPerl in iPerlCommunicationForm.iperlHeads) { if (!muxBrdOrGroup14Nrs.Contains(iPerl.MuxBoardNrOrGroup14)) muxBrdOrGroup14Nrs.Add(iPerl.MuxBoardNrOrGroup14); } StringBuilder sb = new StringBuilder(); if (cfg.UseMuxBoards) { sb.Append("rfidPortNrs = ["); foreach (var v in muxBrdOrGroup14Nrs) sb.Append(string.Format(" {0}", v)); sb.Append(" ], "); } 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) { labels = new Label[MaxTextBoxesCount] { 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 PictureBox[MaxTextBoxesCount] { pictureBox1, pictureBox2, pictureBox3, pictureBox4, pictureBox5, pictureBox6, pictureBox7, pictureBox8, pictureBox9, pictureBox10, pictureBox11, pictureBox12, pictureBox13, pictureBox14, pictureBox15, pictureBox16, pictureBox17, pictureBox18, pictureBox19, pictureBox20, pictureBox21, pictureBox22, pictureBox23, pictureBox24, pictureBox25, pictureBox26, pictureBox27, pictureBox28, pictureBox29, pictureBox30, pictureBox31, pictureBox32, pictureBox33, pictureBox34, pictureBox35, pictureBox36, pictureBox37, pictureBox38, pictureBox39, pictureBox40, pictureBox41, pictureBox42, pictureBox43, pictureBox44, pictureBox45, pictureBox46, pictureBox47, pictureBox48, }; messages = new TextBox[MaxTextBoxesCount] { 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, }; checkBoxes = new CheckBoxImage[MaxTextBoxesCount] { checkBoxImage1, checkBoxImage2, checkBoxImage3, checkBoxImage4, checkBoxImage5, checkBoxImage6, checkBoxImage7, checkBoxImage8, checkBoxImage9, checkBoxImage10, checkBoxImage11, checkBoxImage12, checkBoxImage13, checkBoxImage14, checkBoxImage15, checkBoxImage16, checkBoxImage17, checkBoxImage18, checkBoxImage19, checkBoxImage20, checkBoxImage21, checkBoxImage22, checkBoxImage23, checkBoxImage24, checkBoxImage25, checkBoxImage26, checkBoxImage27, checkBoxImage28, checkBoxImage29, checkBoxImage30, checkBoxImage31, checkBoxImage32, checkBoxImage33, checkBoxImage34, checkBoxImage35, checkBoxImage36, checkBoxImage37, checkBoxImage38, checkBoxImage39, checkBoxImage40, checkBoxImage41, checkBoxImage42, checkBoxImage43, checkBoxImage44, checkBoxImage45, checkBoxImage46, checkBoxImage47, checkBoxImage48, }; ckbIndex = new int[MaxTextBoxesCount]; ckbState = new bool[MaxTextBoxesCount]; textBoxesCount = MaxTextBoxesCount; /// 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]; checkBoxes[dest] = checkBoxes[l * lineSize + l * gap + i]; ckbIndex[l * lineSize + l * gap + i] = dest; dest++; } } textBoxesCount = wmsCount; } if (checkBoxesEditMode) { for (int j = 0; j < textBoxesCount; j++) labels[j].Text = string.Format("iPerl{0}", j + 1); } else { for (int j = 0; j < Math.Min(textBoxesCount, iperlHeads.Count); j++) labels[j].Text = iperlHeads[j].Name; } ResizeDlgToFitEnabledControls(); } void ResizeDlgToFitEnabledControls() { int xMax = 0; int yMax = 0; for (int i = 0; i < textBoxesCount; i++) { if (messages[i].Left + messages[i].Width > xMax) xMax = messages[i].Left + messages[i].Width; if (messages[i].Top + messages[i].Height > yMax) yMax = messages[i].Top + messages[i].Height; } Width = xMax + 30; Height = yMax + 50; saveButton.Location = new Point(Width - 105, saveButton.Location.Y); } void Localize() { Text = Strings.Water_Meter_States; saveButton.Text = Strings.Save; sampleLabel1.Text = Strings.Direction_and_pulses_are_OK; sampleLabel2.Text = Strings.There_are_no_opto_pulses; sampleLabel3.Text = Strings.Direction_is_NOK; samplePictureBox1.BackColor = OptoAndDirOKColor; samplePictureBox2.BackColor = OptoNokColor; samplePictureBox3.BackColor = DirNokColor; } private void iPerlCommunicationForm_Load(object sender, EventArgs e) { Localize(); for (int i = 0; i < textBoxesCount; i++) { labels[i].Visible = counters[i].Visible = messages[i].Visible = checkBoxes[i].Visible = true; if (!checkBoxesEditMode && (iperlHeads[i] == null || iperlHeads[i].Disabled)) { checkBoxes[i].Enabled = checkBoxes[i].Checked = ckbState[i] = false; counters[i].BackColor = DisabledColor; messages[i].Text = Strings.Head_was_disabled_by_the_user; } else { checkBoxes[i].Enabled = checkBoxes[i].Checked = ckbState[i] = true; messages[i].Text = "---"; } } TBF.LocalSettings ls = Program.LocalSettings; Left = (ls.iPerlCommunicationsFormLeft != 0) ? ls.iPerlCommunicationsFormLeft : 150; Top = (ls.iPerlCommunicationsFormTop != 0) ? ls.iPerlCommunicationsFormTop : 150; if (ls.iPerlCommunicationsFormCheckboxes != 0) SetCheckBoxStates(ls.iPerlCommunicationsFormCheckboxes); if (!checkBoxesEditMode) { /// Reset opto-data indication for (int i = 0; i < iperlHeads.Count; i++) { counters[i].BackColor = OptoNokColor; } /// /// Set QuidoRS outputs and start the whole communication process /// by incrementing 'currentGroup'. /// if (cfg.UseMuxBoards && (quido != null)) { quido.SetOutputs((ushort)(16 - currentGroup - 1)); Thread.Sleep(2000); } currentGroup++; /// Start worker threads int wtId = 0; foreach (var wt in workerThreads) wt.Start(new Boxes.IntBox(wtId++)); } } private void NormalClose() { CommCompletedHandler = null; AllCompletedHandler = null; if (multiTestParams != null && multiTestParams.Count > 0 && !multiTestParams[0].SimultWithNext) /// RFID Communication at the end of the cycle does not influence the result { 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.iPerlCommunicationsFormCheckboxes = GetCheckBoxStates(); 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; forcedClose = 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++ ) { Test currentTest = tests[i]; iPerlCommunicationParams currentTestParams = multiTestParams[i]; string currentActivity = currentTestParams.Activity; /// Current activity TBF.UiBridge.TestProgressEventArgs.SetEstimatedTimes(new int[] { 0, 0, 10, 0, 140, 0, 0, 0 }); TBF.UiBridge.Bridge.OnTestProgress(null, new TBF.UiBridge.TestProgressEventArgs(tests[i], Config.Entities.Progress.JustStarted)); if (threadID == 0) { rfidDataLogger.InfoFormat(""); /// Makes the log more readable when there is a lot of data rfidDataLogger.WarnFormat("Activity = {0}", currentActivity); 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; #if TURA_SPECIAL int threadIx = threadID; /// Just one thread for TURA_SPECIAL #else for (int threadIx = threadID; threadIx < threadID + 4; threadIx += cfg.NrThreads) #endif { if (cfg.UseMuxBoards && threadIx >= muxBrdOrGroup14Nrs.Count) break; // quit, all RFID ports of all water meters done bool wmFound = false; for (int wmNr0 = 0; wmNr0 < iperlHeads.Count; wmNr0++) { IperlHead ihead = iperlHeads[wmNr0]; if ((ihead.Group == group) && (threadIx < muxBrdOrGroup14Nrs.Count) && (ihead.MuxBoardNrOrGroup14 == muxBrdOrGroup14Nrs[threadIx])) { wmFound = true; Results.Entities.WaterMeter wm = ProcessData.BatchRslts.WaterMeters[waterMeterPositions0[wmNr0]]; CommErr error; string resultStr = string.Empty; /// /// RFID communication activity call /// if ((ihead == null) || ihead.Disabled || !ckbState[wmNr0]) error = CommErr.HeadDisabledByUser; else if (wm == null) error = CommErr.CommFailed; #if IPERL else if (currentActivity.ToLower().Contains(ReadConfigurationStr.ToLower())) error = ReadConfiguration(threadID, ihead, wm, ref resultStr); else if (currentActivity.ToLower().Contains(SetTestModeStr.ToLower())) error = SetTestMode(threadID, ihead, wm, ref resultStr); else if (currentActivity.ToLower().Equals(SetActiveModeStr.ToLower())) error = SetActiveMode(threadID, ihead, wm, ref resultStr); else if (currentActivity.ToLower().Equals(ReadCalibrationStr.ToLower())) error = ReadCalibration(threadID, ihead, wm, ref resultStr); else if (currentActivity.ToLower().Equals(ReadCalibrationV4Str.ToLower())) error = ReadCalibrationV4(threadID, ihead, wm, ref resultStr); else if (currentActivity.ToLower().Contains(WriteCalibrationFactorStr.ToLower())) error = WriteCalibrationFactor(threadID, ihead, wm, ref resultStr); else if (currentActivity.ToLower().Contains(WriteCalibrationV4FactorsStr.ToLower())) error = WriteCalibrationV4Factors(threadID, ihead, wm, ref resultStr); else if (currentActivity.ToLower().Equals(NormalizeCalibrationFactorStr.ToLower())) error = NormalizeCalibrationFactor(threadID, ihead, wm, ref resultStr); else if (currentActivity.ToLower().Equals(NormalizeCalibrationV4FactorsStr.ToLower())) error = NormalizeCalibrationV4Factors(threadID, ihead, wm, ref resultStr); else if (currentActivity.ToLower().Equals(ReadQ2CorrectionStr.ToLower())) error = ReadQ2Correction(threadID, ihead, wm, ref resultStr); else if (currentActivity.ToLower().Equals(ResetQ2CorrectionStr.ToLower())) error = ResetQ2Correction(threadID, ihead, wm, ref resultStr); else if (currentActivity.ToLower().Equals(InitOrReadQ2CorrectionStr.ToLower())) error = InitOrReadQ2Correction(threadID, ihead, wm, ref resultStr); else if (currentActivity.ToLower().Equals(WriteQ2CorrectionStr.ToLower())) error = WriteQ2Correction(threadID, ihead, wm, currentTest, ref resultStr, Q2CorrType.Standard, null); else if (currentActivity.ToLower().Equals(WriteQ2CorrectionAltStr.ToLower())) error = WriteQ2Correction(threadID, ihead, wm, currentTest, ref resultStr, Q2CorrType.Dewa, null); else if (currentActivity.ToLower().Contains(WriteQ2CorrectionGreeceStr.ToLower())) error = WriteQ2Correction(threadID, ihead, wm, currentTest, ref resultStr, Q2CorrType.Greece, currentActivity.Substring(WriteQ2CorrectionGreeceStr.Length).Trim()); else if (currentActivity.ToLower().Contains(WriteQ2CorrectionRLStr.ToLower())) error = WriteQ2Correction(threadID, ihead, wm, currentTest, ref resultStr, Q2CorrType.RL, currentActivity.Substring(WriteQ2CorrectionRLStr.Length).Trim()); else if (currentActivity.ToLower().Contains(WriteQ2CorrectionLRStr.ToLower())) error = WriteQ2Correction(threadID, ihead, wm, currentTest, ref resultStr, Q2CorrType.LR, currentActivity.Substring(WriteQ2CorrectionLRStr.Length).Trim()); else if (currentActivity.ToLower().Contains(UpdateQ2CorrectionsStr.ToLower())) error = WriteQ2Correction(threadID, ihead, wm, currentTest, ref resultStr, Q2CorrType.Update, currentActivity.Substring(UpdateQ2CorrectionsStr.Length).Trim()); else if (currentActivity.ToLower().Contains(ConditnlUpdateQ2CorrectionsStr.ToLower())) error = WriteQ2Correction(threadID, ihead, wm, currentTest, ref resultStr, Q2CorrType.ConditionalUpdate, currentActivity.Substring(ConditnlUpdateQ2CorrectionsStr.Length).Trim()); else if (currentActivity.ToLower().Equals(Reset2HzCorrectionStr.ToLower())) error = Reset2HzCorrection(threadID, ihead, wm, ref resultStr); else if (currentActivity.ToLower().Equals(Write2HzCorrectionStr.ToLower())) error = Write2HzCorrection(threadID, ihead, wm, ref resultStr); #endif else { error = CommErr.None; resultStr = "Invalid activity"; } /// /// Process the result of RFID communication activity /// if (error == CommErr.HeadDisabledByUser || !ckbState[wmNr0]) { OnCommCompleted(null, new CommCompletedEventArgs(threadID, wmNr0, ihead, wm, Strings.Head_was_disabled_by_the_user, CommErr.HeadDisabledByUser)); } else if (error == CommErr.None) { OnCommCompleted(null, new CommCompletedEventArgs(threadID, wmNr0, ihead, wm, resultStr, CommErr.None)); } else if ((error == CommErr.OutOfRange) || (error == CommErr.Q2OutOfRange)) { OnCommCompleted(null, new CommCompletedEventArgs(threadID, wmNr0, ihead, wm, resultStr, CommErr.None)); } else if (ihead.CommFailed || (error == CommErr.CommFailed)) { ihead.CommFailed = true; if ((wm.ResultCode & (int)Results.Entities.ResultCode.RfidErrorCodeMask) == 0) { wm.ResultCode |= (((int)error << 16) | ((int)tests[i].ItemNr << 20)); } OnCommCompleted(null, new CommCompletedEventArgs(threadID, wmNr0, ihead, wm, Strings.RFID_communication_failed, error)); } else { ihead.CommFailed = true; if ((wm.ResultCode & (int)Results.Entities.ResultCode.RfidErrorCodeMask) == 0) { wm.ResultCode |= (((int)error << 16) | ((int)tests[i].ItemNr << 20)); } string failureCauseLocal = string.Format(Strings.failed_0_1_exclamation, currentActivity, error); rfidDataLogger.ErrorFormat("Group={0}, Board={1}, {2}", currentGroup, ihead.MuxBoardNrOrGroup14, failureCauseLocal); OnCommCompleted(null, new CommCompletedEventArgs(threadID, wmNr0, ihead, wm, failureCauseLocal, error)); } break; } TBF.UiBridge.Bridge.OnTestProgress(null, new TBF.UiBridge.TestProgressEventArgs(tests[i], Config.Entities.Progress.FlowSetting)); } if (!wmFound) { OnCommCompleted(null, new CommCompletedEventArgs(threadID, -1, null, null, string.Empty, CommErr.None)); /// 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; } } #region Communication functions #if IPERL /// /// Read a complete configuration structure of the watermeter /// /// Water meter object /// String passed to caller /// true on success static CommErr ReadConfiguration(int threadId, IperlHead ihead, Results.Entities.WaterMeter wm, ref string resultStr) { /// /// The activity is "Read configuration" (this enables the watermeter, resets error flag) /// or "Read configuration if enabled" (this keeps the error flag). /// if (!multiTestParams[currentActivityStep].Activity.ToLower().Contains(" if enabled")) { ihead.CommFailed = false; } if (ihead.CommFailed) return CommErr.CommFailed; ihead.ConfigStruct = null; /// Clear previous ConfigStruct, avoid reuse of (not anymore valid) PCB Number if (OpenPort(threadId, ihead) != 0) return CommErr.OpenPort; /// Open RFID port CommErr error = CommErr.Read; int readRetVal = 0; /// Read configuration byte[] config = null; for (int j = 0; j < cfg.MaxCommRetries; j++) { readRetVal = ReadRequestPort(threadId, ihead, MessageID.Configuration, 0, ConfigStruct.Length, out config, cfg.CommTimeout); if (readRetVal == 0) { error = CommErr.None; ihead.ConfigStruct = ConfigStruct.FromByteArray(config); resultStr = ihead.ConfigStruct.ToString(1); break; } } ClosePort(threadId, ihead); /// Close RFID port return error + Math.Max(0, Math.Min(readRetVal, 4)); } /// /// 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(int threadId, IperlHead ihead, Results.Entities.WaterMeter wm, ref string resultStr) { if (ihead.CommFailed || ihead.ConfigStruct == null) 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 (ihead.ConfigStruct.MeterState == MeterState.Test && ihead.ConfigStruct.TestModeConfig == testModeConfig) { /// Already in the correct test mode resultStr = "Already " + ihead.ConfigStruct.ToString(1); return CommErr.None; } /// Communication necessary if (OpenPort(threadId, ihead) != 0) return CommErr.OpenPort; /// Open RFID port CommErr error = CommErr.None; if (error==CommErr.None && (ihead.ConfigStruct.TestModeConfig != testModeConfig) && (ihead.ConfigStruct.MeterState != MeterState.Active)) { /// Switch to Active mode in order to change TestModeConfig error = CommErr.CmdActive; byte[] cmd = new byte[1] { (byte)Command.SetActiveMode }; for (int j = 0; j < cfg.MaxCommRetries; j++) { if (0 == WriteRequestPort(threadId, ihead, MessageID.Command, 0, 1, cmd, cfg.CommTimeout)) { error = CommErr.None; break; } } Thread.Sleep(250); } if (error == CommErr.None && (ihead.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(threadId, ihead, MessageID.Configuration, 21, 1, tstMdCfg, cfg.CommTimeout)) { ihead.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)Command.SetTestMode }; for (int j = 0; j < cfg.MaxCommRetries; j++) { if (0 == WriteRequestPort(threadId, ihead, 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[] config = null; for (int j = 0; j < cfg.MaxCommRetries; j++) { if (0 == ReadRequestPort(threadId, ihead, MessageID.Configuration, 0, ConfigStruct.Length, out config, cfg.CommTimeout)) { ihead.ConfigStruct = ConfigStruct.FromByteArray(config); if ((ihead.ConfigStruct.MeterState == MeterState.Test) && (ihead.ConfigStruct.TestModeConfig == testModeConfig)) { error = CommErr.None; resultStr = ihead.ConfigStruct.ToString(1); break; } } } } ClosePort(threadId, ihead); /// 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(int threadId, IperlHead ihead, Results.Entities.WaterMeter wm, ref string resultStr) { if (OpenPort(threadId, ihead) != 0) return CommErr.OpenPort; /// Open RFID port CommErr error = CommErr.CmdActive; /// Switch to active mode byte[] cmd = new byte[1] { (byte)Command.SetActiveMode }; for (int j = 0; j < cfg.MaxCommRetries; j++) { if (0 == WriteRequestPort(threadId, ihead, MessageID.Command, 0, 1, cmd, cfg.CommTimeout)) { error = CommErr.None; break; } } #if VERIFY_ACTIVE_MODE if (error == CommErr.None) { error = CommErr.Verify; /// Read configuration byte[] cfg_0_3 = null; for (int j = 0; j < cfg.MaxCommRetries; j++) { if (ihead.ConfigStruct == null) { if (0 == ReadRequestPort(threadId, ihead, MessageID.Configuration, 0, 4, out cfg_0_3, cfg.CommTimeout)) { ihead.ConfigStruct.Update(0, cfg_0_3); if (ihead.ConfigStruct.MeterState == MeterState.Active) { error = CommErr.None; resultStr = ihead.ConfigStruct.ToString(1); break; } } } } } #else if (error == CommErr.None) { if (ihead.ConfigStruct == null) resultStr = "OK (Config not available)"; else resultStr = ihead.ConfigStruct.ToString(1); } #endif ClosePort(threadId, ihead); /// 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(int threadId, IperlHead ihead, Results.Entities.WaterMeter wm, ref string resultStr) { if (ihead.CommFailed) return CommErr.CommFailed; if (OpenPort(threadId, ihead) != 0) return CommErr.OpenPort; /// Open RFID port CommErr error = CommErr.Read; int readRetVal = 0; /// Read calibration byte[] calib = null; for (int j = 0; j < cfg.MaxCommRetries; j++) { readRetVal = ReadRequestPort(threadId, ihead, MessageID.Calibration, 0, CalibrationStruct.Length, out calib, cfg.CommTimeout); if (readRetVal == 0) { ihead.CalibrationStruct = CalibrationStruct.FromByteArray(calib); if (wm.OrigCalibFactor == 0) { wm.OrigCalibFactor = ihead.CalibrationStruct.Calibration; wm.FWVersion = ihead.CalibrationStruct.FWVersionStr(); } resultStr = ihead.CalibrationStruct.ToString(); error = ihead.VerifyIPerlType() ? CommErr.None : CommErr.WrongIPerlType; break; } } ClosePort(threadId, ihead); /// Close RFID port return error + Math.Max(0, Math.Min(readRetVal, 4)); } /// /// Read a complete calibration structure from the watermeter /// /// Water meter object /// String passed to caller /// true on success static CommErr ReadCalibrationV4(int threadId, IperlHead ihead, Results.Entities.WaterMeter wm, ref string resultStr) { if (ihead.CommFailed) return CommErr.CommFailed; if (OpenPort(threadId, ihead) != 0) return CommErr.OpenPort; /// Open RFID port CommErr error = CommErr.Read; int readRetVal = 0; /// Read calibration byte[] calib = null; for (int j = 0; j < cfg.MaxCommRetries; j++) { readRetVal = ReadRequestPort(threadId, ihead, MessageID.Calibration, 0, CalibrationStructV4.Length, out calib, cfg.CommTimeout); if (readRetVal == 0) { ihead.CalibrationStructV4 = CalibrationStructV4.FromByteArray(calib); if ((wm.OrigCalibFactor == 0) && (wm.OrigCalibFactorLNA == 0)) { wm.OrigCalibFactor = ihead.CalibrationStructV4.Calibration; wm.OrigCalibFactorLNA = ihead.CalibrationStructV4.CalibrationLNA; wm.FWVersion = ihead.CalibrationStructV4.FWVersionStr(); } resultStr = ihead.CalibrationStructV4.ToString(); error = ihead.VerifyIPerlType() ? CommErr.None : CommErr.WrongIPerlType; break; } } ClosePort(threadId, ihead); /// Close RFID port return error + Math.Max(0, Math.Min(readRetVal, 4)); } /// /// Write the calculated calibration factor to the water meter. /// Read a part of CalibrationStruct afterwards to verify factor was written correctly. /// /// Thread ID /// IperlHead object /// Water meter object /// String passed to caller /// true on success static CommErr WriteCalibrationFactor(int threadId, IperlHead ihead, Results.Entities.WaterMeter wm, ref string resultStr) { if (ihead.CommFailed || (ihead.CalibrationStruct == null)) return CommErr.CommFailed; /// /// 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) { /// /// Lower and upper limits for the calibration factor are specified as iPerlCommunication activity arguments /// if (ihead.LastTestResult == null || !ihead.LastTestResult.TestDone) return CommErr.MissingTest; newCalibFactor = ihead.CalculateNewCalibFactor(ihead.LastTestResult, ihead.CalibFactor, factorLimitLo, factorLimitHi); } else if (arguments.Length == 1 && UInt16.TryParse(arguments[0], out val) && val > 0) { /// /// Calibration factor value is specified as an iPerlCommunication activity argument /// newCalibFactor = val; /// Update with specified value } else if (arguments.Length == 1 && wm.GetTestData(arguments[0]) != null) { /// /// Adjustment test name is specified as an iPerlCommunication activity argument /// Results.Entities.TestData adjustTestData = wm.GetTestData(arguments[0]); Results.Entities.MeterTestRslt adjustTestRslt; if (adjustTestData == null) { return CommErr.MissingTest; } else if (adjustTestData.Repeats == 1) { /// Find a test result if Repeats == 1 adjustTestRslt = wm.GetMeterTestRslt(arguments[0]); if (adjustTestRslt == null || !adjustTestRslt.TestDone) return CommErr.MissingTest; } else { /// Calculate a summarized test result if Repeats > 1 adjustTestRslt = new Results.Entities.MeterTestRslt(); for (int i = 1; i <= adjustTestData.Repeats; i++) { Results.Entities.MeterTestRslt oneMTR = wm.GetMeterTestRslt(Utils.TestTitle(adjustTestData, i)); if (oneMTR == null || !oneMTR.TestDone) return CommErr.MissingTest; adjustTestRslt.VolumeMeter += oneMTR.VolumeMeter; adjustTestRslt.VolumeRef += oneMTR.VolumeRef; } } newCalibFactor = ihead.CalculateNewCalibFactor(adjustTestRslt, ihead.CalibFactor, ihead.FactorLimitLo, ihead.FactorLimitHi); } else { /// /// Otherwise the last test is supposed to be an adjustment test /// if (ihead.LastTestResult == null || !ihead.LastTestResult.TestDone) return CommErr.MissingTest; newCalibFactor = ihead.CalculateNewCalibFactor(ihead.LastTestResult, ihead.CalibFactor, ihead.FactorLimitLo, ihead.FactorLimitHi); } } else { /// /// No iPerlCommunication activity arguments --> The last test is supposed to be an adjustment test /// if (ihead.LastTestResult == null || !ihead.LastTestResult.TestDone) return CommErr.MissingTest; newCalibFactor = ihead.CalculateNewCalibFactor(ihead.LastTestResult, ihead.CalibFactor, ihead.FactorLimitLo, ihead.FactorLimitHi); } if (newCalibFactor == 0) return CommErr.OutOfRange; /// /// Start communication with iPerl /// if (OpenPort(threadId, ihead) != 0) return CommErr.OpenPort; /// Open RFID port CommErr error; byte[] data = new byte[2] { (byte)(newCalibFactor & 0x00FF), (byte)((newCalibFactor >> 8) & 0x00FF) }; int writeAndVerifyRetries = 0; do { /// /// Write the new calibration factor (up to cfg.MaxCommRetries tims) /// error = CommErr.Write; if (0 == WriteRequestPort(threadId, ihead, MessageID.Calibration, 2, 2, data, cfg.CommTimeout)) { /// /// Read and verify the calibration factor /// error = CommErr.ReadAfterWrite; byte[] calib_2_3 = null; if (0 == ReadRequestPort(threadId, ihead, MessageID.Calibration, 2, 2, out calib_2_3, cfg.CommTimeout)) { error = CommErr.Verify; if (calib_2_3 != null && calib_2_3.Length == 2 && data[0] == calib_2_3[0] && data[1] == calib_2_3[1]) { error = CommErr.None; ihead.CalibrationStruct.Update(data, 2); wm.CalibFactor = newCalibFactor; resultStr = ihead.CalibrationStruct.ToString(); break; } } } ihead.CalibrationStruct.Update(data, 2); } while (++writeAndVerifyRetries <= cfg.MaxCommRetries); ClosePort(threadId, ihead); /// 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. /// /// Thread ID /// IperlHead object /// Water meter object /// String passed to caller /// true on success static CommErr WriteCalibrationV4Factors(int threadId, IperlHead ihead, Results.Entities.WaterMeter wm, ref string resultStr) { if (ihead.CommFailed || (ihead.CalibrationStructV4 == null)) return CommErr.CommFailed; /// /// Parse calibration factor (1 argument) or calibration factor limits (2 arguments) /// UInt16 newCalibFactor = 0; UInt16 newCalibFactorLNA = 0; if (multiTestParams[currentActivityStep].Activity.Length > WriteCalibrationV4FactorsStr.Length) { UInt16 factorLimitLo = 0; UInt16 factorLimitHi = 0; UInt16 lnaFactorLimitLo = 0; UInt16 lnaFactorLimitHi = 0; Results.Entities.TestData adjustTestData = null; Results.Entities.TestData lnaAdjustTestData = null; Results.Entities.MeterTestRslt adjustTestRslt = null; Results.Entities.MeterTestRslt lnaAdjustTestRslt = null; string calibFactrorStr = multiTestParams[currentActivityStep].Activity.Substring(WriteCalibrationV4FactorsStr.Length + 1); string[] arguments = calibFactrorStr.Split(new char[] { ' ' }); if (arguments.Length == 6) { if (!wm.TryGetTestData(arguments[0], out adjustTestData) || !UInt16.TryParse(arguments[1], out factorLimitLo) || factorLimitLo <= 0 && !UInt16.TryParse(arguments[2], out factorLimitHi) || factorLimitHi <= 0 && !wm.TryGetTestData(arguments[3], out lnaAdjustTestData) || !UInt16.TryParse(arguments[4], out lnaFactorLimitLo) || lnaFactorLimitLo <= 0 && !UInt16.TryParse(arguments[5], out lnaFactorLimitHi) || lnaFactorLimitHi <= 0) { return CommErr.WrongArguments; } else { adjustTestRslt = GetAverageTestRslt(wm, adjustTestData); lnaAdjustTestRslt = GetAverageTestRslt(wm, lnaAdjustTestData); if ((adjustTestRslt == null) || (lnaAdjustTestRslt == null)) { return CommErr.MissingTest; } newCalibFactor = ihead.CalculateNewCalibFactor(adjustTestRslt, ihead.CalibFactor, factorLimitLo, factorLimitHi); newCalibFactorLNA = ihead.CalculateNewCalibFactor(lnaAdjustTestRslt, ihead.CalibFactorLNA, lnaFactorLimitLo, lnaFactorLimitHi); } } else if (arguments.Length == 4) { if (!wm.TryGetTestData(arguments[0], out adjustTestData) || !wm.TryGetTestData(arguments[1], out lnaAdjustTestData) || !UInt16.TryParse(arguments[2], out lnaFactorLimitLo) || lnaFactorLimitLo <= 0 && !UInt16.TryParse(arguments[3], out lnaFactorLimitHi) || lnaFactorLimitHi <= 0) { return CommErr.WrongArguments; } else { factorLimitLo = ihead.FactorLimitLo; factorLimitHi = ihead.FactorLimitHi; adjustTestRslt = GetAverageTestRslt(wm, adjustTestData); lnaAdjustTestRslt = GetAverageTestRslt(wm, lnaAdjustTestData); if ((adjustTestRslt == null) || (lnaAdjustTestRslt == null)) { return CommErr.MissingTest; } newCalibFactor = ihead.CalculateNewCalibFactor(adjustTestRslt, ihead.CalibFactor, factorLimitLo, factorLimitHi); newCalibFactorLNA = ihead.CalculateNewCalibFactor(lnaAdjustTestRslt, ihead.CalibFactorLNA, lnaFactorLimitLo, lnaFactorLimitHi); } } else if (arguments.Length == 2) { if (!wm.TryGetTestData(arguments[0], out adjustTestData) || !wm.TryGetTestData(arguments[1], out lnaAdjustTestData)) { return CommErr.WrongArguments; } else { factorLimitLo = ihead.FactorLimitLo; factorLimitHi = ihead.FactorLimitHi; lnaFactorLimitLo = ihead.FactorLimitLo; lnaFactorLimitHi = ihead.FactorLimitHi; adjustTestRslt = GetAverageTestRslt(wm, adjustTestData); lnaAdjustTestRslt = GetAverageTestRslt(wm, lnaAdjustTestData); if ((adjustTestRslt == null) || (lnaAdjustTestRslt == null)) { return CommErr.MissingTest; } newCalibFactor = ihead.CalculateNewCalibFactor(adjustTestRslt, ihead.CalibFactor, factorLimitLo, factorLimitHi); newCalibFactorLNA = ihead.CalculateNewCalibFactor(lnaAdjustTestRslt, ihead.CalibFactorLNA, lnaFactorLimitLo, lnaFactorLimitHi); } } else { return CommErr.WrongArguments; } } else { return CommErr.WrongArguments; } if (newCalibFactor == 0 || newCalibFactorLNA == 0) return CommErr.OutOfRange; /// /// Start communication with iPerl /// if (OpenPort(threadId, ihead) != 0) return CommErr.OpenPort; /// Open RFID port CommErr error; byte[] data = new byte[2] { (byte)(newCalibFactor & 0x00FF), (byte)((newCalibFactor >> 8) & 0x00FF) }; byte[] dataLNA = new byte[2] { (byte)(newCalibFactorLNA & 0x00FF), (byte)((newCalibFactorLNA >> 8) & 0x00FF) }; int writeAndVerifyRetries = 0; do { /// /// Write the new calibration factor (up to cfg.MaxCommRetries tims) /// error = CommErr.Write; if (0 == WriteRequestPort(threadId, ihead, MessageID.Calibration, 2, 2, data, cfg.CommTimeout) && 0 == WriteRequestPort(threadId, ihead, MessageID.Calibration, 34, 2, dataLNA, cfg.CommTimeout)) { /// /// Read and verify the calibration factor /// error = CommErr.ReadAfterWrite; byte[] calib_2_3 = null; byte[] calib_34_35 = null; if (0 == ReadRequestPort(threadId, ihead, MessageID.Calibration, 2, 2, out calib_2_3, cfg.CommTimeout) && 0 == ReadRequestPort(threadId, ihead, MessageID.Calibration, 34, 2, out calib_34_35, cfg.CommTimeout)) { error = CommErr.Verify; if (calib_2_3 != null && calib_2_3.Length == 2 && data[0] == calib_2_3[0] && data[1] == calib_2_3[1] && calib_34_35 != null && calib_34_35.Length == 2 && dataLNA[0] == calib_34_35[0] && dataLNA[1] == calib_34_35[1]) { error = CommErr.None; ihead.CalibrationStructV4.Update(data, 2); ihead.CalibrationStructV4.Update(dataLNA, 34); wm.CalibFactor = newCalibFactor; wm.CalibFactorLNA = newCalibFactorLNA; resultStr = ihead.CalibrationStructV4.ToString(); break; } } } ihead.CalibrationStructV4.Update(data, 2); ihead.CalibrationStructV4.Update(dataLNA, 34); } while (++writeAndVerifyRetries <= cfg.MaxCommRetries); ClosePort(threadId, ihead); /// Close RFID port return error; } /// /// Get averaged meter test result using test name and repetitions from a given test data. /// /// Water meter /// TestData (name and repetitions) /// (1) selected MeterTestRslt or (2) average of repeated MTR-s or (3) null when at least one MTR is missing static Results.Entities.MeterTestRslt GetAverageTestRslt(Results.Entities.WaterMeter wm, Results.Entities.TestData testData) { Results.Entities.MeterTestRslt avgTestRslt; if (testData.Repeats > 1) { /// Calculate a summarized test result if Repeats > 1 avgTestRslt = new Results.Entities.MeterTestRslt(); for (int i = 1; i <= testData.Repeats; i++) { Results.Entities.MeterTestRslt oneMTR = wm.GetMeterTestRslt(Utils.TestTitle(testData, i)); if (oneMTR == null || !oneMTR.TestDone) return null; avgTestRslt.VolumeMeter += oneMTR.VolumeMeter; avgTestRslt.VolumeRef += oneMTR.VolumeRef; } } else { avgTestRslt = wm.GetMeterTestRslt(Utils.TestTitle(testData, 1)); if (avgTestRslt == null || !avgTestRslt.TestDone) return null; } return avgTestRslt; } /// /// Normalize calibration factor in case ti is close to value 8000. /// /// Water meter object /// String passed to caller /// true on success static CommErr NormalizeCalibrationFactor(int threadId, IperlHead ihead, Results.Entities.WaterMeter wm, ref string resultStr) { if (ihead.CommFailed || (ihead.CalibrationStruct == null)) return CommErr.CommFailed; if (ihead.FactorLimitLo <= ihead.CalibFactor && ihead.CalibFactor <= ihead.FactorLimitHi) { resultStr = "Calibratin factor is OK"; return CommErr.None; /// No need to update the calibration factor } if (OpenPort(threadId, ihead) != 0) return CommErr.OpenPort; /// Open RFID port CommErr error = CommErr.Write; /// Determine the new calibration factor UInt16 defaultCalibFactor; switch (ihead.CalibrationStruct.MeterType) { case MeterType.DN15: defaultCalibFactor = 2710; break; case MeterType.DN20: defaultCalibFactor = 3746; break; case MeterType.DN25: defaultCalibFactor = 3300; break; case MeterType.DN32: defaultCalibFactor = 2500; break; case MeterType.DN40: defaultCalibFactor = 3080; break; case MeterType.DN26: case MeterType.CoaxManifold: default: defaultCalibFactor = 3040; break; } byte[] data = new byte[2] { (byte)(defaultCalibFactor & 0x00FF), (byte)((defaultCalibFactor >> 8) & 0x00FF) }; for (int j = 0; j < cfg.MaxCommRetries; j++) { if (0 == WriteRequestPort(threadId, ihead, MessageID.Calibration, 2, 2, data, cfg.CommTimeout)) { error = CommErr.None; ihead.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(threadId, ihead, MessageID.Calibration, 2, 2, out calib_2_3, cfg.CommTimeout)) { error = CommErr.None; ihead.CalibrationStruct.Update(calib_2_3, 2); resultStr = ihead.CalibrationStruct.ToString(); break; } } } ClosePort(threadId, ihead); /// Close RFID port return error; } /// /// Normalize calibration factor in case ti is close to value 8000. /// /// Water meter object /// String passed to caller /// true on success static CommErr NormalizeCalibrationV4Factors(int threadId, IperlHead ihead, Results.Entities.WaterMeter wm, ref string resultStr) { if (ihead.CommFailed || (ihead.CalibrationStructV4 == null)) return CommErr.CommFailed; if (ihead.FactorLimitLo <= ihead.CalibFactor && ihead.CalibFactor <= ihead.FactorLimitHi) { resultStr = "Calibratin factor is OK"; return CommErr.None; /// No need to update the calibration factor } if (OpenPort(threadId, ihead) != 0) return CommErr.OpenPort; /// Open RFID port CommErr error = CommErr.Write; /// Determine the new calibration factor UInt16 defaultCalibFactor; switch (ihead.CalibrationStructV4.MeterType) { case MeterType.DN15: defaultCalibFactor = 2710; break; case MeterType.DN20: defaultCalibFactor = 3746; break; case MeterType.DN25: defaultCalibFactor = 3300; break; case MeterType.DN32: defaultCalibFactor = 2500; break; case MeterType.DN40: defaultCalibFactor = 3080; break; case MeterType.DN26: case MeterType.CoaxManifold: default: defaultCalibFactor = 3040; break; } byte[] data = new byte[2] { (byte)(defaultCalibFactor & 0x00FF), (byte)((defaultCalibFactor >> 8) & 0x00FF) }; for (int j = 0; j < cfg.MaxCommRetries; j++) { if (0 == WriteRequestPort(threadId, ihead, MessageID.Calibration, 2, 2, data, cfg.CommTimeout)) { error = CommErr.None; ihead.CalibrationStructV4.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(threadId, ihead, MessageID.Calibration, 2, 2, out calib_2_3, cfg.CommTimeout)) { error = CommErr.None; ihead.CalibrationStructV4.Update(calib_2_3, 2); resultStr = ihead.CalibrationStructV4.ToString(); break; } } } ClosePort(threadId, ihead); /// Close RFID port return error; } /// /// 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 ReadQ2Correction(int threadId, IperlHead ihead, Results.Entities.WaterMeter wm, ref string resultStr) { if (ihead.CommFailed) return CommErr.CommFailed; if (OpenPort(threadId, ihead) != 0) return CommErr.OpenPort; /// Open RFID port CommErr error = CommErr.Read; /// Write zero Q2 correction byte[] rdData = null; /// for (int j = 0; j < cfg.MaxCommRetries; j++) { if (0 == ReadRequestPort(threadId, ihead, MessageID.MetrologyMemory, Q2CorrFactorsAddr, 2, out rdData, cfg.CommTimeout)) { error = CommErr.None; break; } } if ((error == CommErr.None) && (rdData != null) && (rdData.Length == 2)) { resultStr = "Q2 corrections successfully read"; wm.Q2CorrLR = ihead.Q2CorrLR = (int)((SByte)rdData[0]); wm.Q2CorrRL = ihead.Q2CorrRL = (int)((SByte)rdData[1]); } ClosePort(threadId, ihead); /// Close RFID port return error; } /// /// 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(int threadId, IperlHead ihead, Results.Entities.WaterMeter wm, ref string resultStr) { return SetQ2Correction(threadId, ihead, wm, ref resultStr, 0, 0); } /// /// 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 SetQ2Correction(int threadId, IperlHead ihead, Results.Entities.WaterMeter wm, ref string resultStr, int factorLR, int factorRL) { if (ihead.CommFailed) return CommErr.CommFailed; if (OpenPort(threadId, ihead) != 0) return CommErr.OpenPort; /// Open RFID port CommErr error = CommErr.Write; /// Write zero Q2 correction byte[] wrData = new byte[2] { (byte)factorLR, (byte)factorRL }; /// for (int j = 0; j < cfg.MaxCommRetries; j++) { if (0 == WriteRequestPort(threadId, ihead, MessageID.MetrologyMemory, Q2CorrFactorsAddr, wrData.Length, wrData, cfg.CommTimeout)) { wm.Q2CorrRL = ihead.Q2CorrRL = factorRL; wm.Q2CorrLR = ihead.Q2CorrLR = factorLR; error = CommErr.None; break; } } /// /// Verification disabled on 16.02.2016 /// #if VERIFY_Q2_CORR_RESET /// 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(threadId, ihead, 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"; ihead.Q2CorrectionFactor = 0; break; } } } #else if (error == CommErr.None) { resultStr = "Q2 corrections reset to 0"; ihead.Q2CorrRL = 0; } #endif ClosePort(threadId, ihead); /// Close RFID port return error; } static CommErr InitOrReadQ2Correction(int threadId, IperlHead ihead, Results.Entities.WaterMeter wm, ref string resultStr) { if (ihead.CommFailed) return CommErr.CommFailed; if (wm.Pruefindex == 1) { return SetQ2Correction(threadId, ihead, wm, ref resultStr, ihead.InitQ2CorrLR, ihead.InitQ2CorrRL); } else { return ReadQ2Correction(threadId, ihead, wm, ref resultStr); } } /// /// Reset 2Hz correction factor in the memory to 0. /// /// Water meter object /// String passed to caller /// true on success static CommErr Reset2HzCorrection(int threadId, IperlHead ihead, Results.Entities.WaterMeter wm, ref string resultStr) { if (ihead.CommFailed) return CommErr.CommFailed; if (OpenPort(threadId, ihead) != 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(threadId, ihead, 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(threadId, ihead, 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"; ihead.Q2CorrectionFactor = 0; break; } } } #else if (error == CommErr.None) { resultStr = "2Hz correction reset to 0"; ihead.Hz2Correction = 0; } #endif ClosePort(threadId, ihead); /// Close RFID port return error; } /// /// Write the calculated Q2 correction factors to the memory. /// Read them back to verify factors were written correctly. /// /// Write Q2 correction /// Write Q2 correction Alt /// Write Q2 correction [Greece | R-L | L-R] testname /// Update Q2 corrections /// Conditional update of Q2 corrections /// /// Water meter object /// String passed to caller /// true on success static CommErr WriteQ2Correction(int threadId, IperlHead ihead, Results.Entities.WaterMeter wm, Test test, ref string resultStr, Q2CorrType q2CorrType, string restOfLine) { if ((q2CorrType == Q2CorrType.ConditionalUpdate) && wm.PassedFromTests()) { resultStr = string.Format("No Q2 correction update (iPerl is OK)"); return CommErr.None; /// No Q2 correction when doing conditional update and WM passed } string[] args = new string[0]; if (!string.IsNullOrEmpty(restOfLine)) { args = restOfLine.Split(new char[] { ' ' }); } Results.Entities.MeterTestRslt q2adjResult; /// The only test or the 1st test (the test in R-L direction) Results.Entities.TestData q2adjTestData; /// if (args.Length == 0) { /// In case of no test name argument, ihead.LastTestResult is used (if it exists) if ((ihead.LastTestResult == null) || !ihead.LastTestResult.TestDone) return CommErr.MissingTest; q2adjResult = ihead.LastTestResult; q2adjTestData = q2adjResult.TestRslt.TestData; } else // if (args.Length >= 1) { /// Get test data from (the 1st) test name q2adjTestData = wm.GetTestData(args[0]); if (q2adjTestData == null) return CommErr.MissingTest; if (q2adjTestData.Repeats == 1) { /// Find a test result if Repets == 1 q2adjResult = wm.GetMeterTestRslt(args[0]); if (q2adjResult == null || !q2adjResult.TestDone) return CommErr.MissingTest; } else { /// Calculate a summarized test result if Repeats > 1 q2adjResult = new Results.Entities.MeterTestRslt(); for (int i = 1; i <= q2adjTestData.Repeats; i++) { Results.Entities.MeterTestRslt oneMTR = wm.GetMeterTestRslt(string.Format("{0} ({1}/{2})", args[0], i, q2adjTestData.Repeats)); if (oneMTR == null || !oneMTR.TestDone) return CommErr.MissingTest; q2adjResult.VolumeMeter += oneMTR.VolumeMeter; q2adjResult.VolumeRef += oneMTR.VolumeRef; } q2adjResult.Error = Config.Formulas.ErrorFromVolumes(q2adjResult.VolumeMeter, q2adjResult.VolumeRef); } } Results.Entities.MeterTestRslt q2adjResult2 = null; /// 2nd test name for a test in L-R direction Results.Entities.TestData q2adjTestData2 = null; /// if (args.Length >= 2) { /// Get the 2nd test data from the 2nd test name q2adjTestData2 = wm.GetTestData(args[1]); if (q2adjTestData2 == null) return CommErr.MissingTest; if (q2adjTestData2.Repeats == 1) { /// Find a test result if Repets == 1 q2adjResult2 = wm.GetMeterTestRslt(args[1]); if (q2adjResult2 == null || !q2adjResult2.TestDone) return CommErr.MissingTest; } else { /// Calculate a summarized test result if Repeats > 1 q2adjResult2 = new Results.Entities.MeterTestRslt(); for (int i = 1; i <= q2adjTestData2.Repeats; i++) { Results.Entities.MeterTestRslt oneMTR = wm.GetMeterTestRslt(string.Format("{0} ({1}/{2})", args[1], i, q2adjTestData2.Repeats)); if (oneMTR == null || !oneMTR.TestDone) return CommErr.MissingTest; q2adjResult2.VolumeMeter += oneMTR.VolumeMeter; q2adjResult2.VolumeRef += oneMTR.VolumeRef; } q2adjResult2.Error = Config.Formulas.ErrorFromVolumes(q2adjResult2.VolumeMeter, q2adjResult2.VolumeRef); } } if (q2adjResult == null) { return CommErr.MissingTest; /// This should never happen } if ((q2CorrType == Q2CorrType.Update || q2CorrType == Q2CorrType.ConditionalUpdate) && (q2adjResult2 == null)) { return CommErr.MissingTest; /// This should never happen, 2 test names required as activity arguments } /// /// Calculate Q2 correction factor(s) from an error measured at Q2 /// double q2Correction = 0; double q2Correction2 = 0; /// L-R direction in case of Q2CorrType.Update and Q2CorrType.ConditionalUpdate /// if ((q2CorrType == Q2CorrType.Update) || (q2CorrType == Q2CorrType.ConditionalUpdate)) { /// /// Update or ConditionalUpdate: 1. Calculate updates, not corrections, 2. Use error limits from this test /// double errLimitLo = test.ErrLimLo + test.Uncertainty; double errLimitHi = test.ErrLimHi - test.Uncertainty; if (errLimitLo <= q2adjResult.Error && q2adjResult.Error <= errLimitHi && errLimitLo <= q2adjResult2.Error && q2adjResult2.Error <= errLimitHi) { /// Calculate updated Q2 correction factors q2Correction = ihead.CalculateQ2CorrectionFactor(q2adjResult, ihead.CalibTargetQ2, q2adjTestData.Qfrom, wm.Q2CorrRL); q2Correction2 = ihead.CalculateQ2CorrectionFactor(q2adjResult2, ihead.CalibTargetQ2, q2adjTestData2.Qfrom, wm.Q2CorrLR); } else { /// At least one of Q2 errors is out of range for Q2 correction /// Q2 error is out of range for the Q2 correction log.WarnFormat("OoR => no Q2 correction: Pos={0}, PCB#={1}, ***Q2_RL err={2}%***, ***Q2_LR err={3}%***, CalTarget={4}%, [Lo={5}%, Hi={6}%]", ihead.Name, ihead.SerialNr, q2adjResult.Error.ToString("F2"), q2adjResult2.Error.ToString("F2"), ihead.CalibTargetQ2.ToString("F1"), errLimitLo.ToString("F1"), errLimitHi.ToString("F1")); ihead.Q2CorrRL = wm.Q2CorrRL; ihead.Q2CorrLR = wm.Q2CorrLR; resultStr = string.Format("Q2 error is out of range => no Q2 correction"); return CommErr.Q2OutOfRange; } } else { /// /// Regular Q2 corrections: 1. Use error limits from 'q2adjResult' /// double errLimitLo = q2adjTestData.ErrLimLo + q2adjTestData.Uncertainty; double errLimitHi = q2adjTestData.ErrLimHi - q2adjTestData.Uncertainty; if ((errLimitLo <= q2adjResult.Error) && (q2adjResult.Error <= errLimitHi)) { /// Calculate Q2 correction factor (assuming it was 0 when doing a test) q2Correction = ihead.CalculateQ2CorrectionFactor(q2adjResult, ihead.CalibTargetQ2, q2adjTestData.Qfrom); } else { /// Q2 error is out of range for Q2 correction log.WarnFormat("OoR => no Q2 correction: Pos={0}, PCB#={1}, ***Q2adj err={2}%***, CalTarget={3}%, [Lo={4}%, Hi={5}%]", ihead.Name, ihead.SerialNr, q2adjResult.Error.ToString("F2"), ihead.CalibTargetQ2.ToString("F1"), errLimitLo.ToString("F1"), errLimitHi.ToString("F1")); resultStr = string.Format("Q2 error is out of range => no Q2 correction"); return CommErr.Q2OutOfRange; } } Byte q2CorrRL = 0; Byte q2CorrLR = 0; /// if (q2CorrType == Q2CorrType.Standard) { /// /// Standard process /// if (Math.Abs(q2adjResult.Error - ihead.CalibTargetQ2) <= 0.5) { resultStr = string.Format("Q2 correction = 0 (writing bypassed)"); return CommErr.None; } q2CorrRL = (byte)((int)Math.Round(1.1 * q2Correction) & 0x000000FF); q2CorrLR = (byte)((int)Math.Round(0.5 * q2Correction) & 0x000000FF); } else if (q2CorrType == Q2CorrType.Dewa) { /// /// Process for DEWA /// if (Math.Abs(q2adjResult.Error - ihead.CalibTargetQ2) <= 0.5) { resultStr = string.Format("Q2 correction = 0 (writing bypassed)"); return CommErr.None; } /// Use the same correction factor in both directions q2CorrRL = (byte)((int)Math.Round(1.1 * q2Correction) & 0x000000FF); q2CorrLR = (byte)((int)Math.Round(1.1 * q2Correction) & 0x000000FF); } else if (q2CorrType == Q2CorrType.Greece) { /// /// Process for Greece /// if (Math.Abs(q2adjResult.Error - ihead.CalibTargetQ2) <= 0.5) { resultStr = string.Format("Q2 correction = 0 (writing bypassed)"); return CommErr.None; } q2CorrRL = (byte)((int)Math.Round(2.2 * q2Correction) & 0x000000FF); q2CorrLR = (byte)((int)Math.Round(1.1 * q2Correction) & 0x000000FF); } else if (q2CorrType == Q2CorrType.RL) /// SUEZ { if (Math.Abs(q2adjResult.Error - ihead.CalibTargetQ2) <= 0.5) { resultStr = string.Format("Q2 correction = 0 (writing bypassed)"); return CommErr.None; } q2CorrRL = (byte)((int)Math.Round(q2Correction) & 0x000000FF); } else if (q2CorrType == Q2CorrType.LR) /// SUEZ { if (Math.Abs(q2adjResult.Error - ihead.CalibTargetQ2) <= 0.5) { resultStr = string.Format("Q2 correction = 0 (writing bypassed)"); return CommErr.None; } q2CorrLR = (byte)((int)Math.Round(q2Correction) & 0x000000FF); } else if ((q2CorrType == Q2CorrType.Update) || (q2CorrType == Q2CorrType.ConditionalUpdate)) { q2CorrRL = (byte)((int)Math.Round(q2Correction) & 0x000000FF); q2CorrLR = (byte)((int)Math.Round(q2Correction2) & 0x000000FF); } /// /// Write calculated Q2 correction factors into the water meter /// if (ihead.CommFailed) return CommErr.CommFailed; if (OpenPort(threadId, ihead) != 0) return CommErr.OpenPort; /// Open RFID port CommErr error = CommErr.Write; if (q2CorrType == Q2CorrType.RL) { /// /// Write R-L Q2 correction factor only /// byte[] wrData = new byte[1] { q2CorrRL }; for (int j = 0; j < cfg.MaxCommRetries; j++) { if (0 == WriteRequestPort(threadId, ihead, MessageID.MetrologyMemory, Q2CorrFactorsAddrRL, wrData.Length, wrData, cfg.CommTimeout)) { error = CommErr.None; break; } } } else if (q2CorrType == Q2CorrType.LR) { /// /// Write L-R Q2 correction factor only /// byte[] wrData = new byte[1] { q2CorrLR }; for (int j = 0; j < cfg.MaxCommRetries; j++) { if (0 == WriteRequestPort(threadId, ihead, MessageID.MetrologyMemory, Q2CorrFactorsAddrLR, wrData.Length, wrData, cfg.CommTimeout)) { error = CommErr.None; break; } } } else { /// /// Write both Q2 correction factors /// byte[] wrData = new byte[2] { q2CorrLR, q2CorrRL }; for (int j = 0; j < cfg.MaxCommRetries; j++) { if (0 == WriteRequestPort(threadId, ihead, 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(threadId, ihead, 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(ihead.Name + ": " + resultStr); wm.Q2ErrWOCorrection = ihead.Q2ErrorWOCorrection; if (q2CorrType != Q2CorrType.LR) wm.Q2CorrRFlow = ihead.Q2CorrRFlow = (int)(sbyte)q2CorrRFlow; if (q2CorrType != Q2CorrType.RL) wm.Q2CorrLFlow = ihead.Q2CorrLFlow = (int)(sbyte)q2CorrLFlow; break; } } } #else /// /// Update WaterMeter entity and IperlHead /// if (error == CommErr.None) { if (q2CorrType == Q2CorrType.RL) { resultStr = string.Format("Q2 correction: RL={0}", (SByte)q2CorrRL); } else if (q2CorrType == Q2CorrType.LR) { resultStr = string.Format("Q2 correction: LR={0}", (SByte)q2CorrLR); } else { resultStr = string.Format("Q2 correction: RL={0}, LR={1}", (SByte)q2CorrRL, (SByte)q2CorrLR); } rfidDataLogger.WarnFormat("{0}: {1}", ihead.Name, resultStr); if ((q2CorrType != Q2CorrType.Update) && (q2CorrType != Q2CorrType.ConditionalUpdate)) { wm.Q2ErrWOCorrection = ihead.Q2ErrWOCorrection = q2adjResult.Error; } if (q2CorrType != Q2CorrType.LR) wm.Q2CorrRL = ihead.Q2CorrRL = (int)(sbyte)q2CorrRL; if (q2CorrType != Q2CorrType.RL) wm.Q2CorrLR = ihead.Q2CorrLR = (int)(sbyte)q2CorrLR; } #endif ClosePort(threadId, ihead); /// 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(int threadId, IperlHead ihead, Results.Entities.WaterMeter wm, ref string resultStr) { if (ihead.CommFailed) return CommErr.CommFailed; /// Calculate the correction ihead.Hz2CorrectionDone = false; int hz2CorrectionFactor; bool wmOK = ihead.Calculate2HzCorrectionFactor(ihead.LastTestResult2, ihead.LastTestResult, out ihead.Diff2Hz8Hz, out hz2CorrectionFactor); if (hz2CorrectionFactor == 0) { resultStr = string.Format("2Hz correction = 0 (writing bypassed)"); return CommErr.None; } if (OpenPort(threadId, ihead) != 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(threadId, ihead, MessageID.MetrologyMemory, Hz2CorrFactorsAddr, wrData.Length, wrData, cfg.CommTimeout)) { error = CommErr.None; wm.Hz2Correction = ihead.Hz2Correction = hz2CorrectionFactor; wm.Hz2CorrectionDone = ihead.Hz2CorrectionDone = true; 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(ihead.Name + ": " + resultStr); wm.Hz2Correction = ihead.Hz2CorrectionFactor = hz2CorrectionFactor; wm.Diff2Hz8Hz = ihead.Diff2Hz8Hz; wm.Hz2CorrectionDone = ihead.Hz2CorrectionDone; break; } } } #else if (error == CommErr.None) { resultStr = string.Format("2Hz correction = {0}", (SByte)hz2CorrectionByte); rfidDataLogger.Warn(ihead.Name + ": " + resultStr); wm.Diff2Hz8Hz = ihead.Diff2Hz8Hz; wm.Hz2CorrectionDone = ihead.Hz2CorrectionDone = true; wm.Hz2Correction = ihead.Hz2Correction = hz2CorrectionFactor; } #endif ClosePort(threadId, ihead); /// Close RFID port return error; } #endif /// IPERL #endregion /// /// 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) { try { /// /// Update the text message /// if (data.WMNr0 >= 0) messages[data.WMNr0].Text = data.CommMessage; /// /// Update head active/inactive switch /// if (data.WMNr0 >= 0 && data.CommErr == CommErr.HeadDisabledByUser) { /// iPerl head was disabled by the user ckbState[data.WMNr0] = false; checkBoxes[data.WMNr0].Checked = false; checkBoxes[data.WMNr0].Enabled = false; if (data.Ihead != null) data.Ihead.Disabled = true; if (data.Wm != null) data.Wm.Disabled = true; } else if (data.WMNr0 >= 0 && data.CommErr == CommErr.None) { /// One RFID communication successful => iPerl cannot be disabled by the user anymore ckbState[data.WMNr0] = true; checkBoxes[data.WMNr0].Checked = true; checkBoxes[data.WMNr0].Enabled = false; } /// /// Update opto-communication indication /// for (int i = 0; i < iperlHeads.Count; i++) { if (iperlHeads[i] == null || iperlHeads[i].Disabled) { counters[i].BackColor = DisabledColor; } else { switch (iperlHeads[i].CheckFlowDirection()) { case OptoHeadState.OptoAndDirOK: counters[i].BackColor = OptoAndDirOKColor; break; case OptoHeadState.DirNok: counters[i].BackColor = DirNokColor; break; default: case OptoHeadState.OptoNok: counters[i].BackColor = OptoNokColor; break; } } } #if !TURA_SPECIAL /// /// Branch /// lock (this) { completedCommCount++; if (cfg.UseMuxBoards && completedCommCount < muxBrdOrGroup14Nrs.Count) return; if (!cfg.UseMuxBoards && completedCommCount < 4) return; completedCommCount = 0; } #endif if (currentGroup < lastGroup) { /// Go to the next step / next group if (quido != null) { quido.SetOutputs((ushort)(16 - currentGroup - 1)); Thread.Sleep(2000); } 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(2000); } currentGroup++; } else { /// Wait until all threads are finished workerThreads[data.ThreadId].Join(2000); NormalClose(); } } catch (Exception e) { log.ErrorFormat("DoOnCommCompleted({0}) failed: {1}", data, e.Message); log.FatalFormat("StackTrace : {0}{1}", Environment.NewLine, e.StackTrace); } } /// /// 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.MethodClass = TbfComponents.FindComponent(test.Method).ClassName; tstRslt.StartTime = tstRslt.Batch.StartTime; tstRslt.EndTime = endTime; tstRslt.FlowSetTime = 0; tstRslt.Buoyancy = Formulas.Buoyancy(); tstRslt.TestTime += testTime; /// [s] total communication time of all tests for (int i = 0; i < iperlHeads.Count; i++) { Results.Entities.MeterTestRslt meterRslt = ProcessData.BatchRslts.GetMeterTestRslt(test.Name, waterMeterPositions0[i], Config.Entities.CompoundMeterId.Single); if (meterRslt != null && iperlHeads[i] != null) { #if ORACLE_DB meterRslt.WaterMeter.WaterMeterData.WMTypeId = iperlHeads[i].WMType_ID; #endif meterRslt.WaterMeter.SerialNr = iperlHeads[i].SerialNr; meterRslt.Passed = (!iperlHeads[i].CommFailed && !iperlHeads[i].Disabled); meterRslt.TestDone = true; } } } } UiBridge.Bridge.OnTestCompleted(this, new UiBridge.TestCompletedEventArgs(string.Empty, null)); } #region Check boxes edit mode support private void checkBoxImage1_Click(object sender, EventArgs e) { ckbState[ckbIndex[0]] = checkBoxImage1.Checked; } private void checkBoxImage2_Click(object sender, EventArgs e) { ckbState[ckbIndex[1]] = checkBoxImage2.Checked; } private void checkBoxImage3_Click(object sender, EventArgs e) { ckbState[ckbIndex[2]] = checkBoxImage3.Checked; } private void checkBoxImage4_Click(object sender, EventArgs e) { ckbState[ckbIndex[3]] = checkBoxImage4.Checked; } private void checkBoxImage5_Click(object sender, EventArgs e) { ckbState[ckbIndex[4]] = checkBoxImage5.Checked; } private void checkBoxImage6_Click(object sender, EventArgs e) { ckbState[ckbIndex[5]] = checkBoxImage6.Checked; } private void checkBoxImage7_Click(object sender, EventArgs e) { ckbState[ckbIndex[6]] = checkBoxImage7.Checked; } private void checkBoxImage8_Click(object sender, EventArgs e) { ckbState[ckbIndex[7]] = checkBoxImage8.Checked; } private void checkBoxImage9_Click(object sender, EventArgs e) { ckbState[ckbIndex[8]] = checkBoxImage9.Checked; } private void checkBoxImage10_Click(object sender, EventArgs e) { ckbState[ckbIndex[9]] = checkBoxImage10.Checked; } private void checkBoxImage11_Click(object sender, EventArgs e) { ckbState[ckbIndex[10]] = checkBoxImage11.Checked; } private void checkBoxImage12_Click(object sender, EventArgs e) { ckbState[ckbIndex[11]] = checkBoxImage12.Checked; } private void checkBoxImage13_Click(object sender, EventArgs e) { ckbState[ckbIndex[12]] = checkBoxImage13.Checked; } private void checkBoxImage14_Click(object sender, EventArgs e) { ckbState[ckbIndex[13]] = checkBoxImage14.Checked; } private void checkBoxImage15_Click(object sender, EventArgs e) { ckbState[ckbIndex[14]] = checkBoxImage15.Checked; } private void checkBoxImage16_Click(object sender, EventArgs e) { ckbState[ckbIndex[15]] = checkBoxImage16.Checked; } private void checkBoxImage17_Click(object sender, EventArgs e) { ckbState[ckbIndex[16]] = checkBoxImage17.Checked; } private void checkBoxImage18_Click(object sender, EventArgs e) { ckbState[ckbIndex[17]] = checkBoxImage18.Checked; } private void checkBoxImage19_Click(object sender, EventArgs e) { ckbState[ckbIndex[18]] = checkBoxImage19.Checked; } private void checkBoxImage20_Click(object sender, EventArgs e) { ckbState[ckbIndex[19]] = checkBoxImage20.Checked; } private void checkBoxImage21_Click(object sender, EventArgs e) { ckbState[ckbIndex[20]] = checkBoxImage21.Checked; } private void checkBoxImage22_Click(object sender, EventArgs e) { ckbState[ckbIndex[21]] = checkBoxImage22.Checked; } private void checkBoxImage23_Click(object sender, EventArgs e) { ckbState[ckbIndex[22]] = checkBoxImage23.Checked; } private void checkBoxImage24_Click(object sender, EventArgs e) { ckbState[ckbIndex[23]] = checkBoxImage24.Checked; } private void checkBoxImage25_Click(object sender, EventArgs e) { ckbState[ckbIndex[24]] = checkBoxImage25.Checked; } private void checkBoxImage26_Click(object sender, EventArgs e) { ckbState[ckbIndex[25]] = checkBoxImage26.Checked; } private void checkBoxImage27_Click(object sender, EventArgs e) { ckbState[ckbIndex[26]] = checkBoxImage27.Checked; } private void checkBoxImage28_Click(object sender, EventArgs e) { ckbState[ckbIndex[27]] = checkBoxImage28.Checked; } private void checkBoxImage29_Click(object sender, EventArgs e) { ckbState[ckbIndex[28]] = checkBoxImage29.Checked; } private void checkBoxImage30_Click(object sender, EventArgs e) { ckbState[ckbIndex[29]] = checkBoxImage30.Checked; } private void checkBoxImage31_Click(object sender, EventArgs e) { ckbState[ckbIndex[30]] = checkBoxImage31.Checked; } private void checkBoxImage32_Click(object sender, EventArgs e) { ckbState[ckbIndex[31]] = checkBoxImage32.Checked; } private void checkBoxImage33_Click(object sender, EventArgs e) { ckbState[ckbIndex[32]] = checkBoxImage33.Checked; } private void checkBoxImage34_Click(object sender, EventArgs e) { ckbState[ckbIndex[33]] = checkBoxImage34.Checked; } private void checkBoxImage35_Click(object sender, EventArgs e) { ckbState[ckbIndex[34]] = checkBoxImage35.Checked; } private void checkBoxImage36_Click(object sender, EventArgs e) { ckbState[ckbIndex[35]] = checkBoxImage36.Checked; } private void checkBoxImage37_Click(object sender, EventArgs e) { ckbState[ckbIndex[36]] = checkBoxImage37.Checked; } private void checkBoxImage38_Click(object sender, EventArgs e) { ckbState[ckbIndex[37]] = checkBoxImage38.Checked; } private void checkBoxImage39_Click(object sender, EventArgs e) { ckbState[ckbIndex[38]] = checkBoxImage39.Checked; } private void checkBoxImage40_Click(object sender, EventArgs e) { ckbState[ckbIndex[39]] = checkBoxImage40.Checked; } private void checkBoxImage41_Click(object sender, EventArgs e) { ckbState[ckbIndex[40]] = checkBoxImage41.Checked; } private void checkBoxImage42_Click(object sender, EventArgs e) { ckbState[ckbIndex[41]] = checkBoxImage42.Checked; } private void checkBoxImage43_Click(object sender, EventArgs e) { ckbState[ckbIndex[42]] = checkBoxImage43.Checked; } private void checkBoxImage44_Click(object sender, EventArgs e) { ckbState[ckbIndex[43]] = checkBoxImage44.Checked; } private void checkBoxImage45_Click(object sender, EventArgs e) { ckbState[ckbIndex[44]] = checkBoxImage45.Checked; } private void checkBoxImage46_Click(object sender, EventArgs e) { ckbState[ckbIndex[45]] = checkBoxImage46.Checked; } private void checkBoxImage47_Click(object sender, EventArgs e) { ckbState[ckbIndex[46]] = checkBoxImage47.Checked; } private void checkBoxImage48_Click(object sender, EventArgs e) { ckbState[ckbIndex[47]] = checkBoxImage48.Checked; } private long GetCheckBoxStates() { long result = 0; for (int i = 0; i < 48; i++) { if (ckbState[ckbIndex[i]]) result += (1L << i); } return result; } private void SetCheckBoxStates(long state) { CheckBoxImage[] chkBoxes = new CheckBoxImage[48] { checkBoxImage1, checkBoxImage2, checkBoxImage3, checkBoxImage4, checkBoxImage5, checkBoxImage6, checkBoxImage7, checkBoxImage8, checkBoxImage9, checkBoxImage10, checkBoxImage11, checkBoxImage12, checkBoxImage13, checkBoxImage14, checkBoxImage15, checkBoxImage16, checkBoxImage17, checkBoxImage18, checkBoxImage19, checkBoxImage20, checkBoxImage21, checkBoxImage22, checkBoxImage23, checkBoxImage24, checkBoxImage25, checkBoxImage26, checkBoxImage27, checkBoxImage28, checkBoxImage29, checkBoxImage30, checkBoxImage31, checkBoxImage32, checkBoxImage33, checkBoxImage34, checkBoxImage35, checkBoxImage36, checkBoxImage37, checkBoxImage38, checkBoxImage39, checkBoxImage40, checkBoxImage41, checkBoxImage42, checkBoxImage43, checkBoxImage44, checkBoxImage45, checkBoxImage46, checkBoxImage47, checkBoxImage48, }; for (int i = 0; i < 48; i++) { chkBoxes[i].Checked = ckbState[ckbIndex[i]] = ((state & (1L << i)) != 0); } } private void saveButton_Click(object sender, EventArgs e) { Program.LocalSettings.iPerlCommunicationsFormCheckboxes = GetCheckBoxStates(); DialogResult = DialogResult.OK; Close(); } #endregion private void iPerlCommunicationForm_FormClosing(object sender, FormClosingEventArgs e) { if (!forcedClose && !formCompleted && !checkBoxesEditMode) { e.Cancel = true; } } } }