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

561 lines
18 KiB
C#

///
/// Copyright (c) 2015 Sensus Metering Systems
/// Author: Milan Hanajík
///
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Threading;
using System.Windows.Forms;
using log4net;
using TBF.Resources;
using TBF.BenchControl.WaterMeters.iPerl;
namespace TBF.BenchControl.TestMethods.iPerlCommunication
{
public partial class iPerlCommunicationForm : Form, GenericDevices.IHasCompleted
{
private static readonly ILog log = LogManager.GetLogger(typeof(iPerlCommunicationForm));
#region DLL_Interface
[DllImport("libRfid.dll", EntryPoint = "getDllVersion")]
public static extern unsafe int getDllVersion();
[DllImport("libRfid.dll", EntryPoint = "openPort")]
static extern unsafe int openPort(int comPort);
[DllImport("libRfid.dll", EntryPoint = "closePort")]
static extern unsafe int closePort();
[DllImport("libRfid.dll", EntryPoint = "readRequestPort")]
static extern unsafe int readRequestPort(MessageID messageId, int offset, int lenght, byte* ptr, int timeout_ms);
[DllImport("libRfid.dll", EntryPoint = "writeRequestPort")]
static extern unsafe int writeRequestPort(MessageID messageId, int offset, int lenght, byte* ptr, int timeout_ms);
/// <summary>
/// Wrapper function with safe interface adn unsafe body
/// </summary>
/// <returns>Value returned by readRequestPort(...)</returns>
public static unsafe int ReadRequestPort(MessageID messageID, int offset, int lenght, out byte[] buffer, int timeout)
{
byte[] buf = new byte[1000];
int retv;
fixed (byte* pBuf = buf)
{
retv = readRequestPort(messageID, offset, lenght, pBuf, timeout);
buffer = new byte[lenght];
for (int i = 0; i < lenght; i++) buffer[i] = buf[i];
}
return retv;
}
/// <summary>
/// Wrapper function with safe interface adn unsafe body
/// </summary>
/// <returns>Value returned by writeRequestPort(...)</returns>
public static unsafe int WriteRequestPort(MessageID messageID, int offset, int lenght, byte[] buffer, int timeout)
{
int retv;
fixed (byte* pBuf = buffer)
{
retv = writeRequestPort(messageID, offset, lenght, pBuf, timeout);
}
return retv;
}
#endregion DLL_Interface
const int CommTimeout = 500;
const int MaxCommRetries = 3;
// 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 readonly int WaterMetersCount;
readonly bool[] disabled;
Entities.TestResult tr;
int textBoxesCount;
Label[] labels;
TextBox[] messages;
static IList<WaterMeters.iPerl.WaterMeter> waterMeters;
Modbus.QuidoRS.QuidoRS quido;
///
/// RFID multiplexer PCB / RFID serial port and worker thread related variables
///
static string activity;
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 IList<bool> stopWorkerThreads; /// form -> worker thread
/// <summary> Parameterless constructor for 3 watermeters </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,
};
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,
};
currentGroup = 0;
lastGroup = 0; /// group numbers are >=1, lastGroup == 0 means no group
completedCommCount = 0;
workerThreads = new List<Thread>();
rfidPortNrs = new List<int>();
stopWorkerThreads = new List<bool>();
formCompleted = false;
tr = null;
/// 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);
};
}
/// <summary>
/// Constructor
/// </summary>
/// <param name="waterMetersCount">Number of text boxes for serial numbers</param>
public iPerlCommunicationForm(IList<GenericDevices.IWaterMeter> waterMeters, string activity)
: this()
{
iPerlCommunicationForm.activity = activity;
this.WaterMetersCount = waterMeters.Count;
iPerlCommunicationForm.waterMeters = new List<WaterMeters.iPerl.WaterMeter>();
foreach (var wm in waterMeters)
{
WaterMeters.iPerl.WaterMeter iPerlWM = wm as WaterMeters.iPerl.WaterMeter;
iPerlCommunicationForm.waterMeters.Add(iPerlWM);
if (iPerlWM.Group > lastGroup) lastGroup = iPerlWM.Group;
if (!rfidPortNrs.Contains(iPerlWM.RfidComPortNr))
{
int threadId = workerThreads.Count;
Thread thread = new Thread(iPerlCommunicationForm.Worker);
workerThreads.Add(thread);
rfidPortNrs.Add(iPerlWM.RfidComPortNr);
stopWorkerThreads.Add(false);
thread.Start(new Boxes.IntBox(threadId));
}
}
activityLabel.Text = activity;
ShuffleTextBoxes(this.WaterMetersCount, Program.LineSize);
disabled = new bool[this.WaterMetersCount];
}
/// <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];
messages[dest] = messages[l * lineSize + l * gap + i];
dest++;
}
}
textBoxesCount = wmsCount;
}
}
void Localize()
{
Text = Strings.Water_Meter_States;
for (int i = 0; i < textBoxesCount; i++)
{
labels[i].Text = string.Format("iPerl{0}", i + 1);
}
}
private void iPerlCommunicationForm_Load(object sender, EventArgs e)
{
Localize();
//Height = 50 + 90 * WaterMetersCount;
for (int i = 0; i < textBoxesCount; i++)
{
if (disabled != null && i < disabled.Length && disabled[i])
{
labels[i].Visible = messages[i].Visible = false;
}
else
{
labels[i].Visible = messages[i].Visible = true;
messages[i].Text = "---";
}
}
///
/// 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;
formCompleted = true;
DialogResult = DialogResult.OK;
Close();
}
void OnAdjustmentInProgress(object sender, UiBridge.AdjustmentInProgressEventArgs args)
{
tr = args.TestResult;
Redraw();
}
void Redraw()
{
if (tr != null)
{
for (int i = 0; i < textBoxesCount; i++)
{
messages[i].Text = tr.Meters[i].VolumeErrorPct.ToString("F1");
}
}
}
private void WMErrorsForm_Paint(object sender, PaintEventArgs e)
{
//if (tr != null)
//{
// System.Drawing.Graphics graphics = this.CreateGraphics();
// for (int i = 0; i < WaterMetersCount; i++)
// {
// PaintOne(graphics, rects[i], tr.Meters[i].VolumeErrorPct, tr.ErrLimLo, tr.ErrLimHi);
// }
//}
}
#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;
DialogResult = DialogResult.Cancel;
Close();
}
#endregion
static void Worker(object threadData)
{
int threadId = (threadData as Boxes.IntBox).Val;
int rfidPortNr = rfidPortNrs[threadId];
for (int group = 1; group <= lastGroup; group++)
{
while (currentGroup != group && !stopWorkerThreads[threadId]) Thread.Sleep(50);
if (stopWorkerThreads[threadId]) break;
int wmNr = 0;
bool wmFound = false;
foreach (var wm in waterMeters)
{
if ((wm.RfidComPortNr == rfidPortNr) && (wm.Group == group))
{
wmFound = true;
if (wm.DebugLevel == Entities.DebugMode.Normal)
{
if (activity.Equals("Read configuration")) ReadConfig(wm, threadId, wmNr);
else if (activity.Equals("Set to test mode")) SetToTestMode(wm, threadId, wmNr);
else if (activity.Equals("Set to active mode")) SetToActiveMode(wm, threadId, wmNr);
else OnCommCompleted(null, new CommCompletedEventArgs(threadId, wmNr, "Invalid activity"));
}
else
{
OnCommCompleted(null, new CommCompletedEventArgs(threadId, wmNr, "Simulation"));
}
break;
}
wmNr++;
}
if (!wmFound) OnCommCompleted(null, new CommCompletedEventArgs(threadId, -1, string.Empty)); /// Send negative wmNr
}
}
static void ReadConfig(WaterMeters.iPerl.WaterMeter wm, int threadId, int wmNr)
{
bool success = (0 == openPort(wm.RfidComPortNr)); /// Open RFID port
byte[] cfg = null;
if (success)
{
success = false;
/// Read configuration
for (int j = 0; j < MaxCommRetries; j++)
{
if (0 == ReadRequestPort(MessageID.Configuration, 0, ConfigStruct.Length, out cfg, CommTimeout)) { success = true; break; }
}
}
closePort(); /// Close RFID port
if (success)
{
OnCommCompleted(null, new CommCompletedEventArgs(threadId, wmNr, ConfigStruct.FromByteArray(cfg).ToString(1)));
}
else
{
OnCommCompleted(null, new CommCompletedEventArgs(threadId, wmNr, "Cannot read configuration"));
}
}
static void SetToTestMode(WaterMeters.iPerl.WaterMeter wm, int threadId, int wmNr)
{
bool success = (openPort(wm.RfidComPortNr) == 0); /// Open RFID port
if (success)
{
success = false;
/// Switch to test mode
byte[] cmd = new byte[1] { (byte)7 };
for (int j = 0; j < MaxCommRetries; j++)
{
if (0 == WriteRequestPort(MessageID.Command, 0, 1, cmd, CommTimeout)) { success = true; break; }
}
}
byte[] cfg = null;
if (success)
{
success = false;
/// Read configuration
for (int j = 0; j < MaxCommRetries; j++)
{
if (0 == ReadRequestPort(MessageID.Configuration, 0, ConfigStruct.Length, out cfg, CommTimeout)) { success = true; break; }
}
}
closePort(); /// Close RFID port
if (success)
{
OnCommCompleted(null, new CommCompletedEventArgs(threadId, wmNr, ConfigStruct.FromByteArray(cfg).ToString(1)));
}
else
{
OnCommCompleted(null, new CommCompletedEventArgs(threadId, wmNr, "Cannot switch to test mode"));
}
}
static void SetToActiveMode(WaterMeters.iPerl.WaterMeter wm, int threadId, int wmNr)
{
bool success = (openPort(wm.RfidComPortNr) == 0); /// Open RFID port
if (success)
{
success = false;
/// Switch to active mode
byte[] cmd = new byte[1] { (byte)6 };
for (int j = 0; j < MaxCommRetries; j++)
{
if (0 == WriteRequestPort(MessageID.Command, 0, 1, cmd, CommTimeout)) { success = true; break; }
}
}
byte[] cfg = null;
if (success)
{
success = false;
/// Read configuration
for (int j = 0; j < MaxCommRetries; j++)
{
if (0 == ReadRequestPort(MessageID.Configuration, 0, ConfigStruct.Length, out cfg, CommTimeout)) { success = true; break; }
}
}
closePort(); /// Close RFID port
if (success)
{
OnCommCompleted(null, new CommCompletedEventArgs(threadId, wmNr, ConfigStruct.FromByteArray(cfg).ToString(1)));
}
else
{
OnCommCompleted(null, new CommCompletedEventArgs(threadId, wmNr, "Cannot switch to active mode"));
}
}
/// <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;
lock (this)
{
if (++completedCommCount < workerThreads.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
{
/// 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;
}
}
}