Add support for Flip Mode configuration in iPerl: Implement `SetFlipMode` functionality in `OptoHeadTest` and `RadioService`, integrate constants, enhance UI, and add unit and integration tests for constant/randomized flip mode settings.
3066 lines
136 KiB
C#
3066 lines
136 KiB
C#
///
|
|
/// Copyright (c) 2015-2023 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.Linq;
|
|
using System.Threading;
|
|
using System.Windows.Forms;
|
|
using log4net;
|
|
using Common;
|
|
using Config.Entities;
|
|
using TBF.Resources;
|
|
using TBF.Rig.Sequences;
|
|
using TBF.Rig.TestMethods.iPerlCommunication.iPerlHead;
|
|
using Results.Entities;
|
|
using static Sensus.iPerl.NfcHandler.MCI_Protocol;
|
|
using System.Threading.Tasks;
|
|
using TBF.Rig.TestMethods.iPerlCommunication.communication.C4.diagnosticLed;
|
|
using TBF.Rig.TestMethods.iPerlCommunication.communication.C4.protocolCommons;
|
|
using TBF.Rig.Uni.SharedDialogs.SmartMetersCommunication;
|
|
|
|
|
|
namespace TBF.Rig.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, /// 0AH = 10
|
|
CmdTest, /// 0BH = 11
|
|
Verify, /// 0CH = 12
|
|
WrongIPerlType, /// 0DH = 13
|
|
OutOfRange, /// 0EH = 14
|
|
Q2OutOfRange, /// 0FH = 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 ReadSerialNrStr = "Read SerialNr";
|
|
public const string SetIdleModeStr = "Set Idle 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 GetDefaultQ2CorrectionsStr = "Get default Q2 corrections";
|
|
public const string ReadQ2CorrectionStr = "Read Q2 corrections";
|
|
public const string ResetQ2CorrectionStr = "Reset Q2 correction";
|
|
public const string WriteDefaultQ2CorrectionsStr = "Write default Q2 corrections";
|
|
public const string InitOrReadQ2CorrectionsStr = "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 WriteQ2CorrectionIncl05Str = "Write_Q2_correction_incl.-0,5..0,5%"; /// No arguments
|
|
public const string WriteQ2CorrectionAltIncl05Str = "Write_Q2_correction_Alt_incl.-0,5..0,5%"; /// No arguments
|
|
public const string WriteQ2CorrectionPlusIncl05Str = "Write_Q2_correction_plus_incl.-0,5..0,5%"; /// No arguments
|
|
public const string WriteQ2CorrectionPlusAltIncl05Str = "Write_Q2_correction_plus_Alt_incl.-0,5..0,5%"; /// No arguments
|
|
public const string WriteQ2CorrectionGreeceIncl05Str = "Write_Q2_correction_Greece_incl.-0,5..0,5%"; /// Arguments: test_name
|
|
public const string WriteQ2CorrectionRLIncl05Str = "Write_Q2_correction_R-L_incl.-0,5..0,5%"; /// Arguments: test_name
|
|
public const string WriteQ2CorrectionLRIncl05Str = "Write_Q2_correction_L-R_incl.-0,5..0,5%"; /// Arguments: test_name
|
|
public const string UpdateBothQ2FactorsTestRLOnlyStr = "Update_both_Q2_factors_Test_RL_only"; /// No arguments
|
|
public const string UpdateBothQ2FactorsTestLROnlyStr = "Update_both_Q2_factors_Test_LR_only"; /// No arguments
|
|
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 ConditnlUpdateQ2CorrRLStr = "Conditional update of Q2 correction R-L"; /// Arguments: R-L_test_name
|
|
public const string ConditnlUpdateQ2CorrLRStr = "Conditional update of Q2 correction L-R"; /// Arguments: L-R_test_name
|
|
public const string Reset2HzCorrectionStr = "Reset 2Hz correction";
|
|
public const string Write2HzCorrectionStr = "Write 2Hz correction";
|
|
public const string DewaReworkRLStr = "DEWA rework R-L";
|
|
public const string DewaReworkLRStr = "DEWA rework L-R";
|
|
public const string StartTestingSealedMetersStr = "Start testing sealed meters";
|
|
public const string EndTestingSealedMetersStr = "End testing sealed meters";
|
|
public const string SimulateCmd = "simulate ";
|
|
|
|
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 Simulation / RFID / NFC Interface: ReadRequestPort, WriteRequestPort
|
|
/// <summary>
|
|
/// Wrapper function with safe interface and unsafe body
|
|
/// </summary>
|
|
/// <param name="iperlHead">Water meter (iPerlHead) object</param>
|
|
/// <returns>Value returned by readRequestPort(...)</returns>
|
|
public static int ReadRequestPort(IperlHead iperlHead, MessageID messageID, StructName structName, int offset, int length, out byte[] buffer)
|
|
{
|
|
Thread.Sleep(Math.Max(250, cfg.DelayBetweenRetries));
|
|
if (iperlHead.DebugLevel == DebugMode.FailureDuringOperation) iperlHead.DebugLevel = DebugMode.Normal;
|
|
|
|
if (iperlHead.DebugLevel != DebugMode.Normal) // Simulation
|
|
{
|
|
throw new Exception("ReadRequestPort() is not supported in simulation mode.");
|
|
//return SimulationServices.ReadRequest(cfg, iperlHead, messageID, offset, length, out buffer);
|
|
}
|
|
|
|
if (iperlHead.CommInterface == CommunicationInterface.NFC) // NFC Interface
|
|
{
|
|
return NfcServices.ReadRequest(cfg, iperlHead, structName, offset, length, out buffer);
|
|
}
|
|
else // RFID Interface
|
|
{
|
|
return RfidServices.ReadRequest(cfg, iperlHead, messageID, offset, length, out buffer);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Wrapper function with safe interface and unsafe body
|
|
/// </summary>
|
|
/// <param name="iperlHead">Water meter (iPerlHead) object</param>
|
|
/// <returns>Value returned by writeRequestPort(...)</returns>
|
|
public static int WriteRequestPort(IperlHead iperlHead, MessageID messageID, StructName structName, int offset, int length, byte[] buffer)
|
|
{
|
|
Thread.Sleep(Math.Max(250, cfg.DelayBetweenRetries));
|
|
if (iperlHead.DebugLevel == DebugMode.FailureDuringOperation) iperlHead.DebugLevel = DebugMode.Normal;
|
|
|
|
if (iperlHead.DebugLevel != DebugMode.Normal) // Simulation
|
|
{
|
|
return SimulationServices.WriteRequest(cfg, iperlHead, messageID, offset, length, buffer);
|
|
}
|
|
|
|
if (iperlHead.CommInterface == CommunicationInterface.NFC) // NFC Interface
|
|
{
|
|
return NfcServices.WriteRequest(cfg, iperlHead, structName, offset, length, buffer);
|
|
}
|
|
else // RFID Interface
|
|
{
|
|
return RfidServices.WriteRequest(cfg, iperlHead, messageID, offset, length, buffer);
|
|
}
|
|
}
|
|
#endregion iPerl_Head_RFID_Interface: ReadRequestPort, WriteRequestPort
|
|
|
|
|
|
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
|
|
|
|
|
|
/// <summary> Number of text boxes for serial numbers </summary>
|
|
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<IperlHead> iperlHeads;
|
|
static IList<int> waterMeterPositions0; /// keeps original 0-based indices in RegisterReaders list
|
|
static int commonWMType;
|
|
|
|
|
|
///
|
|
/// RFID multiplexer PCB / RFID serial port and worker thread related variables
|
|
///
|
|
static TestMethod testMethod;
|
|
public static TestMethodCfg cfg;
|
|
static IList<Config.Entities.Test> tests;
|
|
static IList<iPerlCommunicationParams> 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<Thread> workerThreads;
|
|
static IList<int> muxBrdOrGroup14Nrs;
|
|
static bool stopWorkerThreads; /// form -> worker thread
|
|
|
|
|
|
/// <summary> Parameterless constructor (without watermeters, threads) </summary>
|
|
public iPerlCommunicationForm()
|
|
{
|
|
InitializeComponent();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Constructor for checkBox states (active/inactive iPerl head) editing.
|
|
/// </summary>
|
|
/// <param name="checkBoxes">Initial check box states</param>
|
|
public iPerlCommunicationForm(bool isCheckBoxesEditMode)
|
|
: this()
|
|
{
|
|
iPerlCommunicationForm.cfg = new TestMethodCfg(null); // default iPerl Head communication params
|
|
if (isCheckBoxesEditMode)
|
|
{
|
|
checkBoxesEditMode = true;
|
|
saveButton.Visible = true;
|
|
activityLabel.Text = Strings.Optical_heads;
|
|
ShuffleTextBoxes(ProcessData.WMsCount, ProcessData.LineSize);
|
|
|
|
#if IPERL
|
|
ContextMenu cm = new ContextMenu();
|
|
cm.MenuItems.Add(NewMenuItem("Read PCB Number" , "ReadPCB"));
|
|
cm.MenuItems.Add(NewMenuItem( iPerlCommunicationConstants.ReadAdditionalCommonParametersStr, "ReadAdditionalCommonParameters"));
|
|
cm.MenuItems.Add(NewMenuItem( iPerlCommunicationConstants.SetFlipModeConstantStr, "SetFlipModeConstant"));
|
|
cm.MenuItems.Add(NewMenuItem( iPerlCommunicationConstants.SetFlipModeRandomizedStr, "SetFlipModeRandomized"));
|
|
cm.MenuItems.Add(NewMenuItem("Enter Test Mode" , "StartTestMode"));
|
|
cm.MenuItems.Add(NewMenuItem("Turn Off Test Mode (Enter Active Mode)", "TurnOffTestMode"));
|
|
cm.MenuItems.Add(NewMenuItem("Set Production Mode (Radio not start with flow)" , "SetProductionMode"));
|
|
cm.MenuItems.Add(NewMenuItem("Turn Off Radio" , "TurnOffRadio"));
|
|
cm.MenuItems.Add(NewMenuItem($"Set RFID mode ({Strings.Program_restart_is_required_to_apply_some_settings})" , "SetRFID"));
|
|
cm.MenuItems.Add(NewMenuItem($"Set NFC mode ({Strings.Program_restart_is_required_to_apply_some_settings})" , "SetNFC"));
|
|
/*if (Users.CurrentUser.AuthorizedAs == AuthorizedAs.PowerUser)
|
|
{
|
|
//cm.MenuItems.Add(NewMenuItem("Test WriteRequestPort (u8_Customer_Text)", "WriteRequestPort_u8_Customer_Text"));
|
|
cm.MenuItems.Add(NewMenuItem("Open sealing", "OpenSealing"));
|
|
}*/
|
|
this.ContextMenu = cm;
|
|
#endif
|
|
}
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Constructor for one iPerlCommunication 'test'
|
|
/// </summary>
|
|
/// <param name="waterMetersCount">Number of text boxes for serial numbers</param>
|
|
public iPerlCommunicationForm(TestMethod testMethod, Test test, iPerlCommunicationParams testParams)
|
|
: this(testMethod, new List<Test> { test }, new List<iPerlCommunicationParams> { testParams })
|
|
{
|
|
}
|
|
|
|
/// <summary>
|
|
/// Constructor for multiple iPerlCommunication 'tests'
|
|
/// </summary>
|
|
/// <param name="waterMetersCount">Number of text boxes for serial numbers</param>
|
|
public iPerlCommunicationForm(TestMethod testMethod, IList<Test> tests, IList<iPerlCommunicationParams> multiTestParams)
|
|
: this()
|
|
{
|
|
checkBoxesEditMode = false;
|
|
|
|
iPerlCommunicationForm.testMethod = testMethod;
|
|
iPerlCommunicationForm.cfg = testMethod.Cfg as TestMethodCfg;
|
|
iPerlCommunicationForm.tests = tests;
|
|
iPerlCommunicationForm.multiTestParams = multiTestParams;
|
|
|
|
if (multiTestParams.Count > 0)
|
|
{
|
|
ProcessData.RegisterReaders = StateMachine.GetMetersPath(tests[0]).RegisterReaders;
|
|
activityLabel.Text = multiTestParams[0].Activity;
|
|
foreach (var p in multiTestParams)
|
|
{
|
|
if (p.Activity.ToLower() == GetDefaultQ2CorrectionsStr.ToLower())
|
|
{
|
|
/// Reset IsQ2PreCorrectionCalculated that is used for synchronization/to prevent double REST call
|
|
ProcessData.IsQ2PreCorrectionCalculated = false;
|
|
commonWMType = 0;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
PrepareForTestsActivities();
|
|
}
|
|
|
|
|
|
void PrepareForTestsActivities()
|
|
{
|
|
startTime = DateTime.Now;
|
|
startTimeSec = StateMachine.Time;
|
|
|
|
/// Attach to 'CommCompleted' handler
|
|
CommCompletedHandler += delegate(object sender, CommCompletedEventArgs args)
|
|
{
|
|
if (InvokeRequired)
|
|
{
|
|
Invoke(new EventHandler<CommCompletedEventArgs>(DoOnCommCompleted), sender, args);
|
|
}
|
|
else DoOnCommCompleted(sender, args);
|
|
};
|
|
|
|
/// Attach to 'AllCompleted' handler
|
|
AllCompletedHandler += delegate(object sender, AllCompletedEventArgs args)
|
|
{
|
|
if (InvokeRequired)
|
|
{
|
|
Invoke(new EventHandler<AllCompletedEventArgs>(DoOnAllCompleted), sender, args);
|
|
}
|
|
else DoOnAllCompleted(sender, args);
|
|
};
|
|
|
|
formCompleted = false;
|
|
StartForceCloseHandler();
|
|
|
|
|
|
iperlHeads = new List<IperlHead>();
|
|
waterMeterPositions0 = new List<int>();
|
|
///
|
|
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, ProcessData.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<Thread>();
|
|
for (int i = 0; i < cfg.NrThreads; i++)
|
|
{
|
|
Thread thread = new Thread(Worker);
|
|
thread.CurrentCulture = CultureInfo.CurrentCulture;
|
|
thread.CurrentUICulture = CultureInfo.CurrentUICulture;
|
|
workerThreads.Add(thread);
|
|
}
|
|
|
|
muxBrdOrGroup14Nrs = new List<int>();
|
|
foreach (var iPerl in iPerlCommunicationForm.iperlHeads)
|
|
{
|
|
if (!muxBrdOrGroup14Nrs.Contains(iPerl.MuxBoardNrOrGroup14)) muxBrdOrGroup14Nrs.Add(iPerl.MuxBoardNrOrGroup14);
|
|
}
|
|
|
|
log.WarnFormat("nrThreads = {0}", workerThreads.Count);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Make sure the layout of labels/text boxes on the screen
|
|
/// corresponds to the layout of watermeters of the test bench.
|
|
/// </summary>
|
|
/// <param name="wmsCount">Number of watermeters</param>
|
|
/// <param name="lineSize">Number of watermeters in one line</param>
|
|
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;
|
|
|
|
for (int i = 0; i < ckbIndex.Length; i++)
|
|
{
|
|
ckbIndex[i] = -1; /// Initialize with invalid indices
|
|
}
|
|
|
|
int dest = 0;
|
|
for (int l = 0; l < nrLines; l++)
|
|
{
|
|
for (int i = 0; i < lineSize; i++)
|
|
{
|
|
int origin = l * lineSize + l * gap + i;
|
|
labels[dest] = labels[origin];
|
|
counters[dest] = counters[origin];
|
|
messages[dest] = messages[origin];
|
|
checkBoxes[dest] = checkBoxes[origin];
|
|
ckbIndex[origin] = dest;
|
|
dest++;
|
|
}
|
|
}
|
|
|
|
textBoxesCount = wmsCount;
|
|
}
|
|
|
|
int count = checkBoxesEditMode ? textBoxesCount : Math.Min(textBoxesCount, iperlHeads.Count);
|
|
for (int j = 0; j < count; j++)
|
|
{
|
|
labels[j].Text = (j + 1).ToString();
|
|
}
|
|
|
|
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 + 50;
|
|
Height = yMax + 60;
|
|
}
|
|
|
|
|
|
void Localize()
|
|
{
|
|
Text = checkBoxesEditMode ? Strings.Optical_heads : 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();
|
|
|
|
if (checkBoxesEditMode)
|
|
{
|
|
/// Check box edit mode => Hide explanation of activity colours
|
|
sampleLabel1.Visible = false;
|
|
sampleLabel2.Visible = false;
|
|
sampleLabel3.Visible = false;
|
|
samplePictureBox1.Visible = false;
|
|
samplePictureBox2.Visible = false;
|
|
samplePictureBox3.Visible = false;
|
|
}
|
|
|
|
///
|
|
/// Set checkbox states accroding to iPerlHeads[i].Disabled_Constant states
|
|
///
|
|
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))
|
|
{
|
|
/// iPerl position i+1 is 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
|
|
{
|
|
/// iPerl position i+1 is enabled
|
|
checkBoxes[i].Enabled = checkBoxes[i].Checked = ckbState[i] = true;
|
|
messages[i].Text = "---";
|
|
}
|
|
}
|
|
|
|
///
|
|
/// Set location and checkbox states to values stored in local settings
|
|
///
|
|
TBF.LocalSettings ls = Program.LocalSettings;
|
|
Left = (ls.iPerlCommunicationsFormLeft != 0) ? ls.iPerlCommunicationsFormLeft : 150;
|
|
Top = (ls.iPerlCommunicationsFormTop != 0) ? ls.iPerlCommunicationsFormTop : 150;
|
|
SetCheckBoxStates(ls.OptoHeadsEnabled);
|
|
|
|
if (!checkBoxesEditMode)
|
|
{
|
|
/// Regular activity (not a checkbox edit mode invoked from TBF menu)
|
|
|
|
/// Reset opto-data indication
|
|
for (int i = 0; i < iperlHeads.Count; i++)
|
|
{
|
|
counters[i].BackColor = OptoNokColor;
|
|
}
|
|
|
|
/// Start communication process by incrementing 'currentGroup'.
|
|
currentGroup++;
|
|
|
|
int wtId = 0;
|
|
foreach (var wt in workerThreads)
|
|
{
|
|
wt.Start(new Boxes.IntBox(wtId++)); /// Start worker threads !!!
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
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
|
|
}
|
|
|
|
long checkboxStates = GetCheckBoxStates();
|
|
if (Program.LocalSettings.iPerlCommunicationsFormLeft != Location.X ||
|
|
Program.LocalSettings.iPerlCommunicationsFormTop != Location.Y ||
|
|
Program.LocalSettings.OptoHeadsEnabled != checkboxStates)
|
|
{
|
|
/// Update local settings
|
|
Program.LocalSettings.iPerlCommunicationsFormLeft = Location.X;
|
|
Program.LocalSettings.iPerlCommunicationsFormTop = Location.Y;
|
|
Program.LocalSettings.OptoHeadsEnabled = checkboxStates;
|
|
Program.LocalSettings.Save();
|
|
}
|
|
|
|
formCompleted = true;
|
|
|
|
DialogResult = DialogResult.OK;
|
|
Close();
|
|
}
|
|
|
|
|
|
#region Forced close handling
|
|
|
|
public void StartForceCloseHandler()
|
|
{
|
|
UiBridge.Bridge.CloseModelessFormHandler += delegate(object sender, EventArgs args)
|
|
{
|
|
if (InvokeRequired) { Invoke(new EventHandler<EventArgs>(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
|
|
|
|
|
|
/// <summary>
|
|
/// Worker thread
|
|
/// </summary>
|
|
/// <param name="threadData">Thread ID (integer) wrapped into IntBox</param>
|
|
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], Progress.JustStarted));
|
|
|
|
|
|
if (threadID == 0)
|
|
{
|
|
/// Check whether previous activity was 'Set test mode A0' or 'A4'
|
|
if (i > 0 && multiTestParams[i - 1].Activity.ToLower().Contains(SetTestModeStr.ToLower()) && !multiTestParams[i - 1].Activity.Contains("80"))
|
|
{
|
|
/// Start processing of opto-datastreams from all iPERL-s
|
|
int count = 0;
|
|
for (int wmNr0 = 0; wmNr0 < iperlHeads.Count; wmNr0++)
|
|
{
|
|
WaterMeter wm = (ProcessData.BatchRslts.Batch.WaterMeters != null && ProcessData.BatchRslts.Batch.WaterMeters.Count > wmNr0)
|
|
? ProcessData.BatchRslts.Batch.WaterMeters[wmNr0] : null;
|
|
|
|
IperlHead ihead = iperlHeads[wmNr0];
|
|
|
|
if (ihead != null && wm != null && !wm.Disabled)
|
|
{
|
|
lock (ihead)
|
|
{
|
|
ihead.StartDataStreamProcessing();
|
|
count++;
|
|
}
|
|
}
|
|
}
|
|
log.WarnFormat("End of activity '{0}', StartDataStreamProcessing() of {1} heads was called.", multiTestParams[i - 1].Activity, count);
|
|
}
|
|
|
|
/// A new activity starts - information into RFID data log
|
|
rfidDataLogger.InfoFormat("");
|
|
rfidDataLogger.WarnFormat("Activity = {0}", currentActivity);
|
|
rfidDataLogger.InfoFormat("");
|
|
}
|
|
|
|
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
|
|
{
|
|
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;
|
|
|
|
WaterMeter wm = null;
|
|
if (ProcessData.BatchRslts.Batch.WaterMeters != null)
|
|
{
|
|
foreach (var w in ProcessData.BatchRslts.Batch.WaterMeters)
|
|
{
|
|
if (w.WMPosition == wmNr0 + 1)
|
|
{
|
|
wm = w;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
CommErr error;
|
|
string resultStr = string.Empty;
|
|
|
|
///
|
|
/// RFID communication activity call
|
|
///
|
|
if ((ihead == null) || ihead.Disabled || !ckbState[wmNr0]) error = CommErr.HeadDisabledByUser;
|
|
#if IPERL
|
|
else if (currentActivity.ToLower().Contains(ReadSerialNrStr.ToLower())) error = ReadSerialNr(threadID, ihead, ref resultStr);
|
|
else if (currentActivity.ToLower().Contains(SetTestModeStr.ToLower())) error = SetTestMode(threadID, ihead, ref resultStr);
|
|
else if (currentActivity.ToLower().Equals(SetActiveModeStr.ToLower())) error = SetActiveMode(threadID, ihead, ref resultStr);
|
|
else if (currentActivity.ToLower().Equals(SetIdleModeStr.ToLower())) error = SetIdleMode(threadID, ihead, ref resultStr);
|
|
else if (currentActivity.ToLower().Contains(ReadConfigurationStr.ToLower())) error = ReadConfiguration(ihead, wm, ref resultStr);
|
|
else if (currentActivity.ToLower().Contains( iPerlCommunicationConstants.ReadAdditionalCommonParametersStr.ToLower())) error = ReadAdditionalCommonParameters( ihead,wm, ref resultStr);
|
|
else if (currentActivity.ToLower().Contains( iPerlCommunicationConstants.SetFlipModeConstantStr.ToLower())) error = SetFlipModeConstant( ihead,wm, ref resultStr);
|
|
else if (currentActivity.ToLower().Contains( iPerlCommunicationConstants.SetFlipModeRandomizedStr.ToLower())) error = SetFlipModeRandomized( ihead,wm, ref resultStr);
|
|
///
|
|
/// RFID communication functions below require a reference to water meter entity (wm != null)
|
|
///
|
|
else if (wm == null) error = CommErr.CommFailed;
|
|
else if (currentActivity.ToLower().Contains(SimulateCmd.ToLower())) error = Simulate(ihead, wm, ref resultStr);
|
|
#endif
|
|
else
|
|
{
|
|
error = CommErr.None;
|
|
|
|
ConditionID id = ConditionID.Count;
|
|
#if IPERL
|
|
for (id = ConditionID.A; id < ConditionID.Count; id++)
|
|
{
|
|
if (currentActivity.ToLower() == string.Format(SequenceConditionOp.ConditionNameFmt, id).ToLower())
|
|
{
|
|
error = SetIperlCommMilestoneReached(testMethod, id, ref resultStr);
|
|
break;
|
|
}
|
|
}
|
|
#endif
|
|
if (id == ConditionID.Count)
|
|
{
|
|
resultStr = "Invalid activity"; /// No activity or milestone
|
|
}
|
|
}
|
|
|
|
///
|
|
/// 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.CommFailed));
|
|
}
|
|
else if (ihead.CommFailed || (error == CommErr.CommFailed))
|
|
{
|
|
ihead.CommFailed = true;
|
|
|
|
if (wm != null && (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 != null && (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], 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], Progress.Completed));
|
|
activityStep++;
|
|
|
|
if (stopWorkerThreads) break;
|
|
}
|
|
}
|
|
|
|
private CommErr SetFlipModeRandomized(IperlHead ihead, WaterMeter wm, ref string resultStr)
|
|
{
|
|
return SetFlipMode(
|
|
ihead,
|
|
FlipMode.EnabledRandomized,
|
|
iPerlCommunicationConstants.SetFlipModeRandomizedStr,
|
|
ref resultStr);
|
|
}
|
|
|
|
private CommErr SetFlipModeConstant(IperlHead ihead, WaterMeter wm, ref string resultStr)
|
|
{
|
|
return SetFlipMode(
|
|
ihead,
|
|
FlipMode.DisabledConstant,
|
|
iPerlCommunicationConstants.SetFlipModeConstantStr,
|
|
ref resultStr);
|
|
}
|
|
|
|
private CommErr SetFlipMode(
|
|
IperlHead ihead,
|
|
FlipMode flipMode,
|
|
string activityName,
|
|
ref string resultStr)
|
|
{
|
|
if (ihead == null)
|
|
{
|
|
resultStr = activityName + ": iPerl head is null.";
|
|
return CommErr.CommFailed;
|
|
}
|
|
|
|
if (ihead.OptoHeadTest == null)
|
|
{
|
|
resultStr = activityName + ": OptoHeadTest is not available.";
|
|
return CommErr.CommFailed;
|
|
}
|
|
|
|
ihead.CommFailed = false;
|
|
|
|
try
|
|
{
|
|
log.DebugFormat(
|
|
"{0} started: Head={1}, payload=0x{2:X2}",
|
|
activityName,
|
|
ihead,
|
|
(byte)flipMode);
|
|
|
|
bool successful = ihead.OptoHeadTest.SetFlipMode(flipMode);
|
|
if (successful)
|
|
{
|
|
resultStr = string.Format(
|
|
"{0}: OK (0x{1:X2})",
|
|
activityName,
|
|
(byte)flipMode);
|
|
return CommErr.None;
|
|
}
|
|
|
|
resultStr = activityName + ": FAILED";
|
|
ihead.CommFailed = true;
|
|
return CommErr.Write;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
resultStr = activityName + " failed: " + ex.Message;
|
|
ihead.CommFailed = true;
|
|
log.Error(activityName + " failed.", ex);
|
|
return CommErr.Write;
|
|
}
|
|
}
|
|
|
|
private CommErr ReadSerialNr(int threadId, IperlHead ihead, ref string resultStr)
|
|
{
|
|
log.Debug("ReadSerialNr threadId=" + threadId + ", ihead=" + ihead.ToString());
|
|
if (ihead.ConfigStruct == null)
|
|
{
|
|
log.Debug("ConfigStruct is null - created new in ReadSerialNr()");
|
|
ihead.ConfigStruct = new ConfigStruct();
|
|
}
|
|
if (ihead.CommFailed || ihead.ConfigStruct == null) return CommErr.CommFailed;
|
|
|
|
//I will do communication to meter now
|
|
|
|
CommErr error = CommErr.Read;
|
|
if (ihead.OptoHeadTest.ReadSerialNr())
|
|
{
|
|
log.Debug("ReadSerialNr successful");
|
|
resultStr = string.Format($"Serial No: {ihead.OptoHeadTest.ReadRequest_PCB()}");
|
|
error = CommErr.None;
|
|
}
|
|
else
|
|
{
|
|
resultStr = "Failed Read Serial No";
|
|
}
|
|
|
|
return error;
|
|
}
|
|
|
|
private CommErr SetIdleMode(int threadId, IperlHead ihead, ref string resultStr)
|
|
{
|
|
log.Debug("SetActiveMode threadId=" + threadId);
|
|
CommErr error = CommErr.CmdActive;
|
|
|
|
/// Switch to active mode
|
|
|
|
if (ihead.OptoHeadTest.SetIdleMode())
|
|
{
|
|
error = CommErr.None;
|
|
}
|
|
|
|
|
|
if (error == CommErr.None)
|
|
{
|
|
if (ihead.ConfigStruct == null)
|
|
resultStr = "OK (Config not available)";
|
|
else
|
|
resultStr = ihead.ConfigStruct.GetStatusModeString();
|
|
}
|
|
|
|
|
|
return error;
|
|
}
|
|
|
|
#region Communication functions
|
|
|
|
#if IPERL
|
|
|
|
/// <summary>
|
|
/// Read a complete configuration structure of the watermeter
|
|
/// </summary>
|
|
/// <param name="ihead">iPERL head object</param>
|
|
/// <param name="wm">Water meter object</param>
|
|
/// <param name="resultStr">String passed to caller</param>
|
|
/// <returns>true on success</returns>
|
|
static CommErr ReadConfiguration(IperlHead ihead, WaterMeter wm, ref string resultStr)
|
|
{
|
|
// TODO BUMI in clasic case we need to read most of data - see ConfigStruct
|
|
|
|
|
|
///
|
|
/// 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;
|
|
|
|
//zeroing
|
|
//ihead.ConfigStruct = null; /// Clear previous ConfigStruct, avoid reuse of (not anymore valid) PCB Number
|
|
|
|
CommErr error = CommErr.Read;
|
|
int readRetVal = 0;
|
|
|
|
/// Read configuration
|
|
|
|
if (ihead.OptoHeadTest.ReadConfiguration(DiagnosticLedState.State7))
|
|
{
|
|
error = CommErr.None;
|
|
//read, set and create ConfigStruct is set directly in method ReadConfiguration
|
|
//ihead.ConfigStruct = new ConfigStruct(); //.FromByteArray(config);
|
|
if (ihead.ConfigStruct != null)
|
|
{
|
|
resultStr = ihead.ConfigStruct.ToString(1);
|
|
}
|
|
else
|
|
{
|
|
resultStr = "Data not available";
|
|
}
|
|
}
|
|
else
|
|
{
|
|
resultStr = "Failed to read configuration";
|
|
}
|
|
|
|
//return error + Math.Max(0, Math.Min(readRetVal, 4));
|
|
return error;
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// 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.
|
|
/// </summary>
|
|
/// <param name="threadId">Thread ID passed to RFID communication functions</param>
|
|
/// <param name="ihead">iPERL head object</param>
|
|
/// <param name="resultStr">String passed to caller</param>
|
|
/// <returns>true on success</returns>
|
|
static CommErr SetTestMode(int threadId, IperlHead ihead, ref string resultStr)
|
|
{
|
|
log.Debug("SetTestMode threadId=" + threadId + ", ihead=" + ihead.ToString());
|
|
if (ihead.ConfigStruct == null)
|
|
{
|
|
log.Debug("ConfigStruct is null - created new in SetTestMode()");
|
|
ihead.ConfigStruct = new ConfigStruct();
|
|
}
|
|
if (ihead.CommFailed || ihead.ConfigStruct == null) return CommErr.CommFailed;
|
|
|
|
//I will do communication to meter now
|
|
|
|
DiagnosticLedState testModeConfig = DiagnosticLedState.State7; /// Default value
|
|
///
|
|
|
|
if (ihead.OptoHeadTest.SetTestMode())
|
|
{
|
|
log.Debug($" Iperl:{ihead.Name}, test mode activated now");
|
|
}
|
|
|
|
|
|
if (ihead.ConfigStruct.MeterState == ProtocolStatuses.Active/*Test*/ && ihead.ConfigStruct.TestModeConfig == testModeConfig)
|
|
{
|
|
/// Already in the correct test mode
|
|
resultStr = "Already " + ihead.ConfigStruct.ToString(1);
|
|
log.Debug($" Iperl:{ihead.Name}, status: {resultStr}");
|
|
return CommErr.None;
|
|
}
|
|
|
|
/// Communication necessary
|
|
CommErr error = CommErr.None;
|
|
|
|
/// Now the meter should be in the Test mode ... verify
|
|
if (error == CommErr.None)
|
|
{
|
|
/// Verify the configuration
|
|
error = CommErr.Verify;
|
|
|
|
|
|
if ((ihead.ConfigStruct.MeterState == ProtocolStatuses.Active) &&
|
|
(ihead.ConfigStruct.TestModeConfig == testModeConfig))
|
|
{
|
|
error = CommErr.None;
|
|
resultStr = ihead.ConfigStruct.GetActiveModeString();
|
|
}
|
|
|
|
}
|
|
|
|
return error;
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Set the watermeter to the active mode.
|
|
/// Read a part of configuration afterwards to verify the mode was set correctly.
|
|
/// </summary>
|
|
/// <param name="ihead">iPERL head object</param>
|
|
/// <param name="resultStr">String passed to caller</param>
|
|
/// <returns>true on success</returns>
|
|
static CommErr SetActiveMode(int threadId, IperlHead ihead, ref string resultStr)
|
|
{
|
|
log.Debug("SetActiveMode threadId=" + threadId);
|
|
CommErr error = CommErr.CmdActive;
|
|
|
|
/// Switch to active mode
|
|
|
|
if (ihead.OptoHeadTest.SetActiveMode())
|
|
{
|
|
error = CommErr.None;
|
|
}
|
|
|
|
|
|
if (error == CommErr.None)
|
|
{
|
|
if (ihead.ConfigStruct == null)
|
|
resultStr = "OK (Config not available)";
|
|
else
|
|
resultStr = ihead.ConfigStruct.GetActiveModeString();
|
|
}
|
|
|
|
|
|
return error;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Reads additional common parameters through the optical head.
|
|
/// </summary>
|
|
private static CommErr ReadAdditionalCommonParameters( IperlHead ihead, WaterMeter wm, ref string resultStr)
|
|
{
|
|
if (ihead == null)
|
|
{
|
|
resultStr = "Read additional common parameters: iPerl head is null.";
|
|
return CommErr.CommFailed;
|
|
}
|
|
|
|
if (wm == null)
|
|
{
|
|
resultStr =
|
|
"Read additional common parameters: WaterMeter is null.";
|
|
|
|
return CommErr.CommFailed;
|
|
}
|
|
|
|
ihead.CommFailed = false;
|
|
|
|
if (ihead.OptoHeadTest == null)
|
|
{
|
|
resultStr = "Read additional common parameters: OptoHeadTest is not available.";
|
|
return CommErr.CommFailed;
|
|
}
|
|
|
|
try
|
|
{
|
|
log.DebugFormat( "ReadAdditionalCommonParameters started: Head={0}", ihead);
|
|
|
|
bool successful = ihead.OptoHeadTest.ReadAdditionalCommonParameters( out resultStr, false);
|
|
|
|
log.DebugFormat( "ReadAdditionalCommonParameters finished: Head={0}, successful={1}, result={2}",
|
|
ihead, successful, resultStr);
|
|
|
|
if (successful)
|
|
{
|
|
ConfigStruct config = ihead.ConfigStruct;
|
|
|
|
if (config != null)
|
|
{
|
|
if (config.VersionType != null)
|
|
{
|
|
wm.FWVersion = string.Format(
|
|
"{0} {1} {2}",
|
|
config.VersionType.TouchReadVersion,
|
|
config.VersionType.MeterDeviceType,
|
|
config.VersionType.MeterFirmwareVersion);
|
|
}
|
|
|
|
if (config.ReadingUnits.HasValue)
|
|
{
|
|
wm.SerialNrAux =
|
|
config.ReadingUnits.Value.ToString();
|
|
}
|
|
|
|
if (config.FlipMode.HasValue)
|
|
{
|
|
wm.RadioAddress =
|
|
config.FlipMode.Value.ToString();
|
|
}
|
|
|
|
if (config.Calibration != null)
|
|
{
|
|
wm.CalibFactor =
|
|
config.Calibration.RawValue;
|
|
}
|
|
|
|
log.DebugFormat(
|
|
"Additional common parameters copied to WaterMeter: " +
|
|
"FWVersion={0}, ReadingUnits={1}, FlipMode={2}, CalibFactor={3}",
|
|
wm.FWVersion,
|
|
wm.SerialNrAux,
|
|
wm.RadioAddress,
|
|
wm.CalibFactor);
|
|
}
|
|
else
|
|
{
|
|
log.WarnFormat(
|
|
"Additional common parameters were read, but ConfigStruct is null: Head={0}",
|
|
ihead);
|
|
}
|
|
|
|
return CommErr.None;
|
|
}
|
|
|
|
if (string.IsNullOrWhiteSpace(resultStr))
|
|
{
|
|
resultStr = "Failed to read additional common parameters.";
|
|
}
|
|
|
|
log.ErrorFormat( "ReadAdditionalCommonParameters failed: Head={0}, result={1}", ihead, resultStr);
|
|
|
|
return CommErr.Read;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
resultStr = "Read additional common parameters failed: " + ex.Message;
|
|
log.Error( "ReadAdditionalCommonParameters failed.", ex);
|
|
return CommErr.Read;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Read a complete calibration structure from the watermeter
|
|
/// </summary>
|
|
/// <param name="ihead">Water meter object</param>
|
|
/// <param name="resultStr">String passed to caller</param>
|
|
/// <returns>true on success</returns>
|
|
// static CommErr ReadCalibration(IperlHead ihead, WaterMeter wm, ref string resultStr)
|
|
// {
|
|
// if (ihead.CommFailed) return CommErr.CommFailed;
|
|
//
|
|
// CommErr error = CommErr.Read;
|
|
// int readRetVal = 0;
|
|
//
|
|
// /// Read calibration
|
|
// byte[] calib = null;
|
|
// readRetVal = ReadRequestPort(ihead, MessageID.Calibration, StructName.Calibration, 0, CalibrationStruct.Length, out calib);
|
|
// 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;
|
|
// }
|
|
//
|
|
// return error + Math.Max(0, Math.Min(readRetVal, 4));
|
|
// }
|
|
|
|
|
|
/// <summary>
|
|
/// Read a complete calibration structure from the watermeter
|
|
/// </summary>
|
|
/// <param name="ihead">Water meter object</param>
|
|
/// <param name="resultStr">String passed to caller</param>
|
|
/// <returns>true on success</returns>
|
|
// static CommErr ReadCalibrationV4(IperlHead ihead, WaterMeter wm, ref string resultStr)
|
|
// {
|
|
// if (ihead.CommFailed) return CommErr.CommFailed;
|
|
//
|
|
// CommErr error = CommErr.Read;
|
|
// int readRetVal = 0;
|
|
//
|
|
// /// Read calibration
|
|
// byte[] calib = null;
|
|
// readRetVal = ReadRequestPort(ihead, MessageID.Calibration, StructName.Calibration, 0, CalibrationStructV4.Length, out calib);
|
|
// 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;
|
|
// }
|
|
//
|
|
// return error + Math.Max(0, Math.Min(readRetVal, 4));
|
|
// }
|
|
|
|
|
|
/// <summary>
|
|
/// Write the calculated calibration factor to the water meter.
|
|
/// Read a part of CalibrationStruct afterwards to verify factor was written correctly.
|
|
/// </summary>
|
|
/// <param name="threadId">Thread ID</param>
|
|
/// <param name="ihead">IperlHead object</param>
|
|
/// <param name="wm">Water meter object</param>
|
|
/// <param name="resultStr">String passed to caller</param>
|
|
/// <returns>true on success</returns>
|
|
// static CommErr WriteCalibrationFactor(int threadId, IperlHead ihead, 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
|
|
// ///
|
|
// CommErr error;
|
|
// byte[] data = new byte[2] { (byte)(newCalibFactor & 0x00FF), (byte)((newCalibFactor >> 8) & 0x00FF) };
|
|
// ///
|
|
// /// Write the new calibration factor (up to cfg.MaxCommRetries tims)
|
|
// ///
|
|
// error = CommErr.Write;
|
|
// if (0 == WriteRequestPort(ihead, MessageID.Calibration, StructName.Calibration, 2, 2, data))
|
|
// {
|
|
// ///
|
|
// /// Read and verify the calibration factor
|
|
// ///
|
|
// error = CommErr.ReadAfterWrite;
|
|
// byte[] calib_2_3 = null;
|
|
// if (0 == ReadRequestPort(ihead, MessageID.Calibration, StructName.Calibration, 2, 2, out calib_2_3))
|
|
// {
|
|
// 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();
|
|
// }
|
|
// }
|
|
// }
|
|
//
|
|
// ihead.CalibrationStruct.Update(data, 2);
|
|
//
|
|
// return error;
|
|
// }
|
|
|
|
|
|
/// <summary>
|
|
/// Write the calculated calibration factor to the water meter.
|
|
/// Read a part of CalibrationStruct afterwards to verify factor was written correctly.
|
|
/// </summary>
|
|
/// <param name="threadId">Thread ID</param>
|
|
/// <param name="ihead">IperlHead object</param>
|
|
/// <param name="wm">Water meter object</param>
|
|
/// <param name="resultStr">String passed to caller</param>
|
|
/// <returns>true on success</returns>
|
|
// static CommErr WriteCalibrationV4Factors(int threadId, IperlHead ihead, 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
|
|
// ///
|
|
// 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) };
|
|
// ///
|
|
// /// Write the new calibration factor (up to cfg.MaxCommRetries tims)
|
|
// ///
|
|
// error = CommErr.Write;
|
|
// if (0 == WriteRequestPort(ihead, MessageID.Calibration, StructName.Calibration, 2, 2, data) &&
|
|
// 0 == WriteRequestPort(ihead, MessageID.Calibration, StructName.Calibration, 34, 2, dataLNA))
|
|
// {
|
|
// ///
|
|
// /// Read and verify the calibration factor
|
|
// ///
|
|
// error = CommErr.ReadAfterWrite;
|
|
// byte[] calib_2_3 = null;
|
|
// byte[] calib_34_35 = null;
|
|
// if (0 == ReadRequestPort(ihead, MessageID.Calibration, StructName.Calibration, 2, 2, out calib_2_3) &&
|
|
// 0 == ReadRequestPort(ihead, MessageID.Calibration, StructName.Calibration, 34, 2, out calib_34_35))
|
|
// {
|
|
// 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();
|
|
// }
|
|
// }
|
|
// }
|
|
//
|
|
// ihead.CalibrationStructV4.Update(data, 2);
|
|
// ihead.CalibrationStructV4.Update(dataLNA, 34);
|
|
//
|
|
// return error;
|
|
// }
|
|
|
|
|
|
/// <summary>
|
|
/// Get averaged meter test result using test name and repetitions from a given test data.
|
|
/// </summary>
|
|
/// <param name="wm">Water meter</param>
|
|
/// <param name="testData">TestData (name and repetitions)</param>
|
|
/// <returns>(1) selected MeterTestRslt or (2) average of repeated MTR-s or (3) null when at least one MTR is missing</returns>
|
|
static Results.Entities.MeterTestRslt GetAverageTestRslt(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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Normalize calibration factor in case ti is close to value 8000.
|
|
/// </summary>
|
|
/// <param name="ihead">Water meter object</param>
|
|
/// <param name="resultStr">String passed to caller</param>
|
|
/// <returns>true on success</returns>
|
|
static CommErr NormalizeCalibrationFactor(int threadId, IperlHead ihead, WaterMeter wm, ref string resultStr)
|
|
{
|
|
if (ihead.CommFailed || (ihead.CalibrationStruct == null)) return CommErr.CommFailed;
|
|
|
|
if (ihead.FactorLimitLo <= ihead.CalibFactor && ihead.CalibFactor <= ihead.FactorLimitHi)
|
|
{
|
|
resultStr = "Calibration factor is OK";
|
|
return CommErr.None; /// No need to update the calibration factor
|
|
}
|
|
|
|
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.DN25_Q3_10:
|
|
case MeterType.CoaxManifold:
|
|
default:
|
|
defaultCalibFactor = 3040;
|
|
break;
|
|
}
|
|
|
|
byte[] data = new byte[2] { (byte)(defaultCalibFactor & 0x00FF), (byte)((defaultCalibFactor >> 8) & 0x00FF) };
|
|
if (0 == WriteRequestPort(ihead, MessageID.Calibration, StructName.Calibration, 2, 2, data))
|
|
{
|
|
error = CommErr.None;
|
|
ihead.CalibrationStruct.Update(data, 2);
|
|
}
|
|
|
|
if (error == CommErr.None)
|
|
{
|
|
error = CommErr.Verify;
|
|
|
|
/// Read calibration
|
|
byte[] calib_2_3 = null;
|
|
if (0 == ReadRequestPort(ihead, MessageID.Calibration, StructName.Calibration, 2, 2, out calib_2_3))
|
|
{
|
|
error = CommErr.None;
|
|
ihead.CalibrationStruct.Update(calib_2_3, 2);
|
|
resultStr = ihead.CalibrationStruct.ToString();
|
|
}
|
|
}
|
|
|
|
return error;
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Normalize calibration factor in case ti is close to value 8000.
|
|
/// </summary>
|
|
/// <param name="ihead">Water meter object</param>
|
|
/// <param name="resultStr">String passed to caller</param>
|
|
/// <returns>true on success</returns>
|
|
static CommErr NormalizeCalibrationV4Factors(int threadId, IperlHead ihead, 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
|
|
}
|
|
|
|
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.DN25_Q3_10:
|
|
case MeterType.CoaxManifold:
|
|
default:
|
|
defaultCalibFactor = 3040;
|
|
break;
|
|
}
|
|
|
|
byte[] data = new byte[2] { (byte)(defaultCalibFactor & 0x00FF), (byte)((defaultCalibFactor >> 8) & 0x00FF) };
|
|
if (0 == WriteRequestPort(ihead, MessageID.Calibration, StructName.Calibration, 2, 2, data))
|
|
{
|
|
error = CommErr.None;
|
|
ihead.CalibrationStructV4.Update(data, 2);
|
|
}
|
|
|
|
if (error == CommErr.None)
|
|
{
|
|
error = CommErr.Verify;
|
|
|
|
/// Read calibration
|
|
byte[] calib_2_3 = null;
|
|
if (0 == ReadRequestPort(ihead, MessageID.Calibration, StructName.Calibration, 2, 2, out calib_2_3))
|
|
{
|
|
error = CommErr.None;
|
|
ihead.CalibrationStructV4.Update(calib_2_3, 2);
|
|
resultStr = ihead.CalibrationStructV4.ToString();
|
|
}
|
|
}
|
|
|
|
return error;
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Reset both Q2 correction factors in the memory to 0.
|
|
/// Read them back to verify factors were written correctly.
|
|
/// </summary>
|
|
/// <param name="ihead">Water meter object</param>
|
|
/// <param name="resultStr">String passed to caller</param>
|
|
/// <returns>true on success</returns>
|
|
static CommErr ReadQ2Correction(int threadId, IperlHead ihead, WaterMeter wm, ref string resultStr)
|
|
{
|
|
if (ihead.CommFailed) return CommErr.CommFailed;
|
|
|
|
CommErr error = CommErr.Read;
|
|
|
|
/// Write zero Q2 correction
|
|
byte[] rdData = null;
|
|
///
|
|
if (0 == ReadRequestPort(ihead, MessageID.MetrologyMemory, StructName.ProductionInfo, Q2CorrFactorsAddr, 2, out rdData))
|
|
{
|
|
error = CommErr.None;
|
|
}
|
|
|
|
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]);
|
|
}
|
|
|
|
return error;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Reset both Q2 correction factors to 0.
|
|
/// Read them back to verify factors were written correctly.
|
|
/// </summary>
|
|
/// <param name="ihead">Water meter object</param>
|
|
/// <param name="resultStr">String passed to caller</param>
|
|
/// <returns>true on success</returns>
|
|
static CommErr ResetQ2Correction(int threadId, IperlHead ihead, WaterMeter wm, ref string resultStr)
|
|
{
|
|
return WriteQ2Corrections(threadId, ihead, wm, ref resultStr, 0, 0);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Set both Q2 correction factors to default values obtained by a REST service
|
|
/// Read them back to verify factors were written correctly.
|
|
/// </summary>
|
|
/// <param name="ihead">Water meter object</param>
|
|
/// <param name="resultStr">String passed to caller</param>
|
|
/// <returns>true on success</returns>
|
|
static CommErr WriteDefaultQ2Corrections(int threadId, IperlHead ihead, WaterMeter wm, ref string resultStr)
|
|
{
|
|
if (ihead.CommFailed) return CommErr.CommFailed;
|
|
|
|
if (ProcessData.IsQ2PreCorrectionCalculated)
|
|
{
|
|
return WriteQ2Corrections(threadId, ihead, wm, ref resultStr, ProcessData.CalculatedQ2PreCorrectionLR,
|
|
ProcessData.CalculatedQ2PreCorrectionRL);
|
|
}
|
|
else
|
|
{
|
|
int q2corrRL;
|
|
int q2corrLR;
|
|
switch (ihead.MeterType)
|
|
{
|
|
case MeterType.DN15:
|
|
q2corrRL = cfg.DfltQ2c_15_rl;
|
|
q2corrLR = cfg.DfltQ2c_15_lr;
|
|
break;
|
|
case MeterType.DN20:
|
|
q2corrRL = cfg.DfltQ2c_20_rl;
|
|
q2corrLR = cfg.DfltQ2c_20_lr;
|
|
break;
|
|
case MeterType.DN25:
|
|
q2corrRL = cfg.DfltQ2c_25_63_rl;
|
|
q2corrLR = cfg.DfltQ2c_25_63_lr;
|
|
break;
|
|
case MeterType.DN25_Q3_10:
|
|
q2corrRL = cfg.DfltQ2c_25_10_rl;
|
|
q2corrLR = cfg.DfltQ2c_25_10_lr;
|
|
break;
|
|
case MeterType.DN32:
|
|
q2corrRL = cfg.DfltQ2c_32_rl;
|
|
q2corrLR = cfg.DfltQ2c_32_lr;
|
|
break;
|
|
case MeterType.DN40:
|
|
q2corrRL = cfg.DfltQ2c_40_rl;
|
|
q2corrLR = cfg.DfltQ2c_40_lr;
|
|
break;
|
|
default:
|
|
q2corrRL = 0;
|
|
q2corrLR = 0;
|
|
break;
|
|
}
|
|
|
|
return WriteQ2Corrections(threadId, ihead, wm, ref resultStr, q2corrLR, q2corrRL);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Write both Q2 correction factors to given values.
|
|
/// Read them back to verify factors were written correctly.
|
|
/// </summary>
|
|
/// <param name="ihead">Water meter object</param>
|
|
/// <param name="resultStr">String passed to caller</param>
|
|
/// <returns>true on success</returns>
|
|
static CommErr WriteQ2Corrections(int threadId, IperlHead ihead, WaterMeter wm, ref string resultStr, int factorLR, int factorRL)
|
|
{
|
|
if (ihead.CommFailed) return CommErr.CommFailed;
|
|
|
|
CommErr error = CommErr.Write;
|
|
|
|
/// Write zero Q2 correction
|
|
byte[] wrData = new byte[2] { (byte)factorLR, (byte)factorRL };
|
|
///
|
|
if (0 == WriteRequestPort(ihead, MessageID.MetrologyMemory, StructName.ProductionInfo, Q2CorrFactorsAddr, wrData.Length, wrData))
|
|
{
|
|
wm.Q2CorrRL = ihead.Q2CorrRL = factorRL;
|
|
wm.Q2CorrLR = ihead.Q2CorrLR = factorLR;
|
|
error = CommErr.None;
|
|
}
|
|
|
|
///
|
|
/// 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;
|
|
if ((0 == ReadRequestPort(threadId, ihead, MessageID.MetrologyMemory, Q2CorrFactorsAddr, 2, out rdData)) &&
|
|
(rdData != null) && (rdData.Length == 2) && (rdData[0] == 0) && (rdData[1] == 0))
|
|
{
|
|
error = CommErr.None;
|
|
resultStr = string.Format("Q2 correction set to LR={0}, RL={1}", factorLR, factorRL);
|
|
}
|
|
}
|
|
#else
|
|
if (error == CommErr.None)
|
|
{
|
|
resultStr = string.Format("Q2 correction set to LR={0}, RL={1}", factorLR, factorRL);
|
|
}
|
|
#endif
|
|
|
|
return error;
|
|
}
|
|
|
|
|
|
static CommErr InitOrReadQ2Corrections(int threadId, IperlHead ihead, WaterMeter wm, ref string resultStr)
|
|
{
|
|
if (ihead.CommFailed) return CommErr.CommFailed;
|
|
if (wm.Pruefindex == 0) return CommErr.CommFailed; /// Unknown pruefindex
|
|
|
|
if (wm.Pruefindex == 1)
|
|
{
|
|
int q2corrRL;
|
|
int q2corrLR;
|
|
if (ProcessData.IsQ2PreCorrectionCalculated)
|
|
{
|
|
q2corrRL = ProcessData.CalculatedQ2PreCorrectionRL;
|
|
q2corrLR = ProcessData.CalculatedQ2PreCorrectionLR;
|
|
}
|
|
else
|
|
{
|
|
switch (ihead.MeterType)
|
|
{
|
|
case MeterType.DN15:
|
|
q2corrRL = cfg.DfltQ2c_15_rl;
|
|
q2corrLR = cfg.DfltQ2c_15_lr;
|
|
break;
|
|
case MeterType.DN20:
|
|
q2corrRL = cfg.DfltQ2c_20_rl;
|
|
q2corrLR = cfg.DfltQ2c_20_lr;
|
|
break;
|
|
case MeterType.DN25:
|
|
q2corrRL = cfg.DfltQ2c_25_63_rl;
|
|
q2corrLR = cfg.DfltQ2c_25_63_lr;
|
|
break;
|
|
case MeterType.DN25_Q3_10:
|
|
q2corrRL = cfg.DfltQ2c_25_10_rl;
|
|
q2corrLR = cfg.DfltQ2c_25_10_lr;
|
|
break;
|
|
case MeterType.DN32:
|
|
q2corrRL = cfg.DfltQ2c_32_rl;
|
|
q2corrLR = cfg.DfltQ2c_32_lr;
|
|
break;
|
|
case MeterType.DN40:
|
|
q2corrRL = cfg.DfltQ2c_40_rl;
|
|
q2corrLR = cfg.DfltQ2c_40_lr;
|
|
break;
|
|
default:
|
|
q2corrRL = 0;
|
|
q2corrLR = 0;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return WriteQ2Corrections(threadId, ihead, wm, ref resultStr, q2corrLR, q2corrRL);
|
|
}
|
|
else
|
|
{
|
|
return ReadQ2Correction(threadId, ihead, wm, ref resultStr);
|
|
}
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Reset 2Hz correction factor in the memory to 0.
|
|
/// </summary>
|
|
/// <param name="ihead">Water meter object</param>
|
|
/// <param name="resultStr">String passed to caller</param>
|
|
/// <returns>true on success</returns>
|
|
static CommErr Reset2HzCorrection(int threadId, IperlHead ihead, WaterMeter wm, ref string resultStr)
|
|
{
|
|
if (ihead.CommFailed) return CommErr.CommFailed;
|
|
|
|
CommErr error = CommErr.Write;
|
|
|
|
/// Write zero Q2 correction
|
|
byte[] wrData = new byte[1] { 0 };
|
|
///
|
|
if (0 == WriteRequestPort(ihead, MessageID.MetrologyMemory, StructName.ProductionInfo, Hz2CorrFactorsAddr, wrData.Length, wrData))
|
|
{
|
|
error = CommErr.None;
|
|
}
|
|
|
|
///
|
|
/// Verification disabled on 16.02.2016
|
|
///
|
|
#if false
|
|
/// Verify the correction factors
|
|
if (error == CommErr.None)
|
|
{
|
|
error = CommErr.Verify;
|
|
|
|
/// Read calibration
|
|
byte[] rdData = null;
|
|
if ((0 == ReadRequestPort(threadId, ihead, MessageID.MetrologyMemory, Hz2CorrFactorsAddr, 1, out rdData)) &&
|
|
(rdData != null) && (rdData.Length == 2) && (rdData[0] == 0) && (rdData[1] == 0))
|
|
{
|
|
error = CommErr.None;
|
|
resultStr = "2Hz correction reset to 0";
|
|
ihead.Q2CorrectionFactor = 0;
|
|
}
|
|
}
|
|
#else
|
|
if (error == CommErr.None)
|
|
{
|
|
resultStr = "2Hz correction reset to 0";
|
|
ihead.Hz2Correction = 0;
|
|
}
|
|
#endif
|
|
|
|
return error;
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// 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
|
|
/// </summary>
|
|
/// <param name="ihead">Water meter object</param>
|
|
/// <param name="resultStr">String passed to caller</param>
|
|
/// <returns>true on success</returns>
|
|
static CommErr CalculateAndWriteQ2Corrections(int threadId, IperlHead ihead, WaterMeter wm,
|
|
Test test, ref string resultStr, Q2CorrType q2CorrType, string restOfLine)
|
|
{
|
|
var args = string.IsNullOrEmpty(restOfLine) ? new string[0] : 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; /// The 2nd test in L-R direction in case tests in both directions are done
|
|
///
|
|
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;
|
|
|
|
if (i == 1)
|
|
{
|
|
q2adjResult.CalibFactor = oneMTR.CalibFactor;
|
|
q2adjResult.CalibFactorLNA = oneMTR.CalibFactorLNA;
|
|
q2adjResult.Q2CorrRL = oneMTR.Q2CorrRL;
|
|
q2adjResult.Q2CorrLR = oneMTR.Q2CorrLR;
|
|
}
|
|
}
|
|
|
|
q2adjResult.Error = Config.Formulas.ErrorFromVolumes(q2adjResult.VolumeMeter, q2adjResult.VolumeRef);
|
|
}
|
|
}
|
|
|
|
var q3mtr = wm.MeterTestRslts.FirstOrDefault<Results.Entities.MeterTestRslt>(x => x.Name() == "Q3");
|
|
double q3error = (q3mtr != null) ? q3mtr.Error : 0;
|
|
|
|
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;
|
|
|
|
if (i == 1)
|
|
{
|
|
q2adjResult2.CalibFactor = oneMTR.CalibFactor;
|
|
q2adjResult2.CalibFactorLNA = oneMTR.CalibFactorLNA;
|
|
q2adjResult2.Q2CorrRL = oneMTR.Q2CorrRL;
|
|
q2adjResult2.Q2CorrLR = oneMTR.Q2CorrLR;
|
|
}
|
|
}
|
|
|
|
q2adjResult2.Error = Config.Formulas.ErrorFromVolumes(q2adjResult2.VolumeMeter, q2adjResult2.VolumeRef);
|
|
}
|
|
}
|
|
|
|
|
|
if (q2adjResult == null ||
|
|
((q2CorrType == Q2CorrType.Update || q2CorrType == Q2CorrType.ConditionalUpdate) && q2adjResult2 == null))
|
|
{
|
|
return CommErr.MissingTest; /// Procedure configuration error
|
|
}
|
|
|
|
|
|
if ((q2CorrType == Q2CorrType.ConditionalUpdate ||
|
|
q2CorrType == Q2CorrType.ConditionalUpdateRL ||
|
|
q2CorrType == Q2CorrType.ConditionalUpdateLR) && wm.PassedFromTests())
|
|
{
|
|
resultStr = string.Format("No Q2 correction update");
|
|
rfidDataLogger.InfoFormat("{0}({1}): No Q2 correction update", ihead.Name, ihead.SerialNr);
|
|
return CommErr.None; /// No Q2 correction when doing conditional update and WM passed
|
|
}
|
|
|
|
double errLimitLo = test.ErrLimLo + test.Uncertainty;
|
|
double errLimitHi = test.ErrLimHi - test.Uncertainty;
|
|
|
|
///
|
|
/// Calculate Q2 correction factor(s)
|
|
///
|
|
int q2CorrRL = 0;
|
|
int q2CorrLR = 0;
|
|
///
|
|
if (q2CorrType == Q2CorrType.Standard ||
|
|
q2CorrType == Q2CorrType.Standard_incl_05 ||
|
|
q2CorrType == Q2CorrType.Standard_plus_incl_05)
|
|
{
|
|
///
|
|
/// Standard process (RL)
|
|
/// No 2nd Q2 test, MakeQ2CorrectedFrom(...) is used to calculate Q2 error
|
|
/// Do not forget to modify iPerlCommunicationSeq.MakeQ2CorrectedFrom(...)
|
|
///
|
|
double errorTarget = (q3error * q2adjResult.Error < 0) ? (0.1 * q2adjResult.Error) : (-0.1 * q2adjResult.Error);
|
|
q2CorrRL = Math.Max(-128, Math.Min(127, Convert.ToInt32(ihead.CalculateQ2CorrectionFactor(q2adjResult, q2adjResult.Q2CorrRL, q2adjTestData.Qfrom, errorTarget))));
|
|
}
|
|
else if (q2CorrType == Q2CorrType.Dewa ||
|
|
q2CorrType == Q2CorrType.Dewa_incl_05 ||
|
|
q2CorrType == Q2CorrType.Dewa_plus_incl_05)
|
|
{
|
|
///
|
|
/// Standard process for the oposite flow direction (LR)
|
|
/// No 2nd Q2 test, MakeQ2CorrectedFrom(...) is used to calculate Q2 error
|
|
/// Do not forget to modify iPerlCommunicationSeq.MakeQ2CorrectedFrom(...)
|
|
///
|
|
double errorTarget = (q3error * q2adjResult.Error < 0) ? (0.1 * q2adjResult.Error) : (-0.1 * q2adjResult.Error);
|
|
q2CorrLR = Math.Max(-128, Math.Min(127, Convert.ToInt32(ihead.CalculateQ2CorrectionFactor(q2adjResult, q2adjResult.Q2CorrLR, q2adjTestData.Qfrom, errorTarget))));
|
|
}
|
|
else if (q2CorrType == Q2CorrType.RL ||
|
|
q2CorrType == Q2CorrType.RL_incl_05 ||
|
|
q2CorrType == Q2CorrType.ConditionalUpdateRL)
|
|
{
|
|
///
|
|
/// RL only
|
|
/// Assuming the 2nd Q2 test is done afterwards
|
|
/// MakeQ2CorrectedFrom(...) is not used
|
|
///
|
|
if (q2CorrType == Q2CorrType.RL && Math.Abs(q2adjResult.Error) <= 0.5)
|
|
{
|
|
resultStr = string.Format("Q2 correction = 0 (writing bypassed)");
|
|
return CommErr.None;
|
|
}
|
|
|
|
q2CorrRL = Math.Max(-128, Math.Min(127, Convert.ToInt32(ihead.CalculateQ2CorrectionFactor(q2adjResult, q2adjResult.Q2CorrRL, q2adjTestData.Qfrom))));
|
|
}
|
|
else if (q2CorrType == Q2CorrType.LR ||
|
|
q2CorrType == Q2CorrType.LR_incl_05 ||
|
|
q2CorrType == Q2CorrType.ConditionalUpdateLR)
|
|
{
|
|
///
|
|
/// LR only
|
|
/// Assuming the 2nd Q2 test is done afterwards
|
|
/// MakeQ2CorrectedFrom(...) is not used
|
|
///
|
|
if (q2CorrType == Q2CorrType.LR && Math.Abs(q2adjResult.Error) <= 0.5)
|
|
{
|
|
resultStr = string.Format("Q2 correction = 0 (writing bypassed)");
|
|
return CommErr.None;
|
|
}
|
|
|
|
q2CorrLR = Math.Max(-128, Math.Min(127, Convert.ToInt32(ihead.CalculateQ2CorrectionFactor(q2adjResult, q2adjResult.Q2CorrLR, q2adjTestData.Qfrom))));
|
|
}
|
|
else if (q2CorrType == Q2CorrType.Update || q2CorrType == Q2CorrType.ConditionalUpdate)
|
|
{
|
|
///
|
|
/// After tests in both directions
|
|
/// Assuming the 2nd Q2 tests are done afterwards
|
|
/// MakeQ2CorrectedFrom(...) is not used
|
|
///
|
|
q2CorrRL = Math.Max(-128, Math.Min(127, Convert.ToInt32(ihead.CalculateQ2CorrectionFactor(q2adjResult, q2adjResult.Q2CorrRL, q2adjTestData.Qfrom))));
|
|
q2CorrLR = Math.Max(-128, Math.Min(127, Convert.ToInt32(ihead.CalculateQ2CorrectionFactor(q2adjResult2, q2adjResult2.Q2CorrLR, q2adjTestData2.Qfrom))));
|
|
}
|
|
else if (q2CorrType == Q2CorrType.Greece || q2CorrType == Q2CorrType.Greece_incl_05 || q2CorrType == Q2CorrType.UpdateBothQ2FactorsTestLRDir)
|
|
{
|
|
///
|
|
/// Process for Greece
|
|
///
|
|
if (q2CorrType == Q2CorrType.Greece && Math.Abs(q2adjResult.Error) <= 0.5)
|
|
{
|
|
resultStr = string.Format("Q2 correction = 0 (writing bypassed)");
|
|
return CommErr.None;
|
|
}
|
|
|
|
q2CorrLR = Math.Max(-128, Math.Min(127, Convert.ToInt32(ihead.CalculateQ2CorrectionFactor(q2adjResult, q2adjResult.Q2CorrLR, q2adjTestData.Qfrom))));
|
|
q2CorrRL = q2CorrLR;
|
|
}
|
|
else if (q2CorrType == Q2CorrType.UpdateBothQ2FactorsTestRLDir)
|
|
{
|
|
///
|
|
/// Another strange process
|
|
///
|
|
double errorTarget = (q3error * q2adjResult.Error < 0) ? (0.1 * q2adjResult.Error) : (-0.1 * q2adjResult.Error);
|
|
q2CorrRL = Math.Max(-128, Math.Min(127, Convert.ToInt32(ihead.CalculateQ2CorrectionFactor(q2adjResult, q2adjResult.Q2CorrRL, q2adjTestData.Qfrom, errorTarget))));
|
|
q2CorrLR = q2CorrRL;
|
|
}
|
|
|
|
|
|
///
|
|
/// Write calculated Q2 correction factors into the water meter
|
|
///
|
|
if (ihead.CommFailed) return CommErr.CommFailed;
|
|
|
|
CommErr error = CommErr.Write;
|
|
|
|
if (q2CorrType == Q2CorrType.Standard ||
|
|
q2CorrType == Q2CorrType.Standard_incl_05 ||
|
|
q2CorrType == Q2CorrType.Standard_plus_incl_05 ||
|
|
q2CorrType == Q2CorrType.RL ||
|
|
q2CorrType == Q2CorrType.RL_incl_05 ||
|
|
q2CorrType == Q2CorrType.ConditionalUpdateRL)
|
|
{
|
|
///
|
|
/// Write R-L Q2 correction factor only
|
|
///
|
|
byte[] wrData = new byte[1] { (byte)q2CorrRL };
|
|
if (0 == WriteRequestPort(ihead, MessageID.MetrologyMemory, StructName.ProductionInfo, Q2CorrFactorsAddrRL, wrData.Length, wrData))
|
|
{
|
|
wm.Q2CorrRL = ihead.Q2CorrRL = q2CorrRL;
|
|
resultStr = string.Format("Q2 correction: RL={0}", q2CorrRL);
|
|
error = CommErr.None;
|
|
}
|
|
}
|
|
else if (q2CorrType == Q2CorrType.Dewa ||
|
|
q2CorrType == Q2CorrType.Dewa_incl_05 ||
|
|
q2CorrType == Q2CorrType.Dewa_plus_incl_05 ||
|
|
q2CorrType == Q2CorrType.LR ||
|
|
q2CorrType == Q2CorrType.LR_incl_05 ||
|
|
q2CorrType == Q2CorrType.ConditionalUpdateLR)
|
|
{
|
|
///
|
|
/// Write L-R Q2 correction factor only
|
|
///
|
|
byte[] wrData = new byte[1] { (byte)q2CorrLR };
|
|
if (0 == WriteRequestPort(ihead, MessageID.MetrologyMemory, StructName.ProductionInfo, Q2CorrFactorsAddrLR, wrData.Length, wrData))
|
|
{
|
|
wm.Q2CorrLR = ihead.Q2CorrLR = q2CorrLR;
|
|
resultStr = string.Format("Q2 correction: LR={0}", q2CorrLR);
|
|
error = CommErr.None;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
///
|
|
/// Write both Q2 correction factors
|
|
///
|
|
byte[] wrData = new byte[2] { (byte)q2CorrLR, (byte)q2CorrRL };
|
|
if (0 == WriteRequestPort(ihead, MessageID.MetrologyMemory, StructName.ProductionInfo, Q2CorrFactorsAddr, wrData.Length, wrData))
|
|
{
|
|
wm.Q2CorrRL = ihead.Q2CorrRL = q2CorrRL;
|
|
wm.Q2CorrLR = ihead.Q2CorrLR = q2CorrLR;
|
|
resultStr = string.Format("Q2 correction: RL={0}, LR={1}", q2CorrRL, q2CorrLR);
|
|
error = CommErr.None;
|
|
}
|
|
}
|
|
|
|
|
|
///
|
|
/// Q2 correction factor was successfully written to the water meter.
|
|
/// Check whether the original error at Q2 was within limits.
|
|
///
|
|
if (error == CommErr.None &&
|
|
(q2adjResult.Error < errLimitLo || errLimitHi < q2adjResult.Error ||
|
|
(q2adjResult2 != null && (q2adjResult2.Error < errLimitLo || errLimitHi < q2adjResult2.Error))))
|
|
{
|
|
resultStr += " (OoR)";
|
|
|
|
/// At least one of Q2 errors is out of range for Q2 correction
|
|
log.WarnFormat("OoR => anyhow Q2 correction was done: Pos={0}, PCB#={1}, ***Q2_RL err={2}%***, ***Q2_LR err={3}%***, [Lo={4}%, Hi={5}%]",
|
|
ihead.Name,
|
|
ihead.SerialNr,
|
|
q2adjResult.Error.ToString("F2"),
|
|
q2adjResult2 != null ? q2adjResult2.Error.ToString("F2") : string.Empty,
|
|
errLimitLo.ToString("F2"),
|
|
errLimitHi.ToString("F2"));
|
|
|
|
rfidDataLogger.InfoFormat("{0}({1}): Q2 error ({2},{3}) is out of range ({4}..{5}) => Failed",
|
|
ihead.Name,
|
|
ihead.SerialNr,
|
|
q2adjResult.Error.ToString("F2"),
|
|
q2adjResult2 != null ? q2adjResult2.Error.ToString("F2") : string.Empty,
|
|
errLimitLo.ToString("F2"),
|
|
errLimitHi.ToString("F2"));
|
|
|
|
error = CommErr.Q2OutOfRange;
|
|
}
|
|
|
|
///
|
|
/// Verification disabled on 16.02.2016
|
|
///
|
|
#if false
|
|
if (error == CommErr.None)
|
|
{
|
|
error = CommErr.Verify;
|
|
|
|
/// Read calibration
|
|
byte[] rdData = null;
|
|
if ((0 == ReadRequestPort(threadId, ihead, MessageID.MetrologyMemory, Q2CorrFactorsAddr, 2, out rdData)) &&
|
|
(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;
|
|
}
|
|
}
|
|
#else
|
|
|
|
if ((q2CorrType != Q2CorrType.Update) && (q2CorrType != Q2CorrType.ConditionalUpdate))
|
|
{
|
|
wm.Q2ErrWOCorrection = ihead.Q2ErrWOCorrection = q2adjResult.Error;
|
|
}
|
|
|
|
///
|
|
/// Update WaterMeter entity and IperlHead
|
|
///
|
|
if (error == CommErr.None)
|
|
{
|
|
rfidDataLogger.WarnFormat("{0}({1}): {2}", ihead.Name, ihead.SerialNr, resultStr);
|
|
}
|
|
#endif
|
|
|
|
return error;
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Write the calculated 2Hz correction factor to the memory.
|
|
/// </summary>
|
|
/// <param name="ihead">Water meter object</param>
|
|
/// <param name="resultStr">String passed to caller</param>
|
|
/// <returns>true on success</returns>
|
|
static CommErr Write2HzCorrection(int threadId, IperlHead ihead, 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;
|
|
}
|
|
|
|
CommErr error = CommErr.Write;
|
|
|
|
Byte hz2CorrectionByte = (byte)(hz2CorrectionFactor & 0x000000FF);
|
|
byte[] wrData = new byte[1] { hz2CorrectionByte };
|
|
///
|
|
if (0 == WriteRequestPort(ihead, MessageID.MetrologyMemory, StructName.ProductionInfo, Hz2CorrFactorsAddr, wrData.Length, wrData))
|
|
{
|
|
error = CommErr.None;
|
|
wm.Hz2Correction = ihead.Hz2Correction = hz2CorrectionFactor;
|
|
wm.Hz2CorrectionDone = ihead.Hz2CorrectionDone = true;
|
|
}
|
|
|
|
///
|
|
/// Verification disabled on 16.02.2016
|
|
///
|
|
#if false
|
|
if (error == CommErr.None)
|
|
{
|
|
error = CommErr.Verify;
|
|
|
|
/// Read calibration
|
|
byte[] rdData = null;
|
|
if ((0 == ReadRequestPort(wm, MessageID.MetrologyMemory, Hz2CorrFactorsAddr, 1, out rdData)) &&
|
|
(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;
|
|
}
|
|
}
|
|
#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
|
|
|
|
return error;
|
|
}
|
|
|
|
/// <summary>
|
|
/// DEWA rework.
|
|
/// </summary>
|
|
/// <param name="ihead">iPERL head object</param>
|
|
/// <param name="wm">Water meter object</param>
|
|
/// <param name="flowDir">Arrow direction</param>
|
|
/// <param name="resultStr">String passed to caller</param>
|
|
/// <returns>true on success</returns>
|
|
// static CommErr DewaRework(IperlHead ihead, WaterMeter wm, FlowDir flowDir, ref string resultStr)
|
|
// {
|
|
// if (ihead.CommFailed) return CommErr.CommFailed;
|
|
//
|
|
// byte arrow = (flowDir == FlowDir.L_R) ? (byte)1 : (byte)2; /// L-R is reverse flow (=1), R-L is forward flow (=2)(default)
|
|
//
|
|
// List<iPerlDataWrite> dataToBeWritten = new List<iPerlDataWrite>
|
|
// {
|
|
// new iPerlDataWrite(MessageID.MetrologyMemory, StructName.Calibration, ihead.CommInterface == CommunicationInterface.NFC? 33:0x19A1, new byte[] { (byte)0xA5 }, "Open Sealing"), // ?????
|
|
// new iPerlDataWrite(MessageID.MetrologyMemory, StructName.Calibration, ihead.CommInterface == CommunicationInterface.NFC? 5:0x1985, new byte[] { arrow }, "Arrow"),
|
|
// new iPerlDataWrite(MessageID.MetrologyMemory, StructName.Calibration, ihead.CommInterface == CommunicationInterface.NFC? 28:0x199C, new byte[5], "Clear S/N"),
|
|
// new iPerlDataWrite(MessageID.Configuration, StructName.Configuration, 0x0015, new byte[] { (byte)0xA0 }, "Test mode config = A0"),
|
|
// new iPerlDataWrite(MessageID.MetrologyMemory, StructName.Calibration, ihead.CommInterface == CommunicationInterface.NFC? 14:0x198E, new byte[] { (byte)(2510 & 0xFF), (byte)((2510 >> 8) & 0xFF) }, "Receipt mean current")
|
|
// };
|
|
//
|
|
// // check meter status: 1-Idle, 2-Active, 3-Test, 4-End Of Life
|
|
// if (0 == ReadRequestPort(ihead, MessageID.Configuration, StructName.Configuration, 1, 1, out byte[] rdData))
|
|
// {
|
|
// int eMeterState = (int)((SByte)rdData[0]);
|
|
// switch (eMeterState)
|
|
// {
|
|
// case 1: // Idle -> Test -> Active
|
|
// dataToBeWritten.Add(new iPerlDataWrite(MessageID.Command, StructName.Command, 0x0000, new byte[] { (byte)Command.SetTestMode }, "Set test mode"));
|
|
// dataToBeWritten.Add(new iPerlDataWrite(MessageID.Command, StructName.Command, 0x0000, new byte[] { (byte)Command.SetActiveMode }, "Set active mode"));
|
|
// break;
|
|
// case 3: // Test Mode -> Active
|
|
// dataToBeWritten.Add(new iPerlDataWrite(MessageID.Command, StructName.Command, 0x0000, new byte[] { (byte)Command.SetActiveMode }, "Set active mode"));
|
|
// break;
|
|
// }
|
|
// }
|
|
//
|
|
// dataToBeWritten.Add(new iPerlDataWrite(MessageID.RadioPassthrough, StructName.RadioInfo, 0x1804, new byte[] { (byte)1 }, "System Status"));
|
|
// dataToBeWritten.Add(new iPerlDataWrite(MessageID.RadioPassthrough, StructName.RadioParams, 0x1898, new byte[] { (byte)3 }, "WakeUpInterval"));
|
|
// dataToBeWritten.Add(new iPerlDataWrite(MessageID.RadioPassthrough, StructName.RadioParams, 0x18A2, new byte[] { (byte)0x4A, (byte)0x53, (byte)0x3B, (byte)0x8F,
|
|
// (byte)0x70, (byte)0x31, (byte)0xC2, (byte)0x5D,
|
|
// (byte)0x6F, (byte)0x2D, (byte)0xE8, (byte)0x07,
|
|
// (byte)0x6E, (byte)0x0F, (byte)0x97, (byte)0xC3, }, "AES Key Crypted"));
|
|
//
|
|
// int successfyllyWrittenFlags = 0; /// Ones in this word represent communication failures, LSB represents the first step
|
|
// int mask = 1;
|
|
// foreach (var wData in dataToBeWritten)
|
|
// {
|
|
// bool isSuccessfullyWritten = false;
|
|
// if (0 == WriteRequestPort(ihead, wData.MessageID, wData.StructName, wData.Offset, wData.Data.Length, wData.Data))
|
|
// {
|
|
// isSuccessfullyWritten = true;
|
|
// }
|
|
//
|
|
// if (!isSuccessfullyWritten)
|
|
// {
|
|
// /// Communication failed in this step
|
|
// successfyllyWrittenFlags = (successfyllyWrittenFlags | mask);
|
|
// }
|
|
//
|
|
// mask = (mask << 1); /// Adjust the mask for the next step
|
|
// }
|
|
//
|
|
// if (successfyllyWrittenFlags == 0)
|
|
// {
|
|
// /// Success
|
|
// if (ihead.ConfigStruct != null)
|
|
// {
|
|
// ihead.ConfigStruct.StatusMode = ProtocolStatuses.Active;
|
|
// ihead.ConfigStruct.OpthoStatusMode = DiagnosticLedState.State4;
|
|
// }
|
|
//
|
|
// resultStr = "OK";
|
|
// return CommErr.None;
|
|
// }
|
|
// else
|
|
// {
|
|
// return CommErr.Write;
|
|
// }
|
|
// }
|
|
|
|
/// <summary>
|
|
/// Start testing a sealed meter
|
|
/// </summary>
|
|
/// <param name="ihead">iPERL head object</param>
|
|
/// <param name="wm">Water meter object</param>
|
|
/// <param name="resultStr">String passed to caller</param>
|
|
/// <returns>true on success</returns>
|
|
// static CommErr StartTestingSealedMeter(IperlHead ihead, WaterMeter wm, ref string resultStr)
|
|
// {
|
|
// if (ihead.CommFailed) return CommErr.CommFailed;
|
|
//
|
|
// Byte testModeConfig = 0xA0; /// Default value
|
|
// ///
|
|
// if (multiTestParams[currentActivityStep].Activity.Length > StartTestingSealedMetersStr.Length)
|
|
// {
|
|
// string testModeConfigStr = multiTestParams[currentActivityStep].Activity.Substring(StartTestingSealedMetersStr.Length + 1);
|
|
// UInt16 byteVal;
|
|
// if (UInt16.TryParse(testModeConfigStr, NumberStyles.HexNumber, CultureInfo.CurrentCulture, out byteVal) && byteVal <= 255)
|
|
// {
|
|
// testModeConfig = (Byte)byteVal; /// Update with specified value
|
|
// }
|
|
// }
|
|
//
|
|
// iPerlDataWrite[] dataToBeWritten = new iPerlDataWrite[]
|
|
// {
|
|
// new iPerlDataWrite(MessageID.MetrologyMemory, StructName.Calibration, ihead.CommInterface == CommunicationInterface.NFC? 33:0x19A1, new byte[] { (byte)0xA5 }, "Open Sealing"), /// 0x5A=sealed, 0xA5=unsealed ??????
|
|
// new iPerlDataWrite(MessageID.RadioPassthrough, StructName.RadioInfo, 0x1804, new byte[] { (byte)1 }, "System Status"),
|
|
// new iPerlDataWrite(MessageID.Command, StructName.Command, 0x0000, new byte[] { (byte)Command.SetActiveMode }, "Set active mode"),
|
|
// new iPerlDataWrite(MessageID.Configuration, StructName.Configuration, 0x0015, new byte[] { testModeConfig }, string.Format("Test mode config = {0:X2}", testModeConfig)),
|
|
// };
|
|
//
|
|
// if (ihead.ConfigStruct != null) ihead.OrigTestModeConfig = ihead.ConfigStruct.TestModeConfig;
|
|
//
|
|
// int successfyllyWrittenFlags = 0; /// Ones in this word represent communication failures, LSB represents the first step
|
|
// int mask = 1;
|
|
// foreach (var wData in dataToBeWritten)
|
|
// {
|
|
// bool isSuccessfullyWritten = false;
|
|
// if (0 == WriteRequestPort(ihead, wData.MessageID, wData.StructName, wData.Offset, wData.Data.Length, wData.Data))
|
|
// {
|
|
// isSuccessfullyWritten = true;
|
|
// }
|
|
//
|
|
// if (!isSuccessfullyWritten)
|
|
// {
|
|
// /// Communication failed in this step
|
|
// successfyllyWrittenFlags = (successfyllyWrittenFlags | mask);
|
|
// }
|
|
//
|
|
// mask = (mask << 1); /// Adjust the mask for the next step
|
|
// }
|
|
//
|
|
// if (successfyllyWrittenFlags == 0)
|
|
// {
|
|
// /// Success
|
|
// if (ihead.ConfigStruct != null)
|
|
// {
|
|
// ihead.ConfigStruct.MeterState = MeterState.Active;
|
|
// ihead.ConfigStruct.TestModeConfig = (byte)0xA0;
|
|
// }
|
|
//
|
|
// resultStr = "OK";
|
|
// return CommErr.None;
|
|
// }
|
|
// else
|
|
// {
|
|
// return CommErr.Write;
|
|
// }
|
|
// }
|
|
|
|
/// <summary>
|
|
/// End testing a sealed meter
|
|
/// </summary>
|
|
/// <param name="ihead">iPERL head object</param>
|
|
/// <param name="wm">Water meter object</param>
|
|
/// <param name="resultStr">String passed to caller</param>
|
|
/// <returns>true on success</returns>
|
|
// static CommErr EndTestingSealedMeter(IperlHead ihead, WaterMeter wm, ref string resultStr)
|
|
// {
|
|
// if (ihead.CommFailed) return CommErr.CommFailed;
|
|
//
|
|
// Byte testModeConfig = (ihead.OrigTestModeConfig != 0) ? ihead.OrigTestModeConfig : (byte)0x80; /// Restore original value (default is 0x80)
|
|
//
|
|
// iPerlDataWrite[] dataToBeWritten = new iPerlDataWrite[]
|
|
// {
|
|
// new iPerlDataWrite(MessageID.Command, StructName.Command, 0x0000, new byte[] { (byte)Command.SetActiveMode }, "Set active mode"),
|
|
// new iPerlDataWrite(MessageID.Configuration, StructName.Configuration, 0x0015, new byte[] { testModeConfig }, string.Format("Test mode config = {0:X2}", testModeConfig)),
|
|
// new iPerlDataWrite(MessageID.RadioPassthrough, StructName.RadioInfo, 0x1804, new byte[] { (byte)0 }, "System Status"),
|
|
// new iPerlDataWrite(MessageID.MetrologyMemory, StructName.Calibration, ihead.CommInterface == CommunicationInterface.NFC? 33:0x19A1, new byte[] { (byte)0x5A }, "Close Sealing") /// 0x5A=sealed, 0xA5=unsealed ??????
|
|
// };
|
|
//
|
|
// int successfyllyWrittenFlags = 0; /// Ones in this word represent communication failures, LSB represents the first step
|
|
// int mask = 1;
|
|
// foreach (var wData in dataToBeWritten)
|
|
// {
|
|
// bool isSuccessfullyWritten = false;
|
|
// if (0 == WriteRequestPort(ihead, wData.MessageID, wData.StructName, wData.Offset, wData.Data.Length, wData.Data))
|
|
// {
|
|
// isSuccessfullyWritten = true;
|
|
// }
|
|
//
|
|
// if (!isSuccessfullyWritten)
|
|
// {
|
|
// /// Communication failed in this step
|
|
// successfyllyWrittenFlags = (successfyllyWrittenFlags | mask);
|
|
// }
|
|
//
|
|
// mask = (mask << 1); /// Adjust the mask for the next step
|
|
// }
|
|
//
|
|
// if (successfyllyWrittenFlags == 0)
|
|
// {
|
|
// /// Success
|
|
// if (ihead.ConfigStruct != null)
|
|
// {
|
|
// ihead.ConfigStruct.MeterState = MeterState.Active;
|
|
// ihead.ConfigStruct.TestModeConfig = (byte)0xA0;
|
|
// }
|
|
//
|
|
// resultStr = "OK";
|
|
// return CommErr.None;
|
|
// }
|
|
// else
|
|
// {
|
|
// return CommErr.Write;
|
|
// }
|
|
// }
|
|
|
|
// static CommErr GetQ2PreCorrectionsFormRest(int threadId, IperlHead ihead, WaterMeter wm, ref string resultStr)
|
|
// {
|
|
// if (!ProcessData.IsQ2PreCorrectionCalculated)
|
|
// {
|
|
// ProcessData.IsQ2PreCorrectionCalculated = true;
|
|
// /*bool success = iPerlCommunicationSeq.GetQ2PreCorrectionsOrBackups(cfg, wm.WMTypeId(),
|
|
// out ProcessData.CalculatedQ2PreCorrectionLR,
|
|
// out ProcessData.CalculatedQ2PreCorrectionRL);*/
|
|
// }
|
|
//
|
|
// resultStr = string.Format("Q2 pre-corrections: LR={0} RL={1}", ProcessData.CalculatedQ2PreCorrectionLR, ProcessData.CalculatedQ2PreCorrectionRL);
|
|
// return CommErr.None;
|
|
// }
|
|
|
|
static CommErr SetIperlCommMilestoneReached(TestMethod testMethod, ConditionID id, ref string resultStr)
|
|
{
|
|
if (id < ConditionID.A || id >= ConditionID.Count)
|
|
{
|
|
resultStr = Strings.Error;
|
|
return CommErr.WrongArguments;
|
|
}
|
|
|
|
testMethod.IperlCommMilestone[(int)id] = true;
|
|
resultStr = string.Format(SequenceConditionOp.ConditionNameFmt, id);
|
|
return CommErr.None;
|
|
}
|
|
|
|
static CommErr Simulate(IperlHead ihead, WaterMeter wm, ref string resultStr)
|
|
{
|
|
string[] arguments = multiTestParams[currentActivityStep].Activity.Split(new char[] { ' ' });
|
|
|
|
if (arguments.Length < 2 || arguments[1].ToLower() != "iperls") return CommErr.None;
|
|
|
|
if (ihead.CommFailed) return CommErr.CommFailed;
|
|
|
|
string[] pcbNrs = new string[] { "831232435539", "831232435562",
|
|
"831232435587", "831232432141",
|
|
"831232432497", "831232763641" };
|
|
|
|
if (wm.WMPosition > 0 && wm.WMPosition <= pcbNrs.Length)
|
|
{
|
|
ihead.SerialNr = wm.SerialNr = pcbNrs[wm.WMPosition - 1];
|
|
}
|
|
|
|
resultStr = string.Format("PCB Nr. = {0}", wm.SerialNr ?? "<null>");
|
|
return CommErr.None;
|
|
}
|
|
|
|
#endif /// IPERL
|
|
|
|
#endregion
|
|
|
|
/// <summary>
|
|
/// Called when communication with one watermeter is completed
|
|
/// </summary>
|
|
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<CommCompletedEventArgs> 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)
|
|
{
|
|
if (++completedCommCount < 4) return;
|
|
completedCommCount = 0;
|
|
}
|
|
#endif
|
|
|
|
if (currentGroup < lastGroup)
|
|
{
|
|
/// Go to the next step / next group
|
|
currentGroup++;
|
|
}
|
|
else if (currentActivityStep + 1 < multiTestParams.Count)
|
|
{
|
|
currentGroup = 0;
|
|
currentActivityStep++;
|
|
activityLabel.Text = multiTestParams[currentActivityStep].Activity;
|
|
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);
|
|
}
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Called when communication with all watermeters is completed
|
|
/// </summary>
|
|
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<AllCompletedEventArgs> AllCompletedHandler;
|
|
|
|
void DoOnAllCompleted(object sender, AllCompletedEventArgs data)
|
|
{
|
|
Text = data.CommMessage;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Update test result representing RFID communication success/failure
|
|
/// </summary>
|
|
/// <param name="test"></param>
|
|
void UpdateRfidCommResult(IList<Config.Entities.Test> tests)
|
|
{
|
|
DateTime endTime = DateTime.Now;
|
|
int testTime = StateMachine.Time - startTimeSec;
|
|
|
|
if (!tests.Contains(StateMachine.TestInstances[0].Test))
|
|
{
|
|
tests.Insert(0, StateMachine.TestInstances[0].Test); /// 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)
|
|
{
|
|
Results.Utils.GetCounterStates(tstRslt, Program.LocalSettings.Counters);
|
|
|
|
/// Auxiliary results ... not required
|
|
|
|
/// Main results
|
|
tstRslt.MethodClass = TbfComponents.FindComponent(test.Method).ClassName;
|
|
tstRslt.TestDone = true;
|
|
tstRslt.StartTime = tstRslt.Batch.StartTime;
|
|
tstRslt.EndTime = endTime;
|
|
tstRslt.FlowSetTime = 0;
|
|
tstRslt.MassOfEvapWater = 0;
|
|
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], CompoundMeterId.Single);
|
|
|
|
if (meterRslt != null && iperlHeads[i] != null)
|
|
{
|
|
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));
|
|
}
|
|
|
|
#if IPERL
|
|
private MenuItem NewMenuItem(string text, string tag)
|
|
{
|
|
MenuItem menuItem = new MenuItem { Text = text, Tag = tag };
|
|
menuItem.Click += OnClick_Optical_Heads_Settings_Menu;
|
|
return menuItem;
|
|
}
|
|
private async void OnClick_Optical_Heads_Settings_Menu(object sender, EventArgs e)
|
|
{
|
|
MenuItem menuItem = (MenuItem)sender;
|
|
activityLabel.Text = menuItem.Text;
|
|
List<Task> tasks = new List<Task>();
|
|
foreach (var iHead in ProcessData.IperlHeads)
|
|
{
|
|
if (!checkBoxes[iHead.Position - 1].Checked)
|
|
{
|
|
if (iHead.Position - 1 < messages.Length) messages[iHead.Position - 1].Text = "";
|
|
continue;
|
|
}
|
|
messages[iHead.Position - 1].Text = $"COM{iHead.RfidComPortNr}";
|
|
Application.DoEvents(); // Refresh UI
|
|
tasks.Add(Task.Run(async () =>
|
|
{
|
|
string result = await ProcessTask(iHead, menuItem.Tag);
|
|
this.Invoke((Action)(() =>
|
|
{
|
|
messages[iHead.Position - 1].Text = result;
|
|
Application.DoEvents(); // Refresh UI
|
|
}));
|
|
|
|
}));
|
|
|
|
}
|
|
await Task.WhenAll(tasks);
|
|
}
|
|
|
|
private async Task<string> ProcessTask(IperlHead iHead, object tag)
|
|
{
|
|
string txt = "";
|
|
bool success = false;
|
|
switch (tag)
|
|
{
|
|
case "ReadPCB":
|
|
txt = iHead.OptoHeadTest.ReadRequest_PCB();
|
|
break;
|
|
case "ReadAdditionalCommonParameters":
|
|
{
|
|
if (iHead.OptoHeadTest == null)
|
|
{
|
|
txt = "OptoHeadTest is not available.";
|
|
break;
|
|
}
|
|
|
|
success = iHead.OptoHeadTest .ReadAdditionalCommonParameters( out txt, false);
|
|
|
|
if (!success && string.IsNullOrWhiteSpace(txt))
|
|
{
|
|
txt = "Failed to read additional common parameters.";
|
|
}
|
|
break;
|
|
}
|
|
case "WriteRequestPort_u8_Customer_Text":
|
|
txt = "Not Supported NOW!";//OpticalHeadTest.WriteRequestPort_u8_Customer_Text(iHead);
|
|
break;
|
|
case "OpenSealing":
|
|
txt = "Not Supported NOW!";//OpticalHeadTest.OpenSealing(iHead);
|
|
break;
|
|
case "StartTestMode":
|
|
txt = iHead.OptoHeadTest.SetTestMode(ref success);
|
|
break;
|
|
case "TurnOffTestMode":
|
|
txt = iHead.OptoHeadTest.SetActiveMode(ref success);
|
|
break;
|
|
case "TurnOffRadio":
|
|
txt = "Not Supported NOW!";//OpticalHeadTest.TurnOffRadio(iHead);
|
|
break;
|
|
case "SetProductionMode":
|
|
txt = "Not Supported NOW!";//OpticalHeadTest.SetProductionMode(iHead);
|
|
break;
|
|
case "SetRFID":
|
|
txt = "Not Supported NOW!";//OpticalHeadTest.SetRfidMode(iHead);
|
|
break;
|
|
case "SetNFC":
|
|
txt = "Not Supported NOW!";//OpticalHeadTest.SetNfcMode(iHead);
|
|
break;
|
|
default:
|
|
txt = "Unsupported operation: " + Convert.ToString(tag);
|
|
break;
|
|
}
|
|
return txt;
|
|
}
|
|
#endif /// IPERL
|
|
|
|
#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; }
|
|
|
|
/// <summary>
|
|
/// Get states of checkboxes as one bitfield (long)
|
|
/// </summary>
|
|
/// <returns>Long bitfield</returns>
|
|
private long GetCheckBoxStates()
|
|
{
|
|
long result = 0;
|
|
for (int i = 0; i < 48; i++)
|
|
{
|
|
int wmNr0 = ckbIndex[i];
|
|
if (wmNr0 >= 0 && ckbState[wmNr0])
|
|
{
|
|
result += (1L << wmNr0);
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Set checkboxes to states stored in a bitfield (long)
|
|
/// </summary>
|
|
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++)
|
|
{
|
|
int wmNr0 = ckbIndex[i];
|
|
if (wmNr0 >= 0)
|
|
{
|
|
chkBoxes[i].Checked = ckbState[wmNr0] = ((state & (1L << wmNr0)) != 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void saveButton_Click(object sender, EventArgs e)
|
|
{
|
|
Program.LocalSettings.OptoHeadsEnabled = GetCheckBoxStates();
|
|
Program.LocalSettings.Save();
|
|
DialogResult = DialogResult.OK;
|
|
Close();
|
|
}
|
|
|
|
#endregion
|
|
|
|
private void iPerlCommunicationForm_FormClosing(object sender, FormClosingEventArgs e)
|
|
{
|
|
if (!forcedClose && !formCompleted && !checkBoxesEditMode)
|
|
{
|
|
e.Cancel = true;
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|