UNPKG

@iotize/tap

Version:

IoTize Device client for Javascript

1,379 lines (1,365 loc) 63.9 kB
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