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 { /// /// Mettler Toledo ICS4_5 Weighting terminal connected via serial interface (RS232) /// /// /// Implemented and tested on 15.10.2013 by Milan Hanajik /// public class BalanceDev : ComponentBase, IDevice, GenericDevices.IBalance { /// /// 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.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 float[] Masses; /// protected int balanceNr; /// 0-based balance number protected SerialPort serialPort; protected StringBuilder stringBuilder; public int BalanceNr { get { return balanceNr; } } public float Capacity { get { return balanceCfg.Capacity; } } public int EmptyTimeSec { get { return balanceCfg.EmptyTimeSec; } } /// The state of the mass measurement public MsrmntState MsrmntState { get { return msrmntState; } } protected MsrmntState msrmntState; /// The mass after StartMassMeasurement() when MsrmntState == MsrmntState.Valid public float Mass { get { return mass; } } protected float mass; /// The time stamp after StartMassMeasurement() when MsrmntState == MsrmntState.Valid public int MsrmntTimeStamp { get { return msrmntTimeStamp; } } protected int msrmntTimeStamp; /// /// Constructor /// /// Balance properties /// A list of components loaded so far public BalanceDev(BalanceCfg cfg, IList components) : base(cfg) { 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; } log.Debug(this.ToString()); } public 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(); log.FatalFormat("Successfully initialized device {0}", ToString()); } 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"); } /// /// Get a stable mass measurement /// 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"); } /// /// Get an immediate mass measurement /// 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"); } /// 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 == 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; } } /// /// Parse characters received from the serial port /// public virtual void RunDeviceBefore() { if (balanceCfg.DebugLevel == Entities.DebugMode.Simulate) { mass = TestBenchSim.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}\"", received); return; } else if (fields.Length < 2) { msrmntState = MsrmntState.Failed; log.WarnFormat("Balance response = \"{0}\", 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}\"", received); return; } else { msrmntState = MsrmntState.Failed; log.WarnFormat("Balance response = \"{0}\", error !!!", received); } } else if (fields[0].Equals("S")) /// Stable mass: field[1] = 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}\", 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}\", Mass = {1}", received, mass); } else { msrmntState = MsrmntState.Failed; log.WarnFormat("Balance response = \"{0}\", 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] = 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}\", 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}\", Mass = {1}", received, mass); } else { msrmntState = MsrmntState.Failed; log.WarnFormat("Balance response = \"{0}\", 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}\", Tara = {1}", received, mass); } else { msrmntState = MsrmntState.Failed; log.WarnFormat("Balance response = \"{0}\", Overload or and unexpected error !!!", received); } } else { msrmntState = MsrmntState.Failed; log.WarnFormat("Balance response = \"{0}\", Unexpected error !!!", received); } } } /// Run this device public void RunDeviceAfter() { } /// Stop this device public void StopDevice() { if (serialPort != null) serialPort.Close(); } /// /// Events: BalanceDone, Error /// /// Reference to a variable for 'tara' in kg /// TaringOp instance reference casted to IOperaton public IOperation TaringOp(ref FloatBox 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 FloatBox 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) /// ReadMassAverageOp instance reference casted to IOperaton public IOperation ReadStableMassOp(ref FloatBox mass, int readingsCount) { return new ReadStableMassOp(this, ref mass, readingsCount); } } }