Files
xod/workspace/two-button-switch/__fixtures__/arduino.cpp
2018-01-18 15:35:00 +03:00

1405 lines
36 KiB
C++
Generated
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// The sketch is auto-generated with XOD (https://xod.io).
//
// You can compile and upload it to an Arduino-compatible board with
// Arduino IDE.
//
// Rough code overview:
//
// - Configuration section
// - STL shim
// - Immutable list classes and functions
// - XOD runtime environment
// - Native node implementation
// - Program graph definition
//
// Search for comments fenced with '====' and '----' to navigate through
// the major code blocks.
#include <Arduino.h>
#include <inttypes.h>
#include <avr/pgmspace.h>
/*=============================================================================
*
*
* Configuration
*
*
=============================================================================*/
// Uncomment to turn on debug of the program
//#define XOD_DEBUG
// Uncomment to trace the program runtime in the Serial Monitor
//#define XOD_DEBUG_ENABLE_TRACE
/*=============================================================================
*
*
* STL shim. Provides implementation for vital std::* constructs
*
*
=============================================================================*/
namespace std {
template< class T > struct remove_reference {typedef T type;};
template< class T > struct remove_reference<T&> {typedef T type;};
template< class T > struct remove_reference<T&&> {typedef T type;};
template <class T>
typename remove_reference<T>::type&& move(T&& a) {
return static_cast<typename remove_reference<T>::type&&>(a);
}
} // namespace std
/*=============================================================================
*
*
* XOD-specific list/array implementations
*
*
=============================================================================*/
#ifndef XOD_LIST_H
#define XOD_LIST_H
namespace xod {
namespace detail {
/*
* Cursors are used internaly by iterators and list views. They are not exposed
* directly to a list consumer.
*
* The base `Cursor` is an interface which provides the bare minimum of methods
* to facilitate a single iteration pass.
*/
template<typename T> class Cursor {
public:
virtual ~Cursor() { }
virtual bool isValid() const = 0;
virtual bool value(T* out) const = 0;
virtual void next() = 0;
};
template<typename T> class NilCursor : public Cursor<T> {
public:
virtual bool isValid() const { return false; }
virtual bool value(T*) const { return false; }
virtual void next() { }
};
} // namespace detail
/*
* Iterator is an object used to iterate a list once.
*
* Users create new iterators by calling `someList.iterate()`.
* Iterators are created on stack and are supposed to have a
* short live, e.g. for a duration of `for` loop or nodes
* `evaluate` function. Iterators cant be copied.
*
* Implemented as a pimpl pattern wrapper over the cursor.
* Once created for a cursor, an iterator owns that cursor
* and will delete the cursor object once destroyed itself.
*/
template<typename T>
class Iterator {
public:
static Iterator<T> nil() {
return Iterator<T>(new detail::NilCursor<T>());
}
Iterator(detail::Cursor<T>* cursor)
: _cursor(cursor)
{ }
~Iterator() {
if (_cursor)
delete _cursor;
}
Iterator(const Iterator& that) = delete;
Iterator& operator=(const Iterator& that) = delete;
Iterator(Iterator&& it)
: _cursor(it._cursor)
{
it._cursor = nullptr;
}
Iterator& operator=(Iterator&& it) {
auto tmp = it._cursor;
it._cursor = _cursor;
_cursor = tmp;
return *this;
}
operator bool() const { return _cursor->isValid(); }
bool value(T* out) const {
return _cursor->value(out);
}
T operator*() const {
T out;
_cursor->value(&out);
return out;
}
Iterator& operator++() {
_cursor->next();
return *this;
}
private:
detail::Cursor<T>* _cursor;
};
/*
* An interface for a list view. A particular list view provides a new
* kind of iteration over existing data. This way we can use list slices,
* list concatenations, list rotations, etc without introducing new data
* buffers. We just change the way already existing data is iterated.
*
* ListView is not exposed to a list user directly, it is used internally
* by the List class. However, deriving a new ListView is necessary if you
* make a new list/string processing node.
*/
template<typename T> class ListView {
public:
virtual Iterator<T> iterate() const = 0;
};
/*
* The list as it seen by data consumers. Have a single method `iterate`
* to create a new iterator.
*
* Implemented as pimpl pattern wrapper over a list view. Takes pointer
* to a list view in constructor and expects the view will be alive for
* the whole life time of the list.
*/
template<typename T> class List {
public:
constexpr List()
: _view(nullptr)
{ }
List(const ListView<T>* view)
: _view(view)
{ }
Iterator<T> iterate() const {
return _view ? _view->iterate() : Iterator<T>::nil();
}
// pre 0.15.0 backward compatibility
List* operator->() __attribute__ ((deprecated)) { return this; }
const List* operator->() const __attribute__ ((deprecated)) { return this; }
private:
const ListView<T>* _view;
};
/*
* A list view over an old good plain C array.
*
* Expects the array will be alive for the whole life time of the
* view.
*/
template<typename T> class PlainListView : public ListView<T> {
public:
class Cursor : public detail::Cursor<T> {
public:
Cursor(const PlainListView* owner)
: _owner(owner)
, _idx(0)
{ }
bool isValid() const override {
return _idx < _owner->_len;
}
bool value(T* out) const override {
if (!isValid())
return false;
*out = _owner->_data[_idx];
return true;
}
void next() override { ++_idx; }
private:
const PlainListView* _owner;
size_t _idx;
};
public:
PlainListView(const T* data, size_t len)
: _data(data)
, _len(len)
{ }
virtual Iterator<T> iterate() const override {
return Iterator<T>(new Cursor(this));
}
private:
friend class Cursor;
const T* _data;
size_t _len;
};
/*
* A list view over a null-terminated C-String.
*
* Expects the char buffer will be alive for the whole life time of the view.
* You can use string literals as a buffer, since they are persistent for
* the program execution time.
*/
class CStringView : public ListView<char> {
public:
class Cursor : public detail::Cursor<char> {
public:
Cursor(const char* str)
: _ptr(str)
{ }
bool isValid() const override {
return (bool)*_ptr;
}
bool value(char* out) const override {
*out = *_ptr;
return (bool)*_ptr;
}
void next() override { ++_ptr; }
private:
const char* _ptr;
};
public:
CStringView(const char* str = nullptr)
: _str(str)
{ }
CStringView& operator=(const CStringView& rhs) {
_str = rhs._str;
return *this;
}
virtual Iterator<char> iterate() const override {
return _str ? Iterator<char>(new Cursor(_str)) : Iterator<char>::nil();
}
private:
friend class Cursor;
const char* _str;
};
/*
* A list view over two other lists (Left and Right) which first iterates the
* left one, and when exhausted, iterates the right one.
*
* Expects both Left and Right to be alive for the whole view life time.
*/
template<typename T> class ConcatListView : public ListView<T> {
public:
class Cursor : public detail::Cursor<T> {
public:
Cursor(Iterator<T>&& left, Iterator<T>&& right)
: _left(std::move(left))
, _right(std::move(right))
{ }
bool isValid() const override {
return _left || _right;
}
bool value(T* out) const override {
return _left.value(out) || _right.value(out);
}
void next() override {
_left ? ++_left : ++_right;
}
private:
Iterator<T> _left;
Iterator<T> _right;
};
public:
ConcatListView() { }
ConcatListView(List<T> left, List<T> right)
: _left(left)
, _right(right)
{ }
ConcatListView& operator=(const ConcatListView& rhs) {
_left = rhs._left;
_right = rhs._right;
return *this;
}
virtual Iterator<T> iterate() const override {
return Iterator<T>(new Cursor(_left.iterate(), _right.iterate()));
}
private:
friend class Cursor;
const List<T> _left;
const List<T> _right;
};
//----------------------------------------------------------------------------
// Text string helpers
//----------------------------------------------------------------------------
using XString = List<char>;
/*
* List and list view in a single pack. An utility used to define constant
* string literals in XOD.
*/
class XStringCString : public XString {
public:
XStringCString(const char* str)
: XString(&_view)
, _view(str)
{ }
private:
CStringView _view;
};
} // namespace xod
#endif
/*=============================================================================
*
*
* Basic algorithms for XOD lists
*
*
=============================================================================*/
#ifndef XOD_LIST_FUNCS_H
#define XOD_LIST_FUNCS_H
namespace xod {
/*
* Folds a list from left. Also known as "reduce".
*/
template<typename T, typename TR>
TR foldl(List<T> xs, TR (*func)(TR, T), TR acc) {
for (auto it = xs.iterate(); it; ++it)
acc = func(acc, *it);
return acc;
}
template<typename T> size_t lengthReducer(size_t len, T) {
return len + 1;
}
/*
* Computes length of a list.
*/
template<typename T> size_t length(List<T> xs) {
return foldl(xs, lengthReducer<T>, (size_t)0);
}
template<typename T> T* dumpReducer(T* buff, T x) {
*buff = x;
return buff + 1;
}
/*
* Copies a list content into a memory buffer.
*
* It is expected that `outBuff` has enough size to fit all the data.
*/
template<typename T> size_t dump(List<T> xs, T* outBuff) {
T* buffEnd = foldl(xs, dumpReducer, outBuff);
return buffEnd - outBuff;
}
} // namespace xod
#endif
/*=============================================================================
*
*
* Runtime
*
*
=============================================================================*/
//----------------------------------------------------------------------------
// Debug routines
//----------------------------------------------------------------------------
#ifndef DEBUG_SERIAL
# define DEBUG_SERIAL Serial
#endif
#if defined(XOD_DEBUG) && defined(XOD_DEBUG_ENABLE_TRACE)
# define XOD_TRACE(x) { DEBUG_SERIAL.print(x); DEBUG_SERIAL.flush(); }
# define XOD_TRACE_LN(x) { DEBUG_SERIAL.println(x); DEBUG_SERIAL.flush(); }
# define XOD_TRACE_F(x) XOD_TRACE(F(x))
# define XOD_TRACE_FLN(x) XOD_TRACE_LN(F(x))
#else
# define XOD_TRACE(x)
# define XOD_TRACE_LN(x)
# define XOD_TRACE_F(x)
# define XOD_TRACE_FLN(x)
#endif
//----------------------------------------------------------------------------
// PGM space utilities
//----------------------------------------------------------------------------
#define pgm_read_nodeid(address) (pgm_read_word(address))
/*
* Workaround for bugs:
* https://github.com/arduino/ArduinoCore-sam/pull/43
* https://github.com/arduino/ArduinoCore-samd/pull/253
* Remove after the PRs merge
*/
#if !defined(ARDUINO_ARCH_AVR) && defined(pgm_read_ptr)
# undef pgm_read_ptr
# define pgm_read_ptr(addr) (*(const void **)(addr))
#endif
//----------------------------------------------------------------------------
// Compatibilities
//----------------------------------------------------------------------------
#if !defined(ARDUINO_ARCH_AVR)
/*
* Provide dtostrf function for non-AVR platforms. Although many platforms
* provide a stub many others do not. And the stub is based on `sprintf`
* which doesnt work with floating point formatters on some platforms
* (e.g. Arduino M0).
*
* This is an implementation based on `fcvt` standard function. Taken here:
* https://forum.arduino.cc/index.php?topic=368720.msg2542614#msg2542614
*/
char *dtostrf(double val, int width, unsigned int prec, char *sout) {
int decpt, sign, reqd, pad;
const char *s, *e;
char *p;
s = fcvt(val, prec, &decpt, &sign);
if (prec == 0 && decpt == 0) {
s = (*s < '5') ? "0" : "1";
reqd = 1;
} else {
reqd = strlen(s);
if (reqd > decpt) reqd++;
if (decpt == 0) reqd++;
}
if (sign) reqd++;
p = sout;
e = p + reqd;
pad = width - reqd;
if (pad > 0) {
e += pad;
while (pad-- > 0) *p++ = ' ';
}
if (sign) *p++ = '-';
if (decpt <= 0 && prec > 0) {
*p++ = '0';
*p++ = '.';
e++;
while ( decpt < 0 ) {
decpt++;
*p++ = '0';
}
}
while (p < e) {
*p++ = *s++;
if (p == e) break;
if (--decpt == 0) *p++ = '.';
}
if (width < 0) {
pad = (reqd + width) * -1;
while (pad-- > 0) *p++ = ' ';
}
*p = 0;
return sout;
}
#endif
namespace xod {
//----------------------------------------------------------------------------
// Type definitions
//----------------------------------------------------------------------------
typedef double Number;
typedef bool Logic;
typedef unsigned long TimeMs;
typedef uint8_t DirtyFlags;
//----------------------------------------------------------------------------
// Global variables
//----------------------------------------------------------------------------
TimeMs g_transactionTime;
//----------------------------------------------------------------------------
// Metaprogramming utilities
//----------------------------------------------------------------------------
template<typename T> struct always_false {
enum { value = 0 };
};
//----------------------------------------------------------------------------
// Forward declarations
//----------------------------------------------------------------------------
TimeMs transactionTime();
void runTransaction(bool firstRun);
//----------------------------------------------------------------------------
// Engine (private API)
//----------------------------------------------------------------------------
namespace detail {
template<typename NodeT>
bool isTimedOut(const NodeT* node) {
TimeMs t = node->timeoutAt;
// TODO: deal with uint32 overflow
return t && t < transactionTime();
}
// Marks timed out node dirty. Do not reset timeoutAt here to give
// a chance for a node to get a reasonable result from `isTimedOut`
// later during its `evaluate`
template<typename NodeT>
void checkTriggerTimeout(NodeT* node) {
node->isNodeDirty |= isTimedOut(node);
}
template<typename NodeT>
void clearTimeout(NodeT* node) {
node->timeoutAt = 0;
}
template<typename NodeT>
void clearStaleTimeout(NodeT* node) {
if (isTimedOut(node))
clearTimeout(node);
}
} // namespace detail
//----------------------------------------------------------------------------
// Public API (can be used by native nodes `evaluate` functions)
//----------------------------------------------------------------------------
TimeMs transactionTime() {
return g_transactionTime;
}
template<typename ContextT>
void setTimeout(ContextT* ctx, TimeMs timeout) {
ctx->_node->timeoutAt = transactionTime() + timeout;
}
template<typename ContextT>
void clearTimeout(ContextT* ctx) {
detail::clearTimeout(ctx->_node);
}
template<typename ContextT>
bool isTimedOut(const ContextT* ctx) {
return detail::isTimedOut(ctx->_node);
}
} // namespace xod
//----------------------------------------------------------------------------
// Entry point
//----------------------------------------------------------------------------
void setup() {
// FIXME: looks like there is a rounding bug. Waiting for 100ms fights it
delay(100);
#ifdef XOD_DEBUG
DEBUG_SERIAL.begin(115200);
#endif
XOD_TRACE_FLN("\n\nProgram started");
xod::runTransaction(true);
}
void loop() {
xod::runTransaction(false);
}
/*=============================================================================
*
*
* Native node implementations
*
*
=============================================================================*/
namespace xod {
//-----------------------------------------------------------------------------
// xod/core/gate implementation
//-----------------------------------------------------------------------------
namespace xod__core__gate {
struct State {
};
struct Node {
State state;
Logic output_T;
Logic output_F;
union {
struct {
bool isOutputDirty_T : 1;
bool isOutputDirty_F : 1;
bool isNodeDirty : 1;
};
DirtyFlags dirtyFlags;
};
};
struct input_GATE { };
struct input_TRIG { };
struct output_T { };
struct output_F { };
template<typename PinT> struct ValueType { using T = void; };
template<> struct ValueType<input_GATE> { using T = Logic; };
template<> struct ValueType<input_TRIG> { using T = Logic; };
template<> struct ValueType<output_T> { using T = Logic; };
template<> struct ValueType<output_F> { using T = Logic; };
struct ContextObject {
Node* _node;
Logic _input_GATE;
Logic _input_TRIG;
bool _isInputDirty_TRIG;
};
using Context = ContextObject*;
template<typename PinT> typename ValueType<PinT>::T getValue(Context ctx) {
static_assert(always_false<PinT>::value,
"Invalid pin descriptor. Expected one of:" \
" input_GATE input_TRIG" \
" output_T output_F");
}
template<> Logic getValue<input_GATE>(Context ctx) {
return ctx->_input_GATE;
}
template<> Logic getValue<input_TRIG>(Context ctx) {
return ctx->_input_TRIG;
}
template<> Logic getValue<output_T>(Context ctx) {
return ctx->_node->output_T;
}
template<> Logic getValue<output_F>(Context ctx) {
return ctx->_node->output_F;
}
template<typename InputT> bool isInputDirty(Context ctx) {
static_assert(always_false<InputT>::value,
"Invalid input descriptor. Expected one of:" \
" input_TRIG");
return false;
}
template<> bool isInputDirty<input_TRIG>(Context ctx) {
return ctx->_isInputDirty_TRIG;
}
template<typename OutputT> void emitValue(Context ctx, typename ValueType<OutputT>::T val) {
static_assert(always_false<OutputT>::value,
"Invalid output descriptor. Expected one of:" \
" output_T output_F");
}
template<> void emitValue<output_T>(Context ctx, Logic val) {
ctx->_node->output_T = val;
ctx->_node->isOutputDirty_T = true;
}
template<> void emitValue<output_F>(Context ctx, Logic val) {
ctx->_node->output_F = val;
ctx->_node->isOutputDirty_F = true;
}
State* getState(Context ctx) {
return &ctx->_node->state;
}
void evaluate(Context ctx) {
if (!isInputDirty<input_TRIG>(ctx))
return;
if (getValue<input_GATE>(ctx)) {
emitValue<output_T>(ctx, 1);
} else {
emitValue<output_F>(ctx, 1);
}
}
} // namespace xod__core__gate
//-----------------------------------------------------------------------------
// xod/core/digital_input implementation
//-----------------------------------------------------------------------------
namespace xod__core__digital_input {
struct State {
int configuredPort = -1;
};
struct Node {
State state;
Logic output_SIG;
union {
struct {
bool isOutputDirty_SIG : 1;
bool isNodeDirty : 1;
};
DirtyFlags dirtyFlags;
};
};
struct input_PORT { };
struct input_UPD { };
struct output_SIG { };
template<typename PinT> struct ValueType { using T = void; };
template<> struct ValueType<input_PORT> { using T = Number; };
template<> struct ValueType<input_UPD> { using T = Logic; };
template<> struct ValueType<output_SIG> { using T = Logic; };
struct ContextObject {
Node* _node;
Number _input_PORT;
Logic _input_UPD;
bool _isInputDirty_UPD;
};
using Context = ContextObject*;
template<typename PinT> typename ValueType<PinT>::T getValue(Context ctx) {
static_assert(always_false<PinT>::value,
"Invalid pin descriptor. Expected one of:" \
" input_PORT input_UPD" \
" output_SIG");
}
template<> Number getValue<input_PORT>(Context ctx) {
return ctx->_input_PORT;
}
template<> Logic getValue<input_UPD>(Context ctx) {
return ctx->_input_UPD;
}
template<> Logic getValue<output_SIG>(Context ctx) {
return ctx->_node->output_SIG;
}
template<typename InputT> bool isInputDirty(Context ctx) {
static_assert(always_false<InputT>::value,
"Invalid input descriptor. Expected one of:" \
" input_UPD");
return false;
}
template<> bool isInputDirty<input_UPD>(Context ctx) {
return ctx->_isInputDirty_UPD;
}
template<typename OutputT> void emitValue(Context ctx, typename ValueType<OutputT>::T val) {
static_assert(always_false<OutputT>::value,
"Invalid output descriptor. Expected one of:" \
" output_SIG");
}
template<> void emitValue<output_SIG>(Context ctx, Logic val) {
ctx->_node->output_SIG = val;
ctx->_node->isOutputDirty_SIG = true;
}
State* getState(Context ctx) {
return &ctx->_node->state;
}
void evaluate(Context ctx) {
if (!isInputDirty<input_UPD>(ctx))
return;
State* state = getState(ctx);
const int port = (int)getValue<input_PORT>(ctx);
if (port != state->configuredPort) {
::pinMode(port, INPUT);
// Store configured port so to avoid repeating `pinMode` on
// subsequent requests
state->configuredPort = port;
}
emitValue<output_SIG>(ctx, ::digitalRead(port));
}
} // namespace xod__core__digital_input
//-----------------------------------------------------------------------------
// xod/core/flip_flop implementation
//-----------------------------------------------------------------------------
namespace xod__core__flip_flop {
struct State {
};
struct Node {
State state;
Logic output_MEM;
union {
struct {
bool isOutputDirty_MEM : 1;
bool isNodeDirty : 1;
};
DirtyFlags dirtyFlags;
};
};
struct input_SET { };
struct input_TGL { };
struct input_RST { };
struct output_MEM { };
template<typename PinT> struct ValueType { using T = void; };
template<> struct ValueType<input_SET> { using T = Logic; };
template<> struct ValueType<input_TGL> { using T = Logic; };
template<> struct ValueType<input_RST> { using T = Logic; };
template<> struct ValueType<output_MEM> { using T = Logic; };
struct ContextObject {
Node* _node;
Logic _input_SET;
Logic _input_TGL;
Logic _input_RST;
bool _isInputDirty_SET;
bool _isInputDirty_TGL;
bool _isInputDirty_RST;
};
using Context = ContextObject*;
template<typename PinT> typename ValueType<PinT>::T getValue(Context ctx) {
static_assert(always_false<PinT>::value,
"Invalid pin descriptor. Expected one of:" \
" input_SET input_TGL input_RST" \
" output_MEM");
}
template<> Logic getValue<input_SET>(Context ctx) {
return ctx->_input_SET;
}
template<> Logic getValue<input_TGL>(Context ctx) {
return ctx->_input_TGL;
}
template<> Logic getValue<input_RST>(Context ctx) {
return ctx->_input_RST;
}
template<> Logic getValue<output_MEM>(Context ctx) {
return ctx->_node->output_MEM;
}
template<typename InputT> bool isInputDirty(Context ctx) {
static_assert(always_false<InputT>::value,
"Invalid input descriptor. Expected one of:" \
" input_SET input_TGL input_RST");
return false;
}
template<> bool isInputDirty<input_SET>(Context ctx) {
return ctx->_isInputDirty_SET;
}
template<> bool isInputDirty<input_TGL>(Context ctx) {
return ctx->_isInputDirty_TGL;
}
template<> bool isInputDirty<input_RST>(Context ctx) {
return ctx->_isInputDirty_RST;
}
template<typename OutputT> void emitValue(Context ctx, typename ValueType<OutputT>::T val) {
static_assert(always_false<OutputT>::value,
"Invalid output descriptor. Expected one of:" \
" output_MEM");
}
template<> void emitValue<output_MEM>(Context ctx, Logic val) {
ctx->_node->output_MEM = val;
ctx->_node->isOutputDirty_MEM = true;
}
State* getState(Context ctx) {
return &ctx->_node->state;
}
void evaluate(Context ctx) {
bool oldState = getValue<output_MEM>(ctx);
bool newState = oldState;
if (isInputDirty<input_TGL>(ctx)) {
newState = !oldState;
} else if (isInputDirty<input_SET>(ctx)) {
newState = true;
} else {
newState = false;
}
if (newState == oldState)
return;
emitValue<output_MEM>(ctx, newState);
}
} // namespace xod__core__flip_flop
//-----------------------------------------------------------------------------
// xod/core/digital_output implementation
//-----------------------------------------------------------------------------
namespace xod__core__digital_output {
struct State {
int configuredPort = -1;
};
struct Node {
State state;
union {
struct {
bool isNodeDirty : 1;
};
DirtyFlags dirtyFlags;
};
};
struct input_PORT { };
struct input_SIG { };
template<typename PinT> struct ValueType { using T = void; };
template<> struct ValueType<input_PORT> { using T = Number; };
template<> struct ValueType<input_SIG> { using T = Logic; };
struct ContextObject {
Node* _node;
Number _input_PORT;
Logic _input_SIG;
};
using Context = ContextObject*;
template<typename PinT> typename ValueType<PinT>::T getValue(Context ctx) {
static_assert(always_false<PinT>::value,
"Invalid pin descriptor. Expected one of:" \
" input_PORT input_SIG" \
"");
}
template<> Number getValue<input_PORT>(Context ctx) {
return ctx->_input_PORT;
}
template<> Logic getValue<input_SIG>(Context ctx) {
return ctx->_input_SIG;
}
template<typename InputT> bool isInputDirty(Context ctx) {
static_assert(always_false<InputT>::value,
"Invalid input descriptor. Expected one of:" \
"");
return false;
}
template<typename OutputT> void emitValue(Context ctx, typename ValueType<OutputT>::T val) {
static_assert(always_false<OutputT>::value,
"Invalid output descriptor. Expected one of:" \
"");
}
State* getState(Context ctx) {
return &ctx->_node->state;
}
void evaluate(Context ctx) {
State* state = getState(ctx);
const int port = (int)getValue<input_PORT>(ctx);
if (port != state->configuredPort) {
::pinMode(port, OUTPUT);
// Store configured port so to avoid repeating `pinMode` call if just
// SIG is updated
state->configuredPort = port;
}
const bool val = getValue<input_SIG>(ctx);
::digitalWrite(port, val);
}
} // namespace xod__core__digital_output
//-----------------------------------------------------------------------------
// xod/core/continuously implementation
//-----------------------------------------------------------------------------
namespace xod__core__continuously {
struct State {
};
struct Node {
State state;
TimeMs timeoutAt;
Logic output_TICK;
union {
struct {
bool isOutputDirty_TICK : 1;
bool isNodeDirty : 1;
};
DirtyFlags dirtyFlags;
};
};
struct output_TICK { };
template<typename PinT> struct ValueType { using T = void; };
template<> struct ValueType<output_TICK> { using T = Logic; };
struct ContextObject {
Node* _node;
};
using Context = ContextObject*;
template<typename PinT> typename ValueType<PinT>::T getValue(Context ctx) {
static_assert(always_false<PinT>::value,
"Invalid pin descriptor. Expected one of:" \
"" \
" output_TICK");
}
template<> Logic getValue<output_TICK>(Context ctx) {
return ctx->_node->output_TICK;
}
template<typename InputT> bool isInputDirty(Context ctx) {
static_assert(always_false<InputT>::value,
"Invalid input descriptor. Expected one of:" \
"");
return false;
}
template<typename OutputT> void emitValue(Context ctx, typename ValueType<OutputT>::T val) {
static_assert(always_false<OutputT>::value,
"Invalid output descriptor. Expected one of:" \
" output_TICK");
}
template<> void emitValue<output_TICK>(Context ctx, Logic val) {
ctx->_node->output_TICK = val;
ctx->_node->isOutputDirty_TICK = true;
}
State* getState(Context ctx) {
return &ctx->_node->state;
}
void evaluate(Context ctx) {
emitValue<output_TICK>(ctx, 1);
setTimeout(ctx, 0);
}
} // namespace xod__core__continuously
} // namespace xod
/*=============================================================================
*
*
* Main loop components
*
*
=============================================================================*/
namespace xod {
// Define/allocate persistent storages (state, timeout, output data) for all nodes
constexpr Number node_0_output_VAL = 12;
constexpr Number node_1_output_VAL = 11;
constexpr Number node_2_output_VAL = 13;
constexpr Logic node_3_output_TICK = false;
xod__core__continuously::Node node_3 = {
xod__core__continuously::State(), // state default
0, // timeoutAt
node_3_output_TICK, // output TICK default
false, // TICK dirty
true // node itself dirty
};
constexpr Logic node_4_output_SIG = false;
xod__core__digital_input::Node node_4 = {
xod__core__digital_input::State(), // state default
node_4_output_SIG, // output SIG default
true, // SIG dirty
true // node itself dirty
};
constexpr Logic node_5_output_SIG = false;
xod__core__digital_input::Node node_5 = {
xod__core__digital_input::State(), // state default
node_5_output_SIG, // output SIG default
true, // SIG dirty
true // node itself dirty
};
constexpr Logic node_6_output_T = false;
constexpr Logic node_6_output_F = false;
xod__core__gate::Node node_6 = {
xod__core__gate::State(), // state default
node_6_output_T, // output T default
node_6_output_F, // output F default
false, // T dirty
false, // F dirty
true // node itself dirty
};
constexpr Logic node_7_output_T = false;
constexpr Logic node_7_output_F = false;
xod__core__gate::Node node_7 = {
xod__core__gate::State(), // state default
node_7_output_T, // output T default
node_7_output_F, // output F default
false, // T dirty
false, // F dirty
true // node itself dirty
};
constexpr Logic node_8_output_MEM = false;
xod__core__flip_flop::Node node_8 = {
xod__core__flip_flop::State(), // state default
node_8_output_MEM, // output MEM default
true, // MEM dirty
true // node itself dirty
};
xod__core__digital_output::Node node_9 = {
xod__core__digital_output::State(), // state default
true // node itself dirty
};
void runTransaction(bool firstRun) {
g_transactionTime = millis();
XOD_TRACE_F("Transaction started, t=");
XOD_TRACE_LN(g_transactionTime);
// Check for timeouts
detail::checkTriggerTimeout(&node_3);
// defer-* nodes are always at the very bottom of the graph, so no one will
// recieve values emitted by them. We must evaluate them before everybody
// else to give them a chance to emit values.
//
// If trigerred, keep only output dirty, not the node itself, so it will
// evaluate on the regular pass only if it pushed a new value again.
// Evaluate all dirty nodes
{ // xod__core__continuously #3
if (node_3.isNodeDirty) {
XOD_TRACE_F("Eval node #");
XOD_TRACE_LN(3);
xod__core__continuously::ContextObject ctxObj;
ctxObj._node = &node_3;
// copy data from upstream nodes into context
xod__core__continuously::evaluate(&ctxObj);
// mark downstream nodes dirty
node_6.isNodeDirty |= node_3.isOutputDirty_TICK;
node_4.isNodeDirty |= node_3.isOutputDirty_TICK;
node_7.isNodeDirty |= node_3.isOutputDirty_TICK;
node_5.isNodeDirty |= node_3.isOutputDirty_TICK;
}
}
{ // xod__core__digital_input #4
if (node_4.isNodeDirty) {
XOD_TRACE_F("Eval node #");
XOD_TRACE_LN(4);
xod__core__digital_input::ContextObject ctxObj;
ctxObj._node = &node_4;
// copy data from upstream nodes into context
ctxObj._input_PORT = node_0_output_VAL;
ctxObj._input_UPD = node_3.output_TICK;
ctxObj._isInputDirty_UPD = node_3.isOutputDirty_TICK;
xod__core__digital_input::evaluate(&ctxObj);
// mark downstream nodes dirty
node_6.isNodeDirty |= node_4.isOutputDirty_SIG;
}
}
{ // xod__core__digital_input #5
if (node_5.isNodeDirty) {
XOD_TRACE_F("Eval node #");
XOD_TRACE_LN(5);
xod__core__digital_input::ContextObject ctxObj;
ctxObj._node = &node_5;
// copy data from upstream nodes into context
ctxObj._input_PORT = node_1_output_VAL;
ctxObj._input_UPD = node_3.output_TICK;
ctxObj._isInputDirty_UPD = node_3.isOutputDirty_TICK;
xod__core__digital_input::evaluate(&ctxObj);
// mark downstream nodes dirty
node_7.isNodeDirty |= node_5.isOutputDirty_SIG;
}
}
{ // xod__core__gate #6
if (node_6.isNodeDirty) {
XOD_TRACE_F("Eval node #");
XOD_TRACE_LN(6);
xod__core__gate::ContextObject ctxObj;
ctxObj._node = &node_6;
// copy data from upstream nodes into context
ctxObj._input_GATE = node_4.output_SIG;
ctxObj._input_TRIG = node_3.output_TICK;
ctxObj._isInputDirty_TRIG = node_3.isOutputDirty_TICK;
xod__core__gate::evaluate(&ctxObj);
// mark downstream nodes dirty
node_8.isNodeDirty |= node_6.isOutputDirty_F;
}
}
{ // xod__core__gate #7
if (node_7.isNodeDirty) {
XOD_TRACE_F("Eval node #");
XOD_TRACE_LN(7);
xod__core__gate::ContextObject ctxObj;
ctxObj._node = &node_7;
// copy data from upstream nodes into context
ctxObj._input_GATE = node_5.output_SIG;
ctxObj._input_TRIG = node_3.output_TICK;
ctxObj._isInputDirty_TRIG = node_3.isOutputDirty_TICK;
xod__core__gate::evaluate(&ctxObj);
// mark downstream nodes dirty
node_8.isNodeDirty |= node_7.isOutputDirty_F;
}
}
{ // xod__core__flip_flop #8
if (node_8.isNodeDirty) {
XOD_TRACE_F("Eval node #");
XOD_TRACE_LN(8);
xod__core__flip_flop::ContextObject ctxObj;
ctxObj._node = &node_8;
// copy data from upstream nodes into context
ctxObj._input_SET = node_7.output_F;
ctxObj._input_RST = node_6.output_F;
ctxObj._isInputDirty_TGL = false;
ctxObj._isInputDirty_SET = node_7.isOutputDirty_F;
ctxObj._isInputDirty_RST = node_6.isOutputDirty_F;
xod__core__flip_flop::evaluate(&ctxObj);
// mark downstream nodes dirty
node_9.isNodeDirty |= node_8.isOutputDirty_MEM;
}
}
{ // xod__core__digital_output #9
if (node_9.isNodeDirty) {
XOD_TRACE_F("Eval node #");
XOD_TRACE_LN(9);
xod__core__digital_output::ContextObject ctxObj;
ctxObj._node = &node_9;
// copy data from upstream nodes into context
ctxObj._input_PORT = node_2_output_VAL;
ctxObj._input_SIG = node_8.output_MEM;
xod__core__digital_output::evaluate(&ctxObj);
// mark downstream nodes dirty
}
}
// Clear dirtieness and timeouts for all nodes and pins
node_3.dirtyFlags = 0;
node_4.dirtyFlags = 0;
node_5.dirtyFlags = 0;
node_6.dirtyFlags = 0;
node_7.dirtyFlags = 0;
node_8.dirtyFlags = 0;
node_9.dirtyFlags = 0;
detail::clearStaleTimeout(&node_3);
XOD_TRACE_F("Transaction completed, t=");
XOD_TRACE_LN(millis());
}
} // namespace xod