vrack2-core 0.0.1 → 1.0.1
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 +33 -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 +311 -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 +15 -14
- 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 +346 -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
package/src/ImportManager.ts
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
|
-
import { existsSync, readFileSync } from "fs";
|
|
1
|
+
import { existsSync, readFileSync, lstatSync, readdirSync, statSync } from "fs";
|
|
2
2
|
import path from "path";
|
|
3
|
+
|
|
3
4
|
import ErrorManager from "./errors/ErrorManager";
|
|
4
5
|
import Rule from "./validator/Rule";
|
|
5
6
|
|
|
7
|
+
|
|
6
8
|
ErrorManager.register('ImportManager', 'kwRobwFPzc5g', 'IM_FILE_NOT_FOUND',
|
|
7
9
|
'Import file not found', {
|
|
8
10
|
filePath: Rule.string().description('Path to file')
|
|
@@ -52,6 +54,14 @@ export default class ImportManager {
|
|
|
52
54
|
return ti
|
|
53
55
|
}
|
|
54
56
|
|
|
57
|
+
/**
|
|
58
|
+
* Import class like a vrack2 device style
|
|
59
|
+
*
|
|
60
|
+
* Разделяюет переданную строку пути на 2 части.
|
|
61
|
+
* Первая честь является названием модуля, а вторая часть классом внутри него
|
|
62
|
+
*
|
|
63
|
+
* @example ImportManager.importClass('vrack2-core.Container')
|
|
64
|
+
*/
|
|
55
65
|
static async importClass(cs:string) {
|
|
56
66
|
const acts = cs.split('.')
|
|
57
67
|
const vendor = acts.shift()
|
|
@@ -65,7 +75,14 @@ export default class ImportManager {
|
|
|
65
75
|
return ret
|
|
66
76
|
}
|
|
67
77
|
|
|
68
|
-
|
|
78
|
+
/**
|
|
79
|
+
* Attempts to open a file and use its contents as json
|
|
80
|
+
*
|
|
81
|
+
* Returns the result of parsing json
|
|
82
|
+
*
|
|
83
|
+
* @returns {any} json parsing result
|
|
84
|
+
*/
|
|
85
|
+
static importJSON(filePath: string){
|
|
69
86
|
if (!path.isAbsolute(filePath)){
|
|
70
87
|
filePath = path.join(ImportManager.systemPath(), filePath)
|
|
71
88
|
}
|
|
@@ -76,6 +93,83 @@ export default class ImportManager {
|
|
|
76
93
|
throw ErrorManager.make('IM_FILE_NOT_FOUND', { filePath })
|
|
77
94
|
}
|
|
78
95
|
|
|
96
|
+
/**
|
|
97
|
+
* Returns the class name in the style of import vrack
|
|
98
|
+
*
|
|
99
|
+
* return `Container` from 'vrack2-core.Container' string
|
|
100
|
+
*
|
|
101
|
+
* @param cs Import class string
|
|
102
|
+
*
|
|
103
|
+
*/
|
|
104
|
+
static importClassName(cs: string){
|
|
105
|
+
const acts = cs.split('.')
|
|
106
|
+
return acts.pop()
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Returns the vendor name in the style of import vrack
|
|
111
|
+
*
|
|
112
|
+
* return `vrack2-core` from 'vrack2-core.Container' string
|
|
113
|
+
*
|
|
114
|
+
* @param cs Import class string
|
|
115
|
+
*/
|
|
116
|
+
static importVendorName(cs: string){
|
|
117
|
+
const acts = cs.split('.')
|
|
118
|
+
return acts.shift()
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Returns a list of directories in the specified directory.
|
|
123
|
+
*
|
|
124
|
+
* @param dir path to directory
|
|
125
|
+
*/
|
|
126
|
+
static dirList(dir: string){
|
|
127
|
+
const files = readdirSync(dir)
|
|
128
|
+
const result = []
|
|
129
|
+
for (const i in files) if (statSync(path.join(dir, files[i])).isDirectory()) result.push(files[i])
|
|
130
|
+
return result
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Returns a list of files in the specified directory.
|
|
135
|
+
*
|
|
136
|
+
* @param dir path to directory
|
|
137
|
+
*/
|
|
138
|
+
static fileList(dir: string){
|
|
139
|
+
const files = readdirSync(dir)
|
|
140
|
+
const result = []
|
|
141
|
+
for (const i in files) if (!statSync(path.join(dir, files[i])).isDirectory()) result.push(files[i])
|
|
142
|
+
return result
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Checks if a path is a directory
|
|
147
|
+
*
|
|
148
|
+
* @param dir path to directory
|
|
149
|
+
*/
|
|
150
|
+
static isDir(dir: string){
|
|
151
|
+
if (existsSync(dir) && lstatSync(dir).isDirectory()) return true
|
|
152
|
+
return false
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Checks if a path is a directory
|
|
157
|
+
*
|
|
158
|
+
* @param f path to file
|
|
159
|
+
*/
|
|
160
|
+
static isFile(f: string){
|
|
161
|
+
if (existsSync(f) && lstatSync(f).isFile()) return true
|
|
162
|
+
return false
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Try JSON parse
|
|
167
|
+
*
|
|
168
|
+
* Returns an CoreError[IM_JSON_INCORRECT] if there is a parsing error.
|
|
169
|
+
*
|
|
170
|
+
* @param jsonRaw JSON raw string
|
|
171
|
+
*
|
|
172
|
+
*/
|
|
79
173
|
static tryJsonParse(jsonRaw: string) : any{
|
|
80
174
|
try {
|
|
81
175
|
return JSON.parse(jsonRaw)
|
|
@@ -86,22 +180,36 @@ export default class ImportManager {
|
|
|
86
180
|
}
|
|
87
181
|
}
|
|
88
182
|
|
|
183
|
+
/**
|
|
184
|
+
* Return directory where VRack was launched from
|
|
185
|
+
*/
|
|
186
|
+
static systemPath(){
|
|
187
|
+
return process.cwd()
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Camelize string for input & action handlers
|
|
192
|
+
*
|
|
193
|
+
* Splits a string with a dot and returns the string
|
|
194
|
+
* with capital letters starting from the second word
|
|
195
|
+
*
|
|
196
|
+
* @example
|
|
197
|
+
* ```ts
|
|
198
|
+
* ImportManager.camelize('input.device.port') // return inputDevicePort
|
|
199
|
+
* ```
|
|
200
|
+
*
|
|
201
|
+
* @param text Like a `input.device.port` string
|
|
202
|
+
*/
|
|
89
203
|
static camelize(text: string) {
|
|
90
204
|
return text.replace(/^([A-Z])|[.]+(\w)/g, function (match, p1, p2, offset) {
|
|
91
205
|
if (p2) return p2.toUpperCase()
|
|
92
206
|
return p1.toLowerCase()
|
|
93
207
|
})
|
|
94
208
|
}
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
protected static getSub(imp: any, get: string){
|
|
98
|
-
return imp[get]
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
static systemPath(){
|
|
102
|
-
return process.cwd()
|
|
103
|
-
}
|
|
104
209
|
|
|
210
|
+
/**
|
|
211
|
+
* Try import method
|
|
212
|
+
*/
|
|
105
213
|
protected static async tryImport(p: string){
|
|
106
214
|
try {
|
|
107
215
|
return await import(p)
|
package/src/MainProcess.ts
CHANGED
|
@@ -1,18 +1,63 @@
|
|
|
1
|
+
import Bootstrap, { IBootListConfig } from "./Bootstrap";
|
|
1
2
|
import Container from "./Container";
|
|
2
|
-
import DeviceManager from "./DeviceManager";
|
|
3
3
|
import IServiceStructure from "./IServiceStructure";
|
|
4
|
-
import ImportManager from "./ImportManager";
|
|
5
4
|
|
|
5
|
+
interface IMainProcessInternalOptions {
|
|
6
|
+
/** Container ID */
|
|
7
|
+
id: string;
|
|
8
|
+
/** Service structure */
|
|
9
|
+
service: IServiceStructure;
|
|
10
|
+
/** Path to extends conf file */
|
|
11
|
+
confFile? : string;
|
|
12
|
+
/** Container class */
|
|
13
|
+
ContainerClass: typeof Container;
|
|
14
|
+
/** Bootstrap list config */
|
|
15
|
+
bootstrap: IBootListConfig
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface IMainProcessOptions {
|
|
19
|
+
/** Container ID */
|
|
20
|
+
id: string;
|
|
21
|
+
/** Service structure */
|
|
22
|
+
service: IServiceStructure;
|
|
23
|
+
/** Path to extends conf file */
|
|
24
|
+
confFile? : string;
|
|
25
|
+
/** Container class */
|
|
26
|
+
ContainerClass?: typeof Container;
|
|
27
|
+
/** Bootstrap list config */
|
|
28
|
+
bootstrap?: IBootListConfig
|
|
29
|
+
}
|
|
6
30
|
|
|
7
31
|
export default class MainProcess {
|
|
8
32
|
Container: Container
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
33
|
+
options: IMainProcessInternalOptions = {
|
|
34
|
+
id: 'vrack2',
|
|
35
|
+
service: {
|
|
36
|
+
devices:[] ,
|
|
37
|
+
connections:[],
|
|
38
|
+
},
|
|
39
|
+
ContainerClass: Container,
|
|
40
|
+
bootstrap: {
|
|
41
|
+
DeviceManager: { path: 'vrack2-core.DeviceManager', options: { storageDir: './storage' }},
|
|
42
|
+
DeviceStorage: { path: 'vrack2-core.DeviceFileStorage', options: {} },
|
|
43
|
+
StructureStorage: { path: 'vrack2-core.StructureStorage', options: {} },
|
|
44
|
+
DeviceMetrics: { path: 'vrack2-core.DeviceMetrics', options: {} }
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
Bootstrap: Bootstrap
|
|
48
|
+
constructor(config: IMainProcessOptions){
|
|
49
|
+
Object.assign(this.options, config)
|
|
50
|
+
this.Bootstrap = new Bootstrap(this.options.bootstrap)
|
|
51
|
+
this.Container = new this.options.ContainerClass(this.options.id, this.options.service, this.Bootstrap, this.options.confFile)
|
|
14
52
|
}
|
|
53
|
+
|
|
15
54
|
async run (){
|
|
16
|
-
await this.
|
|
55
|
+
await this.check()
|
|
56
|
+
await this.Container.runProcess()
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
async check(){
|
|
60
|
+
await this.Bootstrap.loadBootList(this.Container)
|
|
61
|
+
await this.Container.init()
|
|
17
62
|
}
|
|
18
63
|
}
|
package/src/Utility.ts
CHANGED
|
@@ -1,44 +1,43 @@
|
|
|
1
1
|
/*
|
|
2
|
-
* Copyright ©
|
|
2
|
+
* Copyright © 2025 Boris Bobylev. All rights reserved.
|
|
3
3
|
* Licensed under the Apache License, Version 2.0
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
* Преобразует строку типа `aaa.bbb.ccc` в строку типа `aaaBbbCcc`
|
|
8
|
-
* Обратите внимание на то, что первый символ первого актета не преобразуется,
|
|
9
|
-
* если есть такая необходимость, можно добавить точку в самое начало строки `.aaa.bbb.ccc`
|
|
10
|
-
* результат выполнения будет `AaaBbbCcc`
|
|
11
|
-
*/
|
|
12
|
-
function camelize(text: string) {
|
|
13
|
-
return text.replace(/^([A-Z])|[.]+(\w)/g, function (match, p1, p2, offset) {
|
|
14
|
-
if (p2) return p2.toUpperCase()
|
|
15
|
-
return p1.toLowerCase()
|
|
16
|
-
})
|
|
17
|
-
}
|
|
6
|
+
import util from "util"
|
|
18
7
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
function validJSON(text: string) {
|
|
26
|
-
try {
|
|
27
|
-
const cfg = JSON.parse(text)
|
|
28
|
-
if (!(cfg && typeof cfg === 'object')) return false
|
|
29
|
-
return cfg
|
|
30
|
-
} catch (e) {
|
|
31
|
-
return false
|
|
8
|
+
export default class Utility {
|
|
9
|
+
/**
|
|
10
|
+
* Check device name (device ID)
|
|
11
|
+
* */
|
|
12
|
+
static async isDeviceName(name: string) {
|
|
13
|
+
return /^[a-zA-Z0-9_*-:]+$/.test(name)
|
|
32
14
|
}
|
|
33
|
-
}
|
|
34
15
|
|
|
35
|
-
/**
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
16
|
+
/**
|
|
17
|
+
* Форматирует любое значение в красивую строку (с отступами, подсветкой и т. д.).
|
|
18
|
+
* Поддерживает: объекты, массивы, примитивы, Error, Map, Set, Buffer и др.
|
|
19
|
+
*
|
|
20
|
+
* @param {any} value - Значение для форматирования
|
|
21
|
+
* @param {object} [options] - Дополнительные опции (как в `util.inspect()`)
|
|
22
|
+
* @returns {string} Отформатированная строка
|
|
23
|
+
*/
|
|
24
|
+
static prettyFormat(value: any, options = {}) {
|
|
25
|
+
const defaultOptions = {
|
|
26
|
+
depth: null, // Показывать всю вложенность
|
|
27
|
+
colors: true, // Цвета в терминале
|
|
28
|
+
compact: false, // Не сжимать в одну строку
|
|
29
|
+
breakLength: 80, // Длина строки перед переносом
|
|
30
|
+
sorted: false, // Сортировка ключей (если true)
|
|
31
|
+
showHidden: false, // Показывать скрытые свойства (для объектов)
|
|
32
|
+
...options // Переопределение дефолтных опций
|
|
33
|
+
};
|
|
43
34
|
|
|
44
|
-
|
|
35
|
+
// Особые случаи (если нужно обработать их по-особому)
|
|
36
|
+
if (value instanceof Error) {
|
|
37
|
+
return value.stack; // Стек ошибки
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Всё остальное форматируем через util.inspect()
|
|
41
|
+
return util.inspect(value, defaultOptions);
|
|
42
|
+
}
|
|
43
|
+
}
|
package/src/actions/Action.ts
CHANGED
|
@@ -3,9 +3,21 @@
|
|
|
3
3
|
* Licensed under the Apache License, Version 2.0
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
+
import BasicAction from "./BasicAction";
|
|
6
7
|
import GlobalAction from "./GlobalAction";
|
|
7
8
|
|
|
9
|
+
/**
|
|
10
|
+
* A class for defining new actions.
|
|
11
|
+
* It is not necessary to use `new Action`.
|
|
12
|
+
* You must use the static method `Action.global` to define a new action
|
|
13
|
+
*/
|
|
8
14
|
export default class Action {
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* The only type of action game at the moment. Does not have any special properties.
|
|
18
|
+
*
|
|
19
|
+
* @see BasicAction
|
|
20
|
+
*/
|
|
9
21
|
static global() {
|
|
10
22
|
return new GlobalAction()
|
|
11
23
|
}
|
|
@@ -8,6 +8,10 @@ import IAction from "./IAction";
|
|
|
8
8
|
import ILocalAction from "./ILocalAction"
|
|
9
9
|
|
|
10
10
|
|
|
11
|
+
/**
|
|
12
|
+
* Action base class.
|
|
13
|
+
* Forms settings for a new action in `fluent interface` style
|
|
14
|
+
*/
|
|
11
15
|
export default class BasicAction {
|
|
12
16
|
protected action: ILocalAction
|
|
13
17
|
|
|
@@ -20,21 +24,80 @@ export default class BasicAction {
|
|
|
20
24
|
}
|
|
21
25
|
}
|
|
22
26
|
|
|
27
|
+
/**
|
|
28
|
+
* Sets the requirements for incoming `Action` parameters
|
|
29
|
+
* These rules will be applied every time the user invokes this action
|
|
30
|
+
*
|
|
31
|
+
* The method necessarily accepts an object.
|
|
32
|
+
* An action method cannot accept anything other than an object
|
|
33
|
+
*
|
|
34
|
+
* @example
|
|
35
|
+
*
|
|
36
|
+
* ```ts
|
|
37
|
+
* Action.global().requirements({
|
|
38
|
+
* id: Rule.number().require().default(0).description('Unique ID'),
|
|
39
|
+
* })
|
|
40
|
+
* ```
|
|
41
|
+
* @param req Object of BasicType rules
|
|
42
|
+
*/
|
|
23
43
|
requirements(req: { [key: string]: BasicType; }){
|
|
24
44
|
this.action.requirements = req
|
|
25
45
|
return this
|
|
26
46
|
}
|
|
27
47
|
|
|
48
|
+
/**
|
|
49
|
+
* Sets the requirements for the return value.
|
|
50
|
+
* This method does not guarantee that this action will return this particular data type.
|
|
51
|
+
* In fact, the return value is not validated.
|
|
52
|
+
* It may help to read the documentation for the device
|
|
53
|
+
*
|
|
54
|
+
* @example
|
|
55
|
+
*
|
|
56
|
+
* ```ts
|
|
57
|
+
* Action.global().returns({
|
|
58
|
+
* id: Rule.number().require().default(0).description('Unique ID'),
|
|
59
|
+
* })
|
|
60
|
+
* ```
|
|
61
|
+
*
|
|
62
|
+
* @param ret Object of BasicType rules
|
|
63
|
+
*/
|
|
28
64
|
returns (ret: { [key: string]: BasicType; }){
|
|
29
65
|
this.action.returns = ret
|
|
30
66
|
return this
|
|
31
67
|
}
|
|
32
68
|
|
|
69
|
+
/**
|
|
70
|
+
* Set description of Action
|
|
71
|
+
*
|
|
72
|
+
* @example
|
|
73
|
+
* ```ts
|
|
74
|
+
* Action.global().description('Short action description')
|
|
75
|
+
* ```
|
|
76
|
+
*/
|
|
33
77
|
description(des: string){
|
|
34
78
|
this.action.description = des
|
|
35
79
|
return this
|
|
36
80
|
}
|
|
37
81
|
|
|
82
|
+
/**
|
|
83
|
+
* Returns the internal Action object.
|
|
84
|
+
* This method is used inside VRack2 when processing an Action inside Container
|
|
85
|
+
*
|
|
86
|
+
* **Do not use this method if it is not necessary**
|
|
87
|
+
*/
|
|
88
|
+
exportRaw(){
|
|
89
|
+
return this.action
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Forms a new IAction object
|
|
94
|
+
* This method is used inside VRack2 when processing an Action inside Container
|
|
95
|
+
*
|
|
96
|
+
* **Do not use this method if it is not necessary**
|
|
97
|
+
*
|
|
98
|
+
* !hide for external users
|
|
99
|
+
* @private
|
|
100
|
+
*/
|
|
38
101
|
export(): IAction{
|
|
39
102
|
const nAction: IAction = {
|
|
40
103
|
type: this.action.type,
|
package/src/actions/IAction.ts
CHANGED
|
@@ -1,8 +1,15 @@
|
|
|
1
1
|
import IValidationRule from "../validator/IValidationRule";
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* Internal (exported) Action interface
|
|
5
|
+
*/
|
|
3
6
|
export default interface IAction {
|
|
7
|
+
/** Type of action ('global' as default) */
|
|
4
8
|
type: string;
|
|
9
|
+
/** Requirements for incoming data */
|
|
5
10
|
requirements: { [key: string]: IValidationRule; };
|
|
11
|
+
/** Requirements to returned data */
|
|
6
12
|
returns: { [key: string]: IValidationRule; };
|
|
13
|
+
/** A short text description of the action */
|
|
7
14
|
description: string;
|
|
8
15
|
}
|
|
@@ -1,8 +1,15 @@
|
|
|
1
1
|
import BasicType from "../validator/types/BasicType";
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* Internal (raw exported) Action interface
|
|
5
|
+
*/
|
|
3
6
|
export default interface ILocalAction {
|
|
7
|
+
/** Type of action ('global' as default) */
|
|
4
8
|
type: string;
|
|
9
|
+
/** Requirements for incoming data */
|
|
5
10
|
requirements: { [key: string]: BasicType; };
|
|
11
|
+
/** Requirements to returned data */
|
|
6
12
|
returns: { [key: string]: BasicType; };
|
|
13
|
+
/** A short text description of the action */
|
|
7
14
|
description: string;
|
|
8
15
|
}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright © 2024 Boris Bobylev. All rights reserved.
|
|
3
|
+
* Licensed under the Apache License, Version 2.0
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import Container from '../Container';
|
|
7
|
+
import BasicType from '../validator/types/BasicType';
|
|
8
|
+
import Validator from '../validator/Validator';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Boot classes are designed to override behavior outside of the container.
|
|
12
|
+
* They can also extend the container's capabilities.
|
|
13
|
+
*
|
|
14
|
+
* Boot classes are similar to devices inside the container,
|
|
15
|
+
* but they do not fall into it and do not have connections.
|
|
16
|
+
*
|
|
17
|
+
* Boot classes are created inside a Bootstrap class.
|
|
18
|
+
* To do this, you need to create a list of classes and specify them for bootstrapping,
|
|
19
|
+
*
|
|
20
|
+
* here is an example of such a list
|
|
21
|
+
*
|
|
22
|
+
* ```ts
|
|
23
|
+
* DeviceManager: { path: 'vrack2-core.DeviceManager', options: { storageDir: './storage' }},
|
|
24
|
+
* DeviceStorage: { path: 'vrack2-core.DeviceFileStorage', options: {} },
|
|
25
|
+
* StructureStorage: { path: 'vrack2-core.StructureStorage', options: {} },
|
|
26
|
+
* DeviceMetrics: { path: 'vrack2-core.DeviceMetrics', options: {} }
|
|
27
|
+
* ```
|
|
28
|
+
* Where:
|
|
29
|
+
* ```
|
|
30
|
+
* [key: string - Unique bootclass identifier] : {
|
|
31
|
+
* path: string - Path to bootclass,
|
|
32
|
+
* options: {any} - Bootclass settings
|
|
33
|
+
* }
|
|
34
|
+
* ```
|
|
35
|
+
*
|
|
36
|
+
* @see DeviceManager For boot class example
|
|
37
|
+
*/
|
|
38
|
+
export default class BootClass {
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* A unique identifier that will be filled in when the class is created within Bootstrap.
|
|
42
|
+
* Can be used to refer to the class directly
|
|
43
|
+
*
|
|
44
|
+
* @example 'DeviceName'
|
|
45
|
+
* */
|
|
46
|
+
id: string;
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* The path to a VRack2-style class that will be populated when the class is created inside Bootstrap.
|
|
50
|
+
* Can be used to determine class membership
|
|
51
|
+
*
|
|
52
|
+
* @example 'vrack.KeyManager'
|
|
53
|
+
*/
|
|
54
|
+
type: string;
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Container for which Boot classes are loaded
|
|
58
|
+
* */
|
|
59
|
+
Container: Container;
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Boot class parameters that will be passed from the settings of the list of boot classes
|
|
63
|
+
*
|
|
64
|
+
* @see checkOptions()
|
|
65
|
+
*/
|
|
66
|
+
options: { [key: string]: any } = {};
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Method returns rules for validation of boot class options
|
|
70
|
+
*
|
|
71
|
+
* @example
|
|
72
|
+
* ```ts
|
|
73
|
+
* return {
|
|
74
|
+
* keysPath: Rule.string().require().default('./keys.json')
|
|
75
|
+
* }
|
|
76
|
+
* ```
|
|
77
|
+
*/
|
|
78
|
+
checkOptions(): { [key: string]: BasicType; } {
|
|
79
|
+
return {}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* @param id Unique ID
|
|
84
|
+
* @param type Device type string
|
|
85
|
+
* @param Container Active loader container
|
|
86
|
+
*/
|
|
87
|
+
constructor(id: string, type: string, Container: Container, options: { [key: string]: any } ) {
|
|
88
|
+
this.id = id
|
|
89
|
+
this.type = type
|
|
90
|
+
this.Container = Container
|
|
91
|
+
this.options = options
|
|
92
|
+
|
|
93
|
+
// Validating
|
|
94
|
+
const rules = this.checkOptions()
|
|
95
|
+
Validator.validate(rules, this.options)
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* The method is the entry point to start the boot class
|
|
101
|
+
*/
|
|
102
|
+
process() { return }
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Similar to `process` but asynchronous,
|
|
106
|
+
* the bootstrap loader will wait for the execution of all
|
|
107
|
+
* the `processPromise` methods of all bootstrap classes.
|
|
108
|
+
*/
|
|
109
|
+
async processPromise() { return }
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Method for calling container system errors
|
|
113
|
+
*/
|
|
114
|
+
error(error: Error){
|
|
115
|
+
this.Container.emit('system.error', error)
|
|
116
|
+
}
|
|
117
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright © 2024 Boris Bobylev. All rights reserved.
|
|
3
|
+
* Licensed under the Apache License, Version 2.0
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import path from "path"
|
|
7
|
+
import fs from 'fs';
|
|
8
|
+
import ImportManager from "../ImportManager";
|
|
9
|
+
import BootClass from "./BootClass";
|
|
10
|
+
import BasicType from "../validator/types/BasicType";
|
|
11
|
+
import Rule from "../validator/Rule";
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* File storage for devices that are used by default
|
|
15
|
+
* Uses container events to load and save storage state
|
|
16
|
+
*
|
|
17
|
+
*/
|
|
18
|
+
export default class DeviceFileStorage extends BootClass {
|
|
19
|
+
|
|
20
|
+
checkOptions(): { [key: string]: BasicType; } {
|
|
21
|
+
return {
|
|
22
|
+
storageDir: Rule.string().require().default('./storage')
|
|
23
|
+
.description('Dir for storage files')
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
process(): void {
|
|
28
|
+
if (!ImportManager.isDir(this.options.storageDir)) fs.mkdirSync(this.options.storageDir, { recursive: true })
|
|
29
|
+
|
|
30
|
+
// Before process load storage data
|
|
31
|
+
this.Container.on('beforeProcess', () => {
|
|
32
|
+
for (const id in this.Container.devices) {
|
|
33
|
+
try {
|
|
34
|
+
this.Container.devices[id].storage = this.loadDeviceStorage(id)
|
|
35
|
+
} catch (error) {
|
|
36
|
+
this.Container.emit('system.error', error)
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
// On device save - save storage data
|
|
42
|
+
this.Container.on('device.save', (data: { device: string, data: string, trace: any }) => {
|
|
43
|
+
try {
|
|
44
|
+
this.saveDeviceStorage(data.device, data.trace)
|
|
45
|
+
} catch (error) {
|
|
46
|
+
this.Container.emit('system.error', error)
|
|
47
|
+
}
|
|
48
|
+
})
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* loading a device storage file
|
|
53
|
+
* If the primary storage file does not exist, an attempt will be made
|
|
54
|
+
* to locate the backup temporary storage file.
|
|
55
|
+
*
|
|
56
|
+
* @param deviceId Device ID
|
|
57
|
+
* @returns {any} Imported JSON
|
|
58
|
+
*/
|
|
59
|
+
async loadDeviceStorage(deviceId: string) {
|
|
60
|
+
const fp = this.makeDeviceStoragePath(deviceId)
|
|
61
|
+
const fptmp = this.makeDeviceStoragePath(deviceId, '-tmp')
|
|
62
|
+
if (!fs.existsSync(fp)) {
|
|
63
|
+
if (!fs.existsSync(fptmp)) return {}
|
|
64
|
+
fs.renameSync(fptmp, fp)
|
|
65
|
+
}
|
|
66
|
+
return ImportManager.importJSON(fp)
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Save data device to storage
|
|
71
|
+
* First, a temporary file is created,
|
|
72
|
+
* and then the temporary file is copied to the main file
|
|
73
|
+
*
|
|
74
|
+
* @param deviceId Device ID
|
|
75
|
+
* @param trace Data object for storage
|
|
76
|
+
*/
|
|
77
|
+
async saveDeviceStorage(deviceId: string, trace: any) {
|
|
78
|
+
const fp = this.makeDeviceStoragePath(deviceId)
|
|
79
|
+
const dp = path.join(this.options.storageDir, this.Container.id)
|
|
80
|
+
const fptmp = this.makeDeviceStoragePath(deviceId, '-tmp')
|
|
81
|
+
if (!fs.existsSync(dp)) { fs.mkdirSync(dp, { recursive: true }) }
|
|
82
|
+
fs.writeFileSync(fptmp, JSON.stringify(trace))
|
|
83
|
+
if (fs.existsSync(fp)) fs.rmSync(fp)
|
|
84
|
+
fs.renameSync(fptmp, fp)
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Forms the path to the storage file
|
|
89
|
+
*
|
|
90
|
+
* @param deviceId Device ID
|
|
91
|
+
* @param prefix Prefix between the device name and its extension
|
|
92
|
+
*/
|
|
93
|
+
protected makeDeviceStoragePath(deviceId: string, prefix = '') {
|
|
94
|
+
return path.join(this.options.storageDir, this.Container.id, deviceId + prefix + '.json')
|
|
95
|
+
}
|
|
96
|
+
}
|