50#include <libminilisp.h>
52#ifndef UNIOT_LISP_HEAP
54#define UNIOT_LISP_HEAP 20480
56#define UNIOT_LISP_HEAP 8192
58#define UNIOT_LISP_HEAP 4096
77 auto eventID = eventObj.
getString(
"eventID");
80 if (eventID.isEmpty() || valueStr.isEmpty()) {
84 auto value = valueStr.toInt();
85 auto isNumber = value || valueStr ==
"0";
90 bool isNewEvent = !mIncomingEvents.exist(eventID);
92 auto eventQueue = std::make_shared<EventQueue>();
93 mIncomingEvents.put(eventID, eventQueue);
97 auto eventQueue = mIncomingEvents.get(eventID);
98 eventQueue->lastAccessed = millis();
100 size_t queueSizeBefore = eventQueue->queue.size();
102 size_t queueSizeAfter = eventQueue->queue.size();
104 if (queueSizeAfter > queueSizeBefore) {
105 UNIOT_LOG_TRACE(
"pushed event '%s' with value '%d', queue size: %d", eventID.c_str(), value, queueSizeAfter);
107 UNIOT_LOG_WARN(
"event queue for '%s' is full (limit: %d), oldest event was dropped", eventID.c_str(), EVENTS_LIMIT);
112 if (!mIncomingEvents.exist(eventID)) {
116 auto eventQueue = mIncomingEvents.get(eventID);
117 eventQueue->isUsedInScript =
true;
118 eventQueue->lastAccessed = millis();
120 return eventQueue->queue.size() > 0;
126 if (!mIncomingEvents.exist(eventID)) {
127 UNIOT_LOG_WARN(
"attempted to pop non-existent event '%s'", eventID.c_str());
131 auto eventQueue = mIncomingEvents.get(eventID);
132 eventQueue->isUsedInScript =
true;
133 eventQueue->lastAccessed = millis();
135 if (eventQueue->queue.size() == 0) {
136 UNIOT_LOG_WARN(
"attempted to pop from empty event queue '%s'", eventID.c_str());
140 return eventQueue->queue.popLimited(emptyEvent);
144 unsigned long currentTime = millis();
146 size_t totalEvents = 0;
147 size_t unusedEvents = 0;
148 size_t expiredEvents = 0;
149 size_t totalQueuedItems = 0;
153 auto eventQueue = entry.second;
154 totalQueuedItems += eventQueue->queue.size();
155 unsigned long timeSinceLastAccess = currentTime - eventQueue->lastAccessed;
157 if (!eventQueue->isUsedInScript) {
159 if (timeSinceLastAccess > EVENT_TTL_MS) {
161 keysToRemove.
push(entry.first);
162 UNIOT_LOG_DEBUG(
"marking event '%s' for removal (unused, last accessed %lu ms ago)",
163 entry.first.c_str(), timeSinceLastAccess);
169 keysToRemove.
forEach([
this](
const String &key) {
170 mIncomingEvents.remove(key);
173 if (expiredEvents > 0) {
174 UNIOT_LOG_INFO(
"cleaned up %d expired events (total: %d, unused: %d, queued items: %d)",
175 expiredEvents, totalEvents, unusedEvents, totalQueuedItems);
176 }
else if (totalEvents > 0) {
177 UNIOT_LOG_TRACE(
"no events to cleanup (total: %d, unused: %d, queued items: %d)", totalEvents, unusedEvents, totalQueuedItems);
182 mIncomingEvents.clean();
189 unsigned long lastAccessed;
192 EventQueue() : lastAccessed(millis()), isUsedInScript(false) {
193 queue.
limit(EVENTS_LIMIT);
197 Map<String, SharedPointer<EventQueue>> mIncomingEvents;
198 static constexpr size_t EVENTS_LIMIT = 2;
199 static constexpr unsigned long EVENT_TTL_MS = 30000;
228 return mTaskLispEval;
232 return mTaskEventCleanup;
241 return lisp_is_created();
250 return mTaskLispEval->isAttached();
258 return lisp_mem_used();
275 mTaskLispEval->detach();
279 auto code = mLastCode.terminate().c_str();
282 _refreshIncomingEvents();
283 lisp_eval(mLispRoot, mLispEnv, code);
284 if (!mTaskLispEval->isAttached()) {
301 for (
auto i = 0; i < description.argsCount; i++) {
319 .
append(description.returnType)
321 .
append(description.argsCount,
reinterpret_cast<const uint8_t *
>(description.argsTypes));
354 _pushIncomingEvent(data);
372 auto fnPrintOut = [](
const char *msg,
int size) {
381 auto fnPrintLog = [](
const char *msg,
int size) {
390 auto fnPrintErr = [](
const char *msg,
int size) {
395 instance.mTaskLispEval->detach();
396 instance._destroyMachine();
399 lisp_set_cycle_yield(yield);
400 lisp_set_printers(fnPrintOut, fnPrintLog, fnPrintErr);
405 auto root = mLispRoot;
408 DEFINE2(t_obj, t_pass);
409 *t_pass = get_variable(root, env,
"#t_pass");
410 (*t_pass)->cdr->value = t;
411 *t_obj = get_variable(root, env,
"#t_obj")->cdr;
412 safe_eval(root, env, t_obj);
422 mEventManager.cleanupUnusedEvents();
432 void _constructLispEnv() {
433 mLispEnvConstructor[0] = NULL;
434 mLispEnvConstructor[1] = NULL;
435 mLispEnvConstructor[2] = ROOT_END;
436 mLispRoot = mLispEnvConstructor;
437 mLispEnv = (Obj **)(mLispEnvConstructor + 1);
446 void _createMachine() {
449 *mLispEnv = make_env(mLispRoot, &Nil, &Nil);
450 define_constants(mLispRoot, mLispEnv);
451 define_primitives(mLispRoot, mLispEnv);
454 add_constant(mLispRoot, mLispEnv,
"#t_obj", &Nil);
455 add_constant_int(mLispRoot, mLispEnv,
"#t_pass", 0);
456 add_primitive(mLispRoot, mLispEnv,
"task", mPrimitiveTask);
457 add_primitive(mLispRoot, mLispEnv,
"is_event", mPrimitiveIsEventAvailable);
458 add_primitive(mLispRoot, mLispEnv,
"pop_event", mPrimitivePopEvent);
459 add_primitive(mLispRoot, mLispEnv,
"push_event", mPrimitivePushEvent);
463 add_primitive(mLispRoot, mLispEnv, holder.first.c_str(), holder.second);
466 UNIOT_LOG_DEBUG(
"lisp machine created, mem used: %d", lisp_mem_used());
472 void _destroyMachine() {
479 void _refreshIncomingEvents() {
480 mEventManager.clean();
489 void _pushIncomingEvent(
const Bytes &eventData) {
491 mEventManager.pushEvent(eventData);
501 bool _isIncomingEventAvailable(
const String &eventID) {
502 return mEventManager.isEventAvailable(eventID);
512 IncomingEventManager::IncomingEvent _popIncomingEvent(
const String &eventID) {
513 return mEventManager.popEvent(eventID);
524 bool _pushOutgoingEvent(String eventID,
int value) {
526 event.put(
"eventID", eventID.c_str());
527 event.put(
"value", value);
547 .init(root, env, list);
550 auto times = expeditor.getArgInt(0);
551 auto ms = expeditor.getArgInt(1);
552 auto obj = expeditor.getArg(2);
555 *t_obj = get_variable(root, env,
"#t_obj");
558 mTaskLispEval->attach(ms, times);
560 return expeditor.makeBool(
true);
575 .init(root, env, list);
578 auto eventId = expeditor.getArgSymbol(0);
579 auto available = _isIncomingEventAvailable(eventId);
581 return expeditor.makeBool(available);
598 .init(root, env, list);
601 auto eventId = expeditor.getArgSymbol(0);
602 auto event = _popIncomingEvent(eventId);
604 UNIOT_LOG_WARN_IF(event.errorCode,
"error popping event '%s': %d", event.errorCode);
606 return expeditor.makeInt(event.value);
622 .init(root, env, list);
625 auto eventId = expeditor.getArgSymbol(0);
626 auto value = expeditor.getArgInt(1);
628 auto sent = _pushOutgoingEvent(eventId, value);
630 return expeditor.makeBool(sent);
637 ClearQueue<Pair<String, Primitive *>> mUserPrimitives;
647 void *mLispEnvConstructor[3];
650 IncomingEventManager mEventManager;
Lisp interpreter event definitions for the Uniot event system.
size_t size() const
Gets the size of the byte array.
Definition Bytes.h:303
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:593
Array appendArray()
Append a new array as an element.
Definition CBORObject.h:635
Definition CBORObject.h:40
Array putArray(int key)
Create or get an array at a specific integer key.
Definition CBORObject.h:121
String getString(int key) const
Get a string value at a specific integer key.
Definition CBORObject.h:419
String getValueAsString(int key) const
Get a value as a string at a specific integer key.
Definition CBORObject.h:440
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:123
void clean()
Definition unLisp.h:181
void cleanupUnusedEvents()
Definition unLisp.h:143
bool isEventAvailable(const String &eventID)
Definition unLisp.h:111
void pushEvent(const Bytes &eventData)
Definition unLisp.h:75
@ BoolInt
Definition LispHelper.h:98
@ Symbol
Definition LispHelper.h:99
@ Cell
Definition LispHelper.h:100
@ Bool
Definition LispHelper.h:97
@ Int
Definition LispHelper.h:96
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:231
void runCode(const Bytes &data)
Run Lisp code in the interpreter.
Definition unLisp.h:269
virtual void onEventReceived(unsigned int topic, int msg) override
Event handler for received events.
Definition unLisp.h:350
void serializePrimitives(CBORObject &obj)
Serialize all registered primitive functions to CBOR format.
Definition unLisp.h:315
TaskScheduler::TaskPtr getTask()
Get the task associated with Lisp evaluation.
Definition unLisp.h:227
const Bytes & getLastCode()
Get the last executed Lisp code.
Definition unLisp.h:329
void cleanLastCode()
Clear the stored last executed code.
Definition unLisp.h:337
size_t memoryUsed()
Get the amount of memory used by the Lisp machine.
Definition unLisp.h:257
bool taskIsRunning()
Check if the Lisp evaluation task is currently running.
Definition unLisp.h:249
unLisp * pushPrimitive(Primitive *primitive)
Add a new primitive function to the Lisp environment.
Definition unLisp.h:295
bool isCreated()
Check if the Lisp machine has been created.
Definition unLisp.h:240
auto MakePair(Args &&...args) -> decltype(std::make_pair(std::forward< Args >(args)...))
Creates a pair instance, alias for std::make_pair.
Definition Common.h:184
std::shared_ptr< T > SharedPointer
Type alias for std::shared_ptr with cleaner syntax.
Definition Common.h:160
std::pair< T_First, T_Second > Pair
Type alias for std::pair with cleaner syntax.
Definition Common.h:169
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:268
#define UNIOT_LOG_TRACE(...)
Log an TRACE level message Used for general information about system operation. Only compiled if UNIO...
Definition Logger.h:314
#define UNIOT_LOG_WARN(...)
Log an WARN level message Used for warnings about potentially problematic situations....
Definition Logger.h:247
#define UNIOT_LOG_WARN_IF(log_cond, log...)
Conditionally log an WARN level message Used for warnings about potentially problematic situations....
Definition Logger.h:255
#define UNIOT_LOG_DEBUG(...)
Log an DEBUG level message Used for general information about system operation. Only compiled if UNIO...
Definition Logger.h:293
SharedPointer< SchedulerTask > TaskPtr
Shared pointer type for scheduler tasks.
Definition TaskScheduler.h:171
static TaskPtr make(SchedulerTask::SchedulerTaskCallback callback)
Static factory method to create a task with a callback.
Definition TaskScheduler.h:193
struct Obj * Object
A pointer to a Lisp object structure.
Definition LispHelper.h:61
void * Root
A generic pointer representing the root of a Lisp environment.
Definition LispHelper.h:76
struct Obj ** VarObject
A pointer to a pointer to a Lisp object structure.
Definition LispHelper.h:69
@ 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 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:71
int32_t value
Definition unLisp.h:68
IncomingEvent(int32_t v, int8_t err=0)
Definition unLisp.h:72
int8_t errorCode
Definition unLisp.h:69
#define UNIOT_LISP_HEAP
Definition unLisp.h:54