/// /// Copyright (c) 2015 Sensus Metering Systems /// Author: Milan Hanajik /// 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.Boxes; namespace TBF.BenchControl.MettlerToledo.Multi { /// /// 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.Multi.Balance({0})", Cfg.ToString(1)); } protected readonly BalanceCfg balanceCfg; protected static BalanceCfg balance1Cfg; /// /// 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 static SerialPort serialPort; protected static StringBuilder stringBuilder; public int BalanceNr { get { return balanceNr; } } public int IdNr { get { return balanceCfg.IdNr; } } public float Capacity { get { return balanceCfg.Capacity; } } public float Resolution { get { return balanceCfg.Resolution; } } public int EmptyTimeSec { get { return balanceCfg.EmptyTimeSec; } } /// The state of the mass measurement public MsrmntState MsrmntState { get { return msrmntState; } } protected static MsrmntState msrmntState; /// The mass after StartMassMeasurement() when MsrmntState == MsrmntState.Valid public float Mass { get { return Masses[balanceNr]; } } /// 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 public BalanceDev(BalanceCfg cfg) : base(cfg) { balanceCfg = cfg; if (cfg.IdNr == 1) balance1Cfg = 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 == 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 bool IsEmpty() { return (Mass <= balanceCfg.Empty); } public bool IsEmpty(float mass) { return (mass <= balanceCfg.Empty); } /// /// Get a stable mass measurement /// 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(); string serialData = string.Format("AR WT0{0}01\r\n", scaleId); serialPort.Write(serialData); log.InfoFormat("{0} - GetStableMassMeasurement() ... \"{1}\" sent", name, serialData); return true; } /// /// Get an immediate mass measurement /// 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(); string serialData = string.Format("AR WT0{0}01\r\n", scaleId); serialPort.Write(serialData); log.InfoFormat("{0} - GetImmediateMassMeasurement() ... \"{1}\" sent", name, serialData); return true; } /// /// Parse characters received from the serial port /// 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; 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); } float weight = 0; if (Utils.TryParseSFloat(weightStr, out weight)) { Masses[balanceIdNr - 1] = weight * Balances[balanceIdNr - 1].Resolution; log.InfoFormat("Balance({0}) Valid response=\"{1}\", Mass = {2}kg", balanceIdNr, received, Masses[balanceIdNr - 1]); msrmntState = MsrmntState.Valid; } else { log.WarnFormat("Balance({0}) Failed response=\"{1}\", Error 1 !!!", balanceIdNr, received); msrmntState = MsrmntState.Failed; } } else { //Balances[balanceIdNr - 1].msrmntState = MsrmntState.Failed; log.WarnFormat("Balance({0}) Failed response=\"{1}\", Error 2 !!!", balanceIdNr, received); msrmntState = MsrmntState.Failed; } } } /// Run this device public void RunDeviceAfter() { } /// Stop this device public void StopDevice() { if (balanceCfg.IdNr == 1 && 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); } } }