Skip to content

Commit

Permalink
added "is bidib running" util and a physical test occupancy observer/…
Browse files Browse the repository at this point in the history
…watchdog prototype.
  • Loading branch information
BLuedtke committed Dec 5, 2024
1 parent eec2489 commit 1575444
Show file tree
Hide file tree
Showing 5 changed files with 220 additions and 6 deletions.
10 changes: 9 additions & 1 deletion include/highlevel/bidib_highlevel_util.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
4 changes: 4 additions & 0 deletions src/highlevel/bidib_highlevel_util.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
170 changes: 170 additions & 0 deletions test/physical/swtbahn-full/testsuite.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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);

Expand All @@ -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);
}

Expand Down
39 changes: 34 additions & 5 deletions test/physical/test_common.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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];
Expand Down Expand Up @@ -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();
Expand Down
3 changes: 3 additions & 0 deletions test/physical/test_common.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down

0 comments on commit 1575444

Please sign in to comment.