32 #include <libnova/julian_day.h>
39 #include <arpa/inet.h>
40 #include <sys/socket.h>
43 #include <netinet/in.h>
48 static std::unique_ptr<SkySafari> tommyGoodBoy(
new SkySafari());
65 bool rc = startServer();
68 skySafariClient->setMount(ActiveDeviceT[ACTIVE_TELESCOPE].text);
69 skySafariClient->setServer(SettingsT[INDISERVER_HOST].text, std::stoi(SettingsT[INDISERVER_PORT].text));
70 skySafariClient->connectServer();
86 IUFillText(&SettingsT[INDISERVER_HOST],
"INDISERVER_HOST",
"indiserver host",
"localhost");
87 IUFillText(&SettingsT[INDISERVER_PORT],
"INDISERVER_PORT",
"indiserver port",
"7624");
88 IUFillText(&SettingsT[SKYSAFARI_PORT],
"SKYSAFARI_PORT",
"SkySafari port",
"9624");
98 IUFillText(&ActiveDeviceT[ACTIVE_TELESCOPE],
"ACTIVE_TELESCOPE",
"Telescope",
"Telescope Simulator");
124 if (!strcmp(SettingsTP.
name, name))
132 if (!strcmp(ActiveDeviceTP.
name, name))
148 if (!strcmp(ServerControlSP.
name, name))
152 if (!strcmp(
IUFindOnSwitchName(states, names, n), ServerControlS[SERVER_ENABLE].name))
155 if (ServerControlS[SERVER_ENABLE].s ==
ISS_ON)
167 if (!strcmp(
IUFindOnSwitchName(states, names, n), ServerControlS[SERVER_DISABLE].name))
170 if (ServerControlS[SERVER_DISABLE].s ==
ISS_ON)
206 struct sockaddr_in cli_socket;
211 cli_len =
sizeof(cli_socket);
212 cli_fd = accept(lsocket, (
struct sockaddr *)&cli_socket, &cli_len);
213 if (cli_fd < 0 && (
errno == EAGAIN ||
errno == EWOULDBLOCK))
230 if ((flags = fcntl(clientFD, F_GETFL, 0)) < 0)
232 LOGF_ERROR(
"Error connecting to SkySafari. F_GETFL: %s", strerror(
errno));
236 if (fcntl(clientFD, F_SETFL, flags | O_NONBLOCK) < 0)
238 LOGF_ERROR(
"Error connecting to SkySafari. F_SETFL: %s", strerror(
errno));
242 if (isSkySafariConnected ==
false)
244 LOG_INFO(
"Connected to SkySafari.");
245 isSkySafariConnected =
true;
252 int rc = read(clientFD,
buffer, 64);
255 std::vector<std::string> commands =
split(
buffer,
'#');
256 for (std::string
cmd : commands)
277 bool SkySafari::startServer()
279 struct sockaddr_in serv_socket;
285 if ((sfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
292 if ((flags = fcntl(sfd, F_GETFL, 0)) < 0)
298 if (fcntl(sfd, F_SETFL, flags | O_NONBLOCK) < 0)
304 memset(&serv_socket, 0,
sizeof(serv_socket));
305 serv_socket.sin_family = AF_INET;
306 serv_socket.sin_addr.s_addr = htonl(INADDR_ANY);
307 serv_socket.sin_port = htons((
unsigned short)atoi(SettingsT[SKYSAFARI_PORT].text));
308 if (setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &reuse,
sizeof(reuse)) < 0)
314 if (::bind(sfd, (
struct sockaddr *)&serv_socket,
sizeof(serv_socket)) < 0)
321 if (listen(sfd, 5) < 0)
329 "SkySafari Server is running. Connect the App now to this machine using SkySafari LX200 driver.");
333 bool SkySafari::stopServer()
340 clientFD = lsocket = -1;
345 void SkySafari::processCommand(std::string
cmd)
349 if (skySafariClient->isConnected() ==
false)
351 LOG_ERROR(
"Internal client is not connected! Please make sure the mount name is set in the Options tab. Disconnect and reconnect to try again.");
356 if (
cmd.compare(0, 2,
"St") == 0)
359 if (sscanf(
cmd.c_str(),
"St%d%*c%d", &dd, &mm) == 2)
362 siteLatitude = dd + mm / 60.0;
369 sendGeographicCoords();
372 else if (
cmd.compare(0, 2,
"Sg") == 0)
375 if (sscanf(
cmd.c_str(),
"Sg%d%*c%d", &ddd, &mm) == 2)
377 haveLongitude =
true;
378 siteLongitude = ddd + mm / 60.0;
381 siteLongitude = 360 - siteLongitude;
388 sendGeographicCoords();
391 else if (
cmd.compare(0, 2,
"SG") == 0)
394 if (sscanf(
cmd.c_str(),
"SG%d", &ofs) == 1)
400 haveUTCoffset =
true;
410 else if (
cmd.compare(0, 2,
"SL") == 0)
413 if (sscanf(
cmd.c_str(),
"SL%d:%d:%d", &hh, &mm, &ss) == 3)
415 LOGF_DEBUG(
"TIME : %02d:%02d:%02d", hh, mm, ss);
430 else if (
cmd.compare(0, 2,
"SC") == 0)
433 if (sscanf(
cmd.c_str(),
"SC%d/%d/%d", &mm, &dd, &yyyy) == 3)
435 LOGF_DEBUG(
"DATE : %02d-%02d-%02d", yyyy, mm, dd);
450 else if (
cmd ==
"GR")
452 auto eqCoordsNP = skySafariClient->getEquatorialCoords();
453 if (eqCoordsNP ==
nullptr)
455 LOG_WARN(
"Unable to communicate with mount, is mount turned on and connected?");
460 char output[32] = { 0 };
462 snprintf(output, 32,
"%02d:%02d:%02d#", hh, mm, ss);
463 sendSkySafari(output);
466 else if (
cmd ==
"GD")
468 auto eqCoordsNP = skySafariClient->getEquatorialCoords();
469 if (eqCoordsNP ==
nullptr)
471 LOG_WARN(
"Unable to communicate with mount, is mount turned on and connected?");
475 char output[32] = { 0 };
477 snprintf(output, 32,
"%c%02d:%02d:%02d#", (eqCoordsNP->at(
AXIS_DE)->getValue() >= 0) ?
'+' :
'-',
478 std::abs(dd), mm, ss);
479 sendSkySafari(output);
482 else if (
cmd.compare(0, 2,
"Sr") == 0)
489 else if (
cmd.compare(0, 2,
"Sd") == 0)
496 else if (
cmd ==
"MS")
498 auto gotoModeSP = skySafariClient->getGotoMode();
499 if (gotoModeSP ==
nullptr)
501 sendSkySafari(
"2<Not Supported>#");
506 auto trackSW = gotoModeSP->findWidgetByName(
"TRACK");
507 if (trackSW ==
nullptr)
509 sendSkySafari(
"2<Not Supported>#");
514 trackSW->setState(
ISS_ON);
515 skySafariClient->sendGotoMode();
517 auto eqCoordsNP = skySafariClient->getEquatorialCoords();
518 eqCoordsNP->at(
AXIS_RA)->setValue(RA);
519 eqCoordsNP->at(
AXIS_DE)->setValue(DE);
520 skySafariClient->sendEquatorialCoords();
525 else if (
cmd ==
"CM")
527 auto gotoModeSP = skySafariClient->getGotoMode();
528 if (gotoModeSP ==
nullptr)
530 sendSkySafari(
"Not Supported#");
535 auto syncSW = gotoModeSP->findWidgetByName(
"SYNC");
536 if (syncSW ==
nullptr)
538 sendSkySafari(
"Not Supported#");
544 skySafariClient->sendGotoMode();
546 auto eqCoordsNP = skySafariClient->getEquatorialCoords();
547 eqCoordsNP->at(
AXIS_RA)->setValue(RA);
548 eqCoordsNP->at(
AXIS_DE)->setValue(DE);
549 skySafariClient->sendEquatorialCoords();
551 sendSkySafari(
" M31 EX GAL MAG 3.5 SZ178.0'#");
556 skySafariClient->abort();
559 else if (
cmd ==
"RG")
561 skySafariClient->setSlewRate(0);
564 else if (
cmd ==
"RC")
566 skySafariClient->setSlewRate(1);
569 else if (
cmd ==
"RM")
571 skySafariClient->setSlewRate(2);
574 else if (
cmd ==
"RS")
576 skySafariClient->setSlewRate(3);
579 else if (
cmd ==
"Mn")
581 auto motionNSNP = skySafariClient->getMotionNS();
585 motionNSNP->at(0)->setState(
ISS_ON);
586 skySafariClient->setMotionNS();
590 else if (
cmd ==
"Qn")
592 auto motionNSNP = skySafariClient->getMotionNS();
596 skySafariClient->setMotionNS();
600 else if (
cmd ==
"Ms")
602 auto motionNSNP = skySafariClient->getMotionNS();
606 motionNSNP->at(1)->setState(
ISS_ON);
607 skySafariClient->setMotionNS();
611 else if (
cmd ==
"Qs")
613 auto motionNSNP = skySafariClient->getMotionNS();
617 skySafariClient->setMotionNS();
621 else if (
cmd ==
"Mw")
623 auto motionWENP = skySafariClient->getMotionWE();
627 motionWENP->at(0)->setState(
ISS_ON);
628 skySafariClient->setMotionWE();
632 else if (
cmd ==
"Qw")
634 auto motionWENP = skySafariClient->getMotionWE();
638 skySafariClient->setMotionWE();
642 else if (
cmd ==
"Me")
644 auto motionWENP = skySafariClient->getMotionWE();
648 motionWENP->at(1)->setState(
ISS_ON);
649 skySafariClient->setMotionWE();
653 else if (
cmd ==
"Qe")
655 auto motionWENP = skySafariClient->getMotionWE();
659 skySafariClient->setMotionWE();
664 void SkySafari::sendGeographicCoords()
666 auto geographicCoords = skySafariClient->getGeographiCoords();
667 if (geographicCoords && haveLatitude && haveLongitude)
669 auto latitude = geographicCoords->findWidgetByName(
"LAT");
670 auto longitude = geographicCoords->findWidgetByName(
"LONG");
671 if (latitude && longitude)
673 latitude->setValue(siteLatitude);
674 longitude->setValue(siteLongitude);
675 skySafariClient->sendGeographicCoords();
678 haveLatitude = haveLongitude =
false;
683 bool SkySafari::sendSkySafari(
const char *message)
687 int bytesWritten = 0, totalBytes = strlen(message);
689 while (bytesWritten < totalBytes)
691 int bytesSent = write(clientFD, message, totalBytes - bytesWritten);
693 bytesWritten += bytesSent;
704 void SkySafari::sendUTCtimedate()
706 auto timeUTC = skySafariClient->getTimeUTC();
707 if (timeUTC && haveUTCoffset && haveUTCtime && haveUTCdate)
714 ln_zonedate zonedate;
716 zonedate.years = yyyy;
717 zonedate.months = timeMonth;
718 zonedate.days = timeDay;
719 zonedate.hours = timeHour;
720 zonedate.minutes = timeMin;
721 zonedate.seconds = timeSec;
722 zonedate.gmtoff = timeUTCOffset * 3600.0;
724 ln_zonedate_to_date(&zonedate, &utcdate);
726 char bufDT[32] = {0};
727 char bufOff[8] = {0};
729 snprintf(bufDT, 32,
"%04d-%02d-%02dT%02d:%02d:%02d", utcdate.years, utcdate.months, utcdate.days, utcdate.hours,
730 utcdate.minutes, (
int)(utcdate.seconds));
731 snprintf(bufOff, 8,
"%4.2f", timeUTCOffset);
733 timeUTC->findWidgetByName(
"UTC")->setText(bufDT);
734 timeUTC->findWidgetByName(
"OFFSET")->setText(bufOff);
736 LOGF_DEBUG(
"send to timedate. %s, %s", bufDT, bufOff);
738 skySafariClient->setTimeUTC();
741 haveUTCoffset = haveUTCtime = haveUTCdate =
false;
746 std::vector<std::string> SkySafari::split(
const std::string &text,
char sep)
748 std::vector<std::string> tokens;
749 std::size_t start = 0, end = 0;
750 while ((end = text.find(sep, start)) != std::string::npos)
752 tokens.push_back(text.substr(start, end - start));
755 tokens.push_back(text.substr(start));
const char * getDeviceName() const
void setDefaultPollingPeriod(uint32_t msec)
setDefaultPollingPeriod Change the default polling period to call TimerHit() function in the driver.
void setVersion(uint16_t vMajor, uint16_t vMinor)
Set driver version information to be defined in DRIVER_INFO property as vMajor.vMinor.
virtual bool loadConfig(bool silent=false, const char *property=nullptr)
Load the last saved configuration file.
void defineProperty(INumberVectorProperty *property)
uint32_t getCurrentPollingPeriod() const
getCurrentPollingPeriod Return the current polling period.
virtual bool initProperties()
Initilize properties initial state and value. The child class must implement this function.
void setDriverInterface(uint16_t value)
setInterface Set driver interface. By default the driver interface is set to GENERAL_DEVICE....
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.
virtual bool initProperties() override
Initilize 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 bool Connect() override
Connect to the device. INDI::DefaultDevice implementation connects to appropriate connection interfac...
virtual void TimerHit() override
Callback function to be called once SetTimer duration elapses.
virtual const char * getDefaultName() override
virtual bool ISNewText(const char *dev, const char *name, char *texts[], char *names[], int n) override
Process the client newSwitch command.
virtual bool saveConfigItems(FILE *fp) override
saveConfigItems Save specific properties in the provide config file handler. Child class usually over...
virtual bool ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n) override
Process the client newSwitch command.
virtual bool Disconnect() override
Disconnect from device.
const char * MAIN_CONTROL_TAB
MAIN_CONTROL_TAB Where all the primary controls for the device are located.
void ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n)
Update the value of an existing switch vector property.
void ISGetProperties(const char *dev)
Get Device Properties.
const char * OPTIONS_TAB
OPTIONS_TAB Where all the driver's options are located. Those may include auxiliary controls,...
int f_scansexa(const char *str0, double *dp)
convert sexagesimal string str AxBxC to double. x can be anything non-numeric. Any missing A,...
void getSexComponents(double value, int *d, int *m, int *s)
Implementations for common driver routines.
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.
const char * IUFindOnSwitchName(ISState *states, char *names[], int n)
Returns the name of the first ON switch it finds in the supplied arguments.
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 IUSaveConfigText(FILE *fp, const ITextVectorProperty *tvp)
Add a text vector property value to the configuration file.
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 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 IDSetText(const ITextVectorProperty *tvp, const char *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,...)
std::vector< uint8_t > buffer
std::vector< std::string > split(const std::string &input, const std::string ®ex)