Files
king-arth-iot-devices/src/sword/swordMain.cpp
T
2026-10-03 16:06:15 +02:00

282 lines
9.7 KiB
C++

#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<SwordConfig> SwordCfg;
unique_ptr<DeviceIoState> 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<SwordConfig>(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<const char*, 4> modes = {"Undefined", "Maintanance", "PromoMode", "Production"};
log.sendBootUp(Version, modes[static_cast<int>(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<DeviceIoState>(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<ResponseTxMsg>(msg->code, 0);
Thread::sleep(500ms);
IoStat->tick();
mqtt.sendStatusMessage(Mode);
// auto e = make_unique<ResponseTxMsg>(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;
}
}
}