diff --git a/site_ansto/instrument/config/environment/isotech_ps.sct b/site_ansto/instrument/config/environment/isotech_ps.sct index 585a29d4..ea3672f4 100644 --- a/site_ansto/instrument/config/environment/isotech_ps.sct +++ b/site_ansto/instrument/config/environment/isotech_ps.sct @@ -5,7 +5,7 @@ driver isotech_ps = { vendor = isotech; device = ps; protocol = std; class = environment; simulation_group = environment_simulation; - protocol_args = '"\r"'; + protocol_args = '{terminator \r}'; group = { readable = 5 mutable = true diff --git a/site_ansto/instrument/config/environment/temperature/julabo_lh45.sct b/site_ansto/instrument/config/environment/temperature/julabo_lh45.sct index 20b2f7e3..5f706bb3 100644 --- a/site_ansto/instrument/config/environment/temperature/julabo_lh45.sct +++ b/site_ansto/instrument/config/environment/temperature/julabo_lh45.sct @@ -8,7 +8,7 @@ driver julabo_lh45_gen = { simulation_group = environment_simulation add_args = '{id 1} {ctrl_sensor "bath"} {tol 5.0}'; make_args = 'id ctrl_sensor tol'; - protocol_args = '"\r"'; + protocol_args = '{terminator \r}'; # # Unnamed group has variables at device level # @@ -22,7 +22,7 @@ driver julabo_lh45_gen = { permlink = 'T.SP01'; writeable = 1; write_function = setPoint; write_command = 'out_sp_00'; driveable = sensor/'value'; - lowerlimit = 10; upperlimit = 90; tolerance = '${tol}'; + lowerlimit = -20; upperlimit = 90; tolerance = '${tol}'; } var overtemp_warnlimit = { read_command = 'in_sp_03'; diff --git a/site_ansto/instrument/util/gen_sct.py b/site_ansto/instrument/util/gen_sct.py index 3cddcfc8..52bda187 100755 --- a/site_ansto/instrument/util/gen_sct.py +++ b/site_ansto/instrument/util/gen_sct.py @@ -30,17 +30,10 @@ # import os +import re import ply.lex as lex import ply.yacc as yacc -global Verbose -global DriverDump -global CodeDump -global FunctionTypes -global DriveableFunctionTypes -global NumberOfLinesIn -global NumberOfLinesOut - states = ( ('tcl', 'exclusive'), ) @@ -63,6 +56,34 @@ DriveableFunctionTypes = [ 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, @@ -96,6 +117,7 @@ reserved = { 'VAR' : 'VAR', 'PROPERTY' : 'PROPERTY', 'CONTROL' : 'CONTROL', + 'CONDITIONAL' : 'CONDITIONAL', 'DATA' : 'DATA', 'NXSAVE' : 'NXSAVE', 'MUTABLE' : 'MUTABLE', @@ -212,16 +234,17 @@ def t_tcl_newline(t): t.lexer.lineno += t.value.count("\n") def t_tcl_error(t): - print("Illegal tcl character '%s'" % t.value[0]) + message = "Illegal tcl character '%s'" % t.value[0] + PrintParseError(message) t.lexer.skip(1) def t_TEXT_STRING1(t): - r'\'[^\']+\'' + r'\'[^\']*\'' t.value = t.value[1:-1] return t def t_TEXT_STRING2(t): - r"\"[^\"]+\"" + r"\"[^\"]*\"" t.value = t.value[1:-1] return t @@ -240,7 +263,8 @@ def t_FLOATER(t): try: t.value = float(t.value) except ValueError: - print "Floating value invalid:", t.value + message = "Floating value invalid: " + repr(t.value) + PrintParseError(message) t.value = 0.0 return t @@ -249,7 +273,8 @@ def t_INTEGER(t): try: t.value = int(t.value) except ValueError: - print "Integer value too large:", t.value + message = "Integer value too large: " + repr(t.value) + PrintParseError(message) t.value = 0 return t @@ -270,7 +295,9 @@ def t_newline(t): t.lexer.lineno += t.value.count("\n") def t_error(t): - print("Illegal character '%s'" % t.value[0]) + message = "Illegal character '%s' at line %d" % \ + (t.value[0], t.lexer.lineno) + PrintParseError(message) t.lexer.skip(1) # @@ -430,6 +457,7 @@ def p_var_typ_ass(p): | DATA EQUALS true_false | NXSAVE EQUALS true_false | MUTABLE EQUALS true_false + | CONDITIONAL EQUALS text_string ''' p[0] = { p[1] : p[3] } @@ -532,6 +560,14 @@ def p_code(p): | 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): @@ -585,7 +621,10 @@ def p_empty(p): pass def p_error(t): - print("Syntax error at '%s'" % t.value), t + message = "Syntax error at line %d" % lexer.lineno + message += " " + repr(t) + message += " " + repr(t.value) + PrintParseError(message) # # Utility functions @@ -598,6 +637,54 @@ def make_path(MyVar): 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 @@ -709,7 +796,8 @@ def build_variable(MyDriver, p): 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) + 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('.') @@ -802,6 +890,30 @@ def adjust_group(MyGroup): 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 @@ -834,7 +946,34 @@ def build_driver(MyDriver, TheTree): MyDriver[key] = item[key] for item in MyDriver['Permlink']: if len(MyDriver['Permlink'][item]) > 1: - print 'Error: duplicate permlink entries for "%s"' % item, MyDriver['Permlink'][item] + 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 # @@ -872,10 +1011,10 @@ def dump_driver_groups(groups, indent): 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, '=', groups[item][subitem] + print indent + ' ', subitem, '= \'%s\'' % groups[item][subitem] if 'GroupProperty' in groups[item]: for subitem in groups[item]['GroupProperty']: - print indent + ' GroupProperty', subitem, '=', groups[item]['GroupProperty'][subitem] + 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 + '}' @@ -893,6 +1032,12 @@ def dump_driver_funcs(funcs): 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: @@ -924,21 +1069,29 @@ def put_preamble(MyDriver): txt += ['namespace eval %s {' % MyDriver['namespace']] txt += [' set debug_threshold %s' % str( MyDriver['debug_threshold'])] if len(MyDriver['Permlink']) > 0: - if 'make_args' in MyDriver and 'id' in MyDriver['make_args'].split(): + 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 fd [open "../log/%s_[basename ${tc_root}].log" "a"]' % MyDriver['name']] - txt += [' set line "[clock format [clock seconds] -format "%T"] ${debug_string}"'] - txt += [' puts ${fd} "${line}"'] + 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 ]'] @@ -1158,7 +1311,12 @@ def put_checkstatus_function(MyDriver, func): txt += ['# %s hook code goes here' % func] txt += [' if {[sct driving]} {'] txt += [' set sp "[sct target]"'] - txt += [' set pv "[hval ${tc_root}/[sct driveable]]"'] + 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] } {'] @@ -1217,14 +1375,14 @@ def put_pid_function(MyDriver, func): 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]}]'] + 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 [expr {[sct pid_ivalue] * [sct pid_integ]}]'] + 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] @@ -1239,156 +1397,201 @@ def put_pid_function(MyDriver, func): txt += ['}'] emit(txt) -def put_group(MyDriver, MyGroup): +def put_var(MyDriver, MyGroup, MyVar): readable_or_writeable = False txt = [] + postfix = [] + if MyGroup['name']: + nodename = MyGroup['path'] + '/' + MyVar['name'] + else: + nodename = MyVar['name'] + + # 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 += [' }'] + + 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 = [] + dfr = [] if MyGroup['name']: txt += [''] txt += [' hfactory ${scobj_hpath}/%s plain spy none' % MyGroup['path']] - if 'GroupProperty' in MyGroup: - for key in sorted(MyGroup['GroupProperty']): - txt += [' hsetprop ${scobj_hpath}/%s %s "%s"' % (MyGroup['path'], key, MyGroup['GroupProperty'][key])] - groupname = MyGroup['path'] + '/' else: - groupname = '' + pass + for var in sorted(MyGroup['Vars']): txt += [''] MyVar = MyGroup['Vars'][var] - nodename = groupname + MyVar['name'] - # 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'] - 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 'permlink' in MyVar: - device_type, node_type = MyVar['permlink'].split('.') - if device_type.startswith("#"): - if 'make_args' in MyDriver and 'permlink' in MyDriver['make_args'].split(): - idx = int(device_type[1:]) - device_type = '[string index ${permlink} %d]' % idx - else: - print 'Error: permlink required in make_ags' - if 'make_args' in MyDriver and 'id' in MyDriver['make_args'].split(): - 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 not MyGroup['name']: + infix, postfix = put_var(MyDriver, MyGroup, MyVar) + txt += infix + dfr += postfix + + 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])] + else: 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 {[string equal -nocase "${simulation_flag}" "false"]} {'] - 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: - poll_period = 1 - if poll_period > 3600: - poll_period = 3600 - 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']: - # Generate __ at runtime for driveable - driveable = '${name}_' + make_path(MyVar) - MyDriver['Deferred'] += ['ansto_makesctdrive %s ${scobj_hpath}/%s ${scobj_hpath}/%s ${sct_controller}' % (driveable, nodename, MyVar['driveable'])] - txt += [' } else {'] - txt += [' %s::sics_log 9 "simulation_flag=${simulation_flag} => No poll/write for %s"' % (MyDriver['namespace'], MyDriver['name'])] - txt += [' }'] + for grp in sorted(MyGroup['Groups']): txt += put_group(MyDriver, MyGroup['Groups'][grp]) + + txt += dfr + + 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): @@ -1398,8 +1601,8 @@ def put_mkDriver(MyDriver): else: line = 'proc %s::mkDriver { sct_controller name device_class simulation_flag ip_address tcp_port } {' % (MyDriver['namespace']) txt += [line] - if 'make_args' in MyDriver: - make_args = ' '.join(["${%s}"%arg for arg in MyDriver['make_args'].split()]) + 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'])] @@ -1413,7 +1616,7 @@ def put_mkDriver(MyDriver): txt += ['# %s hook code ends' % func] else: if len(MyDriver['Permlink']) > 0: - if 'make_args' in MyDriver and 'id' in MyDriver['make_args'].split(): + 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]]'] @@ -1425,6 +1628,7 @@ def put_mkDriver(MyDriver): 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: @@ -1436,6 +1640,7 @@ def put_mkDriver(MyDriver): 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'])] @@ -1456,7 +1661,7 @@ def put_mkDriver(MyDriver): txt += ['}'] emit(txt) -def put_postamble(MyDriver): +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']) @@ -1464,8 +1669,8 @@ def put_postamble(MyDriver): 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' in MyDriver: - make_args = ' '.join(["${%s}"%arg for arg in MyDriver['make_args'].split()]) + 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'])] @@ -1478,18 +1683,29 @@ def put_postamble(MyDriver): txt += [' makesctcontroller sct_${name} aqadapter ${tcp_port}'] txt += [' } else {'] if 'protocol_args' in MyDriver: - protocol_args = MyDriver['protocol_args'].replace('\\', '\\\\').replace('"', '\\"') - txt += [' %s::sics_log 9 "makesctcontroller sct_${name} %s ${ip_address}:${tcp_port} %s"' % (MyDriver['namespace'], MyDriver['protocol'], protocol_args)] - txt += [' makesctcontroller sct_${name} %s ${ip_address}:${tcp_port} %s' % (MyDriver['protocol'], MyDriver['protocol_args'])] + 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} => No sctcontroller for %s"' % (MyDriver['namespace'], MyDriver['name'])] + 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' in MyDriver: - make_args = ' '.join(["${%s}"%arg for arg in MyDriver['make_args'].split()]) + 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: @@ -1501,13 +1717,21 @@ def put_postamble(MyDriver): txt += [' } catch_message ]'] txt += [' handle_exception ${catch_status} ${catch_message}'] txt += ['}'] - 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: @@ -1516,15 +1740,17 @@ def put_postamble(MyDriver): 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" "${simulation_flag}" ${ip_address} ${tcp_port}' % (MyDriver['namespace'], MyDriver['class']) - if 'add_args' in MyDriver: - for arg in MyDriver['add_args'].split(): - line += ' "${%s}"' % arg + 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 sct_%s.tcl"' % MyDriver['name']] - txt += ['%s::sics_log 9 "file evaluation of sct_%s.tcl"' % (MyDriver['namespace'], MyDriver['name'])] + 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): @@ -1559,42 +1785,96 @@ def put_read_config(MyDriver): txt += [' continue'] txt += [' }'] txt += [' if { [string equal -nocase [dict get $v "driver"] "%s"] } {' % MyDriver['name']] - 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 += [' } 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]'] - 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 "terminator"] } {'] - txt += [' ${asyncprotocol} sendterminator "[dict get $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 += [' }'] - if 'make_args' in MyDriver: + 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]'] - txt += [' foreach arg {' + MyDriver['make_args'] + '} {'] + 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'] @@ -1603,17 +1883,10 @@ def put_read_config(MyDriver): txt += [' if { [llength $missing_list] > 0 } {'] txt += [' error "$name is missing configuration values $missing_list"'] txt += [' }'] - txt += [' if { [string equal -nocase ${asyncqueue} "sct"] } {'] - txt += [' ${ns}::add_driver ${name} ${device_class} ${simulation_flag} ${ip_address} ${tcp_port} {*}$arg_list'] - txt += [' } else {'] - txt += [' ${ns}::add_driver ${name} ${device_class} ${simulation_flag} "aqadapter" ${asyncqueue} {*}$arg_list'] - txt += [' }'] + make_args = ' {*}$arg_list' else: - txt += [' if { [string equal -nocase ${asyncqueue} "sct"] } {'] - txt += [' ${ns}::add_driver ${name} ${device_class} ${simulation_flag} ${ip_address} ${tcp_port}'] - txt += [' } else {'] - txt += [' ${ns}::add_driver ${name} ${device_class} ${simulation_flag} "aqadapter" ${asyncqueue}'] - txt += [' }'] + make_args = '' + txt += [' ${ns}::mkDriver sct_${name} ${name} ${device_class} ${simulation_flag} ${ip_address} ${tcp_port}' + make_args] txt += [' }'] txt += [' }'] txt += [' }'] @@ -1661,13 +1934,12 @@ 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') + 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) @@ -1678,7 +1950,9 @@ def generate_driver(MyDriver): print "Function:", f, "Type:", MyDriver['Funcs'][f]['type'], '#Uses:', MyDriver['Funcs'][f]['reference_count'] for l in MyDriver['Funcs'][f]['text']: print " ", l - print "Produced file %s with %d lines." % (filename, NumberOfLinesOut) + if Verbose: + print "Produced file %s with %d lines." % \ + ( MyDriver['filename'], NumberOfLinesOut) def process_drivers(TheDrivers): if Verbose: @@ -1692,62 +1966,144 @@ def process_drivers(TheDrivers): 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 process_source(source_files): - global PathName +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 + global NumberOfLinesIn, NumberOfLinesOut + global SourceData + global PrintedFileName + global SourceFileList, SourceLineList TheDrivers = {} - # - # 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() - - 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') - start_line = lexer.lineno - yaccer.parse(data) - stop_line = lexer.lineno - print 'Consumed file %s with %d lines (%d, %d)' % (source_file, - NumberOfLinesIn, start_line, stop_line - 1) - lexer.lineno = 1 + 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() - print args + if args.verbose: + print args if args.code: CodeDump = True else: @@ -1756,13 +2112,30 @@ def main(): 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: - process_source(source_files) + 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()