Neuron®
The Neuron® is the basis for the creation of open and secure federated networks for smart societies.
Loading...
Searching...
No Matches
MqttTopic.cs
1using System;
3using System.Threading.Tasks;
12
14{
18 public class MqttTopic
19 {
20 private readonly SortedDictionary<string, MqttTopic> topics = new SortedDictionary<string, MqttTopic>();
21 private readonly IMqttTopicNode node;
22 private readonly ThingReference nodeReference;
23 private readonly MqttTopic parent;
24 private readonly MqttBroker broker;
25 private readonly string localTopic;
26 private readonly string fullTopic;
27 private long dataCount = 0;
28 private Exception ex = null;
29 private DateTime exTP = DateTime.MinValue;
30 private IMqttData data = null;
31
36 {
37 this.node = Node;
38 this.fullTopic = FullTopic;
39 this.localTopic = LocalTopic;
40 this.parent = Parent;
41 this.broker = Broker;
42
43 this.nodeReference = Node as ThingReference;
44 if (this.nodeReference is null && !(Node is null))
45 this.nodeReference = new ThingReference(Node.NodeId, Node.SourceId, Node.Partition);
46 }
47
51 public IMqttTopicNode Node => this.node;
52
56 public MqttBroker Broker => this.broker;
57
61 public string LocalTopic => this.localTopic;
62
66 public string FullTopic => this.fullTopic;
67
71 public IMqttData Data => this.data;
72
73 private MqttTopic[] GetChildNodes()
74 {
75 if (this.topics is null)
76 return Array.Empty<MqttTopic>();
77 else
78 {
79 MqttTopic[] Result;
80
81 lock (this.topics)
82 {
83 Result = new MqttTopic[this.topics.Count];
84 this.topics.Values.CopyTo(Result, 0);
85 }
86
87 return Result;
88 }
89 }
90
91 internal async Task<MqttTopic> GetTopic(MqttTopicRepresentation Representation, bool CreateNew, bool IgnoreGuids, MqttBroker Broker)
92 {
93 MqttTopic Topic = await this.GetLocalTopic(Representation, CreateNew, IgnoreGuids, Broker);
94
95 if (Topic is null)
96 return null;
97 else if (Representation.MoveNext(Topic))
98 return await Topic.GetTopic(Representation, CreateNew, IgnoreGuids, Broker);
99 else
100 return Topic;
101 }
102
103 private async Task<MqttTopic> GetLocalTopic(MqttTopicRepresentation Representation, bool CreateNew, bool IgnoreGuids, MqttBroker Broker)
104 {
105 string CurrentSegment = Representation.CurrentSegment;
106 MqttTopic Topic, Topic2;
107
108 lock (this.topics)
109 {
110 if (this.topics.TryGetValue(CurrentSegment, out Topic))
111 return Topic;
112 }
113
114 if (IgnoreGuids && Guid.TryParse(CurrentSegment.Replace('_', '-'), out Guid _))
115 return null;
116
117 if (this.node.HasChildren)
118 {
119 foreach (INode Child in await this.node.ChildNodes)
120 {
121 if (Child is IMqttTopicNode TopicNode && TopicNode.LocalTopic == CurrentSegment)
122 {
123 lock (this.topics)
124 {
125 if (this.topics.TryGetValue(CurrentSegment, out Topic2))
126 return Topic2;
127 else
128 {
129 Topic = new MqttTopic(TopicNode, Representation.ProcessedSegments, CurrentSegment, null, Broker);
130 this.topics[CurrentSegment] = Topic;
131 return Topic;
132 }
133 }
134 }
135 }
136 }
137
138 if (!CreateNew)
139 return null;
140
141 IMqttTopicNode AddNode = Types.FindBest<IMqttTopicNode, MqttTopicRepresentation>(Representation);
142 if (AddNode is null)
143 return null;
144
145 AddNode = await AddNode.CreateNew(Representation);
146 Topic = new MqttTopic(AddNode, Representation.ProcessedSegments, AddNode.LocalTopic, null, Broker);
147
148 lock (this.topics)
149 {
150 if (this.topics.TryGetValue(CurrentSegment, out Topic2))
151 return Topic2;
152 else
153 this.topics[CurrentSegment] = Topic;
154 }
155
156 await this.node.AddAsync(AddNode);
157
158 return Topic;
159 }
160
166 public void SetData<T>(T Data)
167 where T : IMqttData
168 {
169 this.data = Data;
170 }
171
176 public async Task DataReported(MqttContent Content)
177 {
178 int Len = Content.Data.Length;
179 if (Len == 0)
180 {
181 this.data = null;
182 return;
183 }
184
185 this.dataCount += Len;
186
187 bool NewMomentaryValues;
188
189 try
190 {
191 if (this.data is null)
192 this.data = this.FindDataType(Content).CreateNew(this, Content);
193
194 switch (await this.data.DataReported(this, Content))
195 {
196 case DataProcessingResult.Incompatible:
197 default:
198 this.data = null;
199 this.data = this.FindDataType(Content).CreateNew(this, Content);
200 NewMomentaryValues = false;
201 break;
202
203 case DataProcessingResult.Processed:
204 NewMomentaryValues = false;
205 break;
206
207 case DataProcessingResult.ProcessedNewMomentaryValues:
208 NewMomentaryValues = true;
209 break;
210 }
211
212 await this.SetOk();
213 }
214 catch (Exception)
215 {
216 this.data = this.FindDataType(Content).CreateNew(this, Content);
217 NewMomentaryValues = false;
218 }
219
220 if (this.broker.Client?.HasSniffers ?? false)
221 this.data.SnifferOutput(this.broker.Client);
222
223 if (NewMomentaryValues)
224 {
225 try
226 {
227 InternalReadoutRequest Request = new InternalReadoutRequest(string.Empty,
228 new IThingReference[] { this.node }, FieldType.Momentary, null, DateTime.MinValue, DateTime.MaxValue,
229 async (Sender, e) =>
230 {
231 await this.node.NewMomentaryValues(e.Fields);
232
233 MqttTopic Current = this;
234 MqttTopic Parent = this.parent;
235
236 while (!(Parent is null))
237 {
238 foreach (Field F in e.Fields)
239 {
240 if (F.Name == "Value")
241 F.Name = Current.localTopic;
242 else
243 F.Name = Current.localTopic + ", " + F.Name;
244
245 await Parent.node.NewMomentaryValues(F);
246 }
247
248 Current = Parent;
249 Parent = Parent.parent;
250 }
251 },
252 (Sender, e) =>
253 {
254 return Task.CompletedTask;
255 }, null);
256
257 await this.StartReadout(Request, true);
258 }
259 catch (Exception ex)
260 {
261 await this.Exception(ex);
262 }
263 }
264 }
265
272 {
273 try
274 {
275 IMqttData Data = Types.FindBest<IMqttData, MqttContent>(Content);
276 if (!(Data is null))
277 return Data;
278
279 return new BinaryData(this, Content.Data);
280 }
281 catch (Exception)
282 {
283 return new BinaryData(this, Content.Data);
284 }
285 }
286
287 private Task SetOk()
288 {
289 this.ex = null;
290 this.exTP = DateTime.MinValue;
291
292 return this.node.RemoveErrorAsync("Error");
293 }
294
295 private Task Exception(Exception ex)
296 {
297 this.ex = ex;
298 this.exTP = DateTime.UtcNow;
299
300 return this.node.LogErrorAsync("Error", ex.Message);
301 }
302
306 public override string ToString()
307 {
308 return this.fullTopic;
309 }
310
317 public Task StartReadout(ISensorReadout Request, bool DoneAfter)
318 {
319 return this.StartReadout(this.nodeReference, Request, string.Empty, DoneAfter);
320 }
321
325 public async Task StartReadout(ThingReference ThingReference, ISensorReadout Request, string Prefix, bool Last)
326 {
327 try
328 {
329 MqttTopic[] ChildNodes = this.GetChildNodes();
330
331 if (!(ChildNodes is null) && ChildNodes.Length > 0)
332 {
333 foreach (MqttTopic ChildTopic in ChildNodes)
334 {
335 await ChildTopic.StartReadout(ThingReference, Request,
336 string.IsNullOrEmpty(Prefix) ? ChildTopic.LocalTopic : Prefix + ", " + ChildTopic.LocalTopic, false);
337 }
338 }
339
340 if (!(this.ex is null))
341 await Request.ReportErrors(Last, new ThingError(ThingReference, this.exTP, this.ex.Message));
342 else if (this.data is null)
343 {
344 this.data = await this.node.GetDefaultDataObject();
345
346 if (this.data is null)
347 {
348 if (Last)
349 await Request.ReportFields(true);
350 }
351 else
352 await this.data.StartReadout(ThingReference, Request, Prefix, Last);
353 }
354 else
355 await this.data.StartReadout(ThingReference, Request, Prefix, Last);
356
357 await this.node.RemoveErrorAsync("Readout");
358 }
359 catch (Exception ex)
360 {
361 await Request.ReportErrors(Last, new ThingError(ThingReference, DateTime.UtcNow, ex.Message));
362 await this.node.LogErrorAsync("Readout", ex.Message);
363 }
364 }
365
369 public bool IsControllable
370 {
371 get => this.data?.IsControllable ?? false;
372 }
373
379 {
380 if (this.data is null || !this.data.IsControllable)
381 return Array.Empty<ControlParameter>();
382 else
383 return this.data.GetControlParameters();
384 }
385
389 public async Task<IEnumerable<Parameter>> GetDisplayableParametersAsync(LinkedList<Parameter> Parameters,
391 {
392 if (!(this.data is null))
393 {
394 Parameters.AddLast(new StringParameter("Type", await Language.GetStringAsync(typeof(MqttTopicNode), 25, "Type"),
395 await this.data.GetTypeName(Language)));
396 }
397
398 if (this.dataCount > 0)
399 {
400 Parameters.AddLast(new Int64Parameter("Data Count", await Language.GetStringAsync(typeof(MqttTopicNode), 26, "Data Count"),
401 this.dataCount));
402 }
403
404 return Parameters;
405 }
406
412 public bool Remove(string LocalTopic)
413 {
414 if (!(LocalTopic is null))
415 {
416 lock (this.topics)
417 {
418 return this.topics.Remove(LocalTopic);
419 }
420 }
421 else
422 return false;
423 }
424
428 public MqttClient MqttClient => this.broker?.Client;
429 }
430}
Manages an MQTT connection. Implements MQTT v3.1.1, as defined in http://docs.oasis-open....
Definition: MqttClient.cs:30
Information about content received from the MQTT server.
Definition: MqttContent.cs:9
Manages a chat sensor data readout request.
Static class that dynamically manages types and interfaces available in the runtime environment.
Definition: Types.cs:15
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
Abstract base class for control parameters.
Represents an MQTT topic with binary data.
Definition: BinaryData.cs:17
MQTT Broker connection object.
Definition: MqttBroker.cs:17
MQTT Topic information.
Definition: MqttTopic.cs:19
async Task DataReported(MqttContent Content)
Called when new data has been published.
Definition: MqttTopic.cs:176
MqttBroker Broker
MQTT Broker
Definition: MqttTopic.cs:56
void SetData< T >(T Data)
Sets the parsed data of a topic.
Definition: MqttTopic.cs:166
string FullTopic
Full topic name
Definition: MqttTopic.cs:66
async Task< IEnumerable< Parameter > > GetDisplayableParametersAsync(LinkedList< Parameter > Parameters, Language Language, RequestOrigin _)
TODO
Definition: MqttTopic.cs:389
ControlParameter[] GetControlParameters()
Get control parameters for the actuator.
Definition: MqttTopic.cs:378
async Task StartReadout(ThingReference ThingReference, ISensorReadout Request, string Prefix, bool Last)
TODO
Definition: MqttTopic.cs:325
MqttTopic(IMqttTopicNode Node, string FullTopic, string LocalTopic, MqttTopic Parent, MqttBroker Broker)
MQTT Topic information.
Definition: MqttTopic.cs:35
IMqttData FindDataType(MqttContent Content)
FInds best implementation to process binary data.
Definition: MqttTopic.cs:271
bool IsControllable
If the node can be controlled.
Definition: MqttTopic.cs:370
override string ToString()
TODO
Definition: MqttTopic.cs:306
string LocalTopic
Local topic name.
Definition: MqttTopic.cs:61
IMqttTopicNode Node
Reference to the MQTT Topic Node
Definition: MqttTopic.cs:51
bool Remove(string LocalTopic)
Removes a child topic
Definition: MqttTopic.cs:412
IMqttData Data
Current parsed data.
Definition: MqttTopic.cs:71
Task StartReadout(ISensorReadout Request, bool DoneAfter)
Starts the readout of the sensor.
Definition: MqttTopic.cs:317
A Metering node representing an MQTT topic
Contains information about an MQTT topic
bool MoveNext(MqttTopic NewParent)
Moves to the next segment.
string CurrentSegment
Current segment being processed.
Tokens available in request.
Definition: RequestOrigin.cs:9
Base class for all sensor data fields.
Definition: Field.cs:20
Contains information about an error on a thing
Definition: ThingError.cs:10
Contains a reference to a thing
Interface for nodes that are published through the concentrator interface.
Definition: INode.cs:49
Task< IEnumerable< INode > > ChildNodes
Child nodes. If no child nodes are available, null is returned.
Definition: INode.cs:140
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.
Interface for thing references.
string Partition
Optional partition in which the Node ID is unique.
string SourceId
Optional ID of source containing node.
Task NewMomentaryValues(params Field[] Values)
Reports newly measured values.
Interface for MQTT Topic nodes.
string LocalTopic
Local Topic segment
Task< IMqttTopicNode > CreateNew(MqttTopicRepresentation Topic)
Creates a new node of the same type.
Interface for MQTT Data encapsulations
Definition: IMqttData.cs:38
Task< string > GetTypeName(Language Language)
Type name representing data.
ControlParameter[] GetControlParameters()
Gets an array of control parameters
bool IsControllable
If data can be controlled (written)
Definition: IMqttData.cs:62
Task StartReadout(ThingReference ThingReference, ISensorReadout Request, string Prefix, bool Last)
Starts a readout of the data.
Task< DataProcessingResult > DataReported(MqttTopic Topic, MqttContent Content)
Called when new data has been published.
IMqttData CreateNew(MqttTopic Topic, MqttContent Content)
Creates a new instance of the data.
void SnifferOutput(ICommunicationLayer Output)
Outputs the parsed data to the sniffer.
Definition: ImplTypes.g.cs:58
DataProcessingResult
Results from processing an incoming message.
Definition: IMqttData.cs:17
FieldType
Field Type flags
Definition: FieldType.cs:10