Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

51 revamp serial monitor input #65

Open
wants to merge 2 commits into
base: masterv4.3
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 3 additions & 4 deletions platformio.ini
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,10 @@ platform = atmelsam
board = adafruit_feather_m0
framework = arduino
monitor_speed = 115200
monitor_flags =
--raw
--echo
monitor_raw = yes
monitor_echo = yes
extra_scripts = ./upload_script.py
test_build_project_src = true
test_build_src = true
lib_deps =
ArduinoJson@~6.17.2
StreamUtils@~1.6.0
Expand Down
240 changes: 149 additions & 91 deletions src/Application/App-serial.cpp
Original file line number Diff line number Diff line change
@@ -1,38 +1,117 @@
#ifdef COMPONENT_TEST
#include <Application/App.hpp>
#include <Components/Sensors/FlowSensor.hpp>

namespace {
struct SerialRequest {
const char * cmd;
const JsonArray & path;
const JsonDocument & input;
};

using FunctionType = std::function<void(SerialRequest req)>;
using StringToFunc = std::unordered_map<std::string, FunctionType>;
using func = std::function<void(std::vector<std::string> args)>;
using StringToFunc = std::unordered_map<std::string, func>;
StringToFunc mapNameToCallback;

constexpr const char EOT = '\4';

void endTransmission() {
print(EOT);
println(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;
}

void ListCommands(){
println("Available serial commands: ");
println("open [list of ints] - open a list of filter valves");
println("open [air/alcohol/flush] - open said valve");
println("close [list of ints] - close a list of filter valves");
println("close [air/alcohol/flush] - close said valve");
println("pump [on/off/reverse] - change state of pump");
println("intake [on/off] - change state of intake valve");
println("query [status/config/ram/time/sensors] - print info about these objects");
println("alarm [int] - schedule alarm interrupt n seconds in the future (will print info but do nothing)");
return;
}
} // namespace

// Registering Top-level serial commands
void App::setupSerialRouting() {
mapNameToCallback["hyperflush"] = [this](SerialRequest req) {
const auto response = dispatchAPI<API::StartHyperFlush>();
serializeJson(response, Serial);
mapNameToCallback["help"] = [this](std::vector<std::string> args) {
ListCommands();
return;
};

mapNameToCallback["open"] = [this](std::vector<std::string> 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); // adding capacityPerRegister skips the first register
}
} else {
if(string_to_tpic(args[1]) != -1){
shift.setPin(string_to_tpic(args[1]), HIGH);
}
}
shift.write();
return;
};

mapNameToCallback["close"] = [this](std::vector<std::string> 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();
return;
};

mapNameToCallback["pump"] = [this](std::vector<std::string> args) {
const char * endpoint = args[1].c_str();
if (strcmp(endpoint, "on") == 0) {
pump.on();
}
if (strcmp(endpoint, "reverse") == 0) {
pump.on(Direction::reverse);
}
if (strcmp(endpoint, "off") == 0) {
pump.off();
}
return;
};

mapNameToCallback["debubble"] = [this](SerialRequest req) {
const auto response = dispatchAPI<API::StartDebubble>();
serializeJson(response, Serial);
mapNameToCallback["intake"] = [this](std::vector<std::string> args){
const char * endpoint = args[1].c_str();
if(strcmp(endpoint, "on") == 0){
intake.on();
}
if(strcmp(endpoint, "off") == 0){
intake.off();
}
return;
};

mapNameToCallback["query"] = [this](SerialRequest req) {
const char * endpoint = req.path[1];
mapNameToCallback["query"] = [this](std::vector<std::string> args) {
const char * endpoint = args[1].c_str();
if (strcmp(endpoint, "status") == 0) {
const auto & response = dispatchAPI<API::StatusGet>();
serializeJson(response, Serial);
Expand All @@ -52,10 +131,43 @@ 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<std::string> 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];
// This function is pointless in component testing mode because you can't sample in the mode
// Keeping the code commented out in case this changes in the future

/* mapNameToCallback["reset"] = [this](std::vector<std::string> 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]));
Expand All @@ -69,83 +181,29 @@ void App::setupSerialRouting() {
endTransmission();
return;
}
};
}; */
}

/**
*
* {
* path: ["top-level command", "sub1", "sub2"],
* cmd: ""
* ...
* }
*/

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<const char *>()) {
item.second({input["cmd"], input["path"], input});
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<const char *>(), doc, response);
// }

if (strcmp(msg, "status")) {
println(status);
std::vector<std::string> args;
char str[80];
strcpy(str, msg);
println("");
const char delim[2] = " ";
const char * tok = strtok(str, delim);
while (tok != NULL) {
args.push_back(tok);
tok = strtok(NULL, delim);
}

// 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]));
for(auto & command : mapNameToCallback) {
if(command.first == args[0]){
command.second(args);
return;
}
}

// vm.writeToDirectory();
// }

// if (line == "mem") {
// println(free_ram());
// }

// if (line == "hyperflush") {
// beginHyperFlush();
// };
}
//if input doesn't match any commands
ListCommands();
return;
}
#endif
33 changes: 23 additions & 10 deletions src/Application/App.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -123,10 +123,10 @@ class App : public KPController, public KPSerialInputObserver, public TaskObserv
//
// ─── ADDING COMPONENTS ───────────────────────────────────────────
//

#ifdef COMPONENT_TEST
addComponent(KPSerialInput::sharedInstance());
setupSerialRouting();

#endif
addComponent(ActionScheduler::sharedInstance());
addComponent(fileLoader);
addComponent(shift);
Expand Down Expand Up @@ -223,29 +223,42 @@ class App : public KPController, public KPSerialInputObserver, public TaskObserv
file.close();
}



#ifndef COMPONENT_TEST
// RTC Interrupt callback
power.onInterrupt([this]() {
println(GREEN("RTC Interrupted!"));
println(scheduleNextActiveTask().description());
interrupts();
});


nowSampleButton.onInterrupt([this](){
nowSampleButton.onInterrupt([this](){
if(!power.rtc.alarm(1) && !power.rtc.alarm(2)){
println(GREEN("Sample Now Button Interrupted!"));
println(beginNowTask().description());
}
interrupts();
});
#else
// RTC Interrupt callback
power.onInterrupt([this]() {
println(GREEN("RTC Interrupted!"));
interrupts();
});

nowSampleButton.onInterrupt([this](){
if(!power.rtc.alarm(1) && !power.rtc.alarm(2)){
println(GREEN("Sample Now Button Interrupted!"));
}
interrupts();
});
#endif
nowSampleButton.setSampleButton();
runForever(1000, "detailLog", [&]() { logDetail("detail.csv"); });
#if defined(DEBUG) || defined(COMPONENT_TEST)
#if defined(DEBUG)
runForever(2000, "memLog", [&]() { printFreeRam(); });
#endif

#ifndef COMPONENT_TEST
nowSampleButton.setSampleButton();
#endif
#endif
}

void logDetail(const char * filename) {
Expand Down