@corvina/device-client
Version:
Corvina NodeJS Device Client
655 lines • 29.2 kB
JavaScript
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.DeviceService = void 0;
const types_1 = require("./../common/types");
const messagepublisherpolicies_1 = require("./messagepublisherpolicies");
const pem_1 = __importDefault(require("pem"));
const fs_1 = __importDefault(require("fs"));
const licensesaxiosinstance_1 = __importDefault(require("./licensesaxiosinstance"));
const mqtt_1 = __importDefault(require("mqtt"));
const bson_1 = __importDefault(require("bson"));
const lodash_1 = __importDefault(require("lodash"));
const simulation_1 = require("./simulation");
const types_2 = require("../common/types");
const zlib = __importStar(require("zlib"));
const assert = require("assert");
const corvinadatainterface_1 = __importDefault(require("./corvinadatainterface"));
const logger_service_1 = require("./logger.service");
const stream_1 = require("stream");
const https = __importStar(require("https"));
const mqtt_level_store_1 = __importDefault(require("mqtt-level-store"));
const x509 = require("x509.js");
const ONLY_TEST_CONNECTION = process.env["ONLY_TEST_CONNECTION"] === "true" || false;
class DeviceService extends stream_1.EventEmitter {
inited;
initPending;
readyToTransmit;
licenseData;
mqttClient;
msgSentStats = 0;
byteSentStats = 0;
lastDateStats = Date.now();
lastTriedBrokerEndpoint = 0;
empyCacheTopic;
introspectionTopic;
static baseIntrospection = "com.corvina.control.sub.Config:0:2;com.corvina.control.pub.Config:0:2;com.corvina.control.pub.DeviceAlarm:2:0;com.corvina.control.sub.DeviceAlarm:1:0";
customIntrospections;
applyConfigTopic;
consumerPropertiesTopic;
actionAlarmTopic;
configTopic;
availableTagsTopic;
lastConfig;
_deviceConfig;
axios;
dataInterface;
messageStore;
defaultQoS = process.env["MQTT_DEFAULT_QOS"] ? parseInt(process.env["MQTT_DEFAULT_QOS"]) : 0;
constructor() {
super();
if (process.env["MQTT_MSG_STORE_PATH"]) {
this.messageStore = new mqtt_level_store_1.default(process.env["MQTT_MSG_STORE_PATH"]);
}
this._deviceConfig = {};
this.dataInterface = new corvinadatainterface_1.default({
sendMessage: this.sendMessage.bind(this),
});
if (process.env["NODE_TLS_REJECT_UNAUTHORIZED"] !== "0") {
let currentCa = https.globalAgent?.options?.ca;
if (currentCa) {
currentCa = currentCa + "\n" + this.getCA();
}
else {
currentCa = this.getCA();
}
https.globalAgent.options.ca = currentCa;
}
}
get status() {
return {
msgSent: this.getMsgSent(),
bytesSent: this.getBytesSent(),
ready: this.isReady(),
connected: this.isConnected(),
inited: this.isInited(),
};
}
get deviceConfig() {
return this._deviceConfig;
}
getMsgSent() {
return this.msgSentStats;
}
getBytesSent() {
return this.byteSentStats;
}
getAppliedConfig() {
return this.lastConfig;
}
getDeviceConfig() {
return this._deviceConfig;
}
getLicenseData() {
return this.licenseData;
}
setCycleTime(cycleTime) {
this.dataInterface.setCycleTime(cycleTime);
}
reinit(deviceConfig, doInit = false) {
this.inited = false;
this.readyToTransmit = false;
if (this.mqttClient) {
logger_service_1.l.debug("Going to end mqtt client");
this.mqttClient.end(true);
this.mqttClient = null;
}
else {
logger_service_1.l.debug("No mqtt client to end");
}
simulation_1.DataSimulator.clear();
this.initPending = null;
this.licenseData = {};
this.customIntrospections = "";
this.lastConfig = "";
Object.assign(this._deviceConfig, deviceConfig);
this._deviceConfig.dynamicTags = new Map();
logger_service_1.l.info("Init with %j", this._deviceConfig);
this.axios = new licensesaxiosinstance_1.default(this._deviceConfig.pairingEndpoint, this._deviceConfig.activationKey);
this.init();
return this._deviceConfig;
}
isInited() {
return this.inited;
}
isReady() {
return this.readyToTransmit;
}
isConnected() {
return this.mqttClient && this.mqttClient.connected;
}
setReady(ready) {
if (this.readyToTransmit != ready) {
this.readyToTransmit = ready;
if (ready) {
this.emit("ready", ready);
}
else {
this.emit("not_ready", ready);
}
}
}
createCSR(logicalId) {
return new Promise((resolve, reject) => {
pem_1.default.createCSR({
organization: "System",
commonName: `${this.licenseData.logicalId}`,
}, (err, obj) => {
if (err == null) {
logger_service_1.l.debug("PEM RETURNED %s %s", err, obj);
resolve(obj);
}
else {
reject(err);
}
});
});
}
async applyConfig(config) {
if (JSON.stringify(config) == JSON.stringify(this.lastConfig)) {
logger_service_1.l.info("Found same config => return");
return;
}
if (this.initPending) {
await this.initPending;
}
this.setReady(false);
this.customIntrospections = "";
logger_service_1.l.debug("Apply config: %s", JSON.stringify(config));
this.dataInterface.applyConfig(config);
this.dataInterface.config.interfaceNames.forEach((interfaceName) => {
this.customIntrospections += `;${interfaceName}`;
});
logger_service_1.l.debug("Applied config done!");
this.lastConfig = config;
setTimeout(async () => {
logger_service_1.l.debug("Going to end mqtt client");
await this.mqttClient.end();
setTimeout(async () => await this.mqttClient.reconnect({
incomingStore: this.messageStore?.incoming,
outgoingStore: this.messageStore?.outgoing,
}), 1000);
}, 0);
}
serializeMessage(msg) {
if (this._deviceConfig.packetFormat == types_1.PacketFormatEnum.BSON) {
return bson_1.default.serialize({ v: msg.v, t: new Date(msg.t), m: msg.m });
}
else {
return JSON.stringify(msg);
}
}
getCA() {
if (process.env["BROKER_CA_FILE"]) {
try {
return fs_1.default.readFileSync(process.env["BROKER_CA_FILE"]);
}
catch (e) {
logger_service_1.l.error("Error reading CA file: %s", e);
return "";
}
}
return `-----BEGIN CERTIFICATE-----
MIICWTCCAf+gAwIBAgIUAkkMEwP0AejpBDLeXUiBRJSDv7UwCgYIKoZIzj0EAwIw
eTELMAkGA1UEBhMCSVQxDjAMBgNVBAgMBUl0YWx5MSgwJgYDVQQKDB9FeG9yIERl
dmljZXMgRGlnaXRhbCBJZGVudGl0aWVzMTAwLgYDVQQDDCdFeG9yIERldmljZXMg
RGlnaXRhbCBJZGVudGl0aWVzIFJvb3QgQ0EwIBcNMjAxMjEwMTAwOTQ5WhgPMjA2
MjAxMDQxMDA5NDlaMHkxCzAJBgNVBAYTAklUMQ4wDAYDVQQIDAVJdGFseTEoMCYG
A1UECgwfRXhvciBEZXZpY2VzIERpZ2l0YWwgSWRlbnRpdGllczEwMC4GA1UEAwwn
RXhvciBEZXZpY2VzIERpZ2l0YWwgSWRlbnRpdGllcyBSb290IENBMFkwEwYHKoZI
zj0CAQYIKoZIzj0DAQcDQgAEQGKIj1KpHpRk5ZOYvf9g33ENs2gOBu3RsCneaYKQ
Jhhl8wzVnt8vA4wzgv7B9Jui5+efYIk9N19jZ9H8JAjDZKNjMGEwHQYDVR0OBBYE
FO3l09dQYmSZ5+VuR8IDyNDSrP8cMB8GA1UdIwQYMBaAFO3l09dQYmSZ5+VuR8ID
yNDSrP8cMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMAoGCCqGSM49
BAMCA0gAMEUCIEBfvBPKnQSGQhk/JLvtdsC9AUhzmpnmXKqztImkkkfJAiEAqEOc
fLibdXgfUjlbFwApfXoXZsYZMwyFq/HjIKS1pyA=
-----END CERTIFICATE-----`;
}
connectClient(broker_url, key, crt) {
logger_service_1.l.info("Connecting to mqtt broker %s", broker_url);
return new Promise(async (resolve, reject) => {
const mqttClientOptions = {};
mqttClientOptions.rejectUnauthorized = process.env["NODE_TLS_REJECT_UNAUTHORIZED"] === "0" ? false : true;
mqttClientOptions.ca = this.getCA();
mqttClientOptions.key = key;
mqttClientOptions.cert = crt;
mqttClientOptions.clean = true;
mqttClientOptions.clientId = x509.parseCert(crt).subject.commonName;
mqttClientOptions.reconnectPeriod = 10000;
logger_service_1.l.debug(mqttClientOptions, "MQTT options");
mqttClientOptions.incomingStore = this.messageStore?.incoming;
mqttClientOptions.outgoingStore = this.messageStore?.outgoing;
this.mqttClient = mqtt_1.default.connect(broker_url, mqttClientOptions);
logger_service_1.l.debug("MQTT client created");
this.mqttClient.on("connect", async (v) => {
logger_service_1.l.info(`Successfully connected to mqtt broker! ${JSON.stringify(v)}`);
this.subscribeChannel(this.consumerPropertiesTopic);
this.subscribeChannel(this.applyConfigTopic);
this.subscribeChannel(this.actionAlarmTopic);
if (ONLY_TEST_CONNECTION) {
logger_service_1.l.info("Connection test successful!");
process.exit(0);
}
logger_service_1.l.debug("Published introspection " + DeviceService.baseIntrospection + this.customIntrospections);
await this.sendStringMessage(this.introspectionTopic, DeviceService.baseIntrospection + this.customIntrospections, { qos: 2 });
logger_service_1.l.debug("Published empty cache");
await this.sendStringMessage(this.empyCacheTopic, "1", {
qos: 2,
});
logger_service_1.l.debug("Published configuration");
await this.sendStringMessage(this.configTopic, this.serializeMessage({
v: JSON.stringify(this.lastConfig),
t: Date.now(),
}), { qos: 2 });
this.throttledUpdateAvailableTags();
if (this.dataInterface.config) {
this.dataInterface.config.subscribedTopics.forEach((topic, topicName) => {
this.subscribeChannel(this.licenseData.realm + "/" + this.licenseData.logicalId + topicName);
});
}
this.setReady(true);
logger_service_1.l.info("Ready to transmit!");
simulation_1.DataSimulator.clear();
if (this._deviceConfig.simulateTags) {
this._deviceConfig.availableTags.forEach((value) => {
if (value.simulation === null) {
return;
}
new simulation_1.DataSimulator(value.name, value.type, async (t, v, ts) => {
if (this.isReady()) {
return this._internalPost([{ tagName: t, value: v, timestamp: ts }], true);
}
return false;
}, value.simulation);
});
if (this._deviceConfig.simulateAlarms) {
this._deviceConfig.availableAlarms.forEach((value) => {
new simulation_1.AlarmSimulator(value, async (data) => {
if (this.isReady()) {
return this.postAlarm(data);
}
return false;
});
});
}
}
resolve(true);
});
this.mqttClient.on("close", () => {
simulation_1.DataSimulator.clear();
logger_service_1.l.warn("Stream closed!");
});
this.mqttClient.on("reconnect", () => {
simulation_1.DataSimulator.clear();
logger_service_1.l.warn("Stream reconnected!");
});
this.mqttClient.on("error", (error) => {
logger_service_1.l.error(error, "Stream error!");
this.lastTriedBrokerEndpoint++;
reject(error);
});
this.mqttClient.on("message", async (topic, message) => {
logger_service_1.l.info(`Received message on ${topic}\n`);
let decodedMsg;
switch (topic) {
case this.consumerPropertiesTopic.toString():
decodedMsg = zlib.unzipSync(message.slice(4)).toString();
logger_service_1.l.debug("Received consumer properties!");
logger_service_1.l.trace(`<<<< %s %j %d %j`, topic, decodedMsg, message.length, message);
break;
case this.applyConfigTopic.toString():
decodedMsg = bson_1.default.deserialize(message);
logger_service_1.l.trace(`<<<< %s %j %d %j`, topic, decodedMsg, message.length, message);
this.applyConfig(JSON.parse(decodedMsg.v));
break;
case this.actionAlarmTopic.toString():
decodedMsg = bson_1.default.deserialize(message);
logger_service_1.l.trace(`<<<< %s %j %d %j`, topic, decodedMsg, message.length, message);
const x = decodedMsg.v;
const sim = simulation_1.BaseSimulator.simulatorsByTagName.get(simulation_1.AlarmSimulator.alarmSimulatorMapkey(x.name));
if (!sim) {
logger_service_1.l.error("Trying to perform action on unknown alarm %s", x.name);
}
else {
switch (x.command) {
case "ack":
sim.acknowledge(x.evTs, x.user, x.comment);
break;
case "reset":
sim.reset(x.evTs, x.user, x.comment);
break;
}
}
break;
default:
decodedMsg = bson_1.default.deserialize(message);
logger_service_1.l.trace(`<<<< %s %j %d %j`, topic, decodedMsg, message.length, message);
const topicKey = topic.slice(this.licenseData.logicalId.length + this.licenseData.realm.length + 1);
const subscriber = this.dataInterface.config.subscribedTopics.get(topicKey);
if (subscriber) {
this.onWrite(subscriber, decodedMsg);
if (this._deviceConfig.simulateTags) {
this.applyBackToSimulation(subscriber.targetTag, decodedMsg.v);
}
}
else {
logger_service_1.l.info(`Nothing to do for topic ${topic}`);
}
}
});
});
}
subscribeChannel(channel) {
return new Promise((resolve, reject) => {
this.mqttClient.subscribe(channel, function (err) {
if (!err) {
resolve(true);
}
else {
logger_service_1.l.warn(err, `Error subscribing %s`, channel);
reject(err);
}
});
});
}
async sendStringMessage(channel, message, options = {}) {
logger_service_1.l.debug("Going to publish %s", channel);
return new Promise((resolve, reject) => {
this.mqttClient.publish(channel, message, options, (err) => {
if (!err) {
resolve(true);
}
else {
logger_service_1.l.warn(`Error publishing to ${channel}: %j`, err);
reject(err);
}
});
});
}
async sendMessage(topic, payload, options) {
topic = this.licenseData.realm + "/" + this.licenseData.logicalId + topic;
const message = this.serializeMessage(payload);
this.byteSentStats += message.length;
this.msgSentStats += 1;
const timeDiff = Date.now() - this.lastDateStats;
if (timeDiff > 10000) {
this.byteSentStats = 0;
this.msgSentStats = 0;
this.lastDateStats = this.lastDateStats + timeDiff;
}
logger_service_1.l.debug("Going to send to topic %s", topic);
try {
if (!this.readyToTransmit) {
const err = `Cannot publish if not ready to transmit`;
logger_service_1.l.warn(err);
if (options?.cb) {
options.cb(new Error(err), undefined);
}
throw "Cannot publish if not ready to transmit";
}
options = options || { qos: this.defaultQoS };
if (!options.qos) {
options.qos = this.defaultQoS;
}
logger_service_1.l.trace(">>>> %s %j %d %j QoS %d", topic, payload, message.length, message, options.qos);
if (options?.cb) {
await this.mqttClient.publish(topic, message, options, (err, packet) => {
options.cb(err, packet);
});
}
else {
await this.mqttClient.publish(topic, message, options);
}
}
catch (e) {
logger_service_1.l.error("Got error while publishing: ");
logger_service_1.l.error(e);
return false;
}
}
async _asyncInit() {
try {
this.licenseData = await this.axios.init();
logger_service_1.l.debug("Got api key %j ", this.licenseData);
this.inited = true;
const csr = await this.createCSR(this.licenseData.logicalId);
logger_service_1.l.info({ msg: "CSR created", csr });
const crt = await this.axios.doPairing(csr.csr);
logger_service_1.l.info({ msg: "Certificate signed", crt });
assert(await this.axios.verify(crt.client_crt));
this.empyCacheTopic = `${this.licenseData.realm}/${this.licenseData.logicalId}/control/emptyCache`;
this.introspectionTopic = `${this.licenseData.realm}/${this.licenseData.logicalId}`;
this.consumerPropertiesTopic = `${this.licenseData.realm}/${this.licenseData.logicalId}/control/consumer/properties`;
this.applyConfigTopic = `${this.licenseData.realm}/${this.licenseData.logicalId}/com.corvina.control.sub.Config/applyConfiguration`;
this.actionAlarmTopic = `${this.licenseData.realm}/${this.licenseData.logicalId}/com.corvina.control.sub.DeviceAlarm/a`;
this.configTopic = `${this.licenseData.realm}/${this.licenseData.logicalId}/com.corvina.control.pub.Config/configuration`;
this.availableTagsTopic = `${this.licenseData.realm}/${this.licenseData.logicalId}/com.corvina.control.pub.Config/availableTags`;
await this.connectClient(this.licenseData.brokerUrls[this.lastTriedBrokerEndpoint % this.licenseData.brokerUrls.length], csr.clientKey, crt.client_crt);
}
catch (err) {
this.inited = false;
throw err;
}
return this.inited;
}
async init() {
if (this.inited == false && this.initPending == null) {
this.initPending = this._asyncInit();
try {
await this.initPending;
}
catch (err) {
logger_service_1.l.error("Error initing:");
logger_service_1.l.error(err);
this.initPending = null;
const randomRetry = 5 + 10 * Math.random();
logger_service_1.l.warn(`Retry init in ${randomRetry} secs`);
if (this.mqttClient) {
logger_service_1.l.debug("Going to end mqtt client");
this.mqttClient.end(true);
this.mqttClient = null;
}
else {
logger_service_1.l.debug("No mqtt client to end");
}
setTimeout(() => {
this.init();
}, randomRetry * 1000);
}
this.initPending = null;
}
return this.inited;
}
throttledUpdateAvailableTags = lodash_1.default.throttle(async () => {
try {
await this.sendStringMessage(this.availableTagsTopic, this.serializeMessage({
v: JSON.stringify([
...this._deviceConfig.availableTags.values(),
...(this._deviceConfig.dynamicTags ? this._deviceConfig.dynamicTags.values() : []),
]),
t: Date.now(),
}), { qos: 2 });
}
catch (e) { }
}, 1000, { leading: false, trailing: true });
jsToCorvinaType(value) {
switch (typeof value) {
case "number":
return "double";
case "string":
return "string";
case "object":
if (lodash_1.default.isArray(value)) {
if (value.length > 0 && typeof value[0] === "string") {
return "stringarray";
}
return "doublearray";
}
else {
return "struct";
}
break;
default:
return undefined;
}
}
applyBackToSimulation(tagName, value) {
const sim = simulation_1.BaseSimulator.simulatorsByTagName?.get(tagName);
if (sim) {
sim.value = value;
sim.lastSentValue = value;
if (sim.desc?.type == types_2.SimulationType.CONST) {
sim.desc.value = value;
}
}
else {
if (this._deviceConfig.simulateTags) {
const sim = new simulation_1.DataSimulator(tagName, this.jsToCorvinaType(value), async (t, v, ts) => {
if (this.isReady()) {
return this._internalPost([{ tagName: t, value: v, timestamp: ts }], true);
}
return false;
}, { type: types_2.SimulationType.CONST, value: value });
logger_service_1.l.info({ sim }, "Inited new const simulator");
}
}
}
recurseNotifyObject = (prefix, rootValue, ts, calledFromSimulation, options) => {
lodash_1.default.mapKeys(rootValue, (value, key) => {
const decoratedName = `${prefix}${key}`;
if (lodash_1.default.isArray(value) && value.length > 0 && !lodash_1.default.isObject(value[0])) {
for (const e in value) {
this.recurseNotifyObject(`${decoratedName}[${e}]`, value[e], ts, calledFromSimulation, options);
}
}
else if (lodash_1.default.isObject(value)) {
this.recurseNotifyObject(decoratedName + ".", value, ts, calledFromSimulation, options);
}
if (this._deviceConfig.dynamicTags &&
!this._deviceConfig.dynamicTags.has(decoratedName) &&
!this._deviceConfig.availableTags.has(decoratedName) &&
value != undefined) {
this._deviceConfig.dynamicTags.set(decoratedName, {
name: decoratedName,
type: this.jsToCorvinaType(value),
});
this.throttledUpdateAvailableTags();
}
if (this.dataInterface.config) {
if (!calledFromSimulation) {
this.applyBackToSimulation(decoratedName, value);
}
this.dataInterface.notifyTag(decoratedName, new messagepublisherpolicies_1.State(value, ts), options);
}
});
if (this.dataInterface.config && prefix.length > 0) {
this.dataInterface.notifyTag(prefix.endsWith(".") ? prefix.slice(0, -1) : prefix, new messagepublisherpolicies_1.State(rootValue, ts), options);
}
};
async post(dataPoints, options) {
return this._internalPost(dataPoints, false, options);
}
async _internalPost(dataPoints, calledFromSimulation, options) {
if (!this.readyToTransmit) {
const err = `Cannot process ${JSON.stringify(dataPoints)}. Device not ready to transmit!`;
if (options?.cb) {
options.cb(new Error(err), undefined, undefined);
}
logger_service_1.l.info(err);
return false;
}
for (const dp of dataPoints) {
if (dp.tagName == undefined) {
assert(lodash_1.default.isObject(dp.value) && !lodash_1.default.isArray(dp.value));
this.recurseNotifyObject("", dp.value, dp.timestamp, calledFromSimulation, options);
}
else {
if (lodash_1.default.isObject(dp.value) && !options?.recurseNotifyOnlyWholeObject && !lodash_1.default.isArray(dp.value)) {
this.recurseNotifyObject(dp.tagName + ".", dp.value, dp.timestamp, calledFromSimulation, options);
}
else {
if (this.dataInterface.config) {
if (!calledFromSimulation && this._deviceConfig.simulateTags) {
this.applyBackToSimulation(dp.tagName, dp.value);
}
this.dataInterface.notifyTag(dp.tagName, new messagepublisherpolicies_1.State(dp.value, dp.timestamp), options);
}
}
}
}
if (!this.dataInterface.config) {
const err = `Cannot process ${JSON.stringify(dataPoints)}. Device is not configured yet!`;
logger_service_1.l.info(err);
if (options?.cb) {
options.cb(new Error(err), undefined, undefined);
}
return false;
}
return true;
}
async postAlarm(alarmData) {
const payload = this.serializeMessage({ t: Date.now(), v: alarmData });
const topic = `${this.licenseData.realm}/${this.licenseData.logicalId}/com.corvina.control.pub.DeviceAlarm/a`;
logger_service_1.l.debug("Going to send alarm ");
logger_service_1.l.trace(">>>> %s %j %d %j", topic, { t: Date.now(), v: alarmData }, payload.length, payload);
await this.mqttClient.publish(topic, payload, { qos: 2 });
return true;
}
onWrite(subscriber, message) {
logger_service_1.l.debug("CorvinaDataInterface.onWrite %j", message);
this.emit("write", {
topic: subscriber.topic,
modelPath: subscriber.modelPath,
fieldName: subscriber.fieldName,
tagName: subscriber.targetTag,
v: (0, types_1.castCorvinaType)(message.v, subscriber.topicType),
});
}
}
exports.DeviceService = DeviceService;
//# sourceMappingURL=device.service.js.map