#!/usr/bin/env python # Script Context Driver Generator # Author: Douglas Clowes (douglas.clowes@ansto.gov.au) Jan/Feb 2014 # vim: ft=python ts=8 sts=2 sw=2 expandtab autoindent smartindent # # This program generates Script Context Driver TCL files. # # It takes one or more "Script Context Driver Description" files. # Each file may contain one or more driver descriptions. Each driver # description will result in one TCL file. # # The name of the file produced, the TCL namespace used and names within # the file are based on the driver name givin in the driver description. # # TODO: # implement attributes and units on vars # - type part ??? # - nxalias xxxxx # - sdsinfo # fix code for driving # - make each node driveable # - use [sct] in place of ${tc_root} # - associate sensor and call ansto_makesctdrive # check simulation works # handle environmental monitoring (emon) # - figure out how to do it # - make nodes monitorable # - make it conditional # handle the driving settling time in checkstatus # Questions: # what it the 'plain spy none' on hfactory # what is 'mugger' vs 'manager' - seems like alias? # should some hset commands be hupdate commands? # import os import ply.lex as lex import ply.yacc as yacc global Verbose global DriverDump global CodeDump global UsingCreateNode global FunctionTypes global DriveableFunctionTypes global NumberOfLinesIn global NumberOfLinesOut FunctionTypes = [ 'read_function', 'write_function', 'fetch_function', 'check_function', 'checkrange_function', ] DriveableFunctionTypes = [ 'halt_function', 'checklimits_function', 'checkstatus_function', ] UsingCreateNode = False Verbose = False DriverDump = False CodeDump = False # # Tokenizer: This recognizes the tokens which can be keywords, identifiers, # numbers or strings plus the punctuation. # # # Reserved words (keywords) in the form reserved['KEYWORD'] = 'TOKEN' # reserved = { # Driver keywords 'DRIVER' : 'DRIVER', 'VENDOR' : 'VENDOR', 'DEVICE' : 'DEVICE', 'PROTOCOL' : 'PROTOCOL', 'DRIVER_PROPERTY' : 'DRIVER_PROPERTY', 'CLASS' : 'CLASS', 'SIMULATION_GROUP' : 'SIMULATION_GROUP', 'CODE' : 'CODE', 'ADD_ARGS' : 'ADD_ARGS', 'MAKE_ARGS' : 'MAKE_ARGS', 'PROTOCOL_ARGS' : 'PROTOCOL_ARGS', 'USECREATENODE' : 'USECREATENODE', # Group keywords 'GROUP' : 'GROUP', 'GROUP_PROPERTY' : 'GROUP_PROPERTY', # Variable keywords 'VAR' : 'VAR', 'PROPERTY' : 'PROPERTY', 'CONTROL' : 'CONTROL', 'DATA' : 'DATA', 'NXSAVE' : 'NXSAVE', 'MUTABLE' : 'MUTABLE', 'READABLE' : 'READABLE', 'WRITEABLE' : 'WRITEABLE', 'DRIVEABLE' : 'DRIVEABLE', 'TRUE' : 'TRUE', 'FALSE' : 'FALSE', # Data Types 'TYPE' : 'TYPE', 'FLOAT' : 'FLOAT', 'INT' : 'INT', 'TEXT' : 'TEXT', 'NONE' : 'NONE', # Privilege levels 'PRIV' : 'PRIV', 'SPY' : 'SPY', 'USER' : 'USER', 'MANAGER' : 'MANAGER', 'READONLY' : 'READONLY', 'INTERNAL' : 'INTERNAL', # Functions and Commands 'READ_COMMAND' : 'READ_COMMAND', 'READ_FUNCTION' : 'READ_FUNCTION', 'FETCH_FUNCTION' : 'FETCH_FUNCTION', 'WRITE_COMMAND' : 'WRITE_COMMAND', 'WRITE_FUNCTION' : 'WRITE_FUNCTION', 'CHECK_FUNCTION' : 'CHECK_FUNCTION', 'CHECKRANGE_FUNCTION' : 'CHECKRANGE_FUNCTION', 'CHECKLIMITS_FUNCTION' : 'CHECKLIMITS_FUNCTION', 'CHECKSTATUS_FUNCTION' : 'CHECKSTATUS_FUNCTION', 'HALT_FUNCTION' : 'HALT_FUNCTION', # Value setting 'VALUE' : 'VALUE', 'ALLOWED' : 'ALLOWED', 'LOWERLIMIT' : 'LOWERLIMIT', 'UPPERLIMIT' : 'UPPERLIMIT', 'TOLERANCE' : 'TOLERANCE', 'UNITS' : 'UNITS', } # # Tokens list with keyword tokens added at the end # tokens = [ 'LBRACE', 'RBRACE', 'SLASH', 'INTEGER', 'FLOATER', 'CODE_STRING', 'TEXT_STRING', 'EQUALS', 'ID', ] + list(reserved.values()) # # Token rules # t_EQUALS = r'=' t_LBRACE = r'{' t_RBRACE = r'}' t_SLASH = r'/' def t_TEXT_STRING(t): r'\'[^\']+\'' t.value = t.value[1:-1] return t def t_CODE_STRING(t): r'\@.*' t.value = t.value[1:] return t def t_COMMENT(t): r'\#.*' pass # No Return Value. Token discarded def t_FLOATER(t): r'\d+\.\d*([eE]\d+)?' try: t.value = float(t.value) except ValueError: print "Floating value invalid:", t.value t.value = 0.0 return t def t_INTEGER(t): r'\d+' try: t.value = int(t.value) except ValueError: print "Integer value too large:", t.value t.value = 0 return t def t_ID(t): r'[a-zA-Z_][a-zA-Z_0-9]*' t.type = reserved.get(t.value.upper(), 'ID') # Check for reserved words # Force reserved words to lower case for map lookup and comparisson if t.value.upper() in reserved: t.type = reserved[t.value.upper()] t.value = t.value.lower() return t # Ignored characters t_ignore = " \t;" def t_newline(t): r'\n+' t.lexer.lineno += t.value.count("\n") def t_error(t): print("Illegal character '%s'" % t.value[0]) t.lexer.skip(1) # # Parser # # # Parsing rules # # # We don't yet have a need for precedence so leave it empty here # precedence = ( #('left','PLUS','MINUS'), #('left','TIMES','DIVIDE'), #('right','UMINUS'), ) # # The head token - it all reduces to this # def p_driver(p): 'driver : DRIVER id_or_str EQUALS driver_block' p[0] = [{ 'Driver' : {p[2] : p[4]}}] if Verbose: print "Driver:", p[0] global PathName global TheDrivers TheDrivers[p[2]] = p[4] + [{'PathName':PathName}] def p_driver_block(p): 'driver_block : LBRACE driver_statement_list RBRACE' p[0] = p[2] def p_driver_statement_list(p): '''driver_statement_list : driver_statement | driver_statement_list driver_statement ''' if len(p) == 2: p[0] = [p[1]] else: p[0] = p[1] + [p[2]] def p_driver_statement(p): '''driver_statement : driver_assignment | group | code | driver_property ''' p[0] = p[1] def p_driver_assignment(p): ''' driver_assignment : VENDOR EQUALS id_or_str | DEVICE EQUALS id_or_str | PROTOCOL EQUALS id_or_str | CLASS EQUALS id_or_str | SIMULATION_GROUP EQUALS id_or_str | USECREATENODE EQUALS true_false | ADD_ARGS EQUALS TEXT_STRING | MAKE_ARGS EQUALS TEXT_STRING | PROTOCOL_ARGS EQUALS TEXT_STRING ''' p[0] = { p[1] : p[3] } # # The GROUP block # def p_group(p): ''' group : GROUP group_id EQUALS LBRACE group_statement_list RBRACE ''' p[0] = { 'Group' : [{'name': p[2]}] + p[5] } def p_group_id(p): ''' group_id : id_or_str | empty ''' p[0] = p[1] def p_group_statement_list(p): ''' group_statement_list : group_statement | group_statement_list group_statement ''' if len(p) == 2: p[0] = [p[1]] else: p[0] = p[1] + [p[2]] def p_group_statement(p): '''group_statement : group_assignment | variable ''' p[0] = p[1] def p_group_assignment(p): '''group_assignment : group_property | var_typ_ass | property ''' p[0] = p[1] # # The VAR block # def p_variable(p): ''' variable : VAR id_or_str EQUALS LBRACE variable_statement_list RBRACE | VAR id_or_str ''' if len(p) > 3: p[0] = { 'Variable' : [{'name' : p[2]}] + p[5] } else: p[0] = { 'Variable' : [{'name' : p[2]}] } def p_variable_statement_list(p): '''variable_statement_list : variable_statement | variable_statement_list variable_statement ''' if len(p) == 2: p[0] = [p[1]] else: p[0] = p[1] + [p[2]] def p_variable_statement(p): '''variable_statement : var_typ_ass | var_val_ass | property ''' p[0] = p[1] def p_var_typ_ass(p): ''' var_typ_ass : READABLE EQUALS INTEGER | WRITEABLE EQUALS INTEGER | READ_COMMAND EQUALS TEXT_STRING | READ_FUNCTION EQUALS id_or_str | FETCH_FUNCTION EQUALS id_or_str | WRITE_COMMAND EQUALS TEXT_STRING | WRITE_FUNCTION EQUALS id_or_str | CHECK_FUNCTION EQUALS id_or_str | CHECKRANGE_FUNCTION EQUALS id_or_str | CHECKLIMITS_FUNCTION EQUALS id_or_str | CHECKSTATUS_FUNCTION EQUALS id_or_str | HALT_FUNCTION EQUALS id_or_str | TYPE EQUALS type_code | PRIV EQUALS priv_code | CONTROL EQUALS true_false | DATA EQUALS true_false | NXSAVE EQUALS true_false | MUTABLE EQUALS true_false ''' p[0] = { p[1] : p[3] } def p_var_path(p): ''' var_path : id_or_str | var_path SLASH id_or_str ''' if len(p) == 2: p[0] = p[1] else: p[0] = p[1] + '/' + p[3] def p_var_val_ass(p): ''' var_val_ass : VALUE EQUALS FLOATER | VALUE EQUALS TEXT_STRING | VALUE EQUALS INTEGER | ALLOWED EQUALS TEXT_STRING | UNITS EQUALS TEXT_STRING | LOWERLIMIT EQUALS number | UPPERLIMIT EQUALS number | TOLERANCE EQUALS number | DRIVEABLE EQUALS var_path ''' p[0] = { p[1] : p[3] } def p_driver_property(p): ''' driver_property : DRIVER_PROPERTY id_or_str EQUALS value ''' p[0] = { 'DriverProperty' : ( p[2], p[4] ) } def p_group_property(p): ''' group_property : GROUP_PROPERTY id_or_str EQUALS value ''' p[0] = { 'GroupProperty' : ( p[2], p[4] ) } def p_property(p): ''' property : PROPERTY id_or_str EQUALS value ''' p[0] = { 'Property' : ( p[2], p[4] ) } def p_value(p): ''' value : number | id_or_str | true_false ''' p[0] = p[1] def p_number(p): ''' number : INTEGER | FLOATER ''' p[0] = p[1] def p_type_code(p): ''' type_code : FLOAT | INT | TEXT | NONE ''' p[0] = p[1] def p_priv_code(p): ''' priv_code : SPY | USER | MANAGER | READONLY | INTERNAL ''' p[0] = p[1] def p_true_false(p): ''' true_false : TRUE | FALSE ''' p[0] = p[1] # # The CODE block # def p_code(p): ''' code : CODE code_type id_or_str EQUALS LBRACE code_block RBRACE ''' p[0] = { 'Code' : { 'name' : p[3], 'type' : p[2], 'text' : p[6] }} def p_code_type(p): ''' code_type : READ_FUNCTION | FETCH_FUNCTION | WRITE_FUNCTION | CHECK_FUNCTION | CHECKRANGE_FUNCTION | CHECKLIMITS_FUNCTION | CHECKSTATUS_FUNCTION | HALT_FUNCTION | empty ''' p[0] = p[1] def p_code_block(p): '''code_block : empty | code_block CODE_STRING ''' if len(p) == 2: p[0] = [] else: p[0] = p[1] + [p[2]] def p_id_or_str(p): ''' id_or_str : ID | TEXT_STRING ''' p[0] = p[1] def p_empty(p): ''' empty : ''' pass def p_error(t): print("Syntax error at '%s'" % t.value), t # # This section handles building a driver tree from the Abstract Syntax Tree # generated by the parser. The driver tree has all of the defaults and # cascading context explicitly stated to make the code generation simpler. # def init_context(): global ContextStack, ContextIndex ContextStack = [{}] ContextIndex = 0 ContextStack[ContextIndex]['type'] = 'none' ContextStack[ContextIndex]['priv'] = 'user' ContextStack[ContextIndex]['readable'] = 0 ContextStack[ContextIndex]['writeable'] = 0 ContextStack[ContextIndex]['driveable'] = None ContextStack[ContextIndex]['control'] = 'true' ContextStack[ContextIndex]['data'] = 'true' ContextStack[ContextIndex]['mutable'] = 'false' ContextStack[ContextIndex]['nxsave'] = 'true' ContextStack[ContextIndex]['read_function'] = 'rdValue' ContextStack[ContextIndex]['write_function'] = 'setValue' ContextStack[ContextIndex]['fetch_function'] = 'getValue' ContextStack[ContextIndex]['check_function'] = 'noResponse' ContextStack[ContextIndex]['checkrange_function'] = 'checkrange' ContextStack[ContextIndex]['path'] = '' def push_context(): global ContextStack, ContextIndex ContextIndex = ContextIndex + 1 if len(ContextStack) <= ContextIndex: ContextStack.append({}) ContextStack[ContextIndex] = {} for k in ContextStack[ContextIndex - 1].keys(): ContextStack[ContextIndex][k] = ContextStack[ContextIndex - 1][k] def pop_context(): global ContextStack, ContextIndex ContextIndex = ContextIndex - 1 def build_code(MyDriver, p): if Verbose: print 'Code:', p print "Function:", p['name'] MyCode = {} MyCode['name'] = p['name'] MyCode['reference_count'] = 0 if 'type' in p: MyCode['type'] = p['type'] MyCode['text'] = p['text'] if Verbose: for line in p['text']: print " Line:", line return MyCode def build_variable(MyDriver, p): global FunctionTypes global DriveableFunctionTypes if Verbose: print 'Variable:', p MyVar = {} # Copy items for this variable for item in p: if Verbose: print "Variable Item:", item for key in item.keys(): if key == 'Property': if not 'Property' in MyVar: MyVar['Property'] = {} MyVar['Property'][item[key][0]] = item[key][1] else: MyVar[key] = item[key] # copy the defaults for missing items for key in ContextStack[ContextIndex]: if key == 'Property': if 'Property' not in MyVar: MyVar['Property'] = {} for key2 in ContextStack[ContextIndex][key]: if key2 not in MyVar['Property']: MyVar['Property'][key2] = ContextStack[ContextIndex][key][key2] elif not key in MyVar: MyVar[key] = ContextStack[ContextIndex][key] # if this variable is driveable if 'driveable' in MyVar and MyVar['driveable']: # insert defaults for missing driveable functions if 'checklimits_function' not in MyVar: MyVar['checklimits_function'] = 'checklimits' if 'checkstatus_function' not in MyVar: MyVar['checkstatus_function'] = 'checkstatus' if 'halt_function' not in MyVar: MyVar['halt_function'] = 'halt' for func in FunctionTypes + DriveableFunctionTypes: if func in MyVar and MyVar[func] != 'none': if Verbose: print 'Var:', MyVar['name'], 'Func:', func, '=', MyVar[func] if MyVar[func] not in MyDriver['Funcs']: MyDriver['Funcs'][MyVar[func]] = { 'type' : func, 'text' : [], 'reference_count' : 0 } if Verbose: print MyVar['name'], 'Add func ' + MyVar[func], MyDriver['Funcs'][MyVar[func]] elif not MyDriver['Funcs'][MyVar[func]]['type'] == func: # allow override of type none else error message if not MyDriver['Funcs'][MyVar[func]]['type']: if Verbose: print MyVar['name'], 'Mod func type:', MyDriver['Funcs'][MyVar[func]], '= ' + func MyDriver['Funcs'][MyVar[func]]['type'] = func else: # TODO FIXME error message print 'Error: Function type mismatch: var = ' + str(MyVar) + ', code = ' + str(MyDriver['Funcs'][MyVar[func]]) + ', func = ' + str(func) MyDriver['Funcs'][MyVar[func]]['reference_count'] += 1 if Verbose: print '==>>MyVar:', MyVar return MyVar def build_group(MyDriver, p): if Verbose: print 'Group:', p[0]['name'], p push_context() MyGroup = {} MyGroup['Groups'] = {} MyGroup['Vars'] = {} # the sequence of both variables and non-variables is significant # Therefore, they have to be processed in a single sequence if p[0]['name']: if len(ContextStack[ContextIndex]['path']) > 0: ContextStack[ContextIndex]['path'] += '/' ContextStack[ContextIndex]['path'] += p[0]['name'] for item in p: if 'Variable' in item: MyVar = build_variable(MyDriver, item['Variable']) MyGroup['Vars'][MyVar['name']] = MyVar else: if Verbose: print "Group Item:", item if 'GroupProperty' in item: if 'GroupProperty' not in MyGroup: MyGroup['GroupProperty'] = {} MyGroup['GroupProperty'][item['GroupProperty'][0]] = item['GroupProperty'][1] elif 'Property' in item: if 'Property' not in MyGroup: MyGroup['Property'] = {} MyGroup['Property'][item['Property'][0]] = item['Property'][1] if 'Property' not in ContextStack[ContextIndex]: ContextStack[ContextIndex]['Property'] = {} ContextStack[ContextIndex]['Property'][item['Property'][0]] = item['Property'][1] else: for key in item: MyGroup[key] = item[key] if key in ContextStack[ContextIndex]: ContextStack[ContextIndex][key] = item[key] pop_context() return MyGroup def build_driver(MyDriver, TheTree): if Verbose: print "TheTree:", TheTree init_context() for item in [x for x in TheTree if 'Code' in x]: MyCode = build_code(MyDriver, item['Code']) MyDriver['Funcs'][MyCode['name']] = MyCode for item in [x for x in TheTree if 'Group' in x]: MyGroup = build_group(MyDriver, item['Group']) MyDriver['Groups'][MyGroup['name']] = MyGroup for item in TheTree: if Verbose: print "Driver Item:", item if 'Group' in item: continue elif 'Code' in item: continue else: if 'DriverProperty' in item: if 'DriverProperty' not in MyDriver: MyDriver['DriverProperty'] = {} MyDriver['DriverProperty'][item['DriverProperty'][0]] = item['DriverProperty'][1] continue for key in item: MyDriver[key] = item[key] if Verbose: print "MyDriver:", MyDriver # # Driver Dump Functions # def dump_driver_vars(vars): global FunctionTypes global DriveableFunctionTypes for item in sorted(vars): print ' VAR %s = {' % item for subitem in sorted([i for i in vars[item] if i not in FunctionTypes + DriveableFunctionTypes]): print ' %s =' % subitem, vars[item][subitem] for subitem in sorted([i for i in vars[item] if i in FunctionTypes]): print ' %s =' % subitem, vars[item][subitem] for subitem in sorted([i for i in vars[item] if i in DriveableFunctionTypes]): print ' %s =' % subitem, vars[item][subitem] print ' }' def dump_driver_groups(groups): for item in sorted(groups): if item: print ' GROUP ' + item + ' = {' else: print ' GROUP = {' for subitem in sorted([x for x in groups[item] if not x in ['Groups', 'Vars']]): print ' ', subitem, '=', groups[item][subitem] dump_driver_vars(groups[item]['Vars']) print ' }' def dump_driver_funcs(funcs): for item in sorted(funcs): if 'type' in funcs[item] and funcs[item]['type']: print ' CODE ' + funcs[item]['type'] + ' ' + item + ' = {' else: print ' CODE ' + item + ' = {' for line in funcs[item]['text']: print ' @%s' % line print ' }' def dump_driver(MyDriver): print 'DRIVER ' + MyDriver['name'] + ' = {' for item in sorted([x for x in MyDriver if x not in ['Groups', 'Funcs']]): print ' ' + item + ' =', MyDriver[item] #print 'Groups:', MyDriver['Groups'] dump_driver_groups(MyDriver['Groups']) #print 'Funcs:', MyDriver['Funcs'] dump_driver_funcs(MyDriver['Funcs']) print '}' # # Code Generation Functions # def emit(txt): global NumberOfLinesOut NumberOfLinesOut += len(txt) for line in txt: fdo.write(line) if not line.endswith('\n'): fdo.write('\n') def put_preamble(MyDriver): txt = [] txt += ['# Generated driver for %s' % MyDriver['name']] txt += ['# vim: tabstop=8 softtabstop=2 shiftwidth=2 nocindent smartindent'] txt += ['#'] txt += [''] txt += ['namespace eval %s {' % MyDriver['namespace']] txt += [' set debug_threshold 0'] txt += ['}'] txt += [''] txt += ['proc %s::debug_log {debug_level debug_string} {' % MyDriver['namespace']] txt += [' if {${debug_level} >= ${%s::debug_threshold}} {' % MyDriver['namespace']] txt += [' set fd [open "/tmp/%s.log" "a"]' % MyDriver['name']] txt += [' set line "[clock format [clock seconds] -format "%T"] ${debug_string}"'] txt += [' puts ${fd} "${line}"'] txt += [' close ${fd}'] txt += [' }'] txt += ['}'] emit(txt) def put_write_function(MyDriver, func): txt = [''] txt += ['# function to write a parameter value on a device'] txt += ['proc %s::%s {tc_root nextState cmd_str} {' % (MyDriver['namespace'], func)] txt += [' debug_log 1 "%s tc_root=${tc_root} sct=[sct] cmd=${cmd_str}"' % func] txt += [' if { [hpropexists [sct] geterror] } {'] txt += [' hdelprop [sct] geterror'] txt += [' }'] txt += [' set par [sct target]'] txt += [' set cmd "${cmd_str}${par}"'] if func in MyDriver['Funcs'] and len(MyDriver['Funcs'][func]['text']) > 0: txt += ['# hook code starts'] txt += MyDriver['Funcs'][func]['text'] txt += ['# hook code ends'] txt += [' debug_log 1 "%s sct send ${cmd}"' % func] txt += [' sct send "${cmd}"'] txt += [' return ${nextState}'] txt += ['}'] emit(txt) def put_check_function(MyDriver, func): txt = [''] txt += ['# function to check the write parameter on a device'] txt += ['proc %s::%s {tc_root} {' % (MyDriver['namespace'], func)] txt += [' debug_log 1 "%s tc_root=${tc_root} sct=[sct] resp=[sct result]"' % func] if func in MyDriver['Funcs'] and len(MyDriver['Funcs'][func]['text']) > 0: txt += ['# hook code starts'] txt += MyDriver['Funcs'][func]['text'] txt += ['# hook code ends'] txt += [' return "idle"'] txt += ['}'] emit(txt) def put_fetch_function(MyDriver, func): txt = [''] txt += ['# function to request the read of a parameter on a device'] txt += ['proc %s::%s {tc_root nextState cmd_str} {' % (MyDriver['namespace'], func)] txt += [' debug_log 1 "%s tc_root=${tc_root} sct=[sct] cmd=${cmd_str}"' % func] txt += [' if { [hpropexists [sct] geterror] } {'] txt += [' hdelprop [sct] geterror'] txt += [' }'] txt += [' set cmd "${cmd_str}"'] if func in MyDriver['Funcs'] and len(MyDriver['Funcs'][func]['text']) > 0: txt += ['# hook code starts'] txt += MyDriver['Funcs'][func]['text'] txt += ['# hook code ends'] txt += [' debug_log 1 "%s sct send ${cmd}"' % func] txt += [' sct send "${cmd}"'] txt += [' return ${nextState}'] txt += ['}'] emit(txt) def put_read_function(MyDriver, func): txt = [''] txt += ['# function to parse the read of a parameter on a device'] txt += ['proc %s::%s {tc_root} {' % (MyDriver['namespace'], func)] txt += [' debug_log 1 "%s tc_root=${tc_root} sct=[sct] result=[sct result]"' % func] txt += [' if { [hpropexists [sct] geterror] } {'] txt += [' hdelprop [sct] geterror'] txt += [' }'] txt += [' set data [sct result]'] txt += [' set nextState "idle"'] txt += [' if {[string equal -nocase -length 7 ${data} "ASCERR:"]} {'] txt += [' # the protocol driver has reported an error'] txt += [' sct geterror "${data}"'] txt += [' return -code error "[sct geterror]"'] txt += [' }'] if func in MyDriver['Funcs'] and len(MyDriver['Funcs'][func]['text']) > 0: txt += ['# hook code starts'] txt += MyDriver['Funcs'][func]['text'] txt += ['# hook code ends'] txt += [' if { [hpropexists [sct] geterror] } {'] txt += [' debug_log 1 "[sct] error: [sct geterror]"'] txt += [' return -code error "[sct geterror]"'] txt += [' }'] txt += [' if { ${data} != [sct oldval] } {'] txt += [' debug_log 1 "[sct] changed to new:${data}, from old:[sct oldval]"'] txt += [' sct oldval ${data}'] txt += [' sct update ${data}'] txt += [' sct utime readtime'] txt += [' }'] txt += [' return ${nextState}'] txt += ['}'] emit(txt) def put_checkrange_function(MyDriver, func): txt = [''] txt += ['# check function for hset change'] txt += ['proc %s::%s {tc_root} {' % (MyDriver['namespace'], func)] txt += [' debug_log 1 "%s tc_root=${tc_root} sct=[sct] target=[sct target]"' % func] txt += [' set setpoint [sct target]'] txt += [' if { [hpropexists [sct] lowerlimit] } {'] txt += [' set lolimit [sct lowerlimit]'] txt += [' } else {'] txt += [' # lowerlimit not set, use target'] txt += [' set lolimit [sct target]'] txt += [' }'] txt += [' if { [hpropexists [sct] upperlimit] } {'] txt += [' set hilimit [sct upperlimit]'] txt += [' } else {'] txt += [' # upperlimit not set, use target'] txt += [' set hilimit [sct target]'] txt += [' }'] if func in MyDriver['Funcs'] and len(MyDriver['Funcs'][func]['text']) > 0: txt += ['# hook code starts'] txt += MyDriver['Funcs'][func]['text'] txt += ['# hook code ends'] txt += [' if { ${setpoint} < ${lolimit} || ${setpoint} > ${hilimit} } {'] txt += [' error "setpoint ${setpoint} violates limits (${lolimit}..${hilimit}) on [sct]"'] txt += [' }'] txt += [' return OK'] txt += ['}'] emit(txt) def put_checklimits_function(MyDriver, func): txt = [''] txt += ['# checklimits function for driveable interface'] txt += ['proc %s::%s {tc_root} {' % (MyDriver['namespace'], func)] txt += [' debug_log 1 "%s tc_root=${tc_root} sct=[sct] target=[sct target]"' % func] txt += [' set setpoint [sct target]'] txt += [' if { [hpropexists [sct] lowerlimit] } {'] txt += [' set lolimit [sct lowerlimit]'] txt += [' } else {'] txt += [' # lowerlimit not set, use target'] txt += [' set lolimit [sct target]'] txt += [' }'] txt += [' if { [hpropexists [sct] upperlimit] } {'] txt += [' set hilimit [sct upperlimit]'] txt += [' } else {'] txt += [' # upperlimit not set, use target'] txt += [' set hilimit [sct target]'] txt += [' }'] if func in MyDriver['Funcs'] and len(MyDriver['Funcs'][func]['text']) > 0: txt += ['# hook code starts'] txt += MyDriver['Funcs'][func]['text'] txt += ['# hook code ends'] txt += [' if { ${setpoint} < ${lolimit} || ${setpoint} > ${hilimit} } {'] txt += [' sct driving 0'] txt += [' error "setpoint ${setpoint} violates limits (${lolimit}..${hilimit}) on [sct]"'] txt += [' }'] txt += [' return OK'] txt += ['}'] emit(txt) def put_checkstatus_function(MyDriver, func): txt = [''] txt += ['# checkstatus function for driveable interface'] txt += ['proc %s::%s {tc_root} {' % (MyDriver['namespace'], func)] if func in MyDriver['Funcs'] and len(MyDriver['Funcs'][func]['text']) > 0: txt += ['# hook code starts'] txt += MyDriver['Funcs'][func]['text'] txt += ['# hook code ends'] txt += [' if {[sct driving]} {'] txt += [' set sp "[sct target]"'] txt += [' set pv "[hget ${tc_root}/[sct driveable]]"'] txt += [' if { ${pv} > ${sp} - [sct tolerance] && ${pv} < ${sp} + [sct tolerance] } {'] txt += [' sct driving 0'] txt += [' return "idle"'] txt += [' }'] txt += [' return "busy"'] txt += [' } else {'] txt += [' return "idle"'] txt += [' }'] txt += ['}'] emit(txt) def put_halt_function(MyDriver, func): txt = [''] txt += ['# halt function for driveable interface'] txt += ['proc %s::%s {tc_root} {' % (MyDriver['namespace'], func)] txt += [' debug_log 1 "%s tc_root=${tc_root} sct=[sct] driving=[sct driving]"' % func] txt += [' ### TODO hset [sct] [hval [sct]]'] if func in MyDriver['Funcs'] and len(MyDriver['Funcs'][func]['text']) > 0: txt += ['# hook code starts'] txt += MyDriver['Funcs'][func]['text'] txt += ['# hook code ends'] txt += [' sct driving 0'] txt += [' return "idle"'] txt += ['}'] emit(txt) def put_create_node(MyDriver): txt = [''] txt += ['##'] txt += ['# @brief createNode() creates a node for the given nodename with the properties given'] txt += ['#'] txt += ['# @param scobj_hpath string variable holding the path to the object\'s base node in sics (/sample/tc1)'] txt += ['# @param sct_controller name of the scriptcontext object (typically sct_xxx_yyy)'] txt += ['# @param cmdGroup subdirectory (below /sample/tc*/) in which the node is to be created'] txt += ['# @param varName name of the actual node typically representing one device command'] txt += ['# @param readable set to 1 if the node represents a query command, 0 if it is not'] txt += ['# @param writable set to 1 if the node represents a request for a change in settings sent to the device'] txt += ['# @param drivable if set to 1 it prepares the node to provide a drivable interface'] txt += ['# @param dataType data type of the node, must be one of none, int, float, text'] txt += ['# @param permission defines what user group may read/write to this node (is one of spy, user, manager)'] txt += ['# @param rdCmd actual device query command to be sent to the device'] txt += ['# @param rdFunc nextState Function to be called after the getValue function, typically rdValue()'] txt += ['# @param wrCmd actual device write command to be sent to the device'] txt += ['# @param wrFunc Function to be called to send the wrCmd to the device, typically setValue()'] txt += ['# @param allowedValues allowed values for the node data - does not permit other'] txt += ['# @param klass Nexus class name (?)'] txt += ['# @return OK'] txt += ['proc %s::createNode {scobj_hpath sct_controller\\' %MyDriver['namespace']] txt += [' cmdGroup varName\\'] txt += [' readable writable drivable\\'] txt += [' dataType permission\\'] txt += [' rdCmd rdFunc\\'] txt += [' wrCmd wrFunc\\'] txt += [' allowedValues klass} {'] txt += [''] txt += [' set catch_status [ catch {'] txt += [' set ns "[namespace current]"'] txt += [' set nodeName "${scobj_hpath}/${cmdGroup}/${varName}"'] txt += [' if {1 > [string length ${cmdGroup}]} {'] txt += [' set nodeName "${scobj_hpath}/${varName}"'] txt += [' }'] txt += [' debug_log 1 "Creating node ${nodeName}"'] txt += [' hfactory ${nodeName} plain ${permission} ${dataType}'] txt += [' if {${readable} > 0} {'] txt += [' # the node is readable so set it up to be polled using the rdFunc'] txt += [' # rdFunc is getValueFunc.rdValueFunc with both explicit functions'] txt += [' # or rdValueFunc where "getValue" is the implied getValueFunc'] txt += [' set parts [split "${rdFunc}" "."]'] txt += [' if { [llength ${parts}] == 2 } {'] txt += [' set func_name [lindex ${parts} 0]'] txt += [' set next_state [lindex ${parts} 1]'] txt += [' } else {'] txt += [' set func_name "getValue"'] txt += [' set next_state [lindex ${parts} 0]'] txt += [' }'] txt += [' hsetprop ${nodeName} read ${ns}::${func_name} ${scobj_hpath} ${next_state} ${rdCmd}'] txt += [' hsetprop ${nodeName} ${next_state} ${ns}::${next_state} ${scobj_hpath}'] txt += [' # set the poll rate as a period in seconds'] txt += [' # TODO allow directly settable value in seconds'] txt += [' set poll_period 5'] txt += [' if { ${readable} >= 0 && ${readable} <= 300 } {'] txt += [' set poll_period [expr {int(${readable})}]'] txt += [' }'] txt += [' debug_log 1 "Registering node ${nodeName} for poll at ${poll_period} seconds"'] txt += [' ${sct_controller} poll ${nodeName} ${poll_period}'] txt += [' }'] txt += [' if {${writable} > 0} {'] txt += [' # the node is writable so set it up to invoke a callback when written'] txt += [' # rdFunc is putValueFunc.chkWriteFunc with both explicit functions'] txt += [' # or putValueFunc where "noResponse" is the implied chkWriteFunc'] txt += [' set parts [split "${wrFunc}" "."]'] txt += [' if { [llength ${parts}] == 2 } {'] txt += [' set func_name [lindex ${parts} 0]'] txt += [' set next_state [lindex ${parts} 1]'] txt += [' } else {'] txt += [' set func_name [lindex ${parts} 0]'] txt += [' set next_state "noResponse"'] txt += [' }'] txt += [' hsetprop ${nodeName} write ${ns}::${func_name} ${scobj_hpath} ${next_state} ${wrCmd}'] txt += [' hsetprop ${nodeName} ${next_state} ${ns}::${next_state} ${scobj_hpath}'] txt += [' hsetprop ${nodeName} writestatus UNKNOWN'] txt += [' debug_log 1 "Registering node ${nodeName} for write callback"'] txt += [' ${sct_controller} write ${nodeName}'] txt += [' }'] txt += [' # Initialise the previous value to test against'] txt += [' switch -exact ${dataType} {'] txt += [' "none" { }'] txt += [' "int" { hsetprop ${nodeName} oldval -1 }'] txt += [' "float" { hsetprop ${nodeName} oldval -1.0 }'] txt += [' default { hsetprop ${nodeName} oldval UNKNOWN }'] txt += [' }'] txt += [' # Set the allowed values property'] txt += [' if {1 < [string length ${allowedValues}]} {'] txt += [' hsetprop ${nodeName} values ${allowedValues}'] txt += [' }'] txt += [' # Drive adapter interface'] txt += [' # TODO make it a separate function and pass in all this stuff'] txt += [' if {${drivable} > 0} {'] txt += [' hsetprop ${nodeName} check ${ns}::check ${scobj_hpath}'] txt += [' hsetprop ${nodeName} driving 0'] txt += [' hsetprop ${nodeName} checklimits ${ns}::checklimits ${scobj_hpath}'] txt += [' hsetprop ${nodeName} checkstatus ${ns}::checkstatus ${scobj_hpath}'] txt += [' hsetprop ${nodeName} halt ${ns}::halt ${scobj_hpath}'] txt += [' }'] txt += [' } catch_message ]'] txt += [' handle_exception ${catch_status} ${catch_message} "in [namespace current]::createNode"'] txt += [' return OK'] txt += ['}'] emit(txt) def get_one_var(group_name, MyVar): lst = [ group_name ] lst.append(MyVar['name']) if 'readable' in MyVar: lst.append(str(MyVar['readable'])) else: lst.append('0') if 'writeable' in MyVar: lst.append(str(MyVar['writeable'])) else: lst.append('0') if 'driveable' in MyVar: lst.append(str(MyVar['driveable'])) else: lst.append('0') if 'type' in MyVar: lst.append(MyVar['type']) else: lst.append('text') if 'priv' in MyVar: lst.append(MyVar['priv']) else: lst.append('user') if 'read_command' in MyVar: lst.append('{' + MyVar['read_command'] + '}') else: lst.append('{}') if 'read_function' in MyVar: if not MyVar['fetch_function'] == 'none': lst.append('{' + MyVar['fetch_function'] + '.' + MyVar['read_function'] + '}') else: lst.append('{' + MyVar['read_function'] + '}') else: lst.append('{}') if 'write_command' in MyVar: lst.append('{' + MyVar['write_command'] + '}') else: lst.append('{}') if 'write_function' in MyVar: if not MyVar['check_function'] == 'none': lst.append('{' + MyVar['check_function'] + '.' + MyVar['write_function'] + '}') else: lst.append('{' + MyVar['write_function'] + '}') else: lst.append('{}') if 'allowed' in MyVar: lst.append('{' + MyVar['allowed'] + '}') else: lst.append('{}') return lst def put_group(MyDriver, MyGroup): readable_or_writeable = False txt = [] if MyGroup['name']: txt += [''] txt += [' hfactory ${scobj_hpath}/%s plain spy none' % MyGroup['name']] if 'GroupProperty' in MyGroup: for key in sorted(MyGroup['GroupProperty']): txt += [' hsetprop ${scobj_hpath}/%s %s "%s"' % (MyGroup['name'], key, MyGroup['GroupProperty'][key])] groupname = MyGroup['name'] + '/' else: groupname = '' for var in sorted(MyGroup['Vars']): txt += [''] MyVar = MyGroup['Vars'][var] nodename = groupname + MyVar['name'] # Check driveable attributes are present if required if MyVar['driveable']: for attr in ('lowerlimit', 'upperlimit', 'tolerance'): if attr not in MyVar: msg = 'Driveable: %s does not have required attribute: %s' % (nodename, attr) print 'Warning:', msg txt += [' # Warning: ' + msg] txt += [' hfactory ${scobj_hpath}/%s plain %s %s' % (nodename, MyVar['priv'], MyVar['type'])] if MyVar['readable'] > 0: readable_or_writeable = True fetch_func = MyVar['fetch_function'] if fetch_func == 'none': fetch_func = 'getValue' read_func = MyVar['read_function'] read_command = MyVar['read_command'] txt += [' hsetprop ${scobj_hpath}/%s read ${ns}::%s ${scobj_hpath} %s {%s}' % (nodename, fetch_func, read_func, read_command)] txt += [' hsetprop ${scobj_hpath}/%s %s ${ns}::%s ${scobj_hpath}' % (nodename, read_func, read_func)] if MyVar['writeable'] > 0 or MyVar['driveable']: readable_or_writeable = True check_func = MyVar['check_function'] checkrange_func = MyVar['checkrange_function'] write_func = MyVar['write_function'] if 'write_command' in MyVar: write_command = MyVar['write_command'] else: write_command = '' txt += [' hsetprop ${scobj_hpath}/%s write ${ns}::%s ${scobj_hpath} %s {%s}' % (nodename, write_func, check_func, write_command)] txt += [' hsetprop ${scobj_hpath}/%s %s ${ns}::%s ${scobj_hpath}' % (nodename, check_func, check_func)] txt += [' hsetprop ${scobj_hpath}/%s check ${ns}::%s ${scobj_hpath}' % (nodename, checkrange_func)] if MyVar['driveable']: halt_func = MyVar['halt_function'] checklimits_func = MyVar['checklimits_function'] checkstatus_func = MyVar['checkstatus_function'] txt += [' hsetprop ${scobj_hpath}/%s driving 0' % nodename] txt += [' hsetprop ${scobj_hpath}/%s checklimits ${ns}::%s ${scobj_hpath}' % (nodename, checklimits_func)] txt += [' hsetprop ${scobj_hpath}/%s checkstatus ${ns}::%s ${scobj_hpath}' % (nodename, checkstatus_func)] txt += [' hsetprop ${scobj_hpath}/%s halt ${ns}::%s ${scobj_hpath}' % (nodename, halt_func)] txt += [' hsetprop ${scobj_hpath}/%s driveable %s' % (nodename, MyVar['driveable'])] if 'control' in MyVar: txt += [' hsetprop ${scobj_hpath}/%s control %s' % (nodename, MyVar['control'])] if 'data' in MyVar: txt += [' hsetprop ${scobj_hpath}/%s data %s' % (nodename, MyVar['data'])] if 'mutable' in MyVar: txt += [' hsetprop ${scobj_hpath}/%s mutable %s' % (nodename, MyVar['mutable'])] if 'nxsave' in MyVar: txt += [' hsetprop ${scobj_hpath}/%s nxsave %s' % (nodename, MyVar['nxsave'])] if 'lowerlimit' in MyVar: txt += [' hsetprop ${scobj_hpath}/%s lowerlimit %s' % (nodename, MyVar['lowerlimit'])] if 'upperlimit' in MyVar: txt += [' hsetprop ${scobj_hpath}/%s upperlimit %s' % (nodename, MyVar['upperlimit'])] if 'tolerance' in MyVar: txt += [' hsetprop ${scobj_hpath}/%s tolerance %s' % (nodename, MyVar['tolerance'])] if 'units' in MyVar: txt += [' hsetprop ${scobj_hpath}/%s units %s' % (nodename, MyVar['units'])] if 'allowed' in MyVar: txt += [' hsetprop ${scobj_hpath}/%s values %s' % (nodename, MyVar['allowed'])] if 'value' in MyVar: txt += [' hsetprop ${scobj_hpath}/%s oldval %s' % (nodename, MyVar['value'])] txt += [' hset ${scobj_hpath}/%s %s' % (nodename, MyVar['value'])] else: if MyVar['type'] == 'none': pass elif MyVar['type'] == 'int': txt += [' hsetprop ${scobj_hpath}/%s oldval 0' % nodename] elif MyVar['type'] == 'float': txt += [' hsetprop ${scobj_hpath}/%s oldval 0.0' % nodename] else: txt += [' hsetprop ${scobj_hpath}/%s oldval UNKNOWN' % nodename] if 'Property' in MyVar: for key in sorted(MyVar['Property']): txt += [' hsetprop ${scobj_hpath}/%s %s "%s"' % (nodename, key, MyVar['Property'][key])] if not MyGroup['name']: if 'GroupProperty' in MyGroup: txt += [''] for key in sorted(MyGroup['GroupProperty']): txt += [' hsetprop ${scobj_hpath} %s "%s"' % (key, MyGroup['GroupProperty'][key])] if readable_or_writeable: txt += [''] txt += [' if {[SplitReply [%s]]=="false"} {' % MyDriver['simulation_group']] for var in sorted(MyGroup['Vars']): MyVar = MyGroup['Vars'][var] nodename = groupname + MyVar['name'] if MyVar['readable'] > 0: poll_period = MyVar['readable'] if poll_period < 1 or poll_period > 300: poll_period = 5 txt += [' ${sct_controller} poll ${scobj_hpath}/%s %s' % (nodename, poll_period)] for var in sorted(MyGroup['Vars']): MyVar = MyGroup['Vars'][var] nodename = groupname + MyVar['name'] if MyVar['writeable'] > 0 or MyVar['driveable']: txt += [' ${sct_controller} write ${scobj_hpath}/%s' % nodename] if MyVar['driveable']: txt += [' ansto_makesctdrive ${name}_%s ${scobj_hpath}/%s ${scobj_hpath}/%s ${sct_controller}' % (MyVar['name'], nodename, MyVar['driveable'])] txt += [' }'] return txt def put_mk_sct_driver(MyDriver): txt = [''] if 'make_args' in MyDriver: line = 'proc %s::mk_sct_%s { sct_controller name %s } {' % (MyDriver['namespace'], MyDriver['name'], MyDriver['make_args']) else: line = 'proc %s::mk_sct_%s { sct_controller name } {' % (MyDriver['namespace'], MyDriver['name']) txt += [line] txt += [' debug_log 1 "mk_sct_%s for ${name}"' % MyDriver['name']] txt += [' set ns "[namespace current]"'] txt += [' set catch_status [ catch {'] txt += [''] txt += [' MakeSICSObj ${name} SCT_OBJECT'] txt += [''] txt += [' sicslist setatt ${name} klass %s' % MyDriver['class']] txt += [' sicslist setatt ${name} long_name ${name}'] if 'DriverProperty' in MyDriver: for key in MyDriver['DriverProperty']: txt += [' sicslist setatt ${name} %s "%s"' % (key, MyDriver['DriverProperty'][key])] txt += [''] txt += [' set scobj_hpath /sics/${name}'] if UsingCreateNode: groups = MyDriver['Groups'] if len(groups) > 0: for group in groups: if group: txt += [' hfactory ${scobj_hpath}/%s plain spy none' % group] txt += [''] device_commands = [] txt += [' set deviceCommand {'] for grp in sorted(MyDriver['Groups']): if grp: group_name = grp else: group_name = '{}' for var in sorted(MyDriver['Groups'][grp]['Vars']): device_commands.append(get_one_var(group_name, MyDriver['Groups'][grp]['Vars'][var])) lens = {} for cmd in device_commands: for idx, val in enumerate(cmd): if not idx in lens: lens[idx] = 0 if len(val) > lens[idx]: lens[idx] = len(val) for cmd in device_commands: line = ' ' for idx, val in enumerate(cmd): line += " %-*s" % (lens[idx], val) txt += [line + '\\'] txt += [' }'] txt += [' foreach { grpName varName\\'] txt += [' readable writeable driveable\\'] txt += [' dataType permission\\'] txt += [' rdCmd rdFunc wrCmd wrFunc allowedvalues} ${deviceCommand} {'] txt += [' createNode ${scobj_hpath} ${sct_controller}\\'] txt += [' ${cmdGroup} ${varName}\\'] txt += [' ${readable} ${writable} ${drivable}\\'] txt += [' ${dataType} ${permission}\\'] txt += [' ${rdCmd} ${rdFunc}\\'] txt += [' ${wrCmd} ${wrFunc}\\'] txt += [' ${allowedValues} %s' % MyDriver['class']] txt += [' }'] else: for group in sorted(MyDriver['Groups']): txt += put_group(MyDriver, MyDriver['Groups'][group]) txt += [' hsetprop ${scobj_hpath} klass %s' % MyDriver['class']] if 'mkDriver' in MyDriver['Funcs']: txt += ['# hook code starts'] txt += MyDriver['Funcs']['mkDriver']['text'] txt += ['# hook code ends'] txt += [' } catch_message ]'] txt += [' handle_exception ${catch_status} ${catch_message} "in ${ns}::mk_sct_%s"' % MyDriver['name']] txt += ['}'] emit(txt) def put_postamble(MyDriver): txt = [''] txt += ['namespace eval %s {' % MyDriver['namespace']] txt += [' namespace export debug_log'] txt += [' namespace export mk_sct_%s' % MyDriver['name']] txt += ['}'] txt += [''] if 'add_args' in MyDriver: line = 'proc add_%s {name IP port %s} {' % (MyDriver['name'], MyDriver['add_args']) else: line = 'proc add_%s {name IP port} {' % MyDriver['name'] txt += [line] txt += [' set ns "%s"' % MyDriver['namespace']] txt += [' ${ns}::debug_log 1 "add_%s ${name} ${IP} ${port}"' % MyDriver['name']] txt += [' if {[SplitReply [%s]]=="false"} {' % MyDriver['simulation_group']] txt += [' if {[string equal -nocase "aqadapter" "${IP}"]} {'] txt += [' ${ns}::debug_log 1 "makesctcontroller sct_${name} aqadapter ${port}"'] txt += [' makesctcontroller sct_${name} aqadapter ${port}'] txt += [' } else {'] if 'protocol_args' in MyDriver: protocol_args = MyDriver['protocol_args'].replace('\\', '\\\\').replace('"', '\\"') txt += [' ${ns}::debug_log 1 "makesctcontroller sct_${name} %s ${IP}:${port} %s"' % (MyDriver['protocol'], protocol_args)] txt += [' makesctcontroller sct_${name} %s ${IP}:${port} %s' % (MyDriver['protocol'], MyDriver['protocol_args'])] else: txt += [' ${ns}::debug_log 1 "makesctcontroller sct_${name} %s ${IP}:${port}"' % MyDriver['protocol']] txt += [' makesctcontroller sct_${name} %s ${IP}:${port}' % MyDriver['protocol']] txt += [' }'] txt += [' }'] make_args = '' if 'make_args' in MyDriver: for arg in MyDriver['make_args'].split(): if len(make_args) > 0: make_args += ' ' make_args += '${' + arg + '}' txt += [' ${ns}::debug_log 1 "mk_sct_%s sct_${name} ${name} %s"' % (MyDriver['name'], make_args)] txt += [' ${ns}::mk_sct_%s sct_${name} ${name} %s' % (MyDriver['name'], make_args)] else: txt += [' ${ns}::debug_log 1 "mk_sct_%s sct_${name} ${name}"' % MyDriver['name']] txt += [' ${ns}::mk_sct_%s sct_${name} ${name}' % MyDriver['name']] # TODO #txt += [' ${ns}::debug_log 1 "makesctemon ${name} /sics/${name}/emon/monmode /sics/${name}/emon/isintol /sics/${name}/emon/errhandler"'] # txt += [' makesctemon ${name} /sics/${name}/emon/monmode /sics/${name}/emon/isintol /sics/${name}/emon/errhandler'] txt += [' close ${fd}'] txt += ['}'] txt += [''] txt += ['puts stdout "file evaluation of sct_%s.tcl"' % MyDriver['name']] txt += ['%s::debug_log 1 "file evaluation of sct_%s.tcl"' % (MyDriver['namespace'], MyDriver['name'])] emit(txt) def put_standard_code(MyDriver): # emit all of the functions in Funcs for func in sorted(MyDriver['Funcs']): theFunc = MyDriver['Funcs'][func] # Don't generate functions which are not referenced if theFunc['reference_count'] == 0: continue if theFunc['type'] == 'read_function': put_read_function(MyDriver, func); elif theFunc['type'] == 'write_function': put_write_function(MyDriver, func); elif theFunc['type'] == 'fetch_function': put_fetch_function(MyDriver, func); elif theFunc['type'] == 'check_function': put_check_function(MyDriver, func); elif theFunc['type'] == 'checkrange_function': put_checkrange_function(MyDriver, func); elif theFunc['type'] == 'checklimits_function': put_checklimits_function(MyDriver, func); elif theFunc['type'] == 'checkstatus_function': put_checkstatus_function(MyDriver, func); elif theFunc['type'] == 'halt_function': put_halt_function(MyDriver, func); def generate_driver(MyDriver): global NumberOfLinesOut global fdo NumberOfLinesOut = 0 full_filename = filename = "sct_%s.tcl" % MyDriver['name'] if 'PathName' in MyDriver: full_filename = os.path.join(MyDriver['PathName'], filename) fdo = open(full_filename, 'w') put_preamble(MyDriver) put_standard_code(MyDriver) if UsingCreateNode: put_create_node(MyDriver) put_mk_sct_driver(MyDriver) put_postamble(MyDriver) fdo.close() if CodeDump or Verbose: print "Code Fragments:", MyDriver['Funcs'] for f in sorted(MyDriver['Funcs'].keys()): print "Function:", f, "Type:", MyDriver['Funcs'][f]['type'], '#Uses:', MyDriver['Funcs'][f]['reference_count'] for l in MyDriver['Funcs'][f]['text']: print " ", l print "Procuced file %s with %d lines." % (filename, NumberOfLinesOut) def process_drivers(TheDrivers): global UsingCreateNode if Verbose: print "TheDrivers:", TheDrivers for driver in TheDrivers: MyDriver = {'name':driver} MyDriver['namespace'] = '::scobj::%s' % driver MyDriver['Groups'] = {} MyDriver['Funcs'] = {} build_driver(MyDriver, TheDrivers[driver]) if Verbose: print "MyDriver:", MyDriver['name'], '=', MyDriver if 'usecreatenode' in MyDriver: if MyDriver['usecreatenode'] == 'true': UsingCreateNode = True else: UsingCreateNode = False generate_driver(MyDriver) if DriverDump or Verbose: dump_driver(MyDriver) def process_source(source_files): global PathName global TheDrivers global NumberOfLinesIn TheDrivers = {} # # Build the lexer # lexer = lex.lex() # # Build the parser # yaccer = yacc.yacc() for source_file in source_files: PathName = os.path.realpath(os.path.abspath(os.path.dirname(source_file))) fd = open(source_file, 'r') data = fd.read() fd.close() NumberOfLinesIn = data.count('\n') print 'Consumed file %s with %d lines' % (source_file, NumberOfLinesIn) yaccer.parse(data) process_drivers(TheDrivers) def main(): global Verbose global DriverDump global CodeDump import argparse parser = argparse.ArgumentParser() parser.add_argument("-c", "--code", help="dump code", action="store_true") parser.add_argument("-d", "--driver", help="dump driver", action="store_true") parser.add_argument("-v", "--verbose", help="verbose output", action="store_true") parser.add_argument("driver_source", help="driver source file", nargs="*") args = parser.parse_args() print args if args.code: CodeDump = True else: CodeDump = False if args.driver: DriverDump = True else: DriverDump = False if args.verbose: Verbose = True else: Verbose = False source_files = args.driver_source if source_files: process_source(source_files) if __name__ == "__main__": main()