/*
    PIP - Platform Independent Primitives
    Protocol, input/output channel (COM, UDP)
    Copyright (C) 2016  Ivan Pelipenko peri4ko@gmail.com, Bychkov Andrey wapmobil@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 "piprotocol.h"


/** \class PIProtocol
 * \brief 
 * \details
 * \section PIProtocol_sec0 Synopsis
 * 
 * 
 * 
 * */

 /// DEPRECATED
PIProtocol::PIProtocol(const PIString & config, const PIString & name_, void * recHeaderPtr, int recHeaderSize, void * recDataPtr, int recDataSize, void * sendDataPtr_, int sendDataSize_): PIObject() {
	init();
	protName = name_;
	PIObject::setName(name_);
	PIConfig conf(config, PIIODevice::ReadOnly);
	if (!conf.isOpened()) {
		piCoutObj << "Can`t open \"" << config << "\"!";
		devReceiverState = devSenderState = "Config error";
		return;
	}
	PIConfig::Entry & b(conf.getValue(name_)),
					& rb(b.getValue("receiver")),
					& sb(b.getValue("sender"));
	
	init_receiver(b, rb, config);
	init_sender(b, sb, config);

	headerPtr = (uchar * )recHeaderPtr;
	headerSize = recHeaderSize;
	dataPtr = (uchar * )recDataPtr;
	dataSize = recDataSize;
	sendDataPtr = (uchar * )sendDataPtr_;
	sendDataSize = sendDataSize_;
	packet_ext->setHeader(PIByteArray(recHeaderPtr, recHeaderSize));
	packet_ext->setPayloadSize(recDataSize);
	packet_ext->setPacketSize(recDataSize);
	packet_ext->setSplitMode(PIPacketExtractor::Header);
	bool null_h = (recHeaderPtr == 0 || recHeaderSize == 0), null_d = (recDataPtr == 0 || recDataSize == 0);
	if (null_h && null_d) packet_ext->setSplitMode(PIPacketExtractor::None);
	else {
		if (null_h) packet_ext->setSplitMode(PIPacketExtractor::Size);
	}
}


PIProtocol::~PIProtocol() {
	//cout << "prot " << protName << " delete\n";
	if (history_write_rec) {
		if (history_file_rec.isEmpty()) {
			history_file_rec.close();
			history_file_rec.remove();
		}
		history_file_rec.close();
	}
	if (history_write_send) {
		if (history_file_send.isEmpty()) {
			history_file_send.close();
			history_file_send.remove();
		}
		history_file_send.close();
	}
	delete diagTimer;
	delete sendTimer;
	delete secTimer;
	delete packet_ext;
	if (eth != 0) delete eth;
	if (ser != 0) delete ser;
}


void PIProtocol::init() {
	packet_ext = new PIPacketExtractor(0, PIPacketExtractor::None);
	packet_ext->setThreadedReadData(this);
	packet_ext->setThreadedReadSlot(receiveEvent);
	packet_ext->setHeaderCheckSlot(headerValidateEvent);
	packet_ext->setName("__S__PIProtocol::packet_ext");
	work = new_mp_prot = history_write_rec = history_write_send = false;
	eth = 0;
	ser = 0;
	ret_func = 0;
	mp_owner = 0;
	net_diag = PIProtocol::Unknown;
	cur_pckt = 0;
	packets[0] = packets[1] = pckt_cnt = pckt_cnt_max = 0;
	diagTimer = 0;
	timeout_ = 3.f;
	sendTimer = new PITimer(sendEvent, this);
	diagTimer = new PITimer(diagEvent, this);
	secTimer = new PITimer(secEvent, this);
	sendTimer->setName("__S__PIProtocol::sendTimer");
	diagTimer->setName("__S__PIProtocol::diagTimer");
	secTimer->setName("__S__PIProtocol::secTimer");
	wrong_count = receive_count = send_count = missed_count = 0;
	packets_in_sec = packets_out_sec = bytes_in_sec = bytes_out_sec = 0;
	immediate_freq = integral_freq = ifreq = 0.f;
	headerPtr = dataPtr = sendDataPtr = 0;
	headerSize = dataSize = sendDataSize = 0;
	type_rec = type_send = PIProtocol::None;
	devSenderState = devReceiverState = "Unknown";
	devSenderName = devReceiverName = "no device";
	history_rsize_rec = history_rsize_send = "no file";
	history_file_rec.setName("__S__PIProtocol::history_file_rec");
	history_file_send.setName("__S__PIProtocol::history_file_send");
	secTimer->start(1000.);
	/*addEvent("receiver started");
	addEvent("receiver stopped");
	addEvent("sender started");
	addEvent("sender stopped");
	addEvent<bool>("received");
	addEvent<PIProtocol::Quality>("quality changed");
	addEventHandler<float>(HANDLER(PIProtocol, startReceive));
	addEventHandler<float>(HANDLER(PIProtocol, startSend));
	addEventHandler(HANDLER(PIProtocol, start));
	addEventHandler(HANDLER(PIProtocol, stopReceive));
	addEventHandler(HANDLER(PIProtocol, stopSend));
	addEventHandler(HANDLER(PIProtocol, stop));*/
}


void PIProtocol::init_sender(PIConfig::Entry & b, PIConfig::Entry & sb, const PIString & config) {
	int ps, gps;
	bool ok, gok, flag, gflag, has_dev = false;
	float freq, gfreq;
	PIFlags<PISerial::Parameters> pp(0);
	PIString dev, gdev;
	
	if (sb.isEntryExists("ip") && sb.isEntryExists("device")) {
		piCoutObj << "Ambiguous sender type in \"" << config << "\"!";
		devSenderState = "Config error";
		return;
	}
	dev = sb.getValue("ip", "", &ok);
	gdev = b.getValue("ip", "", &gok);
	has_dev = false;
	if (ok || gok) {
		if (gok && !ok) dev = gdev;
		if (gok && ok && (dev != gdev)) {
			piCoutObj << "Ambiguous sender type in \"" << config << "\"!";
			devSenderState = "Config error";
			return;
		}
		ps = sb.getValue("port", 0, &ok);
		gps = b.getValue("port", 0, &gok);
		if (ok || gok) {
			if (gok && !ok) ps = gps;
			if (gok && ok && (ps != gps)) {
				piCoutObj << "Ambiguous send port in \"" << config << "\"!";
				devSenderState = "Config error";
				return;
			}
			type_send = PIProtocol::Ethernet;
			if (eth == 0) eth = new PIEthernet();
			eth->setName("__S__PIProtocol::eth");
			setSenderAddress(dev, ps);
			//setReceiverAddress(dev, ps);
			has_dev = true;
			flag = sb.getValue("reconnectEnabled", true, &ok);
			gflag = b.getValue("reconnectEnabled", true, &gok);
			if (ok || gok) {
				if (gok && !ok) flag = gflag;
				if (gok && ok && (flag != gflag)) {
					piCoutObj << "Ambiguous \"reconnectEnabled\" flag in \"" << config << "\"!";
					devReceiverState = "Config error";
					return;
				}
				eth->setReopenEnabled(flag);
			}
			freq = sb.getValue("reconnectTimeout", 1., &ok);
			gfreq = b.getValue("reconnectTimeout", 1., &gok);
			if (ok || gok) {
				if (gok && !ok) freq = gfreq;
				if (gok && ok && (freq != gfreq)) {
					piCoutObj << "Ambiguous \"reconnectTimeout\" value in \"" << config << "\"!";
					devReceiverState = "Config error";
					return;
				}
				eth->setReopenTimeout(freq * 1000);
			}
			/*if (sendDataPtr_ == 0)
				piCoutObj << "Warning: null send data pointer!";
			if (sendDataSize_ == 0)
				piCoutObj << "Warning: null send data size!";*/
		} else {
			piCoutObj << "Can`t find \"" << name() << ".sender.port\" or \"" << name() << ".port\" in \"" << config << "\"!";
			devSenderState = "Config error";
			return;
		}
	}
	dev = sb.getValue("device", "", &ok);
	gdev = b.getValue("device", "", &gok);
	if (ok || gok) {
		if (gok && !ok) dev = gdev;
		if (gok && ok && (dev != gdev)) {
			piCoutObj << "Ambiguous sender type in \"" << config << "\"!";
			devSenderState = "Config error";
			return;
		}
		ps = sb.getValue("speed", 0, &ok);
		gps = b.getValue("speed", 0, &gok);
		if (ok || gok) {
			if (gok && !ok) ps = gps;
			if (gok && ok && (ps != gps)) {
				piCoutObj << "Ambiguous send \"speed\" in \"" << config << "\"!";
				devSenderState = "Config error";
				return;
			}
			flag = sb.getValue("parity", false, &ok);
			gflag = b.getValue("parity", false, &gok);
			if (ok || gok) {
				if (gok && !ok) flag = gflag;
				if (gok && ok && (flag != gflag)) {
					piCoutObj << "Ambiguous send \"parity\" in \"" << config << "\"!";
					devSenderState = "Config error";
					return;
				}
				pp.setFlag(PISerial::ParityControl, flag);
			}
			flag = sb.getValue("twoStopBits", false, &ok);
			gflag = b.getValue("twoStopBits", false, &gok);
			if (ok || gok) {
				if (gok && !ok) flag = gflag;
				if (gok && ok && (flag != gflag)) {
					piCoutObj << "Ambiguous send \"twoStopBits\" parity in \"" << config << "\"!";
					devSenderState = "Config error";
					return;
				}
				pp.setFlag(PISerial::TwoStopBits, flag);
			}
		} else {
			piCoutObj << "Can`t find \"" << name() << ".sender.speed\" or \"" << name() << ".speed\" in \"" << config << "\"!";
			devSenderState = "Config error";
			return;
		}
		type_send = PIProtocol::Serial;
		if (ser == 0) ser = new PISerial(dev);
		ser->setName("__S__PIProtocol::ser");
		setSenderDevice(dev, (PISerial::Speed)ps);
		ser->setOutSpeed((PISerial::Speed)ps);
		ser->setParameters(pp);
		has_dev = true;
		/*if (sendDataPtr_ == 0)
			piCoutObj << "Warning: null send data pointer!";
		if (sendDataSize_ == 0)
			piCoutObj << "Warning: null send data size!";*/
	}
	history_write_send = sb.getValue("writeHistory", false, &ok);
	bool ghist = b.getValue("writeHistory", false, &gok);
	if (ok || gok) {
		if (gok && !ok) history_write_send = ghist;
		if (gok && ok && (history_write_send != ghist)) {
			piCoutObj << "Ambiguous sender history in \"" << config << "\"!";
			devSenderState = "Config error";
			return;
		}
		if (history_write_send) {
			history_path_send = sb.getValue("historyFile", "./history_" + protName + "_send_" +
														PIDate::current().toString("__dd_mm_yyyy_") +
														PITime::current().toString("_hh_mm_ss_")).value();
			history_id_send = sb.getValue("historyID", 0, &ok);
			if (!ok) {
				history_id_send = ushort(protName.toByteArray().checksumPlain32()) + 1;
				piCoutObj << "Warning: no sender history ID defined, write with ID = " << history_id_send;
			}
			history_file_send.open(history_path_send, PIIODevice::WriteOnly);
		}
	}
	freq = sb.getValue("frequency", -1.f, &ok);
	gfreq = b.getValue("frequency", -1.f, &gok);
	if (gok && !ok) freq = gfreq;
	if (gok && ok && (freq != gfreq)) {
		piCoutObj << "Ambiguous sender frequency in \"" << config << "\"!";
		devSenderState = "Config error";
		return;
	}
	if (freq > 0.f && !has_dev)
		piCoutObj << "Warning: no sender device and not null send frequency!";
	setSenderFrequency(freq);
}


void PIProtocol::init_receiver(PIConfig::Entry & b, PIConfig::Entry & rb, const PIString & config) {
	int ps, gps;
	bool ok, gok, flag, gflag, has_dev = false;
	float freq, gfreq;
	PIFlags<PISerial::Parameters> pp(0);
	PIString dev, gdev;

	if (rb.isEntryExists("ip") && rb.isEntryExists("device")) {
		piCoutObj << "Ambiguous receiver type in \"" << config << "\"!";
		devReceiverState = "Config error";
		return;
	}
	dev = rb.getValue("ip", "", &ok);
	gdev = b.getValue("ip", "", &gok);
	if (ok || gok) {
		if (gok && !ok) dev = gdev;
		if (gok && ok && (dev != gdev)) {
			piCoutObj << "Ambiguous receiver type in \"" << config << "\"!";
			devReceiverState = "Config error";
			return;
		}
		ps = rb.getValue("port", 0, &ok);
		gps = b.getValue("port", 0, &gok);
		if (ok || gok) {
			if (gok && !ok) ps = gps;
			if (gok && ok && (ps != gps)) {
				piCoutObj << "Ambiguous receive port in \"" << config << "\"!";
				devReceiverState = "Config error";
				return;
			}
			type_rec = PIProtocol::Ethernet;
			eth = new PIEthernet();
			eth->setName("__S__PIProtocol::eth");
			packet_ext->setDevice(eth);
			//setSenderAddress(dev, ps);
			setReceiverAddress(dev, ps);
			has_dev = true;
			flag = rb.getValue("reconnectEnabled", true, &ok);
			gflag = b.getValue("reconnectEnabled", true, &gok);
			if (ok || gok) {
				if (gok && !ok) flag = gflag;
				if (gok && ok && (flag != gflag)) {
					piCoutObj << "Ambiguous \"reconnectEnabled\" flag in \"" << config << "\"!";
					devReceiverState = "Config error";
					return;
				}
				eth->setReopenEnabled(flag);
			}
			freq = rb.getValue("reconnectTimeout", 1., &ok);
			gfreq = b.getValue("reconnectTimeout", 1., &gok);
			if (ok || gok) {
				if (gok && !ok) freq = gfreq;
				if (gok && ok && (freq != gfreq)) {
					piCoutObj << "Ambiguous \"reconnectTimeout\" value in \"" << config << "\"!";
					devReceiverState = "Config error";
					return;
				}
				eth->setReopenTimeout(freq * 1000);
			}
			/*if (recDataPtr == 0)
				piCoutObj << "Warning: null receive data pointer!";
			if (recDataSize == 0)
				piCoutObj << "Warning: null receive data size!";*/
		} else {
			piCoutObj << "Can`t find \"" << name() << ".receiver.port\" or \"" << name() << ".port\" in \"" << config << "\"!";
			devReceiverState = "Config error";
			return;
		}
	}
	dev = rb.getValue("device", "", &ok);
	gdev = b.getValue("device", "", &gok);
	if (ok || gok) {
		if (gok && !ok) dev = gdev;
		if (gok && ok && (dev != gdev)) {
			piCoutObj << "Ambiguous receiver type in \"" << config << "\"!";
			devReceiverState = "Config error";
			return;
		}
		ps = rb.getValue("speed", 0, &ok);
		gps = b.getValue("speed", 0, &gok);
		if (ok || gok) {
			if (gok && !ok) ps = gps;
			if (gok && ok && (ps != gps)) {
				piCoutObj << "Ambiguous receive \"speed\" in \"" << config << "\"!";
				devReceiverState = "Config error";
				return;
			}
			flag = rb.getValue("parity", false, &ok);
			gflag = b.getValue("parity", false, &gok);
			if (ok || gok) {
				if (gok && !ok) flag = gflag;
				if (gok && ok && (flag != gflag)) {
					piCoutObj << "Ambiguous receive \"parity\" in \"" << config << "\"!";
					devReceiverState = "Config error";
					return;
				}
				pp.setFlag(PISerial::ParityControl, flag);
			}
			flag = rb.getValue("twoStopBits", false, &ok);
			gflag = b.getValue("twoStopBits", false, &gok);
			if (ok || gok) {
				if (gok && !ok) flag = gflag;
				if (gok && ok && (flag != gflag)) {
					piCoutObj << "Ambiguous receive \"twoStopBits\" parity in \"" << config << "\"!";
					devReceiverState = "Config error";
					return;
				}
				pp.setFlag(PISerial::TwoStopBits, flag);
			}
			type_rec = PIProtocol::Serial;
			type_send = PIProtocol::Serial;
			ser = new PISerial(dev);
			ser->setName("__S__PIProtocol::ser");
			packet_ext->setDevice(ser);
			//setSenderDevice(dev, (PISerial::Speed)ps);
			setReceiverDevice(dev, (PISerial::Speed)ps);
			ser->setInSpeed((PISerial::Speed)ps);
			ser->setParameters(pp);
			ps = rb.getValue("vtime", 1, &ok);
			gps = b.getValue("vtime", 1, &gok);
			if (ok || gok) {
				if (gok && !ok) ps = gps;
				if (gok && ok && (ps != gps)) {
					piCoutObj << "Ambiguous receive \"vtime\" in \"" << config << "\"!";
					devReceiverState = "Config error";
					return;
				}
				ser->setVTime(ps);
			}
			has_dev = true;
			/*if (recDataPtr == 0)
				piCoutObj << "Warning: null receive data pointer!";
			if (recDataSize == 0)
				piCoutObj << "Warning: null receive data size!";*/
		} else {
			piCoutObj << "Can`t find \"" << name() << ".receiver.speed\" or \"" << name() << ".speed\" in \"" << config << "\"!";
			devReceiverState = "Config error";
			return;
		}
	}
	history_write_rec = rb.getValue("writeHistory", false, &ok);
	bool ghist = b.getValue("writeHistory", false, &gok);
	if (ok || gok) {
		if (gok && !ok) history_write_rec = ghist;
		if (gok && ok && (history_write_rec != ghist)) {
			piCoutObj << "Ambiguous receiver history in \"" << config << "\"!";
			devReceiverState = "Config error";
			return;
		}
		if (history_write_rec) {
			history_path_rec = rb.getValue("historyFile", "./history_" + protName + "_rec_" +
														PIDate::current().toString("__dd_mm_yyyy_") +
														PITime::current().toString("_hh_mm_ss_")).value();
			history_id_rec = rb.getValue("historyID", 0, &ok);
			if (!ok) {
				history_id_rec = ushort(protName.toByteArray().checksumPlain32());
				piCoutObj << "Warning: no receiver history ID defined, write with ID = " << history_id_rec;
			}
			history_file_rec.open(history_path_rec, PIIODevice::WriteOnly);
		}
	}
	freq = rb.getValue("frequency", -1.f, &ok);
	gfreq = b.getValue("frequency", -1.f, &gok);
	if (gok && !ok) freq = gfreq;
	if (gok && ok && (freq != gfreq)) {
		piCoutObj << "Ambiguous expected frequency in \"" << config << "\"!";
		devReceiverState = "Config error";
		return;
	}
	if (freq > 0.f && !has_dev)
		piCoutObj << "Warning: no receiver device and not null expected frequency!";
	float tm = b.getValue("disconnectTimeout", 3.f);
	if (tm <= 0.f)
		piCoutObj << "Warning: diconnect timeout <= 0 s!";
	timeout_ = (tm < 0.f) ? 0.f : tm;
	setExpectedFrequency(freq);
}


void PIProtocol::setReceiverDevice(const PIString & device, PISerial::Speed speed, bool force) {
	if (force) {
		type_send = type_rec = PIProtocol::Serial;
		if (ser == 0) {
			ser = new PISerial();
			ser->setName("__S__PIProtocol::ser");
			packet_ext->setDevice(ser);
		}
	}
	if (type_rec == PIProtocol::Serial && ser != 0) {
		ser->setDevice(device);
		ser->setSpeed(speed);
		devReceiverName = device;
		devSenderName = device;
	}
}


void PIProtocol::setReceiverAddress(const PIString & ip, int port, bool force) {
	if (force) {
		type_rec = PIProtocol::Ethernet;
		if (eth == 0) {
			eth = new PIEthernet();
			eth->setName("__S__PIProtocol::eth");
			packet_ext->setDevice(eth);
		}
	}
	if (type_rec == PIProtocol::Ethernet && eth != 0) {
		eth->setReadAddress(ip, port);
		if (ip.trimmed().isEmpty()) devReceiverName = "no ip";
		else devReceiverName = ip + ":" + PIString::fromNumber(port);
	}
}


void PIProtocol::setSenderDevice(const PIString & device, PISerial::Speed speed, bool force) {
	if (force) {
		type_send = type_rec = PIProtocol::Serial;
		if (ser == 0) ser = new PISerial();
		ser->setName("__S__PIProtocol::ser");
	}
	if (type_send == PIProtocol::Serial && ser != 0) {
		ser->setDevice(device);
		ser->setSpeed(speed);
		ser->open();
		devSenderName = device;
	}
}


void PIProtocol::setSenderAddress(const PIString & ip, int port, bool force) {
	if (force) {
		type_send = PIProtocol::Ethernet;
		if (eth == 0) eth = new PIEthernet();
		eth->setName("__S__PIProtocol::eth");
	}
	if (type_send == PIProtocol::Ethernet && eth != 0) {
		eth->setSendAddress(ip, port);
		if (ip.isEmpty()) devSenderName = "no ip";
		else devSenderName = ip + ":" + PIString::fromNumber(port);
	}
}


void PIProtocol::setSenderIP(const PIString & ip, bool force) {
	if (force) {
		type_send = PIProtocol::Ethernet;
		if (eth == 0) eth = new PIEthernet();
	}
	if (type_send == PIProtocol::Ethernet && eth != 0) {
		eth->setSendIP(ip);
		if (ip.isEmpty()) devSenderName = "no ip";
		else devSenderName = ip + ":" + PIString::fromNumber(eth->sendPort());
	}
}


void PIProtocol::setSenderPort(int port, bool force) {
	if (force) {
		type_send = PIProtocol::Ethernet;
		if (eth == 0) eth = new PIEthernet();
		eth->setName("__S__PIProtocol::eth");
	}
	if (type_send == PIProtocol::Ethernet && eth != 0) {
		eth->setSendPort(port);
		if (eth->sendIP().isEmpty()) devSenderName = "no ip";
		else devSenderName = eth->sendIP() + ":" + PIString::fromNumber(port);
	}
}


void PIProtocol::setExpectedFrequency(float frequency) {
	exp_freq = frequency;
	changeDisconnectTimeout();
}


void PIProtocol::changeDisconnectTimeout() {
	pckt_cnt_max = int(round(timeout_ * exp_freq));
	if (pckt_cnt_max < 3) pckt_cnt_max = 3;
	last_packets.resize(pckt_cnt_max);
}


void PIProtocol::startReceive(float exp_frequency) {
	if (exp_frequency > 0.f) exp_freq = exp_frequency;
	//if (type_rec == PIProtocol::Serial) ser->start();
	//if (type_rec == PIProtocol::Ethernet) eth->start();
	packet_ext->startThreadedRead();
	msleep(1);
	check_state();
	if (exp_freq <= 0.f) return;
	setExpectedFrequency(exp_freq);
	diagTimer->start(1000. / exp_freq);
	diag_tm.reset();
	receiverStarted();
}


void PIProtocol::startSend(float frequency) {
	//cout << "** start send " << send_freq << ", " << frequency << endl;
	if (frequency > 0.f) send_freq = frequency;
	msleep(1);
	check_state();
	if (send_freq <= 0.f) return;
	sendTimer->start(1000. / send_freq);
	diag_tm.reset();
	senderStarted();
}


void PIProtocol::stopReceive() {
	//if (type_rec == PIProtocol::Serial) ser->stop();
	//if (type_rec == PIProtocol::Ethernet) eth->stop();
	packet_ext->stop();
	diagTimer->stop();
	receiverStopped();
}


bool PIProtocol::receiveEvent(void * t, uchar * data, int size) {
	PIProtocol * p = (PIProtocol * )t;
	if (!p->receive(data, size)) return false;
	p->work = true;
	//p->lock();
	if (p->validate()) {
		if (p->history_write_rec) {
			p->history_file_rec.writeToBinLog(p->history_id_rec, data, size);
			p->history_rsize_rec.setReadableSize(p->history_file_rec.pos());
		}
		p->received(true);
		//p->unlock();
		p->ifreq = p->diag_tm.elapsed_m();
		if (p->ifreq > 0.) p->ifreq = 1000. / p->ifreq;
		p->diag_tm.reset();
		p->receive_count++;
		p->packets_in_sec++;
		p->bytes_in_sec += size;
		p->cur_pckt = 1;
		if (p->ret_func != 0) p->ret_func(p);
		if (p->mp_owner != 0) PIMultiProtocolBase::receiveEvent(p->mp_owner, p, true, data, size);
		return true;
	}
	p->received(false);
	//p->unlock();
	p->wrong_count++;
	if (p->mp_owner != 0) PIMultiProtocolBase::receiveEvent(p->mp_owner, p, false, data, size);
	return false;
}


void PIProtocol::diagEvent(void * t, int) {
	PIProtocol * p = (PIProtocol * )t;
	p->calc_freq();
	p->calc_diag();
	p->check_state();
	if (p->ser != 0) p->missed_count = p->packet_ext->missedPackets();
}


void PIProtocol::secEvent(void * t, int ) {
	PIProtocol * p = (PIProtocol * )t;
	p->speedIn = PIString::readableSize(p->bytes_in_sec) + "/s";
	p->speedOut = PIString::readableSize(p->bytes_out_sec) + "/s";
	p->bytes_in_sec = p->bytes_out_sec = p->packets_in_sec = p->packets_out_sec = 0;
	if (p->ser != 0) p->missed_count = p->packet_ext->missedPackets();
}


void PIProtocol::calc_diag() {
	PIProtocol::Quality diag;
	if (!work) {
		diag = PIProtocol::Unknown;
		return;
	}
	if (pckt_cnt < pckt_cnt_max) {
		last_packets[pckt_cnt] = cur_pckt;
		pckt_cnt++;
	} else {
		packets[(int)last_packets.back()]--;
		if (!last_packets.isEmpty()) last_packets.pop_back();
		last_packets.push_front(cur_pckt);
	}
	packets[(int)cur_pckt]++;
	cur_pckt = 0;
	float good_percents;
	good_percents = (float)packets[1] / pckt_cnt * 100.f;
	if (good_percents == 0.f) diag = PIProtocol::Failure;
	else if (good_percents <= 20.f) diag = PIProtocol::Bad;
	else if (good_percents > 20.f && good_percents <= 80.f) diag = PIProtocol::Average;
	else diag = PIProtocol::Good;
	if (diag != net_diag) {
		qualityChanged(diag, net_diag);
		net_diag = diag;
	}
}


void PIProtocol::calc_freq() {
	float tf;// = float(1000.f / diagTimer->elapsed_m());
	tf = immediate_freq = ifreq;
	ifreq = 0.f;
	if (last_freq.size_s() >= pckt_cnt_max && last_freq.size_s() > 0) last_freq.pop_front();
	last_freq.push_back(tf);
	tf = last_freq[0];
	for (uint i = 1; i < last_freq.size(); ++i)
		tf += last_freq[i];
	integral_freq = tf / last_freq.size();
}


void PIProtocol::check_state() {
	if (type_rec == PIProtocol::Serial) {
		if (ser != 0) {
			if (ser->isOpened()) devReceiverState = "Opened";
			else devReceiverState = "Not opened";
		}
		else devReceiverState = "Not exists";
	}
	if (type_rec == PIProtocol::Ethernet) {
		if (eth != 0) {
			if (eth->isOpened()) devReceiverState = "Opened";
			else devReceiverState = "Not opened";
		}
		else devReceiverState = "Not exists";
	}
	if (type_send == PIProtocol::Serial) {
		if (ser != 0) {
			if (ser->isOpened()) devSenderState = "Opened";
			else devSenderState = "Not opened";
		}
		else devSenderState = "Not exists";
	}
	if (type_send == PIProtocol::Ethernet) {
		if (eth != 0) {
			if (eth->isOpened()) devSenderState = "Opened";
			else devSenderState = "Not opened";
		}
		else devSenderState = "Not exists";
	}
}


void PIProtocol::send(const void * data, int size, bool direct) {
	if (!direct) {
		if (data == 0 || size == 0) return;
		if (!aboutSend()) return;
	}
	if (history_write_send) {
		history_file_send.writeToBinLog(history_id_send, data, size);
		history_rsize_send.setReadableSize(history_file_send.pos());
	}
	if (type_send == PIProtocol::Serial)
		if (ser->send(data, size)) {
			send_count++;
			packets_out_sec++;
			bytes_out_sec += size;
		}
	if (type_send == PIProtocol::Ethernet)
		if (eth->send(data, size)) {
			send_count++;
			packets_out_sec++;
			bytes_out_sec += size;
		}
}


void PIProtocol::send() {
	//lock();
	//memcpy(packet, sendDataPtr, sendDataSize);
	//unlock();
	if (!aboutSend()) return;
	if (sendDataPtr == 0 || sendDataSize == 0) return;
	if (history_write_send) {
		history_file_send.writeToBinLog(history_id_send, sendDataPtr, sendDataSize);
		history_rsize_send.setReadableSize(history_file_send.pos());
	}
	if (type_send == PIProtocol::Serial)
		if (ser->send(sendDataPtr, sendDataSize)) {
			send_count++;
			packets_out_sec++;
			bytes_out_sec += sendDataSize;
		}
	if (type_send == PIProtocol::Ethernet)
		if (eth->send(sendDataPtr, sendDataSize)) {
			send_count++;
			packets_out_sec++;
			bytes_out_sec += sendDataSize;
		}
}
