e-inkdashboardextended
Version:
E-InkDashboardExtended is a signalK dashboard aimed at devices with e-ink screen. This application is based on the work of Vladimir Kalachikhin.
448 lines (432 loc) • 13.9 kB
HTML
<html>
<head>
<link href="common.css" rel="stylesheet" type="text/css" />
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta http-equiv="Content-Script-Type" content="text/javascript" />
<script src="internationalisation.js"></script>
<!-- required at the beginning, to initialize variables -->
<script src="fCommon.js"></script>
<script src="options.js"></script>
<!-- necessarily at the end so that the variables mentioned there are already there -->
<title>e-inkDashboardExtended</title>
</head>
<body>
<div
id="leftTopBlock"
onclick="bigBlock(this,'leftTopBlockBig');"
style="display: none"
></div>
<div
id="rightTopBlock"
onclick="bigBlock(this,'rightTopBlockBig');"
style="display: none"
></div>
<div
id="leftBottomBlock"
onclick="bigBlock(this,'leftBottomBlockBig');"
style="display: none"
></div>
<div
id="rightBottomBlock"
onclick="bigBlock(this,'rightBottomBlockBig');"
style="display: none"
></div>
<div id="compass">
<div id="center_icon">
<svg
xml:space="default"
version="1.1"
viewBox="0 0 250 250"
xmlns="http://www.w3.org/2000/svg"
>
<path
fill="none"
stroke="black"
stroke-width="10px"
d="M70 125 L 70 245 180 245 180 125 Q 180 65 125 10 70 70 70 125"
/>
</svg>
</div>
<div id="windSVGimage_container">
<svg
xml:space="default"
version="1.1"
viewBox="0 0 100 300"
xmlns="http://www.w3.org/2000/svg"
id="windSVGimage"
>
<line
fill="none"
stroke="black"
stroke-width="10px"
x1="50"
y1="50.5"
x2="50"
y2="130"
></line>
<line
fill="none"
stroke="black"
stroke-width="10px"
x1="46.8"
y1="50"
x2="70"
y2="70"
></line>
<line
fill="none"
stroke="black"
stroke-width="10px"
x1="53.2"
y1="50"
x2="30"
y2="70"
></line>
</svg>
</div>
<div id="max_upwind_angle">
<svg
id="max_upwind_angle_streak_l"
xml:space="default"
version="1.1"
viewBox="0 0 4 300"
xmlns="http://www.w3.org/2000/svg"
>
<line
fill="none"
stroke="black"
stroke-width="7px"
x1="0"
y1="50"
x2="0"
y2="110"
></line>
</svg>
<svg
id="max_upwind_angle_streak_r"
xml:space="default"
version="1.1"
viewBox="0 0 4 300"
xmlns="http://www.w3.org/2000/svg"
>
<line
fill="none"
stroke="black"
stroke-width="7px"
x1="0"
y1="50"
x2="0"
y2="110"
></line>
</svg>
<svg
id="max_downwind_angle_streak_l"
xml:space="default"
version="1.1"
viewBox="0 0 4 300"
xmlns="http://www.w3.org/2000/svg"
>
<line
fill="none"
stroke="black"
stroke-width="7px"
x1="0"
y1="50"
x2="0"
y2="110"
></line>
</svg>
<svg
id="max_downwind_angle_streak_r"
xml:space="default"
version="1.1"
viewBox="0 0 4 300"
xmlns="http://www.w3.org/2000/svg"
>
<line
fill="none"
stroke="black"
stroke-width="7px"
x1="0"
y1="50"
x2="0"
y2="110"
></line>
</svg>
</div>
<div id="center_marc">
<svg
id="center_marc_streak"
xml:space="default"
version="1.1"
viewBox="0 0 4 300"
xmlns="http://www.w3.org/2000/svg"
>
<line
fill="none"
stroke="black"
stroke-width="3px"
x1="0"
y1="10"
x2="0"
y2="110"
></line>
<line
fill="none"
stroke="black"
stroke-width="3px"
x1="0"
y1="190"
x2="0"
y2="290"
></line>
</svg>
<svg
id="center_marc_static"
xml:space="default"
version="1.1"
viewBox="0 0 4 300"
xmlns="http://www.w3.org/2000/svg"
>
<line
fill="none"
stroke="black"
stroke-width="7px"
x1="0"
y1="0"
x2="0"
y2="10"
></line>
</svg>
<div class="compass-card" id="compassCard">
<div class="compass-face">
<div class="charN">
<div class="compas-face-scale">0</div>
N
</div>
<div class="charNNE">
<div class="compas-face-scale">22.5</div>
NNE<br />|
</div>
<div class="charNE">
<div class="compas-face-scale">45</div>
NE
</div>
<div class="charENE">
<div class="compas-face-scale">67.5</div>
ENE<br />|
</div>
<div class="charE">
<div class="compas-face-scale">90</div>
E
</div>
<div class="charESE">
<div class="compas-face-scale">112.5</div>
ESE<br />|
</div>
<div class="charSE">
<div class="compas-face-scale">135</div>
SE
</div>
<div class="charSSE">
<div class="compas-face-scale">157.5</div>
SSE<br />|
</div>
<div class="charS">
<div class="compas-face-scale">180</div>
S
</div>
<div class="charSSW">
<div class="compas-face-scale">202.5</div>
SSW<br />|
</div>
<div class="charSW">
<div class="compas-face-scale">225</div>
SW
</div>
<div class="charWSW">
<div class="compas-face-scale">247.5</div>
WSW<br />|
</div>
<div class="charW">
<div class="compas-face-scale">270</div>
W
</div>
<div class="charWNW">
<div class="compas-face-scale">292.5</div>
WNW<br />|
</div>
<div class="charNW">
<div class="compas-face-scale">315</div>
NW
</div>
<div class="charNNW">
<div class="compas-face-scale">337.5</div>
NNW<br />|
</div>
</div>
<div id="nextPointDirection" style="display: none">
<svg
version="1.1"
viewBox="0 0 10 10"
xmlns="http://www.w3.org/2000/svg"
>
<polygon fill="black" stroke="black" points="1,1 5,9 9,1" />
</svg>
</div>
</div>
</div>
</div>
<script>
//compassCard.style.transform = 'rotate(30deg)';
var tpv = {};
var socket;
var wangle_smoothed = -1;
// Variables with signature strings are defined in internationalisation.js
// Variables - options are defined in options.js
// Final initialization
var instanceSelf = getCookie("e-inkDashboardExtendedInstance"); // The identifier of the instance of the program
if (!instanceSelf) {
instanceSelf = generateUUID();
let expires = new Date();
expires.setTime(expires.getTime() + 24 * 60 * 60 * 1000); // It will decay in a day
document.cookie =
"e-inkDashboardExtendedInstance=" +
instanceSelf +
"; expires=" +
expires +
"; path=/; SameSite=Lax;";
}
if (displayData.wangle && displayData.wangle.trueWind) {
// If the wind is true
compassCard.appendChild(windSVGimage); // Repair the wind symbol into the cartoon
}
// Go
const uri = `ws://${document.location.host}/signalk/v1/stream?subscribe=none`; // We will subscribe separately
doWebsocket(uri); //We launch the listening of the socket and process the resulting
// Then the definition of functions
function doWebsocket(uri) {
socket = new WebSocket(uri);
socket.onopen = websocketOnOpen;
socket.onmessage = websocketOnMessage;
socket.onclose = websocketOnClose;
socket.onerror = websocketOnError;
} // end function doWebsocket
function websocketOnOpen(event) {
/**/
// We form a subscription
let signalKsubscribe = {
context: "vessels.self",
subscribe: [],
};
for (let tpvName in displayData) {
// displayData is in options.js
signalKsubscribe.subscribe.push({
path: displayData[tpvName].signalkPath,
format: "delta",
policy: "instant",
minPeriod: displayData[tpvName].maxRefreshInterval,
});
}
event.target.send(JSON.stringify(signalKsubscribe)); // подписываемся
console.log("Winsocket open: Connection establish with " + uri);
displayON();
display();
} // end function websocketOnOpen
function websocketOnMessage(event) {
// We get the initial data
let SKdata;
try {
SKdata = JSON.parse(event.data); // But just a line is a correct json, and there will be no mistakes if the server has returned not json
if (!SKdata) throw Error("inbound empty");
if (typeof SKdata !== "object")
throw Error("inbound not a object: " + SKdata);
} catch (error) {
console.log("Parsing inbound data error: ", error.message);
tpv = {};
display();
return;
}
if (SKdata.roles) {
// greeting
return;
}
// We take the necessary data
let changedTPV = []; // The list of tpv keys, the values of which received changes, to update them in the interface
let checksTPV = []; // TPV key list, for which Setinterval control of freshness is launched
for (let props of SKdata.updates) {
if (!props.values) continue; // I do not know when and why suddenly it may not be.But - it does not happen
for (let prop of props.values) {
bytpvName: for (let tpvName in displayData) {
// displayData is in options.js
if (prop.path != displayData[tpvName].signalkPath) continue;
// Check for freshness
if (!tpv[tpvName]) {
tpv[tpvName] = {};
// We start cleaning data from outdated
checksTPV[tpvName] = window.setInterval(
chkTPV,
displayData[tpvName].fresh - 1,
tpvName,
checksTPV
);
}
// Leading to normal dimensions
if (prop.value !== null && displayData[tpvName].multiplicator)
tmp = prop.value * displayData[tpvName].multiplicator;
else tmp = prop.value;
// Prevent tiny correction of the rotating components -> reduce frame refresh
if (
(tpvName == "heading" ||
tpvName == "track" ||
tpvName == "wangle") &&
tpv[tpvName].value > 0
) {
if (Math.abs(tpv[tpvName].value - tmp) > 1.0) {
tpv[tpvName].value = tmp;
}
} else {
tpv[tpvName].value = tmp;
}
if (tpvName == "wangle") {
if (wangle_smoothed < 0) {
wangle_smoothed = tpv[tpvName].value;
} else {
wangle_smoothed =
0.3 * tpv[tpvName].value + 0.7 * wangle_smoothed;
tpv[tpvName].value = wangle_smoothed;
}
}
tpv[tpvName].timestamp = Date.parse(props.timestamp);
changedTPV.push(tpvName);
}
}
}
changedTPV = Array.from(new Set(changedTPV)); // unique array
display(changedTPV); // Actually drawing data
} // end function websocketOnMessage
function websocketOnClose(event) {
/**/
if (event.wasClean) {
console.log(
`Websocket closed cleary with cide ${event.code} by reason: ${event.reason}`
);
} else {
// The server killed the process or the network is not available.Usually in this case Event.code 1006
console.log(
`Websocket closed: connection broken with code ${event.code} by reason ${event.reason}`
);
window.setTimeout(doWebsocket, 5000, uri); // restart the socket after 5 seconds
}
tpv = {};
displayOFF();
} // end function websocketOnClose
function websocketOnError(error) {
/**/
console.log(`Websocket error: ${error.message}`);
display(); // Perhaps removing outdated data will support the correct display
} // end function websocketOnError
</script>
</body>
</html>