From 115965fbaead9005a2c57924decd27f2bd907649 Mon Sep 17 00:00:00 2001 From: Douglas Clowes Date: Tue, 4 Feb 2014 08:59:46 +1100 Subject: [PATCH] Make the driveable interface tailorable and suppress unused code block --- site_ansto/instrument/util/gen_sct.py | 140 ++++++++++++++++++-------- 1 file changed, 100 insertions(+), 40 deletions(-) diff --git a/site_ansto/instrument/util/gen_sct.py b/site_ansto/instrument/util/gen_sct.py index 0d40d60c..cd7230f1 100755 --- a/site_ansto/instrument/util/gen_sct.py +++ b/site_ansto/instrument/util/gen_sct.py @@ -1,11 +1,19 @@ #!/usr/bin/env python # Script Context Driver Generator +# Author: Douglas Clowes (douglas.clowes@ansto.gov.au) Jan/Feb 2014 # vim: ft=python ts=8 sts=2 sw=2 expandtab autoindent smartindent # +# This program generates Script Context Driver TCL files. +# +# It takes one or more "Script Context Driver Description" files. +# Each file may contain one or more driver descriptions. Each driver +# description will result in one TCL file. +# +# The name of the file produced, the TCL namespace used and names within +# the file are based on the driver name givin in the driver description. +# # TODO: -# implement fetch/read status (fsm) functions with hooks # implement attributes and units on vars -# - units (mm/degrees/C/K) # - type part ??? # - nxalias xxxxx # - sdsinfo @@ -34,6 +42,7 @@ global DriverDump global CodeDump global UsingCreateNode global FunctionTypes +global DriveableFunctionTypes global NumberOfLinesIn global NumberOfLinesOut @@ -42,8 +51,14 @@ FunctionTypes = [ 'write_function', 'fetch_function', 'check_function', + 'checkrange_function', ] +DriveableFunctionTypes = [ + 'halt_function', + 'checklimits_function', + 'checkstatus_function', + ] UsingCreateNode = False Verbose = False @@ -104,6 +119,10 @@ reserved = { 'WRITE_COMMAND' : 'WRITE_COMMAND', 'WRITE_FUNCTION' : 'WRITE_FUNCTION', 'CHECK_FUNCTION' : 'CHECK_FUNCTION', + 'CHECKRANGE_FUNCTION' : 'CHECKRANGE_FUNCTION', + 'CHECKLIMITS_FUNCTION' : 'CHECKLIMITS_FUNCTION', + 'CHECKSTATUS_FUNCTION' : 'CHECKSTATUS_FUNCTION', + 'HALT_FUNCTION' : 'HALT_FUNCTION', # Value setting 'VALUE' : 'VALUE', 'ALLOWED' : 'ALLOWED', @@ -329,6 +348,10 @@ def p_var_typ_ass(p): | WRITE_COMMAND EQUALS TEXT_STRING | WRITE_FUNCTION EQUALS id_or_str | CHECK_FUNCTION EQUALS id_or_str + | CHECKRANGE_FUNCTION EQUALS id_or_str + | CHECKLIMITS_FUNCTION EQUALS id_or_str + | CHECKSTATUS_FUNCTION EQUALS id_or_str + | HALT_FUNCTION EQUALS id_or_str | TYPE EQUALS type_code | PRIV EQUALS priv_code | CONTROL EQUALS true_false @@ -410,6 +433,10 @@ def p_code_type(p): | FETCH_FUNCTION | WRITE_FUNCTION | CHECK_FUNCTION + | CHECKRANGE_FUNCTION + | CHECKLIMITS_FUNCTION + | CHECKSTATUS_FUNCTION + | HALT_FUNCTION | empty ''' p[0] = p[1] @@ -461,6 +488,7 @@ def init_context(): 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(): @@ -482,6 +510,7 @@ def build_code(MyDriver, 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'] @@ -492,6 +521,7 @@ def build_code(MyDriver, p): def build_variable(MyDriver, p): global FunctionTypes + global DriveableFunctionTypes if Verbose: print 'Variable:', p MyVar = {} @@ -503,26 +533,35 @@ def build_variable(MyDriver, p): for key in ContextStack[ContextIndex]: if not key in MyVar: MyVar[key] = ContextStack[ContextIndex][key] - if 'driveable' in MyVar: - if MyVar['driveable'] > 0: - if not 'driveable' in MyDriver: - MyDriver['driveable'] = 'true' + if 'driveable' in MyVar and MyVar['driveable']: + if not 'driveable' in MyDriver: + MyDriver['driveable'] = 'true' + # defaults for 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: + 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' : [] } + 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 MyDriver['Funcs'][MyVar[func]]['type'] == 'none': + if not MyDriver['Funcs'][MyVar[func]]['type']: + if Verbose: + print MyVar['name'], 'Mod func type:', MyDriver['Funcs'][MyVar[func]], '= ' + func MyDriver['Funcs'][MyVar[func]]['type'] = func else: # TODO FIXME error message print 'Error: Function type mismatch: var = ' + str(MyVar) + ', code = ' + str(MyDriver['Funcs'][MyVar[func]]) + ', func = ' + str(func) + MyDriver['Funcs'][MyVar[func]]['reference_count'] += 1 if Verbose: print '==>>MyVar:', MyVar return MyVar @@ -582,12 +621,16 @@ def build_driver(MyDriver, TheTree): # Driver Dump Functions # def dump_driver_vars(vars): + global FunctionTypes + global DriveableFunctionTypes for item in sorted(vars): print ' VAR %s = {' % item - for subitem in sorted([i for i in vars[item] if i not in FunctionTypes]): + for subitem in sorted([i for i in vars[item] if i not in FunctionTypes + DriveableFunctionTypes]): print ' %s =' % subitem, vars[item][subitem] for subitem in sorted([i for i in vars[item] if i in FunctionTypes]): print ' %s =' % subitem, vars[item][subitem] + for subitem in sorted([i for i in vars[item] if i in DriveableFunctionTypes]): + print ' %s =' % subitem, vars[item][subitem] print ' }' def dump_driver_groups(groups): for item in sorted(groups): @@ -652,6 +695,7 @@ def put_preamble(MyDriver): def put_write_function(MyDriver, func): txt = [''] + txt += ['# function to write a parameter value on a device'] txt += ['proc %s::%s {tc_root nextState cmd_str} {' % (MyDriver['namespace'], func)] txt += [' debug_log 1 "%s tc_root=${tc_root} sct=[sct] cmd=${cmd_str}"' % func] txt += [' if { [hpropexists [sct] geterror] } {'] @@ -671,6 +715,7 @@ def put_write_function(MyDriver, func): def put_check_function(MyDriver, func): txt = [''] + txt += ['# function to check the write parameter on a device'] txt += ['proc %s::%s {tc_root} {' % (MyDriver['namespace'], func)] txt += [' debug_log 1 "%s tc_root=${tc_root} sct=[sct] resp=[sct result]"' % func] if func in MyDriver['Funcs'] and len(MyDriver['Funcs'][func]['text']) > 0: @@ -683,6 +728,7 @@ def put_check_function(MyDriver, func): def put_fetch_function(MyDriver, func): txt = [''] + txt += ['# function to request the read of a parameter on a device'] txt += ['proc %s::%s {tc_root nextState cmd_str} {' % (MyDriver['namespace'], func)] txt += [' debug_log 1 "%s tc_root=${tc_root} sct=[sct] cmd=${cmd_str}"' % func] txt += [' if { [hpropexists [sct] geterror] } {'] @@ -701,6 +747,7 @@ def put_fetch_function(MyDriver, func): def put_read_function(MyDriver, func): txt = [''] + txt += ['# function to parse the read of a parameter on a device'] txt += ['proc %s::%s {tc_root} {' % (MyDriver['namespace'], func)] txt += [' debug_log 1 "%s tc_root=${tc_root} sct=[sct] result=[sct result]"' % func] txt += [' if { [hpropexists [sct] geterror] } {'] @@ -731,11 +778,11 @@ def put_read_function(MyDriver, func): txt += ['}'] emit(txt) -def put_check(MyDriver): +def put_checkrange_function(MyDriver, func): txt = [''] txt += ['# check function for hset change'] - txt += ['proc %s::check {tc_root} {' % MyDriver['namespace']] - txt += [' debug_log 1 "check tc_root=${tc_root} sct=[sct] target=[sct target]"'] + txt += ['proc %s::%s {tc_root} {' % (MyDriver['namespace'], func)] + txt += [' debug_log 1 "%s tc_root=${tc_root} sct=[sct] target=[sct target]"' % func] txt += [' set setpoint [sct target]'] txt += [' if { [hpropexists [sct] lowerlimit] } {'] txt += [' set lolimit [sct lowerlimit]'] @@ -749,9 +796,9 @@ def put_check(MyDriver): txt += [' # upperlimit not set, use target'] txt += [' set hilimit [sct target]'] txt += [' }'] - if 'check' in MyDriver['Funcs'] and len(MyDriver['Funcs']['check']['text']) > 0: + if func in MyDriver['Funcs'] and len(MyDriver['Funcs'][func]['text']) > 0: txt += ['# hook code starts'] - txt += MyDriver['Funcs']['check']['text'] + txt += MyDriver['Funcs'][func]['text'] txt += ['# hook code ends'] txt += [' if { ${setpoint} < ${lolimit} || ${setpoint} > ${hilimit} } {'] txt += [' error "setpoint ${setpoint} violates limits (${lolimit}..${hilimit}) on [sct]"'] @@ -760,11 +807,11 @@ def put_check(MyDriver): txt += ['}'] emit(txt) -def put_checklimits(MyDriver): +def put_checklimits_function(MyDriver, func): txt = [''] txt += ['# checklimits function for driveable interface'] - txt += ['proc %s::checklimits {tc_root} {' % MyDriver['namespace']] - txt += [' debug_log 1 "checklimits tc_root=${tc_root} sct=[sct] target=[sct target]"'] + txt += ['proc %s::%s {tc_root} {' % (MyDriver['namespace'], func)] + txt += [' debug_log 1 "%s tc_root=${tc_root} sct=[sct] target=[sct target]"' % func] txt += [' set setpoint [sct target]'] txt += [' if { [hpropexists [sct] lowerlimit] } {'] txt += [' set lolimit [sct lowerlimit]'] @@ -778,9 +825,9 @@ def put_checklimits(MyDriver): txt += [' # upperlimit not set, use target'] txt += [' set hilimit [sct target]'] txt += [' }'] - if 'checklimits' in MyDriver['Funcs'] and len(MyDriver['Funcs']['checklimits']['text']) > 0: + if func in MyDriver['Funcs'] and len(MyDriver['Funcs'][func]['text']) > 0: txt += ['# hook code starts'] - txt += MyDriver['Funcs']['checklimits']['text'] + txt += MyDriver['Funcs'][func]['text'] txt += ['# hook code ends'] txt += [' if { ${setpoint} < ${lolimit} || ${setpoint} > ${hilimit} } {'] txt += [' sct driving 0'] @@ -790,13 +837,13 @@ def put_checklimits(MyDriver): txt += ['}'] emit(txt) -def put_checkstatus(MyDriver): +def put_checkstatus_function(MyDriver, func): txt = [''] txt += ['# checkstatus function for driveable interface'] - txt += ['proc %s::checkstatus {tc_root} {' % MyDriver['namespace']] - if 'checkstatus' in MyDriver['Funcs'] and len(MyDriver['Funcs']['checkstatus']['text']) > 0: + txt += ['proc %s::%s {tc_root} {' % (MyDriver['namespace'], func)] + if func in MyDriver['Funcs'] and len(MyDriver['Funcs'][func]['text']) > 0: txt += ['# hook code starts'] - txt += MyDriver['Funcs']['checkstatus']['text'] + txt += MyDriver['Funcs'][func]['text'] txt += ['# hook code ends'] txt += [' if {[sct driving]} {'] txt += [' set sp "[sct target]"'] @@ -812,15 +859,15 @@ def put_checkstatus(MyDriver): txt += ['}'] emit(txt) -def put_halt(MyDriver): +def put_halt_function(MyDriver, func): txt = [''] txt += ['# halt function for driveable interface'] - txt += ['proc %s::halt {tc_root} {' % MyDriver['namespace']] - txt += [' debug_log 1 "halt tc_root=${tc_root} sct=[sct] driving=[sct driving]"'] + txt += ['proc %s::%s {tc_root} {' % (MyDriver['namespace'], func)] + txt += [' debug_log 1 "%s tc_root=${tc_root} sct=[sct] driving=[sct driving]"' % func] txt += [' ### TODO hset [sct] [hval [sct]]'] - if 'halt' in MyDriver['Funcs'] and len(MyDriver['Funcs']['halt']['text']) > 0: + if func in MyDriver['Funcs'] and len(MyDriver['Funcs'][func]['text']) > 0: txt += ['# hook code starts'] - txt += MyDriver['Funcs']['halt']['text'] + txt += MyDriver['Funcs'][func]['text'] txt += ['# hook code ends'] txt += [' sct driving 0'] txt += [' return "idle"'] @@ -1015,8 +1062,7 @@ def put_group(MyDriver, MyGroup): if MyVar['writeable'] > 0 or MyVar['driveable']: readable_or_writeable = True check_func = MyVar['check_function'] - if check_func == 'none': - check_func = 'noResponse' + checkrange_func = MyVar['checkrange_function'] write_func = MyVar['write_function'] if 'write_command' in MyVar: write_command = MyVar['write_command'] @@ -1024,12 +1070,15 @@ def put_group(MyDriver, MyGroup): 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}::check ${scobj_hpath}' % nodename] + 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}::checklimits ${scobj_hpath}' % nodename] - txt += [' hsetprop ${scobj_hpath}/%s checkstatus ${ns}::checkstatus ${scobj_hpath}' % nodename] - txt += [' hsetprop ${scobj_hpath}/%s halt ${ns}::halt ${scobj_hpath}' % 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'])] @@ -1208,6 +1257,9 @@ 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': @@ -1216,12 +1268,20 @@ def put_standard_code(MyDriver): put_fetch_function(MyDriver, func); elif theFunc['type'] == 'check_function': put_check_function(MyDriver, func); - put_check(MyDriver) + elif theFunc['type'] == 'checkrange_function': + put_checkrange_function(MyDriver, func); + elif theFunc['type'] == 'checklimits_function': + put_checklimits_function(MyDriver, func); + elif theFunc['type'] == 'checkstatus_function': + put_checkstatus_function(MyDriver, func); + elif theFunc['type'] == 'halt_function': + put_halt_function(MyDriver, func); def put_driveable_code(MyDriver): - put_checklimits(MyDriver) - put_checkstatus(MyDriver) - put_halt(MyDriver) + #put_checklimits_function(MyDriver, 'checklimits') + #put_checkstatus_function(MyDriver, 'checkhstatus') + #put_halt_function(MyDriver, 'halt') + pass def generate_driver(MyDriver): global NumberOfLinesOut @@ -1243,7 +1303,7 @@ def generate_driver(MyDriver): if CodeDump or Verbose: print "Code Fragments:", MyDriver['Funcs'] for f in sorted(MyDriver['Funcs'].keys()): - print "Function:", f, "Type:", MyDriver['Funcs'][f]['type'] + print "Function:", f, "Type:", MyDriver['Funcs'][f]['type'], '#Uses:', MyDriver['Funcs'][f]['reference_count'] for l in MyDriver['Funcs'][f]['text']: print " ", l print "Procuced file %s with %d lines." % (filename, NumberOfLinesOut)