2021-10-04 16:41:16 +00:00
import numpy as np
from datetime import datetime
import time
2021-10-04 16:53:08 +00:00
import argparse
2024-07-15 17:24:19 +00:00
import os . path
2021-10-04 16:53:08 +00:00
parser = argparse . ArgumentParser ( description = ' Copys, renames and fixes logfiles written by bobbycar sd logger. ' )
2024-07-15 17:24:19 +00:00
parser . add_argument ( ' -i ' , ' --input ' , type = argparse . FileType ( ' r ' ) , nargs = ' + ' , required = True , help = " list of input log files " )
parser . add_argument ( ' -o ' , ' --output ' , nargs = ' ? ' , type = argparse . FileType ( ' w ' ) , help = " output filename " )
parser . add_argument ( ' -c ' , ' --consecutive ' , action = " store_true " , help = " add consecutive files to input. If the input file ends with a number the following logfiles will be added. " )
2021-10-04 16:53:08 +00:00
args = parser . parse_args ( )
2021-10-04 16:41:16 +00:00
2024-07-15 17:24:19 +00:00
2021-10-04 16:41:16 +00:00
ok = True
2023-07-12 18:17:27 +00:00
def getTimestamp ( plines ) :
timestampline = - 1
timestampfound = False
while not timestampfound :
timestampline + = 1
timestampfound = ( plines [ timestampline ] . find ( ' TIMESTAMP: ' ) != - 1 )
timestamp = int ( plines [ timestampline ] . split ( ' TIMESTAMP: ' ) [ 1 ] ) #timestamp when file was created
if ( timestampline == - 1 ) :
print ( " Error: Timestamp not found! " )
exit ( )
return timestamp
def filterLines ( plines , plinesStarttime = None ) :
plines = [ x . rstrip ( " \n " ) for x in plines ] #remove \n
pcommentlinesMask = [ True if x . startswith ( ' # ' ) else False for x in plines ] #generate mask for lines with comments
plines = np . array ( plines )
pcommentlinesMask = np . array ( pcommentlinesMask )
if ( plinesStarttime is not None ) : #if starttimelist given, match with pdatalinesOK
plinesStarttime = plinesStarttime [ pcommentlinesMask == False ] #get lines with data
pdatalines = plines [ pcommentlinesMask == False ] #get lines with data
pheader = pdatalines [ 0 ] #header is the first non comment line
pheaderSize = len ( pheader . split ( ' , ' ) ) #how many elements are expected per line
pdatalinesSize = [ len ( x . split ( ' , ' ) ) for x in pdatalines ] #count arraysize for every dataline
if ( plinesStarttime is not None ) : #if starttimelist given, match with pdatalinesOK
plinesStarttime = plinesStarttime [ np . array ( pdatalinesSize ) == pheaderSize ]
pdatalinesOK = pdatalines [ np . array ( pdatalinesSize ) == pheaderSize ]
if ( plinesStarttime is not None ) : #if starttimelist given, match with pdatalinesOK
plinesStarttime = [ plinesStarttime [ i ] for i , x in enumerate ( pdatalinesOK ) if x != pheader ]
pdatalinesOK = [ x for x in pdatalinesOK if x != pheader ] #exclude header from data lines
pdatalinesFail = pdatalines [ np . array ( pdatalinesSize ) != pheaderSize ]
plinesSize = [ len ( x . split ( ' , ' ) ) for x in plines ] #count arraysize for every dataline
plinesOK = np . array ( plinesSize ) == pheaderSize #mask for okay lines (valid for data lines)
return plines , pheader , pcommentlinesMask , pdatalines , pdatalinesFail , pdatalinesOK , pheaderSize , plinesOK , plinesStarttime
2024-07-15 17:24:19 +00:00
inputFilenames = [ ]
if ( args . consecutive ) :
if ( len ( args . input ) != 1 ) :
parser . error ( " in consequtive mode exactly one input file is required " )
exit ( )
nextFilename = args . input [ 0 ] . name
while os . path . isfile ( nextFilename ) :
print ( nextFilename + " exists " )
inputFilenames . append ( nextFilename )
digitStartpos = len ( nextFilename ) - 1
digitEndpos = len ( nextFilename )
while ( not nextFilename [ digitStartpos : digitEndpos ] . isdigit ( ) and digitStartpos > 0 and digitEndpos > 0 ) :
digitStartpos - = 1
digitEndpos - = 1
while ( nextFilename [ digitStartpos : digitEndpos ] . isdigit ( ) and digitStartpos > 0 and digitEndpos > 0 ) :
digitStartpos - = 1
digitStartpos + = 1
number = int ( nextFilename [ digitStartpos : digitEndpos ] ) + 1
nextFilename = nextFilename [ 0 : digitStartpos ] + str ( number ) . zfill ( digitEndpos - digitStartpos ) + nextFilename [ digitEndpos : ]
print ( " " )
print ( inputFilenames )
else :
inputFilenames = [ x . name for x in args . input ]
2023-07-12 18:17:27 +00:00
2021-10-04 16:53:08 +00:00
outputFilename = None
if args . output is not None :
outputFilename = args . output . name
2021-10-04 16:41:16 +00:00
2023-07-12 18:17:27 +00:00
lines = [ ]
linesStarttime = [ ] #offset for every line with timestamp. will be combined to new column
header = " "
for inputFilename in inputFilenames :
print ( " Reading " + str ( inputFilename ) )
inputlines = [ ]
with open ( inputFilename , ' r ' ) as reader :
inputlines = reader . readlines ( )
2021-10-04 16:41:16 +00:00
2023-07-12 18:17:27 +00:00
lines + = inputlines
#Check Headers
_lines , _header , _ , _ , _ , _ , _ , _ , _ = filterLines ( inputlines )
if ( header == " " ) : #is first header
header = _header
2021-10-04 16:41:16 +00:00
2023-07-12 18:17:27 +00:00
assert header == _header , " Header is different! "
2021-10-04 16:41:16 +00:00
2023-07-12 18:17:27 +00:00
_timestamp = getTimestamp ( _lines )
print ( " Timestamp= " + str ( _timestamp ) )
_linesStarttime = [ _timestamp for x in inputlines ] #create as many entries with start timestamp as there are lines in the current file
2021-10-04 16:41:16 +00:00
2023-07-12 18:17:27 +00:00
linesStarttime + = _linesStarttime
print ( " Line in file= " + str ( len ( inputlines ) ) )
2021-10-04 16:41:16 +00:00
2023-07-12 18:17:27 +00:00
assert len ( lines ) == len ( linesStarttime ) , " Length of lines and linesStarttime does not match "
linesStarttime = np . array ( linesStarttime )
lines , header , commentlinesMask , datalines , datalinesFail , datalinesOK , headerSize , linesOK , linesStarttime = filterLines ( lines , linesStarttime )
2021-10-04 16:41:16 +00:00
print ( " Found " + str ( len ( lines ) ) + " lines " )
2023-07-12 16:39:46 +00:00
print ( str ( np . sum ( commentlinesMask ) ) + " comments " )
2021-10-04 16:41:16 +00:00
print ( str ( len ( datalinesFail ) ) + " Datalines Failed " )
print ( str ( len ( datalinesOK ) ) + " Datalines OK " )
print ( " Header Size is " + str ( headerSize ) )
2023-07-12 18:17:27 +00:00
timestamp = getTimestamp ( lines )
2021-10-04 16:41:16 +00:00
filetime = time . strftime ( ' % Y % m %d _ % H % M % S ' , time . localtime ( timestamp ) )
2023-07-12 18:17:27 +00:00
2021-10-04 16:53:08 +00:00
if outputFilename is None :
outputFilename = filetime + " .csv "
2021-10-04 16:41:16 +00:00
#is_dst(datetime(2019, 4, 1), timezone="US/Pacific")
print ( " Timestamp: " + str ( timestamp ) + " -> " + str ( filetime ) )
print ( " UTC: " + datetime . utcfromtimestamp ( timestamp ) . strftime ( ' % A, % Y- % m- %d % H: % M: % S ' ) )
print ( " Local Time: " + time . strftime ( ' % A, % Y- % m- %d % H: % M: % S ' , time . localtime ( timestamp ) ) )
print ( " Writing to: " + str ( outputFilename ) )
2023-07-12 16:39:46 +00:00
print ( " Size lines= " + str ( len ( lines ) ) )
print ( " Size commentlinesMask= " + str ( len ( commentlinesMask ) ) )
print ( " Size datalines= " + str ( len ( datalines ) ) )
print ( " Size linesOK= " + str ( len ( linesOK ) ) )
2023-07-12 18:17:27 +00:00
header = " timestamp, " + header #add timestamp column
writelines = [ str ( linesStarttime [ i ] + float ( x . split ( ' , ' ) [ 0 ] ) ) + " , " + x for i , x in enumerate ( datalinesOK ) ] #add file timestamp to line time and add column to data
2023-07-12 16:39:46 +00:00
2021-10-04 16:41:16 +00:00
linesWritten = 0
2021-10-04 16:53:08 +00:00
if ok :
with open ( outputFilename , ' w ' ) as writer :
2023-07-12 18:17:27 +00:00
writer . write ( header + " \n " ) #write header
for i , line in enumerate ( writelines ) :
2023-07-12 16:39:46 +00:00
writer . write ( line + " \n " )
linesWritten + = 1
2021-10-04 16:53:08 +00:00
print ( str ( linesWritten ) + " lines written to " + str ( outputFilename ) )
else :
print ( " Failed! " )
2021-10-04 16:41:16 +00:00