from hydroparse import TYPE_LENGTHS from .gen_messages import generate_message_h from .gen_enums import generate_enums_h import os NS_PREFIX_LOWER = "hydrolink_" GENERIC_MSG_NAME = f"{NS_PREFIX_LOWER}msg" GENERIC_MSG_STRUCT = f"{GENERIC_MSG_NAME}_s" GENERIC_MSG_TYPE = f"{GENERIC_MSG_NAME}_t" MSG_ID_PREFIX = "HYDROLINK_MSG_ID_" def generate_main_header(msgs, dir): with open(os.path.join(dir, "hydrolink.h"), "w") as f: f.write(f"""#pragma once #include "protocol.h" {"\n".join(f"#include \"{GENERIC_MSG_NAME}_{msg.name_lower}.h\"" for msg in msgs)} """) def generate_protocol(xml, dir): put_defines = [] for typename in TYPE_LENGTHS: length = TYPE_LENGTHS[typename] if length == 1: put_defines.append( f"#define {NS_PREFIX_LOWER}put_{typename}(buf, wire_offset, b) buf[wire_offset] = ({typename})b" ) else: put_defines.append( f"#define {NS_PREFIX_LOWER}put_{typename}(buf, wire_offset, b) byte_swap_{length}(&buf[wire_offset], (const char *)&b)" ) put_defines_str = "\n".join(put_defines) with open(os.path.join(dir, "protocol.h"), "w") as f: f.write(f""" #pragma once #include #define HYDROLINK_HEADER_LEN 4 #define HYDROLINK_CRC_LEN 2 #define HYDROLINK_NON_PAYLOAD_LEN (HYDROLINK_HEADER_LEN + HYDROLINK_CRC_LEN) #define HYDROLINK_MAX_PAYLOAD_LEN {xml.largest_payload} #define HYDROLINK_MAX_MSG_LEN (HYDROLINK_MAX_PAYLOAD_LEN + HYDROLINK_NON_PAYLOAD_LEN) struct {GENERIC_MSG_STRUCT}; typedef struct {GENERIC_MSG_STRUCT} {GENERIC_MSG_TYPE}; struct {GENERIC_MSG_STRUCT} {{ uint8_t id; uint8_t payload_length; uint8_t dst_id; uint8_t src_id; uint8_t payload[HYDROLINK_MAX_PAYLOAD_LEN]; uint16_t crc; }}; #define HYDROLINK_PAYLOAD(msg) ((const char *)(&((msg)->payload[0]))) #define HYDROLINK_PAYLOAD_NON_CONST(msg) ((char *)(&((msg)->payload[0]))) {put_defines_str} inline void byte_swap_2(char *dst, const char *src) {{ dst[0] = src[1]; dst[1] = src[0]; }} inline void byte_swap_4(char *dst, const char *src) {{ dst[0] = src[3]; dst[1] = src[2]; dst[2] = src[1]; dst[3] = src[0]; }} inline void byte_swap_8(char *dst, const char *src) {{ dst[0] = src[7]; dst[1] = src[6]; dst[2] = src[5]; dst[3] = src[4]; dst[4] = src[3]; dst[5] = src[2]; dst[6] = src[1]; dst[7] = src[0]; }} """) f.write(f""" #define HYDROLINK_MSG_RETURN_TYPE(TYPE, SIZE) \\ static inline TYPE HYDROLINK_RETURN_## TYPE(const {GENERIC_MSG_TYPE} *msg, uint8_t offset) {{\\ TYPE ret; \\ byte_swap_## SIZE((char*)&ret, &HYDROLINK_PAYLOAD(msg)[offset]); \\ return ret; \\ }} """) for typename in TYPE_LENGTHS: length = TYPE_LENGTHS[typename] if not length > 1: f.write(f""" #define HYDROLINK_RETURN_{typename}(msg, wire_offset) ({typename})HYDROLINK_PAYLOAD(msg)[wire_offset]""") else: f.write(f""" HYDROLINK_MSG_RETURN_TYPE({typename}, {length}) """) f.write(""" inline void crc_xmodem_init(uint16_t *crc) { *crc = 0; } inline void crc_xmodem_accumulate(uint8_t data, uint16_t *crc) { *crc = *crc ^ ((uint16_t)data << 8); for (uint8_t j = 0; j < 8; j++) { if (*crc & 0x8000) { *crc = (*crc << 1) ^ 0x1021; } else { *crc <<= 1; } } } inline uint16_t crc_xmodem_calculate(const uint8_t *data, uint8_t length) { uint16_t crc; crc_xmodem_init(&crc); for (uint8_t i = 0; i < length; i++) { crc_xmodem_accumulate(*data, &crc); } return crc; } """) f.write(f""" void hydrolink_fill_header_and_crc({GENERIC_MSG_TYPE} *msg, uint8_t msg_id, uint8_t payload_length, uint8_t dst_id, uint8_t src_id, uint8_t crc_extra) {{ msg->id = msg_id; msg->payload_length = payload_length; msg->dst_id = dst_id; msg->src_id = src_id; uint16_t crc; crc_xmodem_init(&crc); for(int i = 0; i < msg->payload_length; ++i) {{ crc_xmodem_accumulate(msg->payload[i], &crc); }} crc_xmodem_accumulate(crc_extra, &crc); msg->crc = crc; }} uint8_t hydrolink_serialize_message({GENERIC_MSG_TYPE} *msg, uint8_t *buffer) {{ buffer[0] = msg->id; buffer[1] = msg->payload_length; buffer[2] = msg->dst_id; buffer[3] = msg->src_id; uint8_t *p = &buffer[4]; for(int i =0; ipayload_length;++i) {{ *p++ = msg->payload[i]; }} *p++ = (uint8_t)(msg->crc >> 8); *p++ = (uint8_t)(msg->crc & 0xff); return HYDROLINK_NON_PAYLOAD_LEN + msg->payload_length; }} uint8_t hydrolink_deserialize_header({GENERIC_MSG_TYPE} *msg, uint8_t *buffer, uint8_t buffer_length) {{ if (buffer_length < HYDROLINK_NON_PAYLOAD_LEN) {{ return 0; }} msg->id = buffer[0]; msg->payload_length = buffer[1]; msg->dst_id = buffer[2]; msg->src_id = buffer[3]; msg->crc = buffer[buffer_length-1] | (((uint16_t)buffer[buffer_length-2]) << 8); if (msg->payload_length + HYDROLINK_NON_PAYLOAD_LEN != buffer_length) {{ return 0; }} uint8_t *p = &buffer[HYDROLINK_HEADER_LEN]; for (int i = 0; i < msg->payload_length; ++i) {{ msg->payload[i] = *p++; }} return msg->payload_length + HYDROLINK_NON_PAYLOAD_LEN; }} """) def generate(xmls, out_dir): msgs = [] for x in xmls: msgs.extend(x.messages) for msg in msgs: generate_message_h(os.path.join(out_dir), msg) generate_main_header(msgs, out_dir) generate_protocol(xmls[0], out_dir) generate_enums_h(out_dir, xmls[0].enums)