init
This commit is contained in:
parent
e97a664658
commit
f4476b67b1
2
.gitignore
vendored
2
.gitignore
vendored
@ -2,3 +2,5 @@
|
||||
/coverage
|
||||
*.log
|
||||
.DS_Store
|
||||
CppPotpourri
|
||||
./tmp
|
||||
41
README.md
41
README.md
@ -1,3 +1,40 @@
|
||||
# osr-package-template
|
||||
|
||||
Package basics
|
||||
# OSR Firmware Library
|
||||
|
||||
## Logging
|
||||
|
||||
- [x] Serial ('Arduino-Log')
|
||||
- [ ] Transport : TCP
|
||||
- [ ] Flags
|
||||
- [ ] Template (Plotter)
|
||||
|
||||
## Compononents
|
||||
|
||||
- [ ] Run-Time Flags
|
||||
- [ ] Network Flags
|
||||
- [ ] Variable Calling Convention
|
||||
- [ ] Callback Signature Register
|
||||
- [ ] Lifecycle: Start, Stop, Reset, Remove, Add
|
||||
- [ ] Name: Optional
|
||||
- [ ] Linked List
|
||||
- [ ] Error Codes
|
||||
|
||||
## Debugging
|
||||
|
||||
## Networking
|
||||
|
||||
- [ ] RS485 Proxy
|
||||
- [ ] CAN
|
||||
- [ ] TCP Raw
|
||||
- [ ] BL
|
||||
|
||||
## Compiling
|
||||
|
||||
- [ ] Platform.IO Module
|
||||
- [ ] Arduino-Lib Release
|
||||
|
||||
## Documentation
|
||||
|
||||
## References
|
||||
|
||||
- [CPP Commons - CppPotpourri](https://github.com/jspark311/CppPotpourri.git)
|
||||
|
||||
11
library.properties
Normal file
11
library.properties
Normal file
@ -0,0 +1,11 @@
|
||||
name=polymech-base
|
||||
version=1.0.0
|
||||
author=mc007
|
||||
maintainer=mc007
|
||||
sentence=polymech-base Library
|
||||
paragraph=
|
||||
category=Uncategorized
|
||||
url=https://github/osr_base
|
||||
architectures=*
|
||||
includes=PolymechBase.h
|
||||
depends=ArduinoLog,Vector,Streaming
|
||||
56
package.json
56
package.json
@ -1,45 +1,23 @@
|
||||
|
||||
{
|
||||
"name": "@plastichub/template",
|
||||
"description": "",
|
||||
"version": "0.3.1",
|
||||
"main": "main.js",
|
||||
"typings": "index.d.ts",
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"bin": {
|
||||
"osr-bin": "main.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"@types/node": "^14.17.5",
|
||||
"@types/yargs": "^17.0.2",
|
||||
"chalk": "^2.4.1",
|
||||
"convert-units": "^2.3.4",
|
||||
"env-var": "^7.0.1",
|
||||
"typescript": "^4.3.5",
|
||||
"yargs": "^14.2.3",
|
||||
"yargs-parser": "^15.0.3"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "tsc; mocha --full-trace mocha \"spec/**/*.spec.js\"",
|
||||
"test-with-coverage": "istanbul cover node_modules/.bin/_mocha -- 'spec/**/*.spec.js'",
|
||||
"lint": "tslint --project=./tsconfig.json",
|
||||
"build": "tsc -p .",
|
||||
"dev": "tsc -p . --declaration -w",
|
||||
"typings": "tsc --declaration",
|
||||
"docs": "npx typedoc src/index.ts",
|
||||
"dev-test-watch": "mocha-typescript-watch"
|
||||
},
|
||||
"homepage": "https://git.osr-plastic.org/plastichub/lib-content",
|
||||
"name": "polymech",
|
||||
"description": "A simple and efficient JSON library for embedded C++. ⭐ 6849 stars on GitHub! Supports serialization, deserialization, MessagePack, streams, filtering, and more. Fully tested and documented.",
|
||||
"homepage": "https://arduinojson.org/?utm_source=meta&utm_medium=library.json",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://git.osr-plastic.org/plastichub/lib-content.git"
|
||||
"url": "https://github.com/bblanchon/ArduinoJson.git"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 14.0.0"
|
||||
"version": "7.3.1",
|
||||
"authors": {
|
||||
"name": "Benoit Blanchon",
|
||||
"url": "https://blog.benoitblanchon.fr"
|
||||
},
|
||||
"license": "BSD-3-Clause",
|
||||
"keywords": [
|
||||
"typescript"
|
||||
]
|
||||
"export": {
|
||||
"include": ["src", "examples", "LICENSE.txt", "polymech.h"]
|
||||
},
|
||||
"frameworks": "*",
|
||||
"platforms": "*",
|
||||
"build": {
|
||||
"libArchive": false
|
||||
}
|
||||
}
|
||||
|
||||
0
src/.gitignore
vendored
0
src/.gitignore
vendored
18
src/Addon.cpp
Normal file
18
src/Addon.cpp
Normal file
@ -0,0 +1,18 @@
|
||||
#include "Addon.h"
|
||||
#include <Streaming.h>
|
||||
#include <Vector.h>
|
||||
#include <Arduino.h>
|
||||
|
||||
Addon *byId(Addons addons, uchar id)
|
||||
{
|
||||
uchar s = addons.size();
|
||||
for (uchar i = 0; i < s; i++)
|
||||
{
|
||||
Addon *addon = addons[i];
|
||||
if (addon->id == id)
|
||||
{
|
||||
return addon;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
58
src/Addon.h
Normal file
58
src/Addon.h
Normal file
@ -0,0 +1,58 @@
|
||||
#ifndef ADDON_H
|
||||
#define ADDON_H
|
||||
|
||||
#include <WString.h>
|
||||
#include <Vector.h>
|
||||
|
||||
#include "enums.h"
|
||||
#include "error_codes.h"
|
||||
#include "macros.h"
|
||||
#include "Component.h"
|
||||
|
||||
// back compat
|
||||
class Addon : public Component
|
||||
{
|
||||
|
||||
public:
|
||||
static const int ADDON_FLAGS_DEFAULT = 1 << OBJECT_RUN_FLAGS::E_OF_LOOP | 1 << OBJECT_RUN_FLAGS::E_OF_INFO | 1 << OBJECT_RUN_FLAGS::E_OF_SETUP;
|
||||
|
||||
/*
|
||||
Addon(
|
||||
String _name,
|
||||
short _id) : Component(_name, _id),
|
||||
name(_name),
|
||||
id(_id),
|
||||
now(0),
|
||||
flags(COMPONENT_DEFAULT)
|
||||
{
|
||||
}
|
||||
|
||||
Addon(
|
||||
String _name,
|
||||
short _id,
|
||||
short _flags) : Component(_name, _id, _flags),
|
||||
name(_name),
|
||||
id(_id),
|
||||
flags(_flags)
|
||||
{
|
||||
}
|
||||
|
||||
Addon(
|
||||
String _name,
|
||||
short _id,
|
||||
short _flags,
|
||||
Addon *_owner) : Component(_name, _id, _flags, _owner),
|
||||
name(_name),
|
||||
id(_id),
|
||||
flags(_flags),
|
||||
owner(_owner)
|
||||
{
|
||||
}
|
||||
*/
|
||||
};
|
||||
|
||||
typedef Vector<Addon *> Addons;
|
||||
Addon *byId(Addons addons, uchar id);
|
||||
typedef short (Addon::*AddonFnPtr)(short);
|
||||
|
||||
#endif
|
||||
149
src/App.cpp
Normal file
149
src/App.cpp
Normal file
@ -0,0 +1,149 @@
|
||||
#include <Vector.h>
|
||||
#include <Arduino.h>
|
||||
|
||||
#include "App.h"
|
||||
#include "Bridge.h"
|
||||
#include "error_codes.h"
|
||||
#include "enums.h"
|
||||
#include "constants.h"
|
||||
#include "config.h"
|
||||
|
||||
static Component *componentsArray[MAX_COMPONENTS];
|
||||
App::App() : Component("APP", COMPONENT_KEY_APP, Component::COMPONENT_DEFAULT)
|
||||
{
|
||||
DEBUG_INTERVAL = DEFAULT_DEBUG_INTERVAL;
|
||||
components.setStorage(componentsArray);
|
||||
debugTS = 0;
|
||||
}
|
||||
////////////////////////////////////////////////
|
||||
//
|
||||
// Component related functions
|
||||
//
|
||||
short App::setup()
|
||||
{
|
||||
short s = components.size();
|
||||
for (short i = 0; i < s; i++)
|
||||
{
|
||||
Component *component = components[i];
|
||||
if (!component)
|
||||
{
|
||||
Log.errorln(F("App::setup - Found NULL component at index %d"), i);
|
||||
continue;
|
||||
}
|
||||
if (!component->owner)
|
||||
{
|
||||
component->owner = this;
|
||||
}
|
||||
if (component->hasFlag(OBJECT_RUN_FLAGS::E_OF_SETUP))
|
||||
{
|
||||
component->setup();
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.verboseln(F("App::setup - Skipping setup() for component (no flag): ID=%d, Name=%s"), component->id, component->name.c_str());
|
||||
}
|
||||
}
|
||||
return E_OK;
|
||||
}
|
||||
|
||||
short App::registerComponents(Bridge *bridge)
|
||||
{
|
||||
short s = components.size();
|
||||
for (short i = 0; i < s; i++)
|
||||
{
|
||||
Component *component = components[i];
|
||||
if (component->hasFlag(OBJECT_RUN_FLAGS::E_OF_DISABLED))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
component->serial_register(bridge);
|
||||
}
|
||||
return E_OK;
|
||||
}
|
||||
|
||||
short App::loop()
|
||||
{
|
||||
timer.tick();
|
||||
now = millis();
|
||||
short s = components.size();
|
||||
for (short i = 0; i < s; i++)
|
||||
{
|
||||
Component *component = components[i];
|
||||
if (component->hasFlag(OBJECT_RUN_FLAGS::E_OF_LOOP))
|
||||
{
|
||||
component->now = now;
|
||||
component->loop();
|
||||
}
|
||||
}
|
||||
debug();
|
||||
return E_OK;
|
||||
}
|
||||
|
||||
short App::numByFlag(ushort flag)
|
||||
{
|
||||
short s = components.size();
|
||||
short l = 0;
|
||||
for (short i = 0; i < s; i++)
|
||||
{
|
||||
Component *component = components[i];
|
||||
if (!!(component->hasFlag(flag)))
|
||||
{
|
||||
l++;
|
||||
}
|
||||
}
|
||||
return l;
|
||||
}
|
||||
|
||||
short App::setDebugParams(short val0, short val1)
|
||||
{
|
||||
DEBUG_INTERVAL = val0;
|
||||
return E_OK;
|
||||
}
|
||||
|
||||
short App::debug()
|
||||
{
|
||||
if (millis() - debugTS < DEBUG_INTERVAL)
|
||||
{
|
||||
return E_OK;
|
||||
}
|
||||
|
||||
debugTS = millis();
|
||||
short s = components.size();
|
||||
for (short i = 0; i < s; i++)
|
||||
{
|
||||
Component *component = components[i];
|
||||
if (component->hasFlag(OBJECT_RUN_FLAGS::E_OF_DEBUG))
|
||||
{
|
||||
component->debug();
|
||||
}
|
||||
}
|
||||
return E_OK;
|
||||
}
|
||||
|
||||
short App::info()
|
||||
{
|
||||
short s = components.size();
|
||||
for (short i = 0; i < s; i++)
|
||||
{
|
||||
Component *component = components[i];
|
||||
if (component->hasFlag(OBJECT_RUN_FLAGS::E_OF_INFO))
|
||||
{
|
||||
component->info();
|
||||
}
|
||||
}
|
||||
return E_OK;
|
||||
}
|
||||
|
||||
Component *App::byId(ushort id)
|
||||
{
|
||||
short s = components.size();
|
||||
for (short i = 0; i < s; i++)
|
||||
{
|
||||
Component *component = components[i];
|
||||
if (component->id == id)
|
||||
{
|
||||
return component;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
131
src/App.h
Normal file
131
src/App.h
Normal file
@ -0,0 +1,131 @@
|
||||
#ifndef APP_H
|
||||
#define APP_H
|
||||
|
||||
#include <Vector.h>
|
||||
#include "xtypes.h"
|
||||
#include "Component.h"
|
||||
#include "xtimer.h"
|
||||
#include "Bridge.h"
|
||||
|
||||
class Bridge;
|
||||
|
||||
/**
|
||||
* @brief The App class represents the main application component.
|
||||
*
|
||||
* This class inherits from the Component class and provides functionality
|
||||
* for setting up, running the main loop, debugging, and providing information
|
||||
* about the application.
|
||||
*/
|
||||
class App : public Component
|
||||
{
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Default constructor for the App class.
|
||||
*/
|
||||
App();
|
||||
|
||||
App(String _name, short _id, int _flags) : Component(_name, _id, _flags) {}
|
||||
|
||||
/**
|
||||
* @brief Retrieves a component by its ID.
|
||||
*
|
||||
* @param id The ID of the component to retrieve.
|
||||
* @return A pointer to the component with the specified ID, or nullptr if not found.
|
||||
*/
|
||||
virtual Component *byId(ushort id);
|
||||
|
||||
/**
|
||||
* @brief Performs the setup operations for the application and it's components.
|
||||
*
|
||||
* @return A status code indicating the result of the setup operation.
|
||||
*/
|
||||
virtual short setup();
|
||||
|
||||
/**
|
||||
* @brief Runs the main loop of the application.
|
||||
*
|
||||
* @return A status code indicating the result of the loop operation.
|
||||
*/
|
||||
virtual short loop();
|
||||
|
||||
/**
|
||||
* @brief Performs debugging operations for the application.
|
||||
*
|
||||
* @return A status code indicating the result of the debug operation.
|
||||
*/
|
||||
virtual short debug();
|
||||
|
||||
/**
|
||||
* @brief Retrieves information about the application.
|
||||
*
|
||||
* @return A status code indicating the result of the info operation.
|
||||
*/
|
||||
virtual short info();
|
||||
|
||||
/**
|
||||
* @brief Retrieves the number of components with a specific flag.
|
||||
*
|
||||
* @param flag The flag to filter the components by.
|
||||
* @return The number of components with the specified flag.
|
||||
*/
|
||||
virtual short numByFlag(ushort flag);
|
||||
|
||||
/**
|
||||
* @brief The list of components in the application.
|
||||
*/
|
||||
Vector<Component *> components;
|
||||
|
||||
/**
|
||||
* @brief The timestamp for the last debug operation.
|
||||
*/
|
||||
millis_t debugTS;
|
||||
|
||||
/**
|
||||
* @brief The timestamp for the last loop operation.
|
||||
*/
|
||||
millis_t loopTS;
|
||||
|
||||
/**
|
||||
* @brief The wait time between loop iterations.
|
||||
*/
|
||||
millis_t wait;
|
||||
|
||||
/**
|
||||
* @brief The timestamp for the last wait operation.
|
||||
*/
|
||||
millis_t waitTS;
|
||||
|
||||
/**
|
||||
* @brief The timer used for scheduling tasks.
|
||||
*/
|
||||
Timer<10, millis> timer;
|
||||
|
||||
/**
|
||||
* @brief The interval for debugging operations.
|
||||
*/
|
||||
millis_t DEBUG_INTERVAL;
|
||||
|
||||
//////////////////////////////////////////
|
||||
//
|
||||
// Messaging / Binding
|
||||
//
|
||||
/**
|
||||
* @brief Register all components in `Bridge`
|
||||
*
|
||||
* @return A status code indicating the result of the setup operation.
|
||||
*/
|
||||
virtual short registerComponents(Bridge *bridge);
|
||||
|
||||
Bridge *bridge;
|
||||
|
||||
virtual void printRegisters() { }
|
||||
|
||||
//////////////////////////////////////////
|
||||
//
|
||||
// Debugging
|
||||
//
|
||||
virtual short setDebugParams(short val0, short val1);
|
||||
};
|
||||
|
||||
#endif
|
||||
151
src/Bridge.cpp
Normal file
151
src/Bridge.cpp
Normal file
@ -0,0 +1,151 @@
|
||||
#include "macros.h"
|
||||
#include <ArduinoLog.h>
|
||||
#include "Bridge.h"
|
||||
#include <Vector.h>
|
||||
#include <Streaming.h>
|
||||
#include "constants.h"
|
||||
#include <StringUtils.h>
|
||||
#include "./enums.h"
|
||||
#include "config.h"
|
||||
|
||||
#define BRIDGE_DEBUG_REGISTER
|
||||
#define BRIDGE_DEBUG_CALL_METHOD
|
||||
#define NB_PAYLOAD_ELEMENTS 3
|
||||
|
||||
class SComponentInfo;
|
||||
SComponentInfo *componentsArray[MAX_COMPONENTS];
|
||||
Vector<SComponentInfo *> componentList;
|
||||
|
||||
Bridge::Bridge(Component *_owner) : Component("Bridge", COMPONENT_KEY_MB_BRIDGE, Component::COMPONENT_DEFAULT, _owner)
|
||||
{
|
||||
componentList.setStorage(componentsArray);
|
||||
}
|
||||
short Bridge::setup()
|
||||
{
|
||||
return E_OK;
|
||||
}
|
||||
|
||||
SComponentInfo *Bridge::hasMethod(ushort id, String methodName)
|
||||
{
|
||||
uchar s = componentList.size();
|
||||
for (uchar i = 0; i < s; i++)
|
||||
{
|
||||
SComponentInfo *val = componentList.at(i);
|
||||
if (val->key == id && val->methodName.equals(methodName))
|
||||
{
|
||||
return val;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
short Bridge::debug()
|
||||
{
|
||||
return E_OK;
|
||||
}
|
||||
short Bridge::list()
|
||||
{
|
||||
uchar s = componentList.size();
|
||||
Log.verboseln("Bridge::list - Registered methods: %d", s);
|
||||
for (uchar i = 0; i < s; i++)
|
||||
{
|
||||
SComponentInfo *val = componentList.at(i);
|
||||
Log.verboseln("\tRegistered Method: %d::%s::%s", val->key, ((Component *)val->instance)->name.c_str(), val->methodName.c_str());
|
||||
}
|
||||
return E_OK;
|
||||
}
|
||||
|
||||
SComponentInfo *Bridge::registerMemberFunction(ushort id, Component *clazz, char *method, ComponentFnPtr ptr)
|
||||
{
|
||||
#ifdef DISABLE_BRIDGE
|
||||
// #pragma message("Bridge::registerMemberFunction : Bridge is disabled!")
|
||||
return NULL;
|
||||
#endif
|
||||
#ifndef DISABLE_BRIDGE
|
||||
if (componentList.size() > MAX_COMPONENTS)
|
||||
{
|
||||
Log.errorln("Bridge::registerMemberFunction : Max components reached : %d", MAX_COMPONENTS);
|
||||
return NULL;
|
||||
}
|
||||
SComponentInfo *meth = hasMethod(id, method);
|
||||
if (meth)
|
||||
{
|
||||
#ifdef BRIDGE_DEBUG_REGISTER
|
||||
Log.verboseln("Register class member: %s::%s already registered!", clazz, method);
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
meth = new SComponentInfo(id, clazz, method, ptr);
|
||||
componentList.push_back(meth);
|
||||
}
|
||||
return meth;
|
||||
#endif
|
||||
return NULL;
|
||||
}
|
||||
|
||||
short Bridge::onMessage(int id, E_CALLS verb, E_MessageFlags flags, String user, Component *src)
|
||||
{
|
||||
|
||||
#ifdef BRIDGE_DEBUG_CALL_METHOD
|
||||
Log.verboseln("Bridge::onMessage: %d::%d::%d::%s - User: %s", id, verb, flags, src->name.c_str(), user.c_str());
|
||||
#endif
|
||||
|
||||
if (strlen(user.c_str()) == 0)
|
||||
{
|
||||
Log.verboseln("Bridge::onMessage: %d : invalid payload", id);
|
||||
return E_NOT_FOUND;
|
||||
}
|
||||
|
||||
switch (verb)
|
||||
{
|
||||
case E_CALLS::EC_USER:
|
||||
case E_CALLS::EC_NONE:
|
||||
case E_CALLS::EC_COMMAND:
|
||||
case E_CALLS::EC_FUNC:
|
||||
{
|
||||
return E_OK;
|
||||
}
|
||||
case E_CALLS::EC_METHOD:
|
||||
{
|
||||
char *strings[NB_PAYLOAD_ELEMENTS];
|
||||
char *ptr = NULL;
|
||||
byte index = 0;
|
||||
ptr = strtok(C_STR(user.c_str()), CC_STR(Bridge::METHOD_DELIMITER));
|
||||
while (ptr != NULL && index <= NB_PAYLOAD_ELEMENTS)
|
||||
{
|
||||
strings[index] = ptr;
|
||||
index++;
|
||||
ptr = strtok(NULL, CC_STR(Bridge::METHOD_DELIMITER));
|
||||
}
|
||||
char *_method = strings[0];
|
||||
if (strlen(_method) == 0)
|
||||
{
|
||||
Log.errorln("Bridge::onMessage: %d : invalid method name", id);
|
||||
return E_NOT_FOUND;
|
||||
}
|
||||
|
||||
SComponentInfo *method = hasMethod(id, _method);
|
||||
if (method)
|
||||
{
|
||||
short arg0 = convertTo<short>(CC_STR(strings[1]));
|
||||
short arg1 = convertTo<short>(CC_STR(strings[2]));
|
||||
Component *component = (Component *)method->instance;
|
||||
ComponentFnPtr ptr = method->mPtr;
|
||||
short ret = (component->*ptr)(arg0, arg1);
|
||||
#ifdef BRIDGE_DEBUG_CALL_METHOD
|
||||
Log.verboseln("Called method: %s(%d)::%s with : %d | %d = %d", component->name.c_str(), id, _method, arg0, arg1, ret);
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef BRIDGE_DEBUG_CALL_METHOD
|
||||
Log.errorln("Method not found: %d::%s - register size %d", id, _method, componentList.size());
|
||||
list();
|
||||
#endif
|
||||
}
|
||||
return E_OK;
|
||||
}
|
||||
}
|
||||
return E_NOT_FOUND;
|
||||
}
|
||||
58
src/Bridge.h
Normal file
58
src/Bridge.h
Normal file
@ -0,0 +1,58 @@
|
||||
#ifndef BRIDGE_H
|
||||
#define BRIDGE_H
|
||||
|
||||
#include "Component.h"
|
||||
#include <WString.h>
|
||||
#include <xtypes.h>
|
||||
#include <enums.h>
|
||||
#include <macros.h>
|
||||
#include <Vector.h>
|
||||
|
||||
class SComponentInfo;
|
||||
|
||||
class SComponentInfo
|
||||
{
|
||||
public:
|
||||
short key;
|
||||
void *instance;
|
||||
String methodName;
|
||||
ComponentFnPtr mPtr;
|
||||
SComponentInfo() {}
|
||||
SComponentInfo(ushort _key, void *_instance, String _methodName, ComponentFnPtr _mPtr) : key(_key),
|
||||
instance(_instance),
|
||||
methodName(_methodName),
|
||||
mPtr(_mPtr) {}
|
||||
};
|
||||
|
||||
class Bridge : public Component
|
||||
{
|
||||
public:
|
||||
Bridge(Component *_owner);
|
||||
|
||||
SComponentInfo *registerMemberFunction(
|
||||
ushort id,
|
||||
Component *clazz,
|
||||
char *method,
|
||||
ComponentFnPtr ptr);
|
||||
|
||||
SComponentInfo *hasMethod(ushort id, String method);
|
||||
short onMessage(int id, E_CALLS verb, E_MessageFlags flags, String user, Component *src);
|
||||
short list();
|
||||
// Component implementation
|
||||
short debug();
|
||||
short setup();
|
||||
|
||||
// --- Methods for ModbusManager ---
|
||||
|
||||
/**
|
||||
* @brief Retrieves a list of all registered component instances.
|
||||
* NOTE: This requires careful memory management. Consider returning pointers or references.
|
||||
* This current implementation returns pointers stored in the internal vector.
|
||||
* @return A vector of Component pointers.
|
||||
*/
|
||||
Vector<Component*> getAllComponents();
|
||||
|
||||
static constexpr char *METHOD_DELIMITER = C_STR(":");
|
||||
};
|
||||
#endif
|
||||
|
||||
92
src/CommandMessage.h
Normal file
92
src/CommandMessage.h
Normal file
@ -0,0 +1,92 @@
|
||||
#ifndef COMMAND_MESSAGE_H
|
||||
#define COMMAND_MESSAGE_H
|
||||
|
||||
#include <Vector.h>
|
||||
#include <ArduinoLog.h>
|
||||
#include <StringUtils.h>
|
||||
#include "./enums.h"
|
||||
|
||||
//#define DEBUG_MESSAGES_PARSE
|
||||
|
||||
#ifdef DEBUG_MESSAGES_PARSE
|
||||
#define DEBUG_MESSAGES_PARSER(format, ...) Log.verboseln(format, ##__VA_ARGS__)
|
||||
#else
|
||||
#define DEBUG_MESSAGES_PARSER(format, ...)
|
||||
#endif
|
||||
|
||||
#define MESSAGE_TOKENS 4
|
||||
|
||||
class CommandMessage
|
||||
{
|
||||
public:
|
||||
short id;
|
||||
E_CALLS verb;
|
||||
E_MessageFlags flags;
|
||||
String payload;
|
||||
millis_t ts;
|
||||
|
||||
static constexpr char *START_STR = C_STR("<<");
|
||||
static constexpr char *END_STR = C_STR(">>");
|
||||
static constexpr char *DELIMITER = C_STR(";");
|
||||
|
||||
static constexpr int START_LENGTH = 2;
|
||||
static constexpr int END_LENGTH = 2;
|
||||
|
||||
CommandMessage(short _id, E_CALLS _verb, E_MessageFlags _flags) : id(_id),
|
||||
verb(_verb),
|
||||
flags(_flags)
|
||||
{
|
||||
}
|
||||
|
||||
// Add default constructor
|
||||
CommandMessage() : id(0),
|
||||
verb(E_CALLS::EC_NONE),
|
||||
flags(E_MessageFlags::E_MF_NONE) {}
|
||||
|
||||
void clear()
|
||||
{
|
||||
id = 0;
|
||||
verb = E_CALLS::EC_NONE;
|
||||
flags = E_MessageFlags::E_MF_NONE;
|
||||
ts = 0;
|
||||
}
|
||||
|
||||
bool matches(String string)
|
||||
{
|
||||
return string.startsWith(CommandMessage::START_STR) && string.endsWith(CommandMessage::END_STR);
|
||||
}
|
||||
|
||||
bool parse(String message)
|
||||
{
|
||||
clear();
|
||||
String data = message.substring(CommandMessage::START_LENGTH, message.length() - CommandMessage::END_LENGTH);
|
||||
char dataBuffer[data.length() + 1];
|
||||
strcpy(dataBuffer, data.c_str());
|
||||
const char *strings[MESSAGE_TOKENS];
|
||||
char *ptr = NULL;
|
||||
byte index = 0;
|
||||
ptr = strtok(dataBuffer, CC_STR(CommandMessage::DELIMITER));
|
||||
while (ptr != NULL && index < MESSAGE_TOKENS)
|
||||
{
|
||||
strings[index] = ptr;
|
||||
index++;
|
||||
ptr = strtok(NULL, CC_STR(CommandMessage::DELIMITER));
|
||||
}
|
||||
|
||||
if (index < MESSAGE_TOKENS)
|
||||
{
|
||||
DEBUG_MESSAGES_PARSER("CommandMessage::parse: invalid message - incomplete - got %d tokens", index);
|
||||
return false;
|
||||
}
|
||||
|
||||
id = atoi(strings[0]);
|
||||
verb = (E_CALLS)convertTo<int>(strings[1]);
|
||||
flags = (E_MessageFlags)convertTo<long int>(strings[2]);
|
||||
payload = String(strings[3]);
|
||||
ts = millis();
|
||||
DEBUG_MESSAGES_PARSER("CommandMessage::parse: id: %d, verb: %d, flags: %d, payload: %s", id, verb, flags, payload.c_str());
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
2
src/Component.cpp
Normal file
2
src/Component.cpp
Normal file
@ -0,0 +1,2 @@
|
||||
#include "./Component.h"
|
||||
#include "./Bridge.h"
|
||||
457
src/Component.h
Normal file
457
src/Component.h
Normal file
@ -0,0 +1,457 @@
|
||||
#ifndef COMPONENT_H
|
||||
#define COMPONENT_H
|
||||
|
||||
#include <WString.h>
|
||||
#include <ArduinoLog.h>
|
||||
#include <Vector.h>
|
||||
#include "./enums.h"
|
||||
#include "constants.h"
|
||||
#include "error_codes.h"
|
||||
#include "macros.h"
|
||||
#include "xtypes.h"
|
||||
|
||||
|
||||
class Bridge;
|
||||
class ModbusTCP;
|
||||
class ModbusBlockView;
|
||||
class MB_Registers;
|
||||
/**
|
||||
* @brief The Component class represents a generic component.
|
||||
*/
|
||||
class Component
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief The default run flags for a component.
|
||||
*/
|
||||
static const int COMPONENT_DEFAULT = 1 << OBJECT_RUN_FLAGS::E_OF_LOOP | 1 << OBJECT_RUN_FLAGS::E_OF_SETUP;
|
||||
|
||||
/**
|
||||
* @brief The default ID for a component.
|
||||
*/
|
||||
static const ushort COMPONENT_NO_ID = 0;
|
||||
|
||||
/**
|
||||
* @brief Default constructor for the Component class.
|
||||
*/
|
||||
Component() : name("NO_NAME"), id(0),
|
||||
flags(OBJECT_RUN_FLAGS::E_OF_NONE),
|
||||
nFlags(OBJECT_NET_CAPS::E_NCAPS_NONE), owner(nullptr) {}
|
||||
|
||||
/**
|
||||
* @brief Constructor for the Component class with a specified name.
|
||||
* @param _name The name of the component.
|
||||
*/
|
||||
Component(String _name) : name(_name), id(COMPONENT_NO_ID),
|
||||
flags(OBJECT_RUN_FLAGS::E_OF_NONE),
|
||||
nFlags(OBJECT_NET_CAPS::E_NCAPS_NONE), owner(nullptr) {}
|
||||
|
||||
/**
|
||||
* @brief Constructor for the Component class with a specified name and ID.
|
||||
* @param _name The name of the component.
|
||||
* @param _id The ID of the component.
|
||||
*/
|
||||
Component(String _name, ushort _id) : name(_name),
|
||||
id(_id),
|
||||
flags(OBJECT_RUN_FLAGS::E_OF_NONE),
|
||||
nFlags(OBJECT_NET_CAPS::E_NCAPS_NONE), owner(nullptr) {}
|
||||
|
||||
/**
|
||||
* @brief Constructor for the Component class with a specified name, ID, and flags.
|
||||
* @param _name The name of the component.
|
||||
* @param _id The ID of the component.
|
||||
* @param _flags The run flags for the component.
|
||||
*/
|
||||
Component(String _name, short _id, int _flags) : name(_name),
|
||||
id(_id),
|
||||
flags(_flags),
|
||||
nFlags(OBJECT_NET_CAPS::E_NCAPS_NONE), owner(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Constructor for the Component class with a specified name, ID, flags, and owner.
|
||||
* @param _name The name of the component.
|
||||
* @param _id The ID of the component.
|
||||
* @param _flags The run flags for the component.
|
||||
* @param _owner The owner of the component.
|
||||
*/
|
||||
Component(String _name, ushort _id, uint16_t _flags, Component *_owner) : name(_name),
|
||||
id(_id),
|
||||
flags(_flags),
|
||||
owner(_owner),
|
||||
nFlags(OBJECT_NET_CAPS::E_NCAPS_NONE) {}
|
||||
|
||||
/**
|
||||
* @brief Destructor for the Component class.
|
||||
*/
|
||||
virtual ~Component() = default;
|
||||
|
||||
/**
|
||||
* @brief Virtual function to destroy the component.
|
||||
* @return The error code indicating the success or failure of the operation.
|
||||
*/
|
||||
virtual short destroy() { return E_OK; };
|
||||
|
||||
/**
|
||||
* @brief Virtual function to debug the component.
|
||||
* @param stream The stream to output the debug information to.
|
||||
* @return The error code indicating the success or failure of the operation.
|
||||
*/
|
||||
virtual short debug() { return E_OK; };
|
||||
|
||||
/**
|
||||
* @brief Virtual function to debug the component.
|
||||
* @param stream The stream to output the debug information to.
|
||||
* @return The error code indicating the success or failure of the operation.
|
||||
*/
|
||||
virtual short debug(short val0, short val1) { return E_OK; };
|
||||
|
||||
/**
|
||||
* @brief Virtual function to display information about the component.
|
||||
* @param stream The stream to output the information to.
|
||||
* @return The error code indicating the success or failure of the operation.
|
||||
*/
|
||||
virtual short info() { return E_OK; };
|
||||
|
||||
/**
|
||||
* @brief Virtual function to display information about the component.
|
||||
* @param stream The stream to output the information to.
|
||||
* @return The error code indicating the success or failure of the operation.
|
||||
*/
|
||||
virtual short info(short val0, short val1) { return E_OK; };
|
||||
|
||||
/**
|
||||
* @brief Virtual function to set up the component.
|
||||
* @return The error code indicating the success or failure of the operation.
|
||||
*/
|
||||
virtual short setup() { return E_OK; };
|
||||
|
||||
/**
|
||||
* @brief Virtual function to run the component in a loop.
|
||||
* @return The error code indicating the success or failure of the operation.
|
||||
*/
|
||||
virtual short loop() { return E_OK; };
|
||||
|
||||
/**
|
||||
* @brief Checks if the component has a specific flag.
|
||||
* @param flag The flag to check.
|
||||
* @return True if the component has the flag, false otherwise.
|
||||
*/
|
||||
bool hasFlag(byte flag)
|
||||
{
|
||||
return TEST(flags, flag);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets a specific flag for the component.
|
||||
* @param flag The flag to set.
|
||||
*/
|
||||
void setFlag(byte flag)
|
||||
{
|
||||
SBI(flags, flag);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets a specific flag for the component.
|
||||
* @param flag The flag to set.
|
||||
*/
|
||||
short toggleFlag(short flag, short value)
|
||||
{
|
||||
if (value)
|
||||
{
|
||||
SBI(flags, flag);
|
||||
}
|
||||
else
|
||||
{
|
||||
CBI(flags, flag);
|
||||
}
|
||||
return flags;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Clears a specific flag for the component.
|
||||
* @param flag The flag to clear.
|
||||
*/
|
||||
void clearFlag(byte flag)
|
||||
{
|
||||
CBI(flags, flag);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Enables the component.
|
||||
*/
|
||||
void enable()
|
||||
{
|
||||
clearFlag(OBJECT_RUN_FLAGS::E_OF_DISABLED);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Disables the component.
|
||||
*/
|
||||
void disable()
|
||||
{
|
||||
setFlag(OBJECT_RUN_FLAGS::E_OF_DISABLED);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Checks if the component is enabled.
|
||||
* @return True if the component is enabled, false otherwise.
|
||||
*/
|
||||
bool enabled()
|
||||
{
|
||||
return !hasFlag(OBJECT_RUN_FLAGS::E_OF_DISABLED);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief The name of the component.
|
||||
*/
|
||||
String name;
|
||||
|
||||
/**
|
||||
* @brief The ID of the component.
|
||||
*/
|
||||
const ushort id;
|
||||
|
||||
/**
|
||||
* @brief The run flags for the component.
|
||||
*/
|
||||
uint16_t flags;
|
||||
|
||||
/**
|
||||
* @brief The network capabilities of the component.
|
||||
*/
|
||||
uint16_t nFlags;
|
||||
|
||||
/**
|
||||
* @brief The owner of the component.
|
||||
*/
|
||||
Component *owner;
|
||||
|
||||
/**
|
||||
* @brief The current time in milliseconds.
|
||||
*/
|
||||
millis_t now;
|
||||
|
||||
/**
|
||||
* @brief The last tick time in milliseconds.
|
||||
*/
|
||||
millis_t last;
|
||||
|
||||
//////////////////////////////////////////
|
||||
//
|
||||
// Component Hierarchy / Lookup
|
||||
//
|
||||
|
||||
/**
|
||||
* @brief Virtual method to retrieve a component managed by this component (or its children) by ID.
|
||||
* The base implementation returns nullptr.
|
||||
* Owners like PHApp should override this to provide actual lookup.
|
||||
* @param id The ID of the component to find.
|
||||
* @return Pointer to the component if found, nullptr otherwise.
|
||||
*/
|
||||
virtual Component* getComponent(short id) { return nullptr; }
|
||||
|
||||
//////////////////////////////////////////
|
||||
//
|
||||
// Messaging
|
||||
// @todo: extract to a separate class
|
||||
|
||||
/**
|
||||
* @brief Handles incoming messages.
|
||||
*
|
||||
* This function is called when a message is received by the component.
|
||||
* It processes the message and returns a short value indicating the status of the operation.
|
||||
*
|
||||
* @param id The ID of the message.
|
||||
* @param verb The type of operation to be performed.
|
||||
* @param flags The flags associated with the message.
|
||||
* @param user A pointer to user-defined data.
|
||||
* @param src The source component that sent the message.
|
||||
* @return A short value indicating the status of the operation.
|
||||
*/
|
||||
virtual short onMessage(int id, E_CALLS verb, E_MessageFlags flags, String user, Component *src)
|
||||
{
|
||||
return E_OK;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Handles incoming messages with a generic void* payload.
|
||||
*
|
||||
* @param id The ID of the message.
|
||||
* @param verb The type of operation to be performed.
|
||||
* @param flags The flags associated with the message.
|
||||
* @param user A pointer to user-defined data (nullptr if not provided).
|
||||
* @param src The source component that sent the message (nullptr if not provided).
|
||||
* @return A short value indicating the status of the operation.
|
||||
*/
|
||||
virtual short onMessage(int id, E_CALLS verb, E_MessageFlags flags, void* user = nullptr, Component *src = nullptr)
|
||||
{
|
||||
return E_OK;
|
||||
};
|
||||
/**
|
||||
* @brief Handles errors.
|
||||
* @param id The ID of the error.
|
||||
* @param error The error code.
|
||||
* @return The error code indicating the success or failure of the operation.
|
||||
*/
|
||||
virtual short onError(short id, short error) { return E_OK; };
|
||||
|
||||
/**
|
||||
* @brief Handles responses.
|
||||
* @param id The ID of the response.
|
||||
* @param response The response code.
|
||||
* @return The response code indicating the success or failure of the operation.
|
||||
*/
|
||||
virtual short onResponse(short id, short response) { return E_OK; };
|
||||
|
||||
//////////////////////////////////////////
|
||||
//
|
||||
// Binding
|
||||
|
||||
/**
|
||||
* Registers methods for the component with the specified bridge.
|
||||
* This method should be overridden by derived classes to provide custom method registration logic.
|
||||
*
|
||||
* @param bridge The bridge to register methods with.
|
||||
* @return The status code indicating the success or failure of the method registration.
|
||||
*/
|
||||
virtual short serial_register(Bridge *bridge) { return E_OK; }
|
||||
|
||||
/**
|
||||
* @brief Sets a specific network capability flag for the component.
|
||||
* @param flag The network capability flag to set (from OBJECT_NET_CAPS).
|
||||
*/
|
||||
void setNetCapability(OBJECT_NET_CAPS flag)
|
||||
{
|
||||
SBI(nFlags, flag);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Checks if the component has a specific network capability flag.
|
||||
* @param flag The network capability flag to check (from OBJECT_NET_CAPS).
|
||||
* @return True if the component has the capability, false otherwise.
|
||||
*/
|
||||
bool hasNetCapability(OBJECT_NET_CAPS flag) const
|
||||
{
|
||||
return TEST(nFlags, flag);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Clears a specific network capability flag for the component.
|
||||
* @param flag The network capability flag to clear (from OBJECT_NET_CAPS).
|
||||
*/
|
||||
void clearNetCapability(OBJECT_NET_CAPS flag)
|
||||
{
|
||||
CBI(nFlags, flag);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////
|
||||
//
|
||||
// Network Interface (Modbus, Serial, CAN, etc.)
|
||||
//
|
||||
|
||||
/**
|
||||
* @brief Called by a network manager (e.g., ModbusTCP) to write a value to this component.
|
||||
* Derived classes should implement this to handle incoming network writes specific to their function.
|
||||
* @param address The specific Modbus address being written to within the component's range.
|
||||
* @param value The value received from the network.
|
||||
* @return E_OK on success, or an appropriate error code.
|
||||
*/
|
||||
virtual short mb_tcp_write(short address, short value) {
|
||||
return 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Variant of mb_tcp_write accepting MB_Registers context.
|
||||
* @param reg The MB_Registers block associated with this write request.
|
||||
* @param value The value received from the network.
|
||||
* @return E_OK on success, or an appropriate error code.
|
||||
*/
|
||||
virtual short mb_tcp_write(MB_Registers * reg, short value) {
|
||||
return 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Called by a network manager (e.g., ModbusTCP) to read a value from this component.
|
||||
* Derived classes should implement this to provide their current state to the network.
|
||||
* @param address The specific Modbus address being read within the component's range.
|
||||
* @return The current value for the given address, or potentially an error indicator.
|
||||
*/
|
||||
virtual short mb_tcp_read(short address) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Variant of mb_tcp_read accepting MB_Registers context.
|
||||
* @param reg The MB_Registers block associated with this read request.
|
||||
* @return The current value for the register block, or potentially an error indicator.
|
||||
*/
|
||||
virtual short mb_tcp_read(MB_Registers * reg) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the last error code
|
||||
*/
|
||||
virtual ushort mb_tcp_error(MB_Registers * reg) { return 0; }
|
||||
|
||||
/**
|
||||
* @brief Called during setup to allow the component to register its Modbus blocks.
|
||||
*
|
||||
* Derived classes should override this. It's recommended to call mb_tcp_blocks()
|
||||
* inside this function, iterate through the returned view, add the runtime
|
||||
* component ID to each MB_Registers struct, and then register it with the manager.
|
||||
*
|
||||
* @param manager Pointer to the ModbusTCP instance.
|
||||
*/
|
||||
virtual void mb_tcp_register(ModbusTCP* manager) const {
|
||||
// Base implementation does nothing.
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets a view of the static Modbus block definitions for this component type.
|
||||
*
|
||||
* @note The componentId field within the returned MB_Registers structs may not be
|
||||
* populated, as the definitions are typically static/constexpr.
|
||||
* Use mb_tcp_register to handle registration with the correct runtime ID.
|
||||
*
|
||||
* @return A ModbusBlockView describing the blocks handled by this component type.
|
||||
* Default implementation returns an empty view {nullptr, 0}.
|
||||
*/
|
||||
virtual ModbusBlockView* mb_tcp_blocks() const { return nullptr; }
|
||||
|
||||
/**
|
||||
* @brief The Modbus slave ID for this component (satisfies the Modbus interfaces)
|
||||
*/
|
||||
ushort slaveId;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* @brief Called by derived classes when their internal state changes in a way that should be reflected on the network.
|
||||
* The base class (or a network manager observing this) should handle queuing the update.
|
||||
*/
|
||||
virtual void notifyStateChange() {
|
||||
// Base implementation could potentially interact with a NetworkManager singleton/instance
|
||||
// Log.verboseln("Component::notifyStateChange - ID %d", id);
|
||||
}
|
||||
|
||||
public:
|
||||
//////////////////////////////////////////
|
||||
//
|
||||
// Component Hierarchy / Lookup
|
||||
virtual Component *byId(ushort id) { return nullptr; }
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Function pointer type for component member functions.
|
||||
*/
|
||||
typedef short (Component::*ComponentFnPtr)(short, short);
|
||||
/**
|
||||
* @brief Function pointer type for component member functions with variable arguments.
|
||||
*/
|
||||
typedef short (Component::*ComponentVarArgsFnPtr)(...);
|
||||
|
||||
typedef short (Component::*ComponentRxFn)(short size, uint8_t rxBuffer[]);
|
||||
|
||||
#endif
|
||||
154
src/Logger.h
Normal file
154
src/Logger.h
Normal file
@ -0,0 +1,154 @@
|
||||
#ifndef CIRCULAR_LOG_PRINTER_H
|
||||
#define CIRCULAR_LOG_PRINTER_H
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <Print.h>
|
||||
#include <esp_log.h>
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Configuration macros ---------------------------------------------------------
|
||||
// • LOG_BUFFER_LINES – number of lines kept in RAM (default 100)
|
||||
// • LOG_BUFFER_LINE_LENGTH – max chars per line incl. null (default 120)
|
||||
// • LOG_BUFFER_THREAD_SAFE – 1 = wrap writes in a FreeRTOS critical section
|
||||
// -----------------------------------------------------------------------------
|
||||
#ifndef LOG_BUFFER_LINES
|
||||
#define LOG_BUFFER_LINES 100
|
||||
#endif
|
||||
|
||||
#ifndef LOG_BUFFER_LINE_LENGTH
|
||||
#define LOG_BUFFER_LINE_LENGTH 120
|
||||
#endif
|
||||
|
||||
#ifndef LOG_BUFFER_THREAD_SAFE
|
||||
#define LOG_BUFFER_THREAD_SAFE 1
|
||||
#endif
|
||||
|
||||
using LogRingBuffer = char[LOG_BUFFER_LINES][LOG_BUFFER_LINE_LENGTH];
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// CircularLogPrinter -----------------------------------------------------------
|
||||
// Stores the last N lines in a ring buffer and optionally mirrors everything to
|
||||
// an arbitrary Print stream. 100 % reinterpret‑cast‑free for MISRA/CPPCHECK
|
||||
// compliance: the code never converts between pointer types, only between
|
||||
// scalar values.
|
||||
// -----------------------------------------------------------------------------
|
||||
class CircularLogPrinter final : public Print {
|
||||
public:
|
||||
explicit CircularLogPrinter(Print* out = &Serial) : _out(out) { clear(); }
|
||||
|
||||
void setOutput(Print* out) { _out = out; }
|
||||
|
||||
void clear() {
|
||||
memset(_buf, 0, sizeof(_buf));
|
||||
_head = 0;
|
||||
_filled = 0;
|
||||
_idx = 0;
|
||||
}
|
||||
|
||||
const char* getLine(size_t i) const {
|
||||
if (i >= lines()) return nullptr;
|
||||
size_t index = (_head + LOG_BUFFER_LINES - i - 1U) % LOG_BUFFER_LINES;
|
||||
return _buf[index];
|
||||
}
|
||||
|
||||
size_t lines() const { return (_filled < LOG_BUFFER_LINES) ? _filled : LOG_BUFFER_LINES; }
|
||||
|
||||
// Redirect ESP‑IDF logging macros (ESP_LOGx) into this printer
|
||||
void attachToEspLog() { esp_log_set_vprintf(&vprintfShim); }
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// Print single byte
|
||||
// ---------------------------------------------------------------------
|
||||
size_t write(uint8_t c) override {
|
||||
#if LOG_BUFFER_THREAD_SAFE
|
||||
portENTER_CRITICAL(&_mux);
|
||||
#endif
|
||||
size_t res = writeByte(static_cast<char>(c));
|
||||
#if LOG_BUFFER_THREAD_SAFE
|
||||
portEXIT_CRITICAL(&_mux);
|
||||
#endif
|
||||
return res;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// Faster multi‑byte write – no pointer casting
|
||||
// ---------------------------------------------------------------------
|
||||
size_t write(const uint8_t* data, size_t size) override {
|
||||
#if LOG_BUFFER_THREAD_SAFE
|
||||
portENTER_CRITICAL(&_mux);
|
||||
#endif
|
||||
size_t i = 0U;
|
||||
while (i < size) {
|
||||
if (static_cast<char>(data[i]) == '\n') {
|
||||
commitLine();
|
||||
++i; // skip newline
|
||||
continue;
|
||||
}
|
||||
appendChar(static_cast<char>(data[i++]));
|
||||
}
|
||||
#if LOG_BUFFER_THREAD_SAFE
|
||||
portEXIT_CRITICAL(&_mux);
|
||||
#endif
|
||||
return size;
|
||||
}
|
||||
|
||||
private:
|
||||
// ---------------------------------------------------------------------
|
||||
// ESP‑IDF vprintf shim – feeds characters individually (no cast needed)
|
||||
// ---------------------------------------------------------------------
|
||||
static int vprintfShim(const char* fmt, va_list args) {
|
||||
char tmp[LOG_BUFFER_LINE_LENGTH];
|
||||
int len = vsnprintf(tmp, sizeof(tmp), fmt, args);
|
||||
if (len < 0) return len;
|
||||
auto& logger = instance();
|
||||
for (int i = 0; i < len; ++i) {
|
||||
logger.write(static_cast<uint8_t>(tmp[i]));
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
static CircularLogPrinter& instance() {
|
||||
static CircularLogPrinter logger;
|
||||
return logger;
|
||||
}
|
||||
|
||||
void appendChar(char c) {
|
||||
if (_idx < LOG_BUFFER_LINE_LENGTH - 1U) {
|
||||
_line[_idx++] = c;
|
||||
}
|
||||
}
|
||||
|
||||
void commitLine() {
|
||||
_line[_idx] = '\0';
|
||||
if (_out) _out->println(_line);
|
||||
strncpy(_buf[_head], _line, LOG_BUFFER_LINE_LENGTH);
|
||||
_head = (_head + 1U) % LOG_BUFFER_LINES;
|
||||
if (_filled < LOG_BUFFER_LINES) ++_filled;
|
||||
_idx = 0U;
|
||||
}
|
||||
|
||||
size_t writeByte(char c) {
|
||||
if (c == '\r') return 1U; // ignore CR
|
||||
if (c == '\n') {
|
||||
commitLine();
|
||||
} else {
|
||||
appendChar(c);
|
||||
}
|
||||
return 1U;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// Members
|
||||
// ---------------------------------------------------------------------
|
||||
LogRingBuffer _buf{};
|
||||
Print* _out = nullptr;
|
||||
size_t _head = 0U;
|
||||
size_t _filled = 0U;
|
||||
char _line[LOG_BUFFER_LINE_LENGTH];
|
||||
size_t _idx = 0U;
|
||||
#if LOG_BUFFER_THREAD_SAFE
|
||||
portMUX_TYPE _mux = portMUX_INITIALIZER_UNLOCKED;
|
||||
#endif
|
||||
};
|
||||
|
||||
#endif // CIRCULAR_LOG_PRINTER_H
|
||||
20
src/Polymech-Base.h
Normal file
20
src/Polymech-Base.h
Normal file
@ -0,0 +1,20 @@
|
||||
#ifndef _osr_base_h
|
||||
#define _osr_base_h
|
||||
|
||||
#if defined(ARDUINO) && ARDUINO >= 100
|
||||
#include "arduino.h"
|
||||
#else
|
||||
#include "WProgram.h"
|
||||
#endif
|
||||
|
||||
int init_osr_base();
|
||||
|
||||
#endif
|
||||
|
||||
#include "Component.h"
|
||||
#include "Addon.h"
|
||||
#include "App.h"
|
||||
#include "constants.h"
|
||||
#include "macros.h"
|
||||
#include "xtypes.h"
|
||||
|
||||
100
src/SRegister.h
Normal file
100
src/SRegister.h
Normal file
@ -0,0 +1,100 @@
|
||||
#ifndef SREGISTER_H
|
||||
#define SREGISTER_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
template <typename T, uint8_t MAX_LENGTH>
|
||||
class ShiftRegister {
|
||||
private:
|
||||
T data;
|
||||
uint8_t length;
|
||||
uint8_t position;
|
||||
int mapping[MAX_LENGTH];
|
||||
|
||||
public:
|
||||
ShiftRegister() : data(0), length(0), position(0) {
|
||||
for (uint8_t i = 0; i < MAX_LENGTH; i++) {
|
||||
mapping[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
ShiftRegister(const int (&_mapping)[MAX_LENGTH]) : data(0), length(0), position(0) {
|
||||
for (uint8_t i = 0; i < MAX_LENGTH; i++) {
|
||||
mapping[i] = _mapping[i];
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t add(T newData){
|
||||
data = (data << 1) | (newData & 1);
|
||||
length = length == MAX_LENGTH ? MAX_LENGTH : length + 1;
|
||||
return position;
|
||||
}
|
||||
|
||||
void map(uint8_t pos, int value) {
|
||||
mapping[pos] = value;
|
||||
}
|
||||
|
||||
int val() const {
|
||||
return mapping[position];
|
||||
}
|
||||
|
||||
uint8_t incr() {
|
||||
position = (position + 1) % MAX_LENGTH;
|
||||
data = (data << 1) | ((data >> (MAX_LENGTH - 1)) & 1);
|
||||
return position;
|
||||
}
|
||||
|
||||
uint8_t decr() {
|
||||
position = (position == 0) ? MAX_LENGTH - 1 : position - 1;
|
||||
data = (data >> 1) | ((data & 1) << (MAX_LENGTH - 1));
|
||||
return position;
|
||||
}
|
||||
|
||||
uint8_t pos() const {
|
||||
return position;
|
||||
}
|
||||
|
||||
uint8_t reset() {
|
||||
data = 0;
|
||||
length = 0;
|
||||
position = 0;
|
||||
return position;
|
||||
}
|
||||
|
||||
uint8_t move(int direction, int steps) {
|
||||
for (int i = 0; i < steps; i++) {
|
||||
if (direction > 0) {
|
||||
incr();
|
||||
} else if (direction < 0) {
|
||||
decr();
|
||||
}
|
||||
}
|
||||
return position;
|
||||
}
|
||||
|
||||
bool isEnd() const {
|
||||
return position == (length - 1) % MAX_LENGTH;
|
||||
}
|
||||
|
||||
ShiftRegister& operator++() {
|
||||
incr();
|
||||
return *this;
|
||||
}
|
||||
|
||||
ShiftRegister& operator--() {
|
||||
decr();
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
Write a class, for C++, implementing a shift register
|
||||
- as template, to specify the type for the storage, eg: int, unsigned int, ...
|
||||
- implement this methods : incr, decr, position, reset, move(int direction, int steps), isEnd
|
||||
- let me specify the max. length of the register
|
||||
- dont use std, at all
|
||||
- use bit shift operators
|
||||
- return the current position in all methods, using uint8_t as return type
|
||||
*/
|
||||
#endif
|
||||
119
src/SerialMessage.cpp
Normal file
119
src/SerialMessage.cpp
Normal file
@ -0,0 +1,119 @@
|
||||
#include <Vector.h>
|
||||
#include <ArduinoLog.h>
|
||||
|
||||
#include "macros.h"
|
||||
#include <constants.h>
|
||||
#include <xtypes.h>
|
||||
|
||||
#include "SerialMessage.h"
|
||||
#include "CommandMessage.h"
|
||||
#include "bridge.h"
|
||||
|
||||
void printStringAsHex(const char *str)
|
||||
{
|
||||
Serial.print(" :: ");
|
||||
for (int i = 0; str[i] != '\0'; i++)
|
||||
{
|
||||
Serial.print("0x");
|
||||
if (str[i] < 0x10)
|
||||
{
|
||||
Serial.print("0");
|
||||
}
|
||||
Serial.print(str[i], HEX);
|
||||
Serial.print(" ");
|
||||
}
|
||||
Serial.println(" :: ");
|
||||
}
|
||||
|
||||
#ifndef SERIAL_COMMAND_PARSE_INTERVAL
|
||||
#define SERIAL_COMMAND_PARSE_INTERVAL 50
|
||||
#endif
|
||||
|
||||
// #define DEBUG_SERIAL_MESSAGES
|
||||
|
||||
#ifdef DEBUG_SERIAL_MESSAGES
|
||||
#define _DEBUG_MESSAGE_HANDLING(format, ...) Log.verboseln(format, ##__VA_ARGS__)
|
||||
#else
|
||||
#define _DEBUG_MESSAGE_HANDLING(format, ...)
|
||||
#endif
|
||||
|
||||
// static CommandMessage *_messages[10]; // Removed unused static array
|
||||
|
||||
short SerialMessage::setup()
|
||||
{
|
||||
// messages.setStorage(_messages); // Removed - uses deleted static array
|
||||
// msg = new CommandMessage(0, E_CALLS::EC_NONE, E_MessageFlags::E_MF_NONE); // Removed - msg is now an object member
|
||||
return E_OK;
|
||||
}
|
||||
|
||||
// Removed unused parse method implementation
|
||||
// CommandMessage *SerialMessage::parse(char *string)
|
||||
// {
|
||||
// return NULL;
|
||||
// }
|
||||
|
||||
short SerialMessage::debug()
|
||||
{
|
||||
return E_OK;
|
||||
}
|
||||
|
||||
String SerialMessage::readStringFromSerial()
|
||||
{
|
||||
String message;
|
||||
while (stream.available())
|
||||
{
|
||||
message = stream.readString();
|
||||
message.trim();
|
||||
}
|
||||
return message;
|
||||
}
|
||||
|
||||
CommandMessage *SerialMessage::read()
|
||||
{
|
||||
String message = readStringFromSerial();
|
||||
if (!message.length())
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
_DEBUG_MESSAGE_HANDLING("SerialMessage::read: message: %s", message.c_str());
|
||||
// Use the member 'msg' object directly
|
||||
if (!this->msg.matches(message.c_str()))
|
||||
{
|
||||
_DEBUG_MESSAGE_HANDLING("SerialMessage::read : Invalid message - no match : %s", message.c_str());
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool validMessage = this->msg.parse(message);
|
||||
if (!validMessage)
|
||||
{
|
||||
_DEBUG_MESSAGE_HANDLING("SerialMessage::read : invalid message - incomplete : %s", message.c_str());
|
||||
return NULL;
|
||||
}
|
||||
// Return address of the member object
|
||||
return &this->msg;
|
||||
}
|
||||
|
||||
short SerialMessage::loop()
|
||||
{
|
||||
if (now - lastRead < SERIAL_COMMAND_PARSE_INTERVAL)
|
||||
{
|
||||
return E_OK;
|
||||
}
|
||||
lastRead = now;
|
||||
// Use the pointer returned by read()
|
||||
CommandMessage *parsedMsg = read();
|
||||
if (parsedMsg)
|
||||
{
|
||||
_DEBUG_MESSAGE_HANDLING("SerialMessage::loop:received message %d :: %s", parsedMsg->id, parsedMsg->payload.c_str());
|
||||
if (owner)
|
||||
{
|
||||
// Pass the pointer to the parsed message (which is &this->msg)
|
||||
owner->onMessage(parsedMsg->id, parsedMsg->verb, parsedMsg->flags, parsedMsg->payload, this);
|
||||
}
|
||||
else
|
||||
{
|
||||
_DEBUG_MESSAGE_HANDLING("SerialMessage::loop: have no owner");
|
||||
}
|
||||
}
|
||||
return E_OK;
|
||||
}
|
||||
92
src/SerialMessage.h
Normal file
92
src/SerialMessage.h
Normal file
@ -0,0 +1,92 @@
|
||||
#ifndef SERIAL_MESSAGE_H
|
||||
#define SERIAL_MESSAGE_H
|
||||
|
||||
#include <Vector.h>
|
||||
#include <ArduinoLog.h>
|
||||
#include <Arduino.h> // Add for Stream, String etc. if not implicit
|
||||
|
||||
#include "xtypes.h"
|
||||
#include "Component.h"
|
||||
#include "CommandMessage.h"
|
||||
#include "config.h"
|
||||
|
||||
#ifndef SERIAL_RX_BUFFER_SIZE
|
||||
#define SERIAL_RX_BUFFER_SIZE 256
|
||||
#endif
|
||||
|
||||
class CommandMessage;
|
||||
/**
|
||||
* @class SerialMessage
|
||||
* @brief Represents a serial message component.
|
||||
*
|
||||
* The SerialMessage class is a component that handles serial communication.
|
||||
* It provides methods for reading, parsing, and handling command messages.
|
||||
*/
|
||||
class SerialMessage : public Component
|
||||
{
|
||||
public:
|
||||
|
||||
/**
|
||||
* @brief Constructs a SerialMessage object.
|
||||
*
|
||||
* @param _stream The serial stream used for communication.
|
||||
* @param _owner The owner component of this SerialMessage.
|
||||
*/
|
||||
SerialMessage(Stream &_stream, Component *_owner) : Component("SerialMessage", COMPONENT_KEY_MB_SERIAL, Component::COMPONENT_DEFAULT, _owner),
|
||||
stream(_stream),
|
||||
_rxBufferIdx(0) // Initialize buffer index
|
||||
{
|
||||
lastRead = 0;
|
||||
_rxBuffer[0] = '\0'; // Initialize buffer (Use single quotes for single char)
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Reads a command message from the serial stream.
|
||||
*
|
||||
* @return A pointer to the CommandMessage object read from the stream.
|
||||
*/
|
||||
CommandMessage *read(); // Removed - logic moved to loop()
|
||||
|
||||
/**
|
||||
* @brief Parses a string into a CommandMessage object.
|
||||
*
|
||||
* @param string The string to be parsed.
|
||||
* @return A pointer to the parsed CommandMessage object.
|
||||
*/
|
||||
CommandMessage *parse(char *string); // Removed unused method
|
||||
|
||||
/**
|
||||
* @brief Executes the main loop of the SerialMessage component.
|
||||
*
|
||||
* @return A status code indicating the result of the loop execution.
|
||||
*/
|
||||
short loop();
|
||||
/**
|
||||
* @brief Performs the setup operations for the SerialMessage component.
|
||||
*
|
||||
* @return A status code indicating the result of the setup.
|
||||
*/
|
||||
short setup();
|
||||
|
||||
/**
|
||||
* @brief Performs debug operations for the SerialMessage component.
|
||||
* @return A status code indicating the result of the debug operation.
|
||||
*/
|
||||
short debug();
|
||||
|
||||
private:
|
||||
// Private members and methods
|
||||
// Vector<CommandMessage *> messages; // Removed unused vector
|
||||
millis_t lastRead;
|
||||
char _rxBuffer[SERIAL_RX_BUFFER_SIZE]; // Receive buffer
|
||||
uint8_t _rxBufferIdx; // Current index in buffer
|
||||
bool _serialConnected = false; // Track connection status
|
||||
|
||||
protected:
|
||||
Stream &stream;
|
||||
CommandMessage msg; // Removed - Will create local instance in loop
|
||||
String readStringFromSerial(); // Removed - logic moved to loop()
|
||||
char _delimiter = '\n'; // Message delimiter (Use single quotes)
|
||||
};
|
||||
|
||||
#endif
|
||||
76
src/StringUtils.cpp
Normal file
76
src/StringUtils.cpp
Normal file
@ -0,0 +1,76 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <ctype.h>
|
||||
#include <string.h>
|
||||
#include "StringUtils.h"
|
||||
#include "xtypes.h"
|
||||
|
||||
|
||||
// Specialization for int
|
||||
template<> int convertTo<int>(cchar* str) {
|
||||
return atoi(str);
|
||||
}
|
||||
template<> short convertTo<short>(cchar* str) {
|
||||
return atoi(str);
|
||||
}
|
||||
|
||||
// Specialization for long int
|
||||
template<> long int convertTo<long int>(cchar* str) {
|
||||
return strtol(str, nullptr, 10);
|
||||
}
|
||||
|
||||
// Specialization for long long int
|
||||
/*
|
||||
template<> long long int convertTo<long long int>(cchar* str) {
|
||||
return strtoll(str, nullptr, 10);
|
||||
}
|
||||
*/
|
||||
|
||||
// Specialization for float
|
||||
template<> float convertTo<float>(cchar* str) {
|
||||
return (float)atof(str);
|
||||
}
|
||||
|
||||
// Specialization for bool
|
||||
template<> bool convertTo<bool>(cchar* str) {
|
||||
return (strcmp(str, "true") == 0 || strcmp(str, "false") == 0 || strcmp(str, "1") == 0 || strcmp(str, "0") == 0 || strcmp(str, "on") == 0 || strcmp(str, "off") == 0);
|
||||
}
|
||||
|
||||
// Specializations for int, long long int, float, bool, and possibly other types...
|
||||
|
||||
// Function to detect if a string is a valid integer
|
||||
bool isInteger(cchar* str) {
|
||||
if (*str == '-' || *str == '+') str++; // Skip sign
|
||||
while (*str) {
|
||||
if (!isdigit(*str)) return false; // Check if all characters are digits
|
||||
str++;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Function to detect if a string is a valid float
|
||||
bool isFloat(cchar* str) {
|
||||
bool hasPeriod = false;
|
||||
if (*str == '-' || *str == '+') str++; // Skip sign
|
||||
while (*str) {
|
||||
if (*str == '.') {
|
||||
if (hasPeriod) return false; // Only one period allowed
|
||||
hasPeriod = true;
|
||||
} else if (!isdigit(*str)) {
|
||||
return false; // All other characters must be digits
|
||||
}
|
||||
str++;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
E_VALUE_TYPE detectType(cchar* str) {
|
||||
if (isInteger(str)) {
|
||||
return TYPE_INT;
|
||||
} else if (isFloat(str)) {
|
||||
return TYPE_FLOAT;
|
||||
}else if (*str != '\0') { // Non-empty string considered as TYPE_STRING
|
||||
return TYPE_STRING;
|
||||
}
|
||||
return TYPE_UNKNOWN;
|
||||
}
|
||||
54
src/StringUtils.h
Normal file
54
src/StringUtils.h
Normal file
@ -0,0 +1,54 @@
|
||||
#ifndef STRINGUTILS_H
|
||||
#define STRINGUTILS_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "./xtypes.h"
|
||||
|
||||
// stricmp() is not available in Visual Studio 2013 and earlier
|
||||
# if defined(_MSC_VER)
|
||||
#define strtok_r strtok_s
|
||||
# ifndef _CRT_SECURE_NO_DEPRECATE
|
||||
# define _CRT_SECURE_NO_DEPRECATE (1)
|
||||
# endif
|
||||
# pragma warning(disable : 4996)
|
||||
# endif
|
||||
|
||||
typedef enum E_VALUE_TYPE {
|
||||
TYPE_INT,
|
||||
TYPE_FLOAT,
|
||||
TYPE_BOOL,
|
||||
TYPE_STRING,
|
||||
TYPE_UNKNOWN
|
||||
} E_VALUE_TYPE;
|
||||
|
||||
|
||||
// Function to convert a string to a native type
|
||||
template<typename T> T convertTo(cchar* str);
|
||||
|
||||
// Specialization for int
|
||||
template<> int convertTo<int>(cchar* str);
|
||||
|
||||
|
||||
// Specialization for long int
|
||||
template<> long int convertTo<long int>(cchar* str);
|
||||
|
||||
// Specialization for long long int
|
||||
//template<> long long int convertTo<long long int>(cchar* str);
|
||||
|
||||
|
||||
|
||||
// Specialization for float
|
||||
template<> float convertTo<float>(cchar* str);
|
||||
|
||||
// Specialization for bool
|
||||
template<> bool convertTo<bool>(cchar* str);
|
||||
|
||||
// Function to detect if a string is a valid integer
|
||||
bool isInteger(cchar* str);
|
||||
// Function to detect if a string is a valid float
|
||||
bool isFloat(cchar* str);
|
||||
|
||||
E_VALUE_TYPE detectType(cchar* str);
|
||||
|
||||
#endif
|
||||
7
src/constants.h
Normal file
7
src/constants.h
Normal file
@ -0,0 +1,7 @@
|
||||
#ifndef CONSTANTS_H
|
||||
#define CONSTANTS_H
|
||||
|
||||
#define MAX_COMPONENTS 30
|
||||
#define DEFAULT_DEBUG_INTERVAL 1000
|
||||
|
||||
#endif
|
||||
160
src/enums.h
Normal file
160
src/enums.h
Normal file
@ -0,0 +1,160 @@
|
||||
#ifndef ENUMS_H
|
||||
#define ENUMS_H
|
||||
|
||||
typedef enum COMPONENT_KEY_BASE
|
||||
{
|
||||
COMPONENT_KEY_NONE = 0,
|
||||
COMPONENT_KEY_APP = 1,
|
||||
COMPONENT_KEY_MB_SERIAL = 2,
|
||||
COMPONENT_KEY_MB_BRIDGE = 3
|
||||
} COMPONENT_KEY_BASE;
|
||||
|
||||
//////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Error Codes
|
||||
|
||||
#define E_OK 0 //all good
|
||||
#define E_SKIP 10 //all good
|
||||
#define E_QUEUED 20 //all good
|
||||
#define E_INVALID_PARAMETER 30 //all good
|
||||
#define E_NO_SUCH_PID 2001 //cant find PID
|
||||
#define E_QUERY_BUFFER_END 99 //have no free query buffer slot
|
||||
|
||||
#define E_USER_START 1000 // base offset for sub system errors
|
||||
|
||||
// PID
|
||||
#define E_PID_CUSTOM 2000 // Custom PID error
|
||||
#define E_PID_TIMEOUT E_PID_CUSTOM + 2 // Timeout
|
||||
#define E_PID_OVERHEAT E_PID_CUSTOM + 3 // Timeout
|
||||
|
||||
// WiFi/Network Errors
|
||||
#define E_WIFI_CONNECTION_FAILED 5001
|
||||
#define E_WEBSERVER_INIT_FAILED 5002
|
||||
#define E_DEPENDENCY_NOT_MET 5003
|
||||
|
||||
#define E_ALLOCATION_FAILED 200
|
||||
#define E_SERIAL_INIT_FAILED 201
|
||||
#define E_NOT_INITIALIZED 202
|
||||
#define E_NOT_IMPLEMENTED 204
|
||||
#define E_MODBUS_INIT_FAILED 205 // Added Modbus RTU Init Error
|
||||
|
||||
#define E_WARNING 10
|
||||
#define E_NOT_FOUND 20
|
||||
#define E_FATAL 100
|
||||
|
||||
typedef enum OBJECT_RUN_FLAGS
|
||||
{
|
||||
E_OF_NONE = 0, /**< No run flag */
|
||||
E_OF_DEBUG = 1, /**< Enable debug call, per frame */
|
||||
E_OF_INFO = 2, /**< Enable info print during boot */
|
||||
E_OF_LOOP = 3, /**< Enable loop invocation on the main loop */
|
||||
E_OF_DISABLED = 4, /**< Disable component entirely */
|
||||
E_OF_SETUP = 5, /**< Enable setup invocation during boot */
|
||||
E_OF_MAIN = 6, /**< reserved */
|
||||
E_OF_BRIDGE = 7 /**< Component registers methods on the Bridge */
|
||||
} OBJECT_RUN_FLAGS;
|
||||
|
||||
typedef enum OBJECT_NET_CAPS
|
||||
{
|
||||
E_NCAPS_NONE = 0,
|
||||
E_NCAPS_MODBUS = 1,
|
||||
E_NCAPS_SERIAL = 2,
|
||||
} OBJECT_NET_CAPS;
|
||||
|
||||
typedef enum MB_REGISTER_MODE
|
||||
{
|
||||
E_MB_REGISTER_MODE_NONE = 0,
|
||||
E_MB_REGISTER_MODE_READ = 1,
|
||||
E_MB_REGISTER_MODE_WRITE = 2,
|
||||
E_MB_REGISTER_MODE_READ_WRITE = 3,
|
||||
} MB_REGISTER_MODE;
|
||||
|
||||
typedef enum E_CALLS
|
||||
{
|
||||
/**< Call a global registered command . */
|
||||
EC_NONE = 0x00000000,
|
||||
/**< Call a global registered command . */
|
||||
EC_COMMAND = 0x00000001,
|
||||
/**< Call component method (See Bridge::registerMemberFunction & Component::serial_register) */
|
||||
EC_METHOD = 0x00000002,
|
||||
/**< Function call type. */
|
||||
EC_FUNC = 0x00000004,
|
||||
/**< User-defined call type. */
|
||||
EC_USER = 0x00000008
|
||||
} E_CALLS;
|
||||
|
||||
/**
|
||||
* @brief Enumeration representing different message flags.
|
||||
*
|
||||
* This enumeration defines the possible flags that can be associated with a message.
|
||||
* Each flag represents a different state or attribute of the message.
|
||||
*/
|
||||
typedef enum E_MessageFlags
|
||||
{
|
||||
/**<Internal: no flags */
|
||||
E_MF_NONE = 0x00000000,
|
||||
/**<Internal: flag designating an unprocessed message */
|
||||
E_MF_NEW = 0x00000001,
|
||||
/**<Internal: Processing message flag */
|
||||
E_MF_PROCESSING = 0x00000002,
|
||||
/**<Internal: Processed message flag */
|
||||
E_MF_PROCESSED = 0x00000004,
|
||||
/**<Internal: Debug message during processing */
|
||||
E_MF_DEBUG = 0x00000008,
|
||||
/**<Sender: Instruct to send a receipt - Default:On */
|
||||
E_MF_RECEIPT = 0x0000010,
|
||||
/**<Sender: Instruct to return component state */
|
||||
E_MF_STATE = 0x00000020
|
||||
} E_MessageFlags;
|
||||
|
||||
// Restore conflicting enum
|
||||
typedef enum
|
||||
{
|
||||
MB_FC_NONE = 0, //!< null operator
|
||||
MB_FC_READ_COILS = 1, //!< FCT=1 -> read coils or digital outputs
|
||||
MB_FC_READ_DISCRETE_INPUT = 2, //!< FCT=2 -> read digital inputs
|
||||
MB_FC_READ_REGISTERS = 3, //!< FCT=3 -> read registers or analog outputs
|
||||
// MB_FC_READ_HOLDING_REGISTERS = 3, //!< FCT=3 -> read registers or analog outputs
|
||||
MB_FC_READ_INPUT_REGISTER = 4, //!< FCT=4 -> read analog inputs
|
||||
MB_FC_WRITE_COIL = 5, //!< FCT=5 -> write single coil or output
|
||||
MB_FC_WRITE_REGISTER = 6, //!< FCT=6 -> write single register
|
||||
MB_FC_WRITE_MULTIPLE_COILS = 15, //!< FCT=15 -> write multiple coils or outputs
|
||||
MB_FC_WRITE_MULTIPLE_REGISTERS = 16 //!< FCT=16 -> write multiple registers
|
||||
} MB_FC;
|
||||
|
||||
typedef enum MODBUS_ERRORS
|
||||
{
|
||||
MODBUS_ERROR_NONE = 0, /**< No error */
|
||||
MODBUS_ERROR_ILLEGAL_FUNCTION = 1, /**< Illegal function error */
|
||||
MODBUS_ERROR_ILLEGAL_DATA_ADDRESS = 2, /**< Illegal data address error */
|
||||
MODBUS_ERROR_ILLEGAL_DATA_VALUE = 3, /**< Illegal data value error */
|
||||
MODBUS_ERROR_SLAVE_DEVICE_FAILURE = 4, /**< Slave device failure error */
|
||||
MODBUS_ERROR_ACKNOWLEDGE = 5, /**< Acknowledge error */
|
||||
MODBUS_ERROR_SLAVE_DEVICE_BUSY = 6, /**< Slave device busy error */
|
||||
MODBUS_ERROR_MEMORY_PARITY = 8, /**< Memory parity error */
|
||||
MODBUS_ERROR_GATEWAY_PATH_UNAVAILABLE = 10, /**< Gateway path unavailable error */
|
||||
MODBUS_ERROR_GATEWAY_TARGET_DEVICE_FAILED_TO_RESPOND = 11 /**< Gateway target device failed to respond error */
|
||||
} MODBUS_ERRORS;
|
||||
|
||||
/**
|
||||
* @brief Defines the type of Modbus object.
|
||||
*/
|
||||
enum E_ModbusType {
|
||||
MB_TYPE_UNKNOWN = 0,
|
||||
MB_TYPE_COIL = 1,
|
||||
MB_TYPE_DISCRETE_INPUT = 2,
|
||||
MB_TYPE_HOLDING_REGISTER = 3,
|
||||
MB_TYPE_INPUT_REGISTER = 4
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Defines the access mode for a Modbus address range.
|
||||
*/
|
||||
enum E_ModbusAccess {
|
||||
MB_ACCESS_NONE = 0,
|
||||
MB_ACCESS_READ_ONLY = 1,
|
||||
MB_ACCESS_WRITE_ONLY = 2,
|
||||
MB_ACCESS_READ_WRITE = 3
|
||||
};
|
||||
|
||||
#endif
|
||||
6
src/error_codes.h
Normal file
6
src/error_codes.h
Normal file
@ -0,0 +1,6 @@
|
||||
#ifndef ERROR_CODES_H
|
||||
#define ERROR_CODES_H
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
390
src/macros.h
Normal file
390
src/macros.h
Normal file
@ -0,0 +1,390 @@
|
||||
#ifndef MACROS_H
|
||||
#define MACROS_H
|
||||
|
||||
#include "./xtypes.h"
|
||||
|
||||
// Macros for adding
|
||||
#define INC_0 1
|
||||
#define INC_1 2
|
||||
#define INC_2 3
|
||||
#define INC_3 4
|
||||
#define INC_4 5
|
||||
#define INC_5 6
|
||||
#define INC_6 7
|
||||
#define INC_7 8
|
||||
#define INC_8 9
|
||||
#define INCREMENT_(n) INC_##n
|
||||
#define INCREMENT(n) INCREMENT_(n)
|
||||
|
||||
// Macros for subtracting
|
||||
#define DEC_1 0
|
||||
#define DEC_2 1
|
||||
#define DEC_3 2
|
||||
#define DEC_4 3
|
||||
#define DEC_5 4
|
||||
#define DEC_6 5
|
||||
#define DEC_7 6
|
||||
#define DEC_8 7
|
||||
#define DEC_9 8
|
||||
#define DECREMENT_(n) DEC_##n
|
||||
#define DECREMENT(n) DECREMENT_(n)
|
||||
|
||||
// compiler - & C quirks
|
||||
#ifndef FORCE_INLINE
|
||||
#define FORCE_INLINE __attribute__((always_inline)) inline
|
||||
#endif
|
||||
|
||||
#define _UNUSED __attribute__((unused))
|
||||
|
||||
// fallback noop
|
||||
#define NOOP (void(0))
|
||||
|
||||
// Option testing
|
||||
|
||||
// Use NUM_ARGS(__VA_ARGS__) to get the number of variadic arguments
|
||||
#define _NUM_ARGS(_, n, m, l, k, j, i, h, g, f, e, d, c, b, a, Z, Y, X, W, V, U, T, S, R, Q, P, O, N, M, L, K, J, I, H, G, F, E, D, C, B, A, OUT, ...) OUT
|
||||
#define NUM_ARGS(V...) _NUM_ARGS(0, V, 40, 39, 38, 37, 36, 35, 34, 33, 32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)
|
||||
|
||||
// Use TWO_ARGS(__VA_ARGS__) to get whether there are 1, 2, or >2 arguments
|
||||
#define _TWO_ARGS(_, n, m, l, k, j, i, h, g, f, e, d, c, b, a, Z, Y, X, W, V, U, T, S, R, Q, P, O, N, M, L, K, J, I, H, G, F, E, D, C, B, A, OUT, ...) OUT
|
||||
#define TWO_ARGS(V...) _TWO_ARGS(0, V, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 1, 0)
|
||||
|
||||
// Concatenate symbol names, without or with pre-expansion
|
||||
#define _CAT(a, V...) a##V
|
||||
#define CAT(a, V...) _CAT(a, V)
|
||||
|
||||
// Macros to chain up to 40 conditions
|
||||
#define _DO_1(W, C, A) (_##W##_1(A))
|
||||
#define _DO_2(W, C, A, B) (_##W##_1(A) C _##W##_1(B))
|
||||
#define _DO_3(W, C, A, V...) (_##W##_1(A) C _DO_2(W, C, V))
|
||||
#define _DO_4(W, C, A, V...) (_##W##_1(A) C _DO_3(W, C, V))
|
||||
#define _DO_5(W, C, A, V...) (_##W##_1(A) C _DO_4(W, C, V))
|
||||
#define _DO_6(W, C, A, V...) (_##W##_1(A) C _DO_5(W, C, V))
|
||||
#define _DO_7(W, C, A, V...) (_##W##_1(A) C _DO_6(W, C, V))
|
||||
#define _DO_8(W, C, A, V...) (_##W##_1(A) C _DO_7(W, C, V))
|
||||
#define _DO_9(W, C, A, V...) (_##W##_1(A) C _DO_8(W, C, V))
|
||||
#define _DO_10(W, C, A, V...) (_##W##_1(A) C _DO_9(W, C, V))
|
||||
#define _DO_11(W, C, A, V...) (_##W##_1(A) C _DO_10(W, C, V))
|
||||
#define _DO_12(W, C, A, V...) (_##W##_1(A) C _DO_11(W, C, V))
|
||||
#define _DO_13(W, C, A, V...) (_##W##_1(A) C _DO_12(W, C, V))
|
||||
#define _DO_14(W, C, A, V...) (_##W##_1(A) C _DO_13(W, C, V))
|
||||
#define _DO_15(W, C, A, V...) (_##W##_1(A) C _DO_14(W, C, V))
|
||||
#define _DO_16(W, C, A, V...) (_##W##_1(A) C _DO_15(W, C, V))
|
||||
#define _DO_17(W, C, A, V...) (_##W##_1(A) C _DO_16(W, C, V))
|
||||
#define _DO_18(W, C, A, V...) (_##W##_1(A) C _DO_17(W, C, V))
|
||||
#define _DO_19(W, C, A, V...) (_##W##_1(A) C _DO_18(W, C, V))
|
||||
#define _DO_20(W, C, A, V...) (_##W##_1(A) C _DO_19(W, C, V))
|
||||
#define _DO_21(W, C, A, V...) (_##W##_1(A) C _DO_20(W, C, V))
|
||||
#define _DO_22(W, C, A, V...) (_##W##_1(A) C _DO_21(W, C, V))
|
||||
#define _DO_23(W, C, A, V...) (_##W##_1(A) C _DO_22(W, C, V))
|
||||
#define _DO_24(W, C, A, V...) (_##W##_1(A) C _DO_23(W, C, V))
|
||||
#define _DO_25(W, C, A, V...) (_##W##_1(A) C _DO_24(W, C, V))
|
||||
#define _DO_26(W, C, A, V...) (_##W##_1(A) C _DO_25(W, C, V))
|
||||
#define _DO_27(W, C, A, V...) (_##W##_1(A) C _DO_26(W, C, V))
|
||||
#define _DO_28(W, C, A, V...) (_##W##_1(A) C _DO_27(W, C, V))
|
||||
#define _DO_29(W, C, A, V...) (_##W##_1(A) C _DO_28(W, C, V))
|
||||
#define _DO_30(W, C, A, V...) (_##W##_1(A) C _DO_29(W, C, V))
|
||||
#define _DO_31(W, C, A, V...) (_##W##_1(A) C _DO_30(W, C, V))
|
||||
#define _DO_32(W, C, A, V...) (_##W##_1(A) C _DO_31(W, C, V))
|
||||
#define _DO_33(W, C, A, V...) (_##W##_1(A) C _DO_32(W, C, V))
|
||||
#define _DO_34(W, C, A, V...) (_##W##_1(A) C _DO_33(W, C, V))
|
||||
#define _DO_35(W, C, A, V...) (_##W##_1(A) C _DO_34(W, C, V))
|
||||
#define _DO_36(W, C, A, V...) (_##W##_1(A) C _DO_35(W, C, V))
|
||||
#define _DO_37(W, C, A, V...) (_##W##_1(A) C _DO_36(W, C, V))
|
||||
#define _DO_38(W, C, A, V...) (_##W##_1(A) C _DO_37(W, C, V))
|
||||
#define _DO_39(W, C, A, V...) (_##W##_1(A) C _DO_38(W, C, V))
|
||||
#define _DO_40(W, C, A, V...) (_##W##_1(A) C _DO_39(W, C, V))
|
||||
#define __DO_N(W, C, N, V...) _DO_##N(W, C, V)
|
||||
#define _DO_N(W, C, N, V...) __DO_N(W, C, N, V)
|
||||
#define DO(W, C, V...) (_DO_N(W, C, NUM_ARGS(V), V))
|
||||
|
||||
#define FIRST(a,...) a
|
||||
#define SECOND(a,b,...) b
|
||||
#define THIRD(a,b,c,...) c
|
||||
|
||||
#define IS_PROBE(V...) SECOND(V, 0) // Get the second item passed, or 0
|
||||
#define PROBE() ~, 1 // Second item will be 1 if this is passed
|
||||
#define _NOT_0 PROBE()
|
||||
#define NOT(x) IS_PROBE(_CAT(_NOT_, x)) // NOT('0') gets '1'. Anything else gets '0'.
|
||||
#define _BOOL(x) NOT(NOT(x)) // _BOOL('0') gets '0'. Anything else gets '1'.
|
||||
|
||||
#define IF_ELSE(TF) _IF_ELSE(_BOOL(TF))
|
||||
#define _IF_ELSE(TF) _CAT(_IF_, TF)
|
||||
|
||||
#define _IF_1(V...) V _IF_1_ELSE
|
||||
#define _IF_0(...) _IF_0_ELSE
|
||||
|
||||
#define _IF_1_ELSE(...)
|
||||
#define _IF_0_ELSE(V...) V
|
||||
|
||||
// Recognize "true" values: blank, 1, 0x1, true
|
||||
#define _ISENA_ ~, 1
|
||||
#define _ISENA_1 ~, 1
|
||||
#define _ISENA_0x1 ~, 1
|
||||
#define _ISENA_true ~, 1
|
||||
#define _ISENA(V...) IS_PROBE(V)
|
||||
|
||||
// Macros to evaluate simple option switches
|
||||
#define _ENA_1(O) _ISENA(CAT(_IS, CAT(ENA_, O)))
|
||||
#define _DIS_1(O) NOT(_ENA_1(O))
|
||||
#define ENABLED(V...) DO(ENA, &&, V)
|
||||
|
||||
#ifndef MACRO_DISABLED
|
||||
#define MACRO_DISABLED(V...) DO(DIS, &&, V)
|
||||
#endif
|
||||
|
||||
#define ANY(V...) !MACRO_DISABLED(V)
|
||||
#define ALL ENABLED
|
||||
#define NONE MACRO_DISABLED
|
||||
#define COUNT_ENABLED(V...) DO(ENA, +, V)
|
||||
#define MANY(V...) (COUNT_ENABLED(V) > 1)
|
||||
|
||||
// #define _CAT(a, ...) a ## __VA_ARGS__
|
||||
// #define SWITCH_ENABLED_ 1
|
||||
// #define ENABLED(b) _CAT(SWITCH_ENABLED_, b)
|
||||
|
||||
// time
|
||||
#define PENDING(NOW, SOON) ((long)(NOW - (SOON)) < 0)
|
||||
#define ELAPSED(NOW, SOON) (!PENDING(NOW, SOON))
|
||||
|
||||
#define MMM_TO_MMS(MM_M) ((MM_M) / 60.0f)
|
||||
#define MMS_TO_MMM(MM_S) ((MM_S) * 60.0f)
|
||||
#define HOUR_MS ((millis_t)1000 * (millis_t)(60 * 60))
|
||||
#define MIN_MS ((millis_t)1000 * (millis_t)(60))
|
||||
#define SECS ((millis_t)1000)
|
||||
|
||||
// bit masks
|
||||
#undef __BV
|
||||
#define __BV(b) (1 << (b))
|
||||
#define TEST(n, b) !!((n) & __BV(b))
|
||||
#define SBI(n, b) (n |= __BV(b))
|
||||
#define CBI(n, b) (n &= ~__BV(b))
|
||||
#define SET_BIT_TO(N, B, TF) \
|
||||
do \
|
||||
{ \
|
||||
if (TF) \
|
||||
SBI(N, B); \
|
||||
else \
|
||||
CBI(N, B); \
|
||||
} while (0)
|
||||
|
||||
#define _BV32(b) (1UL << (b))
|
||||
#define TEST32(n, b) !!((n) & _BV32(b))
|
||||
#define SBI32(n, b) (n |= _BV32(b))
|
||||
#define CBI32(n, b) (n &= ~_BV32(b))
|
||||
#define SIGN(a) ((a > 0) - (a < 0))
|
||||
|
||||
// math basics
|
||||
|
||||
#ifndef WITHIN
|
||||
#define WITHIN(V, L, H) ((V) >= (L) && (V) <= (H))
|
||||
#endif
|
||||
|
||||
#define NUMERIC(a) WITHIN(a, '0', '9')
|
||||
#define DECIMAL(a) (NUMERIC(a) || a == '.')
|
||||
#define NUMERIC_SIGNED(a) (NUMERIC(a) || (a) == '-' || (a) == '+')
|
||||
#define DECIMAL_SIGNED(a) (DECIMAL(a) || (a) == '-' || (a) == '+')
|
||||
#define COUNT(a) (sizeof(a) / sizeof(*a))
|
||||
|
||||
#ifndef ZERO
|
||||
#define ZERO(a) memset(a, 0, sizeof(a))
|
||||
#endif
|
||||
|
||||
#define COPY(a, b) memcpy(a, b, MIN(sizeof(a), sizeof(b)))
|
||||
|
||||
// #define M_PI 3.14159265358979323846f
|
||||
#define RADIANS(d) ((d) * M_PI / 180.0f)
|
||||
#define DEGREES(r) ((r) * 180.0f / M_PI)
|
||||
#define CEILING(x, y) (((x) + (y) - 1) / (y))
|
||||
|
||||
#define UNEAR_ZERO(x) ((x) < 0.000001f)
|
||||
#define NEAR_ZERO(x) WITHIN(x, -0.000001f, 0.000001f)
|
||||
#define NEAR(x, y) NEAR_ZERO((x) - (y))
|
||||
|
||||
#define RECIPROCAL(x) (NEAR_ZERO(x) ? 0 : (1 / float(x)))
|
||||
#define FIXFLOAT(f) ({__typeof__(f) _f = (f); _f + (_f < 0 ? -0.0000005f : 0.0000005f); })
|
||||
|
||||
// value helper macros
|
||||
#define ISEOL(C) ((C) == '\n' || (C) == '\r')
|
||||
#define HEXCHR(a) (NUMERIC(a) ? (a) - '0' : WITHIN(a, 'a', 'f') ? ((a) - 'a' + 10) \
|
||||
: WITHIN(a, 'A', 'F') ? ((a) - 'A' + 10) \
|
||||
: -1)
|
||||
|
||||
// Macros for initializing arrays
|
||||
#define ARRAY_6(v1, v2, v3, v4, v5, v6, ...) \
|
||||
{ \
|
||||
v1, v2, v3, v4, v5, v6 \
|
||||
}
|
||||
#define ARRAY_5(v1, v2, v3, v4, v5, ...) \
|
||||
{ \
|
||||
v1, v2, v3, v4, v5 \
|
||||
}
|
||||
#define ARRAY_4(v1, v2, v3, v4, ...) \
|
||||
{ \
|
||||
v1, v2, v3, v4 \
|
||||
}
|
||||
#define ARRAY_3(v1, v2, v3, ...) \
|
||||
{ \
|
||||
v1, v2, v3 \
|
||||
}
|
||||
#define ARRAY_2(v1, v2, ...) \
|
||||
{ \
|
||||
v1, v2 \
|
||||
}
|
||||
#define ARRAY_1(v1, ...) \
|
||||
{ \
|
||||
v1 \
|
||||
}
|
||||
|
||||
#define _ARRAY_N(N, ...) ARRAY_##N(__VA_ARGS__)
|
||||
#define ARRAY_N(N, ...) _ARRAY_N(N, __VA_ARGS__)
|
||||
|
||||
#define SPACE(A) " " << A << " "
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Macros from Marlin / compat
|
||||
|
||||
#ifndef MARLIN_HEX_VERSION
|
||||
|
||||
// Clock speed factors
|
||||
#if !defined(CYCLES_PER_MICROSECOND) && !defined(__STM32F1__)
|
||||
#define CYCLES_PER_MICROSECOND (F_CPU / 1000000UL) // 16 or 20 on AVR
|
||||
#endif
|
||||
|
||||
// Nanoseconds per cycle
|
||||
#define NANOSECONDS_PER_CYCLE (1000000000.0 / F_CPU)
|
||||
|
||||
// Macros to make a string from a macro
|
||||
#define STRINGIFY_(M) #M
|
||||
#define STRINGIFY(M) STRINGIFY_(M)
|
||||
|
||||
// Macros to chain up to 40 conditions
|
||||
|
||||
#ifdef __cplusplus
|
||||
#ifndef _MINMAX_H_
|
||||
#define _MINMAX_H_
|
||||
|
||||
extern "C++"
|
||||
{
|
||||
|
||||
// C++11 solution that is standards compliant. Return type is deduced automatically
|
||||
template <class N>
|
||||
static constexpr N _MIN(const N val) { return val; }
|
||||
template <class N>
|
||||
static constexpr N _MAX(const N val) { return val; }
|
||||
template <class L, class R>
|
||||
static constexpr auto _MIN(const L lhs, const R rhs) -> decltype(lhs + rhs)
|
||||
{
|
||||
return lhs < rhs ? lhs : rhs;
|
||||
}
|
||||
template <class L, class R>
|
||||
static constexpr auto _MAX(const L lhs, const R rhs) -> decltype(lhs + rhs)
|
||||
{
|
||||
return lhs > rhs ? lhs : rhs;
|
||||
}
|
||||
template <class T, class... Ts>
|
||||
static constexpr const T _MIN(T V, Ts... Vs) { return _MIN(V, _MIN(Vs...)); }
|
||||
template <class T, class... Ts>
|
||||
static constexpr const T _MAX(T V, Ts... Vs) { return _MAX(V, _MAX(Vs...)); }
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
// Allow manipulating enumeration value like flags without ugly cast everywhere
|
||||
#define ENUM_FLAGS(T) \
|
||||
FORCE_INLINE constexpr T operator&(T x, T y) { return static_cast<T>(static_cast<int>(x) & static_cast<int>(y)); } \
|
||||
FORCE_INLINE constexpr T operator|(T x, T y) { return static_cast<T>(static_cast<int>(x) | static_cast<int>(y)); } \
|
||||
FORCE_INLINE constexpr T operator^(T x, T y) { return static_cast<T>(static_cast<int>(x) ^ static_cast<int>(y)); } \
|
||||
FORCE_INLINE constexpr T operator~(T x) { return static_cast<T>(~static_cast<int>(x)); } \
|
||||
FORCE_INLINE T &operator&=(T &x, T y) { return x &= y; } \
|
||||
FORCE_INLINE T &operator|=(T &x, T y) { return x |= y; } \
|
||||
FORCE_INLINE T &operator^=(T &x, T y) { return x ^= y; }
|
||||
|
||||
// C++11 solution that is standard compliant. <type_traits> is not available on all platform
|
||||
namespace Private
|
||||
{
|
||||
template <bool, typename _Tp = void>
|
||||
struct enable_if
|
||||
{
|
||||
};
|
||||
template <typename _Tp>
|
||||
struct enable_if<true, _Tp>
|
||||
{
|
||||
typedef _Tp type;
|
||||
};
|
||||
|
||||
template <typename T, typename U>
|
||||
struct is_same
|
||||
{
|
||||
enum
|
||||
{
|
||||
value = false
|
||||
};
|
||||
};
|
||||
template <typename T>
|
||||
struct is_same<T, T>
|
||||
{
|
||||
enum
|
||||
{
|
||||
value = true
|
||||
};
|
||||
};
|
||||
|
||||
template <typename T, typename... Args>
|
||||
struct first_type_of
|
||||
{
|
||||
typedef T type;
|
||||
};
|
||||
template <typename T>
|
||||
struct first_type_of<T>
|
||||
{
|
||||
typedef T type;
|
||||
};
|
||||
}
|
||||
// C++11 solution using SFINAE to detect the existence of a member in a class at compile time.
|
||||
// It creates a HasMember<Type> structure containing 'value' set to true if the member exists
|
||||
#define HAS_MEMBER_IMPL(Member) \
|
||||
namespace Private \
|
||||
{ \
|
||||
template <typename Type, typename Yes = char, typename No = long> \
|
||||
struct HasMember_##Member \
|
||||
{ \
|
||||
template <typename C> \
|
||||
static Yes &test(decltype(&C::Member)); \
|
||||
template <typename C> \
|
||||
static No &test(...); \
|
||||
enum \
|
||||
{ \
|
||||
value = sizeof(test<Type>(0)) == sizeof(Yes) \
|
||||
}; \
|
||||
}; \
|
||||
}
|
||||
|
||||
// Call the method if it exists, but do nothing if it does not. The method is detected at compile time.
|
||||
// If the method exists, this is inlined and does not cost anything. Else, an "empty" wrapper is created, returning a default value
|
||||
#define CALL_IF_EXISTS_IMPL(Return, Method, ...) \
|
||||
HAS_MEMBER_IMPL(Method) \
|
||||
namespace Private \
|
||||
{ \
|
||||
template <typename T, typename... Args> \
|
||||
FORCE_INLINE typename enable_if<HasMember_##Method<T>::value, Return>::type Call_##Method(T *t, Args... a) { return static_cast<Return>(t->Method(a...)); } \
|
||||
_UNUSED static Return Call_##Method(...) { return __VA_ARGS__; } \
|
||||
}
|
||||
#define CALL_IF_EXISTS(Return, That, Method, ...) \
|
||||
static_cast<Return>(Private::Call_##Method(That, ##__VA_ARGS__))
|
||||
|
||||
#else
|
||||
|
||||
#endif // __cplusplus
|
||||
|
||||
#endif // MARLIN_HEX_VERSION
|
||||
|
||||
///////////////////////////////////////////////////////
|
||||
//
|
||||
// String Conversion / Casting (WString.h substitute)
|
||||
//
|
||||
#define CC_STR(s) (const char *)s
|
||||
#define C_STR(s) (char *)s
|
||||
|
||||
#endif // MACROS_H
|
||||
15
src/utils.cpp
Normal file
15
src/utils.cpp
Normal file
@ -0,0 +1,15 @@
|
||||
#include "utils.h"
|
||||
|
||||
void printHex(uint8_t *data, uint8_t length)
|
||||
{
|
||||
for (int i = 0; i < length; i++)
|
||||
{
|
||||
if (data[i] < 0x10)
|
||||
{
|
||||
Serial.print("0");
|
||||
}
|
||||
Serial.print(data[i], HEX);
|
||||
Serial.print(" : ");
|
||||
}
|
||||
Serial.println(" ");
|
||||
}
|
||||
17
src/utils.h
Normal file
17
src/utils.h
Normal file
@ -0,0 +1,17 @@
|
||||
#ifndef UTILS_H
|
||||
#define UTILS_H
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <stdint.h>
|
||||
|
||||
void printHex(uint8_t *data, uint8_t length);
|
||||
/*
|
||||
template <typename T> T normalizeValue(T value, T maximum) {
|
||||
if (value < 0 || value > maximum) {
|
||||
return 0; // Or any other appropriate value
|
||||
}
|
||||
return value / maximum;
|
||||
}
|
||||
*/
|
||||
|
||||
#endif
|
||||
23
src/xmath.h
Normal file
23
src/xmath.h
Normal file
@ -0,0 +1,23 @@
|
||||
#ifndef XMATH_H
|
||||
#define XMATH_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C++"
|
||||
{
|
||||
template <typename T>
|
||||
T clamp(const T &value, const T &low, const T &high)
|
||||
{
|
||||
return value < low ? low : (value > high ? high : value);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#define RANGE(i, min, max) ((i > min) && (i < max))
|
||||
|
||||
#define NCLAMP(x, min, max) \
|
||||
(((max) == (min)) ? 0.0f : \
|
||||
clamp<float>((static_cast<float>((x) - (min))) / ((max) - (min)), 0.0f, 1.0f))
|
||||
|
||||
|
||||
#endif
|
||||
94
src/xstatistics.cpp
Normal file
94
src/xstatistics.cpp
Normal file
@ -0,0 +1,94 @@
|
||||
#include "xstatistics.h"
|
||||
#include <stdint.h>
|
||||
|
||||
Statistic::Statistic()
|
||||
{
|
||||
clear();
|
||||
}
|
||||
|
||||
// resets all counters
|
||||
void Statistic::clear()
|
||||
{
|
||||
_cnt = 0;
|
||||
_sum = 0;
|
||||
_min = 0;
|
||||
_max = 0;
|
||||
#ifdef STAT_USE_STDEV
|
||||
_ssqdif = 0.0; // not _ssq but sum of square differences
|
||||
// which is SUM(from i = 1 to N) of
|
||||
// (f(i)-_ave_N)**2
|
||||
#endif
|
||||
}
|
||||
|
||||
// adds a new value to the data-set
|
||||
void Statistic::add(const float value)
|
||||
{
|
||||
if (_cnt == 0)
|
||||
{
|
||||
_min = value;
|
||||
_max = value;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (value < _min)
|
||||
_min = value;
|
||||
else if (value > _max)
|
||||
_max = value;
|
||||
}
|
||||
_sum += value;
|
||||
_cnt++;
|
||||
|
||||
#ifdef STAT_USE_STDEV
|
||||
if (_cnt > 1)
|
||||
{
|
||||
float _store = (_sum / _cnt - value);
|
||||
_ssqdif = _ssqdif + _cnt * _store * _store / (_cnt - 1);
|
||||
// ~10% faster but limits the amount of samples to 65K as _cnt*_cnt overflows
|
||||
// float _store = _sum - _cnt * value;
|
||||
// _ssqdif = _ssqdif + _store * _store / (_cnt*_cnt - _cnt);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// returns the average of the data-set added sofar
|
||||
float Statistic::average() const
|
||||
{
|
||||
if (_cnt == 0)
|
||||
return NAN; // original code returned 0
|
||||
return _sum / _cnt;
|
||||
}
|
||||
|
||||
// Population standard deviation = s = sqrt [ S ( Xi - <20> )2 / N ]
|
||||
// http://www.suite101.com/content/how-is-standard-deviation-used-a99084
|
||||
#ifdef STAT_USE_STDEV
|
||||
|
||||
float Statistic::variance() const
|
||||
{
|
||||
if (_cnt == 0)
|
||||
return NAN; // otherwise DIV0 error
|
||||
return _ssqdif / _cnt;
|
||||
}
|
||||
|
||||
float Statistic::mean() const
|
||||
{
|
||||
if (_cnt == 0)
|
||||
return NAN; // otherwise DIV0 error
|
||||
return this->sum() / _cnt;
|
||||
}
|
||||
|
||||
float Statistic::pop_stdev() const
|
||||
{
|
||||
if (_cnt == 0)
|
||||
return NAN; // otherwise DIV0 error
|
||||
return sqrt(_ssqdif / _cnt);
|
||||
}
|
||||
|
||||
float Statistic::unbiased_stdev() const
|
||||
{
|
||||
if (_cnt < 2)
|
||||
return NAN; // otherwise DIV0 error
|
||||
return sqrt(_ssqdif / (_cnt - 1));
|
||||
}
|
||||
|
||||
#endif
|
||||
// END OF FILE
|
||||
116
src/xstatistics.h
Normal file
116
src/xstatistics.h
Normal file
@ -0,0 +1,116 @@
|
||||
#ifndef XSTATISTICS_H
|
||||
#define XSTATISTICS_H
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <math.h>
|
||||
|
||||
#define STAT_USE_STDEV
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
// C++11 solution that is standards compliant. Return type is deduced automatically
|
||||
template <class L, class R>
|
||||
static inline constexpr auto MIN(const L lhs, const R rhs) -> decltype(lhs + rhs)
|
||||
{
|
||||
return lhs < rhs ? lhs : rhs;
|
||||
}
|
||||
template <class L, class R>
|
||||
static inline constexpr auto MAX(const L lhs, const R rhs) -> decltype(lhs + rhs)
|
||||
{
|
||||
return lhs > rhs ? lhs : rhs;
|
||||
}
|
||||
template <class T>
|
||||
static inline constexpr const T ABS(const T v)
|
||||
{
|
||||
return v >= 0 ? v : -v;
|
||||
}
|
||||
#else
|
||||
// Using GCC extensions, but Travis GCC version does not like it and gives
|
||||
// "error: statement-expressions are not allowed outside functions nor in template-argument lists"
|
||||
#define MIN(a, b) \
|
||||
({__typeof__(a) _a = (a); \
|
||||
__typeof__(b) _b = (b); \
|
||||
_a < _b ? _a : _b; })
|
||||
|
||||
#define MAX(a, b) \
|
||||
({__typeof__(a) _a = (a); \
|
||||
__typeof__(b) _b = (b); \
|
||||
_a > _b ? _a : _b; })
|
||||
|
||||
#define ABS(a) \
|
||||
({__typeof__(a) _a = (a); \
|
||||
_a >= 0 ? _a : -_a; })
|
||||
|
||||
#endif
|
||||
|
||||
class Statistic
|
||||
{
|
||||
public:
|
||||
Statistic(); // "switches on/off" stdev run time
|
||||
void clear(); // "switches on/off" stdev run time
|
||||
void add(const float);
|
||||
|
||||
// returns the number of values added
|
||||
uint32_t count() const { return _cnt; }; // zero if empty
|
||||
float sum() const { return _sum; }; // zero if empty
|
||||
float minimum() const { return _min; }; // zero if empty
|
||||
float maximum() const { return _max; }; // zero if empty
|
||||
float average() const; // NAN if empty
|
||||
float mean() const; // zero if empty
|
||||
|
||||
#ifdef STAT_USE_STDEV
|
||||
float variance() const; // NAN if empty
|
||||
float pop_stdev() const; // population stdev // NAN if empty
|
||||
float unbiased_stdev() const; // NAN if empty
|
||||
#endif
|
||||
|
||||
protected:
|
||||
uint32_t _cnt;
|
||||
float _sum;
|
||||
float _min;
|
||||
float _max;
|
||||
#ifdef STAT_USE_STDEV
|
||||
float _ssqdif; // sum of squares difference
|
||||
#endif
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the kth q-quantile.
|
||||
* @link http://en.wikipedia.org/wiki/Quantile#Quantiles_of_a_population
|
||||
* ie: median is 1st 2-quantile
|
||||
* ie: upper quartile is 3rd 4-quantile
|
||||
* @return {Number} q-quantile of values.
|
||||
*/
|
||||
/*
|
||||
const quantile = (arr: number[], i: number, n: number) => {
|
||||
if (i === 0) return Math.min.apply(null, arr);
|
||||
if (i === n) return Math.max.apply(null, arr);
|
||||
|
||||
let sorted = arr.slice(0);
|
||||
sorted.sort((a, b) => a - b);
|
||||
let index = sorted.length * i / n;
|
||||
|
||||
if (index % 1 === 0) {
|
||||
return 0.5 * sorted[index - 1] + 0.5 * sorted[index];
|
||||
}
|
||||
|
||||
return sorted[~~index];
|
||||
};
|
||||
|
||||
export const median = (arr: number[]) => quantile(arr, 1, 2);
|
||||
|
||||
export const sum = (arr: number[]) => arr.reduce((a, b) => a + b, 0);
|
||||
|
||||
export const mean = (arr: number[]) => sum(arr) / arr.length;
|
||||
|
||||
|
||||
// sqare errors along mean
|
||||
const sdiff = (arr: number[], mean: number) => arr.map((v) =>
|
||||
Math.pow(v - mean, 2)
|
||||
);
|
||||
|
||||
export const standardDeviation = (arr: number[]) =>
|
||||
Math.sqrt(mean(sdiff(arr, mean(arr))));
|
||||
*/
|
||||
|
||||
#endif
|
||||
136
src/xtimer.h
Normal file
136
src/xtimer.h
Normal file
@ -0,0 +1,136 @@
|
||||
#ifndef XTIMER_H
|
||||
#define XTIMER_H
|
||||
|
||||
#if defined(ARDUINO) && ARDUINO >= 100
|
||||
#include <Arduino.h>
|
||||
#else
|
||||
#include <WProgram.h>
|
||||
#endif
|
||||
|
||||
#include "macros.h"
|
||||
|
||||
#ifndef TIMER_MAX_TASKS
|
||||
#define TIMER_MAX_TASKS 0x10
|
||||
#endif
|
||||
|
||||
template <
|
||||
size_t max_tasks = TIMER_MAX_TASKS, /* max allocated tasks */
|
||||
unsigned long (*time_func)() = millis /* time function for timer */
|
||||
>
|
||||
class Timer
|
||||
{
|
||||
public:
|
||||
typedef bool (*handler_t)(void *opaque); /* task handler func signature */
|
||||
/* Calls handler with opaque as argument in delay units of time */
|
||||
bool
|
||||
in(unsigned long delay, handler_t h, void *opaque = NULL)
|
||||
{
|
||||
return add_task(time_func(), delay, h, opaque);
|
||||
}
|
||||
|
||||
/* Calls handler with opaque as argument at time */
|
||||
bool
|
||||
at(unsigned long time, handler_t h, void *opaque = NULL)
|
||||
{
|
||||
const unsigned long now = time_func();
|
||||
return add_task(now, time - now, h, opaque);
|
||||
}
|
||||
|
||||
/* Calls handler with opaque as argument every interval units of time */
|
||||
bool
|
||||
every(unsigned long interval, handler_t h, void *opaque = NULL)
|
||||
{
|
||||
return add_task(time_func(), interval, h, opaque, interval);
|
||||
}
|
||||
|
||||
|
||||
/* Ticks the timer forward - call this function in loop() */
|
||||
void
|
||||
tick()
|
||||
{
|
||||
tick(time_func());
|
||||
}
|
||||
|
||||
/* Ticks the timer forward - call this function in loop() */
|
||||
inline void
|
||||
tick(unsigned long t)
|
||||
{
|
||||
for (size_t i = 0; i < max_tasks; ++i)
|
||||
{
|
||||
struct task *const task = &tasks[i];
|
||||
const unsigned long duration = t - task->start;
|
||||
|
||||
if (task->handler && duration >= task->expires)
|
||||
{
|
||||
task->repeat = task->handler(task->opaque) && task->repeat;
|
||||
|
||||
if (task->repeat)
|
||||
task->start = t;
|
||||
else
|
||||
remove(task);
|
||||
}else{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
struct task
|
||||
{
|
||||
handler_t handler; /* task handler callback func */
|
||||
void *opaque; /* argument given to the callback handler */
|
||||
unsigned long start,
|
||||
expires, /* when the task expires */
|
||||
repeat; /* repeat task */
|
||||
} tasks[max_tasks];
|
||||
|
||||
inline void
|
||||
remove(struct task *task)
|
||||
{
|
||||
task->handler = NULL;
|
||||
task->opaque = NULL;
|
||||
task->start = 0;
|
||||
task->expires = 0;
|
||||
task->repeat = 0;
|
||||
}
|
||||
|
||||
inline struct task *
|
||||
next_task_slot()
|
||||
{
|
||||
for (size_t i = 0; i < max_tasks; ++i)
|
||||
{
|
||||
struct task *const slot = &tasks[i];
|
||||
if (slot->handler == NULL)
|
||||
return slot;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
inline struct task *
|
||||
add_task(unsigned long start, unsigned long expires,
|
||||
handler_t h, void *opaque, bool repeat = 0)
|
||||
{
|
||||
struct task *const slot = next_task_slot();
|
||||
|
||||
if (!slot){
|
||||
return NULL;
|
||||
}
|
||||
|
||||
slot->handler = h;
|
||||
slot->opaque = opaque;
|
||||
slot->start = start;
|
||||
slot->expires = expires;
|
||||
slot->repeat = repeat;
|
||||
|
||||
return slot;
|
||||
}
|
||||
};
|
||||
|
||||
/* create a timer with the default settings */
|
||||
inline Timer<>
|
||||
timer_create_default()
|
||||
{
|
||||
return Timer<>();
|
||||
}
|
||||
|
||||
#endif
|
||||
16
src/xtypes.h
Normal file
16
src/xtypes.h
Normal file
@ -0,0 +1,16 @@
|
||||
#ifndef TYPES_H
|
||||
#define TYPES_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
typedef const char cchar;
|
||||
typedef unsigned char uchar;
|
||||
|
||||
typedef unsigned long millis_t;
|
||||
typedef unsigned short ushort;
|
||||
typedef unsigned long ulong;
|
||||
typedef long int lint;
|
||||
typedef long long int llint;
|
||||
|
||||
|
||||
#endif
|
||||
Loading…
Reference in New Issue
Block a user