#!/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=4 sw=4 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 # 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 re import ply.lex as lex import ply.yacc as yacc states = ( ('tcl', 'exclusive'), ) FunctionTypes = [ 'read_function', 'write_function', 'fetch_function', 'check_function', 'pid_function', 'checkrange_function', ] DriveableFunctionTypes = [ 'halt_function', 'checklimits_function', 'checkstatus_function', ] Verbose = False DriverDump = False CodeDump = False PrintedFileName = -1 NumberOfLinesIn = 0 NumberOfLinesOut = 0 SourceFileList = [] SourceLineList = [] def PrintParseError(message): global PrintedFileName global lexer global SourceLineList, SourceData curr_line = lexer.lineno curr_file = SourceLineList[curr_line - 1][0] if curr_file != PrintedFileName: PrintedFileName = curr_file SourceFile = SourceFileList[curr_file] print "in", SourceFile print message print "%4d:" % SourceLineList[curr_line - 1][1], SourceData[curr_line - 1] def PrintPostError(message): global PrintedFileName global SourceLineList curr_file = 0 if curr_file != PrintedFileName: PrintedFileName = curr_file SourceFile = SourceFileList[curr_file] print "in", SourceFile print message # # 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', 'WRAPPER_PROPERTY' : 'WRAPPER_PROPERTY', 'CLASS' : 'CLASS', 'SIMULATION_GROUP' : 'SIMULATION_GROUP', 'DEBUG_THRESHOLD' : 'DEBUG_THRESHOLD', 'CODE' : 'CODE', 'ADD_ARGS' : 'ADD_ARGS', 'MAKE_ARGS' : 'MAKE_ARGS', 'SOBJ_PRIV_TYPE' : 'SOBJ_PRIV_TYPE', 'PROTOCOL_ARGS' : 'PROTOCOL_ARGS', # Group keywords 'GROUP' : 'GROUP', 'GROUP_PROPERTY' : 'GROUP_PROPERTY', # Variable keywords 'VAR' : 'VAR', 'PROPERTY' : 'PROPERTY', 'CONTROL' : 'CONTROL', 'CONDITIONAL' : 'CONDITIONAL', 'DATA' : 'DATA', 'NXSAVE' : 'NXSAVE', 'MUTABLE' : 'MUTABLE', 'READABLE' : 'READABLE', 'WRITEABLE' : 'WRITEABLE', 'DRIVEABLE' : 'DRIVEABLE', 'PERMLINK' : 'PERMLINK', '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', 'PID_FUNCTION' : 'PID_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_STRING1', 'TEXT_STRING2', 'EQUALS', 'ID', 'TCL_BEG', 'TCL_END', 'AT_TCL', 'AT_END', ] + list(reserved.values()) # # Token rules # t_EQUALS = r'=' t_LBRACE = r'{' t_RBRACE = r'}' t_SLASH = r'/' def t_AT_TCL(t): r'@TCL' if Verbose: print 'AT_TCL' t.lexer.begin('tcl') #return t def t_tcl_AT_END(t): r'[ \t]*@END' if Verbose: print 'AT_END' t.lexer.begin('INITIAL') #return t def t_TCL_BEG(t): r'{%%' if Verbose: print 'TCL_BEG' t.lexer.begin('tcl') return t def t_tcl_TCL_END(t): r'.*%%}' if Verbose: print 'TCL_END' t.lexer.begin('INITIAL') return t t_tcl_ignore = "" def t_tcl_CODE_STRING(t): r'.+' if t.value[0] == '@': t.value = t.value[1:] if Verbose: print 'TCL:', t.value return t def t_tcl_newline(t): r'\n+' t.lexer.lineno += t.value.count("\n") def t_tcl_error(t): message = "Illegal tcl character '%s'" % t.value[0] PrintParseError(message) t.lexer.skip(1) def t_TEXT_STRING1(t): r'\'[^\']*\'' t.value = t.value[1:-1] return t def t_TEXT_STRING2(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: message = "Floating value invalid: " + repr(t.value) PrintParseError(message) t.value = 0.0 return t def t_INTEGER(t): r'-?\d+' try: t.value = int(t.value) except ValueError: message = "Integer value too large: " + repr(t.value) PrintParseError(message) 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): message = "Illegal character '%s' at line %d" % \ (t.value[0], t.lexer.lineno) PrintParseError(message) 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 | wrapper_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 | ADD_ARGS EQUALS text_string | MAKE_ARGS EQUALS text_string | SOBJ_PRIV_TYPE EQUALS text_string | PROTOCOL_ARGS EQUALS text_string | DEBUG_THRESHOLD EQUALS value ''' p[0] = { p[1] : p[3] } # # The GROUP block # def p_group(p): ''' group : named_group | unnamed_group ''' p[0] = p[1] def p_named_group(p): ''' named_group : GROUP group_id EQUALS LBRACE group_statement_list RBRACE ''' p[0] = { 'Group' : [{'name': p[2]}] + p[5] } def p_unnamed_group(p): ''' unnamed_group : GROUP EQUALS LBRACE group_statement_list RBRACE ''' p[0] = { 'Group' : [{'name': None}] + p[4] } def p_group_id(p): ''' group_id : id_or_str ''' 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 | group ''' 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 EQUALS LBRACE RBRACE | VAR id_or_str ''' if len(p) > 6: 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 | variable | group ''' 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 | PID_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 | CONDITIONAL EQUALS text_string ''' 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 value | UPPERLIMIT EQUALS value | TOLERANCE EQUALS value | PERMLINK EQUALS text_string | 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_wrapper_property(p): ''' wrapper_property : WRAPPER_PROPERTY id_or_str EQUALS value ''' p[0] = { 'WrapperProperty' : ( 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 | CODE code_type id_or_str EQUALS LBRACE tcl_code_block RBRACE | CODE code_type id_or_str EQUALS TCL_BEG code_block TCL_END ''' if p[3].lower() == 'preamble': name = 'preamble' elif p[3].lower() == 'postamble': name = 'postamble' elif p[3].lower() == 'mkdriver': name = 'mkDriver' elif p[3].lower() == 'mkwrapper': name = 'mkWrapper' 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 | PID_FUNCTION | CHECKRANGE_FUNCTION | CHECKLIMITS_FUNCTION | CHECKSTATUS_FUNCTION | HALT_FUNCTION | empty ''' p[0] = p[1] def p_tcl_code_block(p): ''' tcl_code_block : AT_TCL code_block AT_END ''' p[0] = p[2] 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_text_string(p): ''' text_string : TEXT_STRING1 | TEXT_STRING2 ''' p[0] = p[1] def p_empty(p): ''' empty : ''' pass def p_error(t): message = "Syntax error at line %d" % lexer.lineno message += " " + repr(t) message += " " + repr(t.value) PrintParseError(message) # # Utility functions # def make_path(MyVar, ch = '_'): path = MyVar['path'] if len(path) > 0: path = path.replace('/', ch) path += ch path += MyVar['name'] return path def generate_filename(MyDriver): global args old_name = "sct_%s.tcl" % MyDriver['name'] new_name = "%s_sct.tcl" % MyDriver['name'] if 'PathName' in MyDriver: full_old_name = os.path.join(MyDriver['PathName'], old_name) full_new_name = os.path.join(MyDriver['PathName'], new_name) else: full_old_name = old_name full_new_name = new_name # This block of code generates shell commands to help the old->new rename if Move: fd = open("git_mv.sh", "a") fd.write( "git mv " + full_old_name + " " + full_new_name + "\n") fd.close() fd = open("grep_sed.sh", "a") fd.write( "for f in $(grep " + old_name + " instrument -Irl" + "); do\n") fd.write( " echo ${f}\n") fd.write( " sed -i ${f} -e 's/" + old_name + "/" + new_name + "/'\n") fd.write( "done\n") fd.close() if args.sct == "before": MyDriver['filename'] = old_name MyDriver['fullname'] = full_old_name else: MyDriver['filename'] = new_name MyDriver['fullname'] = full_new_name def parse_args(arg_str): ''' Parse the TCL argument string into a list of identifiers (in order) plus a map of identifier to default value (or None) returned as a 2-tuple ''' arg_list = re.findall(r'({[^}]+}|[A-Za-z0-9_]+)', arg_str) arg_lst = [] arg_map = {} for arg in arg_list: if arg.startswith('{'): bits = arg[1:-1].split(None, 1) arg_lst.append(bits[0]) arg_map[bits[0]] = bits[1] else: arg_lst.append(arg) arg_map[arg] = None return (arg_lst, arg_map) # # 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'] = 'true' 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 = {} MyVar['Property'] = {} MyVar['Group'] = {} MyVar['Variable'] = {} # Copy items for this variable for item in p: if Verbose: print "Variable Item:", item for key in item.keys(): if key == 'Property': MyVar['Property'][item[key][0]] = item[key][1] elif key == 'Group': pass # process nested groups at the bottom elif key == 'Variable': pass # process nested variables at the bottom else: MyVar[key] = item[key] # copy the defaults for missing items for key in ContextStack[ContextIndex]: if key == '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 'sdsinfo' not in MyVar['Property']: MyVar['Property']['sdsinfo'] = '::nexus::scobj::sdsinfo' # set the type if not explicitly set if 'type' not in MyVar['Property']: if 'driveable' in MyVar and MyVar['driveable']: MyVar['Property']['type'] = 'drivable' else: MyVar['Property']['type'] = 'part' # 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 message = 'Error: Function type mismatch: var = ' + str(MyVar) + ', code = ' + str(MyDriver['Funcs'][MyVar[func]]) + ', func = ' + str(func) PrintPostError(message) MyDriver['Funcs'][MyVar[func]]['reference_count'] += 1 if 'permlink' in MyVar: device_type, node_type = MyVar['permlink'].split('.') if node_type not in MyDriver['Permlink']: MyDriver['Permlink'][node_type] = [] MyDriver['Permlink'][node_type] += [make_path(MyVar)] # Process the nested groups for item in p: for key in item.keys(): if key == 'Group': if Verbose: print "SubGroup Item:", item[key] push_context() if len(ContextStack[ContextIndex]['path']) > 0: ContextStack[ContextIndex]['path'] += '/' ContextStack[ContextIndex]['path'] += MyVar['name'] MyVar['Group'][item[key][0]['name']] = build_group(MyDriver, item[key]) pop_context() # Process the nested variables for item in p: for key in item.keys(): if key == 'Variable': if Verbose: print "SubVariable Item:", item[key] push_context() if len(ContextStack[ContextIndex]['path']) > 0: ContextStack[ContextIndex]['path'] += '/' ContextStack[ContextIndex]['path'] += MyVar['name'] MyVar['Variable'][item[key][0]['name']] = build_variable(MyDriver, item[key]) pop_context() 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'] MyGroup['path'] = ContextStack[ContextIndex]['path'] for item in p: if 'Variable' in item: MyVar = build_variable(MyDriver, item['Variable']) MyGroup['Vars'][MyVar['name']] = MyVar elif 'Group' in item: MySubGroup = build_group(MyDriver, item['Group']) MyGroup['Groups'][MySubGroup['name']] = MySubGroup 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() adjust_group(MyGroup) return MyGroup def adjust_group(MyGroup): if Verbose: print 'ante adjust_group', MyGroup MyData = None for var in MyGroup['Vars']: if Verbose: print "Var:", MyGroup['Vars'][var] if 'data' in MyGroup['Vars'][var]: if MyGroup['Vars'][var]['data'] == 'true': MyData = 'true' if 'klass' not in MyGroup['Vars'][var]['Property']: MyGroup['Vars'][var]['Property']['klass'] = 'parameter' else: MyData = 'false' if MyData is None: for grp in MyGroup['Groups']: if Verbose: print "Grp:", MyGroup['Groups'][grp] adjust_group(MyGroup['Groups'][grp]) if 'data' in MyGroup['Groups'][grp]['GroupProperty']: if MyGroup['Groups'][grp]['GroupProperty']['data'] == 'true': MyData = 'true' else: MyData = 'false' break if MyData is not None: if 'GroupProperty' not in MyGroup: MyGroup['GroupProperty'] = {} if 'data' not in MyGroup['GroupProperty']: MyGroup['GroupProperty']['data'] = MyData if MyData: if 'klass' not in MyGroup['GroupProperty']: MyGroup['GroupProperty']['klass'] = '@none' if 'type' not in MyGroup['GroupProperty']: MyGroup['GroupProperty']['type'] = 'part' if Verbose: print 'post adjust_group', MyGroup def check_func_code(MyDriver): for name in MyDriver['Funcs']: #print name left_paren_count = 0 right_paren_count = 0 left_brack_count = 0 right_brack_count = 0 left_brace_count = 0 right_brace_count = 0 for idx, line in enumerate(MyDriver['Funcs'][name]['text']): #print "%4d:" % (idx + 1), line left_paren_count += line.count('(') right_paren_count += line.count(')') left_brack_count += line.count('[') right_brack_count += line.count(']') left_brace_count += line.count('{') right_brace_count += line.count('}') if left_paren_count != right_paren_count: PrintPostError("Warning: Mismatched Parens in function %s (%d != %d)" % (name, left_paren_count, right_paren_count)) if left_brack_count != right_brack_count: PrintPostError("Warning: Mismatched Brackets in function %s (%d != %d)" % (name, left_brack_count, right_brack_count)) if left_brace_count != right_brace_count: PrintPostError("Warning: Mismatched Braces in function %s (%d != %d)" % (name, left_brace_count, right_brace_count)) 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 if 'WrapperProperty' in item: if 'WrapperProperty' not in MyDriver: MyDriver['WrapperProperty'] = {} MyDriver['WrapperProperty'][item['WrapperProperty'][0]] = item['WrapperProperty'][1] continue for key in item: MyDriver[key] = item[key] for item in MyDriver['Permlink']: if len(MyDriver['Permlink'][item]) > 1: message = 'Error: duplicate permlink entries for "%s"' % item message += " " + repr(MyDriver['Permlink'][item]) PrintPostError(message) if 'add_args' in MyDriver: MyDriver['add_args_lst'], MyDriver['add_args_map'] = parse_args(MyDriver['add_args']) if Verbose: print "ADD_ARGS:", MyDriver['add_args'] arg_map = MyDriver['add_args_map'] for arg in arg_map: print ' %s:' % arg, arg_map[arg] if 'make_args' in MyDriver: MyDriver['make_args_lst'], MyDriver['make_args_map'] = parse_args(MyDriver['make_args']) if Verbose: print "MAKE_ARGS:", MyDriver['make_args'] arg_map = MyDriver['make_args_map'] for arg in arg_map: print ' %s:' % arg, arg_map[arg] if 'protocol_args' in MyDriver: MyDriver['protocol_args_lst'], MyDriver['protocol_args_map'] = parse_args(MyDriver['protocol_args']) if Verbose: print "PROTOCOL_ARGS:", MyDriver['protocol_args'] arg_map = MyDriver['protocol_args_map'] for arg in arg_map: print ' %s:' % arg, arg_map[arg] if 'add_args_lst' in MyDriver and 'make_args_lst' in MyDriver: if MyDriver['add_args_lst'] != MyDriver['make_args_lst']: print "Add_Args:", MyDriver['add_args_lst'] print "Make_Args:", MyDriver['make_args_lst'] if Verbose: print "MyDriver:", MyDriver # # Driver Dump Functions # def dump_driver_vars(vars, indent): global FunctionTypes global DriveableFunctionTypes for item in sorted(vars): print indent + 'VAR %s = {' % item Comments = ['name', 'path'] Deferred = ['Property', 'Variable', 'Group'] + Comments + FunctionTypes + DriveableFunctionTypes for Comment in sorted(Comments): if Comment in vars[item]: print indent + ' # %s = \'%s\'' % (Comment, vars[item][Comment]) for subitem in sorted([i for i in vars[item] if i not in Deferred]): print indent + ' %s = \'%s\'' % (subitem, vars[item][subitem]) for subitem in sorted([i for i in vars[item] if i in FunctionTypes]): print indent + ' %s = \'%s\'' % (subitem, vars[item][subitem]) for subitem in sorted([i for i in vars[item] if i in DriveableFunctionTypes]): print indent + ' %s = \'%s\'' % (subitem, vars[item][subitem]) for subitem in sorted([i for i in vars[item]['Property']]): print indent + ' Property \'%s\' = \'%s\'' % (subitem, vars[item]['Property'][subitem]) # Dump the nested groups and vars dump_driver_groups(vars[item]['Group'], indent + ' ') dump_driver_vars(vars[item]['Variable'], indent + ' ') print indent + '}' def dump_driver_groups(groups, indent): for item in sorted(groups): if item: print indent + 'GROUP ' + item + ' = {' else: print indent + 'GROUP = {' Comments = ['name', 'path'] Deferred = ['Groups', 'Vars', 'GroupProperty'] + Comments for Comment in sorted(Comments): if Comment in groups[item]: print indent + ' # %s = \'%s\'' % (Comment, groups[item][Comment]) for subitem in sorted([x for x in groups[item] if not x in Deferred]): print indent + ' ', subitem, '= \'%s\'' % groups[item][subitem] if 'GroupProperty' in groups[item]: for subitem in groups[item]['GroupProperty']: print indent + ' GroupProperty', subitem, '= \'%s\'' % groups[item]['GroupProperty'][subitem] dump_driver_vars(groups[item]['Vars'], indent + ' ') dump_driver_groups(groups[item]['Groups'], indent + ' ') print indent + '}' 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'] + ' = {' Comments = ['PathName', 'Permlink'] Comments += ['add_args_lst'] Comments += ['add_args_map'] Comments += ['make_args_lst'] Comments += ['make_args_map'] Comments += ['protocol_args_lst'] Comments += ['protocol_args_map'] Deferred = ['Groups', 'Funcs', 'Deferred', 'name'] + Comments for Comment in sorted(Comments): if Comment in MyDriver: print '# %s = \'%s\'' % (Comment, MyDriver[Comment]) for item in sorted([x for x in MyDriver if x not in Deferred]): print ' ' + item + ' =', '\'%s\'' % 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: ft=tcl tabstop=8 softtabstop=2 shiftwidth=2 nocindent smartindent'] txt += ['#'] txt += [''] txt += ['namespace eval %s {' % MyDriver['namespace']] txt += [' set debug_threshold %s' % str( MyDriver['debug_threshold'])] if len(MyDriver['Permlink']) > 0: if 'make_args_lst' in MyDriver and 'id' in MyDriver['make_args_lst']: pass else: txt += [' if { ![info exists ::scobj::permlink_device_counter]} {'] txt += [' set ::scobj::permlink_device_counter 0'] txt += [' }'] func = 'preamble' if func in MyDriver['Funcs']: txt += ['# %s hook code starts' % func] txt += MyDriver['Funcs'][func]['text'] txt += ['# %s hook code ends' % func] txt += ['}'] txt += [''] txt += ['proc %s::debug_log {tc_root debug_level debug_string} {' % MyDriver['namespace']] txt += [' set catch_status [ catch {'] txt += [' set debug_threshold [hgetpropval ${tc_root} debug_threshold]'] txt += [' if {${debug_level} >= ${debug_threshold}} {'] txt += [' set now [clock seconds]'] txt += [' set ts [clock format ${now} -format "%Y%m%d"]'] txt += [' set log_file_name "../log/%s_[basename ${tc_root}]_${ts}.log"' % MyDriver['name']] txt += [' set fd [open "${log_file_name}" "a"]'] txt += [' set ts [clock format ${now} -format "%T"]'] txt += [' puts ${fd} "${ts} ${debug_string}"'] txt += [' close ${fd}'] txt += [' }'] txt += [' } catch_message ]'] txt += ['}'] txt += [''] txt += ['proc %s::sics_log {debug_level debug_string} {' % MyDriver['namespace']] txt += [' set catch_status [ catch {'] txt += [' set debug_threshold ${%s::debug_threshold}' % MyDriver['namespace']] txt += [' if {${debug_level} >= ${debug_threshold}} {'] txt += [' sicslog "%s::${debug_string}"' % MyDriver['namespace']] txt += [' }'] txt += [' } catch_message ]'] 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 += [' set catch_status [ catch {'] txt += [' debug_log ${tc_root} 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 += ['# %s hook code starts' % func] txt += MyDriver['Funcs'][func]['text'] txt += ['# %s hook code ends' % func] txt += [' if { [hpropexists [sct] geterror] } {'] txt += [' debug_log ${tc_root} 9 "[sct] error: [sct geterror]"'] txt += [' error "[sct geterror]"'] txt += [' }'] else: txt += ['# %s hook code goes here' % func] txt += [' if { [hpropexists [sct] driving] } {'] txt += [' if { [hpropexists [sct] writestatus] && [sct writestatus] == "start" } {'] txt += [' sct driving 1'] txt += [' }'] txt += [' }'] txt += [' debug_log ${tc_root} 1 "%s sct send ${cmd}"' % func] txt += [' if {![string equal -nocase -length 10 ${cmd} "@@NOSEND@@"]} {'] txt += [' sct send "${cmd}"'] txt += [' }'] txt += [' return ${nextState}'] txt += [' } catch_message ]'] txt += [' handle_exception ${catch_status} ${catch_message}'] 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 += [' set catch_status [ catch {'] txt += [' debug_log ${tc_root} 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 += ['# %s hook code starts' % func] txt += MyDriver['Funcs'][func]['text'] txt += ['# %s hook code ends' % func] else: txt += ['# %s hook code goes here' % func] txt += [' return "idle"'] txt += [' } catch_message ]'] txt += [' handle_exception ${catch_status} ${catch_message}'] 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 += [' set catch_status [ catch {'] txt += [' debug_log ${tc_root} 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 += ['# %s hook code starts' % func] txt += MyDriver['Funcs'][func]['text'] txt += ['# %s hook code ends' % func] txt += [' if { [hpropexists [sct] geterror] } {'] txt += [' debug_log ${tc_root} 9 "[sct] error: [sct geterror]"'] txt += [' error "[sct geterror]"'] txt += [' }'] else: txt += ['# %s hook code goes here' % func] txt += [' debug_log ${tc_root} 1 "%s sct send ${cmd}"' % func] txt += [' if {![string equal -nocase -length 10 ${cmd} "@@NOSEND@@"]} {'] txt += [' sct send "${cmd}"'] txt += [' }'] txt += [' return ${nextState}'] txt += [' } catch_message ]'] txt += [' handle_exception ${catch_status} ${catch_message}'] 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 += [' set catch_status [ catch {'] txt += [' debug_log ${tc_root} 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 += [' error "[sct geterror]"'] txt += [' }'] if func in MyDriver['Funcs'] and len(MyDriver['Funcs'][func]['text']) > 0: txt += ['# %s hook code starts' % func] txt += MyDriver['Funcs'][func]['text'] txt += ['# %s hook code ends' % func] txt += [' if { [hpropexists [sct] geterror] } {'] txt += [' debug_log ${tc_root} 9 "[sct] error: [sct geterror]"'] txt += [' error "[sct geterror]"'] txt += [' }'] else: txt += ['# %s hook code goes here' % func] txt += [' if { ${data} != [sct oldval] } {'] txt += [' debug_log ${tc_root} 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 += [' } catch_message ]'] txt += [' handle_exception ${catch_status} ${catch_message}'] 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 += [' set catch_status [ catch {'] txt += [' debug_log ${tc_root} 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 += ['# %s hook code starts' % func] txt += MyDriver['Funcs'][func]['text'] txt += ['# %s hook code ends' % func] else: txt += ['# %s hook code goes here' % func] txt += [' if { ${setpoint} < ${lolimit} || ${setpoint} > ${hilimit} } {'] txt += [' error "setpoint ${setpoint} violates limits (${lolimit}..${hilimit}) on [sct]"'] txt += [' }'] txt += [' return OK'] txt += [' } catch_message ]'] txt += [' handle_exception ${catch_status} ${catch_message}'] 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 += [' set catch_status [ catch {'] txt += [' debug_log ${tc_root} 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 += ['# %s hook code starts' % func] txt += MyDriver['Funcs'][func]['text'] txt += ['# %s hook code ends' % func] else: txt += ['# %s hook code goes here' % func] 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 += [' } catch_message ]'] txt += [' handle_exception ${catch_status} ${catch_message}'] 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)] txt += [' set catch_status [ catch {'] if func in MyDriver['Funcs'] and len(MyDriver['Funcs'][func]['text']) > 0: txt += ['# %s hook code starts' % func] txt += MyDriver['Funcs'][func]['text'] txt += ['# %s hook code ends' % func] else: txt += ['# %s hook code goes here' % func] txt += [' if {[sct driving]} {'] txt += [' set sp "[sct target]"'] txt += [' if {[hpropexists [sct] simulated] && [sct simulated] == "true"} {'] txt += [' set pv "${sp}"'] txt += [' hupdateif ${tc_root}/[sct driveable] ${sp}'] txt += [' } else {'] txt += [' set pv "[hval ${tc_root}/[sct driveable]]"'] txt += [' }'] txt += [' if { abs(${pv} - ${sp}) <= [sct tolerance] } {'] txt += [' if { [hpropexists [sct] settle_time] } {'] txt += [' if { [hpropexists [sct] settle_time_start] } {'] txt += [' if { [sct utime] - [sct settle_time_start] >= [sct settle_time]} {'] txt += [' sct driving 0'] txt += [' return "idle"'] txt += [' }'] txt += [' return "busy"'] txt += [' } else {'] txt += [' sct utime settle_time_start'] txt += [' return "busy"'] txt += [' }'] txt += [' }'] txt += [' sct driving 0'] txt += [' return "idle"'] txt += [' }'] txt += [' if { [hpropexists [sct] settle_time_start] } {'] txt += [' hdelprop [sct] settle_time_start'] txt += [' }'] txt += [' return "busy"'] txt += [' } else {'] txt += [' return "idle"'] txt += [' }'] txt += [' } catch_message ]'] txt += [' handle_exception ${catch_status} ${catch_message}'] 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 += [' set catch_status [ catch {'] txt += [' debug_log ${tc_root} 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 += ['# %s hook code starts' % func] txt += MyDriver['Funcs'][func]['text'] txt += ['# %s hook code ends' % func] else: txt += ['# %s hook code goes here' % func] txt += [' sct driving 0'] txt += [' return "idle"'] txt += [' } catch_message ]'] txt += [' handle_exception ${catch_status} ${catch_message}'] txt += ['}'] emit(txt) def put_pid_function(MyDriver, func): txt = [''] txt += ['# pid function for PID control'] txt += ['proc %s::%s {tc_root sp pv} {' % (MyDriver['namespace'], func)] txt += [' set catch_status [ catch {'] txt += [' debug_log ${tc_root} 1 "%s tc_root=${tc_root} sct=[sct] pv=${pv} sp=${sp}"' % func] txt += [' sct pid_error [expr {${sp} - ${pv}}]'] txt += [' set p_value [expr {[sct pid_pvalue] * [sct pid_error]}]'] txt += [' set d_value [expr {[sct pid_dvalue] * (${pv} - [sct oldval])}]'] txt += [' sct pid_deriv [sct pid_error]'] txt += [' sct pid_integ [expr {[sct pid_integ] + [sct pid_error] * [sct pid_ivalue]}]'] txt += [' if { [sct pid_integ] > [sct pid_imax] } {'] txt += [' sct pid_integ [sct pid_imax]'] txt += [' }'] txt += [' if { [sct pid_integ] < -[sct pid_imax] } {'] txt += [' sct pid_integ -[sct pid_imax]'] txt += [' }'] txt += [' set i_value [sct pid_integ]'] txt += [' set pid [expr {${p_value} + ${i_value} + ${d_value}}]'] if func in MyDriver['Funcs'] and len(MyDriver['Funcs'][func]['text']) > 0: txt += ['# %s hook code starts' % func] txt += MyDriver['Funcs'][func]['text'] txt += ['# %s hook code ends' % func] else: txt += ['# %s hook code goes here' % func] txt += [' sct pid_output ${pid}'] txt += [' } catch_message ]'] txt += [' handle_exception ${catch_status} ${catch_message}'] txt += [' return ${pid}'] txt += ['}'] emit(txt) def put_var(MyDriver, MyGroup, MyVar): readable_or_writeable = False txt = [] postfix = [] nodename = make_path(MyVar, '/') # Debugging #txt += ['# path = ' + MyVar['path']] #txt += ['# name = ' + nodename] # Check driveable attributes are present if required if 'driveable' in MyVar and 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] # Check PID attributes are present if required if 'pid_function' in MyVar: for attr in ('pid_pvalue', 'pid_ivalue', 'pid_dvalue', 'pid_imax', 'pid_error', 'pid_deriv', 'pid_integ', ): if attr not in MyVar['Property']: msg = 'PID: %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'] if 'read_command' in MyVar: read_command = MyVar['read_command'] else: 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 'permlink' in MyVar: device_type, node_type = MyVar['permlink'].split('.') if device_type.startswith("#"): if 'make_args_lst' in MyDriver and 'permlink' in MyDriver['make_args_lst']: idx = int(device_type[1:]) device_type = '[string index ${permlink} %d]' % idx else: message = 'Error: permlink required in make_ags' PrintPostError(message) if 'make_args_lst' in MyDriver and 'id' in MyDriver['make_args_lst']: permlink = device_type + '[format "%02d" ${id}]' + node_type else: permlink = device_type + '${permlink_device_number}' + node_type txt += [' hsetprop ${scobj_hpath}/%s permlink data_set "%s"' % (nodename, permlink)] txt += [' hsetprop ${scobj_hpath}/%s @description "%s"' % (nodename, permlink)] 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] for key in sorted(MyVar['Property']): txt += [' hsetprop ${scobj_hpath}/%s %s "%s"' % (nodename, key, MyVar['Property'][key])] # Generate __ at runtime for nxalias if 'nxalias' not in MyVar['Property']: nxalias = '${name}_' + make_path(MyVar) txt += [' hsetprop ${scobj_hpath}/%s nxalias "%s"' % (nodename, nxalias)] if readable_or_writeable: txt += [''] txt += [' if {[string equal -nocase "${simulation_flag}" "false"]} {'] if MyVar['readable'] > 0: poll_period = MyVar['readable'] if poll_period < 1: poll_period = 1 if poll_period > 3600: poll_period = 3600 txt += [' ${sct_controller} poll ${scobj_hpath}/%s %s' % (nodename, poll_period)] if MyVar['writeable'] > 0 or MyVar['driveable']: txt += [' ${sct_controller} write ${scobj_hpath}/%s' % nodename] if MyVar['driveable']: # Generate __ at runtime for driveable driveable = '${name}_' + make_path(MyVar) postfix += [' ansto_makesctdrive %s ${scobj_hpath}/%s ${scobj_hpath}/%s ${sct_controller}' % (driveable, nodename, MyVar['driveable'])] txt += [' hsetprop ${scobj_hpath}/%s simulated false' % nodename] txt += [' } else {'] txt += [' %s::sics_log 9 "simulation_flag=${simulation_flag} => No poll/write for %s"' % (MyDriver['namespace'], MyDriver['name'])] txt += [' hsetprop ${scobj_hpath}/%s simulated true' % nodename] txt += [' }'] # Process nested groups for grp in sorted(MyVar['Group']): txt += [''] infix = put_group(MyDriver, MyVar['Group'][grp]) txt += infix # Process nested variables for var in sorted(MyVar['Variable']): txt += [''] MySubVar = MyVar['Variable'][var] infix, dfr = put_var(MyDriver, MyGroup, MySubVar) txt += infix postfix += dfr if 'conditional' in MyVar: for idx, line in enumerate(txt): if len(line) > 0: txt[idx] = ' ' + line txt.insert(0, ' if {%s} {' % MyVar['conditional']) txt.append(' }') if len(postfix) > 0: for idx, line in enumerate(postfix): if len(line) > 0: postfix[idx] = ' ' + line postfix.insert(0, ' if {%s} {' % MyVar['conditional']) postfix.append(' }') return (txt, postfix) def put_group(MyDriver, MyGroup): txt = [] postfix = [] if MyGroup['name']: txt += [''] txt += [' hfactory ${scobj_hpath}/%s plain spy none' % MyGroup['path']] else: pass for var in sorted(MyGroup['Vars']): txt += [''] MyVar = MyGroup['Vars'][var] infix, dfr = put_var(MyDriver, MyGroup, MyVar) txt += infix postfix += dfr if MyGroup['name']: if 'GroupProperty' in MyGroup: for key in sorted(MyGroup['GroupProperty']): txt += [' hsetprop ${scobj_hpath}/%s %s "%s"' % (MyGroup['path'], key, MyGroup['GroupProperty'][key])] elif len(MyGroup['path']) > 0: pass else: if 'GroupProperty' in MyGroup: txt += [''] for key in sorted(MyGroup['GroupProperty']): txt += [' hsetprop ${scobj_hpath} %s "%s"' % (key, MyGroup['GroupProperty'][key])] for grp in sorted(MyGroup['Groups']): txt += put_group(MyDriver, MyGroup['Groups'][grp]) txt += postfix if 'conditional' in MyGroup: for idx, line in enumerate(txt): if len(line) > 0: txt[idx] = ' ' + line if len(txt[0]) == 0: txt.pop(0) txt.insert(0, ' if {%s} {' % MyGroup['conditional']) txt.insert(0, '') txt.append(' }') return txt def put_mkDriver(MyDriver): txt = [''] if 'make_args' in MyDriver: line = 'proc %s::mkDriver { sct_controller name device_class simulation_flag ip_address tcp_port %s } {' % (MyDriver['namespace'], MyDriver['make_args']) else: line = 'proc %s::mkDriver { sct_controller name device_class simulation_flag ip_address tcp_port } {' % (MyDriver['namespace']) txt += [line] if 'make_args_lst' in MyDriver: make_args = ' '.join(["${%s}"%arg for arg in MyDriver['make_args_lst']]) txt += [' %s::sics_log 9 "%s::mkDriver ${sct_controller} ${name} ${device_class} ${simulation_flag} ${ip_address} ${tcp_port} %s"' % (MyDriver['namespace'], MyDriver['namespace'], make_args)] else: txt += [' %s::sics_log 9 "%s::mkDriver ${sct_controller} ${name} ${device_class} ${simulation_flag} ${ip_address} ${tcp_port}"' % (MyDriver['namespace'], MyDriver['namespace'])] txt += [' set ns "[namespace current]"'] txt += [' set catch_status [ catch {'] txt += [''] func = 'mkWrapper' if func in MyDriver['Funcs']: txt += ['# %s hook code starts' % func] txt += MyDriver['Funcs'][func]['text'] txt += ['# %s hook code ends' % func] else: if len(MyDriver['Permlink']) > 0: if 'make_args_lst' in MyDriver and 'id' in MyDriver['make_args_lst']: pass else: txt += [' set permlink_device_number [format "%02d" [incr ::scobj::permlink_device_counter]]'] txt += [''] if 'sobj_priv_type' in MyDriver: priv_type = MyDriver['sobj_priv_type'].split() ms_line = ' MakeSICSObj ${name} SCT_OBJECT %s %s' % (priv_type[0], priv_type[1]) else: ms_line = ' MakeSICSObj ${name} SCT_OBJECT' txt += [ms_line] txt += [''] txt += [' sicslist setatt ${name} driver %s' % MyDriver['name']] txt += [' sicslist setatt ${name} klass ${device_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}'] for group in sorted(MyDriver['Groups']): txt += put_group(MyDriver, MyDriver['Groups'][group]) txt += [' hsetprop ${scobj_hpath} driver %s' % MyDriver['name']] txt += [' hsetprop ${scobj_hpath} klass ${device_class}'] txt += [' hsetprop ${scobj_hpath} data true'] txt += [' hsetprop ${scobj_hpath} debug_threshold %s' % str(MyDriver['debug_threshold'])] if len(MyDriver['Deferred']) > 0: txt += [' if {[string equal -nocase "${simulation_flag}" "false"]} {'] for line in MyDriver['Deferred']: txt += [' ' + line] txt += [' }'] func = 'mkDriver' if func in MyDriver['Funcs']: txt += ['# %s hook code starts' % func] txt += MyDriver['Funcs'][func]['text'] txt += ['# %s hook code ends' % func] else: txt += ['# %s hook code goes here' % func] txt += [' } catch_message ]'] txt += [' handle_exception ${catch_status} ${catch_message}'] txt += ['}'] emit(txt) def put_add_driver(MyDriver): txt = [''] if 'add_args' in MyDriver: line = 'proc %s::add_driver {name device_class simulation_flag ip_address tcp_port %s} {' % (MyDriver['namespace'], MyDriver['add_args']) else: line = 'proc %s::add_driver {name device_class simulation_flag ip_address tcp_port} {' % (MyDriver['namespace']) txt += [line] txt += [' set catch_status [ catch {'] if 'make_args_lst' in MyDriver: make_args = ' '.join(["${%s}"%arg for arg in MyDriver['make_args_lst']]) txt += [' %s::sics_log 9 "%s::add_driver ${name} ${device_class} ${simulation_flag} ${ip_address} ${tcp_port} %s"' % (MyDriver['namespace'], MyDriver['namespace'], make_args)] else: txt += [' %s::sics_log 9 "%s::add_driver ${name} ${device_class} ${simulation_flag} ${ip_address} ${tcp_port}"' % (MyDriver['namespace'], MyDriver['namespace'])] if ('WrapperProperty' in MyDriver) and ('nosctcontroller' in MyDriver['WrapperProperty']): txt += [' %s::sics_log 9 "No sctcontroller for %s"' % (MyDriver['namespace'], MyDriver['name'])] else: txt += [' if {[string equal -nocase "${simulation_flag}" "false"]} {'] txt += [' if {[string equal -nocase "aqadapter" "${ip_address}"]} {'] txt += [' %s::sics_log 9 "makesctcontroller sct_${name} aqadapter ${tcp_port}"' % MyDriver['namespace']] txt += [' makesctcontroller sct_${name} aqadapter ${tcp_port}'] txt += [' } else {'] if 'protocol_args' in MyDriver: protocol_args = [] for arg in MyDriver['protocol_args_lst']: if 'add_args_lst' in MyDriver and arg in MyDriver['add_args_lst']: protocol_args.append('${%s}' % arg) elif arg in MyDriver['protocol_args_map'] and MyDriver['protocol_args_map'][arg] is not None: protocol_args.append(MyDriver['protocol_args_map'][arg]) else: PrintPostError('Protocol arg %s is not in add_args and has no default' % arg) tmp = ' '.join(protocol_args).replace('\\', '\\\\').replace('"', '\\"') txt += [' %s::sics_log 9 "makesctcontroller sct_${name} %s ${ip_address}:${tcp_port} %s"' % (MyDriver['namespace'], MyDriver['protocol'], tmp)] tmp = ' '.join(protocol_args) txt += [' makesctcontroller sct_${name} %s ${ip_address}:${tcp_port} %s' % (MyDriver['protocol'], tmp)] else: txt += [' %s::sics_log 9 "makesctcontroller sct_${name} %s ${ip_address}:${tcp_port}"' % (MyDriver['namespace'], MyDriver['protocol'])] txt += [' makesctcontroller sct_${name} %s ${ip_address}:${tcp_port}' % MyDriver['protocol']] txt += [' }'] txt += [' } else {'] txt += [' %s::sics_log 9 "simulation_flag=${simulation_flag} => Null sctcontroller for %s"' % (MyDriver['namespace'], MyDriver['name'])] txt += [' %s::sics_log 9 "makesctcontroller sct_${name} aqadapter NULL"' % MyDriver['namespace']] txt += [' makesctcontroller sct_${name} aqadapter NULL'] txt += [' }'] if 'make_args_lst' in MyDriver: make_args = ' '.join(["${%s}"%arg for arg in MyDriver['make_args_lst']]) txt += [' %s::sics_log 1 "%s::mkDriver sct_${name} ${name} ${device_class} ${simulation_flag} ${ip_address} ${tcp_port} %s"' % (MyDriver['namespace'], MyDriver['namespace'], make_args)] txt += [' %s::mkDriver sct_${name} ${name} ${device_class} ${simulation_flag} ${ip_address} ${tcp_port} %s' % (MyDriver['namespace'], make_args)] else: txt += [' %s::sics_log 1 "%s::mkDriver sct_${name} ${name} ${device_class} ${simulation_flag} ${ip_address} ${tcp_port}"' % (MyDriver['namespace'], MyDriver['namespace'])] txt += [' %s::mkDriver sct_${name} ${name} ${device_class} ${simulation_flag} ${ip_address} ${tcp_port}' % (MyDriver['namespace'])] # TODO #txt += [' %s::sics_log "makesctemon ${name} /sics/${name}/emon/monmode /sics/${name}/emon/isintol /sics/${name}/emon/errhandler"' % (MyDriver['namespace'])] # txt += [' makesctemon ${name} /sics/${name}/emon/monmode /sics/${name}/emon/isintol /sics/${name}/emon/errhandler'] txt += [' } catch_message ]'] txt += [' handle_exception ${catch_status} ${catch_message}'] txt += ['}'] emit(txt) def put_postamble(MyDriver): txt = [''] txt += ['namespace eval %s {' % MyDriver['namespace']] txt += [' namespace export debug_threshold'] txt += [' namespace export debug_log'] txt += [' namespace export sics_log'] txt += [' namespace export mkDriver'] txt += [' namespace export add_driver'] func = 'postamble' if func in MyDriver['Funcs']: txt += ['# %s hook code starts' % func] txt += MyDriver['Funcs'][func]['text'] txt += ['# %s hook code ends' % func] txt += ['}'] txt += [''] if 'add_args' in MyDriver: line = 'proc add_%s {name ip_address tcp_port %s} {' % (MyDriver['name'], MyDriver['add_args']) else: line = 'proc add_%s {name ip_address tcp_port} {' % MyDriver['name'] txt += [line] txt += [' set simulation_flag "[string tolower [SplitReply [%s]]]"' % MyDriver['simulation_group']] line = ' %s::add_driver ${name} "%s"' % (MyDriver['namespace'], MyDriver['class']) for arg in ['simulation_flag', 'ip_address', 'tcp_port']: line += ' ${%s}' % arg if 'add_args_lst' in MyDriver: for arg in MyDriver['add_args_lst']: line += ' ${%s}' % arg txt += [line] txt += ['}'] txt += [''] txt += ['clientput "file evaluation of %s"' % MyDriver['filename']] txt += ['%s::sics_log 9 "file evaluation of %s"' % (MyDriver['namespace'], MyDriver['filename'])] emit(txt) def put_read_config(MyDriver): txt = [''] txt += ['proc %s::read_config {} {' % MyDriver['namespace']] txt += [' set catch_status [ catch {'] txt += [' set ns "%s"' % MyDriver['namespace']] txt += [' dict for {k u} $::config_dict {'] txt += [' if { [dict exists $u "implementation"] } {'] txt += [' set simulation_flag "[string tolower [SplitReply [%s]]]"' % MyDriver['simulation_group']] txt += [' set device_class "%s"' % MyDriver['class']] txt += [' if { !([dict exists $u "name"] && [dict exists $u "enabled"]) } {'] txt += [' continue'] txt += [' }'] txt += [' set enabled [string tolower [dict get $u "enabled"]]'] txt += [' if { ! ([string equal -nocase $enabled "true" ] || [string equal -nocase $enabled "always"]) } {'] txt += [' continue'] txt += [' }'] txt += [' if { [dict exists $u "simulation_group"] } {'] txt += [' set simulation_flag [SplitReply [[string tolower [dict get $u "simulation_group"]]]]'] txt += [' }'] txt += [' if { [dict exists $u "device_class"] } {'] txt += [' set device_class "[dict get $u "device_class"]"'] txt += [' }'] txt += [' set name [dict get $u name]'] txt += [' set implementation [dict get $u "implementation"]'] txt += [' if { !([dict exists $::config_dict $implementation]) } {'] txt += [' continue'] txt += [' }'] txt += [' set v [dict get $::config_dict $implementation]'] txt += [' if { !([dict exists $v "driver"]) } {'] txt += [' continue'] txt += [' }'] txt += [' if { [string equal -nocase [dict get $v "driver"] "%s"] } {' % MyDriver['name']] if ('WrapperProperty' in MyDriver) and ('nosctcontroller' in MyDriver['WrapperProperty']): txt += [' %s::sics_log 9 "No sctcontroller for %s"' % (MyDriver['namespace'], MyDriver['name'])] txt += [' set ip_address [dict get $v ip]'] txt += [' set tcp_port [dict get $v port]'] else: txt += [' if { ![string equal -nocase "${simulation_flag}" "false"] } {'] txt += [' set asyncqueue "null"'] txt += [' ${ns}::sics_log 9 "simulation_flag=${simulation_flag} => using null asyncqueue"'] txt += [' ${ns}::sics_log 9 "makesctcontroller sct_${name} aqadapter NULL"'] txt += [' makesctcontroller sct_${name} aqadapter NULL'] txt += [' } elseif { [dict exists $v "asyncqueue"] } {'] txt += [' set asyncqueue [dict get $v "asyncqueue"]'] txt += [' if { [string equal -nocase ${asyncqueue} "sct"] } {'] txt += [' set ip_address [dict get $v ip]'] txt += [' set tcp_port [dict get $v port]'] if 'protocol_args_lst' in MyDriver: txt += [' set arg_list [list]'] txt += [' set missing_list [list]'] default_list = [] for arg in [key for key in MyDriver['protocol_args_lst'] if MyDriver['protocol_args_map'][key] is not None]: default_list += [arg, MyDriver['protocol_args_map'][arg]] if len(default_list) > 0: txt += [' array unset default_map'] txt += [' array set default_map [list %s]' % ' '.join(default_list)] txt += [' foreach arg {' + ' '.join(MyDriver['protocol_args_lst']) + '} {'] txt += [' if {[dict exists $u $arg]} {'] txt += [' lappend arg_list "[dict get $u $arg]"'] txt += [' } elseif {[dict exists $v $arg]} {'] txt += [' lappend arg_list "[dict get $v $arg]"'] if len(default_list) > 0: txt += [' } elseif {[info exists default_map($arg)]} {'] txt += [' lappend arg_list $default_map($arg)'] txt += [' } else {'] txt += [' ${ns}::sics_log 9 "Missing configuration value $arg"'] txt += [' lappend missing_list $arg'] txt += [' }'] txt += [' }'] txt += [' if { [llength $missing_list] > 0 } {'] txt += [' error "$name is missing configuration values $missing_list"'] txt += [' }'] protocol_args = ' {*}$arg_list' else: protocol_args = '' txt += [' makesctcontroller sct_${name} %s ${ip_address}:${tcp_port}%s' % (MyDriver['protocol'], protocol_args)] txt += [' } else {'] txt += [' makesctcontroller sct_${name} aqadapter ${asyncqueue}'] txt += [' }'] txt += [' } else {'] txt += [' if { [dict exists $v "asyncprotocol"] } {'] txt += [' set asyncprotocol [dict get $v "asyncprotocol"]'] txt += [' } else {'] txt += [' set asyncprotocol ${name}_protocol'] txt += [' MakeAsyncProtocol ${asyncprotocol}'] txt += [' if { [dict exists $v "sendterminator"] } {'] txt += [' ${asyncprotocol} sendterminator "[dict get $v "sendterminator"]"'] txt += [' } elseif { [dict exists $v "terminator"] } {'] txt += [' ${asyncprotocol} sendterminator "[dict get $v "terminator"]"'] txt += [' }'] txt += [' if { [dict exists $v "replyterminator"] } {'] txt += [' ${asyncprotocol} replyterminator "[dict get $v "replyterminator"]"'] txt += [' } elseif { [dict exists $v "terminator"] } {'] txt += [' ${asyncprotocol} replyterminator "[dict get $v "terminator"]"'] txt += [' }'] txt += [' }'] txt += [' set asyncqueue ${name}_queue'] txt += [' set ip_address [dict get $v ip]'] txt += [' set tcp_port [dict get $v port]'] txt += [' MakeAsyncQueue ${asyncqueue} ${asyncprotocol} ${ip_address} ${tcp_port}'] txt += [' if { [dict exists $v "timeout"] } {'] txt += [' ${asyncqueue} timeout "[dict get $v "timeout"]"'] txt += [' }'] txt += [' makesctcontroller sct_${name} aqadapter ${asyncqueue}'] txt += [' }'] if 'make_args_lst' in MyDriver: txt += [' set arg_list [list]'] txt += [' set missing_list [list]'] default_list = [] for arg in [key for key in MyDriver['make_args_lst'] if MyDriver['make_args_map'][key] is not None]: default_list += [arg, MyDriver['make_args_map'][arg]] if len(default_list) > 0: txt += [' array unset default_map'] txt += [' array set default_map [list %s]' % ' '.join(default_list)] txt += [' foreach arg {' + ' '.join(MyDriver['make_args_lst']) + '} {'] txt += [' if {[dict exists $u $arg]} {'] txt += [' lappend arg_list "[dict get $u $arg]"'] txt += [' } elseif {[dict exists $v $arg]} {'] txt += [' lappend arg_list "[dict get $v $arg]"'] if len(default_list) > 0: txt += [' } elseif {[info exists default_map($arg)]} {'] txt += [' lappend arg_list $default_map($arg)'] txt += [' } else {'] txt += [' ${ns}::sics_log 9 "Missing configuration value $arg"'] txt += [' lappend missing_list $arg'] txt += [' }'] txt += [' }'] txt += [' if { [llength $missing_list] > 0 } {'] txt += [' error "$name is missing configuration values $missing_list"'] txt += [' }'] make_args = ' {*}$arg_list' else: make_args = '' txt += [' ${ns}::mkDriver sct_${name} ${name} ${device_class} ${simulation_flag} ${ip_address} ${tcp_port}' + make_args] txt += [' }'] txt += [' }'] txt += [' }'] txt += [' } catch_message ]'] txt += [' handle_exception ${catch_status} ${catch_message}'] txt += ['}'] emit(txt) def put_check_config(MyDriver): txt = [''] txt += ['if { [info exists ::config_dict] } {'] txt += [' %s::read_config' % MyDriver['namespace']] txt += ['} else {'] txt += [' %s::sics_log 5 "No config dict"' % MyDriver['namespace']] txt += ['}'] 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) elif theFunc['type'] == 'pid_function': put_pid_function(MyDriver, func) def generate_driver(MyDriver): global NumberOfLinesOut global fdo NumberOfLinesOut = 0 generate_filename(MyDriver) fdo = open(MyDriver['fullname'], 'w') put_preamble(MyDriver) put_standard_code(MyDriver) put_mkDriver(MyDriver) put_add_driver(MyDriver) put_postamble(MyDriver) put_read_config(MyDriver) put_check_config(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 if Verbose: print "Produced file %s with %d lines." % \ ( MyDriver['filename'], NumberOfLinesOut) def process_drivers(TheDrivers): if Verbose: print "TheDrivers:", TheDrivers for driver in TheDrivers: MyDriver = {'name':driver} MyDriver['namespace'] = '::scobj::%s' % driver MyDriver['debug_threshold'] = '5' MyDriver['Groups'] = {} MyDriver['Funcs'] = {} MyDriver['Permlink'] = {} MyDriver['Deferred'] = [] build_driver(MyDriver, TheDrivers[driver]) check_func_code(MyDriver) if Verbose: print "MyDriver:", MyDriver['name'], '=', MyDriver if DriverDump or Verbose: dump_driver(MyDriver) generate_driver(MyDriver) def load_file(source_file, depth_list): global SourceFileList, SourceLineList # find the file and set the name SourceFile = os.path.realpath(os.path.abspath(source_file)) if not os.path.isfile(SourceFile): #print source_file, SourceFile, SourceFileList if len(SourceFileList) > 0: trial_name = os.path.join(os.path.dirname(SourceFileList[0]), source_file) #print trial_name if os.path.isfile(trial_name): SourceFile = os.path.realpath(os.path.abspath(trial_name)) if SourceFile in depth_list: PrintPostError('Error: recursive include of: %s' % SourceFile) for idx, name in enumerate(depth_list): PrintPostError(' ' * idx + name) raise Exception('Bad recursive include of "' + SourceFile + '"') SourceFileList.append(SourceFile) curr_file = len(SourceFileList) - 1 fd = open(SourceFile, 'r') LocalData = [] line_no = 0 execing = False exec_input = [] exec_line = 0 for line in fd: line_no += 1 line = line.rstrip('\n') if execing: match = re.match(r'\s*%end', line, flags=re.IGNORECASE) if match: #print "exec_input:" #for temp_line in exec_input: # print " " + temp_line kw = {} kw['exec_output'] = [] exec('\n'.join(exec_input)) in kw #print "exec_output:" for line in kw['exec_output']: # print " " + line LocalData.append(line) SourceLineList.append((curr_file, exec_line)) exec_input = [] execing = False else: exec_input.append(line) continue match = re.match(r'\s*%exec', line, flags=re.IGNORECASE) if match: execing = True exec_line = line_no continue match = re.match(r'\s*%include\s+', line, flags=re.IGNORECASE) if match: new_source = re.sub(r'\s*%include\s+(.*)', r'\1', line, flags=re.IGNORECASE) LocalData += load_file(new_source, depth_list + [SourceFile]) continue LocalData.append(line) SourceLineList.append((curr_file, line_no)) fd.close() return LocalData def dump_source_files(data): global SourceFileList, SourceLineList print "SourceFileList:", SourceFileList print "SourceLineList:", SourceLineList curr_file = -1 for line_no, line in enumerate(data): if SourceLineList[line_no][0] != curr_file: curr_file = SourceLineList[line_no][0] print "File:", SourceFileList[curr_file] print "%4d:" % SourceLineList[line_no][1], line def process_source(source_file): global PathName, SourceFile global TheDrivers global NumberOfLinesIn, NumberOfLinesOut global SourceData global PrintedFileName global SourceFileList, SourceLineList TheDrivers = {} PrintedFileName = -1 NumberOfLinesIn = 0 NumberOfLinesOut = 0 SourceFileList = list() SourceLineList = list() PathName = os.path.realpath(os.path.abspath(os.path.dirname(source_file))) SourceData = load_file(source_file, []) NumberOfLinesIn = len(SourceData) start_line = lexer.lineno yaccer.parse('\n'.join(SourceData)) stop_line = lexer.lineno if Verbose: print 'Consumed file %s with %d lines (%d, %d)' % \ (source_file, NumberOfLinesIn, start_line, stop_line - 1) lexer.lineno = 1 process_drivers(TheDrivers) if args.list: dump_source_files(SourceData) def main(): global lexer, yaccer global Verbose global Move global DriverDump global CodeDump global args import argparse # RELEASE-3_1="before", RELEASE-3_2="after" default_sct = "before" 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("-l", "--list", help="list output", action="store_true") parser.add_argument("-m", "--move", help="generate move commands", action="store_true") parser.add_argument("--sct", help="where to put the sct in the filename [%s]" % default_sct, choices=["before", "after"], default=default_sct) 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() if args.verbose: print args if args.code: CodeDump = True else: CodeDump = False if args.driver: DriverDump = True else: DriverDump = False if args.move: Move = True else: Move = False if args.verbose: Verbose = True else: Verbose = False source_files = args.driver_source # if source_files and len(source_files) > 0: # Build the lexer # lexer = lex.lex() # # Build the parser # #yaccer = yacc.yacc(tabmodule="gen_sct",outputdir="/tmp",write_tables=0,debug=0) yaccer = yacc.yacc(debug=0) for source_file in source_files: process_source(source_file) if __name__ == "__main__": main()