/// /// Copyright (c) 2021 Sensus Slovensko a.s. /// using System; using System.Collections.Generic; using log4net; using TBF.BenchControl.Generic; using TBF.Boxes; using Config.Entities; namespace TBF.BenchControl.Modbus.Novus { public class Novus : ComponentBase, IDevice, GenericDevices.ITempMeter { private static readonly ILog log = LogManager.GetLogger(typeof(Novus)); public override string ToString() { return string.Format("{0}({1})", ClassName, Cfg.ToString(-1)); } const ushort SetpointAddress = 0; /// Temperature setpoint const ushort ProcessValueAddress = 1; /// Actual temperature readonly NovusCfg novusCfg; readonly GenericDevices.IModbus modbus; private float temperatureSetPoint; private bool temperatureSetPointValid; private bool temperatureSetPointRequested; public float requiredTemp { get { return novusCfg.ProcParams.RequiredTemp; } } public Novus() { } public Novus(Generic.IComponentCfg cfg, IList components) : base(cfg) { novusCfg = cfg as NovusCfg; modbus = (GenericDevices.IModbus)TbfComponents.FindComponent(cfg.ParentName, components); if (modbus == null) throw new Exception("Cannot find " + Name + " parent"); } /// Initialize this device public override void Initialize() { temperatureSetPointValid = false; temperatureSetPointRequested = false; actualTemperatureIsValid = false; if (novusCfg.DebugLevel == DebugMode.Simulate) log.FatalFormat("{0} simulated: {1}", Name, this); else log.FatalFormat("{0} initialized: {1}", Name, this); } /// Run this device public void RunDeviceBefore() { if (novusCfg.DebugLevel == DebugMode.Simulate) return; if (modbus.ReceivedTelegrams[novusCfg.ModbusAddress].Count > 0) { byte[] telegram = modbus.ReceivedTelegrams[novusCfg.ModbusAddress].Dequeue(); if (telegram.Length == 7 && telegram[1] == (byte)Function.ReadHoldingRegisters && telegram[2] == 2) { ushort value = (ushort)(256 * telegram[3] + telegram[4]); temperatureSetPoint = (float)value * 0.1f; /// setpoint in 0.1 °C temperatureSetPointValid = true; log.InfoFormat("{0} - Received current temperature setpoint = {1}", Name, temperatureSetPoint); } else if (telegram.Length == 7 && telegram[1] == (byte)Function.ReadInputRegister && telegram[2] == 2) { ushort value = (ushort)(256 * telegram[3] + telegram[4]); actualTemperature = (double)value * 0.1; /// temperature in 0.1 °C actualTemperatureIsValid = true; log.InfoFormat("{0} - Received actual temperature = {1}", Name, actualTemperature); if (novusCfg.ReservoirNr > 0 && novusCfg.ReservoirNr <= 3) { if (temperatureSetPointValid) { StateMachine.ControlBoard.SetReservoirTemp(novusCfg.ReservoirNr, temperatureSetPoint); } else { StateMachine.ControlBoard.SetReservoirTemp(novusCfg.ReservoirNr, 0); } StateMachine.ControlBoard.SetReservoirTemp(novusCfg.ReservoirNr + 1, (float)actualTemperature); } } } } /// Run this device public void RunDeviceAfter() { if (novusCfg.DebugLevel == DebugMode.Simulate) return; if (!temperatureSetPointValid && !temperatureSetPointRequested) { RequestTemperatureSetpoint(); temperatureSetPointRequested = true; } else if (temperatureSetPointValid && requiredTemp != temperatureSetPoint) { SetTemperatureSetpoint(requiredTemp); log.InfoFormat("{0} - Set new temperature setpoint = {1}", Name, requiredTemp); } else if ((StateMachine.Time % 10) == (novusCfg.ModbusAddress % 10)) /// This is to prevent overflow of the Modbus component queue for sending data { RequestActualTemperature(); } } public void StopDevice() { } public void StopDevice2() { } /// /// Set Easytherm controller to required temperature procedure parameter /// /// true = OK, false = any error public bool SetTemperatureSetpoint() { return SetTemperatureSetpoint(requiredTemp); } /// /// Set Easytherm controller to specified temperature /// /// Temperature in °C /// true = OK, false = any error public bool SetTemperatureSetpoint(float temperature) { if (temperature != 0 && (temperature < 5 || temperature > 95.0f)) { return false; /// required temperature out of range } if (temperatureSetPointValid && (temperature == temperatureSetPoint)) { return true; /// already set to the required temperature } if (temperature != 0) /// temperature==0 disables sending the setpoint { ushort usTempSetpoint = Convert.ToUInt16(Math.Round(10 * temperature)); /// setpoint in 0.1 °C ushort destAddress = SetpointAddress; /// SendValueToEasytherm(Function.PresetSingleRegister, destAddress, usTempSetpoint); } temperatureSetPoint = temperature; temperatureSetPointValid = true; return true; } public void RequestTemperatureSetpoint() { SendValueToEasytherm(Function.ReadHoldingRegisters, SetpointAddress, (ushort)1); /// rel.address=2, 1 word } public void RequestActualTemperature() { SendValueToEasytherm(Function.ReadInputRegister, ProcessValueAddress, (ushort)1); // rel.address=x, 1 word } void SendValueToEasytherm(Function function, ushort address, ushort value) { byte[] msg = new byte[8]; msg[0] = novusCfg.ModbusAddress; msg[1] = (byte)function; msg[2] = (byte)(address >> 8); /// Relative address Hi msg[3] = (byte)(address & 0xFF); /// Relative address Lo msg[4] = (byte)(value >> 8); /// Data Hi msg[5] = (byte)(value & 0xFF); /// Data Lo modbus.SendMessage(msg); } /// /// Operations, etc. /// double actualTemperature; bool actualTemperatureIsValid; public double ReadTemperature() { return actualTemperature; } /// /// Events: SetOpututsDone, Error /// /// SetOutputsOp instance reference casted to IOperaton public IOperation SetRequiredTemperatureOp() { return new SetRequiredTemperatureOp(this); } /// /// Events: SetOpututsDone, Error /// /// Reference to a variable for the pressure in Bar /// SetOutputsOp instance reference casted to IOperaton public IOperation SetTemperatureOp(float temperature) { return new SetTemperatureOp(this, temperature); } public IOperation ReadTempOp(ref DoubleBox temp) { return new ReadTempOp(this, ref temp); } public IOperation ReadTempOp(ref DoubleBox temp, Event eventDone) { return new ReadTempOp(this, ref temp, eventDone); } } }