mirror of
https://github.com/slsdetectorgroup/slsDetectorPackage.git
synced 2025-04-20 02:40:03 +02:00
* commands code generation (#803) * commands code generation for only frames command * fix cmake file and add Caller files * working exptime, fully extended commands file and its variants * start adding template commands * add INT_CMD_VEC_ID template * add list command, generate multiple bins, format code * reach 208 commands using the cpp macros * add tests for command parser * start adding tests for commands parser * fix typo to use commands.yaml * add more tests for command_parser * add all template functions (up to 218 commands) * finish template functions and add more CmdProxy.cpp functions (250+) * 257 commands * 300 commands the rest are very special commands * add special commands without generation * separate special functions from generated c++ file * implementing one command for put and get (buggy) * add infer action in a separate file * generate header for special commands from yaml * allow only 0 or 1 for bool inputs * group all commands in gen_commands.py * add help to gen_commands.py * add autocomplete bash script * autocompletion: add support for module levels and help * remove debugging line * add autocompletion, help to commands, change int [0,1] to bool * copy tests for Caller.cpp. Tests pass * update with the new developer branch changes * fix errors after merging (there is problems with tests) * fixed port/stopport in yaml (intput typo), added '_caller' to the test dac and test on chip dac command in global test for cmdcaller * undo previous test simulator debug change * add documentation for the generated code * reducing the comment to be replaced in length so formatting does not split into 2 lines * removed formatting specific style of C++11 in gen_commands.py to keep with the top level clang format of the project * regeneratign code for commands * automation generated * Redirect deprecated commands (#872) * working implementation, need to fix dac * fixed deprecation redirect for dac command * Detector specific autocomplete (#873) * working implementation, need to fix dac * fixed deprecation redirect for dac command * detector specific completion for dac * added autocomplete using detector specific * fixed error when autocompleting partial words * Generate commands/fix commands (#875) * fix vm_a, im_a etc have deg Celsius suffix, also help missing or changed in some places * dac: require det id for all, arg0 to be printed at output, help for onchip dac and dac, onchipdac: spacing * getscan detid and blocking trigger help * udp_Dstlist det_id fixed, but rx_id invalid * cmdApp in line with cmdLineApp (missing version, receiver_id, not creating det object in help action * added set_command to differentiate between check_det_id and require_det_id (mixed up), args: -1 needs to check for at least one argument * reordering * reordering and checked till integer_command_hex * fixed a lot more commands * fix caller tests for eiger * changes to tests after Bechir left * changing .cmd to .cmdcall for the caller commands * fixed tests for caller, still warning for setexptime about cast input * autocomplete ran * add moench test * regenerating autocomplete and commands * fixing other things from merge conflicts (renaming slsDetectorDefs to defs in commands.yaml) * formatting * added code injection to help (#876) * updated 3 commands to have get output that can be put into put (#877) * updated some commands to have get output that can be put into put * fix tests for clkdiv * adding help to free (#878) * removing old commands and renaming them, (also making it work for parameters command as it was still calling cmdproxy) (#879) * More helpful error messages for unsupported actions (#880) * removing old commands and renaming them, (also making it work for parameters command as it was still calling cmdproxy) * Added specific help for unsupported actions * fixed a vetofile get special exception message. more specific warning for special exception message instead of no function warning * added condition checking true in exceptions for special message --------- Co-authored-by: Bechir Brahem <bachbrahem@gmail.com> Co-authored-by: Erik Frojdh <erik.frojdh@gmail.com> Co-authored-by: Dhanya Thattil <dhanya.thattil@psi.ch>
277 lines
12 KiB
Python
277 lines
12 KiB
Python
import argparse
|
|
import os
|
|
import subprocess
|
|
from pathlib import Path
|
|
|
|
import yaml
|
|
|
|
from autocomplete.autocomplete import type_info
|
|
from cpp_codegen.codegen import codegen, if_block, for_block, function, else_block
|
|
from infer_action.check_infer import check_infer
|
|
from autocomplete.autocomplete import type_values
|
|
|
|
GEN_PATH = Path(__file__).parent
|
|
|
|
COMMANDS_PATH = GEN_PATH / 'extended_commands.yaml'
|
|
DEPRECATED_COMMANDS_PATH = GEN_PATH / 'deprecated_commands.yaml'
|
|
CPP_INPUT_PATH = GEN_PATH / 'Caller.in.cpp'
|
|
HEADER_INPUT_PATH = GEN_PATH / 'Caller.in.h'
|
|
CPP_OUTPUT_PATH = GEN_PATH.parent / 'src' / 'Caller.cpp'
|
|
HEADER_OUTPUT_PATH = GEN_PATH.parent / 'src' / 'Caller.h'
|
|
|
|
INFER_HEADER_INPUT_PATH = GEN_PATH / 'inferAction.in.h'
|
|
INFER_CPP_INPUT_PATH = GEN_PATH / 'inferAction.in.cpp'
|
|
INFER_HEADER_OUTPUT_PATH = GEN_PATH.parent / 'src' / 'inferAction.h'
|
|
INFER_CPP_OUTPUT_PATH = GEN_PATH.parent / 'src' / 'inferAction.cpp'
|
|
|
|
|
|
def generate(
|
|
commands_path=COMMANDS_PATH,
|
|
cpp_input_path=CPP_INPUT_PATH,
|
|
header_input_path=HEADER_INPUT_PATH,
|
|
cpp_output_path=CPP_OUTPUT_PATH,
|
|
header_output_path=HEADER_OUTPUT_PATH,
|
|
infer_header_input_path=INFER_HEADER_INPUT_PATH,
|
|
infer_cpp_input_path=INFER_CPP_INPUT_PATH,
|
|
infer_header_output_path=INFER_HEADER_OUTPUT_PATH,
|
|
infer_cpp_output_path=INFER_CPP_OUTPUT_PATH,
|
|
|
|
):
|
|
commands_config = yaml.unsafe_load(commands_path.open('r'))
|
|
deprecated_commands_config = yaml.unsafe_load(DEPRECATED_COMMANDS_PATH.open('r'))
|
|
type_dist, non_dist = check_infer(commands=commands_config)
|
|
|
|
codegen.open(cpp_output_path)
|
|
# write call function
|
|
codegen.write_opening(cpp_input_path)
|
|
|
|
# iterate over the commands and generate code for each
|
|
print(f"[X] found {len(commands_config)} commands")
|
|
print('[*] generating code for commands')
|
|
for command_name, command in commands_config.items():
|
|
if 'is_description' in command and command['is_description']:
|
|
continue
|
|
with function('std::string', 'Caller::' + command['function_alias'], [('int', 'action')]) as fn:
|
|
codegen.write_line('std::ostringstream os;')
|
|
|
|
# print help
|
|
codegen.write_line('// print help')
|
|
with if_block('action == slsDetectorDefs::HELP_ACTION'):
|
|
if command["help"].startswith('code:'):
|
|
codegen.write_line(command["help"].strip('code:'))
|
|
else:
|
|
codegen.write_line(f'os << "Command: {command_name}" << std::endl;')
|
|
codegen.write_line(f'os << R"V0G0N({command["help"]} )V0G0N" << std::endl;')
|
|
codegen.write_line('return os.str();')
|
|
|
|
# check if action and arguments are valid
|
|
|
|
codegen.write_line('// check if action and arguments are valid')
|
|
first = True
|
|
for action, action_params in command['actions'].items():
|
|
|
|
with if_block(f'action == {codegen.actions_dict[action]}', elseif=not first):
|
|
|
|
check_argc = True
|
|
for arg in action_params['args']:
|
|
if arg['argc'] == -1:
|
|
check_argc = False
|
|
break
|
|
# check number of arguments
|
|
condition = "1" if check_argc else "0"
|
|
|
|
if check_argc:
|
|
for arg in action_params['args']:
|
|
condition += f' && args.size() != {arg["argc"]}'
|
|
|
|
with if_block(condition):
|
|
codegen.write_line(f'throw RuntimeError("Wrong number of arguments for action {action}");')
|
|
|
|
for arg in action_params['args']:
|
|
if not check_argc:
|
|
continue
|
|
with if_block(f'args.size() == {arg["argc"]}'):
|
|
# check argument types
|
|
if 'extra_variables' in arg:
|
|
for var in arg['extra_variables']:
|
|
codegen.write_line(f'{var["type"]} {var["name"]} = {var["value"]};')
|
|
|
|
if 'separate_time_units' in arg and arg['separate_time_units']:
|
|
codegen.write_line(f'try {{')
|
|
# TODO: refactor this repeating code
|
|
codegen.write_line(f'std::string tmp_time({arg["separate_time_units"]["input"]});')
|
|
codegen.write_line(f'std::string {arg["separate_time_units"]["output"][1]}'
|
|
f' = RemoveUnit(tmp_time);')
|
|
codegen.write_line(f'auto {arg["separate_time_units"]["output"][0]} = '
|
|
f'StringTo < time::ns > (tmp_time,'
|
|
f' {arg["separate_time_units"]["output"][1]});')
|
|
codegen.write_line(
|
|
f'}} catch (...) {{ throw RuntimeError("Could not convert argument to time::ns");}}')
|
|
|
|
elif 'convert_to_time' in arg and arg['convert_to_time']:
|
|
codegen.write_line(f'try {{')
|
|
|
|
codegen.write_line(
|
|
f'StringTo < time::ns > ({", ".join(arg["convert_to_time"]["input"])});')
|
|
codegen.write_line(
|
|
f'}} catch (...) {{ throw RuntimeError("Could not convert arguments to time::ns");}}')
|
|
|
|
for i in range(len(arg['input'])):
|
|
if not arg['cast_input'][i]:
|
|
continue
|
|
codegen.write_line(f'try {{')
|
|
codegen.write_line(f'StringTo<{arg["input_types"][i]}>({arg["input"][i]});')
|
|
codegen.write_line(f'}} catch (...) {{')
|
|
codegen.write_line(
|
|
f' throw RuntimeError("Could not convert argument {i} to {arg["input_types"][i]}");')
|
|
codegen.write_line(f'}}')
|
|
first = False
|
|
with else_block():
|
|
codegen.write_line(
|
|
f'throw RuntimeError("INTERNAL ERROR: Invalid action: supported actions are {list(command["actions"].keys())}");')
|
|
|
|
# generate code for each action
|
|
codegen.write_line('// generate code for each action')
|
|
for action, action_params in command['actions'].items():
|
|
if 'detectors' in action_params:
|
|
codegen.write_line('auto detector_type = det->getDetectorType().squash();')
|
|
|
|
with if_block(f'action == {codegen.actions_dict[action]}'):
|
|
if 'detectors' in action_params:
|
|
first = True
|
|
for detector, detector_params in action_params['detectors'].items():
|
|
with if_block(f'detector_type == defs::{detector}', elseif=not first):
|
|
codegen.write_arg(detector_params, action, command_name)
|
|
|
|
else_block().__enter__()
|
|
|
|
if not action_params:
|
|
codegen.write_line(f'throw RuntimeError("detector not supported for action: {action}");')
|
|
else:
|
|
tmp_args = []
|
|
if 'args' in action_params:
|
|
tmp_args = action_params['args']
|
|
codegen.write_arg(tmp_args, action, command_name)
|
|
|
|
if 'detectors' in action_params:
|
|
else_block().__exit__()
|
|
|
|
codegen.write_line('return os.str();')
|
|
|
|
# close sls namespace
|
|
codegen.write_closing()
|
|
codegen.close()
|
|
print('[X] .cpp code generated')
|
|
deprecated_commands = []
|
|
codegen.write_header(header_input_path, header_output_path, commands_config, deprecated_commands_config)
|
|
print('[X] header code generated')
|
|
|
|
|
|
codegen.write_infer_header(infer_header_input_path, infer_header_output_path, commands_config) #TODO: add deprecated commands
|
|
print('[X] infer header code generated')
|
|
codegen.open(infer_cpp_output_path)
|
|
|
|
codegen.write_infer_cpp(infer_cpp_input_path, infer_cpp_output_path, commands_config, non_dist, type_dist)
|
|
codegen.close()
|
|
print('[X] infer cpp code generated')
|
|
|
|
|
|
if __name__ == '__main__':
|
|
parser = argparse.ArgumentParser(
|
|
description='generate c++ code for cli commands from the commands.yaml file',
|
|
)
|
|
parser.add_argument('-f', '--format', action='store_true', default=False, dest='format',
|
|
help='format header and cpp file using clang-format')
|
|
parser.add_argument('-p', '--parse', action='store_true', default=False, dest='parse',
|
|
help='parse the commands.yaml file into extended_commands.yaml')
|
|
parser.add_argument('-c', '--check', action='store_true', default=False, dest='check',
|
|
help='check missing commands')
|
|
parser.add_argument('-g', '--generate', action='store_true', default=False, dest='generate', help='generate code (C++ or bash if -a is used)')
|
|
parser.add_argument('-a', '--autocomplete', action='store_true', default=False, dest='autocomplete',
|
|
help='print bash autocomplete values')
|
|
cli_args = parser.parse_args()
|
|
|
|
if cli_args.autocomplete:
|
|
from autocomplete.autocomplete import generate_type_values, generate_bash_autocomplete
|
|
if cli_args.generate:
|
|
generate_bash_autocomplete()
|
|
print('[X] bash autocomplete generated')
|
|
generate_bash_autocomplete(
|
|
output_path=Path(__file__).parent / 'autocomplete' / 'zsh_autocomplete.sh',
|
|
input_path=Path(__file__).parent / 'autocomplete' / 'zsh_autocomplete.in.sh'
|
|
)
|
|
print('[X] zsh autocomplete generated')
|
|
exit(0)
|
|
else:
|
|
ret = generate_type_values()
|
|
print(ret)
|
|
exit(0)
|
|
|
|
if cli_args.check:
|
|
from commands_parser.commands_parser import command_parser
|
|
|
|
commands_config = yaml.unsafe_load(COMMANDS_PATH.open('r'))
|
|
|
|
# infer action based on number of arguments and types
|
|
type_dist, non_dist = check_infer(commands=commands_config)
|
|
|
|
command_parser.verify_format()
|
|
command_parser.parse_all_commands()
|
|
# generate list of commands found in sls_detector_get
|
|
glist_path = GEN_PATH / 'glist'
|
|
ret = subprocess.run([f"sls_detector_get list | tail -n +2 | sort > {glist_path.absolute()}"], shell=True,
|
|
capture_output=True, check=True)
|
|
if ret.stderr != b'':
|
|
print('[!] glist generation failed and glist not found')
|
|
exit(1)
|
|
|
|
if not COMMANDS_PATH.exists():
|
|
print('[!] extended_commands.yaml not found')
|
|
exit(1)
|
|
detglist = set(command['command_name'] for __, command in commands_config.items())
|
|
detglist.add('free')
|
|
detglist.add('list')
|
|
|
|
g_path = GEN_PATH / 'glist'
|
|
if not g_path.exists():
|
|
print('[!] glist not found')
|
|
exit(1)
|
|
glist = set(g_path.read_text().split('\n'))
|
|
if "" in glist:
|
|
glist.remove("")
|
|
if "" in detglist:
|
|
detglist.remove("")
|
|
|
|
not_found = set()
|
|
for command in glist:
|
|
if command not in detglist:
|
|
not_found.add(command)
|
|
print()
|
|
if len(not_found) > 0:
|
|
print(f'[!] found {len(not_found)} missing')
|
|
print(f"not_found: {not_found}")
|
|
else:
|
|
print(f'[X] found no missing commands')
|
|
|
|
for command in detglist:
|
|
if command not in glist:
|
|
print(f'[!] command {command} found in commands.yaml but not found in g list')
|
|
|
|
exit(0)
|
|
|
|
if cli_args.parse:
|
|
from commands_parser.commands_parser import command_parser
|
|
|
|
command_parser.verify_format()
|
|
command_parser.parse_all_commands()
|
|
|
|
if cli_args.generate:
|
|
generate()
|
|
|
|
if cli_args.format:
|
|
files = [CPP_OUTPUT_PATH, HEADER_OUTPUT_PATH, INFER_HEADER_OUTPUT_PATH, INFER_CPP_OUTPUT_PATH]
|
|
for file in files:
|
|
os.system(f'clang-format -i {file.absolute()}')
|
|
#os.system(f'clang-format -i --style="{{Standard: C++11}}" {file.absolute()}')
|
|
print('[X] code formatted')
|