Browse Source

update

master
zhaohe 1 year ago
parent
commit
0b1675fd59
  1. 4
      CMakeLists.txt
  2. BIN
      doc/waveshare-can/CAN配置命令.pdf
  3. BIN
      doc/waveshare-can/USB-CAN-A.zip
  4. 1
      doc/waveshare-can/USB-CAN-A/.gitignore
  5. 21
      doc/waveshare-can/USB-CAN-A/LICENSE
  6. 47
      doc/waveshare-can/USB-CAN-A/README.md
  7. 629
      doc/waveshare-can/USB-CAN-A/canusb.c
  8. BIN
      doc/waveshare-can/USB_(Serial_port)_to_CAN_protocol_defines.pdf
  9. 163
      iflytop_canbus/cmdid.hpp
  10. 25
      iflytop_canbus/iflytop_canbus_frame.hpp
  11. 93
      iflytop_canbus/iflytop_canbus_master.cpp
  12. 86
      iflytop_canbus/iflytop_canbus_master.hpp
  13. 0
      iflytop_canbus/regid.hpp
  14. 166
      iflytop_canbus/waveshare_can.cpp
  15. 110
      iflytop_canbus/waveshare_can.hpp
  16. 27
      libzqt/idatachannel.hpp
  17. 11
      libzqt/logger.cpp
  18. 3
      libzqt/logger.hpp
  19. 23
      libzqt/zexception.hpp
  20. 48
      mainwindow.cpp
  21. 2
      mainwindow.h
  22. 373
      src/electrocardiograph_tester.cpp
  23. 117
      src/electrocardiograph_tester.hpp
  24. 2
      src/qt_serial_datachannel.hpp

4
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
)

BIN
doc/waveshare-can/CAN配置命令.pdf

BIN
doc/waveshare-can/USB-CAN-A.zip

1
doc/waveshare-can/USB-CAN-A/.gitignore

@ -0,0 +1 @@
canusb

21
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.

47
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>
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
```

629
doc/waveshare-can/USB-CAN-A/canusb.c

@ -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;
}

BIN
doc/waveshare-can/USB_(Serial_port)_to_CAN_protocol_defines.pdf

163
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

25
iflytop_canbus/iflytop_canbus_frame.hpp

@ -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

93
iflytop_canbus/iflytop_canbus_master.cpp

@ -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);
}

86
iflytop_canbus/iflytop_canbus_master.hpp

@ -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
iflytop_canbus/regid.hpp

166
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<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);
}
}

110
iflytop_canbus/waveshare_can.hpp

@ -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

27
libzqt/idatachannel.hpp

@ -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

11
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;
}

3
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__);

23
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)

48
mainwindow.cpp

@ -9,22 +9,23 @@
#include <QtWidgets/QLineEdit>
#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<string> 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);
}
}

2
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:
/***********************************************************************************************************************

373
src/electrocardiograph_tester.cpp

@ -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;
}

117
src/electrocardiograph_tester.hpp

@ -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

2
src/qt_serial_datachannel.hpp

@ -21,7 +21,7 @@
#include <QtSerialPort/QSerialPortInfo>
//
#include "zqthread.hpp"
#include "electrocardiograph_tester.hpp"
#include "idatachannel.hpp"
#define SDK_VERSION 1

Loading…
Cancel
Save