@ -18,10 +18,11 @@ def parse_config_file(filename):
for port in config_data [ " ports " ] :
for port in config_data [ " ports " ] :
for device in port [ " devices " ] :
for device in port [ " devices " ] :
device_to_port [ device [ " slave_id " ] ] = port [ " path " ]
device_to_port [ device [ " slave_id " ] ] = port [ " path " ]
device_stats [ device [ " slave_id " ] ] = { " type " : device . get ( " device_type " , " Unknown type " ) , " errors " : 0 , " disconnects " : 0 , " write_failures " : 0 }
device_stats [ device [ " slave_id " ] ] = { " type " : device . get ( " device_type " , " Unknown type " ) , " errors " : 0 , " disconnects " : 0 , " write_failures " : 0 , " invalid_crc_errors " : 0 } # New line
return device_to_port , device_stats
return device_to_port , device_stats
def parse_journal ( device_to_port , device_stats , skip_lines = 10 , history = False ) :
def parse_journal ( device_to_port , device_stats , skip_lines = 10 , history = False ) :
cmd = [ " journalctl " , " -f " , " -u " , " wb-mqtt-serial " ] if not history else [ " journalctl " , " -u " , " wb-mqtt-serial " ]
cmd = [ " journalctl " , " -f " , " -u " , " wb-mqtt-serial " ] if not history else [ " journalctl " , " -u " , " wb-mqtt-serial " ]
p = subprocess . Popen ( cmd , stdout = subprocess . PIPE )
p = subprocess . Popen ( cmd , stdout = subprocess . PIPE )
@ -37,6 +38,7 @@ def parse_journal(device_to_port, device_stats, skip_lines=10, history=False):
match_error = re . search ( r ' modbus:( \ d+): Serial protocol error: request timed out ' , line )
match_error = re . search ( r ' modbus:( \ d+): Serial protocol error: request timed out ' , line )
match_disconnect = re . search ( r ' INFO: \ [serial device \ ] device modbus:( \ d+) is disconnected ' , line )
match_disconnect = re . search ( r ' INFO: \ [serial device \ ] device modbus:( \ d+) is disconnected ' , line )
match_write_failure = re . search ( r ' WARNING: \ [modbus \ ] failed to write: <modbus:( \ d+): ' , line )
match_write_failure = re . search ( r ' WARNING: \ [modbus \ ] failed to write: <modbus:( \ d+): ' , line )
match_invalid_crc = re . search ( r ' modbus:( \ d+): Serial protocol error: malformed response: invalid crc ' , line ) # New line
if match_error :
if match_error :
device_number = match_error . group ( 1 )
device_number = match_error . group ( 1 )
device_stats [ device_number ] [ " errors " ] + = 1
device_stats [ device_number ] [ " errors " ] + = 1
@ -46,6 +48,9 @@ def parse_journal(device_to_port, device_stats, skip_lines=10, history=False):
elif match_write_failure :
elif match_write_failure :
device_number = match_write_failure . group ( 1 )
device_number = match_write_failure . group ( 1 )
device_stats [ device_number ] [ " write_failures " ] + = 1
device_stats [ device_number ] [ " write_failures " ] + = 1
elif match_invalid_crc : # New line
device_number = match_invalid_crc . group ( 1 )
device_stats [ device_number ] [ " invalid_crc_errors " ] + = 1 # New line
last_log_line = line
last_log_line = line
@ -55,6 +60,7 @@ def parse_journal(device_to_port, device_stats, skip_lines=10, history=False):
print_error_statistics ( device_stats , device_to_port )
print_error_statistics ( device_stats , device_to_port )
def print_table ( headers , data ) :
def print_table ( headers , data ) :
col_widths = [
col_widths = [
max ( len ( str ( x ) ) for x in col )
max ( len ( str ( x ) ) for x in col )
@ -72,10 +78,9 @@ def print_table(headers, data):
def print_error_statistics ( device_stats , device_to_port ) :
def print_error_statistics ( device_stats , device_to_port ) :
print ( " \n --- Device Statistics --- " )
print ( " \n --- Error Statistics --- " )
sorted_device_stats = sorted ( device_stats . items ( ) , key = lambda x : ( x [ 1 ] [ " errors " ] , x [ 1 ] [ " disconnects " ] , x [ 1 ] [ " write_failures " ] ) , reverse = True )
sorted_device_stats = sorted ( device_stats . items ( ) , key = lambda x : ( x [ 1 ] [ " errors " ] , x [ 1 ] [ " disconnects " ] , x [ 1 ] [ " write_failures " ] , x [ 1 ] [ " invalid_crc_errors " ] ) , reverse = True )
headers = [ " Type " , " Port " , " ID " , " Timeouts " , " Disconnects " , " Write Failures " , " CRC Errors " ]
headers = [ " Type " , " Port " , " ID " , " Timeouts " , " Disconnects " , " Write Failures " ]
data = [ ]
data = [ ]
for device , stats in sorted_device_stats :
for device , stats in sorted_device_stats :
@ -84,13 +89,15 @@ def print_error_statistics(device_stats, device_to_port):
error_field = str ( stats [ ' errors ' ] )
error_field = str ( stats [ ' errors ' ] )
disconnect_field = str ( stats [ ' disconnects ' ] )
disconnect_field = str ( stats [ ' disconnects ' ] )
write_failure_field = str ( stats [ ' write_failures ' ] )
write_failure_field = str ( stats [ ' write_failures ' ] )
data . append ( [ type_field , device_port , device , error_field , disconnect_field , write_failure_field ] )
invalid_crc_errors_field = str ( stats [ ' invalid_crc_errors ' ] )
data . append ( [ type_field , device_port , device , error_field , disconnect_field , write_failure_field , invalid_crc_errors_field ] )
print_table ( headers , data )
print_table ( headers , data )
def parse_args ( ) :
def parse_args ( ) :
parser = argparse . ArgumentParser ( )
parser = argparse . ArgumentParser ( )
parser . add_argument ( " -H " , " --history " , help = " parse historical data from journal " , action = " store_true " )
parser . add_argument ( " -H " , " --history " , help = " parse historical data from journal " , action = " store_true " )