52 m_ClientAlertTimer.
callOnTimeout(std::bind(&WatchDog::processShutdown,
this));
54 m_WeatherAlertTimer.
callOnTimeout(std::bind(&WatchDog::processShutdown,
this));
78 if (ShutdownTriggerS[TRIGGER_CLIENT].s ==
ISS_ON && HeartBeatNP.
at(0)->
getValue() > 0)
80 LOGF_INFO(
"Client Watchdog is enabled. Shutdown is triggered when no heart beat is received in %.f seconds.",
86 if (ShutdownTriggerS[TRIGGER_WEATHER].s ==
ISS_ON)
88 if (WeatherThresholdN[0].value > 0)
89 LOGF_INFO(
"Weather Watchdog is enabled. Shutdown is triggered %.f seconds after Weather status enters DANGER zone.",
90 WeatherThresholdN[0].value);
92 LOG_INFO(
"Weather Watchdog is enabled. Shutdown is triggered when Weather status in DANGER zone.");
95 IDSnoopDevice(ActiveDeviceT[ACTIVE_WEATHER].text,
"WEATHER_STATUS");
96 IDSnoopDevice(ActiveDeviceT[ACTIVE_TELESCOPE].text,
"TELESCOPE_PARK");
107 m_ClientAlertTimer.
stop();
108 m_WeatherAlertTimer.
stop();
124 HeartBeatNP[0].
fill(
"WATCHDOG_HEARTBEAT_VALUE",
"Threshold (s)",
"%.f", 0, 3600, 60, 0);
129 double threshold = 0;
131 IUFillNumber(&WeatherThresholdN[0],
"WATCHDOG_WEATHER_VALUE",
"Threshold (s)",
"%.f", 0, 3600, 60, threshold);
136 IUFillText(&SettingsT[INDISERVER_HOST],
"INDISERVER_HOST",
"indiserver host",
"localhost");
137 IUFillText(&SettingsT[INDISERVER_PORT],
"INDISERVER_PORT",
"indiserver port",
"7624");
138 IUFillText(&SettingsT[SHUTDOWN_SCRIPT],
"SHUTDOWN_SCRIPT",
"shutdown script",
nullptr);
162 IUFillText(&ActiveDeviceT[ACTIVE_TELESCOPE],
"ACTIVE_TELESCOPE",
"Telescope",
"Telescope Simulator");
163 IUFillText(&ActiveDeviceT[ACTIVE_DOME],
"ACTIVE_DOME",
"Dome",
"Dome Simulator");
164 IUFillText(&ActiveDeviceT[ACTIVE_WEATHER],
"ACTIVE_WEATHER",
"Weather",
"Weather Simulator");
178 DefaultDevice::ISGetProperties(dev);
197 if (!strcmp(SettingsTP.
name, name))
205 m_INDIServerPort = std::stoi(SettingsT[INDISERVER_PORT].text);
218 if (!strcmp(ActiveDeviceTP.
name, name))
220 if (m_WatchDogClientInstance->isBusy())
224 LOG_ERROR(
"Cannot change devices names while shutdown is in progress...");
228 IDSnoopDevice(ActiveDeviceT[ACTIVE_WEATHER].text,
"WEATHER_STATUS");
229 IDSnoopDevice(ActiveDeviceT[ACTIVE_TELESCOPE].text,
"TELESCOPE_PARK");
250 if (!strcmp(WeatherThresholdNP.
name, name))
263 double prevHeartBeat = HeartBeatNP.
at(0)->
getValue();
265 if (m_WatchDogClientInstance->isBusy())
269 LOG_ERROR(
"Cannot change heart beat while shutdown is in progress...");
273 HeartBeatNP.
update(values, names, n);
277 if (ShutdownTriggerS[TRIGGER_CLIENT].s ==
ISS_OFF)
279 m_ClientAlertTimer.
stop();
286 LOG_INFO(
"Client Watchdog is disabled.");
287 m_ClientAlertTimer.
stop();
291 if (prevHeartBeat != HeartBeatNP.
at(0)->
getValue())
293 LOGF_INFO(
"Client Watchdog is enabled. Shutdown is triggered when no heart beat is received in %.f seconds.",
298 LOG_DEBUG(
"Received heart beat from client.");
308 return DefaultDevice::ISNewNumber(dev, name, values, names, n);
319 if (!strcmp(ShutdownProcedureSP.
name, name))
326 LOG_ERROR(
"Error: shutdown script file is not set.");
331 ShutdownProcedureSP.
s =
IPS_OK;
338 else if (!strcmp(MountPolicySP.
name, name))
344 LOG_INFO(
"Mount is ignored. Dome can start parking without waiting for mount to complete parking.");
346 LOG_INFO(
"Mount locks. Dome must wait for mount to park before it can start the parking procedure.");
351 else if (!strcmp(ShutdownTriggerSP.
name, name))
353 std::vector<ISState> oldStates(ShutdownTriggerSP.
nsp,
ISS_OFF);
354 std::vector<ISState> newStates(ShutdownTriggerSP.
nsp,
ISS_OFF);
355 for (
int i = 0; i < ShutdownTriggerSP.
nsp; i++)
356 oldStates[i] = ShutdownTriggerS[i].s;
358 for (
int i = 0; i < ShutdownTriggerSP.
nsp; i++)
359 newStates[i] = ShutdownTriggerS[i].s;
363 if (oldStates[TRIGGER_CLIENT] != newStates[TRIGGER_CLIENT])
366 if (newStates[TRIGGER_CLIENT] ==
ISS_OFF)
368 LOG_INFO(
"Disabling client watchdog. Lost communication with client shall not trigger the shutdown procedure.");
369 m_ClientAlertTimer.
stop();
377 LOG_ERROR(
"Heart beat timeout should be set first.");
379 for (
int i = 0; i < ShutdownTriggerSP.
nsp; i++)
380 ShutdownTriggerS[i].s = oldStates[i];
385 LOGF_INFO(
"Client Watchdog is enabled. Shutdown is triggered after %.f seconds of communication loss with the client.",
392 if (oldStates[TRIGGER_WEATHER] != newStates[TRIGGER_WEATHER])
395 if (newStates[TRIGGER_WEATHER] ==
ISS_OFF)
398 m_WeatherAlertTimer.
stop();
399 LOG_INFO(
"Weather Watchdog is disabled.");
403 IDSnoopDevice(ActiveDeviceT[ACTIVE_WEATHER].text,
"WEATHER_STATUS");
404 LOG_INFO(
"Weather Watchdog is enabled.");
417 return DefaultDevice::ISNewSwitch(dev, name, states, names, n);
428 if (!strcmp(
"WEATHER_STATUS", propName))
437 LOG_INFO(
"Weather status is no longer in DANGER zone.");
438 m_WeatherAlertTimer.
stop();
448 LOG_WARN(
"Weather is in DANGER zone.");
449 if (ShutdownTriggerS[TRIGGER_WEATHER].s ==
ISS_ON && m_WeatherAlertTimer.
isActive() ==
false)
451 if (WeatherThresholdN[0].value > 0)
452 LOGF_INFO(
"Shutdown procedure shall commence in %.f seconds unless weather status improves.", WeatherThresholdN[0].value);
453 m_WeatherAlertTimer.
start(WeatherThresholdN[0].value * 1000);
457 m_WeatherState = newWeatherState;
460 else if (!strcmp(
"TELESCOPE_PARK", propName))
470 if ((!strcmp(elemName,
"PARK") && !strcmp(
pcdataXMLEle(ep),
"On")))
472 else if ((!strcmp(elemName,
"UNPARK") && !strcmp(
pcdataXMLEle(ep),
"On")))
475 if (parked != m_IsMountParked)
477 LOGF_INFO(
"Mount is %s", parked ?
"Parked" :
"Unparked");
478 m_IsMountParked = parked;
482 if (parked ==
false &&
483 ShutdownTriggerS[TRIGGER_WEATHER].s ==
ISS_ON &&
487 LOG_WARN(
"Mount unparked while weather alert is active! Parking mount...");
488 m_WatchDogClientInstance->parkMount();
495 else if (!strcmp(
"DOME_PARK", propName))
505 if ((!strcmp(elemName,
"PARK") && !strcmp(
pcdataXMLEle(ep),
"On")))
507 else if ((!strcmp(elemName,
"UNPARK") && !strcmp(
pcdataXMLEle(ep),
"On")))
510 if (parked != m_IsDomeParked)
512 LOGF_INFO(
"Dome is %s", parked ?
"Parked" :
"Unparked");
513 m_IsDomeParked = parked;
517 if (parked ==
false &&
518 ShutdownTriggerS[TRIGGER_WEATHER].s ==
ISS_ON &&
522 LOG_WARN(
"Dome unparked while weather alert is active! Parking dome...");
523 m_WatchDogClientInstance->parkDome();
530 return DefaultDevice::ISSnoopDevice(root);
540 HeartBeatNP.
save(fp);
554void WatchDog::processShutdown()
567 LOG_WARN(
"Warning! Weather status in DANGER zone, executing shutdown procedure...");
569 LOG_WARN(
"Warning! Heartbeat threshold timed out, executing shutdown procedure...");
588 switch (m_ShutdownStage)
597 LOG_WARN(
"Warning! Weather status in DANGER zone, executing shutdown procedure...");
599 LOG_WARN(
"Warning! Heartbeat threshold timed out, executing shutdown procedure...");
611 m_WatchDogClientInstance->setMount(ActiveDeviceT[ACTIVE_TELESCOPE].text);
614 m_WatchDogClientInstance->setDome(ActiveDeviceT[ACTIVE_DOME].text);
617 m_WatchDogClientInstance->setServer(SettingsT[INDISERVER_HOST].text, m_INDIServerPort);
619 LOG_DEBUG(
"Connecting to INDI server...");
621 m_WatchDogClientInstance->connectServer();
629 if (m_WatchDogClientInstance->isConnected())
631 LOGF_DEBUG(
"Connected to INDI server %s @ %s", SettingsT[0].text,
642 LOG_DEBUG(
"Waiting for INDI server connection...");
648 IPState mountState = m_WatchDogClientInstance->getMountParkState();
667 IPState domeState = m_WatchDogClientInstance->getDomeParkState();
682 LOG_INFO(
"Shutdown procedure complete.");
683 ShutdownProcedureSP.
s =
IPS_OK;
686 if (m_WatchDogClientInstance->isConnected())
702void WatchDog::parkDome()
704 if (m_WatchDogClientInstance->parkDome() ==
false)
706 LOG_ERROR(
"Error: Unable to park dome! Shutdown procedure terminated.");
715void WatchDog::parkMount()
717 if (m_WatchDogClientInstance->parkMount() ==
false)
719 LOG_ERROR(
"Error: Unable to park mount! Shutdown procedure terminated.");
734void WatchDog::executeScript()
749 LOGF_INFO(
"Waiting for script with PID %d to complete...", getpid());
751 if (WIFEXITED(statval))
753 int exit_code = WEXITSTATUS(statval);
754 LOGF_INFO(
"Script complete with exit code %d", exit_code);
760 LOGF_ERROR(
"Error: script %s failed. Shutdown procedure terminated.",
769 "Error: script %s did not terminate with exit. Shutdown procedure terminated.",
const char * getDeviceName() const
virtual bool saveConfig(bool silent=false, const char *property=nullptr)
Save the current properties in a configuration file.
void setDriverInterface(uint32_t value)
setInterface Set driver interface. By default the driver interface is set to GENERAL_DEVICE....
void setVersion(uint16_t vMajor, uint16_t vMinor)
Set driver version information to be defined in DRIVER_INFO property as vMajor.vMinor.
void defineProperty(INumberVectorProperty *property)
virtual bool saveConfigItems(FILE *fp)
saveConfigItems Save specific properties in the provide config file handler. Child class usually over...
uint32_t getCurrentPollingPeriod() const
getCurrentPollingPeriod Return the current polling period.
virtual bool initProperties()
Initialize properties initial state and value. The child class must implement this function.
int SetTimer(uint32_t ms)
Set a timer to call the function TimerHit after ms milliseconds.
void addDebugControl()
Add Debug control to the driver.
virtual bool ISNewText(const char *dev, const char *name, char *texts[], char *names[], int n)
Process the client newSwitch command.
void setState(IPState state)
void apply(const char *format,...) const ATTRIBUTE_FORMAT_PRINTF(2
bool load()
load Attempt to load property values from configuration file.
void save(FILE *f) const
save Save property to configuration file.
const char * getName() const
bool isNameMatch(const char *otherName) const
const WidgetView< T > * at(size_t index) const
bool update(const double values[], const char *const names[], int n)
void fill(const char *device, const char *name, const char *label, const char *group, IPerm permission, double timeout, IPState state)
void setSingleShot(bool singleShot)
Set whether the timer is a single-shot timer.
void callOnTimeout(const std::function< void()> &callback)
void start()
Starts or restarts the timer with the timeout specified in interval.
bool isActive() const
Returns true if the timer is running (pending); otherwise returns false.
void stop()
Stops the timer.
virtual void TimerHit() override
Callback function to be called once SetTimer duration elapses.
virtual bool Disconnect() override
Disconnect from device.
virtual bool ISSnoopDevice(XMLEle *root) override
Process a snoop event from INDI server. This function is called when a snooped property is updated in...
virtual bool initProperties() override
Initialize properties initial state and value. The child class must implement this function.
virtual void ISGetProperties(const char *dev) override
define the driver's properties to the client. Usually, only a minimum set of properties are defined t...
virtual const char * getDefaultName() override
virtual bool ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n) override
Process the client newSwitch command.
virtual bool ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n) override
Process the client newNumber command.
virtual bool Connect() override
Connect to the device. INDI::DefaultDevice implementation connects to appropriate connection interfac...
virtual bool ISNewText(const char *dev, const char *name, char *texts[], char *names[], int n) override
Process the client newSwitch command.
@ WATCHDOG_CLIENT_STARTED
virtual bool saveConfigItems(FILE *fp) override
saveConfigItems Save specific properties in the provide config file handler. Child class usually over...
const char * MAIN_CONTROL_TAB
MAIN_CONTROL_TAB Where all the primary controls for the device are located.
const char * OPTIONS_TAB
OPTIONS_TAB Where all the driver's options are located. Those may include auxiliary controls,...
void IUSaveConfigSwitch(FILE *fp, const ISwitchVectorProperty *svp)
Add a switch vector property value to the configuration file.
void IUFillNumberVector(INumberVectorProperty *nvp, INumber *np, int nnp, const char *dev, const char *name, const char *label, const char *group, IPerm p, double timeout, IPState s)
Assign attributes for a number vector property. The vector's auxiliary elements will be set to NULL.
void IUFillTextVector(ITextVectorProperty *tvp, IText *tp, int ntp, const char *dev, const char *name, const char *label, const char *group, IPerm p, double timeout, IPState s)
Assign attributes for a text vector property. The vector's auxiliary elements will be set to NULL.
void IUSaveConfigNumber(FILE *fp, const INumberVectorProperty *nvp)
Add a number vector property value to the configuration file.
void IUFillSwitch(ISwitch *sp, const char *name, const char *label, ISState s)
Assign attributes for a switch property. The switch's auxiliary elements will be set to NULL.
void IUFillText(IText *tp, const char *name, const char *label, const char *initialText)
Assign attributes for a text property. The text's auxiliary elements will be set to NULL.
void IUFillNumber(INumber *np, const char *name, const char *label, const char *format, double min, double max, double step, double value)
Assign attributes for a number property. The number's auxiliary elements will be set to NULL.
void IUSaveConfigText(FILE *fp, const ITextVectorProperty *tvp)
Add a text vector property value to the configuration file.
int crackIPState(const char *str, IPState *ip)
Extract property state (Idle, OK, Busy, Alert) from the supplied string.
void IUFillSwitchVector(ISwitchVectorProperty *svp, ISwitch *sp, int nsp, const char *dev, const char *name, const char *label, const char *group, IPerm p, ISRule r, double timeout, IPState s)
Assign attributes for a switch vector property. The vector's auxiliary elements will be set to NULL.
int IUUpdateSwitch(ISwitchVectorProperty *svp, ISState *states, char *names[], int n)
Update all switches in a switch vector property.
void IDSetNumber(const INumberVectorProperty *nvp, const char *fmt,...)
void IDSetSwitch(const ISwitchVectorProperty *svp, const char *fmt,...)
int IUUpdateText(ITextVectorProperty *tvp, char *texts[], char *names[], int n)
Update all text members in a text vector property.
void IDSnoopDevice(const char *snooped_device, const char *snooped_property)
Function a Driver calls to snoop on another Device. Snooped messages will then arrive via ISSnoopDevi...
int IUGetConfigNumber(const char *dev, const char *property, const char *member, double *value)
IUGetConfigNumber Opens configuration file and reads single number property.
int IUUpdateNumber(INumberVectorProperty *nvp, double values[], char *names[], int n)
Update all numbers in a number vector property.
void IDSetText(const ITextVectorProperty *tvp, const char *fmt,...)
#define LOGF_INFO(fmt,...)
#define LOGF_DEBUG(fmt,...)
#define LOG_ERROR(txt)
Shorter logging macros. In order to use these macros, the function (or method) "getDeviceName()" must...
#define LOGF_ERROR(fmt,...)
const char * findXMLAttValu(XMLEle *ep, const char *name)
Find an XML element's attribute value.
XMLEle * nextXMLEle(XMLEle *ep, int init)
Iterate an XML element for a list of nesetd XML elements.
char * pcdataXMLEle(XMLEle *ep)
Return the pcdata of an XML element.
std::unique_ptr< WatchDog > juli(new WatchDog())