Neuron®
The Neuron® is the basis for the creation of open and secure federated networks for smart societies.
Loading...
Searching...
No Matches
ProxyMqttChannelTopicNode.cs
1using System;
2using System.Collections.Generic;
3using System.Text;
4using System.Threading.Tasks;
5using Waher.Content;
22
24{
29 {
30 private string proxyNodeId;
31 private string proxyFieldName;
32
37 {
38 }
39
43 [Page(1, "IEEE 1451")]
44 [Header(29, "Proxy Node ID:", 400)]
45 [ToolTip(30, "Node ID of device to represent using this channel node.")]
46 [Required]
47 public string ProxyNodeId
48 {
49 get => this.proxyNodeId;
50 set => this.proxyNodeId = value;
51 }
52
56 [Page(1, "IEEE 1451")]
57 [Header(31, "Proxy Field Name:", 500)]
58 [ToolTip(32, "Name of field to represent using this channel node.")]
59 [Required]
60 public string ProxyFieldName
61 {
62 get => this.proxyFieldName;
63 set => this.proxyFieldName = value;
64 }
65
69 public override string LocalId
70 {
71 get
72 {
73 if (!string.IsNullOrEmpty(this.EntityName))
74 return this.EntityName;
75 else
76 return this.NodeId;
77 }
78 }
79
83 public override Task<string> GetTypeNameAsync(Language Language)
84 {
85 return Language.GetStringAsync(typeof(Ieee1451Parser), 2, "IEEE 1451.0 Proxy Channel");
86 }
87
94 public override async Task<IEnumerable<Parameter>> GetDisplayableParametersAsync(Language Language, RequestOrigin Caller)
95 {
96 LinkedList<Parameter> Parameters = (LinkedList<Parameter>)await base.GetDisplayableParametersAsync(Language, Caller);
97
98 Parameters.AddLast(new Int32Parameter("ChannelId",
99 await Language.GetStringAsync(typeof(MqttNcapTopicNode), 10, "Channel"),
100 this.ChannelId));
101
102 Parameters.AddLast(new StringParameter("TimId",
103 await Language.GetStringAsync(typeof(MqttNcapTopicNode), 7, "TIM ID"),
104 this.TimId));
105
106 Parameters.AddLast(new StringParameter("NcapId",
107 await Language.GetStringAsync(typeof(MqttNcapTopicNode), 4, "NCAP ID"),
108 this.NcapId));
109
110 Parameters.AddLast(new StringParameter("ProxyNodeId",
111 await Language.GetStringAsync(typeof(MqttNcapTopicNode), 34, "Proxy Node ID"),
112 this.ProxyNodeId));
113
114 Parameters.AddLast(new StringParameter("ProxyFieldName",
115 await Language.GetStringAsync(typeof(MqttNcapTopicNode), 35, "Proxy Field Name"),
116 this.ProxyFieldName));
117
118 return Parameters;
119 }
120
124 public override Task<bool> AcceptsParentAsync(INode Parent)
125 {
126 return Task.FromResult(Parent is ProxyMqttTimTopicNode);
127 }
128
132 public override Task<bool> AcceptsChildAsync(INode Child)
133 {
134 return Task.FromResult(false);
135 }
136
143 {
144 if (Topic.SegmentIndex > 0 &&
147 int.TryParse(Topic.CurrentSegment, out _))
148 {
149 return Grade.Excellent;
150 }
151 else
152 return Grade.NotAtAll;
153 }
154
160 public override async Task<IMqttTopicNode> CreateNew(MqttTopicRepresentation Topic)
161 {
162 StringBuilder sb = new StringBuilder();
163
164 sb.Append("Channel-");
165 sb.Append(Topic.Segments[Topic.SegmentIndex - 1]);
166 sb.Append('#');
167 sb.Append(Topic.CurrentSegment);
168
169 return new MqttChannelTopicNode()
170 {
171 NodeId = await GetUniqueNodeId(sb.ToString()),
173 ChannelId = int.Parse(Topic.CurrentSegment),
174 TimId = Topic.Segments[Topic.SegmentIndex - 1],
175 NcapId = Topic.Segments[Topic.SegmentIndex - 2]
176 };
177 }
178
185 public override async Task StartReadout(ISensorReadout Request, bool DoneAfter)
186 {
187 try
188 {
189 await base.StartReadout(Request, false);
190
191 Field[] Fields = await this.ReadSensor(Request.Actor);
192 await Request.ReportFields(DoneAfter, Fields);
193 }
194 catch (Exception ex)
195 {
196 await Request.ReportErrors(DoneAfter, new ThingError(this, ex.Message));
197 }
198 }
199
207 public async Task<Field[]> TryReadSensor(string Actor)
208 {
209 try
210 {
211 Field[] Result = await this.ReadSensor(Actor);
212 return Result;
213 }
214 catch (Exception ex)
215 {
216 await this.LogErrorAsync("ProxyError", ex.Message);
217 return null;
218 }
219 }
220
227 public async Task<Field[]> ReadSensor(string Actor)
228 {
229 MeteringNode Node = await MeteringTopology.GetNode(this.proxyNodeId)
230 ?? throw new Exception("Node not found in Metering Topology: " + this.proxyNodeId);
231
232 if (!(Node is ISensor Sensor))
233 throw new Exception("Node not a sensor node: " + this.proxyNodeId);
234
235 List<Field> Fields = new List<Field>();
236 List<ThingError> Errors = new List<ThingError>();
237 TaskCompletionSource<bool> Completed = new TaskCompletionSource<bool>();
239 new IThingReference[] { Sensor }, FieldType.Momentary,
240 new string[] { this.proxyFieldName }, DateTime.MinValue, DateTime.MaxValue,
241 (_, e) =>
242 {
243 Fields.AddRange(e.Fields);
244 if (e.Done)
245 Completed.TrySetResult(true);
246
247 return Task.CompletedTask;
248 },
249 (_, e) =>
250 {
251 Errors.AddRange(e.Errors);
252 if (e.Done)
253 Completed.TrySetResult(true);
254
255 return Task.CompletedTask;
256 }, null);
257
258 await Sensor.StartReadout(Readout);
259
260 _ = Task.Delay(this.TimeoutMilliseconds).ContinueWith(
261 (_) => Completed.TrySetException(new TimeoutException()));
262
263 await Completed.Task;
264
265 if (Errors.Count > 0)
266 {
267 foreach (ThingError Error in Errors)
268 throw new Exception(Error.ErrorMessage);
269 }
270
271 await this.RemoveErrorAsync("ProxyError");
272
273 return Fields.ToArray();
274 }
275
283 SamplingMode SamplingMode, double TimeoutSeconds)
284 {
285 if (!(await this.GetParent() is ProxyMqttTimTopicNode TimNode))
286 return;
287
288 if (!(await TimNode.GetParent() is ProxyMqttNcapTopicNode NcapNode))
289 return;
290
291 if (!(await NcapNode.GetParent() is DiscoverableTopicNode CommunicationNode))
292 return;
293
294 MqttBrokerNode BrokerNode = await CommunicationNode.GetBroker();
295 if (BrokerNode is null)
296 return;
297
298 MqttBroker Broker = await BrokerNode.GetBroker();
299 if (Broker is null)
300 return;
301
302 string Topic = await CommunicationNode.GetFullTopic();
303 if (string.IsNullOrEmpty(Topic))
304 return;
305
306 Field[] Fields = await this.TryReadSensor(string.Empty);
307 if (Fields is null)
308 return; // TODO: Error response
309
310 Field MainField = this.GetMainField(Fields);
311 string StringValue;
312
313 if (MainField is QuantityField Quantity &&
314 Unit.TryParse(Quantity.Unit, out Unit MainUnit))
315 {
316 double Value = Quantity.Value;
317 double NrDecimals = Quantity.NrDecimals;
318
319 if (PhysicalUnits.TryCreate(MainUnit, ref Value, ref NrDecimals, out _))
320 {
321 byte NrDec = (byte)(NrDecimals < 0 ? 0 : NrDecimals > 255 ? 255 : Math.Round(NrDecimals));
322 StringValue = CommonTypes.Encode(Value, NrDec);
323 }
324 else
325 StringValue = CommonTypes.Encode(Quantity.Value, Quantity.NrDecimals);
326 }
327 else if (MainField is null)
328 return; // TODO: Error response
329 else
330 StringValue = MainField.ObjectValue?.ToString() ?? string.Empty;
331
332 StringBuilder ToSniffer = BrokerNode.HasSniffers ? new StringBuilder() : null;
333
335 (ushort)this.ChannelId, StringValue, MainField.Timestamp, ToSniffer);
336
337 if (!(ToSniffer is null))
338 await BrokerNode.Information(ToSniffer.ToString());
339
340 await Broker.Publish(Topic, MqttQualityOfService.AtLeastOnce, false, Response);
341 }
342
348 public Field GetMainField(Field[] Fields)
349 {
350 foreach (Field Field in Fields)
351 {
352 if (Field.Type == FieldType.Momentary && Field.Name == this.proxyFieldName)
353 return Field;
354 }
355
356 return null;
357 }
358
367 TedsAccessCode TedsAccessCode, uint TedsOffset, double TimeoutSeconds)
368 {
369 if (!(await this.GetParent() is ProxyMqttTimTopicNode TimNode))
370 return;
371
372 if (!(await TimNode.GetParent() is ProxyMqttNcapTopicNode NcapNode))
373 return;
374
375 if (!(await NcapNode.GetParent() is DiscoverableTopicNode CommunicationNode))
376 return;
377
378 MqttBrokerNode BrokerNode = await CommunicationNode.GetBroker();
379 if (BrokerNode is null)
380 return;
381
382 MqttBroker Broker = await BrokerNode.GetBroker();
383 if (Broker is null)
384 return;
385
386 string Topic = await CommunicationNode.GetFullTopic();
387 if (string.IsNullOrEmpty(Topic))
388 return;
389
390 byte[] Response;
391 StringBuilder ToSniffer;
392
393 switch (TedsAccessCode)
394 {
395 case TedsAccessCode.MetaTEDS:
396 ToSniffer = BrokerNode.HasSniffers ? new StringBuilder() : null;
398 this.TimIdBinary, (ushort)this.ChannelId, ToSniffer,
399 new TedsId(99, 255, (byte)TedsAccessCode.MetaTEDS, 2, 1));
400 break;
401
402 case TedsAccessCode.ChanTEDS:
403 Field[] Fields = await this.TryReadSensor(TedsAccessCode.ToString());
404 if (Fields is null)
405 return;
406
407 List<TedsRecord> Records = new List<TedsRecord>();
408 Field MainField = this.GetMainField(Fields);
409
410 if (MainField is QuantityField Quantity &&
411 Unit.TryParse(Quantity.Unit, out Unit MainUnit))
412 {
413 double Value = Quantity.Value;
414 double NrDecimals = Quantity.NrDecimals;
415
416 if (PhysicalUnits.TryCreate(MainUnit, ref Value, ref NrDecimals, out PhysicalUnits Ieee1451Unit))
417 Records.Add(new Ieee1451_0.TEDS.FieldTypes.TransducerChannelTeds.PhysicalUnits(Ieee1451Unit));
418 }
419
420 ToSniffer = BrokerNode.HasSniffers ? new StringBuilder() : null;
421
423 this.TimIdBinary, (ushort)this.ChannelId, ToSniffer,
424 new TedsId(99, 255, (byte)TedsAccessCode.ChanTEDS, 2, 1),
425 Records.ToArray());
426 break;
427
428 case TedsAccessCode.XdcrName:
429 ToSniffer = BrokerNode.HasSniffers ? new StringBuilder() : null;
431 this.TimIdBinary, (ushort)this.ChannelId, ToSniffer,
432 new TedsId(99, 255, (byte)TedsAccessCode.XdcrName, 2, 1),
433 new Format(true),
434 new Ieee1451_0.TEDS.FieldTypes.TransducerNameTeds.Content(this.EntityName));
435 break;
436
437 default:
438 return;
439 }
440
441 if (!(ToSniffer is null))
442 await BrokerNode.Information(ToSniffer.ToString());
443
444 await Broker.Publish(Topic, MqttQualityOfService.AtLeastOnce, false, Response);
445 }
446
447 }
448}
Helps with parsing of commong data types.
Definition: CommonTypes.cs:13
static string Encode(bool x)
Encodes a Boolean for use in XML and other formats.
Definition: CommonTypes.cs:594
Manages a chat sensor data readout request.
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
Represents a unit.
Definition: Unit.cs:15
static bool TryParse(string UnitString, out Unit Unit)
Tries to parse a string into a unit.
Definition: Unit.cs:137
static byte[] SerializeResponse(ushort ErrorCode, byte[] NcapId, byte[] TimId, ushort ChannelId, StringBuilder SnifferOutput, TedsId TedsHeader, params TedsRecord[] Records)
Serializes a response to a TEDS request.
static byte[] SerializeResponse(ushort ErrorCode, byte[] NcapId, byte[] TimId, ushort ChannelId, string Value, DateTime Timestamp, StringBuilder SnifferOutput)
Serializes a request for transducer data.
TEDS identification header (§6.3)
Definition: TedsId.cs:14
MQTT Topic node that publishes discovery commands in accordance with IEEE 1451.0.
Topic node representing an IEEE 1451.0 Channel.
MqttChannelTopicNode()
Topic node representing an IEEE 1451.0 Channel.
Topic node representing an IEEE 1451.0 NCAP.
int TimeoutMilliseconds
Timeout for request/response, in milliseconds.
Topic node representing an IEEE 1451.0 TIM.
Topic node representing a proxy for an IEEE 1451.0 Channel.
async Task TransducerDataRequest(TransducerAccessMessage TransducerAccessMessage, SamplingMode SamplingMode, double TimeoutSeconds)
A request for transducer data has been received.
override Task< string > GetTypeNameAsync(Language Language)
Diaplayable type name for node.
async Task< Field[]> TryReadSensor(string Actor)
Tries to read the proxy node. If not successful, an error is logged on the node, and null is returned...
Field GetMainField(Field[] Fields)
Gets the main field, from a set of fields.
override async Task StartReadout(ISensorReadout Request, bool DoneAfter)
Starts the readout of the sensor.
override Task< bool > AcceptsParentAsync(INode Parent)
If the node accepts a given parent.
async Task< Field[]> ReadSensor(string Actor)
Reads the transducer value.
ProxyMqttChannelTopicNode()
Topic node representing a proxy for an IEEE 1451.0 Channel.
override async Task< IMqttTopicNode > CreateNew(MqttTopicRepresentation Topic)
Creates a new node of the same type.
override async Task< IEnumerable< Parameter > > GetDisplayableParametersAsync(Language Language, RequestOrigin Caller)
Gets displayable parameters.
async Task TedsRequest(TedsAccessMessage TedsAccessMessage, TedsAccessCode TedsAccessCode, uint TedsOffset, double TimeoutSeconds)
A request for TEDS data has been received.
override Task< bool > AcceptsChildAsync(INode Child)
If the node accepts a given child.
override Grade Supports(MqttTopicRepresentation Topic)
How well the topic node supports an MQTT topic
Topic node representing an IEEE 1451.0 NCAP Proxy.
Topic node representing an IEEE 1451.0 TIM Proxy.
Static class for IEEE 1451-related parsing tasks.
Base class for all metering nodes.
Definition: MeteringNode.cs:28
static async Task< string > GetUniqueNodeId(string NodeId)
Gets a Node ID, based on NodeId that is not already available in the database.
Defines the Metering Topology data source. This data source contains a tree structure of persistent r...
static Task< MeteringNode > GetNode(string NodeId)
Gets a node from the Metering Topology
MQTT Broker connection object.
Definition: MqttBroker.cs:17
Task Publish(string Topic, MqttQualityOfService QoS, bool Retain, byte[] Data)
Publishes binary data to a topic.
Definition: MqttBroker.cs:147
IMqttTopicNode Node
Reference to the MQTT Topic Node
Definition: MqttTopic.cs:51
Node representing a connection to an MQTT broker.
Task< MqttBroker > GetBroker()
Gets the corresponding broker node.
async Task Information(string Comment)
Called to inform the viewer of something.
string LocalTopic
Local Topic segment
Contains information about an MQTT topic
string CurrentSegment
Current segment being processed.
MqttTopic CurrentParentTopic
Current parent topic.
string[] Segments
Segments in topic string.
Tokens available in request.
Definition: RequestOrigin.cs:9
Base class for all sensor data fields.
Definition: Field.cs:20
FieldType Type
Field Type flags.
Definition: Field.cs:259
abstract object ObjectValue
Field value, boxed as an object reference.
Definition: Field.cs:416
string Name
Unlocalized field name.
Definition: Field.cs:279
DateTime Timestamp
Timestamp of field value.
Definition: Field.cs:202
Represents a physical quantity value.
Contains information about an error on a thing
Definition: ThingError.cs:10
string ErrorMessage
Error message.
Definition: ThingError.cs:70
Interface for nodes that are published through the concentrator interface.
Definition: INode.cs:49
INode Parent
Parent Node, or null if a root node.
Definition: INode.cs:116
Interface for sensor nodes.
Definition: ISensor.cs:9
Interface for classes managing sensor data readouts.
string Actor
Actor making the request.
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.
Interface for thing references.
Interface for nodes that can return TEDS.
Definition: ITedsNode.cs:10
Interface for nodes that can return transducer information.
Task< bool > RemoveErrorAsync()
Removes error messages with an empty event ID from the node.
Task< INode > GetParent()
Gets the parent of the node.
Task LogErrorAsync(string Body)
Logs an error message on the node.
MqttQualityOfService
MQTT Quality of Service level.
Grade
Grade enumeration
Definition: Grade.cs:7
FieldType
Field Type flags
Definition: FieldType.cs:10
static bool TryCreate(Unit Unit, ref double Value, ref double NrDecimals, out PhysicalUnits Units)
Tries to create an IEEE 1451 units object from a script unit.