/* PIP - Platform Independent Primitives Console output/input Copyright (C) 2020 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 "piconsole.h" #include "piincludes_p.h" #include "pipeer.h" #include "pidiagnostics.h" #include "pisystemmonitor.h" #ifndef WINDOWS # include # include # include #else # include # define COMMON_LVB_UNDERSCORE 0x8000 #endif /** \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 * **/ PRIVATE_DEFINITION_START(PIConsole) #ifdef WINDOWS void getWinCurCoord() {GetConsoleScreenBufferInfo(hOut, &csbi); ccoord = csbi.dwCursorPosition;} COORD & getWinCoord(int dx = 0, int dy = 0) {getWinCurCoord(); ccoord.X += dx; ccoord.Y += dy; return ccoord;} void * hOut; CONSOLE_SCREEN_BUFFER_INFO sbi, csbi; CONSOLE_CURSOR_INFO curinfo; COORD ccoord, ulcoord; WORD dattr; DWORD smode, written; #endif PRIVATE_DEFINITION_END(PIConsole) PIConsole::PIConsole(bool startNow, PIKbdListener::KBFunc slot): PIThread() { setName("console"); setPriority(piLow); needLockRun(true); ret_func = slot; num_format = systime_format = 0; vid = 0; cur_tab = width = height = pwidth = pheight = max_y = 0; def_align = Nothing; tabs.reserve(16); #ifdef WINDOWS PRIVATE->ulcoord.X = 0; PRIVATE->hOut = GetStdHandle(STD_OUTPUT_HANDLE); GetConsoleScreenBufferInfo(PRIVATE->hOut, &PRIVATE->sbi); PRIVATE->dattr = PRIVATE->sbi.wAttributes; width = PRIVATE->sbi.srWindow.Right - PRIVATE->sbi.srWindow.Left; height = PRIVATE->sbi.srWindow.Bottom - PRIVATE->sbi.srWindow.Top; PRIVATE->ulcoord.Y = PRIVATE->sbi.srWindow.Top; GetConsoleMode(PRIVATE->hOut, &PRIVATE->smode); GetConsoleCursorInfo(PRIVATE->hOut, &PRIVATE->curinfo); #else # ifdef FREERTOS width = 80; height = 24; # else winsize ws; ioctl(0, TIOCGWINSZ, &ws); width = ws.ws_col; height = ws.ws_row; # endif #endif addTab("main"); listener = new PIKbdListener(key_event, this); peer_timer = new PITimer(); peer_timer->setName("__S__.PIConsole.peer_timer"); peer = 0; server_mode = pause_ = false; state = Disconnected; peer_timer->addDelimiter(20); peer_timer->setName("__S__PIConsole::peer_timer"); CONNECT2(void, void * , int, peer_timer, tickEvent, this, peerTimer); if (startNow) start(); } PIConsole::~PIConsole() { stopPeer(); if (isRunning()) stop(); clearTabs(false); delete listener; delete peer_timer; #ifdef WINDOWS SetConsoleMode(PRIVATE->hOut, PRIVATE->smode); SetConsoleTextAttribute(PRIVATE->hOut, PRIVATE->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); } void PIConsole::clearTab(uint index) { if (index >= tabs.size()) return; lock(); tabs[index].columns.clear(); if (cur_tab == index) { clearScreen(); fillLabels(); } if (cur_tab >= tabs.size()) cur_tab = tabs.size() - 1; unlock(); } void PIConsole::clearTab(const PIString & name) { uint index = tabs.size() + 1; for (uint i = 0; i < tabs.size(); ++i) { if (tabs[i].name == name) { index = i; break; } } clearTab(index); } void PIConsole::update() { lock(); fillLabels(); unlock(); } 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(PIKbdListener::KeyEvent key, void * t) { PIConsole * p = (PIConsole * )t; int ct = p->cur_tab; if (key.key == PIKbdListener::LeftArrow) { do { ct--; if (ct < 0) return; } while (p->tabs[ct].key == 0); p->setTab(ct); return; } if (key.key == 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.key) { p->setTab(i); return; } } if (p->ret_func != 0) p->ret_func(key, t); p->keyPressed(key, t); } int PIConsole::couts(const PIString & v) { return printf("%s", v.data()); } int PIConsole::couts(const char * v) { return printf("%s", v); } void PIConsole::clearVariables(bool clearScreen) { if (isRunning()) lock(); if (clearScreen && isRunning()) { toUpperLeft(); clearScreenLower(); } columns().clear(); if (isRunning()) unlock(); } void PIConsole::stop(bool clear) { PIThread::stop(true); if (clear) clearScreen(); moveTo(0, max_y + 4); showCursor(); couts(fstr(Normal)); #ifdef WINDOWS SetConsoleMode(PRIVATE->hOut, PRIVATE->smode); SetConsoleTextAttribute(PRIVATE->hOut, PRIVATE->dattr); #endif fflush(0); } PIString PIConsole::fstr(FormatFlags f) { num_format = systime_format = 0; 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; if (f[PIConsole::SystemTimeSplit]) systime_format = 0; if (f[PIConsole::SystemTimeSeconds]) systime_format = 1; #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(PRIVATE->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 } 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) { switch (num_format) {case (1): return printf("0x%.4hX", v); break; case (2): return printf("%o", v); break; case (4): return printf("%s", toBin(&v, 2)); break; default: return printf("%hd", v); break;} } inline int PIConsole::couts(const int v) { switch (num_format) {case (1): return printf("0x%.8X", v); break; case (2): return printf("%o", v); break; case (4): return printf("%s", toBin(&v, 4)); break; default: return printf("%d", v); break;} } inline int PIConsole::couts(const long v) { switch (num_format) {case (1): return printf("0x%.16lX", v); break; case (2): return printf("%lo", v); break; case (4): return printf("%s", toBin(&v, sizeof(v))); break; default: return printf("%ld", v); break;} } inline int PIConsole::couts(const llong v) { switch (num_format) {case (1): return printf("0x%.16llX", v); break; case (2): return printf("%llo", v); break; case (4): return printf("%s", toBin(&v, sizeof(v))); break; default: return printf("%lld", v); break;} } inline int PIConsole::couts(const uchar v) { switch (num_format) {case (1): return printf("0x%.2X", v); break; case (2): return printf("%o", v); break; case (4): return printf("%s", toBin(&v, 1)); break; default: return printf("%u", v); break;} } inline int PIConsole::couts(const ushort v) { switch (num_format) {case (1): return printf("0x%.4hX", v); break; case (2): return printf("%o", v); break; case (4): return printf("%s", toBin(&v, 2)); break; default: return printf("%hu", v); break;} } inline int PIConsole::couts(const uint v) { switch (num_format) {case (1): return printf("0x%.8X", v); break; case (2): return printf("%o", v); break; case (4): return printf("%s", toBin(&v, 4)); break; default: return printf("%u", v); break;} } inline int PIConsole::couts(const ulong v) { switch (num_format) {case (1): return printf("0x%.16lX", v); break; case (2): return printf("%lo", v); break; case (4): return printf("%s", toBin(&v, sizeof(v))); break; default: return printf("%lu", v); break;} } inline int PIConsole::couts(const ullong v) { switch (num_format) {case (1): return printf("0x%.16llX", v); break; case (2): return printf("%llo", v); break; case (4): return printf("%s", toBin(&v, sizeof(v))); break; default: return printf("%llu", v); break;} } inline int PIConsole::couts(const float v) { switch (num_format) {case (3): return printf("%e", v); break; default: return printf("%.5g", v); break;} } inline int PIConsole::couts(const double v) { switch (num_format) {case (3): return printf("%le", v); break; default: return printf("%.5lg", v); break;} } inline int PIConsole::couts(const PISystemTime & v) { switch (systime_format) {case (1): return printf("%.6lg", v.toSeconds()); break; default: return couts(v.seconds) + printf(" s, ") + couts(v.nanoseconds) + printf(" ns"); break;} } void PIConsole::toUpperLeft() { #ifdef WINDOWS SetConsoleCursorPosition(PRIVATE->hOut, PRIVATE->ulcoord); #else printf("\e[H"); #endif } void PIConsole::moveRight(int n) { #ifdef WINDOWS SetConsoleCursorPosition(PRIVATE->hOut, PRIVATE->getWinCoord(n)); #else if (n > 0) printf("\e[%dC", n); #endif } void PIConsole::moveLeft(int n) { #ifdef WINDOWS SetConsoleCursorPosition(PRIVATE->hOut, PRIVATE->getWinCoord(-n)); #else if (n > 0) printf("\e[%dD", n); #endif } void PIConsole::moveTo(int x, int y) { #ifdef WINDOWS PRIVATE->ccoord.X = x; PRIVATE->ccoord.Y = PRIVATE->ulcoord.Y + y; SetConsoleCursorPosition(PRIVATE->hOut, PRIVATE->ccoord); #else printf("\e[%d;%dH", y, x); #endif } void PIConsole::clearScreen() { #ifdef WINDOWS couts(fstr(Normal)); toUpperLeft(); FillConsoleOutputAttribute(PRIVATE->hOut, PRIVATE->dattr, width * (height + 1), PRIVATE->ulcoord, &PRIVATE->written); FillConsoleOutputCharacter(PRIVATE->hOut, ' ', width * (height + 1), PRIVATE->ulcoord, &PRIVATE->written); #else couts(fstr(Normal)); printf("\e[H\e[J"); #endif } void PIConsole::clearScreenLower() { #ifdef WINDOWS couts(fstr(Normal)); PRIVATE->getWinCurCoord(); FillConsoleOutputAttribute(PRIVATE->hOut, PRIVATE->dattr, width * height - width * PRIVATE->ccoord.Y + PRIVATE->ccoord.X, PRIVATE->ccoord, &PRIVATE->written); FillConsoleOutputCharacter(PRIVATE->hOut, ' ', width * height - width * PRIVATE->ccoord.Y + PRIVATE->ccoord.X, PRIVATE->ccoord, &PRIVATE->written); #else couts(fstr(Normal)); printf("\e[J"); #endif } void PIConsole::clearLine() { #ifdef WINDOWS PRIVATE->getWinCurCoord(); FillConsoleOutputAttribute(PRIVATE->hOut, PRIVATE->dattr, width - PRIVATE->ccoord.X, PRIVATE->ccoord, &PRIVATE->written); FillConsoleOutputCharacter(PRIVATE->hOut, ' ', width - PRIVATE->ccoord.X, PRIVATE->ccoord, &PRIVATE->written); #else printf("\e[K"); #endif } void PIConsole::newLine() { #ifdef WINDOWS PRIVATE->getWinCurCoord(); PRIVATE->ccoord.X = 0; PRIVATE->ccoord.Y++; SetConsoleCursorPosition(PRIVATE->hOut, PRIVATE->ccoord); #else printf("\eE"); #endif } void PIConsole::hideCursor() { #ifdef WINDOWS PRIVATE->curinfo.bVisible = false; SetConsoleCursorInfo(PRIVATE->hOut, &PRIVATE->curinfo); #else printf("\e[?25l"); #endif } void PIConsole::showCursor() { #ifdef WINDOWS PRIVATE->curinfo.bVisible = true; SetConsoleCursorInfo(PRIVATE->hOut, &PRIVATE->curinfo); #else printf("\e[?25h"); #endif } void PIConsole::begin() { #ifdef WINDOWS SetConsoleMode(PRIVATE->hOut, ENABLE_WRAP_AT_EOL_OUTPUT); #endif max_y = 0; PICout::__mutex__().lock(); clearScreen(); hideCursor(); fillLabels(); PICout::__mutex__().unlock(); } void PIConsole::run() { if (pause_) return; uint cx, clen = 0; int j; #ifdef WINDOWS GetConsoleScreenBufferInfo(PRIVATE->hOut, &PRIVATE->sbi); width = PRIVATE->sbi.srWindow.Right - PRIVATE->sbi.srWindow.Left; height = PRIVATE->sbi.srWindow.Bottom - PRIVATE->sbi.srWindow.Top; #else # ifdef FREERTOS width = 80; height = 24; # else winsize ws; ioctl(0, TIOCGWINSZ, &ws); width = ws.ws_col; height = ws.ws_row; # endif #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 20: clen = printValue(ptr != 0 ? *(const PISystemTime*)ptr: PISystemTime(), 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(PRIVATE->hOut, &PRIVATE->sbi); width = PRIVATE->sbi.srWindow.Right - PRIVATE->sbi.srWindow.Left; height = PRIVATE->sbi.srWindow.Bottom - PRIVATE->sbi.srWindow.Top; #else # ifdef FREERTOS width = 80; height = 24; # else winsize ws; ioctl(0, TIOCGWINSZ, &ws); width = ws.ws_col; height = ws.ws_row; # endif #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(); //couts(fstr(Normal)); //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, FormatFlags 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, FormatFlags 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, FormatFlags 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, FormatFlags 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, FormatFlags 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, FormatFlags 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, FormatFlags 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, FormatFlags 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, FormatFlags 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, FormatFlags 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, FormatFlags 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, FormatFlags 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, FormatFlags 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, FormatFlags 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, FormatFlags format) { ADD_VAR_BODY tv.type = 13; tv.size = sizeof(*ptr); tv.ptr = ptr; column(col).push_back(tv);} void PIConsole::addVariable(const PIString & name, const PISystemTime * ptr, int col, FormatFlags format) { ADD_VAR_BODY tv.type = 20; 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: * * " 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, FormatFlags 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, FormatFlags format) { addString("monitor " + name, col, format | PIConsole::Bold); //addVariable("PID", &(ptr->statistic().ID), col, format); //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 kernel", &(ptr->statistic().cpu_load_system), col, format); //addVariable("cpu load user", &(ptr->statistic().cpu_load_user), col, format); } void PIConsole::addBitVariable(const PIString & name, const void * ptr, int fromBit, int bitCount, int col, FormatFlags 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, FormatFlags 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, FormatFlags format) { couts(fstr(format)); int ret = couts(value); fstr(PIConsole::Dec); return ret; } inline int PIConsole::printValue(const char * value, FormatFlags format) {PRINT_VAR_BODY} inline int PIConsole::printValue(const bool value, FormatFlags format) {PRINT_VAR_BODY} inline int PIConsole::printValue(const int value, FormatFlags format) {PRINT_VAR_BODY} inline int PIConsole::printValue(const long value, FormatFlags format) {PRINT_VAR_BODY} inline int PIConsole::printValue(const llong value, FormatFlags format) {PRINT_VAR_BODY} inline int PIConsole::printValue(const float value, FormatFlags format) {PRINT_VAR_BODY} inline int PIConsole::printValue(const double value, FormatFlags format) {PRINT_VAR_BODY} inline int PIConsole::printValue(const char value, FormatFlags format) {PRINT_VAR_BODY} inline int PIConsole::printValue(const short value, FormatFlags format) {PRINT_VAR_BODY} inline int PIConsole::printValue(const uchar value, FormatFlags format) {PRINT_VAR_BODY} inline int PIConsole::printValue(const ushort value, FormatFlags format) {PRINT_VAR_BODY} inline int PIConsole::printValue(const uint value, FormatFlags format) {PRINT_VAR_BODY} inline int PIConsole::printValue(const ulong value, FormatFlags format) {PRINT_VAR_BODY} inline int PIConsole::printValue(const ullong value, FormatFlags format) {PRINT_VAR_BODY} inline int PIConsole::printValue(const PISystemTime & value, FormatFlags 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(); randomize(); peer = new PIPeer("_rcc_:" + PIDateTime::current().toString("hhmmssddMMyy_") + PIString::fromNumber(randomi())); 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_tm.reset(); ba << int(0xBB); //piCout << "send to" << server_name << "fetch request disc"; peer->send(p, ba); state = FetchingData; break; case FetchingData: if (peer_tm.elapsed_s() < 3.) return; peer_tm.reset(); ba << int(0xBB); //piCout << "send to" << server_name << "fetch request fd"; peer->send(p, ba); break; case Committing: peer_tm.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; } }