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) {