/// /// 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; } } }