From c2c25dd30e4c81be2f687e161b644f244f20a4ea Mon Sep 17 00:00:00 2001 From: hg Date: Mon, 30 Jun 2014 22:07:03 +0200 Subject: [PATCH] Added restart as recovery when connection to devices is lost. Added a heardbeat-file into which a timestamp is written every second. --- Makefile | 3 + scripts/restartPC.sh | Bin 0 -> 3655 bytes src/GrowControl.cpp | 204 +++++++++++++++++++++++++++++++++++++++++-- src/GrowControl.h | 27 +++++- src/main.cpp | 13 ++- 5 files changed, 238 insertions(+), 9 deletions(-) create mode 100755 scripts/restartPC.sh diff --git a/Makefile b/Makefile index 693f59a..4d520d4 100644 --- a/Makefile +++ b/Makefile @@ -34,7 +34,10 @@ install: bin/growcontrold mkdir -p /opt/growcontrol/save cp bin/growcontrold /opt/growcontrol/bin cp -r config /opt/growcontrol + cp -r scripts /opt/growcontrol + chmod a+x /opt/growcontrol/* uninstall: rm -rf /opt/growcontrol/bin/growcontrold rm -rf /opt/growcontrol/config + rm -rf /opt/growcontrol/scripts diff --git a/scripts/restartPC.sh b/scripts/restartPC.sh new file mode 100755 index 0000000000000000000000000000000000000000..f1cb268a4e855c1786eddf7098f5a3a7b0664df0 GIT binary patch literal 3655 zcmeH}Jr2S!426vlNOWRiHf2UV05cch2K@H21TD+0Q+Dj2S17&T{-*56vCZoGd!a@pC;icT97# S=q$(2_0Zfg&B>y(AO8#WQo?co literal 0 HcmV?d00001 diff --git a/src/GrowControl.cpp b/src/GrowControl.cpp index 734f6ab..c928eb1 100644 --- a/src/GrowControl.cpp +++ b/src/GrowControl.cpp @@ -19,6 +19,8 @@ #include "fanprofile/DummyProfile.h" #include "fanprofile/Dummy2Profile.h" #include "savefile.h" +#include +#include namespace growcontrol { @@ -37,9 +39,16 @@ GrowControl::~GrowControl() { bool GrowControl::connect(std::string workDir, std::string host , uint16_t port, bool retry) { tfConnection.addListener(*this); this->workDirString = workDir; - std::ostringstream ss; - ss << workDir << "/" << "save"; - saveDirString = ss.str(); + { + std::ostringstream ss; + ss << workDir << "/" << "save"; + saveDirString = ss.str(); + } + { + std::ostringstream ss; + ss << workDir << "/" << "scripts"; + scriptDirString = ss.str(); + } try { fan1UpperLimit = saveFileRead(saveDirString, "fan1UpperLimit.save"); @@ -95,8 +104,14 @@ void GrowControl::newDeviceCallback(TfConnection& conn, TfDevice& device) { case TfDeviceIdentifier::MASTER: if ( (rootBrick == nullptr || rootBrick->getDeviceState() != TfDeviceState::CONNECTED) && device.getDeviceState() == TfDeviceState::AVAILABLE && device.getParent() == nullptr ) { + if (rootBrick != nullptr) { + rootBrick->removeListener(*this); + } rootBrick = (TfMasterBrick*)&device; rootBrick->connect(); + rootBrick->addListener(*this); + masterConnectFlag = true; + masterMissingFlag = false; logger << log4cpp::Priority::INFO << "Added root device " << device.getDeviceName(true) << "."; } break; @@ -124,14 +139,22 @@ void GrowControl::newDeviceCallback(TfConnection& conn, TfDevice& device) { if (prioNew < prioT1) { // new device has lower priority than current temp1 if (prioT1 < 10000) { // current temp1 is alive and is moved to temp2 temp2Bricklet = temp1Bricklet; + temp2ConnectFlag =true; + temp2MissingFlag =false; logger << log4cpp::Priority::INFO << "Moving temp1 to temp2."; } temp1Bricklet = (TfTemperatureBricklet*)&device; temp1Bricklet->connect(); + temp1Bricklet->addListener(*this); + temp1ConnectFlag =true; + temp1MissingFlag =false; logger << log4cpp::Priority::INFO << "Added temp1 device" << device.getDeviceName(true) << "."; } else if (prioNew < prioT2) { // new device has lower priority than current temp2 temp2Bricklet = (TfTemperatureBricklet*)&device; temp2Bricklet->connect(); + temp2Bricklet->addListener(*this); + temp2ConnectFlag =true; + temp2MissingFlag =false; logger << log4cpp::Priority::INFO << "Added temp2 device " << device.getDeviceName(true) << "."; } } @@ -140,23 +163,45 @@ void GrowControl::newDeviceCallback(TfConnection& conn, TfDevice& device) { case TfDeviceIdentifier::HUMIDITY: if ( (hum1Bricklet == nullptr || hum1Bricklet->getDeviceState() != TfDeviceState::CONNECTED) && device.getDeviceState() == TfDeviceState::AVAILABLE ) { + if (hum1Bricklet != nullptr) { + hum1Bricklet->removeListener(*this); + } hum1Bricklet = (TfHumidityBricklet*)&device; hum1Bricklet->connect(); + hum1Bricklet->addListener(*this); + hum1ConnectFlag = true; + hum1MissingFlag = false; logger << log4cpp::Priority::INFO << "Added hum1 device " << device.getDeviceName(true) << "."; } break; case TfDeviceIdentifier::IO16: if ( (io16Bricklet == nullptr || io16Bricklet->getDeviceState() != TfDeviceState::CONNECTED) && device.getDeviceState() == TfDeviceState::AVAILABLE ) { + if (io16Bricklet != nullptr) { + io16Bricklet->removeListener(*this); + } io16Bricklet = (TfIO16Bricklet*)&device; io16Bricklet->connect(); io16Bricklet->setPortConfiguration('a', fan1PinMask, 'o', false); + io16ConnectFlag = true; + io16MissingFlag = false; + io16Bricklet->addListener(*this); setFan1Level(fan1Level); logger << log4cpp::Priority::INFO << "Added io16 device " << device.getDeviceName(true) << "."; } break; case TfDeviceIdentifier::LCD20x4: - lcd.setTfObject((TfLCD20x4Bricklet&)device); + if ( (lcdBricklet == nullptr || lcdBricklet->getDeviceState() != TfDeviceState::CONNECTED) + && device.getDeviceState() == TfDeviceState::AVAILABLE ) { + if (lcdBricklet != nullptr) { + lcdBricklet->removeListener(*this); + } + lcdBricklet = (TfLCD20x4Bricklet*)&device; + lcdBricklet->addListener(*this); + lcd.setTfObject((TfLCD20x4Bricklet&)device); + lcdConnectFlag = true; + lcdMissingFlag = false; + } break; default: break; @@ -429,7 +474,7 @@ void GrowControl::shutdownController() { } -void GrowControl::mainLoop() { +void GrowControl::mainLoop(std::string heartBeatFile) { if (fan1ActiveProfile != nullptr) { fan1ActiveProfile->startCallback(); @@ -437,6 +482,39 @@ void GrowControl::mainLoop() { while (mainLoopFlag) { + // Check missing device countdown + if (missingRestartCountdown < 0) { + #define CHECK1_M(name) (!name ## ConnectFlag || name ## MissingFlag) + #define CHECK1_O(name) (name ## ConnectFlag && name ## MissingFlag) + if ( CHECK1_O(master) + || CHECK1_O(temp1) + || CHECK1_O(temp2) + || CHECK1_O(hum1) + || CHECK1_O(io16) + || CHECK1_O(lcd) ) { // all missing devices are connected + missingRestartCountdown = missingRestartMaxCountdown; + logger << log4cpp::Priority::ERROR << "Starting missing device countdown." ; + } + } else if (missingRestartCountdown >= 0) { + #define CHECK2_M(name) (name ## ConnectFlag && !name ## MissingFlag) + #define CHECK2_O(name) (!name ## ConnectFlag || !name ## MissingFlag) + if ( CHECK2_O(master) + && CHECK2_O(temp1) + && CHECK2_O(temp2) + && CHECK2_O(hum1) + && CHECK2_O(io16) + && CHECK2_O(lcd) ) { // all missing devices are connected + missingRestartCountdown = -1; + logger << log4cpp::Priority::ERROR << "Missing device countdown stopped, all missing devices are connected again." ; + } else if (missingRestartCountdown == 0) { + logger << log4cpp::Priority::ERROR << "Missing device timeout, restarting ..." ; + restartPC(); + missingRestartCountdown = -1; + } else { + missingRestartCountdown--; + } + } + // Fetch sensor values GCSensorsInfo sensorValues; sensorValues.timestamp = std::chrono::system_clock::now(); @@ -446,6 +524,9 @@ void GrowControl::mainLoop() { } catch (GCException &e) { sensorValues.temp1.valid = false; sensorValues.temp1.error = e; + if (temp1ConnectFlag) { + temp1MissingFlag = true; + } } try { sensorValues.temp2.value = getTemp2(); @@ -453,6 +534,9 @@ void GrowControl::mainLoop() { } catch (GCException &e) { sensorValues.temp2.valid = false; sensorValues.temp2.error = e; + if (temp2ConnectFlag) { + temp2MissingFlag = true; + } } try { sensorValues.hum1.value = getHum1(); @@ -460,6 +544,9 @@ void GrowControl::mainLoop() { } catch (GCException &e) { sensorValues.hum1.valid = false; sensorValues.hum1.error = e; + if (hum1ConnectFlag) { + hum1MissingFlag = true; + } } try { sensorValues.fan1UpperLimit = getFan1UpperLimit(); @@ -470,6 +557,9 @@ void GrowControl::mainLoop() { } catch (GCException &e) { sensorValues.fan1Level.valid = false; sensorValues.fan1Level.error = e; + if (io16ConnectFlag) { + io16MissingFlag = true; + } } lcd.controlMainLoopCallback(sensorValues); @@ -478,11 +568,32 @@ void GrowControl::mainLoop() { fan1ActiveProfile->mainLoopCallback(sensorValues); } + // Write timestamp to heartbeatfile + if (!heartBeatFile.empty()) { + try { + std::ofstream hbfs(heartBeatFile, std::ios_base::out | std::ios_base::trunc); + if (hbfs) { + std::time_t timestamp = std::chrono::system_clock::to_time_t(sensorValues.timestamp); + hbfs << timestamp << " " << std::ctime(×tamp); + hbfs.flush(); + hbfs.close(); + } else { + logger << log4cpp::Priority::ERROR << "Could not open heartBeatFile " << heartBeatFile + << ": " << strerror(errno); + } + } catch (std::exception& e) { + logger << log4cpp::Priority::ERROR << "Exception while opening heartBeatFile " << heartBeatFile + << ": " << e.what(); + } + } + + // Sleep if (mainLoopFlag) { std::this_thread::sleep_until(sensorValues.timestamp + std::chrono::seconds(1)); } - } + } // end while(mainLoopFlag) + // Save state before exiting try { saveFileWrite(saveDirString, "fan1UpperLimit.save", fan1UpperLimit); } catch (GCException &e) { @@ -517,5 +628,86 @@ void GrowControl::mainLoop() { } +void GrowControl::stateChangeCallback(TfDevice& device, TfDeviceState oldstate, TfDeviceState newstate) { + if (&device == rootBrick) { + if (newstate == TfDeviceState::CONNECTION_LOST || newstate == TfDeviceState::DISCONNECTED) { + if (masterConnectFlag) { + masterMissingFlag = true; + } + } else if (newstate == TfDeviceState::CONNECTED) { + if (masterConnectFlag) { + masterMissingFlag = false; + } + } + } else if (&device == temp1Bricklet) { + if (newstate == TfDeviceState::CONNECTION_LOST || newstate == TfDeviceState::DISCONNECTED) { + if (temp1ConnectFlag) { + temp1MissingFlag = true; + } + } else if (newstate == TfDeviceState::CONNECTED) { + if (temp1ConnectFlag) { + temp1MissingFlag = false; + } + } + } else if (&device == temp2Bricklet) { + if (newstate == TfDeviceState::CONNECTION_LOST || newstate == TfDeviceState::DISCONNECTED) { + if (temp2ConnectFlag) { + temp2MissingFlag = true; + } + } else if (newstate == TfDeviceState::CONNECTED) { + if (temp2ConnectFlag) { + temp2MissingFlag = false; + } + } + } else if (&device == hum1Bricklet) { + if (newstate == TfDeviceState::CONNECTION_LOST || newstate == TfDeviceState::DISCONNECTED) { + if (hum1ConnectFlag) { + hum1MissingFlag = true; + } + } else if (newstate == TfDeviceState::CONNECTED) { + if (hum1ConnectFlag) { + hum1MissingFlag = false; + } + } + } else if (&device == io16Bricklet) { + if (newstate == TfDeviceState::CONNECTION_LOST || newstate == TfDeviceState::DISCONNECTED) { + if (io16ConnectFlag) { + io16MissingFlag = true; + } + } else if (newstate == TfDeviceState::CONNECTED) { + if (io16ConnectFlag) { + io16MissingFlag = false; + } + } + } else if (&device == lcdBricklet) { + if (newstate == TfDeviceState::CONNECTION_LOST || newstate == TfDeviceState::DISCONNECTED) { + if (lcdConnectFlag) { + lcdMissingFlag = true; + } + } else if (newstate == TfDeviceState::CONNECTED) { + if (lcdConnectFlag) { + lcdMissingFlag = false; + } + } + } +} + +void GrowControl::shutdownCallback(TfDevice& device) { + if (masterConnectFlag) { + masterMissingFlag = true; + if (missingRestartCountdown < 0) { + missingRestartCountdown = missingRestartMaxCountdown; + logger << log4cpp::Priority::ERROR << "Starting missing device countdown." ; + } + } +} + + +void GrowControl::restartPC() { + std::ostringstream ss; + ss << scriptDirString << "/restartPC.sh"; + std::string cmd = ss.str(); + system(cmd.c_str()); +} } /* namespace growcontrol */ diff --git a/src/GrowControl.h b/src/GrowControl.h index 8b69432..e68167a 100644 --- a/src/GrowControl.h +++ b/src/GrowControl.h @@ -43,7 +43,7 @@ struct GCSensorsInfo { }; -class GrowControl : public TfConnectionListener { +class GrowControl : public TfConnectionListener, public TfDeviceStateListener { private: static log4cpp::Category& logger; @@ -56,6 +56,7 @@ private: TfTemperatureBricklet* temp2Bricklet = nullptr; TfHumidityBricklet* hum1Bricklet = nullptr; TfIO16Bricklet* io16Bricklet = nullptr; + TfLCD20x4Bricklet* lcdBricklet = nullptr; std::mutex controlMutex; unsigned int sleepDuration = 1; @@ -71,6 +72,22 @@ private: std::string workDirString; std::string saveDirString; + std::string scriptDirString; + + bool masterConnectFlag = false; + bool masterMissingFlag = false; + bool temp1ConnectFlag = false; + bool temp1MissingFlag = false; + bool temp2ConnectFlag = false; + bool temp2MissingFlag = false; + bool hum1ConnectFlag = false; + bool hum1MissingFlag = false; + bool io16ConnectFlag = false; + bool io16MissingFlag = false; + bool lcdConnectFlag = false; + bool lcdMissingFlag = false; + int missingRestartCountdown = -1; + int missingRestartMaxCountdown = 60; public: @@ -82,7 +99,7 @@ public: void newDeviceCallback(TfConnection& conn, TfDevice& device); void shutdownCallback(); - void mainLoop(); + void mainLoop(std::string heartBeatFile); double getTemp1(); int16_t getTemp1Raw(); @@ -110,6 +127,12 @@ public: const std::string getSaveDir() { return saveDirString; } const std::string getWorkDir() { return workDirString; } + const std::string getScriptDir() { return scriptDirString; } + + void stateChangeCallback(TfDevice& device, TfDeviceState oldstate, TfDeviceState newstate); + void shutdownCallback(TfDevice& device); + + void restartPC(); }; } /* namespace growcontrol */ diff --git a/src/main.cpp b/src/main.cpp index 3915669..6456b81 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -32,6 +32,7 @@ std::string socketPath = "/opt/growcontrol/growcontrold.socket"; std::string log4cppConfig = "log4cpp.config"; std::string host = "localhost"; std::string workDir = "/opt/growcontrol"; +std::string heartBeatFile = "/opt/growcontrol/growcontrold.heartbeat"; uint16_t port = 4223; bool daemonMode = false; int pid_fd = 0; @@ -68,6 +69,7 @@ void printUsage(std::ostream& stream, std::string name) { << "\t-l ... path to log4cpp config file" << std::endl << "\t-h ... brickd address" << std::endl << "\t-p ... brickd port" << std::endl + << "\t-hb ... heartbeat file" << std::endl << "\t-w ... working directory" << std::endl; } @@ -126,6 +128,15 @@ int main(int argc, char **argv) { printUsage(std::cerr, argv[0]); exit(1); } + } else if (strcmp(argv[t], "-hb") == 0) { + if (t+1 < argc && argv[t+1][0] != '-') { + t++; + heartBeatFile = argv[t]; + } else { + std::cerr << "Option -hb is missing an argument." << std::endl << std::endl; + printUsage(std::cerr, argv[0]); + exit(1); + } } else if (strcmp(argv[t], "-p") == 0) { if (t+1 < argc && argv[t+1][0] != '-') { t++; @@ -176,7 +187,7 @@ int main(int argc, char **argv) { } catch (GCException &e) { } - controller.mainLoop(); + controller.mainLoop(heartBeatFile); logger << log4cpp::Priority::WARN << "Shutting down ipc ...";