Last active
July 20, 2018 04:54
-
-
Save gabonator/f7cbc1fd3ac79a7fbc09c8d8922bb457 to your computer and use it in GitHub Desktop.
Driver na radiacny kotol ORK 100
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<html> | |
<head> | |
<script src="https://code.jquery.com/jquery-3.1.1.min.js"></script> | |
<script src="https://code.highcharts.com/highcharts.js"></script> | |
<script src="https://code.highcharts.com/highcharts-more.js"></script> | |
</head> | |
<style> | |
#schemecontainer {width:600px; height:580px; float:left; margin-right:20px;} | |
#scheme {position:relative; width:634px; height:563px; zoom:0.9; } | |
.scheme {position:absolute; left:0px; top:0px;} | |
.fan {position:absolute; left:90px; top:450px; display:none} | |
.flame {position:absolute; left:290px; top:390px; width:120px; display:none} | |
.ion {position:absolute; left:270px; top:320px; width:60px; display:none} | |
.spark {position:absolute; left:370px; top:320px; width:60px; display:none} | |
.pump {position:absolute; left:470px; top:397px; display:none} | |
.servo {position:absolute; left:90px; top:360px; display:none} | |
.valve1 {position:absolute; left:350px; top:60px; display:none} | |
.valve2 {position:absolute; left:237px; top:60px; display:none} | |
.sensor1 {position:absolute; left:465px; top:30px; display:none} | |
.sensor2 {position:absolute; left:237px; top:160px; display:none} | |
.sensor3 {position:absolute; left:468px; top:463px; display:none} | |
.temperature {background:url("scheme/thermometer.png"); background-size:40px; background-repeat: no-repeat; | |
padding-left:30px; padding-top:2px; height:40px; width:120px; font-family:arial; font-weight:bold;} | |
#temp_out { position:absolute; left:480px; top:250px; } | |
#temp_in { position:absolute; left:480px; top:350px; } | |
#temp_body { position:absolute; left:350px; top:160px; } | |
#temp_air { position:absolute; left:70px; top:140px; } | |
.info {font-family:arial; font-weight:bold;} | |
#info_servo { position:absolute; left:160px; top:364px; } | |
#container {width:100%} | |
#statePane {font-size:20px;} | |
#stateBox {padding-bottom:0px;} | |
#stateBox h4 {background:#d0d0d0; margin-top:5px; margin-bottom:5px;} | |
#modecontainer {clear:both; height:90px;} | |
.modegroup1 {height:75px; border:#d0d080 5px solid; border-radius:5px; float:left; background:#d0d080; text-align:center; font-family:arial;} | |
.modegroup2 {height:60px; border:#ffffff 5px solid; border-radius:5px; float:left; } | |
.modebox {width:120px; height:55px; line-height:50px; text-align:center; background:#f0f0f0; border:#808080 2px solid; border-radius:5px; | |
margin-right:5px; float:left; font-family:arial; font-size:20px; | |
} | |
.two {font-size:20px; line-height:26px;} | |
.selected {background:#808080; color:#ffffff;} | |
</style> | |
<h1>Riadiaci panel kotla ORK100</h1> | |
<div id="container"> | |
<div id="modecontainer"> | |
<div class="modegroup2"> | |
<div class="modebox selected" id="modeoffline">Offline</div> | |
<div class="modebox two" id="modesafe">Bezpecny rezim</div> | |
</div> | |
<div class="modegroup1"> | |
<div class="modebox" id="modeprepare">Priprava</div> | |
<div class="modebox" id="modeignition">Zapalovanie</div> | |
<div class="modebox" id="modeheating">Kurenie</div> | |
<div class="modebox" id="modestandby">Oddych</div><br> | |
Proces kurenia | |
</div> | |
<div class="modegroup2"> | |
<div class="modebox two" id="modemanual">Manualny rezim</div> | |
<div class="modebox two" id="modelocked">Uzamknuty rezim</div> | |
</div> | |
</div> | |
<div id="schemecontainer"> | |
<div id="scheme"> | |
<img class="scheme" id="scheme_clean" src="scheme/scheme_clean.png"> | |
<img class="fan" id="fan_error" src="scheme/fan_error.png" title="Turbo ventilator - porucha"> | |
<img class="fan" id="fan_icon" src="scheme/fan_icon.png" title="Turbo ventilator - pripraveny"> | |
<img class="fan" id="fan_on" src="scheme/fan_on.png" title="Turbo ventilator - aktivny"> | |
<img class="flame" id="flame_on" src="scheme/flame.png" title="Plamen hori"> | |
<img class="flame" id="flame_off" src="scheme/flame_off.png" title="Plamen nehori"> | |
<img class="flame" id="flame_trans" src="scheme/flame_trans.png" title="Plamen v prechode"> | |
<img class="ion" id="ion_error" src="scheme/ion_error.png" title="Ionizacny senzor - porucha"> | |
<img class="ion" id="ion_flame" src="scheme/ion_flame.png" title="Ionizacny senzor - plamen hori"> | |
<img class="ion" id="ion_icon" src="scheme/ion_icon.png" title="Ionizacny senzor - plamen nehori"> | |
<img class="ion" id="ion_offline" src="scheme/ion_offline.png" title="Ionizacny senzor - kotol vypnuty"> | |
<img class="ion" id="ion_trans" src="scheme/ion_trans.png" title="Ionizacny senzor - plamen v prechode"> | |
<img class="pump" id="pump_error" src="scheme/pump_error.png" title="Obehove cerpadlo - porucha"> | |
<img class="pump" id="pump_icon" src="scheme/pump_icon.png" title="Obehove cerpadlo - pripravene"> | |
<img class="pump" id="pump_on" src="scheme/pump_on.png" title="Obehove cerpadlo - v prevadzke"> | |
<img class="sensor1" id="sensor1_error" src="scheme/sensor_error.png" title="Senzor tlaku plynu na vstupe - porucha, tlak poklesol pod nastavenu uroven"> | |
<img class="sensor1" id="sensor1_icon" src="scheme/sensor_icon.png" title="Senzor tlaku plynu na vstupe - v poriadku"> | |
<img class="sensor2" id="sensor2_error" src="scheme/sensor2_error.png" title="Preslahovy senzor - porucha"> | |
<img class="sensor2" id="sensor2_icon" src="scheme/sensor2_icon.png" title="Preslahovy senzor - v poriadku"> | |
<img class="sensor3" id="sensor3_error" src="scheme/sensor_error.png" title="Senzor tlaku spalin na vystupe - upchaty vyfuk"> | |
<img class="sensor3" id="sensor3_icon" src="scheme/sensor_icon.png" title="Senzor tlaku spalin na vystupe - v poriadku"> | |
<img class="servo" id="servo_icon" src="scheme/servo_icon.png" title="Servo klapky pritoku vzduchu - klapka zatvorena"> | |
<img class="servo" id="servo_working" src="scheme/servo_working.png" title="Servo klapky pritoku vzduchu - v prevadzke"> | |
<img class="spark" id="spark_icon" src="scheme/spark_icon.png" title="Zapalovacia isrka - neaktivna"> | |
<img class="spark" id="spark_on" src="scheme/spark_on.png" title="Zapalovacia isrka - aktivna"> | |
<img class="valve1" id="valve1_icon" src="scheme/valve_icon.png" title="Solenoidovy ventil plynu EVP1 - zatvoreny"> | |
<img class="valve1" id="valve1_working" src="scheme/valve_working.png" title="Solenoidovy ventil plynu EVP1 - otvoreny"> | |
<img class="valve2" id="valve2_icon" src="scheme/valve_icon.png" title="Solenoidovy ventil plynu EVP2 - zatvoreny"> | |
<img class="valve2" id="valve2_working" src="scheme/valve_working.png" title="Solenoidovy ventil plynu EVP2 - otvoreny"> | |
<div class="temperature" id="temp_out">voda vystup<br>??? °C</div> | |
<div class="temperature" id="temp_in">voda vstup<br>??? °C</div> | |
<div class="temperature" id="temp_body">teplota kotla<br>??? °C</div> | |
<div class="temperature" id="temp_air">nasavanie<br>??? °C</div> | |
<div class="info" id="info_servo">klapka<br>otvorena<br>???%</div> | |
</div> | |
</div> | |
<div id="statePane"> | |
<div id="stateBox"> | |
<h4>Ovladanie:</h4> | |
<!--<span id="onlineState"></span><br>--> | |
<input type="button" value="Zastav kotol" onClick="onSafe()" style="font-size:20px"> | |
<input type="button" value="Spusti kurenie" onClick="onStart()" style="font-size:20px"> | |
</div> | |
<div id="stateBox"> | |
<h4>Teploty:</h4> | |
voda vstup=<span id="teplotaVstup"></span>°C<br> | |
voda vystup=<span id="teplotaVystup"></span>°C<br> | |
kotol=<span id="teplotaTermostat"></span>°C<br> | |
nasavanie=<span id="teplotaVzduch"></span>°C<br> | |
</div> | |
<div id="stateBox"> | |
<h4>Poruchy:</h4> | |
<span id="poruchy"></span> | |
</div> | |
<div id="stateBox"> | |
<h4>Zariadenia:</h4> | |
ionizacny senzor=<span id="devIon"></span> (<span id="devIonVolts"></span>V)<br> | |
obehove cerpadlo=<span id="devCerpadlo"></span><br> | |
ventilator=<span id="devVentilator"></span><br> | |
zapalovanie=<span id="devZapalovanie"></span><br> | |
plyn=<span id="devPlyn"></span><br> | |
klapka=<span id="devKlapka"></span><br> | |
</div> | |
<div id="stateBox"> | |
<h4>Stav:</h4> | |
rezim=<span id="state"></span><br> | |
pocet chyb:<span id="infoErrors"></span><br> | |
pocet cyklov:<span id="infoCycles"></span><br> | |
zapnuty:<span id="infoUptime"></span> min<br> | |
zapnuty plyn:<span id="infoGastime"></span> min<br> | |
termostat:<span id="infoConfig"></span>°C<br> | |
</div> | |
</div> | |
</div> | |
<br> | |
<div id="chart" style="width:100%; height:400px;"></div> | |
<br><br> | |
<textarea rows="15" cols="100" id="log"> | |
</textarea> | |
<br> | |
<input type="text" id="send"><input type="button" value="Posli prikaz" onClick="onSend()"> | |
<span id="logspan"></span><br><br> | |
<script> | |
var globalInfo = false; | |
var globalInfoReceived = 0; | |
var first = true; | |
var buffer = ""; | |
socket = new WebSocket("ws://"+window.location.hostname+":2222"); | |
socket.onopen = function() | |
{ | |
console.log("Connected!"); | |
} | |
socket.onclose = function() | |
{ | |
console.log("Connection lost!"); | |
} | |
socket.onerror = function() | |
{ | |
console.log("Connection lost!"); | |
} | |
socket.onmessage = function(msg) | |
{ | |
buffer += new String(msg.data); | |
var lines = buffer.split("\n"); | |
while ( lines.length > 1 ) | |
Receive( lines.shift() ); | |
buffer = lines.join("\n"); | |
} | |
function Receive(msg) | |
{ | |
if (first) | |
{ | |
first = false; | |
return; | |
} | |
console.log(msg); | |
if (msg.charAt(0) == "{" && msg.charAt(msg.length-1) == "}") | |
{ | |
// msg = msg.split("'").join("\""); | |
msg = msg.replace("},devs", "],devs"); | |
msg = msg.replace("ion=", "ion:"); | |
msg = msg.replace("median=", "median:"); | |
var json = eval("("+msg+")"); | |
Process(json); | |
return; | |
} | |
onLog(msg); | |
} | |
function onSend() | |
{ | |
if (globalInfo && globalInfo.state == "manual") | |
socket.send(document.getElementById("send").value); | |
else | |
socket.send(document.getElementById("send").value + "\n"); | |
} | |
function onSafe() | |
{ | |
socket.send("safe\n"); | |
} | |
function onStart() | |
{ | |
socket.send("start\n"); | |
} | |
function onLog(msg) | |
{ | |
$("log").value = msg + "\n" + $("log").value; | |
} | |
$ = function(id) | |
{ | |
return document.getElementById(id); | |
} | |
function Process(json) | |
{ | |
//console.log(json); | |
if (json.state) | |
{ | |
$("state").innerHTML = json.state; | |
if (json.state == "safe") | |
$("state").style = ""; | |
else if (json.state == "automatic-prepare") | |
$("state").style = "color:black; background:yellow;"; | |
else if (json.state == "automatic-ignition") | |
$("state").style = "color:black; background:orange;"; | |
else if (json.state == "automatic-burning") | |
$("state").style = "color:white; background:#00b000;"; | |
else | |
$("state").style = "color:white; background:black;"; | |
} | |
if (json.config) | |
{ | |
$("infoErrors").innerHTML = json.errors; | |
$("infoCycles").innerHTML = json.cycles; | |
$("infoUptime").innerHTML = (json.uptime / 60).toFixed(1); | |
$("infoGastime").innerHTML = (json.plynSeconds / 60).toFixed(1); | |
$("infoConfig").innerHTML = json.config.temperatureSetMin + " / " + json.config.temperatureSetMax; | |
return; | |
} | |
if (json.devs) | |
{ | |
if (json.temp) | |
{ | |
$("teplotaVstup").innerHTML = json.temp.vstup; | |
$("teplotaVystup").innerHTML = json.temp.vystup; | |
$("teplotaTermostat").innerHTML = json.temp.termostat; | |
$("teplotaVzduch").innerHTML = json.temp.vzduch; | |
} | |
if (json.ion) | |
{ | |
$("devIon").innerHTML = json.ion[1]; | |
$("devIonVolts").innerHTML = json.ion[0]; | |
if (json.ion[1] == "Nehori") | |
$("devIon").style = ""; | |
else if (json.ion[1] == "Hori") | |
$("devIon").style = "color:white; background:#00b000;"; | |
else if (json.ion[1] == "Prechod") | |
$("devIon").style = "color:black; background:yellow;"; | |
else | |
$("devIon").style = "color:white; background:black;"; | |
} | |
if (json.errors) | |
{ | |
if (json.errors.length == 0) | |
$("poruchy").innerHTML = "Ziadne"; | |
else | |
$("poruchy").innerHTML = json.errors.join("<br>"); | |
} | |
if (json.devs) | |
{ | |
$("devCerpadlo").innerHTML = json.devs.cerpadlo; | |
$("devCerpadlo").style = json.devs.cerpadlo == "off" ? "" : "color:white; background:#00b000;"; | |
$("devVentilator").innerHTML = json.devs.ventilator; | |
$("devVentilator").style = json.devs.ventilator == "off" ? "" : "color:white; background:#00b000;"; | |
$("devZapalovanie").innerHTML = json.devs.zapalovanie; | |
$("devZapalovanie").style = json.devs.zapalovanie == "off" ? "" : "color:black; background:yellow;"; | |
$("devPlyn").innerHTML = json.devs.plyn; | |
$("devPlyn").style = json.devs.plyn == "off" ? "" : "color:white; background:black;"; | |
$("devKlapka").innerHTML = json.devs.klapka; | |
$("devKlapka").style = json.devs.klapka == "ZATVORENA" ? "" : "color:white; background:black;"; | |
} | |
if (json.devs && json.errors && json.ion) | |
{ | |
globalInfo = json; | |
globalInfoReceived = (new Date()).getTime(); | |
hideElements(["fan_error", "fan_icon", "fan_on"]); | |
if (json.errors.indexOf("ochrana") != -1) | |
showElement("fan_error"); | |
else if (json.devs.ventilator != "off") | |
showElement("fan_on"); | |
else | |
showElement("fan_icon"); | |
hideElements(["flame_on", "flame_off", "flame_trans"]); | |
if (json.ion[1] == "Hori") | |
showElement("flame_on"); | |
else if (json.ion[1] == "Prechod") | |
showElement("flame_trans"); | |
else | |
showElement("flame_off"); | |
hideElements(["ion_error", "ion_flame", "ion_icon", "ion_offline", "ion_trans"]); | |
if (json.ion[1] == "Hori") | |
showElement("ion_flame"); | |
else if (json.ion[1] == "Prechod") | |
showElement("ion_trans"); | |
else if (json.ion[1] == "Nehori") | |
showElement("ion_icon"); | |
else if (json.ion[1] == "Vypnuty") | |
showElement("ion_offline"); | |
else | |
showElement("ion_error"); | |
hideElements(["pump_error", "pump_icon", "pump_on"]); | |
if (json.errors.indexOf("ochrana") != -1) | |
showElement("pump_error"); | |
else if (json.devs.cerpadlo != "off") | |
showElement("pump_on"); | |
else | |
showElement("pump_icon"); | |
hideElements(["spark_icon", "spark_on"]); | |
if (json.devs.zapalovanie != "off") | |
showElement("spark_on"); | |
else | |
showElement("spark_icon"); | |
hideElements(["valve1_icon", "valve1_working"]); | |
if (json.devs.plyn != "off") | |
showElement("valve1_working"); | |
else | |
showElement("valve1_icon"); | |
hideElements(["valve2_icon", "valve2_working"]); | |
if (json.devs.plyn != "off") | |
showElement("valve2_working"); | |
else | |
showElement("valve2_icon"); | |
hideElements(["servo_icon", "servo_working"]); | |
if (json.devs.klapka != "ZATVORENA") | |
showElement("servo_working"); | |
else | |
showElement("servo_icon"); | |
$("info_servo").innerHTML = "klapka<br>" + json.devs.klapka + "<br>" + Math.floor(klapkaState*100/36) + "%"; | |
$("temp_out").innerHTML = "voda vystup<br>" + safeTemp(json.temp.vystup); | |
$("temp_in").innerHTML = "voda vstup<br>" + safeTemp(json.temp.vstup); | |
$("temp_air").innerHTML = "nasavanie<br>" + safeTemp(json.temp.vzduch);// + " °C"; | |
$("temp_body").innerHTML = "teplota kotla<br>" + safeTemp(json.temp.termostat);// + " °C"; | |
hideElements(["sensor1_icon", "sensor1_error"]); | |
if (json.errors.indexOf("plynVstup") != -1) | |
showElement("sensor1_error"); | |
else | |
showElement("sensor1_icon"); | |
hideElements(["sensor2_icon", "sensor2_error"]); | |
if (json.errors.indexOf("preslah") != -1) | |
showElement("sensor2_error"); | |
else | |
showElement("sensor2_icon"); | |
hideElements(["sensor3_icon", "sensor3_error"]); | |
if (json.errors.indexOf("tlakVyfuku") != -1) | |
showElement("sensor3_error"); | |
else | |
showElement("sensor3_icon"); | |
} | |
return; | |
} | |
onLog(JSON.stringify(json)); | |
} | |
function hideElements(arr) | |
{ | |
for (var i in arr) | |
$(arr[i]).style.display = "none"; | |
} | |
function showElement(e) | |
{ | |
$(e).style.display = "block"; | |
} | |
function safeTemp(t) | |
{ | |
if (t=="error") | |
return "<span style='background:red'>Chyba!</span>"; | |
return t + " °C"; | |
} | |
var klapkaState = 0; | |
setInterval(function() | |
{ | |
if (!globalInfo) | |
return; | |
if (globalInfo.devs.klapka == "otvorena") | |
klapkaState = 36; | |
if (globalInfo.devs.klapka == "ZATVORENA") | |
klapkaState = 0; | |
if (globalInfo.devs.klapka == "OTVARA SA") | |
klapkaState = Math.min(36, klapkaState+1); | |
if (globalInfo.devs.klapka == "ZATVARA SA") | |
klapkaState = Math.max(0, klapkaState-1); | |
var msPastLastInfo = (new Date()).getTime() - globalInfoReceived; | |
// $("onlineState").innerHTML = (msPastLastInfo < 5000) ? "Online" : "Offline"; | |
var online = msPastLastInfo < 3000; | |
$("modeoffline").className = !online ? "modebox selected" : "modebox"; | |
$("modesafe").className = (online && globalInfo.state == "safe") ? "modebox two selected" : "modebox two"; | |
$("modeprepare").className = (online && globalInfo.state == "automatic-prepare") ? "modebox selected" : "modebox"; | |
$("modeignition").className = (online && globalInfo.state == "automatic-ignition") ? "modebox selected" : "modebox"; | |
$("modeheating").className = (online && globalInfo.state == "automatic-burning") ? "modebox selected" : "modebox"; | |
$("modestandby").className = (online && globalInfo.state == "automatic-standby") ? "modebox selected" : "modebox"; | |
$("modemanual").className = (online && globalInfo.state == "manual") ? "modebox two selected" : "modebox two"; | |
$("modelocked").className = (online && globalInfo.state == "locked") ? "modebox two selected" : "modebox two"; | |
}, 1000); | |
$("logspan").innerHTML = "<a href='logs/" + datestamp() + ".txt'>Zaznam...</a>"; | |
function datestamp() | |
{ | |
var d = new Date(); | |
return d.getFullYear() + "-" + | |
("0" + (1+d.getMonth())).substr(-2) + "-" + | |
("0" + d.getDate()).substr(-2); | |
} | |
</script> | |
<script src="meas.js"></script> | |
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<html> | |
<head> | |
<script src="https://code.jquery.com/jquery-3.1.1.min.js"></script> | |
<script src="https://code.highcharts.com/highcharts.js"></script> | |
<script src="https://code.highcharts.com/highcharts-more.js"></script> | |
</head> | |
<body> | |
<div id="chart" style="width:100%; height:100%;"></div> | |
</body> | |
<script src="meas.js"></script> | |
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var autoupdate = true; | |
// grafy | |
function loadScript(url, handler) { | |
window.collected = []; | |
window._ = function(json) { | |
window.collected.push(json); | |
} | |
var head = document.getElementsByTagName('head')[0]; | |
var script = document.createElement('script'); | |
script.id = "remote_script"; | |
script.type = 'text/javascript'; | |
script.src = url; | |
script.onload = function() { | |
window._ = null; | |
handler(collected); | |
} | |
for (var i = 0; i < head.childNodes.length; i++) { | |
if (head.childNodes[i].nodeName == "SCRIPT" && head.childNodes[i].id == "remote_script") { | |
head.removeChild(head.childNodes[i]); | |
break; | |
} | |
} | |
head.appendChild(script); | |
} | |
function today() { | |
if (window.location.hash != "") { | |
autoupdate = false; | |
return window.location.hash.substr(1); | |
} | |
return new Date().getFullYear() + "-" + ("0" + (new Date().getMonth() + 1)).substr(-2) + "-" + ("0" + (new Date().getDate())).substr(-2); | |
} | |
/* | |
function joinData(d0, d1) | |
{ | |
var result = []; | |
for (var i=0; i<d0.length; i++) | |
result.push(Math.max(d0[i], d1[i])); | |
return result; | |
} | |
*/ | |
function reduceData(data, len) { | |
while (data.length > len) { | |
var newData = []; | |
for (var i = 0; i < data.length - 1; i += 2) | |
newData.push(data[i]); //.push(joinData(data[i], data[i+1])); | |
if (data.length & 1) | |
newData.push(data[data.length - 1]); | |
data = newData; | |
} | |
return data; | |
} | |
function update() { | |
loadScript("logs/" + today() + ".js", function(data) { | |
var buildSeriesPair = function(id, key, color, visible) { | |
var values; | |
if (key == "$ion") | |
values = data.map(function(json) { | |
return [json.ts, json.data.ion[0]]; | |
}); | |
else if (key == "$cerpadlo") | |
values = data.map(function(json) { | |
return [json.ts, (json.data.devs.cerpadlo != "off") * 1.7 + 8]; | |
}); | |
else if (key == "$plyn") | |
values = data.map(function(json) { | |
return [json.ts, (json.data.devs.plyn != "off") * 1.7 + 6]; | |
}); | |
else if (key == "$ventilator") | |
values = data.map(function(json) { | |
return [json.ts, (json.data.devs.ventilator != "off") * 1.7 + 10]; | |
}); | |
else | |
values = data.map(function(json) { | |
return [json.ts, json.data.temp[key]]; | |
}); | |
// values = reduceData(values, 350); | |
return [{ | |
name: id, | |
data: values, | |
zIndex: 1, | |
marker: { | |
enabled: false | |
// fillColor: 'white', | |
// lineWidth: 2, | |
// lineColor: Highcharts.getOptions().colors[color] | |
}, | |
color: Highcharts.getOptions().colors[color], | |
visible: visible | |
}]; | |
} | |
var series = []; | |
series = series.concat(buildSeriesPair("Teplota kotla", "termostat", 2, true)); | |
series = series.concat(buildSeriesPair("Teplota vody vystup", "vystup", 0, true)); | |
series = series.concat(buildSeriesPair("Teplota vody vstup", "vstup", 1, true)); | |
series = series.concat(buildSeriesPair("Vzduch nasavania", "vzduch", 3, false)); | |
series = series.concat(buildSeriesPair("Ionizacny senzor", "$ion", 6, false)); | |
series = series.concat(buildSeriesPair("Plyn", "$plyn", 5, false)); | |
series = series.concat(buildSeriesPair("Cerpadlo", "$cerpadlo", 4, false)); | |
series = series.concat(buildSeriesPair("Ventilator", "$ventilator", 7, false)); | |
Highcharts.setOptions({ | |
chart: { | |
zoomType: "x" | |
}, | |
global: { | |
useUTC: false | |
}, | |
plotOptions: { | |
series: { | |
animation: false | |
} | |
} | |
}); | |
var chart = Highcharts.chart('chart', { | |
credits: false, | |
title: { | |
text: "Meranie teploty" | |
}, | |
xAxis: { | |
type: 'datetime', | |
// tickInterval: 60 * 60 * 1000 | |
gridLineWidth: 1 | |
}, | |
yAxis: { | |
title: { | |
text: null | |
}, | |
gridLineWidth: 1, | |
min: 0, | |
// max: 350 | |
}, | |
tooltip: { | |
crosshairs: true, | |
shared: true, | |
valueSuffix: " " + String.fromCharCode(0xb0) + "C" | |
}, | |
series: series | |
}); | |
}); | |
} | |
update(); | |
if (autoupdate) | |
setInterval(update, 60 * 1000); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <OneWire.h> | |
#include <EEPROM.h> | |
#define nullptr 0 | |
#define _ASSERT(cond) {if(!(cond)) { Serial.print(F("{assert:'")); Serial.print(#cond); Serial.print("', line:"); Serial.print(__LINE__); Serial.print("}\n"); }} | |
float globalIon = 0; | |
float globalMedianIon = 0; | |
const char appVersion[] = "{version:1.1, builddate:'" __DATE__ "', buildtime:'" __TIME__ "'}\n"; | |
template<typename T, int N> | |
class CMedian | |
{ | |
T memory[N]; | |
bool bFirst{true}; | |
public: | |
void Push(T t) | |
{ | |
if (bFirst) | |
{ | |
for (byte i=0; i<N; i++) | |
memory[i] = t; | |
bFirst = false; | |
return; | |
} | |
for (byte i=1; i<N; i++) | |
memory[i-1] = memory[i]; | |
memory[N-1] = t; | |
} | |
T Get() | |
{ | |
byte indices[N]; | |
for (byte i=0; i<N; i++) | |
indices[i] = i; | |
for (byte i=0; i<N; i++) | |
for (byte j=i+1; j<N; j++) | |
if (memory[indices[i]] > memory[indices[j]]) | |
{ | |
byte temp = indices[i]; | |
indices[i] = indices[j]; | |
indices[j] = temp; | |
} | |
return memory[indices[N/2]]; | |
} | |
T PushGet(T t) | |
{ | |
Push(t); | |
return Get(); | |
} | |
void Dump() | |
{ | |
Serial.print("median = "); | |
for (byte i=0; i<N; i++) | |
{ | |
Serial.print(" "); | |
Serial.print(memory[i]); | |
} | |
Serial.print(" -> "); | |
Serial.print(Get()); | |
Serial.print("\n"); | |
} | |
}; | |
class CLogicState | |
{ | |
CLogicState* m_pPrevState{nullptr}; | |
static CLogicState* m_pLastState; | |
public: | |
CLogicState() | |
{ | |
if (!m_pLastState) | |
{ | |
m_pLastState = this; | |
} | |
else | |
{ | |
CLogicState* pTemp = m_pLastState; | |
m_pLastState = this; | |
m_pPrevState = pTemp; | |
} | |
} | |
static CLogicState* FindState(const char* name) | |
{ | |
CLogicState* pState = m_pLastState; | |
while (pState) | |
{ | |
if (pState->Name() == name) | |
return pState; | |
pState = pState->m_pPrevState; | |
} | |
return nullptr; | |
} | |
virtual char* Name() = 0; | |
virtual void Enter() = 0; | |
virtual void Do() = 0; | |
}; | |
CLogicState* CLogicState::m_pLastState{nullptr}; | |
class CStateMachine | |
{ | |
public: | |
void Do(); | |
void Goto(const char* name); | |
void Error(const __FlashStringHelper* message); | |
void Info(const __FlashStringHelper* message); | |
CLogicState* m_pState {0}; | |
const char* m_pGotoState {"safe"}; | |
} logic; | |
class CTimer | |
{ | |
public: | |
int secondsOn{0}; | |
int secondsOff{9999}; | |
void Update(bool b) | |
{ | |
if (b) | |
{ | |
secondsOff = 0; | |
if (secondsOn < 9999) | |
secondsOn++; | |
} else | |
{ | |
secondsOn = 0; | |
if (secondsOff < 9999) | |
secondsOff++; | |
} | |
} | |
}; | |
class CConfig | |
{ | |
static const unsigned int nConfigOffset; | |
static const unsigned long nCheckToken; | |
private: | |
unsigned long nToken{0}; | |
unsigned int nClassSize{0}; | |
public: | |
float temperatureSetMin{55}; | |
float temperatureSetMax{60}; | |
float temperatureBodyMax{90}; | |
unsigned int postHeatPumpInterval{20*60}; | |
unsigned int initPumpInterval{1*60}; | |
unsigned int hotPumpInterval{5*60}; | |
unsigned int postHeatSleepInterval{30*60}; | |
// DS18S20 ma rozsah -55 do +125 | |
float minSafeTemp{-20}; | |
float maxSafeTemp{95}; | |
bool bNoPump{false}; | |
void test() | |
{ | |
defaults(); | |
postHeatSleepInterval = 10*60; | |
temperatureSetMin = 35.0f; | |
temperatureSetMax = 40.0f; | |
temperatureBodyMax = 45.0f; | |
} | |
void dump() | |
{ | |
Serial.print(F("{temperatureSetMin:")); | |
Serial.print(temperatureSetMin); | |
Serial.print(F(",temperatureSetMax:")); | |
Serial.print(temperatureSetMax); | |
Serial.print(F(",temperatureBodyMax:")); | |
Serial.print(temperatureBodyMax); | |
Serial.print(",postHeatPumpInterval:"); | |
Serial.print(postHeatPumpInterval); | |
Serial.print(",initPumpInterval:"); | |
Serial.print(initPumpInterval); | |
Serial.print(",hotPumpInterval:"); | |
Serial.print(hotPumpInterval); | |
Serial.print(",postHeatSleepInterval:"); | |
Serial.print(postHeatSleepInterval); | |
Serial.print(",minSafeTemp:"); | |
Serial.print(minSafeTemp); | |
Serial.print(",maxSafeTemp:"); | |
Serial.print(maxSafeTemp); | |
Serial.print(",noPump:"); | |
Serial.print(bNoPump); | |
Serial.print("}"); | |
} | |
bool set(char* cmd) | |
{ | |
if (tryset(cmd, "min=", temperatureSetMin)) return true; | |
if (tryset(cmd, "max=", temperatureSetMax)) return true; | |
if (tryset(cmd, "bodymax=", temperatureBodyMax)) return true; | |
if (tryset(cmd, "postpump=", postHeatPumpInterval)) return true; | |
if (tryset(cmd, "initpump=", initPumpInterval)) return true; | |
if (tryset(cmd, "hotpump=", hotPumpInterval)) return true; | |
if (tryset(cmd, "postsleep=", postHeatSleepInterval)) return true; | |
if (tryset(cmd, "safemin=", minSafeTemp)) return true; | |
if (tryset(cmd, "safemax=", maxSafeTemp)) return true; | |
if (tryset(cmd, "nopump=", bNoPump)) return true; | |
return false; | |
} | |
void defaults() | |
{ | |
*this = CConfig(); | |
} | |
bool isValid() | |
{ | |
if (nToken != nCheckToken) | |
return false; | |
if (nClassSize != sizeof(CConfig)) | |
return false; | |
if (temperatureSetMin < 20 || temperatureSetMin > 100 || | |
temperatureSetMax < 20 || temperatureSetMax > 100 || | |
temperatureBodyMax < 20 || temperatureBodyMax > 110 || | |
temperatureSetMin > temperatureSetMax || | |
minSafeTemp < -40 || minSafeTemp > 120 || | |
maxSafeTemp < -40 || maxSafeTemp > 120 || | |
minSafeTemp > maxSafeTemp) | |
{ | |
return false; | |
} | |
if (postHeatPumpInterval > 9000 || | |
initPumpInterval > 9000 || | |
hotPumpInterval > 9000 || | |
postHeatSleepInterval > 9000) | |
{ | |
return false; | |
} | |
if (bNoPump != false && bNoPump != true) | |
return false; | |
return true; | |
} | |
void load() | |
{ | |
EEPROM.get(nConfigOffset, *this); | |
if (!isValid()) | |
{ | |
Serial.print("{error:'read settings failed, resetting to defaults'}"); | |
defaults(); | |
_ASSERT(isValid()); | |
save(); | |
} | |
} | |
void save() | |
{ | |
nToken = nCheckToken; | |
nClassSize = sizeof(CConfig); | |
EEPROM.put(nConfigOffset, *this); | |
} | |
template<typename T> | |
bool tryset(char* cmd, const char* key, T& target) | |
{ | |
byte l = strlen(key); | |
if (strncmp(cmd, key, l) == 0) | |
{ | |
target = (T)atoi(cmd+l); | |
return true; | |
} | |
return false; | |
} | |
} config; | |
const unsigned int CConfig::nConfigOffset{37}; | |
const unsigned long CConfig::nCheckToken{'bla4'}; | |
class CMachine | |
{ | |
static const float invalidTemperature = -999.0f; | |
public: | |
enum EIon | |
{ | |
Error, | |
Hori, | |
Nehori, | |
Prechod, | |
Vypnuty | |
}; | |
enum EKlapka | |
{ | |
Zatvorena=0, | |
Otvorena=1 | |
}; | |
struct { | |
bool ventilator{false}; | |
bool cerpadlo{false}; | |
bool zapalovanie{false}; | |
bool plyn{false}; | |
bool klapka{false}; | |
} sensorsOut; | |
struct { | |
EIon ion{Error}; | |
bool startStop{false}; | |
bool tlakPlynu{false}; | |
bool tlakVyfuku{false}; | |
bool preslah{false}; | |
bool ochranaCerpadlo{false}; | |
bool ochranaVentilator{false}; | |
} sensorsIn; | |
struct { | |
float vodaVstup{invalidTemperature}; | |
float vodaVystup{invalidTemperature}; | |
float kotol{invalidTemperature}; | |
// float vyfuk; | |
float nasavanie{invalidTemperature}; | |
} temperatures; | |
struct { | |
CTimer ventilator; | |
CTimer cerpadlo; | |
CTimer zapalovanie; | |
CTimer plyn; | |
CTimer hori; | |
CTimer klapka; | |
unsigned long uptime{0}; | |
unsigned long plynSeconds{0}; | |
unsigned int heatCycles{0}; | |
unsigned int errors{0}; | |
} timers; | |
bool AnyError() | |
{ | |
// TODO: podmienka plyn & zapalovanie & nehori | |
if (sensorsOut.plyn && sensorsIn.ion != Hori && timers.plyn.secondsOn >= 20) | |
{ | |
if (sensorsIn.tlakPlynu) | |
{ | |
logic.Error(F("Plyn spuseny bez zapalovania. Zavri plynovy ventil")); | |
return true; | |
} | |
} | |
if (sensorsOut.zapalovanie && timers.klapka.secondsOn < 36) | |
{ | |
logic.Error(F("Klapka este zavreta")); | |
return true; | |
} | |
if (timers.zapalovanie.secondsOn >= 10) | |
{ | |
logic.Error(F("Zapalovanie zapnute prilis dlho")); | |
return true; | |
} | |
if (!sensorsIn.tlakPlynu) | |
{ | |
logic.Error(F("Tlak plynu na vstupe nespravny")); | |
return true; | |
} | |
if (!sensorsIn.tlakVyfuku) | |
{ | |
logic.Error(F("Upchaty vyfuk")); | |
return true; | |
} | |
if (!sensorsIn.preslah) | |
{ | |
logic.Error(F("Preslahovy senzor hlasi chybu")); | |
return true; | |
} | |
if (!sensorsIn.ochranaCerpadlo || | |
!sensorsIn.ochranaVentilator) | |
{ | |
logic.Error(F("Nadprudove ochrany hlasia chybu")); | |
return true; | |
} | |
if (timers.uptime>3) | |
{ | |
if (!safeTemp(temperatures.vodaVystup) || | |
!safeTemp(temperatures.vodaVstup) || | |
!safeTemp(temperatures.kotol) || | |
!safeTemp(temperatures.nasavanie) ) | |
{ | |
logic.Error(F("Nepodarili sa odmerat teploty, alebo teplota mimo rozsahu")); | |
return true; | |
} | |
} | |
return false; | |
} | |
bool safeTemp(float t) | |
{ | |
if (t == invalidTemperature) | |
return false; | |
if (t < config.minSafeTemp) | |
return false; | |
if (t > config.maxSafeTemp) | |
return false; | |
return true; | |
} | |
void Update1s() | |
{ | |
timers.ventilator.Update(sensorsOut.ventilator); | |
timers.cerpadlo.Update(sensorsOut.cerpadlo); | |
timers.zapalovanie.Update(sensorsOut.zapalovanie); | |
timers.plyn.Update(sensorsOut.plyn); | |
timers.hori.Update(sensorsIn.ion == Hori); | |
timers.klapka.Update(sensorsOut.klapka); | |
timers.uptime++; | |
if (sensorsOut.plyn) | |
timers.plynSeconds++; | |
Serial.print("{state:'"); | |
Serial.print(logic.m_pState->Name()); | |
Serial.print("',ion:["); | |
Serial.print(globalIon); | |
Serial.print(",'"); | |
switch (sensorsIn.ion) | |
{ | |
case Error: Serial.print(F("Error")); break; | |
case Hori: Serial.print(F("Hori")); break; | |
case Nehori: Serial.print(F("Nehori")); break; | |
case Prechod: Serial.print(F("Prechod")); break; | |
case Vypnuty: Serial.print(F("Vypnuty")); break; | |
} | |
Serial.print("'],temp:{"); | |
Serial.print(F("vstup:")); | |
printTemp(temperatures.vodaVstup); | |
Serial.print(F(",vystup:")); | |
printTemp(temperatures.vodaVystup); | |
Serial.print(F(",termostat:")); | |
printTemp(temperatures.kotol); | |
Serial.print(F(",vzduch:")); | |
printTemp(temperatures.nasavanie); | |
Serial.print(F("},errors:[")); | |
if (!sensorsIn.tlakPlynu) | |
Serial.print(F("'plynVstup',")); | |
if (!sensorsIn.tlakVyfuku) | |
Serial.print(F("'tlakVyfuku',")); | |
if (!sensorsIn.preslah) | |
Serial.print(F("'preslah',")); | |
if (!sensorsIn.ochranaCerpadlo || !sensorsIn.ochranaVentilator) | |
Serial.print(F("'ochrana',")); | |
Serial.print(F("],devs:{")); | |
Serial.print(F("cerpadlo:")); | |
Serial.print(sensorsOut.cerpadlo ? "'on'" : "'off'"); | |
Serial.print(F(",ventilator:")); | |
Serial.print(sensorsOut.ventilator ? "'on'" : "'off'"); | |
Serial.print(F(",zapalovanie:")); | |
Serial.print(sensorsOut.zapalovanie ? "'on'" : "'off'"); | |
Serial.print(F(",plyn:")); | |
Serial.print(sensorsOut.plyn ? "'ON'" : "'off'"); | |
Serial.print(F(",klapka:")); | |
if (timers.klapka.secondsOn >= 36) | |
Serial.print(F("'otvorena'")); | |
else if (timers.klapka.secondsOff >= 36) | |
Serial.print(F("'ZATVORENA'")); | |
else if (sensorsOut.klapka == CMachine::Otvorena) | |
Serial.print(F("'OTVARA SA'")); | |
else if (sensorsOut.klapka == CMachine::Zatvorena) | |
Serial.print(F("'ZATVARA SA'")); | |
Serial.print(F("}}\n")); | |
} | |
void printTemp(float t) | |
{ | |
if (t != invalidTemperature) | |
Serial.print(t); | |
else | |
Serial.print(F("'error'")); | |
} | |
void Update10s() | |
{ | |
Serial.print(F("{uptime:")); | |
Serial.print(timers.uptime); | |
Serial.print(F(",config:")); | |
config.dump(); | |
Serial.print(",errors:"); | |
Serial.print(timers.errors); | |
Serial.print(",cycles:"); | |
Serial.print(timers.heatCycles); | |
Serial.print(",plynSeconds:"); | |
Serial.print(timers.plynSeconds); | |
Serial.print("}\n"); | |
} | |
} machine; | |
class CLogicStateSafe : public CLogicState | |
{ | |
public: | |
virtual char* Name() override | |
{ | |
return "safe"; | |
} | |
virtual void Enter() override | |
{ | |
m_bCerpadloIdle = false; | |
// TODO: preco tu bol && m_bStartup ?? // !false && false = false | |
m_bVentilatorIdle = !machine.sensorsOut.ventilator /*&& m_bStartup*/; | |
machine.timers.ventilator.secondsOn = 0; | |
m_nCerpadloInterval = config.initPumpInterval; | |
machine.sensorsOut.plyn = false; | |
machine.sensorsOut.zapalovanie = false; | |
machine.sensorsOut.klapka = CMachine::Zatvorena; | |
m_bStartup = false; | |
} | |
virtual void Do() override | |
{ | |
if (JeMiTeplo()) | |
m_nCerpadloInterval = config.hotPumpInterval; | |
DobehCerpadla(m_bCerpadloIdle, m_nCerpadloInterval); | |
DobehVentilatora(m_bVentilatorIdle, 30); | |
if (machine.sensorsIn.startStop) | |
{ | |
if (machine.AnyError()) | |
{ | |
logic.Error(F("porucha vstupneho senzora alebo teplomera")); | |
} else | |
{ | |
if (IsFinished()) | |
logic.Goto("automatic-prepare"); | |
else | |
logic.Error(F("Prebieha 30s interval vyfukovania spalin")); | |
} | |
} | |
} | |
bool IsFinished() | |
{ | |
return m_bVentilatorIdle; | |
} | |
static void DobehVentilatora(bool& bVentilatorIdle, int nTime) | |
{ | |
machine.sensorsOut.ventilator = !bVentilatorIdle; | |
if (!bVentilatorIdle) | |
{ | |
bVentilatorIdle = machine.timers.ventilator.secondsOn >= nTime; | |
} | |
} | |
static void DobehCerpadla(bool& bCerpadloIdle, int nTime) | |
{ | |
if (CerpadloShouldRun()) | |
{ | |
bCerpadloIdle = false; | |
machine.sensorsOut.cerpadlo = true; | |
machine.timers.cerpadlo.secondsOn = 0; | |
} else | |
{ | |
if (!bCerpadloIdle) | |
bCerpadloIdle = machine.timers.cerpadlo.secondsOn >= nTime; | |
machine.sensorsOut.cerpadlo = !bCerpadloIdle; | |
} | |
} | |
static bool JeMiTeplo() | |
{ | |
if (machine.temperatures.vodaVystup >= 30.0f || | |
machine.temperatures.vodaVstup >= 30.0f || | |
machine.temperatures.kotol >= 30.0f) | |
return true; | |
return false; | |
} | |
static bool CerpadloShouldRun() | |
{ | |
if (machine.AnyError()) | |
return true; | |
if (JeMiTeplo()) | |
return true; | |
if (abs(machine.temperatures.vodaVystup - machine.temperatures.vodaVstup) > 10.0f) | |
return true; | |
// 20 min po kureni vynut cerpadlo | |
if (machine.timers.hori.secondsOff < config.postHeatPumpInterval || machine.timers.plyn.secondsOff < config.postHeatPumpInterval) | |
return true; | |
return false; | |
} | |
bool m_bCerpadloIdle {false}; | |
bool m_bVentilatorIdle {false}; | |
bool m_bStartup {true}; | |
int m_nCerpadloInterval{60}; | |
} stateSafe; | |
class CStateAutomaticStandby : public CLogicState | |
{ | |
public: | |
virtual char* Name() override | |
{ | |
return "automatic-standby"; | |
} | |
virtual void Enter() override | |
{ | |
m_bCerpadloIdle = false; | |
m_bVentilatorIdle = false; //!machine.sensorsOut.ventilator; | |
machine.sensorsOut.plyn = false; | |
machine.sensorsOut.zapalovanie = false; | |
machine.sensorsOut.klapka = CMachine::Zatvorena; // zatvor klapku, tu otvarame az ked ideme kurit | |
machine.timers.ventilator.secondsOn = 0; | |
} | |
virtual void Do() override | |
{ | |
// v Dobeh cerpadla uz je implementovany post heat interval, | |
// ale ked teplota znova vystupa nad 30, spustame dlhy interval | |
CLogicStateSafe::DobehCerpadla(m_bCerpadloIdle, config.hotPumpInterval); | |
// TODO: chybal dobeh ventilatora | |
CLogicStateSafe::DobehVentilatora(m_bVentilatorIdle, 30); | |
if (machine.sensorsIn.startStop) | |
{ | |
if (!m_bVentilatorIdle) | |
{ | |
logic.Info(F("Vyfukujem spaliny")); | |
} else | |
if (ShouldStopHeat()) | |
{ | |
logic.Info(F("Najprv musim vychladnut")); | |
} else | |
{ | |
logic.Goto("automatic-prepare"); | |
} | |
return; | |
} | |
// musi pockat kym prejde aspon 30 min pred tym nez znova zapne | |
if (machine.timers.hori.secondsOff < config.postHeatSleepInterval || machine.timers.plyn.secondsOff < config.postHeatSleepInterval) | |
return; | |
if (ShouldStartHeat()) | |
logic.Goto("automatic-prepare"); | |
} | |
static bool ShouldStartHeat() | |
{ | |
// 5 stupnova hystereza proti oscilaciam | |
if (machine.temperatures.kotol > config.temperatureBodyMax-5.0f) | |
return false; | |
return max(machine.temperatures.vodaVstup, machine.temperatures.vodaVystup) <= config.temperatureSetMin; | |
} | |
static bool ShouldStopHeat() | |
{ | |
if (machine.temperatures.kotol > config.temperatureBodyMax) | |
return true; | |
return max(machine.temperatures.vodaVstup, machine.temperatures.vodaVystup) > config.temperatureSetMax; | |
} | |
bool m_bCerpadloIdle; | |
bool m_bVentilatorIdle; | |
} stateAutomaticStandby; | |
class : public CLogicState | |
{ | |
public: | |
virtual char* Name() override | |
{ | |
return "automatic-prepare"; | |
} | |
virtual void Enter() override | |
{ | |
_ASSERT(CStateAutomaticStandby::ShouldStartHeat()); | |
machine.sensorsOut.cerpadlo = true; | |
machine.sensorsOut.ventilator = true; | |
// TODO: vymazat pocitadlo klapky? | |
machine.timers.klapka.secondsOn = 0; | |
machine.sensorsOut.klapka = CMachine::Otvorena; | |
} | |
virtual void Do() override | |
{ | |
if (machine.sensorsIn.startStop) | |
{ | |
// mozeme zastavit ventilator, zatial nic nerobil | |
machine.sensorsOut.ventilator = false; | |
logic.Goto("safe"); | |
return; | |
} | |
if (machine.timers.klapka.secondsOn >= 3) | |
{ | |
// Zeby ventilator sposobil pokles napatia? | |
// 4.16, 4.03 | |
if (machine.sensorsIn.ion != CMachine::Nehori) | |
{ | |
logic.Error(F("Nemozem spustit zapalovanie kvoli ionizacnemu senzoru")); | |
Serial.print(F("{ion:")); | |
Serial.print(globalIon); | |
Serial.print(F(",median:")); | |
Serial.print(globalMedianIon); | |
Serial.print(F(",time:")); | |
Serial.print(machine.timers.klapka.secondsOn); | |
Serial.print(F("}\n")); | |
return; | |
} | |
} | |
if (machine.timers.cerpadlo.secondsOn >= 10 && | |
machine.timers.ventilator.secondsOn >= 30 && | |
machine.timers.klapka.secondsOn >= 36) | |
{ | |
logic.Goto("automatic-ignition"); | |
return; | |
} | |
} | |
} stateAutomaticPrepare; | |
class : public CLogicState | |
{ | |
public: | |
virtual char* Name() override | |
{ | |
return "automatic-ignition"; | |
} | |
virtual void Enter() override | |
{ | |
_ASSERT(machine.sensorsIn.ion == CMachine::Nehori); | |
machine.sensorsOut.plyn = true; | |
machine.sensorsOut.zapalovanie = true; | |
machine.timers.zapalovanie.secondsOn = 0; | |
machine.timers.zapalovanie.secondsOff = 0; | |
machine.timers.heatCycles++; | |
} | |
virtual void Do() override | |
{ | |
if (machine.sensorsIn.startStop) | |
{ | |
machine.sensorsOut.zapalovanie = false; | |
machine.sensorsOut.plyn = false; | |
logic.Goto("safe"); | |
return; | |
} | |
if ( machine.timers.hori.secondsOn >= 2) | |
{ | |
machine.sensorsOut.zapalovanie = false; | |
} | |
if (!machine.sensorsOut.zapalovanie) | |
{ | |
// ak uz dozapaloval cakame na rozhorenie | |
if (machine.sensorsIn.ion != CMachine::Hori && machine.sensorsIn.ion != CMachine::Prechod) | |
{ | |
logic.Error(F("Nepodaril sa zapalit plamen")); | |
return; | |
} | |
} | |
if (machine.timers.hori.secondsOn >= 10) | |
{ | |
logic.Info(F("Plamen vydrzal stabilny 10 seknud")); | |
logic.Goto("automatic-burning"); | |
return; | |
} | |
// je v prechode, iskra zrusena, cakame kym sa rozhori | |
if (machine.timers.zapalovanie.secondsOff >= 30) | |
{ | |
logic.Error(F("Nepodaril sa zapalit plamen, stale v prechode")); | |
return; | |
} | |
if (machine.sensorsOut.zapalovanie) | |
{ | |
if (machine.timers.zapalovanie.secondsOn >= 5) | |
{ | |
machine.sensorsOut.zapalovanie = false; | |
} | |
} | |
} | |
} stateAutomaticIngition; | |
class : public CLogicState | |
{ | |
public: | |
virtual char* Name() override | |
{ | |
return "automatic-burning"; | |
} | |
virtual void Enter() override | |
{ | |
} | |
virtual void Do() override | |
{ | |
if (machine.sensorsIn.startStop) | |
{ | |
logic.Goto("safe"); | |
return; | |
} | |
if (machine.sensorsIn.ion != CMachine::Hori) | |
{ | |
logic.Error(F("Zhasol plamen")); | |
return; | |
} | |
if (CStateAutomaticStandby::ShouldStopHeat()) | |
{ | |
logic.Goto("automatic-standby"); | |
} | |
} | |
} stateAutomaticBurning; | |
class : public CLogicState | |
{ | |
public: | |
virtual char* Name() override | |
{ | |
return "manual"; | |
} | |
virtual void Enter() override | |
{ | |
} | |
virtual void Do() override | |
{ | |
if (machine.sensorsIn.startStop) | |
{ | |
logic.Goto("safe"); | |
return; | |
} | |
} | |
} stateManual; | |
class : public CLogicState | |
{ | |
public: | |
virtual char* Name() override | |
{ | |
return "locked"; | |
} | |
virtual void Enter() override | |
{ | |
} | |
virtual void Do() override | |
{ | |
} | |
} stateLocked; | |
void CStateMachine::Do() | |
{ | |
if (m_pGotoState) | |
{ | |
Serial.print("{state:'"); | |
Serial.print(m_pState ? m_pState->Name() : "startup"); | |
Serial.print("',goto:'"); | |
Serial.print(m_pGotoState); | |
Serial.print("'}\n"); | |
CLogicState* pState = CLogicState::FindState(m_pGotoState); | |
m_pGotoState = nullptr; | |
_ASSERT(pState); | |
if (!pState) | |
pState = CLogicState::FindState("safe"); | |
if (pState) | |
{ | |
pState->Enter(); | |
m_pState = pState; | |
if (m_pState->Name() != "locked" && machine.AnyError()) | |
{ | |
Goto("safe"); | |
return; | |
} | |
} | |
} | |
m_pState->Do(); | |
if (m_pState->Name() == "locked") | |
return; | |
if (machine.AnyError() && m_pState->Name() != "safe") | |
Goto("safe"); | |
} | |
void CStateMachine::Error(const __FlashStringHelper* message) | |
{ | |
// TODO: oplati sa nam zahltit cely vystup? | |
// vypis | |
machine.timers.errors++; | |
Serial.print(F("{state:'")); | |
Serial.print(m_pState->Name()); | |
Serial.print(F("',error:'")); | |
Serial.print(message); | |
Serial.print(F("'}\n")); | |
if (m_pState->Name() != "safe") | |
{ | |
Goto("safe"); | |
} | |
else | |
{ | |
// Uz sme v safe mode, vsetko je vypnute, redukujeme chybove hlasky | |
delay(500); | |
} | |
} | |
void CStateMachine::Info(const __FlashStringHelper* message) | |
{ | |
// vypis | |
Serial.print(F("{state:'")); | |
Serial.print(m_pState->Name()); | |
Serial.print(F("',info:'")); | |
Serial.print(message); | |
Serial.print(F("'}\n")); | |
} | |
void CStateMachine::Goto(const char* name) | |
{ | |
if (name == m_pState->Name()) | |
return; | |
m_pGotoState = name; | |
} | |
OneWire oneWire(A4); // nPinTeplomer | |
class CHardware | |
{ | |
private: | |
static const byte nPinOutObehoveCerpadlo = 4; // oranzovy_ | |
static const byte nPinOutVentilator = 5; // oranzovy | |
static const byte nPinOutPlyn = 6; // zeleny_ | |
static const byte nPinOutZapalovanie = 7; // zeleny | |
static const byte nPinOutServoKlapky = 3; // hnedy_ | |
static const byte nPinInStartStop = 8; // zeleny HT/ start stop | |
static const byte nPinInPreslah = 9; // zeleny_ PZS | |
static const byte nPinInTlakSpalin = 10; // oranzovy MSP | |
static const byte nPinInTlakPlynu = 11; // oranzovy_ MPL | |
static const byte nPinInOchrany = 12; // hnedy_ R100A 1/2 | |
static const byte nPinInIonizacnySenzor = A5; | |
static const byte nPinTeplomer = A4; | |
private: | |
struct Status | |
{ | |
byte outObehoveCerpadlo{0}; | |
byte outVentilator{0}; | |
byte outPlyn{0}; | |
byte outZapalovanie{0}; | |
byte outServoKlapky{0}; | |
byte inStartStop{0}; | |
byte inPreslah{0}; | |
byte inTlakSpalin{0}; | |
byte inTlakPlynu{0}; | |
byte inOchrany{0}; | |
uint16_t inIonizacnySenzor{0}; | |
float vodaVstup; | |
float vodaVystup; | |
float termostat; | |
float nasavanie; | |
}; | |
struct Status status; | |
byte nStartStopDebounce{2}; | |
public: | |
void Init() | |
{ | |
Serial.begin(9600); | |
Serial.print(F("{message:'Startujem kontroler kotla ORK100...'}\n")); | |
Serial.print(appVersion); | |
pinMode(nPinOutObehoveCerpadlo, OUTPUT); | |
digitalWrite(nPinOutObehoveCerpadlo, 0); | |
pinMode(nPinOutVentilator, OUTPUT); | |
digitalWrite(nPinOutVentilator, 0); | |
pinMode(nPinOutPlyn, OUTPUT); | |
digitalWrite(nPinOutPlyn, 0); | |
pinMode(nPinOutZapalovanie, OUTPUT); | |
digitalWrite(nPinOutZapalovanie, 0); | |
pinMode(nPinOutServoKlapky, OUTPUT); | |
digitalWrite(nPinOutServoKlapky, 0); | |
pinMode(nPinInStartStop, INPUT_PULLUP); | |
pinMode(nPinInPreslah, INPUT_PULLUP); | |
pinMode(nPinInTlakSpalin, INPUT_PULLUP); | |
pinMode(nPinInTlakPlynu, INPUT_PULLUP); | |
pinMode(nPinInOchrany, INPUT_PULLUP); | |
pinMode(nPinInIonizacnySenzor, INPUT); | |
} | |
void UpdateOutput(struct Status& status) | |
{ | |
digitalWrite(nPinOutObehoveCerpadlo, status.outObehoveCerpadlo); | |
digitalWrite(nPinOutVentilator, status.outVentilator); | |
digitalWrite(nPinOutPlyn, status.outPlyn); | |
digitalWrite(nPinOutZapalovanie, status.outZapalovanie); | |
digitalWrite(nPinOutServoKlapky, status.outServoKlapky); | |
} | |
void UpdateInput(struct Status& status) | |
{ | |
// status.inHlavnyTeplomer = !digitalRead(nPinInHlavnyTeplomer); | |
status.inStartStop = !digitalRead(nPinInStartStop); | |
status.inPreslah = !digitalRead(nPinInPreslah); | |
status.inTlakSpalin = !digitalRead(nPinInTlakSpalin); | |
status.inTlakPlynu = !digitalRead(nPinInTlakPlynu); | |
status.inOchrany = !digitalRead(nPinInOchrany); | |
status.inIonizacnySenzor = analogRead(nPinInIonizacnySenzor); | |
} | |
void Update() | |
{ | |
UpdateInput(status); | |
static CMedian<byte, 5> medStartStop; | |
medStartStop.Push(status.inStartStop); | |
if (medStartStop.Get() && nStartStopDebounce == 0) | |
{ | |
machine.sensorsIn.startStop = true; | |
nStartStopDebounce = 2; | |
} | |
else | |
machine.sensorsIn.startStop = false; | |
static CMedian<byte, 5> medPlyn; | |
static CMedian<byte, 5> medVyfuk; | |
static CMedian<byte, 5> medPreslah; | |
static CMedian<byte, 5> medOchranaCerpadlo; | |
static CMedian<byte, 5> medOchranaVentilator; | |
machine.sensorsIn.tlakPlynu = medPlyn.PushGet(status.inTlakPlynu); | |
machine.sensorsIn.tlakVyfuku = medVyfuk.PushGet(status.inTlakSpalin); | |
machine.sensorsIn.preslah = medPreslah.PushGet(status.inPreslah); | |
machine.sensorsIn.ochranaCerpadlo = medOchranaCerpadlo.PushGet(status.inOchrany); | |
machine.sensorsIn.ochranaVentilator = medOchranaVentilator.PushGet(status.inOchrany); | |
machine.sensorsIn.ion = CMachine::Error; | |
globalIon = status.inIonizacnySenzor*5.0f/1024.0f; | |
static CMedian<int, 5> medianIon; | |
medianIon.Push(status.inIonizacnySenzor); | |
//medianIon.Dump(); | |
float ionVolts = medianIon.Get()*5.0f/1024.0f; | |
globalMedianIon = ionVolts; | |
if (ionVolts >= 4.2 /*&& ionVolts <= 4.95f*/) | |
machine.sensorsIn.ion = CMachine::Nehori; | |
if (ionVolts <= 0.5f) | |
machine.sensorsIn.ion = CMachine::Vypnuty; | |
if (ionVolts >= 1.0f && ionVolts <= 3.2f) | |
machine.sensorsIn.ion = CMachine::Hori; | |
if (ionVolts >= 3.2f && ionVolts <= 4.2f) | |
machine.sensorsIn.ion = CMachine::Prechod; | |
// zapracuj aj vystup z nadprudovej ochrany | |
status.outVentilator = machine.sensorsIn.ochranaVentilator && machine.sensorsOut.ventilator; | |
status.outObehoveCerpadlo = machine.sensorsIn.ochranaCerpadlo && machine.sensorsOut.cerpadlo && (!config.bNoPump); | |
status.outZapalovanie = machine.sensorsOut.zapalovanie; | |
status.outPlyn = machine.sensorsOut.plyn; | |
status.outServoKlapky = machine.sensorsOut.klapka; | |
UpdateOutput(status); | |
} | |
void Update1s() | |
{ | |
if (nStartStopDebounce > 0) | |
nStartStopDebounce--; | |
if (status.outZapalovanie) | |
return; | |
UpdateTemperatures(status); | |
machine.temperatures.vodaVstup = status.vodaVstup; | |
machine.temperatures.vodaVystup = status.vodaVystup; | |
machine.temperatures.nasavanie = status.nasavanie; | |
machine.temperatures.kotol = status.termostat; | |
} | |
float dallasTemperature(uint8_t* deviceAddress, uint8_t* scratchPad) | |
{ | |
typedef enum { | |
// Model IDs | |
DS18S20MODEL =0x10, | |
DS18B20MODEL =0x28, | |
DS1822MODEL =0x22, | |
// Scratchpad locations | |
TEMP_LSB =0, | |
TEMP_MSB =1, | |
HIGH_ALARM_TEMP =2, | |
LOW_ALARM_TEMP =3, | |
CONFIGURATION =4, | |
INTERNAL_BYTE =5, | |
COUNT_REMAIN =6, | |
COUNT_PER_C =7, | |
SCRATCHPAD_CRC =8, | |
// Device resolution | |
TEMP_9_BIT =0x1F, // 9 bit | |
TEMP_10_BIT =0x3F, // 10 bit | |
TEMP_11_BIT =0x5F, // 11 bit | |
TEMP_12_BIT =0x7F, // 12 bit | |
}; | |
int16_t rawTemperature = (((int16_t)scratchPad[TEMP_MSB]) << 8) | scratchPad[TEMP_LSB]; | |
switch (deviceAddress[0]) | |
{ | |
case DS18B20MODEL: | |
case DS1822MODEL: | |
switch (scratchPad[CONFIGURATION]) | |
{ | |
case TEMP_12_BIT: | |
return (float)rawTemperature * 0.0625; | |
case TEMP_11_BIT: | |
return (float)(rawTemperature >> 1) * 0.125; | |
case TEMP_10_BIT: | |
return (float)(rawTemperature >> 2) * 0.25; | |
case TEMP_9_BIT: | |
return (float)(rawTemperature >> 3) * 0.5; | |
} | |
break; | |
case DS18S20MODEL: | |
// Good spot. Thanks Nic Johns for your contribution | |
return (float)(rawTemperature >> 1) - 0.25 +((float)(scratchPad[COUNT_PER_C] - scratchPad[COUNT_REMAIN]) / (float)scratchPad[COUNT_PER_C] ); | |
} | |
return -999.0f; | |
} | |
void UpdateTemperatures(struct Status& status) | |
{ | |
float fTempHlavnyTermostat = -999.0f; | |
float fTempVodaVstup = -999.0f; | |
float fTempVodaVystup = -999.0f; | |
float fTempNasavanie = -999.0f; | |
typedef uint8_t DeviceAddress[8]; | |
typedef enum { | |
STARTCONVO = 0x44, // Tells device to take a temperature reading and put it on the scratchpad | |
READSCRATCH = 0xBE, // Read EEPROM | |
}; | |
typedef enum { | |
HlavnyTermostat = 0x96, | |
VodaVstup = 0x16, | |
Vyfuk = 0xa5, | |
Vzduch = 0x50, | |
VodaVystup = 0xc5 | |
}; | |
// scan bus | |
DeviceAddress deviceAddress; | |
byte scratchpad[10]; | |
oneWire.reset_search(); | |
while (oneWire.search(deviceAddress)) | |
{ | |
if (oneWire.crc8(deviceAddress, 7) == deviceAddress[7]) // valid address | |
{ | |
oneWire.reset(); | |
oneWire.select(deviceAddress); | |
oneWire.write(READSCRATCH); | |
for (byte i=0; i<9; i++) | |
scratchpad[i] = oneWire.read(); | |
float fTemperature = dallasTemperature(deviceAddress, scratchpad); | |
oneWire.reset(); | |
// TODO: unsafe | |
//if (fTemperature != 85.0f) | |
//{ | |
switch (deviceAddress[7]) | |
{ | |
case HlavnyTermostat: fTempHlavnyTermostat = fTemperature; break; | |
case VodaVstup: fTempVodaVstup = fTemperature; break; | |
case VodaVystup: fTempVodaVystup = fTemperature; break; | |
case Vzduch: fTempNasavanie = fTemperature; break; | |
} | |
//} | |
} | |
} | |
// request conversion | |
oneWire.skip(); | |
oneWire.write(STARTCONVO); | |
static CMedian<float, 5> medTermostat; | |
static CMedian<float, 5> medVodaVstup; | |
static CMedian<float, 5> medVodaVystup; | |
static CMedian<float, 5> medNasavanie; | |
status.termostat = medTermostat.PushGet(fTempHlavnyTermostat); | |
status.vodaVstup = medVodaVstup.PushGet(fTempVodaVstup); | |
status.vodaVystup = medVodaVystup.PushGet(fTempVodaVystup); | |
status.nasavanie = medNasavanie.PushGet(fTempNasavanie); | |
} | |
} hardware; | |
void ProcessInput(char ch) | |
{ | |
static char strBuf[16]; | |
static int nBufIndex = 0; | |
if (logic.m_pState->Name() == "manual") | |
{ | |
EvalManual(ch); | |
return; | |
} | |
if (ch==0x0d || ch==0x0a) | |
{ | |
if (nBufIndex > 0 && nBufIndex != 16) | |
{ | |
strBuf[nBufIndex] = 0; | |
Eval(strBuf); | |
} | |
nBufIndex = 0; | |
} else | |
{ | |
if (nBufIndex < 16) | |
{ | |
strBuf[nBufIndex++] = ch; | |
if (nBufIndex == 16) | |
logic.Info(F("Read buffer overflow")); | |
} | |
} | |
} | |
void EvalManual(char ch) | |
{ | |
switch (ch) | |
{ | |
case 'C': machine.sensorsOut.cerpadlo = 1; return; | |
case 'c': machine.sensorsOut.cerpadlo = 0; break; | |
case 'V': machine.sensorsOut.ventilator = 1; break; | |
case 'v': machine.sensorsOut.ventilator = 0; break; | |
case 'P': machine.sensorsOut.plyn = 1; break; | |
case 'p': machine.sensorsOut.plyn = 0; break; | |
case 'Z': machine.sensorsOut.zapalovanie = 1; break; | |
case 'z': machine.sensorsOut.zapalovanie = 0; break; | |
case 'K': machine.sensorsOut.klapka = 1; break; | |
case 'k': machine.sensorsOut.klapka = 0; break; | |
case 's': logic.Goto("safe"); break; | |
case 'l': logic.Goto("locked"); break; | |
case 0x0d: | |
case 0x0a: | |
break; | |
default: | |
logic.Error(F("Invalid character in manual mode")); | |
} | |
} | |
void Eval(char* cmd) | |
{ | |
if (strcmp(cmd, "stop") == 0) | |
{ | |
if (logic.m_pState->Name() == "safe") | |
{ | |
logic.Info(F("Uz je zastaveny")); | |
} else | |
logic.Goto("safe"); | |
return; | |
} | |
if (strcmp(cmd, "start") == 0) | |
{ | |
if (logic.m_pState->Name() == "safe" || logic.m_pState->Name() == "automatic-standby") | |
{ | |
machine.sensorsIn.startStop = true; | |
} else | |
{ | |
logic.Info(F("V tomto stave nie je mozne spustit")); | |
} | |
return; | |
} | |
if (strcmp(cmd, "press") == 0) | |
{ | |
machine.sensorsIn.startStop = true; | |
return; | |
} | |
if (strcmp(cmd, "safe") == 0) | |
{ | |
logic.Goto("safe"); | |
return; | |
} | |
if (strcmp(cmd, "manual") == 0) | |
{ | |
logic.Goto("manual"); | |
return; | |
} | |
if (strcmp(cmd, "locked") == 0) | |
{ | |
logic.Goto("locked"); | |
return; | |
} | |
if (config.set(cmd)) | |
return; | |
if (strcmp(cmd, "save") == 0) | |
{ | |
if (config.isValid()) | |
{ | |
config.save(); | |
logic.Info(F("Config saved")); | |
} | |
else | |
logic.Info(F("Incorrect config values")); | |
return; | |
} | |
if (strcmp(cmd, "test") == 0) | |
{ | |
config.test(); | |
return; | |
} | |
if (strcmp(cmd, "reset") == 0) | |
{ | |
config.defaults(); | |
config.save(); | |
return; | |
} | |
if (strcmp(cmd, "info") == 0) | |
{ | |
Serial.print(appVersion); | |
return; | |
} | |
if (strcmp(cmd, "help") == 0) | |
{ | |
logic.Info(F("https://gist.github.com/gabonator/f7cbc1fd3ac79a7fbc09c8d8922bb457")); | |
logic.Info(F("commands: help, info, start, stop, safe, manual, help, min=, max=, save, reset, test, locked, press")); | |
return; | |
} | |
logic.Info(F("Unknown command")); | |
} | |
void setup() | |
{ | |
hardware.Init(); | |
config.load(); | |
} | |
void loop() | |
{ | |
static unsigned long lLastMillis1s = 0; | |
static unsigned long lLastMillis10s = 0; | |
unsigned long lMillis = millis(); | |
hardware.Update(); | |
if (lMillis - lLastMillis1s >= 1000) | |
{ | |
lLastMillis1s += 1000; | |
hardware.Update1s(); | |
machine.Update1s(); | |
} | |
if (lMillis - lLastMillis10s >= 10000) | |
{ | |
lLastMillis10s += 10000; | |
machine.Update10s(); | |
} | |
while (Serial.available()) | |
{ | |
ProcessInput(Serial.read()); | |
} | |
if (machine.sensorsIn.startStop) | |
{ | |
logic.Info(F("Start-stop signal received")); | |
} | |
logic.Do(); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var fs = require('fs'); | |
var BTSP = require('bluetooth-serial-port'); | |
var serial = new BTSP.BluetoothSerialPort(); | |
var buf = ""; | |
// websocket | |
var wsconns = []; | |
var ws = require("nodejs-websocket") | |
var server = ws.createServer(function (conn) { | |
wsconns.push(conn); | |
console.log("ws: New connection, clients=" + wsconns.length); | |
conn.on("text", function (str) { | |
serial.write(Buffer.from(str), function() {}); | |
}); | |
conn.on("close", function (code, reason) { | |
wsconns.splice(wsconns.indexOf(conn), 1); | |
console.log("Connection closed, clients=" + wsconns.length); | |
}); | |
conn.on("error", function() | |
{ | |
console.log("ws error"); | |
}); | |
}).listen(2222); | |
// web | |
var express = require("express"); | |
var app = express(); | |
app.get("/", function(req, res) { | |
res.sendFile(__dirname + "/index.html") | |
}); | |
app.get(/^(.+)$/, function(req, res){ | |
res.sendFile(__dirname + req.params[0]); | |
}); | |
app.listen(8888, function() { | |
console.log("web: Server started at localhost:8888"); | |
}); | |
// bluetooth | |
var buffer = ""; | |
address = "00:15:FF:F3:56:4C"; | |
channel = 1; | |
var reconnect = false; | |
serial.on('data', function(data) { | |
logData(data); | |
for (wsc in wsconns) | |
wsconns[wsc].sendText(data); | |
}); | |
serial.on('closed', function() { | |
if (reconnect) | |
clearTimeout(reconnect); | |
console.log("bt: connection closed, reconnecting..."); | |
reconnect = setTimeout(btReconnect, 1000); | |
}); | |
function btReconnect() | |
{ | |
if (reconnect) | |
clearInterval(reconnect); | |
console.log("bt: connecting..."); | |
serial.connect(address, channel, function() { | |
console.log('bt: connected'); | |
}, function () { | |
if (reconnect) | |
clearTimeout(reconnect); | |
console.log("bt: cannot connect, reconnecting..."); | |
reconnect = setTimeout(btReconnect, 1000); | |
}); | |
} | |
btReconnect(); | |
// data logger | |
var logBuffer = ""; | |
var logBufferCollect = []; | |
var logLastData = null; | |
function logData(msg) | |
{ | |
logBuffer += msg; | |
var logLines = logBuffer.split("\n"); | |
while (logLines.length > 1) | |
{ | |
var logLine = logLines.shift(); | |
logLine = logLine.replace("},devs", "],devs"); | |
logLine = logLine.replace("ion=", "ion:"); | |
logLine = logLine.replace("median=", "median:"); | |
logBufferCollect.push(timestamp() + " " + logLine); | |
if (logLine.substr(0, 1) == "{" && logLine.substr(-1, 1) == "}" && logLine.indexOf("devs:") != -1) | |
{ | |
logLastData = "_({timestamp:'"+timestamp()+"', ts:"+(new Date().getTime())+", data:"+logLine+"});"; | |
} | |
} | |
logBuffer = logLines[0]; | |
if (logBuffer.length > 512) | |
{ | |
var logLine = logBuffer; | |
logBufferCollect.push(timestamp() + " " + logLine); | |
logBuffer = ""; | |
} | |
} | |
function datestamp() | |
{ | |
var d = new Date(); | |
return d.getFullYear() + "-" + | |
("0" + (1+d.getMonth())).substr(-2) + "-" + | |
("0" + d.getDate()).substr(-2); | |
} | |
function timestamp() | |
{ | |
var d = new Date(); | |
return datestamp() + " " + | |
("0" + d.getHours()).substr(-2) + ":" + | |
("0" + d.getMinutes()).substr(-2) + ":" + | |
("0" + d.getSeconds()).substr(-2); | |
} | |
setInterval(function() | |
{ | |
if (logBufferCollect.length == 0) | |
return; | |
fs.appendFile("logs/"+datestamp()+".txt", logBufferCollect.join("\r\n"), function (e) {if (e) console.log(e)}); | |
logBufferCollect = []; | |
if (logLastData) | |
{ | |
fs.appendFile("logs/"+datestamp()+".js", logLastData + "\r\n", function (e) {if (e) console.log(e)}); | |
logLastData = null; | |
} | |
}, 30000); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment