Skip to content

Commit

Permalink
Merge pull request #53 from andrius-k/add-support-for-sound-alarm-man…
Browse files Browse the repository at this point in the history
…ager

Added visDQMSoundAlarmManager for enabling/disabling DQM P5 sound alarms
  • Loading branch information
rovere authored Feb 13, 2020
2 parents 8ad630d + cbb3243 commit 2508f9d
Show file tree
Hide file tree
Showing 2 changed files with 254 additions and 9 deletions.
43 changes: 34 additions & 9 deletions bin/visDQMSoundAlarmDaemon
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@ import re
import os
import time
import sys
import cjson
import json
from traceback import print_exc
from datetime import datetime
from urllib import quote
from fcntl import lockf, LOCK_EX, LOCK_UN
from socket import socket, AF_INET, SOCK_STREAM, gethostname
from subprocess import Popen,PIPE
from subprocess import Popen, PIPE

# IMPORTANT: If you want to play a test sound, just start the program with
# all the usual parameters, but add "test" as last parameter.
Expand Down Expand Up @@ -39,6 +39,12 @@ from subprocess import Popen,PIPE
# system, the global clock gets reset every time we pass from 0 alarms
# to at least 1 alarm.

# There is an extension to this tool called visDQMSoundAlarmManager.
# It's a small web app which displays all plots located in ERROR_FOLDER
# and provides an ability to disable alarms for chosen plots.
# This tool queries the API of the visDQMSoundAlarmManager for the list
# of disabled plots before sending the alarms.

sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 0)

# --------------------------------------------------------------------
Expand All @@ -53,10 +59,16 @@ if len(sys.argv) >= 7:
# accepted, separated by ','
else:
EMAIL_ADDRESSES = ''
# To enable the special "test-mode" add a 7th argument "test"
# Otherwise the daemon does its normal stuff (which is good)

# URL to get a list of disabled alarms
ALARM_MANAGER_URL = 'http://localhost:8031/disabled'
if len(sys.argv) >= 8:
IS_TEST = (sys.argv[7] == "test")
ALARM_MANAGER_URL = sys.argv[7]

# To enable the special "test-mode" add a 8th argument "test"
# Otherwise the daemon does its normal stuff (which is good)
if len(sys.argv) >= 9:
IS_TEST = (sys.argv[8] == "test")
else:
IS_TEST = False

Expand Down Expand Up @@ -179,7 +191,7 @@ def run_daemon():
# counter.
activeURLErrors = 0

state = cjson.decode(state)
state = json.loads(state)

## Detect No run state
if noRun == False and len(state["contents"]) <= 1:
Expand All @@ -199,13 +211,26 @@ def run_daemon():
knownNewAlarms = set()
for histo in state["contents"]:
if histo.has_key('obj'):
name = histo['obj']
name = '%s/%s' % (histo['dir'], histo['obj'])
else:
continue

# Get the disasbled alarms from the alarm manager:
disabledAlarms = []
try:
datareq = urllib2.Request(ALARM_MANAGER_URL)
result = urllib2.build_opener(urllib2.ProxyHandler({})).open(datareq)
disabledAlarms = json.loads(result.read())
except:
# If we're unable to get disabled alarms, assume that all of them are enabled
pass

if histo['properties']['report']['alarm'] == 1:
knownNewAlarms.add(name)
logme("Info from the DQM GUI: %s" % str(histo))
if name in disabledAlarms:
logme("Histo caused an alarm but it was disabled in the alarm manager: %s" % name)
else:
knownNewAlarms.add(name)
logme("Info from the DQM GUI: %s" % str(histo))

# should alarm be triggered
alarmsNew = knownNewAlarms.difference(knownAlarms)
Expand Down
220 changes: 220 additions & 0 deletions bin/visDQMSoundAlarmManager
Original file line number Diff line number Diff line change
@@ -0,0 +1,220 @@
#!/usr/bin/env python

import sys
import json
import urllib2
import SocketServer
import SimpleHTTPServer
from urllib import quote

# Configurable parameters
GUI_URL = 'http://localhost:8070/dqm/online-dev/'
PORT = 8031

if len(sys.argv) > 1:
GUI_URL = sys.argv[1]

if len(sys.argv) > 2:
PORT = int(sys.argv[2])

# Constants
ERRORS_FOLDER = 'data/json/live/1/Global/Online/ALL/00 Shift/Errors'
STATUSES = { 30: 'OTHER', 50: 'DISABLED', 60: 'INVALID', 70: 'INSUF_STAT', 90: 'DID_NOT_RUN',
100: 'STATUS_OK', 200: 'WARNING', 300: 'ERROR'
}

# Dict with keys of disabled ME names
DISABLED_MES = {}

def start():
SocketServer.TCPServer.allow_reuse_address = True
server = SocketServer.TCPServer(('0.0.0.0', PORT), CustomRequestHandler)
try:
server.serve_forever()
except:
pass
server.shutdown()

class CustomRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
def do_GET(self):

# Return the main table
if self.path == '/':
state = self.get_gui_data()
content = ''

for index, item in enumerate(state['contents']):
if 'obj' in item:
me = '%s/%s' % (item['dir'], item['obj'])
content += '<tr>'
content += ' <td>%s</td>' % me
content += ' <td>%s</td>' % ', '.join(str(x['name']) for x in item['qresults'])
content += ' <td>%s</td>' % ', '.join(str(x['algorithm']) for x in item['qresults'])
content += ' <td>%s</td>' % ', '.join(str(x['message']) for x in item['qresults'])
content += ' <td>%s</td>' % ', '.join(str(STATUSES.get(x['status'], x['status'])) for x in item['qresults'])
content += ' <td>%s</td>' % ', '.join(str(x['result']) for x in item['qresults'])

if me in DISABLED_MES:
content += ' <td class="danger">No</td>'
content += ' <td><button onclick="enable(\'%s\')">Enable</button></td>' % me
else:
content += ' <td class="success">Yes</td>'
content += ' <td><button onclick="disable(\'%s\')">Disable</button></td>' % me

content += '</tr>'

self.send_response(200)
self.end_headers()
self.wfile.write(TEMPLATE.replace('$CONTENT$', content))

# Return all enabled MEs
elif self.path == '/disabled':
result = []
for me in DISABLED_MES:
if DISABLED_MES[me]:
result.append(me)

self.send_response(200)
self.end_headers()
self.wfile.write(json.dumps(result))

else:
self.send_response(404)
self.end_headers()
self.wfile.write('<h1>404 Not Found</h1>')

def do_POST(self):
# Enable given ME
if self.path.startswith('/enable?me='):
me = self.path.split('/enable?me=', 1)[1]
DISABLED_MES.pop(me, None)
self.send_response(200)
self.end_headers()
self.wfile.write(me)

# Disable given ME
elif self.path.startswith('/disable?me='):
me = self.path.split('/disable?me=', 1)[1]
DISABLED_MES[me] = True
self.send_response(200)
self.end_headers()
self.wfile.write(me)

else:
self.send_response(404)
self.end_headers()
self.wfile.write('<h1>404 Not Found</h1>')

def do_HEAD(self):
self.send_response(501)
self.end_headers()
self.wfile.write('<h1>501 Unsupported method</h1>')


def get_gui_data(self):
url = '%s%s' % (GUI_URL, quote(ERRORS_FOLDER))

try:
datareq = urllib2.Request(url)
result = urllib2.build_opener(urllib2.ProxyHandler({})).open(datareq)
state = result.read()
except:
return json.loads('{"contents": [{ "streamerinfo": "" }]}')

state = json.loads(state)

return state


TEMPLATE = '''
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>DQM Sound Alarm Manager</title>
<style>
body {
font-family: arial, sans-serif;
}
h4, h3, h2, p {
padding-left: 6px;
}
table {
border-collapse: collapse;
}
td,
th {
text-align: left;
padding: 6px;
}
tr:nth-child(even) {
background-color: #f1f1f1;
}
.danger {
color: #ff4242;
}
.success {
color: #00b30f;
}
</style>
</head>
<body>
<h3>Enable/disable DQM sound alarms at P5</h3>
<p>You can use this tool to enable/disable P5 sound alarms for certain DQM plot in the Errors folder of the DQM GUI.
If the alarm for a plot is <i>enabled</i>, it <i>will play a sound alarm</i> in the control room.</p>
<p>All alarms will switch to enabled state after the restart of the DQM GUI.</p>
</br>
<table>
<tr>
<th>Monitor element</th>
<th>QResult name</th>
<th>QResult algorithm</th>
<th>QResult Message</th>
<th>QResult Status</th>
<th>QResult Result</th>
<th>Enabled</th>
<th>Action</th>
</tr>
$CONTENT$
</table>
<script>
function disable(me) {
fetch('/disable?me=' + me, {
method: 'post',
}).then(function(response) {
console.log(response)
location.reload()
})
}
function enable(me) {
fetch('/enable?me=' + me, {
method: 'post',
}).then(function(response) {
console.log(response)
location.reload()
})
}
</script>
</body>
</html>
'''


if __name__ == '__main__':
start()

0 comments on commit 2508f9d

Please sign in to comment.