Neuron®
The Neuron® is the basis for the creation of open and secure federated networks for smart societies.
Loading...
Searching...
No Matches
PersistVariable.cs
1using System;
2using System.Text;
3using System.Threading.Tasks;
4using System.Xml;
5using Waher.Content;
10using Waher.Script;
15
17{
22 {
23 private ScriptableBooleanAttribute suppressSample;
24 private Name variableName;
25 private HeaderValue value;
26 private OnlyIfChanged onlyIfChanged;
27
32 : base()
33 {
34 }
35
39 public Name VariableName => this.variableName;
40
44 public HeaderValue Value => this.value;
45
49 public OnlyIfChanged OnlyIfChanged => this.onlyIfChanged;
50
54 [DefaultValueNull]
56 {
57 get => this.suppressSample?.Definition;
58 set => this.suppressSample = new ScriptableBooleanAttribute(value, this);
59 }
60
63 [DefaultValueNull]
64 public string Name { get; set; }
65
69 public override string LocalName => nameof(PersistVariable);
70
75 public override IStateMachineNode Create()
76 {
77 return new PersistVariable();
78 }
79
84 public override async Task Parse(XmlElement Xml)
85 {
86 this.Name = XML.Attribute(Xml, "name");
87 this.suppressSample = new ScriptableBooleanAttribute(XML.Attribute(Xml, "suppressSample"), this);
88
89 await base.Parse(Xml);
90
91 if (string.IsNullOrEmpty(this.Name))
92 this.ConvertValueAttributeToElement<Name>(Xml, true);
93
94 this.ConvertValueAttributeToElement<HeaderValue>(Xml, true);
95 this.ConvertValueAttributeToElement<OnlyIfChanged>(Xml, false);
96 }
97
101 protected override void OnChildNodesUpdated()
102 {
103 base.OnChildNodesUpdated();
104
105 if (string.IsNullOrEmpty(this.Name))
106 this.variableName = this.GetValueElement<Name>();
107
108 this.value = this.GetValueElement<HeaderValue>();
109 this.onlyIfChanged = this.GetValueElement<OnlyIfChanged>();
110 }
111
117 public async Task<bool> GetSuppressSample(EvaluationArguments Arguments)
118 {
119 if (this.suppressSample?.IsEmpty ?? true)
120 return false;
121
122 return await this.suppressSample.Evaluate(Arguments.Variables);
123 }
124
129 public override async Task Execute(EvaluationArguments Arguments)
130 {
131 object Value = await this.value.Evaluate(Arguments);
132 bool OnlyIfChanged;
133 bool SuppressSample;
134 ProfilerThread Thread;
135
136 if (this.onlyIfChanged is null)
137 OnlyIfChanged = false;
138 else if (await this.onlyIfChanged.Evaluate(Arguments) is bool b)
139 OnlyIfChanged = b;
140 else
141 throw new StateMachineException(Arguments.Machine, "OnlyIfChanged must be a boolean value.");
142
143 SuppressSample = await this.GetSuppressSample(Arguments);
144
145 string Name = this.Name;
146
147 if (string.IsNullOrEmpty(Name))
148 {
149 if (this.variableName is null)
150 throw new StateMachineException(Arguments.Machine, "Variable name not defined.");
151
152 Name = (await this.variableName.Evaluate(Arguments))?.ToString();
153 if (string.IsNullOrEmpty(Name))
154 throw new StateMachineException(Arguments.Machine, "Variable name not defined.");
155 }
156
157 if (!Arguments.Variables.TryGetVariable(Name, out Waher.Script.Variable Current))
158 Current = null;
159
160 if (OnlyIfChanged &&
161 !((Value is null) ^ (Current?.ValueObject is null)) &&
162 (Value?.Equals(Current?.ValueObject) ?? true))
163 {
164 return;
165 }
166
167 Arguments.UpdateVariable(Name, Value);
168
169 if (!SuppressSample)
170 {
172 {
173 Variable = Name,
174 Timestamp = DateTime.UtcNow,
175 Value = Value,
176 StateMachineId = Arguments.Machine.StateMachineId,
177 Expires = Arguments.Machine.Expires,
178 ArchiveOptional = Arguments.Machine.ArchiveOptional,
179 ArchiveRequired = Arguments.Machine.ArchiveRequired
180 });
181
182 if (!(Arguments.Profiler is null))
183 {
184 if (IsSample(Value, out double Sample))
185 {
186 Thread = Arguments.Profiler.GetThread(Name, ProfilerThreadType.Analog);
187
188 if (IsSample(Current?.ValueObject, out double Prev))
189 Thread.NewSample(Prev);
190
191 Thread.NewSample(Sample);
192 }
193 else if (Value is IPhysicalQuantity PQ)
194 {
196 Thread = Arguments.Profiler.GetThread(Name + " " + Q.Unit.ToString(), ProfilerThreadType.Analog);
197
198 if (Current?.ValueObject is IPhysicalQuantity Prev)
199 {
201
202 if (PrevQ.Unit.Equals(Q.Unit))
203 Thread.NewSample(PrevQ.Magnitude);
204 }
205
206 Thread.NewSample(Q.Magnitude);
207 }
208 else
209 {
210 Thread = Arguments.Profiler.GetThread(Name, ProfilerThreadType.Sequential);
211
212 if (Value is null)
213 Thread.Idle();
214 else
215 {
216 Type T = Value?.GetType() ?? typeof(object);
217 string Label;
218 int NoteIndex;
219
220 if (T.IsValueType)
221 {
223
224 if (Label.Length > 10)
225 {
226 NoteIndex = Thread.Profiler.AddNote(Label);
227 Label = "Note" + NoteIndex.ToString();
228 }
229 }
230 else if (Value is string s)
231 {
232 if (s.Length > 10)
233 {
234 NoteIndex = Thread.Profiler.AddNote(s);
235 Label = "Note" + NoteIndex.ToString();
236 }
237 else
238 Label = s.Replace('"', '\'');
239 }
240 else
241 {
242 StringBuilder sb = new StringBuilder();
243
244 sb.AppendLine("@startjson");
245 sb.AppendLine(JSON.Encode(Value, true));
246 sb.Append("@endjson");
247
248 NoteIndex = Thread.Profiler.AddNote(sb.ToString());
249 Label = "Note" + NoteIndex.ToString();
250 }
251
252 Thread.NewState(Label);
253 }
254 }
255 }
256 }
257 }
258
259 private static bool IsSample(object Value, out double Sample)
260 {
261 if (Value is null)
262 {
263 Sample = 0;
264 return false;
265 }
266 else if (Value is double d)
267 {
268 Sample = d;
269 return true;
270 }
271 else if (Value is decimal dec)
272 {
273 Sample = (double)dec;
274 return true;
275 }
276 else if (Value is float fl)
277 {
278 Sample = fl;
279 return true;
280 }
281 else if (Value is sbyte i8)
282 {
283 Sample = i8;
284 return true;
285 }
286 else if (Value is short i16)
287 {
288 Sample = i16;
289 return true;
290 }
291 else if (Value is int i32)
292 {
293 Sample = i32;
294 return true;
295 }
296 else if (Value is long i64)
297 {
298 Sample = i64;
299 return true;
300 }
301 else if (Value is byte ui8)
302 {
303 Sample = ui8;
304 return true;
305 }
306 else if (Value is ushort ui16)
307 {
308 Sample = ui16;
309 return true;
310 }
311 else if (Value is uint ui32)
312 {
313 Sample = ui32;
314 return true;
315 }
316 else if (Value is ulong ui64)
317 {
318 Sample = ui64;
319 return true;
320 }
321 else
322 {
323 Sample = 0;
324 return false;
325 }
326 }
327 }
328}
Helps with common JSON-related tasks.
Definition: JSON.cs:16
static string Encode(string s)
Encodes a string for inclusion in JSON.
Definition: JSON.cs:537
Helps with common XML-related tasks.
Definition: XML.cs:21
static string Attribute(XmlElement E, string Name)
Gets the value of an XML attribute.
Definition: XML.cs:1062
Static interface for database persistence. In order to work, a database provider has to be assigned t...
Definition: Database.cs:21
static async Task Insert(object Object)
Inserts an object into the default collection of the database.
Definition: Database.cs:97
ProfilerThread GetThread(string Name, ProfilerThreadType Type)
Gets a profiler thread. If none is available, a new is created.
Definition: Profiler.cs:145
int AddNote(object Note)
Adds a note to the profile.
Definition: Profiler.cs:716
Class that keeps track of events and timing for one thread.
void NewSample(double Sample)
A new sample value has been recored
Profiler Profiler
Profiler reference.
void NewState(string State)
Thread changes state.
Class managing a script expression.
Definition: Expression.cs:41
static string ToExpressionString(object Value)
Converts an object to a string, that can be parsed as part of an expression.
Definition: Expression.cs:5052
PhysicalQuantity ToPhysicalQuantity()
Converts underlying object to a physical quantity.
Contains information about a variable.
Definition: Variable.cs:10
virtual bool TryGetVariable(string Name, out Variable Variable)
Tries to get a variable object, given its name.
Definition: Variables.cs:56
Abstract base class for State-Machine action nodes.
Definition: ActionNode.cs:9
Limits an action to only cases where a value has changed.
Definition: OnlyIfChanged.cs:9
override IStateMachineNode Create()
Creates a new node of the corresponding type.
async Task< bool > GetSuppressSample(EvaluationArguments Arguments)
Gets a Boolean value indicating if samples should be suppressed for the event.
override void OnChildNodesUpdated()
Method called whenever ChildNodes is updated.
override async Task Execute(EvaluationArguments Arguments)
Evaluates the action node
override async Task Parse(XmlElement Xml)
Parses the State-machine node.
OnlyIfChanged OnlyIfChanged
Only persist values if they have changed.
async Task< T > Evaluate(Variables Variables)
Evaluates the attribute
Contains information required for evaluating script in a state-machine.
StateMachine Machine
Reference to state-machine definition.
void UpdateVariable(string Name, object Value)
Updates a variable.
Task< object > Evaluate(EvaluationArguments Arguments)
Evaluates the value node.
Definition: Value.cs:147
Variable definition in a State-Machine
Definition: Variable.cs:14
CaseInsensitiveString StateMachineId
ID of State Machine.
Definition: StateMachine.cs:57
Duration? ArchiveOptional
Duration after which token expires, and the required archiving time, the token can optionally be arch...
DateTime Expires
When state-machine expires
Definition: StateMachine.cs:72
Class representing a sample of a state machine variable over time.
Interface for objects that can be represented as a physical quantity.
delegate string ToString(IElement Element)
Delegate for callback methods that convert an element value to a string.
ProfilerThreadType
Type of profiler thread.
Definition: App.xaml.cs:4