344 lines
11 KiB
C#
344 lines
11 KiB
C#
///
|
|
/// Copyright (c) 2015-2018 Sensus Metering Systems
|
|
///
|
|
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.Multi
|
|
{
|
|
/// <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 : TankDraining, IDevice, GenericDevices.IScale
|
|
{
|
|
/// <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("MettlerToledo.Multi.Balance({0})", Cfg.ToString(1)); }
|
|
|
|
protected readonly BalanceCfg balanceCfg;
|
|
protected static BalanceCfg balance1Cfg;
|
|
|
|
/// <summary>
|
|
/// Enumeration of balances via static fields and methods
|
|
/// </summary>
|
|
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 static SerialPort serialPort;
|
|
protected static StringBuilder stringBuilder;
|
|
|
|
public int ScaleNr { get { return balanceNr; } }
|
|
public int IdNr { get { return balanceCfg.IdNr; } }
|
|
public double Capacity { get { return balanceCfg.Capacity; } }
|
|
public double Resolution { get { return balanceCfg.Resolution; } }
|
|
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; } }
|
|
|
|
|
|
/// <summary>The state of the mass measurement</summary>
|
|
public MsrmntState MsrmntState { get { return msrmntState; } }
|
|
protected static MsrmntState msrmntState;
|
|
|
|
/// <summary>The mass after StartMassMeasurement() when MsrmntState == MsrmntState.Valid</summary>
|
|
public double Mass { get { return Masses[balanceNr]; } }
|
|
|
|
/// <summary>The time stamp after StartMassMeasurement() when MsrmntState == MsrmntState.Valid</summary>
|
|
public int MsrmntTimeStamp { get { return msrmntTimeStamp; } }
|
|
protected int msrmntTimeStamp;
|
|
|
|
public string Format { get { return balanceCfg.Format; } }
|
|
|
|
public BalanceDev()
|
|
{
|
|
}
|
|
|
|
/// <summary>
|
|
/// Constructor
|
|
/// </summary>
|
|
/// <param name="cfg">Balance properties</param>
|
|
public BalanceDev(Generic.IComponentCfg cfg)
|
|
: base(cfg)
|
|
{
|
|
balanceCfg = cfg as BalanceCfg;
|
|
if (balanceCfg.IdNr == 1) balance1Cfg = 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
|
|
|
|
if (balanceCfg.DebugLevel == DebugMode.Simulate) return;
|
|
|
|
if (balanceCfg.IdNr == 1)
|
|
{
|
|
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 (Mass <= balanceCfg.Empty);
|
|
}
|
|
|
|
public override bool ContainsMoreThen(float thld)
|
|
{
|
|
return (Mass >= thld);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get a stable mass measurement
|
|
/// </summary>
|
|
public static bool GetStableMassMeasurement(int scaleId, string name)
|
|
{
|
|
if (balance1Cfg.DebugLevel == DebugMode.Simulate) return true;
|
|
|
|
if (msrmntState == MsrmntState.Busy ||
|
|
msrmntState == MsrmntState.Busy1 ||
|
|
msrmntState == MsrmntState.Busy2 ||
|
|
msrmntState == MsrmntState.Busy3)
|
|
{
|
|
log.WarnFormat("{0} - GetStableMassMeasurement() returns false ... measurementState == {1}", name, msrmntState.ToString());
|
|
return false;
|
|
}
|
|
|
|
msrmntState = (MsrmntState)((int)MsrmntState.Busy + scaleId);
|
|
stringBuilder.Clear();
|
|
#if BADGER_STREDNA_TRAT
|
|
string serialData = string.Format("AR WT0{0}01\r\n", 3 - scaleId); /// 2->1 a 1->2
|
|
#else
|
|
string serialData = string.Format("AR WT0{0}01\r\n", scaleId);
|
|
#endif
|
|
serialPort.Write(serialData);
|
|
log.InfoFormat("{0} - GetStableMassMeasurement() ... \"{1}\" sent", name, serialData);
|
|
return true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get an immediate mass measurement
|
|
/// </summary>
|
|
public static bool GetImmediateMassMeasurement(int scaleId, string name)
|
|
{
|
|
if (balance1Cfg.DebugLevel == DebugMode.Simulate) return true;
|
|
|
|
if (msrmntState == MsrmntState.Busy ||
|
|
msrmntState == MsrmntState.Busy1 ||
|
|
msrmntState == MsrmntState.Busy2 ||
|
|
msrmntState == MsrmntState.Busy3)
|
|
{
|
|
log.WarnFormat("{0} - GetImmediateMassMeasurement() returns false ... measurementState == {1}", name, msrmntState.ToString());
|
|
return false;
|
|
}
|
|
|
|
msrmntState = (MsrmntState)((int)MsrmntState.Busy + scaleId);
|
|
stringBuilder.Clear();
|
|
#if BADGER_STREDNA_TRAT
|
|
string serialData = string.Format("AR WT0{0}01\r\n", 3 - scaleId); /// 2->1 a 1->2
|
|
#else
|
|
string serialData = string.Format("AR WT0{0}01\r\n", scaleId);
|
|
#endif
|
|
serialPort.Write(serialData);
|
|
log.InfoFormat("{0} - GetImmediateMassMeasurement() ... \"{1}\" sent", name, serialData);
|
|
return true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Parse characters received from the serial port
|
|
/// </summary>
|
|
public virtual void RunDeviceBefore()
|
|
{
|
|
if (balanceCfg.DebugLevel == DebugMode.Simulate)
|
|
{
|
|
Masses[balanceNr] = TestBenchSim.GetSimMass(balanceNr);
|
|
msrmntState = MsrmntState.Valid;
|
|
msrmntTimeStamp = StateMachine.Time;
|
|
return;
|
|
}
|
|
|
|
if (balanceCfg.IdNr != 1) return;
|
|
|
|
if (serialPort == null)
|
|
{
|
|
log.ErrorFormat("{0} - RunDevice() - serial port closed", Name);
|
|
return;
|
|
}
|
|
|
|
log.DebugFormat("{0} - RunDevice()", Name);
|
|
|
|
///
|
|
/// Communication runs only when the state is MsrmntState.Busy#
|
|
///
|
|
if (msrmntState != MsrmntState.Busy1 &&
|
|
msrmntState != MsrmntState.Busy2 &&
|
|
msrmntState != MsrmntState.Busy3)
|
|
{
|
|
return;
|
|
}
|
|
|
|
string justReceived = serialPort.ReadExisting();
|
|
if (justReceived != null && justReceived.Length > 0)
|
|
{
|
|
log.DebugFormat("RunDevice() received {0}", justReceived);
|
|
stringBuilder.Append(justReceived);
|
|
}
|
|
|
|
string currentBuffer = stringBuilder.ToString();
|
|
int eolPos = currentBuffer.IndexOf("\r\n");
|
|
if (eolPos >= 0)
|
|
{
|
|
log.InfoFormat("eolPos = {0}", eolPos);
|
|
|
|
msrmntTimeStamp = StateMachine.Time;
|
|
int balanceIdNr = (int)msrmntState - (int)MsrmntState.Busy;
|
|
|
|
/// 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));
|
|
}
|
|
|
|
if (received.Length == 19 && received.Substring(0, 6).Equals("AR A \""))
|
|
{
|
|
string weightStr = received.Substring(6, 12);
|
|
while ((weightStr[0] == ' ') && (weightStr.Length > 1))
|
|
{
|
|
weightStr = weightStr.Substring(1, weightStr.Length - 1);
|
|
}
|
|
|
|
double weight = 0;
|
|
if (Utils.TryParseSDouble(weightStr, out weight))
|
|
{
|
|
Masses[balanceIdNr - 1] = weight * Balances[balanceIdNr - 1].Resolution;
|
|
log.InfoFormat("Balance({0}) Valid response=\"{1}<cr>\", Mass = {2}kg", balanceIdNr, received, Masses[balanceIdNr - 1]);
|
|
|
|
msrmntState = MsrmntState.Valid;
|
|
}
|
|
else
|
|
{
|
|
log.WarnFormat("Balance({0}) Failed response=\"{1}<cr>\", Error 1 !!!", balanceIdNr, received);
|
|
msrmntState = MsrmntState.Failed;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//Balances[balanceIdNr - 1].msrmntState = MsrmntState.Failed;
|
|
log.WarnFormat("Balance({0}) Failed response=\"{1}<cr>\", Error 2 !!!", balanceIdNr, received);
|
|
msrmntState = MsrmntState.Failed;
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>Run this device</summary>
|
|
public void RunDeviceAfter()
|
|
{
|
|
}
|
|
|
|
/// <summary>Stop this device</summary>
|
|
public void StopDevice()
|
|
{
|
|
if ((balanceCfg.DebugLevel != DebugMode.Simulate) && (balanceCfg.IdNr == 1) && (serialPort != null))
|
|
{
|
|
serialPort.Close();
|
|
serialPort = null;
|
|
}
|
|
}
|
|
|
|
public void StopDevice2() { }
|
|
|
|
/// <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 DoubleBox 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 DoubleBox 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>
|
|
/// <param name="maxSpread">Maximum spread of measurements in kg (otherwise the measurement continues)</param>
|
|
/// <param name="method">Method: false = slow (precise), true = fast (immediate)</param>
|
|
/// <returns>ReadMassAverageOp instance reference casted to IOperaton</returns>
|
|
public IOperation ReadStableMassOp(ref DoubleBox mass, int readingsCount, double maxSpread, Config.Entities.MassMethod method)
|
|
{
|
|
return new ReadStableMassOp(this, ref mass, readingsCount, maxSpread, method);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Events: BalanceDone, Error
|
|
/// </summary>
|
|
/// <param name="serialNumber">Reference to the serialNumber</param>
|
|
/// <returns>GetBalanceSNOp instance reference casted to IOperaton</returns>
|
|
public IOperation GetSerNumOp(ref string serialNumber)
|
|
{
|
|
return null;
|
|
}
|
|
}
|
|
}
|