Implement ValueManager and ValueComposer (#183)
* Fix ValueManager implementation * Implement ValueComposer * Add constructor for ValueManager * Add value-expanders.h to flipper_v2.h set * Move COPY_COMPOSE body into a .c file * Add test for ValueManager * Add destructors for ValueMutex, ValueManager and ValueComposer * Use destructors in tests * Move composition logic into perform_compose() * Add docs for perform_compose() * Add test for ValueComposer * Replace atomic_bool with bool as g++ compiler doesn't support C11 atomics * Add Event type * Add semaphore support to the local target * Add test for Event * Update input records and relevant examples * Rename Event to AppEvent in the cc1101-workaround example * Rename Event to AppEvent in the irda example * Use Event in ValueComposer to wait for update request * Add perform_compose_internal() function * fix Event/AppEvent Co-authored-by: aanper <mail@s3f.ru>
This commit is contained in:
parent
69d97afea8
commit
bb68fca20b
@ -49,6 +49,8 @@ C_SOURCES += $(APP_DIR)/tests/minunit_test.c
|
|||||||
C_SOURCES += $(APP_DIR)/tests/furi_valuemutex_test.c
|
C_SOURCES += $(APP_DIR)/tests/furi_valuemutex_test.c
|
||||||
C_SOURCES += $(APP_DIR)/tests/furi_pubsub_test.c
|
C_SOURCES += $(APP_DIR)/tests/furi_pubsub_test.c
|
||||||
C_SOURCES += $(APP_DIR)/tests/furi_memmgr_test.c
|
C_SOURCES += $(APP_DIR)/tests/furi_memmgr_test.c
|
||||||
|
C_SOURCES += $(APP_DIR)/tests/furi_value_expanders_test.c
|
||||||
|
C_SOURCES += $(APP_DIR)/tests/furi_event_test.c
|
||||||
endif
|
endif
|
||||||
|
|
||||||
APP_EXAMPLE_BLINK ?= 0
|
APP_EXAMPLE_BLINK ?= 0
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
#include "flipper.h"
|
#include "flipper_v2.h"
|
||||||
|
|
||||||
static void event_cb(const void* value, size_t size, void* ctx) {
|
static void event_cb(const void* value, void* ctx) {
|
||||||
xSemaphoreGive((SemaphoreHandle_t*)ctx);
|
xSemaphoreGive((SemaphoreHandle_t*)ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -14,7 +14,9 @@ void backlight_control(void* p) {
|
|||||||
SemaphoreHandle_t update = xSemaphoreCreateCountingStatic(255, 0, &event_descriptor);
|
SemaphoreHandle_t update = xSemaphoreCreateCountingStatic(255, 0, &event_descriptor);
|
||||||
|
|
||||||
// open record
|
// open record
|
||||||
furi_open_deprecated("input_events", false, false, event_cb, NULL, (void*)update);
|
PubSub* event_record = furi_open("input_events");
|
||||||
|
assert(event_record != NULL);
|
||||||
|
subscribe_pubsub(event_record, event_cb, (void*)update);
|
||||||
|
|
||||||
// we ready to work
|
// we ready to work
|
||||||
furiac_ready();
|
furiac_ready();
|
||||||
|
@ -115,7 +115,7 @@ typedef struct {
|
|||||||
InputEvent input;
|
InputEvent input;
|
||||||
} value;
|
} value;
|
||||||
EventType type;
|
EventType type;
|
||||||
} Event;
|
} AppEvent;
|
||||||
|
|
||||||
typedef enum { ModeRx, ModeTx } Mode;
|
typedef enum { ModeRx, ModeTx } Mode;
|
||||||
|
|
||||||
@ -195,14 +195,14 @@ static void render_callback(CanvasApi* canvas, void* ctx) {
|
|||||||
static void input_callback(InputEvent* input_event, void* ctx) {
|
static void input_callback(InputEvent* input_event, void* ctx) {
|
||||||
osMessageQueueId_t event_queue = (QueueHandle_t)ctx;
|
osMessageQueueId_t event_queue = (QueueHandle_t)ctx;
|
||||||
|
|
||||||
Event event;
|
AppEvent event;
|
||||||
event.type = EventTypeKey;
|
event.type = EventTypeKey;
|
||||||
event.value.input = *input_event;
|
event.value.input = *input_event;
|
||||||
osMessageQueuePut(event_queue, &event, 0, 0);
|
osMessageQueuePut(event_queue, &event, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" void cc1101_workaround(void* p) {
|
extern "C" void cc1101_workaround(void* p) {
|
||||||
osMessageQueueId_t event_queue = osMessageQueueNew(1, sizeof(Event), NULL);
|
osMessageQueueId_t event_queue = osMessageQueueNew(1, sizeof(AppEvent), NULL);
|
||||||
assert(event_queue);
|
assert(event_queue);
|
||||||
|
|
||||||
State _state;
|
State _state;
|
||||||
@ -266,7 +266,7 @@ extern "C" void cc1101_workaround(void* p) {
|
|||||||
|
|
||||||
const int16_t RSSI_THRESHOLD = -89;
|
const int16_t RSSI_THRESHOLD = -89;
|
||||||
|
|
||||||
Event event;
|
AppEvent event;
|
||||||
while(1) {
|
while(1) {
|
||||||
osStatus_t event_status = osMessageQueueGet(event_queue, &event, NULL, 150);
|
osStatus_t event_status = osMessageQueueGet(event_queue, &event, NULL, 150);
|
||||||
State* state = (State*)acquire_mutex_block(&state_mutex);
|
State* state = (State*)acquire_mutex_block(&state_mutex);
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
#include "u8g2/u8g2.h"
|
#include "u8g2/u8g2.h"
|
||||||
#include "fatfs/ff.h"
|
#include "fatfs/ff.h"
|
||||||
#include "flipper.h"
|
#include "flipper_v2.h"
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
// TODO currently we have small stack, so it will be static
|
// TODO currently we have small stack, so it will be static
|
||||||
@ -26,7 +26,7 @@ typedef struct {
|
|||||||
AppEventType type;
|
AppEventType type;
|
||||||
} AppEvent;
|
} AppEvent;
|
||||||
|
|
||||||
static void event_cb(const void* value, size_t size, void* ctx) {
|
static void event_cb(const void* value, void* ctx) {
|
||||||
QueueHandle_t event_queue = (QueueHandle_t)ctx;
|
QueueHandle_t event_queue = (QueueHandle_t)ctx;
|
||||||
|
|
||||||
AppEvent event;
|
AppEvent event;
|
||||||
@ -56,9 +56,13 @@ void fatfs_list(void* p) {
|
|||||||
furiac_exit(NULL);
|
furiac_exit(NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
FuriRecordSubscriber* event_record =
|
PubSub* event_record = furi_open("input_events");
|
||||||
furi_open_deprecated("input_events", false, false, event_cb, NULL, event_queue);
|
|
||||||
if(event_record == NULL) {
|
if(event_record == NULL) {
|
||||||
|
fuprintf(furi_log, "[widget][fatfs_list] cannot open input_events record\n");
|
||||||
|
furiac_exit(NULL);
|
||||||
|
}
|
||||||
|
PubSubItem* subscription = subscribe_pubsub(event_record, event_cb, event_queue);
|
||||||
|
if(subscription == NULL) {
|
||||||
fuprintf(furi_log, "[widget][fatfs_list] cannot register input_events callback\n");
|
fuprintf(furi_log, "[widget][fatfs_list] cannot register input_events callback\n");
|
||||||
furiac_exit(NULL);
|
furiac_exit(NULL);
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
#include "flipper.h"
|
#include "flipper_v2.h"
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
static void state_cb(const void* value, size_t size, void* ctx) {
|
static void state_cb(const void* value, void* ctx) {
|
||||||
const InputState* state = value;
|
const InputState* state = value;
|
||||||
|
|
||||||
printf("state: %02x\n", *state);
|
printf("state: %02x\n", *state);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void event_cb(const void* value, size_t size, void* ctx) {
|
static void event_cb(const void* value, void* ctx) {
|
||||||
const InputEvent* event = value;
|
const InputEvent* event = value;
|
||||||
|
|
||||||
printf("event: %02x %s\n", event->input, event->state ? "pressed" : "released");
|
printf("event: %02x %s\n", event->input, event->state ? "pressed" : "released");
|
||||||
@ -15,10 +15,13 @@ static void event_cb(const void* value, size_t size, void* ctx) {
|
|||||||
|
|
||||||
void application_input_dump(void* p) {
|
void application_input_dump(void* p) {
|
||||||
// open record
|
// open record
|
||||||
FuriRecordSubscriber* state_record =
|
ValueManager* state_record = furi_open("input_state");
|
||||||
furi_open_deprecated("input_state", false, false, state_cb, NULL, NULL);
|
assert(state_record != NULL);
|
||||||
FuriRecordSubscriber* event_record =
|
subscribe_pubsub(&state_record->pubsub, state_cb, NULL);
|
||||||
furi_open_deprecated("input_events", false, false, event_cb, NULL, NULL);
|
|
||||||
|
PubSub* event_record = furi_open("input_events");
|
||||||
|
assert(event_record != NULL);
|
||||||
|
subscribe_pubsub(event_record, event_cb, NULL);
|
||||||
|
|
||||||
for(;;) {
|
for(;;) {
|
||||||
delay(100);
|
delay(100);
|
||||||
|
@ -1,17 +1,17 @@
|
|||||||
#include "gui_event.h"
|
#include "gui_event.h"
|
||||||
|
|
||||||
#include <flipper.h>
|
#include <flipper_v2.h>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
|
||||||
#define GUI_EVENT_MQUEUE_SIZE 8
|
#define GUI_EVENT_MQUEUE_SIZE 8
|
||||||
|
|
||||||
struct GuiEvent {
|
struct GuiEvent {
|
||||||
FuriRecordSubscriber* input_event_record;
|
PubSub* input_event_record;
|
||||||
osMessageQueueId_t mqueue;
|
osMessageQueueId_t mqueue;
|
||||||
osMutexId_t lock_mutex;
|
osMutexId_t lock_mutex;
|
||||||
};
|
};
|
||||||
|
|
||||||
void gui_event_input_events_callback(const void* value, size_t size, void* ctx) {
|
void gui_event_input_events_callback(const void* value, void* ctx) {
|
||||||
assert(ctx);
|
assert(ctx);
|
||||||
GuiEvent* gui_event = ctx;
|
GuiEvent* gui_event = ctx;
|
||||||
|
|
||||||
@ -29,9 +29,10 @@ GuiEvent* gui_event_alloc() {
|
|||||||
assert(gui_event->mqueue);
|
assert(gui_event->mqueue);
|
||||||
|
|
||||||
// Input
|
// Input
|
||||||
gui_event->input_event_record = furi_open_deprecated(
|
gui_event->input_event_record = furi_open("input_events");
|
||||||
"input_events", false, false, gui_event_input_events_callback, NULL, gui_event);
|
|
||||||
assert(gui_event->input_event_record != NULL);
|
assert(gui_event->input_event_record != NULL);
|
||||||
|
subscribe_pubsub(gui_event->input_event_record, gui_event_input_events_callback, gui_event);
|
||||||
|
|
||||||
// Lock mutex
|
// Lock mutex
|
||||||
gui_event->lock_mutex = osMutexNew(NULL);
|
gui_event->lock_mutex = osMutexNew(NULL);
|
||||||
assert(gui_event->lock_mutex);
|
assert(gui_event->lock_mutex);
|
||||||
|
@ -1,49 +1,47 @@
|
|||||||
#include <input/input.h>
|
#include <input/input.h>
|
||||||
#include <input_priv.h>
|
#include <input_priv.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <flipper.h>
|
#include <flipper_v2.h>
|
||||||
|
|
||||||
#ifdef APP_NFC
|
#ifdef APP_NFC
|
||||||
void st25r3916Isr(void);
|
void st25r3916Isr(void);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static volatile bool initialized = false;
|
static volatile bool initialized = false;
|
||||||
static SemaphoreHandle_t event;
|
static ValueManager input_state_record;
|
||||||
|
static PubSub input_events_record;
|
||||||
|
static Event event;
|
||||||
static InputState input_state = {
|
static InputState input_state = {
|
||||||
false,
|
false,
|
||||||
};
|
};
|
||||||
|
|
||||||
void input_task(void* p) {
|
void input_task(void* p) {
|
||||||
uint32_t state_bits = 0;
|
uint32_t state_bits = 0;
|
||||||
StaticSemaphore_t event_semaphore;
|
|
||||||
uint8_t debounce_counters[INPUT_COUNT];
|
uint8_t debounce_counters[INPUT_COUNT];
|
||||||
|
|
||||||
event = xSemaphoreCreateCountingStatic(1, 0, &event_semaphore);
|
if(!init_managed(&input_state_record, &input_state, sizeof(input_state))) {
|
||||||
|
printf("[input_task] cannot initialize ValueManager for input_state\n");
|
||||||
|
furiac_exit(NULL);
|
||||||
|
}
|
||||||
|
if(!init_pubsub(&input_events_record)) {
|
||||||
|
printf("[input_task] cannot initialize PubSub for input_events\n");
|
||||||
|
furiac_exit(NULL);
|
||||||
|
}
|
||||||
|
if(!init_event(&event)) {
|
||||||
|
printf("[input_task] cannot initialize Event\n");
|
||||||
|
furiac_exit(NULL);
|
||||||
|
}
|
||||||
|
|
||||||
if(!furi_create_deprecated("input_state", (void*)&input_state, sizeof(input_state))) {
|
if(!furi_create("input_state", &input_state_record)) {
|
||||||
printf("[input_task] cannot create the input_state record\n");
|
printf("[input_task] cannot create the input_state record\n");
|
||||||
furiac_exit(NULL);
|
furiac_exit(NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
FuriRecordSubscriber* input_state_record =
|
if(!furi_create("input_events", &input_events_record)) {
|
||||||
furi_open_deprecated("input_state", false, false, NULL, NULL, NULL);
|
|
||||||
if(input_state_record == NULL) {
|
|
||||||
printf("[input_task] cannot open the input_state record\n");
|
|
||||||
furiac_exit(NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!furi_create_deprecated("input_events", NULL, 0)) {
|
|
||||||
printf("[input_task] cannot create the input_events record\n");
|
printf("[input_task] cannot create the input_events record\n");
|
||||||
furiac_exit(NULL);
|
furiac_exit(NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
FuriRecordSubscriber* input_events_record =
|
|
||||||
furi_open_deprecated("input_events", false, false, NULL, NULL, NULL);
|
|
||||||
if(input_events_record == NULL) {
|
|
||||||
printf("[input_task] cannot open the input_events record\n");
|
|
||||||
furiac_exit(NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
// we ready to work
|
// we ready to work
|
||||||
furiac_ready();
|
furiac_ready();
|
||||||
initialized = true;
|
initialized = true;
|
||||||
@ -82,7 +80,7 @@ void input_task(void* p) {
|
|||||||
if(changed_bits != 0) {
|
if(changed_bits != 0) {
|
||||||
// printf("[input] %02x -> %02x\n", state_bits, new_state_bits);
|
// printf("[input] %02x -> %02x\n", state_bits, new_state_bits);
|
||||||
InputState new_state = _BITS2STATE(new_state_bits);
|
InputState new_state = _BITS2STATE(new_state_bits);
|
||||||
furi_write(input_state_record, &new_state, sizeof(new_state));
|
write_managed(&input_state_record, &new_state, sizeof(new_state), osWaitForever);
|
||||||
|
|
||||||
state_bits = new_state_bits;
|
state_bits = new_state_bits;
|
||||||
|
|
||||||
@ -90,13 +88,13 @@ void input_task(void* p) {
|
|||||||
if((changed_bits & (1 << i)) != 0) {
|
if((changed_bits & (1 << i)) != 0) {
|
||||||
bool state = (new_state_bits & (1 << i)) != 0;
|
bool state = (new_state_bits & (1 << i)) != 0;
|
||||||
InputEvent event = {i, state};
|
InputEvent event = {i, state};
|
||||||
furi_write(input_events_record, &event, sizeof(event));
|
notify_pubsub(&input_events_record, &event);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sleep: wait for event
|
// Sleep: wait for event
|
||||||
xSemaphoreTake(event, portMAX_DELAY);
|
wait_event(&event);
|
||||||
} else {
|
} else {
|
||||||
osDelay(1);
|
osDelay(1);
|
||||||
}
|
}
|
||||||
@ -113,12 +111,5 @@ void HAL_GPIO_EXTI_Callback(uint16_t pin) {
|
|||||||
|
|
||||||
if(!initialized) return;
|
if(!initialized) return;
|
||||||
|
|
||||||
BaseType_t task_woken = pdFALSE;
|
signal_event(&event);
|
||||||
|
|
||||||
// Ignore the result, as we do not care about repeated event during event processing.
|
|
||||||
xSemaphoreGiveFromISR(event, &task_woken);
|
|
||||||
|
|
||||||
if(task_woken) {
|
|
||||||
portYIELD_FROM_ISR(task_woken);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,7 @@ typedef struct {
|
|||||||
InputEvent input;
|
InputEvent input;
|
||||||
} value;
|
} value;
|
||||||
EventType type;
|
EventType type;
|
||||||
} Event;
|
} AppEvent;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uint8_t mode_id;
|
uint8_t mode_id;
|
||||||
@ -24,15 +24,15 @@ typedef struct {
|
|||||||
uint8_t samsung_packet_id;
|
uint8_t samsung_packet_id;
|
||||||
} State;
|
} State;
|
||||||
|
|
||||||
typedef void (*ModeInput)(Event*, State*);
|
typedef void (*ModeInput)(AppEvent*, State*);
|
||||||
typedef void (*ModeRender)(CanvasApi*, State*);
|
typedef void (*ModeRender)(CanvasApi*, State*);
|
||||||
|
|
||||||
void input_carrier(Event* event, State* state);
|
void input_carrier(AppEvent* event, State* state);
|
||||||
void render_carrier(CanvasApi* canvas, State* state);
|
void render_carrier(CanvasApi* canvas, State* state);
|
||||||
void input_nec(Event* event, State* state);
|
void input_nec(AppEvent* event, State* state);
|
||||||
void render_nec(CanvasApi* canvas, State* state);
|
void render_nec(CanvasApi* canvas, State* state);
|
||||||
void render_carrier(CanvasApi* canvas, State* state);
|
void render_carrier(CanvasApi* canvas, State* state);
|
||||||
void input_samsung(Event* event, State* state);
|
void input_samsung(AppEvent* event, State* state);
|
||||||
void render_samsung(CanvasApi* canvas, State* state);
|
void render_samsung(CanvasApi* canvas, State* state);
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
@ -120,7 +120,7 @@ void render_samsung(CanvasApi* canvas, State* state) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void input_carrier(Event* event, State* state) {
|
void input_carrier(AppEvent* event, State* state) {
|
||||||
if(event->value.input.input == InputOk) {
|
if(event->value.input.input == InputOk) {
|
||||||
if(event->value.input.state) {
|
if(event->value.input.state) {
|
||||||
hal_pwm_set(
|
hal_pwm_set(
|
||||||
@ -151,7 +151,7 @@ void input_carrier(Event* event, State* state) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void input_nec(Event* event, State* state) {
|
void input_nec(AppEvent* event, State* state) {
|
||||||
uint8_t packets_count = sizeof(nec_packets) / sizeof(nec_packets[0]);
|
uint8_t packets_count = sizeof(nec_packets) / sizeof(nec_packets[0]);
|
||||||
|
|
||||||
if(event->value.input.input == InputOk) {
|
if(event->value.input.input == InputOk) {
|
||||||
@ -180,7 +180,7 @@ void input_nec(Event* event, State* state) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void input_samsung(Event* event, State* state) {
|
void input_samsung(AppEvent* event, State* state) {
|
||||||
uint8_t packets_count = sizeof(samsung_packets) / sizeof(samsung_packets[0]);
|
uint8_t packets_count = sizeof(samsung_packets) / sizeof(samsung_packets[0]);
|
||||||
|
|
||||||
if(event->value.input.input == InputOk) {
|
if(event->value.input.input == InputOk) {
|
||||||
@ -226,14 +226,14 @@ static void render_callback(CanvasApi* canvas, void* ctx) {
|
|||||||
static void input_callback(InputEvent* input_event, void* ctx) {
|
static void input_callback(InputEvent* input_event, void* ctx) {
|
||||||
osMessageQueueId_t event_queue = (QueueHandle_t)ctx;
|
osMessageQueueId_t event_queue = (QueueHandle_t)ctx;
|
||||||
|
|
||||||
Event event;
|
AppEvent event;
|
||||||
event.type = EventTypeKey;
|
event.type = EventTypeKey;
|
||||||
event.value.input = *input_event;
|
event.value.input = *input_event;
|
||||||
osMessageQueuePut(event_queue, &event, 0, 0);
|
osMessageQueuePut(event_queue, &event, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void irda(void* p) {
|
void irda(void* p) {
|
||||||
osMessageQueueId_t event_queue = osMessageQueueNew(1, sizeof(Event), NULL);
|
osMessageQueueId_t event_queue = osMessageQueueNew(1, sizeof(AppEvent), NULL);
|
||||||
|
|
||||||
State _state;
|
State _state;
|
||||||
uint8_t mode_count = sizeof(modes) / sizeof(modes[0]);
|
uint8_t mode_count = sizeof(modes) / sizeof(modes[0]);
|
||||||
@ -264,7 +264,7 @@ void irda(void* p) {
|
|||||||
}
|
}
|
||||||
gui->add_widget(gui, widget, WidgetLayerFullscreen);
|
gui->add_widget(gui, widget, WidgetLayerFullscreen);
|
||||||
|
|
||||||
Event event;
|
AppEvent event;
|
||||||
while(1) {
|
while(1) {
|
||||||
osStatus_t event_status = osMessageQueueGet(event_queue, &event, NULL, osWaitForever);
|
osStatus_t event_status = osMessageQueueGet(event_queue, &event, NULL, osWaitForever);
|
||||||
State* state = (State*)acquire_mutex_block(&state_mutex);
|
State* state = (State*)acquire_mutex_block(&state_mutex);
|
||||||
|
30
applications/tests/furi_event_test.c
Normal file
30
applications/tests/furi_event_test.c
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
#include "flipper_v2.h"
|
||||||
|
#include "minunit.h"
|
||||||
|
|
||||||
|
static void furi_concurent_app(void* p) {
|
||||||
|
Event* event = p;
|
||||||
|
|
||||||
|
signal_event(event);
|
||||||
|
|
||||||
|
furiac_exit(NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_furi_event() {
|
||||||
|
Event event;
|
||||||
|
|
||||||
|
mu_check(init_event(&event));
|
||||||
|
|
||||||
|
// The event should not be signalled right after creation
|
||||||
|
mu_check(!wait_event_with_timeout(&event, 100));
|
||||||
|
|
||||||
|
// Create second app
|
||||||
|
FuriApp* second_app = furiac_start(furi_concurent_app, "furi concurent app", (void*)&event);
|
||||||
|
|
||||||
|
// The event should be signalled now
|
||||||
|
mu_check(wait_event_with_timeout(&event, 100));
|
||||||
|
|
||||||
|
// The event should not be signalled once it's processed
|
||||||
|
mu_check(!wait_event_with_timeout(&event, 100));
|
||||||
|
|
||||||
|
mu_check(delete_event(&event));
|
||||||
|
}
|
145
applications/tests/furi_value_expanders_test.c
Normal file
145
applications/tests/furi_value_expanders_test.c
Normal file
@ -0,0 +1,145 @@
|
|||||||
|
#include "flipper_v2.h"
|
||||||
|
#include "minunit.h"
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint8_t red;
|
||||||
|
uint8_t green;
|
||||||
|
uint8_t blue;
|
||||||
|
} Rgb;
|
||||||
|
|
||||||
|
static uint32_t rgb_final_state;
|
||||||
|
|
||||||
|
static void rgb_clear(void* ctx, void* state) {
|
||||||
|
Rgb* rgb = state;
|
||||||
|
rgb->red = 0;
|
||||||
|
rgb->green = 0;
|
||||||
|
rgb->blue = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void rgb_commit(void* ctx, void* state) {
|
||||||
|
Rgb* rgb = state;
|
||||||
|
rgb_final_state = ((uint32_t)rgb->red) | (((uint32_t)rgb->green) << 8) |
|
||||||
|
(((uint32_t)rgb->blue) << 16);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void set_red_composer(void* ctx, void* state) {
|
||||||
|
Rgb* rgb = state;
|
||||||
|
uint8_t* red = ctx;
|
||||||
|
|
||||||
|
rgb->red = *red;
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_furi_value_composer() {
|
||||||
|
Rgb rgb = {0, 0, 0};
|
||||||
|
ValueComposer composer;
|
||||||
|
Rgb layer1_rgb = {0, 0, 0};
|
||||||
|
ValueMutex layer1_mutex;
|
||||||
|
uint8_t layer2_red = 0;
|
||||||
|
|
||||||
|
rgb_final_state = 0xdeadbeef;
|
||||||
|
|
||||||
|
mu_check(init_composer(&composer, &rgb));
|
||||||
|
|
||||||
|
mu_check(init_mutex(&layer1_mutex, &layer1_rgb, sizeof(layer1_rgb)));
|
||||||
|
|
||||||
|
perform_compose(&composer, rgb_clear, rgb_commit, NULL);
|
||||||
|
mu_assert_int_eq(0xdeadbeef, rgb_final_state);
|
||||||
|
|
||||||
|
ValueComposerHandle* layer1_handle =
|
||||||
|
add_compose_layer(&composer, COPY_COMPOSE, &layer1_mutex, UiLayerNotify);
|
||||||
|
mu_assert_pointers_not_eq(layer1_handle, NULL);
|
||||||
|
|
||||||
|
// RGB state should be updated with the layer1 state
|
||||||
|
perform_compose(&composer, rgb_clear, rgb_commit, NULL);
|
||||||
|
mu_assert_int_eq(0x000000, rgb_final_state);
|
||||||
|
|
||||||
|
layer2_red = 0xcc;
|
||||||
|
ValueComposerHandle* layer2_handle =
|
||||||
|
add_compose_layer(&composer, set_red_composer, &layer2_red, UiLayerAboveNotify);
|
||||||
|
mu_assert_pointers_not_eq(layer2_handle, NULL);
|
||||||
|
|
||||||
|
// RGB state should be updated with the layer1 and layer2 state, in order
|
||||||
|
perform_compose(&composer, rgb_clear, rgb_commit, NULL);
|
||||||
|
mu_assert_int_eq(0x0000cc, rgb_final_state);
|
||||||
|
|
||||||
|
// Change layer1 state
|
||||||
|
Rgb* state = acquire_mutex(&layer1_mutex, 0);
|
||||||
|
mu_assert_pointers_not_eq(state, NULL);
|
||||||
|
state->red = 0x12;
|
||||||
|
state->green = 0x34;
|
||||||
|
state->blue = 0x56;
|
||||||
|
release_mutex(&layer1_mutex, state);
|
||||||
|
|
||||||
|
// Nothing should happen, we need to trigger composition request first
|
||||||
|
perform_compose(&composer, rgb_clear, rgb_commit, NULL);
|
||||||
|
mu_assert_int_eq(0x0000cc, rgb_final_state);
|
||||||
|
|
||||||
|
request_compose(layer1_handle);
|
||||||
|
|
||||||
|
perform_compose(&composer, rgb_clear, rgb_commit, NULL);
|
||||||
|
mu_assert_int_eq(0x5634cc, rgb_final_state);
|
||||||
|
|
||||||
|
// Change layer2 state
|
||||||
|
layer2_red = 0xff;
|
||||||
|
|
||||||
|
// Nothing should happen, we need to trigger composition request first
|
||||||
|
perform_compose(&composer, rgb_clear, rgb_commit, NULL);
|
||||||
|
mu_assert_int_eq(0x5634cc, rgb_final_state);
|
||||||
|
|
||||||
|
request_compose(layer2_handle);
|
||||||
|
|
||||||
|
perform_compose(&composer, rgb_clear, rgb_commit, NULL);
|
||||||
|
mu_assert_int_eq(0x5634ff, rgb_final_state);
|
||||||
|
|
||||||
|
// Remove layer1
|
||||||
|
mu_check(remove_compose_layer(layer1_handle));
|
||||||
|
|
||||||
|
perform_compose(&composer, rgb_clear, rgb_commit, NULL);
|
||||||
|
mu_assert_int_eq(0x0000ff, rgb_final_state);
|
||||||
|
|
||||||
|
// Remove layer2
|
||||||
|
mu_check(remove_compose_layer(layer2_handle));
|
||||||
|
|
||||||
|
perform_compose(&composer, rgb_clear, rgb_commit, NULL);
|
||||||
|
mu_assert_int_eq(0x000000, rgb_final_state);
|
||||||
|
|
||||||
|
mu_check(delete_composer(&composer));
|
||||||
|
}
|
||||||
|
|
||||||
|
static const uint32_t notify_value_0 = 0x12345678;
|
||||||
|
static const uint32_t notify_value_1 = 0x11223344;
|
||||||
|
|
||||||
|
static uint32_t pubsub_value = 0;
|
||||||
|
|
||||||
|
void test_value_manager_handler(void* arg, void* ctx) {
|
||||||
|
pubsub_value = *(uint32_t*)arg;
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_furi_value_manager() {
|
||||||
|
uint32_t value = 0;
|
||||||
|
ValueManager managed;
|
||||||
|
|
||||||
|
mu_check(init_managed(&managed, &value, sizeof(value)));
|
||||||
|
|
||||||
|
pubsub_value = 0;
|
||||||
|
|
||||||
|
PubSubItem* test_pubsub_item;
|
||||||
|
test_pubsub_item = subscribe_pubsub(&managed.pubsub, test_value_manager_handler, 0);
|
||||||
|
mu_assert_pointers_not_eq(test_pubsub_item, NULL);
|
||||||
|
|
||||||
|
mu_check(write_managed(&managed, (void*)¬ify_value_0, sizeof(notify_value_0), 100));
|
||||||
|
|
||||||
|
mu_assert_int_eq(pubsub_value, notify_value_0);
|
||||||
|
|
||||||
|
uint32_t* ptr = acquire_mutex(&managed.value, 100);
|
||||||
|
mu_assert_pointers_not_eq(ptr, NULL);
|
||||||
|
|
||||||
|
*ptr = notify_value_1;
|
||||||
|
|
||||||
|
mu_check(commit_managed(&managed, ptr));
|
||||||
|
|
||||||
|
mu_assert_int_eq(pubsub_value, notify_value_1);
|
||||||
|
|
||||||
|
mu_check(delete_managed(&managed));
|
||||||
|
}
|
@ -37,6 +37,8 @@ void test_furi_valuemutex() {
|
|||||||
//acquire mutex blocking case
|
//acquire mutex blocking case
|
||||||
//write mutex blocking case
|
//write mutex blocking case
|
||||||
//read mutex blocking case
|
//read mutex blocking case
|
||||||
|
|
||||||
|
mu_check(delete_mutex(&valuemutex));
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -119,4 +121,6 @@ void test_furi_concurrent_access() {
|
|||||||
mu_assert_pointers_eq(second_app->handler, NULL);
|
mu_assert_pointers_eq(second_app->handler, NULL);
|
||||||
|
|
||||||
mu_assert_int_eq(value.a, value.b);
|
mu_assert_int_eq(value.a, value.b);
|
||||||
|
|
||||||
|
mu_check(delete_mutex(&mutex));
|
||||||
}
|
}
|
@ -12,6 +12,9 @@ void test_furi_create_open();
|
|||||||
void test_furi_valuemutex();
|
void test_furi_valuemutex();
|
||||||
void test_furi_concurrent_access();
|
void test_furi_concurrent_access();
|
||||||
void test_furi_pubsub();
|
void test_furi_pubsub();
|
||||||
|
void test_furi_value_composer();
|
||||||
|
void test_furi_value_manager();
|
||||||
|
void test_furi_event();
|
||||||
|
|
||||||
void test_furi_memmgr();
|
void test_furi_memmgr();
|
||||||
|
|
||||||
@ -60,6 +63,15 @@ MU_TEST(mu_test_furi_memmgr) {
|
|||||||
test_furi_memmgr();
|
test_furi_memmgr();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MU_TEST(mu_test_furi_value_expanders) {
|
||||||
|
test_furi_value_composer();
|
||||||
|
test_furi_value_manager();
|
||||||
|
}
|
||||||
|
|
||||||
|
MU_TEST(mu_test_furi_event) {
|
||||||
|
test_furi_event();
|
||||||
|
}
|
||||||
|
|
||||||
MU_TEST_SUITE(test_suite) {
|
MU_TEST_SUITE(test_suite) {
|
||||||
MU_SUITE_CONFIGURE(&test_setup, &test_teardown);
|
MU_SUITE_CONFIGURE(&test_setup, &test_teardown);
|
||||||
|
|
||||||
@ -72,6 +84,8 @@ MU_TEST_SUITE(test_suite) {
|
|||||||
MU_RUN_TEST(mu_test_furi_valuemutex);
|
MU_RUN_TEST(mu_test_furi_valuemutex);
|
||||||
MU_RUN_TEST(mu_test_furi_concurrent_access);
|
MU_RUN_TEST(mu_test_furi_concurrent_access);
|
||||||
MU_RUN_TEST(mu_test_furi_pubsub);
|
MU_RUN_TEST(mu_test_furi_pubsub);
|
||||||
|
MU_RUN_TEST(mu_test_furi_value_expanders);
|
||||||
|
MU_RUN_TEST(mu_test_furi_event);
|
||||||
|
|
||||||
MU_RUN_TEST(mu_test_furi_memmgr);
|
MU_RUN_TEST(mu_test_furi_memmgr);
|
||||||
}
|
}
|
||||||
|
24
core/api-basic/event.c
Normal file
24
core/api-basic/event.c
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
#include "event.h"
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
bool init_event(Event* event) {
|
||||||
|
event->semaphore_id = osSemaphoreNew(1, 0, NULL);
|
||||||
|
return event->semaphore_id != NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool delete_event(Event* event) {
|
||||||
|
return osSemaphoreDelete(event->semaphore_id) == osOK;
|
||||||
|
}
|
||||||
|
|
||||||
|
void signal_event(Event* event) {
|
||||||
|
// Ignore the result, as we do not care about repeated event signalling.
|
||||||
|
osSemaphoreRelease(event->semaphore_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
void wait_event(Event* event) {
|
||||||
|
wait_event_with_timeout(event, osWaitForever);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool wait_event_with_timeout(Event* event, uint32_t timeout_ms) {
|
||||||
|
return osSemaphoreAcquire(event->semaphore_id, timeout_ms) == osOK;
|
||||||
|
}
|
36
core/api-basic/event.h
Normal file
36
core/api-basic/event.h
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include "cmsis_os.h"
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
osSemaphoreId_t semaphore_id;
|
||||||
|
} Event;
|
||||||
|
|
||||||
|
/*
|
||||||
|
Creates Event.
|
||||||
|
*/
|
||||||
|
bool init_event(Event* event);
|
||||||
|
|
||||||
|
/*
|
||||||
|
Free resources allocated by `init_event`.
|
||||||
|
This function doesn't free the memory occupied by `Event` itself.
|
||||||
|
*/
|
||||||
|
bool delete_event(Event* event);
|
||||||
|
|
||||||
|
/*
|
||||||
|
Signals the event.
|
||||||
|
If the event is already in "signalled" state, nothing happens.
|
||||||
|
*/
|
||||||
|
void signal_event(Event* event);
|
||||||
|
|
||||||
|
/*
|
||||||
|
Waits until the event is signalled.
|
||||||
|
*/
|
||||||
|
void wait_event(Event* event);
|
||||||
|
|
||||||
|
/*
|
||||||
|
Waits with a timeout until the event is signalled.
|
||||||
|
*/
|
||||||
|
bool wait_event_with_timeout(Event* event, uint32_t timeout_ms);
|
@ -1,4 +1,5 @@
|
|||||||
#include "pubsub.h"
|
#include "pubsub.h"
|
||||||
|
#include "flipper_v2.h"
|
||||||
|
|
||||||
bool init_pubsub(PubSub* pubsub) {
|
bool init_pubsub(PubSub* pubsub) {
|
||||||
// mutex without name,
|
// mutex without name,
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "flipper_v2.h"
|
#include "cmsis_os.h"
|
||||||
#include "m-list.h"
|
#include "m-list.h"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
177
core/api-basic/value-expanders.c
Normal file
177
core/api-basic/value-expanders.c
Normal file
@ -0,0 +1,177 @@
|
|||||||
|
#include "value-expanders.h"
|
||||||
|
|
||||||
|
bool init_composer(ValueComposer* composer, void* value) {
|
||||||
|
if(!init_mutex(&composer->value, value, 0)) return false;
|
||||||
|
|
||||||
|
for(size_t i = 0; i < sizeof(composer->layers) / sizeof(composer->layers[0]); i++) {
|
||||||
|
list_composer_cb_init(composer->layers[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// mutex without name,
|
||||||
|
// no attributes (unfortunatly robust mutex is not supported by FreeRTOS),
|
||||||
|
// with dynamic memory allocation
|
||||||
|
const osMutexAttr_t value_mutex_attr = {
|
||||||
|
.name = NULL, .attr_bits = 0, .cb_mem = NULL, .cb_size = 0U};
|
||||||
|
|
||||||
|
composer->mutex = osMutexNew(&value_mutex_attr);
|
||||||
|
if(composer->mutex == NULL) return false;
|
||||||
|
|
||||||
|
if(!init_event(&composer->request)) return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool delete_composer(ValueComposer* composer) {
|
||||||
|
if(osMutexAcquire(composer->mutex, osWaitForever) == osOK) {
|
||||||
|
bool result = true;
|
||||||
|
result &= delete_mutex(&composer->value);
|
||||||
|
|
||||||
|
for(size_t i = 0; i < sizeof(composer->layers) / sizeof(composer->layers[0]); i++) {
|
||||||
|
list_composer_cb_clear(composer->layers[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
result &= osMutexDelete(composer->mutex) == osOK;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ValueComposerHandle*
|
||||||
|
add_compose_layer(ValueComposer* composer, ValueComposerCallback cb, void* ctx, UiLayer layer) {
|
||||||
|
if(osMutexAcquire(composer->mutex, osWaitForever) == osOK) {
|
||||||
|
// put uninitialized item to the list
|
||||||
|
ValueComposerHandle* handle = list_composer_cb_push_raw(composer->layers[layer]);
|
||||||
|
|
||||||
|
handle->cb = cb;
|
||||||
|
handle->ctx = ctx;
|
||||||
|
handle->layer = layer;
|
||||||
|
handle->composer = composer;
|
||||||
|
|
||||||
|
// TODO unregister handle on app exit
|
||||||
|
//flapp_on_exit(remove_compose_layer, handle);
|
||||||
|
|
||||||
|
osMutexRelease(composer->mutex);
|
||||||
|
|
||||||
|
// Layers changed, request composition
|
||||||
|
signal_event(&composer->request);
|
||||||
|
|
||||||
|
return handle;
|
||||||
|
} else {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool remove_compose_layer(ValueComposerHandle* handle) {
|
||||||
|
ValueComposer* composer = handle->composer;
|
||||||
|
|
||||||
|
if(osMutexAcquire(composer->mutex, osWaitForever) == osOK) {
|
||||||
|
bool result = false;
|
||||||
|
|
||||||
|
// iterate over items
|
||||||
|
list_composer_cb_it_t it;
|
||||||
|
for(list_composer_cb_it(it, composer->layers[handle->layer]); !list_composer_cb_end_p(it);
|
||||||
|
list_composer_cb_next(it)) {
|
||||||
|
const ValueComposerHandle* item = list_composer_cb_cref(it);
|
||||||
|
|
||||||
|
// if the iterator is equal to our element
|
||||||
|
if(item == handle) {
|
||||||
|
list_composer_cb_remove(composer->layers[handle->layer], it);
|
||||||
|
result = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
osMutexRelease(composer->mutex);
|
||||||
|
|
||||||
|
// Layers changed, request composition
|
||||||
|
signal_event(&composer->request);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void request_compose(ValueComposerHandle* handle) {
|
||||||
|
ValueComposer* composer = handle->composer;
|
||||||
|
signal_event(&composer->request);
|
||||||
|
}
|
||||||
|
|
||||||
|
void perform_compose(
|
||||||
|
ValueComposer* composer,
|
||||||
|
ValueComposerCallback start_cb,
|
||||||
|
ValueComposerCallback end_cb,
|
||||||
|
void* ctx) {
|
||||||
|
if(!wait_event_with_timeout(&composer->request, 0)) return;
|
||||||
|
|
||||||
|
void* state = acquire_mutex(&composer->value, 0);
|
||||||
|
if(state == NULL) return;
|
||||||
|
|
||||||
|
if(start_cb != NULL) start_cb(ctx, state);
|
||||||
|
perform_compose_internal(composer, state);
|
||||||
|
if(end_cb != NULL) end_cb(ctx, state);
|
||||||
|
|
||||||
|
release_mutex(&composer->value, state);
|
||||||
|
}
|
||||||
|
|
||||||
|
void perform_compose_internal(ValueComposer* composer, void* state) {
|
||||||
|
if(osMutexAcquire(composer->mutex, osWaitForever) == osOK) {
|
||||||
|
// Compose all levels for now
|
||||||
|
for(size_t i = 0; i < sizeof(composer->layers) / sizeof(composer->layers[0]); i++) {
|
||||||
|
// iterate over items
|
||||||
|
list_composer_cb_it_t it;
|
||||||
|
for(list_composer_cb_it(it, composer->layers[i]); !list_composer_cb_end_p(it);
|
||||||
|
list_composer_cb_next(it)) {
|
||||||
|
const ValueComposerHandle* h = list_composer_cb_cref(it);
|
||||||
|
h->cb(h->ctx, state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
osMutexRelease(composer->mutex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void COPY_COMPOSE(void* ctx, void* state) {
|
||||||
|
read_mutex((ValueMutex*)ctx, state, 0, osWaitForever);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool init_managed(ValueManager* managed, void* value, size_t size) {
|
||||||
|
if(!init_pubsub(&managed->pubsub)) return false;
|
||||||
|
if(!init_mutex(&managed->value, value, size)) {
|
||||||
|
delete_pubsub(&managed->pubsub);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool delete_managed(ValueManager* managed) {
|
||||||
|
bool result = true;
|
||||||
|
result &= delete_mutex(&managed->value);
|
||||||
|
result &= delete_pubsub(&managed->pubsub);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool write_managed(ValueManager* managed, void* data, size_t len, uint32_t timeout) {
|
||||||
|
void* value = acquire_mutex(&managed->value, timeout);
|
||||||
|
if(value == NULL) return false;
|
||||||
|
|
||||||
|
memcpy(value, data, len);
|
||||||
|
|
||||||
|
notify_pubsub(&managed->pubsub, value);
|
||||||
|
|
||||||
|
if(!release_mutex(&managed->value, value)) return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool commit_managed(ValueManager* managed, void* value) {
|
||||||
|
if(value != managed->value.value) return false;
|
||||||
|
|
||||||
|
notify_pubsub(&managed->pubsub, value);
|
||||||
|
|
||||||
|
if(!release_mutex(&managed->value, value)) return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
@ -1,24 +0,0 @@
|
|||||||
#include "value-expanders.h"
|
|
||||||
|
|
||||||
bool commit_managed(ValueManager* managed, void* value) {
|
|
||||||
if(value != managed->mutex->value) return false;
|
|
||||||
|
|
||||||
notify_pubsub(&managed->pubsub, value);
|
|
||||||
|
|
||||||
if(!osMutexGive(managed->mutex)) return false;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool write_managed(ValueManager* managed, void* data, size_t len, uint32_t timeout) {
|
|
||||||
void* value = acquire_mutex(managed->mutex, timeout);
|
|
||||||
if(value == NULL) return false;
|
|
||||||
|
|
||||||
memcpy(value, data, len):
|
|
||||||
|
|
||||||
notify_pubsub(&managed->pubsub, value);
|
|
||||||
|
|
||||||
if(!release_mutex(managed->mutex, value)) return false;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
@ -1,26 +1,76 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "flipper.h"
|
#include "flipper.h"
|
||||||
|
#include "valuemutex.h"
|
||||||
|
#include "pubsub.h"
|
||||||
|
#include "event.h"
|
||||||
|
#include "m-list.h"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
== Value composer ==
|
== Value composer ==
|
||||||
*/
|
*/
|
||||||
|
|
||||||
typedef void(ValueComposerCallback)(void* ctx, void* state);
|
typedef struct ValueComposer ValueComposer;
|
||||||
|
|
||||||
void COPY_COMPOSE(void* ctx, void* state) {
|
typedef void (*ValueComposerCallback)(void* ctx, void* state);
|
||||||
read_mutex((ValueMutex*)ctx, state, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
typedef enum { UiLayerBelowNotify UiLayerNotify, UiLayerAboveNotify } UiLayer;
|
typedef enum { UiLayerBelowNotify, UiLayerNotify, UiLayerAboveNotify } UiLayer;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
ValueComposerCallback cb;
|
||||||
|
void* ctx;
|
||||||
|
UiLayer layer;
|
||||||
|
ValueComposer* composer;
|
||||||
|
} ValueComposerHandle;
|
||||||
|
|
||||||
|
LIST_DEF(list_composer_cb, ValueComposerHandle, M_POD_OPLIST);
|
||||||
|
|
||||||
|
struct ValueComposer {
|
||||||
|
ValueMutex value;
|
||||||
|
list_composer_cb_t layers[3];
|
||||||
|
osMutexId_t mutex;
|
||||||
|
Event request;
|
||||||
|
};
|
||||||
|
|
||||||
|
void COPY_COMPOSE(void* ctx, void* state);
|
||||||
|
|
||||||
|
bool init_composer(ValueComposer* composer, void* value);
|
||||||
|
|
||||||
|
/*
|
||||||
|
Free resources allocated by `init_composer`.
|
||||||
|
This function doesn't free the memory occupied by `ValueComposer` itself.
|
||||||
|
*/
|
||||||
|
bool delete_composer(ValueComposer* composer);
|
||||||
|
|
||||||
ValueComposerHandle*
|
ValueComposerHandle*
|
||||||
add_compose_layer(ValueComposer* composer, ValueComposerCallback cb, void* ctx, uint32_t layer);
|
add_compose_layer(ValueComposer* composer, ValueComposerCallback cb, void* ctx, UiLayer layer);
|
||||||
|
|
||||||
bool remove_compose_layer(ValueComposerHandle* handle);
|
bool remove_compose_layer(ValueComposerHandle* handle);
|
||||||
|
|
||||||
void request_compose(ValueComposerHandle* handle);
|
void request_compose(ValueComposerHandle* handle);
|
||||||
|
|
||||||
|
/*
|
||||||
|
Perform composition if requested.
|
||||||
|
|
||||||
|
`start_cb` and `end_cb` will be called before and after all layer callbacks, respectively.
|
||||||
|
Both `start_cb` and `end_cb` can be NULL. They can be used to set initial state (e.g. clear screen)
|
||||||
|
and commit the final state.
|
||||||
|
*/
|
||||||
|
void perform_compose(
|
||||||
|
ValueComposer* composer,
|
||||||
|
ValueComposerCallback start_cb,
|
||||||
|
ValueComposerCallback end_cb,
|
||||||
|
void* ctx);
|
||||||
|
|
||||||
|
/*
|
||||||
|
Perform composition.
|
||||||
|
|
||||||
|
This function should be called with value mutex acquired.
|
||||||
|
This function is here for convenience, so that developers can write their own compose loops.
|
||||||
|
See `perform_compose` function body for an example.
|
||||||
|
*/
|
||||||
|
void perform_compose_internal(ValueComposer* composer, void* state);
|
||||||
|
|
||||||
// See [LED](LED-API) or [Display](Display-API) API for examples.
|
// See [LED](LED-API) or [Display](Display-API) API for examples.
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -39,6 +89,14 @@ typedef struct {
|
|||||||
PubSub pubsub;
|
PubSub pubsub;
|
||||||
} ValueManager;
|
} ValueManager;
|
||||||
|
|
||||||
|
bool init_managed(ValueManager* managed, void* value, size_t size);
|
||||||
|
|
||||||
|
/*
|
||||||
|
Free resources allocated by `init_managed`.
|
||||||
|
This function doesn't free the memory occupied by `ValueManager` itself.
|
||||||
|
*/
|
||||||
|
bool delete_managed(ValueManager* managed);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
acquire value, changes it and send notify with current value.
|
acquire value, changes it and send notify with current value.
|
||||||
*/
|
*/
|
||||||
|
@ -17,6 +17,14 @@ bool init_mutex(ValueMutex* valuemutex, void* value, size_t size) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool delete_mutex(ValueMutex* valuemutex) {
|
||||||
|
if(osMutexAcquire(valuemutex->mutex, osWaitForever) == osOK) {
|
||||||
|
return osMutexDelete(valuemutex->mutex) == osOK;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void* acquire_mutex(ValueMutex* valuemutex, uint32_t timeout) {
|
void* acquire_mutex(ValueMutex* valuemutex, uint32_t timeout) {
|
||||||
if(osMutexAcquire(valuemutex->mutex, timeout) == osOK) {
|
if(osMutexAcquire(valuemutex->mutex, timeout) == osOK) {
|
||||||
return valuemutex->value;
|
return valuemutex->value;
|
||||||
|
@ -21,6 +21,12 @@ Creates ValueMutex.
|
|||||||
*/
|
*/
|
||||||
bool init_mutex(ValueMutex* valuemutex, void* value, size_t size);
|
bool init_mutex(ValueMutex* valuemutex, void* value, size_t size);
|
||||||
|
|
||||||
|
/*
|
||||||
|
Free resources allocated by `init_mutex`.
|
||||||
|
This function doesn't free the memory occupied by `ValueMutex` itself.
|
||||||
|
*/
|
||||||
|
bool delete_mutex(ValueMutex* valuemutex);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Call for work with data stored in mutex.
|
Call for work with data stored in mutex.
|
||||||
Returns pointer to data if success, NULL otherwise.
|
Returns pointer to data if success, NULL otherwise.
|
||||||
|
@ -11,6 +11,8 @@ extern "C" {
|
|||||||
#include "cmsis_os2.h"
|
#include "cmsis_os2.h"
|
||||||
#include "api-basic/valuemutex.h"
|
#include "api-basic/valuemutex.h"
|
||||||
#include "api-basic/pubsub.h"
|
#include "api-basic/pubsub.h"
|
||||||
|
#include "api-basic/value-expanders.h"
|
||||||
|
#include "api-basic/event.h"
|
||||||
|
|
||||||
#include "api-basic/memmgr.h"
|
#include "api-basic/memmgr.h"
|
||||||
|
|
||||||
|
@ -97,3 +97,12 @@ osStatus_t osMutexRelease (osMutexId_t mutex_id);
|
|||||||
osStatus_t osMutexDelete (osMutexId_t mutex_id);
|
osStatus_t osMutexDelete (osMutexId_t mutex_id);
|
||||||
|
|
||||||
#define osWaitForever portMAX_DELAY
|
#define osWaitForever portMAX_DELAY
|
||||||
|
|
||||||
|
typedef StaticSemaphore_t osSemaphoreDef_t;
|
||||||
|
typedef SemaphoreHandle_t osSemaphoreId_t;
|
||||||
|
typedef struct {} osSemaphoreAttr_t;
|
||||||
|
|
||||||
|
osSemaphoreId_t osSemaphoreNew(uint32_t max_count, uint32_t initial_count, const osSemaphoreAttr_t *attr);
|
||||||
|
osStatus_t osSemaphoreAcquire(osSemaphoreId_t semaphore_id, uint32_t timeout);
|
||||||
|
osStatus_t osSemaphoreRelease(osSemaphoreId_t semaphore_id);
|
||||||
|
osStatus_t osSemaphoreDelete(osSemaphoreId_t semaphore_id);
|
||||||
|
@ -265,3 +265,55 @@ osStatus_t osMutexDelete (osMutexId_t mutex_id) {
|
|||||||
return osError;
|
return osError;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
osSemaphoreId_t osSemaphoreNew(uint32_t max_count, uint32_t initial_count, const osSemaphoreAttr_t *attr) {
|
||||||
|
if(max_count != 1) {
|
||||||
|
// Non-binary semaphors are not supported at the moment
|
||||||
|
return osErrorParameter;
|
||||||
|
}
|
||||||
|
if(attr != NULL) {
|
||||||
|
// Attributes are not supported at the moment
|
||||||
|
return osErrorParameter;
|
||||||
|
}
|
||||||
|
|
||||||
|
SemaphoreHandle_t handle = osMutexNew(NULL);
|
||||||
|
if(handle == NULL) return NULL;
|
||||||
|
|
||||||
|
if(initial_count == 0) {
|
||||||
|
xSemaphoreTake(handle, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
osStatus_t osSemaphoreAcquire(osSemaphoreId_t semaphore_id, uint32_t timeout) {
|
||||||
|
if(semaphore_id == NULL) {
|
||||||
|
return osErrorParameter;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(xSemaphoreTake(semaphore_id, timeout) == pdTRUE) {
|
||||||
|
return osOK;
|
||||||
|
} else {
|
||||||
|
return osErrorTimeout;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
osStatus_t osSemaphoreRelease(osSemaphoreId_t semaphore_id) {
|
||||||
|
if(semaphore_id == NULL) {
|
||||||
|
return osErrorParameter;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(xSemaphoreGive(semaphore_id) == pdTRUE) {
|
||||||
|
return osOK;
|
||||||
|
} else {
|
||||||
|
return osErrorTimeout;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
osStatus_t osSemaphoreDelete(osSemaphoreId_t semaphore_id) {
|
||||||
|
if(semaphore_id == NULL) {
|
||||||
|
return osErrorParameter;
|
||||||
|
}
|
||||||
|
|
||||||
|
return osMutexDelete(semaphore_id);
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user