1 //============================================================================== 2 // 3 // This file is part of GPSTk, the GPS Toolkit. 4 // 5 // The GPSTk is free software; you can redistribute it and/or modify 6 // it under the terms of the GNU Lesser General Public License as published 7 // by the Free Software Foundation; either version 3.0 of the License, or 8 // any later version. 9 // 10 // The GPSTk is distributed in the hope that it will be useful, 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 // GNU Lesser General Public License for more details. 14 // 15 // You should have received a copy of the GNU Lesser General Public 16 // License along with GPSTk; if not, write to the Free Software Foundation, 17 // Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA 18 // 19 // This software was developed by Applied Research Laboratories at the 20 // University of Texas at Austin. 21 // Copyright 2004-2020, The Board of Regents of The University of Texas System 22 // 23 //============================================================================== 24 25 //============================================================================== 26 // 27 // This software was developed by Applied Research Laboratories at the 28 // University of Texas at Austin, under contract to an agency or agencies 29 // within the U.S. Department of Defense. The U.S. Government retains all 30 // rights to use, duplicate, distribute, disclose, or release this software. 31 // 32 // Pursuant to DoD Directive 523024 33 // 34 // DISTRIBUTION STATEMENT A: This software has been approved for public 35 // release, distribution is unlimited. 36 // 37 //============================================================================== 38 39 #ifndef DEVICESTREAM_HPP 40 #define DEVICESTREAM_HPP 41 42 #include <string> 43 #include <termios.h> // POSIX terminal control definitions 44 45 #include "FDStreamBuff.hpp" 46 #include "TCPStreamBuff.hpp" 47 48 namespace gpstk 49 { 50 // A class to hid the details of setting up an iostream to take data from 51 // write data to one of several sources: 52 // standard input/output 53 // a file 54 // a tcp socket 55 // a serial port 56 // See the open() function for how to specify these targets 57 // Note that the base type needs to be a decendant of an fstream for this class to work. 58 template<class T> 59 class DeviceStream : public T 60 { 61 public: 62 DeviceStream(void); 63 64 DeviceStream(const std::string& target, 65 std::ios::openmode mode = std::ios::in); 66 67 void open(const std::string& target, 68 std::ios::openmode mode = std::ios::in); 69 70 void open(const char* p, 71 std::ios::openmode mode = std::ios::in); 72 73 virtual ~DeviceStream(); 74 75 bool is_open() const; 76 77 enum DeviceType {dtStdio, dtFile, dtTCP, dtSerial}; 78 getDeviceType()79 DeviceType getDeviceType() {return deviceType;} 80 getTarget() const81 std::string getTarget() const {return target;} 82 83 private: 84 mutable FDStreamBuff *fdbuff; // mutable so rdbuf() can be const 85 std::string target; 86 DeviceType deviceType; 87 }; 88 89 90 template<class T> DeviceStream(void)91 DeviceStream<T>::DeviceStream(void) 92 : fdbuff(NULL) 93 { 94 open(""); 95 } 96 97 98 template<class T> DeviceStream(const std::string & target,std::ios::openmode mode)99 DeviceStream<T>::DeviceStream(const std::string& target, std::ios::openmode mode) 100 : fdbuff(NULL), 101 target(target) 102 { 103 open(target, mode); 104 }; 105 106 107 template<class T> ~DeviceStream()108 DeviceStream<T>::~DeviceStream() 109 { 110 if (fdbuff) 111 delete fdbuff; 112 } 113 114 115 template<class T> open(const char * p,std::ios::openmode mode)116 void DeviceStream<T>::open(const char* p, std::ios::openmode mode) 117 { 118 open(std::string(p), mode); 119 } 120 121 template<class T> is_open() const122 bool DeviceStream<T>::is_open() const 123 { 124 if (deviceType == dtStdio) 125 return true; 126 if (fdbuff != NULL) 127 return fdbuff->is_open(); 128 } 129 130 template<class T> open(const std::string & target,std::ios::openmode mode)131 void DeviceStream<T>::open(const std::string& target, std::ios::openmode mode) 132 { 133 using namespace std; 134 this->target = target; 135 if (target != "") 136 { 137 if (target.substr(0, 4) == "tcp:") 138 { 139 string ifn=target; 140 int port = 25; 141 ifn.erase(0,4); 142 string::size_type i = ifn.find(":"); 143 if (i<ifn.size()) 144 { 145 port = StringUtils::asInt(ifn.substr(i+1)); 146 ifn.erase(i); 147 } 148 149 TCPStreamBuff *tcpbuff = new TCPStreamBuff(); 150 151 SocketAddr client(ifn, port); 152 if (tcpbuff->connect(client)) 153 { 154 cerr << "Could not connect to " << ifn << endl; 155 exit(-1); 156 } 157 158 deviceType = dtTCP; 159 fdbuff = tcpbuff; 160 basic_ios<char>::rdbuf(fdbuff); 161 } 162 else if (target.substr(0, 4) == "ser:") // A serial port 163 { 164 string ifn=target; 165 ifn.erase(0,4); 166 167 int fd = ::open(ifn.c_str(), O_RDWR | O_NOCTTY); 168 if (fd<0) 169 { 170 cout << "Error opening " << ifn.c_str() << endl; 171 return; 172 } 173 174 // Not sure why this was being done it really should have been 175 // This is just another way to force blocking I/O 176 int rc; 177 rc = fcntl(fd, F_SETFL, 0); 178 if (rc < 0) 179 { 180 cout << "Error in fcntl, rc=" << rc << endl; 181 return; 182 } 183 184 struct termios options; 185 rc=tcgetattr(fd, &options); 186 187 options.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR 188 | IGNCR | ICRNL | IXON); 189 options.c_lflag &= ~(ICANON | ECHO | ECHOE | ECHONL | ISIG | IEXTEN); 190 options.c_oflag &= ~OPOST; 191 options.c_cflag &= ~(CSIZE | PARENB); 192 options.c_cflag |= CS8 | CREAD | HUPCL | CLOCAL; 193 194 options.c_cc[VTIME] = 0; // Wait forever 195 options.c_cc[VMIN] = 16; // And always get at least 16 characters 196 if (rc==cfsetospeed(&options, B115200)) 197 cout << "Error in cfsetospeed(), rc=" << rc << endl; 198 if (rc==cfsetispeed(&options, B115200)) 199 cout << "Error in cfsetispeed(), rc=" << rc << endl; 200 201 // Final step... apply them 202 if (rc==tcsetattr(fd, TCSANOW, &options)) 203 cout << "Error in tcsetattr(), rc=" << rc << endl; 204 205 deviceType = dtSerial; 206 fdbuff = new FDStreamBuff(fd); 207 basic_ios<char>::rdbuf(fdbuff); 208 } 209 else // a regular file 210 { 211 int flags=O_RDONLY; 212 if (mode & ios::out) 213 flags=O_WRONLY|O_CREAT; 214 if (mode & ios::app) flags |= O_APPEND; 215 if (mode & ios::trunc) flags |= O_TRUNC; 216 217 int fd = ::open(target.c_str(), flags, 0666); 218 if (fd<0) 219 { 220 cerr << "Could not open: " << target.c_str() << endl; 221 return; 222 } 223 fdbuff = new FDStreamBuff(fd); 224 basic_ios<char>::rdbuf(fdbuff); 225 226 deviceType = dtFile; 227 } 228 } 229 else // Use standard input/output 230 { 231 if (mode & ios::out) 232 { 233 T::copyfmt(cout); 234 T::clear(cin.rdstate()); 235 basic_ios<char>::rdbuf(cout.rdbuf()); 236 this->target = "<stdout>"; 237 } 238 else 239 { 240 T::copyfmt(cin); 241 T::clear(cin.rdstate()); 242 basic_ios<char>::rdbuf(cin.rdbuf()); 243 this->target = "<stdin>"; 244 } 245 deviceType = dtStdio; 246 } 247 } 248 249 } // end of namespace 250 #endif 251