1
0
Fork 0
mirror of https://git.rwth-aachen.de/acs/public/villas/node/ synced 2025-03-09 00:00:00 +01:00

Merge branch 'node-websocket' into develop

This commit is contained in:
Steffen Vogel 2015-12-09 15:49:54 +01:00
commit 556aaf48b2
25 changed files with 29686 additions and 51 deletions

3
.gitmodules vendored
View file

@ -2,3 +2,6 @@
path = clients/rtds
url = git@github.com:RWTH-ACS/GTFPGA.git
branch = master
[submodule "contrib/websocket/flot"]
path = contrib/websocket/flot
url = https://github.com/flot/flot.git

View file

@ -60,6 +60,13 @@ ifeq ($(shell pkg-config libcurl jansson uuid; echo $$?),0)
LIB_LDLIBS += $(shell pkg-config --libs libcurl jansson uuid)
endif
# Enable WebSocket support
ifeq ($(shell pkg-config libwebsockets; echo $$?),0)
LIB_OBJS += websocket.o
LIB_CFLAGS += $(shell pkg-config --cflags libwebsockets)
LIB_LDLIBS += $(shell pkg-config --libs libwebsockets)
endif
# Enable OPAL-RT Asynchronous Process support (will result in 32bit binary!!!)
ifneq (,$(wildcard $(OPALDIR)/include_target/AsyncApi.h))
LIB_OBJS += opal.o

149
contrib/websocket/app.js Normal file
View file

@ -0,0 +1,149 @@
// global variables
var connection;
var timer;
var seq = 0;
var node = 'websocket'
var url = 'ws://10.211.55.6:8080/' + node;
var protocol = ['live'];
var plotData = [];
var plotOptions = {
xaxis: {
mode: 'time'
},
legend: {
show: true
}
};
var xDelta = 10*1000;
$(document).on('ready', function() {
$.getJSON('/nodes.json', function(data) {
$(data).each(function(index, node) {
$(".node-selector").append($("<li>").text(node));
});
});
$('#slider').slider({
min : 0,
max : 100,
slide : function(event, ui) {
sendMsg(Date.now(), [ ui.value ]);
}
});
wsConnect();
});
$(window).on('beforeunload', wsDisconnect);
function plotUpdate() {
var data = [];
// add data to arrays
for (var i = 0; i < plotData.length; i++) {
// remove old values
//while (plotData[i].length > 0 && plotData[i][0][0] < (Date.now() - xDelta))
// plotData[i].shift()
data[i] = {
data : plotData[i],
shadowSize : 0,
label : "Index " + String(i),
lines : {
lineWidth: 2
}
}
}
var options = {
xaxis: {
min: Date.now() - xDelta,
max: Date.now()
}
}
/* update plot */
$.plot('#placeholder', data, $.extend(true, options, plotOptions));
}
function wsDisconnect() {
connection.close();
$('#connectionStatus')
.text('Disconnected')
.css('color', 'red');
plotData = [];
clearInterval(timer);
}
function wsConnect() {
connection = new WebSocket(url, protocol);
connection.onopen = function() {
$('#connectionStatus')
.text('Connected')
.css('color', 'green');
timer = setInterval(plotUpdate, 1.0 / 25);
};
connection.onclose = function() {
wsDisconnect();
setTimeout(wsConnect, 3000); // retry
};
connection.onerror = function(error) {
$('#connectionStatus').text(function() {
return 'Status: Error: ' + error.message;
});
};
connection.onmessage = function(e) {
console.log(e.data);
var res = e.data.split(/\s+/).map(Number);
var timestamp = res[0] * 1000;
var values = res.slice(1)
// add empty arrays for data
while (plotData.length < values.length)
plotData.push([]);
// add data to arrays
for (var i = 0; i < values.length; i++)
plotData[i].push([timestamp, values[i]]);
};
};
function sendMsg(ts, values) {
connection.send(ts / 1000 + "(" + seq + ") " + values.join(" "));
seq += 1;
}
/* Control event handlers */
function onButtonClick(value) {
sendMsg(Date.now(), [ value ]);
}
function onTextChange() {
sendMsg(Date.now(), [ 1 ]);
}
/* Helpers */
function wsUrl(endpoint) {
var l = window.location;
var protocol = (l.protocol === "https:") ? "wss://" : "ws://";
var port = ((l.port != 80) && (l.port != 443)) ? ":" + l.port : "";
return protocol + l.hostname + port + endpoint;
}

View file

@ -0,0 +1 @@
../../doc/pictures/eonerc_logo.png

@ -0,0 +1 @@
Subproject commit 958e5fd43c6dff4bab3e1fd5cb6109df5c1e8003

View file

@ -0,0 +1,61 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>S2SS / WebSocket Mockup</title>
<link rel="stylesheet" href="style.css" />
<link rel="stylesheet" href="jquery-ui/jquery-ui.theme.css" />
<link rel="stylesheet" href="jquery-ui/jquery-ui.structure.css" />
<script src="jquery/jquery.js"></script>
<script src="jquery-ui/jquery-ui.js"></script>
<script src="flot/jquery.flot.js"></script>
<script src="flot/jquery.flot.time.js"></script>
<script src="app.js"></script>
</head>
<body>
<div id="wrapper">
<div id="header">
<img class="logo" alt="EONERC" src="eonerc_logo.png" />
<h1>S2SS / WebSocket Mockup</h1>
</div>
<div id="container">
<ul class="node-selector"></ul>
<div class="plot-container">
<div id="placeholder" class="plot-placeholder"></div>
</div>
<div class="controls">
<p>
<span>Trigger:</span>
<button onclick="onButtonClick(1)">On</button>
<button onclick="onButtonClick(0)">Off</button>
</p>
<p>
<span>Slider:</span>
<div id="slider"></div>
</p>
<p>
<span>Value:</span>
<input type="text" onchange="onTextChange()" />
</p>
</div>
<div class="connection">
<span>Status:</span> <span id="connectionStatus"></span>
<button id="pause" onclick="wsDisconnect()">Pause</button>
<button id="pause" onclick="wsConnect()">Play</button>
</div>
</div>
<div id="footer">
<p>Copyright 2015: Institute for Automation of Complex Power Systems,
EON Energy Research Center, RWTH Aachen University, Germany</p>
<p>Authors: <a href="mailto:mgrigul@eonerc.rwth-aachen.de">Markus Grigul</a>,
<a href="mailto:stvogel@eonerc.rwth-aachen.de">Steffen Vogel</a></p>
</div>
</div>
</body>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

1225
contrib/websocket/jquery-ui/jquery-ui.css vendored Normal file

File diff suppressed because it is too large Load diff

16617
contrib/websocket/jquery-ui/jquery-ui.js vendored Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,833 @@
/*!
* jQuery UI CSS Framework 1.11.4
* http://jqueryui.com
*
* Copyright jQuery Foundation and other contributors
* Released under the MIT license.
* http://jquery.org/license
*
* http://api.jqueryui.com/category/theming/
*/
/* Layout helpers
----------------------------------*/
.ui-helper-hidden {
display: none;
}
.ui-helper-hidden-accessible {
border: 0;
clip: rect(0 0 0 0);
height: 1px;
margin: -1px;
overflow: hidden;
padding: 0;
position: absolute;
width: 1px;
}
.ui-helper-reset {
margin: 0;
padding: 0;
border: 0;
outline: 0;
line-height: 1.3;
text-decoration: none;
font-size: 100%;
list-style: none;
}
.ui-helper-clearfix:before,
.ui-helper-clearfix:after {
content: "";
display: table;
border-collapse: collapse;
}
.ui-helper-clearfix:after {
clear: both;
}
.ui-helper-clearfix {
min-height: 0; /* support: IE7 */
}
.ui-helper-zfix {
width: 100%;
height: 100%;
top: 0;
left: 0;
position: absolute;
opacity: 0;
filter:Alpha(Opacity=0); /* support: IE8 */
}
.ui-front {
z-index: 100;
}
/* Interaction Cues
----------------------------------*/
.ui-state-disabled {
cursor: default !important;
}
/* Icons
----------------------------------*/
/* states and images */
.ui-icon {
display: block;
text-indent: -99999px;
overflow: hidden;
background-repeat: no-repeat;
}
/* Misc visuals
----------------------------------*/
/* Overlays */
.ui-widget-overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
.ui-draggable-handle {
-ms-touch-action: none;
touch-action: none;
}
.ui-resizable {
position: relative;
}
.ui-resizable-handle {
position: absolute;
font-size: 0.1px;
display: block;
-ms-touch-action: none;
touch-action: none;
}
.ui-resizable-disabled .ui-resizable-handle,
.ui-resizable-autohide .ui-resizable-handle {
display: none;
}
.ui-resizable-n {
cursor: n-resize;
height: 7px;
width: 100%;
top: -5px;
left: 0;
}
.ui-resizable-s {
cursor: s-resize;
height: 7px;
width: 100%;
bottom: -5px;
left: 0;
}
.ui-resizable-e {
cursor: e-resize;
width: 7px;
right: -5px;
top: 0;
height: 100%;
}
.ui-resizable-w {
cursor: w-resize;
width: 7px;
left: -5px;
top: 0;
height: 100%;
}
.ui-resizable-se {
cursor: se-resize;
width: 12px;
height: 12px;
right: 1px;
bottom: 1px;
}
.ui-resizable-sw {
cursor: sw-resize;
width: 9px;
height: 9px;
left: -5px;
bottom: -5px;
}
.ui-resizable-nw {
cursor: nw-resize;
width: 9px;
height: 9px;
left: -5px;
top: -5px;
}
.ui-resizable-ne {
cursor: ne-resize;
width: 9px;
height: 9px;
right: -5px;
top: -5px;
}
.ui-selectable {
-ms-touch-action: none;
touch-action: none;
}
.ui-selectable-helper {
position: absolute;
z-index: 100;
border: 1px dotted black;
}
.ui-sortable-handle {
-ms-touch-action: none;
touch-action: none;
}
.ui-accordion .ui-accordion-header {
display: block;
cursor: pointer;
position: relative;
margin: 2px 0 0 0;
padding: .5em .5em .5em .7em;
min-height: 0; /* support: IE7 */
font-size: 100%;
}
.ui-accordion .ui-accordion-icons {
padding-left: 2.2em;
}
.ui-accordion .ui-accordion-icons .ui-accordion-icons {
padding-left: 2.2em;
}
.ui-accordion .ui-accordion-header .ui-accordion-header-icon {
position: absolute;
left: .5em;
top: 50%;
margin-top: -8px;
}
.ui-accordion .ui-accordion-content {
padding: 1em 2.2em;
border-top: 0;
overflow: auto;
}
.ui-autocomplete {
position: absolute;
top: 0;
left: 0;
cursor: default;
}
.ui-button {
display: inline-block;
position: relative;
padding: 0;
line-height: normal;
margin-right: .1em;
cursor: pointer;
vertical-align: middle;
text-align: center;
overflow: visible; /* removes extra width in IE */
}
.ui-button,
.ui-button:link,
.ui-button:visited,
.ui-button:hover,
.ui-button:active {
text-decoration: none;
}
/* to make room for the icon, a width needs to be set here */
.ui-button-icon-only {
width: 2.2em;
}
/* button elements seem to need a little more width */
button.ui-button-icon-only {
width: 2.4em;
}
.ui-button-icons-only {
width: 3.4em;
}
button.ui-button-icons-only {
width: 3.7em;
}
/* button text element */
.ui-button .ui-button-text {
display: block;
line-height: normal;
}
.ui-button-text-only .ui-button-text {
padding: .4em 1em;
}
.ui-button-icon-only .ui-button-text,
.ui-button-icons-only .ui-button-text {
padding: .4em;
text-indent: -9999999px;
}
.ui-button-text-icon-primary .ui-button-text,
.ui-button-text-icons .ui-button-text {
padding: .4em 1em .4em 2.1em;
}
.ui-button-text-icon-secondary .ui-button-text,
.ui-button-text-icons .ui-button-text {
padding: .4em 2.1em .4em 1em;
}
.ui-button-text-icons .ui-button-text {
padding-left: 2.1em;
padding-right: 2.1em;
}
/* no icon support for input elements, provide padding by default */
input.ui-button {
padding: .4em 1em;
}
/* button icon element(s) */
.ui-button-icon-only .ui-icon,
.ui-button-text-icon-primary .ui-icon,
.ui-button-text-icon-secondary .ui-icon,
.ui-button-text-icons .ui-icon,
.ui-button-icons-only .ui-icon {
position: absolute;
top: 50%;
margin-top: -8px;
}
.ui-button-icon-only .ui-icon {
left: 50%;
margin-left: -8px;
}
.ui-button-text-icon-primary .ui-button-icon-primary,
.ui-button-text-icons .ui-button-icon-primary,
.ui-button-icons-only .ui-button-icon-primary {
left: .5em;
}
.ui-button-text-icon-secondary .ui-button-icon-secondary,
.ui-button-text-icons .ui-button-icon-secondary,
.ui-button-icons-only .ui-button-icon-secondary {
right: .5em;
}
/* button sets */
.ui-buttonset {
margin-right: 7px;
}
.ui-buttonset .ui-button {
margin-left: 0;
margin-right: -.3em;
}
/* workarounds */
/* reset extra padding in Firefox, see h5bp.com/l */
input.ui-button::-moz-focus-inner,
button.ui-button::-moz-focus-inner {
border: 0;
padding: 0;
}
.ui-datepicker {
width: 17em;
padding: .2em .2em 0;
display: none;
}
.ui-datepicker .ui-datepicker-header {
position: relative;
padding: .2em 0;
}
.ui-datepicker .ui-datepicker-prev,
.ui-datepicker .ui-datepicker-next {
position: absolute;
top: 2px;
width: 1.8em;
height: 1.8em;
}
.ui-datepicker .ui-datepicker-prev-hover,
.ui-datepicker .ui-datepicker-next-hover {
top: 1px;
}
.ui-datepicker .ui-datepicker-prev {
left: 2px;
}
.ui-datepicker .ui-datepicker-next {
right: 2px;
}
.ui-datepicker .ui-datepicker-prev-hover {
left: 1px;
}
.ui-datepicker .ui-datepicker-next-hover {
right: 1px;
}
.ui-datepicker .ui-datepicker-prev span,
.ui-datepicker .ui-datepicker-next span {
display: block;
position: absolute;
left: 50%;
margin-left: -8px;
top: 50%;
margin-top: -8px;
}
.ui-datepicker .ui-datepicker-title {
margin: 0 2.3em;
line-height: 1.8em;
text-align: center;
}
.ui-datepicker .ui-datepicker-title select {
font-size: 1em;
margin: 1px 0;
}
.ui-datepicker select.ui-datepicker-month,
.ui-datepicker select.ui-datepicker-year {
width: 45%;
}
.ui-datepicker table {
width: 100%;
font-size: .9em;
border-collapse: collapse;
margin: 0 0 .4em;
}
.ui-datepicker th {
padding: .7em .3em;
text-align: center;
font-weight: bold;
border: 0;
}
.ui-datepicker td {
border: 0;
padding: 1px;
}
.ui-datepicker td span,
.ui-datepicker td a {
display: block;
padding: .2em;
text-align: right;
text-decoration: none;
}
.ui-datepicker .ui-datepicker-buttonpane {
background-image: none;
margin: .7em 0 0 0;
padding: 0 .2em;
border-left: 0;
border-right: 0;
border-bottom: 0;
}
.ui-datepicker .ui-datepicker-buttonpane button {
float: right;
margin: .5em .2em .4em;
cursor: pointer;
padding: .2em .6em .3em .6em;
width: auto;
overflow: visible;
}
.ui-datepicker .ui-datepicker-buttonpane button.ui-datepicker-current {
float: left;
}
/* with multiple calendars */
.ui-datepicker.ui-datepicker-multi {
width: auto;
}
.ui-datepicker-multi .ui-datepicker-group {
float: left;
}
.ui-datepicker-multi .ui-datepicker-group table {
width: 95%;
margin: 0 auto .4em;
}
.ui-datepicker-multi-2 .ui-datepicker-group {
width: 50%;
}
.ui-datepicker-multi-3 .ui-datepicker-group {
width: 33.3%;
}
.ui-datepicker-multi-4 .ui-datepicker-group {
width: 25%;
}
.ui-datepicker-multi .ui-datepicker-group-last .ui-datepicker-header,
.ui-datepicker-multi .ui-datepicker-group-middle .ui-datepicker-header {
border-left-width: 0;
}
.ui-datepicker-multi .ui-datepicker-buttonpane {
clear: left;
}
.ui-datepicker-row-break {
clear: both;
width: 100%;
font-size: 0;
}
/* RTL support */
.ui-datepicker-rtl {
direction: rtl;
}
.ui-datepicker-rtl .ui-datepicker-prev {
right: 2px;
left: auto;
}
.ui-datepicker-rtl .ui-datepicker-next {
left: 2px;
right: auto;
}
.ui-datepicker-rtl .ui-datepicker-prev:hover {
right: 1px;
left: auto;
}
.ui-datepicker-rtl .ui-datepicker-next:hover {
left: 1px;
right: auto;
}
.ui-datepicker-rtl .ui-datepicker-buttonpane {
clear: right;
}
.ui-datepicker-rtl .ui-datepicker-buttonpane button {
float: left;
}
.ui-datepicker-rtl .ui-datepicker-buttonpane button.ui-datepicker-current,
.ui-datepicker-rtl .ui-datepicker-group {
float: right;
}
.ui-datepicker-rtl .ui-datepicker-group-last .ui-datepicker-header,
.ui-datepicker-rtl .ui-datepicker-group-middle .ui-datepicker-header {
border-right-width: 0;
border-left-width: 1px;
}
.ui-dialog {
overflow: hidden;
position: absolute;
top: 0;
left: 0;
padding: .2em;
outline: 0;
}
.ui-dialog .ui-dialog-titlebar {
padding: .4em 1em;
position: relative;
}
.ui-dialog .ui-dialog-title {
float: left;
margin: .1em 0;
white-space: nowrap;
width: 90%;
overflow: hidden;
text-overflow: ellipsis;
}
.ui-dialog .ui-dialog-titlebar-close {
position: absolute;
right: .3em;
top: 50%;
width: 20px;
margin: -10px 0 0 0;
padding: 1px;
height: 20px;
}
.ui-dialog .ui-dialog-content {
position: relative;
border: 0;
padding: .5em 1em;
background: none;
overflow: auto;
}
.ui-dialog .ui-dialog-buttonpane {
text-align: left;
border-width: 1px 0 0 0;
background-image: none;
margin-top: .5em;
padding: .3em 1em .5em .4em;
}
.ui-dialog .ui-dialog-buttonpane .ui-dialog-buttonset {
float: right;
}
.ui-dialog .ui-dialog-buttonpane button {
margin: .5em .4em .5em 0;
cursor: pointer;
}
.ui-dialog .ui-resizable-se {
width: 12px;
height: 12px;
right: -5px;
bottom: -5px;
background-position: 16px 16px;
}
.ui-draggable .ui-dialog-titlebar {
cursor: move;
}
.ui-menu {
list-style: none;
padding: 0;
margin: 0;
display: block;
outline: none;
}
.ui-menu .ui-menu {
position: absolute;
}
.ui-menu .ui-menu-item {
position: relative;
margin: 0;
padding: 3px 1em 3px .4em;
cursor: pointer;
min-height: 0; /* support: IE7 */
/* support: IE10, see #8844 */
list-style-image: url("data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7");
}
.ui-menu .ui-menu-divider {
margin: 5px 0;
height: 0;
font-size: 0;
line-height: 0;
border-width: 1px 0 0 0;
}
.ui-menu .ui-state-focus,
.ui-menu .ui-state-active {
margin: -1px;
}
/* icon support */
.ui-menu-icons {
position: relative;
}
.ui-menu-icons .ui-menu-item {
padding-left: 2em;
}
/* left-aligned */
.ui-menu .ui-icon {
position: absolute;
top: 0;
bottom: 0;
left: .2em;
margin: auto 0;
}
/* right-aligned */
.ui-menu .ui-menu-icon {
left: auto;
right: 0;
}
.ui-progressbar {
height: 2em;
text-align: left;
overflow: hidden;
}
.ui-progressbar .ui-progressbar-value {
margin: -1px;
height: 100%;
}
.ui-progressbar .ui-progressbar-overlay {
background: url("data:image/gif;base64,R0lGODlhKAAoAIABAAAAAP///yH/C05FVFNDQVBFMi4wAwEAAAAh+QQJAQABACwAAAAAKAAoAAACkYwNqXrdC52DS06a7MFZI+4FHBCKoDeWKXqymPqGqxvJrXZbMx7Ttc+w9XgU2FB3lOyQRWET2IFGiU9m1frDVpxZZc6bfHwv4c1YXP6k1Vdy292Fb6UkuvFtXpvWSzA+HycXJHUXiGYIiMg2R6W459gnWGfHNdjIqDWVqemH2ekpObkpOlppWUqZiqr6edqqWQAAIfkECQEAAQAsAAAAACgAKAAAApSMgZnGfaqcg1E2uuzDmmHUBR8Qil95hiPKqWn3aqtLsS18y7G1SzNeowWBENtQd+T1JktP05nzPTdJZlR6vUxNWWjV+vUWhWNkWFwxl9VpZRedYcflIOLafaa28XdsH/ynlcc1uPVDZxQIR0K25+cICCmoqCe5mGhZOfeYSUh5yJcJyrkZWWpaR8doJ2o4NYq62lAAACH5BAkBAAEALAAAAAAoACgAAAKVDI4Yy22ZnINRNqosw0Bv7i1gyHUkFj7oSaWlu3ovC8GxNso5fluz3qLVhBVeT/Lz7ZTHyxL5dDalQWPVOsQWtRnuwXaFTj9jVVh8pma9JjZ4zYSj5ZOyma7uuolffh+IR5aW97cHuBUXKGKXlKjn+DiHWMcYJah4N0lYCMlJOXipGRr5qdgoSTrqWSq6WFl2ypoaUAAAIfkECQEAAQAsAAAAACgAKAAAApaEb6HLgd/iO7FNWtcFWe+ufODGjRfoiJ2akShbueb0wtI50zm02pbvwfWEMWBQ1zKGlLIhskiEPm9R6vRXxV4ZzWT2yHOGpWMyorblKlNp8HmHEb/lCXjcW7bmtXP8Xt229OVWR1fod2eWqNfHuMjXCPkIGNileOiImVmCOEmoSfn3yXlJWmoHGhqp6ilYuWYpmTqKUgAAIfkECQEAAQAsAAAAACgAKAAAApiEH6kb58biQ3FNWtMFWW3eNVcojuFGfqnZqSebuS06w5V80/X02pKe8zFwP6EFWOT1lDFk8rGERh1TTNOocQ61Hm4Xm2VexUHpzjymViHrFbiELsefVrn6XKfnt2Q9G/+Xdie499XHd2g4h7ioOGhXGJboGAnXSBnoBwKYyfioubZJ2Hn0RuRZaflZOil56Zp6iioKSXpUAAAh+QQJAQABACwAAAAAKAAoAAACkoQRqRvnxuI7kU1a1UU5bd5tnSeOZXhmn5lWK3qNTWvRdQxP8qvaC+/yaYQzXO7BMvaUEmJRd3TsiMAgswmNYrSgZdYrTX6tSHGZO73ezuAw2uxuQ+BbeZfMxsexY35+/Qe4J1inV0g4x3WHuMhIl2jXOKT2Q+VU5fgoSUI52VfZyfkJGkha6jmY+aaYdirq+lQAACH5BAkBAAEALAAAAAAoACgAAAKWBIKpYe0L3YNKToqswUlvznigd4wiR4KhZrKt9Upqip61i9E3vMvxRdHlbEFiEXfk9YARYxOZZD6VQ2pUunBmtRXo1Lf8hMVVcNl8JafV38aM2/Fu5V16Bn63r6xt97j09+MXSFi4BniGFae3hzbH9+hYBzkpuUh5aZmHuanZOZgIuvbGiNeomCnaxxap2upaCZsq+1kAACH5BAkBAAEALAAAAAAoACgAAAKXjI8By5zf4kOxTVrXNVlv1X0d8IGZGKLnNpYtm8Lr9cqVeuOSvfOW79D9aDHizNhDJidFZhNydEahOaDH6nomtJjp1tutKoNWkvA6JqfRVLHU/QUfau9l2x7G54d1fl995xcIGAdXqMfBNadoYrhH+Mg2KBlpVpbluCiXmMnZ2Sh4GBqJ+ckIOqqJ6LmKSllZmsoq6wpQAAAh+QQJAQABACwAAAAAKAAoAAAClYx/oLvoxuJDkU1a1YUZbJ59nSd2ZXhWqbRa2/gF8Gu2DY3iqs7yrq+xBYEkYvFSM8aSSObE+ZgRl1BHFZNr7pRCavZ5BW2142hY3AN/zWtsmf12p9XxxFl2lpLn1rseztfXZjdIWIf2s5dItwjYKBgo9yg5pHgzJXTEeGlZuenpyPmpGQoKOWkYmSpaSnqKileI2FAAACH5BAkBAAEALAAAAAAoACgAAAKVjB+gu+jG4kORTVrVhRlsnn2dJ3ZleFaptFrb+CXmO9OozeL5VfP99HvAWhpiUdcwkpBH3825AwYdU8xTqlLGhtCosArKMpvfa1mMRae9VvWZfeB2XfPkeLmm18lUcBj+p5dnN8jXZ3YIGEhYuOUn45aoCDkp16hl5IjYJvjWKcnoGQpqyPlpOhr3aElaqrq56Bq7VAAAOw==");
height: 100%;
filter: alpha(opacity=25); /* support: IE8 */
opacity: 0.25;
}
.ui-progressbar-indeterminate .ui-progressbar-value {
background-image: none;
}
.ui-selectmenu-menu {
padding: 0;
margin: 0;
position: absolute;
top: 0;
left: 0;
display: none;
}
.ui-selectmenu-menu .ui-menu {
overflow: auto;
/* Support: IE7 */
overflow-x: hidden;
padding-bottom: 1px;
}
.ui-selectmenu-menu .ui-menu .ui-selectmenu-optgroup {
font-size: 1em;
font-weight: bold;
line-height: 1.5;
padding: 2px 0.4em;
margin: 0.5em 0 0 0;
height: auto;
border: 0;
}
.ui-selectmenu-open {
display: block;
}
.ui-selectmenu-button {
display: inline-block;
overflow: hidden;
position: relative;
text-decoration: none;
cursor: pointer;
}
.ui-selectmenu-button span.ui-icon {
right: 0.5em;
left: auto;
margin-top: -8px;
position: absolute;
top: 50%;
}
.ui-selectmenu-button span.ui-selectmenu-text {
text-align: left;
padding: 0.4em 2.1em 0.4em 1em;
display: block;
line-height: 1.4;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.ui-slider {
position: relative;
text-align: left;
}
.ui-slider .ui-slider-handle {
position: absolute;
z-index: 2;
width: 1.2em;
height: 1.2em;
cursor: default;
-ms-touch-action: none;
touch-action: none;
}
.ui-slider .ui-slider-range {
position: absolute;
z-index: 1;
font-size: .7em;
display: block;
border: 0;
background-position: 0 0;
}
/* support: IE8 - See #6727 */
.ui-slider.ui-state-disabled .ui-slider-handle,
.ui-slider.ui-state-disabled .ui-slider-range {
filter: inherit;
}
.ui-slider-horizontal {
height: .8em;
}
.ui-slider-horizontal .ui-slider-handle {
top: -.3em;
margin-left: -.6em;
}
.ui-slider-horizontal .ui-slider-range {
top: 0;
height: 100%;
}
.ui-slider-horizontal .ui-slider-range-min {
left: 0;
}
.ui-slider-horizontal .ui-slider-range-max {
right: 0;
}
.ui-slider-vertical {
width: .8em;
height: 100px;
}
.ui-slider-vertical .ui-slider-handle {
left: -.3em;
margin-left: 0;
margin-bottom: -.6em;
}
.ui-slider-vertical .ui-slider-range {
left: 0;
width: 100%;
}
.ui-slider-vertical .ui-slider-range-min {
bottom: 0;
}
.ui-slider-vertical .ui-slider-range-max {
top: 0;
}
.ui-spinner {
position: relative;
display: inline-block;
overflow: hidden;
padding: 0;
vertical-align: middle;
}
.ui-spinner-input {
border: none;
background: none;
color: inherit;
padding: 0;
margin: .2em 0;
vertical-align: middle;
margin-left: .4em;
margin-right: 22px;
}
.ui-spinner-button {
width: 16px;
height: 50%;
font-size: .5em;
padding: 0;
margin: 0;
text-align: center;
position: absolute;
cursor: default;
display: block;
overflow: hidden;
right: 0;
}
/* more specificity required here to override default borders */
.ui-spinner a.ui-spinner-button {
border-top: none;
border-bottom: none;
border-right: none;
}
/* vertically center icon */
.ui-spinner .ui-icon {
position: absolute;
margin-top: -8px;
top: 50%;
left: 0;
}
.ui-spinner-up {
top: 0;
}
.ui-spinner-down {
bottom: 0;
}
/* TR overrides */
.ui-spinner .ui-icon-triangle-1-s {
/* need to fix icons sprite */
background-position: -65px -16px;
}
.ui-tabs {
position: relative;/* position: relative prevents IE scroll bug (element with position: relative inside container with overflow: auto appear as "fixed") */
padding: .2em;
}
.ui-tabs .ui-tabs-nav {
margin: 0;
padding: .2em .2em 0;
}
.ui-tabs .ui-tabs-nav li {
list-style: none;
float: left;
position: relative;
top: 0;
margin: 1px .2em 0 0;
border-bottom-width: 0;
padding: 0;
white-space: nowrap;
}
.ui-tabs .ui-tabs-nav .ui-tabs-anchor {
float: left;
padding: .5em 1em;
text-decoration: none;
}
.ui-tabs .ui-tabs-nav li.ui-tabs-active {
margin-bottom: -1px;
padding-bottom: 1px;
}
.ui-tabs .ui-tabs-nav li.ui-tabs-active .ui-tabs-anchor,
.ui-tabs .ui-tabs-nav li.ui-state-disabled .ui-tabs-anchor,
.ui-tabs .ui-tabs-nav li.ui-tabs-loading .ui-tabs-anchor {
cursor: text;
}
.ui-tabs-collapsible .ui-tabs-nav li.ui-tabs-active .ui-tabs-anchor {
cursor: pointer;
}
.ui-tabs .ui-tabs-panel {
display: block;
border-width: 0;
padding: 1em 1.4em;
background: none;
}
.ui-tooltip {
padding: 8px;
position: absolute;
z-index: 9999;
max-width: 300px;
-webkit-box-shadow: 0 0 5px #aaa;
box-shadow: 0 0 5px #aaa;
}
body .ui-tooltip {
border-width: 2px;
}

View file

@ -0,0 +1,410 @@
/*!
* jQuery UI CSS Framework 1.11.4
* http://jqueryui.com
*
* Copyright jQuery Foundation and other contributors
* Released under the MIT license.
* http://jquery.org/license
*
* http://api.jqueryui.com/category/theming/
*
* To view and modify this theme, visit http://jqueryui.com/themeroller/?ffDefault=Arial%2CHelvetica%2Csans-serif&fsDefault=1em&fwDefault=normal&cornerRadius=3px&bgColorHeader=e9e9e9&bgTextureHeader=flat&borderColorHeader=dddddd&fcHeader=333333&iconColorHeader=444444&bgColorContent=ffffff&bgTextureContent=flat&borderColorContent=dddddd&fcContent=333333&iconColorContent=444444&bgColorDefault=f6f6f6&bgTextureDefault=flat&borderColorDefault=c5c5c5&fcDefault=454545&iconColorDefault=777777&bgColorHover=ededed&bgTextureHover=flat&borderColorHover=cccccc&fcHover=2b2b2b&iconColorHover=555555&bgColorActive=007fff&bgTextureActive=flat&borderColorActive=003eff&fcActive=ffffff&iconColorActive=ffffff&bgColorHighlight=fffa90&bgTextureHighlight=flat&borderColorHighlight=dad55e&fcHighlight=777620&iconColorHighlight=777620&bgColorError=fddfdf&bgTextureError=flat&borderColorError=f1a899&fcError=5f3f3f&iconColorError=cc0000&bgColorOverlay=aaaaaa&bgTextureOverlay=flat&bgImgOpacityOverlay=0&opacityOverlay=30&bgColorShadow=666666&bgTextureShadow=flat&bgImgOpacityShadow=0&opacityShadow=30&thicknessShadow=5px&offsetTopShadow=0px&offsetLeftShadow=0px&cornerRadiusShadow=8px
*/
/* Component containers
----------------------------------*/
.ui-widget {
font-family: Arial,Helvetica,sans-serif;
font-size: 1em;
}
.ui-widget .ui-widget {
font-size: 1em;
}
.ui-widget input,
.ui-widget select,
.ui-widget textarea,
.ui-widget button {
font-family: Arial,Helvetica,sans-serif;
font-size: 1em;
}
.ui-widget-content {
border: 1px solid #dddddd;
background: #ffffff;
color: #333333;
}
.ui-widget-content a {
color: #333333;
}
.ui-widget-header {
border: 1px solid #dddddd;
background: #e9e9e9;
color: #333333;
font-weight: bold;
}
.ui-widget-header a {
color: #333333;
}
/* Interaction states
----------------------------------*/
.ui-state-default,
.ui-widget-content .ui-state-default,
.ui-widget-header .ui-state-default {
border: 1px solid #c5c5c5;
background: #f6f6f6;
font-weight: normal;
color: #454545;
}
.ui-state-default a,
.ui-state-default a:link,
.ui-state-default a:visited {
color: #454545;
text-decoration: none;
}
.ui-state-hover,
.ui-widget-content .ui-state-hover,
.ui-widget-header .ui-state-hover,
.ui-state-focus,
.ui-widget-content .ui-state-focus,
.ui-widget-header .ui-state-focus {
border: 1px solid #cccccc;
background: #ededed;
font-weight: normal;
color: #2b2b2b;
}
.ui-state-hover a,
.ui-state-hover a:hover,
.ui-state-hover a:link,
.ui-state-hover a:visited,
.ui-state-focus a,
.ui-state-focus a:hover,
.ui-state-focus a:link,
.ui-state-focus a:visited {
color: #2b2b2b;
text-decoration: none;
}
.ui-state-active,
.ui-widget-content .ui-state-active,
.ui-widget-header .ui-state-active {
border: 1px solid #003eff;
background: #007fff;
font-weight: normal;
color: #ffffff;
}
.ui-state-active a,
.ui-state-active a:link,
.ui-state-active a:visited {
color: #ffffff;
text-decoration: none;
}
/* Interaction Cues
----------------------------------*/
.ui-state-highlight,
.ui-widget-content .ui-state-highlight,
.ui-widget-header .ui-state-highlight {
border: 1px solid #dad55e;
background: #fffa90;
color: #777620;
}
.ui-state-highlight a,
.ui-widget-content .ui-state-highlight a,
.ui-widget-header .ui-state-highlight a {
color: #777620;
}
.ui-state-error,
.ui-widget-content .ui-state-error,
.ui-widget-header .ui-state-error {
border: 1px solid #f1a899;
background: #fddfdf;
color: #5f3f3f;
}
.ui-state-error a,
.ui-widget-content .ui-state-error a,
.ui-widget-header .ui-state-error a {
color: #5f3f3f;
}
.ui-state-error-text,
.ui-widget-content .ui-state-error-text,
.ui-widget-header .ui-state-error-text {
color: #5f3f3f;
}
.ui-priority-primary,
.ui-widget-content .ui-priority-primary,
.ui-widget-header .ui-priority-primary {
font-weight: bold;
}
.ui-priority-secondary,
.ui-widget-content .ui-priority-secondary,
.ui-widget-header .ui-priority-secondary {
opacity: .7;
filter:Alpha(Opacity=70); /* support: IE8 */
font-weight: normal;
}
.ui-state-disabled,
.ui-widget-content .ui-state-disabled,
.ui-widget-header .ui-state-disabled {
opacity: .35;
filter:Alpha(Opacity=35); /* support: IE8 */
background-image: none;
}
.ui-state-disabled .ui-icon {
filter:Alpha(Opacity=35); /* support: IE8 - See #6059 */
}
/* Icons
----------------------------------*/
/* states and images */
.ui-icon {
width: 16px;
height: 16px;
}
.ui-icon,
.ui-widget-content .ui-icon {
background-image: url("images/ui-icons_444444_256x240.png");
}
.ui-widget-header .ui-icon {
background-image: url("images/ui-icons_444444_256x240.png");
}
.ui-state-default .ui-icon {
background-image: url("images/ui-icons_777777_256x240.png");
}
.ui-state-hover .ui-icon,
.ui-state-focus .ui-icon {
background-image: url("images/ui-icons_555555_256x240.png");
}
.ui-state-active .ui-icon {
background-image: url("images/ui-icons_ffffff_256x240.png");
}
.ui-state-highlight .ui-icon {
background-image: url("images/ui-icons_777620_256x240.png");
}
.ui-state-error .ui-icon,
.ui-state-error-text .ui-icon {
background-image: url("images/ui-icons_cc0000_256x240.png");
}
/* positioning */
.ui-icon-blank { background-position: 16px 16px; }
.ui-icon-carat-1-n { background-position: 0 0; }
.ui-icon-carat-1-ne { background-position: -16px 0; }
.ui-icon-carat-1-e { background-position: -32px 0; }
.ui-icon-carat-1-se { background-position: -48px 0; }
.ui-icon-carat-1-s { background-position: -64px 0; }
.ui-icon-carat-1-sw { background-position: -80px 0; }
.ui-icon-carat-1-w { background-position: -96px 0; }
.ui-icon-carat-1-nw { background-position: -112px 0; }
.ui-icon-carat-2-n-s { background-position: -128px 0; }
.ui-icon-carat-2-e-w { background-position: -144px 0; }
.ui-icon-triangle-1-n { background-position: 0 -16px; }
.ui-icon-triangle-1-ne { background-position: -16px -16px; }
.ui-icon-triangle-1-e { background-position: -32px -16px; }
.ui-icon-triangle-1-se { background-position: -48px -16px; }
.ui-icon-triangle-1-s { background-position: -64px -16px; }
.ui-icon-triangle-1-sw { background-position: -80px -16px; }
.ui-icon-triangle-1-w { background-position: -96px -16px; }
.ui-icon-triangle-1-nw { background-position: -112px -16px; }
.ui-icon-triangle-2-n-s { background-position: -128px -16px; }
.ui-icon-triangle-2-e-w { background-position: -144px -16px; }
.ui-icon-arrow-1-n { background-position: 0 -32px; }
.ui-icon-arrow-1-ne { background-position: -16px -32px; }
.ui-icon-arrow-1-e { background-position: -32px -32px; }
.ui-icon-arrow-1-se { background-position: -48px -32px; }
.ui-icon-arrow-1-s { background-position: -64px -32px; }
.ui-icon-arrow-1-sw { background-position: -80px -32px; }
.ui-icon-arrow-1-w { background-position: -96px -32px; }
.ui-icon-arrow-1-nw { background-position: -112px -32px; }
.ui-icon-arrow-2-n-s { background-position: -128px -32px; }
.ui-icon-arrow-2-ne-sw { background-position: -144px -32px; }
.ui-icon-arrow-2-e-w { background-position: -160px -32px; }
.ui-icon-arrow-2-se-nw { background-position: -176px -32px; }
.ui-icon-arrowstop-1-n { background-position: -192px -32px; }
.ui-icon-arrowstop-1-e { background-position: -208px -32px; }
.ui-icon-arrowstop-1-s { background-position: -224px -32px; }
.ui-icon-arrowstop-1-w { background-position: -240px -32px; }
.ui-icon-arrowthick-1-n { background-position: 0 -48px; }
.ui-icon-arrowthick-1-ne { background-position: -16px -48px; }
.ui-icon-arrowthick-1-e { background-position: -32px -48px; }
.ui-icon-arrowthick-1-se { background-position: -48px -48px; }
.ui-icon-arrowthick-1-s { background-position: -64px -48px; }
.ui-icon-arrowthick-1-sw { background-position: -80px -48px; }
.ui-icon-arrowthick-1-w { background-position: -96px -48px; }
.ui-icon-arrowthick-1-nw { background-position: -112px -48px; }
.ui-icon-arrowthick-2-n-s { background-position: -128px -48px; }
.ui-icon-arrowthick-2-ne-sw { background-position: -144px -48px; }
.ui-icon-arrowthick-2-e-w { background-position: -160px -48px; }
.ui-icon-arrowthick-2-se-nw { background-position: -176px -48px; }
.ui-icon-arrowthickstop-1-n { background-position: -192px -48px; }
.ui-icon-arrowthickstop-1-e { background-position: -208px -48px; }
.ui-icon-arrowthickstop-1-s { background-position: -224px -48px; }
.ui-icon-arrowthickstop-1-w { background-position: -240px -48px; }
.ui-icon-arrowreturnthick-1-w { background-position: 0 -64px; }
.ui-icon-arrowreturnthick-1-n { background-position: -16px -64px; }
.ui-icon-arrowreturnthick-1-e { background-position: -32px -64px; }
.ui-icon-arrowreturnthick-1-s { background-position: -48px -64px; }
.ui-icon-arrowreturn-1-w { background-position: -64px -64px; }
.ui-icon-arrowreturn-1-n { background-position: -80px -64px; }
.ui-icon-arrowreturn-1-e { background-position: -96px -64px; }
.ui-icon-arrowreturn-1-s { background-position: -112px -64px; }
.ui-icon-arrowrefresh-1-w { background-position: -128px -64px; }
.ui-icon-arrowrefresh-1-n { background-position: -144px -64px; }
.ui-icon-arrowrefresh-1-e { background-position: -160px -64px; }
.ui-icon-arrowrefresh-1-s { background-position: -176px -64px; }
.ui-icon-arrow-4 { background-position: 0 -80px; }
.ui-icon-arrow-4-diag { background-position: -16px -80px; }
.ui-icon-extlink { background-position: -32px -80px; }
.ui-icon-newwin { background-position: -48px -80px; }
.ui-icon-refresh { background-position: -64px -80px; }
.ui-icon-shuffle { background-position: -80px -80px; }
.ui-icon-transfer-e-w { background-position: -96px -80px; }
.ui-icon-transferthick-e-w { background-position: -112px -80px; }
.ui-icon-folder-collapsed { background-position: 0 -96px; }
.ui-icon-folder-open { background-position: -16px -96px; }
.ui-icon-document { background-position: -32px -96px; }
.ui-icon-document-b { background-position: -48px -96px; }
.ui-icon-note { background-position: -64px -96px; }
.ui-icon-mail-closed { background-position: -80px -96px; }
.ui-icon-mail-open { background-position: -96px -96px; }
.ui-icon-suitcase { background-position: -112px -96px; }
.ui-icon-comment { background-position: -128px -96px; }
.ui-icon-person { background-position: -144px -96px; }
.ui-icon-print { background-position: -160px -96px; }
.ui-icon-trash { background-position: -176px -96px; }
.ui-icon-locked { background-position: -192px -96px; }
.ui-icon-unlocked { background-position: -208px -96px; }
.ui-icon-bookmark { background-position: -224px -96px; }
.ui-icon-tag { background-position: -240px -96px; }
.ui-icon-home { background-position: 0 -112px; }
.ui-icon-flag { background-position: -16px -112px; }
.ui-icon-calendar { background-position: -32px -112px; }
.ui-icon-cart { background-position: -48px -112px; }
.ui-icon-pencil { background-position: -64px -112px; }
.ui-icon-clock { background-position: -80px -112px; }
.ui-icon-disk { background-position: -96px -112px; }
.ui-icon-calculator { background-position: -112px -112px; }
.ui-icon-zoomin { background-position: -128px -112px; }
.ui-icon-zoomout { background-position: -144px -112px; }
.ui-icon-search { background-position: -160px -112px; }
.ui-icon-wrench { background-position: -176px -112px; }
.ui-icon-gear { background-position: -192px -112px; }
.ui-icon-heart { background-position: -208px -112px; }
.ui-icon-star { background-position: -224px -112px; }
.ui-icon-link { background-position: -240px -112px; }
.ui-icon-cancel { background-position: 0 -128px; }
.ui-icon-plus { background-position: -16px -128px; }
.ui-icon-plusthick { background-position: -32px -128px; }
.ui-icon-minus { background-position: -48px -128px; }
.ui-icon-minusthick { background-position: -64px -128px; }
.ui-icon-close { background-position: -80px -128px; }
.ui-icon-closethick { background-position: -96px -128px; }
.ui-icon-key { background-position: -112px -128px; }
.ui-icon-lightbulb { background-position: -128px -128px; }
.ui-icon-scissors { background-position: -144px -128px; }
.ui-icon-clipboard { background-position: -160px -128px; }
.ui-icon-copy { background-position: -176px -128px; }
.ui-icon-contact { background-position: -192px -128px; }
.ui-icon-image { background-position: -208px -128px; }
.ui-icon-video { background-position: -224px -128px; }
.ui-icon-script { background-position: -240px -128px; }
.ui-icon-alert { background-position: 0 -144px; }
.ui-icon-info { background-position: -16px -144px; }
.ui-icon-notice { background-position: -32px -144px; }
.ui-icon-help { background-position: -48px -144px; }
.ui-icon-check { background-position: -64px -144px; }
.ui-icon-bullet { background-position: -80px -144px; }
.ui-icon-radio-on { background-position: -96px -144px; }
.ui-icon-radio-off { background-position: -112px -144px; }
.ui-icon-pin-w { background-position: -128px -144px; }
.ui-icon-pin-s { background-position: -144px -144px; }
.ui-icon-play { background-position: 0 -160px; }
.ui-icon-pause { background-position: -16px -160px; }
.ui-icon-seek-next { background-position: -32px -160px; }
.ui-icon-seek-prev { background-position: -48px -160px; }
.ui-icon-seek-end { background-position: -64px -160px; }
.ui-icon-seek-start { background-position: -80px -160px; }
/* ui-icon-seek-first is deprecated, use ui-icon-seek-start instead */
.ui-icon-seek-first { background-position: -80px -160px; }
.ui-icon-stop { background-position: -96px -160px; }
.ui-icon-eject { background-position: -112px -160px; }
.ui-icon-volume-off { background-position: -128px -160px; }
.ui-icon-volume-on { background-position: -144px -160px; }
.ui-icon-power { background-position: 0 -176px; }
.ui-icon-signal-diag { background-position: -16px -176px; }
.ui-icon-signal { background-position: -32px -176px; }
.ui-icon-battery-0 { background-position: -48px -176px; }
.ui-icon-battery-1 { background-position: -64px -176px; }
.ui-icon-battery-2 { background-position: -80px -176px; }
.ui-icon-battery-3 { background-position: -96px -176px; }
.ui-icon-circle-plus { background-position: 0 -192px; }
.ui-icon-circle-minus { background-position: -16px -192px; }
.ui-icon-circle-close { background-position: -32px -192px; }
.ui-icon-circle-triangle-e { background-position: -48px -192px; }
.ui-icon-circle-triangle-s { background-position: -64px -192px; }
.ui-icon-circle-triangle-w { background-position: -80px -192px; }
.ui-icon-circle-triangle-n { background-position: -96px -192px; }
.ui-icon-circle-arrow-e { background-position: -112px -192px; }
.ui-icon-circle-arrow-s { background-position: -128px -192px; }
.ui-icon-circle-arrow-w { background-position: -144px -192px; }
.ui-icon-circle-arrow-n { background-position: -160px -192px; }
.ui-icon-circle-zoomin { background-position: -176px -192px; }
.ui-icon-circle-zoomout { background-position: -192px -192px; }
.ui-icon-circle-check { background-position: -208px -192px; }
.ui-icon-circlesmall-plus { background-position: 0 -208px; }
.ui-icon-circlesmall-minus { background-position: -16px -208px; }
.ui-icon-circlesmall-close { background-position: -32px -208px; }
.ui-icon-squaresmall-plus { background-position: -48px -208px; }
.ui-icon-squaresmall-minus { background-position: -64px -208px; }
.ui-icon-squaresmall-close { background-position: -80px -208px; }
.ui-icon-grip-dotted-vertical { background-position: 0 -224px; }
.ui-icon-grip-dotted-horizontal { background-position: -16px -224px; }
.ui-icon-grip-solid-vertical { background-position: -32px -224px; }
.ui-icon-grip-solid-horizontal { background-position: -48px -224px; }
.ui-icon-gripsmall-diagonal-se { background-position: -64px -224px; }
.ui-icon-grip-diagonal-se { background-position: -80px -224px; }
/* Misc visuals
----------------------------------*/
/* Corner radius */
.ui-corner-all,
.ui-corner-top,
.ui-corner-left,
.ui-corner-tl {
border-top-left-radius: 3px;
}
.ui-corner-all,
.ui-corner-top,
.ui-corner-right,
.ui-corner-tr {
border-top-right-radius: 3px;
}
.ui-corner-all,
.ui-corner-bottom,
.ui-corner-left,
.ui-corner-bl {
border-bottom-left-radius: 3px;
}
.ui-corner-all,
.ui-corner-bottom,
.ui-corner-right,
.ui-corner-br {
border-bottom-right-radius: 3px;
}
/* Overlays */
.ui-widget-overlay {
background: #aaaaaa;
opacity: .3;
filter: Alpha(Opacity=30); /* support: IE8 */
}
.ui-widget-shadow {
margin: 0px 0 0 0px;
padding: 5px;
background: #666666;
opacity: .3;
filter: Alpha(Opacity=30); /* support: IE8 */
border-radius: 8px;
}

9789
contrib/websocket/jquery/jquery.js vendored Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,50 @@
html, body {
margin: 0;
padding: 0;
height: 100%;
}
body {
font-family: 'Helvetica Neue', sans-serif;
}
#wrapper {
}
#header {
padding: 20px;
border-bottom: 2px solid gray;
}
#header h1 {
margin: 0;
}
#footer {
padding: 1em;
border-top: 2px solid gray;
}
#container {
padding: 20px;
}
#slider {
width: 300px;
display: inline-block;
}
.logo {
float: right;
}
.plot-container {
width: 100%;
height: 400px;
}
.plot-placeholder {
width: 100%;
height: 100%;
}

View file

@ -18,8 +18,7 @@
/** Static list initialization */
#define LIST_INIT(dtor) { \
.start = NULL, \
.end = NULL, \
.array = NULL, \
.length = 0, \
.capacity = 0, \
.lock = PTHREAD_MUTEX_INITIALIZER, \
@ -27,12 +26,12 @@
}
#define list_length(list) ((list)->length)
#define list_at(list, index) ((list)->length > index ? (list)->start[index] : NULL)
#define list_at(list, index) ((list)->length > index ? (list)->array[index] : NULL)
#define list_first(list) list_at(list, 0)
#define list_last(list) list_at(list, (list)->length-1)
#define list_foreach(ptr, list) for (int _i = 0, _p; _p = 1, _i < (list)->length; _i++) \
for (ptr = (list)->start[_i]; _p--; )
for (ptr = (list)->array[_i]; _p--; )
/** Callback to destroy list elements.
*
@ -44,10 +43,9 @@ typedef void (*dtor_cb_t)(void *);
typedef int (*cmp_cb_t)(const void *, const void *);
struct list {
void **start; /**< Array of pointers to list elements */
void **end; /**< Array of pointers to list elements */
size_t capacity; /**< Size of list::start in elements */
size_t length; /**< Number of elements of list::start which are in use */
void **array; /**< Array of pointers to list elements */
size_t capacity; /**< Size of list::array in elements */
size_t length; /**< Number of elements of list::array which are in use */
dtor_cb_t destructor; /**< A destructor which gets called for every list elements during list_destroy() */
pthread_mutex_t lock; /**< A mutex to allow thread-safe accesses */
};
@ -61,6 +59,9 @@ void list_destroy(struct list *l);
/** Append an element to the end of the list */
void list_push(struct list *l, void *p);
/** Remove all occurences of a list item */
void list_remove(struct list *l, void *p);
/** Search the list for an element whose first element is a character array which matches name.
*
* @see Only possible because of §1424 of http://c0x.coding-guidelines.com/6.7.2.1.html

View file

@ -55,12 +55,11 @@ int msg_verify(struct msg *m);
* @param m A pointer to the message.
* @param flags See msg_flags.
* @param offset A optional offset in seconds. Only used if flags contains MSG_PRINT_OFFSET.
* @retval 0 Success. Everything went well.
* @retval <0 Error. Something went wrong.
* @return Number of bytes written to buf.
*/
int msg_fprint(FILE *f, struct msg *m, int flags, double offset);
int msg_print(char *buf, size_t len, struct msg *m, int flags, double offset);
/** Read a message from a file stream.
/** Read a message from a character buffer.
*
* @param f The file handle from fopen() or stdin.
* @param m A pointer to the message.
@ -69,6 +68,18 @@ int msg_fprint(FILE *f, struct msg *m, int flags, double offset);
* @retval 0 Success. Everything went well.
* @retval <0 Error. Something went wrong.
*/
int msg_scan(const char *line, struct msg *m, int *fl, double *off);
/** Print a raw message in human readable form to a file stream.
*
* @see msg_print()
*/
int msg_fprint(FILE *f, struct msg *m, int flags, double offset);
/** Read a message from a file stream.
*
* @see msg_scan()
*/
int msg_fscan(FILE *f, struct msg *m, int *flags, double *offset);
#endif /* _MSG_H_ */

View file

@ -127,11 +127,13 @@ int version_compare(struct version *a, struct version *b);
int version_parse(const char *s, struct version *v);
/** Check assertion and exit if failed. */
#define assert(exp) do { \
#ifndef assert
#define assert(exp) do { \
if (!EXPECT(exp, 0)) \
error("Assertion failed: '%s' in %s(), %s:%d", \
XSTR(exp), __FUNCTION__, __BASE_FILE__, __LINE__); \
} while (0)
#endif
#endif /* _UTILS_H_ */

56
include/websocket.h Normal file
View file

@ -0,0 +1,56 @@
/** Node type: WebSockets
*
* This file implements the websocket type for nodes.
* It's based on the libwebsockets library.
*
* @file
* @author Steffen Vogel <stvogel@eonerc.rwth-aachen.de>
* @copyright 2014-2015, Institute for Automation of Complex Power Systems, EONERC
* This file is part of S2SS. All Rights Reserved. Proprietary and confidential.
* Unauthorized copying of this file, via any medium is strictly prohibited.
*/
/**
* @addtogroup websockets WebSockets node type
* @ingroup node
* @{
*********************************************************************************/
#ifndef _WEBSOCKET_H_
#define _WEBSOCKET_H_
#include "node.h"
/* Forward declarations */
struct msg;
struct libwebsocket_context;
struct websocket {
struct {
pthread_cond_t cond;
pthread_mutex_t mutex;
struct msg *m;
} read, write;
struct list connections; /**< List of struct libwebsockets sockets */
};
/** @see node_vtable::init */
int websocket_init(int argc, char * argv[], struct settings *set);
/** @see node_vtable::deinit */
int websocket_deinit();
/** @see node_vtable::open */
int websocket_open(struct node *n);
/** @see node_vtable::close */
int websocket_close(struct node *n);
/** @see node_vtable::read */
int websocket_read(struct node *n, struct msg *pool, int poolsize, int first, int cnt);
/** @see node_vtable::write */
int websocket_write(struct node *n, struct msg *pool, int poolsize, int first, int cnt);
#endif /* _WEBSOCKET_H_ */

View file

@ -21,8 +21,7 @@ void list_init(struct list *l, dtor_cb_t dtor)
l->length = 0;
l->capacity = 0;
l->start = NULL;
l->end = NULL;
l->array = NULL;
}
void list_destroy(struct list *l)
@ -34,10 +33,9 @@ void list_destroy(struct list *l)
l->destructor(p);
}
free(l->start);
free(l->array);
l->start =
l->end = NULL;
l->array = NULL;
l->length =
l->capacity = 0;
@ -51,36 +49,51 @@ void list_push(struct list *l, void *p)
pthread_mutex_lock(&l->lock);
/* Resize array if out of capacity */
if (l->end == l->start + l->capacity) {
if (l->length >= l->capacity) {
l->capacity += LIST_CHUNKSIZE;
l->start = realloc(l->start, l->capacity * sizeof(void *));
l->array = realloc(l->array, l->capacity * sizeof(void *));
}
l->start[l->length] = p;
l->array[l->length] = p;
l->length++;
l->end = &l->start[l->length];
pthread_mutex_unlock(&l->lock);
}
void * list_lookup(struct list *l, const char *needle)
void list_remove(struct list *l, void *p)
{
int cmp(const void *elm, const void *needle) {
char *name = * (char **) elm; /* Ugly hack: the lookup key (name) must be the first element in the list element struct */
return strcmp(name, needle);
int removed = 0;
pthread_mutex_lock(&l->lock);
for (int i = 0; i < l->length; i++) {
if (l->array[i] == p)
removed++;
else
l->array[i - removed] = l->array[i];
}
l->length -= removed;
pthread_mutex_unlock(&l->lock);
}
void * list_lookup(struct list *l, const char *name)
{
int cmp_helper(const void *a, const void *b) {
return strcmp(* (char **) a, b);
}
return list_search(l, cmp, (void *) needle);
return list_search(l, cmp_helper, (void *) name);
}
int list_contains(struct list *l, void *p)
{
int cmp(const void *a, const void *b) {
int cmp_helper(const void *a, const void *b) {
return a == b;
}
return list_count(l, cmp, p);
return list_count(l, cmp_helper, p);
}
int list_count(struct list *l, cmp_cb_t cmp, void *ctx)
@ -123,6 +136,8 @@ void list_sort(struct list *l, cmp_cb_t cmp)
}
pthread_mutex_lock(&l->lock);
qsort(l->start, l->length, sizeof(void *), cmp_helper);
qsort(l->array, l->length, sizeof(void *), cmp_helper);
pthread_mutex_unlock(&l->lock);
}

View file

@ -49,34 +49,34 @@ int msg_verify(struct msg *m)
return 0;
}
int msg_fprint(FILE *f, struct msg *m, int flags, double offset)
int msg_print(char *buf, size_t len, struct msg *m, int flags, double offset)
{
fprintf(f, "%u", m->ts.sec);
size_t off = snprintf(buf, len, "%u", m->ts.sec);
if (flags & MSG_PRINT_NANOSECONDS)
fprintf(f, ".%09u", m->ts.nsec);
off += snprintf(buf + off, len - off, ".%09u", m->ts.nsec);
if (flags & MSG_PRINT_OFFSET)
fprintf(f, "%+g", offset);
off += snprintf(buf + off, len - off, "%+g", offset);
if (flags & MSG_PRINT_SEQUENCE)
fprintf(f, "(%u)", m->sequence);
off += snprintf(buf + off, len - off, "(%u)", m->sequence);
if (flags & MSG_PRINT_VALUES) {
for (int i = 0; i < m->length; i++)
fprintf(f, "\t%.6f", m->data[i].f);
off += snprintf(buf + off, len - off, "\t%.6f", m->data[i].f);
}
fprintf(f, "\n");
off += snprintf(buf + off, len - off, "\n");
return 0;
return off + 1; /* trailing '\0' */
}
/** @todo Currently only floating point values are supported */
int msg_fscan(FILE *f, struct msg *m, int *fl, double *off)
int msg_scan(const char *line, struct msg *m, int *fl, double *off)
{
char line[MSG_VALUES * 16];
char *end, *ptr = line;
char *end;
const char *ptr = line;
int flags = 0;
double offset;
@ -90,14 +90,6 @@ int msg_fscan(FILE *f, struct msg *m, int *fl, double *off)
* Please note that only the seconds and at least one value are mandatory
*/
skip: if (fgets(line, sizeof(line), f) == NULL)
return -1; /* An error occured */
/* Skip whitespaces, empty and comment lines */
for (ptr = line; isspace(*ptr); ptr++);
if (*ptr == '\0' || *ptr == '#')
goto skip;
/* Mandatory: seconds */
m->ts.sec = (uint32_t) strtoul(ptr, &end, 10);
if (ptr == end)
@ -165,3 +157,29 @@ skip: if (fgets(line, sizeof(line), f) == NULL)
return m->length;
}
int msg_fprint(FILE *f, struct msg *m, int flags, double offset)
{
char line[4096];
int len = msg_print(line, sizeof(line), m, flags, offset);
fputs(line, f);
return len;
}
int msg_fscan(FILE *f, struct msg *m, int *fl, double *off)
{
char *ptr, line[4096];
skip: if (fgets(line, sizeof(line), f) == NULL)
return -1; /* An error occured */
/* Skip whitespaces, empty and comment lines */
for (ptr = line; isspace(*ptr); ptr++);
if (*ptr == '\0' || *ptr == '#')
goto skip;
return msg_scan(line, m, fl, off);
}

386
lib/websocket.c Normal file
View file

@ -0,0 +1,386 @@
/** Node type: Websockets (libwebsockets)
*
* This file implements the weboscket subtype for nodes.
*
* @author Steffen Vogel <stvogel@eonerc.rwth-aachen.de>
* @copyright 2014-2015, Institute for Automation of Complex Power Systems, EONERC
* This file is part of S2SS. All Rights Reserved. Proprietary and confidential.
* Unauthorized copying of this file, via any medium is strictly prohibited.
*********************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
#include <libwebsockets.h>
#include <config.h>
#include "websocket.h"
#include "timing.h"
#include "utils.h"
#include "msg.h"
/* Forward declarations */
static int protocol_cb_http(struct lws_context *, struct lws *, enum lws_callback_reasons, void *, void *, size_t);
static int protocol_cb_live(struct lws_context *, struct lws *, enum lws_callback_reasons, void *, void *, size_t);
/* Static storage */
static struct websocket_global {
pthread_t thread; /**< All nodes are served by a single websocket server. This server is running in a dedicated thread. */
struct lws_context *context; /**< The libwebsockets server context. */
struct list nodes; /**< List of websocket node instances. */
int port; /**< Port of the build in HTTP / WebSocket server */
const char *ssl_cert; /**< Path to the SSL certitifcate for HTTPS / WSS */
const char *ssl_private_key; /**< Path to the SSL private key for HTTPS / WSS */
const char *htdocs; /**< Path to the directory which should be served by build in HTTP server */
} global;
static struct lws_protocols protocols[] = {
{ "http-only", protocol_cb_http, 0, 0 },
{ "live", protocol_cb_live, sizeof(struct node *), 10 },
{ 0 } /* terminator */
};
/* Choose mime type based on the file extension */
static char * get_mimetype(const char *resource_path)
{
char *extension = strrchr(resource_path, '.');
if (extension == NULL)
return "text/plain";
else if (!strcmp(extension, ".png"))
return "image/png";
else if (!strcmp(extension, ".jpg"))
return "image/jpg";
else if (!strcmp(extension, ".gif"))
return "image/gif";
else if (!strcmp(extension, ".html"))
return "text/html";
else if (!strcmp(extension, ".css"))
return "text/css";
else if (!strcmp(extension, ".js"))
return "application/javascript";
else
return "text/plain";
}
static int protocol_cb_http(struct lws_context *context, struct lws *wsi, enum lws_callback_reasons reason, void *user, void *in, size_t len)
{
struct websocket_global *global = lws_context_user(context);
switch (reason) {
case LWS_CALLBACK_HTTP:
if (!global->htdocs) {
lws_return_http_status(context, wsi, HTTP_STATUS_SERVICE_UNAVAILABLE, NULL);
goto try_to_reuse;
}
if (len < 1) {
lws_return_http_status(context, wsi, HTTP_STATUS_BAD_REQUEST, NULL);
goto try_to_reuse;
}
char *requested_uri = (char *) in;
debug(3, "Got new lws HTTP request: %s", requested_uri);
/* Handle default path */
if (!strcmp(requested_uri, "/")) {
char *response = "HTTP/1.1 302 Found\r\n"
"Content-Length: 0\r\n"
"Location: /index.html\r\n"
"\r\n";
lws_write(wsi, (void *) response, strlen(response), LWS_WRITE_HTTP);
goto try_to_reuse;
}
/* Return list of websocket nodes */
else if (!strcmp(requested_uri, "/nodes.json")) {
char response[4096];
char *body = NULL;
list_foreach(struct node *n, &global->nodes)
strcatf(&body, "'%s', ", n->name);
snprintf(response, sizeof(response),
"HTTP/1.1 200 OK\r\n"
"Connection: close\r\n"
"\r\n"
"[ %.*s ]", (int) strlen(body) - 2, body);
lws_write(wsi, (void *) response, strlen(response), LWS_WRITE_HTTP);
free(body);
return -1;
}
else {
char path[4069];
snprintf(path, sizeof(path), "%s%s", global->htdocs, requested_uri);
/* refuse to serve files we don't understand */
char *mimetype = get_mimetype(path);
if (!mimetype) {
warn("HTTP: Unknown mimetype for %s", path);
lws_return_http_status(context, wsi, HTTP_STATUS_UNSUPPORTED_MEDIA_TYPE, NULL);
return -1;
}
int n = lws_serve_http_file(context, wsi, path, mimetype, NULL, 0);
if (n < 0)
return -1;
else if (n == 0)
break;
else
goto try_to_reuse;
}
default:
break;
}
return 0;
try_to_reuse:
if (lws_http_transaction_completed(wsi))
return -1;
return 0;
}
static int protocol_cb_live(struct lws_context *context, struct lws *wsi, enum lws_callback_reasons reason, void *user, void *in, size_t len)
{
struct node *n;
struct websocket *w;
struct websocket_global *global = lws_context_user(context);
char *buf, uri[1024];
switch (reason) {
case LWS_CALLBACK_ESTABLISHED:
lws_hdr_copy(wsi, uri, sizeof(uri), WSI_TOKEN_GET_URI); /* The path component of the*/
/* Search for node which matches uri */
list_foreach(n, &global->nodes) {
if (!strcmp(n->name, uri + 1)) /* we skip leading '/' */
goto found;
}
warn("WebSocket: New Connection for non-exsitent node: %s", uri + 1);
return -1;
found: * (void **) user = n;
w = n->_vd;
list_push(&w->connections, wsi);
info("WebSocket: New Connection for node: %s", n->name);
return 0;
case LWS_CALLBACK_CLOSED:
n = * (struct node **) user;
w = n->_vd;
list_remove(&w->connections, wsi);
info("WebSocket: Connection closed for node: %s", n->name);
return 0;
case LWS_CALLBACK_SERVER_WRITEABLE:
n = * (struct node **) user;
if (!n)
return 0;
w = n->_vd;
if (!w->write.m)
return 0;
buf = malloc(LWS_SEND_BUFFER_PRE_PADDING + LWS_SEND_BUFFER_POST_PADDING + 4096);
pthread_mutex_lock(&w->write.mutex);
len = msg_print(buf + LWS_SEND_BUFFER_PRE_PADDING, 4096, w->write.m, MSG_PRINT_NANOSECONDS | MSG_PRINT_VALUES, 0);
pthread_mutex_unlock(&w->write.mutex);
lws_write(wsi, (unsigned char *) (buf + LWS_SEND_BUFFER_PRE_PADDING), len, LWS_WRITE_TEXT);
return 0;
case LWS_CALLBACK_RECEIVE:
n = * (struct node **) user;
w = n->_vd;
pthread_mutex_lock(&w->read.mutex);
msg_scan(in, w->read.m, NULL, NULL);
pthread_mutex_unlock(&w->read.mutex);
pthread_cond_broadcast(&w->read.cond);
return 0;
default:
return 0;
}
}
static void logger(int level, const char *msg) {
int len = strlen(msg);
if (strchr(msg, '\n'))
len -= 1;
switch (level) {
case LLL_ERR: error("WebSocket: %.*s", len, msg); break;
case LLL_WARN: warn("WebSocket: %.*s", len, msg); break;
case LLL_INFO: info("WebSocket: %.*s", len, msg); break;
default: debug(1, "WebSocket: %.*s", len, msg); break;
}
}
static void * server_thread(void *ctx)
{
debug(3, "WebSocket: Started server thread");
while (lws_service(global.context, 10) >= 0);
return NULL;
}
int websocket_init(int argc, char * argv[], struct settings *set)
{
lws_set_log_level((1 << LLL_COUNT) - 1, logger);
/* Parse global config */
/*
config_setting_lookup_string(cfg, "ssl_cert", &global.ssl_cert);
config_setting_lookup_string(cfg, "ssl_private_key", &global.ssl_private_key);
config_setting_lookup_string(cfg, "htdocs", &global.htdocs);
if (!config_setting_lookup_int(cfg, "port", &global.port))
port = 80;
*/
/* @todo Fake settings */
global.port = 8080;
global.htdocs = "/s2ss/contrib/websocket";
/* Initialize list of nodes */
list_init(&global.nodes, NULL);
/* Start server */
struct lws_context_creation_info info = {
.port = global.port,
.protocols = protocols,
.extensions = lws_get_internal_extensions(),
.ssl_cert_filepath = global.ssl_cert,
.ssl_private_key_filepath = global.ssl_private_key,
.gid = -1,
.uid = -1,
.user = &global
};
global.context = lws_create_context(&info);
if (global.context == NULL)
error("WebSocket: failed to initialize server");
pthread_create(&global.thread, NULL, server_thread, NULL);
return 0;
}
int websocket_deinit()
{
pthread_join(global.thread, NULL);
lws_cancel_service(global.context);
lws_context_destroy(global.context);
list_destroy(&global.nodes);
return 0;
}
int websocket_open(struct node *n)
{
struct websocket *w = n->_vd;
list_init(&w->connections, NULL);
list_push(&global.nodes, n);
pthread_mutex_init(&w->read.mutex, NULL);
pthread_mutex_init(&w->write.mutex, NULL);
pthread_cond_init(&w->read.cond, NULL);
/* pthread_cond_wait() expects the mutex to be already locked */
pthread_mutex_lock(&w->read.mutex);
return 0;
}
int websocket_close(struct node *n)
{
struct websocket *w = n->_vd;
list_remove(&global.nodes, n);
pthread_mutex_destroy(&w->read.mutex);
pthread_mutex_destroy(&w->write.mutex);
pthread_cond_destroy(&w->read.cond);
return 0;
}
int websocket_read(struct node *n, struct msg *pool, int poolsize, int first, int cnt)
{
struct websocket *w = n->_vd;
struct msg *m = pool + (first % poolsize);
w->read.m = m;
pthread_cond_wait(&w->read.cond, &w->read.mutex);
return 1;
}
int websocket_write(struct node *n, struct msg *pool, int poolsize, int first, int cnt)
{
struct websocket *w = n->_vd;
struct msg *m = pool + (first % poolsize);
pthread_mutex_lock(&w->write.mutex);
w->write.m = m;
/* Notify all active websocket connections to send new data */
list_foreach(struct lws *wsi, &w->connections) {
lws_callback_on_writable(global.context, wsi);
}
pthread_mutex_unlock(&w->write.mutex);
return 0;
}
static struct node_type vt = {
.name = "websocket",
.description = "Send and receive samples of a WebSocket connection (libwebsockets)",
.size = sizeof(struct websocket),
.open = websocket_open,
.close = websocket_close,
.read = websocket_read,
.write = websocket_write,
.init = websocket_init,
.deinit = websocket_deinit
};
REGISTER_NODE_TYPE(&vt)