Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 30 additions & 2 deletions lib/pbio/include/pbio/light_animation.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@
#include <stdbool.h>
#include <stdint.h>

#include <contiki.h>
#include <pbio/os.h>
#include <pbio/config.h>

typedef struct _pbio_light_animation_t pbio_light_animation_t;

Expand All @@ -24,17 +25,44 @@ typedef uint32_t (*pbio_light_animation_next_t)(pbio_light_animation_t *animatio

struct _pbio_light_animation_t {
/** Animation update timer. */
struct etimer timer;
pbio_os_timer_t timer;
/** Animation iterator callback. */
pbio_light_animation_next_t next;
/** Linked list */
pbio_light_animation_t *next_animation;
};

#if PBIO_CONFIG_LIGHT

void pbio_light_animation_init_module(void);
void pbio_light_animation_init(pbio_light_animation_t *animation, pbio_light_animation_next_t next);
void pbio_light_animation_start(pbio_light_animation_t *animation);
void pbio_light_animation_stop(pbio_light_animation_t *animation);
void pbio_light_animation_stop_all(void);
bool pbio_light_animation_is_started(pbio_light_animation_t *animation);

#else // PBIO_CONFIG_LIGHT

static inline void pbio_light_animation_init_module(void) {
}

static inline void pbio_light_animation_init(pbio_light_animation_t *animation, pbio_light_animation_next_t next) {
}

static inline void pbio_light_animation_start(pbio_light_animation_t *animation) {
}

static inline void pbio_light_animation_stop(pbio_light_animation_t *animation) {
}

static inline void pbio_light_animation_stop_all(void) {
}

static inline bool pbio_light_animation_is_started(pbio_light_animation_t *animation) {
return false;
}

#endif // PBIO_CONFIG_LIGHT


#endif // _PBIO_LIGHT_ANIMATION_H_
40 changes: 16 additions & 24 deletions lib/pbio/src/light/animation.c
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,16 @@
#include <assert.h>
#include <stdbool.h>

#include <contiki.h>

#include <pbio/util.h>

#include <pbio/light_animation.h>
#include <pbio/os.h>
#include <pbio/util.h>

/**
* This is used as a value for the next_animation field to indicate when an
* animation is stopped.
*/
#define PBIO_LIGHT_ANIMATION_STOPPED ((pbio_light_animation_t *)1)

PROCESS(pbio_light_animation_process, "light animation");
static pbio_light_animation_t *pbio_light_animation_list_head;

/**
Expand All @@ -46,14 +43,12 @@ void pbio_light_animation_init(pbio_light_animation_t *animation, pbio_light_ani
void pbio_light_animation_start(pbio_light_animation_t *animation) {
assert(animation->next_animation == PBIO_LIGHT_ANIMATION_STOPPED);

// Insert at head of active list.
animation->next_animation = pbio_light_animation_list_head;
pbio_light_animation_list_head = animation;

process_start(&pbio_light_animation_process);
// Fake a timer event to load the first cell.
PROCESS_CONTEXT_BEGIN(&pbio_light_animation_process);
etimer_set(&animation->timer, 0);
PROCESS_CONTEXT_END(&pbio_light_animation_process);
pbio_os_timer_set(&animation->timer, 0);

assert(animation->next_animation != PBIO_LIGHT_ANIMATION_STOPPED);
}
Expand All @@ -69,13 +64,8 @@ void pbio_light_animation_stop(pbio_light_animation_t *animation) {
assert(pbio_light_animation_list_head != NULL);
assert(animation->next_animation != PBIO_LIGHT_ANIMATION_STOPPED);

etimer_stop(&animation->timer);

if (pbio_light_animation_list_head == animation) {
pbio_light_animation_list_head = animation->next_animation;
if (pbio_light_animation_list_head == NULL) {
process_exit(&pbio_light_animation_process);
}
} else {
for (pbio_light_animation_t *a = pbio_light_animation_list_head; a != NULL; a = a->next_animation) {
if (a->next_animation == animation) {
Expand Down Expand Up @@ -110,20 +100,22 @@ bool pbio_light_animation_is_started(pbio_light_animation_t *animation) {
return animation->next_animation != PBIO_LIGHT_ANIMATION_STOPPED;
}

PROCESS_THREAD(pbio_light_animation_process, ev, data) {
PROCESS_BEGIN();
static pbio_error_t pbio_light_animation_process_thread(pbio_os_state_t *state, void *context) {

for (;;) {
PROCESS_WAIT_EVENT_UNTIL(ev == PROCESS_EVENT_TIMER && etimer_expired(data));
struct etimer *timer = data;
pbio_light_animation_t *animation = PBIO_CONTAINER_OF(timer, pbio_light_animation_t, timer);
if (pbio_light_animation_is_started(animation)) {
clock_time_t interval = animation->next(animation);
etimer_reset_with_new_interval(&animation->timer, interval);
for (pbio_light_animation_t *a = pbio_light_animation_list_head; a != NULL; a = a->next_animation) {
if (pbio_os_timer_is_expired(&a->timer)) {
pbio_os_timer_set(&a->timer, a->next(a));
}
}

PROCESS_END();
// This process does not have a state. It is effectively just a poll handler.
return PBIO_ERROR_AGAIN;
}

void pbio_light_animation_init_module(void) {

static pbio_os_process_t pbio_light_animation_process;
pbio_os_process_start(&pbio_light_animation_process, pbio_light_animation_process_thread, NULL);
}

#endif // PBIO_CONFIG_LIGHT
1 change: 1 addition & 0 deletions lib/pbio/src/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ void pbio_init(bool start_processes) {

pbio_battery_init();
pbio_imu_init();
pbio_light_animation_init_module();

if (!start_processes) {
return;
Expand Down
40 changes: 20 additions & 20 deletions lib/pbio/test/src/test_animation.c
Original file line number Diff line number Diff line change
Expand Up @@ -15,45 +15,49 @@

#define TEST_ANIMATION_TIME 10

PROCESS_NAME(pbio_light_animation_process);

static uint8_t test_animation_set_hsv_call_count;

static uint32_t test_animation_next(pbio_light_animation_t *animation) {
test_animation_set_hsv_call_count++;
return TEST_ANIMATION_TIME;
}

static PT_THREAD(test_light_animation(struct pt *pt)) {
PT_BEGIN(pt);
#define YIELD(state) \
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We've only needed it in this test so far, but we should probably move this to the pbio/os module.

do { \
do_yield_now = 1; \
PBIO_OS_ASYNC_SET_CHECKPOINT(state); \
if (do_yield_now) { \
return PBIO_ERROR_AGAIN; \
} \
} while (0)

static pbio_error_t test_light_animation(pbio_os_state_t *state, void *context) {
PBIO_OS_ASYNC_BEGIN(state);

static pbio_light_animation_t test_animation;
pbio_light_animation_init(&test_animation, test_animation_next);

// process should not be running yet
tt_want(!process_is_running(&pbio_light_animation_process));
// animation should not be started yet
tt_want(!pbio_light_animation_is_started(&test_animation));

// starting animation should start process and set a timer at 0ms to call
// starting animation should set a timer at 0ms to call
// next() after handling pending events
pbio_light_animation_start(&test_animation);
tt_want(pbio_light_animation_is_started(&test_animation));
tt_want(process_is_running(&pbio_light_animation_process));
pbio_handle_pending_events();
YIELD(state);
tt_want_uint_op(test_animation_set_hsv_call_count, ==, 1);

// next() should not be called again until after a delay
pbio_test_clock_tick(TEST_ANIMATION_TIME - 1);
PT_YIELD(pt);
pbio_test_clock_tick(TEST_ANIMATION_TIME - 2);
YIELD(state);
tt_want_uint_op(test_animation_set_hsv_call_count, ==, 1);
pbio_test_clock_tick(1);
PT_YIELD(pt);
YIELD(state);
tt_want_uint_op(test_animation_set_hsv_call_count, ==, 2);

// stopping the animation stops the process
// stopping the animation stops the animation
pbio_light_animation_stop(&test_animation);
tt_want(!pbio_light_animation_is_started(&test_animation));
tt_want(!process_is_running(&pbio_light_animation_process));

// exercise multiple animations for code coverage
static pbio_light_animation_t test_animation2;
Expand All @@ -65,27 +69,23 @@ static PT_THREAD(test_light_animation(struct pt *pt)) {
pbio_light_animation_stop(&test_animation);
tt_want(!pbio_light_animation_is_started(&test_animation));
tt_want(pbio_light_animation_is_started(&test_animation2));
tt_want(process_is_running(&pbio_light_animation_process));
pbio_light_animation_stop(&test_animation2);
tt_want(!pbio_light_animation_is_started(&test_animation));
tt_want(!pbio_light_animation_is_started(&test_animation2));
tt_want(!process_is_running(&pbio_light_animation_process));

// stopping all animations stops the process
pbio_light_animation_start(&test_animation);
pbio_light_animation_start(&test_animation2);
tt_want(pbio_light_animation_is_started(&test_animation));
tt_want(pbio_light_animation_is_started(&test_animation2));
tt_want(process_is_running(&pbio_light_animation_process));
pbio_light_animation_stop_all();
tt_want(!pbio_light_animation_is_started(&test_animation));
tt_want(!pbio_light_animation_is_started(&test_animation2));
tt_want(!process_is_running(&pbio_light_animation_process));

PT_END(pt);
PBIO_OS_ASYNC_END(PBIO_SUCCESS);
}

struct testcase_t pbio_light_animation_tests[] = {
PBIO_PT_THREAD_TEST(test_light_animation),
PBIO_PT_THREAD_TEST_WITH_PBIO_OS(test_light_animation),
END_OF_TESTCASES
};
Loading