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.
- package/README.md +25 -4
- package/docs/Bootstrap.md +77 -0
- package/docs/Container.md +124 -0
- package/docs/Device.md +272 -0
- package/docs/FastStart.md +111 -0
- package/docs/Structure.md +148 -0
- package/lib/Bootstrap.d.ts +79 -0
- package/lib/Bootstrap.js +103 -0
- package/lib/Container.d.ts +202 -6
- package/lib/Container.js +295 -27
- package/lib/IServiceStructure.d.ts +8 -0
- package/lib/IStructureDevice.d.ts +5 -0
- package/lib/ImportManager.d.ts +85 -3
- package/lib/ImportManager.js +122 -16
- package/lib/MainProcess.d.ts +30 -3
- package/lib/MainProcess.js +28 -6
- package/lib/Utility.d.ts +15 -21
- package/lib/Utility.js +40 -40
- package/lib/actions/Action.d.ts +10 -0
- package/lib/actions/Action.js +10 -0
- package/lib/actions/BasicAction.d.ts +60 -0
- package/lib/actions/BasicAction.js +62 -0
- package/lib/actions/GlobalAction.d.ts +5 -0
- package/lib/actions/GlobalAction.js +5 -0
- package/lib/actions/IAction.d.ts +7 -0
- package/lib/actions/ILocalAction.d.ts +7 -0
- package/lib/boot/BootClass.d.ts +93 -0
- package/lib/boot/BootClass.js +101 -0
- package/lib/boot/DeviceFileStorage.d.ts +38 -0
- package/lib/boot/DeviceFileStorage.js +112 -0
- package/lib/boot/DeviceManager.d.ts +190 -0
- package/lib/boot/DeviceManager.js +306 -0
- package/lib/boot/DeviceMetrics.d.ts +82 -0
- package/lib/boot/DeviceMetrics.js +128 -0
- package/lib/boot/StructureStorage.d.ts +59 -0
- package/lib/boot/StructureStorage.js +125 -0
- package/lib/errors/CoreError.d.ts +42 -25
- package/lib/errors/CoreError.js +44 -24
- package/lib/errors/ErrorManager.d.ts +18 -20
- package/lib/errors/ErrorManager.js +23 -22
- package/lib/index.d.ts +20 -4
- package/lib/index.js +28 -4
- package/lib/metrics/BasicMetric.d.ts +49 -0
- package/lib/metrics/BasicMetric.js +79 -0
- package/lib/metrics/IMetricSettings.d.ts +32 -0
- package/lib/metrics/IMetricSettings.js +2 -0
- package/lib/metrics/IvMs.d.ts +9 -0
- package/lib/metrics/IvMs.js +22 -0
- package/lib/metrics/IvS.d.ts +9 -0
- package/lib/metrics/IvS.js +22 -0
- package/lib/metrics/IvUs.d.ts +9 -0
- package/lib/metrics/IvUs.js +22 -0
- package/lib/metrics/Metric.d.ts +17 -0
- package/lib/metrics/Metric.js +27 -0
- package/lib/ports/BasicPort.d.ts +39 -0
- package/lib/ports/BasicPort.js +39 -0
- package/lib/ports/ILocalPort.d.ts +7 -0
- package/lib/ports/IPort.d.ts +7 -0
- package/lib/ports/Port.d.ts +10 -0
- package/lib/ports/Port.js +10 -0
- package/lib/ports/ReturnPort.d.ts +5 -0
- package/lib/ports/ReturnPort.js +5 -0
- package/lib/service/Device.d.ts +213 -78
- package/lib/service/Device.js +185 -83
- package/lib/service/DeviceConnect.d.ts +4 -8
- package/lib/service/DeviceConnect.js +4 -8
- package/lib/service/DevicePort.d.ts +15 -6
- package/lib/service/DevicePort.js +29 -12
- package/lib/service/IDeviceEvent.d.ts +4 -1
- package/lib/validator/IValidationProblem.d.ts +7 -0
- package/lib/validator/IValidationRule.d.ts +12 -0
- package/lib/validator/IValidationSubrule.d.ts +2 -0
- package/lib/validator/Rule.d.ts +6 -2
- package/lib/validator/Rule.js +12 -2
- package/lib/validator/Validator.d.ts +48 -3
- package/lib/validator/Validator.js +70 -18
- package/lib/validator/types/AnyType.d.ts +17 -0
- package/lib/validator/types/AnyType.js +34 -0
- package/lib/validator/types/ArrayType.d.ts +37 -4
- package/lib/validator/types/ArrayType.js +42 -6
- package/lib/validator/types/BasicType.d.ts +67 -7
- package/lib/validator/types/BasicType.js +74 -8
- package/lib/validator/types/BooleanType.d.ts +23 -0
- package/lib/validator/types/BooleanType.js +47 -0
- package/lib/validator/types/FunctionType.d.ts +17 -0
- package/lib/validator/types/FunctionType.js +38 -0
- package/lib/validator/types/NumberType.d.ts +40 -5
- package/lib/validator/types/NumberType.js +53 -14
- package/lib/validator/types/ObjectType.d.ts +32 -5
- package/lib/validator/types/ObjectType.js +42 -8
- package/lib/validator/types/StringType.d.ts +30 -5
- package/lib/validator/types/StringType.js +33 -7
- package/package.json +10 -9
- package/src/Bootstrap.ts +122 -0
- package/src/Container.ts +411 -43
- package/src/IServiceStructure.ts +9 -0
- package/src/IStructureDevice.ts +5 -0
- package/src/ImportManager.ts +119 -11
- package/src/MainProcess.ts +53 -8
- package/src/Utility.ts +35 -36
- package/src/actions/Action.ts +12 -0
- package/src/actions/BasicAction.ts +63 -0
- package/src/actions/GlobalAction.ts +5 -0
- package/src/actions/IAction.ts +7 -0
- package/src/actions/ILocalAction.ts +7 -0
- package/src/boot/BootClass.ts +117 -0
- package/src/boot/DeviceFileStorage.ts +96 -0
- package/src/boot/DeviceManager.ts +339 -0
- package/src/boot/DeviceMetrics.ts +129 -0
- package/src/boot/StructureStorage.ts +108 -0
- package/src/errors/CoreError.ts +52 -26
- package/src/errors/ErrorManager.ts +46 -33
- package/src/index.ts +30 -6
- package/src/metrics/BasicMetric.ts +84 -0
- package/src/metrics/IMetricSettings.ts +38 -0
- package/src/metrics/IvMs.ts +18 -0
- package/src/metrics/IvS.ts +18 -0
- package/src/metrics/IvUs.ts +17 -0
- package/src/metrics/Metric.ts +25 -0
- package/src/ports/BasicPort.ts +39 -0
- package/src/ports/ILocalPort.ts +7 -0
- package/src/ports/IPort.ts +7 -0
- package/src/ports/Port.ts +11 -1
- package/src/ports/ReturnPort.ts +5 -0
- package/src/service/Device.ts +234 -103
- package/src/service/DeviceConnect.ts +4 -8
- package/src/service/DevicePort.ts +30 -11
- package/src/service/IDeviceEvent.ts +4 -1
- package/src/validator/IValidationProblem.ts +7 -0
- package/src/validator/IValidationRule.ts +12 -0
- package/src/validator/IValidationSubrule.ts +3 -0
- package/src/validator/Rule.ts +16 -2
- package/src/validator/Validator.ts +74 -23
- package/src/validator/types/AnyType.ts +32 -0
- package/src/validator/types/ArrayType.ts +43 -7
- package/src/validator/types/BasicType.ts +78 -9
- package/src/validator/types/BooleanType.ts +49 -0
- package/src/validator/types/FunctionType.ts +39 -0
- package/src/validator/types/NumberType.ts +53 -15
- package/src/validator/types/ObjectType.ts +52 -14
- package/src/validator/types/StringType.ts +34 -10
- package/docs/RU-README.md +0 -6
- package/lib/DeviceManager.d.ts +0 -32
- package/lib/DeviceManager.js +0 -143
- package/lib/test.d.ts +0 -1
- package/lib/test.js +0 -58
- package/src/DeviceManager.ts +0 -124
- 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
|
+
}
|