From cca06551a5ca4ab21bc10bdb5acd883cc846f1e5 Mon Sep 17 00:00:00 2001 From: Nathan Jesudason Date: Fri, 11 Jun 2021 15:24:48 -0700 Subject: [PATCH 01/61] Implemented NowTask feature --- src/API/API.cpp | 14 ++ src/API/API.hpp | 4 + src/Application/App.hpp | 47 +++++ src/Application/Constants.hpp | 1 + src/Application/Status.hpp | 16 ++ src/Components/SensorArray.hpp | 11 ++ src/Components/SensorArrayObserver.hpp | 2 + src/Components/Sensors/ButtonSensor.hpp | 38 ++++ .../NowTaskStateController.cpp | 37 ++++ .../NowTaskStateController.hpp | 72 +++++++ src/Task/NowTask.hpp | 169 ++++++++++++++++ src/Task/NowTaskManager.hpp | 183 ++++++++++++++++++ src/Task/NowTaskObserver.hpp | 18 ++ src/Task/Task.hpp | 8 +- 14 files changed, 618 insertions(+), 2 deletions(-) create mode 100644 src/Components/Sensors/ButtonSensor.hpp create mode 100644 src/StateControllers/NowTaskStateController.cpp create mode 100644 src/StateControllers/NowTaskStateController.hpp create mode 100644 src/Task/NowTask.hpp create mode 100644 src/Task/NowTaskManager.hpp create mode 100644 src/Task/NowTaskObserver.hpp diff --git a/src/API/API.cpp b/src/API/API.cpp index 77f90f2..0afea1b 100644 --- a/src/API/API.cpp +++ b/src/API/API.cpp @@ -15,6 +15,20 @@ namespace API { return response; } + auto StartNowTask::operator()(App & app) -> R { + decltype(auto) nowTaskName = app.nowTaskStateController.getCurrentState()->getName(); + + R response; + if (strcmp(NowT::IDLE, nowTaskName) == 0) { + app.beginNowTask(); + response["success"] = "Beginning Now Task"; + } else { + response["error"] = "Already Sampling"; + } + + return response; + } + auto StatusGet::operator()(App & app) -> R { R response; encodeJSON(app.status, response.to()); diff --git a/src/API/API.hpp b/src/API/API.hpp index 8599c0c..8379a73 100644 --- a/src/API/API.hpp +++ b/src/API/API.hpp @@ -26,6 +26,10 @@ namespace API { auto operator()(Arg<0>) -> R; }; + struct StartNowTask : APISpec(App &)> { + auto operator()(Arg<0>) -> R; + }; + struct StatusGet : APISpec(App &)> { auto operator()(Arg<0>) -> R; }; diff --git a/src/Application/App.hpp b/src/Application/App.hpp index 014ed80..77c9e7b 100755 --- a/src/Application/App.hpp +++ b/src/Application/App.hpp @@ -18,6 +18,7 @@ #include #include +#include #include #include @@ -26,6 +27,9 @@ #include #include +#include +#include + #include #include @@ -66,9 +70,12 @@ class App : public KPController, public KPSerialInputObserver, public TaskObserv // MainStateController sm; NewStateController newStateController; HyperFlushStateController hyperFlushStateController; + NowTaskStateController nowTaskStateController; + time_t last_nowTask = now(); ValveManager vm; TaskManager tm; + NowTaskManager ntm; SensorArray sensors{"sensor-array"}; @@ -84,6 +91,7 @@ class App : public KPController, public KPSerialInputObserver, public TaskObserv } public: + void virtual setupButtonPress() {} void setup() override { KPSerialInput::sharedInstance().addObserver(this); Serial.begin(115200); @@ -162,6 +170,13 @@ class App : public KPController, public KPSerialInputObserver, public TaskObserv addComponent(hyperFlushStateController); hyperFlushStateController.idle(); // Wait in IDLE + // + // ___ NOW TASK CONTROLLER ____________ + // + + addComponent(nowTaskStateController); + nowTaskStateController.idle(); + // // ─── NEW STATE CONTROLLER ──────────────────────────────────────── // @@ -304,6 +319,30 @@ class App : public KPController, public KPSerialInputObserver, public TaskObserv hyperFlushStateController.begin(); } + void beginNowTask(){ + time_t time_now = now(); + NowTask task = ntm.task; + status.preventShutdown = false; + + if(time_now + nowTaskStateController.get_total_time() >= tm.tasks[tm.getActiveSortedTaskIds().front()].schedule){ + invalidateTaskAndFreeUpValves(tm.tasks[tm.getActiveSortedTaskIds().front()]); + } + TimedAction NowTaskExecution; + const auto timeUntil = 10; + NowTaskExecution.interval = secsToMillis(timeUntil); + NowTaskExecution.callback = [this]() { nowTaskStateController.begin(); }; + run(NowTaskExecution); // async, will be execute later + + + //currentTaskId = id; + status.preventShutdown = true; + vm.setValveStatus(*(task.valve), ValveStatus::operating); + + println("\033[32;1mExecuting task in ", timeUntil, " seconds\033[0m"); + + + } + ValveBlock currentValveNumberToBlock() { return { shift.toRegisterIndex(status.currentValve) + 1, shift.toPinIndex(status.currentValve)}; @@ -433,6 +472,14 @@ class App : public KPController, public KPSerialInputObserver, public TaskObserv * ──────────────────────────────────────────────────────────────────────────── */ void update() override { KPController::update(); + + if(status.buttonPressed){ + NowTask task = ntm.task; + nowTaskStateController.configure(task); + if(now() - last_nowTask > nowTaskStateController.get_total_time()){ + const auto & response = dispatchAPI(); + } + } if (!status.isProgrammingMode() && !status.preventShutdown) { shutdown(); } diff --git a/src/Application/Constants.hpp b/src/Application/Constants.hpp index 16d649d..25cded5 100755 --- a/src/Application/Constants.hpp +++ b/src/Application/Constants.hpp @@ -97,6 +97,7 @@ namespace TaskKeys { __k_auto SAMPLE_VOLUME = "sampleVolume"; __k_auto DRY_TIME = "dryTime"; __k_auto PRESERVE_TIME = "preserveTime"; + __k_auto CURR_VALVE = "currentValve"; } // namespace TaskKeys namespace ValveKeys { diff --git a/src/Application/Status.hpp b/src/Application/Status.hpp index 1cdbd5e..a07c5da 100755 --- a/src/Application/Status.hpp +++ b/src/Application/Status.hpp @@ -28,6 +28,11 @@ class Status : public JsonDecodable, float waterFlow = 0; float sampleVolume = 0; + bool buttonPressed = false; + bool buttonTriggered = false; + bool latestButtonPress = false; + unsigned long buttonStart = 0; + float maxPressure = 0; bool isFull = false; @@ -112,6 +117,17 @@ class Status : public JsonDecodable, waterDepth = std::get<0>(values); } + void buttonDidUpdate(ButtonSensor::SensorData & values) override { + latestButtonPress = (bool)std::get<0>(values); + if (latestButtonPress != buttonTriggered && (unsigned long) (millis() - buttonStart) < 1000) { + buttonTriggered = true; + buttonStart = millis(); + + } else { + buttonPressed = latestButtonPress; + } + } + bool isBatteryLow() const { analogReadResolution(10); return analogRead(HardwarePins::BATTERY_VOLTAGE) <= 860; // 860 is around 12V of battery diff --git a/src/Components/SensorArray.hpp b/src/Components/SensorArray.hpp index 523f18e..71fb698 100644 --- a/src/Components/SensorArray.hpp +++ b/src/Components/SensorArray.hpp @@ -49,6 +49,7 @@ // }; #pragma once +#include //Not a pretty implementation but hopefully works for now #include #include #include @@ -56,11 +57,13 @@ #include #include #include +#include #define PSAddr 0x08 #define FSAddr 0x07 #define BSAddr 0x77 #define DSAddr 0x76 +#define BAddr 0x88 //CHANGE LATER inline bool checkForI2CConnection(unsigned char addr) { Wire.begin(); @@ -76,6 +79,8 @@ class SensorArray : public KPComponent, public KPSubject { PressureSensor pressure{PSAddr}; BaroSensor baro1{BSAddr}; BaroSensor baro2{DSAddr}; + ButtonSensor button{BAddr}; + void setup() override { flow.enabled = true; @@ -96,6 +101,11 @@ class SensorArray : public KPComponent, public KPSubject { baro2.onReceived = [this](BaroSensor::SensorData & data) { updateObservers(&SensorArrayObserver::baro2DidUpdate, data); }; + + button.enabled = checkForI2CConnection(BAddr); + button.onReceived = [this](ButtonSensor::SensorData & data){ + updateObservers(&SensorArrayObserver::buttonDidUpdate, data); + }; } void update() override { @@ -103,5 +113,6 @@ class SensorArray : public KPComponent, public KPSubject { pressure.update(); baro1.update(); baro2.update(); + button.update(); } }; \ No newline at end of file diff --git a/src/Components/SensorArrayObserver.hpp b/src/Components/SensorArrayObserver.hpp index 0f8fcbd..da453bd 100644 --- a/src/Components/SensorArrayObserver.hpp +++ b/src/Components/SensorArrayObserver.hpp @@ -3,6 +3,7 @@ #include #include #include +#include class SensorArrayObserver : public KPObserver { public: @@ -16,4 +17,5 @@ class SensorArrayObserver : public KPObserver { virtual void pressureSensorDidUpdate(PressureSensor::SensorData & values) {} virtual void baro1DidUpdate(BaroSensor::SensorData & values) {} virtual void baro2DidUpdate(BaroSensor::SensorData & values) {} + virtual void buttonDidUpdate(ButtonSensor::SensorData & values) {} }; diff --git a/src/Components/Sensors/ButtonSensor.hpp b/src/Components/Sensors/ButtonSensor.hpp new file mode 100644 index 0000000..49a9ff5 --- /dev/null +++ b/src/Components/Sensors/ButtonSensor.hpp @@ -0,0 +1,38 @@ +#pragma once +#include +#include +#include + +typedef union { + struct + { + bool eventAvailable : 1; //This is bit 0. User mutable, gets set to 1 when a new event occurs. User is expected to write 0 to clear the flag. + bool hasBeenClicked : 1; //Defaults to zero on POR. Gets set to one when the button gets clicked. Must be cleared by the user. + bool isPressed : 1; //Gets set to one if button is pushed. + bool : 5; + }; + uint8_t byteWrapped; +} status; + +class ButtonSensor : public Sensor { +private: + const int ADDR; + void begin() override { + setUpdateFreq(3); + Wire.begin(); + }; + +public: + ButtonSensor(int addr) : ADDR(addr) {} + + SensorData read() override { + Wire.beginTransmission(ADDR); + Wire.write(0x03); //CHANGE LATER + Wire.endTransmission(); + if(Wire.requestFrom(ADDR, static_cast(1)) != 0){ + status s; + s.byteWrapped = Wire.read(); + return {(int)s.isPressed, 1}; + } + } +}; diff --git a/src/StateControllers/NowTaskStateController.cpp b/src/StateControllers/NowTaskStateController.cpp new file mode 100644 index 0000000..0fb48b5 --- /dev/null +++ b/src/StateControllers/NowTaskStateController.cpp @@ -0,0 +1,37 @@ +#include +#include +void NowTaskStateController::setup() { + // registerState(SharedStates::Flush(), FLUSH1, [this](int code) { + // switch (code) { + // case 0: + // return transitionTo(OFFSHOOT_CLEAN_1); + // default: + // halt(TRACE, "Unhandled state transition"); + // } + // }); + // ..or alternatively if state only has one input and one output + + registerState(SharedStates::Flush(), FLUSH_1, OFFSHOOT_CLEAN_1); + registerState(SharedStates::OffshootClean(5), OFFSHOOT_CLEAN_1, FLUSH_2); + registerState(SharedStates::Flush(), FLUSH_2, SAMPLE); + registerState(SharedStates::Sample(), SAMPLE, [this](int code) { + auto & app = *static_cast(controller); + app.sensors.flow.stopMeasurement(); + app.logAfterSample(); + + switch (code) { + case 0: + return transitionTo(OFFSHOOT_CLEAN_2); + default: + halt(TRACE, "Unhandled state transition: ", code); + } + }); + registerState(SharedStates::OffshootClean(10), OFFSHOOT_CLEAN_2, DRY); + registerState(SharedStates::Dry(), DRY, PRESERVE); + registerState(SharedStates::Preserve(), PRESERVE, AIR_FLUSH); + registerState(SharedStates::AirFlush(), AIR_FLUSH, STOP); + + // Reusing STOP and IDLE states from MainStateController + registerState(Main::Stop(), STOP, IDLE); + registerState(Main::Idle(), IDLE); +}; \ No newline at end of file diff --git a/src/StateControllers/NowTaskStateController.hpp b/src/StateControllers/NowTaskStateController.hpp new file mode 100644 index 0000000..fb48667 --- /dev/null +++ b/src/StateControllers/NowTaskStateController.hpp @@ -0,0 +1,72 @@ +#pragma once +#include +#include +#include + +namespace NowT{ + STATE(IDLE); + STATE(FLUSH_1); + STATE(OFFSHOOT_CLEAN_1); + STATE(FLUSH_2); + STATE(SAMPLE); + STATE(OFFSHOOT_CLEAN_2); + STATE(DRY); + STATE(PRESERVE); + STATE(AIR_FLUSH); + STATE(STOP); + + struct Config { + decltype(SharedStates::Flush::time) flushTime; + decltype(SharedStates::Sample::time) sampleTime; + decltype(SharedStates::Sample::pressure) samplePressure; + decltype(SharedStates::Sample::volume) sampleVolume; + decltype(SharedStates::Dry::time) dryTime; + decltype(SharedStates::Preserve::time) preserveTime; + decltype(SharedStates::OffshootPreload::preloadTime) preloadTime; + }; + + class Controller : public StateControllerWithConfig { + public: + Controller() : StateControllerWithConfig("nowtask-state-controller") {} + + void setup(); + + void configureStates() { + decltype(auto) flush1 = getState(FLUSH_1); + flush1.time = config.flushTime; + + decltype(auto) flush2 = getState(FLUSH_2); + flush2.time = config.flushTime; + + decltype(auto) sample = getState(SAMPLE); + sample.time = config.sampleTime; + sample.pressure = config.samplePressure; + sample.volume = config.sampleVolume; + + decltype(auto) dry = getState(DRY); + dry.time = config.dryTime; + + decltype(auto) preserve = getState(PRESERVE); + preserve.time = config.preserveTime; + } + + void begin() override { + configureStates(); + transitionTo(FLUSH_1); + } + + void stop() override { + transitionTo(STOP); + } + + void idle() override { + transitionTo(IDLE); + } + + unsigned long get_total_time() { + return config.flushTime + config.flushTime + config.sampleTime + config.dryTime + config.preserveTime; + } + }; +} // namespace HyperFlush + +using NowTaskStateController = NowT::Controller; diff --git a/src/Task/NowTask.hpp b/src/Task/NowTask.hpp new file mode 100644 index 0000000..7ae4155 --- /dev/null +++ b/src/Task/NowTask.hpp @@ -0,0 +1,169 @@ +#pragma once +#include +#include + +#include + +#include +#include +#include + +#include +#include + +struct NowTask : public JsonEncodable, + public JsonDecodable, + public Printable, + public NowTaskStateController::Configurator { +public: + friend class NowTaskManager; + + int id; + char name[TaskSettings::NAME_LENGTH]{0}; + char notes[TaskSettings::NOTES_LENGTH]{0}; + + long createdAt = 0; + long schedule = 0; + + int status = TaskStatus::inactive; + int timeBetween = 0; + + int flushTime = 0; + float flushVolume = 0; + int sampleTime = 0; + float sampleVolume = 0; + int samplePressure = 0; + int dryTime = 0; + int preserveTime = 0; + + bool deleteOnCompletion = false; + int* valve; +// std::vector valves; + +public: + int valveOffsetStart = 0; + +public: + NowTask() { + valve = (int*) malloc(sizeof(int)); + *valve = 0; + } + NowTask(const NowTask & other) { + valve = (int*) malloc(sizeof(int)); + *valve = *other.valve; + }; + NowTask & operator=(const NowTask &) = default; + + explicit NowTask(const JsonObject & data) { + decodeJSON(data); + } + + ~NowTask() { + free(valve); + } + + int getValveOffsetStart() const { + return valveOffsetStart; + } + + int getNumberOfValves() const { + return 1; + //return valves.size(); + } + + bool isCompleted() const { + if((*valve < ProgramSettings::MAX_VALVES) && (status == TaskStatus::completed)){ + *valve += 1; + } + return status == TaskStatus::completed; + } + + /** ──────────────────────────────────────────────────────────────────────────── + * @brief Get the Current Valve ID + * + * @return int -1 if no more valve, otherwise returns the valve number + * ──────────────────────────────────────────────────────────────────────────── */ + int getCurrentValveId() const { + return *valve; + //return (getValveOffsetStart() >= getNumberOfValves()) ? -1 : valves[getValveOffsetStart()]; + } + +#pragma region JSONDECODABLE + static const char * decoderName() { + return "NowTask"; + } + + static constexpr size_t decodingSize() { + return ProgramSettings::TASK_JSON_BUFFER_SIZE; + } + + void decodeJSON(const JsonVariant & source) override { + using namespace TaskSettings; + using namespace TaskKeys; + + if (source.containsKey(NAME)) { + snprintf(name, NAME_LENGTH, "%s", source[NAME].as()); + } + + if (source.containsKey(NOTES)) { + snprintf(notes, NOTES_LENGTH, "%s", source[NOTES].as()); + } + + id = source[ID]; + status = source[STATUS]; + flushTime = source[FLUSH_TIME]; + flushVolume = source[FLUSH_VOLUME]; + sampleTime = source[SAMPLE_TIME]; + samplePressure = source[SAMPLE_PRESSURE]; + sampleVolume = source[SAMPLE_VOLUME]; + dryTime = source[DRY_TIME]; + preserveTime = source[PRESERVE_TIME]; + *valve = source[CURR_VALVE]; + } +#pragma endregion +#pragma region JSONENCODABLE + + static const char * encoderName() { + return "NowTask"; + } + + static constexpr size_t encodingSize() { + return ProgramSettings::TASK_JSON_BUFFER_SIZE; + } + + bool encodeJSON(const JsonVariant & dst) const override { + using namespace TaskKeys; + // clang-format off + return dst[ID].set(id) + && dst[NAME].set((char *)name) + && dst[NOTES].set((char *) notes) + && dst[STATUS].set(status) + && dst[FLUSH_TIME].set(flushTime) + && dst[FLUSH_VOLUME].set(flushVolume) + && dst[SAMPLE_TIME].set(sampleTime) + && dst[SAMPLE_PRESSURE].set(samplePressure) + && dst[SAMPLE_VOLUME].set(sampleVolume) + && dst[DRY_TIME].set(dryTime) + && dst[PRESERVE_TIME].set(preserveTime) + && dst[VALVES_OFFSET].set(getValveOffsetStart()) + && dst[DELETE].set(deleteOnCompletion) + && dst[CURR_VALVE].set(*valve); + } // clang-format on + + size_t printTo(Print & printer) const override { + StaticJsonDocument doc; + JsonVariant object = doc.to(); + encodeJSON(object); + return serializeJsonPretty(doc, printer); + } +#pragma endregion + + void operator()(NowTaskStateController::Config & config) const { + config.flushTime = flushTime; + config.sampleTime = sampleTime; + config.samplePressure = samplePressure; + config.sampleVolume = sampleVolume; + config.dryTime = dryTime; + config.preserveTime = preserveTime; + } +}; \ No newline at end of file diff --git a/src/Task/NowTaskManager.hpp b/src/Task/NowTaskManager.hpp new file mode 100644 index 0000000..379c44d --- /dev/null +++ b/src/Task/NowTaskManager.hpp @@ -0,0 +1,183 @@ +#pragma once +#include +#include +#include + +#include +#include +#include +#include + +#include +#include "SD.h" + +class NowTaskManager : public KPComponent, + public JsonEncodable, + public Printable, + public KPSubject { + +public: + const char * taskFolder = nullptr; + NowTask task; + + NowTaskManager() : KPComponent("NowTaskManager") {} + + void init(Config & config) { + taskFolder = config.taskFolder; + } + + int generateTaskId() const { + return random(1, RAND_MAX); + } +/* + Task createTask() { + const auto timenow = now(); + Task task; + task.id = generateTaskId(); + task.createdAt = timenow; + task.schedule = timenow; + return task; + } + + + bool advanceTask(int id) { + if (!findTask(id)) { + return false; + } + + auto & task = tasks[id]; + println(GREEN("Task Time betwen: "), task.timeBetween); + task.schedule = now() + std::max(task.timeBetween, 5); + if (++task.valveOffsetStart >= task.getNumberOfValves()) { + return markTaskAsCompleted(id); + } + + return true; + } +*/ + bool setTaskStatus(int id, TaskStatus status) { + task.status = status; + updateObservers(&NowTaskObserver::taskDidUpdate, task); + return true; + } + + int numberOfActiveTasks() const { + if(task.status == TaskStatus::active){ + return 1; + } + return 0; + } + + /* + bool markTaskAsCompleted(int id) { + task.valves.clear(); + if (task.deleteOnCompletion) { + println("DELETED: ", id); + deleteTask(id); + } else { + task.status = TaskStatus::completed; + updateObservers(&TaskObserver::taskDidUpdate, task); + } + + return true; + } + + bool findTask(int id) const { + return tasks.find(id) != tasks.end(); + } + + bool deleteTask(int id) { + if (tasks.erase(id)) { + updateObservers(&TaskObserver::taskDidDelete, id); + return true; + } + + return false; + } + + int deleteIf(std::function predicate) { + int oldSize = tasks.size(); + for (auto it = tasks.begin(); it != tasks.end();) { + if (predicate(it->second)) { + auto id = it->first; + it = tasks.erase(it); + updateObservers(&TaskObserver::taskDidDelete, id); + } else { + it++; + } + } + + return oldSize - tasks.size(); + } + */ + + /** ──────────────────────────────────────────────────────────────────────────── + * @brief Load all tasks object from the specified directory in the SD card + * + * @param _dir Path to tasks directory (default=~/tasks) + * ──────────────────────────────────────────────────────────────────────────── */ + void loadTasksFromDirectory(const char * _dir = nullptr) { + const char * dir = _dir ? _dir : taskFolder; + + JsonFileLoader loader; + loader.createDirectoryIfNeeded(dir); + + // Load task index file and get the number of tasks + auto start = millis(); + KPStringBuilder<32> filepath(dir, "/NowTask.js"); + loader.load(filepath, task); + + println(GREEN("Task Manager"), " finished reading in ", millis() - start, " ms\n"); + // updateObservers(&TaskObserver::taskCollectionDidUpdate, tasks.begin()); + } + + + /** ──────────────────────────────────────────────────────────────────────────── + * @brief Write task array to SD directory + * + * @param _dir Path to tasks directory (default=~/tasks) + * ──────────────────────────────────────────────────────────────────────────── */ + void writeToDirectory(const char * _dir = nullptr) { + const char * dir = _dir ? _dir : taskFolder; + + JsonFileLoader loader; + loader.createDirectoryIfNeeded(dir); + + println("Writing Now Task"); + + KPStringBuilder<32> filename("NowTask.js"); + KPStringBuilder<64> filepath(dir, "/", filename); + loader.save(filepath, task); + } + +#pragma region JSONENCODABLE + static const char * encoderName() { + return "NowTaskManager"; + } + + static constexpr size_t encodingSize() { + return NowTask::encodingSize() * 5; + } + + bool encodeJSON(const JsonVariant & dst) const override { + JsonVariant obj = dst.createNestedObject(); + if (!task.encodeJSON(obj)) { + return false; + } + return true; + } +#pragma endregion +#pragma region PRINTABLE + size_t printTo(Print & p) const { + size_t charWritten = 0; + charWritten += p.println("["); + charWritten += p.print(task); + /*for (const auto & kv : tasks) { + charWritten += p.print(kv.second); + charWritten += p.println(","); + }*/ + + return charWritten + p.println("]"); + } +#pragma endregion +}; diff --git a/src/Task/NowTaskObserver.hpp b/src/Task/NowTaskObserver.hpp new file mode 100644 index 0000000..c2f697e --- /dev/null +++ b/src/Task/NowTaskObserver.hpp @@ -0,0 +1,18 @@ +#pragma once +#include +#include +#include + +class NowTaskObserver : public KPObserver { +public: + const char * ObserverName() const { + return NowTaskObserverName(); + } + + virtual const char * NowTaskObserverName() const { + return " Now Task Observer"; + } + + virtual void taskDidUpdate(const NowTask & task) = 0; + //virtual void taskDidDelete(int id) = 0; +}; \ No newline at end of file diff --git a/src/Task/Task.hpp b/src/Task/Task.hpp index 0c10be5..ecf81d6 100755 --- a/src/Task/Task.hpp +++ b/src/Task/Task.hpp @@ -102,8 +102,12 @@ struct Task : public JsonEncodable, } id = source[ID]; - createdAt = source[CREATED_AT]; - schedule = source[SCHEDULE]; + if(source.containsKey(CREATED_AT)){ + createdAt = source[CREATED_AT]; + } + if(source.containsKey(SCHEDULE)){ + schedule = source[SCHEDULE]; + } status = source[STATUS]; flushTime = source[FLUSH_TIME]; flushVolume = source[FLUSH_VOLUME]; From ac93301875813489a08450d0b2ea03c68b4f12d8 Mon Sep 17 00:00:00 2001 From: Nathan Jesudason Date: Wed, 16 Jun 2021 11:28:04 -0700 Subject: [PATCH 02/61] reset NowTask with valve reset --- src/Application/App-wifi.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Application/App-wifi.cpp b/src/Application/App-wifi.cpp index 84ad9a9..ead7a8b 100644 --- a/src/Application/App-wifi.cpp +++ b/src/Application/App-wifi.cpp @@ -160,6 +160,7 @@ void App::setupServerRouting() { vm.setValveStatus(i, ValveStatus::Code(config.valves[i])); } vm.writeToDirectory(); + *(ntm.task.valve) = 0; res.end(); }); } \ No newline at end of file From bfde09a8680ea4b9dd3b6bcb1634bde8a376b201 Mon Sep 17 00:00:00 2001 From: Nathan Jesudason Date: Wed, 23 Jun 2021 14:22:40 -0700 Subject: [PATCH 03/61] Changed button implementation from I2C to Pin --- platformio.ini | 12 +-- src/Application/App.hpp | 25 +++-- src/Application/Status.hpp | 11 -- src/Components/NowSampleButton.cpp | 15 +++ src/Components/NowSampleButton.hpp | 105 +++++++++++++++++++ src/Components/SensorArray.hpp | 8 -- src/Components/SensorArrayObserver.hpp | 2 - src/Components/Sensors/ButtonSensor.hpp | 38 ------- src/StateControllers/MainStateController.cpp | 5 + src/Task/NowTaskManager.hpp | 36 +++---- src/Task/NowTaskObserver.hpp | 2 +- 11 files changed, 160 insertions(+), 99 deletions(-) create mode 100644 src/Components/NowSampleButton.cpp create mode 100644 src/Components/NowSampleButton.hpp delete mode 100644 src/Components/Sensors/ButtonSensor.hpp diff --git a/platformio.ini b/platformio.ini index f5865ee..5f0805e 100644 --- a/platformio.ini +++ b/platformio.ini @@ -26,15 +26,15 @@ lib_deps = 868@~1.2.4 DS3232RTC -[env:debug] +; [env:debug] ; build_type = debug -build_unflags = -std=gnu++11 -build_flags = -D DEBUG=1 -Wall -Wno-unknown-pragmas -std=c++14 +; build_unflags = -std=gnu++11 +; build_flags = -D DEBUG=1 -Wall -Wno-unknown-pragmas -std=c++14 ; [env:release] ; build_unflags = -std=gnu++11 ; build_flags = -D RELEASE=1 -Wall -Wno-unknown-pragmas -std=c++14 -; [env:live] -; build_unflags = -std=gnu++11 -; build_flags = -D LIVE=1 -Wall -Wno-unknown-pragmas -std=c++14 \ No newline at end of file +[env:live] +build_unflags = -std=gnu++11 +build_flags = -D LIVE=1 -Wall -Wno-unknown-pragmas -std=c++14 \ No newline at end of file diff --git a/src/Application/App.hpp b/src/Application/App.hpp index 77c9e7b..b5abca3 100755 --- a/src/Application/App.hpp +++ b/src/Application/App.hpp @@ -14,6 +14,7 @@ #include #include #include +#include #include #include @@ -36,7 +37,7 @@ #include -class App : public KPController, public KPSerialInputObserver, public TaskObserver { +class App : public KPController, public KPSerialInputObserver, public TaskObserver, public NowTaskObserver { private: void setupAPI(); void setupSerialRouting(); @@ -63,6 +64,7 @@ class App : public KPController, public KPSerialInputObserver, public TaskObserv }; Power power{"power"}; + NowSampleButton nowSampleButton{"nowSampleButton"}; BallIntake intake{shift}; Config config{ProgramSettings::CONFIG_FILE_PATH}; Status status; @@ -133,7 +135,7 @@ class App : public KPController, public KPSerialInputObserver, public TaskObserv addComponent(pump); addComponent(sensors); sensors.addObserver(status); - + addComponent(nowSampleButton); // // ─── LOADING CONFIG FILE ───────────────────────────────────────── // @@ -221,6 +223,15 @@ class App : public KPController, public KPSerialInputObserver, public TaskObserv println(scheduleNextActiveTask().description()); }); + + nowSampleButton.onInterrupt([this](){ + println(RED("Now Sampling!")); + NowTask task = ntm.task; + nowTaskStateController.configure(task); + if(now() - last_nowTask > nowTaskStateController.get_total_time()){ + beginNowTask(); + } + }); runForever(1000, "detailLog", [&]() { logDetail("detail.csv"); }); #ifdef DEBUG runForever(2000, "memLog", [&]() { printFreeRam(); }); @@ -472,14 +483,6 @@ class App : public KPController, public KPSerialInputObserver, public TaskObserv * ──────────────────────────────────────────────────────────────────────────── */ void update() override { KPController::update(); - - if(status.buttonPressed){ - NowTask task = ntm.task; - nowTaskStateController.configure(task); - if(now() - last_nowTask > nowTaskStateController.get_total_time()){ - const auto & response = dispatchAPI(); - } - } if (!status.isProgrammingMode() && !status.preventShutdown) { shutdown(); } @@ -511,7 +514,7 @@ class App : public KPController, public KPSerialInputObserver, public TaskObserv private: void taskDidUpdate(const Task & task) override {} - + void nowTaskDidUpdate(const NowTask & task) override {} void taskDidDelete(int id) override { if (currentTaskId == id) { currentTaskId = 0; diff --git a/src/Application/Status.hpp b/src/Application/Status.hpp index a07c5da..e239371 100755 --- a/src/Application/Status.hpp +++ b/src/Application/Status.hpp @@ -117,17 +117,6 @@ class Status : public JsonDecodable, waterDepth = std::get<0>(values); } - void buttonDidUpdate(ButtonSensor::SensorData & values) override { - latestButtonPress = (bool)std::get<0>(values); - if (latestButtonPress != buttonTriggered && (unsigned long) (millis() - buttonStart) < 1000) { - buttonTriggered = true; - buttonStart = millis(); - - } else { - buttonPressed = latestButtonPress; - } - } - bool isBatteryLow() const { analogReadResolution(10); return analogRead(HardwarePins::BATTERY_VOLTAGE) <= 860; // 860 is around 12V of battery diff --git a/src/Components/NowSampleButton.cpp b/src/Components/NowSampleButton.cpp new file mode 100644 index 0000000..2291b82 --- /dev/null +++ b/src/Components/NowSampleButton.cpp @@ -0,0 +1,15 @@ +#include "Components/NowSampleButton.hpp" + +volatile unsigned long buttonInterruptStart = 0; +volatile bool buttonTriggered = false; +volatile int buttonFlag = 0; + +void button_isr() { + if ((unsigned long) (millis() - buttonInterruptStart) < 200) { + return; + } + + buttonTriggered = true; + buttonInterruptStart = millis(); + buttonFlag = 1; +} diff --git a/src/Components/NowSampleButton.hpp b/src/Components/NowSampleButton.hpp new file mode 100644 index 0000000..accf5e9 --- /dev/null +++ b/src/Components/NowSampleButton.hpp @@ -0,0 +1,105 @@ +/** + * Copyright (c) 2020 Kawin Pechetratanapanit + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#pragma once +#include +#include +#include + +#include + + +// +// ──────────────────────────────────────────────────────────────────────── I ────────── +// :::::: B U T T O N M A N A G E M E N T : : : : : : : : +// ────────────────────────────────────────────────────────────────────────────────── +// +// +// + +extern volatile unsigned long buttonInterruptStart; +extern volatile bool buttonTriggered; +extern volatile int buttonFlag; +extern void button_isr(); + +class NowSampleButton : public KPComponent { +public: + std::function interruptCallback; + + NowSampleButton(const char * name) : KPComponent(name) {} + + void onInterrupt(std::function callbcak) { + interruptCallback = callbcak; + } + + void setupButton() { + pinMode(HardwarePins::BUTTON_PIN, INPUT); + + print("Button Set Up"); + println(); + } + + void setup() override { + setupButton(); + } + + /** ──────────────────────────────────────────────────────────────────────────── + * Runtime update loop. Check if Button has been triggered. + + * ──────────────────────────────────────────────────────────────────────────── */ + void update() override { + if (!buttonTriggered || !interruptCallback) { + return; + } + + // Continue if button has a new press + if (buttonFlag) { + interruptCallback(); + buttonTriggered = false; + } + } + + + + + + /** ──────────────────────────────────────────────────────────────────────────── + * Disable button + * + * ──────────────────────────────────────────────────────────────────────────── */ + void disableSampleButton() { + detachInterrupt(digitalPinToInterrupt(HardwarePins::BUTTON_PIN)); + } + + + + + /** ──────────────────────────────────────────────────────────────────────────── + * Set Button to be able to be pressed again + * ──────────────────────────────────────────────────────────────────────────── */ + void setSampleButton() { + buttonFlag = 0; + attachInterrupt(digitalPinToInterrupt(HardwarePins::BUTTON_PIN), button_isr, RISING); + } + + + +}; diff --git a/src/Components/SensorArray.hpp b/src/Components/SensorArray.hpp index 71fb698..2128196 100644 --- a/src/Components/SensorArray.hpp +++ b/src/Components/SensorArray.hpp @@ -57,13 +57,11 @@ #include #include #include -#include #define PSAddr 0x08 #define FSAddr 0x07 #define BSAddr 0x77 #define DSAddr 0x76 -#define BAddr 0x88 //CHANGE LATER inline bool checkForI2CConnection(unsigned char addr) { Wire.begin(); @@ -79,7 +77,6 @@ class SensorArray : public KPComponent, public KPSubject { PressureSensor pressure{PSAddr}; BaroSensor baro1{BSAddr}; BaroSensor baro2{DSAddr}; - ButtonSensor button{BAddr}; void setup() override { @@ -102,10 +99,6 @@ class SensorArray : public KPComponent, public KPSubject { updateObservers(&SensorArrayObserver::baro2DidUpdate, data); }; - button.enabled = checkForI2CConnection(BAddr); - button.onReceived = [this](ButtonSensor::SensorData & data){ - updateObservers(&SensorArrayObserver::buttonDidUpdate, data); - }; } void update() override { @@ -113,6 +106,5 @@ class SensorArray : public KPComponent, public KPSubject { pressure.update(); baro1.update(); baro2.update(); - button.update(); } }; \ No newline at end of file diff --git a/src/Components/SensorArrayObserver.hpp b/src/Components/SensorArrayObserver.hpp index da453bd..0f8fcbd 100644 --- a/src/Components/SensorArrayObserver.hpp +++ b/src/Components/SensorArrayObserver.hpp @@ -3,7 +3,6 @@ #include #include #include -#include class SensorArrayObserver : public KPObserver { public: @@ -17,5 +16,4 @@ class SensorArrayObserver : public KPObserver { virtual void pressureSensorDidUpdate(PressureSensor::SensorData & values) {} virtual void baro1DidUpdate(BaroSensor::SensorData & values) {} virtual void baro2DidUpdate(BaroSensor::SensorData & values) {} - virtual void buttonDidUpdate(ButtonSensor::SensorData & values) {} }; diff --git a/src/Components/Sensors/ButtonSensor.hpp b/src/Components/Sensors/ButtonSensor.hpp deleted file mode 100644 index 49a9ff5..0000000 --- a/src/Components/Sensors/ButtonSensor.hpp +++ /dev/null @@ -1,38 +0,0 @@ -#pragma once -#include -#include -#include - -typedef union { - struct - { - bool eventAvailable : 1; //This is bit 0. User mutable, gets set to 1 when a new event occurs. User is expected to write 0 to clear the flag. - bool hasBeenClicked : 1; //Defaults to zero on POR. Gets set to one when the button gets clicked. Must be cleared by the user. - bool isPressed : 1; //Gets set to one if button is pushed. - bool : 5; - }; - uint8_t byteWrapped; -} status; - -class ButtonSensor : public Sensor { -private: - const int ADDR; - void begin() override { - setUpdateFreq(3); - Wire.begin(); - }; - -public: - ButtonSensor(int addr) : ADDR(addr) {} - - SensorData read() override { - Wire.beginTransmission(ADDR); - Wire.write(0x03); //CHANGE LATER - Wire.endTransmission(); - if(Wire.requestFrom(ADDR, static_cast(1)) != 0){ - status s; - s.byteWrapped = Wire.read(); - return {(int)s.isPressed, 1}; - } - } -}; diff --git a/src/StateControllers/MainStateController.cpp b/src/StateControllers/MainStateController.cpp index 38bda04..3d9c5e9 100644 --- a/src/StateControllers/MainStateController.cpp +++ b/src/StateControllers/MainStateController.cpp @@ -19,6 +19,11 @@ void Main::Stop::enter(KPStateMachine & sm) { app.tm.advanceTask(currentTaskId); app.tm.writeToDirectory(); + if(app.ntm.advanceTask()){ + app.nowSampleButton.setSampleButton(); + } + app.ntm.writeToDirectory(); + app.currentTaskId = 0; app.status.currentValve = -1; sm.next(); diff --git a/src/Task/NowTaskManager.hpp b/src/Task/NowTaskManager.hpp index 379c44d..a161d20 100644 --- a/src/Task/NowTaskManager.hpp +++ b/src/Task/NowTaskManager.hpp @@ -39,25 +39,22 @@ class NowTaskManager : public KPComponent, return task; } +*/ - bool advanceTask(int id) { - if (!findTask(id)) { - return false; - } + bool advanceTask() { - auto & task = tasks[id]; println(GREEN("Task Time betwen: "), task.timeBetween); task.schedule = now() + std::max(task.timeBetween, 5); if (++task.valveOffsetStart >= task.getNumberOfValves()) { - return markTaskAsCompleted(id); + return markTaskAsCompleted(); } - return true; + return false; } -*/ - bool setTaskStatus(int id, TaskStatus status) { + + bool setTaskStatus(TaskStatus status) { task.status = status; - updateObservers(&NowTaskObserver::taskDidUpdate, task); + updateObservers(&NowTaskObserver::nowTaskDidUpdate, task); return true; } @@ -68,20 +65,15 @@ class NowTaskManager : public KPComponent, return 0; } - /* - bool markTaskAsCompleted(int id) { - task.valves.clear(); - if (task.deleteOnCompletion) { - println("DELETED: ", id); - deleteTask(id); - } else { - task.status = TaskStatus::completed; - updateObservers(&TaskObserver::taskDidUpdate, task); - } + + bool markTaskAsCompleted() { - return true; - } + task.status = TaskStatus::completed; + updateObservers(&NowTaskObserver::nowTaskDidUpdate, task); + return task.isCompleted(); + } + /* bool findTask(int id) const { return tasks.find(id) != tasks.end(); } diff --git a/src/Task/NowTaskObserver.hpp b/src/Task/NowTaskObserver.hpp index c2f697e..65e69d6 100644 --- a/src/Task/NowTaskObserver.hpp +++ b/src/Task/NowTaskObserver.hpp @@ -13,6 +13,6 @@ class NowTaskObserver : public KPObserver { return " Now Task Observer"; } - virtual void taskDidUpdate(const NowTask & task) = 0; + virtual void nowTaskDidUpdate(const NowTask & task) = 0; //virtual void taskDidDelete(int id) = 0; }; \ No newline at end of file From ec524fee2a6f4bb6c57690f034112a5274cdb880 Mon Sep 17 00:00:00 2001 From: Nathan Jesudason Date: Fri, 2 Jul 2021 18:52:09 -0700 Subject: [PATCH 04/61] fixed interrupt and now task config --- platformio.ini | 7 +++---- src/Application/App.hpp | 16 +++++++++++---- src/Components/NowSampleButton.hpp | 10 +++++++--- src/StateControllers/MainStateController.cpp | 1 + .../NowTaskStateController.cpp | 4 ++++ src/Task/NowTask.hpp | 4 ---- src/Task/NowTaskManager.hpp | 20 ++++++++++++------- 7 files changed, 40 insertions(+), 22 deletions(-) diff --git a/platformio.ini b/platformio.ini index 5f0805e..a841a72 100644 --- a/platformio.ini +++ b/platformio.ini @@ -26,10 +26,9 @@ lib_deps = 868@~1.2.4 DS3232RTC -; [env:debug] -; build_type = debug -; build_unflags = -std=gnu++11 -; build_flags = -D DEBUG=1 -Wall -Wno-unknown-pragmas -std=c++14 +[env:debug] +build_unflags = -std=gnu++11 +build_flags = -D DEBUG=1 -Wall -Wno-unknown-pragmas -std=c++14 ; [env:release] ; build_unflags = -std=gnu++11 diff --git a/src/Application/App.hpp b/src/Application/App.hpp index b5abca3..1de9c95 100755 --- a/src/Application/App.hpp +++ b/src/Application/App.hpp @@ -73,7 +73,6 @@ class App : public KPController, public KPSerialInputObserver, public TaskObserv NewStateController newStateController; HyperFlushStateController hyperFlushStateController; NowTaskStateController nowTaskStateController; - time_t last_nowTask = now(); ValveManager vm; TaskManager tm; @@ -160,6 +159,14 @@ class App : public KPController, public KPSerialInputObserver, public TaskObserv tm.addObserver(this); tm.loadTasksFromDirectory(config.taskFolder); + + // + // ___ ADDING NOW TASK MANAGER _____________________________________ + // + + ntm.init(config); + ntm.addObserver(this); + ntm.loadTasksFromDirectory(config.taskFolder); // // ─── HYPER FLUSH CONTROLLER ────────────────────────────────────── // @@ -226,9 +233,10 @@ class App : public KPController, public KPSerialInputObserver, public TaskObserv nowSampleButton.onInterrupt([this](){ println(RED("Now Sampling!")); - NowTask task = ntm.task; - nowTaskStateController.configure(task); - if(now() - last_nowTask > nowTaskStateController.get_total_time()){ + //NowTask task = ntm.task; + nowTaskStateController.configure(ntm.task); + if(now() - ntm.last_nowTask > nowTaskStateController.get_total_time()){ + ntm.last_nowTask = now(); beginNowTask(); } }); diff --git a/src/Components/NowSampleButton.hpp b/src/Components/NowSampleButton.hpp index accf5e9..b535c1b 100644 --- a/src/Components/NowSampleButton.hpp +++ b/src/Components/NowSampleButton.hpp @@ -52,7 +52,8 @@ class NowSampleButton : public KPComponent { void setupButton() { pinMode(HardwarePins::BUTTON_PIN, INPUT); - + buttonFlag = 0; + attachInterrupt(digitalPinToInterrupt(HardwarePins::BUTTON_PIN), button_isr, RISING); print("Button Set Up"); println(); } @@ -71,9 +72,11 @@ class NowSampleButton : public KPComponent { } // Continue if button has a new press - if (buttonFlag) { - interruptCallback(); + if (buttonFlag != 0) { + detachInterrupt(digitalPinToInterrupt(HardwarePins::BUTTON_PIN)); buttonTriggered = false; + interruptCallback(); + } } @@ -97,6 +100,7 @@ class NowSampleButton : public KPComponent { * ──────────────────────────────────────────────────────────────────────────── */ void setSampleButton() { buttonFlag = 0; + println("interrupt attached"); attachInterrupt(digitalPinToInterrupt(HardwarePins::BUTTON_PIN), button_isr, RISING); } diff --git a/src/StateControllers/MainStateController.cpp b/src/StateControllers/MainStateController.cpp index 3d9c5e9..e0c619f 100644 --- a/src/StateControllers/MainStateController.cpp +++ b/src/StateControllers/MainStateController.cpp @@ -20,6 +20,7 @@ void Main::Stop::enter(KPStateMachine & sm) { app.tm.writeToDirectory(); if(app.ntm.advanceTask()){ + println(BLUE("Setting now sample button to be pressed again")); app.nowSampleButton.setSampleButton(); } app.ntm.writeToDirectory(); diff --git a/src/StateControllers/NowTaskStateController.cpp b/src/StateControllers/NowTaskStateController.cpp index 0fb48b5..24c6e9f 100644 --- a/src/StateControllers/NowTaskStateController.cpp +++ b/src/StateControllers/NowTaskStateController.cpp @@ -15,6 +15,9 @@ void NowTaskStateController::setup() { registerState(SharedStates::OffshootClean(5), OFFSHOOT_CLEAN_1, FLUSH_2); registerState(SharedStates::Flush(), FLUSH_2, SAMPLE); registerState(SharedStates::Sample(), SAMPLE, [this](int code) { + #if DEBUG + return transitionTo(OFFSHOOT_CLEAN_2); + #else auto & app = *static_cast(controller); app.sensors.flow.stopMeasurement(); app.logAfterSample(); @@ -25,6 +28,7 @@ void NowTaskStateController::setup() { default: halt(TRACE, "Unhandled state transition: ", code); } + #endif }); registerState(SharedStates::OffshootClean(10), OFFSHOOT_CLEAN_2, DRY); registerState(SharedStates::Dry(), DRY, PRESERVE); diff --git a/src/Task/NowTask.hpp b/src/Task/NowTask.hpp index 7ae4155..c6bf1f8 100644 --- a/src/Task/NowTask.hpp +++ b/src/Task/NowTask.hpp @@ -135,8 +135,6 @@ struct NowTask : public JsonEncodable, using namespace TaskKeys; // clang-format off return dst[ID].set(id) - && dst[NAME].set((char *)name) - && dst[NOTES].set((char *) notes) && dst[STATUS].set(status) && dst[FLUSH_TIME].set(flushTime) && dst[FLUSH_VOLUME].set(flushVolume) @@ -145,8 +143,6 @@ struct NowTask : public JsonEncodable, && dst[SAMPLE_VOLUME].set(sampleVolume) && dst[DRY_TIME].set(dryTime) && dst[PRESERVE_TIME].set(preserveTime) - && dst[VALVES_OFFSET].set(getValveOffsetStart()) - && dst[DELETE].set(deleteOnCompletion) && dst[CURR_VALVE].set(*valve); } // clang-format on diff --git a/src/Task/NowTaskManager.hpp b/src/Task/NowTaskManager.hpp index a161d20..fa90416 100644 --- a/src/Task/NowTaskManager.hpp +++ b/src/Task/NowTaskManager.hpp @@ -20,6 +20,8 @@ class NowTaskManager : public KPComponent, const char * taskFolder = nullptr; NowTask task; + time_t last_nowTask = now(); + NowTaskManager() : KPComponent("NowTaskManager") {} void init(Config & config) { @@ -43,15 +45,20 @@ class NowTaskManager : public KPComponent, bool advanceTask() { - println(GREEN("Task Time betwen: "), task.timeBetween); - task.schedule = now() + std::max(task.timeBetween, 5); - if (++task.valveOffsetStart >= task.getNumberOfValves()) { + println(GREEN("Checking now task")); + if (now() >= task_length() + last_nowTask) { + println(GREEN("now task complete")); return markTaskAsCompleted(); } return false; } + + int task_length() { + return task.flushTime + task.sampleTime + task.samplePressure + task.dryTime + task.preserveTime; + } + bool setTaskStatus(TaskStatus status) { task.status = status; updateObservers(&NowTaskObserver::nowTaskDidUpdate, task); @@ -114,12 +121,11 @@ class NowTaskManager : public KPComponent, JsonFileLoader loader; loader.createDirectoryIfNeeded(dir); - // Load task index file and get the number of tasks auto start = millis(); - KPStringBuilder<32> filepath(dir, "/NowTask.js"); + KPStringBuilder<32> filepath(dir, "/nowtask.js"); loader.load(filepath, task); - println(GREEN("Task Manager"), " finished reading in ", millis() - start, " ms\n"); + println(GREEN("Now Task Manager"), " finished reading in ", millis() - start, " ms\n"); // updateObservers(&TaskObserver::taskCollectionDidUpdate, tasks.begin()); } @@ -137,7 +143,7 @@ class NowTaskManager : public KPComponent, println("Writing Now Task"); - KPStringBuilder<32> filename("NowTask.js"); + KPStringBuilder<32> filename("nowtask.js"); KPStringBuilder<64> filepath(dir, "/", filename); loader.save(filepath, task); } From a273ba42338d06df3e73966e490937520b8ecda7 Mon Sep 17 00:00:00 2001 From: NathanJesudason Date: Thu, 8 Jul 2021 12:18:16 -0700 Subject: [PATCH 05/61] Changed button interrupt to be rtc interrupt --- src/Application/App.hpp | 9 ++++----- src/Components/NowSampleButton.cpp | 10 ++++------ src/Components/NowSampleButton.hpp | 20 +++++++------------- src/Components/Power.cpp | 8 +++++++- src/Components/Power.hpp | 26 +++++++++++++++++++++++++- 5 files changed, 47 insertions(+), 26 deletions(-) diff --git a/src/Application/App.hpp b/src/Application/App.hpp index 1de9c95..5116cd4 100755 --- a/src/Application/App.hpp +++ b/src/Application/App.hpp @@ -14,7 +14,6 @@ #include #include #include -#include #include #include @@ -64,7 +63,7 @@ class App : public KPController, public KPSerialInputObserver, public TaskObserv }; Power power{"power"}; - NowSampleButton nowSampleButton{"nowSampleButton"}; + //nowSampleButton nowSampleButton{"nowSampleButton"}; BallIntake intake{shift}; Config config{ProgramSettings::CONFIG_FILE_PATH}; Status status; @@ -134,7 +133,7 @@ class App : public KPController, public KPSerialInputObserver, public TaskObserv addComponent(pump); addComponent(sensors); sensors.addObserver(status); - addComponent(nowSampleButton); + //addComponent(nowSampleButton); // // ─── LOADING CONFIG FILE ───────────────────────────────────────── // @@ -231,7 +230,7 @@ class App : public KPController, public KPSerialInputObserver, public TaskObserv }); - nowSampleButton.onInterrupt([this](){ + power.onButtonInterrupt([this](){ println(RED("Now Sampling!")); //NowTask task = ntm.task; nowTaskStateController.configure(ntm.task); @@ -528,4 +527,4 @@ class App : public KPController, public KPSerialInputObserver, public TaskObserv currentTaskId = 0; } } -}; \ No newline at end of file +}; diff --git a/src/Components/NowSampleButton.cpp b/src/Components/NowSampleButton.cpp index 2291b82..b3405ac 100644 --- a/src/Components/NowSampleButton.cpp +++ b/src/Components/NowSampleButton.cpp @@ -1,15 +1,13 @@ -#include "Components/NowSampleButton.hpp" +#/*include "Components/NowSampleButton.hpp" volatile unsigned long buttonInterruptStart = 0; volatile bool buttonTriggered = false; volatile int buttonFlag = 0; void button_isr() { - if ((unsigned long) (millis() - buttonInterruptStart) < 200) { + if (() { return; } - buttonTriggered = true; - buttonInterruptStart = millis(); - buttonFlag = 1; -} + +}*/ diff --git a/src/Components/NowSampleButton.hpp b/src/Components/NowSampleButton.hpp index b535c1b..b4c3011 100644 --- a/src/Components/NowSampleButton.hpp +++ b/src/Components/NowSampleButton.hpp @@ -18,7 +18,7 @@ * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ - +/* #pragma once #include #include @@ -34,10 +34,7 @@ // // // - -extern volatile unsigned long buttonInterruptStart; -extern volatile bool buttonTriggered; -extern volatile int buttonFlag; +; extern void button_isr(); class NowSampleButton : public KPComponent { @@ -66,16 +63,13 @@ class NowSampleButton : public KPComponent { * Runtime update loop. Check if Button has been triggered. * ──────────────────────────────────────────────────────────────────────────── */ - void update() override { + /* void update() override { if (!buttonTriggered || !interruptCallback) { return; } // Continue if button has a new press - if (buttonFlag != 0) { - detachInterrupt(digitalPinToInterrupt(HardwarePins::BUTTON_PIN)); - buttonTriggered = false; - interruptCallback(); + if ( } } @@ -88,7 +82,7 @@ class NowSampleButton : public KPComponent { * Disable button * * ──────────────────────────────────────────────────────────────────────────── */ - void disableSampleButton() { + /*void disableSampleButton() { detachInterrupt(digitalPinToInterrupt(HardwarePins::BUTTON_PIN)); } @@ -98,7 +92,7 @@ class NowSampleButton : public KPComponent { /** ──────────────────────────────────────────────────────────────────────────── * Set Button to be able to be pressed again * ──────────────────────────────────────────────────────────────────────────── */ - void setSampleButton() { + /* void () { buttonFlag = 0; println("interrupt attached"); attachInterrupt(digitalPinToInterrupt(HardwarePins::BUTTON_PIN), button_isr, RISING); @@ -106,4 +100,4 @@ class NowSampleButton : public KPComponent { -}; +};*/ diff --git a/src/Components/Power.cpp b/src/Components/Power.cpp index 9402113..61f46c5 100644 --- a/src/Components/Power.cpp +++ b/src/Components/Power.cpp @@ -4,9 +4,15 @@ volatile unsigned long rtcInterruptStart = 0; volatile bool alarmTriggered = false; void rtc_isr() { - if ((unsigned long) (millis() - rtcInterruptStart) < 1000) { + if (((unsigned long) (millis() - rtcInterruptStart) < 1000) && ((unsigned long) (millis() - buttonInterruptStart) < 200)){ return; } + + if(digitalRead(HardwarePins::BUTTON_PIN){ + buttonTriggered = true; + buttonInterruptStart = millis(); + buttonFlag = 1; + } alarmTriggered = true; rtcInterruptStart = millis(); diff --git a/src/Components/Power.hpp b/src/Components/Power.hpp index af0fcc6..e9d047c 100644 --- a/src/Components/Power.hpp +++ b/src/Components/Power.hpp @@ -42,18 +42,27 @@ extern volatile unsigned long rtcInterruptStart; extern volatile bool alarmTriggered; +extern volatile unsigned long buttonInterruptStart; +extern volatile bool buttonTriggered; +extern volatile int buttonFlag; +bool buttonCanTrigger; extern void rtc_isr(); class Power : public KPComponent { public: DS3232RTC rtc; std::function interruptCallback; + std::function buttonCallback; Power(const char * name) : KPComponent(name), rtc(false) {} void onInterrupt(std::function callbcak) { interruptCallback = callbcak; } + + void onButtonInterrupt(std::function callback){ + buttonCallback = callback; + } void setupRTC() { // Initilize RTC I2C Bus @@ -72,6 +81,7 @@ class Power : public KPComponent { print("RTC startup time: "); printCurrentTime(); println(); + } void setup() override { @@ -82,6 +92,10 @@ class Power : public KPComponent { // so we need this internal one. INPUT_PULLUP is required. pinMode(HardwarePins::RTC_INTERRUPT, INPUT_PULLUP); pinMode(HardwarePins::POWER_MODULE, OUTPUT); + + //init button pin + pinMode(HardwarePins::BUTTON_PIN, INPUT); + buttonCanTrigger = true; } /** ──────────────────────────────────────────────────────────────────────────── @@ -89,10 +103,15 @@ class Power : public KPComponent { * ──────────────────────────────────────────────────────────────────────────── */ void update() override { - if (!alarmTriggered || !interruptCallback) { + if (!alarmTriggered || !buttonTriggered || !interruptCallback) { return; } + if(buttonFlag != 0 && buttonCanTrigger) { + buttonCanTrigger = false; + buttonTriggered = false; + buttonCallback(); + } // Check if the interrupt is comming from RTC // This is important in noisy environment if (rtc.alarm(1) || rtc.alarm(2)) { @@ -293,4 +312,9 @@ class Power : public KPComponent { constexpr time_t FUDGE = 10; return t + FUDGE; } + + void setSampleButton() { + buttonFlag = 0; + buttonCanTrigger = true; + } }; From e95eef20962d03fccfe4cebe2607ec948007aa59 Mon Sep 17 00:00:00 2001 From: Nathan Jesudason Date: Sun, 11 Jul 2021 13:25:30 -0700 Subject: [PATCH 06/61] Change button to active low --- src/Application/Constants.hpp | 1 - src/Components/Power.cpp | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/Application/Constants.hpp b/src/Application/Constants.hpp index 25cded5..16d649d 100755 --- a/src/Application/Constants.hpp +++ b/src/Application/Constants.hpp @@ -97,7 +97,6 @@ namespace TaskKeys { __k_auto SAMPLE_VOLUME = "sampleVolume"; __k_auto DRY_TIME = "dryTime"; __k_auto PRESERVE_TIME = "preserveTime"; - __k_auto CURR_VALVE = "currentValve"; } // namespace TaskKeys namespace ValveKeys { diff --git a/src/Components/Power.cpp b/src/Components/Power.cpp index 61f46c5..6ff8945 100644 --- a/src/Components/Power.cpp +++ b/src/Components/Power.cpp @@ -4,11 +4,11 @@ volatile unsigned long rtcInterruptStart = 0; volatile bool alarmTriggered = false; void rtc_isr() { - if (((unsigned long) (millis() - rtcInterruptStart) < 1000) && ((unsigned long) (millis() - buttonInterruptStart) < 200)){ + if (((unsigned long) (millis() - rtcInterruptStart) < 1000) && ((unsigned long) (millis() - buttonInterruptStart) < 1000)){ return; } - if(digitalRead(HardwarePins::BUTTON_PIN){ + if(digitalRead(HardwarePins::BUTTON_PIN) == LOW){ buttonTriggered = true; buttonInterruptStart = millis(); buttonFlag = 1; From dd6727bdbaff6dd6279d4761fe6aca8884da5e88 Mon Sep 17 00:00:00 2001 From: Nathan Jesudason Date: Tue, 20 Jul 2021 14:11:36 -0700 Subject: [PATCH 07/61] Added Server Routing for sample now --- src/Application/App-wifi.cpp | 13 +++++++++++++ src/Application/Constants.hpp | 1 + 2 files changed, 14 insertions(+) diff --git a/src/Application/App-wifi.cpp b/src/Application/App-wifi.cpp index ead7a8b..cfb1307 100644 --- a/src/Application/App-wifi.cpp +++ b/src/Application/App-wifi.cpp @@ -60,6 +60,19 @@ void App::setupServerRouting() { res.end(); }); + // ──────────────────────────────────────────────────────────────────────────────── + // Get now task object + // ──────────────────────────────────────────────────────────────────────────────── + server.get("/api/nowTask", [this](Request &, Response & res) { + StaticJsonDocument response; + encodeJSON(ntm, response.to()); + + KPStringBuilder<10> length(measureJson(response)); + res.setHeader("Content-Length", length); + res.json(response); + res.end(); + }); + // ──────────────────────────────────────────────────────────────────────────────── // Get task with name // ──────────────────────────────────────────────────────────────────────────────── diff --git a/src/Application/Constants.hpp b/src/Application/Constants.hpp index 16d649d..99000ca 100755 --- a/src/Application/Constants.hpp +++ b/src/Application/Constants.hpp @@ -97,6 +97,7 @@ namespace TaskKeys { __k_auto SAMPLE_VOLUME = "sampleVolume"; __k_auto DRY_TIME = "dryTime"; __k_auto PRESERVE_TIME = "preserveTime"; + __k_auto CURR_VALVE = "currentValve"; } // namespace TaskKeys namespace ValveKeys { From 46423e8e668039b32d1864af8dd7dfd7be28559a Mon Sep 17 00:00:00 2001 From: Nathan Jesudason Date: Wed, 21 Jul 2021 13:21:30 -0700 Subject: [PATCH 08/61] Implemented NowTask saving and fixed build errors --- src/API/API.cpp | 15 +++ src/API/API.hpp | 4 + src/Application/App-wifi.cpp | 12 +++ src/Components/NowSampleButton.cpp | 13 --- src/Components/NowSampleButton.hpp | 103 ------------------- src/Components/Power.cpp | 10 +- src/Components/Power.hpp | 2 +- src/StateControllers/MainStateController.cpp | 2 +- 8 files changed, 40 insertions(+), 121 deletions(-) delete mode 100644 src/Components/NowSampleButton.cpp delete mode 100644 src/Components/NowSampleButton.hpp diff --git a/src/API/API.cpp b/src/API/API.cpp index 0afea1b..a95befc 100644 --- a/src/API/API.cpp +++ b/src/API/API.cpp @@ -103,6 +103,21 @@ namespace API { return response; } + auto NowTaskSave::operator()(Arg<0> & app, Arg<1> & input) -> R { + R response; + // Prarse incomming payload + NowTask incomingTask; + incomingTask.decodeJSON(input.as()); + + + // Save + app.ntm.task = incomingTask; + app.ntm.writeToDirectory(); + + response["success"] = "Task successfully saved"; + return response; + } + auto TaskDelete::operator()(Arg<0> & app, Arg<1> & input) -> R { R response; int id = input["id"]; diff --git a/src/API/API.hpp b/src/API/API.hpp index 8379a73..8c7cd9c 100644 --- a/src/API/API.hpp +++ b/src/API/API.hpp @@ -50,6 +50,10 @@ namespace API { auto operator()(Arg<0>, Arg<1>) -> R; }; + struct NowTaskSave : APISpec(App &, JsonDocument &)> { + auto operator()(Arg<0>, Arg<1>) -> R; + }; + struct TaskDelete : APISpec(App &, JsonDocument &)> { auto operator()(Arg<0>, Arg<1>) -> R; }; diff --git a/src/Application/App-wifi.cpp b/src/Application/App-wifi.cpp index cfb1307..b7867ff 100644 --- a/src/Application/App-wifi.cpp +++ b/src/Application/App-wifi.cpp @@ -113,6 +113,18 @@ void App::setupServerRouting() { res.end(); }); + // ──────────────────────────────────────────────────────────────────────────────── + // Update existing task with incoming data + // ──────────────────────────────────────────────────────────────────────────────── + server.post("/api/nowTask/save", [this](Request & req, Response & res) { + StaticJsonDocument body; + deserializeJson(body, req.body); + serializeJsonPretty(body, Serial); + + const auto & response = dispatchAPI(body); + res.json(response); + res.end(); + }); // ──────────────────────────────────────────────────────────────────────────────── // Schedule a task (marking it active) // ──────────────────────────────────────────────────────────────────────────────── diff --git a/src/Components/NowSampleButton.cpp b/src/Components/NowSampleButton.cpp deleted file mode 100644 index b3405ac..0000000 --- a/src/Components/NowSampleButton.cpp +++ /dev/null @@ -1,13 +0,0 @@ -#/*include "Components/NowSampleButton.hpp" - -volatile unsigned long buttonInterruptStart = 0; -volatile bool buttonTriggered = false; -volatile int buttonFlag = 0; - -void button_isr() { - if (() { - return; - } - - -}*/ diff --git a/src/Components/NowSampleButton.hpp b/src/Components/NowSampleButton.hpp deleted file mode 100644 index b4c3011..0000000 --- a/src/Components/NowSampleButton.hpp +++ /dev/null @@ -1,103 +0,0 @@ -/** - * Copyright (c) 2020 Kawin Pechetratanapanit - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of - * this software and associated documentation files (the "Software"), to deal in - * the Software without restriction, including without limitation the rights to - * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of - * the Software, and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS - * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR - * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER - * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -/* -#pragma once -#include -#include -#include - -#include - - -// -// ──────────────────────────────────────────────────────────────────────── I ────────── -// :::::: B U T T O N M A N A G E M E N T : : : : : : : : -// ────────────────────────────────────────────────────────────────────────────────── -// -// -// -; -extern void button_isr(); - -class NowSampleButton : public KPComponent { -public: - std::function interruptCallback; - - NowSampleButton(const char * name) : KPComponent(name) {} - - void onInterrupt(std::function callbcak) { - interruptCallback = callbcak; - } - - void setupButton() { - pinMode(HardwarePins::BUTTON_PIN, INPUT); - buttonFlag = 0; - attachInterrupt(digitalPinToInterrupt(HardwarePins::BUTTON_PIN), button_isr, RISING); - print("Button Set Up"); - println(); - } - - void setup() override { - setupButton(); - } - - /** ──────────────────────────────────────────────────────────────────────────── - * Runtime update loop. Check if Button has been triggered. - - * ──────────────────────────────────────────────────────────────────────────── */ - /* void update() override { - if (!buttonTriggered || !interruptCallback) { - return; - } - - // Continue if button has a new press - if ( - - } - } - - - - - - /** ──────────────────────────────────────────────────────────────────────────── - * Disable button - * - * ──────────────────────────────────────────────────────────────────────────── */ - /*void disableSampleButton() { - detachInterrupt(digitalPinToInterrupt(HardwarePins::BUTTON_PIN)); - } - - - - - /** ──────────────────────────────────────────────────────────────────────────── - * Set Button to be able to be pressed again - * ──────────────────────────────────────────────────────────────────────────── */ - /* void () { - buttonFlag = 0; - println("interrupt attached"); - attachInterrupt(digitalPinToInterrupt(HardwarePins::BUTTON_PIN), button_isr, RISING); - } - - - -};*/ diff --git a/src/Components/Power.cpp b/src/Components/Power.cpp index 6ff8945..a2e0538 100644 --- a/src/Components/Power.cpp +++ b/src/Components/Power.cpp @@ -1,7 +1,11 @@ -#include "Components/Power.hpp" +#include + +volatile unsigned long rtcInterruptStart = 0; +volatile bool alarmTriggered = false; +volatile int buttonFlag = 0; +volatile unsigned long buttonInterruptStart = 0; +volatile bool buttonTriggered = 0; -volatile unsigned long rtcInterruptStart = 0; -volatile bool alarmTriggered = false; void rtc_isr() { if (((unsigned long) (millis() - rtcInterruptStart) < 1000) && ((unsigned long) (millis() - buttonInterruptStart) < 1000)){ diff --git a/src/Components/Power.hpp b/src/Components/Power.hpp index e9d047c..a96385a 100644 --- a/src/Components/Power.hpp +++ b/src/Components/Power.hpp @@ -45,11 +45,11 @@ extern volatile bool alarmTriggered; extern volatile unsigned long buttonInterruptStart; extern volatile bool buttonTriggered; extern volatile int buttonFlag; -bool buttonCanTrigger; extern void rtc_isr(); class Power : public KPComponent { public: + bool buttonCanTrigger; DS3232RTC rtc; std::function interruptCallback; std::function buttonCallback; diff --git a/src/StateControllers/MainStateController.cpp b/src/StateControllers/MainStateController.cpp index e0c619f..2ae6c1e 100644 --- a/src/StateControllers/MainStateController.cpp +++ b/src/StateControllers/MainStateController.cpp @@ -21,7 +21,7 @@ void Main::Stop::enter(KPStateMachine & sm) { if(app.ntm.advanceTask()){ println(BLUE("Setting now sample button to be pressed again")); - app.nowSampleButton.setSampleButton(); + app.power.setSampleButton(); } app.ntm.writeToDirectory(); From ecb04097280bd2b77ed38c419bb1e64e5c42ba90 Mon Sep 17 00:00:00 2001 From: Nathan Jesudason Date: Sat, 24 Jul 2021 18:15:01 -0700 Subject: [PATCH 09/61] Changed post for now task save --- src/Application/App-wifi.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Application/App-wifi.cpp b/src/Application/App-wifi.cpp index b7867ff..f9c41f2 100644 --- a/src/Application/App-wifi.cpp +++ b/src/Application/App-wifi.cpp @@ -63,7 +63,8 @@ void App::setupServerRouting() { // ──────────────────────────────────────────────────────────────────────────────── // Get now task object // ──────────────────────────────────────────────────────────────────────────────── - server.get("/api/nowTask", [this](Request &, Response & res) { + server.get("/api/nowtask", [this](Request &, Response & res) { + println(BLUE("REQUESTING NOW TASK")); StaticJsonDocument response; encodeJSON(ntm, response.to()); @@ -116,7 +117,8 @@ void App::setupServerRouting() { // ──────────────────────────────────────────────────────────────────────────────── // Update existing task with incoming data // ──────────────────────────────────────────────────────────────────────────────── - server.post("/api/nowTask/save", [this](Request & req, Response & res) { + server.post("/api/nowtask/save", [this](Request & req, Response & res) { + println(BLUE("SAVING NOW TASK")); StaticJsonDocument body; deserializeJson(body, req.body); serializeJsonPretty(body, Serial); From bfeb5536c58c0eade8224cc94592153e2fc46bca Mon Sep 17 00:00:00 2001 From: Nathan Jesudason Date: Mon, 26 Jul 2021 15:28:03 -0700 Subject: [PATCH 10/61] changed button pin to A4 --- src/Application/Constants.hpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Application/Constants.hpp b/src/Application/Constants.hpp index 99000ca..1569243 100755 --- a/src/Application/Constants.hpp +++ b/src/Application/Constants.hpp @@ -33,7 +33,7 @@ namespace HardwarePins { #endif __k_auto BATTERY_VOLTAGE = A2; __k_auto ANALOG_SENSOR_1 = A3; - __k_auto ANALOG_SENSOR_2 = A4; + __k_auto BUTTON_PIN = A4; //Previously ANALOG_SENSOR_2 __k_auto SHUTDOWN_OVERRIDE = A5; __k_auto MOTOR_REVERSE = 5; __k_auto MOTOR_FORWARD = 6; @@ -41,7 +41,6 @@ namespace HardwarePins { __k_auto SD_CARD = 10; __k_auto SHFT_REG_CLOCK = 11; __k_auto SHFT_REG_DATA = 12; - __k_auto BUTTON_PIN = 13; }; // namespace HardwarePins namespace ProgramSettings { From 08faee74985a2c737c868f942d3dec8a7e3f1454 Mon Sep 17 00:00:00 2001 From: Nathan Jesudason Date: Tue, 27 Jul 2021 17:28:44 -0700 Subject: [PATCH 11/61] Added robust handling of scheduling and sample now tasks --- src/Application/App.hpp | 17 +++++++++++++++-- src/Components/Power.hpp | 4 ++++ src/StateControllers/MainStateController.cpp | 5 ++++- 3 files changed, 23 insertions(+), 3 deletions(-) diff --git a/src/Application/App.hpp b/src/Application/App.hpp index 5116cd4..297e770 100755 --- a/src/Application/App.hpp +++ b/src/Application/App.hpp @@ -227,14 +227,14 @@ class App : public KPController, public KPSerialInputObserver, public TaskObserv power.onInterrupt([this]() { println(GREEN("RTC Interrupted!")); println(scheduleNextActiveTask().description()); + power.disarmSampleNowButton(); }); power.onButtonInterrupt([this](){ - println(RED("Now Sampling!")); - //NowTask task = ntm.task; nowTaskStateController.configure(ntm.task); if(now() - ntm.last_nowTask > nowTaskStateController.get_total_time()){ + println(RED("Now Sampling!")); ntm.last_nowTask = now(); beginNowTask(); } @@ -345,6 +345,19 @@ class App : public KPController, public KPSerialInputObserver, public TaskObserv if(time_now + nowTaskStateController.get_total_time() >= tm.tasks[tm.getActiveSortedTaskIds().front()].schedule){ invalidateTaskAndFreeUpValves(tm.tasks[tm.getActiveSortedTaskIds().front()]); } + if(vm.valves[*(task.valve)].status != ValveStatus::free){ + *(task.valve) = -1; + for(int i = 0; i < vm.valves.size(); i++){ + if(vm.valves[i].status == ValveStatus::free){ + *(task.valve) = i; + break; + } + } + if(*(task.valve) == -1){ + print(RED("No free valves to sample!")); + return; + } + } TimedAction NowTaskExecution; const auto timeUntil = 10; NowTaskExecution.interval = secsToMillis(timeUntil); diff --git a/src/Components/Power.hpp b/src/Components/Power.hpp index a96385a..641f1e1 100644 --- a/src/Components/Power.hpp +++ b/src/Components/Power.hpp @@ -317,4 +317,8 @@ class Power : public KPComponent { buttonFlag = 0; buttonCanTrigger = true; } + + void disarmSampleNowButton(){ + buttonCanTrigger = false; + } }; diff --git a/src/StateControllers/MainStateController.cpp b/src/StateControllers/MainStateController.cpp index 2ae6c1e..0c3c3a6 100644 --- a/src/StateControllers/MainStateController.cpp +++ b/src/StateControllers/MainStateController.cpp @@ -16,7 +16,10 @@ void Main::Stop::enter(KPStateMachine & sm) { app.vm.writeToDirectory(); auto currentTaskId = app.currentTaskId; - app.tm.advanceTask(currentTaskId); + if(app.tm.advanceTask(currentTaskId)){ + println(BLUE("Setting now sample button to be pressed again")); + app.power.setSampleButton(); + }; app.tm.writeToDirectory(); if(app.ntm.advanceTask()){ From d493dceddeb413bfa7627fad2bfd9c9bd3151e2b Mon Sep 17 00:00:00 2001 From: Nathan Jesudason Date: Sat, 31 Jul 2021 18:14:47 -0700 Subject: [PATCH 12/61] Added LED indicator when nowTask is running --- src/Application/App.hpp | 2 ++ src/Components/Power.hpp | 1 + 2 files changed, 3 insertions(+) diff --git a/src/Application/App.hpp b/src/Application/App.hpp index 297e770..5cd6e24 100755 --- a/src/Application/App.hpp +++ b/src/Application/App.hpp @@ -166,6 +166,7 @@ class App : public KPController, public KPSerialInputObserver, public TaskObserv ntm.init(config); ntm.addObserver(this); ntm.loadTasksFromDirectory(config.taskFolder); + pinMode(LED_BUILTIN, OUTPUT); // // ─── HYPER FLUSH CONTROLLER ────────────────────────────────────── // @@ -235,6 +236,7 @@ class App : public KPController, public KPSerialInputObserver, public TaskObserv nowTaskStateController.configure(ntm.task); if(now() - ntm.last_nowTask > nowTaskStateController.get_total_time()){ println(RED("Now Sampling!")); + digitalWrite(LED_BUILTIN, HIGH); ntm.last_nowTask = now(); beginNowTask(); } diff --git a/src/Components/Power.hpp b/src/Components/Power.hpp index 641f1e1..3cc8fdb 100644 --- a/src/Components/Power.hpp +++ b/src/Components/Power.hpp @@ -314,6 +314,7 @@ class Power : public KPComponent { } void setSampleButton() { + digitalWrite(LED_BUILTIN, LOW); buttonFlag = 0; buttonCanTrigger = true; } From eb61bf2a8db67183c65735878bf262e6e194ffea Mon Sep 17 00:00:00 2001 From: Nathan Jesudason Date: Sun, 8 Aug 2021 17:38:34 -0700 Subject: [PATCH 13/61] Changed implementation of sample now button to be independent of rtc --- src/Application/App.hpp | 22 ++-- src/Components/NowSampleButton.cpp | 15 +++ src/Components/NowSampleButton.hpp | 110 +++++++++++++++++++ src/Components/Power.cpp | 20 +--- src/Components/Power.hpp | 34 +----- src/StateControllers/MainStateController.cpp | 4 +- 6 files changed, 147 insertions(+), 58 deletions(-) create mode 100644 src/Components/NowSampleButton.cpp create mode 100644 src/Components/NowSampleButton.hpp diff --git a/src/Application/App.hpp b/src/Application/App.hpp index 5cd6e24..1d33b0b 100755 --- a/src/Application/App.hpp +++ b/src/Application/App.hpp @@ -14,6 +14,7 @@ #include #include #include +#include #include #include @@ -63,7 +64,7 @@ class App : public KPController, public KPSerialInputObserver, public TaskObserv }; Power power{"power"}; - //nowSampleButton nowSampleButton{"nowSampleButton"}; + NowSampleButton nowSampleButton{"nowSampleButton"}; BallIntake intake{shift}; Config config{ProgramSettings::CONFIG_FILE_PATH}; Status status; @@ -228,16 +229,14 @@ class App : public KPController, public KPSerialInputObserver, public TaskObserv power.onInterrupt([this]() { println(GREEN("RTC Interrupted!")); println(scheduleNextActiveTask().description()); - power.disarmSampleNowButton(); }); - power.onButtonInterrupt([this](){ - nowTaskStateController.configure(ntm.task); - if(now() - ntm.last_nowTask > nowTaskStateController.get_total_time()){ + nowSampleButton.onInterrupt([this](){ + //check to make sure task isn't running that disables button + if(buttonFlag != 0){ println(RED("Now Sampling!")); digitalWrite(LED_BUILTIN, HIGH); - ntm.last_nowTask = now(); beginNowTask(); } }); @@ -343,13 +342,13 @@ class App : public KPController, public KPSerialInputObserver, public TaskObserv time_t time_now = now(); NowTask task = ntm.task; status.preventShutdown = false; - + nowTaskStateController.configure(ntm.task); if(time_now + nowTaskStateController.get_total_time() >= tm.tasks[tm.getActiveSortedTaskIds().front()].schedule){ invalidateTaskAndFreeUpValves(tm.tasks[tm.getActiveSortedTaskIds().front()]); } if(vm.valves[*(task.valve)].status != ValveStatus::free){ *(task.valve) = -1; - for(int i = 0; i < vm.valves.size(); i++){ + for(unsigned int i = 0; i < vm.valves.size(); i++){ if(vm.valves[i].status == ValveStatus::free){ *(task.valve) = i; break; @@ -420,10 +419,15 @@ class App : public KPController, public KPSerialInputObserver, public TaskObserv } if (time_now >= task.schedule - 10) { + if(buttonFlag != 0){ + println(RED("Sample Now Task Occuring")); + invalidateTaskAndFreeUpValves(task); + continue; + } // Wake up between 10 secs of the actual schedule time // Prepare an action to execute at exact time const auto timeUntil = task.schedule - time_now; - + nowSampleButton.disableSampleButton(); TimedAction delayTaskExecution; delayTaskExecution.name = "delayTaskExecution"; delayTaskExecution.interval = secsToMillis(timeUntil); diff --git a/src/Components/NowSampleButton.cpp b/src/Components/NowSampleButton.cpp new file mode 100644 index 0000000..e8676d6 --- /dev/null +++ b/src/Components/NowSampleButton.cpp @@ -0,0 +1,15 @@ +#include "Components/NowSampleButton.hpp" + +volatile unsigned long buttonInterruptStart = 0; +volatile bool buttonTriggered = false; +volatile int buttonFlag = 0; + +void button_isr() { + if ((unsigned long) (millis() - buttonInterruptStart) < 200) { + return; + } + + buttonTriggered = true; + buttonInterruptStart = millis(); + buttonFlag = 1; +} \ No newline at end of file diff --git a/src/Components/NowSampleButton.hpp b/src/Components/NowSampleButton.hpp new file mode 100644 index 0000000..f6eb468 --- /dev/null +++ b/src/Components/NowSampleButton.hpp @@ -0,0 +1,110 @@ +/** + * Copyright (c) 2020 Kawin Pechetratanapanit + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#pragma once +#include +#include +#include + +#include + + +// +// ──────────────────────────────────────────────────────────────────────── I ────────── +// :::::: B U T T O N M A N A G E M E N T : : : : : : : : +// ────────────────────────────────────────────────────────────────────────────────── +// +// +// + +extern volatile unsigned long buttonInterruptStart; +extern volatile bool buttonTriggered; +extern volatile int buttonFlag; +extern void button_isr(); + +class NowSampleButton : public KPComponent { +public: + std::function interruptCallback; + + NowSampleButton(const char * name) : KPComponent(name) {} + + void onInterrupt(std::function callbcak) { + interruptCallback = callbcak; + } + + void setupButton() { + pinMode(HardwarePins::BUTTON_PIN, INPUT); + buttonFlag = 0; + attachInterrupt(digitalPinToInterrupt(HardwarePins::BUTTON_PIN), button_isr, FALLING); + print("Button Set Up"); + println(); + } + + void setup() override { + setupButton(); + } + + /** ──────────────────────────────────────────────────────────────────────────── + * Runtime update loop. Check if Button has been triggered. + * ──────────────────────────────────────────────────────────────────────────── */ + void update() override { + if (!buttonTriggered || !interruptCallback) { + return; + } + + // Continue if button has a new press + if (buttonFlag != 0) { + detachInterrupt(digitalPinToInterrupt(HardwarePins::BUTTON_PIN)); + buttonTriggered = false; + interruptCallback(); + + } + } + + + + + + /** ──────────────────────────────────────────────────────────────────────────── + * Disable button + * + * ──────────────────────────────────────────────────────────────────────────── */ + void disableSampleButton() { + buttonFlag = 0; + detachInterrupt(digitalPinToInterrupt(HardwarePins::BUTTON_PIN)); + } + + + + + /** ──────────────────────────────────────────────────────────────────────────── + * Set Button to be able to be pressed again + * ──────────────────────────────────────────────────────────────────────────── */ + void setSampleButton() { + buttonFlag = 0; + println("interrupt attached"); + digitalWrite(LED_BUILTIN, LOW); + attachInterrupt(digitalPinToInterrupt(HardwarePins::BUTTON_PIN), button_isr, FALLING); + } + + + +}; \ No newline at end of file diff --git a/src/Components/Power.cpp b/src/Components/Power.cpp index a2e0538..1d7a283 100644 --- a/src/Components/Power.cpp +++ b/src/Components/Power.cpp @@ -1,23 +1,13 @@ -#include - -volatile unsigned long rtcInterruptStart = 0; -volatile bool alarmTriggered = false; -volatile int buttonFlag = 0; -volatile unsigned long buttonInterruptStart = 0; -volatile bool buttonTriggered = 0; +#include "Components/Power.hpp" +volatile unsigned long rtcInterruptStart = 0; +volatile bool alarmTriggered = false; void rtc_isr() { - if (((unsigned long) (millis() - rtcInterruptStart) < 1000) && ((unsigned long) (millis() - buttonInterruptStart) < 1000)){ + if ((unsigned long) (millis() - rtcInterruptStart) < 1000) { return; } - - if(digitalRead(HardwarePins::BUTTON_PIN) == LOW){ - buttonTriggered = true; - buttonInterruptStart = millis(); - buttonFlag = 1; - } alarmTriggered = true; rtcInterruptStart = millis(); -} +} \ No newline at end of file diff --git a/src/Components/Power.hpp b/src/Components/Power.hpp index 3cc8fdb..9cafa04 100644 --- a/src/Components/Power.hpp +++ b/src/Components/Power.hpp @@ -42,27 +42,18 @@ extern volatile unsigned long rtcInterruptStart; extern volatile bool alarmTriggered; -extern volatile unsigned long buttonInterruptStart; -extern volatile bool buttonTriggered; -extern volatile int buttonFlag; extern void rtc_isr(); class Power : public KPComponent { public: - bool buttonCanTrigger; DS3232RTC rtc; std::function interruptCallback; - std::function buttonCallback; Power(const char * name) : KPComponent(name), rtc(false) {} void onInterrupt(std::function callbcak) { interruptCallback = callbcak; } - - void onButtonInterrupt(std::function callback){ - buttonCallback = callback; - } void setupRTC() { // Initilize RTC I2C Bus @@ -81,7 +72,6 @@ class Power : public KPComponent { print("RTC startup time: "); printCurrentTime(); println(); - } void setup() override { @@ -92,26 +82,16 @@ class Power : public KPComponent { // so we need this internal one. INPUT_PULLUP is required. pinMode(HardwarePins::RTC_INTERRUPT, INPUT_PULLUP); pinMode(HardwarePins::POWER_MODULE, OUTPUT); - - //init button pin - pinMode(HardwarePins::BUTTON_PIN, INPUT); - buttonCanTrigger = true; } /** ──────────────────────────────────────────────────────────────────────────── * Runtime update loop. Check if RTC has been triggered. - * ──────────────────────────────────────────────────────────────────────────── */ void update() override { - if (!alarmTriggered || !buttonTriggered || !interruptCallback) { + if (!alarmTriggered || !interruptCallback) { return; } - if(buttonFlag != 0 && buttonCanTrigger) { - buttonCanTrigger = false; - buttonTriggered = false; - buttonCallback(); - } // Check if the interrupt is comming from RTC // This is important in noisy environment if (rtc.alarm(1) || rtc.alarm(2)) { @@ -312,14 +292,4 @@ class Power : public KPComponent { constexpr time_t FUDGE = 10; return t + FUDGE; } - - void setSampleButton() { - digitalWrite(LED_BUILTIN, LOW); - buttonFlag = 0; - buttonCanTrigger = true; - } - - void disarmSampleNowButton(){ - buttonCanTrigger = false; - } -}; +}; \ No newline at end of file diff --git a/src/StateControllers/MainStateController.cpp b/src/StateControllers/MainStateController.cpp index 0c3c3a6..e93b5cf 100644 --- a/src/StateControllers/MainStateController.cpp +++ b/src/StateControllers/MainStateController.cpp @@ -18,13 +18,13 @@ void Main::Stop::enter(KPStateMachine & sm) { auto currentTaskId = app.currentTaskId; if(app.tm.advanceTask(currentTaskId)){ println(BLUE("Setting now sample button to be pressed again")); - app.power.setSampleButton(); + app.nowSampleButton.setSampleButton(); }; app.tm.writeToDirectory(); if(app.ntm.advanceTask()){ println(BLUE("Setting now sample button to be pressed again")); - app.power.setSampleButton(); + app.nowSampleButton.setSampleButton(); } app.ntm.writeToDirectory(); From 9ed66c32c228371ffe0cbd5db2551c1ce01ff427 Mon Sep 17 00:00:00 2001 From: Nathan Jesudason Date: Sun, 8 Aug 2021 19:50:28 -0700 Subject: [PATCH 14/61] Added debug print statements around button isr --- src/Components/NowSampleButton.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Components/NowSampleButton.cpp b/src/Components/NowSampleButton.cpp index e8676d6..5100f44 100644 --- a/src/Components/NowSampleButton.cpp +++ b/src/Components/NowSampleButton.cpp @@ -5,7 +5,9 @@ volatile bool buttonTriggered = false; volatile int buttonFlag = 0; void button_isr() { - if ((unsigned long) (millis() - buttonInterruptStart) < 200) { + println(BLUE("BUTTON PRESS DETECTED")); + if ((millis() - buttonInterruptStart) < 200) { + print(RED("Debounce detected")); return; } From f8b6c146996ee969c8cea2b20764af7ed6364a9c Mon Sep 17 00:00:00 2001 From: Nathan Jesudason Date: Tue, 10 Aug 2021 10:47:00 -0700 Subject: [PATCH 15/61] Fixed interrupt to now be on digital pin --- src/Application/App.hpp | 2 +- src/Application/Constants.hpp | 3 ++- src/Components/NowSampleButton.hpp | 4 ++-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/Application/App.hpp b/src/Application/App.hpp index 1d33b0b..1c09f06 100755 --- a/src/Application/App.hpp +++ b/src/Application/App.hpp @@ -134,7 +134,7 @@ class App : public KPController, public KPSerialInputObserver, public TaskObserv addComponent(pump); addComponent(sensors); sensors.addObserver(status); - //addComponent(nowSampleButton); + addComponent(nowSampleButton); // // ─── LOADING CONFIG FILE ───────────────────────────────────────── // diff --git a/src/Application/Constants.hpp b/src/Application/Constants.hpp index 1569243..d658172 100755 --- a/src/Application/Constants.hpp +++ b/src/Application/Constants.hpp @@ -32,8 +32,9 @@ namespace HardwarePins { __k_auto RTC_INTERRUPT = 12; // Change this to A1 to force using A1 for RTC #endif __k_auto BATTERY_VOLTAGE = A2; + //A4 is unused but would be ANALOG_SENSOR_2 __k_auto ANALOG_SENSOR_1 = A3; - __k_auto BUTTON_PIN = A4; //Previously ANALOG_SENSOR_2 + __k_auto BUTTON_PIN = 13; __k_auto SHUTDOWN_OVERRIDE = A5; __k_auto MOTOR_REVERSE = 5; __k_auto MOTOR_FORWARD = 6; diff --git a/src/Components/NowSampleButton.hpp b/src/Components/NowSampleButton.hpp index f6eb468..180ba97 100644 --- a/src/Components/NowSampleButton.hpp +++ b/src/Components/NowSampleButton.hpp @@ -54,7 +54,7 @@ class NowSampleButton : public KPComponent { pinMode(HardwarePins::BUTTON_PIN, INPUT); buttonFlag = 0; attachInterrupt(digitalPinToInterrupt(HardwarePins::BUTTON_PIN), button_isr, FALLING); - print("Button Set Up"); + print(RED("Interrupt Button Set Up")); println(); } @@ -89,7 +89,7 @@ class NowSampleButton : public KPComponent { * ──────────────────────────────────────────────────────────────────────────── */ void disableSampleButton() { buttonFlag = 0; - detachInterrupt(digitalPinToInterrupt(HardwarePins::BUTTON_PIN)); + // detachInterrupt(digitalPinToInterrupt(HardwarePins::BUTTON_PIN)); } From 23ed7d4d8283e59b720a985415a464f4b6f31d8e Mon Sep 17 00:00:00 2001 From: Nathan Jesudason Date: Tue, 10 Aug 2021 10:52:56 -0700 Subject: [PATCH 16/61] Undid needless commit --- src/Components/NowSampleButton.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Components/NowSampleButton.hpp b/src/Components/NowSampleButton.hpp index 180ba97..83869a2 100644 --- a/src/Components/NowSampleButton.hpp +++ b/src/Components/NowSampleButton.hpp @@ -89,7 +89,7 @@ class NowSampleButton : public KPComponent { * ──────────────────────────────────────────────────────────────────────────── */ void disableSampleButton() { buttonFlag = 0; - // detachInterrupt(digitalPinToInterrupt(HardwarePins::BUTTON_PIN)); + detachInterrupt(digitalPinToInterrupt(HardwarePins::BUTTON_PIN)); } From 9b17325fb6e12bebe684e0aee25083ac198bd138 Mon Sep 17 00:00:00 2001 From: Nathan Jesudason Date: Tue, 10 Aug 2021 12:11:18 -0700 Subject: [PATCH 17/61] Fixed instant sample now on setup --- src/Application/App.hpp | 7 +++++++ src/Components/NowSampleButton.hpp | 6 +----- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/Application/App.hpp b/src/Application/App.hpp index 1c09f06..3f9777f 100755 --- a/src/Application/App.hpp +++ b/src/Application/App.hpp @@ -135,6 +135,13 @@ class App : public KPController, public KPSerialInputObserver, public TaskObserv addComponent(sensors); sensors.addObserver(status); addComponent(nowSampleButton); + + TimedAction ArmSampleNowButton; + const auto timeUntil = 20; + ArmSampleNowButton.interval = secsToMillis(timeUntil); + ArmSampleNowButton.callback = [this]() { nowSampleButton.setSampleButton(); }; + run(ArmSampleNowButton); // async, will be execute later + // // ─── LOADING CONFIG FILE ───────────────────────────────────────── // diff --git a/src/Components/NowSampleButton.hpp b/src/Components/NowSampleButton.hpp index 83869a2..fe1ec4f 100644 --- a/src/Components/NowSampleButton.hpp +++ b/src/Components/NowSampleButton.hpp @@ -51,11 +51,7 @@ class NowSampleButton : public KPComponent { } void setupButton() { - pinMode(HardwarePins::BUTTON_PIN, INPUT); - buttonFlag = 0; - attachInterrupt(digitalPinToInterrupt(HardwarePins::BUTTON_PIN), button_isr, FALLING); - print(RED("Interrupt Button Set Up")); - println(); + pinMode(HardwarePins::BUTTON_PIN, INPUT_PULLUP); } void setup() override { From 855c11a58390a2160b07c63adabe945870eae98a Mon Sep 17 00:00:00 2001 From: Nathan Jesudason Date: Tue, 10 Aug 2021 14:51:59 -0700 Subject: [PATCH 18/61] removed redundant task scheduling check --- src/Application/App.hpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/Application/App.hpp b/src/Application/App.hpp index 3f9777f..d1157fd 100755 --- a/src/Application/App.hpp +++ b/src/Application/App.hpp @@ -346,13 +346,9 @@ class App : public KPController, public KPSerialInputObserver, public TaskObserv } void beginNowTask(){ - time_t time_now = now(); NowTask task = ntm.task; status.preventShutdown = false; nowTaskStateController.configure(ntm.task); - if(time_now + nowTaskStateController.get_total_time() >= tm.tasks[tm.getActiveSortedTaskIds().front()].schedule){ - invalidateTaskAndFreeUpValves(tm.tasks[tm.getActiveSortedTaskIds().front()]); - } if(vm.valves[*(task.valve)].status != ValveStatus::free){ *(task.valve) = -1; for(unsigned int i = 0; i < vm.valves.size(); i++){ From 33d3d9ce37a59e605b6acf0935dc7627cea75d3d Mon Sep 17 00:00:00 2001 From: Nathan Jesudason Date: Tue, 10 Aug 2021 16:23:54 -0700 Subject: [PATCH 19/61] Fixed button becoming inactive after failed sample --- src/Application/App.hpp | 5 ++++- src/Components/NowSampleButton.hpp | 10 ++++++---- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/Application/App.hpp b/src/Application/App.hpp index d1157fd..c9604a2 100755 --- a/src/Application/App.hpp +++ b/src/Application/App.hpp @@ -359,9 +359,11 @@ class App : public KPController, public KPSerialInputObserver, public TaskObserv } if(*(task.valve) == -1){ print(RED("No free valves to sample!")); + nowSampleButton.setSampleButton(); return; } } + nowSampleButton.disableSampleButton(); TimedAction NowTaskExecution; const auto timeUntil = 10; NowTaskExecution.interval = secsToMillis(timeUntil); @@ -427,10 +429,11 @@ class App : public KPController, public KPSerialInputObserver, public TaskObserv invalidateTaskAndFreeUpValves(task); continue; } + + nowSampleButton.disableSampleButton(); // Wake up between 10 secs of the actual schedule time // Prepare an action to execute at exact time const auto timeUntil = task.schedule - time_now; - nowSampleButton.disableSampleButton(); TimedAction delayTaskExecution; delayTaskExecution.name = "delayTaskExecution"; delayTaskExecution.interval = secsToMillis(timeUntil); diff --git a/src/Components/NowSampleButton.hpp b/src/Components/NowSampleButton.hpp index fe1ec4f..3b8fbaf 100644 --- a/src/Components/NowSampleButton.hpp +++ b/src/Components/NowSampleButton.hpp @@ -43,6 +43,7 @@ extern void button_isr(); class NowSampleButton : public KPComponent { public: std::function interruptCallback; + bool initButtonPress; NowSampleButton(const char * name) : KPComponent(name) {} @@ -67,11 +68,10 @@ class NowSampleButton : public KPComponent { } // Continue if button has a new press - if (buttonFlag != 0) { - detachInterrupt(digitalPinToInterrupt(HardwarePins::BUTTON_PIN)); + if (buttonFlag != 0 && initButtonPress) { buttonTriggered = false; + initButtonPress = false; interruptCallback(); - } } @@ -84,7 +84,8 @@ class NowSampleButton : public KPComponent { * * ──────────────────────────────────────────────────────────────────────────── */ void disableSampleButton() { - buttonFlag = 0; + buttonTriggered = false; + initButtonPress = false; detachInterrupt(digitalPinToInterrupt(HardwarePins::BUTTON_PIN)); } @@ -96,6 +97,7 @@ class NowSampleButton : public KPComponent { * ──────────────────────────────────────────────────────────────────────────── */ void setSampleButton() { buttonFlag = 0; + initButtonPress = true; println("interrupt attached"); digitalWrite(LED_BUILTIN, LOW); attachInterrupt(digitalPinToInterrupt(HardwarePins::BUTTON_PIN), button_isr, FALLING); From ac9b85f795c4e52b2780a3fa1a83edc8cf12e39a Mon Sep 17 00:00:00 2001 From: Nathan Jesudason Date: Thu, 12 Aug 2021 12:46:27 -0700 Subject: [PATCH 20/61] Fixed interrupt dettaching to unknown behavior. Changed timings --- src/Application/App.hpp | 13 ++++++++----- src/Components/NowSampleButton.cpp | 4 +--- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/Application/App.hpp b/src/Application/App.hpp index c9604a2..ac87ec9 100755 --- a/src/Application/App.hpp +++ b/src/Application/App.hpp @@ -137,7 +137,7 @@ class App : public KPController, public KPSerialInputObserver, public TaskObserv addComponent(nowSampleButton); TimedAction ArmSampleNowButton; - const auto timeUntil = 20; + const auto timeUntil = 5; ArmSampleNowButton.interval = secsToMillis(timeUntil); ArmSampleNowButton.callback = [this]() { nowSampleButton.setSampleButton(); }; run(ArmSampleNowButton); // async, will be execute later @@ -242,7 +242,7 @@ class App : public KPController, public KPSerialInputObserver, public TaskObserv nowSampleButton.onInterrupt([this](){ //check to make sure task isn't running that disables button if(buttonFlag != 0){ - println(RED("Now Sampling!")); + println(GREEN("Sample Now Button Interrupted!")); digitalWrite(LED_BUILTIN, HIGH); beginNowTask(); } @@ -363,7 +363,7 @@ class App : public KPController, public KPSerialInputObserver, public TaskObserv return; } } - nowSampleButton.disableSampleButton(); + TimedAction NowTaskExecution; const auto timeUntil = 10; NowTaskExecution.interval = secsToMillis(timeUntil); @@ -376,8 +376,11 @@ class App : public KPController, public KPSerialInputObserver, public TaskObserv vm.setValveStatus(*(task.valve), ValveStatus::operating); println("\033[32;1mExecuting task in ", timeUntil, " seconds\033[0m"); - - + TimedAction DisableButton; + const auto delay = 0.5; + DisableButton.interval = secsToMillis(delay); + DisableButton.callback = [this]() {nowSampleButton.disableSampleButton(); }; + run(DisableButton); } ValveBlock currentValveNumberToBlock() { diff --git a/src/Components/NowSampleButton.cpp b/src/Components/NowSampleButton.cpp index 5100f44..b8aebc8 100644 --- a/src/Components/NowSampleButton.cpp +++ b/src/Components/NowSampleButton.cpp @@ -5,9 +5,7 @@ volatile bool buttonTriggered = false; volatile int buttonFlag = 0; void button_isr() { - println(BLUE("BUTTON PRESS DETECTED")); - if ((millis() - buttonInterruptStart) < 200) { - print(RED("Debounce detected")); + if ((millis() - buttonInterruptStart) < 1000) { return; } From c97ca4531a6795837accabfac2316325c9671fd5 Mon Sep 17 00:00:00 2001 From: Nathan Jesudason Date: Fri, 20 Aug 2021 16:14:15 -0700 Subject: [PATCH 21/61] refactored sample now variables, added print statements --- src/Application/App.hpp | 18 ++++++++++-------- src/Components/NowSampleButton.cpp | 6 +++--- src/Components/NowSampleButton.hpp | 12 ++---------- src/StateControllers/MainStateController.cpp | 1 + 4 files changed, 16 insertions(+), 21 deletions(-) diff --git a/src/Application/App.hpp b/src/Application/App.hpp index ac87ec9..fbab2c1 100755 --- a/src/Application/App.hpp +++ b/src/Application/App.hpp @@ -81,6 +81,7 @@ class App : public KPController, public KPSerialInputObserver, public TaskObserv SensorArray sensors{"sensor-array"}; int currentTaskId = 0; + bool sampleNowActive = false; private: const char * KPSerialInputObserverName() const override { @@ -241,11 +242,9 @@ class App : public KPController, public KPSerialInputObserver, public TaskObserv nowSampleButton.onInterrupt([this](){ //check to make sure task isn't running that disables button - if(buttonFlag != 0){ - println(GREEN("Sample Now Button Interrupted!")); - digitalWrite(LED_BUILTIN, HIGH); - beginNowTask(); - } + println(GREEN("Sample Now Button Interrupted!")); + digitalWrite(LED_BUILTIN, HIGH); + beginNowTask(); }); runForever(1000, "detailLog", [&]() { logDetail("detail.csv"); }); #ifdef DEBUG @@ -347,11 +346,13 @@ class App : public KPController, public KPSerialInputObserver, public TaskObserv void beginNowTask(){ NowTask task = ntm.task; - status.preventShutdown = false; + println("retrieved task"); nowTaskStateController.configure(ntm.task); + println("task configured"); if(vm.valves[*(task.valve)].status != ValveStatus::free){ *(task.valve) = -1; for(unsigned int i = 0; i < vm.valves.size(); i++){ + println("Test"); if(vm.valves[i].status == ValveStatus::free){ *(task.valve) = i; break; @@ -372,12 +373,13 @@ class App : public KPController, public KPSerialInputObserver, public TaskObserv //currentTaskId = id; + sampleNowActive = true; status.preventShutdown = true; vm.setValveStatus(*(task.valve), ValveStatus::operating); println("\033[32;1mExecuting task in ", timeUntil, " seconds\033[0m"); TimedAction DisableButton; - const auto delay = 0.5; + const auto delay = 0.25; DisableButton.interval = secsToMillis(delay); DisableButton.callback = [this]() {nowSampleButton.disableSampleButton(); }; run(DisableButton); @@ -427,7 +429,7 @@ class App : public KPController, public KPSerialInputObserver, public TaskObserv } if (time_now >= task.schedule - 10) { - if(buttonFlag != 0){ + if(sampleNowActive){ println(RED("Sample Now Task Occuring")); invalidateTaskAndFreeUpValves(task); continue; diff --git a/src/Components/NowSampleButton.cpp b/src/Components/NowSampleButton.cpp index b8aebc8..7afb724 100644 --- a/src/Components/NowSampleButton.cpp +++ b/src/Components/NowSampleButton.cpp @@ -2,14 +2,14 @@ volatile unsigned long buttonInterruptStart = 0; volatile bool buttonTriggered = false; -volatile int buttonFlag = 0; void button_isr() { - if ((millis() - buttonInterruptStart) < 1000) { + println("Button interrupt"); + if ((unsigned long) (millis() - buttonInterruptStart) < 1000) { + println("Debounce detected!"); return; } buttonTriggered = true; buttonInterruptStart = millis(); - buttonFlag = 1; } \ No newline at end of file diff --git a/src/Components/NowSampleButton.hpp b/src/Components/NowSampleButton.hpp index 3b8fbaf..8825ad1 100644 --- a/src/Components/NowSampleButton.hpp +++ b/src/Components/NowSampleButton.hpp @@ -37,13 +37,11 @@ extern volatile unsigned long buttonInterruptStart; extern volatile bool buttonTriggered; -extern volatile int buttonFlag; extern void button_isr(); class NowSampleButton : public KPComponent { public: std::function interruptCallback; - bool initButtonPress; NowSampleButton(const char * name) : KPComponent(name) {} @@ -68,11 +66,8 @@ class NowSampleButton : public KPComponent { } // Continue if button has a new press - if (buttonFlag != 0 && initButtonPress) { - buttonTriggered = false; - initButtonPress = false; - interruptCallback(); - } + buttonTriggered = false; + interruptCallback(); } @@ -85,7 +80,6 @@ class NowSampleButton : public KPComponent { * ──────────────────────────────────────────────────────────────────────────── */ void disableSampleButton() { buttonTriggered = false; - initButtonPress = false; detachInterrupt(digitalPinToInterrupt(HardwarePins::BUTTON_PIN)); } @@ -96,8 +90,6 @@ class NowSampleButton : public KPComponent { * Set Button to be able to be pressed again * ──────────────────────────────────────────────────────────────────────────── */ void setSampleButton() { - buttonFlag = 0; - initButtonPress = true; println("interrupt attached"); digitalWrite(LED_BUILTIN, LOW); attachInterrupt(digitalPinToInterrupt(HardwarePins::BUTTON_PIN), button_isr, FALLING); diff --git a/src/StateControllers/MainStateController.cpp b/src/StateControllers/MainStateController.cpp index e93b5cf..54c27ee 100644 --- a/src/StateControllers/MainStateController.cpp +++ b/src/StateControllers/MainStateController.cpp @@ -29,6 +29,7 @@ void Main::Stop::enter(KPStateMachine & sm) { app.ntm.writeToDirectory(); app.currentTaskId = 0; + app.sampleNowActive = false; app.status.currentValve = -1; sm.next(); } From 8fe7a3c0f3a48b498d0e3c58b7bbab057c3024e0 Mon Sep 17 00:00:00 2001 From: Nathan Jesudason Date: Fri, 20 Aug 2021 18:03:55 -0700 Subject: [PATCH 22/61] moved button pin to A4, changed button set up to be sync at end of start --- src/Application/App.hpp | 9 +++------ src/Application/Constants.hpp | 2 +- src/Components/NowSampleButton.hpp | 2 +- 3 files changed, 5 insertions(+), 8 deletions(-) diff --git a/src/Application/App.hpp b/src/Application/App.hpp index fbab2c1..9e3d69e 100755 --- a/src/Application/App.hpp +++ b/src/Application/App.hpp @@ -137,11 +137,7 @@ class App : public KPController, public KPSerialInputObserver, public TaskObserv sensors.addObserver(status); addComponent(nowSampleButton); - TimedAction ArmSampleNowButton; - const auto timeUntil = 5; - ArmSampleNowButton.interval = secsToMillis(timeUntil); - ArmSampleNowButton.callback = [this]() { nowSampleButton.setSampleButton(); }; - run(ArmSampleNowButton); // async, will be execute later + // // ─── LOADING CONFIG FILE ───────────────────────────────────────── @@ -243,13 +239,14 @@ class App : public KPController, public KPSerialInputObserver, public TaskObserv nowSampleButton.onInterrupt([this](){ //check to make sure task isn't running that disables button println(GREEN("Sample Now Button Interrupted!")); - digitalWrite(LED_BUILTIN, HIGH); + //digitalWrite(LED_BUILTIN, HIGH); beginNowTask(); }); runForever(1000, "detailLog", [&]() { logDetail("detail.csv"); }); #ifdef DEBUG runForever(2000, "memLog", [&]() { printFreeRam(); }); #endif + nowSampleButton.setSampleButton(); } void logDetail(const char * filename) { diff --git a/src/Application/Constants.hpp b/src/Application/Constants.hpp index d658172..c36eb56 100755 --- a/src/Application/Constants.hpp +++ b/src/Application/Constants.hpp @@ -34,7 +34,7 @@ namespace HardwarePins { __k_auto BATTERY_VOLTAGE = A2; //A4 is unused but would be ANALOG_SENSOR_2 __k_auto ANALOG_SENSOR_1 = A3; - __k_auto BUTTON_PIN = 13; + __k_auto BUTTON_PIN = A4; //originally 13, but that's built-in led and may cause conflicts __k_auto SHUTDOWN_OVERRIDE = A5; __k_auto MOTOR_REVERSE = 5; __k_auto MOTOR_FORWARD = 6; diff --git a/src/Components/NowSampleButton.hpp b/src/Components/NowSampleButton.hpp index 8825ad1..8f23af1 100644 --- a/src/Components/NowSampleButton.hpp +++ b/src/Components/NowSampleButton.hpp @@ -91,7 +91,7 @@ class NowSampleButton : public KPComponent { * ──────────────────────────────────────────────────────────────────────────── */ void setSampleButton() { println("interrupt attached"); - digitalWrite(LED_BUILTIN, LOW); + //digitalWrite(LED_BUILTIN, LOW); attachInterrupt(digitalPinToInterrupt(HardwarePins::BUTTON_PIN), button_isr, FALLING); } From bb9a22edb3728eeaf9c06f706191feaf7229dd71 Mon Sep 17 00:00:00 2001 From: Nathan Jesudason Date: Sat, 21 Aug 2021 16:56:37 -0700 Subject: [PATCH 23/61] fixed sample now button resetting on task completion --- src/Application/App.hpp | 10 ++++++++++ src/StateControllers/MainStateController.cpp | 11 ++--------- src/Task/NowTaskManager.hpp | 2 +- src/Task/NowTaskObserver.hpp | 3 ++- src/Task/TaskManager.hpp | 1 + src/Task/TaskObserver.hpp | 1 + 6 files changed, 17 insertions(+), 11 deletions(-) diff --git a/src/Application/App.hpp b/src/Application/App.hpp index 9e3d69e..39bbd9c 100755 --- a/src/Application/App.hpp +++ b/src/Application/App.hpp @@ -554,4 +554,14 @@ class App : public KPController, public KPSerialInputObserver, public TaskObserv currentTaskId = 0; } } + + void taskDidComplete() override { + println(BLUE("Setting now sample button to be pressed again")); + nowSampleButton.setSampleButton(); + } + + void nowTaskDidComplete(const NowTask & task) override { + println(BLUE("Setting now sample button to be pressed again")); + nowSampleButton.setSampleButton(); + } }; diff --git a/src/StateControllers/MainStateController.cpp b/src/StateControllers/MainStateController.cpp index 54c27ee..9bd186b 100644 --- a/src/StateControllers/MainStateController.cpp +++ b/src/StateControllers/MainStateController.cpp @@ -16,18 +16,11 @@ void Main::Stop::enter(KPStateMachine & sm) { app.vm.writeToDirectory(); auto currentTaskId = app.currentTaskId; - if(app.tm.advanceTask(currentTaskId)){ - println(BLUE("Setting now sample button to be pressed again")); - app.nowSampleButton.setSampleButton(); - }; + app.tm.advanceTask(currentTaskId); app.tm.writeToDirectory(); - if(app.ntm.advanceTask()){ - println(BLUE("Setting now sample button to be pressed again")); - app.nowSampleButton.setSampleButton(); - } + app.ntm.advanceTask(); app.ntm.writeToDirectory(); - app.currentTaskId = 0; app.sampleNowActive = false; app.status.currentValve = -1; diff --git a/src/Task/NowTaskManager.hpp b/src/Task/NowTaskManager.hpp index fa90416..932e193 100644 --- a/src/Task/NowTaskManager.hpp +++ b/src/Task/NowTaskManager.hpp @@ -76,7 +76,7 @@ class NowTaskManager : public KPComponent, bool markTaskAsCompleted() { task.status = TaskStatus::completed; - updateObservers(&NowTaskObserver::nowTaskDidUpdate, task); + updateObservers(&NowTaskObserver::nowTaskDidComplete, task); return task.isCompleted(); } diff --git a/src/Task/NowTaskObserver.hpp b/src/Task/NowTaskObserver.hpp index 65e69d6..32f65b6 100644 --- a/src/Task/NowTaskObserver.hpp +++ b/src/Task/NowTaskObserver.hpp @@ -13,6 +13,7 @@ class NowTaskObserver : public KPObserver { return " Now Task Observer"; } - virtual void nowTaskDidUpdate(const NowTask & task) = 0; + virtual void nowTaskDidUpdate(const NowTask & task) = 0; + virtual void nowTaskDidComplete(const NowTask & task) = 0; //virtual void taskDidDelete(int id) = 0; }; \ No newline at end of file diff --git a/src/Task/TaskManager.hpp b/src/Task/TaskManager.hpp index 7152861..8d3f1f3 100755 --- a/src/Task/TaskManager.hpp +++ b/src/Task/TaskManager.hpp @@ -81,6 +81,7 @@ class TaskManager : public KPComponent, return false; } + updateObservers(&TaskObserver::taskDidComplete); auto & task = tasks[id]; task.valves.clear(); if (task.deleteOnCompletion) { diff --git a/src/Task/TaskObserver.hpp b/src/Task/TaskObserver.hpp index b4a7ec6..907349f 100644 --- a/src/Task/TaskObserver.hpp +++ b/src/Task/TaskObserver.hpp @@ -15,4 +15,5 @@ class TaskObserver : public KPObserver { virtual void taskDidUpdate(const Task & task) = 0; virtual void taskDidDelete(int id) = 0; + virtual void taskDidComplete() = 0; }; \ No newline at end of file From 8ba3071feec2d23311f4e0b7e6b1405536a8804c Mon Sep 17 00:00:00 2001 From: Nathan Jesudason Date: Tue, 24 Aug 2021 19:02:21 -0700 Subject: [PATCH 24/61] fixed Sample now and scheduled task integration --- src/Application/App-wifi.cpp | 2 +- src/Application/App.hpp | 181 ++++++++++++------ src/Application/Constants.hpp | 2 +- src/Components/NowSampleButton.cpp | 4 +- src/Components/NowSampleButton.hpp | 6 +- src/StateControllers/MainStateController.cpp | 4 +- .../NowTaskStateController.cpp | 4 - .../NowTaskStateController.hpp | 14 +- src/Task/NowTask.hpp | 47 ++--- src/Task/NowTaskManager.hpp | 1 + 10 files changed, 158 insertions(+), 107 deletions(-) diff --git a/src/Application/App-wifi.cpp b/src/Application/App-wifi.cpp index f9c41f2..abd8175 100644 --- a/src/Application/App-wifi.cpp +++ b/src/Application/App-wifi.cpp @@ -187,7 +187,7 @@ void App::setupServerRouting() { vm.setValveStatus(i, ValveStatus::Code(config.valves[i])); } vm.writeToDirectory(); - *(ntm.task.valve) = 0; + ntm.task.valve = 0; res.end(); }); } \ No newline at end of file diff --git a/src/Application/App.hpp b/src/Application/App.hpp index 39bbd9c..c7be046 100755 --- a/src/Application/App.hpp +++ b/src/Application/App.hpp @@ -171,7 +171,7 @@ class App : public KPController, public KPSerialInputObserver, public TaskObserv ntm.init(config); ntm.addObserver(this); ntm.loadTasksFromDirectory(config.taskFolder); - pinMode(LED_BUILTIN, OUTPUT); + //pinMode(LED_BUILTIN, OUTPUT); // // ─── HYPER FLUSH CONTROLLER ────────────────────────────────────── // @@ -240,7 +240,7 @@ class App : public KPController, public KPSerialInputObserver, public TaskObserv //check to make sure task isn't running that disables button println(GREEN("Sample Now Button Interrupted!")); //digitalWrite(LED_BUILTIN, HIGH); - beginNowTask(); + println(beginNowTask().description()); }); runForever(1000, "detailLog", [&]() { logDetail("detail.csv"); }); #ifdef DEBUG @@ -253,14 +253,49 @@ class App : public KPController, public KPSerialInputObserver, public TaskObserv if (currentTaskId) { SD.begin(HardwarePins::SD_CARD); File log = SD.open(filename, FILE_WRITE); - Task & task = tm.tasks.at(currentTaskId); - + char formattedTime[64]; auto utc = now(); sprintf( formattedTime, "%u/%u/%u %02u:%02u:%02u GMT+0", year(utc), month(utc), day(utc), hour(utc), minute(utc), second(utc)); + Task & task = tm.tasks.at(currentTaskId); + KPStringBuilder<512> data{ + utc, + ",", + formattedTime, + ",", + task.name, + ",", + status.currentValve, + ",", + status.currentStateName, + ",", + task.sampleTime, + ",", + task.samplePressure, + ",", + task.sampleVolume, + ",", + status.temperature, + ",", + status.pressure, + ",", + status.waterVolume}; + log.println(data); + log.flush(); + log.close(); + } else if (sampleNowActive) { + SD.begin(HardwarePins::SD_CARD); + File log = SD.open(filename, FILE_WRITE); + + char formattedTime[64]; + auto utc = now(); + sprintf( + formattedTime, "%u/%u/%u %02u:%02u:%02u GMT+0", year(utc), month(utc), day(utc), + hour(utc), minute(utc), second(utc)); + NowTask & task = ntm.task; KPStringBuilder<512> data{ utc, ",", @@ -290,41 +325,79 @@ class App : public KPController, public KPSerialInputObserver, public TaskObserv } void logAfterSample() { - SD.begin(HardwarePins::SD_CARD); - File log = SD.open(config.logFile, FILE_WRITE); - Task & task = tm.tasks.at(currentTaskId); - - char formattedTime[64]; - auto utc = now(); - sprintf( - formattedTime, "%u/%u/%u %02u:%02u:%02u GMT+0", year(utc), month(utc), day(utc), - hour(utc), minute(utc), second(utc)); - - KPStringBuilder<512> data{ - utc, - ",", - formattedTime, - ",", - task.name, - ",", - status.currentValve, - ",", - status.currentStateName, - ",", - task.sampleTime, - ",", - task.samplePressure, - ",", - task.sampleVolume, - ",", - status.temperature, - ",", - status.maxPressure, - ",", - status.waterVolume}; - log.println(data); - log.flush(); - log.close(); + if(currentTaskId){ + SD.begin(HardwarePins::SD_CARD); + File log = SD.open(config.logFile, FILE_WRITE); + + Task & task = tm.tasks.at(currentTaskId); + + char formattedTime[64]; + auto utc = now(); + sprintf( + formattedTime, "%u/%u/%u %02u:%02u:%02u GMT+0", year(utc), month(utc), day(utc), + hour(utc), minute(utc), second(utc)); + + KPStringBuilder<512> data{ + utc, + ",", + formattedTime, + ",", + task.name, + ",", + status.currentValve, + ",", + status.currentStateName, + ",", + task.sampleTime, + ",", + task.samplePressure, + ",", + task.sampleVolume, + ",", + status.temperature, + ",", + status.maxPressure, + ",", + status.waterVolume}; + log.println(data); + log.flush(); + log.close(); + } else if (sampleNowActive){ + SD.begin(HardwarePins::SD_CARD); + File log = SD.open(config.logFile, FILE_WRITE); + NowTask & task = ntm.task; + char formattedTime[64]; + auto utc = now(); + sprintf( + formattedTime, "%u/%u/%u %02u:%02u:%02u GMT+0", year(utc), month(utc), day(utc), + hour(utc), minute(utc), second(utc)); + + KPStringBuilder<512> data{ + utc, + ",", + formattedTime, + ",", + task.name, + ",", + status.currentValve, + ",", + status.currentStateName, + ",", + task.sampleTime, + ",", + task.samplePressure, + ",", + task.sampleVolume, + ",", + status.temperature, + ",", + status.maxPressure, + ",", + status.waterVolume}; + log.println(data); + log.flush(); + log.close(); + } } template @@ -341,45 +414,41 @@ class App : public KPController, public KPSerialInputObserver, public TaskObserv hyperFlushStateController.begin(); } - void beginNowTask(){ + ScheduleReturnCode beginNowTask(){ NowTask task = ntm.task; - println("retrieved task"); - nowTaskStateController.configure(ntm.task); - println("task configured"); - if(vm.valves[*(task.valve)].status != ValveStatus::free){ - *(task.valve) = -1; + status.preventShutdown = false; + if(task.valve < 0 || task.valve > config.numberOfValves || vm.valves[task.valve].status != ValveStatus::free){ + task.valve = -1; + println(GREEN("Current sample now valve not free")); for(unsigned int i = 0; i < vm.valves.size(); i++){ - println("Test"); if(vm.valves[i].status == ValveStatus::free){ - *(task.valve) = i; + task.valve = i; + println("Current valve is ", i); break; } } - if(*(task.valve) == -1){ + if(task.valve == -1){ print(RED("No free valves to sample!")); nowSampleButton.setSampleButton(); - return; + return ScheduleReturnCode::unavailable; } } TimedAction NowTaskExecution; const auto timeUntil = 10; NowTaskExecution.interval = secsToMillis(timeUntil); + NowTaskExecution.name = "NowTaskExecution"; NowTaskExecution.callback = [this]() { nowTaskStateController.begin(); }; run(NowTaskExecution); // async, will be execute later + nowTaskStateController.configure(task); - //currentTaskId = id; sampleNowActive = true; status.preventShutdown = true; - vm.setValveStatus(*(task.valve), ValveStatus::operating); + vm.setValveStatus(task.valve, ValveStatus::operating); println("\033[32;1mExecuting task in ", timeUntil, " seconds\033[0m"); - TimedAction DisableButton; - const auto delay = 0.25; - DisableButton.interval = secsToMillis(delay); - DisableButton.callback = [this]() {nowSampleButton.disableSampleButton(); }; - run(DisableButton); + return ScheduleReturnCode::scheduled; } ValveBlock currentValveNumberToBlock() { diff --git a/src/Application/Constants.hpp b/src/Application/Constants.hpp index c36eb56..327d582 100755 --- a/src/Application/Constants.hpp +++ b/src/Application/Constants.hpp @@ -34,7 +34,7 @@ namespace HardwarePins { __k_auto BATTERY_VOLTAGE = A2; //A4 is unused but would be ANALOG_SENSOR_2 __k_auto ANALOG_SENSOR_1 = A3; - __k_auto BUTTON_PIN = A4; //originally 13, but that's built-in led and may cause conflicts + __k_auto BUTTON_PIN = 13; //originally 13, but that's built-in led and may cause conflicts __k_auto SHUTDOWN_OVERRIDE = A5; __k_auto MOTOR_REVERSE = 5; __k_auto MOTOR_FORWARD = 6; diff --git a/src/Components/NowSampleButton.cpp b/src/Components/NowSampleButton.cpp index 7afb724..66cd9a5 100644 --- a/src/Components/NowSampleButton.cpp +++ b/src/Components/NowSampleButton.cpp @@ -4,9 +4,7 @@ volatile unsigned long buttonInterruptStart = 0; volatile bool buttonTriggered = false; void button_isr() { - println("Button interrupt"); - if ((unsigned long) (millis() - buttonInterruptStart) < 1000) { - println("Debounce detected!"); + if ((unsigned long) (millis() - buttonInterruptStart) < 3000) { return; } diff --git a/src/Components/NowSampleButton.hpp b/src/Components/NowSampleButton.hpp index 8f23af1..4d4f865 100644 --- a/src/Components/NowSampleButton.hpp +++ b/src/Components/NowSampleButton.hpp @@ -21,7 +21,6 @@ #pragma once #include -#include #include #include @@ -66,6 +65,7 @@ class NowSampleButton : public KPComponent { } // Continue if button has a new press + disableSampleButton(); buttonTriggered = false; interruptCallback(); } @@ -79,7 +79,7 @@ class NowSampleButton : public KPComponent { * * ──────────────────────────────────────────────────────────────────────────── */ void disableSampleButton() { - buttonTriggered = false; + println("Disabled sample now button"); detachInterrupt(digitalPinToInterrupt(HardwarePins::BUTTON_PIN)); } @@ -91,7 +91,7 @@ class NowSampleButton : public KPComponent { * ──────────────────────────────────────────────────────────────────────────── */ void setSampleButton() { println("interrupt attached"); - //digitalWrite(LED_BUILTIN, LOW); + buttonInterruptStart = millis(); attachInterrupt(digitalPinToInterrupt(HardwarePins::BUTTON_PIN), button_isr, FALLING); } diff --git a/src/StateControllers/MainStateController.cpp b/src/StateControllers/MainStateController.cpp index 9bd186b..1c02fe8 100644 --- a/src/StateControllers/MainStateController.cpp +++ b/src/StateControllers/MainStateController.cpp @@ -16,7 +16,9 @@ void Main::Stop::enter(KPStateMachine & sm) { app.vm.writeToDirectory(); auto currentTaskId = app.currentTaskId; - app.tm.advanceTask(currentTaskId); + if(currentTaskId){ + app.tm.advanceTask(currentTaskId); + } app.tm.writeToDirectory(); app.ntm.advanceTask(); diff --git a/src/StateControllers/NowTaskStateController.cpp b/src/StateControllers/NowTaskStateController.cpp index 24c6e9f..0fb48b5 100644 --- a/src/StateControllers/NowTaskStateController.cpp +++ b/src/StateControllers/NowTaskStateController.cpp @@ -15,9 +15,6 @@ void NowTaskStateController::setup() { registerState(SharedStates::OffshootClean(5), OFFSHOOT_CLEAN_1, FLUSH_2); registerState(SharedStates::Flush(), FLUSH_2, SAMPLE); registerState(SharedStates::Sample(), SAMPLE, [this](int code) { - #if DEBUG - return transitionTo(OFFSHOOT_CLEAN_2); - #else auto & app = *static_cast(controller); app.sensors.flow.stopMeasurement(); app.logAfterSample(); @@ -28,7 +25,6 @@ void NowTaskStateController::setup() { default: halt(TRACE, "Unhandled state transition: ", code); } - #endif }); registerState(SharedStates::OffshootClean(10), OFFSHOOT_CLEAN_2, DRY); registerState(SharedStates::Dry(), DRY, PRESERVE); diff --git a/src/StateControllers/NowTaskStateController.hpp b/src/StateControllers/NowTaskStateController.hpp index fb48667..3e506f8 100644 --- a/src/StateControllers/NowTaskStateController.hpp +++ b/src/StateControllers/NowTaskStateController.hpp @@ -3,7 +3,7 @@ #include #include -namespace NowT{ +namespace NowT { STATE(IDLE); STATE(FLUSH_1); STATE(OFFSHOOT_CLEAN_1); @@ -22,15 +22,13 @@ namespace NowT{ decltype(SharedStates::Sample::volume) sampleVolume; decltype(SharedStates::Dry::time) dryTime; decltype(SharedStates::Preserve::time) preserveTime; - decltype(SharedStates::OffshootPreload::preloadTime) preloadTime; }; - class Controller : public StateControllerWithConfig { + class Controller : public StateController, public StateControllerConfig { public: - Controller() : StateControllerWithConfig("nowtask-state-controller") {} + Controller() : StateController("nowtask-state-controller") {} void setup(); - void configureStates() { decltype(auto) flush1 = getState(FLUSH_1); flush1.time = config.flushTime; @@ -63,10 +61,10 @@ namespace NowT{ transitionTo(IDLE); } - unsigned long get_total_time() { - return config.flushTime + config.flushTime + config.sampleTime + config.dryTime + config.preserveTime; + bool isStop() { + return getCurrentState()->getName() == STOP; } }; -} // namespace HyperFlush +}; // namespace HyperFlush using NowTaskStateController = NowT::Controller; diff --git a/src/Task/NowTask.hpp b/src/Task/NowTask.hpp index c6bf1f8..57983ac 100644 --- a/src/Task/NowTask.hpp +++ b/src/Task/NowTask.hpp @@ -18,7 +18,7 @@ struct NowTask : public JsonEncodable, public: friend class NowTaskManager; - int id; + int id = 0; char name[TaskSettings::NAME_LENGTH]{0}; char notes[TaskSettings::NOTES_LENGTH]{0}; @@ -37,31 +37,21 @@ struct NowTask : public JsonEncodable, int preserveTime = 0; bool deleteOnCompletion = false; - int* valve; + int valve = 0; // std::vector valves; public: int valveOffsetStart = 0; public: - NowTask() { - valve = (int*) malloc(sizeof(int)); - *valve = 0; - } - NowTask(const NowTask & other) { - valve = (int*) malloc(sizeof(int)); - *valve = *other.valve; - }; + NowTask() = default; + NowTask(const NowTask & other) = default; NowTask & operator=(const NowTask &) = default; explicit NowTask(const JsonObject & data) { decodeJSON(data); } - ~NowTask() { - free(valve); - } - int getValveOffsetStart() const { return valveOffsetStart; } @@ -72,22 +62,9 @@ struct NowTask : public JsonEncodable, } bool isCompleted() const { - if((*valve < ProgramSettings::MAX_VALVES) && (status == TaskStatus::completed)){ - *valve += 1; - } return status == TaskStatus::completed; } - /** ──────────────────────────────────────────────────────────────────────────── - * @brief Get the Current Valve ID - * - * @return int -1 if no more valve, otherwise returns the valve number - * ──────────────────────────────────────────────────────────────────────────── */ - int getCurrentValveId() const { - return *valve; - //return (getValveOffsetStart() >= getNumberOfValves()) ? -1 : valves[getValveOffsetStart()]; - } - #pragma region JSONDECODABLE static const char * decoderName() { return "NowTask"; @@ -118,7 +95,7 @@ struct NowTask : public JsonEncodable, sampleVolume = source[SAMPLE_VOLUME]; dryTime = source[DRY_TIME]; preserveTime = source[PRESERVE_TIME]; - *valve = source[CURR_VALVE]; + valve = source[CURR_VALVE]; } #pragma endregion #pragma region JSONENCODABLE @@ -134,7 +111,8 @@ struct NowTask : public JsonEncodable, bool encodeJSON(const JsonVariant & dst) const override { using namespace TaskKeys; // clang-format off - return dst[ID].set(id) + return dst[ID].set(id) + && dst[NAME].set((char *)name) && dst[STATUS].set(status) && dst[FLUSH_TIME].set(flushTime) && dst[FLUSH_VOLUME].set(flushVolume) @@ -143,7 +121,7 @@ struct NowTask : public JsonEncodable, && dst[SAMPLE_VOLUME].set(sampleVolume) && dst[DRY_TIME].set(dryTime) && dst[PRESERVE_TIME].set(preserveTime) - && dst[CURR_VALVE].set(*valve); + && dst[CURR_VALVE].set(valve); } // clang-format on size_t printTo(Print & printer) const override { @@ -162,4 +140,13 @@ struct NowTask : public JsonEncodable, config.dryTime = dryTime; config.preserveTime = preserveTime; } + + void operator()(NewStateController::Config & config) const { + config.flushTime = flushTime; + config.sampleTime = sampleTime; + config.samplePressure = samplePressure; + config.sampleVolume = sampleVolume; + config.dryTime = dryTime; + config.preserveTime = preserveTime; + } }; \ No newline at end of file diff --git a/src/Task/NowTaskManager.hpp b/src/Task/NowTaskManager.hpp index 932e193..c640d56 100644 --- a/src/Task/NowTaskManager.hpp +++ b/src/Task/NowTaskManager.hpp @@ -48,6 +48,7 @@ class NowTaskManager : public KPComponent, println(GREEN("Checking now task")); if (now() >= task_length() + last_nowTask) { println(GREEN("now task complete")); + task.valve++; return markTaskAsCompleted(); } From c2293b6fa7cac49ecaf4da17740836bda0432a28 Mon Sep 17 00:00:00 2001 From: Nathan Jesudason Date: Mon, 30 Aug 2021 11:53:07 -0700 Subject: [PATCH 25/61] Implemented debubbler --- src/API/API.cpp | 14 +++++++ src/API/API.hpp | 4 ++ src/Application/App-serial.cpp | 5 +++ src/Application/App-wifi.cpp | 6 +++ src/Application/App.hpp | 17 ++++++++ .../DebubbleStateController.hpp | 42 +++++++++++++++++++ src/States/Shared.cpp | 11 +++++ src/States/Shared.hpp | 10 +++++ 8 files changed, 109 insertions(+) create mode 100644 src/StateControllers/DebubbleStateController.hpp diff --git a/src/API/API.cpp b/src/API/API.cpp index 77f90f2..e2b175c 100644 --- a/src/API/API.cpp +++ b/src/API/API.cpp @@ -15,6 +15,20 @@ namespace API { return response; } + auto StartDebubble::operator()(App & app) -> R { + decltype(auto) debubbleName = app.debubbleStateController.getCurrentState()->getName(); + + R response; + if (strcmp(Debubble::IDLE, debubbleName) == 0) { + app.beginDebubble(); + response["success"] = "Begin preloading water"; + } else { + response["error"] = "Preloading water is already in operation"; + } + + return response; + } + auto StatusGet::operator()(App & app) -> R { R response; encodeJSON(app.status, response.to()); diff --git a/src/API/API.hpp b/src/API/API.hpp index 8599c0c..66a966b 100644 --- a/src/API/API.hpp +++ b/src/API/API.hpp @@ -26,6 +26,10 @@ namespace API { auto operator()(Arg<0>) -> R; }; + struct StartDebubble : APISpec(App &)> { + auto operator()(Arg<0>) -> R; + }; + struct StatusGet : APISpec(App &)> { auto operator()(Arg<0>) -> R; }; diff --git a/src/Application/App-serial.cpp b/src/Application/App-serial.cpp index 6434f5a..9adedd8 100644 --- a/src/Application/App-serial.cpp +++ b/src/Application/App-serial.cpp @@ -26,6 +26,11 @@ void App::setupSerialRouting() { serializeJson(response, Serial); }; + mapNameToCallback["debubble"] = [this](SerialRequest req) { + const auto response = dispatchAPI(); + serializeJson(response, Serial); + }; + mapNameToCallback["query"] = [this](SerialRequest req) { const char * endpoint = req.path[1]; if (strcmp(endpoint, "status") == 0) { diff --git a/src/Application/App-wifi.cpp b/src/Application/App-wifi.cpp index 84ad9a9..3aa08cc 100644 --- a/src/Application/App-wifi.cpp +++ b/src/Application/App-wifi.cpp @@ -20,6 +20,12 @@ void App::setupServerRouting() { res.end(); }); + server.get("/api/alcohol-debubbler", [this](Request &, Response & res) { + const auto & response = dispatchAPI(); + res.json(response); + res.end(); + }); + // ──────────────────────────────────────────────────────────────────────────────── // Get the current status // ──────────────────────────────────────────────────────────────────────────────── diff --git a/src/Application/App.hpp b/src/Application/App.hpp index 014ed80..0702fef 100755 --- a/src/Application/App.hpp +++ b/src/Application/App.hpp @@ -19,6 +19,7 @@ #include #include +#include #include #include @@ -66,6 +67,7 @@ class App : public KPController, public KPSerialInputObserver, public TaskObserv // MainStateController sm; NewStateController newStateController; HyperFlushStateController hyperFlushStateController; + DebubbleStateController debubbleStateController; ValveManager vm; TaskManager tm; @@ -162,6 +164,17 @@ class App : public KPController, public KPSerialInputObserver, public TaskObserv addComponent(hyperFlushStateController); hyperFlushStateController.idle(); // Wait in IDLE + // + // ─── Debubbler CONTROLLER ────────────────────────────────────── + // + + debubbleStateController.configure([](Debubble::Config & config) { + config.time = 10; + }); + + addComponent(debubbleStateController); + debubbleStateController.idle(); // Wait in IDLE + // // ─── NEW STATE CONTROLLER ──────────────────────────────────────── // @@ -304,6 +317,10 @@ class App : public KPController, public KPSerialInputObserver, public TaskObserv hyperFlushStateController.begin(); } + void beginDebubble() { + debubbleStateController.begin(); + } + ValveBlock currentValveNumberToBlock() { return { shift.toRegisterIndex(status.currentValve) + 1, shift.toPinIndex(status.currentValve)}; diff --git a/src/StateControllers/DebubbleStateController.hpp b/src/StateControllers/DebubbleStateController.hpp new file mode 100644 index 0000000..a70b33b --- /dev/null +++ b/src/StateControllers/DebubbleStateController.hpp @@ -0,0 +1,42 @@ +#pragma once +#include +#include + +namespace Debubble { + STATE(IDLE); + STATE(STOP); + STATE(ALCOHOL_PURGE); + + struct Config { + decltype(SharedStates::AlcoholPurge::time) time; + }; + + class Controller : public StateControllerWithConfig { + public: + Controller() : StateControllerWithConfig("debubble-state-machine") {} + + // FLUSH -> OFFSHOOT_PRELOAD -> STOP -> IDLE + void setup() override { + registerState(SharedStates::AlcoholPurge(), ALCOHOL_PURGE, STOP); + registerState(SharedStates::Stop(), STOP, IDLE); + registerState(SharedStates::Idle(), IDLE); + } + + void begin() override { + decltype(auto) alcohol_purge = getState(ALCOHOL_PURGE); + alcohol_purge.time = config.time; + + transitionTo(ALCOHOL_PURGE); + } + + void stop() override { + transitionTo(STOP); + } + + void idle() override { + transitionTo(IDLE); + } + }; +} // namespace Debubble + +using DebubbleStateController = Debubble::Controller; diff --git a/src/States/Shared.cpp b/src/States/Shared.cpp index 5ee3e8b..363268c 100644 --- a/src/States/Shared.cpp +++ b/src/States/Shared.cpp @@ -162,4 +162,15 @@ namespace SharedStates { setTimeCondition(time, [&]() { sm.next(); }); } + + void AlcoholPurge::enter(KPStateMachine & sm) { + auto & app = *static_cast(sm.controller); + app.shift.writeAllRegistersLow(); + app.intake.off(); + app.shift.setPin(TPICDevices::ALCHOHOL_VALVE, HIGH); + app.shift.write(); + app.pump.on(); + + setTimeCondition(time, [&]() { sm.next(); }); + } } // namespace SharedStates \ No newline at end of file diff --git a/src/States/Shared.hpp b/src/States/Shared.hpp index 06e6d58..c561bed 100644 --- a/src/States/Shared.hpp +++ b/src/States/Shared.hpp @@ -108,4 +108,14 @@ namespace SharedStates { unsigned long time = 0; void enter(KPStateMachine & sm) override; }; + + /** ──────────────────────────────────────────────────────────────────────────── + * A state used to remove air bubbles from a alcohol bag + * + * ──────────────────────────────────────────────────────────────────────────── */ + class AlcoholPurge : public KPState { + public: + unsigned long time = 0; + void enter(KPStateMachine & sm) override; + }; } // namespace SharedStates From 62610dddbf868e57636c51a7d83bef775c3b88cc Mon Sep 17 00:00:00 2001 From: Nathan Jesudason Date: Wed, 7 Jul 2021 16:49:32 -0700 Subject: [PATCH 26/61] implemented component test version --- platformio.ini | 7 ++- src/Application/App.hpp | 101 +++++++++++++++++++++++++++++++++- src/Application/Constants.hpp | 2 +- 3 files changed, 106 insertions(+), 4 deletions(-) diff --git a/platformio.ini b/platformio.ini index f5865ee..3885c62 100644 --- a/platformio.ini +++ b/platformio.ini @@ -37,4 +37,9 @@ build_flags = -D DEBUG=1 -Wall -Wno-unknown-pragmas -std=c++14 ; [env:live] ; build_unflags = -std=gnu++11 -; build_flags = -D LIVE=1 -Wall -Wno-unknown-pragmas -std=c++14 \ No newline at end of file +; build_flags = -D LIVE=1 -Wall -Wno-unknown-pragmas -std=c++14 + +; [env:component_test] +; build_unflags = -std=gnu++11 +; build_flags = -D COMPONENT_TEST=1 -Wall -Wno-unknown-pragmas -std=c++14 +; monitor_filters = send_on_enter \ No newline at end of file diff --git a/src/Application/App.hpp b/src/Application/App.hpp index 014ed80..c6c17ea 100755 --- a/src/Application/App.hpp +++ b/src/Application/App.hpp @@ -37,6 +37,7 @@ class App : public KPController, public KPSerialInputObserver, public TaskObserv void setupAPI(); void setupSerialRouting(); void setupServerRouting(); +// void testValve(int v); void commandReceived(const char * line, size_t size) override; public: @@ -83,13 +84,34 @@ class App : public KPController, public KPSerialInputObserver, public TaskObserv return "Application-Task Observer"; } + + void testValve(int v, const char * name) { + println("========="); + print("Testing valve: "); + print(name); + println(); + shift.setAllRegistersLow(); + shift.writePin(v + shift.capacityPerRegister, HIGH); + shift.write(); + println("press any key to continue: "); + while (!Serial.available()) { + yield(); + } + while(Serial.available() > 0){ + Serial.read(); + } + delay(20); + } + public: void setup() override { KPSerialInput::sharedInstance().addObserver(this); Serial.begin(115200); -#ifdef DEBUG +#if defined(DEBUG) || defined(COMPONENT_TEST) while (!Serial) {}; +#endif +#ifdef DEBUG println(); println(BLUE("==================================================")); println(BLUE(" DEBUG MODE")); @@ -207,9 +229,83 @@ class App : public KPController, public KPSerialInputObserver, public TaskObserv }); runForever(1000, "detailLog", [&]() { logDetail("detail.csv"); }); -#ifdef DEBUG +#if defined(DEBUG) || defined(COMPONENT_TEST) runForever(2000, "memLog", [&]() { printFreeRam(); }); #endif + +#ifdef COMPONENT_TEST + println(); + println(BLUE("=================== RUNNING COMPONENT TEST ==================")); + + println("Press any key to start: "); + while (!Serial.available()) { + yield(); + } + while(Serial.available() > 0){ + Serial.read(); + } + char buffer [50]; + for(int i = 0; i < 24; i++){ + std::sprintf(buffer, "%d", i); + testValve(i, buffer); + } + + testValve(TPICDevices::AIR_VALVE, "Air valve"); + testValve(TPICDevices::FLUSH_VALVE, "Flush valve"); + testValve(TPICDevices::ALCHOHOL_VALVE, "Alcohol valve"); + + println("Testing ball valve"); + + shift.writeAllRegistersLow(); + intake.on(); + println("press any key to continue: "); + while (!Serial.available()) { + yield(); + } + while(Serial.available() > 0){ + Serial.read(); + } + intake.off(); + + println(); + println("Testing pump..."); + shift.writeAllRegistersLow(); + shift.setPin(TPICDevices::AIR_VALVE, HIGH); + shift.setPin(TPICDevices::FLUSH_VALVE, HIGH); + shift.write(); + pump.on(); + delay(3000); + pump.off(); + delay(3000); + pump.on(Direction::reverse); + delay(3000); + pump.off(); + println("press any key to continue: "); + while (!Serial.available()) { + yield(); + } + while(Serial.available() > 0){ + Serial.read(); + } + + if(sensors.pressure.enabled){ + println("Pressure sensor detected"); + } else { + println(RED("Pressure sensor not detected")); + } + if(sensors.baro1.enabled){ + println("Baro1 sensor detected"); + }else{ + println(RED("Baro1 sensor not detected")); + } + if(sensors.baro2.enabled){ + println("Baro2 sensor detected"); + }else{ + println(RED("Baro2 sensor not detected")); + } + + println(BLUE("=================== COMPONENT TEST COMPLETE ==================")); +#endif } void logDetail(const char * filename) { @@ -436,6 +532,7 @@ class App : public KPController, public KPSerialInputObserver, public TaskObserv if (!status.isProgrammingMode() && !status.preventShutdown) { shutdown(); } + } /** ──────────────────────────────────────────────────────────────────────────── diff --git a/src/Application/Constants.hpp b/src/Application/Constants.hpp index 16d649d..1b17ae7 100755 --- a/src/Application/Constants.hpp +++ b/src/Application/Constants.hpp @@ -25,7 +25,7 @@ namespace TPICDevices { // turn on namespace HardwarePins { __k_auto POWER_MODULE = A0; -#ifdef LIVE +#if defined(LIVE) || defined(COMPONENT_TEST) // A1 is eDNA, 12 for HYPNOS __k_auto RTC_INTERRUPT = A1; #else From bdb51d689632896c3d2dab54e3b4e5e7a97b9aad Mon Sep 17 00:00:00 2001 From: Nathan Jesudason Date: Thu, 2 Sep 2021 12:48:55 -0700 Subject: [PATCH 27/61] added version dependency for RTC lib --- platformio.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platformio.ini b/platformio.ini index 3885c62..0f3e4bb 100644 --- a/platformio.ini +++ b/platformio.ini @@ -24,7 +24,7 @@ lib_deps = WiFi101@~0.16.0 Low-Power@~1.6 868@~1.2.4 - DS3232RTC + DS3232RTC@~1.2 [env:debug] ; build_type = debug From 65dd742cb3e66217646e6094ce7db648e5574626 Mon Sep 17 00:00:00 2001 From: Nathan Jesudason Date: Thu, 2 Sep 2021 12:21:22 -0700 Subject: [PATCH 28/61] increase task buffer size to 1000 bytes --- src/Application/Constants.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Application/Constants.hpp b/src/Application/Constants.hpp index 1b17ae7..e9cadcd 100755 --- a/src/Application/Constants.hpp +++ b/src/Application/Constants.hpp @@ -49,7 +49,7 @@ namespace ProgramSettings { __k_auto SD_FILE_NAME_LENGTH = 13; __k_auto CONFIG_JSON_BUFFER_SIZE = 800; __k_auto STATUS_JSON_BUFFER_SIZE = 800; - __k_auto TASK_JSON_BUFFER_SIZE = 800; + __k_auto TASK_JSON_BUFFER_SIZE = 1000; __k_auto TASKREF_JSON_BUFFER_SIZE = 50; __k_auto MAX_VALVES = 24; __k_auto VALVE_JSON_BUFFER_SIZE = 500; From e5854f6af6409d26d19bd99da84b7a736fce4c64 Mon Sep 17 00:00:00 2001 From: Nathan Jesudason Date: Wed, 1 Sep 2021 22:17:10 -0700 Subject: [PATCH 29/61] Created issue templates --- .github/ISSUE_TEMPLATE/bug_report.md | 31 +++++++++++++++++++++++ .github/ISSUE_TEMPLATE/feature_request.md | 20 +++++++++++++++ .github/ISSUE_TEMPLATE/known-issue.md | 20 +++++++++++++++ 3 files changed, 71 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/bug_report.md create mode 100644 .github/ISSUE_TEMPLATE/feature_request.md create mode 100644 .github/ISSUE_TEMPLATE/known-issue.md diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000..ad8b7ca --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,31 @@ +--- +name: Bug report +about: Describe an unknown current software or hardware issue +title: '' +labels: bug +assignees: '' + +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**To Reproduce** +Steps to reproduce the behavior: +1. Go to '...' +2. Click on '....' +3. Scroll down to '....' +4. See error + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Screenshots** +If applicable, add screenshots to help explain your problem. + +**Specifications of sampler:** + - Browser used [e.g. chrome, safari] + - Sampler version + +**Additional context** +Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000..62ef820 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,20 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: '' +labels: enhancement +assignees: '' + +--- + +**Describe the feature you'd like** +A clear and concise description of what you want to happen. + +**What is the scenario where someone would want this feature?** +A clear and concise description of why this feature would be useful. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + +**Additional context** +Add any other implementation details here. diff --git a/.github/ISSUE_TEMPLATE/known-issue.md b/.github/ISSUE_TEMPLATE/known-issue.md new file mode 100644 index 0000000..5055301 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/known-issue.md @@ -0,0 +1,20 @@ +--- +name: Known Issue +about: Create a report of a known issue +title: '' +labels: bug, documentation +assignees: '' + +--- + +**What causes this?** +A clear and concise description of what the bug is. + +**What does this problem look like? When would someone experience this problem?** +What someone would experience if they were having this issue. + +**How to resolve/avoid this issue?** +A clear description of what to do to solve this issue. + +**Additional context** +Add any other context about the problem here. From 5f5663bc653537c8b2e19393a430f0a98e691415 Mon Sep 17 00:00:00 2001 From: Nathan Jesudason Date: Fri, 3 Sep 2021 11:56:01 -0700 Subject: [PATCH 30/61] Revert "added version dependency for RTC lib" This reverts commit bdb51d689632896c3d2dab54e3b4e5e7a97b9aad. --- platformio.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platformio.ini b/platformio.ini index 0f3e4bb..3885c62 100644 --- a/platformio.ini +++ b/platformio.ini @@ -24,7 +24,7 @@ lib_deps = WiFi101@~0.16.0 Low-Power@~1.6 868@~1.2.4 - DS3232RTC@~1.2 + DS3232RTC [env:debug] ; build_type = debug From 574e527043a6b2128222bee658221dc2c5861196 Mon Sep 17 00:00:00 2001 From: Nathan Jesudason Date: Fri, 3 Sep 2021 11:59:22 -0700 Subject: [PATCH 31/61] Revert "added version dependency for RTC lib" This reverts commit bdb51d689632896c3d2dab54e3b4e5e7a97b9aad. --- platformio.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platformio.ini b/platformio.ini index 9a3b6c0..af06262 100644 --- a/platformio.ini +++ b/platformio.ini @@ -24,7 +24,7 @@ lib_deps = WiFi101@~0.16.0 Low-Power@~1.6 868@~1.2.4 - DS3232RTC@~1.2 + DS3232RTC [env:debug] build_unflags = -std=gnu++11 From 0da17f6fbca4c0a0a2114ffababbde56f5969d57 Mon Sep 17 00:00:00 2001 From: Nathan Jesudason Date: Sun, 26 Sep 2021 21:45:06 -0700 Subject: [PATCH 32/61] simplified advanceTask function and removed useless functions --- src/StateControllers/MainStateController.cpp | 9 +-- src/Task/NowTaskManager.hpp | 64 +------------------- 2 files changed, 8 insertions(+), 65 deletions(-) diff --git a/src/StateControllers/MainStateController.cpp b/src/StateControllers/MainStateController.cpp index 1c02fe8..5516542 100644 --- a/src/StateControllers/MainStateController.cpp +++ b/src/StateControllers/MainStateController.cpp @@ -18,11 +18,12 @@ void Main::Stop::enter(KPStateMachine & sm) { auto currentTaskId = app.currentTaskId; if(currentTaskId){ app.tm.advanceTask(currentTaskId); + app.tm.writeToDirectory(); + } + if(app.sampleNowActive){ + app.ntm.advanceTask(); + app.ntm.writeToDirectory(); } - app.tm.writeToDirectory(); - - app.ntm.advanceTask(); - app.ntm.writeToDirectory(); app.currentTaskId = 0; app.sampleNowActive = false; app.status.currentValve = -1; diff --git a/src/Task/NowTaskManager.hpp b/src/Task/NowTaskManager.hpp index c640d56..b380eda 100644 --- a/src/Task/NowTaskManager.hpp +++ b/src/Task/NowTaskManager.hpp @@ -20,45 +20,16 @@ class NowTaskManager : public KPComponent, const char * taskFolder = nullptr; NowTask task; - time_t last_nowTask = now(); - NowTaskManager() : KPComponent("NowTaskManager") {} void init(Config & config) { taskFolder = config.taskFolder; } - int generateTaskId() const { - return random(1, RAND_MAX); - } -/* - Task createTask() { - const auto timenow = now(); - Task task; - task.id = generateTaskId(); - task.createdAt = timenow; - task.schedule = timenow; - return task; - } - -*/ - bool advanceTask() { - - println(GREEN("Checking now task")); - if (now() >= task_length() + last_nowTask) { - println(GREEN("now task complete")); - task.valve++; - return markTaskAsCompleted(); - } - - return false; - } - - - int task_length() { - return task.flushTime + task.sampleTime + task.samplePressure + task.dryTime + task.preserveTime; - } + task.valve++; + return markTaskAsCompleted(); +} bool setTaskStatus(TaskStatus status) { task.status = status; @@ -81,35 +52,6 @@ class NowTaskManager : public KPComponent, return task.isCompleted(); } - /* - bool findTask(int id) const { - return tasks.find(id) != tasks.end(); - } - - bool deleteTask(int id) { - if (tasks.erase(id)) { - updateObservers(&TaskObserver::taskDidDelete, id); - return true; - } - - return false; - } - - int deleteIf(std::function predicate) { - int oldSize = tasks.size(); - for (auto it = tasks.begin(); it != tasks.end();) { - if (predicate(it->second)) { - auto id = it->first; - it = tasks.erase(it); - updateObservers(&TaskObserver::taskDidDelete, id); - } else { - it++; - } - } - - return oldSize - tasks.size(); - } - */ /** ──────────────────────────────────────────────────────────────────────────── * @brief Load all tasks object from the specified directory in the SD card From b4c4a22c68cc6880f74b38f1b20c4a667414f3a7 Mon Sep 17 00:00:00 2001 From: Nathan Jesudason Date: Mon, 27 Sep 2021 15:17:26 -0700 Subject: [PATCH 33/61] Added interrupt disabling to make sample now tasks more robust --- src/Application/App.hpp | 5 ++++- src/Components/NowSampleButton.hpp | 1 + src/Components/Power.hpp | 1 + 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/Application/App.hpp b/src/Application/App.hpp index 2c81d80..ccbf8d9 100755 --- a/src/Application/App.hpp +++ b/src/Application/App.hpp @@ -267,14 +267,15 @@ class App : public KPController, public KPSerialInputObserver, public TaskObserv power.onInterrupt([this]() { println(GREEN("RTC Interrupted!")); println(scheduleNextActiveTask().description()); + interrupts(); }); nowSampleButton.onInterrupt([this](){ //check to make sure task isn't running that disables button println(GREEN("Sample Now Button Interrupted!")); - //digitalWrite(LED_BUILTIN, HIGH); println(beginNowTask().description()); + interrupts(); }); runForever(1000, "detailLog", [&]() { logDetail("detail.csv"); }); #if defined(DEBUG) || defined(COMPONENT_TEST) @@ -524,6 +525,8 @@ class App : public KPController, public KPSerialInputObserver, public TaskObserv } ScheduleReturnCode beginNowTask(){ + if(currentTaskId) + return ScheduleReturnCode::unavailable; NowTask task = ntm.task; status.preventShutdown = false; if(task.valve < 0 || task.valve > config.numberOfValves || vm.valves[task.valve].status != ValveStatus::free){ diff --git a/src/Components/NowSampleButton.hpp b/src/Components/NowSampleButton.hpp index 4d4f865..098c751 100644 --- a/src/Components/NowSampleButton.hpp +++ b/src/Components/NowSampleButton.hpp @@ -66,6 +66,7 @@ class NowSampleButton : public KPComponent { // Continue if button has a new press disableSampleButton(); + noInterrupts(); buttonTriggered = false; interruptCallback(); } diff --git a/src/Components/Power.hpp b/src/Components/Power.hpp index 9cafa04..4cf9874 100644 --- a/src/Components/Power.hpp +++ b/src/Components/Power.hpp @@ -96,6 +96,7 @@ class Power : public KPComponent { // This is important in noisy environment if (rtc.alarm(1) || rtc.alarm(2)) { disarmAlarms(); + noInterrupts(); interruptCallback(); alarmTriggered = false; } From b49bcd1fce51a3692628d5a55f7567b334ee81d2 Mon Sep 17 00:00:00 2001 From: Nathan Jesudason Date: Thu, 30 Sep 2021 12:04:43 -0700 Subject: [PATCH 34/61] added sample button reset after a valve ends --- src/StateControllers/MainStateController.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/StateControllers/MainStateController.cpp b/src/StateControllers/MainStateController.cpp index 5516542..af29eac 100644 --- a/src/StateControllers/MainStateController.cpp +++ b/src/StateControllers/MainStateController.cpp @@ -26,6 +26,7 @@ void Main::Stop::enter(KPStateMachine & sm) { } app.currentTaskId = 0; app.sampleNowActive = false; + app.nowSampleButton.setSampleButton(); app.status.currentValve = -1; sm.next(); } From fffa224f6ad86a697c87d73e77ce2cf9d71803c0 Mon Sep 17 00:00:00 2001 From: Nathan Jesudason Date: Tue, 5 Oct 2021 14:19:06 -0700 Subject: [PATCH 35/61] fixed alcohol debubbler code --- src/States/Shared.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/States/Shared.cpp b/src/States/Shared.cpp index 363268c..f8b234f 100644 --- a/src/States/Shared.cpp +++ b/src/States/Shared.cpp @@ -168,6 +168,7 @@ namespace SharedStates { app.shift.writeAllRegistersLow(); app.intake.off(); app.shift.setPin(TPICDevices::ALCHOHOL_VALVE, HIGH); + app.shift.setPin(TPICDevices::FLUSH_VALVE, HIGH); app.shift.write(); app.pump.on(); From 5e05e0279a5ab6283f7db22ef8f8c3f610104545 Mon Sep 17 00:00:00 2001 From: Nathan Jesudason Date: Wed, 6 Oct 2021 14:46:20 -0700 Subject: [PATCH 36/61] added rtc alarm checking to now sample button interrupt --- src/Application/App.hpp | 12 ++++++++---- src/Components/NowSampleButton.hpp | 1 + 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/Application/App.hpp b/src/Application/App.hpp index ccbf8d9..a0a9369 100755 --- a/src/Application/App.hpp +++ b/src/Application/App.hpp @@ -272,9 +272,10 @@ class App : public KPController, public KPSerialInputObserver, public TaskObserv nowSampleButton.onInterrupt([this](){ - //check to make sure task isn't running that disables button - println(GREEN("Sample Now Button Interrupted!")); - println(beginNowTask().description()); + if(!power.rtc.alarm(1) && !power.rtc.alarm(2)){ + println(GREEN("Sample Now Button Interrupted!")); + println(beginNowTask().description()); + } interrupts(); }); runForever(1000, "detailLog", [&]() { logDetail("detail.csv"); }); @@ -583,6 +584,7 @@ class App : public KPController, public KPSerialInputObserver, public TaskObserv * @return false if task is either missed schedule or no active task available. * ──────────────────────────────────────────────────────────────────────────── */ ScheduleReturnCode scheduleNextActiveTask(bool shouldStopCurrentTask = false) { + nowSampleButton.disableSampleButton(); status.preventShutdown = false; for (auto id : tm.getActiveSortedTaskIds()) { Task & task = tm.tasks[id]; @@ -617,7 +619,7 @@ class App : public KPController, public KPSerialInputObserver, public TaskObserv continue; } - nowSampleButton.disableSampleButton(); + // Wake up between 10 secs of the actual schedule time // Prepare an action to execute at exact time const auto timeUntil = task.schedule - time_now; @@ -638,11 +640,13 @@ class App : public KPController, public KPSerialInputObserver, public TaskObserv } else { // Wake up before not due to alarm, reschedule anyway power.scheduleNextAlarm(task.schedule - 8); // 3 < x < 10 + nowSampleButton.setSampleButton(); return ScheduleReturnCode::scheduled; } } currentTaskId = 0; + nowSampleButton.setSampleButton(); return ScheduleReturnCode::unavailable; } diff --git a/src/Components/NowSampleButton.hpp b/src/Components/NowSampleButton.hpp index 098c751..40c6df8 100644 --- a/src/Components/NowSampleButton.hpp +++ b/src/Components/NowSampleButton.hpp @@ -81,6 +81,7 @@ class NowSampleButton : public KPComponent { * ──────────────────────────────────────────────────────────────────────────── */ void disableSampleButton() { println("Disabled sample now button"); + buttonTriggered = false; detachInterrupt(digitalPinToInterrupt(HardwarePins::BUTTON_PIN)); } From bca267e52381ea50a03a55dae05555930e6b0327 Mon Sep 17 00:00:00 2001 From: Nathan Jesudason Date: Wed, 6 Oct 2021 15:11:57 -0700 Subject: [PATCH 37/61] fixed StartNowTask API --- src/API/API.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/API/API.cpp b/src/API/API.cpp index b5c43fb..2afa6bd 100644 --- a/src/API/API.cpp +++ b/src/API/API.cpp @@ -25,6 +25,8 @@ namespace API { } else { response["error"] = "Already Sampling"; } + + return response; } auto StartDebubble::operator()(App & app) -> R { From cf6a47d5ec4cc24f160e90c5805ef199708d6b98 Mon Sep 17 00:00:00 2001 From: Nathan Jesudason Date: Fri, 22 Oct 2021 22:43:44 -0700 Subject: [PATCH 38/61] Shift reg validation (#22) * Revert "added version dependency for RTC lib" This reverts commit bdb51d689632896c3d2dab54e3b4e5e7a97b9aad. * implemented update functions for all v3 states --- src/States/Shared.cpp | 88 +++++++++++++++++++++++++++++++++++++++++++ src/States/Shared.hpp | 21 +++++++++++ 2 files changed, 109 insertions(+) diff --git a/src/States/Shared.cpp b/src/States/Shared.cpp index f8b234f..4e89cf1 100644 --- a/src/States/Shared.cpp +++ b/src/States/Shared.cpp @@ -24,6 +24,18 @@ namespace SharedStates { setTimeCondition(time, [&]() { sm.next(); }); } + void Flush::update(KPStateMachine & sm) { + if ((unsigned long) (millis() - updateTime) < updateDelay) { + return; + } + + updateTime = millis(); + auto & app = *static_cast(sm.controller); + app.shift.setAllRegistersLow(); + app.shift.setPin(TPICDevices::FLUSH_VALVE, HIGH); + app.shift.write(); + } + void FlushVolume::enter(KPStateMachine & sm) { auto & app = *static_cast(sm.controller); app.shift.setAllRegistersLow(); @@ -37,6 +49,18 @@ namespace SharedStates { setTimeCondition(time, [&]() { sm.next(); }); } + void FlushVolume::update(KPStateMachine & sm) { + if ((unsigned long) (millis() - updateTime) < updateDelay) { + return; + } + + updateTime = millis(); + auto & app = *static_cast(sm.controller); + app.shift.setAllRegistersLow(); + app.shift.setPin(TPICDevices::FLUSH_VALVE, HIGH); + app.shift.write(); + } + void AirFlush::enter(KPStateMachine & sm) { auto & app = *static_cast(sm.controller); app.shift.writeAllRegistersLow(); @@ -48,6 +72,18 @@ namespace SharedStates { setTimeCondition(time, [&]() { sm.next(); }); } + void AirFlush::update(KPStateMachine & sm) { + if ((unsigned long) (millis() - updateTime) < updateDelay) { + return; + } + + updateTime = millis(); + auto & app = *static_cast(sm.controller); + app.shift.setAllRegistersLow(); + app.shift.setPin(TPICDevices::FLUSH_VALVE, HIGH); + app.shift.write(); + } + void Sample::enter(KPStateMachine & sm) { // We set the latch valve to intake mode, turn on the filter valve, then the pump auto & app = *static_cast(sm.controller); @@ -84,6 +120,18 @@ namespace SharedStates { setCondition(condition, [&]() { sm.next(); }); } + void Sample::update(KPStateMachine & sm){ + if ((unsigned long) (millis() - updateTime) < updateDelay) { + return; + } + + updateTime = millis(); + auto & app = *static_cast(sm.controller); + app.shift.setAllRegistersLow(); + app.shift.setPin(app.currentValveIdToPin(), HIGH); + app.shift.write(); + } + void Dry::enter(KPStateMachine & sm) { auto & app = *static_cast(sm.controller); app.shift.setAllRegistersLow(); @@ -96,6 +144,19 @@ namespace SharedStates { setTimeCondition(time, [&]() { sm.next(); }); } + void Dry::update(KPStateMachine & sm) { + if ((unsigned long) (millis() - updateTime) < updateDelay) { + return; + } + + updateTime = millis(); + auto & app = *static_cast(sm.controller); + app.shift.setAllRegistersLow(); + app.shift.setPin(TPICDevices::AIR_VALVE, HIGH); + app.shift.setPin(app.currentValveIdToPin(), HIGH); + app.shift.write(); + } + void OffshootClean::enter(KPStateMachine & sm) { auto & app = *static_cast(sm.controller); app.shift.setAllRegistersLow(); // Reset shift registers @@ -108,6 +169,19 @@ namespace SharedStates { setTimeCondition(time, [&]() { sm.next(); }); }; + void OffshootClean::update(KPStateMachine & sm){ + if ((unsigned long) (millis() - updateTime) < updateDelay) { + return; + } + + updateTime = millis(); + auto & app = *static_cast(sm.controller); + app.shift.setAllRegistersLow(); + app.shift.setPin(app.currentValveIdToPin(), HIGH); + app.shift.setPin(TPICDevices::FLUSH_VALVE, HIGH); + app.shift.write(); + } + void OffshootPreload::enter(KPStateMachine & sm) { // Intake valve is opened and the motor is runnning ... // Turnoff only the flush valve @@ -163,6 +237,20 @@ namespace SharedStates { setTimeCondition(time, [&]() { sm.next(); }); } + + void Preserve::update(KPStateMachine & sm){ + if ((unsigned long) (millis() - updateTime) < updateDelay) { + return; + } + + updateTime = millis(); + auto & app = *static_cast(sm.controller); + app.shift.setAllRegistersLow(); + app.shift.setPin(TPICDevices::ALCHOHOL_VALVE, HIGH); + app.shift.setPin(app.currentValveIdToPin(), HIGH); + app.shift.write(); + } + void AlcoholPurge::enter(KPStateMachine & sm) { auto & app = *static_cast(sm.controller); app.shift.writeAllRegistersLow(); diff --git a/src/States/Shared.hpp b/src/States/Shared.hpp index c561bed..3dc2aa9 100644 --- a/src/States/Shared.hpp +++ b/src/States/Shared.hpp @@ -30,6 +30,9 @@ namespace SharedStates { public: unsigned long time = 10; void enter(KPStateMachine & sm) override; + unsigned long updateTime = millis(); + unsigned long updateDelay = 1000; + void update(KPStateMachine & sm) override; }; /** ──────────────────────────────────────────────────────────────────────────── @@ -41,6 +44,9 @@ namespace SharedStates { unsigned long time = 10; unsigned long volume = 1000; void enter(KPStateMachine & sm) override; + unsigned long updateTime = millis(); + unsigned long updateDelay = 1000; + void update(KPStateMachine & sm) override; }; /** ──────────────────────────────────────────────────────────────────────────── @@ -51,6 +57,9 @@ namespace SharedStates { public: unsigned long time = 15; void enter(KPStateMachine & sm) override; + unsigned long updateTime = millis(); + unsigned long updateDelay = 1000; + void update(KPStateMachine & sm) override; }; /** ──────────────────────────────────────────────────────────────────────────── @@ -66,6 +75,9 @@ namespace SharedStates { const char * condition; void enter(KPStateMachine & sm) override; + unsigned long updateTime = millis(); + unsigned long updateDelay = 1000; + void update(KPStateMachine & sm) override; }; /** ──────────────────────────────────────────────────────────────────────────── @@ -76,6 +88,9 @@ namespace SharedStates { public: unsigned long time = 10; void enter(KPStateMachine & sm) override; + unsigned long updateTime = millis(); + unsigned long updateDelay = 1000; + void update(KPStateMachine & sm) override; }; /** ──────────────────────────────────────────────────────────────────────────── @@ -87,6 +102,9 @@ namespace SharedStates { unsigned long time = 5; OffshootClean(unsigned long time) : time(time) {} void enter(KPStateMachine & sm) override; + unsigned long updateTime = millis(); + unsigned long updateDelay = 1000; + void update(KPStateMachine & sm) override; }; /** ──────────────────────────────────────────────────────────────────────────── @@ -107,6 +125,9 @@ namespace SharedStates { public: unsigned long time = 0; void enter(KPStateMachine & sm) override; + unsigned long updateTime = millis(); + unsigned long updateDelay = 1000; + void update(KPStateMachine & sm) override; }; /** ──────────────────────────────────────────────────────────────────────────── From 5e73fdf02202963f7f360b8d99be47ba05dd286e Mon Sep 17 00:00:00 2001 From: Nathan Jesudason Date: Fri, 22 Oct 2021 22:57:10 -0700 Subject: [PATCH 39/61] Shift reg validation (#23) * Revert "added version dependency for RTC lib" This reverts commit bdb51d689632896c3d2dab54e3b4e5e7a97b9aad. * implemented update functions for all v3 states --- src/States/Shared.cpp | 88 +++++++++++++++++++++++++++++++++++++++++++ src/States/Shared.hpp | 21 +++++++++++ 2 files changed, 109 insertions(+) diff --git a/src/States/Shared.cpp b/src/States/Shared.cpp index 363268c..557c2fb 100644 --- a/src/States/Shared.cpp +++ b/src/States/Shared.cpp @@ -24,6 +24,18 @@ namespace SharedStates { setTimeCondition(time, [&]() { sm.next(); }); } + void Flush::update(KPStateMachine & sm) { + if ((unsigned long) (millis() - updateTime) < updateDelay) { + return; + } + + updateTime = millis(); + auto & app = *static_cast(sm.controller); + app.shift.setAllRegistersLow(); + app.shift.setPin(TPICDevices::FLUSH_VALVE, HIGH); + app.shift.write(); + } + void FlushVolume::enter(KPStateMachine & sm) { auto & app = *static_cast(sm.controller); app.shift.setAllRegistersLow(); @@ -37,6 +49,18 @@ namespace SharedStates { setTimeCondition(time, [&]() { sm.next(); }); } + void FlushVolume::update(KPStateMachine & sm) { + if ((unsigned long) (millis() - updateTime) < updateDelay) { + return; + } + + updateTime = millis(); + auto & app = *static_cast(sm.controller); + app.shift.setAllRegistersLow(); + app.shift.setPin(TPICDevices::FLUSH_VALVE, HIGH); + app.shift.write(); + } + void AirFlush::enter(KPStateMachine & sm) { auto & app = *static_cast(sm.controller); app.shift.writeAllRegistersLow(); @@ -48,6 +72,18 @@ namespace SharedStates { setTimeCondition(time, [&]() { sm.next(); }); } + void AirFlush::update(KPStateMachine & sm) { + if ((unsigned long) (millis() - updateTime) < updateDelay) { + return; + } + + updateTime = millis(); + auto & app = *static_cast(sm.controller); + app.shift.setAllRegistersLow(); + app.shift.setPin(TPICDevices::FLUSH_VALVE, HIGH); + app.shift.write(); + } + void Sample::enter(KPStateMachine & sm) { // We set the latch valve to intake mode, turn on the filter valve, then the pump auto & app = *static_cast(sm.controller); @@ -84,6 +120,18 @@ namespace SharedStates { setCondition(condition, [&]() { sm.next(); }); } + void Sample::update(KPStateMachine & sm){ + if ((unsigned long) (millis() - updateTime) < updateDelay) { + return; + } + + updateTime = millis(); + auto & app = *static_cast(sm.controller); + app.shift.setAllRegistersLow(); + app.shift.setPin(app.currentValveIdToPin(), HIGH); + app.shift.write(); + } + void Dry::enter(KPStateMachine & sm) { auto & app = *static_cast(sm.controller); app.shift.setAllRegistersLow(); @@ -96,6 +144,19 @@ namespace SharedStates { setTimeCondition(time, [&]() { sm.next(); }); } + void Dry::update(KPStateMachine & sm) { + if ((unsigned long) (millis() - updateTime) < updateDelay) { + return; + } + + updateTime = millis(); + auto & app = *static_cast(sm.controller); + app.shift.setAllRegistersLow(); + app.shift.setPin(TPICDevices::AIR_VALVE, HIGH); + app.shift.setPin(app.currentValveIdToPin(), HIGH); + app.shift.write(); + } + void OffshootClean::enter(KPStateMachine & sm) { auto & app = *static_cast(sm.controller); app.shift.setAllRegistersLow(); // Reset shift registers @@ -108,6 +169,19 @@ namespace SharedStates { setTimeCondition(time, [&]() { sm.next(); }); }; + void OffshootClean::update(KPStateMachine & sm){ + if ((unsigned long) (millis() - updateTime) < updateDelay) { + return; + } + + updateTime = millis(); + auto & app = *static_cast(sm.controller); + app.shift.setAllRegistersLow(); + app.shift.setPin(app.currentValveIdToPin(), HIGH); + app.shift.setPin(TPICDevices::FLUSH_VALVE, HIGH); + app.shift.write(); + } + void OffshootPreload::enter(KPStateMachine & sm) { // Intake valve is opened and the motor is runnning ... // Turnoff only the flush valve @@ -163,6 +237,20 @@ namespace SharedStates { setTimeCondition(time, [&]() { sm.next(); }); } + + void Preserve::update(KPStateMachine & sm){ + if ((unsigned long) (millis() - updateTime) < updateDelay) { + return; + } + + updateTime = millis(); + auto & app = *static_cast(sm.controller); + app.shift.setAllRegistersLow(); + app.shift.setPin(TPICDevices::ALCHOHOL_VALVE, HIGH); + app.shift.setPin(app.currentValveIdToPin(), HIGH); + app.shift.write(); + } + void AlcoholPurge::enter(KPStateMachine & sm) { auto & app = *static_cast(sm.controller); app.shift.writeAllRegistersLow(); diff --git a/src/States/Shared.hpp b/src/States/Shared.hpp index c561bed..3dc2aa9 100644 --- a/src/States/Shared.hpp +++ b/src/States/Shared.hpp @@ -30,6 +30,9 @@ namespace SharedStates { public: unsigned long time = 10; void enter(KPStateMachine & sm) override; + unsigned long updateTime = millis(); + unsigned long updateDelay = 1000; + void update(KPStateMachine & sm) override; }; /** ──────────────────────────────────────────────────────────────────────────── @@ -41,6 +44,9 @@ namespace SharedStates { unsigned long time = 10; unsigned long volume = 1000; void enter(KPStateMachine & sm) override; + unsigned long updateTime = millis(); + unsigned long updateDelay = 1000; + void update(KPStateMachine & sm) override; }; /** ──────────────────────────────────────────────────────────────────────────── @@ -51,6 +57,9 @@ namespace SharedStates { public: unsigned long time = 15; void enter(KPStateMachine & sm) override; + unsigned long updateTime = millis(); + unsigned long updateDelay = 1000; + void update(KPStateMachine & sm) override; }; /** ──────────────────────────────────────────────────────────────────────────── @@ -66,6 +75,9 @@ namespace SharedStates { const char * condition; void enter(KPStateMachine & sm) override; + unsigned long updateTime = millis(); + unsigned long updateDelay = 1000; + void update(KPStateMachine & sm) override; }; /** ──────────────────────────────────────────────────────────────────────────── @@ -76,6 +88,9 @@ namespace SharedStates { public: unsigned long time = 10; void enter(KPStateMachine & sm) override; + unsigned long updateTime = millis(); + unsigned long updateDelay = 1000; + void update(KPStateMachine & sm) override; }; /** ──────────────────────────────────────────────────────────────────────────── @@ -87,6 +102,9 @@ namespace SharedStates { unsigned long time = 5; OffshootClean(unsigned long time) : time(time) {} void enter(KPStateMachine & sm) override; + unsigned long updateTime = millis(); + unsigned long updateDelay = 1000; + void update(KPStateMachine & sm) override; }; /** ──────────────────────────────────────────────────────────────────────────── @@ -107,6 +125,9 @@ namespace SharedStates { public: unsigned long time = 0; void enter(KPStateMachine & sm) override; + unsigned long updateTime = millis(); + unsigned long updateDelay = 1000; + void update(KPStateMachine & sm) override; }; /** ──────────────────────────────────────────────────────────────────────────── From 8ee90d5833e188549b65ec857432c7316ad6f772 Mon Sep 17 00:00:00 2001 From: Nathan Jesudason Date: Fri, 22 Oct 2021 23:04:40 -0700 Subject: [PATCH 40/61] Added update for AlcoholPurge State --- src/States/Shared.cpp | 15 +++++++++++++++ src/States/Shared.hpp | 3 +++ 2 files changed, 18 insertions(+) diff --git a/src/States/Shared.cpp b/src/States/Shared.cpp index 557c2fb..a45fdf8 100644 --- a/src/States/Shared.cpp +++ b/src/States/Shared.cpp @@ -256,9 +256,24 @@ namespace SharedStates { app.shift.writeAllRegistersLow(); app.intake.off(); app.shift.setPin(TPICDevices::ALCHOHOL_VALVE, HIGH); + app.shift.setPin(TPICDevices::FLUSH_VALVE, HIGH); app.shift.write(); app.pump.on(); setTimeCondition(time, [&]() { sm.next(); }); } + + void AlcoholPurge::update(KPStateMachine & sm){ + if ((unsigned long) (millis() - updateTime) < updateDelay) { + return; + } + + updateTime = millis(); + auto & app = *static_cast(sm.controller); + app.shift.setAllRegistersLow(); + app.shift.setPin(TPICDevices::ALCHOHOL_VALVE, HIGH); + app.shift.setPin(TPICDevices::FLUSH_VALVE, HIGH); + app.shift.write(); + } + } // namespace SharedStates \ No newline at end of file diff --git a/src/States/Shared.hpp b/src/States/Shared.hpp index 3dc2aa9..9553e8b 100644 --- a/src/States/Shared.hpp +++ b/src/States/Shared.hpp @@ -138,5 +138,8 @@ namespace SharedStates { public: unsigned long time = 0; void enter(KPStateMachine & sm) override; + unsigned long updateTime = millis(); + unsigned long updateDelay = 1000; + void update(KPStateMachine & sm) override; }; } // namespace SharedStates From 1e8fe791e2cd6786169cc67b2759fb76080c7fd3 Mon Sep 17 00:00:00 2001 From: Nathan Jesudason Date: Fri, 22 Oct 2021 23:07:04 -0700 Subject: [PATCH 41/61] added update to alcohol purge state --- src/States/Shared.cpp | 13 +++++++++++++ src/States/Shared.hpp | 3 +++ 2 files changed, 16 insertions(+) diff --git a/src/States/Shared.cpp b/src/States/Shared.cpp index 4e89cf1..604203a 100644 --- a/src/States/Shared.cpp +++ b/src/States/Shared.cpp @@ -262,4 +262,17 @@ namespace SharedStates { setTimeCondition(time, [&]() { sm.next(); }); } + + void AlcoholPurge::update(KPStateMachine & sm){ + if ((unsigned long) (millis() - updateTime) < updateDelay) { + return; + } + + updateTime = millis(); + auto & app = *static_cast(sm.controller); + app.shift.setAllRegistersLow(); + app.shift.setPin(TPICDevices::ALCHOHOL_VALVE, HIGH); + app.shift.setPin(TPICDevices::FLUSH_VALVE, HIGH); + app.shift.write(); + } } // namespace SharedStates \ No newline at end of file diff --git a/src/States/Shared.hpp b/src/States/Shared.hpp index 3dc2aa9..9553e8b 100644 --- a/src/States/Shared.hpp +++ b/src/States/Shared.hpp @@ -138,5 +138,8 @@ namespace SharedStates { public: unsigned long time = 0; void enter(KPStateMachine & sm) override; + unsigned long updateTime = millis(); + unsigned long updateDelay = 1000; + void update(KPStateMachine & sm) override; }; } // namespace SharedStates From 888d64b6fd2b6a928cceae631056ea93b1e2bf84 Mon Sep 17 00:00:00 2001 From: Nathan Jesudason Date: Thu, 18 Nov 2021 12:14:39 -0800 Subject: [PATCH 42/61] initial state transition changes --- src/States/Shared.cpp | 21 +++++++++++++++++++++ src/States/Shared.hpp | 2 ++ 2 files changed, 23 insertions(+) diff --git a/src/States/Shared.cpp b/src/States/Shared.cpp index 604203a..9327d28 100644 --- a/src/States/Shared.cpp +++ b/src/States/Shared.cpp @@ -16,6 +16,7 @@ namespace SharedStates { auto & app = *static_cast(sm.controller); app.shift.setAllRegistersLow(); app.intake.on(); + delay(5000); app.shift.setPin(TPICDevices::FLUSH_VALVE, HIGH); app.shift.write(); app.pump.on(); @@ -36,10 +37,17 @@ namespace SharedStates { app.shift.write(); } + void Flush::leave(KPStateMachine & sm) { + auto & app = *static_cast(sm.controller); + app.pump.off(); + delay(1000); + } + void FlushVolume::enter(KPStateMachine & sm) { auto & app = *static_cast(sm.controller); app.shift.setAllRegistersLow(); app.intake.on(); + delay(5000); app.shift.setPin(TPICDevices::FLUSH_VALVE, HIGH); app.shift.write(); app.pump.on(); @@ -89,6 +97,7 @@ namespace SharedStates { auto & app = *static_cast(sm.controller); app.shift.setAllRegistersLow(); app.intake.on(); + delay(5000); app.shift.setPin(app.currentValveIdToPin(), HIGH); app.shift.write(); app.pump.on(); @@ -136,6 +145,7 @@ namespace SharedStates { auto & app = *static_cast(sm.controller); app.shift.setAllRegistersLow(); app.intake.off(); + delay(5000); app.shift.setPin(TPICDevices::AIR_VALVE, HIGH); app.shift.setPin(app.currentValveIdToPin(), HIGH); app.shift.write(); @@ -161,6 +171,7 @@ namespace SharedStates { auto & app = *static_cast(sm.controller); app.shift.setAllRegistersLow(); // Reset shift registers app.intake.on(); + delay(5000); // Delay to ensure ball intake is set properly app.shift.setPin(app.currentValveIdToPin(), HIGH); app.shift.setPin(TPICDevices::FLUSH_VALVE, HIGH); app.shift.write(); @@ -182,12 +193,20 @@ namespace SharedStates { app.shift.write(); } + void OffshootClean::leave(KPStateMachine & sm){ + auto & app = *static_cast(sm.controller); + app.pump.off(); + delay(1000); + } + void OffshootPreload::enter(KPStateMachine & sm) { // Intake valve is opened and the motor is runnning ... // Turnoff only the flush valve auto & app = *static_cast(sm.controller); app.shift.setPin(TPICDevices::FLUSH_VALVE, LOW); app.intake.on(); + delay(5000); + app.pump.on(); // Reserving space ahead of time for performance reserve(app.vm.numberOfValvesInUse + 1); @@ -229,6 +248,7 @@ namespace SharedStates { auto & app = *static_cast(sm.controller); app.shift.writeAllRegistersLow(); app.intake.off(); + delay(5000); app.shift.setPin(TPICDevices::ALCHOHOL_VALVE, HIGH); app.shift.setPin(app.currentValveIdToPin(), HIGH); app.shift.write(); @@ -255,6 +275,7 @@ namespace SharedStates { auto & app = *static_cast(sm.controller); app.shift.writeAllRegistersLow(); app.intake.off(); + delay(5000); app.shift.setPin(TPICDevices::ALCHOHOL_VALVE, HIGH); app.shift.setPin(TPICDevices::FLUSH_VALVE, HIGH); app.shift.write(); diff --git a/src/States/Shared.hpp b/src/States/Shared.hpp index 9553e8b..7fa88fb 100644 --- a/src/States/Shared.hpp +++ b/src/States/Shared.hpp @@ -33,6 +33,7 @@ namespace SharedStates { unsigned long updateTime = millis(); unsigned long updateDelay = 1000; void update(KPStateMachine & sm) override; + void leave(KPStateMachine & sm) override; }; /** ──────────────────────────────────────────────────────────────────────────── @@ -105,6 +106,7 @@ namespace SharedStates { unsigned long updateTime = millis(); unsigned long updateDelay = 1000; void update(KPStateMachine & sm) override; + void leave(KPStateMachine & sm) override; }; /** ──────────────────────────────────────────────────────────────────────────── From 749714c274f0391c529ad83edaac4c537a50e317 Mon Sep 17 00:00:00 2001 From: Nathan Jesudason Date: Fri, 3 Dec 2021 13:10:36 -0800 Subject: [PATCH 43/61] added proper state transitions and depressurizing in hyperflush --- lib/Framework | 2 +- src/States/Shared.cpp | 21 ++++++++++++--------- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/lib/Framework b/lib/Framework index f37d6f7..4dee7f6 160000 --- a/lib/Framework +++ b/lib/Framework @@ -1 +1 @@ -Subproject commit f37d6f7e4e906a32b53f6765d5c3345c46a05ede +Subproject commit 4dee7f65ba09d943498a63012065821448a3a7f3 diff --git a/src/States/Shared.cpp b/src/States/Shared.cpp index 9327d28..dd21dc2 100644 --- a/src/States/Shared.cpp +++ b/src/States/Shared.cpp @@ -22,7 +22,7 @@ namespace SharedStates { app.pump.on(); // To next state after 10 secs - setTimeCondition(time, [&]() { sm.next(); }); + setRelativeTimeCondition(time, [&]() { sm.next(); }); } void Flush::update(KPStateMachine & sm) { @@ -54,7 +54,7 @@ namespace SharedStates { auto condition = [&]() { return app.status.waterVolume >= 500; }; setCondition(condition, [&]() { sm.next(1); }); - setTimeCondition(time, [&]() { sm.next(); }); + setRelativeTimeCondition(time, [&]() { sm.next(); }); } void FlushVolume::update(KPStateMachine & sm) { @@ -119,7 +119,7 @@ namespace SharedStates { this->condition = "pressure"; } - if (timeSinceLastTransition() >= secsToMillis(time)) { + if (timeSinceLastTransition() - 5 >= secsToMillis(time)) { //minus 5 to account for delay this->condition = "time"; } @@ -151,7 +151,7 @@ namespace SharedStates { app.shift.write(); app.pump.on(); - setTimeCondition(time, [&]() { sm.next(); }); + setRelativeTimeCondition(time, [&]() { sm.next(); }); } void Dry::update(KPStateMachine & sm) { @@ -177,7 +177,7 @@ namespace SharedStates { app.shift.write(); app.pump.on(Direction::reverse); - setTimeCondition(time, [&]() { sm.next(); }); + setRelativeTimeCondition(time, [&]() { sm.next(); }); }; void OffshootClean::update(KPStateMachine & sm){ @@ -221,15 +221,18 @@ namespace SharedStates { // Skip the first register auto valvePin = valve.id + app.shift.capacityPerRegister; - setTimeCondition(counter * preloadTime, [&app, prevValvePin, valvePin]() { + setRelativeTimeCondition(counter * preloadTime + counter, [&app, prevValvePin, valvePin]() { if (prevValvePin) { // Turn off the previous valve app.shift.setPin(prevValvePin, LOW); + app.pump.off(); + delay(1000); println("done"); } app.shift.setPin(valvePin, HIGH); app.shift.write(); + app.pump.on(); print("Flushing offshoot ", valvePin - app.shift.capacityPerRegister, "..."); }); @@ -238,7 +241,7 @@ namespace SharedStates { } // Transition to the next state after the last valve - setTimeCondition(counter * preloadTime, [&]() { + setRelativeTimeCondition(counter * preloadTime, [&]() { println("done"); sm.next(); }); @@ -254,7 +257,7 @@ namespace SharedStates { app.shift.write(); app.pump.on(); - setTimeCondition(time, [&]() { sm.next(); }); + setRelativeTimeCondition(time, [&]() { sm.next(); }); } @@ -281,7 +284,7 @@ namespace SharedStates { app.shift.write(); app.pump.on(); - setTimeCondition(time, [&]() { sm.next(); }); + setRelativeTimeCondition(time, [&]() { sm.next(); }); } void AlcoholPurge::update(KPStateMachine & sm){ From 34602280765505d6643a88e857a99e639d4826df Mon Sep 17 00:00:00 2001 From: Nathan Jesudason Date: Thu, 16 Dec 2021 17:32:02 -0800 Subject: [PATCH 44/61] added flush valve pulse and fixed hyperflush ending early --- src/States/Shared.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/States/Shared.cpp b/src/States/Shared.cpp index dd21dc2..5ce1afc 100644 --- a/src/States/Shared.cpp +++ b/src/States/Shared.cpp @@ -204,6 +204,7 @@ namespace SharedStates { // Turnoff only the flush valve auto & app = *static_cast(sm.controller); app.shift.setPin(TPICDevices::FLUSH_VALVE, LOW); + app.shift.write(); app.intake.on(); delay(5000); app.pump.on(); @@ -226,12 +227,16 @@ namespace SharedStates { // Turn off the previous valve app.shift.setPin(prevValvePin, LOW); app.pump.off(); - delay(1000); + app.shift.setPin(TPICDevices::FLUSH_VALVE, HIGH); + app.shift.write(); + delay(500); println("done"); } app.shift.setPin(valvePin, HIGH); + app.shift.setPin(TPICDevices::FLUSH_VALVE, HIGH); app.shift.write(); + delay(500); app.pump.on(); print("Flushing offshoot ", valvePin - app.shift.capacityPerRegister, "..."); }); @@ -241,7 +246,7 @@ namespace SharedStates { } // Transition to the next state after the last valve - setRelativeTimeCondition(counter * preloadTime, [&]() { + setRelativeTimeCondition(counter * preloadTime + counter, [&]() { println("done"); sm.next(); }); From 215b9ffdbb5e0f2b24b5e214bd60b568c5ce2ac9 Mon Sep 17 00:00:00 2001 From: Nathan Jesudason Date: Mon, 20 Dec 2021 14:50:08 -0800 Subject: [PATCH 45/61] changed delay() calls to setting time conditions --- src/States/Shared.cpp | 108 ++++++++++++++++++++++++++---------------- 1 file changed, 68 insertions(+), 40 deletions(-) diff --git a/src/States/Shared.cpp b/src/States/Shared.cpp index 5ce1afc..da2a560 100644 --- a/src/States/Shared.cpp +++ b/src/States/Shared.cpp @@ -16,16 +16,20 @@ namespace SharedStates { auto & app = *static_cast(sm.controller); app.shift.setAllRegistersLow(); app.intake.on(); - delay(5000); - app.shift.setPin(TPICDevices::FLUSH_VALVE, HIGH); - app.shift.write(); - app.pump.on(); + setTimeCondition(5, [&app](){ + app.shift.setPin(TPICDevices::FLUSH_VALVE, HIGH); + app.shift.write(); + app.pump.on(); + }); // To next state after 10 secs - setRelativeTimeCondition(time, [&]() { sm.next(); }); + setTimeCondition(time + 5, [&]() { sm.next(); }); } void Flush::update(KPStateMachine & sm) { + if(timeSinceLastTransition() < 5000){ + return; + } if ((unsigned long) (millis() - updateTime) < updateDelay) { return; } @@ -47,17 +51,21 @@ namespace SharedStates { auto & app = *static_cast(sm.controller); app.shift.setAllRegistersLow(); app.intake.on(); - delay(5000); - app.shift.setPin(TPICDevices::FLUSH_VALVE, HIGH); - app.shift.write(); - app.pump.on(); + setTimeCondition(5, [&app](){ + app.shift.setPin(TPICDevices::FLUSH_VALVE, HIGH); + app.shift.write(); + app.pump.on(); + }); auto condition = [&]() { return app.status.waterVolume >= 500; }; setCondition(condition, [&]() { sm.next(1); }); - setRelativeTimeCondition(time, [&]() { sm.next(); }); + setTimeCondition(time + 5, [&]() { sm.next(); }); } void FlushVolume::update(KPStateMachine & sm) { + if(timeSinceLastTransition() < 5000){ + return; + } if ((unsigned long) (millis() - updateTime) < updateDelay) { return; } @@ -97,11 +105,12 @@ namespace SharedStates { auto & app = *static_cast(sm.controller); app.shift.setAllRegistersLow(); app.intake.on(); - delay(5000); - app.shift.setPin(app.currentValveIdToPin(), HIGH); - app.shift.write(); - app.pump.on(); - + setTimeCondition(5, [&app](){ + app.shift.setPin(app.currentValveIdToPin(), HIGH); + app.shift.write(); + app.pump.on(); + }); + app.sensors.flow.resetVolume(); app.sensors.flow.startMeasurement(); @@ -130,6 +139,10 @@ namespace SharedStates { } void Sample::update(KPStateMachine & sm){ + if(timeSinceLastTransition() < 5000){ + return; + } + if ((unsigned long) (millis() - updateTime) < updateDelay) { return; } @@ -155,6 +168,9 @@ namespace SharedStates { } void Dry::update(KPStateMachine & sm) { + if(timeSinceLastTransition() < 5000){ + return; + } if ((unsigned long) (millis() - updateTime) < updateDelay) { return; } @@ -171,16 +187,21 @@ namespace SharedStates { auto & app = *static_cast(sm.controller); app.shift.setAllRegistersLow(); // Reset shift registers app.intake.on(); - delay(5000); // Delay to ensure ball intake is set properly - app.shift.setPin(app.currentValveIdToPin(), HIGH); - app.shift.setPin(TPICDevices::FLUSH_VALVE, HIGH); - app.shift.write(); - app.pump.on(Direction::reverse); + // Delay to ensure ball intake is set properly + setTimeCondition(5, [&app](){ + app.shift.setPin(app.currentValveIdToPin(), HIGH); + app.shift.setPin(TPICDevices::FLUSH_VALVE, HIGH); + app.shift.write(); + app.pump.on(Direction::reverse); + }); - setRelativeTimeCondition(time, [&]() { sm.next(); }); + setTimeCondition(time + 5, [&]() { sm.next(); }); }; void OffshootClean::update(KPStateMachine & sm){ + if( timeSinceLastTransition() < 5000){ + return; + } if ((unsigned long) (millis() - updateTime) < updateDelay) { return; } @@ -206,9 +227,10 @@ namespace SharedStates { app.shift.setPin(TPICDevices::FLUSH_VALVE, LOW); app.shift.write(); app.intake.on(); - delay(5000); - app.pump.on(); - + setTimeCondition(5, [&app](){ + app.pump.on(); + }); + // Reserving space ahead of time for performance reserve(app.vm.numberOfValvesInUse + 1); println("Begin preloading procedure for ", app.vm.numberOfValvesInUse, " valves..."); @@ -222,7 +244,7 @@ namespace SharedStates { // Skip the first register auto valvePin = valve.id + app.shift.capacityPerRegister; - setRelativeTimeCondition(counter * preloadTime + counter, [&app, prevValvePin, valvePin]() { + setTimeCondition(counter * preloadTime + counter + 5, [&app, prevValvePin, valvePin]() { if (prevValvePin) { // Turn off the previous valve app.shift.setPin(prevValvePin, LOW); @@ -246,7 +268,7 @@ namespace SharedStates { } // Transition to the next state after the last valve - setRelativeTimeCondition(counter * preloadTime + counter, [&]() { + setTimeCondition(counter * preloadTime + counter + 5, [&]() { println("done"); sm.next(); }); @@ -256,17 +278,20 @@ namespace SharedStates { auto & app = *static_cast(sm.controller); app.shift.writeAllRegistersLow(); app.intake.off(); - delay(5000); - app.shift.setPin(TPICDevices::ALCHOHOL_VALVE, HIGH); - app.shift.setPin(app.currentValveIdToPin(), HIGH); - app.shift.write(); - app.pump.on(); - - setRelativeTimeCondition(time, [&]() { sm.next(); }); + setTimeCondition(5, [&app](){ + app.shift.setPin(TPICDevices::ALCHOHOL_VALVE, HIGH); + app.shift.setPin(app.currentValveIdToPin(), HIGH); + app.shift.write(); + app.pump.on(); + }); + setTimeCondition(time + 5, [&]() { sm.next(); }); } void Preserve::update(KPStateMachine & sm){ + if(timeSinceLastTransition() < 5000){ + return; + } if ((unsigned long) (millis() - updateTime) < updateDelay) { return; } @@ -283,16 +308,19 @@ namespace SharedStates { auto & app = *static_cast(sm.controller); app.shift.writeAllRegistersLow(); app.intake.off(); - delay(5000); - app.shift.setPin(TPICDevices::ALCHOHOL_VALVE, HIGH); - app.shift.setPin(TPICDevices::FLUSH_VALVE, HIGH); - app.shift.write(); - app.pump.on(); - - setRelativeTimeCondition(time, [&]() { sm.next(); }); + setTimeCondition(5, [&app](){ + app.shift.setPin(TPICDevices::ALCHOHOL_VALVE, HIGH); + app.shift.setPin(TPICDevices::FLUSH_VALVE, HIGH); + app.shift.write(); + app.pump.on(); + }); + setTimeCondition(time + 5, [&]() { sm.next(); }); } void AlcoholPurge::update(KPStateMachine & sm){ + if(timeSinceLastTransition() < 5000){ + return; + } if ((unsigned long) (millis() - updateTime) < updateDelay) { return; } From a30d398f36ecebdde2904f9a66708b445c01ea6c Mon Sep 17 00:00:00 2001 From: Nathan Jesudason Date: Wed, 12 Jan 2022 11:48:15 -0800 Subject: [PATCH 46/61] added delay between shift registers and pump turning on for each state --- src/States/Shared.cpp | 78 +++++++++++++++++++++++++++++-------------- 1 file changed, 53 insertions(+), 25 deletions(-) diff --git a/src/States/Shared.cpp b/src/States/Shared.cpp index da2a560..c64bb33 100644 --- a/src/States/Shared.cpp +++ b/src/States/Shared.cpp @@ -19,11 +19,15 @@ namespace SharedStates { setTimeCondition(5, [&app](){ app.shift.setPin(TPICDevices::FLUSH_VALVE, HIGH); app.shift.write(); + }); + + setTimeCondition(6, [&app](){ app.pump.on(); }); + // To next state after 10 secs - setTimeCondition(time + 5, [&]() { sm.next(); }); + setTimeCondition(time + 6, [&]() { sm.next(); }); } void Flush::update(KPStateMachine & sm) { @@ -57,9 +61,13 @@ namespace SharedStates { app.pump.on(); }); + setTimeCondition(6, [&app](){ + app.pump.on(); + }); + auto condition = [&]() { return app.status.waterVolume >= 500; }; setCondition(condition, [&]() { sm.next(1); }); - setTimeCondition(time + 5, [&]() { sm.next(); }); + setTimeCondition(time + 6, [&]() { sm.next(); }); } void FlushVolume::update(KPStateMachine & sm) { @@ -83,9 +91,12 @@ namespace SharedStates { app.shift.setPin(TPICDevices::AIR_VALVE, HIGH); app.shift.setPin(TPICDevices::FLUSH_VALVE, HIGH); app.shift.write(); - app.pump.on(); - setTimeCondition(time, [&]() { sm.next(); }); + setTimeCondition(1, [&app](){ + app.pump.on(); + }); + + setTimeCondition(time + 1, [&]() { sm.next(); }); } void AirFlush::update(KPStateMachine & sm) { @@ -108,7 +119,10 @@ namespace SharedStates { setTimeCondition(5, [&app](){ app.shift.setPin(app.currentValveIdToPin(), HIGH); app.shift.write(); - app.pump.on(); + }); + + setTimeCondition(6, [&app](){ + app.pump.on(); }); app.sensors.flow.resetVolume(); @@ -128,7 +142,7 @@ namespace SharedStates { this->condition = "pressure"; } - if (timeSinceLastTransition() - 5 >= secsToMillis(time)) { //minus 5 to account for delay + if (timeSinceLastTransition() - 6 >= secsToMillis(time)) { //minus 6 to account for delay this->condition = "time"; } @@ -158,13 +172,18 @@ namespace SharedStates { auto & app = *static_cast(sm.controller); app.shift.setAllRegistersLow(); app.intake.off(); - delay(5000); - app.shift.setPin(TPICDevices::AIR_VALVE, HIGH); - app.shift.setPin(app.currentValveIdToPin(), HIGH); - app.shift.write(); - app.pump.on(); - setRelativeTimeCondition(time, [&]() { sm.next(); }); + setTimeCondition(5, [&app](){ + app.shift.setPin(TPICDevices::AIR_VALVE, HIGH); + app.shift.setPin(app.currentValveIdToPin(), HIGH); + app.shift.write(); + }); + + setTimeCondition(6, [&app](){ + app.pump.on(); + }); + + setRelativeTimeCondition(time + 6, [&]() { sm.next(); }); } void Dry::update(KPStateMachine & sm) { @@ -192,10 +211,18 @@ namespace SharedStates { app.shift.setPin(app.currentValveIdToPin(), HIGH); app.shift.setPin(TPICDevices::FLUSH_VALVE, HIGH); app.shift.write(); + }); + + setTimeCondition(6, [&app](){ app.pump.on(Direction::reverse); }); - setTimeCondition(time + 5, [&]() { sm.next(); }); + //turn off pump right before state transition (leave function would be harder to have time delay) + setTimeCondition(time + 6, [&app](){ + app.pump.off(); + }); + + setTimeCondition(time + 7, [&]() { sm.next(); }); }; void OffshootClean::update(KPStateMachine & sm){ @@ -214,12 +241,6 @@ namespace SharedStates { app.shift.write(); } - void OffshootClean::leave(KPStateMachine & sm){ - auto & app = *static_cast(sm.controller); - app.pump.off(); - delay(1000); - } - void OffshootPreload::enter(KPStateMachine & sm) { // Intake valve is opened and the motor is runnning ... // Turnoff only the flush valve @@ -244,25 +265,27 @@ namespace SharedStates { // Skip the first register auto valvePin = valve.id + app.shift.capacityPerRegister; - setTimeCondition(counter * preloadTime + counter + 5, [&app, prevValvePin, valvePin]() { + setTimeCondition(counter * preloadTime + 5, [&app, prevValvePin, valvePin]() { if (prevValvePin) { // Turn off the previous valve app.shift.setPin(prevValvePin, LOW); app.pump.off(); app.shift.setPin(TPICDevices::FLUSH_VALVE, HIGH); app.shift.write(); - delay(500); println("done"); } app.shift.setPin(valvePin, HIGH); app.shift.setPin(TPICDevices::FLUSH_VALVE, HIGH); app.shift.write(); - delay(500); - app.pump.on(); + print("Flushing offshoot ", valvePin - app.shift.capacityPerRegister, "..."); }); + setTimeCondition(counter * preloadTime + counter + 5, [&app, prevValvePin, valvePin]() { + app.pump.on(); + }); + prevValvePin = valvePin; counter++; } @@ -282,9 +305,12 @@ namespace SharedStates { app.shift.setPin(TPICDevices::ALCHOHOL_VALVE, HIGH); app.shift.setPin(app.currentValveIdToPin(), HIGH); app.shift.write(); + }); + + setTimeCondition(6, [&app](){ app.pump.on(); }); - setTimeCondition(time + 5, [&]() { sm.next(); }); + setTimeCondition(time + 6, [&]() { sm.next(); }); } @@ -312,9 +338,11 @@ namespace SharedStates { app.shift.setPin(TPICDevices::ALCHOHOL_VALVE, HIGH); app.shift.setPin(TPICDevices::FLUSH_VALVE, HIGH); app.shift.write(); + }); + setTimeCondition(6, [&app](){ app.pump.on(); }); - setTimeCondition(time + 5, [&]() { sm.next(); }); + setTimeCondition(time + 6, [&]() { sm.next(); }); } void AlcoholPurge::update(KPStateMachine & sm){ From 11b9ec97eb7a2b35ded79ea0be634d8c40806931 Mon Sep 17 00:00:00 2001 From: Nathan Jesudason Date: Sat, 15 Jan 2022 12:58:38 -0800 Subject: [PATCH 47/61] fixed bugs with hyperflush and states --- src/States/Shared.cpp | 6 ++++-- src/States/Shared.hpp | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/States/Shared.cpp b/src/States/Shared.cpp index c64bb33..e2bd76f 100644 --- a/src/States/Shared.cpp +++ b/src/States/Shared.cpp @@ -205,6 +205,7 @@ namespace SharedStates { void OffshootClean::enter(KPStateMachine & sm) { auto & app = *static_cast(sm.controller); app.shift.setAllRegistersLow(); // Reset shift registers + app.pump.off(); app.intake.on(); // Delay to ensure ball intake is set properly setTimeCondition(5, [&app](){ @@ -282,7 +283,7 @@ namespace SharedStates { print("Flushing offshoot ", valvePin - app.shift.capacityPerRegister, "..."); }); - setTimeCondition(counter * preloadTime + counter + 5, [&app, prevValvePin, valvePin]() { + setTimeCondition(counter * preloadTime + 6, [&app, prevValvePin, valvePin]() { app.pump.on(); }); @@ -291,7 +292,7 @@ namespace SharedStates { } // Transition to the next state after the last valve - setTimeCondition(counter * preloadTime + counter + 5, [&]() { + setTimeCondition(counter * preloadTime + 6, [&]() { println("done"); sm.next(); }); @@ -299,6 +300,7 @@ namespace SharedStates { void Preserve::enter(KPStateMachine & sm) { auto & app = *static_cast(sm.controller); + app.pump.off(); app.shift.writeAllRegistersLow(); app.intake.off(); setTimeCondition(5, [&app](){ diff --git a/src/States/Shared.hpp b/src/States/Shared.hpp index 7fa88fb..b678cdc 100644 --- a/src/States/Shared.hpp +++ b/src/States/Shared.hpp @@ -106,7 +106,7 @@ namespace SharedStates { unsigned long updateTime = millis(); unsigned long updateDelay = 1000; void update(KPStateMachine & sm) override; - void leave(KPStateMachine & sm) override; + //void leave(KPStateMachine & sm) override; }; /** ──────────────────────────────────────────────────────────────────────────── From 1edf3ab9fc428381612887991d9424a11700be99 Mon Sep 17 00:00:00 2001 From: Nathan Jesudason Date: Wed, 9 Feb 2022 20:45:33 -0800 Subject: [PATCH 48/61] added branch naming convention to README (#30) --- README.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/README.md b/README.md index e0d32fa..f671d23 100644 --- a/README.md +++ b/README.md @@ -40,3 +40,10 @@ You should be able to compile at this point. If so stop here, else please let me ```shell git submodule update --remote --rebase ``` + +### Branch Naming Convention +- (no prefix) - version release or main branch +- (feature) - creating a new feature based on a feature request +- (experimental) - created for testing out something +- (bugfix) - created for fixing a non-critical issue with the code +- (hotfix) - created for fixing a critical issue with code or an issue with a particular sampler From 20d6df10db420a46d76d90eab460e0250d96fcd5 Mon Sep 17 00:00:00 2001 From: Nathan Jesudason Date: Wed, 9 Feb 2022 22:54:17 -0800 Subject: [PATCH 49/61] added comments to flow sensor code and added flow rate to SD data (#31) --- src/Application/App.hpp | 20 ++++++++++++-------- src/Components/Sensor.hpp | 3 +++ src/Components/Sensors/TurbineFlowSensor.cpp | 2 ++ src/Components/Sensors/TurbineFlowSensor.hpp | 9 ++++++++- 4 files changed, 25 insertions(+), 9 deletions(-) diff --git a/src/Application/App.hpp b/src/Application/App.hpp index 9e57dc8..581a250 100755 --- a/src/Application/App.hpp +++ b/src/Application/App.hpp @@ -216,10 +216,10 @@ class App : public KPController, public KPSerialInputObserver, public TaskObserv // Regular log header if (!SD.exists(config.logFile)) { File file = SD.open(config.logFile, FILE_WRITE); - KPStringBuilder<384> header{"UTC, Formatted Time, Task Name, Valve Number, Current " + KPStringBuilder<404> header{"UTC, Formatted Time, Task Name, Valve Number, Current " "State, Config Sample Time, Config Sample " "Pressure, Config Sample Volume, Temperature Recorded," - "Max Pressure Recorded, Volume Recorded\n"}; + "Max Pressure Recorded, Volume Recorded, Flow Rate\n"}; file.println(header); file.close(); } @@ -227,10 +227,10 @@ class App : public KPController, public KPSerialInputObserver, public TaskObserv // Detail log header if (!SD.exists("detail.csv")) { File file = SD.open("detail.csv", FILE_WRITE); - KPStringBuilder<384> header{"UTC, Formatted Time, Task Name, Valve Number, Current " + KPStringBuilder<404> header{"UTC, Formatted Time, Task Name, Valve Number, Current " "State, Config Sample Time, Config Sample " "Pressure, Config Sample Volume, Temperature Recorded," - "Pressure Recorded, Volume Recorded\n"}; + "Pressure Recorded, Volume Recorded, Flow Rate\n"}; file.println(header); file.close(); } @@ -333,7 +333,7 @@ class App : public KPController, public KPSerialInputObserver, public TaskObserv formattedTime, "%u/%u/%u %02u:%02u:%02u GMT+0", year(utc), month(utc), day(utc), hour(utc), minute(utc), second(utc)); - KPStringBuilder<512> data{ + KPStringBuilder<544> data{ utc, ",", formattedTime, @@ -354,7 +354,9 @@ class App : public KPController, public KPSerialInputObserver, public TaskObserv ",", status.pressure, ",", - status.waterVolume}; + status.waterVolume, + ",", + status.waterFlow}; log.println(data); log.flush(); log.close(); @@ -372,7 +374,7 @@ class App : public KPController, public KPSerialInputObserver, public TaskObserv formattedTime, "%u/%u/%u %02u:%02u:%02u GMT+0", year(utc), month(utc), day(utc), hour(utc), minute(utc), second(utc)); - KPStringBuilder<512> data{ + KPStringBuilder<544> data{ utc, ",", formattedTime, @@ -393,7 +395,9 @@ class App : public KPController, public KPSerialInputObserver, public TaskObserv ",", status.maxPressure, ",", - status.waterVolume}; + status.waterVolume, + ",", + status.waterFlow}; log.println(data); log.flush(); log.close(); diff --git a/src/Components/Sensor.hpp b/src/Components/Sensor.hpp index fde3c26..591429b 100644 --- a/src/Components/Sensor.hpp +++ b/src/Components/Sensor.hpp @@ -108,6 +108,9 @@ class Sensor { } // Lazy evaluation: last_update will never be initialized if the sensor is not enabled; + // last_update is initialized to millis() here in the first call, and then set below + // see link for another examples of static vars in functions: + // https://www.tutorialspoint.com/what-is-the-lifetime-of-a-static-variable-in-a-cplusplus-function static long last_update = millis(); if ((millis() - last_update) < updateInterval) { return ErrorCode::notReady; diff --git a/src/Components/Sensors/TurbineFlowSensor.cpp b/src/Components/Sensors/TurbineFlowSensor.cpp index d9657f9..ea4e796 100644 --- a/src/Components/Sensors/TurbineFlowSensor.cpp +++ b/src/Components/Sensors/TurbineFlowSensor.cpp @@ -5,7 +5,9 @@ volatile unsigned long flowIntervalMicros; volatile bool flowUpdated; void flowTick() { + //This is the time interval since the last flowTick flowIntervalMicros = micros() - lastFlowTick; lastFlowTick = micros(); + //make sure to read data flowUpdated = true; } \ No newline at end of file diff --git a/src/Components/Sensors/TurbineFlowSensor.hpp b/src/Components/Sensors/TurbineFlowSensor.hpp index 1e41920..3ac3150 100644 --- a/src/Components/Sensors/TurbineFlowSensor.hpp +++ b/src/Components/Sensors/TurbineFlowSensor.hpp @@ -21,6 +21,7 @@ class TurbineFlowSensor : public Sensor { void begin() override { lastFlowTick = micros(); pinMode(A3, INPUT); + //sensor is updated once a second setUpdateFreq(1000); } @@ -43,9 +44,15 @@ class TurbineFlowSensor : public Sensor { SensorData read() override { if (flowUpdated) { flowUpdated = false; - + //micro is 1e-6, so we divide the micros in a second with the flow interval + //to get the frequency auto hz = 1000000.0 / double(flowIntervalMicros); + //The spec sheet says that the output frequency is between 36.6 to 917 Hz + //So if hz is less than 37/36.6, then the flow is zero. Otherwise, interporlate + //between the frequency into the flow rate. + //flow that the sensor can record is between 0.1LPM and 2.5LPM lpm = hz < 37 ? 0 : interpolate(hz, 37, 917, 0.110, 2.476); + //LPM * change in minute gives volume in liters volume += lpm * (flowIntervalMicros / 60000000.0); println("Volume: ", volume, ", LPM: ", lpm); } else { From 4ca0b682f924d92a9832c7caa3d36cff886e53e8 Mon Sep 17 00:00:00 2001 From: Nathan Jesudason Date: Wed, 9 Feb 2022 23:20:12 -0800 Subject: [PATCH 50/61] removed relative time transition --- lib/Framework | 2 +- src/States/Shared.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/Framework b/lib/Framework index 4dee7f6..f37d6f7 160000 --- a/lib/Framework +++ b/lib/Framework @@ -1 +1 @@ -Subproject commit 4dee7f65ba09d943498a63012065821448a3a7f3 +Subproject commit f37d6f7e4e906a32b53f6765d5c3345c46a05ede diff --git a/src/States/Shared.cpp b/src/States/Shared.cpp index e2bd76f..99fb2d1 100644 --- a/src/States/Shared.cpp +++ b/src/States/Shared.cpp @@ -183,7 +183,7 @@ namespace SharedStates { app.pump.on(); }); - setRelativeTimeCondition(time + 6, [&]() { sm.next(); }); + setTimeCondition(time + 6, [&]() { sm.next(); }); } void Dry::update(KPStateMachine & sm) { From d29b603252760ff4b914ee13c7ba7e3797fa7335 Mon Sep 17 00:00:00 2001 From: Nathan Jesudason Date: Wed, 9 Feb 2022 23:35:06 -0800 Subject: [PATCH 51/61] Feature record flow data (#32) * added comments to flow sensor code and added flow rate to SD data --- .github/ISSUE_TEMPLATE/bug_report.md | 31 ++++++ .github/ISSUE_TEMPLATE/feature_request.md | 20 ++++ .github/ISSUE_TEMPLATE/known-issue.md | 20 ++++ README.md | 7 ++ src/API/API.cpp | 1 + src/API/API.hpp | 1 + src/Application/App.hpp | 34 ++++--- src/Components/Sensor.hpp | 3 + src/Components/Sensors/TurbineFlowSensor.cpp | 2 + src/Components/Sensors/TurbineFlowSensor.hpp | 9 +- src/States/Shared.cpp | 102 +++++++++++++++++++ 11 files changed, 216 insertions(+), 14 deletions(-) create mode 100644 .github/ISSUE_TEMPLATE/bug_report.md create mode 100644 .github/ISSUE_TEMPLATE/feature_request.md create mode 100644 .github/ISSUE_TEMPLATE/known-issue.md diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000..ad8b7ca --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,31 @@ +--- +name: Bug report +about: Describe an unknown current software or hardware issue +title: '' +labels: bug +assignees: '' + +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**To Reproduce** +Steps to reproduce the behavior: +1. Go to '...' +2. Click on '....' +3. Scroll down to '....' +4. See error + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Screenshots** +If applicable, add screenshots to help explain your problem. + +**Specifications of sampler:** + - Browser used [e.g. chrome, safari] + - Sampler version + +**Additional context** +Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000..62ef820 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,20 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: '' +labels: enhancement +assignees: '' + +--- + +**Describe the feature you'd like** +A clear and concise description of what you want to happen. + +**What is the scenario where someone would want this feature?** +A clear and concise description of why this feature would be useful. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + +**Additional context** +Add any other implementation details here. diff --git a/.github/ISSUE_TEMPLATE/known-issue.md b/.github/ISSUE_TEMPLATE/known-issue.md new file mode 100644 index 0000000..5055301 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/known-issue.md @@ -0,0 +1,20 @@ +--- +name: Known Issue +about: Create a report of a known issue +title: '' +labels: bug, documentation +assignees: '' + +--- + +**What causes this?** +A clear and concise description of what the bug is. + +**What does this problem look like? When would someone experience this problem?** +What someone would experience if they were having this issue. + +**How to resolve/avoid this issue?** +A clear description of what to do to solve this issue. + +**Additional context** +Add any other context about the problem here. diff --git a/README.md b/README.md index e0d32fa..f671d23 100644 --- a/README.md +++ b/README.md @@ -40,3 +40,10 @@ You should be able to compile at this point. If so stop here, else please let me ```shell git submodule update --remote --rebase ``` + +### Branch Naming Convention +- (no prefix) - version release or main branch +- (feature) - creating a new feature based on a feature request +- (experimental) - created for testing out something +- (bugfix) - created for fixing a non-critical issue with the code +- (hotfix) - created for fixing a critical issue with code or an issue with a particular sampler diff --git a/src/API/API.cpp b/src/API/API.cpp index 2afa6bd..f1af8d7 100644 --- a/src/API/API.cpp +++ b/src/API/API.cpp @@ -15,6 +15,7 @@ namespace API { return response; } + auto StartNowTask::operator()(App & app) -> R { decltype(auto) nowTaskName = app.nowTaskStateController.getCurrentState()->getName(); diff --git a/src/API/API.hpp b/src/API/API.hpp index 496fc84..4f56013 100644 --- a/src/API/API.hpp +++ b/src/API/API.hpp @@ -30,6 +30,7 @@ namespace API { auto operator()(Arg<0>) -> R; }; + struct StartDebubble : APISpec(App &)> { auto operator()(Arg<0>) -> R; }; diff --git a/src/Application/App.hpp b/src/Application/App.hpp index a0a9369..51b5db9 100755 --- a/src/Application/App.hpp +++ b/src/Application/App.hpp @@ -209,6 +209,7 @@ class App : public KPController, public KPSerialInputObserver, public TaskObserv hyperFlushStateController.idle(); // Wait in IDLE // + // ___ NOW TASK CONTROLLER ____________ // @@ -244,10 +245,10 @@ class App : public KPController, public KPSerialInputObserver, public TaskObserv // Regular log header if (!SD.exists(config.logFile)) { File file = SD.open(config.logFile, FILE_WRITE); - KPStringBuilder<384> header{"UTC, Formatted Time, Task Name, Valve Number, Current " + KPStringBuilder<404> header{"UTC, Formatted Time, Task Name, Valve Number, Current " "State, Config Sample Time, Config Sample " "Pressure, Config Sample Volume, Temperature Recorded," - "Max Pressure Recorded, Volume Recorded\n"}; + "Max Pressure Recorded, Volume Recorded, Flow Rate\n"}; file.println(header); file.close(); } @@ -255,10 +256,10 @@ class App : public KPController, public KPSerialInputObserver, public TaskObserv // Detail log header if (!SD.exists("detail.csv")) { File file = SD.open("detail.csv", FILE_WRITE); - KPStringBuilder<384> header{"UTC, Formatted Time, Task Name, Valve Number, Current " + KPStringBuilder<404> header{"UTC, Formatted Time, Task Name, Valve Number, Current " "State, Config Sample Time, Config Sample " "Pressure, Config Sample Volume, Temperature Recorded," - "Pressure Recorded, Volume Recorded\n"}; + "Pressure Recorded, Volume Recorded, Flow Rate\n"}; file.println(header); file.close(); } @@ -370,9 +371,8 @@ class App : public KPController, public KPSerialInputObserver, public TaskObserv sprintf( formattedTime, "%u/%u/%u %02u:%02u:%02u GMT+0", year(utc), month(utc), day(utc), hour(utc), minute(utc), second(utc)); - Task & task = tm.tasks.at(currentTaskId); - KPStringBuilder<512> data{ + KPStringBuilder<544> data{ utc, ",", formattedTime, @@ -393,7 +393,9 @@ class App : public KPController, public KPSerialInputObserver, public TaskObserv ",", status.pressure, ",", - status.waterVolume}; + status.waterVolume, + ",", + status.waterFlow}; log.println(data); log.flush(); log.close(); @@ -407,7 +409,7 @@ class App : public KPController, public KPSerialInputObserver, public TaskObserv formattedTime, "%u/%u/%u %02u:%02u:%02u GMT+0", year(utc), month(utc), day(utc), hour(utc), minute(utc), second(utc)); NowTask & task = ntm.task; - KPStringBuilder<512> data{ + KPStringBuilder<544> data{ utc, ",", formattedTime, @@ -428,7 +430,9 @@ class App : public KPController, public KPSerialInputObserver, public TaskObserv ",", status.pressure, ",", - status.waterVolume}; + status.waterVolume, + ",", + status.waterFlow}; log.println(data); log.flush(); log.close(); @@ -448,7 +452,7 @@ class App : public KPController, public KPSerialInputObserver, public TaskObserv formattedTime, "%u/%u/%u %02u:%02u:%02u GMT+0", year(utc), month(utc), day(utc), hour(utc), minute(utc), second(utc)); - KPStringBuilder<512> data{ + KPStringBuilder<544> data{ utc, ",", formattedTime, @@ -469,7 +473,9 @@ class App : public KPController, public KPSerialInputObserver, public TaskObserv ",", status.maxPressure, ",", - status.waterVolume}; + status.waterVolume, + ",", + status.waterFlow}; log.println(data); log.flush(); log.close(); @@ -483,7 +489,7 @@ class App : public KPController, public KPSerialInputObserver, public TaskObserv formattedTime, "%u/%u/%u %02u:%02u:%02u GMT+0", year(utc), month(utc), day(utc), hour(utc), minute(utc), second(utc)); - KPStringBuilder<512> data{ + KPStringBuilder<544> data{ utc, ",", formattedTime, @@ -504,7 +510,9 @@ class App : public KPController, public KPSerialInputObserver, public TaskObserv ",", status.maxPressure, ",", - status.waterVolume}; + status.waterVolume, + ",", + status.waterFlow}; log.println(data); log.flush(); log.close(); diff --git a/src/Components/Sensor.hpp b/src/Components/Sensor.hpp index fde3c26..591429b 100644 --- a/src/Components/Sensor.hpp +++ b/src/Components/Sensor.hpp @@ -108,6 +108,9 @@ class Sensor { } // Lazy evaluation: last_update will never be initialized if the sensor is not enabled; + // last_update is initialized to millis() here in the first call, and then set below + // see link for another examples of static vars in functions: + // https://www.tutorialspoint.com/what-is-the-lifetime-of-a-static-variable-in-a-cplusplus-function static long last_update = millis(); if ((millis() - last_update) < updateInterval) { return ErrorCode::notReady; diff --git a/src/Components/Sensors/TurbineFlowSensor.cpp b/src/Components/Sensors/TurbineFlowSensor.cpp index d9657f9..ea4e796 100644 --- a/src/Components/Sensors/TurbineFlowSensor.cpp +++ b/src/Components/Sensors/TurbineFlowSensor.cpp @@ -5,7 +5,9 @@ volatile unsigned long flowIntervalMicros; volatile bool flowUpdated; void flowTick() { + //This is the time interval since the last flowTick flowIntervalMicros = micros() - lastFlowTick; lastFlowTick = micros(); + //make sure to read data flowUpdated = true; } \ No newline at end of file diff --git a/src/Components/Sensors/TurbineFlowSensor.hpp b/src/Components/Sensors/TurbineFlowSensor.hpp index 1e41920..3ac3150 100644 --- a/src/Components/Sensors/TurbineFlowSensor.hpp +++ b/src/Components/Sensors/TurbineFlowSensor.hpp @@ -21,6 +21,7 @@ class TurbineFlowSensor : public Sensor { void begin() override { lastFlowTick = micros(); pinMode(A3, INPUT); + //sensor is updated once a second setUpdateFreq(1000); } @@ -43,9 +44,15 @@ class TurbineFlowSensor : public Sensor { SensorData read() override { if (flowUpdated) { flowUpdated = false; - + //micro is 1e-6, so we divide the micros in a second with the flow interval + //to get the frequency auto hz = 1000000.0 / double(flowIntervalMicros); + //The spec sheet says that the output frequency is between 36.6 to 917 Hz + //So if hz is less than 37/36.6, then the flow is zero. Otherwise, interporlate + //between the frequency into the flow rate. + //flow that the sensor can record is between 0.1LPM and 2.5LPM lpm = hz < 37 ? 0 : interpolate(hz, 37, 917, 0.110, 2.476); + //LPM * change in minute gives volume in liters volume += lpm * (flowIntervalMicros / 60000000.0); println("Volume: ", volume, ", LPM: ", lpm); } else { diff --git a/src/States/Shared.cpp b/src/States/Shared.cpp index 99fb2d1..4ba1c03 100644 --- a/src/States/Shared.cpp +++ b/src/States/Shared.cpp @@ -51,6 +51,18 @@ namespace SharedStates { delay(1000); } + void Flush::update(KPStateMachine & sm) { + if ((unsigned long) (millis() - updateTime) < updateDelay) { + return; + } + + updateTime = millis(); + auto & app = *static_cast(sm.controller); + app.shift.setAllRegistersLow(); + app.shift.setPin(TPICDevices::FLUSH_VALVE, HIGH); + app.shift.write(); + } + void FlushVolume::enter(KPStateMachine & sm) { auto & app = *static_cast(sm.controller); app.shift.setAllRegistersLow(); @@ -85,6 +97,18 @@ namespace SharedStates { app.shift.write(); } + void FlushVolume::update(KPStateMachine & sm) { + if ((unsigned long) (millis() - updateTime) < updateDelay) { + return; + } + + updateTime = millis(); + auto & app = *static_cast(sm.controller); + app.shift.setAllRegistersLow(); + app.shift.setPin(TPICDevices::FLUSH_VALVE, HIGH); + app.shift.write(); + } + void AirFlush::enter(KPStateMachine & sm) { auto & app = *static_cast(sm.controller); app.shift.writeAllRegistersLow(); @@ -111,6 +135,18 @@ namespace SharedStates { app.shift.write(); } + void AirFlush::update(KPStateMachine & sm) { + if ((unsigned long) (millis() - updateTime) < updateDelay) { + return; + } + + updateTime = millis(); + auto & app = *static_cast(sm.controller); + app.shift.setAllRegistersLow(); + app.shift.setPin(TPICDevices::FLUSH_VALVE, HIGH); + app.shift.write(); + } + void Sample::enter(KPStateMachine & sm) { // We set the latch valve to intake mode, turn on the filter valve, then the pump auto & app = *static_cast(sm.controller); @@ -202,6 +238,19 @@ namespace SharedStates { app.shift.write(); } + void Dry::update(KPStateMachine & sm) { + if ((unsigned long) (millis() - updateTime) < updateDelay) { + return; + } + + updateTime = millis(); + auto & app = *static_cast(sm.controller); + app.shift.setAllRegistersLow(); + app.shift.setPin(TPICDevices::AIR_VALVE, HIGH); + app.shift.setPin(app.currentValveIdToPin(), HIGH); + app.shift.write(); + } + void OffshootClean::enter(KPStateMachine & sm) { auto & app = *static_cast(sm.controller); app.shift.setAllRegistersLow(); // Reset shift registers @@ -242,6 +291,19 @@ namespace SharedStates { app.shift.write(); } + void OffshootClean::update(KPStateMachine & sm){ + if ((unsigned long) (millis() - updateTime) < updateDelay) { + return; + } + + updateTime = millis(); + auto & app = *static_cast(sm.controller); + app.shift.setAllRegistersLow(); + app.shift.setPin(app.currentValveIdToPin(), HIGH); + app.shift.setPin(TPICDevices::FLUSH_VALVE, HIGH); + app.shift.write(); + } + void OffshootPreload::enter(KPStateMachine & sm) { // Intake valve is opened and the motor is runnning ... // Turnoff only the flush valve @@ -362,4 +424,44 @@ namespace SharedStates { app.shift.setPin(TPICDevices::FLUSH_VALVE, HIGH); app.shift.write(); } + + + void Preserve::update(KPStateMachine & sm){ + if ((unsigned long) (millis() - updateTime) < updateDelay) { + return; + } + + updateTime = millis(); + auto & app = *static_cast(sm.controller); + app.shift.setAllRegistersLow(); + app.shift.setPin(TPICDevices::ALCHOHOL_VALVE, HIGH); + app.shift.setPin(app.currentValveIdToPin(), HIGH); + app.shift.write(); + } + + void AlcoholPurge::enter(KPStateMachine & sm) { + auto & app = *static_cast(sm.controller); + app.shift.writeAllRegistersLow(); + app.intake.off(); + app.shift.setPin(TPICDevices::ALCHOHOL_VALVE, HIGH); + app.shift.setPin(TPICDevices::FLUSH_VALVE, HIGH); + app.shift.write(); + app.pump.on(); + + setTimeCondition(time, [&]() { sm.next(); }); + } + + void AlcoholPurge::update(KPStateMachine & sm){ + if ((unsigned long) (millis() - updateTime) < updateDelay) { + return; + } + + updateTime = millis(); + auto & app = *static_cast(sm.controller); + app.shift.setAllRegistersLow(); + app.shift.setPin(TPICDevices::ALCHOHOL_VALVE, HIGH); + app.shift.setPin(TPICDevices::FLUSH_VALVE, HIGH); + app.shift.write(); + } + } // namespace SharedStates \ No newline at end of file From beb43793bbc0811c22d54c91279b3c488eefae91 Mon Sep 17 00:00:00 2001 From: Nathan Jesudason Date: Fri, 11 Feb 2022 13:51:04 -0800 Subject: [PATCH 52/61] remove duplicate functions from merge --- src/States/Shared.cpp | 100 ------------------------------------------ 1 file changed, 100 deletions(-) diff --git a/src/States/Shared.cpp b/src/States/Shared.cpp index 4ba1c03..cbe0bd4 100644 --- a/src/States/Shared.cpp +++ b/src/States/Shared.cpp @@ -51,18 +51,6 @@ namespace SharedStates { delay(1000); } - void Flush::update(KPStateMachine & sm) { - if ((unsigned long) (millis() - updateTime) < updateDelay) { - return; - } - - updateTime = millis(); - auto & app = *static_cast(sm.controller); - app.shift.setAllRegistersLow(); - app.shift.setPin(TPICDevices::FLUSH_VALVE, HIGH); - app.shift.write(); - } - void FlushVolume::enter(KPStateMachine & sm) { auto & app = *static_cast(sm.controller); app.shift.setAllRegistersLow(); @@ -97,17 +85,6 @@ namespace SharedStates { app.shift.write(); } - void FlushVolume::update(KPStateMachine & sm) { - if ((unsigned long) (millis() - updateTime) < updateDelay) { - return; - } - - updateTime = millis(); - auto & app = *static_cast(sm.controller); - app.shift.setAllRegistersLow(); - app.shift.setPin(TPICDevices::FLUSH_VALVE, HIGH); - app.shift.write(); - } void AirFlush::enter(KPStateMachine & sm) { auto & app = *static_cast(sm.controller); @@ -135,18 +112,6 @@ namespace SharedStates { app.shift.write(); } - void AirFlush::update(KPStateMachine & sm) { - if ((unsigned long) (millis() - updateTime) < updateDelay) { - return; - } - - updateTime = millis(); - auto & app = *static_cast(sm.controller); - app.shift.setAllRegistersLow(); - app.shift.setPin(TPICDevices::FLUSH_VALVE, HIGH); - app.shift.write(); - } - void Sample::enter(KPStateMachine & sm) { // We set the latch valve to intake mode, turn on the filter valve, then the pump auto & app = *static_cast(sm.controller); @@ -238,19 +203,6 @@ namespace SharedStates { app.shift.write(); } - void Dry::update(KPStateMachine & sm) { - if ((unsigned long) (millis() - updateTime) < updateDelay) { - return; - } - - updateTime = millis(); - auto & app = *static_cast(sm.controller); - app.shift.setAllRegistersLow(); - app.shift.setPin(TPICDevices::AIR_VALVE, HIGH); - app.shift.setPin(app.currentValveIdToPin(), HIGH); - app.shift.write(); - } - void OffshootClean::enter(KPStateMachine & sm) { auto & app = *static_cast(sm.controller); app.shift.setAllRegistersLow(); // Reset shift registers @@ -291,19 +243,6 @@ namespace SharedStates { app.shift.write(); } - void OffshootClean::update(KPStateMachine & sm){ - if ((unsigned long) (millis() - updateTime) < updateDelay) { - return; - } - - updateTime = millis(); - auto & app = *static_cast(sm.controller); - app.shift.setAllRegistersLow(); - app.shift.setPin(app.currentValveIdToPin(), HIGH); - app.shift.setPin(TPICDevices::FLUSH_VALVE, HIGH); - app.shift.write(); - } - void OffshootPreload::enter(KPStateMachine & sm) { // Intake valve is opened and the motor is runnning ... // Turnoff only the flush valve @@ -425,43 +364,4 @@ namespace SharedStates { app.shift.write(); } - - void Preserve::update(KPStateMachine & sm){ - if ((unsigned long) (millis() - updateTime) < updateDelay) { - return; - } - - updateTime = millis(); - auto & app = *static_cast(sm.controller); - app.shift.setAllRegistersLow(); - app.shift.setPin(TPICDevices::ALCHOHOL_VALVE, HIGH); - app.shift.setPin(app.currentValveIdToPin(), HIGH); - app.shift.write(); - } - - void AlcoholPurge::enter(KPStateMachine & sm) { - auto & app = *static_cast(sm.controller); - app.shift.writeAllRegistersLow(); - app.intake.off(); - app.shift.setPin(TPICDevices::ALCHOHOL_VALVE, HIGH); - app.shift.setPin(TPICDevices::FLUSH_VALVE, HIGH); - app.shift.write(); - app.pump.on(); - - setTimeCondition(time, [&]() { sm.next(); }); - } - - void AlcoholPurge::update(KPStateMachine & sm){ - if ((unsigned long) (millis() - updateTime) < updateDelay) { - return; - } - - updateTime = millis(); - auto & app = *static_cast(sm.controller); - app.shift.setAllRegistersLow(); - app.shift.setPin(TPICDevices::ALCHOHOL_VALVE, HIGH); - app.shift.setPin(TPICDevices::FLUSH_VALVE, HIGH); - app.shift.write(); - } - } // namespace SharedStates \ No newline at end of file From 11e3c8220f8177b5acc8640d1debd7984c4c51da Mon Sep 17 00:00:00 2001 From: Nathan Jesudason Date: Fri, 4 Mar 2022 13:50:15 -0800 Subject: [PATCH 53/61] removed main/new task controllers (#33) --- src/API/API.cpp | 2 +- src/Application/App-wifi.cpp | 2 +- src/Application/App.hpp | 16 ++--- src/StateControllers/MainStateController.cpp | 25 ------- src/StateControllers/MainStateController.hpp | 69 ------------------- ...Controller.cpp => TaskStateController.cpp} | 10 ++- ...Controller.hpp => TaskStateController.hpp} | 3 +- src/States/Shared.cpp | 15 +++- src/Task/Task.hpp | 6 +- 9 files changed, 32 insertions(+), 116 deletions(-) delete mode 100644 src/StateControllers/MainStateController.cpp delete mode 100644 src/StateControllers/MainStateController.hpp rename src/StateControllers/{NewStateController.cpp => TaskStateController.cpp} (83%) rename src/StateControllers/{NewStateController.hpp => TaskStateController.hpp} (95%) diff --git a/src/API/API.cpp b/src/API/API.cpp index e2b175c..489ee9f 100644 --- a/src/API/API.cpp +++ b/src/API/API.cpp @@ -160,7 +160,7 @@ namespace API { Task & task = app.tm.tasks[id]; if (app.currentTaskId == task.id) { app.invalidateTaskAndFreeUpValves(task); - app.newStateController.stop(); + app.taskStateController.stop(); } else { app.invalidateTaskAndFreeUpValves(task); app.tm.writeToDirectory(); diff --git a/src/Application/App-wifi.cpp b/src/Application/App-wifi.cpp index 3aa08cc..d8c880a 100644 --- a/src/Application/App-wifi.cpp +++ b/src/Application/App-wifi.cpp @@ -159,7 +159,7 @@ void App::setupServerRouting() { // ──────────────────────────────────────────────────────────────────────────────── // Emergency stop // ──────────────────────────────────────────────────────────────────────────────── - server.get("/stop", [this](Request & req, Response & res) { newStateController.stop(); }); + server.get("/stop", [this](Request & req, Response & res) { taskStateController.stop(); }); server.get("/api/valves/reset", [this](Request & req, Response & res) { for (int i = 0; i < config.numberOfValves; i++) { diff --git a/src/Application/App.hpp b/src/Application/App.hpp index 581a250..7391212 100755 --- a/src/Application/App.hpp +++ b/src/Application/App.hpp @@ -17,7 +17,7 @@ #include #include -#include +#include #include #include @@ -66,7 +66,7 @@ class App : public KPController, public KPSerialInputObserver, public TaskObserv Status status; // MainStateController sm; - NewStateController newStateController; + TaskStateController taskStateController; HyperFlushStateController hyperFlushStateController; DebubbleStateController debubbleStateController; @@ -201,9 +201,9 @@ class App : public KPController, public KPSerialInputObserver, public TaskObserv // ─── NEW STATE CONTROLLER ──────────────────────────────────────── // - addComponent(newStateController); - newStateController.addObserver(status); - newStateController.idle(); // Wait in IDLE + addComponent(taskStateController); + taskStateController.addObserver(status); + taskStateController.idle(); // Wait in IDLE // Print WiFi status if (server.enabled()) { @@ -447,7 +447,7 @@ class App : public KPController, public KPSerialInputObserver, public TaskObserv if (shouldStopCurrentTask) { cancel("delayTaskExecution"); // if (status.currentStateName != HyperFlush::STOP) { - // newStateController.stop(); + // taskStateController.stop(); // } continue; @@ -472,10 +472,10 @@ class App : public KPController, public KPSerialInputObserver, public TaskObserv TimedAction delayTaskExecution; delayTaskExecution.name = "delayTaskExecution"; delayTaskExecution.interval = secsToMillis(timeUntil); - delayTaskExecution.callback = [this]() { newStateController.begin(); }; + delayTaskExecution.callback = [this]() { taskStateController.begin(); }; run(delayTaskExecution); // async, will be execute later - newStateController.configure(task); + taskStateController.configure(task); currentTaskId = id; status.preventShutdown = true; diff --git a/src/StateControllers/MainStateController.cpp b/src/StateControllers/MainStateController.cpp deleted file mode 100644 index 38bda04..0000000 --- a/src/StateControllers/MainStateController.cpp +++ /dev/null @@ -1,25 +0,0 @@ -#include -#include - -void Main::Idle::enter(KPStateMachine & sm) { - auto & app = *static_cast(sm.controller); - println(app.scheduleNextActiveTask().description()); -}; - -void Main::Stop::enter(KPStateMachine & sm) { - auto & app = *static_cast(sm.controller); - app.pump.off(); - app.shift.writeAllRegistersLow(); - app.intake.off(); - - app.vm.setValveStatus(app.status.currentValve, ValveStatus::sampled); - app.vm.writeToDirectory(); - - auto currentTaskId = app.currentTaskId; - app.tm.advanceTask(currentTaskId); - app.tm.writeToDirectory(); - - app.currentTaskId = 0; - app.status.currentValve = -1; - sm.next(); -} diff --git a/src/StateControllers/MainStateController.hpp b/src/StateControllers/MainStateController.hpp deleted file mode 100644 index a27e220..0000000 --- a/src/StateControllers/MainStateController.hpp +++ /dev/null @@ -1,69 +0,0 @@ -#pragma once -#include -#include - -namespace Main { - STATE(STOP); - STATE(IDLE); - STATE(PRESERVE); - STATE(DRY); - STATE(SAMPLE); - STATE(FLUSH); - - /** - * This state checks if there is an active task and schedule it if any. - * [Connections: 0] - */ - class Idle : public KPState { - public: - void enter(KPStateMachine & sm) override; - }; - - /** - * Turn everything off and make sure that anything changes to the valve manager and task - * manager are committed to persistent store. - * [Connections: 1] - */ - class Stop : public KPState { - public: - void enter(KPStateMachine & sm) override; - }; - - struct Config { - decltype(SharedStates::Flush::time) flushTime; - decltype(SharedStates::Sample::time) sampleTime; - decltype(SharedStates::Sample::pressure) samplePressure; - decltype(SharedStates::Sample::volume) sampleVolume; - decltype(SharedStates::Dry::time) dryTime; - decltype(SharedStates::Preserve::time) preserveTime; - }; - - class Controller : public StateController, public StateControllerConfig { - public: - Controller() : StateController("main-state-machine") {} - - // FLUSH -> SAMPLE -> DRY -> PRESERVE -> STOP -> IDLE - void setup() override { - registerState(SharedStates::Flush(), FLUSH, SAMPLE); - registerState(SharedStates::Sample(), SAMPLE, DRY); - registerState(SharedStates::Dry(), DRY, PRESERVE); - registerState(SharedStates::Preserve(), PRESERVE, STOP); - registerState(Stop(), STOP, IDLE); - registerState(Idle(), IDLE); - } - - void begin() override { - transitionTo(FLUSH); - } - - void stop() override { - transitionTo(STOP); - } - - void idle() override { - transitionTo(IDLE); - } - }; -}; // namespace Main - -using MainStateController = Main::Controller; diff --git a/src/StateControllers/NewStateController.cpp b/src/StateControllers/TaskStateController.cpp similarity index 83% rename from src/StateControllers/NewStateController.cpp rename to src/StateControllers/TaskStateController.cpp index b30ecbc..fb27934 100644 --- a/src/StateControllers/NewStateController.cpp +++ b/src/StateControllers/TaskStateController.cpp @@ -1,6 +1,6 @@ -#include +#include #include -void NewStateController::setup() { +void TaskStateController::setup() { // registerState(SharedStates::Flush(), FLUSH1, [this](int code) { // switch (code) { // case 0: @@ -30,8 +30,6 @@ void NewStateController::setup() { registerState(SharedStates::Dry(), DRY, PRESERVE); registerState(SharedStates::Preserve(), PRESERVE, AIR_FLUSH); registerState(SharedStates::AirFlush(), AIR_FLUSH, STOP); - - // Reusing STOP and IDLE states from MainStateController - registerState(Main::Stop(), STOP, IDLE); - registerState(Main::Idle(), IDLE); + registerState(SharedStates::Stop(), STOP, IDLE); + registerState(SharedStates::Idle(), IDLE); }; \ No newline at end of file diff --git a/src/StateControllers/NewStateController.hpp b/src/StateControllers/TaskStateController.hpp similarity index 95% rename from src/StateControllers/NewStateController.hpp rename to src/StateControllers/TaskStateController.hpp index a3a942c..7c0d8b2 100644 --- a/src/StateControllers/NewStateController.hpp +++ b/src/StateControllers/TaskStateController.hpp @@ -1,7 +1,6 @@ #pragma once #include #include -#include namespace New { STATE(IDLE); @@ -67,4 +66,4 @@ namespace New { }; }; // namespace New -using NewStateController = New::Controller; +using TaskStateController = New::Controller; diff --git a/src/States/Shared.cpp b/src/States/Shared.cpp index a45fdf8..45fb606 100644 --- a/src/States/Shared.cpp +++ b/src/States/Shared.cpp @@ -2,13 +2,26 @@ #include namespace SharedStates { - void Idle::enter(KPStateMachine & sm) {} + void Idle::enter(KPStateMachine & sm) { + auto & app = *static_cast(sm.controller); + println(app.scheduleNextActiveTask().description()); + } void Stop::enter(KPStateMachine & sm) { auto & app = *static_cast(sm.controller); app.pump.off(); app.shift.writeAllRegistersLow(); app.intake.off(); + + app.vm.setValveStatus(app.status.currentValve, ValveStatus::sampled); + app.vm.writeToDirectory(); + + auto currentTaskId = app.currentTaskId; + app.tm.advanceTask(currentTaskId); + app.tm.writeToDirectory(); + + app.currentTaskId = 0; + app.status.currentValve = -1; sm.next(); } diff --git a/src/Task/Task.hpp b/src/Task/Task.hpp index 0c10be5..2cd93c0 100755 --- a/src/Task/Task.hpp +++ b/src/Task/Task.hpp @@ -9,12 +9,12 @@ #include #include -#include +#include struct Task : public JsonEncodable, public JsonDecodable, public Printable, - public NewStateController::Configurator { + public TaskStateController::Configurator { public: friend class TaskManager; @@ -155,7 +155,7 @@ struct Task : public JsonEncodable, } #pragma endregion - void operator()(NewStateController::Config & config) const { + void operator()(TaskStateController::Config & config) const { config.flushTime = flushTime; config.sampleTime = sampleTime; config.samplePressure = samplePressure; From cce2b468cd0e00e6d88996e6c2326819a398edf7 Mon Sep 17 00:00:00 2001 From: Nathan Jesudason Date: Fri, 4 Mar 2022 14:51:55 -0800 Subject: [PATCH 54/61] Revert "implemented component test version" This reverts commit 62610dddbf868e57636c51a7d83bef775c3b88cc. --- platformio.ini | 7 +-- src/Application/App.hpp | 101 +--------------------------------- src/Application/Constants.hpp | 2 +- 3 files changed, 4 insertions(+), 106 deletions(-) diff --git a/platformio.ini b/platformio.ini index 3885c62..f5865ee 100644 --- a/platformio.ini +++ b/platformio.ini @@ -37,9 +37,4 @@ build_flags = -D DEBUG=1 -Wall -Wno-unknown-pragmas -std=c++14 ; [env:live] ; build_unflags = -std=gnu++11 -; build_flags = -D LIVE=1 -Wall -Wno-unknown-pragmas -std=c++14 - -; [env:component_test] -; build_unflags = -std=gnu++11 -; build_flags = -D COMPONENT_TEST=1 -Wall -Wno-unknown-pragmas -std=c++14 -; monitor_filters = send_on_enter \ No newline at end of file +; build_flags = -D LIVE=1 -Wall -Wno-unknown-pragmas -std=c++14 \ No newline at end of file diff --git a/src/Application/App.hpp b/src/Application/App.hpp index 7391212..d5fe0fb 100755 --- a/src/Application/App.hpp +++ b/src/Application/App.hpp @@ -38,7 +38,6 @@ class App : public KPController, public KPSerialInputObserver, public TaskObserv void setupAPI(); void setupSerialRouting(); void setupServerRouting(); -// void testValve(int v); void commandReceived(const char * line, size_t size) override; public: @@ -86,34 +85,13 @@ class App : public KPController, public KPSerialInputObserver, public TaskObserv return "Application-Task Observer"; } - - void testValve(int v, const char * name) { - println("========="); - print("Testing valve: "); - print(name); - println(); - shift.setAllRegistersLow(); - shift.writePin(v + shift.capacityPerRegister, HIGH); - shift.write(); - println("press any key to continue: "); - while (!Serial.available()) { - yield(); - } - while(Serial.available() > 0){ - Serial.read(); - } - delay(20); - } - public: void setup() override { KPSerialInput::sharedInstance().addObserver(this); Serial.begin(115200); -#if defined(DEBUG) || defined(COMPONENT_TEST) - while (!Serial) {}; -#endif #ifdef DEBUG + while (!Serial) {}; println(); println(BLUE("==================================================")); println(BLUE(" DEBUG MODE")); @@ -242,83 +220,9 @@ class App : public KPController, public KPSerialInputObserver, public TaskObserv }); runForever(1000, "detailLog", [&]() { logDetail("detail.csv"); }); -#if defined(DEBUG) || defined(COMPONENT_TEST) +#ifdef DEBUG runForever(2000, "memLog", [&]() { printFreeRam(); }); #endif - -#ifdef COMPONENT_TEST - println(); - println(BLUE("=================== RUNNING COMPONENT TEST ==================")); - - println("Press any key to start: "); - while (!Serial.available()) { - yield(); - } - while(Serial.available() > 0){ - Serial.read(); - } - char buffer [50]; - for(int i = 0; i < 24; i++){ - std::sprintf(buffer, "%d", i); - testValve(i, buffer); - } - - testValve(TPICDevices::AIR_VALVE, "Air valve"); - testValve(TPICDevices::FLUSH_VALVE, "Flush valve"); - testValve(TPICDevices::ALCHOHOL_VALVE, "Alcohol valve"); - - println("Testing ball valve"); - - shift.writeAllRegistersLow(); - intake.on(); - println("press any key to continue: "); - while (!Serial.available()) { - yield(); - } - while(Serial.available() > 0){ - Serial.read(); - } - intake.off(); - - println(); - println("Testing pump..."); - shift.writeAllRegistersLow(); - shift.setPin(TPICDevices::AIR_VALVE, HIGH); - shift.setPin(TPICDevices::FLUSH_VALVE, HIGH); - shift.write(); - pump.on(); - delay(3000); - pump.off(); - delay(3000); - pump.on(Direction::reverse); - delay(3000); - pump.off(); - println("press any key to continue: "); - while (!Serial.available()) { - yield(); - } - while(Serial.available() > 0){ - Serial.read(); - } - - if(sensors.pressure.enabled){ - println("Pressure sensor detected"); - } else { - println(RED("Pressure sensor not detected")); - } - if(sensors.baro1.enabled){ - println("Baro1 sensor detected"); - }else{ - println(RED("Baro1 sensor not detected")); - } - if(sensors.baro2.enabled){ - println("Baro2 sensor detected"); - }else{ - println(RED("Baro2 sensor not detected")); - } - - println(BLUE("=================== COMPONENT TEST COMPLETE ==================")); -#endif } void logDetail(const char * filename) { @@ -553,7 +457,6 @@ class App : public KPController, public KPSerialInputObserver, public TaskObserv if (!status.isProgrammingMode() && !status.preventShutdown) { shutdown(); } - } /** ──────────────────────────────────────────────────────────────────────────── diff --git a/src/Application/Constants.hpp b/src/Application/Constants.hpp index e9cadcd..f7c0acd 100755 --- a/src/Application/Constants.hpp +++ b/src/Application/Constants.hpp @@ -25,7 +25,7 @@ namespace TPICDevices { // turn on namespace HardwarePins { __k_auto POWER_MODULE = A0; -#if defined(LIVE) || defined(COMPONENT_TEST) +#ifdef LIVE // A1 is eDNA, 12 for HYPNOS __k_auto RTC_INTERRUPT = A1; #else From e2eaf7c7554c8434e1f0a689603409bcf9d52b6a Mon Sep 17 00:00:00 2001 From: Nathan Jesudason Date: Wed, 16 Mar 2022 15:05:58 -0700 Subject: [PATCH 55/61] Feature component test (#34) * implemented serial monitor component test version --- platformio.ini | 7 +- src/Application/App-serial.cpp | 193 +++++++++++------- src/Application/App.hpp | 9 +- src/Application/Constants.hpp | 2 +- src/Components/StateController.hpp | 1 + .../HyperFlushStateController.cpp | 19 ++ .../HyperFlushStateController.hpp | 9 +- src/States/Shared.cpp | 2 + 8 files changed, 157 insertions(+), 85 deletions(-) create mode 100644 src/StateControllers/HyperFlushStateController.cpp diff --git a/platformio.ini b/platformio.ini index f5865ee..3885c62 100644 --- a/platformio.ini +++ b/platformio.ini @@ -37,4 +37,9 @@ build_flags = -D DEBUG=1 -Wall -Wno-unknown-pragmas -std=c++14 ; [env:live] ; build_unflags = -std=gnu++11 -; build_flags = -D LIVE=1 -Wall -Wno-unknown-pragmas -std=c++14 \ No newline at end of file +; build_flags = -D LIVE=1 -Wall -Wno-unknown-pragmas -std=c++14 + +; [env:component_test] +; build_unflags = -std=gnu++11 +; build_flags = -D COMPONENT_TEST=1 -Wall -Wno-unknown-pragmas -std=c++14 +; monitor_filters = send_on_enter \ No newline at end of file diff --git a/src/Application/App-serial.cpp b/src/Application/App-serial.cpp index 9adedd8..0dc58bc 100644 --- a/src/Application/App-serial.cpp +++ b/src/Application/App-serial.cpp @@ -2,6 +2,12 @@ #include namespace { + + using func = std::function args)>; + struct func_args { + func function; + unsigned short n_args; + }; struct SerialRequest { const char * cmd; const JsonArray & path; @@ -9,7 +15,7 @@ namespace { }; using FunctionType = std::function; - using StringToFunc = std::unordered_map; + using StringToFunc = std::unordered_map; StringToFunc mapNameToCallback; constexpr const char EOT = '\4'; @@ -17,22 +23,80 @@ namespace { void endTransmission() { print(EOT); } + + // https://stackoverflow.com/questions/4654636/how-to-determine-if-a-string-is-a-number-with-c + bool is_number(const std::string& s) + { + std::string::const_iterator it = s.begin(); + while (it != s.end() && std::isdigit(*it)) ++it; + return !s.empty() && it == s.end(); + } + + int string_to_tpic(const std::string& s){ + if(s == "air"){ + return TPICDevices::AIR_VALVE; + } + if(s == "alcohol"){ + return TPICDevices::ALCHOHOL_VALVE; + } + if(s == "flush"){ + return TPICDevices::FLUSH_VALVE; + } + return -1; + } } // namespace // Registering Top-level serial commands void App::setupSerialRouting() { - mapNameToCallback["hyperflush"] = [this](SerialRequest req) { - const auto response = dispatchAPI(); - serializeJson(response, Serial); + mapNameToCallback["open"] = [this](std::vector args) { + //assume that it is a list of valveIDs + if(is_number(args[1])){ + for(unsigned int i = 1; i < args.size(); i++){ + shift.setPin( std::stoi(args[i]) + shift.capacityPerRegister, HIGH); + } + } else { + if(string_to_tpic(args[1]) != -1){ + shift.setPin(string_to_tpic(args[1]), HIGH); + } + } + shift.write(); }; - mapNameToCallback["debubble"] = [this](SerialRequest req) { - const auto response = dispatchAPI(); - serializeJson(response, Serial); + mapNameToCallback["close"] = [this](std::vector args) { + if(is_number(args[1])){ + for(unsigned int i = 1; i < args.size(); i++){ + shift.setPin( std::stoi(args[i]) + shift.capacityPerRegister, LOW); + } + } else { + if(string_to_tpic(args[1]) != -1){ + shift.setPin(string_to_tpic(args[1]), LOW); + } + } + shift.write(); + }; + + mapNameToCallback["pump"] = [this](std::vector args) { + const char * endpoint = args[1].c_str(); + if (strcmp(endpoint, "on") == 0) { + pump.on(); + } + if (strcmp(endpoint, "off") == 0) { + pump.off(); + } + }; + + mapNameToCallback["intake"] = [this](std::vector args){ + const char * endpoint = args[1].c_str(); + if(strcmp(endpoint, "on") == 0){ + intake.on(); + } + if(strcmp(endpoint, "off") == 0){ + intake.off(); + } }; - mapNameToCallback["query"] = [this](SerialRequest req) { - const char * endpoint = req.path[1]; + mapNameToCallback["query"] = [this](std::vector args) { + const char * endpoint = args[1].c_str(); if (strcmp(endpoint, "status") == 0) { const auto & response = dispatchAPI(); serializeJson(response, Serial); @@ -52,10 +116,40 @@ void App::setupSerialRouting() { endTransmission(); return; } + + if(strcmp(endpoint, "time") == 0) { + power.printCurrentTime(); + return; + } + + if (strcmp(endpoint, "sensors") == 0) { + if(sensors.pressure.enabled){ + println("Pressure sensor detected"); + } else { + println(RED("Pressure sensor not detected")); + } + if(sensors.baro1.enabled){ + println("Baro1 sensor detected"); + }else{ + println(RED("Baro1 sensor not detected")); + } + if(sensors.baro2.enabled){ + println("Baro2 sensor detected"); + }else{ + println(RED("Baro2 sensor not detected")); + } + return; + } + }; + + mapNameToCallback["alarm"] = [this](std::vector args) { + long time = std::labs(std::stol(args[1])); + power.scheduleNextAlarm(time + now()); + return; }; - mapNameToCallback["reset"] = [this](SerialRequest req) { - const char * endpoint = req.path[1]; + mapNameToCallback["reset"] = [this](std::vector args) { + const char * endpoint = args[1].c_str(); if (strcmp(endpoint, "valves") == 0) { for (int i = 0; i < config.numberOfValves; i++) { vm.setValveStatus(i, ValveStatus::Code(config.valves[i])); @@ -82,70 +176,23 @@ void App::setupSerialRouting() { */ void App::commandReceived(const char * msg, size_t size) { - if (msg[0] == '{') { - StaticJsonDocument<512> input; - deserializeJson(input, msg); - for (auto & item : mapNameToCallback) { - if (item.first == input["cmd"].as()) { - item.second({input["cmd"], input["path"], input}); + std::vector args; + char str[80]; + strcpy(str, msg); + println("Received: ", str); + const char delim[2] = " "; + const char * tok = strtok(str, delim); + int i = 0; + while (tok != NULL) { + args.push_back(tok); + tok = strtok(NULL, delim); + ++i; + } + for(auto & command : mapNameToCallback) { + if(command.first == args[0]){ + command.second({args}); break; } } - } - - // if (strcmp(msg, "flow temp")) { - // // auto response = FlowSensor::read(); - // } - - // if (strcmp(msg, "flow flow")) {} - // KPString line{msg}; - - // println(line); - - // if (msg[0] == '{') { - // StaticJsonDocument<255> doc; - // deserializeJson(doc, msg); - - // StaticJsonDocument<255> response; - // // dispatch(doc["cmd"].as(), doc, response); - // } - - if (strcmp(msg, "status")) { - println(status); - } - - // if (line == "print config") { - // println(config); - // } - - // if (line == "schedule now") { - // println("Schduling temp task"); - // Task task = tm.createTask(); - // task.schedule = now() + 5; - // task.flushTime = 5; - // task.sampleTime = 5; - // task.deleteOnCompletion = true; - // task.status = TaskStatus::active; - // task.valves.push_back(0); - // tm.insertTask(task); - // println(scheduleNextActiveTask().description()); - // } - - if (strcmp(msg, "reset valves") == 0) { - for (int i = 0; i < config.numberOfValves; i++) { - vm.setValveStatus(i, ValveStatus::Code(config.valves[i])); - } - } - - // vm.writeToDirectory(); - // } - - // if (line == "mem") { - // println(free_ram()); - // } - - // if (line == "hyperflush") { - // beginHyperFlush(); - // }; } \ No newline at end of file diff --git a/src/Application/App.hpp b/src/Application/App.hpp index d5fe0fb..bc679e2 100755 --- a/src/Application/App.hpp +++ b/src/Application/App.hpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include @@ -90,8 +91,10 @@ class App : public KPController, public KPSerialInputObserver, public TaskObserv KPSerialInput::sharedInstance().addObserver(this); Serial.begin(115200); -#ifdef DEBUG +#if defined(DEBUG) || defined(COMPONENT_TEST) while (!Serial) {}; +#endif +#ifdef DEBUG println(); println(BLUE("==================================================")); println(BLUE(" DEBUG MODE")); @@ -220,7 +223,7 @@ class App : public KPController, public KPSerialInputObserver, public TaskObserv }); runForever(1000, "detailLog", [&]() { logDetail("detail.csv"); }); -#ifdef DEBUG +#if defined(DEBUG) runForever(2000, "memLog", [&]() { printFreeRam(); }); #endif } @@ -372,7 +375,6 @@ class App : public KPController, public KPSerialInputObserver, public TaskObserv // Wake up between 10 secs of the actual schedule time // Prepare an action to execute at exact time const auto timeUntil = task.schedule - time_now; - TimedAction delayTaskExecution; delayTaskExecution.name = "delayTaskExecution"; delayTaskExecution.interval = secsToMillis(timeUntil); @@ -457,6 +459,7 @@ class App : public KPController, public KPSerialInputObserver, public TaskObserv if (!status.isProgrammingMode() && !status.preventShutdown) { shutdown(); } + } /** ──────────────────────────────────────────────────────────────────────────── diff --git a/src/Application/Constants.hpp b/src/Application/Constants.hpp index f7c0acd..e9cadcd 100755 --- a/src/Application/Constants.hpp +++ b/src/Application/Constants.hpp @@ -25,7 +25,7 @@ namespace TPICDevices { // turn on namespace HardwarePins { __k_auto POWER_MODULE = A0; -#ifdef LIVE +#if defined(LIVE) || defined(COMPONENT_TEST) // A1 is eDNA, 12 for HYPNOS __k_auto RTC_INTERRUPT = A1; #else diff --git a/src/Components/StateController.hpp b/src/Components/StateController.hpp index e15639e..a736a03 100644 --- a/src/Components/StateController.hpp +++ b/src/Components/StateController.hpp @@ -4,6 +4,7 @@ #include #include + #define STATE(x) constexpr const char * x = #x "_STATE" template diff --git a/src/StateControllers/HyperFlushStateController.cpp b/src/StateControllers/HyperFlushStateController.cpp new file mode 100644 index 0000000..ff4e157 --- /dev/null +++ b/src/StateControllers/HyperFlushStateController.cpp @@ -0,0 +1,19 @@ +#include +#include +void HyperFlushStateController::setup() { + // FLUSH -> OFFSHOOT_PRELOAD -> STOP -> IDLE + registerState(SharedStates::Flush(), FLUSH, OFFSHOOT_PRELOAD); + registerState(SharedStates::OffshootPreload(), OFFSHOOT_PRELOAD, [this](int code) { + auto & app = *static_cast(controller); + app.sensors.flow.stopMeasurement(); + + switch (code) { + case 0: + return transitionTo(STOP); + default: + halt(TRACE, "Unhandled state transition: ", code); + } + }); + registerState(SharedStates::Stop(), STOP, IDLE); + registerState(SharedStates::Idle(), IDLE); +}; \ No newline at end of file diff --git a/src/StateControllers/HyperFlushStateController.hpp b/src/StateControllers/HyperFlushStateController.hpp index d648819..6f770a4 100644 --- a/src/StateControllers/HyperFlushStateController.hpp +++ b/src/StateControllers/HyperFlushStateController.hpp @@ -17,13 +17,8 @@ namespace HyperFlush { public: Controller() : StateControllerWithConfig("hyperflush-state-machine") {} - // FLUSH -> OFFSHOOT_PRELOAD -> STOP -> IDLE - void setup() override { - registerState(SharedStates::Flush(), FLUSH, OFFSHOOT_PRELOAD); - registerState(SharedStates::OffshootPreload(), OFFSHOOT_PRELOAD, STOP); - registerState(SharedStates::Stop(), STOP, IDLE); - registerState(SharedStates::Idle(), IDLE); - } + void setup(); + void begin() override { decltype(auto) flush = getState(FLUSH); diff --git a/src/States/Shared.cpp b/src/States/Shared.cpp index 45fb606..0880fe5 100644 --- a/src/States/Shared.cpp +++ b/src/States/Shared.cpp @@ -200,6 +200,8 @@ namespace SharedStates { // Turnoff only the flush valve auto & app = *static_cast(sm.controller); app.shift.setPin(TPICDevices::FLUSH_VALVE, LOW); + app.sensors.flow.resetVolume(); + app.sensors.flow.startMeasurement(); app.intake.on(); // Reserving space ahead of time for performance From f72f6a7c7cf7bae88f4ee8d95668892d72e2efed Mon Sep 17 00:00:00 2001 From: Nathan Jesudason Date: Wed, 16 Mar 2022 15:11:39 -0700 Subject: [PATCH 56/61] add comments to state code --- src/States/Shared.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/States/Shared.cpp b/src/States/Shared.cpp index cbe0bd4..64ca11f 100644 --- a/src/States/Shared.cpp +++ b/src/States/Shared.cpp @@ -16,11 +16,12 @@ namespace SharedStates { auto & app = *static_cast(sm.controller); app.shift.setAllRegistersLow(); app.intake.on(); + // 5 seconds needed to turn ball intake on setTimeCondition(5, [&app](){ app.shift.setPin(TPICDevices::FLUSH_VALVE, HIGH); app.shift.write(); }); - + //wait 1 second after valve opens before during on pump setTimeCondition(6, [&app](){ app.pump.on(); }); @@ -31,9 +32,12 @@ namespace SharedStates { } void Flush::update(KPStateMachine & sm) { + //don't update valve status during first 5 seconds + //because valve isn't supposed to be on if(timeSinceLastTransition() < 5000){ return; } + // returns if it has already updated in the last second if ((unsigned long) (millis() - updateTime) < updateDelay) { return; } From e15fefb015910365505daf623150b0534b6ed1e5 Mon Sep 17 00:00:00 2001 From: Nathan Jesudason Date: Thu, 7 Jul 2022 17:28:03 -0700 Subject: [PATCH 57/61] Force RTC version to GitHub and Version 1.3.0 (#40) --- platformio.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platformio.ini b/platformio.ini index b72c6e0..4f5e6cb 100644 --- a/platformio.ini +++ b/platformio.ini @@ -24,7 +24,7 @@ lib_deps = WiFi101@~0.16.0 Low-Power@~1.6 868@~1.2.4 - DS3232RTC + https://github.com/JChristensen/DS3232RTC#1.3.0 [env:debug] build_unflags = -std=gnu++11 From 4246b8b72dc62419e1dc0d3866ddc57d911086b8 Mon Sep 17 00:00:00 2001 From: Nathan Jesudason Date: Tue, 17 Jan 2023 14:29:31 -0800 Subject: [PATCH 58/61] Update issue templates Remind programmers to list acceptance criteria for feature requests. --- .github/ISSUE_TEMPLATE/feature_request.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md index 62ef820..97a1ee6 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -16,5 +16,8 @@ A clear and concise description of why this feature would be useful. **Describe alternatives you've considered** A clear and concise description of any alternative solutions or features you've considered. +**Acceptance Criteria** +A clear, ideally quantitative, set of criteria that is comparable to what is currently occurring. + **Additional context** Add any other implementation details here. From 6a85a6cd79ded8eb09706e5558dd070e1b8ebe17 Mon Sep 17 00:00:00 2001 From: Nathan Jesudason Date: Sat, 4 Feb 2023 15:49:03 -0800 Subject: [PATCH 59/61] Create pull_request_template.md (#68) --- .github/pull_request_template.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 .github/pull_request_template.md diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 0000000..5ccae95 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,18 @@ +# Purpose + +This PR (summary of this PR)... +Closes (issue) + +- [ ] Bug fix (non-breaking change which fixes an issue) +- [ ] New feature (non-breaking change which adds functionality) +- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) + +## Checklist + +- [ ] I have self-reviewed this PR +- [ ] I have followed coding style guidelines of this project +- [ ] I have not introduced errors or new warnings +- [ ] I have commented part of the code that might be confusing or hard to understand +- [ ] I have cleaned up WIP commits using Interactive Rebasing +- [ ] I have rebased new changes onto master and resolved any conflicts +- [ ] I have updated the changelog with the new changes From d8c990acc41bb27a0c786a9e7feceb2b0769884a Mon Sep 17 00:00:00 2001 From: Nathan Jesudason Date: Fri, 4 Aug 2023 14:24:36 -0700 Subject: [PATCH 60/61] Create LICENSE (#79) --- LICENSE | 661 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 661 insertions(+) create mode 100644 LICENSE diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..0ad25db --- /dev/null +++ b/LICENSE @@ -0,0 +1,661 @@ + GNU AFFERO GENERAL PUBLIC LICENSE + Version 3, 19 November 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU Affero General Public License is a free, copyleft license for +software and other kinds of works, specifically designed to ensure +cooperation with the community in the case of network server software. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +our General Public Licenses are intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + Developers that use our General Public Licenses protect your rights +with two steps: (1) assert copyright on the software, and (2) offer +you this License which gives you legal permission to copy, distribute +and/or modify the software. + + A secondary benefit of defending all users' freedom is that +improvements made in alternate versions of the program, if they +receive widespread use, become available for other developers to +incorporate. Many developers of free software are heartened and +encouraged by the resulting cooperation. However, in the case of +software used on network servers, this result may fail to come about. +The GNU General Public License permits making a modified version and +letting the public access it on a server without ever releasing its +source code to the public. + + The GNU Affero General Public License is designed specifically to +ensure that, in such cases, the modified source code becomes available +to the community. It requires the operator of a network server to +provide the source code of the modified version running there to the +users of that server. Therefore, public use of a modified version, on +a publicly accessible server, gives the public access to the source +code of the modified version. + + An older license, called the Affero General Public License and +published by Affero, was designed to accomplish similar goals. This is +a different license, not a version of the Affero GPL, but Affero has +released a new version of the Affero GPL which permits relicensing under +this license. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU Affero General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Remote Network Interaction; Use with the GNU General Public License. + + Notwithstanding any other provision of this License, if you modify the +Program, your modified version must prominently offer all users +interacting with it remotely through a computer network (if your version +supports such interaction) an opportunity to receive the Corresponding +Source of your version by providing access to the Corresponding Source +from a network server at no charge, through some standard or customary +means of facilitating copying of software. This Corresponding Source +shall include the Corresponding Source for any work covered by version 3 +of the GNU General Public License that is incorporated pursuant to the +following paragraph. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the work with which it is combined will remain governed by version +3 of the GNU General Public License. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU Affero General Public License from time to time. Such new versions +will be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU Affero General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU Affero General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU Affero General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program 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 Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If your software can interact with users remotely through a computer +network, you should also make sure that it provides a way for users to +get its source. For example, if your program is a web application, its +interface could display a "Source" link that leads users to an archive +of the code. There are many ways you could offer source, and different +solutions will be better for different programs; see section 13 for the +specific requirements. + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU AGPL, see +. From dff4ed03018eac9429781bec2989071fce6fd926 Mon Sep 17 00:00:00 2001 From: Nathan Jesudason Date: Sun, 6 Aug 2023 19:15:48 -0700 Subject: [PATCH 61/61] Create template for onboarding scenarios (#80) --- .github/ISSUE_TEMPLATE/onboarding_problem.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/onboarding_problem.md diff --git a/.github/ISSUE_TEMPLATE/onboarding_problem.md b/.github/ISSUE_TEMPLATE/onboarding_problem.md new file mode 100644 index 0000000..9547d48 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/onboarding_problem.md @@ -0,0 +1,16 @@ +--- +name: Onboarding Problem +about: Create a scenario for a new programmer to fix +title: '' +labels: training +assignees: '' + +--- + +**Scenario** +Describe the "issue" from the perspective of a client + +**Reminders** +- Don't merge any branches associated with creating scenarios or solving scenarios +- Scouts honor - don't compare the scenario branch from main code to solve the problem or old commits as much as possible. + - The goal is to learn the codebase by debugging and writing code, just like you would a real problem. \ No newline at end of file