@iotize/tap
Version:
IoTize Device client for Javascript
1,379 lines (1,365 loc) • 63.9 kB
JavaScript
import { BaseError, CodeError } from '@iotize/common/error';
import { VariableType } from '@iotize/tap/service/impl/variable';
import { hexStringToBuffer, bufferToHexString, asciiStringToByteBuffer } from '@iotize/common/byte-converter';
import { KaitaiStreamWriter } from '@iotize/common/byte-stream';
import { DotAccessor } from '@iotize/common/data-accessor';
import { HostProtocol, SpecialFeature } from '@iotize/tap';
import { ANONYMOUS_USER, isSpecialUser, ADMIN_USER } from '@iotize/tap/configurator';
import { TapnpassProtocolConfigurationConverter, SlaveServerRawConfig, SecurityOptionsConverter, TargetCanProtocolConfiguration, VariableType as VariableType$1, tapnpassConverters, SerialSettings } from '@iotize/tap/service/all';
import { converters } from '@iotize/tap/service/core';
import { parse } from 'fast-xml-parser';
class InvalidConfigurationError extends BaseError {
constructor(msg) {
super(msg);
}
}
var ByteOrder32Bits;
(function (ByteOrder32Bits) {
ByteOrder32Bits[ByteOrder32Bits["B3_B2_B1_B0"] = 0] = "B3_B2_B1_B0";
ByteOrder32Bits[ByteOrder32Bits["B1_B0_B3_B2"] = 1] = "B1_B0_B3_B2";
ByteOrder32Bits[ByteOrder32Bits["B2_B3_B0_B1"] = 2] = "B2_B3_B0_B1";
ByteOrder32Bits[ByteOrder32Bits["B0_B1_B2_B3"] = 3] = "B0_B1_B2_B3";
})(ByteOrder32Bits || (ByteOrder32Bits = {}));
var ByteOrder16Bits;
(function (ByteOrder16Bits) {
ByteOrder16Bits[ByteOrder16Bits["B1_B0"] = 0] = "B1_B0";
ByteOrder16Bits[ByteOrder16Bits["B0_B1"] = 1] = "B0_B1";
})(ByteOrder16Bits || (ByteOrder16Bits = {}));
function toEnumFromValue(mapping, value) {
let values = Object.keys(mapping).map((key) => mapping[key]);
if (values.find((v) => v.toString() === value) === undefined) {
throw new Error(`Missing value "${value}" in enum. Available keys are ${values.join(', ')}`);
}
if (value in mapping) {
// For string to number convertion
return mapping[mapping[value]];
}
return value;
}
function toEnumType(value, mapping) {
if (!(value in mapping)) {
throw new Error(`Missing mapping for ${value}. Available: ${Object.keys(mapping).join(', ')}`);
}
return mapping[value];
}
// export function toEnumFromKey<T>(mapping: any, key: string): T {
// if (!(key in mapping)) {
// throw new Error(`Missing key "${key}" in enum. Available keys are ${Object.keys(mapping).join(', ')}`);
// }
// return mapping[mapping[key]];
// }
class IotizeVariableModel {
constructor(data, iotizeModule, bundle) {
this.data = data;
this.iotizeModule = iotizeModule;
if (bundle) {
this._bundle = bundle;
}
}
toJson() {
return this.data;
}
module() {
return this.iotizeModule;
}
/**
* Size (int bits) of one element
*/
valueSize() {
return IotizeVariableModel.unitSizeToBitSize(parseInt(this.data.$.Unit));
}
valueQuantity() {
return Number(this.data.$.Quantity);
}
name() {
return this.data.$.GUI_Name;
}
id() {
return this.data.$.Id;
}
alias() {
return this.data.$.GUI_Alias;
}
description() {
return this.data.$.GUI_Description;
}
unit() {
return this.data.$.GUI_DispUnit;
}
bundle() {
return this._bundle;
}
// public type(): VariableType.Data {
// if (this.data.$.GUI_float && this.data.$.GUI_float == 1) {
// return VariableType.Data.FLOAT32;
// } else if (this.data.$.GUI_unsigned && this.data.$.GUI_unsigned == 1) {
// return VariableType.Data.UINT32;
// } else {
// return VariableType.Data.INT32;
// }
// }
dataType() {
const unit = parseInt(this.data.$.Unit, 10);
if (this.data.$.GUI_float && this.data.$.GUI_float == 1) {
return VariableType.Data.FLOAT32;
}
else if (this.data.$.GUI_unsigned && this.data.$.GUI_unsigned == 1) {
switch (unit) {
case 0:
return VariableType.Data.BOOLEAN;
case 1:
return VariableType.Data.UINT8;
case 2:
return VariableType.Data.UINT16;
case 3:
return VariableType.Data.UINT32;
default:
throw new Error(`Invalid data size "${unit}" for variable ${this.name()}`);
}
}
else {
switch (unit) {
case 0:
return VariableType.Data.BOOLEAN;
case 1:
return VariableType.Data.INT8;
case 2:
return VariableType.Data.INT16;
case 3:
return VariableType.Data.INT32;
default:
throw new Error(`Invalid data size "${unit}" for variable ${this.name()}`);
}
}
}
/**
* Return attribute by name
*/
attribute(name) {
return this.data.$[name];
}
byteOrder() {
let guiCoding = this.attribute('GUI_coding');
if (guiCoding) {
if (this.valueSize() === 16) {
return toEnumFromValue(ByteOrder16Bits, guiCoding);
}
else if (this.valueSize() === 32) {
return toEnumFromValue(ByteOrder32Bits, guiCoding);
}
}
return undefined;
}
/**
* Convert unit size found in configuration file into a number of bits
*/
static unitSizeToBitSize(unitSize) {
switch (unitSize) {
case 0:
return 1;
case 1:
return 8;
case 2:
return 16;
case 3:
return 32;
default:
throw new Error(`Invalid unit size: ${unitSize}`);
}
}
}
class IotizeProfileModel {
constructor(data) {
this.data = data;
}
toJson() {
return this.data;
}
description() {
return this.data.$.GUI_Description;
}
name() {
return this.data.$.Name;
}
id() {
return this.data.$.Id;
}
}
class IotizeACL {
constructor(data, aclGroup) {
this.aclGroup = aclGroup;
this.data = data;
}
static create(profileName, aclGroup) {
return new IotizeACL({
$: {
ProfileName: profileName,
Rights: '',
},
}, aclGroup);
}
toJson() {
return this.data;
}
listRights() {
let rights = this._getProperty('Rights', '');
if (rights.length === 0) {
return [];
}
return rights.split(',').map((entry) => {
return entry.trim();
});
}
username() {
return this._getProperty('ProfileName');
}
hasRight(name) {
if (this.username() === 'admin') {
return true;
}
let rights = this.listRights();
return rights.indexOf(name) !== -1;
}
_getProperty(name, defaultValue) {
if (!(name in this.data.$)) {
if (defaultValue === undefined) {
throw new InvalidConfigurationError('Missing key ' + name + ' in IoTize ACL');
}
return defaultValue;
}
return this.data.$[name];
}
}
const ANONYMOUS_PROFILE = 'anonymous';
/**
*
*/
class IotizeACLGroup {
constructor(data, bundle) {
this.bundle = bundle;
this.data = data;
}
toJson() {
return this.data;
}
/**
* Get acl for a profile
* @param profileName
*/
getByProfileName(profileName) {
let profiles = this.listProfiles();
for (let profileAcl of profiles) {
if (profileAcl.username() === profileName) {
return profileAcl;
}
}
return IotizeACL.create(profileName, this);
}
hasRight(profileName, right) {
let anonymousProfile = this.getByProfileName(ANONYMOUS_PROFILE);
if (anonymousProfile && anonymousProfile.hasRight(right)) {
return true;
}
if (profileName !== ANONYMOUS_PROFILE) {
let profile = this.getByProfileName(profileName);
if (profile) {
return profile.hasRight(right);
}
}
return false;
}
/**
* List acl entries for each profile
*/
listProfiles() {
if (!this.data.IOTizeACL) {
return [];
}
let entries = [];
for (let data of this.data.IOTizeACL) {
entries.push(new IotizeACL(data, this));
}
return entries;
}
}
class IotizeBundleModel {
constructor(data, iotizeModule) {
this.data = data;
this.iotizeModule = iotizeModule;
}
toJson() {
return this.data;
}
module() {
return this.iotizeModule;
}
id() {
if (!this.data.$.Id) {
throw new InvalidConfigurationError('This bundle does not have an id');
}
return this.data.$.Id;
}
name() {
return this.data.$.GUI_Name || '#' + this.id();
}
dataLogPeriod() {
if (!this.data.$.DataLogPeriod) {
return 0;
}
return Number(this.data.$.DataLogPeriod);
}
hasDataLog() {
return this.dataLogPeriod() > 0;
}
description() {
return this.data.$.GUI_Description;
}
variable(variableId) {
for (let variable of this.variables()) {
if (variable.id() === variableId) {
return variable;
}
}
throw new VariableDoesNotExistError(variableId, this.iotizeModule);
}
hasVariable(variableId) {
for (let variable of this.variables()) {
if (variable.id() === variableId) {
return variable;
}
}
return false;
}
profiles() {
if (!this.data.IOTizeAccessControl) {
return [];
}
let profiles = [];
for (let profile of this.data.IOTizeAccessControl) {
profiles.push(new IotizeProfileModel(profile));
}
return profiles;
}
variables() {
if (!this.data.IOTizeVariable) {
return [];
}
let variables = [];
for (let variable of this.data.IOTizeVariable) {
variables.push(new IotizeVariableModel(variable, this.iotizeModule, this));
}
return variables;
}
getACLGroup() {
let data = {};
if (!this.data.IOTizeAccessControl) {
this.data.IOTizeAccessControl = [];
}
if (this.data.IOTizeAccessControl.length > 0) {
data = this.data.IOTizeAccessControl[0];
}
return new IotizeACLGroup(data, this);
}
}
/**
*
*/
class BundleDoesNotExistError extends BaseError {
// static CODE = 'BundleDoesNotExistError';
constructor(bundleId, config) {
super('Bundle "' + bundleId + '" does not exist.');
this.bundleId = bundleId;
this.config = config;
}
}
/**
*
*/
class VariableDoesNotExistError extends BaseError {
// static CODE = 'VariableDoesNotExistError';
constructor(variableId, config) {
super(`Variable "${variableId}" does not exist.`);
this.variableId = variableId;
this.config = config;
}
}
/**
* Class representing an iotize studio configuration for a module
*/
class IotizeConfigModel {
constructor(data) {
if (!data.IOTizeConfiguration) {
//console.error('Trying to create an IotizeConfigModel with invalid data: ', data);
throw new InvalidConfigurationError('Key IOTizeConfiguration must exists');
}
if (!data.IOTizeConfiguration.IOTizeApp) {
throw new InvalidConfigurationError('Key IOTizeConfiguration.IOTizeApp must exists');
}
if (data.IOTizeConfiguration.IOTizeApp.length === 0 ||
!data.IOTizeConfiguration.IOTizeApp[0]['$']) {
throw new InvalidConfigurationError('No IOTizeConfiguration.IOTizeApp defined in this configuration file');
}
this.data = data;
}
toJson() {
return this.data;
}
getAppConfig() {
return this.data.IOTizeConfiguration.IOTizeApp[0];
}
/**
* Get application name
*/
appName() {
if (!this.getAppConfig().$.Name) {
throw new InvalidConfigurationError('Application Name is not set');
}
return this.getAppConfig().$.Name;
}
/**
* Get config version
*/
version() {
if (!this.getAppConfig().$.Config) {
throw new InvalidConfigurationError('App config version is not set');
}
return this.getAppConfig().$.Config;
}
/**
* Get module serial number
*/
id() {
if (!this.getAppConfig().$.DevIoTizeSN) {
throw new InvalidConfigurationError('Serial number is not set');
}
return this.getAppConfig().$.DevIoTizeSN;
}
/**
* Set module serial number
* @param id
*/
setId(id) {
this.getAppConfig().$.DevIoTizeSN = id;
return this;
}
/**
* Alias for this.id();
*/
serialNumber() {
return this.id();
}
/**
* Get an array of all bundles
*/
bundles() {
if (!this.data.IOTizeConfiguration.IOTizeBundle) {
return [];
}
let bundles = [];
for (let bundle of this.data.IOTizeConfiguration.IOTizeBundle) {
// TODO bug if only one bundle
bundles.push(new IotizeBundleModel(bundle, this));
}
return bundles;
}
/**
* Array of bundles available for a specific profile
*/
bundleAvailableForProfileName(profileName) {
return this.bundles().reduce((list, currentValue) => {
if (currentValue.getACLGroup().hasRight(profileName, 'read')) {
list.push(currentValue);
}
return list;
}, []);
}
/**
* Get an array of all variables
*/
variables() {
if (!this.data.IOTizeConfiguration.IOTizeBundle) {
return [];
}
let res = [];
for (let bundle of this.data.IOTizeConfiguration.IOTizeBundle) {
bundle = new IotizeBundleModel(bundle, this);
res.push(...bundle.variables());
}
return res;
}
/**
* Get bundle by id, if it does not exist, throw BundleDoesNotExistError
* @throws BundleDoesNotExistError
* @param id
*/
bundle(id) {
for (let bundle of this.bundles()) {
if (bundle.id() === id) {
return bundle;
}
}
throw new BundleDoesNotExistError(id, this);
}
hasBundle(id) {
for (let bundle of this.bundles()) {
if (bundle.id() === id) {
return bundle;
}
}
return false;
}
/**
* Get variable by id, if it does not exist, throw VariableDoesNotExistError
* @throws VariableDoesNotExistError
* @param id
*/
variable(variableId) {
for (let variable of this.variables()) {
if (variable.id() == variableId) {
return variable;
}
}
throw new VariableDoesNotExistError(variableId, this);
}
/**
* Get an array of all profiles
*/
profiles() {
if (!this.data.IOTizeConfiguration.IOTizeProfile) {
return [];
}
let profiles = [];
for (let profile of this.data.IOTizeConfiguration.IOTizeProfile) {
// TODO bug if there is only one profile...
profiles.push(new IotizeProfileModel(profile));
}
return profiles;
}
description() {
return this.getAppConfig().$.GUI_Description;
}
name() {
return this.getAppConfig().$.GUI_Name;
}
}
/**
* Utility class to create iotize config model from XML or JSON file
*/
class IotizeConfigFactory {
/**
* Create IotizeConfigModel from a XML string
* @param xml
*/
static fromXML(xml) {
throw new Error(`Add import to @iotize/tap/config/iotize-studio/parser/xml to create configuration model from XML file`);
}
/**
* Create IotizeConfigModel from a JSON string
* @param json
*/
static fromJson(json) {
return new IotizeConfigModel(JSON.parse(json));
}
/**
* Build and IotizeConfigModel
* Input can either be
* - A json string
* - A XML string
* - A JSON Object
*
* TODO create test
* @param data
*/
static create(data) {
if (typeof data === 'string') {
data.trim();
if (data.length === 0) {
throw new Error('Cannot create configuration from empty data');
}
if (data[0] === '{') {
return IotizeConfigFactory.fromJson(data);
}
else {
return IotizeConfigFactory.fromXML(data);
}
}
else {
return new IotizeConfigModel(data);
}
}
}
class IoTizeStudioError extends CodeError {
static invalidKeyValue(key, msg) {
return new IoTizeStudioError(`Invalid IoTize studio value for configuration key "${key}". ${msg}`, IoTizeStudioError.Code.InvalidConfigKeyValue);
}
}
(function (IoTizeStudioError) {
let Code;
(function (Code) {
Code["InvalidConfigKeyValue"] = "IoTizeStudioErrorInvalidConfigKeyValue";
})(Code = IoTizeStudioError.Code || (IoTizeStudioError.Code = {}));
})(IoTizeStudioError || (IoTizeStudioError = {}));
const NetworkModeMapping = {
P2P: 'PEER_TO_PEER',
NETWORK: 'NETWORK',
INFRA: 'NETWORK',
INFRA_CLOUD: 'NETWORK', // TODO check
};
const LowPowerOptimizationLevelMapping = {
0: 'NO',
1: 'SHUTDOWN',
2: 'STANDBY', // TODO CHECK
};
const WIFI_KEY_VISIBILITY_MAPPING = {
0: 'VISIBLE',
1: 'HIDDEN',
};
const S3P_CONFIG_MODE_MAPPING = {
0: 'EMULATION',
1: 'INDEXED',
};
const WIFI_SSID_VISIBILITY_MAPPING = {
0: 'VISIBLE',
1: 'HIDDEN',
2: 'HIDDEN_FROM_SCAN',
};
const WIFI_PROTOCOL_MAPPING = {
1: ['B'],
3: ['B', 'G'],
7: ['B', 'G', 'N'],
};
const TargetProtocolMapping = {
JTAG: 'JTAG',
MODBUS: 'MODBUS',
NONE: 'NONE',
S3P: 'S3P',
SERIAL: 'SERIAL',
SERIAL_STANDARD: 'SERIAL',
SERIAL_VIA_TAPNPASS: 'SERIAL',
CUSTOM: 'CUSTOM',
SWD: 'SWD',
GPIO: 'DIRECT_IO',
DIRECT_IO: 'DIRECT_IO',
DIRECTIO: 'DIRECT_IO',
CAN: 'CAN',
NO_TARGET_PROTOCOL: 'NONE',
MODBUSTCP: 'MODBUS_TCP',
};
const NFCParingModeMapping = {
NO: 'NO',
PAIRING: 'MANDATORY',
'PAIRING+LOGIN': 'MANDATORY_FOR_LOGIN',
};
const NFC_CONNECTION_PRIORITY_MAPPING = {
0: 'NFC_NON_PRIORITY',
1: 'NFC_PRIORITY',
};
// const MAPPING: Record<WriteConfigKey, any> = {
// // App
// 'application.name': 'IOTizeConfiguration.IOTizeApp.#.Name',
// 'application.universalLink': 'IOTizeConfiguration.IOTizeApp.#.WebLink',
// 'application.uri': 'IOTizeConfiguration.IOTizeApp.#.GUI_Path',
// 'application.data': 'TODO',
// // CLOUD
// 'cloud.dataLog.uploadPeriod': 'IOTizeConfiguration.IOTizeApp.CloudServices.#.CloudMqttPeriod',
// 'cloud.dataLog.mqttPublishTopic': 'IOTizeConfiguration.IOTizeApp.WIFI_INFO.#.Cloud_Publish_Topic',
// 'cloud.dataLog.mqttSubscribeTopic': 'IOTizeConfiguration.IOTizeApp.WIFI_INFO.#.Cloud_Subscribe_Topic',
// 'cloud.platform': '',
// // Target
// 'target.protocol.type': 'IOTizeConfiguration.IOTizeApp.#.TargetProtocol',
// // Tap
// 'tap.certificate.privateKey': 'IOTizeConfiguration.IOTizeApp.GUI_MQTT_Info.GUI_DevMQTT_PrivateKey',
// 'tap.certificate.publicKey': 'IOTizeConfiguration.IOTizeApp.GUI_MQTT_Info.GUI_DevMQTT_Certificate',
// // JVM
// 'jvm.code': 'IOTizeConfiguration.IOTizeApp.#.JVM_Custom_Code',
// 'jvm.profileId': 'IOTizeConfiguration.IOTizeApp.#.',
// // LORA
// 'wireless.protocols.lora.config': 'IOTizeConfiguration.IOTizeApp.LoRa_Info.#.LoRaMode',
// 'wireless.protocols.lora.ids': 'IOTizeConfiguration.IOTizeApp.LoRa_Info.#.TODO',
// // MQTT
// 'wireless.protocols.mqtt.clientId': 'IOTizeConfiguration.IOTizeApp.WIFI_INFO.#.MQTTRelay_Client_ID',
// 'wireless.protocols.mqtt.password': 'IOTizeConfiguration.IOTizeApp.WIFI_INFO.#.MQTTRelay_Passwd',
// 'wireless.protocols.mqtt.username': 'IOTizeConfiguration.IOTizeApp.WIFI_INFO.#.MQTTRelay_Broker_Login',
// 'wireless.protocols.mqtt.brokerPort': 'IOTizeConfiguration.IOTizeApp.WIFI_INFO.#.MQTTRelay_Service',
// 'wireless.protocols.mqtt.brokerHostname': 'IOTizeConfiguration.IOTizeApp.WIFI_INFO.#.MQTTRelay_URL',
// 'wireless.protocols.mqtt.brokerCertificate': 'IOTizeConfiguration.IOTizeApp.WIFI_INFO.#.Broker_Certificate',
// 'wireless.protocols.mqtt.topicPrefix': 'IOTizeConfiguration.IOTizeApp.WIFI_INFO.#.TODO',
// // NFC
// 'wireless.protocols.nfc.autoLogProfileId': 'IOTizeConfiguration.IOTizeApp.#.NFCLogin',
// 'wireless.protocols.nfc.connectionPriority': 'IOTizeConfiguration.IOTizeApp.#.NFCInterruptionLock',
// 'wireless.protocols.nfc.pairingMode': 'IOTizeConfiguration.IOTizeApp.#.NFCPairingMandatory',
// // WIFI
// 'wireless.protocols.wifi.ssid': 'IOTizeConfiguration.IOTizeApp.WIFI_Info.ModuleSSID ||',
// 'wireless.protocols.wifi.ssidVisibility': 'IOTizeConfiguration.IOTizeApp.WIFI_Info.Wifi_SSID_Hidden',
// 'wireless.protocols.wifi.keyVisibility': 'IOTizeConfiguration.IOTizeApp.WIFI_Info.#.Wifi_WEPKey_Hidden',
// 'wireless.protocols.wifi.gatewayIp': 'IOTizeConfiguration.IOTizeApp.WIFI_Info.#.',
// 'wireless.protocols.wifi.mask': 'IOTizeConfiguration.IOTizeApp.WIFI_Info.#.',
// 'wireless.protocols.wifi.mode': 'IOTizeConfiguration.IOTizeApp.WIFI_Info.#.',
// 'wireless.protocols.wifi.protocol': 'IOTizeConfiguration.IOTizeApp.WIFI_Info.#.',
// 'wireless.protocols.wifi.region': 'IOTizeConfiguration.IOTizeApp.WIFI_Info.#.',
// // Wireless options
// 'wireless.options.inactivityTimeout': 'IOTizeConfiguration.IOTizeApp.WIFI_Info.#.',
// 'wireless.options.authorizedProtocols': 'IOTizeConfiguration.IOTizeApp.WIFI_Info.#.',
// 'data.profiles[]': 'IOTizeConfiguration.IOTizeApp.IOTizeProfile',
// 'data.profiles[].username': '#.Name',
// 'data.profiles[].alias': '#.GroupId',
// 'data.profiles[].lifeTime': '#.LifeTime',
// 'data.profiles[].passwordKey': '#.Pwd',
// 'data.profiles[].password': false,
// 'data.bundles[]': 'IOTizeConfiguration.IOTizeApp.IOTizeBundle',
// 'data.bundles[].name': '#.Name',
// 'data.bundles[].dataLog.period': '#.DataLogPeriod',
// 'data.bundles[].acl[].$': '#.',
// 'data.variables[].address': '',
// 'data.variables[].bundleId': '',
// 'data.variables[].type': '',
// 'data.variables[].value': '',
// }
function isTruthy(v, falsyValue = ['no', 'false', '0']) {
if (v === undefined) {
return false;
}
if (falsyValue.indexOf(v.toString().toLowerCase()) >= 0) {
return false;
}
return !!v;
}
function convertToVariableType(accessor) {
let unsigned = isTruthy(accessor.value('GUI_unsigned', 'string', '0'));
let float = isTruthy(accessor.value('GUI_float', 'string', '0'));
const unitSize = accessor.value('Unit', 'number', 1);
if (unitSize === 1) {
const xAppRender = accessor.get('XAppRender', undefined);
if (xAppRender) {
const xAppRenderUnitType = xAppRender.value('GUI_UnitType', 'string', undefined);
if (xAppRenderUnitType === 'ASCII') {
return VariableType.Data.ASCII;
}
}
}
// Old version of IoTize Studio was storing UnitType ?
const unitDataType = accessor.value('UnitType', 'string', '');
if (unitDataType) {
if (unitDataType === 'boolean') {
return VariableType.Data.BOOLEAN;
}
else if (unitDataType === 'ASCII') {
return VariableType.Data.ASCII;
}
else {
unsigned = unitDataType === 'unsigned integer';
float = unitDataType === 'float';
}
}
if (float) {
return VariableType.Data.FLOAT32;
}
switch (unitSize) {
case 0:
return unsigned ? VariableType.Data.UINT8 : VariableType.Data.INT8;
case 1:
return unsigned ? VariableType.Data.UINT8 : VariableType.Data.INT8;
case 2:
return unsigned ? VariableType.Data.UINT16 : VariableType.Data.INT16;
case 3:
return unsigned ? VariableType.Data.UINT32 : VariableType.Data.INT32;
default:
throw new Error(`Unknown unit size: "${unitSize}" (type=${typeof unitSize})`);
}
}
function fromMapping(data, attributeName, mapping, defaultValue) {
let value = data.value(attributeName, 'string', defaultValue);
if (!value) {
return undefined;
}
value = value.replace(/ /g, '_').toUpperCase();
if (!(value in mapping)) {
throw new Error(`Invalid value "${value}" for key "${attributeName}"`);
}
return mapping[value];
}
/**
* TODO add to common lib (toEnumFromValue)
* @param mapping
* @param value
*/
function stringToEnum(mapping, value) {
const values = Object.keys(mapping).map((key) => mapping[key]);
if (values.find((v) => v.toString() === value) === undefined) {
throw new Error(`Missing value "${value}" in enum. Available keys are ${values.join(', ')}`);
}
if (value in mapping) {
// For string to number convertion
return mapping[mapping[value]];
}
return value;
}
function mapKey(object, key, value) {
if (value) {
object[key] = value;
}
}
// /**
// * Checks if current bundle can be seen by the current user
// * @param {string} profile name of the profile
// * @returns {boolean} true if bundle is visible to the user
// */
// isVisibleTo(profile: string) {
// let isVisible;
// switch (profile) {
// case 'admin':
// return true;
// case 'supervisor':
// return (this.rights.length !== 1 || this.rights.find( right => right.profile === 'admin') === undefined);
// default :
// isVisible = this.rights.find( right => right.profile === profile? true : right.profile === 'anonymous');
// }
// return isVisible !== undefined ? true : false;
// }
// /**
// * Checks if current bundle can be written by the current user
// * @param profile name of the profile
// * @returns {boolean} true if bundle is writable by the user
// */
// isWritableBy(profile: string) {
// switch (profile) {
// case 'admin':
// return this.rights.find(right => right.write) !== undefined;
// case 'supervisor':
// return this.rights.find(right => (right.write && right.profile !== 'admin')) !== undefined;
// default :
// const profileRights = this.rights.find( right => right.profile === profile);
// const anonymousRights = this.rights.find( right => right.profile === 'anonymous');
// return (profileRights && profileRights.write)? true : (anonymousRights && anonymousRights.write)? true : false;
// }
// }
function xmlToJson(xml) {
return parse(xml, {
attributeNamePrefix: '',
ignoreAttributes: false,
});
}
function convertIoTizeStudioConfigToTapConfig(fileContent) {
// console.log('File content', fileContent);
const config = new IoTizeStudioToTapConfigConverter().convert(new DotAccessor(xmlToJson(fileContent)));
return config;
}
function isHexString(input) {
if (typeof input !== 'string') {
return false;
}
return input.match(/^(0x)?([a-fA-F0-9])*$/);
}
function decodeInitialVariableValue(value) {
const values = value
.split(';')
.reduce((prev, v) => {
if (v.length === 0) {
return prev;
}
if (v.startsWith('0x')) {
prev.push(v);
}
else {
prev.push(parseFloat(v));
}
return prev;
}, []);
return values.length > 0
? values.length === 1
? values[0]
: values
: undefined;
}
function addNodeValueIfExist(data, config, mapping, method = 'string') {
Object.entries(mapping).forEach(([iotzName, apiName]) => {
var _a;
const value = (_a = data
.get(iotzName, undefined)) === null || _a === void 0 ? void 0 : _a.value('Name', method, undefined);
if (value) {
config[apiName] = value;
}
});
}
function addAttributeIfExist(data, config, iotzName, apiName, method) {
const value = data === null || data === void 0 ? void 0 : data.value(iotzName, method, undefined);
if (value !== undefined && value !== '') {
config[apiName] = value;
}
}
function addAttributesIfExists(data, config, mapping, method = 'string') {
Object.entries(mapping).forEach(([iotzName, apiName]) => {
addAttributeIfExist(data, config, iotzName, apiName, method);
});
}
/**
* Convert an IoTize studio configuration file to TapConfiguratorConfig
*/
class IoTizeStudioToTapConfigConverter {
constructor(options = {
strict: false,
}, host) {
this.options = options;
this.host = host;
/**
* Errors reported during convertion when strict mode is disabled
*/
this.warnings = [];
this.modbusSerialConfigConverter = new TapnpassProtocolConfigurationConverter();
}
convert(root) {
const config = root.get('IOTizeConfiguration');
const appConfigNode = config.get('IOTizeApp');
const wifiConfigNode = appConfigNode.get('WIFI_Info', undefined);
const bundles = config.list('IOTizeBundle', []);
const profiles = config.list('IOTizeProfile', []);
const versionString = config.value('Version', 'string', '1.0.0');
const result = {
version: versionString,
application: this.readApplicationConfig(appConfigNode),
data: {
bundles: this.readBundlesConfig(bundles),
profiles: this.readProfilesConfig(profiles),
},
cloud: this.readCloudConfig(appConfigNode),
jvm: this.readJvmConfig(config),
wireless: {
options: this.readWirelessOptionsConfig(appConfigNode),
protocols: {
wifi: this.readWifiConfig(wifiConfigNode),
mqtt: this.readMqttConfig(wifiConfigNode),
ble: this.readBleConfig(appConfigNode),
lora: this.readLoraConfig(appConfigNode),
nfc: this.readNfcConfig(appConfigNode),
},
},
tap: this.readTapConfig(appConfigNode),
target: this.readTargetConfig(appConfigNode),
};
const slaveServerConfig = this.readSlaveServerConfig(appConfigNode);
if (slaveServerConfig) {
result.slaveServer = slaveServerConfig;
}
const ethernet = this.readEthernetConfig(appConfigNode);
if (ethernet) {
result.ports = {
ethernet,
};
}
if (this.warnings.length > 0) {
// TODO
}
return result;
}
reportError(error, message = error.message) {
if (this.options.strict) {
throw error;
}
this.warnings.push({
error: error,
message,
});
}
readSlaveServerConfig(appConfig) {
if (!appConfig) {
return undefined;
}
const channel = appConfig.value('MBS_Com_Channel', 'number', 0);
switch (channel) {
case SlaveServerRawConfig.Channel.CAN:
return {
type: 'CAN',
config: this.readCanProtocolConfig(appConfig),
};
case SlaveServerRawConfig.Channel.MODBUS_RTU:
const targetProtocolConfDataLen = appConfig.value('MBSProtocolConfDataLen', 'number', 0);
let protocolData = hexStringToBuffer(appConfig.value('MBSProtocolConfData', 'string', ''));
return {
type: 'MODBUS_RTU',
config: this._protocolDataToJsonConfig(protocolData, targetProtocolConfDataLen),
};
case SlaveServerRawConfig.Channel.MODBUS_TCP:
return {
type: 'MODBUS_TCP',
config: {
port: appConfig.value('MBS_ModbusTCP_Port', 'number', 502),
firstRegisterAddress: appConfig.value('MBS_ModbusTCP_Addr0', 'number', 0),
},
};
case SlaveServerRawConfig.Channel.SPI:
return {
type: 'SPI',
};
case SlaveServerRawConfig.Channel.DISABLED:
return undefined;
default:
throw new Error('Invalid config'); // TODO proper error
}
}
readMqttConfig(cloudServicesConfig) {
var _a;
if (!cloudServicesConfig) {
return undefined;
}
const config = {};
addNodeValueIfExist(cloudServicesConfig, config, {
CloudHostName: 'brokerHostname',
CloudServiceName: 'brokerPort',
CloudMQTT_UID: 'password',
GUI_DevNetkey: 'relayNetKey',
GUI_DevMQTT_ClientID: 'clientId',
CloudMQTT_USERNAME: 'username',
});
const brokerCertificate = this.readFileContentAsHexString((_a = cloudServicesConfig
.get('AWSRootCertificate', undefined)) === null || _a === void 0 ? void 0 : _a.value('Name', 'string', undefined));
if (brokerCertificate) {
config.brokerCertificate = brokerCertificate;
}
return config;
}
readLoraConfig(appConfigNode) {
return {
config: undefined,
ids: undefined,
};
}
readBleConfig(appConfigNode) {
return {};
}
readNfcConfig(appConfigNode) {
return {
autoLogProfileId: appConfigNode.value('NFCLogin', 'number', undefined),
connectionPriority: fromMapping(appConfigNode, 'NFCInterruptionLock', NFC_CONNECTION_PRIORITY_MAPPING, undefined),
};
}
readWifiConfig(wifiConfigNode) {
if (!wifiConfigNode) {
return undefined;
}
const mode = fromMapping(wifiConfigNode, 'Network', NetworkModeMapping, 'P2P');
const isAP = mode === 'PEER_TO_PEER';
const config = {
mode,
};
addAttributeIfExist(wifiConfigNode, config, 'ModuleIPMask', 'mask', 'string');
addAttributeIfExist(wifiConfigNode, config, 'ModuleIPGateway', 'gatewayIp', 'string');
addAttributeIfExist(wifiConfigNode, config, 'ModuleIPAddress', 'ip', 'string');
addAttributeIfExist(wifiConfigNode, config, 'Wifi_TxLevel', 'txPower', 'number');
const keyVisibility = fromMapping(wifiConfigNode, 'Wifi_WEPKey_Hidden', WIFI_KEY_VISIBILITY_MAPPING, undefined);
if (keyVisibility !== undefined) {
config.keyVisibility = keyVisibility;
}
const protocol = fromMapping(wifiConfigNode, 'Wifi_Protocol', WIFI_PROTOCOL_MAPPING, undefined);
if (protocol) {
config.protocol = protocol;
}
addAttributeIfExist(wifiConfigNode, config, 'Wifi_Region', 'countryCode', 'number');
const ssidVisibility = fromMapping(wifiConfigNode, 'Wifi_SSID_Hidden', WIFI_SSID_VISIBILITY_MAPPING, undefined);
if (ssidVisibility) {
config.ssidVisibility = ssidVisibility;
}
addAttributeIfExist(wifiConfigNode, config, isAP ? 'Module_AP_SSID' : 'ModuleSSID', 'ssid', 'string');
addAttributeIfExist(wifiConfigNode, config, isAP ? 'Module_AP_WEPKey' : 'ModuleWEPKey', 'key', 'string');
return config;
}
toAuthorizedHostProtocols(value) {
if (!value || value === 'ALL' || value === 'FFFF') {
return undefined;
}
if (isHexString(value)) {
const encodedValue = hexStringToBuffer(value);
return converters.hostProtocolArray.decode(encodedValue).map((p) => {
return HostProtocol[p];
});
}
else {
return value.split(',').reduce((prev, value) => {
value = value.trim();
try {
// TODO check it's a read HostProtocolConfig...
prev.push(value); //stringToEnum<HostProtocolConfig>(HostProtocolConfig, value));
}
catch (err) {
this.reportError(err);
}
return prev;
}, []);
}
}
readAuthorizedHostProtocols(appConfigNode) {
let availableHostProtocols = undefined;
const hostProtocolString = appConfigNode.value('HostProtocol', 'string', undefined);
try {
availableHostProtocols =
this.toAuthorizedHostProtocols(hostProtocolString);
}
catch (err) {
this.reportError(err, `Cannot convert HostProtocol value "${hostProtocolString}"`);
}
return availableHostProtocols && availableHostProtocols.length > 0
? availableHostProtocols
: undefined;
}
readWirelessOptionsConfig(appConfigNode) {
const wirelessOptionsConfig = {
inactivityTimeout: appConfigNode.value('LowPowerHostTimeout', 'number', 0),
};
const authorizedProtocols = this.readAuthorizedHostProtocols(appConfigNode);
if (authorizedProtocols) {
wirelessOptionsConfig.authorizedProtocols = authorizedProtocols;
}
const nfcPairingMode = fromMapping(appConfigNode, 'NFCPairingMandatory', NFCParingModeMapping, undefined);
const blePairing = appConfigNode.value('BLEPairingRequest', 'boolean', undefined);
if (nfcPairingMode !== undefined || blePairing !== undefined) {
wirelessOptionsConfig.pairing = {
blePairing,
nfcPairingMode,
};
}
return wirelessOptionsConfig;
}
readJvmConfig(config) {
const appConfigNode = config.get('IOTizeApp');
const envNode = config.get('GUI_Environment', undefined);
const bcbFile = appConfigNode.value('JVM_Custom_Code', 'string', undefined);
const useJvm = envNode === null || envNode === void 0 ? void 0 : envNode.value('UseInternalJVM', 'boolean', false);
if (!useJvm) {
return undefined;
}
return {
code: bcbFile ? this.readFileContentAsHexString(bcbFile) : undefined,
// profileId: appConfigNode.value('TODO', 'number', undefined) // TODO
};
}
readEthernetConfig(accessor) {
const data = {};
addAttributesIfExists(accessor, data, {
EthernetIPAddress: 'ip',
EthernetIPGateway: 'gatewayIp',
EthernetIPMask: 'ipMask',
});
if (Object.keys(data).length === 0) {
return undefined;
}
return data;
}
readFileContentAsHexString(path) {
if (!path) {
return undefined;
}
if (!this.host) {
this.reportError(new Error(`External file reference "${path}" cannot be resolved`));
}
if (this.host) {
try {
const fileContent = this.host.getFileContent(path);
return '0x' + bufferToHexString(fileContent);
}
catch (err) {
this.reportError(err);
}
}
return undefined;
}
readCloudConfig(appConfigNode) {
const cloudService = appConfigNode.get('CloudServices', undefined);
// TODO cloud services
// if (cloudService?.has('CloudMQTT_UID')) {
// let url = '';
// const cloudHostName = cloudService.get('CloudHostName', undefined);
// const cloudServiceName = cloudService.get('CloudServiceName', undefined);
// if (cloudHostName) {
// url = `mqtt://${cloudHostName.value('Name', 'string', '')}:${cloudServiceName?.value('Name', 'string', '') || ''}`;
// }
// // cloud = {
// // // password: cloudService
// // // .get('CloudMQTT_UID', undefined)
// // // ?.value('Name', 'string', '') || '',
// // uploadPeriod: ,
// // url
// // };
// }
const wifiNode = appConfigNode.get('WIFI_Info', undefined);
const cloudConfig = {};
const dataLogConfig = {};
if (wifiNode) {
addAttributesIfExists(wifiNode, dataLogConfig, {
Cloud_Publish_Topic: 'mqttPublishTopic',
Cloud_Subscribe_Topic: 'mqttSubscribeTopic',
});
}
if (cloudService) {
addAttributesIfExists(cloudService, dataLogConfig, {
CloudMqttPeriod: 'uploadPeriod',
}, 'number');
}
if (Object.keys(dataLogConfig).length > 0) {
cloudConfig.dataLog = dataLogConfig;
}
if (Object.keys(cloudConfig).length > 0) {
return cloudConfig;
}
else {
return undefined;
}
}
readApplicationConfig(appConfigNode) {
const appPath = appConfigNode.has('GUI_Path')
? appConfigNode.value('GUI_Path', 'string', '')
: appConfigNode.value('Path', 'string', '');
// const appPathInfo = this.appPathConverter.decode(appPath);
const config = {
name: appConfigNode.value('Name', 'string', 'Tap'),
universalLink: appConfigNode.value('WebLink', 'string', ''),
uri: appPath,
};
const productCode = appConfigNode.value('ProductCode', 'string', undefined);
if (productCode !== undefined) {
config.productCode = productCode;
}
const additionalData = appConfigNode.value('Data', 'string', undefined);
if (additionalData !== undefined) {
config.data = `0x${bufferToHexString(asciiStringToByteBuffer(additionalData))}`;
}
return config;
}
readTapConfig(appConfigNode) {
var _a, _b;
const lockSettingsEncoded = appConfigNode.value('InterfaceLock', 'number', 0);
const lockOptions = new SecurityOptionsConverter().decode(Uint8Array.from([0, lockSettingsEncoded])); // TODO pad instead
const tapOptions = {
security: lockOptions,
powerOptimization: fromMapping(appConfigNode, 'LowPowerLevel', LowPowerOptimizationLevelMapping, undefined),
scram: {
hashIteration: appConfigNode.value('Scram_Hash_It', 'number', 5000),
},
certificate: {
publicKey: this.readFileContentAsHexString((_a = appConfigNode
.get('DeviceCertificate', undefined)) === null || _a === void 0 ? void 0 : _a.value('Name', 'string', undefined)),
privateKey: this.readFileContentAsHexString((_b = appConfigNode
.get('DevicePrivateKey', undefined)) === null || _b === void 0 ? void 0 : _b.value('Name', 'string', undefined)),
},
};
const timeZoneStr = appConfigNode.value('TimeZoneStr', 'string', undefined);
const ntpServer = appConfigNode.value('NTP_URL', 'string', undefined);
if (timeZoneStr || ntpServer) {
tapOptions.time = {
zone: timeZoneStr,
ntpServer,
};
}
return tapOptions;
}
readTapTimeConfig(appConfigNode) {
const zone = appConfigNode.value('TimeZoneStr', 'string', undefined);
const ntpServer = appConfigNode.value('NTP_URL', 'string', undefined);
if (zone || ntpServer) {
return {
zone,
ntpServer,
};
}
return undefined;
}
readTargetConfig(appConfig) {
const firmwareVersion = appConfig.value('Firmware', 'string', undefined);
if (appConfig.value('GUI_TargetType', 'string', '0') === '1') {
return {
type: 'TAPNPASS',
protocol: {
config: this.readTapnpassTargetProtocolConfig(appConfig),
},
firmwareVersion,
};
}
else {
const protocol = this.readMCUTargetProtocol(appConfig);
if (!protocol) {
return undefined;
}
return {
type: 'MCU',
protocol,
firmwareVersion,
};
}
// if (appConfig.has('USB_Aliases')) {
// data.type = "TAPNPASS";
// (data as TapNPassTargetConfig).usb = this._convertUsbData(appConfig);
// }
}
readTargetProtocol(appConfig) {
// const targetProtocolString = appConfig.value('TargetProtocol', 'string', 'SWD');
return fromMapping(appConfig, 'TargetProtocol', TargetProtocolMapping, 'NONE');
}
readTapnpassTargetProtocol(appConfig) {
const targetProtocol = this.readTargetProtocol(appConfig);
switch (targetProtocol) {
case 'SERIAL':
const serialConfig = {
type: 'SERIAL',
config: this.readTapnpassTargetProtocolConfig(appConfig),
};
return serialConfig;
case 'MODBUS':
const modbusConfig = {
type: 'MODBUS',
config: this.readTapnpassTargetProtocolConfig(appConfig),
};
return modbusConfig;
default:
throw new Error(`Illegal target protocol "${targetProtocol === null || targetProtocol === void 0 ? void 0 : targetProtocol.toString()}" for TapNPass target type.`);
}
}
readTapnpassTargetProtocolConfig(appConfig) {
const targetProtocolConfDataLen = appConfig.value('TargetProtocolConfDataLen', 'number', 0);
const protocolData = hexStringToBuffer(appConfig.value('TargetProtocolConfData', 'string', ''));
return this._protocolDataToJsonConfig(protocolData, targetProtocolConfDataLen);
}
readMCUTargetProtocol(appConfig) {
const targetProtocol = this.readTargetProtocol(appConfig);
switch (targetProtocol) {
case 'SWD':
return {
type: targetProtocol,
};
case 'CUSTOM':
return {
type: 'CUSTOM',
// config: '0xTODO'
};
case 'MODBUS':
return {
type: 'MODBUS',
config: this.readModbusProtocolConfig(appConfig),
};
case 'S3P':
return {
type: 'S3P',
config: this.readS3PProtocolConfig(appConfig),
};
case 'SERIAL':
const serialConfig = {
type: 'SERIAL',
config: this.readTapnpassTargetProtocolConfig(appConfig),
};
return serialConfig;
case 'NONE':
return undefined;
case 'DIRECT_IO':
return {
type: 'DIRECT_IO',
};
case 'CAN':
return {
type: 'CAN',
config: this.readCanProtocolConfig(appConfig),
};
case 'MODBUS_TCP':
return {
type: 'MODBUS_TCP',
config: this.readModbusTcpProtocolConfig(appConfig),
};
case 'JTAG':
default:
throw new Error(`Target protocol convertion from "${targetProtocol === null || targetProtocol === void 0 ? void