UNPKG

@corvina/device-client

Version:
465 lines 20.2 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.indexTemplateApply = indexTemplateApply; exports.default = parseDeviceConfig; const logger_service_1 = require("../services/logger.service"); const messagepublisher_1 = require("./messagepublisher"); const messagesubscriber_1 = require("./messagesubscriber"); const messagepublisherpolicies_1 = require("./messagepublisherpolicies"); class InvalidConfigurationError extends Error { } function parseConditions(condition) { switch (condition.type) { case "and": { const andConditions = condition.operands.map(parseConditions); return new messagepublisherpolicies_1.MessagePublisher_AndPolicy(andConditions); } break; case "or": { const orConditions = condition.operands.map(parseConditions); return new messagepublisherpolicies_1.MessagePublisher_OrPolicy(orConditions); } break; case "inband": case "outband": { const inbandCondition = condition; return new messagepublisherpolicies_1.MessagePublisher_AnalogBandPolicy({ tagName: inbandCondition.tagName, min: inbandCondition.low, max: inbandCondition.high, inside: inbandCondition.type === "inband", }); } break; } } function parsePolicy(policyData, namedPolicies) { if (policyData.type !== "send") { return; } if (policyData.instanceOf) { const namedPolicy = namedPolicies.get(policyData.instanceOf); if (!namedPolicy) { throw new InvalidConfigurationError(`Could not find referred policy ${policyData.instanceOf}`); } return namedPolicy.clone(); } let result = undefined; const triggerOperands = []; for (const trigger of policyData.triggers) { switch (trigger.type) { case "timer": { const timerTrigger = trigger; triggerOperands.push(new messagepublisherpolicies_1.MessagePublisher_TimerPolicy(timerTrigger.intervalMs)); } break; case "onchange": { const onChangeTrigger = trigger; const op1 = new messagepublisherpolicies_1.MessagePublisher_OnChangedPolicy({ tagName: onChangeTrigger.tagName, skipFirstNChanges: onChangeTrigger.skipFirstNChanges, deadband: onChangeTrigger.deadband != undefined ? onChangeTrigger.deadband : onChangeTrigger.deadbandPercent, isPercent: onChangeTrigger.deadbandPercent != undefined, }); if (onChangeTrigger.minIntervalMs != undefined) { const op2 = new messagepublisherpolicies_1.MessagePublisher_TimerPolicy(onChangeTrigger.minIntervalMs); triggerOperands.push(new messagepublisherpolicies_1.MessagePublisher_AndPolicy([op1, op2])); } else { triggerOperands.push(op1); } } break; case "fieldchange": { const onFieldChangeTrigger = trigger; const op1 = new messagepublisherpolicies_1.MessagePublisher_OnFieldChangedPolicy({ fieldName: onFieldChangeTrigger.fieldName, skipFirstNChanges: onFieldChangeTrigger.skipFirstNChanges, deadband: onFieldChangeTrigger.deadband != undefined ? onFieldChangeTrigger.deadband : onFieldChangeTrigger.deadbandPercent, isPercent: onFieldChangeTrigger.deadbandPercent != undefined, }); if (onFieldChangeTrigger.minIntervalMs != undefined) { const op2 = new messagepublisherpolicies_1.MessagePublisher_TimerPolicy(onFieldChangeTrigger.minIntervalMs); triggerOperands.push(new messagepublisherpolicies_1.MessagePublisher_AndPolicy([op1, op2])); } else { triggerOperands.push(op1); } } break; case "onlevel": { const onLevelTrigger = trigger; const op = new messagepublisherpolicies_1.MessagePublisher_OnLevelPolicy({ tagName: onLevelTrigger.tagName, skipFirstNChanges: onLevelTrigger.skipFirstNChanges, level: onLevelTrigger.levelString != undefined ? onLevelTrigger.levelString : onLevelTrigger.level, levelMode: onLevelTrigger.mode, deadband: onLevelTrigger.deadband != undefined ? onLevelTrigger.deadband : onLevelTrigger.deadbandPercent, isPercent: onLevelTrigger.deadbandPercent != undefined, }); if (onLevelTrigger.minIntervalMs != undefined) { const op2 = new messagepublisherpolicies_1.MessagePublisher_TimerPolicy(onLevelTrigger.minIntervalMs); triggerOperands.push(new messagepublisherpolicies_1.MessagePublisher_AndPolicy([op, op2])); } else { triggerOperands.push(op); } } break; case "fieldLevel": const onFieldLevelTrigger = trigger; const op = new messagepublisherpolicies_1.MessagePublisher_OnFieldLevelPolicy({ fieldName: onFieldLevelTrigger.fieldName, skipFirstNChanges: onFieldLevelTrigger.skipFirstNChanges, level: onFieldLevelTrigger.levelString != undefined ? onFieldLevelTrigger.levelString : onFieldLevelTrigger.level, levelMode: onFieldLevelTrigger.mode, deadband: onFieldLevelTrigger.deadband != undefined ? onFieldLevelTrigger.deadband : onFieldLevelTrigger.deadbandPercent, isPercent: onFieldLevelTrigger.deadbandPercent != undefined, }); if (onFieldLevelTrigger.minIntervalMs != undefined) { const op2 = new messagepublisherpolicies_1.MessagePublisher_TimerPolicy(onFieldLevelTrigger.minIntervalMs); triggerOperands.push(new messagepublisherpolicies_1.MessagePublisher_AndPolicy([op, op2])); } else { triggerOperands.push(op); } break; } } if (triggerOperands.length > 1) { result = new messagepublisherpolicies_1.MessagePublisher_OrPolicy(triggerOperands); } else if (triggerOperands.length === 1) { result = triggerOperands[0].clone(); } if (policyData.conditions) { const conditions = parseConditions(policyData.conditions); if (result == undefined) { result = conditions; } else { result = new messagepublisherpolicies_1.MessagePublisher_AndPolicy([result, conditions]); } } return result; } function indexTemplateApply(templateString, values) { let output = ""; let content = ""; let index = -1; const ok = true; let State; (function (State) { State[State["STRING"] = 0] = "STRING"; State[State["ESCAPING_STRING"] = 1] = "ESCAPING_STRING"; State[State["ESCAPING_CONTENT"] = 2] = "ESCAPING_CONTENT"; State[State["DOLLAR"] = 3] = "DOLLAR"; State[State["CONTENT"] = 4] = "CONTENT"; })(State || (State = {})); let state; state = State.STRING; for (let i = 0; i < templateString.length; i++) { if (state == State.ESCAPING_STRING) { output += templateString[i]; state = State.STRING; } else if (state == State.ESCAPING_CONTENT) { output += templateString.at(i); state = State.CONTENT; } else { switch (templateString[i]) { case "\\": state = state == State.CONTENT ? State.ESCAPING_CONTENT : State.ESCAPING_STRING; break; case "$": switch (state) { case State.STRING: state = State.DOLLAR; break; case State.DOLLAR: output += "$"; state = State.DOLLAR; break; case State.CONTENT: content += "$"; break; } break; case "{": switch (state) { case State.DOLLAR: state = State.CONTENT; break; case State.CONTENT: content += "{"; break; case State.STRING: output += "{"; break; } break; case "}": switch (state) { case State.DOLLAR: state = State.STRING; output += "$}"; break; case State.CONTENT: state = State.STRING; index = parseInt(content); if (index >= 0 && index < values.length) { output += values[index]; } content = ""; break; case State.STRING: output += "}"; break; } break; default: switch (state) { case State.CONTENT: content += templateString[i]; break; case State.DOLLAR: output += "$"; output += templateString[i]; break; case State.STRING: output += templateString[i]; break; } break; } } } return output; } function initPublisher(publisher, sourceTag, sendPolicy, deviceConfig) { logger_service_1.l.debug("Init publisher %s %o", sourceTag, sendPolicy); const policy = parsePolicy({ type: "send", ...sendPolicy }, deviceConfig.namedPolicies); if (!policy) { return; } policy.setDefaultTagName(sourceTag); publisher.setPolicy(policy); const referencedTags = policy.referencedTags(); for (const i in referencedTags) { const tag = referencedTags[i]; if (tag != sourceTag) { let pubs = deviceConfig.tagPublishers.get(tag); pubs = pubs == undefined ? new Set() : pubs; pubs.add(publisher); deviceConfig.tagPublishers.set(tag, pubs); } } if (sourceTag != undefined) { let pubs = deviceConfig.tagPublishers.get(sourceTag); pubs = pubs == undefined ? new Set() : pubs; pubs.add(publisher); deviceConfig.tagPublishers.set(sourceTag, pubs); } } function parseDeviceConfigurationNode({ node, deviceConfig, nodeName, nodePath, parentNode, arrayIndexes, parentPublisher, }) { const sourceTag = node.datalink && node.datalink.source ? indexTemplateApply(node.datalink.source, arrayIndexes) : undefined; const device_endpoint = node.mapping && node.mapping.device_endpoint ? indexTemplateApply(node.mapping.device_endpoint, arrayIndexes) : undefined; const server_endpoint = node.mapping && node.mapping.server_endpoint ? indexTemplateApply(node.mapping.server_endpoint, arrayIndexes) : undefined; switch (node.type) { case "object": for (const p of Object.keys(node.properties)) { const childNode = node.properties[p]; parseDeviceConfigurationNode({ node: childNode, deviceConfig, nodeName: p, nodePath: nodePath + "/" + p, parentNode: node, arrayIndexes, parentPublisher: null, }); } break; case "struct": let publisher = undefined; if (node.sendPolicy) { publisher = new messagepublisher_1.AggregatedMessagePublisher({ sourceTag, modelPath: nodePath, topic: device_endpoint, }); initPublisher(publisher, sourceTag, node.sendPolicy, deviceConfig); } else { publisher = new messagepublisher_1.AggregatedMessagePublisher({ sourceTag: undefined, modelPath: nodePath, topic: undefined, }); } if (server_endpoint != undefined) { deviceConfig.subscribedTopics.set(server_endpoint, new messagesubscriber_1.MessageSubscriber({ topic: server_endpoint, modelPath: nodePath, topicType: node.type, targetTag: node?.datalink?.source })); } for (const p of Object.keys(node.properties)) { const childNode = node.properties[p]; parseDeviceConfigurationNode({ node: childNode, deviceConfig, nodeName: p, nodePath: nodePath + "/" + p, parentNode: node, arrayIndexes, parentPublisher: publisher, }); } break; case "array": const len = node.length; for (let i = 0; i < len; i++) { const name = `${nodeName}[${i}]`; parseDeviceConfigurationNode({ node: node.item, deviceConfig, nodeName: name, nodePath: nodePath + "/" + name, parentNode: node, arrayIndexes: [...arrayIndexes, i], parentPublisher: null, }); } break; default: if (parentNode.type == "object") { if (node.sendPolicy) { if (!device_endpoint) { logger_service_1.l.warn(`Cannot init publish for datalink ${node?.datalink?.source}: no device endpoint set`); } else { const publisher = new messagepublisher_1.MessagePublisher({ sourceTag, modelPath: nodePath, topic: device_endpoint, topicType: node.type, }); initPublisher(publisher, sourceTag, node.sendPolicy, deviceConfig); } } if (server_endpoint != undefined) { deviceConfig.subscribedTopics.set(server_endpoint, new messagesubscriber_1.MessageSubscriber({ topic: server_endpoint, modelPath: nodePath, topicType: node.type, targetTag: node?.datalink?.source })); } } else if (parentNode.type == "struct") { if (node.sendPolicy) { throw new InvalidConfigurationError(`Cannot specify send policy for struct properties`); } if (parentPublisher.tagName && sourceTag && sourceTag != parentPublisher.tagName) { throw new InvalidConfigurationError(`Cannot mix field datalinks with whole structure datalinks`); } if (parentPublisher.tagName == undefined) { const pub = parentPublisher; pub.addField({ tagName: sourceTag, fieldName: nodeName, type: node.type, }); if (sourceTag != undefined) { let pubs = deviceConfig.tagPublishers.get(sourceTag); pubs = pubs == undefined ? new Set() : pubs; pubs.add(parentPublisher); deviceConfig.tagPublishers.set(sourceTag, pubs); } } else { } if (server_endpoint != undefined) { deviceConfig.subscribedTopics.set(server_endpoint, new messagesubscriber_1.MessageSubscriber({ topic: server_endpoint, modelPath: nodePath, topicType: node.type, fieldName: nodeName, targetTag: node?.datalink?.source })); } } break; } } function simplifyInstanceOf(fullInstanceOf) { return fullInstanceOf.slice(0, fullInstanceOf.indexOf(".")); } function parseDeviceConfig(config) { const result = { interfaceNames: [], tagPublishers: new Map(), namedPolicies: new Map(), subscribedTopics: new Map(), }; if (!config.type || config.type !== "datamodel") { throw new InvalidConfigurationError(`Invalid type "${config.type}"`); } if (!config.properties || Object.keys(config.properties).length <= 0) { throw new InvalidConfigurationError(`Cannot find any configuration data`); } const configData = config.properties[Object.keys(config.properties)[0]]; logger_service_1.l.debug(`Parsing configuration data ${Object.keys(config.properties)[0]}`); if (!configData.interfaces) { throw new InvalidConfigurationError(`Missing interfaces node`); } result.interfaceNames = configData.interfaces.map((i) => `${i.interface_name}:${i.version_major}:${i.version_minor}`); logger_service_1.l.info(`Found interfaces ${result.interfaceNames}`); if (configData.policies) { for (const p of Object.keys(configData.policies)) { logger_service_1.l.info(`Parsing policy ${p}`); const policyData = configData.policies[p]; const policy = parsePolicy(policyData, result.namedPolicies); result.namedPolicies.set(p, policy); } } for (const prop of Object.keys(configData.properties)) { parseDeviceConfigurationNode({ node: configData.properties[prop], deviceConfig: result, nodeName: prop, nodePath: simplifyInstanceOf(configData.instanceOf) + "/" + prop, parentNode: configData, arrayIndexes: [], parentPublisher: null, }); } return result; } //# sourceMappingURL=configparser.js.map