00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039
00040
00041 #include "dashel.h"
00042
00043 #include <cassert>
00044 #include <cstdlib>
00045 #include <malloc.h>
00046 #include <map>
00047 #include <vector>
00048 #include <algorithm>
00049 #include <iostream>
00050 #include <sstream>
00051
00052 #ifdef _MSC_VER
00053 #pragma comment(lib, "ws2_32.lib")
00054 #pragma comment(lib, "wbemuuid.lib")
00055 #pragma comment(lib, "comsuppw.lib")
00056 #endif // _MSC_VER
00057
00058 #define _WIN32_WINNT 0x0501
00059 #include <winsock2.h>
00060 #include <windows.h>
00061 #include <setupapi.h>
00062
00063
00064 #ifdef _MSC_VER
00065 #include <comdef.h>
00066 #include <Wbemidl.h>
00067
00068
00069 #endif // _MSC_VER
00070
00071 #pragma warning(disable:4996)
00072
00073 #include "dashel-private.h"
00074
00078 namespace Dashel
00079 {
00081 template<typename Derived, typename Base>
00082 inline Derived polymorphic_downcast(Base base)
00083 {
00084 Derived derived = dynamic_cast<Derived>(base);
00085 assert(derived);
00086 return derived;
00087 }
00088
00089 void Stream::fail(DashelException::Source s, int se, const char* reason)
00090 {
00091 char sysMessage[1024] = {0};
00092 failedFlag = true;
00093
00094 if (se)
00095 FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, se, 0, sysMessage, 1024, NULL);
00096
00097 failReason = reason;
00098 failReason += " ";
00099 failReason += sysMessage;
00100
00101 throw DashelException(s, se, failReason.c_str(), this);
00102 }
00103
00104
00105
00106 std::map<int, std::pair<std::string, std::string> > SerialPortEnumerator::getPorts()
00107 {
00108 std::map<int, std::pair<std::string, std::string> > ports;
00109
00110 #ifndef _MSC_VER
00111
00112
00113 DWORD n, p;
00114 EnumPorts(NULL, 1, NULL, 0, &n, &p);
00115 PORT_INFO_1 *d = (PORT_INFO_1*)alloca(n);
00116 if(!d)
00117 throw DashelException(DashelException::EnumerationError, GetLastError(), "Could not allocate buffer for port devices.");
00118 if(!EnumPorts(NULL, 1, (LPBYTE)d, n, &n, &p))
00119 throw DashelException(DashelException::EnumerationError, GetLastError(), "Cannot get serial port devices.");
00120
00121 for(n = 0; n < p; ++n)
00122 {
00123 if(!strncmp(d[n].pName, "COM", 3))
00124 {
00125 int v = atoi(&d[n].pName[3]);
00126 if(v > 0 && v < 256)
00127 {
00128 ports.insert(std::pair<int, std::pair<std::string, std::string> >(v, std::pair<std::string, std::string> (d[n].pName, d[n].pName)));
00129 }
00130 }
00131 }
00132
00133 #else // _MSC_VER
00134
00135
00136
00137
00138 HRESULT hr;
00139 hr = CoInitializeEx(0, COINIT_MULTITHREADED);
00140 if(hr == RPC_E_CHANGED_MODE)
00141 {
00142 hr = CoInitializeEx(0, COINIT_APARTMENTTHREADED);
00143 if(FAILED(hr))
00144 throw DashelException(DashelException::EnumerationError, GetLastError(), "Cannot get serial port devices (could not start COM with apartment thread model either).");
00145 }
00146 else if(FAILED(hr))
00147 throw DashelException(DashelException::EnumerationError, GetLastError(), "Cannot get serial port devices (could not start COM).");
00148 hr = CoInitializeSecurity(NULL, -1, NULL, NULL, RPC_C_AUTHN_LEVEL_DEFAULT, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE, NULL);
00149 if(FAILED(hr))
00150 {
00151 CoUninitialize();
00152 throw DashelException(DashelException::EnumerationError, GetLastError(), "Cannot get serial port devices (could not setup COM security).");
00153 }
00154
00155 IWbemLocator *pLoc = NULL;
00156 hr = CoCreateInstance(CLSID_WbemLocator, 0, CLSCTX_INPROC_SERVER, IID_IWbemLocator, (LPVOID *) &pLoc);
00157 if(FAILED(hr))
00158 {
00159 CoUninitialize();
00160 throw DashelException(DashelException::EnumerationError, GetLastError(), "Cannot get serial port devices (could not create WBEM locator).");
00161 }
00162
00163 IWbemServices *pSvc = NULL;
00164 hr = pLoc->ConnectServer(_bstr_t(L"ROOT\\CIMV2"), NULL, NULL, 0, NULL, 0, 0, &pSvc);
00165 if(FAILED(hr))
00166 {
00167 pLoc->Release();
00168 CoUninitialize();
00169 throw DashelException(DashelException::EnumerationError, GetLastError(), "Cannot get serial port devices (could not connect to root of CIMV2).");
00170 }
00171
00172 hr = CoSetProxyBlanket(pSvc, RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, NULL, RPC_C_AUTHN_LEVEL_CALL, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE);
00173 if(FAILED(hr))
00174 {
00175 pSvc->Release();
00176 pLoc->Release();
00177 CoUninitialize();
00178 throw DashelException(DashelException::EnumerationError, GetLastError(), "Cannot get serial port devices (could not set proxy blanket).");
00179 }
00180
00181 IEnumWbemClassObject* pEnumerator = NULL;
00182 hr = pSvc->ExecQuery(bstr_t("WQL"), bstr_t("SELECT * FROM Win32_PnPEntity WHERE ClassGuid=\"{4D36E978-E325-11CE-BFC1-08002BE10318}\""), WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, NULL, &pEnumerator);
00183 if(FAILED(hr))
00184 {
00185 pSvc->Release();
00186 pLoc->Release();
00187 CoUninitialize();
00188 throw DashelException(DashelException::EnumerationError, GetLastError(), "Cannot get serial port devices (could not execute WBEM query).");
00189 }
00190
00191 IWbemClassObject *pclsObj;
00192 ULONG uReturn = 0;
00193 while (pEnumerator)
00194 {
00195 HRESULT hr = pEnumerator->Next(WBEM_INFINITE, 1, &pclsObj, &uReturn);
00196 if(0 == uReturn)
00197 break;
00198
00199 VARIANT vtProp;
00200 VariantInit(&vtProp);
00201 char dn[1024], dcn[1024], *co;
00202
00203
00204 hr = pclsObj->Get(L"Caption", 0, &vtProp, 0, 0);
00205 if(!FAILED(hr))
00206 {
00207 WideCharToMultiByte(CP_UTF8, 0, vtProp.bstrVal, -1, dn, 1024, NULL, NULL);
00208 VariantClear(&vtProp);
00209
00210
00211
00212
00213
00214 if((co = strstr(dn, "(COM")))
00215 {
00216 strcpy(dcn, co+1);
00217 strtok(dcn, ")");
00218
00219 int v = atoi(&dcn[3]);
00220
00221 if(v > 0 && v < 256)
00222 {
00223 std::string name = std::string("\\\\.\\").append(dcn);
00224 ports.insert(std::pair<int, std::pair<std::string, std::string> >(v, std::pair<std::string, std::string> (name, dn)));
00225 }
00226 }
00227 }
00228 pclsObj->Release();
00229 pclsObj = NULL;
00230 }
00231
00232 pSvc->Release();
00233 pLoc->Release();
00234 pEnumerator->Release();
00235 CoUninitialize();
00236
00237 #endif // _MSC_VER
00238
00239 return ports;
00240 };
00241
00242
00244 void startWinSock()
00245 {
00246 bool started = false;
00247 if(!started)
00248 {
00249 WORD ver = 0x0101;
00250 WSADATA d;
00251 memset(&d, 0, sizeof(d));
00252
00253 int rv = WSAStartup(ver, &d);
00254 if(rv)
00255 throw DashelException(DashelException::Unknown, rv, "Could not start WinSock service.");
00256 started = true;
00257 }
00258 }
00259
00261 class WaitableStream: virtual public Stream
00262 {
00263 public:
00265
00267 std::map<EvType,HANDLE> hEvents;
00268
00270 bool readDone;
00271
00272 protected:
00274
00276 HANDLE createEvent(EvType t)
00277 {
00278 HANDLE he = CreateEvent(NULL, FALSE, FALSE, NULL);
00279 hEvents[t] = he;
00280 return he;
00281 }
00282
00284
00287 void addEvent(EvType t, HANDLE he)
00288 {
00289 hEvents[t] = he;
00290 }
00291
00292 public:
00294 WaitableStream(const std::string& protocolName) : Stream(protocolName) { }
00295
00297
00299 virtual ~WaitableStream()
00300 {
00301 for(std::map<EvType, HANDLE>::iterator it = hEvents.begin(); it != hEvents.end(); ++it)
00302 CloseHandle(it->second);
00303 }
00304
00306
00309 virtual void notifyEvent(Hub *srv, EvType& t) { }
00310 };
00311
00313
00316 class SocketServerStream : public WaitableStream
00317 {
00318 protected:
00320 SOCKET sock;
00321
00323 HANDLE hev;
00324
00326 const bool resolveIncomingNames;
00327
00328 public:
00329
00331 SocketServerStream(const std::string& params, const bool resolveIncomingNames) : Stream("tcpin"), WaitableStream("tcpin"),
00332 resolveIncomingNames(resolveIncomingNames)
00333 {
00334 target.add("tcpin:port=5000;address=0.0.0.0");
00335 target.add(params.c_str());
00336
00337 startWinSock();
00338
00339 IPV4Address bindAddress(target.get("address"), target.get<int>("port"));
00340
00341
00342 sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
00343 if (sock == SOCKET_ERROR)
00344 throw DashelException(DashelException::ConnectionFailed, WSAGetLastError(), "Cannot create socket.");
00345
00346
00347 int flag = 1;
00348 if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (const char *)&flag, sizeof (flag)) < 0)
00349 throw DashelException(DashelException::ConnectionFailed, WSAGetLastError(), "Cannot set address reuse flag on socket, probably the port is already in use.");
00350
00351
00352 sockaddr_in addr;
00353 addr.sin_family = AF_INET;
00354 addr.sin_port = htons(bindAddress.port);
00355 addr.sin_addr.s_addr = htonl(bindAddress.address);
00356 if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) != 0)
00357 throw DashelException(DashelException::ConnectionFailed, WSAGetLastError(), "Cannot bind socket to port, probably the port is already in use.");
00358
00359
00360 if(listen(sock, 16) == SOCKET_ERROR)
00361 throw DashelException(DashelException::ConnectionFailed, WSAGetLastError(), "Cannot listen on socket.");
00362
00363
00364 hev = createEvent(EvConnect);
00365 WSAEventSelect(sock, hev, FD_ACCEPT);
00366 }
00367
00369 ~SocketServerStream()
00370 {
00371 if(sock)
00372 closesocket(sock);
00373 }
00374
00376
00379 virtual void notifyEvent(Hub *srv, EvType& t)
00380 {
00381 if(t == EvConnect)
00382 {
00383
00384 struct sockaddr_in targetAddr;
00385 int l = sizeof (targetAddr);
00386 SOCKET trg = accept (sock, (struct sockaddr *)&targetAddr, &l);
00387 if (trg == SOCKET_ERROR)
00388 {
00389 fail(DashelException::ConnectionFailed, WSAGetLastError(), "Cannot accept incoming connection on socket.");
00390 }
00391
00392
00393 std::string ls = IPV4Address(ntohl(targetAddr.sin_addr.s_addr), ntohs(targetAddr.sin_port)).format(resolveIncomingNames);
00394
00395 std::ostringstream buf;
00396 buf << ";connectionPort=";
00397 buf << atoi(getTargetParameter("port").c_str());
00398 buf << ";sock=";
00399 buf << (int)trg;
00400 ls.append(buf.str());
00401 srv->connect(ls);
00402 }
00403 }
00404
00405 virtual void write(const void *data, const size_t size) { }
00406 virtual void flush() { }
00407 virtual void read(void *data, size_t size) { }
00408 };
00409
00411
00414 class StdinStream : public WaitableStream
00415 {
00416 protected:
00418 HANDLE hf;
00419
00421 HANDLE hev;
00422
00423 public:
00424
00426 StdinStream(const std::string& params) : Stream("stdin"), WaitableStream("stdin")
00427 {
00428 target.add(params.c_str());
00429
00430 if((hf = GetStdHandle(STD_INPUT_HANDLE)) == INVALID_HANDLE_VALUE)
00431 throw DashelException(DashelException::ConnectionFailed, GetLastError(), "Cannot open standard input.");
00432
00433 DWORD cm;
00434 GetConsoleMode(hf, &cm);
00435 cm &= ~(ENABLE_ECHO_INPUT | ENABLE_LINE_INPUT);
00436 if(!SetConsoleMode(hf, cm))
00437 throw DashelException(DashelException::ConnectionFailed, GetLastError(), "Cannot change standard input mode to immediate.");
00438
00439
00440 addEvent(EvPotentialData, hf);
00441 hev = createEvent(EvData);
00442 }
00443
00445 ~StdinStream()
00446 {
00447 CloseHandle(hf);
00448 }
00449
00451
00453 virtual void notifyEvent(Hub *srv, EvType& t)
00454 {
00455 DWORD n = 0;
00456 if(GetNumberOfConsoleInputEvents(hf, &n))
00457 {
00458 if(n > 0)
00459 {
00460 INPUT_RECORD ir;
00461 PeekConsoleInput(hf, &ir, 1, &n);
00462 if(ir.EventType != KEY_EVENT)
00463 ReadConsoleInput(hf, &ir, 1, &n);
00464 else
00465 {
00466 t = EvData;
00467 }
00468 }
00469 }
00470 }
00471
00473 virtual void write(const void *data, const size_t size)
00474 {
00475 throw DashelException(DashelException::InvalidOperation, GetLastError(), "Cannot write to standard input.", this);
00476 }
00477
00479 virtual void flush()
00480 {
00481 throw DashelException(DashelException::InvalidOperation, GetLastError(), "Cannot flush standard input.", this);
00482 }
00483
00484 virtual void read(void *data, size_t size)
00485 {
00486 char *ptr = (char *)data;
00487 DWORD left = (DWORD)size;
00488
00489
00490 assert(left == size);
00491
00492 readDone = true;
00493
00494 while (left)
00495 {
00496 DWORD len = 0;
00497 BOOL r;
00498
00499
00500 if((r = ReadFile(hf, ptr, left, &len, NULL)) == 0)
00501 {
00502 fail(DashelException::IOError, GetLastError(), "Read error from standard input.");
00503 }
00504 else
00505 {
00506 ptr += len;
00507 left -= len;
00508 }
00509 }
00510 }
00511
00512 };
00513
00515 class StdoutStream : public WaitableStream
00516 {
00517 protected:
00519 HANDLE hf;
00520
00521 public:
00522
00524 StdoutStream(const std::string& params) : Stream("stdout"), WaitableStream("stdout")
00525 {
00526 target.add(params.c_str());
00527
00528 if((hf = GetStdHandle(STD_OUTPUT_HANDLE)) == INVALID_HANDLE_VALUE)
00529 throw DashelException(DashelException::ConnectionFailed, GetLastError(), "Cannot open standard output.");
00530 }
00531
00533 ~StdoutStream()
00534 {
00535 CloseHandle(hf);
00536 }
00537
00538 virtual void write(const void *data, const size_t size)
00539 {
00540 const char *ptr = (const char *)data;
00541 DWORD left = (DWORD)size;
00542
00543
00544 assert(left == size);
00545
00546 while (left)
00547 {
00548 DWORD len = 0;
00549 BOOL r;
00550
00551
00552 if((r = WriteFile(hf, ptr, left, &len, NULL)) == 0)
00553 {
00554 fail(DashelException::IOError, GetLastError(), "Write error to standard output.");
00555 }
00556 else
00557 {
00558 ptr += len;
00559 left -= len;
00560 }
00561 }
00562 }
00563
00564 virtual void flush()
00565 {
00566 FlushFileBuffers(hf);
00567 }
00568
00569 virtual void read(void *data, size_t size)
00570 {
00571 fail(DashelException::InvalidOperation, GetLastError(), "Cannot read from standard output.");
00572 }
00573
00574 };
00575
00577 class FileStream : public WaitableStream
00578 {
00579 protected:
00581 HANDLE hf;
00582
00584 OVERLAPPED ovl;
00585
00587 DWORD writeOffset;
00588
00590
00594 bool readyToRead;
00595
00597 char readByte;
00598
00600 bool readByteAvailable;
00601
00603 HANDLE hEOF;
00604
00605 protected:
00607
00609 FileStream(const std::string& protocolName, bool dummy) : Stream(protocolName), WaitableStream(protocolName) { }
00610
00612 void startStream(EvType et = EvData)
00613 {
00614 readByteAvailable = false;
00615 memset(&ovl, 0, sizeof(ovl));
00616 ovl.hEvent = createEvent(et);
00617 BOOL r = ReadFile(hf, &readByte, 1, NULL, &ovl);
00618 if(!r)
00619 {
00620 DWORD err = GetLastError();
00621 if(err != ERROR_IO_PENDING)
00622 throw DashelException(DashelException::IOError, GetLastError(), "Cannot read from file stream.");
00623 }
00624 else
00625 readByteAvailable = true;
00626 }
00627
00628 public:
00629
00631 FileStream(const std::string& params) : Stream("file"), WaitableStream("file")
00632 {
00633 target.add("file:name;mode=read");
00634 target.add(params.c_str());
00635 std::string name = target.get("name");
00636 std::string mode = target.get("mode");
00637
00638 hf = NULL;
00639 if (mode == "read")
00640 {
00641 hf = CreateFile(name.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
00642 startStream();
00643 }
00644 else if (mode == "write")
00645 {
00646 writeOffset = 0;
00647 hf = CreateFile(name.c_str(), GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_FLAG_OVERLAPPED, NULL);
00648 }
00649 else if (mode == "readwrite")
00650 {
00651 writeOffset = 0;
00652 hf = CreateFile(name.c_str(), GENERIC_WRITE | GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, FILE_FLAG_OVERLAPPED, NULL);
00653 startStream();
00654 }
00655 if(hf == INVALID_HANDLE_VALUE)
00656 throw DashelException(DashelException::ConnectionFailed, GetLastError(), "Cannot open file.");
00657
00658 hEOF = createEvent(EvClosed);
00659 }
00660
00662 ~FileStream()
00663 {
00664 CloseHandle(hf);
00665 }
00666
00667 virtual void write(const void *data, const size_t size)
00668 {
00669 const char *ptr = (const char *)data;
00670 DWORD left = (DWORD)size;
00671
00672
00673 assert(left == size);
00674
00675 while (left)
00676 {
00677 DWORD len = 0;
00678 OVERLAPPED o;
00679 memset(&o, 0, sizeof(o));
00680
00681 o.Offset = writeOffset;
00682
00683
00684 BOOL r = WriteFile(hf, ptr, left, &len, &o);
00685 if(!r)
00686 {
00687 DWORD err;
00688 switch((err = GetLastError()))
00689 {
00690 case ERROR_IO_PENDING:
00691 GetOverlappedResult(hf, &o, &len, TRUE);
00692 ptr += len;
00693 left -= len;
00694 break;
00695
00696 default:
00697 fail(DashelException::IOError, GetLastError(), "Cannot write to file.");
00698 break;
00699 }
00700 }
00701 else
00702 {
00703 ptr += len;
00704 left -= len;
00705 }
00706
00707 writeOffset += len;
00708 }
00709 }
00710
00711 virtual void flush()
00712 {
00713 FlushFileBuffers(hf);
00714 }
00715
00716 virtual void read(void *data, size_t size)
00717 {
00718 char *ptr = (char *)data;
00719 DWORD left = (DWORD)size;
00720
00721
00722 assert(left == size);
00723
00724 if (size == 0)
00725 return;
00726
00727 readDone = true;
00728
00729 if(!readByteAvailable)
00730 WaitForSingleObject(ovl.hEvent, INFINITE);
00731
00732 DWORD dataUsed;
00733 if(!GetOverlappedResult(hf, &ovl, &dataUsed, TRUE))
00734 fail(DashelException::IOError, GetLastError(), "File read I/O error.");
00735
00736 if(dataUsed)
00737 {
00738 *ptr++ = readByte;
00739 left--;
00740 }
00741 readByteAvailable = false;
00742
00743 while (left)
00744 {
00745 DWORD len = 0;
00746 OVERLAPPED o;
00747 memset(&o, 0, sizeof(o));
00748 o.Offset = ovl.Offset + size - left;
00749 o.hEvent = ovl.hEvent ;
00750
00751
00752 BOOL r = ReadFile(hf, ptr, left, &len, &o);
00753 if(!r)
00754 {
00755 DWORD err;
00756 switch((err = GetLastError()))
00757 {
00758 case ERROR_HANDLE_EOF:
00759 fail(DashelException::ConnectionLost, GetLastError(), "Reached end of file.");
00760 break;
00761
00762 case ERROR_IO_PENDING:
00763 WaitForSingleObject(ovl.hEvent, INFINITE);
00764 if(!GetOverlappedResult(hf, &o, &len, TRUE))
00765 fail(DashelException::IOError, GetLastError(), "File read I/O error.");
00766 if(len == 0)
00767 return;
00768
00769 ptr += len;
00770 left -= len;
00771 break;
00772
00773 default:
00774 fail(DashelException::IOError, GetLastError(), "File read I/O error.");
00775 break;
00776 }
00777 }
00778 else
00779 {
00780 WaitForSingleObject(ovl.hEvent, INFINITE);
00781 ptr += len;
00782 left -= len;
00783 }
00784 }
00785
00786
00787 ovl.Offset += (DWORD)size;
00788 BOOL r = ReadFile(hf, &readByte, 1, &dataUsed, &ovl);
00789 if(!r)
00790 {
00791 DWORD err = GetLastError();
00792 if(err == ERROR_HANDLE_EOF)
00793 {
00794 SetEvent(hEOF);
00795 }
00796 else if(err != ERROR_IO_PENDING)
00797 {
00798 fail(DashelException::IOError, GetLastError(), "Cannot read from file stream.");
00799 }
00800 }
00801 else
00802 readByteAvailable = true;
00803
00804 }
00805
00807
00809 virtual void notifyEvent(Hub *srv, EvType& t)
00810 {
00811 if(t == EvPotentialData)
00812 {
00813 DWORD dataUsed;
00814 GetOverlappedResult(hf, &ovl, &dataUsed, TRUE);
00815 if(dataUsed == 0)
00816 ReadFile(hf, &readByte, 1, NULL, &ovl);
00817 else
00818 {
00819 readByteAvailable = true;
00820 t = EvData;
00821 }
00822 }
00823 }
00824 };
00825
00827 class SerialStream : public FileStream
00828 {
00829 private:
00831
00837 bool buildDCB(HANDLE sp, int speed, int bits, const std::string& parity, const std::string& stopbits, const std::string& fc)
00838 {
00839 DCB dcb;
00840
00841 memset(&dcb, 0, sizeof(dcb));
00842 dcb.DCBlength = sizeof(dcb);
00843
00844 if(!GetCommState(sp, &dcb))
00845 throw DashelException(DashelException::ConnectionFailed, GetLastError(), "Cannot read current serial port state.", this);
00846
00847
00848 memset(&dcb,0,sizeof(dcb));
00849 dcb.DCBlength = sizeof(dcb);
00850 if(fc == "hard")
00851 {
00852 dcb.fOutxCtsFlow = TRUE;
00853 dcb.fRtsControl = RTS_CONTROL_HANDSHAKE;
00854 }
00855 else
00856 {
00857 dcb.fOutxCtsFlow = FALSE;
00858 dcb.fRtsControl = RTS_CONTROL_DISABLE;
00859 }
00860
00861 dcb.fOutxDsrFlow = FALSE;
00862 dcb.fDtrControl = DTR_CONTROL_DISABLE;
00863 dcb.fDsrSensitivity = FALSE;
00864 dcb.fBinary = TRUE;
00865 dcb.fParity = TRUE;
00866 dcb.BaudRate = speed;
00867 dcb.ByteSize = bits;
00868 if(parity == "even")
00869 dcb.Parity = EVENPARITY;
00870 else if(parity == "odd")
00871 dcb.Parity = ODDPARITY;
00872 else if(parity == "space")
00873 dcb.Parity = SPACEPARITY;
00874 else if(parity == "mark")
00875 dcb.Parity = MARKPARITY;
00876 else
00877 dcb.Parity = NOPARITY;
00878
00879 if(stopbits == "1.5")
00880 dcb.StopBits = ONE5STOPBITS;
00881 else if(stopbits == "2")
00882 dcb.StopBits = TWOSTOPBITS;
00883 else
00884 dcb.StopBits = ONESTOPBIT;
00885
00886
00887 if(!SetCommState(sp, &dcb))
00888 throw DashelException(DashelException::ConnectionFailed, GetLastError(), "Cannot set new serial port state.", this);
00889
00890
00891
00892 COMMTIMEOUTS cto;
00893 memset(&cto, 0, sizeof(cto));
00894 cto.ReadIntervalTimeout = 100000;
00895 cto.ReadTotalTimeoutConstant = 100000;
00896 cto.ReadTotalTimeoutMultiplier = 100000;
00897 cto.WriteTotalTimeoutConstant = 100000;
00898 cto.WriteTotalTimeoutMultiplier = 100000;
00899 if(!SetCommTimeouts(sp, &cto))
00900 throw DashelException(DashelException::ConnectionFailed, GetLastError(), "Cannot set new serial port timeouts.", this);
00901
00902 return true;
00903 }
00904
00905 public:
00907
00909 SerialStream(const std::string& params) : Stream("ser"), FileStream("ser", true)
00910 {
00911 target.add("ser:port=1;baud=115200;stop=1;parity=none;fc=none;bits=8");
00912 target.add(params.c_str());
00913
00914 std::string name;
00915 if (target.isSet("device"))
00916 {
00917 target.addParam("device", NULL, true);
00918 target.erase("port");
00919
00920 name = target.get("device");
00921 }
00922 else
00923 {
00924 target.erase("device");
00925
00926 name = std::string("\\\\.\\COM").append(target.get("port"));
00927 }
00928
00929 hf = CreateFile(name.c_str(), GENERIC_WRITE | GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
00930 if(hf == INVALID_HANDLE_VALUE)
00931 throw DashelException(DashelException::ConnectionFailed, GetLastError(), "Cannot open serial port.");
00932
00933 buildDCB(hf, target.get<int>("baud"), target.get<int>("bits"), target.get("parity"), target.get("stop"), target.get("fc"));
00934
00935 startStream(EvPotentialData);
00936 }
00937 };
00938
00940 class SocketStream : public WaitableStream
00941 {
00943 SOCKET sock;
00944
00946 HANDLE hev;
00947
00949 HANDLE hev2;
00950
00952 HANDLE hev3;
00953
00955
00959 bool readyToRead;
00960
00962 char readByte;
00963
00965 bool readByteAvailable;
00966
00967 public:
00969
00971 SocketStream(const std::string& params) : Stream("tcp"), WaitableStream("tcp")
00972 {
00973 target.add("tcp:host;port;connectionPort=-1;sock=0");
00974 target.add(params.c_str());
00975
00976 sock = target.get<SOCKET>("sock");
00977 if(!sock)
00978 {
00979 startWinSock();
00980
00981
00982 sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
00983 if (sock == SOCKET_ERROR)
00984 throw DashelException(DashelException::ConnectionFailed, WSAGetLastError(), "Cannot create socket.");
00985
00986 IPV4Address remoteAddress(target.get("host"), target.get<int>("port"));
00987
00988 sockaddr_in addr;
00989 addr.sin_family = AF_INET;
00990 addr.sin_port = htons(remoteAddress.port);
00991 addr.sin_addr.s_addr = htonl(remoteAddress.address);
00992 if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) != 0)
00993 throw DashelException(DashelException::ConnectionFailed, WSAGetLastError(), "Cannot connect to remote host.");
00994
00995
00996 target.add(remoteAddress.format().c_str());
00997 target.erase("connectionPort");
00998 }
00999 else
01000 {
01001
01002 target.erase("sock");
01003 }
01004
01005 hev2 = createEvent(EvData);
01006 hev3 = createEvent(EvClosed);
01007 hev = createEvent(EvPotentialData);
01008
01009 int rv = WSAEventSelect(sock, hev, FD_READ | FD_CLOSE);
01010 if (rv == SOCKET_ERROR)
01011 throw DashelException(DashelException::ConnectionFailed, WSAGetLastError(), "Cannot select socket events.");
01012
01013 readyToRead = false;
01014 readByteAvailable = false;
01015 }
01016
01017 ~SocketStream()
01018 {
01019 shutdown(sock, SD_BOTH);
01020 closesocket(sock);
01021 }
01022
01024
01026 virtual void notifyEvent(Hub *srv, EvType& t)
01027 {
01028 if(t == EvPotentialData)
01029 {
01030 if(readByteAvailable)
01031 return;
01032
01033 int rv = recv(sock, &readByte, 1, 0);
01034 if(rv <= 0)
01035 {
01036 t = EvClosed;
01037 }
01038 else
01039 {
01040 readByteAvailable = true;
01041 readyToRead = true;
01042 t = EvData;
01043 }
01044 }
01045 }
01046
01047 virtual void write(const void *data, const size_t size)
01048 {
01049 char *ptr = (char *)data;
01050 size_t left = size;
01051
01052 while (left)
01053 {
01054 int len = send(sock, ptr, (int)left, 0);
01055
01056 if (len == SOCKET_ERROR)
01057 {
01058 fail(DashelException::ConnectionFailed, GetLastError(), "Connection lost on write.");
01059 }
01060 else
01061 {
01062 ptr += len;
01063 left -= len;
01064 }
01065 }
01066 }
01067
01068 virtual void flush() { }
01069
01070 virtual void read(void *data, size_t size)
01071 {
01072 char *ptr = (char *)data;
01073 size_t left = size;
01074
01075 if (size == 0)
01076 return;
01077
01078 readDone = true;
01079
01080
01081 if(!readyToRead)
01082 {
01083
01084 WaitForSingleObject(hev, INFINITE);
01085 }
01086 readyToRead = false;
01087
01088 if(readByteAvailable)
01089 {
01090 *ptr++ = readByte;
01091 readByteAvailable = false;
01092 left--;
01093 if(left)
01094 WaitForSingleObject(hev, INFINITE);
01095 }
01096
01097 while (left)
01098 {
01099
01100 int len = recv(sock, ptr, (int)left, 0);
01101
01102
01103 if (len == SOCKET_ERROR)
01104 {
01105
01106 fail(DashelException::ConnectionFailed, GetLastError(), "Connection lost on read.");
01107 }
01108 else if(len == 0)
01109 {
01110
01111 }
01112 else
01113 {
01114 ptr += len;
01115 left -= len;
01116 }
01117 if(left)
01118 {
01119
01120 WaitForSingleObject(hev, INFINITE);
01121 }
01122 }
01123
01124
01125
01126
01127
01128 }
01129 };
01130
01132 class UDPSocketStream: public MemoryPacketStream, public WaitableStream
01133 {
01134 private:
01136 SOCKET sock;
01137
01139 HANDLE hev;
01140
01141 public:
01143 UDPSocketStream(const std::string& targetName) :
01144 Stream("udp"),
01145 MemoryPacketStream("udp"),
01146 WaitableStream("udp")
01147 {
01148 target.add("udp:port=5000;address=0.0.0.0;sock=0");
01149 target.add(targetName.c_str());
01150
01151 sock = target.get<SOCKET>("sock");
01152 if(!sock)
01153 {
01154 startWinSock();
01155
01156
01157 sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
01158 if (sock == SOCKET_ERROR)
01159 throw DashelException(DashelException::ConnectionFailed, WSAGetLastError(), "Cannot create socket.");
01160
01161 IPV4Address bindAddress(target.get("address"), target.get<int>("port"));
01162
01163
01164 sockaddr_in addr;
01165 addr.sin_family = AF_INET;
01166 addr.sin_port = htons(bindAddress.port);
01167 addr.sin_addr.s_addr = htonl(bindAddress.address);
01168 if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) != 0)
01169 throw DashelException(DashelException::ConnectionFailed, WSAGetLastError(), "Cannot bind socket to port, probably the port is already in use.");
01170 }
01171 else
01172 {
01173
01174 target.erase("sock");
01175 }
01176
01177
01178 int broadcastPermission = 1;
01179 setsockopt(sock, SOL_SOCKET, SO_BROADCAST, (const char*)&broadcastPermission, sizeof(broadcastPermission));
01180
01181
01182 hev = createEvent(EvData);
01183
01184 int rv = WSAEventSelect(sock, hev, FD_READ);
01185 if (rv == SOCKET_ERROR)
01186 throw DashelException(DashelException::ConnectionFailed, WSAGetLastError(), "Cannot select socket events.");
01187 }
01188
01189 virtual ~UDPSocketStream()
01190 {
01191 closesocket(sock);
01192 }
01193
01194 virtual void send(const IPV4Address& dest)
01195 {
01196 sockaddr_in addr;
01197 addr.sin_family = AF_INET;
01198 addr.sin_port = htons(dest.port);;
01199 addr.sin_addr.s_addr = htonl(dest.address);
01200
01201 if (sendto(sock, (const char*)sendBuffer.get(), sendBuffer.size(), 0, (struct sockaddr *)&addr, sizeof(addr)) != sendBuffer.size())
01202 fail(DashelException::IOError, WSAGetLastError(), "UDP Socket write I/O error.");
01203
01204 sendBuffer.clear();
01205 }
01206
01207 virtual void receive(IPV4Address& source)
01208 {
01209 unsigned char buf[4006];
01210 sockaddr_in addr;
01211 int addrLen = sizeof(addr);
01212 readDone = true;
01213
01214 int recvCount = recvfrom(sock, (char*)buf, 4096, 0, (struct sockaddr *)&addr, &addrLen);
01215 if (recvCount <= 0)
01216 fail(DashelException::ConnectionLost, WSAGetLastError(), "UDP Socket read I/O error.");
01217
01218 receptionBuffer.resize(recvCount);
01219 std::copy(buf, buf+recvCount, receptionBuffer.begin());
01220
01221 source = IPV4Address(ntohl(addr.sin_addr.s_addr), ntohs(addr.sin_port));
01222 }
01223 };
01224
01225 Hub::Hub(const bool resolveIncomingNames):
01226 resolveIncomingNames(resolveIncomingNames)
01227 {
01228 hTerminate = CreateEvent(NULL, TRUE, FALSE, NULL);
01229 }
01230
01231 Hub::~Hub()
01232 {
01233 for (StreamsSet::iterator it = streams.begin(); it != streams.end(); ++it)
01234 delete *it;
01235 }
01236
01237 Stream* Hub::connect(const std::string &target)
01238 {
01239 std::string proto, params;
01240 size_t c = target.find_first_of(':');
01241 if(c == std::string::npos)
01242 throw DashelException(DashelException::InvalidTarget, 0, "No protocol specified in target.");
01243 proto = target.substr(0, c);
01244 params = target.substr(c+1);
01245
01246 WaitableStream *s = NULL;
01247 if(proto == "file")
01248 s = new FileStream(target);
01249 if(proto == "stdin")
01250 s = new StdinStream(target);
01251 if(proto == "stdout")
01252 s = new StdoutStream(target);
01253 if(proto == "ser")
01254 s = new SerialStream(target);
01255 if(proto == "tcpin")
01256 s = new SocketServerStream(target, resolveIncomingNames);
01257 if(proto == "tcp")
01258 s = new SocketStream(target);
01259 if(proto == "udp")
01260 s = new UDPSocketStream(target);
01261
01262 if(!s)
01263 {
01264 std::string r = "Invalid protocol in target: ";
01265 r = r.append(proto);
01266 throw DashelException(DashelException::InvalidTarget, 0, r.c_str());
01267 }
01268
01269 streams.insert(s);
01270 if (proto != "tcpin")
01271 {
01272 dataStreams.insert(s);
01273 connectionCreated(s);
01274 }
01275 return s;
01276 }
01277
01278 void Hub::run()
01279 {
01280 while(step(-1));
01281 }
01282
01283 bool Hub::step(const int timeout)
01284 {
01285 HANDLE hEvs[64] = { hTerminate };
01286 WaitableStream *strs[64] = { NULL };
01287 EvType ets[64] = { EvClosed };
01288
01289
01290 DWORD ms = timeout >= 0 ? timeout : INFINITE;
01291
01292
01293 do
01294 {
01295 DWORD hc = 1;
01296
01297
01298 for(std::set<Stream*>::iterator it = streams.begin(); it != streams.end(); ++it)
01299 {
01300 WaitableStream* stream = polymorphic_downcast<WaitableStream*>(*it);
01301 for(std::map<EvType,HANDLE>::iterator ei = stream->hEvents.begin(); ei != stream->hEvents.end(); ++ei)
01302 {
01303 strs[hc] = stream;
01304 ets[hc] = ei->first;
01305 hEvs[hc] = ei->second;
01306 hc++;
01307 }
01308 }
01309
01310 DWORD r = WaitForMultipleObjects(hc, hEvs, FALSE, ms);
01311
01312
01313 if (r == WAIT_FAILED)
01314 throw DashelException(DashelException::SyncError, 0, "Wait failed.");
01315 if (r == WAIT_TIMEOUT)
01316 return true;
01317
01318
01319 r -= WAIT_OBJECT_0;
01320 if(r == 0)
01321 {
01322
01323 return false;
01324 }
01325 else
01326 {
01327
01328 strs[r]->notifyEvent(this, ets[r]);
01329
01330
01331 if(ets[r] == EvData)
01332 {
01333 try
01334 {
01335 strs[r]->readDone = false;
01336 incomingData(strs[r]);
01337 }
01338 catch (DashelException e) { }
01339 if(!strs[r]->readDone)
01340 throw DashelException(DashelException::PreviousIncomingDataNotRead, 0, "Previous incoming data not read.", strs[r]);
01341 if(strs[r]->failed())
01342 {
01343 connectionClosed(strs[r], true);
01344 closeStream(strs[r]);
01345 }
01346 }
01347
01348 if(ets[r] == EvClosed)
01349 {
01350 try
01351 {
01352 connectionClosed(strs[r], false);
01353 }
01354 catch (DashelException e) { }
01355 closeStream(strs[r]);
01356 }
01357 }
01358
01359
01360 ms = 0;
01361 }
01362 while(true);
01363 }
01364
01365 void Hub::lock()
01366 {
01367 }
01368
01369 void Hub::unlock()
01370 {
01371 }
01372
01373 void Hub::stop()
01374 {
01375 SetEvent(hTerminate);
01376 }
01377
01378
01379 }