mirror of https://github.com/Desuuuu/klipper.git
buildcommands: Move command/response code generation to its own class
Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
This commit is contained in:
parent
67a5cd0409
commit
9f420f71a5
|
@ -120,10 +120,64 @@ Handlers.append(HandleConstants())
|
||||||
|
|
||||||
|
|
||||||
######################################################################
|
######################################################################
|
||||||
# Command and output parser generation
|
# Wire protocol commands and responses
|
||||||
######################################################################
|
######################################################################
|
||||||
|
|
||||||
def build_parser(parser, iscmd, all_param_types):
|
# Dynamic command and response registration
|
||||||
|
class HandleCommandGeneration:
|
||||||
|
def __init__(self):
|
||||||
|
self.commands = {}
|
||||||
|
self.encoders = []
|
||||||
|
self.msg_to_id = { m: i for i, m in msgproto.DefaultMessages.items() }
|
||||||
|
self.messages_by_name = { m.split()[0]: m for m in self.msg_to_id }
|
||||||
|
self.all_param_types = {}
|
||||||
|
self.ctr_dispatch = {
|
||||||
|
'_DECL_COMMAND': self.decl_command,
|
||||||
|
'_DECL_ENCODER': self.decl_encoder,
|
||||||
|
'_DECL_OUTPUT': self.decl_output
|
||||||
|
}
|
||||||
|
def decl_command(self, req):
|
||||||
|
funcname, flags, msgname = req.split()[1:4]
|
||||||
|
if msgname in self.commands:
|
||||||
|
error("Multiple definitions for command '%s'" % msgname)
|
||||||
|
self.commands[msgname] = (funcname, flags, msgname)
|
||||||
|
msg = req.split(None, 3)[3]
|
||||||
|
m = self.messages_by_name.get(msgname)
|
||||||
|
if m is not None and m != msg:
|
||||||
|
error("Conflicting definition for command '%s'" % msgname)
|
||||||
|
self.messages_by_name[msgname] = msg
|
||||||
|
def decl_encoder(self, req):
|
||||||
|
msg = req.split(None, 1)[1]
|
||||||
|
msgname = msg.split()[0]
|
||||||
|
m = self.messages_by_name.get(msgname)
|
||||||
|
if m is not None and m != msg:
|
||||||
|
error("Conflicting definition for message '%s'" % msgname)
|
||||||
|
self.messages_by_name[msgname] = msg
|
||||||
|
self.encoders.append((msgname, msg))
|
||||||
|
def decl_output(self, req):
|
||||||
|
msg = req.split(None, 1)[1]
|
||||||
|
self.encoders.append((None, msg))
|
||||||
|
def create_message_ids(self):
|
||||||
|
# Create unique ids for each message type
|
||||||
|
msgid = max(self.msg_to_id.values())
|
||||||
|
for msgname in self.commands.keys() + [m for n, m in self.encoders]:
|
||||||
|
msg = self.messages_by_name.get(msgname, msgname)
|
||||||
|
if msg not in self.msg_to_id:
|
||||||
|
msgid += 1
|
||||||
|
self.msg_to_id[msg] = msgid
|
||||||
|
def update_data_dictionary(self, data):
|
||||||
|
self.create_message_ids()
|
||||||
|
messages = { msgid: msg for msg, msgid in self.msg_to_id.items() }
|
||||||
|
data['messages'] = messages
|
||||||
|
commands = [self.msg_to_id[msg]
|
||||||
|
for msgname, msg in self.messages_by_name.items()
|
||||||
|
if msgname in self.commands]
|
||||||
|
data['commands'] = sorted(commands)
|
||||||
|
responses = [self.msg_to_id[msg]
|
||||||
|
for msgname, msg in self.messages_by_name.items()
|
||||||
|
if msgname not in self.commands]
|
||||||
|
data['responses'] = sorted(responses)
|
||||||
|
def build_parser(self, parser, iscmd):
|
||||||
if parser.name == "#output":
|
if parser.name == "#output":
|
||||||
comment = "Output: " + parser.msgformat
|
comment = "Output: " + parser.msgformat
|
||||||
else:
|
else:
|
||||||
|
@ -131,10 +185,10 @@ def build_parser(parser, iscmd, all_param_types):
|
||||||
params = '0'
|
params = '0'
|
||||||
types = tuple([t.__class__.__name__ for t in parser.param_types])
|
types = tuple([t.__class__.__name__ for t in parser.param_types])
|
||||||
if types:
|
if types:
|
||||||
paramid = all_param_types.get(types)
|
paramid = self.all_param_types.get(types)
|
||||||
if paramid is None:
|
if paramid is None:
|
||||||
paramid = len(all_param_types)
|
paramid = len(self.all_param_types)
|
||||||
all_param_types[types] = paramid
|
self.all_param_types[types] = paramid
|
||||||
params = 'command_parameters%d' % (paramid,)
|
params = 'command_parameters%d' % (paramid,)
|
||||||
out = """
|
out = """
|
||||||
// %s
|
// %s
|
||||||
|
@ -152,14 +206,13 @@ def build_parser(parser, iscmd, all_param_types):
|
||||||
+ sum([t.max_length for t in parser.param_types])))
|
+ sum([t.max_length for t in parser.param_types])))
|
||||||
out += " .max_size=%d," % (max_size,)
|
out += " .max_size=%d," % (max_size,)
|
||||||
return out
|
return out
|
||||||
|
def generate_responses_code(self):
|
||||||
def build_encoders(encoders, msg_to_id, all_param_types):
|
|
||||||
encoder_defs = []
|
encoder_defs = []
|
||||||
output_code = []
|
output_code = []
|
||||||
encoder_code = []
|
encoder_code = []
|
||||||
did_output = {}
|
did_output = {}
|
||||||
for msgname, msg in encoders:
|
for msgname, msg in self.encoders:
|
||||||
msgid = msg_to_id[msg]
|
msgid = self.msg_to_id[msg]
|
||||||
if msgid in did_output:
|
if msgid in did_output:
|
||||||
continue
|
continue
|
||||||
s = msg
|
s = msg
|
||||||
|
@ -172,7 +225,7 @@ def build_encoders(encoders, msg_to_id, all_param_types):
|
||||||
else:
|
else:
|
||||||
parser = msgproto.MessageFormat(msgid, msg)
|
parser = msgproto.MessageFormat(msgid, msg)
|
||||||
encoder_code.append(code)
|
encoder_code.append(code)
|
||||||
parsercode = build_parser(parser, 0, all_param_types)
|
parsercode = self.build_parser(parser, 0)
|
||||||
encoder_defs.append(
|
encoder_defs.append(
|
||||||
"const struct command_encoder command_encoder_%s PROGMEM = {"
|
"const struct command_encoder command_encoder_%s PROGMEM = {"
|
||||||
" %s\n};\n" % (
|
" %s\n};\n" % (
|
||||||
|
@ -194,21 +247,14 @@ ctr_lookup_output(const char *str)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
return fmt % ("".join(encoder_defs).strip(), "".join(encoder_code).strip(),
|
return fmt % ("".join(encoder_defs).strip(),
|
||||||
|
"".join(encoder_code).strip(),
|
||||||
"".join(output_code).strip())
|
"".join(output_code).strip())
|
||||||
|
def generate_commands_code(self):
|
||||||
def build_param_types(all_param_types):
|
cmd_by_id = {
|
||||||
sorted_param_types = sorted([(i, a) for a, i in all_param_types.items()])
|
self.msg_to_id[self.messages_by_name.get(msgname, msgname)]: cmd
|
||||||
params = ['']
|
for msgname, cmd in self.commands.items()
|
||||||
for paramid, argtypes in sorted_param_types:
|
}
|
||||||
params.append(
|
|
||||||
'static const uint8_t command_parameters%d[] PROGMEM = {\n'
|
|
||||||
' %s };' % (
|
|
||||||
paramid, ', '.join(argtypes),))
|
|
||||||
params.append('')
|
|
||||||
return "\n".join(params)
|
|
||||||
|
|
||||||
def build_commands(cmd_by_id, messages_by_name, all_param_types):
|
|
||||||
max_cmd_msgid = max(cmd_by_id.keys())
|
max_cmd_msgid = max(cmd_by_id.keys())
|
||||||
index = []
|
index = []
|
||||||
externs = {}
|
externs = {}
|
||||||
|
@ -217,10 +263,10 @@ def build_commands(cmd_by_id, messages_by_name, all_param_types):
|
||||||
index.append(" {\n},")
|
index.append(" {\n},")
|
||||||
continue
|
continue
|
||||||
funcname, flags, msgname = cmd_by_id[msgid]
|
funcname, flags, msgname = cmd_by_id[msgid]
|
||||||
msg = messages_by_name[msgname]
|
msg = self.messages_by_name[msgname]
|
||||||
externs[funcname] = 1
|
externs[funcname] = 1
|
||||||
parser = msgproto.MessageFormat(msgid, msg)
|
parser = msgproto.MessageFormat(msgid, msg)
|
||||||
parsercode = build_parser(parser, 1, all_param_types)
|
parsercode = self.build_parser(parser, 1)
|
||||||
index.append(" {%s\n .flags=%s,\n .func=%s\n}," % (
|
index.append(" {%s\n .flags=%s,\n .func=%s\n}," % (
|
||||||
parsercode, flags, funcname))
|
parsercode, flags, funcname))
|
||||||
index = "".join(index).strip()
|
index = "".join(index).strip()
|
||||||
|
@ -236,21 +282,34 @@ const struct command_parser command_index[] PROGMEM = {
|
||||||
const uint8_t command_index_size PROGMEM = ARRAY_SIZE(command_index);
|
const uint8_t command_index_size PROGMEM = ARRAY_SIZE(command_index);
|
||||||
"""
|
"""
|
||||||
return fmt % (externs, index)
|
return fmt % (externs, index)
|
||||||
|
def generate_param_code(self):
|
||||||
|
sorted_param_types = sorted(
|
||||||
|
[(i, a) for a, i in self.all_param_types.items()])
|
||||||
|
params = ['']
|
||||||
|
for paramid, argtypes in sorted_param_types:
|
||||||
|
params.append(
|
||||||
|
'static const uint8_t command_parameters%d[] PROGMEM = {\n'
|
||||||
|
' %s };' % (
|
||||||
|
paramid, ', '.join(argtypes),))
|
||||||
|
params.append('')
|
||||||
|
return "\n".join(params)
|
||||||
|
def generate_code(self):
|
||||||
|
parsercode = self.generate_responses_code()
|
||||||
|
cmdcode = self.generate_commands_code()
|
||||||
|
paramcode = self.generate_param_code()
|
||||||
|
return paramcode + parsercode + cmdcode
|
||||||
|
|
||||||
|
Handlers.append(HandleCommandGeneration())
|
||||||
|
|
||||||
|
|
||||||
######################################################################
|
######################################################################
|
||||||
# Identify data dictionary generation
|
# Identify data dictionary generation
|
||||||
######################################################################
|
######################################################################
|
||||||
|
|
||||||
def build_identify(cmd_by_id, msg_to_id, responses, version, toolstr):
|
def build_identify(version, toolstr):
|
||||||
#commands, messages
|
|
||||||
messages = dict((msgid, msg) for msg, msgid in msg_to_id.items())
|
|
||||||
data = {}
|
data = {}
|
||||||
for h in Handlers:
|
for h in Handlers:
|
||||||
h.update_data_dictionary(data)
|
h.update_data_dictionary(data)
|
||||||
data['messages'] = messages
|
|
||||||
data['commands'] = sorted(cmd_by_id.keys())
|
|
||||||
data['responses'] = sorted(responses)
|
|
||||||
data['version'] = version
|
data['version'] = version
|
||||||
data['build_versions'] = toolstr
|
data['build_versions'] = toolstr
|
||||||
|
|
||||||
|
@ -371,11 +430,6 @@ def main():
|
||||||
if options.verbose:
|
if options.verbose:
|
||||||
logging.basicConfig(level=logging.DEBUG)
|
logging.basicConfig(level=logging.DEBUG)
|
||||||
|
|
||||||
# Setup
|
|
||||||
commands = {}
|
|
||||||
messages_by_name = dict((m.split()[0], m)
|
|
||||||
for m in msgproto.DefaultMessages.values())
|
|
||||||
encoders = []
|
|
||||||
# Parse request file
|
# Parse request file
|
||||||
ctr_dispatch = { k: v for h in Handlers for k, v in h.ctr_dispatch.items() }
|
ctr_dispatch = { k: v for h in Handlers for k, v in h.ctr_dispatch.items() }
|
||||||
f = open(incmdfile, 'rb')
|
f = open(incmdfile, 'rb')
|
||||||
|
@ -383,62 +437,21 @@ def main():
|
||||||
f.close()
|
f.close()
|
||||||
for req in data.split('\0'):
|
for req in data.split('\0'):
|
||||||
req = req.lstrip()
|
req = req.lstrip()
|
||||||
parts = req.split()
|
if not req:
|
||||||
if not parts:
|
|
||||||
continue
|
continue
|
||||||
cmd = parts[0]
|
cmd = req.split()[0]
|
||||||
msg = req[len(cmd)+1:]
|
if cmd not in ctr_dispatch:
|
||||||
if cmd in ctr_dispatch:
|
|
||||||
ctr_dispatch[cmd](req)
|
|
||||||
elif cmd == '_DECL_COMMAND':
|
|
||||||
funcname, flags, msgname = parts[1:4]
|
|
||||||
if msgname in commands:
|
|
||||||
error("Multiple definitions for command '%s'" % msgname)
|
|
||||||
commands[msgname] = (funcname, flags, msgname)
|
|
||||||
msg = req.split(None, 3)[3]
|
|
||||||
m = messages_by_name.get(msgname)
|
|
||||||
if m is not None and m != msg:
|
|
||||||
error("Conflicting definition for command '%s'" % msgname)
|
|
||||||
messages_by_name[msgname] = msg
|
|
||||||
elif cmd == '_DECL_ENCODER':
|
|
||||||
msgname = parts[1]
|
|
||||||
m = messages_by_name.get(msgname)
|
|
||||||
if m is not None and m != msg:
|
|
||||||
error("Conflicting definition for message '%s'" % msgname)
|
|
||||||
messages_by_name[msgname] = msg
|
|
||||||
encoders.append((msgname, msg))
|
|
||||||
elif cmd == '_DECL_OUTPUT':
|
|
||||||
encoders.append((None, msg))
|
|
||||||
else:
|
|
||||||
error("Unknown build time command '%s'" % cmd)
|
error("Unknown build time command '%s'" % cmd)
|
||||||
# Create unique ids for each message type
|
ctr_dispatch[cmd](req)
|
||||||
msgid = max(msgproto.DefaultMessages.keys())
|
|
||||||
msg_to_id = dict((m, i) for i, m in msgproto.DefaultMessages.items())
|
|
||||||
for msgname in commands.keys() + [m for n, m in encoders]:
|
|
||||||
msg = messages_by_name.get(msgname, msgname)
|
|
||||||
if msg not in msg_to_id:
|
|
||||||
msgid += 1
|
|
||||||
msg_to_id[msg] = msgid
|
|
||||||
# Create message definitions
|
|
||||||
all_param_types = {}
|
|
||||||
parsercode = build_encoders(encoders, msg_to_id, all_param_types)
|
|
||||||
# Create command definitions
|
|
||||||
cmd_by_id = dict((msg_to_id[messages_by_name.get(msgname, msgname)], cmd)
|
|
||||||
for msgname, cmd in commands.items())
|
|
||||||
cmdcode = build_commands(cmd_by_id, messages_by_name, all_param_types)
|
|
||||||
paramcode = build_param_types(all_param_types)
|
|
||||||
# Create identify information
|
# Create identify information
|
||||||
cleanbuild, toolstr = tool_versions(options.tools)
|
cleanbuild, toolstr = tool_versions(options.tools)
|
||||||
version = build_version(options.extra)
|
version = build_version(options.extra)
|
||||||
sys.stdout.write("Version: %s\n" % (version,))
|
sys.stdout.write("Version: %s\n" % (version,))
|
||||||
responses = [msg_to_id[msg] for msgname, msg in messages_by_name.items()
|
datadict, icode = build_identify(version, toolstr)
|
||||||
if msgname not in commands]
|
|
||||||
datadict, icode = build_identify(
|
|
||||||
cmd_by_id, msg_to_id, responses, version, toolstr)
|
|
||||||
# Write output
|
# Write output
|
||||||
f = open(outcfile, 'wb')
|
f = open(outcfile, 'wb')
|
||||||
f.write(FILEHEADER + "".join([h.generate_code() for h in Handlers])
|
f.write(FILEHEADER + "".join([h.generate_code() for h in Handlers])
|
||||||
+ paramcode + parsercode + cmdcode + icode)
|
+ icode)
|
||||||
f.close()
|
f.close()
|
||||||
|
|
||||||
# Write data dictionary
|
# Write data dictionary
|
||||||
|
|
Loading…
Reference in New Issue