fix files
git-svn-id: svn://db.shs.com.ru/pip@11 12ceb7fc-bf1f-11e4-8940-5bc7170c53b5
This commit is contained in:
153
main_udp_filetransfer.cpp
Normal file
153
main_udp_filetransfer.cpp
Normal file
@@ -0,0 +1,153 @@
|
||||
#include "pip.h"
|
||||
#include "pifiletransfer.h"
|
||||
#include "pidatatransfer.h"
|
||||
|
||||
class Ob: public PIObject {
|
||||
PIOBJECT(Ob)
|
||||
public:
|
||||
Ob() {
|
||||
srand(PISystemTime::current().toMicroseconds());
|
||||
bytes_from = bytes_to = 0;
|
||||
|
||||
sft.setName("sft");
|
||||
//sft.setDirectory(sft.directory().cd("..\\"));
|
||||
//piCout << "Send File Transfer DIrectory" << sft.directory().absolutePath();
|
||||
CONNECTU(&sft, sendRequest, this, ssend);
|
||||
|
||||
rft.setName("rft");
|
||||
//rft.setDirectory(rft.directory().cd("..\\1"));
|
||||
//piCout << "Receive File Transfer DIrectory" << rft.directory().absolutePath();
|
||||
CONNECTU(&rft, sendRequest, this, rsend);
|
||||
}
|
||||
|
||||
void startsend() {
|
||||
// PIDir dir = PIDir::current();
|
||||
// dir.cd("..\\");
|
||||
// piCout << dir.absolutePath();
|
||||
// PIVector<PIFile::FileInfo> des = dir.allEntries();
|
||||
// piCout << "all entries" << des.size();
|
||||
// PIFile::FileInfo sde;
|
||||
// piForeachC(PIFile::FileInfo de, des) {
|
||||
// piCout << (de.isDir() ? "dir:" : "file") << de.name << de.size;
|
||||
// if (de.name() == "0") sde = de;
|
||||
// }
|
||||
// sft.setPacketSize(64096);
|
||||
// sft.send(sde);
|
||||
PIByteArray ba;
|
||||
ba.resize(65536);
|
||||
for (int i=0; i<ba.size_s(); i++)
|
||||
ba[i] = rand() % 254;
|
||||
//sft.setPacketSize(128);
|
||||
if (!sft.send(ba)) {piCoutObj << "SEND FAILED"; return;}
|
||||
PIByteArray ba2 = rft.data();
|
||||
// ba[2]++;
|
||||
if (ba != ba2) {piCoutObj << "DATA CORRUPTED"; return;}
|
||||
piCoutObj << "SUCCESS";
|
||||
}
|
||||
|
||||
private:
|
||||
EVENT_HANDLER1(void, ssend, PIByteArray &, data) {
|
||||
bytes_to += data.size();
|
||||
piCout << "[sender]" << sft.stateString() << ". datasize =" << data.size()
|
||||
<< "(" << PIString::readableSize(sft.bytesCur()) << "/" << PIString::readableSize(sft.bytesAll()) << ")"
|
||||
<< "(" << PIString::readableSize(bytes_to) << "/" << PIString::readableSize(bytes_from) << ")";
|
||||
// if(rand()%100 > 80) {data.remove(data.size()/2, 1);}
|
||||
// if(rand()%100 > 70) {data[data.size()-6] = 1;}
|
||||
// if(rand()%100 < 90)
|
||||
rft.received(data);
|
||||
}
|
||||
|
||||
EVENT_HANDLER1(void, rsend, PIByteArray &, data) {
|
||||
bytes_from += data.size();
|
||||
piCout << "[receiver]" << rft.stateString() << ". datasize =" << data.size()
|
||||
<< "(" << PIString::readableSize(rft.bytesCur()) << "/" << PIString::readableSize(rft.bytesAll()) << ")"
|
||||
<< "(" << PIString::readableSize(bytes_to) << "/" << PIString::readableSize(bytes_from) << ")";
|
||||
// if(rand()%100 > 80) {data.remove(data.size()/2, 1);}
|
||||
// if(rand()%100 > 70) {data[data.size()-6] = 1;}
|
||||
// if(rand()%100 < 90)
|
||||
sft.received(data);
|
||||
}
|
||||
|
||||
PIDataTransfer sft;
|
||||
PIDataTransfer rft;
|
||||
ullong bytes_to, bytes_from;
|
||||
};
|
||||
|
||||
|
||||
class UDPFileTransfer: public PITimer {
|
||||
PIOBJECT_SUBCLASS(UDPFileTransfer, PITimer)
|
||||
public:
|
||||
UDPFileTransfer(const PIString &src_ip_port, const PIString &dst_ip_port) {
|
||||
eth.setReadAddress(src_ip_port);
|
||||
eth.setSendAddress(dst_ip_port);
|
||||
//ft.setPacketSize(65000);
|
||||
CONNECTU(&ft, sendRequest, this, ftsend);
|
||||
CONNECTU(&ft, sendFilesStarted, this, ftevent);
|
||||
CONNECTU(&ft, receiveFilesStarted, this, ftevent);
|
||||
CONNECTU(&ft, sendFilesFinished, this, ftevent);
|
||||
CONNECTU(&ft, receiveFilesFinished, this, ftevent);
|
||||
CONNECTU(ð, threadedReadEvent, this, received);
|
||||
start(50);
|
||||
eth.open();
|
||||
eth.startThreadedRead();
|
||||
}
|
||||
|
||||
void startSend(const PIString &file) {
|
||||
ft.send(file);
|
||||
}
|
||||
|
||||
PIFileTransfer ft;
|
||||
|
||||
private:
|
||||
PIEthernet eth;
|
||||
|
||||
void tick(void *, int) {
|
||||
if (ft.isSending() || ft.isReceiving()) ftevent();
|
||||
}
|
||||
|
||||
EVENT_HANDLER(void, ftevent) {
|
||||
piCout << ft.stateString()
|
||||
<< "(" << PIString::readableSize(ft.bytesFileCur()) << "/" << PIString::readableSize(ft.bytesFileAll()) << ", "
|
||||
<< PIString::readableSize(ft.bytesCur()) << "/" << PIString::readableSize(ft.bytesAll()) << ")";
|
||||
}
|
||||
|
||||
EVENT_HANDLER1(void, ftsend, PIByteArray &, data) {
|
||||
eth.send(data);
|
||||
}
|
||||
|
||||
EVENT_HANDLER2(void, received, uchar * , readed, int, size) {
|
||||
PIByteArray ba(readed, size);
|
||||
ft.received(ba);
|
||||
}
|
||||
};
|
||||
|
||||
int main (int argc, char * argv[]) {
|
||||
// Ob o;
|
||||
// PITimeMeasurer tm;
|
||||
// o.startsend();
|
||||
// piCout << tm.elapsed_s();
|
||||
// return 0;
|
||||
if (!(argc == 3 || argc == 4)) {
|
||||
piCout << "UDPFileTransfer";
|
||||
piCout << "USE: piptest [src_ip_port] [dst_ip_port] {filename}";
|
||||
return 0;
|
||||
}
|
||||
PIKbdListener kbd;
|
||||
kbd.enableExitCapture();
|
||||
PIString src = argv[1];
|
||||
PIString dst = argv[2];
|
||||
UDPFileTransfer f(src, dst);
|
||||
piCout << "work directory" << f.ft.directory().absolutePath() << ", listen on" << src << ",send to" << dst;
|
||||
if (argc == 4) {
|
||||
PIString file = argv[3];
|
||||
piCout << "send file" << file;
|
||||
f.startSend(file);
|
||||
return 0;
|
||||
} else {
|
||||
piCout << "wait for receiving";
|
||||
}
|
||||
WAIT_FOR_EXIT
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
361
src/io/pibasetransfer.cpp
Normal file
361
src/io/pibasetransfer.cpp
Normal file
@@ -0,0 +1,361 @@
|
||||
#include "pibasetransfer.h"
|
||||
|
||||
const uint PIBaseTransfer::signature = 0x54444950;
|
||||
|
||||
PIBaseTransfer::PIBaseTransfer(): crc(standardCRC_16()) {
|
||||
header.sig = signature;
|
||||
header.session_id = 0;
|
||||
packet_header_size = sizeof(PacketHeader) + customHeader().size();
|
||||
part_header_size = sizeof(Part) + sizeof(int);
|
||||
is_sending = is_receiving = false;
|
||||
break_ = true;
|
||||
bytes_all = bytes_cur = 0;
|
||||
timeout_ = 1.;
|
||||
setPacketSize(4096);
|
||||
srand(PISystemTime::current().toMilliseconds());
|
||||
}
|
||||
|
||||
|
||||
PIBaseTransfer::~PIBaseTransfer() {
|
||||
break_ = true;
|
||||
session.clear();
|
||||
replies.clear();
|
||||
}
|
||||
|
||||
|
||||
void PIBaseTransfer::stopSend() {
|
||||
if (!is_sending) return;
|
||||
break_ = true;
|
||||
}
|
||||
|
||||
|
||||
void PIBaseTransfer::stopReceive() {
|
||||
if (!is_receiving) return;
|
||||
break_ = true;
|
||||
finish_receive(false);
|
||||
}
|
||||
|
||||
|
||||
void PIBaseTransfer::received(PIByteArray& data) {
|
||||
packet_header_size = sizeof(PacketHeader) + customHeader().size();
|
||||
// piCoutObj << "r" << data.size() << sizeof(PacketHeader);
|
||||
if (data.size() < sizeof(PacketHeader)) return;
|
||||
PacketHeader h;
|
||||
data >> h;
|
||||
PacketType pt = (PacketType)h.type;
|
||||
// piCoutObj << "receive" << h.session_id << h.type << h.id;
|
||||
switch (pt) {
|
||||
case pt_Unknown:
|
||||
break;
|
||||
case pt_Data:
|
||||
if (h.session_id != header.session_id || !is_receiving) {
|
||||
sendBreak(h.session_id);
|
||||
return;
|
||||
} else {
|
||||
uint rcrc = h.crc;
|
||||
uint ccrc = crc.calculate(data.data(), data.size_s());
|
||||
if (rcrc != ccrc) {
|
||||
header.id = h.id;
|
||||
sendReply(pt_ReplyInvalid);
|
||||
piCoutObj << "invalid CRC";
|
||||
} else {
|
||||
processData(h.id, data);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case pt_ReplySuccess:
|
||||
case pt_ReplyInvalid:
|
||||
if (h.session_id != header.session_id) return;
|
||||
if (is_sending) {
|
||||
if (h.id >= 0 && h.id < replies.size())
|
||||
replies[h.id] = pt;
|
||||
}
|
||||
if (is_receiving && h.id == 0) {
|
||||
if (checkSession() == 0 && pt == pt_ReplySuccess) finish_receive(true);
|
||||
}
|
||||
break;
|
||||
case pt_Break:
|
||||
break_ = true;
|
||||
// piCoutObj << "BREAK";
|
||||
if (is_receiving) {
|
||||
stopReceive();
|
||||
return;
|
||||
}
|
||||
if (is_sending) {
|
||||
stopSend();
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case pt_Start:
|
||||
if (is_sending) {
|
||||
sendBreak(h.session_id);
|
||||
return;
|
||||
}
|
||||
if (header.session_id != h.session_id && is_receiving) {
|
||||
sendBreak(h.session_id);
|
||||
return;
|
||||
}
|
||||
if (data.size() == sizeof(StartRequest)) {
|
||||
StartRequest sr;
|
||||
data >> sr;
|
||||
bytes_cur = 0;
|
||||
state_string = "start request";
|
||||
bytes_all = sr.size;
|
||||
header.session_id = h.session_id;
|
||||
header.id = 0;
|
||||
session.clear();
|
||||
replies.clear();
|
||||
session.resize(sr.packets);
|
||||
replies.resize(sr.packets + 1);
|
||||
replies.fill(pt_Unknown);
|
||||
is_receiving = true;
|
||||
break_ = false;
|
||||
receiveStarted();
|
||||
state_string = "receiving";
|
||||
replies[0] = pt_ReplySuccess;
|
||||
sendReply(pt_ReplySuccess);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool PIBaseTransfer::send_process() {
|
||||
packet_header_size = sizeof(PacketHeader) + customHeader().size();
|
||||
break_ = false;
|
||||
is_sending = true;
|
||||
sendStarted();
|
||||
replies.resize(session.size() + 1);
|
||||
replies.fill(pt_Unknown);
|
||||
PIByteArray ba;
|
||||
if (!getStartRequest()) return finish_send(false);
|
||||
state_string = "sending";
|
||||
for (int i = 0; i < session.size_s(); i++) {
|
||||
ba = build_packet(i);
|
||||
sendRequest(ba);
|
||||
if (break_) return finish_send(false);
|
||||
}
|
||||
PITimeMeasurer tm;
|
||||
int prev_chk = 0;
|
||||
while (tm.elapsed_s() < timeout_) {
|
||||
int chk = checkSession();
|
||||
if (chk != prev_chk) tm.reset();
|
||||
if (chk == 0) return finish_send(true);
|
||||
if (chk > 0) {
|
||||
// piCoutObj << "recovery packet" << chk;
|
||||
piMSleep(1);
|
||||
ba = build_packet(chk - 1);
|
||||
sendRequest(ba);
|
||||
}
|
||||
// if (chk == -1) return finish_send(false);
|
||||
if (break_) return finish_send(false);
|
||||
prev_chk = chk;
|
||||
piMSleep(1);
|
||||
}
|
||||
return finish_send(false);
|
||||
}
|
||||
|
||||
|
||||
int PIBaseTransfer::checkSession() {
|
||||
int miss = 0;
|
||||
for (int i = 1; i < replies.size_s(); i++) {
|
||||
if (replies[i] != pt_ReplySuccess) miss++;
|
||||
if (replies[i] == pt_ReplyInvalid) return i;
|
||||
}
|
||||
for (int i = 1; i < replies.size_s(); i++) {
|
||||
if (replies[i] != pt_ReplySuccess) return i;
|
||||
}
|
||||
if (miss > 0) {
|
||||
piCoutObj << "missing" << miss << "packets";
|
||||
return -miss;
|
||||
} else return 0;
|
||||
}
|
||||
|
||||
|
||||
void PIBaseTransfer::buildSession(PIVector<Part> parts) {
|
||||
state_string = "calculating files... ";
|
||||
session.clear();
|
||||
header.session_id = rand();
|
||||
bytes_all = 0;
|
||||
Part fi;
|
||||
int fi_index, fi_prts;
|
||||
PIVector<Part> lfi;
|
||||
int min_size = packet_header_size + part_header_size;
|
||||
int cur_size = min_size;
|
||||
for (int i = 0; i < parts.size_s(); i++) {
|
||||
state_string = "calculating files... " + PIString::fromNumber(i) + " of " + PIString::fromNumber(parts.size());
|
||||
fi.id = parts[i].id;
|
||||
// piCout << fi.id << state_string;
|
||||
bytes_all += parts[i].size;
|
||||
// fi.size = fi.entry.size;
|
||||
fi.start = 0;
|
||||
int rest = parts[i].size - (packet_size - cur_size);
|
||||
// piCout << i << fi.size << rest << bytes_all;
|
||||
if (rest <= 0) {
|
||||
fi.size = parts[i].size;
|
||||
lfi << fi;
|
||||
cur_size += fi.size + part_header_size;
|
||||
} else {
|
||||
fi.size = parts[i].size - rest;
|
||||
fi_index = 1;
|
||||
fi_prts = 1 + 1 + piMaxi(1, rest / (packet_size - min_size));
|
||||
// piCout << fi_prts;
|
||||
lfi << fi;
|
||||
session << lfi;
|
||||
lfi.clear();
|
||||
cur_size = min_size;
|
||||
llong fs = fi.size;
|
||||
for (int j = 1; j < fi_prts; j++) {
|
||||
fi_index++;
|
||||
fi.start = fs;
|
||||
fi.size = piMin<ullong>(parts[i].size - fs, packet_size - min_size);
|
||||
lfi << fi;
|
||||
cur_size += fi.size + part_header_size;
|
||||
if (fi_index != fi_prts) {
|
||||
session << lfi;
|
||||
lfi.clear();
|
||||
cur_size = min_size;
|
||||
fs += fi.size;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (packet_size - cur_size < min_size) {
|
||||
session << lfi;
|
||||
lfi.clear();
|
||||
cur_size = min_size;
|
||||
}
|
||||
}
|
||||
if (cur_size > min_size) session << lfi;
|
||||
}
|
||||
|
||||
|
||||
void PIBaseTransfer::sendBreak(int session_id) {
|
||||
uint psid = header.session_id;
|
||||
header.session_id = session_id;
|
||||
sendReply(pt_Break);
|
||||
header.session_id = psid;
|
||||
}
|
||||
|
||||
|
||||
void PIBaseTransfer::sendReply(PacketType reply) {
|
||||
header.type = reply;
|
||||
PIByteArray ba;
|
||||
ba << header;
|
||||
sendRequest(ba);
|
||||
}
|
||||
|
||||
|
||||
bool PIBaseTransfer::getStartRequest() {
|
||||
PITimeMeasurer tm;
|
||||
header.type = pt_Start;
|
||||
header.id = 0;
|
||||
PIByteArray ba;
|
||||
StartRequest st;
|
||||
st.packets = (uint)session.size();
|
||||
st.size = bytes_all;
|
||||
ba << header;
|
||||
ba << st;
|
||||
state_string = "send request";
|
||||
for (int i = 0; i < 5; i++) {
|
||||
tm.reset();
|
||||
sendRequest(ba);
|
||||
while (tm.elapsed_s() < timeout_) {
|
||||
if (break_) return false;
|
||||
//piCoutObj << send_replyes[0];
|
||||
if (replies[0] == pt_ReplySuccess) {
|
||||
state_string = "send permited!";
|
||||
return true;
|
||||
}
|
||||
piMSleep(10);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void PIBaseTransfer::processData(int id, PIByteArray & data) {
|
||||
// piCoutObj << "received packet" << id << ", size" << data.size();
|
||||
if (id < 1 || id > replies.size_s()) return;
|
||||
if (!session[id - 1].isEmpty()) {
|
||||
header.id = id;
|
||||
replies[id] = pt_ReplySuccess;
|
||||
sendReply(pt_ReplySuccess);
|
||||
if (checkSession() == 0) state_string = "receive ok";
|
||||
return;
|
||||
}
|
||||
Part fi;
|
||||
PIByteArray ba, pheader;
|
||||
pheader.resize(packet_header_size - sizeof(PacketHeader));
|
||||
if (!pheader.isEmpty()) {
|
||||
memcpy(pheader.data(), data.data(), pheader.size());
|
||||
data.remove(0, pheader.size_s());
|
||||
}
|
||||
while (!data.isEmpty()) {
|
||||
ba.clear();
|
||||
data >> fi;
|
||||
/*if (fi.size > 0) */data >> ba;
|
||||
// fi.fsize = ba.size();
|
||||
bytes_cur += fi.size;
|
||||
// piCoutObj << "recv" << fi;
|
||||
session[id - 1] << fi;
|
||||
state_string = "receiving...";
|
||||
receivePart(fi, ba, pheader);
|
||||
}
|
||||
header.id = id;
|
||||
replies[id] = pt_ReplySuccess;
|
||||
if (checkSession() == 0) state_string = "receive ok";
|
||||
sendReply(pt_ReplySuccess);
|
||||
}
|
||||
|
||||
|
||||
PIByteArray PIBaseTransfer::build_packet(int id) {
|
||||
PIByteArray ret;
|
||||
PIByteArray ba;
|
||||
header.id = id + 1;
|
||||
header.type = pt_Data;
|
||||
// piCoutObj << "Packet" << header.id;
|
||||
// piCoutObj << "session id" << header.session_id;
|
||||
//ret << header;
|
||||
ret.append(customHeader());
|
||||
for (int i = 0; i < session[id].size_s(); i++) {
|
||||
Part fi = session[id][i];
|
||||
ret << fi;
|
||||
// piCout << "biuld" << fi;
|
||||
ba = buildPacket(fi);
|
||||
bytes_cur += ba.size();
|
||||
if (ba.size() != fi.size) piCoutObj << "***error while build packet, wrong part size";
|
||||
ret << ba;
|
||||
}
|
||||
header.crc = crc.calculate(ret);
|
||||
PIByteArray hdr; hdr << header;
|
||||
ret.insert(0, hdr);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
bool PIBaseTransfer::finish_send(bool ok) {
|
||||
if (ok) state_string = "send done";
|
||||
else state_string = "send failed";
|
||||
// piCoutObj << state_string << PIString::readableSize(bytes_all);
|
||||
is_sending = false;
|
||||
header.id = 0;
|
||||
if (!ok) sendBreak(header.session_id);
|
||||
else sendReply(pt_ReplySuccess);
|
||||
sendFinished(ok);
|
||||
bytes_all = bytes_cur = 0;
|
||||
return ok;
|
||||
}
|
||||
|
||||
|
||||
void PIBaseTransfer::finish_receive(bool ok) {
|
||||
if (ok) state_string = "receive done";
|
||||
else state_string = "receive failed";
|
||||
// piCoutObj << state_string << PIString::readableSize(bytes_all);
|
||||
is_receiving = false;
|
||||
if (!ok) sendBreak(header.session_id);
|
||||
receiveFinished(ok);
|
||||
bytes_all = bytes_cur = 0;
|
||||
}
|
||||
|
||||
114
src/io/pibasetransfer.h
Normal file
114
src/io/pibasetransfer.h
Normal file
@@ -0,0 +1,114 @@
|
||||
#ifndef PIBASETRANSFER_H
|
||||
#define PIBASETRANSFER_H
|
||||
|
||||
#include "picrc.h"
|
||||
#include "pitimer.h"
|
||||
|
||||
class PIBaseTransfer : public PIObject
|
||||
{
|
||||
PIOBJECT(PIBaseTransfer)
|
||||
public:
|
||||
PIBaseTransfer();
|
||||
~PIBaseTransfer();
|
||||
|
||||
# pragma pack(push,1)
|
||||
struct PacketHeader {
|
||||
uint sig;
|
||||
int type; // PacketType
|
||||
int session_id;
|
||||
uint id;
|
||||
uint crc;
|
||||
bool check_sig() {return (sig == signature);}
|
||||
};
|
||||
|
||||
struct Part {
|
||||
Part(uint id_ = 0, ullong size_ = 0, ullong start_ = 0) : id(id_), size(size_), start(start_) {}
|
||||
uint id;
|
||||
ullong size;
|
||||
ullong start;
|
||||
};
|
||||
# pragma pack(pop)
|
||||
|
||||
void stopSend();
|
||||
void stopReceive();
|
||||
|
||||
bool isSending() const {return is_sending;}
|
||||
bool isReceiving() const {return is_receiving;}
|
||||
|
||||
void setPacketSize(int size) {packet_size = size;}
|
||||
int packetSize() const {return packet_size;}
|
||||
|
||||
void setTimeout(double sec) {timeout_ = sec;}
|
||||
double timeout() const {return timeout_;}
|
||||
|
||||
const PIString & stateString() const {return state_string;}
|
||||
llong bytesAll() const {return bytes_all;}
|
||||
llong bytesCur() const {return bytes_cur;}
|
||||
const PIString * stateString_ptr() const {return &state_string;}
|
||||
const llong * bytesAll_ptr() const {return &bytes_all;}
|
||||
const llong * bytesCur_ptr() const {return &bytes_cur;}
|
||||
|
||||
EVENT(receiveStarted)
|
||||
EVENT1(receiveFinished, bool, ok)
|
||||
EVENT(sendStarted)
|
||||
EVENT1(sendFinished, bool, ok)
|
||||
EVENT1(sendRequest, PIByteArray &, data)
|
||||
EVENT_HANDLER1(void, received, PIByteArray &, data);
|
||||
|
||||
protected:
|
||||
uint packet_header_size, part_header_size;
|
||||
bool break_, is_sending, is_receiving;
|
||||
PIString state_string;
|
||||
llong bytes_all, bytes_cur;
|
||||
|
||||
void buildSession(PIVector<Part> parts);
|
||||
virtual PIByteArray buildPacket(Part fi) = 0;
|
||||
virtual void receivePart(Part fi, PIByteArray ba, PIByteArray pheader) = 0;
|
||||
virtual PIByteArray customHeader() {return PIByteArray();}
|
||||
bool send_process();
|
||||
|
||||
private:
|
||||
|
||||
enum PacketType {pt_Unknown, pt_Data, pt_ReplySuccess, pt_ReplyInvalid, pt_Break, pt_Start};
|
||||
|
||||
# pragma pack(push,1)
|
||||
struct StartRequest {
|
||||
uint packets;
|
||||
ullong size;
|
||||
};
|
||||
# pragma pack(pop)
|
||||
|
||||
friend PIByteArray & operator >>(PIByteArray & s, PIBaseTransfer::StartRequest & v);
|
||||
friend PIByteArray & operator <<(PIByteArray & s, const PIBaseTransfer::StartRequest & v);
|
||||
|
||||
static const uint signature;
|
||||
int packet_size;
|
||||
double timeout_;
|
||||
PIVector<PIVector<Part> > session;
|
||||
PIVector<PacketType> replies;
|
||||
PacketHeader header;
|
||||
CRC_16 crc;
|
||||
|
||||
void processData(int id, PIByteArray &data);
|
||||
PIByteArray build_packet(int id);
|
||||
int checkSession();
|
||||
void sendBreak(int session_id);
|
||||
void sendReply(PacketType reply);
|
||||
bool getStartRequest();
|
||||
bool finish_send(bool ok);
|
||||
void finish_receive(bool ok);
|
||||
};
|
||||
|
||||
inline PIByteArray & operator <<(PIByteArray & s, const PIBaseTransfer::PacketHeader & v) {s << v.sig << v.type << v.session_id << v.id << v.crc; return s;}
|
||||
inline PIByteArray & operator >>(PIByteArray & s, PIBaseTransfer::PacketHeader & v) {s >> v.sig >> v.type >> v.session_id >> v.id >> v.crc; return s;}
|
||||
|
||||
inline PIByteArray & operator <<(PIByteArray & s, const PIBaseTransfer::Part & v) {s << v.id << v.size << v.start; return s;}
|
||||
inline PIByteArray & operator >>(PIByteArray & s, PIBaseTransfer::Part & v) {s >> v.id >> v.size >> v.start; return s;}
|
||||
|
||||
inline PIByteArray & operator <<(PIByteArray & s, const PIBaseTransfer::StartRequest & v) {s << v.packets << v.size; return s;}
|
||||
inline PIByteArray & operator >>(PIByteArray & s, PIBaseTransfer::StartRequest & v) {s >> v.packets >> v.size; return s;}
|
||||
|
||||
inline PICout operator <<(PICout s, const PIBaseTransfer::Part & v) {s.setControl(0, true); s << "Part(\"" << v.id << "\", " << PIString::readableSize(v.start) << " b | " << PIString::readableSize(v.size) << " b)"; s.restoreControl(); return s;}
|
||||
|
||||
|
||||
#endif // PIBASETRANSFER_H
|
||||
@@ -1,1064 +0,0 @@
|
||||
/*
|
||||
PIP - Platform Independent Primitives
|
||||
Console output/input
|
||||
Copyright (C) 2014 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#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() {
|
||||
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
|
||||
ulcoord.X = 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;
|
||||
ulcoord.Y = sbi.srWindow.Top;
|
||||
GetConsoleMode(hOut, &smode);
|
||||
GetConsoleCursorInfo(hOut, &curinfo);
|
||||
#else
|
||||
winsize ws;
|
||||
ioctl(0, TIOCGWINSZ, &ws);
|
||||
width = ws.ws_col;
|
||||
height = ws.ws_row;
|
||||
#endif
|
||||
tabs.reserve(16);
|
||||
addTab("main");
|
||||
listener = new PIKbdListener(key_event, this);
|
||||
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;
|
||||
#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);
|
||||
}
|
||||
|
||||
|
||||
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(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::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(hOut, smode);
|
||||
SetConsoleTextAttribute(hOut, 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(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::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() {
|
||||
if (pause_) return;
|
||||
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<Variable> & 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(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<Variable> & 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<const char * >(src)[stbyte];
|
||||
for (int i = 0; i < count; i++) {
|
||||
ret |= ((cbyte >> cbit & 1) << i);
|
||||
cbit++;
|
||||
if (cbit == 8) {
|
||||
cbit = 0;
|
||||
stbyte++;
|
||||
cbyte = reinterpret_cast<const char * >(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:
|
||||
* * "protocol <name>"
|
||||
* * "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, FormatFlags 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:
|
||||
* * "<name> 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();
|
||||
srand(PISystemTime::current().nanoseconds);
|
||||
peer = new PIPeer("_rcc_:" + PIDateTime::current().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<VariableContent> 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<VariableContent> content;
|
||||
PIMap<int, Variable * > 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;
|
||||
}
|
||||
}
|
||||
@@ -1,508 +0,0 @@
|
||||
/*! \file piconsole.h
|
||||
* \brief Console output class
|
||||
*/
|
||||
/*
|
||||
PIP - Platform Independent Primitives
|
||||
Console output/input
|
||||
Copyright (C) 2014 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef PICONSOLE_H
|
||||
#define PICONSOLE_H
|
||||
|
||||
#include "pikbdlistener.h"
|
||||
#include "piprotocol.h"
|
||||
#include "pidiagnostics.h"
|
||||
#include "pisystemmonitor.h"
|
||||
#ifndef WINDOWS
|
||||
# include <sys/ioctl.h>
|
||||
# include <fcntl.h>
|
||||
#else
|
||||
# define COMMON_LVB_UNDERSCORE 0x8000
|
||||
#endif
|
||||
|
||||
class PIPeer;
|
||||
|
||||
class PIP_EXPORT PIConsole: public PIThread
|
||||
{
|
||||
PIOBJECT_SUBCLASS(PIConsole, PIThread)
|
||||
public:
|
||||
|
||||
//! Constructs %PIConsole with key handler "slot" and if "startNow" start it
|
||||
PIConsole(bool startNow = true, KBFunc slot = 0);
|
||||
|
||||
~PIConsole();
|
||||
|
||||
|
||||
//! Variables output format
|
||||
enum Format {
|
||||
Normal /** Default console format */ = 0x01,
|
||||
Bold /** Bold text */ = 0x02,
|
||||
Faint = 0x04,
|
||||
Italic = 0x08,
|
||||
Underline /** Underlined text */ = 0x10,
|
||||
Blink /** Blinked text */ = 0x20,
|
||||
Inverse /** Swap text and background colors */ = 0x40,
|
||||
Black /** Black text */ = 0x100,
|
||||
Red /** Red text */ = 0x200,
|
||||
Green /** Green text */ = 0x400,
|
||||
Yellow /** Yellow text */ = 0x800,
|
||||
Blue /** Blue text */ = 0x1000,
|
||||
Magenta /** Magenta text */ = 0x2000,
|
||||
Cyan /** Cyan text */ = 0x4000,
|
||||
White /** White text */ = 0x8000,
|
||||
BackBlack /** Black background */ = 0x10000,
|
||||
BackRed /** Red background */ = 0x20000,
|
||||
BackGreen /** Green background */ = 0x40000,
|
||||
BackYellow /** Yellow background */ = 0x80000,
|
||||
BackBlue /** Blue background */ = 0x100000,
|
||||
BackMagenta /** Magenta background */ = 0x200000,
|
||||
BackCyan /** Cyan background */ = 0x400000,
|
||||
BackWhite /** White background */ = 0x800000,
|
||||
Dec /** Decimal base for integers */ = 0x1000000,
|
||||
Hex /** Hexadecimal base for integers */ = 0x2000000,
|
||||
Oct /** Octal base for integers */ = 0x4000000,
|
||||
Bin /** Binary base for integers */ = 0x8000000,
|
||||
Scientific /** Scientific representation of floats */ = 0x10000000,
|
||||
SystemTimeSplit /** PISystemTime split representation (* s, * ns) */ = 0x20000000,
|
||||
SystemTimeSeconds /** PISystemTime seconds representation (*.* s) */ = 0x40000000
|
||||
};
|
||||
|
||||
//! Column labels alignment
|
||||
enum Alignment {
|
||||
Nothing /** No alignment */ ,
|
||||
Left /** Labels align left and variables align left */ ,
|
||||
Right /** Labels align right and variables align left */
|
||||
};
|
||||
|
||||
typedef PIFlags<PIConsole::Format> FormatFlags;
|
||||
|
||||
//! Add to current tab to column "column" string "name" with format "format"
|
||||
void addString(const PIString & name, int column = 1, FormatFlags format = PIConsole::Normal);
|
||||
|
||||
//! Add to current tab to column "column" variable with label "name", pointer "ptr" and format "format"
|
||||
void addVariable(const PIString & name, const PIString * ptr, int column = 1, FormatFlags format = PIConsole::Normal);
|
||||
|
||||
//! Add to current tab to column "column" variable with label "name", pointer "ptr" and format "format"
|
||||
void addVariable(const PIString & name, const char * ptr, int column = 1, FormatFlags format = PIConsole::Normal);
|
||||
|
||||
//! Add to current tab to column "column" variable with label "name", pointer "ptr" and format "format"
|
||||
void addVariable(const PIString & name, const bool * ptr, int column = 1, FormatFlags format = PIConsole::Normal);
|
||||
|
||||
//! Add to current tab to column "column" variable with label "name", pointer "ptr" and format "format"
|
||||
void addVariable(const PIString & name, const short * ptr, int column = 1, FormatFlags format = PIConsole::Normal);
|
||||
|
||||
//! Add to current tab to column "column" variable with label "name", pointer "ptr" and format "format"
|
||||
void addVariable(const PIString & name, const int * ptr, int column = 1, FormatFlags format = PIConsole::Normal);
|
||||
|
||||
//! Add to current tab to column "column" variable with label "name", pointer "ptr" and format "format"
|
||||
void addVariable(const PIString & name, const long * ptr, int column = 1, FormatFlags format = PIConsole::Normal);
|
||||
|
||||
//! Add to current tab to column "column" variable with label "name", pointer "ptr" and format "format"
|
||||
void addVariable(const PIString & name, const llong * ptr, int column = 1, FormatFlags format = PIConsole::Normal);
|
||||
|
||||
//! Add to current tab to column "column" variable with label "name", pointer "ptr" and format "format"
|
||||
void addVariable(const PIString & name, const uchar * ptr, int column = 1, FormatFlags format = PIConsole::Normal);
|
||||
|
||||
//! Add to current tab to column "column" variable with label "name", pointer "ptr" and format "format"
|
||||
void addVariable(const PIString & name, const ushort * ptr, int column = 1, FormatFlags format = PIConsole::Normal);
|
||||
|
||||
//! Add to current tab to column "column" variable with label "name", pointer "ptr" and format "format"
|
||||
void addVariable(const PIString & name, const uint * ptr, int column = 1, FormatFlags format = PIConsole::Normal);
|
||||
|
||||
//! Add to current tab to column "column" variable with label "name", pointer "ptr" and format "format"
|
||||
void addVariable(const PIString & name, const ulong * ptr, int column = 1, FormatFlags format = PIConsole::Normal);
|
||||
|
||||
//! Add to current tab to column "column" variable with label "name", pointer "ptr" and format "format"
|
||||
void addVariable(const PIString & name, const ullong * ptr, int column = 1, FormatFlags format = PIConsole::Normal);
|
||||
|
||||
//! Add to current tab to column "column" variable with label "name", pointer "ptr" and format "format"
|
||||
void addVariable(const PIString & name, const float * ptr, int column = 1, FormatFlags format = PIConsole::Normal);
|
||||
|
||||
//! Add to current tab to column "column" variable with label "name", pointer "ptr" and format "format"
|
||||
void addVariable(const PIString & name, const double * ptr, int column = 1, FormatFlags format = PIConsole::Normal);
|
||||
|
||||
//! Add to current tab to column "column" variable with label "name", pointer "ptr" and format "format"
|
||||
void addVariable(const PIString & name, const PISystemTime * ptr, int column = 1, FormatFlags format = PIConsole::Normal);
|
||||
|
||||
void addVariable(const PIString & name, const PIProtocol * ptr, int column = 1, FormatFlags format = PIConsole::Normal);
|
||||
void addVariable(const PIString & name, const PIDiagnostics * ptr, int column = 1, FormatFlags format = PIConsole::Normal);
|
||||
void addVariable(const PIString & name, const PISystemMonitor * ptr, int column = 1, FormatFlags format = PIConsole::Normal);
|
||||
|
||||
//! Add to current tab to column "column" bits field with label "name", pointer "ptr" and format "format"
|
||||
void addBitVariable(const PIString & name, const void * ptr, int fromBit, int bitsCount, int column = 1, FormatFlags format = PIConsole::Normal);
|
||||
|
||||
//! Add to current tab to column "column" "count" empty lines
|
||||
void addEmptyLine(int column = 1, uint count = 1);
|
||||
|
||||
PIString getString(int x, int y);
|
||||
short getShort(int x, int y) {return getString(x, y).toShort();}
|
||||
int getInt(int x, int y) {return getString(x, y).toInt();}
|
||||
float getFloat(int x, int y) {return getString(x, y).toFloat();}
|
||||
double getDouble(int x, int y) {return getString(x, y).toDouble();}
|
||||
PIString getString(const PIString & name);
|
||||
short getShort(const PIString & name) {return getString(name).toShort();}
|
||||
int getInt(const PIString & name) {return getString(name).toInt();}
|
||||
float getFloat(const PIString & name) {return getString(name).toFloat();}
|
||||
double getDouble(const PIString & name) {return getString(name).toDouble();}
|
||||
|
||||
|
||||
//! Returns tabs count
|
||||
uint tabsCount() const {return tabs.size();}
|
||||
|
||||
//! Returns current tab name
|
||||
PIString currentTab() const {return tabs[cur_tab].name;}
|
||||
|
||||
//! Returns current tab index
|
||||
int currentTabIndex() const {return cur_tab;}
|
||||
|
||||
//! Add new tab with name "name", bind key "bind_key" and returns this tab index
|
||||
int addTab(const PIString & name, char bind_key = 0);
|
||||
|
||||
//! Remove tab with index "index"
|
||||
void removeTab(uint index);
|
||||
|
||||
//! Remove tab with name "name"
|
||||
void removeTab(const PIString & name);
|
||||
|
||||
//! Clear content of tab with index "index"
|
||||
void clearTab(uint index);
|
||||
|
||||
//! Clear content of tab with name "name"
|
||||
void clearTab(const PIString & name);
|
||||
|
||||
//! Set current tab to tab with index "index", returns if tab exists
|
||||
bool setTab(uint index);
|
||||
|
||||
//! Set current tab to tab with name "name", returns if tab exists
|
||||
bool setTab(const PIString & name);
|
||||
|
||||
//! Set tab with index "index" bind key to "bind_key", returns if tab exists
|
||||
bool setTabBindKey(uint index, char bind_key);
|
||||
|
||||
//! Set tab with name "name" bind key to "bind_key", returns if tab exists
|
||||
bool setTabBindKey(const PIString & name, char bind_key);
|
||||
|
||||
//! Remove all tabs and if "clearScreen" clear the screen
|
||||
void clearTabs(bool clearScreen = true) {if (clearScreen && isRunning()) {toUpperLeft(); clearScreenLower();} tabs.clear();}
|
||||
|
||||
|
||||
//! Set custom status text of current tab to "str"
|
||||
void addCustomStatus(const PIString & str) {tabs[cur_tab].status = str;}
|
||||
|
||||
//! Clear custom status text of current tab
|
||||
void clearCustomStatus() {tabs[cur_tab].status.clear();}
|
||||
|
||||
//! Returns default alignment
|
||||
Alignment defaultAlignment() const {return def_align;}
|
||||
|
||||
//! Set default alignment to "align"
|
||||
void setDefaultAlignment(Alignment align) {def_align = align;}
|
||||
|
||||
//! Set column "col" alignment to "align"
|
||||
void setColumnAlignment(int col, Alignment align) {if (col < 0 || col >= columns().size_s()) return; column(col).alignment = align;}
|
||||
|
||||
//! Set all columns of all tabs alignment to "align"
|
||||
void setColumnAlignmentToAll(Alignment align) {piForeach (Tab & i, tabs) piForeach (Column & j, i.columns) j.alignment = align; fillLabels();}
|
||||
|
||||
|
||||
//! Directly call function from \a PIKbdListener
|
||||
void enableExitCapture(char key = 'Q') {listener->enableExitCapture(key);}
|
||||
|
||||
//! Directly call function from \a PIKbdListener
|
||||
void disableExitCapture() {listener->disableExitCapture();}
|
||||
|
||||
//! Directly call function from \a PIKbdListener
|
||||
bool exitCaptured() const {return listener->exitCaptured();}
|
||||
|
||||
//! Directly call function from \a PIKbdListener
|
||||
char exitKey() const {return listener->exitKey();}
|
||||
|
||||
|
||||
int windowWidth() const {return width;}
|
||||
int windowHeight() const {return height;}
|
||||
|
||||
PIString fstr(FormatFlags f);
|
||||
void update();
|
||||
void pause(bool yes) {pause_ = yes;}
|
||||
|
||||
// Server functions
|
||||
void startServer(const PIString & name);
|
||||
void stopPeer();
|
||||
bool isServerStarted() const {return peer != 0;}
|
||||
PIStringList clients() const;
|
||||
|
||||
// Client functions
|
||||
void listenServers();
|
||||
PIStringList availableServers() const;
|
||||
PIString selectedServer() const {return server_name;}
|
||||
void connectToServer(const PIString & name);
|
||||
void disconnect();
|
||||
bool isConnected() const {return state == Connected;}
|
||||
|
||||
#ifdef WINDOWS
|
||||
void toUpperLeft() {SetConsoleCursorPosition(hOut, ulcoord);}
|
||||
void moveRight(int n = 1) {SetConsoleCursorPosition(hOut, getWinCoord(n));}
|
||||
void moveLeft(int n = 1) {SetConsoleCursorPosition(hOut, getWinCoord(-n));}
|
||||
void moveTo(int x = 0, int y = 0) {ccoord.X = x; ccoord.Y = ulcoord.Y + y; SetConsoleCursorPosition(hOut, ccoord);}
|
||||
void clearScreen() {couts(fstr(Normal)); toUpperLeft(); FillConsoleOutputAttribute(hOut, dattr, width * (height + 1), ulcoord, &written);
|
||||
FillConsoleOutputCharacter(hOut, ' ', width * (height + 1), ulcoord, &written);}
|
||||
void clearScreenLower() {couts(fstr(Normal)); getWinCurCoord(); FillConsoleOutputAttribute(hOut, dattr, width * height - width * ccoord.Y + ccoord.X, ccoord, &written);
|
||||
FillConsoleOutputCharacter(hOut, ' ', width * height - width * ccoord.Y + ccoord.X, ccoord, &written);}
|
||||
void clearLine() {getWinCurCoord(); FillConsoleOutputAttribute(hOut, dattr, width - ccoord.X, ccoord, &written);
|
||||
FillConsoleOutputCharacter(hOut, ' ', width - ccoord.X, ccoord, &written);}
|
||||
void newLine() {getWinCurCoord(); ccoord.X = 0; ccoord.Y++; SetConsoleCursorPosition(hOut, ccoord);}
|
||||
void hideCursor() {curinfo.bVisible = false; SetConsoleCursorInfo(hOut, &curinfo);}
|
||||
void showCursor() {curinfo.bVisible = true; SetConsoleCursorInfo(hOut, &curinfo);}
|
||||
#else
|
||||
void toUpperLeft() {printf("\e[H");}
|
||||
void moveRight(int n = 1) {if (n > 0) printf("\e[%dC", n);}
|
||||
void moveLeft(int n = 1) {if (n > 0) printf("\e[%dD", n);}
|
||||
void moveTo(int x = 0, int y = 0) {printf("\e[%d;%dH", y, x);}
|
||||
void clearScreen() {couts(fstr(Normal)); printf("\e[H\e[J");}
|
||||
void clearScreenLower() {couts(fstr(Normal)); printf("\e[J");}
|
||||
void clearLine() {printf("\e[K");}
|
||||
void newLine() {printf("\eE");}
|
||||
void hideCursor() {printf("\e[?25l");}
|
||||
void showCursor() {printf("\e[?25h");}
|
||||
#endif
|
||||
|
||||
EVENT_HANDLER0(void, clearVariables) {clearVariables(true);}
|
||||
EVENT_HANDLER1(void, clearVariables, bool, clearScreen);
|
||||
|
||||
EVENT_HANDLER0(void, waitForFinish) {WAIT_FOR_EXIT}
|
||||
EVENT_HANDLER0(void, start) {start(false);}
|
||||
EVENT_HANDLER1(void, start, bool, wait) {PIThread::start(40); if (wait) waitForFinish();}
|
||||
EVENT_HANDLER0(void, stop) {stop(false);}
|
||||
EVENT_HANDLER1(void, stop, bool, clear);
|
||||
|
||||
EVENT2(keyPressed, char, key, void * , data)
|
||||
|
||||
//! \handlers
|
||||
//! \{
|
||||
|
||||
//! \fn void waitForFinish()
|
||||
//! \brief block until finished (exit key will be pressed)
|
||||
|
||||
//! \fn void clearVariables(bool clearScreen = true)
|
||||
//! \brief Remove all columns at current tab and if "clearScreen" clear the screen
|
||||
|
||||
//! \fn void start(bool wait = false)
|
||||
//! \brief Start console output and if "wait" block until finished (exit key will be pressed)
|
||||
|
||||
//! \fn void stop(bool clear = false)
|
||||
//! \brief Stop console output and if "clear" clear the screen
|
||||
|
||||
//! \}
|
||||
//! \events
|
||||
//! \{
|
||||
|
||||
//! \fn void keyPressed(char key, void * data)
|
||||
//! \brief Raise on key "key" pressed, "data" is pointer to %PIConsole object
|
||||
|
||||
//! \}
|
||||
|
||||
private:
|
||||
#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;}
|
||||
#endif
|
||||
|
||||
void begin();
|
||||
void run();
|
||||
void fillLabels();
|
||||
void status();
|
||||
void checkColumn(uint col) {while (columns().size() < col) columns().push_back(Column(def_align));}
|
||||
int bitsValue(const void * src, int offset, int count) const;
|
||||
const char * toBin(const void * d, int s);
|
||||
inline void printLine(const PIString & str, int dx = 0, FormatFlags format = PIConsole::Normal);
|
||||
inline int printValue(const PIString & str, FormatFlags format = PIConsole::Normal);
|
||||
inline int printValue(const char * str, FormatFlags format = PIConsole::Normal);
|
||||
inline int printValue(const bool value, FormatFlags format = PIConsole::Normal);
|
||||
inline int printValue(const int value, FormatFlags format = PIConsole::Normal);
|
||||
inline int printValue(const long value, FormatFlags format = PIConsole::Normal);
|
||||
inline int printValue(const llong value, FormatFlags format = PIConsole::Normal);
|
||||
inline int printValue(const float value, FormatFlags format = PIConsole::Normal);
|
||||
inline int printValue(const double value, FormatFlags format = PIConsole::Normal);
|
||||
inline int printValue(const char value, FormatFlags format = PIConsole::Normal);
|
||||
inline int printValue(const short value, FormatFlags format = PIConsole::Normal);
|
||||
inline int printValue(const uchar value, FormatFlags format = PIConsole::Normal);
|
||||
inline int printValue(const ushort value, FormatFlags format = PIConsole::Normal);
|
||||
inline int printValue(const uint value, FormatFlags format = PIConsole::Normal);
|
||||
inline int printValue(const ulong value, FormatFlags format = PIConsole::Normal);
|
||||
inline int printValue(const ullong value, FormatFlags format = PIConsole::Normal);
|
||||
inline int printValue(const PISystemTime & value, FormatFlags format = PIConsole::Normal);
|
||||
static void key_event(char key, void * t);
|
||||
|
||||
struct Variable {
|
||||
Variable() {nx = ny = type = offset = bitFrom = bitCount = size = 0; format = Normal; remote = false; ptr = 0; id = 1;}
|
||||
bool isEmpty() const {return (remote ? false : ptr == 0);}
|
||||
const void * data() {return (remote ? rdata.data() : ptr);}
|
||||
void writeData(PIByteArray & ba) {
|
||||
if (remote) ba << rdata;
|
||||
else {
|
||||
if (type == 0) ba << (*(PIString * )ptr);
|
||||
else ba << PIByteArray::RawData(ptr, size);
|
||||
}
|
||||
}
|
||||
PIString name;
|
||||
FormatFlags format;
|
||||
int nx;
|
||||
int ny;
|
||||
int type;
|
||||
int offset;
|
||||
int bitFrom;
|
||||
int bitCount;
|
||||
int size;
|
||||
int id;
|
||||
bool remote;
|
||||
const void * ptr;
|
||||
PIByteArray rdata;
|
||||
void operator =(const Variable & src) {remote = src.remote; name = src.name; format = src.format; type = src.type; offset = src.offset; size = src.size;
|
||||
bitFrom = src.bitFrom; bitCount = src.bitCount; ptr = src.ptr; nx = src.nx; ny = src.ny; rdata = src.rdata; id = src.id;}
|
||||
};
|
||||
|
||||
struct VariableContent {
|
||||
int id;
|
||||
PIByteArray rdata;
|
||||
};
|
||||
|
||||
struct Column {
|
||||
Column(Alignment align = PIConsole::Right) {variables.reserve(32); alignment = align;}
|
||||
PIVector<Variable> variables;
|
||||
Alignment alignment;
|
||||
uint size() const {return variables.size();}
|
||||
Variable & operator [](int index) {return variables[index];}
|
||||
const Variable & operator [](int index) const {return variables[index];}
|
||||
void push_back(const Variable & v) {variables.push_back(v);}
|
||||
void operator =(const Column & src) {variables = src.variables; alignment = src.alignment;}
|
||||
};
|
||||
|
||||
struct Tab {
|
||||
Tab(PIString n = "", char k = 0) {columns.reserve(8); name = n; key = k;}
|
||||
PIVector<Column> columns;
|
||||
PIString name;
|
||||
PIString status;
|
||||
char key;
|
||||
};
|
||||
|
||||
enum ConnectedState {Disconnected, FetchingData, Committing, Connected};
|
||||
|
||||
friend PIByteArray & operator <<(PIByteArray & ba, const PIConsole::VariableContent & v);
|
||||
friend PIByteArray & operator >>(PIByteArray & ba, PIConsole::VariableContent & v);
|
||||
|
||||
friend PIByteArray & operator <<(PIByteArray & ba, const PIConsole::Variable & v);
|
||||
friend PIByteArray & operator >>(PIByteArray & ba, PIConsole::Variable & v);
|
||||
|
||||
friend PIByteArray & operator <<(PIByteArray & ba, const PIConsole::Column & v);
|
||||
friend PIByteArray & operator >>(PIByteArray & ba, PIConsole::Column & v);
|
||||
|
||||
friend PIByteArray & operator <<(PIByteArray & ba, const PIConsole::Tab & v);
|
||||
friend PIByteArray & operator >>(PIByteArray & ba, PIConsole::Tab & v);
|
||||
|
||||
PIVector<Column> & columns() {return tabs[cur_tab].columns;}
|
||||
Column & column(int index) {return tabs[cur_tab].columns[index - 1];}
|
||||
inline int couts(const PIString & v) {return printf("%s", v.data());}
|
||||
inline int couts(const char * v) {return printf("%s", v);}
|
||||
inline int couts(const bool v);
|
||||
inline int couts(const char v);
|
||||
inline int couts(const short v);
|
||||
inline int couts(const int v);
|
||||
inline int couts(const long v);
|
||||
inline int couts(const llong v);
|
||||
inline int couts(const uchar v);
|
||||
inline int couts(const ushort v);
|
||||
inline int couts(const uint v);
|
||||
inline int couts(const ulong v);
|
||||
inline int couts(const ullong v);
|
||||
inline int couts(const float v);
|
||||
inline int couts(const double v);
|
||||
inline int couts(const PISystemTime & v);
|
||||
|
||||
struct RemoteClient;
|
||||
|
||||
void serverSendInfo();
|
||||
void serverSendData();
|
||||
RemoteClient & remoteClient(const PIString & fname);
|
||||
EVENT_HANDLER2(void, peerReceived, const PIString &, from, const PIByteArray &, data);
|
||||
EVENT_HANDLER2(void, peerTimer, void * , data, int, delim);
|
||||
EVENT_HANDLER1(void, peerDisconnectedEvent, const PIString &, name);
|
||||
|
||||
#ifdef WINDOWS
|
||||
void * hOut;
|
||||
CONSOLE_SCREEN_BUFFER_INFO sbi, csbi;
|
||||
CONSOLE_CURSOR_INFO curinfo;
|
||||
COORD ccoord, ulcoord;
|
||||
WORD dattr;
|
||||
DWORD smode, written;
|
||||
#else
|
||||
struct termios sterm, vterm;
|
||||
#endif
|
||||
PIVector<Tab> tabs;
|
||||
PIString binstr, rstr;
|
||||
PIByteArray rba;
|
||||
Variable tv;
|
||||
PIKbdListener * listener;
|
||||
Alignment def_align;
|
||||
KBFunc ret_func;
|
||||
int width, height, pwidth, pheight, ret, col_wid, num_format, systime_format;
|
||||
uint max_y;
|
||||
int vid;
|
||||
uint cur_tab, col_cnt;
|
||||
|
||||
PIPeer * peer;
|
||||
PITimer peer_timer;
|
||||
PITimeMeasurer peer_tm;
|
||||
PIString server_name;
|
||||
bool server_mode, pause_;
|
||||
ConnectedState state;
|
||||
|
||||
/*struct RemoteData {
|
||||
RemoteData() {msg_count = msg_rec = msg_send = 0;}
|
||||
void clear() {msg_count = msg_rec = msg_send = 0; data.clear();}
|
||||
bool isEmpty() const {return msg_count == 0;}
|
||||
bool isReadyRec() const {return msg_count == msg_rec;}
|
||||
bool isReadySend() const {return msg_count == msg_send;}
|
||||
void setData(const PIByteArray & ba) {data = ba; msg_rec = msg_send = 0; msg_count = (data.size_s() - 1) / 4096 + 1;}
|
||||
PIByteArray data;
|
||||
int msg_count;
|
||||
int msg_rec;
|
||||
int msg_send;
|
||||
};*/
|
||||
|
||||
struct RemoteClient {
|
||||
RemoteClient(const PIString & n = "") {name = n; state = Disconnected;}
|
||||
PIString name;
|
||||
ConnectedState state;
|
||||
};
|
||||
|
||||
PIVector<RemoteClient> remote_clients;
|
||||
|
||||
};
|
||||
|
||||
inline PIByteArray & operator <<(PIByteArray & ba, const PIConsole::VariableContent & v) {ba << v.id << v.rdata; return ba;}
|
||||
inline PIByteArray & operator >>(PIByteArray & ba, PIConsole::VariableContent & v) {ba >> v.id; ba >> v.rdata; return ba;}
|
||||
|
||||
inline PIByteArray & operator <<(PIByteArray & ba, const PIConsole::Variable & v) {ba << v.name << v.id << (int)v.format << v.type << v.size << v.bitFrom << v.bitCount; return ba;}
|
||||
inline PIByteArray & operator >>(PIByteArray & ba, PIConsole::Variable & v) {ba >> v.name >> v.id >> (int & )v.format >> v.type >> v.size >> v.bitFrom >> v.bitCount; return ba;}
|
||||
|
||||
inline PIByteArray & operator <<(PIByteArray & ba, const PIConsole::Column & v) {ba << (int)v.alignment << v.variables; return ba;}
|
||||
inline PIByteArray & operator >>(PIByteArray & ba, PIConsole::Column & v) {ba >> (int & )v.alignment >> v.variables; return ba;}
|
||||
|
||||
inline PIByteArray & operator <<(PIByteArray & ba, const PIConsole::Tab & v) {ba << v.name << v.status << (uchar)v.key << v.columns; return ba;}
|
||||
inline PIByteArray & operator >>(PIByteArray & ba, PIConsole::Tab & v) {ba >> v.name >> v.status >> (uchar&)v.key >> v.columns; return ba;}
|
||||
|
||||
#endif // PICONSOLE_H
|
||||
@@ -1,176 +0,0 @@
|
||||
/*
|
||||
PIP - Platform Independent Primitives
|
||||
Keyboard grabber for console
|
||||
Copyright (C) 2014 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "pikbdlistener.h"
|
||||
|
||||
|
||||
/** \class PIKbdListener
|
||||
* \brief Keyboard console input listener
|
||||
* \details This class provide listening of console keyboard input.
|
||||
* There is two ways to receive pressed key:
|
||||
* * external static function with format "void func(char key, void * data_)"
|
||||
* * event \a keyPressed()
|
||||
*
|
||||
* Also there is static variable \a exiting which by default is set to
|
||||
* \b false. If \a enableExitCapture() was called and listener was started
|
||||
* with function \a start(), this variable will be set to \b true if exit
|
||||
* key will be pressed. By default exit key is 'Q' = shift + 'q'.
|
||||
* To wait for this variable changes to \b true there is WAIT_FOR_EXIT macro
|
||||
* \snippet pikbdlistener.cpp main
|
||||
* */
|
||||
|
||||
|
||||
bool PIKbdListener::exiting;
|
||||
|
||||
PIKbdListener::PIKbdListener(KBFunc slot, void * _data): PIThread() {
|
||||
setName("keyboard_listener");
|
||||
#ifdef WINDOWS
|
||||
hIn = GetStdHandle(STD_INPUT_HANDLE);
|
||||
GetConsoleMode(hIn, &smode);
|
||||
#else
|
||||
struct termios term;
|
||||
tcgetattr(0, &term);
|
||||
sterm = term;
|
||||
#endif
|
||||
is_active = true;
|
||||
ret_func = slot;
|
||||
data_ = _data;
|
||||
PIKbdListener::exiting = exit_enabled = false;
|
||||
start();
|
||||
}
|
||||
|
||||
|
||||
void PIKbdListener::begin() {
|
||||
#ifdef WINDOWS
|
||||
GetConsoleMode(hIn, &tmode);
|
||||
SetConsoleMode(hIn, ENABLE_PROCESSED_INPUT);
|
||||
#else
|
||||
struct termios term;
|
||||
tcgetattr(0, &term);
|
||||
term.c_lflag &= ~(ECHO | ICANON) | NOFLSH;
|
||||
tterm = term;
|
||||
tcsetattr(0, TCSANOW, &term);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void PIKbdListener::run() {
|
||||
rc = 0;
|
||||
char lc = 0;
|
||||
#ifdef WINDOWS
|
||||
INPUT_RECORD ir;
|
||||
ReadConsoleInput(hIn, &ir, 1, &ret);
|
||||
if (ir.EventType == KEY_EVENT) {
|
||||
KEY_EVENT_RECORD ker = ir.Event.KeyEvent;
|
||||
if (ker.bKeyDown) {
|
||||
bool ctrl = ((ker.dwControlKeyState & LEFT_CTRL_PRESSED) || (ker.dwControlKeyState & RIGHT_CTRL_PRESSED));
|
||||
bool shift = (ker.dwControlKeyState & SHIFT_PRESSED);
|
||||
if (ker.dwControlKeyState & CAPSLOCK_ON) shift = !shift;
|
||||
//cout << "key " << int(ker.wVirtualKeyCode) << endl;
|
||||
switch (ker.wVirtualKeyCode) {
|
||||
case 37: ret = 1; lc = (ctrl ? CtrlLeftArrow : LeftArrow); break;
|
||||
case 38: ret = 1; lc = (ctrl ? CtrlUpArrow : UpArrow); break;
|
||||
case 39: ret = 1; lc = (ctrl ? CtrlRightArrow : RightArrow); break;
|
||||
case 40: ret = 1; lc = (ctrl ? CtrlDownArrow : DownArrow); break;
|
||||
default: ret = 1; lc = (shift ? char(toupper(ker.uChar.AsciiChar)) : ker.uChar.AsciiChar); break;
|
||||
}
|
||||
if (lc == 0) {piMSleep(10); return;}
|
||||
} else {piMSleep(10); return;}
|
||||
} else {piMSleep(10); return;}
|
||||
/*if (lc == 0) {
|
||||
ReadConsole(hIn, &rc, 1, &ret, 0);
|
||||
//cout << "read console" << endl;
|
||||
lc = char(rc);
|
||||
}*/
|
||||
/*if (ret < 0 || ret > 3) return;
|
||||
lc = char(((uchar * )&rc)[ret - 1]);
|
||||
for (int i = 0; i < ret; ++i)
|
||||
cout << std::hex << int(((uchar * )&rc)[i]) << ' ';
|
||||
cout << endl << std::hex << rc << endl;*/
|
||||
#else
|
||||
tcsetattr(0, TCSANOW, &tterm);
|
||||
ret = read(0, &rc, 4);
|
||||
if (rc == 0) {piMSleep(10); return;}
|
||||
if (ret < 0 || ret > 3) {piMSleep(10); return;}
|
||||
lc = char(((uchar * )&rc)[ret - 1]);
|
||||
/*for (int i = 0; i < ret; ++i)
|
||||
cout << std::hex << int(((uchar * )&rc)[i]) << ' ';
|
||||
cout << endl << std::hex << rc << endl;*/
|
||||
if (((char * )&rc)[0] == '\e' && ret == 3) {
|
||||
if (((char * )&rc)[1] == '[') {
|
||||
switch (((char * )&rc)[2]) {
|
||||
case 'A': lc = UpArrow; break; // up
|
||||
case 'B': lc = DownArrow; break; // down
|
||||
case 'C': lc = RightArrow; break; // right
|
||||
case 'D': lc = LeftArrow; break; // left
|
||||
case 'H': lc = Home; break;
|
||||
case 'F': lc = End; break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (((char * )&rc)[0] == '5' && ret == 2) {
|
||||
switch (((char * )&rc)[1]) {
|
||||
case 'A': lc = CtrlUpArrow; break; // up
|
||||
case 'B': lc = CtrlDownArrow; break; // down
|
||||
case 'C': lc = CtrlRightArrow; break; // right
|
||||
case 'D': lc = CtrlLeftArrow; break; // left
|
||||
case 'H': lc = CtrlHome; break;
|
||||
case 'F': lc = CtrlEnd; break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if ((((char * )&rc)[0] == '\n' || ((char * )&rc)[0] == '\r') && ret == 1)
|
||||
lc = Return;
|
||||
if (exit_enabled && ret == 1 && lc == exit_key) {
|
||||
PIKbdListener::exiting = true;
|
||||
return;
|
||||
}
|
||||
if (ret > 0) {
|
||||
keyPressed(lc, data_);
|
||||
if (ret_func != 0) ret_func(lc, data_);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void PIKbdListener::end() {
|
||||
//cout << "list end" << endl;
|
||||
#ifdef WINDOWS
|
||||
SetConsoleMode(hIn, smode);
|
||||
#else
|
||||
tcsetattr(0, TCSANOW, &sterm);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void PIKbdListener::setActive(bool yes) {
|
||||
is_active = yes;
|
||||
if (is_active) {
|
||||
#ifdef WINDOWS
|
||||
SetConsoleMode(hIn, tmode);
|
||||
#else
|
||||
tcsetattr(0, TCSANOW, &tterm);
|
||||
#endif
|
||||
} else {
|
||||
#ifdef WINDOWS
|
||||
SetConsoleMode(hIn, smode);
|
||||
#else
|
||||
tcsetattr(0, TCSANOW, &sterm);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -1,139 +0,0 @@
|
||||
/*! \file pikbdlistener.h
|
||||
* \brief Keyboard console input listener
|
||||
*/
|
||||
/*
|
||||
PIP - Platform Independent Primitives
|
||||
Keyboard grabber for console
|
||||
Copyright (C) 2014 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef PIKBDLISTENER_H
|
||||
#define PIKBDLISTENER_H
|
||||
|
||||
#include "pithread.h"
|
||||
#ifndef WINDOWS
|
||||
# include <termios.h>
|
||||
#endif
|
||||
|
||||
#define WAIT_FOR_EXIT while (!PIKbdListener::exiting) msleep(5);
|
||||
|
||||
typedef void (*KBFunc)(char, void * );
|
||||
|
||||
class PIP_EXPORT PIKbdListener: public PIThread
|
||||
{
|
||||
PIOBJECT(PIKbdListener)
|
||||
PIOBJECT_PARENT(PIThread)
|
||||
friend class PIConsole;
|
||||
public:
|
||||
|
||||
//! Special keyboard keys
|
||||
enum SpecialSymbol {
|
||||
UpArrow /** Up arrow key */ = -1,
|
||||
DownArrow /** Down arrow key */ = -2,
|
||||
RightArrow /** Right arrow key */ = -3,
|
||||
LeftArrow /** Left arrow key */ = -4,
|
||||
Home /** Home key */ = -9,
|
||||
End /** End key */ = -10,
|
||||
CtrlUpArrow /** Ctrl + Up arrow key */ = -5,
|
||||
CtrlDownArrow /** Ctrl + Down arrow key */ = -6,
|
||||
CtrlRightArrow /** Ctrl + Right arrow key */ = -7,
|
||||
CtrlLeftArrow /** Ctrl + Left arrow key */ = -8,
|
||||
CtrlHome /** Ctrl + Home key */ = -11,
|
||||
CtrlEnd /** Ctrl + End key */ = -12,
|
||||
Return /** Enter key */ = 0x0a,
|
||||
Esc /** Escape key */ = 0x1b,
|
||||
Space /** Space key */ = 0x20,
|
||||
Backspace /** Backspace key */ = 0x7f
|
||||
};
|
||||
|
||||
//! Constructs keyboard listener with external function "slot" and custom data "data"
|
||||
PIKbdListener(KBFunc slot = 0, void * data = 0);
|
||||
|
||||
~PIKbdListener() {terminate(); end();}
|
||||
|
||||
|
||||
//! Returns custom data
|
||||
void * data() {return data_;}
|
||||
|
||||
//! Set custom data to "_data"
|
||||
void setData(void * _data) {data_ = _data;}
|
||||
|
||||
//! Set external function to "slot"
|
||||
void setSlot(KBFunc slot) {ret_func = slot;}
|
||||
|
||||
//! Returns if exit key if awaiting
|
||||
bool exitCaptured() const {return exit_enabled;}
|
||||
|
||||
//! Returns exit key, default 'Q'
|
||||
char exitKey() const {return exit_key;}
|
||||
|
||||
|
||||
//! Returns if keyboard listening is active (not running!)
|
||||
bool isActive() {return is_active;}
|
||||
|
||||
EVENT_HANDLER( void, enableExitCapture) {enableExitCapture('Q');}
|
||||
EVENT_HANDLER1(void, enableExitCapture, char, key) {exit_enabled = true; exit_key = key;}
|
||||
EVENT_HANDLER(void, disableExitCapture) {exit_enabled = false;}
|
||||
EVENT_HANDLER(void, setActive) {setActive(true);}
|
||||
EVENT_HANDLER1(void, setActive, bool, yes);
|
||||
|
||||
EVENT2(keyPressed, char, key, void * , data)
|
||||
|
||||
//! \handlers
|
||||
//! \{
|
||||
|
||||
//! \fn void enableExitCapture(char key = 'Q')
|
||||
//! \brief Enable exit key "key" awaiting
|
||||
|
||||
//! \fn void disableExitCapture()
|
||||
//! \brief Disable exit key awaiting
|
||||
|
||||
//! \fn void setActive(bool yes = true)
|
||||
//! \brief Set keyboard listening is active or not
|
||||
|
||||
//! \}
|
||||
//! \events
|
||||
//! \{
|
||||
|
||||
//! \fn void keyPressed(char key, void * data)
|
||||
//! \brief Raise on key "key" pressed, "data" is custom data
|
||||
|
||||
//! \}
|
||||
|
||||
static bool exiting;
|
||||
|
||||
private:
|
||||
void begin();
|
||||
void run();
|
||||
void end();
|
||||
|
||||
KBFunc ret_func;
|
||||
char exit_key;
|
||||
bool exit_enabled, is_active;
|
||||
void * data_;
|
||||
#ifdef WINDOWS
|
||||
DWORD ret, rc;
|
||||
void * hIn;
|
||||
DWORD smode, tmode;
|
||||
#else
|
||||
int rc;
|
||||
int ret;
|
||||
struct termios sterm, tterm;
|
||||
#endif
|
||||
|
||||
};
|
||||
|
||||
#endif // PIKBDLISTENER_H
|
||||
@@ -1,208 +0,0 @@
|
||||
/*
|
||||
PIP - Platform Independent Primitives
|
||||
Console output/input
|
||||
Copyright (C) 2014 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "piscreen.h"
|
||||
|
||||
|
||||
/** \class PIScreen
|
||||
* \brief Console output class
|
||||
* \details
|
||||
* \section PIScreen_sec0 Synopsis
|
||||
* This class provides output to console with automatic alignment and update.
|
||||
* It supports tabs, keyboard listening, formats and colors.
|
||||
*
|
||||
* \section PIScreen_sec1 Layout
|
||||
* %PIScreen 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 PIScreen_sec2 Keyboard usage
|
||||
* %PIScreen should to be single in application. %PIScreen 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__;
|
||||
|
||||
|
||||
PIScreen::SystemConsole::SystemConsole() {
|
||||
width = height = pwidth = pheight = 0;
|
||||
int w, h;
|
||||
#ifdef WINDOWS
|
||||
ulcoord.X = 0;
|
||||
hOut = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
GetConsoleScreenBufferInfo(hOut, &sbi);
|
||||
dattr = sbi.wAttributes;
|
||||
w = sbi.srWindow.Right - sbi.srWindow.Left;
|
||||
h = sbi.srWindow.Bottom - sbi.srWindow.Top;
|
||||
ulcoord.Y = sbi.srWindow.Top;
|
||||
GetConsoleMode(hOut, &smode);
|
||||
GetConsoleCursorInfo(hOut, &curinfo);
|
||||
#else
|
||||
winsize ws;
|
||||
ioctl(0, TIOCGWINSZ, &ws);
|
||||
w = ws.ws_col;
|
||||
h = ws.ws_row;
|
||||
#endif
|
||||
resize(w, h);
|
||||
}
|
||||
|
||||
|
||||
PIScreen::SystemConsole::~SystemConsole() {
|
||||
#ifdef WINDOWS
|
||||
SetConsoleMode(hOut, smode);
|
||||
SetConsoleTextAttribute(hOut, dattr);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void PIScreen::SystemConsole::begin() {
|
||||
#ifdef WINDOWS
|
||||
SetConsoleMode(hOut, ENABLE_WRAP_AT_EOL_OUTPUT);
|
||||
GetConsoleScreenBufferInfo(hOut, &sbi);
|
||||
bc.X = 0;
|
||||
bc.Y = 0;
|
||||
#endif
|
||||
clearScreen();
|
||||
hideCursor();
|
||||
}
|
||||
|
||||
|
||||
void PIScreen::SystemConsole::end() {
|
||||
showCursor();
|
||||
}
|
||||
|
||||
|
||||
void PIScreen::SystemConsole::prepare() {
|
||||
int w, h;
|
||||
#ifdef WINDOWS
|
||||
GetConsoleScreenBufferInfo(hOut, &sbi);
|
||||
w = sbi.srWindow.Right - sbi.srWindow.Left + 1;
|
||||
h = sbi.srWindow.Bottom - sbi.srWindow.Top + 1;
|
||||
#else
|
||||
winsize ws;
|
||||
ioctl(0, TIOCGWINSZ, &ws);
|
||||
w = ws.ws_col;
|
||||
h = ws.ws_row;
|
||||
#endif
|
||||
resize(w, h);
|
||||
}
|
||||
|
||||
|
||||
void PIScreen::SystemConsole::resize(int w, int h) {
|
||||
if (w == pwidth && h == pheight) return;
|
||||
width = piMax(w, 0);
|
||||
height = piMax(h, 0);
|
||||
pwidth = width;
|
||||
pheight = height;
|
||||
cells.resize(height);
|
||||
pcells.resize(height);
|
||||
for (int i = 0; i < height; ++i) {
|
||||
cells[i].resize(width);
|
||||
pcells[i].resize(width, Cell(0));
|
||||
}
|
||||
#ifdef WINDOWS
|
||||
bs.X = width;
|
||||
bs.Y = height;
|
||||
chars.resize(width * height);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void PIScreen::SystemConsole::print() {
|
||||
#ifdef WINDOWS
|
||||
static int cnt = 0;
|
||||
for (int i = 0; i < width; ++i)
|
||||
for (int j = 0; j < height; ++j) {
|
||||
int k = j * width + i;
|
||||
chars[k].Char.UnicodeChar = 0;
|
||||
chars[k].Char.AsciiChar = ((cnt + i + j / 3)%11 + '0');
|
||||
chars[k].Attributes = sbi.wAttributes;
|
||||
if (i % 3 == 0) chars[k].Attributes |= BACKGROUND_RED;
|
||||
//if (j % 3 == 1) chars[k].Attributes |= BACKGROUND_GREEN;
|
||||
//if (j % 3 == 2) chars[k].Attributes |= BACKGROUND_BLUE;
|
||||
/*if (i % 3 == 2) chars[k].Attributes |= FOREGROUND_INTENSITY;
|
||||
if (i % 3 == 2) {
|
||||
chars[k].Attributes |= FOREGROUND_INTENSITY;
|
||||
chars[k].Attributes &= ~(FOREGROUND_RED | FOREGROUND_BLUE);
|
||||
}*/
|
||||
}
|
||||
srect = sbi.srWindow;
|
||||
WriteConsoleOutput(hOut, chars.data(), bs, bc, &srect);
|
||||
#else
|
||||
for (int i = 0; i < height; ++i)
|
||||
pcells[i] = cells[i];
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
PIScreen::PIScreen(bool startNow, KBFunc slot): PIThread() {
|
||||
setName("screen");
|
||||
setPriority(piLow);
|
||||
needLockRun(true);
|
||||
ret_func = slot;
|
||||
listener = new PIKbdListener(key_event, this);
|
||||
if (startNow) start();
|
||||
}
|
||||
|
||||
|
||||
PIScreen::~PIScreen() {
|
||||
if (isRunning())
|
||||
stop();
|
||||
delete listener;
|
||||
}
|
||||
|
||||
|
||||
void PIScreen::key_event(char key, void * t) {
|
||||
PIScreen * p = (PIScreen * )t;
|
||||
if (p->ret_func != 0) p->ret_func(key, t);
|
||||
p->keyPressed(key, t);
|
||||
}
|
||||
|
||||
|
||||
void PIScreen::stop(bool clear) {
|
||||
PIThread::stop(true);
|
||||
if (clear) console.clearScreen();
|
||||
fflush(0);
|
||||
}
|
||||
|
||||
|
||||
void PIScreen::begin() {
|
||||
console.begin();
|
||||
}
|
||||
|
||||
|
||||
void PIScreen::run() {
|
||||
console.prepare();
|
||||
console.print();
|
||||
}
|
||||
|
||||
|
||||
void PIScreen::end() {
|
||||
console.end();
|
||||
}
|
||||
@@ -1,194 +0,0 @@
|
||||
/*! \file piscreen.h
|
||||
* \brief Console output class
|
||||
*/
|
||||
/*
|
||||
PIP - Platform Independent Primitives
|
||||
Console output/input
|
||||
Copyright (C) 2014 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef PSCREEN_H
|
||||
#define PSCREEN_H
|
||||
|
||||
#include "pikbdlistener.h"
|
||||
#include "piprotocol.h"
|
||||
#include "pidiagnostics.h"
|
||||
#include "pisystemmonitor.h"
|
||||
#ifndef WINDOWS
|
||||
# include <sys/ioctl.h>
|
||||
# include <fcntl.h>
|
||||
#endif
|
||||
|
||||
|
||||
class PIP_EXPORT PIScreen: public PIThread
|
||||
{
|
||||
PIOBJECT_SUBCLASS(PIScreen, PIThread)
|
||||
public:
|
||||
|
||||
//! Constructs %PIScreen with key handler "slot" and if "startNow" start it
|
||||
PIScreen(bool startNow = true, KBFunc slot = 0);
|
||||
|
||||
~PIScreen();
|
||||
|
||||
//! Color for chars or background
|
||||
enum Color {
|
||||
Default /** Default */,
|
||||
Black /** Black */,
|
||||
Red /** Red */,
|
||||
Green /** Green */,
|
||||
Yellow /** Yellow */,
|
||||
Blue /** Blue */,
|
||||
Magenta /** Magenta */,
|
||||
Cyan /** Cyan */,
|
||||
White /** White */
|
||||
};
|
||||
|
||||
//! Flags for chars
|
||||
enum CharFlag {
|
||||
Bold /** Bold flag */ = 0x1,
|
||||
Blink /** Blink flag */ = 0x2,
|
||||
};
|
||||
|
||||
//! Variables output format
|
||||
enum __Format {
|
||||
Dec /** Decimal base for integers */ = 0x1000000,
|
||||
Hex /** Hexadecimal base for integers */ = 0x2000000,
|
||||
Oct /** Octal base for integers */ = 0x4000000,
|
||||
Bin /** Binary base for integers */ = 0x8000000,
|
||||
Scientific /** Scientific representation of floats */ = 0x10000000,
|
||||
SystemTimeSplit /** PISystemTime split representation (* s, * ns) */ = 0x20000000,
|
||||
SystemTimeSeconds /** PISystemTime seconds representation (*.* s) */ = 0x40000000
|
||||
};
|
||||
|
||||
//! Column labels alignment
|
||||
enum Alignment {
|
||||
Nothing /** No alignment */ ,
|
||||
Left /** Labels align left and variables align left */ ,
|
||||
Right /** Labels align right and variables align left */,
|
||||
Center /** */
|
||||
};
|
||||
|
||||
typedef PIFlags<PIScreen::CharFlag> CharFlags;
|
||||
|
||||
|
||||
//! Directly call function from \a PIKbdListener
|
||||
void enableExitCapture(char key = 'Q') {listener->enableExitCapture(key);}
|
||||
|
||||
//! Directly call function from \a PIKbdListener
|
||||
void disableExitCapture() {listener->disableExitCapture();}
|
||||
|
||||
//! Directly call function from \a PIKbdListener
|
||||
bool exitCaptured() const {return listener->exitCaptured();}
|
||||
|
||||
//! Directly call function from \a PIKbdListener
|
||||
char exitKey() const {return listener->exitKey();}
|
||||
|
||||
|
||||
EVENT_HANDLER0(void, waitForFinish) {WAIT_FOR_EXIT}
|
||||
EVENT_HANDLER0(void, start) {start(false);}
|
||||
EVENT_HANDLER1(void, start, bool, wait) {PIThread::start(40); if (wait) waitForFinish();}
|
||||
EVENT_HANDLER0(void, stop) {stop(false);}
|
||||
EVENT_HANDLER1(void, stop, bool, clear);
|
||||
|
||||
EVENT2(keyPressed, char, key, void * , data)
|
||||
|
||||
//! \handlers
|
||||
//! \{
|
||||
|
||||
//! \fn void waitForFinish()
|
||||
//! \brief block until finished (exit key will be pressed)
|
||||
|
||||
//! \fn void clearVariables(bool clearScreen = true)
|
||||
//! \brief Remove all columns at current tab and if "clearScreen" clear the screen
|
||||
|
||||
//! \fn void start(bool wait = false)
|
||||
//! \brief Start console output and if "wait" block until finished (exit key will be pressed)
|
||||
|
||||
//! \fn void stop(bool clear = false)
|
||||
//! \brief Stop console output and if "clear" clear the screen
|
||||
|
||||
//! \}
|
||||
//! \events
|
||||
//! \{
|
||||
|
||||
//! \fn void keyPressed(char key, void * data)
|
||||
//! \brief Raise on key "key" pressed, "data" is pointer to %PIConsole object
|
||||
|
||||
//! \}
|
||||
|
||||
private:
|
||||
class SystemConsole {
|
||||
public:
|
||||
SystemConsole();
|
||||
~SystemConsole();
|
||||
struct Cell {
|
||||
Cell(char c = ' ') {color_back = color_char = flags = 0; symbol = c;}
|
||||
uchar color_char;
|
||||
uchar color_back;
|
||||
ushort flags;
|
||||
PIChar symbol;
|
||||
};
|
||||
void begin();
|
||||
void end();
|
||||
void prepare();
|
||||
void print();
|
||||
void resize(int w, int h);
|
||||
#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 toUpperLeft() {SetConsoleCursorPosition(hOut, ulcoord);}
|
||||
void moveTo(int x = 0, int y = 0) {ccoord.X = x; ccoord.Y = ulcoord.Y + y; SetConsoleCursorPosition(hOut, ccoord);}
|
||||
void hideCursor() {curinfo.bVisible = false; SetConsoleCursorInfo(hOut, &curinfo);}
|
||||
void showCursor() {curinfo.bVisible = true; SetConsoleCursorInfo(hOut, &curinfo);}
|
||||
void clearScreen() {toUpperLeft(); FillConsoleOutputAttribute(hOut, dattr, width * (height + 1), ulcoord, &written);
|
||||
FillConsoleOutputCharacter(hOut, ' ', width * (height + 1), ulcoord, &written);}
|
||||
void clearScreenLower() {getWinCurCoord(); FillConsoleOutputAttribute(hOut, dattr, width * height - width * ccoord.Y + ccoord.X, ccoord, &written);
|
||||
FillConsoleOutputCharacter(hOut, ' ', width * height - width * ccoord.Y + ccoord.X, ccoord, &written);}
|
||||
#else
|
||||
void toUpperLeft() {printf("\e[H");}
|
||||
void moveTo(int x = 0, int y = 0) {printf("\e[%d;%dH", y, x);}
|
||||
void hideCursor() {printf("\e[?25l");}
|
||||
void showCursor() {printf("\e[?25h");}
|
||||
void clearScreen() {printf("\e[0m\e[H\e[J");}
|
||||
void clearScreenLower() {printf("\e[0m\e[J");}
|
||||
#endif
|
||||
#ifdef WINDOWS
|
||||
void * hOut;
|
||||
CONSOLE_SCREEN_BUFFER_INFO sbi, csbi;
|
||||
CONSOLE_CURSOR_INFO curinfo;
|
||||
COORD ccoord, ulcoord, bs, bc;
|
||||
SMALL_RECT srect;
|
||||
WORD dattr;
|
||||
DWORD smode, written;
|
||||
PIVector<CHAR_INFO> chars;
|
||||
#endif
|
||||
int width, height, pwidth, pheight;
|
||||
PIVector<PIVector<Cell> > cells, pcells, dcells;
|
||||
};
|
||||
|
||||
void begin();
|
||||
void run();
|
||||
void end();
|
||||
static void key_event(char key, void * t);
|
||||
|
||||
SystemConsole console;
|
||||
PIKbdListener * listener;
|
||||
KBFunc ret_func;
|
||||
|
||||
|
||||
};
|
||||
|
||||
#endif // PSCREEN_H
|
||||
Reference in New Issue
Block a user