Browse Source

initial version

thomas 4 years ago
parent
commit
f083d0a93d
9 changed files with 341 additions and 0 deletions
  1. 8 0
      .gitignore
  2. 20 0
      CMakeLists.txt
  3. 30 0
      include/software_watchdog.h
  4. 12 0
      src/CMakeLists.txt
  5. 118 0
      src/software_watchdog.c
  6. 26 0
      tests/CMakeLists.txt
  7. 35 0
      tests/test1.c
  8. 36 0
      tests/test2.c
  9. 56 0
      tests/test3.c

+ 8 - 0
.gitignore

@@ -0,0 +1,8 @@
+build
+build-arm
+nbproject
+CMakeLists.txt.user
+.vscode
+.vs
+CMakeSettings.json
+out

+ 20 - 0
CMakeLists.txt

@@ -0,0 +1,20 @@
+cmake_minimum_required (VERSION 2.6)
+project(software-watchdog)
+
+# set(CMAKE_VERBOSE_MAKEFILE ON)
+set(CMAKE_BUILD_TYPE DEBUG)
+
+option(BUILD_TESTING "Build tests for software-watchdog." ON)
+
+set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
+set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
+set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
+
+# link_directories(${LIBRARY_OUTPUT_DIRECTORY})
+add_subdirectory(src)
+if(BUILD_TESTING)
+    enable_testing()
+    add_subdirectory(tests)
+endif()
+
+export (PACKAGE software-watchdog)

+ 30 - 0
include/software_watchdog.h

@@ -0,0 +1,30 @@
+#ifndef SOFTWAREWATCHDOG_H
+#define SOFTWAREWATCHDOG_H
+#include <time.h>
+#include <stdbool.h>
+#include <pthread.h>
+
+// Software WatchDog (swd)
+
+void printHelloWorld();
+
+typedef void (*timeout_cb_t)(void *watchdog);
+
+typedef struct {
+    char *name;
+    timeout_cb_t timeout_cb;
+    struct timespec timeout;
+    struct timespec latestKick;
+    void *userData;
+    bool isEnabled;
+    pthread_t thread_id;
+    pthread_mutex_t mutex;
+} SoftwareWatchdog;
+
+// return -1 on error
+int swd_setup(SoftwareWatchdog *watchdog, char *name, timeout_cb_t timeout_cb, struct timespec timeout, void *userData);
+int swd_enable(SoftwareWatchdog *watchdog);
+void swd_disable(SoftwareWatchdog *watchdog);
+void swd_kick(SoftwareWatchdog *watchdog);
+
+#endif /* SOFTWAREWATCHDOG_H */

+ 12 - 0
src/CMakeLists.txt

@@ -0,0 +1,12 @@
+cmake_minimum_required (VERSION 2.6)
+set(CMAKE_BUILD_TYPE DEBUG)
+
+# TODO: make this repo public
+# TODO: add the possibility to build as library
+
+set(SOFTWAREWATCHDOG_SOURCE_FILES 
+    software_watchdog.c )
+add_library (software-watchdog ${SOFTWAREWATCHDOG_SOURCE_FILES})
+target_include_directories (software-watchdog PUBLIC ../include)
+target_link_libraries(software-watchdog PUBLIC
+    rt)

+ 118 - 0
src/software_watchdog.c

@@ -0,0 +1,118 @@
+// reference: http://man7.org/linux/man-pages/man2/timer_create.2.html
+
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <pthread.h>
+#include <unistd.h>
+#include <stdbool.h>
+#include "software_watchdog.h"
+
+#define CLOCKID CLOCK_REALTIME
+#define SIG SIGRTMIN
+
+void printHelloWorld() {
+    printf("HelloWorld\n");
+}
+
+void subTime(struct timespec *t1, struct timespec *subtrahend) {
+    // get the time difference between now and latestKick
+    t1->tv_sec -= subtrahend->tv_sec;
+    if (t1->tv_nsec < subtrahend->tv_nsec) {
+        // e.g. t1 = 5s.10000ns, subtrahend = 3s.90000ns
+        t1->tv_nsec = 1000000000 - (subtrahend->tv_nsec - t1->tv_nsec);
+        t1->tv_sec += 1;
+    }
+    else {
+        // e.g. t1 = 5s.50000ns, subtrahend = 3s.30000ns
+        t1->tv_nsec -= subtrahend->tv_nsec;
+    }
+}
+
+int compareTime(struct timespec* t1, struct timespec* t2) {
+    if(t1->tv_sec < t2->tv_sec)
+        return -1;
+    if(t1->tv_sec > t2->tv_sec)
+        return 1;
+    if(t1->tv_nsec < t2->tv_nsec)
+        return -1;
+    if(t1->tv_nsec > t2->tv_nsec)
+        return 1;
+    return 0; // equal
+}
+
+void *timer (void *data) {
+    SoftwareWatchdog* watchdog = data;
+    pthread_mutex_lock(&(watchdog->mutex));
+
+    struct timespec now, diff, remaining_sleeptime;
+    remaining_sleeptime = watchdog->timeout;
+
+    pthread_mutex_unlock(&(watchdog->mutex));
+
+    while (watchdog->isEnabled) {
+
+        // printf ("%s: remaining_sleeptime: %ds,%dns", watchdog->name, remaining_sleeptime); fflush(stdout);
+        // printf ("%s: sleep start\n", watchdog->name); fflush(stdout);
+        nanosleep(&remaining_sleeptime, &remaining_sleeptime);
+        // printf ("%s: sleep end\n", watchdog->name); fflush(stdout);
+
+        // get the current time
+        clock_gettime(CLOCK_REALTIME, &(now));
+
+        // get the time difference between now and latestKick
+        pthread_mutex_lock(&(watchdog->mutex));
+        diff = now;
+        subTime(&diff, &(watchdog->latestKick));
+        pthread_mutex_unlock(&(watchdog->mutex));
+
+        if (watchdog->isEnabled && compareTime(&diff, &(watchdog->timeout)) > 0) { // the deadline has been missed
+            // printf ("%s: calling timeout_cb\n", watchdog->name); fflush(stdout);
+            watchdog->timeout_cb(watchdog);
+            remaining_sleeptime = watchdog->timeout;
+        }
+        else {
+            // printf ("%s: no need to call timeout_cb\n", watchdog->name); fflush(stdout);
+            remaining_sleeptime = watchdog->timeout;
+            subTime(&remaining_sleeptime, &diff);
+        }
+    }
+}
+
+int swd_setup(SoftwareWatchdog* watchdog, char *name, timeout_cb_t timeout_cb, struct timespec timeout, void *userData){
+    // TODO: check input parameters
+    watchdog->name = strdup(name);
+    watchdog->timeout_cb = timeout_cb;
+    watchdog->timeout = timeout;
+    watchdog->userData = userData;
+    watchdog->isEnabled = false;
+    pthread_mutex_init(&(watchdog->mutex), NULL);
+}
+
+int swd_enable(SoftwareWatchdog* watchdog) {
+    if (watchdog->isEnabled) {
+        // That's really bad and should not happen
+        return -1;
+    }
+    // create and detach the timer
+    swd_kick(watchdog); // kick it, to initialize the watchdog->latestKick
+    watchdog->isEnabled = true;
+    pthread_create(&(watchdog->thread_id), NULL, timer, (void*)watchdog);
+    pthread_detach(watchdog->thread_id);
+    // printf("%s: threadid: %d\n", watchdog->name, watchdog->thread_id); fflush(stdout);
+    return 0;
+}
+
+void swd_disable(SoftwareWatchdog* watchdog) {
+    watchdog->isEnabled = false;
+}
+
+void swd_kick(SoftwareWatchdog* watchdog) {
+    pthread_mutex_lock(&(watchdog->mutex));
+    clock_gettime(CLOCK_REALTIME, &(watchdog->latestKick));
+    pthread_mutex_unlock(&(watchdog->mutex));
+}
+
+

+ 26 - 0
tests/CMakeLists.txt

@@ -0,0 +1,26 @@
+cmake_minimum_required (VERSION 2.6)
+set(CMAKE_BUILD_TYPE DEBUG)
+
+set(SOFTWAREWATCHDOG_INCLUDE_DIRECTORY ../include)
+    
+MESSAGE("Building software-watchdog test applications")
+
+include_directories(${SOFTWAREWATCHDOG_INCLUDE_DIRECTORY})
+
+add_executable(test1 test1.c)
+target_link_libraries(test1 LINK_PUBLIC 
+    software-watchdog
+    pthread)
+add_test(NAME test1 COMMAND test1)
+
+add_executable(test2 test2.c)
+target_link_libraries(test2 LINK_PUBLIC 
+    software-watchdog
+    pthread)
+add_test(NAME test2 COMMAND test2)
+
+add_executable(test3 test3.c)
+target_link_libraries(test3 LINK_PUBLIC 
+    software-watchdog
+    pthread)
+add_test(NAME test3 COMMAND test3)

+ 35 - 0
tests/test1.c

@@ -0,0 +1,35 @@
+#include <stdio.h>
+#include <unistd.h>
+#include <software_watchdog.h>
+
+// in this test it is expected that the watchdog_timeout_cb is called
+
+int test_result = -1;
+
+void watchdog_timeout_cb(void *data)
+{
+    SoftwareWatchdog *watchdog = data;
+    printf("!! watchdog_timeout_cb called !! \n");
+    fflush(stdout);
+    test_result = 0;
+}
+
+int main(void){
+    printHelloWorld();
+
+    SoftwareWatchdog watchdog1;
+    struct timespec timeout;
+    timeout.tv_nsec = 100*1000000;
+    timeout.tv_sec = 2;
+    swd_setup(&watchdog1, "watchdog1", watchdog_timeout_cb, timeout, NULL);
+    swd_enable(&watchdog1);
+
+    printf("Main: Sleeping for some seconds:\n");
+    for (int i = 3; i > 0; i--) {
+        printf("%d\n", i);
+        fflush(stdout);
+        sleep(1);
+    }
+
+    return test_result;
+}

+ 36 - 0
tests/test2.c

@@ -0,0 +1,36 @@
+#include <stdio.h>
+#include <unistd.h>
+#include <software_watchdog.h>
+
+// in this test it is expected that the watchdog_timeout_cb not called
+
+int test_result = 0;
+
+void watchdog_timeout_cb(void *data)
+{
+    SoftwareWatchdog *watchdog = data;
+    printf("!! watchdog_timeout_cb called !! \n");
+    fflush(stdout);
+    test_result = -1;
+}
+
+int main(void){
+    printHelloWorld();
+
+    SoftwareWatchdog watchdog1;
+    struct timespec timeout;
+    timeout.tv_nsec = 100*1000000;
+    timeout.tv_sec = 1;
+    swd_setup(&watchdog1, "watchdog1", watchdog_timeout_cb, timeout, NULL);
+    swd_enable(&watchdog1);
+
+    printf("Main: Sleeping for some seconds:\n");
+    for (int i = 3; i > 0; i--) {
+        swd_kick(&watchdog1);
+        printf("%d\n", i);
+        fflush(stdout);
+        sleep(1);
+    }
+
+    return test_result;
+}

+ 56 - 0
tests/test3.c

@@ -0,0 +1,56 @@
+#include <stdio.h>
+#include <unistd.h>
+#include <software_watchdog.h>
+
+// in this test it is expected that the watchdog_timeout_cb1 is called, but not watchdog_timeout_cb2
+
+int test_result = -1;
+
+bool cb1called = false;
+bool cb2called = false;
+
+void watchdog_timeout_cb1(void *data)
+{
+    SoftwareWatchdog *watchdog = data;
+    printf("!! watchdog_timeout_cb1 called !! \n");
+    fflush(stdout);
+    cb1called = true;
+}
+
+void watchdog_timeout_cb2(void *data)
+{
+    SoftwareWatchdog *watchdog = data;
+    printf("!! watchdog_timeout_cb2 called !! \n");
+    fflush(stdout);
+    cb2called = true;
+}
+
+int main(void){
+    printHelloWorld();
+
+    SoftwareWatchdog watchdog1;
+    struct timespec timeout1;
+    timeout1.tv_nsec = 500*1000000;
+    timeout1.tv_sec = 1;
+    swd_setup(&watchdog1, "watchdog1", watchdog_timeout_cb1, timeout1, NULL);
+    swd_enable(&watchdog1);
+
+    SoftwareWatchdog watchdog2;
+    struct timespec timeout2;
+    timeout2.tv_nsec = 500*1000000;
+    timeout2.tv_sec = 1;
+    swd_setup(&watchdog2, "watchdog2", watchdog_timeout_cb2, timeout2, NULL);
+    swd_enable(&watchdog2);
+
+    printf("Main: Sleeping for some seconds:\n");
+    for (int i = 3; i > 0; i--) {
+        swd_kick(&watchdog2);
+        printf("%d\n", i);
+        fflush(stdout);
+        sleep(1);
+    }
+
+    if(cb1called && !cb2called)
+        return 0;
+    return -1;
+}