@technoculture/data-bridge 0.1.1 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/CMakeLists.txt +10 -2
  2. package/deps/include/data_bridge/config.hpp +69 -0
  3. package/deps/include/data_bridge/protocol/crc16.hpp +8 -0
  4. package/deps/include/data_bridge/protocol/crc32.hpp +19 -0
  5. package/deps/include/data_bridge/protocol/packet.hpp +175 -0
  6. package/deps/include/data_bridge/protocol/reassembler.hpp +80 -0
  7. package/deps/include/data_bridge/transport/factory.hpp +0 -0
  8. package/deps/include/data_bridge/transport/iserial_port.hpp +15 -0
  9. package/deps/include/data_bridge/transport/serial_port.hpp +28 -0
  10. package/deps/src/CMakeLists.txt +18 -0
  11. package/deps/src/protocol/crc16.cpp +19 -0
  12. package/deps/src/protocol/packet.cpp +1 -0
  13. package/deps/src/transport/platform/linux/linux_serial.cpp +53 -0
  14. package/deps/src/transport/platform/windows/windows_serial.cpp +51 -0
  15. package/dist/index.d.mts +41 -72
  16. package/dist/index.d.ts +41 -72
  17. package/dist/index.js +196 -102
  18. package/dist/index.mjs +194 -103
  19. package/lib/index.ts +12 -160
  20. package/lib/native.ts +71 -0
  21. package/lib/reliable.ts +234 -0
  22. package/lib/resilient.ts +30 -6
  23. package/package.json +6 -4
  24. package/prebuilds/darwin-arm64/Release/data_bridge_node.node +0 -0
  25. package/src/addon.cpp +248 -137
  26. package/prebuilds/darwin-arm64/.ninja_deps +0 -0
  27. package/prebuilds/darwin-arm64/.ninja_log +0 -6
  28. package/prebuilds/darwin-arm64/CMakeCache.txt +0 -398
  29. package/prebuilds/darwin-arm64/CMakeFiles/4.0.3/CMakeCCompiler.cmake +0 -84
  30. package/prebuilds/darwin-arm64/CMakeFiles/4.0.3/CMakeCXXCompiler.cmake +0 -104
  31. package/prebuilds/darwin-arm64/CMakeFiles/4.0.3/CMakeDetermineCompilerABI_C.bin +0 -0
  32. package/prebuilds/darwin-arm64/CMakeFiles/4.0.3/CMakeDetermineCompilerABI_CXX.bin +0 -0
  33. package/prebuilds/darwin-arm64/CMakeFiles/4.0.3/CMakeSystem.cmake +0 -15
  34. package/prebuilds/darwin-arm64/CMakeFiles/4.0.3/CompilerIdC/CMakeCCompilerId.c +0 -905
  35. package/prebuilds/darwin-arm64/CMakeFiles/4.0.3/CompilerIdC/a.out +0 -0
  36. package/prebuilds/darwin-arm64/CMakeFiles/4.0.3/CompilerIdC/apple-sdk.c +0 -1
  37. package/prebuilds/darwin-arm64/CMakeFiles/4.0.3/CompilerIdCXX/CMakeCXXCompilerId.cpp +0 -920
  38. package/prebuilds/darwin-arm64/CMakeFiles/4.0.3/CompilerIdCXX/a.out +0 -0
  39. package/prebuilds/darwin-arm64/CMakeFiles/4.0.3/CompilerIdCXX/apple-sdk.cpp +0 -1
  40. package/prebuilds/darwin-arm64/CMakeFiles/CMakeConfigureLog.yaml +0 -531
  41. package/prebuilds/darwin-arm64/CMakeFiles/InstallScripts.json +0 -7
  42. package/prebuilds/darwin-arm64/CMakeFiles/TargetDirectories.txt +0 -3
  43. package/prebuilds/darwin-arm64/CMakeFiles/cmake.check_cache +0 -1
  44. package/prebuilds/darwin-arm64/CMakeFiles/data_bridge_node.dir/Users/satyamtiwary/Documents/Python-Things/data-bridge/src/protocol/crc16.cpp.o +0 -0
  45. package/prebuilds/darwin-arm64/CMakeFiles/data_bridge_node.dir/Users/satyamtiwary/Documents/Python-Things/data-bridge/src/transport/platform/linux/linux_serial.cpp.o +0 -0
  46. package/prebuilds/darwin-arm64/CMakeFiles/data_bridge_node.dir/src/addon.cpp.o +0 -0
  47. package/prebuilds/darwin-arm64/CMakeFiles/data_bridge_node.dir/src/serial_wrapper.cpp.o +0 -0
  48. package/prebuilds/darwin-arm64/CMakeFiles/rules.ninja +0 -64
  49. package/prebuilds/darwin-arm64/build.ninja +0 -192
  50. package/prebuilds/darwin-arm64/cmake_install.cmake +0 -61
package/CMakeLists.txt CHANGED
@@ -3,8 +3,16 @@ project(data_bridge_node)
3
3
 
4
4
  set(CMAKE_CXX_STANDARD 17)
5
5
 
6
- # Root of the data-bridge project
7
- get_filename_component(PROJECT_ROOT "${CMAKE_CURRENT_SOURCE_DIR}/../.." ABSOLUTE)
6
+ # Check for bundled dependencies (NPM package mode)
7
+ set(LOCAL_DEPS "${CMAKE_CURRENT_SOURCE_DIR}/deps")
8
+ if(EXISTS "${LOCAL_DEPS}/include")
9
+ message(STATUS "Using bundled dependencies in ${LOCAL_DEPS}")
10
+ set(PROJECT_ROOT "${LOCAL_DEPS}")
11
+ else()
12
+ # Development mode (Monorepo)
13
+ get_filename_component(PROJECT_ROOT "${CMAKE_CURRENT_SOURCE_DIR}/../.." ABSOLUTE)
14
+ message(STATUS "Using development source root: ${PROJECT_ROOT}")
15
+ endif()
8
16
 
9
17
  # Include Node-API headers
10
18
  include_directories(${CMAKE_JS_INC})
@@ -0,0 +1,69 @@
1
+ #pragma once
2
+ #include <cstdlib>
3
+ #include <cstdint>
4
+ #include <string>
5
+
6
+ /**
7
+ * Configuration for Data Bridge protocol.
8
+ *
9
+ * Parameters can be set via environment variables.
10
+ * If not set, defaults are used.
11
+ *
12
+ * Environment variables:
13
+ * DATA_BRIDGE_MAX_RETRIES - Maximum retry attempts (default: 10)
14
+ * DATA_BRIDGE_RETRY_TIMEOUT_MS - Timeout between retries in ms (default: 2000)
15
+ * DATA_BRIDGE_FRAGMENT_SIZE - Maximum fragment payload size (default: 256)
16
+ * DATA_BRIDGE_ACK_TIMEOUT_MS - Timeout waiting for ACK in ms (default: 500)
17
+ */
18
+
19
+ namespace DataBridgeConfig {
20
+
21
+ inline int getEnvInt(const char* name, int defaultValue) {
22
+ const char* val = std::getenv(name);
23
+ if (val == nullptr) return defaultValue;
24
+ try {
25
+ return std::stoi(val);
26
+ } catch (...) {
27
+ return defaultValue;
28
+ }
29
+ }
30
+
31
+ // Maximum number of retry attempts before giving up
32
+ inline uint8_t maxRetries() {
33
+ static uint8_t value = static_cast<uint8_t>(
34
+ getEnvInt("DATA_BRIDGE_MAX_RETRIES", 10)
35
+ );
36
+ return value;
37
+ }
38
+
39
+ // Timeout between retries in milliseconds
40
+ inline uint16_t retryTimeoutMs() {
41
+ static uint16_t value = static_cast<uint16_t>(
42
+ getEnvInt("DATA_BRIDGE_RETRY_TIMEOUT_MS", 2000)
43
+ );
44
+ return value;
45
+ }
46
+
47
+ // Maximum payload size per fragment (for large message fragmentation)
48
+ inline uint16_t fragmentSize() {
49
+ static uint16_t value = static_cast<uint16_t>(
50
+ getEnvInt("DATA_BRIDGE_FRAGMENT_SIZE", 256)
51
+ );
52
+ return value;
53
+ }
54
+
55
+ // Timeout waiting for ACK before retry
56
+ inline uint16_t ackTimeoutMs() {
57
+ static uint16_t value = static_cast<uint16_t>(
58
+ getEnvInt("DATA_BRIDGE_ACK_TIMEOUT_MS", 500)
59
+ );
60
+ return value;
61
+ }
62
+
63
+ // Baud rate (default for most embedded devices)
64
+ inline int baudRate() {
65
+ static int value = getEnvInt("DATA_BRIDGE_BAUD_RATE", 115200);
66
+ return value;
67
+ }
68
+
69
+ } // namespace DataBridgeConfig
@@ -0,0 +1,8 @@
1
+ #pragma once
2
+ #include <cstdint>
3
+ #include <cstddef>
4
+
5
+
6
+ uint16_t crc16_ccitt(const uint8_t* data , size_t len);
7
+
8
+
@@ -0,0 +1,19 @@
1
+ #pragma once
2
+ #include <cstdint>
3
+ #include <cstddef>
4
+
5
+ class CRC32 {
6
+ public:
7
+ static uint32_t calculate(const uint8_t* data, size_t length) {
8
+ uint32_t crc = 0xFFFFFFFF;
9
+ for (size_t i = 0; i < length; ++i) {
10
+ uint8_t byte = data[i];
11
+ crc = crc ^ byte;
12
+ for (int j = 0; j < 8; ++j) {
13
+ uint32_t mask = -(crc & 1);
14
+ crc = (crc >> 1) ^ (0xEDB88320 & mask);
15
+ }
16
+ }
17
+ return ~crc;
18
+ }
19
+ };
@@ -0,0 +1,175 @@
1
+ #pragma once
2
+ #include <vector>
3
+ #include <string>
4
+ #include <cstdint>
5
+ #include <utility>
6
+ #include <cstring>
7
+ #include <algorithm>
8
+ #include <data_bridge/protocol/crc32.hpp>
9
+ #include <data_bridge/config.hpp>
10
+
11
+ // Cross-platform packed struct support
12
+ #if defined(_MSC_VER)
13
+ #define PACKED_STRUCT_BEGIN __pragma(pack(push, 1))
14
+ #define PACKED_STRUCT_END __pragma(pack(pop))
15
+ #define PACKED_ATTR
16
+ #elif defined(__GNUC__) || defined(__clang__)
17
+ #define PACKED_STRUCT_BEGIN
18
+ #define PACKED_STRUCT_END
19
+ #define PACKED_ATTR __attribute__((packed))
20
+ #else
21
+ #define PACKED_STRUCT_BEGIN
22
+ #define PACKED_STRUCT_END
23
+ #define PACKED_ATTR
24
+ #endif
25
+
26
+ struct Packet {
27
+ PACKED_STRUCT_BEGIN
28
+ struct Header {
29
+ uint8_t type;
30
+ uint8_t seq_id;
31
+ uint16_t fragment_id;
32
+ uint16_t total_frags;
33
+ uint16_t payload_len;
34
+ uint32_t crc32;
35
+ } PACKED_ATTR;
36
+ PACKED_STRUCT_END
37
+
38
+ // Packet Types
39
+ static constexpr uint8_t TYPE_DATA = 0x10;
40
+ static constexpr uint8_t TYPE_ACK = 0x20;
41
+ static constexpr uint8_t TYPE_NACK = 0x30;
42
+ static constexpr uint8_t TYPE_SYN = 0x40;
43
+
44
+ // COBS delimiter
45
+ static constexpr uint8_t COBS_DELIMITER = 0x00;
46
+
47
+ // Configuration accessors (use env vars or defaults)
48
+ static uint8_t maxRetries() { return DataBridgeConfig::maxRetries(); }
49
+ static uint16_t retryTimeoutMs() { return DataBridgeConfig::retryTimeoutMs(); }
50
+ static uint16_t fragmentSize() { return DataBridgeConfig::fragmentSize(); }
51
+
52
+ struct Frame {
53
+ Header header;
54
+ std::vector<uint8_t> payload;
55
+ bool valid;
56
+ };
57
+
58
+ // Consistent Overhead Byte Stuffing (COBS) Encoding
59
+ static std::vector<uint8_t> cobs_encode(const std::vector<uint8_t>& data) {
60
+ std::vector<uint8_t> encoded;
61
+ encoded.reserve(data.size() + data.size() / 254 + 2);
62
+
63
+ size_t code_idx = 0;
64
+ encoded.push_back(0); // Placeholder for the first code
65
+ uint8_t code = 1;
66
+
67
+ for (uint8_t byte : data) {
68
+ if (byte == 0) {
69
+ encoded[code_idx] = code;
70
+ code_idx = encoded.size();
71
+ encoded.push_back(0); // Placeholder for next code
72
+ code = 1;
73
+ } else {
74
+ encoded.push_back(byte);
75
+ code++;
76
+ if (code == 0xFF) { // Max run length reached
77
+ encoded[code_idx] = code;
78
+ code_idx = encoded.size();
79
+ encoded.push_back(0);
80
+ code = 1;
81
+ }
82
+ }
83
+ }
84
+ encoded[code_idx] = code;
85
+ return encoded;
86
+ }
87
+
88
+ // COBS Decoding
89
+ static std::vector<uint8_t> cobs_decode(const std::vector<uint8_t>& data) {
90
+ std::vector<uint8_t> decoded;
91
+ decoded.reserve(data.size());
92
+
93
+ size_t i = 0;
94
+ while (i < data.size()) {
95
+ uint8_t code = data[i];
96
+ i++;
97
+ if (code == 0) break; // Should not happen in valid COBS before delimiter
98
+
99
+ for (uint8_t j = 1; j < code; j++) {
100
+ if (i >= data.size()) return {}; // Error: truncated
101
+ decoded.push_back(data[i++]);
102
+ }
103
+ if (code < 0xFF && i < data.size()) {
104
+ decoded.push_back(0);
105
+ }
106
+ }
107
+ return decoded;
108
+ }
109
+
110
+ static std::vector<uint8_t> serialize(uint8_t type, uint8_t seq, const std::string& payload, uint16_t frag_id = 0, uint16_t total_frags = 1) {
111
+ Header header;
112
+ header.type = type;
113
+ header.seq_id = seq;
114
+ header.fragment_id = frag_id;
115
+ header.total_frags = total_frags;
116
+ header.payload_len = static_cast<uint16_t>(payload.size());
117
+ header.crc32 = 0; // Calculated later
118
+
119
+ std::vector<uint8_t> raw_packet;
120
+ raw_packet.resize(sizeof(Header) + payload.size());
121
+
122
+ std::memcpy(raw_packet.data(), &header, sizeof(Header));
123
+ std::memcpy(raw_packet.data() + sizeof(Header), payload.data(), payload.size());
124
+
125
+ // Calculate CRC32 of Header + Payload (with CRC field zeroed)
126
+ uint32_t crc = CRC32::calculate(raw_packet.data(), raw_packet.size());
127
+
128
+ // Update header with calculated CRC
129
+ header.crc32 = crc;
130
+ std::memcpy(raw_packet.data(), &header, sizeof(Header));
131
+
132
+ // Encode with COBS
133
+ std::vector<uint8_t> encoded = cobs_encode(raw_packet);
134
+ encoded.push_back(COBS_DELIMITER);
135
+ return encoded;
136
+ }
137
+
138
+ static Frame deserialize(std::vector<uint8_t>& buffer) {
139
+ // Find delimiter
140
+ auto it = std::find(buffer.begin(), buffer.end(), COBS_DELIMITER);
141
+ if (it == buffer.end()) return {{}, {}, false}; // No complete frame yet
142
+
143
+ std::vector<uint8_t> frame_data(buffer.begin(), it);
144
+ buffer.erase(buffer.begin(), it + 1); // Remove processed frame + delimiter
145
+
146
+ if (frame_data.empty()) return {{}, {}, false};
147
+
148
+ std::vector<uint8_t> decoded = cobs_decode(frame_data);
149
+ if (decoded.size() < sizeof(Header)) return {{}, {}, false};
150
+
151
+ Header header;
152
+ std::memcpy(&header, decoded.data(), sizeof(Header));
153
+
154
+ // Validate Length
155
+ if (decoded.size() != sizeof(Header) + header.payload_len) return {{}, {}, false};
156
+
157
+ // Validate CRC
158
+ uint32_t received_crc = header.crc32;
159
+
160
+ // Zero out CRC in buffer to recompute
161
+ Header* header_ptr = reinterpret_cast<Header*>(decoded.data());
162
+ header_ptr->crc32 = 0;
163
+
164
+ uint32_t computed_crc = CRC32::calculate(decoded.data(), decoded.size());
165
+
166
+ if (received_crc == computed_crc) {
167
+ std::vector<uint8_t> payload(decoded.begin() + sizeof(Header), decoded.end());
168
+ // Retrieve original header
169
+ header.crc32 = received_crc;
170
+ return {header, payload, true};
171
+ }
172
+
173
+ return {{}, {}, false};
174
+ }
175
+ };
@@ -0,0 +1,80 @@
1
+ #pragma once
2
+ #include <data_bridge/protocol/packet.hpp>
3
+ #include <vector>
4
+ #include <iostream>
5
+
6
+ class Reassembler {
7
+ public:
8
+ struct Result {
9
+ bool complete;
10
+ std::vector<uint8_t> payload;
11
+ };
12
+
13
+ Reassembler() : current_seq_id_(255), expected_frag_(0), active_(false) {}
14
+
15
+ // Returns true if fragment was accepted (in sequence).
16
+ // Caller should send ACK if this returns true.
17
+ // If returns false, caller might ignore or send NACK/ACK-of-last-good.
18
+ bool process_fragment(const Packet::Frame& frame) {
19
+ if (!frame.valid) return false;
20
+
21
+ uint8_t seq = frame.header.seq_id;
22
+ uint16_t frag = frame.header.fragment_id;
23
+
24
+ // New Sequence ?
25
+ if (seq != current_seq_id_) {
26
+ // Accept if it's a new message (logic can be more complex for strict strictness,
27
+ // e.g. only seq+1, but for now accept any new seq as new message start)
28
+ if (frag == 0) {
29
+ reset(seq);
30
+ } else {
31
+ return false; // Received middle of new message without start?
32
+ }
33
+ }
34
+
35
+ if (frag != expected_frag_) {
36
+ // Out of order fragment
37
+ return false;
38
+ }
39
+
40
+ // Valid fragment
41
+ buffer_.insert(buffer_.end(), frame.payload.begin(), frame.payload.end());
42
+ expected_frag_++;
43
+ return true;
44
+ }
45
+
46
+ bool is_complete(const Packet::Frame& last_frame) const {
47
+ if (!active_) return false;
48
+ return expected_frag_ == last_frame.header.total_frags;
49
+ }
50
+
51
+ bool is_duplicate(const Packet::Frame& frame) const {
52
+ if (!active_) return false;
53
+ uint8_t seq = frame.header.seq_id;
54
+ uint16_t frag = frame.header.fragment_id;
55
+ return (seq == current_seq_id_ && frag < expected_frag_);
56
+ }
57
+
58
+ std::vector<uint8_t> get_data() const {
59
+ return buffer_;
60
+ }
61
+
62
+ size_t get_buffered_size() const {
63
+ return buffer_.size();
64
+ }
65
+
66
+ uint8_t get_current_seq() const { return current_seq_id_; }
67
+
68
+ private:
69
+ void reset(uint8_t seq) {
70
+ buffer_.clear();
71
+ current_seq_id_ = seq;
72
+ expected_frag_ = 0;
73
+ active_ = true;
74
+ }
75
+
76
+ uint8_t current_seq_id_;
77
+ uint16_t expected_frag_;
78
+ std::vector<uint8_t> buffer_;
79
+ bool active_;
80
+ };
File without changes
@@ -0,0 +1,15 @@
1
+ #pragma once
2
+ #include <vector>
3
+ #include <string>
4
+ #include <cstdint>
5
+
6
+ class ISerialPort {
7
+ public:
8
+ virtual ~ISerialPort() = default;
9
+
10
+ virtual bool open(const std::string& port_name, int baud_rate) = 0;
11
+ virtual void close() = 0;
12
+
13
+ virtual int write(const std::vector<uint8_t>& data) = 0;
14
+ virtual int read(uint8_t* buffer, size_t size) = 0;
15
+ };
@@ -0,0 +1,28 @@
1
+ #pragma once
2
+ #include <memory>
3
+ #include <vector>
4
+ #include <string>
5
+
6
+ #include <data_bridge/transport/iserial_port.hpp>
7
+
8
+ class SerialPort : public ISerialPort {
9
+
10
+ public:
11
+ SerialPort();
12
+ ~SerialPort();
13
+
14
+ // Prevent copying (Serial ports are unique resources)
15
+ SerialPort(const SerialPort&) = delete;
16
+ SerialPort& operator=(const SerialPort&) = delete;
17
+
18
+ bool open(const std::string& port_name, int baud_rate) override;
19
+ void close() override;
20
+
21
+ int write(const std::vector<uint8_t>& data) override;
22
+ int read(uint8_t* buffer, size_t size) override;
23
+
24
+ private:
25
+ // The "Pimpl" - This struct is defined only in the .cpp files
26
+ struct Impl;
27
+ std::unique_ptr<Impl> pimpl;
28
+ };
@@ -0,0 +1,18 @@
1
+ add_library(data_bridge STATIC
2
+ protocol/crc16.cpp
3
+ )
4
+
5
+ # Platform specific sources
6
+ if(WIN32)
7
+ target_sources(data_bridge PRIVATE transport/platform/windows/windows_serial.cpp)
8
+ else()
9
+ target_sources(data_bridge PRIVATE transport/platform/linux/linux_serial.cpp)
10
+ endif()
11
+
12
+ target_include_directories(data_bridge PUBLIC
13
+ $<BUILD_INTERFACE:${CMAKE_SOURCE_DIR}/include>
14
+ $<INSTALL_INTERFACE:include>
15
+ )
16
+
17
+ # Export the library name for other subdirectories
18
+ set(DATA_BRIDGE_LIB data_bridge PARENT_SCOPE)
@@ -0,0 +1,19 @@
1
+ #include <data_bridge/protocol/crc16.hpp>
2
+
3
+ uint16_t crc16_ccitt(const uint8_t* data, size_t len) {
4
+ uint16_t crc = 0xFFFF;
5
+
6
+ for (size_t i = 0; i < len; ++i) {
7
+ crc ^= static_cast<uint16_t>(data[i]) << 8;
8
+
9
+ for (int bit = 0; bit < 8; ++bit) {
10
+ if (crc & 0x8000)
11
+ crc = (crc << 1) ^ 0x1021;
12
+ else
13
+ crc <<= 1;
14
+ }
15
+ }
16
+
17
+ return crc;
18
+ }
19
+
@@ -0,0 +1 @@
1
+ #include "protocol/packet.hpp"
@@ -0,0 +1,53 @@
1
+ #include <data_bridge/transport/serial_port.hpp>
2
+ #include <fcntl.h>
3
+ #include <termios.h>
4
+ #include <unistd.h>
5
+
6
+ struct SerialPort::Impl {
7
+ int fd = -1;
8
+ };
9
+
10
+ SerialPort::SerialPort() : pimpl(std::make_unique<Impl>()) {}
11
+ SerialPort::~SerialPort() { close(); }
12
+
13
+ bool SerialPort::open(const std::string& port, int baud) {
14
+ pimpl->fd = ::open(port.c_str(), O_RDWR | O_NOCTTY | O_NDELAY);
15
+ if (pimpl->fd == -1) return false;
16
+
17
+ // Clear O_NDELAY to make it blocking (uses VTIME)
18
+ fcntl(pimpl->fd, F_SETFL, 0);
19
+
20
+ struct termios tty;
21
+ if (tcgetattr(pimpl->fd, &tty) != 0) return false;
22
+
23
+ cfsetospeed(&tty, B115200);
24
+ cfsetispeed(&tty, B115200);
25
+
26
+ // RAW MODE - Essential for binary/JSON data
27
+ cfmakeraw(&tty);
28
+ tty.c_cflag |= (CLOCAL | CREAD);
29
+
30
+ // Explicitly disable flow control just in case cfmakeraw doesn't (it should)
31
+ tty.c_cflag &= ~CRTSCTS;
32
+
33
+ tty.c_cc[VMIN] = 0; // Non-blocking
34
+ tty.c_cc[VTIME] = 1; // 0.1 second timeout (faster polling)
35
+
36
+ tcflush(pimpl->fd, TCIOFLUSH); // Flush on open to clear old buffer garbage
37
+ return tcsetattr(pimpl->fd, TCSANOW, &tty) == 0;
38
+ }
39
+
40
+ int SerialPort::write(const std::vector<uint8_t>& data) {
41
+ int written = ::write(pimpl->fd, data.data(), data.size());
42
+ // Note: tcdrain() removed - it blocks indefinitely on PTYs (used for testing)
43
+ // For real serial ports, the write() is sufficient as our protocol handles ACKs
44
+ return written;
45
+ }
46
+
47
+ int SerialPort::read(uint8_t* buffer, size_t size) {
48
+ return ::read(pimpl->fd, buffer, size);
49
+ }
50
+
51
+ void SerialPort::close() {
52
+ if (pimpl->fd != -1) { ::close(pimpl->fd); pimpl->fd = -1; }
53
+ }
@@ -0,0 +1,51 @@
1
+ #include <data_bridge/transport/serial_port.hpp>
2
+ #include <windows.h>
3
+
4
+ struct SerialPort::Impl {
5
+ HANDLE hSerial = INVALID_HANDLE_VALUE;
6
+ };
7
+
8
+ SerialPort::SerialPort() : pimpl(std::make_unique<Impl>()) {}
9
+ SerialPort::~SerialPort() { close(); }
10
+
11
+ bool SerialPort::open(const std::string& port, int baud) {
12
+ pimpl->hSerial = CreateFileA(port.c_str(), GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
13
+ if (pimpl->hSerial == INVALID_HANDLE_VALUE) return false;
14
+
15
+ DCB dcb = {0};
16
+ dcb.DCBlength = sizeof(dcb);
17
+ GetCommState(pimpl->hSerial, &dcb);
18
+ dcb.BaudRate = CBR_115200;
19
+ dcb.ByteSize = 8;
20
+ dcb.StopBits = ONESTOPBIT;
21
+ dcb.Parity = NOPARITY;
22
+ SetCommState(pimpl->hSerial, &dcb);
23
+
24
+ // TIMEOUTS - Prevents the app from freezing
25
+ COMMTIMEOUTS timeouts = { 0 };
26
+ timeouts.ReadIntervalTimeout = 50;
27
+ timeouts.ReadTotalTimeoutConstant = 50;
28
+ timeouts.ReadTotalTimeoutMultiplier = 10;
29
+ SetCommTimeouts(pimpl->hSerial, &timeouts);
30
+
31
+ return true;
32
+ }
33
+
34
+ int SerialPort::write(const std::vector<uint8_t>& data) {
35
+ DWORD written;
36
+ WriteFile(pimpl->hSerial, data.data(), (DWORD)data.size(), &written, NULL);
37
+ return (int)written;
38
+ }
39
+
40
+ int SerialPort::read(uint8_t* buffer, size_t size) {
41
+ DWORD read;
42
+ if (!ReadFile(pimpl->hSerial, buffer, (DWORD)size, &read, NULL)) return -1;
43
+ return (int)read;
44
+ }
45
+
46
+ void SerialPort::close() {
47
+ if (pimpl->hSerial != INVALID_HANDLE_VALUE) {
48
+ CloseHandle(pimpl->hSerial);
49
+ pimpl->hSerial = INVALID_HANDLE_VALUE;
50
+ }
51
+ }
package/dist/index.d.mts CHANGED
@@ -1,5 +1,45 @@
1
1
  import { EventEmitter } from 'events';
2
2
 
3
+ interface DataBridgeOptions {
4
+ baudRate?: number;
5
+ maxRetries?: number;
6
+ ackTimeoutMs?: number;
7
+ fragmentSize?: number;
8
+ }
9
+ /**
10
+ * ReliableDataBridge
11
+ *
12
+ * Implements Stop-and-Wait ARQ with fragmentation/reassembly.
13
+ * Mirrors the Python bindings.wrapper.DataBridge logic.
14
+ */
15
+ declare class ReliableDataBridge extends EventEmitter {
16
+ private serial;
17
+ private reassembler;
18
+ private isOpen_;
19
+ private options;
20
+ private seqId;
21
+ private rxBuffer;
22
+ private pendingAcks;
23
+ constructor(options?: DataBridgeOptions);
24
+ static open(port: string, baudOrOptions?: number | DataBridgeOptions, cb?: (data: Buffer) => void): Promise<ReliableDataBridge>;
25
+ open(port: string, baud?: number): Promise<boolean>;
26
+ close(): Promise<void>;
27
+ get isOpen(): boolean;
28
+ send(data: string | Buffer): Promise<void>;
29
+ private sendWithRetry;
30
+ private onData;
31
+ }
32
+
33
+ interface SerialPort {
34
+ open(port: string, baud: number, callback: (data: Buffer) => void): boolean;
35
+ write(data: Buffer): number;
36
+ close(): boolean;
37
+ isOpen(): boolean;
38
+ }
39
+ declare const SerialPort: {
40
+ new (): SerialPort;
41
+ };
42
+
3
43
  /**
4
44
  * ResilientDataBridge - Connection-resilient wrapper
5
45
  *
@@ -74,75 +114,4 @@ declare class ResilientDataBridge extends EventEmitter {
74
114
  emit<K extends keyof ResilientEvents>(event: K, ...args: Parameters<ResilientEvents[K]>): boolean;
75
115
  }
76
116
 
77
- /**
78
- * Data Bridge - Guaranteed Reliable Serial Communication
79
- *
80
- * TypeScript wrapper for the native Node-API addon.
81
- * Provides a clean, async-friendly API for Electron applications.
82
- */
83
-
84
- interface DataBridgeOptions {
85
- baudRate?: number;
86
- }
87
- interface DataBridgeEvents {
88
- data: (data: Buffer) => void;
89
- error: (error: Error) => void;
90
- close: () => void;
91
- }
92
- /**
93
- * DataBridge provides guaranteed reliable serial communication.
94
- *
95
- * Features:
96
- * - Automatic retransmission on packet loss
97
- * - CRC32 integrity checking
98
- * - COBS framing for robust delimitation
99
- * - Fragmentation for large messages
100
- *
101
- * @example
102
- * ```typescript
103
- * const bridge = await DataBridge.open('/dev/ttyUSB0');
104
- *
105
- * bridge.on('data', (data) => {
106
- * console.log('Received:', data.toString());
107
- * });
108
- *
109
- * await bridge.send('Hello, World!');
110
- * await bridge.close();
111
- * ```
112
- */
113
- declare class DataBridge extends EventEmitter {
114
- private native;
115
- private _isOpen;
116
- private constructor();
117
- /**
118
- * Open a serial port with guaranteed reliable communication.
119
- *
120
- * @param port - Serial port path (e.g., '/dev/ttyUSB0' or 'COM3')
121
- * @param options - Configuration options
122
- * @returns Promise resolving to a DataBridge instance
123
- */
124
- static open(port: string, options?: DataBridgeOptions): Promise<DataBridge>;
125
- /**
126
- * Send data with guaranteed delivery.
127
- *
128
- * The data will be fragmented if necessary, checksummed, and
129
- * retransmitted until acknowledged by the receiver.
130
- *
131
- * @param data - Data to send (Buffer or string)
132
- * @returns Promise resolving when data is acknowledged
133
- */
134
- send(data: Buffer | string): Promise<void>;
135
- /**
136
- * Close the serial port.
137
- */
138
- close(): Promise<void>;
139
- /**
140
- * Check if the port is currently open.
141
- */
142
- get isOpen(): boolean;
143
- on<K extends keyof DataBridgeEvents>(event: K, listener: DataBridgeEvents[K]): this;
144
- once<K extends keyof DataBridgeEvents>(event: K, listener: DataBridgeEvents[K]): this;
145
- emit<K extends keyof DataBridgeEvents>(event: K, ...args: Parameters<DataBridgeEvents[K]>): boolean;
146
- }
147
-
148
- export { DataBridge, type DataBridgeEvents, type DataBridgeOptions, ResilientDataBridge, type ResilientEvents, type ResilientOptions, DataBridge as default };
117
+ export { ReliableDataBridge as DataBridge, type DataBridgeOptions, SerialPort as RawSerialPort, ResilientDataBridge, type ResilientEvents, type ResilientOptions };