From 6bc73fa166744e91af4bdc92a2f181c3a86c9642 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Heredero?= Date: Tue, 29 Aug 2023 14:11:48 +0200 Subject: [PATCH 1/2] merge alive_checker and watchdog to alive class --- 306-controller_interface.X/app/can_message.c | 14 +- .../app/factory/factory.c | 34 ++- .../app/factory/factory.h | 11 +- 306-controller_interface.X/middleware/alive.c | 259 ++++++++++++++++ 306-controller_interface.X/middleware/alive.h | 178 +++++++++++ .../middleware/alive_checker.c | 176 ----------- .../middleware/alive_checker.h | 134 -------- .../middleware/watchdog.c | 90 ------ .../middleware/watchdog.h | 83 ----- .../nbproject/configurations.xml | 8 +- UML/alive.pdf | Bin 2639 -> 3736 bytes UML/alive.uxf | 287 +++++++++++------- 12 files changed, 650 insertions(+), 624 deletions(-) create mode 100644 306-controller_interface.X/middleware/alive.c create mode 100644 306-controller_interface.X/middleware/alive.h delete mode 100644 306-controller_interface.X/middleware/alive_checker.c delete mode 100644 306-controller_interface.X/middleware/alive_checker.h delete mode 100644 306-controller_interface.X/middleware/watchdog.c delete mode 100644 306-controller_interface.X/middleware/watchdog.h diff --git a/306-controller_interface.X/app/can_message.c b/306-controller_interface.X/app/can_message.c index 05f929a..2e5cca0 100644 --- a/306-controller_interface.X/app/can_message.c +++ b/306-controller_interface.X/app/can_message.c @@ -37,7 +37,7 @@ void CM_processIncome(uint8_t idSender, uint8_t idMsg, uint32_t data){ CAR_CST.CONTROL_ALIVE_TIME = tmpData.separate.byte3; MEM_write_1_byte(MEMADD_CONTROL_STEERING_MODE, CAR_CST.CONTROL_STEERING_MODE); MEM_write_1_byte(MEMADD_CONTROL_ALIVE_TIME, CAR_CST.CONTROL_ALIVE_TIME); - WATCHDOG_setTime(WDcontroller(), CAR_CST.CONTROL_ALIVE_TIME); + ALIVE_setAliveTime(ALcontroller(), CAR_CST.CONTROL_ALIVE_TIME); } if(idMsg == 0x1) { // CONTROL_SPEED_FACTOR @@ -78,7 +78,7 @@ void CM_processIncome(uint8_t idSender, uint8_t idMsg, uint32_t data){ MEM_write_1_byte(MEMADD_JOYSTICK_PARAM2, CAR_CST.JOYSTICK_PARAM2); MEM_write_1_byte(MEMADD_JOYSTICK_ALIVE_TIME, CAR_CST.JOYSTICK_ALIVE_TIME); - ALIVE_CHECKER_setAliveTime(ACjoy(), CAR_CST.JOYSTICK_ALIVE_TIME); + ALIVE_setAliveTime(ALjoy(), CAR_CST.JOYSTICK_ALIVE_TIME); CM_JOY_SETUP(NULL); } @@ -107,15 +107,15 @@ void CM_processIncome(uint8_t idSender, uint8_t idMsg, uint32_t data){ if(idMsg == 0xF) { // JOY_ALIVE // - - - - - ALIVE_CHECKER_ISALIVE(ACjoy()); - if(ACjoy()->state == STAC_DEAD){ + ALIVE_ISALIVE(ALjoy()); + if(ALjoy()->state == STAL_DEAD){ CAR_CST.JOYSTICK_MODE = MEM_read_1_byte(MEMADD_JOYSTICK_MODE); CAR_CST.JOYSTICK_PARAM1 = MEM_read_1_byte(MEMADD_JOYSTICK_PARAM1); CAR_CST.JOYSTICK_PARAM2 = MEM_read_1_byte(MEMADD_JOYSTICK_PARAM2); CAR_CST.JOYSTICK_ALIVE_TIME = MEM_read_1_byte(MEMADD_JOYSTICK_ALIVE_TIME); - ALIVE_CHECKER_setAliveTime(ACjoy(), CAR_CST.JOYSTICK_ALIVE_TIME); - ALIVE_CHECKER_emitBorn(ACjoy(), 0, 0); - ALIVE_CHECKER_emitReady(ACjoy(), 100, 0); + ALIVE_setAliveTime(ALjoy(), CAR_CST.JOYSTICK_ALIVE_TIME); + ALIVE_emitBorn(ALjoy(), 0, 0); + ALIVE_emitReady(ALjoy(), 100, 0); } } break; diff --git a/306-controller_interface.X/app/factory/factory.c b/306-controller_interface.X/app/factory/factory.c index 11b5ec4..fe819c8 100644 --- a/306-controller_interface.X/app/factory/factory.c +++ b/306-controller_interface.X/app/factory/factory.c @@ -30,12 +30,12 @@ LED* l8() { return &theFactory.l8_; } -WATCHDOG* WDcontroller(){ - return &theFactory.WDcontroller_; +ALIVE* ALcontroller(){ + return &theFactory.ALcontroller_; } -ALIVE_CHECKER* ACjoy() { - return &theFactory.ACjoy_; +ALIVE* ALjoy(){ + return &theFactory.ALjoy_; } @@ -67,10 +67,10 @@ void Factory_init() { MEM_init(); // TODO init watchdog with EPROM CST - WATCHDOG_init(WDcontroller()); - WATCHDOG_setTime(WDcontroller(), CAR_CST.CONTROL_ALIVE_TIME); + ALIVE_init(ALcontroller()); + ALIVE_setAliveTime(ALcontroller(), CAR_CST.CONTROL_ALIVE_TIME); - ALIVE_CHECKER_init(ACjoy()); + ALIVE_init(ALjoy()); } //connect objects if required @@ -78,20 +78,22 @@ void Factory_build() { ECAN_SetRXBnInterruptHandler(CAN_newMsg); CAN_onReceiveCan(CM_processIncome); - WATCHDOG_onAlive(WDcontroller(), CM_CONTROLLER_ALIVE, NULL); - ALIVE_CHECKER_onSetup(ACjoy(), CM_JOY_SETUP, NULL); - ALIVE_CHECKER_setAliveTime(ACjoy(), CAR_CST.JOYSTICK_ALIVE_TIME); - ALIVE_CHECKER_onBorn(ACjoy(), LED_on, l1()); - ALIVE_CHECKER_onDead(ACjoy(), LED_off, l1()); + ALIVE_onAlive(ALcontroller(), CM_CONTROLLER_ALIVE, NULL); + + ALIVE_onSetup(ALjoy(), CM_JOY_SETUP, NULL); + ALIVE_setAliveTime(ALjoy(), CAR_CST.JOYSTICK_ALIVE_TIME); + ALIVE_onBorn(ALjoy(), LED_on, l1()); + ALIVE_onDead(ALjoy(), LED_off, l1()); } //start all state machines void Factory_start() { CAN_startBehaviour(); - WATCHDOG_startBehaviour(WDcontroller()); - ALIVE_CHECKER_startBehaviour(ACjoy()); - ALIVE_CHECKER_emitBorn(ACjoy(), 100, 0); - ALIVE_CHECKER_emitReady(ACjoy(), 200, 0); + + ALIVE_startBehaviourSender(ALcontroller()); + ALIVE_startBehaviourChecker(ALjoy()); + ALIVE_emitBorn(ALjoy(), 100, 0); + ALIVE_emitReady(ALjoy(), 200, 0); } diff --git a/306-controller_interface.X/app/factory/factory.h b/306-controller_interface.X/app/factory/factory.h index b847d38..bcbc830 100644 --- a/306-controller_interface.X/app/factory/factory.h +++ b/306-controller_interface.X/app/factory/factory.h @@ -16,9 +16,8 @@ #include "../can_message.h" #include "../../board/led/led.h" #include "../../board/button/button.h" -#include "../../middleware/alive_checker.h" +#include "../../middleware/alive.h" #include "../../middleware/can_interface.h" -#include "../../middleware/watchdog.h" #include "../../middleware/eeprom.h" @@ -32,8 +31,8 @@ typedef struct { LED l7_; LED l8_; - WATCHDOG WDcontroller_; - ALIVE_CHECKER ACjoy_; + ALIVE ALcontroller_; + ALIVE ALjoy_; } Factory; @@ -52,8 +51,8 @@ LED* l6(); LED* l7(); LED* l8(); -WATCHDOG* WDcontroller(); -ALIVE_CHECKER* ACjoy(); +ALIVE* ALcontroller(); +ALIVE* ALjoy(); #endif \ No newline at end of file diff --git a/306-controller_interface.X/middleware/alive.c b/306-controller_interface.X/middleware/alive.c new file mode 100644 index 0000000..146e9f3 --- /dev/null +++ b/306-controller_interface.X/middleware/alive.c @@ -0,0 +1,259 @@ +/** + * @author Rémi Heredero + * @version 1.0.0 + * @date August 2023 + * @file alive.c + */ + +#include "alive.h" + +void ALIVE_init(ALIVE* me){ + me->state = STAL_INIT; + me->isAlive = false; + me->checker = false; + me->sender = false; + me->haveBreak = true; + me->aliveTime = 10; + me->setup.f = NULL; + me->born.f = NULL; + me->wait.f = NULL; + me->dead.f = NULL; + me->alive.f = NULL; + me->break_cb.f = NULL; +} + +void ALIVE_startBehaviourChecker(ALIVE* me){ + POST(me, &ALIVE_processEvent, evALinitChecker, 0, 0); +} + +void ALIVE_startBehaviourSender(ALIVE* me){ + POST(me, &ALIVE_processEvent, evALinitSender, 0, 0); +} + +bool ALIVE_processEvent(Event* ev) { + bool processed = false; + ALIVE* me = (ALIVE*)Event_getTarget(ev); + ALIVE_STATES oldState = me->state; + evIDT evid = Event_getId(ev); + uint64_t data = Event_getData(ev); + + switch (me->state) { // onState + case STAL_INIT: + if (ev->id == evALinitChecker) { + me->state = STAL_SETUP; + } + if (ev->id == evALinitSender) { + me->state = STAL_ALIVE; + ALIVE_emitPoll(me, me->aliveTime*10, 0); + } + break; + + case STAL_SETUP: + if (ev->id == evALborn) { + me->state = STAL_BORN; + } + break; + + case STAL_BORN: + if (ev->id == evALready) { + me->state = STAL_WAIT; + ALIVE_emitPoll(me, me->aliveTime*10, 0); + } + break; + + case STAL_WAIT: + if (ev->id == evALpoll) { + + if (me->aliveTime == 0) { + if (me->haveBreak){ + me->state = STAL_BREAK; + } + } else if (me->isAlive){ + me->state = STAL_WAIT; + ALIVE_emitPoll(me, me->aliveTime*10, 0); + } else { + me->state = STAL_DEAD; + } + me->isAlive = false; + + } + break; + + case STAL_DEAD: + if (ev->id == evALborn) { + me->state = STAL_BORN; + } + break; + + case STAL_ALIVE: + if (ev->id == evALpoll) { + if (me->alive.f != NULL) { + me->alive.f(me->alive.p); + } + if (me->aliveTime == 0) { + if (me->haveBreak){ + me->state == STAL_BREAK; + } + } else { + ALIVE_emitPoll(me, me->aliveTime*10, 0); + } + } + break; + + case STAL_BREAK: + if (ev->id == evALstart) { + ALIVE_emitPoll(me, me->aliveTime*10, 0); + if (me->checker) { + me->state = STAL_WAIT; + } + if (me->sender) { + me->state = STAL_ALIVE; + } + } + break; + } + + if(oldState != me->state){ + switch (oldState) { // onExit + case STAL_INIT: + break; + + case STAL_SETUP: + break; + + case STAL_BORN: + break; + + case STAL_WAIT: + break; + + case STAL_DEAD: + break; + + case STAL_ALIVE: + break; + + case STAL_BREAK: + break; + } + + switch (me->state) { // onEntry + case STAL_INIT: + break; + + case STAL_SETUP: + if (me->setup.f != NULL) { + me->setup.f(me->setup.p); + } + break; + + case STAL_BORN: + if (me->born.f != NULL) { + me->born.f(me->born.p); + } + break; + + case STAL_WAIT: + if (me->wait.f != NULL) { + me->wait.f(me->wait.p); + } + break; + + case STAL_DEAD: + if (me->dead.f != NULL) { + me->dead.f(me->dead.p); + } + break; + + case STAL_ALIVE: + break; + + case STAL_BREAK: + if (me->break_cb.f != NULL) { + me->break_cb.f(me->break_cb.p); + } + break; + } + + processed = true; + } + return processed; +} + +/************* + * Callbacks * + *************/ + +void ALIVE_onSetup(ALIVE* me, ALIVE_CALLBACK_FUNCTION f, void* p) { + me->setup.f = f; + me->setup.p = p; +} + +void ALIVE_onBorn(ALIVE* me, ALIVE_CALLBACK_FUNCTION f, void* p) { + me->born.f = f; + me->born.p = p; +} + +void ALIVE_onWait(ALIVE* me, ALIVE_CALLBACK_FUNCTION f, void* p) { + me->wait.f = f; + me->wait.p = p; +} + +void ALIVE_onDead(ALIVE* me, ALIVE_CALLBACK_FUNCTION f, void* p) { + me->dead.f = f; + me->dead.p = p; +} + +void ALIVE_onAlive(ALIVE* me, ALIVE_CALLBACK_FUNCTION f, void* p) { + me->alive.f = f; + me->alive.p = p; +} + +void ALIVE_onBreak(ALIVE* me, ALIVE_CALLBACK_FUNCTION f, void* p) { + me->break_cb.f = f; + me->break_cb.p = p; +} + +/************ + * EMITTERS * + ************/ + +void ALIVE_emitInitSender(ALIVE* me, uint16_t t, int64_t data) { + POST(me, &ALIVE_processEvent, evALinitSender, t, data); +} + +void ALIVE_emitBorn(ALIVE* me, uint16_t t, int64_t data) { + POST(me, &ALIVE_processEvent, evALborn, t, data); +} + +void ALIVE_emitReady(ALIVE* me, uint16_t t, int64_t data) { + POST(me, &ALIVE_processEvent, evALready, t, data); +} + +void ALIVE_emitPoll(ALIVE* me, uint16_t t, int64_t data) { + POST(me, &ALIVE_processEvent, evALpoll, t, data); +} + +void ALIVE_emitStart(ALIVE* me, uint16_t t, int64_t data) { + POST(me, &ALIVE_processEvent, evALstart, t, data); +} + +/*********** + * SETTERS * + ***********/ + +void ALIVE_setIsAlive(ALIVE* me, bool v) { + me->isAlive = v; +} + +void ALIVE_setHaveBreak(ALIVE* me, bool v) { + me->haveBreak = v; +} + +void ALIVE_setAliveTime(ALIVE* me, uint8_t v) { + me->aliveTime = v; +} + +void ALIVE_ISALIVE(ALIVE* me) { + ALIVE_setIsAlive(me, true); +} diff --git a/306-controller_interface.X/middleware/alive.h b/306-controller_interface.X/middleware/alive.h new file mode 100644 index 0000000..749d9a9 --- /dev/null +++ b/306-controller_interface.X/middleware/alive.h @@ -0,0 +1,178 @@ +/** + * @author Rémi Heredero + * @version 1.0.0 + * @date August 2023 + * @file alive.h + */ +#ifndef ALIVE_H +#define ALIVE_H + +#include "../xf/xf.h" + +typedef enum { + STAL_INIT, + STAL_SETUP, + STAL_BORN, + STAL_WAIT, + STAL_DEAD, + STAL_ALIVE, + STAL_BREAK +} ALIVE_STATES; + +typedef enum { + evALinitChecker = 15, + evALinitSender, + evALborn, + evALready, + evALpoll, + evALstart +} ALIVE_EVENTS; + +typedef void (*ALIVE_CALLBACK_FUNCTION)(void*); +typedef struct { + ALIVE_CALLBACK_FUNCTION f; // function + void* p; // param(s) +} ALIVE_CALLBACK; + +typedef struct { + ALIVE_STATES state; + bool isAlive; + bool checker; + bool sender; + bool haveBreak; + uint8_t aliveTime; + ALIVE_CALLBACK setup; + ALIVE_CALLBACK born; + ALIVE_CALLBACK wait; + ALIVE_CALLBACK dead; + ALIVE_CALLBACK alive; + ALIVE_CALLBACK break_cb; +} ALIVE; + +/** + * Initialize the ALIVE + * @param me the ALIVE itself + */ +void ALIVE_init(ALIVE* me); + +/** + * Start the ALIVE state machine for checker part + * @param me the ALIVE itself + */ +void ALIVE_startBehaviourChecker(ALIVE* me); + +/** + * Start the ALIVE state machine for sender part + * @param me the ALIVE itself + */ +void ALIVE_startBehaviourSender(ALIVE* me); + +/** + * Process the event + * @param ev the event to process + * @return true if the event is processed + */ +bool ALIVE_processEvent(Event* ev); + +/************* + * Callbacks * + *************/ + +/** + * Set the callback function to call when the ALIVE is entering state setup + * @param me the ALIVE itself + * @param f the function to call + * @param p the param(s) to pass to the function + */ +void ALIVE_onSetup(ALIVE* me, ALIVE_CALLBACK_FUNCTION f, void* p); + +/** + * Set the callback function to call when the ALIVE is entering state born + * @param me the ALIVE itself + * @param f the function to call + * @param p the param(s) to pass to the function + */ +void ALIVE_onBorn(ALIVE* me, ALIVE_CALLBACK_FUNCTION f, void* p); + +/** + * Set the callback function to call when the ALIVE is entering state wait + * @param me the ALIVE itself + * @param f the function to call + * @param p the param(s) to pass to the function + */ +void ALIVE_onWait(ALIVE* me, ALIVE_CALLBACK_FUNCTION f, void* p); + +/** + * Set the callback function to call when the ALIVE is entering state dead + * @param me the ALIVE itself + * @param f the function to call + * @param p the param(s) to pass to the function + */ +void ALIVE_onDead(ALIVE* me, ALIVE_CALLBACK_FUNCTION f, void* p); + +/** + * Set the callback function to call when the ALIVE is entering state alive + * @param me the ALIVE itself + * @param f the function to call + * @param p the param(s) to pass to the function + */ +void ALIVE_onAlive(ALIVE* me, ALIVE_CALLBACK_FUNCTION f, void* p); + +/** + * Set the callback function to call when the ALIVE is entering state break + * @param me the ALIVE itself + * @param f the function to call + * @param p the param(s) to pass to the function + */ +void ALIVE_onBreak(ALIVE* me, ALIVE_CALLBACK_FUNCTION f, void* p); + +/************ + * EMITTERS * + ************/ + +/** + * Emit the born event + * @param me the ALIVE itself + * @param t time to wait in ms before triggering event + * @param data data to put on the event for XF + */ +void ALIVE_emitBorn(ALIVE* me, uint16_t t, int64_t data); + +/** + * Emit the ready event + * @param me the ALIVE itself + * @param t time to wait in ms before triggering event + * @param data data to put on the event for XF + */ +void ALIVE_emitReady(ALIVE* me, uint16_t t, int64_t data); + +/** + * Emit the poll event + * @param me the ALIVE itself + * @param t time to wait in ms before triggering event + * @param data data to put on the event for XF + */ +void ALIVE_emitPoll(ALIVE* me, uint16_t t, int64_t data); + +/** + * Emit the start event + * @param me the ALIVE itself + * @param t time to wait in ms before triggering event + * @param data data to put on the event for XF + */ +void ALIVE_emitStart(ALIVE* me, uint16_t t, int64_t data); + + +/*********** + * SETTERS * + ***********/ + +void ALIVE_setIsAlive(ALIVE* me, bool v); + +void ALIVE_setHaveBreak(ALIVE* me, bool v); + +void ALIVE_setAliveTime(ALIVE* me, uint8_t v); + +void ALIVE_ISALIVE(ALIVE* me); + +#endif diff --git a/306-controller_interface.X/middleware/alive_checker.c b/306-controller_interface.X/middleware/alive_checker.c deleted file mode 100644 index 00466a4..0000000 --- a/306-controller_interface.X/middleware/alive_checker.c +++ /dev/null @@ -1,176 +0,0 @@ -/** - * @author Rémi Heredero - * @version 1.0.0 - * @date August 2023 - * @file alive_checker.c - */ - -#include "alive_checker.h" - -void ALIVE_CHECKER_init(ALIVE_CHECKER* me){ - me->state = STAC_INIT; - me->isAlive = false; - me->aliveTime = 10; - me->setup.f = NULL; - me->born.f = NULL; - me->wait.f = NULL; - me->dead.f = NULL; -} - -void ALIVE_CHECKER_startBehaviour(ALIVE_CHECKER* me){ - POST(me, &ALIVE_CHECKER_processEvent, evACinit, 0, 0); -} - -bool ALIVE_CHECKER_processEvent(Event* ev) { - bool processed = false; - ALIVE_CHECKER* me = (ALIVE_CHECKER*)Event_getTarget(ev); - ALIVE_CHECKER_STATES oldState = me->state; - evIDT evid = Event_getId(ev); - uint64_t data = Event_getData(ev); - - switch (me->state) { // onState - case STAC_INIT: - if (ev->id == evACinit) { - me->state = STAC_SETUP; - } - break; - - case STAC_SETUP: - if (ev->id == evACborn) { - me->state = STAC_BORN; - } - break; - - case STAC_BORN: - if (ev->id == evACready) { - me->state = STAC_WAIT; - ALIVE_CHECKER_emitPoll(me, me->aliveTime*10, 0); - } - break; - - case STAC_WAIT: - if (ev->id == evACpoll) { - if (me->isAlive) { - me->state = STAC_WAIT; - ALIVE_CHECKER_emitPoll(me, me->aliveTime*10, 0); - } else { - me->state = STAC_DEAD; - } - me->isAlive = false; - } - break; - - case STAC_DEAD: - if(ev->id == evACborn) { - me->state = STAC_BORN; - } - break; - } - - if(oldState != me->state){ - switch (oldState) { // onExit - case STAC_INIT: - break; - - case STAC_SETUP: - break; - - case STAC_BORN: - break; - - case STAC_WAIT: - break; - - case STAC_DEAD: - break; - } - - switch (me->state) { // onEntry - case STAC_INIT: - break; - - case STAC_SETUP: - if (me->setup.f != NULL) { - me->setup.f(me->setup.p); - } - break; - - case STAC_BORN: - if (me->born.f != NULL) { - me->born.f(me->born.p); - } - break; - - case STAC_WAIT: - if (me->wait.f != NULL) { - me->wait.f(me->wait.p); - } - break; - - case STAC_DEAD: - if (me->dead.f != NULL) { - me->dead.f(me->dead.p); - } - break; - } - - processed = true; - } - return processed; -} - -/************* - * Callbacks * - *************/ - -void ALIVE_CHECKER_onSetup(ALIVE_CHECKER* me, ALIVE_CHECKER_CALLBACK_FUNCTION f, void* p) { - me->setup.f = f; - me->setup.p = p; -} - -void ALIVE_CHECKER_onBorn(ALIVE_CHECKER* me, ALIVE_CHECKER_CALLBACK_FUNCTION f, void* p) { - me->born.f = f; - me->born.p = p; -} - -void ALIVE_CHECKER_onWait(ALIVE_CHECKER* me, ALIVE_CHECKER_CALLBACK_FUNCTION f, void* p) { - me->wait.f = f; - me->wait.p = p; -} - -void ALIVE_CHECKER_onDead(ALIVE_CHECKER* me, ALIVE_CHECKER_CALLBACK_FUNCTION f, void* p) { - me->dead.f = f; - me->dead.p = p; -} - -/************ - * EMITTERS * - ************/ - -void ALIVE_CHECKER_emitBorn(ALIVE_CHECKER* me, uint16_t t, int64_t data) { - POST(me, &ALIVE_CHECKER_processEvent, evACborn, t, data); -} - -void ALIVE_CHECKER_emitReady(ALIVE_CHECKER* me, uint16_t t, int64_t data) { - POST(me, &ALIVE_CHECKER_processEvent, evACready, t, data); -} - -void ALIVE_CHECKER_emitPoll(ALIVE_CHECKER* me, uint16_t t, int64_t data) { - POST(me, &ALIVE_CHECKER_processEvent, evACpoll, t, data); -} - -/*********** - * SETTERS * - ***********/ - -void ALIVE_CHECKER_setAliveTime(ALIVE_CHECKER* me, uint8_t v) { - me->aliveTime = v; -} - -void ALIVE_CHECKER_setIsAlive(ALIVE_CHECKER* me, bool v) { - me->isAlive = v; -} - -void ALIVE_CHECKER_ISALIVE(ALIVE_CHECKER* me) { - ALIVE_CHECKER_setIsAlive(me, true); -} diff --git a/306-controller_interface.X/middleware/alive_checker.h b/306-controller_interface.X/middleware/alive_checker.h deleted file mode 100644 index 6b57ac7..0000000 --- a/306-controller_interface.X/middleware/alive_checker.h +++ /dev/null @@ -1,134 +0,0 @@ -/** - * @author Rémi Heredero - * @version 1.0.0 - * @date August 2023 - * @file alive_checker.h - */ -#ifndef ALIVE_CHECKER_H -#define ALIVE_CHECKER_H - -#include "../xf/xf.h" - -typedef enum { - STAC_INIT, - STAC_SETUP, - STAC_BORN, - STAC_WAIT, - STAC_DEAD -} ALIVE_CHECKER_STATES; - -typedef enum { - evACinit = 15, - evACborn, - evACready, - evACpoll -} ALIVE_CHECKER_EVENTS; - -typedef void (*ALIVE_CHECKER_CALLBACK_FUNCTION)(void*); -typedef struct { - ALIVE_CHECKER_CALLBACK_FUNCTION f; // function - void* p; // param(s) -} ALIVE_CHECKER_CALLBACK; - -typedef struct { - ALIVE_CHECKER_STATES state; - bool isAlive; - uint8_t aliveTime; - ALIVE_CHECKER_CALLBACK setup; - ALIVE_CHECKER_CALLBACK born; - ALIVE_CHECKER_CALLBACK wait; - ALIVE_CHECKER_CALLBACK dead; -} ALIVE_CHECKER; - -/** - * Initialize the ALIVE_CHECKER - * @param me the ALIVE_CHECKER itself - */ -void ALIVE_CHECKER_init(ALIVE_CHECKER* me); - -/** - * Start the ALIVE_CHECKER state machine - * @param me the ALIVE_CHECKER itself - */ -void ALIVE_CHECKER_startBehaviour(ALIVE_CHECKER* me); - -/** - * Process the event - * @param ev the event to process - * @return true if the event is processed - */ -bool ALIVE_CHECKER_processEvent(Event* ev); - -/************* - * Callbacks * - *************/ - -/** - * Set the callback function to call when the ALIVE_CHECKER is entering state setup - * @param me the ALIVE_CHECKER itself - * @param f the function to call - * @param p the param(s) to pass to the function - */ -void ALIVE_CHECKER_onSetup(ALIVE_CHECKER* me, ALIVE_CHECKER_CALLBACK_FUNCTION f, void* p); - -/** - * Set the callback function to call when the ALIVE_CHECKER is entering state born - * @param me the ALIVE_CHECKER itself - * @param f the function to call - * @param p the param(s) to pass to the function - */ -void ALIVE_CHECKER_onBorn(ALIVE_CHECKER* me, ALIVE_CHECKER_CALLBACK_FUNCTION f, void* p); - -/** - * Set the callback function to call when the ALIVE_CHECKER is entering state wait - * @param me the ALIVE_CHECKER itself - * @param f the function to call - * @param p the param(s) to pass to the function - */ -void ALIVE_CHECKER_onWait(ALIVE_CHECKER* me, ALIVE_CHECKER_CALLBACK_FUNCTION f, void* p); - -/** - * Set the callback function to call when the ALIVE_CHECKER is entering state dead - * @param me the ALIVE_CHECKER itself - * @param f the function to call - * @param p the param(s) to pass to the function - */ -void ALIVE_CHECKER_onDead(ALIVE_CHECKER* me, ALIVE_CHECKER_CALLBACK_FUNCTION f, void* p); - -/************ - * EMITTERS * - ************/ - -/** - * Emit the born event - * @param me the ALIVE_CHECKER itself - * @param t time to wait in ms before triggering event - * @param data data to put on the event for XF - */ -void ALIVE_CHECKER_emitBorn(ALIVE_CHECKER* me, uint16_t t, int64_t data); - -/** - * Emit the ready event - * @param me the ALIVE_CHECKER itself - * @param t time to wait in ms before triggering event - * @param data data to put on the event for XF - */ -void ALIVE_CHECKER_emitReady(ALIVE_CHECKER* me, uint16_t t, int64_t data); - -/** - * Emit the poll event - * @param me the ALIVE_CHECKER itself - * @param t time to wait in ms before triggering event - * @param data data to put on the event for XF - */ -void ALIVE_CHECKER_emitPoll(ALIVE_CHECKER* me, uint16_t t, int64_t data); - -/*********** - * SETTERS * - ***********/ - -void ALIVE_CHECKER_setAliveTime(ALIVE_CHECKER* me, uint8_t v); -void ALIVE_CHECKER_setIsAlive(ALIVE_CHECKER* me, bool v); -void ALIVE_CHECKER_ISALIVE(ALIVE_CHECKER* me); // Use this one when you receive CAN message - -#endif diff --git a/306-controller_interface.X/middleware/watchdog.c b/306-controller_interface.X/middleware/watchdog.c deleted file mode 100644 index 5840bf5..0000000 --- a/306-controller_interface.X/middleware/watchdog.c +++ /dev/null @@ -1,90 +0,0 @@ -/** - * @author Rémi Heredero - * @version 1.0.0 - * @date August 2023 - * @file watchdog.c - */ - -#include "watchdog.h" - -void WATCHDOG_init(WATCHDOG* me){ - me->state = STWD_INIT; - me->time = 10; - me->alive.f = NULL; -} - -void WATCHDOG_startBehaviour(WATCHDOG* me){ - POST(me, &WATCHDOG_processEvent, evWDinit, 0, 0); -} - -bool WATCHDOG_processEvent(Event* ev) { - bool processed = false; - WATCHDOG* me = (WATCHDOG*)Event_getTarget(ev); - WATCHDOG_STATES oldState = me->state; - evIDT evid = Event_getId(ev); - uint64_t data = Event_getData(ev); - - switch (me->state) { // onState - case STWD_INIT: - if (ev->id == evWDinit) { - me->state = STWD_ALIVE; - WATCHDOG_emitPoll(me, 10*me->time, 0); - } - break; - - case STWD_ALIVE: - if (ev->id == evWDpoll) { - WATCHDOG_emitPoll(me, 10*me->time, 0); - if (me->alive.f != NULL) { - me->alive.f(me->alive.p); - } - } - break; - } - - if(oldState != me->state){ - switch (oldState) { // onExit - case STWD_INIT: - break; - - case STWD_ALIVE: - break; - } - - switch (me->state) { // onEntry - case STWD_INIT: - break; - - case STWD_ALIVE: - break; - } - - processed = true; - } - return processed; -} - -/************* - * Callbacks * - *************/ - -void WATCHDOG_onAlive(WATCHDOG* me, WATCHDOG_CALLBACK_FUNCTION f, void* p) { - me->alive.f = f; - me->alive.p = p; -} - -/************ - * EMITTERS * - ************/ - -void WATCHDOG_emitPoll(WATCHDOG* me, uint16_t t, int64_t data) { - POST(me, &WATCHDOG_processEvent, evWDpoll, t, data); -} - -/*********** - * SETTERS * - ***********/ - -void WATCHDOG_setTime(WATCHDOG* me, uint8_t v) { - me->time = v; -} diff --git a/306-controller_interface.X/middleware/watchdog.h b/306-controller_interface.X/middleware/watchdog.h deleted file mode 100644 index ed2d184..0000000 --- a/306-controller_interface.X/middleware/watchdog.h +++ /dev/null @@ -1,83 +0,0 @@ -/** - * @author Rémi Heredero - * @version 1.0.0 - * @date August 2023 - * @file watchdog.h - */ -#ifndef WATCHDOG_H -#define WATCHDOG_H - -#include "../xf/xf.h" - -typedef enum { - STWD_INIT, - STWD_ALIVE -} WATCHDOG_STATES; - -typedef enum { - evWDinit = 20, - evWDpoll -} WATCHDOG_EVENTS; - -typedef void (*WATCHDOG_CALLBACK_FUNCTION)(void*); -typedef struct { - WATCHDOG_CALLBACK_FUNCTION f; // function - void* p; // param(s) -} WATCHDOG_CALLBACK; - -typedef struct { - WATCHDOG_STATES state; - uint8_t time; - WATCHDOG_CALLBACK alive; -} WATCHDOG; - -/** - * Initialize the WATCHDOG - * @param me the WATCHDOG itself - */ -void WATCHDOG_init(WATCHDOG* me); - -/** - * Start the WATCHDOG state machine - * @param me the WATCHDOG itself - */ -void WATCHDOG_startBehaviour(WATCHDOG* me); - -/** - * Process the event - * @param ev the event to process - * @return true if the event is processed - */ -bool WATCHDOG_processEvent(Event* ev); - -/************* - * Callbacks * - *************/ - -/** - * Set the callback function to call when the WATCHDOG is entering state alive - * @param me the WATCHDOG itself - * @param f the function to call - * @param p the param(s) to pass to the function - */ -void WATCHDOG_onAlive(WATCHDOG* me, WATCHDOG_CALLBACK_FUNCTION f, void* p); - -/************ - * EMITTERS * - ************/ - -/** - * Emit the poll event - * @param me the WATCHDOG itself - * @param t time to wait in ms before triggering event - * @param data data to put on the event for XF - */ -void WATCHDOG_emitPoll(WATCHDOG* me, uint16_t t, int64_t data); - -/*********** - * SETTERS * - ***********/ - -void WATCHDOG_setTime(WATCHDOG* me, uint8_t v); - -#endif diff --git a/306-controller_interface.X/nbproject/configurations.xml b/306-controller_interface.X/nbproject/configurations.xml index 3dc5c26..d5e1e13 100644 --- a/306-controller_interface.X/nbproject/configurations.xml +++ b/306-controller_interface.X/nbproject/configurations.xml @@ -26,8 +26,7 @@ middleware/can_interface.h - middleware/alive_checker.h - middleware/watchdog.h + middleware/alive.h xf/event.h @@ -63,8 +62,7 @@ middleware/can_interface.c - middleware/alive_checker.c - middleware/watchdog.c + middleware/alive.c xf/event.c @@ -94,7 +92,7 @@ PICkit3PlatformTool XC8 2.41 - 2 + 3 diff --git a/UML/alive.pdf b/UML/alive.pdf index 75b99e57b0615e294c094164e6f3c290aa07eb5b..47870de23692ec49421a6752b17b97330e54098a 100644 GIT binary patch delta 3029 zcmai0c{CJm7mj2Y`_9+~5fZbS8Duy1bu1HQGM4PJE6eH~saA+V>N9nNJB za}C#I@>`e$uI-yZZL7uiKWr4WR>f4>XYzJ9I7Y1)?w(dqcvw;W);RFvZAnt)*|%Q@ z8j`=Xdk3DL$pIw>8Re+T;Ct$wqk@aNEk!%fdzGZe0FdNnY@@)l(7k2{uQ%Nn8qJT{ z&hUQZ_n~towpaP@cyv@LJ-o#0_%i8zQyF@HUVB)GmWyujw$qLzvX?`G(7xUvg?eZS~w~pNp9#G%yWFa9~{+s@~X&z)OO znG>I4Dd|;w#fFr!wtIe9-?$(q@O%gxtJee)iPp%RD5#-#!f)JKFi|@Bep7_ZvCN#P zGa2utf6>pBQKsf`CzE*-+mN=*U)G|yK8k}2=Xc#cxMaUi)>hBm7G1S`nNXzqk!p-`rSowysyH0xS5*9hw($Su<1-r3NBg>#xBvBZ<3i(B8oj=A>Ew%|pk^l+5!D{2ZA!s#b?_2^zJfbbcJiRxvgXRYbg3XP)m+sg^2_8pPi7 z7IWNn7bEZ=Yjmv4@8$?k-WgoMLEZY+;_gN2i%_=%lP3r=+lCb&avHGJ;|LJRhk6Qtwhx34ZteC`c>YAvY=9 zz%IfbFF(X%)}g7srDj-e6I1JkD!JMx^I9O$%xE+I5@AYzGKGIt=e)YKFv4wqq+d32 zC86$)&9&W-IKhBLOrBVxF3v{KfSK!Z>(leVCmw{C+_`NT)!*l#YV7*g1k=d5n0&j6UNdoTi_NChfm5NQT z377lrlrGae;Ad=_i|!Zd`4%WEdm3j831w`&no2NXBXn9liSAA&W*Fwqrl-Mrjjo@R z7{4mvMLEQNiYY;wNssaOhgDz~x4eQITIGHK7G2gaotCseU*&P}a4kYZm!$OhHB}4~P z{)J->R;LE?lILVnJ=?E~PxXRbTR{-%Uebh+?ifk;n9qx<#lhpt%UO}O=P7EoAW7ko zOI?Yfbv@5yo(Glwc0rVe9Eg-dTQA z8TK$|?Z`>1t76LdE4U>Ja$&cauRK!U+g+hiY8M&usf6EBNB%oavVT zP0ppfCI7*3=c%5W-Y1V!X#sZPvYanoYujpP^eJS0vJ}$}_1)>W6uW;74SP$9YdTSe zDJVtE@La;{$A$DsM8N!N@GN`OO@*FEAWsBruQ3a{gfoqU}`a@ zBIR`riM{#@5iI$}f&&q23sxqAlaMOW$Gq0J8pb3fuya^m|NY-OXL10b7D>HCEt zrFb<=)QKc9^L*HsLv{;4LPR+stbrVa?@W)9Un_8#-+)=(5EDAQUh{*LM+@kpd)n~< zsv;GW&{0yf?#)pR!TwE+GcoIFCOPReGE(S~EzEh#eKnd!$iuoA-g{=nT~g5iW*s<3 zgCO)p9zQ-u-s@WG+Qt{G;O6eyRl?GMSG}>|hC6CE+aA_bbW08wQ+c^F{*xffvj#k_D zJi~>_-f6|4C==VbK@mrR{{42s>HWa&t{L5YqK%MihOuFmbsN_J|K)R(F8JK^^^k~p z9OuB!mNe^4)=^ltaXQ7c|?lUfGYfR=U^O z%pZS9&XI`O*-~kXzP3%hcZ81tL8Tp$Vn(4MU=)P9dPL-aU}i=#AgLP6(iSR)I5Ze@ zuw2<3APg7^`4>C@Q0Pyrc@Qdk0sbNG{vpBOUwfF1ij_;y!4(XJ|5E{U_jmiboFS+b zW;}J8S%k_55TRaSW}|um#Mq&ln*SD{s04s8H4VUi@W4skW)@+<{9=AuoB<+?P&hRM zAj6CJnfA9yAke>?F@NCx)F2E>HDo=i z4a5AQ8Uk`KtiN{!hrm>S=TLBJ1*@nFO2sB1;9!8rU*k6R_Xq$(57Y<0z*rybq=8n2 yxS^4*FoZi24n?~{kZ4sT(gW%ahj|cPVD288|5rgh1{7z5!{9KWtgL}07Wfa~`#O^V delta 2062 zcmaJ?e>~Is8n1h#8WxgJnEY5I_ML4uG5Hx$p{xyynXlC<%WO`BMhcm7j+JteQbO; zm}oFPqUKk8o?g`@>T=2%9~QYgSsJ^2j%VP5tX}V}d#X)3u2lDI+OqB0PrBJ31%!O; zFXq!%@~Jpv!vy!8p#OS9KzwoVjdS<81J;g)`BM}78km_&Wp$H`*r|B>k4qK8(^W&J z*e{*>(~o$|>F0nJ60WtAcXIAca01a)nsTCl_QgG+uk;bd{!4Rw=LSp1+JL5O)vsS$ zbD2qKBXxu9&N;Jah7Pii8@1`GVCZp1+hY+Sbs>1&Y*vrpeB`phQ70GI_8-j8E;b%i zmL;Z7)e~=$t5=D?5w#u{?V#7rw3Oc4a{Pn9H{(9<;l@(4-A`82w>UFJX?IGR)_F*i zLr&dToim~n0v95kGTfEbGmsiJd{)!U(j!jCMOJf`W!CACu+}k^YvTD({04#nH++#kIE)J5Y?& z0=AKH9Ad}v51YuIotwmwIh-JVR<}5^f)kXTRZuRe=hR+=lf)uR{p#}MTMC0O7R^v( zas0+JByuX96n8a{(eD*dSj78B!mgI0&E&B)#KO&1FHxN)JwH5tdKs=iNpA9hLW)g%Zw!;bAP zCFq&G!CJOB>eV3=cII~=7MLeCE(X{xXscx((nEe|AFj;(b@a2oj0f-}NQc2dSdC2` zIadB@*{4FJDII|i&w8xcxl3TO?nRh`Y(9n2^#wG|3^R>c(oJJY?5=}j{#p~S8WIe> z%5}zStgLuk4b&&QW?KAP-Hxkv{3Jf#C-2iysR~;o`HLouN0O?9$33Ut^P*X zf!O_*brPng-14PHN5#Cv=Rb~gqVFun&B>yJ{h^BFpQW;x{C@RIPvgpNqcx@Od6YJa zSA|1t|KO22f?nl<-jk86h4%f0kFj>D)9nQDEg*N4?}ohJ@lnqiiQD!gi zyf!mH3FxLQZTlzs`K&B0B-9P`Eg)l!>m{P%-33hM(pVokaHej|soGKMcJ@$IJp_!Y{+E2GU)^L7Eje_qD|@(WAB7;5$ZZ z&EPhXi#nsVh+a01$n>5dwolA#i9E z)!Gk?VsY7YFiPIE-B}SFg#r**sC+$kH3k}4zs-^;7i$-J2#nxFArw8{&)SE^mRkq_ zrLc&^f0JXOy)ZI#LPZB6sOUn?FkMJlu03HY&>{>DrO5G&TwAHAK!z%+kU&M(2~ZGF zzcK*muQTLO%qh^?g~p+sU@_zs8DP|kf`AS5jVgUD9v}c1G!{Cks`nq9rK%@CfIxK% z53t3_%P0g9fq?xEPyilS=_CA!5C12*I9r<)e71Pp3LoA^;nVoP!gw5XQO$%05PpyT zZxCaPUBN=Y%kNwvUw4m2{dX>hO*?i1WGlw*!DF!?0>D`Nu~_ogQQW1UwP!>)OFoGI zz^;fu`%oYhz&K#T!EhQT9Iy#RqwuzP+b}|SI0}mf>2xgdk0pPx0y+iPS3v^=EX>r@ I?SMP%U-YhZJ^%m! diff --git a/UML/alive.uxf b/UML/alive.uxf index e54aa37..859c367 100644 --- a/UML/alive.uxf +++ b/UML/alive.uxf @@ -1,13 +1,13 @@ - 15 + 14 UMLSpecialState - 510 - 30 - 30 - 30 + 714 + 28 + 56 + 56 type=initial @@ -15,47 +15,48 @@ Relation - 510 - 45 - 90 - 105 + 462 + 70 + 308 + 154 lt=-> -evInit - 10.0;10.0;10.0;50.0 +evInitChecker + 200.0;10.0;200.0;50.0;10.0;50.0;10.0;90.0 UMLState - 390 - 120 - 285 - 90 + 350 + 196 + 266 + 112 SETUP -- -/entry: sendParamsOnCan +/entry: sendParamsOnCan +/entry: checker = true Relation - 510 - 195 - 105 - 105 + 462 + 294 + 98 + 98 lt=-> -evBorn +m1=evBorn 10.0;10.0;10.0;50.0 UMLState - 390 - 270 - 285 - 90 + 350 + 364 + 266 + 84 BORN -- @@ -65,10 +66,10 @@ evBorn UMLState - 390 - 420 - 285 - 120 + 350 + 504 + 266 + 112 WAIT -- @@ -80,22 +81,22 @@ isAlive = false Relation - 510 - 345 - 120 - 105 + 462 + 434 + 112 + 98 lt=-> -evReady +m1=evReady 10.0;10.0;10.0;50.0 UMLState - 390 - 720 - 285 - 90 + 350 + 784 + 266 + 84 DEAD -- @@ -105,94 +106,166 @@ evReady Relation - 510 - 525 - 105 - 105 - - lt=-> -evPoll - 10.0;10.0;10.0;50.0 - - - UMLSpecialState - - 495 - 600 - 60 - 60 - - type=decision - - - - Relation - - 510 - 645 - 90 - 105 - - lt=-> -m1=[else] - 10.0;10.0;10.0;50.0 - - - Relation - - 315 - 465 + 280 + 546 210 - 210 + 182 lt=-> -m1=[isAlive] - 120.0;110.0;10.0;110.0;10.0;10.0;50.0;10.0 +m1=evPoll\n[isAlive] + 80.0;50.0;80.0;110.0;10.0;110.0;10.0;10.0;50.0;10.0 Relation - 660 - 135 - 210 - 405 + 224 + 392 + 336 + 560 lt=-> -evDisable - 10.0;250.0;60.0;250.0;60.0;10.0;10.0;10.0 - - - Relation - - 255 - 300 - 300 - 600 - - lt=-> -evBorn +m1=evBorn 180.0;340.0;180.0;380.0;10.0;380.0;10.0;10.0;90.0;10.0 Relation - 660 - 495 - 120 - 315 + 462 + 602 + 112 + 210 - lt=- - 10.0;190.0;60.0;190.0;60.0;10.0 + lt=-> +m1=evPoll\n[default] + 10.0;10.0;10.0;130.0 Relation - 660 - 315 - 120 - 45 + 728 + 70 + 308 + 154 - lt=- - 10.0;10.0;60.0;10.0 + lt=-> +evInitSender + 10.0;10.0;10.0;50.0;200.0;50.0;200.0;90.0 + + + Relation + + 784 + 252 + 224 + 140 + + lt=-> +m1=evPoll\n[else] + 100.0;40.0;100.0;70.0;10.0;70.0;10.0;10.0;60.0;10.0 + + + UMLNote + + 784 + 28 + 140 + 56 + + read time on +EPROM + + + + UMLState + + 868 + 196 + 266 + 112 + + ALIVE +-- +\entry: sender = true +-- +sendAliveOnCan + + + + UMLState + + 868 + 518 + 266 + 84 + + lt=.. +BREAK +-.. + + + + + Relation + + 994 + 294 + 210 + 252 + + lt=..> +m1=evPoll\n[time==0]\n[haveBreak] + 60.0;10.0;60.0;110.0;10.0;110.0;10.0;160.0 + + + Relation + + 560 + 546 + 336 + 182 + + lt=..> +m1=evPoll\n[time==0]\n[haveBreak] + 10.0;50.0;10.0;110.0;90.0;110.0;180.0;10.0;220.0;10.0 + + + Relation + + 602 + 546 + 420 + 182 + + lt=..> +m1=evStart\n[checker] + 220.0;40.0;220.0;110.0;150.0;110.0;60.0;10.0;10.0;10.0 + + + Relation + + 1078 + 252 + 182 + 476 + + lt=..> +m1=evStart\n[sender] + 10.0;250.0;10.0;320.0;110.0;320.0;110.0;10.0;40.0;10.0 + + + UMLNote + + 868 + 728 + 266 + 154 + + lt=.. +break part can be disable +with setHaveBreak(false) + +not all childrens have a break +for time at 0 + From 7e5d85070f6b0098b5422b774da5468ad938ef37 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Heredero?= Date: Tue, 29 Aug 2023 14:38:50 +0200 Subject: [PATCH 2/2] add can sequence --- UML/can_sequence.png | Bin 0 -> 32882 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 UML/can_sequence.png diff --git a/UML/can_sequence.png b/UML/can_sequence.png new file mode 100644 index 0000000000000000000000000000000000000000..22a7e7072f40b1963d7e6dbedbe03e0f1ffd9e9e GIT binary patch literal 32882 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 CHYIET literal 0 HcmV?d00001