Client Fully Connects with BrandMeister

Thanks to Colin Durbridge G4EML for figuring out MANY of the
BrandMeister changes to HB Protocol (some not documented).
This commit is contained in:
Cort Buffington 2016-07-23 15:53:22 -05:00
parent a8dc160bb4
commit addc9cb52c
4 changed files with 69 additions and 83 deletions

View File

@ -23,7 +23,8 @@ def build_config(_config_file):
# Process GLOBAL items in the configuration
CONFIG['GLOBAL'].update({
'PATH': config.get(section, 'PATH')
'PATH': config.get(section, 'PATH'),
'PING_TIME': config.getint(section, 'PING_TIME')
})
elif section == 'LOGGER':
@ -49,13 +50,14 @@ def build_config(_config_file):
'RADIO_ID': hex(int(config.get(section, 'RADIO_ID')))[2:].rjust(8,'0').decode('hex'),
'RX_FREQ': config.get(section, 'RX_FREQ').rjust(9),
'TX_FREQ': config.get(section, 'TX_FREQ').rjust(9),
'TX_POWER': config.get(section, 'TX_POWER').rjust(2),
'COLORCODE': config.get(section, 'COLORCODE').rjust(2),
'TX_POWER': config.get(section, 'TX_POWER').rjust(2,'0'),
'COLORCODE': config.get(section, 'COLORCODE').rjust(2,'0'),
'LATITUDE': config.get(section, 'LATITUDE').rjust(8),
'LONGITUDE': config.get(section, 'LONGITUDE').rjust(9),
'HEIGHT': config.get(section, 'HEIGHT').rjust(3),
'HEIGHT': config.get(section, 'HEIGHT').rjust(3,'0'),
'LOCATION': config.get(section, 'LOCATION').rjust(20),
'DESCRIPTION': config.get(section, 'DESCRIPTION').rjust(20),
'DESCRIPTION': config.get(section, 'DESCRIPTION').rjust(19),
'SLOTS': config.get(section, 'SLOTS'),
'URL': config.get(section, 'URL').rjust(124),
'SOFTWARE_ID': config.get(section, 'SOFTWARE_ID').rjust(40),
'PACKAGE_ID': config.get(section, 'PACKAGE_ID').rjust(40)

View File

@ -1,21 +0,0 @@
# Copyright (c) 2016 Cortney T. Buffington, N0MJS and the K0USY Group. n0mjs@me.com
#
# This work is licensed under the Creative Attribution-NonCommercial-ShareAlike
# 3.0 Unported License.To view a copy of this license, visit
# http://creativecommons.org/licenses/by-nc-sa/3.0/ or send a letter to
# Creative Commons, 444 Castro Street, Suite 900, Mountain View,
# California, 94041, USA.
# Known HomeBrew Repeater Message Types
# In message below, "ID" is taken to mean the 4-byte HEX repeater ID string of the repaeter(DMR Radio ID)
RPTL = 'RPTL' # Initial LOGIN, RPTL+ID
MSTNAK = 'MSTNAK' # Master negatvive ack, MSTNAK+ID
MSTACK = 'MSTACK' # Master acknowledgement, MSTACK+ID
# if in response to a login, MSTACK+ID+(random 32-bit integer (as a string))
RPTK = 'RPTK' # See explantation elsewhere about passphrase, ID and SHA-256 hash!
MSTPING = 'MSTPING' # From the repeater, MSTPING+ID
RPTPONG = 'RPTPONG' # From the master, MSTPONG+ID
MSTCL = 'MSTCL' # From the master, MSTCL+ID indicates close-down of the master
RPTCL = 'RPTCL' # From the repeater, RPTCL+ID indicates close-down of the repeater
RPTC = 'RPTC' # From the repeater, information packet about the repeater
DMRD = 'DMRD' # DMR data, format documented elsewhere

View File

@ -1,12 +1,25 @@
# PROGRAM-WIDE PARAMETERS GO HERE
# PATH - working path for files, leave it alone unless you NEED to change it
# PING_TIME - the interval that clients will ping the master, and re-try registraion
[GLOBAL]
PATH: ./
PING_TIME
# LOGGING CONFIGURATION
# Should be self explanatory. See Python logging module for more information
# Several convenient handlers have been pre-configured, check out the module
# hb_log.py to see them
[LOGGER]
LOG_FILE: /tmp/hblink.log
LOG_HANDLERS: console-timed
LOG_LEVEL: DEBUG
LOG_NAME: HBlink
# MASTER INSTANCES - DUPLICATE SECTION FOR MULTIPLE CLIENTS
# HomeBrew Protocol Master instances go here.
# IP may be left blank if there's one interface on your system.
# Port should be the port you want this master to listen on. It must be unique
# and unused by anything else.
[MASTER-1]
MODE: MASTER
ENABLED: True
@ -14,13 +27,13 @@ IP:
PORT: 54000
PASSPHRASE: s3cr37w0rd
[MASTER-2]
MODE: MASTER
ENABLED: False
IP:
PORT: 55000
PASSPHRASE: 13370p3r470r
# CLIENT INSTANCES - DUPLICATE SECTION FOR MULTIPLE CLIENTS
# There are a LOT of errors in the HB Protocol specifications on this one!
# MOST of these items are just strings and will be properly dealt with by the program
# The TX & RX Frequencies are 9-digit numbers, and are the frequency in Hz.
# Latitude is an 8-digit unsigned floating point number.
# Longitude is a 9-digit signed floating point number.
# Height is in meters
[REPEATER-1]
MODE: CLIENT
ENABLED: True
@ -31,38 +44,15 @@ MASTER_PORT: 54000
PASSPHRASE: homebrew
CALLSIGN: W1ABC
RADIO_ID: 312000
RX_FREQ: 449.000000
TX_FREQ: 444.000000
RX_FREQ: 449000000
TX_FREQ: 444000000
TX_POWER: 25
ColorCode: 1
LATITUDE: 38.00000
LONGITUDE: -95.00000
LATITUDE: 038.0000
LONGITUDE: -095.0000
HEIGHT: 75
LOCATION: Anywhere, USA
DESCRIPTION: This is a cool repeater
URL: www.w1abc.org
SOFTWARE_ID: HBlink v1.0
PACKAGE_ID: HBlink v1.0
[REPEATER-2]
MODE: CLIENT
ENABLED: False
IP:
PORT: 54002
MASTER_IP: 172.16.1.1
MASTER_PORT: 54000
PASSPHRASE: homebrew
CALLSIGN: K9ABC
RADIO_ID: 312001
RX_FREQ: 448.000000
TX_FREQ: 443.000000
TX_POWER: 40
ColorCode: 1
LATITUDE: 38.10000
LONGITUDE: -95.10000
HEIGHT: 50
LOCATION: Somewhere, USA
DESCRIPTION: This is a cooler repeater
URL: www.k9abc.org
SOFTWARE_ID: HBlink v1.0
PACKAGE_ID: HBlink v1.0
SOFTWARE_ID: HBlink
PACKAGE_ID: v0.1

View File

@ -12,6 +12,7 @@ from __future__ import print_function
import argparse
import sys
import os
import signal
# Specifig functions from modules we need
from binascii import b2a_hex as h
@ -31,12 +32,11 @@ from twisted.internet import task
# Other files we pull from -- this is mostly for readability and segmentation
import hb_log
import hb_config
from hb_message_types import *
# Does anybody read this stuff? There's a PEP somewhere that says I should do this.
__author__ = 'Cortney T. Buffington, N0MJS'
__copyright__ = 'Copyright (c) 2013 - 2016 Cortney T. Buffington, N0MJS and the K0USY Group'
__credits__ = 'Steve Zingman, N4IRS; Mike Zingman, N4IRR; Jonathan Naylor, G4KLX; Hans Barthen, DL5DI; Torsten Shultze, DG1HT'
__credits__ = 'Colin Durbridge, G4EML, Steve Zingman, N4IRS; Mike Zingman, N4IRR; Jonathan Naylor, G4KLX; Hans Barthen, DL5DI; Torsten Shultze, DG1HT'
__license__ = 'Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported'
__maintainer__ = 'Cort Buffington, N0MJS'
__email__ = 'n0mjs@me.com'
@ -69,7 +69,20 @@ if cli_args.LOG_LEVEL:
logger = hb_log.config_logging(CONFIG['LOGGER'])
logger.debug('Logging system started, anything from here on gets logged')
# Shut ourselves down gracefully by disconnecting from the masters and clients.
def handler(_signal, _frame):
logger.info('*** HBLINK IS TERMINATING WITH SIGNAL %s ***', str(_signal))
for client in clients:
this_client = clients[client]
this_client.send_packet('RPTCL'+CONFIG['CLIENTS'][client]['RADIO_ID'])
logger.info('(%s) De-Registering From the Master', client)
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)
#************************************************
# HERE ARE THE IMPORTANT PARTS
@ -93,41 +106,39 @@ class HBCLIENT(DatagramProtocol):
else:
# If we didn't get called correctly, log it!
logger.error('(%s) HBCLIENT was not called with an argument. Terminating', self._client)
sys.exit()
def send_packet(self, _packet):
print('did this')
self.transport.write(_packet, (self._config['MASTER_IP'], self._config['MASTER_PORT']))
# KEEP THE FOLLOWING COMMENTED OUT UNLESS YOU'RE DEBUGGING DEEPLY!!!!
logger.debug('(%s) TX Packet to %s on port %s: %s', self._client, self._config['MASTER_IP'], self._config['MASTER_PORT'], h(_packet))
sys.exit()
def startProtocol(self):
# Set up periodic loop for sending pings to the master. Run every minute
self._peer_maintenance = task.LoopingCall(self.peer_maintenance_loop)
self._peer_maintenance_loop = self._peer_maintenance.start(10)
self._peer_maintenance_loop = self._peer_maintenance.start(CONFIG['GLOBAL']['PING_TIME'])
def peer_maintenance_loop(self):
if self._stats['CONNECTION'] == 'NO':
self.send_packet(RPTL+self._config['RADIO_ID'])
self._stats['PINGS_SENT'] = 0
self._stats['PINGS_ACKD'] = 0
self._stats['CONNECTION'] = 'RTPL_SENT'
self.send_packet('RPTL'+self._config['RADIO_ID'])
logger.debug('(%s) Sending login request to master', self._client)
if self._stats['CONNECTION'] == 'YES':
self.send_packet('RPTPING'+self._config['RADIO_ID'])
logger.debug('(%s) Ping Sent to Master', self._client)
self._stats['PINGS_SENT'] += 1
logger.info('(%s) RPTPING Sent to Master. Total Pings Since Connected: %s', self._client, self._stats['PINGS_SENT'])
def send_packet(self, _packet):
self.transport.write(_packet, (self._config['MASTER_IP'], self._config['MASTER_PORT']))
# KEEP THE FOLLOWING COMMENTED OUT UNLESS YOU'RE DEBUGGING DEEPLY!!!!
logger.debug('(%s) TX Packet to %s on port %s: %s', self._client, self._config['MASTER_IP'], self._config['MASTER_PORT'], h(_packet))
#logger.debug('(%s) TX Packet to %s on port %s: %s', self._client, self._config['MASTER_IP'], self._config['MASTER_PORT'], h(_packet))
def datagramReceived(self, _data, (_host, _port)):
_command = _data[:4]
if _command == 'DMRD': # DMRData -- encapsulated DMR data frame
print('DMRD Received')
logger.debug('(%s) DMRD Received', self._client)
elif _command == 'MSTN': # Actually MSTNAK -- a NACK from the master
print('MSTNAC Received')
print('(%s) MSTNAC Received', self._client)
self._stats['CONNECTION'] = 'NO'
elif _command == 'RPTA': # Actually RPTACK -- an ACK from the master
if self._stats['CONNECTION'] == 'RTPL_SENT':
@ -141,7 +152,7 @@ class HBCLIENT(DatagramProtocol):
elif self._stats['CONNECTION'] == 'AUTHENTICATED':
if _data[6:10] == self._config['RADIO_ID']:
logger.info('(%s) Repeater Authentication Accepted', self._client)
_config_packet = str(int(h(self._config['RADIO_ID']), 16)).rjust(8)+\
_config_packet = self._config['RADIO_ID']+\
self._config['CALLSIGN']+\
self._config['RX_FREQ']+\
self._config['TX_FREQ']+\
@ -152,30 +163,34 @@ class HBCLIENT(DatagramProtocol):
self._config['HEIGHT']+\
self._config['LOCATION']+\
self._config['DESCRIPTION']+\
self._config['SLOTS']+\
self._config['URL']+\
self._config['SOFTWARE_ID']+\
self._config['PACKAGE_ID']
self.send_packet('RPTC'+_config_packet)
print(len('RPTC'+_config_packet))
self._stats['CONNECTION'] = 'CONFIG-SENT'
logger.info('(%s) Repeater Configuration Sent', self._client)
elif self._stats['CONNECTION'] == 'CONFIG-SENT':
if _data[6:10] == self._config['RADIO_ID']:
logger.info('(%s) Repeater Configuration Accepted', self._client)
self._stats['CONNECTION'] = 'YES'
logger.info('(%s) Connection to Master Completed', self._client)
elif _command == 'MSTP': # Actually MSTPONG -- a reply to RPTPING (send by client)
print('MSTPONG Received')
self._stats['PINGS_ACKD'] += 1
logger.info('(%s) MSTPONG Received. Total Pongs Since Connected: %s', self._client, self._stats['PINGS_ACKD'])
elif _command == 'MSTC': # Actually MSTCL -- notify the master this client is closing
print('MSTCL Recieved')
elif _command == 'MSTC': # Actually MSTCL -- notify us the master is closing down
self._stats['CONNECTION'] = 'NO'
logger.info('(%s) MSTCL Recieved', self._client)
else:
logger.error('(%s) Received an invalid command in packet: %s', self._client, h(_data))
print('Received Packet:', h(_data))
#print('Received Packet:', h(_data))
#************************************************