Commit cfcd71adb3d362627d4ef1a16d6ffc6517cc0265

Authored by DK
1 parent a6661147

Implemented GUI configuration for MODBUS gateway extension.

... ... @@ -384,7 +384,8 @@ export default angular.module('thingsboard.types', [])
384 384 extensionType: {
385 385 http: "HTTP",
386 386 mqtt: "MQTT",
387   - opc: "OPC UA"
  387 + opc: "OPC UA",
  388 + modbus: "MODBUS"
388 389 },
389 390 extensionValueType: {
390 391 string: 'value.string',
... ... @@ -428,6 +429,26 @@ export default angular.module('thingsboard.types', [])
428 429 PKCS12: "PKCS12",
429 430 JKS: "JKS"
430 431 },
  432 + extensionModbusFunctionCodes: {
  433 + 1: "Read Coils (1)",
  434 + 2: "Read Discrete Inputs (2)",
  435 + 3: "Read Multiple Holding Registers (3)",
  436 + 4: "Read Input Registers (4)"
  437 + },
  438 + extensionModbusTransports: {
  439 + tcp: "TCP",
  440 + udp: "UDP",
  441 + rtu: "RTU"
  442 + },
  443 + extensionModbusRtuParities: {
  444 + none: "none",
  445 + even: "even",
  446 + odd: "odd"
  447 + },
  448 + extensionModbusRtuEncodings: {
  449 + ascii: "ascii",
  450 + rtu: "rtu"
  451 + },
431 452 latestTelemetry: {
432 453 value: "LATEST_TELEMETRY",
433 454 name: "attribute.scope-latest-telemetry",
... ...
... ... @@ -49,7 +49,7 @@ export default function ExtensionDialogController($scope, $mdDialog, $translate,
49 49 "brokers": []
50 50 };
51 51 }
52   - if (vm.extension.type === "OPC UA") {
  52 + if (vm.extension.type === "OPC UA" || vm.extension.type === "MODBUS") {
53 53 vm.extension.configuration = {
54 54 "servers": []
55 55 };
... ...
... ... @@ -62,6 +62,7 @@
62 62 <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>
63 63 <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>
64 64 <div tb-extension-form-opc configuration="vm.extension.configuration" ng-if="vm.extension.type && vm.extension.type == vm.types.extensionType.opc"></div>
  65 + <div tb-extension-form-modbus configuration="vm.extension.configuration" ng-if="vm.extension.type && vm.extension.type == vm.types.extensionType.modbus"></div>
65 66 </fieldset>
66 67 </md-content>
67 68 </div>
... ...
  1 +/*
  2 + * Copyright © 2016-2018 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +import 'brace/ext/language_tools';
  17 +import 'brace/mode/json';
  18 +import 'brace/theme/github';
  19 +
  20 +import './extension-form.scss';
  21 +
  22 +/* eslint-disable angular/log */
  23 +
  24 +import extensionFormModbusTemplate from './extension-form-modbus.tpl.html';
  25 +
  26 +/* eslint-enable import/no-unresolved, import/default */
  27 +
  28 +/*@ngInject*/
  29 +export default function ExtensionFormModbusDirective($compile, $templateCache, $translate, types) {
  30 +
  31 +
  32 + var linker = function(scope, element) {
  33 +
  34 +
  35 + function Server() {
  36 + this.transport = {
  37 + "type": "tcp",
  38 + "host": "localhost",
  39 + "port": 502,
  40 + "timeout": 3000
  41 + };
  42 + this.devices = []
  43 + }
  44 +
  45 + function Device() {
  46 + this.unitId = 1;
  47 + this.deviceName = "";
  48 + this.attributesPollPeriod = 1000;
  49 + this.timeseriesPollPeriod = 1000;
  50 + this.attributes = [];
  51 + this.timeseries = [];
  52 + }
  53 +
  54 + function Tag(globalPollPeriod) {
  55 + this.tag = "";
  56 + this.type = "long";
  57 + this.pollPeriod = globalPollPeriod;
  58 + this.functionCode = 3;
  59 + this.address = 0;
  60 + this.registerCount = 1;
  61 + this.bit = 0;
  62 + this.byteOrder = "BIG";
  63 + }
  64 +
  65 +
  66 + var template = $templateCache.get(extensionFormModbusTemplate);
  67 + element.html(template);
  68 +
  69 + scope.types = types;
  70 + scope.theForm = scope.$parent.theForm;
  71 +
  72 +
  73 + if (!scope.configuration.servers.length) {
  74 + scope.configuration.servers.push(new Server());
  75 + }
  76 +
  77 + scope.addServer = function(serversList) {
  78 + serversList.push(new Server());
  79 + scope.theForm.$setDirty();
  80 + };
  81 +
  82 + scope.addDevice = function(deviceList) {
  83 + deviceList.push(new Device());
  84 + scope.theForm.$setDirty();
  85 + };
  86 +
  87 + scope.addNewAttribute = function(device) {
  88 + device.attributes.push(new Tag(device.attributesPollPeriod));
  89 + scope.theForm.$setDirty();
  90 + };
  91 +
  92 + scope.addNewTimeseries = function(device) {
  93 + device.timeseries.push(new Tag(device.timeseriesPollPeriod));
  94 + scope.theForm.$setDirty();
  95 + };
  96 +
  97 + scope.removeItem = (item, itemList) => {
  98 + var index = itemList.indexOf(item);
  99 + if (index > -1) {
  100 + itemList.splice(index, 1);
  101 + }
  102 + scope.theForm.$setDirty();
  103 + };
  104 +
  105 +
  106 + $compile(element.contents())(scope);
  107 +
  108 +
  109 + scope.collapseValidation = function(index, id) {
  110 + var invalidState = angular.element('#'+id+':has(.ng-invalid)');
  111 + if(invalidState.length) {
  112 + invalidState.addClass('inner-invalid');
  113 + }
  114 + };
  115 +
  116 + scope.expandValidation = function (index, id) {
  117 + var invalidState = angular.element('#'+id);
  118 + invalidState.removeClass('inner-invalid');
  119 + };
  120 +
  121 + };
  122 +
  123 + return {
  124 + restrict: "A",
  125 + link: linker,
  126 + scope: {
  127 + configuration: "=",
  128 + isAdd: "="
  129 + }
  130 + }
  131 +}
\ No newline at end of file
... ...
  1 +<!--
  2 +
  3 + Copyright © 2016-2018 The Thingsboard Authors
  4 +
  5 + Licensed under the Apache License, Version 2.0 (the "License");
  6 + you may not use this file except in compliance with the License.
  7 + You may obtain a copy of the License at
  8 +
  9 + http://www.apache.org/licenses/LICENSE-2.0
  10 +
  11 + Unless required by applicable law or agreed to in writing, software
  12 + distributed under the License is distributed on an "AS IS" BASIS,
  13 + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14 + See the License for the specific language governing permissions and
  15 + limitations under the License.
  16 +
  17 +-->
  18 +<md-card class="extension-form extension-modbus">
  19 + <md-card-title>
  20 + <md-card-title-text>
  21 + <span translate class="md-headline">extension.configuration</span>
  22 + </md-card-title-text>
  23 + </md-card-title>
  24 +
  25 + <md-card-content>
  26 + <v-accordion id="modbus-server-configs-accordion" class="vAccordion--default" onexpand="expandValidation(index, id)" oncollapse="collapseValidation(index, id)">
  27 + <v-pane id="modbus-servers-pane" expanded="true">
  28 + <v-pane-header>
  29 + {{ 'extension.modbus-server' | translate }}
  30 + </v-pane-header>
  31 +
  32 + <v-pane-content>
  33 + <div ng-if="configuration.servers.length === 0">
  34 + <span translate layout-align="center center" class="tb-prompt">extension.modbus-add-server-prompt</span>
  35 + </div>
  36 +
  37 + <div ng-if="configuration.servers.length > 0">
  38 + <ol class="list-group">
  39 + <li class="list-group-item" ng-repeat="(serverIndex, server) in configuration.servers">
  40 + <md-button aria-label="{{ 'action.remove' | translate }}"
  41 + class="md-icon-button"
  42 + ng-click="removeItem(server, configuration.servers)"
  43 + ng-hide="configuration.servers.length < 2"
  44 + >
  45 + <ng-md-icon icon="close" aria-label="{{ 'action.remove' | translate }}"></ng-md-icon>
  46 + <md-tooltip md-direction="top">
  47 + {{ 'action.remove' | translate }}
  48 + </md-tooltip>
  49 + </md-button>
  50 +
  51 + <md-card>
  52 + <md-card-content>
  53 +
  54 + <div layout="row">
  55 +
  56 + <md-input-container flex="50" class="md-block tb-container-for-select">
  57 + <label translate>extension.modbus-transport</label>
  58 + <md-select required
  59 + name="transportType_{{serverIndex}}"
  60 + ng-model="server.transport.type"
  61 + >
  62 + <md-option ng-value="transportType"
  63 + ng-repeat="(transportType, transportValue) in types.extensionModbusTransports"
  64 + ><span ng-bind="transportValue"></span></md-option>
  65 + </md-select>
  66 + <div ng-messages="theForm['transportType_' + serverIndex].$error">
  67 + <div translate
  68 + ng-message="required"
  69 + >extension.field-required</div>
  70 + </div>
  71 + </md-input-container>
  72 +
  73 + </div>
  74 +
  75 + <div layout="row" ng-if="server.transport.type == 'tcp'">
  76 + <md-input-container flex="33" class="md-block">
  77 + <label translate>extension.host</label>
  78 + <input required name="transportHost_{{serverIndex}}" ng-model="server.transport.host">
  79 + <div ng-messages="theForm['transportHost_' + serverIndex].$error">
  80 + <div translate ng-message="required">extension.field-required</div>
  81 + </div>
  82 + </md-input-container>
  83 +
  84 + <md-input-container flex="33" class="md-block">
  85 + <label translate>extension.port</label>
  86 + <input type="number"
  87 + required
  88 + name="transportPort_{{serverIndex}}"
  89 + ng-model="server.transport.port"
  90 + min="1"
  91 + max="65535"
  92 + >
  93 + <div ng-messages="theForm['transportPort_' + serverIndex].$error">
  94 + <div translate
  95 + ng-message="required"
  96 + >extension.field-required</div>
  97 + <div translate
  98 + ng-message="min"
  99 + >extension.port-range</div>
  100 + <div translate
  101 + ng-message="max"
  102 + >extension.port-range</div>
  103 + </div>
  104 + </md-input-container>
  105 +
  106 + <md-input-container flex="33" class="md-block">
  107 + <label translate>extension.timeout</label>
  108 + <input type="number"
  109 + required name="transportTimeout_{{serverIndex}}"
  110 + ng-model="server.transport.timeout"
  111 + >
  112 + <div ng-messages="theForm['transportTimeout_' + serverIndex].$error">
  113 + <div translate
  114 + ng-message="required"
  115 + >extension.field-required</div>
  116 + </div>
  117 + </md-input-container>
  118 + </div>
  119 +
  120 + <div layout="row" ng-if="server.transport.type == 'udp'">
  121 + <md-input-container flex="33" class="md-block">
  122 + <label translate>extension.host</label>
  123 + <input required name="transportHost_{{serverIndex}}" ng-model="server.transport.host">
  124 + <div ng-messages="theForm['transportHost_' + serverIndex].$error">
  125 + <div translate ng-message="required">extension.field-required</div>
  126 + </div>
  127 + </md-input-container>
  128 +
  129 + <md-input-container flex="33" class="md-block">
  130 + <label translate>extension.port</label>
  131 + <input type="number"
  132 + required
  133 + name="transportPort_{{serverIndex}}"
  134 + ng-model="server.transport.port"
  135 + min="1"
  136 + max="65535"
  137 + >
  138 + <div ng-messages="theForm['transportPort_' + serverIndex].$error">
  139 + <div translate
  140 + ng-message="required"
  141 + >extension.field-required</div>
  142 + <div translate
  143 + ng-message="min"
  144 + >extension.port-range</div>
  145 + <div translate
  146 + ng-message="max"
  147 + >extension.port-range</div>
  148 + </div>
  149 + </md-input-container>
  150 +
  151 + <md-input-container flex="33" class="md-block">
  152 + <label translate>extension.timeout</label>
  153 + <input type="number"
  154 + required name="transportTimeout_{{serverIndex}}"
  155 + ng-model="server.transport.timeout"
  156 + >
  157 + <div ng-messages="theForm['transportTimeout_' + serverIndex].$error">
  158 + <div translate
  159 + ng-message="required"
  160 + >extension.field-required</div>
  161 + </div>
  162 + </md-input-container>
  163 + </div>
  164 +
  165 + <div ng-if="server.transport.type == 'rtu'">
  166 +
  167 + <div layout="row">
  168 + <md-input-container flex="70" class="md-block">
  169 + <label translate>extension.modbus-port-name</label>
  170 + <input required name="transportPortName_{{serverIndex}}" ng-model="server.transport.portName">
  171 + <div ng-messages="theForm['transportPortName_' + serverIndex].$error">
  172 + <div translate ng-message="required">extension.field-required</div>
  173 + </div>
  174 + </md-input-container>
  175 +
  176 + <md-input-container flex="30" class="md-block">
  177 + <label translate>extension.timeout</label>
  178 + <input type="number"
  179 + required name="transportTimeout_{{serverIndex}}"
  180 + ng-model="server.transport.timeout"
  181 + >
  182 + <div ng-messages="theForm['transportTimeout_' + serverIndex].$error">
  183 + <div translate
  184 + ng-message="required"
  185 + >extension.field-required</div>
  186 + </div>
  187 + </md-input-container>
  188 + </div>
  189 +
  190 + <div layout="row">
  191 + <md-input-container flex="50" class="md-block tb-container-for-select">
  192 + <label translate>extension.modbus-encoding</label>
  193 + <md-select required
  194 + name="transportEncoding_{{serverIndex}}"
  195 + ng-model="server.transport.encoding"
  196 + >
  197 + <md-option ng-value="encodingType"
  198 + ng-repeat="(encodingType, encodingValue) in types.extensionModbusRtuEncodings"
  199 + ><span ng-bind="encodingValue"></span></md-option>
  200 + </md-select>
  201 + <div ng-messages="theForm['transportEncoding_' + serverIndex].$error">
  202 + <div translate
  203 + ng-message="required"
  204 + >extension.field-required</div>
  205 + </div>
  206 + </md-input-container>
  207 +
  208 + <md-input-container flex="50" class="md-block tb-container-for-select">
  209 + <label translate>extension.modbus-parity</label>
  210 + <md-select name="transportParity_{{serverIndex}}" ng-model="server.transport.parity">
  211 + <md-option ng-repeat="(parityKey, parityValue) in types.extensionModbusRtuParities"
  212 + ng-value="parityValue"
  213 + >
  214 + {{parityValue}}
  215 + </md-option>
  216 + </md-select>
  217 + <div ng-messages="theForm['transportParity_' + serverIndex].$error">
  218 + <div translate
  219 + ng-message="required"
  220 + >extension.field-required</div>
  221 + </div>
  222 + </md-input-container>
  223 +
  224 + </div>
  225 +
  226 + <div layout="row">
  227 + <md-input-container flex="33" class="md-block">
  228 + <label translate>extension.modbus-baudrate</label>
  229 + <input type="number"
  230 + required name="transportBaudRate_{{serverIndex}}"
  231 + ng-model="server.transport.baudRate"
  232 + >
  233 + <div ng-messages="theForm['transportBaudRate_' + serverIndex].$error">
  234 + <div translate
  235 + ng-message="required"
  236 + >extension.field-required</div>
  237 + </div>
  238 + </md-input-container>
  239 +
  240 + <md-input-container flex="33" class="md-block">
  241 + <label translate>extension.modbus-databits</label>
  242 + <input type="number"
  243 + required
  244 + name="transportDataBits_{{serverIndex}}"
  245 + ng-model="server.transport.dataBits"
  246 + min="7"
  247 + max="8"
  248 + >
  249 + <div ng-messages="theForm['transportDataBits_' + serverIndex].$error">
  250 + <div translate
  251 + ng-message="required"
  252 + >extension.field-required</div>
  253 + <div translate
  254 + ng-message="min"
  255 + >extension.modbus-databits-range</div>
  256 + <div translate
  257 + ng-message="max"
  258 + >extension.modbus-databits-range</div>
  259 + </div>
  260 + </md-input-container>
  261 +
  262 + <md-input-container flex="33" class="md-block">
  263 + <label translate>extension.modbus-stopbits</label>
  264 + <input type="number"
  265 + required
  266 + name="transportStopBits_{{serverIndex}}"
  267 + ng-model="server.transport.stopBits"
  268 + min="1"
  269 + max="2"
  270 + >
  271 + <div ng-messages="theForm['transportStopBits_' + serverIndex].$error">
  272 + <div translate
  273 + ng-message="required"
  274 + >extension.field-required</div>
  275 + <div translate
  276 + ng-message="min"
  277 + >extension.modbus-stopbits-range</div>
  278 + <div translate
  279 + ng-message="max"
  280 + >extension.modbus-stopbits-range</div>
  281 + </div>
  282 + </md-input-container>
  283 + </div>
  284 + </div>
  285 +
  286 + <v-accordion id="modbus-mapping-accordion"
  287 + class="vAccordion--default"
  288 + onexpand="expandValidation(index, id)" oncollapse="collapseValidation(index, id)">
  289 + <v-pane id="modbus-mapping-pane_{{serverIndex}}">
  290 + <v-pane-header>
  291 + {{ 'extension.mapping' | translate }}
  292 + </v-pane-header>
  293 + <v-pane-content>
  294 + <div ng-if="server.devices.length > 0">
  295 + <ol class="list-group">
  296 + <li class="list-group-item"
  297 + ng-repeat="(deviceIndex, device) in server.devices"
  298 + >
  299 + <md-button aria-label="{{ 'action.remove' | translate }}"
  300 + class="md-icon-button"
  301 + ng-click="removeItem(device, server.devices)"
  302 + >
  303 + <ng-md-icon icon="close" aria-label="{{ 'action.remove' | translate }}"></ng-md-icon>
  304 + <md-tooltip md-direction="top">
  305 + {{ 'action.remove' | translate }}
  306 + </md-tooltip>
  307 + </md-button>
  308 +
  309 + <md-card>
  310 + <md-card-content>
  311 + <div flex layout="row">
  312 + <md-input-container flex="80" class="md-block">
  313 + <label translate>extension.modbus-device-name</label>
  314 + <input required
  315 + name="deviceName_{{serverIndex}}{{deviceIndex}}"
  316 + ng-model="device.deviceName"
  317 + >
  318 + <div ng-messages="theForm['deviceName_' + serverIndex + deviceIndex].$error">
  319 + <div translate
  320 + ng-message="required"
  321 + >extension.field-required</div>
  322 + </div>
  323 + </md-input-container>
  324 +
  325 + <md-input-container flex="20" class="md-block">
  326 + <label translate>extension.modbus-unit-id</label>
  327 + <input type="number"
  328 + required
  329 + name="unitId_{{serverIndex}}{{deviceIndex}}"
  330 + ng-model="device.unitId"
  331 + min="1"
  332 + max="247"
  333 + >
  334 +
  335 + <div ng-messages="theForm['unitId_' + serverIndex + deviceIndex].$error">
  336 + <div translate
  337 + ng-message="required"
  338 + >extension.field-required</div>
  339 + <div translate
  340 + ng-message="min"
  341 + >extension.modbus-unit-id-range</div>
  342 + <div translate
  343 + ng-message="max"
  344 + >extension.modbus-unit-id-range</div>
  345 + </div>
  346 + </md-input-container>
  347 + </div>
  348 +
  349 + <div flex layout="row">
  350 + <md-input-container flex="50" class="md-block">
  351 + <label translate>extension.modbus-attributes-poll-period</label>
  352 + <input type="number"
  353 + required
  354 + name="attributesPollPeriod_{{serverIndex}}{{deviceIndex}}"
  355 + ng-model="device.attributesPollPeriod"
  356 + min="1"
  357 + >
  358 +
  359 + <div ng-messages="theForm['attributesPollPeriod_' + serverIndex + deviceIndex].$error">
  360 + <div translate
  361 + ng-message="required"
  362 + >extension.field-required</div>
  363 + <div translate
  364 + ng-message="min"
  365 + >extension.modbus-poll-period-range</div>
  366 + </div>
  367 + </md-input-container>
  368 +
  369 + <md-input-container flex="50" class="md-block">
  370 + <label translate>extension.modbus-timeseries-poll-period</label>
  371 + <input type="number"
  372 + required
  373 + name="timeseriesPollPeriod_{{serverIndex}}{{deviceIndex}}"
  374 + ng-model="device.timeseriesPollPeriod"
  375 + min="1"
  376 + >
  377 +
  378 + <div ng-messages="theForm['timeseriesPollPeriod_' + serverIndex + deviceIndex].$error">
  379 + <div translate
  380 + ng-message="required"
  381 + >extension.field-required</div>
  382 + <div translate
  383 + ng-message="min"
  384 + >extension.modbus-poll-period-range</div>
  385 + </div>
  386 + </md-input-container>
  387 +
  388 + </div>
  389 +
  390 +
  391 + <v-accordion id="modbus-attributes-accordion"
  392 + class="vAccordion--default"
  393 + onexpand="expandValidation(index, id)" oncollapse="collapseValidation(index, id)">
  394 + <v-pane id="modbus-attributes-pane_{{serverIndex}}{{deviceIndex}}">
  395 + <v-pane-header>
  396 + {{ 'extension.attributes' | translate }}
  397 + </v-pane-header>
  398 + <v-pane-content>
  399 + <div ng-show="device.attributes.length > 0">
  400 + <ol class="list-group">
  401 + <li class="list-group-item"
  402 + ng-repeat="(attributeIndex, attribute) in device.attributes"
  403 + >
  404 + <md-button aria-label="{{ 'action.remove' | translate }}"
  405 + class="md-icon-button"
  406 + ng-click="removeItem(attribute, device.attributes)">
  407 + <ng-md-icon icon="close"
  408 + aria-label="{{ 'action.remove' | translate }}"
  409 + ></ng-md-icon>
  410 + <md-tooltip md-direction="top">
  411 + {{ 'action.remove' | translate }}
  412 + </md-tooltip>
  413 + </md-button>
  414 + <md-card>
  415 + <md-card-content>
  416 +
  417 + <section flex layout="row">
  418 + <md-input-container flex="50" class="md-block">
  419 + <label translate>extension.modbus-tag</label>
  420 + <input required
  421 + name="modbusAttributeTag_{{serverIndex}}{{deviceIndex}}{{attributeIndex}}"
  422 + ng-model="attribute.tag"
  423 + >
  424 + <div ng-messages="theForm['modbusAttributeTag_' + serverIndex + deviceIndex + attributeIndex].$error">
  425 + <div translate
  426 + ng-message="required"
  427 + >extension.field-required</div>
  428 + </div>
  429 + </md-input-container>
  430 +
  431 + <md-input-container flex="30" class="md-block tb-container-for-select">
  432 + <label translate>extension.type</label>
  433 + <md-select required name="modbusAttributeType_{{serverIndex}}{{deviceIndex}}{{attributeIndex}}"
  434 + ng-model="attribute.type"
  435 + >
  436 + <md-option ng-repeat="(attrType, attrTypeValue) in types.extensionValueType"
  437 + ng-value="attrType"
  438 + >
  439 + {{attrTypeValue | translate}}
  440 + </md-option>
  441 + </md-select>
  442 + <div ng-messages="theForm['modbusAttributeType_' + serverIndex + deviceIndex + attributeIndex].$error">
  443 + <div translate
  444 + ng-message="required"
  445 + >extension.field-required</div>
  446 + </div>
  447 + </md-input-container>
  448 +
  449 + <md-input-container flex="20" class="md-block">
  450 + <label translate>extension.modbus-poll-period</label>
  451 + <input type="number"
  452 + name="pollPeriod_{{serverIndex}}{{deviceIndex}}"
  453 + ng-model="attribute.pollPeriod"
  454 + min="1"
  455 + >
  456 +
  457 + <div ng-messages="theForm['pollPeriod_' + serverIndex + deviceIndex].$error">
  458 + <div translate
  459 + ng-message="min"
  460 + >extension.modbus-poll-period-range</div>
  461 + </div>
  462 + </md-input-container>
  463 + </section>
  464 +
  465 + <section flex layout="row">
  466 + <md-input-container flex="50" class="md-block tb-container-for-select">
  467 + <label translate>extension.type</label>
  468 + <md-select required name="modbusAttributeFunctionCode_{{serverIndex}}{{deviceIndex}}{{attributeIndex}}"
  469 + ng-model="attribute.functionCode"
  470 + >
  471 + <md-option ng-repeat="(functionCode, functionName) in types.extensionModbusFunctionCodes"
  472 + ng-value="functionCode"
  473 + >
  474 + {{functionName}}
  475 + </md-option>
  476 + </md-select>
  477 + <div ng-messages="theForm['modbusAttributeFunctionCode_' + serverIndex + deviceIndex + attributeIndex].$error">
  478 + <div translate
  479 + ng-message="required"
  480 + >extension.field-required</div>
  481 + </div>
  482 + </md-input-container>
  483 +
  484 + <md-input-container flex="50" class="md-block">
  485 + <label translate>extension.modbus-register-address</label>
  486 + <input type="number"
  487 + required
  488 + name="address_{{serverIndex}}{{deviceIndex}}"
  489 + ng-model="attribute.address"
  490 + min="0"
  491 + max="65535"
  492 + >
  493 +
  494 + <div ng-messages="theForm['address_' + serverIndex + deviceIndex].$error">
  495 + <div translate
  496 + ng-message="required"
  497 + >extension.field-required</div>
  498 + <div translate
  499 + ng-message="min"
  500 + >extension.modbus-register-address-range</div>
  501 + <div translate
  502 + ng-message="max"
  503 + >extension.modbus-register-address-range</div>
  504 + </div>
  505 + </md-input-container>
  506 +
  507 + </section>
  508 +
  509 + <section flex layout="row">
  510 + <md-input-container flex="50" class="md-block" ng-if="attribute.type != 'boolean'">
  511 + <label translate>extension.modbus-register-count</label>
  512 + <input type="number"
  513 + name="registerCount_{{serverIndex}}{{deviceIndex}}"
  514 + ng-model="attribute.registerCount"
  515 + min="1"
  516 + >
  517 +
  518 + <div ng-messages="theForm['registerCount_' + serverIndex + deviceIndex].$error">
  519 + <div translate
  520 + ng-message="min"
  521 + >extension.modbus-register-count-range</div>
  522 + </div>
  523 + </md-input-container>
  524 +
  525 + <md-input-container flex="50" class="md-block" ng-if="attribute.type == 'boolean' && attribute.functionCode >= 3">
  526 + <label translate>extension.modbus-register-bit-index</label>
  527 + <input type="number"
  528 + required
  529 + name="bit_{{serverIndex}}{{deviceIndex}}"
  530 + ng-model="attribute.bit"
  531 + min="0"
  532 + max="15"
  533 + >
  534 +
  535 + <div ng-messages="theForm['bit_' + serverIndex + deviceIndex].$error">
  536 + <div translate
  537 + ng-message="required"
  538 + >extension.field-required</div>
  539 + <div translate
  540 + ng-message="min"
  541 + >extension.modbus-register-bit-index-range</div>
  542 + <div translate
  543 + ng-message="max"
  544 + >extension.modbus-register-bit-index-range</div>
  545 + </div>
  546 + </md-input-container>
  547 + </section>
  548 +
  549 + <section flex layout="row">
  550 + <md-input-container flex="100" class="md-block" ng-if="attribute.functionCode >= 3">
  551 + <label translate>extension.modbus-byte-order</label>
  552 + <input required
  553 + name="modbusByteOrder_{{serverIndex}}{{deviceIndex}}{{attributeIndex}}"
  554 + ng-model="attribute.byteOrder"
  555 + >
  556 + <div ng-messages="theForm['modbusByteOrder_' + serverIndex + deviceIndex + attributeIndex].$error">
  557 + <div translate
  558 + ng-message="required"
  559 + >extension.field-required</div>
  560 + </div>
  561 + </md-input-container>
  562 +
  563 + </section>
  564 +
  565 +
  566 + </md-card-content>
  567 + </md-card>
  568 + </li>
  569 + </ol>
  570 + </div>
  571 + <div flex layout="row" layout-align="start center">
  572 + <md-button class="md-primary md-raised"
  573 + ng-click="addNewAttribute(device)"
  574 + aria-label="{{ 'action.add' | translate }}"
  575 + >
  576 + <md-icon class="material-icons">add</md-icon>
  577 + <span translate>extension.add-attribute</span>
  578 + </md-button>
  579 + </div>
  580 + </v-pane-content>
  581 + </v-pane>
  582 + </v-accordion>
  583 +
  584 + <v-accordion id="modbus-timeseries-accordion" class="vAccordion--default" onexpand="expandValidation(index, id)" oncollapse="collapseValidation(index, id)">
  585 + <v-pane id="modbus-timeseries-pane_{{serverIndex}}{{deviceIndex}}">
  586 + <v-pane-header>
  587 + {{ 'extension.timeseries' | translate }}
  588 + </v-pane-header>
  589 + <v-pane-content>
  590 + <div ng-show="device.timeseries.length > 0">
  591 + <ol class="list-group">
  592 + <li class="list-group-item"
  593 + ng-repeat="(timeseriesIndex, timeserie) in device.timeseries"
  594 + >
  595 + <md-button aria-label="{{ 'action.remove' | translate }}"
  596 + class="md-icon-button"
  597 + ng-click="removeItem(timeserie, device.timeseries)">
  598 + <ng-md-icon icon="close"
  599 + aria-label="{{ 'action.remove' | translate }}"
  600 + ></ng-md-icon>
  601 + <md-tooltip md-direction="top">
  602 + {{ 'action.remove' | translate }}
  603 + </md-tooltip>
  604 + </md-button>
  605 + <md-card>
  606 + <md-card-content>
  607 +
  608 + <section flex layout="row">
  609 + <md-input-container flex="50" class="md-block">
  610 + <label translate>extension.modbus-tag</label>
  611 + <input required
  612 + name="modbusTimeserieTag_{{serverIndex}}{{deviceIndex}}{{timeseriesIndex}}"
  613 + ng-model="timeserie.tag"
  614 + >
  615 + <div ng-messages="theForm['modbusTimeserieTag_' + serverIndex + deviceIndex + timeseriesIndex].$error">
  616 + <div translate
  617 + ng-message="required"
  618 + >extension.field-required</div>
  619 + </div>
  620 + </md-input-container>
  621 +
  622 + <md-input-container flex="30" class="md-block tb-container-for-select">
  623 + <label translate>extension.type</label>
  624 + <md-select required name="modbusTimeserieType_{{serverIndex}}{{deviceIndex}}{{timeseriesIndex}}"
  625 + ng-model="timeserie.type"
  626 + >
  627 + <md-option ng-repeat="(attrType, attrTypeValue) in types.extensionValueType"
  628 + ng-value="attrType"
  629 + >
  630 + {{attrTypeValue | translate}}
  631 + </md-option>
  632 + </md-select>
  633 + <div ng-messages="theForm['modbusTimeserieType_' + serverIndex + deviceIndex + timeseriesIndex].$error">
  634 + <div translate
  635 + ng-message="required"
  636 + >extension.field-required</div>
  637 + </div>
  638 + </md-input-container>
  639 +
  640 + <md-input-container flex="20" class="md-block">
  641 + <label translate>extension.modbus-poll-period</label>
  642 + <input type="number"
  643 + name="pollPeriod_{{serverIndex}}{{deviceIndex}}"
  644 + ng-model="timeserie.pollPeriod"
  645 + min="1"
  646 + >
  647 +
  648 + <div ng-messages="theForm['pollPeriod_' + serverIndex + deviceIndex].$error">
  649 + <div translate
  650 + ng-message="min"
  651 + >extension.modbus-poll-period-range</div>
  652 + </div>
  653 + </md-input-container>
  654 + </section>
  655 +
  656 + <section flex layout="row">
  657 + <md-input-container flex="50" class="md-block tb-container-for-select">
  658 + <label translate>extension.type</label>
  659 + <md-select required name="modbusTimeserieFunctionCode_{{serverIndex}}{{deviceIndex}}{{timeseriesIndex}}"
  660 + ng-model="timeserie.functionCode"
  661 + >
  662 + <md-option ng-repeat="(functionCode, functionName) in types.extensionModbusFunctionCodes"
  663 + ng-value="functionCode"
  664 + >
  665 + {{functionName}}
  666 + </md-option>
  667 + </md-select>
  668 + <div ng-messages="theForm['modbusTimeserieFunctionCode_' + serverIndex + deviceIndex + timeseriesIndex].$error">
  669 + <div translate
  670 + ng-message="required"
  671 + >extension.field-required</div>
  672 + </div>
  673 + </md-input-container>
  674 +
  675 + <md-input-container flex="50" class="md-block">
  676 + <label translate>extension.modbus-register-address</label>
  677 + <input type="number"
  678 + required
  679 + name="address_{{serverIndex}}{{deviceIndex}}"
  680 + ng-model="timeserie.address"
  681 + min="0"
  682 + max="65535"
  683 + >
  684 +
  685 + <div ng-messages="theForm['address_' + serverIndex + deviceIndex].$error">
  686 + <div translate
  687 + ng-message="required"
  688 + >extension.field-required</div>
  689 + <div translate
  690 + ng-message="min"
  691 + >extension.modbus-register-address-range</div>
  692 + <div translate
  693 + ng-message="max"
  694 + >extension.modbus-register-address-range</div>
  695 + </div>
  696 + </md-input-container>
  697 + </section>
  698 +
  699 + <section flex layout="row">
  700 + <md-input-container flex="50" class="md-block" ng-if="timeserie.type != 'boolean'">
  701 + <label translate>extension.modbus-register-count</label>
  702 + <input type="number"
  703 + name="registerCount_{{serverIndex}}{{deviceIndex}}"
  704 + ng-model="timeserie.registerCount"
  705 + min="1"
  706 + >
  707 +
  708 + <div ng-messages="theForm['registerCount_' + serverIndex + deviceIndex].$error">
  709 + <div translate
  710 + ng-message="min"
  711 + >extension.modbus-register-count-range</div>
  712 + </div>
  713 + </md-input-container>
  714 +
  715 + <md-input-container flex="50" class="md-block" ng-if="timeserie.type == 'boolean' && timeserie.functionCode >= 3">
  716 + <label translate>extension.modbus-register-bit-index</label>
  717 + <input type="number"
  718 + required
  719 + name="bit_{{serverIndex}}{{deviceIndex}}"
  720 + ng-model="timeserie.bit"
  721 + min="0"
  722 + max="15"
  723 + >
  724 +
  725 + <div ng-messages="theForm['bit_' + serverIndex + deviceIndex].$error">
  726 + <div translate
  727 + ng-message="required"
  728 + >extension.field-required</div>
  729 + <div translate
  730 + ng-message="min"
  731 + >extension.modbus-register-bit-index-range</div>
  732 + <div translate
  733 + ng-message="max"
  734 + >extension.modbus-register-bit-index-range</div>
  735 + </div>
  736 + </md-input-container>
  737 + </section>
  738 +
  739 + <section flex layout="row">
  740 + <md-input-container flex="100" class="md-block" ng-if="timeserie.functionCode >= 3">
  741 + <label translate>extension.modbus-byte-order</label>
  742 + <input required
  743 + name="modbusByteOrder_{{serverIndex}}{{deviceIndex}}{{timeseriesIndex}}"
  744 + ng-model="timeserie.byteOrder"
  745 + >
  746 + <div ng-messages="theForm['modbusByteOrder_' + serverIndex + deviceIndex + timeseriesIndex].$error">
  747 + <div translate
  748 + ng-message="required"
  749 + >extension.field-required</div>
  750 + </div>
  751 + </md-input-container>
  752 +
  753 + </section>
  754 + </md-card-content>
  755 + </md-card>
  756 + </li>
  757 + </ol>
  758 + </div>
  759 + <div flex layout="row" layout-align="start center">
  760 + <md-button class="md-primary md-raised"
  761 + ng-click="addNewTimeseries(device)"
  762 + aria-label="{{ 'action.add' | translate }}"
  763 + >
  764 + <md-icon class="material-icons">add</md-icon>
  765 + <span translate>extension.add-timeseries</span>
  766 + </md-button>
  767 + </div>
  768 + </v-pane-content>
  769 + </v-pane>
  770 + </v-accordion>
  771 +
  772 +
  773 + </md-card-content>
  774 + </md-card>
  775 + </li>
  776 + </ol>
  777 + </div>
  778 + <div flex
  779 + layout="row"
  780 + layout-align="start center"
  781 + >
  782 + <md-button class="md-primary md-raised"
  783 + ng-click="addDevice(server.devices)"
  784 + aria-label="{{ 'action.add' | translate }}"
  785 + >
  786 + <md-icon class="material-icons">add</md-icon>
  787 + <span translate>extension.add-device</span>
  788 + </md-button>
  789 + </div>
  790 + </v-pane-content>
  791 + </v-pane>
  792 + </v-accordion>
  793 +
  794 + </md-card-content>
  795 + </md-card>
  796 + </li>
  797 + </ol>
  798 +
  799 + <div flex
  800 + layout="row"
  801 + layout-align="start center"
  802 + >
  803 + <md-button class="md-primary md-raised"
  804 + ng-click="addServer(configuration.servers)"
  805 + aria-label="{{ 'action.add' | translate }}"
  806 + >
  807 + <md-icon class="material-icons">add</md-icon>
  808 + <span translate>extension.modbus-add-server</span>
  809 + </md-button>
  810 + </div>
  811 +
  812 + </div>
  813 + </v-pane-content>
  814 + </v-pane>
  815 + </v-accordion>
  816 + <!--{{config}}-->
  817 + </md-card-content>
  818 +</md-card>
\ No newline at end of file
... ...
... ... @@ -17,6 +17,8 @@ import ExtensionTableDirective from './extension-table.directive';
17 17 import ExtensionFormHttpDirective from './extensions-forms/extension-form-http.directive';
18 18 import ExtensionFormMqttDirective from './extensions-forms/extension-form-mqtt.directive'
19 19 import ExtensionFormOpcDirective from './extensions-forms/extension-form-opc.directive';
  20 +import ExtensionFormModbusDirective from './extensions-forms/extension-form-modbus.directive';
  21 +
20 22 import {ParseToNull} from './extension-dialog.controller';
21 23
22 24 export default angular.module('thingsboard.extension', [])
... ... @@ -24,5 +26,6 @@ export default angular.module('thingsboard.extension', [])
24 26 .directive('tbExtensionFormHttp', ExtensionFormHttpDirective)
25 27 .directive('tbExtensionFormMqtt', ExtensionFormMqttDirective)
26 28 .directive('tbExtensionFormOpc', ExtensionFormOpcDirective)
  29 + .directive('tbExtensionFormModbus', ExtensionFormModbusDirective)
27 30 .directive('parseToNull', ParseToNull)
28 31 .name;
\ No newline at end of file
... ...
... ... @@ -866,8 +866,10 @@ export default angular.module('thingsboard.locale', [])
866 866 "response-timeout": "Response timeout in milliseconds",
867 867 "topic-expression": "Topic expression",
868 868 "client-scope": "Client scope",
  869 + "add-device": "Add device",
869 870 "opc-server": "Servers",
870 871 "opc-add-server": "Add server",
  872 + "opc-add-server-prompt": "Please add server",
871 873 "opc-application-name": "Application name",
872 874 "opc-application-uri": "Application uri",
873 875 "opc-scan-period-in-seconds": "Scan period in seconds",
... ... @@ -882,6 +884,34 @@ export default angular.module('thingsboard.locale', [])
882 884 "opc-keystore-key-password":"Key password",
883 885 "opc-device-node-pattern":"Device node pattern",
884 886 "opc-device-name-pattern":"Device name pattern",
  887 + "modbus-server": "Servers/slaves",
  888 + "modbus-add-server": "Add server/slave",
  889 + "modbus-add-server-prompt": "Please add server/slave",
  890 + "modbus-transport": "Transport",
  891 + "modbus-port-name": "Serial port name",
  892 + "modbus-encoding": "Encoding",
  893 + "modbus-parity": "Parity",
  894 + "modbus-baudrate": "Baud rate",
  895 + "modbus-databits": "Data bits",
  896 + "modbus-stopbits": "Stop bits",
  897 + "modbus-databits-range": "Data bits should be in a range from 7 to 8.",
  898 + "modbus-stopbits-range": "Stop bits should be in a range from 1 to 2.",
  899 + "modbus-unit-id": "Unit ID",
  900 + "modbus-unit-id-range": "Unit ID should be in a range from 1 to 247.",
  901 + "modbus-device-name":"Device name",
  902 + "modbus-poll-period": "Poll period (ms)",
  903 + "modbus-attributes-poll-period": "Attributes poll period (ms)",
  904 + "modbus-timeseries-poll-period": "Timeseries poll period (ms)",
  905 + "modbus-poll-period-range": "Poll period should be positive value.",
  906 + "modbus-tag": "Tag",
  907 + "modbus-function": "Function",
  908 + "modbus-register-address": "Register address",
  909 + "modbus-register-address-range": "Register address should be in a range from 0 to 65535.",
  910 + "modbus-register-bit-index": "Bit index",
  911 + "modbus-register-bit-index-range": "Bit index should be in a range from 0 to 15.",
  912 + "modbus-register-count": "Register count",
  913 + "modbus-register-count-range": "Register count should be a positive value.",
  914 + "modbus-byte-order": "Byte order",
885 915
886 916 "sync": {
887 917 "status": "Status",
... ...