#include "swordMain.hpp" #include "defines.hpp" #include "mqttChannel.hpp" using namespace pal; using namespace std::literals::string_view_literals; using namespace std::literals::chrono_literals; using namespace std; const string Version = "V 0.9.0"; unique_ptr SwordCfg; unique_ptr IoStat; OpMode Mode = OpMode::Maintanance; void processPromo(MqttChannel& mqtt, SeqLogger& log); void processProduction(MqttChannel& mqtt, SeqLogger& log); int main(int argc, char* argv[]) { CLI::App app{"Sword Firmware"}; string appName = "SwordDebug"; string modeArg = "test"; string assetsBasePath = "/home/user/assets/sword"; string brocker = "mqtts://excaliburs-erben.de:8883"; string topic = "staging/outdoor/king-arthur/sword/"; string user = "exodususer"; string password = "awesomeexodusoutdooradventures"; string logUrl = "log.excaliburs-erben.de"; string logApiKey = "uYpJC6WyZAbFK2MUgwNH"; app.add_option("startmode", modeArg, "Startup mode")->required(); app.add_option("basepath", assetsBasePath, "Base bath for assets")->required(); app.add_option("-n,--name", appName, "Name of App"); app.add_option("-b,--brocker", brocker, "MQTT Topic URL"); app.add_option("-t,--topic", topic, "MQTT Topic"); app.add_option("-p,--passwd", password, "MQTT Brocker password"); app.add_option("-u,--user", user, "MQTT Brocker user"); app.add_option("-l,--log", logUrl, "Logging Service"); app.add_option("-k,--key", logApiKey, "Logging API Key"); CLI11_PARSE(app, argc, argv); SeqLogger log(logUrl, logApiKey, appName); SwordCfg = make_unique(assetsBasePath, brocker, topic, user, password); fmt::println("{}", app.get_description()); if (modeArg == "promo") { fmt::println("starting with promo"); Mode = OpMode::PromoMode; } else if (modeArg == "game") { fmt::println("starting with promo"); Mode = OpMode::GamingMode; } else { fmt::println("unknown {}", modeArg); } constexpr array modes = {"Undefined", "Maintanance", "PromoMode", "Production"}; log.sendBootUp(Version, modes[static_cast(Mode)]); auto itrHandler = [&]() { TriggeredInput itr(26, "PItr"); while (true) { cout << "Await next interrupt\n"; if (itr.await()) { cout << "Interrupt Event\n"; } } }; thread interruptThread(itrHandler); InputPort sbPort(SwordEntryPin, "PinSEntry"); InputPort sePort(SwordEndPin, "PInSEnd"); InputPort spPort(SwordPulledPin, "PInSPull"); InputPort sparePort(sparePin, "spare"); OutputPort mOut1(LinMotPin1, "M1:1"); OutputPort mOut2(LinMotPin2, "M1:2"); InputTransAware SwordEntry(sbPort); InputTransAware SwordEnd(sePort); InputTransAware SwordPulled(spPort); LinMot SwordLatch(mOut1, mOut2); I2cMaster i2cDev(II2cMaster::ClockRate::_400k); AdcAds1115 adc(i2cDev, AdcAds1115::I2CAddress::Gnd); // Check if the ADC is connected if (!adc.isConnected()) { fmt::println("Failed to connect ot Sensor {}", modeArg); } CurrMon mon(adc); IoStat = make_unique(SwordEntry, SwordEnd, SwordPulled, SwordLatch); MqttChannel mqtt(appName, Version, *SwordCfg, *IoStat, log); auto resetCall = [&]() { log.sendEvent("Reset", ""); }; mqtt.flushInput(); static auto conTask = Thread::createExplicit("MqttTask", MqttChannel::runMasterService, ref(mqtt), resetCall, ref(mon)); SwordLatch.moveOut(); fmt::println("Running App"); while (true) { switch (Mode) { case OpMode::Maintanance: { fmt::println("Running Maintanance"); // txQueue.emplace(msg->code, 0); Thread::sleep(500ms); IoStat->tick(); mqtt.sendStatusMessage(Mode); // auto e = make_unique(msg->code, 0); // txQueue.enqueue(move(e)); } break; case OpMode::PromoMode: { fmt::println("Running PromoMode"); processPromo(mqtt, log); } break; case OpMode::GamingMode: { fmt::println("Running GamingMode"); processProduction(mqtt, log); } break; default: break; } } interruptThread.join(); return EXIT_SUCCESS; } enum MuteState { WaitForUnmute, WaitForMute, WaitForNextDay, }; void processPromo(MqttChannel& mqtt, SeqLogger& log) { auto muterS = MuteState::WaitForUnmute; Tick tmu(1min); Tick tn(1s); Tick tsf(30min); Waiter dP; auto& serCmds = mqtt.getSerCommands(); while (true) { Thread::sleep(10ms); IoStat->tick(); if (IoStat->SwordPulled.isPosEdge()) { if (!IoStat->Mute) { if (dP.tick(IoStat->PromoDelay)) { log.sendEvent("Pull", "Start Audio"); if (tsf.tick()) { MediaPlayer::playWavFile(SwordCfg->SoundPromo); } else { MediaPlayer::playWavFile(SwordCfg->SoundPromoShort); } dP.sample(); fmt::println("Muting next for {}", IoStat->PromoDelay); } else { fmt::println("still muted"); } } } if (tn.tick()) { mqtt.sendStatusMessage(Mode); } if (tmu.tick()) { auto now = std::chrono::system_clock::now(); std::time_t time = std::chrono::system_clock::to_time_t(now); std::tm* lTime = std::localtime(&time); int current_hour = lTime->tm_hour; switch (muterS) { case WaitForUnmute: if (current_hour >= 9) { fmt::println("[09:00 Threshold] Unmute"); log.sendEvent("Unmute", "Time for unmute"); IoStat->Mute = false; muterS = WaitForMute; } break; case WaitForMute: if (current_hour >= 21) { fmt::println("[21:00 Threshold] Mute"); log.sendEvent("Mute", "Time for mute"); IoStat->Mute = true; muterS = WaitForNextDay; } break; case WaitForNextDay: if (current_hour < 1) { fmt::println("[01:00 Threshold] new day"); muterS = WaitForUnmute; } break; default: break; } } } } void processProduction(MqttChannel& mqtt, SeqLogger& log) { auto& serCmds = mqtt.getSerCommands(); auto& gameCmds = mqtt.getGameCommands(); while (true) { auto msg = mqtt.tryDeque(); if (!msg) { continue; } auto code = gameCmds.at(msg->Payload); switch (code) { case GameModeCommands_e::GameStarted: { fmt::println("Got GameStarted"); log.sendEvent("Req", "GameStarted"); } case GameModeCommands_e::GameFinished: { fmt::println("Got GameFinished"); log.sendEvent("Req", "GameFinished"); } case GameModeCommands_e::GameNextTryLocked: { fmt::println("Got NextTryLock"); log.sendEvent("Req", "StayLockded"); while (true) { IoStat->SwordPulled.sample(); if (IoStat->SwordPulled.isPosEdge()) { mqtt.sendStatusMessage(Mode); // say: You are not the one MediaPlayer::playWavFile(SwordCfg->SoundDeny); Thread::sleep(15s); log.sendEvent("Req", "Pulled"); mqtt.sendEvent("pull"); } } } break; case GameModeCommands_e::GameNextTryRelease: { fmt::println("NextTryRelease"); log.sendEvent("Req", "UnlockSword"); IoStat->SwordLatch.moveIn(); while (true) { IoStat->SwordEnd.sample(); if (IoStat->SwordEnd.isNegEdge()) { mqtt.sendStatusMessage(Mode); break; } } while (true) { IoStat->SwordEntry.sample(); if (IoStat->SwordEntry.isNegEdge()) { mqtt.sendStatusMessage(Mode); break; } } mqtt.sendEvent("pulled-out"); log.sendEvent("Req", "Pulled Out"); // say: You ARE the one MediaPlayer::playWavFile(SwordCfg->SoundAllow); Thread::sleep(15s); mqtt.sendEvent("ceremonyDone"); while (true) { IoStat->SwordEnd.sample(); if (IoStat->SwordEnd.isNegEdge()) { mqtt.sendStatusMessage(Mode); break; } } while (true) { IoStat->SwordEntry.sample(); if (IoStat->SwordEntry.isNegEdge()) { mqtt.sendStatusMessage(Mode); break; } } log.sendEvent("Req", "Put Back"); mqtt.sendEvent("put-in"); } break; } } }