diff --git a/.gitignore b/.gitignore index a84d202..5dd363b 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,5 @@ */xcshareddata/* *.xcuserdatad .DS_Store -work/* \ No newline at end of file +work/* +.vscode/*ÅÅ \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index ade7107..4e10800 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -16,6 +16,56 @@ "iterator": "cpp", "string_view": "cpp", "unordered_map": "cpp", - "vector": "cpp" - } + "vector": "cpp", + "iosfwd": "cpp", + "map": "cpp", + "__bits": "cpp", + "__config": "cpp", + "__debug": "cpp", + "__errc": "cpp", + "__functional_base": "cpp", + "__locale": "cpp", + "__node_handle": "cpp", + "__nullptr": "cpp", + "__string": "cpp", + "__threading_support": "cpp", + "__tree": "cpp", + "__tuple": "cpp", + "algorithm": "cpp", + "bit": "cpp", + "bitset": "cpp", + "cctype": "cpp", + "chrono": "cpp", + "clocale": "cpp", + "cmath": "cpp", + "complex": "cpp", + "cstdarg": "cpp", + "cstddef": "cpp", + "cstdint": "cpp", + "cstdio": "cpp", + "cstdlib": "cpp", + "cstring": "cpp", + "ctime": "cpp", + "cwchar": "cpp", + "cwctype": "cpp", + "exception": "cpp", + "functional": "cpp", + "ios": "cpp", + "iostream": "cpp", + "istream": "cpp", + "limits": "cpp", + "locale": "cpp", + "new": "cpp", + "optional": "cpp", + "ostream": "cpp", + "ratio": "cpp", + "sstream": "cpp", + "streambuf": "cpp", + "system_error": "cpp", + "tuple": "cpp", + "type_traits": "cpp", + "typeinfo": "cpp", + "utility": "cpp" + }, + "C_Cpp.errorSquiggles": "Disabled" } \ No newline at end of file diff --git a/README.md b/README.md index f978a05..674aa15 100755 --- a/README.md +++ b/README.md @@ -10,6 +10,54 @@ What is AtomicX? AtomicX is a general purpose **cooperative** thread lib for emb ## Implementations from Work on progress +## Version 1.3.0 + +* NOW, I am thrilled to announce that AtomicX have `send` and `receive` functions that enables data transferring between two or more threads, allowing real client / server application strategy inside embedded and non-embedded application, this way, small MCUs can be used to easily transport stack data (which is protected in this thread system) to other thread. + +* Ported and enhanced the DotMatrix project, it implements a full Dot Led Matrix scrolling text system with + - Serial terminal + - Telnet Terminal + - UDP Trap (You can send a UDP message to IP/2221 and it will display, I deal for trapping messages) + - An amazing general log API based on `iostream`, capable of adding "Specialized Loggers", and add using `logger.AddLogger`: + ```cpp + logger << LOG::ERROR << "Failed to start WiFi." << std::endl; + ``` + - based on ESP8266 and 8 or 4 Dot Matrix leds array + - Just connect the Led Matrix (Dot Matrix Modul 8x8 Display Matrix Max7219 Led Lcd with 8 or 4 8x8 display) + ``` + Designed for NodeMCU ESP8266 + ################# DISPLAY CONNECTIONS ################ + LED Matrix Pin -> ESP8266 Pin + Vcc -> 3v (3V on NodeMCU 3V3 on WEMOS) + Gnd -> Gnd (G on NodeMCU) + DIN -> D7 (Same Pin for WEMOS) + CS -> D4 (Same Pin for WEMOS) + CLK -> D5 (Same Pin for WEMOS) + ``` + +* Added WaiTAny, that extends the Wait/Notify functionality, since it will also receive, and return by reference, ANY TAG. giving the developer ability to easily create a full Client / Service infra structure. + +* Dropping BROKER functionality, and welcoming **Broadcasting** functionality, it will enable a thread to receive all broadcasts asynchronously sent by other threads, only by enabling it and implementing the handler : + ```cpp + virtual void BroadcastHandler (const size_t& messageReference, const Message& message) + ``` + and enabling it using the code: + + ```cpp + SetReceiveBroadcast (true); + ``` + The handler will deliver tree parameters: + - `size_t MessageReference` that works like a reference of what is the message about; + - `size_t Message.message` which is the message payload (could be even an pointer); + - `size_t Message.tag` that can be used as a meaning for the message: + + Example: + - `messageReference=BROADCAST_IRCAMERA` + - `Message.message=CAMERA_DONE` + - `Message.tag=CAMERA_READINGS` + + On this simple example using 'mnemonics', that could have been enum class or directives, a single message was able to inform asynchronously that a IR CAMERA just read something, but could have informed ERROR or even that it was READING...., the approach allows a powerful controller, since you can apply layers on your code making processing really fast and precise and less messages will travel across the systems. + * `mutex` and `smartMutex` now have timeout, by using `lock()` and `sharedLock()`, if no timeout is given: `lock()` or `sharedLock()` wait indefinitely (fully back compatible with existing code) ## Version 1.2.1 @@ -88,9 +136,11 @@ What is AtomicX? AtomicX is a general purpose **cooperative** thread lib for emb * **DOES NOT DISPLACE STACK, IT WILL STILL AVAILABLE FOR PROCESSING**, the *Stack Page* will only hold a backup of the most necessary information needed, allowing stacks in few bites most if the time. This implementation if highly suitable for Microcontrollers like ATINY85, for example, that only has 512 bites, and you can have 5 or more threads doing things for you, only backup the most important context information. - * *IMPORTANT*: DO NOT USE CONTEXT MEMORY POINTER to exchange information to other threads, wait/notify and etc. All threads will use the *dafault stack memory* to execute, instead use Global variables, allocated memory or atomicx_smart_ptr objects. -* Since it implements Cooperative thread every execution will atomic between *atomicx* thrteads. +* **STACK MEMORY ARE PROTECTED** by nature and confined to the context execution only, if you want to exchange non-data global use `atomicx::send` and `atomicx::receive`. + * *IMPORTANT*: DO NOT USE CONTEXT(stack) MEMORY POINTER to exchange information to other threads, extractables like wait/notify, lock uses global memory instead. All threads will use the *default stack memory* which is protected during execution, instead use Global variables, allocated memory or atomicx_smart_ptr objects. Alternatively use `atomicx::send` and `atomicx::receive` to transport stack, global or heap memory data. + +* Since it implements Cooperative thread every execution will atomic between *atomicx* thrteads. * AtomicX **DOES NOT DISPLACE STACK**, yes, it will use a novel technique that allow you to use full stack memory freely, and once done, just call `Yield()` to switch the context. 1. Allow you to use all your stack during thread execution and only switch once back to an appropriate place @@ -124,7 +174,7 @@ What is AtomicX? AtomicX is a general purpose **cooperative** thread lib for emb * Thread can wait for an event to happen. * On event notification a `atomix::message` can be sent/received -* A message broker based on observer pattern +* (DEPRECATED) A message broker based on observer pattern (NOW DROPPED and REPLACED BY BROADCAST) * A thread can use `WaitBroker Message` to wait for any specifc topic asynchronously. * Instead of having a `Subcrib` call, the developer will provide a `IsSubscribed` method that the kernel will use to determine if the object/thread is subscribed to a given topic. * Broker uses `atomicx::message` to transport information. For inter process Object transport, please use atomicx::queue. diff --git a/atomicx/atomicx.cpp b/atomicx/atomicx.cpp index 7e24d74..551765d 100644 --- a/atomicx/atomicx.cpp +++ b/atomicx/atomicx.cpp @@ -16,13 +16,6 @@ #include -extern "C" -{ - #pragma weak yield - void yield(void) - {} -} - namespace thread { // Static initializations @@ -30,6 +23,7 @@ namespace thread static atomicx* ms_paLast=nullptr; static jmp_buf ms_joinContext{}; static atomicx* ms_pCurrent=nullptr; + static bool ms_running=false; atomicx::semaphore::semaphore(size_t nMaxShared) : m_maxShared(nMaxShared) { @@ -46,7 +40,7 @@ namespace thread while (m_counter >= m_maxShared) { - if (timeout.IsTimedout () || GetCurrent()->Wait (*this, 1, timeout.GetRemaining()) == false) + if (timeout.IsTimedout () || GetCurrent() == nullptr || GetCurrent()->Wait (*this, 1, timeout.GetRemaining()) == false) { return false; } @@ -59,7 +53,7 @@ namespace thread void atomicx::semaphore::release() { - if (m_counter) + if (m_counter && GetCurrent() != nullptr) { m_counter --; @@ -79,7 +73,7 @@ namespace thread size_t atomicx::semaphore::GetWaitCount () { - return GetCurrent()->HasWaitings (*this, 1); + return GetCurrent() != nullptr ? GetCurrent()->HasWaitings (*this, 1) : 0; } // Smart Semaphore, manages the semaphore com comply with RII @@ -133,6 +127,11 @@ namespace thread return bAcquired; } + atomicx::Timeout::Timeout () : m_timeoutValue (0) + { + Set (0); + } + atomicx::Timeout::Timeout (atomicx_time nTimeoutValue) : m_timeoutValue (0) { Set (nTimeoutValue); @@ -160,28 +159,33 @@ namespace thread return startTime - GetRemaining (); } - atomicx::aiterator::aiterator(atomicx* ptr) : m_ptr(ptr) - {} + // atomicx::aiterator::aiterator(atomicx* ptr) : m_ptr(ptr) + // {} - atomicx& atomicx::aiterator::operator*() const - { - return *m_ptr; - } + // atomicx& atomicx::aiterator::operator*() const + // { + // return *m_ptr; + // } - atomicx* atomicx::aiterator::operator->() - { - return m_ptr; - } + // atomicx* atomicx::aiterator::operator->() + // { + // return m_ptr; + // } + + // atomicx::aiterator& atomicx::aiterator::operator++() + // { + // if (m_ptr != nullptr) m_ptr = m_ptr->m_paNext; + // return *this; + // } + + iterator atomicx::begin() { return iterator(ms_paFirst); } + iterator atomicx::end() { return iterator(nullptr); } - atomicx::aiterator& atomicx::aiterator::operator++() + atomicx* atomicx::operator++ (void) { - if (m_ptr != nullptr) m_ptr = m_ptr->m_paNext; - return *this; + return m_paNext; } - atomicx::aiterator atomicx::begin() { return aiterator(ms_paFirst); } - atomicx::aiterator atomicx::end() { return aiterator(nullptr); } - atomicx* atomicx::GetCurrent() { return ms_pCurrent; @@ -202,12 +206,45 @@ namespace thread } } + void atomicx::RemoveThisThread() + { + if (m_paNext == nullptr && m_paPrev == nullptr) + { + ms_paFirst = nullptr; + m_paPrev = nullptr; + ms_pCurrent = nullptr; + } + else if (m_paPrev == nullptr) + { + m_paNext->m_paPrev = nullptr; + ms_paFirst = m_paNext; + ms_pCurrent = ms_paFirst; + } + else if (m_paNext == nullptr) + { + m_paPrev->m_paNext = nullptr; + ms_paLast = m_paPrev; + ms_pCurrent = ms_paFirst; + } + else + { + m_paPrev->m_paNext = m_paNext; + m_paNext->m_paPrev = m_paPrev; + ms_pCurrent = m_paNext->m_paPrev; + } + } + bool atomicx::SelectNextThread() { atomicx* pItem = ms_paFirst; - do + if (pItem != nullptr) do { + if (pItem->m_aStatus == aTypes::stop) + { + continue; + } + if (ms_pCurrent->m_nTargetTime == 0 && pItem->m_nTargetTime > 0) { ms_pCurrent = pItem; @@ -263,6 +300,11 @@ namespace thread } } + if (ms_pCurrent->m_flags.dynamicNice == true) + { + ms_pCurrent->m_nice = ((ms_pCurrent->m_LastUserExecTime) + ms_pCurrent->m_nice) / 2; + } + return true; } @@ -270,11 +312,10 @@ namespace thread { if (ms_paFirst != nullptr) { - static bool nRunning = true; - + ms_running = true; ms_pCurrent = ms_paFirst; - while (nRunning && SelectNextThread ()) + while (ms_running && SelectNextThread ()) { if (setjmp(ms_joinContext) == 0) { @@ -302,33 +343,35 @@ namespace thread } } + ms_running = false; + return false; } bool atomicx::Yield(atomicx_time nSleep) { - m_LastUserExecTime = GetCurrentTick () - m_lastResumeUserTime; + ms_pCurrent->m_LastUserExecTime = GetCurrentTick () - ms_pCurrent->m_lastResumeUserTime; - if (m_aStatus == aTypes::running) + if (ms_pCurrent->m_aStatus == aTypes::running) { - m_aStatus = aTypes::sleep; - m_aSubStatus = aSubTypes::ok; - m_nTargetTime=Atomicx_GetTick() + (nSleep == ATOMICX_TIME_MAX ? m_nice : nSleep); + ms_pCurrent->m_aStatus = aTypes::sleep; + ms_pCurrent->m_aSubStatus = aSubTypes::ok; + ms_pCurrent->m_nTargetTime=Atomicx_GetTick() + (nSleep == ATOMICX_TIME_MAX ? ms_pCurrent->m_nice : nSleep); } - else if (m_aStatus == aTypes::wait) + else if (ms_pCurrent->m_aStatus == aTypes::wait) { - m_nTargetTime=nSleep > 0 ? nSleep + Atomicx_GetTick() : 0; + ms_pCurrent->m_nTargetTime=nSleep > 0 ? nSleep + Atomicx_GetTick() : 0; } else { - m_nTargetTime = (atomicx_time)~0; + ms_pCurrent->m_nTargetTime = (atomicx_time)~0; } volatile uint8_t nStackEnd=0; - m_pStaskEnd = &nStackEnd; - m_stacUsedkSize = (size_t) (m_pStaskStart - m_pStaskEnd); + ms_pCurrent->m_pStaskEnd = &nStackEnd; + ms_pCurrent->m_stacUsedkSize = static_cast(ms_pCurrent->m_pStaskStart - ms_pCurrent->m_pStaskEnd + 1); - if (m_stacUsedkSize > m_stackSize || m_stack == nullptr) + if (ms_pCurrent->m_stacUsedkSize > ms_pCurrent->m_stackSize || ms_pCurrent->m_stack == nullptr) { /* * Controll the auto-stack memory @@ -338,65 +381,64 @@ namespace thread * to control errors */ - if (m_flags.autoStack == true) + if (ms_pCurrent->m_flags.autoStack == true) { - if (m_stack != nullptr) + if (ms_pCurrent->m_stack != nullptr) { - free ((void*) m_stack); + free ((void*) ms_pCurrent->m_stack); } - if (m_stacUsedkSize > m_stackSize) + if (ms_pCurrent->m_stacUsedkSize > ms_pCurrent->m_stackSize) { - m_stackSize = m_stacUsedkSize + m_stackIncreasePace; + ms_pCurrent->m_stackSize = ms_pCurrent->m_stacUsedkSize + ms_pCurrent->m_stackIncreasePace; } - if ((m_stack = (uint8_t*) malloc (m_stackSize)) == nullptr) + if ((ms_pCurrent->m_stack = (volatile uint8_t*) malloc (ms_pCurrent->m_stackSize)) == nullptr) { - m_aStatus = aTypes::stackOverflow; + ms_pCurrent->m_aStatus = aTypes::stackOverflow; } } else { - m_aStatus = aTypes::stackOverflow; + ms_pCurrent->m_aStatus = aTypes::stackOverflow; } - if (m_aStatus == aTypes::stackOverflow) + if (ms_pCurrent->m_aStatus == aTypes::stackOverflow) { - (void) StackOverflowHandler(); + (void) ms_pCurrent->StackOverflowHandler(); abort(); } } - if (m_aStatus != aTypes::stackOverflow && memcpy((void*)m_stack, (const void*) m_pStaskEnd, m_stacUsedkSize) != (void*) m_stack) + if (ms_pCurrent->m_aStatus != aTypes::stackOverflow && memcpy((void*)ms_pCurrent->m_stack, (const void*) ms_pCurrent->m_pStaskEnd, ms_pCurrent->m_stacUsedkSize) != (void*) ms_pCurrent->m_stack) { return false; } - if (setjmp(m_context) == 0) + if (setjmp(ms_pCurrent->m_context) == 0) { longjmp(ms_joinContext, 1); } else { - ms_pCurrent->m_stacUsedkSize = (size_t) (ms_pCurrent->m_pStaskStart - &nStackEnd); if (memcpy((void*) ms_pCurrent->m_pStaskEnd, (const void*) ms_pCurrent->m_stack, ms_pCurrent->m_stacUsedkSize) != (void*) ms_pCurrent->m_pStaskEnd) { return false; } - } - - ms_pCurrent->m_lastResumeUserTime = GetCurrentTick (); - ms_pCurrent->m_aStatus = aTypes::running; + ms_pCurrent->m_aStatus = aTypes::running; - if (ms_pCurrent->m_flags.dynamicNice == true) - { - ms_pCurrent->m_nice = ((ms_pCurrent->m_LastUserExecTime) + ms_pCurrent->m_nice) / 2; + ms_pCurrent->m_lastResumeUserTime = Atomicx_GetTick (); } return true; } + bool atomicx::IsKernelRunning() + { + return ms_running; + } + void atomicx::YieldNow () { m_aStatus = aTypes::now; @@ -419,59 +461,65 @@ namespace thread return m_stacUsedkSize; } - void atomicx::RemoveThisThread() - { - if (m_paNext == nullptr && m_paPrev == nullptr) - { - ms_paFirst = nullptr; - m_paPrev = nullptr; - ms_pCurrent = nullptr; - } - else if (m_paPrev == nullptr) - { - m_paNext->m_paPrev = nullptr; - ms_paFirst = m_paNext; - ms_pCurrent = ms_paFirst; - } - else if (m_paNext == nullptr) - { - m_paPrev->m_paNext = nullptr; - ms_paLast = m_paPrev; - ms_pCurrent = ms_paFirst; - } - else - { - m_paPrev->m_paNext = m_paNext; - m_paNext->m_paPrev = m_paPrev; - ms_pCurrent = m_paNext->m_paPrev; - } - } - - void atomicx::SetDefaultParameters () + void atomicx::SetDefaultInitializations () { m_flags.autoStack = false; m_flags.dynamicNice = false; + m_flags.broadcast = false; + m_flags.attached = true; + + AddThisThread(); } atomicx::atomicx(size_t nStackSize, int nStackIncreasePace) : m_context{}, m_stackSize(nStackSize), m_stackIncreasePace(nStackIncreasePace), m_stack(nullptr) { - SetDefaultParameters (); + SetDefaultInitializations (); m_flags.autoStack = true; - AddThisThread(); } - atomicx::~atomicx() + void atomicx::DestroyThread() { - RemoveThisThread(); - - if (m_flags.autoStack == true && m_stack != nullptr) + if (m_flags.attached) { - free((void*)m_stack); + RemoveThisThread(); + + if (m_flags.autoStack == true && m_stack != nullptr) + { + free((void*)m_stack); + } + + m_flags.attached = false; } } + void atomicx::finish() noexcept + { + return; + } + + void atomicx::Detach() + { + this->finish (); + DestroyThread (); + + Yield (); + } + + void atomicx::Restart() + { + this->finish (); + m_aStatus = aTypes::start; + + Yield (); + } + + atomicx::~atomicx() + { + DestroyThread (); + } + const char* atomicx::GetName(void) { return "thread"; @@ -675,202 +723,120 @@ namespace thread return (nCRC); } - uint32_t atomicx::GetTopicID (const char* pszTopic, size_t nKeyLenght) + atomicx_time atomicx::GetCurrentTick(void) { - return ((uint32_t) ((crc16 ((const uint8_t*)pszTopic, nKeyLenght, 0) << 15) | crc16 ((const uint8_t*)pszTopic, nKeyLenght, 0x8408))); + return Atomicx_GetTick (); } - bool atomicx::HasSubscriptions (const char* pszKey, size_t nKeyLenght) + bool atomicx::IsStackSelfManaged(void) { - if (pszKey != nullptr && nKeyLenght > 0) - { - uint32_t nKeyID = GetTopicID(pszKey, nKeyLenght); - - for (auto& thr : *this) - { - if (nKeyID == thr.m_TopicId || IsSubscribed (pszKey, nKeyLenght)) - { - return true; - } - } - } - - return false; + return m_flags.autoStack; } - bool atomicx::HasSubscriptions (uint32_t nKeyID) + atomicx_time atomicx::GetLastUserExecTime() { - for (auto& thr : *this) - { - if (nKeyID == thr.m_TopicId) - { - return true; - } - } - - return false; + return m_LastUserExecTime; } - bool atomicx::WaitBrokerMessage (const char* pszKey, size_t nKeyLenght, Message& message) + void atomicx::SetStackIncreasePace(size_t nIncreasePace) { - if (pszKey != nullptr && nKeyLenght > 0) - { - m_aStatus = aTypes::subscription; - - m_TopicId = GetTopicID(pszKey, nKeyLenght); - m_nTargetTime = Atomicx_GetTick(); - - m_lockMessage = {0,0}; - - Yield(); - - message.message = m_lockMessage.message; - message.tag = m_lockMessage.tag; - - return true; - } - - return false; + m_stackIncreasePace = nIncreasePace; } - bool atomicx::WaitBrokerMessage (const char* pszKey, size_t nKeyLenght) + size_t atomicx::GetStackIncreasePace(void) { - if (pszKey != nullptr && nKeyLenght > 0) - { - m_aStatus = aTypes::subscription; - - m_TopicId = GetTopicID(pszKey, nKeyLenght); - m_nTargetTime = Atomicx_GetTick(); - - Yield(); - - m_lockMessage = {0,0}; - - return true; - } + return m_stackIncreasePace; + } - return false; + void atomicx::SetDynamicNice(bool status) + { + m_flags.dynamicNice = status; } - bool atomicx::SafePublish (const char* pszKey, size_t nKeyLenght, const Message message) + bool atomicx::IsDynamicNiceOn() { - size_t nCounter=0; + return m_flags.dynamicNice; + } - if (pszKey != nullptr && nKeyLenght > 0) + atomicx* atomicx::GetThread(size_t threadId) + { + for (auto& th : *(thread::atomicx::GetCurrent())) { - uint32_t nTagId = GetTopicID(pszKey, nKeyLenght); - - for (auto& thr : *this) + if (reinterpret_cast(&th) == threadId) { - if (nTagId == thr.m_TopicId) - { - nCounter++; - - thr.m_aStatus = aTypes::now; - thr.m_TopicId = 0; - thr.m_nTargetTime = Atomicx_GetTick(); - thr.m_lockMessage.message = message.message; - thr.m_lockMessage.tag = message.tag; - } - - if (thr.IsSubscribed(pszKey, nKeyLenght)) - { - nCounter++; - - thr.BrokerHandler(pszKey, nKeyLenght, thr.m_lockMessage); - } + return &th; } } - return nCounter ? true : false; + return nullptr; } - bool atomicx::Publish (const char* pszKey, size_t nKeyLenght, const Message message) + atomicx& atomicx::GetThread (void) { - bool nReturn = SafePublish(pszKey, nKeyLenght, message); - - if (nReturn) Yield(); - - return nReturn; + return *this; } - bool atomicx::SafePublish (const char* pszKey, size_t nKeyLenght) + size_t atomicx::GetThreadCount() { size_t nCounter=0; - if (pszKey != nullptr && nKeyLenght > 0) + for (auto& th : *(thread::atomicx::GetCurrent())) { - uint32_t nTagId = GetTopicID(pszKey, nKeyLenght); - - for (auto& thr : *this) - { - if (nTagId == thr.m_TopicId) - { - nCounter++; - - thr.m_aStatus = aTypes::now; - thr.m_TopicId = 0; - thr.m_nTargetTime = Atomicx_GetTick(); - thr.m_lockMessage = {0,0}; - } - - if (thr.IsSubscribed(pszKey, nKeyLenght)) - { - nCounter++; - - thr.m_lockMessage = {0,0}; - thr.BrokerHandler(pszKey, nKeyLenght, thr.m_lockMessage); - } - - } - - if (nCounter) Yield(); + (void) th; + nCounter++; } - return nCounter ? true : false; + return nCounter; } - bool atomicx::Publish (const char* pszKey, size_t nKeyLenght) + void atomicx::SetReceiveBroadcast (bool bBroadcastStatus) { - bool nReturn = SafePublish(pszKey, nKeyLenght); - - if (nReturn) Yield(); - - return nReturn; + m_flags.broadcast = bBroadcastStatus; } - atomicx_time atomicx::GetCurrentTick(void) + size_t atomicx::BroadcastMessage (const size_t messageReference, const Message message) { - return Atomicx_GetTick (); - } + size_t nReceived = 0; - bool atomicx::IsStackSelfManaged(void) - { - return m_flags.autoStack; - } + for (auto& thr : *this) + { + if (thr.m_flags.broadcast == true) + { + BroadcastHandler (messageReference, message); + nReceived++; + } + } - atomicx_time atomicx::GetLastUserExecTime() - { - return m_LastUserExecTime; + return nReceived; } - void atomicx::SetStackIncreasePace(size_t nIncreasePace) + void atomicx::BroadcastHandler (const size_t& messageReference, const Message& message) { - m_stackIncreasePace = nIncreasePace; + (void) messageReference; // to avoid unused variable + (void) message; // to avoid unused variable } - size_t atomicx::GetStackIncreasePace(void) + void atomicx::Stop () { - return m_stackIncreasePace; + m_aStatus = aTypes::stop; + m_aSubStatus = aSubTypes::none; + m_nTargetTime = 0; + + Yield(); } - void atomicx::SetDynamicNice(bool status) + void atomicx::Resume () { - m_flags.dynamicNice = status; + m_aStatus = aTypes::now; + m_aSubStatus = aSubTypes::ok; + m_nTargetTime = 0; + + Yield(); } - bool atomicx::IsDynamicNiceOn() + bool atomicx::IsStopped () { - return m_flags.dynamicNice; + return m_aStatus == aTypes::stop; } + } diff --git a/atomicx/atomicx.hpp b/atomicx/atomicx.hpp index 4a41c2e..fccb7ca 100644 --- a/atomicx/atomicx.hpp +++ b/atomicx/atomicx.hpp @@ -8,21 +8,31 @@ #ifndef atomic_hpp #define atomic_hpp +#include +#include #include #include #include +#include /* Official version */ -#define ATOMICX_VERSION "1.2.1" +#define ATOMICX_VERSION "1.3.0" #define ATOMIC_VERSION_LABEL "AtomicX v" ATOMICX_VERSION " built at " __TIMESTAMP__ using atomicx_time = uint32_t; #define ATOMICX_TIME_MAX ((atomicx_time) ~0) -extern "C" +/** + * @brief Calculate the stack size based on MCU channel size 8, 16, 32, 64 bits multiples + * + * @param units Number of units + * + * @return size_t return units * (MCU channel size) + */ +constexpr size_t CALCSTACKSIZE(size_t units) { - extern void yield(void); + return units * sizeof (size_t); } /** @@ -44,6 +54,192 @@ extern void Atomicx_SleepTick(atomicx_time nSleep); namespace thread { + /** + * @brief General purpose iterator facility + * + * @tparam T + */ + template class iterator + { + public: + iterator() = delete; + + /** + * @brief Type based constructor + * + * @param ptr Type pointer to iterate + */ + iterator(T* ptr) : m_ptr(ptr) + {} + + /* + * Access operator + */ + T& operator*() + { + return *m_ptr; + } + T* operator->() + { + return &m_ptr; + } + + /* + * Movement operator + */ + iterator& operator++() + { + if (m_ptr != nullptr) + { + m_ptr = m_ptr->operator++ (); + } + + return *this; + } + + /* + * Binary operators + */ + friend bool operator== (const iterator& a, const iterator& b){ return a.m_ptr == b.m_ptr;}; + friend bool operator!= (const iterator& a, const iterator& b){ return a.m_ptr != b.m_ptr;}; + + private: + T* m_ptr = nullptr; + }; + + /** + * @brief Link Item to turn any object attachable to LinkList + * + * @tparam T Type to be managed + */ + templateclass LinkItem + { + public: + + /** + * @brief Easy way to have access to the item object + * + * @return T& Referece to the item object + */ + T& operator()() + { + return (T&)*this; + } + + T* operator++ (void) + { + return next; + } + private: + template friend class LinkList; + template friend class iterator; + + LinkItem* next = nullptr; + LinkItem* prev = nullptr; + }; + + /** + * @brief List of attachable object by extending LinkItem + * + * @tparam T Type of the object + * + * @note Note that this does not create memory, it uses next/prev added to the object + * by extending the object to LinkItem and manages it life cycle, is automatically + * inserted and deleted on instantiation and object destruction. + */ + templateclass LinkList + { + public: + + /** + * @brief Attach a LinkItem enabled object to the list + * + * @param listItem The object + * + * @return true if it was successfull attached + */ + bool AttachBack(LinkItem& listItem) + { + if (first == nullptr) + { + first = &listItem; + last = first; + } + else + { + listItem.prev = last; + last->next = &listItem; + last = &listItem; + } + + return true; + } + + /** + * @brief Detach a specifc LinkItem from the managed list + * + * @param listItem LinkIntem enabled object to be deleted + * + * @return true if successfull detached + */ + bool Detach(LinkItem& listItem) + { + if (listItem.next == nullptr && listItem.prev == nullptr) + { + first = nullptr; + listItem.prev = nullptr; + current = nullptr; + } + else if (listItem.prev == nullptr) + { + listItem.next->prev = nullptr; + first = listItem.next; + current = first; + } + else if (listItem.next == nullptr) + { + listItem.prev->next = nullptr; + last = listItem.prev; + current = last; + } + else + { + listItem.prev->next = listItem.next; + listItem.next->prev = listItem.prev; + current = listItem.next->prev; + } + + return true; + } + + /** + * @brief thread::iterator helper for signaling beginning + * + * @return iterator> the first item + */ + iterator> begin() + { + return iterator>(first); + } + + /** + * @brief thread::iterator helper for signaling ending + * + * @return iterator> the final of the list + */ + iterator> end() + { + return iterator>(nullptr); + } + + private: + template friend class iterator; + + LinkItem* current=nullptr; + LinkItem* first=nullptr; + LinkItem* last=nullptr; + }; + class atomicx { public: @@ -68,6 +264,7 @@ namespace thread enum class aSubTypes : uint8_t { + none=0, error=10, ok, look, @@ -81,6 +278,12 @@ namespace thread all = 1 }; + struct Message + { + size_t message; + size_t tag; + }; + /** * @brief Timeout Check object */ @@ -88,7 +291,15 @@ namespace thread class Timeout { public: - Timeout () = delete; + + /** + * @brief Default construct a new Timeout object + * + * @note To decrease the amount of memory, Timeout does not save + * the start time. + * Special use case: if nTimeoutValue == 0, IsTimedout is always false. + */ + Timeout (); /** * @brief Construct a new Timeout object @@ -137,57 +348,20 @@ namespace thread private: atomicx_time m_timeoutValue = 0; }; - /** - * ------------------------------ - * ITERATOR FOR THREAD LISTING - * ------------------------------ - */ - class aiterator - { - public: - aiterator() = delete; - - /** - * @brief atomicx based constructor - * - * @param ptr atomicx pointer to iterate - */ - aiterator(atomicx* ptr); - - /* - * Access operator - */ - atomicx& operator*() const; - atomicx* operator->(); - - /* - * Movement operator - */ - aiterator& operator++(); - - /* - * Binary operators - */ - friend bool operator== (const aiterator& a, const aiterator& b){ return a.m_ptr == b.m_ptr;}; - friend bool operator!= (const aiterator& a, const aiterator& b){ return a.m_ptr != b.m_ptr;}; - - private: - atomicx* m_ptr; - }; /** * @brief Get the beggining of the list * - * @return aiterator + * @return iterator */ - aiterator begin(void); + iterator begin(void); /** * @brief Get the end of the list * - * @return aiterator + * @return iterator */ - aiterator end(void); + iterator end(void); /** * ------------------------------ @@ -816,6 +990,30 @@ namespace thread */ static atomicx* GetCurrent(); + /** + * @brief Construct a new atomicx object and set initial auto stack and increase pace + * + * @param nStackSize Initial Size of the stack + * @param nStackIncreasePace defalt=1, The increase pace on each resize + */ + atomicx(size_t nStackSize=0, int nStackIncreasePace=1); + + /** + * @brief Get the Thread pointer from a object + * + * @param threadId The thread ID reference + * + * @return atomicx* nullprt if not found otherwise the pointer to the thread + */ + static atomicx* GetThread(size_t threadId); + + /** + * @brief Get the current Thread reference object + * + * @return atomicx& Current thread reference object + */ + atomicx& GetThread (void); + /** * @brief Once it is call the process blocks execution and start all threads * @@ -823,6 +1021,13 @@ namespace thread */ static bool Start(void); + /** + * @brief Report if the kernel is already running + * + * @return true is thread::atomicx::start is running, otherwise false + */ + static bool IsKernelRunning(); + /** * @brief Get the current thread ID * @@ -830,6 +1035,7 @@ namespace thread */ size_t GetID(void); + /** * @brief Get the Max Stack Size for the thread * @@ -919,41 +1125,7 @@ namespace thread */ template atomicx(T (&stack)[N]) : m_context{}, m_stackSize{N}, m_stack((volatile uint8_t*) stack) { - SetDefaultParameters(); - - AddThisThread(); - } - - /** - * @brief Construct a new atomicx object and set initial auto stack and increase pace - * - * @param nStackSize Initial Size of the stack - * @param nStackIncreasePace defalt=1, The increase pace on each resize - */ - atomicx(size_t nStackSize=0, int nStackIncreasePace=1); - - /** - * @brief The pure virtual function that runs the thread loop - * - * @note REQUIRED implementation and once it returns it will execute finish method - */ - virtual void run(void) noexcept = 0; - - /** - * @brief Handles the StackOverflow of the current thread - * - * @note REQUIRED - */ - virtual void StackOverflowHandler(void) noexcept = 0; - - /** - * @brief Called right after run returns, can be used to self-destroy the object and other maintenance actions - * - * @note if not implemented a default "empty" call is used instead - */ - virtual void finish() noexcept - { - return; + SetDefaultInitializations(); } /** @@ -1002,103 +1174,219 @@ namespace thread bool IsDynamicNiceOn(); /** - * SPECIAL PRIVATE SECTION FOR HELPER METHODS USED BY PROCTED METHODS + * @brief Return how many threads assigned + * + * @return size_t number of assigned threads */ - private: + size_t GetThreadCount(); /** - * @brief Set the Default Parameters for constructors - * + * @brief Atop current thread context */ - void SetDefaultParameters (); + void Stop (); - template void SetWaitParammeters (T& refVar, size_t nTag=0, aSubTypes asubType = aSubTypes::wait) - { - m_TopicId = 0; - m_pLockId = (uint8_t*)&refVar; - m_aStatus = aTypes::wait; - m_aSubStatus = asubType; + /** + * @brief Report if the current thread is stopped + * + * @return true if stopped otherwise false + */ + bool IsStopped (); - m_lockMessage.tag = nTag; - m_lockMessage.message = 0; - } + /** + * @brief If stooped resume current thread + */ + void Resume(); + + /** + * @brief Finish and detach the thread from the pool + + * @return true successfully detached + */ + bool Finish(); + + /** + * @brief Detach current thread from the thread controller + * + * @return true if successful detached, otherwise zero + */ + void Detach(); + + /** + * @brief Mark thread to be restarted, all ongoing procedures will be dropped, finish is also called + * + * @return true if successful mask to restart, otherwise zero + */ + void Restart(); /** + * @brief Return the pointer for the next thread + * + * @return atomicx* if next thread return pointer otherwise nullptr + */ + atomicx* operator++ (void); + + /** * @brief Safely notify all Waits from a specific reference pointer along with a message without triggering context change * * @tparam T Type of the reference pointer * @param nMessage The size_t message to be sent * @param refVar The reference pointer used a a notifier - * @param nTag The size_t tag that will give meaning to the notification + * @param nTag The size_t tag that will give meaning to the notification, if nTag == 0 means notify all refVar regardless * @param notifyAll default = false, and only the fist available refVar Waiting thread will be notified, if true all available * refVar waiting thread will be notified. + * @param asubType Type of the notification, only use it if you know what you are doing, it creates a different + * type of wait/notify, deafault == aSubType::wait * * @return true if at least one got notified, otherwise false. */ - template size_t SafeNotifier(size_t& nMessage, T& refVar, size_t nTag, aSubTypes subType, NotifyType notifyAll=NotifyType::one) + template size_t SafeNotify(size_t& nMessage, T& refVar, size_t nTag, NotifyType notifyAll=NotifyType::one, aSubTypes asubType = aSubTypes::wait) { - size_t nRet = 0; - - for (auto& thr : *this) - { - if (thr.m_aSubStatus == subType && thr.m_aStatus == aTypes::wait && thr.m_pLockId == (void*) &refVar && nTag == thr.m_lockMessage.tag) - { - thr.m_TopicId = 0; - thr.m_aStatus = aTypes::now; - thr.m_nTargetTime = 0; - thr.m_pLockId = nullptr; - - thr.m_lockMessage.message = nMessage; - thr.m_lockMessage.tag = nTag; - - nRet++; - - if (notifyAll == NotifyType::one) - { - break; - } - } - } - - return nRet; + return SafeNotifier(nMessage, refVar, nTag, asubType, notifyAll); } /** - * @brief Safely notify all LookForWaitings from a specific reference pointer along with a message without triggering context change + * @brief Notify all Waits from a specific reference pointer along with a message and trigger context change if at least one wait thread got notified * * @tparam T Type of the reference pointer + * @param nMessage The size_t message to be sent * @param refVar The reference pointer used a a notifier - * @param nTag The size_t tag that will give meaning to the notification + * @param nTag The size_t tag that will give meaning to the notification, if nTag == 0 means notify all refVar regardless + * @param notifyAll default = false, and only the fist available refVar Waiting thread will be notified, if true all available + * refVar waiting thread will be notified. + * @param asubType Type of the notification, only use it if you know what you are doing, it creates a different + * type of wait/notify, deafault == aSubType::wait * * @return true if at least one got notified, otherwise false. */ - template size_t SafeNotifyLookWaitings(T& refVar, size_t nTag) + template size_t Notify(size_t& nMessage, T& refVar, size_t nTag, NotifyType notifyAll=NotifyType::one, aSubTypes asubType = aSubTypes::wait) { - size_t message=0; + size_t bRet = SafeNotify (nMessage, refVar, nTag, notifyAll, asubType); - return SafeNotifier(message, refVar, nTag, aSubTypes::look, NotifyType::all); - } + if (bRet) Yield(0); - /** - * PROTECTED METHODS, THOSE WILL BE ONLY ACCESSIBLE BY THE THREAD ITSELF - */ - protected: + return bRet; + } - struct Message + template size_t Notify(size_t&& nMessage, T& refVar, size_t nTag, NotifyType notifyAll=NotifyType::one, aSubTypes asubType = aSubTypes::wait) { - size_t tag; - size_t message; - }; + size_t bRet = SafeNotify (nMessage, refVar, nTag, notifyAll, asubType); + + if (bRet) Yield(0); + + return bRet; + } /** - * @brief calculate the Topic ID for a given topic text - * - * @param pszTopic Topic Text in C string - * @param nKeyLenght Size, in bytes + zero terminated char + * @brief SYNC Waits for at least one Wait call for a given reference pointer along with a message and trigger context change * - * @return uint32_t The calculated topic ID + * @tparam T Type of the reference pointer + * @param nMessage The size_t message to be sent + * @param refVar The reference pointer used a a notifier + * @param nTag The size_t tag that will give meaning to the notification, if nTag == 0 means notify all refVar regardless + * @param waitForWaitings default=0 (waiting for Waiting calls) othersize wait for Wait commands compatible with the paramenters (Sync call). + * @param notifyAll default = false, and only the fist available refVar Waiting thread will be notified, if true all available + * refVar waiting thread will be notified. + * @param asubType Type of the notification, only use it if you know what you are doing, it creates a different + * type of wait/notify, deafault == aSubType::wait + * + * @return 0 if no thread gets notified or timeout otherwise how much threads notified + */ + template size_t SyncNotify(size_t& nMessage, T& refVar, size_t nTag, atomicx_time waitForWaitings=0, NotifyType notifyAll=NotifyType::one, aSubTypes asubType = aSubTypes::wait) + { + if (LookForWaitings (refVar, nTag, waitForWaitings) == false) + { + return 0; + } + + size_t bRet = SafeNotify (nMessage, refVar, nTag, notifyAll, asubType); + + if (bRet) Yield(0); + + return bRet; + } + + template size_t SyncNotify(size_t&& nMessage, T& refVar, size_t nTag, atomicx_time waitForWaitings=0, NotifyType notifyAll=NotifyType::one, aSubTypes asubType = aSubTypes::wait) + { + if (LookForWaitings (refVar, nTag, waitForWaitings) == false) + { + return 0; + } + + size_t bRet = SafeNotify (nMessage, refVar, nTag, notifyAll, asubType); + + if (bRet) Yield(0); + + return bRet; + } + + /** + * @brief Safely notify all Waits from a specific reference pointer without triggering context change + * + * @tparam T Type of the reference pointer + * @param refVar The reference pointer used a a notifier + * @param nTag The size_t tag that will give meaning to the notification, if nTag == 0 means notify all refVar regardless + * @param notifyAll default = false, and only the fist available refVar Waiting thread will be notified, if true all available + * refVar waiting thread will be notified. + * @param asubType Type of the notification, only use it if you know what you are doing, it creates a different + * type of wait/notify, deafault == aSubType::wait + * + * @return true if at least one got notified, otherwise false. */ - uint32_t GetTopicID (const char* pszTopic, size_t nKeyLenght); + template size_t SafeNotify(T& refVar, size_t nTag, NotifyType notifyAll=NotifyType::one, aSubTypes asubType = aSubTypes::wait) + { + size_t message=0; + return SafeNotifier (message, refVar, nTag, asubType, notifyAll); + } + + /** + * @brief SYNC Waits for at least one Wait call for a given reference pointer and trigger context change + * + * @tparam T Type of the reference pointer + * @param refVar The reference pointer used a a notifier + * @param nTag The size_t tag that will give meaning to the notification, if nTag == 0 means notify all refVar regardless + * @param waitForWaitings default=0 (waiting for Waiting calls) othersize wait for Wait commands compatible with the paramenters (Sync call). + * @param notifyAll default = false, and only the fist available refVar Waiting thread will be notified, if true all available + * refVar waiting thread will be notified. + * @param asubType Type of the notification, only use it if you know what you are doing, it creates a different + * type of wait/notify, deafault == aSubType::wait + * + * @return true if at least one got notified, otherwise false. + */ + template size_t SyncNotify(T& refVar, size_t nTag, atomicx_time waitForWaitings=0, NotifyType notifyAll=NotifyType::one, aSubTypes asubType = aSubTypes::wait) + { + if (LookForWaitings (refVar, nTag, waitForWaitings) == false) + { + return 0; + } + + size_t bRet = SafeNotify(refVar, nTag, notifyAll, asubType); + + if (bRet) Yield(0); + + return bRet; + } + + /** + * @brief Notify all Waits from a specific reference pointer and trigger context change if at least one wait thread got notified + * + * @tparam T Type of the reference pointer + * @param refVar The reference pointer used a a notifier + * @param nTag The size_t tag that will give meaning to the notification, if nTag == 0 means notify all refVar regardless + * @param notifyAll default = false, and only the fist available refVar Waiting thread will be notified, if true all available + * refVar waiting thread will be notified. + * @param asubType Type of the notification, only use it if you know what you are doing, it creates a different + * type of wait/notify, deafault == aSubType::wait + * + * @return true if at least one got notified, otherwise false. + */ + template size_t Notify(T& refVar, size_t nTag, NotifyType notifyAll=NotifyType::one, aSubTypes asubType = aSubTypes::wait) + { + size_t bRet = SafeNotify(refVar, nTag, notifyAll, asubType); + + if (bRet) Yield(0); + + return bRet; + } /** * ------------------------------ @@ -1111,21 +1399,19 @@ namespace thread * * @tparam T Type of the reference pointer * @param refVar The reference pointer - * @param nTag The notification meaning, if nTag == 0 means wait all refVar regardless + * @param nTag The notification meaning * @param waitFor default=0, if 0 wait indefinitely, otherwise wait for custom tick granularity times * @param hasAtleast define how minimal Wait calls to report true * * @return true There is thread waiting for the given refVar/nTag */ - template bool LookForWaitings(T& refVar, size_t nTag, size_t hasAtleast, atomicx_time waitFor) + template bool LookForWaitings(T& refVar, size_t nTag, size_t hasAtleast, Timeout waitFor) { - Timeout timeout (waitFor); - - while ((waitFor == 0 || timeout.IsTimedout () == false) && IsWaiting(refVar, nTag, hasAtleast) == false) + while (waitFor.IsTimedout () == false && IsWaiting(refVar, nTag, hasAtleast) == false) { SetWaitParammeters (refVar, nTag, aSubTypes::look); - Yield(waitFor); + Yield(waitFor.GetRemaining ()); m_lockMessage = {0,0}; @@ -1133,15 +1419,9 @@ namespace thread { return false; } - - // Decrease the timeout time to slice the remaining time otherwise break it - if (waitFor == 0 || (waitFor = timeout.GetRemaining ()) == 0) - { - break; - } } - return (timeout.IsTimedout ()) ? false : true; + return (waitFor.IsTimedout ()) ? false : true; } /** @@ -1149,7 +1429,7 @@ namespace thread * * @tparam T Type of the reference pointer * @param refVar The reference pointer - * @param nTag The notification meaning, if nTag == 0 means wait all refVar regardless + * @param nTag The notification meaning * @param waitFor default=0, if 0 wait indefinitely, otherwise wait for custom tick granularity times * * @return true There is thread waiting for the given refVar/nTag @@ -1178,7 +1458,7 @@ namespace thread * * @tparam T Type of the reference pointer * @param refVar The reference pointer used a a notifier - * @param nTag The size_t tag that will give meaning to the notification, if nTag == 0 mean all bTag for the refVar + * @param nTag The size_t tag that will give meaning to the notification * @param asubType Type of the notification, only use it if you know what you are doing, it creates a different * type of wait/notify, deafault == aSubType::wait * @@ -1186,19 +1466,13 @@ namespace thread * * @note This is a powerful tool since it create layers of waiting within the same reference pointer */ - template bool IsWaiting(T& refVar, size_t nTag=0, size_t hasAtleast = 1, aSubTypes asubType = aSubTypes::wait) + template bool IsWaiting(T& refVar, size_t nTag, size_t hasAtleast = 1, aSubTypes asubType = aSubTypes::wait) { hasAtleast = hasAtleast == 0 ? 1 : hasAtleast; - for (auto& thr : *this) + if (HasWaitings (refVar, nTag, asubType)>=hasAtleast) { - if (thr.m_aSubStatus == asubType && thr.m_aStatus == aTypes::wait && thr.m_pLockId == (void*) &refVar && (thr.m_lockMessage.tag == nTag)) - { - if ((--hasAtleast) == 0) - { - return true; - } - } + return true; } return false; @@ -1209,7 +1483,7 @@ namespace thread * * @tparam T Type of the reference pointer * @param refVar The reference pointer used a a notifier - * @param nTag The size_t tag that will give meaning to the notification, if nTag == 0 mean all bTag for the refVar + * @param nTag The size_t tag that will give meaning to the notification * @param asubType Type of the notification, only use it if you know what you are doing, it creates a different * type of wait/notify, deafault == aSubType::wait * @@ -1217,13 +1491,13 @@ namespace thread * * @note This is a powerful tool since it create layers of waiting within the same reference pointer */ - template size_t HasWaitings(T& refVar, size_t nTag=0, aSubTypes asubType = aSubTypes::wait) + template size_t HasWaitings(T& refVar, size_t nTag, aSubTypes asubType = aSubTypes::wait) { size_t nCounter = 0; for (auto& thr : *this) { - if (thr.m_aSubStatus == asubType && thr.m_aStatus == aTypes::wait && thr.m_aStatus == aTypes::wait && thr.m_pLockId == (void*) &refVar && (thr.m_lockMessage.tag == nTag)) + if (IsNotificationEligible (thr, refVar, nTag, asubType)) { nCounter++; } @@ -1238,14 +1512,18 @@ namespace thread * @tparam T Type of the reference pointer * @param nMessage the size_t message to be received * @param refVar the reference pointer used as a notifier - * @param nTag the size_t tag that will give meaning to the the message, if nTag == 0 means wait all refVar regardless - * @param waitFor default==0 (undefinitly), How log to wait for a notification based on atomicx_time + * @param nTag the size_t tag that will give meaning to the the message + * @param waitFor default==0 (indefinitely), How log to wait for a notification based on atomicx_time * @param asubType Type of the notification, only use it if you know what you are doing, it creates a different * type of wait/notify, deafault == aSubType::wait * @return true if it was successfully received. + * + * @note if tag is set to zero Waits will return false on call. */ - template bool Wait(size_t& nMessage, T& refVar, size_t nTag=0, atomicx_time waitFor=0, aSubTypes asubType = aSubTypes::wait) + template bool Wait(size_t& nMessage, T& refVar, size_t nTag, atomicx_time waitFor=0, aSubTypes asubType = aSubTypes::wait) { + if (nTag == 0 ) return false; + SafeNotifyLookWaitings(refVar, nTag); SetWaitParammeters (refVar, nTag, asubType); @@ -1275,331 +1553,369 @@ namespace thread * @tparam T Type of the reference pointer * @param refVar the reference pointer used as a notifier * @param nTag the size_t tag that will give meaning to the the message, if nTag == 0 means wait all refVar regardless - * @param waitFor default==0 (undefinitly), How log to wait for a notification based on atomicx_time + * @param waitFor default==0 (indefinitely), How log to wait for a notification based on atomicx_time * @param asubType Type of the notification, only use it if you know what you are doing, it creates a different * type of wait/notify, deafault == aSubType::wait * * @return true if it was successfully received. */ - template bool Wait(T& refVar, size_t nTag=0, atomicx_time waitFor=0, aSubTypes asubType = aSubTypes::wait) + template bool Wait(T& refVar, size_t nTag, atomicx_time waitFor=0, aSubTypes asubType = aSubTypes::wait) { + size_t nMessage = 0; + + return Wait (nMessage, refVar, nTag, waitFor, asubType); + } + + /** + * @brief Blocks/Waits for any notification from a specific reference pointer and reports message and tag + * + * @tparam T Type of the reference pointer + * @param nMessage return ref size_t message received + * @param refVar the reference pointer used as a notifier + * @param nTag return ref size_t tag that will give meaning to the the message + * @param waitFor default==0 (indefinitely), How log to wait for a notification based on atomicx_time + * @param asubType Type of the notification, only use it if you know what you are doing, it creates a different + * type of wait/notify, deafault == aSubType::wait + * @return true if it was successfully received. + */ + template bool WaitAny(size_t& nMessage, T& refVar, size_t& nTag, atomicx_time waitFor=0, aSubTypes asubType = aSubTypes::wait) + { + nTag = 0; + SafeNotifyLookWaitings(refVar, nTag); SetWaitParammeters (refVar, nTag, asubType); - m_lockMessage.tag = nTag; - Yield(waitFor); bool bRet = false; if (m_aSubStatus != aSubTypes::timeout) { + nMessage = m_lockMessage.message; + nTag = m_lockMessage.tag; + bRet = true; } m_lockMessage = {0,0}; + m_aSubStatus = aSubTypes::ok; return bRet; } /** - * @brief Safely notify all Waits from a specific reference pointer along with a message without triggering context change + * @brief Blocks/Waits for any notification from a specific reference pointer and also return the tag * * @tparam T Type of the reference pointer - * @param nMessage The size_t message to be sent - * @param refVar The reference pointer used a a notifier - * @param nTag The size_t tag that will give meaning to the notification, if nTag == 0 means notify all refVar regardless - * @param notifyAll default = false, and only the fist available refVar Waiting thread will be notified, if true all available - * refVar waiting thread will be notified. + * @param refVar the reference pointer used as a notifier + * @param nTag return ref size_t tag that will give meaning to the the message + * @param waitFor default==0 (indefinitely), How log to wait for a notification based on atomicx_time * @param asubType Type of the notification, only use it if you know what you are doing, it creates a different * type of wait/notify, deafault == aSubType::wait * - * @return true if at least one got notified, otherwise false. + * @return true if it was successfully received. */ - template size_t SafeNotify(size_t& nMessage, T& refVar, size_t nTag=0, NotifyType notifyAll=NotifyType::one, aSubTypes asubType = aSubTypes::wait) + template bool WaitAny(T& refVar, size_t& nTag, atomicx_time waitFor=0, aSubTypes asubType = aSubTypes::wait) { - return SafeNotifier(nMessage, refVar, nTag, asubType, notifyAll); + size_t nMessage = 0; + + return WaitAny (nMessage, refVar, nTag, waitFor, asubType); } /** - * @brief Notify all Waits from a specific reference pointer along with a message and trigger context change if at least one wait thread got notified - * - * @tparam T Type of the reference pointer - * @param nMessage The size_t message to be sent - * @param refVar The reference pointer used a a notifier - * @param nTag The size_t tag that will give meaning to the notification, if nTag == 0 means notify all refVar regardless - * @param notifyAll default = false, and only the fist available refVar Waiting thread will be notified, if true all available - * refVar waiting thread will be notified. - * @param asubType Type of the notification, only use it if you know what you are doing, it creates a different - * type of wait/notify, deafault == aSubType::wait - * - * @return true if at least one got notified, otherwise false. + * ------------------------------ + * MESSAGE BROADCAST IMPLEMENTATION + * Public part for the implementation + * ------------------------------ */ - template size_t Notify(size_t& nMessage, T& refVar, size_t nTag=0, NotifyType notifyAll=NotifyType::one, aSubTypes asubType = aSubTypes::wait) - { - size_t bRet = SafeNotify (nMessage, refVar, nTag, notifyAll, asubType); - - if (bRet) Yield(0); - - return bRet; - } - - template size_t Notify(size_t&& nMessage, T& refVar, size_t nTag=0, NotifyType notifyAll=NotifyType::one, aSubTypes asubType = aSubTypes::wait) - { - size_t bRet = SafeNotify (nMessage, refVar, nTag, notifyAll, asubType); - - if (bRet) Yield(0); - - return bRet; - } /** - * @brief SYNC Waits for at least one Wait call for a given reference pointer along with a message and trigger context change + * @brief Broadcast a message to all threads * - * @tparam T Type of the reference pointer - * @param nMessage The size_t message to be sent - * @param refVar The reference pointer used a a notifier - * @param nTag The size_t tag that will give meaning to the notification, if nTag == 0 means notify all refVar regardless - * @param waitForWaitings default=0 (waiting for Waiting calls) othersize wait for Wait commands compatible with the paramenters (Sync call). - * @param notifyAll default = false, and only the fist available refVar Waiting thread will be notified, if true all available - * refVar waiting thread will be notified. - * @param asubType Type of the notification, only use it if you know what you are doing, it creates a different - * type of wait/notify, deafault == aSubType::wait + * @param messageReference Works as the signaling + * @param message Message structure with the message + * message is the payload + * tag is the meaning * - * @return true if at least one got notified, otherwise false. + * @return size_t */ - template size_t SyncNotify(size_t& nMessage, T& refVar, size_t nTag=0, atomicx_time waitForWaitings=0, NotifyType notifyAll=NotifyType::one, aSubTypes asubType = aSubTypes::wait) - { - if (LookForWaitings (refVar, nTag, waitForWaitings) == false) - { - return 0; - } + size_t BroadcastMessage (const size_t messageReference, const Message message); - size_t bRet = SafeNotify (nMessage, refVar, nTag, notifyAll, asubType); + /** + * SEND AND RECEIVE DATA USING DATA PIPES + * to 1 to many + */ + + enum class TransferState : uint8_t + { + ready=10, + start=11, + transfering=12, + end=13 + }; - if (bRet) Yield(0); + typedef size_t tranfer_t; + + template uint16_t Send (T& refVar, uint8_t* pData, uint16_t nDataSize, Timeout timeout) + { + size_t nReceivers = 0; - return bRet; - } + if (pData == nullptr || nDataSize == 0) return 0; - template size_t SyncNotify(size_t&& nMessage, T& refVar, size_t nTag=0, atomicx_time waitForWaitings=0, NotifyType notifyAll=NotifyType::one, aSubTypes asubType = aSubTypes::wait) - { - if (LookForWaitings (refVar, nTag, waitForWaitings) == false) + // Starting + if ((nReceivers = SyncNotify (nDataSize, refVar, (size_t) TransferState::start, timeout.GetRemaining ())) == 0) { + // There is no reader listening the same refVar return 0; } - size_t bRet = SafeNotify (nMessage, refVar, nTag, notifyAll, asubType); - - if (bRet) Yield(0); - - return bRet; - } + uint8_t nMessage [sizeof (tranfer_t)]; + const uint8_t nPayloadSize = sizeof (tranfer_t)-1; + uint16_t nDataSent = 0; + + // Transfering + while (nDataSize && ! timeout.IsTimedout ()) + { + nMessage [0] = nDataSize < (nPayloadSize) ? nDataSize : nPayloadSize; + + if (! (memcpy ((void*) &nMessage[1], pData, nMessage [0]) != nullptr + && LookForWaitings(refVar, (size_t) TransferState::transfering, (size_t) nReceivers, timeout.GetRemaining ()) + && ! timeout.IsTimedout () + && SyncNotify (*((size_t*) nMessage), refVar, (size_t) TransferState::transfering, timeout.GetRemaining ()))) + { + return 0; + } - /** - * @brief Safely notify all Waits from a specific reference pointer without triggering context change - * - * @tparam T Type of the reference pointer - * @param refVar The reference pointer used a a notifier - * @param nTag The size_t tag that will give meaning to the notification, if nTag == 0 means notify all refVar regardless - * @param notifyAll default = false, and only the fist available refVar Waiting thread will be notified, if true all available - * refVar waiting thread will be notified. - * @param asubType Type of the notification, only use it if you know what you are doing, it creates a different - * type of wait/notify, deafault == aSubType::wait - * - * @return true if at least one got notified, otherwise false. - */ - template size_t SafeNotify(T& refVar, size_t nTag=0, NotifyType notifyAll=NotifyType::one, aSubTypes asubType = aSubTypes::wait) - { - size_t message=0; - return SafeNotifier (message, refVar, nTag, asubType, notifyAll); - } + nDataSize -= (size_t) nMessage [0]; + pData += (size_t) nMessage [0]; + nDataSent += (size_t) nMessage [0]; + } - /** - * @brief SYNC Waits for at least one Wait call for a given reference pointer and trigger context change - * - * @tparam T Type of the reference pointer - * @param refVar The reference pointer used a a notifier - * @param nTag The size_t tag that will give meaning to the notification, if nTag == 0 means notify all refVar regardless - * @param waitForWaitings default=0 (waiting for Waiting calls) othersize wait for Wait commands compatible with the paramenters (Sync call). - * @param notifyAll default = false, and only the fist available refVar Waiting thread will be notified, if true all available - * refVar waiting thread will be notified. - * @param asubType Type of the notification, only use it if you know what you are doing, it creates a different - * type of wait/notify, deafault == aSubType::wait - * - * @return true if at least one got notified, otherwise false. - */ - template size_t SyncNotify(T& refVar, size_t nTag, atomicx_time waitForWaitings=0, NotifyType notifyAll=NotifyType::one, aSubTypes asubType = aSubTypes::wait) - { - if (LookForWaitings (refVar, nTag, waitForWaitings) == false) + // Finishing + if (! (! timeout.IsTimedout () && LookForWaitings(refVar, (size_t) TransferState::transfering, (size_t) nReceivers, timeout.GetRemaining ()) && ! timeout.IsTimedout () + && SyncNotify (nDataSent, refVar, (size_t) TransferState::end, timeout.GetRemaining ()))) { return 0; } - size_t bRet = SafeNotify(refVar, nTag, notifyAll, asubType); - - if (bRet) Yield(0); - - return bRet; + return nDataSent; } - /** - * @brief Notify all Waits from a specific reference pointer and trigger context change if at least one wait thread got notified - * - * @tparam T Type of the reference pointer - * @param refVar The reference pointer used a a notifier - * @param nTag The size_t tag that will give meaning to the notification, if nTag == 0 means notify all refVar regardless - * @param notifyAll default = false, and only the fist available refVar Waiting thread will be notified, if true all available - * refVar waiting thread will be notified. - * @param asubType Type of the notification, only use it if you know what you are doing, it creates a different - * type of wait/notify, deafault == aSubType::wait - * - * @return true if at least one got notified, otherwise false. - */ - template size_t Notify(T& refVar, size_t nTag=0, NotifyType notifyAll=NotifyType::one, aSubTypes asubType = aSubTypes::wait) + template uint16_t Receive (T& refVar, uint8_t* pData, uint16_t nDataSize, Timeout timeout) { - size_t bRet = SafeNotify(refVar, nTag, notifyAll, asubType); + size_t nMessage = 0; + size_t nTag = 0; + TransferState state = TransferState::ready; + + uint8_t nWriteLen = 0; + uint16_t nReceivedLen = 0; - if (bRet) Yield(0); + if (pData == nullptr || nDataSize == 0) return 0; - return bRet; + while (! timeout.IsTimedout () && WaitAny (nMessage, refVar, nTag, timeout.GetRemaining ())) + { + //Wait to start + if (state == TransferState::ready) + { + if ((TransferState) nTag == TransferState::start) + { + state = TransferState::transfering; + } + else + { + continue; + } + } + else if (state == TransferState::transfering && state == (TransferState) nTag && nReceivedLen < nDataSize) // transfering + { + // Transfering + uint8_t* nPayload = (uint8_t*) &nMessage; + nWriteLen = (nReceivedLen + nPayload [0]) > nDataSize ? (nDataSize - nReceivedLen) : nPayload [0]; + + if (memcpy (pData, &nPayload[1], nWriteLen) == nullptr) + { + return 0; + } + + pData += nPayload [0]; + nReceivedLen += nPayload [0]; + } + else if (state == TransferState::transfering && (TransferState) nTag == TransferState::end) + { + // Ending + break; + } + } + + return timeout.IsTimedout () ? 0 : nReceivedLen; } - /** - * ------------------------------ - * SMART BROKER IMPLEMENTATION - * ------------------------------ - */ + /** + * PROTECTED METHODS, THOSE WILL BE ONLY ACCESSIBLE BY THE THREAD ITSELF + */ + protected: /** - * @brief Block and wait for message from a specific topic string - * - * @param pszKey The Topic string - * @param nKeyLenght The size of the topic string in bytes - * @param message the atomicx::message structure with message and tag + * @brief The pure virtual function that runs the thread loop * - * @return true if it was successfully received, otherwise false + * @note REQUIRED implementation and once it returns it will execute finish method */ - bool WaitBrokerMessage (const char* pszKey, size_t nKeyLenght, Message& message); + virtual void run(void) noexcept = 0; /** - * @brief Block and wait for a notification from a specific topic string - * - * @param pszKey The Topic string - * @param nKeyLenght The size of the topic string in bytes + * @brief Handles the StackOverflow of the current thread * - * @return true if it was successfully received, otherwise false + * @note REQUIRED */ - bool WaitBrokerMessage (const char* pszKey, size_t nKeyLenght); + virtual void StackOverflowHandler(void) noexcept = 0; /** - * @brief Publish a message for a specific topic string and trigger a context change if any delivered - * - * @param pszKey The Topic string - * @param nKeyLenght The size of the topic string in bytes - * @param message the atomicx::message structure with message and tag + * @brief Called right after run returns, can be used to self-destroy the object and other maintenance actions * - * @return true if at least one thread has received a message + * @note if not implemented a default "empty" call is used instead */ - bool Publish (const char* pszKey, size_t nKeyLenght, const Message message); + virtual void finish() noexcept; /** - * @brief Safely Publish a message for a specific topic string DO NOT trigger a context change if any delivered - * - * @param pszKey The Topic string - * @param nKeyLenght The size of the topic string in bytes - * @param message the atomicx::message structure with message and tag - * - * @return true if at least one thread has received a message - * - * @note Ideal for been used with interrupt request + * ------------------------------ + * MESSAGE BROADCAST IMPLEMENTATION + * ------------------------------ */ - bool SafePublish (const char* pszKey, size_t nKeyLenght, const Message message); + /** + * @brief The default broadcast handler used to received the message + * + * @param messageReference Works as the signaling + * @param message Message structure with the message + * message is the payload + * tag is the meaning + */ + virtual void BroadcastHandler (const size_t& messageReference, const Message& message); + /** - * @brief Publish a notification for a specific topic string and trigger a context change if any delivered - * - * @param pszKey The Topic string - * @param nKeyLenght The size of the topic string in bytes - * - * @return true if at least one thread has received a message + * @brief Enable or Disable async broadcast + * + * @param bBroadcastStatus if true, the thread will receive broadcast otherwise no */ - bool Publish (const char* pszKey, size_t nKeyLenght); + void SetReceiveBroadcast (bool bBroadcastStatus); /** - * @brief Safely Publish a notification for a specific topic string DO NOT trigger a context change if any delivered - * - * @param pszKey The Topic string - * @param nKeyLenght The size of the topic string in bytes - * - * @return true if at least one thread has received a message + * @brief Set the Stack Increase Pace object * - * @note Ideal for been used with interrupt request + * @param nIncreasePace The new stack increase pace value */ - bool SafePublish (const char* pszKey, size_t nKeyLenght); + void SetStackIncreasePace(size_t nIncreasePace); + /** - * @brief Check if there is subscryption for a specific Topic String - * - * @param pszTopic The Topic string in C string - * @param nKeyLenght The Topic C string length in bytes - * - * @return true if any substriction is found, otherwise false + * SPECIAL PRIVATE SECTION FOR HELPER METHODS USED BY PROCTED METHODS */ - bool HasSubscriptions (const char* pszTopic, size_t nKeyLenght); + private: /** - * @brief Check if there is subscryption for a specific Topic ID - * - * @param nKeyID The Topic ID uint32_t - * - * @return true if any substriction is found, otherwise false + * @brief Report is the current thread, refVar, ntag and subType is eligible to receive a notification + * + * @tparam T the object used as the reference pointer + * @param thr The thread to be notified + * @param refVar The reference pointer used as a notifier + * @param nTag The size_t tag that will give meaning to the notification + * @param subType The subtype of the type::wait + * + * @return false if the current thread can not be notified with the given configuration, otherwise true */ - bool HasSubscriptions (uint32_t nKeyID); + template bool IsNotificationEligible (atomicx& thr, T& refVar, size_t nTag, aSubTypes subType) + { + if (thr.m_aSubStatus == subType && + thr.m_aStatus == aTypes::wait && + thr.m_pLockId == (void*) &refVar && + (nTag == 0 || thr.m_lockMessage.tag == 0 || nTag == thr.m_lockMessage.tag)) + { + return true; + } + + return false; + } /** - * @brief Default broker handler for a subscribed message - * - * @param pszKey The Topic C string - * @param nKeyLenght The Topic C string size in bytes - * @param message The atomicx::message payload received + * @brief Safely notify all LookForWaitings from a specific reference pointer along with a message without triggering context change * - * @return true signify it was correctly processed + * @tparam T Type of the reference pointer + * @param refVar The reference pointer used as a notifier + * @param nTag The size_t tag that will give meaning to the notification * - * @note Can be overloaded by the derived by the derived thread implementation and specialized, - * otherwise a empty function will be called instead + * @return true if at least one got notified, otherwise false. */ - virtual bool BrokerHandler(const char* pszKey, size_t nKeyLenght, Message& message) + template size_t SafeNotifyLookWaitings(T& refVar, size_t nTag) { - (void) pszKey; (void) nKeyLenght; (void) message; - return false; + size_t message=0; + + return SafeNotifier(message, refVar, nTag, aSubTypes::look, NotifyType::all); } /** - * @brief Specialize and gives power to decide if a topic is subscrybed on not + * @brief Safely notify all Waits from a specific reference pointer along with a message without triggering context change * - * @param pszKey The Topic C String - * @param nKeyLenght The Topic C String size in bytes + * @tparam T Type of the reference pointer + * @param nMessage The size_t message to be sent + * @param refVar The reference pointer used as a notifier + * @param nTag The size_t tag that will give meaning to the notification + * @param notifyAll default = false, and only the fist available refVar Waiting thread will be notified, if true all available + * refVar waiting thread will be notified. * - * @return true if the given topic was subscribed, otherwise false. + * @return true if at least one got notified, otherwise false. */ - virtual bool IsSubscribed (const char* pszKey, size_t nKeyLenght) + template size_t SafeNotifier(size_t& nMessage, T& refVar, size_t nTag, aSubTypes subType, NotifyType notifyAll=NotifyType::one) { - (void) pszKey; (void) nKeyLenght; + size_t nRet = 0; - return false; + for (auto& thr : *this) + { + if (IsNotificationEligible (thr, refVar, nTag, subType)) + { + thr.m_aStatus = aTypes::now; + thr.m_nTargetTime = 0; + thr.m_pLockId = nullptr; + + thr.m_lockMessage.message = nMessage; + thr.m_lockMessage.tag = nTag; + + nRet++; + + if (notifyAll == NotifyType::one) + { + break; + } + } + } + + return nRet; } /** - * @brief Set the Stack Increase Pace object + * @brief Set the Default Parameters for constructors * - * @param nIncreasePace The new stack increase pace value */ - void SetStackIncreasePace(size_t nIncreasePace); + void SetDefaultInitializations (); - private: + /** + * @brief Configure the Wait parameters for the current thread. + * + * @tparam T + * @param refVar + * @param nTag + * @param asubType + */ + template void SetWaitParammeters (T& refVar, size_t nTag, aSubTypes asubType = aSubTypes::wait) + { + m_pLockId = (uint8_t*)&refVar; + m_aStatus = aTypes::wait; + m_aSubStatus = asubType; + + m_lockMessage.tag = nTag; + m_lockMessage.message = 0; + } /** * @brief Add the current thread to the global thread list @@ -1611,6 +1927,11 @@ namespace thread */ void RemoveThisThread(); + /** + * @brief Internally used to unload thread parameters and detach from the static list + */ + void DestroyThread(); + /** * @brief CRC16 used to compose a multi uint32_t for Topic ID * @@ -1650,8 +1971,6 @@ namespace thread atomicx_time m_LastUserExecTime=0; atomicx_time m_lastResumeUserTime=0; - uint32_t m_TopicId=0; - aTypes m_aStatus = aTypes::start; aSubTypes m_aSubStatus = aSubTypes::ok; @@ -1663,9 +1982,12 @@ namespace thread struct { + bool KernelIsRunning : 1; bool autoStack : 1; bool dynamicNice : 1; - } m_flags = {}; + bool broadcast : 1; + bool attached :1; + } m_flags = {0, 0,0,0,0}; }; } diff --git a/examples/Arduino/DotMatrix/DotMatrix.ino b/examples/Arduino/DotMatrix/DotMatrix.ino new file mode 100755 index 0000000..6b8f74b --- /dev/null +++ b/examples/Arduino/DotMatrix/DotMatrix.ino @@ -0,0 +1,206 @@ +/// +/// @author GUSTAVO CAMPOS +/// @author GUSTAVO CAMPOS +/// @date 05/02/2020 +/// @version 0.1 +/// +/// @copyright (c) GUSTAVO CAMPOS, 2019 +/// @copyright Licence +/// +/* + MIT License + + Copyright (c) 2021 Gustavo Campos + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +*/ + +#ifndef __DOTMATRIX_H__ +#define __DOTMATRIX_H__ + +#include +#include + + +#include "Arduino.h" +#include "user_interface.h" + + +#include "atomicx.hpp" +#include + +#include +#include +#include + +#include "utils.hpp" +#include "TextScroller.hpp" +#include "TerminalInterface.hpp" +#include "SerialTerminal.hpp" +#include "Listner.hpp" + +#define MAX(x, y) (x > y ? x : y) +#define MIN(x, y) (x < y ? x : y) + +#ifndef STASSID +#define STASSID "WhiteKingdom2.4Ghz" +#define STAPSK "Creative01000" +#endif + +// Functions + +atomicx_time Atomicx_GetTick(void) +{ + return millis(); +} + +void Atomicx_SleepTick(atomicx_time nSleep) +{ + delay(nSleep); +} + +/* + External Threads --------------------------------------------------- +*/ + +//Dot Matrix text scroller service +TextScroller Matrix (1); + +//Listnet Service +ListenerServer NetworkServices (10); + +std::string strSystemNextMessage="AtomicX"; + +/* + SYSTEM CLASS --------------------------------------------------- +*/ + +class System : public thread::atomicx +{ +public: + System (atomicx_time nNice) : atomicx(250,50) + { + Serial.println (F("Starting up System...")); + Serial.flush (); + + SetNice (nNice); + } + + char* GetName() + { + return "System"; + } + +protected: + + void run() noexcept override + { + size_t nTag = 0; + size_t nMessage = 0; + atomicx_time last = GetCurrentTick () + 2000; + uint8_t nScrollStage = 0; + + Matrix.SetSpeed (2); + + for(;;) + { + if (WaitAny (nMessage, sysCmds, nTag, GetNice ())) + { + if (nTag == 1) + { + if (sysCmds == SysCommands::Matrix_Ready) + { + Matrix = ""; + + switch (nScrollStage) + { + // case 0: + // { + // Matrix = ""; + // Matrix () << "AtomicX, free: " << std::to_string(system_get_free_heap_size()) << "b, thr: " << std::to_string(GetThreadCount ()); + + // if (WiFi.status () == WL_CONNECTED) + // { + // Matrix () << ", ip: " << WiFi.localIP ().toString ().c_str (); + // } + + // Matrix.SetSpeed (4); + // } + // break; + + default: + + if (Wait (Matrix, TEXTSCROLLER_NOTIFY_NEW_TEXT, 5) || Matrix().str() != strSystemNextMessage) + { + Matrix = strSystemNextMessage; + } + + nScrollStage = -1; + } + + nScrollStage++; + } + } + } + //Serial.println ("System routine."); + } + } + + void StackOverflowHandler(void) noexcept override + { + PrintStackOverflow(); + } +}; + +void setup() +{ + util::InitSerial(); + + gdbstub_init(); + + vt100::ResetColor (); + //vt100::ClearConsole (); + vt100::HideCursor (); + + vt100::SetLocation (1,1); + Serial.println (F(ATOMIC_VERSION_LABEL)); + + Matrix.SetSpeed (3); + + SerialTerminal serialTerminal(1); + + System system (100); + + Serial.println (F("Thermal Camera Demo ------------------------------------")); + Serial.flush (); + + logger.AddLogger (new SerialLogger ()); + + thread::atomicx::Start(); + + Serial.println (F("FULL DEADLOCK ------------------------------------")); + Serial.flush (); + +} + +void loop() +{ +} + +#endif // __DOTMATRIX_H__ \ No newline at end of file diff --git a/examples/Arduino/DotMatrix/Listner.cpp b/examples/Arduino/DotMatrix/Listner.cpp new file mode 100644 index 0000000..e69de29 diff --git a/examples/Arduino/DotMatrix/Listner.hpp b/examples/Arduino/DotMatrix/Listner.hpp new file mode 100644 index 0000000..667198c --- /dev/null +++ b/examples/Arduino/DotMatrix/Listner.hpp @@ -0,0 +1,203 @@ +/** + * @file Listner.hpp + * @author your name (you@domain.com) + * @brief + * @version 0.1 + * @date 2022-02-18 + * + * @copyright Copyright (c) 2022 + * + */ + +#include "Arduino.h" +#include "atomicx.hpp" + +#include +#include + +#include + +#include "TelnetTerminal.hpp" +#include "utils.hpp" + +extern LoggerStream logger; + +#define MAX_SRV_CLIENTS 5 + +class ListenerServer : public thread::atomicx +{ +public: + ListenerServer(atomicx_time nNice) : thread::atomicx (250, 50), m_tcpServer(m_tcpPort) + { + SetNice (nNice); + } + +protected: + + bool InitiateNetwork (Timeout timeout) + { + logger << "Trying to connect to WIFI." << std::endl; + + util::SetDisplayMessage ("Connecting to WiFi"); + + WiFi.begin (m_ssid, m_pass); + + while (WiFi.status () != WL_CONNECTED && timeout.IsTimedout() == false) + { + Yield (500); + } + + if (WiFi.status() == WL_CONNECTED) + { + m_wifiStatus = true; + + util::SetDisplayMessage ("Connected to WIFI."); + + logger << LOG::INFO << "WIFI connected, IP " << WiFi.localIP().toString().c_str () << std::endl; + } + else + { + util::SetDisplayMessage ("Failed to connect."); + logger << LOG::ERROR << "Failed to start WiFi." << std::endl; + } + + return m_wifiStatus; + } + + bool StartUdpServices (Timeout timeout) + { + logger << "Configuring UDP Service. " << std::endl; + + while ((m_updServerStatus = m_udp.begin (m_udpPort)) == false && ! timeout.IsTimedout ()) + { + Yield (200); + } + + if (m_updServerStatus) + { + logger << LOG::INFO << "UDP Service ready on port " << m_udpPort << std::endl; + } + else + { + logger << LOG::ERROR << "Failed to start UDP Service on port " << m_udpPort << std::endl; + } + + return m_updServerStatus; + } + + bool StartingTcpServices (Timeout timeout) + { + logger << "Configuring TCP Telnet Service. " << std::endl; + do + { + m_tcpServer.begin (); /* code */ + } while (m_tcpServer.status () != LISTEN && timeout.IsTimedout ()); + + if (m_tcpServer.status () == LISTEN) + { + m_tcpServer.setNoDelay (true); + logger << LOG::INFO << "TCP Telnet Service ready" << std::endl; + } + else + { + logger << LOG::ERROR << "Failed to start TCP Telnet Service on port " << m_udpPort << std::endl; + } + + return timeout.IsTimedout () ? false : true; + } + + void run() noexcept final + { + char readBuffer [50] = ""; + ssize_t nAvaliableData = 0; + std::string strBuffer(0,' '); + + strBuffer.reserve (sizeof (readBuffer)); + + do + { + if (m_wifiStatus == false && InitiateNetwork (60000)) + { + if (m_updServerStatus == false) + { + StartingTcpServices (1000); + } + + if (m_updServerStatus == false) + { + StartUdpServices (1000); + } + + util::SetDisplayMessage (WiFi.localIP ().toString().c_str ()); + } + else + { + if ((nAvaliableData = (ssize_t) m_udp.parsePacket ()) > 0) + { + strBuffer.clear (); + ssize_t nRead; + + do + { + memset ((void*) readBuffer, 0, sizeof (readBuffer)); + + nRead = m_udp.read (readBuffer, sizeof (readBuffer)-1); + strBuffer.append (readBuffer, nRead); + + nAvaliableData -= nRead; + + } while (m_udp.available ()); + + m_udp.flush (); + + util::SetDisplayMessage (strBuffer, false); + + logger << LOG::ALERT << "Received: " << strBuffer << std::endl; + } + + if (m_tcpServer.hasClient ()) + { + logger << "Starting up a new remote terminal." << std::endl; + + // Starting a new thread + new TelnetTerminal (10, m_tcpServer); + } + } + } while (Yield ()); + } + + void StackOverflowHandler(void) noexcept override + { + PrintStackOverflow(); + } + + const char* GetName () + { + return "NetServices"; + } + +private: + bool m_wifiStatus = false; + + const char *m_ssid = "WhiteKingdom2.4Ghz"; + const char *m_pass = "Creative01000"; + const unsigned int m_localTelnetPort = 22; + + union Ip + { + uint8_t num [4]; + uint32_t address; + }; + + // UDP Server + bool m_updServerStatus = false; + bool m_telnetServerStatus = false; + + const uint m_udpPort = 2221; + WiFiUDP m_udp; + + // Telnet IP server + const uint m_tcpPort = 23; + + WiFiServer m_tcpServer; +}; diff --git a/examples/Arduino/DotMatrix/Logger.cpp b/examples/Arduino/DotMatrix/Logger.cpp new file mode 100644 index 0000000..19ef36c --- /dev/null +++ b/examples/Arduino/DotMatrix/Logger.cpp @@ -0,0 +1,162 @@ +/** + * @file Logger.hpp + * @author Gustavo Campos (lgustavocampos@gmail.com) + * @brief General Logger facility for c++ + * @version 0.1 + * @date 2022-03-05 + * + * @copyright Copyright (c) 2022 + * + */ + +#include "Arduino.h" + +#include +#include +#include +#include + +#include "Logger.hpp" + +// initializing static list +std::set> LoggerStreamBuffer::loggerList = {}; + +LoggerStream logger (LogType::Debug); + +LoggerInterface::~LoggerInterface () +{} + +void LoggerInterface::flush (LogType logType, const std::string& strMessage) +{ + (void) logType; (void) strMessage; +} + +void LoggerInterface::init () +{ + +} + +LoggerStreamBuffer::LoggerStreamBuffer(LogType minimalLogType) : + m_msgLogType (minimalLogType), m_logType(minimalLogType),m_strMessage{} +{} + +LoggerStreamBuffer::~LoggerStreamBuffer() +{} + +std::streamsize LoggerStreamBuffer::FlushBuffer() +{ + std::streamsize length = m_strMessage.length (); + + if (m_msgLogType <= m_logType) + { + for (auto& loggerItem : loggerList) + { + loggerItem->flush (m_msgLogType, m_strMessage); + } + } + + m_strMessage = ""; + m_msgLogType = LogType::Debug; + + return length; +} + + const char* LoggerStreamBuffer::GetLogTypeName () + { + return LOG::GetLogTypeName (m_msgLogType); + } + + /** + * @brief streambuf virtual method to process a buffer of bytes + * + * @param streamBuffer The provided bytes buffer + * @param length The size of the bytes buffer + * + * @return std::streamsize The total utilized bytes from the buffer + */ +std::streamsize LoggerStreamBuffer::xsputn(const char_type* streamBuffer, std::streamsize length) +{ + m_strMessage.append (streamBuffer, length); + return length; +} + + /** + * @brief streambuf virtual method to process a single character + * + * @param character The single character to process + * + * @return int_type The processed character + * + * @note The type can only be defined once by each message. + * any other attempt will be discarded + */ +int LoggerStreamBuffer::overflow(int_type character) +{ + if (character == '\n') + { + FlushBuffer (); + } + else + { + m_strMessage.append ((char*) &character, 1); + } + + return 1; +} + +void LoggerStreamBuffer::SetMessageType (LogType logType) +{ + m_msgLogType = logType; +} + +void LoggerStreamBuffer::SetMinimalLogType (LogType logType) +{ + m_logType = logType; +} + +bool LoggerStreamBuffer::AddLogger (LoggerInterface* externalLogger) +{ + auto ret = loggerList.insert (std::unique_ptr (externalLogger)); + + if (ret.second) + { + externalLogger->init (); + } + + return true; //ret.second; +} + +bool LoggerStream::AddLogger (LoggerInterface* externalLogger) +{ + if (externalLogger == nullptr) + { + return false; + } + + return sbuffer.AddLogger (externalLogger); +} + +LoggerStream::LoggerStream (LogType logType) : sbuffer (logType) +{ + this->rdbuf (&sbuffer); +} + +void LoggerStream::SetMessageType (LogType logType) +{ + sbuffer.SetMessageType (logType); +} + +void LoggerStream::flush () +{ + sbuffer.FlushBuffer (); +} + +const char* LoggerStream::GetLogTypeName () +{ + return sbuffer.GetLogTypeName (); +} + +void LoggerStream::SetMinimalLogType (LogType logType) +{ + sbuffer.SetMinimalLogType (logType); +} \ No newline at end of file diff --git a/examples/Arduino/DotMatrix/Logger.hpp b/examples/Arduino/DotMatrix/Logger.hpp new file mode 100644 index 0000000..3c0cfe3 --- /dev/null +++ b/examples/Arduino/DotMatrix/Logger.hpp @@ -0,0 +1,202 @@ +/** + * @file Logger.hpp + * @author Gustavo Campos (lgustavocampos@gmail.com) + * @brief General Logger facility for c++ + * @version 0.1 + * @date 2022-03-05 + * + * @copyright Copyright (c) 2022 + * + */ + +#ifndef __LOGGER_HPP__ +#define __LOGGER_HPP__ + +#include +#include +#include +#include +#include +#include + +enum class LogType : uint8_t +{ + Emergency, + Alert, + Critical, + Error, + Warning, + Info, + Debug, +}; + +namespace LOG +{ + static const char* GetLogTypeName (LogType logType) + { + switch (logType) + { + case LogType::Emergency: return "Emergency"; + case LogType::Alert: return "Alert"; + case LogType::Critical: return "Critical"; + case LogType::Error: return "Error"; + case LogType::Warning: return "Warning"; + case LogType::Info: return "Info"; + case LogType::Debug: return "Debug"; + }; + + return "Undefined"; + } +} // namespace LOG + +class LoggerInterface +{ + public: + virtual ~LoggerInterface (); + + virtual void flush (LogType LogType, const std::string& strMessage); + virtual void init (); +}; + +class LoggerStreamBuffer : public std::streambuf +{ +public: + LoggerStreamBuffer () = delete; + LoggerStreamBuffer(LogType minimalLogType); + + ~LoggerStreamBuffer(); + + std::streamsize FlushBuffer(); + +protected: + + friend class LoggerStream; + + /** + * @brief streambuf virtual method to process a buffer of bytes + * + * @param streamBuffer The provided bytes buffer + * @param length The size of the bytes buffer + * + * @return std::streamsize The total utilized bytes from the buffer + */ + std::streamsize xsputn(const char_type* streamBuffer, std::streamsize length) override; + + /** + * @brief streambuf virtual method to process a single character + * + * @param character The single character to process + * + * @return int_type The processed character + * + * @note The type can only be defined once by each message. + * any other attempt will be discarded + */ + int_type overflow(int_type character) override; + + void SetMessageType (LogType logType); + + void SetMinimalLogType (LogType logType); + + const char* GetLogTypeName (); + + bool AddLogger (LoggerInterface* externalLogger); + +private: + LogType m_msgLogType; + LogType m_logType; + std::string m_strMessage; + + static std::set> loggerList; +}; + +class LoggerStream : public std::ostream +{ +public: + LoggerStream () = delete; + + LoggerStream (LogType logType = LogType::Debug); + + void SetMessageType (LogType logType); + void SetMinimalLogType (LogType logType); + + void flush (); + + const char* GetLogTypeName (); + + bool AddLogger (LoggerInterface* externalLogger); + +protected: +private: + LoggerStreamBuffer sbuffer; +}; + +extern LoggerStream logger; + +namespace LOG +{ + static std::ostream& EMERGENCY (std::ostream& os) + { + LoggerStream& log = static_cast(os); + + log.SetMessageType (LogType::Emergency); + + return os; + } + + static std::ostream& ALERT (std::ostream& os) + { + LoggerStream& log = static_cast(os); + + log.SetMessageType (LogType::Alert); + + return os; + } + + static std::ostream& CRITICAL (std::ostream& os) + { + LoggerStream& log = static_cast(os); + + log.SetMessageType (LogType::Critical); + + return os; + } + + static std::ostream& ERROR (std::ostream& os) + { + LoggerStream& log = static_cast(os); + + log.SetMessageType (LogType::Error); + + return os; + } + + static std::ostream& WARNING (std::ostream& os) + { + LoggerStream& log = static_cast(os); + + log.SetMessageType (LogType::Warning); + + return os; + } + + static std::ostream& INFO (std::ostream& os) + { + LoggerStream& log = static_cast(os); + + log.SetMessageType (LogType::Info); + + return os; + } + + static std::ostream& DEBUG (std::ostream& os) + { + LoggerStream& log = static_cast(os); + + log.SetMessageType (LogType::Debug); + + return os; + } + +} // namespace LOG +#endif \ No newline at end of file diff --git a/examples/Arduino/DotMatrix/SerialTerminal.cpp b/examples/Arduino/DotMatrix/SerialTerminal.cpp new file mode 100644 index 0000000..eddd514 --- /dev/null +++ b/examples/Arduino/DotMatrix/SerialTerminal.cpp @@ -0,0 +1,83 @@ +/// +/// @author GUSTAVO CAMPOS +/// @author GUSTAVO CAMPOS +/// @date 05/02/2020 +/// @version 0.1 +/// +/// @copyright (c) GUSTAVO CAMPOS, 2019 +/// @copyright Licence +/// +/* + MIT License + + Copyright (c) 2021 Gustavo Campos + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +*/ + +#include "atomicx.hpp" +#include "Arduino.h" +#include +#include + +#include "utils.hpp" +#include "TerminalInterface.hpp" + +#include "SerialTerminal.hpp" + +SerialTerminal::SerialTerminal(atomicx_time nNice) : TerminalInterface(nNice, Serial) +{ + void InitSerial(void); +} + +void SerialTerminal::PrintMOTD () +{ + client().println ("-------------------------------------"); + client().println ("Dotmatrix OS"); + client().println ("Welcome to Serial based terminal session."); + client().println ("-------------------------------------"); + client().flush(); +} + +bool SerialTerminal::IsConnected () +{ + return ((! Serial) == false); +} + +const char* SerialTerminal::GetName () +{ + return "SerialTerm"; +} + +void SerialLogger::init () +{ +} + +void SerialLogger::flush (LogType logType, const std::string& strMessage) +{ + if (Serial) + { + Serial.printf ("\r\e[K%-12u", millis ()); + Serial.print (" ["); + Serial.print (LOG::GetLogTypeName (logType)); + Serial.print ("] "); + Serial.println (strMessage.c_str ()); + Serial.flush (); + } +} \ No newline at end of file diff --git a/examples/Arduino/DotMatrix/SerialTerminal.hpp b/examples/Arduino/DotMatrix/SerialTerminal.hpp new file mode 100644 index 0000000..425b21f --- /dev/null +++ b/examples/Arduino/DotMatrix/SerialTerminal.hpp @@ -0,0 +1,68 @@ +/// +/// @author GUSTAVO CAMPOS +/// @author GUSTAVO CAMPOS +/// @date 05/02/2020 +/// @version 0.1 +/// +/// @copyright (c) GUSTAVO CAMPOS, 2019 +/// @copyright Licence +/// +/* + MIT License + + Copyright (c) 2021 Gustavo Campos + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +*/ + +#ifndef __SERIALTERMINAL_HPP__ +#define __SERIALTERMINAL_HPP__ + +#include "atomicx.hpp" +#include "Arduino.h" +#include +#include + +#include "utils.hpp" +#include "TerminalInterface.hpp" + +class SerialTerminal : public TerminalInterface +{ +public: + SerialTerminal() = delete; + SerialTerminal (atomicx_time nNice); + +protected: + void PrintMOTD() final; + + virtual const char* GetName () override; + + virtual bool IsConnected () override; +private: + +}; + +class SerialLogger : public LoggerInterface +{ +protected: + virtual void init () override; + virtual void flush (LogType logType, const std::string& strMessage) override; +}; + +#endif \ No newline at end of file diff --git a/examples/Arduino/DotMatrix/TelnetTerminal.cpp b/examples/Arduino/DotMatrix/TelnetTerminal.cpp new file mode 100644 index 0000000..66339c3 --- /dev/null +++ b/examples/Arduino/DotMatrix/TelnetTerminal.cpp @@ -0,0 +1,87 @@ +/// +/// @author GUSTAVO CAMPOS +/// @author GUSTAVO CAMPOS +/// @date 05/02/2020 +/// @version 0.1 +/// +/// @copyright (c) GUSTAVO CAMPOS, 2019 +/// @copyright Licence +/// +/* + MIT License + + Copyright (c) 2021 Gustavo Campos + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +*/ + +#include "atomicx.hpp" +#include "Arduino.h" +#include +#include + +#include "utils.hpp" +#include "TerminalInterface.hpp" + +#include "TelnetTerminal.hpp" + +TelnetTerminal::TelnetTerminal(atomicx_time nNice, WiFiServer& server) : TerminalInterface(nNice, (Stream&) m_telnetClient), m_telnetClient (server.available ()), pszName{} +{ + std::stringstream sstrName; + sstrName << m_telnetClient.remoteIP().toString ().c_str () << ":" << m_telnetClient.remotePort(); + + strncpy (pszName, sstrName.str ().c_str (), sizeof (pszName)-1); + + logger << LOG::INFO << "Telnet terminal on " << m_telnetClient.remoteIP ().toString ().c_str () << " started." << std::endl; +} + +void TelnetTerminal::PrintMOTD () +{ + client().println ("-------------------------------------"); + client().println ("Dotmatrix OS - Telnet Service over TCP"); + client().println ("Welcome to Serial based terminal session."); + client().printf ("Client: %s:%u\r\n", m_telnetClient.remoteIP(), m_telnetClient.remotePort ()); + client().println ("-------------------------------------"); + client().flush(); +} + +void TelnetTerminal::finish() noexcept +{ + if (IsConnected ()) + { + m_telnetClient.printf ("Closing connection. \r\n"); + + m_telnetClient.stop (); + } + + logger << LOG::INFO << "Telnet terminal " << m_telnetClient.remoteIP().toString ().c_str () << ", disconnecting." << std::endl; + + delete this; +} + +bool TelnetTerminal::IsConnected () +{ + return (m_telnetClient.connected ()); +} + +const char* TelnetTerminal::GetName () +{ + + return pszName; +} \ No newline at end of file diff --git a/examples/Arduino/DotMatrix/TelnetTerminal.hpp b/examples/Arduino/DotMatrix/TelnetTerminal.hpp new file mode 100644 index 0000000..4fc0638 --- /dev/null +++ b/examples/Arduino/DotMatrix/TelnetTerminal.hpp @@ -0,0 +1,69 @@ +/// +/// @author GUSTAVO CAMPOS +/// @author GUSTAVO CAMPOS +/// @date 05/02/2020 +/// @version 0.1 +/// +/// @copyright (c) GUSTAVO CAMPOS, 2019 +/// @copyright Licence +/// +/* + MIT License + + Copyright (c) 2021 Gustavo Campos + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +*/ + +#ifndef __TELNETTERMINAL_HPP__ +#define __TELNETTERMINAL_HPP__ + +#include "atomicx.hpp" +#include "Arduino.h" + +#include + +#include +#include +#include + +#include "utils.hpp" +#include "TerminalInterface.hpp" + +class TelnetTerminal : public TerminalInterface +{ +public: + TelnetTerminal() = delete; + TelnetTerminal (atomicx_time nNice, WiFiServer& server); + +protected: + void PrintMOTD() final; + + virtual void finish() noexcept override; + + virtual bool IsConnected () override; + + virtual const char* GetName () override; + +private: + WiFiClient m_telnetClient; + char pszName [20]; +}; + +#endif \ No newline at end of file diff --git a/examples/Arduino/DotMatrix/TerminalCommands.cpp b/examples/Arduino/DotMatrix/TerminalCommands.cpp new file mode 100644 index 0000000..a8a9bb0 --- /dev/null +++ b/examples/Arduino/DotMatrix/TerminalCommands.cpp @@ -0,0 +1,148 @@ +/// +/// @author GUSTAVO CAMPOS +/// @author GUSTAVO CAMPOS +/// @date 05/02/2020 +/// @version 0.1 +/// +/// @copyright (c) GUSTAVO CAMPOS, 2019 +/// @copyright Licence +/// +/* + MIT License + + Copyright (c) 2021 Gustavo Campos + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +*/ + +#include +#include +#include +#include "Arduino.h" +#include "atomicx.hpp" + +#include "utils.hpp" + +#include "TerminalCommands.hpp" + +// Methods implementations +commands::System::System () : CommandTerminalInterface ("system") +{ + return; +} + +const char* commands::System::GetCommandDescription () +{ + return "Print system information"; +} + +bool commands::System::Execute (Stream& client, const std::string& commandLine) +{ + thread::atomicx::GetCurrent()->Yield (10); + + client.println ("Message displaying ------------------"); + client.println (util::GetDisplayMessage().c_str ()); + client.println ("ESP8266 System ------------------"); + client.printf ("%-20s: [%u]\r\n", "Processor ID", system_get_chip_id ()); + client.printf ("%-20s: [%s]\r\n", "SDK Version", system_get_sdk_version ()); + client.printf ("%-20s: [%uMhz]\r\n", "CPU Freequency", system_get_cpu_freq ()); + client.printf ("%-20s: [%u Bytes]\r\n", "Memory", system_get_free_heap_size ()); + + thread::atomicx::GetCurrent()->Yield (10); + + if (WiFi.status () != WL_NO_SHIELD) + { + client.println ("-[Connection]----------------------"); + client.printf ("%-20s: (%u)\r\n", "Status", WiFi.status ()); + client.printf ("%-20s: [%s]\r\n", "Mac Address", WiFi.macAddress ().c_str ()); + + thread::atomicx::GetCurrent()->Yield (10); + + if (WiFi.status () == WL_CONNECTED) + { + client.printf ("%-20s: [%s]\r\n", "SSID", WiFi.SSID ().c_str ()); + client.printf ("%-20s: [%d dBm]\r\n", "Signal", WiFi.RSSI ()); + client.printf ("%-20s: [%u Mhz]\r\n", "Channel", WiFi.channel ()); + client.printf ("%-20s: [%s]\r\n", "IPAddress", WiFi.localIP ().toString ().c_str ()); + client.printf ("%-20s: [%s]\r\n", "Net Mask", WiFi.subnetMask ().toString ().c_str ()); + client.printf ("%-20s: [%s]\r\n", "Gateway", WiFi.gatewayIP ().toString ().c_str ()); + client.printf ("%-20s: [%s]\r\n", "DNS", WiFi.dnsIP ().toString ().c_str ()); + + thread::atomicx::GetCurrent()->Yield (10); + + } + else + { + client.println ("Not connected to WiFi..."); + } + } + + thread::atomicx::GetCurrent()->Yield (10); + + client.println ("-[Process]----------------------"); + + ListAllThreads (client); + + return true; +} + +commands::Display::Display () : CommandTerminalInterface ("display") +{ + util::SetDisplayMessage ("AtomicX terminal command is ready."); +} + +const char* commands::Display::GetCommandDescription () +{ + return "Define a new custom message to be displied."; +} + +bool commands::Display::Execute (Stream& client, const std::string& commandLine) +{ + std::string strAttribute = ""; + + uint8_t ret = 0; + + if (! ParseOption (commandLine, 1, strAttribute) || strAttribute.length () == 0) + { + client.print ((short int) strAttribute.length ()); + client.print (": Error, no attribute provided, please "); + client.println (GetCommandDescription ()); + client.flush (); + + return false; + } + + client.printf ("Setting up message: [%s]\r\n", strAttribute.c_str ()); + client.flush (); + + client.print ("\e[K Wait, notifying thread..\r"); Serial.flush (); + + if (util::SetDisplayMessage (strAttribute)) + { + Serial.println ("\e[K done."); + } + else + { + Serial.println ("\e[K ERROR, thread is not responding., message was not set"); + } + + Serial.flush (); + + return true; +} \ No newline at end of file diff --git a/examples/Arduino/DotMatrix/TerminalCommands.hpp b/examples/Arduino/DotMatrix/TerminalCommands.hpp new file mode 100644 index 0000000..85f8416 --- /dev/null +++ b/examples/Arduino/DotMatrix/TerminalCommands.hpp @@ -0,0 +1,72 @@ +/// +/// @author GUSTAVO CAMPOS +/// @author GUSTAVO CAMPOS +/// @date 05/02/2020 +/// @version 0.1 +/// +/// @copyright (c) GUSTAVO CAMPOS, 2019 +/// @copyright Licence +/// +/* + MIT License + + Copyright (c) 2021 Gustavo Campos + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +*/ + +#ifndef __TERMINALCOMMAND_HPP__ +#define __TERMINALCOMMAND_HPP__ + +#include "Arduino.h" +#include +#include +#include + +#include "utils.hpp" +#include "TerminalInterface.hpp" +#include "TextScroller.hpp" + +extern TextScroller Matrix; + +namespace commands +{ + class System : public CommandTerminalInterface + { + public: + System(); + + protected: + const char* GetCommandDescription() final; + + bool Execute(Stream& client, const std::string& commandLine); + }; + + class Display : public CommandTerminalInterface + { + public: + Display (); + + protected: + const char* GetCommandDescription() final; + + bool Execute(Stream& client, const std::string& commandLine); + }; +} +#endif \ No newline at end of file diff --git a/examples/Arduino/DotMatrix/TerminalInterface.cpp b/examples/Arduino/DotMatrix/TerminalInterface.cpp new file mode 100644 index 0000000..c5db8d6 --- /dev/null +++ b/examples/Arduino/DotMatrix/TerminalInterface.cpp @@ -0,0 +1,336 @@ +/// +/// @author GUSTAVO CAMPOS +/// @author GUSTAVO CAMPOS +/// @date 05/02/2020 +/// @version 0.1 +/// +/// @copyright (c) GUSTAVO CAMPOS, 2019 +/// @copyright Licence +/// +/* + MIT License + + Copyright (c) 2021 Gustavo Campos + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +*/ + +#include "TextScroller.hpp" +#include "TerminalCommands.hpp" +#include "TerminalInterface.hpp" + +// Static initializations +CommandTerminalMap TerminalInterface::m_mapCommands={}; + +// Default initializations for commands +commands::Help l_helpCommandInstance; +commands::System l_systemCommand; +commands::Display l_displayCommand; + +extern TextScroller Matrix; + +// Helpper functions +uint8_t ParseOption (const std::string& commandLine, uint8_t nCommandIndex, std::string& returnText, bool countOnly) +{ + uint8_t nCommandOffSet = 0; + + nCommandIndex++; + + enum class state : uint8_t + { + NoText, + Word, + Text, + } currentState; + + currentState = state::NoText; + returnText = ""; + + bool boolScape = false; + + std::string strBuffer=""; + + for (char chChar : commandLine) + { + thread::atomicx::GetCurrent()->Yield (1); + + if (currentState == state::NoText) + { + if (chChar == '"' || chChar == '\'') + { + // set Text state + currentState = state::Text; + nCommandOffSet++; + continue; + } + else if (chChar != ' ') + { + // Set Word state + currentState = state::Word; + nCommandOffSet++; + } + } + + if (currentState != state::NoText) + { + if (boolScape == false) + { + if (currentState == state::Text && chChar == '"' || chChar == '\'') + { + currentState = state::NoText; + } + else if (currentState == state::Word && chChar == ' ') + { + currentState = state::NoText; + } + if (chChar == '\\') + { + boolScape = true; + strBuffer.pop_back (); + continue; + } + } + else if (!isdigit (chChar)) + { + boolScape = false; + } + + if (currentState == state::NoText) + { + if (countOnly == false && nCommandIndex == nCommandOffSet) + { + break; + } + + returnText = ""; + } + else + { + if (countOnly == false) + { + if (boolScape == true and isdigit (chChar)) + { + strBuffer += chChar; + } + else + { + // To add special char \000\ 00 = number only + if (strBuffer.length () > 0) + { + returnText += (static_cast (atoi (strBuffer.c_str ()))); + strBuffer = ""; + } + else + { + returnText += (static_cast(chChar)); /* code */ + } + } + } + } + } + } + + if (nCommandIndex != nCommandOffSet) + { + returnText = ""; + } + + return nCommandOffSet; +} + +// Default methods implementations + +TerminalInterface::TerminalInterface(atomicx_time nNice, Stream& client) : atomicx(500, 50), m_client(client) +{ + SetNice (nNice); +} + +CommandTerminalMap& TerminalInterface::GetMapCommands () +{ + return TerminalInterface::m_mapCommands; +} + +inline int TerminalInterface::WaitForClientData (int &nChars) +{ + nChars = 0; + + for (nChars = 0;IsConnected () && (nChars = m_client.available ()) == 0; Yield (2)); + + return nChars; +} + +bool TerminalInterface::ReadCommandLine (std::string& readCommand) +{ + uint8_t chChar = 0; + int nChars = 0; + + while ((nChars || (WaitForClientData (nChars)) > 0) && IsConnected ()) + { + chChar = m_client.read (); + + if (chChar == TERMINAL_BS && readCommand.length () > 0) + { + m_client.print ((char)TERMINAL_BS); + m_client.print (' '); + m_client.print ((char)TERMINAL_BS); + + //readCommand.remove (readCommand.length ()-1); + readCommand.pop_back (); + } + else if (chChar == '\r') + { + m_client.println (F("")); + break; + } + else if (chChar >= 32 && chChar < 127) + { + m_client.print ((char)chChar); + m_client.flush (); + m_client.flush (); + readCommand += ((char) chChar); + } + + nChars--; + } + + if (IsConnected () == false) + { + readCommand=""; + return false; + } + + util::trim(readCommand); + + return true; +} + +void TerminalInterface::run() +{ + std::string strCommandLine = ""; + std::string strCommand = ""; + std::string strTerminal = "Terminal"; + + Yield (1000); + + PrintMOTD (); + + for (;IsConnected ();) + { + strTerminal = ""; + strTerminal += "\r\eK Terminal>"; + + m_client.print (strTerminal.c_str ()); + m_client.flush (); + + if (ReadCommandLine (strCommandLine) && strCommandLine.length ()) + { + if (ParseOption (strCommandLine, 0, strCommand)) + { + if (strCommand == "exit" ) + { + break; + } + else if (m_mapCommands.find (strCommand) != m_mapCommands.end()) + { + m_mapCommands [strCommand]->Execute(m_client, strCommandLine); + } + else + { + m_client.print ("Received Command: "); + m_client.println (strCommand.c_str ()); + m_client.println ("Command not found, here is the list of avaliable commands:"); + m_mapCommands ["help"]->Execute(m_client, strCommandLine); + } + + strCommandLine = ""; strCommand = ""; + } + } + } +} + +void TerminalInterface::StackOverflowHandler(void) +{ + PrintStackOverflow(); +} + +const char* TerminalInterface::GetName () +{ + return "TerminalInterface"; +} + +// CommandTerminalInterface implementations +CommandTerminalInterface::CommandTerminalInterface(std::string strCommandName) : strCommandName (strCommandName) +{ + GetMapCommands().insert(std::make_pair(strCommandName, this)); +} + +CommandTerminalInterface::~CommandTerminalInterface() +{ + GetMapCommands().erase ((const std::string) strCommandName); +} + +const std::string& CommandTerminalInterface::GetCommandName() +{ + return strCommandName; +} + +CommandTerminalMap& CommandTerminalInterface::GetMapCommands() +{ + return TerminalInterface::GetMapCommands (); +} + +// Default command implementation +commands::Help::Help () : CommandTerminalInterface ("help") +{ + return; +} + +const char* commands::Help::GetCommandDescription() +{ + return "List this help text"; +} + +bool commands::Help::Execute(Stream& client, const std::string& commandLine) +{ + CommandTerminalMap& map = GetMapCommands (); + + client.println ("Help"); + client.println ("--------------------------------------"); + + for (auto& item : map) + { + client.printf ("%-15s %s\r\n", item.first.c_str(), item.second->GetCommandDescription ()); + } + + return true; +} + +void TerminalInterface::PrintMOTD () +{ + m_client.println ("-------------------------------------"); + m_client.println ("Dotmatrix OS"); + m_client.println ("Welcome to the terminal session."); + m_client.println ("-------------------------------------"); + m_client.flush(); +} + +Stream& TerminalInterface::client (void) +{ + return m_client; +} \ No newline at end of file diff --git a/examples/Arduino/DotMatrix/TerminalInterface.hpp b/examples/Arduino/DotMatrix/TerminalInterface.hpp new file mode 100644 index 0000000..3b2b9b1 --- /dev/null +++ b/examples/Arduino/DotMatrix/TerminalInterface.hpp @@ -0,0 +1,148 @@ +/// +/// @author GUSTAVO CAMPOS +/// @author GUSTAVO CAMPOS +/// @date 05/02/2020 +/// @version 0.1 +/// +/// @copyright (c) GUSTAVO CAMPOS, 2019 +/// @copyright Licence +/// +/* + MIT License + + Copyright (c) 2021 Gustavo Campos + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +*/ + +#ifndef __TERMINAL_HPP__ +#define __TERMINAL_HPP__ + +// Designed for NodeMCU ESP8266 +//################# DISPLAY CONNECTIONS ################ +// LED Matrix Pin -> ESP8266 Pin +// Vcc -> 3v (3V on NodeMCU 3V3 on WEMOS) +// Gnd -> Gnd (G on NodeMCU) +// DIN -> D7 (Same Pin for WEMOS) +// CS -> D4 (Same Pin for WEMOS) +// CLK -> D5 (Same Pin for WEMOS) + +#include "atomicx.hpp" +#include "Arduino.h" +#include +#include +#include + +#include "utils.hpp" + +class TerminalInterface; +class CommandTerminalInterface; + +using CommandTerminalMap = std::map; + +#define TERMINAL_BS 8 + +/** + * @brief Parse a command line option and return the command attribute data for a index or how much data present + * + * @param commandLine Command line text + * @param nCommandIndex index fo the desired command attribute + * @param returnText command attribute data + * @param countOnly if yes will return how much command attributes in the command line + * + * @return uint8_t the index of what was reported or the number or elements if countOnly = true + */ +uint8_t ParseOption (const std::string& commandLine, uint8_t nCommandIndex, std::string& returnText, bool countOnly=false); + +/** + * CommandTerminal Interdace class + */ +class CommandTerminalInterface +{ +public: + virtual const char* GetCommandDescription() = 0; + +protected: + + friend class TerminalInterface; + + CommandTerminalInterface() = delete; + CommandTerminalInterface(std::string strCommandNme); + + virtual ~CommandTerminalInterface(); + + const std::string& GetCommandName(); + + CommandTerminalMap& GetMapCommands(); + + virtual bool Execute(Stream& client, const std::string& commandLine) = 0; + +private: + const std::string strCommandName; +}; + +/** + * Terminal interface class + */ +class TerminalInterface : public thread::atomicx +{ +public: + TerminalInterface(atomicx_time nNice, Stream& client); + Stream& client (void); + +protected: + virtual void PrintMOTD (); + + friend class CommandTerminalInterface; + + static CommandTerminalMap& GetMapCommands (); + + int WaitForClientData (int& nChars); + + bool ReadCommandLine (std::string& readCommand); + + void run() noexcept final; + + void StackOverflowHandler(void) noexcept override; + + const char* GetName (); + + virtual bool IsConnected () = 0; + +private: + static CommandTerminalMap m_mapCommands; + Stream& m_client; +}; + + +namespace commands +{ + class Help : public CommandTerminalInterface + { + public: + Help(); + + protected: + const char* GetCommandDescription() final; + + bool Execute(Stream& client, const std::string& commandLine); + }; + +} +#endif \ No newline at end of file diff --git a/examples/Arduino/DotMatrix/TextScroller.cpp b/examples/Arduino/DotMatrix/TextScroller.cpp new file mode 100644 index 0000000..bb76da3 --- /dev/null +++ b/examples/Arduino/DotMatrix/TextScroller.cpp @@ -0,0 +1,206 @@ + +/// +/// @author GUSTAVO CAMPOS +/// @author GUSTAVO CAMPOS +/// @date 05/02/2020 +/// @version 0.1 +/// +/// @copyright (c) GUSTAVO CAMPOS, 2019 +/// @copyright Licence +/// +/* + MIT License + + Copyright (c) 2021 Gustavo Campos + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +*/ + +#include + +// Designed for NodeMCU ESP8266 +//################# DISPLAY CONNECTIONS ################ +// LED Matrix Pin -> ESP8266 Pin +// Vcc -> 3v (3V on NodeMCU 3V3 on WEMOS) +// Gnd -> Gnd (G on NodeMCU) +// DIN -> D7 (Same Pin for WEMOS) +// CS -> D4 (Same Pin for WEMOS) +// CLK -> D5 (Same Pin for WEMOS) + +#include "atomicx.hpp" +#include "Arduino.h" +#include "user_interface.h" +#include +#include +#include + +#include "utils.hpp" +#include "TextScroller.hpp" + +uint64_t TextScroller::getLetter (int nIndex, const char* pszMessage, uint16_t nMessageLen) +{ + int nCharacter = nIndex > nMessageLen || nIndex < 0 ? ' ' : pszMessage[nIndex]; + + return getImage (nCharacter - ' '); +} + +uint64_t TextScroller::getImage (int nIndex) +{ + uint64_t nBuffer = 0xAA; + + nIndex = nIndex > byteImagesLen || nIndex < 0 ? 0 : nIndex; + + memcpy_P (&nBuffer, byteImages + nIndex, sizeof (uint64_t)); + + return nBuffer; +} + +void TextScroller::printScrollBytes (uint16_t nDigit, const uint64_t charLeft, const uint64_t charRight, uint8_t nOffset) +{ + int i = 0; + + for (i = 0; i < 8; i++) + { + printRow (nDigit, i, (((uint8_t*)&charLeft)[i] << (8 - nOffset) | ((uint8_t*)&charRight)[i] >> nOffset)); + } +} + + +void TextScroller::printRow (uint16_t nDigit, uint8_t nRowIndex, uint8_t nRow) +{ + lc.setRow (nDigit, 7 - nRowIndex, nRow); +} + +void TextScroller::run(void) +{ + begin(); + + for (;;) + { + Yield (); + + if (show (sstrMatrixText.str().c_str(), sstrMatrixText.str().length()) == false) + { + sysCmds = SysCommands::Matrix_Ready; + + if (! SyncNotify (sysCmds, 1, 1000)) + { + Serial.printf (">>> ERROR, failed to notify System... (%zp)\r\n", &sysCmds); + Serial.flush (); + } + } + } + + return; +} + +void TextScroller::StackOverflowHandler(void) +{ + PrintStackOverflow (); +} + +char* TextScroller::GetName (void) +{ + return "Matrix"; +} + +bool TextScroller::begin() +{ + // Intentionally delaying to give time to the SPI to start + delay (1000); + + uint8_t nCounter=nNumberDigits; + + if (nCounter) do + { + lc.shutdown (nCounter, false); // The MAX72XX is in power-saving mode on startup + lc.setIntensity (nCounter, 0); // Set the brightness to maximum value + lc.clearDisplay (nCounter); // and clear the display + + } while (nCounter--); + + return true; +} + +TextScroller::TextScroller (atomicx_time nNice) : atomicx(250, 50), nOffset (0), nSpeed (2), lc(LedControl (DIN, CLK, CS, MAX_LED_MATRIX)) +{ + nNumberDigits = MAX_LED_MATRIX; + nIndex = (nSpeed == 0) ? nNumberDigits * (-1) : 0; + + SetNice (nNice); + + sstrMatrixText.str("Welcome to AtomicX DotMatrix WiFi server."); +} + +void TextScroller::SetSpeed(int nSpeed) +{ + this->nSpeed = nSpeed; +} + +bool TextScroller::show (const char* pszMessage, const uint16_t nMessageLen) +{ + uint8_t nCount; + + if (nSpeed > 0 && nSpeed % 8 == 0) nSpeed++; + + if (nSpeed > 0) do + { + if (nOffset >= 7) + { + nIndex = nIndex + 1 > (int)nMessageLen ? (int)nNumberDigits * (-1) : nIndex + 1; + nOffset = 0; + + if ((int)nNumberDigits * (-1) == nIndex) return false; + } + else + { + nOffset = (int)nOffset + (nSpeed % 8); + } + } while (nOffset >= 8); + + + for (nCount = 0; nCount < nNumberDigits; nCount++) + { + printScrollBytes ( + nCount, + getLetter (nIndex + 1, pszMessage, nMessageLen), + getLetter (nIndex, pszMessage, nMessageLen), + (uint8_t)nOffset); + + nIndex = (int)nIndex + 1; + + Yield (); + } + + nIndex = (int)nIndex - (nCount - (nSpeed / 8)); + + return true; +} + +std::stringstream& TextScroller::operator= (const std::string& strValue) +{ + sstrMatrixText.str(strValue); + + return sstrMatrixText; +} + +std::stringstream& TextScroller::operator() () +{ + return sstrMatrixText; +} \ No newline at end of file diff --git a/examples/Arduino/DotMatrix/TextScroller.hpp b/examples/Arduino/DotMatrix/TextScroller.hpp new file mode 100644 index 0000000..52b9e71 --- /dev/null +++ b/examples/Arduino/DotMatrix/TextScroller.hpp @@ -0,0 +1,152 @@ +/// +/// @author GUSTAVO CAMPOS +/// @author GUSTAVO CAMPOS +/// @date 05/02/2020 +/// @version 0.1 +/// +/// @copyright (c) GUSTAVO CAMPOS, 2019 +/// @copyright Licence +/// +/* + MIT License + + Copyright (c) 2021 Gustavo Campos + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +*/ + +#ifndef __TEXTSCROLLER_HPP__ +#define __TEXTSCROLLER_HPP__ + +#include + +// Designed for NodeMCU ESP8266 +//################# DISPLAY CONNECTIONS ################ +// LED Matrix Pin -> ESP8266 Pin +// Vcc -> 3v (3V on NodeMCU 3V3 on WEMOS) +// Gnd -> Gnd (G on NodeMCU) +// DIN -> D7 (Same Pin for WEMOS) +// CS -> D4 (Same Pin for WEMOS) +// CLK -> D5 (Same Pin for WEMOS) + +#include "atomicx.hpp" +#include "Arduino.h" +#include "user_interface.h" +#include +#include +#include +#include + +#include "utils.hpp" + +enum TextScroller_Constants +{ + TEXTSCROLLER_NOTIFY_NEW_TEXT = 10 +}; + +// const data + +static const uint64_t byteImages[] PROGMEM = { + 0x0000000000000000, 0x00180018183c3c18, 0x0000000012246c6c, 0x0036367f367f3636, 0x000c1f301e033e0c, 0x0063660c18336300, 0x006e333b6e1c361c, + 0x0000000000030606, 0x00180c0606060c18, 0x00060c1818180c06, 0x0000663cff3c6600, 0x00000c0c3f0c0c00, 0x060c0c0000000000, 0x000000003f000000, + 0x000c0c0000000000, 0x000103060c183060, 0x003e676f7b73633e, 0x003f0c0c0c0c0e0c, 0x003f33061c30331e, 0x001e33301c30331e, 0x0078307f33363c38, + 0x001e3330301f033f, 0x001e33331f03061c, 0x000c0c0c1830333f, 0x001e33331e33331e, 0x000e18303e33331e, 0x000c0c00000c0c00, 0x060c0c00000c0c00, + 0x00180c0603060c18, 0x00003f00003f0000, 0x00060c1830180c06, 0x000c000c1830331e, 0x001e037b7b7b633e, 0x6666667e66663c00, 0x3e66663e66663e00, + 0x3c66060606663c00, 0x3e66666666663e00, 0x7e06063e06067e00, 0x0606063e06067e00, 0x3c66760606663c00, 0x6666667e66666600, 0x3c18181818183c00, + 0x1c36363030307800, 0x66361e0e1e366600, 0x7e06060606060600, 0xc6c6c6d6feeec600, 0xc6c6e6f6decec600, 0x3c66666666663c00, 0x06063e6666663e00, + 0x603c766666663c00, 0x66361e3e66663e00, 0x3c66603c06663c00, 0x18181818185a7e00, 0x7c66666666666600, 0x183c666666666600, 0xc6eefed6c6c6c600, + 0xc6c66c386cc6c600, 0x1818183c66666600, 0x7e060c1830607e00, 0x001e06060606061e, 0x00406030180c0603, 0x001e18181818181e, 0x0000000063361c08, + 0x003f000000000000, 0x0000000000180c06, 0x7c667c603c000000, 0x3e66663e06060600, 0x3c6606663c000000, 0x7c66667c60606000, 0x3c067e663c000000, + 0x0c0c3e0c0c6c3800, 0x3c607c66667c0000, 0x6666663e06060600, 0x3c18181800180000, 0x1c36363030003000, 0x66361e3666060600, 0x1818181818181800, + 0xd6d6feeec6000000, 0x6666667e3e000000, 0x3c6666663c000000, 0x06063e66663e0000, 0xf0b03c36363c0000, 0x060666663e000000, 0x3e603c067c000000, + 0x1818187e18180000, 0x7c66666666000000, 0x183c666600000000, 0x7cd6d6d6c6000000, 0x663c183c66000000, 0x3c607c6666000000, 0x3c0c18303c000000, + 0x00380c0c070c0c38, 0x0c0c0c0c0c0c0c0c, 0x00070c0c380c0c07, 0x0000000000003b6e, 0x5554545050404000, 0x3f21212121212121, 0x3f212d2121212121, + 0x3f212d212d212121, 0x3f212d212d212d21, 0x3f212d2d2d212121, 0x3f212d2d2d2d2d2d, 0x00040a1120408000, 0x081c3e7f1c1c1c1c, 0x0010307fff7f3010, + 0x1c1c1c1c7f3e1c08, 0x00080cfefffe0c08, 0x40e040181e0f0f07, 0x939398cc6730180f, 0xffa9a9b7d9eff1ff, 0x1800ff7a3a3a3c18, 0x3c428199b985423c, + 0x18423ca5a53c4218, 0x0000ff81864c3800, 0x1428ff81864c3800, 0x0000ff81864f3e05, 0x0000ff81864f3e05, 0x18141010fe7c3800, 0x08081c1c3e363e1c, + 0x00aa000000000000, 0x000a080000000000, 0x002a282000000000, 0x00aaa8a0840a1000, 0x3c46e7e7e3ff663c, 0x0082442810284482, 0x003844aa92aa4438, + 0x003864f2ba9e4c38, 0xff8199a5c3422418, 0x7e3cdbc383343624, 0xff8199a5c3ff0000, 0x3c66c39999db5a18, 0xff000001010000ff, 0xff000003030000ff, + 0xff000006060000ff, 0xff00000c0c0000ff, 0xff000018180000ff, 0xff000030300000ff, 0xff000060600000ff, 0xff0000c0c00000ff, 0xff000080800000ff}; + +const int byteImagesLen = sizeof (byteImages) / 8; + +/* + MATRIX TEXT SCROLL --------------------------------------------------- +*/ + +static const uint8_t MAX_LED_MATRIX=8; + +static const int DIN = D4; // MISO - NodeMCU - D4 (TXD1) +static const int CS = D7; // MOSI - NodeMCU - D7 (HMOSI) +static const int CLK = D5; // SS - NodeMCU - D5 (HSCLK) + +class TextScroller : public thread::atomicx +{ +public: + template std::stringstream& operator<< (T&& value) + { + sstrMatrixText << value; + + return sstrMatrixText; + } + + std::stringstream& operator = (const std::string& strValue); + std::stringstream& operator() (); + + TextScroller (atomicx_time nNice); + + TextScroller () = delete; + + void SetSpeed(int nSpeed); + + bool show (const char* pszMessage, const uint16_t nMessageLen); + +protected: + + void printRow (uint16_t nDigit, uint8_t nRowIndex, uint8_t nRow); + + void run(void) noexcept override; + + void StackOverflowHandler(void) noexcept final; + + char* GetName (void); + + bool begin(); + +private: + + uint64_t getLetter (int nIndex, const char* pszMessage, uint16_t nMessageLen); + + uint64_t getImage (int nIndex); + + void printScrollBytes (uint16_t nDigit, const uint64_t charLeft, const uint64_t charRight, uint8_t nOffset); + + uint8_t m_stack [250]={}; + + int nNumberDigits = MAX_LED_MATRIX; + uint8_t nOffset = 0; + int nIndex = nNumberDigits * (-1); + + std::stringstream sstrMatrixText; + uint8_t nSpeed; + + LedControl lc; +}; + +#endif // __TEXTSCROLLER_H__ \ No newline at end of file diff --git a/examples/Arduino/pubsublock/atomicx.cpp b/examples/Arduino/DotMatrix/atomicx.cpp similarity index 100% rename from examples/Arduino/pubsublock/atomicx.cpp rename to examples/Arduino/DotMatrix/atomicx.cpp diff --git a/examples/Arduino/pubsublock/atomicx.hpp b/examples/Arduino/DotMatrix/atomicx.hpp similarity index 100% rename from examples/Arduino/pubsublock/atomicx.hpp rename to examples/Arduino/DotMatrix/atomicx.hpp diff --git a/examples/Arduino/DotMatrix/broadcast.sh b/examples/Arduino/DotMatrix/broadcast.sh new file mode 100755 index 0000000..aa62741 --- /dev/null +++ b/examples/Arduino/DotMatrix/broadcast.sh @@ -0,0 +1 @@ +socat - UDP-DATAGRAM:192.168.1.255:2221,broadcast diff --git a/examples/Arduino/DotMatrix/send.py b/examples/Arduino/DotMatrix/send.py new file mode 100644 index 0000000..3c15d90 --- /dev/null +++ b/examples/Arduino/DotMatrix/send.py @@ -0,0 +1,22 @@ +# Send UDP broadcast packets + +MYPORT = 2221 + +from datetime import datetime +import sys, time +from socket import * + +s = socket(AF_INET, SOCK_DGRAM) +s.bind(('', 0)) +s.setsockopt(SOL_SOCKET, SO_BROADCAST, 1) + +while 1: + data = datetime.now().strftime("%d/%m/%y") + s.sendto(data, ('192.168.1.255', MYPORT)) + time.sleep(5) + data = datetime.now().strftime("%H:%M") + s.sendto(data, ('192.168.1.255', MYPORT)) + time.sleep(5) + + + diff --git a/examples/Arduino/DotMatrix/utils.cpp b/examples/Arduino/DotMatrix/utils.cpp new file mode 100644 index 0000000..ea3c65e --- /dev/null +++ b/examples/Arduino/DotMatrix/utils.cpp @@ -0,0 +1,157 @@ + +#include + +#include "Arduino.h" +#include "TextScroller.hpp" +#include "utils.hpp" + +// Global values + +TermCommands termCmds = TermCommands::none; +SysCommands sysCmds = SysCommands::none; + +// Global functions +void ListAllThreads(Stream& client) +{ + size_t nCount=0; + + client.flush(); + + client.println (F("\e[K[THREAD]-----------------------------------------------")); + + client.printf ("\e[K>>> Free RAM: %ub\r\n", system_get_free_heap_size ()); + client.printf ("\e[KAtomicX context size: %zub\r\n", sizeof (thread::atomicx)); + + client.println ("\e[K---------------------------------------------------------"); + + for (auto& th : *(thread::atomicx::GetCurrent())) + { + thread::atomicx::GetCurrent()->Yield (10); + + client.printf ("\eK%c%-3u %-8zu '%-20s' nc:%-6u stk:(%6zub/%6zub) sts:(%-3u,%-3u) last: %ums\r\n", + thread::atomicx::GetCurrent() == &th ? '* ' : ' ', ++nCount, + (size_t) th.GetID(), th.GetName(), + th.GetNice(), th.GetUsedStackSize(), th.GetStackSize(), + th.GetStatus(), th.GetSubStatus(), + th.GetLastUserExecTime() + ); + } + + client.println ("\e[K---------------------------------------------------------"); + + client.flush(); +} + +extern std::string strSystemNextMessage; +extern TextScroller Matrix; + +// Namespaced functions and attributes +namespace util +{ + void InitSerial(void) + { + Serial.begin (115200); + + if (! Serial) + { + + // Wait serial to proper start. + while (! Serial); + } + } + + // trim from start (in place) + void ltrim(std::string &s) + { + s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](unsigned char ch) { + return !std::isspace(ch); + })); + } + + // trim from end (in place) + void rtrim(std::string &s) + { + s.erase(std::find_if(s.rbegin(), s.rend(), [](unsigned char ch) { + return !std::isspace(ch); + }).base(), s.end()); + } + + // trim from both ends (in place) + void trim(std::string &s) + { + ltrim(s); + rtrim(s); + } + + bool SetDisplayMessage (const std::string& strMessage, bool wait) + { + int nRet = 0; + + strSystemNextMessage = strMessage; + + /** + * @brief It will wait up to 5 minutes in line to be notified + */ + if ((! wait || ! thread::atomicx::IsKernelRunning ()) || thread::atomicx::GetCurrent()->SyncNotify (Matrix, TEXTSCROLLER_NOTIFY_NEW_TEXT, 300000)) + { + nRet = true; + } + + return nRet; + } + + const std::string GetDisplayMessage (void) + { + return Matrix().str(); + } + +} + +namespace vt100 +{ + void SetLocation (uint16_t nY, uint16_t nX) + { + Serial.print (F("\033[")); + Serial.print (nY); + Serial.print (F(";")); + Serial.print (nX); + Serial.print (F("H")); + Serial.flush (); + } + + // works with 256 colors + void SetColor (const uint8_t nFgColor, const uint8_t nBgColor) + { + Serial.print (F("\033[")); + Serial.print (nFgColor + 30); + Serial.print (F(";")); + Serial.print (nBgColor + 40); + Serial.print (F("m")); + Serial.flush (); + } + + void ResetColor () + { + Serial.print (F("\033[0m")); Serial.flush (); + } + + void HideCursor () + { + Serial.print (F("\033[?25l")); Serial.flush (); + } + + void ShowCursor () + { + Serial.print (F("\033[?25h")); Serial.flush (); + } + + void ClearConsole () + { + Serial.print (F("\033[2J")); Serial.flush (); + } + + void ReverseColor () + { + Serial.print (F("\033[7m")); Serial.flush (); + } +} diff --git a/examples/Arduino/DotMatrix/utils.hpp b/examples/Arduino/DotMatrix/utils.hpp new file mode 100644 index 0000000..922535e --- /dev/null +++ b/examples/Arduino/DotMatrix/utils.hpp @@ -0,0 +1,96 @@ + +#ifndef __UTILS_HPP__ +#define __UTILS_HPP__ + +#include +#include "atomicx.hpp" +#include "Arduino.h" + +#include + +#include "Logger.hpp" + +extern LoggerStream logger; + +constexpr std::size_t operator "" _z ( unsigned long long n ) + { return n; } + + +// global functions and attributes + +#define MAX(x, y) (x > y ? x : y) +#define MIN(x, y) (x < y ? x : y) + +#define PrintStackOverflow() \ + Serial.print (__FUNCTION__); \ + Serial.print ("["); \ + Serial.print (GetName ()); \ + Serial.print (F("/")); \ + Serial.print ((size_t) this); \ + Serial.print ("]: StackSize: ["); \ + Serial.print (GetStackSize ()); \ + Serial.print ("]. Stack used ["); \ + Serial.print (GetUsedStackSize()); \ + Serial.println (F("]")); \ + Serial.flush(); \ + +enum class TermCommands +{ + none=0, + ThermalCam_Update, + ThreadList_Update +}; + +extern TermCommands termCmds; + +enum class SysCommands +{ + none = 0, + Matrix_Ready=10 +}; + +extern SysCommands sysCmds; + +// Global functions +void ListAllThreads(Stream& client); + +// Namespaced functions and attributes +namespace util +{ + constexpr size_t GetStackSize(size_t sizeRef) + { + return sizeRef * sizeof (size_t); + } + + void InitSerial(void); + + void ltrim(std::string &s); + + void rtrim(std::string &s); + + void trim(std::string &s); + + bool SetDisplayMessage (const std::string& strMessage, bool wait = true); + + const std::string GetDisplayMessage (void); +} + +namespace vt100 +{ + void SetLocation (uint16_t nY, uint16_t nX); + + // works with 256 colors + void SetColor (const uint8_t nFgColor, const uint8_t nBgColor); + + void ResetColor (); + + void HideCursor (); + + void ShowCursor (); + + void ClearConsole (); + + void ReverseColor (); +} + +#endif \ No newline at end of file diff --git a/examples/Arduino/README.MD b/examples/Arduino/README.MD index 195779b..c523b44 100644 --- a/examples/Arduino/README.MD +++ b/examples/Arduino/README.MD @@ -15,12 +15,18 @@ arduino-cli board listall esp8266:esp8266:wifi arduino-cli board listall avr +arduino-cli lib install "Adafruit AMG88xx Library" + +arduino-cli lib search AMG88 + ``` ## Compiling and uploading ```bash +arduino-cli compile -b arduino:samd:mkrzero --upload -p + arduino-cli compile -b arduino:avr:nano:cpu=atmega328 --upload -p "$1" "$2" # upload AVR using USB-ASP @@ -35,6 +41,8 @@ arduino-cli compile -u -p "$1" -b Arduino_STM32:STM32F1:genericSTM32F103C:uploa arduino-cli compile -v -b esp8266:esp8266:wifi_kit_8:baud=921600 -upload -p "" "" +arduino-cli compile -v -b esp8266:esp8266:nodemcuv2:baud=921600 -upload -p "" "" + arduino-cli compile -b arduino:mbed_nano:nano33ble -upload -p "" "" # Using ST-LINK diff --git a/examples/Arduino/ThermalCameraDemo/ThermalCameraDemo.ino b/examples/Arduino/ThermalCameraDemo/ThermalCameraDemo.ino index 0844a9d..d9ff9cc 100644 --- a/examples/Arduino/ThermalCameraDemo/ThermalCameraDemo.ino +++ b/examples/Arduino/ThermalCameraDemo/ThermalCameraDemo.ino @@ -51,7 +51,6 @@ const int byteImagesLen = sizeof (byteImages) / 8; atomicx_time Atomicx_GetTick(void) { - ::yield(); return millis(); } diff --git a/examples/Arduino/avrAutoRobotController/avrAutoRobotController.ino b/examples/Arduino/avrAutoRobotController/avrAutoRobotController.ino index 5ec64a2..32d0307 100644 --- a/examples/Arduino/avrAutoRobotController/avrAutoRobotController.ino +++ b/examples/Arduino/avrAutoRobotController/avrAutoRobotController.ino @@ -45,7 +45,6 @@ size_t GetFreeRam () { atomicx_time Atomicx_GetTick(void) { - ::yield(); return millis(); } @@ -260,11 +259,9 @@ public: for (;;) { - Yield (); - size_t nMessage = 0; - if (Wait (nMessage, motorsContext, (size_t) MotorStatus::request, 100)) + if (Wait (nMessage, motorsContext, (size_t) MotorStatus::request, GetNice ())) { Serial.print ("Motor "); Serial.print ((char) ('A' + nCurrentMotor)); Serial.println (":Notification received."); @@ -295,12 +292,9 @@ public: Serial.print ((char) ('A' + nCurrentMotor)); Serial.println (F(": System was not notified.\n")); } - - Serial.flush (); } m_motor.volts = static_cast(random (2000, 2700)) / 100.0; - } } @@ -349,8 +343,6 @@ uint8_t ConvertStrCommand (String& strCommand) // ------------------------------------------------------------------------------------------- -#define TERMINAL_BS 8 - /** * @brief SERIAL COMM * @@ -476,10 +468,8 @@ public: } }; - // ------------------------------------------------------------------------------------------- - /** * @brief SYSTEM THREAD * diff --git a/examples/Arduino/pubsublock/pubsublock.ino b/examples/Arduino/pubsublock/pubsublock.ino deleted file mode 100644 index a842792..0000000 --- a/examples/Arduino/pubsublock/pubsublock.ino +++ /dev/null @@ -1,259 +0,0 @@ -/* - -*/ -#include "arduino.h" - -#include "atomicx.hpp" - -#include - -#include -#include -#include - -using namespace thread; - -atomicx_time Atomicx_GetTick(void) -{ - ::yield(); - return millis(); -} - -void Atomicx_SleepTick(atomicx_time nSleep) -{ - //ListAllThreads(); - delay(nSleep); -} - -size_t nDataCount=0; -atomicx::mutex gLock; - -String strTopic = "message/topic"; - -class Consumer : public atomicx -{ -public: - Consumer(uint32_t nNice, const char* pszName) : stack{}, atomicx (stack), pszName(pszName) - { - SetNice(nNice); - } - - const char* GetName () override - { - return pszName; - } - - ~Consumer() - { - Serial.print("Deleting Consumer: "); - Serial.print (", ID: "); - Serial.println ((size_t) this); - } - - bool IsSubscribed (const char* pszKey, size_t nKeyLenght) override - { - Serial.print (__FUNCTION__); - Serial.print (":"); - Serial.print (pszKey); - - if (strncmp (pszKey, strTopic.c_str(), nKeyLenght) == 0) - { - Serial.println (":Has Subscriptions....."); - } - else - { - Serial.println (":Has NO Subscriptions....."); - } - - Serial.flush(); - - return true; - } - - bool BrokerHandler(const char* pszKey, size_t nKeyLenght, Message& message) - { - Serial.print (GetName()); - Serial.print (":"); - Serial.print (pszKey); - Serial.print (":"); - Serial.print (message.tag); - Serial.print (":"); - Serial.print (message.message); - Serial.println (": async Handling topic ...."); - Serial.flush(); - - return true; - } - - void run() noexcept override - { - Message message={0,0}; - - do - { - WaitBrokerMessage (strTopic.c_str(), strTopic.length(), message); - - // -------------------------------------- - - Serial.print ("Executing Consumer "); - Serial.print (GetName()); - Serial.print (": "); - Serial.print ((size_t) this); - Serial.print (": Stack used: "); - Serial.print (GetUsedStackSize()); - Serial.print (", locks/shared:"); - Serial.print (gLock.IsLocked()); - Serial.print ("/"); - Serial.print (gLock.IsShared()); - Serial.print (", nTag: "); - Serial.print (message.tag); - Serial.print (", Message: "); - Serial.println (message.message); - - Serial.flush(); - - } while (Yield()); - } - - void StackOverflowHandler(void) noexcept final - { - Serial.print (__FUNCTION__); - Serial.print ("["); - Serial.print (GetName ()); - Serial.print ((size_t) this); - Serial.print (": Stack used "); - Serial.print (GetUsedStackSize()); - Serial.print ("/"); - Serial.println (GetStackSize()); - Serial.flush(); - } - -private: - uint8_t stack[sizeof(size_t) * 50]; - const char* pszName; -}; - - -class Producer : public atomicx -{ -public: - Producer(uint32_t nNice, const char* pszName) : stack{}, atomicx (stack), pszName(pszName) - { - SetNice(nNice); - } - - const char* GetName () override - { - return pszName; - } - - ~Producer() - { - Serial.print("Deleting "); - Serial.println ((size_t) this); - } - - void run() noexcept override - { - size_t nCount=0; - size_t nTag = GetID (); - - do - { - Serial.print ("Executing "); - Serial.print (GetName()); - Serial.print (": "); - Serial.print (GetName ()); - Serial.print ((size_t) this); - Serial.print (", locks/shared:"); - Serial.print (gLock.IsLocked()); - Serial.print ("/"); - Serial.print (gLock.IsShared()); - - Serial.print (", Counter: "); - Serial.println (nCount++); - - Serial.flush(); - - Publish (strTopic.c_str(), strTopic.length(), {nTag, nCount}); - - //atomicx::smart_ptr Consumer_thread (new Consumer(100, "t::Consumer")); - - } while (Yield ()); - } - - void StackOverflowHandler(void) noexcept final - { - Serial.print (__FUNCTION__); - Serial.print ("["); - Serial.print (GetName ()); - Serial.print ((size_t) this); - Serial.print (": Stack used "); - Serial.println (GetUsedStackSize()); - Serial.flush(); - } - - void ListAllThreads() - { - size_t nCount=0; - - Serial.print ("Context: "); - Serial.print (sizeof (atomicx)); - Serial.println ("---------------------------------"); - - for (auto& th : *this) - { - Serial.print (++nCount); - Serial.print (":'"); - Serial.print (th.GetName()); - Serial.print ("' "); - Serial.print ((size_t) &th); - Serial.print (", Nice: "); - Serial.print (th.GetNice()); - Serial.print ("\t, Stack: "); - Serial.print (th.GetStackSize()); - Serial.print ("\t, UsedStack: "); - Serial.print(th.GetUsedStackSize()); - Serial.print ("\t, Status: "); - Serial.println (th.GetStatus()); - - Serial.flush(); - } - } - -private: - uint8_t stack[sizeof(size_t) * 50]; - const char* pszName; -}; - -void setup() -{ - Serial.begin (115200); - - while (! Serial) delay (100); - - delay (2000); - - Serial.println ("-----------------------------------------------\n"); - Serial.println ("Starting up..."); - Serial.println ("-----------------------------------------------\n"); - Serial.flush(); - - - Producer T1(100, "Thread 1"); - Consumer E1(100, "Consumer 1"); - Consumer E2(250, "Consumer 2"); - Consumer E4(500, "Consumer 3"); - - T1.ListAllThreads (); - - atomicx::Start(); - - Serial.println ("Full lock detected..."); - - T1.ListAllThreads (); -} - -void loop() { - -} diff --git a/examples/Arduino/send_receive/atomicx.cpp b/examples/Arduino/send_receive/atomicx.cpp new file mode 120000 index 0000000..7494a3a --- /dev/null +++ b/examples/Arduino/send_receive/atomicx.cpp @@ -0,0 +1 @@ +../../../atomicx/atomicx.cpp \ No newline at end of file diff --git a/examples/Arduino/send_receive/atomicx.hpp b/examples/Arduino/send_receive/atomicx.hpp new file mode 120000 index 0000000..0127fc9 --- /dev/null +++ b/examples/Arduino/send_receive/atomicx.hpp @@ -0,0 +1 @@ +../../../atomicx/atomicx.hpp \ No newline at end of file diff --git a/examples/Arduino/send_receive/send_receive.ino b/examples/Arduino/send_receive/send_receive.ino new file mode 100644 index 0000000..8d7f961 --- /dev/null +++ b/examples/Arduino/send_receive/send_receive.ino @@ -0,0 +1,186 @@ +/* + +*/ +#include "arduino.h" + +#include "atomicx.hpp" + +#include + +#include +#include +#include + +using namespace thread; + +void ListAllThreads(); + +atomicx_time Atomicx_GetTick(void) +{ + return millis(); +} + +void Atomicx_SleepTick(atomicx_time nSleep) +{ + //ListAllThreads(); + delay(nSleep); +} + +constexpr size_t GetStackSize(size_t sizeRef) +{ + return sizeRef * sizeof (size_t); +} + +// signal +uint8_t dataPoint = 0; + +struct transf +{ + int Counter; + char pszData[20]; +}; + +class Consumer : public atomicx +{ +public: + Consumer(uint32_t nNice) : atomicx (m_stack), m_stack{} + { + SetNice(nNice); + } + + const char* GetName () override + { + return "consumer"; + } + + ~Consumer() + { + Serial.print (F("Deleting Consumer: ID: ")); + Serial.println ((size_t) this); + } + + void run() noexcept override + { + size_t nReceived; + transf tr = {19, ""}; + + for (;;) + { + Serial.print (GetLastUserExecTime()); + Serial.println (F("ms last exec time, Receiving data...")); Serial.flush (); + + if ((nReceived = Receive(dataPoint, (uint8_t*) &tr, (uint16_t) sizeof (tr), 10000))) + { + Serial.print (F("Received: val: ")); + Serial.print (tr.Counter); + Serial.print (F(", String: ")); + Serial.println (tr.pszData); + Serial.flush (); + } + else + { + Serial.println (F("Failed to receive information.")); + Serial.println (tr.Counter); + Serial.flush (); + } + } + } + + void StackOverflowHandler(void) noexcept final + { + Serial.print (__FUNCTION__); + Serial.print (F("[")); + Serial.print (GetName ()); + Serial.print ((size_t) this); + Serial.print (F(": Stack used ")); + Serial.print (GetUsedStackSize()); + Serial.print (F("/")); + Serial.println (GetStackSize()); + Serial.flush(); + } + +private: + uint8_t m_stack[::GetStackSize(65)]; +}; + +class Producer : public atomicx +{ +public: + Producer(uint32_t nNice) : atomicx (m_stack), m_stack{} + { + SetNice(nNice); + } + + const char* GetName () override + { + return "Producer"; + } + + ~Producer() + { + Serial.print("Deleting Producer"); + Serial.println ((size_t) this); + } + + void run() noexcept override + { + transf tr {0, ""}; + int16_t nResponse; + + for(;;) + { + tr.Counter ++; + snprintf(tr.pszData, sizeof (tr.pszData), "Counter: %u", tr.Counter); + Serial.print (GetLastUserExecTime()); + Serial.print (F("ms last exectime, Sending data...:")); Serial.println (tr.pszData); Serial.flush (); + + if ((nResponse = Send (dataPoint, (uint8_t*) &tr, (uint16_t) sizeof (tr), 10000)) > 0) + { + Serial.print (F("Data Sent:")); Serial.println (nResponse); Serial.flush (); + } + else + { + Serial.print (F("Failed to send data...")); Serial.println (nResponse); Serial.flush (); + } + } + } + + void StackOverflowHandler(void) noexcept final + { + Serial.print (__FUNCTION__); + Serial.print (F("[")); + Serial.print (GetName ()); + Serial.print ((size_t) this); + Serial.print (F(": Stack used ")); + Serial.println (GetUsedStackSize()); + Serial.flush(); + } + +private: + uint8_t m_stack[::GetStackSize(65)]; + String m_threadName; +}; + +void setup() +{ + Serial.begin (115200); + + while (! Serial) delay (100); + + delay (2000); + + Serial.println (F("Starting UP-----------------------------------------------\n")); + + Serial.flush(); + + Producer T1(500); + Consumer E1(500); + + atomicx::Start(); + + Serial.println (F("Full lock detected...")); +} + +void loop() { + +} diff --git a/examples/Arduino/sharedlock/sharedlock.ino b/examples/Arduino/sharedlock/sharedlock.ino index 39c5b51..573dafb 100644 --- a/examples/Arduino/sharedlock/sharedlock.ino +++ b/examples/Arduino/sharedlock/sharedlock.ino @@ -20,7 +20,6 @@ void ListAllThreads(); atomicx_time Atomicx_GetTick(void) { - ::yield(); return millis(); } diff --git a/examples/Arduino/watchdog/atomicx.cpp b/examples/Arduino/watchdog/atomicx.cpp new file mode 120000 index 0000000..7494a3a --- /dev/null +++ b/examples/Arduino/watchdog/atomicx.cpp @@ -0,0 +1 @@ +../../../atomicx/atomicx.cpp \ No newline at end of file diff --git a/examples/Arduino/watchdog/atomicx.hpp b/examples/Arduino/watchdog/atomicx.hpp new file mode 120000 index 0000000..0127fc9 --- /dev/null +++ b/examples/Arduino/watchdog/atomicx.hpp @@ -0,0 +1 @@ +../../../atomicx/atomicx.hpp \ No newline at end of file diff --git a/examples/Arduino/watchdog/watchdog.cpp b/examples/Arduino/watchdog/watchdog.cpp new file mode 100644 index 0000000..f62f5d1 --- /dev/null +++ b/examples/Arduino/watchdog/watchdog.cpp @@ -0,0 +1,167 @@ +/** + * @file watchdog.cpp + * @author your name (you@domain.com) + * @brief + * @version 0.1 + * @date 2022-02-16 + * + * @copyright Copyright (c) 2022 + * + */ + +#include "watchdog.hpp" + +Watchdog::Item::Item (atomicx& thread, atomicx_time allowedTime, bool isCritical): + nThreadId (&thread), + nextAllarm (), + allowedTime (allowedTime), + isCritical (isCritical), + recoverCounter(0) +{ + this->nextAllarm.Set (allowedTime); + Watchdog::GetInstance().AttachThread (*this); +} + +Watchdog::Item::~Item () +{ + Watchdog::GetInstance().DetachThread (*this); +} + +Watchdog& Watchdog::GetInstance() +{ + static Watchdog instance; + return instance; +} + +const char* Watchdog::GetName () +{ + return "Watchdog"; +} + +Watchdog::~Watchdog() +{} + +uint8_t Watchdog::GetAllarmCounter (size_t threadId) +{ + auto* pwtdItem = FindThread (threadId); + uint8_t nRet = 0; + + if (pwtdItem) + { + nRet = (*pwtdItem)().recoverCounter; + } + + return nRet; +} + +Watchdog::Item* Watchdog::FindThread (size_t threadId) +{ + for (auto& wtdItem : list) + { + if ((size_t) wtdItem().nThreadId == threadId) + { + return &(wtdItem()); + } + } + + return nullptr; +} + +bool Watchdog::Feed () +{ + auto* pwtdItem = FindThread (thread::atomicx::GetCurrent()->GetID ()); + + if (pwtdItem != nullptr) + { + pwtdItem->nextAllarm.Set(pwtdItem->allowedTime); + pwtdItem->recoverCounter=0; + return true; + } + + return false; +} + +bool Watchdog::AttachThread (Item& thread) +{ + list.AttachBack (thread); +} + +bool Watchdog::DetachThread (Item& thread) +{ + list.Detach (thread); +} + +void Watchdog::abort (Item& item, const char* abortMessage) +{ + Serial.print (F("Watchdog: ")); + Serial.print (item.nThreadId); + Serial.print (": counter "); + Serial.print (item.recoverCounter); + Serial.print (": "); + Serial.println (abortMessage); + Serial.flush (); + + Serial.println (F(">>> RESETING\n\n")); + Serial.flush (); + + void (*resetptr)(void) = 0x0000; + resetptr(); +} + +void Watchdog::run() +{ + do + { + for (auto& wtdItem : list) + { + if (wtdItem().nextAllarm.IsTimedout ()) + { + if (wtdItem().recoverCounter == 3) + { + if (wtdItem().isCritical) + { + abort (wtdItem(), "Maximum recovery time reached, restarting"); + } + else + { + atomicx* pthread = thread::atomicx::GetThread (wtdItem().nThreadId); + + if (pthread && pthread->IsStopped () == false) + { + pthread->Restart (); + wtdItem().nextAllarm.Set (wtdItem().allowedTime); + wtdItem().recoverCounter = 0; + } + } + } + else + { + wtdItem().nextAllarm.Set ((wtdItem().allowedTime)/2); + wtdItem().recoverCounter++; + } + } + } + + Serial.println ("----"); + Serial.flush (); + + } while (Yield()); +} + +void Watchdog::StackOverflowHandler(void) +{ + Serial.print (__FUNCTION__); + Serial.print (F("[")); + Serial.print (GetName ()); + Serial.print ((size_t) this); + Serial.print (F("] Stack used ")); + Serial.print (GetUsedStackSize()); + Serial.print (F("/")); + Serial.println (GetStackSize()); + Serial.flush(); +} + +Watchdog::Watchdog() : atomicx (CALCSTACKSIZE(20), 10) +{ + SetNice(1000); +} diff --git a/examples/Arduino/watchdog/watchdog.hpp b/examples/Arduino/watchdog/watchdog.hpp new file mode 100644 index 0000000..085dab2 --- /dev/null +++ b/examples/Arduino/watchdog/watchdog.hpp @@ -0,0 +1,78 @@ +/** + * @file watchdog.hpp + * @author your name (you@domain.com) + * @brief + * @version 0.1 + * @date 2022-02-16 + * + * @copyright Copyright (c) 2022 + * + */ + +#ifndef __WATCHDOG_HPP__ +#define __WATCHDOG_HPP__ + +#include "arduino.h" + +#include "atomicx.hpp" + +using namespace thread; + +class Watchdog: public atomicx +{ +public: + /** + * @brief Item class used to make any atomicx thread attachable + */ + class Item : public LinkItem + { + public: + Item () = delete; + + Item (atomicx& thread, atomicx_time allowedTime = 1000, bool isCritical = false); + + virtual ~Item (); + + public: + friend class Watchdog; + + size_t nThreadId = 0; + atomicx::Timeout nextAllarm; + atomicx_time allowedTime=0; + + bool isCritical : 1; + uint8_t recoverCounter : 2; + }; + + static Watchdog& GetInstance(); + + const char* GetName () override; + + ~Watchdog(); + + uint8_t GetAllarmCounter (size_t threadId); + + Item* FindThread (size_t threadId); + + bool Feed (); + + bool AttachThread (Item& thread); + + bool DetachThread (Item& thread); + +protected: + + void abort (Item& item, const char* abortMessage); + + void run() noexcept override; + + void StackOverflowHandler(void) noexcept final; + +private: + + Watchdog(); + + LinkList list; +}; + +#endif \ No newline at end of file diff --git a/examples/Arduino/watchdog/watchdog.ino b/examples/Arduino/watchdog/watchdog.ino new file mode 100644 index 0000000..46c7424 --- /dev/null +++ b/examples/Arduino/watchdog/watchdog.ino @@ -0,0 +1,229 @@ +/* + +*/ + +#include "arduino.h" + +#include "atomicx.hpp" + +#include +#include +#include +#include + +#include "watchdog.hpp" + +using namespace thread; + +size_t nDataCount=0; + +void ListAllThreads(); + +atomicx_time Atomicx_GetTick(void) +{ + return millis(); +} + +void Atomicx_SleepTick(atomicx_time nSleep) +{ + delay(nSleep); +} + +constexpr size_t GetStackSize(size_t sizeRef) +{ + return sizeRef * sizeof (size_t); +} + +class ThreadAllarm : public atomicx, public Watchdog::Item +{ +public: + ThreadAllarm(uint32_t nNice) : atomicx (::GetStackSize(20), 10), Item (GetThread (), 1000) + { + SetNice(nNice); + } + + const char* GetName () override + { + return "ThreadFeeded"; + } + + virtual ~ThreadAllarm() + { + Serial.print("Deleting ThreadAllarm"); + Serial.print (", ID: "); + Serial.println ((size_t) this); + } + + void run() noexcept override + { + Serial.print (F(">>>>> Starting up: ")); Serial.println (GetName ()); Serial.flush (); + delay (2000); + + do + { + Serial.print ("Executing: "); Serial.println (GetName ()); Serial.flush (); + ListAllThreads (); + + if (Watchdog::GetInstance().GetAllarmCounter (GetID()) == 3) + { + Watchdog::GetInstance().Feed (); + } + } while (Yield(random (1000, 2000))); + } + + void finish () noexcept override + { + Serial.print (F(">>>>>>>Detroying Thread, counter ")); + Serial.println (Watchdog::GetInstance().GetAllarmCounter (GetID())); + Serial.print (F(": ")); + Serial.println (GetName ()); + Serial.flush (); + } + + void StackOverflowHandler(void) noexcept final + { + Serial.print (__FUNCTION__); + Serial.print ("["); + Serial.print (GetName ()); + Serial.print ((size_t) this); + Serial.print ("] Stack used "); + Serial.print (GetUsedStackSize()); + Serial.print ("/"); + Serial.println (GetStackSize()); + Serial.flush(); + } + +private: +}; + +class ThreadAllarm2 : public atomicx, Watchdog::Item +{ +public: + ThreadAllarm2(uint32_t nNice) : atomicx (::GetStackSize(20), 10), Item (GetThread (), 1800, true) + { + SetNice(nNice); + } + + const char* GetName () override + { + return "ThreadStarved"; + } + + ~ThreadAllarm2() + { + Serial.print("Deleting ThreadAllarm"); + Serial.print (", ID: "); + Serial.println ((size_t) this); + } + +protected: + + void run() noexcept override + { + Serial.print (F(">>>>> Starting up: ")); Serial.println (GetName ()); Serial.flush (); + delay (2000); + + do + { + Serial.print ("Executing: "); Serial.println (GetName ()); Serial.flush (); + ListAllThreads (); + + if (Watchdog::GetInstance().GetAllarmCounter (GetID()) == 3) + { + Watchdog::GetInstance().Feed (); + } + } while (Yield(random (1000, 2000))); + } + + void finish () noexcept override + { + Serial.print (F(">>>>>>>Detroying Thread, counter ")); + Serial.println (Watchdog::GetInstance().GetAllarmCounter (GetID())); + Serial.print (F(": ")); + Serial.println (GetName ()); + Serial.flush (); + } + + void StackOverflowHandler(void) noexcept final + { + Serial.print (__FUNCTION__); + Serial.print ("["); + Serial.print (GetName ()); + Serial.print ((size_t) this); + Serial.print ("] Stack used "); + Serial.print (GetUsedStackSize()); + Serial.print ("/"); + Serial.println (GetStackSize()); + Serial.flush(); + } + +private: +}; + +void ListAllThreads() +{ + size_t nCount=0; + + Serial.println ("[THREAD]-----------------------------------------------"); + Serial.print (F("Context size: ")); Serial.println (sizeof (thread::atomicx)); + Serial.println ("[List active threads]----------------------------------"); + + Serial.flush(); + + for (auto& th : *(atomicx::GetCurrent())) + { + Serial.print (atomicx::GetCurrent() == &th ? "* " : " "); + Serial.print (++nCount); + Serial.print (":'"); + Serial.print (th.GetName()); + Serial.print ("' "); + Serial.print ((size_t) &th); + Serial.print (", Nice: "); + Serial.print (th.GetNice()); + Serial.print (", Stack: "); + Serial.print (th.IsStackSelfManaged() ? 'A' : ' '); + Serial.print (th.GetStackSize()); + Serial.print (", UsedStack: "); + Serial.print(th.GetUsedStackSize()); + Serial.print (", Status: "); + Serial.print (th.GetStatus()); + Serial.print (", WTD Allarm: "); + Serial.println (Watchdog::GetInstance().GetAllarmCounter (th.GetID())); + Serial.flush(); + } + + Serial.println (""); + + Serial.flush(); +} + + +void setup() +{ + Serial.begin (115200); + + while (! Serial) delay (100); + + delay (2000); + + Serial.println ("Starting UP-----------------------------------------------\n"); + Serial.flush (); + + ThreadAllarm allarm1 (1000); + ThreadAllarm2 allarm2 (1000); + + //ListAllThreads (); + + Serial.println ("running kernel\n"); + Serial.flush (); + + atomicx::Start(); + + Serial.println ("Full lock detected..."); + + ListAllThreads (); +} + +void loop() { + +} diff --git a/main.cpp b/main.cpp index cb05cd2..71b9a86 100644 --- a/main.cpp +++ b/main.cpp @@ -47,10 +47,13 @@ void Atomicx_SleepTick(atomicx_time nSleep) usleep ((useconds_t)nSleep * 1000); } -atomicx::queueq(5); size_t nGlobalCount = 0; -atomicx::semaphore sem(2); +struct transf +{ + size_t Counter; + char pszData[80]; +}; class ThreadConsummer : public atomicx { @@ -71,21 +74,21 @@ class ThreadConsummer : public atomicx void run(void) noexcept override { - while (Yield ()) - { - smartSemaphore ssem (sem); + size_t nReceived; - if (ssem.acquire (0)) + for (;;) + { + transf tr = {.Counter=19, .pszData=""}; + + if ((nReceived = Receive(nGlobalCount, (uint8_t*) &tr, (uint16_t) sizeof (tr), 1000))) { - std::cout << GetName () << ":" << (size_t) this << " ACQUIRED, Max: " << sem.GetMaxAcquired () << ", Acquired: " << sem.GetCount () << ", Waiting: " << sem.GetWaitCount () << std::endl << std::flush; - std::cout << GetName () << ":" << (size_t) this << " GlobalCounter: " << nGlobalCount << std::endl << std::flush; + printf ("Receiver: nReceived: %zu, Counter: %zu (%s)\n", nReceived, tr.Counter, tr.pszData); } else { - std::cout << GetName () << ":" << (size_t) this << " TIMED OUT, Max: " << sem.GetMaxAcquired () << ", Acquired: " << sem.GetCount () << ", Waiting: " << sem.GetWaitCount () << std::endl << std::flush; + printf ("Receiver: failed receive...\n"); + ListAllThreads(); } - - Yield (); } } @@ -120,9 +123,21 @@ class Thread : public atomicx void run() noexcept override { - while (Yield ()) + transf tr { .Counter=0, .pszData=""}; + + for (;;) { - nGlobalCount++; + tr.Counter ++; + snprintf(tr.pszData, sizeof tr.pszData, "Counter: %zu", tr.Counter); + + if (Send (nGlobalCount, (uint8_t*) &tr, (uint16_t) sizeof (tr), 1000) == false) + { + printf ("Publish %zu: (%s) Failed to send data.\n", GetID(), GetName ()); + } + else + { + printf ("%zu: (%s) Data Sent..\n", GetID(), GetName ()); + } } } @@ -140,8 +155,6 @@ class Thread : public atomicx const char* m_pszName; }; - - void ListAllThreads() { std::cout << "[Thread]-----------------------------------------------" << std::endl; @@ -154,39 +167,20 @@ void ListAllThreads() std::cout << "-------------------------------------------------------" << std::endl; } -Thread t1(500, "Producer 1"); -ThreadConsummer e1(500, "Consumer 1"); int main() { - q.PushBack(1); - q.PushBack(2); - q.PushBack(3); - q.PushBack(4); - q.PushBack(5); - q.PushBack(6); - q.PushBack(7); - q.PushBack(8); - - q.PushFront(-1); - q.PushFront(-2); - q.PushFront(-3); - q.PushFront(-5); - - // while (q.GetSize()) std::cout << "push: " << q.pop() << std::endl; - - - ThreadConsummer e2(500, "Consumer 2"); - ThreadConsummer e3(500, "Consumer 3"); - ThreadConsummer e4(500, "Consumer 4"); - ThreadConsummer e5(500, "Consumer 5"); - ThreadConsummer e6(500, "Consumer 6"); - - std::cout << "end context" << std::endl; + Thread t1(1000, "Producer 1"); + //Thread t2(1000, "Producer 2"); + //Thread t3(1000, "Producer 3"); + //Thread t4(1000, "Producer 4"); + + ThreadConsummer e1(500, "Consumer 1"); + std::cout << "LISTING....." << std::endl; - ListAllThreads (); + ListAllThreads (); std::cout << "END LISTING....." << std::endl;