Neuron®
The Neuron® is the basis for the creation of open and secure federated networks for smart societies.
Loading...
Searching...
No Matches
ModbusUnitHoldingRegisterNode.cs
1using System;
2using System.Collections.Generic;
3using System.Threading.Tasks;
11
12namespace Waher.Things.Modbus
13{
18 {
23 : base()
24 {
25 this.Multiplier = 1.0;
26 this.Divisor = 1.0;
27 }
28
32 [Page(4, "Modbus", 100)]
33 [Header(13, "Register Number:")]
34 [ToolTip(14, "Register number on the Modbus unit.")]
35 [Range(0, 65535)]
36 [Required]
37 public int RegisterNr { get; set; }
38
42 [Page(4, "Modbus", 100)]
43 [Header(25, "Raw Name:")]
44 [ToolTip(26, "Custom field name for raw value.")]
45 [DefaultValueStringEmpty]
46 public string RawName { get; set; }
47
51 [Page(4, "Modbus", 100)]
52 [Header(11, "Field Name:")]
53 [ToolTip(12, "Custom field name for value.")]
54 [DefaultValueStringEmpty]
55 public string FieldName { get; set; }
56
60 [Page(4, "Modbus", 100)]
61 [Header(18, "Multiplier:")]
62 [ToolTip(19, "Multiplier will be multiplied to the register before reporting value.")]
63 [DefaultValue(1.0)]
64 [Text(TextPosition.BeforeField, 24, "Raw value will be transformed as follows: Value = ((Raw * Multiplier) / Divisor) + Offset. To this transformed value, the unit will be added.")]
65 public double Multiplier { get; set; }
66
70 [Page(4, "Modbus", 100)]
71 [Header(20, "Divisor:")]
72 [ToolTip(21, "Divisor will be divided from the register (after multiplication) before reporting value.")]
73 [DefaultValue(1.0)]
74 public double Divisor { get; set; }
75
79 [Page(4, "Modbus", 100)]
80 [Header(22, "Offset:")]
81 [ToolTip(23, "Offset will be addded to the register (after division) before reporting value.")]
82 [DefaultValue(0.0)]
83 public double Offset { get; set; }
84
88 [Page(4, "Modbus", 100)]
89 [Header(16, "Unit:")]
90 [ToolTip(17, "Unit of register value (after scaling and offset).")]
91 [DefaultValueStringEmpty]
92 public string Unit { get; set; }
93
97 [Page(4, "Modbus", 100)]
98 [Header(46, "Switch byte order.")]
99 [ToolTip(47, "If checked, byte order in registers will be reversed.")]
100 [DefaultValue(false)]
101 public bool SwitchByteOrder { get; set; }
102
108 public override Task<string> GetTypeNameAsync(Language Language)
109 {
110 return Language.GetStringAsync(typeof(ModbusGatewayNode), 27, "Holding Register (4x)");
111 }
112
119 public override async Task<IEnumerable<Parameter>> GetDisplayableParametersAsync(Language Language, RequestOrigin Caller)
120 {
121 LinkedList<Parameter> Result = await base.GetDisplayableParametersAsync(Language, Caller) as LinkedList<Parameter>;
122
123 Result.AddLast(new Int32Parameter("Nr", await Language.GetStringAsync(typeof(ModbusGatewayNode), 10, "Nr"), this.RegisterNr));
124
125 return Result;
126 }
127
132 public async Task StartReadout(ISensorReadout Request)
133 {
134 ModbusTcpClient Client = await (await this.GetGateway()).GetTcpIpConnection();
135 await Client.Enter();
136 try
137 {
138 ushort[] Values = await Client.ReadMultipleRegisters((byte)(await this.GetUnitNode()).UnitId, (ushort)this.RegisterNr, 1);
139 ushort Raw = CheckOrder(this.SwitchByteOrder, Values[0]);
140 double Value = ((Raw * this.Multiplier) / this.Divisor) + this.Offset;
141 int NrDec = Math.Min(255, Math.Max(0, (int)Math.Ceiling(-Math.Log10(this.Multiplier / this.Divisor))));
142 DateTime TP = DateTime.UtcNow;
143
144 ThingReference This = await this.GetReportAs();
145 List<Field> Fields = new List<Field>
146 {
147 new QuantityField(This, TP, this.GetFieldName(), Value, (byte)NrDec, this.Unit, FieldType.Momentary, FieldQoS.AutomaticReadout, true)
148 };
149
150 if (!string.IsNullOrEmpty(this.RawName))
151 Fields.Add(new Int32Field(This, TP, this.RawName, Raw, FieldType.Momentary, FieldQoS.AutomaticReadout, true));
152
153 await Request.ReportFields(true, Fields.ToArray());
154 }
155 catch (Exception ex)
156 {
157 await Request.ReportErrors(true, new ThingError(this, ex.Message));
158 }
159 finally
160 {
161 await Client.Leave();
162 }
163 }
164
169 public string GetFieldName()
170 {
171 if (string.IsNullOrEmpty(this.FieldName))
172 return "Value";
173 else
174 return this.FieldName;
175 }
176
181 public Task<ControlParameter[]> GetControlParameters()
182 {
183 List<ControlParameter> Parameters = new List<ControlParameter>()
184 {
185 new DoubleControlParameter("Value", "Modbus", this.GetFieldName(), "Register output",
186 this.Offset, ((65535 * this.Multiplier) / this.Divisor) + this.Offset,
187 async (Node) =>
188 {
189 ModbusTcpClient Client = await (await this.GetGateway()).GetTcpIpConnection();
190 await Client.Enter();
191 try
192 {
193 ushort[] Values = await Client.ReadMultipleRegisters((byte)(await this.GetUnitNode()).UnitId, (ushort)this.RegisterNr, 1);
194 return ((CheckOrder(this.SwitchByteOrder, Values[0]) * this.Multiplier) / this.Divisor) + this.Offset;
195 }
196 finally
197 {
198 await Client.Leave();
199 }
200 },
201 async (Node, Value) =>
202 {
203 ModbusTcpClient Client = await (await this.GetGateway()).GetTcpIpConnection();
204 await Client.Enter();
205 try
206 {
207 ushort Raw = (ushort)Math.Min(65535, Math.Max(0, ((Value - this.Offset) * this.Divisor) / this.Multiplier));
208 ushort WritenValue = await Client.WriteRegister((byte)(await this.GetUnitNode()).UnitId, (ushort)this.RegisterNr,
209 CheckOrder(this.SwitchByteOrder, Raw));
210
211 if (WritenValue != Value)
212 throw new Exception("Register value not changed correctly.");
213 }
214 finally
215 {
216 await Client.Leave();
217 }
218 })
219 };
220
221 if (!string.IsNullOrEmpty(this.RawName))
222 {
223 Parameters.Add(new Int32ControlParameter("Value", "Modbus", this.RawName, "Raw register output", 0, 65535,
224 async (Node) =>
225 {
226 ModbusTcpClient Client = await (await this.GetGateway()).GetTcpIpConnection();
227 await Client.Enter();
228 try
229 {
230 ushort[] Values = await Client.ReadMultipleRegisters((byte)(await this.GetUnitNode()).UnitId, (ushort)this.RegisterNr, 1);
231 return CheckOrder(this.SwitchByteOrder, Values[0]);
232 }
233 finally
234 {
235 await Client.Leave();
236 }
237 },
238 async (Node, Value) =>
239 {
240 ModbusTcpClient Client = await (await this.GetGateway()).GetTcpIpConnection();
241 await Client.Enter();
242 try
243 {
244 ushort WritenValue = await Client.WriteRegister((byte)(await this.GetUnitNode()).UnitId, (ushort)this.RegisterNr,
245 CheckOrder(this.SwitchByteOrder, (ushort)Value));
246
247 if (WritenValue != Value)
248 throw new Exception("Register value not changed correctly.");
249 }
250 finally
251 {
252 await Client.Leave();
253 }
254 }));
255 }
256
257 return Task.FromResult(Parameters.ToArray());
258 }
259
260 internal static ushort CheckOrder(bool SwitchByteOrder, ushort Value)
261 {
262 if (SwitchByteOrder)
263 {
264 ushort Value2 = (ushort)(Value & 0xff);
265 Value2 <<= 8;
266 Value2 |= (ushort)(Value >> 8);
267
268 return Value2;
269 }
270 else
271 return Value;
272 }
273 }
274}
Task Leave()
Leaves unique access to the TCP client. Must be called exactly one for each call to Enter.
async Task< ushort[]> ReadMultipleRegisters(byte UnitAddress, ushort ReferenceNumber, ushort NrWords)
Reads multiple registers from a Modbus unit.
Task Enter()
Enters unique access to the TCP client. Must be followed by exactly one Leave call.
async Task< ushort > WriteRegister(byte UnitAddress, ushort ReferenceNumber, ushort Value)
Write to a single register
Contains information about a language.
Definition: Language.cs:17
Task< string > GetStringAsync(Type Type, int Id, string Default)
Gets the string value of a string ID. If no such string exists, a string is created with the default ...
Definition: Language.cs:209
Node representing a TCP/IP connection to a Modbus Gateway
Abstract base class for child nodes to Mobus Unit nodes.
async Task< ModbusGatewayNode > GetGateway()
Modbus Gateway node.
async Task< ModbusUnitNode > GetUnitNode()
Modbus Unit node.
async Task< ThingReference > GetReportAs()
Under what node fields are to be reported.
Represents a holding register on a Modbus unit node.
override async Task< IEnumerable< Parameter > > GetDisplayableParametersAsync(Language Language, RequestOrigin Caller)
Gets displayable parameters.
override Task< string > GetTypeNameAsync(Language Language)
Gets the type name of the node.
Task< ControlParameter[]> GetControlParameters()
Get control parameters for the actuator.
async Task StartReadout(ISensorReadout Request)
Starts the readout of the sensor.
ModbusUnitHoldingRegisterNode()
Represents a register on a Modbus unit node.
bool SwitchByteOrder
If the byte order in words should be switched.
Tokens available in request.
Definition: RequestOrigin.cs:9
Represents a 32-bit integer value.
Definition: Int32Field.cs:10
Represents a physical quantity value.
Contains information about an error on a thing
Definition: ThingError.cs:10
Contains a reference to a thing
Interface for actuator nodes.
Definition: IActuator.cs:10
Interface for sensor nodes.
Definition: ISensor.cs:9
Interface for classes managing sensor data readouts.
Task ReportErrors(bool Done, params ThingError[] Errors)
Report error states to the client.
Task ReportFields(bool Done, params Field[] Fields)
Report read fields to the client.
TextPosition
Where the instructions are to be place.
Definition: TextAttribute.cs:9
FieldQoS
Field Quality of Service flags
Definition: FieldQoS.cs:10
FieldType
Field Type flags
Definition: FieldType.cs:10