diff --git a/.gitignore b/.gitignore index 696ea802..b4b9422c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ **/build/ **/src-gen/ **/bin/ +**/*.log benchmarks/include .vscode/ cmake-build-debug diff --git a/CMakeLists.txt b/CMakeLists.txt index 9d3b0461..bfcc7679 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -78,7 +78,7 @@ if (NOT PLATFORM STREQUAL "ZEPHYR") endif() # Add compile definitions for platform and network channel specifics. -target_compile_definitions(reactor-uc PRIVATE "PLATFORM_${PLATFORM}") +target_compile_definitions(reactor-uc PUBLIC "PLATFORM_${PLATFORM}") # Add compile definition for scheduler used target_compile_definitions(reactor-uc PRIVATE "SCHEDULER_${SCHEDULER}") diff --git a/Makefile b/Makefile index cfda6301..72bb4acb 100644 --- a/Makefile +++ b/Makefile @@ -49,4 +49,4 @@ format-check: ci: clean test coverage format-check clean: - rm -rf build + rm -rf build test/lf/src-gen test/lf/bin diff --git a/examples/posix/federated/receiver.c b/examples/posix/federated/receiver.c index f9ab15b7..2acc4bf1 100644 --- a/examples/posix/federated/receiver.c +++ b/examples/posix/federated/receiver.c @@ -50,18 +50,19 @@ LF_REACTOR_CTOR_SIGNATURE_WITH_PARAMETERS(Receiver, InputExternalCtorArgs *in_ex LF_PORT_REGISTER_EFFECT(self->in, self->r, 1); } -LF_DEFINE_FEDERATED_INPUT_CONNECTION(Receiver, in, msg_t, 5, MSEC(100), false); +LF_DEFINE_FEDERATED_INPUT_CONNECTION_STRUCT(Receiver, in, msg_t, 5); +LF_DEFINE_FEDERATED_INPUT_CONNECTION_CTOR(Receiver, in, msg_t, 5, MSEC(100), false); typedef struct { FederatedConnectionBundle super; TcpIpChannel channel; LF_FEDERATED_INPUT_CONNECTION_INSTANCE(Receiver, in); LF_FEDERATED_CONNECTION_BUNDLE_BOOKKEEPING_INSTANCES(1, 0) -} LF_FEDERATED_CONNECTION_BUNDLE_NAME(Receiver, Sender); +} LF_FEDERATED_CONNECTION_BUNDLE_TYPE(Receiver, Sender); LF_FEDERATED_CONNECTION_BUNDLE_CTOR_SIGNATURE(Receiver, Sender) { LF_FEDERATED_CONNECTION_BUNDLE_CTOR_PREAMBLE(); - TcpIpChannel_ctor(&self->channel, parent->env, "127.0.0.1", PORT_NUM, AF_INET, false); + TcpIpChannel_ctor(&self->channel, "127.0.0.1", PORT_NUM, AF_INET, false); LF_FEDERATED_CONNECTION_BUNDLE_CALL_CTOR(); LF_INITIALIZE_FEDERATED_INPUT_CONNECTION(Receiver, in, deserialize_msg_t); } @@ -80,7 +81,7 @@ LF_REACTOR_CTOR_SIGNATURE(MainRecv) { LF_DEFINE_CHILD_INPUT_ARGS(receiver, in, 1, 1); LF_INITIALIZE_CHILD_REACTOR_WITH_PARAMETERS(Receiver, receiver, 1, _receiver_in_args[i]); LF_INITIALIZE_FEDERATED_CONNECTION_BUNDLE(Receiver, Sender); - LF_BUNDLE_REGISTER_DOWNSTREAM(Receiver, Sender, receiver, in); + lf_connect_federated_input(&self->Receiver_Sender_bundle.inputs[0]->super, &self->receiver->in[0].super); } LF_ENTRY_POINT_FEDERATED(MainRecv, SEC(1), true, true, 1, false) diff --git a/examples/posix/federated/sender.c b/examples/posix/federated/sender.c index fa8a39ab..36e49884 100644 --- a/examples/posix/federated/sender.c +++ b/examples/posix/federated/sender.c @@ -59,18 +59,19 @@ LF_REACTOR_CTOR_SIGNATURE_WITH_PARAMETERS(Sender, OutputExternalCtorArgs *out_ex LF_PORT_REGISTER_SOURCE(self->out, self->r, 1); } -LF_DEFINE_FEDERATED_OUTPUT_CONNECTION(Sender, out, msg_t, 1) +LF_DEFINE_FEDERATED_OUTPUT_CONNECTION_STRUCT(Sender, out, msg_t) +LF_DEFINE_FEDERATED_OUTPUT_CONNECTION_CTOR(Sender, out, msg_t) typedef struct { FederatedConnectionBundle super; TcpIpChannel channel; LF_FEDERATED_OUTPUT_CONNECTION_INSTANCE(Sender, out); LF_FEDERATED_CONNECTION_BUNDLE_BOOKKEEPING_INSTANCES(0, 1); -} LF_FEDERATED_CONNECTION_BUNDLE_NAME(Sender, Receiver); +} LF_FEDERATED_CONNECTION_BUNDLE_TYPE(Sender, Receiver); LF_FEDERATED_CONNECTION_BUNDLE_CTOR_SIGNATURE(Sender, Receiver) { LF_FEDERATED_CONNECTION_BUNDLE_CTOR_PREAMBLE(); - TcpIpChannel_ctor(&self->channel, parent->env, "127.0.0.1", PORT_NUM, AF_INET, true); + TcpIpChannel_ctor(&self->channel, "127.0.0.1", PORT_NUM, AF_INET, true); LF_FEDERATED_CONNECTION_BUNDLE_CALL_CTOR(); @@ -94,8 +95,9 @@ LF_REACTOR_CTOR_SIGNATURE(MainSender) { LF_DEFINE_CHILD_OUTPUT_ARGS(sender, out, 1, 1); LF_INITIALIZE_CHILD_REACTOR_WITH_PARAMETERS(Sender, sender, 1, _sender_out_args[i]); LF_INITIALIZE_FEDERATED_CONNECTION_BUNDLE(Sender, Receiver); - LF_BUNDLE_REGISTER_UPSTREAM(Sender, Receiver, sender, out); + lf_connect_federated_output(self->Sender_Receiver_bundle.outputs[0], self->sender->out); } + LF_ENTRY_POINT_FEDERATED(MainSender, SEC(1), true, false, 1, true) int main() { diff --git a/examples/posix/hello/hello.c b/examples/posix/hello/hello.c index 8ec71d4d..122dd145 100644 --- a/examples/posix/hello/hello.c +++ b/examples/posix/hello/hello.c @@ -4,7 +4,7 @@ LF_DEFINE_REACTION_BODY(TimerSource, r) { LF_SCOPE_SELF(TimerSource); LF_SCOPE_ENV(); - printf("TimerSource World @ %lld\n", env->get_elapsed_logical_time(env)); + printf("TimerSource World @ %"PRId64"\n", env->get_elapsed_logical_time(env)); } int main() { diff --git a/examples/riot/coap_federated/receiver/main.c b/examples/riot/coap_federated/receiver/main.c index 9c50219c..8afb351b 100755 --- a/examples/riot/coap_federated/receiver/main.c +++ b/examples/riot/coap_federated/receiver/main.c @@ -53,18 +53,19 @@ LF_REACTOR_CTOR_SIGNATURE_WITH_PARAMETERS(Receiver, InputExternalCtorArgs *in_ex LF_PORT_REGISTER_EFFECT(self->in, self->r, 1); } -LF_DEFINE_FEDERATED_INPUT_CONNECTION(Receiver, in, lf_msg_t, 5, MSEC(100), false) +LF_DEFINE_FEDERATED_INPUT_CONNECTION_STRUCT(Receiver, in, msg_t, 5); +LF_DEFINE_FEDERATED_INPUT_CONNECTION_CTOR(Receiver, in, msg_t, 5, MSEC(100), false); typedef struct { FederatedConnectionBundle super; CoapUdpIpChannel channel; LF_FEDERATED_INPUT_CONNECTION_INSTANCE(Receiver, in); LF_FEDERATED_CONNECTION_BUNDLE_BOOKKEEPING_INSTANCES(1, 0) -} LF_FEDERATED_CONNECTION_BUNDLE_NAME(Receiver, Sender); +} LF_FEDERATED_CONNECTION_BUNDLE_TYPE(Receiver, Sender); LF_FEDERATED_CONNECTION_BUNDLE_CTOR_SIGNATURE(Receiver, Sender) { LF_FEDERATED_CONNECTION_BUNDLE_CTOR_PREAMBLE(); - CoapUdpIpChannel_ctor(&self->channel, parent->env, REMOTE_ADDRESS, REMOTE_PROTOCOL_FAMILY); + CoapUdpIpChannel_ctor(&self->channel, REMOTE_ADDRESS, REMOTE_PROTOCOL_FAMILY); LF_FEDERATED_CONNECTION_BUNDLE_CALL_CTOR(); LF_INITIALIZE_FEDERATED_INPUT_CONNECTION(Receiver, in, deserialize_msg_t); } @@ -83,7 +84,7 @@ LF_REACTOR_CTOR_SIGNATURE(MainRecv) { LF_DEFINE_CHILD_INPUT_ARGS(receiver, in, 1, 1); LF_INITIALIZE_CHILD_REACTOR_WITH_PARAMETERS(Receiver, receiver, 1, _receiver_in_args[i]); LF_INITIALIZE_FEDERATED_CONNECTION_BUNDLE(Receiver, Sender); - LF_BUNDLE_REGISTER_DOWNSTREAM(Receiver, Sender, receiver, in); + lf_connect_federated_input(&self->Receiver_Sender_bundle.inputs[0]->super, &self->receiver->in[0].super); } LF_ENTRY_POINT_FEDERATED(MainRecv, SEC(1), true, true, 1, false) diff --git a/examples/riot/coap_federated/sender/main.c b/examples/riot/coap_federated/sender/main.c index 9a0e47b3..0f0a8ac1 100755 --- a/examples/riot/coap_federated/sender/main.c +++ b/examples/riot/coap_federated/sender/main.c @@ -60,18 +60,19 @@ LF_REACTOR_CTOR_SIGNATURE_WITH_PARAMETERS(Sender, OutputExternalCtorArgs *out_ex LF_PORT_REGISTER_SOURCE(self->out, self->r, 1); } -LF_DEFINE_FEDERATED_OUTPUT_CONNECTION(Sender, out, lf_msg_t, 1) +LF_DEFINE_FEDERATED_OUTPUT_CONNECTION_STRUCT(Sender, out, msg_t) +LF_DEFINE_FEDERATED_OUTPUT_CONNECTION_CTOR(Sender, out, msg_t) typedef struct { FederatedConnectionBundle super; CoapUdpIpChannel channel; LF_FEDERATED_OUTPUT_CONNECTION_INSTANCE(Sender, out); LF_FEDERATED_CONNECTION_BUNDLE_BOOKKEEPING_INSTANCES(0, 1); -} LF_FEDERATED_CONNECTION_BUNDLE_NAME(Sender, Receiver); +} LF_FEDERATED_CONNECTION_BUNDLE_TYPE(Sender, Receiver); LF_FEDERATED_CONNECTION_BUNDLE_CTOR_SIGNATURE(Sender, Receiver) { LF_FEDERATED_CONNECTION_BUNDLE_CTOR_PREAMBLE(); - CoapUdpIpChannel_ctor(&self->channel, parent->env, REMOTE_ADDRESS, REMOTE_PROTOCOL_FAMILY); + CoapUdpIpChannel_ctor(&self->channel, REMOTE_ADDRESS, REMOTE_PROTOCOL_FAMILY); LF_FEDERATED_CONNECTION_BUNDLE_CALL_CTOR(); LF_INITIALIZE_FEDERATED_OUTPUT_CONNECTION(Sender, out, serialize_msg_t); } @@ -93,7 +94,7 @@ LF_REACTOR_CTOR_SIGNATURE(MainSender) { LF_DEFINE_CHILD_OUTPUT_ARGS(sender, out, 1, 1); LF_INITIALIZE_CHILD_REACTOR_WITH_PARAMETERS(Sender, sender, 1, _sender_out_args[i]); LF_INITIALIZE_FEDERATED_CONNECTION_BUNDLE(Sender, Receiver); - LF_BUNDLE_REGISTER_UPSTREAM(Sender, Receiver, sender, out); + lf_connect_federated_output((Connection *)self->Sender_Receiver_bundle.outputs[0], (Port *)self->sender->out); } LF_ENTRY_POINT_FEDERATED(MainSender, SEC(1), true, false, 1, true) diff --git a/examples/riot/hello/main.c b/examples/riot/hello/main.c index f6900958..c752cafe 100755 --- a/examples/riot/hello/main.c +++ b/examples/riot/hello/main.c @@ -5,7 +5,7 @@ LF_DEFINE_REACTION_BODY(TimerSource, r) { LF_SCOPE_SELF(TimerSource); LF_SCOPE_ENV(); - printf("TimerSource World @ %lld\n", env->get_elapsed_logical_time(env)); + printf("Hello World @ %lld\n", env->get_elapsed_logical_time(env)); } int main() { diff --git a/examples/zephyr/basic_federated/common/receiver.h b/examples/zephyr/basic_federated/common/receiver.h index db922e64..aa1fa838 100644 --- a/examples/zephyr/basic_federated/common/receiver.h +++ b/examples/zephyr/basic_federated/common/receiver.h @@ -64,18 +64,19 @@ LF_REACTOR_CTOR_SIGNATURE_WITH_PARAMETERS(Receiver, InputExternalCtorArgs *in_ex LF_PORT_REGISTER_EFFECT(self->in, self->r, 1); } -LF_DEFINE_FEDERATED_INPUT_CONNECTION(Receiver, in, msg_t, 5, MSEC(100), false); +LF_DEFINE_FEDERATED_INPUT_CONNECTION_STRUCT(Receiver, in, msg_t, 5); +LF_DEFINE_FEDERATED_INPUT_CONNECTION_CTOR(Receiver, in, msg_t, 5, MSEC(100), false); typedef struct { FederatedConnectionBundle super; TcpIpChannel channel; LF_FEDERATED_INPUT_CONNECTION_INSTANCE(Receiver, in); LF_FEDERATED_CONNECTION_BUNDLE_BOOKKEEPING_INSTANCES(1, 0) -} LF_FEDERATED_CONNECTION_BUNDLE_NAME(Receiver, Sender); +} LF_FEDERATED_CONNECTION_BUNDLE_TYPE(Receiver, Sender); LF_FEDERATED_CONNECTION_BUNDLE_CTOR_SIGNATURE(Receiver, Sender) { LF_FEDERATED_CONNECTION_BUNDLE_CTOR_PREAMBLE(); - TcpIpChannel_ctor(&self->channel, parent->env, IP_ADDR, PORT_NUM, AF_INET, false); + TcpIpChannel_ctor(&self->channel, IP_ADDR, PORT_NUM, AF_INET, false); LF_FEDERATED_CONNECTION_BUNDLE_CALL_CTOR(); LF_INITIALIZE_FEDERATED_INPUT_CONNECTION(Receiver, in, deserialize_payload_default); } @@ -94,6 +95,6 @@ LF_REACTOR_CTOR_SIGNATURE(MainRecv) { LF_DEFINE_CHILD_INPUT_ARGS(receiver, in, 1, 1); LF_INITIALIZE_CHILD_REACTOR_WITH_PARAMETERS(Receiver, receiver, 1, _receiver_in_args[i]); LF_INITIALIZE_FEDERATED_CONNECTION_BUNDLE(Receiver, Sender); - LF_BUNDLE_REGISTER_DOWNSTREAM(Receiver, Sender, receiver, in); + lf_connect_federated_input(&self->Receiver_Sender_bundle.inputs[0]->super, &self->receiver->in[0].super); } LF_ENTRY_POINT_FEDERATED(MainRecv, FOREVER, true, true, 1, false) diff --git a/examples/zephyr/basic_federated/federated_sender/src/sender.c b/examples/zephyr/basic_federated/federated_sender/src/sender.c index 8e4f0a0a..0fc87f48 100644 --- a/examples/zephyr/basic_federated/federated_sender/src/sender.c +++ b/examples/zephyr/basic_federated/federated_sender/src/sender.c @@ -97,25 +97,26 @@ LF_REACTOR_CTOR_SIGNATURE_WITH_PARAMETERS(Sender, OutputExternalCtorArgs *out_ex LF_PORT_REGISTER_SOURCE(self->out, self->r, 1); } -LF_DEFINE_FEDERATED_OUTPUT_CONNECTION(Sender, out, msg_t, 1) +LF_DEFINE_FEDERATED_OUTPUT_CONNECTION_STRUCT(Sender, out, msg_t) +LF_DEFINE_FEDERATED_OUTPUT_CONNECTION_CTOR(Sender, out, msg_t) typedef struct { FederatedConnectionBundle super; TcpIpChannel channel; LF_FEDERATED_OUTPUT_CONNECTION_INSTANCE(Sender, out); LF_FEDERATED_CONNECTION_BUNDLE_BOOKKEEPING_INSTANCES(0, 1); -} LF_FEDERATED_CONNECTION_BUNDLE_NAME(Sender, Receiver1); +} LF_FEDERATED_CONNECTION_BUNDLE_TYPE(Sender, Receiver1); typedef struct { FederatedConnectionBundle super; TcpIpChannel channel; LF_FEDERATED_OUTPUT_CONNECTION_INSTANCE(Sender, out); LF_FEDERATED_CONNECTION_BUNDLE_BOOKKEEPING_INSTANCES(0, 1); -} LF_FEDERATED_CONNECTION_BUNDLE_NAME(Sender, Receiver2); +} LF_FEDERATED_CONNECTION_BUNDLE_TYPE(Sender, Receiver2); LF_FEDERATED_CONNECTION_BUNDLE_CTOR_SIGNATURE(Sender, Receiver1) { LF_FEDERATED_CONNECTION_BUNDLE_CTOR_PREAMBLE(); - TcpIpChannel_ctor(&self->channel, parent->env, "192.168.1.100", PORT_CONN_1, AF_INET, true); + TcpIpChannel_ctor(&self->channel, "192.168.1.100", PORT_CONN_1, AF_INET, true); LF_FEDERATED_CONNECTION_BUNDLE_CALL_CTOR(); @@ -124,7 +125,7 @@ LF_FEDERATED_CONNECTION_BUNDLE_CTOR_SIGNATURE(Sender, Receiver1) { LF_FEDERATED_CONNECTION_BUNDLE_CTOR_SIGNATURE(Sender, Receiver2) { LF_FEDERATED_CONNECTION_BUNDLE_CTOR_PREAMBLE(); - TcpIpChannel_ctor(&self->channel, parent->env, "192.168.1.100", PORT_CONN_2, AF_INET, true); + TcpIpChannel_ctor(&self->channel, "192.168.1.100", PORT_CONN_2, AF_INET, true); LF_FEDERATED_CONNECTION_BUNDLE_CALL_CTOR(); @@ -152,8 +153,8 @@ LF_REACTOR_CTOR_SIGNATURE(MainSender) { LF_INITIALIZE_FEDERATED_CONNECTION_BUNDLE(Sender, Receiver1); LF_INITIALIZE_FEDERATED_CONNECTION_BUNDLE(Sender, Receiver2); - LF_BUNDLE_REGISTER_UPSTREAM(Sender, Receiver1, sender, out); - LF_BUNDLE_REGISTER_UPSTREAM(Sender, Receiver2, sender, out); + lf_connect_federated_output(self->Sender_Receiver1_bundle.outputs[0], self->sender->out); + lf_connect_federated_output(self->Sender_Receiver2_bundle.outputs[0], self->sender->out); } LF_ENTRY_POINT_FEDERATED(MainSender, FOREVER, true, true, 2, true) diff --git a/include/reactor-uc/environment.h b/include/reactor-uc/environment.h index 32d698eb..ee0e542b 100644 --- a/include/reactor-uc/environment.h +++ b/include/reactor-uc/environment.h @@ -9,6 +9,7 @@ #include "reactor-uc/scheduler.h" typedef struct Environment Environment; +extern Environment *_lf_environment; // NOLINT struct Environment { Reactor *main; // The top-level reactor of the program. diff --git a/include/reactor-uc/logging.h b/include/reactor-uc/logging.h index 9ea8d7ba..1a077433 100644 --- a/include/reactor-uc/logging.h +++ b/include/reactor-uc/logging.h @@ -5,30 +5,41 @@ // The different verbosity levels supported #define LF_LOG_LEVEL_OFF 0 -#define LF_LOG_LEVEL_INFO 1 -#define LF_LOG_LEVEL_ERR 2 -#define LF_LOG_LEVEL_WARN 3 -#define LF_LOG_LEVEL_DEBUG 4 +#define LF_LOG_LEVEL_ERROR 1 +#define LF_LOG_LEVEL_WARN 2 +#define LF_LOG_LEVEL_INFO 3 +#define LF_LOG_LEVEL_LOG 4 +#define LF_LOG_LEVEL_DEBUG 5 // Add color codes to the output +#ifndef LF_COLORIZE_LOGS #define LF_COLORIZE_LOGS 1 +#endif + +// Add timestamp to the logs +#if !defined(LF_TIMESTAMP_LOGS) && !defined(PLATFORM_FLEXPRET) +#define LF_TIMESTAMP_LOGS 1 +#else +#undef LF_TIMESTAMP_LOGS +#define LF_TIMESTAMP_LOGS 0 +#endif // The default log level for any unspecified module #ifndef LF_LOG_LEVEL_ALL #ifndef NDEBUG #define LF_LOG_LEVEL_ALL LF_LOG_LEVEL_DEBUG #else -#define LF_LOG_LEVEL_ALL LF_LOG_LEVEL_ERR +#define LF_LOG_LEVEL_ALL LF_LOG_LEVEL_ERROR #endif #endif // Define the log level for each module. If not defined, use LF_LOG_LEVEL_ALL -// or set to LF_LOG_LEVEL_ERR if LF_LOG_LEVEL_ALL is not defined. +// or set to LF_LOG_LEVEL_ERROR if LF_LOG_LEVEL_ALL is not defined. #ifndef LF_LOG_LEVEL_ENV #ifdef LF_LOG_LEVEL_ALL #define LF_LOG_LEVEL_ENV LF_LOG_LEVEL_ALL #else -#define LF_LOG_LEVEL_ENV LF_LOG_LEVEL_ERR +#define LF_LOG_LEVEL_ENV LF_LOG_LEVEL_ERROR #endif #endif @@ -36,7 +47,7 @@ #ifdef LF_LOG_LEVEL_ALL #define LF_LOG_LEVEL_SCHED LF_LOG_LEVEL_ALL #else -#define LF_LOG_LEVEL_SCHED LF_LOG_LEVEL_ERR +#define LF_LOG_LEVEL_SCHED LF_LOG_LEVEL_ERROR #endif #endif @@ -44,7 +55,7 @@ #ifdef LF_LOG_LEVEL_ALL #define LF_LOG_LEVEL_QUEUE LF_LOG_LEVEL_ALL #else -#define LF_LOG_LEVEL_QUEUE LF_LOG_LEVEL_ERR +#define LF_LOG_LEVEL_QUEUE LF_LOG_LEVEL_ERROR #endif #endif @@ -52,7 +63,7 @@ #ifdef LF_LOG_LEVEL_ALL #define LF_LOG_LEVEL_FED LF_LOG_LEVEL_ALL #else -#define LF_LOG_LEVEL_FED LF_LOG_LEVEL_ERR +#define LF_LOG_LEVEL_FED LF_LOG_LEVEL_ERROR #endif #endif @@ -60,7 +71,7 @@ #ifdef LF_LOG_LEVEL_ALL #define LF_LOG_LEVEL_TRIG LF_LOG_LEVEL_ALL #else -#define LF_LOG_LEVEL_TRIG LF_LOG_LEVEL_ERR +#define LF_LOG_LEVEL_TRIG LF_LOG_LEVEL_ERROR #endif #endif @@ -68,7 +79,7 @@ #ifdef LF_LOG_LEVEL_ALL #define LF_LOG_LEVEL_PLATFORM LF_LOG_LEVEL_ALL #else -#define LF_LOG_LEVEL_PLATFORM LF_LOG_LEVEL_ERR +#define LF_LOG_LEVEL_PLATFORM LF_LOG_LEVEL_ERROR #endif #endif @@ -76,7 +87,7 @@ #ifdef LF_LOG_LEVEL_ALL #define LF_LOG_LEVEL_CONN LF_LOG_LEVEL_ALL #else -#define LF_LOG_LEVEL_CONN LF_LOG_LEVEL_ERR +#define LF_LOG_LEVEL_CONN LF_LOG_LEVEL_ERROR #endif #endif @@ -84,7 +95,7 @@ #ifdef LF_LOG_LEVEL_ALL #define LF_LOG_LEVEL_NET LF_LOG_LEVEL_ALL #else -#define LF_LOG_LEVEL_NET LF_LOG_LEVEL_ERR +#define LF_LOG_LEVEL_NET LF_LOG_LEVEL_ERROR #endif #endif @@ -106,7 +117,7 @@ } \ } while (0) -#define LF_ERR(module, fmt, ...) LF_LOG(LF_LOG_LEVEL_ERR, module, fmt, ##__VA_ARGS__) +#define LF_ERR(module, fmt, ...) LF_LOG(LF_LOG_LEVEL_ERROR, module, fmt, ##__VA_ARGS__) #define LF_WARN(module, fmt, ...) LF_LOG(LF_LOG_LEVEL_WARN, module, fmt, ##__VA_ARGS__) #define LF_INFO(module, fmt, ...) LF_LOG(LF_LOG_LEVEL_INFO, module, fmt, ##__VA_ARGS__) #define LF_DEBUG(module, fmt, ...) LF_LOG(LF_LOG_LEVEL_DEBUG, module, fmt, ##__VA_ARGS__) diff --git a/include/reactor-uc/macros.h b/include/reactor-uc/macros.h index 08b26e02..5eba466d 100644 --- a/include/reactor-uc/macros.h +++ b/include/reactor-uc/macros.h @@ -137,52 +137,7 @@ (__reaction)->effects[(__reaction)->effects_registered++] = (Trigger *)&(TheEffect); \ } while (0) -// Convenience macro to register a downstream port on a connection. -#define LF_CONN_REGISTER_DOWNSTREAM_INTERNAL(conn, down) \ - do { \ - ((Connection *)&(conn))->register_downstream((Connection *)&(conn), (Port *)&(down)); \ - } while (0) - -// Convenience macro to register an upstream port on a connection -#define LF_CONN_REGISTER_UPSTREAM_INTERNAL(conn, up) \ - do { \ - Port *_up = (Port *)&(up); \ - ((Connection *)&(conn))->upstream = _up; \ - assert(_up->conns_out_registered < _up->conns_out_size); \ - _up->conns_out[_up->conns_out_registered++] = (Connection *)&(conn); \ - } while (0) - -#define LF_BUNDLE_REGISTER_DOWNSTREAM(ReactorName, OtherName, InstanceName, Port) \ - LF_CONN_REGISTER_DOWNSTREAM_INTERNAL(self->ReactorName##_##OtherName##_bundle.conn_##Port, self->InstanceName->Port); - -#define LF_BUNDLE_REGISTER_UPSTREAM(ReactorName, OtherName, InstanceName, Port) \ - LF_CONN_REGISTER_UPSTREAM_INTERNAL(self->ReactorName##_##OtherName##_bundle.conn_##Port, self->InstanceName->Port); - -#define LF_CONN_REGISTER_UPSTREAM(Conn, ReactorUp, PortUp, BankWidth, PortWidth) \ - for (int i = 0; i < (BankWidth); i++) { \ - for (int j = 0; j < (PortWidth); j++) { \ - LF_CONN_REGISTER_UPSTREAM_INTERNAL(self->Conn[i][j], ReactorUp[i].PortUp[j]); \ - } \ - } - -#define LF_CONN_REGISTER_DOWNSTREAM(Conn, BankWidthUp, PortWidthUp, ReactorDown, PortDown, BankWidthDown, \ - PortWidthDown) \ - for (int i = 0; i < (BankWidthDown); i++) { \ - for (int j = 0; j < (PortWidthDown); j++) { \ - LF_CONN_REGISTER_DOWNSTREAM_INTERNAL(self->Conn[_##Conn##_i][_##Conn##_j], ReactorDown[i].PortDown[j]); \ - _##Conn##_j++; \ - if (_##Conn##_j == (PortWidthUp)) { \ - _##Conn##_j = 0; \ - _##Conn##_i++; \ - } \ - if (_##Conn##_i == (BankWidthUp)) { \ - _##Conn##_i = 0; \ - } \ - } \ - } - // Macros for creating the structs and ctors - #define LF_REACTOR_CTOR_PREAMBLE() \ size_t _reactions_idx = 0; \ (void)_reactions_idx; \ @@ -514,8 +469,6 @@ } #define LF_INITIALIZE_LOGICAL_CONNECTION(ParentName, ConnName, BankWidth, PortWidth) \ - int _##ConnName##_i = 0; \ - int _##ConnName##_j = 0; \ for (int i = 0; i < (BankWidth); i++) { \ for (int j = 0; j < (PortWidth); j++) { \ ParentName##_##ConnName##_ctor(&self->ConnName[i][j], &self->super); \ @@ -542,8 +495,6 @@ // FIXME: Duplicated #define LF_INITIALIZE_DELAYED_CONNECTION(ParentName, ConnName, BankWidth, PortWidth) \ - int _##ConnName##_i = 0; \ - int _##ConnName##_j = 0; \ for (int i = 0; i < (BankWidth); i++) { \ for (int j = 0; j < (PortWidth); j++) { \ ParentName##_##ConnName##_ctor(&self->ConnName[i][j], &self->super); \ @@ -551,26 +502,29 @@ } typedef struct FederatedOutputConnection FederatedOutputConnection; -#define LF_DEFINE_FEDERATED_OUTPUT_CONNECTION(ReactorName, OutputName, BufferType, BufferSize) \ +#define LF_DEFINE_FEDERATED_OUTPUT_CONNECTION_STRUCT(ReactorName, OutputName, BufferType) \ typedef struct { \ FederatedOutputConnection super; \ - BufferType payload_buf[(BufferSize)]; \ - bool payload_used_buf[(BufferSize)]; \ - } ReactorName##_##OutputName##_conn; \ - \ + BufferType payload_buf[1]; \ + bool payload_used_buf[1]; \ + } ReactorName##_##OutputName##_conn; + +#define LF_DEFINE_FEDERATED_OUTPUT_CONNECTION_CTOR(ReactorName, OutputName, BufferType) \ void ReactorName##_##OutputName##_conn_ctor(ReactorName##_##OutputName##_conn *self, Reactor *parent, \ FederatedConnectionBundle *bundle) { \ FederatedOutputConnection_ctor(&self->super, parent, bundle, 0, (void *)&self->payload_buf, \ - (bool *)&self->payload_used_buf, sizeof(BufferType), BufferSize); \ + (bool *)&self->payload_used_buf, sizeof(BufferType), 1); \ } -#define LF_FEDERATED_OUTPUT_CONNECTION_INSTANCE(ReactorName, OutputName) \ - ReactorName##_##OutputName##_conn conn_##OutputName +#define LF_FEDERATED_OUTPUT_CONNECTION_INSTANCE(ReactorName, OutputName) ReactorName##_##OutputName##_conn OutputName -#define LF_FEDERATED_CONNECTION_BUNDLE_INSTANCE(ReactorName, OtherName) \ - ReactorName##_##OtherName##_Bundle ReactorName##_##OtherName##_bundle +#define LF_FEDERATED_CONNECTION_BUNDLE_TYPE(ReactorName, OtherName) ReactorName##_##OtherName##_Bundle + +#define LF_FEDERATED_CONNECTION_BUNDLE_NAME(ReactorName, OtherName) ReactorName##_##OtherName##_bundle -#define LF_FEDERATED_CONNECTION_BUNDLE_NAME(ReactorName, OtherName) ReactorName##_##OtherName##_Bundle +#define LF_FEDERATED_CONNECTION_BUNDLE_INSTANCE(ReactorName, OtherName) \ + LF_FEDERATED_CONNECTION_BUNDLE_TYPE(ReactorName, OtherName) \ + LF_FEDERATED_CONNECTION_BUNDLE_NAME(ReactorName, OtherName) #define LF_FEDERATED_CONNECTION_BUNDLE_CTOR_SIGNATURE(ReactorName, OtherName) \ void ReactorName##_##OtherName##_Bundle_ctor(ReactorName##_##OtherName##_Bundle *self, Reactor *parent) @@ -592,31 +546,32 @@ typedef struct FederatedOutputConnection FederatedOutputConnection; self->_bundles[_bundle_idx++] = &self->ReactorName##_##OtherName##_bundle.super; #define LF_INITIALIZE_FEDERATED_OUTPUT_CONNECTION(ReactorName, OutputName, SerializeFunc) \ - ReactorName##_##OutputName##_conn_ctor(&self->conn_##OutputName, self->super.parent, &self->super); \ - self->outputs[_inputs_idx] = &self->conn_##OutputName.super; \ - self->serialize_hooks[_inputs_idx] = SerializeFunc; \ + ReactorName##_##OutputName##_conn_ctor(&self->OutputName, self->super.parent, &self->super); \ + self->outputs[_outputs_idx] = &self->OutputName.super; \ + self->serialize_hooks[_outputs_idx] = SerializeFunc; \ _outputs_idx++; typedef struct FederatedInputConnection FederatedInputConnection; -#define LF_DEFINE_FEDERATED_INPUT_CONNECTION(ReactorName, InputName, BufferType, BufferSize, Delay, IsPhysical) \ +#define LF_DEFINE_FEDERATED_INPUT_CONNECTION_STRUCT(ReactorName, InputName, BufferType, BufferSize) \ typedef struct { \ FederatedInputConnection super; \ BufferType payload_buf[(BufferSize)]; \ bool payload_used_buf[(BufferSize)]; \ Port *downstreams[1]; \ - } ReactorName##_##InputName##_conn; \ - \ + } ReactorName##_##InputName##_conn; + +#define LF_DEFINE_FEDERATED_INPUT_CONNECTION_CTOR(ReactorName, InputName, BufferType, BufferSize, Delay, IsPhysical) \ void ReactorName##_##InputName##_conn_ctor(ReactorName##_##InputName##_conn *self, Reactor *parent) { \ FederatedInputConnection_ctor(&self->super, parent, Delay, IsPhysical, (Port **)&self->downstreams, 1, \ (void *)&self->payload_buf, (bool *)&self->payload_used_buf, sizeof(BufferType), \ BufferSize); \ } -#define LF_FEDERATED_INPUT_CONNECTION_INSTANCE(ReactorName, InputName) ReactorName##_##InputName##_conn conn_##InputName +#define LF_FEDERATED_INPUT_CONNECTION_INSTANCE(ReactorName, InputName) ReactorName##_##InputName##_conn InputName #define LF_INITIALIZE_FEDERATED_INPUT_CONNECTION(ReactorName, InputName, DeserializeFunc) \ - ReactorName##_##InputName##_conn_ctor(&self->conn_##InputName, self->super.parent); \ - self->inputs[_inputs_idx] = &self->conn_##InputName.super; \ + ReactorName##_##InputName##_conn_ctor(&self->InputName, self->super.parent); \ + self->inputs[_inputs_idx] = &self->InputName.super; \ self->deserialize_hooks[_inputs_idx] = DeserializeFunc; \ _inputs_idx++; @@ -649,6 +604,7 @@ typedef struct FederatedInputConnection FederatedInputConnection; #define LF_ENTRY_POINT(MainReactorName, Timeout, KeepAlive, Fast) \ MainReactorName main_reactor; \ Environment env; \ + Environment *_lf_environment = &env; \ void lf_exit(void) { Environment_free(&env); } \ void lf_start() { \ Environment_ctor(&env, (Reactor *)&main_reactor); \ @@ -664,6 +620,7 @@ typedef struct FederatedInputConnection FederatedInputConnection; #define LF_ENTRY_POINT_FEDERATED(FederateName, Timeout, KeepAlive, HasInputs, NumBundles, IsLeader) \ FederateName main_reactor; \ Environment env; \ + Environment *_lf_environment = &env; \ void lf_exit(void) { Environment_free(&env); } \ void lf_start() { \ Environment_ctor(&env, (Reactor *)&main_reactor); \ diff --git a/include/reactor-uc/network_channel.h b/include/reactor-uc/network_channel.h index 3f0ad755..51e82321 100644 --- a/include/reactor-uc/network_channel.h +++ b/include/reactor-uc/network_channel.h @@ -55,6 +55,13 @@ struct NetworkChannel { */ bool (*is_connected)(NetworkChannel *self); + /** + * @brief Has the network channel ever been connected to its peer? + * This is needed because we currently require an initial connection + * to be established to all peers before a federate can start. + */ + bool (*was_ever_connected)(NetworkChannel *self); + /** * @brief Opens the connection to the corresponding NetworkChannel on another federate (non-blocking). * The channel is not connected unless @p is_connected returns true. @@ -88,4 +95,35 @@ struct NetworkChannel { void (*free)(NetworkChannel *self); }; +#if defined(PLATFORM_POSIX) +#ifdef NETWORK_CHANNEL_TCP_POSIX +#include "platform/posix/tcp_ip_channel.h" +#endif + +#elif defined(PLATFORM_ZEPHYR) +#ifdef NETWORK_CHANNEL_TCP_POSIX +#include "platform/posix/tcp_ip_channel.h" +#endif + +#elif defined(PLATFORM_RIOT) +#ifdef NETWORK_CHANNEL_TCP_POSIX +#include "platform/posix/tcp_ip_channel.h" +#endif +#ifdef NETWORK_CHANNEL_COAP_RIOT +#include "platform/riot/coap_udp_ip_channel.h" +#endif + +#elif defined(PLATFORM_PICO) +#ifdef NETWORK_CHANNEL_TCP_POSIX +#error "NETWORK_POSIX_TCP not supported on PICO" +#endif + +#elif defined(PLATFORM_FLEXPRET) +#ifdef NETWORK_CHANNEL_TCP_POSIX +#error "NETWORK_POSIX_TCP not supported on FlexPRET" +#endif + +#else +#error "Platform not supported" +#endif #endif // REACTOR_UC_NETWORK_CHANNEL_H diff --git a/include/reactor-uc/platform/posix/tcp_ip_channel.h b/include/reactor-uc/platform/posix/tcp_ip_channel.h index 4633ed50..20695fef 100644 --- a/include/reactor-uc/platform/posix/tcp_ip_channel.h +++ b/include/reactor-uc/platform/posix/tcp_ip_channel.h @@ -3,6 +3,7 @@ #include #include #include +#include #include "proto/message.pb.h" #include "reactor-uc/error.h" @@ -24,9 +25,9 @@ struct TcpIpChannel { int fd; int client; - int send_failed_event_fds[2]; // These file descriptors are used to signal the recv select to stop blocking using - // a socketpair + int send_failed_event_fds[2]; // These file descriptors are used to signal the recv select to stop blocking NetworkChannelState state; + pthread_mutex_t mutex; const char *host; unsigned short port; @@ -40,6 +41,8 @@ struct TcpIpChannel { fd_set set; bool is_server; bool terminate; + bool has_warned_about_connection_failure; + bool was_ever_connected; // required for callbacks pthread_t worker_thread; @@ -50,7 +53,6 @@ struct TcpIpChannel { void (*receive_callback)(FederatedConnectionBundle *conn, const FederateMessage *message); }; -void TcpIpChannel_ctor(TcpIpChannel *self, Environment *env, const char *host, unsigned short port, int protocol_family, - bool is_server); +void TcpIpChannel_ctor(TcpIpChannel *self, const char *host, unsigned short port, int protocol_family, bool is_server); #endif diff --git a/include/reactor-uc/platform/riot/coap_udp_ip_channel.h b/include/reactor-uc/platform/riot/coap_udp_ip_channel.h index 0c5948ff..c2ca8016 100644 --- a/include/reactor-uc/platform/riot/coap_udp_ip_channel.h +++ b/include/reactor-uc/platform/riot/coap_udp_ip_channel.h @@ -23,6 +23,7 @@ struct CoapUdpIpChannel { sock_udp_ep_t remote; bool send_ack_received; + bool was_ever_connected; FederateMessage output; uint8_t write_buffer[COAP_UDP_IP_CHANNEL_BUFFERSIZE]; @@ -30,7 +31,6 @@ struct CoapUdpIpChannel { void (*receive_callback)(FederatedConnectionBundle *conn, const FederateMessage *message); }; -void CoapUdpIpChannel_ctor(CoapUdpIpChannel *self, Environment *env, const char *remote_address, - int remote_protocol_family); +void CoapUdpIpChannel_ctor(CoapUdpIpChannel *self, const char *remote_address, int remote_protocol_family); #endif diff --git a/include/reactor-uc/reactor-uc.h b/include/reactor-uc/reactor-uc.h index f7be4828..041e8cd1 100644 --- a/include/reactor-uc/reactor-uc.h +++ b/include/reactor-uc/reactor-uc.h @@ -13,6 +13,7 @@ #include "reactor-uc/builtin_triggers.h" #include "reactor-uc/connection.h" #include "reactor-uc/environment.h" +#include "reactor-uc/network_channel.h" #include "reactor-uc/error.h" #include "reactor-uc/federated.h" #include "reactor-uc/logging.h" diff --git a/include/reactor-uc/tag.h b/include/reactor-uc/tag.h index 443ba5e1..da5f2065 100644 --- a/include/reactor-uc/tag.h +++ b/include/reactor-uc/tag.h @@ -21,6 +21,7 @@ #define SECS(t) ((interval_t)(t * 1000000000LL)) #define SECOND(t) ((interval_t)(t * 1000000000LL)) #define SECONDS(t) ((interval_t)(t * 1000000000LL)) +#define MIN(t) ((interval_t)(t * 60000000000LL)) #define MINUTE(t) ((interval_t)(t * 60000000000LL)) #define MINUTES(t) ((interval_t)(t * 60000000000LL)) #define HOUR(t) ((interval_t)(t * 3600000000000LL)) diff --git a/include/reactor-uc/util.h b/include/reactor-uc/util.h index 5fb0c7e0..1dbb0f30 100644 --- a/include/reactor-uc/util.h +++ b/include/reactor-uc/util.h @@ -6,4 +6,8 @@ void lf_connect(Connection *connection, Port *upstream, Port *downstream); +void lf_connect_federated_output(Connection *connection, Port *output); + +void lf_connect_federated_input(Connection *connection, Port *input); + #endif \ No newline at end of file diff --git a/lfc/core/src/main/java/org/lflang/AttributeUtils.java b/lfc/core/src/main/java/org/lflang/AttributeUtils.java index 79447cb4..741058ec 100644 --- a/lfc/core/src/main/java/org/lflang/AttributeUtils.java +++ b/lfc/core/src/main/java/org/lflang/AttributeUtils.java @@ -75,6 +75,8 @@ public static List getAttributes(EObject node) { return ((Instantiation) node).getAttributes(); } else if (node instanceof Watchdog) { return ((Watchdog) node).getAttributes(); + } else if (node instanceof Connection) { + return ((Connection) node).getAttributes(); } throw new IllegalArgumentException("Not annotatable: " + node); } @@ -110,6 +112,14 @@ public static List findAttributesByName(EObject node, String name) { .toList(); } + public static List findAttributesByNameStartingWith(EObject node, String name) { + List attrs = getAttributes(node); + return attrs.stream() + .filter( + it -> + it.getAttrName().contains(name)) // case-insensitive search (more user-friendly) + .toList(); + } /** * Return the first argument specified for the attribute. * @@ -276,6 +286,14 @@ public static Attribute getEnclaveAttribute(Instantiation node) { return findAttributeByName(node, "enclave"); } + public static List getInterfaceAttributes(Instantiation node) { + return findAttributesByNameStartingWith(node, "interface"); + } + + public static Attribute getLinkAttribute(Connection node) { + return findAttributeByName(node, "link"); + } + /** Return true if the specified instance has an {@code @enclave} attribute. */ public static boolean isEnclave(Instantiation node) { return getEnclaveAttribute(node) != null; diff --git a/lfc/core/src/main/java/org/lflang/LinguaFranca.xtext b/lfc/core/src/main/java/org/lflang/LinguaFranca.xtext index ba45b3bd..8565b3fa 100644 --- a/lfc/core/src/main/java/org/lflang/LinguaFranca.xtext +++ b/lfc/core/src/main/java/org/lflang/LinguaFranca.xtext @@ -237,6 +237,7 @@ Instantiation: ')' (('at' host=Host ';') | ';'?); Connection: + (attributes+=Attribute)* ((leftPorts += VarRef (',' leftPorts += VarRef)*) | ( '(' leftPorts += VarRef (',' leftPorts += VarRef)* ')' iterated ?= '+'?)) ('->' | physical?='~>') diff --git a/lfc/core/src/main/java/org/lflang/generator/LFGenerator.java b/lfc/core/src/main/java/org/lflang/generator/LFGenerator.java index 2cf6868c..f3b24150 100644 --- a/lfc/core/src/main/java/org/lflang/generator/LFGenerator.java +++ b/lfc/core/src/main/java/org/lflang/generator/LFGenerator.java @@ -2,22 +2,22 @@ import com.google.inject.Inject; import com.google.inject.Injector; -import java.io.IOException; + import java.nio.file.Path; -import java.util.Arrays; + import org.eclipse.emf.ecore.resource.Resource; import org.eclipse.xtext.generator.AbstractGenerator; import org.eclipse.xtext.generator.IFileSystemAccess2; import org.eclipse.xtext.generator.IGeneratorContext; -import org.eclipse.xtext.util.RuntimeIOException; import org.lflang.FileConfig; import org.lflang.MessageReporter; import org.lflang.ast.ASTUtils; import org.lflang.generator.uc.UcFileConfig; -import org.lflang.generator.uc.UcGenerator; import org.lflang.scoping.LFGlobalScopeProvider; import org.lflang.target.Target; +import static org.lflang.generator.uc.UcGeneratorKt.createUcGenerator; + /** Generates code from your model files on save. */ public class LFGenerator extends AbstractGenerator { @@ -64,7 +64,7 @@ private GeneratorBase createGenerator(LFGeneratorContext context) { // case CPP -> new CppGenerator(context, scopeProvider); // case TS -> new TSGenerator(context); // case Rust -> new RustGenerator(context, scopeProvider); - case UC -> new UcGenerator(context, scopeProvider); + case UC -> createUcGenerator(context, scopeProvider); }; } diff --git a/lfc/core/src/main/java/org/lflang/target/Target.java b/lfc/core/src/main/java/org/lflang/target/Target.java index 7f504cfd..c1872607 100644 --- a/lfc/core/src/main/java/org/lflang/target/Target.java +++ b/lfc/core/src/main/java/org/lflang/target/Target.java @@ -573,6 +573,7 @@ public void initialize(TargetConfig config) { TimeOutProperty.INSTANCE, FastProperty.INSTANCE, KeepaliveProperty.INSTANCE, + LoggingProperty.INSTANCE, CmakeIncludeProperty.INSTANCE, FilesProperty.INSTANCE); diff --git a/lfc/core/src/main/java/org/lflang/validation/AttributeSpec.java b/lfc/core/src/main/java/org/lflang/validation/AttributeSpec.java index a685e98c..2bdcaf2e 100644 --- a/lfc/core/src/main/java/org/lflang/validation/AttributeSpec.java +++ b/lfc/core/src/main/java/org/lflang/validation/AttributeSpec.java @@ -1,4 +1,4 @@ -/************* + /************* * Copyright (c) 2019-2022, The University of California at Berkeley. * * Redistribution and use in source and binary forms, with or without modification, @@ -243,5 +243,34 @@ enum AttrParamType { ATTRIBUTE_SPECS_BY_NAME.put( "_networkReactor", new AttributeSpec(List.of(new AttrParamSpec(VALUE_ATTR, AttrParamType.STRING, false)))); - } + // @interface:tcp(name="string", address="string") e.g. @interface:tcp(name="if1", address="127.0.0.1") + ATTRIBUTE_SPECS_BY_NAME.put( + "interface_tcp", + new AttributeSpec( + List.of( + new AttrParamSpec("name", AttrParamType.STRING, true), + new AttrParamSpec("address", AttrParamType.STRING, true)))); + ATTRIBUTE_SPECS_BY_NAME.put( + "interface_coap", + new AttributeSpec( + List.of( + new AttrParamSpec("name", AttrParamType.STRING, true), + new AttrParamSpec("address", AttrParamType.STRING, true)))); + ATTRIBUTE_SPECS_BY_NAME.put( + "interface_custom", + new AttributeSpec( + List.of( + new AttrParamSpec("name", AttrParamType.STRING, false), + new AttrParamSpec("args", AttrParamType.STRING, true), + new AttrParamSpec("include", AttrParamType.STRING, false)))); + // @link(type="string", server_port=int, server_side="string", args="string") e.g. @link(type="TcpIp", server_port=1042) + ATTRIBUTE_SPECS_BY_NAME.put( + "link", + new AttributeSpec( + List.of( + new AttrParamSpec("left", AttrParamType.STRING, true), + new AttrParamSpec("right", AttrParamType.STRING, true), + new AttrParamSpec("server_port", AttrParamType.INT, true), + new AttrParamSpec("server_side", AttrParamType.STRING, true)))); +} } diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcCmakeGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcCmakeGenerator.kt index 6d94ef65..7f4f5f1e 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcCmakeGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcCmakeGenerator.kt @@ -1,78 +1,88 @@ package org.lflang.generator.uc -import org.lflang.FileConfig +import org.lflang.* import org.lflang.target.TargetConfig import org.lflang.generator.PrependOperator -import org.lflang.generator.uc.UcReactorGenerator.Companion.getEventQueueSize -import org.lflang.generator.uc.UcReactorGenerator.Companion.getReactionQueueSize -import org.lflang.joinWithLn +import org.lflang.generator.uc.UcInstanceGenerator.Companion.isAFederate +import org.lflang.lf.Instantiation import org.lflang.lf.Reactor import org.lflang.target.property.BuildTypeProperty import org.lflang.target.property.CmakeIncludeProperty +import org.lflang.target.property.LoggingProperty import org.lflang.target.property.PlatformProperty import org.lflang.target.property.type.PlatformType -import org.lflang.toUnixString -import org.lflang.unreachable import org.lflang.util.FileUtil import java.nio.file.Path import java.time.LocalDateTime import kotlin.io.path.name import kotlin.math.max -class UcCmakeGenerator(private val main: Reactor, private val targetConfig: TargetConfig, private val fileConfig: FileConfig) { - private val S = '$' // a little trick to escape the dollar sign with $S - private val platform = targetConfig.get(PlatformProperty.INSTANCE).platform - val includeFiles = targetConfig.get(CmakeIncludeProperty.INSTANCE)?.map { fileConfig.srcPath.resolve(it).toUnixString() } +abstract class UcCmakeGenerator(private val targetConfig: TargetConfig, private val fileConfig: UcFileConfig, private val numEvents: Int, private val numReactions: Int) { + protected val S = '$' // a little trick to escape the dollar sign with $S + private val minCmakeVersion = "3.10" + protected val includeFiles = targetConfig.get(CmakeIncludeProperty.INSTANCE)?.map { fileConfig.srcPath.resolve(it).toUnixString() } + protected val platform = targetConfig.get(PlatformProperty.INSTANCE).platform + abstract val mainTarget: String + abstract fun generateCmake(sources: List): String - - fun generateCmake(sources: List) = - if (platform == PlatformType.Platform.NATIVE) { - generateCmakePosix(sources) - } else { - generateCmakeEmbedded(sources) - } - - fun generateCmakeEmbedded(sources: List) = with(PrependOperator) { - """ - |# This file is generated by LFC. It is meant to be included in - |# an existing CMake project. - | + protected fun generateCmakeCommon(sources: List, compileDefs: List) = with(PrependOperator) { + """ | |set(LFC_GEN_SOURCES ${" | "..sources.filterNot{it.name == "lf_main.c"}.joinWithLn { "$S{CMAKE_CURRENT_LIST_DIR}/${it.toUnixString()}"}} + |) + |set(LFC_GEN_COMPILE_DEFS + ${" | "..compileDefs.joinWithLn { it }} |) |set(LFC_GEN_MAIN "$S{CMAKE_CURRENT_LIST_DIR}/lf_main.c") |set(REACTOR_UC_PATH $S{CMAKE_CURRENT_LIST_DIR}/reactor-uc) |set(LFC_GEN_INCLUDE_DIRS $S{CMAKE_CURRENT_LIST_DIR}) - |set(REACTION_QUEUE_SIZE ${main.getReactionQueueSize()} CACHE STRING "Size of the reaction queue") - |set(EVENT_QUEUE_SIZE ${main.getEventQueueSize()} CACHE STRING "Size of the event queue") - | + |set(REACTION_QUEUE_SIZE ${max(numReactions, 1)} CACHE STRING "Size of the reaction queue") + |set(EVENT_QUEUE_SIZE ${max(numEvents, 2)} CACHE STRING "Size of the event queue") """.trimMargin() } - fun generateCmakePosix(sources: List) = with(PrependOperator) { + protected fun generateCmakePosix(sources: List, compileDefs: List) = with(PrependOperator) { """ - |cmake_minimum_required(VERSION 3.10) - |project(${fileConfig.name} VERSION 0.0.0 LANGUAGES C) - |set(PLATFORM POSIX CACHE STRING "Target platform") - |set(REACTION_QUEUE_SIZE ${max(main.getReactionQueueSize(), 1)} CACHE STRING "Size of the reaction queue") - |set(EVENT_QUEUE_SIZE ${max(main.getEventQueueSize(), 1)} CACHE STRING "Size of the event queue") + |cmake_minimum_required(VERSION $minCmakeVersion) + |project(${mainTarget} LANGUAGES C) + |set(LF_MAIN_TARGET ${mainTarget}) |set(CMAKE_BUILD_TYPE ${targetConfig.getOrDefault(BuildTypeProperty.INSTANCE)}) - | - |set(LF_MAIN_TARGET ${fileConfig.name}) - |set(SOURCES - ${" | "..sources.joinWithLn { it.toUnixString() }} - |) - |add_executable($S{LF_MAIN_TARGET} $S{SOURCES}) + |set(PLATFORM POSIX CACHE STRING "Target platform") + ${" |"..generateCmakeCommon(sources, compileDefs)} + |add_executable($S{LF_MAIN_TARGET} $S{LFC_GEN_SOURCES} $S{LFC_GEN_MAIN}) |install(TARGETS $S{LF_MAIN_TARGET} | RUNTIME DESTINATION $S{CMAKE_INSTALL_BINDIR} | OPTIONAL |) - | - |add_subdirectory(reactor-uc) + |add_compile_definitions("LF_LOG_LEVEL_ALL=LF_LOG_LEVEL_${targetConfig.getOrDefault(LoggingProperty.INSTANCE).name.uppercase()}") + |add_compile_definitions($S{LFC_GEN_COMPILE_DEFS}) + |add_subdirectory($S{REACTOR_UC_PATH}) |target_link_libraries($S{LF_MAIN_TARGET} PRIVATE reactor-uc) - |target_include_directories($S{LF_MAIN_TARGET} PRIVATE $S{CMAKE_CURRENT_LIST_DIR}) - ${" |"..(includeFiles?.joinWithLn { "include(\"$it\")" } ?: "")} + |target_include_directories($S{LF_MAIN_TARGET} PRIVATE $S{LFC_GEN_INCLUDE_DIRS}) + ${" |"..(includeFiles?.joinWithLn { "include(\"$it\")" } ?: "")} """.trimMargin() } } + +class UcCmakeGeneratorNonFederated(private val mainDef: Instantiation, targetConfig: TargetConfig, fileConfig: UcFileConfig, numEvents: Int, numReactions: Int): UcCmakeGenerator(targetConfig, fileConfig, numEvents, numReactions) { + override val mainTarget = fileConfig.name + + override fun generateCmake(sources: List) = + if (platform == PlatformType.Platform.NATIVE) { + generateCmakePosix(sources, emptyList()) + } else { + generateCmakeCommon(sources, emptyList()) + } +} + +class UcCmakeGeneratorFederated(private val federate: UcFederate, targetConfig: TargetConfig, fileConfig: UcFileConfig, numEvents: Int, numReactions: Int): UcCmakeGenerator(targetConfig, fileConfig, numEvents, numReactions) { + override val mainTarget = federate.codeType + + override fun generateCmake(sources: List) = + if (platform == PlatformType.Platform.NATIVE) { + generateCmakePosix(sources, federate.getCompileDefs()) + } else { + generateCmakeCommon(sources, federate.getCompileDefs()) + } +} diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcConnectionGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcConnectionGenerator.kt index 90811452..387eb703 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcConnectionGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcConnectionGenerator.kt @@ -3,235 +3,473 @@ package org.lflang.generator.uc import org.lflang.* import org.lflang.generator.PrependOperator import org.lflang.generator.orNever -import org.lflang.generator.uc.UcInstanceGenerator.Companion.width -import org.lflang.generator.uc.UcPortGenerator.Companion.width +import org.lflang.generator.uc.UcInstanceGenerator.Companion.isAFederate import org.lflang.generator.uc.UcReactorGenerator.Companion.codeType import org.lflang.lf.* - -// Representing a runtime connection with a single upstream port. An optimization is to group -// all identical connections (with same delay and is_physical) in a single connection -class UcGroupedConnection(val varRef: VarRef, val conn: Connection, val uid: Int) { - private val dests = mutableListOf>>() - private var destsBuffer = mutableListOf>() - val srcInst: Instantiation? = varRef.container - val srcPort = varRef.variable as Port - val bankWidth = srcInst?.width?:1 - val portWidth = srcPort.width - val uniqueName = "conn_${srcInst?.name?:""}_${srcPort.name}_${uid}" - val maxNumPendingEvents = 16 // FIXME: Must be derived from the program - - val delay = conn.delay.orNever().toCCode() - val isPhysical = conn.isPhysical - val isLogical = !isPhysical && conn.delay == null - - fun numDownstreams() = dests.size - - fun getDests() = dests - - fun addDest(channels: Pair) { - destsBuffer.add(channels) - } - - fun commit() { - dests.add(destsBuffer) - destsBuffer = mutableListOf>() - } -} - -// Convenience class around a port variable reference. It is used to encapsulate the management of multi-connections -// where a single lhs port has to -class UcChannel(val varRef: VarRef, val port_idx: Int, val bank_idx: Int) { - private val portOfContainedReactor = varRef.container != null - private val index = "${if (portOfContainedReactor) "[${bank_idx}]" else ""}[${port_idx}]" - private val reactorInstance = if (portOfContainedReactor) "${varRef.container.name}[${bank_idx}]." else "" - private val portInstance = "${varRef.name}[${port_idx}]" - - fun generateChannelPointer() = "&self->${reactorInstance}${portInstance}" -} - -// Wrapper around a variable reference to a Port. Contains a channel for each bank/multiport within it. -// For each connection statement where a port is referenced, we create an UcPort and use this class -// to figure out how the individual channels are connect to other UcPorts. -class UcPort(val varRef: VarRef) { - val bankWidth = varRef.container?.width?:1 - val portWidth = (varRef.variable as Port).width - private val isInterleaved = varRef.isInterleaved - private val channels = ArrayDeque() - - // Construct the stack of channels belonging to this port. If this port is interleaved, - // then we create channels first for ports then for banks. - init { - if (isInterleaved) { - for (i in 0..portWidth-1) { - for (j in 0..bankWidth-1) { - channels.add(UcChannel(varRef, i, j)) - } - } - } else { - for (i in 0..bankWidth-1) { - for (j in 0..portWidth-1) { - channels.add(UcChannel(varRef, j, i)) - } - } - } - } - - fun takeRemainingChannels(): List = takeChannels(channels.size) - - // Get a number of channels from this port. This has sideeffects and will remove these - // channels from the port. - fun takeChannels(numChannels: Int): List { - assert(numChannels >= channels.size) - val res = mutableListOf() - for (i in 1..numChannels) { - res.add(channels.removeFirst()) - } - return res - } - - fun channelsLeft(): Int = channels.size -} - - -// A class for maintaining all the runtime UcConnections within a reactor. -class UcConnections() { - val connections = mutableListOf(); - - // Parse a connection and update the list of UcGroupedConnection. This is non-trivial since a single variable - // in the connection can have several banks and multiports. In a multi-connection, we might connect some channels - // to one variable and others to another. - fun addConnection(conn: Connection) { - // First translate the variables into our UcPort which also has information of channels (banks x multiports) - val rhsPorts= conn.rightPorts.map {UcPort(it)} +/** + * This generator creates code for configuring the connections between reactors. This is perhaps the most complicated + * part of the code-generator. This generator handles both federated and non-federated programs + */ +class UcConnectionGenerator( + private val reactor: Reactor, // The reactor to generator connections for + private val currentFederate: UcFederate?, // The federate to generate connections for. If set then `reactor` should be the top-level reactor. + private val allFederates: List // A list of all the federates in the program. Only used for federated code-gen. +) { + + /** A list containing all non-federated gruoped connections within this reactor. */ + private val nonFederatedConnections: List + + /** A list containing all federated connection bundles (each containing grouped connections) within this reactor, that + * has the current federate as a src or dest. + */ + private val federatedConnectionBundles: List + + private val isFederated = currentFederate != null + + /** + * Given a LF connection and possibly the list of federates of the program. Create all the ConnectionChannels + * found within the LF Connection. This must handle multiports, banks, iterated connections and federated connections. + */ + private fun parseConnectionChannels(conn: Connection, federates: List): List { + val res = mutableListOf() + val rhsPorts = conn.rightPorts.map { getChannelQueue(it, federates) } var rhsPortIndex = 0 - var lhsPorts= conn.leftPorts.map{UcPort(it)} + + var lhsPorts = conn.leftPorts.map { getChannelQueue(it, federates) } var lhsPortIndex = 0 // Keep parsing out connections until we are out of right-hand-side (rhs) ports while (rhsPortIndex < rhsPorts.size) { // First get the current lhs and rhs port and UcGroupedConnection that we are working with - val lhsPort = lhsPorts.get(lhsPortIndex) - val rhsPort = rhsPorts.get(rhsPortIndex) - val ucConnection = getOrCreateNewGroupedConnection(lhsPort.varRef, conn) - + val lhsPort = lhsPorts[lhsPortIndex] + val rhsPort = rhsPorts[rhsPortIndex] if (rhsPort.channelsLeft() > lhsPort.channelsLeft()) { - // If we have more channels left in the rhs variable, then we "complete" a downstreamSet fo - // the lhs, commit it, and move to the next lhs variable val rhsChannelsToAdd = rhsPort.takeChannels(lhsPort.channelsLeft()) val lhsChannelsToAdd = lhsPort.takeRemainingChannels() - lhsChannelsToAdd.zip(rhsChannelsToAdd).forEach{ ucConnection.addDest(it)} - ucConnection.commit() + lhsChannelsToAdd.zip(rhsChannelsToAdd) + .forEach { res.add(UcConnectionChannel(it.first, it.second, conn)) } lhsPortIndex += 1 } else if (rhsPort.channelsLeft() < lhsPort.channelsLeft()) { - // If we have more channels left in the lhs variable, we dont complete the downstreamSet yet, - // we move to the next rhsChannel. Only if this was the very last rhs variable do we commit. val numRhsChannelsToAdd = rhsPort.channelsLeft() val rhsChannelsToAdd = rhsPort.takeRemainingChannels() val lhsChannelsToAdd = lhsPort.takeChannels(numRhsChannelsToAdd) - lhsChannelsToAdd.zip(rhsChannelsToAdd).forEach{ ucConnection.addDest(it)} + lhsChannelsToAdd.zip(rhsChannelsToAdd) + .forEach { res.add(UcConnectionChannel(it.first, it.second, conn)) } rhsPortIndex += 1 - if (rhsPortIndex >= rhsPorts.size) { - ucConnection.commit() - } } else { - // Channels are same size - lhsPort.takeRemainingChannels().zip(rhsPort.takeRemainingChannels()).forEach { ucConnection.addDest(it) } - ucConnection.commit() + lhsPort.takeRemainingChannels().zip(rhsPort.takeRemainingChannels()) + .forEach { res.add(UcConnectionChannel(it.first, it.second, conn)) } rhsPortIndex += 1 lhsPortIndex += 1 } // If we are out of lhs variables, but not rhs, then there should be an iterated connection. // We handle it by resetting the lhsChannels variable and index and continuing until - // we have been thorugh all rhs channels. + // we have been through all rhs channels. if (lhsPortIndex >= lhsPorts.size && rhsPortIndex < rhsPorts.size) { assert(conn.isIterated) - lhsPorts = conn.leftPorts.map{UcPort(it)} + lhsPorts = conn.leftPorts.map {getChannelQueue(it, federates)} lhsPortIndex = 0 } - } } - - /** Finds an existing GroupedConnection from srcVarRef with matchin connection properties (physical and delay). */ - fun findExistingGroupedConnection(srcVarRef: VarRef, conn: Connection): UcGroupedConnection? { - return connections.find { c -> c.varRef == srcVarRef && c.isPhysical == conn.isPhysical && c.delay == conn.delay.orNever().toCCode()} + return res } - /** Finds an existing grouped connection, or creates a new.*/ - fun getOrCreateNewGroupedConnection(srcVarRef: VarRef, conn: Connection): UcGroupedConnection { - var res = findExistingGroupedConnection(srcVarRef, conn) - if (res == null) { - res = UcGroupedConnection(srcVarRef, conn, connections.size) - connections.add(res) + /** + * Given a list of ConnectionChannels, group them together. How they are grouepd depends on + * whether we are dealing with federated or non-federated reactors. + */ + private fun groupConnections(channels: List): List { + val res = mutableListOf() + val channels = HashSet(channels) + + while (channels.isNotEmpty()) { + val c = channels.first()!! + + if (c.isFederated) { + val grouped = + channels.filter { + it.conn.delayString == c.conn.delayString && + it.conn.isPhysical == c.conn.isPhysical && + it.src.federate == c.src.federate && + it.dest.federate == c.dest.federate && + it.getChannelType() == c.getChannelType() + } + + val srcFed = allFederates.find { it == UcFederate(c.src.varRef.container, c.src.bankIdx) }!! + val destFed = allFederates.find { it == UcFederate(c.dest.varRef.container, c.dest.bankIdx) }!! + val groupedConnection = UcFederatedGroupedConnection( + c.src.varRef, + grouped, + c.conn, + srcFed, + destFed, + ) + + res.add(groupedConnection) + channels.removeAll(grouped.toSet()) + + } else { + val grouped = + channels.filter { + it.conn.delayString == c.conn.delayString && + it.conn.isPhysical == c.conn.isPhysical && + !it.isFederated && + it.src.varRef == c.src.varRef && + it.src.federate == c.src.federate + } + + val groupedConnection = UcGroupedConnection(c.src.varRef, grouped, c.conn) + res.add(groupedConnection) + channels.removeAll(grouped.toSet()) + } } return res } - /** Find the number of grouped connections coming out of a particular port. This is needed to know how many - * Connection pointers to allocated on the self-struct of a reactor containing another reactor with an output port. */ - fun findNumGroupedConnectionsFromPort(srcInst: Instantiation?, srcPort: Port) = - connections.filter{ c -> c.srcPort == srcPort && c.srcInst == srcInst}.size -} + /** Given a port VarRef, and the list of federates. Create a channel queue. I.e. create + * all the UcChannels and associate them with the correct federates. + */ + private fun getChannelQueue(portVarRef: VarRef, federates: List): UcChannelQueue { + return if (portVarRef.container?.isAFederate ?: false) { + val federates = allFederates.filter { it.inst == portVarRef.container } + UcChannelQueue(portVarRef, federates) + } else { + UcChannelQueue(portVarRef, emptyList()) + } + } + + companion object { + private val Connection.delayString + get(): String = this.delay.orNever().toCCode() + + /** Whether we have initialized the UcFederates with NetworkInterfaces. This is only done once. */ + private var federateInterfacesInitialized = false + /** A global list of FederatedConnectionBundles. It is computed once and reused when code-generating */ + private var allFederatedConnectionBundles: List = emptyList() + + /** + * This function takes a list of grouped connections and creates the necessary FederatedConnectionBundles. + * The bundles are written to the global variable allFederatedConnectionBundles and shared accross federates. + * Thus, this function should only be called once during code-gen. + */ + private fun createFederatedConnectionBundles(groupedConnections: List) { + val groupedSet = HashSet(groupedConnections) + val bundles = mutableListOf() + + while (groupedSet.isNotEmpty()) { + val g = groupedSet.first()!! + val toRemove = mutableListOf(g) + when (g) { + is UcFederatedGroupedConnection -> { + val group = groupedSet.filterIsInstance().filter { + (it.srcFed == g.srcFed && it.destFed == g.destFed) || + (it.srcFed == g.destFed && it.destFed == g.srcFed) + } + + bundles.add( + UcFederatedConnectionBundle( + g.srcFed, g.destFed, group + ) + ) + + toRemove.addAll(group) + } + } + groupedSet.removeAll(toRemove.toSet()) + } + allFederatedConnectionBundles = bundles + } + } -class UcConnectionGenerator(private val reactor: Reactor) { - private val ucConnections = UcConnections() init { - reactor.allConnections.forEach { ucConnections.addConnection(it) } + // Only pass through all federates and add NetworkInterface objects to them once. + if (isFederated && !federateInterfacesInitialized) { + for (fed in allFederates) { + UcNetworkInterfaceFactory.createInterfaces(fed).forEach { fed.addInterface(it) } + } + federateInterfacesInitialized = true + } + + // Parse out all GroupedConnections. Note that this is repeated for each federate. + val channels = mutableListOf() + reactor.allConnections.forEach { channels.addAll(parseConnectionChannels(it, allFederates)) } + val grouped = groupConnections(channels) + nonFederatedConnections = mutableListOf() + federatedConnectionBundles = mutableListOf() + + if (isFederated) { + // Only parse out federated connection bundles once for the very first federate + if (allFederatedConnectionBundles.isEmpty()) { + createFederatedConnectionBundles(grouped) + } + + // Filter out the relevant bundles for this federate + federatedConnectionBundles.addAll( + allFederatedConnectionBundles.filter { it.src == currentFederate || it.dest == currentFederate } + ) + // Add all non-federated connections (e.g. a loopback connection) + nonFederatedConnections.addAll( + grouped + .filterNot { it is UcFederatedGroupedConnection } + .filter { it.channels.fold(true) { acc, c -> acc && (c.src.federate == currentFederate) } } + ) + } else { + // In the non-federated case, all grouped connections are handled togehter. + nonFederatedConnections.addAll(grouped) + } + + // Assign a unique ID to each connection to avoid possible naming conflicts in the generated code. + val allGroupedConnections = + federatedConnectionBundles.map { it.groupedConnections }.flatten().plus(nonFederatedConnections) + allGroupedConnections.forEachIndexed { idx, el -> + el.assignUid(idx) + } } + + fun getNumFederatedConnectionBundles() = federatedConnectionBundles.size + fun getNumConnectionsFromPort(instantiation: Instantiation?, port: Port): Int { - return ucConnections.findNumGroupedConnectionsFromPort(instantiation, port) + var count = 0 + // Find all outgoing non-federated grouped connections from this port + for (groupedConn in nonFederatedConnections) { + if (groupedConn.srcInst == instantiation && groupedConn.srcPort == port) { + count += 1 + } + } + + // Find all outgoing federated grouped connections from this port. + for (federatedConnectionBundle in federatedConnectionBundles) { + for (groupedConn in federatedConnectionBundle.groupedConnections) { + if (groupedConn.srcFed == currentFederate && groupedConn.srcInst == instantiation && groupedConn.srcPort == port) { + count += 1 + } + } + } + return count } - private fun generateLogicalSelfStruct(conn: UcGroupedConnection) = "LF_DEFINE_LOGICAL_CONNECTION_STRUCT(${reactor.codeType}, ${conn.uniqueName}, ${conn.numDownstreams()})"; - private fun generateLogicalCtor(conn: UcGroupedConnection) = "LF_DEFINE_LOGICAL_CONNECTION_CTOR(${reactor.codeType}, ${conn.uniqueName}, ${conn.numDownstreams()})"; + private fun generateLogicalSelfStruct(conn: UcGroupedConnection) = + "LF_DEFINE_LOGICAL_CONNECTION_STRUCT(${reactor.codeType}, ${conn.getUniqueName()}, ${conn.numDownstreams()});" + + private fun generateLogicalCtor(conn: UcGroupedConnection) = + "LF_DEFINE_LOGICAL_CONNECTION_CTOR(${reactor.codeType}, ${conn.getUniqueName()}, ${conn.numDownstreams()});" + + private fun generateDelayedSelfStruct(conn: UcGroupedConnection) = + "LF_DEFINE_DELAYED_CONNECTION_STRUCT(${reactor.codeType}, ${conn.getUniqueName()}, ${conn.numDownstreams()}, ${conn.srcPort.type.toText()}, ${conn.maxNumPendingEvents}, ${conn.delay});" + + private fun generateDelayedCtor(conn: UcGroupedConnection) = + "LF_DEFINE_DELAYED_CONNECTION_CTOR(${reactor.codeType}, ${conn.getUniqueName()}, ${conn.numDownstreams()}, ${conn.srcPort.type.toText()}, ${conn.maxNumPendingEvents}, ${conn.delay}, ${conn.isPhysical});" - private fun generateDelayedSelfStruct(conn: UcGroupedConnection) = "LF_DEFINE_DELAYED_CONNECTION_STRUCT(${reactor.codeType}, ${conn.uniqueName}, ${conn.numDownstreams()}, ${conn.srcPort.type.toText()}, ${conn.maxNumPendingEvents}, ${conn.delay})"; - private fun generateDelayedCtor(conn: UcGroupedConnection) = "LF_DEFINE_DELAYED_CONNECTION_CTOR(${reactor.codeType}, ${conn.uniqueName}, ${conn.numDownstreams()}, ${conn.srcPort.type.toText()}, ${conn.maxNumPendingEvents}, ${conn.delay}, ${conn.isPhysical})"; + private fun generateFederatedInputSelfStruct(conn: UcGroupedConnection) = + "LF_DEFINE_FEDERATED_INPUT_CONNECTION_STRUCT(${reactor.codeType}, ${conn.getUniqueName()}, ${conn.srcPort.type.toText()}, ${conn.maxNumPendingEvents});" + private fun generateFederatedInputCtor(conn: UcGroupedConnection) = + "LF_DEFINE_FEDERATED_INPUT_CONNECTION_CTOR(${reactor.codeType}, ${conn.getUniqueName()}, ${conn.srcPort.type.toText()}, ${conn.maxNumPendingEvents}, ${conn.delay}, ${conn.isPhysical});" - private fun generateReactorCtorCode(conn: UcGroupedConnection) = with(PrependOperator) { - """ - |${if (conn.isLogical) "LF_INITIALIZE_LOGICAL_CONNECTION(" else "LF_INITIALIZE_DELAYED_CONNECTION("}${reactor.codeType}, ${conn.uniqueName}, ${conn.bankWidth}, ${conn.portWidth}); - """.trimMargin() - }; + private fun generateFederatedOutputSelfStruct(conn: UcGroupedConnection) = + "LF_DEFINE_FEDERATED_OUTPUT_CONNECTION_STRUCT(${reactor.codeType}, ${conn.getUniqueName()}, ${conn.srcPort.type.toText()});" - private fun generateConnectChannel(conn: UcGroupedConnection, channels: Pair ) = with(PrependOperator) { - """|lf_connect((Connection *) &self->${conn.uniqueName}[${channels.first.bank_idx}][${channels.first.port_idx}], (Port *) ${channels.first.generateChannelPointer()}, (Port *) ${channels.second.generateChannelPointer()}); + private fun generateFederatedOutputCtor(conn: UcGroupedConnection) = + "LF_DEFINE_FEDERATED_OUTPUT_CONNECTION_CTOR(${reactor.codeType}, ${conn.getUniqueName()}, ${conn.srcPort.type.toText()});" + + private fun generateFederatedConnectionSelfStruct(conn: UcFederatedGroupedConnection) = + if (conn.srcFed == currentFederate) generateFederatedOutputSelfStruct(conn) else generateFederatedInputSelfStruct( + conn + ) + + private fun generateFederatedConnectionCtor(conn: UcFederatedGroupedConnection) = + if (conn.srcFed == currentFederate) generateFederatedOutputCtor(conn) else generateFederatedInputCtor(conn) + + private fun generateFederatedOutputInstance(conn: UcGroupedConnection) = + "LF_FEDERATED_OUTPUT_CONNECTION_INSTANCE(${reactor.codeType}, ${conn.getUniqueName()});" + + private fun generateFederatedInputInstance(conn: UcGroupedConnection) = + "LF_FEDERATED_INPUT_CONNECTION_INSTANCE(${reactor.codeType}, ${conn.getUniqueName()});" + + private fun generateFederatedConnectionInstance(conn: UcFederatedGroupedConnection) = + if (conn.srcFed == currentFederate) generateFederatedOutputInstance(conn) else generateFederatedInputInstance( + conn + ) + + private fun generateInitializeFederatedOutput(conn: UcFederatedGroupedConnection) = + "LF_INITIALIZE_FEDERATED_OUTPUT_CONNECTION(${reactor.codeType}, ${conn.getUniqueName()}, ${conn.serializeFunc});" + + private fun generateInitializeFederatedInput(conn: UcFederatedGroupedConnection) = + "LF_INITIALIZE_FEDERATED_INPUT_CONNECTION(${reactor.codeType}, ${conn.getUniqueName()}, ${conn.deserializeFunc});" + + private fun generateInitializeFederatedConnection(conn: UcFederatedGroupedConnection) = + if (conn.srcFed == currentFederate) generateInitializeFederatedOutput(conn) else generateInitializeFederatedInput( + conn + ) + + + private fun generateReactorCtorCode(conn: UcGroupedConnection) = """ + |${if (conn.isLogical) "LF_INITIALIZE_LOGICAL_CONNECTION(" else "LF_INITIALIZE_DELAYED_CONNECTION("}${reactor.codeType}, ${conn.getUniqueName()}, ${conn.bankWidth}, ${conn.portWidth}); """.trimMargin() + + private fun generateFederateCtorCode(conn: UcFederatedConnectionBundle) = + "LF_INITIALIZE_FEDERATED_CONNECTION_BUNDLE(${conn.src.codeType}, ${conn.dest.codeType});" + + private fun generateConnectChannel(groupedConn: UcGroupedConnection, channel: UcConnectionChannel) = + """|lf_connect((Connection *) &self->${groupedConn.getUniqueName()}[${channel.src.getCodeBankIdx()}][${channel.src.getCodePortIdx()}], (Port *) ${channel.src.generateChannelPointer()}, (Port *) ${channel.dest.generateChannelPointer()}); + """.trimMargin() + + private fun generateConnectionStatements(conn: UcGroupedConnection) = conn.channels + .joinToString(separator = "\n") { generateConnectChannel(conn, it) } + + private fun generateConnectFederateOutputChannel( + bundle: UcFederatedConnectionBundle, + conn: UcFederatedGroupedConnection + ) = + conn.channels.joinWithLn { + "lf_connect_federated_output((Connection *)&self->LF_FEDERATED_CONNECTION_BUNDLE_NAME(${bundle.src.codeType}, ${bundle.dest.codeType}).${conn.getUniqueName()}, (Port*) &self->${bundle.src.inst.name}[0].${it.src.varRef.name}[${it.src.portIdx}]);" + } + + private fun generateConnectFederateInputChannel( + bundle: UcFederatedConnectionBundle, + conn: UcGroupedConnection + ) = + conn.channels.joinWithLn { + "lf_connect_federated_input((Connection *)&self->LF_FEDERATED_CONNECTION_BUNDLE_NAME(${bundle.src.codeType}, ${bundle.dest.codeType}).${conn.getUniqueName()}, (Port*) &self->${bundle.dest.inst.name}[0].${it.dest.varRef.name}[${it.dest.portIdx}]);" + } + + + private fun generateFederateConnectionStatements(conn: UcFederatedConnectionBundle) = + conn.groupedConnections.joinWithLn { + if (it.srcFed == currentFederate) { + generateConnectFederateOutputChannel(conn, it) + } else { + generateConnectFederateInputChannel(conn, it) + } + } + + fun generateFederateCtorCodes() = + federatedConnectionBundles.joinToString( + prefix = "// Initialize connection bundles\n", + separator = "\n", + postfix = "\n" + ) { generateFederateCtorCode(it) } + + federatedConnectionBundles.joinToString( + prefix = "// Do connections \n", + separator = "\n", + postfix = "\n" + ) { generateFederateConnectionStatements(it) } + + fun generateReactorCtorCodes() = + nonFederatedConnections.joinToString( + prefix = "// Initialize connections\n", + separator = "\n", + postfix = "\n" + ) { generateReactorCtorCode(it) } + + nonFederatedConnections.joinToString( + prefix = "// Do connections \n", + separator = "\n", + postfix = "\n" + ) { generateConnectionStatements(it) } + + fun generateCtors() = nonFederatedConnections.joinToString( + prefix = "// Connection constructors\n", + separator = "\n", + postfix = "\n" + ) { + if (it.isDelayed) generateDelayedCtor(it) + else generateLogicalCtor(it) } - private fun generateConnectionStatements(conn: UcGroupedConnection) = with(PrependOperator) { - conn.getDests().joinToString(separator="\n"){it.joinToString(separator = "\n") { generateConnectChannel(conn, it)}} + fun generateSelfStructs() = + nonFederatedConnections.joinToString(prefix = "// Connection structs\n", separator = "\n", postfix = "\n") { + if (it.isLogical) generateLogicalSelfStruct(it) + else generateDelayedSelfStruct(it) + } + + private fun generateFederatedConnectionBundleSelfStruct(bundle: UcFederatedConnectionBundle) = + with(PrependOperator) { + """ |typedef struct { + | FederatedConnectionBundle super; + ${" | "..bundle.networkChannel.codeType} channel; + ${" | "..bundle.groupedConnections.joinWithLn { generateFederatedConnectionInstance(it) }} + | LF_FEDERATED_CONNECTION_BUNDLE_BOOKKEEPING_INSTANCES(${bundle.numInputs(currentFederate!!)}, ${ + bundle.numOutputs( + currentFederate + ) + }); + |} LF_FEDERATED_CONNECTION_BUNDLE_TYPE(${bundle.src.codeType}, ${bundle.dest.codeType}); + | + """.trimMargin() + } + + private fun generateFederatedConnectionBundleCtor(bundle: UcFederatedConnectionBundle) = with(PrependOperator) { + """ |LF_FEDERATED_CONNECTION_BUNDLE_CTOR_SIGNATURE(${bundle.src.codeType}, ${bundle.dest.codeType}) { + | LF_FEDERATED_CONNECTION_BUNDLE_CTOR_PREAMBLE(); + | ${bundle.generateNetworkChannelCtor(currentFederate!!)} + | LF_FEDERATED_CONNECTION_BUNDLE_CALL_CTOR(); + ${" | "..bundle.groupedConnections.joinWithLn { generateInitializeFederatedConnection(it) }} + |} + """.trimMargin() } - fun generateReactorCtorCodes() = - ucConnections.connections.joinToString(prefix = "// Initialize connections\n", separator = "\n", postfix = "\n") { generateReactorCtorCode(it)} + - ucConnections.connections.joinToString(prefix = "// Initialize connections\n", separator = "\n", postfix = "\n") { generateConnectionStatements(it)} + fun generateFederatedSelfStructs() = federatedConnectionBundles.joinToString( + prefix = "// Federated Connections\n", + separator = "\n", + postfix = "\n" + ) { itOuter -> + itOuter.groupedConnections.joinToString( + prefix = "// Federated input and output connection self structs\n", + separator = "\n", + postfix = "\n" + ) { generateFederatedConnectionSelfStruct(it) } + + generateFederatedConnectionBundleSelfStruct(itOuter) + } - fun generateCtors() = ucConnections.connections.joinToString(prefix = "// Connection constructors\n", separator = "\n", postfix = "\n"){ - if(it.conn.isPhysical || it.conn.delay != null) generateDelayedCtor(it) - else generateLogicalCtor(it) + fun generateFederatedCtors() = federatedConnectionBundles.joinToString( + prefix = "// Federated Connections\n", + separator = "\n", + postfix = "\n" + ) { itOuter -> + itOuter.groupedConnections.joinToString( + prefix = "// Federated input and output connection constructors\n", + separator = "\n", + postfix = "\n" + ) { generateFederatedConnectionCtor(it) } + + generateFederatedConnectionBundleCtor(itOuter) } - fun generateSelfStructs() = ucConnections.connections.joinToString(prefix = "// Connection structs\n", separator = "\n", postfix = "\n") { - if (it.isLogical) generateLogicalSelfStruct(it) - else generateDelayedSelfStruct(it) + + fun generateFederateStructFields() = federatedConnectionBundles.joinToString( + prefix = "// Federated Connections\n", + separator = "\n", + postfix = "\n" + ) { + "LF_FEDERATED_CONNECTION_BUNDLE_INSTANCE(${it.src.codeType}, ${it.dest.codeType});" } + fun generateReactorStructFields() = - ucConnections.connections.joinToString(prefix = "// Connections \n", separator = "\n", postfix = "\n") { - if (it.isLogical) "LF_LOGICAL_CONNECTION_INSTANCE(${reactor.codeType}, ${it.uniqueName}, ${it.bankWidth}, ${it.portWidth});" - else "LF_DELAYED_CONNECTION_INSTANCE(${reactor.codeType}, ${it.uniqueName}, ${it.bankWidth}, ${it.portWidth});" + nonFederatedConnections.joinToString(prefix = "// Connections \n", separator = "\n", postfix = "\n") { + if (it.isLogical) "LF_LOGICAL_CONNECTION_INSTANCE(${reactor.codeType}, ${it.getUniqueName()}, ${it.bankWidth}, ${it.portWidth});" + else "LF_DELAYED_CONNECTION_INSTANCE(${reactor.codeType}, ${it.getUniqueName()}, ${it.bankWidth}, ${it.portWidth});" } fun getMaxNumPendingEvents(): Int { var res = 0 - for (conn in ucConnections.connections) { + for (conn in nonFederatedConnections) { if (!conn.isLogical) { res += conn.maxNumPendingEvents } } + for (bundle in federatedConnectionBundles) { + for (conn in bundle.groupedConnections) { + if (conn.destFed == currentFederate) { + res += conn.maxNumPendingEvents + } + } + } return res } + + fun generateNetworkChannelIncludes(): String = + federatedConnectionBundles.distinctBy { it.networkChannel.type } + .joinWithLn { it.networkChannel.src.iface.includeHeaders } } diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcConnectionUtils.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcConnectionUtils.kt new file mode 100644 index 00000000..9688358b --- /dev/null +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcConnectionUtils.kt @@ -0,0 +1,174 @@ +package org.lflang.generator.uc + +import org.lflang.AttributeUtils.getLinkAttribute +import org.lflang.generator.orNever +import org.lflang.generator.uc.UcInstanceGenerator.Companion.codeWidth +import org.lflang.generator.uc.UcInstanceGenerator.Companion.width +import org.lflang.generator.uc.UcPortGenerator.Companion.width +import org.lflang.lf.Connection +import org.lflang.lf.Port +import org.lflang.lf.VarRef + +/** A UcConnectionChannel is the fundamental lowest-level representation of a connection in a LF program. + * It connects two UcChannels, one at the source and one at the destination. + */ +class UcConnectionChannel(val src: UcChannel, val dest: UcChannel, val conn: Connection) { + val isFederated = (src.federate != null) && (dest.federate != null) && (src.federate != dest.federate) + + /** Get the NetworkChannelType of this connection. If we are not in a federated program it is NONE */ + fun getChannelType(): NetworkChannelType { + val linkAttr = getLinkAttribute(conn) + return if (linkAttr == null) { + src.federate?.getDefaultInterface()?.type ?: NetworkChannelType.NONE + } else { + val srcIf = linkAttr.getParamString("left") + if (srcIf == null) { + src.federate?.getDefaultInterface()?.type ?: NetworkChannelType.NONE + } else { + src.federate?.getInterface(srcIf)?.type ?: NetworkChannelType.NONE + } + } + } +} + +/** + * A GroupedConnection is a set of ConnectionChannels that can be grouped together for efficiency. + * All ConnectionChannels that start from the same LF port, either because of multiports, banks, or + * multiple connections. Are grouped. + * TODO: Give a better exaplanation for what a GroupedConnection is. + */ +open class UcGroupedConnection( + val src: VarRef, + val channels: List, + val lfConn: Connection, +) { + val delay = lfConn.delay.orNever().toCCode() + val isPhysical = lfConn.isPhysical + val isLogical = !lfConn.isPhysical && lfConn.delay == null + val srcInst = src.container + val srcPort = src.variable as Port + val isDelayed = lfConn.isPhysical || !isLogical // We define physical connections as delayed. + + private var uid: Int = -1 + + val bankWidth = srcInst?.codeWidth ?: 1 + val portWidth = srcPort.width + val numDownstreams = { + val frequencyMap = + channels.groupingBy { Pair(it.src.getCodePortIdx(), it.src.getCodeBankIdx()) }.eachCount() + frequencyMap.values.maxOrNull() ?: 0 + } + val maxNumPendingEvents = 16 // FIXME: Must be derived from the program + + fun assignUid(id: Int) { + uid = id + } + + fun getUniqueName() = "conn_${srcInst?.name ?: ""}_${srcPort.name}_${uid}" +} + +/** + * In federated programs, we must group connections differently. For a non federated program. + * A single output port connected to N different input ports could be grouped to a single GroupedConnection. + * For a federated program, we would need N GroupedConnections if an output port goes to N different federates. + * Moreover, if there are several kinds of NetworkChannels in the program, then we can only group connections + * transported over the same channels. + */ +class UcFederatedGroupedConnection( + src: VarRef, + channels: List, + lfConn: Connection, + val srcFed: UcFederate, + val destFed: UcFederate, +) : UcGroupedConnection(src, channels, lfConn) { + + // FIXME: Allow user to override and provide these. + val serializeFunc = "serialize_payload_default" + val deserializeFunc = "deserialize_payload_default" +} + +/** + * A FederatedConnectionBundle will contain all GroupedConnections going between two federates, in either direction. + * It also contains a NetworkChannel connecting and NetworkEndpoint in each federate. + */ +class UcFederatedConnectionBundle( + val src: UcFederate, + val dest: UcFederate, + val groupedConnections: List +) { + val networkChannel: UcNetworkChannel = UcNetworkChannel.createNetworkEndpointsAndChannelForBundle(this) + + fun numOutputs(federate: UcFederate) = groupedConnections.count { it.srcFed == federate } + + fun numInputs(federate: UcFederate) = groupedConnections.count { it.destFed == federate } + + fun generateNetworkChannelCtor(federate: UcFederate): String = + if (federate == src) { + networkChannel.generateChannelCtorSrc() + } else { + networkChannel.generateChannelCtorDest() + } +} + +/** + * An UcChannel represents a single channel of an LF Port. Due to Multiports and Banks, each LF Port can have multiple channels. + */ +class UcChannel(val varRef: VarRef, val portIdx: Int, val bankIdx: Int, val federate: UcFederate?) { + fun getCodePortIdx() = portIdx + fun getCodeBankIdx() = if (federate == null) bankIdx else 0 + + private val portOfContainedReactor = varRef.container != null + private val reactorInstance = + if (portOfContainedReactor) "${varRef.container.name}[${getCodeBankIdx()}]." else "" + private val portInstance = "${varRef.name}[${getCodePortIdx()}]" + + fun generateChannelPointer() = "&self->${reactorInstance}${portInstance}" +} + +/** + * This is a convenience-wrapper around a LF Port. It will construct + * UcChannels corresponding to the multiports and banks. + * + * If this is a federates program. it must be passed a list of federates associated with the LF Port. + * It is a list in case it is a bank. Then each federate of the bank must be passed to the constructor. + */ +class UcChannelQueue(varRef: VarRef, federates: List) { + private val bankWidth = varRef.container?.width ?: 1 + private val portWidth = (varRef.variable as Port).width + private val isInterleaved = varRef.isInterleaved + /** A queue of UcChannels that can be popped of as we create UcConnetions */ + private val channels = ArrayDeque() + + // Construct the stack of channels belonging to this port. If this port is interleaved, + // then we create channels first for ports then for banks. + init { + if (isInterleaved) { + for (i in 0.. = takeChannels(channels.size) + + // Get a number of channels from this port. This has sideeffects and will remove these + // channels from the port. + fun takeChannels(numChannels: Int): List { + assert(numChannels >= channels.size) + val res = mutableListOf() + for (i in 1..numChannels) { + res.add(channels.removeFirst()) + } + return res + } + + fun channelsLeft(): Int = channels.size +} diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcExtensions.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcExtensions.kt index 8214fd67..879c7f0e 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcExtensions.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcExtensions.kt @@ -1,10 +1,7 @@ package org.lflang.generator.uc import org.lflang.* -import org.lflang.lf.BuiltinTriggerRef -import org.lflang.lf.Expression -import org.lflang.lf.TriggerRef -import org.lflang.lf.VarRef +import org.lflang.lf.* fun TimeValue.toCCode() = UcTypes.getTargetTimeExpr(this) fun Expression.toCCode(inferredType: InferredType? = null): String = @@ -26,3 +23,6 @@ val TriggerRef.name: String is BuiltinTriggerRef -> type.literal else -> unreachable() } + +fun Attribute.getParamString(param: String): String? = attrParms.find {it.name == param}?.value?.trim('"') +fun Attribute.getParamInt(param: String): Int? = attrParms.find {it.name == param}?.value?.toInt() diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFederate.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFederate.kt new file mode 100644 index 00000000..e729eb9c --- /dev/null +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFederate.kt @@ -0,0 +1,33 @@ +package org.lflang.generator.uc + +import org.lflang.generator.uc.UcInstanceGenerator.Companion.codeTypeFederate +import org.lflang.isBank +import org.lflang.lf.Instantiation + +class UcFederate(val inst: Instantiation, val bankIdx: Int) { + val isBank = inst.isBank + private val interfaces = mutableListOf() + + val codeType = if (isBank) "${inst.codeTypeFederate}_${bankIdx}" else inst.codeTypeFederate + + fun addInterface(iface: UcNetworkInterface) { + interfaces.add(iface) + } + + fun getInterface(name: String): UcNetworkInterface = + interfaces.find { it.name == name }!! + + fun getDefaultInterface(): UcNetworkInterface = + interfaces.first() + + fun getCompileDefs(): List = interfaces.distinctBy { it.type }.map { it.compileDefs } + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other !is UcFederate) return false + + val sameInst = inst == other.inst + val sameBank = bankIdx == other.bankIdx + return if (isBank) sameInst && sameBank else sameInst + } +} diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFederateGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFederateGenerator.kt index 3e50e564..519f6146 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFederateGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFederateGenerator.kt @@ -1,61 +1,67 @@ package org.lflang.generator.uc -import org.lflang.MessageReporter +import org.lflang.* import org.lflang.generator.PrependOperator import org.lflang.lf.* -import org.lflang.reactor -import org.lflang.toUnixString -class UcFederateGenerator(private val federate: Instantiation, fileConfig: UcFileConfig, messageReporter: MessageReporter) { - private val container = federate.eContainer() as Reactor - private val reactor = federate.reactor - private val connections = UcConnectionGenerator(container) - private val parameters = UcParameterGenerator(container) +class UcFederateGenerator(private val currentFederate: UcFederate, private val otherFederates: List, private val fileConfig: UcFileConfig, messageReporter: MessageReporter) { + + private val container = currentFederate.inst.eContainer() as Reactor + private val reactor = currentFederate.inst.reactor + private val connections = UcConnectionGenerator(container, currentFederate, otherFederates) + private val parameters = UcParameterGenerator(container, currentFederate) private val ports = UcPortGenerator(container, connections) private val reactions = UcReactionGenerator(container) private val instances = UcInstanceGenerator(container, parameters, ports, connections, reactions, fileConfig, messageReporter) - private val headerFile = fileConfig.getReactorHeaderPath(federate.reactor).toUnixString() - - fun numBundles() = 1 + private val headerFile = "lf_federate.h" + private val includeGuard = "LFC_GEN_FEDERATE_${currentFederate.inst.name.uppercase()}_H" - private val Reactor.codeType - get(): String = "federate_${federate.name}" + fun getMaxNumPendingEvents(): Int { + return connections.getMaxNumPendingEvents() + } private fun generateFederateStruct() = with(PrependOperator) { """ |typedef struct { | Reactor super; - ${" | "..instances.generateReactorStructField(federate)} + ${" | "..instances.generateReactorStructField(currentFederate.inst)} ${" | "..connections.generateReactorStructFields()} - | LF_FEDERATE_BOOKKEEPING_INSTANCES(${numBundles()}); - |} ${reactor.codeType}; + ${" | "..connections.generateFederateStructFields()} + | LF_FEDERATE_BOOKKEEPING_INSTANCES(${connections.getNumFederatedConnectionBundles()}); + |} ${currentFederate.codeType}; | """.trimMargin() } private fun generateCtorDefinition() = with(PrependOperator) { """ - |LF_REACTOR_CTOR_SIGNATURE(${reactor.codeType}) { + |${generateCtorDeclaration()} { | LF_FEDERATE_CTOR_PREAMBLE(); - | LF_REACTOR_CTOR(${reactor.codeType}); - ${" | "..instances.generateReactorCtorCode(federate)} + | LF_REACTOR_CTOR(${currentFederate.codeType}); + ${" | "..instances.generateReactorCtorCode(currentFederate.inst)} + ${" | "..connections.generateFederateCtorCodes()} ${" | "..connections.generateReactorCtorCodes()} |} | """.trimMargin() } + private fun generateCtorDeclaration() = "LF_REACTOR_CTOR_SIGNATURE(${currentFederate.codeType})" + fun generateHeader() = with(PrependOperator) { """ + |#ifndef ${includeGuard} + |#define ${includeGuard} |#include "reactor-uc/reactor-uc.h" + |#include "${fileConfig.getReactorHeaderPath(reactor).toUnixString()}" + ${" |"..connections.generateNetworkChannelIncludes()} | - ${" |"..instances.generateIncludes()} - ${" |"..reactions.generateSelfStructs()} - ${" |"..ports.generateSelfStructs()} + ${" |"..connections.generateFederatedSelfStructs()} ${" |"..connections.generateSelfStructs()} - |//The reactor self struct - | + ${" |"..generateFederateStruct()} + ${" |"..generateCtorDeclaration()}; + |#endif // ${includeGuard} """.trimMargin() } @@ -63,9 +69,7 @@ class UcFederateGenerator(private val federate: Instantiation, fileConfig: UcFil """ |#include "${headerFile}" | - ${" |"..reactions.generateReactionBodies()} - ${" |"..reactions.generateReactionCtors()} - ${" |"..ports.generateCtors()} + ${" |"..connections.generateFederatedCtors()} ${" |"..connections.generateCtors()} ${" |"..generateCtorDefinition()} | diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFederatedLaunchScriptGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFederatedLaunchScriptGenerator.kt new file mode 100644 index 00000000..2709d7c0 --- /dev/null +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFederatedLaunchScriptGenerator.kt @@ -0,0 +1,50 @@ +package org.lflang.generator.uc + +import org.lflang.generator.PrependOperator +import org.lflang.joinWithLn + +class UcFederatedLaunchScriptGenerator(private val fileConfig: UcFileConfig) { + private val S = '$' // a little trick to escape the dollar sign with $S + fun generateLaunchScript(federates: List): String = with(PrependOperator) { + """ |#!/bin/env bash + | + |set -m + |shopt -s huponexit + |cleanup() { + | if [ "$S{EXITED_SUCCESSFULLY}" ] ; then + | exit 0 + | else + | printf "Killing federate %s.\n" $S{pids[*]} + | # The || true clause means this is not an error if kill fails. + | kill $S{pids[@]} || true + | exit 1 + | fi + |} + | + |trap 'cleanup; exit' EXIT + | + |# Launch all federates + |pids=() + ${" |"..federates.joinWithLn { launchFederate(it) }} + | + |# Wait for all federates to finish + |for pid in "$S{pids[@]}" + |do + | wait $S{pid} || exit $S? + |done + |EXITED_SUCCESSFULLY=true + """.trimMargin() + } + + private fun launchFederate(federate: UcFederate) = + """ + |echo "#### Launching federate ${federate.codeType}" + |if [ "${S}1" = "-l" ]; then + | ${fileConfig.binPath}/${federate.codeType} | tee ${federate.codeType}.log & + |else + | ${fileConfig.binPath}/${federate.codeType} & + |fi + |pids+=($S!) + | + """.trimMargin() +} \ No newline at end of file diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFileConfig.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFileConfig.kt index 0fa246bc..b8ea6b4c 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFileConfig.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFileConfig.kt @@ -2,6 +2,8 @@ package org.lflang.generator.uc import org.eclipse.emf.ecore.resource.Resource import org.lflang.FileConfig +import org.lflang.generator.uc.UcInstanceGenerator.Companion.codeTypeFederate +import org.lflang.lf.Instantiation import org.lflang.lf.Reactor import org.lflang.name import org.lflang.util.FileUtil @@ -24,16 +26,12 @@ class UcFileConfig(resource: Resource, srcGenBasePath: Path, useHierarchicalBin: /** Relative path to the directory where all source files for this resource should be generated in. */ private fun getGenDir(r: Resource): Path = this.getDirectory(r).resolve(r.name) - /** Path to the header file corresponding to this reactor */ - fun getReactorHeaderPath(r: Reactor): Path = getGenDir(r.eResource()).resolve("${r.name}.h") - /** Path to the header file with preambles defined for this reactor */ fun getPreambleHeaderPath(r: Resource): Path = getGenDir(r).resolve("_lf_preamble.h") - /** Path to the source file corresponding to this reactor (needed for non generic reactors) */ fun getReactorSourcePath(r: Reactor): Path = getGenDir(r.eResource()).resolve("${r.name}.c") - /** Path to the build directory containing CMake-generated files */ - val buildPath: Path get() = this.outPath.resolve("build") + /** Path to the header file corresponding to this reactor */ + fun getReactorHeaderPath(r: Reactor): Path = getGenDir(r.eResource()).resolve("${r.name}.h") } \ No newline at end of file diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcGenerator.kt index 88bb2b26..c306d128 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcGenerator.kt @@ -1,28 +1,37 @@ package org.lflang.generator.uc +import org.apache.commons.lang3.tuple.MutablePair +import org.eclipse.emf.ecore.EObject import org.eclipse.emf.ecore.resource.Resource +import org.eclipse.xtext.xbase.lib.IteratorExtensions +import org.lflang.allInstantiations +import org.lflang.allReactions import org.lflang.generator.* -import org.lflang.generator.GeneratorUtils.canGenerate -import org.lflang.generator.LFGeneratorContext.Mode -import org.lflang.isGeneric +import org.lflang.generator.uc.UcInstanceGenerator.Companion.width +import org.lflang.generator.uc.UcReactorGenerator.Companion.hasStartup import org.lflang.lf.Instantiation import org.lflang.lf.Reactor import org.lflang.reactor import org.lflang.scoping.LFGlobalScopeProvider import org.lflang.target.Target import org.lflang.target.property.* -import org.lflang.target.property.type.PlatformType -import org.lflang.util.FileUtil -import java.io.IOException -import java.nio.file.Files import java.nio.file.Path +/** Creates either a Federated or NonFederated generator depending on the type of LF program */ +fun createUcGenerator(context: LFGeneratorContext, scopeProvider: LFGlobalScopeProvider): UcGenerator { + val nodes: Iterable = IteratorExtensions.toIterable(context.getFileConfig().resource.getAllContents()); + for (reactor in nodes.filterIsInstance()) { + if (reactor.isFederated) { + return UcGeneratorFederated(context, scopeProvider) + } + } + return UcGeneratorNonFederated(context, scopeProvider) +} + @Suppress("unused") -class UcGenerator( - val context: LFGeneratorContext, - private val scopeProvider: LFGlobalScopeProvider -) : - GeneratorBase(context) { +abstract class UcGenerator( + val context: LFGeneratorContext, protected val scopeProvider: LFGlobalScopeProvider +) : GeneratorBase(context) { // keep a list of all source files we generate val ucSources = mutableListOf() @@ -31,124 +40,74 @@ class UcGenerator( val fileConfig: UcFileConfig = context.fileConfig as UcFileConfig val platform = targetConfig.get(PlatformProperty.INSTANCE) - companion object { - const val libDir = "/lib/c" - const val MINIMUM_CMAKE_VERSION = "3.5" - } - - // Returns a possibly empty list of the federates in the current program - private fun getAllFederates(): List { - val res = mutableListOf() - for (reactor in reactors) { - if (reactor.isFederated) { - res.addAll(reactor.instantiations) - } + // Contains the maximum number of pending events required by each reactor. + // Is updated as reactors are analyzed and code-generated. + val maxNumPendingEvents = mutableMapOf() + + // Compute the total number of events and reactions within an instance (and its children) + // Also returns whether there is any startup event within the instance. + private fun totalNumEventsAndReactions(inst: Instantiation): Triple { + var numEvents = 0 + var numReactions = 0 + var hasStartup = false + val remaining = mutableListOf() + remaining.addAll(inst.reactor.allInstantiations) + while (remaining.isNotEmpty()) { + val child = remaining.removeFirst() + val childRes = totalNumEventsAndReactions(child) + + numEvents += childRes.first * child.width + numReactions += childRes.second * child.width + hasStartup = hasStartup or childRes.third } - return res + numEvents += maxNumPendingEvents[inst.reactor]!! + numReactions += inst.reactor.allReactions.size + hasStartup = hasStartup or inst.reactor.hasStartup + return Triple(numEvents, numReactions, hasStartup) } - fun doGenerateTopLevel(resource: Resource, context: LFGeneratorContext, srcGenPath: Path) { - if (!canGenerate(errorsOccurred(), mainDef, messageReporter, context)) return - - // create a platform-specific generator - val platformGenerator: UcPlatformGenerator = getPlatformGenerator(srcGenPath) - - // generate all core files - generateFiles(srcGenPath, getAllImportedResources(resource)) - - // generate platform specific files - platformGenerator.generatePlatformFiles() - - // We only invoke CMake on the generated sources when we are targeting POSIX. If not - // it is the users responsibility to integrate this into the build system of the target - // platform. - if (platform.platform != PlatformType.Platform.NATIVE) { - println("Exiting before invoking target compiler.") - context.finish(GeneratorResult.GENERATED_NO_EXECUTABLE.apply(context, codeMaps)) - } else if (context.mode == Mode.LSP_MEDIUM) { - context.reportProgress( - "Code generation complete. Validating generated code...", IntegratedBuilder.GENERATED_PERCENT_PROGRESS - ) - if (platformGenerator.doCompile(context)) { - UcValidator(fileConfig, messageReporter, codeMaps).doValidate(context) - context.finish(GeneratorResult.GENERATED_NO_EXECUTABLE.apply(context, codeMaps)) - } else { - context.unsuccessfulFinish() - } - } else { - context.reportProgress( - "Code generation complete. Compiling...", IntegratedBuilder.GENERATED_PERCENT_PROGRESS - ) - if (platformGenerator.doCompile(context)) { - context.finish(GeneratorResult.Status.COMPILED, codeMaps) - } else { - context.unsuccessfulFinish() - } + // Compute the total number of events and reactions for a top-level reactor. + fun totalNumEventsAndReactions(main: Reactor): Pair { + val res = MutablePair(maxNumPendingEvents[main]!!, main.allReactions.size) + var hasStartup = main.hasStartup + for (inst in main.allInstantiations) { + val childRes = totalNumEventsAndReactions(inst) + res.left += childRes.first * inst.width + res.right += childRes.second * inst.width + hasStartup = hasStartup or childRes.third } + if (hasStartup) res.left += 1 + return res.toPair() } - override fun doGenerate(resource: Resource, context: LFGeneratorContext) { - super.doGenerate(resource, context) - - val federates = getAllFederates(); - if (federates.isEmpty()) { - doGenerateTopLevel(resource, context, fileConfig.srcGenPath) - } else { - for (federate in federates) { - mainDef = federate - doGenerateTopLevel(resource, context, fileConfig.srcGenPath.resolve(federate.name)) - } - } - + companion object { + const val libDir = "/lib/c" + const val MINIMUM_CMAKE_VERSION = "3.5" } - /** - * Copy the contents of the entire src-gen directory to a nested src-gen directory next to the generated Dockerfile. - */ - private fun copySrcGenBaseDirIntoDockerDir() { - FileUtil.deleteDirectory(context.fileConfig.srcGenPath.resolve("src-gen")) - try { - // We need to copy in two steps via a temporary directory, as the target directory - // is located within the source directory. Without the temporary directory, copying - // fails as we modify the source while writing the target. - val tempDir = Files.createTempDirectory(context.fileConfig.outPath, "src-gen-directory") - try { - FileUtil.copyDirectoryContents(context.fileConfig.srcGenBasePath, tempDir, false) - FileUtil.copyDirectoryContents(tempDir, context.fileConfig.srcGenPath.resolve("src-gen"), false) - } catch (e: IOException) { - context.errorReporter.nowhere() - .error("Failed to copy sources to make them accessible to Docker: " + if (e.message == null) "No cause given" else e.message) - e.printStackTrace() - } finally { - FileUtil.deleteDirectory(tempDir) - } - if (errorsOccurred()) { - return + // Returns a possibly empty list of the federates in the current program. + protected fun getAllFederates(): List { + val res = mutableListOf() + for (reactor in reactors) { + if (reactor.isFederated) { + res.addAll(reactor.allInstantiations) } - } catch (e: IOException) { - context.errorReporter.nowhere().error("Failed to create temporary directory.") - e.printStackTrace() } + return res } - private fun fetchReactorUc(version: String) { - val libPath = fileConfig.srcGenBasePath.resolve("reactor-uc") - // abort if the directory already exists - if (Files.isDirectory(libPath)) { - return + // Returns a list of all instantiated reactors within a top-level reactor. + protected fun getAllInstantiatedReactors(top: Reactor): List { + val res = mutableListOf() + for (inst in top.allInstantiations) { + res.add(inst.reactor) + res.addAll(getAllInstantiatedReactors(inst.reactor)) } - // clone the reactor-cpp repo and fetch the specified version - Files.createDirectories(libPath) - commandFactory.createCommand( - "git", - listOf("clone", "-n", "https://github.com/lf-lang/reactor-uc.git", "reactor-uc-$version"), - fileConfig.srcGenBasePath - ).run() - commandFactory.createCommand("git", listOf("checkout", version), libPath).run() + return res.distinct() } - private fun getAllImportedResources(resource: Resource): Set { + protected fun getAllImportedResources(resource: Resource): Set { val resources: MutableSet = scopeProvider.getImportedResources(resource) val importedRresources = resources.subtract(setOf(resource)) resources.addAll(importedRresources.map { getAllImportedResources(it) }.flatten()) @@ -156,37 +115,6 @@ class UcGenerator( return resources } - private fun generateFiles(srcGenPath: Path, resources: Set) { - // generate header and source files for all reactors - for (r in reactors.filterNot {it.isFederated}) { - val generator = UcReactorGenerator(r, fileConfig, messageReporter) - val headerFile = fileConfig.getReactorHeaderPath(r) - val sourceFile = fileConfig.getReactorSourcePath(r) - val reactorCodeMap = CodeMap.fromGeneratedCode(generator.generateSource()) - if (!r.isGeneric) - ucSources.add(sourceFile) - codeMaps[srcGenPath.resolve(sourceFile)] = reactorCodeMap - val headerCodeMap = CodeMap.fromGeneratedCode(generator.generateHeader()) - codeMaps[srcGenPath.resolve(headerFile)] = headerCodeMap - - FileUtil.writeToFile(headerCodeMap.generatedCode, srcGenPath.resolve(headerFile), true) - FileUtil.writeToFile(reactorCodeMap.generatedCode, srcGenPath.resolve(sourceFile), true) - } - - - for (r in resources) { - val generator = UcPreambleGenerator(r, fileConfig, scopeProvider) - val headerFile = fileConfig.getPreambleHeaderPath(r); - val preambleCodeMap = CodeMap.fromGeneratedCode(generator.generateHeader()) - codeMaps[srcGenPath.resolve(headerFile)] = preambleCodeMap - FileUtil.writeToFile(preambleCodeMap.generatedCode, srcGenPath.resolve(headerFile), true) - } - } - - private fun getPlatformGenerator(srcGenPath: Path) = UcStandaloneGenerator(this, srcGenPath) - override fun getTarget() = Target.UC - override fun getTargetTypes(): TargetTypes = UcTypes - } diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcGeneratorFederated.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcGeneratorFederated.kt new file mode 100644 index 00000000..ab72af76 --- /dev/null +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcGeneratorFederated.kt @@ -0,0 +1,160 @@ +package org.lflang.generator.uc + +import org.eclipse.emf.ecore.resource.Resource +import org.lflang.generator.CodeMap +import org.lflang.generator.GeneratorResult +import org.lflang.generator.GeneratorUtils.canGenerate +import org.lflang.generator.LFGeneratorContext +import org.lflang.generator.uc.UcInstanceGenerator.Companion.width +import org.lflang.lf.LfFactory +import org.lflang.lf.Reactor +import org.lflang.reactor +import org.lflang.scoping.LFGlobalScopeProvider +import org.lflang.target.property.type.PlatformType +import org.lflang.util.FileUtil +import java.nio.file.Path +import java.nio.file.Paths + +class UcGeneratorFederated( + context: LFGeneratorContext, scopeProvider: LFGlobalScopeProvider +) : UcGenerator(context, scopeProvider) { + + private val nonFederatedGenerator = UcGeneratorNonFederated(context, scopeProvider) + val federates = mutableListOf() + + // Compute the total number of events and reactions within a federate. This reuses the + // computation for the non-federated case, but federated connections are also considered. + fun totalNumEventsAndReactionsFederated(federate: UcFederate): Pair { + val eventsFromFederatedConnections = maxNumPendingEvents[mainDef.reactor]!! + val eventsAndReactionsInFederate = nonFederatedGenerator.totalNumEventsAndReactions(federate.inst.reactor) + return Pair( + eventsFromFederatedConnections + eventsAndReactionsInFederate.first, + eventsAndReactionsInFederate.second + ) + } + + private fun doGenerateFederate( + resource: Resource, + context: LFGeneratorContext, + srcGenPath: Path, + federate: UcFederate + ): GeneratorResult.Status { + if (!canGenerate(errorsOccurred(), federate.inst, messageReporter, context)) return GeneratorResult.Status.FAILED + + // generate header and source files for all reactors + getAllInstantiatedReactors(federate.inst.reactor).map { + nonFederatedGenerator.generateReactorFiles( + it, + srcGenPath + ) + } + + generateFederateFiles(federate, srcGenPath) + + // Collect the info on the generated sources + ucSources.addAll(nonFederatedGenerator.ucSources) + codeMaps.putAll(nonFederatedGenerator.codeMaps) + + for (r in getAllImportedResources(resource)) { + val generator = UcPreambleGenerator(r, fileConfig, scopeProvider) + val headerFile = fileConfig.getPreambleHeaderPath(r) + val preambleCodeMap = CodeMap.fromGeneratedCode(generator.generateHeader()) + codeMaps[srcGenPath.resolve(headerFile)] = preambleCodeMap + FileUtil.writeToFile(preambleCodeMap.generatedCode, srcGenPath.resolve(headerFile), true) + } + return GeneratorResult.Status.GENERATED + } + + // The same UcGeneratorFederated is used to iteratively generated a project for each federate. + // After generating we need to clear certain state variables before starting with the next federate. + private fun clearStateFromPreviousFederate() { + codeMaps.clear() + ucSources.clear() + maxNumPendingEvents.clear() + nonFederatedGenerator.maxNumPendingEvents.clear() + nonFederatedGenerator.codeMaps.clear() + nonFederatedGenerator.ucSources.clear() + } + + // This function creates an instantiation for the top-level main reactor. + private fun createMainDef() { + for (reactor in reactors) { + if (reactor.isFederated) { // Note that this will be the "main" top-level reactor. Not each federate. + this.mainDef = LfFactory.eINSTANCE.createInstantiation() + this.mainDef.name = reactor.name + this.mainDef.reactorClass = reactor + } + } + } + + override fun doGenerate(resource: Resource, context: LFGeneratorContext) { + super.doGenerate(resource, context) + createMainDef() + for (inst in getAllFederates()) { + for (bankIdx in 0.. { + val octets = address.address.split(".") + val lastOctet = octets.last().toInt() + require(lastOctet < (255 - count)) { "Cannot increment the last octet of an IPv4 address beyond 255" } + IPv4(octets.dropLast(1).joinToString(".") + "." + (lastOctet + count)) + } + + is IPv6 -> { + val segments = address.address.split(":") + val lastSegment = BigInteger(segments.last(), 16) + val incremented = lastSegment + count.toBigInteger() + require( + incremented <= BigInteger( + "FFFF", + 16 + ) + ) { "Cannot increment the last segment of an IPv6 address beyond FFFF" } + IPv6(segments.dropLast(1).joinToString(":") + ":" + incremented.toString(16)) + } + } + } + } +} + + + +class IpPortManager { + private val currentPort = AtomicInteger(1024) // Starting port number + private val usedPorts = mutableSetOf() + + @Synchronized + fun acquirePortNumber(): Int { + while (true) { + val port = currentPort.getAndIncrement() + if (port in 1024..65535 && usedPorts.add(port)) { + return port + } + } + } + + @Synchronized + fun reservePortNumber(port: Int) { + assert(port < 65535) + assert(!usedPorts.contains(port)) + usedPorts.add(port) + } +} + +object IpAddressManager { + private val usedIps = mutableSetOf() + private val portManagers = mutableMapOf() + + @Synchronized + fun acquireIp(ip: IPAddress) { + if (ip != IPAddress.fromString("127.0.0.1")) { + require(!usedIps.contains(ip)) + usedIps.add(ip) + } + } + + fun getPortManager(ip: IPAddress): IpPortManager { + if (portManagers.contains(ip)) { + return portManagers[ip]!! + } else { + val newManager = IpPortManager() + portManagers[ip] = newManager + return newManager + } + } +} +class UcIpAddress { } \ No newline at end of file diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcMainGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcMainGenerator.kt index 4b45066f..462f2869 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcMainGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcMainGenerator.kt @@ -3,36 +3,55 @@ package org.lflang.generator.uc import org.lflang.target.TargetConfig import org.lflang.generator.PrependOperator import org.lflang.generator.uc.UcReactorGenerator.Companion.codeType -import org.lflang.inferredType -import org.lflang.lf.Parameter import org.lflang.lf.Reactor +import org.lflang.reactor import org.lflang.target.property.FastProperty import org.lflang.target.property.KeepaliveProperty -import org.lflang.target.property.PlatformProperty import org.lflang.target.property.TimeOutProperty -import org.lflang.target.property.type.PlatformType import org.lflang.toUnixString -class UcMainGenerator( +abstract class UcMainGenerator(val targetConfig: TargetConfig) { + abstract fun generateStartSource(): String + fun getDuration() = if (targetConfig.isSet(TimeOutProperty.INSTANCE)) targetConfig.get(TimeOutProperty.INSTANCE).toCCode() else "FOREVER" + fun keepAlive() = if(targetConfig.isSet(KeepaliveProperty.INSTANCE)) "true" else "false" + fun fast() = if(targetConfig.isSet(FastProperty.INSTANCE)) "true" else "false" + fun generateStartHeader() = with(PrependOperator) { + """ + |#ifndef REACTOR_UC_LF_MAIN_H + |#define REACTOR_UC_LF_MAIN_H + | + |void lf_start(void); + | + |#endif + | + """.trimMargin() + } + + fun generateMainSource() = with(PrependOperator) { + """ + |#include "lf_start.h" + |int main(void) { + | lf_start(); + | return 0; + |} + """.trimMargin() + } +} +class UcMainGeneratorNonFederated( private val main: Reactor, - private val targetConfig: TargetConfig, + targetConfig: TargetConfig, private val fileConfig: UcFileConfig, -) { +): UcMainGenerator(targetConfig) { private val ucParameterGenerator = UcParameterGenerator(main) - fun getDuration() = if (targetConfig.isSet(TimeOutProperty.INSTANCE)) targetConfig.get(TimeOutProperty.INSTANCE).toCCode() else "FOREVER" - - fun keepAlive() = if(targetConfig.isSet(KeepaliveProperty.INSTANCE)) "true" else "false" - - fun fast() = if(targetConfig.isSet(FastProperty.INSTANCE)) "true" else "false" - - fun generateStartSource() = with(PrependOperator) { + override fun generateStartSource() = with(PrependOperator) { """ |#include "reactor-uc/reactor-uc.h" |#include "${fileConfig.getReactorHeaderPath(main).toUnixString()}" |static ${main.codeType} main_reactor; |static Environment lf_environment; + |Environment *_lf_environment = &lf_environment; |void lf_exit(void) { | Environment_free(&lf_environment); |} @@ -48,24 +67,39 @@ class UcMainGenerator( |} """.trimMargin() } +} +class UcMainGeneratorFederated( + private val currentFederate: UcFederate, + private val otherFederates: List, + targetConfig: TargetConfig, + private val fileConfig: UcFileConfig, +): UcMainGenerator(targetConfig) { - fun generateStartHeader() = with(PrependOperator) { - """ - |#ifndef REACTOR_UC_LF_MAIN_H - |#define REACTOR_UC_LF_MAIN_H - | - |void lf_start(void); - | - |#endif - | - """.trimMargin() - } - - fun generateMainSource() = with(PrependOperator) { + private val top = currentFederate.inst.eContainer() as Reactor + private val ucConnectionGenerator = UcConnectionGenerator(top, currentFederate, otherFederates) + override fun generateStartSource() = with(PrependOperator) { """ - |#include "lf_start.h" - |int main(void) { - | lf_start(); + |#include "reactor-uc/reactor-uc.h" + |#include "lf_federate.h" + |static ${currentFederate.codeType} main_reactor; + |static Environment lf_environment; + |Environment *_lf_environment = &lf_environment; + |void lf_exit(void) { + | Environment_free(&lf_environment); + |} + |void lf_start(void) { + | Environment_ctor(&lf_environment, (Reactor *)&main_reactor); + | lf_environment.scheduler->duration = ${getDuration()}; + | lf_environment.scheduler->keep_alive = ${keepAlive()}; + | lf_environment.scheduler->leader = ${top.instantiations.first() == currentFederate.inst && currentFederate.bankIdx == 0}; + | lf_environment.fast_mode = ${fast()}; + | lf_environment.has_async_events = ${currentFederate.inst.reactor.inputs.isNotEmpty()}; + | ${currentFederate.codeType}_ctor(&main_reactor, NULL, &lf_environment); + | lf_environment.net_bundles_size = ${ucConnectionGenerator.getNumFederatedConnectionBundles()}; + | lf_environment.net_bundles = (FederatedConnectionBundle **) &main_reactor._bundles; + | lf_environment.assemble(&lf_environment); + | lf_environment.start(&lf_environment); + | lf_exit(); |} """.trimMargin() } diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcMakeGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcMakeGenerator.kt index 7b73a834..95509554 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcMakeGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcMakeGenerator.kt @@ -3,29 +3,39 @@ package org.lflang.generator.uc import org.lflang.FileConfig import org.lflang.target.TargetConfig import org.lflang.generator.PrependOperator -import org.lflang.generator.uc.UcReactorGenerator.Companion.getEventQueueSize -import org.lflang.generator.uc.UcReactorGenerator.Companion.getReactionQueueSize import org.lflang.joinWithLn import org.lflang.lf.Reactor -import org.lflang.target.property.BuildTypeProperty import org.lflang.toUnixString import java.nio.file.Path -import java.time.LocalDateTime import kotlin.io.path.name import kotlin.math.max -class UcMakeGenerator(private val main: Reactor, private val targetConfig: TargetConfig, private val fileConfig: FileConfig) { - private val S = '$' // a little trick to escape the dollar sign with $S - fun generateMake(sources: List) = with(PrependOperator) { - val sources = sources.filterNot { it.name=="lf_main.c" } +abstract class UcMakeGenerator(private val mainTarget: String, private val numEvents: Int, private val numReactions: Int) { + abstract fun generateMake(sources: List): String + protected val S = '$' // a little trick to escape the dollar sign with $S + fun doGenerateMake(sources: List, compileDefs: List) = with(PrependOperator) { + val sources = sources.filterNot { it.name == "lf_main.c" } """ - | # Makefile generated for ${fileConfig.name} + | # Makefile generated for ${mainTarget} |LFC_GEN_SOURCES = \ - ${" | "..sources.joinWithLn { it.toUnixString() + if (it != sources.last()) " \\" else ""}} + ${" | "..sources.joinWithLn { it.toUnixString() + if (it != sources.last()) " \\" else "" }} |LFC_GEN_MAIN = lf_main.c - |REACTION_QUEUE_SIZE = ${max(main.getReactionQueueSize(), 1)} - |EVENT_QUEUE_SIZE = ${max(main.getEventQueueSize(), 1)} + |LFC_GEN_COMPILE_DEFS = \ + ${" | "..compileDefs.joinWithLn { it + if (it != compileDefs.last()) " \\" else "" }} + |REACTION_QUEUE_SIZE = ${max(numReactions, 1)} + |EVENT_QUEUE_SIZE = ${max(numEvents, 2)} | """.trimMargin() } } + +class UcMakeGeneratorNonFederated(private val main: Reactor, private val targetConfig: TargetConfig, private val fileConfig: FileConfig, numEvents: Int, numReactions: Int) + : UcMakeGenerator(fileConfig.name, numEvents, numReactions) { + override fun generateMake(sources: List) = doGenerateMake(sources, emptyList()) + +} + +class UcMakeGeneratorFederated(private val federate: UcFederate, targetConfig: TargetConfig, fileConfig: UcFileConfig, numEvents: Int, numReactions: Int) + : UcMakeGenerator(federate.codeType, numEvents, numReactions) { + override fun generateMake(sources: List) = doGenerateMake(sources, federate.getCompileDefs()) +} diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcNetworkChannel.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcNetworkChannel.kt new file mode 100644 index 00000000..f7bcc613 --- /dev/null +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcNetworkChannel.kt @@ -0,0 +1,279 @@ +package org.lflang.generator.uc + +import org.lflang.AttributeUtils.getInterfaceAttributes +import org.lflang.AttributeUtils.getLinkAttribute +import org.lflang.generator.uc.NetworkChannelType.* +import org.lflang.lf.Attribute + +// An enumeration of the supported NetworkChannels +enum class NetworkChannelType { + TCP_IP, CUSTOM, COAP_UDP_IP, NONE +} + + +object UcNetworkInterfaceFactory { + private val creators: Map UcNetworkInterface> = + mapOf( + Pair(TCP_IP) { federate, attr -> UcTcpIpInterface.fromAttribute(federate, attr) }, + Pair(COAP_UDP_IP) { federate, attr -> UcCoapUdpIpInterface.fromAttribute(federate, attr) }, + Pair(CUSTOM) { federate, attr -> UcCustomInterface.fromAttribute(federate, attr) } + ) + + fun createInterfaces(federate: UcFederate): List { + val attrs: List = getInterfaceAttributes(federate.inst) + return if (attrs.isEmpty()) { + listOf(createDefaultInterface()) + } else { + attrs.map { createInterfaceFromAttribute(federate, it) } + } + } + + private fun createInterfaceFromAttribute(federate: UcFederate, attr: Attribute): UcNetworkInterface { + val protocol = attr.attrName.substringAfter("_") + return when (protocol) { + "tcp" -> creators.get(TCP_IP)!!.invoke(federate, attr) + "coap" -> creators.get(COAP_UDP_IP)!!.invoke(federate, attr) + "custom" -> creators.get(CUSTOM)!!.invoke(federate, attr) + else -> throw IllegalArgumentException("Unrecognized interface attribute $attr") + } + } + + private fun createDefaultInterface(): UcNetworkInterface = UcTcpIpInterface(ipAddress = IPAddress.fromString("127.0.0.1")) +} + +// A NetworkEndpoint is a communication endpoint located at the UcNetworkInterface of a federate. +// A NetworkChannel is between two NetworkEndpoints. +abstract class UcNetworkEndpoint(val iface: UcNetworkInterface) +class UcTcpIpEndpoint(val ipAddress: IPAddress, val port: Int, iface: UcTcpIpInterface) : UcNetworkEndpoint(iface) {} +class UcCoapUdpIpEndpoint(val ipAddress: IPAddress, iface: UcCoapUdpIpInterface) : UcNetworkEndpoint(iface) {} +class UcCustomEndpoint(iface: UcCustomInterface) : UcNetworkEndpoint(iface) {} + +// A federate can have several NetworkInterfaces, which are specified using attributes in the LF program. +// A NetworkInterface has a name and can contain a set of endpoints. +abstract class UcNetworkInterface(val type: NetworkChannelType, val name: String) { + val endpoints = mutableListOf() + + /** A header file that should be included to support this NetworkInterface. Used by CustomInterface */ + abstract val includeHeaders: String + + /** A compile definition which must be defined to get support for this NetworkInterface*/ + abstract val compileDefs: String +} + +class UcTcpIpInterface(private val ipAddress: IPAddress, name: String? = null) : + UcNetworkInterface(TCP_IP, name ?: "tcp") { + private val portManager = IpAddressManager.getPortManager(ipAddress) + override val includeHeaders: String = "" + override val compileDefs: String = "NETWORK_CHANNEL_TCP_POSIX" + + fun createEndpoint(port: Int?): UcTcpIpEndpoint { + val portNum = if (port != null) { + portManager.reservePortNumber(port) + port + } else { + portManager.acquirePortNumber() + } + val ep = UcTcpIpEndpoint(ipAddress, portNum, this) + endpoints.add(ep) + return ep + } + + companion object { + fun fromAttribute(federate: UcFederate, attr: Attribute): UcTcpIpInterface { + val address = attr.getParamString("address") + val name = attr.getParamString("name") + val ip = if (address != null) { + var address = IPAddress.fromString(address) + + if (federate.isBank) { + address = IPAddress.increment(address, federate.bankIdx - 1) + } + address + } else { + IPAddress.fromString("127.0.0.1") + } + IpAddressManager.acquireIp(ip) + return UcTcpIpInterface(ip, name) + } + } +} + +class UcCoapUdpIpInterface(private val ipAddress: IPAddress, name: String? = null) : + UcNetworkInterface(COAP_UDP_IP, name ?: "coap") { + override val includeHeaders: String = "" + override val compileDefs: String = "NETWORK_CHANNEL_COAP_UDP" + + fun createEndpoint(): UcCoapUdpIpEndpoint { + val ep = UcCoapUdpIpEndpoint(ipAddress, this) + endpoints.add(ep) + return ep + } + + companion object { + fun fromAttribute(federate: UcFederate, attr: Attribute): UcCoapUdpIpInterface { + val address = attr.getParamString("address") + val name = attr.getParamString("name") + val ip = if (address != null) { + var address = IPAddress.fromString(address) + + if (federate.isBank) { + address = IPAddress.increment(address, federate.bankIdx - 1) + } + address + } else { + IPAddress.fromString("127.0.0.1") + } + IpAddressManager.acquireIp(ip) + return UcCoapUdpIpInterface(ip, name) + } + } +} + +class UcCustomInterface(name: String, val include: String, val args: String? = null) : + UcNetworkInterface(CUSTOM, name) { + override val compileDefs = "" + override val includeHeaders: String = "#include \"$include\"" + + fun createEndpoint(): UcCustomEndpoint { + val ep = UcCustomEndpoint(this) + endpoints.add(ep) + return ep + } + + companion object { + fun fromAttribute(federate: UcFederate, attr: Attribute): UcCustomInterface { + val name = attr.getParamString("name") + val include = attr.getParamString("include") + val args = attr.getParamString("args") + return UcCustomInterface(name!!, include!!, args) + } + } +} + +/** A UcNetworkChannel is created by giving two endpoints and deciding which one is the server */ +abstract class UcNetworkChannel( + val type: NetworkChannelType, + val src: UcNetworkEndpoint, + val dest: UcNetworkEndpoint, + val serverLhs: Boolean, +) { + /** Generate code calling the constructor of the source endpoint */ + abstract fun generateChannelCtorSrc(): String + /** Generate code calling the constructor of the destination endpoint */ + abstract fun generateChannelCtorDest(): String + abstract val codeType: String + + companion object { + /** Given a FederatedConnection bundle which contains an LF connection and + * all the connection channels. Create an endpoint at source and destination and a UcNetworkChannel + * connecting the, + */ + fun createNetworkEndpointsAndChannelForBundle(bundle: UcFederatedConnectionBundle): UcNetworkChannel { + val attr: Attribute? = getLinkAttribute(bundle.groupedConnections.first().lfConn) + var srcIf: UcNetworkInterface + var destIf: UcNetworkInterface + var channel: UcNetworkChannel + var serverLhs = true + var serverPort: Int? = null + + if (attr == null) { + // If there is no @link attribute on the connection we just get the default (unless there + // is ambiguity) + srcIf = bundle.src.getDefaultInterface() + destIf = bundle.dest.getDefaultInterface() + } else { + // Parse the @link attribute and generate a UcNetworkChannel between the correct + // interfaces. + val srcIfName = attr.getParamString("left") + val destIfName = attr.getParamString("right") + val serverSideAttr = attr.getParamString("server_side") + serverPort = attr.getParamInt("server_port") + srcIf = + if (srcIfName != null) bundle.src.getInterface(srcIfName) else bundle.src.getDefaultInterface() + destIf = + if (destIfName != null) bundle.dest.getInterface(destIfName) else bundle.dest.getDefaultInterface() + serverLhs = if (serverSideAttr == null) true else !serverSideAttr!!.equals("right") + } + + require(srcIf.type == destIf.type) + when (srcIf.type) { + TCP_IP -> { + val srcEp = (srcIf as UcTcpIpInterface).createEndpoint(if (serverLhs) serverPort else null) + val destEp = (destIf as UcTcpIpInterface).createEndpoint(if (!serverLhs) serverPort else null) + channel = UcTcpIpChannel(srcEp, destEp, serverLhs) + } + + COAP_UDP_IP -> { + val srcEp = (srcIf as UcCoapUdpIpInterface).createEndpoint() + val destEp = (destIf as UcCoapUdpIpInterface).createEndpoint() + channel = UcCoapUdpIpChannel(srcEp, destEp, serverLhs) + } + + CUSTOM -> { + val srcEp = (srcIf as UcCustomInterface).createEndpoint() + val destEp = (destIf as UcCustomInterface).createEndpoint() + channel = UcCustomChannel(srcEp, destEp) + } + NONE -> throw IllegalArgumentException("Tried creating network channel with type=NONE") + } + return channel + } + } +} + +class UcTcpIpChannel( + src: UcTcpIpEndpoint, + dest: UcTcpIpEndpoint, + serverLhs: Boolean = true, +) : UcNetworkChannel(TCP_IP, src, dest, serverLhs) { + private val srcTcp = src + private val destTcp = dest + + override fun generateChannelCtorSrc() = + "TcpIpChannel_ctor(&self->channel, \"${if (serverLhs) srcTcp.ipAddress.address else destTcp.ipAddress.address}\", ${if (serverLhs) srcTcp.port else destTcp.port}, AF_INET, ${serverLhs});" + + override fun generateChannelCtorDest() = + "TcpIpChannel_ctor(&self->channel, \"${if (serverLhs) srcTcp.ipAddress.address else destTcp.ipAddress.address}\", ${if (serverLhs) srcTcp.port else destTcp.port}, AF_INET, ${!serverLhs});" + + override val codeType: String + get() = "TcpIpChannel" +} + +class UcCoapUdpIpChannel( + src: UcCoapUdpIpEndpoint, + dest: UcCoapUdpIpEndpoint, + serverLhs: Boolean = true, +) : UcNetworkChannel(COAP_UDP_IP, src, dest, serverLhs) { + private val srcAddr = src.ipAddress.address + private val destAddr = dest.ipAddress.address + + override fun generateChannelCtorSrc() = + "CoapUdpIpChannel_ctor(&self->channel, \"${if (serverLhs) srcAddr else destAddr}\", AF_INET, ${serverLhs});" + + override fun generateChannelCtorDest() = + "CoapUdpIpChannel_ctor(&self->channel, \"${if (serverLhs) srcAddr else destAddr}\" AF_INET, ${!serverLhs});" + + + override val codeType: String + get() = "CoapUdpIpChannel" +} + +class UcCustomChannel( + src: UcCustomEndpoint, + dest: UcCustomEndpoint, + serverLhs: Boolean = true, +) : UcNetworkChannel(CUSTOM, src, dest, serverLhs) { + val srcIface = src.iface as UcCustomInterface + val destIface = dest.iface as UcCustomInterface + private val srcArgs = if (srcIface.args != null) ", ${srcIface.args}" else "" + private val destArgs = if (destIface.args != null) ", ${destIface.args}" else "" + + override fun generateChannelCtorSrc() = + "${srcIface.name}_ctor(&self->channel, ${srcArgs});" + + override fun generateChannelCtorDest() = + "${destIface.name}_ctor(&self->channel, ${destArgs});" + + override val codeType: String + get() = srcIface.name +} diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcParameterGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcParameterGenerator.kt index d83813c4..f11ee7b2 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcParameterGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcParameterGenerator.kt @@ -5,7 +5,7 @@ import org.lflang.lf.Instantiation import org.lflang.lf.Parameter import org.lflang.lf.Reactor -class UcParameterGenerator(private val reactor: Reactor) { +class UcParameterGenerator(private val reactor: Reactor, private val federate: UcFederate? = null) { companion object { @@ -33,7 +33,11 @@ class UcParameterGenerator(private val reactor: Reactor) { fun generateReactorCtorDeclArguments(r: Instantiation) = r.reactor.allParameters.joinToString(separator = "") { if (it.name == "bank_idx" || it.name == "bank_index") { - ", i" + if (federate != null) { + ", ${federate.bankIdx}" + } else { + ", i" + } } else if (r.parameters.filter{ p -> p.lhs.name == it.name}.isEmpty()) { ", ${it.init.expr.toCCode()}" } else { diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcPlatformGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcPlatformGenerator.kt index 2bde831d..defa85bc 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcPlatformGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcPlatformGenerator.kt @@ -1,33 +1,207 @@ package org.lflang.generator.uc import org.lflang.MessageReporter +import org.lflang.generator.CodeMap import org.lflang.target.TargetConfig import org.lflang.generator.GeneratorCommandFactory import org.lflang.generator.LFGeneratorContext +import org.lflang.generator.uc.UcInstanceGenerator.Companion.isAFederate +import org.lflang.reactor import org.lflang.target.property.BuildTypeProperty +import org.lflang.target.property.type.BuildTypeType.BuildType import org.lflang.toDefinition import org.lflang.toUnixString +import org.lflang.util.FileUtil +import org.lflang.util.LFCommand +import java.nio.file.Files import java.nio.file.Path +import java.nio.file.Paths +import kotlin.io.path.createSymbolicLinkPointingTo /** Abstract class for generating platform specific files and invoking the target compiler. */ abstract class UcPlatformGenerator(protected val generator: UcGenerator) { - protected val codeMaps = generator.codeMaps - protected val ucSources = generator.ucSources + private val codeMaps = generator.codeMaps + private val ucSources = generator.ucSources protected val messageReporter: MessageReporter = generator.messageReporter protected val fileConfig: UcFileConfig = generator.fileConfig protected val targetConfig: TargetConfig = generator.targetConfig - protected val commandFactory: GeneratorCommandFactory = generator.commandFactory + private val commandFactory: GeneratorCommandFactory = generator.commandFactory protected val mainReactor = generator.mainDef.reactorClass.toDefinition() + abstract val buildPath: Path + abstract val srcGenPath: Path + abstract val targetName: String - protected val relativeBinDir = fileConfig.outPath.relativize(fileConfig.binPath).toUnixString() + private val relativeBinDir = fileConfig.outPath.relativize(fileConfig.binPath).toUnixString() abstract fun generatePlatformFiles() - abstract fun doCompile(context: LFGeneratorContext, onlyGenerateBuildFiles: Boolean = false): Boolean - - protected val cmakeArgs: List + private val cmakeArgs: List get() = listOf( "-DCMAKE_BUILD_TYPE=${targetConfig.get(BuildTypeProperty.INSTANCE)}", ) + companion object { + fun buildTypeToCmakeConfig(type: BuildType) = when (type) { + BuildType.TEST -> "Debug" + else -> type.toString() + } + } + + fun doGeneratePlatformFiles(mainGenerator: UcMainGenerator, cmakeGenerator: UcCmakeGenerator, makeGenerator: UcMakeGenerator) { + val reactorUCEnvPath = System.getenv("REACTOR_UC_PATH") + if (reactorUCEnvPath == null) { + messageReporter.nowhere().error("REACTOR_UC_PATH environment variable not defined. Do source env.bash in reactor-uc") + return; + } + val runtimePath: Path = Paths.get(reactorUCEnvPath) + + val startSourceFile = Paths.get("lf_start.c") + val startHeaderFile = Paths.get("lf_start.h") + val mainSourceFile = Paths.get("lf_main.c") + + val startCodeMap = CodeMap.fromGeneratedCode(mainGenerator.generateStartSource()) + val mainCodeMap = CodeMap.fromGeneratedCode(mainGenerator.generateMainSource()) + + ucSources.addAll(listOf(startSourceFile, mainSourceFile)) + codeMaps[srcGenPath.resolve(startSourceFile)] = startCodeMap + codeMaps[srcGenPath.resolve(mainSourceFile)] = mainCodeMap + + FileUtil.writeToFile(startCodeMap.generatedCode, srcGenPath.resolve(startSourceFile), true) + FileUtil.writeToFile(mainCodeMap.generatedCode, srcGenPath.resolve(mainSourceFile), true) + FileUtil.writeToFile(mainGenerator.generateStartHeader(), srcGenPath.resolve(startHeaderFile), true) + + FileUtil.writeToFile(cmakeGenerator.generateCmake(ucSources), srcGenPath.resolve("CMakeLists.txt"), true) + val runtimeSymlinkPath: Path = srcGenPath.resolve("reactor-uc"); + try { + runtimeSymlinkPath.createSymbolicLinkPointingTo(runtimePath); + } catch (e: Exception) { + // Do nothing + } + + FileUtil.writeToFile(makeGenerator.generateMake(ucSources), srcGenPath.resolve("Makefile"), true) + } + + fun doCompile(context: LFGeneratorContext, onlyGenerateBuildFiles: Boolean = false): Boolean { + // make sure the build directory exists + Files.createDirectories(buildPath) + + val version = checkCmakeVersion() + var parallelize = true + if (version != null && version.compareVersion("3.12.0") < 0) { + messageReporter.nowhere().warning("CMAKE is older than version 3.12. Parallel building is not supported.") + parallelize = false + } + + if (version != null) { + val cmakeReturnCode = runCmake(context) + + if (cmakeReturnCode == 0 && !onlyGenerateBuildFiles) { + // If cmake succeeded, run make + val makeCommand = createMakeCommand(buildPath, parallelize, targetName) + val makeReturnCode = UcValidator(fileConfig, messageReporter, codeMaps).run(makeCommand, context.cancelIndicator) + var installReturnCode = 0 + if (makeReturnCode == 0) { + val installCommand = createMakeCommand(buildPath, parallelize, "install") + installReturnCode = installCommand.run(context.cancelIndicator) + if (installReturnCode == 0) { + println("SUCCESS (compiling generated C code)") + println("Generated source code is in ${fileConfig.srcGenPath}") + println("Compiled binary is in ${fileConfig.binPath}") + } + } + if ((makeReturnCode != 0 || installReturnCode != 0) && !messageReporter.errorsOccurred) { + // If errors occurred but none were reported, then the following message is the best we can do. + messageReporter.nowhere().error("make failed with error code $makeReturnCode") + } + } + if (cmakeReturnCode != 0) { + messageReporter.nowhere().error("cmake failed with error code $cmakeReturnCode") + } + } + return !messageReporter.errorsOccurred + } + + private fun checkCmakeVersion(): String? { + // get the installed cmake version and make sure it is at least 3.5 + val cmd = commandFactory.createCommand("cmake", listOf("--version"), buildPath) + var version: String? = null + if (cmd != null && cmd.run() == 0) { + val regex = "\\d+(\\.\\d+)+".toRegex() + version = regex.find(cmd.output.toString())?.value + } + if (version == null || version.compareVersion("3.5.0") < 0) { + messageReporter.nowhere( + ).error( + "The uC target requires CMAKE >= 3.5.0 to compile the generated code. " + + "Auto-compiling can be disabled using the \"no-compile: true\" target property." + ) + return null + } + + return version + } + private fun runCmake(context: LFGeneratorContext): Int { + val cmakeCommand = createCmakeCommand(buildPath, fileConfig.outPath) + return cmakeCommand.run(context.cancelIndicator) + } + + private fun String.compareVersion(other: String): Int { + val a = this.split(".").map { it.toInt() } + val b = other.split(".").map { it.toInt() } + for (x in (a zip b)) { + val res = x.first.compareTo(x.second) + if (res != 0) + return res + } + return 0 + } + + private fun getMakeArgs(buildPath: Path, parallelize: Boolean, target: String): List { + val cmakeConfig = buildTypeToCmakeConfig(targetConfig.get(BuildTypeProperty.INSTANCE)) + val makeArgs = mutableListOf( + "--build", + buildPath.fileName.toString(), + "--config", + cmakeConfig, + "--target", + target + ) + + if (parallelize) { + makeArgs.addAll(listOf("--parallel", Runtime.getRuntime().availableProcessors().toString())) + } + + return makeArgs + } + private fun createMakeCommand(buildPath: Path, parallelize: Boolean, target: String): LFCommand { + val makeArgs = getMakeArgs(buildPath, parallelize, target) + return commandFactory.createCommand("cmake", makeArgs, buildPath.parent) + } + + private fun getCmakeArgs( + buildPath: Path, + outPath: Path, + sourcesRoot: String? = null + ) = cmakeArgs + listOf( + "-DCMAKE_INSTALL_PREFIX=${outPath.toUnixString()}", + "-DCMAKE_INSTALL_BINDIR=$relativeBinDir", + "--fresh", + "-S", + sourcesRoot ?: srcGenPath.toUnixString(), + "-B", + buildPath.fileName.toString() + ) + + private fun createCmakeCommand( + buildPath: Path, + outPath: Path, + sourcesRoot: String? = null + ): LFCommand { + val cmd = commandFactory.createCommand( + "cmake", + getCmakeArgs(buildPath, outPath, sourcesRoot), + buildPath.parent + ) + return cmd + } } diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcPlatformGeneratorFederated.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcPlatformGeneratorFederated.kt new file mode 100644 index 00000000..e8810314 --- /dev/null +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcPlatformGeneratorFederated.kt @@ -0,0 +1,34 @@ +package org.lflang.generator.uc + +import org.lflang.reactor +import org.lflang.target.property.PlatformProperty +import org.lflang.target.property.type.PlatformType +import org.lflang.util.FileUtil +import java.nio.file.Path + +class UcPlatformGeneratorFederated(generator: UcGeneratorFederated, override val srcGenPath: Path, private val federate: UcFederate) : + UcPlatformGenerator(generator) { + + override val buildPath = srcGenPath.resolve("build") + override val targetName: String = federate.codeType + + override fun generatePlatformFiles() { + val generator = generator as UcGeneratorFederated + val mainGenerator = UcMainGeneratorFederated(federate, generator.federates, generator.targetConfig, generator.fileConfig) + val numEventsAndReactions = generator.totalNumEventsAndReactionsFederated(federate) + val cmakeGenerator = UcCmakeGeneratorFederated(federate, targetConfig, generator.fileConfig, numEventsAndReactions.first, numEventsAndReactions.second) + val makeGenerator = UcMakeGeneratorFederated(federate, targetConfig, generator.fileConfig, numEventsAndReactions.first, numEventsAndReactions.second) + super.doGeneratePlatformFiles(mainGenerator, cmakeGenerator, makeGenerator) + + if (targetConfig.get(PlatformProperty.INSTANCE).platform == PlatformType.Platform.NATIVE) { + generateLaunchScript() + } + } + + fun generateLaunchScript() { + val generator = generator as UcGeneratorFederated + val launchScriptGenerator = UcFederatedLaunchScriptGenerator(fileConfig) + FileUtil.writeToFile(launchScriptGenerator.generateLaunchScript(generator.federates), fileConfig.binPath.resolve(fileConfig.name)) + fileConfig.binPath.resolve(fileConfig.name).toFile().setExecutable(true) + } +} \ No newline at end of file diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcPlatformGeneratorNonFederated.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcPlatformGeneratorNonFederated.kt new file mode 100644 index 00000000..f6b95f73 --- /dev/null +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcPlatformGeneratorNonFederated.kt @@ -0,0 +1,29 @@ +package org.lflang.generator.uc + +import org.lflang.generator.CodeMap +import org.lflang.generator.LFGeneratorContext +import org.lflang.reactor +import org.lflang.target.property.BuildTypeProperty +import org.lflang.target.property.type.BuildTypeType.BuildType +import org.lflang.toUnixString +import org.lflang.util.FileUtil +import org.lflang.util.LFCommand +import java.nio.file.Files +import java.nio.file.Path +import java.nio.file.Paths +import kotlin.io.path.createSymbolicLinkPointingTo + +class UcPlatformGeneratorNonFederated(generator: UcGenerator, override val srcGenPath: Path) : + UcPlatformGenerator(generator) { + + override val buildPath = srcGenPath.resolve("build") + override val targetName = fileConfig.name + + override fun generatePlatformFiles() { + val mainGenerator = UcMainGeneratorNonFederated(mainReactor, generator.targetConfig, generator.fileConfig) + val numEventsAndReactions = generator.totalNumEventsAndReactions(generator.mainDef.reactor) + val cmakeGenerator = UcCmakeGeneratorNonFederated(generator.mainDef, targetConfig, generator.fileConfig, numEventsAndReactions.first, numEventsAndReactions.second) + val makeGenerator = UcMakeGeneratorNonFederated(mainReactor, targetConfig, generator.fileConfig, numEventsAndReactions.first, numEventsAndReactions.second) + super.doGeneratePlatformFiles(mainGenerator, cmakeGenerator, makeGenerator) + } +} \ No newline at end of file diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcPortGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcPortGenerator.kt index 5824313b..3e45ddba 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcPortGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcPortGenerator.kt @@ -25,8 +25,7 @@ package org.lflang.generator.uc import org.lflang.* -import org.lflang.generator.PrependOperator -import org.lflang.generator.uc.UcInstanceGenerator.Companion.width +import org.lflang.generator.uc.UcInstanceGenerator.Companion.codeWidth import org.lflang.generator.uc.UcReactorGenerator.Companion.codeType import org.lflang.generator.uc.UcReactorGenerator.Companion.getEffects import org.lflang.generator.uc.UcReactorGenerator.Companion.getObservers @@ -81,7 +80,7 @@ class UcPortGenerator(private val reactor: Reactor, private val connections: UcC when (it) { is Input -> generateInputCtor(it) is Output -> generateOutputCtor(it) - else -> "" // FIXME: Runtime exception + else -> throw IllegalArgumentException("Error: Port was neither input nor output") } } @@ -92,18 +91,18 @@ class UcPortGenerator(private val reactor: Reactor, private val connections: UcC when(port) { is Input -> generateReactorCtorCode(port) is Output -> generateReactorCtorCode(port) - else -> "" // FIXME: Runtime exception + else -> throw IllegalArgumentException("Error: Port was neither input nor output") } fun generateReactorCtorCodes() = reactor.allInputs.plus(reactor.allOutputs).joinToString(prefix = "// Initialize ports\n", separator = "\n", postfix = "\n") { generateReactorCtorCode(it)} fun generateDefineContainedOutputArgs(r: Instantiation) = r.reactor.allOutputs.joinToString(separator = "\n", prefix = "\n", postfix = "\n") { - "LF_DEFINE_CHILD_OUTPUT_ARGS(${r.name}, ${it.name}, ${r.width}, ${it.width});" + "LF_DEFINE_CHILD_OUTPUT_ARGS(${r.name}, ${it.name}, ${r.codeWidth}, ${it.width});" } fun generateDefineContainedInputArgs(r: Instantiation) = r.reactor.allInputs.joinToString(separator = "\n", prefix = "\n", postfix = "\n") { - "LF_DEFINE_CHILD_INPUT_ARGS(${r.name}, ${it.name}, ${r.width}, ${it.width});" + "LF_DEFINE_CHILD_INPUT_ARGS(${r.name}, ${it.name}, ${r.codeWidth}, ${it.width});" } fun generateReactorCtorDefArguments() = diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcReactionGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcReactionGenerator.kt index 9d3a853c..e05b9e90 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcReactionGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcReactionGenerator.kt @@ -2,6 +2,7 @@ package org.lflang.generator.uc import org.lflang.* import org.lflang.generator.PrependOperator +import org.lflang.generator.uc.UcInstanceGenerator.Companion.codeWidth import org.lflang.generator.uc.UcInstanceGenerator.Companion.width import org.lflang.generator.uc.UcPortGenerator.Companion.width import org.lflang.generator.uc.UcReactorGenerator.Companion.codeType @@ -59,7 +60,7 @@ class UcReactionGenerator(private val reactor: Reactor) { } } for (effect in allContainedEffects) { - res += effect.container.width * (effect.variable as Port).width + res += effect.container.codeWidth * (effect.variable as Port).width } return res } @@ -112,7 +113,7 @@ class UcReactionGenerator(private val reactor: Reactor) { private fun registerPortSource(varRef: VarRef, port: Port, reaction: Reaction) = if (varRef.container != null) { - (0..(varRef.container.width-1)).toList().joinToString(separator = "\n") { + (0..(varRef.container.codeWidth-1)).toList().joinToString(separator = "\n") { "LF_PORT_REGISTER_SOURCE(self->${varRef.container.name}[${it}].${port.name}, ${reaction.nameInReactor}, ${port.width})" } } else { @@ -136,7 +137,7 @@ class UcReactionGenerator(private val reactor: Reactor) { private fun registerPortEffect(varRef: VarRef, port: Port, reaction: Reaction) = if (varRef.container != null) { - (0..(varRef.container.width-1)).toList().joinToString(separator = "\n") { + (0..(varRef.container.codeWidth-1)).toList().joinToString(separator = "\n") { "LF_PORT_REGISTER_EFFECT(self->${varRef.container.name}[${it}].${port.name}, ${reaction.nameInReactor}, ${port.width})" } } else { @@ -153,7 +154,7 @@ class UcReactionGenerator(private val reactor: Reactor) { private fun registerPortObserver(varRef: VarRef, port: Port, reaction: Reaction) = if (varRef.container != null) { - (0..(varRef.container.width-1)).toList().joinToString(separator = "\n") { + (0..(varRef.container.codeWidth-1)).toList().joinToString(separator = "\n") { "LF_PORT_REGISTER_OBSERVER(self->${varRef.container.name}[${it}].${port.name}, ${reaction.nameInReactor}, ${port.width})" } } else { @@ -239,9 +240,9 @@ class UcReactionGenerator(private val reactor: Reactor) { private fun generateContainedTriggerInScope(trigger: VarRef) = if (trigger.variable.isMultiport) { - "LF_MULTIPORT_PTR_INSTANCE(${trigger.container.reactor.codeType}, ${trigger.name}, ${(trigger.variable as Port).width});" // FIXME: What about this? + "LF_MULTIPORT_PTR_INSTANCE(${trigger.container.reactor.codeType}, ${trigger.name}, ${(trigger.variable as Port).width});" } else { - "LF_PORT_PTR_INSTANCE(${trigger.container.reactor.codeType}, ${trigger.name});" // FIXME: What about this? + "LF_PORT_PTR_INSTANCE(${trigger.container.reactor.codeType}, ${trigger.name});" } private fun generateContainedMultiportTriggerFieldInit(instName: String, containerName: String, trigger: VarRef, port: Port) = with(PrependOperator) { @@ -265,10 +266,9 @@ class UcReactionGenerator(private val reactor: Reactor) { if (trigger.variable.isMultiport) { generateContainedMultiportTriggerFieldInit("${instName}[i]", "&self->${trigger.container.name}[i]", trigger, trigger.variable as Port) } else { - "${instName}[i].${trigger.name} = &self->${trigger.container.name}[i].${trigger.name};" + "${instName}[i].${trigger.name} = self->${trigger.container.name}[i].${trigger.name};" } - // FIXME: This must also consider multiports and banks private fun generateContainedReactorScope(triggers: List, inst: Instantiation) = with(PrependOperator) { """| |// Generated struct providing access to ports of child reactor `${inst.name}` @@ -286,9 +286,9 @@ class UcReactionGenerator(private val reactor: Reactor) { |struct _${inst.reactor.codeType}_${inst.name} { ${"| "..triggers.joinToString { generateContainedTriggerInScope(it) }} |}; - |struct _${inst.reactor.codeType}_${inst.name} ${inst.name}[${inst.width}]; - |size_t ${inst.name}_width = ${inst.width}; - |for (int i = 0; i<${inst.width}; i++) { + |struct _${inst.reactor.codeType}_${inst.name} ${inst.name}[${inst.codeWidth}]; + |size_t ${inst.name}_width = ${inst.codeWidth}; + |for (int i = 0; i<${inst.codeWidth}; i++) { ${"| "..triggers.joinToString(separator = "\n") {generateContainedBankTriggerFieldInit( "${inst.name}", it)}} |} """.trimMargin() @@ -301,7 +301,7 @@ class UcReactionGenerator(private val reactor: Reactor) { private fun generateContainedTriggersAndSourcesInScope(reaction: Reaction) = reaction.allContainedEffectsTriggersAndSources.toList() .joinToString(separator = "\n") { - if (it.first.width > 1) { + if (it.first.codeWidth > 1) { generateContainedBankScope(it.second, it.first) } else { generateContainedReactorScope(it.second, it.first) diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcReactorGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcReactorGenerator.kt index 58186f52..87e66186 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcReactorGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcReactorGenerator.kt @@ -3,6 +3,7 @@ package org.lflang.generator.uc import org.lflang.* import org.lflang.generator.PrependOperator import org.lflang.generator.uc.UcActionGenerator.Companion.maxNumPendingEvents +import org.lflang.generator.uc.UcInstanceGenerator.Companion.codeWidth import org.lflang.generator.uc.UcInstanceGenerator.Companion.width import org.lflang.generator.uc.UcPortGenerator.Companion.width import org.lflang.lf.* @@ -26,10 +27,10 @@ class UcReactorGenerator(private val reactor: Reactor, private val fileConfig: U return res; } - private val numChildren = reactor.allInstantiations.map { it.width }.sum() + private val numChildren = reactor.allInstantiations.map { it.codeWidth }.sum() private val parameters = UcParameterGenerator(reactor) - private val connections = UcConnectionGenerator(reactor) + private val connections = UcConnectionGenerator(reactor, null, emptyList()) private val state = UcStateGenerator(reactor) private val ports = UcPortGenerator(reactor, connections) private val timers = UcTimerGenerator(reactor) @@ -76,36 +77,15 @@ class UcReactorGenerator(private val reactor: Reactor, private val fileConfig: U fun Reactor.getObservers(v: BuiltinTrigger) = allReactions.filter { it.sources.filter { it.name == v.literal }.isNotEmpty() } + } - fun Reactor.getEventQueueSize(): Int { - var childrenEvents = 0 - for (child in this.allInstantiations) { - childrenEvents += child.reactor.getEventQueueSize()*child.width - } - var currentReactorsEvents = 0 - for (timer in this.allTimers) { - currentReactorsEvents += 1 - } - for (action in this.allActions) { - currentReactorsEvents += action.maxNumPendingEvents - } - - if (hasShutdown) currentReactorsEvents += 1 - if (hasStartup) currentReactorsEvents += 1 - - val ucConnections = UcConnectionGenerator(this) - currentReactorsEvents += ucConnections.getMaxNumPendingEvents() - return childrenEvents + currentReactorsEvents - } - - fun Reactor.getReactionQueueSize(): Int { - var res = 0 - for (child in allInstantiations) { - res += child.reactor.getReactionQueueSize() * child.width - } - res += reactions.size - return res + fun getMaxNumPendingEvents(): Int { + var numEvents = reactor.allTimers.count() + for (action in reactor.allActions) { + numEvents += action.maxNumPendingEvents } + numEvents += connections.getMaxNumPendingEvents() + return numEvents } private fun generateReactorStruct() = with(PrependOperator) { diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcStandaloneGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcStandaloneGenerator.kt deleted file mode 100644 index 4c161ca3..00000000 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcStandaloneGenerator.kt +++ /dev/null @@ -1,197 +0,0 @@ -package org.lflang.generator.uc - -import org.lflang.generator.CodeMap -import org.lflang.generator.LFGeneratorContext -import org.lflang.target.property.BuildTypeProperty -import org.lflang.target.property.type.BuildTypeType.BuildType -import org.lflang.toUnixString -import org.lflang.util.FileUtil -import org.lflang.util.LFCommand -import java.nio.file.Files -import java.nio.file.Path -import java.nio.file.Paths -import kotlin.io.path.createSymbolicLinkPointingTo - -class UcStandaloneGenerator(generator: UcGenerator, val srcGenPath: Path) : - UcPlatformGenerator(generator) { - - companion object { - fun buildTypeToCmakeConfig(type: BuildType) = when (type) { - BuildType.TEST -> "Debug" - else -> type.toString() - } - } - - override fun generatePlatformFiles() { - val reactorUCEnvPath = System.getenv("REACTOR_UC_PATH") - // FIXME: Improve this error handling - if (reactorUCEnvPath == null) { - messageReporter.nowhere().error("REACTOR_UC_PATH environment variable not defined. Do source env.bash in reactor-uc") - return; - } - val runtimePath: Path = Paths.get(reactorUCEnvPath) - // generate the main source file (containing main()) - val mainGenerator = UcMainGenerator(mainReactor, generator.targetConfig, generator.fileConfig) - - val startSourceFile = Paths.get("lf_start.c") - val startHeaderFile = Paths.get("lf_start.h") - val mainSourceFile = Paths.get("lf_main.c") - - val startCodeMap = CodeMap.fromGeneratedCode(mainGenerator.generateStartSource()) - val mainCodeMap = CodeMap.fromGeneratedCode(mainGenerator.generateMainSource()) - - ucSources.addAll(listOf(startSourceFile, mainSourceFile)) - codeMaps[srcGenPath.resolve(startSourceFile)] = startCodeMap - codeMaps[srcGenPath.resolve(mainSourceFile)] = mainCodeMap - - FileUtil.writeToFile(startCodeMap.generatedCode, srcGenPath.resolve(startSourceFile), true) - FileUtil.writeToFile(mainCodeMap.generatedCode, srcGenPath.resolve(mainSourceFile), true) - FileUtil.writeToFile(mainGenerator.generateStartHeader(), srcGenPath.resolve(startHeaderFile), true) - - val cmakeGenerator = UcCmakeGenerator(mainReactor, targetConfig, generator.fileConfig) - val makeGenerator = UcMakeGenerator(mainReactor, targetConfig, generator.fileConfig) - val pkgName = fileConfig.srcGenPkgPath.fileName.toString() - FileUtil.writeToFile(cmakeGenerator.generateCmake(ucSources), srcGenPath.resolve("CMakeLists.txt"), true) - val runtimeSymlinkPath: Path = srcGenPath.resolve("reactor-uc"); - try { - runtimeSymlinkPath.createSymbolicLinkPointingTo(runtimePath); - } catch (e: Exception) { - // Do nothing - } - - FileUtil.writeToFile(makeGenerator.generateMake(ucSources), srcGenPath.resolve("Makefile"), true) - } - - override fun doCompile(context: LFGeneratorContext, onlyGenerateBuildFiles: Boolean): Boolean { - - // make sure the build directory exists - Files.createDirectories(fileConfig.buildPath) - - val version = checkCmakeVersion() - var parallelize = true - if (version != null && version.compareVersion("3.12.0") < 0) { - messageReporter.nowhere().warning("CMAKE is older than version 3.12. Parallel building is not supported.") - parallelize = false - } - - if (version != null) { - val cmakeReturnCode = runCmake(context) - - if (cmakeReturnCode == 0 && !onlyGenerateBuildFiles) { - // If cmake succeeded, run make - val makeCommand = createMakeCommand(fileConfig.buildPath, parallelize, fileConfig.name) - val makeReturnCode = UcValidator(fileConfig, messageReporter, codeMaps).run(makeCommand, context.cancelIndicator) - var installReturnCode = 0 - if (makeReturnCode == 0) { - val installCommand = createMakeCommand(fileConfig.buildPath, parallelize, "install") - installReturnCode = installCommand.run(context.cancelIndicator) - if (installReturnCode == 0) { - println("SUCCESS (compiling generated C code)") - println("Generated source code is in ${fileConfig.srcGenPath}") - println("Compiled binary is in ${fileConfig.binPath}") - } - } - if ((makeReturnCode != 0 || installReturnCode != 0) && !messageReporter.errorsOccurred) { - // If errors occurred but none were reported, then the following message is the best we can do. - messageReporter.nowhere().error("make failed with error code $makeReturnCode") - } - } - if (cmakeReturnCode != 0) { - messageReporter.nowhere().error("cmake failed with error code $cmakeReturnCode") - } - } - return !messageReporter.errorsOccurred - } - - private fun checkCmakeVersion(): String? { - // get the installed cmake version and make sure it is at least 3.5 - val cmd = commandFactory.createCommand("cmake", listOf("--version"), fileConfig.buildPath) - var version: String? = null - if (cmd != null && cmd.run() == 0) { - val regex = "\\d+(\\.\\d+)+".toRegex() - version = regex.find(cmd.output.toString())?.value - } - if (version == null || version.compareVersion("3.5.0") < 0) { - messageReporter.nowhere( - ).error( - "The uC target requires CMAKE >= 3.5.0 to compile the generated code. " + - "Auto-compiling can be disabled using the \"no-compile: true\" target property." - ) - return null - } - - return version - } - - - /** - * Run CMake to generate build files. - * @return True, if cmake run successfully - */ - private fun runCmake(context: LFGeneratorContext): Int { - val cmakeCommand = createCmakeCommand(fileConfig.buildPath, fileConfig.outPath) - return cmakeCommand.run(context.cancelIndicator) - } - - private fun String.compareVersion(other: String): Int { - val a = this.split(".").map { it.toInt() } - val b = other.split(".").map { it.toInt() } - for (x in (a zip b)) { - val res = x.first.compareTo(x.second) - if (res != 0) - return res - } - return 0 - } - - private fun getMakeArgs(buildPath: Path, parallelize: Boolean, target: String): List { - val cmakeConfig = buildTypeToCmakeConfig(targetConfig.get(BuildTypeProperty.INSTANCE)) - val makeArgs = mutableListOf( - "--build", - buildPath.fileName.toString(), - "--config", - cmakeConfig, - "--target", - target - ) - - if (parallelize) { - makeArgs.addAll(listOf("--parallel", Runtime.getRuntime().availableProcessors().toString())) - } - - return makeArgs - } - - - private fun createMakeCommand(buildPath: Path, parallelize: Boolean, target: String): LFCommand { - val makeArgs = getMakeArgs(buildPath, parallelize, target) - return commandFactory.createCommand("cmake", makeArgs, buildPath.parent) - } - - private fun getCmakeArgs( - buildPath: Path, - outPath: Path, - sourcesRoot: String? = null - ) = cmakeArgs + listOf( - "-DCMAKE_INSTALL_PREFIX=${outPath.toUnixString()}", - "-DCMAKE_INSTALL_BINDIR=$relativeBinDir", - "--fresh", - "-S", - sourcesRoot ?: fileConfig.srcGenPath.toUnixString(), - "-B", - buildPath.fileName.toString() - ) - - private fun createCmakeCommand( - buildPath: Path, - outPath: Path, - sourcesRoot: String? = null - ): LFCommand { - val cmd = commandFactory.createCommand( - "cmake", - getCmakeArgs(buildPath, outPath, sourcesRoot), - buildPath.parent - ) - return cmd - } -} \ No newline at end of file diff --git a/src/environment.c b/src/environment.c index 8029ee79..f4d98a23 100644 --- a/src/environment.c +++ b/src/environment.c @@ -49,7 +49,11 @@ interval_t Environment_get_physical_time(Environment *self) { return self->platform->get_physical_time(self->platform); } interval_t Environment_get_elapsed_physical_time(Environment *self) { - return self->platform->get_physical_time(self->platform) - self->scheduler->start_time; + if (self->scheduler->start_time == NEVER) { + return 0; + } else { + return self->platform->get_physical_time(self->platform) - self->scheduler->start_time; + } } void Environment_enter_critical_section(Environment *self) { if (self->has_async_events) { @@ -89,7 +93,7 @@ void Environment_ctor(Environment *self, Reactor *main) { void Environment_free(Environment *self) { (void)self; - LF_INFO(ENV, "Freeing environment"); + LF_INFO(ENV, "Reactor shutting down, freeing environment."); for (size_t i = 0; i < self->net_bundles_size; i++) { NetworkChannel *chan = self->net_bundles[i]->net_channel; chan->free(chan); diff --git a/src/federated.c b/src/federated.c index 35e3780c..4dd5a647 100644 --- a/src/federated.c +++ b/src/federated.c @@ -7,6 +7,7 @@ void LogicalConnection_trigger_downstreams(Connection *self, const void *value, size_t value_size); void FederatedConnectionBundle_connect_to_peers(FederatedConnectionBundle **bundles, size_t bundles_size) { + LF_INFO(FED, "%s connecting to %zu federated peers", _lf_environment->main->name, bundles_size); lf_ret_t ret; Environment *env = bundles[0]->parent->env; @@ -24,7 +25,7 @@ void FederatedConnectionBundle_connect_to_peers(FederatedConnectionBundle **bund for (size_t i = 0; i < bundles_size; i++) { FederatedConnectionBundle *bundle = bundles[i]; NetworkChannel *chan = bundle->net_channel; - if (!chan->is_connected(chan)) { + if (!chan->was_ever_connected(chan)) { if (chan->expected_connect_duration < wait_before_retry && chan->expected_connect_duration > 0) { wait_before_retry = chan->expected_connect_duration; } @@ -35,6 +36,8 @@ void FederatedConnectionBundle_connect_to_peers(FederatedConnectionBundle **bund env->platform->wait_for(env->platform, wait_before_retry); } } + + LF_INFO(FED, "%s Established connection to all %zu federated peers", _lf_environment->main->name, bundles_size); } // Called when a reaction does lf_set(outputPort). Should buffer the output data @@ -150,7 +153,7 @@ void FederatedInputConnection_ctor(FederatedInputConnection *self, Reactor *pare self->delay = delay; self->type = type; self->last_known_tag = NEVER_TAG; - self->safe_to_assume_absent = FOREVER; + self->safe_to_assume_absent = 0; // FIXME: This should be set by the user } void FederatedConnectionBundle_handle_start_tag_signal(FederatedConnectionBundle *self, const FederateMessage *_msg) { @@ -225,21 +228,29 @@ void FederatedConnectionBundle_handle_tagged_msg(FederatedConnectionBundle *self ret = sched->schedule_at_locked(sched, &event); switch (ret) { case LF_AFTER_STOP_TAG: - LF_WARN(FED, "Tried scheduling event after stop tag. Dropping\n"); + LF_WARN(FED, "Tried scheduling event after stop tag. Dropping"); break; case LF_PAST_TAG: - LF_WARN(FED, "Tried scheduling event to a past tag. Dropping\n"); + LF_ERR(FED, "Safe-to-process violation! Tried scheduling event to a past tag. Handling now instead!"); + event.tag = sched->current_tag(sched); + event.tag.microstep++; + status = sched->schedule_at_locked(sched, &event); + if (status != LF_OK) { + LF_ERR(FED, "Failed to schedule event at current tag also. Dropping"); + } else { + env->platform->new_async_event(env->platform); + } break; case LF_OK: env->platform->new_async_event(env->platform); break; default: - LF_ERR(FED, "Unknown return value `%d` from schedule_at_locked\n", ret); + LF_ERR(FED, "Unknown return value `%d` from schedule_at_locked", ret); validate(false); break; } } else { - LF_ERR(FED, "Cannot deserialize message from other Federate. Dropping\n"); + LF_ERR(FED, "Cannot deserialize message from other Federate. Dropping"); } if (lf_tag_compare(input->last_known_tag, tag) < 0) { diff --git a/src/logging.c b/src/logging.c index c99bdeec..43eeba62 100644 --- a/src/logging.c +++ b/src/logging.c @@ -1,5 +1,6 @@ #include "reactor-uc/logging.h" #include "reactor-uc/platform.h" +#include "reactor-uc/environment.h" #include #include @@ -22,7 +23,7 @@ void log_printf(const char *fmt, ...) { void log_message(int level, const char *module, const char *fmt, ...) { const char *level_str; switch (level) { - case LF_LOG_LEVEL_ERR: + case LF_LOG_LEVEL_ERROR: level_str = "ERROR"; break; case LF_LOG_LEVEL_WARN: @@ -42,9 +43,9 @@ void log_message(int level, const char *module, const char *fmt, ...) { va_list args; va_start(args, fmt); -#ifdef LF_COLORIZE_LOGS +#if LF_COLORIZE_LOGS == 1 switch (level) { - case LF_LOG_LEVEL_ERR: + case LF_LOG_LEVEL_ERROR: log_printf(ANSI_COLOR_RED); break; case LF_LOG_LEVEL_WARN: @@ -60,9 +61,20 @@ void log_message(int level, const char *module, const char *fmt, ...) { break; } #endif + +#if LF_TIMESTAMP_LOGS == 1 + instant_t timestamp = 0; + if (_lf_environment) { + timestamp = _lf_environment->get_elapsed_physical_time(_lf_environment); + } + log_printf("%" PRId64 " [%s] [%s] ", timestamp, level_str, module); +#else + log_printf("[%s] [%s] ", level_str, module); +#endif + Platform_vprintf(fmt, args); -#ifdef LF_COLORIZE_LOGS +#if LF_COLORIZE_LOGS == 1 log_printf(ANSI_COLOR_RESET); #endif log_printf("\n"); diff --git a/src/platform/posix/posix.c b/src/platform/posix/posix.c index 9b479eca..05a3c932 100644 --- a/src/platform/posix/posix.c +++ b/src/platform/posix/posix.c @@ -15,7 +15,7 @@ void Platform_vprintf(const char *fmt, va_list args) { vprintf(fmt, args); } // lf_exit should be defined in main.c and should call Environment_free, if not we provide an empty implementation here. __attribute__((weak)) void lf_exit(void) { exit(0); } -static void handle_ctrlc(int sig) { +static void handle_signal(int sig) { (void)sig; lf_exit(); exit(0); @@ -29,7 +29,8 @@ static struct timespec convert_ns_to_timespec(instant_t time) { } lf_ret_t PlatformPosix_initialize(Platform *_self) { - signal(SIGINT, handle_ctrlc); + signal(SIGINT, handle_signal); + signal(SIGTERM, handle_signal); PlatformPosix *self = (PlatformPosix *)_self; if (pthread_mutex_init(&self->lock, NULL) != 0) { LF_ERR(PLATFORM, "Failed to initialize mutex"); diff --git a/src/platform/posix/tcp_ip_channel.c b/src/platform/posix/tcp_ip_channel.c index 1ced9370..5517d934 100644 --- a/src/platform/posix/tcp_ip_channel.c +++ b/src/platform/posix/tcp_ip_channel.c @@ -31,33 +31,40 @@ #define TCP_IP_CHANNEL_DEBUG(fmt, ...) \ LF_DEBUG(NET, "TcpIpChannel: [%s] " fmt, self->is_server ? "server" : "client", ##__VA_ARGS__) -static bool _is_globals_initialized = false; -static Environment *_env; - // Forward declarations static void *_TcpIpChannel_worker_thread(void *untyped_self); static void _TcpIpChannel_update_state(TcpIpChannel *self, NetworkChannelState new_state) { - TCP_IP_CHANNEL_DEBUG("Update state: %s => %s\n", NetworkChannel_state_to_string(self->state), + TCP_IP_CHANNEL_DEBUG("Update state: %s => %s", NetworkChannel_state_to_string(self->state), NetworkChannel_state_to_string(new_state)); + pthread_mutex_lock(&self->mutex); // Store old state NetworkChannelState old_state = self->state; // Update the state of the channel to its new state self->state = new_state; + pthread_mutex_unlock(&self->mutex); + + if (new_state == NETWORK_CHANNEL_STATE_CONNECTED) { + self->was_ever_connected = true; + } // Inform runtime about new state if it changed from or to NETWORK_CHANNEL_STATE_CONNECTED if ((old_state == NETWORK_CHANNEL_STATE_CONNECTED && new_state != NETWORK_CHANNEL_STATE_CONNECTED) || (old_state != NETWORK_CHANNEL_STATE_CONNECTED && new_state == NETWORK_CHANNEL_STATE_CONNECTED)) { - _env->platform->new_async_event(_env->platform); + _lf_environment->platform->new_async_event(_lf_environment->platform); } + TCP_IP_CHANNEL_DEBUG("Update state: %s => %s done", NetworkChannel_state_to_string(self->state), + NetworkChannel_state_to_string(new_state)); } static NetworkChannelState _TcpIpChannel_get_state(TcpIpChannel *self) { NetworkChannelState state; + pthread_mutex_lock(&self->mutex); state = self->state; + pthread_mutex_unlock(&self->mutex); return state; } @@ -70,6 +77,18 @@ static lf_ret_t _TcpIpChannel_reset_socket(TcpIpChannel *self) { return LF_ERR; } } + if (self->send_failed_event_fds[0] > 0) { + if (close(self->send_failed_event_fds[0]) < 0) { + TCP_IP_CHANNEL_ERR("Error closing send_failed_event_fds[0] errno=%d", errno); + return LF_ERR; + } + } + if (self->send_failed_event_fds[1] > 0) { + if (close(self->send_failed_event_fds[1]) < 0) { + TCP_IP_CHANNEL_ERR("Error closing send_failed_event_fds[1] errno=%d", errno); + return LF_ERR; + } + } if ((self->fd = socket(self->protocol_family, SOCK_STREAM, 0)) < 0) { TCP_IP_CHANNEL_ERR("Error opening socket errno=%d", errno); @@ -93,6 +112,9 @@ static void _TcpIpChannel_spawn_worker_thread(TcpIpChannel *self) { int res; TCP_IP_CHANNEL_INFO("Spawning worker thread"); + // set terminate to false so the loop runs + self->terminate = false; + memset(&self->worker_thread_stack, 0, TCP_IP_CHANNEL_RECV_THREAD_STACK_SIZE); if (pthread_attr_init(&self->worker_thread_attr) != 0) { throw("pthread_attr_init failed"); @@ -101,6 +123,7 @@ static void _TcpIpChannel_spawn_worker_thread(TcpIpChannel *self) { TCP_IP_CHANNEL_RECV_THREAD_STACK_SIZE - TCP_IP_CHANNEL_RECV_THREAD_STACK_GUARD_SIZE) < 0) { throw("pthread_attr_setstack failed"); } + res = pthread_create(&self->worker_thread, &self->worker_thread_attr, _TcpIpChannel_worker_thread, self); if (res < 0) { throw("pthread_create failed"); @@ -111,6 +134,7 @@ static lf_ret_t _TcpIpChannel_server_bind(TcpIpChannel *self) { struct sockaddr_in serv_addr; serv_addr.sin_family = self->protocol_family; serv_addr.sin_port = htons(self->port); + TCP_IP_CHANNEL_INFO("Bind to %s:%u", self->host, self->port); // turn human-readable address into something the os can work with if (inet_pton(self->protocol_family, self->host, &serv_addr.sin_addr) <= 0) { @@ -122,6 +146,7 @@ static lf_ret_t _TcpIpChannel_server_bind(TcpIpChannel *self) { int ret = bind(self->fd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)); if (ret < 0) { TCP_IP_CHANNEL_ERR("Could not bind to %s:%d errno=%d", self->host, self->port, errno); + throw("Bind failed"); _TcpIpChannel_update_state(self, NETWORK_CHANNEL_STATE_CONNECTION_FAILED); return LF_ERR; } @@ -147,11 +172,15 @@ static lf_ret_t _TcpIpChannel_try_connect_server(NetworkChannel *untyped_self) { if (new_socket >= 0) { self->client = new_socket; FD_SET(new_socket, &self->set); + TCP_IP_CHANNEL_INFO("Connceted to client with address %s", inet_ntoa(address.sin_addr)); _TcpIpChannel_update_state(self, NETWORK_CHANNEL_STATE_CONNECTED); return LF_OK; } else { if (errno == EWOULDBLOCK || errno == EAGAIN) { - TCP_IP_CHANNEL_DEBUG("Accept failed. Try again. errno=%d", errno); + if (!self->has_warned_about_connection_failure) { + TCP_IP_CHANNEL_WARN("Accept failed with errno=%d. Will only print one warning.", errno); + self->has_warned_about_connection_failure = true; + } return LF_OK; } else { TCP_IP_CHANNEL_ERR("Accept failed. errno=%d", errno); @@ -177,6 +206,7 @@ static lf_ret_t _TcpIpChannel_try_connect_client(NetworkChannel *untyped_self) { int ret = connect(self->fd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)); if (ret == 0) { + TCP_IP_CHANNEL_INFO("Connected to server on %s:%d", self->host, self->port); _TcpIpChannel_update_state(self, NETWORK_CHANNEL_STATE_CONNECTED); return LF_OK; } else { @@ -185,7 +215,11 @@ static lf_ret_t _TcpIpChannel_try_connect_client(NetworkChannel *untyped_self) { TCP_IP_CHANNEL_DEBUG("Connection in progress!"); return LF_OK; } else { - TCP_IP_CHANNEL_ERR("Connect failed errno=%d", errno); + if (!self->has_warned_about_connection_failure) { + TCP_IP_CHANNEL_WARN("Connect to %s:%d failed with errno=%d. Will only print one error.", self->host, + self->port, errno); + self->has_warned_about_connection_failure = true; + } _TcpIpChannel_update_state(self, NETWORK_CHANNEL_STATE_CONNECTION_FAILED); return LF_ERR; } @@ -306,6 +340,7 @@ static lf_ret_t _TcpIpChannel_receive(NetworkChannel *untyped_self, FederateMess case ENOTCONN: case ECONNABORTED: TCP_IP_CHANNEL_ERR("Error recv from socket errno=%d", errno); + _TcpIpChannel_update_state(self, NETWORK_CHANNEL_STATE_LOST_CONNECTION); return LF_ERR; case EAGAIN: /* The socket has no new data to receive */ @@ -314,7 +349,8 @@ static lf_ret_t _TcpIpChannel_receive(NetworkChannel *untyped_self, FederateMess continue; } else if (bytes_read == 0) { // This means the connection was closed. - TCP_IP_CHANNEL_DEBUG("Other federate gracefully closed socket"); + _TcpIpChannel_update_state(self, NETWORK_CHANNEL_STATE_CLOSED); + TCP_IP_CHANNEL_WARN("Other federate closed socket"); return LF_ERR; } @@ -338,7 +374,11 @@ static lf_ret_t _TcpIpChannel_receive(NetworkChannel *untyped_self, FederateMess static void TcpIpChannel_close_connection(NetworkChannel *untyped_self) { TcpIpChannel *self = (TcpIpChannel *)untyped_self; - TCP_IP_CHANNEL_DEBUG("Close connection"); + TCP_IP_CHANNEL_DEBUG("Closing connection"); + + if (self->state != NETWORK_CHANNEL_STATE_CONNECTED) { + return; + } if (self->is_server && self->client != 0) { if (close(self->client) < 0) { @@ -360,15 +400,16 @@ static void *_TcpIpChannel_worker_thread(void *untyped_self) { TCP_IP_CHANNEL_INFO("Starting worker thread"); - // set terminate to false so the loop runs - self->terminate = false; - while (!self->terminate) { switch (_TcpIpChannel_get_state(self)) { case NETWORK_CHANNEL_STATE_OPEN: { /* try to connect */ if (self->is_server) { - _TcpIpChannel_server_bind(self); + ret = _TcpIpChannel_server_bind(self); + if (ret != LF_OK) { + _TcpIpChannel_update_state(self, NETWORK_CHANNEL_STATE_CONNECTION_FAILED); + break; + } _TcpIpChannel_try_connect_server(untyped_self); } else { _TcpIpChannel_try_connect_client(untyped_self); @@ -376,12 +417,12 @@ static void *_TcpIpChannel_worker_thread(void *untyped_self) { } break; case NETWORK_CHANNEL_STATE_CONNECTION_IN_PROGRESS: { - _env->platform->wait_for(_env->platform, self->super.expected_connect_duration); + _lf_environment->platform->wait_for(_lf_environment->platform, self->super.expected_connect_duration); } break; case NETWORK_CHANNEL_STATE_LOST_CONNECTION: case NETWORK_CHANNEL_STATE_CONNECTION_FAILED: { - _env->platform->wait_for(_env->platform, self->super.expected_connect_duration); + _lf_environment->platform->wait_for(_lf_environment->platform, self->super.expected_connect_duration); _TcpIpChannel_reset_socket(self); _TcpIpChannel_update_state(self, NETWORK_CHANNEL_STATE_OPEN); } break; @@ -435,6 +476,7 @@ static void *_TcpIpChannel_worker_thread(void *untyped_self) { } } + TCP_IP_CHANNEL_INFO("Worker thread terminates"); return NULL; } @@ -458,6 +500,7 @@ static void TcpIpChannel_free(NetworkChannel *untyped_self) { TCP_IP_CHANNEL_DEBUG("Stopping worker thread"); err = pthread_cancel(self->worker_thread); + if (err != 0) { TCP_IP_CHANNEL_ERR("Error canceling worker thread %d", err); } @@ -473,6 +516,7 @@ static void TcpIpChannel_free(NetworkChannel *untyped_self) { } } self->super.close_connection((NetworkChannel *)self); + pthread_mutex_destroy(&self->mutex); } static bool TcpIpChannel_is_connected(NetworkChannel *untyped_self) { @@ -481,18 +525,17 @@ static bool TcpIpChannel_is_connected(NetworkChannel *untyped_self) { return _TcpIpChannel_get_state(self) == NETWORK_CHANNEL_STATE_CONNECTED; } -void TcpIpChannel_ctor(TcpIpChannel *self, Environment *env, const char *host, unsigned short port, int protocol_family, - bool is_server) { +static bool TcpIpChannel_was_ever_connected(NetworkChannel *untyped_self) { + TcpIpChannel *self = (TcpIpChannel *)untyped_self; + return self->was_ever_connected; +} + +void TcpIpChannel_ctor(TcpIpChannel *self, const char *host, unsigned short port, int protocol_family, bool is_server) { assert(self != NULL); - assert(env != NULL); assert(host != NULL); - // Initialize global coap server if not already done - if (!_is_globals_initialized) { - _is_globals_initialized = true; - - // Set environment - _env = env; + if (pthread_mutex_init(&self->mutex, NULL) != 0) { + throw("Failed to initialize mutex"); } self->is_server = is_server; @@ -506,6 +549,7 @@ void TcpIpChannel_ctor(TcpIpChannel *self, Environment *env, const char *host, u self->state = NETWORK_CHANNEL_STATE_UNINITIALIZED; self->super.is_connected = TcpIpChannel_is_connected; + self->super.was_ever_connected = TcpIpChannel_was_ever_connected; self->super.open_connection = TcpIpChannel_open_connection; self->super.close_connection = TcpIpChannel_close_connection; self->super.send_blocking = TcpIpChannel_send_blocking; @@ -516,6 +560,8 @@ void TcpIpChannel_ctor(TcpIpChannel *self, Environment *env, const char *host, u self->receive_callback = NULL; self->federated_connection = NULL; self->worker_thread = 0; + self->has_warned_about_connection_failure = false; + self->was_ever_connected = false; _TcpIpChannel_reset_socket(self); _TcpIpChannel_spawn_worker_thread(self); diff --git a/src/platform/riot/coap_udp_ip_channel.c b/src/platform/riot/coap_udp_ip_channel.c index 07e11fb6..3c5d3b27 100644 --- a/src/platform/riot/coap_udp_ip_channel.c +++ b/src/platform/riot/coap_udp_ip_channel.c @@ -15,11 +15,10 @@ char _connection_thread_stack[THREAD_STACKSIZE_MAIN]; int _connection_thread_pid = 0; -static bool _is_globals_initialized = false; -static Environment *_env; +static bool _coap_is_globals_initialized = false; static void _CoapUdpIpChannel_update_state(CoapUdpIpChannel *self, NetworkChannelState new_state) { - COAP_UDP_IP_CHANNEL_DEBUG("Update state: %s => %s\n", NetworkChannel_state_to_string(self->state), + COAP_UDP_IP_CHANNEL_DEBUG("Update state: %s => %s", NetworkChannel_state_to_string(self->state), NetworkChannel_state_to_string(new_state)); // Store old state @@ -30,10 +29,14 @@ static void _CoapUdpIpChannel_update_state(CoapUdpIpChannel *self, NetworkChanne self->state = new_state; mutex_unlock(&self->state_mutex); + if (new_state == NETWORK_CHANNEL_STATE_CONNECTED) { + self->was_ever_connected = true; + } + // Inform runtime about new state if it changed from or to NETWORK_CHANNEL_STATE_CONNECTED if ((old_state == NETWORK_CHANNEL_STATE_CONNECTED && new_state != NETWORK_CHANNEL_STATE_CONNECTED) || (old_state != NETWORK_CHANNEL_STATE_CONNECTED && new_state == NETWORK_CHANNEL_STATE_CONNECTED)) { - _env->platform->new_async_event(_env->platform); + _lf_environment->platform->new_async_event(_lf_environment->platform); } // Let connection thread evaluate new state of this channel @@ -49,14 +52,14 @@ static void _CoapUdpIpChannel_update_state_if_not(CoapUdpIpChannel *self, Networ // Update the state of the channel itself mutex_lock(&self->state_mutex); if (self->state != if_not) { - COAP_UDP_IP_CHANNEL_DEBUG("Update state: %s => %s\n", NetworkChannel_state_to_string(self->state), + COAP_UDP_IP_CHANNEL_DEBUG("Update state: %s => %s", NetworkChannel_state_to_string(self->state), NetworkChannel_state_to_string(new_state)); self->state = new_state; } mutex_unlock(&self->state_mutex); // Inform runtime about new state - _env->platform->new_async_event(_env->platform); + _lf_environment->platform->new_async_event(_lf_environment->platform); } static NetworkChannelState _CoapUdpIpChannel_get_state(CoapUdpIpChannel *self) { @@ -71,9 +74,9 @@ static NetworkChannelState _CoapUdpIpChannel_get_state(CoapUdpIpChannel *self) { static CoapUdpIpChannel *_CoapUdpIpChannel_get_coap_channel_by_remote(const sock_udp_ep_t *remote) { CoapUdpIpChannel *channel; - for (size_t i = 0; i < _env->net_bundles_size; i++) { - if (_env->net_bundles[i]->net_channel->type == NETWORK_CHANNEL_TYPE_COAP_UDP_IP) { - channel = (CoapUdpIpChannel *)_env->net_bundles[i]->net_channel; + for (size_t i = 0; i < _lf_environment->net_bundles_size; i++) { + if (_lf_environment->net_bundles[i]->net_channel->type == NETWORK_CHANNEL_TYPE_COAP_UDP_IP) { + channel = (CoapUdpIpChannel *)_lf_environment->net_bundles[i]->net_channel; if (sock_udp_ep_equal(&channel->remote, remote)) { return channel; @@ -357,6 +360,11 @@ static bool CoapUdpIpChannel_is_connected(NetworkChannel *untyped_self) { return _CoapUdpIpChannel_get_state(self) == NETWORK_CHANNEL_STATE_CONNECTED; } +static bool CoapUdpIpChannel_was_ever_connected(NetworkChannel *untyped_self) { + CoapUdpIpChannel *self = (CoapUdpIpChannel *)untyped_self; + return self->was_ever_connected; +} + void *_CoapUdpIpChannel_connection_thread(void *arg) { COAP_UDP_IP_CHANNEL_DEBUG("Start connection thread"); (void)arg; @@ -395,18 +403,13 @@ void *_CoapUdpIpChannel_connection_thread(void *arg) { return NULL; } -void CoapUdpIpChannel_ctor(CoapUdpIpChannel *self, Environment *env, const char *remote_address, - int remote_protocol_family) { +void CoapUdpIpChannel_ctor(CoapUdpIpChannel *self, const char *remote_address, int remote_protocol_family) { assert(self != NULL); - assert(env != NULL); assert(remote_address != NULL); // Initialize global coap server if not already done - if (!_is_globals_initialized) { - _is_globals_initialized = true; - - // Set environment - _env = env; + if (!_coap_is_globals_initialized) { + _coap_is_globals_initialized = true; // Initialize coap server gcoap_register_listener(&_listener); @@ -421,6 +424,7 @@ void CoapUdpIpChannel_ctor(CoapUdpIpChannel *self, Environment *env, const char self->super.expected_connect_duration = COAP_UDP_IP_CHANNEL_EXPECTED_CONNECT_DURATION; self->super.type = NETWORK_CHANNEL_TYPE_COAP_UDP_IP; self->super.is_connected = CoapUdpIpChannel_is_connected; + self->super.was_ever_connected = CoapUdpIpChannel_was_ever_connected; self->super.open_connection = CoapUdpIpChannel_open_connection; self->super.close_connection = CoapUdpIpChannel_close_connection; self->super.send_blocking = CoapUdpIpChannel_send_blocking; @@ -432,6 +436,7 @@ void CoapUdpIpChannel_ctor(CoapUdpIpChannel *self, Environment *env, const char self->federated_connection = NULL; self->state = NETWORK_CHANNEL_STATE_UNINITIALIZED; self->state_mutex = (mutex_t)MUTEX_INIT; + self->was_ever_connected = false; // Convert host to udp socket if (inet_pton(remote_protocol_family, remote_address, self->remote.addr.ipv6) == 1) { diff --git a/src/queues.c b/src/queues.c index b835ce7f..7ebe6e3b 100644 --- a/src/queues.c +++ b/src/queues.c @@ -19,7 +19,7 @@ lf_ret_t EventQueue_insert(EventQueue *self, Event *event) { LF_DEBUG(QUEUE, "Inserting event with tag %" PRId64 ":%" PRIu32 " into EventQueue", event->tag.time, event->tag.microstep); if (self->size >= EVENT_QUEUE_SIZE) { - LF_ERR(QUEUE, "EventQueue is full"); + LF_ERR(QUEUE, "EventQueue is full has size %d", self->size); assert(false); return LF_OUT_OF_BOUNDS; } diff --git a/src/reaction.c b/src/reaction.c index d8a253d5..43f36ede 100644 --- a/src/reaction.c +++ b/src/reaction.c @@ -10,14 +10,14 @@ size_t Reaction_get_level(Reaction *self) { return self->level; } -static size_t calculate_port_level(Port *port) { - size_t current = 0; +int calculate_port_level(Port *port) { + int current = -1; if (port->conn_in) { Port *final_upstream_port = port->conn_in->get_final_upstream(port->conn_in); if (final_upstream_port) { for (size_t k = 0; k < final_upstream_port->sources.size; k++) { Reaction *upstream = final_upstream_port->sources.reactions[k]; - size_t upstream_level = upstream->get_level(upstream); + int upstream_level = upstream->get_level(upstream); if (upstream_level > current) { current = upstream_level; } @@ -27,23 +27,23 @@ static size_t calculate_port_level(Port *port) { for (size_t i = 0; i < port->sources.size; i++) { Reaction *source = port->sources.reactions[i]; - size_t source_level = source->get_level(source); + int source_level = source->get_level(source); if (source_level > current) { current = source_level; } } - LF_INFO(ENV, "Input port %p has level %d", port, current); + LF_DEBUG(ENV, "Input port %p has level %d", port, current); return current; } size_t Reaction_calculate_trigger_level(Reaction *self, Trigger *trigger) { - size_t max_level = 0; + int max_level = 0; if (trigger->type == TRIG_INPUT || trigger->type == TRIG_OUTPUT) { Port *port = (Port *)trigger; for (size_t j = 0; j < port->effects.size; j++) { if (port->effects.reactions[j] == self) { - size_t level_from_port = calculate_port_level(port) + 1; + int level_from_port = calculate_port_level(port) + 1; if (level_from_port > max_level) { max_level = level_from_port; } @@ -51,7 +51,7 @@ size_t Reaction_calculate_trigger_level(Reaction *self, Trigger *trigger) { } for (size_t j = 0; j < port->observers.size; j++) { if (port->observers.reactions[j] == self) { - size_t level_from_port = calculate_port_level(port) + 1; + int level_from_port = calculate_port_level(port) + 1; if (level_from_port > max_level) { max_level = level_from_port; } diff --git a/src/schedulers/dynamic/scheduler.c b/src/schedulers/dynamic/scheduler.c index f41b1c44..7b84614f 100644 --- a/src/schedulers/dynamic/scheduler.c +++ b/src/schedulers/dynamic/scheduler.c @@ -62,12 +62,11 @@ static lf_ret_t Scheduler_federated_acquire_tag(Scheduler *untyped_self, tag_t n FederatedConnectionBundle *bundle = env->net_bundles[i]; for (size_t j = 0; j < bundle->inputs_size; j++) { FederatedInputConnection *input = bundle->inputs[j]; - validate(input->safe_to_assume_absent == FOREVER); // TODO: We only support dataflow like things now // Find the max safe-to-assume-absent value and go to sleep waiting for this. if (lf_tag_compare(input->last_known_tag, next_tag) < 0) { LF_DEBUG(SCHED, "Input %p is unresolved, latest known tag was %" PRId64 ":%" PRIu32, input, input->last_known_tag.time, input->last_known_tag.microstep); - LF_DEBUG(SCHED, "Input %p has STAA of %" PRId64, input->safe_to_assume_absent); + LF_DEBUG(SCHED, "Input %p has STAA of %" PRId64, input, input->safe_to_assume_absent); if (input->safe_to_assume_absent > additional_sleep) { additional_sleep = input->safe_to_assume_absent; } @@ -77,7 +76,7 @@ static lf_ret_t Scheduler_federated_acquire_tag(Scheduler *untyped_self, tag_t n if (additional_sleep > 0) { LF_DEBUG(SCHED, "Need to sleep for additional %" PRId64 " ns", additional_sleep); - instant_t sleep_until = lf_time_add(env->get_logical_time(env), additional_sleep); + instant_t sleep_until = lf_time_add(next_tag.time, additional_sleep); return env->wait_until(env, sleep_until); } else { return LF_OK; @@ -234,8 +233,8 @@ void Scheduler_run(Scheduler *untyped_self) { tag_t next_tag; bool non_terminating = self->super.keep_alive || env->has_async_events; bool going_to_shutdown = false; - LF_INFO(SCHED, "Scheduler running with non_terminating=%d has_async_events=%d", non_terminating, - env->has_async_events); + LF_DEBUG(SCHED, "Scheduler running with non_terminating=%d has_async_events=%d", non_terminating, + env->has_async_events); env->enter_critical_section(env); @@ -244,8 +243,8 @@ void Scheduler_run(Scheduler *untyped_self) { LF_DEBUG(SCHED, "Next event is at %" PRId64 ":%" PRIu32, next_tag.time, next_tag.microstep); if (lf_tag_compare(next_tag, self->stop_tag) > 0) { - LF_INFO(SCHED, "Next event is beyond stop tag: %" PRId64 ":%" PRIu32, self->stop_tag.time, - self->stop_tag.microstep); + LF_DEBUG(SCHED, "Next event is beyond stop tag: %" PRId64 ":%" PRIu32, self->stop_tag.time, + self->stop_tag.microstep); next_tag = self->stop_tag; going_to_shutdown = true; } diff --git a/src/util.c b/src/util.c index 9c6c0f79..34064959 100644 --- a/src/util.c +++ b/src/util.c @@ -8,3 +8,13 @@ void lf_connect(Connection *connection, Port *upstream, Port *downstream) { connection->upstream = upstream; connection->register_downstream(connection, downstream); } + +void lf_connect_federated_output(Connection *connection, Port *output) { + validate(output->conns_out_registered < output->conns_out_size); + output->conns_out[output->conns_out_registered++] = connection; + connection->upstream = output; +} + +void lf_connect_federated_input(Connection *connection, Port *input) { + connection->register_downstream(connection, input); +} diff --git a/test/lf/Makefile b/test/lf/Makefile index 7250352a..8766b49e 100644 --- a/test/lf/Makefile +++ b/test/lf/Makefile @@ -1,6 +1,11 @@ # Very simple Makefile script to build and compile all the LF tests. SRCS = $(wildcard src/*.lf) BINS = $(patsubst src/%.lf, bin/%, $(SRCS)) +TIMEOUT_S = 60 + +SRCS_ONLY_BUILD = $(wildcard src/only_build/*.lf) +BINS_ONLY_BUILD = $(patsubst src/only_build/%.lf, bin/%, $(SRCS)) + LFC_PATH=../../lfc LFC = ${LFC_PATH}/build/install/lf-cli/bin/lfc @@ -12,7 +17,10 @@ build_lfc: bin/%: src/%.lf ${LFC} $^ -c - ./$@ + timeout ${TIMEOUT_S}s ./$@ +bin/%: src/only_build/%.lf + ${LFC} $^ -c + clean: rm -rf build bin src-gen \ No newline at end of file diff --git a/test/lf/src/FederatedAttr.lf b/test/lf/src/FederatedAttr.lf new file mode 100644 index 00000000..e59db42c --- /dev/null +++ b/test/lf/src/FederatedAttr.lf @@ -0,0 +1,34 @@ +target uC { + platform: Native, + timeout: 1 sec +} + +reactor Src(id: int = 0) { + output out: int + reaction(startup) -> out{= + printf("Hello from Src!\n"); + lf_set(out, self->id); + =} +} + +reactor Dst { + input in: int + reaction(startup) {= + printf("Hello from Dst!\n"); + =} + reaction(in) {= + printf("Received %d from Src\n", in->value); + =} +} + +federated reactor { + + @interface_tcp(name="if1", address="127.0.0.1") + r1 = new Src(id=42) + + @interface_tcp(name="if1", address="127.0.0.1") + r2 = new Dst() + + @link(left="if1", right="if1", server_side="right", server_port=1042) + r1.out -> r2.in +} diff --git a/test/lf/src/FederatedBank.lf b/test/lf/src/FederatedBank.lf new file mode 100644 index 00000000..b588e302 --- /dev/null +++ b/test/lf/src/FederatedBank.lf @@ -0,0 +1,58 @@ +target uC { + platform: Native, + timeout: 1 sec, + logging: Info +} + + +reactor Src { + output out: int + + reaction(startup) -> out {= + printf("Hello from Src!\n"); + lf_set(out, 42); + =} + reaction(shutdown) {= + printf("Src is shutting down\n"); + =} +} + +reactor Fed(bank_idx: int = 0) { + output out: int + input in: int + input in2: int + state check1: bool = false + state check2: bool = false + + reaction(in) -> out {= + printf("Received %d from src \n", in->value); + lf_set(out, self->bank_idx); + validate(in->value == 42); + validate(!self->check2); + self->check1 = true; + =} + + reaction(in2) {= + printf("Received %d from myself\n", in2->value); + validate(in2->value == self->bank_idx); + validate(self->check1); + self->check2 = true; + =} + + reaction(shutdown) {= + validate(self->check2); + validate(self->check1); + printf("Federate %d is shutting down\n", self->bank_idx); + =} +} + + +federated reactor { + src = new Src() + dests = new [2] Fed() + + (src.out)+ -> dests.in + + dests.out -> dests.in2 + +} \ No newline at end of file diff --git a/test/lf/src/FederatedBankMultiport.lf b/test/lf/src/FederatedBankMultiport.lf new file mode 100644 index 00000000..0e428d3c --- /dev/null +++ b/test/lf/src/FederatedBankMultiport.lf @@ -0,0 +1,35 @@ +target uC { + platform: Native, + timeout: 5 sec, + logging: debug, + build-type: Release +} + +reactor Fed(bank_idx: int = 0) { + output [4] out: int + input [4] in: int + + reaction(startup) -> out {= + printf("Hello from Fed %u\n", self->bank_idx); + for (int i = 0; ibank_idx); + } + =} + + reaction(in) {= + for (int i = 0; ivalue == i); + if (self->bank_idx == 0) { + printf("%"PRId64" Fed %u Received %d from %d \n", env->get_elapsed_logical_time(env), self->bank_idx, in[i]->value, i); + } + } + } + =} +} + + +federated reactor { + feds = new [4] Fed() + feds.out ~> interleaved(feds.in) after 100 msec +} \ No newline at end of file diff --git a/test/lf/src/FederatedConnection.lf b/test/lf/src/FederatedConnection.lf new file mode 100644 index 00000000..95164d63 --- /dev/null +++ b/test/lf/src/FederatedConnection.lf @@ -0,0 +1,36 @@ +target uC { + platform: Native, + timeout: 1sec +} + +reactor Src(id: int = 0) { + output out: int + reaction(startup) -> out{= + printf("Hello from Src!\n"); + lf_set(out, self->id); + =} +} + +reactor Dst { + input in: int + state check: bool = false + reaction(startup) {= + printf("Hello from Dst!\n"); + =} + reaction(in) {= + printf("Received %d from Src\n", in->value); + validate(in->value == 42); + self->check = true; + env->request_shutdown(env); + =} + + reaction(shutdown) {= + validate(self->check); + =} +} + +federated reactor { + r1 = new Src(id=42) + r2 = new Dst() + r1.out -> r2.in +} \ No newline at end of file diff --git a/test/lf/src/FederatedLoopbackConnection.lf b/test/lf/src/FederatedLoopbackConnection.lf new file mode 100644 index 00000000..e47dea5f --- /dev/null +++ b/test/lf/src/FederatedLoopbackConnection.lf @@ -0,0 +1,33 @@ +target uC { + platform: Native, + timeout: 1 msec +} + +reactor Src(id: int = 0) { + output out: int + input in: int + reaction(startup) -> out{= + printf("Hello from Src!\n"); + lf_set(out, self->id); + =} + reaction(in) {= + printf("Received %d from myself\n", in->value); + =} +} + +reactor Dst { + input in: int + reaction(startup) {= + printf("Hello from Dst!\n"); + =} + reaction(in) {= + printf("Received %d from Src\n", in->value); + =} +} + +federated reactor { + r1 = new Src(id=42) + r2 = new Dst() + r1.out -> r2.in + r1.out -> r1.in +} \ No newline at end of file diff --git a/test/lf/src/SimpleFederated.lf b/test/lf/src/SimpleFederated.lf new file mode 100644 index 00000000..1515ea05 --- /dev/null +++ b/test/lf/src/SimpleFederated.lf @@ -0,0 +1,14 @@ +target uC { + platform: Native +} + +reactor R(id: int = 0) { + reaction(startup) {= + printf("Hello from Federate %d!\n", self->id); + =} +} + +federated reactor { + r1 = new R(id=1) + r2 = new R(id=2) +} \ No newline at end of file diff --git a/test/lf/src/failing/SimpleFederated.lf b/test/lf/src/failing/SimpleFederated.lf deleted file mode 100644 index 9f68f4b9..00000000 --- a/test/lf/src/failing/SimpleFederated.lf +++ /dev/null @@ -1,20 +0,0 @@ -target uC - - -reactor X { - reaction(startup) {= - printf("Hello from LF!\n"); - =} -} - -reactor R { - reaction(startup) {= - printf("Hello from LF!\n"); - =} - x = new X() -} - -federated reactor { - r1 = new R() - r2 = new R() -} \ No newline at end of file diff --git a/test/lf/src/only_build/FederatedAttrCustom.lf b/test/lf/src/only_build/FederatedAttrCustom.lf new file mode 100644 index 00000000..542d9427 --- /dev/null +++ b/test/lf/src/only_build/FederatedAttrCustom.lf @@ -0,0 +1,33 @@ +target uC { + platform: RIOT, + timeout: 1 msec +} + +reactor Src(id: int = 0) { + output out: int + reaction(startup) -> out{= + printf("Hello from Src!\n"); + lf_set(out, self->id); + =} +} + +reactor Dst { + input in: int + reaction(startup) {= + printf("Hello from Dst!\n"); + =} + reaction(in) {= + printf("Received %d from Src\n", in->value); + =} +} + +federated reactor { + + @interface_custom(name="MyInterface", include="my_interface.h", args="1") + r1 = new Src(id=42) + + @interface_custom(name="MyInterface", include="my_interface.h", args="2") + r2 = new Dst() + + r1.out -> r2.in +} diff --git a/test/lf/src/only_build/FederatedCoap.lf b/test/lf/src/only_build/FederatedCoap.lf new file mode 100644 index 00000000..94a098b6 --- /dev/null +++ b/test/lf/src/only_build/FederatedCoap.lf @@ -0,0 +1,34 @@ +target uC { + platform: RIOT, + timeout: 1 msec +} + +reactor Src(id: int = 0) { + output out: int + reaction(startup) -> out{= + printf("Hello from Src!\n"); + lf_set(out, self->id); + =} +} + +reactor Dst { + input in: int + reaction(startup) {= + printf("Hello from Dst!\n"); + =} + reaction(in) {= + printf("Received %d from Src\n", in->value); + =} +} + +federated reactor { + + @interface_coap(name="if1", address="127.0.0.1") + r1 = new Src(id=42) + + @interface_coap(name="if1", address="127.0.0.1") + r2 = new Dst() + + @link(left="if1", right="if1") + r1.out -> r2.in +} diff --git a/test/platform/riot/coap_channel_test/main.c b/test/platform/riot/coap_channel_test/main.c index 310da758..b9ee35c6 100644 --- a/test/platform/riot/coap_channel_test/main.c +++ b/test/platform/riot/coap_channel_test/main.c @@ -14,6 +14,7 @@ Reactor parent; Environment env; +Environment *_lf_environment = &env; FederatedConnectionBundle bundle; FederatedConnectionBundle *net_bundles[] = {&bundle}; @@ -30,7 +31,7 @@ void setUp(void) { env.net_bundles_size = 1; /* init channel */ - CoapUdpIpChannel_ctor(&_coap_channel, &env, REMOTE_ADDRESS, REMOTE_PROTOCOL_FAMILY); + CoapUdpIpChannel_ctor(&_coap_channel, REMOTE_ADDRESS, REMOTE_PROTOCOL_FAMILY); /* init bundle */ FederatedConnectionBundle_ctor(&bundle, &parent, channel, NULL, NULL, 0, NULL, NULL, 0); diff --git a/test/unit/action_lib.h b/test/unit/action_lib.h index ff4d87b7..12bb086a 100644 --- a/test/unit/action_lib.h +++ b/test/unit/action_lib.h @@ -48,10 +48,11 @@ LF_REACTOR_CTOR_SIGNATURE(ActionLib) { self->cnt = 0; } +ActionLib my_reactor; +Environment env; +Environment *_lf_environment = &env; void action_lib_start(interval_t duration) { - ActionLib my_reactor; - Environment env; Environment_ctor(&env, (Reactor *)&my_reactor); ActionLib_ctor(&my_reactor, NULL, &env); env.scheduler->duration = duration; diff --git a/test/unit/deadline_test.c b/test/unit/deadline_test.c index 22b4a6a9..789761c2 100644 --- a/test/unit/deadline_test.c +++ b/test/unit/deadline_test.c @@ -45,6 +45,8 @@ LF_REACTOR_CTOR_SIGNATURE(TimerTest) { TimerTest my_reactor; Environment env; +Environment* _lf_environment = &env; + void test_simple() { Environment_ctor(&env, (Reactor *)&my_reactor); env.scheduler->duration = MSEC(100); diff --git a/test/unit/delayed_conn_test.c b/test/unit/delayed_conn_test.c index f359c085..05d74014 100644 --- a/test/unit/delayed_conn_test.c +++ b/test/unit/delayed_conn_test.c @@ -93,18 +93,19 @@ LF_REACTOR_CTOR_SIGNATURE(Main) { LF_REACTOR_CTOR(Main); LF_DEFINE_CHILD_OUTPUT_ARGS(sender, out, 1, 1); - LF_INITIALIZE_CHILD_REACTOR_WITH_PARAMETERS(Sender, sender, 1, _sender_out_args); + LF_INITIALIZE_CHILD_REACTOR_WITH_PARAMETERS(Sender, sender, 1, &_sender_out_args[0][0]); LF_DEFINE_CHILD_INPUT_ARGS(receiver, in, 1, 1); - LF_INITIALIZE_CHILD_REACTOR_WITH_PARAMETERS(Receiver, receiver, 1, _receiver_in_args); + LF_INITIALIZE_CHILD_REACTOR_WITH_PARAMETERS(Receiver, receiver, 1, &_receiver_in_args[0][0]); LF_INITIALIZE_DELAYED_CONNECTION(Main, sender_out, 1, 1); - LF_CONN_REGISTER_UPSTREAM(sender_out, self->sender, out, 1, 1); - LF_CONN_REGISTER_DOWNSTREAM(sender_out, 1,1, self->receiver, in, 1, 1); + lf_connect(&self->sender_out[0][0].super.super, &self->sender->out[0].super, &self->receiver->in[0].super); } +Environment env; +Environment* _lf_environment = &env; + void test_simple() { Main main; - Environment env; Environment_ctor(&env, (Reactor *)&main); Main_ctor(&main, NULL, &env); env.scheduler->duration = MSEC(100); diff --git a/test/unit/event_queue_test.c b/test/unit/event_queue_test.c index b449784c..b9a44545 100644 --- a/test/unit/event_queue_test.c +++ b/test/unit/event_queue_test.c @@ -31,6 +31,7 @@ void test_insert(void) { TEST_ASSERT_EQUAL(lf_tag_compare(eptr.tag, e3.tag), 0); } +Environment * _lf_environment = NULL; int main(void) { UNITY_BEGIN(); RUN_TEST(test_insert); diff --git a/test/unit/port_test.c b/test/unit/port_test.c index 52bbfb8d..efd7547e 100644 --- a/test/unit/port_test.c +++ b/test/unit/port_test.c @@ -92,18 +92,19 @@ LF_REACTOR_CTOR_SIGNATURE(Main) { LF_REACTOR_CTOR(Main); LF_DEFINE_CHILD_OUTPUT_ARGS(sender, out,1,1); - LF_INITIALIZE_CHILD_REACTOR_WITH_PARAMETERS(Sender, sender,1, &_sender_out_args[0]); + LF_INITIALIZE_CHILD_REACTOR_WITH_PARAMETERS(Sender, sender,1, &_sender_out_args[0][0]); LF_DEFINE_CHILD_INPUT_ARGS(receiver, in,1,1); - LF_INITIALIZE_CHILD_REACTOR_WITH_PARAMETERS(Receiver, receiver,1, &_receiver_in_args[0]); + LF_INITIALIZE_CHILD_REACTOR_WITH_PARAMETERS(Receiver, receiver,1, &_receiver_in_args[0][0]); LF_INITIALIZE_LOGICAL_CONNECTION(Main, sender_out, 1, 1); - LF_CONN_REGISTER_UPSTREAM(sender_out, self->sender, out, 1, 1); - LF_CONN_REGISTER_DOWNSTREAM(sender_out, 1,1,self->receiver, in, 1, 1); + lf_connect(&self->sender_out[0][0].super.super, &self->sender->out[0].super, &self->receiver->in[0].super); } +Environment env; +Environment* _lf_environment = &env; + void test_simple() { Main main; - Environment env; Environment_ctor(&env, (Reactor *)&main); Main_ctor(&main, NULL, &env); env.scheduler->duration = MSEC(100); diff --git a/test/unit/reaction_queue_test.c b/test/unit/reaction_queue_test.c index 373310ca..4bbc5412 100644 --- a/test/unit/reaction_queue_test.c +++ b/test/unit/reaction_queue_test.c @@ -1,3 +1,4 @@ +#include "reactor-uc/environment.h" #include "reactor-uc/queues.h" #include "unity.h" @@ -38,6 +39,7 @@ void test_levels_with_gaps(void) { TEST_ASSERT_EQUAL_PTR(r, &rs[i]); } } +Environment * _lf_environment = NULL; int main(void) { UNITY_BEGIN(); diff --git a/test/unit/tcp_channel_test.c b/test/unit/tcp_channel_test.c index dea324c7..8277ab40 100644 --- a/test/unit/tcp_channel_test.c +++ b/test/unit/tcp_channel_test.c @@ -16,6 +16,7 @@ Reactor parent; Environment env; +Environment *_lf_environment = &env; FederatedConnectionBundle server_bundle; FederatedConnectionBundle client_bundle; FederatedConnectionBundle *net_bundles[] = {&server_bundle, &client_bundle}; @@ -35,10 +36,10 @@ void setUp(void) { env.net_bundles_size = 2; /* init server */ - TcpIpChannel_ctor(&_server_tcp_channel, &env, HOST, PORT, AF_INET, true); + TcpIpChannel_ctor(&_server_tcp_channel, HOST, PORT, AF_INET, true); /* init client */ - TcpIpChannel_ctor(&_client_tcp_channel, &env, HOST, PORT, AF_INET, false); + TcpIpChannel_ctor(&_client_tcp_channel, HOST, PORT, AF_INET, false); /* init bundles */ FederatedConnectionBundle_ctor(&server_bundle, &parent, server_channel, NULL, NULL, 0, NULL, NULL, 0); diff --git a/test/unit/timer_test.c b/test/unit/timer_test.c index ed6a9bbd..aa1cb54f 100644 --- a/test/unit/timer_test.c +++ b/test/unit/timer_test.c @@ -32,6 +32,7 @@ LF_REACTOR_CTOR_SIGNATURE(TimerTest) { TimerTest my_reactor; Environment env; +Environment* _lf_environment = &env; void test_simple() { Environment_ctor(&env, (Reactor *)&my_reactor); env.scheduler->duration = MSEC(100);