diff --git a/CMakeLists.txt b/CMakeLists.txt index 27c40a7..94f1500 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -32,7 +32,9 @@ set(PROJECT_SOURCES mainwindow.ui src/qt_serial_datachannel.cpp - src/electrocardiograph_tester.cpp + + iflytop_canbus/iflytop_canbus_master.cpp + iflytop_canbus/waveshare_can.cpp ) diff --git a/doc/waveshare-can/CAN配置命令.pdf b/doc/waveshare-can/CAN配置命令.pdf new file mode 100644 index 0000000..514e877 Binary files /dev/null and b/doc/waveshare-can/CAN配置命令.pdf differ diff --git a/doc/waveshare-can/USB-CAN-A.zip b/doc/waveshare-can/USB-CAN-A.zip new file mode 100644 index 0000000..9043134 Binary files /dev/null and b/doc/waveshare-can/USB-CAN-A.zip differ diff --git a/doc/waveshare-can/USB-CAN-A/.gitignore b/doc/waveshare-can/USB-CAN-A/.gitignore new file mode 100644 index 0000000..69baf21 --- /dev/null +++ b/doc/waveshare-can/USB-CAN-A/.gitignore @@ -0,0 +1 @@ +canusb diff --git a/doc/waveshare-can/USB-CAN-A/LICENSE b/doc/waveshare-can/USB-CAN-A/LICENSE new file mode 100644 index 0000000..ed71abf --- /dev/null +++ b/doc/waveshare-can/USB-CAN-A/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2018 Kjetil Erga + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/doc/waveshare-can/USB-CAN-A/README.md b/doc/waveshare-can/USB-CAN-A/README.md new file mode 100644 index 0000000..125bfec --- /dev/null +++ b/doc/waveshare-can/USB-CAN-A/README.md @@ -0,0 +1,47 @@ +# USB-CAN Analyzer Linux Support + +This is a small C program that dumps the CAN traffic for one these adapters: +![alt text](USB-CAN.jpg) + +These adapters can be found everywhere on Ebay nowadays, but there is no official Linux support. Only a Windows binary file [stored directly on GitHub](https://github.com/SeeedDocument/USB-CAN_Analyzer). + +When plugged in, it will show something like this: +``` +Bus 002 Device 006: ID 1a86:7523 QinHeng Electronics HL-340 USB-Serial adapter +``` +And the whole thing is actually a USB to serial converter, for which Linux will provide the 'ch341-uart' driver and create a new /dev/ttyUSB device. So this program simply implements part of that serial protocol. + +## Build + +`canusb.c` can be compile just by running `make` or with: + +``` +$ gcc canusb.c -o canusb +``` +## Usage + +``` +$ ./canusb -h +Usage: ./canusb +Options: + -h Display this help and exit. + -t Print TTY/serial traffic debugging info on stderr. + -d DEVICE Use TTY DEVICE. + -s SPEED Set CAN SPEED in bps. + -b BAUDRATE Set TTY/serial BAUDRATE (default: 2000000). + -i ID Inject using ID (specified as hex string). + -j DATA CAN DATA to inject (specified as hex string). + -n COUNT Terminate after COUNT frames (default: infinite). + -g MS Inject sleep gap in MS milliseconds (default: 200 ms). + -m MODE Inject payload MODE (0 = random, 1 = incremental, 2 = fixed). +``` + + +## Example commands: +``` +# dump CAN bus traffic from 1 Mbit CAN bus +$ ./canusb -t -d /dev/ttyUSB0 -s 1000000 -t + +# send the bytes 0xBEEE from ID 005 on at 1 Mbit CAN bus +$ ./canusb -d /dev/ttyUSB0 -s 1000000 -t -i 5 -j BEEE +``` diff --git a/doc/waveshare-can/USB-CAN-A/canusb.c b/doc/waveshare-can/USB-CAN-A/canusb.c new file mode 100644 index 0000000..d1aaba2 --- /dev/null +++ b/doc/waveshare-can/USB-CAN-A/canusb.c @@ -0,0 +1,629 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* struct termios2 */ +#include +#include +#include +#include + +#define CANUSB_INJECT_SLEEP_GAP_DEFAULT 200 /* ms */ +#define CANUSB_TTY_BAUD_RATE_DEFAULT 2000000 + +typedef enum { + CANUSB_SPEED_1000000 = 0x01, + CANUSB_SPEED_800000 = 0x02, + CANUSB_SPEED_500000 = 0x03, + CANUSB_SPEED_400000 = 0x04, + CANUSB_SPEED_250000 = 0x05, + CANUSB_SPEED_200000 = 0x06, + CANUSB_SPEED_125000 = 0x07, + CANUSB_SPEED_100000 = 0x08, + CANUSB_SPEED_50000 = 0x09, + CANUSB_SPEED_20000 = 0x0a, + CANUSB_SPEED_10000 = 0x0b, + CANUSB_SPEED_5000 = 0x0c, +} CANUSB_SPEED; + +typedef enum { + CANUSB_MODE_NORMAL = 0x00, + CANUSB_MODE_LOOPBACK = 0x01, + CANUSB_MODE_SILENT = 0x02, + CANUSB_MODE_LOOPBACK_SILENT = 0x03, +} CANUSB_MODE; + +typedef enum { + CANUSB_FRAME_STANDARD = 0x01, + CANUSB_FRAME_EXTENDED = 0x02, +} CANUSB_FRAME; + +typedef enum { + CANUSB_INJECT_PAYLOAD_MODE_RANDOM = 0, + CANUSB_INJECT_PAYLOAD_MODE_INCREMENTAL = 1, + CANUSB_INJECT_PAYLOAD_MODE_FIXED = 2, +} CANUSB_PAYLOAD_MODE; + + + +static int terminate_after = 0; +static int program_running = 1; +static int inject_payload_mode = CANUSB_INJECT_PAYLOAD_MODE_FIXED; +static float inject_sleep_gap = CANUSB_INJECT_SLEEP_GAP_DEFAULT; +static int print_traffic = 0; + + + +static CANUSB_SPEED canusb_int_to_speed(int speed) +{ + switch (speed) { + case 1000000: + return CANUSB_SPEED_1000000; + case 800000: + return CANUSB_SPEED_800000; + case 500000: + return CANUSB_SPEED_500000; + case 400000: + return CANUSB_SPEED_400000; + case 250000: + return CANUSB_SPEED_250000; + case 200000: + return CANUSB_SPEED_200000; + case 125000: + return CANUSB_SPEED_125000; + case 100000: + return CANUSB_SPEED_100000; + case 50000: + return CANUSB_SPEED_50000; + case 20000: + return CANUSB_SPEED_20000; + case 10000: + return CANUSB_SPEED_10000; + case 5000: + return CANUSB_SPEED_5000; + default: + return 0; + } +} + + + +static int generate_checksum(const unsigned char *data, int data_len) +{ + int i, checksum; + + checksum = 0; + for (i = 0; i < data_len; i++) { + checksum += data[i]; + } + + return checksum & 0xff; +} + + + +static int frame_is_complete(const unsigned char *frame, int frame_len) +{ + if (frame_len > 0) { + if (frame[0] != 0xaa) { + /* Need to sync on 0xaa at start of frames, so just skip. */ + return 1; + } + } + + if (frame_len < 2) { + return 0; + } + + if (frame[1] == 0x55) { /* Command frame... */ + if (frame_len >= 20) { /* ...always 20 bytes. */ + return 1; + } else { + return 0; + } + } else if ((frame[1] >> 4) == 0xc) { /* Data frame... */ + if (frame_len >= (frame[1] & 0xf) + 5) { /* ...payload and 5 bytes. */ + return 1; + } else { + return 0; + } + } + + /* Unhandled frame type. */ + return 1; +} + + + +static int frame_send(int tty_fd, const unsigned char *frame, int frame_len) +{ + int result, i; + + if (print_traffic) { + printf(">>> "); + for (i = 0; i < frame_len; i++) { + printf("%02x ", frame[i]); + } + if (print_traffic > 1) { + printf(" '"); + for (i = 4; i < frame_len - 1; i++) { + printf("%c", isalnum(frame[i]) ? frame[i] : '.'); + } + printf("'"); + } + printf("\n"); + } + + result = write(tty_fd, frame, frame_len); + if (result == -1) { + fprintf(stderr, "write() failed: %s\n", strerror(errno)); + return -1; + } + + return frame_len; +} + + + +static int frame_recv(int tty_fd, unsigned char *frame, int frame_len_max) +{ + int result, frame_len, checksum; + unsigned char byte; + + if (print_traffic) + fprintf(stderr, "<<< "); + + frame_len = 0; + while (program_running) { + result = read(tty_fd, &byte, 1); + if (result == -1) { + if (errno != EAGAIN && errno != EWOULDBLOCK) { + fprintf(stderr, "read() failed: %s\n", strerror(errno)); + return -1; + } + + } else if (result > 0) { + if (print_traffic) + fprintf(stderr, "%02x ", byte); + + if (frame_len == frame_len_max) { + fprintf(stderr, "frame_recv() failed: Overflow\n"); + return -1; + } + + frame[frame_len++] = byte; + + if (frame_is_complete(frame, frame_len)) { + break; + } + } + + usleep(10); + } + + if (print_traffic) + fprintf(stderr, "\n"); + + /* Compare checksum for command frames only. */ + if ((frame_len == 20) && (frame[0] == 0xaa) && (frame[1] == 0x55)) { + checksum = generate_checksum(&frame[2], 17); + if (checksum != frame[frame_len - 1]) { + fprintf(stderr, "frame_recv() failed: Checksum incorrect\n"); + return -1; + } + } + + return frame_len; +} + + + +static int command_settings(int tty_fd, CANUSB_SPEED speed, CANUSB_MODE mode, CANUSB_FRAME frame) +{ + int cmd_frame_len; + unsigned char cmd_frame[20]; + + cmd_frame_len = 0; + cmd_frame[cmd_frame_len++] = 0xaa; + cmd_frame[cmd_frame_len++] = 0x55; + cmd_frame[cmd_frame_len++] = 0x12; + cmd_frame[cmd_frame_len++] = speed; + cmd_frame[cmd_frame_len++] = frame; + cmd_frame[cmd_frame_len++] = 0; /* Filter ID not handled. */ + cmd_frame[cmd_frame_len++] = 0; /* Filter ID not handled. */ + cmd_frame[cmd_frame_len++] = 0; /* Filter ID not handled. */ + cmd_frame[cmd_frame_len++] = 0; /* Filter ID not handled. */ + cmd_frame[cmd_frame_len++] = 0; /* Mask ID not handled. */ + cmd_frame[cmd_frame_len++] = 0; /* Mask ID not handled. */ + cmd_frame[cmd_frame_len++] = 0; /* Mask ID not handled. */ + cmd_frame[cmd_frame_len++] = 0; /* Mask ID not handled. */ + cmd_frame[cmd_frame_len++] = mode; + cmd_frame[cmd_frame_len++] = 0x01; + cmd_frame[cmd_frame_len++] = 0; + cmd_frame[cmd_frame_len++] = 0; + cmd_frame[cmd_frame_len++] = 0; + cmd_frame[cmd_frame_len++] = 0; + cmd_frame[cmd_frame_len++] = generate_checksum(&cmd_frame[2], 17); + + if (frame_send(tty_fd, cmd_frame, cmd_frame_len) < 0) { + return -1; + } + + return 0; +} + + + +static int send_data_frame(int tty_fd, CANUSB_FRAME frame, unsigned char id_lsb, unsigned char id_msb, unsigned char data[], int data_length_code) +{ +#define MAX_FRAME_SIZE 13 + int data_frame_len = 0; + unsigned char data_frame[MAX_FRAME_SIZE] = {0x00}; + + if (data_length_code < 0 || data_length_code > 8) + { + fprintf(stderr, "Data length code (DLC) must be between 0 and 8!\n"); + return -1; + } + + /* Byte 0: Packet Start */ + data_frame[data_frame_len++] = 0xaa; + + /* Byte 1: CAN Bus Data Frame Information */ + data_frame[data_frame_len] = 0x00; + data_frame[data_frame_len] |= 0xC0; /* Bit 7 Always 1, Bit 6 Always 1 */ + if (frame == CANUSB_FRAME_STANDARD) + data_frame[data_frame_len] &= 0xDF; /* STD frame */ + else /* CANUSB_FRAME_EXTENDED */ + data_frame[data_frame_len] |= 0x20; /* EXT frame */ + data_frame[data_frame_len] &= 0xEF; /* 0=Data */ + data_frame[data_frame_len] |= data_length_code; /* DLC=data_len */ + data_frame_len++; + + /* Byte 2 to 3: ID */ + data_frame[data_frame_len++] = id_lsb; /* lsb */ + data_frame[data_frame_len++] = id_msb; /* msb */ + + /* Byte 4 to (4+data_len): Data */ + for (int i = 0; i < data_length_code; i++) + data_frame[data_frame_len++] = data[i]; + + /* Last byte: End of frame */ + data_frame[data_frame_len++] = 0x55; + + if (frame_send(tty_fd, data_frame, data_frame_len) < 0) + { + fprintf(stderr, "Unable to send frame!\n"); + return -1; + } + + return 0; +} + + + +static int hex_value(int c) +{ + if (c >= 0x30 && c <= 0x39) /* '0' - '9' */ + return c - 0x30; + else if (c >= 0x41 && c <= 0x46) /* 'A' - 'F' */ + return (c - 0x41) + 10; + else if (c >= 0x61 && c <= 0x66) /* 'a' - 'f' */ + return (c - 0x61) + 10; + else + return -1; +} + + + +static int convert_from_hex(const char *hex_string, unsigned char *bin_string, int bin_string_len) +{ + int n1, n2, high; + + high = -1; + n1 = n2 = 0; + while (hex_string[n1] != '\0') { + if (hex_value(hex_string[n1]) >= 0) { + if (high == -1) { + high = hex_string[n1]; + } else { + bin_string[n2] = hex_value(high) * 16 + hex_value(hex_string[n1]); + n2++; + if (n2 >= bin_string_len) { + printf("hex string truncated to %d bytes\n", n2); + break; + } + high = -1; + } + } + n1++; + } + + return n2; +} + + + +static int inject_data_frame(int tty_fd, const char *hex_id, const char *hex_data) +{ + int data_len; + unsigned char binary_data[8]; + unsigned char binary_id_lsb = 0, binary_id_msb = 0; + struct timespec gap_ts; + struct timeval now; + int error = 0; + + gap_ts.tv_sec = inject_sleep_gap / 1000; + gap_ts.tv_nsec = (long)(((long long)(inject_sleep_gap * 1000000)) % 1000000000LL); + + /* Set seed value for pseudo random numbers. */ + gettimeofday(&now, NULL); + srandom(now.tv_usec); + + data_len = convert_from_hex(hex_data, binary_data, sizeof(binary_data)); + if (data_len == 0) { + fprintf(stderr, "Unable to convert data from hex to binary!\n"); + return -1; + } + + switch (strlen(hex_id)) { + case 1: + binary_id_lsb = hex_value(hex_id[0]); + break; + + case 2: + binary_id_lsb = (hex_value(hex_id[0]) * 16) + hex_value(hex_id[1]); + break; + + case 3: + binary_id_msb = hex_value(hex_id[0]); + binary_id_lsb = (hex_value(hex_id[1]) * 16) + hex_value(hex_id[2]); + break; + + default: + fprintf(stderr, "Unable to convert ID from hex to binary!\n"); + return -1; + } + + while (program_running && ! error) { + if (gap_ts.tv_sec || gap_ts.tv_nsec) + nanosleep(&gap_ts, NULL); + + if (terminate_after && (--terminate_after == 0)) + program_running = 0; + + if (inject_payload_mode == CANUSB_INJECT_PAYLOAD_MODE_RANDOM) { + int i; + for (i = 0; i < data_len; i++) + binary_data[i] = random(); + } else if (inject_payload_mode == CANUSB_INJECT_PAYLOAD_MODE_INCREMENTAL) { + int i; + for (i = 0; i < data_len; i++) + binary_data[i]++; + } + + error = send_data_frame(tty_fd, CANUSB_FRAME_STANDARD, binary_id_lsb, binary_id_msb, binary_data, data_len); + } + + return error; +} + + + +static void dump_data_frames(int tty_fd) +{ + int i, frame_len; + unsigned char frame[32]; + struct timespec ts; + + while (program_running) { + frame_len = frame_recv(tty_fd, frame, sizeof(frame)); + + if (! program_running) + break; + + clock_gettime(CLOCK_MONOTONIC, &ts); + printf("%lu.%06lu ", ts.tv_sec, ts.tv_nsec / 1000); + + if (frame_len == -1) { + printf("Frame recieve error!\n"); + + } else { + + if ((frame_len >= 6) && + (frame[0] == 0xaa) && + ((frame[1] >> 4) == 0xc)) { + printf("Frame ID: %02x%02x, Data: ", frame[3], frame[2]); + for (i = frame_len - 2; i > 3; i--) { + printf("%02x ", frame[i]); + } + printf("\n"); + + } else { + printf("Unknown: "); + for (i = 0; i <= frame_len; i++) { + printf("%02x ", frame[i]); + } + printf("\n"); + } + } + + if (terminate_after && (--terminate_after == 0)) + program_running = 0; + } +} + + + +static int adapter_init(const char *tty_device, int baudrate) +{ + int tty_fd, result; + struct termios2 tio; + + tty_fd = open(tty_device, O_RDWR | O_NOCTTY | O_NONBLOCK); + if (tty_fd == -1) { + fprintf(stderr, "open(%s) failed: %s\n", tty_device, strerror(errno)); + return -1; + } + + result = ioctl(tty_fd, TCGETS2, &tio); + if (result == -1) { + fprintf(stderr, "ioctl() failed: %s\n", strerror(errno)); + close(tty_fd); + return -1; + } + + tio.c_cflag &= ~CBAUD; + tio.c_cflag = BOTHER | CS8 | CSTOPB; + tio.c_iflag = IGNPAR; + tio.c_oflag = 0; + tio.c_lflag = 0; + tio.c_ispeed = baudrate; + tio.c_ospeed = baudrate; + + result = ioctl(tty_fd, TCSETS2, &tio); + if (result == -1) { + fprintf(stderr, "ioctl() failed: %s\n", strerror(errno)); + close(tty_fd); + return -1; + } + + return tty_fd; +} + + + +static void display_help(const char *progname) +{ + fprintf(stderr, "Usage: %s \n", progname); + fprintf(stderr, "Options:\n" + " -h Display this help and exit.\n" + " -t Print TTY/serial traffic debugging info on stderr.\n" + " -d DEVICE Use TTY DEVICE.\n" + " -s SPEED Set CAN SPEED in bps.\n" + " -b BAUDRATE Set TTY/serial BAUDRATE (default: %d).\n" + " -i ID Inject using ID (specified as hex string).\n" + " -j DATA CAN DATA to inject (specified as hex string).\n" + " -n COUNT Terminate after COUNT frames (default: infinite).\n" + " -g MS Inject sleep gap in MS milliseconds (default: %d ms).\n" + " -m MODE Inject payload MODE (%d = random, %d = incremental, %d = fixed).\n" + "\n", + CANUSB_TTY_BAUD_RATE_DEFAULT, + CANUSB_INJECT_SLEEP_GAP_DEFAULT, + CANUSB_INJECT_PAYLOAD_MODE_RANDOM, + CANUSB_INJECT_PAYLOAD_MODE_INCREMENTAL, + CANUSB_INJECT_PAYLOAD_MODE_FIXED); +} + + + +static void sigterm(int signo) +{ + program_running = 0; +} + + + +int main(int argc, char *argv[]) +{ + int c, tty_fd; + char *tty_device = NULL, *inject_data = NULL, *inject_id = NULL; + CANUSB_SPEED speed = 0; + int baudrate = CANUSB_TTY_BAUD_RATE_DEFAULT; + + while ((c = getopt(argc, argv, "htd:s:b:i:j:n:g:m:")) != -1) { + switch (c) { + case 'h': + display_help(argv[0]); + return EXIT_SUCCESS; + + case 't': + print_traffic++; + break; + + case 'd': + tty_device = optarg; + break; + + case 's': + speed = canusb_int_to_speed(atoi(optarg)); + break; + + case 'b': + baudrate = atoi(optarg); + break; + + case 'i': + inject_id = optarg; + break; + + case 'j': + inject_data = optarg; + break; + + case 'n': + terminate_after = atoi(optarg); + break; + + case 'g': + inject_sleep_gap = strtof(optarg, NULL); + break; + + case 'm': + inject_payload_mode = atoi(optarg); + break; + + case '?': + default: + display_help(argv[0]); + return EXIT_FAILURE; + } + } + + signal(SIGTERM, sigterm); + signal(SIGHUP, sigterm); + signal(SIGINT, sigterm); + + if (tty_device == NULL) { + fprintf(stderr, "Please specify a TTY!\n"); + display_help(argv[0]); + return EXIT_FAILURE; + } + + if (speed == 0) { + fprintf(stderr, "Please specify a valid speed!\n"); + display_help(argv[0]); + return EXIT_FAILURE; + } + + tty_fd = adapter_init(tty_device, baudrate); + if (tty_fd == -1) { + return EXIT_FAILURE; + } + + command_settings(tty_fd, speed, CANUSB_MODE_NORMAL, CANUSB_FRAME_STANDARD); + + if (inject_data == NULL) { + /* Dumping mode (default). */ + dump_data_frames(tty_fd); + } else { + /* Inject mode. */ + if (inject_id == NULL) { + fprintf(stderr, "Please specify a ID for injection!\n"); + display_help(argv[0]); + return EXIT_FAILURE; + } + if (inject_data_frame(tty_fd, inject_id, inject_data) == -1) { + return EXIT_FAILURE; + } else { + return EXIT_SUCCESS; + } + } + + return EXIT_SUCCESS; +} diff --git a/doc/waveshare-can/USB_(Serial_port)_to_CAN_protocol_defines.pdf b/doc/waveshare-can/USB_(Serial_port)_to_CAN_protocol_defines.pdf new file mode 100644 index 0000000..798a2ad Binary files /dev/null and b/doc/waveshare-can/USB_(Serial_port)_to_CAN_protocol_defines.pdf differ diff --git a/iflytop_canbus/cmdid.hpp b/iflytop_canbus/cmdid.hpp new file mode 100644 index 0000000..60a5177 --- /dev/null +++ b/iflytop_canbus/cmdid.hpp @@ -0,0 +1,163 @@ +#pragma once +#define CMDID(cmdid, cmdSubId) ((cmdid << 8) + cmdSubId) + +namespace iflytop { +namespace zcr { +typedef enum { +#if 0 + + virtual int32_t board_reset() = 0; +#endif + + kboard_reset = CMDID(0, 0), // para:{}, ack:{} + + kevent_bus_reg_change_report = CMDID(0, 100), // para:{}, ack:{} +#if 0 + virtual int32_t module_get_state(int32_t state_id, int32_t *state_value) { return err::koperation_not_support; } + virtual int32_t module_read_raw(int32_t startadd, int32_t *data, int32_t *len) { return err::koperation_not_support; } + virtual int32_t module_enable(int32_t enable) { return err::koperation_not_support; } + virtual int32_t module_start() { return err::koperation_not_support; } +#endif + kmodule_ping = CMDID(1, 0), // para:{}, ack:{} + kmodule_stop = CMDID(1, 1), // para:{}, ack:{} + kmodule_break = CMDID(1, 2), // para:{}, ack:{} + kmodule_get_last_exec_status = CMDID(1, 3), // para:{}, ack:{4} + kmodule_get_status = CMDID(1, 4), // para:{}, ack:{4} + kmodule_set_reg = CMDID(1, 5), // para:{4,4}, ack:{} + kmodule_get_reg = CMDID(1, 6), // para:{4}, ack:{4}I + kmodule_readio = CMDID(1, 7), // para:{}, ack:{4} + kmodule_writeio = CMDID(1, 8), // para:{4}, ack:{} + kmodule_read_adc = CMDID(1, 9), // para:{4}, ack:{4} + kmodule_get_error = CMDID(1, 10), // para:{}, ack:{1} + kmodule_clear_error = CMDID(1, 11), // para:{}, ack:{} + kmodule_set_inited_flag = CMDID(1, 12), // para:{4}, ack:{} + kmodule_get_inited_flag = CMDID(1, 13), // para:{}, ack:{4} + kmodule_factory_reset = CMDID(1, 14), // para:{}, ack:{} + kmodule_flush_cfg = CMDID(1, 15), // para:{}, ack:{} + kmodule_active_cfg = CMDID(1, 16), // para:{}, ack:{} + kmodule_read_raw = CMDID(1, 19), // para:{4,4}, ack:{4} + kmodule_enable = CMDID(1, 20), // para:{4}, ack:{} + kmodule_start = CMDID(1, 21), // para:{4}, ack:{} + +#if 0 + virtual int32_t motor_enable(int32_t enable) { return err::koperation_not_support; } + virtual int32_t motor_rotate(int32_t direction, int32_t motor_velocity, int32_t acc) { return err::koperation_not_support; } + virtual int32_t motor_move_by(int32_t distance, int32_t motor_velocity, int32_t acc) { return err::koperation_not_support; } + virtual int32_t motor_move_to(int32_t position, int32_t motor_velocity, int32_t acc) { return err::koperation_not_support; } + + virtual int32_t motor_rotate_acctime(int32_t direction, int32_t motor_velocity, int32_t acctime) { return err::koperation_not_support; } + virtual int32_t motor_move_by_acctime(int32_t distance, int32_t motor_velocity, int32_t acctime) { return err::koperation_not_support; } + virtual int32_t motor_move_to_acctime(int32_t position, int32_t motor_velocity, int32_t acctime) { return err::koperation_not_support; } + + virtual int32_t motor_rotate_with_torque(int32_t direction, int32_t torque) { return err::koperation_not_support; } + virtual int32_t motor_move_to_torque(int32_t pos, int32_t torque, int32_t overtime) { return err::koperation_not_support; } + + virtual int32_t motor_move_to_zero_forward(int32_t findzerospeed, int32_t findzeroedge_speed, int32_t acc, int32_t overtime) { return err::koperation_not_support; } + virtual int32_t motor_move_to_zero_backward(int32_t findzerospeed, int32_t findzeroedge_speed, int32_t acc, int32_t overtime) { return err::koperation_not_support; } + + virtual int32_t motor_move_to_zero_forward_and_calculated_shift(int32_t findzerospeed, int32_t findzeroedge_speed, int32_t acc, int32_t overtime) { return err::koperation_not_support; } + virtual int32_t motor_move_to_zero_backward_and_calculated_shift(int32_t findzerospeed, int32_t findzeroedge_speed, int32_t acc, int32_t overtime) { return err::koperation_not_support; } + + virtual int32_t motor_read_pos(int32_t* pos) { return err::koperation_not_support; } + virtual int32_t motor_set_current_pos_by_change_shift(int32_t pos) { return err::koperation_not_support; } // 一般用于舵机 + + virtual int32_t motor_easy_rotate(int32_t direction) { return err::koperation_not_support; }; + virtual int32_t motor_easy_move_by(int32_t distance) { return err::koperation_not_support; }; + virtual int32_t motor_easy_move_to(int32_t position) { return err::koperation_not_support; }; + virtual int32_t motor_easy_move_to_zero(int32_t direction) { return err::koperation_not_support; }; + virtual int32_t motor_easy_move_to_io(int32_t ioindex, int32_t direction) { return err::koperation_not_support; }; + +#endif + + kmotor_enable = CMDID(2, 1), // para:{1}, ack:{} + kmotor_rotate = CMDID(2, 2), // para:{1,4}, ack:{} + kmotor_move_by = CMDID(2, 3), // para:{4,4}, ack:{} + kmotor_move_to = CMDID(2, 4), // para:{4,4}, ack:{} + kmotor_rotate_acctime = CMDID(2, 5), // para:{4,4}, ack:{} + kmotor_move_by_acctime = CMDID(2, 6), // para:{4,4}, ack:{} + kmotor_move_to_acctime = CMDID(2, 7), // para:{4,4}, ack:{} + kmotor_rotate_with_torque = CMDID(2, 8), // para:{4,4}, ack:{} + kmotor_move_to_zero_forward = CMDID(2, 9), // para:{4,4,4,4}, ack:{} //int32_t findzerospeed, int32_t findzeroedge_speed, int32_t acc, int32_t overtime + kmotor_move_to_zero_backward = CMDID(2, 10), // para:{4,4,4,4}, ack:{} //int32_t findzerospeed, int32_t findzeroedge_speed, int32_t acc, int32_t overtime + kmotor_read_pos = CMDID(2, 11), // para:{}, ack:{4} + kmotor_set_current_pos_by_change_shift = CMDID(2, 12), // para:{4}, ack:{} + kmotor_motor_move_to_zero_forward_and_calculated_shift = CMDID(2, 13), // para:{4,4,4,4}, ack:{} //int32_t findzerospeed, int32_t findzeroedge_speed, int32_t acc, int32_t overtime + kmotor_motor_move_to_zero_backward_and_calculated_shift = CMDID(2, 14), // para:{4,4,4,4}, ack:{} //int32_t findzerospeed, int32_t findzeroedge_speed, int32_t acc, int32_t overtime + kmotor_move_to_torque = CMDID(2, 15), // para:{4,4,4}, ack:{} + kmotor_calculated_pos_by_move_to_zero = CMDID(2, 16), // para:{}, ack:{} + + kmotor_easy_rotate = CMDID(2, 17), // para:{4}, ack:{} + kmotor_easy_move_by = CMDID(2, 18), // para:{4}, ack:{} + kmotor_easy_move_to = CMDID(2, 19), // para:{4}, ack:{} + kmotor_easy_move_to_zero = CMDID(2, 20), // para:{1}, ack:{} + kmotor_easy_set_current_pos = CMDID(2, 21), // para:{4}, ack:{} + kmotor_easy_move_to_io = CMDID(2, 22), // para:{4,4}, ack:{} + + kmotor_set_subdevice_reg = CMDID(2, 23), // para:{4,4}, ack:{} + kmotor_get_subdevice_reg = CMDID(2, 24), // para:{4,4}, ack:{} + + kmotor_read_enc_val = CMDID(2, 25), // para:{}, ack:{4} + kmotor_set_enc_resolution = CMDID(2, 26), // para:{4}, ack:{} + kmotor_get_enc_resolution = CMDID(2, 27), // para:{}, ack:{4} + +#if 0 + virtual ~ZIXYMotor() {} + virtual int32_t xymotor_enable(int32_t enable) { return err::koperation_not_support; } + virtual int32_t xymotor_move_by(int32_t dx, int32_t dy, int32_t motor_velocity) { return err::koperation_not_support; } + virtual int32_t xymotor_move_to(int32_t x, int32_t y, int32_t motor_velocity) { return err::koperation_not_support; } + virtual int32_t xymotor_move_to_zero() { return err::koperation_not_support; } + virtual int32_t xymotor_move_to_zero_and_calculated_shift() { return err::koperation_not_support; } + virtual int32_t xymotor_read_pos(int32_t *x, int32_t *y) { return err::koperation_not_support; } + virtual int32_t xymotor_calculated_pos_by_move_to_zero() { return err::koperation_not_support; } +#endif + kxymotor_enable = CMDID(3, 1), // para:{1}, ack:{} + kxymotor_move_by = CMDID(3, 2), // para:{4,4,4}, ack:{} + kxymotor_move_to = CMDID(3, 3), // para:{4,4,4}, ack:{} + kxymotor_move_to_zero = CMDID(3, 4), // para:{}, ack:{} + kxymotor_move_to_zero_and_calculated_shift = CMDID(3, 5), // para:{4,4,4,4}, ack:{} //int32_t findzerospeed, int32_t findzeroedge_speed, int32_t acc, int32_t overtime + kxymotor_read_pos = CMDID(3, 6), // para:{}, ack:{4,4} + kxymotor_calculated_pos_by_move_to_zero = CMDID(3, 7), // para:{}, ack:{} + +#if 0 + virtual int32_t code_scaner_start_scan() { return err::koperation_not_support; } + virtual int32_t code_scaner_stop_scan() { return err::koperation_not_support; } + virtual int32_t code_scaner_read_scaner_result(int32_t startadd, uint8_t *data, int32_t *len) { return err::koperation_not_support; } +#endif + + kcode_scaner_start_scan = CMDID(4, 1), // para:{}, ack:{} + kcode_scaner_stop_scan = CMDID(4, 2), // para:{}, ack:{} + kcode_scaner_read_scaner_result = CMDID(4, 3), // para:{4,4}, ack:{4} + +#if 0 + virtual int32_t pipette_ctrl_init_device() { return err::koperation_not_support; }; + virtual int32_t pipette_ctrl_put_tip() { return err::koperation_not_support; }; + virtual int32_t pipette_ctrl_move_to_ul(int32_t ul) { return err::koperation_not_support; }; +#endif + + kpipette_ctrl_init_device = CMDID(5, 1), // para:{}, ack:{} + kpipette_ctrl_put_tip = CMDID(5, 2), // para:{}, ack:{} + kpipette_ctrl_move_to_ul = CMDID(5, 3), // para:{4}, ack:{} + +#if 0 + virtual int32_t a8000_optical_module_power_ctrl(int32_t state) = 0; + virtual int32_t a8000_optical_open_laser(int32_t type) = 0; + virtual int32_t a8000_optical_close_laser(int32_t type) = 0; + virtual int32_t a8000_optical_set_laster_gain(int32_t type, int32_t gain) = 0; + virtual int32_t a8000_optical_set_scan_amp_gain(int32_t type, int32_t gain) = 0; + virtual int32_t a8000_optical_read_scanner_adc_val(int32_t type, int32_t* adcval) = 0; + virtual int32_t a8000_optical_read_laster_adc_val(int32_t type, int32_t* adcval) = 0; + virtual int32_t a8000_optical_scan_current_point_amp_adc_val(int32_t type, int32_t lastergain, int32_t ampgain, int32_t* laster_fb_val, int32_t* adcval) = 0; +#endif + ka8000_optical_module_power_ctrl = CMDID(6, 0), // para:{4}, ack:{} + ka8000_optical_open_laser = CMDID(6, 1), // para:{4}, ack:{} + ka8000_optical_close_laser = CMDID(6, 2), // para:{4}, ack:{} + ka8000_optical_set_laster_gain = CMDID(6, 3), // para:{4,4}, ack:{} + ka8000_optical_set_scan_amp_gain = CMDID(6, 4), // para:{4,4}, ack:{} + ka8000_optical_read_scanner_adc_val = CMDID(6, 5), // para:{4}, ack:{4} + ka8000_optical_read_laster_adc_val = CMDID(6, 6), // para:{4}, ack:{4} + ka8000_optical_scan_current_point_amp_adc_val = CMDID(6, 7), // para:{4,4,4,4}, ack:{4,4} + +} cmdid_t; + +} // namespace zcr +} // namespace iflytop \ No newline at end of file diff --git a/iflytop_canbus/iflytop_canbus_frame.hpp b/iflytop_canbus/iflytop_canbus_frame.hpp new file mode 100644 index 0000000..15c3b2c --- /dev/null +++ b/iflytop_canbus/iflytop_canbus_frame.hpp @@ -0,0 +1,25 @@ +#pragma once +#include + +namespace iflytop { +namespace zcr { +#pragma pack(push, 1) +typedef struct { + uint16_t packetindex; + uint16_t cmd_main_id; // cmd main id + uint8_t cmd_sub_id; // cmd sub id + uint8_t packet_type; // + uint16_t device_id; + uint8_t data[]; +} iflytop_canbus_frame_t; +#pragma pack(pop) + +typedef enum { + kptv2_cmd = 0, + kptv2_ack = 1, + kptv2_error_ack = 2, + kptv2_event = 3, +} iflytop_canbus_frame_packet_type_t; + +} // namespace zcr +} // namespace iflytop \ No newline at end of file diff --git a/iflytop_canbus/iflytop_canbus_master.cpp b/iflytop_canbus/iflytop_canbus_master.cpp index e69de29..8c9e7f1 100644 --- a/iflytop_canbus/iflytop_canbus_master.cpp +++ b/iflytop_canbus/iflytop_canbus_master.cpp @@ -0,0 +1,93 @@ +#include "iflytop_canbus_master.hpp" + +#include + +#include "logger.hpp" +#include "zexception.hpp" +using namespace iflytop; +using namespace zcr; + +#define TAG "IflytopCanbusMaster" + +IflytopCanbusMaster *IflytopCanbusMaster::ins() { + static IflytopCanbusMaster *ins = new IflytopCanbusMaster(); + return ins; +} + +void IflytopCanbusMaster::initialize(IDataChannel *channel) { // + m_channel = channel; + + m_waveCan.init(channel, [this](WaveshareCan::can_rx_frame_t *canframe) { // + ZLOGI(TAG, "can rx: 0x%08x %d %s", canframe->id, canframe->dlc, zhex2str(canframe->data, canframe->dlc).c_str()); + }); +} + +void IflytopCanbusMaster::updateChannelConfig() { // + m_waveCan.setCanpPrameter(CANUSB_SPEED_500000, CANUSB_MODE_NORMAL, CANUSB_FRAME_EXTENDED); +} + +void IflytopCanbusMaster::callcmd(int32_t device_id, int32_t cmdid) { callcmd(device_id, cmdid, nullptr, 0); } + +void IflytopCanbusMaster::callcmd(int32_t device_id, int32_t cmdid, int32_t param0) { + int32_t param[4]; + param[0] = param0; + callcmd(device_id, cmdid, (uint8_t *)param, 4); +} +void IflytopCanbusMaster::callcmd(int32_t device_id, int32_t cmdid, int32_t param0, int32_t param1) { + int32_t param[4]; + param[0] = param0; + param[1] = param1; + callcmd(device_id, cmdid, (uint8_t *)param, 8); +} +void IflytopCanbusMaster::callcmd(int32_t device_id, int32_t cmdid, int32_t param0, int32_t param1, int32_t param2) { + int32_t param[4]; + param[0] = param0; + param[1] = param1; + param[2] = param2; + callcmd(device_id, cmdid, (uint8_t *)param, 12); +} + +void IflytopCanbusMaster::callcmd(int32_t device_id, int32_t cmdid, uint8_t *data, size_t len) { // + iflytop_canbus_frame_t *frame = (iflytop_canbus_frame_t *)m_txbuf; + frame->cmd_main_id = cmdid >> 8; + frame->cmd_sub_id = cmdid & 0xff; + + frame->packetindex = m_txindex; + frame->device_id = device_id; + frame->packet_type = kptv2_cmd; + if (len != 0) memcpy(frame->data, data, len); + sendframe(frame, len + sizeof(iflytop_canbus_frame_t)); + + m_txindex++; +} + +void IflytopCanbusMaster::sendframe(iflytop_canbus_frame_t *frame, size_t len) { + uint8_t *packet = (uint8_t *)frame; + + int npacket = len / 8 + (len % 8 == 0 ? 0 : 1); + if (npacket > 255) { + throw zexception(ke_invalid_packet_format, "channel is not open"); + return; + } + int finalpacketlen = len % 8 == 0 ? 8 : len % 8; + + for (uint8_t i = 0; i < npacket; i++) { + if (i == npacket - 1) { + sendsubframe(npacket, i, packet + i * 8, finalpacketlen); + } else { + sendsubframe(npacket, i, packet + i * 8, 8); + } + } +} + +void IflytopCanbusMaster::sendsubframe(int npacket, int packetIndex, uint8_t *packet, size_t len) { + uint32_t canid; + uint8_t candata[8]; + uint8_t cantxlen; + + canid = (0 << 16) | (npacket << 8) | packetIndex; + memcpy(candata, packet, len); + cantxlen = len; + + m_waveCan.sendExtframe(canid, candata, cantxlen); +} diff --git a/iflytop_canbus/iflytop_canbus_master.hpp b/iflytop_canbus/iflytop_canbus_master.hpp index e69de29..198f76b 100644 --- a/iflytop_canbus/iflytop_canbus_master.hpp +++ b/iflytop_canbus/iflytop_canbus_master.hpp @@ -0,0 +1,86 @@ +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cmdid.hpp" +#include "idatachannel.hpp" +#include "iflytop_canbus_frame.hpp" +#include "waveshare_can.hpp" + +namespace iflytop { +using namespace std; +using namespace zcr; + +class RxReceiptContext { + public: + bool waittingForReceipt; + bool receiptIsReady; + uint16_t waittingIndex; + uint8_t receipt[1024]; + size_t receiptLen; +}; + +typedef enum { + kcmd_cmd, + kcmd_receipt, + kcmd_report, + kcmd_ch4_data, +} raw_data_type_t; + +typedef function on_raw_data_t; + +class IflytopCanbusMaster { + IflytopCanbusMaster() {} + + IDataChannel *m_channel = nullptr; + + uint8_t m_rxcache[1024]; + int32_t m_rxlen = 0; + bool m_rxcache_is_full = false; + + mutex lock_; + + unique_ptr m_thread; + + RxReceiptContext m_rxReceiptContext; + mutex m_rxReceiptContext_lock; + + /******************************************************************************* + * TX CONTEXT * + *******************************************************************************/ + mutex m_tx_lock; + uint8_t m_txbuf[1024]; + uint8_t m_rxbuf[1024]; + int32_t m_rxsize; + uint8_t m_txindex = 0; + + WaveshareCan m_waveCan; + + public: + static IflytopCanbusMaster *ins(); + + void initialize(IDataChannel *channel); + void updateChannelConfig(); + + void callcmd(int32_t device_id, int32_t cmdid, uint8_t *data, size_t len); + void callcmd(int32_t device_id, int32_t cmdid); + void callcmd(int32_t device_id, int32_t cmdid, int32_t param0); + void callcmd(int32_t device_id, int32_t cmdid, int32_t param0, int32_t param1); + void callcmd(int32_t device_id, int32_t cmdid, int32_t param0, int32_t param1, int32_t param2); + + private: + void sendframe(iflytop_canbus_frame_t *frame, size_t len); + void sendsubframe(int npacket, int packetIndex, uint8_t *packet, size_t len); +}; + +} // namespace iflytop diff --git a/iflytop_canbus/regid.hpp b/iflytop_canbus/regid.hpp new file mode 100644 index 0000000..e69de29 diff --git a/iflytop_canbus/waveshare_can.cpp b/iflytop_canbus/waveshare_can.cpp new file mode 100644 index 0000000..7f1721d --- /dev/null +++ b/iflytop_canbus/waveshare_can.cpp @@ -0,0 +1,166 @@ +#include "waveshare_can.hpp" + +#include "libzqt\zexception.hpp" +#include "logger.hpp" + +using namespace iflytop; +using namespace std; +#define CANUSB_TTY_BAUD_RATE_DEFAULT 2000000 + +#define TAG "WaveshareCan" + +static int generate_checksum(const unsigned char* data, int data_len) { + int i, checksum; + + checksum = 0; + for (i = 0; i < data_len; i++) { + checksum += data[i]; + } + + return checksum & 0xff; +} + +static bool isHeader(uint8_t* data) { + if (data[0] == 0xAA && data[1] == 0x55) { + return true; + } + return false; +} + +bool WaveshareCan::init(IDataChannel* ch, frame_callback_t framecb) { + m_frame_callback = framecb; + m_ch = ch; + + m_ch->regRxListener([this](uint8_t* data, size_t len) { + { + lock_guard lock(lock_); + if (len + m_rxlen > sizeof(m_rxcache)) { + m_rxlen = 0; + } + memcpy(m_rxcache + m_rxlen, data, len); + m_rxlen += len; + } + }); + m_thread.reset(new thread([this]() { + while (true) { + this_thread::sleep_for(chrono::milliseconds(1)); + + { + lock_guard lock(lock_); + // 1.找头部 + int32_t headerpos = -1; + for (int32_t i = 0; i < m_rxlen - 1; i++) { + if (isHeader(m_rxcache + i)) { + headerpos = i; + break; + } + } + + if (headerpos == -1) { + continue; + } + + // 将包头移动到缓存头部 + if (headerpos != 0) { + memmove(m_rxcache, m_rxcache + headerpos, m_rxlen - headerpos); + m_rxlen = m_rxlen - headerpos; + continue; + } + + if (m_rxlen < 20) continue; + + onReceivePacket(m_rxcache, 20); + memmove(m_rxcache, m_rxcache + 20, m_rxlen - 20); + m_rxlen = m_rxlen - 20; + } + } + })); +} +void WaveshareCan::setCanpPrameter(CANUSB_SPEED speed, CANUSB_MODE mode, CANUSB_FRAME frame) { + int cmd_frame_len; + unsigned char cmd_frame[20]; + + cmd_frame_len = 0; + cmd_frame[0] = 0xaa; + cmd_frame[1] = 0x55; + cmd_frame[2] = 0x02; // 0x02:用固定20字节协议收发数据 0x12-设置用可变协议收发数据 + cmd_frame[3] = speed; // speed + cmd_frame[4] = frame; // 帧类型 + cmd_frame[5] = 0; /* Filter ID not handled. */ + cmd_frame[6] = 0; /* Filter ID not handled. */ + cmd_frame[7] = 0; /* Filter ID not handled. */ + cmd_frame[8] = 0; /* Filter ID not handled. */ + cmd_frame[9] = 0; /* Mask ID not handled. */ + cmd_frame[10] = 0; /* Mask ID not handled. */ + cmd_frame[11] = 0; /* Mask ID not handled. */ + cmd_frame[12] = 0; /* Mask ID not handled. */ + cmd_frame[13] = mode; // 模式 + cmd_frame[14] = 0x01; // 是否自动重发 + cmd_frame[15] = 0; // 占位,备用 + cmd_frame[16] = 0; // 占位,备用 + cmd_frame[17] = 0; // 占位,备用 + cmd_frame[18] = 0; // 占位,备用 + cmd_frame[19] = generate_checksum(&cmd_frame[2], 17); + + if (!m_ch || !m_ch->isOpen()) { + throw zexception(ke_channel_is_close, "channel is not open"); + } + m_ch->send(cmd_frame, 20); +} + +bool WaveshareCan::sendframe(CANUSB_FRAME frametype, uint32_t id, unsigned char data[], int data_length_code) { + int data_frame_len = 0; + unsigned char data_frame[20] = {0x00}; + waveshare_can_packet_t* packet = (waveshare_can_packet_t*)data_frame; + + packet->packet_header0 = 0xaa; + packet->packet_header1 = 0x55; + packet->type = 0x01; // 数据 + packet->frame_type = frametype; + packet->frame_format = 0x01; // 数据帧 + packet->frame_id_data_1 = id & 0xff; + packet->frame_id_data_2 = (id >> 8) & 0xff; + packet->frame_id_data_3 = (id >> 16) & 0xff; + packet->frame_id_data_4 = (id >> 24) & 0xff; + packet->frame_data_length = data_length_code; + packet->frame_data_1 = data[0]; + packet->frame_data_2 = data[1]; + packet->frame_data_3 = data[2]; + packet->frame_data_4 = data[3]; + packet->frame_data_5 = data[4]; + packet->frame_data_6 = data[5]; + packet->frame_data_7 = data[6]; + packet->frame_data_8 = data[7]; + packet->reserve = 0; + packet->check_code = generate_checksum(&data_frame[2], 17); + + if (!m_ch || !m_ch->isOpen()) { + throw zexception(ke_channel_is_close, "channel is not open"); + } + + ZLOGI(TAG, "tx 0x%08x, %s", id, zhex2str(data, data_length_code).c_str()); + m_ch->send(data_frame, 20); + + return true; +} + +int WaveshareCan::onReceivePacket(uint8_t* data, size_t len) { + waveshare_can_packet_t* rxpacket = (waveshare_can_packet_t*)data; + + can_rx_frame_t canframe; + canframe.dlc = rxpacket->frame_data_length; + canframe.id = rxpacket->frame_id_data_1 | ((uint32_t)rxpacket->frame_id_data_2 << 8) | ((uint32_t)rxpacket->frame_id_data_3 << 16) | ((uint32_t)rxpacket->frame_id_data_4 << 24); + + canframe.data[0] = rxpacket->frame_data_1; + canframe.data[1] = rxpacket->frame_data_2; + canframe.data[2] = rxpacket->frame_data_3; + canframe.data[3] = rxpacket->frame_data_4; + canframe.data[4] = rxpacket->frame_data_5; + canframe.data[5] = rxpacket->frame_data_6; + canframe.data[6] = rxpacket->frame_data_6; + canframe.data[7] = rxpacket->frame_data_6; + + if (m_frame_callback) { + m_frame_callback(&canframe); + } +} diff --git a/iflytop_canbus/waveshare_can.hpp b/iflytop_canbus/waveshare_can.hpp new file mode 100644 index 0000000..fa4ec0d --- /dev/null +++ b/iflytop_canbus/waveshare_can.hpp @@ -0,0 +1,110 @@ +#pragma once + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "idatachannel.hpp" +namespace iflytop { +#define CANUSB_TTY_BAUD_RATE_DEFAULT 2000000 + +typedef enum { + CANUSB_SPEED_1000000 = 0x01, + CANUSB_SPEED_800000 = 0x02, + CANUSB_SPEED_500000 = 0x03, + CANUSB_SPEED_400000 = 0x04, + CANUSB_SPEED_250000 = 0x05, + CANUSB_SPEED_200000 = 0x06, + CANUSB_SPEED_125000 = 0x07, + CANUSB_SPEED_100000 = 0x08, + CANUSB_SPEED_50000 = 0x09, + CANUSB_SPEED_20000 = 0x0a, + CANUSB_SPEED_10000 = 0x0b, + CANUSB_SPEED_5000 = 0x0c, +} CANUSB_SPEED; + +typedef enum { + CANUSB_MODE_NORMAL = 0x00, + CANUSB_MODE_LOOPBACK = 0x01, + CANUSB_MODE_SILENT = 0x02, + CANUSB_MODE_LOOPBACK_SILENT = 0x03, +} CANUSB_MODE; + +typedef enum { + CANUSB_FRAME_STANDARD = 0x01, + CANUSB_FRAME_EXTENDED = 0x02, +} CANUSB_FRAME; + +typedef enum { + CANUSB_INJECT_PAYLOAD_MODE_RANDOM = 0, + CANUSB_INJECT_PAYLOAD_MODE_INCREMENTAL = 1, + CANUSB_INJECT_PAYLOAD_MODE_FIXED = 2, +} CANUSB_PAYLOAD_MODE; + +typedef struct { + uint8_t packet_header0; + uint8_t packet_header1; + uint8_t type; + uint8_t frame_type; + uint8_t frame_format; + uint8_t frame_id_data_1; + uint8_t frame_id_data_2; + uint8_t frame_id_data_3; + uint8_t frame_id_data_4; + uint8_t frame_data_length; + uint8_t frame_data_1; + uint8_t frame_data_2; + uint8_t frame_data_3; + uint8_t frame_data_4; + uint8_t frame_data_5; + uint8_t frame_data_6; + uint8_t frame_data_7; + uint8_t frame_data_8; + uint8_t reserve; + uint8_t check_code; +} waveshare_can_packet_t; + +class WaveshareCan { + public: + typedef struct { + uint32_t id; + uint8_t dlc; + uint8_t data[8]; + } can_rx_frame_t; + + typedef function frame_callback_t; + + private: + unique_ptr m_thread; + frame_callback_t m_frame_callback; + IDataChannel* m_ch = nullptr; + mutex lock_; + + uint8_t m_rxcache[1024] = {0}; + uint8_t m_rxlen = 0; + + public: + bool init(IDataChannel* ch, frame_callback_t framecb); + + void setCanpPrameter(CANUSB_SPEED speed, CANUSB_MODE mode, CANUSB_FRAME frame); + bool sendStdframe(uint32_t id, unsigned char data[], int data_length_code) { return sendframe(CANUSB_FRAME_STANDARD, id, data, data_length_code); } + bool sendExtframe(uint32_t id, unsigned char data[], int data_length_code) { return sendframe(CANUSB_FRAME_EXTENDED, id, data, data_length_code); } + bool sendframe(CANUSB_FRAME frame, uint32_t id, unsigned char data[], int data_length_code); + + private: + int onReceivePacket(uint8_t* data, size_t len); +}; + +} // namespace iflytop diff --git a/libzqt/idatachannel.hpp b/libzqt/idatachannel.hpp new file mode 100644 index 0000000..c0d8738 --- /dev/null +++ b/libzqt/idatachannel.hpp @@ -0,0 +1,27 @@ +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + + +namespace iflytop { +using namespace std; + +class IDataChannel { + public: + virtual ~IDataChannel(){}; + virtual bool isOpen() = 0; + virtual bool send(const uint8_t *data, size_t len) = 0; + virtual void regRxListener(function cb) = 0; +}; +} // namespace iflytop diff --git a/libzqt/logger.cpp b/libzqt/logger.cpp index ce8558f..2d7b173 100644 --- a/libzqt/logger.cpp +++ b/libzqt/logger.cpp @@ -26,8 +26,6 @@ std::string zhex2str(const uint8_t* hex, size_t len) { return str; } - - std::string zhex2binary(uint8_t hex) { std::string str; for (int i = 0; i < 8; i++) { @@ -36,3 +34,12 @@ std::string zhex2binary(uint8_t hex) { } return str; } + +std::string zhex32ToBinary(uint32_t hex) { + std::string str; + for (int i = 0; i < 32; i++) { + str += (hex & 0x80000000) ? "1" : "0"; + hex <<= 1; + } + return str; +} diff --git a/libzqt/logger.hpp b/libzqt/logger.hpp index d26f11a..5a20883 100644 --- a/libzqt/logger.hpp +++ b/libzqt/logger.hpp @@ -8,7 +8,8 @@ void zos_log(const char* fmt, ...); int32_t zos_get_ticket(); std::string zhex2str(const uint8_t* hex, size_t len); -std::string zhex2binary( uint8_t hex); +std::string zhex2binary(uint8_t hex); +std::string zhex32ToBinary(uint32_t hex); #define ZLOGI(TAG, fmt, ...) zos_log("%08lu INFO [%-10s] " fmt "", zos_get_ticket(), TAG, ##__VA_ARGS__); #define ZLOGD(TAG, fmt, ...) zos_log("%08lu DEBU [%-10s] " fmt "", zos_get_ticket(), TAG, ##__VA_ARGS__); diff --git a/libzqt/zexception.hpp b/libzqt/zexception.hpp index 2d88e7b..a38af94 100644 --- a/libzqt/zexception.hpp +++ b/libzqt/zexception.hpp @@ -19,4 +19,27 @@ class zexception : public exception { const char* what() const noexcept override { return m_ecodeinfo.c_str(); } int32_t ecode() const noexcept { return m_ecode; } }; + +typedef enum { + ke_ok = 0, + ke_channel_is_close = 1, + ke_invalid_param = 2, + ke_invalid_packet_format = 3, +} zecode_t; + +static inline const char* zecode2str(zecode_t ecode) { + switch (ecode) { + case ke_ok: + return "success"; + case ke_channel_is_close: + return "channel_is_close"; + case ke_invalid_param: + return "invalid_param"; + case ke_invalid_packet_format: + return "invalid_packet_format"; + default: + return "unkown error"; + } +} + } // namespace std _GLIBCXX_VISIBILITY(default) diff --git a/mainwindow.cpp b/mainwindow.cpp index 6f547c7..f33fc03 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -9,22 +9,23 @@ #include #include "./ui_mainwindow.h" -#include "electrocardiograph_tester.hpp" +#include "iflytop_canbus/iflytop_canbus_master.hpp" #include "logger.hpp" #include "qt_serial_datachannel.hpp" #include "zexception.hpp" - using namespace std; using namespace iflytop; +using namespace zcr; typedef enum { kone_lead_ecg, kthree_lead_ecg, } device_type_t; -static MainWindow *m_mainWindow; -static QTDataChannel G_QTDataChannel; -static QTDataChannel G_WaveDataChannel; -static device_type_t m_devicetype; +static MainWindow *m_mainWindow; +static QTDataChannel G_QTDataChannel; +static QTDataChannel G_WaveDataChannel; +static device_type_t m_devicetype; +static IflytopCanbusMaster *m_canbusMaster; QT_REQUIRE_CONFIG(groupbox); @@ -179,6 +180,7 @@ void MainWindow::constructBaseUI() { ui->serialBaudrateCB->addItem("115200"); ui->serialBaudrateCB->addItem("460800"); ui->serialBaudrateCB->addItem("500000"); + ui->serialBaudrateCB->addItem("2000000"); ui->serialBaudrateCB->setCurrentIndex(6); connect(ui->serialPortRefreshKey, &QPushButton::clicked, this, [this](bool check) { @@ -199,6 +201,10 @@ void MainWindow::constructBaseUI() { G_QTDataChannel.setFlowControl(QSerialPort::NoFlowControl); G_QTDataChannel.setStopBits(QSerialPort::OneStop); + /** + * @brief 串口打开时,初始化相应参数 + */ + if (!G_QTDataChannel.open()) { QMessageBox::about(NULL, "提示", "串口无法打开,串口不存在或已被占??"); return; @@ -208,6 +214,8 @@ void MainWindow::constructBaseUI() { ui->serialBaudrateCB->setEnabled(false); ui->serialPortCB->setEnabled(false); ui->serialPortRefreshKey->setEnabled(false); + + IflytopCanbusMaster::ins()->updateChannelConfig(); } else { G_QTDataChannel.close(); ui->serialOpenKey->setText("打开"); @@ -235,6 +243,7 @@ void MainWindow::constructBaseUI() { ui->waveSerialBaudrateCB->addItem("115200"); ui->waveSerialBaudrateCB->addItem("460800"); ui->waveSerialBaudrateCB->addItem("500000"); + ui->waveSerialBaudrateCB->addItem("2000000"); // ui->waveSerialBaudrateCB->addItems ui->waveSerialBaudrateCB->setCurrentIndex(6); @@ -359,7 +368,7 @@ void MainWindow::addNewButtonStyle1(QWidget *box, QString zh_name, QStringList p layout->addWidget(lineEdit, rowcnt, i + 1, 1, 1); } - connect(button, &QPushButton::clicked, this, [onButtonClick, lineEdits]() { + connect(button, &QPushButton::clicked, this, [onButtonClick, lineEdits, this]() { vector params; for (int i = 0; i < lineEdits.size(); i++) { params.push_back(lineEdits[i]->text().toStdString()); @@ -368,7 +377,11 @@ void MainWindow::addNewButtonStyle1(QWidget *box, QString zh_name, QStringList p for (int i = 0; i < params.size(); i++) { args[i] = (char *)params[i].c_str(); } - onButtonClick(params.size(), (const char **)args); + try { + onButtonClick(params.size(), (const char **)args); + } catch (const std::zexception &e) { + processException(e); + } }); } @@ -396,27 +409,24 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWi */ G_QTDataChannel.init(); G_WaveDataChannel.init(); - ElectrocardiographTester::ins()->initialize(&G_QTDataChannel); + IflytopCanbusMaster::ins()->initialize(&G_QTDataChannel); } MainWindow::~MainWindow() { delete ui; } -void MainWindow::processException(zexception &e) { // - instructionPreviewShow("%s:%s", e.what(), hrs_ecode2str((ify_hrs_error_code_t)e.ecode())); +void MainWindow::processException(const zexception &e) { // + instructionPreviewShow("%s", e.what()); } void MainWindow::constructAppUI() { { - QWidget *tab = allocNewTab("测试TAB"); + QWidget *tab = allocNewTab("模块操作"); { - QGroupBox *box = allocNewBox(tab, "测试BOX", 4); - addNewButtonStyle1(box, "测试按钮1", {"123", "456", "789"}, [](int argn, const char **args) {}); - addNewButtonStyle1(box, "测试按钮1", {"123"}, [](int argn, const char **args) {}); + QGroupBox *box = allocNewBox(tab, "模块基础操作", 4); + addNewButtonStyle1(box, "扫描模块", {"deviceId"}, [](int argn, const char **args) { // + IflytopCanbusMaster::ins()->callcmd(atoi(args[0]), kmodule_ping); + }); endAllocNewBox(box); } endAllocNewTab(tab); } - - - - } diff --git a/mainwindow.h b/mainwindow.h index df4770b..15b6dcb 100644 --- a/mainwindow.h +++ b/mainwindow.h @@ -101,7 +101,7 @@ class MainWindow : public QMainWindow { void displayInfo(bool suc, QString info); private: - void processException(std::zexception &e); + void processException(const std::zexception &e); private: /*********************************************************************************************************************** diff --git a/src/electrocardiograph_tester.cpp b/src/electrocardiograph_tester.cpp deleted file mode 100644 index 5fa3a5b..0000000 --- a/src/electrocardiograph_tester.cpp +++ /dev/null @@ -1,373 +0,0 @@ -#include "electrocardiograph_tester.hpp" - -#include -#include - -#include "logger.hpp" -#include "zexception.hpp" -using namespace iflytop; - -#define TAG "ElectrocardiographTester" - -/******************************************************************************* - * UTILS_BEGIN * - *******************************************************************************/ - -static bool isHeader(uint8_t *data, uint8_t *headerType) { - if (data[0] == 0x5A && data[1] == 0xA5) { - *headerType = 1; - return true; - } - - if (data[0] == 0x4A && data[1] == 0xA4) { - *headerType = 2; - return true; - } - - return false; -} - -static bool isTail(uint8_t headerType, uint8_t *data) { - if (headerType == 1 && data[0] == 0x5B && data[1] == 0xB5) { - return true; - } - - if (headerType == 2 && data[0] == 0x4B && data[1] == 0xB4) { - return true; - } - - return false; -} -/******************************************************************************* - * UTILS_END * - *******************************************************************************/ -ElectrocardiographTester *ElectrocardiographTester::ins() { - static ElectrocardiographTester *ins = nullptr; - if (ins == nullptr) { - ins = new ElectrocardiographTester(); - } - return ins; -} -// 030065ab04000063076207650768076407305b -// 5aa5030065b00400006607650768076e076407445bb5 -// 5aa5 -void ElectrocardiographTester::initialize(IDataChannel *channel) { // - m_channel = channel; - m_channel->regRxListener([this](uint8_t *data, size_t len) { - { - // ZLOGI(TAG, "rx: %s", zhex2str(data, len).c_str()); - lock_guard lock(lock_); - if (len + m_rxlen > sizeof(m_rxcache)) { - m_rxlen = 0; - } - memcpy(m_rxcache + m_rxlen, data, len); - m_rxlen += len; - } - }); - m_thread.reset(new thread([this]() { - while (true) { - this_thread::sleep_for(chrono::milliseconds(2)); - - { - lock_guard lock(lock_); - // 1.找头部 - int32_t headerpos = -1; - uint8_t headerType = 0; - for (int32_t i = 0; i < m_rxlen - 1; i++) { - if (isHeader(m_rxcache + i, &headerType)) { - headerpos = i; - break; - } - } - - if (headerpos == -1) { - continue; - } - - // 2.找尾 - int32_t tailpos = -1; - for (int32_t i = headerpos + 2; i < m_rxlen - 1; i++) { - if (isTail(headerType, m_rxcache + i)) { - tailpos = i; - break; - } - } - - if (tailpos == -1) { - continue; - } - - // 3.处理数据 - - int32_t datalen = tailpos - headerpos + 2; - uint8_t *packet = &m_rxcache[headerpos]; - - if (packet[0] == 0x5A && packet[1] == 0xA5) { - processCh3RxData(&packet[2], datalen - 4); - } else if (packet[0] == 0x4A && packet[1] == 0xA4) { - processCh4RxData(&packet[2], datalen - 4); - } else { - ZLOGI(TAG, "unknow packet: %s", zhex2str(packet, datalen).c_str()); - } - - // 4. 移除已经处理的数据 - int32_t leftlen = m_rxlen - tailpos - 2; - if (leftlen > 0) { - memmove(m_rxcache, m_rxcache + tailpos + 2, leftlen); - } - m_rxlen = leftlen; - } - } - })); -} -// m_on_raw_data_cb -void ElectrocardiographTester::regReportCB(on_report_t cb) { m_on_report = cb; } -void ElectrocardiographTester::regCh4CheckSumPacketReport(on_ch4_check_sum_packet_report_t cb) { m_on_ch4_check_sum_packet_report = cb; } - -void ElectrocardiographTester::regRawDataCB(on_raw_data_t cb) { m_on_raw_data_cb = cb; } - -typedef struct { - uint32_t rxcnt; - uint32_t m_rx_sum_cnt; -} block_data_rx_state_t; - -void ElectrocardiographTester::processCh4RxData(uint8_t *rx, int32_t rxlen) { - block_data_rx_state_t *block_data_rx_state = (block_data_rx_state_t *)rx; - if (m_on_ch4_check_sum_packet_report) m_on_ch4_check_sum_packet_report(block_data_rx_state->rxcnt, block_data_rx_state->m_rx_sum_cnt); - return; -} -void ElectrocardiographTester::processCh3RxData(uint8_t *rx, int32_t rxlen) { - ify_hrs_packet_t *rxcmd = (ify_hrs_packet_t *)rx; - if (rxcmd->frame_type == kifyhrs_pt_cmd_receipt || rxcmd->frame_type == kifyhrs_pt_error_receipt) { - if (rxcmd->frame_index == m_rxReceiptContext.waittingIndex) { - lock_guard lock(m_rxReceiptContext_lock); - m_rxReceiptContext.receiptIsReady = true; - m_rxReceiptContext.receiptLen = rxlen; - memcpy(m_rxReceiptContext.receipt, &rx[0], rxlen); - } else { - ZLOGE(TAG, "Rx index not match, %s", zhex2str(rx, rxlen).c_str()); - return; - } - } else if (rxcmd->frame_type == kifyhrs_pt_report) { - processRxReportPacket(rxcmd, rxlen); - } -} -void ElectrocardiographTester::processRxReportPacket(ify_hrs_packet_t *rx, int32_t rxlen) { - if (m_on_report == nullptr) { - return; - } - uint8_t *rawrx = (uint8_t *)rx; - uint8_t checsum = 0; - bool checkok = true; - for (int32_t i = 0; i < rxlen - 1; i++) { - checsum += rawrx[i]; - } - if (checsum != rawrx[rxlen - 1]) { - checkok = false; - } - - if (m_on_raw_data_cb) m_on_raw_data_cb(kcmd_report, (uint8_t *)rx, rxlen); - m_on_report(checkok, rx, rxlen); -} - -void ElectrocardiographTester::sendCmd(ify_hrs_packet_t *tx, int32_t txlen, ify_hrs_packet_t *rx, int32_t *rxlen, int32_t overtime) { - if (m_channel == nullptr || m_channel->isOpen() == false) { - throw zexception(kifyhrs_ecode_channle_is_close, "channel is not open"); - } - - uint8_t txindex = m_txindex++; - - ify_hrs_packet_t *txcmd = (ify_hrs_packet_t *)tx; - txcmd->frame_index = txindex; - txcmd->frame_type = kifyhrs_pt_cmd; - - { - lock_guard lock(m_rxReceiptContext_lock); - m_rxReceiptContext.waittingIndex = txindex; - m_rxReceiptContext.waittingForReceipt = true; - m_rxReceiptContext.receiptIsReady = false; - m_rxReceiptContext.receiptLen = 0; - } - - if (m_on_raw_data_cb) m_on_raw_data_cb(kcmd_cmd, (uint8_t *)tx, txlen); - - m_channel->send((uint8_t *)tx, txlen); - bool rxreceipt = false; - - for (int32_t i = 0; i < overtime; i++) { - { - lock_guard lock(m_rxReceiptContext_lock); - /** - * @brief 接收到回执 - */ - if (m_rxReceiptContext.receiptIsReady) { - memcpy(rx, m_rxReceiptContext.receipt, m_rxReceiptContext.receiptLen); - *rxlen = m_rxReceiptContext.receiptLen; - rxreceipt = true; - if (m_on_raw_data_cb) m_on_raw_data_cb(kcmd_receipt, (uint8_t *)rx, *rxlen); - break; - } - } - this_thread::sleep_for(chrono::milliseconds(1)); - } - - if (!rxreceipt) { - throw zexception(kifyhrs_ecode_overtime, "overtime"); - } - - ify_hrs_packet_t *rxcmd = (ify_hrs_packet_t *)rx; - if (rxcmd->frame_type == kifyhrs_pt_error_receipt) { - error_receipt_t *receipt = (error_receipt_t *)rxcmd->data; - throw zexception(receipt->errorcode, "ecode"); - } - - return; -} - -void ElectrocardiographTester::readDeviceVersion(device_version_info_receipt_t *version) { - lock_guard lock(m_tx_lock); - - m_txcmd->cmd = ify_hrs_cmd_read_device_version; - sendCmd(m_txcmd, sizeof(ify_hrs_packet_t), m_rxcmd, &m_rxsize, 100); - - device_version_info_receipt_t *receipt = (device_version_info_receipt_t *)m_rxcmd->data; - memcpy(version, receipt, sizeof(device_version_info_receipt_t)); - return; -} - -void ElectrocardiographTester::readSensorInfo(sensor_info_receipt_t *sensor) { - lock_guard lock(m_tx_lock); - - m_txcmd->cmd = ify_hrs_cmd_read_sensor_info; - sendCmd(m_txcmd, sizeof(ify_hrs_packet_t), m_rxcmd, &m_rxsize, 100); - - sensor_info_receipt_t *receipt = (sensor_info_receipt_t *)m_rxcmd->data; - memcpy(sensor, receipt, sizeof(sensor_info_receipt_t)); - return; -} -void ElectrocardiographTester::readDeviceState(device_state_receipt_t *state) { - lock_guard lock(m_tx_lock); - - m_txcmd->cmd = ify_hrs_cmd_read_device_state; - sendCmd(m_txcmd, sizeof(ify_hrs_packet_t), m_rxcmd, &m_rxsize, 100); - - device_state_receipt_t *receipt = (device_state_receipt_t *)m_rxcmd->data; - memcpy(state, receipt, sizeof(device_state_receipt_t)); - return; -} - -void ElectrocardiographTester::readTime(read_time_receipt_t *time) { - lock_guard lock(m_tx_lock); - - m_txcmd->cmd = ify_hrs_cmd_read_time; - sendCmd(m_txcmd, sizeof(ify_hrs_packet_t), m_rxcmd, &m_rxsize, 100); - - read_time_receipt_t *receipt = (read_time_receipt_t *)m_rxcmd->data; - memcpy(time, receipt, sizeof(read_time_receipt_t)); - return; -} - -void ElectrocardiographTester::syncTime(uint8_t year, uint8_t month, uint8_t day, uint8_t hour, uint8_t minute, uint8_t second) { - lock_guard lock(m_tx_lock); - - sync_time_cmd_t *cmd = (sync_time_cmd_t *)m_txcmd->data; - cmd->year = year; - cmd->month = month; - cmd->day = day; - cmd->hour = hour; - cmd->minute = minute; - cmd->second = second; - - m_txcmd->cmd = ify_hrs_cmd_sync_time; - sendCmd(m_txcmd, sizeof(ify_hrs_packet_t) + sizeof(sync_time_cmd_t), m_rxcmd, &m_rxsize, 100); - return; -} - -void ElectrocardiographTester::startCapture() { - lock_guard lock(m_tx_lock); - - m_txcmd->cmd = ify_hrs_cmd_start_capture; - sendCmd(m_txcmd, sizeof(ify_hrs_packet_t), m_rxcmd, &m_rxsize, 100); - return; -} -void ElectrocardiographTester::stopCapture() { - lock_guard lock(m_tx_lock); - - m_txcmd->cmd = ify_hrs_cmd_stop_capture; - sendCmd(m_txcmd, sizeof(ify_hrs_packet_t), m_rxcmd, &m_rxsize, 100); - return; -} -void ElectrocardiographTester::startRealtimeReport() { - lock_guard lock(m_tx_lock); - - m_txcmd->cmd = ify_hrs_cmd_start_realtime_report; - sendCmd(m_txcmd, sizeof(ify_hrs_packet_t), m_rxcmd, &m_rxsize, 100); - return; -} -void ElectrocardiographTester::stopRealtimeReport() { - lock_guard lock(m_tx_lock); - - m_txcmd->cmd = ify_hrs_cmd_stop_realtime_report; - sendCmd(m_txcmd, sizeof(ify_hrs_packet_t), m_rxcmd, &m_rxsize, 100); - return; -} -void ElectrocardiographTester::readRecordsInfo(int32_t recordoff, read_record_info_receipt_t *recordinfo) { - lock_guard lock(m_tx_lock); - - m_txcmd->cmd = ify_hrs_cmd_read_records_info; - read_record_info_cmd_t *cmd = (read_record_info_cmd_t *)m_txcmd->data; - cmd->record_index = recordoff; - - sendCmd(m_txcmd, sizeof(ify_hrs_packet_t) + sizeof(read_record_info_cmd_t), m_rxcmd, &m_rxsize, 100); - - read_record_info_receipt_t *receipt = (read_record_info_receipt_t *)m_rxcmd->data; - memcpy(recordinfo, receipt, sizeof(read_record_info_receipt_t)); - return; -} -void ElectrocardiographTester::delRecord(uint8_t *recordId) { - lock_guard lock(m_tx_lock); - - del_record_cmd_t *cmd = (del_record_cmd_t *)m_txcmd->data; - memcpy(cmd->record_id, recordId, 6); - - m_txcmd->cmd = ify_hrs_cmd_del_record; - sendCmd(m_txcmd, sizeof(ify_hrs_packet_t) + sizeof(del_record_cmd_t), m_rxcmd, &m_rxsize, 100); - return; -} -void ElectrocardiographTester::startUploadRecord(uint8_t *recordId) { - lock_guard lock(m_tx_lock); - - start_upload_record_cmd_t *cmd = (start_upload_record_cmd_t *)m_txcmd->data; - memcpy(cmd->record_id, recordId, 6); - - m_txcmd->cmd = ify_hrs_cmd_start_upload_record; - sendCmd(m_txcmd, sizeof(ify_hrs_packet_t) + sizeof(start_upload_record_cmd_t), m_rxcmd, &m_rxsize, 100); - return; -} - -void ElectrocardiographTester::stopUploadRecord() { - lock_guard lock(m_tx_lock); - - m_txcmd->cmd = ify_hrs_cmd_stop_upload_record; - sendCmd(m_txcmd, sizeof(ify_hrs_packet_t), m_rxcmd, &m_rxsize, 100); - return; -} - -void ElectrocardiographTester::readSn(string &sn) { - lock_guard lock(m_tx_lock); - - m_txcmd->cmd = ify_hrs_cmd_read_sn; - sendCmd(m_txcmd, sizeof(ify_hrs_packet_t), m_rxcmd, &m_rxsize, 100); - - read_sn_receipt_t *receipt = (read_sn_receipt_t *)m_rxcmd->data; - sn = string((char *)receipt->sn, 14); - return; -} - -void ElectrocardiographTester::reset() { - lock_guard lock(m_tx_lock); - - m_txcmd->cmd = ify_hrs_cmd_reset; - sendCmd(m_txcmd, sizeof(ify_hrs_packet_t), m_rxcmd, &m_rxsize, 100); - return; -} diff --git a/src/electrocardiograph_tester.hpp b/src/electrocardiograph_tester.hpp deleted file mode 100644 index fa666b9..0000000 --- a/src/electrocardiograph_tester.hpp +++ /dev/null @@ -1,117 +0,0 @@ -#pragma once -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "electrocardiograph_tester.hpp" -#include "heart_rate_sensor_protocol.h" - -#define SDK_VERSION 1 - -namespace iflytop { -using namespace std; - -typedef enum { - kcmd_cmd, - kcmd_receipt, - kcmd_report, - kcmd_ch4_data, -} raw_data_type_t; - -typedef function on_report_t; -typedef function on_ch4_check_sum_packet_report_t; -typedef function on_raw_data_t; - -class IDataChannel { - public: - virtual ~IDataChannel(){}; - virtual bool isOpen() = 0; - virtual bool send(const uint8_t *data, size_t len) = 0; - virtual void regRxListener(function cb) = 0; -}; - -class RxReceiptContext { - public: - bool waittingForReceipt; - bool receiptIsReady; - uint16_t waittingIndex; - uint8_t receipt[1024]; - size_t receiptLen; -}; - -class ElectrocardiographTester { - ElectrocardiographTester() {} - - IDataChannel *m_channel = nullptr; - - uint8_t m_rxcache[1024]; - int32_t m_rxlen = 0; - bool m_rxcache_is_full = false; - - mutex lock_; - - unique_ptr m_thread; - - RxReceiptContext m_rxReceiptContext; - mutex m_rxReceiptContext_lock; - - /******************************************************************************* - * TX CONTEXT * - *******************************************************************************/ - mutex m_tx_lock; - uint8_t m_txbuf[1024]; - uint8_t m_rxbuf[1024]; - ify_hrs_packet_t *m_txcmd = (ify_hrs_packet_t *)m_txbuf; - ify_hrs_packet_t *m_rxcmd = (ify_hrs_packet_t *)m_rxbuf; - int32_t m_rxsize; - uint8_t m_txindex = 0; - - /******************************************************************************* - * ReportCB * - *******************************************************************************/ - on_report_t m_on_report; - on_ch4_check_sum_packet_report_t m_on_ch4_check_sum_packet_report; - on_raw_data_t m_on_raw_data_cb; - - public: - static ElectrocardiographTester *ins(); - - void initialize(IDataChannel *channel); - void regReportCB(on_report_t cb); - void regCh4CheckSumPacketReport(on_ch4_check_sum_packet_report_t cb); - void regRawDataCB(on_raw_data_t cb); - - public: - void readDeviceVersion(device_version_info_receipt_t *version); // 读取设备版本信息 - void readSensorInfo(sensor_info_receipt_t *sensor); // 读取传感器信息 - void readDeviceState(device_state_receipt_t *state); // 读取设备状态信息 - void readTime(read_time_receipt_t *time); // 读取设备时间 - void syncTime(uint8_t year, uint8_t month, uint8_t day, uint8_t hour, uint8_t minute, uint8_t second); // 同步设备时间 - void startCapture(); // 开始采集 - void stopCapture(); // 停止采集 - void startRealtimeReport(); // 开始实时数据上报 - void stopRealtimeReport(); // 停止实时数据上报 - void readRecordsInfo(int32_t recordoff, read_record_info_receipt_t *recordinfo); // 读取记录信息 - void delRecord(uint8_t *recordId); // 删除记录 - void startUploadRecord(uint8_t *recordId); // 开始上传记录 - void stopUploadRecord(); // 停止上传记录 - void readSn(string &sn); - void reset(); // 重置设备 - - private: - void sendCmd(ify_hrs_packet_t *tx, int32_t txlen, ify_hrs_packet_t *rx, int32_t *rxlen, int32_t overtime); - void processCh3RxData(uint8_t *rx, int32_t rxlen); // 指令通道 - void processCh4RxData(uint8_t *rx, int32_t rxlen); // 心电原始数据通道 - void processRxReportPacket(ify_hrs_packet_t *rx, int32_t rxlen); -}; - -} // namespace iflytop diff --git a/src/qt_serial_datachannel.hpp b/src/qt_serial_datachannel.hpp index 7d60f67..2c71827 100644 --- a/src/qt_serial_datachannel.hpp +++ b/src/qt_serial_datachannel.hpp @@ -21,7 +21,7 @@ #include // #include "zqthread.hpp" -#include "electrocardiograph_tester.hpp" +#include "idatachannel.hpp" #define SDK_VERSION 1