Added restart as recovery when connection to devices is lost.
Added a heardbeat-file into which a timestamp is written every second.
This commit is contained in:
@@ -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
|
||||
|
||||
Executable
BIN
Binary file not shown.
+198
-6
@@ -19,6 +19,8 @@
|
||||
#include "fanprofile/DummyProfile.h"
|
||||
#include "fanprofile/Dummy2Profile.h"
|
||||
#include "savefile.h"
|
||||
#include <cstdio>
|
||||
#include <cerrno>
|
||||
|
||||
|
||||
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<unsigned>(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<unsigned>(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 */
|
||||
|
||||
+25
-2
@@ -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 */
|
||||
|
||||
+12
-1
@@ -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 ...";
|
||||
|
||||
|
||||
Reference in New Issue
Block a user