diff --git a/CMakeLists.txt b/CMakeLists.txt index 6c9e208e..4c4fe507 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,6 @@ project(pip) cmake_minimum_required(VERSION 2.6) -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g3") +#set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g3") include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR}) include(CheckFunctionExists) diff --git a/doc/examples/pistatemachine.cpp b/doc/examples/pistatemachine.cpp index d5228a91..f7d2dda3 100644 --- a/doc/examples/pistatemachine.cpp +++ b/doc/examples/pistatemachine.cpp @@ -4,7 +4,7 @@ enum Mode {Start, Manual, Auto, Finish, End}; class Machine: public PIStateMachine { - PIOBJECT(Machine) + PIOBJECT_SUBCLASS(Machine, PIObject) public: Machine() { addState(Start, "start", HANDLER(startFunc)); diff --git a/main.cpp b/main.cpp index c82c2ab5..6620bcfb 100644 --- a/main.cpp +++ b/main.cpp @@ -1,9 +1,12 @@ #include "pip.h" #include "ccm_kbd.h" +TileProgress * tp; void key_event(PIKbdListener::KeyEvent e, void*) { PICodeInfo::EnumInfo * ei = PICodeInfo::enumsInfo->value("PIKbdListener::SpecialKey"); if (!ei) return; + if (e.key == '-') {tp->value -= 1.; return;} + if (e.key == '+') {tp->value += 1.; return;} piCout << PICoutManipulators::NewLine << "modifiers" << e.modifiers; piForeachC (PICodeInfo::EnumeratorInfo & i, ei->members) if (i.value == e.key) { @@ -21,7 +24,7 @@ public: delete t->parentTile(); } EVENT_HANDLER1(void, eventKey, PIKbdListener::KeyEvent, e) { - piCout << "key" << e.key; + //piCout << "key" << e.key; } }; @@ -112,15 +115,13 @@ public: }; int main (int argc, char * argv[]) { - int cc = PIString(argv[1]).toInt(); + /*int cc = PIString(argv[1]).toInt(); piCout << "Deque"; Test > testd(cc); - /*piCout << PICoutManipulators::NewLine << "Vector"; - Test > testv(cc);*/ + return 0;*/ - return 0; /*if (!(argc == 3 || argc == 4)) { piCout << "UDPFileTransfer"; piCout << "USE: piptest [src_ip_port] [dst_ip_port] {filename}"; @@ -140,12 +141,13 @@ int main (int argc, char * argv[]) { } else { piCout << "wait for receiving"; }*/ + Catcher catcher; PIScreen screen(false, key_event); CONNECTU(&screen, tileEvent, &catcher, event) CONNECTU(&screen, keyPressed, &catcher, eventKey) screen.enableExitCapture(PIKbdListener::F10); - //screen.start(); + screen.start(); float cx = 0, cy = 0, vx = 1., vy = 0.3, t = 0.; PITimeMeasurer tm; Color col = Red; @@ -220,14 +222,13 @@ int main (int argc, char * argv[]) { tile2->size_policy = PIScreenTypes::Preferred; tile->addTile(tile2); - tile2 = new TileButtons("butt0"); + /*tile2 = new TileButtons("butt0"); tile2->back_format.color_back = Transparent; tile->addTile(tile2); ((TileButtons*)tile2)->content << TileButtons::Button("first", CellFormat(Green, Transparent)); ((TileButtons*)tile2)->content << TileButtons::Button("sec", CellFormat(Green, Red)); ((TileButtons*)tile2)->content << TileButtons::Button("3", CellFormat(Green, Transparent)); ((TileButtons*)tile2)->direction = PIScreenTypes::Horizontal; - //((TileButtons*)tile)->alignment = Center; tile2 = new TileButtons("butt1"); tile2->back_format.color_back = Transparent; @@ -236,11 +237,52 @@ int main (int argc, char * argv[]) { ((TileButtons*)tile2)->content << TileButtons::Button("sec2", CellFormat(Green, Red)); ((TileButtons*)tile2)->content << TileButtons::Button("333", CellFormat(Green, Transparent)); ((TileButtons*)tile2)->direction = PIScreenTypes::Horizontal; - //((TileButtons*)tile)->alignment = Center; - ((TileButtons*)tile)->back_format.color_back = Yellow; + tile2 = new TileButtons("butt2"); + tile2->back_format.color_back = Transparent; + tile->addTile(tile2); + ((TileButtons*)tile2)->content << TileButtons::Button("fF", CellFormat(Green, Transparent)); + ((TileButtons*)tile2)->content << TileButtons::Button("sec2", CellFormat(Green, Red)); + ((TileButtons*)tile2)->content << TileButtons::Button("333", CellFormat(Green, Transparent)); + ((TileButtons*)tile2)->direction = PIScreenTypes::Vertical;*/ + + tile2 = new TileButton("butt0"); + tile2->back_format.color_back = Transparent; + tile->addTile(tile2); + ((TileButton*)tile2)->text = "first"; + + tile2 = new TileButton("butt1"); + tile2->back_format.color_back = Transparent; + tile->addTile(tile2); + ((TileButton*)tile2)->text = "sec2"; + + tile2 = new TileCheck("check0"); + tile2->back_format.color_back = Transparent; + tile->addTile(tile2); + ((TileCheck*)tile2)->text = "check"; + + tile2 = new TileCheck("check1"); + tile2->back_format.color_back = Transparent; + tile->addTile(tile2); + ((TileCheck*)tile2)->text = "ch"; + ((TileCheck*)tile2)->toggled = true; + + tile2 = new TileButton("butt2"); + tile2->back_format.color_back = Transparent; + tile->addTile(tile2); + ((TileButton*)tile2)->text = "F"; + + tp = new TileProgress("butt2"); + //tile2->back_format.color_back = Transparent; + tp->maximum = 200; + tp->prefix = "prog: "; + tp->suffix = " bytes"; + tile->addTile(tp); + + ((TileButton*)tile)->back_format.color_back = Yellow; tile->setMargins(2, 2, 1, 1); tile->spacing = 1; + tile->direction = PIScreenTypes::Horizontal; screen.setDialogTile(tile); //screen.rootTile()->hide(); diff --git a/src/console/piscreen.cpp b/src/console/piscreen.cpp index e7ec0b49..f406c372 100644 --- a/src/console/piscreen.cpp +++ b/src/console/piscreen.cpp @@ -379,10 +379,14 @@ bool PIScreen::nextFocus(PIScreenTile * rt, PIKbdListener::KeyEvent key) { if (tile_focus) { if (tile_focus->focus_flags[NextByTab] && key.key == PIKbdListener::Tab) next = 1; - if (tile_focus->focus_flags[NextByArrows]) { + if (tile_focus->focus_flags[NextByArrowsHorizontal]) { if (key.key == PIKbdListener::LeftArrow) next = -1; if (key.key == PIKbdListener::RightArrow) next = 1; } + if (tile_focus->focus_flags[NextByArrowsVertical]) { + if (key.key == PIKbdListener::UpArrow) next = -1; + if (key.key == PIKbdListener::DownArrow) next = 1; + } } //piCout << ftl.size() << ind << next; if (next != 0) { diff --git a/src/console/piscreentiles.cpp b/src/console/piscreentiles.cpp index 1bd93eb7..041381ae 100644 --- a/src/console/piscreentiles.cpp +++ b/src/console/piscreentiles.cpp @@ -79,7 +79,7 @@ void TileSimple::drawEvent(PIScreenDrawer * d) { TileList::TileList(const PIString & n): PIScreenTile(n) { alignment = Left; - focus_flags = CanHasFocus | NextByArrows | NextByTab; + focus_flags = CanHasFocus | NextByArrowsHorizontal | NextByTab; lhei = offset = cur = 0; selection_mode = NoSelection; } @@ -206,6 +206,39 @@ bool TileList::keyEvent(PIKbdListener::KeyEvent key) { +TileButton::TileButton(const PIString & n): PIScreenTile(n) { + focus_flags = CanHasFocus | NextByTab | NextByArrowsAll; +} + + +void TileButton::sizeHint(int & w, int & h) const { + w = text.size_s() + 2; + h = 1; +} + + +void TileButton::drawEvent(PIScreenDrawer * d) { + Color cb = has_focus ? Blue : Cyan; + Color ct = has_focus ? White : Black; + int ff = has_focus ? Bold : 0; + d->fillRect(x, y, x + width, y + 1, ' ', Default, cb); + d->drawText(x, y, "[", ct, Transparent, ff); + d->drawText(x + (width - text.size_s()) / 2, y, text, ct, Transparent, ff); + d->drawText(x + width - 1, y, "]", ct, Transparent, ff); +} + + +bool TileButton::keyEvent(PIKbdListener::KeyEvent key) { + if (key.key == PIKbdListener::Space || key.key == PIKbdListener::Return) { + raiseEvent(TileEvent(ButtonClicked)); + return true; + } + return PIScreenTile::keyEvent(key); +} + + + + TileButtons::TileButtons(const PIString & n): PIScreenTile(n) { focus_flags = CanHasFocus | NextByTab; direction = Horizontal; @@ -217,13 +250,13 @@ void TileButtons::sizeHint(int & w, int & h) const { w = h = 0; if (direction == Horizontal) { piForeachC (Button & b, content) - w += b.first.size_s() + 2; + w += b.first.size_s() + 4; w += piMaxi(0, content.size_s() - 1) * 2; - h += 3; + h += 1; } else { piForeachC (Button & b, content) - w = piMaxi(w, b.first.size_s() + 2 + 4); - h += content.size_s() * 3; + w = piMaxi(w, b.first.size_s() + 4); + h += content.size_s(); h += piMaxi(0, content.size_s() - 1); } } @@ -243,15 +276,17 @@ void TileButtons::drawEvent(PIScreenDrawer * d) { Button & b(content[i]); int cw = b.first.size_s() + 2, xo(0); if (direction == Vertical) { - cw = width - 4; + cw = width - 2; xo = (cw - b.first.size_s()) / 2 - 1; } - d->fillRect(cx, cy, cx + cw, cy + 3, ' ', Default, cb); - d->drawText(cx + 1 + xo, cy + 1, b.first, ct, Transparent, ff); + d->fillRect(cx, cy, cx + cw + 2, cy + 1, ' ', Default, cb); + d->drawText(cx, cy, "[", ct, Transparent, ff); + d->drawText(cx + xo + 2, cy, b.first, ct, Transparent, ff); + d->drawText(cx + cw + 1, cy, "]", ct, Transparent, ff); if (direction == Horizontal) - cx += b.first.size_s() + 4; + cx += b.first.size_s() + 6; else - cy += 4; + cy += 2; } } @@ -275,3 +310,72 @@ bool TileButtons::keyEvent(PIKbdListener::KeyEvent key) { }; return PIScreenTile::keyEvent(key); } + + + + +TileCheck::TileCheck(const PIString & n): PIScreenTile(n) { + focus_flags = CanHasFocus | NextByTab | NextByArrowsAll; + toggled = false; +} + + +void TileCheck::sizeHint(int & w, int & h) const { + w = text.size_s() + 4; + h = 1; +} + + +void TileCheck::drawEvent(PIScreenDrawer * d) { + Color cb = has_focus ? Blue : Cyan; + Color ct = has_focus ? White : Black; + int ff = has_focus ? Bold : 0; + PIString cs("[ ]"); + if (toggled) cs[1] = '*'; + d->fillRect(x, y, x + width, y + 1, ' ', Default, cb); + d->drawText(x, y, cs, ct, Transparent, ff); + d->drawText(x + 4, y, text, ct, Transparent, ff); +} + + +bool TileCheck::keyEvent(PIKbdListener::KeyEvent key) { + if (key.key == PIKbdListener::Space || key.key == PIKbdListener::Return) { + toggled = !toggled; + raiseEvent(TileEvent(Toggled, toggled)); + return true; + } + return PIScreenTile::keyEvent(key); +} + + + + +TileProgress::TileProgress(const PIString & n): PIScreenTile(n) { + maximum = 100.; + value = 0.; + suffix = " %"; +} + + +void TileProgress::sizeHint(int & w, int & h) const { + w = 20; + h = 1; +} + + +void TileProgress::drawEvent(PIScreenDrawer * d) { + int v = maximum == 0. ? 0 : piClampd(piRoundd(value / maximum * 100.), 0, 100); + PIString s = prefix + PIString::fromNumber(piRoundd(value)) + suffix; + int w = piRoundd(v / 100. * width), sx = (width - s.size_s()) / 2; + d->fillRect(x, y, x + width, y + 1, ' ', Default, Cyan); + d->fillRect(x, y, x + w, y + 1, ' ', Default, Blue); + if (w < sx) + d->drawText(x + sx, y, s, Black, Transparent); + else if (w >= sx + s.size_s()) + d->drawText(x + sx, y, s, White, Transparent); + else { + int fw = w - sx; + d->drawText(x + sx, y, s.left(fw), White, Transparent); + d->drawText(x + sx + fw, y, s.cutLeft(fw), Black, Transparent); + } +} diff --git a/src/console/piscreentiles.h b/src/console/piscreentiles.h index 4108aa24..975cc8b6 100644 --- a/src/console/piscreentiles.h +++ b/src/console/piscreentiles.h @@ -67,6 +67,23 @@ protected: +class TileButton: public PIScreenTile { +public: + TileButton(const PIString & n = PIString()); + enum EventType { + ButtonClicked + }; + PIScreenTypes::CellFormat format; + PIString text; +protected: + void sizeHint(int & w, int & h) const; + void drawEvent(PIScreenDrawer * d); + bool keyEvent(PIKbdListener::KeyEvent key); +}; + + + + class TileButtons: public PIScreenTile { public: TileButtons(const PIString & n = PIString()); @@ -83,4 +100,38 @@ protected: }; + + +class TileCheck: public PIScreenTile { +public: + TileCheck(const PIString & n = PIString()); + enum EventType { + Toggled + }; + PIScreenTypes::CellFormat format; + PIString text; + bool toggled; +protected: + void sizeHint(int & w, int & h) const; + void drawEvent(PIScreenDrawer * d); + bool keyEvent(PIKbdListener::KeyEvent key); +}; + + + + +class TileProgress: public PIScreenTile { +public: + TileProgress(const PIString & n = PIString()); + PIScreenTypes::CellFormat format; + PIString prefix; + PIString suffix; + double maximum; + double value; +protected: + void sizeHint(int & w, int & h) const; + void drawEvent(PIScreenDrawer * d); +}; + + #endif // PISCREENTILES_H diff --git a/src/console/piscreentypes.h b/src/console/piscreentypes.h index e5542559..858b5c6a 100644 --- a/src/console/piscreentypes.h +++ b/src/console/piscreentypes.h @@ -74,7 +74,9 @@ namespace PIScreenTypes { enum FocusFlag { CanHasFocus /** Tile can has focus */ = 0x1, NextByTab /** Focus passed to next tile by tab key */ = 0x2, - NextByArrows /** Focus passed to next tile by arrow keys */ = 0x4 + NextByArrowsHorizontal /** Focus passed to next tile by arrow keys left or right */ = 0x4, + NextByArrowsVertical /** Focus passed to next tile by arrow keys up or down */ = 0x8, + NextByArrowsAll /** Focus passed to next tile by any arrow key */ = NextByArrowsHorizontal | NextByArrowsVertical }; typedef PIFlags CharFlags; diff --git a/src/core/picli.h b/src/core/picli.h index 305231fd..1872c907 100755 --- a/src/core/picli.h +++ b/src/core/picli.h @@ -27,7 +27,7 @@ class PIP_EXPORT PICLI: public PIObject { - PIOBJECT(PICLI) + PIOBJECT_SUBCLASS(PICLI, PIObject) public: //! Constructor diff --git a/src/core/piobject.cpp b/src/core/piobject.cpp index 1da11ac1..c93bad88 100755 --- a/src/core/piobject.cpp +++ b/src/core/piobject.cpp @@ -290,6 +290,7 @@ void PIObject::piDisconnect(PIObject * src, const PIString & sig) { void PIObject::piDisconnect(PIObject * src) { + src->deleted(); PIMutexLocker _ml(src->mutex_connect); PIVector cv = src->connectors.toVector(); piForeach (PIObject * o, cv) { diff --git a/src/core/piobject.h b/src/core/piobject.h index fbe52d86..a441dc96 100755 --- a/src/core/piobject.h +++ b/src/core/piobject.h @@ -412,6 +412,7 @@ class PIP_EXPORT PIObject { friend class PIObjectManager; friend void dumpApplication(); + typedef PIObject __PIObject__; public: //! Contructs PIObject with name "name" @@ -689,7 +690,19 @@ protected: virtual void propertyChanged(const PIString & name) {} - static const PIString __classNameS() {return PIString();} \ + static const PIString __classNameS() {return PIString("PIObject");} + + EVENT(deleted) + +//! \events +//! \{ + + /** \fn void deleted() + * \brief Raise before object delete + * \note This event raised from destructor, so use only emitter() value, + * don`t try to cast deleted object to some subclass! */ + +//! \} private: struct Connection { diff --git a/src/core/pistatemachine.h b/src/core/pistatemachine.h index 2eb7c707..6ab963f9 100755 --- a/src/core/pistatemachine.h +++ b/src/core/pistatemachine.h @@ -75,7 +75,7 @@ template class PIP_EXPORT PIStateMachine: public PIObject { - PIOBJECT(PIStateMachine) + PIOBJECT_SUBCLASS(PIStateMachine, PIObject) public: //! Constructs an empty state machine PIStateMachine(void * _parent = 0) {if (_parent == 0) parent_ = this; else parent_ = _parent; resetConditions();} diff --git a/src/io/pibasetransfer.h b/src/io/pibasetransfer.h index 6b233c76..da99c416 100644 --- a/src/io/pibasetransfer.h +++ b/src/io/pibasetransfer.h @@ -4,9 +4,9 @@ #include "picrc.h" #include "pitimer.h" -class PIBaseTransfer : public PIObject +class PIBaseTransfer: public PIObject { - PIOBJECT(PIBaseTransfer) + PIOBJECT_SUBCLASS(PIBaseTransfer, PIObject) public: PIBaseTransfer(); ~PIBaseTransfer(); diff --git a/src/io/piconnection.h b/src/io/piconnection.h index e6424399..cf995c36 100755 --- a/src/io/piconnection.h +++ b/src/io/piconnection.h @@ -30,7 +30,7 @@ class PIConfig; class PIP_EXPORT PIConnection: public PIObject { - PIOBJECT(PIConnection) + PIOBJECT_SUBCLASS(PIConnection, PIObject) public: //! Constructs an empty connection @@ -373,7 +373,7 @@ private: }; class Sender: public PITimer { - PIOBJECT(Sender) + PIOBJECT_SUBCLASS(Sender, PIObject) public: Sender(PIConnection * parent_ = 0): parent(parent_), int_(0.f) {needLockRun(true);} ~Sender() {stop();} diff --git a/src/io/pidatatransfer.h b/src/io/pidatatransfer.h index 1ec8cffd..2f8abd01 100644 --- a/src/io/pidatatransfer.h +++ b/src/io/pidatatransfer.h @@ -3,7 +3,7 @@ #include "pibasetransfer.h" -class PIDataTransfer : public PIBaseTransfer +class PIDataTransfer: public PIBaseTransfer { PIOBJECT_SUBCLASS(PIDataTransfer, PIBaseTransfer) public: @@ -11,12 +11,13 @@ public: ~PIDataTransfer() {;} bool send(const PIByteArray &ba); - const PIByteArray &data() {return data_;} + const PIByteArray & data() {return data_;} private: - virtual PIByteArray buildPacket(Part p); - virtual void receivePart(PIBaseTransfer::Part fi, PIByteArray ba, PIByteArray pheader); + virtual PIByteArray buildPacket(Part p); + virtual void receivePart(PIBaseTransfer::Part fi, PIByteArray ba, PIByteArray pheader); PIByteArray data_; + }; diff --git a/src/io/piethernet.cpp b/src/io/piethernet.cpp index b3349a87..37125d33 100755 --- a/src/io/piethernet.cpp +++ b/src/io/piethernet.cpp @@ -613,6 +613,14 @@ int PIEthernet::write(const void * data, int max_size) { return -1; } + +void PIEthernet::clientDeleted() { + clients_mutex.lock(); + clients_.removeOne((PIEthernet*)emitter()); + clients_mutex.unlock(); +} + + void PIEthernet::server_func(void * eth) { PIEthernet * ce = (PIEthernet * )eth; if (ce->listen_threaded) { @@ -628,14 +636,27 @@ void PIEthernet::server_func(void * eth) { socklen_t slen = sizeof(client_addr); int s = accept(ce->sock, (sockaddr * )&client_addr, &slen); if (s == -1) { + int lerr = ethErrorCore(); +#ifdef WINDOWS + if (lerr == WSAETIMEDOUT) { +#else + if (lerr == EAGAIN) { +#endif + piMSleep(10); + return; + } if (ce->debug()) piCout << "[PIEthernet] Can`t accept new connection, " << ethErrorString(); piMSleep(10); return; } PIString ip(inet_ntoa(client_addr.sin_addr)); ip += ":" + PIString::fromNumber(htons(client_addr.sin_port)); - ce->clients_ << new PIEthernet(s, ip); - ce->newConnection(ce->clients_.back()); + PIEthernet * e = new PIEthernet(s, ip); + ce->clients_mutex.lock(); + CONNECTU(e, deleted, ce, clientDeleted) + ce->clients_ << e; + ce->clients_mutex.unlock(); + ce->newConnection(e); //cout << "connected " << ip << endl; //char d[256]; //cout << " recv " << recv(s, d, 256, 0) << endl; diff --git a/src/io/piethernet.h b/src/io/piethernet.h index 0839f90b..694870bb 100755 --- a/src/io/piethernet.h +++ b/src/io/piethernet.h @@ -396,11 +396,13 @@ protected: bool connected_, connecting_, listen_threaded, server_bounded; mutable PIString ip_, ip_s, ip_c, ip_r; PIThread server_thread_; + PIMutex clients_mutex; PIVector clients_; PIQueue mcast_queue; PIStringList mcast_groups; private: + EVENT_HANDLER(void, clientDeleted); static void server_func(void * eth); void setType(Type t, bool reopen = true) {setProperty("type", (int)t); if (reopen && isOpened()) {closeDevice(); init(); openDevice();}} diff --git a/src/io/pipeer.h b/src/io/pipeer.h index 90c17a9e..9c87d48a 100755 --- a/src/io/pipeer.h +++ b/src/io/pipeer.h @@ -28,7 +28,7 @@ class PIP_EXPORT PIPeer: public PIObject { - PIOBJECT(PIPeer) + PIOBJECT_SUBCLASS(PIPeer, PIObject) private: struct PeerData { PeerData() {msg_count = msg_rec = 0;} diff --git a/src/io/piprotocol.h b/src/io/piprotocol.h index 82b7dd47..31966036 100755 --- a/src/io/piprotocol.h +++ b/src/io/piprotocol.h @@ -35,7 +35,7 @@ class PIProtocol; class PIP_EXPORT PIMultiProtocolBase: protected PIObject { - PIOBJECT(PIMultiProtocolBase) + PIOBJECT_SUBCLASS(PIMultiProtocolBase, PIObject) friend class PIProtocol; public: PIMultiProtocolBase() {;} @@ -72,7 +72,7 @@ typedef void (*ReceiveFunc)(void * ); /// void send(const void * data, int size, bool direct = false) class PIP_EXPORT PIProtocol: public PIObject { - PIOBJECT(PIProtocol) + PIOBJECT_SUBCLASS(PIProtocol, PIObject) friend class PIMultiProtocolBase; friend class PIMultiProtocol; enum Type {None, Serial, Ethernet}; diff --git a/src/thread/pithread.h b/src/thread/pithread.h index c0acf756..9a2cddc5 100755 --- a/src/thread/pithread.h +++ b/src/thread/pithread.h @@ -33,7 +33,7 @@ typedef void (*ThreadFunc)(void * ); class PIP_EXPORT PIThread: public PIObject { - PIOBJECT(PIThread) + PIOBJECT_SUBCLASS(PIThread, PIObject) public: //! Contructs thread with custom data "data", external function "func" and main loop delay "loop_delay". diff --git a/src/thread/pitimer.h b/src/thread/pitimer.h index 6a4512e6..a0a07dcb 100755 --- a/src/thread/pitimer.h +++ b/src/thread/pitimer.h @@ -130,7 +130,7 @@ private: class PITimer: public PIObject { - PIOBJECT(PITimer) + PIOBJECT_SUBCLASS(PITimer, PIObject) public: //! \brief Constructs timer with PITimer::Thread implementation