diff --git a/.gitignore b/.gitignore index ed21527..a336324 100644 --- a/.gitignore +++ b/.gitignore @@ -3,16 +3,8 @@ compile_commands.json *.pyc # Files that should be written from CAN spec -src/constants.h -src/pack_unpack.c -src/pack_unpack.h -src/enum_atom.h -src/structs.h -src/computers/ -src/send_receive.c -src/bus.h -src/drivers/inc/ -src/test/ +src/* +src/** **/*.o **/*.out diff --git a/generator/computers_c.py b/generator/computers_c.py deleted file mode 100644 index 188b2e5..0000000 --- a/generator/computers_c.py +++ /dev/null @@ -1,10 +0,0 @@ -import os - - -def write(env, computers, input_path, output_path): - os.makedirs(output_path, exist_ok=True) - template = env.get_template(str(input_path)) - for computer in computers: - f_path = os.path.join(output_path, 'canlib_{}.c'.format(computer.name)) - with open(f_path, 'w') as f: - f.write(template.render(computer=computer)) diff --git a/generator/computers_cpp.py b/generator/computers_cpp.py new file mode 100644 index 0000000..c2d8e18 --- /dev/null +++ b/generator/computers_cpp.py @@ -0,0 +1,10 @@ +import os + + +def write(env, computers, input_path, output_path, testing): + os.makedirs(output_path, exist_ok=True) + template = env.get_template(str(input_path)) + for computer in computers: + f_path = os.path.join(output_path, 'canlib_{}.cpp'.format(computer.name)) + with open(f_path, 'w') as f: + f.write(template.render(computer=computer, testing=testing)) diff --git a/generator/computers_h.py b/generator/computers_h.py deleted file mode 100644 index dc5833b..0000000 --- a/generator/computers_h.py +++ /dev/null @@ -1,11 +0,0 @@ -import os - - -def write(env, computers, input_path, output_path): - os.makedirs(output_path, exist_ok=True) - template = env.get_template(str(input_path)) - - for computer in computers: - f_path = os.path.join(output_path, 'canlib_{}.h'.format(computer.name)) - with open(f_path, 'w') as f: - f.write(template.render(computer=computer)) diff --git a/generator/computers_hpp.py b/generator/computers_hpp.py new file mode 100644 index 0000000..c912942 --- /dev/null +++ b/generator/computers_hpp.py @@ -0,0 +1,11 @@ +import os + + +def write(env, computers, input_path, output_path, testing): + os.makedirs(output_path, exist_ok=True) + template = env.get_template(str(input_path)) + + for computer in computers: + f_path = os.path.join(output_path, 'canlib_{}.hpp'.format(computer.name)) + with open(f_path, 'w') as f: + f.write(template.render(computer=computer, testing=testing)) diff --git a/generator/drivers_inc.py b/generator/drivers_inc.py index 3efa784..da61641 100644 --- a/generator/drivers_inc.py +++ b/generator/drivers_inc.py @@ -13,16 +13,17 @@ def find_architecture(system, template): return None -def write(env, system, input_path, output_path): +def write(env, system, input_path, output_path, testing = False): input_path = Path(input_path) output_path = Path(output_path) output_path.mkdir(parents=True, exist_ok=True) for template_path in input_path.glob('*'): - architecture_name = template_path.stem.strip('.h') + architecture_name = template_path.stem.strip('.hpp') architecture = find_architecture(system, architecture_name) output_file = output_path.joinpath(template_path.stem) template = env.get_template(str(template_path)) - with open(output_file, 'w') as f: - f.write(template.render(architecture=architecture)) + if (testing == (architecture_name == 'testfamily')): + with open(output_file, 'w') as f: + f.write(template.render(architecture=architecture)) diff --git a/generator/main.py b/generator/main.py index e059e75..c8b267f 100644 --- a/generator/main.py +++ b/generator/main.py @@ -9,23 +9,38 @@ import ParseCAN.ParseCAN as ParseCAN import constants -import computers_h -import computers_c +import computers_hpp +import computers_cpp import drivers_inc -src_dir = Path('../src/') -constants_path = src_dir.joinpath('constants.h') -drivers_inc_dir_path = src_dir.joinpath('drivers/inc') -computer_h_dir_path = src_dir.joinpath('computers/inc') -computer_c_dir_path = src_dir.joinpath('computers/src') +src_dir = Path('../src/src/') +inc_dir = Path('../src/inc/') +drivers_inc_dir_path = inc_dir.joinpath('drivers/') +computer_hpp_dir_path = inc_dir.joinpath('computers/') +computer_cpp_dir_path = src_dir.joinpath('computers/') template_dir = Path('./templates/') -computer_c_template_path = template_dir.joinpath('computer.c.j2') -computer_h_template_path = template_dir.joinpath('computer.h.j2') -constants_template_path = template_dir.joinpath('constants.h.j2') +computer_cpp_template_path = template_dir.joinpath('computer.cpp.j2') +computer_hpp_template_path = template_dir.joinpath('computer.hpp.j2') drivers_inc_template_dir_path = template_dir.joinpath('drivers/inc') +from pint import UnitRegistry +def get_ms(period_str): + if type(period_str) is int: + # If it's set as an integer, assume ms + return period_str + + ur = UnitRegistry() + return (int)(ur[period_str].to('ms').magnitude) + +def get_num_msg_types(msg): + if hasattr(msg, "frame"): + return 1 + sum([get_num_msg_types(sub_frame) for sub_frame in msg.frame]) + return 1 + +def get_len(can): + return len(can.bus) # FROM: https://github.com/duelafn/python-jinja2-apci/blob/master/jinja2_apci/error.py class RaiseExtension(Extension): @@ -51,37 +66,61 @@ def parse(self, parser): def _raise(self, msg, caller): raise TemplateRuntimeError(msg) - def render_template_from_to(env, input_path, output_path): template = env.get_template(str(input_path)) with open(output_path, 'w') as f: - f.write(template.render()) - + if output_path in [inc_dir.joinpath("structs.hpp"), src_dir.joinpath("structs.cpp")]: + f.write(template.render(get_len = get_len, get_ms = get_ms, get_num_msg_types = get_num_msg_types)) + else: + f.write(template.render(get_len = get_len)) def render_template(env, relative_path): - render_template_from_to(env, template_dir.joinpath(f"{relative_path}.j2"), src_dir.joinpath(relative_path)) + if relative_path.endswith('hpp') or relative_path.endswith('h'): + render_template_from_to(env, template_dir.joinpath(f"{relative_path}.j2"), inc_dir.joinpath(relative_path)) + else: + render_template_from_to(env, template_dir.joinpath(f"{relative_path}.j2"), src_dir.joinpath(relative_path)) if __name__ == '__main__': - specpath = sys.argv[1] - specfile = open(specpath, 'r') - system = ParseCAN.spec.System.from_yaml(specfile) - can = system.protocol['name']['can'] - - script_dir = os.path.dirname(sys.argv[0]) - if script_dir == '': - script_dir = '.' - os.chdir(script_dir) - - template_loader = jinja2.FileSystemLoader(searchpath=".") - template_env = jinja2.Environment(loader=template_loader, keep_trailing_newline=True, extensions=[RaiseExtension]) - template_env.globals["can"] = can - template_env.globals["system"] = system - - for filename in ["pack_unpack.c", "pack_unpack.h", "enum_atom.h", "send_receive.c", "structs.h", "bus.h"]: - render_template(template_env, filename) - - constants.write(template_env, constants_template_path, constants_path) - computers_h.write(template_env, system.computer, computer_h_template_path, computer_h_dir_path) - computers_c.write(template_env, system.computer, computer_c_template_path, computer_c_dir_path) - drivers_inc.write(template_env, system, drivers_inc_template_dir_path, drivers_inc_dir_path) + pth = os.path.join(os.path.dirname(os.path.abspath(__file__)), '..') + spth = str(pth) + if '--clean' in sys.argv: + os.system("rm " + spth + "/src/inc/computers/*.hpp") + os.system("rm " + spth + "/src/inc/drivers/*.hpp") + os.system("rm " + spth + "/src/inc/bus.hpp " + spth + "/src/inc/pack_unpack.hpp " + spth + "/src/inc/structs.hpp") + os.system("rm " + spth + "/src/src/computers/*.cpp") + os.system("rm " + spth + "/src/src/bus.cpp " + spth + "/src/src/pack_unpack.cpp " + spth + "/src/src/structs.cpp") + else: + specpath = sys.argv[1] + specfile = open(specpath, 'r') + system = ParseCAN.spec.System.from_yaml(specfile) + can = system.protocol['name']['can'] + + script_dir = os.path.dirname(sys.argv[0]) + if script_dir == '': + script_dir = '.' + os.chdir(script_dir) + + template_loader = jinja2.FileSystemLoader(searchpath=".") + template_env = jinja2.Environment(loader=template_loader, keep_trailing_newline=True, extensions=[RaiseExtension]) + template_env.globals["can"] = can + template_env.globals["system"] = system + + for filename in ["pack_unpack.cpp", "pack_unpack.hpp", "structs.hpp", "bus.hpp", "bus.cpp", "structs.cpp"]: + render_template(template_env, filename) + + testing = '--testing' in sys.argv + computers_hpp.write(template_env, system.computer, computer_hpp_template_path, computer_hpp_dir_path, testing) + computers_cpp.write(template_env, system.computer, computer_cpp_template_path, computer_cpp_dir_path, testing) + drivers_inc.write(template_env, system, drivers_inc_template_dir_path, drivers_inc_dir_path, testing) + clang_style = "" + styles = [arg for arg in sys.argv if arg.startswith('style=')] + if len(styles) > 0: + clang_style = styles[0] + try: + os.system('clang-format -i ' + clang_style + ' ' + spth + '/src/inc/drivers/*.hpp ' + spth + '/src/src/drivers/*.cpp') + os.system('clang-format -i ' + clang_style + ' ' + spth + '/src/inc/computers/*.hpp ' + spth + '/src/src/computers/*.cpp') + os.system('clang-format -i ' + clang_style + ' ' + spth + '/src/inc/*.hpp') + os.system('clang-format -i ' + clang_style + ' ' + spth + '/src/src/*.cpp') + except: + print('Error during clang-format, is it installed?') diff --git a/generator/templates/bus.cpp.j2 b/generator/templates/bus.cpp.j2 new file mode 100644 index 0000000..f81acf9 --- /dev/null +++ b/generator/templates/bus.cpp.j2 @@ -0,0 +1,68 @@ +{%- macro key_type(num_bits) -%} uint{{ (((num_bits / 8) | round(method='ceil')) * 8) | int }}_t {%- endmacro -%} + +{%- macro last_sent_def(bus, msg, msg_name) -%} +{%- if msg.frame is defined -%} + {% for sub_frame in msg.frame -%} + {{ last_sent_def(bus, sub_frame, msg_name + '::' + sub_frame.name) }} + {%- endfor %} +{%- else -%} + Clock::time_point {{msg_name}}::last_sent_; +{%- endif %} +{%- endmacro -%} + +#include "static.hpp" +#include "bus.hpp" +#include "structs.hpp" +#include "pack_unpack.hpp" + +namespace CANlib { + +extern const std::pair* frame_id_range[{{ get_len(can) }}]; +extern const uint32_t* frame_len[{{ get_len(can) }}]; +extern const uint32_t* keys[{{ get_len(can) }}]; +extern const size_t can_size[{{ get_len(can) }}]; +extern Message** messages[{{ get_len(can) }}]; + +{% for bus in can.bus %} +{{ last_sent_def(bus, bus, bus.name) }} +{% endfor %} + +} + +using namespace CANlib; + +static uint32_t identify_internal(const uint32_t key, const Frame& frame, int l, int r, int bus_idx) { + for (int i = l;i <= r;++i) { + if (keys[bus_idx][i] == key) { + int left_bound = frame_id_range[bus_idx][i].first; + int right_bound = frame_id_range[bus_idx][i].second; + // If this is a frame search inside the frame, otherwise return the index of the message + if (left_bound <= right_bound) { + uint64_t bitstring; + to_bitstring((uint8_t*)frame.data, &bitstring); + return identify_internal(EXTRACT(bitstring, left_bound, right_bound), frame, i + 1, i + 1 + frame_len[bus_idx][i], bus_idx); + } else { + return i; + } + } + } + return 0; +} + + +uint32_t CANlib::identify(AbstractBus bus, const Frame& frame) { + const int bus_idx = static_cast(bus); + return identify_internal(frame.id, frame, 1, can_size[bus_idx] - 1, bus_idx); +} + +void CANlib::handle_frame(AbstractBus bus_name, const Frame& frame) { + if (bus_name == AbstractBus::INVALID_NAME) { + return; + } + + uint32_t message_idx = identify(bus_name, frame); + uint32_t bus_idx = static_cast(bus_name); + if (message_idx > 0) { + messages[bus_idx][message_idx]->pack(frame); + } +} diff --git a/generator/templates/bus.h.j2 b/generator/templates/bus.h.j2 deleted file mode 100644 index 8524ebb..0000000 --- a/generator/templates/bus.h.j2 +++ /dev/null @@ -1,34 +0,0 @@ -{%- set ns = namespace(first_frame=true) -%} - -{%- macro enum_name(bus, msg, msg_name=msg.name) -%} -{%- if msg.frame is defined -%} -{% for sub_frame in msg.frame -%} -{{ enum_name(bus, sub_frame, msg_name + '_' + sub_frame.name) }} -{%- endfor -%} -{%- else %} - CANlib_{{ bus.name }}_{{ msg_name }}{%- if ns.first_frame %} = 2 - {%- set ns.first_frame = false -%} - {%- endif -%}, -{%- endif -%} -{%- endmacro -%} -#pragma once - -#include "static.h" - -#define CANlib_UNKNOWN_MSG 0 - -typedef enum { -{%- for bus in can.bus %} - {{ bus.name }}, -{%- endfor %} -} CANlib_Bus_T; -{% for bus in can.bus %} -{%- set ns.first_frame = true %} -typedef enum { -{%- for msg in bus.frame -%} -{{- enum_name(bus, msg) }} -{%- endfor %} -} CANlib_{{ bus.name }}_T; - -CANlib_{{ bus.name }}_T CANlib_Identify_{{ bus.name }}(Frame* frame); -{% endfor -%} diff --git a/generator/templates/bus.hpp.j2 b/generator/templates/bus.hpp.j2 new file mode 100644 index 0000000..4d196db --- /dev/null +++ b/generator/templates/bus.hpp.j2 @@ -0,0 +1,40 @@ +{%- set ns = namespace(first_frame=true) -%} + +{%- macro enum_name(msg, msg_name=msg.name) -%} + {%- if msg.frame is defined -%} + {% for sub_frame in msg.frame -%} + {{ enum_name(sub_frame, msg_name + '_' + sub_frame.name) }} + {%- endfor -%} + {%- else %} + {{ msg_name }}, + {%- endif -%} +{%- endmacro -%} +#pragma once + +#include "static.hpp" + +namespace CANlib { + +enum class AbstractBus { + {%- for bus in can.bus %} + {{ bus.name }}, + {%- endfor %} + INVALID_NAME, +}; + +uint32_t identify(AbstractBus, const Frame&); +void handle_frame(AbstractBus, const Frame&); + +{% for bus in can.bus %} + namespace {{bus.name}} { + {%- set ns.first_frame = true %} + enum class MessageType { + UNKNOWN_MSG = 0, + {%- for msg in bus.frame -%} + {{- enum_name(msg) }} + {%- endfor %} + }; + } // {{bus.name}} +{% endfor -%} + +} // CANlib diff --git a/generator/templates/computer.c.j2 b/generator/templates/computer.c.j2 deleted file mode 100644 index 5f7fc82..0000000 --- a/generator/templates/computer.c.j2 +++ /dev/null @@ -1,134 +0,0 @@ -{%- macro key_type(num_bits) -%} uint{{ (((num_bits / 8) | round(method='ceil')) * 8) | int }}_t {%- endmacro -%} - -{%- macro handle_case(busnm, msg, msg_name=msg.name, indent=2) -%} -{%- if msg.frame is defined -%} -case CANlib_{{ busnm }}_{{ msg_name }}_key: { - uint64_t bitstring; - to_bitstring(frame->data, &bitstring); - {{ key_type(msg.slice.length) }} {{ busnm }}_{{ msg_name }}_key = EXTRACT(bitstring, {{ msg.slice.start }}, {{ msg.slice.length }}); - switch ({{ busnm }}_{{ msg_name }}_key) { - {%- for sub_frame in msg.frame %} - {{ handle_case(busnm, sub_frame, msg_name + '_' + sub_frame.name, indent + 2) | indent(indent + 2) }} - {%- endfor %} - default: - return; - } - break; -} -{%- else -%} -case CANlib_{{ busnm }}_{{ msg_name }}_key: - CANlib_Handle_{{ busnm }}_{{ msg_name }}(frame); - break; -{%- endif -%} -{%- endmacro -%} - -{%- macro identify_case(busnm, msg, msg_name=msg.name, indent=2) -%} -{%- if msg.frame is defined -%} -case CANlib_{{ busnm }}_{{ msg_name }}_key: { - uint64_t bitstring; - to_bitstring(frame->data, &bitstring); - {{ key_type(msg.slice.length) }} {{ busnm }}_{{ msg_name }}_key = EXTRACT(bitstring, {{ msg.slice.start }}, {{ msg.slice.length }}); - switch ({{ busnm }}_{{ msg_name }}_key) { - {%- for sub_frame in msg.frame %} - {{ identify_case(busnm, sub_frame, msg_name + '_' + sub_frame.name, indent + 2) | indent(indent + 2) }} - {%- endfor %} - default: - return CANlib_UNKNOWN_MSG; - break; - } - break; -} -{%- else -%} -case CANlib_{{ busnm }}_{{ msg_name }}_key: - return CANlib_{{ busnm }}_{{ msg_name }}; - break; -{%- endif -%} -{%- endmacro -%} - -#include "canlib_{{ computer.name }}.h" -#include "constants.h" -#include "bus.h" -#include "pack_unpack.h" -#include "evil_macros.h" -#include "drivers/inc/{{ system.architecture['name'][computer.architecture].family }}.h" - -#ifndef UNUSED -#define UNUSED(X) (void)X -#endif - -CAN_Raw_Bus_T CANlib_GetRawBus(CANlib_Bus_T bus) { - switch (bus) { -{%- for busnm, rawnm in computer.participation['name']['can'].mapping.items() %} - case {{ busnm }}: - return {{ rawnm }}; -{%- endfor %} - default: - return INVALID_BUS; - } -} - -CAN_Raw_Bus_T CANlib_GetConceptualBus(CAN_Raw_Bus_T bus) { - switch (bus) { -{%- for busnm, rawnm in computer.participation['name']['can'].mapping.items() %} - case {{ rawnm }}: - return {{ busnm }}; -{%- endfor %} - default: - return INVALID_BUS; - } -} - -{%- for busnm, bus in computer.participation['name']['can'].subscribe.items() %} - -CANlib_{{ busnm }}_T CANlib_Identify_{{ busnm }}(Frame* frame) { - switch (frame->id) { - {%- for frame in bus %} - {{ identify_case(busnm, frame) | indent(4) }} - {%- endfor %} - default: - return CANlib_UNKNOWN_MSG; - break; - } -} -{%- endfor %} - -{%- for busnm, bus in computer.participation['name']['can'].subscribe.items() %} - -static void CANlib_HandleFrame_{{ busnm }}(Frame *frame) { - switch (frame->id) { -{%- for frame in bus %} - {{ handle_case(busnm, frame) | indent(4) }} -{%- endfor %} - default: - return; - } -} -{%- endfor %} - -void CANlib_HandleFrame(CAN_Raw_Bus_T raw_bus, Frame* frame) { - switch (raw_bus) { -{%- for busnm in computer.participation['name']['can'].subscribe.keys() %} - case {{computer.participation['name']['can'].mapping[busnm] }}: - CANlib_HandleFrame_{{ busnm }}(frame); - break; -{%- endfor %} - default: - UNUSED(frame); - break; - } -} - -{%- for busnm in computer.participation['name']['can'].subscribe.keys() %} - -static void CANlib_update_can_{{ busnm }}(void) { - Frame frame; - CANlib_ReadFrame(&frame, {{ busnm }}); - CANlib_HandleFrame_{{ busnm }}(&frame); -} -{%- endfor %} - -void CANlib_update_can(void) { -{%- for busnm in computer.participation['name']['can'].subscribe.keys() %} - CANlib_update_can_{{ busnm }}(); -{%- endfor %} -} diff --git a/generator/templates/computer.cpp.j2 b/generator/templates/computer.cpp.j2 new file mode 100644 index 0000000..58cf452 --- /dev/null +++ b/generator/templates/computer.cpp.j2 @@ -0,0 +1,50 @@ +#include "canlib_{{ computer.name }}.hpp" +#include "bus.hpp" +#include "driver.hpp" +#include "{{ system.architecture['name'][computer.architecture].family }}.hpp" + +namespace CANlib { + +RawBus get_raw_bus(AbstractBus bus_name) { + switch (bus_name) { +{%- for busnm, rawnm in computer.participation['name']['can'].mapping.items() %} + case AbstractBus::{{ busnm }}: + return RawBus::{{ rawnm }}; +{%- endfor %} + default: + return RawBus::INVALID_BUS; + } +} + +AbstractBus get_bus_name(RawBus bus) { + switch (bus) { +{%- for busnm, rawnm in computer.participation['name']['can'].mapping.items() %} + case RawBus::{{ rawnm }}: + return AbstractBus::{{ busnm }}; +{%- endfor %} + default: + return AbstractBus::INVALID_NAME; + } +} + +{% for busnm, rawnm in computer.participation['name']['can'].mapping.items() %} +{% if testing -%} + void {{busnm}}_update_can() { +{%- else -%} + static void {{busnm}}_update_can() { +{%- endif %} + Frame frame; + read_frame(frame, AbstractBus::{{ busnm }}); + handle_frame(AbstractBus::{{busnm}}, frame); +} +{% endfor %} + +void update_can(void) { +{%- for busnm in computer.participation['name']['can'].subscribe.keys() %} + {{ busnm }}_update_can(); +{%- endfor %} +} + +} + + diff --git a/generator/templates/computer.h.j2 b/generator/templates/computer.h.j2 deleted file mode 100644 index 93197dc..0000000 --- a/generator/templates/computer.h.j2 +++ /dev/null @@ -1,55 +0,0 @@ -{%- macro send_declaration(bus_name, msg, msg_name=msg.name) -%} -{%- if msg.frame is defined -%} -{%- for sub_frame in msg.frame -%} -{{ send_declaration(bus_name, sub_frame, msg_name + '_' + sub_frame.name) }} -{%- endfor -%} -{%- else -%} -void CANlib_Send_{{ bus_name }}_{{ msg_name }}(CANlib_{{ bus_name }}_{{ msg_name }}_T *inp); -{% endif -%} -{%- endmacro -%} - -{%- macro handle_stuff(bus_name, msg, msg_name=msg.name) -%} -{%- if msg.frame is defined -%} -{%- for sub_frame in msg.frame -%} -{{ handle_stuff(bus_name, sub_frame, msg_name + '_' + sub_frame.name) }} -{%- endfor -%} -{%- else -%} -extern CANlib_{{ bus_name }}_{{ msg_name }}_T CANlib_{{ bus_name }}_{{ msg_name }}_Input; -void CANlib_Handle_{{ bus_name }}_{{ msg_name }}(Frame *frame); -{% endif -%} -{%- endmacro -%} - -#pragma once - -#include -#include - -#include "driver.h" -#include "constants.h" -#include "enum_atom.h" -#include "structs.h" -#include "static.h" -#include "evil_macros.h" -#include "bus.h" -#include "pack_unpack.h" - -#ifdef __cplusplus -extern "C" { -#endif // __cplusplus -{% for bus_name, bus in computer.participation['name']['can'].publish.items() %} -{% for frame in bus -%} -{{ send_declaration(bus_name, frame) -}} -{%- endfor -%} -{% endfor -%} - -{% for bus_name, bus in computer.participation['name']['can'].subscribe.items() %} -{% for frame in bus -%} -{{ handle_stuff(bus_name, frame) -}} -{%- endfor -%} -{% endfor %} -void CANlib_update_can(void); -void CANlib_HandleFrame(CAN_Raw_Bus_T raw_bus, Frame* frame); - -#ifdef __cplusplus -} // extern "C" -#endif // __cplusplus diff --git a/generator/templates/computer.hpp.j2 b/generator/templates/computer.hpp.j2 new file mode 100644 index 0000000..917e79b --- /dev/null +++ b/generator/templates/computer.hpp.j2 @@ -0,0 +1,18 @@ +#pragma once + +#include +#include + +#include "driver.hpp" +#include "structs.hpp" +#include "static.hpp" +#include "bus.hpp" + +namespace CANlib { + {%- if testing -%} + {%- for busnm, rawnm in computer.participation['name']['can'].mapping.items() %} + void {{busnm}}_update_can(); + {%- endfor %} + {% endif %} + void update_can(); +} diff --git a/generator/templates/constants.h.j2 b/generator/templates/constants.h.j2 deleted file mode 100644 index 7682f13..0000000 --- a/generator/templates/constants.h.j2 +++ /dev/null @@ -1,31 +0,0 @@ -{%- macro key_defines(bus, msg, msg_name=msg.name) -%} -{%- if msg.frame is defined -%} -#define CANlib_{{ bus.name }}_{{ msg_name }}_key 0x{{ '%0X' % msg.key }} -{% for sub_frame in msg.frame -%} -{{ key_defines(bus, sub_frame, msg_name + '_' + sub_frame.name) }} -{% endfor -%} -{%- else -%} -#define CANlib_{{ bus.name }}_{{ msg_name }}_key 0x{{ '%0X' % msg.key }} -{%- endif %} -{%- endmacro -%} -#pragma once - -// CAN IDs -{%- for bus in can.bus %} - -// {{ bus.name }} -{%- for msg in bus.frame %} -{{ key_defines(bus, msg) }} -{%- endfor %} -{%- endfor %} - -// Periods (ms) -{%- for bus in can.bus %} - -// {{ bus.name }} -{%- for msg in bus.frame %} -{%- if msg.period %} -#define CANlib_{{ bus.name }}_{{ msg.name }}_period {{ get_ms(msg.period) }} -{%- endif %} -{%- endfor %} -{%- endfor %} diff --git a/generator/templates/drivers/inc/stm32f2xx.h.j2 b/generator/templates/drivers/inc/stm32f2xx.h.j2 deleted file mode 100644 index a90b72f..0000000 --- a/generator/templates/drivers/inc/stm32f2xx.h.j2 +++ /dev/null @@ -1,11 +0,0 @@ -#pragma once - -#include "static.h" - -#include "stm32f2xx_hal_can.h" -#include - -typedef HAL_StatusTypeDef CANlib_Transmit_Error_T; - -HAL_StatusTypeDef CANlib_Init(uint32_t baudrate); -HAL_StatusTypeDef CANlib_TransmitFrame(Frame *frame); diff --git a/generator/templates/drivers/inc/stm32f4xx.h.j2 b/generator/templates/drivers/inc/stm32f4xx.h.j2 deleted file mode 100644 index af0bed1..0000000 --- a/generator/templates/drivers/inc/stm32f4xx.h.j2 +++ /dev/null @@ -1,33 +0,0 @@ -#pragma once - -#include "static.h" -#include "stm32f4xx_hal.h" -#include "bus.h" - -#include - -#ifdef __cplusplus -extern "C" { -#endif // __cplusplus - -typedef enum { -{%- for bus in architecture.participation['name']['can'].buses -%} -{%- if bus == 'INVALID_BUS' -%} - {%- raise "INVALID_BUS is a reserved bus name" -%} -{%- endif %} - {{ bus }}, -{%- endfor %} - INVALID_BUS -} CAN_Raw_Bus_T; - -typedef uint32_t Time_T; // in ms -typedef HAL_StatusTypeDef CANlib_Transmit_Error_T; -typedef HAL_StatusTypeDef CANlib_Init_Error_T; - -CANlib_Transmit_Error_T CANlib_TransmitFrame(Frame *frame, CANlib_Bus_T bus); -void CANlib_ReadFrame(Frame *frame, CANlib_Bus_T bus); -Time_T CANlib_GetTick(void); - -#ifdef __cplusplus -} // extern "C" -#endif // __cplusplus diff --git a/generator/templates/drivers/inc/stm32f4xx.hpp.j2 b/generator/templates/drivers/inc/stm32f4xx.hpp.j2 new file mode 100644 index 0000000..8ab940d --- /dev/null +++ b/generator/templates/drivers/inc/stm32f4xx.hpp.j2 @@ -0,0 +1,28 @@ +#pragma once + +#include "static.hpp" +#include "stm32f4xx_hal.h" +#include "bus.hpp" +#include "clock.hpp" + +#include + +namespace CANlib { + +enum class RawBus { +{%- for bus in architecture.participation['name']['can'].buses -%} +{%- if bus == 'INVALID_BUS' -%} + {%- raise "INVALID_BUS is a reserved bus name" -%} +{%- endif %} + {{ bus }}, +{%- endfor %} + INVALID_BUS, +}; + +typedef HAL_StatusTypeDef TransmitError; +typedef HAL_StatusTypeDef InitError; + +TransmitError transmit_frame(const Frame& frame, AbstractBus bus_name); +void read_frame(Frame& frame, AbstractBus bus_name); + +} // CANlib diff --git a/generator/templates/drivers/inc/testfamily.hpp.j2 b/generator/templates/drivers/inc/testfamily.hpp.j2 new file mode 100644 index 0000000..0339e6c --- /dev/null +++ b/generator/templates/drivers/inc/testfamily.hpp.j2 @@ -0,0 +1,48 @@ +#pragma once + +#include "static.hpp" +#include "bus.hpp" + +#include +#include +#include +#include +#include +#include "clock.hpp" + + +namespace CANlib { + + +class TestCAN { +public: + void setFrameToSend(const Frame& f) {frame_to_send = f;} + void receiveFrame(const Frame& f) {frames_received.push_back(f);} + size_t framesReceived() {return frames_received.size();} + void popFrame() {frames_received.pop_back();} + Frame topFrame() {return frames_received.back();} + Frame frameToSend() {return frame_to_send;} + void clear() {frames_received.clear(); memset(&frame_to_send, 0, sizeof(frame_to_send));} +protected: + std::vector frames_received; + Frame frame_to_send; +}; + +enum class RawBus { +{%- for bus in architecture.participation['name']['can'].buses -%} +{%- if bus == 'INVALID_BUS' -%} + {%- raise "INVALID_BUS is a reserved bus name" -%} +{%- endif %} + {{ bus }}, +{%- endfor %} + INVALID_BUS, +}; + +typedef bool TransmitError; + +TransmitError transmit_frame(const Frame& frame, AbstractBus bus_name); +void read_frame(Frame& frame, AbstractBus bus_name); +RawBus get_raw_bus(AbstractBus); +AbstractBus get_bus_name(RawBus); + +} // CANlib diff --git a/generator/templates/enum_atom.h.j2 b/generator/templates/enum_atom.h.j2 deleted file mode 100644 index 8fb0a16..0000000 --- a/generator/templates/enum_atom.h.j2 +++ /dev/null @@ -1,32 +0,0 @@ -{%- macro enums(bus, msg, msg_name=msg.name) -%} -{%- if msg.frame is defined -%} -{% for sub_frame in msg.frame -%} -{{ enums(bus, sub_frame, msg_name + '_' + sub_frame.name) }} -{%- endfor -%} -{%- else -%} -{%- for atom in msg.atom -%} -{%- if atom.type.isenum() -%} -typedef enum { -{%- for enum in atom.type.enum -%} -{%- if enum.name == 'NUM_FIELDS' -%} - {%- raise "Enum name for {{ atom.name }} cannot be NUM_FIELDS" -%} -{%- else %} - CANlib_{{ bus.name }}_{{ msg_name }}_{{ atom.name }}_{{ enum.name }} = {{ enum.value }}, -{%- endif -%} -{%- endfor %} - CANlib_{{ bus.name }}_{{ msg_name }}_{{ atom.name }}_NUM_FIELDS = {{ atom.type.enum | length }} -} CANlib_{{ bus.name }}_{{ msg_name }}_{{ atom.name }}_T; - -{% endif -%} -{%- endfor -%} -{%- endif -%} -{%- endmacro -%} - -#pragma once - -{% for bus in can.bus -%} -// {{ bus.name }} -{% for msg in bus.frame -%} - {{ enums(bus, msg) }} -{%- endfor -%} -{%- endfor -%} diff --git a/generator/templates/pack_unpack.c.j2 b/generator/templates/pack_unpack.c.j2 deleted file mode 100644 index bf60f12..0000000 --- a/generator/templates/pack_unpack.c.j2 +++ /dev/null @@ -1,145 +0,0 @@ -{%- macro can_pack_handler(frame, name_prepends, bus_ext, parent_slices = []) -%} -{%- if frame.frame is defined -%} -{% for sub_frame in frame.frame -%} -{{ can_pack_handler(sub_frame, name_prepends + [frame.name], bus_ext, parent_slices + [frame.slice]) }} - -{% endfor -%} -{%- else -%} -{%- set name_prepends = name_prepends + [frame.name] -%} -{%- set tot_name = '_'.join(name_prepends) -%} -void CANlib_Pack_{{ tot_name }}(CANlib_{{ tot_name }}_T *type_in, Frame *can_out) { - uint64_t bitstring = 0; -{%- for i in range(parent_slices | length) -%} -{%- set part_name = '_'.join(name_prepends[:i + 3]) %} - bitstring = INSERT(CANlib_{{ part_name }}_key, bitstring, {{ parent_slices[i].start }}, {{ parent_slices[i].length }}); -{% endfor -%} -{%- set ns = namespace(length=0) -%} -{%- for atom in frame.atom %} - bitstring = INSERT({%- if atom.type.endianness.islittle() and not atom.type.isbool() -%}swap_{{ atom.type.type }}{%- endif -%}(type_in->{{ atom.name }}), bitstring, {{ atom.slice.start }}, {{ atom.slice.length }}); -{%- set ns.length = [ns.length, atom.slice.start + atom.slice.length] | max -%} -{%- endfor %} - from_bitstring(&bitstring, can_out->data); - can_out->id = CANlib_{{ '_'.join(name_prepends[:2]) }}_key; - can_out->dlc = {{ (ns.length / 8) | round(method='ceil') | int }}; - can_out->extended = {{ bus_ext | lower }}; -} -{%- endif %} -{%- endmacro -%} - -{%- macro can_unpack_handler(frame, name_prepends) -%} -{%- if frame.frame is defined -%} -{% for sub_frame in frame.frame -%} -{{ can_unpack_handler(sub_frame, name_prepends + '_' + frame.name) }} - -{% endfor -%} -{%- else -%} -{%- set tot_name = name_prepends + '_' + frame.name -%} -void CANlib_Unpack_{{ tot_name }}(Frame *can_in, CANlib_{{ tot_name }}_T *type_out) { - uint64_t bitstring = 0; - to_bitstring(can_in->data, &bitstring); -{%- for atom in frame.atom %} - type_out->{{ atom.name }} = {% if 0 %}{% endif %} - {%- if atom.type.isenum() -%} - (CANlib_{{ tot_name }}_{{ atom.name }}_T) - {%- endif -%} - {%- if atom.type.issigned() -%} - SIGN( - {%- endif -%} - {%- if atom.type.endianness.islittle() and not atom.type.isbool() -%} - swap_{{ atom.type.type }}( - {%- endif -%} - EXTRACT(bitstring, {{ atom.slice.start }}, {{ atom.slice.length }}) - {%- if atom.type.endianness.islittle() and not atom.type.isbool() -%} - ) - {%- endif -%} - {%- if atom.type.issigned() -%} - , {{ atom.slice.length }}) - {%- endif -%} - ; -{%- endfor %} -} -{%- endif %} -{%- endmacro -%} -#include "pack_unpack.h" -#include "evil_macros.h" - -typedef union { - uint8_t byte[8]; - uint64_t bitstring; -} DATA_T; - -// utility functions -void data_transfer(DATA_T *in, DATA_T *out) { - uint8_t i; - for (i = 0; i < 8; i++) { - (*out).byte[7 - i] = (*in).byte[i]; - } -} - -void to_bitstring(uint8_t in[], uint64_t *out) { - data_transfer((DATA_T*)in, (DATA_T*)out); -} - -void from_bitstring(uint64_t *in, uint8_t out[]) { - data_transfer((DATA_T*)in, (DATA_T*)out); -} - -// Shameless copypasta-ing from Stack Overflow for trivial endian swap. -// https://stackoverflow.com/a/2637138 -uint8_t swap_uint8(uint8_t val) { - return val; -} - -int8_t swap_int8(int8_t val) { - return val; -} - -uint16_t swap_uint16(uint16_t val) { - return (val << 8) | (val >> 8); -} - -int16_t swap_int16(int16_t val) { - return (val << 8) | ((val >> 8) & 0xFF); -} - -uint32_t swap_uint32(uint32_t val) { - val = ((val << 8) & 0xFF00FF00) | ((val >> 8) & 0xFF00FF); - return (val << 16) | (val >> 16); -} - -int32_t swap_int32(int32_t val) { - val = ((val << 8) & 0xFF00FF00) | ((val >> 8) & 0xFF00FF); - return (val << 16) | ((val >> 16) & 0xFFFF); -} - -int64_t swap_int64(int64_t val) { - val = ((val << 8) & 0xFF00FF00FF00FF00ULL) | - ((val >> 8) & 0x00FF00FF00FF00FFULL); - val = ((val << 16) & 0xFFFF0000FFFF0000ULL) | - ((val >> 16) & 0x0000FFFF0000FFFFULL); - return (val << 32) | ((val >> 32) & 0xFFFFFFFFULL); -} - -uint64_t swap_uint64(uint64_t val) { - val = ((val << 8) & 0xFF00FF00FF00FF00ULL) | - ((val >> 8) & 0x00FF00FF00FF00FFULL); - val = ((val << 16) & 0xFFFF0000FFFF0000ULL) | - ((val >> 16) & 0x0000FFFF0000FFFFULL); - return (val << 32) | (val >> 32); -} - -// Pack functions -{% for bus in can.bus %} -{%- for msg in bus.frame -%} -{{ can_pack_handler(msg, [bus.name], bus.extended) }} - -{% endfor %} -{% endfor %} - -// Unpack functions -{% for bus in can.bus %} -{%- for msg in bus.frame -%} -{{ can_unpack_handler(msg, bus.name) }} - -{% endfor %} -{% endfor %} diff --git a/generator/templates/pack_unpack.cpp.j2 b/generator/templates/pack_unpack.cpp.j2 new file mode 100644 index 0000000..e02245e --- /dev/null +++ b/generator/templates/pack_unpack.cpp.j2 @@ -0,0 +1,136 @@ +{%- macro can_pack_handler(frame, name_prepends, bus_ext, parent_slices = []) -%} +{%- if frame.frame is defined -%} + {% for sub_frame in frame.frame %} + {{ can_pack_handler(sub_frame, name_prepends + [frame.name], bus_ext, parent_slices + [frame.slice]) }} + {% endfor -%} + +{% else -%} + {%- set name_prepends = name_prepends + [frame.name] -%} + {%- set tot_name = '::'.join(name_prepends) -%} + void {{tot_name}}::unpack(Frame& can_out) { + uint64_t bitstring = 0; + {%- for i in range(parent_slices | length) -%} + {%- set part_name = '::'.join(name_prepends[:i + 3]) %} + bitstring = INSERT(CANlib::{{ part_name }}::key, bitstring, {{ parent_slices[i].start }}, {{ parent_slices[i].length }}); + {% endfor -%} + {%- set ns = namespace(length=0) -%} + {%- for atom in frame.atom %} + bitstring = INSERT({%- if atom.type.endianness.islittle() and not atom.type.isbool() -%}swap_{{ atom.type.type }}(static_cast<{{atom.type.type}}_t>(this->{{ atom.name }})) {% else %} (static_cast(this->{{ atom.name }})) {%- endif -%}, bitstring, {{ atom.slice.start }}, {{ atom.slice.length }}); + {%- set ns.length = [ns.length, atom.slice.start + atom.slice.length] | max -%} + {%- endfor %} + from_bitstring(&bitstring, can_out.data); + can_out.id = CANlib::{{ "::".join(name_prepends[:2]) }}::key; + can_out.dlc = {{ (ns.length / 8) | round(method='ceil') | int }}; + can_out.extended = {{ bus_ext | lower }}; + } +{% endif %} +{%- endmacro -%} + +{%- macro can_unpack_handler(frame, name_prepends) -%} +{%- if frame.frame is defined -%} + {% for sub_frame in frame.frame -%} + {{ can_unpack_handler(sub_frame, name_prepends + [sub_frame.name]) }} + {% endfor -%} + +{% else -%} + {%- set tot_name = "::".join(name_prepends) -%} + void {{ tot_name }}::pack(const Frame& can_in) { + uint64_t bitstring = 0; + to_bitstring((uint8_t*)can_in.data, &bitstring); + {%- for atom in frame.atom %} + this->{{ atom.name }} = {% if 0 %}{% endif %} + {%- if atom.type.isenum() -%} + ({{ atom.name }}_T) + {%- endif -%} + {%- if atom.type.issigned() -%} + SIGN( + {%- endif -%} + {%- if atom.type.endianness.islittle() and not atom.type.isbool() -%} + swap_{{ atom.type.type }}( + {%- endif -%} + EXTRACT(bitstring, {{ atom.slice.start }}, {{ atom.slice.length }}) + {%- if atom.type.endianness.islittle() and not atom.type.isbool() -%} + ) + {%- endif -%} + {%- if atom.type.issigned() -%} + , {{ atom.slice.length }}) + {%- endif -%} + ; + {%- endfor %} + } +{% endif %} +{%- endmacro -%} +#include "structs.hpp" +#include "pack_unpack.hpp" + +using namespace CANlib; + +// utility functions +void data_transfer(DATA_T *in, DATA_T *out) { + uint8_t i; + for (i = 0; i < 8; i++) { + (*out).byte[7 - i] = (*in).byte[i]; + } +} + +void to_bitstring(uint8_t in[], uint64_t *out) { + data_transfer((DATA_T*)in, (DATA_T*)out); +} + +void from_bitstring(uint64_t *in, uint8_t out[]) { + data_transfer((DATA_T*)in, (DATA_T*)out); +} + +// Shameless copypasta-ing from Stack Overflow for trivial endian swap. +// https://stackoverflow.com/a/2637138 +uint8_t swap_uint8(uint8_t val) { + return val; +} + +int8_t swap_int8(int8_t val) { + return val; +} + +uint16_t swap_uint16(uint16_t val) { + return (val << 8) | (val >> 8); +} + +int16_t swap_int16(int16_t val) { + return (val << 8) | ((val >> 8) & 0xFF); +} + +uint32_t swap_uint32(uint32_t val) { + val = ((val << 8) & 0xFF00FF00) | ((val >> 8) & 0xFF00FF); + return (val << 16) | (val >> 16); +} + +int32_t swap_int32(int32_t val) { + val = ((val << 8) & 0xFF00FF00) | ((val >> 8) & 0xFF00FF); + return (val << 16) | ((val >> 16) & 0xFFFF); +} + +int64_t swap_int64(int64_t val) { + val = ((val << 8) & 0xFF00FF00FF00FF00ULL) | + ((val >> 8) & 0x00FF00FF00FF00FFULL); + val = ((val << 16) & 0xFFFF0000FFFF0000ULL) | + ((val >> 16) & 0x0000FFFF0000FFFFULL); + return (val << 32) | ((val >> 32) & 0xFFFFFFFFULL); +} + +uint64_t swap_uint64(uint64_t val) { + val = ((val << 8) & 0xFF00FF00FF00FF00ULL) | + ((val >> 8) & 0x00FF00FF00FF00FFULL); + val = ((val << 16) & 0xFFFF0000FFFF0000ULL) | + ((val >> 16) & 0x0000FFFF0000FFFFULL); + return (val << 32) | (val >> 32); +} + +// Pack+Unpack functions +{% for bus in can.bus %} +{%- for msg in bus.frame -%} +{{ can_pack_handler(msg, [bus.name], bus.extended) }} + +{{ can_unpack_handler(msg, [bus.name, msg.name]) }} + +{% endfor %} +{% endfor %} diff --git a/generator/templates/pack_unpack.h.j2 b/generator/templates/pack_unpack.h.j2 deleted file mode 100644 index 90b043d..0000000 --- a/generator/templates/pack_unpack.h.j2 +++ /dev/null @@ -1,27 +0,0 @@ -{%- macro function_declarations(bus, msg, msg_name=msg.name) -%} -{%- if msg.frame is defined -%} -{% for sub_frame in msg.frame -%} -{{ function_declarations(bus, sub_frame, msg_name + '_' + sub_frame.name) }} -{% endfor -%} -{%- else -%} -void CANlib_Pack_{{ bus.name }}_{{ msg_name }}(CANlib_{{ bus.name }}_{{ msg_name }}_T *type_in, Frame *can_out); -void CANlib_Unpack_{{ bus.name }}_{{ msg_name }}(Frame *can_in, CANlib_{{ bus.name }}_{{ msg_name }}_T *type_out); -{% endif %} -{%- endmacro -%} -#pragma once - -#include -#include - -#include "static.h" -#include "constants.h" -#include "structs.h" - -{%- for bus in can.bus %} - -// {{ bus.name }} -{%- for msg in bus.frame %} -{{ function_declarations(bus, msg) }} -{%- endfor %} -{%- endfor %} -void to_bitstring(uint8_t in[], uint64_t *out); diff --git a/src/evil_macros.h b/generator/templates/pack_unpack.hpp.j2 similarity index 68% rename from src/evil_macros.h rename to generator/templates/pack_unpack.hpp.j2 index 54a7746..0619d37 100644 --- a/src/evil_macros.h +++ b/generator/templates/pack_unpack.hpp.j2 @@ -1,8 +1,5 @@ -#ifndef _CAN_LIBRARY_EVIL_MACROS_H -#define _CAN_LIBRARY_EVIL_MACROS_H - -#include -#include +#pragma once +#include #define TOGGLE(input, test, idx) \ if (test) { \ @@ -38,4 +35,20 @@ #define BIT_GET(input, bit_idx) (bool)((input) & (1UL << (bit_idx))) -#endif // _CAN_LIBRARY_EVIL_MACROS_H +struct DATA_T { + uint8_t byte[8]; + uint64_t bitstring; +}; + +// utility functions +void data_transfer(DATA_T *in, DATA_T *out); +void to_bitstring(uint8_t in[], uint64_t *out); +void from_bitstring(uint64_t *in, uint8_t out[]); +uint8_t swap_uint8(uint8_t val); +int8_t swap_int8(int8_t val); +uint16_t swap_uint16(uint16_t val); +int16_t swap_int16(int16_t val); +uint32_t swap_uint32(uint32_t val); +int32_t swap_int32(int32_t val); +int64_t swap_int64(int64_t val); +uint64_t swap_uint64(uint64_t val); diff --git a/generator/templates/send_receive.c.j2 b/generator/templates/send_receive.c.j2 deleted file mode 100644 index 7754b0b..0000000 --- a/generator/templates/send_receive.c.j2 +++ /dev/null @@ -1,55 +0,0 @@ -{%- macro struct_declarations(bus, msg, msg_name=msg.name) -%} -{%- if msg.frame is defined -%} -{%- for sub_frame in msg.frame -%} -{{ struct_declarations(bus, sub_frame, msg_name + '_' + sub_frame.name) }} -{%- endfor -%} -{%- else -%} -CANlib_{{ bus.name }}_{{ msg_name }}_T CANlib_{{ bus.name }}_{{ msg_name }}_Input; -{% endif -%} -{%- endmacro -%} - -{%- macro function_definitions(bus, msg, msg_name=msg.name) -%} -{%- if msg.frame is defined -%} -{% for sub_frame in msg.frame -%} -{{ function_definitions(bus, sub_frame, msg_name + '_' + sub_frame.name) }} -{%- endfor -%} -{%- else %} -void CANlib_Send_{{ bus.name }}_{{ msg_name }}(CANlib_{{ bus.name }}_{{ msg_name }}_T *inp) { -{%- if msg.period is not none %} - LIMIT(CANlib_{{ bus.name }}_{{ msg_name }}); -{%- endif %} - Frame frame; - CANlib_Pack_{{ bus.name }}_{{ msg_name }}(inp, &frame); - CANlib_TransmitFrame(&frame, {{ bus.name }}); -} - -void CANlib_Handle_{{ bus.name }}_{{ msg_name }}(Frame *frame) { - CANlib_Unpack_{{ bus.name }}_{{ msg_name }}(frame, &CANlib_{{ bus.name }}_{{ msg_name }}_Input); -} -{% endif -%} -{%- endmacro -%} - -#include "pack_unpack.h" -#include "bus.h" -#include "driver.h" - -#define LIMIT(name) \ - static Time_T last_sent = 0; \ - if (CANlib_GetTick() - last_sent < name##_period) return; \ - last_sent = CANlib_GetTick(); - -// Input Structs -{% for bus in can.bus %} -// {{ bus.name }} -{% for msg in bus.frame -%} -{{ struct_declarations(bus, msg) }} -{%- endfor %} -{%- endfor %} - -// Functions -{% for bus in can.bus %} -// {{ bus.name }} -{% for msg in bus.frame -%} -{{- function_definitions(bus, msg) -}} -{%- endfor -%} -{%- endfor -%} diff --git a/generator/templates/structs.cpp.j2 b/generator/templates/structs.cpp.j2 new file mode 100644 index 0000000..976d256 --- /dev/null +++ b/generator/templates/structs.cpp.j2 @@ -0,0 +1,178 @@ +{%- macro struct_declarations(bus, msg, msg_names = [msg.name]) -%} +{%- if msg.frame is defined %} + + namespace {{msg.name}} { + {% for sub_frame in msg.frame -%} + {{ struct_declarations(bus, sub_frame, msg_names + [sub_frame.name]) }} + {%- endfor %} + + } +{% else %} + {{msg.name}} {{msg.name}}_input; + + {{ msg.name }}* CANlib::{{bus.name}}::{{'::'.join(msg_names)}}::get_input() { + return &{{msg.name}}_input; + } + +{% endif -%} +{%- endmacro -%} + +{%- macro get_message_type(bus, msg, msg_names) -%} +{%- if msg.frame is defined -%} + MessageType::UNKNOWN_MSG, + {%- for sub_frame in msg.frame -%} + {{ get_message_type(bus, sub_frame, msg_names + [sub_frame.name]) }} + {%- endfor -%} +{%- else -%} + MessageType::{{'_'.join(msg_names)}}, +{%- endif -%} +{%- endmacro -%} + +{%- macro get_messages(msg, msg_names) -%} +{%- if msg.frame is defined -%} + nullptr, + {%- for sub_frame in msg.frame -%} + {{ get_messages(sub_frame, msg_names + [sub_frame.name]) }} + {%- endfor -%} +{%- else -%} + &{{'::'.join(msg_names)}}_input, +{%- endif -%} +{%- endmacro -%} + +{%- macro get_keys(msg, msg_names) -%} +{%- if msg.frame is defined -%} + {{ "::".join(msg_names) }}::key, + {%- for sub_frame in msg.frame -%} + {{ get_keys(sub_frame, msg_names + [sub_frame.name]) }} + {%- endfor -%} +{%- else -%} + {{"::".join(msg_names)}}::key, +{%- endif -%} +{%- endmacro -%} + +{%- macro get_message_ranges(msg, msg_names) -%} +{%- if msg.frame is defined -%} + std::make_pair({{ msg.slice.start }}, {{ msg.slice.length }}), + {%- for sub_frame in msg.frame -%} + {{ get_message_ranges(sub_frame, msg_names + [sub_frame.name]) }} + {%- endfor -%} +{%- else -%} + std::make_pair(1, 0), +{%- endif -%} +{%- endmacro -%} + +{%- macro get_frame_ranges(msg, msg_names, cnt = 0) -%} +{%- if msg.frame is defined -%} + {{ get_num_msg_types(msg) }}, + {%- for sub_frame in msg.frame -%} + {{ get_frame_ranges(sub_frame, msg_names + [sub_frame.name], cnt + 1) }} + {%- endfor -%} +{%- else -%} + 0, +{%- endif -%} +{%- endmacro -%} + +#include +#include +#include +#include "static.hpp" +#include "bus.hpp" +#include "structs.hpp" + +namespace CANlib { + +void send_period(Clock::time_point &last_sent, Message* msg) { + if (Clock::now() - last_sent > msg->get_period()) { + last_sent = Clock::now(); + send(msg); + } +} + +void send(Message* msg) { + Frame frame; + msg->unpack(frame); + transmit_frame(frame, msg->get_bus_name()); +} + +{% for bus in can.bus %} +namespace {{bus.name}} { +{% for msg in bus.frame -%} +{{ struct_declarations(bus, msg) }} +{%- endfor %} + +extern const MessageType message_types[{{get_num_msg_types(bus)}}] = { + MessageType::UNKNOWN_MSG, + {% for msg in bus.frame -%} + {{ get_message_type(bus, msg, [msg.name]) }} + {%- endfor -%} +}; + +} + +static const uint32_t {{bus.name}}_keys[] = { + 0, + {% for msg in bus.frame -%} + {{ get_keys(msg, [bus.name, msg.name]) }} + {%- endfor -%} +}; + +/** + * In case the some index represents a frame, the first element represents the lower bound + * of the frame inside the array and the second element represents the upper bound of the + * frame inside the array. + **/ +static const std::pair {{bus.name}}_frame_id_range[] = { + std::make_pair(1,0), + {% for msg in bus.frame -%} + {{ get_message_ranges(msg, [bus.name, msg.name]) }} + {%- endfor -%} +}; + +// In case some index represents a frame, it will contain number of messages inside the frame +static const uint32_t {{bus.name}}_frame_len[] = { + 0, + {% for msg in bus.frame -%} + {{ get_frame_ranges(msg, [bus.name, msg.name]) }} + {%- endfor -%} +}; + +static Message* {{bus.name}}_messages[] = { + nullptr, + {%- for msg in bus.frame -%} + {{ get_messages(msg, ['CANlib', bus.name, msg.name]) }} + {%- endfor -%} +}; +{% endfor %} + +const uint32_t* keys[{{ get_len(can) }}] = { +{%- for bus in can.bus -%} +{{bus.name}}_keys, +{%- endfor -%} +}; + +const std::pair* frame_id_range[{{ get_len(can) }}] = { +{%- for bus in can.bus -%} +{{bus.name}}_frame_id_range, +{%- endfor -%} +}; + +const uint32_t* frame_len[{{ get_len(can) }}] = { +{%- for bus in can.bus -%} +{{bus.name}}_frame_len, +{%- endfor -%} +}; + +extern const size_t can_size[{{ get_len(can) }}] = { +{%- for bus in can.bus -%} +{{ get_num_msg_types(bus) }}, +{%- endfor -%} +}; + +Message** messages[{{ get_len(can) }}] = { +{%- for bus in can.bus -%} +{{bus.name}}_messages, +{%- endfor -%} + +}; + +} diff --git a/generator/templates/structs.h.j2 b/generator/templates/structs.h.j2 deleted file mode 100644 index 6f78075..0000000 --- a/generator/templates/structs.h.j2 +++ /dev/null @@ -1,31 +0,0 @@ -{%- macro struct_declarations(bus, msg, msg_name=msg.name) -%} -{%- if msg.frame is defined -%} -{%- for sub_frame in msg.frame -%} -{{ struct_declarations(bus, sub_frame, msg_name + '_' + sub_frame.name) }} -{%- endfor -%} -{%- else %} -typedef struct { -{%- for atom in msg.atom %} -{%- if atom.type.isenum() %} - CANlib_{{ bus.name }}_{{ msg_name }}_{{ atom.name }}_T {{ atom.name }}; -{%- else %} - {{ atom.type.ctype() }} {{ atom.name }}; -{%- endif -%} -{% endfor %} -} CANlib_{{ bus.name }}_{{ msg_name }}_T; -{% endif %} -{%- endmacro -%} - -#pragma once - -#include -#include - -#include "enum_atom.h" - -{% for bus in can.bus -%} -// {{ bus.name }} -{% for msg in bus.frame -%} -{{ struct_declarations(bus, msg) }} -{%- endfor -%} -{%- endfor -%} diff --git a/generator/templates/structs.hpp.j2 b/generator/templates/structs.hpp.j2 new file mode 100644 index 0000000..f3e4597 --- /dev/null +++ b/generator/templates/structs.hpp.j2 @@ -0,0 +1,135 @@ +{%- macro struct_declarations(bus, msg, msg_names = [msg.name]) -%} +{%- if msg.frame is defined %} + namespace {{msg.name}} { + + const uint32_t key = 0x{{ '%0X' % msg.key }}; + {% for sub_frame in msg.frame %} + {{ struct_declarations(bus, sub_frame, msg_names + [sub_frame.name]) }} + {% endfor %} + } // {{msg_name}} + +{% else %} + struct {{msg.name}} : public Message_{{ bus.name }} { + static {{msg.name}} * get_input(); + MessageType get_message_type() override { + return MessageType::{{ '_'.join(msg_names) }}; + } + void unpack(Frame&) override; + void pack(const Frame&) override; + uint32_t get_key() override { + return key; + } + void send() override { + CANlib::send_period(this->last_sent_, this); + } + AbstractBus get_bus_name() override { + return AbstractBus::{{ bus.name }}; + } + Clock::duration get_period() override { + {% if msg.period %} + return std::chrono::milliseconds({{ get_ms(msg.period) }}); + {% else %} + return std::chrono::milliseconds(0); + {% endif %} + } + {% for atom in msg.atom %} + {%- if atom.type.isenum() -%} + enum class {{ atom.name }}_T { + {%- for enum in atom.type.enum -%} + {%- if enum.name == 'NUM_FIELDS' -%} + {%- raise "Enum name for {{ atom.name }} cannot be NUM_FIELDS" -%} + {%- else %} + {{ enum.name }} = {{ enum.value }}, + {%- endif -%} + {%- endfor %} + NUM_FIELDS = {{ atom.type.enum | length }} + }; + {% endif -%} + {% endfor %} + {%- for atom in msg.atom %} + {%- if atom.type.isenum() %} + {{ atom.name }}_T {{ atom.name }}; + {%- else %} + {{ atom.type.ctype() }} {{ atom.name }}; + {%- endif -%} + {% endfor %} + static Clock::time_point last_sent_; + static const uint32_t key = 0x{{ '%0X' % msg.key }}; + }; +{% endif %} +{%- endmacro -%} + +{%- macro get_keys(msg, msg_names) -%} +{%- if msg.frame is defined -%} + {{ "::".join(msg_names) }}::key, + {%- for sub_frame in msg.frame -%} + {{ get_keys(sub_frame, msg_names + [sub_frame.name]) }} + {%- endfor -%} +{%- else -%} + {{"::".join(msg_names)}}::key, +{%- endif -%} +{%- endmacro -%} + +{%- macro get_message_ranges(msg, msg_names) -%} +{%- if msg.frame is defined -%} + std::make_pair({{ msg.slice.start }}, {{ msg.slice.length }}), + {%- for sub_frame in msg.frame -%} + {{ get_message_ranges(sub_frame, msg_names + [sub_frame.name]) }} + {%- endfor -%} +{%- else -%} + std::make_pair(1, 0), +{%- endif -%} +{%- endmacro -%} + +{%- macro get_frame_ranges(msg, msg_names, cnt = 0) -%} +{%- if msg.frame is defined -%} + {{ get_msg_len(msg) }}, + {%- for sub_frame in msg.frame -%} + {{ get_frame_ranges(sub_frame, msg_names + [sub_frame.name], cnt + 1) }} + {%- endfor -%} +{%- else -%} + 0, +{%- endif -%} +{%- endmacro -%} + +#pragma once + +#include +#include +#include +#include "driver.hpp" +#include "static.hpp" +#include "clock.hpp" +#include "bus.hpp" + +namespace CANlib { + +struct Message { + virtual void unpack(Frame&) = 0; + virtual void pack(const Frame&) = 0; + virtual uint32_t get_key() = 0; + virtual void send() = 0; + virtual AbstractBus get_bus_name() = 0; + virtual Clock::duration get_period() = 0; +}; + +void send_period(Clock::time_point &last_sent_, Message* msg); +void send(Message* msg); + +{%- for bus in can.bus %} + + namespace {{ bus.name }} { + struct Message_{{bus.name}} : public Message { + virtual MessageType get_message_type() = 0; + }; + + {% for msg in bus.frame -%} + {{ struct_declarations(bus, msg) }} + {%- endfor %} + + } // {{ bus.name }} + +{% endfor %} + + +} // CANlib diff --git a/src/driver.h b/src/driver.h deleted file mode 100644 index 82ba4f1..0000000 --- a/src/driver.h +++ /dev/null @@ -1,21 +0,0 @@ -#pragma once - -// INCLUDE THIS AFTER YOUR DRIVER - -#ifndef CANLIB_ARCH -#error "No architecture specified!" -#endif - -#if CANLIB_ARCH == STM32F4xx -#include "drivers/inc/stm32f4xx.h" -#elif CANLIB_ARCH == STM32F2xx -#include "drivers/inc/stm32f2xx.h" -#else -#error "Architecture not supported!" -#endif - -#include "bus.h" - -CANlib_Transmit_Error_T CANlib_TransmitFrame(Frame *frame, CANlib_Bus_T bus); -void CANlib_ReadFrame(Frame *frame, CANlib_Bus_T bus); -CAN_Raw_Bus_T CANlib_GetRawBus(CANlib_Bus_T bus); diff --git a/src/drivers/src/stm32f2xx.c b/src/drivers/src/stm32f2xx.c deleted file mode 100644 index 79548b7..0000000 --- a/src/drivers/src/stm32f2xx.c +++ /dev/null @@ -1,99 +0,0 @@ -#include "static.h" - -#include "stm32f2xx_hal_can.h" -#include - -CAN_HandleTypeDef CanHandle; - -HAL_StatusTypeDef CANlib_Init(uint32_t baudrate) { - // TODO calculate baudrate and set stuff accordingly - // Right now the baudrate is hard-coded to 500kbaud - // Can change prescaler to change this - // 2 Mbaud / prescaler = baudrate (prescaler goes from 1 to 1024) - - CAN_FilterConfTypeDef sFilterConfig; - static CanTxMsgTypeDef TxMessage; - static CanRxMsgTypeDef RxMessage; - - CanHandle.Instance = CAN1; - CanHandle.pTxMsg = &TxMessage; - CanHandle.pRxMsg = &RxMessage; - - CanHandle.Init.TTCM = DISABLE; - CanHandle.Init.ABOM = DISABLE; - CanHandle.Init.AWUM = DISABLE; - CanHandle.Init.NART = DISABLE; - CanHandle.Init.RFLM = DISABLE; - CanHandle.Init.TXFP = DISABLE; - CanHandle.Init.Mode = CAN_MODE_NORMAL; - CanHandle.Init.SJW = CAN_SJW_1TQ; - CanHandle.Init.BS1 = CAN_BS1_6TQ; - CanHandle.Init.BS2 = CAN_BS2_8TQ; - CanHandle.Init.Prescaler = 4; - - - HAL_StatusTypeDef status; - - if ((status = HAL_CAN_Init(&CanHandle)) != HAL_OK) { - /* Initialization Error */ - - printf("[CAN INIT] ERROR\r\n"); - return status; - } - - - // TODO: Might want to comment this out - sFilterConfig.FilterNumber = 0; - sFilterConfig.FilterMode = CAN_FILTERMODE_IDMASK; - sFilterConfig.FilterScale = CAN_FILTERSCALE_32BIT; - sFilterConfig.FilterIdHigh = 0x0000; - sFilterConfig.FilterIdLow = 0x0000; - sFilterConfig.FilterMaskIdHigh = 0x0000; - sFilterConfig.FilterMaskIdLow = 0x0000; - sFilterConfig.FilterFIFOAssignment = 0; - sFilterConfig.FilterActivation = ENABLE; - sFilterConfig.BankNumber = 14; - - if ((status = HAL_CAN_ConfigFilter(&CanHandle, &sFilterConfig)) != HAL_OK) { - return status; - } - - if ((status = HAL_CAN_Receive_IT(&CanHandle, CAN_FIFO0)) != HAL_OK) { - return status; - } - - return HAL_OK; -} - -HAL_StatusTypeDef CANlib_TransmitFrame(Frame *frame) { - if (frame->extended) { - CanHandle.pTxMsg->ExtId = frame->id; - CanHandle.pTxMsg->IDE = CAN_ID_EXT; - } else { - CanHandle.pTxMsg->StdId = frame->id; - CanHandle.pTxMsg->IDE = CAN_ID_STD; - } - - CanHandle.pTxMsg->DLC = frame->len; - CanHandle.pTxMsg->RTR = CAN_RTR_DATA; - - memcpy(CanHandle.pTxMsg->Data, frame->data, sizeof(frame->data)); - - __disable_irq(); - HAL_StatusTypeDef CAN_TX_STATUS = HAL_CAN_Transmit_IT(&CanHandle); - __enable_irq(); - - if (CAN_TX_STATUS != HAL_OK) { - // TODO handle error - printf("[CAN TX] ERROR: HAL_StatusTypeDef is %d\r\n", (int) CAN_TX_STATUS); - printf("[CAN TX] ERROR: HAL_CAN_StateTypeDef is %d\r\n", CanHandle.State); - printf("[CAN TX] ERROR: ErrorCode is %d\r\n", CanHandle.ErrorCode); - - return CAN_TX_STATUS; - } - - // ~HACK~ - HAL_CAN_Receive_IT(&CanHandle, CAN_FIFO0); - - return HAL_OK; -} diff --git a/src/drivers/src/test.c b/src/drivers/src/test.c deleted file mode 100644 index b857e45..0000000 --- a/src/drivers/src/test.c +++ /dev/null @@ -1,12 +0,0 @@ -#include "../inc/test.h" -#include - -CANlib_Transmit_Error_T CANlib_TransmitFrame(Frame *frame){ - printf("id: %d dlc: %d\n", frame->id, frame->dlc); - return true; -} - -CANlib_Init_Error_T CANlib_Init(uint32_t baudrate) { - printf("Initialized %d!\n", baudrate); - return true; -} diff --git a/src/inc/clock.hpp b/src/inc/clock.hpp new file mode 100644 index 0000000..f36eda4 --- /dev/null +++ b/src/inc/clock.hpp @@ -0,0 +1,22 @@ +#pragma once + +#include +#include + +namespace CANlib { + +// a generic clock interface that when implemented should statisfy TrivialClock requirements +// implementation should be provided in a source file +// https://en.cppreference.com/w/cpp/named_req/TrivialClock +struct Clock { + typedef int32_t rep; + typedef std::milli period; + typedef std::chrono::duration duration; + typedef std::chrono::time_point time_point; + + static time_point now() noexcept; + + static constexpr bool is_steady = true; +}; + +} // namespace CANlib diff --git a/src/inc/driver.hpp b/src/inc/driver.hpp new file mode 100644 index 0000000..83818c9 --- /dev/null +++ b/src/inc/driver.hpp @@ -0,0 +1,18 @@ +#pragma once + +#ifdef CANLIB_ARCH_STM32F4xx +#include "drivers/stm32f4xx.hpp" +#elif defined(CANLIB_ARCH_STM32F2XX) +#include "drivers/stm32f2xx.hpp" +#elif defined(CANLIB_ARCH_TESTFAMILY) +#include "drivers/testfamily.hpp" +#else +#error "No architecture specified!" +#endif + +#include "bus.hpp" + +namespace CANlib { +RawBus get_raw_bus(AbstractBus bus_name); +AbstractBus get_bus_name(RawBus bus); +} // namespace CANlib diff --git a/src/inc/static.hpp b/src/inc/static.hpp new file mode 100644 index 0000000..8185213 --- /dev/null +++ b/src/inc/static.hpp @@ -0,0 +1,27 @@ +#pragma once + +#include +#include +#include + +struct Frame { + uint32_t id; + uint8_t data[8]; + uint8_t dlc; + bool extended; + Frame &operator=(const Frame &f) { + id = f.id; + for (int i = 0; i < 8; ++i) { + data[i] = f.data[i]; + } + dlc = f.dlc; + extended = f.extended; + return *this; + } + Frame() { + id = 0; + memset(data, 0, sizeof(data)); + dlc = 0; + extended = false; + } +}; diff --git a/src/drivers/src/stm32f4xx.c b/src/src/drivers/stm32f4xx.cpp similarity index 53% rename from src/drivers/src/stm32f4xx.c rename to src/src/drivers/stm32f4xx.cpp index ae01576..429ee75 100644 --- a/src/drivers/src/stm32f4xx.c +++ b/src/src/drivers/stm32f4xx.cpp @@ -1,10 +1,9 @@ -#include "static.h" +#include "static.hpp" #include #include -#include "bus.h" -#include "driver.h" -#include "drivers/inc/stm32f4xx.h" +#include "bus.hpp" +#include "driver.hpp" #include "stm32f4xx_hal.h" #ifdef USING_LOGGING_CALLBACK #include "log.h" @@ -14,20 +13,28 @@ extern CAN_HandleTypeDef hcan1; extern CAN_HandleTypeDef hcan2; extern CAN_HandleTypeDef hcan3; -HAL_StatusTypeDef CANlib_TransmitFrame(Frame *frame, CANlib_Bus_T bus) { - CAN_Raw_Bus_T raw_bus = CANlib_GetRawBus(bus); +using namespace CANlib; + +Clock::time_point Clock::now() noexcept { + static const auto start_time = HAL_GetTick(); + const auto time_passed = HAL_GetTick() - start_time; + return time_point(std::chrono::milliseconds(time_passed)); +} + +TransmitError CANlib::transmit_frame(const Frame &frame, AbstractBus bus_name) { + RawBus raw_bus = get_raw_bus(bus_name); int bus_num; CAN_HandleTypeDef *hcan; switch (raw_bus) { - case CAN_1: + case RawBus::CAN_1: hcan = &hcan1; bus_num = 1; break; - case CAN_2: + case RawBus::CAN_2: hcan = &hcan2; bus_num = 2; break; - case CAN_3: + case RawBus::CAN_3: hcan = &hcan3; bus_num = 3; break; @@ -38,9 +45,9 @@ HAL_StatusTypeDef CANlib_TransmitFrame(Frame *frame, CANlib_Bus_T bus) { CAN_TxHeaderTypeDef pHeader; uint32_t pTxMailbox = 0; - pHeader.DLC = frame->dlc; - pHeader.StdId = frame->id; - pHeader.IDE = frame->extended ? CAN_ID_EXT : CAN_ID_STD; + pHeader.DLC = frame.dlc; + pHeader.StdId = frame.id; + pHeader.IDE = frame.extended ? CAN_ID_EXT : CAN_ID_STD; pHeader.RTR = CAN_RTR_DATA; // Data frame (as opposed to a remote frame) pHeader.TransmitGlobalTime = DISABLE; // Don't replace last 2 bytes of data with TX time. #ifdef USING_LOGGING_CALLBACK @@ -48,20 +55,20 @@ HAL_StatusTypeDef CANlib_TransmitFrame(Frame *frame, CANlib_Bus_T bus) { #else UNUSED(bus_num); #endif - return HAL_CAN_AddTxMessage(hcan, &pHeader, frame->data, &pTxMailbox); + return HAL_CAN_AddTxMessage(hcan, &pHeader, (uint8_t *)frame.data, &pTxMailbox); } -void CANlib_ReadFrame(Frame *frame, CANlib_Bus_T bus) { - CAN_Raw_Bus_T raw_bus = CANlib_GetRawBus(bus); +void CANlib::read_frame(Frame &frame, AbstractBus bus_name) { + RawBus raw_bus = get_raw_bus(bus_name); CAN_HandleTypeDef *hcan; switch (raw_bus) { - case CAN_1: + case RawBus::CAN_1: hcan = &hcan1; break; - case CAN_2: + case RawBus::CAN_2: hcan = &hcan2; break; - case CAN_3: + case RawBus::CAN_3: hcan = &hcan3; break; default: @@ -73,16 +80,12 @@ void CANlib_ReadFrame(Frame *frame, CANlib_Bus_T bus) { for (int fifo = 0; fifo < 2; fifo++) { // There are 2 receive FIFOs if (HAL_CAN_GetRxFifoFillLevel(hcan, fifo) > 0) { HAL_CAN_GetRxMessage(hcan, fifo, &pHeader, data); - frame->id = pHeader.IDE == CAN_ID_STD ? pHeader.StdId : pHeader.ExtId; - frame->dlc = pHeader.DLC; + frame.id = pHeader.IDE == CAN_ID_STD ? pHeader.StdId : pHeader.ExtId; + frame.dlc = pHeader.DLC; - memcpy(frame->data, data, sizeof(data)); - frame->extended = pHeader.IDE == CAN_ID_EXT; + memcpy(frame.data, data, sizeof(data)); + frame.extended = pHeader.IDE == CAN_ID_EXT; return; } } } - -Time_T CANlib_GetTick(void) { - return HAL_GetTick(); -} diff --git a/src/src/drivers/testfamily.cpp b/src/src/drivers/testfamily.cpp new file mode 100644 index 0000000..19173f6 --- /dev/null +++ b/src/src/drivers/testfamily.cpp @@ -0,0 +1,55 @@ +#include "static.hpp" + +#include +#include +#include +#include "bus.hpp" +#include "driver.hpp" +#include "drivers/testfamily.hpp" + +using namespace CANlib; + +TestCAN can1; +TestCAN can2; +TestCAN can3; + +Clock::time_point Clock::now() noexcept { + static const auto start_time = std::chrono::system_clock::now(); + const auto time_passed = std::chrono::system_clock::now() - start_time; + std::chrono::milliseconds us = std::chrono::duration_cast(time_passed); + return time_point(std::chrono::milliseconds(us.count())); +} + +TransmitError CANlib::transmit_frame(const Frame &frame, AbstractBus bus_name) { + switch (get_raw_bus(bus_name)) { + case RawBus::CAN_1: + can1.receiveFrame(frame); + break; + case RawBus::CAN_2: + can2.receiveFrame(frame); + break; + case RawBus::CAN_3: + can3.receiveFrame(frame); + break; + default: + return true; + } + + return false; +} + +void CANlib::read_frame(Frame &frame, AbstractBus bus_name) { + switch (get_raw_bus(bus_name)) { + case RawBus::CAN_1: + frame = can1.frameToSend(); + break; + case RawBus::CAN_2: + frame = can2.frameToSend(); + break; + case RawBus::CAN_3: + frame = can3.frameToSend(); + break; + default: + return; + } +} diff --git a/src/static.h b/src/static.h deleted file mode 100644 index e4934ef..0000000 --- a/src/static.h +++ /dev/null @@ -1,11 +0,0 @@ -#pragma once - -#include -#include - -typedef struct { - uint32_t id; - uint8_t data[8]; - uint8_t dlc; - bool extended; -} Frame; diff --git a/test/Makefile b/test/Makefile new file mode 100644 index 0000000..b0719f2 --- /dev/null +++ b/test/Makefile @@ -0,0 +1,93 @@ +TARGET_EXEC := canlib-tests + +BUILD_DIR := ./build +SRCS_DIR := ../src/src tests/src + +COMP_SRC := ../src/src/computers/*.cpp + +# String substitution for every C/C++ file. +# As an example, hello.cpp turns into ./build/hello.cpp.o + +# String substitution (suffix version without %). +# As an example, ./build/hello.cpp.o turns into ./build/hello.cpp.d +#DEPS := $(OBJS:.o=.d) + +# Add a prefix to INC_DIRS. So moduleA would become -ImoduleA. GCC understands this -I flag +INC_FLAGS := -I../src/inc -Itests/inc +COMPUTER_FLAGS := -I../src/inc/drivers -I../src/inc/computers + +# The -MMD and -MP flags together generate Makefiles for us! +# These files will have .d instead of .o as the output. +CPPFLAGS := $(INC_FLAGS) -std=c++17 + +# The final build step. +gen: + python3 ../generator/main.py can_spec.yml --testing + +main0: gen + g++ $(CPPFLAGS) $(COMPUTER_FLAGS) $(CXXFLAGS) -DCANLIB_ARCH_TESTFAMILY -DARCH0 ../src/src/bus.cpp ../src/src/structs.cpp ../src/src/drivers/testfamily.cpp ../src/src/pack_unpack.cpp ../src/src/computers/canlib_testcomp0.cpp tests/src/identify.cpp tests/src/message.cpp tests/src/send.cpp tests/src/constants.cpp tests/src/arch0.cpp tests/main.cpp -o main0 +main1: gen + g++ $(CPPFLAGS) $(COMPUTER_FLAGS) $(CXXFLAGS) -DCANLIB_ARCH_TESTFAMILY -DARCH1 ../src/src/bus.cpp ../src/src/structs.cpp ../src/src/drivers/testfamily.cpp ../src/src/pack_unpack.cpp ../src/src/computers/canlib_testcomp1.cpp tests/src/identify.cpp tests/src/message.cpp tests/src/send.cpp tests/src/constants.cpp tests/src/arch1.cpp tests/main.cpp -o main1 +main2: gen + g++ $(CPPFLAGS) $(COMPUTER_FLAGS) $(CXXFLAGS) -DCANLIB_ARCH_TESTFAMILY -DARCH2 ../src/src/bus.cpp ../src/src/structs.cpp ../src/src/drivers/testfamily.cpp ../src/src/pack_unpack.cpp ../src/src/computers/canlib_testcomp2.cpp tests/src/identify.cpp tests/src/message.cpp tests/src/send.cpp tests/src/constants.cpp tests/src/arch2.cpp tests/main.cpp -o main2 + +$(BUILD_DIR): + mkdir -p build/src/src + mkdir -p build/src/drivers + mkdir -p build/src/src/computers + mkdir -p build/tests/src + +# Build step for C++ source +$(BUILD_DIR)/src/src/bus.cpp.o: $(BUILD_DIR) + g++ ../src/src/bus.cpp $(CPPFLAGS) $(CXXFLAGS) -c $< -o $(BUILD_DIR)/src/src/bus.cpp.o + +$(BUILD_DIR)/src/src/structs.cpp.o: $(BUILD_DIR) + g++ ../src/src/structs.cpp $(CPPFLAGS) $(CXXFLAGS) -c $< -o $(BUILD_DIR)/src/src/structs.cpp.o + +$(BUILD_DIR)/src/src/pack_unpack.cpp.o: $(BUILD_DIR) + g++ ../src/src/pack_unpack.cpp $(CPPFLAGS) $(CXXFLAGS) -c $< -o $(BUILD_DIR)/src/src/pack_unpack.cpp.o + +$(BUILD_DIR)/tests/src/send.cpp.o: $(BUILD_DIR) + g++ tests/src/send.cpp $(CPPFLAGS) $(COMPUTER_FLAGS) $(CXXFLAGS) -c $< -o $(BUILD_DIR)/tests/src/send.cpp.o + +$(BUILD_DIR)/tests/src/constants.cpp.o: $(BUILD_DIR) + g++ tests/src/constants.cpp $(CPPFLAGS) $(COMPUTER_FLAGS) $(CXXFLAGS) -c $< -o $(BUILD_DIR)/tests/src/constants.cpp.o + +$(BUILD_DIR)/tests/src/identify.cpp.o: $(BUILD_DIR) + g++ tests/src/identify.cpp $(CPPFLAGS) $(COMPUTER_FLAGS) $(CXXFLAGS) -c $< -o $(BUILD_DIR)/tests/src/identify.cpp.o + +$(BUILD_DIR)/tests/src/arch0.cpp.o: $(BUILD_DIR) + g++ tests/src/arch0.cpp $(CPPFLAGS) $(COMPUTER_FLAGS) $(CXXFLAGS) -c $< -o $(BUILD_DIR)/tests/src/arch0.cpp.o + +$(BUILD_DIR)/tests/src/arch1.cpp.o: $(BUILD_DIR) + g++ tests/src/arch1.cpp $(CPPFLAGS) $(COMPUTER_FLAGS) $(CXXFLAGS) -c $< -o $(BUILD_DIR)/tests/src/arch1.cpp.o + +$(BUILD_DIR)/tests/src/arch2.cpp.o: $(BUILD_DIR) + g++ tests/src/arch2.cpp $(CPPFLAGS) $(COMPUTER_FLAGS) $(CXXFLAGS) -c $< -o $(BUILD_DIR)/tests/src/arch2.cpp.o + +$(BUILD_DIR)/tests/src/message.cpp.o: $(BUILD_DIR) + g++ tests/src/message.cpp $(CPPFLAGS) $(COMPUTER_FLAGS) $(CXXFLAGS) -c $< -o $(BUILD_DIR)/tests/src/message.cpp.o + +$(BUILD_DIR)/src/src/drivers/testfamily.cpp.o: $(BUILD_DIR) + g++ ../src/src/drivers/testfamily.cpp $(CPPFLAGS) $(COMPUTER_FLAGS) $(CXXFLAGS) -c $< -o $(BUILD_DIR)/src/src/drivers/testfamily.cpp.o + +$(BUILD_DIR)/tests/main.cpp.o: $(BUILD_DIR) + g++ tests/main.cpp $(CPPFLAGS) $(COMPUTER_FLAGS) $(CXXFLAGS) -c $< -o $(BUILD_DIR)/./tests/main.cpp.o + +$(BUILD_DIR)/src/src/computers/canlib_testcomp0.cpp.o: $(BUILD_DIR) + g++ ../src/src/computers/canlib_testcomp0.cpp $(CPPFLAGS) $(COMPUTER_FLAGS) $(CXXFLAGS) -c $< -o $(BUILD_DIR)/src/src/computers/canlib_testcomp0.cpp.o + +$(BUILD_DIR)/src/src/computers/canlib_testcomp1.cpp.o: $(BUILD_DIR) + g++ ../src/src/computers/canlib_testcomp1.cpp $(CPPFLAGS) $(COMPUTER_FLAGS) $(CXXFLAGS) -c $< -o $(BUILD_DIR)/src/src/computers/canlib_testcomp1.cpp.o + +$(BUILD_DIR)/src/src/computers/canlib_testcomp2.cpp.o: $(BUILD_DIR) + g++ ../src/src/computers/canlib_testcomp2.cpp $(CPPFLAGS) $(COMPUTER_FLAGS) $(CXXFLAGS) -c $< -o $(BUILD_DIR)/src/src/computers/canlib_testcomp2.cpp.o + +.PHONY: clean build +clean: + python3 ../generator/main.py --clean + rm -r build/ + rm main0 main1 main2 +build: main0 main1 main2 +run: + ./main0 & ./main1 & ./main2 diff --git a/test/can_spec.yml b/test/can_spec.yml new file mode 100644 index 0000000..72f4bc0 --- /dev/null +++ b/test/can_spec.yml @@ -0,0 +1,498 @@ +name: MY20 +units: +architecture: + testarch: + family: testfamily + participation: + can: + buses: + - CAN_1 + - CAN_2 + - CAN_3 +computer: + testcomp0: + architecture: testarch + participation: + can: + mapping: + map1: CAN_2 + map2: CAN_1 + publish: + map1: + - B + - E + subscribe: + map2: + - F + - M + - N + map1: + - A + - C + - D + testcomp1: + architecture: testarch + participation: + can: + mapping: + map1: CAN_2 + map2: CAN_3 + publish: + map2: + - F + - H + - J + map1: + - A + - D + subscribe: + map1: + - B + map2: + - I + - K + - L + - M + testcomp2: + architecture: testarch + participation: + can: + mapping: + map1: CAN_3 + map2: CAN_1 + publish: + map1: + - A + - B + - C + map2: + - G + - I + - L + subscribe: + map1: + - D + - E + map2: + - F + - H + - J + - K + - M + - N +protocol: + can: + bus: + map1: + version: 2.0B + baudrate: 500000 + extended: false + frame: + A: + key: 0x2FF + period: 50ms + atom: + ARG0: + slice: 7 + 1 + type: bool little + ARG1: + slice: 8 + 16 + type: uint16 little + ARG2: + slice: 24 + 16 + type: uint16 little + ARG3: + slice: 40 + 16 + type: uint16 little + B: + key: 0x305 + period: 200ms + atom: + ARG0: + slice: 5 + 3 + type: + endianness: big + enum: + IDLE: 1 + CHARGE: 2 + RECOVERABLE_ERROR: 3 + NONRECOVERABLE_ERROR: 4 + ARG1: + slice: 8 + 16 + type: uint16 little + ARG2: + slice: 24 + 16 + type: uint16 little + ARG3: + slice: 40 + 16 + type: uint16 little + ARG4: + slice: 56 + 8 + type: uint8 little + C: + key: 0x306 + period: 200ms + atom: + ARG0: + slice: 0 + 8 + type: int8 little + ARG1: + slice: 8 + 8 + type: int8 little + ARG2: + slice: 16 + 16 + type: uint16 little + ARG3: + slice: 32 + 16 + type: uint16 little + ARG4: + slice: 48 + 8 + type: uint8 little + D: + key: 0x307 + period: 200ms + atom: + ARG0: + slice: 0 + 1 + type: &bit_type + endianness: big + enum: + LO: 0 + HI: 1 + ARG1: + slice: 2 + 1 + type: *bit_type + ARG2: + slice: 3 + 1 + type: *bit_type + ARG3: + slice: 4 + 1 + type: *bit_type + ARG4: + slice: 5 + 1 + type: *bit_type + ARG5: + slice: 6 + 1 + type: *bit_type + ARG6: + slice: 7 + 1 + type: *bit_type + ARG7: + slice: 9 + 1 + type: *bit_type + ARG8: + slice: 16 + 1 + type: *bit_type + ARG9: + slice: 17 + 1 + type: *bit_type + E: + key: 0x308 + period: 1000ms + atom: + ARG0: + slice: 0 + 48 + type: uint64 little + ARG1: + slice: 48 + 16 + type: uint16 little + map2: + version: 2.0B + baudrate: 1000000 + extended: false + frame: + F: + key: 0x016 + period: 101ms + atom: + ARG0: + slice: 0 + 10 + type: int16 big + ARG1: + slice: 10 + 8 + type: uint8 big + ARG2: + slice: 18 + 10 + type: int16 big + ARG3: + slice: 28 + 8 + type: uint8 big + ARG4: + slice: 36 + 10 + type: int16 big + ARG5: + slice: 46 + 8 + type: uint8 big + ARG6: + slice: 54 + 10 + type: int16 big + G: + key: 0x017 + period: 31ms + atom: + ARG0: + slice: 0 + 14 + type: uint16 big + ARG1: + slice: 14 + 14 + type: uint16 big + ARG2: + slice: 28 + 32 + type: uint32 big + H: + key: 0x0A0 + atom: + <<: &temps + a_temp: + slice: 0 + 16 + unit: decidegC + type: int16 little + b_temp: + slice: 16 + 16 + unit: decidegC + type: int16 little + c_temp: + slice: 32 + 16 + unit: decidegC + type: int16 little + d_temp: + slice: 48 + 16 + unit: decidegC + type: int16 little + I: + key: 0xA2 + atom: + ARG0: + slice: 0 + 16 + type: int16 little + ARG1: + slice: 16 + 16 + type: int16 little + ARG2: + slice: 32 + 16 + type: int16 little + ARG3: + slice: 48 + 16 + type: int16 little + J: + key: 0x0C2 + slice: 0 + 8 + frame: + AA: + key: 0 + <<: &bool_data + atom: + write_success: + slice: 23 + 1 + type: bool little + data: + slice: 39 + 1 + type: bool little + BB: + key: 0x14 + <<: *bool_data + #Required to correctly parse the data with the script, as this parameter is constantly + #being transmitted over the CAN bus. + CC: + key: 0x64 + slice: 8 + 8 + frame: + AAA: + key: 0x69 + atom: + <<: &write_success + write_success: # whether EEPROM has been re-written + slice: 23 + 1 + type: bool little + data: + <<: &signed_int_data + slice: 32 + 16 + type: int16 little + BBB: + key: 0x70 + atom: + <<: *write_success + data: + <<: *signed_int_data + CCC: + key: 0x71 + atom: + <<: *write_success + data: + <<: *signed_int_data + DDD: + key: 0x72 + atom: + <<: *write_success + data: + <<: *signed_int_data + DD: + key: 0x65 + atom: + <<: &write_success_2 + write_success: # whether EEPROM has been re-written + slice: 23 + 1 + type: bool little + data: + <<: &signed_int_data_2 + slice: 32 + 16 + type: int16 little + EE: + key: 0x66 + atom: + <<: *write_success_2 + data: + <<: *signed_int_data_2 + FF: + key: 0x67 + atom: + <<: *write_success_2 + data: + <<: *signed_int_data_2 + GG: + key: 0x68 + atom: + <<: *write_success_2 + data: + <<: *signed_int_data_2 + HH: + key: 0x6a + atom: + <<: *write_success_2 + data: + <<: *signed_int_data_2 + II: + key: 0x6b + atom: + <<: *write_success_2 + data: + <<: &unsigned_int_data + slice: 32 + 16 + type: uint16 little + JJ: + key: 0x6c + atom: + <<: *write_success_2 + data: + <<: *unsigned_int_data + KK: + key: 0x6d + atom: + <<: *write_success_2 + data: + <<: *unsigned_int_data + LL: + key: 0x6f + atom: + <<: *write_success_2 + data: + <<: *signed_int_data + MM: + key: 0xab + <<: *bool_data + NN: + key: 0x91 + <<: *bool_data + K: + key: 0x0BB + atom: + ARG0: + slice: 0 + 16 + type: uint16 big + ARG1: + slice: 16 + 16 + type: uint16 big + L: + key: 0x0DF + period: 103ms + atom: + ARG0: + slice: 0 + 1 + type: bool big + ARG1: + slice: 1 + 1 + type: bool big + ARG2: + slice: 2 + 1 + type: bool big + ARG3: + slice: 3 + 1 + type: bool big + ARG4: + slice: 4 + 1 + type: bool big + ARG5: + slice: 5 + 1 + type: bool big + ARG6: + slice: 6 + 1 + type: bool big + ARG7: + slice: 7 + 1 + type: bool big + ARG8: + slice: 8 + 1 + type: bool big + ARG9: + slice: 9 + 1 + type: bool big + ARG10: + slice: 10 + 1 + type: bool big + ARG11: + slice: 11 + 1 + type: bool big + ARG12: + slice: 12 + 1 + type: bool big + ARG13: + slice: 13 + 1 + type: bool big + ARG14: + slice: 14 + 1 + type: bool big + ARG15: + slice: 15 + 1 + type: bool big + ARG16: + slice: 16 + 1 + type: bool big + ARG17: + slice: 17 + 8 + type: uint8 big + M: + key: 0x0F0 + period: 103ms + atom: + ARG0: + slice: 0 + 10 + type: int16 big + ARG1: + slice: 10 + 16 + type: uint16 big + N: + key: 0x400 + atom: + ARG0: + slice: 3 + 1 + type: bool big + ARG1: + slice: 4 + 3 + type: + endianness: big + enum: + x1: 0 + x2: 1 + x4: 2 + x8: 3 + x16: 4 + x32: 5 + ARG2: + slice: 8 + 1 + type: bool big + ARG3: + slice: 9 + 1 + type: bool big + ARG4: + slice: 10 + 1 + type: bool big diff --git a/test/tests/inc/test.hpp b/test/tests/inc/test.hpp new file mode 100644 index 0000000..2dffd46 --- /dev/null +++ b/test/tests/inc/test.hpp @@ -0,0 +1,32 @@ +#pragma once +#include +#include "bus.hpp" +#include "driver.hpp" +#include "structs.hpp" +#include "testfamily.hpp" + +#ifdef ARCH0 +#define MAP1_CAN can2 +#define MAP2_CAN can1 +#endif + +#ifdef ARCH1 +#define MAP1_CAN can2 +#define MAP2_CAN can3 +#endif + +#ifdef ARCH2 +#define MAP1_CAN can3 +#define MAP2_CAN can1 +#endif + +extern std::default_random_engine generator; +extern std::uniform_int_distribution distribution; + +void testArch(); +void testPackUnpack(); +void testKeys(); +void testPeriod(); +void testMessageType(); +void testIdentify(); +void testSend(); diff --git a/test/tests/main.cpp b/test/tests/main.cpp new file mode 100644 index 0000000..2a83f05 --- /dev/null +++ b/test/tests/main.cpp @@ -0,0 +1,17 @@ +#include +#include "test.hpp" + +std::default_random_engine generator; +std::uniform_int_distribution distribution(0, 511); + +int main() { + testIdentify(); + testArch(); + testKeys(); + testPeriod(); + testMessageType(); + testPackUnpack(); + testSend(); + printf("Success ..."); + return 0; +} diff --git a/test/tests/src/arch0.cpp b/test/tests/src/arch0.cpp new file mode 100644 index 0000000..bf514d2 --- /dev/null +++ b/test/tests/src/arch0.cpp @@ -0,0 +1,64 @@ +#include +#include +#include "canlib_testcomp0.hpp" +#include "test.hpp" + +using namespace CANlib; +using namespace map1; +using namespace map2; + +extern TestCAN can1; +extern TestCAN can2; +extern TestCAN can3; + +void testArch() { + // AbstractBus/RawBus mapping is correct + assert(get_raw_bus(AbstractBus::map1) == RawBus::CAN_2); + assert(get_raw_bus(AbstractBus::map2) == RawBus::CAN_1); + assert(get_raw_bus(AbstractBus::INVALID_NAME) == RawBus::INVALID_BUS); + assert(get_bus_name(RawBus::CAN_2) == AbstractBus::map1); + assert(get_bus_name(RawBus::CAN_1) == AbstractBus::map2); + assert(get_bus_name(RawBus::CAN_3) == AbstractBus::INVALID_NAME); + assert(get_bus_name(RawBus::INVALID_BUS) == AbstractBus::INVALID_NAME); + can1.clear(); + can2.clear(); + can3.clear(); + // Correct cans receive the messages + send(map1::A::get_input()); + send(map2::F::get_input()); + assert(1 == can1.framesReceived()); + assert(1 == can2.framesReceived()); + assert(0 == can3.framesReceived()); + can1.clear(); + can2.clear(); + can3.clear(); + Frame f; + map1::A A_copy; + A_copy.ARG0 = true; + A_copy.ARG1 = 69; + A_copy.ARG2 = 69; + A_copy.ARG3 = 69; + A_copy.unpack(f); + // can2 will send A_copy + can2.setFrameToSend(f); + map1::A A_copy2; + map1_update_can(); + map1::A *A_input = map1::A::get_input(); + // The received message is the same as A_copy + assert(A_input->ARG0 && A_input->ARG1 == 69 && A_input->ARG2 == 69 && A_input->ARG3 == 69); + map2::F F_copy; + F_copy.ARG0 = 69; + F_copy.ARG1 = 69; + F_copy.ARG2 = 69; + F_copy.ARG3 = 69; + F_copy.ARG4 = 69; + F_copy.ARG5 = 69; + F_copy.unpack(f); + // can1 will send F_copy + can1.setFrameToSend(f); + map2_update_can(); + map2::F *F_input = map2::F::get_input(); + // The received message is the same as F_copy + assert(F_input->ARG0 == 69 && F_input->ARG1 == 69 && F_input->ARG2 == 69 && F_input->ARG3 == 69 && + F_input->ARG4 == 69 && F_input->ARG5 == 69); +} diff --git a/test/tests/src/arch1.cpp b/test/tests/src/arch1.cpp new file mode 100644 index 0000000..25878de --- /dev/null +++ b/test/tests/src/arch1.cpp @@ -0,0 +1,63 @@ +#include +#include "canlib_testcomp1.hpp" +#include "test.hpp" + +using namespace CANlib; +using namespace map1; +using namespace map2; + +extern TestCAN can1; +extern TestCAN can2; +extern TestCAN can3; + +void testArch() { + // AbstractBus/RawBus mapping is correct + assert(get_raw_bus(AbstractBus::map1) == RawBus::CAN_2); + assert(get_raw_bus(AbstractBus::map2) == RawBus::CAN_3); + assert(get_raw_bus(AbstractBus::INVALID_NAME) == RawBus::INVALID_BUS); + assert(get_bus_name(RawBus::CAN_2) == AbstractBus::map1); + assert(get_bus_name(RawBus::CAN_3) == AbstractBus::map2); + assert(get_bus_name(RawBus::CAN_1) == AbstractBus::INVALID_NAME); + assert(get_bus_name(RawBus::INVALID_BUS) == AbstractBus::INVALID_NAME); + can1.clear(); + can2.clear(); + can3.clear(); + // Correct cans receive the messages + send(map1::A::get_input()); + send(map2::F::get_input()); + assert(0 == can1.framesReceived()); + assert(1 == can2.framesReceived()); + assert(1 == can3.framesReceived()); + can1.clear(); + can2.clear(); + can3.clear(); + Frame f; + map1::A A_copy; + A_copy.ARG0 = true; + A_copy.ARG1 = 69; + A_copy.ARG2 = 69; + A_copy.ARG3 = 69; + A_copy.unpack(f); + // can2 will send A_copy + can2.setFrameToSend(f); + map1::A A_copy2; + map1_update_can(); + map1::A *A_input = map1::A::get_input(); + // The received message is the same as A_copy + assert(A_input->ARG0 && A_input->ARG1 == 69 && A_input->ARG2 == 69 && A_input->ARG3 == 69); + map2::F F_copy; + F_copy.ARG0 = 69; + F_copy.ARG1 = 69; + F_copy.ARG2 = 69; + F_copy.ARG3 = 69; + F_copy.ARG4 = 69; + F_copy.ARG5 = 69; + F_copy.unpack(f); + // can3 will send F_copy + can3.setFrameToSend(f); + map2_update_can(); + map2::F *F_input = map2::F::get_input(); + // The received message is the same as F_copy + assert(F_input->ARG0 == 69 && F_input->ARG1 == 69 && F_input->ARG2 == 69 && F_input->ARG3 == 69 && + F_input->ARG4 == 69 && F_input->ARG5 == 69); +} diff --git a/test/tests/src/arch2.cpp b/test/tests/src/arch2.cpp new file mode 100644 index 0000000..ec075eb --- /dev/null +++ b/test/tests/src/arch2.cpp @@ -0,0 +1,72 @@ +#include +#include "canlib_testcomp2.hpp" +#include "test.hpp" + +namespace CANlib { +namespace map1 { +extern A A_input; +} // namespace map1 +namespace map2 { +extern F F_input; +} // namespace map2 +} // namespace CANlib + +using namespace CANlib; +using namespace map1; +using namespace map2; + +extern TestCAN can1; +extern TestCAN can2; +extern TestCAN can3; + +void testArch() { + // AbstractBus/RawBus mapping is correct + assert(get_raw_bus(AbstractBus::map1) == RawBus::CAN_3); + assert(get_raw_bus(AbstractBus::map2) == RawBus::CAN_1); + assert(get_raw_bus(AbstractBus::INVALID_NAME) == RawBus::INVALID_BUS); + assert(get_bus_name(RawBus::CAN_3) == AbstractBus::map1); + assert(get_bus_name(RawBus::CAN_1) == AbstractBus::map2); + assert(get_bus_name(RawBus::CAN_2) == AbstractBus::INVALID_NAME); + assert(get_bus_name(RawBus::INVALID_BUS) == AbstractBus::INVALID_NAME); + can1.clear(); + can2.clear(); + can3.clear(); + // Correct cans receive the messages + send(map1::A::get_input()); + send(map2::F::get_input()); + assert(1 == can1.framesReceived()); + assert(0 == can2.framesReceived()); + assert(1 == can3.framesReceived()); + can1.clear(); + can2.clear(); + can3.clear(); + Frame f; + map1::A A_copy; + A_copy.ARG0 = true; + A_copy.ARG1 = 69; + A_copy.ARG2 = 69; + A_copy.ARG3 = 69; + A_copy.unpack(f); + // can3 will send A_copy + can3.setFrameToSend(f); + map1::A A_copy2; + map1_update_can(); + map1::A *A_input = map1::A::get_input(); + // The received message is the same as A_copy + assert(A_input->ARG0 && A_input->ARG1 == 69 && A_input->ARG2 == 69 && A_input->ARG3 == 69); + map2::F F_copy; + F_copy.ARG0 = 69; + F_copy.ARG1 = 69; + F_copy.ARG2 = 69; + F_copy.ARG3 = 69; + F_copy.ARG4 = 69; + F_copy.ARG5 = 69; + F_copy.unpack(f); + // can1 will send F_copy + can1.setFrameToSend(f); + map2_update_can(); + map2::F *F_input = map2::F::get_input(); + // The received message is the same as F_copy + assert(F_input->ARG0 == 69 && F_input->ARG1 == 69 && F_input->ARG2 == 69 && F_input->ARG3 == 69 && + F_input->ARG4 == 69 && F_input->ARG5 == 69); +} diff --git a/test/tests/src/constants.cpp b/test/tests/src/constants.cpp new file mode 100644 index 0000000..a931f76 --- /dev/null +++ b/test/tests/src/constants.cpp @@ -0,0 +1,338 @@ +#include +#include +#include "test.hpp" + +namespace CANlib { +namespace map1 { +extern MessageType message_types[6]; +} +namespace map2 { +extern MessageType message_types[28]; +} +} // namespace CANlib + +using namespace CANlib; +using namespace std::chrono_literals; +using map1::A; +using map1::B; +using map1::C; +using map1::D; +using map1::E; +using map2::F; +using map2::G; +using map2::H; +using map2::I; +using map2::K; +using map2::L; +using map2::M; +using map2::N; +using map2::J::AA; +using map2::J::BB; +using map2::J::DD; +using map2::J::EE; +using map2::J::FF; +using map2::J::GG; +using map2::J::HH; +using map2::J::II; +using map2::J::JJ; +using map2::J::KK; +using map2::J::LL; +using map2::J::MM; +using map2::J::NN; +using map2::J::CC::AAA; +using map2::J::CC::BBB; +using map2::J::CC::CCC; +using map2::J::CC::DDD; + +void testKeys() { + A A_copy; + B B_copy; + C C_copy; + D D_copy; + E E_copy; + F F_copy; + G G_copy; + H H_copy; + I I_copy; + AA AA_copy; + BB BB_copy; + AAA AAA_copy; + BBB BBB_copy; + CCC CCC_copy; + DDD DDD_copy; + DD DD_copy; + EE EE_copy; + FF FF_copy; + GG GG_copy; + HH HH_copy; + II II_copy; + JJ JJ_copy; + KK KK_copy; + LL LL_copy; + MM MM_copy; + NN NN_copy; + K K_copy; + L L_copy; + M M_copy; + N N_copy; + assert(A_copy.get_key() == 0x2FF); + assert(map1::A::key == 0x2FF); + assert(B_copy.get_key() == 0x305); + assert(map1::B::key == 0x305); + assert(C_copy.get_key() == 0x306); + assert(map1::C::key == 0x306); + assert(D_copy.get_key() == 0x307); + assert(map1::D::key == 0x307); + assert(E_copy.get_key() == 0x308); + assert(map1::E::key == 0x308); + assert(F_copy.get_key() == 0x16); + assert(map2::F::key == 0x16); + assert(G_copy.get_key() == 0x17); + assert(map2::G::key == 0x17); + assert(H_copy.get_key() == 0xA0); + assert(map2::H::key == 0xA0); + assert(I_copy.get_key() == 0xA2); + assert(map2::I::key == 0xA2); + assert(map2::J::key == 0xC2); + assert(AA_copy.get_key() == 0x0); + assert(map2::J::AA::key == 0x0); + assert(BB_copy.get_key() == 0x14); + assert(map2::J::BB::key == 0x14); + assert(map2::J::CC::key == 0x64); + assert(AAA_copy.get_key() == 0x69); + assert(map2::J::CC::AAA::key == 0x69); + assert(BBB_copy.get_key() == 0x70); + assert(map2::J::CC::BBB::key == 0x70); + assert(CCC_copy.get_key() == 0x71); + assert(map2::J::CC::CCC::key == 0x71); + assert(DDD_copy.get_key() == 0x72); + assert(map2::J::CC::DDD::key == 0x72); + assert(DD_copy.get_key() == 0x65); + assert(map2::J::DD::key == 0x65); + assert(EE_copy.get_key() == 0x66); + assert(map2::J::EE::key == 0x66); + assert(FF_copy.get_key() == 0x67); + assert(map2::J::FF::key == 0x67); + assert(GG_copy.get_key() == 0x68); + assert(map2::J::GG::key == 0x68); + assert(HH_copy.get_key() == 0x6A); + assert(map2::J::HH::key == 0x6A); + assert(II_copy.get_key() == 0x6B); + assert(map2::J::II::key == 0x6B); + assert(JJ_copy.get_key() == 0x6C); + assert(map2::J::JJ::key == 0x6C); + assert(KK_copy.get_key() == 0x6D); + assert(map2::J::KK::key == 0x6D); + assert(LL_copy.get_key() == 0x6F); + assert(map2::J::LL::key == 0x6F); + assert(MM_copy.get_key() == 0xAB); + assert(map2::J::MM::key == 0xAB); + assert(NN_copy.get_key() == 0x91); + assert(map2::J::NN::key == 0x91); + assert(K_copy.get_key() == 0xBB); + assert(map2::K::key == 0xBB); + assert(L_copy.get_key() == 0xDF); + assert(map2::L::key == 0xDF); + assert(M_copy.get_key() == 0xF0); + assert(map2::M::key == 0xF0); + assert(N_copy.get_key() == 0x400); + assert(map2::N::key == 0x400); +} + +void testPeriod() { + map1::A A_copy; + map1::B B_copy; + map1::C C_copy; + map1::D D_copy; + map1::E E_copy; + map2::F F_copy; + map2::G G_copy; + map2::H H_copy; + map2::I I_copy; + map2::J::AA AA_copy; + map2::J::BB BB_copy; + map2::J::CC::AAA AAA_copy; + map2::J::CC::BBB BBB_copy; + map2::J::CC::CCC CCC_copy; + map2::J::CC::DDD DDD_copy; + map2::J::DD DD_copy; + map2::J::EE EE_copy; + map2::J::FF FF_copy; + map2::J::GG GG_copy; + map2::J::HH HH_copy; + map2::J::II II_copy; + map2::J::JJ JJ_copy; + map2::J::KK KK_copy; + map2::J::LL LL_copy; + map2::J::MM MM_copy; + map2::J::NN NN_copy; + map2::K K_copy; + map2::L L_copy; + map2::M M_copy; + map2::N N_copy; + assert(A_copy.get_period() == 50ms); + assert(B_copy.get_period() == 200ms); + assert(C_copy.get_period() == 200ms); + assert(D_copy.get_period() == 200ms); + assert(E_copy.get_period() == 1000ms); + assert(F_copy.get_period() == 101ms); + assert(G_copy.get_period() == 31ms); + assert(H_copy.get_period() == 0ms); + assert(I_copy.get_period() == 0ms); + assert(AA_copy.get_period() == 0ms); + assert(BB_copy.get_period() == 0ms); + assert(AAA_copy.get_period() == 0ms); + assert(BBB_copy.get_period() == 0ms); + assert(CCC_copy.get_period() == 0ms); + assert(DDD_copy.get_period() == 0ms); + assert(DD_copy.get_period() == 0ms); + assert(EE_copy.get_period() == 0ms); + assert(FF_copy.get_period() == 0ms); + assert(GG_copy.get_period() == 0ms); + assert(HH_copy.get_period() == 0ms); + assert(II_copy.get_period() == 0ms); + assert(JJ_copy.get_period() == 0ms); + assert(KK_copy.get_period() == 0ms); + assert(LL_copy.get_period() == 0ms); + assert(MM_copy.get_period() == 0ms); + assert(NN_copy.get_period() == 0ms); + assert(K_copy.get_period() == 0ms); + assert(L_copy.get_period() == 103ms); + assert(M_copy.get_period() == 103ms); + assert(N_copy.get_period() == 0ms); +} + +#define CREATE_TEST(Name, bus_idx, key, msg) \ + static void testMessageType##Name() { \ + Name Name##_copy; \ + Frame f; \ + Name##_copy.unpack(f); \ + int idx = identify(AbstractBus::map##bus_idx, f); \ + assert(map##bus_idx::message_types[idx] == map##bus_idx::MessageType::msg); \ + } +CREATE_TEST(A, 1, 0X2FF, A) +CREATE_TEST(B, 1, 0X305, B) +CREATE_TEST(C, 1, 0X306, C) +CREATE_TEST(D, 1, 0X307, D) +CREATE_TEST(E, 1, 0X308, E) +CREATE_TEST(F, 2, 0X016, F) +CREATE_TEST(G, 2, 0X017, G) +CREATE_TEST(H, 2, 0X0A0, H) +CREATE_TEST(I, 2, 0XA2, I) +CREATE_TEST(AA, 2, 0, J_AA) +CREATE_TEST(BB, 2, 0X14, J_BB) +CREATE_TEST(AAA, 2, 0X69, J_CC_AAA) +CREATE_TEST(BBB, 2, 0X70, J_CC_BBB) +CREATE_TEST(CCC, 2, 0X71, J_CC_CCC) +CREATE_TEST(DDD, 2, 0X72, J_CC_DDD) +CREATE_TEST(DD, 2, 0X65, J_DD) +CREATE_TEST(EE, 2, 0X66, J_EE) +CREATE_TEST(FF, 2, 0X67, J_FF) +CREATE_TEST(GG, 2, 0X68, J_GG) +CREATE_TEST(HH, 2, 0X6A, J_HH) +CREATE_TEST(II, 2, 0X6B, J_II) +CREATE_TEST(JJ, 2, 0X6C, J_JJ) +CREATE_TEST(KK, 2, 0X6D, J_KK) +CREATE_TEST(LL, 2, 0X6F, J_LL) +CREATE_TEST(MM, 2, 0XAB, J_MM) +CREATE_TEST(NN, 2, 0X91, J_NN) +CREATE_TEST(K, 2, 0X0BB, K) +CREATE_TEST(L, 2, 0X0DF, L) +CREATE_TEST(M, 2, 0X0F0, M) +CREATE_TEST(N, 2, 0X400, N) + +void testMessageType() { + testMessageTypeA(); + testMessageTypeB(); + testMessageTypeC(); + testMessageTypeD(); + testMessageTypeE(); + testMessageTypeF(); + testMessageTypeG(); + testMessageTypeH(); + testMessageTypeI(); + testMessageTypeAA(); + testMessageTypeBB(); + testMessageTypeAAA(); + testMessageTypeBBB(); + testMessageTypeCCC(); + testMessageTypeDDD(); + testMessageTypeDD(); + testMessageTypeEE(); + testMessageTypeFF(); + testMessageTypeGG(); + testMessageTypeHH(); + testMessageTypeII(); + testMessageTypeJJ(); + testMessageTypeKK(); + testMessageTypeLL(); + testMessageTypeMM(); + testMessageTypeNN(); + testMessageTypeK(); + testMessageTypeL(); + testMessageTypeM(); + testMessageTypeN(); +} + +void testBusNames() { + map1::A A_copy; + map1::B B_copy; + map1::C C_copy; + map1::D D_copy; + map1::E E_copy; + map2::F F_copy; + map2::G G_copy; + map2::H H_copy; + map2::I I_copy; + map2::J::AA AA_copy; + map2::J::BB BB_copy; + map2::J::CC::AAA AAA_copy; + map2::J::CC::BBB BBB_copy; + map2::J::CC::CCC CCC_copy; + map2::J::CC::DDD DDD_copy; + map2::J::DD DD_copy; + map2::J::EE EE_copy; + map2::J::FF FF_copy; + map2::J::GG GG_copy; + map2::J::HH HH_copy; + map2::J::II II_copy; + map2::J::JJ JJ_copy; + map2::J::KK KK_copy; + map2::J::LL LL_copy; + map2::J::MM MM_copy; + map2::J::NN NN_copy; + map2::K K_copy; + map2::L L_copy; + map2::M M_copy; + map2::N N_copy; + assert(A_copy.get_bus_name() == AbstractBus::map1); + assert(B_copy.get_bus_name() == AbstractBus::map1); + assert(C_copy.get_bus_name() == AbstractBus::map1); + assert(D_copy.get_bus_name() == AbstractBus::map1); + assert(E_copy.get_bus_name() == AbstractBus::map1); + assert(F_copy.get_bus_name() == AbstractBus::map2); + assert(G_copy.get_bus_name() == AbstractBus::map2); + assert(H_copy.get_bus_name() == AbstractBus::map2); + assert(I_copy.get_bus_name() == AbstractBus::map2); + assert(AA_copy.get_bus_name() == AbstractBus::map2); + assert(BB_copy.get_bus_name() == AbstractBus::map2); + assert(AAA_copy.get_bus_name() == AbstractBus::map2); + assert(BBB_copy.get_bus_name() == AbstractBus::map2); + assert(CCC_copy.get_bus_name() == AbstractBus::map2); + assert(DDD_copy.get_bus_name() == AbstractBus::map2); + assert(DD_copy.get_bus_name() == AbstractBus::map2); + assert(EE_copy.get_bus_name() == AbstractBus::map2); + assert(FF_copy.get_bus_name() == AbstractBus::map2); + assert(GG_copy.get_bus_name() == AbstractBus::map2); + assert(HH_copy.get_bus_name() == AbstractBus::map2); + assert(II_copy.get_bus_name() == AbstractBus::map2); + assert(JJ_copy.get_bus_name() == AbstractBus::map2); + assert(KK_copy.get_bus_name() == AbstractBus::map2); + assert(LL_copy.get_bus_name() == AbstractBus::map2); + assert(MM_copy.get_bus_name() == AbstractBus::map2); + assert(NN_copy.get_bus_name() == AbstractBus::map2); + assert(K_copy.get_bus_name() == AbstractBus::map2); + assert(L_copy.get_bus_name() == AbstractBus::map2); + assert(M_copy.get_bus_name() == AbstractBus::map2); + assert(N_copy.get_bus_name() == AbstractBus::map2); +} diff --git a/test/tests/src/identify.cpp b/test/tests/src/identify.cpp new file mode 100644 index 0000000..1330735 --- /dev/null +++ b/test/tests/src/identify.cpp @@ -0,0 +1,102 @@ +#include +#include +#include "test.hpp" + +namespace CANlib { +extern Message **messages[2]; +} + +using namespace CANlib; +using namespace map1; +using namespace map2; +using namespace J; +using namespace CC; + +#define CREATE_TEST0(Name, bus_idx, key) \ + static void test##Name() { \ + for (int cs = 0; cs < 100; ++cs) { \ + Frame f; \ + f.dlc = distribution(generator); \ + for (int i = 0; i < 8; ++i) { \ + f.data[i] = distribution(generator); \ + } \ + f.id = key; \ + int idx = identify(AbstractBus::map##bus_idx, f); \ + Message *msg = messages[bus_idx - 1][idx]; \ + assert((dynamic_cast(msg)) != nullptr); \ + } \ + } + +#define CREATE_TEST1(Name, bus_idx, key) \ + static void test##Name() { \ + Name Name##_copy; \ + Frame f; \ + Name##_copy.unpack(f); \ + int idx = identify(AbstractBus::map##bus_idx, f); \ + Message *msg = messages[bus_idx - 1][idx]; \ + assert((dynamic_cast(msg)) != nullptr); \ + } + +CREATE_TEST0(A, 1, 0X2FF) +CREATE_TEST0(B, 1, 0X305) +CREATE_TEST0(C, 1, 0X306) +CREATE_TEST0(D, 1, 0X307) +CREATE_TEST0(E, 1, 0X308) +CREATE_TEST0(F, 2, 0X016) +CREATE_TEST0(G, 2, 0X017) +CREATE_TEST0(H, 2, 0X0A0) +CREATE_TEST0(I, 2, 0XA2) +CREATE_TEST1(AA, 2, 0) +CREATE_TEST1(BB, 2, 0X14) +CREATE_TEST1(AAA, 2, 0X69) +CREATE_TEST1(BBB, 2, 0X70) +CREATE_TEST1(CCC, 2, 0X71) +CREATE_TEST1(DDD, 2, 0X72) +CREATE_TEST1(DD, 2, 0X65) +CREATE_TEST1(EE, 2, 0X66) +CREATE_TEST1(FF, 2, 0X67) +CREATE_TEST1(GG, 2, 0X68) +CREATE_TEST1(HH, 2, 0X6A) +CREATE_TEST1(II, 2, 0X6B) +CREATE_TEST1(JJ, 2, 0X6C) +CREATE_TEST1(KK, 2, 0X6D) +CREATE_TEST1(LL, 2, 0X6F) +CREATE_TEST1(MM, 2, 0XAB) +CREATE_TEST1(NN, 2, 0X91) +CREATE_TEST0(K, 2, 0X0BB) +CREATE_TEST0(L, 2, 0X0DF) +CREATE_TEST0(M, 2, 0X0F0) +CREATE_TEST0(N, 2, 0X400) + +void testIdentify() { + testA(); + testB(); + testC(); + testD(); + testE(); + testF(); + testG(); + testH(); + testI(); + testAA(); + testBB(); + testAAA(); + testBBB(); + testCCC(); + testDDD(); + testDD(); + testEE(); + testFF(); + testGG(); + testHH(); + testII(); + testJJ(); + testKK(); + testLL(); + testMM(); + testNN(); + testK(); + testL(); + testM(); + testN(); +} diff --git a/test/tests/src/message.cpp b/test/tests/src/message.cpp new file mode 100644 index 0000000..5e6a532 --- /dev/null +++ b/test/tests/src/message.cpp @@ -0,0 +1,109 @@ +#include +#include +#include "pack_unpack.hpp" +#include "test.hpp" + +using namespace CANlib; +using namespace map1; +using namespace map2; +using namespace J; +using namespace CC; + +#define CREATE_TEST(Name, BITMASK) \ + void test##Name() { \ + for (int cs = 0; cs < 10000; ++cs) { \ + Name Name##_copy; \ + Frame f0; \ + for (int i = 0; i < 8; ++i) { \ + f0.data[i] = distribution(generator); \ + } \ + uint64_t bitstring0; \ + to_bitstring((uint8_t *)f0.data, &bitstring0); \ + bitstring0 &= BITMASK; \ + Name##_copy.pack(f0); \ + Frame f1; \ + memset(f1.data, 0, sizeof(f1.data)); \ + Name##_copy.unpack(f1); \ + uint64_t bitstring1; \ + to_bitstring((uint8_t *)f1.data, &bitstring1); \ + bitstring1 &= BITMASK; \ + /** \ + * The frame should be the same before \ + * and after we pack and unpack it \ + **/ \ + assert(bitstring0 == bitstring1); \ + } \ + } + +CREATE_TEST(A, (~ZEROES_MASK(7, 49))) +CREATE_TEST(B, (~ZEROES_MASK(5, 59))) +CREATE_TEST(C, (~ZEROES_MASK(0, 56))) +CREATE_TEST(D, + (~ZEROES_MASK(0, 1)) | (~ZEROES_MASK(2, 1)) | (~ZEROES_MASK(3, 1)) | + (~ZEROES_MASK(4, 1)) | (~ZEROES_MASK(5, 1)) | (~ZEROES_MASK(6, 1)) | + (~ZEROES_MASK(7, 1)) | (~ZEROES_MASK(9, 1)) | (~ZEROES_MASK(16, 1)) | + (~ZEROES_MASK(17, 1))) +CREATE_TEST(E, (~ZEROES_MASK(0, 64))) +CREATE_TEST(F, + (~ZEROES_MASK(0, 10)) | (~ZEROES_MASK(10, 8)) | (~ZEROES_MASK(18, 10)) | + (~ZEROES_MASK(28, 8)) | (~ZEROES_MASK(36, 10)) | (~ZEROES_MASK(46, 8)) | + (~ZEROES_MASK(54, 10))) +CREATE_TEST(G, (~ZEROES_MASK(0, 60))) +CREATE_TEST(H, (~ZEROES_MASK(0, 64))) +CREATE_TEST(I, (~ZEROES_MASK(0, 64))) +CREATE_TEST(AA, (~ZEROES_MASK(23, 1)) | (~ZEROES_MASK(39, 1))) +CREATE_TEST(BB, (~ZEROES_MASK(23, 1)) | (~ZEROES_MASK(39, 1))) +CREATE_TEST(AAA, (~ZEROES_MASK(23, 1) | (~ZEROES_MASK(32, 16)))) +CREATE_TEST(BBB, (~ZEROES_MASK(23, 1)) | (~ZEROES_MASK(32, 16))) +CREATE_TEST(CCC, (~ZEROES_MASK(23, 1)) | (~ZEROES_MASK(32, 16))) +CREATE_TEST(DDD, (~ZEROES_MASK(23, 1) | (~ZEROES_MASK(32, 16)))) +CREATE_TEST(DD, (~ZEROES_MASK(23, 1) | (~ZEROES_MASK(32, 16)))) +CREATE_TEST(EE, (~ZEROES_MASK(23, 1) | (~ZEROES_MASK(32, 16)))) +CREATE_TEST(FF, (~ZEROES_MASK(23, 1) | (~ZEROES_MASK(32, 16)))) +CREATE_TEST(GG, (~ZEROES_MASK(23, 1) | (~ZEROES_MASK(32, 16)))) +CREATE_TEST(HH, (~ZEROES_MASK(23, 1) | (~ZEROES_MASK(32, 16)))) +CREATE_TEST(II, (~ZEROES_MASK(23, 1) | (~ZEROES_MASK(32, 16)))) +CREATE_TEST(JJ, (~ZEROES_MASK(23, 1) | (~ZEROES_MASK(32, 16)))) +CREATE_TEST(KK, (~ZEROES_MASK(23, 1) | (~ZEROES_MASK(32, 16)))) +CREATE_TEST(LL, (~ZEROES_MASK(23, 1) | (~ZEROES_MASK(32, 16)))) +CREATE_TEST(MM, (~ZEROES_MASK(23, 1) | (~ZEROES_MASK(39, 1)))) +CREATE_TEST(NN, (~ZEROES_MASK(23, 1) | (~ZEROES_MASK(39, 1)))) +CREATE_TEST(K, (~ZEROES_MASK(0, 32))) +CREATE_TEST(L, (~ZEROES_MASK(0, 25))) +CREATE_TEST(M, (~ZEROES_MASK(0, 16))) +CREATE_TEST(N, + ((~ZEROES_MASK(3, 1)) | (~ZEROES_MASK(4, 3)) | (~ZEROES_MASK(8, 1)) | + (~ZEROES_MASK(9, 1)) | (~ZEROES_MASK(10, 1)))) + +void testPackUnpack() { + testA(); + testB(); + testC(); + testD(); + testE(); + testF(); + testG(); + testH(); + testI(); + testAA(); + testBB(); + testAAA(); + testBBB(); + testCCC(); + testDDD(); + testDD(); + testEE(); + testFF(); + testGG(); + testHH(); + testII(); + testJJ(); + testKK(); + testLL(); + testMM(); + testNN(); + testK(); + testL(); + testM(); + testN(); +} diff --git a/test/tests/src/send.cpp b/test/tests/src/send.cpp new file mode 100644 index 0000000..9af941e --- /dev/null +++ b/test/tests/src/send.cpp @@ -0,0 +1,125 @@ +#include +#include +#include +#include +#include "pack_unpack.hpp" +#include "test.hpp" + +using namespace CANlib; +using namespace map1; +using namespace map2; +using namespace J; +using namespace CC; +using namespace std::chrono_literals; + +extern TestCAN can1; +extern TestCAN can2; +extern TestCAN can3; + +#define CREATE_TEST(Name, can, BITMASK) \ + static void testSend##Name() { \ + Name Name##_copy; \ + Frame f0; \ + for (int i = 0; i < 8; ++i) { \ + f0.data[i] = distribution(generator); \ + } \ + uint64_t bitstring0; \ + to_bitstring((uint8_t *)f0.data, &bitstring0); \ + bitstring0 &= BITMASK; \ + Name##_copy.pack(f0); \ + can.clear(); \ + if (Name##_copy.get_period() != 0ms) { \ + auto starting_time = Clock::now(); \ + auto tmp = starting_time; \ + while (Clock::now() - starting_time <= Name##_copy.get_period() + 5ms) { \ + send_period(tmp, &Name##_copy); \ + } \ + } else { \ + send(&Name##_copy); \ + } \ + uint64_t bitstring1; \ + /* Expecting to receive the message exactly once */ \ + assert(can.framesReceived() == 1); \ + Frame f1 = can.topFrame(); \ + to_bitstring((uint8_t *)f1.data, &bitstring1); \ + bitstring1 &= BITMASK; \ + assert(bitstring0 == bitstring1); \ + } + +CREATE_TEST(A, MAP1_CAN, (~ZEROES_MASK(7, 49))) +CREATE_TEST(B, MAP1_CAN, (~ZEROES_MASK(5, 59))) +CREATE_TEST(C, MAP1_CAN, (~ZEROES_MASK(0, 56))) +CREATE_TEST(D, + MAP1_CAN, + (~ZEROES_MASK(0, 1)) | (~ZEROES_MASK(2, 1)) | (~ZEROES_MASK(3, 1)) | + (~ZEROES_MASK(4, 1)) | (~ZEROES_MASK(5, 1)) | (~ZEROES_MASK(6, 1)) | + (~ZEROES_MASK(7, 1)) | (~ZEROES_MASK(9, 1)) | (~ZEROES_MASK(16, 1)) | + (~ZEROES_MASK(17, 1))) +CREATE_TEST(E, MAP1_CAN, (~ZEROES_MASK(0, 64))) +CREATE_TEST(F, + MAP2_CAN, + (~ZEROES_MASK(0, 10)) | (~ZEROES_MASK(10, 8)) | (~ZEROES_MASK(18, 10)) | + (~ZEROES_MASK(28, 8)) | (~ZEROES_MASK(36, 10)) | (~ZEROES_MASK(46, 8)) | + (~ZEROES_MASK(54, 10))) +CREATE_TEST(G, MAP2_CAN, (~ZEROES_MASK(0, 60))) +CREATE_TEST(H, MAP2_CAN, (~ZEROES_MASK(0, 64))) +CREATE_TEST(I, MAP2_CAN, (~ZEROES_MASK(0, 64))) +CREATE_TEST(AA, MAP2_CAN, (~ZEROES_MASK(23, 1)) | (~ZEROES_MASK(39, 1))) +CREATE_TEST(BB, MAP2_CAN, (~ZEROES_MASK(23, 1)) | (~ZEROES_MASK(39, 1))) +CREATE_TEST(AAA, MAP2_CAN, (~ZEROES_MASK(23, 1) | (~ZEROES_MASK(32, 16)))) +CREATE_TEST(BBB, MAP2_CAN, (~ZEROES_MASK(23, 1)) | (~ZEROES_MASK(32, 16))) +CREATE_TEST(CCC, MAP2_CAN, (~ZEROES_MASK(23, 1)) | (~ZEROES_MASK(32, 16))) +CREATE_TEST(DDD, MAP2_CAN, (~ZEROES_MASK(23, 1) | (~ZEROES_MASK(32, 16)))) +CREATE_TEST(DD, MAP2_CAN, (~ZEROES_MASK(23, 1) | (~ZEROES_MASK(32, 16)))) +CREATE_TEST(EE, MAP2_CAN, (~ZEROES_MASK(23, 1) | (~ZEROES_MASK(32, 16)))) +CREATE_TEST(FF, MAP2_CAN, (~ZEROES_MASK(23, 1) | (~ZEROES_MASK(32, 16)))) +CREATE_TEST(GG, MAP2_CAN, (~ZEROES_MASK(23, 1) | (~ZEROES_MASK(32, 16)))) +CREATE_TEST(HH, MAP2_CAN, (~ZEROES_MASK(23, 1) | (~ZEROES_MASK(32, 16)))) +CREATE_TEST(II, MAP2_CAN, (~ZEROES_MASK(23, 1) | (~ZEROES_MASK(32, 16)))) +CREATE_TEST(JJ, MAP2_CAN, (~ZEROES_MASK(23, 1) | (~ZEROES_MASK(32, 16)))) +CREATE_TEST(KK, MAP2_CAN, (~ZEROES_MASK(23, 1) | (~ZEROES_MASK(32, 16)))) +CREATE_TEST(LL, MAP2_CAN, (~ZEROES_MASK(23, 1) | (~ZEROES_MASK(32, 16)))) +CREATE_TEST(MM, MAP2_CAN, (~ZEROES_MASK(23, 1) | (~ZEROES_MASK(39, 1)))) +CREATE_TEST(NN, MAP2_CAN, (~ZEROES_MASK(23, 1) | (~ZEROES_MASK(39, 1)))) +CREATE_TEST(K, MAP2_CAN, (~ZEROES_MASK(0, 32))) +CREATE_TEST(L, MAP2_CAN, (~ZEROES_MASK(0, 25))) +CREATE_TEST(M, MAP2_CAN, (~ZEROES_MASK(0, 16))) +CREATE_TEST(N, + MAP2_CAN, + ((~ZEROES_MASK(3, 1)) | (~ZEROES_MASK(4, 3)) | (~ZEROES_MASK(8, 1)) | + (~ZEROES_MASK(9, 1)) | (~ZEROES_MASK(10, 1)))) + +void testSend() { + for (int cs = 0; cs < 10; cs++) { + testSendA(); + testSendB(); + testSendC(); + testSendD(); + testSendE(); + testSendF(); + testSendG(); + testSendH(); + testSendI(); + testSendAA(); + testSendBB(); + testSendAAA(); + testSendBBB(); + testSendCCC(); + testSendDDD(); + testSendDD(); + testSendEE(); + testSendFF(); + testSendGG(); + testSendHH(); + testSendII(); + testSendJJ(); + testSendKK(); + testSendLL(); + testSendMM(); + testSendNN(); + testSendK(); + testSendL(); + testSendM(); + testSendN(); + } +}