Instrument Neutral Distributed Interface INDI  1.9.2
connectiontcp.cpp
Go to the documentation of this file.
1 /*******************************************************************************
2  Copyright(c) 2017 Jasem Mutlaq. All rights reserved.
3 
4  This library is free software; you can redistribute it and/or
5  modify it under the terms of the GNU Library General Public
6  License version 2 as published by the Free Software Foundation.
7 
8  This library is distributed in the hope that it will be useful,
9  but WITHOUT ANY WARRANTY; without even the implied warranty of
10  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11  Library General Public License for more details.
12 
13  You should have received a copy of the GNU Library General Public License
14  along with this library; see the file COPYING.LIB. If not, write to
15  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
16  Boston, MA 02110-1301, USA.
17 *******************************************************************************/
18 
19 #include "connectiontcp.h"
20 
21 #include "indilogger.h"
22 #include "indistandardproperty.h"
23 
24 #include <cerrno>
25 #include <netdb.h>
26 #include <cstring>
27 #include <unistd.h>
28 
29 #ifdef __FreeBSD__
30 #include <arpa/inet.h>
31 #include <netinet/in.h>
32 #include <sys/socket.h>
33 #endif
34 
35 namespace Connection
36 {
37 extern const char *CONNECTION_TAB;
38 
39 TCP::TCP(INDI::DefaultDevice *dev) : Interface(dev, CONNECTION_TCP)
40 {
41  char defaultHostname[MAXINDINAME] = {0};
42  char defaultPort[MAXINDINAME] = {0};
43 
44  // Try to load the port from the config file. If that fails, use default port.
45  IUGetConfigText(dev->getDeviceName(), INDI::SP::DEVICE_ADDRESS, "ADDRESS", defaultHostname, MAXINDINAME);
47 
48  // Address/Port
49  IUFillText(&AddressT[0], "ADDRESS", "Address", defaultHostname);
50  IUFillText(&AddressT[1], "PORT", "Port", defaultPort);
51  IUFillTextVector(&AddressTP, AddressT, 2, getDeviceName(), "DEVICE_ADDRESS", "Server", CONNECTION_TAB,
52  IP_RW, 60, IPS_IDLE);
53 
54  IUFillSwitch(&TcpUdpS[0], "TCP", "TCP", ISS_ON);
55  IUFillSwitch(&TcpUdpS[1], "UDP", "UDP", ISS_OFF);
56  IUFillSwitchVector(&TcpUdpSP, TcpUdpS, 2, getDeviceName(), "CONNECTION_TYPE", "Connection Type",
58 
59 }
60 
61 bool TCP::ISNewText(const char *dev, const char *name, char *texts[], char *names[], int n)
62 {
63  if (!strcmp(dev, m_Device->getDeviceName()))
64  {
65  // TCP Server settings
66  if (!strcmp(name, AddressTP.name))
67  {
68  IUUpdateText(&AddressTP, texts, names, n);
69  AddressTP.s = IPS_OK;
70  IDSetText(&AddressTP, nullptr);
71  return true;
72  }
73  }
74 
75  return false;
76 }
77 
78 bool TCP::ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n)
79 {
80  if (!strcmp(dev, m_Device->getDeviceName()))
81  {
82  if (!strcmp(name, TcpUdpSP.name))
83  {
84  IUUpdateSwitch(&TcpUdpSP, states, names, n);
85  TcpUdpSP.s = IPS_OK;
86 
87  IDSetSwitch(&TcpUdpSP, nullptr);
88 
89  return true;
90  }
91  }
92 
93  return false;
94 }
95 
96 bool TCP::Connect()
97 {
98  if (AddressT[0].text == nullptr || AddressT[0].text[0] == '\0' || AddressT[1].text == nullptr ||
99  AddressT[1].text[0] == '\0')
100  {
101  LOG_ERROR("Error! Server address is missing or invalid.");
102  return false;
103  }
104 
105  const char *hostname = AddressT[0].text;
106  const char *port = AddressT[1].text;
107 
108  LOGF_INFO("Connecting to %s@%s ...", hostname, port);
109 
110  if (m_Device->isSimulation() == false)
111  {
112  struct sockaddr_in serv_addr;
113  struct hostent *hp = nullptr;
114 
115  struct timeval ts;
116  ts.tv_sec = SOCKET_TIMEOUT;
117  ts.tv_usec = 0;
118 
119  if (sockfd != -1)
120  close(sockfd);
121 
122  // Lookup host name or IPv4 address
123  hp = gethostbyname(hostname);
124  if (!hp)
125  {
126  LOG_ERROR("Failed to lookup IP Address or hostname.");
127  return false;
128  }
129 
130  memset(&serv_addr, 0, sizeof(serv_addr));
131  serv_addr.sin_family = AF_INET;
132  serv_addr.sin_addr.s_addr = ((struct in_addr *)(hp->h_addr_list[0]))->s_addr;
133  serv_addr.sin_port = htons(atoi(port));
134 
135  int socketType = 0;
136  if (TcpUdpS[0].s == ISS_ON)
137  {
138  socketType = SOCK_STREAM;
139  }
140  else
141  {
142  socketType = SOCK_DGRAM;
143  }
144 
145  if ((sockfd = socket(AF_INET, socketType, 0)) < 0)
146  {
147  LOG_ERROR("Failed to create socket.");
148  return false;
149  }
150 
151  // Set the socket receiving and sending timeouts
152  setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, (char *)&ts, sizeof(struct timeval));
153  setsockopt(sockfd, SOL_SOCKET, SO_SNDTIMEO, (char *)&ts, sizeof(struct timeval));
154 
155  // Connect to the device
156  if (::connect(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0)
157  {
158  LOGF_ERROR("Failed to connect to %s@%s: %s.", hostname, port, strerror(errno));
159  close(sockfd);
160  sockfd = -1;
161  return false;
162  }
163  }
164 
165  PortFD = sockfd;
166 
167  LOG_DEBUG("Connection successful, attempting handshake...");
168  bool rc = Handshake();
169 
170  if (rc)
171  {
172  LOGF_INFO("%s is online.", getDeviceName());
173  m_Device->saveConfig(true, "DEVICE_ADDRESS");
174  m_Device->saveConfig(true, "CONNECTION_TYPE");
175  }
176  else
177  LOG_DEBUG("Handshake failed.");
178 
179  return rc;
180 }
181 
182 bool TCP::Disconnect()
183 {
184  if (sockfd > 0)
185  {
186  close(sockfd);
187  sockfd = PortFD = -1;
188  }
189 
190  return true;
191 }
192 
193 void TCP::Activated()
194 {
197  m_Device->loadConfig(true, "DEVICE_ADDRESS");
198  m_Device->loadConfig(true, "CONNECTION_TYPE");
199 }
200 
201 void TCP::Deactivated()
202 {
205 }
206 
207 bool TCP::saveConfigItems(FILE *fp)
208 {
211 
212  return true;
213 }
214 
215 void TCP::setDefaultHost(const char *addressHost)
216 {
217  IUSaveText(&AddressT[0], addressHost);
218 }
219 
220 void TCP::setDefaultPort(uint32_t addressPort)
221 {
222  char portStr[8];
223  snprintf(portStr, 8, "%d", addressPort);
224  IUSaveText(&AddressT[1], portStr);
225 }
226 
228 {
230  TcpUdpS[type].s = ISS_ON;
231  IDSetSwitch(&TcpUdpSP, nullptr);
232 }
233 }
indistandardproperty.h
Connection::Interface::Handshake
std::function< bool()> Handshake
Definition: connectioninterface.h:129
Connection::TCP::AddressTP
ITextVectorProperty AddressTP
Definition: connectiontcp.h:112
LOGF_ERROR
#define LOGF_ERROR(fmt,...)
Definition: indilogger.h:80
IPS_OK
@ IPS_OK
Definition: indiapi.h:161
Connection::TCP::Disconnect
virtual bool Disconnect() override
Disconnect Disconnect from device.
Definition: connectiontcp.cpp:198
INDI::SP::DEVICE_ADDRESS
const char * DEVICE_ADDRESS
Device hostname and port. It is part of Connection::TCPInterface to manage connections to devices ove...
Definition: indistandardproperty.cpp:67
ISS_OFF
@ ISS_OFF
Definition: indiapi.h:150
Connection::TCP::TcpUdpS
ISwitch TcpUdpS[2]
Definition: connectiontcp.h:115
connectiontcp.h
IDSetText
void IDSetText(const ITextVectorProperty *t, const char *msg,...) ATTRIBUTE_FORMAT_PRINTF(2
Tell client to update an existing text vector property.
MAXINDINAME
#define MAXINDINAME
Definition: indiapi.h:190
INDI::DefaultDevice::isSimulation
bool isSimulation() const
Definition: defaultdevice.cpp:734
INDI::DefaultDevice::defineProperty
void defineProperty(INumberVectorProperty *property)
Definition: defaultdevice.cpp:997
IUFillTextVector
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.
Definition: indidriver.c:477
INDI::BaseDevice::getDeviceName
const char * getDeviceName() const
Definition: basedevice.cpp:799
IUFillText
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.
Definition: indidriver.c:369
Connection::TCP::PortFD
int PortFD
Definition: connectiontcp.h:121
IUUpdateText
int IUUpdateText(ITextVectorProperty *tvp, char *texts[], char *names[], int n)
Update all text members in a text vector property.
Definition: indidriver.c:259
Connection::TCP::saveConfigItems
virtual bool saveConfigItems(FILE *fp) override
Definition: connectiontcp.cpp:223
Connection::TCP::sockfd
int sockfd
Definition: connectiontcp.h:118
IUResetSwitch
void IUResetSwitch(ISwitchVectorProperty *svp)
Reset all switches in a switch vector property to OFF.
Definition: indicom.c:1421
Connection::Interface::getDeviceName
const char * getDeviceName()
Definition: connectioninterface.cpp:53
indilogger.h
Connection::TCP::Connect
virtual bool Connect() override
Connect Connect to device via the implemented communication medium. Do not perform any handshakes.
Definition: connectiontcp.cpp:112
Connection::TCP::SOCKET_TIMEOUT
const uint8_t SOCKET_TIMEOUT
Definition: connectiontcp.h:119
type
__le16 type
Definition: pwc-ioctl.h:2
IUGetConfigText
int IUGetConfigText(const char *dev, const char *property, const char *member, char *value, int len)
IUGetConfigText Opens configuration file and reads single text property.
Definition: indidriver.c:1447
IUSaveConfigText
void IUSaveConfigText(FILE *fp, const ITextVectorProperty *tvp)
Add a text vector property value to the configuration file.
Definition: indicom.c:1439
Connection::TCP::setConnectionType
void setConnectionType(int type)
Definition: connectiontcp.cpp:243
Connection::Interface::type
virtual Type type()
type Return connection type
Definition: connectioninterface.h:105
IUFillSwitchVector
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.
Definition: indidriver.c:412
ISR_1OFMANY
@ ISR_1OFMANY
Definition: indiapi.h:172
Connection::Interface::m_Device
INDI::DefaultDevice * m_Device
Definition: connectioninterface.h:130
IPS_IDLE
@ IPS_IDLE
Definition: indiapi.h:160
Connection::CONNECTION_TAB
const char * CONNECTION_TAB
CONNECTION_TAB Where all device connection settings (serial, usb, ethernet) are defined and controlle...
Definition: connectioninterface.cpp:41
Connection::TCP::ISNewText
virtual bool ISNewText(const char *dev, const char *name, char *texts[], char *names[], int n) override
Definition: connectiontcp.cpp:77
INDI::DefaultDevice::loadConfig
virtual bool loadConfig(bool silent=false, const char *property=nullptr)
Load the last saved configuration file.
Definition: defaultdevice.cpp:147
IUUpdateSwitch
int IUUpdateSwitch(ISwitchVectorProperty *svp, ISState *states, char *names[], int n)
Update all switches in a switch vector property.
Definition: indidriver.c:171
_ITextVectorProperty::name
char name[MAXINDINAME]
Definition: indiapi.h:249
Connection::TCP::TCP
TCP(INDI::DefaultDevice *dev)
Definition: connectiontcp.cpp:55
LOG_DEBUG
#define LOG_DEBUG(txt)
Definition: indilogger.h:75
Connection::TCP::Activated
virtual void Activated() override
Activated Function called by the framework when the plugin is activated (i.e. selected by the user)....
Definition: connectiontcp.cpp:209
LOGF_INFO
#define LOGF_INFO(fmt,...)
Definition: indilogger.h:82
LOG_ERROR
#define LOG_ERROR(txt)
Shorter logging macros. In order to use these macros, the function (or method) "getDeviceName()" must...
Definition: indilogger.h:72
Connection::TCP::TcpUdpSP
ISwitchVectorProperty TcpUdpSP
Definition: connectiontcp.h:116
Connection
Combines all INDI Connection Plugins. Each INDI connection plugin is responsible of managing communic...
Definition: arduino_st4.h:33
IUSaveText
void IUSaveText(IText *tp, const char *newtext)
Function to reliably save new text in a IText.
Definition: indicom.c:1428
Connection::TCP::port
virtual uint32_t port() const
Definition: connectiontcp.h:98
_ISwitchVectorProperty::s
IPState s
Definition: indiapi.h:382
Connection::TCP::setDefaultHost
void setDefaultHost(const char *addressHost)
Definition: connectiontcp.cpp:231
_ITextVectorProperty::s
IPState s
Definition: indiapi.h:259
INDI::DefaultDevice::saveConfig
virtual bool saveConfig(bool silent=false, const char *property=nullptr)
Save the current properties in a configuration file.
Definition: defaultdevice.cpp:221
Connection::TCP::setDefaultPort
void setDefaultPort(uint32_t addressPort)
Definition: connectiontcp.cpp:236
Connection::TCP::name
virtual std::string name() override
Definition: connectiontcp.h:93
Connection::TCP::Deactivated
virtual void Deactivated() override
Deactivated Function called by the framework when the plugin is deactivated. It is usually used to de...
Definition: connectiontcp.cpp:217
IP_RW
@ IP_RW
Definition: indiapi.h:185
ISState
ISState
Switch state.
Definition: indiapi.h:148
IUSaveConfigSwitch
void IUSaveConfigSwitch(FILE *fp, const ISwitchVectorProperty *svp)
Add a switch vector property value to the configuration file.
Definition: indicom.c:1444
INDI::DefaultDevice
Class to provide extended functionality for devices in addition to the functionality provided by INDI...
Definition: defaultdevice.h:118
Connection::TCP::AddressT
IText AddressT[2]
Definition: connectiontcp.h:113
INDI::DefaultDevice::deleteProperty
virtual bool deleteProperty(const char *propertyName)
Delete a property and unregister it. It will also be deleted from all clients.
Definition: defaultdevice.cpp:965
IDSetSwitch
void void void void void IDSetSwitch(const ISwitchVectorProperty *s, const char *msg,...) ATTRIBUTE_FORMAT_PRINTF(2
Tell client to update an existing switch vector property.
errno
int errno
IUFillSwitch
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.
Definition: indidriver.c:320
Connection::TCP::ISNewSwitch
virtual bool ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n) override
Definition: connectiontcp.cpp:94
_ISwitchVectorProperty::name
char name[MAXINDINAME]
Definition: indiapi.h:370
ISS_ON
@ ISS_ON
Definition: indiapi.h:151