wip
This commit is contained in:
parent
39922749ec
commit
a8dbc10b2b
@ -2,7 +2,7 @@ cmake_minimum_required (VERSION 3.15)
|
||||
project (simplesync)
|
||||
|
||||
set(CMAKE_CXX_FLAGS "-Wall -Wextra -pedantic")
|
||||
set(CMAKE_CXX_STANDARD 20)
|
||||
set(CMAKE_CXX_STANDARD 11)
|
||||
option(BUILD_TESTS "Build all tests." OFF)
|
||||
option(BUILD_PYLIB "Build python lib." OFF)
|
||||
|
||||
@ -52,7 +52,7 @@ install(EXPORT simplesyncTargets
|
||||
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/simplesync-config.cmake
|
||||
${CMAKE_CURRENT_BINARY_DIR}/simplesync-config-version.cmake
|
||||
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/simplesync )
|
||||
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/src/simplesync.hpp
|
||||
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/include/simplesync.hpp
|
||||
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/simplesync)
|
||||
export( EXPORT simplesyncTargets
|
||||
FILE ${CMAKE_CURRENT_BINARY_DIR}/simplesync-targets.cmake)
|
||||
|
23
example/simplesynctui.py
Executable file
23
example/simplesynctui.py
Executable file
@ -0,0 +1,23 @@
|
||||
#!/usr/bin/python
|
||||
import serial
|
||||
import pysimplesync
|
||||
|
||||
try:
|
||||
ser = serial.Serial("/dev/ttyACM0");
|
||||
except:
|
||||
ser = serial.Serial("/dev/ttyACM1");
|
||||
|
||||
def write_to_ser(buf):
|
||||
ser.write(bytes(buf))
|
||||
sync = pysimplesync.SimpleSync(write_to_ser)
|
||||
|
||||
ser.reset_input_buffer()
|
||||
sync.request_all_interfaces()
|
||||
|
||||
while True:
|
||||
sync.handle_stream(ser.read_until(b"\x00"))
|
||||
print("\t".join([str(a) for a in list(sync)]))
|
||||
sync["v2"] = -400
|
||||
sync["v1"] = 400
|
||||
# 11.62V := 604
|
||||
sync.update();
|
@ -1,23 +1,28 @@
|
||||
#ifndef SIMPLESYNC_HPP
|
||||
#define SIMPLESYNC_HPP
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
|
||||
#ifdef ARDUINO
|
||||
#include <Arduino.h>
|
||||
#ifdef __AVR__
|
||||
#include "include/nonstd_functional.hpp"
|
||||
using nonstd::function;
|
||||
#else /* ndef __AVR__ */
|
||||
using std::function;
|
||||
#define STDCLIB
|
||||
#endif
|
||||
#else /* ndef ARDUINO */
|
||||
#define STDCLIB
|
||||
#endif
|
||||
#ifdef STDCLIB
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
using std::function;
|
||||
using std::string;
|
||||
#endif
|
||||
|
||||
namespace simplesync {
|
||||
@ -25,7 +30,7 @@ namespace simplesync {
|
||||
enum error_t {
|
||||
BUFFER_OVERFLOW = (-1),
|
||||
INVALID_COMMAND = (-2),
|
||||
ID_INVALID = (-3),
|
||||
UNKNOWN_INTERFACE = (-3),
|
||||
OUT_OF_MEMORY = (-4),
|
||||
CRC_MISSMATCH = (-5),
|
||||
};
|
||||
@ -71,7 +76,7 @@ inline int cobs_decode(uint8_t *buf_in, unsigned int &buf_in_size_used, uint8_t
|
||||
}
|
||||
buf_out_size_used = i_out;
|
||||
buf_in_size_used = i_in;
|
||||
return 0;
|
||||
return buf_out_size_used;
|
||||
}
|
||||
|
||||
inline unsigned int encode_zigzag(int input) {
|
||||
@ -80,33 +85,36 @@ inline unsigned int encode_zigzag(int input) {
|
||||
|
||||
inline unsigned int decode_zigzag(int input) { return (input >> 1) ^ (-(input & 1)); }
|
||||
|
||||
inline unsigned int encode_unsigned_varint(unsigned int input, uint8_t *out) {
|
||||
inline int encode_unsigned_varint(unsigned int input, uint8_t *out, unsigned int out_size) {
|
||||
int offset = 0;
|
||||
while (input > 0x7F) {
|
||||
out[offset++] = input & 0x7F;
|
||||
input >>= 7;
|
||||
if (offset + 1 >= (int)out_size) return -1;
|
||||
}
|
||||
out[offset++] = input | 0x80;
|
||||
return offset;
|
||||
}
|
||||
|
||||
inline unsigned int encode_varint(int input, uint8_t *out) {
|
||||
return encode_unsigned_varint(encode_zigzag(input), out);
|
||||
inline int encode_varint(int input, uint8_t *out, unsigned int out_size) {
|
||||
return encode_unsigned_varint(encode_zigzag(input), out, out_size);
|
||||
}
|
||||
|
||||
inline unsigned int decode_unsigned_varint(uint8_t *input, unsigned int &out) {
|
||||
inline int decode_unsigned_varint(uint8_t *input, unsigned int input_size, unsigned int &out) {
|
||||
int offset = 0;
|
||||
out = 0;
|
||||
for (;; offset += 1) {
|
||||
out |= ((unsigned int)(input[offset] & 0x7F)) << (7 * offset);
|
||||
if (input[offset] & 0x80) break;
|
||||
if (offset + 1 >= (int)input_size || offset + 1 >= (int)sizeof(unsigned int))
|
||||
return BUFFER_OVERFLOW;
|
||||
}
|
||||
return offset + 1;
|
||||
}
|
||||
|
||||
inline unsigned int decode_varint(uint8_t *input, int &out) {
|
||||
inline int decode_varint(uint8_t *input, unsigned int input_size, int &out) {
|
||||
unsigned int out_uint;
|
||||
int offset = decode_unsigned_varint(input, out_uint);
|
||||
int offset = decode_unsigned_varint(input, input_size, out_uint);
|
||||
out = decode_zigzag(out_uint);
|
||||
return offset;
|
||||
}
|
||||
@ -127,97 +135,228 @@ inline uint16_t calc_fletcher_checksum(uint8_t *buf, unsigned int buf_size) {
|
||||
return (c1 << 8 | c0);
|
||||
}
|
||||
|
||||
template <typename time_t, typename timedelta_t, unsigned int BUF_SIZE = 256,
|
||||
unsigned int str_len_max = 32>
|
||||
template <typename timepoint_t, typename timepointdelta_t, unsigned int BUF_SIZE = 256,
|
||||
bool DYNAMIC_INTERFACES = false>
|
||||
class SimpleSync {
|
||||
public:
|
||||
static constexpr unsigned int STR_LEN_MAX = str_len_max;
|
||||
class NumberInterface {
|
||||
class Interface {
|
||||
public:
|
||||
time_t last_send;
|
||||
char *key;
|
||||
timedelta_t update_interval;
|
||||
uint8_t type;
|
||||
string key;
|
||||
|
||||
std::function<int(uint8_t *buf, unsigned int buf_size)> pack;
|
||||
std::function<int(uint8_t *buf, unsigned int buf_size)> unpack;
|
||||
|
||||
bool send_requested = true;
|
||||
int value;
|
||||
NumberInterface(SimpleSync &s, char *key, timedelta_t update_interval)
|
||||
: key(key), update_interval(update_interval) {
|
||||
s.number_interfaces.push_back(this);
|
||||
timepoint_t last_send;
|
||||
|
||||
timepointdelta_t update_interval;
|
||||
bool update_periodically;
|
||||
|
||||
void *data;
|
||||
bool data_owned;
|
||||
|
||||
Interface(uint8_t type, string key, void *data,
|
||||
std::function<int(uint8_t *buf, unsigned int buf_size)> pack,
|
||||
std::function<int(uint8_t *buf, unsigned int buf_size)> unpack)
|
||||
: type(type), key(key), pack(pack), unpack(unpack), update_periodically(false),
|
||||
data(data), data_owned(false) {}
|
||||
~Interface() {
|
||||
if (data != nullptr && data_owned) switch (type) {
|
||||
case 0x01:
|
||||
delete (int *)data;
|
||||
break;
|
||||
case 0x02:
|
||||
delete (string *)data;
|
||||
break;
|
||||
default:
|
||||
free(data);
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class StringInterface {
|
||||
public:
|
||||
time_t last_send;
|
||||
char *key;
|
||||
timedelta_t update_interval;
|
||||
bool send_requested = true;
|
||||
char value[str_len_max + 1];
|
||||
StringInterface(SimpleSync &s, char *key, timedelta_t update_interval)
|
||||
: key(key), update_interval(update_interval) {
|
||||
value[str_len_max] = 0x00;
|
||||
s.string_interfaces.push_back(this);
|
||||
static Interface *NumberPtr(SimpleSync &sync, string key, int *value_ptr, bool ro = false) {
|
||||
sync.interfaces.push_back(std::unique_ptr<Interface>(new Interface(
|
||||
0x01, key, (void *)value_ptr,
|
||||
[=](uint8_t *buf, unsigned int buf_size) -> int { /* pack */
|
||||
if (buf_size < sizeof(int) * 8 / 7)
|
||||
return BUFFER_OVERFLOW;
|
||||
return encode_varint(*value_ptr, buf,
|
||||
buf_size);
|
||||
},
|
||||
[=](uint8_t *buf, unsigned int buf_size) -> int { /* unpack */
|
||||
buf_size = 1 * buf_size;
|
||||
if (ro) {
|
||||
int ro_value = 0;
|
||||
int ret = decode_varint(
|
||||
buf, buf_size, ro_value);
|
||||
// if (ro_value != *value) // TODO
|
||||
// Error ro value
|
||||
return ret;
|
||||
}
|
||||
return decode_varint(buf, buf_size,
|
||||
*value_ptr);
|
||||
})));
|
||||
return sync.interfaces.back().get();
|
||||
}
|
||||
};
|
||||
|
||||
std::vector<NumberInterface *> number_interfaces;
|
||||
std::vector<StringInterface *> string_interfaces;
|
||||
static Interface *NumberPtrPeriodic(SimpleSync &sync, string key, int *value_ptr,
|
||||
timepointdelta_t update_interval, bool ro = false) {
|
||||
auto i = NumberPtr(sync, key, value_ptr, ro);
|
||||
if (i) {
|
||||
i->update_interval = update_interval;
|
||||
i->update_periodically = true;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
void add_number(string key, int *value_ptr, timepointdelta_t update_interval) {
|
||||
NumberPtrPeriodic(*this, key, value_ptr, update_interval);
|
||||
}
|
||||
void add_number_ro(string key, int *value_ptr, timepointdelta_t update_interval) {
|
||||
NumberPtrPeriodic(*this, key, value_ptr, update_interval, true);
|
||||
}
|
||||
|
||||
static Interface *Number(SimpleSync &sync, string key) {
|
||||
auto v_ptr = new int;
|
||||
Interface *i = NumberPtr(sync, key, v_ptr);
|
||||
if (i)
|
||||
i->data_owned = true;
|
||||
else
|
||||
delete v_ptr;
|
||||
return i;
|
||||
}
|
||||
|
||||
static Interface *TextPtr(SimpleSync &sync, string key, string *value_ptr) {
|
||||
sync.interfaces.push_back(std::unique_ptr<Interface>(new Interface(
|
||||
0x02, key, (void *)value_ptr,
|
||||
[=](uint8_t *buf, unsigned int buf_size) -> int { /* pack */
|
||||
auto value_len =
|
||||
1 + value_ptr->size();
|
||||
if (value_len > buf_size)
|
||||
return BUFFER_OVERFLOW;
|
||||
memcpy(buf, value_ptr->c_str(),
|
||||
value_len - 1);
|
||||
buf[value_len - 1] = 0;
|
||||
return value_len;
|
||||
},
|
||||
[=](uint8_t *buf, unsigned int buf_size) -> int { /* unpack */
|
||||
auto strl =
|
||||
strnlen((char *)buf, buf_size) +
|
||||
1;
|
||||
if (strl > buf_size)
|
||||
return BUFFER_OVERFLOW;
|
||||
value_ptr->assign((char *)buf);
|
||||
return strl;
|
||||
})));
|
||||
return sync.interfaces.back().get();
|
||||
}
|
||||
|
||||
static Interface *TextPtrPeriodic(SimpleSync &sync, string key, string *value_ptr,
|
||||
timepointdelta_t update_interval) {
|
||||
auto i = TextPtr(sync, key, value_ptr);
|
||||
if (i) {
|
||||
i->update_interval = update_interval;
|
||||
i->update_periodically = true;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
static Interface *Text(SimpleSync &sync, string key) {
|
||||
auto v_ptr = new string;
|
||||
Interface *i = TextPtr(sync, key, v_ptr);
|
||||
if (i)
|
||||
i->data_owned = true;
|
||||
else
|
||||
delete v_ptr;
|
||||
return i;
|
||||
}
|
||||
|
||||
// int parse_number(uint8_t *buf, unsigned int buf_size, int* value){
|
||||
// return decode_varint(&buf[parsed], *value);
|
||||
// }
|
||||
|
||||
std::vector<std::unique_ptr<Interface>> interfaces;
|
||||
|
||||
function<int(uint8_t *, unsigned int)> write_pkg;
|
||||
function<void(NumberInterface *&, char *, int)> number_update;
|
||||
function<void(StringInterface *&, char *, char *)> string_update;
|
||||
SimpleSync(function<int(uint8_t *, unsigned int)> write_pkg,
|
||||
function<void(NumberInterface *&, char *, int)> number_update,
|
||||
function<void(StringInterface *&, char *, char *)> string_update)
|
||||
: write_pkg(write_pkg), number_update(number_update), string_update(string_update) {}
|
||||
function<timepoint_t()> get_time;
|
||||
SimpleSync(function<int(uint8_t *, unsigned int)> write_pkg, function<timepoint_t()> get_time)
|
||||
: write_pkg(write_pkg), get_time(get_time) {
|
||||
last_pkg_recived = get_time();
|
||||
}
|
||||
|
||||
Interface *get_interface(const uint8_t type, const char *const key) const {
|
||||
for (auto &i : interfaces) {
|
||||
if (i->type == type && strcmp(i->key.c_str(), key) == 0) {
|
||||
return i.get();
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
Interface *get_or_create_interface(uint8_t type, const char *const key) {
|
||||
Interface *i_exists = get_interface(type, key);
|
||||
if (i_exists) {
|
||||
return i_exists;
|
||||
}
|
||||
if (DYNAMIC_INTERFACES) {
|
||||
switch (type) {
|
||||
case 0x01:
|
||||
return Number(*this, key);
|
||||
case 0x02:
|
||||
return Text(*this, key);
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool watchdog_enabled = false;
|
||||
function<void()> watchdog_func;
|
||||
timepointdelta_t watchdog_timeout;
|
||||
timepoint_t last_pkg_recived;
|
||||
void set_watchdog(const function<void()> &f, timepointdelta_t timeout) {
|
||||
watchdog_enabled = bool(f);
|
||||
if (!watchdog_enabled) return;
|
||||
watchdog_timeout = timeout;
|
||||
watchdog_func = f;
|
||||
}
|
||||
|
||||
void check_watchdog(const timepoint_t &time) {
|
||||
if (watchdog_enabled && time > last_pkg_recived + watchdog_timeout) watchdog_func();
|
||||
}
|
||||
|
||||
int parse_pkg(uint8_t *buf, unsigned int buf_size) {
|
||||
if (buf_size < 4) return CRC_MISSMATCH;
|
||||
uint16_t crc = calc_fletcher_checksum(buf, buf_size - 2);
|
||||
if (buf[--buf_size] != (crc & 0xFF) || (buf[--buf_size] != (crc >> 8)))
|
||||
return CRC_MISSMATCH;
|
||||
timepoint_t time = get_time();
|
||||
check_watchdog(time);
|
||||
for (unsigned int parsed = 0; parsed < buf_size;) {
|
||||
switch (buf[parsed]) {
|
||||
uint8_t type = buf[parsed++];
|
||||
switch (type) {
|
||||
case 0x00: {
|
||||
for (auto ni : number_interfaces) ni->send_requested = true;
|
||||
for (auto ni : string_interfaces) ni->send_requested = true;
|
||||
} break;
|
||||
case 0x01: {
|
||||
char *key = (char *)&buf[parsed + 1];
|
||||
parsed += 1 + strlen(key) + 1;
|
||||
int value;
|
||||
parsed += decode_varint(&buf[parsed], value);
|
||||
NumberInterface *ni_ptr = nullptr;
|
||||
for (auto ni : number_interfaces) {
|
||||
if (strcmp(ni->key, key) == 0) {
|
||||
ni_ptr = ni;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (number_update) {
|
||||
number_update(ni_ptr, key, value);
|
||||
}
|
||||
if (ni_ptr) {
|
||||
ni_ptr->value = value;
|
||||
}
|
||||
char *key = (char *)&buf[parsed];
|
||||
auto keylen = strnlen(key, buf_size - parsed) + 1;
|
||||
if (keylen + parsed >= buf_size) return BUFFER_OVERFLOW;
|
||||
parsed += keylen;
|
||||
if (keylen == 0)
|
||||
for (auto &ni : interfaces) ni->send_requested = true;
|
||||
} break;
|
||||
case 0x01:
|
||||
case 0x02: {
|
||||
char *key = (char *)&buf[parsed + 1];
|
||||
parsed += 1 + strlen(key) + 1;
|
||||
char *value = (char *)&buf[parsed];
|
||||
unsigned int strl = strlen(value) + 1;
|
||||
parsed += strl;
|
||||
StringInterface *stri_ptr = nullptr;
|
||||
for (StringInterface *si : string_interfaces) {
|
||||
if (strcmp(si->key, key) == 0) {
|
||||
stri_ptr = si;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (string_update) {
|
||||
string_update(stri_ptr, key, value);
|
||||
}
|
||||
if (stri_ptr) {
|
||||
memcpy(stri_ptr->value, value, str_len_max > strl ? strl : str_len_max);
|
||||
char *key = (char *)&buf[parsed];
|
||||
unsigned int keylen = strnlen(key, buf_size - parsed) + 1;
|
||||
if (keylen + parsed >= buf_size) return BUFFER_OVERFLOW;
|
||||
parsed += keylen;
|
||||
Interface *ni = get_or_create_interface(type, key);
|
||||
if (ni == nullptr) return UNKNOWN_INTERFACE;
|
||||
ni->send_requested = false;
|
||||
int unpacked_bytes = ni->unpack(buf + parsed, buf_size - parsed);
|
||||
if (unpacked_bytes >= 0) {
|
||||
parsed += unpacked_bytes;
|
||||
} else { // ERROR -> return
|
||||
return unpacked_bytes;
|
||||
}
|
||||
} break;
|
||||
default:
|
||||
@ -225,80 +364,68 @@ class SimpleSync {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
last_pkg_recived = time;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void request_all_interfaces() const {
|
||||
uint8_t buf[] = {0x00, 0x00};
|
||||
unsigned int buf_len_used = sizeof(buf);
|
||||
uint8_t cobs_buf[10];
|
||||
unsigned int cobs_buf_needed = cobs_encode(buf, buf_len_used, cobs_buf, BUF_SIZE);
|
||||
write_pkg(cobs_buf, cobs_buf_needed);
|
||||
uint8_t buf[] = {0x00, 0x00, 0xac, 0xcd, 0xef};
|
||||
encode_and_write_pkg(buf, 2);
|
||||
}
|
||||
|
||||
void update(time_t time) {
|
||||
private:
|
||||
void encode_and_write_pkg(uint8_t *buf, unsigned int buf_size) const {
|
||||
if (buf_size <= 1) {
|
||||
return;
|
||||
}
|
||||
uint8_t cobs_buf[BUF_SIZE];
|
||||
uint16_t crc = calc_fletcher_checksum(buf, buf_size);
|
||||
buf[buf_size++] = crc >> 8;
|
||||
buf[buf_size++] = crc & 0xFF;
|
||||
int cobs_buf_needed = cobs_encode(buf, buf_size, cobs_buf, BUF_SIZE);
|
||||
if (cobs_buf_needed < 0) {
|
||||
// TODO: error
|
||||
} else {
|
||||
write_pkg(cobs_buf, cobs_buf_needed);
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
void update() {
|
||||
timepoint_t time = get_time();
|
||||
check_watchdog(time);
|
||||
uint8_t buf[BUF_SIZE];
|
||||
unsigned int buf_len_used = 0;
|
||||
for (auto ni : number_interfaces) {
|
||||
for (auto &ni : interfaces) {
|
||||
if (ni->send_requested ||
|
||||
(ni->last_send > time || ni->last_send <= time - ni->update_interval)) {
|
||||
unsigned int strl = strlen(ni->key) + 1;
|
||||
unsigned int buf_len_needed = 1 + strl + 8 * sizeof(int) / 7;
|
||||
(ni->update_periodically &&
|
||||
(ni->last_send > time || ni->last_send <= time - ni->update_interval))) {
|
||||
unsigned int strl = ni->key.size() + 1;
|
||||
unsigned int buf_len_needed =
|
||||
1 + strl + 32; // TODO get real pack size (not always 32)
|
||||
buf_len_needed += buf_len_needed / 0xFF + 3 + 2; // COBS overhead
|
||||
if (BUF_SIZE < buf_len_needed) continue; // Error: update to large, skip interface
|
||||
if (BUF_SIZE < buf_len_used + buf_len_needed) {
|
||||
uint8_t cobs_buf[BUF_SIZE];
|
||||
uint16_t crc = calc_fletcher_checksum(buf, buf_len_used);
|
||||
buf[buf_len_used++] = crc >> 8;
|
||||
buf[buf_len_used++] = crc & 0xFF;
|
||||
unsigned int cobs_buf_needed =
|
||||
cobs_encode(buf, buf_len_used, cobs_buf, BUF_SIZE);
|
||||
write_pkg(cobs_buf, cobs_buf_needed);
|
||||
encode_and_write_pkg(buf, buf_len_used);
|
||||
buf_len_used = 0;
|
||||
}
|
||||
buf[buf_len_used++] = 0x01;
|
||||
memcpy((char *)&buf[buf_len_used], ni->key, strl);
|
||||
buf[buf_len_used++] = ni->type;
|
||||
memcpy((char *)&buf[buf_len_used], ni->key.c_str(), strl);
|
||||
buf_len_used += strl;
|
||||
buf_len_used += encode_varint(ni->value, &buf[buf_len_used]);
|
||||
int pack_len = ni->pack(&buf[buf_len_used], BUF_SIZE - buf_len_used);
|
||||
if (pack_len < 0) {
|
||||
// ERROR -> return
|
||||
buf_len_used = 0;
|
||||
return;
|
||||
}
|
||||
buf_len_used += pack_len;
|
||||
ni->last_send = time;
|
||||
ni->send_requested = false;
|
||||
}
|
||||
}
|
||||
for (auto ni : string_interfaces) {
|
||||
if (ni->send_requested ||
|
||||
(ni->last_send > time || ni->last_send <= time - ni->update_interval)) {
|
||||
unsigned int strl = strlen(ni->key) + 1;
|
||||
unsigned int strl_value = strlen(ni->value) + 1;
|
||||
unsigned int buf_len_needed = 1 + strl + strl_value;
|
||||
buf_len_needed += buf_len_needed / 0xFF + 3 + 2; // COBS overhead
|
||||
if (BUF_SIZE < buf_len_needed) continue; // Error: update to large, skip interface
|
||||
if (BUF_SIZE < buf_len_used + buf_len_needed + 2) {
|
||||
uint8_t cobs_buf[BUF_SIZE];
|
||||
uint16_t crc = calc_fletcher_checksum(buf, buf_len_used);
|
||||
buf[buf_len_used++] = crc >> 8;
|
||||
buf[buf_len_used++] = crc & 0xFF;
|
||||
unsigned int cobs_buf_needed =
|
||||
cobs_encode(buf, buf_len_used, cobs_buf, BUF_SIZE);
|
||||
write_pkg(cobs_buf, cobs_buf_needed);
|
||||
encode_and_write_pkg(buf, buf_len_used);
|
||||
buf_len_used = 0;
|
||||
}
|
||||
buf[buf_len_used++] = 0x02;
|
||||
memcpy((char *)&buf[buf_len_used], ni->key, strl);
|
||||
buf_len_used += strl;
|
||||
memcpy((char *)&buf[buf_len_used], ni->value, strl_value);
|
||||
buf_len_used += strl_value;
|
||||
ni->last_send = time;
|
||||
ni->send_requested = false;
|
||||
}
|
||||
}
|
||||
if (buf_len_used == 0) return;
|
||||
uint8_t cobs_buf[BUF_SIZE];
|
||||
uint16_t crc = calc_fletcher_checksum(buf, buf_len_used);
|
||||
buf[buf_len_used++] = crc >> 8;
|
||||
buf[buf_len_used++] = crc & 0xFF;
|
||||
unsigned int cobs_buf_needed = cobs_encode(buf, buf_len_used, cobs_buf, BUF_SIZE);
|
||||
write_pkg(cobs_buf, cobs_buf_needed);
|
||||
}
|
||||
|
||||
int parse_stream_buf(uint8_t *buf, unsigned int buf_size) {
|
||||
int parsed = 0;
|
||||
@ -307,8 +434,10 @@ class SimpleSync {
|
||||
unsigned int buf_used = buf_size - parsed;
|
||||
uint8_t packet_buf[BUF_SIZE];
|
||||
unsigned int packed_buf_used = sizeof(packet_buf);
|
||||
if (cobs_decode(buf + parsed, buf_used, packet_buf, packed_buf_used) == 0) {
|
||||
parse_pkg(packet_buf, packed_buf_used);
|
||||
if (cobs_decode(buf + parsed, buf_used, packet_buf, packed_buf_used) >= 0) {
|
||||
if (parse_pkg(packet_buf, packed_buf_used) < 0) {
|
||||
// TODO error
|
||||
}
|
||||
}
|
||||
parsed = i + 1;
|
||||
}
|
||||
@ -326,7 +455,7 @@ class SimpleSync {
|
||||
uint8_t packet_buf[BUF_SIZE];
|
||||
unsigned int packed_buf_used = sizeof(packet_buf);
|
||||
if (cobs_decode(stream_buf, stream_buf_pkg_delimiter, packet_buf,
|
||||
packed_buf_used) == 0) {
|
||||
packed_buf_used) >= 0) {
|
||||
parse_pkg(packet_buf, packed_buf_used);
|
||||
}
|
||||
i += 1;
|
||||
@ -350,31 +479,29 @@ class SimpleSync {
|
||||
return 0;
|
||||
}
|
||||
|
||||
NumberInterface *get_number_if(const char *const key) const {
|
||||
for (const auto ni : number_interfaces) {
|
||||
if (strcmp(ni->key, key) == 0) return ni;
|
||||
}
|
||||
int *get_number(const char *const key) {
|
||||
Interface *i = get_interface(0x01, key);
|
||||
if (i) return (int *)i->data;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
StringInterface *get_string_if(const char *const key) const {
|
||||
for (const auto ni : string_interfaces) {
|
||||
if (strcmp(ni->key, key) == 0) return ni;
|
||||
}
|
||||
string *get_string(const char *const key) {
|
||||
Interface *i = get_interface(0x02, key);
|
||||
if (i) return (string *)i->data;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
#ifdef ARDUINO
|
||||
SimpleSync(Print *ostream, function<void(NumberInterface *&, char *, int)> number_update,
|
||||
function<void(StringInterface *&, char *, char *)> string_update)
|
||||
: write_pkg([ostream](uint8_t *buf, unsigned int buf_size) {
|
||||
SimpleSync(Print *ostream, function<timepoint_t()> get_time)
|
||||
: SimpleSync(
|
||||
[ostream](uint8_t *buf, unsigned int buf_size) {
|
||||
if (ostream) {
|
||||
ostream->write(buf, buf_size);
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}),
|
||||
number_update(number_update), string_update(string_update) {}
|
||||
},
|
||||
get_time) {}
|
||||
int handle_stream(Stream &in_stream) {
|
||||
unsigned int buf_new_size = in_stream.available();
|
||||
if (buf_new_size + stream_buf_used > BUF_SIZE) {
|
||||
@ -390,81 +517,5 @@ class SimpleSync {
|
||||
#endif
|
||||
};
|
||||
|
||||
template <typename time_t, typename timedelta_t, unsigned int buf_len = 256,
|
||||
unsigned int str_len_max = 32>
|
||||
class SimpleSyncStatic : public SimpleSync<time_t, timedelta_t, buf_len, str_len_max> {
|
||||
public:
|
||||
SimpleSyncStatic(function<int(uint8_t *, unsigned int)> write_pkg)
|
||||
: SimpleSync<time_t, timedelta_t, buf_len, str_len_max>(write_pkg, nullptr, nullptr){};
|
||||
#ifdef ARDUINO
|
||||
SimpleSyncStatic(Print *ostream)
|
||||
: SimpleSync<time_t, timedelta_t, buf_len, str_len_max>(ostream, nullptr, nullptr) {}
|
||||
#endif
|
||||
};
|
||||
template <typename time_t, typename timedelta_t, unsigned int buf_len = 256,
|
||||
unsigned int str_len_max = 32>
|
||||
class SimpleSyncDynamic : public SimpleSync<time_t, timedelta_t, buf_len, str_len_max> {
|
||||
std::vector<typename SimpleSync<time_t, timedelta_t, buf_len, str_len_max>::NumberInterface *>
|
||||
dyn_number_interfaces;
|
||||
std::vector<typename SimpleSync<time_t, timedelta_t, buf_len, str_len_max>::StringInterface *>
|
||||
dyn_string_interfaces;
|
||||
std::vector<char *> dyn_keys;
|
||||
|
||||
public:
|
||||
SimpleSyncDynamic(
|
||||
function<int(uint8_t *, unsigned int)> write_pkg,
|
||||
function<
|
||||
void(typename SimpleSync<time_t, timedelta_t, buf_len, str_len_max>::NumberInterface *&,
|
||||
char *, int)>
|
||||
dyn_number_update,
|
||||
function<
|
||||
void(typename SimpleSync<time_t, timedelta_t, buf_len, str_len_max>::StringInterface *&,
|
||||
char *, char *)>
|
||||
dyn_string_update,
|
||||
timedelta_t dyn_update_feq)
|
||||
: SimpleSync<time_t, timedelta_t, buf_len, str_len_max>(
|
||||
write_pkg,
|
||||
[this, dyn_number_update, dyn_update_feq](
|
||||
typename SimpleSync<time_t, timedelta_t, buf_len, str_len_max>::NumberInterface *
|
||||
&ni,
|
||||
char *key, int value) {
|
||||
if (ni == nullptr) {
|
||||
auto allocated_key = (char *)malloc(strlen(key) + 1);
|
||||
strcpy(allocated_key, key);
|
||||
dyn_keys.push_back(allocated_key);
|
||||
ni = new SimpleSync<time_t, timedelta_t, buf_len,
|
||||
str_len_max>::NumberInterface(*this, allocated_key,
|
||||
dyn_update_feq);
|
||||
ni->send_requested = false;
|
||||
dyn_number_interfaces.push_back(ni);
|
||||
}
|
||||
dyn_number_update(ni, key, value);
|
||||
},
|
||||
[this, dyn_string_update, dyn_update_feq](
|
||||
typename SimpleSync<time_t, timedelta_t, buf_len, str_len_max>::StringInterface *
|
||||
&ni,
|
||||
char *key, char *value) {
|
||||
if (ni == nullptr) {
|
||||
auto allocated_key = (char *)malloc(strlen(key) + 1);
|
||||
strcpy(allocated_key, key);
|
||||
dyn_keys.push_back(allocated_key);
|
||||
ni = new SimpleSync<time_t, timedelta_t, buf_len,
|
||||
str_len_max>::StringInterface(*this, allocated_key,
|
||||
dyn_update_feq);
|
||||
ni->send_requested = false;
|
||||
dyn_string_interfaces.push_back(ni);
|
||||
}
|
||||
dyn_string_update(ni, key, value);
|
||||
}){};
|
||||
~SimpleSyncDynamic() {
|
||||
for (auto i : dyn_number_interfaces) delete i;
|
||||
for (auto i : dyn_string_interfaces) delete i;
|
||||
for (auto i : dyn_keys) free(i);
|
||||
};
|
||||
#ifdef ARDUINO
|
||||
SimpleSyncDynamic(Print *ostream)
|
||||
: SimpleSync<time_t, timedelta_t, buf_len, str_len_max>(ostream, nullptr, nullptr) {}
|
||||
#endif
|
||||
};
|
||||
} // namespace simplesync
|
||||
#endif // SIMPLESYNC_HPP
|
||||
|
@ -1,4 +1,5 @@
|
||||
#include <chrono>
|
||||
#include <functional>
|
||||
#include <pybind11/cast.h>
|
||||
#include <pybind11/chrono.h>
|
||||
#include <pybind11/functional.h>
|
||||
@ -8,44 +9,48 @@
|
||||
#include "simplesync.hpp"
|
||||
|
||||
namespace py = pybind11;
|
||||
typedef simplesync::SimpleSyncDynamic<std::chrono::time_point<std::chrono::steady_clock>,
|
||||
std::chrono::nanoseconds, 1024>
|
||||
typedef simplesync::SimpleSync<std::chrono::time_point<std::chrono::steady_clock>,
|
||||
std::chrono::nanoseconds, 1024, true>
|
||||
SimpleS;
|
||||
|
||||
PYBIND11_MODULE(pysimplesync, m) {
|
||||
py::class_<SimpleS>(m, "SimpleSync")
|
||||
.def(py::init([]() {
|
||||
.def(py::init([](const std::function<void(py::bytes)> &write) {
|
||||
return std::unique_ptr<SimpleS>(new SimpleS(
|
||||
[](uint8_t *buf, unsigned int buf_size) {
|
||||
py::print(py::bytes((char *)buf, buf_size));
|
||||
[write](uint8_t *buf, unsigned int buf_size) {
|
||||
std::string sbuf;
|
||||
sbuf.assign((char *)buf, buf_size);
|
||||
write(py::bytes(sbuf));
|
||||
// py::print(py::bytes((char *)buf, buf_size));
|
||||
return 0;
|
||||
},
|
||||
[](SimpleS::NumberInterface *&, char *key, int value) {
|
||||
py::print(std::string(key), ": ", value);
|
||||
},
|
||||
nullptr, std::chrono::nanoseconds::max()));
|
||||
[]() { return (std::chrono::steady_clock::now()); }));
|
||||
}))
|
||||
.def("request_all_interfaces", &SimpleS::request_all_interfaces)
|
||||
.def(
|
||||
"__getitem__",
|
||||
[](SimpleS &s, const std::string &key) -> py::object {
|
||||
auto ni = s.get_number_if(key.c_str());
|
||||
if (ni) return py::int_(ni->value);
|
||||
auto si = s.get_string_if(key.c_str());
|
||||
if (si) return py::str(si->value);
|
||||
auto ni = s.get_number(key.c_str());
|
||||
if (ni) return py::int_(*ni);
|
||||
auto si = s.get_string(key.c_str());
|
||||
if (si) return py::str(*si);
|
||||
throw pybind11::key_error();
|
||||
},
|
||||
py::is_operator())
|
||||
.def(
|
||||
"__getitem__",
|
||||
[](SimpleS &s, unsigned int i) -> py::tuple {
|
||||
if (i < s.number_interfaces.size()) {
|
||||
return py::make_tuple(py::str(s.number_interfaces[i]->key),
|
||||
py::int_(s.number_interfaces[i]->value));
|
||||
if (i < s.interfaces.size()) {
|
||||
auto &interface = s.interfaces[i];
|
||||
if (!interface) throw pybind11::index_error();
|
||||
switch (interface->type) {
|
||||
case 0x01:
|
||||
return py::make_tuple(py::str(interface->key),
|
||||
py::int_(*s.get_number(interface->key.c_str())));
|
||||
case 0x02:
|
||||
return py::make_tuple(py::str(interface->key),
|
||||
py::str(*s.get_string(interface->key.c_str())));
|
||||
}
|
||||
i -= s.number_interfaces.size();
|
||||
if (i < s.string_interfaces.size()) {
|
||||
return py::make_tuple(py::str(s.string_interfaces[i]->key),
|
||||
py::str(s.string_interfaces[i]->value));
|
||||
}
|
||||
throw pybind11::index_error();
|
||||
},
|
||||
@ -54,19 +59,16 @@ PYBIND11_MODULE(pysimplesync, m) {
|
||||
"__setitem__",
|
||||
[](SimpleS &s, const std::string &key, py::object value) {
|
||||
if (py::isinstance<py::int_>(value)) {
|
||||
auto ni = s.get_number_if(key.c_str());
|
||||
auto ni = s.get_or_create_interface(0x01, key.c_str());
|
||||
if (ni) {
|
||||
ni->value = value.cast<int>();
|
||||
*(int *)ni->data = value.cast<int>();
|
||||
ni->send_requested = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if (py::isinstance<py::str>(value)) {
|
||||
auto ni = s.get_string_if(key.c_str());
|
||||
} else if (py::isinstance<py::str>(value)) {
|
||||
auto ni = s.get_or_create_interface(0x02, key.c_str());
|
||||
if (ni) {
|
||||
if(value.cast<std::string>().length() > SimpleS::STR_LEN_MAX)
|
||||
throw pybind11::buffer_error();
|
||||
strcpy(ni->value, value.cast<std::string>().c_str());
|
||||
*(std::string *)ni->data = value.cast<std::string>();
|
||||
ni->send_requested = true;
|
||||
return;
|
||||
}
|
||||
@ -75,7 +77,7 @@ PYBIND11_MODULE(pysimplesync, m) {
|
||||
throw pybind11::key_error();
|
||||
},
|
||||
py::is_operator())
|
||||
.def("update", [](SimpleS &s) { s.update(std::chrono::steady_clock::now()); })
|
||||
.def("update", &SimpleS::update)
|
||||
.def("handle_stream", [](SimpleS &s, const std::string &buf_new) {
|
||||
/* handle_stream(b'\x06\x01\x74\x65\x73\x74\x04\x6d\x21\x97\x00') */
|
||||
s.handle_stream((const uint8_t *)buf_new.data(), buf_new.length());
|
||||
|
@ -1,22 +1,22 @@
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <gtest/gtest.h>
|
||||
#include <iostream>
|
||||
|
||||
#include "simplesync.hpp"
|
||||
|
||||
|
||||
TEST(simplesync_test, varint) {
|
||||
|
||||
uint8_t buf[1024];
|
||||
for (int i = -999999; i < 999999; i += 29) {
|
||||
int offset = simplesync::encode_varint(i, buf);
|
||||
int offset = simplesync::encode_varint(i, buf, sizeof(buf));
|
||||
ASSERT_LE(1, offset);
|
||||
for (int o = 0; o < offset - 1; o += 1)
|
||||
EXPECT_FALSE(buf[o] & 0x80) << "offset:" << offset << " " << (uint32_t)(buf[o] & 0x7F);
|
||||
EXPECT_TRUE(buf[offset - 1] & 0x80)
|
||||
<< "offset:" << offset << " " << (uint32_t)(buf[offset - 1] & 0x7F);
|
||||
int out;
|
||||
offset = simplesync::decode_varint(buf, out);
|
||||
offset = simplesync::decode_varint(buf, sizeof(buf), out);
|
||||
ASSERT_LE(1, offset);
|
||||
ASSERT_EQ(out, i);
|
||||
}
|
||||
@ -33,8 +33,8 @@ TEST(simplesync_test, cobs_encode) {
|
||||
|
||||
unsigned int buf_out_used = sizeof(buf);
|
||||
unsigned int buf_in_used = sizeof(buf_coded);
|
||||
int error = simplesync::cobs_decode(buf_coded, buf_in_used, buf, buf_out_used);
|
||||
ASSERT_EQ(error, 0);
|
||||
int decoded_bytes = simplesync::cobs_decode(buf_coded, buf_in_used, buf, buf_out_used);
|
||||
ASSERT_EQ(decoded_bytes, sizeof(buf_raw));
|
||||
ASSERT_EQ(buf_used, sizeof(buf_coded));
|
||||
ASSERT_EQ(buf_out_used, sizeof(buf_raw));
|
||||
ASSERT_EQ(0, memcmp(buf_raw, buf, sizeof(buf_raw)));
|
||||
@ -46,33 +46,47 @@ unsigned int buf_used = 0;
|
||||
|
||||
TEST(simplesync_test, encode_decode) {
|
||||
buf_used = 0;
|
||||
typedef simplesync::SimpleSyncStatic<int, int, 999> S;
|
||||
S s([](uint8_t *buf, unsigned int buf_size) {
|
||||
int time = 0;
|
||||
typedef simplesync::SimpleSync<int, int, 512, true> S;
|
||||
S s_send(
|
||||
[](uint8_t *buf, unsigned int buf_size) {
|
||||
memcpy(stream_buf + buf_used, buf, buf_size);
|
||||
buf_used += buf_size;
|
||||
return 0;
|
||||
});
|
||||
},
|
||||
[&]() { return time; });
|
||||
S s_recv([](uint8_t *, unsigned int) { return 0; }, [&]() { return time; });
|
||||
|
||||
S::NumberInterface n1(s, (char *)"n1", 0);
|
||||
S::StringInterface s1(s, (char *)"s1", 0);
|
||||
S::NumberInterface n2(s, (char *)"n2", 2);
|
||||
int n1 = 4;
|
||||
int n2 = 2;
|
||||
std::string str = "hello";
|
||||
S::NumberPtrPeriodic(s_send, "n1", &n1, 0);
|
||||
S::NumberPtrPeriodic(s_send, "n2", &n2, 2);
|
||||
S::TextPtrPeriodic(s_send, "s1", &str, 0);
|
||||
|
||||
n1.value = 4;
|
||||
strcpy(s1.value, "hallo");
|
||||
n2.value = 2;
|
||||
|
||||
for (int time = 0; time < 5; time += 1) {
|
||||
for (time = 0; time < 5; time += 1) {
|
||||
buf_used = 0;
|
||||
s.update(time);
|
||||
s.update(time);
|
||||
s_send.update();
|
||||
s_send.update();
|
||||
for (unsigned int i = 0; i < buf_used; i += 1)
|
||||
std::cout << std::hex << (int)stream_buf[i] << " ";
|
||||
std::cout << std::dec << std::endl;
|
||||
|
||||
unsigned int parsed = 0;
|
||||
for (unsigned int i = 1; i <= buf_used; i += 1)
|
||||
parsed += s.parse_stream_buf(stream_buf + parsed, i - parsed);
|
||||
parsed += s_recv.parse_stream_buf(stream_buf + parsed, i - parsed);
|
||||
ASSERT_EQ(parsed, buf_used);
|
||||
}
|
||||
ASSERT_EQ(n1.value, 4);
|
||||
ASSERT_EQ(n2.value, 2);
|
||||
ASSERT_STREQ(s1.value,"hallo");
|
||||
str = "hollo2";
|
||||
ASSERT_NE(s_recv.get_number((char *)"n1"), nullptr);
|
||||
ASSERT_EQ(*s_recv.get_number((char *)"n1"), n1);
|
||||
ASSERT_NE(s_recv.get_number((char *)"n2"), nullptr);
|
||||
ASSERT_EQ(*s_recv.get_number((char *)"n2"), n2);
|
||||
ASSERT_STREQ(s_send.get_string((char *)"s1")->c_str(), "hollo2");
|
||||
ASSERT_STREQ(str.c_str(), "hollo2");
|
||||
ASSERT_STREQ(s_recv.get_string((char *)"s1")->c_str(), "hello");
|
||||
ASSERT_EQ(4, n1);
|
||||
ASSERT_EQ(2, n2);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user