Neuron®
The Neuron® is the basis for the creation of open and secure federated networks for smart societies.
Loading...
Searching...
No Matches
InternalDatabaseOutput.cs
1using System;
3using System.Text;
4using System.Threading.Tasks;
5using Waher.Events;
12using Waher.Things;
15
17{
22 {
23 private string collection = string.Empty;
24 private string[] fieldIndices = null;
25 private bool checkCollection = false;
26 private bool checkIndices = false;
27
32 : base()
33 {
34 }
35
39 [Header(2, "Collection:", 10)]
40 [Page(3, "Output", 0)]
41 [ToolTip(4, "Name of collection in internal database that will house the sensor data.")]
42 [Required]
43 public string Collection
44 {
45 get => this.collection;
46 set
47 {
48 if (this.collection != value)
49 {
50 this.collection = value;
51 this.checkCollection = true;
52 this.checkIndices = true;
53 }
54 }
55 }
56
60 [Header(5, "Index field names:", 20)]
61 [Page(3, "Output", 0)]
62 [ToolTip(6, "Indices will be added on these field names for quicker access to records.")]
63 public string[] FieldIndices
64 {
65 get => this.fieldIndices;
66 set
67 {
68 if (!this.fieldIndices.ElementEquals(value))
69 {
70 this.fieldIndices = value;
71 this.checkIndices = true;
72 }
73 }
74 }
75
79 [Header(7, "Store physical units.", 30)]
80 [Page(3, "Output", 0)]
81 [ToolTip(8, "If physical units should be stored in a separate property.")]
82 public bool StoreUnits { get; set; }
83
87 [Header(9, "Store field type.", 40)]
88 [Page(3, "Output", 0)]
89 [ToolTip(10, "If field type should be stored in a separate property.")]
90 public bool StoreFieldType { get; set; }
91
95 [Header(11, "Store quality of service.", 50)]
96 [Page(3, "Output", 0)]
97 [ToolTip(12, "If quality of service should be stored in a separate property.")]
98 public bool StoreQoS { get; set; }
99
105 public override Task<string> GetTypeNameAsync(Language Language)
106 {
107 return Language.GetStringAsync(typeof(InternalDatabaseOutput), 1, "Internal Database Output");
108 }
109
115 public override Task<bool> AcceptsChildAsync(INode Child)
116 {
117 return Task.FromResult(false);
118 }
119
125 public override Task<bool> AcceptsParentAsync(INode Parent)
126 {
127 return Task.FromResult(Parent is Root);
128 }
129
135 public async Task OutputFields(ISensor Sensor, Field[] Fields)
136 {
137 using Semaphore Semaphore = await Semaphores.BeginWrite("collection:" + this.collection);
138
139 if (this.checkCollection)
140 {
141 if (string.IsNullOrEmpty(this.collection))
142 {
143 await this.LogErrorAsync("CollectionError", "Collection name cannot be empty.");
144 return;
145 }
146 else
147 await this.RemoveErrorAsync("CollectionError");
148
149 string[] Collections = await Database.GetCollections();
150
151 if (Array.IndexOf(Collections, this.collection) < 0)
152 this.checkIndices = true;
153
154 this.checkCollection = false;
155 }
156
157 if (this.checkIndices)
158 {
159 Dictionary<string, string[]> Indices = new Dictionary<string, string[]>();
160
161 foreach (string[] Index in await Database.GetIndices(this.collection))
162 Indices[IndexKey(Index)] = Index;
163
164 await this.AddIndexIfNotDefined(Indices, new string[]
165 {
166 "NodeId",
167 "Timestamp"
168 });
169
170 await this.AddIndexIfNotDefined(Indices, new string[]
171 {
172 "Timestamp",
173 "NodeId"
174 });
175
176 foreach (string FieldName in this.fieldIndices ?? Array.Empty<string>())
177 {
178 string s = FieldName.Trim();
179 if (string.IsNullOrEmpty(s))
180 continue;
181
182 await this.AddIndexIfNotDefined(Indices, new string[]
183 {
184 s,
185 "NodeId",
186 "Timestamp"
187 });
188
189 await this.AddIndexIfNotDefined(Indices, new string[]
190 {
191 s,
192 "Timestamp",
193 "NodeId"
194 });
195 }
196
197 foreach (KeyValuePair<string, string[]> IndexLeft in Indices)
198 await Database.RemoveIndex(this.collection, IndexLeft.Value);
199
200 this.checkIndices = false;
201 }
202
203 Dictionary<DateTime, GenericObject> PerTime = new Dictionary<DateTime, GenericObject>();
204 GenericObject Obj = null;
205 DateTime LastTime = DateTime.MinValue;
206
207 foreach (Field Field in Fields)
208 {
209 if (Obj is null || LastTime != Field.Timestamp)
210 {
211 LastTime = Field.Timestamp;
212
213 if (!PerTime.TryGetValue(Field.Timestamp, out Obj))
214 {
215 Obj = new GenericObject(this.collection, string.Empty, Guid.Empty,
216 new KeyValuePair<string, object>("NodeId", Sensor.NodeId),
217 new KeyValuePair<string, object>("Timestamp", LastTime));
218 PerTime[LastTime] = Obj;
219 }
220 }
221
222 object Value = Field.ObjectValue;
223
224 if (Value is PhysicalQuantity Q)
225 {
226 Obj[Field.Name] = Q.Magnitude;
227
228 if (this.StoreUnits)
229 Obj[Field.Name + ", Unit"] = Q.Unit;
230 }
231 else
232 Obj[Field.Name] = Value;
233
234 if (this.StoreFieldType)
235 Obj[Field.Name + ", Type"] = Field.Type;
236
237 if (this.StoreQoS)
238 Obj[Field.Name + ", QoS"] = Field.QoS;
239 }
240
241 await Database.Insert(PerTime.Values);
242 }
243
244 private async Task AddIndexIfNotDefined(Dictionary<string, string[]> Indices, string[] Index)
245 {
246 string Key = IndexKey(Index);
247 if (!Indices.Remove(Key))
248 await Database.AddIndex(this.collection, Index);
249 }
250
251 private static string IndexKey(string[] Fields)
252 {
253 StringBuilder sb = new StringBuilder();
254 bool First = true;
255
256 foreach (string Field in Fields)
257 {
258 if (First)
259 First = false;
260 else
261 sb.AppendLine();
262
263 sb.Append(Field);
264 }
265
266 return sb.ToString();
267 }
268 }
269}
Stores sensor data in the internal database.
override 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 ...
override Task< string > GetTypeNameAsync(Language Language)
Gets the type name of the node.
async Task OutputFields(ISensor Sensor, Field[] Fields)
Outputs a collection of sensor data fields.
bool StoreUnits
If physical quantity units should be stored.
InternalDatabaseOutput()
Stores sensor data in the internal database.
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...
Class for the root node of the Output data source.
Definition: Root.cs:11
Base class for all output nodes.
Definition: OutputNode.cs:29
INode Parent
Parent Node, or null if a root node.
Definition: OutputNode.cs:620
virtual Task LogErrorAsync(string Body)
Logs an error message on the node.
Definition: OutputNode.cs:192
virtual Task< bool > RemoveErrorAsync()
Removes error messages with an empty event ID from the node.
Definition: OutputNode.cs:357
Static interface for database persistence. In order to work, a database provider has to be assigned t...
Definition: Database.cs:21
static Task RemoveIndex(string CollectionName, string[] FieldNames)
Removes an index from a collection, if one exist.
Definition: Database.cs:2231
static Task AddIndex(string CollectionName, string[] FieldNames)
Adds an index to a collection, if one does not already exist.
Definition: Database.cs:2220
static Task< string[]> GetCollections()
Gets an array of available collections.
Definition: Database.cs:2355
static Task< string[][]> GetIndices(string CollectionName)
Removes an index from a collection, if one exist.
Definition: Database.cs:2243
static async Task Insert(object Object)
Inserts an object into the default collection of the database.
Definition: Database.cs:97
Generic object. Contains a sequence of properties.
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
Represents a named semaphore, i.e. an object, identified by a name, that allows single concurrent wri...
Definition: Semaphore.cs:19
Static class of application-wide semaphores that can be used to order access to editable objects.
Definition: Semaphores.cs:17
static async Task< Semaphore > BeginWrite(string Key)
Waits until the semaphore identified by Key is ready for writing. Each call to BeginWrite must be fo...
Definition: Semaphores.cs:91
Base class for all sensor data fields.
Definition: Field.cs:20
FieldQoS QoS
Field Quality of Service flags.
Definition: Field.cs:269
FieldType Type
Field Type flags.
Definition: Field.cs:259
abstract object ObjectValue
Field value, boxed as an object reference.
Definition: Field.cs:416
string Name
Unlocalized field name.
Definition: Field.cs:279
DateTime Timestamp
Timestamp of field value.
Definition: Field.cs:202
Base Interface for all sensor-data output nodes.
Interface for nodes that are published through the concentrator interface.
Definition: INode.cs:49
Interface for sensor nodes.
Definition: ISensor.cs:9