Files
sics/site_ansto/instrument/util/gen_sct.py
2014-06-17 14:53:48 +10:00

1595 lines
55 KiB
Python
Executable File

#!/usr/bin/env python
# Script Context Driver Generator
# Author: Douglas Clowes (douglas.clowes@ansto.gov.au) Jan/Feb 2014
# vim: ft=python ts=8 sts=2 sw=2 expandtab autoindent smartindent
#
# This program generates Script Context Driver TCL files.
#
# It takes one or more "Script Context Driver Description" files.
# Each file may contain one or more driver descriptions. Each driver
# description will result in one TCL file.
#
# The name of the file produced, the TCL namespace used and names within
# the file are based on the driver name givin in the driver description.
#
# TODO:
# implement attributes and units on vars
# - type part ???
# - nxalias xxxxx
# - sdsinfo
# check simulation works
# handle environmental monitoring (emon)
# - figure out how to do it
# - make nodes monitorable
# - make it conditional
# handle the driving settling time in checkstatus
# Questions:
# what it the 'plain spy none' on hfactory
# what is 'mugger' vs 'manager' - seems like alias?
# should some hset commands be hupdate commands?
#
import os
import ply.lex as lex
import ply.yacc as yacc
global Verbose
global DriverDump
global CodeDump
global FunctionTypes
global DriveableFunctionTypes
global NumberOfLinesIn
global NumberOfLinesOut
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
#
# Tokenizer: This recognizes the tokens which can be keywords, identifiers,
# numbers or strings plus the punctuation.
#
#
# Reserved words (keywords) in the form reserved['KEYWORD'] = 'TOKEN'
#
reserved = {
# Driver keywords
'DRIVER' : 'DRIVER',
'VENDOR' : 'VENDOR',
'DEVICE' : 'DEVICE',
'PROTOCOL' : 'PROTOCOL',
'DRIVER_PROPERTY' : 'DRIVER_PROPERTY',
'CLASS' : 'CLASS',
'SIMULATION_GROUP' : 'SIMULATION_GROUP',
'DEBUG_THRESHOLD' : 'DEBUG_THRESHOLD',
'CODE' : 'CODE',
'ADD_ARGS' : 'ADD_ARGS',
'MAKE_ARGS' : 'MAKE_ARGS',
'PROTOCOL_ARGS' : 'PROTOCOL_ARGS',
# Group keywords
'GROUP' : 'GROUP',
'GROUP_PROPERTY' : 'GROUP_PROPERTY',
# Variable keywords
'VAR' : 'VAR',
'PROPERTY' : 'PROPERTY',
'CONTROL' : 'CONTROL',
'DATA' : 'DATA',
'NXSAVE' : 'NXSAVE',
'MUTABLE' : 'MUTABLE',
'READABLE' : 'READABLE',
'WRITEABLE' : 'WRITEABLE',
'DRIVEABLE' : 'DRIVEABLE',
'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',
] + list(reserved.values())
#
# Token rules
#
t_EQUALS = r'='
t_LBRACE = r'{'
t_RBRACE = r'}'
t_SLASH = r'/'
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):
print("Illegal tcl character '%s'" % t.value[0])
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:
print "Floating value invalid:", t.value
t.value = 0.0
return t
def t_INTEGER(t):
r'-?\d+'
try:
t.value = int(t.value)
except ValueError:
print "Integer value too large:", t.value
t.value = 0
return t
def t_ID(t):
r'[a-zA-Z_][a-zA-Z_0-9]*'
t.type = reserved.get(t.value.upper(), 'ID') # Check for reserved words
# Force reserved words to lower case for map lookup and comparisson
if t.value.upper() in reserved:
t.type = reserved[t.value.upper()]
t.value = t.value.lower()
return t
# Ignored characters
t_ignore = " \t;"
def t_newline(t):
r'\n+'
t.lexer.lineno += t.value.count("\n")
def t_error(t):
print("Illegal character '%s'" % t.value[0])
t.lexer.skip(1)
#
# Parser
#
#
# Parsing rules
#
#
# We don't yet have a need for precedence so leave it empty here
#
precedence = (
#('left','PLUS','MINUS'),
#('left','TIMES','DIVIDE'),
#('right','UMINUS'),
)
#
# The head token - it all reduces to this
#
def p_driver(p):
'driver : DRIVER id_or_str EQUALS driver_block'
p[0] = [{ 'Driver' : {p[2] : p[4]}}]
if Verbose:
print "Driver:", p[0]
global PathName
global TheDrivers
TheDrivers[p[2]] = p[4] + [{'PathName':PathName}]
def p_driver_block(p):
'driver_block : LBRACE driver_statement_list RBRACE'
p[0] = p[2]
def p_driver_statement_list(p):
'''driver_statement_list : driver_statement
| driver_statement_list driver_statement
'''
if len(p) == 2:
p[0] = [p[1]]
else:
p[0] = p[1] + [p[2]]
def p_driver_statement(p):
'''driver_statement : driver_assignment
| group
| code
| driver_property
'''
p[0] = p[1]
def p_driver_assignment(p):
'''
driver_assignment : VENDOR EQUALS id_or_str
| DEVICE EQUALS id_or_str
| PROTOCOL EQUALS id_or_str
| CLASS EQUALS id_or_str
| SIMULATION_GROUP EQUALS id_or_str
| ADD_ARGS EQUALS text_string
| MAKE_ARGS 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 : GROUP group_id EQUALS LBRACE group_statement_list RBRACE
'''
p[0] = { 'Group' : [{'name': p[2]}] + p[5] }
def p_group_id(p):
'''
group_id : id_or_str
| empty
'''
p[0] = p[1]
def p_group_statement_list(p):
'''
group_statement_list : group_statement
| group_statement_list group_statement
'''
if len(p) == 2:
p[0] = [p[1]]
else:
p[0] = p[1] + [p[2]]
def p_group_statement(p):
'''group_statement : group_assignment
| variable
| 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
'''
if len(p) > 3:
p[0] = { 'Variable' : [{'name' : p[2]}] + p[5] }
else:
p[0] = { 'Variable' : [{'name' : p[2]}] }
def p_variable_statement_list(p):
'''variable_statement_list : variable_statement
| variable_statement_list variable_statement
'''
if len(p) == 2:
p[0] = [p[1]]
else:
p[0] = p[1] + [p[2]]
def p_variable_statement(p):
'''variable_statement : var_typ_ass
| var_val_ass
| property
'''
p[0] = p[1]
def p_var_typ_ass(p):
'''
var_typ_ass : READABLE EQUALS INTEGER
| WRITEABLE EQUALS INTEGER
| READ_COMMAND EQUALS text_string
| READ_FUNCTION EQUALS id_or_str
| FETCH_FUNCTION EQUALS id_or_str
| WRITE_COMMAND EQUALS text_string
| WRITE_FUNCTION EQUALS id_or_str
| CHECK_FUNCTION EQUALS id_or_str
| 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
'''
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_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 TCL_BEG code_block TCL_END
'''
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_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):
print("Syntax error at '%s'" % t.value), t
#
# Utility functions
#
def make_path(MyVar):
path = MyVar['path']
if len(path) > 0:
path = path.replace('/', '_')
path += '_'
path += MyVar['name']
return path
#
# This section handles building a driver tree from the Abstract Syntax Tree
# generated by the parser. The driver tree has all of the defaults and
# cascading context explicitly stated to make the code generation simpler.
#
def init_context():
global ContextStack, ContextIndex
ContextStack = [{}]
ContextIndex = 0
ContextStack[ContextIndex]['type'] = 'none'
ContextStack[ContextIndex]['priv'] = 'user'
ContextStack[ContextIndex]['readable'] = 0
ContextStack[ContextIndex]['writeable'] = 0
ContextStack[ContextIndex]['driveable'] = None
ContextStack[ContextIndex]['control'] = 'true'
ContextStack[ContextIndex]['data'] = 'true'
ContextStack[ContextIndex]['mutable'] = 'false'
ContextStack[ContextIndex]['nxsave'] = 'true'
ContextStack[ContextIndex]['read_function'] = 'rdValue'
ContextStack[ContextIndex]['write_function'] = 'setValue'
ContextStack[ContextIndex]['fetch_function'] = 'getValue'
ContextStack[ContextIndex]['check_function'] = 'noResponse'
ContextStack[ContextIndex]['checkrange_function'] = 'checkrange'
ContextStack[ContextIndex]['path'] = ''
def push_context():
global ContextStack, ContextIndex
ContextIndex = ContextIndex + 1
if len(ContextStack) <= ContextIndex:
ContextStack.append({})
ContextStack[ContextIndex] = {}
for k in ContextStack[ContextIndex - 1].keys():
ContextStack[ContextIndex][k] = ContextStack[ContextIndex - 1][k]
def pop_context():
global ContextStack, ContextIndex
ContextIndex = ContextIndex - 1
def build_code(MyDriver, p):
if Verbose:
print 'Code:', p
print "Function:", p['name']
MyCode = {}
MyCode['name'] = p['name']
MyCode['reference_count'] = 0
if 'type' in p:
MyCode['type'] = p['type']
MyCode['text'] = p['text']
if Verbose:
for line in p['text']:
print " Line:", line
return MyCode
def build_variable(MyDriver, p):
global FunctionTypes
global DriveableFunctionTypes
if Verbose:
print 'Variable:', p
MyVar = {}
MyVar['Property'] = {}
# 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]
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
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 '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)];
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()
return MyGroup
def build_driver(MyDriver, TheTree):
if Verbose:
print "TheTree:", TheTree
init_context()
for item in [x for x in TheTree if 'Code' in x]:
MyCode = build_code(MyDriver, item['Code'])
MyDriver['Funcs'][MyCode['name']] = MyCode
for item in [x for x in TheTree if 'Group' in x]:
MyGroup = build_group(MyDriver, item['Group'])
MyDriver['Groups'][MyGroup['name']] = MyGroup
for item in TheTree:
if Verbose:
print "Driver Item:", item
if 'Group' in item:
continue
elif 'Code' in item:
continue
else:
if 'DriverProperty' in item:
if 'DriverProperty' not in MyDriver:
MyDriver['DriverProperty'] = {}
MyDriver['DriverProperty'][item['DriverProperty'][0]] = item['DriverProperty'][1]
continue
for key in item:
MyDriver[key] = item[key]
for item in MyDriver['Permlink']:
if len(MyDriver['Permlink'][item]) > 1:
print 'Error: duplicate permlink entries for "%s"' % item, MyDriver['Permlink'][item]
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
for subitem in sorted([i for i in vars[item] if i not in FunctionTypes + DriveableFunctionTypes]):
print indent + ' %s =' % subitem, vars[item][subitem]
for subitem in sorted([i for i in vars[item] if i in FunctionTypes]):
print indent + ' %s =' % subitem, vars[item][subitem]
for subitem in sorted([i for i in vars[item] if i in DriveableFunctionTypes]):
print indent + ' %s =' % subitem, vars[item][subitem]
print indent + ' }'
def dump_driver_groups(groups, indent):
for item in sorted(groups):
if item:
print indent + 'GROUP ' + item + ' = {'
else:
print indent + 'GROUP = {'
for subitem in sorted([x for x in groups[item] if not x in ['Groups', 'Vars']]):
print indent + ' ', subitem, '=', groups[item][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'] + ' = {'
for item in sorted([x for x in MyDriver if x not in ['Groups', 'Funcs']]):
print ' ' + item + ' =', MyDriver[item]
#print 'Groups:', MyDriver['Groups']
dump_driver_groups(MyDriver['Groups'], ' ')
#print 'Funcs:', MyDriver['Funcs']
dump_driver_funcs(MyDriver['Funcs'])
print '}'
#
# Code Generation Functions
#
def emit(txt):
global NumberOfLinesOut
NumberOfLinesOut += len(txt)
for line in txt:
fdo.write(line)
if not line.endswith('\n'):
fdo.write('\n')
def put_preamble(MyDriver):
txt = []
txt += ['# Generated driver for %s' % MyDriver['name']]
txt += ['# vim: 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' in MyDriver and 'id' in MyDriver['make_args'].split():
pass
else:
txt += [' if { ![info exists ::scobj::permlink_device_counter]} {']
txt += [' set ::scobj::permlink_device_counter 0']
txt += [' }']
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 += [' 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 += [' set pv "[hval ${tc_root}/[sct driveable]]"']
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]]']
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 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_group(MyDriver, MyGroup):
readable_or_writeable = False
txt = []
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 = ''
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 <dev>_<group...>_<name> 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']:
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 [SplitReply [%s]] "false"]} {' % MyDriver['simulation_group']]
for var in sorted(MyGroup['Vars']):
MyVar = MyGroup['Vars'][var]
nodename = groupname + MyVar['name']
if MyVar['readable'] > 0:
poll_period = MyVar['readable']
if poll_period < 1 or poll_period > 300:
poll_period = 5
txt += [' ${sct_controller} poll ${scobj_hpath}/%s %s' % (nodename, poll_period)]
for var in sorted(MyGroup['Vars']):
MyVar = MyGroup['Vars'][var]
nodename = groupname + MyVar['name']
if MyVar['writeable'] > 0 or MyVar['driveable']:
txt += [' ${sct_controller} write ${scobj_hpath}/%s' % nodename]
if MyVar['driveable']:
# Generate <dev>_<group...>_<name> at runtime for driveable
driveable = '${name}_' + make_path(MyVar)
txt += [' ansto_makesctdrive %s ${scobj_hpath}/%s ${scobj_hpath}/%s ${sct_controller}' % (driveable, nodename, MyVar['driveable'])]
txt += [' } else {']
txt += [' %s::sics_log 9 "[%s] => No poll/write for %s"' % (MyDriver['namespace'], MyDriver['simulation_group'], MyDriver['name'])]
txt += [' }']
for grp in sorted(MyGroup['Groups']):
txt += put_group(MyDriver, MyGroup['Groups'][grp])
return txt
def put_mkDriver(MyDriver):
txt = ['']
if 'make_args' in MyDriver:
line = 'proc %s::mkDriver { sct_controller name %s } {' % (MyDriver['namespace'], MyDriver['make_args'])
else:
line = 'proc %s::mkDriver { sct_controller name } {' % (MyDriver['namespace'])
txt += [line]
if 'make_args' in MyDriver:
make_args = ' '.join(["${%s}"%arg for arg in MyDriver['make_args'].split()])
txt += [' %s::sics_log 9 "%s::mkDriver ${sct_controller} ${name} %s"' % (MyDriver['namespace'], MyDriver['namespace'], make_args)]
else:
txt += [' %s::sics_log 9 "%s::mkDriver for ${name}"' % (MyDriver['namespace'], MyDriver['namespace'])]
txt += [' set ns "[namespace current]"']
txt += [' set catch_status [ catch {']
txt += ['']
if len(MyDriver['Permlink']) > 0:
if 'make_args' in MyDriver and 'id' in MyDriver['make_args'].split():
pass
else:
txt += [' set permlink_device_number [format "%02d" [incr ::scobj::permlink_device_counter]]']
txt += ['']
txt += [' MakeSICSObj ${name} SCT_OBJECT']
txt += ['']
txt += [' sicslist setatt ${name} klass %s' % MyDriver['class']]
txt += [' sicslist setatt ${name} long_name ${name}']
if 'DriverProperty' in MyDriver:
for key in MyDriver['DriverProperty']:
txt += [' sicslist setatt ${name} %s "%s"' % (key, MyDriver['DriverProperty'][key])]
txt += ['']
txt += [' set scobj_hpath /sics/${name}']
for group in sorted(MyDriver['Groups']):
txt += put_group(MyDriver, MyDriver['Groups'][group])
txt += [' hsetprop ${scobj_hpath} klass %s' % MyDriver['class']]
txt += [' hsetprop ${scobj_hpath} debug_threshold %s' % str(MyDriver['debug_threshold'])]
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_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 += ['}']
txt += ['']
if 'add_args' in MyDriver:
line = 'proc add_%s {name IP port %s} {' % (MyDriver['name'], MyDriver['add_args'])
else:
line = 'proc add_%s {name IP port} {' % MyDriver['name']
txt += [line]
txt += [' set catch_status [ catch {']
if 'make_args' in MyDriver:
make_args = ' '.join(["${%s}"%arg for arg in MyDriver['make_args'].split()])
txt += [' %s::sics_log 9 "add_%s ${name} ${IP} ${port} %s"' % (MyDriver['namespace'], MyDriver['name'], make_args)]
else:
txt += [' %s::sics_log 9 "add_%s ${name} ${IP} ${port}"' % (MyDriver['namespace'], MyDriver['name'])]
txt += [' if {[string equal -nocase [SplitReply [%s]] "false"]} {' % MyDriver['simulation_group']]
txt += [' if {[string equal -nocase "aqadapter" "${IP}"]} {']
txt += [' %s::sics_log 9 "makesctcontroller sct_${name} aqadapter ${port}"' % MyDriver['namespace']]
txt += [' makesctcontroller sct_${name} aqadapter ${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}:${port} %s"' % (MyDriver['namespace'], MyDriver['protocol'], protocol_args)]
txt += [' makesctcontroller sct_${name} %s ${IP}:${port} %s' % (MyDriver['protocol'], MyDriver['protocol_args'])]
else:
txt += [' %s::sics_log 9 "makesctcontroller sct_${name} %s ${IP}:${port}"' % (MyDriver['namespace'], MyDriver['protocol'])]
txt += [' makesctcontroller sct_${name} %s ${IP}:${port}' % MyDriver['protocol']]
txt += [' }']
txt += [' } else {']
txt += [' %s::sics_log 9 "[%s] => No sctcontroller for %s"' % (MyDriver['namespace'], MyDriver['simulation_group'], MyDriver['name'])]
txt += [' }']
if 'make_args' in MyDriver:
make_args = ' '.join(["${%s}"%arg for arg in MyDriver['make_args'].split()])
txt += [' %s::sics_log 1 "%s::mkDriver sct_${name} ${name} %s"' % (MyDriver['namespace'], MyDriver['namespace'], make_args)]
txt += [' %s::mkDriver sct_${name} ${name} %s' % (MyDriver['namespace'], make_args)]
else:
txt += [' %s::sics_log 1 "%s::mkDriver sct_${name} ${name}"' % (MyDriver['namespace'], MyDriver['namespace'])]
txt += [' %s::mkDriver sct_${name} ${name}' % (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 += ['}']
txt += ['']
txt += ['puts stdout "file evaluation of sct_%s.tcl"' % MyDriver['name']]
txt += ['%s::sics_log 9 "file evaluation of sct_%s.tcl"' % (MyDriver['namespace'], MyDriver['name'])]
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 v} $::config_dict {']
txt += [' if { [dict exists $v "driver"] } {']
txt += [' if { [dict get $v "driver"] == "%s" } {' % MyDriver['name']]
txt += [' if { [dict get $v enabled] } {']
txt += [' set name [dict get $v name]']
txt += [' if { ![string equal -nocase [SplitReply [%s]] "false"] } {' % MyDriver['simulation_group']]
txt += [' set asyncqueue "null"']
txt += [' ${ns}::sics_log 9 "[%s] => using null asyncqueue"' % MyDriver['simulation_group']]
txt += [' } elseif { [dict exists $v "asyncqueue"] } {']
txt += [' set asyncqueue [dict get $v "asyncqueue"]']
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 [dict get $v ip]']
txt += [' set PORT [dict get $v port]']
txt += [' MakeAsyncQueue ${asyncqueue} ${asyncprotocol} ${IP} ${PORT}']
txt += [' if { [dict exists $v "timeout"] } {']
txt += [' ${asyncqueue} timeout "[dict get $v "timeout"]"']
txt += [' }']
txt += [' }']
if 'make_args' in MyDriver:
txt += [' set arg_list [list]']
txt += [' foreach arg {' + MyDriver['make_args'] + '} {']
txt += [' if {[dict exists $v $arg]} {']
txt += [' lappend arg_list "[dict get $v $arg]"']
txt += [' } else {']
txt += [' ${ns}::sics_log 9 "Missing configuration value $arg"']
txt += [' error "Missing configuration value $arg"']
txt += [' }']
txt += [' }']
txt += [' add_%s ${name} "aqadapter" ${asyncqueue} {*}$arg_list' % MyDriver['name']]
else:
txt += [' add_%s ${name} "aqadapter" ${asyncqueue}' % MyDriver['name']]
txt += [' }']
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
full_filename = filename = "sct_%s.tcl" % MyDriver['name']
if 'PathName' in MyDriver:
full_filename = os.path.join(MyDriver['PathName'], filename)
fdo = open(full_filename, 'w')
put_preamble(MyDriver)
put_standard_code(MyDriver)
put_mkDriver(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
print "Produced file %s with %d lines." % (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'] = {}
build_driver(MyDriver, TheDrivers[driver])
if Verbose:
print "MyDriver:", MyDriver['name'], '=', MyDriver
if DriverDump or Verbose:
dump_driver(MyDriver)
generate_driver(MyDriver)
def process_source(source_files):
global PathName
global TheDrivers
global NumberOfLinesIn
TheDrivers = {}
#
# Build the lexer
#
lexer = lex.lex()
#
# Build the parser
#
#yaccer = yacc.yacc(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
process_drivers(TheDrivers)
def main():
global Verbose
global DriverDump
global CodeDump
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("-c", "--code", help="dump code",
action="store_true")
parser.add_argument("-d", "--driver", help="dump driver",
action="store_true")
parser.add_argument("-v", "--verbose", help="verbose output",
action="store_true")
parser.add_argument("driver_source", help="driver source file", nargs="*")
args = parser.parse_args()
print args
if args.code:
CodeDump = True
else:
CodeDump = False
if args.driver:
DriverDump = True
else:
DriverDump = False
if args.verbose:
Verbose = True
else:
Verbose = False
source_files = args.driver_source
if source_files:
process_source(source_files)
if __name__ == "__main__":
main()