Skip to content

Commit af4d4a5

Browse files
authored
TCP/IP Bundle Unix (#34)
* first sketch of tcp/ip bundle on unix * fixed bug in bundle and added errors * factoring out encoding and decoding of protobuf * working state with extra function to toggle blocking vs non-blocking * code cleanup * adding nanopb as a proper git submodule * exclude protobuf generated files from checks * fixing ci
1 parent 7b42427 commit af4d4a5

27 files changed

+499
-36
lines changed

.clang-tidy

+4-2
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,10 @@ Checks: '
99
cert-*,
1010
-readability-else-after-return,
1111
-clang-analyzer-core.CallAndMessage,
12-
-clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling'
12+
-clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling
13+
-modernize-macro-to-enum
14+
'
1315
WarningsAsErrors: true
1416
HeaderFilterRegex: ''
1517
AnalyzeTemporaryDtors: false
16-
FormatStyle: none
18+
FormatStyle: none

.gitmodules

+3
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
11
[submodule "external/Unity"]
22
path = external/Unity
33
url = https://github.com/ThrowTheSwitch/Unity.git
4+
[submodule "external/nanopb"]
5+
path = external/nanopb
6+
url = https://github.com/nanopb/nanopb.git

CMakeLists.txt

+26-5
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ cmake_minimum_required(VERSION 3.9)
22
project(reactor-uc LANGUAGES C)
33

44
# Command line options for the build
5+
set(BUILD_EXAMPLES ON CACHE BOOL "Build examples")
56
set(BUILD_TESTS OFF CACHE BOOL "Build all tests")
67
set(BUILD_LF_TESTS OFF CACHE BOOL "Build lf tests")
78
set(BUILD_UNIT_TESTS OFF CACHE BOOL "Build unit tests")
@@ -38,19 +39,39 @@ if(BUILD_TESTS)
3839
endif()
3940
endif()
4041

41-
# TODO: Explicitly name all the sources.
42-
file(GLOB SOURCES "src/*.c")
43-
message(${SOURCES})
42+
file(GLOB SOURCES "src/*.c" "src/generated/*.c")
43+
44+
# you maybe think this is stupid, but the nanopb cmake-package exports only cpp functions & libraries
45+
add_library(nanopb
46+
external/nanopb/pb_decode.c
47+
external/nanopb/pb_encode.c
48+
external/nanopb/pb_decode.c
49+
external/nanopb/pb_common.c
50+
)
51+
52+
set_target_properties(nanopb PROPERTIES C_CLANG_TIDY "") # Disable clang-tidy for this external lib.
53+
set_source_files_properties("external/nanopb/*.c" PROPERTIES SKIP_LINTING ON)
54+
set_source_files_properties(src/generated/message.pb.c include/reactor-uc/generated/message.pb.h PROPERTIES SKIP_LINTING ON)
4455

4556
if (PLATFORM STREQUAL "POSIX")
4657
add_library(reactor-uc STATIC ${SOURCES})
47-
target_compile_definitions(reactor-uc PUBLIC PLATFORM_POSIX)
48-
target_link_libraries(reactor-uc PRIVATE pthread)
58+
add_compile_definitions(PLATFORM_POSIX)
59+
target_include_directories(reactor-uc PUBLIC external/)
60+
target_link_libraries(reactor-uc PRIVATE pthread nanopb)
61+
62+
if(BUILD_EXAMPLES)
63+
add_subdirectory(examples/posix)
64+
endif ()
65+
4966
elseif (PLATFORM STREQUAL "ZEPHYR")
5067
zephyr_library_named(reactor-uc)
5168
zephyr_library_sources(${SOURCES})
5269
zephyr_library_link_libraries(kernel)
5370
add_compile_definitions(PLATFORM_ZEPHYR)
71+
72+
if(BUILD_EXAMPLES)
73+
add_subdirectory(examples/zephyr)
74+
endif ()
5475
else ()
5576
message(FATAL_ERROR "No valid platform specified")
5677
endif ()

Makefile

+3-4
Original file line numberDiff line numberDiff line change
@@ -30,19 +30,18 @@ asan:
3030
make test -C build
3131

3232
# Format the code base
33-
SRC_FILES := $(shell find src -name '*.c')
34-
HDR_FILES := $(shell find include -name '*.h')
33+
SRC_FILES := $(shell find ./src -path ./src/generated -prune -o -name '*.c' -print)
34+
HDR_FILES := $(shell find ./include -path ./include/reactor-uc/generated -prune -o -name '*.h' -print)
35+
3536
format:
3637
clang-format -i -style=file $(SRC_FILES) $(HDR_FILES)
3738

3839
# Check that the code base is formatted
3940
format-check:
4041
clang-format --dry-run --Werror -style=file $(SRC_FILES) $(HDR_FILES)
4142

42-
4343
# Run the entire CI flow
4444
ci: clean test coverage format-check
4545

46-
4746
clean:
4847
rm -rf build

examples/CMakeLists.txt

Whitespace-only changes.

examples/posix/CMakeLists.txt

+10-5
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,14 @@
11
cmake_minimum_required(VERSION 3.20.0)
22
set(PLATFORM "POSIX" CACHE STRING "Platform to target")
33

4-
project(timer-ex)
4+
add_executable(timer_ex timer_ex.c)
5+
target_link_libraries(timer_ex PRIVATE reactor-uc)
56

6-
add_executable(timer_ex)
7-
target_sources(timer_ex PRIVATE timer_ex.c)
8-
add_subdirectory(../../ reactor-uc)
9-
target_link_libraries(timer_ex PRIVATE reactor-uc)
7+
add_executable(tcp_ip_bundle_server testing_posix_tcp_ip_bundle_server.c)
8+
target_link_libraries(tcp_ip_bundle_server PUBLIC reactor-uc)
9+
10+
add_executable(tcp_ip_bundle_client testing_posix_tcp_ip_bundle_client.c)
11+
target_link_libraries(tcp_ip_bundle_client PUBLIC reactor-uc)
12+
13+
add_executable(nanopb_test testing_nanopb.c)
14+
target_link_libraries(nanopb_test PRIVATE reactor-uc nanopb)

examples/posix/testing_nanopb.c

+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
#include <stdio.h>
2+
#include <stdlib.h>
3+
4+
#include "../../../reactor-uc/include/reactor-uc/generated/message.pb.h"
5+
#include "../../../reactor-uc/include/reactor-uc/encoding.h"
6+
7+
#define BUFFER_SIZE 1024
8+
#define MSG_ID 42
9+
10+
int main() {
11+
12+
PortMessage original_message;
13+
PortMessage deserialized_message;
14+
unsigned char buffer[BUFFER_SIZE];
15+
unsigned char * message = NULL;
16+
int message_size = 0;
17+
18+
original_message.connection_number = MSG_ID;
19+
const char* text = "Hello World1234";
20+
memcpy(original_message.message, text, sizeof("Hello World1234")); // NOLINT
21+
22+
message = buffer;
23+
message_size = encode_protobuf(&original_message, buffer, BUFFER_SIZE);
24+
if (message_size < 0) {
25+
printf("encoding failed!\n");
26+
exit(1);
27+
}
28+
29+
int remaining_bytes = decode_protobuf(&deserialized_message, message, message_size);
30+
31+
if (remaining_bytes < 0) {
32+
printf("decoding failed!\n");
33+
exit(1);
34+
}
35+
36+
printf("o: %i d: %i\n", original_message.connection_number, deserialized_message.connection_number);
37+
printf("o: %s d: %s\n", original_message.message, deserialized_message.message);
38+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
#include "reactor-uc/reactor-uc.h"
2+
#include "reactor-uc/platform/posix/tcp_ip_bundle.h"
3+
#include <sys/socket.h>
4+
#include <unistd.h>
5+
6+
#include "reactor-uc/generated/message.pb.h"
7+
8+
int main() {
9+
TcpIpBundle bundle;
10+
11+
// server address
12+
const char* host = "127.0.0.1";
13+
unsigned short port = 8900; // NOLINT
14+
15+
// message for server
16+
PortMessage port_message;
17+
port_message.connection_number = 42; // NOLINT
18+
const char* message = "Hello World1234";
19+
memcpy(port_message.message, message, sizeof("Hello World1234")); // NOLINT
20+
21+
// creating a server that listens on loopback device on port 8900
22+
TcpIpBundle_ctor(&bundle, host, port, AF_INET);
23+
24+
// binding to that address
25+
bundle.connect(&bundle);
26+
27+
// change the bundle to non-blocking
28+
bundle.change_block_state(&bundle, false);
29+
30+
// sending message
31+
bundle.send(&bundle, &port_message);
32+
33+
// waiting for reply
34+
PortMessage* received_message = NULL;
35+
do {
36+
received_message = bundle.receive(&bundle);
37+
} while (received_message == NULL);
38+
39+
printf("Received message with connection number %i and content %s\n", received_message->connection_number, received_message->message);
40+
41+
bundle.close(&bundle);
42+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
#include "reactor-uc/reactor-uc.h"
2+
#include "reactor-uc/platform/posix/tcp_ip_bundle.h"
3+
#include <sys/socket.h>
4+
#include <unistd.h>
5+
6+
int main() {
7+
TcpIpBundle bundle;
8+
9+
const char* host = "127.0.0.1";
10+
unsigned short port = 8900; // NOLINT
11+
12+
// creating a server that listens on loopback device on port 8900
13+
TcpIpBundle_ctor(&bundle, host, port, AF_INET);
14+
15+
// binding to that address
16+
bundle.bind(&bundle);
17+
18+
// change the bundle to non-blocking
19+
bundle.change_block_state(&bundle, false);
20+
21+
// accept one connection
22+
bool new_connection;
23+
do {
24+
new_connection = bundle.accept(&bundle);
25+
} while (!new_connection);
26+
27+
// waiting for messages from client
28+
PortMessage* message = NULL;
29+
do {
30+
message = bundle.receive(&bundle);
31+
sleep(1);
32+
} while (message == NULL);
33+
34+
printf("Received message with connection number %i and content %s\n", message->connection_number, message->message);
35+
36+
bundle.send(&bundle, message);
37+
38+
bundle.close(&bundle);
39+
}

external/nanopb

Submodule nanopb added at 736ac3a

include/reactor-uc/encoding.h

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
#ifndef REACTOR_UC_ENCODING_H
2+
#define REACTOR_UC_ENCODING_H
3+
4+
#include "generated/message.pb.h"
5+
#include <nanopb/pb_decode.h>
6+
#include <nanopb/pb_encode.h>
7+
8+
int encode_protobuf(const PortMessage *message, unsigned char *buffer, size_t buffer_size) {
9+
// turing write buffer into pb_ostream buffer
10+
pb_ostream_t stream_out = pb_ostream_from_buffer(buffer, buffer_size);
11+
12+
// serializing protobuf into buffer
13+
if (!pb_encode(&stream_out, PortMessage_fields, message)) {
14+
printf("protobuf encoding error %s\n", stream_out.errmsg);
15+
return -1;
16+
}
17+
18+
return (int)stream_out.bytes_written;
19+
}
20+
21+
int decode_protobuf(PortMessage *message, const unsigned char *buffer, size_t buffer_size) {
22+
pb_istream_t stream_in = pb_istream_from_buffer(buffer, buffer_size);
23+
24+
if (!pb_decode(&stream_in, PortMessage_fields, message)) {
25+
printf("protobuf decoding error %s\n", stream_in.errmsg);
26+
return -1;
27+
}
28+
29+
return (int)stream_in.bytes_left;
30+
}
31+
32+
#endif // REACTOR_UC_ENCODING_H

include/reactor-uc/error.h

+4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#ifndef REACTOR_UC_ERROR_H
22
#define REACTOR_UC_ERROR_H
3+
34
#include <assert.h>
45
#include <stdio.h>
56
#include <stdlib.h>
@@ -14,6 +15,9 @@ typedef enum {
1415
LF_EMPTY,
1516
LF_INVALID_VALUE,
1617
LF_OUT_OF_BOUNDS,
18+
LF_INCOMPLETE,
19+
LF_COULD_NOT_CONNECT,
20+
LF_NETWORK_SETUP_FAILED
1721
} lf_ret_t;
1822

1923
// Runtime validation. Crashes the program if expr is not true
+52
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/* Automatically generated nanopb header */
2+
/* Generated by nanopb-1.0.0-dev */
3+
4+
#ifndef PB_MESSAGE_PB_H_INCLUDED
5+
#define PB_MESSAGE_PB_H_INCLUDED
6+
#include <nanopb/pb.h>
7+
//#include "generator/proto/nanopb.pb.h"
8+
9+
#if PB_PROTO_HEADER_VERSION != 40
10+
#error Regenerate this file with the current version of nanopb generator.
11+
#endif
12+
13+
/* Struct definitions */
14+
typedef struct _PortMessage { // NOLINT
15+
int32_t connection_number;
16+
char message[833]; // NOLINT
17+
} PortMessage;
18+
19+
20+
#ifdef __cplusplus
21+
extern "C" {
22+
#endif
23+
24+
/* Initializer values for message structs */
25+
#define PortMessage_init_default {0, ""}
26+
#define PortMessage_init_zero {0, ""}
27+
28+
/* Field tags (for use in manual encoding/decoding) */
29+
#define PortMessage_connection_number_tag 1
30+
#define PortMessage_message_tag 2
31+
32+
/* Struct field encoding specification for nanopb */
33+
#define PortMessage_FIELDLIST(X, a) \
34+
X(a, STATIC, REQUIRED, INT32, connection_number, 1) \
35+
X(a, STATIC, REQUIRED, STRING, message, 2)
36+
#define PortMessage_CALLBACK NULL
37+
#define PortMessage_DEFAULT NULL
38+
39+
extern const pb_msgdesc_t PortMessage_msg;
40+
41+
/* Defines for backwards compatibility with code written before nanopb-0.4.0 */
42+
#define PortMessage_fields &PortMessage_msg
43+
44+
/* Maximum encoded size of messages (where known) */
45+
#define MESSAGE_PB_H_MAX_SIZE PortMessage_size
46+
#define PortMessage_size 846
47+
48+
#ifdef __cplusplus
49+
} /* extern "C" */
50+
#endif
51+
52+
#endif
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
2+
import "generator/proto/nanopb.proto";
3+
4+
message PortMessage {
5+
required int32 connection_number = 1;
6+
required string message = 2 [(nanopb).max_length = 832];
7+
}
8+
File renamed without changes.

0 commit comments

Comments
 (0)