- added events Event.TimerBusy, Event.ValvesBusy, etc. - fixed bug in SetValvesOp - preparing a virtual bench drawing on the Process screen - partly done
431 lines
14 KiB
C#
431 lines
14 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Globalization;
|
|
using System.Text;
|
|
using System.IO.Ports;
|
|
using log4net;
|
|
using TBF.BenchControl.Generic;
|
|
using TBF.Boxes;
|
|
|
|
namespace TBF.BenchControl.MettlerToledo
|
|
{
|
|
/// <summary>
|
|
/// Mettler Toledo ICS4_5 Weighting terminal connected via serial interface (RS232)
|
|
/// </summary>
|
|
/// <note>
|
|
/// Implemented and tested on 15.10.2013 by Milan Hanajik
|
|
/// </note>
|
|
public class BalanceDev : TestBenchSim, IComponent, IDevice, GenericDevices.IBalance
|
|
{
|
|
/// <summary>
|
|
/// Info: StartMassMeasurement() and "Balnce response = .., Mass = ..."
|
|
/// Warn: Start measurement when busy is true
|
|
/// </summary>
|
|
private static readonly ILog log = LogManager.GetLogger(typeof(BalanceDev));
|
|
public override string ToString() { return string.Format("{0}, Mettler-Toledo Balance", balanceCfg.Name); }
|
|
|
|
/// <summary>
|
|
/// Enumeration of balances via static fields and methods
|
|
/// </summary>
|
|
static int nextBalanceIdx = 0;
|
|
public static void ResetStaticProperties() { nextBalanceIdx = 0; }
|
|
public static int BalancesCount { get { return nextBalanceIdx; } }
|
|
public static BalanceDev[] Balances;
|
|
public static float[] Masses;
|
|
///
|
|
protected int balanceNr; /// 0-based balance number
|
|
|
|
protected readonly BalanceCfg balanceCfg;
|
|
public Generic.IComponentCfg Cfg { get { return balanceCfg; } }
|
|
|
|
protected SerialPort serialPort;
|
|
protected StringBuilder stringBuilder;
|
|
|
|
public string Name { get { return balanceCfg.Name; } }
|
|
public int BalanceNr { get { return balanceNr; } }
|
|
public float Capacity { get { return balanceCfg.Capacity; } }
|
|
public int EmptyTimeSec { get { return balanceCfg.EmptyTimeSec; } }
|
|
public IList<Entities.MeasurementCorrection> Corrections { get { return balanceCfg.Corrections; } }
|
|
|
|
/// <summary>The state of the mass measurement</summary>
|
|
public MsrmntState MsrmntState
|
|
{
|
|
get { return msrmntState; }
|
|
}
|
|
protected MsrmntState msrmntState;
|
|
|
|
/// <summary>The mass after StartMassMeasurement() when MsrmntState == MsrmntState.Valid</summary>
|
|
public float Mass
|
|
{
|
|
get { return mass; }
|
|
}
|
|
protected float mass;
|
|
|
|
/// <summary>The time stamp after StartMassMeasurement() when MsrmntState == MsrmntState.Valid</summary>
|
|
public int MsrmntTimeStamp
|
|
{
|
|
get { return msrmntTimeStamp; }
|
|
}
|
|
protected int msrmntTimeStamp;
|
|
|
|
/// <summary>
|
|
/// Constructor
|
|
/// </summary>
|
|
/// <param name="balanceEntity">Balance properties</param>
|
|
public BalanceDev(BalanceCfg cfg, IList<Generic.IComponent> components)
|
|
{
|
|
if (cfg == null) throw new ArgumentNullException("cfg");
|
|
this.balanceCfg = cfg;
|
|
|
|
balanceNr = nextBalanceIdx++;
|
|
Masses = new float[nextBalanceIdx]; /// (re)initialize the static array of masses of all balances
|
|
|
|
if (Balances == null || Balances.Length < nextBalanceIdx)
|
|
{
|
|
BalanceDev[] balancesSoFar = Balances;
|
|
Balances = new BalanceDev[nextBalanceIdx];
|
|
if (balancesSoFar != null) for (int i = 0; i < balancesSoFar.Length; i++) Balances[i] = balancesSoFar[i];
|
|
Balances[nextBalanceIdx - 1] = this;
|
|
}
|
|
}
|
|
|
|
public new void Initialize()
|
|
{
|
|
msrmntState = MsrmntState.Failed; /// Data not valid yet
|
|
|
|
if (balanceCfg.DebugLevel == Entities.DebugMode.Simulate) return;
|
|
|
|
stringBuilder = new StringBuilder(40);
|
|
string comPortName = "COM" + balanceCfg.ComPortNr.ToString();
|
|
serialPort = new SerialPort(comPortName, balanceCfg.BaudRate, balanceCfg.Parity, balanceCfg.DataBits, balanceCfg.StopBits);
|
|
serialPort.Handshake = balanceCfg.Handshake;
|
|
serialPort.Open();
|
|
}
|
|
|
|
public bool IsEmpty(float mass)
|
|
{
|
|
return (mass <= balanceCfg.Empty);
|
|
}
|
|
|
|
public void ZeroWhenStable()
|
|
{
|
|
if (balanceCfg.DebugLevel == Entities.DebugMode.Simulate) return;
|
|
|
|
if (msrmntState == MsrmntState.Busy)
|
|
{
|
|
log.Warn("ZeroWhenStable() returns without starting zeroing the scale because measurementState == MeasurementState.Busy");
|
|
return;
|
|
}
|
|
|
|
log.Info("ZeroWhenStable()");
|
|
|
|
msrmntState = MsrmntState.Busy;
|
|
stringBuilder.Clear();
|
|
serialPort.Write("Z\r\n");
|
|
}
|
|
|
|
public void Taring()
|
|
{
|
|
if (balanceCfg.DebugLevel == Entities.DebugMode.Simulate) return;
|
|
|
|
if (msrmntState == MsrmntState.Busy)
|
|
{
|
|
log.Warn("Taring() returns without starting taring the scale because measurementState == MeasurementState.Busy");
|
|
return;
|
|
}
|
|
|
|
log.Info("Taring()");
|
|
|
|
msrmntState = MsrmntState.Busy;
|
|
stringBuilder.Clear();
|
|
serialPort.Write("T\r\n");
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get a stable mass measurement
|
|
/// </summary>
|
|
public void GetStableMassMeasurement()
|
|
{
|
|
if (balanceCfg.DebugLevel == Entities.DebugMode.Simulate) return;
|
|
|
|
if (msrmntState == MsrmntState.Busy)
|
|
{
|
|
log.Warn("GetStableMassMeasurement() returns without starting a measurement because measurementState == MeasurementState.Busy");
|
|
return;
|
|
}
|
|
|
|
log.Info("GetStableMassMeasurement()");
|
|
|
|
msrmntState = MsrmntState.Busy;
|
|
stringBuilder.Clear();
|
|
serialPort.Write("S\r\n");
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get an immediate mass measurement
|
|
/// </summary>
|
|
public void GetImmediateMassMeasurement()
|
|
{
|
|
if (balanceCfg.DebugLevel == Entities.DebugMode.Simulate) return;
|
|
|
|
if (msrmntState == MsrmntState.Busy)
|
|
{
|
|
log.Warn("GetImmediateMassMeasurement() returns without starting a measurement because measurementState == MeasurementState.Busy");
|
|
return;
|
|
}
|
|
|
|
log.Info("GetImmediateMassMeasurement()");
|
|
|
|
msrmntState = MsrmntState.Busy;
|
|
stringBuilder.Clear();
|
|
serialPort.Write("SI\r\n");
|
|
}
|
|
|
|
/// <summary>Units for SetUnits() argument</summary>
|
|
public enum Units
|
|
{
|
|
g,
|
|
kg,
|
|
t,
|
|
lb,
|
|
oz
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get the serial number
|
|
/// </summary>
|
|
/// <remarks>After a successful SetUnits(kg) mass is still sent in g. MH 15.10.2013</remarks>
|
|
public void SetUnits(Units unit)
|
|
{
|
|
if (balanceCfg.DebugLevel == Entities.DebugMode.Simulate) return;
|
|
|
|
if (msrmntState == MsrmntState.Busy)
|
|
{
|
|
log.Warn("SetUnits() returns without setting the units because measurementState == MeasurementState.Busy");
|
|
return;
|
|
}
|
|
|
|
log.InfoFormat("SetUnits({0})", unit);
|
|
|
|
msrmntState = MsrmntState.Busy;
|
|
stringBuilder.Clear();
|
|
switch (unit)
|
|
{
|
|
case Units.g: serialPort.Write("U g\r\n"); break;
|
|
case Units.kg: serialPort.Write("U kg\r\n"); break;
|
|
case Units.t: serialPort.Write("U t\r\n"); break;
|
|
case Units.lb: serialPort.Write("U lb\r\n"); break;
|
|
case Units.oz: serialPort.Write("U oz\r\n"); break;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Parse characters received from the serial port
|
|
/// </summary>
|
|
public virtual void RunDeviceBefore()
|
|
{
|
|
if (balanceCfg.DebugLevel == Entities.DebugMode.Simulate)
|
|
{
|
|
mass = GetSimMass(balanceNr);
|
|
Masses[balanceNr] = mass;
|
|
msrmntState = MsrmntState.Valid;
|
|
msrmntTimeStamp = StateMachine.Time;
|
|
return;
|
|
}
|
|
|
|
log.Debug("RunDevice()");
|
|
|
|
if (msrmntState != MsrmntState.Busy) return; /// No measurement in progress
|
|
|
|
string justReceived = serialPort.ReadExisting();
|
|
if (justReceived != null && justReceived.Length > 0)
|
|
{
|
|
log.Debug("RunDevice() received " + justReceived);
|
|
stringBuilder.Append(justReceived);
|
|
}
|
|
|
|
string currentBuffer = stringBuilder.ToString();
|
|
int eolPos = currentBuffer.IndexOf("\r\n");
|
|
if (eolPos >= 0)
|
|
{
|
|
msrmntTimeStamp = StateMachine.Time;
|
|
|
|
/// Get part of theh string before EOL
|
|
string received = currentBuffer.Substring(0, eolPos);
|
|
|
|
/// Put the rest back to the string builder
|
|
stringBuilder.Clear();
|
|
if (currentBuffer.Length > eolPos + 2)
|
|
{
|
|
stringBuilder.Append(currentBuffer.Substring(eolPos + 2));
|
|
}
|
|
|
|
while (true)
|
|
{
|
|
string tmp = received.Replace(" ", " ");
|
|
if (tmp.Length == received.Length) break;
|
|
received = tmp;
|
|
}
|
|
|
|
string[] fields = received.Split(new char[] { ' ' });
|
|
|
|
if (fields.Length == 1 && fields[0].Equals("ZA")) /// Zero when stable
|
|
{
|
|
mass = 0;
|
|
Masses[balanceNr] = mass;
|
|
//msrmntState = MsrmntState.Valid;
|
|
log.InfoFormat("Balance response = \"{0}<cr><lf>\"", received);
|
|
return;
|
|
}
|
|
else if (fields.Length < 2)
|
|
{
|
|
msrmntState = MsrmntState.Failed;
|
|
log.WarnFormat("Balance response = \"{0}<cr><lf>\", Unexpected error !!!", received);
|
|
return;
|
|
}
|
|
|
|
if (fields[0].Equals("Z")) /// Zero when stable
|
|
{
|
|
if (fields.Length == 2 && fields[1].Equals("A"))
|
|
{
|
|
mass = 0;
|
|
Masses[balanceNr] = mass;
|
|
//msrmntState = MsrmntState.Valid;
|
|
log.InfoFormat("Balance response = \"{0}<cr><lf>\"", received);
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
msrmntState = MsrmntState.Failed;
|
|
log.WarnFormat("Balance response = \"{0}<cr><lf>\", error !!!", received);
|
|
}
|
|
}
|
|
else if (fields[0].Equals("S")) /// Stable mass: field[1] = <mass> field[2] = "kg"
|
|
{
|
|
if (fields.Length >= 3 &&
|
|
float.TryParse(fields[1],
|
|
NumberStyles.AllowDecimalPoint | NumberStyles.AllowLeadingSign,
|
|
CultureInfo.InvariantCulture,
|
|
out mass))
|
|
{
|
|
if (fields[2].Equals("g")) mass /= 1000.0f;
|
|
if (fields[2].Equals("t")) mass *= 1000.0f;
|
|
Masses[balanceNr] = mass;
|
|
msrmntState = MsrmntState.Valid;
|
|
log.InfoFormat("Balance response = \"{0}<cr><lf>\", Mass = {1}", received, mass);
|
|
}
|
|
else if (fields.Length == 2 && fields[1].Equals("-"))
|
|
{
|
|
mass = 0;
|
|
Masses[balanceNr] = mass;
|
|
msrmntState = MsrmntState.Valid;
|
|
log.InfoFormat("Balance response = \"{0}<cr><lf>\", Mass = {1}", received, mass);
|
|
}
|
|
else
|
|
{
|
|
msrmntState = MsrmntState.Failed;
|
|
log.WarnFormat("Balance response = \"{0}<cr><lf>\", Overload or and unexpected error !!!", received);
|
|
bool b = float.TryParse(fields[1], NumberStyles.AllowDecimalPoint | NumberStyles.AllowLeadingSign, CultureInfo.InvariantCulture, out mass);
|
|
|
|
}
|
|
}
|
|
else if (fields[0].Equals("SD")) /// Dynamic mass: field[1] = <mass> field[2] = "kg"
|
|
{
|
|
if (fields.Length >= 3 &&
|
|
float.TryParse(fields[1],
|
|
NumberStyles.AllowDecimalPoint | NumberStyles.AllowLeadingSign,
|
|
CultureInfo.InvariantCulture,
|
|
out mass))
|
|
{
|
|
if (fields[2].Equals("g")) mass /= 1000.0f;
|
|
if (fields[2].Equals("t")) mass *= 1000.0f;
|
|
Masses[balanceNr] = mass;
|
|
msrmntState = MsrmntState.Valid;
|
|
log.InfoFormat("Balance response = \"{0}<cr><lf>\", Mass = {1}", received, mass);
|
|
}
|
|
else if (fields.Length == 2 && fields[1].Equals("-"))
|
|
{
|
|
mass = 0;
|
|
Masses[balanceNr] = mass;
|
|
msrmntState = MsrmntState.Valid;
|
|
log.InfoFormat("Balance response = \"{0}<cr><lf>\", Mass = {1}", received, mass);
|
|
}
|
|
else
|
|
{
|
|
msrmntState = MsrmntState.Failed;
|
|
log.WarnFormat("Balance response = \"{0}<cr><lf>\", Overload or and unexpected error !!!", received);
|
|
bool b = float.TryParse(fields[1], NumberStyles.AllowDecimalPoint | NumberStyles.AllowLeadingSign, CultureInfo.InvariantCulture, out mass);
|
|
|
|
}
|
|
}
|
|
else if (fields[0].Equals("TB")) /// Taring
|
|
{
|
|
if (fields.Length >= 3 &&
|
|
float.TryParse(fields[1],
|
|
NumberStyles.AllowDecimalPoint | NumberStyles.AllowLeadingSign,
|
|
CultureInfo.InvariantCulture,
|
|
out mass))
|
|
{
|
|
if (fields[2].Equals("g")) mass /= 1000.0f;
|
|
if (fields[2].Equals("t")) mass *= 1000.0f;
|
|
msrmntState = MsrmntState.Valid;
|
|
log.InfoFormat("Balance response = \"{0}<cr><lf>\", Tara = {1}", received, mass);
|
|
}
|
|
else
|
|
{
|
|
msrmntState = MsrmntState.Failed;
|
|
log.WarnFormat("Balance response = \"{0}<cr><lf>\", Overload or and unexpected error !!!", received);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
msrmntState = MsrmntState.Failed;
|
|
log.WarnFormat("Balance response = \"{0}<cr><lf>\", Unexpected error !!!", received);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>Run this device</summary>
|
|
public void RunDeviceAfter()
|
|
{
|
|
}
|
|
|
|
/// <summary>Stop this device</summary>
|
|
public void StopDevice()
|
|
{
|
|
if (serialPort != null) serialPort.Close();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Events: BalanceDone, Error
|
|
/// </summary>
|
|
/// <param name="result">Reference to a variable for 'tara' in kg</param>
|
|
/// <returns>TaringOp instance reference casted to IOperaton</returns>
|
|
public IOperation TaringOp(ref FloatBox tara)
|
|
{
|
|
return new TaringOp(this, ref tara);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Events: BalanceDone, Error
|
|
/// </summary>
|
|
/// <param name="result">Reference to a variable for the measured mass in kg</param>
|
|
/// <returns>ReadMassOp instance reference casted to IOperaton</returns>
|
|
public IOperation ReadMassOp(ref FloatBox mass)
|
|
{
|
|
return new ReadMassOp(this, ref mass);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Events: BalanceDone, Error
|
|
/// </summary>
|
|
/// <param name="result">Reference to a variable for the measured mass in kg</param>
|
|
/// <param name="readingsCount">Required mass readings count (>= 3)</param>
|
|
/// <returns>ReadMassAverageOp instance reference casted to IOperaton</returns>
|
|
public IOperation ReadStableMassOp(ref FloatBox mass, int readingsCount)
|
|
{
|
|
return new ReadStableMassOp(this, ref mass, readingsCount);
|
|
}
|
|
}
|
|
}
|