From d687321b37e5a85f2f893542a462f44d27e47ec5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9mie=20Klein?= Date: Wed, 9 Jul 2025 23:49:24 +0200 Subject: [PATCH 1/3] Change log level from INFO to DEBUG for HTTP client connection and request logging in WeatherSafetyAlpaca --- drivers/weather/weather_safety_alpaca.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/weather/weather_safety_alpaca.cpp b/drivers/weather/weather_safety_alpaca.cpp index 4a3058ed4d..f0ca048518 100644 --- a/drivers/weather/weather_safety_alpaca.cpp +++ b/drivers/weather/weather_safety_alpaca.cpp @@ -172,14 +172,14 @@ bool WeatherSafetyAlpaca::makeAlpacaRequest(const std::string& path, nlohmann::j try { // Log connection details - LOGF_INFO("Creating HTTP client for host: %s, port: %s", + LOGF_DEBUG("Creating HTTP client for host: %s, port: %s", ServerAddressTP[0].getText(), ServerAddressTP[1].getText()); httplib::Client cli(ServerAddressTP[0].getText(), std::stoi(ServerAddressTP[1].getText())); // Log timeout settings - LOGF_INFO("Setting timeouts - Connection: %d sec, Read: %d sec", + LOGF_DEBUG("Setting timeouts - Connection: %d sec, Read: %d sec", static_cast(ConnectionSettingsNP[0].getValue()), static_cast(ConnectionSettingsNP[0].getValue())); @@ -187,7 +187,7 @@ bool WeatherSafetyAlpaca::makeAlpacaRequest(const std::string& path, nlohmann::j cli.set_read_timeout(static_cast(ConnectionSettingsNP[0].getValue())); // Log request details - LOGF_INFO("Making %s request to path: %s", + LOGF_DEBUG("Making %s request to path: %s", isPut ? "PUT" : "GET", path.c_str()); @@ -201,16 +201,16 @@ bool WeatherSafetyAlpaca::makeAlpacaRequest(const std::string& path, nlohmann::j } // Log response status and headers - LOGF_INFO("HTTP Status: %d", result->status); + LOGF_DEBUG("HTTP Status: %d", result->status); for (const auto& header : result->headers) { - LOGF_INFO("Response Header - %s: %s", + LOGF_DEBUG("Response Header - %s: %s", header.first.c_str(), header.second.c_str()); } // Log response body - LOGF_INFO("Response Body: %s", result->body.c_str()); + LOGF_DEBUG("Response Body: %s", result->body.c_str()); if (result->status != 200) { @@ -221,7 +221,7 @@ bool WeatherSafetyAlpaca::makeAlpacaRequest(const std::string& path, nlohmann::j response = nlohmann::json::parse(result->body); // Log parsed JSON response - LOGF_INFO("Parsed JSON response: %s", response.dump(2).c_str()); + LOGF_DEBUG("Parsed JSON response: %s", response.dump(2).c_str()); if (response["ErrorNumber"].get() != 0) { From d430d1376c05e129dbe37debb411db5443e8c644 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9mie=20Klein?= Date: Thu, 10 Jul 2025 20:55:52 +0200 Subject: [PATCH 2/3] Add WandererEclipse driver implementation This commit introduces the WandererEclipse driver, implementing the DustCapInterface for controlling a dust cap mechanism. Key features include property initialization, torque control settings, and serial communication for device status requests. The driver supports multiple torque levels and includes methods for parking and un-parking the cap, as well as handling firmware information. --- drivers/auxiliary/wanderer_eclipse.cpp | 265 +++++++++++++++++++++++++ drivers/auxiliary/wanderer_eclipse.h | 87 ++++++++ 2 files changed, 352 insertions(+) create mode 100644 drivers/auxiliary/wanderer_eclipse.cpp create mode 100644 drivers/auxiliary/wanderer_eclipse.h diff --git a/drivers/auxiliary/wanderer_eclipse.cpp b/drivers/auxiliary/wanderer_eclipse.cpp new file mode 100644 index 0000000000..00780680c9 --- /dev/null +++ b/drivers/auxiliary/wanderer_eclipse.cpp @@ -0,0 +1,265 @@ +/******************************************************************************* + Copyright(c) 2025 Jérémie Klein. All rights reserved. + + Wanderer Eclipse + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. + + The full GNU General Public License is included in this distribution in the + file called LICENSE. +*******************************************************************************/ + +#include "wanderer_eclipse.h" +#include "indicom.h" +#include "connectionplugins/connectionserial.h" +#include +#include +#include +#include +#include +#include +#include +#include + +static std::unique_ptr wanderereclipse(new WandererEclipse()); + +WandererEclipse::WandererEclipse() : DustCapInterface(this) +{ + setVersion(1, 0); +} + +const char *WandererEclipse::getDefaultName() +{ + return "WandererEclipse"; +} + +bool WandererEclipse::initProperties() +{ + INDI::DefaultDevice::initProperties(); + + // Dust cap interface + DI::initProperties(MAIN_CONTROL_TAB); + setDriverInterface(AUX_INTERFACE | DUSTCAP_INTERFACE); + addAuxControls(); + + // Torque property + TorqueSP[TORQUE_LOW].fill("TORQUE_LOW", "Low", ISS_OFF); + TorqueSP[TORQUE_MEDIUM].fill("TORQUE_MEDIUM", "Medium", ISS_ON); + TorqueSP[TORQUE_HIGH].fill("TORQUE_HIGH", "High", ISS_OFF); + TorqueSP.fill(getDeviceName(), "TORQUE", "Motor Torque", MAIN_CONTROL_TAB, IP_RW, 60, IPS_IDLE); + + // Firmware info + FirmwareTP[FIRMWARE_VERSION].fill("FIRMWARE_VERSION", "Firmware Version", "Unknown"); + FirmwareTP.fill(getDeviceName(), "FIRMWARE_INFO", "Firmware", MAIN_CONTROL_TAB, IP_RO, 60, IPS_IDLE); + + setDefaultPollingPeriod(2000); + + serialConnection = new Connection::Serial(this); + serialConnection->setDefaultBaudRate(Connection::Serial::B_19200); + serialConnection->registerHandshake([&]() { + return requestStatus(); + }); + registerConnection(serialConnection); + + return true; +} + +bool WandererEclipse::updateProperties() +{ + INDI::DefaultDevice::updateProperties(); + + if (isConnected()) + { + // Update firmware info + char firmwareStr[16]; + snprintf(firmwareStr, sizeof(firmwareStr), "%d", firmware); + FirmwareTP[FIRMWARE_VERSION].setText(firmwareStr); + defineProperty(FirmwareTP); + defineProperty(TorqueSP); + } + else + { + deleteProperty(FirmwareTP); + deleteProperty(TorqueSP); + } + + DI::updateProperties(); + return true; +} + +void WandererEclipse::ISGetProperties(const char *dev) +{ + INDI::DefaultDevice::ISGetProperties(dev); +} + +bool WandererEclipse::ISSnoopDevice(XMLEle *root) +{ + return INDI::DefaultDevice::ISSnoopDevice(root); +} + +bool WandererEclipse::ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n) +{ + if (dev && !strcmp(dev, getDeviceName())) + { + // Torque + if (TorqueSP.isNameMatch(name)) + { + int newTorque = -1; + for (int i = 0; i < n; i++) + { + if (states[i] == ISS_ON) + { + if (strcmp(names[i], "TORQUE_LOW") == 0) + newTorque = 0; + else if (strcmp(names[i], "TORQUE_MEDIUM") == 0) + newTorque = 1; + else if (strcmp(names[i], "TORQUE_HIGH") == 0) + newTorque = 2; + } + } + if (newTorque != -1 && newTorque != torqueLevel) + { + // Send torque command: 2000 (low), 2001 (medium), 2002 (high) + char cmd[8]; + snprintf(cmd, sizeof(cmd), "20%d", newTorque); + if (sendCommand(cmd)) + { + torqueLevel = newTorque; + for (int i = 0; i < 3; i++) + TorqueSP[i].setState(i == newTorque ? ISS_ON : ISS_OFF); + TorqueSP.setState(IPS_OK); + } + else + { + TorqueSP.setState(IPS_ALERT); + } + TorqueSP.apply(); + return true; + } + } + } + return INDI::DefaultDevice::ISNewSwitch(dev, name, states, names, n); +} + +bool WandererEclipse::toggleCover(bool open) +{ + char cmd[8] = {0}; + // 1001 = open, 1000 = close + snprintf(cmd, sizeof(cmd), "100%d", open ? 1 : 0); + return sendCommand(cmd); +} + +IPState WandererEclipse::ParkCap() +{ + ParkCapSP.setState(IPS_BUSY); + ParkCapSP.apply(); + if (toggleCover(false)) + return IPS_BUSY; + ParkCapSP.setState(IPS_ALERT); + ParkCapSP.apply(); + return IPS_ALERT; +} + +IPState WandererEclipse::UnParkCap() +{ + ParkCapSP.setState(IPS_BUSY); + ParkCapSP.apply(); + if (toggleCover(true)) + return IPS_BUSY; + ParkCapSP.setState(IPS_ALERT); + ParkCapSP.apply(); + return IPS_ALERT; +} + +bool WandererEclipse::sendCommand(const std::string &command) +{ + std::lock_guard lock(serialPortMutex); + int nbytes_written = 0, rc = -1; + std::string command_termination = "\n"; + if ((rc = tty_write_string(PortFD, (command + command_termination).c_str(), &nbytes_written)) != TTY_OK) + { + char errorMessage[MAXRBUF]; + tty_error_msg(rc, errorMessage, MAXRBUF); + LOGF_ERROR("Serial write error: %s", errorMessage); + return false; + } + return true; +} + +bool WandererEclipse::requestStatus() +{ + std::lock_guard lock(serialPortMutex); + PortFD = serialConnection->getPortFD(); + tcflush(PortFD, TCIOFLUSH); + char buffer[512] = {0}; + int nbytes_read = 0, rc = -1; + // Send status request command: 1500001\n + if (!sendCommand("1500001")) + return false; + + // Read response (should be a single line) + if ((rc = tty_read_section(PortFD, buffer, '\n', 2, &nbytes_read)) != TTY_OK) + { + if (rc == TTY_TIME_OUT) + { + LOG_DEBUG("Timeout reading from device, will try again later"); + return true; + } + char errorMessage[MAXRBUF]; + tty_error_msg(rc, errorMessage, MAXRBUF); + LOGF_ERROR("Failed to read data from device. Error: %s", errorMessage); + return false; + } + return parseDeviceStatus(buffer); +} + +bool WandererEclipse::parseDeviceStatus(const char *data) +{ + // Example: WandererTilterM54A***A***A***A***A\n + // TODO: Parse the status string according to the protocol in the image + // Set firmware, isOpen, etc. + // For now, just log the data + LOGF_DEBUG("Status Data: %s", data); + // Example parsing (to be implemented as per actual protocol) + return true; +} + +void WandererEclipse::updateStatus(bool isOpen, int torqueLevel) +{ + // Update internal state and UI if needed + this->isOpen = isOpen; + this->torqueLevel = torqueLevel; + // Update UI properties if needed +} + +void WandererEclipse::TimerHit() +{ + if (!isConnected()) + { + SetTimer(getPollingPeriod()); + return; + } + // Only request status on timer if needed (not real-time) + requestStatus(); + SetTimer(getPollingPeriod()); +} + +bool WandererEclipse::saveConfigItems(FILE *fp) +{ + INDI::DefaultDevice::saveConfigItems(fp); + TorqueSP.save(fp); + return true; +} \ No newline at end of file diff --git a/drivers/auxiliary/wanderer_eclipse.h b/drivers/auxiliary/wanderer_eclipse.h new file mode 100644 index 0000000000..5b6ecbf4ee --- /dev/null +++ b/drivers/auxiliary/wanderer_eclipse.h @@ -0,0 +1,87 @@ +/******************************************************************************* + Copyright(c) 2025 Jérémie Klein. All rights reserved. + + Wanderer Eclipse + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. + + The full GNU General Public License is included in this distribution in the + file called LICENSE. +*******************************************************************************/ + +#pragma once + +#include "defaultdevice.h" +#include "indidustcapinterface.h" +#include + +namespace Connection +{ +class Serial; +} + +class WandererEclipse : public INDI::DefaultDevice, public INDI::DustCapInterface +{ +public: + WandererEclipse(); + virtual ~WandererEclipse() = default; + + virtual bool initProperties() override; + virtual void ISGetProperties(const char *dev) override; + virtual bool ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n) override; + virtual bool updateProperties() override; + virtual bool ISSnoopDevice(XMLEle *root) override; + +protected: + // From Dust Cap + virtual IPState ParkCap() override; + virtual IPState UnParkCap() override; + const char *getDefaultName() override; + virtual bool saveConfigItems(FILE *fp) override; + virtual void TimerHit() override; + +private: + int firmware = 0; + bool toggleCover(bool open); + bool sendCommand(const std::string &command); + bool requestStatus(); + bool parseDeviceStatus(const char *data); + void updateStatus(bool isOpen, int torqueLevel); + + // Torque property (0: low, 1: medium, 2: high) + INDI::PropertySwitch TorqueSP{3}; + enum + { + TORQUE_LOW, + TORQUE_MEDIUM, + TORQUE_HIGH, + }; + + // Cap state + bool isOpen = false; + int torqueLevel = 1; // Default to medium + + // Firmware information + INDI::PropertyText FirmwareTP{1}; + enum + { + FIRMWARE_VERSION, + }; + + int PortFD{ -1 }; + Connection::Serial *serialConnection{ nullptr }; + std::timed_mutex serialPortMutex; +}; \ No newline at end of file From 27e2cf017395e7d057cf87d67d2a749b7b64eca8 Mon Sep 17 00:00:00 2001 From: grm Date: Fri, 11 Jul 2025 18:24:43 +0200 Subject: [PATCH 3/3] Add WandererCover Eclipse driver support This commit adds the WandererCover Eclipse driver to the project, including its source file and CMake configuration. The driver is designed to interface with the WandererCover Eclipse device, providing necessary properties and functionality. Additionally, a minor update was made to the torque property initialization in the WandererEclipse driver for improved handling. --- drivers.xml | 4 ++++ drivers/auxiliary/CMakeLists.txt | 8 ++++++++ drivers/auxiliary/wanderer_eclipse.cpp | 2 +- 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/drivers.xml b/drivers.xml index a3b81593c8..2c83a021e4 100644 --- a/drivers.xml +++ b/drivers.xml @@ -839,6 +839,10 @@ indi_wanderercover_v4_ec 1.2 + + indi_wanderer_eclipse + 1.2 + indi_dragon_light 1.0 diff --git a/drivers/auxiliary/CMakeLists.txt b/drivers/auxiliary/CMakeLists.txt index 833a59e5a8..1138122bfd 100644 --- a/drivers/auxiliary/CMakeLists.txt +++ b/drivers/auxiliary/CMakeLists.txt @@ -373,3 +373,11 @@ SET(ups_SRC add_executable(indi_ups_safety ${ups_SRC}) target_link_libraries(indi_ups_safety indidriver) install(TARGETS indi_ups_safety RUNTIME DESTINATION bin) + +# ########## WandererCover V4-EC############### +SET(indi_wanderer_eclipse_SRC + wanderer_eclipse.cpp) + +add_executable(indi_wanderer_eclipse ${indi_wanderer_eclipse_SRC}) +target_link_libraries(indi_wanderer_eclipse indidriver) +install(TARGETS indi_wanderer_eclipse RUNTIME DESTINATION bin) \ No newline at end of file diff --git a/drivers/auxiliary/wanderer_eclipse.cpp b/drivers/auxiliary/wanderer_eclipse.cpp index 00780680c9..853ea62358 100644 --- a/drivers/auxiliary/wanderer_eclipse.cpp +++ b/drivers/auxiliary/wanderer_eclipse.cpp @@ -59,7 +59,7 @@ bool WandererEclipse::initProperties() TorqueSP[TORQUE_LOW].fill("TORQUE_LOW", "Low", ISS_OFF); TorqueSP[TORQUE_MEDIUM].fill("TORQUE_MEDIUM", "Medium", ISS_ON); TorqueSP[TORQUE_HIGH].fill("TORQUE_HIGH", "High", ISS_OFF); - TorqueSP.fill(getDeviceName(), "TORQUE", "Motor Torque", MAIN_CONTROL_TAB, IP_RW, 60, IPS_IDLE); + TorqueSP.fill(getDeviceName(), "TORQUE", "Motor Torque", MAIN_CONTROL_TAB, IP_RW, ISR_1OFMANY, 60, IPS_IDLE); // Firmware info FirmwareTP[FIRMWARE_VERSION].fill("FIRMWARE_VERSION", "Firmware Version", "Unknown");