///
/// Copyright (c) 2013-2018 Sensus Slovensko a.s.
///
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Text;
using System.IO.Ports;
using log4net;
using Config.Entities;
using TBF.BenchControl.Generic;
using TBF.BenchControl.GenericDevices;
using TBF.Boxes;
namespace TBF.BenchControl.MettlerToledo.Standard
{
///
/// Mettler Toledo ICS4_5 Weighting terminal connected via serial interface (RS232)
///
///
/// Implemented and tested on 15.10.2013 by Milan Hanajik
///
public class BalanceDev : TankDraining, IDevice, GenericDevices.IScale
{
///
/// Info: StartMassMeasurement() and "Balnce response = .., Mass = ..."
/// Warn: Start measurement when busy is true
///
private static readonly ILog log = LogManager.GetLogger(typeof(BalanceDev));
public override string ToString() { return string.Format("MettlerToledo.Standard.Balance({0})", Cfg.ToString(1)); }
protected readonly BalanceCfg balanceCfg;
///
/// Enumeration of balances via static fields and methods
///
static int nextBalanceIdx = 0;
public static new void ResetStaticProperties()
{
nextBalanceIdx = 0;
}
public static int BalancesCount { get { return nextBalanceIdx; } }
public static BalanceDev[] Balances;
public static double[] Masses;
///
protected int balanceNr; /// 0-based balance number
protected SerialPort serialPort;
protected StringBuilder stringBuilder;
public int ScaleNr { get { return balanceNr; } }
public double Capacity { get { return balanceCfg.Capacity; } }
public int EmptyTimeSec { get { return balanceCfg.DrainTimeSec; } }
public IValve DrainValve2 { get { return null; } }
/// Buoyancy
public float BuoyancyTemp { get { return balanceCfg.BuoyancyTemp; } }
public float BuoyancyPress { get { return balanceCfg.BuoyancyPress; } }
public float BuoyancyHumi { get { return balanceCfg.BuoyancyHumi; } }
public double WeightStandardDensity { get { return balanceCfg.WeightStandardDensity; } }
/// The state of the mass measurement
public MsrmntState MsrmntState { get { return msrmntState; } }
protected MsrmntState msrmntState;
/// Time since the last serial command in seconds
public int MsrmntTime;
/// The mass after StartMassMeasurement() when MsrmntState == MsrmntState.Valid
public double Mass { get { return mass; } }
protected double mass;
/// The time stamp after StartMassMeasurement() when MsrmntState == MsrmntState.Valid
public int MsrmntTimeStamp { get { return msrmntTimeStamp; } }
protected int msrmntTimeStamp;
public string Format { get { return balanceCfg.Format; } }
public BalanceDev()
{
}
///
/// Constructor
///
/// Balance properties
/// A list of components loaded so far
public BalanceDev(Generic.IComponentCfg cfg)
: base(cfg)
{
balanceCfg = cfg as BalanceCfg;
balanceNr = nextBalanceIdx++;
Masses = new double[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;
}
log.Debug(this.ToString());
}
public void Initialize()
{
base.Initalize();
msrmntState = MsrmntState.Failed; /// Data not valid yet
MsrmntTime = 0;
if (balanceCfg.DebugLevel == 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();
log.FatalFormat("Successfully initialized device {0}", ToString());
}
public override bool IsEmpty()
{
return DrainValve.State ? (mass <= balanceCfg.Empty) : (mass <= balanceCfg.EmptyUp);
}
public override bool ContainsMoreThen(float thld)
{
return (mass >= thld);
}
public void ZeroWhenStable()
{
if (balanceCfg.DebugLevel == DebugMode.Simulate) return;
if (msrmntState == MsrmntState.Busy)
{
log.WarnFormat("{0} - ZeroWhenStable() returns without starting zeroing the scale because measurementState == MeasurementState.Busy", Name);
return;
}
log.InfoFormat("{0} - ZeroWhenStable()", Name);
msrmntState = MsrmntState.Busy;
MsrmntTime = 0;
stringBuilder.Clear();
serialPort.Write("Z\r\n");
}
public virtual void Taring()
{
if (balanceCfg.DebugLevel == DebugMode.Simulate) return;
if (msrmntState == MsrmntState.Busy)
{
log.WarnFormat("{0} - Taring() returns without starting taring the scale because measurementState == MeasurementState.Busy", Name);
return;
}
log.InfoFormat("{0} - Taring()", Name);
msrmntState = MsrmntState.Busy;
MsrmntTime = 0;
stringBuilder.Clear();
serialPort.Write("T\r\n");
}
///
/// Get a stable mass measurement
///
public void GetStableMassMeasurement()
{
if (balanceCfg.DebugLevel == DebugMode.Simulate) return;
if (msrmntState == MsrmntState.Busy)
{
log.WarnFormat("{0} - GetStableMassMeasurement() returns without starting a measurement because measurementState == MeasurementState.Busy", Name);
return;
}
log.InfoFormat("{0} - GetStableMassMeasurement()", Name);
msrmntState = MsrmntState.Busy;
MsrmntTime = 0;
stringBuilder.Clear();
serialPort.Write("S\r\n");
}
///
/// Get an immediate mass measurement
///
public void GetImmediateMassMeasurement()
{
if (balanceCfg.DebugLevel == DebugMode.Simulate || serialPort == null) return;
if (msrmntState == MsrmntState.Busy)
{
log.WarnFormat("{0} - GetImmediateMassMeasurement() returns without starting a measurement because measurementState == MeasurementState.Busy", Name);
return;
}
log.InfoFormat("{0} - GetImmediateMassMeasurement()", Name);
msrmntState = MsrmntState.Busy;
MsrmntTime = 0;
stringBuilder.Clear();
serialPort.Write("SI\r\n");
}
/// Units for SetUnits() argument
public enum Units
{
g,
kg,
t,
lb,
oz
}
///
/// Get the serial number
///
/// After a successful SetUnits(kg) mass is still sent in g. MH 15.10.2013
public void SetUnits(Units unit)
{
if (balanceCfg.DebugLevel == DebugMode.Simulate) return;
if (msrmntState == MsrmntState.Busy)
{
log.WarnFormat("{0} - SetUnits() returns without setting the units because measurementState == MeasurementState.Busy", Name);
return;
}
log.InfoFormat("{0} - SetUnits({1})", Name, 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;
}
}
///
/// Parse characters received from the serial port
///
public virtual void RunDeviceBefore()
{
base.RunBefore();
if (balanceCfg.DebugLevel == DebugMode.Simulate)
{
Masses[balanceNr] = TestBenchSim.GetSimMass(balanceNr);
msrmntState = MsrmntState.Valid;
msrmntTimeStamp = StateMachine.Time;
return;
}
if (serialPort == null)
{
log.ErrorFormat("{0} - RunDevice() - serial port closed", Name);
return;
}
log.DebugFormat("{0} - RunDeviceBefore()", Name);
if (msrmntState != MsrmntState.Busy) return; /// No measurement in progress
string justReceived = serialPort.ReadExisting();
if (justReceived != null && justReceived.Length > 0)
{
log.DebugFormat("{0} - RunDeviceBefore() received {1}", Name, justReceived);
stringBuilder.Append(justReceived);
}
string currentBuffer = stringBuilder.ToString();
int eolPos = currentBuffer.IndexOf("\r\n");
if (eolPos >= 0)
{
msrmntTimeStamp = StateMachine.Time;
/// Get part of the 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("{0} - Balance response = \"{1}\"", Name, received);
return;
}
else if (fields.Length < 2)
{
msrmntState = MsrmntState.Failed;
log.WarnFormat("{0} - Balance response = \"{1}\", Unexpected error !!!", Name, 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("{0} - Balance response = \"{1}\"", Name, received);
return;
}
else
{
msrmntState = MsrmntState.Failed;
log.WarnFormat("{0} - Balance response = \"{1}\", error !!!", Name, received);
}
}
else if (fields[0].Equals("S")) /// Stable mass: field[1] = field[2] = "kg"
{
if (fields.Length == 3 &&
double.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("{0} - Balance response = \"{1}\", Mass = {2}", Name, received, mass);
}
else if (fields.Length == 4 && (fields[1].Equals("S") || fields[1].Equals("D")) &&
double.TryParse(fields[2],
NumberStyles.AllowDecimalPoint | NumberStyles.AllowLeadingSign,
CultureInfo.InvariantCulture,
out mass))
{
if (fields[3].Equals("g")) mass /= 1000.0f;
if (fields[3].Equals("t")) mass *= 1000.0f;
Masses[balanceNr] = mass;
msrmntState = MsrmntState.Valid;
log.InfoFormat("{0} - Balance response = \"{1}\", Mass = {2}", Name, received, mass);
}
else if (fields.Length == 2 && fields[1].Equals("-"))
{
mass = 0;
Masses[balanceNr] = mass;
msrmntState = MsrmntState.Valid;
log.InfoFormat("{0} - Balance response = \"{1}\", Mass = {2}", Name, received, mass);
}
else
{
msrmntState = MsrmntState.Failed;
log.WarnFormat("{0} Balance response = \"{1}\", Overload or and unexpected error !!!", Name, received);
bool b = double.TryParse(fields[1], NumberStyles.AllowDecimalPoint | NumberStyles.AllowLeadingSign, CultureInfo.InvariantCulture, out mass);
}
}
else if (fields[0].Equals("SD")) /// Dynamic mass: field[1] = field[2] = "kg"
{
if (fields.Length >= 3 &&
double.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("{0} - Balance response = \"{1}\", Mass = {2}", Name, received, mass);
}
else if (fields.Length == 2 && fields[1].Equals("-"))
{
mass = 0;
Masses[balanceNr] = mass;
msrmntState = MsrmntState.Valid;
log.InfoFormat("{0} - Balance response = \"{1}\", Mass = {2}", Name, received, mass);
}
else
{
msrmntState = MsrmntState.Failed;
log.WarnFormat("{0} - Balance response = \"{1}\", Overload or and unexpected error !!!", Name, received);
bool b = double.TryParse(fields[1], NumberStyles.AllowDecimalPoint | NumberStyles.AllowLeadingSign, CultureInfo.InvariantCulture, out mass);
}
}
else if (fields[0].Equals("TB")) /// Taring
{
if (fields.Length >= 3 &&
double.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("{0} - Balance response = \"{1}\", Tara = {2}", Name, received, mass);
}
else
{
msrmntState = MsrmntState.Failed;
log.WarnFormat("{0} - Balance response = \"{1}\", Overload or and unexpected error !!!", Name, received);
}
}
else if (fields[0].Equals("T")) /// Taring
{
if (fields.Length >= 4 && fields[1].Equals("S") &&
double.TryParse(fields[2],
NumberStyles.AllowDecimalPoint | NumberStyles.AllowLeadingSign,
CultureInfo.InvariantCulture,
out mass))
{
if (fields[3].Equals("g")) mass /= 1000.0f;
if (fields[3].Equals("t")) mass *= 1000.0f;
msrmntState = MsrmntState.Valid;
log.InfoFormat("{0} - Balance response = \"{1}\", Tara = {2}", Name, received, mass);
}
else
{
msrmntState = MsrmntState.Failed;
log.WarnFormat("{0} - Balance response = \"{1}\", Overload or and unexpected error !!!", Name, received);
}
}
else
{
msrmntState = MsrmntState.Failed;
log.WarnFormat("{0} - Balance response = \"{1}\", Unexpected error !!!", Name, received);
}
}
}
/// Run this device
public void RunDeviceAfter()
{
base.RunAfter();
}
/// Stop this device
public void StopDevice()
{
if ((balanceCfg.DebugLevel != DebugMode.Simulate) && (serialPort != null))
{
serialPort.Close();
serialPort = null;
}
}
public void StopDevice2() { }
///
/// Events: BalanceDone, Error
///
/// Reference to a variable for 'tara' in kg
/// TaringOp instance reference casted to IOperaton
public IOperation TaringOp(ref DoubleBox tara)
{
return new TaringOp(this, ref tara);
}
///
/// Events: BalanceDone, Error
///
/// Reference to a variable for the measured mass in kg
/// ReadMassOp instance reference casted to IOperaton
public IOperation ReadMassOp(ref DoubleBox mass)
{
return new ReadMassOp(this, ref mass);
}
///
/// Events: BalanceDone, Error
///
/// Reference to a variable for the measured mass in kg
/// Required mass readings count (>= 3)
/// Maximum spread of measurements in kg (otherwise the measurement continues)
/// Method: false = slow (precise), true = fast (immediate)
/// ReadMassAverageOp instance reference casted to IOperaton
public IOperation ReadStableMassOp(ref DoubleBox mass, int readingsCount, double maxSpread, Config.Entities.MassMethod method)
{
return new ReadStableMassOp(this, ref mass, readingsCount, maxSpread, method);
}
///
/// Events: BalanceDone, Error
///
/// Reference to the serialNumber
/// GetBalanceSNOp instance reference casted to IOperaton
public virtual IOperation GetSerNumOp(ref string serialNumber)
{
return null;
}
}
}