[Project] Initial Commit

This commit is contained in:
Robert von Burg 2020-01-27 16:18:15 +01:00
parent b040e85637
commit a90ad2dfd1
9 changed files with 1117 additions and 2 deletions

4
.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
# Created by .ignore support plugin (hsz.mobi)
.idea/
bower_components/
*.iml

View File

@ -1,2 +1,2 @@
# strolch-plc-ui
The UI for the software PLC based on Strolch's runtime
# strolch-wc-plc
The UI components for the software PLC based on Strolch's runtime

61
bower.json Normal file
View File

@ -0,0 +1,61 @@
{
"name": "strolch-wc-plc",
"description": "Strolch PLC WebComponents",
"version": "0.1.0",
"authors": ["Robert von Burg"],
"keywords": [
"strolch",
"inspector"
],
"main": "strolch-wc-plc-logical-devices.html",
"private": true,
"repository": {
"type": "git",
"url": "git@github.com:4treesCH/strolch-wc-plc.git"
},
"license": "https://github.com/4treesCH/strolch-wc-plc/blob/master/LICENSE",
"homepage": "https://github.com/4treesCH/strolch-wc-plc",
"ignore": [],
"dependencies": {
"strolchjs": "4treesCH/strolchjs#^0.2.7",
"strolch-wc-styles": "4treesCH/strolch-wc-styles#^0.3.0",
"strolch-wc-debounced-input": "4treesCH/strolch-wc-debounced-input#^0.1.0",
"strolch-wc-tree": "4treesCH/strolch-wc-tree#^0.1.5",
"strolch-wc-ws-observer": "4treesCH/strolch-wc-ws-observer#^0.1.5",
"strolch-wc-localize-behavior": "4treesCH/strolch-wc-localize-behavior#^1.1.4",
"polymer": "Polymer/polymer#^1.11.3",
"iron-ajax": "PolymerElements/iron-ajax#^1.4.4",
"iron-pages": "PolymerElements/iron-pages#^1.0.9",
"iron-icons": "PolymerElements/iron-icons#^1.2.1",
"iron-a11y-keys": "PolymerElements/iron-a11y-keys#^1.0.9",
"app-route": "PolymerElements/app-route#^1.0.1",
"app-layout": "PolymerElements/app-layout#^1.0.1",
"paper-material": "PolymerElements/paper-material#^1.0.7",
"paper-header-panel": "PolymerElements/paper-header-panel#^1.1.7",
"paper-card": "PolymerElements/paper-card#^1.1.6",
"paper-badge": "PolymerElements/paper-badge#^1.1.4",
"paper-dialog": "PolymerElements/paper-dialog#^1.1.0",
"paper-button": "PolymerElements/paper-button#^1.0.15",
"paper-toolbar": "PolymerElements/paper-toolbar#^1.1.7",
"paper-icon-button": "PolymerElements/paper-icon-button#^1.1.6",
"paper-tabs": "PolymerElements/paper-tabs#^1.8.0",
"paper-dropdown-menu": "PolymerElements/paper-dropdown-menu#^1.5.1",
"paper-styles": "PolymerElements/paper-styles#^1.3.1",
"paper-listbox": "polymerelements/paper-listbox#^1.1.3",
"paper-item": "PolymerElements/paper-item#^1.2.2",
"paper-input": "PolymerElements/paper-input#^1.2.1",
"paper-checkbox": "PolymerElements/paper-checkbox#^1.4.2",
"paper-radio-group": "PolymerElements/paper-radio-group#^1.2.2",
"vaadin-date-picker": "vaadin/vaadin-date-picker#^1.2.4",
"vaadin-upload": "vaadin/vaadin-upload#v2.1.3"
},
"devDependencies": {
}
}

69
locales.json Normal file
View File

@ -0,0 +1,69 @@
{
"de": {
"refresh": "Aktualisieren",
"close": "Schliessen",
"cancel": "Abbrechen",
"ok": "Ok",
"plcConnections": "SPS Verbindungen",
"plcLogicalDevices": "SPS Logische Geräte",
"class": "Klasse",
"connect": "Verbinden",
"disconnect": "Trennen",
"Connected": "Verbunden",
"Disconnected": "Getrennt",
"addresses": "Adressen",
"notifications": "Benachrichtigungen",
"telegrams": "Telegramme",
"send": "Senden",
"notify": "Benachrichtigen",
"id": "ID",
"name": "Name",
"on": "Ein",
"off": "Aus",
"address": "Adresse",
"i2cBus": "I2C Bus",
"interruptPinName": "Interrupt Pin",
"virtualAddresses": "Virtuelle Adressen",
"errorOccurred": "Fehler aufgetreten",
"plc": "SPS",
"start": "Starten",
"stop": "Stoppen",
"reconfigure": "Neu Konfigurieren",
"info": "Info",
"reconnect": "Wiederhestellen"
},
"en": {
"info": "Info",
"start": "Start",
"stop": "Stop",
"reconfigure": "Reconfigure",
"plc": "PLC",
"errorOccurred": "Error Occurred",
"virtualAddresses": "Virtual Addresses",
"on": "On",
"off": "Off",
"address": "Address",
"i2cBus": "I2C Bus",
"interruptPinName": "Interrupt Pin",
"id": "ID",
"name": "Name",
"send": "Send",
"notify": "Notify",
"addresses": "Addresses",
"notifications": "Notifications",
"telegrams": "Telegrams",
"Connected": "Connected",
"Disconnected": "Disconnected",
"connect": "Connect",
"disconnect": "Disconnect",
"class": "Class",
"plcConnections": "PLC Connections",
"plcLogicalDevices": "PLC Logical Devices",
"logout": "Logout",
"refresh": "Refresh",
"close": "Close",
"cancel": "Cancel",
"ok": "Ok",
"reconnect": "Reconnect"
}
}

View File

@ -0,0 +1,83 @@
<link rel="import" href="../polymer/polymer.html">
<script>
StrolchPlcBehavior = {
properties: {
localesPath: {
type: String,
value: './locales.json'
}
},
arrayFilled: function (array) {
return !!(array && array.length && array.length > 0);
},
isTelegram: function (addressType) {
return addressType === "Telegram";
},
isNotification: function (addressType) {
return addressType === "Notification";
},
isBoolean: function (type) {
return type === 'Boolean';
},
isNumber: function (type) {
switch (type) {
case 'Short':
case 'Integer':
case 'Long':
case 'Float':
case 'Double':
return true;
default:
return false;
}
},
isInteger: function (type) {
switch (type) {
case 'Short':
case 'Integer':
case 'Long':
return true;
default:
return false;
}
},
isFloat: function (type) {
switch (type) {
case 'Float':
case 'Double':
return true;
default:
return false;
}
},
isString: function (type) {
switch (type) {
case 'String':
case 'ByteArray':
return true;
default:
return false;
}
},
isPlcError: function (plc) {
return plc.state === "Failed";
},
isError: function (device) {
return device.state === "Error" || device.stateMsg !== "";
},
isOn: function (device) {
return device.state === "On";
},
onRequestError: function (e) {
this.fire("strolch-ajax-request-error", e.detail);
}
}
</script>

View File

@ -0,0 +1,233 @@
<link rel="import" href="../polymer/polymer.html">
<link rel="import" href="../iron-ajax/iron-ajax.html">
<link rel="import" href="../iron-icons/iron-icons.html">
<link rel="import" href="../iron-icons/notification-icons.html">
<link rel="import" href="../iron-icons/maps-icons.html">
<link rel="import" href="../iron-icons/av-icons.html">
<link rel="import" href="../iron-icons/communication-icons.html">
<link rel="import" href="../iron-icons/device-icons.html">
<link rel="import" href="../iron-icons/hardware-icons.html">
<link rel="import" href="../iron-icons/editor-icons.html">
<link rel="import" href="../iron-icons/places-icons.html">
<link rel="import" href="../iron-icons/social-icons.html">
<link rel="import" href="../paper-material/paper-material.html">
<link rel="import" href="../paper-card/paper-card.html">
<link rel="import" href="../paper-button/paper-button.html">
<link rel="import" href="../paper-icon-button/paper-icon-button.html">
<link rel="import" href="../paper-toggle-button/paper-toggle-button.html">
<link rel="import" href="../strolch-wc-localize-behavior/strolch-wc-localize-behavior.html">
<link rel="import" href="./strolch-wc-plc-style.html">
<link rel="import" href="./strolch-wc-plc-behavior.html">
<dom-module id="strolch-wc-plc-connections">
<template>
<style is="custom-style" include="strolch-wc-plc-style">
.label {
min-width: 250px;
margin-right: 10px;
}
</style>
<div class="actions">
<paper-icon-button icon="refresh" on-tap="_refresh"></paper-icon-button>
</div>
<div class="container">
<paper-card class="item" elevation="1">
<div class="header"><h3>[[localize('plc')]]</h3></div>
<div class="card-content g-flex-table">
<div class="g-flex-table-row">
<div class="g-flex-table-cell label">[[localize('class')]]</div>
<div class="g-flex-table-cell">[[plc.className]]</div>
</div>
<div class="g-flex-table-row">
<div class="g-flex-table-cell label">
<template is="dom-if" if="[[isPlcError(plc)]]">
<iron-icon class="error-icon" icon="warning"></iron-icon>
</template>
<template is="dom-if" if="[[!isPlcError(plc)]]">
<iron-icon icon="info-outline"></iron-icon>
</template>
</div>
<template is="dom-if" if="[[isPlcError(plc)]]">
<div class="g-flex-table-cell">[[plc.stateMsg]]</div>
</template>
<template is="dom-if" if="[[!isPlcError(plc)]]">
<div class="g-flex-table-cell">[[localize(plc.state)]]</div>
</template>
</div>
</div>
<div class="card-actions">
<dfiv class="card-actions-right">
<paper-button on-tap="startPlc" raised>[[localize('start')]]
</paper-button>
<paper-button on-tap="stopPlc" raised>[[localize('stop')]]
</paper-button>
<paper-button on-tap="reconfigurePlc" raised>[[localize('reconfigure')]]
</paper-button>
</dfiv>
</div>
</paper-card>
<template is="dom-repeat" items="[[connections]]" as="connection">
<paper-card class="item" elevation="1">
<div class="header"><h3>[[connection.id]] / [[connection.name]]</h3></div>
<div class="card-content g-flex-table">
<div class="g-flex-table-row">
<div class="g-flex-table-cell label">[[localize('id')]]</div>
<div class="g-flex-table-cell">[[connection.id]]</div>
</div>
<div class="g-flex-table-row">
<div class="g-flex-table-cell label">[[localize('name')]]</div>
<div class="g-flex-table-cell">[[connection.name]]</div>
</div>
<div class="g-flex-table-row">
<div class="g-flex-table-cell label">[[localize('class')]]</div>
<div class="g-flex-table-cell">[[connection.className]]</div>
</div>
<div class="g-flex-table-row">
<div class="g-flex-table-cell label">
<template is="dom-if" if="[[isError(connection)]]">
<iron-icon class="error-icon" icon="warning"></iron-icon>
</template>
<template is="dom-if" if="[[!isError(connection)]]">
<iron-icon icon="info-outline"></iron-icon>
</template>
</div>
<template is="dom-if" if="[[isError(connection)]]">
<div class="g-flex-table-cell">[[connection.stateMsg]]</div>
</template>
<template is="dom-if" if="[[!isError(connection)]]">
<div class="g-flex-table-cell">[[localize(connection.state)]]</div>
</template>
</div>
<template is="dom-repeat" items="[[connection.parameters]]" as="param">
<div class="g-flex-table-row">
<div class="g-flex-table-cell label">[[param.id]]</div>
<div class="g-flex-table-cell">[[param.value]]</div>
</div>
</template>
</div>
<div class="card-actions">
<dfiv class="card-actions-right">
<paper-button on-tap="connect" raised>[[localize('connect')]]
</paper-button>
<paper-button on-tap="disconnect" raised>[[localize('disconnect')]]
</paper-button>
</dfiv>
</div>
</paper-card>
</template>
</div>
<iron-ajax id="ajaxGetPlc"
url="[[baseRestPath]]/plc/state"
content-type="application/json"
handle-as="json"
method="GET"
on-response="onGetPlcResponse"
on-error="onRequestError"></iron-ajax>
<iron-ajax id="ajaxGetConnections"
url="[[baseRestPath]]/plc/connections"
content-type="application/json"
handle-as="json"
method="GET"
on-response="onGetConnectionsResponse"
on-error="onRequestError"></iron-ajax>
<iron-ajax id="ajaxPutPlcState"
content-type="application/json"
handle-as="json"
method="PUT"
on-response="_refresh"
on-error="onAjaxError"></iron-ajax>
<iron-ajax id="ajaxPutConnectionState"
content-type="application/json"
handle-as="json"
method="PUT"
on-response="_refresh"
on-error="onAjaxError"></iron-ajax>
</template>
<script>
Polymer({
is: 'strolch-wc-plc-connections',
behaviors: [
StrolchLocalizeBehavior, StrolchPlcBehavior
],
properties: {
plc: {
type: Object
},
connections: {
type: Array,
value: []
}
},
onGetConnectionsResponse: function (e) {
this.connections = e.detail.response.data;
},
onGetPlcResponse: function (e) {
this.plc = e.detail.response.data;
},
isError: function (connection) {
return connection != null && connection.state === "Failed";
},
connect: function (e) {
this.sendSwitchState(e.model.connection.id, "Connected");
},
disconnect: function (e) {
this.sendSwitchState(e.model.connection.id, "Disconnected");
},
sendSwitchState: function (connectionId, state) {
this.$.ajaxPutConnectionState.url = this.baseRestPath + "/plc/connections/" + connectionId + "/state/" + state;
this.$.ajaxPutConnectionState.body = null;
this.$.ajaxPutConnectionState.generateRequest();
},
startPlc: function (e) {
this.sendPlcState("Started");
},
stopPlc: function (e) {
this.sendPlcState("Stopped");
},
reconfigurePlc: function (e) {
this.sendPlcState("Configured");
},
sendPlcState: function (state) {
this.$.ajaxPutPlcState.url = this.baseRestPath + "/plc/state/" + state;
this.$.ajaxPutPlcState.body = null;
this.$.ajaxPutPlcState.generateRequest();
},
reload: function (e) {
this._refresh();
},
_refresh: function (e) {
this.$.ajaxGetPlc.generateRequest();
this.$.ajaxGetConnections.generateRequest();
},
onAjaxError: function (e) {
this._refresh();
this.onRequestError(e);
},
});
</script>
</dom-module>

View File

@ -0,0 +1,286 @@
<link rel="import" href="../polymer/polymer.html">
<link rel="import" href="../iron-ajax/iron-ajax.html">
<link rel="import" href="../iron-icons/iron-icons.html">
<link rel="import" href="../iron-icons/notification-icons.html">
<link rel="import" href="../iron-icons/maps-icons.html">
<link rel="import" href="../iron-icons/av-icons.html">
<link rel="import" href="../iron-icons/communication-icons.html">
<link rel="import" href="../iron-icons/device-icons.html">
<link rel="import" href="../iron-icons/hardware-icons.html">
<link rel="import" href="../iron-icons/editor-icons.html">
<link rel="import" href="../iron-icons/places-icons.html">
<link rel="import" href="../iron-icons/social-icons.html">
<link rel="import" href="../paper-material/paper-material.html">
<link rel="import" href="../paper-card/paper-card.html">
<link rel="import" href="../paper-button/paper-button.html">
<link rel="import" href="../paper-input/paper-input.html">
<link rel="import" href="../paper-icon-button/paper-icon-button.html">
<link rel="import" href="../paper-toggle-button/paper-toggle-button.html">
<link rel="import" href="../paper-radio-group/paper-radio-group.html">
<link rel="import" href="../strolch-wc-localize-behavior/strolch-wc-localize-behavior.html">
<link rel="import" href="./strolch-wc-plc-style.html">
<link rel="import" href="./strolch-wc-plc-behavior.html">
<dom-module id="strolch-wc-plc-logical-device">
<template>
<style is="custom-style" include="strolch-wc-plc-style">
</style>
<paper-card elevation="1">
<div class="header"><h3>[[device.name]]</h3></div>
<div class="card-content">
<div class="g-row">
<div class="g-5">
<h4>[[localize('addresses')]]</h4>
<template is="dom-repeat" items="[[addresses]]" as="address">
<div class="g-flex-table-row">
<div class="g-flex-table-cell address-field" title="[[address.id]]">
<div class="address">[[address.resource]] - [[address.action]]</div>
<div class="hw-address">[[address.address]]</div>
</div>
<div class="g-flex-table-cell address-field no-grow">
<template is="dom-if" if="[[isBoolean(address.valueType)]]">
<template is="dom-if" if="[[address.value]]">
<iron-icon style="color:#0b8043"
icon="icons:radio-button-checked"></iron-icon>
</template>
<template is="dom-if" if="[[!address.value]]">
<iron-icon style="color:cornflowerblue"
icon="icons:radio-button-unchecked"></iron-icon>
</template>
</template>
<template is="dom-if" if="[[isNumber(address.valueType)]]">
[[address.value]]
</template>
<template is="dom-if" if="[[isString(address.valueType)]]">
[[address.value]]
</template>
</div>
</div>
</template>
</div>
<div class="g-7">
<template is="dom-if" if="[[arrayFilled(telegrams)]]">
<h4>[[localize('telegrams')]]</h4>
<template is="dom-repeat" items="[[telegrams]]" as="telegram">
<div class="g-flex-table-row">
<div class="g-flex-table-cell address-field" title="[[telegram.id]]">
<div class="address">[[telegram.resource]] - [[telegram.action]]</div>
<div class="hw-address">[[telegram.address]]</div>
</div>
<div class="g-flex-table-cell no-grow value-cell">
<template is="dom-if" if="[[isBoolean(telegram.valueType)]]">
<paper-radio-button checked="{{telegram.value}}">[[localize('on')]]
</paper-radio-button>
<paper-radio-button checked="{{!telegram.value}}">[[localize('off')]]
</paper-radio-button>
</template>
<template is="dom-if" if="[[isNumber(telegram.valueType)]]">
<paper-input class="value-input"
value="{{telegram.value}}"
no-label-float></paper-input>
</template>
<template is="dom-if" if="[[isString(telegram.valueType)]]">
<paper-input class="value-input"
value="{{telegram.value}}"
no-label-float></paper-input>
</template>
</div>
<div class="g-flex-table-cell no-grow">
<paper-button raised on-tap="sendTelegram">[[localize('send')]]
</paper-button>
</div>
</div>
</template>
</template>
<template is="dom-if" if="[[arrayFilled(notifications)]]">
<h4>[[localize('notifications')]]</h4>
<template is="dom-repeat" items="[[notifications]]" as="notification">
<div class="g-flex-table-row">
<div class="g-flex-table-cell address-field" title="[[notification.id]]">
<div class="address">[[notification.resource]] - [[notification.action]]</div>
<div class="hw-address">[[notification.address]]</div>
</div>
<div class="g-flex-table-cell no-grow value-cell">
<template is="dom-if" if="[[isBoolean(notification.valueType)]]">
<paper-radio-button checked="{{notification.value}}">[[localize('on')]]
</paper-radio-button>
<paper-radio-button checked="{{!notification.value}}">[[localize('off')]]
</paper-radio-button>
</paper-radio-group>
</template>
<template is="dom-if" if="[[isNumber(notification.valueType)]]">
<paper-input class="value-input"
value="{{notification.value}}"
no-label-float></paper-input>
</template>
<template is="dom-if" if="[[isString(notification.valueType)]]">
<paper-input class="value-input"
value="{{notification.value}}"
no-label-float></paper-input>
</template>
</div>
<div class="g-flex-table-cell no-grow">
<paper-button raised on-tap="sendNotification">[[localize('notify')]]
</paper-button>
</div>
</div>
</template>
</template>
</div>
</div>
</div>
</paper-card>
<iron-ajax id="ajaxGetAddresses"
url="[[baseRestPath]]/plc/logicalDevices/[[logicalDeviceId]]/addresses"
content-type="application/json"
handle-as="json"
method="GET"
on-response="onGetAddressesResponse"
on-error="onRequestError"></iron-ajax>
<iron-ajax id="ajaxGetTelegrams"
url="[[baseRestPath]]/plc/logicalDevices/[[logicalDeviceId]]/telegrams"
content-type="application/json"
handle-as="json"
method="GET"
on-response="onGetTelegramsResponse"
on-error="onRequestError"></iron-ajax>
<iron-ajax id="ajaxGetNotifications"
url="[[baseRestPath]]/plc/logicalDevices/[[logicalDeviceId]]/notifications"
content-type="application/json"
handle-as="json"
method="GET"
on-response="onGetNotificationsResponse"
on-error="onRequestError"></iron-ajax>
<iron-ajax id="ajaxPutAddress"
url="[[baseRestPath]]/plc/addresses"
content-type="application/json"
handle-as="json"
method="PUT"
on-response="_refresh"
on-error="onAjaxError"></iron-ajax>
</template>
<script>
Polymer({
is: 'strolch-wc-plc-logical-device',
behaviors: [
StrolchLocalizeBehavior, StrolchPlcBehavior
],
properties: {
observerHandler: {
type: Object
},
logicalDeviceId: {
type: String
},
device: {
type: Object,
observer: 'deviceChanged'
},
addresses: {
type: Array,
value: []
},
telegrams: {
type: Array,
value: []
},
notifications: {
type: Array,
value: []
}
},
/* Listeners */
onGetAddressesResponse: function (e) {
var addresses = e.detail.response.data;
for (var i = 0; i < addresses.length; i++) {
var a = addresses[i];
this.observerHandler.register(a.objectType, a.type, a.id, this.handleAddressUpdate.bind(this));
}
this.addresses = addresses;
},
onGetTelegramsResponse: function (e) {
this.telegrams = e.detail.response.data;
},
onGetNotificationsResponse: function (e) {
this.notifications = e.detail.response.data;
},
deviceChanged: function (newValue) {
if (newValue != null) {
this.logicalDeviceId = newValue.id;
this.$.ajaxGetAddresses.generateRequest();
this.$.ajaxGetTelegrams.generateRequest();
this.$.ajaxGetNotifications.generateRequest();
}
},
sendTelegram: function (e) {
var a = e.model.telegram;
this.sendAddress("Telegram", a.resource, a.action, a.value);
},
sendNotification: function (e) {
var a = e.model.notification;
this.sendAddress("Notification", a.resource, a.action, a.value);
},
sendAddressNotification: function (e) {
var a = e.model.address;
this.sendAddress("Notification", a.resource, a.action, a.value);
},
sendAddress: function (type, resource, action, value) {
console.log("Sending " + type + " for " + resource + " - " + action + ": " + value);
this.$.ajaxPutAddress.body = {
type: type,
resource: resource,
action: action,
value: value
};
this.$.ajaxPutAddress.generateRequest();
},
_refresh: function () {
console.log("Should refresh!");
},
onAjaxError: function (e) {
this._refresh();
this.onRequestError(e);
},
handleAddressUpdate: function (address) {
for (var i = 0; i < this.addresses.length; i++) {
if (this.addresses[i].id === address.id) {
this.set("addresses." + i + ".value", address.value);
break;
}
}
},
/* Lifecycle */
reload: function () {
}
});
</script>
</dom-module>

View File

@ -0,0 +1,260 @@
<link rel="import" href="../polymer/polymer.html">
<link rel="import" href="../iron-ajax/iron-ajax.html">
<link rel="import" href="../iron-icons/iron-icons.html">
<link rel="import" href="../iron-icons/notification-icons.html">
<link rel="import" href="../iron-icons/maps-icons.html">
<link rel="import" href="../iron-icons/av-icons.html">
<link rel="import" href="../iron-icons/communication-icons.html">
<link rel="import" href="../iron-icons/device-icons.html">
<link rel="import" href="../iron-icons/hardware-icons.html">
<link rel="import" href="../iron-icons/editor-icons.html">
<link rel="import" href="../iron-icons/places-icons.html">
<link rel="import" href="../iron-icons/social-icons.html">
<link rel="import" href="../paper-material/paper-material.html">
<link rel="import" href="../paper-card/paper-card.html">
<link rel="import" href="../paper-button/paper-button.html">
<link rel="import" href="../paper-input/paper-input.html">
<link rel="import" href="../paper-icon-button/paper-icon-button.html">
<link rel="import" href="../paper-toggle-button/paper-toggle-button.html">
<link rel="import" href="../paper-radio-group/paper-radio-group.html">
<link rel="import" href="../strolch-wc-localize-behavior/strolch-wc-localize-behavior.html">
<link rel="import" href="../strolch-wc-ws-observer/strolch-wc-ws-observer.html">
<link rel="import" href="./strolch-wc-plc-style.html">
<link rel="import" href="./strolch-wc-plc-behavior.html">
<link rel="import" href="./strolch-wc-plc-logical-device.html">
<dom-module id="strolch-wc-plc-logical-devices">
<template>
<style is="custom-style" include="strolch-wc-plc-style">
</style>
<div class="actions">
<paper-icon-button class="g-pull-right" icon="refresh" on-tap="_refresh"></paper-icon-button>
</div>
<template is="dom-if" if="[[arrayFilled(virtualAddresses)]]">
<h2>[[localize('virtualAddresses')]]</h2>
<paper-card elevation="1">
<div class="card-content">
<div class="g-row">
<div class="g-12">
<template is="dom-repeat" items="[[virtualAddresses]]" as="address">
<div class="g-flex-table-row">
<div class="g-flex-table-cell address-field">
<div class="address">[[address.resource]] - [[address.action]]</div>
<div class="hw-address">[[address.address]]</div>
</div>
<div class="g-flex-table-cell no-grow value-cell">
<template is="dom-if" if="[[isBoolean(address.type)]]">
<paper-radio-group selected="{{address.value}}" attr-for-selected="name">
<paper-radio-button name="true">[[localize('on')]]
</paper-radio-button>
<paper-radio-button name="false">[[localize('off')]]
</paper-radio-button>
</paper-radio-group>
</template>
<template is="dom-if" if="[[isNumber(address.type)]]">
<paper-input class="value-input"
value="{{address.value}}"
no-label-float></paper-input>
</template>
<template is="dom-if" if="[[isString(address.type)]]">
<paper-input class="value-input"
value="{{address.value}}"
no-label-float></paper-input>
</template>
</div>
<div class="g-flex-table-cell no-grow">
<paper-button raised on-tap="sendAddressNotification">[[localize('notify')]]
</paper-button>
</div>
</div>
</template>
</div>
</div>
</div>
</paper-card>
</template>
<template is="dom-repeat" items="[[devicesByGroup]]" as="group">
<h2>[[group.name]]</h2>
<template is="dom-repeat" items="[[group.data]]" as="device">
<strolch-wc-plc-logical-device device="[[device]]"
base-rest-path="[[baseRestPath]]"
observer-handler="[[observerHandler]]"></strolch-wc-plc-logical-device>
</template>
</template>
<iron-ajax id="ajaxGetDevices"
url="[[baseRestPath]]/plc/logicalDevices"
content-type="application/json"
handle-as="json"
method="GET"
on-response="onGetDevicesResponse"
on-error="onRequestError"></iron-ajax>
<iron-ajax id="ajaxGetVirtualAddresses"
url="[[baseRestPath]]/plc/addresses/virtual"
content-type="application/json"
handle-as="json"
method="GET"
on-response="onGetVirtualAddressesResponse"
on-error="onRequestError"></iron-ajax>
<iron-ajax id="ajaxPutAddress"
url="[[baseRestPath]]/plc/addresses"
content-type="application/json"
handle-as="json"
method="PUT"
on-response="_refresh"
on-error="onAjaxError"></iron-ajax>
<strolch-wc-ws-observer id="observerHandler" ws-path="[[wsObserverPath]]"></strolch-wc-ws-observer>
</template>
<script>
Polymer({
is: 'strolch-wc-plc-logical-devices',
behaviors: [
StrolchLocalizeBehavior, StrolchPlcBehavior
],
properties: {
observerHandler: {
type: Object,
value: function () {
return ObserverHandler;
}
},
registeredForUpdates: {
type: Boolean,
value: false
},
virtualAddresses: {
type: Array,
value: []
},
devicesByGroup: {
type: Array,
value: []
},
wsObserverPath: {
type: String,
value: function () {
return CustomWeb.baseWsPath + "/plc/observer";
}
}
},
/* Listeners */
onGetDevicesResponse: function (e) {
this.devicesByGroup = e.detail.response.data;
},
onGetVirtualAddressesResponse: function (e) {
this.virtualAddresses = e.detail.response.data;
},
sendAddressNotification: function (e) {
var a = e.model.address;
this.sendAddress("Notification", a.resource, a.action, a.value);
},
sendAddress: function (type, resource, action, value) {
console.log("Sending " + type + " for " + resource + " - " + action + ": " + value);
this.$.ajaxPutAddress.body = {
type: type,
resource: resource,
action: action,
value: value
};
this.$.ajaxPutAddress.generateRequest();
},
_refresh: function () {
this.$.ajaxGetDevices.generateRequest();
this.$.ajaxGetVirtualAddresses.generateRequest();
if (!this.registeredForUpdates) {
this.$.observerHandler.register("Resource", "PlcAddress", "strolch-wc-plc-logical-devices", true, this.handleUpdate.bind(this));
this.registeredForUpdates = true;
}
},
onAjaxError: function (e) {
this._refresh();
this.onRequestError(e);
},
/* Lifecycle */
reload: function () {
this._refresh();
},
handleUpdate: function (notifyType, objectType, type, elements) {
console.log("Received updates for " + elements.length + " " + objectType + " " + type + " elements.");
for (var i = 0; i < elements.length; i++) {
this.observerHandler.notifyUpdate(objectType, type, elements[i].id, elements[i]);
}
}
});
var registrations = {};
ObserverHandler = {
register: function (objectType, type, id, callback) {
if (objectType == null || type == null || id == null || callback == null)
throw "One of objectType, type, id or callback is null: " + objectType + ", " + type + ", " + id + ", " + callback;
var byObjectType = registrations[objectType];
if (byObjectType == null) {
byObjectType = {};
registrations[objectType] = byObjectType;
}
var byType = byObjectType[type];
if (byType == null) {
byType = {};
byObjectType[type] = byType;
}
byType[id] = callback;
},
notifyUpdate: function (objectType, type, id, element) {
if (objectType == null || type == null || id == null || element == null)
throw "One of objectType, type, id or element is null: " + objectType + ", " + type + ", " + id + ", " + element;
var byObjectType = registrations[objectType];
if (byObjectType == null) {
console.log("No observers for objectType " + objectType);
return;
}
var byType = byObjectType[type];
if (byType == null) {
console.log("No observers for byType " + byType);
return;
}
var callback = byType[id];
callback(element);
}
}
</script>
</dom-module>

119
strolch-wc-plc-style.html Normal file
View File

@ -0,0 +1,119 @@
<link rel="import" href="../polymer/polymer.html">
<link rel="import" href="../paper-styles/color.html">
<link rel="import" href="../strolch-wc-styles/strolch-wc-styles.html">
<dom-module id="strolch-wc-plc-style">
<template>
<style is="strolch-wc-plc-style" include="strolch-wc-styles">
:host {
display: block;
}
paper-card {
margin-bottom: 10px;
padding: 10px;
width: calc(100% - 20px);
}
paper-icon-button, iron-icon {
color: var(--app-secondary-color);
}
paper-toggle-button {
height: 35px;
}
h2, h3 {
margin-left: 10px;
margin-bottom: 10px;
}
h2, h3, h4 {
margin: 10px 0 0;
padding: 0;
}
.container {
display: flex; /* or inline-flex */
flex-direction: column;
flex-wrap: wrap;
}
.item {
order: 0; /* default is 0 */
flex-grow: 0; /* default 0 */
flex-shrink: 0; /* default 1 */
flex-basis: auto; /* default auto */
}
.actions {
display: flex; /* or inline-flex */
flex-direction: row;
justify-content: flex-end;
}
.card-content {
padding: 0;
}
.card-actions {
display: flex; /* or inline-flex */
flex-direction: row;
justify-content: flex-end;
}
.card-actions-right {
display: flex; /* or inline-flex */
flex-grow: 1; /* default 0 */
justify-content: flex-end;
}
.g-flex-table-cell {
padding: 0;
flex-basis: 150px;
min-height: 32px;
}
.label {
flex-flow: row-reverse;
flex-basis: 150px;
flex-grow: 0;
}
.error-icon {
color: var(--app-dark-warning-bg-color);
}
.no-grow {
flex-grow: 0;
}
.value-input {
width: 120px;
}
.address-field {
display: block;
padding-top: 10px;
}
.address {
display: block;
width: 100%;
}
.hw-address {
display: block;
width: 100%;
font-size: smaller;
color: gray;
}
paper-radio-button {
padding-left: 12px;
}
</style>
</template>
</dom-module>