Modbus.Novus --> Modbus.TempControl.Novus, Common.Modbus: string[] ComponentNames, etc. (builds OK)

This commit is contained in:
Milan Hanajik 2022-05-16 14:59:05 +02:00
parent 80d347ef54
commit 7f3cc27138
20 changed files with 841 additions and 928 deletions

View File

@ -0,0 +1,19 @@
///
/// Copyright (c) 2022 Sensus Slovensko a.s.
///
using System;
namespace TBF.Rig.GenericDevices
{
public interface ITempControl : ITempMeter
{
/// <summary>
/// Set temperature controller setpoint to specified value in °C
/// </summary>
/// <param name="reqTempC">Temperature in °C</param>
/// <returns>true = OK, false = any error</returns>
bool SetTemperatureSetpoint(double temperatureC);
IOperation SetTemperatureSetpointOp(double temperatureC);
}
}

View File

@ -30,16 +30,16 @@ namespace TBF.Rig.Modbus.Common
DateTime lastSerialPortWrite;
bool initialRunDeviceCommComplete;
public Queue<byte[]>[] ReceivedTelegrams
{
get { return receivedTelegrams; }
}
Queue<byte[]>[] receivedTelegrams;
Queue<byte[]> telegramsToSend;
public Modbus() { }
public Queue<byte[]>[] ReceivedTelegrams { get { return receivedTelegrams; } }
Queue<byte[]>[] receivedTelegrams;
public string[] ComponentNames { get { return componentNames; } }
string[] componentNames;
public Modbus() { }
/// <summary>
/// Ambient temperature / humidity / pressure meter 'Greco' connected via serial interface (RS232)

View File

@ -1,24 +0,0 @@
///
/// Copyright (c) 2021 Sensus Slovensko a.s.
///
using System.Collections.Generic;
using TBF.Rig.Generic;
namespace TBF.Rig.Modbus.Novus
{
public class Factory : IComponentFactory
{
public string ClassName { get { return GetType().Namespace.Substring(8); } }
public IComponent DummyComponent() { return new Novus(); }
public IComponent GetComponent(IComponentCfg cfg, IList<IComponent> components) { return new Novus(cfg, components); }
public IComponentCfg DefaultConfig() { return new NovusCfg(this); }
public IComponentCfg CmpntCfgFromCmpntEntity(Config.Entities.Component component)
{
return ComponentCfgBase.CreateFromDbEntity(NovusCfg.Serializer, component, this);
}
}
}

View File

@ -1,227 +0,0 @@
///
/// Copyright (c) 2021 Sensus Slovensko a.s.
///
using System;
using System.Collections.Generic;
using log4net;
using Common;
using TBF.Rig.Generic;
using TBF.Boxes;
namespace TBF.Rig.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<Generic.IComponent> 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");
}
/// <summary>Initialize this device</summary>
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);
}
/// <summary>Run this device</summary>
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);
}
}
}
}
/// <summary>Run this device</summary>
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() { }
/// <summary>
/// Set Easytherm controller to required temperature procedure parameter
/// </summary>
/// <returns>true = OK, false = any error</returns>
public bool SetTemperatureSetpoint()
{
return SetTemperatureSetpoint(requiredTemp);
}
/// <summary>
/// Set Easytherm controller to specified temperature
/// </summary>
/// <param name="temperature">Temperature in °C</param>
/// <returns>true = OK, false = any error</returns>
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;
}
/// <summary>
/// Events: SetOpututsDone, Error
/// </summary>
/// <returns>SetOutputsOp instance reference casted to IOperaton</returns>
public IOperation SetRequiredTemperatureOp()
{
return new SetRequiredTemperatureOp(this);
}
/// <summary>
/// Events: SetOpututsDone, Error
/// </summary>
/// <param name="temperature">Reference to a variable for the pressure in Bar</param>
/// <returns>SetOutputsOp instance reference casted to IOperaton</returns>
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);
}
}
}

View File

@ -1,55 +0,0 @@
///
/// Copyright (c) 2021 Sensus Slovensko a.s.
///
using System.Collections.Generic;
using System.Xml.Serialization;
using Common;
using TBF.Rig.Generic;
namespace TBF.Rig.Modbus.Novus
{
public class NovusCfg : ComponentCfgBase, Generic.IChildComponentCfg
{
public static XmlSerializer Serializer = XmlSerializer.FromTypes(new[] { typeof(NovusCfg) })[0];
public override XmlSerializer GetSerializer() { return Serializer; }
public IComponentCfgCtrl GetControl(IList<Config.Entities.Component> cmpntEntities) { return new NovusCfgCtrl(); }
///
/// Serialized parameters
///
public byte ModbusAddress; /// 1..254
public int ReservoirNr;
/// <summary> Procedure parameters </summary>
[XmlIgnore]
public ProcParams ProcParams;
public override IParamsProvider GetRuntimeProcParamsProvider() { return ProcParams; }
public override IParamsProvider CreateProcParamsProvider() { return new ProcParams(true); }
/// Private parameterless constructor invoked by all other (public) constructors
NovusCfg()
{
Name = "Novus";
ParentName = "Modbus";
ModbusAddress = 1;
ProcParams = CreateProcParamsProvider() as ProcParams;
}
public NovusCfg(IComponentFactory factory)
: this()
{
this.Factory = factory;
}
public string ToString(int i)
{
return string.Format("Name={0}, ModbusAddr={1}, Parent={2}",
Name,
ModbusAddress,
(string.IsNullOrEmpty(ParentName) ? "-" : ParentName));
}
}
}

View File

@ -1,114 +0,0 @@
///
/// Copyright (c) 2021 Sensus Slovensko a.s.
///
using System;
using System.Windows.Forms;
using Common;
using Config.Entities;
using TBF.Rig.Generic;
using TBF.UI.Bench.Components;
namespace TBF.Rig.Modbus.Novus
{
public partial class NovusCfgCtrl : UserControl, IComponentCfgCtrl
{
ComponentParametersDlg parent;
public bool ShowMore { get { return false; } }
NovusCfg config;
public IComponentCfg Config
{
get { return config as IComponentCfg; }
set
{
config = value as NovusCfg;
Redraw();
}
}
public NovusCfgCtrl()
{
InitializeComponent();
}
private void QuidoRSCfgCtrl_Load(object sender, EventArgs e)
{
parent = ParentForm as ComponentParametersDlg;
if (parent == null) return;
if (parent.CmpntEntities != null)
{
foreach (var cmpnt in parent.CmpntEntities)
{
if (TbfComponents.CmpntFactoryFromClassName(cmpnt.ClassName) is Modbus.Common.Factory)
{
parentNameComboBox.Items.Add(cmpnt.Name);
}
}
}
Redraw();
}
public void Closing()
{
}
void Redraw()
{
if (config == null) return; /// Control was not loaded, settings were not changed
componentNameLabel.Text = config.Factory.ClassName;
nameTextBox.Text = config.Name;
parentNameComboBox.Text = string.IsNullOrEmpty(config.ParentName) ? "---" : config.ParentName;
modbusAddressTextBox.Text = config.ModbusAddress.ToString();
reservoirNrTextBox.Text = config.ReservoirNr.ToString();
}
public void Unlock()
{
nameTextBox.Enabled = true;
parentNameComboBox.Enabled = true;
modbusAddressTextBox.Enabled = true;
reservoirNrTextBox.Enabled = true;
}
public CfgUpdateFlags VerifyCfg(ref string message)
{
CfgUpdateFlags flags = CfgUpdateFlags.None;
int dummy;
if (!parentNameComboBox.Items.Contains(parentNameComboBox.Text))
{
flags |= CfgUpdateFlags.Error;
message += Environment.NewLine + "Invalid 'Parent Name'";
}
if (!int.TryParse(modbusAddressTextBox.Text, out dummy) || dummy < 0 || dummy > 255)
{
flags |= CfgUpdateFlags.Error;
message += Environment.NewLine + "'Modbus Address' should be between 0 and 255";
}
if (!int.TryParse(reservoirNrTextBox.Text, out dummy) || dummy < 0 || dummy > 3)
{
flags |= CfgUpdateFlags.Error;
message += Environment.NewLine + "'Reservoir Nr.' should be between 0 and 3";
}
return flags;
}
public CfgUpdateFlags UpdateCfg()
{
CfgUpdateFlags flags = CfgUpdateFlags.RestartRqrd;
if (config == null) return CfgUpdateFlags.Error; /// Control was not loaded, settings were not changed
config.Name = nameTextBox.Text;
config.ParentName = parentNameComboBox.Text.Equals("---") ? string.Empty : parentNameComboBox.Text;
config.ModbusAddress = (byte)int.Parse(modbusAddressTextBox.Text);
config.ReservoirNr = int.Parse(reservoirNrTextBox.Text);
return flags;
}
}
}

View File

@ -1,156 +0,0 @@
///
/// Copyright (c) 2021 Sensus Slovensko a.s.
///
namespace TBF.Rig.Modbus.Novus
{
partial class NovusCfgCtrl
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Component Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.parentNameLabel = new System.Windows.Forms.Label();
this.nameTextBox = new System.Windows.Forms.TextBox();
this.nameLabel = new System.Windows.Forms.Label();
this.componentNameLabel = new System.Windows.Forms.Label();
this.modbusAddressLabel = new System.Windows.Forms.Label();
this.modbusAddressTextBox = new System.Windows.Forms.TextBox();
this.parentNameComboBox = new System.Windows.Forms.ComboBox();
this.reservoirNrTextBox = new System.Windows.Forms.TextBox();
this.reservoirNrLabel = new System.Windows.Forms.Label();
this.SuspendLayout();
//
// parentNameLabel
//
this.parentNameLabel.AutoSize = true;
this.parentNameLabel.Location = new System.Drawing.Point(28, 81);
this.parentNameLabel.Name = "parentNameLabel";
this.parentNameLabel.Size = new System.Drawing.Size(69, 13);
this.parentNameLabel.TabIndex = 3;
this.parentNameLabel.Text = "Parent Name";
//
// nameTextBox
//
this.nameTextBox.Enabled = false;
this.nameTextBox.Location = new System.Drawing.Point(138, 52);
this.nameTextBox.Name = "nameTextBox";
this.nameTextBox.Size = new System.Drawing.Size(130, 20);
this.nameTextBox.TabIndex = 2;
//
// nameLabel
//
this.nameLabel.AutoSize = true;
this.nameLabel.Location = new System.Drawing.Point(28, 55);
this.nameLabel.Name = "nameLabel";
this.nameLabel.Size = new System.Drawing.Size(35, 13);
this.nameLabel.TabIndex = 1;
this.nameLabel.Text = "Name";
//
// componentNameLabel
//
this.componentNameLabel.AutoSize = true;
this.componentNameLabel.Location = new System.Drawing.Point(135, 28);
this.componentNameLabel.Name = "componentNameLabel";
this.componentNameLabel.Size = new System.Drawing.Size(83, 13);
this.componentNameLabel.TabIndex = 0;
this.componentNameLabel.Text = "ComonentName";
//
// modbusAddressLabel
//
this.modbusAddressLabel.AutoSize = true;
this.modbusAddressLabel.Location = new System.Drawing.Point(28, 108);
this.modbusAddressLabel.Name = "modbusAddressLabel";
this.modbusAddressLabel.Size = new System.Drawing.Size(86, 13);
this.modbusAddressLabel.TabIndex = 7;
this.modbusAddressLabel.Text = "Modbus Address";
//
// modbusAddressTextBox
//
this.modbusAddressTextBox.Enabled = false;
this.modbusAddressTextBox.Location = new System.Drawing.Point(138, 105);
this.modbusAddressTextBox.Name = "modbusAddressTextBox";
this.modbusAddressTextBox.Size = new System.Drawing.Size(46, 20);
this.modbusAddressTextBox.TabIndex = 8;
//
// parentNameComboBox
//
this.parentNameComboBox.Enabled = false;
this.parentNameComboBox.FormattingEnabled = true;
this.parentNameComboBox.Location = new System.Drawing.Point(138, 78);
this.parentNameComboBox.Name = "parentNameComboBox";
this.parentNameComboBox.Size = new System.Drawing.Size(130, 21);
this.parentNameComboBox.TabIndex = 4;
//
// reservoirNrTextBox
//
this.reservoirNrTextBox.Enabled = false;
this.reservoirNrTextBox.Location = new System.Drawing.Point(138, 130);
this.reservoirNrTextBox.Name = "reservoirNrTextBox";
this.reservoirNrTextBox.Size = new System.Drawing.Size(46, 20);
this.reservoirNrTextBox.TabIndex = 10;
//
// reservoirNrLabel
//
this.reservoirNrLabel.AutoSize = true;
this.reservoirNrLabel.Location = new System.Drawing.Point(28, 133);
this.reservoirNrLabel.Name = "reservoirNrLabel";
this.reservoirNrLabel.Size = new System.Drawing.Size(67, 13);
this.reservoirNrLabel.TabIndex = 9;
this.reservoirNrLabel.Text = "Reservoir nr.";
//
// EasythermCfgCtrl
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.Controls.Add(this.reservoirNrTextBox);
this.Controls.Add(this.reservoirNrLabel);
this.Controls.Add(this.parentNameComboBox);
this.Controls.Add(this.modbusAddressTextBox);
this.Controls.Add(this.modbusAddressLabel);
this.Controls.Add(this.parentNameLabel);
this.Controls.Add(this.nameTextBox);
this.Controls.Add(this.nameLabel);
this.Controls.Add(this.componentNameLabel);
this.Name = "EasythermCfgCtrl";
this.Size = new System.Drawing.Size(300, 200);
this.Load += new System.EventHandler(this.QuidoRSCfgCtrl_Load);
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private System.Windows.Forms.Label parentNameLabel;
private System.Windows.Forms.TextBox nameTextBox;
private System.Windows.Forms.Label nameLabel;
private System.Windows.Forms.Label componentNameLabel;
private System.Windows.Forms.Label modbusAddressLabel;
private System.Windows.Forms.TextBox modbusAddressTextBox;
private System.Windows.Forms.ComboBox parentNameComboBox;
private System.Windows.Forms.TextBox reservoirNrTextBox;
private System.Windows.Forms.Label reservoirNrLabel;
}
}

View File

@ -1,120 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

View File

@ -1,140 +0,0 @@
///
/// Copyright (c) 2021 Sensus Slovensko a.s.
///
using System.IO;
using System.Xml.Serialization;
using Common;
using Config.Entities;
using TBF.Rig.Generic;
using TBF.Resources;
namespace TBF.Rig.Modbus.Novus
{
public class ProcParams : ProcedureParamsBase, IParamsProvider, IProcedureParams
{
public static XmlSerializer Serializer = XmlSerializer.FromTypes(new[] { typeof(ProcParams) })[0];
public override XmlSerializer GetSerializer() { return Serializer; }
public float RequiredTemp; /// [°C] target temperature for a temperature controller or 0=not communicated to the controller
public override void InitializeAll()
{
RequiredTemp = 0.0f;
}
string[] paramNames = new string[]
{
Strings.Required_temperature_C,
};
public override string ParamName(int i) { return paramNames[i]; }
public override int ParamsCount() { return paramNames.Length; }
public override string ToString(int i)
{
switch (i)
{
case 0: return (RequiredTemp == 0) ? "---" : RequiredTemp.ToString();
default: return string.Empty;
}
}
public CfgUpdateFlags UpdateParam(int i, string strValue)
{
switch (i)
{
case 0:
if (strValue == "-" || strValue == "--" || strValue == "---")
{
RequiredTemp = 0;
}
else
{
RequiredTemp = Utils.ParseUFloat(strValue);
}
return CfgUpdateFlags.None;
default:
return CfgUpdateFlags.None;
}
}
/// <summary>
/// Verifiy the string representation of the parameter
/// </summary>
/// <param name="i">Parameter ID</param>
/// <param name="strValue">String representation of the parameter</param>
/// <param name="message">In case false is returned this is the error messsage to be displayed</param>
/// <returns>true = parameter OK, false = parameter NOK</returns>
public bool ValidateParam(int i, string strValue, out string message)
{
message = string.Empty;
float temp;
switch (i)
{
case 0: /// T_hot_heating
if (strValue == "-" || strValue == "--" || strValue == "---" ||
(Utils.TryParseUFloat(strValue, out temp) && (temp == 0 || (temp >= 5 && temp <= 95.0f))))
{
return true;
}
break;
default:
message = "Invalid index";
return false;
}
message = ParamName(i) + " is invalid";
return false;
}
void CopyContentTo(ProcParams prms)
{
prms.RequiredTemp = this.RequiredTemp;
}
public IParamsProvider Clone()
{
ProcParams pars = new ProcParams();
CopyContentTo(pars);
return pars;
}
public override void UpdateFromDbEntity(ComponentProcedure dbEntity)
{
if (dbEntity == null) return;
try
{
ProcParams tmp = Serializer.Deserialize(new StringReader(dbEntity.Parameters)) as ProcParams;
procedureParamsEntity = dbEntity;
componentName = dbEntity.CmpntName;
procedure = dbEntity.Procedure;
if (tmp != null) tmp.CopyContentTo(this);
}
catch
{
}
}
/// <summary>
/// Parameterless constructor initializes the parameters
/// </summary>
public ProcParams()
{
}
public ProcParams(bool initialize)
{
if (initialize) InitializeAll();
}
public ProcParams(ComponentProcedure procParamsEntity, string componentName, Procedure procedure)
{
this.procedureParamsEntity = procParamsEntity;
this.componentName = componentName;
this.procedure = procedure;
}
}
}

View File

@ -1,55 +0,0 @@
///
/// Copyright (c) 2021 Sensus Slovensko a.s.
///
using System;
using log4net;
using TBF.Boxes;
namespace TBF.Rig.Modbus.Novus
{
public class SetRequiredTemperatureOp : IOperation
{
private static readonly ILog log = LogManager.GetLogger(typeof(SetRequiredTemperatureOp));
public override string ToString() { return string.Format("SetRequiredTemperatureOp({0})", eventDone); }
/// Set by the constructor
readonly Novus easytherm;
readonly Event eventDone;
/// <summary>
/// Events: PressureInDone, PressureOutDone or Error
/// </summary>
/// <param name="easytherm">Pressure meter reference</param>
/// <param name="pressure">Reference to the measured pressure variable, value is in bar</param>
/// <param name="eventDone">Event to be returned by Run() when completed OK</param>
public SetRequiredTemperatureOp(Novus easytherm, Event eventDone)
{
if (easytherm == null) throw new ArgumentNullException("easytherm");
this.easytherm = easytherm;
this.eventDone = eventDone;
log.Debug(this.ToString());
}
public SetRequiredTemperatureOp(Novus easytherm)
: this(easytherm, Event.ConditionMet)
{
}
/// <summary>Start this operation</summary>
public void Start()
{
easytherm.SetTemperatureSetpoint();
}
/// <summary>Run this operation</summary>
public Event Run()
{
return eventDone;
}
/// <summary>Stop this operation</summary>
public void Stop()
{
}
}
}

View File

@ -0,0 +1,25 @@
///
/// Copyright (c) 2022 Sensus Slovensko a.s.
///
using System.Collections.Generic;
using TBF.Rig.Generic;
namespace TBF.Rig.Modbus.TempControl.Novus
{
public class Factory : IComponentFactory
{
public string ClassName { get { return GetType().Namespace.Substring(8); } }
public override string ToString() { return ClassName; }
public IComponent DummyComponent() { return new TControl(); }
public IComponent GetComponent(IComponentCfg cfg, IList<IComponent> components) { return new TControl(cfg, components); }
public IComponentCfg DefaultConfig() { return new TControlCfg(this); }
public IComponentCfg CmpntCfgFromCmpntEntity(Config.Entities.Component component)
{
return ComponentCfgBase.CreateFromDbEntity(TControlCfg.Serializer, component, this);
}
}
}

View File

@ -0,0 +1,167 @@
///
/// Copyright (c) 2022 Sensus Slovensko a.s.
///
using System.IO;
using System.Xml.Serialization;
using Common;
using Config.Entities;
using TBF.Resources;
using TBF.Rig.Generic;
using TBF.Rig.Sequences;
namespace TBF.Rig.Modbus.TempControl.Novus
{
public class ProcParams : ProcedureParamsBase, IParamsProvider, IProcedureParams
{
public static XmlSerializer Serializer = XmlSerializer.FromTypes(new[] { typeof(ProcParams) })[0];
public override XmlSerializer GetSerializer() { return Serializer; }
public double Temp_A; /// [°C] Target temperature for a tempe. controller or 0=not communicated to the controller
public double Temp_B; /// [°C] Target temperature for a tempe. controller or 0=not communicated to the controller
public double Temp_C; /// [°C] Target temperature for a tempe. controller or 0=not communicated to the controller
public double Temp_D; /// [°C] Target temperature for a tempe. controller or 0=not communicated to the controller
public double Temp_E; /// [°C] Target temperature for a tempe. controller or 0=not communicated to the controller
public override void InitializeAll()
{
Temp_A = 10.0;
Temp_B = 20.0;
Temp_C = 30.0;
Temp_D = 40.0;
Temp_E = 50.0;
}
string[] paramNames = new string[]
{
string.Format("{0} A [{1}]", Strings.Temperature, ProcessData.TempUnit.ToDescription()),
string.Format("{0} B [{1}]", Strings.Temperature, ProcessData.TempUnit.ToDescription()),
string.Format("{0} C [{1}]", Strings.Temperature, ProcessData.TempUnit.ToDescription()),
string.Format("{0} D [{1}]", Strings.Temperature, ProcessData.TempUnit.ToDescription()),
string.Format("{0} E [{1}]", Strings.Temperature, ProcessData.TempUnit.ToDescription()),
};
public override string ParamName(int i) { return paramNames[i]; }
public override int ParamsCount() { return paramNames.Length; }
public override string ToString(int i)
{
switch (i)
{
case 0: return (Temp_A == 0) ? "---" : Units.ConvertTo(ProcessData.TempUnit, Temp_A).ToString();
case 1: return (Temp_B == 0) ? "---" : Units.ConvertTo(ProcessData.TempUnit, Temp_B).ToString();
case 2: return (Temp_C == 0) ? "---" : Units.ConvertTo(ProcessData.TempUnit, Temp_C).ToString();
case 3: return (Temp_D == 0) ? "---" : Units.ConvertTo(ProcessData.TempUnit, Temp_D).ToString();
case 4: return (Temp_E == 0) ? "---" : Units.ConvertTo(ProcessData.TempUnit, Temp_E).ToString();
default: return string.Empty;
}
}
public CfgUpdateFlags UpdateParam(int i, string str)
{
bool isDash = (str == "-" || str == "--" || str == "---");
switch (i)
{
case 0:
Temp_A = isDash ? 0 : Units.ConvertFrom(ProcessData.TempUnit, Utils.ParseUDouble(str));
return CfgUpdateFlags.None;
case 1:
Temp_B = isDash ? 0 : Units.ConvertFrom(ProcessData.TempUnit, Utils.ParseUDouble(str));
return CfgUpdateFlags.None;
case 2:
Temp_C = isDash ? 0 : Units.ConvertFrom(ProcessData.TempUnit, Utils.ParseUDouble(str));
return CfgUpdateFlags.None;
case 3:
Temp_D = isDash ? 0 : Units.ConvertFrom(ProcessData.TempUnit, Utils.ParseUDouble(str));
return CfgUpdateFlags.None;
case 4:
Temp_E = isDash ? 0 : Units.ConvertFrom(ProcessData.TempUnit, Utils.ParseUDouble(str));
return CfgUpdateFlags.None;
default:
return CfgUpdateFlags.None;
}
}
/// <summary>
/// Verifiy the string representation of the parameter
/// </summary>
/// <param name="i">Parameter ID</param>
/// <param name="str">String representation of the parameter</param>
/// <param name="message">In case false is returned this is the error messsage to be displayed</param>
/// <returns>true = parameter OK, false = parameter NOK</returns>
public bool ValidateParam(int i, string str, out string message)
{
message = string.Empty;
double ddummy;
switch (i)
{
case 0: /// T_hot_heating
if (str == "-" || str == "--" || str == "---" ||
(Utils.TryParseUDouble(str, out ddummy) && (ddummy == 0 || (ddummy >= 5 && ddummy <= 95.0))))
{
return true;
}
break;
default:
message = "Invalid index";
return false;
}
message = ParamName(i) + " is invalid";
return false;
}
void CopyContentTo(ProcParams prms)
{
prms.Temp_A = Temp_A;
prms.Temp_B = Temp_B;
prms.Temp_C = Temp_C;
prms.Temp_D = Temp_D;
prms.Temp_E = Temp_E;
}
public IParamsProvider Clone()
{
ProcParams pars = new ProcParams();
CopyContentTo(pars);
return pars;
}
public override void UpdateFromDbEntity(ComponentProcedure dbEntity)
{
if (dbEntity == null) return;
try
{
ProcParams tmp = Serializer.Deserialize(new StringReader(dbEntity.Parameters)) as ProcParams;
procedureParamsEntity = dbEntity;
componentName = dbEntity.CmpntName;
procedure = dbEntity.Procedure;
if (tmp != null) tmp.CopyContentTo(this);
}
catch
{
}
}
/// <summary>
/// Parameterless constructor initializes the parameters
/// </summary>
public ProcParams()
{
}
public ProcParams(bool initialize)
{
if (initialize) InitializeAll();
}
public ProcParams(ComponentProcedure procParamsEntity, string componentName, Procedure procedure)
{
this.procedureParamsEntity = procParamsEntity;
this.componentName = componentName;
this.procedure = procedure;
}
}
}

View File

@ -1,11 +1,11 @@
///
/// Copyright (c) 2021 Sensus Slovensko a.s.
/// Copyright (c) 2022 Sensus Slovensko a.s.
///
using System;
using log4net;
using TBF.Boxes;
namespace TBF.Rig.Modbus.Novus
namespace TBF.Rig.Modbus.TempControl.Novus
{
public class ReadTempOp : IOperation
{
@ -13,7 +13,7 @@ namespace TBF.Rig.Modbus.Novus
public override string ToString() { return string.Format("ReadTempOp({0},.,{1})", novus.Name, eventDone); }
/// Set by the constructor
Novus novus;
TControl novus;
Event eventDone;
DoubleBox temp;
@ -23,7 +23,7 @@ namespace TBF.Rig.Modbus.Novus
/// <param name="easytherm">TempMeter reference</param>
/// <param name="temp">Reference to the variable, value is in degree Celsius</param>
/// <param name="eventDone">Event to be returned by Run() when completed OK</param>
public ReadTempOp(Novus easytherm, ref DoubleBox temp, Event eventDone)
public ReadTempOp(TControl easytherm, ref DoubleBox temp, Event eventDone)
{
if (easytherm == null) throw new ArgumentNullException("easytherm");
this.novus = easytherm;
@ -32,7 +32,7 @@ namespace TBF.Rig.Modbus.Novus
log.Debug(this.ToString());
}
public ReadTempOp(Novus tempMeter, ref DoubleBox temp)
public ReadTempOp(TControl tempMeter, ref DoubleBox temp)
: this(tempMeter, ref temp, Event.TempDone)
{
}

View File

@ -1,11 +1,11 @@
///
/// Copyright (c) 2021 Sensus Slovensko a.s.
/// Copyright (c) 2022 Sensus Slovensko a.s.
///
using System;
using log4net;
using TBF.Boxes;
namespace TBF.Rig.Modbus.Novus
namespace TBF.Rig.Modbus.TempControl.Novus
{
public class SetTemperatureOp : IOperation
{
@ -13,7 +13,7 @@ namespace TBF.Rig.Modbus.Novus
public override string ToString() { return string.Format("SetTemperatureOp({0},{1})", temperatureSetpoint, eventDone); }
/// Set by the constructor
readonly Novus easytherm;
readonly TControl easytherm;
readonly Event eventDone;
float temperatureSetpoint;
@ -23,7 +23,7 @@ namespace TBF.Rig.Modbus.Novus
/// <param name="easytherm">Pressure meter reference</param>
/// <param name="pressure">Reference to the measured pressure variable, value is in bar</param>
/// <param name="eventDone">Event to be returned by Run() when completed OK</param>
public SetTemperatureOp(Novus easytherm, float temperatureSetpoint, Event eventDone)
public SetTemperatureOp(TControl easytherm, float temperatureSetpoint, Event eventDone)
{
if (easytherm == null) throw new ArgumentNullException("easytherm");
this.easytherm = easytherm;
@ -32,7 +32,7 @@ namespace TBF.Rig.Modbus.Novus
log.Debug(this.ToString());
}
public SetTemperatureOp(Novus easytherm, float temperatureSetpoint)
public SetTemperatureOp(TControl easytherm, float temperatureSetpoint)
: this(easytherm, temperatureSetpoint, Event.SetOutputsDone)
{
}

View File

@ -0,0 +1,56 @@
///
/// Copyright (c) 2022 Sensus Slovensko a.s.
///
using System;
using log4net;
using TBF.Boxes;
namespace TBF.Rig.Modbus.TempControl.Novus
{
public class SetTemperatureSetpointOp : IOperation
{
private static readonly ILog log = LogManager.GetLogger(typeof(SetTemperatureSetpointOp));
public override string ToString() { return string.Format("SetTemperatureSetpointOp({0})", eventDone); }
/// Set by the constructor
readonly TControl tempControl;
readonly double temperature;
readonly Event eventDone;
/// <summary>
/// Events: PressureInDone, PressureOutDone or Error
/// </summary>
/// <param name="tempControl">Temp. controller reference</param>
/// <param name="eventDone">Event to be returned by Run() when completed OK</param>
public SetTemperatureSetpointOp(TControl tempControl, double temperature, Event eventDone)
{
if (tempControl == null) throw new ArgumentNullException("tempControl");
this.tempControl = tempControl;
this.temperature = temperature;
this.eventDone = eventDone;
log.Debug(this.ToString());
}
public SetTemperatureSetpointOp(TControl tempControl, double temperature)
: this(tempControl, temperature, Event.ConditionNotMet)
{
}
/// <summary>Start this operation</summary>
public void Start()
{
tempControl.SetTemperatureSetpoint(temperature);
}
/// <summary>Run this operation</summary>
public Event Run()
{
return eventDone;
}
/// <summary>Stop this operation</summary>
public void Stop()
{
}
}
}

View File

@ -0,0 +1,344 @@
///
/// Copyright (c) 2022 Sensus Slovensko a.s.
///
using System;
using System.Collections.Generic;
using System.Drawing;
using log4net;
using Common;
using Config.Entities;
using TBF.Boxes;
using TBF.Rig.GenericDevices;
namespace TBF.Rig.Modbus.TempControl.Novus
{
public class TControl : ComponentBase, Generic.IDevice, ITempControl, ISequenceCondition
{
private static readonly ILog log = LogManager.GetLogger(typeof(TControl));
public override string ToString() { return string.Format("{0}({1})", ClassName, Cfg.ToString(-1)); }
const UInt16 SetpointAddress = 0; /// Temperature setpoint address
const UInt16 ProcessValueAddress = 1; /// Actual temperature address
const UInt16 Status1Address = 6; /// Status word 1 address
const UInt16 FWVersionAddress = 7; /// Firmware version address
const UInt16 ControllerIdAddress = 8; /// Controller ID address
const UInt16 Status2Address = 9; /// Status word 2 address
const UInt16 Status3Address = 10; /// Status word 3 address
readonly TControlCfg novusCfg;
Common.Modbus modbus;
int ticketNumber; /// 0 .. number of devices registered for regular polling - 1
public bool MsrmntAvailable { get { return true; } }
public double MeasuredVal { get { return ReadTemperature(); } }
public double MsrdValLimLo { get { return Units.ConvertFrom(MsrdUnit, novusCfg.MsrdValLimLo); } }
public double MsrdValLimHi { get { return Units.ConvertFrom(MsrdUnit, novusCfg.MsrdValLimHi); } }
public Unit MsrdUnit { get { return novusCfg.MsrdUnit; } }
public string MsrdFormat { get { return novusCfg.MsrdFormat; } }
public string AltString { get { return string.Empty; } }
public double SetpointVal
{
get { return temperatureSetPoint; }
set { SetTemperatureSetpoint(value); }
}
///
public void IncreaseSetpoint()
{
double tempStepC = isFahrenheit ? Units.ConvertFrom(Unit.F, novusCfg.SetpStep) : novusCfg.SetpStep;
double newSetpoint;
if (novusCfg.RoundToSetpStep)
{
double roundedSetpoint = tempStepC * Math.Round(temperatureSetPoint / tempStepC);
newSetpoint = (roundedSetpoint <= temperatureSetPoint) ? (roundedSetpoint + tempStepC) : roundedSetpoint;
}
else
{
newSetpoint = temperatureSetPoint + tempStepC;
}
SetTemperatureSetpoint(Math.Min(newSetpoint, 95.0));
}
///
public void DecreaseSetpoint()
{
double tempStepC = isFahrenheit ? Units.ConvertFrom(Unit.F, novusCfg.SetpStep) : novusCfg.SetpStep;
double newSetpoint;
if (novusCfg.RoundToSetpStep)
{
double roundedSetpoint = tempStepC * Math.Round(temperatureSetPoint / tempStepC);
newSetpoint = (roundedSetpoint >= temperatureSetPoint) ? (roundedSetpoint - tempStepC) : roundedSetpoint;
}
else
{
newSetpoint = temperatureSetPoint - tempStepC;
}
SetTemperatureSetpoint(Math.Max(newSetpoint, 5.0));
}
bool novusDataValid;
bool temperatureSetPointValid { get { return novusDataValid; } }
Int16 status1;
Int16 status2;
Int16 status3;
Int16 fwVersion;
Int16 controllerId;
double temperatureSetPoint;
double actualTemperature;
bool isHeating { get { return (status2 & (1 << 11)) != 0; } } /// Output 1: Heating
bool isCooling { get { return (status2 & (1 << 12)) != 0; } } /// Output 2: Cooling
bool isFahrenheit { get { return (status2 & (1 << 9)) != 0; } }
public TControl() { }
public TControl(Generic.IComponentCfg cfg, IList<Generic.IComponent> components)
: base(cfg)
{
novusCfg = cfg as TControlCfg;
CreateConditions(true);
}
/// <summary>Initialize this device</summary>
public override void Initialize()
{
novusDataValid = false;
if (novusCfg.DebugLevel == DebugMode.Simulate)
{
actualTemperature = 22;
temperatureSetPoint = 18;
log.FatalFormat("{0} simulated: {1}", Name, this);
}
else
{
modbus = TbfComponents.FindComponent(novusCfg.ParentName) as Common.Modbus;
if (modbus == null) throw new Exception("Cannot find " + Name + " parent");
modbus.ComponentNames[novusCfg.ModbusAddress] = Name;
//ticketNumber = modbus.RegisterForPolling();
//TBF.UiBridge.Bridge.SetpointChangeHandler += delegate(object sndr, TBF.UiBridge.SetpointChangeArgs args)
//{
// if (args.Name == Name)
// {
// if (args.Increase) IncreaseSetpoint(); else DecreaseSetpoint();
// }
//};
log.FatalFormat("{0} initialized: {1}", Name, this);
}
}
/// <summary>Run this device</summary>
public void RunDeviceBefore()
{
if (DebugLevel != DebugMode.Simulate && DebugLevel != DebugMode.FailureDuringOperation)
{
while (modbus.ReceivedTelegrams[novusCfg.ModbusAddress].Count > 0)
{
byte[] telegram = modbus.ReceivedTelegrams[novusCfg.ModbusAddress].Dequeue();
if (telegram.Length == 5)
{
log.InfoFormat("[{0}] received 5 bytes", Name);
}
else if (telegram.Length == 27 && telegram[1] == (byte)Function.ReadHoldingRegisters && telegram[2] == 22)
{
Int16[] values = new Int16[11];
for (int i = 0; i < values.Length; i++)
{
values[i] = (Int16)(256 * telegram[2 * i + 3] + telegram[2 * i + 4]
- ((telegram[2 * i + 3] & 0x80) != 0 ? 65536 : 0));
}
status1 = values[Status1Address];
status2 = values[Status2Address];
status3 = values[Status3Address];
fwVersion = values[FWVersionAddress];
controllerId = values[ControllerIdAddress];
double setpointCorF = (double)values[SetpointAddress] * 0.1;
temperatureSetPoint = isFahrenheit ? Units.ConvertFrom(Unit.F, setpointCorF) : setpointCorF;
double temperatureCorF = (double)values[ProcessValueAddress] * 0.1;
actualTemperature = isFahrenheit ? Units.ConvertFrom(Unit.F, temperatureCorF) : temperatureCorF;
novusDataValid = true;
log.InfoFormat("[{0}] setpoint = {1} temperature = {2}", Name, temperatureSetPoint, actualTemperature);
log.InfoFormat("[{0}] s1=0x{1} s2=0x{2} s3=0x{3} fw={4} id={5}",
Name, status1.ToString("X4"), status2.ToString("X4"), status3.ToString("X4"),
fwVersion, controllerId == 18 ? "N1200HC" : controllerId == 48 ? "N1200" : "unknonw");
}
else
{
log.InfoFormat("[{0}] received soemthing else", Name);
}
}
}
else if (novusCfg.DebugLevel == DebugMode.Simulate)
{
switch ((StateMachine.Time / 3) % 3)
{
case 0: status2 = 0; break;
case 1: status2 = 1 << 11; break;
case 2: status2 = 1 << 12; break;
}
}
}
/// <summary>Run this device</summary>
public void RunDeviceAfter()
{
if (DebugLevel != DebugMode.Simulate && DebugLevel != DebugMode.FailureDuringOperation
//&& modbus.IsMyTurn(ticketNumber)
)
{
SendValueToNovus(Function.ReadHoldingRegisters, 0, 11); /// Read setpoint, PV (actual temperature), status1, status2, status3, FW version etc.
//SendValueToNovus(Function.ReadHoldingRegisters, 0, 2); /// Read setpoint and PV (actual temperature)
//SendValueToNovus(Function.ReadHoldingRegisters, 6, 5); /// Read status1, status2, status3, FW version etc.
}
}
public void StopDevice() { }
public void StopDevice2() { }
/// <summary>
/// Set temperature controller setpoint to specified value
/// </summary>
/// <param name="temperatureC">Temperature in °C</param>
/// <returns>true = OK, false = any error</returns>
public bool SetTemperatureSetpoint(double temperatureC)
{
if (temperatureC != 0 && (temperatureC < 5.0 || temperatureC > 95.0))
{
return false; /// required temperature out of range
}
if (novusDataValid && (temperatureC == temperatureSetPoint))
{
return true; /// already set to the required temperature
}
if (temperatureC != 0) /// temperature==0 disables sending the setpoint
{
double reqTemp = isFahrenheit ? Units.ConvertTo(Unit.F, temperatureC) : temperatureC;
Int16 tempSetpointInt16 = Convert.ToInt16(Math.Round(10 * reqTemp)); /// setpoint in 0.1 °C or °F
ushort destAddress = SetpointAddress;
///
SendValueToNovus(Function.PresetSingleRegister, destAddress, tempSetpointInt16);
}
temperatureSetPoint = temperatureC;
return true;
}
void SendValueToNovus(Function function, UInt16 address, Int16 value)
{
byte[] msg = new byte[8];
msg[0] = (byte)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.
///
public double ReadTemperature()
{
return actualTemperature;
}
/// <summary>
/// Events: SetOpututsDone, Error
/// </summary>
/// <returns>SetOutputsOp instance reference casted to IOperaton</returns>
public IOperation SetTemperatureSetpointOp(double temperature)
{
return new SetTemperatureSetpointOp(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);
}
///
/// ISequenceCondition interface implementation (conditions in transition sequences)
///
IList<string> sequenceConditionNames;
IList<IOperation> sequenceConditions;
public int ConditionsCount { get { return sequenceConditions != null ? sequenceConditions.Count : 0; } }
/// Strings are added to the combo-box for transition sequence condition selection
public string ConditionName(int i)
{
if (sequenceConditionNames != null && i < sequenceConditionNames.Count && i >= 0)
return sequenceConditionNames[i];
else
return string.Empty;
}
/// Operations are executed as a part of a transition sequence
public IOperation ConditionOp(int i)
{
if (sequenceConditions != null && i < sequenceConditions.Count && i >= 0)
return sequenceConditions[i];
else
return null;
}
void ClearConditions()
{
sequenceConditionNames = new List<string>();
sequenceConditions = new List<IOperation>();
}
void AddCondition(string conditionName, bool createOperation, IOperation conditionOperation)
{
sequenceConditionNames.Add(conditionName);
if (createOperation) sequenceConditions.Add(conditionOperation);
}
/// <summary>
/// Create a list of conditions
/// </summary>
/// <param name="createOps">true = create names and condition operations, false = create names only</param>
void CreateConditions(bool createOps)
{
ClearConditions();
AddCondition("Set temperature setpoint A", createOps, SetTemperatureSetpointOp(novusCfg.ProcParams.Temp_A));
AddCondition("Set temperature setpoint B", createOps, SetTemperatureSetpointOp(novusCfg.ProcParams.Temp_B));
AddCondition("Set temperature setpoint C", createOps, SetTemperatureSetpointOp(novusCfg.ProcParams.Temp_C));
AddCondition("Set temperature setpoint D", createOps, SetTemperatureSetpointOp(novusCfg.ProcParams.Temp_D));
AddCondition("Set temperature setpoint E", createOps, SetTemperatureSetpointOp(novusCfg.ProcParams.Temp_E));
}
}
}

View File

@ -0,0 +1,202 @@
///
/// Copyright (c) 2022 Sensus Slovensko a.s.
///
using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Serialization;
using Common;
using Config.Entities;
using TBF.Resources;
using TBF.Rig.Generic;
namespace TBF.Rig.Modbus.TempControl.Novus
{
public class TControlCfg : ComponentCfgBase, IChildComponentCfg, IParamsProvider
{
public static XmlSerializer Serializer = XmlSerializer.FromTypes(new[] { typeof(TControlCfg) })[0];
public override XmlSerializer GetSerializer() { return Serializer; }
public IComponentCfgCtrl GetControl(IList<Component> cmpntEntities)
{
IList<string> parents = new List<string>();
foreach (var c in cmpntEntities) if (c.ClassName == "Modbus") parents.Add(c.Name);
return new Configs.ParamsProvider.ComponentCfgCtrl(this, parents);
}
///
/// Serialized parameters
///
public int ModbusAddress; /// 0: 1..254
public string MsrdFormat { get; set; } /// 1
public Unit MsrdUnit { get; set; } /// 2
public double MsrdValLimLo { get; set; }
public double MsrdValLimHi { get; set; }
public string SetpFormat { get; set; } /// 3
public Unit SetpUnit { get; set; } /// 4
public double SetpStep { get; set; } /// 5
public bool RoundToSetpStep { get; set; } /// 6
/// <summary> Procedure parameters </summary>
[XmlIgnore]
public ProcParams ProcParams;
public override IParamsProvider GetRuntimeProcParamsProvider() { return ProcParams; }
public override IParamsProvider CreateProcParamsProvider() { return new ProcParams(true); }
/// Private parameterless constructor invoked by all other (public) constructors
TControlCfg()
{
ProcParams = new ProcParams(true);
}
public TControlCfg(IComponentFactory factory)
: this()
{
this.Factory = factory;
InitializeAll();
}
public string ComponentName { get { return Name; } }
public void InitializeAll()
{
Name = "Novus";
ParentName = "Modbus";
ModbusAddress = 49;
MsrdFormat = "{0:F1} °C";
MsrdUnit = Unit.C;
MsrdValLimLo = 5;
MsrdValLimHi = 95;
SetpFormat = "{0:F1} °C";
SetpUnit = Unit.C;
SetpStep = 1.0;
RoundToSetpStep = true;
}
string[] paramNames = new string[]
{
"Modbus address", /// 0
"Measured temperature format", /// 1
"Unit of measured temperature", /// 2
"Setpoint format", /// 3
"Unit of temperature setpoint", /// 4
"Setpoint step", /// 5
"Round to setpoint step", /// 6
};
public string ParamName(int i) { return paramNames[i]; }
public int ParamsCount() { return paramNames.Length; }
public ICollection<string> ParamValues(int i)
{
switch (i)
{
case 2:
case 4:
return new string[] { "°C", "°F", "K" };
case 6:
return new string[] { Strings.yes, Strings.no };
default:
return null;
}
}
public string ToString(int i)
{
switch (i)
{
case 0: return ModbusAddress.ToString();
case 1: return MsrdFormat;
case 2: return (MsrdUnit == Unit.K) ? "K" : (MsrdUnit == Unit.F) ? "°F" : "°C";
case 3: return SetpFormat;
case 4: return (SetpUnit == Unit.K) ? "K" : (SetpUnit == Unit.F) ? "°F" : "°C";
case 5: return SetpStep.ToString();
case 6: return RoundToSetpStep ? Strings.yes : Strings.no;
default:
return string.Format("{0}({1}) address={2}", Name, string.IsNullOrEmpty(ParentName) ? "-" : ParentName, ModbusAddress);
}
}
public CfgUpdateFlags UpdateParam(int i, string str)
{
switch (i)
{
case 0: ModbusAddress = int.Parse(str); return CfgUpdateFlags.RestartRqrd;
case 1: MsrdFormat = str; return CfgUpdateFlags.RestartRqrd;
case 2: MsrdUnit = (str == "°K" || str == "K") ? Unit.K
: (str == "°F" || str == "F") ? Unit.F
: Unit.C;
return CfgUpdateFlags.RestartRqrd;
case 3: SetpFormat = str; return CfgUpdateFlags.RestartRqrd;
case 4: SetpUnit = (str == "°K" || str == "K") ? Unit.K
: (str == "°F" || str == "F") ? Unit.F
: Unit.C;
return CfgUpdateFlags.RestartRqrd;
case 5: SetpStep = Utils.ParseUDouble(str); return CfgUpdateFlags.RestartRqrd;
case 6: RoundToSetpStep = (str == Strings.yes); return CfgUpdateFlags.RestartRqrd;
default: return CfgUpdateFlags.None;
}
}
public bool ValidateParam(int i, string str, out string message)
{
message = string.Empty;
int idummy;
double ddummy;
switch (i)
{
case 0:
if (int.TryParse(str, out idummy) && idummy >= 1 && idummy <= 254) return true;
break;
case 1:
case 3:
return true;
case 2:
case 4:
if (str == "°K" || str == "K" || str == "°F" || str == "F" || str == "°C" || str == "C") return true;
break;
case 5:
if (Utils.TryParseUDouble(str, out ddummy)) return true;
break;
case 6:
if (str == Strings.yes || str == Strings.no) return true;
break;
default:
message = "Invalid index";
return false;
}
message = ParamName(i) + " is invalid";
return false;
}
void CopyContentTo(TControlCfg prms)
{
prms.ParentName = ParentName;
prms.ModbusAddress = ModbusAddress;
prms.MsrdFormat = MsrdFormat;
prms.MsrdUnit = MsrdUnit;
prms.MsrdValLimLo = MsrdValLimLo;
prms.MsrdValLimHi = MsrdValLimHi;
prms.SetpFormat = SetpFormat;
prms.SetpUnit = SetpUnit;
prms.SetpStep = SetpStep;
prms.RoundToSetpStep = RoundToSetpStep;
}
public IParamsProvider Clone()
{
TControlCfg pars = new TControlCfg();
CopyContentTo(pars);
return pars;
}
public bool UpdateEmbeddedDbEntity()
{
return true; /// =OK, do nothing
}
}
}

View File

@ -122,11 +122,11 @@ namespace TBF.Rig
Factories.Add(new MettlerToledo.Multi.BalanceFactory()); /// MettlerToledo-Multi
Factories.Add(new Modbus.CometAmbient.Factory()); /// Modbus.CometAmbient.Ambient
Factories.Add(new Modbus.Common.Factory()); /// Modbus
Factories.Add(new Modbus.Novus.Factory()); /// Modbus.Novus
Factories.Add(new Modbus.PressureMeter.Meret.Factory()); /// Modbus.PressureMeter.Meret - Meret pressure meter connected via modbus
Factories.Add(new Modbus.QuidoRS.Factory()); /// Modbus.QuidoRS
Factories.Add(new Modbus.TankSelector.Factory()); /// Modbus.TankSelector
Factories.Add(new Modbus.TempControl.Easytherm.Factory()); /// Modbus.TempControl.Easytherm
Factories.Add(new Modbus.TempControl.Novus.Factory()); /// Modbus.TempControl.Novus
Factories.Add(new Modbus.UltrasoundLevelMeter.Factory());
Factories.Add(new Modbus.WaterAnalyzer.Factory());
Factories.Add(new Modbus.WaterAnalyzer2.Factory());

View File

@ -734,6 +734,7 @@
<Compile Include="Rig\GenericDevices\IStatisticsMonitoring.cs" />
<Compile Include="Rig\GenericDevices\ITankDraining.cs" />
<Compile Include="Rig\GenericDevices\ITankDrainingCfg.cs" />
<Compile Include="Rig\GenericDevices\ITempControl.cs" />
<Compile Include="Rig\GenericDevices\ITestMethodWith2ndPass.cs" />
<Compile Include="Rig\GenericDevices\IVolumeMeter.cs" />
<Compile Include="Rig\Hart\Common\Enums.cs" />
@ -818,19 +819,6 @@
<DependentUpon>ModbusCfgCtrl.cs</DependentUpon>
</Compile>
<Compile Include="Rig\Modbus\Common\Factory.cs" />
<Compile Include="Rig\Modbus\Novus\Novus.cs" />
<Compile Include="Rig\Modbus\Novus\NovusCfg.cs" />
<Compile Include="Rig\Modbus\Novus\NovusCfgCtrl.cs">
<SubType>UserControl</SubType>
</Compile>
<Compile Include="Rig\Modbus\Novus\NovusCfgCtrl.designer.cs">
<DependentUpon>NovusCfgCtrl.cs</DependentUpon>
</Compile>
<Compile Include="Rig\Modbus\Novus\Factory.cs" />
<Compile Include="Rig\Modbus\Novus\ProcParams.cs" />
<Compile Include="Rig\Modbus\Novus\ReadTempOp.cs" />
<Compile Include="Rig\Modbus\Novus\SetRequiredTemperatureOp.cs" />
<Compile Include="Rig\Modbus\Novus\SetTemperatureOp.cs" />
<Compile Include="Rig\Modbus\PressureMeter\Meret\Factory.cs" />
<Compile Include="Rig\Modbus\PressureMeter\Meret\PressureMeter.cs" />
<Compile Include="Rig\Modbus\PressureMeter\Meret\PressureMeterCfg.cs" />
@ -884,6 +872,13 @@
<Compile Include="Rig\Modbus\TempControl\Easytherm\ReadTempOp.cs" />
<Compile Include="Rig\Modbus\TempControl\Easytherm\SetRequiredTemperatureOp.cs" />
<Compile Include="Rig\Modbus\TempControl\Easytherm\SetTemperatureOp.cs" />
<Compile Include="Rig\Modbus\TempControl\Novus\Factory.cs" />
<Compile Include="Rig\Modbus\TempControl\Novus\ProcParams.cs" />
<Compile Include="Rig\Modbus\TempControl\Novus\ReadTempOp.cs" />
<Compile Include="Rig\Modbus\TempControl\Novus\SetTemperatureOp.cs" />
<Compile Include="Rig\Modbus\TempControl\Novus\SetTemperatureSetpointOp.cs" />
<Compile Include="Rig\Modbus\TempControl\Novus\TControl.cs" />
<Compile Include="Rig\Modbus\TempControl\Novus\TControlCfg.cs" />
<Compile Include="Rig\Modbus\UltrasoundLevelMeter\Factory.cs" />
<Compile Include="Rig\Modbus\UltrasoundLevelMeter\LevelMeter.cs" />
<Compile Include="Rig\Modbus\UltrasoundLevelMeter\LevelMeterCfg.cs" />
@ -3047,9 +3042,6 @@
<EmbeddedResource Include="Rig\Modbus\Common\ModbusCfgCtrl.resx">
<DependentUpon>ModbusCfgCtrl.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="Rig\Modbus\Novus\NovusCfgCtrl.resx">
<DependentUpon>NovusCfgCtrl.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="Rig\Modbus\PressureMeter\Meret\PressureMeterCfgCtrl.resx">
<DependentUpon>PressureMeterCfgCtrl.cs</DependentUpon>
</EmbeddedResource>
@ -3802,7 +3794,6 @@
</ProjectReference>
</ItemGroup>
<ItemGroup>
<Folder Include="Rig\Modbus\TempControl\Novus\" />
<Folder Include="Rig\Sirt\V2012_868MHz\" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />

View File

@ -334,7 +334,7 @@ namespace TBF.UI.Procedures
if (cmpnt is TBF.Rig.TestMethods.S640Communication.S640Start) other.Add(cmpnt);
if (cmpnt is TBF.Rig.TestMethods.S640Communication.S640End) other.Add(cmpnt);
if (cmpnt is TBF.Rig.Modbus.TempControl.Easytherm.Easytherm) other.Add(cmpnt);
if (cmpnt is TBF.Rig.Modbus.Novus.Novus) other.Add(cmpnt);
if (cmpnt is TBF.Rig.Modbus.TempControl.Novus.TControl) other.Add(cmpnt);
}
if (other.Count > 0)