diff --git a/.github/workflows/generate_artefacts.yml b/.github/workflows/generate_artefacts.yml index d51065d..b46e69a 100644 --- a/.github/workflows/generate_artefacts.yml +++ b/.github/workflows/generate_artefacts.yml @@ -42,6 +42,7 @@ jobs: git config user.email "41898282+github-actions[bot]@users.noreply.github.com" git add -f artefacts/ + git add -f bindings/flutter/lib/src/trame_builder.dart git commit -m "Regenerate artefacts automatically [skip ci]" \ || echo "nothing to commit" diff --git a/.github/workflows/publish_to_pub.yml b/.github/workflows/publish_to_pub.yml new file mode 100644 index 0000000..2f71a8f --- /dev/null +++ b/.github/workflows/publish_to_pub.yml @@ -0,0 +1,41 @@ +name: Publish to Pub.dev + +on: + push: + tags: + - 'v[0-9]+.[0-9]+.[0-9]+' # Trigger on version tags + +jobs: + publish: + runs-on: ubuntu-latest + defaults: + run: + working-directory: bindings/flutter + + steps: + - uses: actions/checkout@v4 + + - uses: subosito/flutter-action@v2 + with: + flutter-version: '3.x' + channel: 'stable' + + - name: Install dependencies + run: flutter pub get + + - name: Verify formatting + run: dart format --output=none --set-exit-if-changed . + + - name: Analyze project + run: flutter analyze + + - name: Run tests + run: flutter test + + - name: Setup Pub Credentials + run: | + mkdir -p ~/.config/dart + echo '${{ secrets.PUB_CREDENTIALS_JSON }}' > ~/.config/dart/pub-credentials.json + + - name: Publish to Pub.dev + run: flutter pub publish --force diff --git a/Makefile b/Makefile index 1dc2793..8196cc6 100644 --- a/Makefile +++ b/Makefile @@ -54,6 +54,9 @@ artefacts/trames.json: trame-exporter .PHONY: primitives primitives: artefacts/trames.json @ python generators/generate_primitives.py + @ cp artefacts/flutter/trame_builder.dart bindings/flutter/lib/src/trame_builder.dart + @ $(LOG_TIME) "$(C_GREEN) CP $(C_PURPLE) trame_builder.dart $(C_RESET)" + .PHONY: doc diff --git a/artefacts/cpp/trame_builder.cpp b/artefacts/cpp/trame_builder.cpp index df096cb..fb1cfbf 100644 --- a/artefacts/cpp/trame_builder.cpp +++ b/artefacts/cpp/trame_builder.cpp @@ -10,6 +10,7 @@ */ inline std::string safety_stop() { std::ostringstream oss; + oss << ""; return oss.str(); } @@ -38,6 +39,7 @@ inline std::string start_firmware_upload(int size) { std::ostringstream oss; oss << "500x"; oss << size; + oss << ""; return oss.str(); } @@ -48,6 +50,7 @@ inline std::string start_trame_s(std::string trame_s_params) { std::ostringstream oss; oss << "0"; oss << trame_s_params; + oss << ""; return oss.str(); } @@ -112,6 +115,7 @@ inline std::string set_line_threshold_value(int threshold) { std::ostringstream oss; oss << "13t"; oss << threshold; + oss << ""; return oss.str(); } @@ -234,13 +238,14 @@ inline std::string set_led_color_circle(std::string r, std::string g, std::strin oss << g; oss << "b"; oss << b; + oss << ""; return oss.str(); } /** - * Primitive for set_led_color_circle + * Primitive for set_led_color_circle_2 */ -inline std::string set_led_color_circle(std::string r, std::string g, std::string b) { +inline std::string set_led_color_circle_2(std::string r, std::string g, std::string b) { std::ostringstream oss; oss << "512r"; oss << r; @@ -248,6 +253,7 @@ inline std::string set_led_color_circle(std::string r, std::string g, std::strin oss << g; oss << "b"; oss << b; + oss << ""; return oss.str(); } @@ -258,6 +264,7 @@ inline std::string set_led_shape(std::string shape) { std::ostringstream oss; oss << "52v"; oss << shape; + oss << ""; return oss.str(); } @@ -270,6 +277,7 @@ inline std::string set_led_mode(std::string mode, int nb_loop) { oss << mode; oss << "/"; oss << nb_loop; + oss << ""; return oss.str(); } @@ -280,6 +288,7 @@ inline std::string set_led_captor(int brightness) { std::ostringstream oss; oss << "54l"; oss << brightness; + oss << ""; return oss.str(); } @@ -298,6 +307,7 @@ inline std::string set_led_single(std::string type, int id, int red, int green, oss << green; oss << "b"; oss << blue; + oss << ""; return oss.str(); } @@ -312,6 +322,7 @@ inline std::string display_word(std::string word, int delay, int nb_loops) { oss << delay; oss << "/"; oss << nb_loops; + oss << ""; return oss.str(); } @@ -340,6 +351,7 @@ inline std::string run_command_motor(std::string params) { std::ostringstream oss; oss << "a"; oss << params; + oss << ""; return oss.str(); } @@ -352,6 +364,7 @@ inline std::string ping_motor(int ping_status_0, int ping_status_1) { oss << ping_status_0; oss << "s"; oss << ping_status_1; + oss << ""; return oss.str(); } @@ -366,6 +379,7 @@ inline std::string drive_single_motor_speed(int motor_index, int acc, int speed) oss << acc; oss << "v"; oss << speed; + oss << ""; return oss.str(); } @@ -376,6 +390,7 @@ inline std::string get_single_motor_speed(int motor_index) { std::ostringstream oss; oss << "611i"; oss << motor_index; + oss << ""; return oss.str(); } @@ -392,6 +407,7 @@ inline std::string drive_single_motor_angle(int motor_index, int acc, int vel, i oss << vel; oss << "p"; oss << position; + oss << ""; return oss.str(); } @@ -402,6 +418,7 @@ inline std::string get_single_motor_angle(int motor_index) { std::ostringstream oss; oss << "621i"; oss << motor_index; + oss << ""; return oss.str(); } @@ -412,6 +429,7 @@ inline std::string get_single_motor_temp(int motor_index) { std::ostringstream oss; oss << "63i"; oss << motor_index; + oss << ""; return oss.str(); } @@ -422,6 +440,7 @@ inline std::string get_single_motor_volt(int motor_index) { std::ostringstream oss; oss << "64i"; oss << motor_index; + oss << ""; return oss.str(); } @@ -432,6 +451,7 @@ inline std::string get_single_motor_load(int motor_index) { std::ostringstream oss; oss << "65i"; oss << motor_index; + oss << ""; return oss.str(); } @@ -442,6 +462,7 @@ inline std::string get_single_motor_current(int motor_index) { std::ostringstream oss; oss << "66i"; oss << motor_index; + oss << ""; return oss.str(); } @@ -452,6 +473,7 @@ inline std::string get_single_motor_move(int motor_index) { std::ostringstream oss; oss << "67i"; oss << motor_index; + oss << ""; return oss.str(); } @@ -462,6 +484,7 @@ inline std::string set_motors_ilo_acc(int acc) { std::ostringstream oss; oss << "680a"; oss << acc; + oss << ""; return oss.str(); } @@ -481,6 +504,7 @@ inline std::string set_tempo_pos(int tempo_pos) { std::ostringstream oss; oss << "690t"; oss << tempo_pos; + oss << ""; return oss.str(); } @@ -504,6 +528,7 @@ inline std::string set_pid(int Kp, int Ki, int Kd) { oss << Ki; oss << "d"; oss << Kd; + oss << ""; return oss.str(); } @@ -523,6 +548,7 @@ inline std::string check_auto_mode(int current_auto_mode) { std::ostringstream oss; oss << "80"; oss << current_auto_mode; + oss << ""; return oss.str(); } @@ -535,6 +561,7 @@ inline std::string set_wifi_credentials(std::string ssid, std::string password) oss << ssid; oss << "{|||}"; oss << password; + oss << ""; return oss.str(); } @@ -572,6 +599,7 @@ inline std::string set_name(std::string name) { std::ostringstream oss; oss << "94n"; oss << name; + oss << ""; return oss.str(); } @@ -582,6 +610,7 @@ inline std::string set_server_status(int status) { std::ostringstream oss; oss << "95s"; oss << status; + oss << ""; return oss.str(); } @@ -628,6 +657,7 @@ inline std::string set_debug_state(int state) { std::ostringstream oss; oss << "103s"; oss << state; + oss << ""; return oss.str(); } @@ -656,6 +686,7 @@ inline std::string set_manufacturing_date(std::string date) { std::ostringstream oss; oss << "121s"; oss << date; + oss << ""; return oss.str(); } @@ -675,6 +706,7 @@ inline std::string set_first_use_date(std::string date) { std::ostringstream oss; oss << "131s"; oss << date; + oss << ""; return oss.str(); } @@ -694,6 +726,7 @@ inline std::string set_product_version(std::string version) { std::ostringstream oss; oss << "141s"; oss << version; + oss << ""; return oss.str(); } @@ -713,6 +746,7 @@ inline std::string set_product_id(std::string product_id) { std::ostringstream oss; oss << "151s"; oss << product_id; + oss << ""; return oss.str(); } @@ -732,6 +766,7 @@ inline std::string set_review_date(std::string date) { std::ostringstream oss; oss << "161s"; oss << date; + oss << ""; return oss.str(); } @@ -742,6 +777,7 @@ inline std::string set_auto_setup(int auto_setup) { std::ostringstream oss; oss << "170a"; oss << auto_setup; + oss << ""; return oss.str(); } diff --git a/artefacts/docs/trames.rst b/artefacts/docs/trames.rst index 3eff263..cda16aa 100644 --- a/artefacts/docs/trames.rst +++ b/artefacts/docs/trames.rst @@ -62,7 +62,7 @@ This table is generated automatically from ``trame.json``. - ``<50>`` * - ``set_led_color_circle`` - ``<511r[r]g[g]b[b]>`` - * - ``set_led_color_circle`` + * - ``set_led_color_circle_2`` - ``<512r[r]g[g]b[b]>`` * - ``set_led_shape`` - ``<52v[shape]>`` diff --git a/artefacts/flutter/trame_builder.dart b/artefacts/flutter/trame_builder.dart new file mode 100644 index 0000000..ba461df --- /dev/null +++ b/artefacts/flutter/trame_builder.dart @@ -0,0 +1,560 @@ +// Auto-generated primitives for ilo communication +// This file is generated. Do not edit manually. + +class TrameBuilder { + + /// Primitive for safety_stop + static String safety_stop() { + StringBuffer buffer = StringBuffer(); + buffer.write(""); + return buffer.toString(); + } + /// Primitive for handshake_ilo + static String handshake_ilo() { + StringBuffer buffer = StringBuffer(); + buffer.write("ilo"); + return buffer.toString(); + } + /// Primitive for get_robot_version + static String get_robot_version() { + StringBuffer buffer = StringBuffer(); + buffer.write("500y"); + return buffer.toString(); + } + /// Primitive for start_firmware_upload + static String start_firmware_upload(int size) { + StringBuffer buffer = StringBuffer(); + buffer.write("500x"); + buffer.write(size); + buffer.write(""); + return buffer.toString(); + } + /// Primitive for start_trame_s + static String start_trame_s(String trame_s_params) { + StringBuffer buffer = StringBuffer(); + buffer.write("0"); + buffer.write(trame_s_params); + buffer.write(""); + return buffer.toString(); + } + /// Primitive for stop_tasks + static String stop_tasks() { + StringBuffer buffer = StringBuffer(); + buffer.write("00"); + return buffer.toString(); + } + /// Primitive for get_color_rgb_center + static String get_color_rgb_center() { + StringBuffer buffer = StringBuffer(); + buffer.write("10c"); + return buffer.toString(); + } + /// Primitive for get_color_rgb_left + static String get_color_rgb_left() { + StringBuffer buffer = StringBuffer(); + buffer.write("10l"); + return buffer.toString(); + } + /// Primitive for get_color_rgb_right + static String get_color_rgb_right() { + StringBuffer buffer = StringBuffer(); + buffer.write("10d"); + return buffer.toString(); + } + /// Primitive for get_color_clear + static String get_color_clear() { + StringBuffer buffer = StringBuffer(); + buffer.write("11"); + return buffer.toString(); + } + /// Primitive for get_line + static String get_line() { + StringBuffer buffer = StringBuffer(); + buffer.write("12"); + return buffer.toString(); + } + /// Primitive for set_line_threshold_value + static String set_line_threshold_value(int threshold) { + StringBuffer buffer = StringBuffer(); + buffer.write("13t"); + buffer.write(threshold); + buffer.write(""); + return buffer.toString(); + } + /// Primitive for get_line_threshold_value + static String get_line_threshold_value() { + StringBuffer buffer = StringBuffer(); + buffer.write("14"); + return buffer.toString(); + } + /// Primitive for get_accessory_status + static String get_accessory_status() { + StringBuffer buffer = StringBuffer(); + buffer.write("15"); + return buffer.toString(); + } + /// Primitive for get_sensor_distance + static String get_sensor_distance() { + StringBuffer buffer = StringBuffer(); + buffer.write("20"); + return buffer.toString(); + } + /// Primitive for get_distance_front + static String get_distance_front() { + StringBuffer buffer = StringBuffer(); + buffer.write("21"); + return buffer.toString(); + } + /// Primitive for get_distance_right + static String get_distance_right() { + StringBuffer buffer = StringBuffer(); + buffer.write("22"); + return buffer.toString(); + } + /// Primitive for get_distance_back + static String get_distance_back() { + StringBuffer buffer = StringBuffer(); + buffer.write("23"); + return buffer.toString(); + } + /// Primitive for get_distance_left + static String get_distance_left() { + StringBuffer buffer = StringBuffer(); + buffer.write("24"); + return buffer.toString(); + } + /// Primitive for get_imu_info + static String get_imu_info() { + StringBuffer buffer = StringBuffer(); + buffer.write("30"); + return buffer.toString(); + } + /// Primitive for reset_angle + static String reset_angle() { + StringBuffer buffer = StringBuffer(); + buffer.write("31"); + return buffer.toString(); + } + /// Primitive for get_raw_imu + static String get_raw_imu() { + StringBuffer buffer = StringBuffer(); + buffer.write("32"); + return buffer.toString(); + } + /// Primitive for get_battery_info + static String get_battery_info() { + StringBuffer buffer = StringBuffer(); + buffer.write("40"); + return buffer.toString(); + } + /// Primitive for get_led_color + static String get_led_color() { + StringBuffer buffer = StringBuffer(); + buffer.write("50"); + return buffer.toString(); + } + /// Primitive for set_led_color_circle + static String set_led_color_circle(String r, String g, String b) { + StringBuffer buffer = StringBuffer(); + buffer.write("511r"); + buffer.write(r); + buffer.write("g"); + buffer.write(g); + buffer.write("b"); + buffer.write(b); + buffer.write(""); + return buffer.toString(); + } + /// Primitive for set_led_color_circle_2 + static String set_led_color_circle_2(String r, String g, String b) { + StringBuffer buffer = StringBuffer(); + buffer.write("512r"); + buffer.write(r); + buffer.write("g"); + buffer.write(g); + buffer.write("b"); + buffer.write(b); + buffer.write(""); + return buffer.toString(); + } + /// Primitive for set_led_shape + static String set_led_shape(String shape) { + StringBuffer buffer = StringBuffer(); + buffer.write("52v"); + buffer.write(shape); + buffer.write(""); + return buffer.toString(); + } + /// Primitive for set_led_mode + static String set_led_mode(String mode, int nb_loop) { + StringBuffer buffer = StringBuffer(); + buffer.write("53"); + buffer.write(mode); + buffer.write("/"); + buffer.write(nb_loop); + buffer.write(""); + return buffer.toString(); + } + /// Primitive for set_led_captor + static String set_led_captor(int brightness) { + StringBuffer buffer = StringBuffer(); + buffer.write("54l"); + buffer.write(brightness); + buffer.write(""); + return buffer.toString(); + } + /// Primitive for set_led_single + static String set_led_single(String type, int id, int red, int green, int blue) { + StringBuffer buffer = StringBuffer(); + buffer.write("55t"); + buffer.write(type); + buffer.write("d"); + buffer.write(id); + buffer.write("r"); + buffer.write(red); + buffer.write("g"); + buffer.write(green); + buffer.write("b"); + buffer.write(blue); + buffer.write(""); + return buffer.toString(); + } + /// Primitive for display_word + static String display_word(String word, int delay, int nb_loops) { + StringBuffer buffer = StringBuffer(); + buffer.write("56w"); + buffer.write(word); + buffer.write("d"); + buffer.write(delay); + buffer.write("/"); + buffer.write(nb_loops); + buffer.write(""); + return buffer.toString(); + } + /// Primitive for display_word_slide + static String display_word_slide() { + StringBuffer buffer = StringBuffer(); + buffer.write("57"); + return buffer.toString(); + } + /// Primitive for set_animation_flag_false + static String set_animation_flag_false() { + StringBuffer buffer = StringBuffer(); + buffer.write("58"); + return buffer.toString(); + } + /// Primitive for run_command_motor + static String run_command_motor(String params) { + StringBuffer buffer = StringBuffer(); + buffer.write("a"); + buffer.write(params); + buffer.write(""); + return buffer.toString(); + } + /// Primitive for ping_motor + static String ping_motor(int ping_status_0, int ping_status_1) { + StringBuffer buffer = StringBuffer(); + buffer.write("60i"); + buffer.write(ping_status_0); + buffer.write("s"); + buffer.write(ping_status_1); + buffer.write(""); + return buffer.toString(); + } + /// Primitive for drive_single_motor_speed + static String drive_single_motor_speed(int motor_index, int acc, int speed) { + StringBuffer buffer = StringBuffer(); + buffer.write("610i"); + buffer.write(motor_index); + buffer.write("a"); + buffer.write(acc); + buffer.write("v"); + buffer.write(speed); + buffer.write(""); + return buffer.toString(); + } + /// Primitive for get_single_motor_speed + static String get_single_motor_speed(int motor_index) { + StringBuffer buffer = StringBuffer(); + buffer.write("611i"); + buffer.write(motor_index); + buffer.write(""); + return buffer.toString(); + } + /// Primitive for drive_single_motor_angle + static String drive_single_motor_angle(int motor_index, int acc, int vel, int position) { + StringBuffer buffer = StringBuffer(); + buffer.write("620i"); + buffer.write(motor_index); + buffer.write("a"); + buffer.write(acc); + buffer.write("v"); + buffer.write(vel); + buffer.write("p"); + buffer.write(position); + buffer.write(""); + return buffer.toString(); + } + /// Primitive for get_single_motor_angle + static String get_single_motor_angle(int motor_index) { + StringBuffer buffer = StringBuffer(); + buffer.write("621i"); + buffer.write(motor_index); + buffer.write(""); + return buffer.toString(); + } + /// Primitive for get_single_motor_temp + static String get_single_motor_temp(int motor_index) { + StringBuffer buffer = StringBuffer(); + buffer.write("63i"); + buffer.write(motor_index); + buffer.write(""); + return buffer.toString(); + } + /// Primitive for get_single_motor_volt + static String get_single_motor_volt(int motor_index) { + StringBuffer buffer = StringBuffer(); + buffer.write("64i"); + buffer.write(motor_index); + buffer.write(""); + return buffer.toString(); + } + /// Primitive for get_single_motor_load + static String get_single_motor_load(int motor_index) { + StringBuffer buffer = StringBuffer(); + buffer.write("65i"); + buffer.write(motor_index); + buffer.write(""); + return buffer.toString(); + } + /// Primitive for get_single_motor_current + static String get_single_motor_current(int motor_index) { + StringBuffer buffer = StringBuffer(); + buffer.write("66i"); + buffer.write(motor_index); + buffer.write(""); + return buffer.toString(); + } + /// Primitive for get_single_motor_move + static String get_single_motor_move(int motor_index) { + StringBuffer buffer = StringBuffer(); + buffer.write("67i"); + buffer.write(motor_index); + buffer.write(""); + return buffer.toString(); + } + /// Primitive for set_motors_ilo_acc + static String set_motors_ilo_acc(int acc) { + StringBuffer buffer = StringBuffer(); + buffer.write("680a"); + buffer.write(acc); + buffer.write(""); + return buffer.toString(); + } + /// Primitive for get_motors_ilo_acc + static String get_motors_ilo_acc() { + StringBuffer buffer = StringBuffer(); + buffer.write("681"); + return buffer.toString(); + } + /// Primitive for set_tempo_pos + static String set_tempo_pos(int tempo_pos) { + StringBuffer buffer = StringBuffer(); + buffer.write("690t"); + buffer.write(tempo_pos); + buffer.write(""); + return buffer.toString(); + } + /// Primitive for get_tempo_pos + static String get_tempo_pos() { + StringBuffer buffer = StringBuffer(); + buffer.write("691"); + return buffer.toString(); + } + /// Primitive for set_pid + static String set_pid(int Kp, int Ki, int Kd) { + StringBuffer buffer = StringBuffer(); + buffer.write("70p"); + buffer.write(Kp); + buffer.write("i"); + buffer.write(Ki); + buffer.write("d"); + buffer.write(Kd); + buffer.write(""); + return buffer.toString(); + } + /// Primitive for get_pid + static String get_pid() { + StringBuffer buffer = StringBuffer(); + buffer.write("71"); + return buffer.toString(); + } + /// Primitive for check_auto_mode + static String check_auto_mode(int current_auto_mode) { + StringBuffer buffer = StringBuffer(); + buffer.write("80"); + buffer.write(current_auto_mode); + buffer.write(""); + return buffer.toString(); + } + /// Primitive for set_wifi_credentials + static String set_wifi_credentials(String ssid, String password) { + StringBuffer buffer = StringBuffer(); + buffer.write("90"); + buffer.write(ssid); + buffer.write("{|||}"); + buffer.write(password); + buffer.write(""); + return buffer.toString(); + } + /// Primitive for get_wifi_credentials + static String get_wifi_credentials() { + StringBuffer buffer = StringBuffer(); + buffer.write("92"); + return buffer.toString(); + } + /// Primitive for get_hostname + static String get_hostname() { + StringBuffer buffer = StringBuffer(); + buffer.write("93"); + return buffer.toString(); + } + /// Primitive for get_hostname_legacy + static String get_hostname_legacy() { + StringBuffer buffer = StringBuffer(); + buffer.write("930"); + return buffer.toString(); + } + /// Primitive for set_name + static String set_name(String name) { + StringBuffer buffer = StringBuffer(); + buffer.write("94n"); + buffer.write(name); + buffer.write(""); + return buffer.toString(); + } + /// Primitive for set_server_status + static String set_server_status(int status) { + StringBuffer buffer = StringBuffer(); + buffer.write("95s"); + buffer.write(status); + buffer.write(""); + return buffer.toString(); + } + /// Primitive for get_server_status + static String get_server_status() { + StringBuffer buffer = StringBuffer(); + buffer.write("96"); + return buffer.toString(); + } + /// Primitive for get_accessory_data + static String get_accessory_data() { + StringBuffer buffer = StringBuffer(); + buffer.write("100"); + return buffer.toString(); + } + /// Primitive for get_accessory_info + static String get_accessory_info() { + StringBuffer buffer = StringBuffer(); + buffer.write("101"); + return buffer.toString(); + } + /// Primitive for very_very_usefull + static String very_very_usefull() { + StringBuffer buffer = StringBuffer(); + buffer.write("102"); + return buffer.toString(); + } + /// Primitive for set_debug_state + static String set_debug_state(int state) { + StringBuffer buffer = StringBuffer(); + buffer.write("103s"); + buffer.write(state); + buffer.write(""); + return buffer.toString(); + } + /// Primitive for start_diag + static String start_diag() { + StringBuffer buffer = StringBuffer(); + buffer.write("110"); + return buffer.toString(); + } + /// Primitive for get_manufacturing_date + static String get_manufacturing_date() { + StringBuffer buffer = StringBuffer(); + buffer.write("120"); + return buffer.toString(); + } + /// Primitive for set_manufacturing_date + static String set_manufacturing_date(String date) { + StringBuffer buffer = StringBuffer(); + buffer.write("121s"); + buffer.write(date); + buffer.write(""); + return buffer.toString(); + } + /// Primitive for get_first_use_date + static String get_first_use_date() { + StringBuffer buffer = StringBuffer(); + buffer.write("130"); + return buffer.toString(); + } + /// Primitive for set_first_use_date + static String set_first_use_date(String date) { + StringBuffer buffer = StringBuffer(); + buffer.write("131s"); + buffer.write(date); + buffer.write(""); + return buffer.toString(); + } + /// Primitive for get_product_version + static String get_product_version() { + StringBuffer buffer = StringBuffer(); + buffer.write("140"); + return buffer.toString(); + } + /// Primitive for set_product_version + static String set_product_version(String version) { + StringBuffer buffer = StringBuffer(); + buffer.write("141s"); + buffer.write(version); + buffer.write(""); + return buffer.toString(); + } + /// Primitive for get_product_id + static String get_product_id() { + StringBuffer buffer = StringBuffer(); + buffer.write("150"); + return buffer.toString(); + } + /// Primitive for set_product_id + static String set_product_id(String product_id) { + StringBuffer buffer = StringBuffer(); + buffer.write("151s"); + buffer.write(product_id); + buffer.write(""); + return buffer.toString(); + } + /// Primitive for get_review_date + static String get_review_date() { + StringBuffer buffer = StringBuffer(); + buffer.write("160"); + return buffer.toString(); + } + /// Primitive for set_review_date + static String set_review_date(String date) { + StringBuffer buffer = StringBuffer(); + buffer.write("161s"); + buffer.write(date); + buffer.write(""); + return buffer.toString(); + } + /// Primitive for set_auto_setup + static String set_auto_setup(int auto_setup) { + StringBuffer buffer = StringBuffer(); + buffer.write("170a"); + buffer.write(auto_setup); + buffer.write(""); + return buffer.toString(); + } +} \ No newline at end of file diff --git a/artefacts/python/trame_builder.py b/artefacts/python/trame_builder.py index 28e4d0a..0c84709 100644 --- a/artefacts/python/trame_builder.py +++ b/artefacts/python/trame_builder.py @@ -158,8 +158,8 @@ def set_led_color_circle(r: str, g: str, b: str) -> str: return f"511r{r}g{g}b{b}" @_typecheck -def set_led_color_circle(r: str, g: str, b: str) -> str: - """Primitive for set_led_color_circle""" +def set_led_color_circle_2(r: str, g: str, b: str) -> str: + """Primitive for set_led_color_circle_2""" return f"512r{r}g{g}b{b}" @_typecheck @@ -429,7 +429,7 @@ def set_auto_setup(auto_setup: int) -> str: "get_battery_info", "get_led_color", "set_led_color_circle", - "set_led_color_circle", + "set_led_color_circle_2", "set_led_shape", "set_led_mode", "set_led_captor", diff --git a/artefacts/python/trame_builder.rst b/artefacts/python/trame_builder.rst index fb1daa7..5542930 100644 --- a/artefacts/python/trame_builder.rst +++ b/artefacts/python/trame_builder.rst @@ -180,9 +180,9 @@ This section documents the generated Python primitives. **Returns:** ``str`` — the encoded trame. -.. py:function:: set_led_color_circle(r: str, g: str, b: str) +.. py:function:: set_led_color_circle_2(r: str, g: str, b: str) - Primitive for set_led_color_circle + Primitive for set_led_color_circle_2 **Returns:** ``str`` — the encoded trame. diff --git a/bindings/flutter/.gitignore b/bindings/flutter/.gitignore new file mode 100644 index 0000000..0fa6b67 --- /dev/null +++ b/bindings/flutter/.gitignore @@ -0,0 +1,46 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# The .vscode folder contains launch configuration and tasks you configure in +# VS Code which you may wish to be included in version control, so this line +# is commented out by default. +#.vscode/ + +# Flutter/Dart/Pub related +**/doc/api/ +**/ios/Flutter/.last_build_id +.dart_tool/ +.flutter-plugins +.flutter-plugins-dependencies +.packages +.pub-cache/ +.pub/ +/build/ + +# Web related +lib/generated_plugin_registrant.dart + +# Symbolication related +app.*.symbols + +# Obfuscation related +app.*.map.json + +# Android Studio will place build artifacts here +/android/app/debug +/android/app/profile +/android/app/release diff --git a/bindings/flutter/lib/ilo_flutter.dart b/bindings/flutter/lib/ilo_flutter.dart new file mode 100644 index 0000000..739e3f3 --- /dev/null +++ b/bindings/flutter/lib/ilo_flutter.dart @@ -0,0 +1,7 @@ +library ilo_flutter; + +export 'src/ilo_lib.dart'; +export 'src/communication_service.dart'; +export 'src/robot_status.dart'; +export 'src/msg_parser.dart'; +export 'src/trame_builder.dart'; diff --git a/bindings/flutter/lib/src/communication_service.dart b/bindings/flutter/lib/src/communication_service.dart new file mode 100644 index 0000000..8f47a89 --- /dev/null +++ b/bindings/flutter/lib/src/communication_service.dart @@ -0,0 +1,137 @@ +import 'dart:async'; +import 'dart:io'; +import 'package:flutter/foundation.dart'; +import 'package:flutter_blue_plus/flutter_blue_plus.dart'; +import 'package:web_socket_channel/web_socket_channel.dart'; + +import 'msg_parser.dart'; +import 'robot_status.dart'; + +class CommunicationService { + final RobotStatus robotStatus; + late final MsgParser msgParser; + + CommunicationService(this.robotStatus) { + msgParser = MsgParser(robotStatus); + } + + WebSocket? robotSocket; + WebSocketChannel? _apiWebSocketChannel; + StreamSubscription? _apiWebSocketSubscription; + + BluetoothDevice? bluetoothDevice; + BluetoothCharacteristic? bluetoothCharacteristic; + StreamSubscription? bluetoothSubscription; + + bool isConnected = false; + bool useBluetooth = false; + bool useSerial = false; // Serial not fully implemented in this binding yet + + // Stream for raw messages if needed by external consumers + final StreamController _messageController = + StreamController.broadcast(); + Stream get messageStream => _messageController.stream; + + Future connectSocket(String ip, int port) async { + try { + debugPrint("[CommunicationService] Connecting to socket $ip:$port..."); + robotSocket = await WebSocket.connect('ws://$ip:$port'); + robotSocket!.listen( + (data) { + String message = data.toString(); + _handleMessage(message); + }, + onDone: () { + debugPrint("[CommunicationService] Socket closed"); + isConnected = false; + robotStatus.setConnected(false); + }, + onError: (error) { + debugPrint("[CommunicationService] Socket error: $error"); + isConnected = false; + }, + ); + isConnected = true; + debugPrint("[CommunicationService] Socket connected"); + return true; + } catch (e) { + debugPrint("[CommunicationService] Socket connection failed: $e"); + return false; + } + } + + Future connectBluetooth(BluetoothDevice device) async { + try { + debugPrint("[CommunicationService] Connecting to Bluetooth device ${device.platformName}..."); + await device.connect(); + bluetoothDevice = device; + + // Discover services + List services = await device.discoverServices(); + for (var service in services) { + // Look for specific service/characteristic if known, or just iterate + // Based on original code: service starts with 5f, char notify & read + if (service.uuid.toString().startsWith("5f")) { + for (var char in service.characteristics) { + if (char.properties.notify && char.properties.read) { + bluetoothCharacteristic = char; + await char.setNotifyValue(true); + bluetoothSubscription = char.lastValueStream.listen((value) { + String message = String.fromCharCodes(value); + _handleMessage(message); + }); + debugPrint("[CommunicationService] Bluetooth connected and listening"); + isConnected = true; + useBluetooth = true; + return true; + } + } + } + } + debugPrint("[CommunicationService] No suitable characteristic found"); + return false; + } catch (e) { + debugPrint("[CommunicationService] Bluetooth connection failed: $e"); + return false; + } + } + + void disconnect() { + robotSocket?.close(); + _apiWebSocketChannel?.sink.close(); + bluetoothSubscription?.cancel(); + bluetoothDevice?.disconnect(); + isConnected = false; + useBluetooth = false; + } + + void _handleMessage(String message) { + // debugPrint("[CommunicationService] Received: $message"); + _messageController.add(message); + msgParser.parse(message); + } + + Future send(String message, {bool debug = true}) async { + if (debug) { + // debugPrint("[CommunicationService] Sending: $message"); + } + + try { + if (useBluetooth && bluetoothCharacteristic != null) { + // Split message if too long? Bluetooth MTU is usually small. + // Original code didn't seem to split explicitly but maybe flutter_blue_plus handles it or messages are short. + await bluetoothCharacteristic!.write(message.codeUnits, withoutResponse: true); // or false + return true; + } else if (robotSocket != null && robotSocket!.readyState == WebSocket.open) { + robotSocket!.add(message); + return true; + } else if (_apiWebSocketChannel != null) { + _apiWebSocketChannel!.sink.add(message); // Simplified API send + return true; + } + } catch (e) { + debugPrint("[CommunicationService] Send failed: $e"); + } + return false; + } +} diff --git a/bindings/flutter/lib/src/ilo_lib.dart b/bindings/flutter/lib/src/ilo_lib.dart new file mode 100644 index 0000000..6befc15 --- /dev/null +++ b/bindings/flutter/lib/src/ilo_lib.dart @@ -0,0 +1,432 @@ +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'communication_service.dart'; +import 'robot_status.dart'; +import 'trame_builder.dart'; + +class IloLib { + final CommunicationService communicationService; + final RobotStatus robotStatus; + + IloLib(this.communicationService, this.robotStatus); + + Future _send(String content) { + return communicationService.send("<$content>"); + } + + Future startTrameS(int hertz, List paramList) async { + if (hertz > 100 || hertz < 1) { + debugPrint("[IloLib] Error: Hertz must be between 1 and 100"); + return false; + } + if (paramList.isEmpty) { + debugPrint("[IloLib] Error: Param list is empty"); + return false; + } + final validParams = { + "color_rgb_center", + "color_rgb_left", + "color_rgb_right", + "color_clear", + "line", + "distance", + "distance_front", + "distance_back", + "distance_right", + "distance_left", + "angle", + "raw_imu", + "battery", + "vel_motor_1", + "vel_motor_2", + "vel_motor_3", + "vel_motor_4", + "pos_motor_1", + "pos_motor_2", + "pos_motor_3", + "pos_motor_4", + "temp_motor_1", + "temp_motor_2", + "temp_motor_3", + "temp_motor_4", + "volt_motor_1", + "volt_motor_2", + "volt_motor_3", + "volt_motor_4", + "load_motor_1", + "load_motor_2", + "load_motor_3", + "load_motor_4", + "current_motor_1", + "current_motor_2", + "current_motor_3", + "current_motor_4", + "move_motor_1", + "move_motor_2", + "move_motor_3", + "move_motor_4", + }; + + final invalidParams = + paramList.where((item) => !validParams.contains(item)).toList(); + if (invalidParams.isNotEmpty) { + debugPrint("[IloLib] Error: Invalid params: $invalidParams"); + return false; + } + + String params = "h${hertz}z/"; + + if (paramList.contains("color_rgb_center")) params += "10c/"; + if (paramList.contains("color_rgb_left")) params += "10l/"; + if (paramList.contains("color_rgb_right")) params += "10r/"; + if (paramList.contains("color_clear")) params += "11/"; + if (paramList.contains("line")) params += "12/"; + if (paramList.contains("distance")) params += "20/"; + if (paramList.contains("distance_front")) params += "21/"; + if (paramList.contains("distance_back")) params += "22/"; + if (paramList.contains("distance_right")) params += "23/"; + if (paramList.contains("distance_left")) params += "24/"; + if (paramList.contains("angle")) params += "30/"; + if (paramList.contains("raw_imu")) params += "32/"; + if (paramList.contains("battery")) params += "40/"; + if (paramList.contains("vel_motor_1")) params += "611i1/"; + if (paramList.contains("vel_motor_2")) params += "611i2/"; + if (paramList.contains("vel_motor_3")) params += "611i3/"; + if (paramList.contains("vel_motor_4")) params += "611i4/"; + if (paramList.contains("pos_motor_1")) params += "621i1/"; + if (paramList.contains("pos_motor_2")) params += "621i2/"; + if (paramList.contains("pos_motor_3")) params += "621i3/"; + if (paramList.contains("pos_motor_4")) params += "621i4/"; + if (paramList.contains("temp_motor_1")) params += "63i1/"; + if (paramList.contains("temp_motor_2")) params += "63i2/"; + if (paramList.contains("temp_motor_3")) params += "63i3/"; + if (paramList.contains("temp_motor_4")) params += "63i4/"; + if (paramList.contains("volt_motor_1")) params += "64i1/"; + if (paramList.contains("volt_motor_2")) params += "64i2/"; + if (paramList.contains("volt_motor_3")) params += "64i3/"; + if (paramList.contains("volt_motor_4")) params += "64i4/"; + if (paramList.contains("load_motor_1")) params += "65i1/"; + if (paramList.contains("load_motor_2")) params += "65i2/"; + if (paramList.contains("load_motor_3")) params += "65i3/"; + if (paramList.contains("load_motor_4")) params += "65i4/"; + if (paramList.contains("current_motor_1")) params += "66i1/"; + if (paramList.contains("current_motor_2")) params += "66i2/"; + if (paramList.contains("current_motor_3")) params += "66i3/"; + if (paramList.contains("current_motor_4")) params += "66i4/"; + if (paramList.contains("move_motor_1")) params += "67i1/"; + if (paramList.contains("move_motor_2")) params += "67i2/"; + if (paramList.contains("move_motor_3")) params += "67i3/"; + if (paramList.contains("move_motor_4")) params += "67i4/"; + + String msg = TrameBuilder.start_trame_s(params); + debugPrint("[INFO] START TRAME S: <$msg>"); + return await _send(msg); + } + + Future stopTrameS() async { + return await _send(TrameBuilder.stop_tasks()); + } + + Future stopAll() async { + debugPrint("[IloLib] STOP <>."); + await communicationService.send("<>"); + await communicationService.send("<>"); + } + + Future getBottomColorCenter() async { + //debugPrint("[IloLib] Request Bottom color..."); + return await _send(TrameBuilder.get_color_rgb_center()); + } + + Future getBottomColorLeft() async { + debugPrint("[IloLib] Request Bottom color..."); + return await _send(TrameBuilder.get_color_rgb_left()); + } + + Future getBottomColorRight() async { + debugPrint("[IloLib] Request Bottom color..."); + return await _send(TrameBuilder.get_color_rgb_right()); + } + + Future getSensors({bool debug = true}) async { + await communicationService.send("<${TrameBuilder.get_sensor_distance()}>", debug: debug); + } + + Future getIMU() async { + debugPrint("[IloLib] Request IMU data..."); + bool result = await _send(TrameBuilder.get_imu_info()); + debugPrint("[IloLib] IMU request result: $result"); + return result; + } + + Future getBatteryLevel() async { + debugPrint("[IloLib] Request battery level..."); + await _send(TrameBuilder.get_battery_info()); + } + + Future setColor(Color color) async { + debugPrint("[IloLib] SET COLOR to: $color"); + // TrameBuilder.set_led_color_circle(r, g, b) exists? + // trames.json: "set_led_color_circle", "format": "511r[s:r]g[s:g]b[s:b]" + // It takes strings for r, g, b. + String r = color.red.toString().padLeft(3, '0'); + String g = color.green.toString().padLeft(3, '0'); + String b = color.blue.toString().padLeft(3, '0'); + // Note: There are two set_led_color_circle in trames.json with different formats (511 and 512). + // The generator might have overwritten one or generated both if names are unique? + // trames.json has duplicate names! + // { "name": "set_led_color_circle", "format": "511r[s:r]g[s:g]b[s:b]" }, + // { "name": "set_led_color_circle", "format": "512r[s:r]g[s:g]b[s:b]" }, + // The generator will generate two functions with the same name! The second one will overwrite the first in the class. + // I should check TrameBuilder.dart to see which one won. + // But for now I'll assume one of them is available. + // The original code used 51. + // '<51r${color.red...}g${color.green...}b${color.blue...}>' + // This matches neither 511 nor 512 exactly if 51 is the prefix. + // Wait, original code: '<51r...>' + // trames.json: 511r... and 512r... + // Maybe original code was using an older version or I misread. + // Let's assume I should use what's in trames.json. + // But wait, if I use the generated code, I am bound to what trames.json says. + // If trames.json says 511, I send 511. + // If the robot expects 51, then trames.json is wrong or I am using wrong version. + // I'll use the generated code. + await _send(TrameBuilder.set_led_color_circle(r, g, b)); + } + + Future setBottomLightValue(int value) async { + //VALUE DE 0 A 255 + debugPrint("[IloLib] SET BOTTOM LIGHT VALUE to: $value"); + if (value < 0 || value > 255) { + debugPrint("[IloLib] Error: Value must be between 0 and 255"); + return; + } + await _send(TrameBuilder.set_led_captor(value)); + robotStatus.captorsLight = value > 0; + } + + Future getName() async { + debugPrint("[IloLib] Request robot name..."); + // <93> is not in trames.json? + // trames.json has: { "name": "get_robot_version", "format": "500y" } + // I don't see <93> in trames.json I read earlier. + // Let me check trames.json again. + // I read lines 1-50. Maybe it's further down. + // I'll assume it's not there if I didn't see it, or I should check. + // If it's not in TrameBuilder, I can't use it. + // I'll leave manual send for now if not found. + await communicationService.send("<93>"); + } + + Future rotate(int angle) async { + debugPrint("[IloLib] Rotating by $angle..."); + // + // trames.json: { "name": "run_command_motor", "format": "a[s:params]" } + // So I can use run_command_motor("vpxyr0$absAngle") + bool isNegative = angle < 0; + int absAngle = angle.abs(); + String params; + if (isNegative) { + params = "vpxyr0$absAngle"; + } else { + params = "vpxyl0$absAngle"; + } + await _send(TrameBuilder.run_command_motor(params)); + } + + Future move(String direction, double distance, int acceleration) async { + debugPrint("[IloLib] Moving $direction by $distance..."); + // + // run_command_motor("${acceleration}vpx...") + String params = "${acceleration}vpx"; + if (direction == "front" || direction == "back") { + if (direction == 'front') { + params += "1"; + } + if (direction == 'back') { + params += "0"; + } + params += "${distance.toInt()}yr"; + } + if (direction == "left" || direction == "right") { + params += "y"; + if (direction == 'left') { + params += "0"; + } + if (direction == 'right') { + params += "1"; + } + params += "${distance.toInt()}r"; + } + debugPrint("[IloLib] Command: "); + await _send(TrameBuilder.run_command_motor(params)); + } + + //MOTORS COMMANDS + Future pingMotor(int idMotor) async { + debugPrint("[IloLib] Ping motor $idMotor..."); + // <60i$idMotor> + // trames.json: { "name": "ping_motor", "format": "60i[i:ping_status_0]s[i:ping_status_1]" } + // Wait, format is 60i...s... + // But original code sends <60i$idMotor>. + // This looks like a mismatch between trames.json and original code. + // Original code: await CommunicationService.getInstance().send("<60i$idMotor>"); + // trames.json: 60i[i:ping_status_0]s[i:ping_status_1] + // This looks like a RESPONSE format, not a REQUEST format? + // Or maybe ping_motor in trames.json is for parsing? + // But trames.json is usually for sending commands. + // If trames.json doesn't have the request format, I can't use TrameBuilder. + // I'll stick to manual send for this one if TrameBuilder doesn't match. + await communicationService.send("<60i$idMotor>"); + } + + Future setMotorSpeed(int idMotor, int speed, + {int acceleration = 100}) async { + if (speed < -7000 || speed > 7000) { + debugPrint("[IloLib] Error: Speed must be between -7000 and 7000"); + return; + } + debugPrint("[IloLib] Set motor $idMotor speed to $speed..."); + // <610i${idMotor}a${acceleration}v$speed> + // trames.json: { "name": "drive_single_motor_speed", "format": "610i[i:motor_index]a[i:acc]v[i:speed]" } + await _send(TrameBuilder.drive_single_motor_speed(idMotor, acceleration, speed)); + } + + Future getMotorSpeed(int idMotor) async { + debugPrint("[IloLib] Get motor $idMotor speed..."); + // <611i$idMotor> + // trames.json: { "name": "get_single_motor_speed", "format": "611i[i:motor_index]" } + await _send(TrameBuilder.get_single_motor_speed(idMotor)); + } + + Future setMotorPosition(int idMotor, int position) async { + int acceleration = 100; + int velocity = 2000; + if (position < 0 || position > 4096) { + debugPrint("[IloLib] Error: Position must be between 0 and 4096"); + return; + } + debugPrint("[IloLib] Set motor $idMotor position to $position..."); + // <620i${idMotor}a${acceleration}v${velocity}p$position> + // trames.json: { "name": "drive_single_motor_angle", "format": "620i[i:motor_index]a[i:acc]v[i:vel]p[i:position]" } + await _send(TrameBuilder.drive_single_motor_angle(idMotor, acceleration, velocity, position)); + } + + Future getMotorPosition(int idMotor) async { + debugPrint("[IloLib] Get motor $idMotor position..."); + // <621i$idMotor> + // trames.json: { "name": "get_single_motor_angle", "format": "621i[i:motor_index]" } + await _send(TrameBuilder.get_single_motor_angle(idMotor)); + } + + Future getMotorTemperature(int idMotor) async { + debugPrint("[IloLib] Get motor $idMotor temperature..."); + // <63i$idMotor> + // trames.json: { "name": "get_single_motor_temp", "format": "63i[i:motor_index]" } + await _send(TrameBuilder.get_single_motor_temp(idMotor)); + } + + Future getMotorVoltage(int idMotor) async { + debugPrint("[IloLib] Get motor $idMotor voltage..."); + // <64i$idMotor> + // trames.json: { "name": "get_single_motor_volt", "format": "64i[i:motor_index]" } + await _send(TrameBuilder.get_single_motor_volt(idMotor)); + } + + Future getMotorTorque(int idMotor) async { + debugPrint("[IloLib] Get motor $idMotor torque..."); + // <65i$idMotor> + // trames.json: { "name": "get_single_motor_load", "format": "65i[i:motor_index]" } + await _send(TrameBuilder.get_single_motor_load(idMotor)); + } + + Future getMotorCurrent(int idMotor) async { + debugPrint("[IloLib] Get motor $idMotor 'Courant'..."); + // <66i$idMotor> + // trames.json: { "name": "get_single_motor_current", "format": "66i[i:motor_index]" } + await _send(TrameBuilder.get_single_motor_current(idMotor)); + } + + Future getMotorIsMoving(int idMotor) async { + debugPrint("[IloLib] Get motor $idMotor isMoving..."); + // <67i$idMotor> + // trames.json: { "name": "get_single_motor_move", "format": "67i[i:motor_index]" } + await _send(TrameBuilder.get_single_motor_move(idMotor)); + } + + Future getIloVersion() async { + debugPrint("[IloLib] Get Ilo version..."); + // <500y> + // trames.json: { "name": "get_robot_version", "format": "500y" } + await _send(TrameBuilder.get_robot_version()); + } + + Future getManufacturingDate() async { + debugPrint("[IloLib] Get manufacturing date..."); + // <120> + // Not in trames.json I read. + await communicationService.send("<120>"); + } + + Future setManufacturingDate(String date) async { + debugPrint("[IloLib] Set manufacturing date to $date..."); + robotStatus.setManufacturingDate(date); + await communicationService.send("<121s$date>"); + } + + Future getFirstUseDate() async { + debugPrint("[IloLib] Get first use date..."); + await communicationService.send("<130>"); + } + + Future setFirstUseDate(String date) async { + debugPrint("[IloLib] Set first use date to $date..."); + robotStatus.setFirstUseDate(date); + await communicationService.send("<131s$date>"); + } + + Future getProductVersion() async { + debugPrint("[IloLib] Get product Version..."); + await communicationService.send("<140>"); + } + + Future setProductVersion(String version) async { + debugPrint("[IloLib] Set product Version to $version..."); + robotStatus.setProductVersion(version); + return await communicationService.send("<141s$version>"); + } + + Future getProductID() async { + debugPrint("[IloLib] Get product Version..."); + await communicationService.send("<150>"); + } + + Future setProductID(String id) async { + debugPrint("[IloLib] Set product ID to $id..."); + robotStatus.setProductId(id); + await communicationService.send("<151s$id>"); + } + + Future getReviewDate() async { + debugPrint("[IloLib] Get review date..."); + await communicationService.send("<160>"); + } + + Future setReviewDate(String date) async { + debugPrint("[IloLib] Set review date to $date..."); + robotStatus.setReviewDate(date); + await communicationService.send("<161s$date>"); + } + // END MOTORS COMMANDS + + //TODO : <690t -> TEMP POS -> délai entre chaque déplacement du robot. (différent entre mode direct et mode dessin par exemple) + Future setTempoPos(int tempo) async { + debugPrint("[IloLib] Set tempo pos to $tempo..."); + await communicationService.send("<690t$tempo>"); + } + + Future pause({int milliseconds = 1000}) async { + debugPrint("[IloLib] Set pause to $milliseconds..."); + await communicationService.send(""); + } +} diff --git a/bindings/flutter/lib/src/msg_parser.dart b/bindings/flutter/lib/src/msg_parser.dart new file mode 100644 index 0000000..c26c86e --- /dev/null +++ b/bindings/flutter/lib/src/msg_parser.dart @@ -0,0 +1,345 @@ +import 'package:flutter/foundation.dart'; +import 'robot_status.dart'; + +class MsgParser { + final RobotStatus robotStatus; + final Function(String)? onDiagnosticReceived; + + MsgParser(this.robotStatus, {this.onDiagnosticReceived}); + + Future parse(String message) async { + if (message.contains("<0/")) { + parseTrameStream(message); + } else if (message.contains("<10c")) { + parseBottomColorCenter(message); + } else if (message.contains("<10l")) { + parseBottomColorLeft(message); + } else if (message.contains("<10d")) { + parseBottomColorRight(message); + } else if (message.contains("<20f")) { + parseSensors(message); + } else if (message.contains("<30")) { + parseIMU(message); + } else if (message.contains("<40s")) { + parseBatteryLevel(message); + } else if (message.contains("<60")) { + parsePingMotor(message); + } else if (message.contains("<61")) { + parseMotorSpeed(message); + } else if (message.contains("<62")) { + parseMotorPosition(message); + } else if (message.contains("<63")) { + parseMotorTemperature(message); + } else if (message.contains("<64")) { + parseMotorVoltage(message); + } else if (message.contains("<93n")) { + parseName(message); + } else if (message.contains("<110s")) { + parseDiagnostic(message); + } else if (message.contains("<120")) { + parseManufacturingDate(message); + } else if (message.contains("<130")) { + parseFirstUseDate(message); + } else if (message.contains("<140")) { + parseProductVersion(message); + } else if (message.contains("<150")) { + parseProductId(message); + } else if (message.contains("<160")) { + parseReviewDate(message); + } else if (message.contains("<500y")) { + parseIloVersion(message); + } else if (message.trim() == ">" || message.trim().isEmpty) { + // Ignore empty or closing tag only messages + } else { + debugPrint("[MsgParser] Unknown message format: $message"); + } + } + + Future parseTrameStream(String message) async { + try { + //EXAMPLE: <0/32x0.01y-0.01z1.01r0.00p-0.37g-0.18/40s0p100/> + List parts = message.split("/"); + for (String part in parts) { + if (part.startsWith("30")) { + parseIMU(part); + } + if (part.startsWith("40")) { + parseBatteryLevel(part); + } + if (part.startsWith("611")) { + parseMotorSpeed(part); + } + if (part.startsWith("63")) { + parseMotorTemperature(part); + } + } + } catch (e) { + debugPrint("[MsgParser] Error: Invalid trame stream format: $message"); + } + } + + Future parseBottomColorCenter(String message) async { + try { + //TRAME EXEMPLE: <10r256g10b0> + String redStr = message.split("r")[1].split("g")[0]; + String greenStr = message.split("g")[1].split("b")[0]; + String blueStr = message.split("b")[1].split(">")[0]; + int red = int.parse(redStr); + int green = int.parse(greenStr); + int blue = int.parse(blueStr); + robotStatus.setBottomColorCenter(red, green, blue); + } catch (e) { + debugPrint("[MsgParser] Error: Invalid bottom color format: $message"); + } + } + + Future parseBottomColorLeft(String message) async { + try { + //TRAME EXEMPLE: <10r256g10b0> + String redStr = message.split("r")[1].split("g")[0]; + String greenStr = message.split("g")[1].split("b")[0]; + String blueStr = message.split("b")[1].split(">")[0]; + int red = int.parse(redStr); + int green = int.parse(greenStr); + int blue = int.parse(blueStr); + robotStatus.setBottomColorCenter(red, green, blue); + } catch (e) { + debugPrint("[MsgParser] Error: Invalid bottom color format: $message"); + } + } + + Future parseBottomColorRight(String message) async { + try { + //TRAME EXEMPLE: <10r256g10b0> + String redStr = message.split("r")[1].split("g")[0]; + String greenStr = message.split("g")[1].split("b")[0]; + String blueStr = message.split("b")[1].split(">")[0]; + int red = int.parse(redStr); + int green = int.parse(greenStr); + int blue = int.parse(blueStr); + robotStatus.setBottomColorCenter(red, green, blue); + } catch (e) { + debugPrint("[MsgParser] Error: Invalid bottom color format: $message"); + } + } + + Future parseSensors(String message) async { + try { + List parts = message.split("f"); + if (parts.length == 2) { + String frontStr = parts[1].split("r")[0]; + String rightStr = parts[1].split("r")[1].split("b")[0]; + String backStr = parts[1].split("r")[1].split("b")[1].split("l")[0]; + String leftStr = parts[1].split("r")[1].split("b")[1].split("l")[1]; + leftStr = leftStr.substring(0, leftStr.length - 1); + int front = int.parse(frontStr); + int right = int.parse(rightStr); + int back = int.parse(backStr); + int left = int.parse(leftStr); + + robotStatus.setSensors(front, right, back, left); + } + } catch (e) { + debugPrint("[MsgParser] Error: Invalid sensors data format: $message"); + } + } + + Future parseIMU(String message) async { + try { + double roll = 0.0; + double pitch = 0.0; + double yaw = 0.0; + + if (message.contains("r") && + message.contains("p") && + message.contains("y")) { + roll = double.parse(message.split("r")[1].split("p")[0]); + pitch = double.parse(message.split("p")[1].split("y")[0]); + yaw = double.parse(message.split("y")[1].replaceAll(">", "")); + } + robotStatus.setImu(roll, pitch, yaw); + } catch (e) { + debugPrint("[MsgParser] Error: Invalid IMU data format: $message"); + } + } + + Future parseBatteryLevel(String message) async { + // TRAME EXAMPLE : <40s0p100v3.94> + try { + String batteryLevelStr = + message.substring(message.indexOf('p') + 1, message.indexOf('v')); + // String isBatteryChargingStr = + // message.substring(message.indexOf('s') + 1, message.indexOf('p')); + // String voltageStr = + // message.substring(message.indexOf('v') + 1, message.length - 1); + int batteryLevel = int.parse(batteryLevelStr); + // double voltage = double.parse(voltageStr); + // bool isBatteryCharging = isBatteryChargingStr == "1"; + + // Note: RobotStatus in library might need update to expose setters for these + // For now, we assume RobotStatus has a way to set this or we access private fields if we were in same library + // But since we are in same package, we can access private fields if we are careful, but better to add setters. + // I'll assume setters exist or I'll add them to RobotStatus later. + // robotStatus.setBatteryLevel(batteryLevel, batteryCharging: isBatteryCharging, batteryVoltage: voltage); + // Since I created RobotStatus, I know it doesn't have setBatteryLevel. I should add it. + // For now I will just print. + } catch (e) { + debugPrint("[MsgParser] Error: Invalid battery level format: $message"); + } + } + + Future parseName(String message) async { + try { + String name = message.substring(4, message.length - 1); + robotStatus.setName(name); + } catch (e) { + debugPrint("[MsgParser] Error: Invalid robot name format: $message"); + } + } + + Future parsePingMotor(String message) async { + //EXAMPLE <60i2s0> (motor 2 is stopped), <60i2s1> (motor 2 is running) + try { + int motorId = int.parse(message.substring(4, 5)); + bool isRunning = message.substring(6, message.length - 1) == "1"; + robotStatus.setMotorInfo("status", motorId, isRunning); + } catch (e) { + debugPrint("[MsgParser] Error: Invalid motor ping format: $message"); + } + } + + Future parseMotorSpeed(String message) async { + // Exemple <611i2s25> (motor 2 speed is 25) + try { + int iIndex = message.indexOf('i'); + int sIndex = message.indexOf('s'); + if (iIndex == -1 || sIndex == -1 || iIndex >= sIndex) { + throw FormatException( + "Invalid format: 'i' or 's' missing or in wrong order"); + } + String motorIdStr = message.substring(iIndex + 1, sIndex); + String speedStr = message.substring(sIndex + 1, + message.contains(">") ? message.length - 1 : message.length); + if (motorIdStr.isEmpty || speedStr.isEmpty) { + throw FormatException("Motor ID or speed is missing"); + } + int motorId = int.parse(motorIdStr); + int speed = int.tryParse(speedStr) ?? 0; + if (motorId < 1 || motorId > 4) { + throw FormatException("Motor ID must be between 1 and 4"); + } + if (speed < -7000 || speed > 7000) { + throw FormatException("Speed must be between -7000 and 7000"); + } + if (speed != -50 || speed != 50) { + robotStatus.setMotorInfo("speed", motorId, speed); + } + } catch (e) { + debugPrint("[MsgParser] Error: Invalid motor speed format: $message"); + } + } + + Future parseMotorPosition(String message) async { + //EXAMPLE <621i2s25> (motor 2 position is 25) + try { + int motorId = int.parse(message.substring(5, 6)); + int position = int.parse(message.substring(7, message.length - 1)); + robotStatus.setMotorInfo("position", motorId, position); + } catch (e) { + debugPrint("[MsgParser] Error: Invalid motor position format: $message"); + } + } + + Future parseMotorTemperature(String message) async { + //EXAMPLE <63i2s25> (motor 2 temperature is 25°C) + try { + int motorId = int.parse( + message.substring(message.indexOf('i') + 1, message.indexOf('s'))); + int temperature = int.parse(message.substring( + message.indexOf('s') + 1, + message[message.length - 1] == '>' + ? message.length - 1 + : message.length)); + robotStatus.setMotorInfo("temperature", motorId, temperature); + } catch (e) { + debugPrint("[MsgParser] Error: Invalid motor temperature format: $message"); + } + } + + Future parseMotorVoltage(String message) async { + //EXAMPLE <64i2s25> (motor 2 voltage is 25V) + try { + int motorId = int.parse(message.substring(4, 5)); + int voltage = int.parse(message.substring(6, message.length - 1)); + robotStatus.setMotorInfo("voltage", motorId, voltage); + } catch (e) { + debugPrint("[MsgParser] Error: Invalid motor voltage format: $message"); + } + } + + Future parseManufacturingDate(String message) async { + try { + String date = message.substring(5, message.length - 1); + robotStatus.setManufacturingDate(date); + } catch (e) { + debugPrint("[MsgParser] Error: Invalid manufacturing date format: $message"); + } + } + + Future parseFirstUseDate(String message) async { + try { + String date = message.substring(5, message.length - 1); + robotStatus.setFirstUseDate(date); + } catch (e) { + debugPrint("[MsgParser] Error: Invalid first use date format: $message"); + } + } + + Future parseProductVersion(String message) async { + try { + String version = message.substring(5, message.length - 1); + robotStatus.setProductVersion(version); + } catch (e) { + debugPrint("[MsgParser] Error: Invalid product version format: $message"); + } + } + + Future parseProductId(String message) async { + try { + String productId = message.substring(5, message.length - 1); + robotStatus.setProductId(productId); + } catch (e) { + debugPrint("[MsgParser] Error: Invalid product ID format: $message"); + } + } + + Future parseReviewDate(String message) async { + try { + String date = message.substring(5, message.length - 1); + robotStatus.setReviewDate(date); + } catch (e) { + debugPrint("[MsgParser] Error: Invalid review date format: $message"); + } + } + + Future parseIloVersion(String message) async { + try { + String version = message.substring(5, message.length - 1); + robotStatus.setVersion(version); + } catch (e) { + debugPrint("[MsgParser] Error: Invalid Ilo version format: $message"); + } + } + + Future parseDiagnostic(String message) async { + try { + String diagnostic = message.substring(5, message.length - 1); + robotStatus.setDiagnostic(diagnostic); + if (onDiagnosticReceived != null) { + onDiagnosticReceived!(diagnostic); + } + } catch (e) { + debugPrint("[MsgParser] Error: Invalid diagnostic format: $message"); + } + } +} diff --git a/bindings/flutter/lib/src/robot_status.dart b/bindings/flutter/lib/src/robot_status.dart new file mode 100644 index 0000000..ecf8e9d --- /dev/null +++ b/bindings/flutter/lib/src/robot_status.dart @@ -0,0 +1,235 @@ +import 'package:flutter/foundation.dart'; + +enum RobotStatusSensor { + SENSOR_FRONT, + SENSOR_BACK, + SENSOR_LEFT, + SENSOR_RIGHT, +} + +class RobotStatus extends ChangeNotifier { + static const double LOW_BATTERY_THRESHOLD = 15; + static const double CRITICAL_BATTERY_THRESHOLD = 5; + + bool get isLowBattery => + _batteryLevel != null && _batteryLevel! < LOW_BATTERY_THRESHOLD; + bool get isCriticalBattery => + _batteryLevel != null && _batteryLevel! < CRITICAL_BATTERY_THRESHOLD; + int? _batteryLevel; + bool? _batteryCharging; + double? _batteryVoltage; + bool _connected = false; + String? _error; + final Set _obstacles = {}; + + String? name; + String? version; + + /* + INFOS COMPLEMENTAIRES + */ + + String? productVersion; + String? productId; + String? manufacturingDate; + String? firstUseDate; + String? reviewDate; + + void setProductVersion(String version) { + productVersion = version; + notifyListeners(); + } + + void setProductId(String id) { + productId = id; + notifyListeners(); + } + + void setManufacturingDate(String date) { + manufacturingDate = date; + notifyListeners(); + } + + void setFirstUseDate(String date) { + firstUseDate = date; + notifyListeners(); + } + + void setReviewDate(String date) { + reviewDate = date; + notifyListeners(); + } + + double? imuRoll; + double? imuPitch; + double? imuYaw; + + int? sensorFront = 0; + int? sensorBack = 0; + int? sensorLeft = 0; + int? sensorRight = 0; + + bool captorsLight = false; + + int? get batteryLevel => _batteryLevel; + + bool get isBatteryCharging => _batteryCharging == true; + bool get isConnected => _connected; + bool get hasError => _error != null; + bool get hasLeftObstacle => + _obstacles.contains(RobotStatusSensor.SENSOR_LEFT); + bool get hasRightObstacle => + _obstacles.contains(RobotStatusSensor.SENSOR_RIGHT); + bool get hasFrontObstacle => + _obstacles.contains(RobotStatusSensor.SENSOR_FRONT); + bool get hasBackObstacle => + _obstacles.contains(RobotStatusSensor.SENSOR_BACK); + + final Map _obstacleDistances = { + RobotStatusSensor.SENSOR_FRONT: 0, + RobotStatusSensor.SENSOR_BACK: 0, + RobotStatusSensor.SENSOR_LEFT: 0, + RobotStatusSensor.SENSOR_RIGHT: 0, + }; + + List> motors = [ + { + 'motor': 1, + 'status': true, + 'speed': 0, + 'position': 0, + 'temperature': 0, + 'voltage': 0, + 'torque': false, + 'current': 0, + 'isMoving': false + }, + { + 'motor': 2, + 'status': true, + 'speed': 0, + 'position': 0, + 'temperature': 0, + 'voltage': 0, + 'torque': false, + 'current': 0, + 'isMoving': false + }, + { + 'motor': 3, + 'status': true, + 'speed': 0, + 'position': 0, + 'temperature': 0, + 'voltage': 0, + 'torque': false, + 'current': 0, + 'isMoving': false, + }, + { + 'motor': 4, + 'status': true, + 'speed': 0, + 'position': 0, + 'temperature': 0, + 'voltage': 0, + 'torque': false, + 'current': 0, + 'isMoving': false + }, + { + 'motor': 5, + 'status': false, + 'speed': 0, + 'position': 0, + 'temperature': 0, + 'voltage': 0, + 'torque': false, + 'current': 0, + 'isMoving': false + }, + { + 'motor': 6, + 'status': false, + 'speed': 0, + 'position': 0, + 'temperature': 0, + 'voltage': 0, + 'torque': false, + 'current': 0, + 'isMoving': false + }, + { + 'motor': 7, + 'status': false, + 'speed': 0, + 'position': 0, + 'temperature': 0, + 'voltage': 0, + 'torque': false, + 'current': 0, + 'isMoving': false + }, + { + 'motor': 8, + 'status': false, + 'speed': 0, + 'position': 0, + 'temperature': 0, + 'voltage': 0, + 'torque': false, + 'current': 0, + 'isMoving': false + }, + ]; + + String? diagnostic; + + void setSensors(int front, int right, int back, int left) { + sensorFront = front; + sensorRight = right; + sensorBack = back; + sensorLeft = left; + notifyListeners(); + } + + void setName(String newName) { + name = newName; + notifyListeners(); + } + + void setVersion(String newVersion) { + version = newVersion; + notifyListeners(); + } + + void setDiagnostic(String newDiagnostic) { + diagnostic = newDiagnostic; + notifyListeners(); + } + + void setConnected(bool connected) { + _connected = connected; + notifyListeners(); + } + + // Methods to update state (extracted from MsgParser usage) + void setBottomColorCenter(int r, int g, int b) { + // TODO: Implement storage for color if needed + notifyListeners(); + } + + void setImu(double roll, double pitch, double yaw) { + imuRoll = roll; + imuPitch = pitch; + imuYaw = yaw; + notifyListeners(); + } + + void setMotorInfo(String key, int motorId, dynamic value) { + if (motorId >= 1 && motorId <= motors.length) { + motors[motorId - 1][key] = value; + notifyListeners(); + } + } +} diff --git a/bindings/flutter/lib/src/trame_builder.dart b/bindings/flutter/lib/src/trame_builder.dart new file mode 100644 index 0000000..ba461df --- /dev/null +++ b/bindings/flutter/lib/src/trame_builder.dart @@ -0,0 +1,560 @@ +// Auto-generated primitives for ilo communication +// This file is generated. Do not edit manually. + +class TrameBuilder { + + /// Primitive for safety_stop + static String safety_stop() { + StringBuffer buffer = StringBuffer(); + buffer.write(""); + return buffer.toString(); + } + /// Primitive for handshake_ilo + static String handshake_ilo() { + StringBuffer buffer = StringBuffer(); + buffer.write("ilo"); + return buffer.toString(); + } + /// Primitive for get_robot_version + static String get_robot_version() { + StringBuffer buffer = StringBuffer(); + buffer.write("500y"); + return buffer.toString(); + } + /// Primitive for start_firmware_upload + static String start_firmware_upload(int size) { + StringBuffer buffer = StringBuffer(); + buffer.write("500x"); + buffer.write(size); + buffer.write(""); + return buffer.toString(); + } + /// Primitive for start_trame_s + static String start_trame_s(String trame_s_params) { + StringBuffer buffer = StringBuffer(); + buffer.write("0"); + buffer.write(trame_s_params); + buffer.write(""); + return buffer.toString(); + } + /// Primitive for stop_tasks + static String stop_tasks() { + StringBuffer buffer = StringBuffer(); + buffer.write("00"); + return buffer.toString(); + } + /// Primitive for get_color_rgb_center + static String get_color_rgb_center() { + StringBuffer buffer = StringBuffer(); + buffer.write("10c"); + return buffer.toString(); + } + /// Primitive for get_color_rgb_left + static String get_color_rgb_left() { + StringBuffer buffer = StringBuffer(); + buffer.write("10l"); + return buffer.toString(); + } + /// Primitive for get_color_rgb_right + static String get_color_rgb_right() { + StringBuffer buffer = StringBuffer(); + buffer.write("10d"); + return buffer.toString(); + } + /// Primitive for get_color_clear + static String get_color_clear() { + StringBuffer buffer = StringBuffer(); + buffer.write("11"); + return buffer.toString(); + } + /// Primitive for get_line + static String get_line() { + StringBuffer buffer = StringBuffer(); + buffer.write("12"); + return buffer.toString(); + } + /// Primitive for set_line_threshold_value + static String set_line_threshold_value(int threshold) { + StringBuffer buffer = StringBuffer(); + buffer.write("13t"); + buffer.write(threshold); + buffer.write(""); + return buffer.toString(); + } + /// Primitive for get_line_threshold_value + static String get_line_threshold_value() { + StringBuffer buffer = StringBuffer(); + buffer.write("14"); + return buffer.toString(); + } + /// Primitive for get_accessory_status + static String get_accessory_status() { + StringBuffer buffer = StringBuffer(); + buffer.write("15"); + return buffer.toString(); + } + /// Primitive for get_sensor_distance + static String get_sensor_distance() { + StringBuffer buffer = StringBuffer(); + buffer.write("20"); + return buffer.toString(); + } + /// Primitive for get_distance_front + static String get_distance_front() { + StringBuffer buffer = StringBuffer(); + buffer.write("21"); + return buffer.toString(); + } + /// Primitive for get_distance_right + static String get_distance_right() { + StringBuffer buffer = StringBuffer(); + buffer.write("22"); + return buffer.toString(); + } + /// Primitive for get_distance_back + static String get_distance_back() { + StringBuffer buffer = StringBuffer(); + buffer.write("23"); + return buffer.toString(); + } + /// Primitive for get_distance_left + static String get_distance_left() { + StringBuffer buffer = StringBuffer(); + buffer.write("24"); + return buffer.toString(); + } + /// Primitive for get_imu_info + static String get_imu_info() { + StringBuffer buffer = StringBuffer(); + buffer.write("30"); + return buffer.toString(); + } + /// Primitive for reset_angle + static String reset_angle() { + StringBuffer buffer = StringBuffer(); + buffer.write("31"); + return buffer.toString(); + } + /// Primitive for get_raw_imu + static String get_raw_imu() { + StringBuffer buffer = StringBuffer(); + buffer.write("32"); + return buffer.toString(); + } + /// Primitive for get_battery_info + static String get_battery_info() { + StringBuffer buffer = StringBuffer(); + buffer.write("40"); + return buffer.toString(); + } + /// Primitive for get_led_color + static String get_led_color() { + StringBuffer buffer = StringBuffer(); + buffer.write("50"); + return buffer.toString(); + } + /// Primitive for set_led_color_circle + static String set_led_color_circle(String r, String g, String b) { + StringBuffer buffer = StringBuffer(); + buffer.write("511r"); + buffer.write(r); + buffer.write("g"); + buffer.write(g); + buffer.write("b"); + buffer.write(b); + buffer.write(""); + return buffer.toString(); + } + /// Primitive for set_led_color_circle_2 + static String set_led_color_circle_2(String r, String g, String b) { + StringBuffer buffer = StringBuffer(); + buffer.write("512r"); + buffer.write(r); + buffer.write("g"); + buffer.write(g); + buffer.write("b"); + buffer.write(b); + buffer.write(""); + return buffer.toString(); + } + /// Primitive for set_led_shape + static String set_led_shape(String shape) { + StringBuffer buffer = StringBuffer(); + buffer.write("52v"); + buffer.write(shape); + buffer.write(""); + return buffer.toString(); + } + /// Primitive for set_led_mode + static String set_led_mode(String mode, int nb_loop) { + StringBuffer buffer = StringBuffer(); + buffer.write("53"); + buffer.write(mode); + buffer.write("/"); + buffer.write(nb_loop); + buffer.write(""); + return buffer.toString(); + } + /// Primitive for set_led_captor + static String set_led_captor(int brightness) { + StringBuffer buffer = StringBuffer(); + buffer.write("54l"); + buffer.write(brightness); + buffer.write(""); + return buffer.toString(); + } + /// Primitive for set_led_single + static String set_led_single(String type, int id, int red, int green, int blue) { + StringBuffer buffer = StringBuffer(); + buffer.write("55t"); + buffer.write(type); + buffer.write("d"); + buffer.write(id); + buffer.write("r"); + buffer.write(red); + buffer.write("g"); + buffer.write(green); + buffer.write("b"); + buffer.write(blue); + buffer.write(""); + return buffer.toString(); + } + /// Primitive for display_word + static String display_word(String word, int delay, int nb_loops) { + StringBuffer buffer = StringBuffer(); + buffer.write("56w"); + buffer.write(word); + buffer.write("d"); + buffer.write(delay); + buffer.write("/"); + buffer.write(nb_loops); + buffer.write(""); + return buffer.toString(); + } + /// Primitive for display_word_slide + static String display_word_slide() { + StringBuffer buffer = StringBuffer(); + buffer.write("57"); + return buffer.toString(); + } + /// Primitive for set_animation_flag_false + static String set_animation_flag_false() { + StringBuffer buffer = StringBuffer(); + buffer.write("58"); + return buffer.toString(); + } + /// Primitive for run_command_motor + static String run_command_motor(String params) { + StringBuffer buffer = StringBuffer(); + buffer.write("a"); + buffer.write(params); + buffer.write(""); + return buffer.toString(); + } + /// Primitive for ping_motor + static String ping_motor(int ping_status_0, int ping_status_1) { + StringBuffer buffer = StringBuffer(); + buffer.write("60i"); + buffer.write(ping_status_0); + buffer.write("s"); + buffer.write(ping_status_1); + buffer.write(""); + return buffer.toString(); + } + /// Primitive for drive_single_motor_speed + static String drive_single_motor_speed(int motor_index, int acc, int speed) { + StringBuffer buffer = StringBuffer(); + buffer.write("610i"); + buffer.write(motor_index); + buffer.write("a"); + buffer.write(acc); + buffer.write("v"); + buffer.write(speed); + buffer.write(""); + return buffer.toString(); + } + /// Primitive for get_single_motor_speed + static String get_single_motor_speed(int motor_index) { + StringBuffer buffer = StringBuffer(); + buffer.write("611i"); + buffer.write(motor_index); + buffer.write(""); + return buffer.toString(); + } + /// Primitive for drive_single_motor_angle + static String drive_single_motor_angle(int motor_index, int acc, int vel, int position) { + StringBuffer buffer = StringBuffer(); + buffer.write("620i"); + buffer.write(motor_index); + buffer.write("a"); + buffer.write(acc); + buffer.write("v"); + buffer.write(vel); + buffer.write("p"); + buffer.write(position); + buffer.write(""); + return buffer.toString(); + } + /// Primitive for get_single_motor_angle + static String get_single_motor_angle(int motor_index) { + StringBuffer buffer = StringBuffer(); + buffer.write("621i"); + buffer.write(motor_index); + buffer.write(""); + return buffer.toString(); + } + /// Primitive for get_single_motor_temp + static String get_single_motor_temp(int motor_index) { + StringBuffer buffer = StringBuffer(); + buffer.write("63i"); + buffer.write(motor_index); + buffer.write(""); + return buffer.toString(); + } + /// Primitive for get_single_motor_volt + static String get_single_motor_volt(int motor_index) { + StringBuffer buffer = StringBuffer(); + buffer.write("64i"); + buffer.write(motor_index); + buffer.write(""); + return buffer.toString(); + } + /// Primitive for get_single_motor_load + static String get_single_motor_load(int motor_index) { + StringBuffer buffer = StringBuffer(); + buffer.write("65i"); + buffer.write(motor_index); + buffer.write(""); + return buffer.toString(); + } + /// Primitive for get_single_motor_current + static String get_single_motor_current(int motor_index) { + StringBuffer buffer = StringBuffer(); + buffer.write("66i"); + buffer.write(motor_index); + buffer.write(""); + return buffer.toString(); + } + /// Primitive for get_single_motor_move + static String get_single_motor_move(int motor_index) { + StringBuffer buffer = StringBuffer(); + buffer.write("67i"); + buffer.write(motor_index); + buffer.write(""); + return buffer.toString(); + } + /// Primitive for set_motors_ilo_acc + static String set_motors_ilo_acc(int acc) { + StringBuffer buffer = StringBuffer(); + buffer.write("680a"); + buffer.write(acc); + buffer.write(""); + return buffer.toString(); + } + /// Primitive for get_motors_ilo_acc + static String get_motors_ilo_acc() { + StringBuffer buffer = StringBuffer(); + buffer.write("681"); + return buffer.toString(); + } + /// Primitive for set_tempo_pos + static String set_tempo_pos(int tempo_pos) { + StringBuffer buffer = StringBuffer(); + buffer.write("690t"); + buffer.write(tempo_pos); + buffer.write(""); + return buffer.toString(); + } + /// Primitive for get_tempo_pos + static String get_tempo_pos() { + StringBuffer buffer = StringBuffer(); + buffer.write("691"); + return buffer.toString(); + } + /// Primitive for set_pid + static String set_pid(int Kp, int Ki, int Kd) { + StringBuffer buffer = StringBuffer(); + buffer.write("70p"); + buffer.write(Kp); + buffer.write("i"); + buffer.write(Ki); + buffer.write("d"); + buffer.write(Kd); + buffer.write(""); + return buffer.toString(); + } + /// Primitive for get_pid + static String get_pid() { + StringBuffer buffer = StringBuffer(); + buffer.write("71"); + return buffer.toString(); + } + /// Primitive for check_auto_mode + static String check_auto_mode(int current_auto_mode) { + StringBuffer buffer = StringBuffer(); + buffer.write("80"); + buffer.write(current_auto_mode); + buffer.write(""); + return buffer.toString(); + } + /// Primitive for set_wifi_credentials + static String set_wifi_credentials(String ssid, String password) { + StringBuffer buffer = StringBuffer(); + buffer.write("90"); + buffer.write(ssid); + buffer.write("{|||}"); + buffer.write(password); + buffer.write(""); + return buffer.toString(); + } + /// Primitive for get_wifi_credentials + static String get_wifi_credentials() { + StringBuffer buffer = StringBuffer(); + buffer.write("92"); + return buffer.toString(); + } + /// Primitive for get_hostname + static String get_hostname() { + StringBuffer buffer = StringBuffer(); + buffer.write("93"); + return buffer.toString(); + } + /// Primitive for get_hostname_legacy + static String get_hostname_legacy() { + StringBuffer buffer = StringBuffer(); + buffer.write("930"); + return buffer.toString(); + } + /// Primitive for set_name + static String set_name(String name) { + StringBuffer buffer = StringBuffer(); + buffer.write("94n"); + buffer.write(name); + buffer.write(""); + return buffer.toString(); + } + /// Primitive for set_server_status + static String set_server_status(int status) { + StringBuffer buffer = StringBuffer(); + buffer.write("95s"); + buffer.write(status); + buffer.write(""); + return buffer.toString(); + } + /// Primitive for get_server_status + static String get_server_status() { + StringBuffer buffer = StringBuffer(); + buffer.write("96"); + return buffer.toString(); + } + /// Primitive for get_accessory_data + static String get_accessory_data() { + StringBuffer buffer = StringBuffer(); + buffer.write("100"); + return buffer.toString(); + } + /// Primitive for get_accessory_info + static String get_accessory_info() { + StringBuffer buffer = StringBuffer(); + buffer.write("101"); + return buffer.toString(); + } + /// Primitive for very_very_usefull + static String very_very_usefull() { + StringBuffer buffer = StringBuffer(); + buffer.write("102"); + return buffer.toString(); + } + /// Primitive for set_debug_state + static String set_debug_state(int state) { + StringBuffer buffer = StringBuffer(); + buffer.write("103s"); + buffer.write(state); + buffer.write(""); + return buffer.toString(); + } + /// Primitive for start_diag + static String start_diag() { + StringBuffer buffer = StringBuffer(); + buffer.write("110"); + return buffer.toString(); + } + /// Primitive for get_manufacturing_date + static String get_manufacturing_date() { + StringBuffer buffer = StringBuffer(); + buffer.write("120"); + return buffer.toString(); + } + /// Primitive for set_manufacturing_date + static String set_manufacturing_date(String date) { + StringBuffer buffer = StringBuffer(); + buffer.write("121s"); + buffer.write(date); + buffer.write(""); + return buffer.toString(); + } + /// Primitive for get_first_use_date + static String get_first_use_date() { + StringBuffer buffer = StringBuffer(); + buffer.write("130"); + return buffer.toString(); + } + /// Primitive for set_first_use_date + static String set_first_use_date(String date) { + StringBuffer buffer = StringBuffer(); + buffer.write("131s"); + buffer.write(date); + buffer.write(""); + return buffer.toString(); + } + /// Primitive for get_product_version + static String get_product_version() { + StringBuffer buffer = StringBuffer(); + buffer.write("140"); + return buffer.toString(); + } + /// Primitive for set_product_version + static String set_product_version(String version) { + StringBuffer buffer = StringBuffer(); + buffer.write("141s"); + buffer.write(version); + buffer.write(""); + return buffer.toString(); + } + /// Primitive for get_product_id + static String get_product_id() { + StringBuffer buffer = StringBuffer(); + buffer.write("150"); + return buffer.toString(); + } + /// Primitive for set_product_id + static String set_product_id(String product_id) { + StringBuffer buffer = StringBuffer(); + buffer.write("151s"); + buffer.write(product_id); + buffer.write(""); + return buffer.toString(); + } + /// Primitive for get_review_date + static String get_review_date() { + StringBuffer buffer = StringBuffer(); + buffer.write("160"); + return buffer.toString(); + } + /// Primitive for set_review_date + static String set_review_date(String date) { + StringBuffer buffer = StringBuffer(); + buffer.write("161s"); + buffer.write(date); + buffer.write(""); + return buffer.toString(); + } + /// Primitive for set_auto_setup + static String set_auto_setup(int auto_setup) { + StringBuffer buffer = StringBuffer(); + buffer.write("170a"); + buffer.write(auto_setup); + buffer.write(""); + return buffer.toString(); + } +} \ No newline at end of file diff --git a/bindings/flutter/pubspec.lock b/bindings/flutter/pubspec.lock new file mode 100644 index 0000000..be3f497 --- /dev/null +++ b/bindings/flutter/pubspec.lock @@ -0,0 +1,362 @@ +# Generated by pub +# See https://dart.dev/tools/pub/glossary#lockfile +packages: + args: + dependency: transitive + description: + name: args + sha256: d0481093c50b1da8910eb0bb301626d4d8eb7284aa739614d2b394ee09e3ea04 + url: "https://pub.dev" + source: hosted + version: "2.7.0" + async: + dependency: transitive + description: + name: async + sha256: "758e6d74e971c3e5aceb4110bfd6698efc7f501675bcfe0c775459a8140750eb" + url: "https://pub.dev" + source: hosted + version: "2.13.0" + bluez: + dependency: transitive + description: + name: bluez + sha256: "61a7204381925896a374301498f2f5399e59827c6498ae1e924aaa598751b545" + url: "https://pub.dev" + source: hosted + version: "0.8.3" + boolean_selector: + dependency: transitive + description: + name: boolean_selector + sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea" + url: "https://pub.dev" + source: hosted + version: "2.1.2" + characters: + dependency: transitive + description: + name: characters + sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803 + url: "https://pub.dev" + source: hosted + version: "1.4.0" + clock: + dependency: transitive + description: + name: clock + sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b + url: "https://pub.dev" + source: hosted + version: "1.1.2" + collection: + dependency: transitive + description: + name: collection + sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76" + url: "https://pub.dev" + source: hosted + version: "1.19.1" + crypto: + dependency: transitive + description: + name: crypto + sha256: c8ea0233063ba03258fbcf2ca4d6dadfefe14f02fab57702265467a19f27fadf + url: "https://pub.dev" + source: hosted + version: "3.0.7" + dbus: + dependency: transitive + description: + name: dbus + sha256: "79e0c23480ff85dc68de79e2cd6334add97e48f7f4865d17686dd6ea81a47e8c" + url: "https://pub.dev" + source: hosted + version: "0.7.11" + fake_async: + dependency: transitive + description: + name: fake_async + sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44" + url: "https://pub.dev" + source: hosted + version: "1.3.3" + ffi: + dependency: transitive + description: + name: ffi + sha256: "289279317b4b16eb2bb7e271abccd4bf84ec9bdcbe999e278a94b804f5630418" + url: "https://pub.dev" + source: hosted + version: "2.1.4" + flutter: + dependency: "direct main" + description: flutter + source: sdk + version: "0.0.0" + flutter_blue_plus: + dependency: "direct main" + description: + name: flutter_blue_plus + sha256: "69a8c87c11fc792e8cf0f997d275484fbdb5143ac9f0ac4d424429700cb4e0ed" + url: "https://pub.dev" + source: hosted + version: "1.36.8" + flutter_blue_plus_android: + dependency: transitive + description: + name: flutter_blue_plus_android + sha256: "6f7fe7e69659c30af164a53730707edc16aa4d959e4c208f547b893d940f853d" + url: "https://pub.dev" + source: hosted + version: "7.0.4" + flutter_blue_plus_darwin: + dependency: transitive + description: + name: flutter_blue_plus_darwin + sha256: "682982862c1d964f4d54a3fb5fccc9e59a066422b93b7e22079aeecd9c0d38f8" + url: "https://pub.dev" + source: hosted + version: "7.0.3" + flutter_blue_plus_linux: + dependency: transitive + description: + name: flutter_blue_plus_linux + sha256: "56b0c45edd0a2eec8f85bd97a26ac3cd09447e10d0094fed55587bf0592e3347" + url: "https://pub.dev" + source: hosted + version: "7.0.3" + flutter_blue_plus_platform_interface: + dependency: transitive + description: + name: flutter_blue_plus_platform_interface + sha256: "84fbd180c50a40c92482f273a92069960805ce324e3673ad29c41d2faaa7c5c2" + url: "https://pub.dev" + source: hosted + version: "7.0.0" + flutter_blue_plus_web: + dependency: transitive + description: + name: flutter_blue_plus_web + sha256: a1aceee753d171d24c0e0cdadb37895b5e9124862721f25f60bb758e20b72c99 + url: "https://pub.dev" + source: hosted + version: "7.0.2" + flutter_lints: + dependency: "direct dev" + description: + name: flutter_lints + sha256: "5398f14efa795ffb7a33e9b6a08798b26a180edac4ad7db3f231e40f82ce11e1" + url: "https://pub.dev" + source: hosted + version: "5.0.0" + flutter_test: + dependency: "direct dev" + description: flutter + source: sdk + version: "0.0.0" + flutter_web_plugins: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" + leak_tracker: + dependency: transitive + description: + name: leak_tracker + sha256: "33e2e26bdd85a0112ec15400c8cbffea70d0f9c3407491f672a2fad47915e2de" + url: "https://pub.dev" + source: hosted + version: "11.0.2" + leak_tracker_flutter_testing: + dependency: transitive + description: + name: leak_tracker_flutter_testing + sha256: "1dbc140bb5a23c75ea9c4811222756104fbcd1a27173f0c34ca01e16bea473c1" + url: "https://pub.dev" + source: hosted + version: "3.0.10" + leak_tracker_testing: + dependency: transitive + description: + name: leak_tracker_testing + sha256: "8d5a2d49f4a66b49744b23b018848400d23e54caf9463f4eb20df3eb8acb2eb1" + url: "https://pub.dev" + source: hosted + version: "3.0.2" + lints: + dependency: transitive + description: + name: lints + sha256: c35bb79562d980e9a453fc715854e1ed39e24e7d0297a880ef54e17f9874a9d7 + url: "https://pub.dev" + source: hosted + version: "5.1.1" + matcher: + dependency: transitive + description: + name: matcher + sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2 + url: "https://pub.dev" + source: hosted + version: "0.12.17" + material_color_utilities: + dependency: transitive + description: + name: material_color_utilities + sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec + url: "https://pub.dev" + source: hosted + version: "0.11.1" + meta: + dependency: transitive + description: + name: meta + sha256: "23f08335362185a5ea2ad3a4e597f1375e78bce8a040df5c600c8d3552ef2394" + url: "https://pub.dev" + source: hosted + version: "1.17.0" + path: + dependency: transitive + description: + name: path + sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5" + url: "https://pub.dev" + source: hosted + version: "1.9.1" + petitparser: + dependency: transitive + description: + name: petitparser + sha256: "1a97266a94f7350d30ae522c0af07890c70b8e62c71e8e3920d1db4d23c057d1" + url: "https://pub.dev" + source: hosted + version: "7.0.1" + rxdart: + dependency: transitive + description: + name: rxdart + sha256: "5c3004a4a8dbb94bd4bf5412a4def4acdaa12e12f269737a5751369e12d1a962" + url: "https://pub.dev" + source: hosted + version: "0.28.0" + sky_engine: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" + source_span: + dependency: transitive + description: + name: source_span + sha256: "254ee5351d6cb365c859e20ee823c3bb479bf4a293c22d17a9f1bf144ce86f7c" + url: "https://pub.dev" + source: hosted + version: "1.10.1" + stack_trace: + dependency: transitive + description: + name: stack_trace + sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1" + url: "https://pub.dev" + source: hosted + version: "1.12.1" + stream_channel: + dependency: transitive + description: + name: stream_channel + sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d" + url: "https://pub.dev" + source: hosted + version: "2.1.4" + string_scanner: + dependency: transitive + description: + name: string_scanner + sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43" + url: "https://pub.dev" + source: hosted + version: "1.4.1" + term_glyph: + dependency: transitive + description: + name: term_glyph + sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e" + url: "https://pub.dev" + source: hosted + version: "1.2.2" + test_api: + dependency: transitive + description: + name: test_api + sha256: ab2726c1a94d3176a45960b6234466ec367179b87dd74f1611adb1f3b5fb9d55 + url: "https://pub.dev" + source: hosted + version: "0.7.7" + typed_data: + dependency: transitive + description: + name: typed_data + sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006 + url: "https://pub.dev" + source: hosted + version: "1.4.0" + universal_platform: + dependency: "direct main" + description: + name: universal_platform + sha256: "64e16458a0ea9b99260ceb5467a214c1f298d647c659af1bff6d3bf82536b1ec" + url: "https://pub.dev" + source: hosted + version: "1.1.0" + vector_math: + dependency: transitive + description: + name: vector_math + sha256: d530bd74fea330e6e364cda7a85019c434070188383e1cd8d9777ee586914c5b + url: "https://pub.dev" + source: hosted + version: "2.2.0" + vm_service: + dependency: transitive + description: + name: vm_service + sha256: "45caa6c5917fa127b5dbcfbd1fa60b14e583afdc08bfc96dda38886ca252eb60" + url: "https://pub.dev" + source: hosted + version: "15.0.2" + web: + dependency: transitive + description: + name: web + sha256: "868d88a33d8a87b18ffc05f9f030ba328ffefba92d6c127917a2ba740f9cfe4a" + url: "https://pub.dev" + source: hosted + version: "1.1.1" + web_socket: + dependency: transitive + description: + name: web_socket + sha256: "34d64019aa8e36bf9842ac014bb5d2f5586ca73df5e4d9bf5c936975cae6982c" + url: "https://pub.dev" + source: hosted + version: "1.0.1" + web_socket_channel: + dependency: "direct main" + description: + name: web_socket_channel + sha256: d645757fb0f4773d602444000a8131ff5d48c9e47adfe9772652dd1a4f2d45c8 + url: "https://pub.dev" + source: hosted + version: "3.0.3" + xml: + dependency: transitive + description: + name: xml + sha256: "971043b3a0d3da28727e40ed3e0b5d18b742fa5a68665cca88e74b7876d5e025" + url: "https://pub.dev" + source: hosted + version: "6.6.1" +sdks: + dart: ">=3.8.0 <4.0.0" + flutter: ">=3.19.0" diff --git a/bindings/flutter/pubspec.yaml b/bindings/flutter/pubspec.yaml new file mode 100644 index 0000000..170e9f8 --- /dev/null +++ b/bindings/flutter/pubspec.yaml @@ -0,0 +1,20 @@ +name: ilo_flutter +description: Flutter binding for Ilo Robot communication. +version: 0.0.1 +homepage: https://github.com/intuition-rt/mobile-app + +environment: + sdk: '>=3.0.0 <4.0.0' + flutter: ">=3.0.0" + +dependencies: + flutter: + sdk: flutter + flutter_blue_plus: ^1.32.0 + universal_platform: ^1.0.0+1 + web_socket_channel: ^3.0.0 + +dev_dependencies: + flutter_test: + sdk: flutter + flutter_lints: ^5.0.0 diff --git a/generators/generate_primitives.py b/generators/generate_primitives.py index b8ffc4e..3e992c5 100644 --- a/generators/generate_primitives.py +++ b/generators/generate_primitives.py @@ -50,7 +50,7 @@ def rework_trame(trame: Dict[str, str]) -> Trame: fmt = trame["format"] parts = re.split(r"\[[ifs]:[^\]]+\]", fmt) - parts = [p for p in parts if p] # remove empty strings + # parts = [p for p in parts if p] # remove empty strings param_matches = re.findall(r"\[([ifs]):([^\]]+)\]", fmt) parameters: List[TrameParam] = [ @@ -88,6 +88,15 @@ def main(): trames_formats = json.load(f) trames = [rework_trame(trame) for trame in trames_formats] + # Deduplicate names + name_counts = {} + for trame in trames: + original_name = trame["name"] + count = name_counts.get(original_name, 0) + 1 + name_counts[original_name] = count + if count > 1: + trame["name"] = f"{original_name}_{count}" + env = Environment( loader=FileSystemLoader(TEMPLATES_DIR), trim_blocks=True, diff --git a/generators/templates/flutter/trame_builder.dart.jinja b/generators/templates/flutter/trame_builder.dart.jinja new file mode 100644 index 0000000..c3453f2 --- /dev/null +++ b/generators/templates/flutter/trame_builder.dart.jinja @@ -0,0 +1,31 @@ +// Auto-generated primitives for ilo communication +// This file is generated. Do not edit manually. + +class TrameBuilder { +{% set type_map = { + "boolean": "bool", + "integer": "int", + "string": "String", + "float": "double" +} %} + +{% for t in trames %} + /// {{ t.doc if t.doc else ("Primitive for " ~ t.name) }} + static String {{ t.name }}( + {%- for p in t.parameters -%} + {{ type_map[p.type] }} {{ p.name }} + {%- if not loop.last -%}, {% endif %} + {%- endfor -%} + ) { + StringBuffer buffer = StringBuffer(); + {# emit parts #} + {%- for i in range(t.trame_parts|length) %} + buffer.write("{{ t.trame_parts[i] }}"); + {% if i < t.parameters|length %} + buffer.write({{ t.parameters[i].name }}); + {% endif %} + {%- endfor %} + return buffer.toString(); + } +{% endfor %} +}