Neuron®
The Neuron® is the basis for the creation of open and secure federated networks for smart societies.
Loading...
Searching...
No Matches
SensorDataView.xaml.cs
1using System;
2using System.IO;
3using System.Threading.Tasks;
4using System.Collections.Generic;
5using System.Text;
6using System.Xml;
7using System.Xml.Schema;
8using System.Xml.Xsl;
9using System.Windows;
10using System.Windows.Controls;
11using Microsoft.Win32;
14using Waher.Events;
16using Waher.Things;
20
22{
26 public partial class SensorDataView : UserControl, ITabView
27 {
28 private SensorDataClientRequest request;
29 private readonly TreeNode node;
30 private readonly Dictionary<string, bool> nodes = new Dictionary<string, bool>();
31 private readonly Dictionary<string, bool> failed = new Dictionary<string, bool>();
32 private readonly bool subscription;
33
35 {
36 this.request = Request;
37 this.node = Node;
38 this.subscription = Subscription;
39
41
42 if (!(this.request is null))
43 {
44 this.Request_OnErrorsReceived(this, Request.Errors);
45 this.request.OnErrorsReceived += this.Request_OnErrorsReceived;
46
47 this.Request_OnFieldsReceived(this, Request.ReadFields);
48 this.request.OnFieldsReceived += this.Request_OnFieldsReceived;
49
50 this.Request_OnStateChanged(this, Request.State);
51 this.request.OnStateChanged += this.Request_OnStateChanged;
52 }
53 }
54
55 [Obsolete("Use DisposeAsync() instead.")]
56 public void Dispose()
57 {
58 try
59 {
60 this.DisposeAsync().Wait();
61 }
62 catch (Exception ex)
63 {
64 Log.Exception(ex);
65 }
66 }
67
68 public async Task DisposeAsync()
69 {
70 if (!(this.request is null))
71 {
73 await Subscription.Unsubscribe();
74
75 this.request.OnStateChanged -= this.Request_OnStateChanged;
76 this.request.OnFieldsReceived -= this.Request_OnFieldsReceived;
77 this.request.OnErrorsReceived -= this.Request_OnErrorsReceived;
78 }
79
80 if (this.SensorDataListView.View is GridView GridView)
81 {
82 Registry.SetValue(MainWindow.registryKey, "SensorDataTimestampWidth", (int)GridView.Columns[0].Width, RegistryValueKind.DWord);
83 Registry.SetValue(MainWindow.registryKey, "SensorDataFieldWidth", (int)GridView.Columns[1].Width, RegistryValueKind.DWord);
84 Registry.SetValue(MainWindow.registryKey, "SensorDataValueWidth", (int)GridView.Columns[2].Width, RegistryValueKind.DWord);
85 Registry.SetValue(MainWindow.registryKey, "SensorDataUnitWidth", (int)GridView.Columns[3].Width, RegistryValueKind.DWord);
86 Registry.SetValue(MainWindow.registryKey, "SensorDataStatusWidth", (int)GridView.Columns[4].Width, RegistryValueKind.DWord);
87 Registry.SetValue(MainWindow.registryKey, "SensorDataTypeWidth", (int)GridView.Columns[5].Width, RegistryValueKind.DWord);
88 }
89
90 this.Node?.ViewClosed();
91 }
92
93 public TreeNode Node => this.node;
94
95 private void UserControl_SizeChanged(object Sender, SizeChangedEventArgs e)
96 {
97 if (this.SensorDataListView.View is GridView GridView)
98 {
99 GridView.Columns[2].Width = Math.Max(this.ActualWidth - GridView.Columns[0].ActualWidth - GridView.Columns[1].ActualWidth -
100 GridView.Columns[3].ActualWidth - GridView.Columns[4].ActualWidth - GridView.Columns[5].ActualWidth -
101 SystemParameters.VerticalScrollBarWidth - 8, 10);
102 }
103 }
104
105 private Task Request_OnErrorsReceived(object _, IEnumerable<ThingError> NewErrors)
106 {
107 MainWindow.UpdateGui(this.OnErrorsReceived, NewErrors);
108 return Task.CompletedTask;
109 }
110
111 private Task OnErrorsReceived(object P)
112 {
113 IEnumerable<ThingError> NewErrors = (IEnumerable<ThingError>)P;
114 string LastKey = null;
115 string Key;
116
117 lock (this.nodes)
118 {
119 foreach (ThingError Error in NewErrors)
120 {
121 Key = Error.Key;
122 if (LastKey is null || Key != LastKey)
123 {
124 LastKey = Key;
125 this.failed[Key] = true;
126 this.nodes[Key] = true;
127 }
128
129 this.SensorDataListView.Items.Add(new ErrorItem(Error));
130 }
131
132 this.NodesFailedLabel.Content = this.failed.Count.ToString();
133 this.NodesTotalLabel.Content = this.nodes.Count.ToString();
134 }
135
136 return Task.CompletedTask;
137 }
138
139 private Task Request_OnFieldsReceived(object _, IEnumerable<Field> NewFields)
140 {
141 MainWindow.UpdateGui(this.OnFieldsReceived, NewFields);
142 return Task.CompletedTask;
143 }
144
145 private Task OnFieldsReceived(object P)
146 {
147 IEnumerable<Field> NewFields = (IEnumerable<Field>)P;
148 string LastKey = null;
149 string Key;
150
151 lock (this.nodes)
152 {
153 foreach (Field Field in NewFields)
154 {
155 Key = Field.Thing.Key;
156 if (LastKey is null || Key != LastKey)
157 {
158 LastKey = Key;
159 this.nodes[Key] = true;
160 }
161
162 this.SensorDataListView.Items.Add(new FieldItem(Field));
163 }
164
165 this.NodesTotalLabel.Content = this.nodes.Count.ToString();
166 this.FieldsLabel.Content = this.SensorDataListView.Items.Count.ToString();
167 }
168
169 return Task.CompletedTask;
170 }
171
172 private Task Request_OnStateChanged(object _, SensorDataReadoutState NewState)
173 {
174 MainWindow.UpdateGui(this.OnStateChanged, NewState);
175 return Task.CompletedTask;
176 }
177
178 private async Task OnStateChanged(object P)
179 {
181
182 switch (NewState)
183 {
184 case SensorDataReadoutState.Requested:
185 this.StateLabel.Content = "Requested";
186 break;
187
188 case SensorDataReadoutState.Accepted:
189 this.StateLabel.Content = "Accepted";
190 break;
191
192 case SensorDataReadoutState.Started:
193 this.StateLabel.Content = "Started";
194 break;
195
196 case SensorDataReadoutState.Receiving:
197 this.StateLabel.Content = "Receiving";
198 break;
199
200 case SensorDataReadoutState.Done:
201 this.StateLabel.Content = "Done";
202 await this.Done();
203 break;
204
205 case SensorDataReadoutState.Cancelled:
206 this.StateLabel.Content = "Cancelled";
207 break;
208
209 case SensorDataReadoutState.Failure:
210 this.StateLabel.Content = "Failure";
211 await this.Done();
212 break;
213
214 default:
215 this.StateLabel.Content = NewState.ToString();
216 break;
217 }
218 }
219
220 private async Task Done()
221 {
222 lock (this.nodes)
223 {
224 this.NodesTotalLabel.Content = this.nodes.Count;
225 this.NodesOkLabel.Content = this.nodes.Count - this.failed.Count;
226 }
227
228 if (this.subscription && !(this.request is SensorDataSubscriptionRequest))
229 {
230 List<FieldSubscriptionRule> Rules = new List<FieldSubscriptionRule>();
231 Field Field;
232
233 foreach (FieldItem FieldItem in this.SensorDataListView.Items)
234 {
236
237 if (Field is BooleanField B)
238 Rules.Add(new FieldSubscriptionRule(Field.Name, B.Value ? 1 : 0, 1));
239 else if (Field is QuantityField Q)
240 Rules.Add(new FieldSubscriptionRule(Field.Name, Q.Value, 1));
241 else if (Field is Int32Field I32)
242 Rules.Add(new FieldSubscriptionRule(Field.Name, I32.Value, 1));
243 else if (Field is Int64Field I64)
244 Rules.Add(new FieldSubscriptionRule(Field.Name, I64.Value, 1));
246 Rules.Add(new FieldSubscriptionRule(Field.Name, string.Empty, 1));
247 }
248
249 this.request.OnStateChanged -= this.Request_OnStateChanged;
250 this.request.OnFieldsReceived -= this.Request_OnFieldsReceived;
251 this.request.OnErrorsReceived -= this.Request_OnErrorsReceived;
252 this.request = null;
253
254 this.request = await this.node.SubscribeSensorDataMomentaryReadout(Rules.ToArray());
255
256 this.request.OnStateChanged += this.Request_OnStateChanged;
257 this.request.OnFieldsReceived += this.Request_OnFieldsReceived;
258 this.request.OnErrorsReceived += this.Request_OnErrorsReceived;
259 }
260 }
261
262 public void NewButton_Click(object Sender, RoutedEventArgs e)
263 {
264 this.SensorDataListView.Items.Clear();
265 }
266
267 public void SaveButton_Click(object Sender, RoutedEventArgs e)
268 {
269 this.SaveAsButton_Click(Sender, e);
270 }
271
272 public void SaveAsButton_Click(object Sender, RoutedEventArgs e)
273 {
274 SaveFileDialog Dialog = new SaveFileDialog()
275 {
276 AddExtension = true,
277 CheckPathExists = true,
278 CreatePrompt = false,
279 DefaultExt = "html",
280 Filter = "XML Files (*.xml)|*.xml|HTML Files (*.html,*.htm)|*.html,*.htm|All Files (*.*)|*.*",
281 Title = "Save Sensor data readout"
282 };
283
284 bool? Result = Dialog.ShowDialog(MainWindow.FindWindow(this));
285
286 if (Result.HasValue && Result.Value)
287 {
288 try
289 {
290 if (Dialog.FilterIndex == 2)
291 {
292 StringBuilder Xml = new StringBuilder();
293 using (XmlWriter w = XmlWriter.Create(Xml, XML.WriterSettings(true, true)))
294 {
295 this.SaveAsXml(w);
296 }
297
298 string Html = XSL.Transform(Xml.ToString(), sensorDataToHtml);
299
300 File.WriteAllText(Dialog.FileName, Html, System.Text.Encoding.UTF8);
301 }
302 else
303 {
304 using (FileStream f = File.Create(Dialog.FileName))
305 {
306 using (XmlWriter w = XmlWriter.Create(f, XML.WriterSettings(true, false)))
307 {
308 this.SaveAsXml(w);
309 }
310 }
311 }
312 }
313 catch (Exception ex)
314 {
315 MessageBox.Show(MainWindow.FindWindow(this), ex.Message, "Unable to save file.", MessageBoxButton.OK, MessageBoxImage.Error);
316 }
317 }
318 }
319
320 private static readonly XslCompiledTransform sensorDataToHtml = XSL.LoadTransform("Waher.Client.WPF.Transforms.SensorDataToHTML.xslt");
321 private static readonly XmlSchema schema1 = XSL.LoadSchema("Waher.Client.WPF.Schema.SensorData.xsd");
322 private static readonly XmlSchema schema2 = XSL.LoadSchema("Waher.Client.WPF.Schema.sensor-data.xsd");
323 private const string sensorDataNamespace = "http://waher.se/Schema/SensorData.xsd";
324 private const string sensorDataRoot = "SensorData";
325
326 private void SaveAsXml(XmlWriter w)
327 {
328 List<Field> Fields = new List<Field>();
329
330 foreach (FieldItem Item in this.SensorDataListView.Items)
331 Fields.Add(Item.Field);
332
333 w.WriteStartElement(sensorDataRoot, sensorDataNamespace);
334 w.WriteAttributeString("state", this.StateLabel.Content.ToString());
335 w.WriteAttributeString("nodesOk", this.NodesOkLabel.Content.ToString());
336 w.WriteAttributeString("nodesFailed", this.NodesFailedLabel.Content.ToString());
337 w.WriteAttributeString("nodesTotal", this.NodesTotalLabel.Content.ToString());
338 w.WriteAttributeString("fields", this.FieldsLabel.Content.ToString());
339 // TODO: Nr errors.
340
341 if (Fields.Count > 0)
342 SensorDataServerRequest.OutputFields(w, Fields, this.request.Id, true, null);
343
344 w.WriteEndElement();
345 w.Flush();
346 }
347
348 public void OpenButton_Click(object Sender, RoutedEventArgs e)
349 {
350 try
351 {
352 OpenFileDialog Dialog = new OpenFileDialog()
353 {
354 AddExtension = true,
355 CheckFileExists = true,
356 CheckPathExists = true,
357 DefaultExt = "xml",
358 Filter = "XML Files (*.xml)|*.xml|All Files (*.*)|*.*",
359 Multiselect = false,
360 ShowReadOnly = true,
361 Title = "Open sensor data readout"
362 };
363
364 bool? Result = Dialog.ShowDialog(MainWindow.FindWindow(this));
365
366 if (Result.HasValue && Result.Value)
367 {
368 XmlDocument Xml = new XmlDocument()
369 {
370 PreserveWhitespace = true
371 };
372 Xml.Load(Dialog.FileName);
373
374 this.Load(Xml, Dialog.FileName);
375 }
376 }
377 catch (Exception ex)
378 {
379 ex = Log.UnnestException(ex);
380 MessageBox.Show(ex.Message, "Unable to load file.", MessageBoxButton.OK, MessageBoxImage.Error);
381 }
382 }
383
384 public void Load(XmlDocument Xml, string FileName)
385 {
386 XmlElement E;
387
388 XSL.Validate(FileName, Xml, sensorDataRoot, sensorDataNamespace, schema1, schema2);
389
390 this.SensorDataListView.Items.Clear();
391 this.nodes.Clear();
392 this.failed.Clear();
393
394 this.StateLabel.Content = XML.Attribute(Xml.DocumentElement, "state", string.Empty);
395 this.NodesOkLabel.Content = XML.Attribute(Xml.DocumentElement, "nodesOk", string.Empty);
396 this.NodesFailedLabel.Content = XML.Attribute(Xml.DocumentElement, "nodesFailed", string.Empty);
397 this.NodesTotalLabel.Content = XML.Attribute(Xml.DocumentElement, "nodesTotal", string.Empty);
398 this.FieldsLabel.Content = XML.Attribute(Xml.DocumentElement, "fields", string.Empty);
399
400 foreach (XmlNode N in Xml.DocumentElement.ChildNodes)
401 {
402 E = N as XmlElement;
403 if (E is null)
404 continue;
405
406 switch (E.LocalName)
407 {
408 case "resp":
409 Tuple<List<Field>, List<ThingError>> Response = SensorClient.ParseFields(E, out bool _);
410
411 if (!(Response.Item1 is null))
412 {
413 foreach (Field Field in Response.Item1)
414 {
415 this.nodes[Field.Thing.Key] = true;
416 this.SensorDataListView.Items.Add(new FieldItem(Field));
417 }
418 }
419
420 if (!(Response.Item2 is null))
421 {
422 foreach (ThingError Error in Response.Item2)
423 {
424 string Key = Error.Key;
425 this.failed[Key] = true;
426 this.nodes[Key] = true;
427 }
428 }
429
430 this.NodesFailedLabel.Content = this.failed.Count.ToString();
431 this.NodesTotalLabel.Content = this.nodes.Count.ToString();
432
433 break;
434 }
435 }
436 }
437
438 private void UserControl_Loaded(object Sender, RoutedEventArgs e)
439 {
440 if (this.SensorDataListView.View is GridView GridView)
441 {
442 object Value;
443
444 Value = Registry.GetValue(MainWindow.registryKey, "SensorDataTimestampWidth", (int)GridView.Columns[0].Width);
445 if (!(Value is null) && Value is int i && i > 0)
446 GridView.Columns[0].Width = i;
447
448 Value = Registry.GetValue(MainWindow.registryKey, "SensorDataFieldWidth", (int)GridView.Columns[1].Width);
449 if (!(Value is null) && Value is int i2 && i2 > 0)
450 GridView.Columns[1].Width = i2;
451
452 Value = Registry.GetValue(MainWindow.registryKey, "SensorDataValueWidth", (int)GridView.Columns[2].Width);
453 if (!(Value is null) && Value is int i3 && i3 > 0)
454 GridView.Columns[2].Width = i3;
455
456 Value = Registry.GetValue(MainWindow.registryKey, "SensorDataUnitWidth", (int)GridView.Columns[3].Width);
457 if (!(Value is null) && Value is int i4 && i4 > 0)
458 GridView.Columns[3].Width = i4;
459
460 Value = Registry.GetValue(MainWindow.registryKey, "SensorDataStatusWidth", (int)GridView.Columns[4].Width);
461 if (!(Value is null) && Value is int i5 && i5 > 0)
462 GridView.Columns[4].Width = i5;
463
464 Value = Registry.GetValue(MainWindow.registryKey, "SensorDataTypeWidth", (int)GridView.Columns[5].Width);
465 if (!(Value is null) && Value is int i6 && i6 > 0)
466 GridView.Columns[5].Width = i6;
467 }
468
469 }
470
471 }
472}
Represents one item in a sensor data readout.
Definition: ErrorItem.cs:12
Represents one item in a sensor data readout.
Definition: FieldItem.cs:14
Interaction logic for SensorDataView.xaml
void InitializeComponent()
InitializeComponent
Interaction logic for xaml
Abstract base class for tree nodes in the connection view.
Definition: TreeNode.cs:24
virtual void ViewClosed()
Method called when the view has been closed.
Definition: TreeNode.cs:865
Helps with common XML-related tasks.
Definition: XML.cs:19
static string Attribute(XmlElement E, string Name)
Gets the value of an XML attribute.
Definition: XML.cs:914
static XmlWriterSettings WriterSettings(bool Indent, bool OmitXmlDeclaration)
Gets an XML writer settings object.
Definition: XML.cs:1177
Static class managing loading of XSL resources stored as embedded resources or in content files.
Definition: XSL.cs:15
static XmlSchema LoadSchema(string ResourceName)
Loads an XML schema from an embedded resource.
Definition: XSL.cs:23
static XslCompiledTransform LoadTransform(string ResourceName)
Loads an XSL transformation from an embedded resource.
Definition: XSL.cs:70
static string Transform(string XML, XslCompiledTransform Transform)
Transforms an XML document using an XSL transform.
Definition: XSL.cs:162
static void Validate(string ObjectID, XmlDocument Xml, params XmlSchema[] Schemas)
Validates an XML document given a set of XML schemas.
Definition: XSL.cs:118
Static class managing the application event log. Applications and services log events on this static ...
Definition: Log.cs:13
static void Exception(Exception Exception, string Object, string Actor, string EventId, EventLevel Level, string Facility, string Module, params KeyValuePair< string, object >[] Tags)
Logs an exception. Event type will be determined by the severity of the exception.
Definition: Log.cs:1647
static Exception UnnestException(Exception Exception)
Unnests an exception, to extract the relevant inner exception.
Definition: Log.cs:818
Maintains the status of a field subscription rule.
Implements an XMPP sensor client interface.
Definition: SensorClient.cs:21
static SensorData ParseFields(XmlElement Content)
Parses sensor data field definitions.
Field[] ReadFields
Fields received during the readout.
ThingError[] Errors
Errors logged during the readout. If an error reference lacks a reference to a node (i....
SensorDataReadoutState State
Current state of readout.
static bool OutputFields(XmlWriter Xml, IEnumerable< Field > Fields, string Id, bool Done)
Outputs a set of fields to XML using the field format specified in the neuro-foundation....
Maintains the status of a subscription.
Definition: Subscription.cs:13
Represents a boolean value that can be either true or false.
Definition: BooleanField.cs:11
Represents a date value.
Definition: DateField.cs:11
Represents a date and optional time value.
Represents a duration value. Duration values adhere to the type specified by xsd:duration.
Base class for all sensor data fields.
Definition: Field.cs:20
ThingReference Thing
Reference to the thing to which the field belongs.
Definition: Field.cs:192
string Name
Unlocalized field name.
Definition: Field.cs:279
Represents a 32-bit integer value.
Definition: Int32Field.cs:10
Represents a 64-bit integer value.
Definition: Int64Field.cs:10
Represents a physical quantity value.
Represents a string value.
Definition: StringField.cs:10
Represents a time value. Time values adhere to the type specified by xsd:time.
Definition: TimeField.cs:14
Contains information about an error on a thing
Definition: ThingError.cs:10
string Key
Key for thing reference: [NodeId[, SourceId[, Partition]]]
Interface for tab view user controls in the client.
Definition: ITabView.cs:10
SensorDataReadoutState
Sensor Data Readout States.