Neuron®
The Neuron® is the basis for the creation of open and secure federated networks for smart societies.
Loading...
Searching...
No Matches
GraphModel.cs
3using SkiaSharp;
4using System.ComponentModel;
5using System.Text;
6using Waher.Events;
7using Waher.Script;
11
13{
17 public class GraphModel : INotifyPropertyChanged, IDisposable
18 {
19 private readonly SortedDictionary<DateTime, Field> fieldValues = [];
20 private readonly SortedDictionary<DateTime, Field> minFieldValues = [];
21 private readonly SortedDictionary<DateTime, Field> maxFieldValues = [];
22 private readonly string fieldName;
23 private Timer? timer = null;
24 private ImageSource? image = null;
25
31 {
32 this.fieldName = Field.Name;
33
34 this.Add(Field);
35 }
36
40 public string FieldName => this.fieldName;
41
45 public ImageSource? Image => this.image;
46
50 public bool HasImage => this.image is not null;
51
56 public void Add(Field Field)
57 {
58 lock (this.fieldValues)
59 {
60 this.fieldValues[Field.Timestamp] = Field;
61 }
62
63 this.Invalidate();
64 }
65
70 public void AddMin(Field Field)
71 {
72 lock (this.fieldValues)
73 {
74 this.minFieldValues[Field.Timestamp] = Field;
75 }
76
77 this.Invalidate();
78 }
79
84 public void AddMax(Field Field)
85 {
86 lock (this.fieldValues)
87 {
88 this.maxFieldValues[Field.Timestamp] = Field;
89 }
90
91 this.Invalidate();
92 }
93
94 private void Invalidate()
95 {
96 this.timer?.Dispose();
97 this.timer = null;
98
99 this.timer = new Timer(this.GenerateGraph, null, 500, Timeout.Infinite);
100 }
101
102 private async void GenerateGraph(object? _)
103 {
104 try
105 {
106 Variables v = [];
107 Expression Exp;
108 bool HasMin = false;
109 bool HasMax = false;
110
111 lock (this.fieldValues)
112 {
113 int c = this.fieldValues.Count;
114 DateTime[] Timepoints = new DateTime[c];
115 Field[] Values = new Field[c];
116
117 this.fieldValues.Keys.CopyTo(Timepoints, 0);
118 this.fieldValues.Values.CopyTo(Values, 0);
119
120 v["x"] = new DateTimeVector(Timepoints);
121 v["y"] = new ObjectVector(Values);
122
123 if (this.minFieldValues.Count > 0)
124 {
125 c = this.minFieldValues.Count;
126
127 Timepoints = new DateTime[c];
128 Values = new Field[c];
129
130 this.minFieldValues.Keys.CopyTo(Timepoints, 0);
131 this.minFieldValues.Values.CopyTo(Values, 0);
132
133 v["xMin"] = new DateTimeVector(Timepoints);
134 v["yMin"] = new ObjectVector(Values);
135
136 HasMin = true;
137 }
138
139 if (this.maxFieldValues.Count > 0)
140 {
141 c = this.maxFieldValues.Count;
142
143 Timepoints = new DateTime[c];
144 Values = new Field[c];
145
146 this.maxFieldValues.Keys.CopyTo(Timepoints, 0);
147 this.maxFieldValues.Values.CopyTo(Values, 0);
148
149 v["xMax"] = new DateTimeVector(Timepoints);
150 v["yMax"] = new ObjectVector(Values);
151
152 HasMax = true;
153 }
154 }
155
156 StringBuilder sb = new();
157 bool Found = false;
158
159 sb.AppendLine("y:=y.Value;");
160 sb.AppendLine("G:=scatter2d(x,y,'Black',7);");
161
162 if (HasMin)
163 {
164 sb.AppendLine("yMin:=yMin.Value;");
165 sb.AppendLine("G+=scatter2d(xMin,yMin,'Black',7);");
166 }
167
168 if (HasMax)
169 {
170 sb.AppendLine("yMax:=yMax.Value;");
171 sb.AppendLine("G+=scatter2d(xMax,yMax,'Black',7);");
172 }
173
174 if (HasMin)
175 sb.AppendLine("G+=plot2dline(xMin,yMin,'Blue',5);");
176
177 if (HasMax)
178 sb.AppendLine("G+=plot2dline(xMax,yMax,'Red',5);");
179
180 sb.AppendLine("G+=plot2dline(x,y,'Green',5);");
181 sb.Append("G.LabelX:='");
182 sb.Append(ServiceRef.Localizer[nameof(AppResources.Time)]);
183 sb.AppendLine("';");
184 sb.Append("G.LabelY:='");
185
186 foreach (Field F in this.fieldValues.Values)
187 {
188 if (F is QuantityField Q)
189 {
190 sb.Append(Q.Unit);
191 Found = true;
192 break;
193 }
194 }
195
196 if (!Found)
197 sb.Append(this.fieldName.Replace("\\", "\\\\").Replace("\n", "\\n").Replace("\r", "\\r").Replace("\t", "\\t").Replace("'", "\\'"));
198
199 sb.AppendLine("';");
200 sb.AppendLine("G");
201
202 Exp = new Expression(sb.ToString());
203
204 Graph? Graph = await Exp.EvaluateAsync(v) as Graph;
205
206 if (Graph is not null)
207 {
208 GraphSettings Settings = new()
209 {
210 Width = 1280,
211 Height = 720,
212 AxisColor = SKColors.Black, // TODO: Light & Dark Theme
213 BackgroundColor = SKColors.White, // TODO: Light & Dark Theme
214 GridColor = SKColors.LightGray, // TODO: Light & Dark Theme
215 AxisWidth = 5,
216 FontName = "Arial", // TODO: App font
217 LabelFontSize = 40,
218 GridWidth = 3
219 };
220
221 PixelInformation Pixels = Graph.CreatePixels(Settings);
222 byte[] Png = Pixels.EncodeAsPng();
223
224 MainThread.BeginInvokeOnMainThread(() =>
225 {
226 bool OldWasNull = this.image is null;
227 this.image = ImageSource.FromStream(() => new MemoryStream(Png));
228
229 this.RaisePropertyChanged(nameof(this.Image));
230 if (OldWasNull)
231 this.RaisePropertyChanged(nameof(this.HasImage));
232 });
233 }
234 }
235 catch (Exception ex)
236 {
237 Log.Exception(ex);
238 }
239 }
240
241 private void RaisePropertyChanged(string Name)
242 {
243 try
244 {
245 this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(Name));
246 }
247 catch (Exception ex)
248 {
249 Log.Exception(ex);
250 }
251 }
252
256 public void Dispose()
257 {
258 this.Dispose(true);
259 GC.SuppressFinalize(this);
260 }
261
265 protected virtual void Dispose(bool disposing)
266 {
267 this.timer?.Dispose();
268 this.timer = null;
269 }
270
274 public event PropertyChangedEventHandler? PropertyChanged;
275 }
276}
Base class that references services in the app.
Definition: ServiceRef.cs:31
static IStringLocalizer Localizer
Localization service
Definition: ServiceRef.cs:235
Represents a set of historical field values.
Definition: GraphModel.cs:18
void Add(Field Field)
Adds a historical field value.
Definition: GraphModel.cs:56
virtual void Dispose(bool disposing)
IDisposable.Dispose
Definition: GraphModel.cs:265
void AddMax(Field Field)
Adds a historical maximum field value.
Definition: GraphModel.cs:84
PropertyChangedEventHandler? PropertyChanged
Event raised when a property has changed.
Definition: GraphModel.cs:274
void AddMin(Field Field)
Adds a historical minimum field value.
Definition: GraphModel.cs:70
GraphModel(Field Field)
Represents a set of historical field values.
Definition: GraphModel.cs:30
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
Class managing a script expression.
Definition: Expression.cs:39
async Task< object > EvaluateAsync(Variables Variables)
Evaluates the expression, using the variables provided in the Variables collection....
Definition: Expression.cs:4275
Base class for graphs.
Definition: Graph.cs:79
PixelInformation CreatePixels()
Creates a bitmap of the graph.
Definition: Graph.cs:362
Contains pixel information
virtual byte[] EncodeAsPng()
Encodes the pixels into a binary PNG image.
Collection of variables.
Definition: Variables.cs:25
Base class for all sensor data fields.
Definition: Field.cs:20
string Name
Unlocalized field name.
Definition: Field.cs:279
DateTime Timestamp
Timestamp of field value.
Definition: Field.cs:202
Represents a physical quantity value.