2018-06-22 10:34:14 -04:00
#!/usr/bin/env python
#
###############################################################################
# Copyright (C) 2016 Cortney T. Buffington, N0MJS <n0mjs@me.com>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
###############################################################################
from __future__ import print_function
2018-11-09 14:52:12 -05:00
# Standard modules
2018-06-22 10:34:14 -04:00
import logging
import sys
# Twisted modules
from twisted . internet . protocol import ReconnectingClientFactory , Protocol
from twisted . protocols . basic import NetstringReceiver
from twisted . internet import reactor , task
from twisted . web . server import Site
from twisted . web . static import File
from twisted . web . resource import Resource
# Autobahn provides websocket service under Twisted
from autobahn . twisted . websocket import WebSocketServerProtocol , WebSocketServerFactory
# Specific functions to import from standard modules
from pprint import pprint
from time import time , strftime , localtime
from cPickle import loads
from binascii import b2a_hex as h
from os . path import getmtime
from collections import deque
2018-12-02 18:03:09 -05:00
from time import time
2018-06-22 10:34:14 -04:00
# Web templating environment
from jinja2 import Environment , PackageLoader , select_autoescape
# Utilities from K0USY Group sister project
2018-12-02 14:33:45 -05:00
from dmr_utils . utils import int_id , get_alias , try_download , mk_full_id_dict , hex_str_4
2018-06-22 10:34:14 -04:00
# Configuration variables and IPSC constants
from config import *
#from ipsc_const import *
# Opcodes for reporting protocol to HBlink
OPCODE = {
' CONFIG_REQ ' : ' \x00 ' ,
' CONFIG_SND ' : ' \x01 ' ,
' BRIDGE_REQ ' : ' \x02 ' ,
' BRIDGE_SND ' : ' \x03 ' ,
' CONFIG_UPD ' : ' \x04 ' ,
' BRIDGE_UPD ' : ' \x05 ' ,
' LINK_EVENT ' : ' \x06 ' ,
' BRDG_EVENT ' : ' \x07 ' ,
}
# Global Variables:
CONFIG = { }
2018-11-30 14:38:44 -05:00
CTABLE = { ' MASTERS ' : { } , ' PEERS ' : { } , ' OPENBRIDGES ' : { } }
2018-06-22 10:34:14 -04:00
BRIDGES = { }
BTABLE = { }
BTABLE [ ' BRIDGES ' ] = { }
BRIDGES_RX = ' '
CONFIG_RX = ' '
LOGBUF = deque ( 100 * [ ' ' ] , 100 )
RED = ' #ff0000 '
2018-12-05 12:57:01 -05:00
BLACK = ' #000000 '
2018-06-22 10:34:14 -04:00
GREEN = ' #00ff00 '
BLUE = ' #0000ff '
ORANGE = ' #ff8000 '
WHITE = ' #ffffff '
# For importing HTML templates
def get_template ( _file ) :
with open ( _file , ' r ' ) as html :
return html . read ( )
# Alias string processor
def alias_string ( _id , _dict ) :
alias = get_alias ( _id , _dict , ' CALLSIGN ' , ' CITY ' , ' STATE ' )
if type ( alias ) == list :
for x , item in enumerate ( alias ) :
if item == None :
alias . pop ( x )
return ' , ' . join ( alias )
else :
return alias
2018-11-29 15:11:56 -05:00
def alias_short ( _id , _dict ) :
alias = get_alias ( _id , _dict , ' CALLSIGN ' , ' NAME ' )
if type ( alias ) == list :
for x , item in enumerate ( alias ) :
if item == None :
alias . pop ( x )
return ' , ' . join ( alias )
else :
return str ( alias )
2018-12-01 12:31:35 -05:00
2018-11-29 15:17:41 -05:00
def alias_call ( _id , _dict ) :
alias = get_alias ( _id , _dict , ' CALLSIGN ' )
if type ( alias ) == list :
for x , item in enumerate ( alias ) :
if item == None :
alias . pop ( x )
return ' , ' . join ( alias )
else :
return str ( alias )
2018-12-05 10:14:34 -05:00
# Return friendly elpasted time from time in seconds.
def since ( _time ) :
now = int ( time ( ) )
_time = now - int ( _time )
seconds = _time % 60
minutes = ( _time / 60 ) % 60
hours = ( _time / 60 / 60 ) % 24
days = ( _time / 60 / 60 / 24 )
if days :
return ' {} d {} h ' . format ( days , hours )
elif hours :
return ' {} h {} m ' . format ( hours , minutes )
elif minutes :
return ' {} m {} s ' . format ( minutes , seconds )
else :
return ' {} s ' . format ( seconds )
2018-11-29 15:11:56 -05:00
2018-12-02 14:33:45 -05:00
def add_hb_peer ( _peer_conf , _ctable_loc , _peer ) :
_ctable_loc [ int_id ( _peer ) ] = { }
_ctable_peer = _ctable_loc [ int_id ( _peer ) ]
# if the Frequency is 000.xxx assume it's not an RF peer, otherwise format the text fields
if _peer_conf [ ' TX_FREQ ' ] [ : 3 ] == ' 000 ' or _peer_conf [ ' RX_FREQ ' ] [ : 3 ] == ' 000 ' :
_ctable_peer [ ' TX_FREQ ' ] = ' N/A '
_ctable_peer [ ' RX_FREQ ' ] = ' '
else :
_ctable_peer [ ' TX_FREQ ' ] = ' TX: ' + _peer_conf [ ' TX_FREQ ' ] [ : 3 ] + ' . ' + _peer_conf [ ' TX_FREQ ' ] [ 3 : 7 ]
_ctable_peer [ ' RX_FREQ ' ] = ' RX: ' + _peer_conf [ ' RX_FREQ ' ] [ : 3 ] + ' . ' + _peer_conf [ ' RX_FREQ ' ] [ 3 : 7 ]
# timeslots are kinda complicated too. 0 = none, 1 or 2 mean that one slot, 3 is both, and anythign else it considered DMO
if ( _peer_conf [ ' SLOTS ' ] == ' 0 ' ) :
_ctable_peer [ ' SLOTS ' ] = ' NONE '
2018-12-06 12:51:07 -05:00
elif ( _peer_conf [ ' SLOTS ' ] < = ' 2 ' ) :
2018-12-02 14:33:45 -05:00
_ctable_peer [ ' SLOTS ' ] = _peer_conf [ ' SLOTS ' ]
elif ( _peer_conf [ ' SLOTS ' ] == ' 3 ' ) :
_ctable_peer [ ' SLOTS ' ] = ' BOTH '
else :
_ctable_peer [ ' SLOTS ' ] = ' DMO '
# Simple translation items
_ctable_peer [ ' COLORCODE ' ] = _peer_conf [ ' COLORCODE ' ]
_ctable_peer [ ' CALLSIGN ' ] = _peer_conf [ ' CALLSIGN ' ]
_ctable_peer [ ' LOCATION ' ] = _peer_conf [ ' LOCATION ' ]
_ctable_peer [ ' CONNECTION ' ] = _peer_conf [ ' CONNECTION ' ]
2018-12-05 10:14:34 -05:00
_ctable_peer [ ' CONNECTED ' ] = since ( _peer_conf [ ' CONNECTED ' ] )
2018-12-02 14:33:45 -05:00
_ctable_peer [ ' IP ' ] = _peer_conf [ ' IP ' ]
_ctable_peer [ ' PORT ' ] = _peer_conf [ ' PORT ' ]
2018-12-05 10:14:34 -05:00
#_ctable_peer['LAST_PING'] = _peer_conf['LAST_PING']
2018-12-02 14:33:45 -05:00
# SLOT 1&2 - for real-time montior: make the structure for later use
for ts in range ( 1 , 3 ) :
_ctable_peer [ ts ] = { }
_ctable_peer [ ts ] [ ' COLOR ' ] = ' '
2018-12-05 12:57:01 -05:00
_ctable_peer [ ts ] [ ' BGCOLOR ' ] = ' '
2018-12-02 14:33:45 -05:00
_ctable_peer [ ts ] [ ' TS ' ] = ' '
_ctable_peer [ ts ] [ ' TYPE ' ] = ' '
_ctable_peer [ ts ] [ ' SUB ' ] = ' '
_ctable_peer [ ts ] [ ' SRC ' ] = ' '
_ctable_peer [ ts ] [ ' DEST ' ] = ' '
2018-06-22 10:34:14 -04:00
# Build the HBlink connections table
2018-11-30 22:06:05 -05:00
def build_hblink_table ( _config , _stats_table ) :
2018-12-01 12:31:35 -05:00
for _hbp , _hbp_data in _config . iteritems ( ) :
2018-06-22 10:34:14 -04:00
if _hbp_data [ ' ENABLED ' ] == True :
2018-12-02 14:33:45 -05:00
# Process Master Systems
2018-06-22 10:34:14 -04:00
if _hbp_data [ ' MODE ' ] == ' MASTER ' :
_stats_table [ ' MASTERS ' ] [ _hbp ] = { }
2018-12-02 14:33:45 -05:00
if _hbp_data [ ' REPEAT ' ] :
_stats_table [ ' MASTERS ' ] [ _hbp ] [ ' REPEAT ' ] = " repeat "
else :
_stats_table [ ' MASTERS ' ] [ _hbp ] [ ' REPEAT ' ] = " isolate "
2018-10-29 09:18:44 -04:00
_stats_table [ ' MASTERS ' ] [ _hbp ] [ ' PEERS ' ] = { }
2018-12-02 14:33:45 -05:00
for _peer in _hbp_data [ ' PEERS ' ] :
add_hb_peer ( _hbp_data [ ' PEERS ' ] [ _peer ] , _stats_table [ ' MASTERS ' ] [ _hbp ] [ ' PEERS ' ] , _peer )
# Proccess Peer Systems
2018-10-29 11:43:09 -04:00
elif _hbp_data [ ' MODE ' ] == ' PEER ' :
2018-10-29 09:18:44 -04:00
_stats_table [ ' PEERS ' ] [ _hbp ] = { }
_stats_table [ ' PEERS ' ] [ _hbp ] [ ' CALLSIGN ' ] = _hbp_data [ ' CALLSIGN ' ]
2018-12-05 14:45:11 -05:00
_stats_table [ ' PEERS ' ] [ _hbp ] [ ' LOCATION ' ] = _hbp_data [ ' LOCATION ' ]
2018-10-29 09:18:44 -04:00
_stats_table [ ' PEERS ' ] [ _hbp ] [ ' RADIO_ID ' ] = int_id ( _hbp_data [ ' RADIO_ID ' ] )
_stats_table [ ' PEERS ' ] [ _hbp ] [ ' MASTER_IP ' ] = _hbp_data [ ' MASTER_IP ' ]
2018-12-05 14:45:11 -05:00
_stats_table [ ' PEERS ' ] [ _hbp ] [ ' MASTER_PORT ' ] = _hbp_data [ ' MASTER_PORT ' ]
2018-12-06 12:54:08 -05:00
_stats_table [ ' PEERS ' ] [ _hbp ] [ ' CONNECTION ' ] = ' yes ' #_hbp_data['CONNECTION']
_stats_table [ ' PEERS ' ] [ _hbp ] [ ' CONNECTED ' ] = time ( ) #since(_hbp_data['CONNECTED'])
2018-10-29 09:18:44 -04:00
_stats_table [ ' PEERS ' ] [ _hbp ] [ ' STATS ' ] = _hbp_data [ ' STATS ' ]
2018-12-06 12:51:07 -05:00
if _hbp_data [ ' SLOTS ' ] == 0 :
_stats_table [ ' SLOTS ' ] [ _hbp ] [ ' SLOTS ' ] = ' NONE '
elif _hbp_data [ ' SLOTS ' ] < = ' 2 ' :
_stats_table [ ' SLOTS ' ] [ _hbp ] [ ' SLOTS ' ] = _hbp_data [ ' SLOTS ' ]
elif _hbp_data [ ' SLOTS ' ] == ' 3 ' :
_stats_table [ ' SLOTS ' ] [ _hbp ] [ ' SLOTS ' ] = ' BOTH '
else :
_stats_table [ ' SLOTS ' ] [ _hbp ] [ ' SLOTS ' ] = ' DMO '
2018-12-02 14:33:45 -05:00
# Process OpenBridge systems
2018-10-29 11:43:09 -04:00
elif _hbp_data [ ' MODE ' ] == ' OPENBRIDGE ' :
_stats_table [ ' OPENBRIDGES ' ] [ _hbp ] = { }
_stats_table [ ' OPENBRIDGES ' ] [ _hbp ] [ ' NETWORK_ID ' ] = int_id ( _hbp_data [ ' NETWORK_ID ' ] )
_stats_table [ ' OPENBRIDGES ' ] [ _hbp ] [ ' TARGET_IP ' ] = _hbp_data [ ' TARGET_IP ' ]
_stats_table [ ' OPENBRIDGES ' ] [ _hbp ] [ ' TARGET_PORT ' ] = _hbp_data [ ' TARGET_PORT ' ]
2018-06-22 10:34:14 -04:00
return ( _stats_table )
2018-12-01 12:31:35 -05:00
2018-11-30 22:06:05 -05:00
def update_hblink_table ( _config , _stats_table ) :
2018-12-02 14:33:45 -05:00
logger . info ( ' running update table ' )
# Is there a system in HBlink's config monitor doesn't know about?
for _hbp in _config :
add_list = [ ]
if _config [ _hbp ] [ ' MODE ' ] == ' MASTER ' :
2018-12-02 18:03:09 -05:00
for _peer in _config [ _hbp ] [ ' PEERS ' ] :
2018-12-02 14:33:45 -05:00
if int_id ( _peer ) not in _stats_table [ ' MASTERS ' ] [ _hbp ] [ ' PEERS ' ] :
2018-12-02 18:03:09 -05:00
logger . info ( ' Adding peer to CTABLE that has registerred: %s ' , int_id ( _peer ) )
2018-12-02 14:33:45 -05:00
add_hb_peer ( _config [ _hbp ] [ ' PEERS ' ] [ _peer ] , _stats_table [ ' MASTERS ' ] [ _hbp ] [ ' PEERS ' ] , _peer )
# Is there a system in monitor that's been removed from HBlink's config?
for _hbp in _stats_table [ ' MASTERS ' ] :
remove_list = [ ]
if _config [ _hbp ] [ ' MODE ' ] == ' MASTER ' :
for _peer in _stats_table [ ' MASTERS ' ] [ _hbp ] [ ' PEERS ' ] :
if hex_str_4 ( _peer ) not in _config [ _hbp ] [ ' PEERS ' ] :
remove_list . append ( _peer )
2018-12-02 18:03:09 -05:00
2018-12-02 14:33:45 -05:00
for _peer in remove_list :
2018-12-02 18:03:09 -05:00
logger . info ( ' Deleting stats peer not in hblink config: %s ' , _peer )
2018-12-02 14:33:45 -05:00
del ( _stats_table [ ' MASTERS ' ] [ _hbp ] [ ' PEERS ' ] [ _peer ] )
2018-12-02 18:03:09 -05:00
2018-12-05 10:14:34 -05:00
# Update connection time
2018-12-02 18:03:09 -05:00
for _hbp in _stats_table [ ' MASTERS ' ] :
if _config [ _hbp ] [ ' MODE ' ] == ' MASTER ' :
for _peer in _stats_table [ ' MASTERS ' ] [ _hbp ] [ ' PEERS ' ] :
if hex_str_4 ( _peer ) in _config [ _hbp ] [ ' PEERS ' ] :
2018-12-05 10:14:34 -05:00
_stats_table [ ' MASTERS ' ] [ _hbp ] [ ' PEERS ' ] [ _peer ] [ ' CONNECTED ' ] = since ( _config [ _hbp ] [ ' PEERS ' ] [ hex_str_4 ( _peer ) ] [ ' CONNECTED ' ] )
2018-12-02 14:33:45 -05:00
build_stats ( )
2018-11-30 22:06:05 -05:00
2018-06-22 10:34:14 -04:00
#
# CONFBRIDGE TABLE FUNCTIONS
#
def build_bridge_table ( _bridges ) :
_stats_table = { }
_now = time ( )
_cnow = strftime ( ' % Y- % m- %d % H: % M: % S ' , localtime ( _now ) )
2018-12-01 12:31:35 -05:00
2018-06-22 10:34:14 -04:00
for _bridge , _bridge_data in _bridges . iteritems ( ) :
_stats_table [ _bridge ] = { }
for system in _bridges [ _bridge ] :
_stats_table [ _bridge ] [ system [ ' SYSTEM ' ] ] = { }
_stats_table [ _bridge ] [ system [ ' SYSTEM ' ] ] [ ' TS ' ] = system [ ' TS ' ]
_stats_table [ _bridge ] [ system [ ' SYSTEM ' ] ] [ ' TGID ' ] = int_id ( system [ ' TGID ' ] )
2018-12-01 12:31:35 -05:00
2018-06-22 10:34:14 -04:00
if system [ ' TO_TYPE ' ] == ' ON ' or system [ ' TO_TYPE ' ] == ' OFF ' :
if system [ ' TIMER ' ] - _now > 0 :
_stats_table [ _bridge ] [ system [ ' SYSTEM ' ] ] [ ' EXP_TIME ' ] = int ( system [ ' TIMER ' ] - _now )
else :
_stats_table [ _bridge ] [ system [ ' SYSTEM ' ] ] [ ' EXP_TIME ' ] = ' Expired '
if system [ ' TO_TYPE ' ] == ' ON ' :
_stats_table [ _bridge ] [ system [ ' SYSTEM ' ] ] [ ' TO_ACTION ' ] = ' Disconnect '
else :
_stats_table [ _bridge ] [ system [ ' SYSTEM ' ] ] [ ' TO_ACTION ' ] = ' Connect '
else :
_stats_table [ _bridge ] [ system [ ' SYSTEM ' ] ] [ ' EXP_TIME ' ] = ' N/A '
_stats_table [ _bridge ] [ system [ ' SYSTEM ' ] ] [ ' TO_ACTION ' ] = ' None '
2018-12-01 12:31:35 -05:00
2018-06-22 10:34:14 -04:00
if system [ ' ACTIVE ' ] == True :
_stats_table [ _bridge ] [ system [ ' SYSTEM ' ] ] [ ' ACTIVE ' ] = ' Connected '
_stats_table [ _bridge ] [ system [ ' SYSTEM ' ] ] [ ' COLOR ' ] = GREEN
2018-12-05 12:57:01 -05:00
_stats_table [ _bridge ] [ system [ ' SYSTEM ' ] ] [ ' BGCOLOR ' ] = BLACK
2018-06-22 10:34:14 -04:00
elif system [ ' ACTIVE ' ] == False :
_stats_table [ _bridge ] [ system [ ' SYSTEM ' ] ] [ ' ACTIVE ' ] = ' Disconnected '
_stats_table [ _bridge ] [ system [ ' SYSTEM ' ] ] [ ' COLOR ' ] = RED
2018-12-05 12:57:01 -05:00
_stats_table [ _bridge ] [ system [ ' SYSTEM ' ] ] [ ' BGCOLOR ' ] = WHITE
2018-12-01 12:31:35 -05:00
2018-06-22 10:34:14 -04:00
for i in range ( len ( system [ ' ON ' ] ) ) :
system [ ' ON ' ] [ i ] = str ( int_id ( system [ ' ON ' ] [ i ] ) )
2018-12-01 12:31:35 -05:00
2018-06-22 10:34:14 -04:00
_stats_table [ _bridge ] [ system [ ' SYSTEM ' ] ] [ ' TRIG_ON ' ] = ' , ' . join ( system [ ' ON ' ] )
2018-12-01 12:31:35 -05:00
2018-06-22 10:34:14 -04:00
for i in range ( len ( system [ ' OFF ' ] ) ) :
system [ ' OFF ' ] [ i ] = str ( int_id ( system [ ' OFF ' ] [ i ] ) )
2018-12-01 12:31:35 -05:00
2018-06-22 10:34:14 -04:00
_stats_table [ _bridge ] [ system [ ' SYSTEM ' ] ] [ ' TRIG_OFF ' ] = ' , ' . join ( system [ ' OFF ' ] )
return _stats_table
#
# BUILD HBlink AND CONFBRIDGE TABLES FROM CONFIG/BRIDGES DICTS
# THIS CURRENTLY IS A TIMED CALL
#
build_time = time ( )
def build_stats ( ) :
global build_time
now = time ( )
if True : #now > build_time + 1:
if CONFIG :
table = ' d ' + dtemplate . render ( _table = CTABLE )
dashboard_server . broadcast ( table )
if BRIDGES :
table = ' b ' + btemplate . render ( _table = BTABLE [ ' BRIDGES ' ] )
dashboard_server . broadcast ( table )
build_time = now
2018-12-02 18:03:09 -05:00
def rts_update ( p ) :
2018-11-30 19:50:30 -05:00
callType = p [ 0 ]
2018-12-05 12:57:01 -05:00
action = p [ 1 ]
trx = p [ 2 ]
system = p [ 3 ]
streamId = p [ 4 ]
sourcePeer = int ( p [ 5 ] )
sourceSub = int ( p [ 6 ] )
timeSlot = int ( p [ 7 ] )
destination = int ( p [ 8 ] )
2018-11-30 21:45:52 -05:00
if system in CTABLE [ ' MASTERS ' ] :
2018-12-05 12:57:01 -05:00
for peer in CTABLE [ ' MASTERS ' ] [ system ] [ ' PEERS ' ] :
if sourcePeer == peer :
color = ' 00ff00 '
bgcolor = ' 000000 '
else :
color = ' ff0000 '
bgcolor = ' ffffff '
#color, bgcolor = '00ff00', '000000' if sourcePeer == peer else 'ff0000', '000000'
2018-12-02 14:33:45 -05:00
if action == ' START ' :
2018-12-05 12:57:01 -05:00
CTABLE [ ' MASTERS ' ] [ system ] [ ' PEERS ' ] [ peer ] [ timeSlot ] [ ' TS ' ] = True
CTABLE [ ' MASTERS ' ] [ system ] [ ' PEERS ' ] [ peer ] [ timeSlot ] [ ' COLOR ' ] = color
CTABLE [ ' MASTERS ' ] [ system ] [ ' PEERS ' ] [ peer ] [ timeSlot ] [ ' BGCOLOR ' ] = bgcolor
CTABLE [ ' MASTERS ' ] [ system ] [ ' PEERS ' ] [ peer ] [ timeSlot ] [ ' TYPE ' ] = callType
CTABLE [ ' MASTERS ' ] [ system ] [ ' PEERS ' ] [ peer ] [ timeSlot ] [ ' SUB ' ] = sourceSub
CTABLE [ ' MASTERS ' ] [ system ] [ ' PEERS ' ] [ peer ] [ timeSlot ] [ ' SRC ' ] = peer
CTABLE [ ' MASTERS ' ] [ system ] [ ' PEERS ' ] [ peer ] [ timeSlot ] [ ' DEST ' ] = destination
2018-12-02 14:33:45 -05:00
if action == ' END ' :
2018-12-05 12:57:01 -05:00
CTABLE [ ' MASTERS ' ] [ system ] [ ' PEERS ' ] [ peer ] [ timeSlot ] [ ' TS ' ] = False
CTABLE [ ' MASTERS ' ] [ system ] [ ' PEERS ' ] [ peer ] [ timeSlot ] [ ' COLOR ' ] = ' ffffff '
CTABLE [ ' MASTERS ' ] [ system ] [ ' PEERS ' ] [ peer ] [ timeSlot ] [ ' BGCOLOR ' ] = ' 000000 '
CTABLE [ ' MASTERS ' ] [ system ] [ ' PEERS ' ] [ peer ] [ timeSlot ] [ ' TYPE ' ] = ' '
CTABLE [ ' MASTERS ' ] [ system ] [ ' PEERS ' ] [ peer ] [ timeSlot ] [ ' SUB ' ] = ' '
CTABLE [ ' MASTERS ' ] [ system ] [ ' PEERS ' ] [ peer ] [ timeSlot ] [ ' SRC ' ] = ' '
CTABLE [ ' MASTERS ' ] [ system ] [ ' PEERS ' ] [ peer ] [ timeSlot ] [ ' DEST ' ] = ' '
2018-12-05 14:45:11 -05:00
else :
logger . warning ( ' tried to update a tranmission for a peer not yet listed: system %s , action %s , callType %s , tx/rx %s , streamid %s , sourcePeer %s , sourceSub %s , timeSlot %s , destination %s ' , system , action , callType , trx , streamId , sourcePeer , sourceSub , timeSlot , destination )
if system in CTABLE [ ' OPENBRIDGES ' ] :
pass
if system in CTABLE [ ' PEERS ' ] :
pass
2018-12-05 12:57:01 -05:00
build_stats ( )
2018-12-05 14:45:11 -05:00
2018-06-22 10:34:14 -04:00
#
# PROCESS IN COMING MESSAGES AND TAKE THE CORRECT ACTION DEPENING ON THE OPCODE
#
def process_message ( _message ) :
global CTABLE , CONFIG , BRIDGES , CONFIG_RX , BRIDGES_RX
opcode = _message [ : 1 ]
_now = strftime ( ' % Y- % m- %d % H: % M: % S % Z ' , localtime ( time ( ) ) )
2018-12-01 12:31:35 -05:00
2018-06-22 10:34:14 -04:00
if opcode == OPCODE [ ' CONFIG_SND ' ] :
logging . debug ( ' got CONFIG_SND opcode ' )
CONFIG = load_dictionary ( _message )
CONFIG_RX = strftime ( ' % Y- % m- %d % H: % M: % S ' , localtime ( time ( ) ) )
2018-12-02 14:33:45 -05:00
if CTABLE [ ' MASTERS ' ] :
2018-11-30 22:06:05 -05:00
update_hblink_table ( CONFIG , CTABLE )
else :
build_hblink_table ( CONFIG , CTABLE )
2018-12-01 12:31:35 -05:00
2018-06-22 10:34:14 -04:00
elif opcode == OPCODE [ ' BRIDGE_SND ' ] :
logging . debug ( ' got BRIDGE_SND opcode ' )
BRIDGES = load_dictionary ( _message )
BRIDGES_RX = strftime ( ' % Y- % m- %d % H: % M: % S ' , localtime ( time ( ) ) )
BTABLE [ ' BRIDGES ' ] = build_bridge_table ( BRIDGES )
2018-12-01 12:31:35 -05:00
2018-06-22 10:34:14 -04:00
elif opcode == OPCODE [ ' LINK_EVENT ' ] :
logging . info ( ' LINK_EVENT Received: {} ' . format ( repr ( _message [ 1 : ] ) ) )
2018-12-01 12:31:35 -05:00
2018-06-22 10:34:14 -04:00
elif opcode == OPCODE [ ' BRDG_EVENT ' ] :
logging . info ( ' BRIDGE EVENT: {} ' . format ( repr ( _message [ 1 : ] ) ) )
p = _message [ 1 : ] . split ( " , " )
2018-12-02 18:03:09 -05:00
rts_update ( p )
2018-12-05 12:57:01 -05:00
if p [ 0 ] == ' GROUP VOICE ' and p [ 2 ] != ' TX ' :
2018-06-22 10:34:14 -04:00
if p [ 1 ] == ' END ' :
2018-12-05 12:57:01 -05:00
log_message = ' {} : {} {} : SYS: {:12.12s} SRC: {:8.8s} ; {:15.15s} TS: {} TGID: {:>5s} SUB: {:8.8s} ; {:30.30s} Time: {} s ' . format ( _now , p [ 0 ] , p [ 1 ] , p [ 3 ] , p [ 5 ] , alias_call ( int ( p [ 5 ] ) , peer_ids ) , p [ 7 ] , p [ 8 ] , p [ 6 ] , alias_short ( int ( p [ 6 ] ) , subscriber_ids ) , p [ 9 ] )
2018-06-22 10:34:14 -04:00
elif p [ 1 ] == ' START ' :
2018-12-05 12:57:01 -05:00
log_message = ' {} : {} {} : SYS: {:12.12s} SRC: {:8.8s} ; {:15.15s} TS: {} TGID: {:>5s} SUB: {:8.8s} ; {:30.30s} ' . format ( _now , p [ 0 ] , p [ 1 ] , p [ 3 ] , p [ 5 ] , alias_call ( int ( p [ 5 ] ) , peer_ids ) , p [ 7 ] , p [ 8 ] , p [ 6 ] , alias_short ( int ( p [ 6 ] ) , subscriber_ids ) )
2018-06-22 10:34:14 -04:00
elif p [ 1 ] == ' END WITHOUT MATCHING START ' :
2018-12-05 12:57:01 -05:00
log_message = ' {} : {} {} on SYSTEM {:12.12s} : SRC: {:8.8s} ; {} :15.15s TS: {} TGID: {:>5s} SUB: {:8.8s} ; {:30.30s} ' . format ( _now , p [ 0 ] , p [ 1 ] , p [ 3 ] , p [ 5 ] , alias_call ( int ( p [ 5 ] ) , peer_ids ) , p [ 7 ] , p [ 8 ] , p [ 6 ] , alias_short ( int ( p [ 6 ] ) , subscriber_ids ) )
2018-06-22 10:34:14 -04:00
else :
log_message = ' {} : UNKNOWN GROUP VOICE LOG MESSAGE ' . format ( _now )
2018-12-01 12:31:35 -05:00
2018-12-05 12:57:01 -05:00
dashboard_server . broadcast ( ' l ' + log_message )
LOGBUF . append ( log_message )
else :
logging . debug ( ' {} : UNKNOWN LOG MESSAGE ' . format ( _now ) )
2018-06-22 10:34:14 -04:00
else :
logging . debug ( ' got unknown opcode: {} , message: {} ' . format ( repr ( opcode ) , repr ( _message [ 1 : ] ) ) )
def load_dictionary ( _message ) :
data = _message [ 1 : ]
return loads ( data )
logging . debug ( ' Successfully decoded dictionary ' )
#
# COMMUNICATION WITH THE HBlink INSTANCE
#
class report ( NetstringReceiver ) :
def __init__ ( self ) :
pass
def connectionMade ( self ) :
pass
def connectionLost ( self , reason ) :
pass
2018-12-01 12:31:35 -05:00
2018-06-22 10:34:14 -04:00
def stringReceived ( self , data ) :
process_message ( data )
class reportClientFactory ( ReconnectingClientFactory ) :
def __init__ ( self ) :
2018-10-29 11:43:09 -04:00
logging . info ( ' reportClient object for connecting to HBlink.py created at: %s ' , self )
2018-12-01 12:31:35 -05:00
2018-06-22 10:34:14 -04:00
def startedConnecting ( self , connector ) :
logging . info ( ' Initiating Connection to Server. ' )
if ' dashboard_server ' in locals ( ) or ' dashboard_server ' in globals ( ) :
dashboard_server . broadcast ( ' q ' + ' Connection to HBlink Established ' )
def buildProtocol ( self , addr ) :
logging . info ( ' Connected. ' )
logging . info ( ' Resetting reconnection delay ' )
self . resetDelay ( )
return report ( )
def clientConnectionLost ( self , connector , reason ) :
logging . info ( ' Lost connection. Reason: %s ' , reason )
ReconnectingClientFactory . clientConnectionLost ( self , connector , reason )
dashboard_server . broadcast ( ' q ' + ' Connection to HBlink Lost ' )
def clientConnectionFailed ( self , connector , reason ) :
logging . info ( ' Connection failed. Reason: %s ' , reason )
ReconnectingClientFactory . clientConnectionFailed ( self , connector , reason )
#
# WEBSOCKET COMMUNICATION WITH THE DASHBOARD CLIENT
#
class dashboard ( WebSocketServerProtocol ) :
2018-12-01 12:31:35 -05:00
2018-06-22 10:34:14 -04:00
def onConnect ( self , request ) :
logging . info ( ' Client connecting: %s ' , request . peer )
def onOpen ( self ) :
logging . info ( ' WebSocket connection open. ' )
self . factory . register ( self )
self . sendMessage ( ' d ' + str ( dtemplate . render ( _table = CTABLE ) ) )
self . sendMessage ( ' b ' + str ( btemplate . render ( _table = BTABLE [ ' BRIDGES ' ] ) ) )
for _message in LOGBUF :
if _message :
self . sendMessage ( ' l ' + _message )
def onMessage ( self , payload , isBinary ) :
if isBinary :
logging . info ( ' Binary message received: %s bytes ' , len ( payload ) )
else :
logging . info ( ' Text message received: %s ' , payload . decode ( ' utf8 ' ) )
def connectionLost ( self , reason ) :
WebSocketServerProtocol . connectionLost ( self , reason )
self . factory . unregister ( self )
def onClose ( self , wasClean , code , reason ) :
logging . info ( ' WebSocket connection closed: %s ' , reason )
class dashboardFactory ( WebSocketServerFactory ) :
def __init__ ( self , url ) :
WebSocketServerFactory . __init__ ( self , url )
self . clients = [ ]
def register ( self , client ) :
if client not in self . clients :
logging . info ( ' registered client %s ' , client . peer )
self . clients . append ( client )
def unregister ( self , client ) :
if client in self . clients :
logging . info ( ' unregistered client %s ' , client . peer )
self . clients . remove ( client )
def broadcast ( self , msg ) :
logging . debug ( ' broadcasting message to: %s ' , self . clients )
for c in self . clients :
c . sendMessage ( msg . encode ( ' utf8 ' ) )
logging . debug ( ' message sent to %s ' , c . peer )
2018-12-01 12:31:35 -05:00
2018-06-22 10:34:14 -04:00
#
# STATIC WEBSERVER
#
class web_server ( Resource ) :
isLeaf = True
def render_GET ( self , request ) :
logging . info ( ' static website requested: %s ' , request )
if request . uri == ' / ' :
return index_html
else :
return ' Bad request '
if __name__ == ' __main__ ' :
2018-12-02 14:33:45 -05:00
logging . basicConfig (
level = logging . INFO ,
filename = ( LOG_PATH + LOG_NAME ) ,
filemode = ' a '
)
console = logging . StreamHandler ( )
console . setLevel ( logging . INFO )
logging . getLogger ( ' ' ) . addHandler ( console )
logger = logging . getLogger ( __name__ )
2018-06-22 10:34:14 -04:00
logging . info ( ' web_tables.py starting up ' )
2018-12-05 10:14:34 -05:00
logger . info ( ' \n \n Copyright (c) 2017, 2018 \n \t The Founding Members of the K0USY Group. All rights reserved. \n ' )
2018-12-01 12:31:35 -05:00
2018-06-22 10:34:14 -04:00
# Download alias files
result = try_download ( PATH , ' peer_ids.csv ' , PEER_URL , ( FILE_RELOAD * 86400 ) )
logging . info ( result )
2018-12-01 12:31:35 -05:00
2018-06-22 10:34:14 -04:00
result = try_download ( PATH , ' subscriber_ids.csv ' , SUBSCRIBER_URL , ( FILE_RELOAD * 86400 ) )
logging . info ( result )
2018-12-01 12:31:35 -05:00
2018-06-22 10:34:14 -04:00
# Make Alias Dictionaries
peer_ids = mk_full_id_dict ( PATH , PEER_FILE , ' peer ' )
if peer_ids :
logging . info ( ' ID ALIAS MAPPER: peer_ids dictionary is available ' )
2018-12-01 12:31:35 -05:00
2018-06-22 10:34:14 -04:00
subscriber_ids = mk_full_id_dict ( PATH , SUBSCRIBER_FILE , ' subscriber ' )
if subscriber_ids :
logging . info ( ' ID ALIAS MAPPER: subscriber_ids dictionary is available ' )
2018-12-01 12:31:35 -05:00
2018-06-22 10:34:14 -04:00
talkgroup_ids = mk_full_id_dict ( PATH , TGID_FILE , ' tgid ' )
if talkgroup_ids :
logging . info ( ' ID ALIAS MAPPER: talkgroup_ids dictionary is available ' )
2018-12-01 12:31:35 -05:00
2018-06-22 10:34:14 -04:00
local_subscriber_ids = mk_full_id_dict ( PATH , LOCAL_SUB_FILE , ' subscriber ' )
if local_subscriber_ids :
logging . info ( ' ID ALIAS MAPPER: local_subscriber_ids added to subscriber_ids dictionary ' )
subscriber_ids . update ( local_subscriber_ids )
2018-12-01 12:31:35 -05:00
2018-06-22 10:34:14 -04:00
local_peer_ids = mk_full_id_dict ( PATH , LOCAL_PEER_FILE , ' peer ' )
if local_peer_ids :
logging . info ( ' ID ALIAS MAPPER: local_peer_ids added peer_ids dictionary ' )
peer_ids . update ( local_peer_ids )
2018-12-01 12:31:35 -05:00
2018-06-22 10:34:14 -04:00
# Jinja2 Stuff
env = Environment (
loader = PackageLoader ( ' web_tables ' , ' templates ' ) ,
autoescape = select_autoescape ( [ ' html ' , ' xml ' ] )
)
dtemplate = env . get_template ( ' hblink_table.html ' )
btemplate = env . get_template ( ' bridge_table.html ' )
2018-12-01 12:31:35 -05:00
2018-06-22 10:34:14 -04:00
# Create Static Website index file
index_html = get_template ( PATH + ' index_template.html ' )
index_html = index_html . replace ( ' <<<system_name>>> ' , REPORT_NAME )
2018-12-01 12:31:35 -05:00
2018-06-22 10:34:14 -04:00
# Start update loop
update_stats = task . LoopingCall ( build_stats )
update_stats . start ( FREQUENCY )
2018-12-01 12:31:35 -05:00
2018-06-22 10:34:14 -04:00
# Connect to HBlink
reactor . connectTCP ( HBLINK_IP , HBLINK_PORT , reportClientFactory ( ) )
2018-12-01 12:31:35 -05:00
2018-06-22 10:34:14 -04:00
# Create websocket server to push content to clients
dashboard_server = dashboardFactory ( ' ws://*:9000 ' )
dashboard_server . protocol = dashboard
reactor . listenTCP ( 9000 , dashboard_server )
# Create static web server to push initial index.html
website = Site ( web_server ( ) )
reactor . listenTCP ( WEB_SERVER_PORT , website )
reactor . run ( )