tbf/TestBenchFramework/BenchControl/TestMethods/iPerlCommunication/iPerlCommunicationForm.cs

1587 lines
57 KiB
C#

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