/* PIP - Platform Independent Primitives Console output/input Copyright (C) 2013 Ivan Pelipenko peri4ko@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "piconsole.h" #include "pipeer.h" /** \class PIConsole * \brief Console output class * \details * \section PIConsole_sec0 Synopsis * This class provides output to console with automatic alignment and update. * It supports tabs, keyboard listening, formats and colors. * * \section PIConsole_sec1 Layout * %PIConsole works with variable pointers. You should add your variables with * functions \a addVariable() which receives label name, pointer to variable * and optional column and format. Columns count is dynamically increased if * new column used. E.g. if you add variable to empty tab to column 3, columns * count will be increased to 3, but two firsts columns will be empty. Each column * filled from top to bottom, but you can add just string with function * \a addString() or add empty line with function \a addEmptyLine(). Layout scheme: * \image html piconsole_layout.png * * \section PIConsole_sec2 Keyboard usage * %PIConsole should to be single in application. %PIConsole aggregate PIKbdListener * which grab keyboard and automatic switch tabs by theirs bind keys. If there is no * tab binded to pressed key external function "slot" will be called * **/ extern PIMutex __PICout_mutex__; PIConsole::PIConsole(bool startNow, KBFunc slot): PIThread() { setPriority(piLow); needLockRun(true); ret_func = slot; num_format = 0; vid = 0; cur_tab = width = height = pwidth = pheight = max_y = 0; def_align = Nothing; #ifdef WINDOWS ulcoord.X = ulcoord.Y = 0; hOut = GetStdHandle(STD_OUTPUT_HANDLE); GetConsoleScreenBufferInfo(hOut, &sbi); dattr = sbi.wAttributes; width = sbi.srWindow.Right - sbi.srWindow.Left; height = sbi.srWindow.Bottom - sbi.srWindow.Top; GetConsoleMode(hOut, &smode); GetConsoleCursorInfo(hOut, &curinfo); #endif tabs.reserve(16); addTab("main"); listener = new PIKbdListener(key_event, this); peer = 0; server_mode = false; state = Disconnected; peer_timer.addDelimiter(20); CONNECT2(void, void * , int, &peer_timer, timeout, this, peerTimer); if (startNow) start(); } PIConsole::~PIConsole() { stopPeer(); if (isRunning()) stop(); clearTabs(false); delete listener; #ifdef WINDOWS SetConsoleMode(hOut, smode); SetConsoleTextAttribute(hOut, dattr); #endif } int PIConsole::addTab(const PIString & name, char bind_key) { if (isRunning()) lock(); tabs.push_back(Tab(name, bind_key)); cur_tab = tabs.size() - 1; if (isRunning()) unlock(); return tabs.size(); } void PIConsole::removeTab(uint index) { if (index >= tabs.size()) return; if (isRunning()) lock(); tabs.remove(index); if (cur_tab >= tabs.size()) cur_tab = tabs.size() - 1; if (isRunning()) unlock(); } void PIConsole::removeTab(const PIString & name) { uint index = tabs.size() + 1; for (uint i = 0; i < tabs.size(); ++i) { if (tabs[i].name == name) { index = i; break; } } removeTab(index); } bool PIConsole::setTab(uint index) { if (index >= tabs.size()) return false; if (!isRunning()) { cur_tab = index; return true; } lock(); __PICout_mutex__.lock(); cur_tab = index; clearScreen(); fillLabels(); __PICout_mutex__.unlock(); unlock(); return true; } bool PIConsole::setTab(const PIString & name) { uint index = tabs.size() + 1; for (uint i = 0; i < tabs.size(); ++i) { if (tabs[i].name == name) { index = i; break; } } return setTab(index); } bool PIConsole::setTabBindKey(uint index, char bind_key) { if (index >= tabs.size()) return false; tabs[index].key = bind_key; return true; } bool PIConsole::setTabBindKey(const PIString & name, char bind_key) { uint index =tabs.size() + 1; for (uint i = 0; i < tabs.size(); ++i) { if (tabs[i].name == name) { index = i; break; } } return setTabBindKey(index, bind_key); } void PIConsole::key_event(char key, void * t) { PIConsole * p = (PIConsole * )t; int ct = p->cur_tab; if (key == char(PIKbdListener::LeftArrow)) { do { ct--; if (ct < 0) return; } while (p->tabs[ct].key == 0); p->setTab(ct); return; } if (key == char(PIKbdListener::RightArrow)) { do { ct++; if (ct >= p->tabs.size_s()) return; } while (p->tabs[ct].key == 0); p->setTab(ct); return; } for (uint i = 0; i < p->tabsCount(); ++i) { if (p->tabs[i].key == key) { p->setTab(i); return; } } if (p->ret_func != 0) p->ret_func(key, t); p->keyPressed(key, t); } void PIConsole::stop(bool clear) { PIThread::stop(true); if (clear) clearScreen(); moveTo(0, max_y + 4); showCursor(); couts(fstr(Normal)); #ifdef WINDOWS SetConsoleMode(hOut, smode); SetConsoleTextAttribute(hOut, dattr); #endif fflush(0); } PIString PIConsole::fstr(PIFlags f) { if (f[PIConsole::Dec]) num_format = 0; if (f[PIConsole::Hex]) num_format = 1; if (f[PIConsole::Oct]) num_format = 2; if (f[PIConsole::Bin]) num_format = 4; if (f[PIConsole::Scientific]) num_format = 3; #ifdef WINDOWS WORD attr = 0; if (f[PIConsole::Inverse]) { if (f[PIConsole::Red]) attr |= BACKGROUND_RED; if (f[PIConsole::Green]) attr |= BACKGROUND_GREEN; if (f[PIConsole::Blue]) attr |= BACKGROUND_BLUE; if (f[PIConsole::Yellow]) attr |= (BACKGROUND_RED | BACKGROUND_GREEN); if (f[PIConsole::Magenta]) attr |= (BACKGROUND_RED | BACKGROUND_BLUE); if (f[PIConsole::Cyan]) attr |= (BACKGROUND_GREEN | BACKGROUND_BLUE); if (f[PIConsole::White]) attr |= (BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE); if (f[PIConsole::BackRed]) attr |= FOREGROUND_RED; if (f[PIConsole::BackGreen]) attr |= FOREGROUND_GREEN; if (f[PIConsole::BackBlue]) attr |= FOREGROUND_BLUE; if (f[PIConsole::BackYellow]) attr |= (FOREGROUND_RED | FOREGROUND_GREEN); if (f[PIConsole::BackMagenta]) attr |= (FOREGROUND_RED | FOREGROUND_BLUE); if (f[PIConsole::BackCyan]) attr |= (FOREGROUND_GREEN | FOREGROUND_BLUE); if (f[PIConsole::BackWhite]) attr |= (FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE); if ((attr & BACKGROUND_RED) + (attr & BACKGROUND_GREEN) + (attr & BACKGROUND_BLUE) == 0) attr |= FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE; } else { if (f[PIConsole::Red]) attr |= FOREGROUND_RED; if (f[PIConsole::Green]) attr |= FOREGROUND_GREEN; if (f[PIConsole::Blue]) attr |= FOREGROUND_BLUE; if (f[PIConsole::Yellow]) attr |= (FOREGROUND_RED | FOREGROUND_GREEN); if (f[PIConsole::Magenta]) attr |= (FOREGROUND_RED | FOREGROUND_BLUE); if (f[PIConsole::Cyan]) attr |= (FOREGROUND_GREEN | FOREGROUND_BLUE); if (f[PIConsole::White]) attr |= (FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE); if (f[PIConsole::BackRed]) attr |= BACKGROUND_RED; if (f[PIConsole::BackGreen]) attr |= BACKGROUND_GREEN; if (f[PIConsole::BackBlue]) attr |= BACKGROUND_BLUE; if (f[PIConsole::BackYellow]) attr |= (BACKGROUND_RED | BACKGROUND_GREEN); if (f[PIConsole::BackMagenta]) attr |= (BACKGROUND_RED | BACKGROUND_BLUE); if (f[PIConsole::BackCyan]) attr |= (BACKGROUND_GREEN | BACKGROUND_BLUE); if (f[PIConsole::BackWhite]) attr |= (BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE); if ((attr & FOREGROUND_RED) + (attr & FOREGROUND_GREEN) + (attr & FOREGROUND_BLUE) == 0) attr |= FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE; } if (f[PIConsole::Bold]) attr |= FOREGROUND_INTENSITY; if (f[PIConsole::Underline]) attr |= COMMON_LVB_UNDERSCORE; SetConsoleTextAttribute(hOut, attr); return PIString(); #else PIString ts("\e[0"); if (f[PIConsole::Bold]) ts += ";1"; if (f[PIConsole::Faint]) ts += ";2"; if (f[PIConsole::Italic]) ts += ";3"; if (f[PIConsole::Underline]) ts += ";4"; if (f[PIConsole::Blink]) ts += ";5"; if (f[PIConsole::Inverse]) ts += ";7"; if (f[PIConsole::Black]) ts += ";30"; if (f[PIConsole::Red]) ts += ";31"; if (f[PIConsole::Green]) ts += ";32"; if (f[PIConsole::Yellow]) ts += ";33"; if (f[PIConsole::Blue]) ts += ";34"; if (f[PIConsole::Magenta]) ts += ";35"; if (f[PIConsole::Cyan]) ts += ";36"; if (f[PIConsole::White]) ts += ";37"; if (f[PIConsole::BackBlack]) ts += ";40"; if (f[PIConsole::BackRed]) ts += ";41"; if (f[PIConsole::BackGreen]) ts += ";42"; if (f[PIConsole::BackYellow]) ts += ";43"; if (f[PIConsole::BackBlue]) ts += ";44"; if (f[PIConsole::BackMagenta]) ts += ";45"; if (f[PIConsole::BackCyan]) ts += ";46"; if (f[PIConsole::BackWhite]) ts += ";47"; return ts + "m"; #endif } #define siprint(x) switch (num_format) {case (1): return printf("0x%.4hX", x); break; case (2): return printf("%o", x); break; case (4): return printf("%s", toBin(&x, 2)); break; default: return printf("%hd", x); break;} #define iprint(x) switch (num_format) {case (1): return printf("0x%.8X", x); break; case (2): return printf("%o", x); break; case (4): return printf("%s", toBin(&x, 4)); break; default: return printf("%d", x); break;} #define liprint(x) switch (num_format) {case (1): return printf("0x%.16lX", x); break; case (2): return printf("%lo", x); break; case (4): return printf("%s", toBin(&x, sizeof(x))); break; default: return printf("%ld", x); break;} #define lliprint(x) switch (num_format) {case (1): return printf("0x%.16llX", x); break; case (2): return printf("%llo", x); break; case (4): return printf("%s", toBin(&x, sizeof(x))); break; default: return printf("%lld", x); break;} #define cuprint(x) switch (num_format) {case (1): return printf("0x%.2X", x); break; case (2): return printf("%o", x); break; case (4): return printf("%s", toBin(&x, 1)); break; default: return printf("%u", x); break;} #define suprint(x) switch (num_format) {case (1): return printf("0x%.4hX", x); break; case (2): return printf("%o", x); break; case (4): return printf("%s", toBin(&x, 2)); break; default: return printf("%hu", x); break;} #define uprint(x) switch (num_format) {case (1): return printf("0x%.8X", x); break; case (2): return printf("%o", x); break; case (4): return printf("%s", toBin(&x, 4)); break; default: return printf("%u", x); break;} #define luprint(x) switch (num_format) {case (1): return printf("0x%.16lX", x); break; case (2): return printf("%lo", x); break; case (4): return printf("%s", toBin(&x, sizeof(x))); break; default: return printf("%lu", x); break;} #define lluprint(x) switch (num_format) {case (1): return printf("0x%.16llX", x); break; case (2): return printf("%llo", x); break; case (4): return printf("%s", toBin(&x, sizeof(x))); break; default: return printf("%llu", x); break;} #define fprint(x) switch (num_format) {case (3): return printf("%e", x); break; default: return printf("%.5g", x); break;} #define dprint(x) switch (num_format) {case (3): return printf("%le", x); break; default: return printf("%.5lg", x); break;} inline int PIConsole::couts(const PIString & v) {return printf("%s", v.data());} inline int PIConsole::couts(const char * v) {return printf("%s", v);} inline int PIConsole::couts(const bool v) {return (v ? printf("true") : printf("false"));} inline int PIConsole::couts(const char v) {return printf("%c", v);} inline int PIConsole::couts(const short v) {siprint(v);} inline int PIConsole::couts(const int v) {iprint(v);} inline int PIConsole::couts(const long v) {liprint(v);} inline int PIConsole::couts(const llong v) {lliprint(v);} inline int PIConsole::couts(const uchar v) {cuprint(v);} inline int PIConsole::couts(const ushort v) {suprint(v);} inline int PIConsole::couts(const uint v) {uprint(v);} inline int PIConsole::couts(const ulong v) {luprint(v);} inline int PIConsole::couts(const ullong v) {lluprint(v);} inline int PIConsole::couts(const float v) {fprint(v);} inline int PIConsole::couts(const double v) {dprint(v);} void PIConsole::begin() { #ifdef WINDOWS SetConsoleMode(hOut, ENABLE_WRAP_AT_EOL_OUTPUT); #endif max_y = 0; __PICout_mutex__.lock(); clearScreen(); hideCursor(); fillLabels(); __PICout_mutex__.unlock(); } void PIConsole::run() { uint cx, clen = 0; int j; #ifdef WINDOWS GetConsoleScreenBufferInfo(hOut, &sbi); width = sbi.srWindow.Right - sbi.srWindow.Left; height = sbi.srWindow.Bottom - sbi.srWindow.Top; #else winsize ws; ioctl(0, TIOCGWINSZ, &ws); width = ws.ws_col; height = ws.ws_row; #endif //fflush(0); return; __PICout_mutex__.lock(); if (pwidth != width || pheight != height) { clearScreen(); fillLabels(); } pwidth = width; pheight = height; col_cnt = columns().size(); col_wid = (col_cnt > 0) ? width / col_cnt : width; for (uint i = 0; i < col_cnt; ++i) { PIVector & cvars(tabs[cur_tab].columns[i].variables); cx = col_wid * i; toUpperLeft(); if (max_y < cvars.size()) max_y = cvars.size(); j = 0; piForeachC (Variable & tv, cvars) { if (j > height - 3) continue; j++; moveRight(cx); if (tv.type == 15) { newLine(); continue; } moveRight(tv.offset); const void * ptr = 0; if (tv.remote) { if (tv.type == 0) { rstr.clear(); rba = tv.rdata; rba >> rstr; rstr.trim(); ptr = &rstr; } else ptr = tv.rdata.data(); } else ptr = tv.ptr; switch (tv.type) { case 0: clen = printValue(ptr != 0 ? *(const PIString*)ptr : PIString(), tv.format); break; case 1: clen = printValue(ptr != 0 ? *(const bool*)ptr : false, tv.format); break; case 2: clen = printValue(ptr != 0 ? *(const int*)ptr : 0, tv.format); break; case 3: clen = printValue(ptr != 0 ? *(const long*)ptr : 0l, tv.format); break; case 4: clen = printValue(ptr != 0 ? *(const char*)ptr : char(0), tv.format); break; case 5: clen = printValue(ptr != 0 ? *(const float*)ptr : 0.f, tv.format); break; case 6: clen = printValue(ptr != 0 ? *(const double*)ptr : 0., tv.format); break; case 7: clen = printValue(ptr != 0 ? *(const short*)ptr : short(0), tv.format); break; case 8: clen = printValue(ptr != 0 ? *(const uint*)ptr : 0u, tv.format); break; case 9: clen = printValue(ptr != 0 ? *(const ulong*)ptr : 0ul, tv.format); break; case 10: clen = printValue(ptr != 0 ? *(const ushort*)ptr : ushort(0), tv.format); break; case 11: clen = printValue(ptr != 0 ? *(const uchar*)ptr : uchar(0), tv.format); break; case 12: clen = printValue(ptr != 0 ? *(const llong*)ptr : 0l, tv.format); break; case 13: clen = printValue(ptr != 0 ? *(const ullong*)ptr: 0ull, tv.format); break; case 14: clen = printValue(bitsValue(ptr, tv.bitFrom, tv.bitCount), tv.format); break; } if (clen + tv.offset < (uint)col_wid) { PIString ts = PIString( #if defined(QNX) || defined(FREE_BSD) col_wid - clen - tv.offset - 1, ' '); #else col_wid - clen - tv.offset, ' '); #endif printf("%s", ts.data()); } newLine(); } } #ifdef WINDOWS moveTo(0, max_y + 1); #else moveTo(0, max_y + 2); #endif fflush(0); __PICout_mutex__.unlock(); } void PIConsole::fillLabels() { if (!isRunning()) return; uint cx, cy, mx = 0, dx; #ifdef WINDOWS GetConsoleScreenBufferInfo(hOut, &sbi); width = sbi.srWindow.Right - sbi.srWindow.Left; height = sbi.srWindow.Bottom - sbi.srWindow.Top; #else winsize ws; ioctl(0, TIOCGWINSZ, &ws); width = ws.ws_col; height = ws.ws_row; #endif max_y = 0; col_cnt = columns().size(); col_wid = (col_cnt > 0) ? width / col_cnt : width; for (uint i = 0; i < col_cnt; ++i) { Column & ccol(tabs[cur_tab].columns[i]); PIVector & cvars(ccol.variables); if (ccol.alignment != Nothing) { mx = 0; piForeachC (Variable & j, cvars) if (!j.isEmpty()) if (mx < j.name.size()) mx = j.name.size(); mx += 2; } cx = col_wid * i; cy = 1; toUpperLeft(); for (uint j = 0; j < cvars.size(); ++j) { if (int(j) > height - 3) continue; if (max_y < j) max_y = j; moveRight(cx); Variable & tv(cvars[j]); cvars[j].nx = cx; cvars[j].ny = cy; if (tv.name.isEmpty()) { cvars[j].offset = 0; clearLine(); newLine(); cy++; continue; } clearLine(); //piCout << tv.name << tv.type << tv.ptr; if (tv.type == 15) { cvars[j].offset = cvars[j].name.length(); cvars[j].nx += cvars[j].offset; printLine(tv.name, cx, tv.format); newLine(); cy++; continue; } if (!tv.isEmpty()) { switch (ccol.alignment) { case Nothing: cvars[j].offset = (tv.name + ": ").length(); cvars[j].nx += cvars[j].offset; printValue(tv.name + ": ", tv.format); break; case Left: cvars[j].offset = mx; cvars[j].nx += cvars[j].offset; printValue(tv.name + ": ", tv.format); break; case Right: cvars[j].offset = mx; cvars[j].nx += cvars[j].offset; dx = mx - (tv.name + ": ").length(); moveRight(dx); printValue(tv.name + ": ", tv.format); moveLeft(dx); break; } } newLine(); cy++; } } #ifdef WINDOWS moveTo(0, max_y + 1); #else moveTo(0, max_y + 2); #endif if (!tabs[cur_tab].status.isEmpty()) { printValue(tabs[cur_tab].status); newLine(); } status(); //fflush(0); } void PIConsole::status() { Tab * ctab; //clearLine(); for (uint i = 0; i < tabsCount(); ++i) { ctab = &tabs[i]; if (ctab->key == 0) continue; printValue(ctab->key, PIConsole::White | PIConsole::Bold); if (i == cur_tab) printValue(ctab->name + " ", PIConsole::BackYellow | PIConsole::Black); else printValue(ctab->name + " ", PIConsole::Cyan | PIConsole::Inverse); printValue(" "); } newLine(); } int PIConsole::bitsValue(const void * src, int offset, int count) const { int ret = 0, stbyte = offset / 8, cbit = offset - stbyte * 8; char cbyte = reinterpret_cast(src)[stbyte]; for (int i = 0; i < count; i++) { ret |= ((cbyte >> cbit & 1) << i); cbit++; if (cbit == 8) { cbit = 0; stbyte++; cbyte = reinterpret_cast(src)[stbyte]; } } return ret; } const char * PIConsole::toBin(const void * d, int s) { binstr.clear(); uchar cc, b; for (int i = 0; i < s; ++i) { cc = ((const uchar *)d)[i]; b = 1; for (int j = 0; j < 8; ++j) { binstr << (cc & b ? "1" : "0"); b <<= 1; } if (i < s - 1) binstr << " "; } binstr.reverse(); return binstr.data(); } #define ADD_VAR_BODY vid++; tv.id = vid; tv.name = name; tv.bitFrom = tv.bitCount = 0; tv.format = format; tv.remote = false; checkColumn(col); void PIConsole::addString(const PIString & name, int col, PIFlags format) { ADD_VAR_BODY tv.type = 15; tv.size = 0; tv.ptr = 0; column(col).push_back(tv);} void PIConsole::addVariable(const PIString & name, const PIString * ptr, int col, PIFlags format) { ADD_VAR_BODY tv.type = 0; tv.size = 0; tv.ptr = ptr; column(col).push_back(tv);} void PIConsole::addVariable(const PIString & name, const bool * ptr, int col, PIFlags format) { ADD_VAR_BODY tv.type = 1; tv.size = sizeof(*ptr); tv.ptr = ptr; column(col).push_back(tv);} void PIConsole::addVariable(const PIString & name, const int * ptr, int col, PIFlags format) { ADD_VAR_BODY tv.type = 2; tv.size = sizeof(*ptr); tv.ptr = ptr; column(col).push_back(tv);} void PIConsole::addVariable(const PIString & name, const long * ptr, int col, PIFlags format) { ADD_VAR_BODY tv.type = 3; tv.size = sizeof(*ptr); tv.ptr = ptr; column(col).push_back(tv);} void PIConsole::addVariable(const PIString & name, const char * ptr, int col, PIFlags format) { ADD_VAR_BODY tv.type = 4; tv.size = sizeof(*ptr); tv.ptr = ptr; column(col).push_back(tv);} void PIConsole::addVariable(const PIString & name, const float * ptr, int col, PIFlags format) { ADD_VAR_BODY tv.type = 5; tv.size = sizeof(*ptr); tv.ptr = ptr; column(col).push_back(tv);} void PIConsole::addVariable(const PIString & name, const double * ptr, int col, PIFlags format) { ADD_VAR_BODY tv.type = 6; tv.size = sizeof(*ptr); tv.ptr = ptr; column(col).push_back(tv);} void PIConsole::addVariable(const PIString & name, const short * ptr, int col, PIFlags format) { ADD_VAR_BODY tv.type = 7; tv.size = sizeof(*ptr); tv.ptr = ptr; column(col).push_back(tv);} void PIConsole::addVariable(const PIString & name, const uint * ptr, int col, PIFlags format) { ADD_VAR_BODY tv.type = 8; tv.size = sizeof(*ptr); tv.ptr = ptr; column(col).push_back(tv);} void PIConsole::addVariable(const PIString & name, const ulong * ptr, int col, PIFlags format) { ADD_VAR_BODY tv.type = 9; tv.size = sizeof(*ptr); tv.ptr = ptr; column(col).push_back(tv);} void PIConsole::addVariable(const PIString & name, const ushort * ptr, int col, PIFlags format) { ADD_VAR_BODY tv.type = 10; tv.size = sizeof(*ptr); tv.ptr = ptr; column(col).push_back(tv);} void PIConsole::addVariable(const PIString & name, const uchar * ptr, int col, PIFlags format) { ADD_VAR_BODY tv.type = 11; tv.size = sizeof(*ptr); tv.ptr = ptr; column(col).push_back(tv);} void PIConsole::addVariable(const PIString & name, const llong * ptr, int col, PIFlags format) { ADD_VAR_BODY tv.type = 12; tv.size = sizeof(*ptr); tv.ptr = ptr; column(col).push_back(tv);} void PIConsole::addVariable(const PIString & name, const ullong * ptr, int col, PIFlags format) { ADD_VAR_BODY tv.type = 13; tv.size = sizeof(*ptr); tv.ptr = ptr; column(col).push_back(tv);} /** \brief Add to current tab to column "column" variable with label "name", pointer "ptr" and format "format" * \details This function add to column "column" next lines: * * "protocol " * * "Rec - receiverDeviceName": \a PIProtocol::receiverDeviceState * * "Send - senderDeviceName": \a PIProtocol::senderDeviceState * * "Received count": \a PIProtocol::receiveCount * * "Invalid count": \a PIProtocol::wrongCount * * "Missed count": \a PIProtocol::missedCount * * "Sended count": \a PIProtocol::sendCount * * "Immediate Frequency, Hz": \a PIProtocol::immediateFrequency * * "Integral Frequency, Hz": \a PIProtocol::integralFrequency * * "Receive speed": \a PIProtocol::receiveSpeed * * "Send speed": \a PIProtocol::sendSpeed * * "Receiver history size": \a PIProtocol::receiverHistorySize * * "Sender history size": \a PIProtocol::senderHistorySize * * "Disconnect Timeout, s": \a PIProtocol::disconnectTimeout * * "Quality": \a PIProtocol::quality * */ void PIConsole::addVariable(const PIString & name, const PIProtocol * ptr, int col, PIFlags format) { addString("protocol " + name, col, format | PIConsole::Bold); addVariable("Rec - " + ptr->receiverDeviceName(), ptr->receiverDeviceState_ptr(), col, format); addVariable("Send - " + ptr->senderDeviceName(), ptr->senderDeviceState_ptr(), col, format); addVariable("Received count", ptr->receiveCount_ptr(), col, format); addVariable("Invalid count", ptr->wrongCount_ptr(), col, format); addVariable("Missed count", ptr->missedCount_ptr(), col, format); addVariable("Sended count", ptr->sendCount_ptr(), col, format); addVariable("Immediate Frequency, Hz", ptr->immediateFrequency_ptr(), col, format); addVariable("Integral Frequency, Hz", ptr->integralFrequency_ptr(), col, format); addVariable("Receive speed", ptr->receiveSpeed_ptr(), col, format); addVariable("Send speed", ptr->sendSpeed_ptr(), col, format); addVariable("Receiver history size", ptr->receiverHistorySize_ptr(), col, format); addVariable("Sender history size", ptr->senderHistorySize_ptr(), col, format); addVariable("Disconnect Timeout, s", ptr->disconnectTimeout_ptr(), col, format); addVariable("Quality", ptr->quality_ptr(), col, format); } /** \brief Add to current tab to column "column" variable with label "name", pointer "ptr" and format "format" * \details This function add to column "column" next lines: * * " diagnostics" * * "Received count": \a PIDiagnostics::receiveCount * * "Invalid count": \a PIDiagnostics::wrongCount * * "Sended count": \a PIDiagnostics::sendCount * * "Immediate Frequency, Hz": \a PIDiagnostics::immediateFrequency * * "Integral Frequency, Hz": \a PIDiagnostics::integralFrequency * * "Receive speed": \a PIDiagnostics::receiveSpeed * * "Send speed": \a PIDiagnostics::sendSpeed * * "Quality": \a PIDiagnostics::quality * */ void PIConsole::addVariable(const PIString & name, const PIDiagnostics * ptr, int col, PIFlags format) { addString(name + " diagnostics", col, format | PIConsole::Bold); addVariable("Received count", ptr->receiveCount_ptr(), col, format); addVariable("Invalid count", ptr->wrongCount_ptr(), col, format); addVariable("Sended count", ptr->sendCount_ptr(), col, format); addVariable("Immediate Frequency, Hz", ptr->immediateFrequency_ptr(), col, format); addVariable("Integral Frequency, Hz", ptr->integralFrequency_ptr(), col, format); addVariable("Receive speed", ptr->receiveSpeed_ptr(), col, format); addVariable("Send speed", ptr->sendSpeed_ptr(), col, format); addVariable("Quality", ptr->quality_ptr(), col, format); } void PIConsole::addVariable(const PIString & name, const PISystemMonitor * ptr, int col, PIFlags format) { addString("monitor " + name, col, format | PIConsole::Bold); addVariable("state", &(ptr->statistic().state), col, format); addVariable("threads", &(ptr->statistic().threads), col, format); addVariable("priority", &(ptr->statistic().priority), col, format); addVariable("memory physical", &(ptr->statistic().physical_memsize_readable), col, format); addVariable("memory shared", &(ptr->statistic().share_memsize_readable), col, format); addVariable("cpu load", &(ptr->statistic().cpu_load_system), col, format); } void PIConsole::addBitVariable(const PIString & name, const void * ptr, int fromBit, int bitCount, int col, PIFlags format) { vid++; tv.id = vid; tv.size = sizeof(ullong); tv.name = name; tv.bitFrom = fromBit; tv.bitCount = bitCount; tv.type = 14; tv.ptr = ptr; tv.format = format; checkColumn(col); column(col).push_back(tv);} void PIConsole::addEmptyLine(int col, uint count) { tv.id = 0; tv.size = 0; tv.name = ""; tv.type = 0; tv.ptr = 0; tv.format = Normal; for (uint i = 0; i < count; ++i) { checkColumn(col); column(col).push_back(tv); } } PIString PIConsole::getString(int x, int y) { bool run = isRunning(); if (run) PIThread::stop(true); listener->setActive(false); msleep(50); #ifdef WINDOWS moveTo(x - 1, y - 1); #else moveTo(x, y); #endif showCursor(); PIByteArray ba(4096); #ifdef CC_VC int ret = scanf_s(" %s", ba.data()); #else int ret = scanf(" %s", ba.data()); #endif listener->setActive(true); if (run) start(); if (ret >= 1) return PIString(ba); else return PIString(); } PIString PIConsole::getString(const PIString & name) { piForeachC (Column & i, tabs[cur_tab].columns) piForeachC (Variable & j, i.variables) if (j.name == name) return getString(j.nx + 1, j.ny); return PIString(); } #define PRINT_VAR_BODY couts(fstr(format)); int ret = couts(value); couts(fstr(PIConsole::Dec)); return ret; inline void PIConsole::printLine(const PIString & value, int dx, PIFlags format) { int i = width - value.length() - dx; #if defined(QNX) || defined(FREE_BSD) --i; #endif PIString ts = fstr(format); couts(ts); if (i >= 0) ts = value + PIString(i, ' '); else ts = value.left(value.size() + i); couts(ts); couts(fstr(Dec)); } inline int PIConsole::printValue(const PIString & value, PIFlags format) { couts(fstr(format)); int ret = couts(value); fstr(PIConsole::Dec); return ret; } inline int PIConsole::printValue(const char * value, PIFlags format) {PRINT_VAR_BODY} inline int PIConsole::printValue(const bool value, PIFlags format) {PRINT_VAR_BODY} inline int PIConsole::printValue(const int value, PIFlags format) {PRINT_VAR_BODY} inline int PIConsole::printValue(const long value, PIFlags format) {PRINT_VAR_BODY} inline int PIConsole::printValue(const llong value, PIFlags format) {PRINT_VAR_BODY} inline int PIConsole::printValue(const float value, PIFlags format) {PRINT_VAR_BODY} inline int PIConsole::printValue(const double value, PIFlags format) {PRINT_VAR_BODY} inline int PIConsole::printValue(const char value, PIFlags format) {PRINT_VAR_BODY} inline int PIConsole::printValue(const short value, PIFlags format) {PRINT_VAR_BODY} inline int PIConsole::printValue(const uchar value, PIFlags format) {PRINT_VAR_BODY} inline int PIConsole::printValue(const ushort value, PIFlags format) {PRINT_VAR_BODY} inline int PIConsole::printValue(const uint value, PIFlags format) {PRINT_VAR_BODY} inline int PIConsole::printValue(const ulong value, PIFlags format) {PRINT_VAR_BODY} inline int PIConsole::printValue(const ullong value, PIFlags format) {PRINT_VAR_BODY} void PIConsole::startServer(const PIString & name) { stopPeer(); server_mode = true; peer = new PIPeer("_rcs_:" + name); CONNECT2(void, const PIString & , const PIByteArray &, peer, dataReceivedEvent, this, peerReceived); CONNECT1(void, const PIString & , peer, peerDisconnectedEvent, this, peerDisconnectedEvent); peer_timer.start(50.); serverSendInfo(); } void PIConsole::stopPeer() { remote_clients.clear(); peer_timer.stop(); if (peer != 0) delete peer; peer = 0; state = Disconnected; } PIStringList PIConsole::clients() const { PIStringList sl; if (peer == 0) return sl; piForeachC (PIPeer::PeerInfo & i, peer->allPeers()) { if (i.name.left(6) != "_rcc_:") continue; sl << i.name.right(i.name.length() - 6); } return sl; } void PIConsole::listenServers() { stopPeer(); server_mode = false; server_name.clear(); srand(currentSystemTime().nanoseconds); peer = new PIPeer("_rcc_:" + currentDateTime().toString("hhmmssddMMyy_") + PIString::fromNumber(rand())); CONNECT2(void, const PIString & , const PIByteArray &, peer, dataReceivedEvent, this, peerReceived); peer_timer.start(100.); } PIStringList PIConsole::availableServers() const { PIStringList sl; if (peer == 0) return sl; piForeachC (PIPeer::PeerInfo & i, peer->allPeers()) { if (i.name.left(6) != "_rcs_:") continue; sl << i.name.right(i.name.length() - 6); } return sl; } void PIConsole::connectToServer(const PIString & name) { if (peer == 0) listenServers(); server_name = name; } void PIConsole::disconnect() { stopPeer(); } void PIConsole::serverSendInfo() { if (peer == 0) return; PIByteArray ba; ba << int(0xAA); peer->sendToAll(ba); } void PIConsole::serverSendData() { if (peer == 0) return; PIByteArray ba; PIVector content; piForeach (Tab & t, tabs) piForeach (Column & c, t.columns) piForeach (Variable & v, c.variables) if (!v.isEmpty() && v.id > 0) { VariableContent vc; vc.id = v.id; v.writeData(vc.rdata); content << vc; } piForeach (RemoteClient & rc, remote_clients) { ba.clear(); switch (rc.state) { case FetchingData: ba << int(0xCC) << tabs; //piCout << "server send const data" << rc.name << ba.size_s(); break; case Committing: ba << int(0xDD); break; case Connected: ba << int(0xEE) << content; //piCout << "send data" << ba.size(); break; default: break; } if (!ba.isEmpty()) peer->send(rc.name, ba); } } PIConsole::RemoteClient & PIConsole::remoteClient(const PIString & fname) { piForeach (RemoteClient & i, remote_clients) if (i.name == fname) return i; remote_clients << RemoteClient(fname); return remote_clients.back(); } void PIConsole::peerReceived(const PIString & from, const PIByteArray & data) { int type; PIByteArray ba(data); ba >> type; //piCout << "rec packet from" << from << "type" << PICoutManipulators::Hex << type; if (server_mode) { if (from.left(5) != "_rcc_") return; //PIString rcn = from.right(from.length() - 6); RemoteClient & rc(remoteClient(from)); switch (type) { case 0xBB: // fetch const data request //piCout << "fetch data request from" << from << rc.state; if (rc.state != Connected) rc.state = FetchingData; break; case 0xCC: // const data commit //piCout << "commit from" << from; if (rc.state != Connected) rc.state = Connected; break; default: break; } } else { PIVector content; PIMap vids; if (from.left(5) != "_rcs_") return; PIString rcn = from.right(from.length() - 6); switch (type) { case 0xAA: // new server //piCout << "new server" << rcn; break; case 0xCC: // const data //piCout << "received const data"; state = Committing; ba >> tabs; cur_tab = tabs.isEmpty() ? -1 : 0; piForeach (Tab & t, tabs) piForeach (Column & c, t.columns) piForeach (Variable & v, c.variables) v.remote = true; break; case 0xDD: // const data commit //piCout << "received commit"; state = Connected; break; case 0xEE: // dynamic data //piCout << "received data" << ba.size_s(); piForeach (Tab & t, tabs) piForeach (Column & c, t.columns) piForeach (Variable & v, c.variables) if (!v.isEmpty() && v.id > 0) vids[v.id] = &v; ba >> content; piForeach (VariableContent & vc, content) { if (vc.id <= 0) continue; Variable * v = vids.at(vc.id); if (v == 0) continue; //piCout << "read" << v->name << vc.rdata.size_s(); v->rdata = vc.rdata; } break; default: break; } } } void PIConsole::peerTimer(void * data, int delim) { if (peer == 0) return; //piCout << "timer" << delim; if (server_mode) { if (delim == 20) serverSendInfo(); else serverSendData(); } else { if (delim != 1 || server_name.isEmpty()) return; const PIPeer::PeerInfo * p = peer->getPeerByName("_rcs_:" + server_name); if (p == 0) return; PIByteArray ba; switch (state) { case Disconnected: peer_timer.reset(); ba << int(0xBB); //piCout << "send to" << server_name << "fetch request disc"; peer->send(p, ba); state = FetchingData; break; case FetchingData: if (peer_timer.elapsed_s() < 3.) return; peer_timer.reset(); ba << int(0xBB); //piCout << "send to" << server_name << "fetch request fd"; peer->send(p, ba); break; case Committing: peer_timer.reset(); ba << int(0xCC); //piCout << "send to" << server_name << "committing"; state = Connected; peer->send(p, ba); break; default: break; }; } } void PIConsole::peerDisconnectedEvent(const PIString & name) { for (int i = 0; i < remote_clients.size_s(); ++i) if (remote_clients[i].name == name) { remote_clients.remove(i); --i; } }