3using System.Threading.Tasks;
29 private static Scheduler scheduler =
null;
31 private readonly Dictionary<string, SensorData.Field> fields =
new Dictionary<string, SensorData.Field>();
32 private List<SensorData.Field> toReport =
null;
33 private DateTime nextReport = DateTime.MinValue;
34 private bool hasReport =
false;
76 return Task.FromResult(
88 await base.AnnotatePropertyForm(Form);
90 if ((this.
MetaData?.Length ?? 0) > 0)
95 string ExternalDescription = await
Namespace.
GetStringAsync(3,
"Meta-data value is defined by external source.");
102 Form.
Pages.Add(MetaDataPage);
107 if (Tag.
Value is
string s)
110 new string[] { s },
null, ExternalDescription,
new StringDataType(),
111 null,
null,
false,
false,
false);
113 else if (Tag.
Value is
int i)
116 new string[] { i.ToString() },
null, ExternalDescription,
new IntegerDataType(),
117 null,
null,
false,
false,
false);
119 else if (Tag.
Value is
long l)
122 new string[] { l.ToString() },
null, ExternalDescription,
new LongDataType(),
123 null,
null,
false,
false,
false);
125 else if (Tag.
Value is
short sh)
128 new string[] { sh.ToString() },
null, ExternalDescription,
new ShortDataType(),
129 null,
null,
false,
false,
false);
131 else if (Tag.
Value is
byte b2)
134 new string[] { b2.ToString() },
null, ExternalDescription,
new ByteDataType(),
135 null,
null,
false,
false,
false);
137 else if (Tag.
Value is
double d)
140 new string[] { CommonTypes.Encode(d) },
null, ExternalDescription,
new DoubleDataType(),
141 null,
null,
false,
false,
false);
143 else if (Tag.
Value is decimal d2)
146 new string[] { CommonTypes.Encode(d2) },
null, ExternalDescription,
new DecimalDataType(),
147 null,
null,
false,
false,
false);
149 else if (Tag.
Value is
bool b)
152 new string[] { CommonTypes.Encode(b) },
null, ExternalDescription,
new BooleanDataType(),
153 null,
null,
false,
false,
false);
155 else if (Tag.
Value is TimeSpan TS)
158 new string[] { TS.ToString() },
null, ExternalDescription,
new TimeDataType(),
159 null,
null,
false,
false,
false);
161 else if (Tag.
Value is DateTime TP)
164 new string[] { XML.Encode(TP) },
null, ExternalDescription,
new DateTimeDataType(),
165 null,
null,
false,
false,
false);
167 else if (Tag.
Value is Uri Uri)
170 new string[] { Uri.ToString() },
null, ExternalDescription,
new AnyUriDataType(),
171 null,
null,
false,
false,
false);
173 else if (Tag.
Value is
string[] Rows)
176 Rows,
null, ExternalDescription,
null,
null,
null,
false,
false,
false);
181 new string[] { Tag.Value?.ToString() ?? string.Empty },
null, ExternalDescription,
null,
182 null,
null,
false,
false,
false);
200 if (this.metaDataByName is
null)
201 this.BuildDictionary();
215 private void BuildDictionary()
217 SortedDictionary<string, MetaDataValue> ByName =
new SortedDictionary<string, MetaDataValue>();
225 this.metaDataByName = ByName;
228 private SortedDictionary<string, MetaDataValue> metaDataByName =
null;
241 return Task.CompletedTask;
242 else if (Prev is
int)
245 Field.Error =
"Value must be a valid integer.";
247 else if (Prev is
long)
250 Field.Error =
"Value must be a valid long integer.";
252 else if (Prev is
short)
255 Field.Error =
"Value must be a valid short integer.";
257 else if (Prev is
byte)
260 Field.Error =
"Value must be a valid byte.";
262 else if (Prev is
double)
265 Field.Error =
"Value must be a valid double-precision floating-point value.";
267 else if (Prev is decimal)
270 Field.Error =
"Value must be a valid decimal-precision floating-point value.";
272 else if (Prev is
bool)
275 Field.Error =
"Value must be a valid boolean value.";
277 else if (Prev is TimeSpan)
280 Field.Error =
"Value must be a valid TimeSpan value.";
282 else if (Prev is DateTime)
285 Field.Error =
"Value must be a valid DateTime value.";
287 else if (Prev is Uri)
290 Field.Error =
"Value must be a valid URI value.";
292 else if (Prev is
string[])
294 return Task.CompletedTask;
298 return Task.CompletedTask;
304 Field.Error = ex.Message;
308 return Task.CompletedTask;
317 if (this.metaDataByName is
null)
318 this.BuildDictionary();
324 if (Prev.Value is
string)
326 else if (Prev.Value is
int)
331 else if (Prev.Value is
long)
336 else if (Prev.Value is
short)
339 Field.Error =
"Value must be a valid short integer.";
341 else if (Prev.Value is
byte)
346 else if (Prev.Value is
double)
351 else if (Prev.Value is decimal)
356 else if (Prev.Value is
bool)
361 else if (Prev.Value is TimeSpan)
366 else if (Prev.Value is DateTime)
371 else if (Prev.Value is Uri)
376 else if (Prev.Value is
string[])
384 Field.Error = ex.Message;
390 return Task.CompletedTask;
413 if (this.metaDataByName is
null)
414 this.BuildDictionary();
419 this.SetMetaDataPriv(
Name, Value);
424 private void SetMetaDataPriv(
string Name,
object Value)
433 this.metaDataByName.Values.CopyTo(Values, 0);
434 this.MetaData = Values;
445 LinkedList<Parameter> Result = await base.GetDisplayableParametersAsync(
Language, Caller) as LinkedList<Parameter>;
451 if (Tag.
Value is
string s)
453 else if (Tag.
Value is
int i)
455 else if (Tag.
Value is
long l)
457 else if (Tag.
Value is
short sh)
459 else if (Tag.
Value is
byte b)
461 else if (Tag.
Value is
double d)
463 else if (Tag.
Value is decimal d2)
465 else if (Tag.
Value is
bool b2)
467 else if (Tag.
Value is TimeSpan TS)
469 else if (Tag.
Value is DateTime TP)
471 else if (Tag.
Value is Uri Uri)
497 SensorData.Field[] ToReport =
null;
501 foreach (SensorData.Field
Field in Fields)
505 if (
Field.Type.HasFlag(SensorData.FieldType.Momentary))
507 this.toReport ??=
new List<SensorData.
Field>();
508 this.toReport.Add(
Field);
509 this.hasReport =
true;
515 if (scheduler is
null)
517 ToReport = this.toReport.ToArray();
518 this.toReport.Clear();
519 this.hasReport =
false;
523 if (this.nextReport != DateTime.MinValue)
524 scheduler.
Remove(this.nextReport);
526 this.nextReport = scheduler.
Add(DateTime.Now.AddMilliseconds(250), this.DoReport,
null);
531 if (!(ToReport is
null))
535 private Task DoReport(
object _)
537 SensorData.Field[] ToReport;
541 ToReport = this.toReport.ToArray();
542 this.toReport.Clear();
543 this.hasReport =
false;
570 return this.fields.Count > 0;
583 List<SensorData.Field> ToReport =
new List<SensorData.Field>();
587 foreach (SensorData.Field
Field in
this.fields.Values)
594 if (DoneAfter || ToReport.Count > 0)
597 return Task.CompletedTask;
616 FieldValue is
double ||
617 FieldValue is
string ||
618 FieldValue is
bool ||
619 FieldValue is Enum ||
620 FieldValue is DateTime ||
621 FieldValue is TimeSpan ||
623 FieldValue is sbyte ||
624 FieldValue is
short ||
626 FieldValue is
long ||
627 FieldValue is
byte ||
628 FieldValue is ushort ||
629 FieldValue is uint ||
643 List<ControlParameter> Parameters =
new List<ControlParameter>();
650 if (FieldValue is
double d)
653 _ => Task.FromResult<
double?>(d),
657 await this.DoCallback(CallbackUrl, PayloadScript, d);
660 else if (FieldValue is
string s)
663 _ => Task.FromResult<
string>(s),
667 await this.DoCallback(CallbackUrl, PayloadScript, s);
670 else if (FieldValue is
bool b)
673 _ => Task.FromResult<
bool?>(b),
677 await this.DoCallback(CallbackUrl, PayloadScript, b);
680 else if (FieldValue is Enum e)
682 Parameters.Add(
new EnumControlParameter(FieldName,
"Control", FieldName +
":",
"Value to set.", e.GetType(),
683 _ => Task.FromResult<Enum>(e),
687 await this.DoCallback(CallbackUrl, PayloadScript, e);
690 else if (FieldValue is DateTime DT)
693 _ => Task.FromResult<DateTime?>(DT),
697 await this.DoCallback(CallbackUrl, PayloadScript, DT);
700 else if (FieldValue is TimeSpan TS)
703 _ => Task.FromResult<TimeSpan?>(TS),
707 await this.DoCallback(CallbackUrl, PayloadScript, TS);
717 await this.DoCallback(CallbackUrl, PayloadScript, D);
720 else if (FieldValue is sbyte i8)
722 Parameters.Add(
new Int32ControlParameter(FieldName,
"Control", FieldName +
":",
"Value to set.", sbyte.MinValue, sbyte.MaxValue,
723 _ => Task.FromResult<
int?>(i8),
727 await this.DoCallback<int>(CallbackUrl, PayloadScript, i8);
730 else if (FieldValue is
short i16)
732 Parameters.Add(
new Int32ControlParameter(FieldName,
"Control", FieldName +
":",
"Value to set.",
short.MinValue,
short.MaxValue,
733 _ => Task.FromResult<
int?>(i16),
737 await this.DoCallback<int>(CallbackUrl, PayloadScript, i16);
740 else if (FieldValue is
int i32)
742 Parameters.Add(
new Int32ControlParameter(FieldName,
"Control", FieldName +
":",
"Value to set.",
null,
null,
743 _ => Task.FromResult<
int?>(i32),
747 await this.DoCallback<int>(CallbackUrl, PayloadScript, i32);
750 else if (FieldValue is
long i64)
752 Parameters.Add(
new Int64ControlParameter(FieldName,
"Control", FieldName +
":",
"Value to set.",
null,
null,
753 _ => Task.FromResult<
long?>(i64),
757 await this.DoCallback<long>(CallbackUrl, PayloadScript, i64);
760 else if (FieldValue is
byte ui8)
762 Parameters.Add(
new Int32ControlParameter(FieldName,
"Control", FieldName +
":",
"Value to set.",
byte.MinValue,
byte.MaxValue,
763 _ => Task.FromResult<
int?>(ui8),
767 await this.DoCallback<int>(CallbackUrl, PayloadScript, ui8);
770 else if (FieldValue is ushort ui16)
772 Parameters.Add(
new Int32ControlParameter(FieldName,
"Control", FieldName +
":",
"Value to set.", ushort.MinValue, ushort.MaxValue,
773 _ => Task.FromResult<
int?>(ui16),
776 ui16 = (ushort)Value;
777 await this.DoCallback<int>(CallbackUrl, PayloadScript, ui16);
780 else if (FieldValue is uint ui32)
782 Parameters.Add(
new Int64ControlParameter(FieldName,
"Control", FieldName +
":",
"Value to set.", uint.MinValue, uint.MaxValue,
783 _ => Task.FromResult<
long?>(ui32),
787 await this.DoCallback<long>(CallbackUrl, PayloadScript, ui32);
790 else if (FieldValue is ulong ui64)
792 Parameters.Add(
new DoubleControlParameter(FieldName,
"Control", FieldName +
":",
"Value to set.", ulong.MinValue, ulong.MaxValue,
793 _ => Task.FromResult<
double?>(ui64),
797 await this.DoCallback<double>(CallbackUrl, PayloadScript, ui64);
802 return Task.FromResult(Parameters.ToArray());
805 private async Task DoCallback<T>(
string CallbackUrl,
string PayloadScript, T Value)
820 public override Task<IEnumerable<ICommand>>
Commands => this.GetCommands();
822 private async Task<IEnumerable<ICommand>> GetCommands()
824 List<ICommand>
Commands =
new List<ICommand>();
825 Commands.AddRange(await base.Commands);
827 Commands.Add(
new AddMetaDataString(
this));
828 Commands.Add(
new AddMetaDataInt32(
this));
829 Commands.Add(
new AddMetaDataInt64(
this));
830 Commands.Add(
new AddMetaDataDouble(
this));
831 Commands.Add(
new AddMetaDataBoolean(
this));
832 Commands.Add(
new AddMetaDataDateTime(
this));
833 Commands.Add(
new AddMetaDataTimeSpan(
this));
834 Commands.Add(
new AddMetaDataDuration(
this));
Helps with parsing of commong data types.
static bool TryParse(string s, out double Value)
Tries to decode a string encoded double.
Contains information about a response to a content request.
void AssertOk()
Asserts response is OK.
Static class managing encoding and decoding of internet content.
static Task< ContentResponse > PostAsync(Uri Uri, object Data, params KeyValuePair< string, string >[] Headers)
Posts to a resource, using a Uniform Resource Identifier (or Locator).
Helps with common XML-related tasks.
static bool TryParse(string s, out DateTime Value)
Tries to decode a string encoded DateTime.
Static class managing the application event log. Applications and services log events on this static ...
static Exception UnnestException(Exception Exception)
Unnests an exception, to extract the relevant inner exception.
Class managing a page in a data form layout.
Static class that dynamically manages types and interfaces available in the runtime environment.
static bool TryGetModuleParameter(string Name, out object Value)
Tries to get a module parameter value.
Contains information about a language.
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 ...
async Task< Namespace > GetNamespaceAsync(string Name)
Gets the namespace object, given its name, if available.
Contains information about a namespace in a language.
Task< LanguageString > GetStringAsync(int Id)
Gets the string object, given its ID, if available.
Basic access point for runtime language localization.
static async Task< Language > GetLanguageAsync(string Code)
Gets the languge object, given its language code, if available.
Class that can be used to schedule events in time. It uses a timer to execute tasks at the appointed ...
bool Remove(DateTime When)
Removes an event scheduled for a given point in time.
DateTime Add(DateTime When, Action< object > Callback, object State)
Adds an event.
Class managing a script expression.
static Task< object > EvalAsync(string Script)
Evaluates script, in string format.
Boolean control parameter.
DateTime control parameter.
Double control parameter.
Duration control parameter.
Enumeration control parameter.
String control parameter.
Boolean-valued parameter.
DateTime-valued parameter.
TimeSpan-valued parameter.
Task NewMomentaryValues(params Field[] Values)
Reports newly measured values.
Class for the root node of the Metering topology.
Base class for all provisioned metering nodes.
Tokens available in request.
Virtual node, that can be used as a placeholder for services.
object GetMetaData(string Name)
Gets a meta-data value, if available.
virtual Task StartReadout(ISensorReadout Request, bool DoneAfter)
Starts the readout of the sensor.
override async Task< IEnumerable< Parameter > > GetDisplayableParametersAsync(Language Language, RequestOrigin Caller)
Gets displayable parameters.
override async Task AnnotatePropertyForm(FormState Form)
Annotates the property form.
VirtualNode()
Virtual node, that can be used as a placeholder for services.
async Task ReportSensorData(params SensorData.Field[] Fields)
Reports sensor data on the node.
Task StartReadout(ISensorReadout Request)
Starts the readout of the sensor.
virtual Task< ControlParameter[]> GetControlParameters()
Get control parameters for the actuator.
override Task< bool > AcceptsParentAsync(INode Parent)
If the node accepts a presumptive parent, i.e. can be added to that parent (if that parent accepts th...
override bool IsControllable
If the node can be controlled.
override Task< string > GetTypeNameAsync(Language Language)
Gets the type name of the node.
override bool IsReadable
If the node can be read.
override Task< IEnumerable< ICommand > > Commands
Available command objects. If no commands are available, null is returned.
Task ValidateCustomProperty(Field Field)
Performs custom validation of a property.
MetaDataValue[] MetaData
Meta-data attached to virtual node.
Task SetCustomProperty(Field Field)
Sets the custom parameter to the value(s) provided in the field.
bool TryGetMetaDataValue(string Name, out object Value)
Tries to get a meta-data value
Task ReportSensorData(SensorData.Field Field)
Reports sensor data on the node.
async Task SetMetaData(string Name, object Value)
Sets a meta-data value.
override async Task< bool > AcceptsChildAsync(INode Child)
If the node accepts a presumptive child, i.e. can receive as a child (if that child accepts the node ...
Interface for actuator nodes.
Interface for nodes that are published through the concentrator interface.
Task UpdateAsync()
Updates the node (in persisted storage).
INode Parent
Parent Node, or null if a root node.
Task< bool > AcceptsParentAsync(INode Parent)
If the node accepts a presumptive parent, i.e. can be added to that parent (if that parent accepts th...
Interface for sensor nodes.
Interface for classes managing sensor data readouts.
bool IsIncluded(string FieldName)
Checks if a field with the given parameters is included in the readout.
Task ReportFields(bool Done, params Field[] Fields)
Report read fields to the client.
Represents a duration value, as defined by the xsd:duration data type: http://www....