24 changed files with 1436 additions and 515 deletions
-
4CMakeLists.txt
-
BINdoc/waveshare-can/CAN配置命令.pdf
-
BINdoc/waveshare-can/USB-CAN-A.zip
-
1doc/waveshare-can/USB-CAN-A/.gitignore
-
21doc/waveshare-can/USB-CAN-A/LICENSE
-
47doc/waveshare-can/USB-CAN-A/README.md
-
629doc/waveshare-can/USB-CAN-A/canusb.c
-
BINdoc/waveshare-can/USB_(Serial_port)_to_CAN_protocol_defines.pdf
-
163iflytop_canbus/cmdid.hpp
-
25iflytop_canbus/iflytop_canbus_frame.hpp
-
93iflytop_canbus/iflytop_canbus_master.cpp
-
86iflytop_canbus/iflytop_canbus_master.hpp
-
0iflytop_canbus/regid.hpp
-
166iflytop_canbus/waveshare_can.cpp
-
110iflytop_canbus/waveshare_can.hpp
-
27libzqt/idatachannel.hpp
-
11libzqt/logger.cpp
-
3libzqt/logger.hpp
-
23libzqt/zexception.hpp
-
48mainwindow.cpp
-
2mainwindow.h
-
373src/electrocardiograph_tester.cpp
-
117src/electrocardiograph_tester.hpp
-
2src/qt_serial_datachannel.hpp
@ -0,0 +1 @@ |
|||
canusb |
@ -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. |
@ -0,0 +1,47 @@ |
|||
# USB-CAN Analyzer Linux Support |
|||
|
|||
This is a small C program that dumps the CAN traffic for one these adapters: |
|||
 |
|||
|
|||
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> |
|||
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 |
|||
``` |
@ -0,0 +1,629 @@ |
|||
#include <stdlib.h> |
|||
#include <string.h> |
|||
#include <stdio.h> |
|||
#include <errno.h> |
|||
#include <unistd.h> |
|||
#include <sys/ioctl.h> |
|||
#include <sys/types.h> |
|||
#include <sys/stat.h> |
|||
#include <fcntl.h> |
|||
#include <asm/termbits.h> /* struct termios2 */ |
|||
#include <time.h> |
|||
#include <ctype.h> |
|||
#include <signal.h> |
|||
#include <sys/time.h> |
|||
|
|||
#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 <options>\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; |
|||
} |
@ -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
|
@ -0,0 +1,25 @@ |
|||
#pragma once
|
|||
#include <stdint.h>
|
|||
|
|||
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
|
@ -0,0 +1,93 @@ |
|||
#include "iflytop_canbus_master.hpp"
|
|||
|
|||
#include <stdarg.h>
|
|||
|
|||
#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); |
|||
} |
@ -0,0 +1,86 @@ |
|||
#pragma once
|
|||
#include <fstream>
|
|||
#include <functional>
|
|||
#include <iostream>
|
|||
#include <list>
|
|||
#include <map>
|
|||
#include <memory>
|
|||
#include <mutex>
|
|||
#include <set>
|
|||
#include <sstream>
|
|||
#include <string>
|
|||
#include <thread>
|
|||
#include <vector>
|
|||
|
|||
#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<void(raw_data_type_t type, uint8_t *hex, uint32_t hexlen)> 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<thread> 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
|
@ -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<mutex> 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<mutex> 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); |
|||
} |
|||
} |
@ -0,0 +1,110 @@ |
|||
#pragma once
|
|||
|
|||
#include <stdint.h>
|
|||
#include <stdio.h>
|
|||
#include <windows.h>
|
|||
|
|||
#include <fstream>
|
|||
#include <functional>
|
|||
#include <iostream>
|
|||
#include <list>
|
|||
#include <map>
|
|||
#include <memory>
|
|||
#include <set>
|
|||
#include <sstream>
|
|||
#include <string>
|
|||
#include <thread>
|
|||
#include <vector>
|
|||
|
|||
#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<void(can_rx_frame_t*)> frame_callback_t; |
|||
|
|||
private: |
|||
unique_ptr<thread> 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
|
@ -0,0 +1,27 @@ |
|||
#pragma once
|
|||
#include <fstream>
|
|||
#include <functional>
|
|||
#include <iostream>
|
|||
#include <list>
|
|||
#include <map>
|
|||
#include <memory>
|
|||
#include <mutex>
|
|||
#include <set>
|
|||
#include <sstream>
|
|||
#include <string>
|
|||
#include <thread>
|
|||
#include <vector>
|
|||
|
|||
|
|||
|
|||
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<void(uint8_t *data, size_t len)> cb) = 0; |
|||
}; |
|||
} // namespace iflytop
|
@ -1,373 +0,0 @@ |
|||
#include "electrocardiograph_tester.hpp"
|
|||
|
|||
#include <stdlib.h>
|
|||
#include <string.h>
|
|||
|
|||
#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<mutex> 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<mutex> 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<mutex> 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<mutex> 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<mutex> 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<mutex> 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<mutex> 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<mutex> 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<mutex> 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<mutex> 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<mutex> 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<mutex> 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<mutex> 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<mutex> 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<mutex> 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<mutex> 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<mutex> 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<mutex> 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<mutex> 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<mutex> 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; |
|||
} |
@ -1,117 +0,0 @@ |
|||
#pragma once
|
|||
#include <fstream>
|
|||
#include <functional>
|
|||
#include <iostream>
|
|||
#include <list>
|
|||
#include <map>
|
|||
#include <memory>
|
|||
#include <mutex>
|
|||
#include <set>
|
|||
#include <sstream>
|
|||
#include <string>
|
|||
#include <thread>
|
|||
#include <vector>
|
|||
|
|||
#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<void(bool checkok,ify_hrs_packet_t *report_packet, size_t len)> on_report_t; |
|||
typedef function<void(uint32_t rxcnt, uint32_t report_packet_checksum)> on_ch4_check_sum_packet_report_t; |
|||
typedef function<void(raw_data_type_t type, uint8_t *hex, uint32_t hexlen)> 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<void(uint8_t *data, size_t len)> 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<thread> 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
|
Write
Preview
Loading…
Cancel
Save
Reference in new issue