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
@@ -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
- static async importJSON(filePath: string){
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)
@@ -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
- DeviceManager : DeviceManager
10
- constructor(ContainerClass: typeof Container, DeviceManagerClass: typeof DeviceManager, service: IServiceStructure){
11
- this.DeviceManager = new DeviceManagerClass(ImportManager.systemPath(), './devices')
12
- this.DeviceManager.updateList()
13
- this.Container = new ContainerClass(service, this.DeviceManager)
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.Container.run()
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 © 2022 Boris Bobylev. All rights reserved.
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
- * Проверяет, является ли JSON строка валидным JSON
21
- * Обычно используется, когда неважно в чем конретно проблема в JSON
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
- * Преобразует строку в локальный путь VRack, на данный момент
37
- * это просто приведение строки в lowerCase
38
- *
39
- */
40
- function toPath(text: string) {
41
- return text.toLowerCase()
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
- export { camelize, validJSON, toPath }
35
+ // Особые случаи (если нужно обработать их по-особому)
36
+ if (value instanceof Error) {
37
+ return value.stack; // Стек ошибки
38
+ }
39
+
40
+ // Всё остальное форматируем через util.inspect()
41
+ return util.inspect(value, defaultOptions);
42
+ }
43
+ }
@@ -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,
@@ -5,6 +5,11 @@
5
5
 
6
6
  import BasicAction from "./BasicAction";
7
7
 
8
+ /**
9
+ * Standart action class
10
+ *
11
+ * @see BasicAction
12
+ */
8
13
  export default class GlobalAction extends BasicAction {
9
14
 
10
15
  constructor() {
@@ -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
+ }