50#include <libminilisp.h>
67 auto eventID = eventObj.
getString(
"eventID");
70 if (eventID.isEmpty() || valueStr.isEmpty()) {
74 auto value = valueStr.toInt();
75 auto isNumber = value || valueStr ==
"0";
80 bool isNewEvent = !mIncomingEvents.exist(eventID);
82 auto eventQueue = std::make_shared<EventQueue>();
83 mIncomingEvents.put(eventID, eventQueue);
87 auto eventQueue = mIncomingEvents.get(eventID);
88 eventQueue->lastAccessed = millis();
90 size_t queueSizeBefore = eventQueue->queue.size();
92 size_t queueSizeAfter = eventQueue->queue.size();
94 if (queueSizeAfter > queueSizeBefore) {
95 UNIOT_LOG_TRACE(
"pushed event '%s' with value '%d', queue size: %d", eventID.c_str(), value, queueSizeAfter);
97 UNIOT_LOG_WARN(
"event queue for '%s' is full (limit: %d), oldest event was dropped", eventID.c_str(), EVENTS_LIMIT);
102 if (!mIncomingEvents.exist(eventID)) {
106 auto eventQueue = mIncomingEvents.get(eventID);
107 eventQueue->isUsedInScript =
true;
108 eventQueue->lastAccessed = millis();
110 return eventQueue->queue.size() > 0;
116 if (!mIncomingEvents.exist(eventID)) {
117 UNIOT_LOG_WARN(
"attempted to pop non-existent event '%s'", eventID.c_str());
121 auto eventQueue = mIncomingEvents.get(eventID);
122 eventQueue->isUsedInScript =
true;
123 eventQueue->lastAccessed = millis();
125 if (eventQueue->queue.size() == 0) {
126 UNIOT_LOG_WARN(
"attempted to pop from empty event queue '%s'", eventID.c_str());
130 return eventQueue->queue.popLimited(emptyEvent);
134 unsigned long currentTime = millis();
136 size_t totalEvents = 0;
137 size_t unusedEvents = 0;
138 size_t expiredEvents = 0;
139 size_t totalQueuedItems = 0;
143 auto eventQueue = entry.second;
144 totalQueuedItems += eventQueue->queue.size();
145 unsigned long timeSinceLastAccess = currentTime - eventQueue->lastAccessed;
147 if (!eventQueue->isUsedInScript) {
149 if (timeSinceLastAccess > EVENT_TTL_MS) {
151 keysToRemove.
push(entry.first);
152 UNIOT_LOG_DEBUG(
"marking event '%s' for removal (unused, last accessed %lu ms ago)",
153 entry.first.c_str(), timeSinceLastAccess);
159 keysToRemove.
forEach([
this](
const String &key) {
160 mIncomingEvents.remove(key);
163 if (expiredEvents > 0) {
164 UNIOT_LOG_INFO(
"cleaned up %d expired events (total: %d, unused: %d, queued items: %d)",
165 expiredEvents, totalEvents, unusedEvents, totalQueuedItems);
166 }
else if (totalEvents > 0) {
167 UNIOT_LOG_TRACE(
"no events to cleanup (total: %d, unused: %d, queued items: %d)", totalEvents, unusedEvents, totalQueuedItems);
172 mIncomingEvents.clean();
179 unsigned long lastAccessed;
182 EventQueue() : lastAccessed(millis()), isUsedInScript(false) {
183 queue.
limit(EVENTS_LIMIT);
187 Map<String, SharedPointer<EventQueue>> mIncomingEvents;
188 static constexpr size_t EVENTS_LIMIT = 2;
189 static constexpr unsigned long EVENT_TTL_MS = 30000;
218 return mTaskLispEval;
222 return mTaskEventCleanup;
231 return lisp_is_created();
240 return mTaskLispEval->isAttached();
248 return lisp_mem_used();
269 mTaskLispEval->detach();
276 mTaskLispEval->detach();
278 _createMachine(reason);
280 auto code = mLastCode.terminate().c_str();
283 _refreshIncomingEvents();
284 lisp_eval(mLispRoot, mLispEnv, code);
285 if (!mTaskLispEval->isAttached()) {
335 for (
auto i = 0; i < description.argsCount; i++) {
353 .
append(description.returnType)
355 .
append(description.argsCount,
reinterpret_cast<const uint8_t *
>(description.argsTypes));
388 _pushIncomingEvent(data);
406 auto fnPrintOut = [](
const char *msg,
int size) {
415 auto fnPrintLog = [](
const char *msg,
int size) {
424 auto fnPrintErr = [](
const char *msg,
int size) {
429 instance.mTaskLispEval->detach();
433 lisp_set_cycle_yield(yield);
434 lisp_set_printers(fnPrintOut, fnPrintLog, fnPrintErr);
439 auto root = mLispRoot;
442 DEFINE2(t_obj, t_pass);
443 *t_pass = get_variable(root, env,
"#t_pass");
444 (*t_pass)->cdr->value = t;
445 *t_obj = get_variable(root, env,
"#t_obj")->cdr;
446 safe_eval(root, env, t_obj);
456 mEventManager.cleanupUnusedEvents();
466 void _constructLispEnv() {
467 mLispEnvConstructor[0] = NULL;
468 mLispEnvConstructor[1] = NULL;
469 mLispEnvConstructor[2] = ROOT_END;
470 mLispRoot = mLispEnvConstructor;
471 mLispEnv = (Obj **)(mLispEnvConstructor + 1);
484 *mLispEnv = make_env(mLispRoot, &Nil, &Nil);
485 define_constants(mLispRoot, mLispEnv);
486 define_primitives(mLispRoot, mLispEnv);
489 add_constant(mLispRoot, mLispEnv,
"#t_obj", &Nil);
490 add_constant_int(mLispRoot, mLispEnv,
"#t_pass", 0);
491 add_primitive(mLispRoot, mLispEnv,
"task", mPrimitiveTask);
492 add_primitive(mLispRoot, mLispEnv,
"is_event", mPrimitiveIsEventAvailable);
493 add_primitive(mLispRoot, mLispEnv,
"pop_event", mPrimitivePopEvent);
494 add_primitive(mLispRoot, mLispEnv,
"push_event", mPrimitivePushEvent);
498 add_primitive(mLispRoot, mLispEnv, holder.first.c_str(), holder.second);
501 UNIOT_LOG_DEBUG(
"lisp machine created, mem used: %d", lisp_mem_used());
523 if (!lisp_is_created()) {
537 void _refreshIncomingEvents() {
538 mEventManager.clean();
547 void _pushIncomingEvent(
const Bytes &eventData) {
549 mEventManager.pushEvent(eventData);
559 bool _isIncomingEventAvailable(
const String &eventID) {
560 return mEventManager.isEventAvailable(eventID);
570 IncomingEventManager::IncomingEvent _popIncomingEvent(
const String &eventID) {
571 return mEventManager.popEvent(eventID);
582 bool _pushOutgoingEvent(String eventID,
int value) {
584 event.put(
"eventID", eventID.c_str());
585 event.put(
"value", value);
605 .init(root, env, list);
608 auto times = expeditor.getArgInt(0);
609 auto ms = expeditor.getArgInt(1);
610 auto obj = expeditor.getArg(2);
613 *t_obj = get_variable(root, env,
"#t_obj");
616 mTaskLispEval->attach(ms, times);
618 return expeditor.makeBool(
true);
633 .init(root, env, list);
636 auto eventId = expeditor.getArgSymbol(0);
637 auto available = _isIncomingEventAvailable(eventId);
639 return expeditor.makeBool(available);
656 .init(root, env, list);
659 auto eventId = expeditor.getArgSymbol(0);
660 auto event = _popIncomingEvent(eventId);
661 UNIOT_LOG_WARN_IF(event.errorCode,
"error popping event '%s': %d", eventId, event.errorCode);
662 return expeditor.makeInt(event.value);
678 .init(root, env, list);
681 auto eventId = expeditor.getArgSymbol(0);
682 auto value = expeditor.getArgInt(1);
684 auto sent = _pushOutgoingEvent(eventId, value);
686 return expeditor.makeBool(sent);
693 ClearQueue<Pair<String, Primitive *>> mUserPrimitives;
703 void *mLispEnvConstructor[3];
709 IncomingEventManager mEventManager;
Lisp interpreter event definitions for the Uniot event system.
Bytes & terminate()
Ensures the byte array is null-terminated.
Definition Bytes.h:281
const char * c_str() const
Gets the byte array as a C string.
Definition Bytes.h:306
size_t size() const
Gets the size of the byte array.
Definition Bytes.h:343
Definition ClearQueue.h:38
void forEach(VoidCallback callback) const
Executes a callback function on each element in the queue.
Definition ClearQueue.h:325
void push(const T &value)
Adds an element to the end of the queue.
Definition ClearQueue.h:207
Definition LimitedQueue.h:36
size_t limit() const
Get the current size limit of the queue.
Definition LimitedQueue.h:51
Array & append(int value)
Append an integer to the array.
Definition CBORObject.h:603
Array appendArray()
Append a new array as an element.
Definition CBORObject.h:645
Definition CBORObject.h:40
Array putArray(int key)
Create or get an array at a specific integer key.
Definition CBORObject.h:131
String getString(int key) const
Get a string value at a specific integer key.
Definition CBORObject.h:429
String getValueAsString(int key) const
Get a value as a string at a specific integer key.
Definition CBORObject.h:450
void emitEvent(unsigned int topic, int msg)
void receiveDataFromChannel(T_topic channel, DataChannelCallback callback)
Receives data from a specific channel on all connected EventBus instances.
Definition EventEntity.h:117
bool sendDataToChannel(T_topic channel, T_data data)
Sends data to a specific channel on all connected EventBus instances.
Definition EventEntity.h:98
EventListener * listenToEvent(unsigned int topic)
IncomingEvent popEvent(const String &eventID)
Definition unLisp.h:113
void clean()
Definition unLisp.h:171
void cleanupUnusedEvents()
Definition unLisp.h:133
bool isEventAvailable(const String &eventID)
Definition unLisp.h:101
void pushEvent(const Bytes &eventData)
Definition unLisp.h:65
@ BoolInt
Definition LispHelper.h:100
@ Symbol
Definition LispHelper.h:101
@ Cell
Definition LispHelper.h:102
@ Bool
Definition LispHelper.h:99
@ Int
Definition LispHelper.h:98
static PrimitiveDescription extractDescription(Primitive *primitive)
Extracts description metadata from a primitive function.
Definition PrimitiveExpeditor.h:116
static PrimitiveExpeditorInitializer describe(const String &name, Lisp::Type returnType, int argsCount,...)
Describes a primitive function by setting up its metadata.
Definition PrimitiveExpeditor.h:84
void assertDescribedArgs()
Asserts arguments against the primitive's description.
Definition PrimitiveExpeditor.h:297
Singleton(const Singleton &)=delete
static unLisp & getInstance()
Definition Singleton.h:73
TaskScheduler::TaskPtr getCleanupTask()
Definition unLisp.h:221
void setStopHook(LispStopHook hook)
Set the hook called when a script stops.
Definition unLisp.h:319
void setStartHook(LispStartHook hook)
Set the hook called when a script starts.
Definition unLisp.h:301
virtual void onEventReceived(unsigned int topic, int msg) override
Event handler for received events.
Definition unLisp.h:384
void serializePrimitives(CBORObject &obj)
Serialize all registered primitive functions to CBOR format.
Definition unLisp.h:349
TaskScheduler::TaskPtr getTask()
Get the task associated with Lisp evaluation.
Definition unLisp.h:217
const Bytes & getLastCode()
Get the last executed Lisp code.
Definition unLisp.h:363
void cleanLastCode()
Clear the stored last executed code.
Definition unLisp.h:371
size_t memoryUsed()
Get the amount of memory used by the Lisp machine.
Definition unLisp.h:247
void runCode(const Bytes &data, LispStartReason reason=LispStartReason::Received)
Run Lisp code in the interpreter.
Definition unLisp.h:259
bool taskIsRunning()
Check if the Lisp evaluation task is currently running.
Definition unLisp.h:239
unLisp * pushPrimitive(Primitive *primitive)
Add a new primitive function to the Lisp environment.
Definition unLisp.h:329
bool isCreated()
Check if the Lisp machine has been created.
Definition unLisp.h:230
auto MakePair(Args &&...args) -> decltype(std::make_pair(std::forward< Args >(args)...))
Creates a pair instance, alias for std::make_pair.
Definition Common.h:337
std::shared_ptr< T > SharedPointer
Type alias for std::shared_ptr with cleaner syntax.
Definition Common.h:313
std::pair< T_First, T_Second > Pair
Type alias for std::pair with cleaner syntax.
Definition Common.h:322
#define UNIOT_LISP_HEAP
Bytes of heap the Lisp interpreter is given at startup.
Definition Common.h:130
#define UNIOT_LISP_MAX_EVAL_STACK
Bytes of C stack an evaluation may spend before a script is abandoned.
Definition Common.h:158
EventListener< unsigned int, int, Bytes > CoreEventListener
Type alias for the common EventListener configuration used in the core system.
Definition EventListener.h:100
#define UNIOT_LOG_INFO(...)
Log an INFO level message Used for general information about system operation. Only compiled if UNIOT...
Definition Logger.h:315
#define UNIOT_LOG_TRACE(...)
Log an TRACE level message Used for general information about system operation. Only compiled if UNIO...
Definition Logger.h:361
#define UNIOT_LOG_WARN(...)
Log an WARN level message Used for warnings about potentially problematic situations....
Definition Logger.h:294
#define UNIOT_LOG_WARN_IF(log_cond, log...)
Conditionally log an WARN level message Used for warnings about potentially problematic situations....
Definition Logger.h:302
#define UNIOT_LOG_DEBUG(...)
Log an DEBUG level message Used for general information about system operation. Only compiled if UNIO...
Definition Logger.h:340
SharedPointer< SchedulerTask > TaskPtr
Shared pointer type for scheduler tasks.
Definition TaskScheduler.h:190
static TaskPtr make(SchedulerTask::SchedulerTaskCallback callback)
Static factory method to create a task with a callback.
Definition TaskScheduler.h:212
LispStartReason
Why a script started running.
Definition LispHelper.h:166
struct Obj * Object
A pointer to a Lisp object structure.
Definition LispHelper.h:63
void * Root
A generic pointer representing the root of a Lisp environment.
Definition LispHelper.h:78
LispStopReason
Why a script stopped running.
Definition LispHelper.h:175
std::function< void(LispStartReason)> LispStartHook
Called after the interpreter is built and before the script is evaluated.
Definition LispHelper.h:186
struct Obj ** VarObject
A pointer to a pointer to a Lisp object structure.
Definition LispHelper.h:71
std::function< void(LispStopReason)> LispStopHook
Called before the interpreter is destroyed.
Definition LispHelper.h:192
@ Received
A script that arrived over MQTT.
Definition LispHelper.h:168
@ Completed
The script ran to the end, or a finite task used up its passes.
Definition LispHelper.h:176
@ Cleared
An empty script arrived, which means "run nothing".
Definition LispHelper.h:178
@ Replaced
A new script arrived and took its place.
Definition LispHelper.h:177
@ Failed
A Lisp error tore the machine down.
Definition LispHelper.h:179
@ OUT_LISP_REQUEST
Topic for Lisp requests to the application services.
Definition LispEvents.h:89
@ OUT_LISP_MSG
Topic for Lisp output messages and notifications.
Definition LispEvents.h:88
@ IN_LISP_EVENT
Topic for incoming events from application to Lisp.
Definition LispEvents.h:91
@ OUT_LISP_EVENT
Topic for outgoing events from Lisp to application.
Definition LispEvents.h:90
@ IN_NEW_EVENT
New incoming event received from application for Lisp processing.
Definition LispEvents.h:107
@ OUT_MSG_ADDED
Standard output message was added to the output buffer.
Definition LispEvents.h:102
@ OUT_MSG_LOG
Log message was generated by the Lisp interpreter.
Definition LispEvents.h:103
@ OUT_NEW_EVENT
New outgoing event generated by Lisp for the application.
Definition LispEvents.h:106
@ OUT_REFRESH_EVENTS
Request to refresh the event queue and process pending events.
Definition LispEvents.h:105
@ OUT_MSG_ERROR
Error message was generated due to Lisp execution failure.
Definition LispEvents.h:104
@ OUT_LISP_LOG
Channel for Lisp log messages and debug information.
Definition LispEvents.h:74
@ IN_EVENT
Channel for incoming events from the application to Lisp.
Definition LispEvents.h:77
@ OUT_LISP
Channel for standard Lisp output (stdout equivalent)
Definition LispEvents.h:73
@ OUT_LISP_ERR
Channel for Lisp error messages and exceptions.
Definition LispEvents.h:75
@ OUT_EVENT
Channel for outgoing events from Lisp to the application.
Definition LispEvents.h:76
Contains type definitions and utilities for interacting with the Lisp interpreter.
Contains descriptions and implementations of primitive functions for hardware interaction.
Definition LispPrimitives.cpp:21
Contains all classes and functions related to the Uniot Core.
IncomingEvent()
Definition unLisp.h:61
int32_t value
Definition unLisp.h:58
IncomingEvent(int32_t v, int8_t err=0)
Definition unLisp.h:62
int8_t errorCode
Definition unLisp.h:59