Files
pip/piserial.cpp

408 lines
10 KiB
C++

/*
PIP - Platform Independent Primitives
COM
Copyright (C) 2013 Ivan Pelipenko peri4ko@gmail.com, Bychkov Andrey wapmobil@gmail.com
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "piserial.h"
PISerial::PISerial(const PIString & device, void * data_, ReadRetFunc slot): PIIODevice(device, ReadWrite) {
piMonitor.serials++;
setPriority(piHigh);
path_ = device;
data = data_;
fd = -1;
headerPtr = 0;
params = 0;
vtime = 1;
ret_func_ = slot;
#ifdef WINDOWS
hCom = 0;
#endif
ispeed = ospeed = S115200;
init();
}
PISerial::PISerial(void * data_, ReadRetFunc slot): PIIODevice("", ReadWrite) {
piMonitor.serials++;
setPriority(piHigh);
data = data_;
fd = -1;
headerPtr = 0;
params = 0;
vtime = 1;
ret_func_ = slot;
#ifdef WINDOWS
hCom = 0;
#endif
ispeed = ospeed = S115200;
init();
}
PISerial::~PISerial() {
piMonitor.serials--;
}
bool PISerial::setPin(int number, bool on) {
switch (number) {
case 1: return setCAR(on); break;
case 2: return setSR(on); break;
case 3: return setST(on); break;
case 4: return setDTR(on); break;
case 5:
piCoutObj << "[PISerial] Pin number 5 is ground";
return false;
case 6: return setDSR(on); break;
case 7: return setRTS(on); break;
case 8: return setCTS(on); break;
case 9: return setRNG(on); break;
default:
piCoutObj << "[PISerial] Pin number " << number << " doesn`t exists!";
return false;
}
return false;
}
bool PISerial::isPin(int number) const {
switch (number) {
case 1: return isCAR(); break;
case 2: return isSR(); break;
case 3: return isST(); break;
case 4: return isDTR(); break;
case 5: return false;
case 6: return isDSR(); break;
case 7: return isRTS(); break;
case 8: return isCTS(); break;
case 9: return isRNG(); break;
default:
piCoutObj << "[PISerial] Pin number " << number << " doesn`t exists!";
return false;
}
return false;
}
bool PISerial::setBit(int bit, bool on, const PIString & bname) {
#ifndef WINDOWS
if (fd < 0) {
piCoutObj << "[PISerial] set" << bname << " error: \"" << path_ << "\" is not opened!";
return false;
}
if (ioctl(fd, on ? TIOCMBIS : TIOCMBIC, &bit) < 0) {
piCoutObj << "[PISerial] set" << bname << " error: " << errorString();
return false;
}
return true;
#else
piCoutObj << "[PISerial] set" << bname << " doesn`t implemented on Windows, sorry :-(";
return false;
#endif
}
bool PISerial::isBit(int bit, const PIString & bname) const {
#ifndef WINDOWS
if (fd < 0) {
piCoutObj << "[PISerial] is" << bname << " error: \"" << path_ << "\" is not opened!";
return false;
}
int ret = 0;
if (ioctl(fd, TIOCMGET, &ret) < 0)
piCoutObj << "[PISerial] is" << bname << " error: " << errorString();
return ret & bit;
#else
piCoutObj << "[PISerial] set" << bname << " doesn`t implemented on Windows, sorry :-(";
return false;
#endif
}
bool PISerial::closeDevice() {
if (!isInitialized()) return true;
if (isRunning()) {
stop();
PIThread::terminate();
}
#ifdef WINDOWS
if (fd != -1) {
SetCommState(hCom, &sdesc);
SetCommMask(hCom, mask);
CloseHandle(hCom);
fd = -1;
}
#else
if (fd != -1) {
tcsetattr(fd, TCSANOW, &sdesc);
::close(fd);
fd = -1;
}
#endif
return true;
}
int PISerial::convertSpeed(PISerial::Speed speed) {
switch (speed) {
case S50: return B50;
case S75: return B75;
case S110: return B110;
case S300: return B300;
case S600: return B600;
case S1200: return B1200;
case S2400: return B2400;
case S4800: return B4800;
case S9600: return B9600;
case S19200: return B19200;
case S38400: return B38400;
case S57600: return B57600;
case S115200: return B115200;
case S1500000: return B1500000;
case S2000000: return B2000000;
case S2500000: return B2500000;
case S3000000: return B3000000;
case S3500000: return B3500000;
case S4000000: return B4000000;
default: break;
}
return B115200;
}
bool PISerial::read(void * data, int size, double timeout_ms) {
if (data == 0) return false;
int ret, all = 0;
if (timeout_ms > 0.) {
setReadIsBlocking(false);
all = ::read(fd, data, 1);
timer.reset();
while (all < size && timer.elapsed_m() < timeout_ms) {
ret = ::read(fd, &((uchar * )data)[all], size - all);
if (ret > 0) all += ret;
else msleep(1);
}
received(data, all);
return (all == size);
} else {
setReadIsBlocking(true);
all = ::read(fd, data, 1);
while (all < size) {
ret = ::read(fd, &((uchar * )data)[all], size - all);
if (ret > 0) all += ret;
}
received(data, all);
return (all == size);
}
return false;
}
PIByteArray PISerial::readData(int size, double timeout_ms) {
int ret, all = 0;
uchar td[1024];
PIByteArray str;
if (timeout_ms > 0.) {
setReadIsBlocking(false);
timer.reset();
if (size <= 0) {
while (timer.elapsed_m() < timeout_ms) {
ret = ::read(fd, td, 1024);
if (ret <= 0) msleep(1);
else str << PIByteArray(td, ret);
}
} else {
while (all < size && timer.elapsed_m() < timeout_ms) {
ret = ::read(fd, td, size - all);
if (ret <= 0) msleep(1);
else {
str << PIByteArray(td, ret);
all += ret;
}
}
}
} else {
setReadIsBlocking(true);
all = ::read(fd, td, 1);
str << PIByteArray(td, all);
while (all < size) {
ret = ::read(fd, td, size - all);
if (ret <= 0) msleep(1);
else {
str << PIByteArray(td, ret);
all += ret;
}
}
}
return str;
}
PIString PISerial::read(int size, double timeout_ms) {
int ret, all = 0;
uchar td[1024];
PIString str;
if (timeout_ms > 0.) {
setReadIsBlocking(false);
timer.reset();
if (size <= 0) {
while (timer.elapsed_m() < timeout_ms) {
ret = ::read(fd, td, 1024);
if (ret <= 0) msleep(1);
else str << PIString((char*)td, ret);
}
} else {
while (all < size && timer.elapsed_m() < timeout_ms) {
ret = ::read(fd, td, size - all);
if (ret <= 0) msleep(1);
else {
str << PIString((char*)td, ret);
all += ret;
}
}
}
} else {
setReadIsBlocking(true);
all = ::read(fd, td, 1);
str << PIString((char*)td, all);
while (all < size) {
ret = ::read(fd, td, size - all);
if (ret <= 0) msleep(1);
else {
str << PIString((char*)td, ret);
all += ret;
}
}
}
return str;
}
bool PISerial::openDevice() {
#ifdef WINDOWS
DWORD ds = 0, sm = 0;
if (isReadable()) {ds |= GENERIC_READ; sm |= FILE_SHARE_READ;}
if (isWriteable()) {ds |= GENERIC_WRITE; sm |= FILE_SHARE_WRITE;}
hCom = CreateFileA(path_.data(), ds, sm, 0, OPEN_EXISTING, FILE_ATTRIBUTE_SYSTEM, 0);
if(hCom == INVALID_HANDLE_VALUE) {
piCoutObj << "[PISerial] Unable to open \"" << path_ << "\"";
return false;
}
fd = 0;
COMMTIMEOUTS times;
times.ReadIntervalTimeout = vtime;
times.ReadTotalTimeoutConstant = 1;
times.ReadTotalTimeoutMultiplier = 0;
times.WriteTotalTimeoutConstant = 1;
times.WriteTotalTimeoutMultiplier = 0;
if (SetCommTimeouts(hCom, &times) == -1) {
piCoutObj << "[PISerial] Unable to set timeouts for \"" << path_ << "\"";
CloseHandle(hCom);
fd = -1;
return false;
}
GetCommMask(hCom, &mask);
SetCommMask(hCom, EV_RXCHAR);
GetCommState(hCom, &sdesc);
desc = sdesc;
desc.DCBlength = sizeof(desc);
desc.BaudRate = convertSpeed(ispeed);
desc.ByteSize = 8;
if (params[PISerial::ParityControl]) {
desc.fParity = 1;
desc.Parity = params[PISerial::ParityOdd] ? 1 : 2;
}
desc.StopBits = params[PISerial::TwoStopBits] ? TWOSTOPBITS : ONESTOPBIT;
if (SetCommState(hCom, &desc) == -1) {
piCoutObj << "[PISerial] Unable to set comm state for \"" << path_ << "\"";
CloseHandle(hCom);
fd = -1;
return false;
}
#else
int om = 0;
switch (mode_) {
case PIIODevice::ReadOnly: om = O_RDONLY; break;
case PIIODevice::WriteOnly: om = O_WRONLY; break;
case PIIODevice::ReadWrite: om = O_RDWR; break;
}
//cout << "init ser " << path_ << " mode " << om << " param " << params << endl;
fd = ::open(path_.data(), O_NOCTTY | om);
if(fd == -1) {
piCoutObj << "[PISerial] Unable to open \"" << path_ << "\"";
return false;
}
tcgetattr(fd, &desc);
sdesc = desc;
desc.c_iflag = desc.c_oflag = desc.c_lflag = desc.c_cflag = 0;
desc.c_cflag = CLOCAL | CSIZE | CS8;
if (isReadable()) desc.c_cflag |= CREAD;
if (params[PISerial::HardwareFlowControl]) desc.c_cflag |= CRTSCTS;
if (params[PISerial::TwoStopBits]) desc.c_cflag |= CSTOPB;
if (params[PISerial::ParityControl]) {
desc.c_iflag |= INPCK;
desc.c_cflag |= PARENB;
if (params[PISerial::ParityOdd]) desc.c_cflag |= PARODD;
}
desc.c_cc[VMIN] = 1;
desc.c_cc[VTIME] = vtime;
cfsetispeed(&desc, convertSpeed(ispeed));
cfsetospeed(&desc, convertSpeed(ospeed));
tcflush(fd, TCIOFLUSH);
fcntl(fd, F_SETFL, 0);
if(tcsetattr(fd, TCSANOW, &desc) < 0) {
piCoutObj << "[PISerial] Can`t set attributes for \"" << path_ << "\"";
::close(fd);
return false;
}
//piCoutObj << "[PISerial] Initialized " << path_;
#endif
return true;
}
int PISerial::write(const void * data, int max_size, bool wait) {
//piCoutObj << "[PISerial] send " << max_size << ": " << PIString((char*)data, max_size);
if (fd == -1 || !canWrite()) {
//piCoutObj << "[PISerial] Can`t write to uninitialized COM";
return -1;
}
#ifdef WINDOWS
DWORD wrote;
WriteFile(hCom, data, max_size, &wrote, 0);
if (wait) {
DWORD event;
SetCommMask(hCom, EV_TXEMPTY);
WaitCommEvent(hCom, &event, NULL);
SetCommMask(hCom, EV_RXCHAR);
}
#else
int wrote;
wrote = ::write(fd, data, max_size);
if (wait) tcdrain(fd);
#endif
return (int)wrote;
//piCoutObj << "[PISerial] Error while sending";
//piCoutObj << "[PISerial] Wrote " << wrote << " bytes in " << path_;
}