@microtronics/studio-cli 0.60.0 → 0.62.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.
@@ -1,889 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.YamlPreCompiler = void 0;
4
- const yaml_1 = require("yaml");
5
- const diagnostics_1 = require("../diagnostics");
6
- const coreDefinitions_1 = require("./coreDefinitions");
7
- const helper_1 = require("../helper");
8
- var DDEContainerName = coreDefinitions_1.DDE_Core.DDEContainerName;
9
- var allowedRelationTypes = coreDefinitions_1.DDE_Core.allowedRelationTypes;
10
- var regexRelation = coreDefinitions_1.DDE_Core.regexRelation;
11
- var ContainerType = coreDefinitions_1.DDE_Core.ContainerType;
12
- var checkForReservedFieldName = coreDefinitions_1.DDE_Core.checkForReservedFieldName;
13
- var ContainerName = coreDefinitions_1.DDE_Core.ContainerName;
14
- var CONTAINER_DEFS = coreDefinitions_1.DDE_Core.CONTAINER_DEFS;
15
- var SERVER_CONTAINER = coreDefinitions_1.DDE_Core.SERVER_CONTAINER;
16
- var HISTDATA_CONTAINER = coreDefinitions_1.DDE_Core.HISTDATA_CONTAINER;
17
- var CONFIG_CONTAINER = coreDefinitions_1.DDE_Core.CONFIG_CONTAINER;
18
- var TransferDirection = coreDefinitions_1.DDE_Core.TransferDirection;
19
- var fieldTypeDefinitions = coreDefinitions_1.DDE_Core.fieldTypeDefinitions;
20
- var yamlRangeToLineInformation = helper_1.Helper.yamlRangeToLineInformation;
21
- const api_1 = require("../api");
22
- var YamlPreCompiler;
23
- (function (YamlPreCompiler) {
24
- class PreCompiler {
25
- manifest;
26
- libraryFiles;
27
- ddeHistory;
28
- fieldNamesPerContainer = {};
29
- usedContainerNames = {};
30
- fileDiagnostics = [];
31
- libraryDDE = [];
32
- libraryYaml = {};
33
- constructor(manifest, libraryFiles, ddeHistory) {
34
- this.manifest = manifest;
35
- this.libraryFiles = libraryFiles;
36
- this.ddeHistory = ddeHistory;
37
- }
38
- /**
39
- * Parse the given library files and return the diagnostics
40
- */
41
- parseLibraries() {
42
- this.libraryDDE.length = 0;
43
- const libDiagnostics = [];
44
- this.libraryFiles.forEach(file => {
45
- const { parsedDDE, diagnostics, parsedYaml } = this.parseYamlFile(file.path, file.yaml, file.libraryName);
46
- libDiagnostics.push(...diagnostics);
47
- this.libraryDDE.push(parsedDDE);
48
- this.libraryYaml[file.libraryName] = parsedYaml;
49
- });
50
- return libDiagnostics;
51
- }
52
- get previousVersion() {
53
- return { setPointList: { ...this.ddeHistory?.setPointList }, containers: { ...this.ddeHistory?.containers } };
54
- }
55
- getContainerOfPreviousVersion(containerName) {
56
- const previousDDE = this.previousVersion;
57
- const cleanContainerName = containerName.startsWith('timeseries_') ? containerName.substring(11) : containerName;
58
- return (Object.values(previousDDE.containers).find(containerDefinition => containerDefinition.title === cleanContainerName) || null);
59
- }
60
- parseMainDDE(filePath, yaml) {
61
- const { parsedDDE, diagnostics } = this.parseYamlFile(filePath, yaml);
62
- const hasError = diagnostics.find(diagnostic => diagnostic.level === 'error');
63
- //early abort if any file has an error
64
- if (hasError) {
65
- return { ddeJSON: null, diagnostics };
66
- }
67
- const mergedDDE = this.mergeAndFormat(parsedDDE);
68
- return { ddeJSON: mergedDDE, diagnostics };
69
- }
70
- /**
71
- * Parses a YAML file and validates its content to produce a structured JSON object along with diagnostic information.
72
- *
73
- * @param filePath The URI of the YAML file being parsed.
74
- * @param yaml The raw YAML content to parse.
75
- * @param libraryName An optional parameter specifying the library name for additional validation.
76
- * @return An object containing the parsed DDE JSON and an array of diagnostic messages related to the file.
77
- */
78
- parseYamlFile(filePath, yaml, libraryName) {
79
- this.fileDiagnostics.length = 0;
80
- const lineCounter = new yaml_1.LineCounter();
81
- const yamlDoc = (0, yaml_1.parseDocument)(yaml, { lineCounter });
82
- // dde is empty - skip parsing
83
- if (!yamlDoc.contents) {
84
- return {
85
- parsedDDE: {
86
- containers: {},
87
- setPointList: {}
88
- },
89
- diagnostics: [],
90
- parsedYaml: null
91
- };
92
- }
93
- const docContent = yamlDoc.contents || new yaml_1.YAMLMap();
94
- const containerList = this.validateContainerDefinition(docContent.items);
95
- containerList.forEach(container => {
96
- this.validateAllowedContainer(filePath, container, lineCounter, libraryName);
97
- this.validateContainerFields(yamlDoc, filePath, container, lineCounter);
98
- });
99
- // mapAsMap is used to keep order of fields:
100
- return {
101
- parsedDDE: this.yamlDocToDdeJson(yamlDoc.toJS({ mapAsMap: true }), libraryName),
102
- diagnostics: [...this.fileDiagnostics],
103
- parsedYaml: yamlDoc
104
- };
105
- }
106
- // eslint-disable-next-line sonarjs/cognitive-complexity
107
- yamlDocToDdeJson(ddeMap, libraryName) {
108
- const ddeJson = {
109
- containers: {},
110
- setPointList: {}
111
- };
112
- const allContainer = new Map();
113
- const allowedContainer = [...Object.values(DDEContainerName)];
114
- if (ddeMap.get('cloudProject')) {
115
- const knownContainer = Object.values(this.previousVersion.containers).map(containerDefinition => {
116
- return containerDefinition.title;
117
- });
118
- const allContainer = [...new Set([...allowedContainer, ...knownContainer])];
119
- allowedContainer.length = 0;
120
- allowedContainer.push(...allContainer);
121
- }
122
- for (const containerName of allowedContainer) {
123
- if (ddeMap.has(containerName)) {
124
- allContainer.set(containerName, ddeMap.get(containerName));
125
- }
126
- }
127
- const timeseries = ddeMap.get('timeseries');
128
- if (timeseries) {
129
- for (const [timeseriesName, timeseriesContent] of timeseries) {
130
- allContainer.set(timeseriesName, timeseriesContent);
131
- }
132
- }
133
- for (const [containerName, containerContent] of allContainer) {
134
- const { realContainer, transferDirection, legacyTitle } = this.getRealContainerInformation(containerName);
135
- if (!realContainer) {
136
- continue;
137
- }
138
- const containerDefinition = CONTAINER_DEFS[realContainer];
139
- const containerTitle = containerName.includes('timeseries_')
140
- ? containerName.replace('timeseries_', '')
141
- : containerName;
142
- const containerInfo = {
143
- name: realContainer,
144
- title: containerTitle,
145
- previousTitle: legacyTitle,
146
- containerType: containerDefinition.ctype,
147
- volatile: containerName === DDEContainerName.volatile,
148
- transferDirection,
149
- fieldCount: containerContent.size,
150
- knownNames: [],
151
- libraries: [],
152
- arrays: {},
153
- shadows: {},
154
- fields: [],
155
- size: 0,
156
- fieldIndex: 0
157
- };
158
- if (libraryName) {
159
- containerInfo.libraries.push(libraryName);
160
- }
161
- for (const [fieldName, fieldContent] of containerContent) {
162
- const containerFields = [];
163
- const currentFieldType = fieldContent.get('type');
164
- const typeDefinition = fieldTypeDefinitions[currentFieldType];
165
- if (!typeDefinition && currentFieldType !== 'array') {
166
- continue;
167
- }
168
- // handle arrays with subfields
169
- if (currentFieldType === 'array') {
170
- // generate dde fields for the current array...
171
- const allArrayFields = [];
172
- const length = fieldContent.get('length');
173
- const arrayFields = fieldContent.get('fields');
174
- const arrayInformation = {
175
- name: fieldName,
176
- length: length,
177
- library: libraryName || null,
178
- fields: []
179
- };
180
- // store the information about the array within the container info
181
- containerInfo.arrays[fieldName] = arrayInformation;
182
- for (let arrayIdx = 0; arrayIdx < length; arrayIdx++) {
183
- const arrayRef = {
184
- name: fieldName,
185
- index: arrayIdx
186
- };
187
- // iterate through the subfields and create them
188
- for (const [subFieldName, subFieldContent] of arrayFields) {
189
- const subTypeDefinition = fieldTypeDefinitions[subFieldContent.get('type')];
190
- if (!subTypeDefinition) {
191
- continue;
192
- }
193
- // only add the field if it's unknown
194
- if (!arrayInformation.fields?.find(field => field.name === subFieldName)) {
195
- // @ts-ignore
196
- arrayInformation.fields.push({
197
- name: subFieldName,
198
- type: subFieldContent.get('type'),
199
- byteSize: subTypeDefinition.len || subFieldContent.get('bytes') || subFieldContent.get('length')
200
- });
201
- }
202
- const fullName = `${fieldName}[${arrayIdx}].${subFieldName}`;
203
- const { /*arrayInfo,*/ fields, shadowInfo } = this.createDDEJsonFields(fullName, libraryName, subTypeDefinition, subFieldContent);
204
- // the subfield is an array, add the array info
205
- // todo are subarrays even possible?
206
- /*if (arrayInfo) {
207
- containerInfo.arrays[arrayInfo.name] = arrayInfo;
208
- }*/
209
- fields.forEach(field => {
210
- field.arrayRef = arrayRef;
211
- });
212
- if (shadowInfo) {
213
- containerInfo.shadows[shadowInfo.name] = shadowInfo;
214
- }
215
- allArrayFields.push(...fields);
216
- }
217
- }
218
- containerFields.push(...allArrayFields);
219
- }
220
- else {
221
- // standard container field with possible flat array or shadow fields
222
- const { arrayInfo, fields, shadowInfo } = this.createDDEJsonFields(fieldName, libraryName, typeDefinition, fieldContent);
223
- if (arrayInfo) {
224
- containerInfo.arrays[arrayInfo.name] = arrayInfo;
225
- }
226
- if (shadowInfo) {
227
- containerInfo.shadows[shadowInfo.name] = shadowInfo;
228
- }
229
- containerFields.push(...fields);
230
- }
231
- // calculate the container size
232
- containerFields.forEach(field => {
233
- field.index = ++containerInfo.fieldIndex;
234
- containerInfo.fields.push(field);
235
- containerInfo.size += field.byteSize;
236
- });
237
- }
238
- ddeJson.containers[realContainer] = containerInfo;
239
- }
240
- return ddeJson;
241
- }
242
- getRealContainerInformation(ddeContainerName) {
243
- let realContainer = undefined;
244
- let transferDirection = undefined;
245
- // keep track of the previous container name of an imported legacy project
246
- let legacyTitle = undefined;
247
- if (this.previousVersion) {
248
- const currentName = ddeContainerName.startsWith('timeseries_')
249
- ? ddeContainerName.substring(11)
250
- : ddeContainerName;
251
- const { name, direction, previousTitle } = this.findPreviousUsedContainer(currentName);
252
- realContainer = name;
253
- transferDirection = direction;
254
- legacyTitle = previousTitle;
255
- }
256
- if (!realContainer) {
257
- // reuse known container
258
- const isKnown = Object.entries(this.usedContainerNames).find(([, ddeName]) => ddeName === ddeContainerName);
259
- if (isKnown) {
260
- realContainer = isKnown[0];
261
- }
262
- }
263
- if (!realContainer) {
264
- realContainer = this.findNextFreeContainer(ddeContainerName);
265
- }
266
- this.usedContainerNames[realContainer] = ddeContainerName;
267
- if (!transferDirection) {
268
- transferDirection = [DDEContainerName.setting, DDEContainerName.command].includes(ddeContainerName)
269
- ? TransferDirection.down
270
- : TransferDirection.up;
271
- }
272
- if (realContainer && [ContainerName.configA, ContainerName.configB].includes(realContainer)) {
273
- transferDirection = TransferDirection.backendOnly;
274
- }
275
- return { realContainer, transferDirection, legacyTitle };
276
- }
277
- findNextFreeContainer(ddeContainerName) {
278
- // aloha is a predefined container
279
- if (ddeContainerName === ContainerName.aloha) {
280
- return ContainerName.aloha;
281
- }
282
- let containerList = CONFIG_CONTAINER;
283
- // timeseries container:
284
- if (ddeContainerName.startsWith('timeseries_')) {
285
- //get the next available histdata container.
286
- containerList = HISTDATA_CONTAINER;
287
- }
288
- else if (
289
- // server container
290
- [DDEContainerName.relations, DDEContainerName.storage].includes(ddeContainerName)) {
291
- containerList = SERVER_CONTAINER;
292
- }
293
- const previousContainerList = this.previousVersion?.containers;
294
- // filter out all containers that are already used
295
- if (previousContainerList) {
296
- containerList = containerList.filter(containerName => {
297
- return !Object.hasOwn(previousContainerList, containerName);
298
- });
299
- }
300
- return containerList.find(containerName => {
301
- return !Object.hasOwn(this.usedContainerNames, containerName);
302
- });
303
- }
304
- findPreviousUsedContainer(ddeContainerName) {
305
- const previousContainerList = this.previousVersion.containers;
306
- const previousContainer = Object.values(previousContainerList).find(containerDefinition => {
307
- return containerDefinition.title === ddeContainerName;
308
- });
309
- return {
310
- name: previousContainer?.name,
311
- direction: previousContainer?.transferDirection,
312
- previousTitle: previousContainer?.previousTitle
313
- };
314
- }
315
- createDDEJsonFields(fieldName, libraryName, typeDefinition, fieldContent) {
316
- const arrayLength = fieldContent.get('array');
317
- const fields = [];
318
- let arrayInfo = undefined;
319
- let shadowInfo = undefined;
320
- if (arrayLength) {
321
- arrayInfo = {
322
- name: fieldName,
323
- library: libraryName || null,
324
- length: arrayLength
325
- };
326
- // add array info to container info
327
- for (let fieldArrayIdx = 0; fieldArrayIdx < arrayLength; fieldArrayIdx++) {
328
- const arrayFieldName = `${fieldName.toLowerCase()}[${fieldArrayIdx}]`;
329
- fields.push(this.buildDDEJsonField(arrayFieldName, fieldContent, { name: fieldName, index: fieldArrayIdx }, typeDefinition, libraryName, null));
330
- }
331
- }
332
- else {
333
- const newField = this.buildDDEJsonField(fieldName, fieldContent, null, typeDefinition, libraryName, null);
334
- fields.push(newField);
335
- const shadowFieldContent = fieldContent.get('shadowFields');
336
- // validate if the given field has shadow fields:
337
- if (shadowFieldContent) {
338
- shadowInfo = {
339
- name: newField.name,
340
- byteSize: newField.byteSize,
341
- byteOffset: newField.byteOffset //init with null, will be filled afterwards!
342
- };
343
- for (const [subFieldName, subFieldContent] of shadowFieldContent) {
344
- let shadowFieldName = subFieldName;
345
- if (fieldName.includes('.')) {
346
- shadowFieldName = fieldName.split('.')[0] + `.${subFieldName}`;
347
- }
348
- fields.push(this.buildDDEJsonField(shadowFieldName, subFieldContent, null, typeDefinition, libraryName, newField.name));
349
- }
350
- }
351
- }
352
- return {
353
- arrayInfo,
354
- shadowInfo,
355
- fields: fields
356
- };
357
- }
358
- // eslint-disable-next-line sonarjs/cognitive-complexity
359
- buildDDEJsonField(name, fieldContent, arrayRef, typeDefinition, libraryName, shadowRef) {
360
- return {
361
- id: name.toLowerCase(),
362
- name: name,
363
- index: 0,
364
- library: libraryName || null,
365
- byteOffset: 0,
366
- // either length of type || if binary bytes || if string length
367
- byteSize: typeDefinition.len || fieldContent.get('bytes') || fieldContent.get('length'),
368
- type: fieldContent.get('type'),
369
- title: fieldContent.get('title') || null,
370
- arrayRef,
371
- shadowRef,
372
- unit: fieldContent.get('unit'),
373
- hex: fieldContent.get('hex'),
374
- decimalPlaces: fieldContent.get('decimalPlaces'),
375
- writePermission: this.parseUserLevel(fieldContent.get('writePermission')),
376
- readPermission: this.parseUserLevel(fieldContent.get('readPermission')),
377
- channelMode: this.parseChannelMode(fieldContent.get('channelMode')),
378
- defaultValue: fieldContent.get('defaultValue'),
379
- scale: fieldContent.get('scale'),
380
- offset: fieldContent.get('offset'),
381
- chartMinimum: fieldContent.get('chartMinimum'),
382
- description: fieldContent.get('description'),
383
- chartMaximum: fieldContent.get('chartMaximum'),
384
- chartAlarmLow: fieldContent.get('chartAlarmLow'),
385
- chartAlarmHigh: fieldContent.get('chartAlarmHigh'),
386
- chartWarningLow: fieldContent.get('chartWarningLow'),
387
- chartWarningHigh: fieldContent.get('chartWarningHigh'),
388
- chartHysteresis: fieldContent.get('chartHysteresis'),
389
- chartTriggerThreshold: fieldContent.get('chartTriggerThreshold'),
390
- chartTriggerRelation: this.parseTriggerUndershoot(fieldContent.get('chartTriggerRelation')),
391
- chartSetPoint: fieldContent.get('chartSetPoint'),
392
- utilsTransferInterval: Boolean(fieldContent.get('utilsTransferInterval')),
393
- bitmask: fieldContent.get('bitmask')
394
- };
395
- }
396
- resolvePossibleAlias(yamlDoc, value) {
397
- return (0, yaml_1.isAlias)(value) ? value.resolve(yamlDoc).value : value?.value;
398
- }
399
- validateContainerDefinition(yamlItems) {
400
- const definedContainer = [];
401
- yamlItems.forEach(yamlItem => {
402
- const itemKey = yamlItem.key;
403
- const containerName = itemKey.value;
404
- if (containerName === 'timeseries') {
405
- yamlItem.value.items.forEach(timeSeriesContainer => {
406
- timeSeriesContainer.key.value = `timeseries_${timeSeriesContainer.key}`;
407
- //@ts-ignore
408
- definedContainer.push(timeSeriesContainer);
409
- });
410
- }
411
- else if (!['dde', 'cloudProject', 'constants'].includes(containerName)) {
412
- //@ts-ignore
413
- definedContainer.push(yamlItem);
414
- }
415
- });
416
- return definedContainer;
417
- }
418
- parseUserLevel(userLevelString) {
419
- if (userLevelString === 'read-only') {
420
- return 99;
421
- }
422
- if (userLevelString?.startsWith('UL')) {
423
- return Number(userLevelString.substring(2));
424
- }
425
- return undefined;
426
- }
427
- parseChannelMode(channelModeString) {
428
- if (!channelModeString) {
429
- return undefined;
430
- }
431
- return ({
432
- digital: 1,
433
- 'day-counter': 2,
434
- 'interval-counter': 3,
435
- analog: 6,
436
- 'infinite-counter': 12
437
- }[channelModeString] ?? channelModeString);
438
- }
439
- parseTriggerUndershoot(triggerUndershoot) {
440
- if (!triggerUndershoot) {
441
- return undefined;
442
- }
443
- return ({
444
- 'greater-or-equal': 0,
445
- 'less-or-equal': 1
446
- }[triggerUndershoot] ?? triggerUndershoot);
447
- }
448
- validateAllowedContainer(sourceUri, container, lineCounter, libraryName) {
449
- const containerFields = container.value; //(container.value as YAMLMap).get('fields', true);
450
- if (!containerFields) {
451
- return;
452
- }
453
- const containerName = container.key.value;
454
- if (libraryName && (containerName.startsWith('aloha') || containerName.startsWith('timeseries'))) {
455
- const invalidName = containerName.includes('_') ? containerName.split('_')[1] : containerName;
456
- this.fileDiagnostics.push({
457
- file: sourceUri,
458
- level: 'error',
459
- message: `"${invalidName}" not allowed within a library`,
460
- ...yamlRangeToLineInformation(lineCounter, container.key.range)
461
- });
462
- }
463
- if (api_1.Manifest.isAnalytics(this.manifest) &&
464
- ![DDEContainerName.relations, DDEContainerName.storage].includes(containerName)) {
465
- const invalidName = containerName.includes('_') ? containerName.split('_')[1] : containerName;
466
- this.fileDiagnostics.push({
467
- file: sourceUri,
468
- level: 'error',
469
- message: `"${invalidName}" not allowed within an analytics app. Only "relations" and "storage" are allowed.`,
470
- ...yamlRangeToLineInformation(lineCounter, container.key.range)
471
- });
472
- }
473
- }
474
- validateContainerFields(yamlDoc, sourceUri, container, lineCounter) {
475
- const containerFields = container.value; //(container.value as YAMLMap).get('fields', true);
476
- if (!containerFields) {
477
- return;
478
- }
479
- const containerName = container.key.value;
480
- // todo validate fields across all container?
481
- if (!Object.hasOwn(this.fieldNamesPerContainer, containerName)) {
482
- this.fieldNamesPerContainer[containerName] = [];
483
- }
484
- const previousContainerDefinition = this.getContainerOfPreviousVersion(containerName);
485
- //@ts-ignore
486
- containerFields.items.forEach(containerFieldPair => {
487
- const containerFieldKey = containerFieldPair.key;
488
- const fieldName = containerFieldKey.value;
489
- this.checkForContainerRelation(containerFieldPair, yamlDoc, sourceUri, lineCounter);
490
- this.validateFieldName(containerFieldPair, containerName, sourceUri, lineCounter);
491
- this.checkFieldNameLength(containerFieldPair, containerName, sourceUri, lineCounter);
492
- const previousDefinition = previousContainerDefinition?.fields.find(field => field.id === fieldName.toLowerCase());
493
- const fieldItemValues = containerFieldPair.value;
494
- const typeField = fieldItemValues.get('type', true);
495
- const type = this.resolvePossibleAlias(yamlDoc, typeField);
496
- const bytesField = fieldItemValues.get('bytes', true);
497
- const bytes = this.resolvePossibleAlias(yamlDoc, bytesField);
498
- // validate if the type of this field has changed
499
- if (previousDefinition) {
500
- if (typeField && type && previousDefinition.type !== type) {
501
- this.fileDiagnostics.push({
502
- file: sourceUri,
503
- level: 'warning',
504
- message: `Field type changed from "${previousDefinition.type}" to "${type}".\nChanging the type of an existing field can result in data loss or unexpected behaviour.`,
505
- ...yamlRangeToLineInformation(lineCounter, typeField.range)
506
- });
507
- }
508
- if (bytesField && bytes && previousDefinition.byteSize !== bytes) {
509
- this.fileDiagnostics.push({
510
- file: sourceUri,
511
- level: 'warning',
512
- message: `Byte size changed from '${previousDefinition.byteSize}' to '${bytes}'.\nChanging the size of an existing field can result in data loss or unexpected behaviour.`,
513
- ...yamlRangeToLineInformation(lineCounter, bytesField.range)
514
- });
515
- }
516
- }
517
- });
518
- }
519
- checkForContainerRelation(containerFieldPair, yamlDoc, sourceUri, lineCounter) {
520
- // if it is a structured array, validate the subfields for any relation
521
- const isStructArray = containerFieldPair.value.get('fields');
522
- if (isStructArray) {
523
- isStructArray.items.forEach((arrayItem) => {
524
- this.checkForContainerRelation(arrayItem, yamlDoc, sourceUri, lineCounter);
525
- });
526
- }
527
- const allFieldItems = containerFieldPair.value.items;
528
- allFieldItems.forEach(fieldItem => {
529
- const itemValue = fieldItem.value;
530
- // check if the field value is a relation
531
- if (itemValue?.value && regexRelation.exec(`${itemValue.value}`)) {
532
- const itemType = fieldItem.key.value;
533
- // relations for the given property are not allowed.
534
- if (itemType) {
535
- if (!Object.hasOwn(allowedRelationTypes, itemType)) {
536
- return this.fileDiagnostics.push({
537
- file: sourceUri,
538
- level: 'error',
539
- message: `Property "${itemType}" does not allow relations.`,
540
- ...yamlRangeToLineInformation(lineCounter, itemValue.range)
541
- });
542
- }
543
- if (['scale', 'offset'].includes(itemType)) {
544
- return this.fileDiagnostics.push({
545
- file: sourceUri,
546
- level: 'warning',
547
- message: `Container relations for "${itemType}" are not available within the dlo source.`,
548
- ...yamlRangeToLineInformation(lineCounter, itemValue.range)
549
- });
550
- }
551
- }
552
- return this.validateContainerRelation(itemType, itemValue, yamlDoc, sourceUri, lineCounter);
553
- }
554
- });
555
- }
556
- validateFieldName(containerFieldPair, containerName, sourceUri, lineCounter) {
557
- const knownFieldNames = this.fieldNamesPerContainer[containerName];
558
- const containerFieldKey = containerFieldPair.key;
559
- const fieldName = containerFieldKey.value;
560
- if (checkForReservedFieldName(fieldName)) {
561
- this.fileDiagnostics.push({
562
- file: sourceUri,
563
- level: 'error',
564
- message: `Reserved property "${fieldName}" is not allowed.`,
565
- ...yamlRangeToLineInformation(lineCounter, containerFieldKey.range)
566
- });
567
- }
568
- const shadowFields = containerFieldPair.value.get('shadowFields');
569
- if (shadowFields) {
570
- // validate if all shadowFields are unique
571
- shadowFields.items.forEach((shadowField) => {
572
- this.validateFieldName(shadowField, containerName, sourceUri, lineCounter);
573
- });
574
- }
575
- // check if this fieldname is already in use
576
- if (knownFieldNames.includes(fieldName.toLowerCase())) {
577
- this.fileDiagnostics.push({
578
- file: sourceUri,
579
- level: 'error',
580
- message: `Field name "${fieldName}" is already occupied`,
581
- ...yamlRangeToLineInformation(lineCounter, containerFieldKey.range)
582
- });
583
- }
584
- else {
585
- knownFieldNames.push(fieldName.toLowerCase());
586
- }
587
- }
588
- checkFieldNameLength(containerFieldPair, containerName, sourceUri, lineCounter) {
589
- const containerFieldKey = containerFieldPair.key;
590
- const fieldName = containerFieldKey.value;
591
- const plainContainerName = containerName.startsWith('timeseries') ? containerName.substring(10) : containerName;
592
- const dloBaseName = `DDE_${plainContainerName}_`;
593
- const maxChars = 31 - dloBaseName.length;
594
- if (dloBaseName.length + fieldName.length > 31) {
595
- this.fileDiagnostics.push({
596
- file: sourceUri,
597
- level: 'error',
598
- code: diagnostics_1.Diagnostics.CODE.ddeFieldLength,
599
- message: `Field name "${fieldName}" to long. ${maxChars} characters allowed for this field/container.`,
600
- ...yamlRangeToLineInformation(lineCounter, containerFieldKey.range)
601
- });
602
- }
603
- }
604
- // eslint-disable-next-line sonarjs/cognitive-complexity
605
- validateContainerRelation(itemType, itemValue, yamlDoc, sourceUri, lineCounter) {
606
- //validate if the relation exist
607
- const [root, fieldRef, arrayField] = itemValue.value.split('.', 3);
608
- const [libraryRelation, containerName] = root.split('@');
609
- const libraryName = libraryRelation ? libraryRelation.split(':')[1] : null;
610
- const yamlContentToSearch = libraryName && this.libraryYaml[libraryName] ? this.libraryYaml[libraryName] : yamlDoc;
611
- const containerExist = yamlContentToSearch.contents?.get(containerName);
612
- const referencedField = fieldRef.split('[')[0];
613
- let relatedField = null;
614
- if (containerExist) {
615
- const fieldExist = containerExist.get(referencedField);
616
- if (fieldExist) {
617
- // check if an array field is referenced...
618
- if (arrayField) {
619
- const arrayItems = fieldExist.get('fields');
620
- if (arrayItems && arrayItems.get(arrayField)) {
621
- relatedField = arrayItems.get(arrayField);
622
- }
623
- }
624
- else {
625
- relatedField = fieldExist;
626
- }
627
- }
628
- else {
629
- // search for related field within the shadowfields
630
- relatedField = this.findRelatedShadowField(containerExist, referencedField);
631
- }
632
- }
633
- // validate if the target field type matches
634
- if (relatedField) {
635
- // @ts-ignore
636
- const allowedTypes = allowedRelationTypes[itemType];
637
- const relatedType = relatedField.get('type');
638
- if (!allowedTypes.includes(relatedType)) {
639
- this.fileDiagnostics.push({
640
- file: sourceUri,
641
- level: 'error',
642
- message: `Related field type "${relatedType}" is not allowed for attribute "${itemType}".`,
643
- ...yamlRangeToLineInformation(lineCounter, itemValue.range)
644
- });
645
- }
646
- }
647
- else if (libraryName && !this.libraryYaml[libraryName]) {
648
- this.fileDiagnostics.push({
649
- file: sourceUri,
650
- level: 'error',
651
- message: `Related library "${libraryName}" does not exist.`,
652
- ...yamlRangeToLineInformation(lineCounter, itemValue.range)
653
- });
654
- }
655
- else {
656
- this.fileDiagnostics.push({
657
- file: sourceUri,
658
- level: 'error',
659
- message: `Related field "${itemValue.value}" does not exist.`,
660
- ...yamlRangeToLineInformation(lineCounter, itemValue.range)
661
- });
662
- }
663
- }
664
- findRelatedShadowField(relatedContainer, referencedField) {
665
- for (const field of relatedContainer.items) {
666
- const fieldValue = field.value;
667
- const shadowFields = fieldValue.get('shadowFields');
668
- if (!shadowFields) {
669
- continue;
670
- }
671
- const refField = shadowFields.get(referencedField);
672
- if (refField) {
673
- return refField;
674
- }
675
- }
676
- return undefined;
677
- }
678
- mergeAndFormat(mainDDE) {
679
- const mergedDDE = {
680
- containers: {},
681
- setPointList: {}
682
- };
683
- for (const library of this.libraryDDE) {
684
- this.combineDDE(mergedDDE, library);
685
- }
686
- this.combineDDE(mergedDDE, mainDDE);
687
- this.sortContainerFields(mergedDDE);
688
- this.generateSetPointList(mergedDDE);
689
- return mergedDDE;
690
- }
691
- combineDDE(destination, source) {
692
- for (const [container, containerValues] of Object.entries(source.containers)) {
693
- let currentContainer = destination.containers[container];
694
- if (!currentContainer) {
695
- currentContainer = destination.containers[container] = structuredClone(containerValues);
696
- currentContainer.knownNames.unshift(containerValues.title);
697
- currentContainer.fieldCount = currentContainer.fields.length;
698
- currentContainer.fieldIndex = 0;
699
- }
700
- else {
701
- // the last title wins!
702
- currentContainer.title = containerValues.title;
703
- // keep all known names...
704
- currentContainer.knownNames.unshift(containerValues.title);
705
- currentContainer.libraries.push(...containerValues.libraries);
706
- currentContainer.fields.push(...containerValues.fields);
707
- currentContainer.arrays = { ...currentContainer.arrays, ...containerValues.arrays };
708
- currentContainer.shadows = { ...currentContainer.shadows, ...containerValues.shadows };
709
- }
710
- // first byte of a histdata container is the split tag.
711
- let byteOffset = this.initialContainerByteOffset(currentContainer);
712
- //calculate the byteOffset for each field:
713
- for (const [index, field] of currentContainer.fields.entries()) {
714
- // get the actual byteoffset of the parent shadow root
715
- if (field.shadowRef) {
716
- const shadowRef = currentContainer.shadows[field.shadowRef];
717
- field.byteOffset = shadowRef.byteOffset;
718
- }
719
- else {
720
- field.byteOffset = byteOffset;
721
- this.updateShadowFieldInformation(destination.containers[container], field);
722
- byteOffset += field.byteSize || 0;
723
- }
724
- if (index > currentContainer.fieldCount - 1) {
725
- field.index += currentContainer.fieldIndex;
726
- }
727
- }
728
- // update fieldIndex
729
- currentContainer.fieldIndex += containerValues.fieldIndex;
730
- // remove duplicates from
731
- currentContainer.knownNames = [...new Set(currentContainer.knownNames)];
732
- currentContainer.libraries = [...new Set(currentContainer.libraries)];
733
- currentContainer.fieldCount = currentContainer.fields.length;
734
- }
735
- }
736
- initialContainerByteOffset(container) {
737
- return container.containerType === ContainerType.hx ? 1 : 0;
738
- }
739
- updateShadowFieldInformation(container, field) {
740
- if (field && Object.hasOwn(container.shadows, field.name)) {
741
- container.shadows[field.name].byteOffset = field.byteOffset;
742
- }
743
- }
744
- sortContainerFields(mergedDDE) {
745
- for (const containerDefinition of Object.values(mergedDDE.containers)) {
746
- const { sortedFields, containerSize } = this.sortFields(containerDefinition);
747
- containerDefinition.fields = sortedFields;
748
- containerDefinition.size = containerSize;
749
- containerDefinition.fieldCount = containerDefinition.fields.length;
750
- }
751
- }
752
- sortFields(containerDefinition) {
753
- const containerFields = containerDefinition.fields.filter(field => !field.shadowRef);
754
- const shadowFields = containerDefinition.fields.filter(field => field.shadowRef);
755
- const sortedFields = [];
756
- const containerName = containerDefinition.name;
757
- const previousContainer = this.previousVersion.containers[containerName];
758
- if (previousContainer) {
759
- const previousFields = this.sortPreviousContainerFields(containerDefinition, containerFields);
760
- sortedFields.push(...previousFields);
761
- }
762
- else {
763
- sortedFields.push(...containerFields);
764
- }
765
- // update shadow field information
766
- sortedFields.forEach(field => {
767
- this.updateShadowFieldInformation(containerDefinition, field);
768
- });
769
- // get the last field of the container for calculating the container size
770
- const lastField = sortedFields[sortedFields.length - 1];
771
- const containerSize = lastField.byteSize + lastField.byteOffset;
772
- // insert all shadowfields at the end with the correct offset information
773
- shadowFields.forEach(field => {
774
- field.byteOffset = containerDefinition.shadows[field.shadowRef].byteOffset;
775
- sortedFields.push(field);
776
- });
777
- return { sortedFields, containerSize };
778
- }
779
- // eslint-disable-next-line sonarjs/cognitive-complexity
780
- sortPreviousContainerFields(containerDefinition, containerFields) {
781
- const containerName = containerDefinition.name;
782
- const previousContainer = this.previousVersion.containers[containerName];
783
- const sortedFields = [];
784
- let byteOffset = this.initialContainerByteOffset(containerDefinition);
785
- // iterate over the existing container
786
- for (const previousVersionField of previousContainer.fields) {
787
- if (previousVersionField.shadowRef) {
788
- continue;
789
- }
790
- const existingLineIndex = containerFields.findIndex(field => {
791
- return previousVersionField.id === field.name?.toLowerCase();
792
- });
793
- // if the previous existing line doesn't exist anymore
794
- // get the byte offset and pass it to the next line
795
- if (existingLineIndex === -1) {
796
- byteOffset = previousVersionField.byteOffset + previousVersionField.byteSize;
797
- continue;
798
- }
799
- const existingField = containerFields[existingLineIndex];
800
- // validate if the field type and length has changed
801
- if (previousVersionField.type !== existingField.type &&
802
- previousVersionField.byteSize !== existingField.byteSize) {
803
- // if the length changed, handle the field as a "new" field
804
- byteOffset = previousVersionField.byteOffset + previousVersionField.byteSize;
805
- continue;
806
- }
807
- // set the previous byte offset to prevent any field movement
808
- existingField.byteOffset = previousVersionField.byteOffset;
809
- //remove existing line from available list
810
- containerFields.splice(existingLineIndex, 1);
811
- sortedFields.push(existingField);
812
- byteOffset = this.initialContainerByteOffset(containerDefinition);
813
- }
814
- // only add a possible shadow field if the previous byteoffset is different than the initial one
815
- // histdata starts with 1 cfg with 0
816
- if (byteOffset > this.initialContainerByteOffset(containerDefinition)) {
817
- const nextField = containerFields.shift();
818
- sortedFields.push(...this.generatePlaceholderFieldIfNeeded(byteOffset, nextField));
819
- this.updateShadowFieldInformation(containerDefinition, nextField);
820
- }
821
- //insert new fields and adjust their byte offset:
822
- for (const newField of containerFields) {
823
- // only calculate the byteOffset for none shadow fields!
824
- if (newField.shadowRef) {
825
- newField.byteOffset = containerDefinition.shadows[newField.shadowRef].byteOffset;
826
- }
827
- else {
828
- this.updateShadowFieldInformation(containerDefinition, newField);
829
- let prevField = sortedFields[sortedFields.length - 1];
830
- // get the reference if it is a shadow field
831
- if (prevField.shadowRef) {
832
- prevField = containerDefinition.shadows[prevField.shadowRef];
833
- }
834
- newField.byteOffset = prevField.byteOffset + prevField.byteSize;
835
- }
836
- sortedFields.push(newField);
837
- }
838
- return sortedFields;
839
- }
840
- generatePlaceholderFieldIfNeeded(byteOffset, nextField) {
841
- const fieldList = [];
842
- // crate a shadow field if there isn't any known field left
843
- if (!nextField) {
844
- const shadowField = {
845
- byteOffset: byteOffset,
846
- byteSize: 0,
847
- id: '_shadow',
848
- library: null,
849
- name: '_shadow',
850
- type: 'shadow',
851
- arrayRef: null,
852
- shadowRef: null,
853
- title: null,
854
- index: 0
855
- };
856
- fieldList.push(shadowField);
857
- }
858
- else {
859
- // add the byteoffset to the next known field
860
- nextField.byteOffset = byteOffset;
861
- fieldList.push(nextField);
862
- }
863
- return fieldList;
864
- }
865
- generateSetPointList(mergedDDE) {
866
- // copy previous versions setpointlist
867
- mergedDDE.setPointList = { ...(this.previousVersion.setPointList || {}) };
868
- //find fields with SetPointAttribute:
869
- for (const containerDefinition of Object.values(mergedDDE.containers)) {
870
- for (const field of containerDefinition.fields) {
871
- if (!field.chartSetPoint) {
872
- continue;
873
- }
874
- //setpoint is already known -> skip
875
- if (Object.hasOwn(mergedDDE.setPointList, field.name)) {
876
- continue;
877
- }
878
- mergedDDE.setPointList[field.name] = this.getNextSetpointValue(mergedDDE.setPointList);
879
- }
880
- }
881
- }
882
- getNextSetpointValue(setPointList) {
883
- const knownValues = [...Object.values(setPointList), 0];
884
- return Math.max(...knownValues) + 1;
885
- }
886
- }
887
- YamlPreCompiler.PreCompiler = PreCompiler;
888
- })(YamlPreCompiler || (exports.YamlPreCompiler = YamlPreCompiler = {}));
889
- //# sourceMappingURL=yamlDdePreCompiler.js.map