Initial monitoring support

This commit is contained in:
thiagoralves 2019-06-17 12:32:07 -05:00
parent 6ac8d4f76b
commit 92c8fede60
3 changed files with 354 additions and 4 deletions

101
webserver/monitoring.py Normal file
View File

@ -0,0 +1,101 @@
import time, threading
from pymodbus.client.sync import ModbusTcpClient
class debug_var():
name = ''
location = ''
type = ''
forced = 'No'
value = 0
debug_vars = []
monitor_active = False
mb_client = None
def parse_st(st_file):
global debug_vars
filepath = './st_files/' + st_file
st_program = open(filepath, 'r')
for line in st_program.readlines():
if line.find(' AT ') > 0:
debug_data = debug_var()
tmp = line.strip().split(' ')
debug_data.name = tmp[0]
debug_data.location = tmp[2]
debug_data.type = tmp[4].split(';')[0]
debug_vars.append(debug_data)
for debugs in debug_vars:
print('Name: ' + debugs.name)
print('Location: ' + debugs.location)
print('Type: ' + debugs.type)
print('')
def cleanup():
del debug_vars[:]
def modbus_monitor():
global mb_client
for debug_data in debug_vars:
if (debug_data.location.find('IX')) > 0:
#Reading Input Status
mb_address = debug_data.location.split('%IX')[1].split('.')
result = mb_client.read_discrete_inputs(int(mb_address[0])*8 + int(mb_address[1]), 1)
debug_data.value = result.bits[0]
elif (debug_data.location.find('QX')) > 0:
#Reading Coils
mb_address = debug_data.location.split('%QX')[1].split('.')
result = mb_client.read_coils(int(mb_address[0])*8 + int(mb_address[1]), 1)
debug_data.value = result.bits[0]
elif (debug_data.location.find('IW')) > 0:
#Reading Input Registers
mb_address = debug_data.location.split('%IW')[1]
result = mb_client.read_input_registers(int(mb_address), 1)
debug_data.value = result.registers[0]
elif (debug_data.location.find('QW')) > 0:
#Reading Holding Registers
mb_address = debug_data.location.split('%QW')[1]
result = mb_client.read_holding_registers(int(mb_address), 1)
debug_data.value = result.registers[0]
elif (debug_data.location.find('MW')) > 0:
#Reading Word Memory
mb_address = debug_data.location.split('%MW')[1]
result = mb_client.read_holding_registers(int(mb_address) + 1024, 1)
debug_data.value = result.registers[0]
elif (debug_data.location.find('MD')) > 0:
#Reading Double Memory
print('hi')
elif (debug_data.location.find('ML')) > 0:
#Reading Long Memory
print('hi')
if (monitor_active == True):
threading.Timer(0.5, modbus_monitor).start()
def start_monitor():
global monitor_active
global mb_client
if (monitor_active != True):
monitor_active = True
mb_client = ModbusTcpClient('127.0.0.1')
modbus_monitor()
def stop_monitor():
global monitor_active
global mb_client
if (monitor_active != False):
monitor_active = False
mb_client.close()

View File

@ -680,6 +680,154 @@ loading logs...
</script>
</html>"""
monitoring_head = """
/* OpenPLC Style */
.top {
position:absolute;
left:0; right:0; top:0;
height: 50px;
background-color: #1F1F1F;
position: fixed;
overflow: hidden;
z-index: 10
}
.main {
position: absolute;
left:0px; top:50px; right:0; bottom:0;
}
.user {
position:absolute;
left:75%; right:0; top:0;
height: 50px;
position: fixed;
overflow: hidden;
z-index: 11;
text-align:right;
}
.button {
background-color: #E02222;
border: 1px solid #1F1F1F;
border-radius: 4px;
color: white;
padding: 15px 32px;
text-align: center;
text-decoration: none;
display: inline-block;
font-size: 16px;
font-family: "Roboto", sans-serif;
}
.button:hover {
background-color: #B51A1A;
}
table, h1, h2, h3, p {
font-family: "Roboto", sans-serif;
border-collapse: collapse;
width: 100%;
}
td, th {
border: 1px solid #cccccc;
text-align: left;
padding: 8px;
}
tr:nth-child(even) {
background-color: #eeeeee;
}
tr:hover {
cursor: hand;background-color: slategray;
}
label {
font-family: arial, sans-serif;
}
input[type=text], input[type=password], select, textarea {
width: 100%;
padding: 12px 20px;
margin: 8px 0;
display: inline-block;
border: 1px solid #ccc;
border-radius: 4px;
box-sizing: border-box;
font-family: "Roboto", sans-serif;
}
</style>
<body onload='loadData()'>"""
monitoring_tail = """
</table>
</div>
</div>
</div>
</div>
</body>
<script>
var req;
function loadData()
{
url = 'monitor-update'
try
{
req = new XMLHttpRequest();
} catch (e)
{
try
{
req = new ActiveXObject('Msxml2.XMLHTTP');
} catch (e)
{
try
{
req = new ActiveXObject('Microsoft.XMLHTTP');
} catch (oc)
{
alert('No AJAX Support');
return;
}
}
}
req.onreadystatechange = processReqChange;
req.open('GET', url, true);
req.send(null);
}
function processReqChange()
{
//If req shows 'complete'
if (req.readyState == 4)
{
mon_table = document.getElementById('monitor_table');
//If 'OK'
if (req.status == 200)
{
//Update table contents
mon_table.innerHTML = req.responseText;
//Start a new update timer
timeoutID = setTimeout('loadData()', 100);
}
else
{
runtime_logs.value = 'There was a problem retrieving the logs. Error: ' + req.statusText;
}
}
}
</script>
</html>"""
add_user_tail = """
</div>
<div style="margin-left:320px; margin-right:70px">

View File

@ -9,6 +9,7 @@ import datetime
import time
import pages
import openplc
import monitoring as monitor
import sys
import flask
@ -452,9 +453,12 @@ def start_plc():
if (flask_login.current_user.is_authenticated == False):
return flask.redirect(flask.url_for('login'))
else:
monitor.stop_monitor()
openplc_runtime.start_runtime()
time.sleep(1)
configure_runtime()
monitor.cleanup()
monitor.parse_st(openplc_runtime.project_file)
return flask.redirect(flask.url_for('dashboard'))
@ -466,6 +470,7 @@ def stop_plc():
else:
openplc_runtime.stop_runtime()
time.sleep(1)
monitor.stop_monitor()
return flask.redirect(flask.url_for('dashboard'))
@ -484,6 +489,7 @@ def dashboard():
if (flask_login.current_user.is_authenticated == False):
return flask.redirect(flask.url_for('login'))
else:
monitor.stop_monitor()
if (openplc_runtime.status() == "Compiling"): return draw_compiling_page()
return_str = pages.w3_style + pages.dashboard_head + draw_top_div()
return_str += """
@ -529,6 +535,7 @@ def programs():
if (flask_login.current_user.is_authenticated == False):
return flask.redirect(flask.url_for('login'))
else:
monitor.stop_monitor()
if (openplc_runtime.status() == "Compiling"): return draw_compiling_page()
list_all = False
if (flask.request.args.get('list_all') == '1'):
@ -847,6 +854,7 @@ def modbus():
if (flask_login.current_user.is_authenticated == False):
return flask.redirect(flask.url_for('login'))
else:
monitor.stop_monitor()
if (openplc_runtime.status() == "Compiling"): return draw_compiling_page()
return_str = pages.w3_style + pages.style + draw_top_div()
return_str += """
@ -1265,15 +1273,101 @@ def monitoring():
return flask.redirect(flask.url_for('login'))
else:
if (openplc_runtime.status() == "Compiling"): return draw_compiling_page()
return_str = draw_blank_page()
return_str = pages.w3_style + pages.monitoring_head + draw_top_div()
return_str += """
<div class='main'>
<div class='w3-sidebar w3-bar-block' style='width:250px; background-color:#3D3D3D'>
<br>
<br>
<a href="dashboard" class="w3-bar-item w3-button"><img src="/static/home-icon-64x64.png" alt="Dashboard" style="width:47px;height:32px;padding:0px 15px 0px 0px;float:left"><p style='font-family:"Roboto", sans-serif; font-size:20px; color:white;margin: 2px 0px 0px 0px'>Dashboard</p></a>
<a href='programs' class='w3-bar-item w3-button'><img src='/static/programs-icon-64x64.png' alt='Programs' style='width:47px;height:32px;padding:0px 15px 0px 0px;float:left'><p style='font-family:\"Roboto\", sans-serif; font-size:20px; color:white;margin: 2px 0px 0px 0px'>Programs</p></a>
<a href='modbus' class='w3-bar-item w3-button'><img src='/static/modbus-icon-512x512.png' alt='Modbus' style='width:47px;height:32px;padding:0px 15px 0px 0px;float:left'><p style='font-family:\"Roboto\", sans-serif; font-size:20px; color:white;margin: 2px 0px 0px 0px'>Slave Devices</p></a>
<a href="monitoring" class="w3-bar-item w3-button" style="background-color:#E02222; padding-right:0px;padding-top:0px;padding-bottom:0px"><img src="/static/monitoring-icon-64x64.png" alt="Monitoring" style="width:47px;height:39px;padding:7px 15px 0px 0px;float:left"><img src="/static/arrow.png" style="width:17px;height:49px;padding:0px 0px 0px 0px;margin: 0px 0px 0px 0px; float:right"><p style='font-family:"Roboto", sans-serif; font-size:20px; color:white;margin: 10px 0px 0px 0px'>Monitoring</p></a>
<a href='hardware' class='w3-bar-item w3-button'><img src='/static/hardware-icon-980x974.png' alt='Hardware' style='width:47px;height:32px;padding:0px 15px 0px 0px;float:left'><p style='font-family:\"Roboto\", sans-serif; font-size:20px; color:white;margin: 2px 0px 0px 0px'>Hardware</p></a>
<a href='users' class='w3-bar-item w3-button'><img src='/static/users-icon-64x64.png' alt='Users' style='width:47px;height:32px;padding:0px 15px 0px 0px;float:left'><p style='font-family:\"Roboto\", sans-serif; font-size:20px; color:white;margin: 2px 0px 0px 0px'>Users</p></a>
<a href='settings' class='w3-bar-item w3-button'><img src='/static/settings-icon-64x64.png' alt='Settings' style='width:47px;height:32px;padding:0px 15px 0px 0px;float:left'><p style='font-family:\"Roboto\", sans-serif; font-size:20px; color:white;margin: 2px 0px 0px 0px'>Settings</p></a>
<a href='logout' class='w3-bar-item w3-button'><img src='/static/logout-icon-64x64.png' alt='Logout' style='width:47px;height:32px;padding:0px 15px 0px 0px;float:left'><p style='font-family:\"Roboto\", sans-serif; font-size:20px; color:white;margin: 2px 0px 0px 0px'>Logout</p></a>
<br>
<br>"""
return_str += draw_status()
return_str += """
</div>
<div style="margin-left:320px; margin-right:70px">
<div style="w3-container">
<br>
<h2>Monitoring</h2>
<p>This feature is not available yet! Check back later...<p>
<p>The table below displays a list of the OpenPLC points used by the currently running program. By clicking in one of the listed points it is possible to see more information about it and also to force it to be a different value.</p>
<div id='monitor_table'>
<table>
<col width="50"><col width="10"><col width="10"><col width="10"><col width="100">
<tr style='background-color: white'>
<th>Point Name</th><th>Type</th><th>Location</th><th>Forced</th><th>Value</th>
</tr>"""
if (openplc_runtime.status() == "Running"):
monitor.start_monitor()
data_index = 0
for debug_data in monitor.debug_vars:
return_str += '<tr style="height:60px" onclick="document.location=\'point-info?table_id=' + str(data_index) + '\'">'
return_str += '<td>' + debug_data.name + '</td><td>' + debug_data.type + '</td><td>' + debug_data.location + '</td><td>' + debug_data.forced + '</td><td valign="middle">'
if (debug_data.type == 'BOOL'):
if (debug_data.value == 0):
return_str += '<img src="/static/bool_false.png" alt="bool_false" style="width:40px;height:40px;vertical-align:middle; margin-right:10px">FALSE</td>'
else:
return_str += '<img src="/static/bool_true.png" alt="bool_true" style="width:40px;height:40px;vertical-align:middle; margin-right:10px">TRUE</td>'
else:
percentage = (debug_data.value*100)/65535
return_str += '<div class="w3-grey w3-round" style="height:40px"><div class="w3-container w3-blue w3-round" style="height:40px;width:' + str(int(percentage)) + '%"><p style="margin-top:10px">' + str(debug_data.value) + '</p></div></div></td>'
return_str += '</tr>'
data_index += 1
return_str += pages.monitoring_tail
else:
return_str += """
</table>
</div>
</div>
</div>
</div>
</body>
</html>"""
return return_str
@app.route('/monitor-update', methods=['GET', 'POST'])
def monitor_update():
if (flask_login.current_user.is_authenticated == False):
return flask.redirect(flask.url_for('login'))
else:
#if (openplc_runtime.status() == "Compiling"): return 'OpenPLC is compiling new code. Please wait'
return_str = """
<table>
<col width="50"><col width="10"><col width="10"><col width="10"><col width="100">
<tr style='background-color: white'>
<th>Point Name</th><th>Type</th><th>Location</th><th>Forced</th><th>Value</th>
</tr>"""
#if (openplc_runtime.status() == "Running"):
if (True):
monitor.start_monitor()
data_index = 0
for debug_data in monitor.debug_vars:
return_str += '<tr style="height:60px" onclick="document.location=\'point-info?table_id=' + str(data_index) + '\'">'
return_str += '<td>' + debug_data.name + '</td><td>' + debug_data.type + '</td><td>' + debug_data.location + '</td><td>' + debug_data.forced + '</td><td valign="middle">'
if (debug_data.type == 'BOOL'):
if (debug_data.value == 0):
return_str += '<img src="/static/bool_false.png" alt="bool_false" style="width:40px;height:40px;vertical-align:middle; margin-right:10px">FALSE</td>'
else:
return_str += '<img src="/static/bool_true.png" alt="bool_true" style="width:40px;height:40px;vertical-align:middle; margin-right:10px">TRUE</td>'
else:
percentage = (debug_data.value*100)/65535
return_str += '<div class="w3-grey w3-round" style="height:40px"><div class="w3-container w3-blue w3-round" style="height:40px;width:' + str(int(percentage)) + '%"><p style="margin-top:10px">' + str(debug_data.value) + '</p></div></div></td>'
return_str += '</tr>'
data_index += 1
return_str += """
</table>"""
return return_str
@ -1282,6 +1376,7 @@ def hardware():
if (flask_login.current_user.is_authenticated == False):
return flask.redirect(flask.url_for('login'))
else:
monitor.stop_monitor()
if (openplc_runtime.status() == "Compiling"): return draw_compiling_page()
if (flask.request.method == 'GET'):
with open('./scripts/openplc_driver') as f: current_driver = f.read().rstrip()
@ -1365,6 +1460,7 @@ def users():
if (flask_login.current_user.is_authenticated == False):
return flask.redirect(flask.url_for('login'))
else:
monitor.stop_monitor()
if (openplc_runtime.status() == "Compiling"): return draw_compiling_page()
return_str = pages.w3_style + pages.style + draw_top_div()
return_str += """
@ -1656,6 +1752,7 @@ def settings():
if (flask_login.current_user.is_authenticated == False):
return flask.redirect(flask.url_for('login'))
else:
monitor.stop_monitor()
if (openplc_runtime.status() == "Compiling"): return draw_compiling_page()
if (flask.request.method == 'GET'):
return_str = pages.w3_style + pages.settings_style + draw_top_div() + pages.settings_head
@ -1866,8 +1963,12 @@ def settings():
@app.route('/logout')
def logout():
flask_login.logout_user()
return flask.redirect(flask.url_for('login'))
if (flask_login.current_user.is_authenticated == False):
return flask.redirect(flask.url_for('login'))
else:
monitor.stop_monitor()
flask_login.logout_user()
return flask.redirect(flask.url_for('login'))
@login_manager.unauthorized_handler