Add debug logging and UI enhancements for data entry processes: Improve register reader handling in sequences and forms, add detailed data entry debug logs, integrate serial number and volume watermarking, and update localized strings in multiple languages.
3156 lines
141 KiB
C#
3156 lines
141 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 IList<int> displayedHeadPositions0;
|
|
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;
|
|
IList<int> displayPositions0 = iperlHeads
|
|
.Select(head => head.Position - 1)
|
|
.ToList();
|
|
|
|
log.WarnFormat(
|
|
"IPERL_DIALOG_LAYOUT_V3 Prepare: Assembly={0}, Version={1}, RegisterReaders={2}, Heads={3}, DisplayPositions=[{4}], ReaderIndexes=[{5}]",
|
|
typeof(iPerlCommunicationForm).Assembly.Location,
|
|
typeof(iPerlCommunicationForm).Assembly.GetName().Version,
|
|
ProcessData.RegisterReaders.Length,
|
|
string.Join(", ", iperlHeads.Select((head, index) =>
|
|
string.Format("local{0}:{1}/Position={2}", index, head.Name, head.Position))),
|
|
string.Join(",", displayPositions0.Select(position => position + 1)),
|
|
string.Join(",", waterMeterPositions0));
|
|
|
|
ShuffleTextBoxes(
|
|
WaterMetersCount,
|
|
ProcessData.LineSize,
|
|
displayPositions0);
|
|
|
|
///
|
|
/// 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, IList<int> positions0 = null)
|
|
{
|
|
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];
|
|
for (int i = 0; i < ckbIndex.Length; i++)
|
|
{
|
|
ckbIndex[i] = -1;
|
|
}
|
|
ckbState = new bool[MaxTextBoxesCount];
|
|
|
|
|
|
textBoxesCount = MaxTextBoxesCount;
|
|
///
|
|
log.WarnFormat(
|
|
"IPERL_DIALOG_LAYOUT_V3 Shuffle start: wmsCount={0}, lineSize={1}, editMode={2}, positions=[{3}]",
|
|
wmsCount,
|
|
lineSize,
|
|
checkBoxesEditMode,
|
|
positions0 == null ? "<null>" : string.Join(",", positions0.Select(position => position + 1)));
|
|
|
|
if (positions0 != null && positions0.Count == wmsCount)
|
|
{
|
|
int layoutLines = lineSize > 0
|
|
? (ProcessData.WMsCount + lineSize - 1) / lineSize
|
|
: 1;
|
|
int layoutGap = layoutLines > 0
|
|
? (MaxTextBoxesCount - ProcessData.WMsCount) / layoutLines
|
|
: 0;
|
|
|
|
for (int dest = 0; dest < positions0.Count; dest++)
|
|
{
|
|
int headPosition0 = positions0[dest];
|
|
int visualLine = lineSize > 0 ? headPosition0 / lineSize : 0;
|
|
int origin = headPosition0 + visualLine * layoutGap;
|
|
if (origin < 0 || origin >= MaxTextBoxesCount)
|
|
throw new ArgumentOutOfRangeException(nameof(positions0));
|
|
|
|
labels[dest] = labels[origin];
|
|
counters[dest] = counters[origin];
|
|
messages[dest] = messages[origin];
|
|
checkBoxes[dest] = checkBoxes[origin];
|
|
ckbIndex[origin] = dest;
|
|
|
|
log.WarnFormat(
|
|
"IPERL_DIALOG_LAYOUT_V3 Map: localIndex={0}, headPosition={1}, visualSlot={2}, labelControl={3}, messageControl={4}, checkBoxControl={5}",
|
|
dest,
|
|
headPosition0 + 1,
|
|
origin + 1,
|
|
labels[dest].Name,
|
|
messages[dest].Name,
|
|
checkBoxes[dest].Name);
|
|
}
|
|
|
|
textBoxesCount = wmsCount;
|
|
}
|
|
else if (wmsCount < textBoxesCount && lineSize > 0)
|
|
{
|
|
int nrLines = (wmsCount + lineSize - 1) / lineSize;
|
|
int gap = (textBoxesCount - wmsCount) / nrLines;
|
|
|
|
int dest = 0;
|
|
for (int l = 0; l < nrLines; l++)
|
|
{
|
|
for (int i = 0; i < lineSize; i++)
|
|
{
|
|
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;
|
|
}
|
|
|
|
displayedHeadPositions0 = positions0 != null && positions0.Count == textBoxesCount
|
|
? positions0.ToList()
|
|
: Enumerable.Range(0, textBoxesCount).ToList();
|
|
|
|
int count = checkBoxesEditMode ? textBoxesCount : Math.Min(textBoxesCount, iperlHeads.Count);
|
|
for (int j = 0; j < count; j++)
|
|
{
|
|
labels[j].Text = (displayedHeadPositions0[j] + 1).ToString();
|
|
|
|
log.WarnFormat(
|
|
"IPERL_DIALOG_LAYOUT_V3 Label: localIndex={0}, control={1}, text={2}",
|
|
j,
|
|
labels[j].Name,
|
|
labels[j].Text);
|
|
}
|
|
|
|
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();
|
|
log.WarnFormat(
|
|
"IPERL_SELECTION_CLOSE Previous=0x{0:X12}, Current=0x{1:X12}, " +
|
|
"DisplayedPositions=[{2}], SelectedDisplayedPositions=[{3}]",
|
|
Program.LocalSettings.OptoHeadsEnabled, checkboxStates,
|
|
string.Join(",", displayedHeadPositions0.Select(position => position + 1)),
|
|
string.Join(",", displayedHeadPositions0
|
|
.Where(position => (checkboxStates & (1L << position)) != 0)
|
|
.Select(position => position + 1)));
|
|
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.Randomized,
|
|
iPerlCommunicationConstants.SetFlipModeRandomizedStr,
|
|
ref resultStr);
|
|
}
|
|
|
|
private CommErr SetFlipModeConstant(IperlHead ihead, WaterMeter wm, ref string resultStr)
|
|
{
|
|
return SetFlipMode(
|
|
ihead,
|
|
FlipMode.Constant,
|
|
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.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,
|
|
config.FlipMode.HasValue ? config.FlipMode.Value.ToString() : "<not read>",
|
|
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 "SetFlipModeConstant":
|
|
SetFlipModeConstant(iHead, null, ref txt);
|
|
break;
|
|
case "SetFlipModeRandomized":
|
|
SetFlipModeRandomized(iHead, null, ref txt);
|
|
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 = Program.LocalSettings.OptoHeadsEnabled;
|
|
for (int localIndex = 0; localIndex < textBoxesCount; localIndex++)
|
|
{
|
|
int headPosition0 = displayedHeadPositions0[localIndex];
|
|
long mask = 1L << headPosition0;
|
|
|
|
if (ckbState[localIndex])
|
|
{
|
|
result |= mask;
|
|
}
|
|
else
|
|
{
|
|
result &= ~mask;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Set checkboxes to states stored in a bitfield (long)
|
|
/// </summary>
|
|
private void SetCheckBoxStates(long state)
|
|
{
|
|
log.WarnFormat(
|
|
"IPERL_DIALOG_LAYOUT_V3 SetCheckBoxStates: state=0x{0:X12}, displayedHeadPositions=[{1}]",
|
|
state,
|
|
string.Join(",", displayedHeadPositions0.Select(position => position + 1)));
|
|
|
|
for (int localIndex = 0; localIndex < textBoxesCount; localIndex++)
|
|
{
|
|
int headPosition0 = displayedHeadPositions0[localIndex];
|
|
bool enabled = (state & (1L << headPosition0)) != 0;
|
|
checkBoxes[localIndex].Checked = ckbState[localIndex] = enabled;
|
|
log.WarnFormat(
|
|
"IPERL_DIALOG_LAYOUT_V3 Enabled: headPosition={0}, localIndex={1}, enabled={2}, control={3}",
|
|
headPosition0 + 1,
|
|
localIndex,
|
|
enabled,
|
|
checkBoxes[localIndex].Name);
|
|
}
|
|
}
|
|
|
|
private void saveButton_Click(object sender, EventArgs e)
|
|
{
|
|
long previousStates = Program.LocalSettings.OptoHeadsEnabled;
|
|
long checkboxStates = GetCheckBoxStates();
|
|
log.WarnFormat(
|
|
"IPERL_SELECTION_SAVE Previous=0x{0:X12}, Current=0x{1:X12}, " +
|
|
"DisplayedPositions=[{2}], SelectedDisplayedPositions=[{3}]",
|
|
previousStates, checkboxStates,
|
|
string.Join(",", displayedHeadPositions0.Select(position => position + 1)),
|
|
string.Join(",", displayedHeadPositions0
|
|
.Where(position => (checkboxStates & (1L << position)) != 0)
|
|
.Select(position => position + 1)));
|
|
|
|
Program.LocalSettings.OptoHeadsEnabled = checkboxStates;
|
|
Program.LocalSettings.Save();
|
|
DialogResult = DialogResult.OK;
|
|
Close();
|
|
}
|
|
|
|
#endregion
|
|
|
|
private void iPerlCommunicationForm_FormClosing(object sender, FormClosingEventArgs e)
|
|
{
|
|
if (!forcedClose && !formCompleted && !checkBoxesEditMode)
|
|
{
|
|
e.Cancel = true;
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|