@corvina/device-client
Version:
Corvina NodeJS Device Client
466 lines • 20.2 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.indexTemplateApply = void 0;
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;
}
exports.indexTemplateApply = indexTemplateApply;
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;
}
exports.default = parseDeviceConfig;
//# sourceMappingURL=configparser.js.map