You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1092 lines
44 KiB

<html>
<link rel="stylesheet" type="text/css" href="../../lib/css/themes/jquery-ui/redmond/jquery-ui.min.css"/>
<script type="text/javascript" src="../../lib/js/jquery-1.11.1.min.js"></script>
<script type="text/javascript" src="../../lib/js/jquery-ui.min.js"></script>
<script type="text/javascript" src="../../socket.io/socket.io.js"></script>
<link rel="stylesheet" type="text/css" href="./lib/css/jsgrid.css"/>
<link rel="stylesheet" type="text/css" href="./lib/css/jsgrid-theme.css"/>
<script type="text/javascript" src="./lib/js/jsgrid.js"></script>
<script type="text/javascript" src="./lib/js/grid.locale-de.js"></script>
<script type="text/javascript" src="./lib/js/grid.locale-ru.js"></script>
<link rel="stylesheet" type="text/css" href="../../css/adapter.css"/>
<script type="text/javascript" src="../../js/translate.js"></script>
<script type="text/javascript" src="../../js/adapter-settings.js"></script>
<script type="text/javascript" src="words.js"></script>
<style>
.dialog-config {
overflow: hidden !important;
}
.ui-tabs-panel {
height: calc(100% - 70px);
padding: 0 !important;
}
.table_header {
background-color: blue;
color: white;
}
.bind {
width: 150px;
text-align: right;
}
.ui-jqgrid-titlebar {
display: none;
}
.ui-jqgrid-bdiv {
overflow-x: hidden !important;
}
.ui-button-text-only .ui-button-text {
padding: .3em !important;
}
.modbus-tab {
/*overflow: hidden;*/
position: relative;
height: calc(100% - 20px);
width: 100%;
}
.modbus-grid {
height: 100% !important;
width: 100% !important;
}
.jsgrid-grid-body {
height: calc(100% - 58px) !important;
}
.jsgrid-table {
width: 100% !important;
font-size: 1em;
}
.jsgrid-insert-mode-button {
display: none;
}
/*.jsgrid-edit-row input {
padding: 0;
}*/
.jsgrid-table td {
padding: 0.2em 0.2em !important;
}
.table_head_btn {
display: inline-flex;
text-align: center;
font-size: 1.1em;
position: absolute;
z-index: 99;
right: 5px;
top: -27px;
color: white;
}
.back_image {
position: absolute;
top: 0;
left: 0;
height: 100%;
width: 100%;
z-index: 0;
opacity: 0.3;
}
.logo {
position: absolute;
top: 0;
left: 0;
z-index: 1;
color: rgb(0, 153, 153);
font-weight: bolder;
font-size: 39px;
padding: 9px;
font-family: sans-serif;
}
tr td {
padding-left: 10px;
}
#adapter-container {
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
-webkit-user-drag: none;
user-select: none;
}
#export-dlg .select-dropdown {
display: none;
}
#export-dlg .dropdown-content {
display: none;
}
#export-dlg .caret {
display: none;
}
</style>
<script type="text/javascript">
var onChange = null;
var activePage = '';
var offsets = {
disInputs: 10001,
coils: 1,
inputRegs: 30001,
holdingRegs: 40001
};
var isDirect = false;
var typeItemsLen = {
'uint8be': 1,
'stringle': 0
};
var attempts = 0;
var ignoreDialog = 0;
function getExport(data, id, delimiter) {
delimiter = delimiter || '\t';
var text = '';
text += 'address' + delimiter +
'name' + delimiter +
'description' + delimiter;
if (id !== 'coils' && id !== 'disInputs') {
text += 'unit' + delimiter;
text += 'type' + delimiter;
text += 'len' + delimiter;
text += 'factor' + delimiter;
text += 'offset' + delimiter;
}
text += 'role' + delimiter +
'room' + delimiter;
if (id === 'coils' || id === 'holdingRegs') {
text += 'poll' + delimiter +
'wp';
if (!data.params.slave) {
text += delimiter + 'cw';
}
}
text += '\n';
for (var i = 0; i < data[id].length; i++) {
text += data[id][i]._address + delimiter;
text += (data[id][i].name || '') + delimiter;
text += (data[id][i].description || '') + delimiter;
if (id !== 'coils' && id !== 'disInputs') {
text += (data[id][i].unit || '') + delimiter;
text += (data[id][i].type || '') + delimiter;
text += (data[id][i].len || 1) + delimiter;
text += (data[id][i].factor || 1) + delimiter;
text += (data[id][i].offset || 0) + delimiter;
}
text += (data[id][i].role || '') + delimiter;
text += (data[id][i].room || '') + delimiter;
if (id === 'coils' || id === 'holdingRegs') {
text += (data[id][i].poll ? 'true' : 'false') + delimiter;
text += (data[id][i].wp ? 'true' : 'false') + delimiter;
if (!data.params.slave) {
text += (data[id][i].cw ? 'true' : 'false') + delimiter;
}
}
text += '\n';
}
return text;
}
function getImport(data, id, text) {
var delimiter;
if (text.indexOf('\t') !== -1) {
delimiter = '\t';
} else if (text.indexOf(';') !== -1) {
delimiter = ';';
} else {
delimiter = ' ';
}
var lines = text.split('\r\n');
if (lines.length === 1) {
lines = text.split('\n');
if (lines.length === 1) {
lines = text.split('\r');
}
}
var isAliases = $('#modbus_showAliases').prop('checked');
var mapping;
if (id !== 'coils' && id !== 'disInputs') {
mapping = ['address', 'name', 'description', 'unit', 'type', 'len', 'factor', 'offset', 'role', 'room'];
} else {
mapping = ['address', 'name', 'description', 'role', 'room'];
}
for (var i = 0; i < lines.length; i++) {
var parts = lines[i].split(delimiter);
if (parts.length === 1) continue;
if (parseInt(parts[0].trim(), 10).toString() !== parts[0]) {
// may be it is fields description
mapping = parts;
continue;
}
var obj = {};
var j = 0;
for (var m = 0; m < mapping.length; m++) {
var val = parts[m].trim();
if (mapping[m] === 'address') {
obj._address = parseInt(val, 10) || 0;
if (isAliases) {
obj.address = alias2address(id, obj._address);
} else {
obj.address = obj._address;
}
} else if (mapping[m] === 'factor') {
obj.factor = parseFloat(val) || 1;
} else if (mapping[m] === 'offset') {
obj.offset = parseFloat(val) || 0;
} else if (mapping[m] === 'len') {
obj.len = parseInt(val, 10) || 1;
} else if (mapping[m] === 'poll') {
obj[mapping[m]] = val || '';
obj.poll = (obj.poll.toLowerCase() === 'true' || obj.poll === '1' || obj.poll === '+');
} else if (mapping[m] === 'wp') {
obj[mapping[m]] = val || '';
obj.wp = (obj.wp.toLowerCase() === 'true' || obj.wp === '1' || obj.wp === '+');
} else if (mapping[m] === 'cw') {
obj[mapping[m]] = val || '';
obj.cw = (obj.cw.toLowerCase() === 'true' || obj.cw === '1' || obj.cw === '+');
} else {
obj[mapping[m]] = val || '';
}
}
if (typeItemsLen[obj.type]) obj.len = typeItemsLen[obj.type];
data[id].push(obj);
}
$('#modbus_' + id).jsGrid('render');
}
function switchOneType(data, id) {
var isDirect = $('#modbus_directAddresses').prop('checked');
if ($('#modbus_showAliases').prop('checked')) {
for (var i = 0; i < data[id].length; i++) {
data[id][i]._address = address2alias(id, data[id][i].address, isDirect);
}
} else {
for (var i = 0; i < data[id].length; i++) {
data[id][i]._address = parseInt(data[id][i].address, 10) || 0;
}
}
$('#modbus_' + id).jsGrid('render');
}
function switchAll(data) {
switchOneType(data, 'holdingRegs');
if ($('#modbus_showAliases').prop('checked')) {
$('.offsets').show();
} else {
$('.offsets').hide();
}
}
function recalculate(data, id, oldOffset, newOffset) {
if ($('#modbus_showAliases').prop('checked')) {
for (var i = 0; i < data[id].length; i++) {
data[id][i].address = parseInt(data[id][i].address, 10) || 0;
data[id][i].address += (oldOffset - newOffset);
}
}
}
function initTabs(onchange, data) {
if (!data) {
data = {};
$.each($('.modbus-grid'), function () {
var id = $(this).attr('id');
var key = id.split('_')[1];
if ($(this).data('JSGrid')._editingRow) {
$(this).jsGrid('updateItem');
}
data[key] = $(this).jsGrid('option', 'data');
});
}
getEnums('rooms', function (err, rooms) {
$.each($('.modbus-grid'), function () {
var _id = $(this).attr('id');
// todo read roles and expand this list
var roleItems = [
{value: '', text: ''},
{value: 'switch', text: 'switch'},
{value: 'button', text: 'button'},
{value: 'indicator', text: 'indicator'}
];
var typeItems = [
{value: '', text: ''},
{value: 'int8le', text: 'Signed 8 bit (Little Endian)'}
];
var roomsItems = [{value: '', text: ''}];
for (var room in rooms) {
if (rooms.hasOwnProperty(room)) {
roomsItems.push({value: room, text: rooms[room].common.name || room.substring('enum.rooms.'.length)});
}
}
var fields = [
{name: '_address' , title: _('Address'), type: 'text', width: '110px', sorter: 'number'},
{name: 'deviceId' , title: _('deviceId'), type: 'text', width: '60px', sorter: 'number'},
{name: 'name' , title: _('Name'), type: 'text', width: '*'},
{name: 'description', title: _('Description'), type: 'text', width: '*'},
{name: 'unit' , title: _('Unit'), type: 'text', width: '50px'},
{name: 'type' , title: _('Type'), type: 'select', width: '200px', items: typeItems, valueField: 'value', textField: 'text'},
{name: 'len' , title: _('Length'), type: 'text', width: '50px'},
{name: 'factor' , title: _('Factor'), type: 'text', width: '50px'},
{name: 'offset' , title: _('Offset'), type: 'text', width: '50px'},
{name: 'role' , title: _('Role'), type: 'select', width: '100px', items: roleItems, valueField: 'value', textField: 'text'},
{name: 'room' , title: _('Room'), type: 'select', width: '100px', items: roomsItems, valueField: 'value', textField: 'text'},
{name: 'poll' , title: _('poll'), type: 'checkbox', width: '35px'},
{name: 'wp' , title: _('WP'), type: 'checkbox', width: '35px'},
{name: 'cw' , title: _('CW'), type: 'checkbox', width: '35px'},
{ type: 'control', width: '80px'}
];
var g = $('#' + _id).jsGrid({
width: 'auto',
height: 'auto',
inserting: true,
editing: true,
sorting: true,
heading: true,
paging: false,
deleteConfirm: function (el, item, cb) {
if (!ignoreDialog || Date.now() - ignoreDialog > 60000) {
var buttons = {};
buttons[_('Delete')] = function() {
if ($('#delete_ignore_dialog').prop('checked')) {
ignoreDialog = Date.now();
}
cb && cb(true);
$( this ).dialog('close');
};
buttons[_('Cancel')] = function() {
cb && cb(false);
$( this ).dialog('close');
};
$('#delete_ignore_dialog').prop('checked', false);
$("#delete-dlg").dialog({
resizable: false,
height: 'auto',
title: _('Delete address'),
width: 500,
modal: true,
buttons: buttons
});
} else {
cb && cb(true);
}
},
data: data[_id.split('_')[1]],
fields: fields,
editRowRenderer: function(item) {
var $result = $('<tr>').addClass(this.editRowClass);
this._eachField(function(field) {
$('<td>').addClass(field.css)
.appendTo($result)
.append(field.editTemplate ? field.editTemplate(item ? item[field.name] : '', item) : '')
.width(field.width || 'auto').keypress(function (e) {
if (e.which === 13) {
$('#' + _id).jsGrid('updateItem');
}
onchange();
});
});
return $result;
},
onItemUpdated: function (obj) {
/* {grid, row, item, itemIndex, previousItem}*/
var id = obj.grid._body.parent().attr('id');
var changed = false;
if (obj.item.wp && !obj.item.poll) {
obj.item.poll = true;
changed = true;
}
if (parseInt(obj.item.address, 10).toString() !== obj.item.address.toString()) changed = true;
obj.item._address = parseInt(obj.item._address, 10);
if ($('#modbus_showAliases').prop('checked')) {
var isDirect = $('#modbus_directAddresses').prop('checked');
obj.item.address = alias2address(id.split('_').pop(), obj.item._address, isDirect);
} else {
obj.item.address = obj.item._address;
}
if (obj.item.type === '') {
obj.item.type = typeItems[1].value;
changed = true;
}
// update length
if (obj.item.type === 'string' || obj.item.type === 'stringle') {
if (obj.item.factor !== '') {
obj.item.factor = '';
changed = true;
}
if (obj.item.offset !== '') {
obj.item.offset = '';
changed = true;
}
obj.item.len = parseInt(obj.item.len, 10) || 0;
if (!obj.item.len) {
obj.item.len = 2;
changed = true;
}
} else if (typeItemsLen[obj.item.type]) {
obj.item.len = parseInt(obj.item.len, 10);
if (obj.item.len != typeItemsLen[obj.item.type]) {
obj.item.len = typeItemsLen[obj.item.type];
changed = true;
}
if (parseFloat(obj.item.factor).toString() !== obj.item.factor.toString()) {
obj.item.factor = parseFloat(obj.item.factor) || 1;
changed = true;
}
if (parseFloat(obj.item.offset).toString() !== obj.item.offset.toString()) {
obj.item.offset = parseFloat(obj.item.offset) || 0;
changed = true;
}
}
if (!obj.item.role && obj.item.type !== 'string' && obj.item.type !== 'stringle') {
if (id === 'modbus_disInputs') {
obj.item.role = 'status';
} else if (id === 'modbus_coils') {
obj.item.role = 'button';
} else if (id === 'modbus_inputRegs') {
obj.item.role = 'value';
} else if (id === 'modbus_holdingRegs') {
obj.item.role = 'level';
}
changed = true;
}
if (changed) this.render();
onchange();
},
onItemInserting: function (obj) {
if (obj.item.poll !== undefined) {
obj.item.poll = true;
}
// find automatically next address
var id = obj.grid._body.parent().attr('id').split('_')[1];
if (data[id].length) {
var max = 0;
obj.item.role = data[id][data[id].length - 1].role;
obj.item.poll = data[id][data[id].length - 1].poll;
obj.item.wp = data[id][data[id].length - 1].wp;
obj.item.cw = data[id][data[id].length - 1].cw;
obj.item.type = data[id][data[id].length - 1].type;
obj.item.len = data[id][data[id].length - 1].len;
obj.item.factor = data[id][data[id].length - 1].factor;
obj.item.offset = data[id][data[id].length - 1].offset;
obj.item.deviceId = data[id][data[id].length - 1].deviceId;
var len;
var defaultDeviceId = parseInt($('#modbus_deviceId').val(), 10) || 1;
var actualDeviceId = parseInt(obj.item.deviceId, 10);
if (!actualDeviceId) actualDeviceId = defaultDeviceId;
for (var a = 0; a < data[id].length; a++) {
var deviceId = parseInt(data[id][a].deviceId, 10);
if (!deviceId) deviceId = defaultDeviceId;
if (deviceId !== actualDeviceId) continue;
if (!obj.item.type) {
len = 1;
} else if (obj.item.type !== 'string' && obj.item.type !== 'stringle') {
len = typeItemsLen[obj.item.type];
} else {
len = obj.item.len;
}
data[id][a]._address = parseInt(data[id][a]._address, 10);
if (data[id][a]._address + len > max) max = data[id][a]._address + len;
}
obj.item._address = max;
if ($('#modbus_showAliases').prop('checked')) {
obj.item.address = alias2address(id, parseInt(obj.item._address, 10), isDirect);
} else {
obj.item.address = obj.item._address;
}
} else {
if ($('#modbus_showAliases').prop('checked')) {
obj.item.address = alias2address(id, obj.item._address, isDirect);
} else {
obj.item.address = 0;
obj.item._address = 0;
}
if (id === 'inputRegs' || 'holdingRegs') {
obj.item.factor = 1;
obj.item.offset = 0;
obj.item.type = 'uint16be';
obj.item.len = '';
}
}
onchange();
},
onItemDeleted: function (obj) {
onchange();
}
});
g.data('JSGrid')._editRow = function($row) {
if(this._editingRow) {
this.updateItem();
}
var item = $row.data('JSGridItem');
if (!item) return;
var $editRow = this._createEditRow(item);
this._editingRow = $row;
$row.hide();
$editRow.insertAfter($row);
$row.data('JSGridEditRow', $editRow);
};
setTimeout(function () {
// add custom titles
$('.jsgrid-header-sortable').each(function () {
if ($(this).text() === _('poll')) {
$(this).attr('title', _('Enable polling of data point'));
} else
if ($(this).text() === _('WP')) {
$(this).attr('title', _('Write pulses (true=>false edge)'));
} else
if ($(this).text() === _('CW')) {
$(this).attr('title', _('Cyclic write'));
}
});
}, 1000);
});
switchAll(data);
});
}
function load(settings, onchange) {
$('#btn_loadsymbole').button().click(function () {
$('#symol_import').trigger('click')
});
$('#btn_loaddb').button().click(function () {
$('#db_import').trigger('click')
});
$('.btn_toggle_pool').button().click(function (e, ui) {
var grid = $(this).parent().data('grid');
var $grid = $('#' + grid);
var data = $grid.jsGrid('option', 'data');
$.each(data, function () {
this.poll = !this.poll;
});
$grid.jsGrid('option', 'data', data);
onChange();
});
$('.btn_toggle_rw').button().click(function () {
var grid = $(this).parent().data('grid');
var $grid = $('#' + grid);
var data = $grid.jsGrid('option', 'data');
$.each(data, function () {
this.RW = !this.RW;
});
$grid.jsGrid('option', 'data', data);
onChange();
});
$('.btn_toggle_wp').button().click(function () {
var grid = $(this).parent().data('grid');
var $grid = $('#' + grid);
var data = $grid.jsGrid('option', 'data');
$.each(data, function () {
this.wp = !this.wp;
});
$grid.jsGrid('option', 'data', data);
onChange();
});
var $adapterContainer = $('#adapter-container');
$adapterContainer.tabs({
activate: function (event, ui) {
switch(ui.newPanel.selector) {
case '#modbus_tab_g':
activePage = 0;
break;
case '#modbus_tab_regsis_inputs':
activePage = 1;
break;
case '#modbus_tab_coils':
activePage = 2;
break;
case '#modbus_tab_input_regs':
activePage = 3;
break;
case '#modbus_tab_holding_regs':
activePage = 4;
break;
default:
activePage = 0;
break;
}
if (typeof localStorage !== 'undefined') {
localStorage['modbuspage'] = activePage.toString();
}
}
});
$('.btn_export').button({
icons: {primary: 'ui-icon-circle-minus'},
text: false
})
.attr('title', _('Export to CSV'))
.css({width: 24, height: 24})
.click(function () {
var id = $(this).data('table');
// build text
var text = getExport(data, id);
$('#export-dlg').show();
$('#export_textarea').val(text).trigger('select');
document.execCommand('copy');
});
$('#export-dlg').click(function () {
$(this).hide();
});
$('.btn_import').button({
icons: {primary: 'ui-icon-circle-plus'},
text: false
})
.attr('title', _('Import from CSV'))
.css({width: 24, height: 24})
.click(function () {
$('#import-dlg').show().data('table', $(this).data('table'));
$('#import_textarea').val('').focus();
});
$('#import_button_ok').button({
icons: {primary: 'ui-icon-check'},
text: _('Export')
}).click(function () {
var $importDlg = $('#import-dlg');
getImport(data, $importDlg.data('table'), $('#import_textarea').val());
$importDlg.hide();
onchange();
});
$('#import_button_cancel').button({
icons: {primary: 'ui-icon-close'},
text: _('Close')
}).click(function () {
$('#import-dlg').hide();
});
$('.btn_delete_all').button({
icons: {primary: 'ui-icon-trash'},
text: false
})
.attr('title', _('Delete all entries'))
.css({width: 24, height: 24})
.click(function () {
if (window.confirm(_('All entries will be deleted. Are you sure?'))) {
var id = $(this).data('table');
if (data[id].length) {
data[id].splice(0, data[id].length);
$('#modbus_' + id).jsGrid('render');
onchange();
}
}
});
if (systemLang === 'de') {
translate_de();
}
if (typeof localStorage !== 'undefined') {
activePage = localStorage['modbuspage'];
$adapterContainer.tabs({active: parseInt(activePage || 0, 10)});
}
settings.params = settings.params || {};
var data = {
disInputs: settings.disInputs || [],
coils: settings.coils || [],
inputRegs: settings.inputRegs || [],
holdingRegs: settings.holdingRegs || [],
params: {
bind: settings.params.bind || '192.168.0.1',
port: settings.params.port || 502,
deviceId: settings.params.deviceId || 0,
slave: settings.params.slave || 0,
recon: settings.params.recon || 60000,
poll: settings.params.poll || 1000,
pulsetime: settings.params.pulsetime || 1000,
round: settings.params.round || 2,
maxBlock: settings.params.maxBlock || 100,
maxBoolBlock: settings.params.maxBoolBlock || 128,
disInputsOffset: settings.params.disInputsOffset || 10001,
coilsOffset: settings.params.coilsOffset || 1,
inputRegsOffset: settings.params.inputRegsOffset || 30001,
holdingRegsOffset: settings.params.holdingRegsOffset || 40001,
showAliases: (settings.params.showAliases === undefined) ? true : settings.params.showAliases,
directAddresses: settings.params.directAddresses || false,
alwaysUpdate: settings.params.alwaysUpdate || false,
doNotRoundAddressToWord: settings.params.doNotRoundAddressToWord || false,
comName: settings.params.comName || '',
type: settings.params.type || 'tcp',
dataBits: settings.params.dataBits || 8,
stopBits: settings.params.stopBits || 1,
parity: settings.params.parity || 'none',
baudRate: settings.params.baudRate || 9600,
timeout: settings.params.timeout || 5000,
multiDeviceId: settings.params.multiDeviceId || false
}
};
data.params.showAliases = (data.params.showAliases === 'true' || data.params.showAliases === true);
data.params.directAddresses = (data.params.directAddresses === 'true' || data.params.directAddresses === true);
data.params.alwaysUpdate = (data.params.alwaysUpdate === 'true' || data.params.alwaysUpdate === true);
onChange = onchange;
$.each($('.modbus-params'), function () {
var id = $(this).attr('id');
if ($(this).attr('type') === 'checkbox') {
$(this).prop('checked', !!data.params[id.split('_')[1]]);
} else {
$(this).val(data.params[id.split('_')[1]]);
}
$(this).on('change', function () {
var id = $(this).attr('id').split('_')[1];
if (id === 'disInputsOffset') {
recalculate(data, 'disInputs', offsets.disInputs, parseInt($(this).val(), 10));
offsets.disInputs = parseInt($(this).val(), 10);
}
if (id === 'coilsOffset') {
recalculate(data, 'coils', offsets.coils, parseInt($(this).val(), 10));
offsets.coils = parseInt($(this).val(), 10);
}
if (id === 'inputRegsOffset') {
recalculate(data, 'inputRegs', offsets.inputRegs, parseInt($(this).val(), 10));
offsets.inputRegs = parseInt($(this).val(), 10);
}
if (id === 'holdingRegsOffset') {
recalculate(data, 'holdingRegs', offsets.holdingRegs, parseInt($(this).val(), 10));
offsets.holdingRegs = parseInt($(this).val(), 10);
}
if (id === 'showAliases') {
switchAll(data);
}
onchange();
}).on('keyup', function () {
$(this).trigger('change');
});
});
isDirect = !!settings.params.directAddresses;
$('#modbus_directAddresses').on('change', function () {
isDirect = $(this).prop('checked');
});
$('#modbus_showAliases').on('change', function () {
if (!$(this).prop('checked')) {
$('#modbus_directAddresses').prop('checked', false).prop('disabled', true);
} else {
$('#modbus_directAddresses').prop('disabled', false);
}
}).trigger('change');
settings.params.type = settings.params.type || 'tcp';
$('#modbus_type').on('change', function () {
if ($(this).val() === 'tcp' || $(this).val() === 'tcprtu') {
$('.tcp').show();
$('.serial').hide();
if ($(this).val() === 'tcprtu') {
$('#modbus_slave').val(0).prop('disabled', true).trigger('change');
} else {
$('#modbus_slave').prop('disabled', false);
}
} else {
$('.tcp').hide();
$('.serial').show();
$('#modbus_slave').val(0).prop('disabled', true).trigger('change');
}
initTabs(onchange);
}).trigger('change');
$('#modbus_slave').on('change', function () {
if ($(this).val() === '0') {
$('#multiDeviceId').show();
} else {
$('#multiDeviceId').hide();
$('#modbus_multiDeviceId').prop('checked', false);
}
initTabs(onchange);
}).trigger('change');
$('#multiDeviceId').on('change', function () {
initTabs(onchange);
});
getIsAdapterAlive(function (isAlive) {
if (isAlive || common.enabled) {
//
}
});
initTabs(onchange, data);
onchange(false);
}
function save(callback) {
var obj = {
params: {}
};
// finish editing
$.each($('.modbus-grid'), function () {
var id = $(this).attr('id');
var key = id.split('_')[1];
if ($(this).data('JSGrid')._editingRow) {
$(this).jsGrid('updateItem');
}
obj[key] = $(this).jsGrid('option', 'data');
});
$.each($('.modbus-params'), function () {
var id = $(this).attr('id').split('_')[1];
if ($(this).attr('type') === 'checkbox') {
obj.params[id] = $(this).prop('checked');
} else {
obj.params[id] = $(this).val();
}
});
callback(obj);
}
var _rmap = {
0: 15,
1: 14,
2: 13,
3: 12,
4: 11,
5: 10,
6: 9,
7: 8,
8: 7,
9: 6,
10: 5,
11: 4,
12: 3,
13: 2,
14: 1,
15: 0
};
var _dmap = {
0: 0,
1: 1,
2: 2,
3: 3,
4: 4,
5: 5,
6: 6,
7: 7,
8: 8,
9: 9,
10: 10,
11: 11,
12: 12,
13: 13,
14: 14,
15: 15
};
function address2alias(id, address, isDirect) {
var oldAddr = address;
if (typeof address === 'string') address = parseInt(address, 10);
if (typeof offsets[id] === 'string') offsets[id] = parseInt(offsets[id], 10);
address = parseInt(address, 10) || 0;
if (id === 'disInputs' || id === 'coils') {
address = Math.floor(address / 16) * 16 + (isDirect ? _dmap[address % 16] : _rmap[address % 16]);
address += offsets[id];
console.log('Convert addr: ' + oldAddr + ' => alias: ' + address);
return address
} else {
return address + offsets[id];
}
}
function alias2address(id, alias, isDirect) {
var oldAlias = alias;
if (typeof alias === 'string') alias = parseInt(alias, 10);
if (typeof offsets[id] === 'string') offsets[id] = parseInt(offsets[id], 10);
if (id === 'disInputs' || id === 'coils') {
alias = alias - offsets[id];
alias = Math.floor(alias / 16) * 16 + (isDirect ? _dmap[alias % 16] : _rmap[alias % 16]);
console.log('Convert alias: ' + oldAlias + ' => addr: ' + alias);
return alias;
} else {
return alias - offsets[id];
}
}
</script>
<!-- you have to put your config page in a div with id adapter-container -->
<div id="adapter-container" style="height: calc(100% - 40px);width: calc(100% - 10px); overflow: hidden;">
<ul>
<li><a href="#modbus_tab_g" class="translate">General</a></li>
<li><a href="#modbus_tab_holding_regs">Holding Registers</a></li>
</ul>
<div id="modbus_tab_g" style="display: flex; align-items: center; flex-direction: column;overflow: auto" class="modbus-tab">
<div class="logo">串口开关适配器</div>
<img src="img/plc_back.png" class="back_image">
<table class="ui-widget-content" style="z-index: 2;margin-top: 20px; border-collapse: collapse; display: inline; border: 1px solid black;">
<tr>
<td style="text-align: center" colspan="3" class="ui-widget-header translate">General</td>
</tr>
<tr>
<td><label class="translate" for="modbus_showAliases">Use aliases as address:</label></td>
<td><input style="margin-right: 5px;text-align: right" class="modbus-params" type="checkbox" id="modbus_showAliases"/></td>
<td></td>
</tr>
<tr class="direct-addresses">
<td><label class="translate" for="modbus_directAddresses">Use direct addresses by aliases:</label></td>
<td><input style="margin-right: 5px;text-align: right" class="modbus-params" type="checkbox" id="modbus_directAddresses"/></td>
<td></td>
</tr>
<tr>
<td><label class="translate" for="modbus_doNotRoundAddressToWord">Do not align addresses to word:</label></td>
<td><input style="margin-right: 5px; text-align: right" class="modbus-params" type="checkbox" id="modbus_doNotRoundAddressToWord"/></td>
<td></td>
</tr>
<tr>
<td style="width: 184px"><label class="translate" for="modbus_round">Round Real to:</label></td>
<td style="text-align: center"><input style="margin-right: 5px; text-align: right" type="text" class="modbus-params" id="modbus_round"></td>
<td></td>
</tr>
<tr>
<td><label class="translate" for="modbus_poll">Poll delay:</label></td>
<td style="text-align: center"><input style="margin-right: 5px;text-align: right" class="modbus-params" id="modbus_poll"/></td>
<td style="padding-right: 20px">ms</td>
</tr>
<tr>
<td><label class="translate" for="modbus_recon">Reconnect time:</label></td>
<td style="text-align: center"><input style="margin-right: 5px;text-align: right" type="text" class="modbus-params" id="modbus_recon"></td>
<td>ms</td>
</tr>
<tr>
<td><label for="modbus_timeout" class="translate">Read timeout:</label></td>
<td style="text-align: center"><input style="margin-right: 5px;text-align: right" type="text" class="modbus-params" id="modbus_timeout"></td>
<td>ms</td>
</tr>
<tr>
<td><label class="translate" for="modbus_pulsetime">Pulse time:</label></td>
<td style="text-align: center"><input style="margin-right: 5px;text-align: right" type="text" class="modbus-params" id="modbus_pulsetime"></td>
<td>ms</td>
</tr>
<tr>
<td><label class="translate" for="modbus_maxBlock">Max read request length:</label></td>
<td style="text-align: center"><input style="margin-right: 5px;text-align: right" type="text" class="modbus-params" id="modbus_maxBlock"></td>
<td class="translate">registers</td>
</tr>
<tr>
<td><label class="translate" for="modbus_maxBoolBlock">Max read request length (booleans):</label></td>
<td style="text-align: center"><input style="margin-right: 5px;text-align: right" type="text" class="modbus-params" id="modbus_maxBoolBlock"></td>
<td class="translate">registers</td>
</tr>
<tr>
<td><label class="translate" for="modbus_alwaysUpdate">Update unchanged states:</label></td>
<td><input style="margin-right: 5px;text-align: right" class="modbus-params" type="checkbox" id="modbus_alwaysUpdate"/></td>
</tr>
<tr>
<td colspan="3" style="height: 10px"></td>
</tr>
</table>
</div>
<div id="modbus_tab_holding_regs" class="modbus-tab">
<div class="table_head_btn" data-grid="modbus_holdingRegs">
<button class="btn_delete_all translateB" data-table="holdingRegs"></button>
<button class="btn_export translateB" data-table="holdingRegs"></button>
<button class="btn_import translateB" data-table="holdingRegs"></button>
<!--button class="btn_toggle_pool translateB" title="Polling">Toggle poll</button-->
<!--button class="btn_toggle_wp translateB" title="write as Pulse">Toggle WP</button-->
</div>
<div id="modbus_holdingRegs" class="modbus-grid"></div>
</div>
<div id="export-dlg" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; display: none; z-index: 100; background: white">
<div id="export_help" class="translate" style="position: absolute; opacity: 0.8; top: 10px; right: 10px; font-size: 24px; font-weight: bold; color: blue">Text copied to clipboard. Click to close the window</div>
<textarea id="export_textarea" readonly style="resize: none; width: 100%; height: calc(100% - 40px); margin-top: 40px;"></textarea>
</div>
<div id="import-dlg" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; display: none; z-index: 100; background: white">
<textarea id="import_textarea" style="width: 100%; height: calc(100% - 40px); resize: none;"></textarea>
<button id="import_button_ok" style="position: absolute; bottom: 10px; right: 120px;" ></button>
<button id="import_button_cancel" style="position: absolute; bottom: 10px; right: 10px;"></button>
</div>
<div id="delete-dlg" style="display: none;">
<p>
<span class="ui-icon ui-icon-alert" style="float:left; margin:12px 12px 20px 0;"></span>
<span class="translate">Are you sure?</span><br><br>
<input type="checkbox" id="delete_ignore_dialog" /><label class="translate">Say "yes" for the next minute:</label>
</p>
</div>
</div>
</html>