Merge remote-tracking branch 'origin/modularization' into modularization
# Conflicts: # dmrlink.py # dmrlink_config.py
This commit is contained in:
commit
23354574d1
114
dmr_utils.py
Executable file
114
dmr_utils.py
Executable file
@ -0,0 +1,114 @@
|
||||
#!/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
|
||||
|
||||
import os
|
||||
|
||||
from time import time
|
||||
from urllib import URLopener
|
||||
from csv import reader as csv_reader
|
||||
from binascii import b2a_hex as ahex
|
||||
|
||||
# Does anybody read this stuff? There's a PEP somewhere that says I should do this.
|
||||
__author__ = 'Cortney T. Buffington, N0MJS'
|
||||
__copyright__ = 'Copyright (c) 2016 Cortney T. Buffington, N0MJS and the K0USY Group'
|
||||
__credits__ = 'Colin Durbridge, G4EML, Steve Zingman, N4IRS; Mike Zingman'
|
||||
__license__ = 'GNU GPLv3'
|
||||
__maintainer__ = 'Cort Buffington, N0MJS'
|
||||
__email__ = 'n0mjs@me.com'
|
||||
|
||||
#************************************************
|
||||
# STRING UTILITY FUNCTIONS
|
||||
#************************************************
|
||||
|
||||
# Create a 2 byte hex string from an integer
|
||||
def hex_str_2(_int_id):
|
||||
try:
|
||||
return format(_int_id,'x').rjust(4,'0').decode('hex')
|
||||
except TypeError:
|
||||
raise
|
||||
|
||||
# Create a 3 byte hex string from an integer
|
||||
def hex_str_3(_int_id):
|
||||
try:
|
||||
return format(_int_id,'x').rjust(6,'0').decode('hex')
|
||||
except TypeError:
|
||||
raise
|
||||
|
||||
# Create a 4 byte hex string from an integer
|
||||
def hex_str_4(_int_id):
|
||||
try:
|
||||
return format(_int_id,'x').rjust(8,'0').decode('hex')
|
||||
except TypeError:
|
||||
raise
|
||||
|
||||
# Convert a hex string to an int (radio ID, etc.)
|
||||
def int_id(_hex_string):
|
||||
return int(ahex(_hex_string), 16)
|
||||
|
||||
|
||||
#************************************************
|
||||
# ID ALIAS FUNCTIONS
|
||||
#************************************************
|
||||
|
||||
# Download and build dictionaries for mapping number to aliases
|
||||
# Used by applications. These lookups take time, please do not shove them
|
||||
# into this file everywhere and send a pull request!!!
|
||||
# Download a new file if it doesn't exist, or is older than the stale time
|
||||
def try_download(_path, _file, _url, _stale,):
|
||||
now = time()
|
||||
url = URLopener()
|
||||
file_exists = os.path.isfile(_path+_file) == True
|
||||
if file_exists:
|
||||
file_old = (os.path.getmtime(_path+_file) + _stale) < now
|
||||
if not file_exists or (file_exists and file_old):
|
||||
try:
|
||||
url.retrieve(_url, _path+_file)
|
||||
result = 'ID ALIAS MAPPER: \'{}\' successfully downloaded'.format(_file)
|
||||
except IOError:
|
||||
result = 'ID ALIAS MAPPER: \'{}\' could not be downloaded'.format(_file)
|
||||
else:
|
||||
result = 'ID ALIAS MAPPER: \'{}\' is current, not downloaded'.format(_file)
|
||||
url.close()
|
||||
return result
|
||||
|
||||
def mk_id_dict(_path, _file):
|
||||
dict = {}
|
||||
try:
|
||||
with open(_path+_file, 'rU') as _handle:
|
||||
ids = csv_reader(_handle, dialect='excel', delimiter=',')
|
||||
for row in ids:
|
||||
dict[int(row[0])] = (row[1])
|
||||
_handle.close
|
||||
return dict
|
||||
except IOError:
|
||||
return dict
|
||||
|
||||
def get_info(_id, _dict):
|
||||
if _id in _dict:
|
||||
return _dict[_id]
|
||||
return _id
|
||||
|
||||
def get_alias(_id, _dict):
|
||||
_int_id = int_id(_id)
|
||||
if _int_id in _dict:
|
||||
return _dict[_int_id]
|
||||
return _int_id
|
165
dmrlink.py
165
dmrlink.py
@ -48,11 +48,16 @@ from twisted.internet.protocol import DatagramProtocol
|
||||
from twisted.internet import reactor
|
||||
from twisted.internet import task
|
||||
|
||||
<<<<<<< HEAD
|
||||
from ipsc.ipsc_const import *
|
||||
from ipsc.ipsc_mask import *
|
||||
from dmrlink_config import build_config
|
||||
from dmrlink_log import config_logging
|
||||
|
||||
=======
|
||||
from dmr_utils import hex_str_2, hex_str_3, hex_str_4, int_id
|
||||
from dmrlink_config import build_config
|
||||
>>>>>>> origin/modularization
|
||||
|
||||
__author__ = 'Cortney T. Buffington, N0MJS'
|
||||
__copyright__ = 'Copyright (c) 2013 - 2016 Cortney T. Buffington, N0MJS and the K0USY Group'
|
||||
@ -61,6 +66,7 @@ __license__ = 'GNU GPLv3'
|
||||
__maintainer__ = 'Cort Buffington, N0MJS'
|
||||
__email__ = 'n0mjs@me.com'
|
||||
|
||||
<<<<<<< HEAD
|
||||
# Global variables used whether we are a module or __main__
|
||||
systems = {}
|
||||
|
||||
@ -92,6 +98,95 @@ def config_reporting_loop(_type):
|
||||
def reporting_loop():
|
||||
logger.debug('Periodic Reporting Loop Started (NULL)')
|
||||
|
||||
=======
|
||||
# Global variables for all class instances
|
||||
systems = {}
|
||||
|
||||
#************************************************
|
||||
# CONFIGURE THE SYSTEM LOGGER
|
||||
#************************************************
|
||||
|
||||
def config_logging(_logger):
|
||||
dictConfig({
|
||||
'version': 1,
|
||||
'disable_existing_loggers': False,
|
||||
'filters': {
|
||||
},
|
||||
'formatters': {
|
||||
'verbose': {
|
||||
'format': '%(levelname)s %(asctime)s %(module)s %(process)d %(thread)d %(message)s'
|
||||
},
|
||||
'timed': {
|
||||
'format': '%(levelname)s %(asctime)s %(message)s'
|
||||
},
|
||||
'simple': {
|
||||
'format': '%(levelname)s %(message)s'
|
||||
},
|
||||
'syslog': {
|
||||
'format': '%(name)s (%(process)d): %(levelname)s %(message)s'
|
||||
}
|
||||
},
|
||||
'handlers': {
|
||||
'null': {
|
||||
'class': 'logging.NullHandler'
|
||||
},
|
||||
'console': {
|
||||
'class': 'logging.StreamHandler',
|
||||
'formatter': 'simple'
|
||||
},
|
||||
'console-timed': {
|
||||
'class': 'logging.StreamHandler',
|
||||
'formatter': 'timed'
|
||||
},
|
||||
'file': {
|
||||
'class': 'logging.FileHandler',
|
||||
'formatter': 'simple',
|
||||
'filename': _logger['LOG_FILE'],
|
||||
},
|
||||
'file-timed': {
|
||||
'class': 'logging.FileHandler',
|
||||
'formatter': 'timed',
|
||||
'filename': _logger['LOG_FILE'],
|
||||
},
|
||||
'syslog': {
|
||||
'class': 'logging.handlers.SysLogHandler',
|
||||
'formatter': 'syslog',
|
||||
}
|
||||
},
|
||||
'loggers': {
|
||||
_logger['LOG_NAME']: {
|
||||
'handlers': _logger['LOG_HANDLERS'].split(','),
|
||||
'level': _logger['LOG_LEVEL'],
|
||||
'propagate': True,
|
||||
}
|
||||
}
|
||||
})
|
||||
return logging.getLogger(_logger['LOG_NAME'])
|
||||
|
||||
|
||||
#************************************************
|
||||
# IMPORTING OTHER FILES - '#include'
|
||||
#************************************************
|
||||
|
||||
# Import IPSC message types and version information
|
||||
#
|
||||
try:
|
||||
from ipsc.ipsc_const import *
|
||||
except ImportError:
|
||||
sys.exit('IPSC message types file not found or invalid')
|
||||
|
||||
# Import IPSC flag mask values
|
||||
#
|
||||
try:
|
||||
from ipsc.ipsc_mask import *
|
||||
except ImportError:
|
||||
sys.exit('IPSC mask values file not found or invalid')
|
||||
|
||||
|
||||
#************************************************
|
||||
# UTILITY FUNCTIONS FOR INTERNAL USE
|
||||
#************************************************
|
||||
>>>>>>> origin/modularization
|
||||
|
||||
# Determine if the provided peer ID is valid for the provided network
|
||||
#
|
||||
@ -321,6 +416,56 @@ def print_master(_network):
|
||||
print('\t\tStatus: {}, KeepAlives Sent: {}, KeepAlives Outstanding: {}, KeepAlives Missed: {}' .format(_master['STATUS']['CONNECTED'], _master['STATUS']['KEEP_ALIVES_SENT'], _master['STATUS']['KEEP_ALIVES_OUTSTANDING'], _master['STATUS']['KEEP_ALIVES_MISSED']))
|
||||
print('\t\t KeepAlives Received: {}, Last KeepAlive Received at: {}' .format(_master['STATUS']['KEEP_ALIVES_RECEIVED'], _master['STATUS']['KEEP_ALIVE_RX_TIME']))
|
||||
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
|
||||
# Timed loop used for reporting IPSC status
|
||||
#
|
||||
# REPORT BASED ON THE TYPE SELECTED IN THE MAIN CONFIG FILE
|
||||
def config_reports(_config):
|
||||
global reporting_loop
|
||||
|
||||
if _config['REPORTS']['REPORT_NETWORKS'] == 'PICKLE':
|
||||
def reporting_loop():
|
||||
logger.debug('Periodic Reporting Loop Started (PICKLE)')
|
||||
try:
|
||||
with open(_config['REPORTS']['REPORT_PATH']+'dmrlink_stats.pickle', 'wb') as file:
|
||||
pickle_dump(_config['SYSTEMS'], file, 2)
|
||||
file.close()
|
||||
except IOError as detail:
|
||||
logger.error('I/O Error: %s', detail)
|
||||
|
||||
elif _config['REPORTS']['REPORT_NETWORKS'] == 'PRINT':
|
||||
def reporting_loop():
|
||||
logger.debug('Periodic Reporting Loop Started (PRINT)')
|
||||
for system in _config['SYSTEMS']:
|
||||
print_master(system)
|
||||
print_peer_list(system)
|
||||
|
||||
else:
|
||||
def reporting_loop():
|
||||
logger.debug('Periodic Reporting Loop Started (NULL)')
|
||||
|
||||
|
||||
# Shut ourselves down gracefully with the IPSC peers.
|
||||
#
|
||||
def handler(_signal, _frame):
|
||||
logger.info('*** DMRLINK IS TERMINATING WITH SIGNAL %s ***', str(_signal))
|
||||
|
||||
for system in systems:
|
||||
this_ipsc = systems[system]
|
||||
logger.info('De-Registering from IPSC %s', system)
|
||||
de_reg_req_pkt = this_ipsc.hashed_packet(this_ipsc._local['AUTH_KEY'], this_ipsc.DE_REG_REQ_PKT)
|
||||
this_ipsc.send_to_ipsc(de_reg_req_pkt)
|
||||
|
||||
reactor.stop()
|
||||
|
||||
# Set signal handers so that we can gracefully exit if need be
|
||||
for sig in [signal.SIGTERM, signal.SIGINT, signal.SIGQUIT]:
|
||||
signal.signal(sig, handler)
|
||||
|
||||
|
||||
>>>>>>> origin/modularization
|
||||
|
||||
#************************************************
|
||||
# IPSC CLASS
|
||||
@ -816,11 +961,13 @@ class IPSC(DatagramProtocol):
|
||||
return
|
||||
return
|
||||
|
||||
|
||||
# MOTOROLA XCMP/XNL CONTROL PROTOCOL: We don't process these (yet)
|
||||
elif _packettype == XCMP_XNL:
|
||||
self.xcmp_xnl(self._network, data)
|
||||
return
|
||||
|
||||
|
||||
# ORIGINATED BY PEERS, NOT IPSC MAINTENANCE: Call monitoring is all we've found here so far
|
||||
elif _packettype == CALL_MON_STATUS:
|
||||
self.call_mon_status(self._network, data)
|
||||
@ -834,6 +981,7 @@ class IPSC(DatagramProtocol):
|
||||
self.call_mon_nack(self._network, data)
|
||||
return
|
||||
|
||||
|
||||
# IPSC CONNECTION MAINTENANCE MESSAGES
|
||||
elif _packettype == DE_REG_REQ:
|
||||
de_register_peer(self._network, _peerid)
|
||||
@ -850,9 +998,8 @@ class IPSC(DatagramProtocol):
|
||||
return
|
||||
return
|
||||
|
||||
#
|
||||
|
||||
# THE FOLLOWING PACKETS ARE RECEIVED ONLY IF WE ARE OPERATING AS A PEER
|
||||
#
|
||||
|
||||
# ONLY ACCEPT FROM A PREVIOUSLY VALIDATED PEER
|
||||
if _packettype in PEER_REQUIRED:
|
||||
@ -898,7 +1045,6 @@ class IPSC(DatagramProtocol):
|
||||
return
|
||||
return
|
||||
|
||||
|
||||
# THIS MEANS WE HAVE SUCCESSFULLY REGISTERED TO OUR MASTER - RECORD MASTER INFORMATION
|
||||
elif _packettype == MASTER_REG_REPLY:
|
||||
self.master_reg_reply(data, _peerid)
|
||||
@ -906,7 +1052,6 @@ class IPSC(DatagramProtocol):
|
||||
|
||||
|
||||
# THE FOLLOWING PACKETS ARE RECEIVED ONLLY IF WE ARE OPERATING AS A MASTER
|
||||
|
||||
# REQUESTS FROM PEERS: WE MUST REPLY IMMEDIATELY FOR IPSC MAINTENANCE
|
||||
|
||||
# REQUEST TO REGISTER TO THE IPSC
|
||||
@ -924,7 +1069,10 @@ class IPSC(DatagramProtocol):
|
||||
self.peer_list_req(_peerid)
|
||||
return
|
||||
|
||||
<<<<<<< HEAD
|
||||
|
||||
=======
|
||||
>>>>>>> origin/modularization
|
||||
# PACKET IS OF AN UNKNOWN TYPE. LOG IT AND IDENTTIFY IT!
|
||||
else:
|
||||
self.unknown_message(self._network, _packettype, _peerid, data)
|
||||
@ -949,11 +1097,20 @@ if __name__ == '__main__':
|
||||
if not cli_args.CFG_FILE:
|
||||
cli_args.CFG_FILE = os.path.dirname(os.path.abspath(__file__))+'/dmrlink.cfg'
|
||||
|
||||
<<<<<<< HEAD
|
||||
# Call the external routine to build the configuration dictionary
|
||||
CONFIG = build_config(cli_args.CFG_FILE)
|
||||
|
||||
# Call the external routing to start the system logger
|
||||
logger = config_logging(CONFIG['LOGGER'])
|
||||
=======
|
||||
|
||||
CONFIG = build_config(cli_args.CFG_FILE)
|
||||
logger = config_logging(CONFIG['LOGGER'])
|
||||
config_reports(CONFIG)
|
||||
|
||||
|
||||
>>>>>>> origin/modularization
|
||||
logger.info('DMRlink \'dmrlink.py\' (c) 2013 - 2015 N0MJS & the K0USY Group - SYSTEM STARTING...')
|
||||
|
||||
# Shut ourselves down gracefully with the IPSC peers.
|
||||
|
@ -23,6 +23,7 @@ import sys
|
||||
|
||||
from socket import gethostbyname
|
||||
|
||||
<<<<<<< HEAD
|
||||
# Does anybody read this stuff? There's a PEP somewhere that says I should do this.
|
||||
__author__ = 'Cortney T. Buffington, N0MJS'
|
||||
__copyright__ = 'Copyright (c) 2016 Cortney T. Buffington, N0MJS and the K0USY Group'
|
||||
@ -30,6 +31,14 @@ __license__ = 'GNU GPLv3'
|
||||
__maintainer__ = 'Cort Buffington, N0MJS'
|
||||
__email__ = 'n0mjs@me.com'
|
||||
|
||||
=======
|
||||
__author__ = 'Cortney T. Buffington, N0MJS'
|
||||
__copyright__ = 'Copyright (c) 2016 Cortney T. Buffington, N0MJS and the K0USY Group'
|
||||
__credits__ = 'Adam Fast, KC0YLK; Dave Kierzkowski, KD8EYF; Steve Zingman, N4IRS; Mike Zingman, N4IRR'
|
||||
__license__ = 'GNU GPLv3'
|
||||
__maintainer__ = 'Cort Buffington, N0MJS'
|
||||
__email__ = 'n0mjs@me.com'
|
||||
>>>>>>> origin/modularization
|
||||
|
||||
def build_config(_config_file):
|
||||
config = ConfigParser.ConfigParser()
|
||||
@ -178,6 +187,7 @@ def build_config(_config_file):
|
||||
return CONFIG
|
||||
|
||||
|
||||
<<<<<<< HEAD
|
||||
# Used to run this file direclty and print the config,
|
||||
# which might be useful for debugging
|
||||
if __name__ == '__main__':
|
||||
@ -201,3 +211,7 @@ if __name__ == '__main__':
|
||||
|
||||
|
||||
pprint(build_config(cli_args.CONFIG_FILE))
|
||||
=======
|
||||
if __name__ == '__main__':
|
||||
pass
|
||||
>>>>>>> origin/modularization
|
||||
|
Loading…
Reference in New Issue
Block a user