vrack2-core 0.0.1 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (148) hide show
  1. package/README.md +25 -4
  2. package/docs/Bootstrap.md +77 -0
  3. package/docs/Container.md +124 -0
  4. package/docs/Device.md +272 -0
  5. package/docs/FastStart.md +111 -0
  6. package/docs/Structure.md +148 -0
  7. package/lib/Bootstrap.d.ts +79 -0
  8. package/lib/Bootstrap.js +103 -0
  9. package/lib/Container.d.ts +202 -6
  10. package/lib/Container.js +295 -27
  11. package/lib/IServiceStructure.d.ts +8 -0
  12. package/lib/IStructureDevice.d.ts +5 -0
  13. package/lib/ImportManager.d.ts +85 -3
  14. package/lib/ImportManager.js +122 -16
  15. package/lib/MainProcess.d.ts +30 -3
  16. package/lib/MainProcess.js +28 -6
  17. package/lib/Utility.d.ts +15 -21
  18. package/lib/Utility.js +40 -40
  19. package/lib/actions/Action.d.ts +10 -0
  20. package/lib/actions/Action.js +10 -0
  21. package/lib/actions/BasicAction.d.ts +60 -0
  22. package/lib/actions/BasicAction.js +62 -0
  23. package/lib/actions/GlobalAction.d.ts +5 -0
  24. package/lib/actions/GlobalAction.js +5 -0
  25. package/lib/actions/IAction.d.ts +7 -0
  26. package/lib/actions/ILocalAction.d.ts +7 -0
  27. package/lib/boot/BootClass.d.ts +93 -0
  28. package/lib/boot/BootClass.js +101 -0
  29. package/lib/boot/DeviceFileStorage.d.ts +38 -0
  30. package/lib/boot/DeviceFileStorage.js +112 -0
  31. package/lib/boot/DeviceManager.d.ts +190 -0
  32. package/lib/boot/DeviceManager.js +306 -0
  33. package/lib/boot/DeviceMetrics.d.ts +82 -0
  34. package/lib/boot/DeviceMetrics.js +128 -0
  35. package/lib/boot/StructureStorage.d.ts +59 -0
  36. package/lib/boot/StructureStorage.js +125 -0
  37. package/lib/errors/CoreError.d.ts +42 -25
  38. package/lib/errors/CoreError.js +44 -24
  39. package/lib/errors/ErrorManager.d.ts +18 -20
  40. package/lib/errors/ErrorManager.js +23 -22
  41. package/lib/index.d.ts +20 -4
  42. package/lib/index.js +28 -4
  43. package/lib/metrics/BasicMetric.d.ts +49 -0
  44. package/lib/metrics/BasicMetric.js +79 -0
  45. package/lib/metrics/IMetricSettings.d.ts +32 -0
  46. package/lib/metrics/IMetricSettings.js +2 -0
  47. package/lib/metrics/IvMs.d.ts +9 -0
  48. package/lib/metrics/IvMs.js +22 -0
  49. package/lib/metrics/IvS.d.ts +9 -0
  50. package/lib/metrics/IvS.js +22 -0
  51. package/lib/metrics/IvUs.d.ts +9 -0
  52. package/lib/metrics/IvUs.js +22 -0
  53. package/lib/metrics/Metric.d.ts +17 -0
  54. package/lib/metrics/Metric.js +27 -0
  55. package/lib/ports/BasicPort.d.ts +39 -0
  56. package/lib/ports/BasicPort.js +39 -0
  57. package/lib/ports/ILocalPort.d.ts +7 -0
  58. package/lib/ports/IPort.d.ts +7 -0
  59. package/lib/ports/Port.d.ts +10 -0
  60. package/lib/ports/Port.js +10 -0
  61. package/lib/ports/ReturnPort.d.ts +5 -0
  62. package/lib/ports/ReturnPort.js +5 -0
  63. package/lib/service/Device.d.ts +213 -78
  64. package/lib/service/Device.js +185 -83
  65. package/lib/service/DeviceConnect.d.ts +4 -8
  66. package/lib/service/DeviceConnect.js +4 -8
  67. package/lib/service/DevicePort.d.ts +15 -6
  68. package/lib/service/DevicePort.js +29 -12
  69. package/lib/service/IDeviceEvent.d.ts +4 -1
  70. package/lib/validator/IValidationProblem.d.ts +7 -0
  71. package/lib/validator/IValidationRule.d.ts +12 -0
  72. package/lib/validator/IValidationSubrule.d.ts +2 -0
  73. package/lib/validator/Rule.d.ts +6 -2
  74. package/lib/validator/Rule.js +12 -2
  75. package/lib/validator/Validator.d.ts +48 -3
  76. package/lib/validator/Validator.js +70 -18
  77. package/lib/validator/types/AnyType.d.ts +17 -0
  78. package/lib/validator/types/AnyType.js +34 -0
  79. package/lib/validator/types/ArrayType.d.ts +37 -4
  80. package/lib/validator/types/ArrayType.js +42 -6
  81. package/lib/validator/types/BasicType.d.ts +67 -7
  82. package/lib/validator/types/BasicType.js +74 -8
  83. package/lib/validator/types/BooleanType.d.ts +23 -0
  84. package/lib/validator/types/BooleanType.js +47 -0
  85. package/lib/validator/types/FunctionType.d.ts +17 -0
  86. package/lib/validator/types/FunctionType.js +38 -0
  87. package/lib/validator/types/NumberType.d.ts +40 -5
  88. package/lib/validator/types/NumberType.js +53 -14
  89. package/lib/validator/types/ObjectType.d.ts +32 -5
  90. package/lib/validator/types/ObjectType.js +42 -8
  91. package/lib/validator/types/StringType.d.ts +30 -5
  92. package/lib/validator/types/StringType.js +33 -7
  93. package/package.json +10 -9
  94. package/src/Bootstrap.ts +122 -0
  95. package/src/Container.ts +411 -43
  96. package/src/IServiceStructure.ts +9 -0
  97. package/src/IStructureDevice.ts +5 -0
  98. package/src/ImportManager.ts +119 -11
  99. package/src/MainProcess.ts +53 -8
  100. package/src/Utility.ts +35 -36
  101. package/src/actions/Action.ts +12 -0
  102. package/src/actions/BasicAction.ts +63 -0
  103. package/src/actions/GlobalAction.ts +5 -0
  104. package/src/actions/IAction.ts +7 -0
  105. package/src/actions/ILocalAction.ts +7 -0
  106. package/src/boot/BootClass.ts +117 -0
  107. package/src/boot/DeviceFileStorage.ts +96 -0
  108. package/src/boot/DeviceManager.ts +339 -0
  109. package/src/boot/DeviceMetrics.ts +129 -0
  110. package/src/boot/StructureStorage.ts +108 -0
  111. package/src/errors/CoreError.ts +52 -26
  112. package/src/errors/ErrorManager.ts +46 -33
  113. package/src/index.ts +30 -6
  114. package/src/metrics/BasicMetric.ts +84 -0
  115. package/src/metrics/IMetricSettings.ts +38 -0
  116. package/src/metrics/IvMs.ts +18 -0
  117. package/src/metrics/IvS.ts +18 -0
  118. package/src/metrics/IvUs.ts +17 -0
  119. package/src/metrics/Metric.ts +25 -0
  120. package/src/ports/BasicPort.ts +39 -0
  121. package/src/ports/ILocalPort.ts +7 -0
  122. package/src/ports/IPort.ts +7 -0
  123. package/src/ports/Port.ts +11 -1
  124. package/src/ports/ReturnPort.ts +5 -0
  125. package/src/service/Device.ts +234 -103
  126. package/src/service/DeviceConnect.ts +4 -8
  127. package/src/service/DevicePort.ts +30 -11
  128. package/src/service/IDeviceEvent.ts +4 -1
  129. package/src/validator/IValidationProblem.ts +7 -0
  130. package/src/validator/IValidationRule.ts +12 -0
  131. package/src/validator/IValidationSubrule.ts +3 -0
  132. package/src/validator/Rule.ts +16 -2
  133. package/src/validator/Validator.ts +74 -23
  134. package/src/validator/types/AnyType.ts +32 -0
  135. package/src/validator/types/ArrayType.ts +43 -7
  136. package/src/validator/types/BasicType.ts +78 -9
  137. package/src/validator/types/BooleanType.ts +49 -0
  138. package/src/validator/types/FunctionType.ts +39 -0
  139. package/src/validator/types/NumberType.ts +53 -15
  140. package/src/validator/types/ObjectType.ts +52 -14
  141. package/src/validator/types/StringType.ts +34 -10
  142. package/docs/RU-README.md +0 -6
  143. package/lib/DeviceManager.d.ts +0 -32
  144. package/lib/DeviceManager.js +0 -143
  145. package/lib/test.d.ts +0 -1
  146. package/lib/test.js +0 -58
  147. package/src/DeviceManager.ts +0 -124
  148. package/src/test.ts +0 -82
@@ -0,0 +1,339 @@
1
+ import path from 'path'
2
+ import fs from 'fs'
3
+ import ErrorManager from '../errors/ErrorManager'
4
+ import Rule from '../validator/Rule'
5
+ import CoreError from '../errors/CoreError'
6
+ import BootClass from './BootClass'
7
+ import ImportManager from '../ImportManager'
8
+ import BasicType from '../validator/types/BasicType'
9
+ import Device from '../service/Device'
10
+ import IAction from '../actions/IAction'
11
+ import IMetricSettings from '../metrics/IMetricSettings'
12
+ import IPort from '../ports/IPort'
13
+ import IValidationRule from '../validator/IValidationRule'
14
+
15
+ export interface IDeviceVendor {
16
+ /** Group name = Vendor name */
17
+ name: string,
18
+ /** Global devices dir */
19
+ dir: string,
20
+
21
+ /**
22
+ * List of all devices in group
23
+ * Like a
24
+ *
25
+ * ```
26
+ * ['Device1', 'Device2', 'Device3', 'Device4']
27
+ * ```
28
+ */
29
+ deviceList: Array<string>,
30
+
31
+ /**
32
+ * Errors in group
33
+ * Contains all errors collected in the group during initialization
34
+ *
35
+ * Errors like a DM_LIST_NOT_FOUND & DM_LIST_INCORRECT
36
+ */
37
+ errors: Array<CoreError>,
38
+
39
+ /**
40
+ * Not used now
41
+ * Vendor description
42
+ */
43
+ description: string
44
+ }
45
+
46
+ /**
47
+ * An object based on this interface contains all
48
+ * the basic exported information about the device
49
+ *
50
+ * @see getDeviceInfo
51
+ */
52
+ export interface IDeivceInfo {
53
+ /** List of device actions */
54
+ actions: { [key: string]: IAction }
55
+ /** List of device metrics */
56
+ metrics: { [key: string]: IMetricSettings }
57
+ /** List of device inputs */
58
+ inputs: { [key: string]: IPort }
59
+ /** List of device outputs */
60
+ outputs: { [key: string]: IPort }
61
+ /** List of device options rules */
62
+ options: { [key: string]: IValidationRule }
63
+ /** Device descriotion */
64
+ description: string
65
+ }
66
+
67
+ ErrorManager.register(
68
+ 'DeviceManager',
69
+ 'S5dBTBKTnVbF',
70
+ 'DM_DEVICE_NOT_FOUND',
71
+ 'Device not found', {
72
+ device: Rule.string().require().example('Master').description('Device name')
73
+ })
74
+
75
+ ErrorManager.register(
76
+ 'DeviceManager',
77
+ '2CJSDE95V9O1',
78
+ 'DM_LIST_NOT_FOUND',
79
+ 'list.json not found', {
80
+ vendor: Rule.string().require().example('vrack').description('Device group name')
81
+ })
82
+
83
+ ErrorManager.register(
84
+ 'DeviceManager',
85
+ 'BPX96F93ZCJA',
86
+ 'DM_LIST_INCORRECT',
87
+ 'list.json is not correct', {
88
+ vendor: Rule.string().require().example('vrack').description('Device group name')
89
+ })
90
+
91
+ ErrorManager.register(
92
+ 'DeviceManager',
93
+ 'H1NDNVCOLWST',
94
+ 'DM_VENDOR_NOT_FOUND',
95
+ 'Vendor not found', {
96
+ vendor: Rule.string().require().example('vrack').description('Vendor name')
97
+ })
98
+
99
+ ErrorManager.register('DeviceManager', 'HQ62LW1YLTDE', 'DM_GET_INFO_EXCEPTION', 'Error retrieving device data, see error in attachment', {})
100
+
101
+
102
+ /**
103
+ * BootClass which generates a list of available devices for the container.
104
+ * Provides the ability to initialize devices and provide information about them
105
+ *
106
+ *
107
+ * @see get
108
+ */
109
+ export default class DeviceManager extends BootClass {
110
+
111
+ /**
112
+ * Contains a list of devices and their full path to the class
113
+ * Like a:
114
+ *
115
+ * ```js
116
+ * {
117
+ * "vendorname.Devicename": '/path/to/js/file.js'
118
+ * }
119
+ * ```
120
+ * */
121
+ protected devices = new Map<string, string>()
122
+
123
+ /**
124
+ * Vendor array.
125
+ *
126
+ * @see IDeviceVendor
127
+ */
128
+ protected vendorList: Array<IDeviceVendor> = []
129
+
130
+ checkOptions(): { [key: string]: BasicType; } {
131
+ return {
132
+ dir: Rule.string().require().default('./devices'),
133
+ systemDir: Rule.string().require().default(ImportManager.systemPath()),
134
+ }
135
+ }
136
+
137
+ process(): void {
138
+ this.updateDeviceList()
139
+ }
140
+
141
+ /**
142
+ * Return vendor list
143
+ *
144
+ * @example
145
+ * ```ts
146
+ * ['vrack', 'basic', 'net']
147
+ * ```
148
+ */
149
+ getVendorList() {
150
+ const result: Array<string> = []
151
+ for (const dgroup of this.vendorList) result.push(dgroup.name)
152
+ return result
153
+ }
154
+
155
+ /**
156
+ * Returns a list of devices from a specific vendor
157
+ *
158
+ * @param vendor Vendor name
159
+ * @see getVendorList()
160
+ */
161
+ getVendorDeviceList(vendor: string): Array<string> {
162
+ const vgroup = this.findVendor(vendor)
163
+ if (typeof vgroup === "boolean") throw ErrorManager.make('DM_VENDOR_NOT_FOUND', { vendor }).setTrace(new Error())
164
+ return vgroup.deviceList
165
+ }
166
+
167
+ /**
168
+ * Exports all basic device parameters and collects them in one object
169
+ * Used to generate documentation for the device
170
+ *
171
+ * @see IDeivceInfo
172
+ */
173
+ async getDeviceInfo(vendor: string, device: string) {
174
+ const di = [vendor, device].join('.')
175
+ const DeviceClass = await this.get(di)
176
+ const dev = new DeviceClass('1', di, this) as Device
177
+ const result: IDeivceInfo = { actions: {}, metrics: {}, inputs: {}, outputs: {}, options: {}, description: '' }
178
+ try {
179
+ const preOptions = dev.checkOptions();
180
+ for (const oName in preOptions)
181
+ result.options[oName] = preOptions[oName].export();
182
+ const dInputs = dev.inputs();
183
+ for (const iName in dInputs)
184
+ result.inputs[iName] = dInputs[iName].export();
185
+ const dOutputs = dev.outputs();
186
+ for (const oName in dOutputs)
187
+ result.outputs[oName] = dOutputs[oName].export();
188
+ const dActions = dev.actions();
189
+ for (const aName in dActions)
190
+ result.actions[aName] = dActions[aName].export();
191
+ const dMetrics = dev.metrics();
192
+ for (const mName in dMetrics)
193
+ result.metrics[mName] = dMetrics[mName].export();
194
+ result.description = dev.description()
195
+ } catch (error) {
196
+ throw ErrorManager.make('DM_GET_INFO_EXCEPTION').setTrace(new Error).add(error as Error)
197
+ }
198
+ return result
199
+ }
200
+
201
+ /**
202
+ * Method for updating the device list
203
+ * Updates the available device lists.
204
+ *
205
+ * Searches the directory specified in `options.dir` and adds all directories in it as vendors.
206
+ * Each folder attempts to read the list.json file to get a list of vendor devices
207
+ *
208
+ * Two types of list.json file definition are supported
209
+ *
210
+ * First type - array:
211
+ *
212
+ * ```json
213
+ * ['Device1', 'Device2', 'Device3']
214
+ * ```
215
+ * Second type - Object
216
+ * This approach allows you to point to devices that are deeper than the vendor's root folder
217
+ *
218
+ * ```json
219
+ * {
220
+ * "Master": "devices/Master",
221
+ * "Interval": "devices/Interval",
222
+ * "Guard": "devices/Guard",
223
+ * }
224
+ * ```
225
+ *
226
+ * @see arrayDeviceListPrepare()
227
+ * @see objectDeviceListPrepare()
228
+ */
229
+ protected updateDeviceList() {
230
+ this.vendorList = []
231
+ this.devices.clear()
232
+ const dirs = ImportManager.dirList(path.join(this.options.systemDir, this.options.dir)).sort()
233
+ for (const vendor of dirs) {
234
+ const group: IDeviceVendor = {
235
+ name: vendor,
236
+ dir: this.options.dir,
237
+ deviceList: [],
238
+ errors: [],
239
+ description: ''
240
+ }
241
+ this.vendorList.push(group)
242
+ const listPath = path.join(this.options.systemDir, this.options.dir, vendor, 'list.json')
243
+ if (!fs.existsSync(listPath)) { group.errors.push(ErrorManager.make('DM_LIST_NOT_FOUND', { vendor }).setTrace(new Error)); continue }
244
+ try {
245
+ const list: Array<string> = JSON.parse(fs.readFileSync(listPath).toString('utf-8'))
246
+ if (Array.isArray(list)) { // if device list like a [ 'DeviceName', 'DeviceTwo' ]
247
+ group.deviceList = this.arrayDeviceListPrepare(list, group);
248
+ } else if (typeof list === "object") { // if device list lie a { DeviceID?: 'PathToDevice' }
249
+ group.deviceList = this.objectDeviceListPrepare(list, group);
250
+ } else throw new Error()
251
+ } catch (err) {
252
+ if (err instanceof CoreError) group.errors.push(err)
253
+ else group.errors.push(ErrorManager.make('DM_LIST_INCORRECT', { vendor }).setTrace(new Error))
254
+ }
255
+ }
256
+ }
257
+
258
+ /**
259
+ * Return class of device
260
+ *
261
+ * Requires special device string of path
262
+ * example "vrack.System" where "vrack" is vendor and "System" is device class
263
+ * @param {string} device Device path string
264
+ */
265
+ async get(device: string, updateList = true): Promise<any> {
266
+ const p: string | undefined = this.devices.get(device) // return device path or undefined
267
+ if (typeof p === 'string') {
268
+ const deviceClass = await import(p) // try import
269
+ if (deviceClass.default) return deviceClass.default
270
+ }
271
+ // Устройство не найдено, но не исключено что если перестроить дерево
272
+ // вендоров и устройств все будет так же
273
+ // По умолчанию мы попытаемся обновить дерево
274
+ if (updateList){
275
+ await this.updateDeviceList()
276
+ return await this.get(device, false)
277
+ }
278
+ throw ErrorManager.make('DM_DEVICE_NOT_FOUND', { device })
279
+ }
280
+
281
+ /**
282
+ * Find vendor by name
283
+ *
284
+ * Not found if return boolean
285
+ */
286
+ protected findVendor(vendor: string): boolean | IDeviceVendor {
287
+ for (const dgroup of this.vendorList) if (dgroup.name === vendor) return dgroup
288
+ return false
289
+ }
290
+
291
+ /**
292
+ * Processes a list of devices as an Array
293
+ *
294
+ * Takes an Array of type
295
+ * ```
296
+ * ['DeviceName', 'DeviceName2']
297
+ * ```
298
+ * @return {Array<string>} Sorted list
299
+ */
300
+ protected arrayDeviceListPrepare(list: Array<string>, group: IDeviceVendor) {
301
+ for (const name of list) {
302
+ const reqPath = path.join(this.options.systemDir, this.options.dir, group.name, name)
303
+ if (!fs.existsSync(reqPath + '.js')) {
304
+ group.errors.push(ErrorManager.make('DM_DEVICE_NOT_FOUND', { device: name }))
305
+ }
306
+ this.devices.set(group.name + '.' + name, reqPath)
307
+ }
308
+ list.sort()
309
+ return list
310
+ }
311
+
312
+ /**
313
+ * Processes a list of devices as an object
314
+ *
315
+ * Takes an object of type
316
+ * ```
317
+ * {
318
+ * DeviceName: 'path/to/Device'
319
+ * }
320
+ * ```
321
+ *
322
+ * return keys of list
323
+ * ```
324
+ * ['DeviceName']
325
+ * ```
326
+ *
327
+ * Set in this.devices for device name requiere path
328
+ */
329
+ protected objectDeviceListPrepare(list: { [key: string]: string }, group: IDeviceVendor) {
330
+ for (const name in list) {
331
+ const reqPath = path.join(this.options.systemDir, this.options.dir, group.name, list[name])
332
+ if (!fs.existsSync(reqPath + '.js')) {
333
+ group.errors.push(ErrorManager.make('DM_DEVICE_NOT_FOUND', { device: name }).setTrace(new Error))
334
+ }
335
+ this.devices.set(group.name + '.' + name, reqPath)
336
+ }
337
+ return Object.keys(list).sort()
338
+ }
339
+ }
@@ -0,0 +1,129 @@
1
+ /*
2
+ * Copyright © 2025 Boris Bobylev. All rights reserved.
3
+ * Licensed under the Apache License, Version 2.0
4
+ */
5
+
6
+ import { Interval, IntervalMs, IntervalUs, SingleDB } from "vrack-db";
7
+ import IDeviceEvent from "../service/IDeviceEvent";
8
+ import BootClass from "./BootClass";
9
+ import IMetricSettings from "../metrics/IMetricSettings";
10
+
11
+ /**
12
+ * A class to support metrics inside devices
13
+ * Using a database vrack-db. Collects and stores container metrics
14
+ *
15
+ * @see SingleDB
16
+ *
17
+ * Uses the `device.metric` and `device.register.metric` events
18
+ *
19
+ * @see deviceMetric()
20
+ * @see deviceRegiterMetric()
21
+ */
22
+ export default class DeviceMetrics extends BootClass {
23
+ /**
24
+ * VRack-DB class instance
25
+ */
26
+ DB = new SingleDB()
27
+
28
+ process(): void {
29
+ this.Container.on('device.metric', this.deviceMetric.bind(this))
30
+ this.Container.on('device.register.metric', this.deviceRegiterMetric.bind(this))
31
+ }
32
+
33
+ /**
34
+ * Checks if the metric exists in the device
35
+ *
36
+ * @param device Device ID
37
+ * @param name Registered metric name
38
+ */
39
+ has(device: string, name: string){
40
+ const path = this.getMetricPath(device, name)
41
+ if (!this.DB.has(path)) return false
42
+ return true
43
+ }
44
+
45
+ /**
46
+ * Read device metric from the database
47
+ *
48
+ * @param device Device ID
49
+ * @param name Registered metric name
50
+ * @param period Period in the format 'now-6h:now' @see SingleDB.read
51
+ * @param precision Accuracy interval '15m', '5s', '1h' or count of metrics 10, 200, 1500
52
+ * @param precision func Data aggregation function @see SingleDB.read
53
+ */
54
+ read(device: string, name: string, period: string, precision: string | number, func?: string) {
55
+ const path = this.getMetricPath(device, name)
56
+ return this.DB.read(path, period, precision, func)
57
+ }
58
+
59
+ /**
60
+ * Registers the device metric
61
+ *
62
+ * When a device is initialized - the container gets a list of
63
+ * device metrics and passes them to the `device.register.metric` event for each metric.
64
+ *
65
+ * @param nEvent Object like a { device: 'Device ID', data: 'metric.name', trace: IMetricSettings object}
66
+ * @see IMetricSettings
67
+ * @see registerMetric
68
+ */
69
+ protected deviceRegiterMetric(nEvent: IDeviceEvent) {
70
+ this.registerMetric(this.getMetricPath(nEvent.device, nEvent.data), nEvent.trace)
71
+ }
72
+
73
+ /**
74
+ * @see deviceRegiterMetric
75
+ * */
76
+ protected registerMetric(path: string, metric: IMetricSettings) {
77
+ this.DB.metric({
78
+ name: path,
79
+ retentions: metric.retentions,
80
+ tStorage: metric.tType,
81
+ vStorage: metric.vType,
82
+ CInterval: this.selectInterval(metric.interval)
83
+ })
84
+ }
85
+
86
+ /**
87
+ * A method of writing a metric to a database.
88
+ *
89
+ * @param nEvent Object like a { device: 'Device ID', data: 'metric.name', trace: { value: metric value 123, modify: function of vrack db modify } }
90
+ * @example
91
+ * ```ts
92
+ * deviceMetric({
93
+ * device: 'Device ID',
94
+ * data: 'metric.name',
95
+ * trace: { value: 5.25, modify: 'last'}
96
+ * })
97
+ * ```
98
+ */
99
+ protected deviceMetric(nEvent: IDeviceEvent) {
100
+ const path = this.getMetricPath(nEvent.device, nEvent.data)
101
+ if (!this.DB.has(path)) return
102
+ this.DB.write(path, nEvent.trace.value, 0, nEvent.trace.modify)
103
+ }
104
+
105
+ /**
106
+ * Selects the interval class depending on the specified minimal time unit
107
+ *
108
+ * @param interval s | ms | us
109
+ *
110
+ */
111
+ protected selectInterval(interval: string): typeof Interval {
112
+ switch (interval) {
113
+ case 's': return Interval
114
+ case 'ms': return IntervalMs
115
+ case 'us': return IntervalUs
116
+ }
117
+ return Interval
118
+ }
119
+
120
+ /**
121
+ * Return metric path
122
+ *
123
+ * @param device Device ID
124
+ * @param name Metric name
125
+ */
126
+ protected getMetricPath(device: string, name: string) {
127
+ return (device + '.' + name).toLowerCase()
128
+ }
129
+ }
@@ -0,0 +1,108 @@
1
+ /*
2
+ * Copyright © 2024 Boris Bobylev. All rights reserved.
3
+ * Licensed under the Apache License, Version 2.0
4
+ */
5
+
6
+ import BootClass from "./BootClass";
7
+ import BasicType from "../validator/types/BasicType";
8
+ import Rule from "../validator/Rule";
9
+ import { existsSync, mkdirSync, writeFileSync } from "fs";
10
+ import path from "path";
11
+ import ImportManager from "../ImportManager";
12
+ import { IContainerStructure } from "../Container";
13
+ import ErrorManager from "../errors/ErrorManager";
14
+
15
+ ErrorManager.register('StructureStorage', 'FKb5raEUDFgU', 'SS_STRUCT_NOT_FOUND', 'Structure ID not found', {
16
+ id: Rule.string().require().example('vrack').description('Structure id')
17
+ })
18
+ /**
19
+ * A boot class for storing the structure of a container.
20
+ *
21
+ * When a container is created, it is assigned a unique identifier.
22
+ * This identifier is used as parameters of methods.
23
+ *
24
+ * @see getById
25
+ * @see updateById
26
+ *
27
+ * When the container is loaded it calls “beforeLoaded”.
28
+ * At this point it updates the structure from disk to the structure
29
+ * of the container itself and writes the changes
30
+ *
31
+ * @see beforeLoadedUpdate
32
+ *
33
+ * */
34
+ export default class StructureStorage extends BootClass {
35
+
36
+ checkOptions(): { [key: string]: BasicType; } {
37
+ return {
38
+ structureDir: Rule.string().require().default('./structure')
39
+ }
40
+ }
41
+
42
+ process(): void {
43
+ if (!existsSync(this.options.structureDir)) mkdirSync(this.options.structureDir, { recursive: true })
44
+ this.Container.on('beforeLoaded', this.beforeLoadedUpdate.bind(this))
45
+ }
46
+
47
+ /**
48
+ * Updates the structure on disk using the structure of
49
+ * the container itself when it is loaded
50
+ *
51
+ *
52
+ * @see StructureStorage.process
53
+ */
54
+ async beforeLoadedUpdate(){
55
+ const fp = this.makeFilePath(this.Container.id)
56
+ let structure: IContainerStructure = {}
57
+ try {
58
+ if (existsSync(fp)) structure = ImportManager.importJSON(fp)
59
+ const cStruct = await this.Container.getStructure()
60
+ this.updateStructure(cStruct,structure, this.Container.id)
61
+ } catch (error) { this.error(error as Error) }
62
+ }
63
+
64
+ /**
65
+ * Returns structure by container identifier
66
+ *
67
+ * @param id Container ID
68
+ */
69
+ async getById(id: string): Promise<IContainerStructure>{
70
+ const fp = this.makeFilePath(id)
71
+ if (!existsSync(fp)) throw ErrorManager.make('SS_STRUCT_NOT_FOUND', { id })
72
+ return ImportManager.importJSON(fp)
73
+ }
74
+
75
+ /**
76
+ * Updating the container structure
77
+ *
78
+ * @param id Container ID
79
+ * @param structure updated container structure object
80
+ */
81
+ async updateById(id: string, structure: IContainerStructure){
82
+ const cStruct = await this.getById(id)
83
+ this.updateStructure(cStruct, structure, id)
84
+ }
85
+
86
+ /**
87
+ * Updates the display structure parameter from the file structure
88
+ *
89
+ * @param cStruct now container structure
90
+ * @param structure new structure
91
+ */
92
+ protected updateStructure(cStruct: IContainerStructure,structure: IContainerStructure, id: string) {
93
+ for (const dID in cStruct) {
94
+ if (structure[dID] && structure[dID].display) cStruct[dID].display = structure[dID].display
95
+ }
96
+ const fp = this.makeFilePath(id)
97
+ writeFileSync(fp, JSON.stringify(cStruct))
98
+ }
99
+
100
+ /**
101
+ * Forms the path to the structure file by its identifier
102
+ *
103
+ * @param id Container ID
104
+ */
105
+ protected makeFilePath(id: string) {
106
+ return path.join(this.options.structureDir, id + '.json')
107
+ }
108
+ }