From 1575444934b9e8c55c10b47d1d5e67961f6358b6 Mon Sep 17 00:00:00 2001 From: Bernhard Luedtke Date: Thu, 5 Dec 2024 11:30:51 +0100 Subject: [PATCH] added "is bidib running" util and a physical test occupancy observer/watchdog prototype. --- include/highlevel/bidib_highlevel_util.h | 10 +- src/highlevel/bidib_highlevel_util.c | 4 + test/physical/swtbahn-full/testsuite.c | 170 +++++++++++++++++++++++ test/physical/test_common.c | 39 +++++- test/physical/test_common.h | 3 + 5 files changed, 220 insertions(+), 6 deletions(-) diff --git a/include/highlevel/bidib_highlevel_util.h b/include/highlevel/bidib_highlevel_util.h index 1214b6e..ddccb7e 100644 --- a/include/highlevel/bidib_highlevel_util.h +++ b/include/highlevel/bidib_highlevel_util.h @@ -97,12 +97,20 @@ uint8_t *bidib_read_error_message(void); */ void bidib_flush(void); +/** + * Check if bidib is currently running. + * + * @return true if bidib is running + * @return false otherwise + */ +bool bidib_is_running(); + /** * Customised syslog function to prepend libbidib to log messages. * This is needed to differentiate log messages that originate from this bidib * library or from a user application. */ - void syslog_libbidib(int priority, const char *format, ...); +void syslog_libbidib(int priority, const char *format, ...); #endif diff --git a/src/highlevel/bidib_highlevel_util.c b/src/highlevel/bidib_highlevel_util.c index 71430ab..de7fb33 100644 --- a/src/highlevel/bidib_highlevel_util.c +++ b/src/highlevel/bidib_highlevel_util.c @@ -257,6 +257,10 @@ void bidib_stop(void) { } } +bool bidib_is_running() { + return bidib_running; +} + void syslog_libbidib(int priority, const char *format, ...) { char string[1024]; va_list arg; diff --git a/test/physical/swtbahn-full/testsuite.c b/test/physical/swtbahn-full/testsuite.c index ffb0ed4..6e6965d 100644 --- a/test/physical/swtbahn-full/testsuite.c +++ b/test/physical/swtbahn-full/testsuite.c @@ -58,6 +58,78 @@ void testsuite_case_pointSerial(t_testsuite_test_result *result) { testsuite_case_pointSerial_common(result); } +/* +typedef struct { + char *train; + char **forbidden_segments; + int forbidden_segments_len; + // if true, only checks whether any of the forbidden segments gets occupied, + // not if it gets occupied *by the train*. + bool check_occ_only; + // To be set by the caller to request the observer to terminate. + volatile bool stop_requested; +} t_bidib_occ_observer_info_multi; +*/ +typedef struct { + char *train; + char *forbidden_segment; + // if true, only checks whether any of the forbidden segments gets occupied, + // not if it gets occupied *by the train*. + bool check_occ_only; + // To be set by the caller to request the observer to terminate. + volatile bool stop_requested; +} t_bidib_occ_observer_info; + +void free_obs_info_util(t_bidib_occ_observer_info *obs_info_ptr) { + if (obs_info_ptr != NULL) { + if (obs_info_ptr->forbidden_segment != NULL) { + free(obs_info_ptr->forbidden_segment); + } + if (obs_info_ptr->train != NULL) { + free(obs_info_ptr->train); + } + free(obs_info_ptr); + obs_info_ptr = NULL; + } +} + +// Expects the arg pointer to be a pointer to a t_bidib_occ_observer_info struct. +static void *occupancy_observer(void *arg) { + if (arg == NULL) { + printf("testsuite: Occ-Observer exit: arg is NULL\n"); + pthread_exit(NULL); + } + t_bidib_occ_observer_info *obs_info = arg; + t_bidib_dcc_address_query tr_dcc_addr = bidib_get_train_dcc_addr(obs_info->train); + if (!tr_dcc_addr.known) { + printf("testsuite: Occ-Observer exit: tr_dcc_addr unknown\n"); + pthread_exit(NULL); + } + while (!obs_info->stop_requested) { + const char *i_segmt = obs_info->forbidden_segment; + bool violation = false; + if (i_segmt == NULL) { + continue; + } else if (obs_info->check_occ_only) { + violation = testsuite_is_segment_occupied(i_segmt); + } else { + violation = testsuite_is_segment_occupied_by_dcc_addr(i_segmt, tr_dcc_addr.dcc_address); + } + if (violation) { + printf("!!!\nOccupancy Observer: Train %s (or something else) occupies forbidden " + "segment %s! Stopping train, then stopping bidib.\n!!!\n", + obs_info->train, i_segmt); + bidib_set_train_speed(obs_info->train, 0, "master"); + bidib_flush(); + // Stop via the signal handler for consistent debugging + testsuite_signal_callback_handler(-1); + pthread_exit(NULL); + } + usleep(100000); // 0.1s + } + pthread_exit(NULL); +} + bool route1(const char *train) { if (!testsuite_trainReady(train, "seg58")) { return false; @@ -429,6 +501,18 @@ void testsuite_case_swtbahnFullTrackCoverage(const char *train) { } } +/* +typedef struct { + char *train; + char *forbidden_segment; + // if true, only checks whether any of the forbidden segments gets occupied, + // not if it gets occupied *by the train*. + bool check_occ_only; + // To be set by the caller to request the observer to terminate. + volatile bool stop_requested; +} t_bidib_occ_observer_info; +*/ + static void *route99(void *arg) { const char *train1 = arg; @@ -478,19 +562,65 @@ static void *route99(void *arg) { printf("testsuite: route99 - one or more points are not in expected aspect."); pthread_exit(NULL); } + + static pthread_t route99_observer_thread; + + t_bidib_occ_observer_info *obs1_info = malloc(sizeof(t_bidib_occ_observer_info)); + obs1_info->check_occ_only = false; + obs1_info->train = strdup(train1); + obs1_info->stop_requested = false; + obs1_info->forbidden_segment = strdup("seg60"); // skip 1 to give enough time for drive_to. + + pthread_create(&route99_observer_thread, NULL, occupancy_observer, (void*) obs1_info); + testsuite_driveTo("seg57", 50, train1); + + obs1_info->stop_requested = true; + pthread_join(route99_observer_thread, NULL); + testsuite_set_signal("signal30", "aspect_stop"); + + + obs1_info->stop_requested = false; + free(obs1_info->forbidden_segment); + obs1_info->forbidden_segment = strdup("seg66"); // skip 1 to give enough time for drive_to. + pthread_create(&route99_observer_thread, NULL, occupancy_observer, (void*) obs1_info); + testsuite_driveTo("seg64", 50, train1); + + obs1_info->stop_requested = true; + pthread_join(route99_observer_thread, NULL); + testsuite_set_signal("signal33", "aspect_stop"); testsuite_set_signal("signal35a", "aspect_stop"); testsuite_set_signal("signal35b", "aspect_stop"); + + obs1_info->stop_requested = false; + free(obs1_info->forbidden_segment); + obs1_info->forbidden_segment = strdup("seg40"); // skip 1 to give enough time for drive_to. + pthread_create(&route99_observer_thread, NULL, occupancy_observer, (void*) obs1_info); + testsuite_driveTo("seg69", 50, train1); + + obs1_info->stop_requested = true; + pthread_join(route99_observer_thread, NULL); + testsuite_set_signal("signal37", "aspect_stop"); + + obs1_info->stop_requested = false; + free(obs1_info->forbidden_segment); + obs1_info->forbidden_segment = strdup("seg47"); + pthread_create(&route99_observer_thread, NULL, occupancy_observer, (void*) obs1_info); + testsuite_driveTo("seg46", 50, train1); + + obs1_info->stop_requested = true; + pthread_join(route99_observer_thread, NULL); + sleep(1); testsuite_driveToStop("seg47", 20, train1); @@ -504,24 +634,64 @@ static void *route99(void *arg) { testsuite_set_signal("signal32", "aspect_go"); sleep(1); + + obs1_info->stop_requested = false; + free(obs1_info->forbidden_segment); + obs1_info->forbidden_segment = strdup("seg48"); // skip 1 to give enough time for drive_to. + pthread_create(&route99_observer_thread, NULL, occupancy_observer, (void*) obs1_info); testsuite_driveTo("seg45", -50, train1); + + obs1_info->stop_requested = true; + pthread_join(route99_observer_thread, NULL); + testsuite_set_signal("signal26", "aspect_stop"); + + obs1_info->stop_requested = false; + free(obs1_info->forbidden_segment); + obs1_info->forbidden_segment = strdup("seg34"); // skip 1 to give enough time for drive_to. + pthread_create(&route99_observer_thread, NULL, occupancy_observer, (void*) obs1_info); + testsuite_driveTo("seg67", -50, train1); + + obs1_info->stop_requested = true; + pthread_join(route99_observer_thread, NULL); + testsuite_set_signal("signal38", "aspect_stop"); testsuite_set_signal("signal36", "aspect_stop"); + + obs1_info->stop_requested = false; + free(obs1_info->forbidden_segment); + obs1_info->forbidden_segment = strdup("seg60"); // skip 1 to give enough time for drive_to. + pthread_create(&route99_observer_thread, NULL, occupancy_observer, (void*) obs1_info); + testsuite_driveTo("seg62", -50, train1); + obs1_info->stop_requested = true; + pthread_join(route99_observer_thread, NULL); + testsuite_set_signal("signal34", "aspect_stop"); testsuite_set_signal("signal32", "aspect_stop"); + + obs1_info->stop_requested = false; + free(obs1_info->forbidden_segment); + obs1_info->forbidden_segment = strdup("seg59"); // for very last drive_to. + pthread_create(&route99_observer_thread, NULL, occupancy_observer, (void*) obs1_info); + testsuite_driveTo("seg60", -50, train1); testsuite_driveTo("seg53", -40, train1); testsuite_driveTo("seg58", -30, train1); sleep(2); testsuite_driveToStop("seg58", -20, train1); + + obs1_info->stop_requested = true; + pthread_join(route99_observer_thread, NULL); + sleep(2); + + free_obs_info_util(obs1_info); pthread_exit(NULL); } diff --git a/test/physical/test_common.c b/test/physical/test_common.c index 263425c..d336378 100644 --- a/test/physical/test_common.c +++ b/test/physical/test_common.c @@ -119,16 +119,16 @@ void testsuite_stopBidib(void) { } void testsuite_signal_callback_handler(int signum) { - printf("testsuite: SIGINT - before stopping, debug logs:\n"); + printf("testsuite: SIG %d - before stopping, debug logs:\n", signum); printf(" Track output states:\n"); testsuite_logAllTrackOutputStates(); printf("\n"); printf(" Booster power states:\n"); testsuite_logAllBoosterPowerStates(); printf("\n"); - printf("testsuite: SIGINT - now stopping libbidib.\n"); + printf("testsuite: SIG %d - now stopping libbidib.\n", signum); testsuite_stopBidib(); - printf("testsuite: SIGINT - libbidib stopped.\n"); + printf("testsuite: SIG %d - libbidib stopped.\n", signum); exit(signum); } @@ -201,7 +201,7 @@ void testsuite_driveTo_legacy(const char *segment, int speed, const char *train) bidib_set_train_speed(train, speed, "master"); bidib_flush(); long counter = 0; - while (1) { + while (bidib_is_running()) { t_bidib_train_position_query trainPosition = bidib_get_train_position(train); for (size_t i = 0; i < trainPosition.length; i++) { if (strcmp(segment, trainPosition.segments[i]) == 0) { @@ -236,7 +236,7 @@ void testsuite_driveTo(const char *segment, int speed, const char *train) { t_bidib_dcc_address_query tr_dcc_addr = bidib_get_train_dcc_addr(train); t_bidib_dcc_address dcc_address; long counter = 0; - while (1) { + while (bidib_is_running()) { t_bidib_segment_state_query seg_query = bidib_get_segment_state(segment); for (size_t j = 0; j < seg_query.data.dcc_address_cnt; j++) { dcc_address = seg_query.data.dcc_addresses[j]; @@ -269,6 +269,35 @@ void testsuite_driveToStop(const char *segment, int speed, const char *train) { bidib_flush(); } +bool testsuite_is_segment_occupied(const char *segment) { + t_bidib_segment_state_query seg_query = bidib_get_segment_state(segment); + bool ret = seg_query.known && seg_query.data.occupied; + bidib_free_segment_state_query(seg_query); + return ret; +} + +bool testsuite_is_segment_occupied_by_train(const char *segment, const char *train) { + t_bidib_dcc_address_query tr_dcc_addr = bidib_get_train_dcc_addr(train); + return testsuite_is_segment_occupied_by_dcc_addr(segment, tr_dcc_addr.dcc_address); +} + +bool testsuite_is_segment_occupied_by_dcc_addr(const char *segment, t_bidib_dcc_address dcc_address) { + t_bidib_segment_state_query seg_query = bidib_get_segment_state(segment); + if (!(seg_query.known && seg_query.data.occupied)) { + bidib_free_segment_state_query(seg_query); + return false; + } + for (size_t j = 0; j < seg_query.data.dcc_address_cnt; j++) { + t_bidib_dcc_address *seg_dcc_j = &seg_query.data.dcc_addresses[j]; + if (dcc_address.addrh == seg_dcc_j->addrh && dcc_address.addrl == seg_dcc_j->addrl) { + bidib_free_segment_state_query(seg_query); + return true; + } + } + bidib_free_segment_state_query(seg_query); + return false; +} + void testsuite_set_signal(const char *signal, const char *aspect) { bidib_set_signal(signal, aspect); bidib_flush(); diff --git a/test/physical/test_common.h b/test/physical/test_common.h index 2445159..63d73f0 100644 --- a/test/physical/test_common.h +++ b/test/physical/test_common.h @@ -75,6 +75,9 @@ void testsuite_logAllBoosterPowerStates(); bool testsuite_trainReady(const char *train, const char *segment); void testsuite_driveTo(const char *segment, int speed, const char *train); void testsuite_driveToStop(const char *segment, int speed, const char *train); +bool testsuite_is_segment_occupied(const char *segment); +bool testsuite_is_segment_occupied_by_train(const char *segment, const char *train); +bool testsuite_is_segment_occupied_by_dcc_addr(const char *segment, t_bidib_dcc_address dcc_address); // Accessories void testsuite_set_signal(const char *signal, const char *aspect);