class LogFile: def __init__(self, prefix, version, sourceFile, vars=None, events=None, outputdir=None): if not vars: vars = [] if not events: events = [] self.variables = vars self.events = events self.prefix = prefix self.version = version self.source = sourceFile hfile = headerFileName(prefix) + '.h' cfile = headerFileName(prefix) + '.c' if outputdir: hfile = os.path.join(outputdir, hfile) cfile = os.path.join(outputdir, cfile) self.hFile = CodeWriter(hfile) self.cFile = CodeWriter(cfile) def constructCodeFile(self): self.cFile.clear() self.cFile.append(AutogenString(self.source)) #include the header file self.cFile.include('"{file}.h"'.format(file=headerFileName(self.prefix))) self.cFile.appendLine() #add in the global functions self.cFile.startComment() self.cFile.appendLine("Global functions") self.cFile.finishComment() self.cFile.appendLine() self.createResetFunction() self.createCopyAllToFunction() self.createCopyDataToFunction() self.createCopyAllFromFunction() self.createCopyDataFromFunction() self.getSelectionSizeFunction() self.cFile.appendLine() self.cFile.startComment() self.cFile.appendLine("Individual variable functions") self.cFile.finishComment() self.cFile.appendLine() #add in the functions to add variables for v in self.variables: self.createAdditionFunction(v) self.createDecodeFunction(v) self.titleByIndexFunction() self.unitsByIndexFunction() self.valueByIndexFunction() if len(self.events) > 0: self.cFile.appendLine() self.cFile.appendLine(comment='Functions to copy *events* to a buffer') for e in self.events: self.addEventCopyFuncs(e) self.cFile.appendLine(comment='Decode a {pref} event to a string'.format(pref=self.prefix)) self.eventsToStringFunction() self.cFile.appendLine() self.cFile.appendLine(comment='Functions for formatting individual events to a string') for e in self.events: self.eventToStringFunc(e) def constructHeaderFile(self): self.hFile.clear() self.hFile.append(AutogenString(self.source)) self.hFile.startIf(headerDefineName(self.prefix),invert=True) self.hFile.define(headerDefineName(self.prefix)) self.hFile.appendLine() self.hFile.include('"logjam_common.h"', comment='common LogJam routines') self.hFile.appendLine() self.hFile.externEntry() #version information self.hFile.appendLine() self.hFile.appendLine(comment='{pre} logging version'.format(pre=self.prefix)) self.hFile.define('LOG_{pref}_VERSION()'.format(pref=self.prefix),value='"{v}"'.format(v=self.version)) vMaj, vMin = self.version.split('.') self.hFile.define('LOG_{pref}_VERSION_MAJOR'.format(pref=self.prefix),value=vMaj) self.hFile.define('LOG_{pref}_VERSION_MINOR'.format(pref=self.prefix),value=vMin) self.hFile.appendLine() #create global enumeration for the variables fn = lambda i,val: "{val} is stored in byte {n}, position {m}".format(val=val,n=int(int(i)/8),m=i%8) self.hFile.createEnum('Log{pref}_VariableEnum_t'.format(pref=self.prefix),[v.getEnumString() for v in self.variables],split=8,commentFunc=fn) self.hFile.appendLine(comment='{n} bytes are required to store all parameter selction bits for {log} logging'.format(n=bitfieldSize(len(self.variables)),log=self.prefix)) self.hFile.define('LOG_{pref}_SELECTION_BYTES'.format(pref=self.prefix.upper()),value=bitfieldSize(len(self.variables))) self.hFile.appendLine() self.hFile.appendLine(comment="Number of variables defined for the '{pref}' logging structure".format(pref=self.prefix)) self.hFile.define('LOG_{pref}_VARIABLE_COUNT'.format(pref=self.prefix.upper()), value=len(self.variables)) self.hFile.appendLine() self.hFile.appendLine(comment="Struct definition for storing the selection bits of the " + self.prefix + " logging struct") self.hFile.appendLine(comment='This is not stored as a native c bitfield to preserve explicit ordering between processors, compilers, etc') self.hFile.appendLine('typedef uint8_t {name}[LOG_{pref}_SELECTION_BYTES];'.format(pref=self.prefix.upper(),name=bitfieldStructName(self.prefix))) self.hFile.appendLine() self.hFile.appendLine(comment="Data struct definition for the " + self.prefix + " logging struct") self.createDataStruct() #total data size d_size = sum([v.bytes for v in self.variables]) self.hFile.appendLine() self.hFile.appendLine(comment='{n} bytes are required to store all the data parameters'.format(n=d_size)) self.hFile.define('LOG_{pref}_DATA_BYTES'.format(pref=self.prefix.upper()),value=d_size) self.hFile.appendLine() #events if len(self.events) > 0: self.hFile.appendLine(comment='Logging event definitions for the {the}'.format(the=self.prefix)) self.hFile.appendLine(comment='Enumeration starts at 0x80 as *generic* events are 0x00 -> 0x7F') self.hFile.createEnum('Log{pref}_EventEnum_t'.format(pref=self.prefix),[e.getEnumString() for e in self.events],start="0x80") self.hFile.appendLine() self.hFile.appendLine(comment='Functions to copy various log events to logging buffer') self.hFile.appendLine(comment='Each function returns the number of bytes written to the log') self.hFile.appendLine(comment='Pointer is automatically incremented as required') #functions for the events for e in self.events: self.hFile.appendLine('inline uint8_t {func};'.format(func=e.eventPrototype())) self.hFile.appendLine(); self.hFile.appendLine(comment='Function to extract an event from a buffer, and format it as a human-readable string') self.hFile.appendLine(self.eventsToStringPrototype() + ';') self.hFile.appendLine() self.hFile.appendLine(comment='Functions for turning individual events into strings') for e in self.events: self.hFile.appendLine(e.toStringPrototype() + ';',comment='Format the {evt} event into a string'.format(evt=e.getEnumString())) self.hFile.appendLine() self.hFile.startComment() self.hFile.appendLine("Global Functions:") self.hFile.finishComment() self.hFile.appendLine(comment="Reset the bitfield of the logging structure") self.hFile.appendLine(self.resetPrototype() + ";") self.hFile.appendLine(comment='Copy *all* data from the logging structure') self.hFile.appendLine(self.copyAllPrototype() + ';') self.hFile.appendLine(comment="Copy *selected* data from the logging structure") self.hFile.appendLine(self.copySelectedPrototype() + ";") self.hFile.appendLine(comment='Copy all data back out from a buffer') self.hFile.appendLine(self.copyAllFromPrototype() + ';') self.hFile.appendLine(comment='Copy *selected* data back out from a buffer') self.hFile.appendLine(self.copyDataFromPrototype() + ';') self.hFile.appendLine(comment='Get the total size of the selected variables') self.hFile.appendLine(self.getSelectionSizePrototype() + ';') self.hFile.appendLine() self.hFile.appendLine(comment="Functions for getting variable information based on the index"); self.hFile.appendLine(self.titleByIndexPrototype()+';') self.hFile.appendLine(self.unitsByIndexPrototype()+';') self.hFile.appendLine(self.valueByIndexPrototype() + ';') self.hFile.appendLine() self.hFile.startComment() self.hFile.appendLine("Variable Functions:") self.hFile.appendLine("These functions are applied to individual variables within the logging structure") self.hFile.finishComment() #add in the 'addition' functions for var in self.variables: self.hFile.appendLine() self.hFile.appendLine(comment="Functions for the '{name}' variable".format(name=var.name)) self.hFile.define('Log{prefix}_{name}Title() {title}'.format( prefix=self.prefix, name=var.name, title=var.getTitleString()), comment='Title string for {var} variable'.format(var=var.name)) self.hFile.define('Log{prefix}_{name}Units() {units}'.format( prefix=self.prefix, name=var.name, units=var.getUnitsString()), comment='Units string for {var} variable'.format(var=var.name)) self.hFile.appendLine(self.additionPrototype(var) + '; //Add ' + var.name + " to the log struct") self.hFile.appendLine(self.decodePrototype(var) + '; //Decode ' + var.name + ' into a printable string') self.hFile.appendLine() self.hFile.externExit() self.hFile.endIf() def saveFiles(self): self.constructHeaderFile() self.constructCodeFile() self.hFile.writeToFile() self.cFile.writeToFile() def addEventCopyFuncs(self, e): #copy TO buffer self.cFile.appendLine('inline uint8_t {func}'.format(func=e.eventPrototype())) self.cFile.openBrace() #copy across the event type self.cFile.appendLine('*(*ptr++) = {evt};'.format(evt=e.getEnumString()),comment='Copy the event type to the buffer') if len(e.variables) > 0: self.cFile.appendLine() for v in e.variables: self.copyVarToBuffer(v,struct='',pointer='ptr') self.cFile.appendLine() self.cFile.appendLine('return {n};'.format(n=e.eventSize()),comment='Number of bytes copied') self.cFile.closeBrace() self.cFile.appendLine() #create the struct of the variables def createDataStruct(self): self.hFile.appendLine('typedef struct {') self.hFile.tabIn() for v in self.variables: self.hFile.appendLine(comment="Variable '{name}'{units}{scaler} ({n} bytes)".format( name = v.name, units = ", units='{u}'".format(u=v.units) if v.units else '', scaler = ", scaler=1.0/{scaler}".format(scaler=v.scaler) if v.scaler > 1 else '', n = v.bytes)) self.hFile.appendLine(v.dataString()) self.hFile.tabOut() self.hFile.appendLine('} ' + dataStructName(self.prefix) + ';') def additionPrototype(self,var): return self.createVariableFunction(var,'add',returnType='bool',extra=[('onlyIfNew','bool')]) #create the function for adding a variable to the logging structure def createAdditionFunction(self, var): self.cFile.appendLine(comment='Add variable {name} to the {prefix} logging struct'.format( name=var.name, prefix=self.prefix)) self.cFile.appendLine(self.additionPrototype(var)) self.cFile.openBrace() self.cFile.appendLine('if (onlyIfNew == true)',comment='Ignore value if it is the same as the value already stored') self.cFile.openBrace() self.cFile.appendLine('if (data->{var} == {var})'.format(var=var.name)) self.cFile.tabIn() self.cFile.appendLine('return false;') self.cFile.tabOut() self.cFile.closeBrace() self.cFile.appendLine() self.cFile.appendLine(var.setBit('selection'),comment='Set the appropriate bit') #now actually add the variable in self.cFile.appendLine(var.addVariable('data')) self.cFile.appendLine() self.cFile.appendLine('return true;') self.cFile.closeBrace() self.cFile.appendLine() #function for decoding a particular variable into a printable string for writing to a log file def decodePrototype(self, var): return self.createVariableFunction(var,'decode',blank=True,bits=False,returnType='void',extra=[('*str','char')]) def createDecodeFunction(self, var): self.cFile.appendLine(comment='Decode the {name} variable and return a printable string (e.g. for saving to a log file'.format(name=var.name)) self.cFile.appendLine(comment='Pointer to *str must have enough space allocated!') self.cFile.appendLine(self.decodePrototype(var)) self.cFile.openBrace() self.cFile.appendLine('sprintf(str,"{patt}",data->{var});'.format(patt=var.getStringCast(),var=var.name)) self.cFile.closeBrace() self.cFile.appendLine() pass #create a function pointing to a particular variable def createVariableFunction(self, var, name, blank=False, extra=None,ptr=False, **params): name = var.getFunctionName(name) if not extra: extra = [] if not blank: extra = [('{ptr}{name}'.format(ptr='*' if ptr else '',name=var.name),var.format)] + extra return self.createFunctionPrototype(name,extra=extra,**params) """ Create a function type of given NAME name - name of the function data - Include a pointer to the LogData_t struct? bits - Include a pointer to the LogBitfield_t struct? inline - Make the function inline? returnType - Function return type extra - Extra parameters to pass to the function - list of tuples """ def createFunctionPrototype(self, name, data=True, bits=True, inline=False, returnType='void', extra=None): if not extra: extra = [] #pass extra parameters to the function as such #params = {'*dest': 'void'} (name, type) paramstring = "" for pair in extra: paramstring += ', ' paramstring += pair[1] paramstring += ' ' paramstring += pair[0] return '{inline}{returnType} Log{prefix}_{name}({data}{comma}{bits}{params})'.format( inline='inline ' if inline else '', returnType=returnType, prefix=self.prefix.capitalize(), name=name, comma=', ' if data and bits else '', data=dataStructName(self.prefix) + " *data" if data else "", bits=bitfieldStructName(self.prefix) + " *selection" if bits else "", params=paramstring) def resetPrototype(self): return self.createFunctionPrototype('ResetSelection',data=False) #create a function to reset the logging structure def createResetFunction(self): #add the reset function to the c file self.cFile.appendLine(comment='Reset the log data struct (e.g. after writing to memory)') self.cFile.appendLine(comment='Only the selection bits need to be reset') self.cFile.appendLine(self.resetPrototype()) self.cFile.openBrace() self.cFile.appendLine('uint8_t *bf = (uint8_t*) selection;') for i in range(bitfieldSize(len(self.variables))): self.cFile.appendLine('bf[{n}] = 0; //Clear byte {x} of {y}'.format(n=i,x=i+1,y=bitfieldSize(len(self.variables)))) self.cFile.closeBrace() self.cFile.appendLine() """ Functions for copying data out of a struct and into a linear buffer """ def copyAllPrototype(self): return self.createFunctionPrototype('CopyAllToBuffer',bits=False,extra=[('*dest','void')]) #create a function to copy ALL parameters across, conserving data format def createCopyAllToFunction(self): self.cFile.appendLine(comment="Copy ALL data in the log struct to the provided address") self.cFile.appendLine(comment="Data will be copied even if the associated selection bit is cleared") self.cFile.appendLine(self.copyAllPrototype()) self.cFile.openBrace() self.cFile.appendLine('uint8_t *ptr = (uint8_t*) dest; //Pointer for keeping track of data addressing') self.cFile.appendLine() for var in self.variables: self.copyVarToBuffer(var) self.cFile.appendLine() self.cFile.closeBrace() self.cFile.appendLine() def copySelectedPrototype(self): return self.createFunctionPrototype('CopyDataToBuffer',extra=[('*dest','void')], returnType='uint16_t') #create a function that copies across ONLY the bits that are set def createCopyDataToFunction(self): self.cFile.appendLine(comment="Copy across data whose selection bit is set in the provided bitfield") self.cFile.appendLine(comment="Only data selected will be copied (in sequence)") self.cFile.appendLine(comment="Ensure a copy of the selection bits is stored for decoding") self.cFile.appendLine(self.copySelectedPrototype()) self.cFile.openBrace() self.cFile.appendLine('uint8_t *ptr = (uint8_t*) dest; //Pointer for keeping track of data addressing') self.cFile.appendLine('uint8_t *bf = (uint8_t*) selection; //Pointer for keeping track of the bitfield') self.cFile.appendLine('uint16_t count = 0; //Variable for keeping track of how many bytes were copied') self.cFile.appendLine() self.cFile.appendLine(comment='Copy the selection for keeping track of data') self.copyBitfieldToBuffer(count=True) self.cFile.appendLine() self.cFile.appendLine(comment='Check each variable in the logging struct to see if it should be added') for var in self.variables: self.cFile.appendLine('if ({test})'.format(test=var.getBit('selection'))) self.cFile.openBrace() self.copyVarToBuffer(var, count=True) self.cFile.closeBrace() self.cFile.appendLine() self.cFile.appendLine('return count; //Return the number of bytes that were actually copied') self.cFile.closeBrace() self.cFile.appendLine() def copyBitfieldToBuffer(self, count=False): #bitfield is called 'selection' locally #size of the bitfield bf_size = bitfieldSize(len(self.variables)) for i in range(bf_size): self.cFile.appendLine('*(ptr++) = bf[{i}];'.format(i = i)) if count: self.cFile.appendLine('count += {size};'.format(size=bf_size)) def copyBitfieldFromBuffer(self, count=False): bf_size = bitfieldSize(len(self.variables)) for i in range(bf_size): self.cFile.appendLine('bf[{i}] = *(ptr++);'.format(i=i)) if count: self.cFile.appendLine('count += {size};'.format(size=bf_size)) def copyVarToBuffer(self, var, struct='data->', pointer='&ptr', count=False): self.cFile.appendLine('Copy{sign}{bits}ToBuffer({struct}{name}, {ptr});'.format( sign='I' if var.isSigned() else 'U', bits=var.bytes*8, struct=struct, name=var.name, ptr = pointer), comment= "Copy the '{var}' variable ({n} bytes)".format(var=var.name,n=var.bytes)) if count: self.cFile.appendLine('count += {size};'.format(size=var.bytes)) def copyVarFromBuffer(self, var, struct='data->',pointer='&ptr',count=False): self.cFile.appendLine('Copy{sign}{bits}FromBuffer({struct}{name}, {ptr});'.format( sign='I' if var.isSigned() else 'U', bits=var.bytes*8, struct=struct, name=var.name, ptr = pointer, ), comment="Copy the '{var}' variable ({n} bytes)".format(var=var.name,n=var.bytes)) if count: self.cFile.appendLine('count += {size};'.format(size=var.bytes)) """ Functions for copying data back out of a buffer """ def copyAllFromPrototype(self): return self.createFunctionPrototype( 'CopyAllFromBuffer', bits = False, extra = [('*src','void')]) def createCopyAllFromFunction(self): self.cFile.appendLine(comment="Copy across *all* data from a buffer") self.cFile.appendLine(comment="Data will be copied even if it is invalid (selection bit is cleared)") self.cFile.appendLine(self.copyAllFromPrototype()) self.cFile.openBrace() self.cFile.appendLine('uint8_t *ptr = (uint8_t*) src; //Pointer for keeping track of data addressing') self.cFile.appendLine() for var in self.variables: self.copyVarFromBuffer(var) self.cFile.appendLine() self.cFile.closeBrace() self.cFile.appendLine() def copyDataFromPrototype(self): return self.createFunctionPrototype('CopyDataFromBuffer', returnType='uint16_t', extra = [('*src','void')]) def createCopyDataFromFunction(self): self.cFile.appendLine(comment="Copy across *selected* data from a buffer") self.cFile.appendLine(self.copyDataFromPrototype()) self.cFile.openBrace() self.cFile.appendLine('uint8_t *ptr = (uint8_t*) src; //Pointer for keeping track of data addressing') self.cFile.appendLine('uint8_t *bf = (uint8_t*) selection; //Pointer for keeping track of the bitfield') self.cFile.appendLine('uint16_t count = 0; //Variable for keeping track of how many bytes were copied') self.cFile.appendLine() self.cFile.appendLine(comment='Copy the selection bits') self.copyBitfieldFromBuffer(count=True) self.cFile.appendLine() self.cFile.appendLine(comment='Only copy across variables that have actually been stored in the buffer') for var in self.variables: self.cFile.appendLine('if ({test})'.format(test=var.getBit('selection'))) self.cFile.openBrace() self.copyVarFromBuffer(var,count=True) self.cFile.closeBrace() self.cFile.appendLine() self.cFile.appendLine('return count; //Return the number of bytes that were actually copied') self.cFile.closeBrace() self.cFile.appendLine() #enumerate through all the varibles in the struct, perform 'function' for each def createCaseEnumeration(self, vars=None, blankFunction=None, returnFunction=None): if not vars: vars = self.variables for var in vars: self.cFile.addCase(var.getEnumString()) if blankFunction: blank = blankFunction(var) if not blank.endswith(';'): blank += ';' self.cFile.appendLine(blank) if returnFunction: self.cFile.returnFromCase(returnFunction(var)) else: self.cFile.breakFromCase() def titleByIndexPrototype(self): return 'char* Log{pref}_GetTitleByIndex(uint8_t index)'.format(pref=self.prefix) def titleByIndexFunction(self): self.cFile.appendLine(comment='Get the title of a variable based on its enumerated value') self.cFile.appendLine(self.titleByIndexPrototype()) self.cFile.openBrace() self.cFile.startSwitch('index') #add case labels #function to return the index fn = lambda var: 'Log{prefix}_{var}Title()'.format(prefix=self.prefix,var=var.name) self.createCaseEnumeration(returnFunction = fn) self.cFile.endSwitch() self.cFile.appendLine(comment='Default return value') self.cFile.appendLine('return "";') self.cFile.closeBrace() self.cFile.appendLine() def unitsByIndexPrototype(self): return 'char* Log{pref}_GetUnitsByIndex(uint8_t index)'.format(pref=self.prefix) def unitsByIndexFunction(self): self.cFile.appendLine(comment='Get the units of a variable based on its enumerated value') self.cFile.appendLine(self.unitsByIndexPrototype()) self.cFile.openBrace() self.cFile.startSwitch('index') fn = lambda var: 'Log{prefix}_{var}Units()'.format(prefix=self.prefix,var=var.name) self.createCaseEnumeration(returnFunction = fn) self.cFile.endSwitch() self.cFile.appendLine(comment='Default return value') self.cFile.appendLine('return "";') self.cFile.closeBrace() self.cFile.appendLine() def valueByIndexPrototype(self): return self.createFunctionPrototype('GetValueByIndex',bits=False,extra=[('index','uint8_t'), ('*str','char')]) def valueByIndexFunction(self): self.cFile.appendLine(comment='Get a string-representation of a given variable, based on its enumerated value') self.cFile.appendLine(self.valueByIndexPrototype()) self.cFile.openBrace() self.cFile.startSwitch('index') fn = lambda var: 'Log{prefix}_Decode{name}(data,str)'.format(prefix=self.prefix.capitalize(),name=var.name) self.createCaseEnumeration(blankFunction=fn) self.cFile.endSwitch() self.cFile.closeBrace() self.cFile.appendLine() #function to turn an event into a string #pass a pointer to where the event data starts #pointer will be auto-incremented #returns 'true' if an event was extracted, else false def eventsToStringPrototype(self): return 'bool Log{pref}_EventToString(uint8_t **ptr, char *str)'.format(pref=self.prefix) def eventsToStringFunction(self): self.cFile.startComment() self.cFile.appendLine('Extract an event from a buffer, given a pointer to the buffer, and a pointer to where the event will be strung') self.cFile.appendLine('Function will auto-increment the pointer as necessary') self.cFile.appendLine('Returns true if event was extracted and formatted as string, else returns false') self.cFile.finishComment() self.cFile.appendLine(self.eventsToStringPrototype()) self.cFile.openBrace() self.cFile.appendLine() self.cFile.appendLine('#error this needs to be completed') self.cFile.startSwitch('TBD') #function for formatting a given even to a string fn = lambda var: 'Log{pref}_EventToString_{name}(ptr,str)'.format(pref=var.prefix,name=var.name) self.createCaseEnumeration(vars = self.events, blankFunction = fn) self.cFile.addCase('default') self.cFile.returnFromCase(value='false') self.cFile.endSwitch() self.cFile.appendLine('return true;',comment='Default return case') self.cFile.closeBrace() #func for formatting an individual func to a string def eventToStringFunc(self, evt): self.cFile.appendLine(comment='Format a {evt} event into a readable string'.format(evt=evt.name)) self.cFile.appendLine(comment='Auto-increment the **ptr pointer') self.cFile.appendLine(evt.toStringPrototype()) self.cFile.openBrace() #define vars for this event for v in evt.variables: #local var for temp storage of data self.cFile.appendLine('{fmt} {name};'.format(fmt=v.format,name=v.name),comment="Temporary storage for '{var}' variable".format(var=v.name)) self.cFile.appendLine() if len(evt.variables) > 0: self.cFile.appendLine(comment='Copy the event variables from the buffer') for v in evt.variables: self.copyVarFromBuffer(v,struct='&',pointer='ptr') self.cFile.appendLine() #compile a list of variables associated with this event fmts = " ".join([v.getStringCast() for v in evt.variables]) vars = ", ".join([v.name for v in evt.variables]) self.cFile.appendLine('sprintf(str,"Event: {evt}{sep}{formats}"{comma}{vars});'.format( evt = evt.getEnumString(), sep = ' -> ' if len(fmts) > 0 else '', formats = fmts, comma = ', ' if len(vars) > 0 else '', vars = vars)) self.cFile.closeBrace() self.cFile.appendLine() #function to determine the size of the selected data def getSelectionSizePrototype(self): return self.createFunctionPrototype('GetSelectionSize',data=False,returnType='uint16_t') def getSelectionSizeFunction(self): self.cFile.appendLine(comment='Get the total size of the selected variables') self.cFile.appendLine(self.getSelectionSizePrototype()) self.cFile.openBrace() self.cFile.appendLine('uint16_t size = 0;') self.cFile.appendLine() for var in self.variables: self.cFile.appendLine('if ({test})'.format(test=var.getBit('selection'))) self.cFile.openBrace() self.cFile.appendLine('size += {n};'.format(n=var.bytes)) self.cFile.closeBrace() self.cFile.appendLine() self.cFile.appendLine('return size;') self.cFile.closeBrace() self.cFile.appendLine()