diff --git a/.gitmodules b/.gitmodules index 879382468..2eaf3e3ca 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,6 +1,12 @@ -[submodule "libraries/PacketSerial"] +[submodule "lib/PacketSerial"] + active = true path = lib/PacketSerial url = https://github.com/bakercp/PacketSerial.git -[submodule "libraries/Adafruit_MCP23008"] - url = https://github.com/adafruit/Adafruit-MCP23008-library.git +[submodule "lib/Adafruit_MCP23008"] + active = true path = lib/Adafruit_MCP23008 + url = https://github.com/adafruit/Adafruit-MCP23008-library.git +[submodule "lib/avr-libc"] + active = true + path = lib/avr-libc + url = https://github.com/avrdudes/avr-libc.git diff --git a/doc/finite_state_machine.md b/doc/finite_state_machine.md index 754d2f090..25645a66b 100644 --- a/doc/finite_state_machine.md +++ b/doc/finite_state_machine.md @@ -1,9 +1,10 @@ ### Finite State Machine -The finite state machine is defined in the `Fsm` class. +The finite state machine is defined in the `Controller` class. | State | Action | --: | :-- + `Idle` | Wait for information on machine type. `Init` | Wait for carriage to be put in the correct location. `Ready` | Wait to start operation. `Knit` | Operate in knitting mode. @@ -14,9 +15,11 @@ A tabular representation of state transitions follows. | Transition | Function / condition | --: | :-- - `Init -> Test` | `Tester::startTest()` - `Ready -> Test` | `Tester::startTest()` - `Test -> Init` | `Tester::quitCmd()` - `Init -> Ready` | `Knitter::isReady()` - `Ready -> Knit` | `Knitter::startKnitting()` - `Knit -> Ready` | `m_workedOnLine && m_lastLineFlag` + `Idle -> Init` | `Com::h_reqInit()` + `Init -> Ready` | `OpInit::update()` + `Ready -> Knit` | `OpKnit::startKnitting()` + `Init -> Test` | `OpTest::startTest()` + `Ready -> Test` | `OpTest::startTest()` + `Knit -> Test` | `OpTest::startTest()` + `Knit -> Init` | `m_workedOnLine && m_lastLineFlag` + `Test -> Init` | `OpTest::end()` diff --git a/platformio.ini b/platformio.ini index cf5bd9d53..9a94645ab 100644 --- a/platformio.ini +++ b/platformio.ini @@ -13,8 +13,8 @@ default_envs = uno [env] framework = arduino -extra_scripts = - pre:scripts/preBuild.py +extra_scripts = + pre:scripts/preBuild.py [env:uno] platform = atmelavr diff --git a/scripts/preBuild.py b/scripts/preBuild.py index b2fe7cb28..0f303e7fc 100644 --- a/scripts/preBuild.py +++ b/scripts/preBuild.py @@ -5,7 +5,7 @@ Import("env") print("Pre build script") -# Reads the current git tag of the repo and returns the version number +# Reads the current git tag of the repo and returns the version number # elements # In case there are changes since the last tag, the dirty flag is set # In case the git tag does not match the x.y.z format, 0.0.0 is used as fallback diff --git a/src/ayab/atomic.h b/src/ayab/atomic.h new file mode 100644 index 000000000..03e9123eb --- /dev/null +++ b/src/ayab/atomic.h @@ -0,0 +1,58 @@ +#include +#include + +/* Inline assembly suggested by @jpcornil-git */ + +#define ENTER_CRITICAL() __asm__ __volatile__ ( \ + "in __tmp_reg__, __SREG__" "\n\t" \ + "cli" "\n\t" \ + "push __tmp_reg__" "\n\t" \ + ::: "memory" \ + ) + +#define EXIT_CRITICAL() __asm__ __volatile__ ( \ + "pop __tmp_reg__" "\n\t" \ + "out __SREG__, __tmp_reg__" "\n\t" \ + ::: "memory" \ + ) + +/* Suggested by @jpcornil-git based on https://arduino.stackexchange.com/questions/77494/which-arduinos-support-atomic-block */ + +#define ATOMIC_BLOCK(type) for(type; type##_OBJECT_NAME.run(); type##_OBJECT_NAME.stop()) +#define ATOMIC_RESTORESTATE_OBJECT_NAME atomicBlockRestoreState_ +#define ATOMIC_RESTORESTATE AtomicBlockRestoreState ATOMIC_RESTORESTATE_OBJECT_NAME + +class AtomicBlockRestoreState +{ +public: + // Constructor: called when the object is created + inline AtomicBlockRestoreState() + { + sreg_save = SREG; // save status register + cli(); // turn interrupts OFF + } + + // Destructor: called when the object is destroyed (ex: goes out-of-scope) + inline ~AtomicBlockRestoreState() + { + SREG = sreg_save; // restore status register + __asm__ volatile ("" ::: "memory"); // memory barrier + } + + // Can we run? Returns true to run the `for` loop or + // `false` to stop it. + inline bool run() + { + return run_now; + } + + // Tell the `for` loop to stop + inline void stop() + { + run_now = false; + } + +private: + bool run_now = true; + uint8_t sreg_save; +}; diff --git a/src/ayab/beeper.cpp b/src/ayab/beeper.cpp index fe0f5330a..985433ec0 100644 --- a/src/ayab/beeper.cpp +++ b/src/ayab/beeper.cpp @@ -80,7 +80,7 @@ void Beeper::endWork() { /*! * Beep handler scheduled from main loop */ -void Beeper::schedule() { +void Beeper::update() { long unsigned int now = millis(); switch (m_currentState) { case BeepState::On: @@ -107,7 +107,7 @@ void Beeper::schedule() { } break; case BeepState::Idle: - default: + default: // GCOVR_EXCL_LINE break; } } diff --git a/src/ayab/beeper.h b/src/ayab/beeper.h index 4b1e23183..263dcd55b 100644 --- a/src/ayab/beeper.h +++ b/src/ayab/beeper.h @@ -17,7 +17,7 @@ * along with AYAB. If not, see . * * Original Work Copyright 2013 Christian Obersteiner, Andreas Müller - * Modified Work Copyright 2020 Sturla Lange, Tom Price + * Modified Work Copyright 2020-3 Sturla Lange, Tom Price * http://ayab-knitting.com */ @@ -44,12 +44,12 @@ class BeeperInterface { // any methods that need to be mocked should go here virtual void init(bool enabled) = 0; - virtual bool enabled() = 0; - virtual BeepState getState() = 0; + virtual void update() = 0; virtual void ready() = 0; virtual void finishedLine() = 0; virtual void endWork() = 0; - virtual void schedule() = 0; + virtual BeepState getState() = 0; + virtual bool enabled() = 0; }; // Container class for the static methods that control the beeper. @@ -67,12 +67,12 @@ class GlobalBeeper final { static BeeperInterface *m_instance; static void init(bool enabled); - static bool enabled(); - static BeepState getState(); + static void update(); static void ready(); static void finishedLine(); static void endWork(); - static void schedule(); + static BeepState getState(); + static bool enabled(); }; /*! @@ -81,12 +81,12 @@ class GlobalBeeper final { class Beeper : public BeeperInterface { public: void init(bool enabled) final; - bool enabled() final; - BeepState getState() final; + void update() final; void ready() final; void finishedLine() final; void endWork() final; - void schedule() final; + BeepState getState() final; + bool enabled() final; private: void beep(uint8_t repeats); diff --git a/src/ayab/com.cpp b/src/ayab/com.cpp index 1b680b94e..fd1e9468e 100644 --- a/src/ayab/com.cpp +++ b/src/ayab/com.cpp @@ -22,9 +22,13 @@ * http://ayab-knitting.com */ +#include "beeper.h" #include "com.h" -#include "knitter.h" -#include "tester.h" +#include "controller.h" + +#include "opInit.h" +#include "opKnit.h" +#include "opTest.h" /*! * \brief Initialize serial communication. @@ -43,6 +47,39 @@ void Com::update() { m_packetSerial.update(); } +/*! + * \brief Calculate CRC8 of a buffer. + * \param buffer A pointer to a data buffer. + * \param len The number of bytes of data in the data buffer. + * + * Based on + * https://www.leonardomiliani.com/en/2013/un-semplice-crc8-per-arduino/ + * + * CRC-8 - based on the CRC8 formulas by Dallas/Maxim + * code released under the therms of the GNU GPL 3.0 license + * + * Faster code using a lookup table is available, if needed. + */ +uint8_t Com::CRC8(const uint8_t *buffer, size_t len) const { + uint8_t crc = 0x00U; + + while (len--) { + uint8_t extract = *buffer; + buffer++; + + for (uint8_t tempI = 8U; tempI; tempI--) { + uint8_t sum = (crc ^ extract) & 0x01U; + crc >>= 1U; + + if (sum) { + crc ^= 0x8CU; + } + extract >>= 1U; + } + } + return crc; +} + /*! * \brief Send a packet of data. * \param payload A pointer to a data buffer. @@ -67,7 +104,7 @@ void Com::send(uint8_t *payload, size_t length) const { * \param id The msgid to be sent. * \param msg Pointer to a data buffer containing a null-terminated string. */ -void Com::sendMsg(AYAB_API_t id, const char *msg) { +void Com::sendMsg(API_t id, const char *msg) { uint8_t length = 0; msgBuffer[length++] = static_cast(id); while (*msg) { @@ -75,7 +112,7 @@ void Com::sendMsg(AYAB_API_t id, const char *msg) { } m_packetSerial.send(msgBuffer, length); } -void Com::sendMsg(AYAB_API_t id, char *msg) { +void Com::sendMsg(API_t id, char *msg) { sendMsg(id, static_cast(msg)); } @@ -86,7 +123,7 @@ void Com::sendMsg(AYAB_API_t id, char *msg) { */ void Com::send_reqLine(const uint8_t lineNumber, Err_t error) const { // `payload` will be allocated on stack since length is compile-time constant - uint8_t payload[REQLINE_LEN] = {static_cast(AYAB_API::reqLine), lineNumber, static_cast(error)}; + uint8_t payload[REQLINE_LEN] = {static_cast(API_t::reqLine), lineNumber, static_cast(error)}; send(static_cast(payload), REQLINE_LEN); } @@ -96,102 +133,35 @@ void Com::send_reqLine(const uint8_t lineNumber, Err_t error) const { * \param position Position of knitting carriage in needles from left hand side. * \param initState State of readiness (0 = ready, other values = not ready). */ -void Com::send_indState(Carriage_t carriage, uint8_t position, - Err_t error) const { +void Com::send_indState(Err_t error) const { uint16_t leftHallValue = GlobalEncoders::getHallValue(Direction_t::Left); uint16_t rightHallValue = GlobalEncoders::getHallValue(Direction_t::Right); // `payload` will be allocated on stack since length is compile-time constant uint8_t payload[INDSTATE_LEN] = { - static_cast(AYAB_API::indState), + static_cast(API_t::indState), static_cast(error), - static_cast(GlobalFsm::getState()), + static_cast(GlobalController::getState()->state()), highByte(leftHallValue), lowByte(leftHallValue), highByte(rightHallValue), lowByte(rightHallValue), - static_cast(carriage), - position, - static_cast(GlobalEncoders::getDirection()), + static_cast(GlobalController::getCarriage()), + GlobalController::getPosition(), + static_cast(GlobalController::getDirection()), }; send(static_cast(payload), INDSTATE_LEN); } -/*! +/*! GCOVR_EXCL_START + * * \brief Callback for PacketSerial. * \param buffer A pointer to a data buffer. * \param size The number of bytes in the data buffer. */ void Com::onPacketReceived(const uint8_t *buffer, size_t size) { - switch (buffer[0]) { - case static_cast(AYAB_API::reqInit): - h_reqInit(buffer, size); - break; - - case static_cast(AYAB_API::reqStart): - h_reqStart(buffer, size); - break; - - case static_cast(AYAB_API::cnfLine): - h_cnfLine(buffer, size); - break; - - case static_cast(AYAB_API::reqInfo): - h_reqInfo(); - break; - - case static_cast(AYAB_API::reqTest): - h_reqTest(buffer, size); - break; - - case static_cast(AYAB_API::helpCmd): - GlobalTester::helpCmd(); - break; - - case static_cast(AYAB_API::sendCmd): - GlobalTester::sendCmd(); - break; - - case static_cast(AYAB_API::beepCmd): - GlobalTester::beepCmd(); - break; - - case static_cast(AYAB_API::setSingleCmd): - GlobalTester::setSingleCmd(buffer, size); - break; - - case static_cast(AYAB_API::setAllCmd): - GlobalTester::setAllCmd(buffer, size); - break; - - case static_cast(AYAB_API::readEOLsensorsCmd): - GlobalTester::readEOLsensorsCmd(); - break; - - case static_cast(AYAB_API::readEncodersCmd): - GlobalTester::readEncodersCmd(); - break; - - case static_cast(AYAB_API::autoReadCmd): - GlobalTester::autoReadCmd(); - break; - - case static_cast(AYAB_API::autoTestCmd): - GlobalTester::autoTestCmd(); - break; - - case static_cast(AYAB_API::stopCmd): - GlobalTester::stopCmd(); - break; - - case static_cast(AYAB_API::quitCmd): - GlobalTester::quitCmd(); - break; - - default: - h_unrecognized(); - break; - } + GlobalController::getState()->com(buffer, size); } +// GCOVR_EXCL_STOP // Serial command handling @@ -203,23 +173,26 @@ void Com::onPacketReceived(const uint8_t *buffer, size_t size) { void Com::h_reqInit(const uint8_t *buffer, size_t size) { if (size < 3U) { // Need 3 bytes from buffer below. - send_cnfInit(ErrorCode::expected_longer_message); + send_cnfInit(Err_t::Expected_longer_message); return; } - auto machineType = static_cast(buffer[1]); - uint8_t crc8 = buffer[2]; // Check crc on bytes 0-4 of buffer. if (crc8 != CRC8(buffer, 2)) { - send_cnfInit(ErrorCode::checksum_error); + send_cnfInit(Err_t::Checksum_error); return; } - memset(lineBuffer, 0xFF, MAX_LINE_BUFFER_LEN); + auto machineType = static_cast(buffer[1]); + if (machineType == Machine_t::NoMachine) { + send_cnfInit(Err_t::No_machine_type); + return; + } - Err_t error = GlobalKnitter::initMachine(machineType); - send_cnfInit(error); + GlobalController::setMachineType(machineType); + GlobalController::setState(GlobalOpInit::m_instance); + send_cnfInit(Err_t::Success); } /*! @@ -230,22 +203,22 @@ void Com::h_reqInit(const uint8_t *buffer, size_t size) { void Com::h_reqStart(const uint8_t *buffer, size_t size) { if (size < 5U) { // Need 5 bytes from buffer below. - send_cnfStart(ErrorCode::expected_longer_message); + send_cnfStart(Err_t::Expected_longer_message); return; } - uint8_t startNeedle = buffer[1]; - uint8_t stopNeedle = buffer[2]; - auto continuousReportingEnabled = static_cast(buffer[3] & 1); - auto beeperEnabled = static_cast(buffer[3] & 2); - uint8_t crc8 = buffer[4]; // Check crc on bytes 0-4 of buffer. if (crc8 != CRC8(buffer, 4)) { - send_cnfStart(ErrorCode::checksum_error); + send_cnfStart(Err_t::Checksum_error); return; } + uint8_t startNeedle = buffer[1]; + uint8_t stopNeedle = buffer[2]; + auto continuousReportingEnabled = static_cast(buffer[3] & 1); + auto beeperEnabled = static_cast(buffer[3] & 2); + GlobalBeeper::init(beeperEnabled); memset(lineBuffer, 0xFF, MAX_LINE_BUFFER_LEN); @@ -253,7 +226,7 @@ void Com::h_reqStart(const uint8_t *buffer, size_t size) { // Previously, it returned `true` for success and `false` for failure. // Now, it returns `0` for success and an informative error code otherwise. Err_t error = - GlobalKnitter::startKnitting(startNeedle, stopNeedle, + GlobalOpKnit::startKnitting(startNeedle, stopNeedle, lineBuffer, continuousReportingEnabled); send_cnfStart(error); } @@ -267,7 +240,7 @@ void Com::h_reqStart(const uint8_t *buffer, size_t size) { * \todo sl: Assert size? Handle error? */ void Com::h_cnfLine(const uint8_t *buffer, size_t size) { - auto machineType = static_cast(GlobalKnitter::getMachineType()); + auto machineType = static_cast(GlobalController::getMachineType()); uint8_t lenLineBuffer = LINE_BUFFER_LEN[machineType]; if (size < lenLineBuffer + 5U) { // message is too short @@ -293,11 +266,11 @@ void Com::h_cnfLine(const uint8_t *buffer, size_t size) { return; } - if (GlobalKnitter::setNextLine(lineNumber)) { + if (GlobalOpKnit::setNextLine(lineNumber)) { // Line was accepted bool flagLastLine = bitRead(flags, 0U); if (flagLastLine) { - GlobalKnitter::setLastLine(); + GlobalOpKnit::setLastLine(); } } } @@ -313,33 +286,25 @@ void Com::h_reqInfo() const { /*! * \brief Handle `reqTest` (request hardware test) command. - * \param buffer A pointer to a data buffer. - * \param size The number of bytes in the data buffer. */ -void Com::h_reqTest(const uint8_t *buffer, size_t size) const { - if (size < 2U) { - // message is too short - send_cnfTest(ErrorCode::expected_longer_message); - return; - } - - auto machineType = static_cast(buffer[1]); +void Com::h_reqTest() const { + GlobalController::setState(GlobalOpTest::m_instance); + send_cnfTest(Err_t::Success); +} - // Note (August 2020): the return value of this function has changed. - // Previously, it returned `true` for success and `false` for failure. - // Now, it returns `0` for success and an informative error code otherwise. - Err_t error = GlobalTester::startTest(machineType); - send_cnfTest(error); +/*! + * \brief Handle `quitCmd` (cancel) command. + */ +void Com::h_quitCmd() const { + GlobalController::setState(GlobalOpInit::m_instance); } -// GCOVR_EXCL_START /*! * \brief Handle unrecognized command. */ void Com::h_unrecognized() const { // do nothing } -// GCOVR_EXCL_STOP /*! * \brief Send `cnfInfo` message. @@ -348,7 +313,7 @@ void Com::send_cnfInfo() const { // Max. length of suffix string: 16 bytes + \0 // `payload` will be allocated on stack since length is compile-time constant uint8_t payload[22]; - payload[0] = static_cast(AYAB_API::cnfInfo); + payload[0] = static_cast(API_t::cnfInfo); payload[1] = API_VERSION; payload[2] = FW_VERSION_MAJ; payload[3] = FW_VERSION_MIN; @@ -364,7 +329,7 @@ void Com::send_cnfInfo() const { void Com::send_cnfInit(Err_t error) const { // `payload` will be allocated on stack since length is compile-time constant uint8_t payload[2]; - payload[0] = static_cast(AYAB_API::cnfInit); + payload[0] = static_cast(API_t::cnfInit); payload[1] = static_cast(error); send(payload, 2); } @@ -377,7 +342,7 @@ void Com::send_cnfInit(Err_t error) const { void Com::send_cnfStart(Err_t error) const { // `payload` will be allocated on stack since length is compile-time constant uint8_t payload[2]; - payload[0] = static_cast(AYAB_API::cnfStart); + payload[0] = static_cast(API_t::cnfStart); payload[1] = static_cast(error); send(payload, 2); } @@ -389,41 +354,7 @@ void Com::send_cnfStart(Err_t error) const { void Com::send_cnfTest(Err_t error) const { // `payload` will be allocated on stack since length is compile-time constant uint8_t payload[2]; - payload[0] = static_cast(AYAB_API::cnfTest); + payload[0] = static_cast(API_t::cnfTest); payload[1] = static_cast(error); send(payload, 2); } - -/*! - * \brief Calculate CRC8 of a buffer. - * \param buffer A pointer to a data buffer. - * \param len The number of bytes of data in the data buffer. - * - * Based on - * https://www.leonardomiliani.com/en/2013/un-semplice-crc8-per-arduino/ - * - * CRC-8 - based on the CRC8 formulas by Dallas/Maxim - * code released under the therms of the GNU GPL 3.0 license - * - * Faster code using a lookup table is available, if needed. - */ -uint8_t Com::CRC8(const uint8_t *buffer, size_t len) const { - uint8_t crc = 0x00U; - - while (len--) { - uint8_t extract = *buffer; - buffer++; - - for (uint8_t tempI = 8U; tempI; tempI--) { - uint8_t sum = (crc ^ extract) & 0x01U; - crc >>= 1U; - - if (sum) { - crc ^= 0x8CU; - } - extract >>= 1U; - } - } - return crc; -} - diff --git a/src/ayab/com.h b/src/ayab/com.h index 343c06fcf..304c56abb 100644 --- a/src/ayab/com.h +++ b/src/ayab/com.h @@ -17,7 +17,7 @@ * along with AYAB. If not, see . * * Original Work Copyright 2013 Christian Obersteiner, Andreas Müller - * Modified Work Copyright 2020 Sturla Lange, Tom Price + * Modified Work Copyright 2020-3 Sturla Lange, Tom Price * http://ayab-knitting.com */ @@ -27,8 +27,7 @@ #include #include -#include "encoders.h" -#include "fsm.h" +#include "op.h" #ifndef AYAB_TESTS #include "version.h" @@ -72,7 +71,7 @@ enum class AYAB_API : unsigned char { testRes = 0xEE, debug = 0x9F }; -using AYAB_API_t = enum AYAB_API; +using API_t = enum AYAB_API; // API constants constexpr uint8_t INDSTATE_LEN = 10U; @@ -85,14 +84,21 @@ class ComInterface { // any methods that need to be mocked should go here virtual void init() = 0; virtual void update() = 0; + virtual uint8_t CRC8(const uint8_t *buffer, size_t len) const = 0; virtual void send(uint8_t *payload, size_t length) const = 0; - virtual void sendMsg(AYAB_API_t id, const char *msg) = 0; - virtual void sendMsg(AYAB_API_t id, char *msg) = 0; - virtual void send_reqLine(const uint8_t lineNumber, - Err_t error = ErrorCode::success) const = 0; - virtual void send_indState(Carriage_t carriage, uint8_t position, - Err_t error = ErrorCode::success) const = 0; + virtual void sendMsg(API_t id, const char *msg) = 0; + virtual void sendMsg(API_t id, char *msg) = 0; + virtual void send_reqLine(const uint8_t lineNumber, Err_t error = Err_t::Success) const = 0; + virtual void send_indState(Err_t error) const = 0; virtual void onPacketReceived(const uint8_t *buffer, size_t size) = 0; + + virtual void h_reqInit(const uint8_t *buffer, size_t size) = 0; + virtual void h_reqStart(const uint8_t *buffer, size_t size) = 0; + virtual void h_cnfLine(const uint8_t *buffer, size_t size) = 0; + virtual void h_reqInfo() const = 0; + virtual void h_reqTest() const = 0; + virtual void h_quitCmd() const = 0; + virtual void h_unrecognized() const = 0; }; // Container class for the static methods that implement the serial API. @@ -111,14 +117,22 @@ class GlobalCom final { static void init(); static void update(); + static uint8_t CRC8(const uint8_t *buffer, size_t len); static void send(uint8_t *payload, size_t length); - static void sendMsg(AYAB_API_t id, const char *msg); - static void sendMsg(AYAB_API_t id, char *msg); - static void send_reqLine(const uint8_t lineNumber, Err_t error = ErrorCode::success); - static void send_indState(Carriage_t carriage, uint8_t position, - Err_t error = ErrorCode::success); + static void sendMsg(API_t id, const char *msg); + static void sendMsg(API_t id, char *msg); + static void send_reqLine(const uint8_t lineNumber, Err_t error = Err_t::Success); + static void send_indState(Err_t error = Err_t::Success); static void onPacketReceived(const uint8_t *buffer, size_t size); + static void h_reqInit(const uint8_t *buffer, size_t size); + static void h_reqStart(const uint8_t *buffer, size_t size); + static void h_cnfLine(const uint8_t *buffer, size_t size); + static void h_reqInfo(); + static void h_reqTest(); + static void h_quitCmd(); + static void h_unrecognized(); + private: static SLIPPacketSerial m_packetSerial; }; @@ -127,31 +141,31 @@ class Com : public ComInterface { public: void init() final; void update() final; + uint8_t CRC8(const uint8_t *buffer, size_t len) const; void send(uint8_t *payload, size_t length) const final; - void sendMsg(AYAB_API_t id, const char *msg) final; - void sendMsg(AYAB_API_t id, char *msg) final; - void send_reqLine(const uint8_t lineNumber, Err_t error = ErrorCode::success) const final; - void send_indState(Carriage_t carriage, uint8_t position, - Err_t error = ErrorCode::success) const final; + void sendMsg(API_t id, const char *msg) final; + void sendMsg(API_t id, char *msg) final; + void send_reqLine(const uint8_t lineNumber, Err_t error = Err_t::Success) const final; + void send_indState(Err_t error = Err_t::Success) const final; void onPacketReceived(const uint8_t *buffer, size_t size) final; + void h_reqInit(const uint8_t *buffer, size_t size) final; + void h_reqStart(const uint8_t *buffer, size_t size) final; + void h_cnfLine(const uint8_t *buffer, size_t size) final; + void h_reqInfo() const final; + void h_reqTest() const final; + void h_quitCmd() const final; + void h_unrecognized() const final; + private: SLIPPacketSerial m_packetSerial; uint8_t lineBuffer[MAX_LINE_BUFFER_LEN] = {0}; uint8_t msgBuffer[MAX_MSG_BUFFER_LEN] = {0}; - void h_reqInit(const uint8_t *buffer, size_t size); - void h_reqStart(const uint8_t *buffer, size_t size); - void h_cnfLine(const uint8_t *buffer, size_t size); - void h_reqInfo() const; - void h_reqTest(const uint8_t *buffer, size_t size) const; - void h_unrecognized() const; - void send_cnfInfo() const; void send_cnfInit(Err_t error) const; void send_cnfStart(Err_t error) const; void send_cnfTest(Err_t error) const; - uint8_t CRC8(const uint8_t *buffer, size_t len) const; }; #endif // COM_H_ diff --git a/src/ayab/controller.cpp b/src/ayab/controller.cpp new file mode 100644 index 000000000..c04618aa7 --- /dev/null +++ b/src/ayab/controller.cpp @@ -0,0 +1,159 @@ +/*! + * \file controller.cpp + * \brief Class containing methods for knit and test operations. + * + * This file is part of AYAB. + * + * AYAB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * AYAB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with AYAB. If not, see . + * + * Original Work Copyright 2013-2015 Christian Obersteiner, Andreas Müller + * Modified Work Copyright 2020-3 Sturla Lange, Tom Price + * http://ayab-knitting.com + */ + +#include "board.h" +#include + +#include "encoders.h" +#include "controller.h" + +#include "opIdle.h" + +// Public methods + +/*! + * \brief Initialize Finite State Machine. + */ +void Controller::init() { + m_machineType = Machine_t::NoMachine; + m_carriage = Carriage_t::NoCarriage; + m_direction = Direction_t::NoDirection; + m_hallActive = Direction_t::NoDirection; + m_beltShift = BeltShift_t::Unknown; + m_position = 0; + m_currentState = GlobalOpIdle::m_instance; + m_nextState = GlobalOpIdle::m_instance; +} + +/*! + * \brief Dispatch on machine state; update machine state + */ +void Controller::update() { + cacheEncoders(); + m_currentState->update(); + + if (m_currentState == m_nextState) { + return; + } + + // else + m_currentState->end(); + m_nextState->begin(); + m_currentState = m_nextState; +} + +/*! + * \brief Cache Encoder values + * The code that saves the Encoder values is bookended by macros + * ensuring that interrupts are disabled while the code executes. + */ +void Controller::cacheEncoders() { +#ifndef AYAB_TESTS + ATOMIC_BLOCK(ATOMIC_RESTORESTATE) { + // start of critical code +#endif + m_beltShift = GlobalEncoders::getBeltShift(); + m_carriage = GlobalEncoders::getCarriage(); + m_direction = GlobalEncoders::getDirection(); + m_hallActive = GlobalEncoders::getHallActive(); + m_position = GlobalEncoders::getPosition(); +#ifndef AYAB_TESTS + // end of critical code + } +#endif +} + +/*! + * \brief Set machine state. + * \param state State. + * + * Does not take effect until next `update()` + */ +void Controller::setState(OpInterface *state) { + m_nextState = state; +} + +/*! + * \brief Get machine state. + * \return Current state of Finite State Machine. + */ +OpInterface *Controller::getState() { + return m_currentState; +} + +/*! + * \brief Set machine type. + * \param Machine type. + */ +void Controller::setMachineType(Machine_t machineType) { + m_machineType = machineType; +} + +/*! + * \brief Get knitting machine type. + * \return Machine type. + */ +Machine_t Controller::getMachineType() { + return m_machineType; +} + +/*! + * \brief Get cached beltShift value. + * \return Cached beltShift value. + */ +BeltShift_t Controller::getBeltShift() { + return m_beltShift; +} + +/*! + * \brief Get cached carriage value. + * \return Cached carriage value. + */ +Carriage_t Controller::getCarriage() { + return m_carriage; +} + +/*! + * \brief Get cached direction value. + * \return Cached direction value. + */ +Direction_t Controller::getDirection() { + return m_direction; +} + +/*! + * \brief Get cached hallActive value. + * \return Cached hallActive value. + */ +Direction_t Controller::getHallActive() { + return m_hallActive; +} + +/*! + * \brief Get cached position value. + * \return Cached position value. + */ +uint8_t Controller::getPosition() { + return m_position; +} diff --git a/src/ayab/controller.h b/src/ayab/controller.h new file mode 100644 index 000000000..2b9d0912b --- /dev/null +++ b/src/ayab/controller.h @@ -0,0 +1,116 @@ +/*!` + * \file controller.h + * + * This file is part of AYAB. + * + * AYAB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * AYAB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with AYAB. If not, see . + * + * Original Work Copyright 2013 Christian Obersteiner, Andreas Müller + * Modified Work Copyright 2020-3 Sturla Lange, Tom Price + * http://ayab-knitting.com + */ + +#ifndef CONTROLLER_H_ +#define CONTROLLER_H_ + +#include + +#include "encoders.h" +#include "op.h" + +class ControllerInterface { +public: + virtual ~ControllerInterface() = default; + + // any methods that need to be mocked should go here + virtual void init() = 0; + virtual void update() = 0; + virtual void cacheEncoders() = 0; + virtual void setState(OpInterface *state) = 0; + virtual OpInterface *getState() = 0; + virtual void setMachineType(Machine_t) = 0; + virtual Machine_t getMachineType() = 0; + virtual BeltShift_t getBeltShift() = 0; + virtual Carriage_t getCarriage() = 0; + virtual Direction_t getDirection() = 0; + virtual Direction_t getHallActive() = 0; + virtual uint8_t getPosition() = 0; +}; + +// Singleton container class for static methods. +// Dependency injection is enabled using a pointer +// to a global instance of either `Controller` or `ControllerMock` +// both of which classes implement the pure virtual methods +// of the `ControllerInterface` class. + +class GlobalController final { +private: + // singleton class so private constructor is appropriate + GlobalController() = default; + +public: + // pointer to global instance whose methods are implemented + static ControllerInterface *m_instance; + + static void init(); + static void update(); + static void cacheEncoders(); + static void setState(OpInterface *state); + static OpInterface *getState(); + static void setMachineType(Machine_t); + static Machine_t getMachineType(); + static BeltShift_t getBeltShift(); + static Carriage_t getCarriage(); + static Direction_t getDirection(); + static Direction_t getHallActive(); + static uint8_t getPosition(); +}; + +class Controller : public ControllerInterface { +public: + void init() final; + void update() final; + void cacheEncoders() final; + void setState(OpInterface *state) final; + OpInterface *getState() final; + void setMachineType(Machine_t) final; + Machine_t getMachineType() final; + BeltShift_t getBeltShift() final; + Carriage_t getCarriage() final; + Direction_t getDirection() final; + Direction_t getHallActive() final; + uint8_t getPosition() final; + + // machine state + OpInterface *m_currentState; + OpInterface *m_nextState; + + // machine type + Machine_t m_machineType; + + // cached Encoder values + BeltShift_t m_beltShift; + Carriage_t m_carriage; + Direction_t m_direction; + Direction_t m_hallActive; + uint8_t m_position; + +#if AYAB_TESTS + // Note: ideally tests would only rely on the public interface. + FRIEND_TEST(TestOpKnit, test_getStartOffset); + FRIEND_TEST(TestController, test_update_init); +#endif +}; + +#endif // CONTROLLER_H_ diff --git a/src/ayab/encoders.cpp b/src/ayab/encoders.cpp index 89ec47d9c..2d6fde150 100644 --- a/src/ayab/encoders.cpp +++ b/src/ayab/encoders.cpp @@ -19,23 +19,50 @@ * along with AYAB. If not, see . * * Original Work Copyright 2013-2015 Christian Obersteiner, Andreas Müller - * Modified Work Copyright 2020 Sturla Lange, Tom Price + * Modified Work Copyright 2020-3 Sturla Lange, Tom Price * http://ayab-knitting.com */ -#include "board.h" #include #include "encoders.h" +/*! + * \brief Initialize machine type. + * \param machineType Machine type. + */ +void Encoders::init(Machine_t machineType) { + m_machineType = machineType; + m_position = 0U; + m_direction = Direction_t::NoDirection; + m_hallActive = Direction_t::NoDirection; + m_beltShift = BeltShift::Unknown; + m_carriage = Carriage_t::NoCarriage; + m_oldState = false; +} /*! - * \brief Service encoder A interrupt routine. + * \brief Initialize interrupt service routine for Encoders object. + */ +void Encoders::setUpInterrupt() { +#ifndef AYAB_TESTS + // (re-)attach ENC_PIN_A(=2), interrupt #0 + detachInterrupt(digitalPinToInterrupt(ENC_PIN_A)); + // Attaching ENC_PIN_A, Interrupt #0 + // This interrupt cannot be enabled until + // the machine type has been validated. + attachInterrupt(digitalPinToInterrupt(ENC_PIN_A), GlobalEncoders::isr, CHANGE); +#endif // AYAB_TESTS +} + +/*! + * \brief Interrupt service routine. * + * Update machine state data. Must execute as fast as possible. * Determines edge of signal and dispatches to private rising/falling functions. - * `m_machineType` assumed valid. + * Machine type assumed valid. */ -void Encoders::encA_interrupt() { +void Encoders::isr() { m_hallActive = Direction_t::NoDirection; auto currentState = static_cast(digitalRead(ENC_PIN_A)); @@ -49,7 +76,7 @@ void Encoders::encA_interrupt() { } /*! - * \brief Read hall sensor on left and right. + * \brief Read Hall sensor on left and right. * \param pSensor Which sensor to read (left or right). */ uint16_t Encoders::getHallValue(Direction_t pSensor) { @@ -63,20 +90,6 @@ uint16_t Encoders::getHallValue(Direction_t pSensor) { } } -/*! - * \brief Initialize machine type. - * \param machineType Machine type. - */ -void Encoders::init(Machine_t machineType) { - m_machineType = machineType; - m_position = 0U; - m_direction = Direction_t::NoDirection; - m_hallActive = Direction_t::NoDirection; - m_beltShift = BeltShift::Unknown; - m_carriage = Carriage_t::NoCarriage; - m_oldState = false; -} - /*! * \brief Get position member. */ @@ -130,7 +143,7 @@ Machine_t Encoders::getMachineType() { */ void Encoders::encA_rising() { // Update direction - m_direction = digitalRead(ENC_PIN_B) != 0 ? Direction_t::Right : Direction_t::Left; + m_direction = digitalRead(ENC_PIN_B) ? Direction_t::Right : Direction_t::Left; // Update carriage position if ((Direction_t::Right == m_direction) && (m_position < END_RIGHT[static_cast(m_machineType)])) { @@ -149,10 +162,12 @@ void Encoders::encA_rising() { return; } - // In front of Left Hall Sensor? + // Hall value is used to detect whether carriage is in front of Left Hall sensor uint16_t hallValue = analogRead(EOL_PIN_L); + if ((hallValue < FILTER_L_MIN[static_cast(m_machineType)]) || (hallValue > FILTER_L_MAX[static_cast(m_machineType)])) { + // In front of Left Hall Sensor m_hallActive = Direction_t::Left; Carriage detected_carriage = Carriage_t::NoCarriage; @@ -171,9 +186,7 @@ void Encoders::encA_rising() { if (detected_carriage == Carriage_t::Knit) { start_position = start_position + MAGNET_DISTANCE_270; } - } else if (m_carriage == Carriage_t::NoCarriage) { - m_carriage = detected_carriage; - } else if (m_carriage != detected_carriage && m_position > start_position) { + } else if ((m_carriage != Carriage_t::NoCarriage) && (m_carriage != detected_carriage) && (m_position > start_position)) { m_carriage = Carriage_t::Garter; // Belt shift and start position were set when the first magnet passed @@ -181,10 +194,11 @@ void Encoders::encA_rising() { return; } else { m_carriage = detected_carriage; - } - // Belt shift signal only decided in front of hall sensor - m_beltShift = digitalRead(ENC_PIN_C) != 0 ? BeltShift::Regular : BeltShift::Shifted; + // Belt shift signal only decided in front of Hall sensor + // Belt shift is ignored for KH270 + m_beltShift = digitalRead(ENC_PIN_C) ? BeltShift::Regular : BeltShift::Shifted; + } // Known position of the carriage -> overwrite position m_position = start_position; @@ -207,27 +221,30 @@ void Encoders::encA_falling() { m_position = m_position - 1; } - // In front of Right Hall Sensor? + // Hall value is used to detect whether carriage is in front of Right Hall sensor uint16_t hallValue = analogRead(EOL_PIN_R); // Avoid 'comparison of unsigned expression < 0 is always false' // by being explicit about that behaviour being expected. bool hallValueSmall = false; - hallValueSmall = (hallValue < FILTER_R_MIN[static_cast(m_machineType)]); - if (hallValueSmall || hallValue > FILTER_R_MAX[static_cast(m_machineType)]) { + if (hallValueSmall || (hallValue > FILTER_R_MAX[static_cast(m_machineType)])) { + // In front of Right Hall Sensor m_hallActive = Direction_t::Right; - // The garter carriage has a second set of magnets that are going to + // The Garter carriage has a second set of magnets that are going to // pass the sensor and will reset state incorrectly if allowed to // continue. if (hallValueSmall && (m_carriage != Carriage_t::Garter)) { m_carriage = Carriage_t::Knit; } - // Belt shift signal only decided in front of hall sensor - m_beltShift = digitalRead(ENC_PIN_C) != 0 ? BeltShift::Shifted : BeltShift::Regular; + // Belt shift is ignored for KH270 + if (m_machineType != Machine_t::Kh270) { + // Belt shift signal only decided in front of Hall sensor + m_beltShift = digitalRead(ENC_PIN_C) ? BeltShift::Shifted : BeltShift::Regular; + } // Known position of the carriage -> overwrite position m_position = END_RIGHT_MINUS_OFFSET[static_cast(m_machineType)]; diff --git a/src/ayab/encoders.h b/src/ayab/encoders.h index 228e4d569..031d5b987 100644 --- a/src/ayab/encoders.h +++ b/src/ayab/encoders.h @@ -17,18 +17,18 @@ * along with AYAB. If not, see . * * Original Work Copyright 2013 Christian Obersteiner, Andreas Müller - * Modified Work Copyright 2020 Sturla Lange, Tom Price + * Modified Work Copyright 2020-3 Sturla Lange, Tom Price * http://ayab-knitting.com */ #ifndef ENCODERS_H_ #define ENCODERS_H_ +#include "board.h" #include // Enumerated constants - enum class Direction : unsigned char { NoDirection = 0xFF, Left = 0, @@ -84,19 +84,19 @@ constexpr uint8_t START_OFFSET[NUM_MACHINES][NUM_DIRECTIONS][NUM_CARRIAGES] = { { // K, L, G {40U, 40U, 32U}, // Left - {16U, 16U, 56U} // Right + {16U, 16U, 56U} // Right }, // KH930 { // K, L, G {40U, 40U, 8U}, // Left - {16U, 16U, 32U} // Right + {16U, 16U, 32U} // Right }, // KH270 { // K {28U, 0U, 0U}, // Left: x % 12 == 4 - {16U, 0U, 0U} // Right: (x + 6) % 12 == 10 + {16U, 0U, 0U} // Right: (x + 6) % 12 == 10 }}; // Should be calibrated to each device @@ -108,8 +108,6 @@ constexpr uint16_t FILTER_L_MAX[NUM_MACHINES] = { 600U, 600U, 600U}; constexpr uint16_t FILTER_R_MIN[NUM_MACHINES] = { 200U, 0U, 0U}; constexpr uint16_t FILTER_R_MAX[NUM_MACHINES] = {1023U, 600U, 600U}; -constexpr uint16_t SOLENOIDS_BITMASK = 0xFFFFU; - constexpr uint8_t MAGNET_DISTANCE_270 = 12U; /*! @@ -122,9 +120,10 @@ class EncodersInterface { virtual ~EncodersInterface() = default; // any methods that need to be mocked should go here - virtual void encA_interrupt() = 0; - virtual uint16_t getHallValue(Direction_t pSensor) = 0; virtual void init(Machine_t machineType) = 0; + virtual void setUpInterrupt() = 0; + virtual void isr() = 0; + virtual uint16_t getHallValue(Direction_t pSensor) = 0; virtual Machine_t getMachineType() = 0; virtual BeltShift_t getBeltShift() = 0; virtual Carriage_t getCarriage() = 0; @@ -147,9 +146,12 @@ class GlobalEncoders final { // pointer to global instance whose methods are implemented static EncodersInterface *m_instance; - static void encA_interrupt(); - static uint16_t getHallValue(Direction_t pSensor); static void init(Machine_t machineType); + static void setUpInterrupt(); +//#ifndef AYAB_TESTS + static void isr(); +//#endif + static uint16_t getHallValue(Direction_t pSensor); static Machine_t getMachineType(); static BeltShift_t getBeltShift(); static Carriage_t getCarriage(); @@ -162,9 +164,10 @@ class Encoders : public EncodersInterface { public: Encoders() = default; - void encA_interrupt() final; - uint16_t getHallValue(Direction_t pSensor) final; void init(Machine_t machineType) final; + void setUpInterrupt() final; + void isr() final; + uint16_t getHallValue(Direction_t pSensor) final; Machine_t getMachineType() final; BeltShift_t getBeltShift() final; Carriage_t getCarriage() final; @@ -184,6 +187,12 @@ class Encoders : public EncodersInterface { void encA_rising(); void encA_falling(); + +#if AYAB_TESTS + // Note: ideally tests would only rely on the public interface. + FRIEND_TEST(EncodersTest, test_encA_falling_not_in_front); + FRIEND_TEST(EncodersTest, test_encA_rising_in_front_notKH270); +#endif }; #endif // ENCODERS_H_ diff --git a/src/ayab/fsm.cpp b/src/ayab/fsm.cpp deleted file mode 100644 index f01a4a882..000000000 --- a/src/ayab/fsm.cpp +++ /dev/null @@ -1,174 +0,0 @@ -/*! - * \file fsm.cpp - * \brief Singleton class containing methods for the finite state machine - * that co-ordinates the AYAB firmware. - * - * This file is part of AYAB. - * - * AYAB is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * AYAB is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with AYAB. If not, see . - * - * Original Work Copyright 2013-2015 Christian Obersteiner, Andreas Müller - * Modified Work Copyright 2020-3 Sturla Lange, Tom Price - * http://ayab-knitting.com - */ - -// GCOVR_EXCL_START -// There are some odd gaps in the `gcovr` coverage for this file. -// Maybe this could happen if there were missing `Mock::VerifyAndClear` -// statements in `test_fsm.cpp`. - -#include "board.h" -#include - -#include "com.h" -#include "fsm.h" -#include "knitter.h" - -// Public methods - -/*! - * \brief Initialize Finite State Machine. - */ -void Fsm::init() { - m_currentState = OpState::wait_for_machine; - m_nextState = OpState::wait_for_machine; - m_flash = false; - m_flashTime = millis(); - m_error = ErrorCode::success; -} - -/*! - * \brief Get machine state. - * \return Current state of Finite State Machine. - */ -OpState_t Fsm::getState() { - return m_currentState; -} - -/*! - * \brief Set machine state. - * \param state State. - * - * Does not take effect until next `dispatch()` - */ -void Fsm::setState(OpState_t state) { - m_nextState = state; -} - -/*! - * \brief Dispatch on machine state - */ -void Fsm::dispatch() { - switch (m_currentState) { - case OpState::wait_for_machine: - state_wait_for_machine(); - break; - - case OpState::init: - state_init(); - break; - - case OpState::ready: - state_ready(); - break; - - case OpState::knit: - state_knit(); - break; - - case OpState::test: - state_test(); - break; - - case OpState::error: - state_error(); - break; - - default: - break; - } - m_currentState = m_nextState; - GlobalCom::update(); -} -// GCOVR_EXCL_STOP - -// Private methods - -/*! - * \brief Action of machine in state `wait_for_machine`. - */ -void Fsm::state_wait_for_machine() const { - digitalWrite(LED_PIN_A, LOW); // green LED off -} - -/*! - * \brief Action of machine in state `OpState::init`. - */ -void Fsm::state_init() { - digitalWrite(LED_PIN_A, LOW); // green LED off - if (GlobalKnitter::isReady()) { - setState(OpState::ready); - } -} - -/*! - * \brief Action of machine in state `OpState::ready`. - */ -void Fsm::state_ready() const { - digitalWrite(LED_PIN_A, LOW); // green LED off -} - -/*! - * \brief Action of machine in state `OpState::knit`. - */ -void Fsm::state_knit() const { - digitalWrite(LED_PIN_A, HIGH); // green LED on - GlobalKnitter::knit(); -} - -/*! - * \brief Action of machine in state `OpState::test`. - */ -void Fsm::state_test() const { - GlobalKnitter::encodePosition(); - GlobalTester::loop(); - if (m_nextState == OpState::init) { - // quit test - GlobalKnitter::init(); - } -} - -/*! - * \brief Action of machine in state `OpState::error`. - */ -void Fsm::state_error() { - if (m_nextState == OpState::init) { - // exit error state - digitalWrite(LED_PIN_B, LOW); // yellow LED off - GlobalKnitter::init(); - return; - } - // every 500ms - // send `indState` and flash LEDs - unsigned long now = millis(); - if (now - m_flashTime >= FLASH_DELAY) { - digitalWrite(LED_PIN_A, m_flash); // green LED - digitalWrite(LED_PIN_B, !m_flash); // yellow LED - m_flash = !m_flash; - m_flashTime = now; - - // send error message - GlobalKnitter::indState(m_error); - } -} diff --git a/src/ayab/fsm.h b/src/ayab/fsm.h deleted file mode 100644 index 798303a63..000000000 --- a/src/ayab/fsm.h +++ /dev/null @@ -1,142 +0,0 @@ -/*!` - * \file fsm.h - * - * This file is part of AYAB. - * - * AYAB is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * AYAB is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with AYAB. If not, see . - * - * Original Work Copyright 2013 Christian Obersteiner, Andreas Müller - * Modified Work Copyright 2020 Sturla Lange, Tom Price - * http://ayab-knitting.com - */ - -#ifndef FSM_H_ -#define FSM_H_ - -enum class OpState : unsigned char { - wait_for_machine, - init, - ready, - knit, - test, - error -}; -using OpState_t = enum OpState; - -// As of APIv6, the only important distinction -// is between `ErrorCode::success` (0) and any other value. -// Informative error codes are provided for -// diagnostic purposes (that is, for debugging). -// Non-zero error codes are subject to change. -// Such changes will be considered non-breaking. -enum class ErrorCode : unsigned char { - success = 0x00, - - // message not understood - expected_longer_message = 0x01, - unrecognized_msgid = 0x02, - unexpected_msgid = 0x03, - checksum_error = 0x04, - - // invalid arguments - machine_type_invalid = 0x10, - needle_value_invalid = 0x11, - null_pointer_argument = 0x12, - argument_invalid = 0x13, - arguments_incompatible = 0x13, - - // device not initialized - no_machine_type = 0x20, - no_carriage = 0x21, - no_direction = 0x22, - no_beltshift = 0x23, - - // machine in wrong FSM state - machine_state_init = 0xE0, - machine_state_ready = 0xE1, - machine_state_knit = 0xE2, - machine_state_test = 0xE3, - wrong_machine_state = 0xEF, - - // generic error codes - warning = 0xF0, // ignorable error - recoverable_error = 0xF1, - critical_error = 0xF2, - fatal_error = 0xF3, - unspecified_failure = 0xFF -}; -using Err_t = enum ErrorCode; - -constexpr unsigned int FLASH_DELAY = 500; // ms - -class FsmInterface { -public: - virtual ~FsmInterface() = default; - - // any methods that need to be mocked should go here - virtual void init() = 0; - virtual OpState_t getState() = 0; - virtual void setState(OpState_t state) = 0; - virtual void dispatch() = 0; -}; - -// Singleton container class for static methods. -// Dependency injection is enabled using a pointer -// to a global instance of either `Knitter` or `KnitterMock` -// both of which classes implement the pure virtual methods -// of the `KnitterInterface` class. - -class GlobalFsm final { -private: - // singleton class so private constructor is appropriate - GlobalFsm() = default; - -public: - // pointer to global instance whose methods are implemented - static FsmInterface *m_instance; - - static void init(); - static OpState_t getState(); - static void setState(OpState_t state); - static void dispatch(); -}; - -class Fsm : public FsmInterface { -public: - void init() final; - OpState_t getState() final; - void setState(OpState_t state) final; - void dispatch() final; - -private: - void state_wait_for_machine() const; - void state_init(); - void state_ready() const; - void state_knit() const; - void state_test() const; - void state_error(); - - // machine state - OpState_t m_currentState; - OpState_t m_nextState; - - // error state - Err_t m_error; - - // flashing LEDs in error state - bool m_flash; - unsigned long m_flashTime; -}; - -#endif // FSM_H_ diff --git a/test/mocks/fsm_mock.cpp b/src/ayab/global_OpError.cpp similarity index 58% rename from test/mocks/fsm_mock.cpp rename to src/ayab/global_OpError.cpp index d2ca46c06..6d06dd5fb 100644 --- a/test/mocks/fsm_mock.cpp +++ b/src/ayab/global_OpError.cpp @@ -1,5 +1,6 @@ -/*!` - * \file fsm_mock.cpp +/*! + * \file global_OpError.cpp + * \brief Singleton class containing methods for hardware testing. * * This file is part of AYAB. * @@ -21,41 +22,30 @@ * http://ayab-knitting.com */ -#include -#include +#include "opError.h" -static FsmMock *gFsmMock = nullptr; +// static member functions -FsmMock *fsmMockInstance() { - if (!gFsmMock) { - gFsmMock = new FsmMock(); - } - return gFsmMock; +OpState_t GlobalOpError::state() { + return m_instance->state(); } -void releaseFsmMock() { - if (gFsmMock) { - delete gFsmMock; - gFsmMock = nullptr; - } +void GlobalOpError::init() { + m_instance->init(); } -void Fsm::init() { - assert(gFsmMock != nullptr); - gFsmMock->init(); +void GlobalOpError::begin() { + m_instance->begin(); } -OpState_t Fsm::getState() { - assert(gFsmMock != nullptr); - return gFsmMock->getState(); +void GlobalOpError::update() { + m_instance->update(); } -void Fsm::setState(OpState_t state) { - assert(gFsmMock != nullptr); - gFsmMock->setState(state); +void GlobalOpError::com(const uint8_t *buffer, size_t size) { + m_instance->com(buffer, size); } -void Fsm::dispatch() { - assert(gFsmMock != nullptr); - gFsmMock->dispatch(); +void GlobalOpError::end() { + m_instance->end(); } diff --git a/src/ayab/global_fsm.cpp b/src/ayab/global_OpIdle.cpp similarity index 62% rename from src/ayab/global_fsm.cpp rename to src/ayab/global_OpIdle.cpp index 5b5543039..e08f39a9b 100644 --- a/src/ayab/global_fsm.cpp +++ b/src/ayab/global_OpIdle.cpp @@ -1,5 +1,7 @@ /*! - * \file global_fsm.cpp + * \file global_OpIdle.cpp + * \brief Singleton class containing methods for hardware testing. + * * This file is part of AYAB. * * AYAB is free software: you can redistribute it and/or modify @@ -16,26 +18,34 @@ * along with AYAB. If not, see . * * Original Work Copyright 2013 Christian Obersteiner, Andreas Müller - * Modified Work Copyright 2020 Sturla Lange, Tom Price + * Modified Work Copyright 2020-3 Sturla Lange, Tom Price * http://ayab-knitting.com */ -#include "fsm.h" +#include "opIdle.h" // static member functions -void GlobalFsm::init() { +OpState_t GlobalOpIdle::state() { + return m_instance->state(); +} + +void GlobalOpIdle::init() { m_instance->init(); } -OpState_t GlobalFsm::getState() { - return m_instance->getState(); +void GlobalOpIdle::begin() { + m_instance->begin(); +} + +void GlobalOpIdle::update() { + m_instance->update(); } -void GlobalFsm::setState(OpState_t state) { - m_instance->setState(state); +void GlobalOpIdle::com(const uint8_t *buffer, size_t size) { + m_instance->com(buffer, size); } -void GlobalFsm::dispatch() { - m_instance->dispatch(); +void GlobalOpIdle::end() { + m_instance->end(); } diff --git a/src/ayab/global_OpInit.cpp b/src/ayab/global_OpInit.cpp new file mode 100644 index 000000000..9e7237119 --- /dev/null +++ b/src/ayab/global_OpInit.cpp @@ -0,0 +1,56 @@ +/*! + * \file global_OpInit.cpp + * \brief Singleton class containing methods for hardware testing. + * + * This file is part of AYAB. + * + * AYAB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * AYAB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with AYAB. If not, see . + * + * Original Work Copyright 2013 Christian Obersteiner, Andreas Müller + * Modified Work Copyright 2020-3 Sturla Lange, Tom Price + * http://ayab-knitting.com + */ + +#include "opInit.h" + +// static member functions + +OpState_t GlobalOpInit::state() { + return m_instance->state(); +} + +void GlobalOpInit::init() { + m_instance->init(); +} + +void GlobalOpInit::begin() { + m_instance->begin(); +} + +void GlobalOpInit::update() { + m_instance->update(); +} + +void GlobalOpInit::com(const uint8_t *buffer, size_t size) { + m_instance->com(buffer, size); +} + +void GlobalOpInit::end() { + m_instance->end(); + +} + +bool GlobalOpInit::isReady() { + return m_instance->isReady(); +} diff --git a/src/ayab/global_knitter.cpp b/src/ayab/global_OpKnit.cpp similarity index 59% rename from src/ayab/global_knitter.cpp rename to src/ayab/global_OpKnit.cpp index 6b330cc96..467c7ea4d 100644 --- a/src/ayab/global_knitter.cpp +++ b/src/ayab/global_OpKnit.cpp @@ -1,5 +1,5 @@ /*! - * \file global_knitter.cpp + * \file global_OpKnit.cpp * \brief Singleton class containing methods for the finite state machine * that co-ordinates the AYAB firmware. * @@ -19,71 +19,62 @@ * along with AYAB. If not, see . * * Original Work Copyright 2013 Christian Obersteiner, Andreas Müller - * Modified Work Copyright 2020 Sturla Lange, Tom Price + * Modified Work Copyright 2020-3 Sturla Lange, Tom Price * http://ayab-knitting.com */ -#include "knitter.h" +#include "opKnit.h" // static member functions -void GlobalKnitter::init() { +OpState_t GlobalOpKnit::state() { + return m_instance->state(); +} + +void GlobalOpKnit::init() { m_instance->init(); } -void GlobalKnitter::setUpInterrupt() { - m_instance->setUpInterrupt(); +void GlobalOpKnit::begin() { + m_instance->begin(); +} + +void GlobalOpKnit::update() { + m_instance->update(); } -#ifndef AYAB_TESTS -void GlobalKnitter::isr() { - m_instance->isr(); +void GlobalOpKnit::com(const uint8_t *buffer, size_t size) { + m_instance->com(buffer, size); } -#endif -Err_t GlobalKnitter::initMachine(Machine_t machine) { - return m_instance->initMachine(machine); +void GlobalOpKnit::end() { + m_instance->end(); } -Err_t GlobalKnitter::startKnitting(uint8_t startNeedle, + +Err_t GlobalOpKnit::startKnitting(uint8_t startNeedle, uint8_t stopNeedle, uint8_t *pattern_start, bool continuousReportingEnabled) { return m_instance->startKnitting(startNeedle, stopNeedle, pattern_start, continuousReportingEnabled); } -void GlobalKnitter::encodePosition() { +void GlobalOpKnit::encodePosition() { m_instance->encodePosition(); } -bool GlobalKnitter::isReady() { - return m_instance->isReady(); -} - -void GlobalKnitter::knit() { +void GlobalOpKnit::knit() { m_instance->knit(); } -void GlobalKnitter::indState(Err_t error) { - m_instance->indState(error); -} - -uint8_t GlobalKnitter::getStartOffset(const Direction_t direction) { +uint8_t GlobalOpKnit::getStartOffset(const Direction_t direction) { return m_instance->getStartOffset(direction); } -Machine_t GlobalKnitter::getMachineType() { - return m_instance->getMachineType(); -} - -bool GlobalKnitter::setNextLine(uint8_t lineNumber) { +bool GlobalOpKnit::setNextLine(uint8_t lineNumber) { return m_instance->setNextLine(lineNumber); } -void GlobalKnitter::setLastLine() { +void GlobalOpKnit::setLastLine() { m_instance->setLastLine(); } - -void GlobalKnitter::setMachineType(Machine_t machineType) { - m_instance->setMachineType(machineType); -} diff --git a/src/ayab/global_OpReady.cpp b/src/ayab/global_OpReady.cpp new file mode 100644 index 000000000..7cfbd58c2 --- /dev/null +++ b/src/ayab/global_OpReady.cpp @@ -0,0 +1,51 @@ +/*! + * \file global_OpReady.cpp + * \brief Singleton class containing methods for hardware testing. + * + * This file is part of AYAB. + * + * AYAB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * AYAB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with AYAB. If not, see . + * + * Original Work Copyright 2013 Christian Obersteiner, Andreas Müller + * Modified Work Copyright 2020-3 Sturla Lange, Tom Price + * http://ayab-knitting.com + */ + +#include "opReady.h" + +// static member functions + +OpState_t GlobalOpReady::state() { + return m_instance->state(); +} + +void GlobalOpReady::init() { + m_instance->init(); +} + +void GlobalOpReady::begin() { + m_instance->begin(); +} + +void GlobalOpReady::update() { + m_instance->update(); +} + +void GlobalOpReady::com(const uint8_t *buffer, size_t size) { + m_instance->com(buffer, size); +} + +void GlobalOpReady::end() { + m_instance->end(); +} diff --git a/src/ayab/global_tester.cpp b/src/ayab/global_OpTest.cpp similarity index 57% rename from src/ayab/global_tester.cpp rename to src/ayab/global_OpTest.cpp index 45dba8fc0..929eb73d2 100644 --- a/src/ayab/global_tester.cpp +++ b/src/ayab/global_OpTest.cpp @@ -1,5 +1,5 @@ /*! - * \file global_tester.cpp + * \file global_OpTest.cpp * \brief Singleton class containing methods for hardware testing. * * This file is part of AYAB. @@ -18,68 +18,85 @@ * along with AYAB. If not, see . * * Original Work Copyright 2013 Christian Obersteiner, Andreas Müller - * Modified Work Copyright 2020 Sturla Lange, Tom Price + * Modified Work Copyright 2020-3 Sturla Lange, Tom Price * http://ayab-knitting.com */ -#include "tester.h" +#include "opTest.h" // static member functions -Err_t GlobalTester::startTest(Machine_t machineType) { - return m_instance->startTest(machineType); +OpState_t GlobalOpTest::state() { + return m_instance->state(); } -void GlobalTester::loop() { - m_instance->loop(); +void GlobalOpTest::init() { + m_instance->init(); } -void GlobalTester::helpCmd() { +void GlobalOpTest::begin() { + m_instance->begin(); +} + +void GlobalOpTest::update() { + m_instance->update(); +} + +void GlobalOpTest::com(const uint8_t *buffer, size_t size) { + m_instance->com(buffer, size); +} + +void GlobalOpTest::end() { + m_instance->end(); +} + + +bool GlobalOpTest::enabled() { + return m_instance->enabled(); +} + +void GlobalOpTest::helpCmd() { m_instance->helpCmd(); } -void GlobalTester::sendCmd() { +void GlobalOpTest::sendCmd() { m_instance->sendCmd(); } -void GlobalTester::beepCmd() { +void GlobalOpTest::beepCmd() { m_instance->beepCmd(); } -void GlobalTester::setSingleCmd(const uint8_t *buffer, size_t size) { +void GlobalOpTest::setSingleCmd(const uint8_t *buffer, size_t size) { m_instance->setSingleCmd(buffer, size); } -void GlobalTester::setAllCmd(const uint8_t *buffer, size_t size) { +void GlobalOpTest::setAllCmd(const uint8_t *buffer, size_t size) { m_instance->setAllCmd(buffer, size); } -void GlobalTester::readEOLsensorsCmd() { +void GlobalOpTest::readEOLsensorsCmd() { m_instance->readEOLsensorsCmd(); } -void GlobalTester::readEncodersCmd() { +void GlobalOpTest::readEncodersCmd() { m_instance->readEncodersCmd(); } -void GlobalTester::autoReadCmd() { +void GlobalOpTest::autoReadCmd() { m_instance->autoReadCmd(); } -void GlobalTester::autoTestCmd() { +void GlobalOpTest::autoTestCmd() { m_instance->autoTestCmd(); } -void GlobalTester::stopCmd() { +void GlobalOpTest::stopCmd() { m_instance->stopCmd(); } -void GlobalTester::quitCmd() { - m_instance->quitCmd(); -} - #ifndef AYAB_TESTS -void GlobalTester::encoderAChange() { +void GlobalOpTest::encoderAChange() { m_instance->encoderAChange(); } #endif // AYAB_TESTS diff --git a/src/ayab/global_beeper.cpp b/src/ayab/global_beeper.cpp index a6544a276..987e0d0d5 100644 --- a/src/ayab/global_beeper.cpp +++ b/src/ayab/global_beeper.cpp @@ -19,7 +19,7 @@ * along with AYAB. If not, see . * * Original Work Copyright 2013 Christian Obersteiner, Andreas Müller - * Modified Work Copyright 2020 Sturla Lange, Tom Price + * Modified Work Copyright 2020-3 Sturla Lange, Tom Price * http://ayab-knitting.com */ @@ -31,8 +31,8 @@ void GlobalBeeper::init(bool enabled) { m_instance->init(enabled); } -bool GlobalBeeper::enabled() { - return m_instance->enabled(); +void GlobalBeeper::update() { + m_instance->update(); } void GlobalBeeper::ready() { @@ -47,6 +47,10 @@ void GlobalBeeper::endWork() { m_instance->endWork(); } -void GlobalBeeper::schedule() { - m_instance->schedule(); +BeepState GlobalBeeper::getState() { + return m_instance->getState(); +} + +bool GlobalBeeper::enabled() { + return m_instance->enabled(); } diff --git a/src/ayab/global_com.cpp b/src/ayab/global_com.cpp index e7dea5b45..81b5b75b5 100644 --- a/src/ayab/global_com.cpp +++ b/src/ayab/global_com.cpp @@ -18,7 +18,7 @@ * along with AYAB. If not, see . * * Original Work Copyright 2013 Christian Obersteiner, Andreas Müller - * Modified Work Copyright 2020 Sturla Lange, Tom Price + * Modified Work Copyright 2020-3 Sturla Lange, Tom Price * http://ayab-knitting.com */ @@ -36,29 +36,60 @@ void GlobalCom::update() { m_instance->update(); } +uint8_t GlobalCom::CRC8(const uint8_t *buffer, size_t len) { + return m_instance->CRC8(buffer, len); +} + void GlobalCom::send(uint8_t *payload, size_t length) { m_instance->send(payload, length); } -void GlobalCom::sendMsg(AYAB_API_t id, const char *msg) { +void GlobalCom::sendMsg(API_t id, const char *msg) { m_instance->sendMsg(id, msg); } -void GlobalCom::sendMsg(AYAB_API_t id, char *msg) { +void GlobalCom::sendMsg(API_t id, char *msg) { m_instance->sendMsg(id, msg); } +void GlobalCom::send_reqLine(const uint8_t lineNumber, Err_t error) { + m_instance->send_reqLine(lineNumber, error); +} + +void GlobalCom::send_indState(Err_t error) { + m_instance->send_indState(error); +} + // GCOVR_EXCL_START void GlobalCom::onPacketReceived(const uint8_t *buffer, size_t size) { m_instance->onPacketReceived(buffer, size); } // GCOVR_EXCL_STOP -void GlobalCom::send_reqLine(const uint8_t lineNumber, Err_t error) { - m_instance->send_reqLine(lineNumber, error); +void GlobalCom::h_reqInit(const uint8_t *buffer, size_t size) { + m_instance->h_reqInit(buffer, size); +} + +void GlobalCom::h_reqStart(const uint8_t *buffer, size_t size) { + m_instance->h_reqStart(buffer, size); +} + +void GlobalCom::h_cnfLine(const uint8_t *buffer, size_t size) { + m_instance->h_cnfLine(buffer, size); +} + +void GlobalCom::h_reqInfo() { + m_instance->h_reqInfo(); +} + +void GlobalCom::h_reqTest() { + m_instance->h_reqTest(); +} + +void GlobalCom::h_quitCmd() { + m_instance->h_quitCmd(); } -void GlobalCom::send_indState(Carriage_t carriage, uint8_t position, - Err_t error) { - m_instance->send_indState(carriage, position, error); +void GlobalCom::h_unrecognized() { + m_instance->h_unrecognized(); } diff --git a/src/ayab/global_controller.cpp b/src/ayab/global_controller.cpp new file mode 100644 index 000000000..49297c5f8 --- /dev/null +++ b/src/ayab/global_controller.cpp @@ -0,0 +1,73 @@ +/*! + * \file global_controller.cpp + * This file is part of AYAB. + * + * AYAB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * AYAB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with AYAB. If not, see . + * + * Original Work Copyright 2013 Christian Obersteiner, Andreas Müller + * Modified Work Copyright 2020-3 Sturla Lange, Tom Price + * http://ayab-knitting.com + */ + +#include "controller.h" + +// static member functions + +void GlobalController::init() { + m_instance->init(); +} + +void GlobalController::update() { + m_instance->update(); +} + +void GlobalController::cacheEncoders() { + m_instance->cacheEncoders(); +} + +void GlobalController::setState(OpInterface* state) { + m_instance->setState(state); +} + +OpInterface *GlobalController::getState() { + return m_instance->getState(); +} + +void GlobalController::setMachineType(Machine_t machineType) { + m_instance->setMachineType(machineType); +} + +Machine_t GlobalController::getMachineType() { + return m_instance->getMachineType(); +} + +BeltShift_t GlobalController::getBeltShift() { + return m_instance->getBeltShift(); +} + +Carriage_t GlobalController::getCarriage() { + return m_instance->getCarriage(); +} + +Direction_t GlobalController::getDirection() { + return m_instance->getDirection(); +} + +Direction_t GlobalController::getHallActive() { + return m_instance->getHallActive(); +} + +uint8_t GlobalController::getPosition() { + return m_instance->getPosition(); +} diff --git a/src/ayab/global_encoders.cpp b/src/ayab/global_encoders.cpp index 85aa05e9f..79116dae8 100644 --- a/src/ayab/global_encoders.cpp +++ b/src/ayab/global_encoders.cpp @@ -19,22 +19,29 @@ * along with AYAB. If not, see . * * Original Work Copyright 2013 Christian Obersteiner, Andreas Müller - * Modified Work Copyright 2020 Sturla Lange, Tom Price + * Modified Work Copyright 2020-3 Sturla Lange, Tom Price * http://ayab-knitting.com */ #include "encoders.h" +#include "opKnit.h" -void GlobalEncoders::encA_interrupt() { - m_instance->encA_interrupt(); +void GlobalEncoders::init(Machine_t machineType) { + m_instance->init(machineType); } -uint16_t GlobalEncoders::getHallValue(Direction_t pSensor) { - return m_instance->getHallValue(pSensor); +void GlobalEncoders::setUpInterrupt() { + m_instance->setUpInterrupt(); } -void GlobalEncoders::init(Machine_t machineType) { - m_instance->init(machineType); +#ifndef AYAB_TESTS +void GlobalEncoders::isr() { + m_instance->isr(); +} +#endif // AYAB_TESTS + +uint16_t GlobalEncoders::getHallValue(Direction_t pSensor) { + return m_instance->getHallValue(pSensor); } BeltShift_t GlobalEncoders::getBeltShift() { diff --git a/src/ayab/global_solenoids.cpp b/src/ayab/global_solenoids.cpp index e77232c4c..556561aa6 100644 --- a/src/ayab/global_solenoids.cpp +++ b/src/ayab/global_solenoids.cpp @@ -19,7 +19,7 @@ * along with AYAB. If not, see . * * Original Work Copyright 2013 Christian Obersteiner, Andreas Müller - * Modified Work Copyright 2020 Sturla Lange, Tom Price + * Modified Work Copyright 2020-3 Sturla Lange, Tom Price * http://ayab-knitting.com */ diff --git a/src/ayab/main.cpp b/src/ayab/main.cpp index 3da59d969..ac0c6477e 100644 --- a/src/ayab/main.cpp +++ b/src/ayab/main.cpp @@ -18,7 +18,7 @@ * along with AYAB. If not, see . * * Original Work Copyright 2013 Christian Obersteiner, Andreas Müller - * Modified Work Copyright 2020 Sturla Lange, Tom Price + * Modified Work Copyright 2020-3 Sturla Lange, Tom Price * http://ayab-knitting.com */ @@ -26,22 +26,32 @@ #include "beeper.h" #include "com.h" +#include "controller.h" #include "encoders.h" -#include "fsm.h" -#include "knitter.h" #include "solenoids.h" -#include "tester.h" + +#include "opIdle.h" +#include "opInit.h" +#include "opReady.h" +#include "opKnit.h" +#include "opTest.h" +#include "opError.h" // Global definitions: references elsewhere must use `extern`. // Each of the following is a pointer to a singleton class // containing static methods. constexpr GlobalBeeper *beeper; constexpr GlobalCom *com; +constexpr GlobalController *controller; constexpr GlobalEncoders *encoders; -constexpr GlobalFsm *fsm; -constexpr GlobalKnitter *knitter; constexpr GlobalSolenoids *solenoids; -constexpr GlobalTester *tester; + +constexpr GlobalOpIdle *opIdle; +constexpr GlobalOpInit *opInit; +constexpr GlobalOpReady *opReady; +constexpr GlobalOpKnit *opKnit; +constexpr GlobalOpTest *opTest; +constexpr GlobalOpError *opError; // Initialize static members. // Each singleton class contains a pointer to a static instance @@ -50,28 +60,38 @@ constexpr GlobalTester *tester; BeeperInterface *GlobalBeeper::m_instance = new Beeper(); ComInterface *GlobalCom::m_instance = new Com(); EncodersInterface *GlobalEncoders::m_instance = new Encoders(); -FsmInterface *GlobalFsm::m_instance = new Fsm(); -KnitterInterface *GlobalKnitter::m_instance = new Knitter(); +ControllerInterface *GlobalController::m_instance = new Controller(); SolenoidsInterface *GlobalSolenoids::m_instance = new Solenoids(); -TesterInterface *GlobalTester::m_instance = new Tester(); + +OpIdleInterface *GlobalOpIdle::m_instance = new OpIdle(); +OpInitInterface *GlobalOpInit::m_instance = new OpInit(); +OpReadyInterface *GlobalOpReady::m_instance = new OpReady(); +OpKnitInterface *GlobalOpKnit::m_instance = new OpKnit(); +OpTestInterface *GlobalOpTest::m_instance = new OpTest(); +OpErrorInterface *GlobalOpError::m_instance = new OpError(); /*! * Setup - do once before going to the main loop. */ void setup() { + // Objects running in async context GlobalBeeper::init(false); GlobalCom::init(); - GlobalFsm::init(); - GlobalKnitter::init(); + GlobalController::init(); GlobalSolenoids::init(); + + GlobalOpKnit::init(); } /*! * Main Loop - repeat forever. */ void loop() { - GlobalFsm::dispatch(); + // Non-blocking methods + // Cooperative Round Robin scheduling + GlobalController::update(); + GlobalCom::update(); if (GlobalBeeper::enabled()) { - GlobalBeeper::schedule(); + GlobalBeeper::update(); } } diff --git a/src/ayab/op.h b/src/ayab/op.h new file mode 100644 index 000000000..09f0651c7 --- /dev/null +++ b/src/ayab/op.h @@ -0,0 +1,95 @@ +/*!` + * \file op.h + * + * This file is part of AYAB. + * + * AYAB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * AYAB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with AYAB. If not, see . + * + * Original Work Copyright 2013 Christian Obersteiner, Andreas Müller + * Modified Work Copyright 2020-3 Sturla Lange, Tom Price + * http://ayab-knitting.com + */ + +#ifndef OP_H_ +#define OP_H_ + +#include + +enum class OpState { + Idle = 0, + Init = 1, + Ready = 2, + Knit = 3, + Test = 4, + Error = 5}; +using OpState_t = enum OpState; + +// As of APIv6, the only important distinction +// is between `Err_t::Success` (0) and any other value. +// Informative error codes are provided for +// diagnostic purposes (that is, for debugging). +// Non-zero error codes are subject to change. +// Such changes will be considered non-breaking. +enum class ErrorCode : unsigned char { + Success = 0x00, + + // message not understood + Expected_longer_message = 0x01, + Unrecognized_msgid = 0x02, + Unexpected_msgid = 0x03, + Checksum_error = 0x04, + + // invalid arguments + Machine_type_invalid = 0x10, + Needle_value_invalid = 0x11, + Null_pointer_argument = 0x12, + Argument_invalid = 0x13, + Arguments_incompatible = 0x13, + + // device not initialized + No_machine_type = 0x20, + No_carriage = 0x21, + No_direction = 0x22, + No_beltshift = 0x23, + + // machine in wrong FSM state + Machine_state_init = 0xE0, + Machine_state_ready = 0xE1, + Machine_state_knit = 0xE2, + Machine_state_test = 0xE3, + Wrong_machine_state = 0xEF, + + // generic error codes + Warning = 0xF0, // ignorable error + Recoverable_error = 0xF1, + Critical_error = 0xF2, + Fatal_error = 0xF3, + Unspecified_failure = 0xFF +}; +using Err_t = enum ErrorCode; + +class OpInterface { +public: + virtual ~OpInterface() = default; + + // any methods that need to be mocked should go here + virtual OpState_t state() = 0; + virtual void init() = 0; + virtual void begin() = 0; + virtual void update() = 0; + virtual void com(const uint8_t *buffer, size_t size) = 0; + virtual void end() = 0; +}; + +#endif // OP_H_ diff --git a/src/ayab/opError.cpp b/src/ayab/opError.cpp new file mode 100644 index 000000000..6ca8af262 --- /dev/null +++ b/src/ayab/opError.cpp @@ -0,0 +1,86 @@ +/*! + * \file opError.cpp + * \brief Class containing methods for hardware testing. + * + * This file is part of AYAB. + * + * AYAB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * AYAB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with AYAB. If not, see . + * + * Original Work Copyright 2013 Christian Obersteiner, Andreas Müller + * Modified Work Copyright 2020-3 Sturla Lange, Tom Price + * http://ayab-knitting.com + */ + +#include "com.h" + +#include "opError.h" +#include "opKnit.h" + +/*! + * \brief Initialize state OpError + */ +void OpError::init() { +} + +/*! + * \brief enum OpState + * \return OpState_t::Error + */ +OpState_t OpError::state() { + return OpState_t::Error; +} + +/*! + * \brief Start state OpError + */ +void OpError::begin() { + m_flash = false; + m_flashTime = millis(); + m_error = Err_t::Success; // FIXME use other nonzero error code +} + +/*! + * \brief Update method for state OpError + */ +void OpError::update() { + // every 500 ms + // send `indState` and flash LEDs + uint32_t now = millis(); + if (now - m_flashTime >= FLASH_DELAY) { + digitalWrite(LED_PIN_A, m_flash); // green LED + digitalWrite(LED_PIN_B, !m_flash); // yellow LED + m_flash = !m_flash; + m_flashTime = now; + // send error message + GlobalCom::send_indState(m_error); + } +} + +/*! + * \brief Communication callback for state OpError + */ +void OpError::com(const uint8_t *buffer, size_t size) { + // to avoid warning about unused parameters + (void) buffer; + (void) size; +} + +/*! + * \brief Finish state OpError + */ +void OpError::end() { + digitalWrite(LED_PIN_A, LOW); // green LED off + digitalWrite(LED_PIN_B, LOW); // yellow LED off + GlobalOpKnit::init(); +} diff --git a/src/ayab/opError.h b/src/ayab/opError.h new file mode 100644 index 000000000..919fc397c --- /dev/null +++ b/src/ayab/opError.h @@ -0,0 +1,77 @@ +/*! + * \file opError.h + * This file is part of AYAB. + * + * AYAB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * AYAB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with AYAB. If not, see . + * + * Original Work Copyright 2013 Christian Obersteiner, Andreas Müller + * Modified Work Copyright 2020-3 Sturla Lange, Tom Price + * http://ayab-knitting.com + */ + +#ifndef OP_ERROR_H_ +#define OP_ERROR_H_ + +#include + +#include "op.h" + +constexpr uint16_t FLASH_DELAY = 500; // ms + +class OpErrorInterface : public OpInterface { +public: + virtual ~OpErrorInterface() = default; +}; + +// Container class for the static methods that implement the hardware test +// commands. Dependency injection is enabled using a pointer to a global +// instance of either `OpError` or `OpErrorMock`, both of which classes +// implement the pure virtual methods of the `OpErrorInterface` class. + +class GlobalOpError final { +private: + // singleton class so private constructor is appropriate + GlobalOpError() = default; + +public: + // pointer to global instance whose methods are implemented + static OpErrorInterface *m_instance; + + static OpState_t state(); + static void init(); + static void begin(); + static void update(); + static void com(const uint8_t *buffer, size_t size); + static void end(); +}; + +class OpError : public OpErrorInterface { +public: + OpState_t state() final; + void init() final; + void begin() final; + void update() final; + void com(const uint8_t *buffer, size_t size) final; + void end() final; + +private: + // error state + Err_t m_error; + + // flashing LEDs in error state + bool m_flash; + uint32_t m_flashTime; +}; + +#endif // OP_ERROR_H_ diff --git a/src/ayab/opIdle.cpp b/src/ayab/opIdle.cpp new file mode 100644 index 000000000..2fca70a2b --- /dev/null +++ b/src/ayab/opIdle.cpp @@ -0,0 +1,77 @@ +/*! + * \file opIdle.cpp + * \brief Class containing methods for hardware testing. + * + * This file is part of AYAB. + * + * AYAB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * AYAB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with AYAB. If not, see . + * + * Original Work Copyright 2013 Christian Obersteiner, Andreas Müller + * Modified Work Copyright 2020-3 Sturla Lange, Tom Price + * http://ayab-knitting.com + */ + +#include +#include "board.h" + +#include "com.h" +#include "opIdle.h" + +/*! + * \brief enum OpState + * \return OpState_t::Idle + */ +OpState_t OpIdle::state() { + return OpState_t::Idle; +} + +/*! + * \brief Initialize state OpIdle + */ +void OpIdle::init() { +} + +/*! + * \brief Start state OpIdle + */ +void OpIdle::begin() { + digitalWrite(LED_PIN_A, LOW); // green LED off +} + +/*! + * \brief Update method for state OpIdle + */ +void OpIdle::update() { +} + +/*! + * \brief Communication callback for state OpIdle + */ +void OpIdle::com(const uint8_t *buffer, size_t size) { + switch (buffer[0]) { + case static_cast(API_t::reqInit): + GlobalCom::h_reqInit(buffer, size); + break; + + default: + GlobalCom::h_unrecognized(); + break; + } +} + +/*! + * \brief Finish state OpIdle + */ +void OpIdle::end() { +} diff --git a/src/ayab/opIdle.h b/src/ayab/opIdle.h new file mode 100644 index 000000000..0bc04d274 --- /dev/null +++ b/src/ayab/opIdle.h @@ -0,0 +1,65 @@ +/*! + * \file opIdle.h + * This file is part of AYAB. + * + * AYAB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * AYAB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with AYAB. If not, see . + * + * Original Work Copyright 2013 Christian Obersteiner, Andreas Müller + * Modified Work Copyright 2020-3 Sturla Lange, Tom Price + * http://ayab-knitting.com + */ + +#ifndef OP_IDLE_H_ +#define OP_IDLE_H_ + +#include "op.h" + +class OpIdleInterface : public OpInterface { +public: + virtual ~OpIdleInterface() = default; +}; + +// Container class for the static methods that implement the hardware test +// commands. Dependency injection is enabled using a pointer to a global +// instance of either `OpIdle` or `OpIdleMock`, both of which classes +// implement the pure virtual methods of the `OpIdleInterface` class. + +class GlobalOpIdle final { +private: + // singleton class so private constructor is appropriate + GlobalOpIdle() = default; + +public: + // pointer to global instance whose methods are implemented + static OpIdleInterface *m_instance; + + static OpState_t state(); + static void init(); + static void begin(); + static void update(); + static void com(const uint8_t *buffer, size_t size); + static void end(); +}; + +class OpIdle : public OpIdleInterface { +public: + OpState_t state() final; + void init() final; + void begin() final; + void update() final; + void com(const uint8_t *buffer, size_t size) final; + void end() final; +}; + +#endif // OP_IDLE_H_ diff --git a/src/ayab/opInit.cpp b/src/ayab/opInit.cpp new file mode 100644 index 000000000..a4c37ef81 --- /dev/null +++ b/src/ayab/opInit.cpp @@ -0,0 +1,140 @@ +/*! + * \file opInit.cpp + * \brief Class containing methods for hardware testing. + * + * This file is part of AYAB. + * + * AYAB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * AYAB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with AYAB. If not, see . + * + * Original Work Copyright 2013 Christian Obersteiner, Andreas Müller + * Modified Work Copyright 2020-3 Sturla Lange, Tom Price + * http://ayab-knitting.com + */ + +#include +#include "board.h" + +#include "com.h" +#include "controller.h" +#include "solenoids.h" + +#include "opInit.h" +#include "opKnit.h" +#include "opReady.h" + +/*! + * \brief enum OpState + * \return OpState_t::Init + */ +OpState_t OpInit::state() { + return OpState_t::Init; +} + +/*! + * \brief Initialize state OpInit + */ +void OpInit::init() { + m_lastHall = Direction_t::NoDirection; +#ifdef DBG_NOMACHINE + m_prevState = false; +#endif +} + +/*! + * \brief Start state OpInit + */ +void OpInit::begin() { + GlobalEncoders::init(GlobalController::getMachineType()); + GlobalEncoders::setUpInterrupt(); + digitalWrite(LED_PIN_A, LOW); // green LED off +} + +/*! + * \brief Update method for state OpInit + * Assess whether the Finite State Machine is ready to move from state `OpInit` to `OpReady`. + */ +void OpInit::update() { + if (isReady()) { + GlobalController::setState(GlobalOpReady::m_instance); + } +} + +/*! + * \brief Assess whether the Finite State Machine is ready to move from state `OpInit` to `OpReady`. + * \return `true` if ready to move from state `OpInit` to `OpReady`, false otherwise. + */ +bool OpInit::isReady() { +#ifdef DBG_NOMACHINE + // TODO(who?): check if debounce is needed + bool state = digitalRead(DBG_BTN_PIN); + + if (m_prevState && !state) { +#else + // In order to support the garter carriage, we need to wait and see if there + // will be a second magnet passing the sensor. + // Keep track of the last seen Hall sensor because we may be making a decision + // after it passes. + auto hallActive = GlobalController::getHallActive(); + if (hallActive != Direction_t::NoDirection) { + m_lastHall = hallActive; + } + + auto direction = GlobalController::getDirection(); + auto position = GlobalController::getPosition(); + auto machineType = static_cast(GlobalController::getMachineType()); + bool passedLeft = (Direction_t::Right == direction) && (Direction_t::Left == m_lastHall) && + (position > (END_LEFT_PLUS_OFFSET[machineType] + GARTER_SLOP)); + bool passedRight = (Direction_t::Left == direction) && (Direction_t::Right == m_lastHall) && + (position < (END_RIGHT_MINUS_OFFSET[machineType] - GARTER_SLOP)); + + // Machine is initialized when Left Hall sensor is passed in Right direction + // New feature (August 2020): the machine is also initialized + // when the Right Hall sensor is passed in Left direction. + if (passedLeft || passedRight) { + +#endif // DBG_NOMACHINE + GlobalSolenoids::setSolenoids(SOLENOIDS_BITMASK); + GlobalCom::send_indState(Err_t::Success); + // move to `OpReady` + return true; + } + +#ifdef DBG_NOMACHINE + m_prevState = state; +#endif + // stay in `OpInit` + return false; +} + +/*! + * \brief Communication callback for state OpInit + */ +void OpInit::com(const uint8_t *buffer, size_t size) { + switch (buffer[0]) { + case static_cast(API_t::reqTest): + GlobalCom::h_reqTest(); + break; + + default: + GlobalCom::h_unrecognized(); + break; + } + (void) size; // unused +} + +/*! + * \brief Finish state OpInit + */ +void OpInit::end() { +} diff --git a/src/ayab/opInit.h b/src/ayab/opInit.h new file mode 100644 index 000000000..085c2c306 --- /dev/null +++ b/src/ayab/opInit.h @@ -0,0 +1,83 @@ +/*! + * \file opInit.h + * This file is part of AYAB. + * + * AYAB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * AYAB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with AYAB. If not, see . + * + * Original Work Copyright 2013 Christian Obersteiner, Andreas Müller + * Modified Work Copyright 2020-3 Sturla Lange, Tom Price + * http://ayab-knitting.com + */ + +#ifndef OP_INIT_H_ +#define OP_INIT_H_ + +#include "op.h" +#include "encoders.h" + +class OpInitInterface : public OpInterface { +public: + virtual ~OpInitInterface() = default; + + virtual bool isReady() = 0; +}; + +// Container class for the static methods that implement the hardware test +// commands. Dependency injection is enabled using a pointer to a global +// instance of either `OpInit` or `OpInitMock`, both of which classes +// implement the pure virtual methods of the `OpInitInterface` class. + +class GlobalOpInit final { +private: + // singleton class so private constructor is appropriate + GlobalOpInit() = default; + +public: + // pointer to global instance whose methods are implemented + static OpInitInterface *m_instance; + + static OpState_t state(); + static void init(); + static void begin(); + static void update(); + static void com(const uint8_t *buffer, size_t size); + static void end(); + + static bool isReady(); +}; + +class OpInit : public OpInitInterface { +public: + OpState_t state() final; + void init() final; + void begin() final; + void update() final; + void com(const uint8_t *buffer, size_t size) final; + void end() final; + + bool isReady() final; + +private: + Direction_t m_lastHall; +#ifdef DBG_NOMACHINE + bool m_prevState; +#endif + +#if AYAB_TESTS + // Note: ideally tests would only rely on the public interface. + FRIEND_TEST(OpInitTest, test_init); +#endif +}; + +#endif // OP_INIT_H_ diff --git a/src/ayab/knitter.cpp b/src/ayab/opKnit.cpp similarity index 51% rename from src/ayab/knitter.cpp rename to src/ayab/opKnit.cpp index 3af894c14..6224eeba6 100644 --- a/src/ayab/knitter.cpp +++ b/src/ayab/opKnit.cpp @@ -1,5 +1,5 @@ /*! - * \file knitter.cpp + * \file opKnit.cpp * \brief Class containing methods for the finite state machine * that co-ordinates the AYAB firmware. * @@ -23,15 +23,18 @@ * http://ayab-knitting.com */ -#include "board.h" #include +#include "board.h" #include "beeper.h" #include "com.h" +#include "controller.h" #include "encoders.h" -#include "fsm.h" -#include "knitter.h" -#include "tester.h" +#include "solenoids.h" + +#include "opInit.h" +#include "opReady.h" +#include "opKnit.h" #ifdef CLANG_TIDY // clang-tidy doesn't find these macros for some reason, @@ -41,11 +44,19 @@ constexpr uint16_t UINT16_MAX = 0xFFFFU; #endif /*! - * \brief Initialize Knitter object. + * \brief enum OpState + * \return OpState_t::Knit + */ +OpState_t OpKnit::state() { + return OpState_t::Knit; +} + +/*! + * \brief Initialize OpKnit object. * * Initialize the solenoids as well as pins and interrupts. */ -void Knitter::init() { +void OpKnit::init() { pinMode(ENC_PIN_A, INPUT); pinMode(ENC_PIN_B, INPUT); pinMode(ENC_PIN_C, INPUT); @@ -62,7 +73,6 @@ void Knitter::init() { // explicitly initialize members // job parameters - m_machineType = Machine_t::NoMachine; m_startNeedle = 0U; m_stopNeedle = 0U; m_lineBuffer = nullptr; @@ -74,88 +84,80 @@ void Knitter::init() { m_sOldPosition = 0U; m_firstRun = true; m_workedOnLine = false; - m_lastHall = Direction_t::NoDirection; - m_position = 0U; - m_hallActive = Direction_t::NoDirection; - m_pixelToSet = 0; #ifdef DBG_NOMACHINE m_prevState = false; #endif + + m_solenoidToSet = 0U; + m_pixelToSet = 0U; } /*! - * \brief Initialize interrupt service routine for Knitter object. + * \brief Start `OpKnit` operation. */ -void Knitter::setUpInterrupt() { - // (re-)attach ENC_PIN_A(=2), interrupt #0 - detachInterrupt(digitalPinToInterrupt(ENC_PIN_A)); -#ifndef AYAB_TESTS - // Attaching ENC_PIN_A, Interrupt #0 - // This interrupt cannot be enabled until - // the machine type has been validated. - attachInterrupt(digitalPinToInterrupt(ENC_PIN_A), GlobalKnitter::isr, CHANGE); -#endif // AYAB_TESTS +void OpKnit::begin() { + GlobalEncoders::init(GlobalController::getMachineType()); + GlobalEncoders::setUpInterrupt(); } /*! - * \brief Interrupt service routine. - * - * Update machine state data. - * Must execute as fast as possible. - * Machine type assumed valid. + * \brief Update knitting operation. */ -void Knitter::isr() { - // update machine state data - GlobalEncoders::encA_interrupt(); - m_position = GlobalEncoders::getPosition(); - m_direction = GlobalEncoders::getDirection(); - m_hallActive = GlobalEncoders::getHallActive(); - m_beltShift = GlobalEncoders::getBeltShift(); - m_carriage = GlobalEncoders::getCarriage(); +void OpKnit::update() { + knit(); } /*! - * \brief Initialize machine type. - * \param machineType Machine type. - * \return Error code (0 = success, other values = error). + * \brief Communication callback for knitting operation. */ -Err_t Knitter::initMachine(Machine_t machineType) { - if (GlobalFsm::getState() != OpState::wait_for_machine) { - return ErrorCode::wrong_machine_state; - } - if (machineType == Machine_t::NoMachine) { - return ErrorCode::no_machine_type; - } - m_machineType = machineType; +void OpKnit::com(const uint8_t *buffer, size_t size) { + switch (buffer[0]) { + case static_cast(API_t::cnfLine): + GlobalCom::h_cnfLine(buffer, size); + break; + + case static_cast(API_t::reqTest): + GlobalCom::h_reqTest(); + break; - GlobalEncoders::init(machineType); - GlobalFsm::setState(OpState::init); + // FIXME needs to be a `Cancel` command in the API that resets state from `OpKnit` to `OpInit` + default: + GlobalCom::h_unrecognized(); + break; + } +} - // Now that we have enough start state, we can set up interrupts - setUpInterrupt(); +/*! + * \brief Finish knitting operation. + */ +void OpKnit::end() { + GlobalBeeper::endWork(); + GlobalSolenoids::setSolenoids(SOLENOIDS_BITMASK); - return ErrorCode::success; + // detaching ENC_PIN_A, Interrupt #0 + /* detachInterrupt(digitalPinToInterrupt(ENC_PIN_A)); */ } /*! - * \brief Enter `OpState::knit` machine state. + * \brief Obtain parameters of knitting pattern. * \param startNeedle Position of first needle in the pattern. * \param stopNeedle Position of last needle in the pattern. * \param patternStart Pointer to buffer containing pattern data. * \param continuousReportingEnabled Flag variable indicating whether the device continuously reports its status to the host. * \return Error code (0 = success, other values = error). */ -Err_t Knitter::startKnitting(uint8_t startNeedle, +Err_t OpKnit::startKnitting(uint8_t startNeedle, uint8_t stopNeedle, uint8_t *pattern_start, bool continuousReportingEnabled) { - if (GlobalFsm::getState() != OpState::ready) { - return ErrorCode::wrong_machine_state; + Machine_t machineType = GlobalController::getMachineType(); + if (machineType == Machine_t::NoMachine) { + return Err_t::No_machine_type; } if (pattern_start == nullptr) { - return ErrorCode::null_pointer_argument; + return Err_t::Null_pointer_argument; } - if ((startNeedle >= stopNeedle) || (stopNeedle >= NUM_NEEDLES[static_cast(m_machineType)])) { - return ErrorCode::needle_value_invalid; + if ((startNeedle >= stopNeedle) || (stopNeedle >= NUM_NEEDLES[static_cast(machineType)])) { + return Err_t::Needle_value_invalid; } // record argument values @@ -171,11 +173,11 @@ Err_t Knitter::startKnitting(uint8_t startNeedle, m_lastLineFlag = false; // proceed to next state - GlobalFsm::setState(OpState::knit); + GlobalController::setState(GlobalOpKnit::m_instance); GlobalBeeper::ready(); // success - return ErrorCode::success; + return Err_t::Success; } /*! @@ -183,60 +185,21 @@ Err_t Knitter::startKnitting(uint8_t startNeedle, * * Used in hardware test procedure. */ -void Knitter::encodePosition() { - if (m_sOldPosition != m_position) { +void OpKnit::encodePosition() { + auto position = GlobalController::getPosition(); + if (m_sOldPosition != position) { // only act if there is an actual change of position // store current encoder position for next call of this function - m_sOldPosition = m_position; - calculatePixelAndSolenoid(); - indState(ErrorCode::unspecified_failure); - } -} - -/*! - * \brief Assess whether the Finite State Machine is ready to move from state `OpState::init` to `OpState::ready`. - * \return `true` if ready to move from state `OpState::init` to `OpState::ready`, false otherwise. - */ -bool Knitter::isReady() { -#ifdef DBG_NOMACHINE - // TODO(who?): check if debounce is needed - bool state = digitalRead(DBG_BTN_PIN); - - if (m_prevState && !state) { -#else - // In order to support the garter carriage, we need to wait and see if there - // will be a second magnet passing the sensor. - // Keep track of the last seen hall sensor because we may be making a decision - // after it passes. - if (m_hallActive != Direction_t::NoDirection) { - m_lastHall = m_hallActive; + m_sOldPosition = position; + (void) calculatePixelAndSolenoid(); + GlobalCom::send_indState(Err_t::Unspecified_failure); // FIXME is this the right error code? } - - bool passedLeft = (Direction_t::Right == m_direction) && (Direction_t::Left == m_lastHall) && - (m_position > (END_LEFT_PLUS_OFFSET[static_cast(m_machineType)] + GARTER_SLOP)); - bool passedRight = (Direction_t::Left == m_direction) && (Direction_t::Right == m_lastHall) && - (m_position < (END_RIGHT_MINUS_OFFSET[static_cast(m_machineType)] - GARTER_SLOP)); - // Machine is initialized when left Hall sensor is passed in Right direction - // New feature (August 2020): the machine is also initialized - // when the right Hall sensor is passed in Left direction. - if (passedLeft || passedRight) { - -#endif // DBG_NOMACHINE - GlobalSolenoids::setSolenoids(SOLENOIDS_BITMASK); - indState(ErrorCode::success); - return true; // move to `OpState::ready` - } - -#ifdef DBG_NOMACHINE - m_prevState = state; -#endif - return false; // stay in `OpState::init` } /*! - * \brief Function that is repeatedly called during state `OpState::knit` + * \brief Function that is repeatedly called during state `OpKnit` */ -void Knitter::knit() { +void OpKnit::knit() { if (m_firstRun) { m_firstRun = false; // TODO(who?): optimize delay for various Arduino models @@ -256,17 +219,18 @@ void Knitter::knit() { } m_prevState = state; #else + auto position = GlobalController::getPosition(); // only act if there is an actual change of position - if (m_sOldPosition == m_position) { + if (m_sOldPosition == position) { return; } // store current carriage position for next call of this function - m_sOldPosition = m_position; + m_sOldPosition = position; if (m_continuousReportingEnabled) { // send current position to GUI - indState(ErrorCode::success); + GlobalCom::send_indState(Err_t::Success); } if (!calculatePixelAndSolenoid()) { @@ -288,8 +252,9 @@ void Knitter::knit() { m_workedOnLine = true; } - if (((m_pixelToSet < m_startNeedle - END_OF_LINE_OFFSET_L[static_cast(m_machineType)]) || - (m_pixelToSet > m_stopNeedle + END_OF_LINE_OFFSET_R[static_cast(m_machineType)])) && + auto machineType = static_cast(GlobalController::getMachineType()); + if (((m_pixelToSet < m_startNeedle - END_OF_LINE_OFFSET_L[machineType]) || + (m_pixelToSet > m_stopNeedle + END_OF_LINE_OFFSET_R[machineType])) && m_workedOnLine) { // outside of the active needles and // already worked on the current line -> finished the line @@ -300,39 +265,26 @@ void Knitter::knit() { ++m_currentLineNumber; reqLine(m_currentLineNumber); } else if (m_lastLineFlag) { - stopKnitting(); + // move to state `OpInit` + GlobalController::setState(GlobalOpInit::m_instance); } } #endif // DBG_NOMACHINE } -/*! - * \brief Send `indState` message. - * \param error Error state (0 = success, other values = error). - */ -void Knitter::indState(Err_t error) { - GlobalCom::send_indState(m_carriage, m_position, error); -} - -/*! - * \brief Get knitting machine type. - * \return Machine type. - */ -Machine_t Knitter::getMachineType() { - return m_machineType; -} - /*! * \brief Get start offset. * \return Start offset, or 0 if unobtainable. */ -uint8_t Knitter::getStartOffset(const Direction_t direction) { +uint8_t OpKnit::getStartOffset(const Direction_t direction) { + auto carriage = GlobalController::getCarriage(); + auto machineType = GlobalController::getMachineType(); if ((direction == Direction_t::NoDirection) || - (m_carriage == Carriage_t::NoCarriage) || - (m_machineType == Machine_t::NoMachine)) { + (carriage == Carriage_t::NoCarriage) || + (machineType == Machine_t::NoMachine)) { return 0U; } - return START_OFFSET[static_cast(m_machineType)][static_cast(direction)][static_cast(m_carriage)]; + return START_OFFSET[static_cast(machineType)][static_cast(direction)][static_cast(carriage)]; } /*! @@ -340,7 +292,7 @@ uint8_t Knitter::getStartOffset(const Direction_t direction) { * \param lineNumber Line number (0-indexed and modulo 256). * \return `true` if successful, `false` otherwise. */ -bool Knitter::setNextLine(uint8_t lineNumber) { +bool OpKnit::setNextLine(uint8_t lineNumber) { if (m_lineRequested) { // Is there even a need for a new line? if (lineNumber == m_currentLineNumber) { @@ -359,26 +311,18 @@ bool Knitter::setNextLine(uint8_t lineNumber) { * \brief Get value of last line flag. * \param `true` if current line is the last line in the pattern, `false` otherwise. */ -void Knitter::setLastLine() { +void OpKnit::setLastLine() { m_lastLineFlag = true; } -/*! - * \brief Set machine type. - * \param Machine type. - */ -void Knitter::setMachineType(Machine_t machineType) { - m_machineType = machineType; -} - // private methods /*! * \brief Send `reqLine` message. * \param lineNumber Line number requested. */ -void Knitter::reqLine(uint8_t lineNumber) { - GlobalCom::send_reqLine(lineNumber, ErrorCode::success); +void OpKnit::reqLine(uint8_t lineNumber) { + GlobalCom::send_reqLine(lineNumber, Err_t::Success); m_lineRequested = true; } @@ -386,25 +330,31 @@ void Knitter::reqLine(uint8_t lineNumber) { * \brief Calculate the solenoid and pixel to be set. * \return `true` if successful, `false` otherwise. */ -bool Knitter::calculatePixelAndSolenoid() { +bool OpKnit::calculatePixelAndSolenoid() { uint8_t startOffset = 0; - switch (m_direction) { + auto position = GlobalController::getPosition(); + auto direction = GlobalController::getDirection(); + auto beltShift = GlobalController::getBeltShift(); + auto carriage = GlobalController::getCarriage(); + auto machineType = GlobalController::getMachineType(); + + switch (direction) { // calculate the solenoid and pixel to be set // implemented according to machine manual // magic numbers from machine manual case Direction_t::Right: startOffset = getStartOffset(Direction_t::Left); - if (m_position >= startOffset) { - m_pixelToSet = m_position - startOffset; + if (position >= startOffset) { + m_pixelToSet = position - startOffset; - if ((BeltShift::Regular == m_beltShift) || (m_machineType == Machine_t::Kh270)) { - m_solenoidToSet = m_position % SOLENOIDS_NUM[static_cast(m_machineType)]; - } else if (BeltShift::Shifted == m_beltShift) { - m_solenoidToSet = (m_position - HALF_SOLENOIDS_NUM[static_cast(m_machineType)]) % SOLENOIDS_NUM[static_cast(m_machineType)]; + if ((BeltShift::Regular == beltShift) || (machineType == Machine_t::Kh270)) { + m_solenoidToSet = position % SOLENOIDS_NUM[static_cast(machineType)]; + } else if (BeltShift::Shifted == beltShift) { + m_solenoidToSet = (position - HALF_SOLENOIDS_NUM[static_cast(machineType)]) % SOLENOIDS_NUM[static_cast(machineType)]; } - if (Carriage_t::Lace == m_carriage) { - m_pixelToSet = m_pixelToSet + HALF_SOLENOIDS_NUM[static_cast(m_machineType)]; + if (Carriage_t::Lace == carriage) { + m_pixelToSet = m_pixelToSet + HALF_SOLENOIDS_NUM[static_cast(machineType)]; } } else { return false; @@ -413,42 +363,29 @@ bool Knitter::calculatePixelAndSolenoid() { case Direction_t::Left: startOffset = getStartOffset(Direction_t::Right); - if (m_position <= (END_RIGHT[static_cast(m_machineType)] - startOffset)) { - m_pixelToSet = m_position - startOffset; + if (position <= (END_RIGHT[static_cast(machineType)] - startOffset)) { + m_pixelToSet = position - startOffset; - if ((BeltShift::Regular == m_beltShift) || (m_machineType == Machine_t::Kh270)) { - m_solenoidToSet = (m_position + HALF_SOLENOIDS_NUM[static_cast(m_machineType)]) % SOLENOIDS_NUM[static_cast(m_machineType)]; - } else if (BeltShift::Shifted == m_beltShift) { - m_solenoidToSet = m_position % SOLENOIDS_NUM[static_cast(m_machineType)]; + if ((BeltShift::Regular == beltShift) || (machineType == Machine_t::Kh270)) { + m_solenoidToSet = (position + HALF_SOLENOIDS_NUM[static_cast(machineType)]) % SOLENOIDS_NUM[static_cast(machineType)]; + } else if (BeltShift::Shifted == beltShift) { + m_solenoidToSet = position % SOLENOIDS_NUM[static_cast(machineType)]; } - if (Carriage_t::Lace == m_carriage) { - m_pixelToSet = m_pixelToSet - SOLENOIDS_NUM[static_cast(m_machineType)]; + if (Carriage_t::Lace == carriage) { + m_pixelToSet = m_pixelToSet - SOLENOIDS_NUM[static_cast(machineType)]; } } else { return false; } break; + case Direction_t::NoDirection: default: return false; } // The 270 has 12 solenoids but they get shifted over 3 bits - if (m_machineType == Machine_t::Kh270) { + if (machineType == Machine_t::Kh270) { m_solenoidToSet = m_solenoidToSet + 3; } return true; } - -/*! - * \brief Finish knitting procedure. - */ -void Knitter::stopKnitting() const { - GlobalBeeper::endWork(); - GlobalFsm::setState(OpState::ready); - - GlobalSolenoids::setSolenoids(SOLENOIDS_BITMASK); - GlobalBeeper::finishedLine(); - - // detaching ENC_PIN_A, Interrupt #0 - /* detachInterrupt(digitalPinToInterrupt(ENC_PIN_A)); */ -} diff --git a/src/ayab/knitter.h b/src/ayab/opKnit.h similarity index 66% rename from src/ayab/knitter.h rename to src/ayab/opKnit.h index 70c04792c..04a080674 100644 --- a/src/ayab/knitter.h +++ b/src/ayab/opKnit.h @@ -1,5 +1,5 @@ /*!` - * \file knitter.h + * \file opKnit.h * * This file is part of AYAB. * @@ -17,40 +17,29 @@ * along with AYAB. If not, see . * * Original Work Copyright 2013 Christian Obersteiner, Andreas Müller - * Modified Work Copyright 2020 Sturla Lange, Tom Price + * Modified Work Copyright 2020-3 Sturla Lange, Tom Price * http://ayab-knitting.com */ -#ifndef KNITTER_H_ -#define KNITTER_H_ +#ifndef OP_KNIT_H_ +#define OP_KNIT_H_ -#include "beeper.h" -#include "com.h" +#include "controller.h" #include "encoders.h" -#include "solenoids.h" -#include "tester.h" -class KnitterInterface { +class OpKnitInterface : public OpInterface { public: - virtual ~KnitterInterface() = default; + virtual ~OpKnitInterface() = default; // any methods that need to be mocked should go here - virtual void init() = 0; - virtual void setUpInterrupt() = 0; - virtual void isr() = 0; virtual Err_t startKnitting(uint8_t startNeedle, uint8_t stopNeedle, uint8_t *pattern_start, bool continuousReportingEnabled) = 0; - virtual Err_t initMachine(Machine_t machine) = 0; virtual void encodePosition() = 0; - virtual bool isReady() = 0; virtual void knit() = 0; - virtual void indState(Err_t error = ErrorCode::success) = 0; virtual uint8_t getStartOffset(const Direction_t direction) = 0; - virtual Machine_t getMachineType() = 0; virtual bool setNextLine(uint8_t lineNumber) = 0; virtual void setLastLine() = 0; - virtual void setMachineType(Machine_t) = 0; }; // Singleton container class for static methods. @@ -59,73 +48,61 @@ class KnitterInterface { // both of which classes implement the pure virtual methods // of the `KnitterInterface` class. -class GlobalKnitter final { +class GlobalOpKnit final { private: // singleton class so private constructor is appropriate - GlobalKnitter() = default; + GlobalOpKnit() = default; public: // pointer to global instance whose methods are implemented - static KnitterInterface *m_instance; + static OpKnitInterface *m_instance; + static OpState_t state(); static void init(); - static void setUpInterrupt(); -#ifndef AYAB_TESTS - static void isr(); -#endif + static void begin(); + static void update(); + static void com(const uint8_t *buffer, size_t size); + static void end(); + static Err_t startKnitting(uint8_t startNeedle, uint8_t stopNeedle, uint8_t *pattern_start, bool continuousReportingEnabled); - static Err_t initMachine(Machine_t machine); static void encodePosition(); - static bool isReady(); static void knit(); - static void indState(Err_t error = ErrorCode::success); static uint8_t getStartOffset(const Direction_t direction); - static Machine_t getMachineType(); static bool setNextLine(uint8_t lineNumber); static void setLastLine(); - static void setMachineType(Machine_t); }; -class Knitter : public KnitterInterface { +class OpKnit : public OpKnitInterface { public: + OpState_t state() final; void init() final; - void setUpInterrupt() final; - void isr() final; + void begin() final; + void update() final; + void com(const uint8_t *buffer, size_t size) final; + void end() final; + Err_t startKnitting(uint8_t startNeedle, uint8_t stopNeedle, uint8_t *pattern_start, bool continuousReportingEnabled) final; - Err_t initMachine(Machine_t machine) final; void encodePosition() final; - bool isReady() final; void knit() final; - void indState(Err_t error = ErrorCode::success) final; uint8_t getStartOffset(const Direction_t direction) final; - Machine_t getMachineType() final; bool setNextLine(uint8_t lineNumber) final; void setLastLine() final; - void setMachineType(Machine_t) final; private: void reqLine(uint8_t lineNumber); bool calculatePixelAndSolenoid(); - void stopKnitting() const; // job parameters - Machine_t m_machineType; uint8_t m_startNeedle; uint8_t m_stopNeedle; uint8_t *m_lineBuffer; bool m_continuousReportingEnabled; // current machine state - uint8_t m_position; - Direction_t m_direction; - Direction_t m_hallActive; - BeltShift_t m_beltShift; - Carriage_t m_carriage; - bool m_lineRequested; uint8_t m_currentLineNumber; bool m_lastLineFlag; @@ -133,7 +110,6 @@ class Knitter : public KnitterInterface { uint8_t m_sOldPosition; bool m_firstRun; bool m_workedOnLine; - Direction_t m_lastHall; #ifdef DBG_NOMACHINE bool m_prevState; #endif @@ -144,9 +120,11 @@ class Knitter : public KnitterInterface { #if AYAB_TESTS // Note: ideally tests would only rely on the public interface. - FRIEND_TEST(KnitterTest, test_getStartOffset); - FRIEND_TEST(KnitterTest, test_knit_lastLine_and_no_req); + FRIEND_TEST(OpKnitTest, test_encodePosition); + FRIEND_TEST(OpKnitTest, test_getStartOffset); + FRIEND_TEST(OpKnitTest, test_knit_lastLine_and_no_req); + FRIEND_TEST(OpKnitTest, test_calculatePixelAndSolenoid); #endif }; -#endif // KNITTER_H_ +#endif // OP_KNIT_H_ diff --git a/src/ayab/opReady.cpp b/src/ayab/opReady.cpp new file mode 100644 index 000000000..d47b0a12c --- /dev/null +++ b/src/ayab/opReady.cpp @@ -0,0 +1,81 @@ +/*! + * \file opReady.cpp + * \brief Class containing methods for hardware testing. + * + * This file is part of AYAB. + * + * AYAB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * AYAB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with AYAB. If not, see . + * + * Original Work Copyright 2013 Christian Obersteiner, Andreas Müller + * Modified Work Copyright 2020-3 Sturla Lange, Tom Price + * http://ayab-knitting.com + */ + +#include +#include "board.h" + +#include "com.h" +#include "opReady.h" + +/*! + * \brief enum OpState + * \return OpState_t::Ready + */ +OpState_t OpReady::state() { + return OpState_t::Ready; +} + +/*! + * \brief Initialize state OpReady + */ +void OpReady::init() { +} + +/*! + * \brief Start state OpReady + */ +void OpReady::begin() { + digitalWrite(LED_PIN_A, LOW); // green LED off +} + +/*! + * \brief Update method for state OpReady + */ +void OpReady::update() { +} + +/*! + * \brief Communication callback for state OpReady + */ +void OpReady::com(const uint8_t *buffer, size_t size) { + switch (buffer[0]) { + case static_cast(API_t::reqStart): + GlobalCom::h_reqStart(buffer, size); + break; + + case static_cast(API_t::reqTest): + GlobalCom::h_reqTest(); + break; + + default: + GlobalCom::h_unrecognized(); + break; + } +} + +/*! + * \brief Finish state OpReady + */ +void OpReady::end() { +} diff --git a/src/ayab/opReady.h b/src/ayab/opReady.h new file mode 100644 index 000000000..32f9eab00 --- /dev/null +++ b/src/ayab/opReady.h @@ -0,0 +1,65 @@ +/*! + * \file opReady.h + * This file is part of AYAB. + * + * AYAB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * AYAB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with AYAB. If not, see . + * + * Original Work Copyright 2013 Christian Obersteiner, Andreas Müller + * Modified Work Copyright 2020-3 Sturla Lange, Tom Price + * http://ayab-knitting.com + */ + +#ifndef OP_READY_H_ +#define OP_READY_H_ + +#include "op.h" + +class OpReadyInterface : public OpInterface { +public: + virtual ~OpReadyInterface() = default; +}; + +// Container class for the static methods that implement the hardware test +// commands. Dependency injection is enabled using a pointer to a global +// instance of either `OpReady` or `OpReadyMock`, both of which classes +// implement the pure virtual methods of the `OpReadyInterface` class. + +class GlobalOpReady final { +private: + // singleton class so private constructor is appropriate + GlobalOpReady() = default; + +public: + // pointer to global instance whose methods are implemented + static OpReadyInterface *m_instance; + + static OpState_t state(); + static void init(); + static void begin(); + static void update(); + static void com(const uint8_t *buffer, size_t size); + static void end(); +}; + +class OpReady : public OpReadyInterface { +public: + OpState_t state() final; + void init() final; + void begin() final; + void update() final; + void com(const uint8_t *buffer, size_t size) final; + void end() final; +}; + +#endif // OP_READY_H_ diff --git a/src/ayab/opTest.cpp b/src/ayab/opTest.cpp new file mode 100644 index 000000000..8c2d4dd43 --- /dev/null +++ b/src/ayab/opTest.cpp @@ -0,0 +1,378 @@ +/*! + * \file opTest.cpp + * \brief Class containing methods for hardware testing. + * + * This file is part of AYAB. + * + * AYAB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * AYAB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with AYAB. If not, see . + * + * Original Work Copyright 2013 Christian Obersteiner, Andreas Müller + * Modified Work Copyright 2020-3 Sturla Lange, Tom Price + * http://ayab-knitting.com + */ + +#include "board.h" + +#include "beeper.h" +#include "com.h" +#include "controller.h" +#include "encoders.h" +#include "solenoids.h" + +#include "opInit.h" +#include "opKnit.h" +#include "opTest.h" + +// public methods + +/*! + * \brief enum OpState + * \return OpState_t::Test + */ +OpState_t OpTest::state() { + return OpState_t::Test; +} + +/*! + * \brief Initialization for hardware tests. + */ +void OpTest::init() { +} + +/*! + * \brief Start hardware test. + * \param machineType Machine type. + */ +void OpTest::begin() { + // Print welcome message + GlobalCom::sendMsg(API_t::testRes, "AYAB Hardware OpTest, "); + snprintf(buf, BUFFER_LEN, "Firmware v%hhu", FW_VERSION_MAJ); + GlobalCom::sendMsg(API_t::testRes, buf); + snprintf(buf, BUFFER_LEN, ".%hhu", FW_VERSION_MIN); + GlobalCom::sendMsg(API_t::testRes, buf); + snprintf(buf, BUFFER_LEN, " API v%hhu\n\n", API_VERSION); + GlobalCom::sendMsg(API_t::testRes, buf); + helpCmd(); + +#ifndef AYAB_TESTS + // attach interrupt for ENC_PIN_A(=2), interrupt #0 + detachInterrupt(digitalPinToInterrupt(ENC_PIN_A)); + // Attaching ENC_PIN_A, Interrupt #0 + // This interrupt cannot be enabled until + // the machine type has been validated. + attachInterrupt(digitalPinToInterrupt(ENC_PIN_A), GlobalOpTest::encoderAChange, RISING); +#endif // AYAB_TESTS + + m_autoReadOn = false; + m_autoTestOn = false; + m_lastTime = millis(); + m_timerEventOdd = false; +} + +/*! + * \brief Main loop for hardware tests. + */ +void OpTest::update() { + if (enabled()) { + GlobalOpKnit::encodePosition(); + uint32_t now = millis(); + if (now - m_lastTime >= TEST_LOOP_DELAY) { + m_lastTime = now; + handleTimerEvent(); + } + } +} + +/*! + * \brief Communication callback for hardware tests. + */ +void OpTest::com(const uint8_t *buffer, size_t size) { + switch (buffer[0]) { + case static_cast(API_t::helpCmd): + helpCmd(); + break; + + case static_cast(API_t::sendCmd): + sendCmd(); + break; + + case static_cast(API_t::beepCmd): + beepCmd(); + break; + + case static_cast(API_t::setSingleCmd): + setSingleCmd(buffer, size); + break; + + case static_cast(API_t::setAllCmd): + setAllCmd(buffer, size); + break; + + case static_cast(API_t::readEOLsensorsCmd): + readEOLsensorsCmd(); + break; + + case static_cast(API_t::readEncodersCmd): + readEncodersCmd(); + break; + + case static_cast(API_t::autoReadCmd): + autoReadCmd(); + break; + + case static_cast(API_t::autoTestCmd): + autoTestCmd(); + break; + + case static_cast(API_t::stopCmd): + stopCmd(); + break; + + case static_cast(API_t::quitCmd): + GlobalCom::h_quitCmd(); + break; + + default: + GlobalCom::h_unrecognized(); + break; + } +} + +/*! + * \brief Finish hardware tests. + */ +void OpTest::end() { + m_autoReadOn = false; + m_autoTestOn = false; + GlobalOpKnit::init(); + GlobalEncoders::setUpInterrupt(); +} + +/*! + * \brief Returns whether the hardware test loop is active. + */ + bool OpTest::enabled() { + return m_autoReadOn || m_autoTestOn; +} + +/*! + * \brief Help command handler. + */ +void OpTest::helpCmd() { + GlobalCom::sendMsg(API_t::testRes, "The following commands are available:\n"); + GlobalCom::sendMsg(API_t::testRes, "setSingle [0..15] [1/0]\n"); + GlobalCom::sendMsg(API_t::testRes, "setAll [0..FFFF]\n"); + GlobalCom::sendMsg(API_t::testRes, "readEOLsensors\n"); + GlobalCom::sendMsg(API_t::testRes, "readEncoders\n"); + GlobalCom::sendMsg(API_t::testRes, "beep\n"); + GlobalCom::sendMsg(API_t::testRes, "autoRead\n"); + GlobalCom::sendMsg(API_t::testRes, "autoTest\n"); + GlobalCom::sendMsg(API_t::testRes, "send\n"); + GlobalCom::sendMsg(API_t::testRes, "stop\n"); + GlobalCom::sendMsg(API_t::testRes, "quit\n"); + GlobalCom::sendMsg(API_t::testRes, "help\n"); +} + +/*! + * \brief Send command handler. + */ +void OpTest::sendCmd() { + GlobalCom::sendMsg(API_t::testRes, "Called send\n"); + uint8_t p[] = {0x31, 0x32, 0x33}; + GlobalCom::send(p, 3); + GlobalCom::sendMsg(API_t::testRes, "\n"); +} + +/*! + * \brief Beep command handler. + */ +void OpTest::beepCmd() { + GlobalCom::sendMsg(API_t::testRes, "Called beep\n"); + beep(); +} + +/*! + * \brief Set single solenoid command handler. + * \param buffer Pointer to a data buffer. + * \param size Number of bytes of data in the buffer. + */ +void OpTest::setSingleCmd(const uint8_t *buffer, size_t size) { + GlobalCom::sendMsg(API_t::testRes, "Called setSingle\n"); + if (size < 3U) { + GlobalCom::sendMsg(API_t::testRes, "Error: invalid arguments\n"); + return; + } + uint8_t solenoidNumber = buffer[1]; + if (solenoidNumber > 15) { + snprintf(buf, BUFFER_LEN, "Error: invalid solenoid index %i\n", solenoidNumber); + GlobalCom::sendMsg(API_t::testRes, buf); + return; + } + uint8_t solenoidState = buffer[2]; + if (solenoidState > 1) { + snprintf(buf, BUFFER_LEN, "Error: invalid solenoid value %i\n", solenoidState); + GlobalCom::sendMsg(API_t::testRes, buf); + return; + } + GlobalSolenoids::setSolenoid(solenoidNumber, solenoidState); +} + +/*! + * \brief Set all solenoids command handler. + * \param buffer Pointer to a data buffer. + * \param size Number of bytes of data in the buffer. + */ +void OpTest::setAllCmd(const uint8_t *buffer, size_t size) { + GlobalCom::sendMsg(API_t::testRes, "Called setAll\n"); + if (size < 3U) { + GlobalCom::sendMsg(API_t::testRes, "Error: invalid arguments\n"); + return; + } + uint16_t solenoidState = (buffer[1] << 8) + buffer[2]; + GlobalSolenoids::setSolenoids(solenoidState); +} + +/*! + * \brief Read EOL sensors command handler. + */ +void OpTest::readEOLsensorsCmd() { + GlobalCom::sendMsg(API_t::testRes, "Called readEOLsensors\n"); + readEOLsensors(); + GlobalCom::sendMsg(API_t::testRes, "\n"); +} + +/*! + * \brief Read encoders command handler. + */ +void OpTest::readEncodersCmd() { + GlobalCom::sendMsg(API_t::testRes, "Called readEncoders\n"); + readEncoders(); + GlobalCom::sendMsg(API_t::testRes, "\n"); +} + +/*! + * \brief Auto read command handler. + */ +void OpTest::autoReadCmd() { + GlobalCom::sendMsg(API_t::testRes, "Called autoRead, send stop to quit\n"); + m_autoReadOn = true; +} + +/*! + * \brief Auto test command handler. + */ +void OpTest::autoTestCmd() { + GlobalCom::sendMsg(API_t::testRes, "Called autoTest, send stop to quit\n"); + m_autoTestOn = true; +} + +/*! + * \brief Stop command handler. + */ +void OpTest::stopCmd() { + m_autoReadOn = false; + m_autoTestOn = false; +} + +#ifndef AYAB_TESTS +/*! + * \brief Interrupt service routine for encoder A. + */ +void OpTest::encoderAChange() { + beep(); +} +#endif // AYAB_TESTS + +// Private member functions + +/*! + * \brief Make a beep. + */ +void OpTest::beep() const { + GlobalBeeper::ready(); +} + +/*! + * \brief Read the Hall sensors that determine which carriage is in use. + */ +void OpTest::readEncoders() const { + GlobalCom::sendMsg(API_t::testRes, " ENC_A: "); + bool state = digitalRead(ENC_PIN_A); + GlobalCom::sendMsg(API_t::testRes, state ? "HIGH" : "LOW"); + GlobalCom::sendMsg(API_t::testRes, " ENC_B: "); + state = digitalRead(ENC_PIN_B); + GlobalCom::sendMsg(API_t::testRes, state ? "HIGH" : "LOW"); + GlobalCom::sendMsg(API_t::testRes, " ENC_C: "); + state = digitalRead(ENC_PIN_C); + GlobalCom::sendMsg(API_t::testRes, state ? "HIGH" : "LOW"); +} + +/*! + * \brief Read the End of Line sensors. + */ +void OpTest::readEOLsensors() { + auto hallSensor = static_cast(analogRead(EOL_PIN_L)); + snprintf(buf, BUFFER_LEN, " EOL_L: %hu", hallSensor); + GlobalCom::sendMsg(API_t::testRes, buf); + hallSensor = static_cast(analogRead(EOL_PIN_R)); + snprintf(buf, BUFFER_LEN, " EOL_R: %hu", hallSensor); + GlobalCom::sendMsg(API_t::testRes, buf); +} + +/*! + * \brief Read both carriage sensors and End of Line sensors. + */ +void OpTest::autoRead() { + GlobalCom::sendMsg(API_t::testRes, "\n"); + readEOLsensors(); + readEncoders(); + GlobalCom::sendMsg(API_t::testRes, "\n"); +} + +/*! + * \brief Set even-numbered solenoids. + */ +void OpTest::autoTestEven() const { + GlobalCom::sendMsg(API_t::testRes, "Set even solenoids\n"); + digitalWrite(LED_PIN_A, HIGH); + digitalWrite(LED_PIN_B, HIGH); + GlobalSolenoids::setSolenoids(0xAAAA); +} + +/*! + * \brief Set odd-numbered solenoids. + */ +void OpTest::autoTestOdd() const { + GlobalCom::sendMsg(API_t::testRes, "Set odd solenoids\n"); + digitalWrite(LED_PIN_A, LOW); + digitalWrite(LED_PIN_B, LOW); + GlobalSolenoids::setSolenoids(0x5555); +} + +/*! + * \brief Timer event every 500ms to handle auto functions. + */ +void OpTest::handleTimerEvent() { + if (m_autoReadOn && m_timerEventOdd) { + autoRead(); + } + if (m_autoTestOn) { + if (m_timerEventOdd) { + autoTestOdd(); + } else { + autoTestEven(); + } + } + m_timerEventOdd = !m_timerEventOdd; +} diff --git a/src/ayab/tester.h b/src/ayab/opTest.h similarity index 72% rename from src/ayab/tester.h rename to src/ayab/opTest.h index 61fcf476c..f1b03aba1 100644 --- a/src/ayab/tester.h +++ b/src/ayab/opTest.h @@ -1,5 +1,5 @@ /*! - * \file tester.h + * \file opTest.h * This file is part of AYAB. * * AYAB is free software: you can redistribute it and/or modify @@ -16,29 +16,29 @@ * along with AYAB. If not, see . * * Original Work Copyright 2013 Christian Obersteiner, Andreas Müller - * Modified Work Copyright 2020 Sturla Lange, Tom Price + * Modified Work Copyright 2020-3 Sturla Lange, Tom Price * http://ayab-knitting.com */ -#ifndef TESTER_H_ -#define TESTER_H_ +#ifndef OP_TEST_H_ +#define OP_TEST_H_ #include #include "beeper.h" #include "com.h" #include "encoders.h" +#include "op.h" constexpr uint8_t BUFFER_LEN = 40; -constexpr unsigned int TEST_LOOP_DELAY = 500; // ms +constexpr uint16_t TEST_LOOP_DELAY = 500; // ms -class TesterInterface { +class OpTestInterface : public OpInterface { public: - virtual ~TesterInterface() = default; + virtual ~OpTestInterface() = default; // any methods that need to be mocked should go here - virtual Err_t startTest(Machine_t machineType) = 0; - virtual void loop() = 0; + virtual bool enabled() = 0; virtual void helpCmd() = 0; virtual void sendCmd() = 0; virtual void beepCmd() = 0; @@ -49,29 +49,33 @@ class TesterInterface { virtual void autoReadCmd() = 0; virtual void autoTestCmd() = 0; virtual void stopCmd() = 0; - virtual void quitCmd() = 0; #ifndef AYAB_TESTS virtual void encoderAChange(); #endif }; // Container class for the static methods that implement the hardware test -// commands. Originally these methods were called back by `SerialCommand`. -// Dependency injection is enabled using a pointer to a global instance of -// either `Tester` or `TesterMock`, both of which classes implement the -// pure virtual methods of `TesterInterface`. +// commands. Dependency injection is enabled using a pointer to a global +// instance of either `OpTest` or `OpTestMock`, both of which classes +// implement the pure virtual methods of the `OpTestInterface` class. -class GlobalTester final { +class GlobalOpTest final { private: // singleton class so private constructor is appropriate - GlobalTester() = default; + GlobalOpTest() = default; public: // pointer to global instance whose methods are implemented - static TesterInterface *m_instance; + static OpTestInterface *m_instance; - static Err_t startTest(Machine_t machineType); - static void loop(); + static OpState_t state(); + static void init(); + static void begin(); + static void update(); + static void com(const uint8_t *buffer, size_t size); + static void end(); + + static bool enabled(); static void helpCmd(); static void sendCmd(); static void beepCmd(); @@ -82,16 +86,21 @@ class GlobalTester final { static void autoReadCmd(); static void autoTestCmd(); static void stopCmd(); - static void quitCmd(); #ifndef AYAB_TESTS static void encoderAChange(); #endif }; -class Tester : public TesterInterface { +class OpTest : public OpTestInterface { public: - Err_t startTest(Machine_t machineType) final; - void loop() final; + OpState_t state() final; + void init() final; + void begin() final; + void update() final; + void com(const uint8_t *buffer, size_t size) final; + void end() final; + + bool enabled() final; void helpCmd() final; void sendCmd() final; void beepCmd() final; @@ -102,13 +111,11 @@ class Tester : public TesterInterface { void autoReadCmd() final; void autoTestCmd() final; void stopCmd() final; - void quitCmd() final; #ifndef AYAB_TESTS void encoderAChange() final; #endif private: - void setUp(); void beep() const; void readEOLsensors(); void readEncoders() const; @@ -119,10 +126,10 @@ class Tester : public TesterInterface { bool m_autoReadOn = false; bool m_autoTestOn = false; - unsigned long m_lastTime = 0U; + uint32_t m_lastTime = 0U; bool m_timerEventOdd = false; char buf[BUFFER_LEN] = {0}; }; -#endif // TESTER_H_ +#endif // OP_TEST_H_ diff --git a/src/ayab/solenoids.cpp b/src/ayab/solenoids.cpp index b74c8124c..7f53b5fa6 100644 --- a/src/ayab/solenoids.cpp +++ b/src/ayab/solenoids.cpp @@ -94,7 +94,7 @@ void Solenoids::setSolenoids(uint16_t state) { */ // GCOVR_EXCL_START void Solenoids::write(uint16_t newState) { - (void)newState; + //(void)newState; mcp_0.writeGPIO(lowByte(newState)); mcp_1.writeGPIO(highByte(newState)); } diff --git a/src/ayab/solenoids.h b/src/ayab/solenoids.h index f5c895806..793e5d63e 100644 --- a/src/ayab/solenoids.h +++ b/src/ayab/solenoids.h @@ -36,6 +36,7 @@ constexpr uint8_t SOLENOIDS_NUM[NUM_MACHINES] = {16U, 16U, 12U}; constexpr uint8_t HALF_SOLENOIDS_NUM[NUM_MACHINES] = {8U, 8U, 6U}; constexpr uint8_t SOLENOIDS_I2C_ADDRESS_MASK = 0x20U; constexpr uint8_t SOLENOID_BUFFER_SIZE = 16U; +constexpr uint16_t SOLENOIDS_BITMASK = 0xFFFFU; class SolenoidsInterface { public: diff --git a/src/ayab/tester.cpp b/src/ayab/tester.cpp deleted file mode 100644 index 71246c7eb..000000000 --- a/src/ayab/tester.cpp +++ /dev/null @@ -1,308 +0,0 @@ -/*! - * \file tester.cpp - * \brief Class containing methods for hardware testing. - * - * This file is part of AYAB. - * - * AYAB is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * AYAB is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with AYAB. If not, see . - * - * Original Work Copyright 2013 Christian Obersteiner, Andreas Müller - * Modified Work Copyright 2020-3 Sturla Lange, Tom Price - * http://ayab-knitting.com - */ - -#include - -#include "beeper.h" -#include "com.h" -#include "fsm.h" -#include "knitter.h" -#include "tester.h" - -// public methods - -/*! - * \brief Help command handler. - */ -void Tester::helpCmd() { - GlobalCom::sendMsg(AYAB_API::testRes, "The following commands are available:\n"); - GlobalCom::sendMsg(AYAB_API::testRes, "setSingle [0..15] [1/0]\n"); - GlobalCom::sendMsg(AYAB_API::testRes, "setAll [0..FFFF]\n"); - GlobalCom::sendMsg(AYAB_API::testRes, "readEOLsensors\n"); - GlobalCom::sendMsg(AYAB_API::testRes, "readEncoders\n"); - GlobalCom::sendMsg(AYAB_API::testRes, "beep\n"); - GlobalCom::sendMsg(AYAB_API::testRes, "autoRead\n"); - GlobalCom::sendMsg(AYAB_API::testRes, "autoTest\n"); - GlobalCom::sendMsg(AYAB_API::testRes, "send\n"); - GlobalCom::sendMsg(AYAB_API::testRes, "stop\n"); - GlobalCom::sendMsg(AYAB_API::testRes, "quit\n"); - GlobalCom::sendMsg(AYAB_API::testRes, "help\n"); -} - -/*! - * \brief Send command handler. - */ -void Tester::sendCmd() { - GlobalCom::sendMsg(AYAB_API::testRes, "Called send\n"); - uint8_t p[] = {0x31, 0x32, 0x33}; - GlobalCom::send(p, 3); - GlobalCom::sendMsg(AYAB_API::testRes, "\n"); -} - -/*! - * \brief Beep command handler. - */ -void Tester::beepCmd() { - GlobalCom::sendMsg(AYAB_API::testRes, "Called beep\n"); - beep(); -} - -/*! - * \brief Set single solenoid command handler. - * \param buffer Pointer to a data buffer. - * \param size Number of bytes of data in the buffer. - */ -void Tester::setSingleCmd(const uint8_t *buffer, size_t size) { - GlobalCom::sendMsg(AYAB_API::testRes, "Called setSingle\n"); - if (size < 3U) { - GlobalCom::sendMsg(AYAB_API::testRes, "Error: invalid arguments\n"); - return; - } - uint8_t solenoidNumber = buffer[1]; - if (solenoidNumber > 15) { - snprintf(buf, BUFFER_LEN, "Error: invalid solenoid index %i\n", solenoidNumber); - GlobalCom::sendMsg(AYAB_API::testRes, buf); - return; - } - uint8_t solenoidState = buffer[2]; - if (solenoidState > 1) { - snprintf(buf, BUFFER_LEN, "Error: invalid solenoid value %i\n", solenoidState); - GlobalCom::sendMsg(AYAB_API::testRes, buf); - return; - } - GlobalSolenoids::setSolenoid(solenoidNumber, solenoidState); -} - -/*! - * \brief Set all solenoids command handler. - * \param buffer Pointer to a data buffer. - * \param size Number of bytes of data in the buffer. - */ -void Tester::setAllCmd(const uint8_t *buffer, size_t size) { - GlobalCom::sendMsg(AYAB_API::testRes, "Called setAll\n"); - if (size < 3U) { - GlobalCom::sendMsg(AYAB_API::testRes, "Error: invalid arguments\n"); - return; - } - uint16_t solenoidState = (buffer[1] << 8) + buffer[2]; - GlobalSolenoids::setSolenoids(solenoidState); -} - -/*! - * \brief Read EOL sensors command handler. - */ -void Tester::readEOLsensorsCmd() { - GlobalCom::sendMsg(AYAB_API::testRes, "Called readEOLsensors\n"); - readEOLsensors(); - GlobalCom::sendMsg(AYAB_API::testRes, "\n"); -} - -/*! - * \brief Read encoders command handler. - */ -void Tester::readEncodersCmd() { - GlobalCom::sendMsg(AYAB_API::testRes, "Called readEncoders\n"); - readEncoders(); - GlobalCom::sendMsg(AYAB_API::testRes, "\n"); -} - -/*! - * \brief Auto read command handler. - */ -void Tester::autoReadCmd() { - GlobalCom::sendMsg(AYAB_API::testRes, "Called autoRead, send stop to quit\n"); - m_autoReadOn = true; -} - -/*! - * \brief Auto test command handler. - */ -void Tester::autoTestCmd() { - GlobalCom::sendMsg(AYAB_API::testRes, "Called autoTest, send stop to quit\n"); - m_autoTestOn = true; -} - -/*! - * \brief Stop command handler. - */ -void Tester::stopCmd() { - m_autoReadOn = false; - m_autoTestOn = false; -} - -/*! - * \brief Quit command handler. - */ -void Tester::quitCmd() { - GlobalFsm::setState(OpState::init); - GlobalKnitter::setUpInterrupt(); -} - -/*! - * \brief Start hardware test. - * \param machineType Machine type. - * \return Error code (0 = success, other values = error). - */ -Err_t Tester::startTest(Machine_t machineType) { - OpState_t currentState = GlobalFsm::getState(); - if (OpState::wait_for_machine == currentState || OpState::init == currentState || OpState::ready == currentState) { - GlobalFsm::setState(OpState::test); - GlobalKnitter::setMachineType(machineType); - setUp(); - return ErrorCode::success; - } - return ErrorCode::wrong_machine_state; -} - -/*! - * \brief Main loop for hardware tests. - */ -void Tester::loop() { - unsigned long now = millis(); - if (now - m_lastTime >= TEST_LOOP_DELAY) { - m_lastTime = now; - handleTimerEvent(); - } -} - -#ifndef AYAB_TESTS -/*! - * \brief Interrupt service routine for encoder A. - */ -void Tester::encoderAChange() { - beep(); -} -#endif // AYAB_TESTS - -// Private member functions - -/*! - * \brief Setup for hardware tests. - */ -void Tester::setUp() { - // Print welcome message - GlobalCom::sendMsg(AYAB_API::testRes, "AYAB Hardware Test, "); - snprintf(buf, BUFFER_LEN, "Firmware v%hhu", FW_VERSION_MAJ); - GlobalCom::sendMsg(AYAB_API::testRes, buf); - snprintf(buf, BUFFER_LEN, ".%hhu", FW_VERSION_MIN); - GlobalCom::sendMsg(AYAB_API::testRes, buf); - snprintf(buf, BUFFER_LEN, " API v%hhu\n\n", API_VERSION); - GlobalCom::sendMsg(AYAB_API::testRes, buf); - helpCmd(); - - // attach interrupt for ENC_PIN_A(=2), interrupt #0 - detachInterrupt(digitalPinToInterrupt(ENC_PIN_A)); -#ifndef AYAB_TESTS - // Attaching ENC_PIN_A, Interrupt #0 - // This interrupt cannot be enabled until - // the machine type has been validated. - attachInterrupt(digitalPinToInterrupt(ENC_PIN_A), GlobalTester::encoderAChange, RISING); -#endif // AYAB_TESTS - - m_autoReadOn = false; - m_autoTestOn = false; - m_lastTime = millis(); - m_timerEventOdd = false; -} - -/*! - * \brief Make a beep. - */ -void Tester::beep() const { - GlobalBeeper::ready(); -} - -/*! - * \brief Read the Hall sensors that determine which carriage is in use. - */ -void Tester::readEncoders() const { - GlobalCom::sendMsg(AYAB_API::testRes, " ENC_A: "); - bool state = digitalRead(ENC_PIN_A); - GlobalCom::sendMsg(AYAB_API::testRes, state ? "HIGH" : "LOW"); - GlobalCom::sendMsg(AYAB_API::testRes, " ENC_B: "); - state = digitalRead(ENC_PIN_B); - GlobalCom::sendMsg(AYAB_API::testRes, state ? "HIGH" : "LOW"); - GlobalCom::sendMsg(AYAB_API::testRes, " ENC_C: "); - state = digitalRead(ENC_PIN_C); - GlobalCom::sendMsg(AYAB_API::testRes, state ? "HIGH" : "LOW"); -} - -/*! - * \brief Read the End of Line sensors. - */ -void Tester::readEOLsensors() { - auto hallSensor = static_cast(analogRead(EOL_PIN_L)); - snprintf(buf, BUFFER_LEN, " EOL_L: %hu", hallSensor); - GlobalCom::sendMsg(AYAB_API::testRes, buf); - hallSensor = static_cast(analogRead(EOL_PIN_R)); - snprintf(buf, BUFFER_LEN, " EOL_R: %hu", hallSensor); - GlobalCom::sendMsg(AYAB_API::testRes, buf); -} - -/*! - * \brief Read both carriage sensors and End of Line sensors. - */ -void Tester::autoRead() { - GlobalCom::sendMsg(AYAB_API::testRes, "\n"); - readEOLsensors(); - readEncoders(); - GlobalCom::sendMsg(AYAB_API::testRes, "\n"); -} - -/*! - * \brief Set even-numbered solenoids. - */ -void Tester::autoTestEven() const { - GlobalCom::sendMsg(AYAB_API::testRes, "Set even solenoids\n"); - digitalWrite(LED_PIN_A, HIGH); - digitalWrite(LED_PIN_B, HIGH); - GlobalSolenoids::setSolenoids(0xAAAA); -} - -/*! - * \brief Set odd-numbered solenoids. - */ -void Tester::autoTestOdd() const { - GlobalCom::sendMsg(AYAB_API::testRes, "Set odd solenoids\n"); - digitalWrite(LED_PIN_A, LOW); - digitalWrite(LED_PIN_B, LOW); - GlobalSolenoids::setSolenoids(0x5555); -} - -/*! - * \brief Timer event every 500ms to handle auto functions. - */ -void Tester::handleTimerEvent() { - if (m_autoReadOn && m_timerEventOdd) { - autoRead(); - } - if (m_autoTestOn) { - if (m_timerEventOdd) { - autoTestOdd(); - } else { - autoTestEven(); - } - } - m_timerEventOdd = !m_timerEventOdd; -} diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 2fde0172e..25793aaed 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -33,6 +33,7 @@ set(COMMON_INCLUDES ) set(EXTERNAL_LIB_INCLUDES ${LIBRARY_DIRECTORY}/Adafruit_MCP23008 + #${LIBRARY_DIRECTORY}/avr-libc/include ) set(COMMON_SOURCES ${PROJECT_SOURCE_DIR}/test_boards.cpp @@ -53,15 +54,31 @@ set(COMMON_SOURCES ${SOURCE_DIRECTORY}/global_com.cpp ${PROJECT_SOURCE_DIR}/test_com.cpp - ${SOURCE_DIRECTORY}/tester.cpp - ${SOURCE_DIRECTORY}/global_tester.cpp - ${PROJECT_SOURCE_DIR}/test_tester.cpp + ${SOURCE_DIRECTORY}/opIdle.cpp + ${SOURCE_DIRECTORY}/global_OpIdle.cpp + ${PROJECT_SOURCE_DIR}/test_OpIdle.cpp - ${SOURCE_DIRECTORY}/global_knitter.cpp - ${PROJECT_SOURCE_DIR}/mocks/knitter_mock.cpp + ${SOURCE_DIRECTORY}/opInit.cpp + ${SOURCE_DIRECTORY}/global_OpInit.cpp + ${PROJECT_SOURCE_DIR}/test_OpInit.cpp - ${SOURCE_DIRECTORY}/global_fsm.cpp - ${PROJECT_SOURCE_DIR}/mocks/fsm_mock.cpp + ${SOURCE_DIRECTORY}/opReady.cpp + ${SOURCE_DIRECTORY}/global_OpReady.cpp + ${PROJECT_SOURCE_DIR}/test_OpReady.cpp + + ${SOURCE_DIRECTORY}/opTest.cpp + ${SOURCE_DIRECTORY}/global_OpTest.cpp + ${PROJECT_SOURCE_DIR}/test_OpTest.cpp + + ${SOURCE_DIRECTORY}/opError.cpp + ${SOURCE_DIRECTORY}/global_OpError.cpp + ${PROJECT_SOURCE_DIR}/test_OpError.cpp + + ${SOURCE_DIRECTORY}/global_OpKnit.cpp + ${PROJECT_SOURCE_DIR}/mocks/opKnit_mock.cpp + + ${SOURCE_DIRECTORY}/global_controller.cpp + ${PROJECT_SOURCE_DIR}/mocks/controller_mock.cpp ) set(COMMON_DEFINES ARDUINO=1819 @@ -122,7 +139,7 @@ endfunction() add_board(uno) -add_executable(${PROJECT_NAME}_knitter +add_executable(${PROJECT_NAME}_knit ${PROJECT_SOURCE_DIR}/test_all.cpp ${SOURCE_DIRECTORY}/global_beeper.cpp @@ -137,36 +154,48 @@ add_executable(${PROJECT_NAME}_knitter ${SOURCE_DIRECTORY}/global_solenoids.cpp ${PROJECT_SOURCE_DIR}/mocks/solenoids_mock.cpp - ${SOURCE_DIRECTORY}/global_tester.cpp - ${PROJECT_SOURCE_DIR}/mocks/tester_mock.cpp + ${SOURCE_DIRECTORY}/global_OpIdle.cpp + ${PROJECT_SOURCE_DIR}/mocks/opIdle_mock.cpp + + ${SOURCE_DIRECTORY}/global_OpInit.cpp + ${PROJECT_SOURCE_DIR}/mocks/opInit_mock.cpp + + ${SOURCE_DIRECTORY}/global_OpReady.cpp + ${PROJECT_SOURCE_DIR}/mocks/opReady_mock.cpp + + ${SOURCE_DIRECTORY}/global_OpTest.cpp + ${PROJECT_SOURCE_DIR}/mocks/opTest_mock.cpp + + ${SOURCE_DIRECTORY}/global_OpError.cpp + ${PROJECT_SOURCE_DIR}/mocks/opError_mock.cpp - ${SOURCE_DIRECTORY}/fsm.cpp - ${SOURCE_DIRECTORY}/global_fsm.cpp - ${PROJECT_SOURCE_DIR}/test_fsm.cpp + ${SOURCE_DIRECTORY}/controller.cpp + ${SOURCE_DIRECTORY}/global_controller.cpp + ${PROJECT_SOURCE_DIR}/test_controller.cpp - ${SOURCE_DIRECTORY}/knitter.cpp - ${SOURCE_DIRECTORY}/global_knitter.cpp - ${PROJECT_SOURCE_DIR}/test_knitter.cpp + ${SOURCE_DIRECTORY}/opKnit.cpp + ${SOURCE_DIRECTORY}/global_OpKnit.cpp + ${PROJECT_SOURCE_DIR}/test_OpKnit.cpp ) -target_include_directories(${PROJECT_NAME}_knitter +target_include_directories(${PROJECT_NAME}_knit PRIVATE ${COMMON_INCLUDES} ${EXTERNAL_LIB_INCLUDES} ) -target_compile_definitions(${PROJECT_NAME}_knitter +target_compile_definitions(${PROJECT_NAME}_knit PRIVATE ${COMMON_DEFINES} __AVR_ATmega168__ ) -target_compile_options(${PROJECT_NAME}_knitter PRIVATE +target_compile_options(${PROJECT_NAME}_knit PRIVATE ${COMMON_FLAGS} ) -target_link_libraries(${PROJECT_NAME}_knitter +target_link_libraries(${PROJECT_NAME}_knit ${COMMON_LINKER_FLAGS} ) -add_dependencies(${PROJECT_NAME}_knitter arduino_mock) +add_dependencies(${PROJECT_NAME}_knit arduino_mock) enable_testing() include(GoogleTest) gtest_discover_tests(${PROJECT_NAME}_uno TEST_PREFIX uno_ XML_OUTPUT_DIR ./xml_out) -gtest_discover_tests(${PROJECT_NAME}_knitter TEST_PREFIX knitter_ XML_OUTPUT_DIR ./xml_out) +gtest_discover_tests(${PROJECT_NAME}_knit TEST_PREFIX knit_ XML_OUTPUT_DIR ./xml_out) diff --git a/test/mocks/Arduino.h b/test/mocks/Arduino.h index 95c40fbe9..164dbc84d 100644 --- a/test/mocks/Arduino.h +++ b/test/mocks/Arduino.h @@ -17,7 +17,7 @@ * along with AYAB. If not, see . * * Original Work Copyright 2013 Christian Obersteiner, Andreas Müller - * Modified Work Copyright 2020 Sturla Lange, Tom Price + * Modified Work Copyright 2020-3 Sturla Lange, Tom Price * http://ayab-knitting.com */ diff --git a/test/mocks/avr/common.h b/test/mocks/avr/common.h new file mode 100644 index 000000000..358b32892 --- /dev/null +++ b/test/mocks/avr/common.h @@ -0,0 +1,335 @@ +/* Copyright (c) 2007 Eric B. Weddington + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of the copyright holders nor the names of + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. */ + +/* $Id$ */ + + +#ifndef _AVR_COMMON_H +#define _AVR_COMMON_H + +#include + +/* +This purpose of this header is to define registers that have not been +previously defined in the individual device IO header files, and to define +other symbols that are common across AVR device families. + +This file is designed to be included in after the individual +device IO header files, and after + +*/ + +/*------------ Registers Not Previously Defined ------------*/ + +/* +These are registers that are not previously defined in the individual +IO header files, OR they are defined here because they are used in parts of +avr-libc even if a device is not selected but a general architecture has +been selected. +*/ + + +/* +Stack pointer register. + +AVR architecture 1 has no RAM, thus no stack pointer. + +All other architectures do have a stack pointer. Some devices have only +less than 256 bytes of possible RAM locations (128 Bytes of SRAM +and no option for external RAM), thus SPH is officially "reserved" +for them. +*/ +#if __AVR_ARCH__ >= 100 +# ifndef SPL +# define SPL _SFR_MEM8(0x3D) +# endif +# ifndef SPH +# define SPH _SFR_MEM8(0x3E) +# endif +# ifndef SP +# define SP _SFR_MEM16(0x3D) +# endif +#elif __AVR_ARCH__ != 1 +# ifndef SPL +# define SPL _SFR_IO8(0x3D) +# endif +# if XRAMEND < 0x100 && !defined(__COMPILING_AVR_LIBC__) +# ifndef SP +# define SP _SFR_IO8(0x3D) +# endif +# else +# ifndef SP +# define SP _SFR_IO16(0x3D) +# endif +# ifndef SPH +# define SPH _SFR_IO8(0x3E) +# endif +# endif /* XRAMEND < 0x100 && !defined(__COMPILING_AVR_LIBC__) */ +#endif /* __AVR_ARCH__ != 1 */ + + +/* Status Register */ +#ifndef SREG +# if __AVR_ARCH__ >= 100 +# define SREG _SFR_MEM8(0x3F) +# else +# define SREG _SFR_IO8(0x3F) +# endif +#endif + + +/* SREG bit definitions */ +#ifndef SREG_C +# define SREG_C (0) +#endif +#ifndef SREG_Z +# define SREG_Z (1) +#endif +#ifndef SREG_N +# define SREG_N (2) +#endif +#ifndef SREG_V +# define SREG_V (3) +#endif +#ifndef SREG_S +# define SREG_S (4) +#endif +#ifndef SREG_H +# define SREG_H (5) +#endif +#ifndef SREG_T +# define SREG_T (6) +#endif +#ifndef SREG_I +# define SREG_I (7) +#endif + + +#if defined(__COMPILING_AVR_LIBC__) + +/* AVR 6 Architecture */ +# if __AVR_ARCH__ == 6 +# ifndef EIND +# define EIND _SFR_IO8(0X3C) +# endif +/* XMEGA Architectures */ +# elif __AVR_ARCH__ >= 100 +# ifndef EIND +# define EIND _SFR_MEM8(0x3C) +# endif +# endif + +/* +Only few devices come without EEPROM. In order to assemble the +EEPROM library components without defining a specific device, we +keep the EEPROM-related definitions here. +*/ + +/* EEPROM Control Register */ +# ifndef EECR +# define EECR _SFR_IO8(0x1C) +# endif + +/* EEPROM Data Register */ +# ifndef EEDR +# define EEDR _SFR_IO8(0x1D) +# endif + +/* EEPROM Address Register */ +# ifndef EEAR +# define EEAR _SFR_IO16(0x1E) +# endif +# ifndef EEARL +# define EEARL _SFR_IO8(0x1E) +# endif +# ifndef EEARH +# define EEARH _SFR_IO8(0x1F) +# endif + +/* EEPROM Control Register bits */ +# ifndef EERE +# define EERE (0) +# endif +# ifndef EEWE +# define EEWE (1) +# endif +# ifndef EEMWE +# define EEMWE (2) +# endif +# ifndef EERIE +# define EERIE (3) +# endif + + +/* RAM Page Z Select Register */ +#ifndef RAMPZ +# if defined(__AVR_HAVE_RAMPZ__) && __AVR_HAVE_RAMPZ__ +# if __AVR_ARCH__ >= 100 +# define RAMPZ _SFR_MEM8(0x3B) +# else +# define RAMPZ _SFR_IO8(0x3B) +# endif +# endif +#endif + +#endif /* __COMPILING_AVR_LIBC__ */ + + + +/*------------ Common Symbols ------------*/ + +/* +Generic definitions for registers that are common across multiple AVR devices +and families. +*/ + +/* Pointer registers definitions */ +#if __AVR_ARCH__ != 1 /* avr1 does not have X and Y pointers */ +# define XL r26 +# define XH r27 +# define YL r28 +# define YH r29 +#endif /* #if __AVR_ARCH__ != 1 */ +#define ZL r30 +#define ZH r31 + + +/* Status Register */ +#if defined(SREG) +# define AVR_STATUS_REG SREG +# if __AVR_ARCH__ >= 100 +# define AVR_STATUS_ADDR _SFR_MEM_ADDR(SREG) +# else +# define AVR_STATUS_ADDR _SFR_IO_ADDR(SREG) +# endif +#endif + +/* Stack Pointer (combined) Register */ +#if defined(SP) +# define AVR_STACK_POINTER_REG SP +# if __AVR_ARCH__ >= 100 +# define AVR_STACK_POINTER_ADDR _SFR_MEM_ADDR(SP) +# else +# define AVR_STACK_POINTER_ADDR _SFR_IO_ADDR(SP) +# endif +#endif + +/* Stack Pointer High Register */ +#if defined(SPH) +# define _HAVE_AVR_STACK_POINTER_HI 1 +# define AVR_STACK_POINTER_HI_REG SPH +# if __AVR_ARCH__ >= 100 +# define AVR_STACK_POINTER_HI_ADDR _SFR_MEM_ADDR(SPH) +# else +# define AVR_STACK_POINTER_HI_ADDR _SFR_IO_ADDR(SPH) +# endif +#endif + +/* Stack Pointer Low Register */ +#if defined(SPL) +# define AVR_STACK_POINTER_LO_REG SPL +# if __AVR_ARCH__ >= 100 +# define AVR_STACK_POINTER_LO_ADDR _SFR_MEM_ADDR(SPL) +# else +# define AVR_STACK_POINTER_LO_ADDR _SFR_IO_ADDR(SPL) +# endif +#endif + +/* RAMPD Register */ +#if defined(RAMPD) +# define AVR_RAMPD_REG RAMPD +# if __AVR_ARCH__ >= 100 +# define AVR_RAMPD_ADDR _SFR_MEM_ADDR(RAMPD) +# else +# define AVR_RAMPD_ADDR _SFR_IO_ADDR(RAMPD) +# endif +#endif + +/* RAMPX Register */ +#if defined(RAMPX) +# define AVR_RAMPX_REG RAMPX +# if __AVR_ARCH__ >= 100 +# define AVR_RAMPX_ADDR _SFR_MEM_ADDR(RAMPX) +# else +# define AVR_RAMPX_ADDR _SFR_IO_ADDR(RAMPX) +# endif +#endif + +/* RAMPY Register */ +#if defined(RAMPY) +# define AVR_RAMPY_REG RAMPY +# if __AVR_ARCH__ >= 100 +# define AVR_RAMPY_ADDR _SFR_MEM_ADDR(RAMPY) +# else +# define AVR_RAMPY_ADDR _SFR_IO_ADDR(RAMPY) +# endif +#endif + +/* RAMPZ Register */ +#if defined(RAMPZ) +# define AVR_RAMPZ_REG RAMPZ +# if __AVR_ARCH__ >= 100 +# define AVR_RAMPZ_ADDR _SFR_MEM_ADDR(RAMPZ) +# else +# define AVR_RAMPZ_ADDR _SFR_IO_ADDR(RAMPZ) +# endif +#endif + +/* Extended Indirect Register */ +#if defined(EIND) +# define AVR_EXTENDED_INDIRECT_REG EIND +# if __AVR_ARCH__ >= 100 +# define AVR_EXTENDED_INDIRECT_ADDR _SFR_MEM_ADDR(EIND) +# else +# define AVR_EXTENDED_INDIRECT_ADDR _SFR_IO_ADDR(EIND) +# endif +#endif + +/*------------ Workaround to old compilers (4.1.2 and earlier) ------------*/ + +#ifndef __AVR_HAVE_MOVW__ +# if defined(__AVR_ENHANCED__) && __AVR_ENHANCED__ +# define __AVR_HAVE_MOVW__ 1 +# endif +#endif + +#ifndef __AVR_HAVE_LPMX__ +# if defined(__AVR_ENHANCED__) && __AVR_ENHANCED__ +# define __AVR_HAVE_LPMX__ 1 +# endif +#endif + +#ifndef __AVR_HAVE_MUL__ +# if defined(__AVR_ENHANCED__) && __AVR_ENHANCED__ +# define __AVR_HAVE_MUL__ 1 +# endif +#endif + +#endif /* _AVR_COMMON_H */ diff --git a/test/mocks/avr/fuse.h b/test/mocks/avr/fuse.h new file mode 100644 index 000000000..22888a03e --- /dev/null +++ b/test/mocks/avr/fuse.h @@ -0,0 +1,274 @@ +/* Copyright (c) 2007, Atmel Corporation + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of the copyright holders nor the names of + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. */ + +/* $Id$ */ + +/* avr/fuse.h - Fuse API */ + +#ifndef _AVR_FUSE_H_ +#define _AVR_FUSE_H_ 1 + +/* This file must be explicitly included by . */ +#if !defined(_AVR_IO_H_) +#error "You must #include and not by itself." +#endif + + +/** \file */ +/** \defgroup avr_fuse : Fuse Support + + \par Introduction + + The Fuse API allows a user to specify the fuse settings for the specific + AVR device they are compiling for. These fuse settings will be placed + in a special section in the ELF output file, after linking. + + Programming tools can take advantage of the fuse information embedded in + the ELF file, by extracting this information and determining if the fuses + need to be programmed before programming the Flash and EEPROM memories. + This also allows a single ELF file to contain all the + information needed to program an AVR. + + To use the Fuse API, include the header file, which in turn + automatically includes the individual I/O header file and the + file. These other two files provides everything necessary to set the AVR + fuses. + + \par Fuse API + + Each I/O header file must define the FUSE_MEMORY_SIZE macro which is + defined to the number of fuse bytes that exist in the AVR device. + + A new type, __fuse_t, is defined as a structure. The number of fields in + this structure are determined by the number of fuse bytes in the + FUSE_MEMORY_SIZE macro. + + If FUSE_MEMORY_SIZE == 1, there is only a single field: byte, of type + unsigned char. + + If FUSE_MEMORY_SIZE == 2, there are two fields: low, and high, of type + unsigned char. + + If FUSE_MEMORY_SIZE == 3, there are three fields: low, high, and extended, + of type unsigned char. + + If FUSE_MEMORY_SIZE > 3, there is a single field: byte, which is an array + of unsigned char with the size of the array being FUSE_MEMORY_SIZE. + + A convenience macro, FUSEMEM, is defined as a GCC attribute for a + custom-named section of ".fuse". + + A convenience macro, FUSES, is defined that declares a variable, __fuse, of + type __fuse_t with the attribute defined by FUSEMEM. This variable + allows the end user to easily set the fuse data. + + \note If a device-specific I/O header file has previously defined FUSEMEM, + then FUSEMEM is not redefined. If a device-specific I/O header file has + previously defined FUSES, then FUSES is not redefined. + + Each AVR device I/O header file has a set of defined macros which specify the + actual fuse bits available on that device. The AVR fuses have inverted + values, logical 1 for an unprogrammed (disabled) bit and logical 0 for a + programmed (enabled) bit. The defined macros for each individual fuse + bit represent this in their definition by a bit-wise inversion of a mask. + For example, the FUSE_EESAVE fuse in the ATmega128 is defined as: + \code + #define FUSE_EESAVE ~_BV(3) + \endcode + \note The _BV macro creates a bit mask from a bit number. It is then + inverted to represent logical values for a fuse memory byte. + + To combine the fuse bits macros together to represent a whole fuse byte, + use the bitwise AND operator, like so: + \code + (FUSE_BOOTSZ0 & FUSE_BOOTSZ1 & FUSE_EESAVE & FUSE_SPIEN & FUSE_JTAGEN) + \endcode + + Each device I/O header file also defines macros that provide default values + for each fuse byte that is available. LFUSE_DEFAULT is defined for a Low + Fuse byte. HFUSE_DEFAULT is defined for a High Fuse byte. EFUSE_DEFAULT + is defined for an Extended Fuse byte. + + If FUSE_MEMORY_SIZE > 3, then the I/O header file defines macros that + provide default values for each fuse byte like so: + FUSE0_DEFAULT + FUSE1_DEFAULT + FUSE2_DEFAULT + FUSE3_DEFAULT + FUSE4_DEFAULT + .... + + \par API Usage Example + + Putting all of this together is easy. Using C99's designated initializers: + + \code + #include + + FUSES = + { + .low = LFUSE_DEFAULT, + .high = (FUSE_BOOTSZ0 & FUSE_BOOTSZ1 & FUSE_EESAVE & FUSE_SPIEN & FUSE_JTAGEN), + .extended = EFUSE_DEFAULT, + }; + + int main(void) + { + return 0; + } + \endcode + + Or, using the variable directly instead of the FUSES macro, + + \code + #include + + __fuse_t __fuse __attribute__((section (".fuse"))) = + { + .low = LFUSE_DEFAULT, + .high = (FUSE_BOOTSZ0 & FUSE_BOOTSZ1 & FUSE_EESAVE & FUSE_SPIEN & FUSE_JTAGEN), + .extended = EFUSE_DEFAULT, + }; + + int main(void) + { + return 0; + } + \endcode + + If you are compiling in C++, you cannot use the designated intializers so + you must do: + + \code + #include + + FUSES = + { + LFUSE_DEFAULT, // .low + (FUSE_BOOTSZ0 & FUSE_BOOTSZ1 & FUSE_EESAVE & FUSE_SPIEN & FUSE_JTAGEN), // .high + EFUSE_DEFAULT, // .extended + }; + + int main(void) + { + return 0; + } + \endcode + + + However there are a number of caveats that you need to be aware of to + use this API properly. + + Be sure to include to get all of the definitions for the API. + The FUSES macro defines a global variable to store the fuse data. This + variable is assigned to its own linker section. Assign the desired fuse + values immediately in the variable initialization. + + The .fuse section in the ELF file will get its values from the initial + variable assignment ONLY. This means that you can NOT assign values to + this variable in functions and the new values will not be put into the + ELF .fuse section. + + The global variable is declared in the FUSES macro has two leading + underscores, which means that it is reserved for the "implementation", + meaning the library, so it will not conflict with a user-named variable. + + You must initialize ALL fields in the __fuse_t structure. This is because + the fuse bits in all bytes default to a logical 1, meaning unprogrammed. + Normal uninitialized data defaults to all locgial zeros. So it is vital that + all fuse bytes are initialized, even with default data. If they are not, + then the fuse bits may not programmed to the desired settings. + + Be sure to have the -mmcu=device flag in your compile command line and + your linker command line to have the correct device selected and to have + the correct I/O header file included when you include . + + You can print out the contents of the .fuse section in the ELF file by + using this command line: + \code + avr-objdump -s -j .fuse + \endcode + The section contents shows the address on the left, then the data going from + lower address to a higher address, left to right. + +*/ + +#if !(defined(__ASSEMBLER__) || defined(__DOXYGEN__)) + +#ifndef FUSEMEM +#define FUSEMEM __attribute__((__used__, __section__ (".fuse"))) +#endif + +#if FUSE_MEMORY_SIZE > 3 + +typedef struct +{ + unsigned char byte[FUSE_MEMORY_SIZE]; +} __fuse_t; + + +#elif FUSE_MEMORY_SIZE == 3 + +typedef struct +{ + unsigned char low; + unsigned char high; + unsigned char extended; +} __fuse_t; + +#elif FUSE_MEMORY_SIZE == 2 + +typedef struct +{ + unsigned char low; + unsigned char high; +} __fuse_t; + +#elif FUSE_MEMORY_SIZE == 1 + +typedef struct +{ + unsigned char byte; +} __fuse_t; + +#endif + +#if !defined(FUSES) + #if defined(__AVR_XMEGA__) + #define FUSES NVM_FUSES_t __fuse FUSEMEM + #else + #define FUSES __fuse_t __fuse FUSEMEM + #endif +#endif + + +#endif /* !(__ASSEMBLER__ || __DOXYGEN__) */ + +#endif /* _AVR_FUSE_H_ */ diff --git a/test/mocks/avr/interrupt.h b/test/mocks/avr/interrupt.h new file mode 100644 index 000000000..48e898438 --- /dev/null +++ b/test/mocks/avr/interrupt.h @@ -0,0 +1,378 @@ +/* Copyright (c) 2002,2005,2007 Marek Michalkiewicz + Copyright (c) 2007, Dean Camera + + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of the copyright holders nor the names of + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. */ + +/* $Id$ */ + +#ifndef _AVR_INTERRUPT_H_ +#define _AVR_INTERRUPT_H_ + +#include + +#if !defined(__DOXYGEN__) && !defined(__STRINGIFY) +/* Auxiliary macro for ISR_ALIAS(). */ +#define __STRINGIFY(x) #x +#endif /* !defined(__DOXYGEN__) */ + +/** +\file +\@{ +*/ + + +/** \name Global manipulation of the interrupt flag + + The global interrupt flag is maintained in the I bit of the status + register (SREG). + + Handling interrupts frequently requires attention regarding atomic + access to objects that could be altered by code running within an + interrupt context, see . + + Frequently, interrupts are being disabled for periods of time in + order to perform certain operations without being disturbed; see + \ref optim_code_reorder for things to be taken into account with + respect to compiler optimizations. +*/ + +#if defined(__DOXYGEN__) +/** \def sei() + \ingroup avr_interrupts + + Enables interrupts by setting the global interrupt mask. This function + actually compiles into a single line of assembly, so there is no function + call overhead. However, the macro also implies a memory barrier + which can cause additional loss of optimization. + + In order to implement atomic access to multi-byte objects, + consider using the macros from , rather than + implementing them manually with cli() and sei(). +*/ +#define sei() +#else /* !DOXYGEN */ +# define sei() __asm__ __volatile__ ("sei" ::: "memory") +#endif /* DOXYGEN */ + +#if defined(__DOXYGEN__) +/** \def cli() + \ingroup avr_interrupts + + Disables all interrupts by clearing the global interrupt mask. This function + actually compiles into a single line of assembly, so there is no function + call overhead. However, the macro also implies a memory barrier + which can cause additional loss of optimization. + + In order to implement atomic access to multi-byte objects, + consider using the macros from , rather than + implementing them manually with cli() and sei(). +*/ +#define cli() +#else /* !DOXYGEN */ +# define cli() __asm__ __volatile__ ("cli" ::: "memory") +#endif /* DOXYGEN */ + + +/** \name Macros for writing interrupt handler functions */ + + +#if defined(__DOXYGEN__) +/** \def ISR(vector [, attributes]) + \ingroup avr_interrupts + + Introduces an interrupt handler function (interrupt service + routine) that runs with global interrupts initially disabled + by default with no attributes specified. + + The attributes are optional and alter the behaviour and resultant + generated code of the interrupt routine. Multiple attributes may + be used for a single function, with a space seperating each + attribute. + + Valid attributes are ISR_BLOCK, ISR_NOBLOCK, ISR_NAKED and + ISR_ALIASOF(vect). + + \c vector must be one of the interrupt vector names that are + valid for the particular MCU type. +*/ +# define ISR(vector, [attributes]) +#else /* real code */ + +#if (__GNUC__ == 4 && __GNUC_MINOR__ >= 1) || (__GNUC__ > 4) +# define __INTR_ATTRS __used__, __externally_visible__ +#else /* GCC < 4.1 */ +# define __INTR_ATTRS __used__ +#endif + +#ifdef __cplusplus +# define ISR(vector, ...) \ + extern "C" void vector (void) __attribute__ ((__signal__,__INTR_ATTRS)) __VA_ARGS__; \ + void vector (void) +#else +# define ISR(vector, ...) \ + void vector (void) __attribute__ ((__signal__,__INTR_ATTRS)) __VA_ARGS__; \ + void vector (void) +#endif + +#endif /* DOXYGEN */ + +#if defined(__DOXYGEN__) +/** \def SIGNAL(vector) + \ingroup avr_interrupts + + Introduces an interrupt handler function that runs with global interrupts + initially disabled. + + This is the same as the ISR macro without optional attributes. + \deprecated Do not use SIGNAL() in new code. Use ISR() instead. +*/ +# define SIGNAL(vector) +#else /* real code */ + +#ifdef __cplusplus +# define SIGNAL(vector) \ + extern "C" void vector(void) __attribute__ ((__signal__, __INTR_ATTRS)); \ + void vector (void) +#else +# define SIGNAL(vector) \ + void vector (void) __attribute__ ((__signal__, __INTR_ATTRS)); \ + void vector (void) +#endif + +#endif /* DOXYGEN */ + +#if defined(__DOXYGEN__) +/** \def EMPTY_INTERRUPT(vector) + \ingroup avr_interrupts + + Defines an empty interrupt handler function. This will not generate + any prolog or epilog code and will only return from the ISR. Do not + define a function body as this will define it for you. + Example: + \code EMPTY_INTERRUPT(ADC_vect);\endcode */ +# define EMPTY_INTERRUPT(vector) +#else /* real code */ + +#ifdef __cplusplus +# define EMPTY_INTERRUPT(vector) \ + extern "C" void vector(void) __attribute__ ((__signal__,__naked__,__INTR_ATTRS)); \ + void vector (void) { __asm__ __volatile__ ("reti" ::: "memory"); } +#else +# define EMPTY_INTERRUPT(vector) \ + void vector (void) __attribute__ ((__signal__,__naked__,__INTR_ATTRS)); \ + void vector (void) { __asm__ __volatile__ ("reti" ::: "memory"); } +#endif + +#endif /* DOXYGEN */ + +#if defined(__DOXYGEN__) +/** \def ISR_ALIAS(vector, target_vector) + \ingroup avr_interrupts + + Aliases a given vector to another one in the same manner as the + ISR_ALIASOF attribute for the ISR() macro. Unlike the ISR_ALIASOF + attribute macro however, this is compatible for all versions of + GCC rather than just GCC version 4.2 onwards. + + \note This macro creates a trampoline function for the aliased + macro. This will result in a two cycle penalty for the aliased + vector compared to the ISR the vector is aliased to, due to the + JMP/RJMP opcode used. + + \deprecated + For new code, the use of ISR(..., ISR_ALIASOF(...)) is + recommended. + + Example: + \code + ISR(INT0_vect) + { + PORTB = 42; + } + + ISR_ALIAS(INT1_vect, INT0_vect); + \endcode + +*/ +# define ISR_ALIAS(vector, target_vector) +#else /* real code */ + +#ifdef __cplusplus +# define ISR_ALIAS(vector, tgt) extern "C" void vector (void) \ + __attribute__((__signal__, __naked__, __INTR_ATTRS)); \ + void vector (void) { __asm__ __volatile__ ("%~jmp " __STRINGIFY(tgt) ::); } +#else /* !__cplusplus */ +# define ISR_ALIAS(vector, tgt) void vector (void) \ + __attribute__((__signal__, __naked__, __INTR_ATTRS)); \ + void vector (void) { __asm__ __volatile__ ("%~jmp " __STRINGIFY(tgt) ::); } +#endif /* __cplusplus */ + +#endif /* DOXYGEN */ + +#if defined(__DOXYGEN__) +/** \def reti() + \ingroup avr_interrupts + + Returns from an interrupt routine, enabling global interrupts. This should + be the last command executed before leaving an ISR defined with the ISR_NAKED + attribute. + + This macro actually compiles into a single line of assembly, so there is + no function call overhead. +*/ +# define reti() +#else /* !DOXYGEN */ +# define reti() __asm__ __volatile__ ("reti" ::: "memory") +#endif /* DOXYGEN */ + +#if defined(__DOXYGEN__) +/** \def BADISR_vect + \ingroup avr_interrupts + + \code #include \endcode + + This is a vector which is aliased to __vector_default, the vector + executed when an ISR fires with no accompanying ISR handler. This + may be used along with the ISR() macro to create a catch-all for + undefined but used ISRs for debugging purposes. +*/ +# define BADISR_vect +#else /* !DOXYGEN */ +# define BADISR_vect __vector_default +#endif /* DOXYGEN */ + +/** \name ISR attributes */ + +#if defined(__DOXYGEN__) +/** \def ISR_BLOCK + \ingroup avr_interrupts + + Identical to an ISR with no attributes specified. Global + interrupts are initially disabled by the AVR hardware when + entering the ISR, without the compiler modifying this state. + + Use this attribute in the attributes parameter of the ISR macro. +*/ +# define ISR_BLOCK + +/** \def ISR_NOBLOCK + \ingroup avr_interrupts + + ISR runs with global interrupts initially enabled. The interrupt + enable flag is activated by the compiler as early as possible + within the ISR to ensure minimal processing delay for nested + interrupts. + + This may be used to create nested ISRs, however care should be + taken to avoid stack overflows, or to avoid infinitely entering + the ISR for those cases where the AVR hardware does not clear the + respective interrupt flag before entering the ISR. + + Use this attribute in the attributes parameter of the ISR macro. +*/ +# define ISR_NOBLOCK + +/** \def ISR_NAKED + \ingroup avr_interrupts + + ISR is created with no prologue or epilogue code. The user code is + responsible for preservation of the machine state including the + SREG register, as well as placing a reti() at the end of the + interrupt routine. + + Use this attribute in the attributes parameter of the ISR macro. +*/ +# define ISR_NAKED + +/** \def ISR_FLATTEN + \ingroup avr_interrupts + + The compiler will try to inline all called function into the ISR. + This has an effect with GCC 4.6 and newer only. + + Use this attribute in the attributes parameter of the ISR macro. +*/ +# define ISR_FLATTEN + +/** \def ISR_NOICF + \ingroup avr_interrupts + + Avoid identical-code-folding optimization against this ISR. + This has an effect with GCC 5 and newer only. + + Use this attribute in the attributes parameter of the ISR macro. +*/ +# define ISR_NOICF + +/** \def ISR_ALIASOF(target_vector) + \ingroup avr_interrupts + + The ISR is linked to another ISR, specified by the vect parameter. + This is compatible with GCC 4.2 and greater only. + + Use this attribute in the attributes parameter of the ISR macro. + Example: + \code + ISR (INT0_vect) + { + PORTB = 42; + } + + ISR (INT1_vect, ISR_ALIASOF (INT0_vect)); + \endcode +*/ +# define ISR_ALIASOF(target_vector) +#else /* !DOXYGEN */ +# define ISR_BLOCK /* empty */ +/* FIXME: This won't work with older versions of avr-gcc as ISR_NOBLOCK + will use `signal' and `interrupt' at the same time. */ +# define ISR_NOBLOCK __attribute__((__interrupt__)) +# define ISR_NAKED __attribute__((__naked__)) + +#if (__GNUC__ == 4 && __GNUC_MINOR__ >= 6) || (__GNUC__ >= 5) +# define ISR_FLATTEN __attribute__((__flatten__)) +#else +# define ISR_FLATTEN /* empty */ +#endif /* has flatten (GCC 4.6+) */ + +#if defined (__has_attribute) +#if __has_attribute (__no_icf__) +# define ISR_NOICF __attribute__((__no_icf__)) +#else +# define ISR_NOICF /* empty */ +#endif /* has no_icf */ +#endif /* has __has_attribute (GCC 5+) */ + +# define ISR_ALIASOF(v) __attribute__((__alias__(__STRINGIFY(v)))) +#endif /* DOXYGEN */ + +/* \@} */ + +#endif diff --git a/test/mocks/avr/io.h b/test/mocks/avr/io.h new file mode 100644 index 000000000..2d36d1687 --- /dev/null +++ b/test/mocks/avr/io.h @@ -0,0 +1,761 @@ +/* Copyright (c) 2002,2003,2005,2006,2007 Marek Michalkiewicz, Joerg Wunsch + Copyright (c) 2007 Eric B. Weddington + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of the copyright holders nor the names of + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. */ + +/* $Id$ */ + +/** \file */ +/** \defgroup avr_io : AVR device-specific IO definitions + \code #include \endcode + + This header file includes the apropriate IO definitions for the + device that has been specified by the -mmcu= compiler + command-line switch. This is done by diverting to the appropriate + file <avr/ioXXXX.h> which should + never be included directly. Some register names common to all + AVR devices are defined directly within <avr/common.h>, + which is included in <avr/io.h>, + but most of the details come from the respective include file. + + Note that this file always includes the following files: + \code + #include + #include + #include + #include + \endcode + See \ref avr_sfr for more details about that header file. + + Included are definitions of the IO register set and their + respective bit values as specified in the Atmel documentation. + Note that inconsistencies in naming conventions, + so even identical functions sometimes get different names on + different devices. + + Also included are the specific names useable for interrupt + function definitions as documented + \ref avr_signames "here". + + Finally, the following macros are defined: + + - \b RAMEND +
+ The last on-chip RAM address. +
+ - \b XRAMEND +
+ The last possible RAM location that is addressable. This is equal to + RAMEND for devices that do not allow for external RAM. For devices + that allow external RAM, this will be larger than RAMEND. +
+ - \b E2END +
+ The last EEPROM address. +
+ - \b FLASHEND +
+ The last byte address in the Flash program space. +
+ - \b SPM_PAGESIZE +
+ For devices with bootloader support, the flash pagesize + (in bytes) to be used for the \c SPM instruction. + - \b E2PAGESIZE +
+ The size of the EEPROM page. + +*/ + +#ifndef _AVR_IO_H_ +#define _AVR_IO_H_ + +#include + +#if defined (__AVR_AT94K__) +# include +#elif defined (__AVR_AT43USB320__) +# include +#elif defined (__AVR_AT43USB355__) +# include +#elif defined (__AVR_AT76C711__) +# include +#elif defined (__AVR_AT86RF401__) +# include +#elif defined (__AVR_AT90PWM1__) +# include +#elif defined (__AVR_AT90PWM2__) +# include +#elif defined (__AVR_AT90PWM2B__) +# include +#elif defined (__AVR_AT90PWM3__) +# include +#elif defined (__AVR_AT90PWM3B__) +# include +#elif defined (__AVR_AT90PWM216__) +# include +#elif defined (__AVR_AT90PWM316__) +# include +#elif defined (__AVR_AT90PWM161__) +# include +#elif defined (__AVR_AT90PWM81__) +# include +#elif defined (__AVR_ATmega8U2__) +# include +#elif defined (__AVR_ATmega16M1__) +# include +#elif defined (__AVR_ATmega16U2__) +# include +#elif defined (__AVR_ATmega16U4__) +# include +#elif defined (__AVR_ATmega32C1__) +# include +#elif defined (__AVR_ATmega32M1__) +# include +#elif defined (__AVR_ATmega32U2__) +# include +#elif defined (__AVR_ATmega32U4__) +# include +#elif defined (__AVR_ATmega32U6__) +# include +#elif defined (__AVR_ATmega64C1__) +# include +#elif defined (__AVR_ATmega64M1__) +# include +#elif defined (__AVR_ATmega128__) +# include +#elif defined (__AVR_ATmega128A__) +# include +#elif defined (__AVR_ATmega1280__) +# include +#elif defined (__AVR_ATmega1281__) +# include +#elif defined (__AVR_ATmega1284__) +# include +#elif defined (__AVR_ATmega1284P__) +# include +#elif defined (__AVR_ATmega128RFA1__) +# include +#elif defined (__AVR_ATmega1284RFR2__) +# include +#elif defined (__AVR_ATmega128RFR2__) +# include +#elif defined (__AVR_ATmega2564RFR2__) +# include +#elif defined (__AVR_ATmega256RFR2__) +# include +#elif defined (__AVR_ATmega2560__) +# include +#elif defined (__AVR_ATmega2561__) +# include +#elif defined (__AVR_AT90CAN32__) +# include +#elif defined (__AVR_AT90CAN64__) +# include +#elif defined (__AVR_AT90CAN128__) +# include +#elif defined (__AVR_AT90USB82__) +# include +#elif defined (__AVR_AT90USB162__) +# include +#elif defined (__AVR_AT90USB646__) +# include +#elif defined (__AVR_AT90USB647__) +# include +#elif defined (__AVR_AT90USB1286__) +# include +#elif defined (__AVR_AT90USB1287__) +# include +#elif defined (__AVR_ATmega644RFR2__) +# include +#elif defined (__AVR_ATmega64RFR2__) +# include +#elif defined (__AVR_ATmega64__) +# include +#elif defined (__AVR_ATmega64A__) +# include +#elif defined (__AVR_ATmega640__) +# include +#elif defined (__AVR_ATmega644__) +# include +#elif defined (__AVR_ATmega644A__) +# include +#elif defined (__AVR_ATmega644P__) +# include +#elif defined (__AVR_ATmega644PA__) +# include +#elif defined (__AVR_ATmega645__) || defined (__AVR_ATmega645A__) || defined (__AVR_ATmega645P__) +# include +#elif defined (__AVR_ATmega6450__) || defined (__AVR_ATmega6450A__) || defined (__AVR_ATmega6450P__) +# include +#elif defined (__AVR_ATmega649__) || defined (__AVR_ATmega649A__) +# include +#elif defined (__AVR_ATmega6490__) || defined (__AVR_ATmega6490A__) || defined (__AVR_ATmega6490P__) +# include +#elif defined (__AVR_ATmega649P__) +# include +#elif defined (__AVR_ATmega64HVE__) +# include +#elif defined (__AVR_ATmega64HVE2__) +# include +#elif defined (__AVR_ATmega103__) +# include +#elif defined (__AVR_ATmega32__) +# include +#elif defined (__AVR_ATmega32A__) +# include +#elif defined (__AVR_ATmega323__) +# include +#elif defined (__AVR_ATmega324P__) || defined (__AVR_ATmega324A__) +# include +#elif defined (__AVR_ATmega324PA__) +# include +#elif defined (__AVR_ATmega325__) || defined (__AVR_ATmega325A__) +# include +#elif defined (__AVR_ATmega325P__) +# include +#elif defined (__AVR_ATmega325PA__) +# include +#elif defined (__AVR_ATmega3250__) || defined (__AVR_ATmega3250A__) +# include +#elif defined (__AVR_ATmega3250P__) +# include +#elif defined (__AVR_ATmega3250PA__) +# include +#elif defined (__AVR_ATmega328P__) || defined (__AVR_ATmega328__) +# include +#elif defined (__AVR_ATmega329__) || defined (__AVR_ATmega329A__) +# include +#elif defined (__AVR_ATmega329P__) || defined (__AVR_ATmega329PA__) +# include +#elif defined (__AVR_ATmega3290__) || defined (__AVR_ATmega3290A__) +# include +#elif defined (__AVR_ATmega3290P__) +# include +#elif defined (__AVR_ATmega3290PA__) +# include +#elif defined (__AVR_ATmega32HVB__) +# include +#elif defined (__AVR_ATmega32HVBREVB__) +# include +#elif defined (__AVR_ATmega406__) +# include +#elif defined (__AVR_ATmega16__) +# include +#elif defined (__AVR_ATmega16A__) +# include +#elif defined (__AVR_ATmega161__) +# include +#elif defined (__AVR_ATmega162__) +# include +#elif defined (__AVR_ATmega163__) +# include +#elif defined (__AVR_ATmega164P__) || defined (__AVR_ATmega164A__) +# include +#elif defined (__AVR_ATmega164PA__) +# include +#elif defined (__AVR_ATmega165__) +# include +#elif defined (__AVR_ATmega165A__) +# include +#elif defined (__AVR_ATmega165P__) +# include +#elif defined (__AVR_ATmega165PA__) +# include +#elif defined (__AVR_ATmega168__) +# include +#elif defined (__AVR_ATmega168A__) +# include +#elif defined (__AVR_ATmega168P__) +# include +#elif defined (__AVR_ATmega168PA__) +# include +#elif defined (__AVR_ATmega169__) || defined (__AVR_ATmega169A__) +# include +#elif defined (__AVR_ATmega169P__) +# include +#elif defined (__AVR_ATmega169PA__) +# include +#elif defined (__AVR_ATmega8HVA__) +# include +#elif defined (__AVR_ATmega16HVA__) +# include +#elif defined (__AVR_ATmega16HVA2__) +# include +#elif defined (__AVR_ATmega16HVB__) +# include +#elif defined (__AVR_ATmega16HVBREVB__) +# include +#elif defined (__AVR_ATmega8__) +# include +#elif defined (__AVR_ATmega8A__) +# include +#elif defined (__AVR_ATmega48__) +# include +#elif defined (__AVR_ATmega48A__) +# include +#elif defined (__AVR_ATmega48PA__) +# include +#elif defined (__AVR_ATmega48PB__) +# include +#elif defined (__AVR_ATmega48P__) +# include +#elif defined (__AVR_ATmega88__) +# include +#elif defined (__AVR_ATmega88A__) +# include +#elif defined (__AVR_ATmega88P__) +# include +#elif defined (__AVR_ATmega88PA__) +# include +#elif defined (__AVR_ATmega88PB__) +# include +#elif defined (__AVR_ATmega8515__) +# include +#elif defined (__AVR_ATmega8535__) +# include +#elif defined (__AVR_AT90S8535__) +# include +#elif defined (__AVR_AT90C8534__) +# include +#elif defined (__AVR_AT90S8515__) +# include +#elif defined (__AVR_AT90S4434__) +# include +#elif defined (__AVR_AT90S4433__) +# include +#elif defined (__AVR_AT90S4414__) +# include +#elif defined (__AVR_ATtiny22__) +# include +#elif defined (__AVR_ATtiny26__) +# include +#elif defined (__AVR_AT90S2343__) +# include +#elif defined (__AVR_AT90S2333__) +# include +#elif defined (__AVR_AT90S2323__) +# include +#elif defined (__AVR_AT90S2313__) +# include +#elif defined (__AVR_ATtiny4__) +# include +#elif defined (__AVR_ATtiny5__) +# include +#elif defined (__AVR_ATtiny9__) +# include +#elif defined (__AVR_ATtiny10__) +# include +#elif defined (__AVR_ATtiny20__) +# include +#elif defined (__AVR_ATtiny40__) +# include +#elif defined (__AVR_ATtiny2313__) +# include +#elif defined (__AVR_ATtiny2313A__) +# include +#elif defined (__AVR_ATtiny13__) +# include +#elif defined (__AVR_ATtiny13A__) +# include +#elif defined (__AVR_ATtiny25__) +# include +#elif defined (__AVR_ATtiny4313__) +# include +#elif defined (__AVR_ATtiny45__) +# include +#elif defined (__AVR_ATtiny85__) +# include +#elif defined (__AVR_ATtiny24__) +# include +#elif defined (__AVR_ATtiny24A__) +# include +#elif defined (__AVR_ATtiny44__) +# include +#elif defined (__AVR_ATtiny44A__) +# include +#elif defined (__AVR_ATtiny441__) +# include +#elif defined (__AVR_ATtiny84__) +# include +#elif defined (__AVR_ATtiny84A__) +# include +#elif defined (__AVR_ATtiny841__) +# include +#elif defined (__AVR_ATtiny261__) +# include +#elif defined (__AVR_ATtiny261A__) +# include +#elif defined (__AVR_ATtiny461__) +# include +#elif defined (__AVR_ATtiny461A__) +# include +#elif defined (__AVR_ATtiny861__) +# include +#elif defined (__AVR_ATtiny861A__) +# include +#elif defined (__AVR_ATtiny43U__) +# include +#elif defined (__AVR_ATtiny48__) +# include +#elif defined (__AVR_ATtiny88__) +# include +#elif defined (__AVR_ATtiny828__) +# include +#elif defined (__AVR_ATtiny87__) +# include +#elif defined (__AVR_ATtiny167__) +# include +#elif defined (__AVR_ATtiny1634__) +# include +#elif defined (__AVR_ATtiny202__) +# include +#elif defined (__AVR_ATtiny204__) +# include +#elif defined (__AVR_ATtiny212__) +# include +#elif defined (__AVR_ATtiny214__) +# include +#elif defined (__AVR_ATtiny402__) +# include +#elif defined (__AVR_ATtiny404__) +# include +#elif defined (__AVR_ATtiny406__) +# include +#elif defined (__AVR_ATtiny412__) +# include +#elif defined (__AVR_ATtiny414__) +# include +#elif defined (__AVR_ATtiny416__) +# include +#elif defined (__AVR_ATtiny417__) +# include +#elif defined (__AVR_ATtiny424__) +# include +#elif defined (__AVR_ATtiny426__) +# include +#elif defined (__AVR_ATtiny427__) +# include +#elif defined (__AVR_ATtiny804__) +# include +#elif defined (__AVR_ATtiny806__) +# include +#elif defined (__AVR_ATtiny807__) +# include +#elif defined (__AVR_ATtiny814__) +# include +#elif defined (__AVR_ATtiny816__) +# include +#elif defined (__AVR_ATtiny817__) +# include +#elif defined (__AVR_ATtiny824__) +# include +#elif defined (__AVR_ATtiny826__) +# include +#elif defined (__AVR_ATtiny827__) +# include +#elif defined (__AVR_ATtiny1604__) +# include +#elif defined (__AVR_ATtiny1606__) +# include +#elif defined (__AVR_ATtiny1607__) +# include +#elif defined (__AVR_ATtiny1614__) +# include +#elif defined (__AVR_ATtiny1616__) +# include +#elif defined (__AVR_ATtiny1617__) +# include +#elif defined (__AVR_ATtiny1624__) +# include +#elif defined (__AVR_ATtiny1626__) +# include +#elif defined (__AVR_ATtiny1627__) +# include +#elif defined (__AVR_ATtiny3214__) +# include +#elif defined (__AVR_ATtiny3216__) +# include +#elif defined (__AVR_ATtiny3217__) +# include +#elif defined (__AVR_ATtiny3224__) +# include +#elif defined (__AVR_ATtiny3226__) +# include +#elif defined (__AVR_ATtiny3227__) +# include +#elif defined (__AVR_ATmega808__) +# include +#elif defined (__AVR_ATmega809__) +# include +#elif defined (__AVR_ATmega1608__) +# include +#elif defined (__AVR_ATmega1609__) +# include +#elif defined (__AVR_ATmega3208__) +# include +#elif defined (__AVR_ATmega3209__) +# include +#elif defined (__AVR_ATmega4808__) +# include +#elif defined (__AVR_ATmega4809__) +# include +#elif defined (__AVR_AT90SCR100__) +# include +#elif defined (__AVR_ATxmega8E5__) +# include +#elif defined (__AVR_ATxmega16A4__) +# include +#elif defined (__AVR_ATxmega16A4U__) +# include +#elif defined (__AVR_ATxmega16C4__) +# include +#elif defined (__AVR_ATxmega16D4__) +# include +#elif defined (__AVR_ATxmega32A4__) +# include +#elif defined (__AVR_ATxmega32A4U__) +# include +#elif defined (__AVR_ATxmega32C3__) +# include +#elif defined (__AVR_ATxmega32C4__) +# include +#elif defined (__AVR_ATxmega32D3__) +# include +#elif defined (__AVR_ATxmega32D4__) +# include +#elif defined (__AVR_ATxmega32E5__) +# include +#elif defined (__AVR_ATxmega64A1__) +# include +#elif defined (__AVR_ATxmega64A1U__) +# include +#elif defined (__AVR_ATxmega64A3__) +# include +#elif defined (__AVR_ATxmega64A3U__) +# include +#elif defined (__AVR_ATxmega64A4U__) +# include +#elif defined (__AVR_ATxmega64B1__) +# include +#elif defined (__AVR_ATxmega64B3__) +# include +#elif defined (__AVR_ATxmega64C3__) +# include +#elif defined (__AVR_ATxmega64D3__) +# include +#elif defined (__AVR_ATxmega64D4__) +# include +#elif defined (__AVR_ATxmega128A1__) +# include +#elif defined (__AVR_ATxmega128A1U__) +# include +#elif defined (__AVR_ATxmega128A4U__) +# include +#elif defined (__AVR_ATxmega128A3__) +# include +#elif defined (__AVR_ATxmega128A3U__) +# include +#elif defined (__AVR_ATxmega128B1__) +# include +#elif defined (__AVR_ATxmega128B3__) +# include +#elif defined (__AVR_ATxmega128C3__) +# include +#elif defined (__AVR_ATxmega128D3__) +# include +#elif defined (__AVR_ATxmega128D4__) +# include +#elif defined (__AVR_ATxmega192A3__) +# include +#elif defined (__AVR_ATxmega192A3U__) +# include +#elif defined (__AVR_ATxmega192C3__) +# include +#elif defined (__AVR_ATxmega192D3__) +# include +#elif defined (__AVR_ATxmega256A3__) +# include +#elif defined (__AVR_ATxmega256A3U__) +# include +#elif defined (__AVR_ATxmega256A3B__) +# include +#elif defined (__AVR_ATxmega256A3BU__) +# include +#elif defined (__AVR_ATxmega256C3__) +# include +#elif defined (__AVR_ATxmega256D3__) +# include +#elif defined (__AVR_ATxmega384C3__) +# include +#elif defined (__AVR_ATxmega384D3__) +# include +#elif defined (__AVR_ATA5702M322__) +# include +#elif defined (__AVR_ATA5782__) +# include +#elif defined (__AVR_ATA5790__) +# include +#elif defined (__AVR_ATA5790N__) +# include +#elif defined (__AVR_ATA5831__) +# include +#elif defined (__AVR_ATA5272__) +# include +#elif defined (__AVR_ATA5505__) +# include +#elif defined (__AVR_ATA5795__) +# include +#elif defined (__AVR_ATA6285__) +# include +#elif defined (__AVR_ATA6286__) +# include +#elif defined (__AVR_ATA6289__) +# include +#elif defined (__AVR_ATA6612C__) +# include +#elif defined (__AVR_ATA6613C__) +# include +#elif defined (__AVR_ATA6614Q__) +# include +#elif defined (__AVR_ATA6616C__) +# include +#elif defined (__AVR_ATA6617C__) +# include +#elif defined (__AVR_ATA664251__) +# include +/* avr1: the following only supported for assembler programs */ +#elif defined (__AVR_ATtiny28__) +# include +#elif defined (__AVR_AT90S1200__) +# include +#elif defined (__AVR_ATtiny15__) +# include +#elif defined (__AVR_ATtiny12__) +# include +#elif defined (__AVR_ATtiny11__) +# include +#elif defined (__AVR_M3000__) +# include +#elif defined (__AVR_AVR32DA28__) +# include +#elif defined (__AVR_AVR32DA32__) +# include +#elif defined (__AVR_AVR32DA48__) +# include +#elif defined (__AVR_AVR64DA28__) +# include +#elif defined (__AVR_AVR64DA32__) +# include +#elif defined (__AVR_AVR64DA48__) +# include +#elif defined (__AVR_AVR64DA64__) +# include +#elif defined (__AVR_AVR128DA28__) +# include +#elif defined (__AVR_AVR128DA32__) +# include +#elif defined (__AVR_AVR128DA48__) +# include +#elif defined (__AVR_AVR128DA64__) +# include +#elif defined (__AVR_AVR32DB28__) +# include +#elif defined (__AVR_AVR32DB32__) +# include +#elif defined (__AVR_AVR32DB48__) +# include +#elif defined (__AVR_AVR64DB28__) +# include +#elif defined (__AVR_AVR64DB32__) +# include +#elif defined (__AVR_AVR64DB48__) +# include +#elif defined (__AVR_AVR64DB64__) +# include +#elif defined (__AVR_AVR128DB28__) +# include +#elif defined (__AVR_AVR128DB32__) +# include +#elif defined (__AVR_AVR128DB48__) +# include +#elif defined (__AVR_AVR128DB64__) +# include +#elif defined (__AVR_AVR16DD14__) +# include +#elif defined (__AVR_AVR16DD20__) +# include +#elif defined (__AVR_AVR16DD28__) +# include +#elif defined (__AVR_AVR16DD32__) +# include +#elif defined (__AVR_AVR32DD14__) +# include +#elif defined (__AVR_AVR32DD20__) +# include +#elif defined (__AVR_AVR32DD28__) +# include +#elif defined (__AVR_AVR32DD32__) +# include +#elif defined (__AVR_AVR64DD14__) +# include +#elif defined (__AVR_AVR64DD20__) +# include +#elif defined (__AVR_AVR64DD28__) +# include +#elif defined (__AVR_AVR64DD32__) +# include +#elif defined (__AVR_DEV_LIB_NAME__) +# define __concat__(a,b) a##b +# define __header1__(a,b) __concat__(a,b) +# define __AVR_DEVICE_HEADER__ +# include __AVR_DEVICE_HEADER__ +#else +# if !defined(__COMPILING_AVR_LIBC__) +# warning "device type not defined" +# endif +#endif + +#include + +#include + +#include + +#if __AVR_ARCH__ >= 100 +# include +#endif + +/* Include fuse.h after individual IO header files. */ +#include + +/* Include lock.h after individual IO header files. */ +#include + +#endif /* _AVR_IO_H_ */ diff --git a/test/mocks/avr/iom168.h b/test/mocks/avr/iom168.h new file mode 100644 index 000000000..55d061182 --- /dev/null +++ b/test/mocks/avr/iom168.h @@ -0,0 +1,99 @@ +/* Copyright (c) 2004, Theodore A. Roth + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of the copyright holders nor the names of + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. */ + +/* $Id$ */ + +#ifndef _AVR_IOM168_H_ +#define _AVR_IOM168_H_ 1 + +#include + +/* Constants */ +#define SPM_PAGESIZE 128 +#define RAMSTART (0x100) +#define RAMEND 0x4FF +#define XRAMEND RAMEND +#define E2END 0x1FF +#define E2PAGESIZE 4 +#define FLASHEND 0x3FFF + + +/* Fuses */ +#define FUSE_MEMORY_SIZE 3 + +/* Low Fuse Byte */ +#define FUSE_CKSEL0 (unsigned char)~_BV(0) /* Select Clock Source */ +#define FUSE_CKSEL1 (unsigned char)~_BV(1) /* Select Clock Source */ +#define FUSE_CKSEL2 (unsigned char)~_BV(2) /* Select Clock Source */ +#define FUSE_CKSEL3 (unsigned char)~_BV(3) /* Select Clock Source */ +#define FUSE_SUT0 (unsigned char)~_BV(4) /* Select start-up time */ +#define FUSE_SUT1 (unsigned char)~_BV(5) /* Select start-up time */ +#define FUSE_CKOUT (unsigned char)~_BV(6) /* Clock output */ +#define FUSE_CKDIV8 (unsigned char)~_BV(7) /* Divide clock by 8 */ +#define LFUSE_DEFAULT (FUSE_CKSEL0 & FUSE_CKSEL2 & FUSE_CKSEL3 & FUSE_SUT0 & FUSE_CKDIV8) + +/* High Fuse Byte */ +#define FUSE_BODLEVEL0 (unsigned char)~_BV(0) /* Brown-out Detector trigger level */ +#define FUSE_BODLEVEL1 (unsigned char)~_BV(1) /* Brown-out Detector trigger level */ +#define FUSE_BODLEVEL2 (unsigned char)~_BV(2) /* Brown-out Detector trigger level */ +#define FUSE_EESAVE (unsigned char)~_BV(3) /* EEPROM memory is preserved through chip erase */ +#define FUSE_WDTON (unsigned char)~_BV(4) /* Watchdog Timer Always On */ +#define FUSE_SPIEN (unsigned char)~_BV(5) /* Enable Serial programming and Data Downloading */ +#define FUSE_DWEN (unsigned char)~_BV(6) /* debugWIRE Enable */ +#define FUSE_RSTDISBL (unsigned char)~_BV(7) /* External reset disable */ +#define HFUSE_DEFAULT (FUSE_SPIEN) + +/* Extended Fuse Byte */ +#define FUSE_BOOTRST (unsigned char)~_BV(0) +#define FUSE_BOOTSZ0 (unsigned char)~_BV(1) +#define FUSE_BOOTSZ1 (unsigned char)~_BV(2) +#define EFUSE_DEFAULT (FUSE_BOOTSZ0 & FUSE_BOOTSZ1) + + +/* Lock Bits */ +#define __LOCK_BITS_EXIST +#define __BOOT_LOCK_BITS_0_EXIST +#define __BOOT_LOCK_BITS_1_EXIST + + +/* Signature */ +#define SIGNATURE_0 0x1E +#define SIGNATURE_1 0x94 +#define SIGNATURE_2 0x06 + + +#define SLEEP_MODE_IDLE (0x00<<1) +#define SLEEP_MODE_ADC (0x01<<1) +#define SLEEP_MODE_PWR_DOWN (0x02<<1) +#define SLEEP_MODE_PWR_SAVE (0x03<<1) +#define SLEEP_MODE_STANDBY (0x06<<1) + + +#endif /* _AVR_IOM168_H_ */ diff --git a/test/mocks/avr/iomx8.h b/test/mocks/avr/iomx8.h new file mode 100644 index 000000000..b1cc8e786 --- /dev/null +++ b/test/mocks/avr/iomx8.h @@ -0,0 +1,808 @@ +/* Copyright (c) 2004,2005, Theodore A. Roth + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of the copyright holders nor the names of + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. */ + +/* $Id$ */ + +/* avr/iomx8.h - definitions for ATmega48, ATmega88 and ATmega168 */ + +#ifndef _AVR_IOMX8_H_ +#define _AVR_IOMX8_H_ 1 + +/* This file should only be included from , never directly. */ + +#ifndef _AVR_IO_H_ +# error "Include instead of this file." +#endif + +#ifndef _AVR_IOXXX_H_ +# define _AVR_IOXXX_H_ "iomx8.h" +#else +# error "Attempt to include more than one file." +#endif + +/* I/O registers */ + +/* Port B */ + +#define PINB _SFR_IO8 (0x03) +/* PINB */ +#define PINB7 7 +#define PINB6 6 +#define PINB5 5 +#define PINB4 4 +#define PINB3 3 +#define PINB2 2 +#define PINB1 1 +#define PINB0 0 + +#define DDRB _SFR_IO8 (0x04) +/* DDRB */ +#define DDB7 7 +#define DDB6 6 +#define DDB5 5 +#define DDB4 4 +#define DDB3 3 +#define DDB2 2 +#define DDB1 1 +#define DDB0 0 + +#define PORTB _SFR_IO8 (0x05) +/* PORTB */ +#define PB7 7 +#define PB6 6 +#define PB5 5 +#define PB4 4 +#define PB3 3 +#define PB2 2 +#define PB1 1 +#define PB0 0 + +/* Port C */ + +#define PINC _SFR_IO8 (0x06) +/* PINC */ +#define PINC6 6 +#define PINC5 5 +#define PINC4 4 +#define PINC3 3 +#define PINC2 2 +#define PINC1 1 +#define PINC0 0 + +#define DDRC _SFR_IO8 (0x07) +/* DDRC */ +#define DDC6 6 +#define DDC5 5 +#define DDC4 4 +#define DDC3 3 +#define DDC2 2 +#define DDC1 1 +#define DDC0 0 + +#define PORTC _SFR_IO8 (0x08) +/* PORTC */ +#define PC6 6 +#define PC5 5 +#define PC4 4 +#define PC3 3 +#define PC2 2 +#define PC1 1 +#define PC0 0 + +/* Port D */ + +#define PIND _SFR_IO8 (0x09) +/* PIND */ +#define PIND7 7 +#define PIND6 6 +#define PIND5 5 +#define PIND4 4 +#define PIND3 3 +#define PIND2 2 +#define PIND1 1 +#define PIND0 0 + +#define DDRD _SFR_IO8 (0x0A) +/* DDRD */ +#define DDD7 7 +#define DDD6 6 +#define DDD5 5 +#define DDD4 4 +#define DDD3 3 +#define DDD2 2 +#define DDD1 1 +#define DDD0 0 + +#define PORTD _SFR_IO8 (0x0B) +/* PORTD */ +#define PD7 7 +#define PD6 6 +#define PD5 5 +#define PD4 4 +#define PD3 3 +#define PD2 2 +#define PD1 1 +#define PD0 0 + +#define TIFR0 _SFR_IO8 (0x15) +/* TIFR0 */ +#define OCF0B 2 +#define OCF0A 1 +#define TOV0 0 + +#define TIFR1 _SFR_IO8 (0x16) +/* TIFR1 */ +#define ICF1 5 +#define OCF1B 2 +#define OCF1A 1 +#define TOV1 0 + +#define TIFR2 _SFR_IO8 (0x17) +/* TIFR2 */ +#define OCF2B 2 +#define OCF2A 1 +#define TOV2 0 + +#define PCIFR _SFR_IO8 (0x1B) +/* PCIFR */ +#define PCIF2 2 +#define PCIF1 1 +#define PCIF0 0 + +#define EIFR _SFR_IO8 (0x1C) +/* EIFR */ +#define INTF1 1 +#define INTF0 0 + +#define EIMSK _SFR_IO8 (0x1D) +/* EIMSK */ +#define INT1 1 +#define INT0 0 + +#define GPIOR0 _SFR_IO8 (0x1E) + +#define EECR _SFR_IO8(0x1F) +/* EECT - EEPROM Control Register */ +#define EEPM1 5 +#define EEPM0 4 +#define EERIE 3 +#define EEMPE 2 +#define EEPE 1 +#define EERE 0 + +#define EEDR _SFR_IO8(0X20) + +/* Combine EEARL and EEARH */ +#define EEAR _SFR_IO16(0x21) +#define EEARL _SFR_IO8(0x21) +#define EEARH _SFR_IO8(0X22) +/* +Even though EEARH is not used by the mega48, the EEAR8 bit in the register +must be written to 0, according to the datasheet, hence the EEARH register +must be defined for the mega48. +*/ +/* 6-char sequence denoting where to find the EEPROM registers in memory space. + Adresses denoted in hex syntax with uppercase letters. Used by the EEPROM + subroutines. + First two letters: EECR address. + Second two letters: EEDR address. + Last two letters: EEAR address. */ +#define __EEPROM_REG_LOCATIONS__ 1F2021 + + +#define GTCCR _SFR_IO8 (0x23) +/* GTCCR */ +#define TSM 7 +#define PSRASY 1 +#define PSRSYNC 0 + +#define TCCR0A _SFR_IO8 (0x24) +/* TCCR0A */ +#define COM0A1 7 +#define COM0A0 6 +#define COM0B1 5 +#define COM0B0 4 +#define WGM01 1 +#define WGM00 0 + +#define TCCR0B _SFR_IO8 (0x25) +/* TCCR0A */ +#define FOC0A 7 +#define FOC0B 6 +#define WGM02 3 +#define CS02 2 +#define CS01 1 +#define CS00 0 + +#define TCNT0 _SFR_IO8 (0x26) +#define OCR0A _SFR_IO8 (0x27) +#define OCR0B _SFR_IO8 (0x28) + +#define GPIOR1 _SFR_IO8 (0x2A) +#define GPIOR2 _SFR_IO8 (0x2B) + +#define SPCR _SFR_IO8 (0x2C) +/* SPCR */ +#define SPIE 7 +#define SPE 6 +#define DORD 5 +#define MSTR 4 +#define CPOL 3 +#define CPHA 2 +#define SPR1 1 +#define SPR0 0 + +#define SPSR _SFR_IO8 (0x2D) +/* SPSR */ +#define SPIF 7 +#define WCOL 6 +#define SPI2X 0 + +#define SPDR _SFR_IO8 (0x2E) + +#define ACSR _SFR_IO8 (0x30) +/* ACSR */ +#define ACD 7 +#define ACBG 6 +#define ACO 5 +#define ACI 4 +#define ACIE 3 +#define ACIC 2 +#define ACIS1 1 +#define ACIS0 0 + +#define MONDR _SFR_IO8 (0x31) + +#define SMCR _SFR_IO8 (0x33) +/* SMCR */ +#define SM2 3 +#define SM1 2 +#define SM0 1 +#define SE 0 + +#define MCUSR _SFR_IO8 (0x34) +/* MCUSR */ +#define WDRF 3 +#define BORF 2 +#define EXTRF 1 +#define PORF 0 + +#define MCUCR _SFR_IO8 (0x35) +/* MCUCR */ +#define PUD 4 +#if defined (__AVR_ATmega88__) || defined (__AVR_ATmega168__) +#define IVSEL 1 +#define IVCE 0 +#endif + +#define SPMCSR _SFR_IO8 (0x37) +/* SPMCSR */ +#define SPMIE 7 +#if defined (__AVR_ATmega88__) || defined (__AVR_ATmega168__) || (__AVR_ATmega88P__) || defined (__AVR_ATmega168P__) || (__AVR_ATmega88A__) || defined (__AVR_ATmega168A__) || (__AVR_ATmega88PA__) || defined (__AVR_ATmega168PA__) +# define RWWSB 6 +# define RWWSRE 4 +#endif +#if defined(__AVR_ATmega48A) || defined(__AVR_ATmega48PA) || defined(__AVR_ATmega88A) || defined(__AVR_ATmega88PA) || defined(__AVR_ATmega168A) || defined(__AVR_ATmega168PA) + #define SIGRD 5 +#endif +#define BLBSET 3 +#define PGWRT 2 +#define PGERS 1 +#define SELFPRGEN 0 +#define SPMEN 0 + +/* 0x3D..0x3E SP [defined in ] */ +/* 0x3F SREG [defined in ] */ + +#define WDTCSR _SFR_MEM8 (0x60) +/* WDTCSR */ +#define WDIF 7 +#define WDIE 6 +#define WDP3 5 +#define WDCE 4 +#define WDE 3 +#define WDP2 2 +#define WDP1 1 +#define WDP0 0 + +#define CLKPR _SFR_MEM8 (0x61) +/* CLKPR */ +#define CLKPCE 7 +#define CLKPS3 3 +#define CLKPS2 2 +#define CLKPS1 1 +#define CLKPS0 0 + +#define PRR _SFR_MEM8 (0x64) +/* PRR */ +#define PRTWI 7 +#define PRTIM2 6 +#define PRTIM0 5 +#define PRTIM1 3 +#define PRSPI 2 +#define PRUSART0 1 +#define PRADC 0 + +#define __AVR_HAVE_PRR ((1<: Lockbit Support + + \par Introduction + + The Lockbit API allows a user to specify the lockbit settings for the + specific AVR device they are compiling for. These lockbit settings will be + placed in a special section in the ELF output file, after linking. + + Programming tools can take advantage of the lockbit information embedded in + the ELF file, by extracting this information and determining if the lockbits + need to be programmed after programming the Flash and EEPROM memories. + This also allows a single ELF file to contain all the + information needed to program an AVR. + + To use the Lockbit API, include the header file, which in turn + automatically includes the individual I/O header file and the + file. These other two files provides everything necessary to set the AVR + lockbits. + + \par Lockbit API + + Each I/O header file may define up to 3 macros that controls what kinds + of lockbits are available to the user. + + If __LOCK_BITS_EXIST is defined, then two lock bits are available to the + user and 3 mode settings are defined for these two bits. + + If __BOOT_LOCK_BITS_0_EXIST is defined, then the two BLB0 lock bits are + available to the user and 4 mode settings are defined for these two bits. + + If __BOOT_LOCK_BITS_1_EXIST is defined, then the two BLB1 lock bits are + available to the user and 4 mode settings are defined for these two bits. + + If __BOOT_LOCK_APPLICATION_TABLE_BITS_EXIST is defined then two lock bits + are available to set the locking mode for the Application Table Section + (which is used in the XMEGA family). + + If __BOOT_LOCK_APPLICATION_BITS_EXIST is defined then two lock bits are + available to set the locking mode for the Application Section (which is used + in the XMEGA family). + + If __BOOT_LOCK_BOOT_BITS_EXIST is defined then two lock bits are available + to set the locking mode for the Boot Loader Section (which is used in the + XMEGA family). + + The AVR lockbit modes have inverted values, logical 1 for an unprogrammed + (disabled) bit and logical 0 for a programmed (enabled) bit. The defined + macros for each individual lock bit represent this in their definition by a + bit-wise inversion of a mask. For example, the LB_MODE_3 macro is defined + as: + \code + #define LB_MODE_3 (0xFC) +` \endcode + + To combine the lockbit mode macros together to represent a whole byte, + use the bitwise AND operator, like so: + \code + (LB_MODE_3 & BLB0_MODE_2) + \endcode + + also defines a macro that provides a default lockbit value: + LOCKBITS_DEFAULT which is defined to be 0xFF. + + See the AVR device specific datasheet for more details about these + lock bits and the available mode settings. + + A convenience macro, LOCKMEM, is defined as a GCC attribute for a + custom-named section of ".lock". + + A convenience macro, LOCKBITS, is defined that declares a variable, __lock, + of type unsigned char with the attribute defined by LOCKMEM. This variable + allows the end user to easily set the lockbit data. + + \note If a device-specific I/O header file has previously defined LOCKMEM, + then LOCKMEM is not redefined. If a device-specific I/O header file has + previously defined LOCKBITS, then LOCKBITS is not redefined. LOCKBITS is + currently known to be defined in the I/O header files for the XMEGA devices. + + \par API Usage Example + + Putting all of this together is easy: + + \code + #include + + LOCKBITS = (LB_MODE_1 & BLB0_MODE_3 & BLB1_MODE_4); + + int main(void) + { + return 0; + } + \endcode + + Or: + + \code + #include + + unsigned char __lock __attribute__((section (".lock"))) = + (LB_MODE_1 & BLB0_MODE_3 & BLB1_MODE_4); + + int main(void) + { + return 0; + } + \endcode + + + + However there are a number of caveats that you need to be aware of to + use this API properly. + + Be sure to include to get all of the definitions for the API. + The LOCKBITS macro defines a global variable to store the lockbit data. This + variable is assigned to its own linker section. Assign the desired lockbit + values immediately in the variable initialization. + + The .lock section in the ELF file will get its values from the initial + variable assignment ONLY. This means that you can NOT assign values to + this variable in functions and the new values will not be put into the + ELF .lock section. + + The global variable is declared in the LOCKBITS macro has two leading + underscores, which means that it is reserved for the "implementation", + meaning the library, so it will not conflict with a user-named variable. + + You must initialize the lockbit variable to some meaningful value, even + if it is the default value. This is because the lockbits default to a + logical 1, meaning unprogrammed. Normal uninitialized data defaults to all + locgial zeros. So it is vital that all lockbits are initialized, even with + default data. If they are not, then the lockbits may not programmed to the + desired settings and can possibly put your device into an unrecoverable + state. + + Be sure to have the -mmcu=device flag in your compile command line and + your linker command line to have the correct device selected and to have + the correct I/O header file included when you include . + + You can print out the contents of the .lock section in the ELF file by + using this command line: + \code + avr-objdump -s -j .lock + \endcode + +*/ + + +#if !(defined(__ASSEMBLER__) || defined(__DOXYGEN__)) + +#ifndef LOCKMEM +#define LOCKMEM __attribute__((__used__, __section__ (".lock"))) +#endif + +#ifndef LOCKBITS +#define LOCKBITS unsigned char __lock LOCKMEM +#endif + +/* Lock Bit Modes */ +#if defined(__LOCK_BITS_EXIST) +#define LB_MODE_1 (0xFF) +#define LB_MODE_2 (0xFE) +#define LB_MODE_3 (0xFC) +#endif + +#if defined(__BOOT_LOCK_BITS_0_EXIST) +#define BLB0_MODE_1 (0xFF) +#define BLB0_MODE_2 (0xFB) +#define BLB0_MODE_3 (0xF3) +#define BLB0_MODE_4 (0xF7) +#endif + +#if defined(__BOOT_LOCK_BITS_1_EXIST) +#define BLB1_MODE_1 (0xFF) +#define BLB1_MODE_2 (0xEF) +#define BLB1_MODE_3 (0xCF) +#define BLB1_MODE_4 (0xDF) +#endif + +#if defined(__BOOT_LOCK_APPLICATION_TABLE_BITS_EXIST) +#define BLBAT0 ~_BV(2) +#define BLBAT1 ~_BV(3) +#endif + +#if defined(__BOOT_LOCK_APPLICATION_BITS_EXIST) +#define BLBA0 ~_BV(4) +#define BLBA1 ~_BV(5) +#endif + +#if defined(__BOOT_LOCK_BOOT_BITS_EXIST) +#define BLBB0 ~_BV(6) +#define BLBB1 ~_BV(7) +#endif + + +#define LOCKBITS_DEFAULT (0xFF) + +#endif /* !(__ASSEMBLER || __DOXYGEN__) */ + + +#endif /* _AVR_LOCK_H_ */ diff --git a/test/mocks/avr/portpins.h b/test/mocks/avr/portpins.h new file mode 100644 index 000000000..c179edfab --- /dev/null +++ b/test/mocks/avr/portpins.h @@ -0,0 +1,549 @@ +/* Copyright (c) 2003 Theodore A. Roth + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of the copyright holders nor the names of + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. */ + +/* $Id$ */ + +#ifndef _AVR_PORTPINS_H_ +#define _AVR_PORTPINS_H_ 1 + +/* This file should only be included from , never directly. */ + +#ifndef _AVR_IO_H_ +# error "Include instead of this file." +#endif + +/* Define Generic PORTn, DDn, and PINn values. */ + +/* Port Data Register (generic) */ +#define PORT7 7 +#define PORT6 6 +#define PORT5 5 +#define PORT4 4 +#define PORT3 3 +#define PORT2 2 +#define PORT1 1 +#define PORT0 0 + +/* Port Data Direction Register (generic) */ +#define DD7 7 +#define DD6 6 +#define DD5 5 +#define DD4 4 +#define DD3 3 +#define DD2 2 +#define DD1 1 +#define DD0 0 + +/* Port Input Pins (generic) */ +#define PIN7 7 +#define PIN6 6 +#define PIN5 5 +#define PIN4 4 +#define PIN3 3 +#define PIN2 2 +#define PIN1 1 +#define PIN0 0 + +/* Define PORTxn an Pxn values for all possible port pins if not defined already by io.h. */ + +/* PORT A */ + +#if defined(PA0) && !defined(PORTA0) +# define PORTA0 PA0 +#elif defined(PORTA0) && !defined(PA0) +# define PA0 PORTA0 +#endif +#if defined(PA1) && !defined(PORTA1) +# define PORTA1 PA1 +#elif defined(PORTA1) && !defined(PA1) +# define PA1 PORTA1 +#endif +#if defined(PA2) && !defined(PORTA2) +# define PORTA2 PA2 +#elif defined(PORTA2) && !defined(PA2) +# define PA2 PORTA2 +#endif +#if defined(PA3) && !defined(PORTA3) +# define PORTA3 PA3 +#elif defined(PORTA3) && !defined(PA3) +# define PA3 PORTA3 +#endif +#if defined(PA4) && !defined(PORTA4) +# define PORTA4 PA4 +#elif defined(PORTA4) && !defined(PA4) +# define PA4 PORTA4 +#endif +#if defined(PA5) && !defined(PORTA5) +# define PORTA5 PA5 +#elif defined(PORTA5) && !defined(PA5) +# define PA5 PORTA5 +#endif +#if defined(PA6) && !defined(PORTA6) +# define PORTA6 PA6 +#elif defined(PORTA6) && !defined(PA6) +# define PA6 PORTA6 +#endif +#if defined(PA7) && !defined(PORTA7) +# define PORTA7 PA7 +#elif defined(PORTA7) && !defined(PA7) +# define PA7 PORTA7 +#endif + +/* PORT B */ + +#if defined(PB0) && !defined(PORTB0) +# define PORTB0 PB0 +#elif defined(PORTB0) && !defined(PB0) +# define PB0 PORTB0 +#endif +#if defined(PB1) && !defined(PORTB1) +# define PORTB1 PB1 +#elif defined(PORTB1) && !defined(PB1) +# define PB1 PORTB1 +#endif +#if defined(PB2) && !defined(PORTB2) +# define PORTB2 PB2 +#elif defined(PORTB2) && !defined(PB2) +# define PB2 PORTB2 +#endif +#if defined(PB3) && !defined(PORTB3) +# define PORTB3 PB3 +#elif defined(PORTB3) && !defined(PB3) +# define PB3 PORTB3 +#endif +#if defined(PB4) && !defined(PORTB4) +# define PORTB4 PB4 +#elif defined(PORTB4) && !defined(PB4) +# define PB4 PORTB4 +#endif +#if defined(PB5) && !defined(PORTB5) +# define PORTB5 PB5 +#elif defined(PORTB5) && !defined(PB5) +# define PB5 PORTB5 +#endif +#if defined(PB6) && !defined(PORTB6) +# define PORTB6 PB6 +#elif defined(PORTB6) && !defined(PB6) +# define PB6 PORTB6 +#endif +#if defined(PB7) && !defined(PORTB7) +# define PORTB7 PB7 +#elif defined(PORTB7) && !defined(PB7) +# define PB7 PORTB7 +#endif + +/* PORT C */ + +#if defined(PC0) && !defined(PORTC0) +# define PORTC0 PC0 +#elif defined(PORTC0) && !defined(PC0) +# define PC0 PORTC0 +#endif +#if defined(PC1) && !defined(PORTC1) +# define PORTC1 PC1 +#elif defined(PORTC1) && !defined(PC1) +# define PC1 PORTC1 +#endif +#if defined(PC2) && !defined(PORTC2) +# define PORTC2 PC2 +#elif defined(PORTC2) && !defined(PC2) +# define PC2 PORTC2 +#endif +#if defined(PC3) && !defined(PORTC3) +# define PORTC3 PC3 +#elif defined(PORTC3) && !defined(PC3) +# define PC3 PORTC3 +#endif +#if defined(PC4) && !defined(PORTC4) +# define PORTC4 PC4 +#elif defined(PORTC4) && !defined(PC4) +# define PC4 PORTC4 +#endif +#if defined(PC5) && !defined(PORTC5) +# define PORTC5 PC5 +#elif defined(PORTC5) && !defined(PC5) +# define PC5 PORTC5 +#endif +#if defined(PC6) && !defined(PORTC6) +# define PORTC6 PC6 +#elif defined(PORTC6) && !defined(PC6) +# define PC6 PORTC6 +#endif +#if defined(PC7) && !defined(PORTC7) +# define PORTC7 PC7 +#elif defined(PORTC7) && !defined(PC7) +# define PC7 PORTC7 +#endif + +/* PORT D */ + +#if defined(PD0) && !defined(PORTD0) +# define PORTD0 PD0 +#elif defined(PORTD0) && !defined(PD0) +# define PD0 PORTD0 +#endif +#if defined(PD1) && !defined(PORTD1) +# define PORTD1 PD1 +#elif defined(PORTD1) && !defined(PD1) +# define PD1 PORTD1 +#endif +#if defined(PD2) && !defined(PORTD2) +# define PORTD2 PD2 +#elif defined(PORTD2) && !defined(PD2) +# define PD2 PORTD2 +#endif +#if defined(PD3) && !defined(PORTD3) +# define PORTD3 PD3 +#elif defined(PORTD3) && !defined(PD3) +# define PD3 PORTD3 +#endif +#if defined(PD4) && !defined(PORTD4) +# define PORTD4 PD4 +#elif defined(PORTD4) && !defined(PD4) +# define PD4 PORTD4 +#endif +#if defined(PD5) && !defined(PORTD5) +# define PORTD5 PD5 +#elif defined(PORTD5) && !defined(PD5) +# define PD5 PORTD5 +#endif +#if defined(PD6) && !defined(PORTD6) +# define PORTD6 PD6 +#elif defined(PORTD6) && !defined(PD6) +# define PD6 PORTD6 +#endif +#if defined(PD7) && !defined(PORTD7) +# define PORTD7 PD7 +#elif defined(PORTD7) && !defined(PD7) +# define PD7 PORTD7 +#endif + +/* PORT E */ + +#if defined(PE0) && !defined(PORTE0) +# define PORTE0 PE0 +#elif defined(PORTE0) && !defined(PE0) +# define PE0 PORTE0 +#endif +#if defined(PE1) && !defined(PORTE1) +# define PORTE1 PE1 +#elif defined(PORTE1) && !defined(PE1) +# define PE1 PORTE1 +#endif +#if defined(PE2) && !defined(PORTE2) +# define PORTE2 PE2 +#elif defined(PORTE2) && !defined(PE2) +# define PE2 PORTE2 +#endif +#if defined(PE3) && !defined(PORTE3) +# define PORTE3 PE3 +#elif defined(PORTE3) && !defined(PE3) +# define PE3 PORTE3 +#endif +#if defined(PE4) && !defined(PORTE4) +# define PORTE4 PE4 +#elif defined(PORTE4) && !defined(PE4) +# define PE4 PORTE4 +#endif +#if defined(PE5) && !defined(PORTE5) +# define PORTE5 PE5 +#elif defined(PORTE5) && !defined(PE5) +# define PE5 PORTE5 +#endif +#if defined(PE6) && !defined(PORTE6) +# define PORTE6 PE6 +#elif defined(PORTE6) && !defined(PE6) +# define PE6 PORTE6 +#endif +#if defined(PE7) && !defined(PORTE7) +# define PORTE7 PE7 +#elif defined(PORTE7) && !defined(PE7) +# define PE7 PORTE7 +#endif + +/* PORT F */ + +#if defined(PF0) && !defined(PORTF0) +# define PORTF0 PF0 +#elif defined(PORTF0) && !defined(PF0) +# define PF0 PORTF0 +#endif +#if defined(PF1) && !defined(PORTF1) +# define PORTF1 PF1 +#elif defined(PORTF1) && !defined(PF1) +# define PF1 PORTF1 +#endif +#if defined(PF2) && !defined(PORTF2) +# define PORTF2 PF2 +#elif defined(PORTF2) && !defined(PF2) +# define PF2 PORTF2 +#endif +#if defined(PF3) && !defined(PORTF3) +# define PORTF3 PF3 +#elif defined(PORTF3) && !defined(PF3) +# define PF3 PORTF3 +#endif +#if defined(PF4) && !defined(PORTF4) +# define PORTF4 PF4 +#elif defined(PORTF4) && !defined(PF4) +# define PF4 PORTF4 +#endif +#if defined(PF5) && !defined(PORTF5) +# define PORTF5 PF5 +#elif defined(PORTF5) && !defined(PF5) +# define PF5 PORTF5 +#endif +#if defined(PF6) && !defined(PORTF6) +# define PORTF6 PF6 +#elif defined(PORTF6) && !defined(PF6) +# define PF6 PORTF6 +#endif +#if defined(PF7) && !defined(PORTF7) +# define PORTF7 PF7 +#elif defined(PORTF7) && !defined(PF7) +# define PF7 PORTF7 +#endif + +/* PORT G */ + +#if defined(PG0) && !defined(PORTG0) +# define PORTG0 PG0 +#elif defined(PORTG0) && !defined(PG0) +# define PG0 PORTG0 +#endif +#if defined(PG1) && !defined(PORTG1) +# define PORTG1 PG1 +#elif defined(PORTG1) && !defined(PG1) +# define PG1 PORTG1 +#endif +#if defined(PG2) && !defined(PORTG2) +# define PORTG2 PG2 +#elif defined(PORTG2) && !defined(PG2) +# define PG2 PORTG2 +#endif +#if defined(PG3) && !defined(PORTG3) +# define PORTG3 PG3 +#elif defined(PORTG3) && !defined(PG3) +# define PG3 PORTG3 +#endif +#if defined(PG4) && !defined(PORTG4) +# define PORTG4 PG4 +#elif defined(PORTG4) && !defined(PG4) +# define PG4 PORTG4 +#endif +#if defined(PG5) && !defined(PORTG5) +# define PORTG5 PG5 +#elif defined(PORTG5) && !defined(PG5) +# define PG5 PORTG5 +#endif +#if defined(PG6) && !defined(PORTG6) +# define PORTG6 PG6 +#elif defined(PORTG6) && !defined(PG6) +# define PG6 PORTG6 +#endif +#if defined(PG7) && !defined(PORTG7) +# define PORTG7 PG7 +#elif defined(PORTG7) && !defined(PG7) +# define PG7 PORTG7 +#endif + +/* PORT H */ + +#if defined(PH0) && !defined(PORTH0) +# define PORTH0 PH0 +#elif defined(PORTH0) && !defined(PH0) +# define PH0 PORTH0 +#endif +#if defined(PH1) && !defined(PORTH1) +# define PORTH1 PH1 +#elif defined(PORTH1) && !defined(PH1) +# define PH1 PORTH1 +#endif +#if defined(PH2) && !defined(PORTH2) +# define PORTH2 PH2 +#elif defined(PORTH2) && !defined(PH2) +# define PH2 PORTH2 +#endif +#if defined(PH3) && !defined(PORTH3) +# define PORTH3 PH3 +#elif defined(PORTH3) && !defined(PH3) +# define PH3 PORTH3 +#endif +#if defined(PH4) && !defined(PORTH4) +# define PORTH4 PH4 +#elif defined(PORTH4) && !defined(PH4) +# define PH4 PORTH4 +#endif +#if defined(PH5) && !defined(PORTH5) +# define PORTH5 PH5 +#elif defined(PORTH5) && !defined(PH5) +# define PH5 PORTH5 +#endif +#if defined(PH6) && !defined(PORTH6) +# define PORTH6 PH6 +#elif defined(PORTH6) && !defined(PH6) +# define PH6 PORTH6 +#endif +#if defined(PH7) && !defined(PORTH7) +# define PORTH7 PH7 +#elif defined(PORTH7) && !defined(PH7) +# define PH7 PORTH7 +#endif + +/* PORT J */ + +#if defined(PJ0) && !defined(PORTJ0) +# define PORTJ0 PJ0 +#elif defined(PORTJ0) && !defined(PJ0) +# define PJ0 PORTJ0 +#endif +#if defined(PJ1) && !defined(PORTJ1) +# define PORTJ1 PJ1 +#elif defined(PORTJ1) && !defined(PJ1) +# define PJ1 PORTJ1 +#endif +#if defined(PJ2) && !defined(PORTJ2) +# define PORTJ2 PJ2 +#elif defined(PORTJ2) && !defined(PJ2) +# define PJ2 PORTJ2 +#endif +#if defined(PJ3) && !defined(PORTJ3) +# define PORTJ3 PJ3 +#elif defined(PORTJ3) && !defined(PJ3) +# define PJ3 PORTJ3 +#endif +#if defined(PJ4) && !defined(PORTJ4) +# define PORTJ4 PJ4 +#elif defined(PORTJ4) && !defined(PJ4) +# define PJ4 PORTJ4 +#endif +#if defined(PJ5) && !defined(PORTJ5) +# define PORTJ5 PJ5 +#elif defined(PORTJ5) && !defined(PJ5) +# define PJ5 PORTJ5 +#endif +#if defined(PJ6) && !defined(PORTJ6) +# define PORTJ6 PJ6 +#elif defined(PORTJ6) && !defined(PJ6) +# define PJ6 PORTJ6 +#endif +#if defined(PJ7) && !defined(PORTJ7) +# define PORTJ7 PJ7 +#elif defined(PORTJ7) && !defined(PJ7) +# define PJ7 PORTJ7 +#endif + +/* PORT K */ + +#if defined(PK0) && !defined(PORTK0) +# define PORTK0 PK0 +#elif defined(PORTK0) && !defined(PK0) +# define PK0 PORTK0 +#endif +#if defined(PK1) && !defined(PORTK1) +# define PORTK1 PK1 +#elif defined(PORTK1) && !defined(PK1) +# define PK1 PORTK1 +#endif +#if defined(PK2) && !defined(PORTK2) +# define PORTK2 PK2 +#elif defined(PORTK2) && !defined(PK2) +# define PK2 PORTK2 +#endif +#if defined(PK3) && !defined(PORTK3) +# define PORTK3 PK3 +#elif defined(PORTK3) && !defined(PK3) +# define PK3 PORTK3 +#endif +#if defined(PK4) && !defined(PORTK4) +# define PORTK4 PK4 +#elif defined(PORTK4) && !defined(PK4) +# define PK4 PORTK4 +#endif +#if defined(PK5) && !defined(PORTK5) +# define PORTK5 PK5 +#elif defined(PORTK5) && !defined(PK5) +# define PK5 PORTK5 +#endif +#if defined(PK6) && !defined(PORTK6) +# define PORTK6 PK6 +#elif defined(PORTK6) && !defined(PK6) +# define PK6 PORTK6 +#endif +#if defined(PK7) && !defined(PORTK7) +# define PORTK7 PK7 +#elif defined(PORTK7) && !defined(PK7) +# define PK7 PORTK7 +#endif + +/* PORT L */ + +#if defined(PL0) && !defined(PORTL0) +# define PORTL0 PL0 +#elif defined(PORTL0) && !defined(PL0) +# define PL0 PORTL0 +#endif +#if defined(PL1) && !defined(PORTL1) +# define PORTL1 PL1 +#elif defined(PORTL1) && !defined(PL1) +# define PL1 PORTL1 +#endif +#if defined(PL2) && !defined(PORTL2) +# define PORTL2 PL2 +#elif defined(PORTL2) && !defined(PL2) +# define PL2 PORTL2 +#endif +#if defined(PL3) && !defined(PORTL3) +# define PORTL3 PL3 +#elif defined(PORTL3) && !defined(PL3) +# define PL3 PORTL3 +#endif +#if defined(PL4) && !defined(PORTL4) +# define PORTL4 PL4 +#elif defined(PORTL4) && !defined(PL4) +# define PL4 PORTL4 +#endif +#if defined(PL5) && !defined(PORTL5) +# define PORTL5 PL5 +#elif defined(PORTL5) && !defined(PL5) +# define PL5 PORTL5 +#endif +#if defined(PL6) && !defined(PORTL6) +# define PORTL6 PL6 +#elif defined(PORTL6) && !defined(PL6) +# define PL6 PORTL6 +#endif +#if defined(PL7) && !defined(PORTL7) +# define PORTL7 PL7 +#elif defined(PORTL7) && !defined(PL7) +# define PL7 PORTL7 +#endif + +#endif /* _AVR_PORTPINS_H_ */ diff --git a/test/mocks/avr/sfr_defs.h b/test/mocks/avr/sfr_defs.h new file mode 100644 index 000000000..2f355721f --- /dev/null +++ b/test/mocks/avr/sfr_defs.h @@ -0,0 +1,269 @@ +/* Copyright (c) 2002, Marek Michalkiewicz + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of the copyright holders nor the names of + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. */ + +/* avr/sfr_defs.h - macros for accessing AVR special function registers */ + +/* $Id$ */ + +#ifndef _AVR_SFR_DEFS_H_ +#define _AVR_SFR_DEFS_H_ 1 + +/** \defgroup avr_sfr_notes Additional notes from + \ingroup avr_sfr + + The \c file is included by all of the \c + files, which use macros defined here to make the special function register + definitions look like C variables or simple constants, depending on the + _SFR_ASM_COMPAT define. Some examples from \c to + show how to define such macros: + +\code +#define PORTA _SFR_IO8(0x02) +#define EEAR _SFR_IO16(0x21) +#define UDR0 _SFR_MEM8(0xC6) +#define TCNT3 _SFR_MEM16(0x94) +#define CANIDT _SFR_MEM32(0xF0) +\endcode + + If \c _SFR_ASM_COMPAT is not defined, C programs can use names like + PORTA directly in C expressions (also on the left side of + assignment operators) and GCC will do the right thing (use short I/O + instructions if possible). The \c __SFR_OFFSET definition is not used in + any way in this case. + + Define \c _SFR_ASM_COMPAT as 1 to make these names work as simple constants + (addresses of the I/O registers). This is necessary when included in + preprocessed assembler (*.S) source files, so it is done automatically if + \c __ASSEMBLER__ is defined. By default, all addresses are defined as if + they were memory addresses (used in \c lds/sts instructions). To use these + addresses in \c in/out instructions, you must subtract 0x20 from them. + + For more backwards compatibility, insert the following at the start of your + old assembler source file: + +\code +#define __SFR_OFFSET 0 +\endcode + + This automatically subtracts 0x20 from I/O space addresses, but it's a + hack, so it is recommended to change your source: wrap such addresses in + macros defined here, as shown below. After this is done, the + __SFR_OFFSET definition is no longer necessary and can be removed. + + Real example - this code could be used in a boot loader that is portable + between devices with \c SPMCR at different addresses. + +\verbatim +: #define SPMCR _SFR_IO8(0x37) +: #define SPMCR _SFR_MEM8(0x68) +\endverbatim + +\code +#if _SFR_IO_REG_P(SPMCR) + out _SFR_IO_ADDR(SPMCR), r24 +#else + sts _SFR_MEM_ADDR(SPMCR), r24 +#endif +\endcode + + You can use the \c in/out/cbi/sbi/sbic/sbis instructions, without the + _SFR_IO_REG_P test, if you know that the register is in the I/O + space (as with \c SREG, for example). If it isn't, the assembler will + complain (I/O address out of range 0...0x3f), so this should be fairly + safe. + + If you do not define \c __SFR_OFFSET (so it will be 0x20 by default), all + special register addresses are defined as memory addresses (so \c SREG is + 0x5f), and (if code size and speed are not important, and you don't like + the ugly \#if above) you can always use lds/sts to access them. But, this + will not work if __SFR_OFFSET != 0x20, so use a different macro + (defined only if __SFR_OFFSET == 0x20) for safety: + +\code + sts _SFR_ADDR(SPMCR), r24 +\endcode + + In C programs, all 3 combinations of \c _SFR_ASM_COMPAT and + __SFR_OFFSET are supported - the \c _SFR_ADDR(SPMCR) macro can be + used to get the address of the \c SPMCR register (0x57 or 0x68 depending on + device). */ + +#ifdef __ASSEMBLER__ +#define _SFR_ASM_COMPAT 1 +#elif !defined(_SFR_ASM_COMPAT) +#define _SFR_ASM_COMPAT 0 +#endif + +#ifndef __ASSEMBLER__ +/* These only work in C programs. */ +#include + +#define _MMIO_BYTE(mem_addr) (*(volatile uint8_t *)(mem_addr)) +#define _MMIO_WORD(mem_addr) (*(volatile uint16_t *)(mem_addr)) +#define _MMIO_DWORD(mem_addr) (*(volatile uint32_t *)(mem_addr)) +#endif + +#if _SFR_ASM_COMPAT + +#ifndef __SFR_OFFSET +/* Define as 0 before including this file for compatibility with old asm + sources that don't subtract __SFR_OFFSET from symbolic I/O addresses. */ +# if __AVR_ARCH__ >= 100 +# define __SFR_OFFSET 0x00 +# else +# define __SFR_OFFSET 0x20 +# endif +#endif + +#if (__SFR_OFFSET != 0) && (__SFR_OFFSET != 0x20) +#error "__SFR_OFFSET must be 0 or 0x20" +#endif + +#define _SFR_MEM8(mem_addr) (mem_addr) +#define _SFR_MEM16(mem_addr) (mem_addr) +#define _SFR_MEM32(mem_addr) (mem_addr) +#define _SFR_IO8(io_addr) ((io_addr) + __SFR_OFFSET) +#define _SFR_IO16(io_addr) ((io_addr) + __SFR_OFFSET) + +#define _SFR_IO_ADDR(sfr) ((sfr) - __SFR_OFFSET) +#define _SFR_MEM_ADDR(sfr) (sfr) +#define _SFR_IO_REG_P(sfr) ((sfr) < 0x40 + __SFR_OFFSET) + +#if (__SFR_OFFSET == 0x20) +/* No need to use ?: operator, so works in assembler too. */ +#define _SFR_ADDR(sfr) _SFR_MEM_ADDR(sfr) +#elif !defined(__ASSEMBLER__) +#define _SFR_ADDR(sfr) (_SFR_IO_REG_P(sfr) ? (_SFR_IO_ADDR(sfr) + 0x20) : _SFR_MEM_ADDR(sfr)) +#endif + +#else /* !_SFR_ASM_COMPAT */ + +#ifndef __SFR_OFFSET +# if __AVR_ARCH__ >= 100 +# define __SFR_OFFSET 0x00 +# else +# define __SFR_OFFSET 0x20 +# endif +#endif + +#define _SFR_MEM8(mem_addr) _MMIO_BYTE(mem_addr) +#define _SFR_MEM16(mem_addr) _MMIO_WORD(mem_addr) +#define _SFR_MEM32(mem_addr) _MMIO_DWORD(mem_addr) +#define _SFR_IO8(io_addr) _MMIO_BYTE((io_addr) + __SFR_OFFSET) +#define _SFR_IO16(io_addr) _MMIO_WORD((io_addr) + __SFR_OFFSET) + +#define _SFR_MEM_ADDR(sfr) ((uint16_t) &(sfr)) +#define _SFR_IO_ADDR(sfr) (_SFR_MEM_ADDR(sfr) - __SFR_OFFSET) +#define _SFR_IO_REG_P(sfr) (_SFR_MEM_ADDR(sfr) < 0x40 + __SFR_OFFSET) + +#define _SFR_ADDR(sfr) _SFR_MEM_ADDR(sfr) + +#endif /* !_SFR_ASM_COMPAT */ + +#define _SFR_BYTE(sfr) _MMIO_BYTE(_SFR_ADDR(sfr)) +#define _SFR_WORD(sfr) _MMIO_WORD(_SFR_ADDR(sfr)) +#define _SFR_DWORD(sfr) _MMIO_DWORD(_SFR_ADDR(sfr)) + +/** \name Bit manipulation */ + +/*@{*/ +/** \def _BV + \ingroup avr_sfr + + \code #include \endcode + + Converts a bit number into a byte value. + + \note The bit shift is performed by the compiler which then inserts the + result into the code. Thus, there is no run-time overhead when using + _BV(). */ + +#define _BV(bit) (1 << (bit)) + +/*@}*/ + +#ifndef _VECTOR +#define _VECTOR(N) __vector_ ## N +#endif + +#ifndef __ASSEMBLER__ + + +/** \name IO register bit manipulation */ + +/*@{*/ + + + +/** \def bit_is_set + \ingroup avr_sfr + + \code #include \endcode + + Test whether bit \c bit in IO register \c sfr is set. + This will return a 0 if the bit is clear, and non-zero + if the bit is set. */ + +#define bit_is_set(sfr, bit) (_SFR_BYTE(sfr) & _BV(bit)) + +/** \def bit_is_clear + \ingroup avr_sfr + + \code #include \endcode + + Test whether bit \c bit in IO register \c sfr is clear. + This will return non-zero if the bit is clear, and a 0 + if the bit is set. */ + +#define bit_is_clear(sfr, bit) (!(_SFR_BYTE(sfr) & _BV(bit))) + +/** \def loop_until_bit_is_set + \ingroup avr_sfr + + \code #include \endcode + + Wait until bit \c bit in IO register \c sfr is set. */ + +#define loop_until_bit_is_set(sfr, bit) do { } while (bit_is_clear(sfr, bit)) + +/** \def loop_until_bit_is_clear + \ingroup avr_sfr + + \code #include \endcode + + Wait until bit \c bit in IO register \c sfr is clear. */ + +#define loop_until_bit_is_clear(sfr, bit) do { } while (bit_is_set(sfr, bit)) + +/*@}*/ + +#endif /* !__ASSEMBLER__ */ + +#endif /* _SFR_DEFS_H_ */ diff --git a/test/mocks/avr/version.h b/test/mocks/avr/version.h new file mode 100644 index 000000000..9eb34a51d --- /dev/null +++ b/test/mocks/avr/version.h @@ -0,0 +1,90 @@ +/* Copyright (c) 2005, Joerg Wunsch -*- c -*- + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of the copyright holders nor the names of + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. */ + +/* $Id$ */ + +/** \defgroup avr_version : avr-libc version macros + \code #include \endcode + + This header file defines macros that contain version numbers and + strings describing the current version of avr-libc. + + The version number itself basically consists of three pieces that + are separated by a dot: the major number, the minor number, and + the revision number. For development versions (which use an odd + minor number), the string representation additionally gets the + date code (YYYYMMDD) appended. + + This file will also be included by \c . That way, + portable tests can be implemented using \c that can be + used in code that wants to remain backwards-compatible to library + versions prior to the date when the library version API had been + added, as referenced but undefined C preprocessor macros + automatically evaluate to 0. +*/ + +#ifndef _AVR_VERSION_H_ +#define _AVR_VERSION_H_ + +/** \ingroup avr_version + String literal representation of the current library version. */ +#define __AVR_LIBC_VERSION_STRING__ "@AVR_LIBC_VERSION@" + +/** \ingroup avr_version + Numerical representation of the current library version. + + In the numerical representation, the major number is multiplied by + 10000, the minor number by 100, and all three parts are then + added. It is intented to provide a monotonically increasing + numerical value that can easily be used in numerical checks. + */ +#define __AVR_LIBC_VERSION__ @AVR_LIBC_VERSION_NUMERIC@UL + +/** \ingroup avr_version + String literal representation of the release date. */ +#define __AVR_LIBC_DATE_STRING__ "@AVR_LIBC_RELDATE@" + +/** \ingroup avr_version + Numerical representation of the release date. */ +#define __AVR_LIBC_DATE_ @AVR_LIBC_RELDATE@UL + +/** \ingroup avr_version + Library major version number. */ +#define __AVR_LIBC_MAJOR__ @AVR_LIBC_MAJOR@ + +/** \ingroup avr_version + Library minor version number. */ +#define __AVR_LIBC_MINOR__ @AVR_LIBC_MINOR@ + +/** \ingroup avr_version + Library revision number. */ +#define __AVR_LIBC_REVISION__ @AVR_LIBC_REVISION@ + +#endif /* _AVR_VERSION_H_ */ diff --git a/test/mocks/beeper_mock.cpp b/test/mocks/beeper_mock.cpp index 1e5c9923e..46edf683c 100644 --- a/test/mocks/beeper_mock.cpp +++ b/test/mocks/beeper_mock.cpp @@ -43,14 +43,9 @@ void Beeper::init(bool enabled) { gBeeperMock->init(enabled); } -bool Beeper::enabled() { +void Beeper::update() { assert(gBeeperMock != nullptr); - return gBeeperMock->enabled(); -} - -BeepState Beeper::getState() { - assert(gBeeperMock != nullptr); - return gBeeperMock->getState(); + gBeeperMock->update(); } void Beeper::ready() { @@ -68,7 +63,12 @@ void Beeper::endWork() { gBeeperMock->endWork(); } -void Beeper::schedule() { +BeepState Beeper::getState() { assert(gBeeperMock != nullptr); - gBeeperMock->schedule(); + return gBeeperMock->getState(); +} + +bool Beeper::enabled() { + assert(gBeeperMock != nullptr); + return gBeeperMock->enabled(); } diff --git a/test/mocks/beeper_mock.h b/test/mocks/beeper_mock.h index ad1f8dd5a..cae881d40 100644 --- a/test/mocks/beeper_mock.h +++ b/test/mocks/beeper_mock.h @@ -17,7 +17,7 @@ * along with AYAB. If not, see . * * Original Work Copyright 2013 Christian Obersteiner, Andreas Müller - * Modified Work Copyright 2020 Sturla Lange, Tom Price + * Modified Work Copyright 2020-3 Sturla Lange, Tom Price * http://ayab-knitting.com */ @@ -31,12 +31,12 @@ class BeeperMock : public BeeperInterface { public: MOCK_METHOD1(init, void(bool)); - MOCK_METHOD0(enabled, bool()); - MOCK_METHOD0(getState, BeepState()); + MOCK_METHOD0(update, void()); MOCK_METHOD0(ready, void()); MOCK_METHOD0(finishedLine, void()); MOCK_METHOD0(endWork, void()); - MOCK_METHOD0(schedule, void()); + MOCK_METHOD0(getState, BeepState()); + MOCK_METHOD0(enabled, bool()); }; BeeperMock *beeperMockInstance(); diff --git a/test/mocks/com_mock.cpp b/test/mocks/com_mock.cpp index fcc677263..9408d730d 100644 --- a/test/mocks/com_mock.cpp +++ b/test/mocks/com_mock.cpp @@ -50,17 +50,22 @@ void Com::update() { gComMock->update(); } +uint8_t Com::CRC8(const uint8_t *buffer, size_t len) const { + assert(gComMock != nullptr); + return gComMock->CRC8(buffer, len); +} + void Com::send(uint8_t *payload, size_t length) const { assert(gComMock != nullptr); gComMock->send(payload, length); } -void Com::sendMsg(AYAB_API_t id, const char *msg) { +void Com::sendMsg(API_t id, const char *msg) { assert(gComMock != nullptr); gComMock->sendMsg(id, msg); } -void Com::sendMsg(AYAB_API_t id, char *msg) { +void Com::sendMsg(API_t id, char *msg) { assert(gComMock != nullptr); gComMock->sendMsg(id, msg); } @@ -70,13 +75,47 @@ void Com::send_reqLine(const uint8_t lineNumber, Err_t error) const { gComMock->send_reqLine(lineNumber, error); } -void Com::send_indState(Carriage_t carriage, uint8_t position, - Err_t error) const { +void Com::send_indState(Err_t error) const { assert(gComMock != nullptr); - gComMock->send_indState(carriage, position, error); + gComMock->send_indState(error); } void Com::onPacketReceived(const uint8_t *buffer, size_t size) { assert(gComMock != nullptr); gComMock->onPacketReceived(buffer, size); } + +void Com::h_reqInit(const uint8_t *buffer, size_t size) { + assert(gComMock != nullptr); + gComMock->h_reqInit(buffer, size); +} + +void Com::h_reqStart(const uint8_t *buffer, size_t size) { + assert(gComMock != nullptr); + gComMock->h_reqStart(buffer, size); +} + +void Com::h_cnfLine(const uint8_t *buffer, size_t size) { + assert(gComMock != nullptr); + gComMock->h_cnfLine(buffer, size); +} + +void Com::h_reqInfo() const { + assert(gComMock != nullptr); + gComMock->h_reqInfo(); +} + +void Com::h_reqTest() const { + assert(gComMock != nullptr); + gComMock->h_reqTest(); +} + +void Com::h_quitCmd() const { + assert(gComMock != nullptr); + gComMock->h_quitCmd(); +} + +void Com::h_unrecognized() const { + assert(gComMock != nullptr); + gComMock->h_unrecognized(); +} diff --git a/test/mocks/com_mock.h b/test/mocks/com_mock.h index bf4016631..d68e23949 100644 --- a/test/mocks/com_mock.h +++ b/test/mocks/com_mock.h @@ -17,7 +17,7 @@ * along with AYAB. If not, see . * * Original Work Copyright 2013 Christian Obersteiner, Andreas Müller - * Modified Work Copyright 2020 Sturla Lange, Tom Price + * Modified Work Copyright 2020-3 Sturla Lange, Tom Price * http://ayab-knitting.com */ @@ -32,13 +32,21 @@ class ComMock : public ComInterface { public: MOCK_METHOD0(init, void()); MOCK_METHOD0(update, void()); + MOCK_CONST_METHOD2(CRC8, uint8_t(const uint8_t *buffer, size_t len)); MOCK_CONST_METHOD2(send, void(uint8_t *payload, size_t length)); - MOCK_METHOD2(sendMsg, void(AYAB_API_t id, const char *msg)); - MOCK_METHOD2(sendMsg, void(AYAB_API_t id, char *msg)); + MOCK_METHOD2(sendMsg, void(API_t id, const char *msg)); + MOCK_METHOD2(sendMsg, void(API_t id, char *msg)); MOCK_CONST_METHOD2(send_reqLine, void(const uint8_t lineNumber, Err_t error)); - MOCK_CONST_METHOD3(send_indState, void(Carriage_t carriage, uint8_t position, - Err_t error)); + MOCK_CONST_METHOD1(send_indState, void(Err_t error)); MOCK_METHOD2(onPacketReceived, void(const uint8_t *buffer, size_t size)); + + MOCK_METHOD2(h_reqInit, void(const uint8_t *buffer, size_t size)); + MOCK_METHOD2(h_reqStart, void(const uint8_t *buffer, size_t size)); + MOCK_METHOD2(h_cnfLine, void(const uint8_t *buffer, size_t size)); + MOCK_CONST_METHOD0(h_reqInfo, void()); + MOCK_CONST_METHOD0(h_reqTest, void()); + MOCK_CONST_METHOD0(h_quitCmd, void()); + MOCK_CONST_METHOD0(h_unrecognized, void()); }; ComMock *comMockInstance(); diff --git a/test/mocks/controller_mock.cpp b/test/mocks/controller_mock.cpp new file mode 100644 index 000000000..886fb018a --- /dev/null +++ b/test/mocks/controller_mock.cpp @@ -0,0 +1,101 @@ +/*!` + * \file controller_mock.cpp + * + * This file is part of AYAB. + * + * AYAB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * AYAB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with AYAB. If not, see . + * + * Original Work Copyright 2013 Christian Obersteiner, Andreas Müller + * Modified Work Copyright 2020-3 Sturla Lange, Tom Price + * http://ayab-knitting.com + */ + +#include +#include + +static ControllerMock *gControllerMock = nullptr; + +ControllerMock *controllerMockInstance() { + if (!gControllerMock) { + gControllerMock = new ControllerMock(); + } + return gControllerMock; +} + +void releaseControllerMock() { + if (gControllerMock) { + delete gControllerMock; + gControllerMock = nullptr; + } +} + +void Controller::init() { + assert(gControllerMock != nullptr); + gControllerMock->init(); +} + +void Controller::update() { + assert(gControllerMock != nullptr); + gControllerMock->update(); +} + +void Controller::cacheEncoders() { + assert(gControllerMock != nullptr); + gControllerMock->cacheEncoders(); +} + +void Controller::setState(OpInterface *state) { + assert(gControllerMock != nullptr); + gControllerMock->setState(state); +} + +OpInterface *Controller::getState() { + assert(gControllerMock != nullptr); + return gControllerMock->getState(); +} + +void Controller::setMachineType(Machine_t machineType) { + assert(gControllerMock != nullptr); + gControllerMock->setMachineType(machineType); +} + +Machine_t Controller::getMachineType() { + assert(gControllerMock != nullptr); + return gControllerMock->getMachineType(); +} + +BeltShift_t Controller::getBeltShift() { + assert(gControllerMock != nullptr); + return gControllerMock->getBeltShift(); +} + +Carriage_t Controller::getCarriage() { + assert(gControllerMock != nullptr); + return gControllerMock->getCarriage(); +} + +Direction_t Controller::getDirection() { + assert(gControllerMock != nullptr); + return gControllerMock->getDirection(); +} + +Direction_t Controller::getHallActive() { + assert(gControllerMock != nullptr); + return gControllerMock->getHallActive(); +} + +uint8_t Controller::getPosition() { + assert(gControllerMock != nullptr); + return gControllerMock->getPosition(); +} diff --git a/test/mocks/controller_mock.h b/test/mocks/controller_mock.h new file mode 100644 index 000000000..1afd15e47 --- /dev/null +++ b/test/mocks/controller_mock.h @@ -0,0 +1,51 @@ +/*!` + * \file controller_mock.h + * + * This file is part of AYAB. + * + * AYAB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * AYAB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with AYAB. If not, see . + * + * Original Work Copyright 2013 Christian Obersteiner, Andreas Müller + * Modified Work Copyright 2020-3 Sturla Lange, Tom Price + * http://ayab-knitting.com + */ + +#ifndef CONTROLLER_MOCK_H_ +#define CONTROLLER_MOCK_H_ + +#include + +#include +#include + +class ControllerMock : public ControllerInterface { +public: + MOCK_METHOD0(init, void()); + MOCK_METHOD0(update, void()); + MOCK_METHOD0(cacheEncoders, void()); + MOCK_METHOD1(setState, void(OpInterface *state)); + MOCK_METHOD0(getState, OpInterface*()); + MOCK_METHOD1(setMachineType, void(Machine_t machineType)); + MOCK_METHOD0(getMachineType, Machine_t()); + MOCK_METHOD0(getBeltShift, BeltShift_t()); + MOCK_METHOD0(getCarriage, Carriage_t()); + MOCK_METHOD0(getDirection, Direction_t()); + MOCK_METHOD0(getHallActive, Direction_t()); + MOCK_METHOD0(getPosition, uint8_t()); +}; + +ControllerMock *controllerMockInstance(); +void releaseControllerMock(); + +#endif // CONTROLLER_MOCK_H_ diff --git a/test/mocks/encoders_mock.cpp b/test/mocks/encoders_mock.cpp index 08b72397e..28922db80 100644 --- a/test/mocks/encoders_mock.cpp +++ b/test/mocks/encoders_mock.cpp @@ -44,14 +44,19 @@ void Encoders::init(Machine_t machineType) { return gEncodersMock->init(machineType); } -void Encoders::encA_interrupt() { +void Encoders::setUpInterrupt() { assert(gEncodersMock != nullptr); - gEncodersMock->encA_interrupt(); + gEncodersMock->setUpInterrupt(); } -uint8_t Encoders::getPosition() { +void Encoders::isr() { assert(gEncodersMock != nullptr); - return gEncodersMock->getPosition(); + gEncodersMock->isr(); +} + +uint16_t Encoders::getHallValue(Direction_t dir) { + assert(gEncodersMock != nullptr); + return gEncodersMock->getHallValue(dir); } BeltShift_t Encoders::getBeltShift() { @@ -64,11 +69,6 @@ Direction_t Encoders::getDirection() { return gEncodersMock->getDirection(); } -Direction_t Encoders::getHallActive() { - assert(gEncodersMock != nullptr); - return gEncodersMock->getHallActive(); -} - Carriage_t Encoders::getCarriage() { assert(gEncodersMock != nullptr); return gEncodersMock->getCarriage(); @@ -79,7 +79,13 @@ Machine_t Encoders::getMachineType() { return gEncodersMock->getMachineType(); } -uint16_t Encoders::getHallValue(Direction_t dir) { +Direction_t Encoders::getHallActive() { assert(gEncodersMock != nullptr); - return gEncodersMock->getHallValue(dir); + return gEncodersMock->getHallActive(); } + +uint8_t Encoders::getPosition() { + assert(gEncodersMock != nullptr); + return gEncodersMock->getPosition(); +} + diff --git a/test/mocks/encoders_mock.h b/test/mocks/encoders_mock.h index d46573442..a6b3391c7 100644 --- a/test/mocks/encoders_mock.h +++ b/test/mocks/encoders_mock.h @@ -17,7 +17,7 @@ * along with AYAB. If not, see . * * Original Work Copyright 2013 Christian Obersteiner, Andreas Müller - * Modified Work Copyright 2020 Sturla Lange, Tom Price + * Modified Work Copyright 2020-3 Sturla Lange, Tom Price * http://ayab-knitting.com */ @@ -30,14 +30,15 @@ class EncodersMock : public EncodersInterface { public: MOCK_METHOD1(init, void(Machine_t)); + MOCK_METHOD0(setUpInterrupt, void()); + MOCK_METHOD0(isr, void()); + MOCK_METHOD1(getHallValue, uint16_t(Direction_t)); MOCK_METHOD0(getBeltShift, BeltShift_t()); MOCK_METHOD0(getDirection, Direction_t()); MOCK_METHOD0(getCarriage, Carriage_t()); MOCK_METHOD0(getMachineType, Machine_t()); - MOCK_METHOD0(encA_interrupt, void()); - MOCK_METHOD0(getPosition, uint8_t()); MOCK_METHOD0(getHallActive, Direction_t()); - MOCK_METHOD1(getHallValue, uint16_t(Direction_t)); + MOCK_METHOD0(getPosition, uint8_t()); }; EncodersMock *encodersMockInstance(); diff --git a/test/mocks/knitter_mock.cpp b/test/mocks/knitter_mock.cpp deleted file mode 100644 index bd57fc179..000000000 --- a/test/mocks/knitter_mock.cpp +++ /dev/null @@ -1,113 +0,0 @@ -/*!` - * \file knitter_mock.cpp - * - * This file is part of AYAB. - * - * AYAB is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * AYAB is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with AYAB. If not, see . - * - * Original Work Copyright 2013 Christian Obersteiner, Andreas Müller - * Modified Work Copyright 2020-3 Sturla Lange, Tom Price - * http://ayab-knitting.com - */ - -#include -#include - -static KnitterMock *gKnitterMock = nullptr; -KnitterMock *knitterMockInstance() { - if (!gKnitterMock) { - gKnitterMock = new KnitterMock(); - } - return gKnitterMock; -} - -void releaseKnitterMock() { - if (gKnitterMock) { - delete gKnitterMock; - gKnitterMock = nullptr; - } -} - -void Knitter::init() { - assert(gKnitterMock != nullptr); - gKnitterMock->init(); -} - -void Knitter::setUpInterrupt() { - assert(gKnitterMock != nullptr); - gKnitterMock->setUpInterrupt(); -} - -void Knitter::isr() { - assert(gKnitterMock != nullptr); - gKnitterMock->isr(); -} - -Err_t Knitter::startKnitting(uint8_t startNeedle, - uint8_t stopNeedle, uint8_t *pattern_start, - bool continuousReportingEnabled) { - assert(gKnitterMock != nullptr); - return gKnitterMock->startKnitting(startNeedle, stopNeedle, - pattern_start, continuousReportingEnabled); -} - -Err_t Knitter::initMachine(Machine_t machineType) { - assert(gKnitterMock != nullptr); - return gKnitterMock->initMachine(machineType); -} - -void Knitter::encodePosition() { - assert(gKnitterMock != nullptr); - gKnitterMock->encodePosition(); -} - -bool Knitter::isReady() { - assert(gKnitterMock != nullptr); - return gKnitterMock->isReady(); -} - -void Knitter::knit() { - assert(gKnitterMock != nullptr); - gKnitterMock->knit(); -} - -void Knitter::indState(Err_t error) { - assert(gKnitterMock != nullptr); - gKnitterMock->indState(error); -} - -uint8_t Knitter::getStartOffset(const Direction_t direction) { - assert(gKnitterMock != nullptr); - return gKnitterMock->getStartOffset(direction); -} - -Machine_t Knitter::getMachineType() { - assert(gKnitterMock != nullptr); - return gKnitterMock->getMachineType(); -} - -bool Knitter::setNextLine(uint8_t lineNumber) { - assert(gKnitterMock != nullptr); - return gKnitterMock->setNextLine(lineNumber); -} - -void Knitter::setLastLine() { - assert(gKnitterMock != nullptr); - gKnitterMock->setLastLine(); -} - -void Knitter::setMachineType(Machine_t machineType) { - assert(gKnitterMock != nullptr); - return gKnitterMock->setMachineType(machineType); -} diff --git a/test/mocks/opError_mock.cpp b/test/mocks/opError_mock.cpp new file mode 100644 index 000000000..c057b13a9 --- /dev/null +++ b/test/mocks/opError_mock.cpp @@ -0,0 +1,70 @@ +/*!` + * \file opError_mock.cpp + * + * This file is part of AYAB. + * + * AYAB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * AYAB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with AYAB. If not, see . + * + * Original Work Copyright 2013 Christian Obersteiner, Andreas Müller + * Modified Work Copyright 2020-3 Sturla Lange, Tom Price + * http://ayab-knitting.com + */ + +#include + +static OpErrorMock *gOpErrorMock = nullptr; + +OpErrorMock *OpErrorMockInstance() { + if (!gOpErrorMock) { + gOpErrorMock = new OpErrorMock(); + } + return gOpErrorMock; +} + +void releaseOpErrorMock() { + if (gOpErrorMock) { + delete gOpErrorMock; + gOpErrorMock = nullptr; + } +} + +OpState_t OpError::state() { + assert(gOpErrorMock != nullptr); + return gOpErrorMock->state(); +} + +void OpError::init() { + assert(gOpErrorMock != nullptr); + gOpErrorMock->init(); +} + +void OpError::begin() { + assert(gOpErrorMock != nullptr); + return gOpErrorMock->begin(); +} + +void OpError::update() { + assert(gOpErrorMock != nullptr); + gOpErrorMock->update(); +} + +void OpError::com(const uint8_t *buffer, size_t size) { + assert(gOpErrorMock != nullptr); + gOpErrorMock->com(buffer, size); +} + +void OpError::end() { + assert(gOpErrorMock != nullptr); + gOpErrorMock->end(); +} diff --git a/test/mocks/opError_mock.h b/test/mocks/opError_mock.h new file mode 100644 index 000000000..991660643 --- /dev/null +++ b/test/mocks/opError_mock.h @@ -0,0 +1,43 @@ +/*!` + * \file opError_mock.h + * + * This file is part of AYAB. + * + * AYAB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * AYAB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with AYAB. If not, see . + * + * Original Work Copyright 2013 Christian Obersteiner, Andreas Müller + * Modified Work Copyright 2020-3 Sturla Lange, Tom Price + * http://ayab-knitting.com + */ + +#ifndef OP_ERROR_MOCK_H_ +#define OP_ERROR_MOCK_H_ + +#include +#include + +class OpErrorMock : public OpErrorInterface { +public: + MOCK_METHOD0(state, OpState_t()); + MOCK_METHOD0(init, void()); + MOCK_METHOD0(begin, void()); + MOCK_METHOD0(update, void()); + MOCK_METHOD2(com, void(const uint8_t *buffer, size_t size)); + MOCK_METHOD0(end, void()); +}; + +OpErrorMock *OpErrorMockInstance(); +void releaseOpErrorMock(); + +#endif // OP_ERROR_MOCK_H_ diff --git a/test/mocks/opIdle_mock.cpp b/test/mocks/opIdle_mock.cpp new file mode 100644 index 000000000..2e9055014 --- /dev/null +++ b/test/mocks/opIdle_mock.cpp @@ -0,0 +1,70 @@ +/*!` + * \file opIdle_mock.cpp + * + * This file is part of AYAB. + * + * AYAB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * AYAB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with AYAB. If not, see . + * + * Original Work Copyright 2013 Christian Obersteiner, Andreas Müller + * Modified Work Copyright 2020-3 Sturla Lange, Tom Price + * http://ayab-knitting.com + */ + +#include + +static OpIdleMock *gOpIdleMock = nullptr; + +OpIdleMock *OpIdleMockInstance() { + if (!gOpIdleMock) { + gOpIdleMock = new OpIdleMock(); + } + return gOpIdleMock; +} + +void releaseOpIdleMock() { + if (gOpIdleMock) { + delete gOpIdleMock; + gOpIdleMock = nullptr; + } +} + +OpState_t OpIdle::state() { + assert(gOpIdleMock != nullptr); + return gOpIdleMock->state(); +} + +void OpIdle::init() { + assert(gOpIdleMock != nullptr); + gOpIdleMock->init(); +} + +void OpIdle::begin() { + assert(gOpIdleMock != nullptr); + gOpIdleMock->begin(); +} + +void OpIdle::update() { + assert(gOpIdleMock != nullptr); + gOpIdleMock->update(); +} + +void OpIdle::com(const uint8_t *buffer, size_t size) { + assert(gOpIdleMock != nullptr); + gOpIdleMock->com(buffer, size); +} + +void OpIdle::end() { + assert(gOpIdleMock != nullptr); + gOpIdleMock->end(); +} diff --git a/test/mocks/fsm_mock.h b/test/mocks/opIdle_mock.h similarity index 64% rename from test/mocks/fsm_mock.h rename to test/mocks/opIdle_mock.h index 01492c013..80c63f0c0 100644 --- a/test/mocks/fsm_mock.h +++ b/test/mocks/opIdle_mock.h @@ -1,5 +1,5 @@ /*!` - * \file fsm_mock.h + * \file opIdle_mock.h * * This file is part of AYAB. * @@ -17,25 +17,27 @@ * along with AYAB. If not, see . * * Original Work Copyright 2013 Christian Obersteiner, Andreas Müller - * Modified Work Copyright 2020 Sturla Lange, Tom Price + * Modified Work Copyright 2020-3 Sturla Lange, Tom Price * http://ayab-knitting.com */ -#ifndef FSM_MOCK_H_ -#define FSM_MOCK_H_ +#ifndef OP_IDLE_MOCK_H_ +#define OP_IDLE_MOCK_H_ -#include #include +#include -class FsmMock : public FsmInterface { +class OpIdleMock : public OpIdleInterface { public: + MOCK_METHOD0(state, OpState_t()); MOCK_METHOD0(init, void()); - MOCK_METHOD0(getState, OpState_t()); - MOCK_METHOD1(setState, void(OpState_t state)); - MOCK_METHOD0(dispatch, void()); + MOCK_METHOD0(begin, void()); + MOCK_METHOD0(update, void()); + MOCK_METHOD2(com, void(const uint8_t *buffer, size_t size)); + MOCK_METHOD0(end, void()); }; -FsmMock *fsmMockInstance(); -void releaseFsmMock(); +OpIdleMock *OpIdleMockInstance(); +void releaseOpIdleMock(); -#endif // FSM_MOCK_H_ +#endif // OP_IDLE_MOCK_H_ diff --git a/test/mocks/opInit_mock.cpp b/test/mocks/opInit_mock.cpp new file mode 100644 index 000000000..49bb9afb8 --- /dev/null +++ b/test/mocks/opInit_mock.cpp @@ -0,0 +1,75 @@ +/*!` + * \file opInit_mock.cpp + * + * This file is part of AYAB. + * + * AYAB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * AYAB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with AYAB. If not, see . + * + * Original Work Copyright 2013 Christian Obersteiner, Andreas Müller + * Modified Work Copyright 2020-3 Sturla Lange, Tom Price + * http://ayab-knitting.com + */ + +#include + +static OpInitMock *gOpInitMock = nullptr; + +OpInitMock *OpInitMockInstance() { + if (!gOpInitMock) { + gOpInitMock = new OpInitMock(); + } + return gOpInitMock; +} + +void releaseOpInitMock() { + if (gOpInitMock) { + delete gOpInitMock; + gOpInitMock = nullptr; + } +} + +OpState_t OpInit::state() { + assert(gOpInitMock != nullptr); + return gOpInitMock->state(); +} + +void OpInit::init() { + assert(gOpInitMock != nullptr); + gOpInitMock->init(); +} + +void OpInit::begin() { + assert(gOpInitMock != nullptr); + gOpInitMock->begin(); +} + +void OpInit::update() { + assert(gOpInitMock != nullptr); + gOpInitMock->update(); +} + +void OpInit::com(const uint8_t *buffer, size_t size) { + assert(gOpInitMock != nullptr); + gOpInitMock->com(buffer, size); +} + +void OpInit::end() { + assert(gOpInitMock != nullptr); + gOpInitMock->end(); +} + +bool OpInit::isReady() { + assert(gOpInitMock != nullptr); + return gOpInitMock->isReady(); +} diff --git a/test/mocks/opInit_mock.h b/test/mocks/opInit_mock.h new file mode 100644 index 000000000..d37e0d957 --- /dev/null +++ b/test/mocks/opInit_mock.h @@ -0,0 +1,44 @@ +/*!` + * \file opInit_mock.h + * + * This file is part of AYAB. + * + * AYAB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * AYAB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with AYAB. If not, see . + * + * Original Work Copyright 2013 Christian Obersteiner, Andreas Müller + * Modified Work Copyright 2020-3 Sturla Lange, Tom Price + * http://ayab-knitting.com + */ + +#ifndef OP_INIT_MOCK_H_ +#define OP_INIT_MOCK_H_ + +#include +#include + +class OpInitMock : public OpInitInterface { +public: + MOCK_METHOD0(state, OpState_t()); + MOCK_METHOD0(init, void()); + MOCK_METHOD0(begin, void()); + MOCK_METHOD0(update, void()); + MOCK_METHOD2(com, void(const uint8_t *buffer, size_t size)); + MOCK_METHOD0(end, void()); + MOCK_METHOD0(isReady, bool()); +}; + +OpInitMock *OpInitMockInstance(); +void releaseOpInitMock(); + +#endif // OP_INIT_MOCK_H_ diff --git a/test/mocks/opKnit_mock.cpp b/test/mocks/opKnit_mock.cpp new file mode 100644 index 000000000..d2f1788cf --- /dev/null +++ b/test/mocks/opKnit_mock.cpp @@ -0,0 +1,102 @@ +/*!` + * \file opKnit_mock.cpp + * + * This file is part of AYAB. + * + * AYAB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * AYAB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with AYAB. If not, see . + * + * Original Work Copyright 2013 Christian Obersteiner, Andreas Müller + * Modified Work Copyright 2020-3 Sturla Lange, Tom Price + * http://ayab-knitting.com + */ + +#include + +static OpKnitMock *gOpKnitMock = nullptr; +OpKnitMock *OpKnitMockInstance() { + if (!gOpKnitMock) { + gOpKnitMock = new OpKnitMock(); + } + return gOpKnitMock; +} + +void releaseOpKnitMock() { + if (gOpKnitMock) { + delete gOpKnitMock; + gOpKnitMock = nullptr; + } +} + +OpState_t OpKnit::state() { + assert(gOpKnitMock != nullptr); + return gOpKnitMock->state(); +} + +void OpKnit::init() { + assert(gOpKnitMock != nullptr); + gOpKnitMock->init(); +} + +void OpKnit::begin() { + assert(gOpKnitMock != nullptr); + return gOpKnitMock->begin(); +} + +void OpKnit::update() { + assert(gOpKnitMock != nullptr); + gOpKnitMock->update(); +} + +void OpKnit::com(const uint8_t *buffer, size_t size) { + assert(gOpKnitMock != nullptr); + gOpKnitMock->com(buffer, size); +} + +void OpKnit::end() { + assert(gOpKnitMock != nullptr); + gOpKnitMock->end(); +} + +Err_t OpKnit::startKnitting(uint8_t startNeedle, + uint8_t stopNeedle, uint8_t *pattern_start, + bool continuousReportingEnabled) { + assert(gOpKnitMock != nullptr); + return gOpKnitMock->startKnitting(startNeedle, stopNeedle, + pattern_start, continuousReportingEnabled); +} + +void OpKnit::encodePosition() { + assert(gOpKnitMock != nullptr); + gOpKnitMock->encodePosition(); +} + +void OpKnit::knit() { + assert(gOpKnitMock != nullptr); + gOpKnitMock->knit(); +} + +uint8_t OpKnit::getStartOffset(const Direction_t direction) { + assert(gOpKnitMock != nullptr); + return gOpKnitMock->getStartOffset(direction); +} + +bool OpKnit::setNextLine(uint8_t lineNumber) { + assert(gOpKnitMock != nullptr); + return gOpKnitMock->setNextLine(lineNumber); +} + +void OpKnit::setLastLine() { + assert(gOpKnitMock != nullptr); + gOpKnitMock->setLastLine(); +} diff --git a/test/mocks/knitter_mock.h b/test/mocks/opKnit_mock.h similarity index 69% rename from test/mocks/knitter_mock.h rename to test/mocks/opKnit_mock.h index 10469706b..fc702187a 100644 --- a/test/mocks/knitter_mock.h +++ b/test/mocks/opKnit_mock.h @@ -1,5 +1,5 @@ /*!` - * \file knitter_mock.h + * \file opKnit_mock.h * * This file is part of AYAB. * @@ -17,37 +17,38 @@ * along with AYAB. If not, see . * * Original Work Copyright 2013 Christian Obersteiner, Andreas Müller - * Modified Work Copyright 2020 Sturla Lange, Tom Price + * Modified Work Copyright 2020-3 Sturla Lange, Tom Price * http://ayab-knitting.com */ -#ifndef KNITTER_MOCK_H_ -#define KNITTER_MOCK_H_ +#ifndef OP_KNIT_MOCK_H_ +#define OP_KNIT_MOCK_H_ #include -#include -class KnitterMock : public KnitterInterface { +#include +#include + +class OpKnitMock : public OpKnitInterface { public: + MOCK_METHOD0(state, OpState_t()); MOCK_METHOD0(init, void()); - MOCK_METHOD0(setUpInterrupt, void()); - MOCK_METHOD0(isr, void()); + MOCK_METHOD0(begin, void()); + MOCK_METHOD0(update, void()); + MOCK_METHOD2(com, void(const uint8_t *buffer, size_t size)); + MOCK_METHOD0(end, void()); + MOCK_METHOD4(startKnitting, Err_t(uint8_t startNeedle, uint8_t stopNeedle, uint8_t *pattern_start, bool continuousReportingEnabled)); - MOCK_METHOD1(initMachine, Err_t(Machine_t machineType)); MOCK_METHOD0(encodePosition, void()); - MOCK_METHOD0(isReady, bool()); MOCK_METHOD0(knit, void()); - MOCK_METHOD1(indState, void(Err_t error)); MOCK_METHOD1(getStartOffset, uint8_t(const Direction_t direction)); - MOCK_METHOD0(getMachineType, Machine_t()); MOCK_METHOD1(setNextLine, bool(uint8_t lineNumber)); MOCK_METHOD0(setLastLine, void()); - MOCK_METHOD1(setMachineType, void(Machine_t)); }; -KnitterMock *knitterMockInstance(); -void releaseKnitterMock(); +OpKnitMock *OpKnitMockInstance(); +void releaseOpKnitMock(); -#endif // KNITTER_MOCK_H_ +#endif // OP_KNIT_MOCK_H_ diff --git a/test/mocks/opReady_mock.cpp b/test/mocks/opReady_mock.cpp new file mode 100644 index 000000000..bea04bfe3 --- /dev/null +++ b/test/mocks/opReady_mock.cpp @@ -0,0 +1,70 @@ +/*!` + * \file opReady_mock.cpp + * + * This file is part of AYAB. + * + * AYAB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * AYAB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with AYAB. If not, see . + * + * Original Work Copyright 2013 Christian Obersteiner, Andreas Müller + * Modified Work Copyright 2020-3 Sturla Lange, Tom Price + * http://ayab-knitting.com + */ + +#include + +static OpReadyMock *gOpReadyMock = nullptr; + +OpReadyMock *OpReadyMockInstance() { + if (!gOpReadyMock) { + gOpReadyMock = new OpReadyMock(); + } + return gOpReadyMock; +} + +void releaseOpReadyMock() { + if (gOpReadyMock) { + delete gOpReadyMock; + gOpReadyMock = nullptr; + } +} + +OpState_t OpReady::state() { + assert(gOpReadyMock != nullptr); + return gOpReadyMock->state(); +} + +void OpReady::init() { + assert(gOpReadyMock != nullptr); + gOpReadyMock->init(); +} + +void OpReady::begin() { + assert(gOpReadyMock != nullptr); + return gOpReadyMock->begin(); +} + +void OpReady::update() { + assert(gOpReadyMock != nullptr); + gOpReadyMock->update(); +} + +void OpReady::com(const uint8_t *buffer, size_t size) { + assert(gOpReadyMock != nullptr); + gOpReadyMock->com(buffer, size); +} + +void OpReady::end() { + assert(gOpReadyMock != nullptr); + gOpReadyMock->end(); +} diff --git a/test/mocks/opReady_mock.h b/test/mocks/opReady_mock.h new file mode 100644 index 000000000..46566f411 --- /dev/null +++ b/test/mocks/opReady_mock.h @@ -0,0 +1,43 @@ +/*!` + * \file opReady_mock.h + * + * This file is part of AYAB. + * + * AYAB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * AYAB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with AYAB. If not, see . + * + * Original Work Copyright 2013 Christian Obersteiner, Andreas Müller + * Modified Work Copyright 2020-3 Sturla Lange, Tom Price + * http://ayab-knitting.com + */ + +#ifndef OP_READY_MOCK_H_ +#define OP_READY_MOCK_H_ + +#include +#include + +class OpReadyMock : public OpReadyInterface { +public: + MOCK_METHOD0(state, OpState_t()); + MOCK_METHOD0(init, void()); + MOCK_METHOD0(begin, void()); + MOCK_METHOD0(update, void()); + MOCK_METHOD2(com, void(const uint8_t *buffer, size_t size)); + MOCK_METHOD0(end, void()); +}; + +OpReadyMock *opReadyMockInstance(); +void releaseOpReadyMock(); + +#endif // OP_READY_MOCK_H_ diff --git a/test/mocks/opTest_mock.cpp b/test/mocks/opTest_mock.cpp new file mode 100644 index 000000000..d98678817 --- /dev/null +++ b/test/mocks/opTest_mock.cpp @@ -0,0 +1,125 @@ +/*!` + * \file opTest_mock.cpp + * + * This file is part of AYAB. + * + * AYAB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * AYAB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with AYAB. If not, see . + * + * Original Work Copyright 2013 Christian Obersteiner, Andreas Müller + * Modified Work Copyright 2020-3 Sturla Lange, Tom Price + * http://ayab-knitting.com + */ + +#include + +static OpTestMock *gOpTestMock = nullptr; + +OpTestMock *OpTestMockInstance() { + if (!gOpTestMock) { + gOpTestMock = new OpTestMock(); + } + return gOpTestMock; +} + +void releaseOpTestMock() { + if (gOpTestMock) { + delete gOpTestMock; + gOpTestMock = nullptr; + } +} + +OpState_t OpTest::state() { + assert(gOpTestMock != nullptr); + return gOpTestMock->state(); +} + +void OpTest::init() { + assert(gOpTestMock != nullptr); + gOpTestMock->init(); +} + +void OpTest::begin() { + assert(gOpTestMock != nullptr); + return gOpTestMock->begin(); +} + +void OpTest::update() { + assert(gOpTestMock != nullptr); + gOpTestMock->update(); +} + +void OpTest::com(const uint8_t *buffer, size_t size) { + assert(gOpTestMock != nullptr); + gOpTestMock->com(buffer, size); +} + +void OpTest::end() { + assert(gOpTestMock != nullptr); + gOpTestMock->end(); +} + +bool OpTest::enabled() { + assert(gOpTestMock != nullptr); + return gOpTestMock->enabled(); +} + +void OpTest::helpCmd() { + assert(gOpTestMock != nullptr); + gOpTestMock->helpCmd(); +} + +void OpTest::sendCmd() { + assert(gOpTestMock != nullptr); + gOpTestMock->sendCmd(); +} + +void OpTest::beepCmd() { + assert(gOpTestMock != nullptr); + gOpTestMock->beepCmd(); +} + +void OpTest::setSingleCmd(const uint8_t *buffer, size_t size) { + assert(gOpTestMock != nullptr); + gOpTestMock->setSingleCmd(buffer, size); +} + +void OpTest::setAllCmd(const uint8_t *buffer, size_t size) { + assert(gOpTestMock != nullptr); + gOpTestMock->setAllCmd(buffer, size); +} + +void OpTest::readEOLsensorsCmd() { + assert(gOpTestMock != nullptr); + gOpTestMock->readEOLsensorsCmd(); +} + +void OpTest::readEncodersCmd() { + assert(gOpTestMock != nullptr); + gOpTestMock->readEncodersCmd(); +} + +void OpTest::autoReadCmd() { + assert(gOpTestMock != nullptr); + gOpTestMock->autoReadCmd(); +} + +void OpTest::autoTestCmd() { + assert(gOpTestMock != nullptr); + gOpTestMock->autoTestCmd(); +} + +void OpTest::stopCmd() { + assert(gOpTestMock != nullptr); + gOpTestMock->stopCmd(); +} diff --git a/test/mocks/tester_mock.h b/test/mocks/opTest_mock.h similarity index 70% rename from test/mocks/tester_mock.h rename to test/mocks/opTest_mock.h index 17ce5b20b..f5fed1950 100644 --- a/test/mocks/tester_mock.h +++ b/test/mocks/opTest_mock.h @@ -1,5 +1,5 @@ /*!` - * \file tester_mock.h + * \file opTest_mock.h * * This file is part of AYAB. * @@ -17,20 +17,26 @@ * along with AYAB. If not, see . * * Original Work Copyright 2013 Christian Obersteiner, Andreas Müller - * Modified Work Copyright 2020 Sturla Lange, Tom Price + * Modified Work Copyright 2020-3 Sturla Lange, Tom Price * http://ayab-knitting.com */ -#ifndef TESTER_MOCK_H_ -#define TESTER_MOCK_H_ +#ifndef OP_TEST_MOCK_H_ +#define OP_TEST_MOCK_H_ #include -#include +#include -class TesterMock : public TesterInterface { +class OpTestMock : public OpTestInterface { public: - MOCK_METHOD1(startTest, Err_t(Machine_t machineType)); - MOCK_METHOD0(loop, void()); + MOCK_METHOD0(state, OpState_t()); + MOCK_METHOD0(init, void()); + MOCK_METHOD0(begin, void()); + MOCK_METHOD0(update, void()); + MOCK_METHOD2(com, void(const uint8_t *buffer, size_t size)); + MOCK_METHOD0(end, void()); + + MOCK_METHOD0(enabled, bool()); MOCK_METHOD0(helpCmd, void()); MOCK_METHOD0(sendCmd, void()); MOCK_METHOD0(beepCmd, void()); @@ -41,10 +47,9 @@ class TesterMock : public TesterInterface { MOCK_METHOD0(autoReadCmd, void()); MOCK_METHOD0(autoTestCmd, void()); MOCK_METHOD0(stopCmd, void()); - MOCK_METHOD0(quitCmd, void()); }; -TesterMock *testerMockInstance(); -void releaseTesterMock(); +OpTestMock *OpTestMockInstance(); +void releaseOpTestMock(); -#endif // TESTER_MOCK_H_ +#endif // TEST_MOCK_H_ diff --git a/test/mocks/solenoids_mock.h b/test/mocks/solenoids_mock.h index 437c3965a..ed2ea2929 100644 --- a/test/mocks/solenoids_mock.h +++ b/test/mocks/solenoids_mock.h @@ -17,7 +17,7 @@ * along with AYAB. If not, see . * * Original Work Copyright 2013 Christian Obersteiner, Andreas Müller - * Modified Work Copyright 2020 Sturla Lange, Tom Price + * Modified Work Copyright 2020-3 Sturla Lange, Tom Price * http://ayab-knitting.com */ diff --git a/test/mocks/tester_mock.cpp b/test/mocks/tester_mock.cpp deleted file mode 100644 index ced0b5596..000000000 --- a/test/mocks/tester_mock.cpp +++ /dev/null @@ -1,105 +0,0 @@ -/*!` - * \file tester_mock.cpp - * - * This file is part of AYAB. - * - * AYAB is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * AYAB is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with AYAB. If not, see . - * - * Original Work Copyright 2013 Christian Obersteiner, Andreas Müller - * Modified Work Copyright 2020-3 Sturla Lange, Tom Price - * http://ayab-knitting.com - */ - -#include - -static TesterMock *gTesterMock = nullptr; - -TesterMock *testerMockInstance() { - if (!gTesterMock) { - gTesterMock = new TesterMock(); - } - return gTesterMock; -} - -void releaseTesterMock() { - if (gTesterMock) { - delete gTesterMock; - gTesterMock = nullptr; - } -} - -Err_t Tester::startTest(Machine_t machineType) { - assert(gTesterMock != nullptr); - return gTesterMock->startTest(machineType); -} - -void Tester::loop() { - assert(gTesterMock != nullptr); - gTesterMock->loop(); -} - -void Tester::helpCmd() { - assert(gTesterMock != nullptr); - gTesterMock->helpCmd(); -} - -void Tester::sendCmd() { - assert(gTesterMock != nullptr); - gTesterMock->sendCmd(); -} - -void Tester::beepCmd() { - assert(gTesterMock != nullptr); - gTesterMock->beepCmd(); -} - -void Tester::setSingleCmd(const uint8_t *buffer, size_t size) { - assert(gTesterMock != nullptr); - gTesterMock->setSingleCmd(buffer, size); -} - -void Tester::setAllCmd(const uint8_t *buffer, size_t size) { - assert(gTesterMock != nullptr); - gTesterMock->setAllCmd(buffer, size); -} - -void Tester::readEOLsensorsCmd() { - assert(gTesterMock != nullptr); - gTesterMock->readEOLsensorsCmd(); -} - -void Tester::readEncodersCmd() { - assert(gTesterMock != nullptr); - gTesterMock->readEncodersCmd(); -} - -void Tester::autoReadCmd() { - assert(gTesterMock != nullptr); - gTesterMock->autoReadCmd(); -} - -void Tester::autoTestCmd() { - assert(gTesterMock != nullptr); - gTesterMock->autoTestCmd(); -} - -void Tester::stopCmd() { - assert(gTesterMock != nullptr); - gTesterMock->stopCmd(); -} - -void Tester::quitCmd() { - assert(gTesterMock != nullptr); - gTesterMock->quitCmd(); -} diff --git a/test/mocks/util/atomic.h b/test/mocks/util/atomic.h new file mode 100644 index 000000000..86e454548 --- /dev/null +++ b/test/mocks/util/atomic.h @@ -0,0 +1,310 @@ +/* Copyright (c) 2007 Dean Camera + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of the copyright holders nor the names of + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ + +/* $Id$ */ + +#ifndef _UTIL_ATOMIC_H_ +#define _UTIL_ATOMIC_H_ 1 + +#include +#include + +#if !defined(__DOXYGEN__) +/* Internal helper functions. */ +static __inline__ uint8_t __iSeiRetVal(void) +{ + sei(); + return 1; +} + +static __inline__ uint8_t __iCliRetVal(void) +{ + cli(); + return 1; +} + +static __inline__ void __iSeiParam(const uint8_t *__s) +{ + sei(); + __asm__ volatile ("" ::: "memory"); + (void)__s; +} + +static __inline__ void __iCliParam(const uint8_t *__s) +{ + cli(); + __asm__ volatile ("" ::: "memory"); + (void)__s; +} + +static __inline__ void __iRestore(const uint8_t *__s) +{ + SREG = *__s; + __asm__ volatile ("" ::: "memory"); +} +#endif /* !__DOXYGEN__ */ + +/** \file */ +/** \defgroup util_atomic Atomically and Non-Atomically Executed Code Blocks + + \code + #include + \endcode + + \note The macros in this header file require the ISO/IEC 9899:1999 + ("ISO C99") feature of for loop variables that are declared inside + the for loop itself. For that reason, this header file can only + be used if the standard level of the compiler (option --std=) is + set to either \c c99 or \c gnu99. + + The macros in this header file deal with code blocks that are + guaranteed to be excuted Atomically or Non-Atmomically. The term + "Atomic" in this context refers to the unability of the respective + code to be interrupted. + + These macros operate via automatic manipulation of the Global + Interrupt Status (I) bit of the SREG register. Exit paths from + both block types are all managed automatically without the need + for special considerations, i. e. the interrupt status will be + restored to the same value it had when entering the respective + block (unless ATOMIC_FORCEON or NONATOMIC_FORCEOFF are used). + + A typical example that requires atomic access is a 16 (or more) + bit variable that is shared between the main execution path and an + ISR. While declaring such a variable as volatile ensures that the + compiler will not optimize accesses to it away, it does not + guarantee atomic access to it. Assuming the following example: + + \code +#include +#include +#include + +volatile uint16_t ctr; + +ISR(TIMER1_OVF_vect) +{ + ctr--; +} + +... +int +main(void) +{ + ... + ctr = 0x200; + start_timer(); + while (ctr != 0) + // wait + ; + ... +} + \endcode + + There is a chance where the main context will exit its wait loop + when the variable \c ctr just reached the value 0xFF. This happens + because the compiler cannot natively access a 16-bit variable + atomically in an 8-bit CPU. So the variable is for example at + 0x100, the compiler then tests the low byte for 0, which succeeds. + It then proceeds to test the high byte, but that moment the ISR + triggers, and the main context is interrupted. The ISR will + decrement the variable from 0x100 to 0xFF, and the main context + proceeds. It now tests the high byte of the variable which is + (now) also 0, so it concludes the variable has reached 0, and + terminates the loop. + + Using the macros from this header file, the above code can be + rewritten like: + + \code +#include +#include +#include +#include + +volatile uint16_t ctr; + +ISR(TIMER1_OVF_vect) +{ + ctr--; +} + +... +int +main(void) +{ + ... + ctr = 0x200; + start_timer(); + sei(); + uint16_t ctr_copy; + do + { + ATOMIC_BLOCK(ATOMIC_FORCEON) + { + ctr_copy = ctr; + } + } + while (ctr_copy != 0); + ... +} + \endcode + + This will install the appropriate interrupt protection before + accessing variable \c ctr, so it is guaranteed to be consistently + tested. If the global interrupt state were uncertain before + entering the ATOMIC_BLOCK, it should be executed with the + parameter ATOMIC_RESTORESTATE rather than ATOMIC_FORCEON. + + See \ref optim_code_reorder for things to be taken into account + with respect to compiler optimizations. +*/ + +/** \def ATOMIC_BLOCK(type) + \ingroup util_atomic + + Creates a block of code that is guaranteed to be executed + atomically. Upon entering the block the Global Interrupt Status + flag in SREG is disabled, and re-enabled upon exiting the block + from any exit path. + + Two possible macro parameters are permitted, ATOMIC_RESTORESTATE + and ATOMIC_FORCEON. +*/ +#if defined(__DOXYGEN__) +#define ATOMIC_BLOCK(type) +#else +#define ATOMIC_BLOCK(type) for ( type, __ToDo = __iCliRetVal(); \ + __ToDo ; __ToDo = 0 ) +#endif /* __DOXYGEN__ */ + +/** \def NONATOMIC_BLOCK(type) + \ingroup util_atomic + + Creates a block of code that is executed non-atomically. Upon + entering the block the Global Interrupt Status flag in SREG is + enabled, and disabled upon exiting the block from any exit + path. This is useful when nested inside ATOMIC_BLOCK sections, + allowing for non-atomic execution of small blocks of code while + maintaining the atomic access of the other sections of the parent + ATOMIC_BLOCK. + + Two possible macro parameters are permitted, + NONATOMIC_RESTORESTATE and NONATOMIC_FORCEOFF. +*/ +#if defined(__DOXYGEN__) +#define NONATOMIC_BLOCK(type) +#else +#define NONATOMIC_BLOCK(type) for ( type, __ToDo = __iSeiRetVal(); \ + __ToDo ; __ToDo = 0 ) +#endif /* __DOXYGEN__ */ + +/** \def ATOMIC_RESTORESTATE + \ingroup util_atomic + + This is a possible parameter for ATOMIC_BLOCK. When used, it will + cause the ATOMIC_BLOCK to restore the previous state of the SREG + register, saved before the Global Interrupt Status flag bit was + disabled. The net effect of this is to make the ATOMIC_BLOCK's + contents guaranteed atomic, without changing the state of the + Global Interrupt Status flag when execution of the block + completes. +*/ +#if defined(__DOXYGEN__) +#define ATOMIC_RESTORESTATE +#else +#define ATOMIC_RESTORESTATE uint8_t sreg_save \ + __attribute__((__cleanup__(__iRestore))) = SREG +#endif /* __DOXYGEN__ */ + +/** \def ATOMIC_FORCEON + \ingroup util_atomic + + This is a possible parameter for ATOMIC_BLOCK. When used, it will + cause the ATOMIC_BLOCK to force the state of the SREG register on + exit, enabling the Global Interrupt Status flag bit. This saves a + small amout of flash space, a register, and one or more processor + cycles, since the previous value of the SREG register does not need + to be saved at the start of the block. + + Care should be taken that ATOMIC_FORCEON is only used when it is + known that interrupts are enabled before the block's execution or + when the side effects of enabling global interrupts at the block's + completion are known and understood. +*/ +#if defined(__DOXYGEN__) +#define ATOMIC_FORCEON +#else +#define ATOMIC_FORCEON uint8_t sreg_save \ + __attribute__((__cleanup__(__iSeiParam))) = 0 +#endif /* __DOXYGEN__ */ + +/** \def NONATOMIC_RESTORESTATE + \ingroup util_atomic + + This is a possible parameter for NONATOMIC_BLOCK. When used, it + will cause the NONATOMIC_BLOCK to restore the previous state of + the SREG register, saved before the Global Interrupt Status flag + bit was enabled. The net effect of this is to make the + NONATOMIC_BLOCK's contents guaranteed non-atomic, without changing + the state of the Global Interrupt Status flag when execution of + the block completes. +*/ +#if defined(__DOXYGEN__) +#define NONATOMIC_RESTORESTATE +#else +#define NONATOMIC_RESTORESTATE uint8_t sreg_save \ + __attribute__((__cleanup__(__iRestore))) = SREG +#endif /* __DOXYGEN__ */ + +/** \def NONATOMIC_FORCEOFF + \ingroup util_atomic + + This is a possible parameter for NONATOMIC_BLOCK. When used, it + will cause the NONATOMIC_BLOCK to force the state of the SREG + register on exit, disabling the Global Interrupt Status flag + bit. This saves a small amout of flash space, a register, and one + or more processor cycles, since the previous value of the SREG + register does not need to be saved at the start of the block. + + Care should be taken that NONATOMIC_FORCEOFF is only used when it + is known that interrupts are disabled before the block's execution + or when the side effects of disabling global interrupts at the + block's completion are known and understood. +*/ +#if defined(__DOXYGEN__) +#define NONATOMIC_FORCEOFF +#else +#define NONATOMIC_FORCEOFF uint8_t sreg_save \ + __attribute__((__cleanup__(__iCliParam))) = 0 +#endif /* __DOXYGEN__ */ + +#endif diff --git a/test/test.sh b/test/test.sh index 6d17ce51c..9cd5aac9d 100755 --- a/test/test.sh +++ b/test/test.sh @@ -45,10 +45,22 @@ GTEST_COLOR=1 ctest $ctest_verbose --output-on-failure . cd ../.. -GCOVR_ARGS="--exclude-unreachable-branches --exclude-throw-branches \ +GCOVR_ARGS="--exclude-unreachable-branches \ + --exclude-throw-branches \ + --decisions \ --exclude-directories 'test/build/arduino_mock$' \ - -e test_* -e lib* -e src/ayab/global_knitter.cpp \ - -e src/ayab/global_fsm.cpp" + -e test_* -e lib* \ + -e src/ayab/global_OpIdle.cpp \ + -e src/ayab/global_OpInit.cpp \ + -e src/ayab/global_OpTest.cpp \ + -e src/ayab/global_OpReady.cpp \ + -e src/ayab/global_OpError.cpp \ + -e src/ayab/global_OpKnit.cpp \ + -e src/ayab/global_beeper.cpp \ + -e src/ayab/global_com.cpp \ + -e src/ayab/global_controller.cpp \ + -e src/ayab/global_encoders.cpp \ + -e src/ayab/global_solenoids.cpp" if [[ $sonar -eq 1 ]]; then gcovr -r . $GCOVR_ARGS --sonarqube ./test/build/coverage.xml diff --git a/test/test_OpError.cpp b/test/test_OpError.cpp new file mode 100644 index 000000000..e8d2cd20e --- /dev/null +++ b/test/test_OpError.cpp @@ -0,0 +1,131 @@ +/*!` + * \file test_OpError.cpp + * + * This file is part of AYAB. + * + * AYAB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * AYAB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with AYAB. If not, see . + * + * Original Work Copyright 2013 Christian Obersteiner, Andreas Müller + * Modified Work Copyright 2020-3 Sturla Lange, Tom Price + * http://ayab-knitting.com + */ + +#include + +#include + +#include +#include + +using ::testing::_; +using ::testing::An; +using ::testing::AtLeast; +using ::testing::Mock; +using ::testing::Return; + +extern OpError *opError; + +extern ControllerMock *controller; +extern OpKnitMock *opKnit; + +class OpErrorTest : public ::testing::Test { +protected: + void SetUp() override { + arduinoMock = arduinoMockInstance(); + serialMock = serialMockInstance(); + // serialCommandMock = serialCommandMockInstance(); + + // pointers to global instances + controllerMock = controller; + opKnitMock = opKnit; + + // The global instances do not get destroyed at the end of each test. + // Ordinarily the mock instance would be local and such behaviour would + // cause a memory leak. We must notify the test that this is not the case. + Mock::AllowLeak(controllerMock); + Mock::AllowLeak(opKnitMock); + } + + void TearDown() override { + releaseArduinoMock(); + releaseSerialMock(); + } + + ArduinoMock *arduinoMock; + SerialMock *serialMock; + ControllerMock *controllerMock; + OpKnitMock *opKnitMock; +}; + +TEST_F(OpErrorTest, test_state) { + ASSERT_EQ(opError->state(), OpState_t::Error); +} + +TEST_F(OpErrorTest, test_begin) { + EXPECT_CALL(*arduinoMock, millis); + opError->begin(); +} + +TEST_F(OpErrorTest, test_init) { + // no calls expected + opError->init(); +} + +TEST_F(OpErrorTest, test_com) { + // no calls expected + const uint8_t *buffer = {}; + opError->com(buffer, 0); +} + +TEST_F(OpErrorTest, test_end) { + EXPECT_CALL(*arduinoMock, digitalWrite(LED_PIN_A, LOW)); + EXPECT_CALL(*arduinoMock, digitalWrite(LED_PIN_B, LOW)); + EXPECT_CALL(*opKnitMock, init()); + opError->end(); +} + +TEST_F(OpErrorTest, test_update) { + EXPECT_CALL(*arduinoMock, millis).WillOnce(Return(0U)); + opError->begin(); + + // too soon to flash + EXPECT_CALL(*arduinoMock, millis).WillOnce(Return(FLASH_DELAY - 1)); + EXPECT_CALL(*arduinoMock, digitalWrite).Times(0); + opError->update(); + + // flash first time + EXPECT_CALL(*arduinoMock, millis).WillOnce(Return(FLASH_DELAY)); + EXPECT_CALL(*arduinoMock, digitalWrite(LED_PIN_A, LOW)); + EXPECT_CALL(*arduinoMock, digitalWrite(LED_PIN_B, HIGH)); + // send_indState + EXPECT_CALL(*controllerMock, getState).WillOnce(Return(opError)); + EXPECT_CALL(*controllerMock, getCarriage); + EXPECT_CALL(*controllerMock, getPosition); + EXPECT_CALL(*controllerMock, getDirection); + opError->update(); + + // alternate flash + EXPECT_CALL(*arduinoMock, millis).WillOnce(Return(FLASH_DELAY * 2)); + EXPECT_CALL(*arduinoMock, digitalWrite(LED_PIN_A, HIGH)); + EXPECT_CALL(*arduinoMock, digitalWrite(LED_PIN_B, LOW)); + // send_indState + EXPECT_CALL(*controllerMock, getState).WillOnce(Return(opError)); + EXPECT_CALL(*controllerMock, getCarriage); + EXPECT_CALL(*controllerMock, getPosition); + EXPECT_CALL(*controllerMock, getDirection); + opError->update(); + + // test expectations without destroying instance + ASSERT_TRUE(Mock::VerifyAndClear(controllerMock)); +} diff --git a/test/test_OpIdle.cpp b/test/test_OpIdle.cpp new file mode 100644 index 000000000..8d87b745b --- /dev/null +++ b/test/test_OpIdle.cpp @@ -0,0 +1,107 @@ +/*!` + * \file test_OpIdle.cpp + * + * This file is part of AYAB. + * + * AYAB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * AYAB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with AYAB. If not, see . + * + * Original Work Copyright 2013 Christian Obersteiner, Andreas Müller + * Modified Work Copyright 2020-3 Sturla Lange, Tom Price + * http://ayab-knitting.com + */ + +#include + +#include +#include + +#include +#include + +using ::testing::_; +using ::testing::An; +using ::testing::AtLeast; +using ::testing::Mock; +using ::testing::Return; + +extern OpIdle *opIdle; + +extern ControllerMock *controller; +extern OpKnitMock *opKnit; + +class OpIdleTest : public ::testing::Test { +protected: + void SetUp() override { + arduinoMock = arduinoMockInstance(); + serialMock = serialMockInstance(); + // serialCommandMock = serialCommandMockInstance(); + + // pointers to global instances + controllerMock = controller; + opKnitMock = opKnit; + + // The global instances do not get destroyed at the end of each test. + // Ordinarily the mock instance would be local and such behaviour would + // cause a memory leak. We must notify the test that this is not the case. + Mock::AllowLeak(controllerMock); + Mock::AllowLeak(opKnitMock); + } + + void TearDown() override { + releaseArduinoMock(); + releaseSerialMock(); + } + + ArduinoMock *arduinoMock; + ControllerMock *controllerMock; + SerialMock *serialMock; + OpKnitMock *opKnitMock; +}; + +TEST_F(OpIdleTest, test_state) { + ASSERT_EQ(opIdle->state(), OpState_t::Idle); +} + +TEST_F(OpIdleTest, test_begin) { + EXPECT_CALL(*arduinoMock, digitalWrite(LED_PIN_A, LOW)); + opIdle->begin(); +} + +TEST_F(OpIdleTest, test_init) { + // no calls expected + opIdle->init(); +} + +TEST_F(OpIdleTest, test_reqTest) { + // no calls expected + // can't enter state `OpTest` from state `OpIdle` + const uint8_t buffer[] = {static_cast(API_t::reqTest)}; + opIdle->com(buffer, 1); +} + +TEST_F(OpIdleTest, test_unrecognized) { + // no calls expected + const uint8_t buffer[] = {0xFF}; + opIdle->com(buffer, 1); +} + +TEST_F(OpIdleTest, test_update) { + // no calls expected + opIdle->update(); +} + +TEST_F(OpIdleTest, test_end) { + // no calls expected + opIdle->end(); +} diff --git a/test/test_OpInit.cpp b/test/test_OpInit.cpp new file mode 100644 index 000000000..ad592ca01 --- /dev/null +++ b/test/test_OpInit.cpp @@ -0,0 +1,224 @@ +/*!` + * \file test_OpInit.cpp + * + * This file is part of AYAB. + * + * AYAB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * AYAB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with AYAB. If not, see . + * + * Original Work Copyright 2013 Christian Obersteiner, Andreas Müller + * Modified Work Copyright 2020-3 Sturla Lange, Tom Price + * http://ayab-knitting.com + */ + +#include + +#include +#include +#include + +#include +#include + +using ::testing::_; +using ::testing::An; +using ::testing::AtLeast; +using ::testing::Mock; +using ::testing::Return; + +extern OpInit *opInit; +extern OpReady *opReady; +extern OpTest *opTest; + +extern ControllerMock *controller; +extern OpKnitMock *opKnit; + +class OpInitTest : public ::testing::Test { +protected: + void SetUp() override { + arduinoMock = arduinoMockInstance(); + serialMock = serialMockInstance(); + // serialCommandMock = serialCommandMockInstance(); + + // pointers to global instances + controllerMock = controller; + opKnitMock = opKnit; + + // The global instances do not get destroyed at the end of each test. + // Ordinarily the mock instance would be local and such behaviour would + // cause a memory leak. We must notify the test that this is not the case. + Mock::AllowLeak(controllerMock); + Mock::AllowLeak(opKnitMock); + } + + void TearDown() override { + releaseArduinoMock(); + releaseSerialMock(); + } + + ArduinoMock *arduinoMock; + SerialMock *serialMock; + ControllerMock *controllerMock; + OpKnitMock *opKnitMock; + + uint8_t get_position_past_left(Machine_t m) { + return (END_LEFT_PLUS_OFFSET[static_cast(m)] + GARTER_SLOP) + 1; + } + + uint8_t get_position_past_right(Machine_t m) { + return (END_RIGHT_MINUS_OFFSET[static_cast(m)] - GARTER_SLOP) - 1; + } + + void expect_update(uint16_t pos, Direction_t dir, Direction_t hall) { + EXPECT_CALL(*controllerMock, getPosition).WillRepeatedly(Return(pos)); + EXPECT_CALL(*controllerMock, getDirection).WillRepeatedly(Return(dir)); + EXPECT_CALL(*controllerMock, getHallActive).WillRepeatedly(Return(hall)); + EXPECT_CALL(*controllerMock, getMachineType).WillRepeatedly(Return(Machine_t::Kh910)); + EXPECT_CALL(*controllerMock, getState).WillRepeatedly(Return(opInit)); + } + + void expect_ready(bool ready) { + if (ready) { + EXPECT_CALL(*controllerMock, getState).WillOnce(Return(opInit)); + } + ASSERT_EQ(opInit->isReady(), ready); + } +}; + +TEST_F(OpInitTest, test_state) { + ASSERT_EQ(opInit->state(), OpState_t::Init); +} + +TEST_F(OpInitTest, test_init) { + // no calls expected + opInit->init(); + ASSERT_EQ(opInit->m_lastHall, Direction_t::NoDirection); +} + +TEST_F(OpInitTest, test_reqTest) { + EXPECT_CALL(*controllerMock, setState(opTest)); + const uint8_t buffer[] = {static_cast(API_t::reqTest)}; + opInit->com(buffer, 1); + + // test expectations without destroying instance + ASSERT_TRUE(Mock::VerifyAndClear(controllerMock)); +} + +TEST_F(OpInitTest, test_com_unrecognized) { + // no calls expected + const uint8_t buffer[] = {0xFF}; + opInit->com(buffer, 1); +} + +TEST_F(OpInitTest, test_end) { + // no calls expected + opInit->end(); +} + +TEST_F(OpInitTest, test_begin910) { + EXPECT_CALL(*controllerMock, getMachineType()); + EXPECT_CALL(*arduinoMock, digitalWrite(LED_PIN_A, LOW)); + opInit->begin(); + + // test expectations without destroying instance + ASSERT_TRUE(Mock::VerifyAndClear(controllerMock)); +} + +TEST_F(OpInitTest, test_updateF) { + // isReady() == false + expect_update(get_position_past_right(Machine_t::Kh910), Direction_t::Left, Direction_t::Left); + EXPECT_CALL(*controllerMock, getState).Times(0); + EXPECT_CALL(*controllerMock, setState(opReady)).Times(0); + opInit->update(); + + // test expectations without destroying instance + ASSERT_TRUE(Mock::VerifyAndClear(controllerMock)); +} + +TEST_F(OpInitTest, test_updateT) { + // isReady() == true + expect_update(get_position_past_left(Machine_t::Kh910), Direction_t::Right, Direction_t::Left); + EXPECT_CALL(*controllerMock, getState).WillOnce(Return(opInit)); + EXPECT_CALL(*controllerMock, setState(opReady)); + opInit->update(); + + // test expectations without destroying instance + ASSERT_TRUE(Mock::VerifyAndClear(controllerMock)); +} + +TEST_F(OpInitTest, test_op_init_RLL) { + // not ready + expect_update(get_position_past_right(Machine_t::Kh910), Direction_t::Left, Direction_t::Left); + expect_ready(false); + + // test expectations without destroying instance + ASSERT_TRUE(Mock::VerifyAndClear(controllerMock)); +} + +TEST_F(OpInitTest, test_op_init_LRR) { + // not ready + expect_update(get_position_past_left(Machine_t::Kh910), Direction_t::Right, Direction_t::Right); + expect_ready(false); + + // test expectations without destroying instance + ASSERT_TRUE(Mock::VerifyAndClear(controllerMock)); +} + +TEST_F(OpInitTest, test_op_init_LRL) { + // Machine is initialized when Left hall sensor + // is passed in Right direction inside active needles. + expect_update(get_position_past_left(Machine_t::Kh910), Direction_t::Right, Direction_t::Left); + expect_ready(true); + + // test expectations without destroying instance + ASSERT_TRUE(Mock::VerifyAndClear(controllerMock)); +} + +TEST_F(OpInitTest, test_op_init_XRL) { + // Machine is initialized when Left hall sensor + // is passed in Right direction inside active needles. + expect_update(get_position_past_left(Machine_t::Kh910) - 2, Direction_t::Right, Direction_t::Left); + expect_ready(false); + + // test expectations without destroying instance + ASSERT_TRUE(Mock::VerifyAndClear(controllerMock)); +} + +TEST_F(OpInitTest, test_op_init_XLR) { + // New feature (August 2020): the machine is also initialized + // when the right Hall sensor is passed in the Left direction. + expect_update(get_position_past_right(Machine_t::Kh910) + 2, Direction_t::Left, Direction_t::Right); + expect_ready(false); + + // test expectations without destroying instance + ASSERT_TRUE(Mock::VerifyAndClear(controllerMock)); +} + +TEST_F(OpInitTest, test_op_init_RLR) { + // New feature (August 2020): the machine is also initialized + // when the right Hall sensor is passed in the Left direction. + expect_update(get_position_past_right(Machine_t::Kh910), Direction_t::Left, Direction_t::Right); + expect_ready(true); + + // test expectations without destroying instance + ASSERT_TRUE(Mock::VerifyAndClear(controllerMock)); +} + +TEST_F(OpInitTest, test_op_init_RLN) { + // not ready + expect_update(get_position_past_right(Machine_t::Kh910), Direction_t::Left, Direction_t::NoDirection); + expect_ready(false); + + // test expectations without destroying instance + ASSERT_TRUE(Mock::VerifyAndClear(controllerMock)); +} diff --git a/test/test_OpKnit.cpp b/test/test_OpKnit.cpp new file mode 100644 index 000000000..c909e58cf --- /dev/null +++ b/test/test_OpKnit.cpp @@ -0,0 +1,755 @@ +/*!` + * \file test_OpKnit.cpp + * + * This file is part of AYAB. + * + * AYAB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * AYAB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with AYAB. If not, see . + * + * Original Work Copyright 2013 Christian Obersteiner, Andreas Müller + * Modified Work Copyright 2020-3 Sturla Lange, Tom Price + * http://ayab-knitting.com + */ + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +using ::testing::_; +using ::testing::AtLeast; +using ::testing::Mock; +using ::testing::Return; +using ::testing::TypedEq; + +extern OpKnit *opKnit; +extern Controller *controller; + +extern BeeperMock *beeper; +extern ComMock *com; +extern EncodersMock *encoders; +extern SolenoidsMock *solenoids; + +extern OpIdleMock *opIdle; +extern OpInitMock *opInit; +extern OpTestMock *opTest; +extern OpReadyMock *opReady; + +class OpKnitTest : public ::testing::Test { +protected: + void SetUp() override { + arduinoMock = arduinoMockInstance(); + + // pointers to global instances + beeperMock = beeper; + comMock = com; + encodersMock = encoders; + solenoidsMock = solenoids; + + opIdleMock = opIdle; + opInitMock = opInit; + opReadyMock = opReady; + opTestMock = opTest; + + // The global instances do not get destroyed at the end of each test. + // Ordinarily the mock instances would be local and such behaviour would + // cause a memory leak. We must notify the test that this is not the case. + Mock::AllowLeak(beeperMock); + Mock::AllowLeak(comMock); + Mock::AllowLeak(encodersMock); + Mock::AllowLeak(solenoidsMock); + + Mock::AllowLeak(opIdleMock); + Mock::AllowLeak(opInitMock); + Mock::AllowLeak(opReadyMock); + Mock::AllowLeak(opTestMock); + + // start in state `OpIdle` + controller->init(); + opIdle->init(); + opInit->init(); + expect_opKnit_init(); + opKnit->init(); + expected_cacheISR(Direction_t::NoDirection, Direction_t::NoDirection); + } + + void TearDown() override { + releaseArduinoMock(); + } + + ArduinoMock *arduinoMock; + BeeperMock *beeperMock; + ComMock *comMock; + EncodersMock *encodersMock; + SolenoidsMock *solenoidsMock; + + OpIdleMock *opIdleMock; + OpInitMock *opInitMock; + OpReadyMock *opReadyMock; + OpTestMock *opTestMock; + + uint8_t get_position_past_left(Machine_t m) { + return (END_LEFT_PLUS_OFFSET[static_cast(m)] + GARTER_SLOP) + 1; + } + + void expect_opKnit_init() { + EXPECT_CALL(*arduinoMock, pinMode(ENC_PIN_A, INPUT)); + EXPECT_CALL(*arduinoMock, pinMode(ENC_PIN_B, INPUT)); + EXPECT_CALL(*arduinoMock, pinMode(ENC_PIN_C, INPUT)); + + EXPECT_CALL(*arduinoMock, pinMode(LED_PIN_A, OUTPUT)); + EXPECT_CALL(*arduinoMock, pinMode(LED_PIN_B, OUTPUT)); + + EXPECT_CALL(*arduinoMock, digitalWrite(LED_PIN_A, HIGH)); // green LED on + EXPECT_CALL(*arduinoMock, digitalWrite(LED_PIN_B, HIGH)); // yellow LED on + + EXPECT_CALL(*solenoidsMock, init); + } + + void expect_cacheISR(uint16_t pos, Direction_t dir, Direction_t hall, + BeltShift_t belt, Carriage_t carriage) { + EXPECT_CALL(*encodersMock, getPosition).WillRepeatedly(Return(pos)); + EXPECT_CALL(*encodersMock, getDirection).WillRepeatedly(Return(dir)); + EXPECT_CALL(*encodersMock, getHallActive).WillRepeatedly(Return(hall)); + EXPECT_CALL(*encodersMock, getBeltShift).WillRepeatedly(Return(belt)); + EXPECT_CALL(*encodersMock, getCarriage).WillRepeatedly(Return(carriage)); + } + + void expected_cacheISR(uint16_t pos, Direction_t dir, Direction_t hall, + BeltShift_t belt, Carriage_t carriage) { + expect_cacheISR(pos, dir, hall, belt, carriage); + controller->cacheEncoders(); + } + + void expected_cacheISR(uint16_t pos, Direction_t dir, BeltShift_t belt, Carriage_t carriage) { + expect_cacheISR(pos, dir, Direction_t::NoDirection, belt, carriage); + controller->cacheEncoders(); + } + + void expect_cacheISR(Direction_t dir, Direction_t hall) { + expect_cacheISR(1, dir, hall, BeltShift::Regular, Carriage_t::Knit); + } + + void expected_cacheISR(uint8_t pos, Direction_t dir, Direction_t hall) { + expect_cacheISR(pos, dir, hall, BeltShift::Regular, Carriage_t::Knit); + controller->cacheEncoders(); + } + + void expected_cacheISR(Direction_t dir, Direction_t hall) { + expect_cacheISR(dir, hall); + controller->cacheEncoders(); + } + + void expect_cacheISR(uint16_t pos) { + expect_cacheISR(pos, Direction_t::Right, Direction_t::Left, BeltShift::Regular, Carriage_t::Garter); + } + + void expected_cacheISR(uint16_t pos) { + expect_cacheISR(pos); + controller->cacheEncoders(); + } + + void expected_cacheISR() { + expected_cacheISR(1); + } + + void expect_reqLine() { + EXPECT_CALL(*comMock, send_reqLine); + } + + void expect_indState() { + EXPECT_CALL(*comMock, send_indState); + } + + void expected_init_machine(Machine_t m) { + // starts in state `OpIdle` + ASSERT_EQ(controller->getState(), opIdle); + + controller->setMachineType(m); + controller->setState(opInit); + expected_update_idle(); + + // transition to state `OpInit` + ASSERT_EQ(controller->getState(), opInit); + } + + void expected_get_ready() { + controller->setState(opReady); + expected_update_init(); + } + + void get_to_ready(Machine_t m) { + expected_init_machine(m); + expected_get_ready(); + ASSERT_EQ(controller->getState(), opReady); + } + + void get_to_knit(Machine_t m) { + get_to_ready(m); + + uint8_t pattern[] = {1}; + EXPECT_CALL(*beeperMock, ready); + ASSERT_EQ(opKnit->startKnitting(0, NUM_NEEDLES[static_cast(m)] - 1, pattern, false), Err_t::Success); + ASSERT_EQ(controller->getState(), opReady); + + EXPECT_CALL(*encodersMock, init); + EXPECT_CALL(*encodersMock, setUpInterrupt); + expected_update_ready(); + + // ends in state `OpKnit` + ASSERT_EQ(controller->getState(), opKnit); + } + + void expected_update() { + controller->update(); + } + + void expected_update_knit(bool first) { + if (first) { + get_to_knit(Machine_t::Kh910); + expect_first_knit(); + //EXPECT_CALL(*arduinoMock, digitalWrite(LED_PIN_A, HIGH)); // green LED on + expected_update(); + return; + } + ASSERT_EQ(controller->getState(), opKnit); + //EXPECT_CALL(*arduinoMock, digitalWrite(LED_PIN_A, HIGH)); // green LED on + expected_update(); + } + + void expected_update_idle() { + // starts in state `OpIdle` + ASSERT_EQ(controller->getState(), opIdle); + + //EXPECT_CALL(*arduinoMock, digitalWrite(LED_PIN_A, LOW)); + expected_update(); + } + + void expected_update_init() { + // starts in state `OpInit` + ASSERT_EQ(controller->getState(), opInit); + + //EXPECT_CALL(*arduinoMock, digitalWrite(LED_PIN_A, LOW)); + expected_update(); + } + + void expected_update_ready() { + // starts in state `OpReady` + ASSERT_EQ(controller->getState(), opReady); + + //EXPECT_CALL(*arduinoMock, digitalWrite(LED_PIN_A, LOW)); + expected_update(); + } + + void expected_update_test() { + // starts in state `OpTest` + ASSERT_EQ(controller->getState(), opTest); + + //expect_indState(); + EXPECT_CALL(*opTestMock, update); + expected_update(); + } + + void expect_first_knit() { + EXPECT_CALL(*arduinoMock, delay(START_KNITTING_DELAY)); + EXPECT_CALL(*beeperMock, finishedLine); + expect_reqLine(); + } +}; + +TEST_F(OpKnitTest, test_state) { + ASSERT_EQ(opKnit->state(), OpState_t::Knit); +} + +TEST_F(OpKnitTest, test_send) { + uint8_t p[] = {1, 2, 3, 4, 5}; + EXPECT_CALL(*comMock, send); + comMock->send(p, 5); + + // test expectations without destroying instance + ASSERT_TRUE(Mock::VerifyAndClear(comMock)); +} + +TEST_F(OpKnitTest, test_com) { + const uint8_t cnf[] = {static_cast(API_t::cnfLine)}; + EXPECT_CALL(*comMock, h_cnfLine); + opKnit->com(cnf, 1); + + const uint8_t reqTest[] = {static_cast(API_t::reqTest)}; + EXPECT_CALL(*comMock, h_reqTest); + opKnit->com(reqTest, 1); + + const uint8_t unrec[] = {0xFF}; + EXPECT_CALL(*comMock, h_unrecognized); + opKnit->com(unrec, 1); + + // test expectations without destroying instance + ASSERT_TRUE(Mock::VerifyAndClear(comMock)); +} + +TEST_F(OpKnitTest, test_encodePosition) { + opKnit->m_sOldPosition = controller->getPosition(); + EXPECT_CALL(*comMock, send_indState).Times(0); + opKnit->encodePosition(); + + opKnit->m_sOldPosition += 1; + EXPECT_CALL(*comMock, send_indState).Times(1); + opKnit->encodePosition(); + + // test expectations without destroying instance + ASSERT_TRUE(Mock::VerifyAndClear(comMock)); +} + +TEST_F(OpKnitTest, test_cacheISR) { + expected_cacheISR(); + + // test expectations without destroying instance + ASSERT_TRUE(Mock::VerifyAndClear(encodersMock)); +} + +TEST_F(OpKnitTest, test_init_machine) { + expected_init_machine(Machine_t::Kh910); + + // test expectations without destroying instance + ASSERT_TRUE(Mock::VerifyAndClear(opIdleMock)); + ASSERT_TRUE(Mock::VerifyAndClear(opInitMock)); +} + +TEST_F(OpKnitTest, test_startKnitting_NoMachine) { + uint8_t pattern[] = {1}; + Machine_t m = controller->getMachineType(); + ASSERT_EQ(m, Machine_t::NoMachine); + + opKnit->begin(); + ASSERT_TRUE( + opKnit->startKnitting(0, NUM_NEEDLES[static_cast(m)] - 1, pattern, false) != Err_t::Success); +} + +TEST_F(OpKnitTest, test_startKnitting_Kh910) { + get_to_knit(Machine_t::Kh910); + + // test expectations without destroying instance + ASSERT_TRUE(Mock::VerifyAndClear(beeperMock)); + ASSERT_TRUE(Mock::VerifyAndClear(comMock)); + ASSERT_TRUE(Mock::VerifyAndClear(encodersMock)); + ASSERT_TRUE(Mock::VerifyAndClear(solenoidsMock)); + ASSERT_TRUE(Mock::VerifyAndClear(opInitMock)); +} + +TEST_F(OpKnitTest, test_startKnitting_Kh270) { + get_to_knit(Machine_t::Kh270); + + // test expectations without destroying instance + ASSERT_TRUE(Mock::VerifyAndClear(beeperMock)); + ASSERT_TRUE(Mock::VerifyAndClear(comMock)); + ASSERT_TRUE(Mock::VerifyAndClear(encodersMock)); + ASSERT_TRUE(Mock::VerifyAndClear(solenoidsMock)); + ASSERT_TRUE(Mock::VerifyAndClear(opInitMock)); +} + +TEST_F(OpKnitTest, test_startKnitting_failures) { + uint8_t pattern[] = {1}; + get_to_ready(Machine_t::Kh910); + + // `m_stopNeedle` lower than `m_startNeedle` + ASSERT_TRUE(opKnit->startKnitting(1, 0, pattern, false) != Err_t::Success); + + // `m_stopNeedle` out of range + ASSERT_TRUE(opKnit->startKnitting(0, NUM_NEEDLES[static_cast(Machine_t::Kh910)], pattern, + false) != Err_t::Success); + + // null pattern + ASSERT_TRUE(opKnit->startKnitting(0, NUM_NEEDLES[static_cast(Machine_t::Kh910)] - 1, nullptr, + false) != Err_t::Success); + + // test expectations without destroying instance + ASSERT_TRUE(Mock::VerifyAndClear(solenoidsMock)); + ASSERT_TRUE(Mock::VerifyAndClear(encodersMock)); + ASSERT_TRUE(Mock::VerifyAndClear(comMock)); +} + +TEST_F(OpKnitTest, test_setNextLine) { + // set `m_lineRequested` + ASSERT_EQ(opKnit->setNextLine(1), false); + + expected_update_knit(true); + + // outside of the active needles + expected_cacheISR(NUM_NEEDLES[static_cast(Machine_t::Kh910)] + END_OF_LINE_OFFSET_R[static_cast(Machine_t::Kh910)] + 1 + opKnit->getStartOffset(Direction_t::Left)); + EXPECT_CALL(*solenoidsMock, setSolenoid).Times(1); + expected_update_knit(false); + + // wrong line number + EXPECT_CALL(*beeperMock, finishedLine).Times(0); + expect_reqLine(); + ASSERT_EQ(opKnit->setNextLine(1), false); + + // correct line number + EXPECT_CALL(*beeperMock, finishedLine).Times(1); + ASSERT_EQ(opKnit->setNextLine(0), true); + + // `m_lineRequested` has been set to `false` + ASSERT_EQ(opKnit->setNextLine(0), false); + + // test expectations without destroying instance + ASSERT_TRUE(Mock::VerifyAndClear(solenoidsMock)); + ASSERT_TRUE(Mock::VerifyAndClear(encodersMock)); + ASSERT_TRUE(Mock::VerifyAndClear(beeperMock)); + ASSERT_TRUE(Mock::VerifyAndClear(comMock)); +} + +TEST_F(OpKnitTest, test_knit_Kh910) { + get_to_ready(Machine_t::Kh910); + + // knit + uint8_t pattern[] = {1}; + + // `m_startNeedle` is greater than `m_pixelToSet` + EXPECT_CALL(*beeperMock, ready); + const uint8_t START_NEEDLE = NUM_NEEDLES[static_cast(Machine_t::Kh910)] - 2; + const uint8_t STOP_NEEDLE = NUM_NEEDLES[static_cast(Machine_t::Kh910)] - 1; + opKnit->startKnitting(START_NEEDLE, STOP_NEEDLE, pattern, true); + //EXPECT_CALL(*arduinoMock, digitalWrite(LED_PIN_A, LOW)); // green LED off + expected_update(); + + // first knit + expect_first_knit(); + expect_indState(); + expected_update_knit(false); + + // no useful position calculated by `calculatePixelAndSolenoid()` + expected_cacheISR(100, Direction_t::NoDirection, Direction_t::Right, BeltShift::Shifted, Carriage_t::Knit); + EXPECT_CALL(*solenoidsMock, setSolenoid).Times(0); + expect_indState(); + expected_update_knit(false); + + // don't set `m_workedonline` to `true` + const uint8_t OFFSET = END_OF_LINE_OFFSET_R[static_cast(Machine_t::Kh910)]; + expected_cacheISR(8 + STOP_NEEDLE + OFFSET); + EXPECT_CALL(*solenoidsMock, setSolenoid); + expect_indState(); + expected_update_knit(false); + + expected_cacheISR(START_NEEDLE); + EXPECT_CALL(*solenoidsMock, setSolenoid); + expect_indState(); + expected_update_knit(false); + + // test expectations without destroying instance + ASSERT_TRUE(Mock::VerifyAndClear(solenoidsMock)); + ASSERT_TRUE(Mock::VerifyAndClear(encodersMock)); + ASSERT_TRUE(Mock::VerifyAndClear(beeperMock)); + ASSERT_TRUE(Mock::VerifyAndClear(comMock)); +} + +TEST_F(OpKnitTest, test_knit_Kh270) { + get_to_ready(Machine_t::Kh270); + + // knit + uint8_t pattern[] = {1}; + + // `m_startNeedle` is greater than `m_pixelToSet` + EXPECT_CALL(*beeperMock, ready); + const uint8_t START_NEEDLE = NUM_NEEDLES[static_cast(Machine_t::Kh270)] - 2; + const uint8_t STOP_NEEDLE = NUM_NEEDLES[static_cast(Machine_t::Kh270)] - 1; + opKnit->startKnitting(START_NEEDLE, STOP_NEEDLE, pattern, true); + //EXPECT_CALL(*arduinoMock, digitalWrite(LED_PIN_A, LOW)); + expected_update(); + + // first knit + expect_first_knit(); + expect_indState(); + expected_update_knit(false); + + // second knit + expected_cacheISR(START_NEEDLE); + expect_indState(); + EXPECT_CALL(*solenoidsMock, setSolenoid); + expected_update_knit(false); + + // no useful position calculated by `calculatePixelAndSolenoid()` + expected_cacheISR(60, Direction_t::NoDirection, Direction_t::Right, BeltShift::Shifted, Carriage_t::Knit); + EXPECT_CALL(*solenoidsMock, setSolenoid).Times(0); + expect_indState(); + expected_update_knit(false); + + // don't set `m_workedonline` to `true` + const uint8_t OFFSET = END_OF_LINE_OFFSET_R[static_cast(Machine_t::Kh270)]; + expected_cacheISR(8 + STOP_NEEDLE + OFFSET, Direction_t::Right, Direction_t::Left, BeltShift::Regular, Carriage_t::Knit); + EXPECT_CALL(*solenoidsMock, setSolenoid); + expect_indState(); + expected_update_knit(false); + + expected_cacheISR(START_NEEDLE, Direction_t::Right, Direction_t::Left, BeltShift::Regular, Carriage_t::Knit); + EXPECT_CALL(*solenoidsMock, setSolenoid); + expect_indState(); + expected_update_knit(false); + + // test expectations without destroying instance + ASSERT_TRUE(Mock::VerifyAndClear(solenoidsMock)); + ASSERT_TRUE(Mock::VerifyAndClear(encodersMock)); + ASSERT_TRUE(Mock::VerifyAndClear(beeperMock)); + ASSERT_TRUE(Mock::VerifyAndClear(comMock)); +} + +TEST_F(OpKnitTest, test_knit_line_request) { + // `m_workedOnLine` is set to `true` + expected_update_knit(true); + + // Position has changed since last call to operate function + // `m_pixelToSet` is set above `m_stopNeedle` + END_OF_LINE_OFFSET_R + expected_cacheISR(NUM_NEEDLES[static_cast(Machine_t::Kh910)] + 8 + END_OF_LINE_OFFSET_R[static_cast(Machine_t::Kh910)] + 1); + + EXPECT_CALL(*solenoidsMock, setSolenoid); + expected_update_knit(false); + + // no change in position, no action. + EXPECT_CALL(*solenoidsMock, setSolenoid).Times(0); + expected_update_knit(false); + + // test expectations without destroying instance + ASSERT_TRUE(Mock::VerifyAndClear(solenoidsMock)); + ASSERT_TRUE(Mock::VerifyAndClear(encodersMock)); + ASSERT_TRUE(Mock::VerifyAndClear(beeperMock)); + ASSERT_TRUE(Mock::VerifyAndClear(comMock)); +} + +TEST_F(OpKnitTest, test_knit_lastLine) { + expected_update_knit(true); + + // Run one knit inside the working needles. + EXPECT_CALL(*solenoidsMock, setSolenoid); + expected_cacheISR(opKnit->getStartOffset(Direction_t::Left) + 20); + // `m_workedOnLine` is set to true + expected_update_knit(false); + + // Position has changed since last call to operate function + // `m_pixelToSet` is above `m_stopNeedle` + END_OF_LINE_OFFSET_R + expected_cacheISR(NUM_NEEDLES[static_cast(Machine_t::Kh910)] + END_OF_LINE_OFFSET_R[static_cast(Machine_t::Kh910)] + 1 + opKnit->getStartOffset(Direction_t::Left)); + + // `m_lastLineFlag` is `true` + opKnit->setLastLine(); + + EXPECT_CALL(*solenoidsMock, setSolenoid); + EXPECT_CALL(*beeperMock, endWork); + EXPECT_CALL(*solenoidsMock, setSolenoids(SOLENOIDS_BITMASK)); + //EXPECT_CALL(*beeperMock, finishedLine); + expected_update_knit(false); + + // test expectations without destroying instance + ASSERT_TRUE(Mock::VerifyAndClear(solenoidsMock)); + ASSERT_TRUE(Mock::VerifyAndClear(encodersMock)); + ASSERT_TRUE(Mock::VerifyAndClear(beeperMock)); + ASSERT_TRUE(Mock::VerifyAndClear(comMock)); +} + +TEST_F(OpKnitTest, test_knit_lastLine_and_no_req) { + expected_update_knit(true); + + // Run one knit inside the working needles. + EXPECT_CALL(*solenoidsMock, setSolenoid); + expected_cacheISR(opKnit->getStartOffset(Direction_t::Left) + 20); + // `m_workedOnLine` is set to true + expected_update_knit(false); + + // Position has changed since last call to operate function + // `m_pixelToSet` is above `m_stopNeedle` + END_OF_LINE_OFFSET_R + expected_cacheISR(NUM_NEEDLES[static_cast(Machine_t::Kh910)] + END_OF_LINE_OFFSET_R[static_cast(Machine_t::Kh910)] + 1 + opKnit->getStartOffset(Direction_t::Left)); + + // `m_lastLineFlag` is `true` + opKnit->setLastLine(); + + // Note: probing private data and methods to get full branch coverage. + opKnit->m_lineRequested = false; + + // EXPECT_CALL(*arduinoMock, digitalWrite(LED_PIN_A, 1)); + EXPECT_CALL(*solenoidsMock, setSolenoid); + EXPECT_CALL(*beeperMock, endWork); + EXPECT_CALL(*solenoidsMock, setSolenoids(SOLENOIDS_BITMASK)); + //EXPECT_CALL(*beeperMock, finishedLine); + expected_update_knit(false); + + // test expectations without destroying instance + ASSERT_TRUE(Mock::VerifyAndClear(solenoidsMock)); + ASSERT_TRUE(Mock::VerifyAndClear(encodersMock)); + ASSERT_TRUE(Mock::VerifyAndClear(beeperMock)); + ASSERT_TRUE(Mock::VerifyAndClear(comMock)); +} + +TEST_F(OpKnitTest, test_knit_same_position) { + expected_update_knit(true); + + // no call to `setSolenoid()` since position was the same + EXPECT_CALL(*solenoidsMock, setSolenoid).Times(0); + expected_update_knit(false); + + // test expectations without destroying instance + ASSERT_TRUE(Mock::VerifyAndClear(solenoidsMock)); + ASSERT_TRUE(Mock::VerifyAndClear(encodersMock)); + ASSERT_TRUE(Mock::VerifyAndClear(beeperMock)); + ASSERT_TRUE(Mock::VerifyAndClear(comMock)); +} + +TEST_F(OpKnitTest, test_knit_new_line) { + // _workedOnLine is set to true + expected_update_knit(true); + + // Run one knit inside the working needles. + EXPECT_CALL(*solenoidsMock, setSolenoid); + expected_cacheISR(opKnit->getStartOffset(Direction_t::Left) + 20); + // `m_workedOnLine` is set to true + expected_update_knit(false); + + // Position has changed since last call to operate function + // `m_pixelToSet` is above `m_stopNeedle` + END_OF_LINE_OFFSET_R + expected_cacheISR(NUM_NEEDLES[static_cast(Machine_t::Kh910)] + END_OF_LINE_OFFSET_R[static_cast(Machine_t::Kh910)] + 1 + opKnit->getStartOffset(Direction_t::Left)); + + // set `m_lineRequested` to `false` + EXPECT_CALL(*beeperMock, finishedLine); + opKnit->setNextLine(0); + + EXPECT_CALL(*solenoidsMock, setSolenoid); + + // `reqLine()` is called which calls `send_reqLine()` + expect_reqLine(); + expected_update_knit(false); + + // test expectations without destroying instance + ASSERT_TRUE(Mock::VerifyAndClear(solenoidsMock)); + ASSERT_TRUE(Mock::VerifyAndClear(encodersMock)); + ASSERT_TRUE(Mock::VerifyAndClear(beeperMock)); + ASSERT_TRUE(Mock::VerifyAndClear(comMock)); +} + +TEST_F(OpKnitTest, test_calculatePixelAndSolenoid) { + // Initialize KH910 + expected_init_machine(Machine_t::Kh910); + controller->setState(opKnit); + expected_update_init(); + + // No direction + // Lace carriage, no direction, need to change position to enter test + expected_cacheISR(100, Direction_t::NoDirection, BeltShift::Shifted, Carriage_t::Lace); + ASSERT_FALSE(opKnit->calculatePixelAndSolenoid()); + // opKnit->pixelToSet not set + // opKnit->m_solenoidToSet not set + + // Right direction + // Lace carriage, no belt on Right, have not reached offset + expected_cacheISR(39, Direction_t::Right, BeltShift::Unknown, Carriage_t::Lace); + ASSERT_FALSE(opKnit->calculatePixelAndSolenoid()); + // opKnit->pixelToSet not set + // opKnit->m_solenoidToSet not set + + // Lace carriage, no belt on Right, need to change position to enter test + expected_cacheISR(100, Direction_t::Right, BeltShift::Unknown, Carriage_t::Lace); + ASSERT_TRUE(opKnit->calculatePixelAndSolenoid()); + ASSERT_EQ(opKnit->m_pixelToSet, (100 - 40) + 8); + // opKnit->m_solenoidToSet not set + + // Lace carriage, regular belt on Right + expected_cacheISR(100, Direction_t::Right, BeltShift::Regular, Carriage_t::Lace); + ASSERT_TRUE(opKnit->calculatePixelAndSolenoid()); + ASSERT_EQ(opKnit->m_pixelToSet, (100 - 40) + 8); + ASSERT_EQ(opKnit->m_solenoidToSet, 100 % 16); + + // Lace carriage, shifted belt on Right + expected_cacheISR(100, Direction_t::Right, BeltShift::Shifted, Carriage_t::Lace); + ASSERT_TRUE(opKnit->calculatePixelAndSolenoid()); + ASSERT_EQ(opKnit->m_pixelToSet, (100 - 40) + 8); + ASSERT_EQ(opKnit->m_solenoidToSet, (100 - 8) % 16); + + // Left direction + // Lace carriage, no belt on Left, off of Right end, position is changed + expected_cacheISR(END_RIGHT[static_cast(Machine_t::Kh910)] - 15, Direction_t::Left, BeltShift::Unknown, Carriage_t::Lace); + ASSERT_FALSE(opKnit->calculatePixelAndSolenoid()); + // opKnit->pixelToSet not set + // opKnit->m_solenoidToSet not set + + // Lace carriage, no belt on Left + expected_cacheISR(100, Direction_t::Left, BeltShift::Unknown, Carriage_t::Lace); + ASSERT_TRUE(opKnit->calculatePixelAndSolenoid()); + ASSERT_EQ(opKnit->m_pixelToSet, 100 - 16 - 16); + // opKnit->m_solenoidToSet not set + + // Lace carriage, regular belt on Left + expected_cacheISR(100, Direction_t::Left, BeltShift::Regular, Carriage_t::Lace); + ASSERT_TRUE(opKnit->calculatePixelAndSolenoid()); + ASSERT_EQ(opKnit->m_pixelToSet, 100 - 16 - 16); + ASSERT_EQ(opKnit->m_solenoidToSet, (100 + 8) % 16); + + // Garter Carriage, no belt on Left, need to change position to enter test + expected_cacheISR(100, Direction_t::Left, BeltShift::Unknown, Carriage_t::Garter); + ASSERT_TRUE(opKnit->calculatePixelAndSolenoid()); + ASSERT_EQ(opKnit->m_pixelToSet, 100 - 56); + // opKnit->m_solenoidToSet not set + + // Garter carriage, regular belt on Left, need to change position to enter test + expected_cacheISR(100, Direction_t::Left, BeltShift::Regular, Carriage_t::Garter); + ASSERT_TRUE(opKnit->calculatePixelAndSolenoid()); + ASSERT_EQ(opKnit->m_pixelToSet, 100 - 56); + ASSERT_EQ(opKnit->m_solenoidToSet, (100 + 8) % 16); + + // Garter carriage, shifted belt on Left, need to change position to enter test + expected_cacheISR(100, Direction_t::Left, BeltShift::Shifted, Carriage_t::Garter); + ASSERT_TRUE(opKnit->calculatePixelAndSolenoid()); + ASSERT_EQ(opKnit->m_pixelToSet, 100 - 56); + ASSERT_EQ(opKnit->m_solenoidToSet, 100 % 16); + + // KH270 + controller->setMachineType(Machine_t::Kh270); + + // K carriage, no belt on Left + expected_cacheISR(0, Direction_t::Left, BeltShift::Unknown, Carriage_t::Knit); + ASSERT_TRUE(opKnit->calculatePixelAndSolenoid()); + ASSERT_EQ(opKnit->m_pixelToSet, static_cast(0 - 16)); + ASSERT_EQ(opKnit->m_solenoidToSet, static_cast(0 + 6) % 12 + 3); + + // K carriage, no belt on Right + expected_cacheISR(END_RIGHT[static_cast(Machine_t::Kh270)], Direction_t::Right, BeltShift::Unknown, Carriage_t::Knit); + ASSERT_TRUE(opKnit->calculatePixelAndSolenoid()); + ASSERT_EQ(opKnit->m_pixelToSet, (140 - 28)); + ASSERT_EQ(opKnit->m_solenoidToSet, 140 % 12 + 3); + + // Test expectations without destroying instance + ASSERT_TRUE(Mock::VerifyAndClear(solenoidsMock)); + ASSERT_TRUE(Mock::VerifyAndClear(encodersMock)); + ASSERT_TRUE(Mock::VerifyAndClear(comMock)); +} + +TEST_F(OpKnitTest, test_getStartOffset) { + // out of range values + controller->m_carriage = Carriage_t::Knit; + ASSERT_EQ(opKnit->getStartOffset(Direction_t::NoDirection), 0); + + controller->m_carriage = Carriage_t::NoCarriage; + ASSERT_EQ(opKnit->getStartOffset(Direction_t::Left), 0); + ASSERT_EQ(opKnit->getStartOffset(Direction_t::Right), 0); + + controller->m_carriage = Carriage_t::Lace; + controller->m_machineType = Machine_t::NoMachine; + ASSERT_EQ(opKnit->getStartOffset(Direction_t::Left), 0); + ASSERT_EQ(opKnit->getStartOffset(Direction_t::Right), 0); +} diff --git a/test/test_OpReady.cpp b/test/test_OpReady.cpp new file mode 100644 index 000000000..2f02fd786 --- /dev/null +++ b/test/test_OpReady.cpp @@ -0,0 +1,115 @@ +/*!` + * \file test_OpReady.cpp + * + * This file is part of AYAB. + * + * AYAB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * AYAB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with AYAB. If not, see . + * + * Original Work Copyright 2013 Christian Obersteiner, Andreas Müller + * Modified Work Copyright 2020-3 Sturla Lange, Tom Price + * http://ayab-knitting.com + */ + +#include + +#include + +#include +#include + +#include +#include + +using ::testing::_; +using ::testing::An; +using ::testing::AtLeast; +using ::testing::Mock; +using ::testing::Return; + +extern OpReady *opReady; +extern OpTest *opTest; + +extern ControllerMock *controller; +extern OpKnitMock *opKnit; + +class OpReadyTest : public ::testing::Test { +protected: + void SetUp() override { + arduinoMock = arduinoMockInstance(); + serialMock = serialMockInstance(); + // serialCommandMock = serialCommandMockInstance(); + + // pointers to global instances + controllerMock = controller; + opKnitMock = opKnit; + + // The global instances do not get destroyed at the end of each test. + // Ordinarily the mock instance would be local and such behaviour would + // cause a memory leak. We must notify the test that this is not the case. + Mock::AllowLeak(controllerMock); + Mock::AllowLeak(opKnitMock); + } + + void TearDown() override { + releaseArduinoMock(); + releaseSerialMock(); + } + + ArduinoMock *arduinoMock; + ControllerMock *controllerMock; + SerialMock *serialMock; + OpKnitMock *opKnitMock; +}; + +TEST_F(OpReadyTest, test_state) { + ASSERT_EQ(opReady->state(), OpState_t::Ready); +} + +TEST_F(OpReadyTest, test_begin) { + EXPECT_CALL(*arduinoMock, digitalWrite(LED_PIN_A, LOW)); + opReady->begin(); +} + +TEST_F(OpReadyTest, test_init) { + // no calls expected + opReady->init(); +} + +TEST_F(OpReadyTest, test_reqStart) { + EXPECT_CALL(*opKnitMock, startKnitting); + const uint8_t buffer[] = {static_cast(API_t::reqStart), 0, 10, 1, 0x36}; + opReady->com(buffer, 5); +} + +TEST_F(OpReadyTest, test_reqTest) { + EXPECT_CALL(*controllerMock, setState(opTest)); + const uint8_t buffer[] = {static_cast(API_t::reqTest)}; + opReady->com(buffer, 1); +} + +TEST_F(OpReadyTest, test_unrecognized) { + // no calls expected + const uint8_t buffer[] = {0xFF}; + opReady->com(buffer, 1); +} + +TEST_F(OpReadyTest, test_update) { + // no calls expected + opReady->update(); +} + +TEST_F(OpReadyTest, test_end) { + // no calls expected + opReady->end(); +} diff --git a/test/test_OpTest.cpp b/test/test_OpTest.cpp new file mode 100644 index 000000000..828c8a126 --- /dev/null +++ b/test/test_OpTest.cpp @@ -0,0 +1,336 @@ +/*!` + * \file test_OpTest.cpp + * + * This file is part of AYAB. + * + * AYAB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * AYAB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with AYAB. If not, see . + * + * Original Work Copyright 2013 Christian Obersteiner, Andreas Müller + * Modified Work Copyright 2020-3 Sturla Lange, Tom Price + * http://ayab-knitting.com + */ + +#include + +#include + +#include +#include +#include + +#include +#include + +using ::testing::_; +using ::testing::An; +using ::testing::AtLeast; +using ::testing::Mock; +using ::testing::Return; + +extern Beeper *beeper; + +extern OpInit *opInit; +extern OpReady *opReady; +extern OpTest *opTest; + +extern OpKnitMock *opKnit; +extern ControllerMock *controller; + +class OpTestTest : public ::testing::Test { +protected: + void SetUp() override { + arduinoMock = arduinoMockInstance(); + serialMock = serialMockInstance(); + // serialCommandMock = serialCommandMockInstance(); + + // pointers to global instances + controllerMock = controller; + opKnitMock = opKnit; + + // The global instances do not get destroyed at the end of each test. + // Ordinarily the mock instance would be local and such behaviour would + // cause a memory leak. We must notify the test that this is not the case. + Mock::AllowLeak(controllerMock); + Mock::AllowLeak(opKnitMock); + + beeper->init(true); + } + + void TearDown() override { + releaseArduinoMock(); + releaseSerialMock(); + } + + ArduinoMock *arduinoMock; + SerialMock *serialMock; + ControllerMock *controllerMock; + OpKnitMock *opKnitMock; + + void expect_startTest(uint32_t t) { + expect_write(false); + EXPECT_CALL(*arduinoMock, millis).WillOnce(Return(t)); + opTest->begin(); + } + + void expect_write(bool once) { + return; + //TODO: FIXME: Mock PocketSerial, so this works again. + if (once) { + EXPECT_CALL(*serialMock, write(_, _)); + EXPECT_CALL(*serialMock, write(SLIP::END)); + } else { + EXPECT_CALL(*serialMock, write(_, _)).Times(AtLeast(1)); + EXPECT_CALL(*serialMock, write(SLIP::END)).Times(AtLeast(1)); + } + } + + void expect_readEOLsensors(bool flag) { + uint8_t n = flag ? 1 : 0; + EXPECT_CALL(*arduinoMock, analogRead(EOL_PIN_L)).Times(n); + EXPECT_CALL(*arduinoMock, analogRead(EOL_PIN_R)).Times(n); + } + + void expect_readEncoders(bool flag) { + uint8_t n = flag ? 1 : 0; + EXPECT_CALL(*arduinoMock, digitalRead(ENC_PIN_A)).Times(n); + EXPECT_CALL(*arduinoMock, digitalRead(ENC_PIN_B)).Times(n); + EXPECT_CALL(*arduinoMock, digitalRead(ENC_PIN_C)).Times(n); + } +}; + +TEST_F(OpTestTest, test_state) { + ASSERT_EQ(opTest->state(), OpState_t::Test); +} + +TEST_F(OpTestTest, test_init) { + // no calls expected + opTest->init(); +} + +TEST_F(OpTestTest, test_enabled) { + const uint8_t stopCmd[] = {static_cast(API_t::stopCmd)}; + const uint8_t autoReadCmd[] = {static_cast(API_t::autoReadCmd)}; + const uint8_t autoTestCmd[] = {static_cast(API_t::autoTestCmd)}; + opTest->com(stopCmd, 1); + ASSERT_EQ(opTest->enabled(), false); + opTest->com(autoReadCmd, 1); + ASSERT_EQ(opTest->enabled(), true); + opTest->com(autoTestCmd, 1); + ASSERT_EQ(opTest->enabled(), true); + opTest->com(stopCmd, 1); + opTest->com(autoTestCmd, 1); + ASSERT_EQ(opTest->enabled(), true); +} + +TEST_F(OpTestTest, test_helpCmd) { + expect_write(false); + opTest->helpCmd(); +} + +TEST_F(OpTestTest, test_sendCmd) { + expect_write(false); + opTest->sendCmd(); +} + +TEST_F(OpTestTest, test_beepCmd) { + expect_write(true); + opTest->beepCmd(); + EXPECT_CALL(*arduinoMock, millis).WillOnce(Return(0U)); + beeper->update(); + EXPECT_CALL(*arduinoMock, analogWrite(PIEZO_PIN, BEEP_ON_DUTY)); + EXPECT_CALL(*arduinoMock, millis).WillOnce(Return(1U)); + beeper->update(); +} + +TEST_F(OpTestTest, test_setSingleCmd_fail1) { + const uint8_t buf[] = {static_cast(API_t::setSingleCmd), 0}; + expect_write(false); + opTest->setSingleCmd(buf, 2); +} + +TEST_F(OpTestTest, test_setSingleCmd_fail2) { + const uint8_t buf[] = {static_cast(API_t::setSingleCmd), 16, 0}; + expect_write(false); + opTest->setSingleCmd(buf, 3); +} + +TEST_F(OpTestTest, test_setSingleCmd_fail3) { + const uint8_t buf[] = {static_cast(API_t::setSingleCmd), 15, 2}; + expect_write(false); + opTest->setSingleCmd(buf, 3); +} + +TEST_F(OpTestTest, test_setSingleCmd_success) { + const uint8_t buf[] = {static_cast(API_t::setSingleCmd), 15, 1}; + expect_write(true); + opTest->setSingleCmd(buf, 3); +} + +TEST_F(OpTestTest, test_setAllCmd_fail1) { + const uint8_t buf[] = {static_cast(API_t::setAllCmd), 0}; + expect_write(false); + opTest->setAllCmd(buf, 2); +} + +TEST_F(OpTestTest, test_setAllCmd_success) { + const uint8_t buf[] = {static_cast(API_t::setAllCmd), 0xFF, 0xFF}; + expect_write(true); + opTest->setAllCmd(buf, 3); +} + +TEST_F(OpTestTest, test_readEOLsensorsCmd) { + expect_write(false); + expect_readEOLsensors(true); + opTest->readEOLsensorsCmd(); +} + +TEST_F(OpTestTest, test_readEncodersCmd_low) { + expect_write(false); + EXPECT_CALL(*arduinoMock, digitalRead).WillRepeatedly(Return(LOW)); + opTest->readEncodersCmd(); +} + +TEST_F(OpTestTest, test_readEncodersCmd_high) { + expect_write(false); + EXPECT_CALL(*arduinoMock, digitalRead).WillRepeatedly(Return(HIGH)); + opTest->readEncodersCmd(); +} + +TEST_F(OpTestTest, test_autoReadCmd) { + const uint8_t buf[] = {static_cast(API_t::autoReadCmd)}; + expect_write(true); + opTest->com(buf, 1); +} + +TEST_F(OpTestTest, test_autoTestCmd) { + const uint8_t buf[] = {static_cast(API_t::autoTestCmd)}; + expect_write(true); + opTest->com(buf, 1); +} + +TEST_F(OpTestTest, test_quitCmd) { + const uint8_t buf[] = {static_cast(API_t::quitCmd)}; + EXPECT_CALL(*controllerMock, setState(opInit)); + opTest->com(buf, 1); + + // test expectations without destroying instance + ASSERT_TRUE(Mock::VerifyAndClear(controllerMock)); +} + +TEST_F(OpTestTest, test_end) { + EXPECT_CALL(*opKnitMock, init); + opTest->end(); + + // test expectations without destroying instance + ASSERT_TRUE(Mock::VerifyAndClear(opKnitMock)); +} + +TEST_F(OpTestTest, test_loop_null) { + expect_startTest(0U); + EXPECT_CALL(*arduinoMock, millis).Times(0); + opTest->update(); +} + +TEST_F(OpTestTest, test_autoRead) { + expect_startTest(0U); + opTest->autoReadCmd(); + + // nothing has happened yet + EXPECT_CALL(*arduinoMock, millis).WillOnce(Return(TEST_LOOP_DELAY - 1)); + EXPECT_CALL(*opKnitMock, encodePosition); + expect_write(false); + expect_readEOLsensors(false); + expect_readEncoders(false); + opTest->update(); + + // m_timerEventOdd = false + EXPECT_CALL(*arduinoMock, millis).WillOnce(Return(TEST_LOOP_DELAY)); + EXPECT_CALL(*opKnitMock, encodePosition); + expect_write(true); + expect_readEOLsensors(false); + expect_readEncoders(false); + opTest->update(); + + // m_timerEventOdd = false + EXPECT_CALL(*arduinoMock, millis).WillOnce(Return(2 * TEST_LOOP_DELAY)); + EXPECT_CALL(*opKnitMock, encodePosition); + expect_write(false); + expect_readEOLsensors(true); + expect_readEncoders(true); + opTest->update(); + + // after `stopCmd()` + opTest->stopCmd(); + EXPECT_CALL(*arduinoMock, millis).Times(0); + EXPECT_CALL(*opKnitMock, encodePosition).Times(0); + expect_write(false); + expect_readEOLsensors(false); + expect_readEncoders(false); + opTest->update(); + + // test expectations without destroying instance + ASSERT_TRUE(Mock::VerifyAndClear(opKnitMock)); +} + +TEST_F(OpTestTest, test_autoTest) { + expect_startTest(0U); + opTest->autoTestCmd(); + + // nothing has happened yet + EXPECT_CALL(*arduinoMock, millis).WillOnce(Return(TEST_LOOP_DELAY - 1)); + EXPECT_CALL(*opKnitMock, encodePosition); + expect_write(false); + EXPECT_CALL(*arduinoMock, digitalWrite(LED_PIN_A, HIGH)).Times(0); + EXPECT_CALL(*arduinoMock, digitalWrite(LED_PIN_B, HIGH)).Times(0); + opTest->update(); + + // m_timerEventOdd = false + EXPECT_CALL(*arduinoMock, millis).WillOnce(Return(TEST_LOOP_DELAY)); + EXPECT_CALL(*opKnitMock, encodePosition); + expect_write(true); + EXPECT_CALL(*arduinoMock, digitalWrite(LED_PIN_A, HIGH)); + EXPECT_CALL(*arduinoMock, digitalWrite(LED_PIN_B, HIGH)); + opTest->update(); + + // m_timerEventOdd = false + EXPECT_CALL(*arduinoMock, millis).WillOnce(Return(2 * TEST_LOOP_DELAY)); + EXPECT_CALL(*opKnitMock, encodePosition); + expect_write(false); + EXPECT_CALL(*arduinoMock, digitalWrite(LED_PIN_A, LOW)); + EXPECT_CALL(*arduinoMock, digitalWrite(LED_PIN_B, LOW)); + opTest->update(); + + // after `stopCmd()` + opTest->stopCmd(); + EXPECT_CALL(*arduinoMock, millis).Times(0); + EXPECT_CALL(*opKnitMock, encodePosition).Times(0); + expect_write(false); + EXPECT_CALL(*arduinoMock, digitalWrite(LED_PIN_A, _)).Times(0); + EXPECT_CALL(*arduinoMock, digitalWrite(LED_PIN_B, _)).Times(0); + opTest->update(); + + // test expectations without destroying instance + ASSERT_TRUE(Mock::VerifyAndClear(opKnitMock)); +} + +TEST_F(OpTestTest, test_startTest_success) { + expect_startTest(0U); +} + +TEST_F(OpTestTest, test_unrecognized) { + // no calls expected + const uint8_t buffer[] = {0xFF}; + opTest->com(buffer, 1); +} + diff --git a/test/test_all.cpp b/test/test_all.cpp index 44e3a57e6..ebe880ba9 100644 --- a/test/test_all.cpp +++ b/test/test_all.cpp @@ -17,41 +17,56 @@ * along with AYAB. If not, see . * * Original Work Copyright 2013 Christian Obersteiner, Andreas Müller - * Modified Work Copyright 2020 Sturla Lange, Tom Price + * Modified Work Copyright 2020-3 Sturla Lange, Tom Price * http://ayab-knitting.com */ #include "gtest/gtest.h" -#include -#include +#include +#include #include #include #include #include -#include + +#include +#include +#include +#include +#include // global definitions // references everywhere else must use `extern` -Fsm *fsm = new Fsm(); -Knitter *knitter = new Knitter(); +Controller *controller = new Controller(); +OpKnit *opKnit = new OpKnit(); + +BeeperMock *beeper = new BeeperMock(); +ComMock *com = new ComMock(); +EncodersMock *encoders = new EncodersMock(); +SolenoidsMock *solenoids = new SolenoidsMock(); -BeeperMock *beeper = new BeeperMock(); -ComMock *com = new ComMock(); -EncodersMock *encoders = new EncodersMock(); -SolenoidsMock *solenoids = new SolenoidsMock(); -TesterMock *tester = new TesterMock(); +OpIdleMock *opIdle = new OpIdleMock(); +OpInitMock *opInit = new OpInitMock(); +OpReadyMock *opReady = new OpReadyMock(); +OpTestMock *opTest = new OpTestMock(); +OpErrorMock *opError = new OpErrorMock(); // instantiate singleton classes with mock objects -FsmInterface *GlobalFsm::m_instance = fsm; -KnitterInterface *GlobalKnitter::m_instance = knitter; - -BeeperInterface *GlobalBeeper::m_instance = beeper; -ComInterface *GlobalCom::m_instance = com; -EncodersInterface *GlobalEncoders::m_instance = encoders; -SolenoidsInterface *GlobalSolenoids::m_instance = solenoids; -TesterInterface *GlobalTester::m_instance = tester; +ControllerInterface *GlobalController::m_instance = controller; +OpKnitInterface *GlobalOpKnit::m_instance = opKnit; + +BeeperInterface *GlobalBeeper::m_instance = beeper; +ComInterface *GlobalCom::m_instance = com; +EncodersInterface *GlobalEncoders::m_instance = encoders; +SolenoidsInterface *GlobalSolenoids::m_instance = solenoids; + +OpIdleInterface *GlobalOpIdle::m_instance = opIdle; +OpInitInterface *GlobalOpInit::m_instance = opInit; +OpReadyInterface *GlobalOpReady::m_instance = opReady; +OpTestInterface *GlobalOpTest::m_instance = opTest; +OpErrorInterface *GlobalOpError::m_instance = opError; int main(int argc, char *argv[]) { ::testing::InitGoogleMock(&argc, argv); diff --git a/test/test_beeper.cpp b/test/test_beeper.cpp index cace6daff..5e990bcba 100644 --- a/test/test_beeper.cpp +++ b/test/test_beeper.cpp @@ -17,7 +17,7 @@ * along with AYAB. If not, see . * * Original Work Copyright 2013 Christian Obersteiner, Andreas Müller - * Modified Work Copyright 2020 Sturla Lange, Tom Price + * Modified Work Copyright 2020-3 Sturla Lange, Tom Price * http://ayab-knitting.com */ @@ -44,11 +44,11 @@ class BeeperTest : public ::testing::Test { void expectedBeepSchedule(unsigned long t) { EXPECT_CALL(*arduinoMock, millis).WillOnce(Return(t)); - beeper->schedule(); + beeper->update(); } - void expectedBeepRepeats(uint8_t repeats, bool enabled) { - if (enabled) { + void expectedBeepRepeats(uint8_t repeats) { + if (beeper->enabled()) { ASSERT_EQ(beeper->getState(), BeepState::Wait); for (uint8_t i = 0; i < repeats; i++) { expectedBeepSchedule(BEEP_DELAY * 2 * i); @@ -56,6 +56,8 @@ class BeeperTest : public ::testing::Test { EXPECT_CALL(*arduinoMock, analogWrite(PIEZO_PIN, BEEP_ON_DUTY)); expectedBeepSchedule(BEEP_DELAY * 2 * i); ASSERT_EQ(beeper->getState(), BeepState::Wait); + expectedBeepSchedule(BEEP_DELAY * (2 * i + 1) - 1); + ASSERT_EQ(beeper->getState(), BeepState::Wait); expectedBeepSchedule(BEEP_DELAY * (2 * i + 1)); ASSERT_EQ(beeper->getState(), BeepState::Off); EXPECT_CALL(*arduinoMock, analogWrite(PIEZO_PIN, BEEP_OFF_DUTY)); @@ -66,6 +68,7 @@ class BeeperTest : public ::testing::Test { expectedBeepSchedule(BEEP_DELAY * (2 * repeats)); } ASSERT_EQ(beeper->getState(), BeepState::Idle); + expectedBeepSchedule(BEEP_DELAY * (2 * repeats) + 1); } }; @@ -73,40 +76,40 @@ TEST_F(BeeperTest, test_ready_enabled) { beeper->init(true); EXPECT_CALL(*arduinoMock, millis).WillOnce(Return(0U)); beeper->ready(); - expectedBeepRepeats(BEEP_NUM_READY, true); + expectedBeepRepeats(BEEP_NUM_READY); } TEST_F(BeeperTest, test_finishedLine_enabled) { beeper->init(true); EXPECT_CALL(*arduinoMock, millis).WillOnce(Return(0U)); beeper->finishedLine(); - expectedBeepRepeats(BEEP_NUM_FINISHEDLINE, true); + expectedBeepRepeats(BEEP_NUM_FINISHEDLINE); } TEST_F(BeeperTest, test_endWork_enabled) { beeper->init(true); EXPECT_CALL(*arduinoMock, millis).WillOnce(Return(0U)); beeper->endWork(); - expectedBeepRepeats(BEEP_NUM_ENDWORK, true); + expectedBeepRepeats(BEEP_NUM_ENDWORK); } TEST_F(BeeperTest, test_ready_disabled) { beeper->init(false); EXPECT_CALL(*arduinoMock, millis).Times(0); beeper->ready(); - expectedBeepRepeats(BEEP_NUM_READY, false); + expectedBeepRepeats(BEEP_NUM_READY); } TEST_F(BeeperTest, test_finishedLine_disabled) { beeper->init(false); EXPECT_CALL(*arduinoMock, millis).Times(0); beeper->finishedLine(); - expectedBeepRepeats(BEEP_NUM_FINISHEDLINE, false); + expectedBeepRepeats(BEEP_NUM_FINISHEDLINE); } TEST_F(BeeperTest, test_endWork_disabled) { beeper->init(false); EXPECT_CALL(*arduinoMock, millis).Times(0); beeper->endWork(); - expectedBeepRepeats(BEEP_NUM_ENDWORK, false); + expectedBeepRepeats(BEEP_NUM_ENDWORK); } diff --git a/test/test_boards.cpp b/test/test_boards.cpp index bd6926844..ccd36eb75 100644 --- a/test/test_boards.cpp +++ b/test/test_boards.cpp @@ -17,7 +17,7 @@ * along with AYAB. If not, see . * * Original Work Copyright 2013 Christian Obersteiner, Andreas Müller - * Modified Work Copyright 2020 Sturla Lange, Tom Price + * Modified Work Copyright 2020-3 Sturla Lange, Tom Price * http://ayab-knitting.com */ @@ -27,31 +27,46 @@ #include #include #include -#include -#include -#include +#include +#include +#include +#include +#include + +#include +#include // global definitions // references everywhere else must use `extern` -Beeper *beeper = new Beeper(); -Com *com = new Com(); -Encoders *encoders = new Encoders(); +Beeper *beeper = new Beeper(); +Com *com = new Com(); +Encoders *encoders = new Encoders(); Solenoids *solenoids = new Solenoids(); -Tester *tester = new Tester(); -FsmMock *fsm = new FsmMock(); -KnitterMock *knitter = new KnitterMock(); +OpIdle *opIdle = new OpIdle(); +OpInit *opInit = new OpInit(); +OpReady *opReady = new OpReady(); +OpTest *opTest = new OpTest(); +OpError *opError = new OpError(); + +ControllerMock *controller = new ControllerMock(); +OpKnitMock *opKnit = new OpKnitMock(); // initialize static members -BeeperInterface *GlobalBeeper::m_instance = beeper; -ComInterface *GlobalCom::m_instance = com; -EncodersInterface *GlobalEncoders::m_instance = encoders; -SolenoidsInterface *GlobalSolenoids::m_instance = solenoids; -TesterInterface *GlobalTester::m_instance = tester; - -FsmInterface *GlobalFsm::m_instance = fsm; -KnitterInterface *GlobalKnitter::m_instance = knitter; +BeeperInterface *GlobalBeeper::m_instance = beeper; +ComInterface *GlobalCom::m_instance = com; +EncodersInterface *GlobalEncoders::m_instance = encoders; +SolenoidsInterface *GlobalSolenoids::m_instance = solenoids; + +OpIdleInterface *GlobalOpIdle::m_instance = opIdle; +OpInitInterface *GlobalOpInit::m_instance = opInit; +OpReadyInterface *GlobalOpReady::m_instance = opReady; +OpTestInterface *GlobalOpTest::m_instance = opTest; +OpErrorInterface *GlobalOpError::m_instance = opError; + +ControllerInterface *GlobalController::m_instance = controller; +OpKnitInterface *GlobalOpKnit::m_instance = opKnit; int main(int argc, char *argv[]) { ::testing::InitGoogleMock(&argc, argv); diff --git a/test/test_com.cpp b/test/test_com.cpp index 40bb329d7..276b4bf73 100644 --- a/test/test_com.cpp +++ b/test/test_com.cpp @@ -1,5 +1,5 @@ /*!` - * \file test_serial_encoding.cpp + * \file test_com.cpp * * This file is part of AYAB. * @@ -17,7 +17,7 @@ * along with AYAB. If not, see . * * Original Work Copyright 2013 Christian Obersteiner, Andreas Müller - * Modified Work Copyright 2020 Sturla Lange, Tom Price + * Modified Work Copyright 2020-3 Sturla Lange, Tom Price * http://ayab-knitting.com */ @@ -25,10 +25,13 @@ #include #include -#include -#include -#include +#include +#include +#include + +#include +#include using ::testing::_; using ::testing::AtLeast; @@ -38,8 +41,12 @@ using ::testing::Return; extern Com *com; extern Beeper *beeper; -extern FsmMock *fsm; -extern KnitterMock *knitter; +extern OpIdle *opIdle; +extern OpInit *opInit; +extern OpTest *opTest; + +extern ControllerMock *controller; +extern OpKnitMock *opKnit; class ComTest : public ::testing::Test { protected: @@ -48,18 +55,19 @@ class ComTest : public ::testing::Test { serialMock = serialMockInstance(); // pointer to global instance - fsmMock = fsm; - knitterMock = knitter; + controllerMock = controller; + opKnitMock = opKnit; // The global instance does not get destroyed at the end of each test. // Ordinarily the mock instance would be local and such behaviour would // cause a memory leak. We must notify the test that this is not the case. - Mock::AllowLeak(fsmMock); - Mock::AllowLeak(knitterMock); + Mock::AllowLeak(controllerMock); + Mock::AllowLeak(opKnitMock); beeper->init(true); expect_init(); com->init(); + controllerMock->init(); } void TearDown() override { @@ -68,9 +76,9 @@ class ComTest : public ::testing::Test { } ArduinoMock *arduinoMock; - FsmMock *fsmMock; - KnitterMock *knitterMock; SerialMock *serialMock; + ControllerMock *controllerMock; + OpKnitMock *opKnitMock; void expect_init() { //EXPECT_CALL(*serialMock, begin); @@ -90,152 +98,157 @@ class ComTest : public ::testing::Test { void expected_write_onPacketReceived(uint8_t *buffer, size_t size, bool once) { expect_write(once); - com->onPacketReceived(buffer, size); + //com->onPacketReceived(buffer, size); + opTest->com(buffer, size); } void reqInit(Machine_t machine) { - uint8_t buffer[] = {static_cast(AYAB_API::reqInit), static_cast(machine)}; - EXPECT_CALL(*fsmMock, setState(OpState::init)); - expected_write_onPacketReceived(buffer, sizeof(buffer), true); + uint8_t buffer[] = {static_cast(API_t::reqInit), static_cast(machine), 0}; + buffer[2] = com->CRC8(buffer, 2); + EXPECT_CALL(*controllerMock, setState(opInit)); + expect_write(true); + opIdle->com(buffer, sizeof(buffer)); } }; -/* -TEST_F(ComTest, test_API) { - ASSERT_EQ(API_VERSION, 6); +TEST_F(ComTest, test_reqInit_fail1) { + uint8_t buffer[] = {static_cast(API_t::reqInit), static_cast(Machine_t::Kh930)}; + EXPECT_CALL(*controllerMock, setState(opInit)).Times(0); + expect_write(true); + opIdle->com(buffer, sizeof(buffer)); } -*/ -TEST_F(ComTest, test_reqInit_too_short_error) { - uint8_t buffer[] = {static_cast(AYAB_API::reqInit), static_cast(Machine_t::Kh910)}; - //EXPECT_CALL(*serialMock, write(static_cast(AYAB_API::cnfInit))); - //EXPECT_CALL(*serialMock, write(EXPECTED_LONGER_MESSAGE)); - //EXPECT_CALL(*serialMock, write(SLIP::END)); - EXPECT_CALL(*fsmMock, setState(OpState::init)).Times(0); - com->onPacketReceived(buffer, sizeof(buffer)); - - // test expectations without destroying instance - ASSERT_TRUE(Mock::VerifyAndClear(fsmMock)); +TEST_F(ComTest, test_reqInit_fail2) { + uint8_t buffer[] = {static_cast(API_t::reqInit), static_cast(Machine_t::Kh930), 0}; + buffer[2] = com->CRC8(buffer, 2) ^ 1; + EXPECT_CALL(*controllerMock, setState(opInit)).Times(0); + expect_write(true); + opIdle->com(buffer, sizeof(buffer)); } -TEST_F(ComTest, test_reqInit_checksum_error) { - uint8_t buffer[] = {static_cast(AYAB_API::reqInit), static_cast(Machine_t::Kh910), 0}; - //EXPECT_CALL(*serialMock, write(static_cast(AYAB_API::cnfInit))); - //EXPECT_CALL(*serialMock, write(CHECKSUM_ERROR)); - //EXPECT_CALL(*serialMock, write(SLIP::END)); - EXPECT_CALL(*fsmMock, setState(OpState::init)).Times(0); - com->onPacketReceived(buffer, sizeof(buffer)); +TEST_F(ComTest, test_reqInit_fail3) { + uint8_t buffer[] = {static_cast(API_t::reqInit), static_cast(Machine_t::NoMachine), 0}; + buffer[2] = com->CRC8(buffer, 2); + EXPECT_CALL(*controllerMock, setState(opInit)).Times(0); + expect_write(true); + opIdle->com(buffer, sizeof(buffer)); +} - // test expectations without destroying instance - ASSERT_TRUE(Mock::VerifyAndClear(fsmMock)); +/* +TEST_F(ComTest, test_API) { + ASSERT_EQ(API_VERSION, 6); } +*/ +/* TEST_F(ComTest, test_reqtest_fail) { // no machineType - uint8_t buffer[] = {static_cast(AYAB_API::reqTest)}; - EXPECT_CALL(*fsmMock, setState(OpState::test)).Times(0); + uint8_t buffer[] = {static_cast(API_t::reqTest)}; + EXPECT_CALL(*controllerMock, setState(opTest)).Times(0); expected_write_onPacketReceived(buffer, sizeof(buffer), true); // test expectations without destroying instance - ASSERT_TRUE(Mock::VerifyAndClear(fsmMock)); + ASSERT_TRUE(Mock::VerifyAndClear(controllerMock)); } +*/ -TEST_F(ComTest, test_reqtest_success_KH270) { - uint8_t buffer[] = {static_cast(AYAB_API::reqTest), static_cast(Machine_t::Kh270)}; - EXPECT_CALL(*fsmMock, setState(OpState::test)); - EXPECT_CALL(*knitterMock, setMachineType(Machine_t::Kh270)); - EXPECT_CALL(*arduinoMock, millis); - expected_write_onPacketReceived(buffer, sizeof(buffer), false); +TEST_F(ComTest, test_reqtest) { + EXPECT_CALL(*controllerMock, setState(opTest)); + expect_write(true); + com->h_reqTest(); // test expectations without destroying instance - ASSERT_TRUE(Mock::VerifyAndClear(fsmMock)); - ASSERT_TRUE(Mock::VerifyAndClear(knitterMock)); + ASSERT_TRUE(Mock::VerifyAndClear(controllerMock)); } TEST_F(ComTest, test_reqstart_fail1) { // checksum wrong - uint8_t buffer[] = {static_cast(AYAB_API::reqStart), 0, 10, 1, 0x73}; - EXPECT_CALL(*knitterMock, startKnitting).Times(0); - expected_write_onPacketReceived(buffer, sizeof(buffer), true); + uint8_t buffer[] = {static_cast(API_t::reqStart), 0, 10, 1, 0x73}; + EXPECT_CALL(*opKnitMock, startKnitting).Times(0); + expect_write(true); + com->h_reqStart(buffer, sizeof(buffer)); // test expectations without destroying instance - ASSERT_TRUE(Mock::VerifyAndClear(knitterMock)); + ASSERT_TRUE(Mock::VerifyAndClear(opKnitMock)); } TEST_F(ComTest, test_reqstart_fail2) { // not enough bytes - uint8_t buffer[] = {static_cast(AYAB_API::reqStart), 0, 1, 0x74}; - EXPECT_CALL(*knitterMock, startKnitting).Times(0); - expected_write_onPacketReceived(buffer, sizeof(buffer) - 1, true); + uint8_t buffer[] = {static_cast(API_t::reqStart), 0, 1, 0x74}; + EXPECT_CALL(*opKnitMock, startKnitting).Times(0); + expect_write(true); + com->h_reqStart(buffer, sizeof(buffer) - 1); // test expectations without destroying instance - ASSERT_TRUE(Mock::VerifyAndClear(knitterMock)); + ASSERT_TRUE(Mock::VerifyAndClear(opKnitMock)); } TEST_F(ComTest, test_reqstart_success_KH910) { reqInit(Machine_t::Kh910); - uint8_t buffer[] = {static_cast(AYAB_API::reqStart), 0, 10, 1, 0x36}; - EXPECT_CALL(*knitterMock, startKnitting); - expected_write_onPacketReceived(buffer, sizeof(buffer), false); + uint8_t buffer[] = {static_cast(API_t::reqStart), 0, 10, 1, 0x36}; + EXPECT_CALL(*opKnitMock, startKnitting); + expect_write(true); + com->h_reqStart(buffer, sizeof(buffer)); // test expectations without destroying instance - ASSERT_TRUE(Mock::VerifyAndClear(knitterMock)); + ASSERT_TRUE(Mock::VerifyAndClear(opKnitMock)); } TEST_F(ComTest, test_reqstart_success_KH270) { reqInit(Machine_t::Kh270); - uint8_t buffer[] = {static_cast(AYAB_API::reqStart), 0, 10, 1, 0x36}; - EXPECT_CALL(*knitterMock, startKnitting); - expected_write_onPacketReceived(buffer, sizeof(buffer), false); + uint8_t buffer[] = {static_cast(API_t::reqStart), 0, 10, 1, 0x36}; + EXPECT_CALL(*opKnitMock, startKnitting); + expect_write(true); + com->h_reqStart(buffer, sizeof(buffer)); // test expectations without destroying instance - ASSERT_TRUE(Mock::VerifyAndClear(knitterMock)); + ASSERT_TRUE(Mock::VerifyAndClear(opKnitMock)); } TEST_F(ComTest, test_reqinfo) { - uint8_t buffer[] = {static_cast(AYAB_API::reqInfo)}; - expected_write_onPacketReceived(buffer, sizeof(buffer), true); + expect_write(true); + com->h_reqInfo(); } TEST_F(ComTest, test_helpCmd) { - uint8_t buffer[] = {static_cast(AYAB_API::helpCmd)}; + uint8_t buffer[] = {static_cast(API_t::helpCmd)}; expected_write_onPacketReceived(buffer, sizeof(buffer), false); } TEST_F(ComTest, test_sendCmd) { - uint8_t buffer[] = {static_cast(AYAB_API::sendCmd)}; + uint8_t buffer[] = {static_cast(API_t::sendCmd)}; expected_write_onPacketReceived(buffer, sizeof(buffer), false); } TEST_F(ComTest, test_beepCmd) { - uint8_t buffer[] = {static_cast(AYAB_API::beepCmd)}; + uint8_t buffer[] = {static_cast(API_t::beepCmd)}; expected_write_onPacketReceived(buffer, sizeof(buffer), true); EXPECT_CALL(*arduinoMock, millis).WillOnce(Return(0U)); - beeper->schedule(); + beeper->update(); EXPECT_CALL(*arduinoMock, analogWrite(PIEZO_PIN, BEEP_ON_DUTY)); EXPECT_CALL(*arduinoMock, millis).WillOnce(Return(1U)); - beeper->schedule(); + beeper->update(); } TEST_F(ComTest, test_setSingleCmd) { - uint8_t buffer[] = {static_cast(AYAB_API::setSingleCmd), 0, 0}; + uint8_t buffer[] = {static_cast(API_t::setSingleCmd), 0, 0}; expected_write_onPacketReceived(buffer, sizeof(buffer), true); } TEST_F(ComTest, test_setAllCmd) { - uint8_t buffer[] = {static_cast(AYAB_API::setAllCmd), 0, 0}; + uint8_t buffer[] = {static_cast(API_t::setAllCmd), 0, 0}; expected_write_onPacketReceived(buffer, sizeof(buffer), true); } TEST_F(ComTest, test_readEOLsensorsCmd) { - uint8_t buffer[] = {static_cast(AYAB_API::readEOLsensorsCmd)}; + uint8_t buffer[] = {static_cast(API_t::readEOLsensorsCmd)}; EXPECT_CALL(*arduinoMock, analogRead(EOL_PIN_L)); EXPECT_CALL(*arduinoMock, analogRead(EOL_PIN_R)); expected_write_onPacketReceived(buffer, sizeof(buffer), false); } TEST_F(ComTest, test_readEncodersCmd) { - uint8_t buffer[] = {static_cast(AYAB_API::readEncodersCmd)}; + uint8_t buffer[] = {static_cast(API_t::readEncodersCmd)}; EXPECT_CALL(*arduinoMock, digitalRead(ENC_PIN_A)); EXPECT_CALL(*arduinoMock, digitalRead(ENC_PIN_B)); EXPECT_CALL(*arduinoMock, digitalRead(ENC_PIN_C)); @@ -243,104 +256,81 @@ TEST_F(ComTest, test_readEncodersCmd) { } TEST_F(ComTest, test_autoReadCmd) { - uint8_t buffer[] = {static_cast(AYAB_API::autoReadCmd)}; + uint8_t buffer[] = {static_cast(API_t::autoReadCmd)}; expected_write_onPacketReceived(buffer, sizeof(buffer), true); } TEST_F(ComTest, test_autoTestCmd) { - uint8_t buffer[] = {static_cast(AYAB_API::autoTestCmd)}; + uint8_t buffer[] = {static_cast(API_t::autoTestCmd)}; expected_write_onPacketReceived(buffer, sizeof(buffer), true); } +/* TEST_F(ComTest, test_stopCmd) { - uint8_t buffer[] = {static_cast(AYAB_API::stopCmd)}; + uint8_t buffer[] = {static_cast(API_t::stopCmd)}; com->onPacketReceived(buffer, sizeof(buffer)); } +*/ +/* TEST_F(ComTest, test_quitCmd) { - uint8_t buffer[] = {static_cast(AYAB_API::quitCmd)}; - EXPECT_CALL(*knitterMock, setUpInterrupt); - EXPECT_CALL(*fsmMock, setState(OpState::init)); - com->onPacketReceived(buffer, sizeof(buffer)); + EXPECT_CALL(*controllerMock, setState(opInit)); + EXPECT_CALL(*opKnitMock, init); + com->h_quitCmd(); // test expectations without destroying instance - ASSERT_TRUE(Mock::VerifyAndClear(knitterMock)); - ASSERT_TRUE(Mock::VerifyAndClear(fsmMock)); -} - -TEST_F(ComTest, test_unrecognized) { - uint8_t buffer[] = {0xFF}; - com->onPacketReceived(buffer, sizeof(buffer)); + ASSERT_TRUE(Mock::VerifyAndClear(opKnitMock)); + ASSERT_TRUE(Mock::VerifyAndClear(controllerMock)); } +*/ TEST_F(ComTest, test_cnfline_kh910) { // dummy pattern uint8_t pattern[] = {1}; // message for machine with 200 needles - uint8_t buffer[30] = {static_cast(AYAB_API::cnfLine) /* 0x42 */, - 0, - 0, - 1, - 0xDE, - 0xAD, - 0xBE, - 0xEF, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, + uint8_t buffer[30] = {static_cast(API_t::cnfLine) /* 0x42 */, + 0, 0, 1, + 0xDE, 0xAD, 0xBE, 0xEF, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xA7}; // CRC8 - // start KH910 job - knitterMock->initMachine(Machine_t::Kh910); - knitterMock->startKnitting(0, 199, pattern, false); + // start job + reqInit(Machine_t::Kh910); + opKnitMock->begin(); + opKnitMock->startKnitting(0, 199, pattern, false); // first call increments line number to zero, not accepted - EXPECT_CALL(*knitterMock, setNextLine).WillOnce(Return(false)); - EXPECT_CALL(*knitterMock, setLastLine).Times(0); - com->onPacketReceived(buffer, sizeof(buffer)); + EXPECT_CALL(*opKnitMock, setNextLine).WillOnce(Return(false)); + EXPECT_CALL(*opKnitMock, setLastLine).Times(0); + com->h_cnfLine(buffer, sizeof(buffer)); // second call Line accepted, last line - EXPECT_CALL(*knitterMock, setNextLine).WillOnce(Return(true)); - EXPECT_CALL(*knitterMock, setLastLine).Times(1); - com->onPacketReceived(buffer, sizeof(buffer)); + EXPECT_CALL(*opKnitMock, setNextLine).WillOnce(Return(true)); + EXPECT_CALL(*opKnitMock, setLastLine).Times(1); + com->h_cnfLine(buffer, sizeof(buffer)); // not last line buffer[3] = 0x00; buffer[29] = 0xC0; - EXPECT_CALL(*knitterMock, setNextLine).WillOnce(Return(true)); - EXPECT_CALL(*knitterMock, setLastLine).Times(0); - com->onPacketReceived(buffer, sizeof(buffer)); + EXPECT_CALL(*opKnitMock, setNextLine).WillOnce(Return(true)); + EXPECT_CALL(*opKnitMock, setLastLine).Times(0); + com->h_cnfLine(buffer, sizeof(buffer)); // checksum wrong - EXPECT_CALL(*knitterMock, setNextLine).Times(0); + EXPECT_CALL(*opKnitMock, setNextLine).Times(0); buffer[29]--; - com->onPacketReceived(buffer, sizeof(buffer)); + com->h_cnfLine(buffer, sizeof(buffer)); // not enough bytes in buffer - EXPECT_CALL(*knitterMock, setNextLine).Times(0); - com->onPacketReceived(buffer, sizeof(buffer) - 1); + EXPECT_CALL(*opKnitMock, setNextLine).Times(0); + com->h_cnfLine(buffer, sizeof(buffer) - 1); // test expectations without destroying instance - ASSERT_TRUE(Mock::VerifyAndClear(knitterMock)); + ASSERT_TRUE(Mock::VerifyAndClear(opKnitMock)); } /* @@ -351,21 +341,31 @@ TEST_F(ComTest, test_cnfline_kh270) { // message for KH270 // CRC8 calculated with // http://tomeko.net/online_tools/crc8.php?lang=en - uint8_t buffer[20] = {static_cast(AYAB_API::cnfLine), 0, 0, 1, + uint8_t buffer[20] = {static_cast(API_t::cnfLine), + 0, 0, 1, 0xDE, 0xAD, 0xBE, 0xEF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xab}; // CRC8 - // start KH270 job - knitterMock->startOperation(Kh270, 0, 113, pattern, false); - com->onPacketReceived(buffer, sizeof(buffer)); + 0xA7}; // CRC8 + + // start job + reqInit(Machine_t::Kh270); + opKnitMock->begin(); + opKnitMock->startKnitting(0, 113, pattern, false); + + // Last line accepted + EXPECT_CALL(*opKnitMock, setNextLine).WillOnce(Return(true)); + EXPECT_CALL(*opKnitMock, setLastLine).Times(1); + com->h_cnfLine(buffer, sizeof(buffer)); } */ +/* TEST_F(ComTest, test_debug) { - uint8_t buffer[] = {static_cast(AYAB_API::debug)}; + uint8_t buffer[] = {static_cast(API_t::debug)}; com->onPacketReceived(buffer, sizeof(buffer)); } +*/ TEST_F(ComTest, test_update) { //EXPECT_CALL(*serialMock, available); @@ -380,13 +380,13 @@ TEST_F(ComTest, test_send) { TEST_F(ComTest, test_sendMsg1) { expect_write(true); - com->sendMsg(AYAB_API::testRes, "abc"); + com->sendMsg(API_t::testRes, "abc"); } TEST_F(ComTest, test_sendMsg2) { char buf[] = "abc\0"; expect_write(true); - com->sendMsg(AYAB_API::testRes, buf); + com->sendMsg(API_t::testRes, buf); } TEST_F(ComTest, test_send_reqLine) { @@ -397,6 +397,13 @@ TEST_F(ComTest, test_send_reqLine) { TEST_F(ComTest, test_send_indState) { EXPECT_CALL(*arduinoMock, analogRead(EOL_PIN_L)); EXPECT_CALL(*arduinoMock, analogRead(EOL_PIN_R)); + EXPECT_CALL(*controllerMock, getState).WillOnce(Return(opInit)); + EXPECT_CALL(*controllerMock, getCarriage); + EXPECT_CALL(*controllerMock, getPosition); + EXPECT_CALL(*controllerMock, getDirection); expect_write(true); - com->send_indState(Carriage::Knit, 0, ErrorCode::success); + com->send_indState(Err_t::Success); + + // test expectations without destroying instance + ASSERT_TRUE(Mock::VerifyAndClear(controllerMock)); } diff --git a/test/test_controller.cpp b/test/test_controller.cpp new file mode 100644 index 000000000..163803e0b --- /dev/null +++ b/test/test_controller.cpp @@ -0,0 +1,299 @@ +/*!` + * \file test_controller.cpp + * + * This file is part of AYAB. + * + * AYAB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * AYAB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with AYAB. If not, see . + * + * Original Work Copyright 2013 Christian Obersteiner, Andreas Müller + * Modified Work Copyright 2020-3 Sturla Lange, Tom Price + * http://ayab-knitting.com + */ + +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +using ::testing::_; +using ::testing::AtLeast; +using ::testing::Mock; +using ::testing::Return; +using ::testing::Test; + +extern Controller *controller; +extern OpKnit *opKnit; + +extern BeeperMock *beeper; +extern ComMock *com; +extern EncodersMock *encoders; +extern SolenoidsMock *solenoids; + +extern OpIdleMock *opIdle; +extern OpInitMock *opInit; +extern OpReadyMock *opReady; +extern OpTestMock *opTest; +extern OpErrorMock *opError; + +class ControllerTest : public ::testing::Test { +protected: + void SetUp() override { + arduinoMock = arduinoMockInstance(); + serialMock = serialMockInstance(); + + // pointers to global instances + beeperMock = beeper; + comMock = com; + encodersMock = encoders; + solenoidsMock = solenoids; + + opIdleMock = opIdle; + opInitMock = opInit; + opReadyMock = opReady; + opTestMock = opTest; + opErrorMock = opError; + + // The global instance does not get destroyed at the end of each test. + // Ordinarily the mock instance would be local and such behaviour would + // cause a memory leak. We must notify the test that this is not the case. + Mock::AllowLeak(beeperMock); + Mock::AllowLeak(comMock); + Mock::AllowLeak(encodersMock); + Mock::AllowLeak(solenoidsMock); + + Mock::AllowLeak(opIdleMock); + Mock::AllowLeak(opInitMock); + Mock::AllowLeak(opReadyMock); + Mock::AllowLeak(opTestMock); + Mock::AllowLeak(opErrorMock); + + // start in state `OpIdle` + controller->init(); + expect_knit_init(); + opKnit->init(); + controller->setMachineType(Machine_t::Kh910); + expected_isready(Direction_t::NoDirection, Direction_t::NoDirection, 0); + } + + void TearDown() override { + releaseArduinoMock(); + releaseSerialMock(); + } + + ArduinoMock *arduinoMock; + BeeperMock *beeperMock; + ComMock *comMock; + EncodersMock *encodersMock; + SerialMock *serialMock; + SolenoidsMock *solenoidsMock; + + OpIdleMock *opIdleMock; + OpInitMock *opInitMock; + OpReadyMock *opReadyMock; + OpTestMock *opTestMock; + OpErrorMock *opErrorMock; + + void expect_knit_init() { + EXPECT_CALL(*arduinoMock, pinMode(ENC_PIN_A, INPUT)); + EXPECT_CALL(*arduinoMock, pinMode(ENC_PIN_B, INPUT)); + EXPECT_CALL(*arduinoMock, pinMode(ENC_PIN_C, INPUT)); + EXPECT_CALL(*arduinoMock, pinMode(LED_PIN_A, OUTPUT)); + EXPECT_CALL(*arduinoMock, pinMode(LED_PIN_B, OUTPUT)); + EXPECT_CALL(*arduinoMock, digitalWrite(LED_PIN_A, HIGH)); + EXPECT_CALL(*arduinoMock, digitalWrite(LED_PIN_B, HIGH)); + EXPECT_CALL(*solenoidsMock, init); + } + + void expect_reqLine() { + EXPECT_CALL(*comMock, send_reqLine); + } + + void expect_indState() { + EXPECT_CALL(*comMock, send_indState); + } + + void expected_isready(Direction_t dir, Direction_t hall, uint8_t position) { + controller->m_direction = dir; + controller->m_hallActive = hall; + controller->m_position = position; + } + + void expected_state(OpInterface *state) { + controller->setState(state); + controller->update(); + } + + void expected_update() { + EXPECT_CALL(*encodersMock, getPosition).Times(1); + EXPECT_CALL(*encodersMock, getDirection).Times(1); + EXPECT_CALL(*encodersMock, getHallActive).Times(1); + EXPECT_CALL(*encodersMock, getBeltShift).Times(1); + EXPECT_CALL(*encodersMock, getCarriage).Times(1); + controller->update(); + + // test expectations without destroying instance + ASSERT_TRUE(Mock::VerifyAndClear(encodersMock)); + } + + void expected_update_idle() { + // starts in state `OpIdle` + ASSERT_EQ(controller->getState(), opIdleMock); + + EXPECT_CALL(*opIdleMock, update); + expected_update(); + + // test expectations without destroying instance + ASSERT_TRUE(Mock::VerifyAndClear(opIdleMock)); + } + + void expected_update_init() { + // starts in state `OpInit` + ASSERT_EQ(controller->getState(), opInitMock); + + EXPECT_CALL(*opInitMock, update); + expected_update(); + + // test expectations without destroying instance + ASSERT_TRUE(Mock::VerifyAndClear(opInitMock)); + } + + void expected_update_ready() { + // starts in state `OpReady` + ASSERT_EQ(controller->getState(), opReadyMock); + + EXPECT_CALL(*opReadyMock, update); + expected_update(); + + // test expectations without destroying instance + ASSERT_TRUE(Mock::VerifyAndClear(opReadyMock)); + } + + void expected_update_knit() { + // starts in state `OpKnit` + ASSERT_EQ(controller->getState(), opKnit); + + expected_update(); + } + + void expected_update_test() { + // starts in state `OpTest` + ASSERT_EQ(controller->getState(), opTestMock); + + EXPECT_CALL(*opTestMock, update); + expected_update(); + + // test expectations without destroying instance + ASSERT_TRUE(Mock::VerifyAndClear(opTestMock)); + } + + void expect_first_knit() { + EXPECT_CALL(*arduinoMock, delay(START_KNITTING_DELAY)); + EXPECT_CALL(*beeperMock, finishedLine); + expect_reqLine(); + } +}; + +TEST_F(ControllerTest, test_setState) { + controller->setState(opInitMock); + + EXPECT_CALL(*opIdle, end); + EXPECT_CALL(*opInit, begin); + expected_update_idle(); + ASSERT_EQ(controller->getState(), opInitMock); + + EXPECT_CALL(*opInitMock, state).WillOnce(Return(OpState_t::Init)); + controller->getState()->state(); + + // test expectations without destroying instance + ASSERT_TRUE(Mock::VerifyAndClear(opIdleMock)); + ASSERT_TRUE(Mock::VerifyAndClear(opInitMock)); +} + +TEST_F(ControllerTest, test_getHallActive) { + controller->init(); + ASSERT_EQ(controller->getHallActive(), Direction_t::NoDirection); +} + +TEST_F(ControllerTest, test_ready_state) { + controller->setState(opReadyMock); + expected_update_idle(); + ASSERT_EQ(controller->getState(), opReadyMock); + + EXPECT_CALL(*opReadyMock, state).WillOnce(Return(OpState_t::Ready)); + controller->getState()->state(); +} + +TEST_F(ControllerTest, test_update_knit) { + // get to state `OpReady` + controller->setState(opReadyMock); + expected_update_idle(); + + // get to state `OpKnit` + controller->setState(opKnit); + expected_update_ready(); + ASSERT_EQ(controller->getState(), opKnit); + + // now in state `OpKnit` + expect_first_knit(); + expected_update_knit(); + + // test expectations without destroying instance + ASSERT_TRUE(Mock::VerifyAndClear(beeperMock)); + ASSERT_TRUE(Mock::VerifyAndClear(comMock)); +} + +TEST_F(ControllerTest, test_update_test) { + // get in state `OpTest` + controller->setState(opTestMock); + expected_update_idle(); + + // now in state `OpTest` + expected_update_test(); + ASSERT_EQ(controller->getState(), opTestMock); + + EXPECT_CALL(*opTestMock, state).WillOnce(Return(OpState_t::Test)); + controller->getState()->state(); + + // now quit test + controller->setState(opInitMock); + EXPECT_CALL(*opTestMock, end); + EXPECT_CALL(*opInitMock, begin); + expected_update_test(); + ASSERT_EQ(controller->getState(), opInitMock); + + // test expectations without destroying instance + ASSERT_TRUE(Mock::VerifyAndClear(opInitMock)); + ASSERT_TRUE(Mock::VerifyAndClear(opTestMock)); +} + +TEST_F(ControllerTest, test_error_state) { + controller->setState(opErrorMock); + expected_update_idle(); + ASSERT_EQ(controller->getState(), opErrorMock); + + EXPECT_CALL(*opErrorMock, state).WillOnce(Return(OpState_t::Error)); + controller->getState()->state(); +} diff --git a/test/test_encoders.cpp b/test/test_encoders.cpp index 599078caf..6aae8441f 100644 --- a/test/test_encoders.cpp +++ b/test/test_encoders.cpp @@ -1,4 +1,4 @@ -/*!`s +/*! * \file test_encoders.cpp * * This file is part of AYAB. @@ -17,7 +17,7 @@ * along with AYAB. If not, see . * * Original Work Copyright 2013 Christian Obersteiner, Andreas Müller - * Modified Work Copyright 2020 Sturla Lange, Tom Price + * Modified Work Copyright 2020-3 Sturla Lange, Tom Price * http://ayab-knitting.com */ @@ -45,264 +45,376 @@ class EncodersTest : public ::testing::Test { }; TEST_F(EncodersTest, test_encA_rising_not_in_front) { + // Create a falling edge + EXPECT_CALL(*arduinoMock, digitalRead(ENC_PIN_A)).WillOnce(Return(false)); // We should not enter the falling function EXPECT_CALL(*arduinoMock, analogRead(EOL_PIN_R)).Times(0); - // Create a rising edge - EXPECT_CALL(*arduinoMock, digitalRead(ENC_PIN_A)) - .WillOnce(Return(false)) - .WillOnce(Return(true)); - // We have not entered the rising function yet - EXPECT_CALL(*arduinoMock, analogRead(EOL_PIN_L)).Times(0); - encoders->encA_interrupt(); - // Enter rising function, direction is right + encoders->isr(); + + // Enter rising function, direction is Right + EXPECT_CALL(*arduinoMock, digitalRead(ENC_PIN_A)).WillOnce(Return(true)); EXPECT_CALL(*arduinoMock, digitalRead(ENC_PIN_B)).WillOnce(Return(true)); // Not in front of Left Hall Sensor EXPECT_CALL(*arduinoMock, analogRead(EOL_PIN_L)) .WillOnce(Return(FILTER_L_MIN[static_cast(encoders->getMachineType())])); - encoders->encA_interrupt(); + encoders->isr(); + ASSERT_EQ(encoders->getDirection(), Direction_t::Right); ASSERT_EQ(encoders->getPosition(), 0x01); ASSERT_EQ(encoders->getCarriage(), Carriage_t::NoCarriage); + + // Enter falling function, direction is Right + EXPECT_CALL(*arduinoMock, digitalRead(ENC_PIN_A)).WillOnce(Return(false)); + EXPECT_CALL(*arduinoMock, digitalRead(ENC_PIN_B)).WillOnce(Return(false)); + // In front of Right Hall Sensor + EXPECT_CALL(*arduinoMock, analogRead(EOL_PIN_R)) + .WillOnce(Return(FILTER_R_MIN[static_cast(encoders->getMachineType())] - 1)); + EXPECT_CALL(*arduinoMock, digitalRead(ENC_PIN_C)); + encoders->isr(); + + ASSERT_EQ(encoders->getDirection(), Direction_t::Right); + ASSERT_EQ(encoders->getPosition(), END_RIGHT_MINUS_OFFSET[static_cast(encoders->getMachineType())]); + ASSERT_EQ(encoders->getCarriage(), Carriage_t::Knit); + + // Enter rising function, direction is Right + EXPECT_CALL(*arduinoMock, digitalRead(ENC_PIN_A)).WillOnce(Return(true)); + EXPECT_CALL(*arduinoMock, digitalRead(ENC_PIN_B)).WillOnce(Return(true)); + // In front of Left Hall Sensor + EXPECT_CALL(*arduinoMock, analogRead(EOL_PIN_L)) + .WillOnce(Return(FILTER_L_MAX[static_cast(encoders->getMachineType())] + 1)); + EXPECT_CALL(*arduinoMock, digitalRead(ENC_PIN_C)); + encoders->isr(); + + ASSERT_EQ(encoders->getDirection(), Direction_t::Right); + ASSERT_EQ(encoders->getPosition(), END_LEFT_PLUS_OFFSET[static_cast(encoders->getMachineType())]); + ASSERT_EQ(encoders->getCarriage(), Carriage_t::Knit); } TEST_F(EncodersTest, test_encA_rising_in_front_notKH270) { ASSERT_FALSE(encoders->getMachineType() == Machine_t::Kh270); ASSERT_EQ(encoders->getCarriage(), Carriage_t::NoCarriage); + + // Create a falling edge + EXPECT_CALL(*arduinoMock, digitalRead(ENC_PIN_A)).WillOnce(Return(false)); // We should not enter the falling function EXPECT_CALL(*arduinoMock, analogRead(EOL_PIN_R)).Times(0); - // Create a rising edge - EXPECT_CALL(*arduinoMock, digitalRead(ENC_PIN_A)).WillOnce(Return(false)); - // We have not entered the rising function yet - EXPECT_CALL(*arduinoMock, analogRead(EOL_PIN_L)).Times(0); + encoders->isr(); - encoders->encA_interrupt(); - - // Create a rising edge + // Enter rising function, direction is Right EXPECT_CALL(*arduinoMock, digitalRead(ENC_PIN_A)).WillOnce(Return(true)); - // Enter rising function, direction is right EXPECT_CALL(*arduinoMock, digitalRead(ENC_PIN_B)).WillOnce(Return(true)); // In front of Left Hall Sensor EXPECT_CALL(*arduinoMock, analogRead(EOL_PIN_L)) .WillOnce(Return(FILTER_L_MIN[static_cast(encoders->getMachineType())] - 1)); // BeltShift is regular EXPECT_CALL(*arduinoMock, digitalRead(ENC_PIN_C)).WillOnce(Return(true)); - - encoders->encA_interrupt(); + encoders->isr(); ASSERT_EQ(encoders->getDirection(), Direction_t::Right); ASSERT_EQ(encoders->getHallActive(), Direction_t::Left); - ASSERT_EQ(encoders->getPosition(), END_OFFSET[static_cast(encoders->getMachineType())]); + ASSERT_EQ(encoders->getPosition(), END_LEFT_PLUS_OFFSET[static_cast(encoders->getMachineType())]); ASSERT_EQ(encoders->getCarriage(), Carriage_t::Lace); ASSERT_EQ(encoders->getBeltShift(), BeltShift::Regular); + + // Enter falling function, direction is Right + EXPECT_CALL(*arduinoMock, digitalRead(ENC_PIN_A)).WillOnce(Return(false)); + EXPECT_CALL(*arduinoMock, digitalRead(ENC_PIN_B)).WillOnce(Return(false)); + // Not in front of Right Hall Sensor + EXPECT_CALL(*arduinoMock, analogRead(EOL_PIN_R)) + .WillOnce(Return(FILTER_R_MIN[static_cast(encoders->getMachineType())])); + encoders->isr(); + + encoders->m_position = 0; + // Enter rising function, direction is Right + EXPECT_CALL(*arduinoMock, digitalRead(ENC_PIN_A)).WillOnce(Return(true)); + EXPECT_CALL(*arduinoMock, digitalRead(ENC_PIN_B)).WillOnce(Return(true)); + // In front of Left Hall Sensor + EXPECT_CALL(*arduinoMock, analogRead(EOL_PIN_L)) + .WillOnce(Return(FILTER_L_MAX[static_cast(encoders->getMachineType())] + 1)); + EXPECT_CALL(*arduinoMock, digitalRead(ENC_PIN_C)); + encoders->isr(); + + ASSERT_EQ(encoders->getDirection(), Direction_t::Right); + ASSERT_EQ(encoders->getPosition(), END_LEFT_PLUS_OFFSET[static_cast(encoders->getMachineType())]); + ASSERT_EQ(encoders->getCarriage(), Carriage_t::Knit); } TEST_F(EncodersTest, test_encA_rising_in_front_KH270) { encoders->init(Machine_t::Kh270); - ASSERT_TRUE(encoders->getMachineType() == Machine_t::Kh270); + ASSERT_EQ(encoders->getMachineType(), Machine_t::Kh270); + + // Create a rising edge + EXPECT_CALL(*arduinoMock, digitalRead(ENC_PIN_A)).WillOnce(Return(false)); // We should not enter the falling function EXPECT_CALL(*arduinoMock, analogRead(EOL_PIN_R)).Times(0); - // Create a rising edge + encoders->isr(); + + // Enter rising function, direction is Right + EXPECT_CALL(*arduinoMock, digitalRead(ENC_PIN_A)).WillOnce(Return(true)); + EXPECT_CALL(*arduinoMock, digitalRead(ENC_PIN_B)).WillOnce(Return(true)); + // In front of Left Hall Sensor + EXPECT_CALL(*arduinoMock, analogRead(EOL_PIN_L)) + .WillOnce(Return(FILTER_L_MIN[static_cast(Machine_t::Kh270)] - 1)); + // BeltShift is ignored + EXPECT_CALL(*arduinoMock, digitalRead(ENC_PIN_C)).Times(0); + encoders->isr(); + + ASSERT_EQ(encoders->getDirection(), Direction_t::Right); + ASSERT_EQ(encoders->getHallActive(), Direction_t::Left); + ASSERT_EQ(encoders->getPosition(), END_LEFT_PLUS_OFFSET[static_cast(Machine_t::Kh270)]); + ASSERT_EQ(encoders->getCarriage(), Carriage_t::Knit); + ASSERT_EQ(encoders->getBeltShift(), BeltShift::Unknown); + + // Enter falling function EXPECT_CALL(*arduinoMock, digitalRead(ENC_PIN_A)).WillOnce(Return(false)); - // We have not entered the rising function yet - EXPECT_CALL(*arduinoMock, analogRead(EOL_PIN_L)).Times(0); + EXPECT_CALL(*arduinoMock, digitalRead(ENC_PIN_B)).WillOnce(Return(false)); + // In front of Left Hall Sensor + EXPECT_CALL(*arduinoMock, analogRead(EOL_PIN_R)) + .WillOnce(Return(FILTER_R_MIN[static_cast(encoders->getMachineType())] - 1)); + // BeltShift is ignored + EXPECT_CALL(*arduinoMock, digitalRead(ENC_PIN_C)).Times(0); + encoders->isr(); - encoders->encA_interrupt(); + ASSERT_EQ(encoders->getDirection(), Direction_t::Right); + ASSERT_EQ(encoders->getHallActive(), Direction_t::Right); + ASSERT_EQ(encoders->getPosition(), END_RIGHT_MINUS_OFFSET[static_cast(Machine_t::Kh270)]); + ASSERT_EQ(encoders->getCarriage(), Carriage_t::Knit); + ASSERT_EQ(encoders->getBeltShift(), BeltShift::Unknown); // Create a rising edge EXPECT_CALL(*arduinoMock, digitalRead(ENC_PIN_A)).WillOnce(Return(true)); - // Enter rising function, direction is right EXPECT_CALL(*arduinoMock, digitalRead(ENC_PIN_B)).WillOnce(Return(true)); - // In front of Left Hall Sensor - EXPECT_CALL(*arduinoMock, analogRead(EOL_PIN_L)) - .WillOnce(Return(FILTER_L_MIN[static_cast(encoders->getMachineType())] - 1)); - // BeltShift is regular - EXPECT_CALL(*arduinoMock, digitalRead(ENC_PIN_C)).WillOnce(Return(true)); + // We will not enter the rising function + EXPECT_CALL(*arduinoMock, analogRead(EOL_PIN_L)).Times(0); + encoders->isr(); +} - encoders->encA_interrupt(); +TEST_F(EncodersTest, test_encA_rising_after_KH270) { + encoders->init(Machine_t::Kh270); + ASSERT_EQ(encoders->getMachineType(), Machine_t::Kh270); + + // Create a falling edge + EXPECT_CALL(*arduinoMock, digitalRead(ENC_PIN_A)).WillOnce(Return(false)); + // We should not enter the falling function + EXPECT_CALL(*arduinoMock, analogRead(EOL_PIN_R)).Times(0); + encoders->isr(); + + // Enter rising function, direction is Right + EXPECT_CALL(*arduinoMock, digitalRead(ENC_PIN_A)).WillOnce(Return(true)); + EXPECT_CALL(*arduinoMock, digitalRead(ENC_PIN_B)).WillOnce(Return(true)); + // After Left Hall Sensor + EXPECT_CALL(*arduinoMock, analogRead(EOL_PIN_L)) + .WillOnce(Return(FILTER_L_MAX[static_cast(encoders->getMachineType())] + 1)); + // BeltShift is ignored + EXPECT_CALL(*arduinoMock, digitalRead(ENC_PIN_C)).Times(0); + encoders->isr(); ASSERT_EQ(encoders->getDirection(), Direction_t::Right); ASSERT_EQ(encoders->getHallActive(), Direction_t::Left); - ASSERT_EQ(encoders->getPosition(), END_OFFSET[static_cast(encoders->getMachineType())]); + ASSERT_EQ(encoders->getPosition(), END_LEFT_PLUS_OFFSET[static_cast(Machine_t::Kh270)] + MAGNET_DISTANCE_270); ASSERT_EQ(encoders->getCarriage(), Carriage_t::Knit); - ASSERT_EQ(encoders->getBeltShift(), BeltShift::Regular); + ASSERT_EQ(encoders->getBeltShift(), BeltShift::Unknown); } -TEST_F(EncodersTest, test_encA_rising_in_front_G_carriage) { +TEST_F(EncodersTest, test_G_carriage) { // Create a rising edge EXPECT_CALL(*arduinoMock, digitalRead(ENC_PIN_A)).WillOnce(Return(true)); - // Enter rising function, direction is right - EXPECT_CALL(*arduinoMock, digitalRead(ENC_PIN_B)).WillOnce(Return(true)).WillOnce(Return(false)); + // Enter rising function, direction is Right + EXPECT_CALL(*arduinoMock, digitalRead(ENC_PIN_B)).WillOnce(Return(true)); // In front of Left Hall Sensor EXPECT_CALL(*arduinoMock, analogRead(EOL_PIN_L)) .WillOnce(Return(FILTER_L_MAX[static_cast(encoders->getMachineType())] + 1)); // BeltShift is regular EXPECT_CALL(*arduinoMock, digitalRead(ENC_PIN_C)).WillOnce(Return(true)); - - encoders->encA_interrupt(); + encoders->isr(); ASSERT_EQ(encoders->getCarriage(), Carriage_t::Knit); - // Create a falling edge + // Enter falling function, direction is Right EXPECT_CALL(*arduinoMock, digitalRead(ENC_PIN_A)).WillOnce(Return(false)); + EXPECT_CALL(*arduinoMock, digitalRead(ENC_PIN_B)).WillOnce(Return(false)); + // In front of Right Hall Sensor EXPECT_CALL(*arduinoMock, analogRead(EOL_PIN_R)) .WillOnce(Return(FILTER_R_MAX[static_cast(encoders->getMachineType())] + 1)); EXPECT_CALL(*arduinoMock, digitalRead(ENC_PIN_C)); - encoders->encA_interrupt(); - // Create a rising edge + encoders->isr(); + + // Enter rising function, direction is Right EXPECT_CALL(*arduinoMock, digitalRead(ENC_PIN_A)).WillOnce(Return(true)); - // Enter rising function, direction is right EXPECT_CALL(*arduinoMock, digitalRead(ENC_PIN_B)).WillOnce(Return(true)); // In front of Left Hall Sensor EXPECT_CALL(*arduinoMock, analogRead(EOL_PIN_L)) .WillOnce(Return(FILTER_L_MIN[static_cast(encoders->getMachineType())] - 1)); - - encoders->encA_interrupt(); + encoders->isr(); ASSERT_EQ(encoders->getCarriage(), Carriage_t::Garter); + + // Create a falling edge, then a rising edge, direction is Right: + EXPECT_CALL(*arduinoMock, digitalRead(ENC_PIN_A)).WillOnce(Return(false)).WillOnce(Return(true)); + EXPECT_CALL(*arduinoMock, digitalRead(ENC_PIN_B)).WillOnce(Return(false)).WillOnce(Return(true)); + // Not in front of Right Hall Sensor + EXPECT_CALL(*arduinoMock, analogRead(EOL_PIN_R)) + .WillOnce(Return(FILTER_R_MIN[static_cast(encoders->getMachineType())])); + // We will not enter the rising function + EXPECT_CALL(*arduinoMock, analogRead(EOL_PIN_L)).Times(0); + encoders->isr(); + encoders->isr(); + + // Create a falling edge, direction is Right: + EXPECT_CALL(*arduinoMock, digitalRead(ENC_PIN_A)).WillOnce(Return(false)); + EXPECT_CALL(*arduinoMock, digitalRead(ENC_PIN_B)).WillOnce(Return(false)); + // In front of Right Hall Sensor + EXPECT_CALL(*arduinoMock, analogRead(EOL_PIN_R)) + .WillOnce(Return(FILTER_R_MIN[static_cast(encoders->getMachineType())] - 1)); + EXPECT_CALL(*arduinoMock, digitalRead(ENC_PIN_C)); + encoders->isr(); } TEST_F(EncodersTest, test_encA_falling_not_in_front) { - // Create a falling edge - EXPECT_CALL(*arduinoMock, digitalRead(ENC_PIN_A)) - .WillOnce(Return(true)) - .WillOnce(Return(true)); - // We have not entered the falling function yet - EXPECT_CALL(*arduinoMock, analogRead(EOL_PIN_R)).Times(0); - - // Enter rising function, direction is right - EXPECT_CALL(*arduinoMock, digitalRead(ENC_PIN_B)).WillOnce(Return(true)).WillOnce(Return(false)); + // Rising edge, direction is Right + EXPECT_CALL(*arduinoMock, digitalRead(ENC_PIN_A)).WillOnce(Return(true)); + EXPECT_CALL(*arduinoMock, digitalRead(ENC_PIN_B)).WillOnce(Return(true)); // Not in front of Left Hall Sensor EXPECT_CALL(*arduinoMock, analogRead(EOL_PIN_L)) .WillOnce(Return(FILTER_L_MIN[static_cast(encoders->getMachineType())])); - encoders->encA_interrupt(); - encoders->encA_interrupt(); + encoders->isr(); + // Create rising edge + EXPECT_CALL(*arduinoMock, digitalRead(ENC_PIN_A)).WillOnce(Return(true)); + // Rising function not entered + EXPECT_CALL(*arduinoMock, digitalRead(ENC_PIN_B)).Times(0); + EXPECT_CALL(*arduinoMock, analogRead(EOL_PIN_L)).Times(0); + encoders->isr(); + + // Falling edge, direction is Left + encoders->m_position = END_LEFT[static_cast(encoders->getMachineType())] + 1; EXPECT_CALL(*arduinoMock, digitalRead(ENC_PIN_A)).WillOnce(Return(false)); + EXPECT_CALL(*arduinoMock, digitalRead(ENC_PIN_B)).WillOnce(Return(true)); + // Not in front of Right Hall Sensor EXPECT_CALL(*arduinoMock, analogRead(EOL_PIN_R)) .WillOnce(Return(FILTER_R_MIN[static_cast(encoders->getMachineType())])); - - encoders->encA_interrupt(); + encoders->isr(); } TEST_F(EncodersTest, test_encA_falling_in_front) { - // Create a falling edge - EXPECT_CALL(*arduinoMock, digitalRead(ENC_PIN_A)) - .WillOnce(Return(true)) - .WillOnce(Return(true)); - // We have not entered the falling function yet - EXPECT_CALL(*arduinoMock, analogRead(EOL_PIN_R)).Times(0); - - // Enter rising function, direction is left - EXPECT_CALL(*arduinoMock, digitalRead(ENC_PIN_B)).WillOnce(Return(false)).WillOnce(Return(false)); + // Enter rising function, direction is Left + EXPECT_CALL(*arduinoMock, digitalRead(ENC_PIN_A)).WillOnce(Return(true)); + EXPECT_CALL(*arduinoMock, digitalRead(ENC_PIN_B)).WillOnce(Return(false)); // In front of Left Hall Sensor EXPECT_CALL(*arduinoMock, analogRead(EOL_PIN_L)) .WillOnce(Return(FILTER_L_MIN[static_cast(encoders->getMachineType())])); - encoders->encA_interrupt(); - encoders->encA_interrupt(); + encoders->isr(); + // Falling edge, direction is Left EXPECT_CALL(*arduinoMock, digitalRead(ENC_PIN_A)).WillOnce(Return(false)); + EXPECT_CALL(*arduinoMock, digitalRead(ENC_PIN_B)).WillOnce(Return(false)); + // In front of Left Hall Sensor EXPECT_CALL(*arduinoMock, analogRead(EOL_PIN_R)) .WillOnce(Return(FILTER_R_MAX[static_cast(encoders->getMachineType())] + 1)); // BeltShift is shifted EXPECT_CALL(*arduinoMock, digitalRead(ENC_PIN_C)).WillOnce(Return(true)); - - encoders->encA_interrupt(); + encoders->isr(); ASSERT_EQ(encoders->getDirection(), Direction_t::Right); ASSERT_EQ(encoders->getHallActive(), Direction_t::Right); - ASSERT_EQ(encoders->getPosition(), 227); + ASSERT_EQ(encoders->getPosition(), END_RIGHT_MINUS_OFFSET[static_cast(encoders->getMachineType())]); ASSERT_EQ(encoders->getCarriage(), Carriage_t::NoCarriage); ASSERT_EQ(encoders->getBeltShift(), BeltShift::Shifted); } TEST_F(EncodersTest, test_encA_falling_at_end) { - // Create a falling edge + // Rising edge, direction is Right EXPECT_CALL(*arduinoMock, digitalRead(ENC_PIN_A)).WillOnce(Return(true)); - - // Enter rising function, direction is left - EXPECT_CALL(*arduinoMock, digitalRead(ENC_PIN_B)).WillOnce(Return(false)).WillOnce(Return(true)); + EXPECT_CALL(*arduinoMock, digitalRead(ENC_PIN_B)).WillOnce(Return(false)); // In front of Left Hall Sensor EXPECT_CALL(*arduinoMock, analogRead(EOL_PIN_L)) .WillOnce(Return(FILTER_L_MAX[static_cast(encoders->getMachineType())])); EXPECT_CALL(*arduinoMock, digitalRead(ENC_PIN_C)).Times(0); - encoders->encA_interrupt(); + encoders->isr(); + // Falling edge, direction is Left EXPECT_CALL(*arduinoMock, digitalRead(ENC_PIN_A)).WillOnce(Return(false)); + EXPECT_CALL(*arduinoMock, digitalRead(ENC_PIN_B)).WillOnce(Return(true)); EXPECT_CALL(*arduinoMock, analogRead(EOL_PIN_R)) .WillOnce(Return(FILTER_R_MAX[static_cast(encoders->getMachineType())] + 1)); EXPECT_CALL(*arduinoMock, digitalRead(ENC_PIN_C)); + encoders->isr(); - encoders->encA_interrupt(); - ASSERT_EQ(encoders->getPosition(), 227); + ASSERT_EQ(encoders->getPosition(), END_RIGHT_MINUS_OFFSET[static_cast(encoders->getMachineType())]); - uint16_t pos = 227; + uint16_t pos = END_RIGHT_MINUS_OFFSET[static_cast(encoders->getMachineType())]; while (pos < END_RIGHT[static_cast(encoders->getMachineType())]) { - // Rising + // Rising edge EXPECT_CALL(*arduinoMock, digitalRead(ENC_PIN_A)).WillOnce(Return(true)); EXPECT_CALL(*arduinoMock, digitalRead(ENC_PIN_B)).WillOnce(Return(true)); EXPECT_CALL(*arduinoMock, analogRead(EOL_PIN_L)) .WillOnce(Return(FILTER_L_MAX[static_cast(encoders->getMachineType())])); - encoders->encA_interrupt(); + encoders->isr(); + ASSERT_EQ(encoders->getPosition(), ++pos); - // Falling + // Falling edge EXPECT_CALL(*arduinoMock, digitalRead(ENC_PIN_A)).WillOnce(Return(false)); EXPECT_CALL(*arduinoMock, digitalRead(ENC_PIN_B)); EXPECT_CALL(*arduinoMock, analogRead(EOL_PIN_R)) .WillOnce(Return(FILTER_R_MAX[static_cast(encoders->getMachineType())])); - encoders->encA_interrupt(); + encoders->isr(); + ASSERT_EQ(encoders->getPosition(), pos); } ASSERT_EQ(encoders->getPosition(), pos); - // Rising + // Rising edge EXPECT_CALL(*arduinoMock, digitalRead(ENC_PIN_A)).WillOnce(Return(true)); EXPECT_CALL(*arduinoMock, digitalRead(ENC_PIN_B)).WillOnce(Return(true)); EXPECT_CALL(*arduinoMock, analogRead(EOL_PIN_L)) .WillOnce(Return(FILTER_L_MAX[static_cast(encoders->getMachineType())])); - encoders->encA_interrupt(); + encoders->isr(); + ASSERT_EQ(encoders->getPosition(), pos); } // requires FILTER_R_MIN != 0 TEST_F(EncodersTest, test_encA_falling_set_K_carriage_KH910) { - ASSERT_TRUE(encoders->getMachineType() == Machine_t::Kh910); + ASSERT_EQ(encoders->getMachineType(), Machine_t::Kh910); - // Create a rising edge + // Rising edge EXPECT_CALL(*arduinoMock, digitalRead(ENC_PIN_A)).WillOnce(Return(true)); EXPECT_CALL(*arduinoMock, digitalRead(ENC_PIN_B)); EXPECT_CALL(*arduinoMock, digitalRead(ENC_PIN_C)); EXPECT_CALL(*arduinoMock, analogRead(EOL_PIN_L)); - encoders->encA_interrupt(); + encoders->isr(); - // falling edge + // Falling edge EXPECT_CALL(*arduinoMock, digitalRead(ENC_PIN_A)).WillOnce(Return(false)); EXPECT_CALL(*arduinoMock, digitalRead(ENC_PIN_B)); EXPECT_CALL(*arduinoMock, analogRead(EOL_PIN_R)) .WillOnce(Return(FILTER_R_MIN[static_cast(encoders->getMachineType())] - 1)); EXPECT_CALL(*arduinoMock, digitalRead(ENC_PIN_C)); + encoders->isr(); - encoders->encA_interrupt(); ASSERT_EQ(encoders->getCarriage(), Carriage_t::Knit); } TEST_F(EncodersTest, test_encA_falling_not_at_end) { - // rising, direction is left + // Rising edge, direction is Left EXPECT_CALL(*arduinoMock, digitalRead(ENC_PIN_A)).WillOnce(Return(true)); EXPECT_CALL(*arduinoMock, digitalRead(ENC_PIN_B)).WillOnce(Return(false)); EXPECT_CALL(*arduinoMock, digitalRead(ENC_PIN_C)); EXPECT_CALL(*arduinoMock, analogRead(EOL_PIN_L)) .WillOnce(Return(FILTER_R_MAX[static_cast(encoders->getMachineType())] + 1)); - encoders->encA_interrupt(); - ASSERT_EQ(encoders->getPosition(), 28); + encoders->isr(); + + ASSERT_EQ(encoders->getPosition(), END_LEFT_PLUS_OFFSET[static_cast(encoders->getMachineType())]); - // falling, direction is left and pos is > 0 + // Falling edge, direction is Left, and pos is > 0 EXPECT_CALL(*arduinoMock, digitalRead(ENC_PIN_A)).WillOnce(Return(false)); EXPECT_CALL(*arduinoMock, digitalRead(ENC_PIN_B)).WillOnce(Return(false)); EXPECT_CALL(*arduinoMock, analogRead(EOL_PIN_R)) .WillOnce(Return(FILTER_R_MAX[static_cast(encoders->getMachineType())])); - encoders->encA_interrupt(); - ASSERT_EQ(encoders->getPosition(), 28); + encoders->isr(); + + ASSERT_EQ(encoders->getPosition(), END_LEFT_PLUS_OFFSET[static_cast(encoders->getMachineType())]); } TEST_F(EncodersTest, test_getPosition) { @@ -344,12 +456,15 @@ TEST_F(EncodersTest, test_init) { TEST_F(EncodersTest, test_getHallValue) { uint16_t v = encoders->getHallValue(Direction_t::NoDirection); ASSERT_EQ(v, 0u); + EXPECT_CALL(*arduinoMock, analogRead(EOL_PIN_L)); v = encoders->getHallValue(Direction_t::Left); ASSERT_EQ(v, 0u); + EXPECT_CALL(*arduinoMock, analogRead(EOL_PIN_R)); v = encoders->getHallValue(Direction_t::Right); ASSERT_EQ(v, 0u); + EXPECT_CALL(*arduinoMock, analogRead(EOL_PIN_R)).WillOnce(Return(0xbeefu)); v = encoders->getHallValue(Direction_t::Right); ASSERT_EQ(v, 0xbeefu); diff --git a/test/test_fsm.cpp b/test/test_fsm.cpp deleted file mode 100644 index c40510704..000000000 --- a/test/test_fsm.cpp +++ /dev/null @@ -1,346 +0,0 @@ -/*!` - * \file test_fsm.cpp - * - * This file is part of AYAB. - * - * AYAB is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * AYAB is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with AYAB. If not, see . - * - * Original Work Copyright 2013 Christian Obersteiner, Andreas Müller - * Modified Work Copyright 2020 Sturla Lange, Tom Price - * http://ayab-knitting.com - */ - -#include - -#include -#include -#include - -#include -#include -#include -#include -#include - -using ::testing::_; -using ::testing::AtLeast; -using ::testing::Mock; -using ::testing::Return; -using ::testing::Test; - -extern Fsm *fsm; -extern Knitter *knitter; - -extern BeeperMock *beeper; -extern ComMock *com; -extern EncodersMock *encoders; -extern SolenoidsMock *solenoids; -extern TesterMock *tester; - -// Defaults for position -const uint8_t positionPassedLeft = (END_LEFT_PLUS_OFFSET[static_cast(Machine_t::Kh910)] + GARTER_SLOP) + 1; -const uint8_t positionPassedRight = (END_RIGHT_MINUS_OFFSET[static_cast(Machine_t::Kh910)] - GARTER_SLOP) - 1; - -class FsmTest : public ::testing::Test { -protected: - void SetUp() override { - arduinoMock = arduinoMockInstance(); - serialMock = serialMockInstance(); - - // pointers to global instances - beeperMock = beeper; - comMock = com; - encodersMock = encoders; - solenoidsMock = solenoids; - testerMock = tester; - - // The global instance does not get destroyed at the end of each test. - // Ordinarily the mock instance would be local and such behaviour would - // cause a memory leak. We must notify the test that this is not the case. - Mock::AllowLeak(beeperMock); - Mock::AllowLeak(comMock); - Mock::AllowLeak(encodersMock); - Mock::AllowLeak(solenoidsMock); - Mock::AllowLeak(testerMock); - - // start in state `OpState::init` - EXPECT_CALL(*arduinoMock, millis); - fsm->init(); - // expected_isr(NoDirection, NoDirection); - // EXPECT_CALL(*arduinoMock, digitalWrite(LED_PIN_A, LOW)); - // fsm->setState(OpState::init); - // EXPECT_CALL(*comMock, update); - // fsm->dispatch(); - // ASSERT_TRUE(fsm->getState() == OpState::init); - expect_knitter_init(); - knitter->init(); - knitter->setMachineType(Machine_t::Kh910); - expected_isr(Direction_t::NoDirection, Direction_t::NoDirection, 0); - } - - void TearDown() override { - releaseArduinoMock(); - releaseSerialMock(); - } - - ArduinoMock *arduinoMock; - BeeperMock *beeperMock; - ComMock *comMock; - EncodersMock *encodersMock; - SerialMock *serialMock; - SolenoidsMock *solenoidsMock; - TesterMock *testerMock; - - void expect_knitter_init() { - EXPECT_CALL(*arduinoMock, pinMode(ENC_PIN_A, INPUT)); - EXPECT_CALL(*arduinoMock, pinMode(ENC_PIN_B, INPUT)); - EXPECT_CALL(*arduinoMock, pinMode(ENC_PIN_C, INPUT)); - EXPECT_CALL(*arduinoMock, pinMode(LED_PIN_A, OUTPUT)); - EXPECT_CALL(*arduinoMock, pinMode(LED_PIN_B, OUTPUT)); - EXPECT_CALL(*arduinoMock, digitalWrite(LED_PIN_A, HIGH)); - EXPECT_CALL(*arduinoMock, digitalWrite(LED_PIN_B, HIGH)); - EXPECT_CALL(*solenoidsMock, init); - } - - void expected_isr(Direction_t dir, Direction_t hall, uint8_t position) { - EXPECT_CALL(*encodersMock, encA_interrupt); - EXPECT_CALL(*encodersMock, getPosition).WillRepeatedly(Return(position)); - EXPECT_CALL(*encodersMock, getDirection).WillRepeatedly(Return(dir)); - EXPECT_CALL(*encodersMock, getHallActive).WillRepeatedly(Return(hall)); - EXPECT_CALL(*encodersMock, getBeltShift).Times(AtLeast(1)); - EXPECT_CALL(*encodersMock, getCarriage).Times(AtLeast(1)); - knitter->isr(); - - // test expectations without destroying instance - ASSERT_TRUE(Mock::VerifyAndClear(encodersMock)); - } - - void expect_reqLine() { - EXPECT_CALL(*comMock, send_reqLine); - } - - void expect_indState() { - EXPECT_CALL(*comMock, send_indState); - } - - void expect_get_ready() { - // start in state `OpState::init` - ASSERT_EQ(fsm->getState(), OpState::init); - - expect_indState(); - EXPECT_CALL(*solenoidsMock, setSolenoids(SOLENOIDS_BITMASK)); - EXPECT_CALL(*arduinoMock, digitalWrite(LED_PIN_A, LOW)); - } - - void get_in_ready() { - expect_get_ready(); - expected_state(OpState::ready); - - // ends in state `OpState::ready` - ASSERT_EQ(fsm->getState(), OpState::ready); - } - - void expected_state(OpState_t state) { - fsm->setState(state); - expected_dispatch(); - } - - void expected_dispatch() { - EXPECT_CALL(*comMock, update); - fsm->dispatch(); - - // test expectations without destroying instance - ASSERT_TRUE(Mock::VerifyAndClear(comMock)); - } - - void expected_dispatch_wait_for_machine() { - ASSERT_EQ(fsm->getState(), OpState::wait_for_machine); - - EXPECT_CALL(*arduinoMock, digitalWrite(LED_PIN_A, LOW)); - expected_dispatch(); - } - - void expected_dispatch_init() { - // starts in state `OpState::init` - ASSERT_EQ(fsm->getState(), OpState::init); - - EXPECT_CALL(*arduinoMock, digitalWrite(LED_PIN_A, LOW)); - expected_dispatch(); - } - - void expected_dispatch_ready() { - // starts in state `OpState::ready` - ASSERT_EQ(fsm->getState(), OpState::ready); - - EXPECT_CALL(*arduinoMock, digitalWrite(LED_PIN_A, LOW)); - expected_dispatch(); - } - - void expected_dispatch_knit() { - // starts in state `OpState::knit` - ASSERT_EQ(fsm->getState(), OpState::knit); - - EXPECT_CALL(*arduinoMock, digitalWrite(LED_PIN_A, HIGH)); // green LED on - expected_dispatch(); - } - - void expected_dispatch_test() { - // starts in state `OpState::test` - ASSERT_EQ(fsm->getState(), OpState::test); - - EXPECT_CALL(*testerMock, loop); - expected_dispatch(); - - // test expectations without destroying instance - ASSERT_TRUE(Mock::VerifyAndClear(testerMock)); - } - - void expected_dispatch_error(unsigned long t) { - // starts in state `OpState::error` - ASSERT_EQ(fsm->getState(), OpState::error); - - EXPECT_CALL(*arduinoMock, millis).WillOnce(Return(t)); - expected_dispatch(); - } - - void expect_first_knit() { - EXPECT_CALL(*arduinoMock, delay(START_KNITTING_DELAY)); - EXPECT_CALL(*beeperMock, finishedLine); - expect_reqLine(); - } -}; - -TEST_F(FsmTest, test_setState) { - fsm->setState(OpState::ready); - expected_dispatch_wait_for_machine(); - ASSERT_TRUE(fsm->getState() == OpState::ready); -} - -TEST_F(FsmTest, test_dispatch_init) { - // Get to init - fsm->setState(OpState::init); - expected_dispatch_wait_for_machine(); - ASSERT_EQ(fsm->getState(), OpState::init); - - // no transition to state `OpState::ready` - expected_isr(Direction_t::Left, Direction_t::Left, 0); - expected_dispatch_init(); - ASSERT_TRUE(fsm->getState() == OpState::init); - - // no transition to state `OpState::ready` - expected_isr(Direction_t::Right, Direction_t::Right, 0); - expected_dispatch_init(); - ASSERT_TRUE(fsm->getState() == OpState::init); - - // transition to state `OpState::ready` - expected_isr(Direction_t::Left, Direction_t::Right, positionPassedRight); - expect_get_ready(); - expected_dispatch(); - ASSERT_EQ(fsm->getState(), OpState::ready); - - // get to state `OpState::init` - fsm->setState(OpState::init); - expected_dispatch_ready(); - - // transition to state `OpState::ready` - expected_isr(Direction_t::Right, Direction_t::Left, positionPassedLeft); - expect_get_ready(); - expected_dispatch(); - ASSERT_TRUE(fsm->getState() == OpState::ready); - - // test expectations without destroying instance - ASSERT_TRUE(Mock::VerifyAndClear(comMock)); - ASSERT_TRUE(Mock::VerifyAndClear(solenoidsMock)); -} - -TEST_F(FsmTest, test_dispatch_test) { - // get in state `OpState::test` - fsm->setState(OpState::test); - expected_dispatch_wait_for_machine(); - - // now in state `OpState::test` - expected_dispatch_test(); - - // now quit test - fsm->setState(OpState::init); - expect_knitter_init(); - expected_dispatch_test(); - ASSERT_TRUE(fsm->getState() == OpState::init); - - // test expectations without destroying instance - ASSERT_TRUE(Mock::VerifyAndClear(solenoidsMock)); -} - -TEST_F(FsmTest, test_dispatch_knit) { - // get to state `OpState::ready` - fsm->setState(OpState::ready); - expected_dispatch_wait_for_machine(); - - // get to state `OpState::knit` - fsm->setState(OpState::knit); - expected_dispatch_ready(); - ASSERT_TRUE(fsm->getState() == OpState::knit); - - // now in state `OpState::knit` - expect_first_knit(); - expected_dispatch_knit(); - - // test expectations without destroying instance - ASSERT_TRUE(Mock::VerifyAndClear(beeperMock)); - ASSERT_TRUE(Mock::VerifyAndClear(comMock)); -} - -TEST_F(FsmTest, test_dispatch_error) { - // get to state `OpState::error` - fsm->setState(OpState::error); - expected_dispatch_wait_for_machine(); - - // now in state `OpState::error` - expected_dispatch_error(0); - - // too soon to flash - EXPECT_CALL(*arduinoMock, digitalWrite).Times(0); - expected_dispatch_error(FLASH_DELAY - 1); - - // flash first time - EXPECT_CALL(*arduinoMock, digitalWrite(LED_PIN_A, LOW)); - EXPECT_CALL(*arduinoMock, digitalWrite(LED_PIN_B, HIGH)); - EXPECT_CALL(*comMock, send_indState); - expected_dispatch_error(FLASH_DELAY); - - // alternate flash - EXPECT_CALL(*arduinoMock, digitalWrite(LED_PIN_A, HIGH)); - EXPECT_CALL(*arduinoMock, digitalWrite(LED_PIN_B, LOW)); - EXPECT_CALL(*comMock, send_indState); - expected_dispatch_error(2 * FLASH_DELAY); - - // get to state `OpState::init` - EXPECT_CALL(*arduinoMock, digitalWrite(LED_PIN_B, LOW)); - expect_knitter_init(); - expected_state(OpState::init); - - // test expectations without destroying instance - ASSERT_TRUE(Mock::VerifyAndClear(solenoidsMock)); -} - -TEST_F(FsmTest, test_dispatch_default) { - // get to default state - fsm->setState(static_cast(99)); - expected_dispatch_wait_for_machine(); - ASSERT_TRUE(static_cast(fsm->getState()) == 99); - - // now in default state - EXPECT_CALL(*arduinoMock, digitalWrite).Times(0); - expected_dispatch(); -} diff --git a/test/test_knitter.cpp b/test/test_knitter.cpp deleted file mode 100644 index 13894b931..000000000 --- a/test/test_knitter.cpp +++ /dev/null @@ -1,736 +0,0 @@ -/*!` - * \file test_knitter.cpp - * - * This file is part of AYAB. - * - * AYAB is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * AYAB is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with AYAB. If not, see . - * - * Original Work Copyright 2013 Christian Obersteiner, Andreas Müller - * Modified Work Copyright 2020 Sturla Lange, Tom Price - * http://ayab-knitting.com - */ - -#include - -#include -#include - -#include -#include -#include -#include -#include -#include - -using ::testing::_; -using ::testing::AtLeast; -using ::testing::Mock; -using ::testing::Return; -using ::testing::TypedEq; - -extern Knitter *knitter; -extern Fsm *fsm; - -extern BeeperMock *beeper; -extern ComMock *com; -extern EncodersMock *encoders; -extern SolenoidsMock *solenoids; -extern TesterMock *tester; - -class KnitterTest : public ::testing::Test { -protected: - void SetUp() override { - arduinoMock = arduinoMockInstance(); - - // pointers to global instances - beeperMock = beeper; - comMock = com; - encodersMock = encoders; - solenoidsMock = solenoids; - testerMock = tester; - - // The global instances do not get destroyed at the end of each test. - // Ordinarily the mock instances would be local and such behaviour would - // cause a memory leak. We must notify the test that this is not the case. - Mock::AllowLeak(beeperMock); - Mock::AllowLeak(comMock); - Mock::AllowLeak(encodersMock); - Mock::AllowLeak(solenoidsMock); - Mock::AllowLeak(testerMock); - - // start in state `OpState::init` - expected_isr(Direction_t::NoDirection, Direction_t::NoDirection); - EXPECT_CALL(*arduinoMock, millis); - fsm->init(); - expect_knitter_init(); - knitter->init(); - } - - void TearDown() override { - releaseArduinoMock(); - } - - ArduinoMock *arduinoMock; - BeeperMock *beeperMock; - ComMock *comMock; - EncodersMock *encodersMock; - SolenoidsMock *solenoidsMock; - TesterMock *testerMock; - - uint8_t get_position_past_left() { - return (END_LEFT_PLUS_OFFSET[static_cast(encoders->getMachineType())] + GARTER_SLOP) + 1; - } - - uint8_t get_position_past_right() { - return (END_RIGHT_MINUS_OFFSET[static_cast(encoders->getMachineType())] - GARTER_SLOP) - 1; - } - - void expect_knitter_init() { - EXPECT_CALL(*arduinoMock, pinMode(ENC_PIN_A, INPUT)); - EXPECT_CALL(*arduinoMock, pinMode(ENC_PIN_B, INPUT)); - EXPECT_CALL(*arduinoMock, pinMode(ENC_PIN_C, INPUT)); - - EXPECT_CALL(*arduinoMock, pinMode(LED_PIN_A, OUTPUT)); - EXPECT_CALL(*arduinoMock, pinMode(LED_PIN_B, OUTPUT)); - - EXPECT_CALL(*arduinoMock, digitalWrite(LED_PIN_A, HIGH)); // green LED on - EXPECT_CALL(*arduinoMock, digitalWrite(LED_PIN_B, HIGH)); // yellow LED on - - EXPECT_CALL(*solenoidsMock, init); - } - - void expect_isr(uint16_t pos, Direction_t dir, Direction_t hall, - BeltShift_t belt, Carriage_t carriage) { - EXPECT_CALL(*encodersMock, encA_interrupt); - EXPECT_CALL(*encodersMock, getPosition).WillRepeatedly(Return(pos)); - EXPECT_CALL(*encodersMock, getDirection).WillRepeatedly(Return(dir)); - EXPECT_CALL(*encodersMock, getHallActive).WillRepeatedly(Return(hall)); - EXPECT_CALL(*encodersMock, getBeltShift).WillRepeatedly(Return(belt)); - EXPECT_CALL(*encodersMock, getCarriage).WillRepeatedly(Return(carriage)); - } - - void expected_isr(uint16_t pos, Direction_t dir, Direction_t hall, - BeltShift_t belt, Carriage_t carriage) { - expect_isr(pos, dir, hall, belt, carriage); - knitter->isr(); - } - - void expect_isr(Direction_t dir, Direction_t hall) { - expect_isr(1, dir, hall, BeltShift::Regular, Carriage_t::Knit); - } - - void expected_isr(uint8_t pos, Direction_t dir, Direction_t hall) { - expect_isr(pos, dir, hall, BeltShift::Regular, Carriage_t::Knit); - knitter->isr(); - } - - void expected_isr(Direction_t dir, Direction_t hall) { - expect_isr(dir, hall); - knitter->isr(); - } - - void expect_isr(uint16_t pos) { - expect_isr(pos, Direction_t::Right, Direction_t::Left, BeltShift::Regular, Carriage_t::Garter); - } - - void expected_isr(uint16_t pos) { - expect_isr(pos); - knitter->isr(); - } - - void expected_isr() { - expected_isr(1); - } - - void expect_reqLine() { - EXPECT_CALL(*comMock, send_reqLine); - } - - void expect_indState() { - EXPECT_CALL(*comMock, send_indState); - } - - void expected_dispatch() { - EXPECT_CALL(*comMock, update); - fsm->dispatch(); - } - - void expected_get_ready() { - // starts in state `OpState::wait_for_machine` - ASSERT_EQ(fsm->getState(), OpState::init); - - EXPECT_CALL(*solenoidsMock, setSolenoids(SOLENOIDS_BITMASK)); - expect_indState(); - expected_dispatch_init(); - - ASSERT_EQ(fsm->getState(), OpState::ready); - } - - void expected_init_machine(Machine_t m) { - // Init the machine - ASSERT_EQ(knitter->initMachine(m), ErrorCode::success); - expected_dispatch_wait_for_machine(); - - ASSERT_EQ(fsm->getState(), OpState::init); - } - - void get_to_ready(Machine_t m) { - expected_init_machine(m); - // Machine is initialized when Left hall sensor - // is passed in Right direction inside active needles. - uint8_t position = get_position_past_left(); - expected_isr(position, Direction_t::Right, Direction_t::Left); - expected_get_ready(); - } - - void get_to_knit(Machine_t m) { - EXPECT_CALL(*encodersMock, init); - get_to_ready(m); - uint8_t pattern[] = {1}; - EXPECT_CALL(*beeperMock, ready); - ASSERT_EQ(knitter->startKnitting(0, NUM_NEEDLES[static_cast(m)] - 1, pattern, false), ErrorCode::success); - expected_dispatch_ready(); - - // ends in state `OpState::knit` - ASSERT_TRUE(fsm->getState() == OpState::knit); - } - - void expected_dispatch_knit(bool first) { - if (first) { - get_to_knit(Machine_t::Kh910); - expect_first_knit(); - EXPECT_CALL(*arduinoMock, digitalWrite(LED_PIN_A, HIGH)); // green LED on - expected_dispatch(); - return; - } - ASSERT_TRUE(fsm->getState() == OpState::knit); - EXPECT_CALL(*arduinoMock, digitalWrite(LED_PIN_A, HIGH)); // green LED on - expected_dispatch(); - } - - void expected_dispatch_wait_for_machine() { - // starts in state `OpState::init` - ASSERT_EQ(fsm->getState(), OpState::wait_for_machine); - - EXPECT_CALL(*arduinoMock, digitalWrite(LED_PIN_A, LOW)); - expected_dispatch(); - } - - void expected_dispatch_init() { - // starts in state `OpState::init` - ASSERT_EQ(fsm->getState(), OpState::init); - - EXPECT_CALL(*arduinoMock, digitalWrite(LED_PIN_A, LOW)); - expected_dispatch(); - } - - void expected_dispatch_ready() { - // starts in state `OpState::ready` - ASSERT_TRUE(fsm->getState() == OpState::ready); - - EXPECT_CALL(*arduinoMock, digitalWrite(LED_PIN_A, LOW)); - expected_dispatch(); - } - - void expected_dispatch_test() { - // starts in state `OpState::test` - ASSERT_EQ(fsm->getState(), OpState::test); - - expect_indState(); - EXPECT_CALL(*testerMock, loop); - expected_dispatch(); - } - - void expect_first_knit() { - EXPECT_CALL(*arduinoMock, delay(START_KNITTING_DELAY)); - EXPECT_CALL(*beeperMock, finishedLine); - expect_reqLine(); - } -}; - -TEST_F(KnitterTest, test_send) { - uint8_t p[] = {1, 2, 3, 4, 5}; - EXPECT_CALL(*comMock, send); - comMock->send(p, 5); - - // test expectations without destroying instance - ASSERT_TRUE(Mock::VerifyAndClear(solenoidsMock)); - ASSERT_TRUE(Mock::VerifyAndClear(comMock)); -} - -TEST_F(KnitterTest, test_isr) { - expected_isr(1); - - // test expectations without destroying instance - ASSERT_TRUE(Mock::VerifyAndClear(solenoidsMock)); - ASSERT_TRUE(Mock::VerifyAndClear(encodersMock)); -} - -TEST_F(KnitterTest, test_startKnitting_NoMachine) { - uint8_t pattern[] = {1}; - Machine_t m = knitter->getMachineType(); - ASSERT_EQ(m, Machine_t::NoMachine); - ASSERT_TRUE(knitter->initMachine(m) != ErrorCode::success); - ASSERT_TRUE( - knitter->startKnitting(0, NUM_NEEDLES[static_cast(m)] - 1, pattern, false) != ErrorCode::success); - - // test expectations without destroying instance - ASSERT_TRUE(Mock::VerifyAndClear(solenoidsMock)); -} - -TEST_F(KnitterTest, test_startKnitting_invalidMachine) { - uint8_t pattern[] = {1}; - ASSERT_TRUE(knitter->initMachine(Machine_t::NoMachine) != ErrorCode::success); - ASSERT_TRUE(knitter->startKnitting(0, 1, pattern, false) != ErrorCode::success); - - // test expectations without destroying instance - ASSERT_TRUE(Mock::VerifyAndClear(solenoidsMock)); -} - -TEST_F(KnitterTest, test_startKnitting_notReady) { - uint8_t pattern[] = {1}; - ASSERT_TRUE(knitter->startKnitting(0, NUM_NEEDLES[static_cast(Machine_t::Kh910)] - 1, pattern, - false) != ErrorCode::success); - - // test expectations without destroying instance - ASSERT_TRUE(Mock::VerifyAndClear(solenoidsMock)); -} - -TEST_F(KnitterTest, test_startKnitting_Kh910) { - get_to_knit(Machine_t::Kh910); - - // test expectations without destroying instance - ASSERT_TRUE(Mock::VerifyAndClear(solenoidsMock)); - ASSERT_TRUE(Mock::VerifyAndClear(encodersMock)); - ASSERT_TRUE(Mock::VerifyAndClear(beeperMock)); - ASSERT_TRUE(Mock::VerifyAndClear(comMock)); -} - -TEST_F(KnitterTest, test_startKnitting_Kh270) { - get_to_knit(Machine_t::Kh270); - - // test expectations without destroying instance - ASSERT_TRUE(Mock::VerifyAndClear(solenoidsMock)); - ASSERT_TRUE(Mock::VerifyAndClear(encodersMock)); - ASSERT_TRUE(Mock::VerifyAndClear(beeperMock)); - ASSERT_TRUE(Mock::VerifyAndClear(comMock)); -} - -TEST_F(KnitterTest, test_startKnitting_failures) { - uint8_t pattern[] = {1}; - get_to_ready(Machine_t::Kh910); - - // `m_stopNeedle` lower than `m_startNeedle` - ASSERT_TRUE(knitter->startKnitting(1, 0, pattern, false) != ErrorCode::success); - - // `m_stopNeedle` out of range - ASSERT_TRUE(knitter->startKnitting(0, NUM_NEEDLES[static_cast(Machine_t::Kh910)], pattern, - false) != ErrorCode::success); - - // null pattern - ASSERT_TRUE(knitter->startKnitting(0, NUM_NEEDLES[static_cast(Machine_t::Kh910)] - 1, nullptr, - false) != ErrorCode::success); - - // test expectations without destroying instance - ASSERT_TRUE(Mock::VerifyAndClear(solenoidsMock)); - ASSERT_TRUE(Mock::VerifyAndClear(encodersMock)); - ASSERT_TRUE(Mock::VerifyAndClear(comMock)); -} - -TEST_F(KnitterTest, test_setNextLine) { - // set `m_lineRequested` - ASSERT_EQ(knitter->setNextLine(1), false); - - expected_dispatch_knit(true); - - // outside of the active needles - expected_isr(NUM_NEEDLES[static_cast(Machine_t::Kh910)] + END_OF_LINE_OFFSET_R[static_cast(Machine_t::Kh910)] + 1 + knitter->getStartOffset(Direction_t::Left)); - EXPECT_CALL(*solenoidsMock, setSolenoid).Times(1); - expected_dispatch_knit(false); - - // wrong line number - EXPECT_CALL(*beeperMock, finishedLine).Times(0); - expect_reqLine(); - ASSERT_EQ(knitter->setNextLine(1), false); - - // correct line number - EXPECT_CALL(*beeperMock, finishedLine).Times(1); - ASSERT_EQ(knitter->setNextLine(0), true); - - // `m_lineRequested` has been set to `false` - ASSERT_EQ(knitter->setNextLine(0), false); - - // test expectations without destroying instance - ASSERT_TRUE(Mock::VerifyAndClear(solenoidsMock)); - ASSERT_TRUE(Mock::VerifyAndClear(encodersMock)); - ASSERT_TRUE(Mock::VerifyAndClear(beeperMock)); - ASSERT_TRUE(Mock::VerifyAndClear(comMock)); -} - -TEST_F(KnitterTest, test_knit_Kh910) { - get_to_ready(Machine_t::Kh910); - - // knit - uint8_t pattern[] = {1}; - - // `m_startNeedle` is greater than `m_pixelToSet` - EXPECT_CALL(*beeperMock, ready); - const uint8_t START_NEEDLE = NUM_NEEDLES[static_cast(Machine_t::Kh910)] - 2; - const uint8_t STOP_NEEDLE = NUM_NEEDLES[static_cast(Machine_t::Kh910)] - 1; - knitter->startKnitting(START_NEEDLE, STOP_NEEDLE, pattern, true); - EXPECT_CALL(*arduinoMock, digitalWrite(LED_PIN_A, LOW)); // green LED off - expected_dispatch(); - - // first knit - expect_first_knit(); - expect_indState(); - expected_dispatch_knit(false); - - // no useful position calculated by `calculatePixelAndSolenoid()` - expected_isr(100, Direction_t::NoDirection, Direction_t::Right, BeltShift::Shifted, Carriage_t::Knit); - EXPECT_CALL(*solenoidsMock, setSolenoid).Times(0); - expect_indState(); - expected_dispatch_knit(false); - - // don't set `m_workedonline` to `true` - const uint8_t OFFSET = END_OF_LINE_OFFSET_R[static_cast(Machine_t::Kh910)]; - expected_isr(8 + STOP_NEEDLE + OFFSET); - EXPECT_CALL(*solenoidsMock, setSolenoid); - expect_indState(); - expected_dispatch_knit(false); - - expected_isr(START_NEEDLE); - EXPECT_CALL(*solenoidsMock, setSolenoid); - expect_indState(); - expected_dispatch_knit(false); - - // test expectations without destroying instance - ASSERT_TRUE(Mock::VerifyAndClear(solenoidsMock)); - ASSERT_TRUE(Mock::VerifyAndClear(encodersMock)); - ASSERT_TRUE(Mock::VerifyAndClear(beeperMock)); - ASSERT_TRUE(Mock::VerifyAndClear(comMock)); -} - -TEST_F(KnitterTest, test_knit_Kh270) { - get_to_ready(Machine_t::Kh270); - - // knit - uint8_t pattern[] = {1}; - - // `m_startNeedle` is greater than `m_pixelToSet` - EXPECT_CALL(*beeperMock, ready); - const uint8_t START_NEEDLE = NUM_NEEDLES[static_cast(Machine_t::Kh270)] - 2; - const uint8_t STOP_NEEDLE = NUM_NEEDLES[static_cast(Machine_t::Kh270)] - 1; - knitter->startKnitting(START_NEEDLE, STOP_NEEDLE, pattern, true); - EXPECT_CALL(*arduinoMock, digitalWrite(LED_PIN_A, LOW)); - expected_dispatch(); - - // first knit - expect_first_knit(); - expect_indState(); - expected_dispatch_knit(false); - - // second knit - expected_isr(START_NEEDLE); - expect_indState(); - EXPECT_CALL(*solenoidsMock, setSolenoid); - expected_dispatch_knit(false); - - // no useful position calculated by `calculatePixelAndSolenoid()` - expected_isr(60, Direction_t::NoDirection, Direction_t::Right, BeltShift::Shifted, Carriage_t::Knit); - EXPECT_CALL(*solenoidsMock, setSolenoid).Times(0); - expect_indState(); - expected_dispatch_knit(false); - - // don't set `m_workedonline` to `true` - const uint8_t OFFSET = END_OF_LINE_OFFSET_R[static_cast(Machine_t::Kh270)]; - expected_isr(8 + STOP_NEEDLE + OFFSET, Direction_t::Right, Direction_t::Left, BeltShift::Regular, Carriage_t::Knit); - EXPECT_CALL(*solenoidsMock, setSolenoid); - expect_indState(); - expected_dispatch_knit(false); - - expected_isr(START_NEEDLE, Direction_t::Right, Direction_t::Left, BeltShift::Regular, Carriage_t::Knit); - EXPECT_CALL(*solenoidsMock, setSolenoid); - expect_indState(); - expected_dispatch_knit(false); - - // test expectations without destroying instance - ASSERT_TRUE(Mock::VerifyAndClear(solenoidsMock)); - ASSERT_TRUE(Mock::VerifyAndClear(encodersMock)); - ASSERT_TRUE(Mock::VerifyAndClear(beeperMock)); - ASSERT_TRUE(Mock::VerifyAndClear(comMock)); -} - -TEST_F(KnitterTest, test_knit_line_request) { - // `m_workedOnLine` is set to `true` - expected_dispatch_knit(true); - - // Position has changed since last call to operate function - // `m_pixelToSet` is set above `m_stopNeedle` + END_OF_LINE_OFFSET_R - expected_isr(NUM_NEEDLES[static_cast(Machine_t::Kh910)] + 8 + END_OF_LINE_OFFSET_R[static_cast(Machine_t::Kh910)] + 1); - - EXPECT_CALL(*solenoidsMock, setSolenoid); - expected_dispatch_knit(false); - - // no change in position, no action. - EXPECT_CALL(*solenoidsMock, setSolenoid).Times(0); - expected_dispatch_knit(false); - - // test expectations without destroying instance - ASSERT_TRUE(Mock::VerifyAndClear(solenoidsMock)); - ASSERT_TRUE(Mock::VerifyAndClear(encodersMock)); - ASSERT_TRUE(Mock::VerifyAndClear(beeperMock)); - ASSERT_TRUE(Mock::VerifyAndClear(comMock)); -} - -TEST_F(KnitterTest, test_knit_lastLine) { - expected_dispatch_knit(true); - - // Run one knit inside the working needles. - EXPECT_CALL(*solenoidsMock, setSolenoid); - expected_isr(knitter->getStartOffset(Direction_t::Left) + 20); - // `m_workedOnLine` is set to true - expected_dispatch_knit(false); - - // Position has changed since last call to operate function - // `m_pixelToSet` is above `m_stopNeedle` + END_OF_LINE_OFFSET_R - expected_isr(NUM_NEEDLES[static_cast(Machine_t::Kh910)] + END_OF_LINE_OFFSET_R[static_cast(Machine_t::Kh910)] + 1 + knitter->getStartOffset(Direction_t::Left)); - - // `m_lastLineFlag` is `true` - knitter->setLastLine(); - - EXPECT_CALL(*solenoidsMock, setSolenoid); - EXPECT_CALL(*beeperMock, endWork); - EXPECT_CALL(*solenoidsMock, setSolenoids(SOLENOIDS_BITMASK)); - EXPECT_CALL(*beeperMock, finishedLine); - expected_dispatch_knit(false); - - // test expectations without destroying instance - ASSERT_TRUE(Mock::VerifyAndClear(solenoidsMock)); - ASSERT_TRUE(Mock::VerifyAndClear(encodersMock)); - ASSERT_TRUE(Mock::VerifyAndClear(beeperMock)); - ASSERT_TRUE(Mock::VerifyAndClear(comMock)); -} - -TEST_F(KnitterTest, test_knit_lastLine_and_no_req) { - get_to_knit(Machine_t::Kh910); - - // Note: probing private data and methods to get full branch coverage. - knitter->m_stopNeedle = 100; - uint8_t wanted_pixel = - knitter->m_stopNeedle + END_OF_LINE_OFFSET_R[static_cast(Machine_t::Kh910)] + 1; - knitter->m_firstRun = false; - knitter->m_direction = Direction_t::Left; - knitter->m_position = wanted_pixel + knitter->getStartOffset(Direction_t::Right); - knitter->m_workedOnLine = true; - knitter->m_lineRequested = false; - knitter->m_lastLineFlag = true; - - // EXPECT_CALL(*arduinoMock, digitalWrite(LED_PIN_A, 1)); - EXPECT_CALL(*solenoidsMock, setSolenoid); - EXPECT_CALL(*beeperMock, endWork); - EXPECT_CALL(*solenoidsMock, setSolenoids(SOLENOIDS_BITMASK)); - EXPECT_CALL(*beeperMock, finishedLine); - knitter->knit(); - - ASSERT_EQ(knitter->getStartOffset(Direction_t::NoDirection), 0); - knitter->m_carriage = Carriage_t::NoCarriage; - ASSERT_EQ(knitter->getStartOffset(Direction_t::Right), 0); - - // test expectations without destroying instance - ASSERT_TRUE(Mock::VerifyAndClear(solenoidsMock)); - ASSERT_TRUE(Mock::VerifyAndClear(encodersMock)); - ASSERT_TRUE(Mock::VerifyAndClear(beeperMock)); - ASSERT_TRUE(Mock::VerifyAndClear(comMock)); -} - -TEST_F(KnitterTest, test_knit_same_position) { - expected_dispatch_knit(true); - - // no call to `setSolenoid()` since position was the same - EXPECT_CALL(*solenoidsMock, setSolenoid).Times(0); - expected_dispatch_knit(false); - - // test expectations without destroying instance - ASSERT_TRUE(Mock::VerifyAndClear(solenoidsMock)); - ASSERT_TRUE(Mock::VerifyAndClear(encodersMock)); - ASSERT_TRUE(Mock::VerifyAndClear(beeperMock)); - ASSERT_TRUE(Mock::VerifyAndClear(comMock)); -} - -TEST_F(KnitterTest, test_knit_new_line) { - // _workedOnLine is set to true - expected_dispatch_knit(true); - - // Run one knit inside the working needles. - EXPECT_CALL(*solenoidsMock, setSolenoid); - expected_isr(knitter->getStartOffset(Direction_t::Left) + 20); - // `m_workedOnLine` is set to true - expected_dispatch_knit(false); - - // Position has changed since last call to operate function - // `m_pixelToSet` is above `m_stopNeedle` + END_OF_LINE_OFFSET_R - expected_isr(NUM_NEEDLES[static_cast(Machine_t::Kh910)] + END_OF_LINE_OFFSET_R[static_cast(Machine_t::Kh910)] + 1 + knitter->getStartOffset(Direction_t::Left)); - - // set `m_lineRequested` to `false` - EXPECT_CALL(*beeperMock, finishedLine); - knitter->setNextLine(0); - - EXPECT_CALL(*solenoidsMock, setSolenoid); - - // `reqLine()` is called which calls `send_reqLine()` - expect_reqLine(); - expected_dispatch_knit(false); - - // test expectations without destroying instance - ASSERT_TRUE(Mock::VerifyAndClear(solenoidsMock)); - ASSERT_TRUE(Mock::VerifyAndClear(encodersMock)); - ASSERT_TRUE(Mock::VerifyAndClear(beeperMock)); - ASSERT_TRUE(Mock::VerifyAndClear(comMock)); -} - -TEST_F(KnitterTest, test_calculatePixelAndSolenoid) { - // initialize - expected_init_machine(Machine_t::Kh910); - fsm->setState(OpState::test); - expected_dispatch_init(); - - // new position, different beltShift and active hall - expected_isr(100, Direction_t::Right, Direction_t::Right, BeltShift::Shifted, Carriage_t::Lace); - expected_dispatch_test(); - - // no direction, need to change position to enter test - expected_isr(101, Direction_t::NoDirection, Direction_t::Right, BeltShift::Shifted, Carriage_t::Lace); - expected_dispatch_test(); - - // no belt, need to change position to enter test - expected_isr(100, Direction_t::Right, Direction_t::Right, BeltShift::Unknown, Carriage_t::Lace); - expected_dispatch_test(); - - // no belt on left side, need to change position to enter test - expected_isr(101, Direction_t::Left, Direction_t::Right, BeltShift::Unknown, Carriage_t::Garter); - expected_dispatch_test(); - - // left Lace carriage - expected_isr(100, Direction_t::Left, Direction_t::Right, BeltShift::Unknown, Carriage_t::Lace); - expected_dispatch_test(); - - // regular belt on left, need to change position to enter test - expected_isr(101, Direction_t::Left, Direction_t::Right, BeltShift::Regular, Carriage_t::Garter); - expected_dispatch_test(); - - // shifted belt on left, need to change position to enter test - expected_isr(100, Direction_t::Left, Direction_t::Right, BeltShift::Shifted, Carriage_t::Garter); - expected_dispatch_test(); - - // off of right end, position is changed - expected_isr(END_RIGHT[static_cast(Machine_t::Kh910)], Direction_t::Left, Direction_t::Right, BeltShift::Unknown, Carriage_t::Lace); - expected_dispatch_test(); - - // direction right, have not reached offset - expected_isr(39, Direction_t::Right, Direction_t::Left, BeltShift::Unknown, Carriage_t::Lace); - expected_dispatch_test(); - - // KH270 - knitter->setMachineType(Machine_t::Kh270); - - // K carriage direction left - expected_isr(0, Direction_t::Left, Direction_t::Right, BeltShift::Regular, Carriage_t::Knit); - expected_dispatch_test(); - - // K carriage direction right - expected_isr(END_RIGHT[static_cast(Machine_t::Kh270)], Direction_t::Right, Direction_t::Left, BeltShift::Regular, Carriage_t::Knit); - expected_dispatch_test(); - - // test expectations without destroying instance - ASSERT_TRUE(Mock::VerifyAndClear(solenoidsMock)); - ASSERT_TRUE(Mock::VerifyAndClear(encodersMock)); - ASSERT_TRUE(Mock::VerifyAndClear(testerMock)); - ASSERT_TRUE(Mock::VerifyAndClear(comMock)); -} - -TEST_F(KnitterTest, test_getStartOffset) { - // out of range values - knitter->m_carriage = Carriage_t::Knit; - ASSERT_EQ(knitter->getStartOffset(Direction_t::NoDirection), 0); - - knitter->m_carriage = Carriage_t::NoCarriage; - ASSERT_EQ(knitter->getStartOffset(Direction_t::Left), 0); - ASSERT_EQ(knitter->getStartOffset(Direction_t::Right), 0); - - knitter->m_carriage = Carriage_t::Lace; - knitter->m_machineType = Machine_t::NoMachine; - ASSERT_EQ(knitter->getStartOffset(Direction_t::Left), 0); - ASSERT_EQ(knitter->getStartOffset(Direction_t::Right), 0); - - // test expectations without destroying instance - ASSERT_TRUE(Mock::VerifyAndClear(solenoidsMock)); -} - -TEST_F(KnitterTest, test_fsm_init_LL) { - expected_init_machine(Machine_t::Kh910); - - // not ready - expected_isr(get_position_past_right(), Direction_t::Left, Direction_t::Left); - expected_dispatch_init(); - ASSERT_EQ(fsm->getState(), OpState::init); - - // test expectations without destroying instance - ASSERT_TRUE(Mock::VerifyAndClear(solenoidsMock)); - ASSERT_TRUE(Mock::VerifyAndClear(comMock)); - ASSERT_TRUE(Mock::VerifyAndClear(encodersMock)); -} - -TEST_F(KnitterTest, test_fsm_init_RR) { - expected_init_machine(Machine_t::Kh910); - - // still not ready - expected_isr(get_position_past_left(), Direction_t::Right, Direction_t::Right); - expected_dispatch_init(); - ASSERT_EQ(fsm->getState(), OpState::init); - - // test expectations without destroying instance - ASSERT_TRUE(Mock::VerifyAndClear(solenoidsMock)); - ASSERT_TRUE(Mock::VerifyAndClear(comMock)); - ASSERT_TRUE(Mock::VerifyAndClear(encodersMock)); -} - -TEST_F(KnitterTest, test_fsm_init_RL) { - expected_init_machine(Machine_t::Kh910); - - // Machine is initialized when Left hall sensor - // is passed in Right direction inside active needles. - expected_isr(get_position_past_left(), Direction_t::Right, Direction_t::Left); - expected_get_ready(); - - // test expectations without destroying instance - ASSERT_TRUE(Mock::VerifyAndClear(solenoidsMock)); - ASSERT_TRUE(Mock::VerifyAndClear(comMock)); - ASSERT_TRUE(Mock::VerifyAndClear(encodersMock)); -} - -TEST_F(KnitterTest, test_fsm_init_LR) { - expected_init_machine(Machine_t::Kh910); - - // New feature (August 2020): the machine is also initialized - // when the right Hall sensor is passed in the Left direction. - expected_isr(get_position_past_right(), Direction_t::Left, Direction_t::Right); - expected_get_ready(); - ASSERT_EQ(fsm->getState(), OpState::ready); - - // test expectations without destroying instance - ASSERT_TRUE(Mock::VerifyAndClear(solenoidsMock)); - ASSERT_TRUE(Mock::VerifyAndClear(comMock)); - ASSERT_TRUE(Mock::VerifyAndClear(encodersMock)); -} diff --git a/test/test_solenoids.cpp b/test/test_solenoids.cpp index 8dad99645..ce5580aab 100644 --- a/test/test_solenoids.cpp +++ b/test/test_solenoids.cpp @@ -17,7 +17,7 @@ * along with AYAB. If not, see . * * Original Work Copyright 2013 Christian Obersteiner, Andreas Müller - * Modified Work Copyright 2020 Sturla Lange, Tom Price + * Modified Work Copyright 2020-3 Sturla Lange, Tom Price * http://ayab-knitting.com */ @@ -50,28 +50,28 @@ TEST_F(SolenoidsTest, test_construct) { TEST_F(SolenoidsTest, test_init) { solenoids->init(); - ASSERT_TRUE(solenoids->solenoidState == 0U); + ASSERT_EQ(solenoids->solenoidState, 0U); } TEST_F(SolenoidsTest, test_setSolenoid1) { solenoids->setSolenoids(0); - ASSERT_TRUE(solenoids->solenoidState == 0U); + ASSERT_EQ(solenoids->solenoidState, 0U); solenoids->setSolenoid(0, true); - ASSERT_TRUE(solenoids->solenoidState == 1U); + ASSERT_EQ(solenoids->solenoidState, 1U); } TEST_F(SolenoidsTest, test_setSolenoid2) { solenoids->setSolenoids(0); - ASSERT_TRUE(solenoids->solenoidState == 0U); + ASSERT_EQ(solenoids->solenoidState, 0U); solenoids->setSolenoids(0); - ASSERT_TRUE(solenoids->solenoidState == 0U); + ASSERT_EQ(solenoids->solenoidState, 0U); solenoids->setSolenoid(0, false); - ASSERT_TRUE(solenoids->solenoidState == 0U); + ASSERT_EQ(solenoids->solenoidState, 0U); } TEST_F(SolenoidsTest, test_setSolenoid3) { solenoids->setSolenoids(0x8000); - ASSERT_TRUE(solenoids->solenoidState == 0x8000U); + ASSERT_EQ(solenoids->solenoidState, 0x8000U); solenoids->setSolenoid(16, false); - ASSERT_TRUE(solenoids->solenoidState == 0x8000U); + ASSERT_EQ(solenoids->solenoidState, 0x8000U); } diff --git a/test/test_tester.cpp b/test/test_tester.cpp deleted file mode 100644 index 70f78dc82..000000000 --- a/test/test_tester.cpp +++ /dev/null @@ -1,266 +0,0 @@ -/*!` - * \file test_tester.cpp - * - * This file is part of AYAB. - * - * AYAB is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * AYAB is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with AYAB. If not, see . - * - * Original Work Copyright 2013 Christian Obersteiner, Andreas Müller - * Modified Work Copyright 2020 Sturla Lange, Tom Price - * http://ayab-knitting.com - */ - -#include - -#include -#include - -#include -#include - -using ::testing::_; -using ::testing::An; -using ::testing::AtLeast; -using ::testing::Mock; -using ::testing::Return; - -extern Beeper *beeper; -extern Tester *tester; - -extern FsmMock *fsm; -extern KnitterMock *knitter; - -class TesterTest : public ::testing::Test { -protected: - void SetUp() override { - arduinoMock = arduinoMockInstance(); - serialMock = serialMockInstance(); - // serialCommandMock = serialCommandMockInstance(); - - // pointers to global instances - fsmMock = fsm; - knitterMock = knitter; - - // The global instances do not get destroyed at the end of each test. - // Ordinarily the mock instance would be local and such behaviour would - // cause a memory leak. We must notify the test that this is not the case. - Mock::AllowLeak(fsmMock); - Mock::AllowLeak(knitterMock); - - beeper->init(true); - } - - void TearDown() override { - releaseArduinoMock(); - releaseSerialMock(); - } - - ArduinoMock *arduinoMock; - FsmMock *fsmMock; - KnitterMock *knitterMock; - SerialMock *serialMock; - - void expect_startTest(unsigned long t) { - EXPECT_CALL(*fsmMock, getState).WillOnce(Return(OpState::ready)); - EXPECT_CALL(*fsmMock, setState(OpState::test)); - EXPECT_CALL(*knitterMock, setMachineType(Machine_t::Kh930)); - expect_write(false); - - // `setUp()` must have been called to reach `millis()` - EXPECT_CALL(*arduinoMock, millis).WillOnce(Return(t)); - - ASSERT_TRUE(tester->startTest(Machine_t::Kh930) == ErrorCode::success); - } - - void expect_write(bool once) { - return; - //TODO: FIXME: Mock PocketSerial, so this works again. - if (once) { - EXPECT_CALL(*serialMock, write(_, _)); - EXPECT_CALL(*serialMock, write(SLIP::END)); - } else { - EXPECT_CALL(*serialMock, write(_, _)).Times(AtLeast(1)); - EXPECT_CALL(*serialMock, write(SLIP::END)).Times(AtLeast(1)); - } - } - - void expect_readEOLsensors(bool flag) { - uint8_t n = flag ? 1 : 0; - EXPECT_CALL(*arduinoMock, analogRead(EOL_PIN_L)).Times(n); - EXPECT_CALL(*arduinoMock, analogRead(EOL_PIN_R)).Times(n); - } - - void expect_readEncoders(bool flag) { - uint8_t n = flag ? 1 : 0; - EXPECT_CALL(*arduinoMock, digitalRead(ENC_PIN_A)).Times(n); - EXPECT_CALL(*arduinoMock, digitalRead(ENC_PIN_B)).Times(n); - EXPECT_CALL(*arduinoMock, digitalRead(ENC_PIN_C)).Times(n); - } -}; - -TEST_F(TesterTest, test_helpCmd) { - expect_write(false); - tester->helpCmd(); -} - -TEST_F(TesterTest, test_sendCmd) { - expect_write(false); - tester->sendCmd(); -} - -TEST_F(TesterTest, test_beepCmd) { - expect_write(true); - tester->beepCmd(); - EXPECT_CALL(*arduinoMock, millis).WillOnce(Return(0U)); - beeper->schedule(); - EXPECT_CALL(*arduinoMock, analogWrite(PIEZO_PIN, BEEP_ON_DUTY)); - EXPECT_CALL(*arduinoMock, millis).WillOnce(Return(1U)); - beeper->schedule(); -} - -TEST_F(TesterTest, test_setSingleCmd_fail1) { - const uint8_t buf[] = {static_cast(AYAB_API::setSingleCmd), 0}; - expect_write(false); - tester->setSingleCmd(buf, 2); -} - -TEST_F(TesterTest, test_setSingleCmd_fail2) { - const uint8_t buf[] = {static_cast(AYAB_API::setSingleCmd), 16, 0}; - expect_write(false); - tester->setSingleCmd(buf, 3); -} - -TEST_F(TesterTest, test_setSingleCmd_fail3) { - const uint8_t buf[] = {static_cast(AYAB_API::setSingleCmd), 15, 2}; - expect_write(false); - tester->setSingleCmd(buf, 3); -} - -TEST_F(TesterTest, test_setSingleCmd_success) { - const uint8_t buf[] = {static_cast(AYAB_API::setSingleCmd), 15, 1}; - expect_write(true); - tester->setSingleCmd(buf, 3); -} - -TEST_F(TesterTest, test_setAllCmd_fail1) { - const uint8_t buf[] = {static_cast(AYAB_API::setAllCmd), 0}; - expect_write(false); - tester->setAllCmd(buf, 2); -} - -TEST_F(TesterTest, test_setAllCmd_success) { - const uint8_t buf[] = {static_cast(AYAB_API::setAllCmd), 0xff, 0xff}; - expect_write(true); - tester->setAllCmd(buf, 3); -} - -TEST_F(TesterTest, test_readEOLsensorsCmd) { - expect_write(false); - expect_readEOLsensors(true); - tester->readEOLsensorsCmd(); -} - -TEST_F(TesterTest, test_readEncodersCmd_low) { - expect_write(false); - EXPECT_CALL(*arduinoMock, digitalRead).WillRepeatedly(Return(LOW)); - tester->readEncodersCmd(); -} - -TEST_F(TesterTest, test_readEncodersCmd_high) { - expect_write(false); - EXPECT_CALL(*arduinoMock, digitalRead).WillRepeatedly(Return(HIGH)); - tester->readEncodersCmd(); -} - -TEST_F(TesterTest, test_autoReadCmd) { - expect_write(true); - tester->autoReadCmd(); -} - -TEST_F(TesterTest, test_autoTestCmd) { - expect_write(true); - tester->autoTestCmd(); -} - -TEST_F(TesterTest, test_quitCmd) { - EXPECT_CALL(*knitterMock, setUpInterrupt); - EXPECT_CALL(*fsmMock, setState(OpState::init)); - tester->quitCmd(); - - // test expectations without destroying instance - ASSERT_TRUE(Mock::VerifyAndClear(knitterMock)); - ASSERT_TRUE(Mock::VerifyAndClear(fsmMock)); -} - -TEST_F(TesterTest, test_loop_default) { - expect_startTest(0); - EXPECT_CALL(*arduinoMock, millis).WillOnce(Return(TEST_LOOP_DELAY - 1)); - tester->loop(); -} - -TEST_F(TesterTest, test_loop_null) { - expect_startTest(0); - EXPECT_CALL(*arduinoMock, millis).WillOnce(Return(TEST_LOOP_DELAY)); - tester->loop(); -} - -TEST_F(TesterTest, test_loop_autoTest) { - expect_startTest(0); - tester->autoReadCmd(); - tester->autoTestCmd(); - - // m_timerEventOdd = false - EXPECT_CALL(*arduinoMock, millis).WillOnce(Return(TEST_LOOP_DELAY)); - expect_write(true); - expect_readEOLsensors(false); - expect_readEncoders(false); - EXPECT_CALL(*arduinoMock, digitalWrite(LED_PIN_A, HIGH)); - EXPECT_CALL(*arduinoMock, digitalWrite(LED_PIN_B, HIGH)); - tester->loop(); - - // m_timerEventOdd = false - EXPECT_CALL(*arduinoMock, millis).WillOnce(Return(2 * TEST_LOOP_DELAY)); - expect_write(false); - expect_readEOLsensors(true); - expect_readEncoders(true); - EXPECT_CALL(*arduinoMock, digitalWrite(LED_PIN_A, LOW)); - EXPECT_CALL(*arduinoMock, digitalWrite(LED_PIN_B, LOW)); - tester->loop(); - - // after `stopCmd()` - tester->stopCmd(); - EXPECT_CALL(*arduinoMock, millis).WillOnce(Return(3 * TEST_LOOP_DELAY)); - expect_readEOLsensors(false); - expect_readEncoders(false); - EXPECT_CALL(*arduinoMock, digitalWrite(LED_PIN_A, _)).Times(0); - EXPECT_CALL(*arduinoMock, digitalWrite(LED_PIN_B, _)).Times(0); - tester->loop(); -} - -TEST_F(TesterTest, test_startTest_fail) { - // can't start test from state `OpState::knit` - EXPECT_CALL(*fsmMock, getState).WillOnce(Return(OpState::knit)); - ASSERT_TRUE(tester->startTest(Machine_t::Kh910) != ErrorCode::success); - - // test expectations without destroying instance - ASSERT_TRUE(Mock::VerifyAndClear(fsmMock)); -} - -TEST_F(TesterTest, test_startTest_success) { - expect_startTest(0); - - // test expectations without destroying instance - ASSERT_TRUE(Mock::VerifyAndClear(fsmMock)); - ASSERT_TRUE(Mock::VerifyAndClear(knitterMock)); -}