POCloud templates

This commit is contained in:
Patrick McDonagh
2018-06-19 14:08:49 -05:00
parent 5848e2fd4e
commit 9dbb825103
163 changed files with 1709 additions and 71 deletions

View File

@@ -0,0 +1,240 @@
<div class="row">
<div class="col-xs-3">
<h2>Pond 1</h2>
<form class="form-inline">
<div class="form-group">
<label for="high-setpoint-1">High Setpoint</label>
<input class="form-control setpoint"
id="high-setpoint-1"
type="number"
step="0.1"
value="<%= channels["plcpond.pond1highalarmsetpoint"].value %>" />
<a href="#"
data-confirm-message="Are you sure you want to do this?"
data-refreshpause="1"
data-command=""
data-staticsend="1"
data-channelId="<%= channels["plcpond.pond1highalarmsetpoint"].channelId %>"
data-techname="<%=channels["plcpond.pond1highalarmsetpoint"].techName %>"
data-name="<%= channels["plcpond.pond1highalarmsetpoint"].name%>"
data-nodechannelcurrentId="<%= channels["plcpond.pond1highalarmsetpoint"].nodechannelcurrentId %>"
id="<%= channels["plcpond.pond1highalarmsetpoint"].channelId %>"
class="btn btn-large btn-theme animated setstatic material-icons">send</a>
</div>
</form>
<br />
<hr />
<br />
<form class="form-inline">
<div class="form-group">
<label for="low-setpoint-1">Low Setpoint</label>
<input class="form-control setpoint"
id="low-setpoint-1"
type="number"
step="0.1"
value="<%= channels["plcpond.pond1lowalarmsetpoint"].value %>" />
<a href="#"
data-confirm-message="Are you sure you want to do this?"
data-refreshpause="1"
data-command=""
data-staticsend="1"
data-channelId="<%= channels["plcpond.pond1lowalarmsetpoint"].channelId %>"
data-techname="<%=channels["plcpond.pond1lowalarmsetpoint"].techName %>"
data-name="<%= channels["plcpond.pond1lowalarmsetpoint"].name%>"
data-nodechannelcurrentId="<%= channels["plcpond.pond1lowalarmsetpoint"].nodechannelcurrentId %>"
id="<%= channels["plcpond.pond1lowalarmsetpoint"].channelId %>"
class="btn btn-large btn-theme animated setstatic material-icons">send</a>
</div>
</form>
</div>
<div class="col-xs-3">
<h2>Pond 2</h2>
<form class="form-inline">
<div class="form-group">
<label for="high-setpoint-2">High Setpoint</label>
<input class="form-control setpoint"
id="high-setpoint-2"
type="number"
step="0.1"
value="<%= channels["plcpond.pond2highalarmsetpoint"].value %>" />
<a href="#"
data-confirm-message="Are you sure you want to do this?"
data-refreshpause="1"
data-command=""
data-staticsend="1"
data-channelId="<%= channels["plcpond.pond2highalarmsetpoint"].channelId %>"
data-techname="<%=channels["plcpond.pond2highalarmsetpoint"].techName %>"
data-name="<%= channels["plcpond.pond2highalarmsetpoint"].name%>"
data-nodechannelcurrentId="<%= channels["plcpond.pond2highalarmsetpoint"].nodechannelcurrentId %>"
id="<%= channels["plcpond.pond2highalarmsetpoint"].channelId %>"
class="btn btn-large btn-theme animated setstatic material-icons">send</a>
</div>
</form>
<br />
<hr />
<br />
<form class="form-inline">
<div class="form-group">
<label for="low-setpoint-2">Low Setpoint</label>
<input class="form-control setpoint"
id="low-setpoint-2"
type="number"
step="0.1"
value="<%= channels["plcpond.pond2lowalarmsetpoint"].value %>" />
<a href="#"
data-confirm-message="Are you sure you want to do this?"
data-refreshpause="1"
data-command=""
data-staticsend="1"
data-channelId="<%= channels["plcpond.pond2lowalarmsetpoint"].channelId %>"
data-techname="<%=channels["plcpond.pond2lowalarmsetpoint"].techName %>"
data-name="<%= channels["plcpond.pond2lowalarmsetpoint"].name%>"
data-nodechannelcurrentId="<%= channels["plcpond.pond2lowalarmsetpoint"].nodechannelcurrentId %>"
id="<%= channels["plcpond.pond2lowalarmsetpoint"].channelId %>"
class="btn btn-large btn-theme animated setstatic material-icons">send</a>
</div>
</form>
</div>
<div class="col-xs-3">
<h2>Pond 3</h2>
<form class="form-inline">
<div class="form-group">
<label for="high-setpoint-3">High Setpoint</label>
<input class="form-control setpoint"
id="high-setpoint-3"
type="number"
step="0.1"
value="<%= channels["plcpond.pond3highalarmsetpoint"].value %>" />
<a href="#"
data-confirm-message="Are you sure you want to do this?"
data-refreshpause="1"
data-command=""
data-staticsend="1"
data-channelId="<%= channels["plcpond.pond3highalarmsetpoint"].channelId %>"
data-techname="<%=channels["plcpond.pond3highalarmsetpoint"].techName %>"
data-name="<%= channels["plcpond.pond3highalarmsetpoint"].name%>"
data-nodechannelcurrentId="<%= channels["plcpond.pond3highalarmsetpoint"].nodechannelcurrentId %>"
id="<%= channels["plcpond.pond3highalarmsetpoint"].channelId %>"
class="btn btn-large btn-theme animated setstatic material-icons">send</a>
</div>
</form>
<br />
<hr />
<br />
<form class="form-inline">
<div class="form-group">
<label for="low-setpoint-3">Low Setpoint</label>
<input class="form-control setpoint"
id="low-setpoint-3"
type="number"
step="0.1"
value="<%= channels["plcpond.pond3lowalarmsetpoint"].value %>" />
<a href="#"
data-confirm-message="Are you sure you want to do this?"
data-refreshpause="1"
data-command=""
data-staticsend="1"
data-channelId="<%= channels["plcpond.pond3lowalarmsetpoint"].channelId %>"
data-techname="<%=channels["plcpond.pond3lowalarmsetpoint"].techName %>"
data-name="<%= channels["plcpond.pond3lowalarmsetpoint"].name%>"
data-nodechannelcurrentId="<%= channels["plcpond.pond3lowalarmsetpoint"].nodechannelcurrentId %>"
id="<%= channels["plcpond.pond3lowalarmsetpoint"].channelId %>"
class="btn btn-large btn-theme animated setstatic material-icons">send</a>
</div>
</form>
</div>
<div class="col-xs-3">
<h2>Pond 4</h2>
<form class="form-inline">
<div class="form-group">
<label for="high-setpoint-4">High Setpoint</label>
<input class="form-control setpoint"
id="high-setpoint-4"
type="number"
step="0.1"
value="<%= channels["plcpond.pond4highalarmsetpoint"].value %>" />
<a href="#"
data-confirm-message="Are you sure you want to do this?"
data-refreshpause="1"
data-command=""
data-staticsend="1"
data-channelId="<%= channels["plcpond.pond4highalarmsetpoint"].channelId %>"
data-techname="<%=channels["plcpond.pond4highalarmsetpoint"].techName %>"
data-name="<%= channels["plcpond.pond4highalarmsetpoint"].name%>"
data-nodechannelcurrentId="<%= channels["plcpond.pond4highalarmsetpoint"].nodechannelcurrentId %>"
id="<%= channels["plcpond.pond4highalarmsetpoint"].channelId %>"
class="btn btn-large btn-theme animated setstatic material-icons">send</a>
</div>
</form>
<br />
<hr />
<br />
<form class="form-inline">
<div class="form-group">
<label for="low-setpoint-4">Low Setpoint</label>
<input class="form-control setpoint"
id="low-setpoint-4"
type="number"
step="0.1"
value="<%= channels["plcpond.pond4lowalarmsetpoint"].value %>" />
<a href="#"
data-confirm-message="Are you sure you want to do this?"
data-refreshpause="1"
data-command=""
data-staticsend="1"
data-channelId="<%= channels["plcpond.pond4lowalarmsetpoint"].channelId %>"
data-techname="<%=channels["plcpond.pond4lowalarmsetpoint"].techName %>"
data-name="<%= channels["plcpond.pond4lowalarmsetpoint"].name%>"
data-nodechannelcurrentId="<%= channels["plcpond.pond4lowalarmsetpoint"].nodechannelcurrentId %>"
id="<%= channels["plcpond.pond4lowalarmsetpoint"].channelId %>"
class="btn btn-large btn-theme animated setstatic material-icons">send</a>
</div>
</form>
</div>
</div>
<script type="text/javascript">
$("#high-setpoint-1").change(function(){
$("#<%= channels["plcpond.pond1highalarmsetpoint"].channelId %>").attr('data-staticsend', $("#high-setpoint-1").val());
})
$("#high-setpoint-2").change(function(){
$("#<%= channels["plcpond.pond2highalarmsetpoint"].channelId %>").attr('data-staticsend', $("#high-setpoint-2").val());
})
$("#high-setpoint-3").change(function(){
$("#<%= channels["plcpond.pond3highalarmsetpoint"].channelId %>").attr('data-staticsend', $("#high-setpoint-3").val());
})
$("#high-setpoint-4").change(function(){
$("#<%= channels["plcpond.pond4highalarmsetpoint"].channelId %>").attr('data-staticsend', $("#high-setpoint-4").val());
})
$("#low-setpoint-1").change(function(){
$("#<%= channels["plcpond.pond1lowalarmsetpoint"].channelId %>").attr('data-staticsend', $("#low-setpoint-1").val());
})
$("#low-setpoint-2").change(function(){
$("#<%= channels["plcpond.pond2lowalarmsetpoint"].channelId %>").attr('data-staticsend', $("#low-setpoint-2").val());
})
$("#low-setpoint-3").change(function(){
$("#<%= channels["plcpond.pond3lowalarmsetpoint"].channelId %>").attr('data-staticsend', $("#low-setpoint-3").val());
})
$("#low-setpoint-4").change(function(){
$("#<%= channels["plcpond.pond4lowalarmsetpoint"].channelId %>").attr('data-staticsend', $("#low-setpoint-4").val());
})
</script>

View File

@@ -0,0 +1 @@
<module>Alerts</module>

View File

@@ -0,0 +1,180 @@
<div class="row">
<div class="col-xs-10 col-xs-offset-1">
<form>
<div class="form-group">
<label for="number-of-ponds">Number of Ponds</label>
<input class="form-control"
id="number-of-ponds"
type="number"
step="1"
value="<%= channels["plcpond.cfgnumberofponds"].value %>" />
<a href="#"
data-confirm-message="Are you sure you want to do this?"
data-refreshpause="1"
data-command=""
data-staticsend="1"
data-channelId="<%= channels["plcpond.cfgnumberofponds"].channelId %>"
data-techname="<%=channels["plcpond.cfgnumberofponds"].techName %>"
data-name="<%= channels["plcpond.cfgnumberofponds"].name%>"
data-nodechannelcurrentId="<%= channels["plcpond.cfgnumberofponds"].nodechannelcurrentId %>"
id="<%= channels["plcpond.cfgnumberofponds"].channelId %>"
class="btn btn-large btn-theme animated setstatic material-icons">send</a>
</div>
</form>
</div>
</div>
<div class="row">
<div class="col-xs-6">
<h2>Pond 1 Calibration</h2>
<table id="calibration-data-table-1" class="table">
<thead>
<tr>
<th>Level (Ft.)</th>
<th>Volume</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
</div>
<div class="col-xs-6">
<h2>Pond 2 Calibration</h2>
<table id="calibration-data-table-2" class="table">
<thead>
<tr>
<th>Level (Ft.)</th>
<th>Volume</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
</div>
</div>
<div class="row">
<div class="col-xs-6">
<h2>Pond 3 Calibration</h2>
<table id="calibration-data-table-3" class="table">
<thead>
<tr>
<th>Level (Ft.)</th>
<th>Volume</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
</div>
<div class="col-xs-6">
<h2>Pond 4 Calibration</h2>
<table id="calibration-data-table-4" class="table">
<thead>
<tr>
<th>Level (Ft.)</th>
<th>Volume</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
</div>
</div>
<div class="row pad15">
<div class="col-md-12 entry-top-level" id="new-entry">
<h2>Add a new Calibration Point</h2>
<form>
<div class="form-group">
<label for="pond-select">Pond Number</label>
<select class="form-control" id="pond-select">
<option value=1 >1</option>
<option value=2 >2</option>
<option value=3 >3</option>
<option value=4 >4</option>
</select>
</div>
<div class="form-group">
<label for="height-box">Height</label>
<input class="form-control height_box"
id="height-box"
type="number"
step="any"
value="0">
</div>
<div class="form-group">
<label for="volume-box">Volume</label>
<input class="form-control volume_box"
id="volume-box"
type="number"
step="any"
value="0">
</div>
<a href="#"
data-confirm-message="Are you sure you want to do this?"
data-refreshpause="1"
data-command=""
data-staticsend="{'pond':0, 'height':0.0, 'volume':0.0}"
data-channelId="<%= channels["plcpond.addcalibrationpoint"].channelId %>"
data-techname="<%=channels["plcpond.addcalibrationpoint"].techName %>"
data-name="<%= channels["plcpond.addcalibrationpoint"].name%>"
data-nodechannelcurrentId="<%= channels["plcpond.addcalibrationpoint"].nodechannelcurrentId %>"
id="<%= channels["plcpond.addcalibrationpoint"].channelId %>"
class="btn btn-large btn-theme animated setstatic material-icons">send</a>
</form>
</div>
</div>
<script type="text/javascript">
String.prototype.replaceAll = function(search, replacement) {
var target = this;
return target.replace(new RegExp(search, 'g'), replacement);
};
var configRaw = [""];
configRaw.push("<%= channels["plcpond.pond1calibration"].value %>");
configRaw.push("<%= channels["plcpond.pond2calibration"].value %>");
configRaw.push("<%= channels["plcpond.pond3calibration"].value %>");
configRaw.push("<%= channels["plcpond.pond4calibration"].value %>");
for (var j = 1; j<=4; j++){
var configData = JSON.parse(configRaw[j].replaceAll("'", '"'));
console.log(configData);
var tableRows = [];
for (var i = 0; i < configData.length; i++){
var thisHtml = "<tr><td>" + configData[i].height + "</td><td>" + configData[i].volume + '</td><td><a href="#" data-confirm-message="Are you sure you want to do this?" data-refreshpause="1" data-command="" data-staticsend="{\'pond\': '+ j +', \'point\': '+ i +'}" data-channelId="<%= channels["plcpond.deletecalibrationpoint"].channelId %>" data-techname="<%=channels["plcpond.deletecalibrationpoint"].techName %>" data-name="<%= channels["plcpond.deletecalibrationpoint"].name%>" data-nodechannelcurrentId="<%= channels["plcpond.deletecalibrationpoint"].nodechannelcurrentId %>" id="<%= channels["plcpond.deletecalibrationpoint"].channelId %>" class="btn btn-large btn-theme animated setstatic">Delete</a></tr>';
tableRows.push(thisHtml);
}
$("#calibration-data-table-"+ j +" > tbody").html(tableRows);
}
$("#height-box").change(function(){
var currentNewPoint = JSON.parse($("#<%= channels["plcpond.addcalibrationpoint"].channelId %>").attr('data-staticsend').replaceAll("'", '"'));
currentNewPoint['height'] = $("#height-box").val();
$("#<%= channels["plcpond.addcalibrationpoint"].channelId %>").attr('data-staticsend', JSON.stringify(currentNewPoint).replaceAll('"', "'"));
})
$("#volume-box").change(function(){
var currentNewPoint = JSON.parse($("#<%= channels["plcpond.addcalibrationpoint"].channelId %>").attr('data-staticsend').replaceAll("'", '"'));
currentNewPoint['volume'] = $("#volume-box").val();
$("#<%= channels["plcpond.addcalibrationpoint"].channelId %>").attr('data-staticsend', JSON.stringify(currentNewPoint).replaceAll('"', "'"));
})
$("#pond-select").change(function(){
var currentNewPoint = JSON.parse($("#<%= channels["plcpond.addcalibrationpoint"].channelId %>").attr('data-staticsend').replaceAll("'", '"'));
currentNewPoint['pond'] = $("#pond-select").val();
$("#<%= channels["plcpond.addcalibrationpoint"].channelId %>").attr('data-staticsend', JSON.stringify(currentNewPoint).replaceAll('"', "'"));
})
$("#number-of-ponds").change(function(){
$("#<%= channels["plcpond.cfgnumberofponds"].channelId %>").attr('data-staticsend', $("#number-of-ponds").val());
})
</script>

View File

@@ -0,0 +1,42 @@
<div class="row row-flex box-me">
<div class="col-md-6 text-center">
<h2 class="uppercase">Public IP Address</h2>
<p><%= channels["plcpond.public_ip_address"].value %><p>
</div>
</div>
<style>
.uppercase {
text-transform: uppercase;
font-size: 14px;
color: #666;
font-weight: 400;
letter-spacing: 1px;
z-index: 100;
}
.box-me {
position: relative;
padding: 0.5em;
padding-bottom: 1.5em;
border: 1px solid #eee;
/*margin: 1em 0;*/
}
.row-flex {
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
flex-wrap: wrap;
}
.row-flex > [class*='col-'] {
display: flex;
flex-direction: column;
}
p {
font-size: 1.25em;
}
</style>

View File

@@ -0,0 +1,6 @@
<div class='col-xs-1'>
<div class="<%= nodecolors.statuscolor %> nodecolor"></div>
</div>
<div class='col-xs-6'>
<h3><%= node.vanityname %></h3>
</div>

View File

@@ -0,0 +1,31 @@
<style>
.header h4 {
position: relative;
top: 0.9em;
}
.header h2 {
text-transform: uppercase;
font-size: 14px;
color: #aaa;
margin: 0.75em 0;
}
.header p {
font-size: 24px;
color: black;
font-weight: 600;
}
</style>
<div class="row header">
<div class="col-xs-1">
<div class="<%= nodecolors.statuscolor %> nodecolor"></div>
</div>
<div class="col-xs-2">
<img src="<%= nodeimgurl %>" />
</div>
<div class="col-xs-4">
<h4><%= node.vanityname %></h4>
</div>
</div>

View File

@@ -0,0 +1,265 @@
<div class="box-me">
<div class="row">
<div class="col-xs-12 text-center">
<h1>Level</h1>
</div>
</div>
<div class="row">
<div class="make-gauge col-xs-4"
data-deviceName="plcpond"
data-channelName="pond1height"
data-displayName="Pond 1 Height"
data-units="Ft"
data-min="0"
data-max="50"
data-decimalPlaces="2"
data-channelId="<%= channels['plcpond.pond1height'].channelId %>"
data-timestamp="<%= channels['plcpond.pond1height'].timestamp %>"
></div>
<% if (parseInt(channels['plcpond.cfgnumberofponds'].value) >= 2) { %>
<div class="make-gauge col-xs-4"
data-deviceName="plcpond"
data-channelName="pond2height"
data-displayName="Pond 2 Height"
data-units="Ft"
data-min="0"
data-max="50"
data-decimalPlaces="2"
data-channelId="<%= channels['plcpond.pond2height'].channelId %>"
data-timestamp="<%= channels['plcpond.pond2height'].timestamp %>"
></div>
<% }%>
<% if (parseInt(channels['plcpond.cfgnumberofponds'].value) >= 3) { %>
<div class="make-gauge col-xs-4"
data-deviceName="plcpond"
data-channelName="pond3height"
data-displayName="Pond 3 Height"
data-units="Ft"
data-min="0"
data-max="50"
data-decimalPlaces="2"
data-channelId="<%= channels['plcpond.pond3height'].channelId %>"
data-timestamp="<%= channels['plcpond.pond3height'].timestamp %>"
></div>
<% }%>
<% if (parseInt(channels['plcpond.cfgnumberofponds'].value) >= 4) { %>
<div class="make-gauge col-xs-4"
data-deviceName="plcpond"
data-channelName="pond4height"
data-displayName="Pond 4 Height"
data-units="Ft"
data-min="0"
data-max="50"
data-decimalPlaces="2"
data-channelId="<%= channels['plcpond.pond4height'].channelId %>"
data-timestamp="<%= channels['plcpond.pond4height'].timestamp %>"
></div>
<% }%>
</div>
</div>
<div class="box-me">
<div class="row">
<div class="col-xs-12 text-center">
<h1>Volume</h1>
</div>
</div>
<div class="row">
<div class="make-gauge col-xs-4"
data-deviceName="plcpond"
data-channelName="pond1volume"
data-displayName="Pond 1 Volume"
data-units="BBL"
data-min="0"
data-max="50000"
data-decimalPlaces="0"
data-channelId="<%= channels['plcpond.pond1volume'].channelId %>"
data-timestamp="<%= channels['plcpond.pond1volume'].timestamp %>"
></div>
<% if (parseInt(channels['plcpond.cfgnumberofponds'].value) >= 2) { %>
<div class="make-gauge col-xs-4"
data-deviceName="plcpond"
data-channelName="pond2volume"
data-displayName="Pond 2 Volume"
data-units="BBL"
data-min="0"
data-max="50000"
data-decimalPlaces="0"
data-channelId="<%= channels['plcpond.pond2volume'].channelId %>"
data-timestamp="<%= channels['plcpond.pond2volume'].timestamp %>"
></div>
<% }%>
<% if (parseInt(channels['plcpond.cfgnumberofponds'].value) >= 3) { %>
<div class="make-gauge col-xs-4"
data-deviceName="plcpond"
data-channelName="pond3volume"
data-displayName="Pond 3 Volume"
data-units="BBL"
data-min="0"
data-max="50000"
data-decimalPlaces="0"
data-channelId="<%= channels['plcpond.pond3volume'].channelId %>"
data-timestamp="<%= channels['plcpond.pond3volume'].timestamp %>"
></div>
<% }%>
<% if (parseInt(channels['plcpond.cfgnumberofponds'].value) >= 4) { %>
<div class="make-gauge col-xs-4"
data-deviceName="plcpond"
data-channelName="pond4volume"
data-displayName="Pond 4 Volume"
data-units="BBL"
data-min="0"
data-max="50000"
data-decimalPlaces="0"
data-channelId="<%= channels['plcpond.pond4volume'].channelId %>"
data-timestamp="<%= channels['plcpond.pond4volume'].timestamp %>"
></div>
<% }%>
<% if (parseInt(channels['plcpond.cfgnumberofponds'].value) >= 2) { %>
<div class="make-gauge col-xs-4"
data-deviceName="plcpond"
data-channelName="pondvolumetotal"
data-displayName="Pond Volume Total"
data-units="BBL"
data-min="0"
data-max="200000"
data-decimalPlaces="0"
data-channelId="<%= channels['plcpond.pondvolumetotal'].channelId %>"
data-timestamp="<%= channels['plcpond.pondvolumetotal'].timestamp %>"
></div>
<% }%>
</div>
</div>
<style>
.box-me {
position: relative;
padding: 0.5em;
padding-bottom: 1.5em;
border: 1px solid #eee;
/*margin: 1em 0;*/
}
.box-me .gauge-box {
margin-top: -0.25em;
}
h2 {
text-transform: uppercase;
font-size: 14px;
color: #666;
font-weight: 400;
letter-spacing: 1px;
z-index: 100;
}
.dynamic-chart-form {
background-color: whiteSmoke;
padding: 1em 0.5em;
margin-top: 1em;
}
.row-flex {
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
flex-wrap: wrap;
}
.row-flex > [class*='col-'] {
display: flex;
flex-direction: column;
}
#systemStatusTimelineContainer h2 {
text-transform: uppercase;
font-size: 14px;
color: #666;
font-weight: 400;
letter-spacing: 1px;
z-index: 100;
}
.slice.node-detail hr {
border-color: #ccc;
}
.slice.node-detail #alarms li {
margin-bottom: 1em;
padding: 0.5em;
}
.slice.node-detail #alarms li:nth-child(even){
background-color: whiteSmoke;
}
.slice.node-detail #alarms li span {
margin-left: 1em;
color: #aaa;
}
</style>
<script>
$('.val_box').each(function(topLevel){
$(this).change(function(){
var id = "#" + $(this).closest(".entry-top-level").attr('id');
if (id !== "#undefined"){
var val = $(id).find('.val_box').val();
var tag = $(id).find('.setstatic').attr('data-staticsend', val);
console.log($(id).find('.setstatic').attr('data-staticsend'));
}
});
});
$('.make-gauge').each(function(gaugeDiv) {
const deviceName = $(this).attr("data-deviceName");
const channelName = $(this).attr("data-channelName");
const displayName = $(this).attr("data-displayName");
const units = $(this).attr("data-units");
const min = $(this).attr("data-min");
const max = $(this).attr("data-max");
const decimalPlaces = $(this).attr("data-decimalPlaces");
const channelId = $(this).attr("data-channelId");
const timestamp = $(this).attr("data-timestamp");
const gaugeHTML = makeGauge(deviceName, channelName, displayName, units, min, max, decimalPlaces, channelId, timestamp);
$(this).html(gaugeHTML);
});
function makeGauge(deviceName, channelName, displayName, units, min, max, decimalPlaces, channelId, timestamp) {
return (
'<div class="text-center"> \
<h2>' + displayName + '</h2> \
<div class="gauge-box"> \
<div data-labelheight="10" \
style="height: 170px; background: transparent; margin: 0 auto;" \
id="gauge-' + channelName + '" \
data-chart="solidgauge" \
data-nodename="' + deviceName + '.' + channelName + '" \
data-units="'+ units + '" \
data-min="' + min + '" \
data-max="' + max + '" \
data-decimalplaces="' + decimalPlaces + '" \
data-colors="0.1:#DF5353,0.5:#DDDF0D,0.9:#55BF3B" \
data-valuefontsize="18px"> \
</div> \
<div class- "timestamp-box"> \
<a href="#" data-channelId="' + channelId + '" class="data-table" title="Download Channel History"> \
<i class="fa fa-download"></i> \
</a> \
</div> \
<span data-timeupdate="' + channelName + '">'+ timestamp + '</span> \
</div> \
</div>'
);
}
</script>

View File

@@ -0,0 +1,34 @@
$problem.Node.vanityname$ $problem.Problemstatus.message$ Alert
Issue Recorded: $problem.Problemstatus.message$
<br>
System Name: $problem.Node.vanityname$
<br><br>
Pond 1 Level: $plcpond.pond1height$
<br>
Pond 1 Volume: $plcpond.pond1volume$
<br><br>
Pond 2 Level: $plcpond.pond2height$
<br>
Pond 2 Volume: $plcpond.pond2volume$
<br><br>
Pond 3 Level: $plcpond.pond3height$
<br>
Pond 3 Volume: $plcpond.pond3volume$
<br><br>
Pond 4 Level: $plcpond.pond4height$
<br>
Pond 4 Volume: $plcpond.pond4volume$
<br><br>
$problem.Node.vanityname$ $problem.Problemstatus.message$ ($plcpond.pond4height$ @ $plcpond.pond4height.timestamp$)

View File

@@ -0,0 +1,15 @@
<a href="#"
data-channelId="<%= channels["plcpond.log"].channelId %>"
class="data-table btn-block btn btn-theme animated"
title="Device Log"><i style='margin-left: 0.5em; cursor: pointer' class="fa fa-th-list icon-theme"></i> Device Log</a>
<a href="#"
data-refreshpause="1"
data-staticsend="1"
data-channelId="<%= channels["plcpond.sync"].channelId %>"
data-techname="<%=channels["plcpond.sync"].techName %>"
data-name="<%= channels["plcpond.sync"].name%>"
data-nodechannelcurrentId="<%= channels["plcpond.sync"].nodechannelcurrentId %>"
id="<%= channels["plcpond.sync"].channelId %>"
class="btn btn-large btn-block btn-theme animated setstatic mqtt">
<i class="icon-repeat icon-white mqtt" ></i>Sync All Data</a>

View File

@@ -0,0 +1,37 @@
<div class='col-xs-12' style="padding-top: 1em; margin-bottom: 1em;">
<div class="input-daterange input-group" id="datepicker">
<input data-chartid="dynamicChart" id="fromDate" data-daysofhistory="7" type="text" class="form-control" name="start">
<span class="input-group-addon">to</span>
<input class="form-control" data-chartid="dynamicChart" id="toDate" type="text" name="end">
<span class='input-group-btn'>
<a href="#!" data-chartid="dynamicChart" data-otherchartids="statusTimeline" class="btn chart-update btn-theme">Run</a>
</span>
</div>
</div>
<hr>
<div class='clearfix col-xs-12'
style='height: 450px'
id="dynamicChart"
data-chart="dynamicchart"
data-daysofhistory="7"
data-chartlabel="Data"
data-ylabel=""
data-xlabel="Date"
data-units=""
data-channelnames="plcpond.pond1height,plcpond.pond2height,plcpond.pond3height,plcpond.pond4height,plcpond.pond1volume,plcpond.pond2volume,plcpond.pond3volume,plcpond.pond4volume,plcpond.pondvolumetotal">
</div>
<style>
.dynamic-chart-form {
background-color: whiteSmoke;
padding: 1em 0.5em;
margin-top: 1em;
}
#systemStatusTimelineContainer h2 {
text-transform: uppercase;
font-size: 14px;
color: #666;
font-weight: 400;
letter-spacing: 1px;
z-index: 100;
}
</style>

View File

@@ -0,0 +1,296 @@
"""Define Meshify channel class."""
import time
from pycomm.ab_comm.clx import Driver as ClxDriver
from pycomm.cip.cip_base import CommError, DataError
TAG_DATAERROR_SLEEPTIME = 5
def binarray(intval):
"""Split an integer into its bits."""
bin_string = '{0:08b}'.format(intval)
bin_arr = [i for i in bin_string]
bin_arr.reverse()
return bin_arr
def read_tag(addr, tag, plc_type="CLX"):
"""Read a tag from the PLC."""
direct = plc_type == "Micro800"
addr = str(addr)
tag = str(tag)
c = ClxDriver()
try:
if c.open(addr, direct_connection=direct):
try:
v = c.read_tag(tag)
return v
except DataError as e:
c.close()
time.sleep(TAG_DATAERROR_SLEEPTIME)
print("Data Error during readTag({}, {}, plc_type='{}'): {}".format(addr, tag, plc_type, e))
else:
raise DataError("no data")
except CommError:
# err = c.get_status()
c.close()
print("Could not connect during readTag({}, {})".format(addr, tag))
# print err
except AttributeError as e:
c.close()
print("AttributeError during readTag({}, {}): \n{}".format(addr, tag, e))
c.close()
def read_array(addr, tag, start, end, plc_type="CLX"):
"""Read an array from the PLC."""
direct = plc_type == "Micro800"
c = ClxDriver()
if c.open(addr, direct_connection=direct):
arr_vals = []
try:
for i in range(start, end):
tag_w_index = tag + "[{}]".format(i)
v = c.read_tag(tag_w_index)
# print('{} - {}'.format(tag_w_index, v))
arr_vals.append(round(v[0], 4))
# print(v)
if arr_vals:
return arr_vals
print("No length for {}".format(addr))
return False
except Exception:
print("Error during readArray({}, {}, {}, {})".format(addr, tag, start, end))
err = c.get_status()
c.close()
print(err)
return False
c.close()
return False
def write_tag(addr, tag, val, plc_type="CLX"):
"""Write a tag value to the PLC."""
direct = plc_type == "Micro800"
clx = ClxDriver()
if clx.open(addr, direct_connection=direct):
try:
prevval = clx.read_tag(tag)
if direct:
time.sleep(1)
write_result = clx.write_tag(tag, val, prevval[1])
return write_result
except Exception:
print("Error during writeTag({}, {}, {})".format(addr, tag, val))
err = clx.get_status()
clx.close()
print(err)
return False
clx.close()
return False
class Channel(object):
"""Holds the configuration for a Meshify channel."""
def __init__(self, mesh_name, data_type, chg_threshold, guarantee_sec, map_=False, write_enabled=False):
"""Initialize the channel."""
self.mesh_name = mesh_name
self.data_type = data_type
self.last_value = None
self.value = None
self.last_send_time = 0
self.chg_threshold = chg_threshold
self.guarantee_sec = guarantee_sec
self.map_ = map_
self.write_enabled = write_enabled
def __str__(self):
"""Create a string for the channel."""
return "{}\nvalue: {}, last_send_time: {}".format(self.mesh_name, self.value, self.last_send_time)
def check(self, new_value, force_send=False):
"""Check to see if the new_value needs to be stored."""
send_needed = False
send_reason = ""
if self.data_type == 'BOOL' or self.data_type == 'STRING':
if self.last_send_time == 0:
send_needed = True
send_reason = "no send time"
elif self.value is None:
send_needed = True
send_reason = "no value"
elif not (self.value == new_value):
if self.map_:
if not self.value == self.map_[new_value]:
send_needed = True
send_reason = "value change"
else:
send_needed = True
send_reason = "value change"
elif (time.time() - self.last_send_time) > self.guarantee_sec:
send_needed = True
send_reason = "guarantee sec"
elif force_send:
send_needed = True
send_reason = "forced"
else:
if self.last_send_time == 0:
send_needed = True
send_reason = "no send time"
elif self.value is None:
send_needed = True
send_reason = "no value"
elif abs(self.value - new_value) > self.chg_threshold:
send_needed = True
send_reason = "change threshold"
elif (time.time() - self.last_send_time) > self.guarantee_sec:
send_needed = True
send_reason = "guarantee sec"
elif force_send:
send_needed = True
send_reason = "forced"
if send_needed:
self.last_value = self.value
if self.map_:
try:
self.value = self.map_[new_value]
except KeyError:
print("Cannot find a map value for {} in {} for {}".format(new_value, self.map_, self.mesh_name))
self.value = new_value
else:
self.value = new_value
self.last_send_time = time.time()
print("Sending {} for {} - {}".format(self.value, self.mesh_name, send_reason))
return send_needed
def read(self):
"""Read the value."""
pass
def identity(sent):
"""Return exactly what was sent to it."""
return sent
class ModbusChannel(Channel):
"""Modbus channel object."""
def __init__(self, mesh_name, register_number, data_type, chg_threshold, guarantee_sec, channel_size=1, map_=False, write_enabled=False, transformFn=identity):
"""Initialize the channel."""
super(ModbusChannel, self).__init__(mesh_name, data_type, chg_threshold, guarantee_sec, map_, write_enabled)
self.mesh_name = mesh_name
self.register_number = register_number
self.channel_size = channel_size
self.data_type = data_type
self.last_value = None
self.value = None
self.last_send_time = 0
self.chg_threshold = chg_threshold
self.guarantee_sec = guarantee_sec
self.map_ = map_
self.write_enabled = write_enabled
self.transformFn = transformFn
def read(self, mbsvalue):
"""Return the transformed read value."""
return self.transformFn(mbsvalue)
class PLCChannel(Channel):
"""PLC Channel Object."""
def __init__(self, ip, mesh_name, plc_tag, data_type, chg_threshold, guarantee_sec, map_=False, write_enabled=False, plc_type='CLX'):
"""Initialize the channel."""
super(PLCChannel, self).__init__(mesh_name, data_type, chg_threshold, guarantee_sec, map_, write_enabled)
self.plc_ip = ip
self.mesh_name = mesh_name
self.plc_tag = plc_tag
self.data_type = data_type
self.last_value = None
self.value = None
self.last_send_time = 0
self.chg_threshold = chg_threshold
self.guarantee_sec = guarantee_sec
self.map_ = map_
self.write_enabled = write_enabled
self.plc_type = plc_type
def read(self):
"""Read the value."""
plc_value = None
if self.plc_tag and self.plc_ip:
read_value = read_tag(self.plc_ip, self.plc_tag, plc_type=self.plc_type)
if read_value:
plc_value = read_value[0]
return plc_value
class BoolArrayChannels(Channel):
"""Hold the configuration for a set of boolean array channels."""
def __init__(self, ip, mesh_name, plc_tag, data_type, chg_threshold, guarantee_sec, map_=False, write_enabled=False):
"""Initialize the channel."""
self.plc_ip = ip
self.mesh_name = mesh_name
self.plc_tag = plc_tag
self.data_type = data_type
self.last_value = None
self.value = None
self.last_send_time = 0
self.chg_threshold = chg_threshold
self.guarantee_sec = guarantee_sec
self.map_ = map_
self.write_enabled = write_enabled
def compare_values(self, new_val_dict):
"""Compare new values to old values to see if the values need storing."""
send = False
for idx in new_val_dict:
try:
if new_val_dict[idx] != self.last_value[idx]:
send = True
except KeyError:
print("Key Error in self.compare_values for index {}".format(idx))
send = True
return send
def read(self, force_send=False):
"""Read the value and check to see if needs to be stored."""
send_needed = False
send_reason = ""
if self.plc_tag:
v = read_tag(self.plc_ip, self.plc_tag)
if v:
bool_arr = binarray(v[0])
new_val = {}
for idx in self.map_:
try:
new_val[self.map_[idx]] = bool_arr[idx]
except KeyError:
print("Not able to get value for index {}".format(idx))
if self.last_send_time == 0:
send_needed = True
send_reason = "no send time"
elif self.value is None:
send_needed = True
send_reason = "no value"
elif self.compare_values(new_val):
send_needed = True
send_reason = "value change"
elif (time.time() - self.last_send_time) > self.guarantee_sec:
send_needed = True
send_reason = "guarantee sec"
elif force_send:
send_needed = True
send_reason = "forced"
if send_needed:
self.value = new_val
self.last_value = self.value
self.last_send_time = time.time()
print("Sending {} for {} - {}".format(self.value, self.mesh_name, send_reason))
return send_needed

Binary file not shown.

View File

@@ -0,0 +1,28 @@
id,name,deviceTypeId,fromMe,io,subTitle,helpExplanation,channelType,dataType,defaultValue,regex,regexErrMsg,units,min,max,change,guaranteedReportPeriod,minReportTime
13723,log,463,FALSE,readwrite,Log,Device Log,device,string,Created,,,,,,,,
,pond1height,463,FALSE,readonly,Pond 1 Height,in Ft,device,float,-99,,,,,,,,
,pond1volume,463,FALSE,readonly,Pond 1 Volume,in BBL,device,float,-99,,,,,,,,
,pond2height,463,FALSE,readonly,Pond 2 Height,in Ft,device,float,-99,,,,,,,,
,pond2volume,463,FALSE,readonly,Pond 2 Volume,in BBL,device,float,-99,,,,,,,,
,pond3height,463,FALSE,readonly,Pond 3 Height,in Ft,device,float,-99,,,,,,,,
,pond3volume,463,FALSE,readonly,Pond 3 Volume,in BBL,device,float,-99,,,,,,,,
,pond4height,463,FALSE,readonly,Pond 4 Height,in Ft,device,float,-99,,,,,,,,
,pond4volume,463,FALSE,readonly,Pond 4 Volume,in BBL,device,float,-99,,,,,,,,
,cfgnumberofponds,463,FALSE,readwrite,Number of Ponds Configured,4-Jan,device,integer,0,,,,,,,,
,public_ip_address,463,FALSE,readonly,Public IP Address,network device address,device,string,,,,,,,,,
,pondvolumetotal,463,FALSE,readonly,Total Pond Volume,in BBL,device,float,-99,,,,,,,,
,pond1calibration,463,FALSE,readonly,Pond 1 Calibration Table,JSON list of objects with height and volume props,device,string,[],,,,,,,,
,pond2calibration,463,FALSE,readonly,Pond 2 Calibration Table,JSON list of objects with height and volume props,device,string,[],,,,,,,,
,pond3calibration,463,FALSE,readonly,Pond 3 Calibration Table,JSON list of objects with height and volume props,device,string,[],,,,,,,,
,pond4calibration,463,FALSE,readonly,Pond 4 Calibration Table,JSON list of objects with height and volume props,device,string,[],,,,,,,,
,addcalibrationpoint,463,FALSE,readwrite,Add Calibration Point Command,JSON Object with pond and height and volume props,device,string,,,,,,,,,
,deletecalibrationpoint,463,FALSE,readwrite,Delete Calibration Point Command,JSON Object with pond and point properties,device,string,,,,,,,,,
,sync,463,FALSE,readwrite,Sync data,Synchronize command,device,string,,,,,,,,,
,pond1highalarmsetpoint,463,FALSE,readwrite,Pond 1 High Alarm Setpoint,in Ft,user input,float,50,,,,,,,,
,pond1lowalarmsetpoint,463,FALSE,readwrite,Pond 1 Low Alarm Setpoint,in Ft,user input,float,0,,,,,,,,
,pond2highalarmsetpoint,463,FALSE,readwrite,Pond 2 High Alarm Setpoint,in Ft,user input,float,50,,,,,,,,
,pond2lowalarmsetpoint,463,FALSE,readwrite,Pond 2 Low Alarm Setpoint,in Ft,user input,float,0,,,,,,,,
,pond3highalarmsetpoint,463,FALSE,readwrite,Pond 3 High Alarm Setpoint,in Ft,user input,float,50,,,,,,,,
,pond3lowalarmsetpoint,463,FALSE,readwrite,Pond 3 Low Alarm Setpoint,in Ft,user input,float,0,,,,,,,,
,pond4highalarmsetpoint,463,FALSE,readwrite,Pond 4 High Alarm Setpoint,in Ft,user input,float,50,,,,,,,,
,pond4lowalarmsetpoint,463,FALSE,readwrite,Pond 4 Low Alarm Setpoint,in Ft,user input,float,0,,,,,,,,
1 id name deviceTypeId fromMe io subTitle helpExplanation channelType dataType defaultValue regex regexErrMsg units min max change guaranteedReportPeriod minReportTime
2 13723 log 463 FALSE readwrite Log Device Log device string Created
3 pond1height 463 FALSE readonly Pond 1 Height in Ft device float -99
4 pond1volume 463 FALSE readonly Pond 1 Volume in BBL device float -99
5 pond2height 463 FALSE readonly Pond 2 Height in Ft device float -99
6 pond2volume 463 FALSE readonly Pond 2 Volume in BBL device float -99
7 pond3height 463 FALSE readonly Pond 3 Height in Ft device float -99
8 pond3volume 463 FALSE readonly Pond 3 Volume in BBL device float -99
9 pond4height 463 FALSE readonly Pond 4 Height in Ft device float -99
10 pond4volume 463 FALSE readonly Pond 4 Volume in BBL device float -99
11 cfgnumberofponds 463 FALSE readwrite Number of Ponds Configured 4-Jan device integer 0
12 public_ip_address 463 FALSE readonly Public IP Address network device address device string
13 pondvolumetotal 463 FALSE readonly Total Pond Volume in BBL device float -99
14 pond1calibration 463 FALSE readonly Pond 1 Calibration Table JSON list of objects with height and volume props device string []
15 pond2calibration 463 FALSE readonly Pond 2 Calibration Table JSON list of objects with height and volume props device string []
16 pond3calibration 463 FALSE readonly Pond 3 Calibration Table JSON list of objects with height and volume props device string []
17 pond4calibration 463 FALSE readonly Pond 4 Calibration Table JSON list of objects with height and volume props device string []
18 addcalibrationpoint 463 FALSE readwrite Add Calibration Point Command JSON Object with pond and height and volume props device string
19 deletecalibrationpoint 463 FALSE readwrite Delete Calibration Point Command JSON Object with pond and point properties device string
20 sync 463 FALSE readwrite Sync data Synchronize command device string
21 pond1highalarmsetpoint 463 FALSE readwrite Pond 1 High Alarm Setpoint in Ft user input float 50
22 pond1lowalarmsetpoint 463 FALSE readwrite Pond 1 Low Alarm Setpoint in Ft user input float 0
23 pond2highalarmsetpoint 463 FALSE readwrite Pond 2 High Alarm Setpoint in Ft user input float 50
24 pond2lowalarmsetpoint 463 FALSE readwrite Pond 2 Low Alarm Setpoint in Ft user input float 0
25 pond3highalarmsetpoint 463 FALSE readwrite Pond 3 High Alarm Setpoint in Ft user input float 50
26 pond3lowalarmsetpoint 463 FALSE readwrite Pond 3 Low Alarm Setpoint in Ft user input float 0
27 pond4highalarmsetpoint 463 FALSE readwrite Pond 4 High Alarm Setpoint in Ft user input float 50
28 pond4lowalarmsetpoint 463 FALSE readwrite Pond 4 Low Alarm Setpoint in Ft user input float 0

View File

@@ -0,0 +1,11 @@
{
"driverFileName": "plcpond.py",
"deviceName": "plcpond",
"driverId": "0220",
"releaseVersion": "1",
"files": {
"file1": "plcpond.py",
"file2": "utilities.py",
"file3": "Channel.py"
}
}

View File

@@ -0,0 +1,2 @@
class deviceBase(object):
pass

View File

@@ -0,0 +1,11 @@
{
"name": "plcpond",
"driverFilename": "plcpond.py",
"driverId": "0000",
"additionalDriverFiles": [
"utilities.py",
"Channel.py"
],
"version": 1,
"s3BucketName": "plcpond"
}

View File

@@ -0,0 +1,247 @@
"""Driver for plcpond"""
import threading
import sys
import json
import time
import logging
from random import randint
from device_base import deviceBase
from Channel import PLCChannel, read_tag, write_tag, TAG_DATAERROR_SLEEPTIME
from utilities import get_public_ip_address
_ = None
# LOGGING SETUP
from logging.handlers import RotatingFileHandler
log_formatter = logging.Formatter('%(asctime)s %(levelname)s %(funcName)s(%(lineno)d) %(message)s')
logFile = './plcpond.log'
my_handler = RotatingFileHandler(logFile, mode='a', maxBytes=500*1024, backupCount=2, encoding=None, delay=0)
my_handler.setFormatter(log_formatter)
my_handler.setLevel(logging.INFO)
logger = logging.getLogger('plcpond')
logger.setLevel(logging.INFO)
logger.addHandler(my_handler)
console_out = logging.StreamHandler(sys.stdout)
console_out.setFormatter(log_formatter)
logger.addHandler(console_out)
logger.info("plcpond startup")
# GLOBAL VARIABLES
WATCHDOG_SEND_PERIOD = 3600 # Seconds, the longest amount of time before sending the watchdog status
PLC_IP_ADDRESS = "192.168.1.12"
CHANNELS = [
PLCChannel(PLC_IP_ADDRESS, "cfgnumberofponds", "cfgNumberOfPonds", "REAL", 0.5, 600, map_=False, write_enabled=False, plc_type='Micro800'),
PLCChannel(PLC_IP_ADDRESS, "pond1height", "pond1Height", "REAL", 0.5, 600, map_=False, write_enabled=False, plc_type='Micro800'),
PLCChannel(PLC_IP_ADDRESS, "pond2height", "pond2Height", "REAL", 0.5, 600, map_=False, write_enabled=False, plc_type='Micro800'),
PLCChannel(PLC_IP_ADDRESS, "pond3height", "pond3Height", "REAL", 0.5, 600, map_=False, write_enabled=False, plc_type='Micro800'),
PLCChannel(PLC_IP_ADDRESS, "pond4height", "pond4Height", "REAL", 0.5, 600, map_=False, write_enabled=False, plc_type='Micro800'),
PLCChannel(PLC_IP_ADDRESS, "pond1volume", "pond1Volume", "REAL", 500.0, 600, map_=False, write_enabled=False, plc_type='Micro800'),
PLCChannel(PLC_IP_ADDRESS, "pond2volume", "pond2Volume", "REAL", 500.0, 600, map_=False, write_enabled=False, plc_type='Micro800'),
PLCChannel(PLC_IP_ADDRESS, "pond3volume", "pond3Volume", "REAL", 500.0, 600, map_=False, write_enabled=False, plc_type='Micro800'),
PLCChannel(PLC_IP_ADDRESS, "pond4volume", "pond4Volume", "REAL", 500.0, 600, map_=False, write_enabled=False, plc_type='Micro800'),
PLCChannel(PLC_IP_ADDRESS, "pondvolumetotal", "pondVolumeTotal", "REAL", 500.0, 600, map_=False, write_enabled=False, plc_type='Micro800')
]
CALIBRATION_TABLES = [[],[], [], [], []] # position 0 is a dummy table
class start(threading.Thread, deviceBase):
"""Start class required by Meshify."""
def __init__(self, name=None, number=None, mac=None, Q=None, mcu=None, companyId=None, offset=None, mqtt=None, Nodes=None):
"""Initialize the driver."""
threading.Thread.__init__(self)
deviceBase.__init__(self, name=name, number=number, mac=mac, Q=Q, mcu=mcu, companyId=companyId, offset=offset, mqtt=mqtt, Nodes=Nodes)
self.daemon = True
self.version = "1"
self.finished = threading.Event()
self.force_send = False
threading.Thread.start(self)
# this is a required function for all drivers, its goal is to upload some piece of data
# about your device so it can be seen on the web
def register(self):
"""Register the driver."""
# self.sendtodb("log", "BOOM! Booted.", 0)
pass
def run(self):
"""Actually run the driver."""
wait_sec = 15
for i in range(0, wait_sec):
print("plcpond driver will start in {} seconds".format(wait_sec - i))
time.sleep(1)
logger.info("BOOM! Starting plcpond driver...")
public_ip_address = get_public_ip_address()
self.sendtodbDev(1, 'public_ip_address', public_ip_address, 0, 'plcpond')
watchdog = self.plcpond_watchdog()
self.sendtodbDev(1, 'watchdog', watchdog, 0, 'plcpond')
watchdog_send_timestamp = time.time()
self.nodes["plcpond_0199"] = self
send_loops = 0
watchdog_loops = 0
watchdog_check_after = 5000
while True:
if self.force_send:
logger.warning("FORCE SEND: TRUE")
for c in CHANNELS:
v = c.read()
if v is not None: # read returns None if it fails
if c.check(v, self.force_send):
self.sendtodbDev(1, c.mesh_name, c.value, 0, 'plcpond')
time.sleep(TAG_DATAERROR_SLEEPTIME) # sleep to allow Micro800 to handle ENET requests
for pond_index in range(1, 5):
self.read_pond_calibration(pond_index)
# print("plcpond driver still alive...")
if self.force_send:
if send_loops > 2:
logger.warning("Turning off force_send")
self.force_send = False
send_loops = 0
else:
send_loops += 1
watchdog_loops += 1
if watchdog_loops >= watchdog_check_after:
test_watchdog = self.plcpond_watchdog()
if not test_watchdog == watchdog or (time.time() - watchdog_send_timestamp) > WATCHDOG_SEND_PERIOD:
self.sendtodbDev(1, 'watchdog', test_watchdog, 0, 'plcpond')
watchdog = test_watchdog
test_public_ip = get_public_ip_address()
if not test_public_ip == public_ip_address:
self.sendtodbDev(1, 'public_ip_address', test_public_ip, 0, 'plcpond')
public_ip_address = test_public_ip
watchdog_loops = 0
def read_pond_calibration(self, pond_number):
"""Read all calibration values for a specific pond."""
last_read_height = -1.0
cal_values = []
cal_index = 1
while last_read_height != 0 and cal_index <=10:
cal_tag_height = "pond{}CalibrationHeight[{}]".format(pond_number, cal_index)
cal_tag_volume = "pond{}CalibrationVolume[{}]".format(pond_number, cal_index)
read_height = read_tag(PLC_IP_ADDRESS, cal_tag_height, plc_type="Micro800")
time.sleep(2)
read_volume = read_tag(PLC_IP_ADDRESS, cal_tag_volume, plc_type="Micro800")
time.sleep(2)
if read_height and read_volume:
if read_height[0] > 0.0:
cal_values.append({"height": read_height[0], "volume": read_volume[0]})
last_read_height = read_height[0]
cal_index += 1
if cal_values != CALIBRATION_TABLES[pond_number] or self.force_send:
calibration_channel = "pond{}calibration".format(pond_number)
calibration_string = json.dumps(cal_values).replace('"', "'")
self.sendtodbDev(1, calibration_channel, calibration_string, 0, 'plcpond')
CALIBRATION_TABLES[pond_number] = cal_values
def plcpond_watchdog(self):
"""Write a random integer to the PLC and then 1 seconds later check that it has been decremented by 1."""
randval = randint(0, 32767)
write_tag(str(PLC_IP_ADDRESS), 'watchdog_INT', randval, plc_type="Micro800")
time.sleep(1)
watchdog_val = read_tag(str(PLC_IP_ADDRESS), 'watchdog_INT', plc_type="Micro800")
try:
return (randval - 1) == watchdog_val[0]
except (KeyError, TypeError):
return False
def plcpond_sync(self, name, value):
"""Sync all data from the driver."""
self.force_send = True
# self.sendtodb("log", "synced", 0)
return True
def plcpond_deletecalibrationpoint(self, name, value):
"""Delete a calibration point from a calibration table"""
# {"pond": int, "point": int}
value = value.replace("'", '"')
parsed = json.loads(value)
try:
pond_number = int(parsed['pond'])
point_number = int(parsed['point'])
write_pond = write_tag(PLC_IP_ADDRESS, "inpPondNumber", pond_number, plc_type="Micro800")
time.sleep(2)
write_point = write_tag(PLC_IP_ADDRESS, "inpDeletePointIndex", point_number, plc_type="Micro800")
time.sleep(2)
if write_pond and write_point:
write_cmd = write_tag(PLC_IP_ADDRESS, "cmdDeleteCalibrationPoint", 1, plc_type="Micro800")
time.sleep(2)
if write_cmd:
read_val = read_tag(PLC_IP_ADDRESS, "deleteSuccess", plc_type="Micro800")
if read_val[0] == 1:
self.read_pond_calibration(pond_number)
return True
return "Wrote everything successfully, but delete didn't succeed (Check pond and point values)."
return "Didn't write delete command correctly."
return "Didn't write pond or point correctly."
except KeyError as e:
return "Couldn't parse input value: {} -- {}".format(value, e)
def plcpond_cfgnumberofponds(self, name, value):
"""Write the number of ponds to the plc."""
value = int(value)
return write_tag(PLC_IP_ADDRESS, "cfgNumberOfPonds", value, plc_type="Micro800")
def plcpond_addcalibrationpoint(self, name, value):
"""Add a calibration point to the calibration table"""
# value = {"pond": int, "height": float, "volume": float}
value = value.replace("'", '"')
parsed = json.loads(value)
try:
# parse json values, throw an error if one is missing
pond_number = int(parsed['pond'])
height = float(parsed['height'])
volume = float(parsed['volume'])
# write values to the tags
write_pond = write_tag(PLC_IP_ADDRESS, "inpPondNumber", pond_number, plc_type="Micro800")
time.sleep(2)
write_height = write_tag(PLC_IP_ADDRESS, "inpPondHeight", height, plc_type="Micro800")
time.sleep(2)
write_volume= write_tag(PLC_IP_ADDRESS, "inpPondVolume", volume, plc_type="Micro800")
time.sleep(2)
if write_pond and write_height and write_volume:
write_cmd = write_tag(PLC_IP_ADDRESS, "cmdAddCalibrationPoint", 1, plc_type="Micro800")
time.sleep(2)
if write_cmd:
read_val = read_tag(PLC_IP_ADDRESS, "addSuccess", plc_type="Micro800")
if read_val[0] == 1:
self.read_pond_calibration(pond_number)
return True
return "Wrote everything successfully, but delete didn't succeed (Check pond and point values)."
return "Didn't write delete command correctly."
return "Didn't write pond or point correctly."
except KeyError as e:
return "Couldn't parse input value: {} -- {}".format(value, e)
def plcpond_writeplctag(self, name, value):
"""Write a value to the PLC."""
new_val = json.loads(str(value).replace("'", '"'))
tag_n = str(new_val['tag']) # "cmd_Start"
val_n = new_val['val']
w = write_tag(str(PLC_IP_ADDRESS), tag_n, val_n)
print("Result of plcpond_writeplctag(self, {}, {}) = {}".format(name, value, w))
if w is None:
w = "Error writing to PLC..."
return w

View File

@@ -0,0 +1,26 @@
import json
import time
from Channel import read_tag, write_tag
PLC_IP_ADDRESS = "192.168.1.12"
def read_pond_calibration(pond_number):
"""Read all calibration values for a specific pond."""
last_read_height = -1.0
cal_values = []
cal_index = 1
while last_read_height != 0 and cal_index <=10:
cal_tag_height = "pond{}CalibrationHeight[{}]".format(pond_number, cal_index)
cal_tag_volume = "pond{}CalibrationVolume[{}]".format(pond_number, cal_index)
print(cal_tag_height, cal_tag_volume)
read_height = read_tag(PLC_IP_ADDRESS, cal_tag_height, plc_type="Micro800")
time.sleep(2)
read_volume = read_tag(PLC_IP_ADDRESS, cal_tag_volume, plc_type="Micro800")
time.sleep(2)
print(read_height, read_volume)
if read_height and read_volume:
if read_height[0] > 0.0:
cal_values.append({"height": read_height[0], "volume": read_volume[0]})
last_read_height = read_height[0]
cal_index += 1
return cal_values

View File

@@ -0,0 +1,51 @@
"""Utility functions for the driver."""
import socket
import struct
def get_public_ip_address():
"""Find the public IP Address of the host device."""
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.connect(("8.8.8.8", 80))
ip = s.getsockname()[0]
s.close()
return ip
def int_to_float16(int_to_convert):
"""Convert integer into float16 representation."""
bin_rep = ('0' * 16 + '{0:b}'.format(int_to_convert))[-16:]
sign = 1.0
if int(bin_rep[0]) == 1:
sign = -1.0
exponent = float(int(bin_rep[1:6], 2))
fraction = float(int(bin_rep[6:17], 2))
if exponent == float(0b00000):
return sign * 2 ** -14 * fraction / (2.0 ** 10.0)
elif exponent == float(0b11111):
if fraction == 0:
return sign * float("inf")
else:
return float("NaN")
else:
frac_part = 1.0 + fraction / (2.0 ** 10.0)
return sign * (2 ** (exponent - 15)) * frac_part
def ints_to_float(int1, int2):
"""Convert 2 registers into a floating point number."""
mypack = struct.pack('>HH', int1, int2)
f = struct.unpack('>f', mypack)
print("[{}, {}] >> {}".format(int1, int2, f[0]))
return f[0]
def degf_to_degc(temp_f):
"""Convert deg F to deg C."""
return (temp_f - 32.0) * (5.0/9.0)
def degc_to_degf(temp_c):
"""Convert deg C to deg F."""
return temp_c * 1.8 + 32.0