Implemented GUI configuration for MODBUS gateway extension.

This commit is contained in:
DK 2018-04-11 15:32:13 +05:00
parent a6661147be
commit cfcd71adb3
7 changed files with 1006 additions and 2 deletions

View File

@ -384,7 +384,8 @@ export default angular.module('thingsboard.types', [])
extensionType: {
http: "HTTP",
mqtt: "MQTT",
opc: "OPC UA"
opc: "OPC UA",
modbus: "MODBUS"
},
extensionValueType: {
string: 'value.string',
@ -428,6 +429,26 @@ export default angular.module('thingsboard.types', [])
PKCS12: "PKCS12",
JKS: "JKS"
},
extensionModbusFunctionCodes: {
1: "Read Coils (1)",
2: "Read Discrete Inputs (2)",
3: "Read Multiple Holding Registers (3)",
4: "Read Input Registers (4)"
},
extensionModbusTransports: {
tcp: "TCP",
udp: "UDP",
rtu: "RTU"
},
extensionModbusRtuParities: {
none: "none",
even: "even",
odd: "odd"
},
extensionModbusRtuEncodings: {
ascii: "ascii",
rtu: "rtu"
},
latestTelemetry: {
value: "LATEST_TELEMETRY",
name: "attribute.scope-latest-telemetry",

View File

@ -49,7 +49,7 @@ export default function ExtensionDialogController($scope, $mdDialog, $translate,
"brokers": []
};
}
if (vm.extension.type === "OPC UA") {
if (vm.extension.type === "OPC UA" || vm.extension.type === "MODBUS") {
vm.extension.configuration = {
"servers": []
};

View File

@ -62,6 +62,7 @@
<div tb-extension-form-http config="vm.extension.configuration" is-add="vm.isAdd" ng-if="vm.extension.type && vm.extension.type == vm.types.extensionType.http"></div>
<div tb-extension-form-mqtt config="vm.extension.configuration" is-add="vm.isAdd" ng-if="vm.extension.type && vm.extension.type == vm.types.extensionType.mqtt"></div>
<div tb-extension-form-opc configuration="vm.extension.configuration" ng-if="vm.extension.type && vm.extension.type == vm.types.extensionType.opc"></div>
<div tb-extension-form-modbus configuration="vm.extension.configuration" ng-if="vm.extension.type && vm.extension.type == vm.types.extensionType.modbus"></div>
</fieldset>
</md-content>
</div>

View File

@ -0,0 +1,131 @@
/*
* Copyright © 2016-2018 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import 'brace/ext/language_tools';
import 'brace/mode/json';
import 'brace/theme/github';
import './extension-form.scss';
/* eslint-disable angular/log */
import extensionFormModbusTemplate from './extension-form-modbus.tpl.html';
/* eslint-enable import/no-unresolved, import/default */
/*@ngInject*/
export default function ExtensionFormModbusDirective($compile, $templateCache, $translate, types) {
var linker = function(scope, element) {
function Server() {
this.transport = {
"type": "tcp",
"host": "localhost",
"port": 502,
"timeout": 3000
};
this.devices = []
}
function Device() {
this.unitId = 1;
this.deviceName = "";
this.attributesPollPeriod = 1000;
this.timeseriesPollPeriod = 1000;
this.attributes = [];
this.timeseries = [];
}
function Tag(globalPollPeriod) {
this.tag = "";
this.type = "long";
this.pollPeriod = globalPollPeriod;
this.functionCode = 3;
this.address = 0;
this.registerCount = 1;
this.bit = 0;
this.byteOrder = "BIG";
}
var template = $templateCache.get(extensionFormModbusTemplate);
element.html(template);
scope.types = types;
scope.theForm = scope.$parent.theForm;
if (!scope.configuration.servers.length) {
scope.configuration.servers.push(new Server());
}
scope.addServer = function(serversList) {
serversList.push(new Server());
scope.theForm.$setDirty();
};
scope.addDevice = function(deviceList) {
deviceList.push(new Device());
scope.theForm.$setDirty();
};
scope.addNewAttribute = function(device) {
device.attributes.push(new Tag(device.attributesPollPeriod));
scope.theForm.$setDirty();
};
scope.addNewTimeseries = function(device) {
device.timeseries.push(new Tag(device.timeseriesPollPeriod));
scope.theForm.$setDirty();
};
scope.removeItem = (item, itemList) => {
var index = itemList.indexOf(item);
if (index > -1) {
itemList.splice(index, 1);
}
scope.theForm.$setDirty();
};
$compile(element.contents())(scope);
scope.collapseValidation = function(index, id) {
var invalidState = angular.element('#'+id+':has(.ng-invalid)');
if(invalidState.length) {
invalidState.addClass('inner-invalid');
}
};
scope.expandValidation = function (index, id) {
var invalidState = angular.element('#'+id);
invalidState.removeClass('inner-invalid');
};
};
return {
restrict: "A",
link: linker,
scope: {
configuration: "=",
isAdd: "="
}
}
}

View File

@ -0,0 +1,818 @@
<!--
Copyright © 2016-2018 The Thingsboard Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<md-card class="extension-form extension-modbus">
<md-card-title>
<md-card-title-text>
<span translate class="md-headline">extension.configuration</span>
</md-card-title-text>
</md-card-title>
<md-card-content>
<v-accordion id="modbus-server-configs-accordion" class="vAccordion--default" onexpand="expandValidation(index, id)" oncollapse="collapseValidation(index, id)">
<v-pane id="modbus-servers-pane" expanded="true">
<v-pane-header>
{{ 'extension.modbus-server' | translate }}
</v-pane-header>
<v-pane-content>
<div ng-if="configuration.servers.length === 0">
<span translate layout-align="center center" class="tb-prompt">extension.modbus-add-server-prompt</span>
</div>
<div ng-if="configuration.servers.length > 0">
<ol class="list-group">
<li class="list-group-item" ng-repeat="(serverIndex, server) in configuration.servers">
<md-button aria-label="{{ 'action.remove' | translate }}"
class="md-icon-button"
ng-click="removeItem(server, configuration.servers)"
ng-hide="configuration.servers.length < 2"
>
<ng-md-icon icon="close" aria-label="{{ 'action.remove' | translate }}"></ng-md-icon>
<md-tooltip md-direction="top">
{{ 'action.remove' | translate }}
</md-tooltip>
</md-button>
<md-card>
<md-card-content>
<div layout="row">
<md-input-container flex="50" class="md-block tb-container-for-select">
<label translate>extension.modbus-transport</label>
<md-select required
name="transportType_{{serverIndex}}"
ng-model="server.transport.type"
>
<md-option ng-value="transportType"
ng-repeat="(transportType, transportValue) in types.extensionModbusTransports"
><span ng-bind="transportValue"></span></md-option>
</md-select>
<div ng-messages="theForm['transportType_' + serverIndex].$error">
<div translate
ng-message="required"
>extension.field-required</div>
</div>
</md-input-container>
</div>
<div layout="row" ng-if="server.transport.type == 'tcp'">
<md-input-container flex="33" class="md-block">
<label translate>extension.host</label>
<input required name="transportHost_{{serverIndex}}" ng-model="server.transport.host">
<div ng-messages="theForm['transportHost_' + serverIndex].$error">
<div translate ng-message="required">extension.field-required</div>
</div>
</md-input-container>
<md-input-container flex="33" class="md-block">
<label translate>extension.port</label>
<input type="number"
required
name="transportPort_{{serverIndex}}"
ng-model="server.transport.port"
min="1"
max="65535"
>
<div ng-messages="theForm['transportPort_' + serverIndex].$error">
<div translate
ng-message="required"
>extension.field-required</div>
<div translate
ng-message="min"
>extension.port-range</div>
<div translate
ng-message="max"
>extension.port-range</div>
</div>
</md-input-container>
<md-input-container flex="33" class="md-block">
<label translate>extension.timeout</label>
<input type="number"
required name="transportTimeout_{{serverIndex}}"
ng-model="server.transport.timeout"
>
<div ng-messages="theForm['transportTimeout_' + serverIndex].$error">
<div translate
ng-message="required"
>extension.field-required</div>
</div>
</md-input-container>
</div>
<div layout="row" ng-if="server.transport.type == 'udp'">
<md-input-container flex="33" class="md-block">
<label translate>extension.host</label>
<input required name="transportHost_{{serverIndex}}" ng-model="server.transport.host">
<div ng-messages="theForm['transportHost_' + serverIndex].$error">
<div translate ng-message="required">extension.field-required</div>
</div>
</md-input-container>
<md-input-container flex="33" class="md-block">
<label translate>extension.port</label>
<input type="number"
required
name="transportPort_{{serverIndex}}"
ng-model="server.transport.port"
min="1"
max="65535"
>
<div ng-messages="theForm['transportPort_' + serverIndex].$error">
<div translate
ng-message="required"
>extension.field-required</div>
<div translate
ng-message="min"
>extension.port-range</div>
<div translate
ng-message="max"
>extension.port-range</div>
</div>
</md-input-container>
<md-input-container flex="33" class="md-block">
<label translate>extension.timeout</label>
<input type="number"
required name="transportTimeout_{{serverIndex}}"
ng-model="server.transport.timeout"
>
<div ng-messages="theForm['transportTimeout_' + serverIndex].$error">
<div translate
ng-message="required"
>extension.field-required</div>
</div>
</md-input-container>
</div>
<div ng-if="server.transport.type == 'rtu'">
<div layout="row">
<md-input-container flex="70" class="md-block">
<label translate>extension.modbus-port-name</label>
<input required name="transportPortName_{{serverIndex}}" ng-model="server.transport.portName">
<div ng-messages="theForm['transportPortName_' + serverIndex].$error">
<div translate ng-message="required">extension.field-required</div>
</div>
</md-input-container>
<md-input-container flex="30" class="md-block">
<label translate>extension.timeout</label>
<input type="number"
required name="transportTimeout_{{serverIndex}}"
ng-model="server.transport.timeout"
>
<div ng-messages="theForm['transportTimeout_' + serverIndex].$error">
<div translate
ng-message="required"
>extension.field-required</div>
</div>
</md-input-container>
</div>
<div layout="row">
<md-input-container flex="50" class="md-block tb-container-for-select">
<label translate>extension.modbus-encoding</label>
<md-select required
name="transportEncoding_{{serverIndex}}"
ng-model="server.transport.encoding"
>
<md-option ng-value="encodingType"
ng-repeat="(encodingType, encodingValue) in types.extensionModbusRtuEncodings"
><span ng-bind="encodingValue"></span></md-option>
</md-select>
<div ng-messages="theForm['transportEncoding_' + serverIndex].$error">
<div translate
ng-message="required"
>extension.field-required</div>
</div>
</md-input-container>
<md-input-container flex="50" class="md-block tb-container-for-select">
<label translate>extension.modbus-parity</label>
<md-select name="transportParity_{{serverIndex}}" ng-model="server.transport.parity">
<md-option ng-repeat="(parityKey, parityValue) in types.extensionModbusRtuParities"
ng-value="parityValue"
>
{{parityValue}}
</md-option>
</md-select>
<div ng-messages="theForm['transportParity_' + serverIndex].$error">
<div translate
ng-message="required"
>extension.field-required</div>
</div>
</md-input-container>
</div>
<div layout="row">
<md-input-container flex="33" class="md-block">
<label translate>extension.modbus-baudrate</label>
<input type="number"
required name="transportBaudRate_{{serverIndex}}"
ng-model="server.transport.baudRate"
>
<div ng-messages="theForm['transportBaudRate_' + serverIndex].$error">
<div translate
ng-message="required"
>extension.field-required</div>
</div>
</md-input-container>
<md-input-container flex="33" class="md-block">
<label translate>extension.modbus-databits</label>
<input type="number"
required
name="transportDataBits_{{serverIndex}}"
ng-model="server.transport.dataBits"
min="7"
max="8"
>
<div ng-messages="theForm['transportDataBits_' + serverIndex].$error">
<div translate
ng-message="required"
>extension.field-required</div>
<div translate
ng-message="min"
>extension.modbus-databits-range</div>
<div translate
ng-message="max"
>extension.modbus-databits-range</div>
</div>
</md-input-container>
<md-input-container flex="33" class="md-block">
<label translate>extension.modbus-stopbits</label>
<input type="number"
required
name="transportStopBits_{{serverIndex}}"
ng-model="server.transport.stopBits"
min="1"
max="2"
>
<div ng-messages="theForm['transportStopBits_' + serverIndex].$error">
<div translate
ng-message="required"
>extension.field-required</div>
<div translate
ng-message="min"
>extension.modbus-stopbits-range</div>
<div translate
ng-message="max"
>extension.modbus-stopbits-range</div>
</div>
</md-input-container>
</div>
</div>
<v-accordion id="modbus-mapping-accordion"
class="vAccordion--default"
onexpand="expandValidation(index, id)" oncollapse="collapseValidation(index, id)">
<v-pane id="modbus-mapping-pane_{{serverIndex}}">
<v-pane-header>
{{ 'extension.mapping' | translate }}
</v-pane-header>
<v-pane-content>
<div ng-if="server.devices.length > 0">
<ol class="list-group">
<li class="list-group-item"
ng-repeat="(deviceIndex, device) in server.devices"
>
<md-button aria-label="{{ 'action.remove' | translate }}"
class="md-icon-button"
ng-click="removeItem(device, server.devices)"
>
<ng-md-icon icon="close" aria-label="{{ 'action.remove' | translate }}"></ng-md-icon>
<md-tooltip md-direction="top">
{{ 'action.remove' | translate }}
</md-tooltip>
</md-button>
<md-card>
<md-card-content>
<div flex layout="row">
<md-input-container flex="80" class="md-block">
<label translate>extension.modbus-device-name</label>
<input required
name="deviceName_{{serverIndex}}{{deviceIndex}}"
ng-model="device.deviceName"
>
<div ng-messages="theForm['deviceName_' + serverIndex + deviceIndex].$error">
<div translate
ng-message="required"
>extension.field-required</div>
</div>
</md-input-container>
<md-input-container flex="20" class="md-block">
<label translate>extension.modbus-unit-id</label>
<input type="number"
required
name="unitId_{{serverIndex}}{{deviceIndex}}"
ng-model="device.unitId"
min="1"
max="247"
>
<div ng-messages="theForm['unitId_' + serverIndex + deviceIndex].$error">
<div translate
ng-message="required"
>extension.field-required</div>
<div translate
ng-message="min"
>extension.modbus-unit-id-range</div>
<div translate
ng-message="max"
>extension.modbus-unit-id-range</div>
</div>
</md-input-container>
</div>
<div flex layout="row">
<md-input-container flex="50" class="md-block">
<label translate>extension.modbus-attributes-poll-period</label>
<input type="number"
required
name="attributesPollPeriod_{{serverIndex}}{{deviceIndex}}"
ng-model="device.attributesPollPeriod"
min="1"
>
<div ng-messages="theForm['attributesPollPeriod_' + serverIndex + deviceIndex].$error">
<div translate
ng-message="required"
>extension.field-required</div>
<div translate
ng-message="min"
>extension.modbus-poll-period-range</div>
</div>
</md-input-container>
<md-input-container flex="50" class="md-block">
<label translate>extension.modbus-timeseries-poll-period</label>
<input type="number"
required
name="timeseriesPollPeriod_{{serverIndex}}{{deviceIndex}}"
ng-model="device.timeseriesPollPeriod"
min="1"
>
<div ng-messages="theForm['timeseriesPollPeriod_' + serverIndex + deviceIndex].$error">
<div translate
ng-message="required"
>extension.field-required</div>
<div translate
ng-message="min"
>extension.modbus-poll-period-range</div>
</div>
</md-input-container>
</div>
<v-accordion id="modbus-attributes-accordion"
class="vAccordion--default"
onexpand="expandValidation(index, id)" oncollapse="collapseValidation(index, id)">
<v-pane id="modbus-attributes-pane_{{serverIndex}}{{deviceIndex}}">
<v-pane-header>
{{ 'extension.attributes' | translate }}
</v-pane-header>
<v-pane-content>
<div ng-show="device.attributes.length > 0">
<ol class="list-group">
<li class="list-group-item"
ng-repeat="(attributeIndex, attribute) in device.attributes"
>
<md-button aria-label="{{ 'action.remove' | translate }}"
class="md-icon-button"
ng-click="removeItem(attribute, device.attributes)">
<ng-md-icon icon="close"
aria-label="{{ 'action.remove' | translate }}"
></ng-md-icon>
<md-tooltip md-direction="top">
{{ 'action.remove' | translate }}
</md-tooltip>
</md-button>
<md-card>
<md-card-content>
<section flex layout="row">
<md-input-container flex="50" class="md-block">
<label translate>extension.modbus-tag</label>
<input required
name="modbusAttributeTag_{{serverIndex}}{{deviceIndex}}{{attributeIndex}}"
ng-model="attribute.tag"
>
<div ng-messages="theForm['modbusAttributeTag_' + serverIndex + deviceIndex + attributeIndex].$error">
<div translate
ng-message="required"
>extension.field-required</div>
</div>
</md-input-container>
<md-input-container flex="30" class="md-block tb-container-for-select">
<label translate>extension.type</label>
<md-select required name="modbusAttributeType_{{serverIndex}}{{deviceIndex}}{{attributeIndex}}"
ng-model="attribute.type"
>
<md-option ng-repeat="(attrType, attrTypeValue) in types.extensionValueType"
ng-value="attrType"
>
{{attrTypeValue | translate}}
</md-option>
</md-select>
<div ng-messages="theForm['modbusAttributeType_' + serverIndex + deviceIndex + attributeIndex].$error">
<div translate
ng-message="required"
>extension.field-required</div>
</div>
</md-input-container>
<md-input-container flex="20" class="md-block">
<label translate>extension.modbus-poll-period</label>
<input type="number"
name="pollPeriod_{{serverIndex}}{{deviceIndex}}"
ng-model="attribute.pollPeriod"
min="1"
>
<div ng-messages="theForm['pollPeriod_' + serverIndex + deviceIndex].$error">
<div translate
ng-message="min"
>extension.modbus-poll-period-range</div>
</div>
</md-input-container>
</section>
<section flex layout="row">
<md-input-container flex="50" class="md-block tb-container-for-select">
<label translate>extension.type</label>
<md-select required name="modbusAttributeFunctionCode_{{serverIndex}}{{deviceIndex}}{{attributeIndex}}"
ng-model="attribute.functionCode"
>
<md-option ng-repeat="(functionCode, functionName) in types.extensionModbusFunctionCodes"
ng-value="functionCode"
>
{{functionName}}
</md-option>
</md-select>
<div ng-messages="theForm['modbusAttributeFunctionCode_' + serverIndex + deviceIndex + attributeIndex].$error">
<div translate
ng-message="required"
>extension.field-required</div>
</div>
</md-input-container>
<md-input-container flex="50" class="md-block">
<label translate>extension.modbus-register-address</label>
<input type="number"
required
name="address_{{serverIndex}}{{deviceIndex}}"
ng-model="attribute.address"
min="0"
max="65535"
>
<div ng-messages="theForm['address_' + serverIndex + deviceIndex].$error">
<div translate
ng-message="required"
>extension.field-required</div>
<div translate
ng-message="min"
>extension.modbus-register-address-range</div>
<div translate
ng-message="max"
>extension.modbus-register-address-range</div>
</div>
</md-input-container>
</section>
<section flex layout="row">
<md-input-container flex="50" class="md-block" ng-if="attribute.type != 'boolean'">
<label translate>extension.modbus-register-count</label>
<input type="number"
name="registerCount_{{serverIndex}}{{deviceIndex}}"
ng-model="attribute.registerCount"
min="1"
>
<div ng-messages="theForm['registerCount_' + serverIndex + deviceIndex].$error">
<div translate
ng-message="min"
>extension.modbus-register-count-range</div>
</div>
</md-input-container>
<md-input-container flex="50" class="md-block" ng-if="attribute.type == 'boolean' && attribute.functionCode >= 3">
<label translate>extension.modbus-register-bit-index</label>
<input type="number"
required
name="bit_{{serverIndex}}{{deviceIndex}}"
ng-model="attribute.bit"
min="0"
max="15"
>
<div ng-messages="theForm['bit_' + serverIndex + deviceIndex].$error">
<div translate
ng-message="required"
>extension.field-required</div>
<div translate
ng-message="min"
>extension.modbus-register-bit-index-range</div>
<div translate
ng-message="max"
>extension.modbus-register-bit-index-range</div>
</div>
</md-input-container>
</section>
<section flex layout="row">
<md-input-container flex="100" class="md-block" ng-if="attribute.functionCode >= 3">
<label translate>extension.modbus-byte-order</label>
<input required
name="modbusByteOrder_{{serverIndex}}{{deviceIndex}}{{attributeIndex}}"
ng-model="attribute.byteOrder"
>
<div ng-messages="theForm['modbusByteOrder_' + serverIndex + deviceIndex + attributeIndex].$error">
<div translate
ng-message="required"
>extension.field-required</div>
</div>
</md-input-container>
</section>
</md-card-content>
</md-card>
</li>
</ol>
</div>
<div flex layout="row" layout-align="start center">
<md-button class="md-primary md-raised"
ng-click="addNewAttribute(device)"
aria-label="{{ 'action.add' | translate }}"
>
<md-icon class="material-icons">add</md-icon>
<span translate>extension.add-attribute</span>
</md-button>
</div>
</v-pane-content>
</v-pane>
</v-accordion>
<v-accordion id="modbus-timeseries-accordion" class="vAccordion--default" onexpand="expandValidation(index, id)" oncollapse="collapseValidation(index, id)">
<v-pane id="modbus-timeseries-pane_{{serverIndex}}{{deviceIndex}}">
<v-pane-header>
{{ 'extension.timeseries' | translate }}
</v-pane-header>
<v-pane-content>
<div ng-show="device.timeseries.length > 0">
<ol class="list-group">
<li class="list-group-item"
ng-repeat="(timeseriesIndex, timeserie) in device.timeseries"
>
<md-button aria-label="{{ 'action.remove' | translate }}"
class="md-icon-button"
ng-click="removeItem(timeserie, device.timeseries)">
<ng-md-icon icon="close"
aria-label="{{ 'action.remove' | translate }}"
></ng-md-icon>
<md-tooltip md-direction="top">
{{ 'action.remove' | translate }}
</md-tooltip>
</md-button>
<md-card>
<md-card-content>
<section flex layout="row">
<md-input-container flex="50" class="md-block">
<label translate>extension.modbus-tag</label>
<input required
name="modbusTimeserieTag_{{serverIndex}}{{deviceIndex}}{{timeseriesIndex}}"
ng-model="timeserie.tag"
>
<div ng-messages="theForm['modbusTimeserieTag_' + serverIndex + deviceIndex + timeseriesIndex].$error">
<div translate
ng-message="required"
>extension.field-required</div>
</div>
</md-input-container>
<md-input-container flex="30" class="md-block tb-container-for-select">
<label translate>extension.type</label>
<md-select required name="modbusTimeserieType_{{serverIndex}}{{deviceIndex}}{{timeseriesIndex}}"
ng-model="timeserie.type"
>
<md-option ng-repeat="(attrType, attrTypeValue) in types.extensionValueType"
ng-value="attrType"
>
{{attrTypeValue | translate}}
</md-option>
</md-select>
<div ng-messages="theForm['modbusTimeserieType_' + serverIndex + deviceIndex + timeseriesIndex].$error">
<div translate
ng-message="required"
>extension.field-required</div>
</div>
</md-input-container>
<md-input-container flex="20" class="md-block">
<label translate>extension.modbus-poll-period</label>
<input type="number"
name="pollPeriod_{{serverIndex}}{{deviceIndex}}"
ng-model="timeserie.pollPeriod"
min="1"
>
<div ng-messages="theForm['pollPeriod_' + serverIndex + deviceIndex].$error">
<div translate
ng-message="min"
>extension.modbus-poll-period-range</div>
</div>
</md-input-container>
</section>
<section flex layout="row">
<md-input-container flex="50" class="md-block tb-container-for-select">
<label translate>extension.type</label>
<md-select required name="modbusTimeserieFunctionCode_{{serverIndex}}{{deviceIndex}}{{timeseriesIndex}}"
ng-model="timeserie.functionCode"
>
<md-option ng-repeat="(functionCode, functionName) in types.extensionModbusFunctionCodes"
ng-value="functionCode"
>
{{functionName}}
</md-option>
</md-select>
<div ng-messages="theForm['modbusTimeserieFunctionCode_' + serverIndex + deviceIndex + timeseriesIndex].$error">
<div translate
ng-message="required"
>extension.field-required</div>
</div>
</md-input-container>
<md-input-container flex="50" class="md-block">
<label translate>extension.modbus-register-address</label>
<input type="number"
required
name="address_{{serverIndex}}{{deviceIndex}}"
ng-model="timeserie.address"
min="0"
max="65535"
>
<div ng-messages="theForm['address_' + serverIndex + deviceIndex].$error">
<div translate
ng-message="required"
>extension.field-required</div>
<div translate
ng-message="min"
>extension.modbus-register-address-range</div>
<div translate
ng-message="max"
>extension.modbus-register-address-range</div>
</div>
</md-input-container>
</section>
<section flex layout="row">
<md-input-container flex="50" class="md-block" ng-if="timeserie.type != 'boolean'">
<label translate>extension.modbus-register-count</label>
<input type="number"
name="registerCount_{{serverIndex}}{{deviceIndex}}"
ng-model="timeserie.registerCount"
min="1"
>
<div ng-messages="theForm['registerCount_' + serverIndex + deviceIndex].$error">
<div translate
ng-message="min"
>extension.modbus-register-count-range</div>
</div>
</md-input-container>
<md-input-container flex="50" class="md-block" ng-if="timeserie.type == 'boolean' && timeserie.functionCode >= 3">
<label translate>extension.modbus-register-bit-index</label>
<input type="number"
required
name="bit_{{serverIndex}}{{deviceIndex}}"
ng-model="timeserie.bit"
min="0"
max="15"
>
<div ng-messages="theForm['bit_' + serverIndex + deviceIndex].$error">
<div translate
ng-message="required"
>extension.field-required</div>
<div translate
ng-message="min"
>extension.modbus-register-bit-index-range</div>
<div translate
ng-message="max"
>extension.modbus-register-bit-index-range</div>
</div>
</md-input-container>
</section>
<section flex layout="row">
<md-input-container flex="100" class="md-block" ng-if="timeserie.functionCode >= 3">
<label translate>extension.modbus-byte-order</label>
<input required
name="modbusByteOrder_{{serverIndex}}{{deviceIndex}}{{timeseriesIndex}}"
ng-model="timeserie.byteOrder"
>
<div ng-messages="theForm['modbusByteOrder_' + serverIndex + deviceIndex + timeseriesIndex].$error">
<div translate
ng-message="required"
>extension.field-required</div>
</div>
</md-input-container>
</section>
</md-card-content>
</md-card>
</li>
</ol>
</div>
<div flex layout="row" layout-align="start center">
<md-button class="md-primary md-raised"
ng-click="addNewTimeseries(device)"
aria-label="{{ 'action.add' | translate }}"
>
<md-icon class="material-icons">add</md-icon>
<span translate>extension.add-timeseries</span>
</md-button>
</div>
</v-pane-content>
</v-pane>
</v-accordion>
</md-card-content>
</md-card>
</li>
</ol>
</div>
<div flex
layout="row"
layout-align="start center"
>
<md-button class="md-primary md-raised"
ng-click="addDevice(server.devices)"
aria-label="{{ 'action.add' | translate }}"
>
<md-icon class="material-icons">add</md-icon>
<span translate>extension.add-device</span>
</md-button>
</div>
</v-pane-content>
</v-pane>
</v-accordion>
</md-card-content>
</md-card>
</li>
</ol>
<div flex
layout="row"
layout-align="start center"
>
<md-button class="md-primary md-raised"
ng-click="addServer(configuration.servers)"
aria-label="{{ 'action.add' | translate }}"
>
<md-icon class="material-icons">add</md-icon>
<span translate>extension.modbus-add-server</span>
</md-button>
</div>
</div>
</v-pane-content>
</v-pane>
</v-accordion>
<!--{{config}}-->
</md-card-content>
</md-card>

View File

@ -17,6 +17,8 @@ import ExtensionTableDirective from './extension-table.directive';
import ExtensionFormHttpDirective from './extensions-forms/extension-form-http.directive';
import ExtensionFormMqttDirective from './extensions-forms/extension-form-mqtt.directive'
import ExtensionFormOpcDirective from './extensions-forms/extension-form-opc.directive';
import ExtensionFormModbusDirective from './extensions-forms/extension-form-modbus.directive';
import {ParseToNull} from './extension-dialog.controller';
export default angular.module('thingsboard.extension', [])
@ -24,5 +26,6 @@ export default angular.module('thingsboard.extension', [])
.directive('tbExtensionFormHttp', ExtensionFormHttpDirective)
.directive('tbExtensionFormMqtt', ExtensionFormMqttDirective)
.directive('tbExtensionFormOpc', ExtensionFormOpcDirective)
.directive('tbExtensionFormModbus', ExtensionFormModbusDirective)
.directive('parseToNull', ParseToNull)
.name;

View File

@ -866,8 +866,10 @@ export default angular.module('thingsboard.locale', [])
"response-timeout": "Response timeout in milliseconds",
"topic-expression": "Topic expression",
"client-scope": "Client scope",
"add-device": "Add device",
"opc-server": "Servers",
"opc-add-server": "Add server",
"opc-add-server-prompt": "Please add server",
"opc-application-name": "Application name",
"opc-application-uri": "Application uri",
"opc-scan-period-in-seconds": "Scan period in seconds",
@ -882,6 +884,34 @@ export default angular.module('thingsboard.locale', [])
"opc-keystore-key-password":"Key password",
"opc-device-node-pattern":"Device node pattern",
"opc-device-name-pattern":"Device name pattern",
"modbus-server": "Servers/slaves",
"modbus-add-server": "Add server/slave",
"modbus-add-server-prompt": "Please add server/slave",
"modbus-transport": "Transport",
"modbus-port-name": "Serial port name",
"modbus-encoding": "Encoding",
"modbus-parity": "Parity",
"modbus-baudrate": "Baud rate",
"modbus-databits": "Data bits",
"modbus-stopbits": "Stop bits",
"modbus-databits-range": "Data bits should be in a range from 7 to 8.",
"modbus-stopbits-range": "Stop bits should be in a range from 1 to 2.",
"modbus-unit-id": "Unit ID",
"modbus-unit-id-range": "Unit ID should be in a range from 1 to 247.",
"modbus-device-name":"Device name",
"modbus-poll-period": "Poll period (ms)",
"modbus-attributes-poll-period": "Attributes poll period (ms)",
"modbus-timeseries-poll-period": "Timeseries poll period (ms)",
"modbus-poll-period-range": "Poll period should be positive value.",
"modbus-tag": "Tag",
"modbus-function": "Function",
"modbus-register-address": "Register address",
"modbus-register-address-range": "Register address should be in a range from 0 to 65535.",
"modbus-register-bit-index": "Bit index",
"modbus-register-bit-index-range": "Bit index should be in a range from 0 to 15.",
"modbus-register-count": "Register count",
"modbus-register-count-range": "Register count should be a positive value.",
"modbus-byte-order": "Byte order",
"sync": {
"status": "Status",