diff --git a/.gitignore b/.gitignore index 3634e636..092160f1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ /src_main/piversion.h /.svn /doc/rtf +_unsused diff --git a/CMakeLists.txt b/CMakeLists.txt index 70e3fcd2..85921c43 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.0) cmake_policy(SET CMP0017 NEW) # need include() with .cmake project(pip) set(_PIP_MAJOR 1) -set(_PIP_MINOR 16) +set(_PIP_MINOR 17) set(_PIP_REVISION 0) set(_PIP_SUFFIX alpha) set(_PIP_COMPANY SHS) @@ -84,6 +84,8 @@ set(PIP_SRC_DIRS "src_main" "src_crypt" "src_compress" "src_usb" "src_fftw" "src set(PIP_LIBS_TARGETS pip) set(LIBS_MAIN) set(LIBS_STATUS) +set(HDRS) +set(PHDRS) if (TESTS) set(PIP_SRC_CONCURRENT_TEST "src_concurrent/test") @@ -110,12 +112,16 @@ set_version(PIP REVISION "${_PIP_REVISION}" BUILD "${BUILD_NUMBER}" SUFFIX "${_PIP_SUFFIX}" - OUTPUT "${CMAKE_CURRENT_SOURCE_DIR}/${PIP_SRC_MAIN}/piversion.h") + OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/piversion.h") set_deploy_property(pip ${PIP_LIB_TYPE} LABEL "PIP main library" FULLNAME "${_PIP_DOMAIN}.pip" COMPANY "${_PIP_COMPANY}" INFO "Platform-Independent Primitives") +if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/${PIP_SRC_MAIN}/piversion.h") + file(REMOVE "${CMAKE_CURRENT_SOURCE_DIR}/${PIP_SRC_MAIN}/piversion.h") +endif() +list(APPEND HDRS "${CMAKE_CURRENT_BINARY_DIR}/piversion.h") message(STATUS "Building PIP version ${PIP_VERSION} (${PIP_LIB_TYPE})") @@ -352,7 +358,7 @@ if(NOT PIP_FREERTOS) if(WIN32) if(${C_COMPILER} STREQUAL "cl.exe") else() - list(APPEND LIBS_MAIN ws2_32 iphlpapi psapi) + list(APPEND LIBS_MAIN ws2_32 iphlpapi psapi cfgmgr32 setupapi) endif() else() list(APPEND LIBS_MAIN dl) diff --git a/main.cpp b/main.cpp index 38f8dd69..e7c406b4 100644 --- a/main.cpp +++ b/main.cpp @@ -111,14 +111,18 @@ private: }; */ -PIKbdListener kbd; + +PIKbdListener kbd(0, 0, false); + int main(int argc, char * argv[]) { - /*PIStringList dl = PISerial::availableDevices(); - piCout << dl; - PISerial ser(dl[0]); - piCout << ser.open(PIIODevice::ReadWrite) << &ser; - */ + //piCout << PISerial::availableDevicesInfo(); + PIIODevice * d = PIIODevice::createFromFullPath(argv[1]); + piCout << d; + if (d) { + d->open(); + piCout << d->constructFullPath() << d->isOpened(); + } return 0; } diff --git a/src_io_utils/pistreampacker.cpp b/src_io_utils/pistreampacker.cpp index ec5d9be4..73a3f249 100644 --- a/src_io_utils/pistreampacker.cpp +++ b/src_io_utils/pistreampacker.cpp @@ -17,8 +17,15 @@ along with this program. If not, see . */ +#ifdef __GNUC__ +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wnonnull" +#endif #include "pistreampacker.h" #include "piiodevice.h" +#ifdef __GNUC__ +# pragma GCC diagnostic pop +#endif /** \class PIStreamPacker * \brief Simple packet wrap aroud any PIIODevice diff --git a/src_main/core/pibase.h b/src_main/core/pibase.h index 8a3ea2b2..230668bd 100644 --- a/src_main/core/pibase.h +++ b/src_main/core/pibase.h @@ -215,9 +215,9 @@ #define PRIVATE_DEFINITION_END(c) \ }; \ c::__PrivateInitializer__::__PrivateInitializer__() {p = new c::__Private__();} \ - c::__PrivateInitializer__::__PrivateInitializer__(const c::__PrivateInitializer__ & o) {/*if (p) delete p;*/ p = new c::__Private__();} \ + c::__PrivateInitializer__::__PrivateInitializer__(const c::__PrivateInitializer__ & ) {/*if (p) delete p;*/ p = new c::__Private__();} \ c::__PrivateInitializer__::~__PrivateInitializer__() {delete p; p = 0;} \ - c::__PrivateInitializer__ & c::__PrivateInitializer__::operator =(const c::__PrivateInitializer__ & o) {if (p) delete p; p = new c::__Private__(); return *this;} + c::__PrivateInitializer__ & c::__PrivateInitializer__::operator =(const c::__PrivateInitializer__ & ) {if (p) delete p; p = new c::__Private__(); return *this;} #define PRIVATE (__privateinitializer__.p) #define PRIVATEWB __privateinitializer__.p diff --git a/src_main/core/piobject.h b/src_main/core/piobject.h index a33e6922..9269b043 100755 --- a/src_main/core/piobject.h +++ b/src_main/core/piobject.h @@ -201,9 +201,9 @@ typedef name __PIObject__; \ public: \ static const PIString __classNameS() {return PIStringAscii(#name);} \ - static const uint __classNameIDS() {static uint ret = PIStringAscii(#name).hash(); return ret;} \ + static uint __classNameIDS() {static uint ret = PIStringAscii(#name).hash(); return ret;} \ virtual const char * className() const {return #name;} \ - virtual const uint classNameID() const {static uint ret = PIStringAscii(#name).hash(); return ret;} \ + virtual uint classNameID() const {static uint ret = PIStringAscii(#name).hash(); return ret;} \ private: \ virtual int ptrOffset() const {name * o = (name*)100; return int(llong((PIObject*)o) - llong(o));} \ class __BaseInitializer__ { \ @@ -515,10 +515,10 @@ public: //! Returns object class name virtual const char * className() const {return "PIObject";} - virtual const uint classNameID() const {static uint ret = PIStringAscii("PIObject").hash(); return ret;} + virtual uint classNameID() const {static uint ret = PIStringAscii("PIObject").hash(); return ret;} static const PIString __classNameS() {return PIStringAscii("PIObject");} - static const uint __classNameIDS() {static uint ret = PIStringAscii("PIObject").hash(); return ret;} + static uint __classNameIDS() {static uint ret = PIStringAscii("PIObject").hash(); return ret;} //! Returns parent object class name virtual const char * parentClassName() const {return "";} diff --git a/src_main/introspection/piintrospection_threads_p.cpp b/src_main/introspection/piintrospection_threads_p.cpp index b7430e86..94cf1326 100644 --- a/src_main/introspection/piintrospection_threads_p.cpp +++ b/src_main/introspection/piintrospection_threads_p.cpp @@ -36,7 +36,8 @@ PIIntrospectionThreads::PIIntrospectionThreads() { void PIIntrospectionThreads::threadNew(PIThread * t) { PIMutexLocker _ml(mutex); - ThreadInfo & ti(threads[t]); + //ThreadInfo & ti(threads[t]); + threads.insert(t, ThreadInfo()); //piCout << "register thread" << id << name; } diff --git a/src_main/io_devices/piconfig.cpp b/src_main/io_devices/piconfig.cpp index 70b7b28d..d0452e4f 100755 --- a/src_main/io_devices/piconfig.cpp +++ b/src_main/io_devices/piconfig.cpp @@ -49,6 +49,14 @@ * debug = true * \endcode * + * You can use multiline values ends with " \" + * \code + * value = start \ #s comment + * _mid \ + * _end + * \endcode + * In this example value = "start_mid_end" + * * \section PIConfig_sec1 Concepts * Each node of internal tree has type PIConfig::Entry. %PIConfig * has one root element \a rootEntry(). Any entry of configuration file is a @@ -249,7 +257,7 @@ void PIConfig::Entry::coutt(std::ostream & s, const PIString & p) const { void PIConfig::Entry::piCoutt(PICout s, const PIString & p) const { PIString nl = p + " "; - if (!_value.isEmpty()) s << p << _name << " = " << _value << PICoutManipulators::NewLine; + if (!_value.isEmpty()) s << p << _name << " = " << _value << " (" << _type << " " << _comment << ")" << PICoutManipulators::NewLine; else std::cout << p << _name << std::endl; piForeachC (Entry * i, _children) i->piCoutt(s, nl); } @@ -261,6 +269,7 @@ PIConfig::PIConfig(const PIString & path, PIIODevice::DeviceMode mode) { dev = new PIFile(path, mode); if (!dev->isOpened()) dev->open(path, mode); + incdirs << PIFile::fileInfo(path).dir(); parse(); } @@ -277,7 +286,11 @@ PIConfig::PIConfig(PIIODevice * device, PIIODevice::DeviceMode mode) { _init(); own_dev = false; dev = device; - if (dev) dev->open(mode); + if (dev) { + dev->open(mode); + if (PIString(dev->className()) == "PIFile") + incdirs << PIFile::fileInfo(((PIFile*)dev)->path()).dir(); + } parse(); } @@ -716,9 +729,9 @@ void PIConfig::parse() { //piCout << "[PIConfig] charset" << PIFile::defaultCharset(); PIString src, str, tab, comm, all, name, type, prefix, tprefix; PIStringList tree; - Entry * entry, * te, * ce; + Entry * entry = 0, * te = 0, * ce = 0; int ind, sind; - bool isNew, isPrefix; + bool isNew = false, isPrefix = false, wasMultiline = false, isMultiline = false; piForeach (PIConfig * c, inc_devs) delete c; inc_devs.clear(); @@ -739,27 +752,45 @@ void PIConfig::parse() { //piCout << "line \"" << str << "\""; tab = str.left(str.find(str.trimmed().left(1))); str.trim(); - //cout << endl << str << endl << endl; -// piCout << "[PIConfig] str" << str.size() << str << str.toUTF8(); all = str; + + sind = str.find('#'); + if (sind > 0) { + comm = str.mid(sind + 1).trimmed(); + if (!comm.isEmpty()) { + type = comm[0]; + comm.cutLeft(1).trim(); + } else type = "s"; + str = str.left(sind).trim(); + } else { + type = "s"; + comm = ""; + } + + if (str.endsWith(" \\")) { + isMultiline = true; + str.cutRight(2).trim(); + } else + isMultiline = false; + + if (wasMultiline) { + wasMultiline = false; + if (ce) { + ce->_value += str; + ce->_all += " \\\n" + all; + } + str.clear(); + } else + ce = 0; + wasMultiline = isMultiline; + + //piCout << "[PIConfig] str" << str.size() << str << str.toUTF8(); ind = str.find('='); if ((ind > 0) && (str[0] != '#')) { - sind = str.find('#'); - if (sind > 0) { - comm = str.right(str.length() - sind - 1).trimmed(); - if (comm.length() > 0) type = comm[0]; - else type = "s"; - comm = comm.right(comm.length() - 1).trimmed(); - str = str.left(sind); - } else { - type = "s"; - comm = ""; - } - //name = str.left(ind).trimmed(); tree = (prefix + str.left(ind).trimmed()).split(delim); if (tree.front() == "include") { - name = str.right(str.length() - ind - 1).trimmed(); + name = str.mid(ind + 1).trimmed(); PIConfig * iconf = new PIConfig(name, incdirs); //piCout << "include" << name << iconf->dev; if (!iconf->dev) { @@ -797,7 +828,7 @@ void PIConfig::parse() { ce->delim = delim; ce->_tab = tab; ce->_name = name; - ce->_value = str.right(str.length() - ind - 1).trimmed(); + ce->_value = str.mid(ind + 1).trimmed(); ce->_type = type; ce->_comment = comm; //piCout << "[PIConfig] comm" << comm.size() << comm << comm.toUTF8(); diff --git a/src_main/io_devices/piconfig.h b/src_main/io_devices/piconfig.h index edde0769..aeba3861 100755 --- a/src_main/io_devices/piconfig.h +++ b/src_main/io_devices/piconfig.h @@ -512,7 +512,10 @@ std::ostream & operator <<(std::ostream & s, const PIConfig::Entry & v); #endif inline PICout operator <<(PICout s, const PIConfig::Branch & v) {s.setControl(0, true); v.piCoutt(s, ""); s.restoreControl(); return s;} -inline PICout operator <<(PICout s, const PIConfig::Entry & v) {s << v.value(); return s;} +inline PICout operator <<(PICout s, const PIConfig::Entry & v) { + s << v.value() << "(" << v.type() << v.comment() << ")"; + return s; +} /** \relatesalso PIConfig \relatesalso PIIODevice diff --git a/src_main/io_devices/pigpio.cpp b/src_main/io_devices/pigpio.cpp new file mode 100644 index 00000000..d6d0f9e4 --- /dev/null +++ b/src_main/io_devices/pigpio.cpp @@ -0,0 +1,268 @@ +/* + PIP - Platform Independent Primitives + GPIO + Andrey Bychkov work.a.b@yandex.ru, Ivan Pelipenko peri4ko@yandex.ru + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program. If not, see . +*/ + +#include "pigpio.h" +#ifdef LINUX +# define GPIO_SYS_CLASS +#endif +#ifdef GPIO_SYS_CLASS +# include +# include +# include +#endif +#ifdef __GNUC__ +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wunused-but-set-variable" +#endif + + +/*! \class PIGPIO + * \brief GPIO support + * + * \section PIGPIO_sec0 Synopsis + * This class provide initialize, get/set and watch functions for GPIO. + * + * Currently supported only \"/sys/class/gpio\" mechanism on Linux. + * + * This class should be used with \a PIGPIO::instance() singleton. + * + * + * + * \section PIGPIO_sec1 API + * There are several function to directly read or write pin states. + * + * Also you can start %PIGPIO as thread to watch pin states and receive + * \a pinChanged() event. + * + */ + + +PIGPIO::PIGPIO(): PIThread() { +} + + +PIGPIO::~PIGPIO() { + stop(); + waitForFinish(100); + PIMutexLocker ml(mutex); +#ifdef GPIO_SYS_CLASS + PIVector ids = gpio_.keys(); + for(int i = 0; i < ids.size_s(); i++) { + GPIOData & g(gpio_[ids[i]]); + if (g.fd != -1) { + ::close(g.fd); + g.fd = -1; + } + } + gpio_.clear(); +#endif +} + + +PIGPIO * PIGPIO::instance() { + static PIGPIO ret; + return &ret; +} + + + +PIString PIGPIO::GPIOName(int gpio_num) { + return PIStringAscii("gpio") + PIString::fromNumber(gpio_num); +} + + +void PIGPIO::exportGPIO(int gpio_num) { +#ifdef GPIO_SYS_CLASS + PIString valfile = "/sys/class/gpio/" + GPIOName(gpio_num) + "/value"; + int fd = ::open(valfile.dataAscii(), O_RDONLY); + if (fd != -1) { + ::close(fd); + return; + } + int ret = 0; + ret = system(PIString("echo " + PIString::fromNumber(gpio_num) + " >> /sys/class/gpio/export").dataAscii()); + if (ret == 0) { + PITimeMeasurer tm; + while (tm.elapsed_s() < 1.) { + fd = ::open(valfile.dataAscii(), O_RDWR); + if (fd != -1) { + ::close(fd); + return; + } + piMSleep(1); + } + } +#endif +} + + +void PIGPIO::openGPIO(GPIOData & g) { +#ifdef GPIO_SYS_CLASS + if (g.fd != -1) { + ::close(g.fd); + g.fd = -1; + } + PIString fp = "/sys/class/gpio/" + g.name + "/value"; + g.fd = ::open(fp.dataAscii(), O_RDWR); + //piCoutObj << "initGPIO" << g.num << ":" << fp << g.fd << errorString(); +#endif +} + + +bool PIGPIO::getPinState(int gpio_num) { +#ifdef GPIO_SYS_CLASS + GPIOData & g(gpio_[gpio_num]); + char r = 0; + int ret = 0; + if (g.fd != -1) { + ::lseek(g.fd, 0, SEEK_SET); + ret = ::read(g.fd, &r, sizeof(r)); + if (ret > 0) { + if (r == '1') return true; + if (r == '0') return false; + } + } + //piCoutObj << "pinState" << gpio_num << ":" << ret << (int)r << errorString(); + //initGPIO(gpio_num, PIGPIO::In); TODO: discuss - why? +#endif + return false; +} + + +void PIGPIO::begin() { + PIMutexLocker ml(mutex); + if (watch_state.isEmpty()) return; + PIVector ids = watch_state.keys(); + for(int i = 0; i < ids.size_s(); i++) { + GPIOData & g(gpio_[ids[i]]); + if (g.num != -1 && !g.name.isEmpty()) { + openGPIO(g); + watch_state[ids[i]] = getPinState(ids[i]); + } + } +} + + +void PIGPIO::run() { + PIMutexLocker ml(mutex); + if (watch_state.isEmpty()) return; + PIVector ids = watch_state.keys(); + for(int i = 0; i < ids.size_s(); i++) { + GPIOData & g(gpio_[ids[i]]); + if (g.num != -1 && !g.name.isEmpty()) { + bool v = getPinState(g.num); + //piCoutObj << "*** pin state ***" << ids[i] << "=" << v; + if (watch_state[g.num] != v) { + watch_state[g.num] = v; + pinChanged(g.num, v); + } + } + } +} + + +void PIGPIO::end() { + PIMutexLocker ml(mutex); + if (watch_state.isEmpty()) return; + PIVector ids = watch_state.keys(); + for(int i = 0; i < ids.size_s(); i++) { + GPIOData & g(gpio_[ids[i]]); + if (g.fd != -1) { +#ifdef GPIO_SYS_CLASS + ::close(g.fd); +#endif + g.fd = -1; + } + } +} + + +void PIGPIO::initPin(int gpio_num, Direction dir) { //, PullUpDownControl pull) { +#ifdef GPIO_SYS_CLASS + PIMutexLocker ml(mutex); + GPIOData & g(gpio_[gpio_num]); + if (g.num == -1) { + g.num = gpio_num; + g.name = GPIOName(gpio_num); + exportGPIO(gpio_num); + } + g.dir = dir; + int ret = 0; + switch(dir) { + case In: + ret = system(("echo in >> /sys/class/gpio/" + g.name + "/direction").dataAscii()); + break; + case Out: + ret = system(("echo out >> /sys/class/gpio/" + g.name + "/direction").dataAscii()); + break; + default: break; + } + openGPIO(g); +#endif +} + + +void PIGPIO::pinSet(int gpio_num, bool value) { +#ifdef GPIO_SYS_CLASS + PIMutexLocker ml(mutex); + GPIOData & g(gpio_[gpio_num]); + int ret = 0; + if (g.fd != -1) { + if (value) ret = ::write(g.fd, "1", 1); + else ret = ::write(g.fd, "0", 1); + } + //piCoutObj << "pinSet" << gpio_num << ":" << ret << errorString(); +#endif +} + + +bool PIGPIO::pinState(int gpio_num) { + PIMutexLocker ml(mutex); + return getPinState(gpio_num); +} + + +void PIGPIO::pinBeginWatch(int gpio_num) { + PIMutexLocker ml(mutex); + GPIOData & g(gpio_[gpio_num]); + if (g.fd != -1) { +#ifdef GPIO_SYS_CLASS + ::close(g.fd); +#endif + g.fd = -1; + } + watch_state.insert(gpio_num, false); +} + + +void PIGPIO::pinEndWatch(int gpio_num) { + PIMutexLocker ml(mutex); + watch_state.remove(gpio_num); +} + + +void PIGPIO::clearWatch() { + PIMutexLocker ml(mutex); + watch_state.clear(); +} + + +#ifdef __GNUC__ +# pragma GCC diagnostic pop +#endif diff --git a/src_main/io_devices/pigpio.h b/src_main/io_devices/pigpio.h new file mode 100644 index 00000000..ea8dc4b6 --- /dev/null +++ b/src_main/io_devices/pigpio.h @@ -0,0 +1,117 @@ +/*! \file pigpio.h + * \brief GPIO +*/ +/* + PIP - Platform Independent Primitives + GPIO + Andrey Bychkov work.a.b@yandex.ru, Ivan Pelipenko peri4ko@yandex.ru + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program. If not, see . +*/ + +#ifndef PIGPIO_H +#define PIGPIO_H + +#include "pithread.h" + + +class PIGPIO: public PIThread +{ + PIOBJECT_SUBCLASS(PIGPIO, PIThread) +public: + PIGPIO(); + virtual ~PIGPIO(); + + //! \brief Work mode for pin + enum Direction { + In /** Input direction (read) */, + Out /** Output direction (write) */ + }; + //enum PullUpDownControl {NoPull, PullUp, PullDown}; + + + //! \brief Returns singleton object of %PIGPIO + static PIGPIO * instance(); + + + //! \brief Initialize pin \"gpio_num\" for \"dir\" mode + void initPin(int gpio_num, Direction dir = PIGPIO::In);//, PullUpDownControl pull); + + + //! \brief Set pin \"gpio_num\" value to \"value\" + void pinSet (int gpio_num, bool value); + + //! \brief Set pin \"gpio_num\" value to \b true + void pinHigh (int gpio_num) {pinSet(gpio_num, true );} + + //! \brief Set pin \"gpio_num\" value to \b false + void pinLow (int gpio_num) {pinSet(gpio_num, false);} + + //! \brief Returns pin \"gpio_num\" state + bool pinState(int gpio_num); + + + //! \brief Starts watch for pin \"gpio_num\". + //! \details Pins watching starts only with \a PIThread::start() function! + //! This function doesn`t affect thread state + void pinBeginWatch(int gpio_num); + + //! \brief End watch for pin \"gpio_num\". + //! \details Pins watching starts only with \a PIThread::start() function! + //! This function doesn`t affect thread state + void pinEndWatch (int gpio_num); + + //! \brief End watch for all pins. + //! \details Pins watching starts only with \a PIThread::start() function! + //! This function doesn`t affect thread state + void clearWatch(); + + + EVENT2(pinChanged, int, gpio_num, bool, new_value) + + //! \events + //! \{ + + //! \fn void pinChanged(int gpio_num, bool new_value) + //! \brief Raise on pin \"gpio_num\" state changes to \"new_value\" + //! \details Important! This event will be raised only with started + //! thread. + + //! \} + +private: + struct GPIOData { + GPIOData() {dir = PIGPIO::In; num = fd = -1;} + PIString name; + int dir; + int num; + int fd; + }; + void exportGPIO(int gpio_num); + void openGPIO(GPIOData & g); + bool getPinState(int gpio_num); + void begin(); + void run(); + void end(); + + static PIString GPIOName(int gpio_num); + + PIMap gpio_; + PIMap watch_state; + PIMutex mutex; + +}; + + +#endif // PIDIR_H diff --git a/src_main/io_devices/piserial.cpp b/src_main/io_devices/piserial.cpp index bf81dda2..a83691c6 100755 --- a/src_main/io_devices/piserial.cpp +++ b/src_main/io_devices/piserial.cpp @@ -23,10 +23,10 @@ #include "pipropertystorage.h" #include -#if defined(WINDOWS) || defined(FREERTOS) +#if defined(FREERTOS) # define PISERIAL_NO_PINS #endif -#ifdef PISERIAL_NO_PINS +#if defined(PISERIAL_NO_PINS) || defined(WINDOWS) # define TIOCM_LE 1 # define TIOCM_DTR 4 # define TIOCM_RTS 7 @@ -38,7 +38,19 @@ # define TIOCM_DSR 6 #endif #ifdef WINDOWS +# ifndef INITGUID +# define INITGUID +# include +# undef INITGUID +# else +# include +# endif +# include # include +# include +# include +# include +# include # define B50 50 # define B75 75 # define B110 110 @@ -130,7 +142,13 @@ * \section PISerial_sec0 Synopsis * This class provide access to serial device, e.g. COM port. It can read, * write, wait for write. There are several read and write functions. - * + * + * \section PISerial_sec0 FullPath + * Since version 1.16.0 you can use as \a path DeviceInfo::id() USB identifier. + * \code + * PISerial * s = new PISerial("0403:6001"); + * PIIODevice * d = PIIODevice::createFromFullPath("ser://0403:6001:115200"); + * \endcode * */ @@ -149,6 +167,21 @@ PRIVATE_DEFINITION_START(PISerial) PRIVATE_DEFINITION_END(PISerial) + + +PISerial::DeviceInfo::DeviceInfo() { + vID = pID = 0; +} + + +PIString PISerial::DeviceInfo::id() const { + return PIString::fromNumber(vID, 16).toLowerCase().expandLeftTo(4, '0') + ":" + + PIString::fromNumber(pID, 16).toLowerCase().expandLeftTo(4, '0'); +} + + + + PISerial::PISerial(): PIIODevice("", ReadWrite) { construct(); } @@ -255,37 +288,51 @@ bool PISerial::isDSR() const {return isBit(TIOCM_DSR, "DSR");} bool PISerial::setBit(int bit, bool on, const PIString & bname) { -#ifndef PISERIAL_NO_PINS if (fd < 0) { piCoutObj << "setBit" << bname << " error: \"" << path() << "\" is not opened!"; return false; } +#ifndef PISERIAL_NO_PINS +# ifdef WINDOWS + static int bit_map_on [] = {0, 0, 0, 0, SETDTR, 0, 0, SETRTS, 0, 0, 0}; + static int bit_map_off[] = {0, 0, 0, 0, CLRDTR, 0, 0, CLRRTS, 0, 0, 0}; + int action = (on ? bit_map_on : bit_map_off)[bit]; + if (action > 0) { + if (EscapeCommFunction(PRIVATE->hCom, action) == 0) { + piCoutObj << "setBit" << bname << " error: " << errorString(); + return false; + } + return true; + } +# else if (ioctl(fd, on ? TIOCMBIS : TIOCMBIC, &bit) < 0) { piCoutObj << "setBit" << bname << " error: " << errorString(); return false; } return true; -#else +# endif +#endif piCoutObj << "setBit" << bname << " doesn`t implemented, sorry :-("; return false; -#endif } bool PISerial::isBit(int bit, const PIString & bname) const { -#ifndef PISERIAL_NO_PINS if (fd < 0) { piCoutObj << "isBit" << bname << " error: \"" << path() << "\" is not opened!"; return false; } +#ifndef PISERIAL_NO_PINS +# ifdef WINDOWS +# else int ret = 0; if (ioctl(fd, TIOCMGET, &ret) < 0) piCoutObj << "isBit" << bname << " error: " << errorString(); return ret & bit; -#else +# endif +#endif piCoutObj << "isBit" << bname << " doesn`t implemented, sorry :-("; return false; -#endif } @@ -296,28 +343,6 @@ void PISerial::flush() { } -bool PISerial::closeDevice() { - if (isRunning() && !isStopping()) { - stop(); - PIThread::terminate(); - } - if (fd != -1) { -#ifdef WINDOWS - SetCommState(PRIVATE->hCom, &PRIVATE->sdesc); - SetCommMask(PRIVATE->hCom, PRIVATE->mask); -// piCoutObj << "close" << - CloseHandle(PRIVATE->hCom); - PRIVATE->hCom = 0; -#else - tcsetattr(fd, TCSANOW, &PRIVATE->sdesc); - ::close(fd); -#endif - fd = -1; - } - return true; -} - - int PISerial::convertSpeed(PISerial::Speed speed) { switch (speed) { case S50: return B50; @@ -509,16 +534,31 @@ bool PISerial::send(const void * data, int size) { bool PISerial::openDevice() { - //piCout << "ser open" << path(); - if (path().isEmpty()) return false; + PIString p = path(); + //piCout << "ser open" << p; + PIString pl = p.toLowerCase().removeAll(' '); + if (!pl.startsWith("/") && !pl.startsWith("com")) { + p.clear(); + PIVector devs = availableDevicesInfo(); + piForeachC (DeviceInfo & d, devs) { + if (d.id() == pl) { + p = d.path; + piBreak; + } + } + if (p.isEmpty()) { + piCoutObj << "Unable to find device \"" << pl << "\""; + } + } + if (p.isEmpty()) return false; #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;} - PIString wp = "//./" + path(); - PRIVATE->hCom = CreateFileA(wp.data(), ds, sm, 0, OPEN_EXISTING, FILE_ATTRIBUTE_SYSTEM, 0); + PIString wp = "//./" + p; + PRIVATE->hCom = CreateFileA(wp.dataAscii(), ds, sm, 0, OPEN_EXISTING, FILE_ATTRIBUTE_SYSTEM, 0); if (PRIVATE->hCom == INVALID_HANDLE_VALUE) { - piCoutObj << "Unable to open \"" << path() << "\""; + piCoutObj << "Unable to open \"" << p << "\""; fd = -1; return false; } @@ -530,21 +570,42 @@ bool PISerial::openDevice() { 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); + fd = ::open(p.data(), O_NOCTTY | om); if (fd == -1) { - piCoutObj << "Unable to open \"" << path() << "\""; + piCoutObj << "Unable to open \"" << p << "\""; return false; } tcgetattr(fd, &PRIVATE->desc); PRIVATE->sdesc = PRIVATE->desc; - //piCoutObj << "Initialized " << path_; + //piCoutObj << "Initialized " << p; #endif applySettings(); return true; } +bool PISerial::closeDevice() { + if (isRunning() && !isStopping()) { + stop(); + PIThread::terminate(); + } + if (fd != -1) { +#ifdef WINDOWS + SetCommState(PRIVATE->hCom, &PRIVATE->sdesc); + SetCommMask(PRIVATE->hCom, PRIVATE->mask); +// piCoutObj << "close" << + CloseHandle(PRIVATE->hCom); + PRIVATE->hCom = 0; +#else + tcsetattr(fd, TCSANOW, &PRIVATE->sdesc); + ::close(fd); +#endif + fd = -1; + } + return true; +} + + void PISerial::applySettings() { #ifdef WINDOWS if (fd == -1) return; @@ -718,6 +779,13 @@ PIString PISerial::constructFullPathDevice() const { void PISerial::configureFromFullPathDevice(const PIString & full_path) { PIStringList pl = full_path.split(":"); + if (pl.size_s() > 1) { + PIString _s = pl[0].toLowerCase().removeAll(' ').trim(); + if (!_s.startsWith("/") && !_s.startsWith("com")) { + pl[0] += ":" + pl[1]; + pl.remove(1); + } + } for (int i = 0; i < pl.size_s(); ++i) { PIString p(pl[i]); switch (i) { @@ -792,24 +860,134 @@ PIVector PISerial::availableSpeeds() { PIStringList PISerial::availableDevices(bool test) { - PIStringList dl; + PIVector devs = availableDevicesInfo(test); + PIStringList ret; + piForeachC (DeviceInfo & d, devs) + ret << d.path; + return ret; +} + + #ifdef WINDOWS - HKEY key = 0; +PIString devicePortName(HDEVINFO deviceInfoSet, PSP_DEVINFO_DATA deviceInfoData) { + PIString ret; + const HKEY key = SetupDiOpenDevRegKey(deviceInfoSet, deviceInfoData, DICS_FLAG_GLOBAL, 0, DIREG_DEV, KEY_READ); + if (key == INVALID_HANDLE_VALUE) + return ret; + static const wchar_t * const keyTokens[] = { + L"PortName\0", + L"PortNumber\0" + }; + static const int keys_count = sizeof(keyTokens) / sizeof(keyTokens[0]); + for (int i = 0; i < keys_count; ++i) { + DWORD dataType = 0; + DWORD bytesRequired = MAX_PATH; + PIVector outputBuffer(MAX_PATH + 1); + for (;;) { + LONG res = RegQueryValueExW(key, keyTokens[i], NULL, &dataType, (LPBYTE)outputBuffer.data(), &bytesRequired); + if (res == ERROR_MORE_DATA) { + outputBuffer.resize(bytesRequired / sizeof(wchar_t) + 2); + continue; + } else if (res == ERROR_SUCCESS) { + if (dataType == REG_SZ) + ret = PIString(outputBuffer.data()); + else if (dataType == REG_DWORD) + ret = PIStringAscii("COM") + PIString::fromNumber(*(PDWORD(&outputBuffer[0]))); + } + break; + } + if (!ret.isEmpty()) + break; + } + RegCloseKey(key); + return ret; +} + + +PIString deviceRegistryProperty(HDEVINFO deviceInfoSet, PSP_DEVINFO_DATA deviceInfoData, DWORD property) { + DWORD dataType = 0; + DWORD bytesRequired = MAX_PATH; + PIVector outputBuffer(MAX_PATH + 1); + for (;;) { + if (SetupDiGetDeviceRegistryPropertyW(deviceInfoSet, deviceInfoData, property, &dataType, (PBYTE)outputBuffer.data(), bytesRequired, &bytesRequired)) + break; + if ((GetLastError() != ERROR_INSUFFICIENT_BUFFER) || (dataType != REG_SZ && dataType != REG_EXPAND_SZ)) + return PIString(); + outputBuffer.resize(bytesRequired / sizeof(wchar_t) + 2, 0); + } + return PIString(outputBuffer.data()); +} + + +PIString deviceInstanceIdentifier(DEVINST deviceInstanceNumber) { + PIVector outputBuffer(MAX_DEVICE_ID_LEN + 1); + if (CM_Get_Device_IDW(deviceInstanceNumber, (PWCHAR)outputBuffer.data(), MAX_DEVICE_ID_LEN, 0) != CR_SUCCESS) { + return PIString(); + } + return PIString(outputBuffer.data()); +} + + +bool parseID(PIString str, PISerial::DeviceInfo & di) { + if (str.isEmpty()) return false; + int i = str.find("VID_"); + if (i > 0) di.vID = str.mid(i + 4, 4).toInt(16); + i = str.find("PID_"); + if (i > 0) di.pID = str.mid(i + 4, 4).toInt(16); + return (di.vID > 0) && (di.pID > 0); +} +#endif + + +PIVector PISerial::availableDevicesInfo(bool test) { + PIVector ret; + DeviceInfo di; +#ifdef WINDOWS + /*HKEY key = 0; RegOpenKey(HKEY_LOCAL_MACHINE, (LPCTSTR)"HARDWARE\\DEVICEMAP\\SERIALCOMM", &key); if (key != 0) { char name[1024], data[1024]; DWORD index = 0; - LONG ret = ERROR_SUCCESS; - while (ret != ERROR_NO_MORE_ITEMS) { + LONG res = ERROR_SUCCESS; + while (res != ERROR_NO_MORE_ITEMS) { memset(name, 0, 1024); memset(data, 0, 1024); DWORD name_len = 1024, data_len = 1024, type = 0; - ret = RegEnumValue(key, index, (LPTSTR)name, &name_len, NULL, &type, (uchar * )data, &data_len); - if (ret == ERROR_NO_MORE_ITEMS) break; - dl << PIString(data, data_len).trim(); + res = RegEnumValue(key, index, (LPTSTR)name, &name_len, NULL, &type, (uchar * )data, &data_len); + if (res == ERROR_NO_MORE_ITEMS) break; + di.path = PIString(data, data_len).trim(); + ret << di; index++; } RegCloseKey(key); + }*/ + static const GUID guids[] = {GUID_DEVINTERFACE_MODEM, GUID_DEVINTERFACE_COMPORT}; + static const int guids_cnt = sizeof(guids) / sizeof(GUID); + for (int i = 0; i < guids_cnt; ++i) { + const HDEVINFO dis = SetupDiGetClassDevs(&(guids[i]), NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); + if (dis == INVALID_HANDLE_VALUE) continue; + SP_DEVINFO_DATA did; + memset(&did, 0, sizeof(did)); + did.cbSize = sizeof(did); + DWORD index = 0; + while (SetupDiEnumDeviceInfo(dis, index++, &did)) { + di = DeviceInfo(); + di.path = devicePortName(dis, &did); + if (!di.path.startsWith("COM")) continue; + di.description = deviceRegistryProperty(dis, &did, SPDRP_DEVICEDESC); + di.manufacturer = deviceRegistryProperty(dis, &did, SPDRP_MFG); + PIString id_str = deviceInstanceIdentifier(did.DevInst); + if (!parseID(id_str, di)) { + DEVINST pdev = 0; + if (CM_Get_Parent(&pdev, did.DevInst, 0) == CR_SUCCESS) { + id_str = deviceInstanceIdentifier(pdev); + parseID(id_str, di); + } + } + ret << di; + //piCout << "dev" << did.DevInst << di; + } + SetupDiDestroyDeviceInfoList(dis); } #else # ifndef ANDROID @@ -849,24 +1027,51 @@ PIStringList PISerial::availableDevices(bool test) { # endif PIDir dir("/dev"); PIVector de = dir.entries(); +# ifdef LINUX + char linkbuf[1024]; +# endif piForeachC (PIFile::FileInfo & e, de) { // TODO changes in FileInfo piForeachC (PIString & p, prefixes) { - if (e.name().startsWith(p)) - dl << e.path; + if (e.name().startsWith(p)) { + di = DeviceInfo(); + di.path = e.path; +# ifdef LINUX + ssize_t lsz = readlink(("/sys/class/tty/" + e.name()).dataAscii(), linkbuf, 1024); + if (lsz > 0) { + PIString fpath = "/sys/class/tty/" + PIString(linkbuf, lsz) + "/"; + PIFile _f; + for (int i = 0; i < 5; ++i) { + fpath += "../"; + //piCout << "try" << fpath; + if (_f.open(fpath + "idVendor", PIIODevice::ReadOnly)) + di.vID = PIString(_f.readAll()).trim().toInt(16); + if (_f.open(fpath + "idProduct", PIIODevice::ReadOnly)) + di.pID = PIString(_f.readAll()).trim().toInt(16); + if (_f.open(fpath + "product", PIIODevice::ReadOnly)) + di.description = PIString(_f.readAll()).trim(); + if (_f.open(fpath + "manufacturer", PIIODevice::ReadOnly)) + di.manufacturer = PIString(_f.readAll()).trim(); + if (di.pID > 0) + break; + } + } +# endif + ret << di; + } } } # endif #endif if (test) { - for (int i = 0; i < dl.size_s(); ++i) { + for (int i = 0; i < ret.size_s(); ++i) { #ifdef WINDOWS - void * hComm = CreateFileA(dl[i].data(), GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_ATTRIBUTE_SYSTEM, 0); + void * hComm = CreateFileA(ret[i].path.dataAscii(), GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_ATTRIBUTE_SYSTEM, 0); if (hComm == INVALID_HANDLE_VALUE) { #else - int fd = ::open(dl[i].data(), O_NOCTTY | O_RDONLY); + int fd = ::open(ret[i].path.dataAscii(), O_NOCTTY | O_RDONLY); if (fd == -1) { #endif - dl.remove(i); + ret.remove(i); --i; continue; } @@ -889,7 +1094,7 @@ PIStringList PISerial::availableDevices(bool test) { #endif if (!rok) { - dl.remove(i); + ret.remove(i); --i; continue; } @@ -900,7 +1105,7 @@ PIStringList PISerial::availableDevices(bool test) { #endif } } - return dl; + return ret; } diff --git a/src_main/io_devices/piserial.h b/src_main/io_devices/piserial.h index 685926c9..e52fec2f 100755 --- a/src_main/io_devices/piserial.h +++ b/src_main/io_devices/piserial.h @@ -71,6 +71,29 @@ public: S4000000 /*! 4000000 baud */ = 4000000 }; + //! \brief Information about serial device + struct DeviceInfo { + DeviceInfo(); + + //! \brief String representation of USB ID in format \"xxxx:xxxx\" + PIString id() const; + + //! \brief USB Vendor ID + uint vID; + + //! \brief USB Product ID + uint pID; + + //! \brief Path to device, e.g. "COM2" or "/dev/ttyUSB0" + PIString path; + + //! \brief Device description + PIString description; + + //! \brief Device manufacturer + PIString manufacturer; + }; + //! Contructs %PISerial with device name "device", speed "speed" and parameters "params" explicit PISerial(const PIString & device, PISerial::Speed speed = S115200, PIFlags params = 0); @@ -173,9 +196,12 @@ public: //! \brief Returns all available speeds for serial devices static PIVector availableSpeeds(); - //! \brief Returns all available system devices. If "test" each device will be tried to open + //! \brief Returns all available system devices path. If "test" each device will be tried to open static PIStringList availableDevices(bool test = false); + //! \brief Returns all available system devices. If "test" each device will be tried to open + static PIVector availableDevicesInfo(bool test = false); + //! \ioparams //! \{ #ifdef DOXYGEN @@ -232,4 +258,16 @@ protected: }; + +inline PICout operator <<(PICout s, const PISerial::DeviceInfo & v) { + s << v.path << " (" << v.id() << ", \"" << v.manufacturer << "\", \"" << v.description << "\")"; + return s; +} + +inline bool operator ==(const PISerial::DeviceInfo & v0, const PISerial::DeviceInfo & v1) {return v0.path == v1.path;} +inline bool operator !=(const PISerial::DeviceInfo & v0, const PISerial::DeviceInfo & v1) {return v0.path != v1.path;} +inline PIByteArray & operator <<(PIByteArray & s, const PISerial::DeviceInfo & v) {s << v.vID << v.pID << v.path << v.description << v.manufacturer; return s;} +inline PIByteArray & operator >>(PIByteArray & s, PISerial::DeviceInfo & v) {s >> v.vID >> v.pID >> v.path >> v.description >> v.manufacturer; return s;} + + #endif // PISERIAL_H diff --git a/src_main/system/pisystemmonitor.cpp b/src_main/system/pisystemmonitor.cpp index 5d29308b..686c3942 100755 --- a/src_main/system/pisystemmonitor.cpp +++ b/src_main/system/pisystemmonitor.cpp @@ -375,7 +375,6 @@ void PISystemMonitor::gatherThread(llong id) { ts.name = tbid.value(id, ""); #else ts.name = tbid.value(id, ""); - PISystemTime ct = PISystemTime::current(); # ifndef WINDOWS PIFile f(PRIVATE->proc_dir + "task/" + PIString::fromNumber(id) + "/stat"); //piCout << f.path(); @@ -399,6 +398,7 @@ void PISystemMonitor::gatherThread(llong id) { ts.user_time = PISystemTime::fromMilliseconds(sl[12].toInt() * 10.); ts.kernel_time = PISystemTime::fromMilliseconds(sl[13].toInt() * 10.); # else + PISystemTime ct = PISystemTime::current(); FILETIME times[4]; HANDLE thdl = OpenThread(THREAD_QUERY_INFORMATION, FALSE, DWORD(id)); if (thdl == NULL) {