6#include "clientmanagerlite.h"
10#include "inditelescopelite.h"
12#include "kstarslite.h"
14#include "skymaplite.h"
15#include "fitsviewer/fitsdata.h"
16#include "kstarslite/imageprovider.h"
17#include "kstarslite/skyitems/telescopesymbolsitem.h"
19#include <KLocalizedString>
21#include <QApplication>
24#include <QImageReader>
26#include <QJsonDocument>
28#include <QQmlApplicationEngine>
30#include <QTemporaryFile>
32const char *libindi_strings_context =
"string from libindi, used in the config dialog";
35#include "libraw/libraw.h"
38DeviceInfoLite::DeviceInfoLite(INDI::BaseDevice *dev) : device(dev)
42DeviceInfoLite::~DeviceInfoLite()
46ClientManagerLite::ClientManagerLite(
QQmlContext& main_context) : context(main_context)
49 defaultImageType =
".jpeg";
52 qmlRegisterType<TelescopeLite>(
"TelescopeLiteEnums", 1, 0,
"TelescopeNS");
53 qmlRegisterType<TelescopeLite>(
"TelescopeLiteEnums", 1, 0,
"TelescopeWE");
54 qmlRegisterType<TelescopeLite>(
"TelescopeLiteEnums", 1, 0,
"TelescopeCommand");
58ClientManagerLite::~ClientManagerLite()
62bool ClientManagerLite::setHost(
const QString &ip,
unsigned int port)
67 qDebug() << ip << port;
72 setLastUsedServer(ip);
73 setLastUsedPort(port);
81void ClientManagerLite::disconnectHost()
90 if (webMProfilesReply.get() !=
nullptr)
93 QString urlStr(
QString(
"http://%1:%2/api/profiles").arg(ip).arg(port));
96 webMProfilesReply.reset(manager.
get(request));
100 setLastUsedServer(ip);
101 setLastUsedWebManagerPort(port);
106 if (webMStartProfileReply.get() !=
nullptr)
109 QString urlStr(
"http://%1:%2/api/server/start/%3");
111 urlStr = urlStr.
arg(getLastUsedServer()).
arg(getLastUsedWebManagerPort()).
arg(profile);
123 if (webMStopProfileReply.get() !=
nullptr)
126 QString urlStr(
QString(
"http://%1:%2/api/server/stop").arg(getLastUsedServer()).arg(getLastUsedWebManagerPort()));
138 if (webMProfilesReply.get() !=
nullptr)
140 qWarning(
"Web Manager profile query error: %d", (
int)code);
142 webMProfilesReply.release()->deleteLater();
145 if (webMStatusReply.get() !=
nullptr)
147 qWarning(
"Web Manager status query error: %d", (
int)code);
149 webMStatusReply.release()->deleteLater();
152 if (webMStopProfileReply.get() !=
nullptr)
154 qWarning(
"Web Manager stop active profile error: %d", (
int)code);
156 webMStopProfileReply.release()->deleteLater();
159 if (webMStartProfileReply.get() !=
nullptr)
161 qWarning(
"Web Manager start active profile error: %d", (
int)code);
163 webMStartProfileReply.release()->deleteLater();
171 if (webMProfilesReply.get() !=
nullptr)
173 QByteArray responseData = webMProfilesReply->readAll();
176 webMProfilesReply.release()->deleteLater();
184 webMProfiles.
clear();
185 for (
int i = 0; i < array.
size(); ++i)
189 webMProfiles += array.
at(i).
toObject()[
"name"].toString();
193 QString urlStr(
QString(
"http://%1:%2/api/server/status").arg(getLastUsedServer()).arg(getLastUsedWebManagerPort()));
196 webMStatusReply.reset(manager.
get(request));
203 if (webMStatusReply.get() !=
nullptr)
205 QByteArray responseData = webMStatusReply->readAll();
208 webMStatusReply.release()->deleteLater();
217 if (!
object.contains(
"status") || !
object.contains(
"active_profile"))
222 QString statusStr =
object[
"status"].toString();
223 QString activeProfileStr =
object[
"active_profile"].toString();
225 indiControlPage->
setProperty(
"webMBrowserButtonVisible",
true);
226 indiControlPage->
setProperty(
"webMStatusTextVisible",
true);
227 if (statusStr ==
"True")
230 indiControlPage->
setProperty(
"webMStatusText",
i18n(
"Web Manager Status: Online"));
231 indiControlPage->
setProperty(
"webMActiveProfileText",
232 i18n(
"Active Profile: %1", activeProfileStr));
233 indiControlPage->
setProperty(
"webMActiveProfileLayoutVisible",
true);
234 indiControlPage->
setProperty(
"webMProfileListVisible",
false);
237 indiControlPage->
setProperty(
"webMStatusText",
i18n(
"Web Manager Status: Offline"));
238 indiControlPage->
setProperty(
"webMActiveProfileLayoutVisible",
false);
240 indiControlPage->
setProperty(
"webMProfileListVisible",
true);
245 if (webMStopProfileReply.get() !=
nullptr)
247 webMStopProfileReply.release()->deleteLater();
249 indiControlPage->
setProperty(
"webMStatusTextVisible",
true);
250 indiControlPage->
setProperty(
"webMActiveProfileLayoutVisible",
false);
252 indiControlPage->
setProperty(
"webMProfileListVisible",
true);
256 if (webMStartProfileReply.get() !=
nullptr)
258 webMStartProfileReply.release()->deleteLater();
260 QString urlStr(
"http://%1:%2/api/server/status");
262 urlStr = urlStr.
arg(getLastUsedServer()).
arg(getLastUsedWebManagerPort());
265 webMStatusReply.reset(manager.
get(request));
278 for (
auto& devInfo : m_devices)
280 if (devInfo->telescope.get())
282 return devInfo->telescope.get();
288void ClientManagerLite::setConnectedHost(
const QString &connectedHost)
290 m_connectedHost = connectedHost;
291 setConnected(m_connectedHost.
size() > 0);
293 emit connectedHostChanged(connectedHost);
296void ClientManagerLite::setConnected(
bool connected)
298 m_connected = connected;
299 emit connectedChanged(connected);
304 foreach (DeviceInfoLite *devInfo, m_devices)
306 if (devInfo->device->getDeviceName() == device)
308 INDI::Property prop = devInfo->device->getProperty(property.
toLatin1());
311 IPState state = prop->getState();
314 ILight *lights = prop->getLight()->lp;
315 for (
int i = 0; i < prop->getLight()->nlp; i++)
317 if (lights[i].name == name)
322 if (i == prop->getLight()->nlp - 1)
354void ClientManagerLite::buildTextGUI(Property *property)
356 auto tvp =
property->getText();
360 for (
const auto &it: *tvp)
374 label = tvp->getName();
384 switch (property->getPermission())
401 emit createINDIText(property->getDeviceName(), property->getName(), label, name, text, read, write);
405void ClientManagerLite::buildNumberGUI(Property *property)
407 auto nvp =
property->getNumber();
412 for (
const auto &it: nvp)
415 char iNumber[MAXINDIFORMAT];
429 label = np->getName();
431 numberFormat(iNumber, np.getFormat(), np.getValue());
443 if (it.getStep() != 0 && (it.getMax() - it.getMin()) / it.getStep() <= 100)
446 switch (property->getPermission())
463 emit createINDINumber(property->getDeviceName(), property->getName(), label, name, text, read, write,
468void ClientManagerLite::buildMenuGUI(INDI::Property property)
473 auto svp =
property->getSwitch();
480 buildSwitch(
false, &it, property);
500void ClientManagerLite::buildSwitchGUI(INDI::Property property, PGui guiType)
502 auto svp =
property->getSwitch();
503 bool exclusive =
false;
508 if (guiType == PG_BUTTONS)
510 if (svp->getRule() == ISR_1OFMANY)
515 else if (guiType == PG_RADIO)
523 buildSwitch(
true, &it, property, exclusive, guiType);
527void ClientManagerLite::buildSwitch(
bool buttonGroup, ISwitch *sw, INDI::Property property,
bool exclusive,
533 if (label ==
"(I18N_EMPTY_MESSAGE)")
540 if (label ==
"(I18N_EMPTY_MESSAGE)")
545 bool isSelected =
false;
548 emit createINDIMenu(property->getDeviceName(), property->getName(), label, sw->name, isSelected);
554 if (sw->svp->p == IP_RO)
555 enabled = (sw->s == ISS_ON);
560 emit createINDIButton(property->getDeviceName(), property->getName(), label, name,
true,
true, exclusive,
561 sw->s == ISS_ON, enabled);
565 emit createINDIRadio(property->getDeviceName(), property->getName(), label, name,
true,
true, exclusive,
566 sw->s == ISS_ON, enabled);
586void ClientManagerLite::buildLightGUI(INDI::Property property)
588 auto lvp =
property->getLight();
598 if (label ==
"(I18N_EMPTY_MESSAGE)")
599 label = it.getLabel();
602 label =
i18nc(libindi_strings_context, it.getName());
604 if (label ==
"(I18N_EMPTY_MESSAGE)")
605 label = it.getName();;
607 emit createINDILight(property->getDeviceName(), property->getName(), label, name);
649void ClientManagerLite::sendNewINDISwitch(
const QString &deviceName,
const QString &propName,
const QString &name)
651 foreach (DeviceInfoLite *devInfo, m_devices)
653 INDI::BaseDevice *device = devInfo->device;
654 if (device->getDeviceName() == deviceName)
656 auto property = device->getProperty(propName.
toLatin1());
659 auto svp =
property->getSwitch();
669 if (sp->isNameMatch(
"CONNECT"))
672 sp->setState(ISS_ON);
675 if (svp->getRule() == ISR_1OFMANY)
678 sp->setState(ISS_ON);
682 if (svp->getRule() == ISR_ATMOST1)
684 ISState prev_state = sp->getState();
686 sp->setState(prev_state);
689 sp->setState(sp->getState() == ISS_ON ? ISS_OFF : ISS_ON);
697void ClientManagerLite::sendNewINDINumber(
const QString &deviceName,
const QString &propName,
const QString &numberName,
700 foreach (DeviceInfoLite *devInfo, m_devices)
702 INDI::BaseDevice *device = devInfo->device;
703 if (device->getDeviceName() == deviceName)
705 auto np = device->getNumber(propName.
toLatin1());
708 auto n = np->findWIdgetByName(numberName.
toLatin1());
716 qDebug() <<
"Could not find property: " << deviceName <<
"." << propName <<
"." << numberName;
720 qDebug() <<
"Could not find property: " << deviceName <<
"." << propName <<
"." << numberName;
726void ClientManagerLite::sendNewINDIText(
const QString &deviceName,
const QString &propName,
const QString &fieldName,
729 foreach (DeviceInfoLite *devInfo, m_devices)
731 INDI::BaseDevice *device = devInfo->device;
732 if (device->getDeviceName() == deviceName)
734 auto tp = device->getText(propName.
toLatin1());
737 auto t = tp->findWidgetByName(fieldName.
toLatin1());
745 qDebug() <<
"Could not find property: " << deviceName <<
"." << propName <<
"." << fieldName;
749 qDebug() <<
"Could not find property: " << deviceName <<
"." << propName <<
"." << fieldName;
755void ClientManagerLite::sendNewINDISwitch(
const QString &deviceName,
const QString &propName,
int index)
759 foreach (DeviceInfoLite *devInfo, m_devices)
761 INDI::BaseDevice *device = devInfo->device;
762 if (device->getDeviceName() == deviceName)
764 auto property = device->getProperty(propName.
toStdString().c_str());
767 auto svp =
property->getSwitch();
772 if (index >= svp->count())
775 auto sp = svp->at(index);
778 sp->setState(ISS_ON);
790 QString const fileEnding =
"kstars-lite-" + dateTime;
796 i18n(
"JPEG (*.jpeg);;JPG (*.jpg);;PNG (*.png);;BMP (*.bmp)"));
800 if (displayImage.
save(filename))
802 emit newINDIMessage(
"File " + filename +
" was successfully saved");
806 emit newINDIMessage(
"Couldn't save file " + filename);
810bool ClientManagerLite::isDeviceConnected(
const QString &deviceName)
812 INDI::BaseDevice *device = getDevice(deviceName.
toStdString().c_str());
814 if (device !=
nullptr)
816 return device->isConnected();
821void ClientManagerLite::connectNewDevice(
const QString& device_name)
823 connectDevice(qPrintable(device_name));
826void ClientManagerLite::newDevice(INDI::BaseDevice *dp)
828 setBLOBMode(B_ALSO, dp->getDeviceName());
830 QString deviceName = dp->getDeviceName();
834 qWarning() <<
"Received invalid device with empty name! Ignoring the device...";
838 if (Options::verboseLogging())
839 qDebug() <<
"Received new device " << deviceName;
840 emit newINDIDevice(deviceName);
842 DeviceInfoLite *devInfo =
new DeviceInfoLite(dp);
845 m_devices.append(devInfo);
850void ClientManagerLite::removeDevice(BaseDevice *dp)
852 emit removeINDIDevice(
QString(dp->getDeviceName()));
855void ClientManagerLite::newProperty(INDI::Property property)
857 QString deviceName =
property->getDeviceName();
859 QString groupName =
property->getGroupName();
862 DeviceInfoLite *devInfo =
nullptr;
864 foreach (DeviceInfoLite *di, m_devices)
866 if (di->device->getDeviceName() == deviceName)
874 if ((!strcmp(property->getName(),
"EQUATORIAL_EOD_COORD") ||
875 !strcmp(property->getName(),
"EQUATORIAL_COORD") ||
876 !strcmp(property->getName(),
"HORIZONTAL_COORD")))
878 devInfo->telescope.reset(
new TelescopeLite(devInfo->device));
879 m_telescope = devInfo->telescope.get();
880 emit telescopeAdded(m_telescope);
883 if (devInfo->telescope->isConnected())
885 emit deviceConnected(devInfo->telescope->getDeviceName(),
true);
886 emit telescopeConnected(devInfo->telescope.get());
891 emit newINDIProperty(deviceName, name, groupName, type, label);
893 switch (property->getType())
896 if (property->getSwitch()->r == ISR_NOFMANY)
898 else if (property->getSwitch()->nsp > 4)
901 guiType = PG_BUTTONS;
903 if (guiType == PG_MENU)
904 buildMenuGUI(property);
906 buildSwitchGUI(property, guiType);
910 buildTextGUI(property);
913 buildNumberGUI(property);
917 buildLightGUI(property);
929void ClientManagerLite::removeProperty(INDI::Property property)
931 if (property ==
nullptr)
934 emit removeINDIProperty(property->getDeviceName(), property->getGroupName(), property->getName());
936 DeviceInfoLite *devInfo =
nullptr;
937 foreach (DeviceInfoLite *di, m_devices)
939 if (di->device == property->getBaseDevice())
947 if ((!strcmp(property->getName(),
"EQUATORIAL_EOD_COORD") || !strcmp(property->getName(),
"HORIZONTAL_COORD")))
949 if (devInfo->telescope.get() !=
nullptr)
951 emit telescopeRemoved(devInfo->telescope.get());
958void ClientManagerLite::newBLOB(IBLOB *bp)
960 processBLOBasCCD(bp);
961 emit newLEDState(bp->bvp->device, bp->name);
964bool ClientManagerLite::processBLOBasCCD(IBLOB *bp)
977 QString deviceName = bp->bvp->device;
984 else if (format.contains(
"fits"))
986 else if (format.contains(
"cr2"))
989 if (BType == BLOB_OTHER)
1002 if (
QDir(currentDir).exists() ==
false)
1005 QString filename(currentDir +
'/');
1009 tmpFile.setAutoRemove(
false);
1011 if (!tmpFile.open())
1013 qDebug() <<
"ISD:CCD Error: Unable to open " << filename <<
endl;
1020 for (nr = 0; nr < (int)bp->size; nr += n)
1021 n = out.writeRawData(
static_cast<char *
>(bp->blob) + nr, bp->size - nr);
1025 filename = tmpFile.fileName();
1032 strncpy(BLOBFilename, filename.toLatin1(), MAXINDIFILENAME);
1033 bp->aux2 = BLOBFilename;
1045 if (BType == BLOB_IMAGE || BType == BLOB_CR2)
1047 if (BType == BLOB_CR2)
1050 LibRaw RawProcessor;
1051#define OUT RawProcessor.imgdata.params
1053 OUT.use_camera_wb = 1;
1058 QString rawFileName = filename;
1062 jpgPreview.setAutoRemove(
false);
1065 QString jpeg_filename = jpgPreview.fileName();
1067 RawProcessor.open_file(filename.toLatin1());
1068 RawProcessor.unpack();
1069 RawProcessor.dcraw_process();
1070 RawProcessor.dcraw_ppm_tiff_writer(jpeg_filename.
toLatin1());
1072 filename = jpeg_filename;
1078 QString rawFileName = filename;
1082 jpgPreview.setAutoRemove(
false);
1085 QString jpeg_filename = jpgPreview.fileName();
1087 QString cmd =
QString(
"/bin/sh -c \"dcraw -c -q 0 -w -H 5 -b 8 %1 | cjpeg -quality 80 > %2\"")
1089 .
arg(jpeg_filename);
1093 filename = jpeg_filename;
1097 emit newINDIMessage(
1098 i18n(
"Unable to find dcraw and cjpeg. Please install the required tools to convert CR2 to JPEG."));
1099 emit newINDIBLOBImage(deviceName,
false);
1105 displayImage.
load(filename);
1108 emit newINDIBLOBImage(deviceName,
true);
1111 else if (BType == BLOB_FITS)
1113 displayImage = FITSData::FITSToImage(filename);
1116 emit newINDIBLOBImage(deviceName,
true);
1119 emit newINDIBLOBImage(deviceName,
false);
1123void ClientManagerLite::newSwitch(ISwitchVectorProperty *svp)
1125 for (
int i = 0; i < svp->nsp; ++i)
1127 ISwitch *sw = &(svp->sp[i]);
1130 emit deviceConnected(svp->device, sw->s == ISS_ON);
1131 if (m_telescope && m_telescope->getDeviceName() == svp->device)
1133 if (sw->s == ISS_ON)
1135 emit telescopeConnected(m_telescope);
1137 emit telescopeDisconnected();
1143 emit newINDISwitch(svp->device, svp->name, sw->name, sw->s == ISS_ON);
1144 emit newLEDState(svp->device, svp->name);
1149void ClientManagerLite::newNumber(INumberVectorProperty *nvp)
1151 if ((!strcmp(nvp->name,
"EQUATORIAL_EOD_COORD") || !strcmp(nvp->name,
"HORIZONTAL_COORD")))
1156 QString deviceName = nvp->device;
1158 for (
int i = 0; i < nvp->nnp; ++i)
1160 INumber num = nvp->np[i];
1161 char buf[MAXINDIFORMAT];
1162 numberFormat(buf, num.format, num.value);
1163 QString numberName = num.name;
1165 emit newINDINumber(deviceName, propName, numberName,
QString(buf).trimmed());
1166 emit newLEDState(deviceName, propName);
1170void ClientManagerLite::newText(ITextVectorProperty *tvp)
1172 QString deviceName = tvp->device;
1174 for (
int i = 0; i < tvp->ntp; ++i)
1176 IText text = tvp->tp[i];
1177 QString fieldName = text.name;
1179 emit newINDIText(deviceName, propName, fieldName, text.text);
1180 emit newLEDState(deviceName, propName);
1184void ClientManagerLite::newLight(ILightVectorProperty *lvp)
1186 emit newINDILight(lvp->device, lvp->name);
1187 emit newLEDState(lvp->device, lvp->name);
1190void ClientManagerLite::newMessage(INDI::BaseDevice *dp,
int messageID)
1195void ClientManagerLite::serverDisconnected(
int exit_code)
1199 setConnected(
false);
1202void ClientManagerLite::clearDevices()
1205 foreach (DeviceInfoLite *devInfo, m_devices)
1207 if (devInfo->telescope.get() !=
nullptr)
1209 emit telescopeRemoved(devInfo->telescope.get());
1216QString ClientManagerLite::getLastUsedServer()
1218 return Options::lastServer();
1221void ClientManagerLite::setLastUsedServer(
const QString &server)
1223 if (getLastUsedServer() != server)
1225 Options::setLastServer(server);
1226 lastUsedServerChanged();
1230int ClientManagerLite::getLastUsedPort()
1232 return Options::lastServerPort();
1235void ClientManagerLite::setLastUsedPort(
int port)
1237 if (getLastUsedPort() != port)
1239 Options::setLastServerPort(port);
1240 lastUsedPortChanged();
1244int ClientManagerLite::getLastUsedWebManagerPort()
1246 return Options::lastWebManagerPort();
1249void ClientManagerLite::setLastUsedWebManagerPort(
int port)
1251 if (getLastUsedWebManagerPort() != port)
1253 Options::setLastWebManagerPort(port);
1254 lastUsedWebManagerPortChanged();
Q_INVOKABLE QString syncLED(const QString &device, const QString &property, const QString &name="")
syncLED
Q_INVOKABLE void webManagerReplyError(QNetworkReply::NetworkError code)
Handle the errors of the async Web Manager requests.
Q_INVOKABLE void webManagerReplyFinished()
Do actions when async Web Manager requests are finished.
Q_INVOKABLE void getWebManagerProfiles(const QString &ip, unsigned int port)
Get the profiles from Web Manager.
Q_INVOKABLE bool saveDisplayImage()
saveDisplayImage
Q_INVOKABLE void startWebManagerProfile(const QString &profile)
Start an INDI server with a Web Manager profile.
Q_INVOKABLE void stopWebManagerProfile()
Stop the INDI server with an active Web Manager profile.
void addImage(const QString &id, QImage image)
Add image to the list of images with the given id.
static KStarsLite * Instance()
ImageProvider * imageProvider() const
void notificationMessage(QString msg)
Once this signal is emitted, notification with text msg will appear on the screen.
device handle controlling telescope.
QString i18nc(const char *context, const char *text, const TYPE &arg...)
QString i18n(const char *text, const TYPE &arg...)
Type type(const QSqlDatabase &db)
QVariant read(const QByteArray &data, int versionOverride=0)
QString name(StandardAction id)
QString label(StandardShortcut id)
const char * constData() const const
QDateTime currentDateTime()
QString toString(QStringView format, QCalendar cal) const const
bool mkpath(const QString &dirPath) const const
QString path() const const
QString getSaveFileName(QWidget *parent, const QString &caption, const QString &dir, const QString &filter, QString *selectedFilter, Options options)
QString filePath() const const
bool load(QIODevice *device, const char *format)
bool save(QIODevice *device, const char *format, int quality) const const
QJsonValue at(qsizetype i) const const
qsizetype size() const const
QJsonArray array() const const
QJsonDocument fromJson(const QByteArray &json, QJsonParseError *error)
bool isArray() const const
bool contains(QLatin1StringView key) const const
bool isObject() const const
QJsonObject toObject() const const
QNetworkReply * get(const QNetworkRequest &request)
QNetworkReply * post(const QNetworkRequest &request, QHttpMultiPart *multiPart)
bool setProperty(const char *name, QVariant &&value)
void start(OpenMode mode)
bool waitForFinished(int msecs)
void setContextProperty(const QString &name, QObject *value)
QString findExecutable(const QString &executableName, const QStringList &paths)
QString arg(Args &&... args) const const
bool endsWith(QChar c, Qt::CaseSensitivity cs) const const
QString fromStdString(const std::string &str)
bool isEmpty() const const
qsizetype lastIndexOf(QChar ch, Qt::CaseSensitivity cs) const const
QString number(double n, char format, int precision)
QString & remove(QChar ch, Qt::CaseSensitivity cs)
qsizetype size() const const
QByteArray toLatin1() const const
QString toLower() const const
std::string toStdString() const const
QByteArray toUtf8() const const
QTextStream & endl(QTextStream &stream)
QVariant fromValue(T &&value)