/* PIP - Platform Independent Primitives Complex I/O point Copyright (C) 2016 Ivan Pelipenko peri4ko@yandex.ru This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "piconnection.h" #include "piconfig.h" /** \class PIConnection * \brief Complex Input/Output point * * \section PIConnection_synopsis Synopsis * %PIConnection provides abstract layer over physical devices, * filtering and connecting data streams. Each %PIConnection * works through Device Pool, so several %PIConnections can * read from single physical device. General scheme: * \image html piconnection.png * * \section PIConnection_pool Device pool concept * Device pool is static object, single for each application, which * contains unique devices. Each %PIConnection works with real devices * through Device pool. Each device has assosiated thread for read * and it can be started or stopped with %PIConnection functions * \a startThreadedRead() and \a stopThreadedRead(). * * \section PIConnection_filters Filters * %PIConnection filter is a PIPacketExtractor and assosiated * array of devices or other filters. When read thread is successfully read * from device this data can be passed to one or more filters. Each filter * has name and filter names should be unique. You can use this name for * access to PIPacketExtractor* with function \a filter(), or get array of * assosiated devices and filters with function \a filterBoundedDevices(). * One filter can receive data from several sources, and can be bounded to * several filters. * \image html piconnection_filters.png * * \section PIConnection_diag Diagnostics * %PIConnection create PIDiagnostics for each device or filter. You can * access to these objects with functions \a diagnostic(). * * \section PIConnection_sender Senders * %PIConnection can send data to devices with named timers ("senders"). * You can create sender or add device to sender with function \a addSender(). * Each sender has internal timer and every tick execute virtual function * \a senderData(). Returns value of this function sended to bounded devices. * You can assign fixed send data to sender with function \a setSenderFixedData(). * In this case sender will NOT execute \a senderData(), but send assigned data. * \image html piconnection_senders.png * * \section PIConnection_config Configuration * You can create %PIConnection from config file section or configure * it later with function \a configureFromConfig(). Devices describes * with its full pathes, for details see \ref PIIODevice_sec7. Example: * \image html piconnection_conf.png * Also %PIConnection can create PIString with its configuration with * function \a makeConfig(). This string can be directly inserted into the * config file. * */ PIVector PIConnection::_connections; PIConnection::PIConnection(const PIString & name): PIObject(name) { _connections << this; } PIConnection::PIConnection(const PIString & config, const PIString & name_): PIObject(name_) { _connections << this; configureFromConfig(config, name_); } PIConnection::PIConnection(PIString * string, const PIString & name_): PIObject(name_) { _connections << this; configureFromString(string, name_); } PIConnection::~PIConnection() { __device_pool__->unboundConnection(this); removeAllFilters(); _connections.removeAll(this); } bool PIConnection::configureFromConfig(const PIString & conf_path, const PIString & name_) { PIConfig conf(conf_path, PIIODevice::ReadOnly); return configure(conf, name_); } bool PIConnection::configureFromString(PIString * string, const PIString & name_) { PIConfig conf(string, PIIODevice::ReadOnly); return configure(conf, name_); } bool PIConnection::configure(PIConfig & conf, const PIString & name_) { if (!conf.isOpened()) return false; __device_pool__->unboundConnection(this); removeAllSenders(); removeAllChannels(); removeAllFilters(); removeAllDevices(); setName(name_); if (name_.isEmpty()) piCoutObj << "Warning, can't configure connection with empty name"; PIConfig::Entry ce(conf.getValue(name_)); PIConfig::Branch db(ce.getValue("device").children()), fb(ce.getValue("filter").children()), cb(ce.getValue("channel").children()), sb(ce.getValue("sender").children()); PIStringList dev_list(ce.getValue("device").value()); PIStringList name_list(ce.getValue("device").name()); piForeachC (PIConfig::Entry * e, db) { dev_list << e->value(); name_list << e->name(); } PIMap dev_aliases; for (int i = 0; i < dev_list.size_s(); ++i) { PIString fn(dev_list[i]); if (fn.isEmpty()) continue; PIString & n(name_list[i]); PIIODevice::DeviceMode dm = PIIODevice::ReadWrite; PIIODevice::splitFullPath(fn, &fn, &dm); //piCout << fn; PIIODevice * dev = addDevice(fn, dm); if (!dev) continue; dev_aliases[n] = fn; device_names[n] = dev; setDeviceName(dev, n); dev->setName(name_ + ".device." + dev_list[i]); PIDiagnostics * diag = diags_.value(dev, 0); if (diag != 0) diag->setDisconnectTimeout(ce.getValue("device." + n + ".disconnectTimeout", diag->disconnectTimeout())); } int added(0), padded(-1), tries(0); bool pdebug = debug(); setDebug(false); PIStringList filter_fails; while (added != padded && tries < 100) { padded = added; added = 0; ++tries; piForeachC (PIConfig::Entry * e, fb) { PIPacketExtractor::SplitMode sm = PIPacketExtractor::None; PIString sms(e->getValue("splitMode").value()); int smi = sms.toInt(); if (smi >= 1 && smi <= 5) sm = (PIPacketExtractor::SplitMode)smi; else { sms = sms.trim().toLowerCase(); if (sms.find("header") >= 0 && sms.find("footer") >= 0) sm = PIPacketExtractor::HeaderAndFooter; else { if (sms.find("header") >= 0) sm = PIPacketExtractor::Header; else { if (sms.find("footer") >= 0) sm = PIPacketExtractor::Footer; else { if (sms.find("time") >= 0) sm = PIPacketExtractor::Timeout; else { if (sms.find("size") >= 0) sm = PIPacketExtractor::Size; } } } } } PIStringList devs(e->value()); PIConfig::Branch db(e->getValue("device").children()); devs << e->getValue("device", "").value(); piForeachC (PIConfig::Entry * e2, db) devs << e2->value(); devs.removeStrings(""); if (devs.isEmpty()) continue; PIString dname = dev_aliases.value(devs.front(), devs.front()); PIPacketExtractor * pe = addFilter(e->name(), dname, sm); if (pe == 0) { if (!filter_fails.contains(dname)) filter_fails << dname; continue; } else { filter_fails.removeAll(dname); } ++added; for (int i = 1; i < devs.size_s(); ++i) { dname = dev_aliases.value(devs[i], devs[i]); if (addFilter(e->name(), dname, sm) != 0) { filter_fails.removeAll(dname); ++added; } else { if (!filter_fails.contains(dname)) filter_fails << dname; } } PIDiagnostics * diag = diags_.value(pe, 0); if (diag != 0) diag->setDisconnectTimeout(e->getValue("disconnectTimeout", diag->disconnectTimeout())); pe->setPayloadSize(e->getValue("payloadSize", pe->payloadSize())); pe->setPacketSize(e->getValue("packetSize", pe->packetSize())); pe->setTimeout(e->getValue("timeout", pe->timeout())); pe->setHeader(PIByteArray::fromString(e->getValue("header", "").value())); pe->setFooter(PIByteArray::fromString(e->getValue("footer", "").value())); } } setDebug(pdebug); piForeachC (PIString & f, filter_fails) piCoutObj << "\"addFilter\" error: no such device \"" << f << "\"!"; piForeachC (PIConfig::Entry * e, cb) { PIString f(e->getValue("from").value()), t(e->getValue("to").value()); addChannel(dev_aliases.value(f, f), dev_aliases.value(t, t)); } piForeachC (PIConfig::Entry * e, sb) { PIStringList devs(e->value()); PIConfig::Branch db(e->getValue("device").children()); devs << e->getValue("device", "").value(); piForeachC (PIConfig::Entry * e2, db) devs << e2->value(); devs.removeStrings(""); if (devs.isEmpty()) continue; float freq = e->getValue("frequency"); piForeachC (PIString & d, devs) addSender(e->name(), dev_aliases.value(d, d), freq); PIByteArray fd(PIByteArray::fromString(e->getValue("fixedData").value())); setSenderFixedData(e->name(), fd); } return true; } PIString PIConnection::makeConfig() const { PIString ret; ret << "[" << name() << "]\n"; PIVector devs(boundedDevices()); int dn(-1); piForeachC (PIIODevice * d, devs) { PIStringList dnl(deviceNames(d)); if (dnl.isEmpty()) dnl << PIString::fromNumber(++dn); piForeachC (PIString & dname, dnl) { ret << "device." << dname << " = " << d->constructFullPath() << " #s\n"; PIDiagnostics * diag = diags_.value(const_cast(d), 0); if (diag != 0) ret << "device." << dname << ".disconnectTimeout = " << diag->disconnectTimeout() << " #f\n"; } } piForeachC (PEPair & f, extractors) { if (f.second == 0) continue; if (f.second->extractor == 0) continue; PIString prefix = "filter." + f.first; for (int i = 0; i < f.second->devices.size_s(); ++i) ret << prefix << ".device." << i << " = " << device_names.key(f.second->devices[i]) << " #s\n"; PIDiagnostics * diag = diags_.value(f.second->extractor, 0); if (diag != 0) ret << prefix << ".disconnectTimeout = " << diag->disconnectTimeout() << " #f\n"; ret << prefix << ".splitMode = "; switch (f.second->extractor->splitMode()) { case PIPacketExtractor::None: ret << "none"; break; case PIPacketExtractor::Header: ret << "header"; break; case PIPacketExtractor::Footer: ret << "footer"; break; case PIPacketExtractor::HeaderAndFooter: ret << "header & footer"; break; case PIPacketExtractor::Size: ret << "size"; break; case PIPacketExtractor::Timeout: ret << "timeout"; break; } ret << " #s\n"; ret << prefix << ".payloadSize = " << f.second->extractor->payloadSize() << " #n\n"; ret << prefix << ".packetSize = " << f.second->extractor->packetSize() << " #n\n"; ret << prefix << ".timeout = " << f.second->extractor->timeout() << " #f\n"; ret << prefix << ".header = " << f.second->extractor->header().toString() << " #s\n"; ret << prefix << ".footer = " << f.second->extractor->footer().toString() << " #s\n"; } dn = 0; piForeachC (CPair & c, channels_) { piForeachC (PIIODevice * d, c.second) { PIString prefix = "channel." + PIString::fromNumber(dn); ++dn; ret << prefix << ".from = " << devPath(c.first) << " #s\n"; ret << prefix << ".to = " << devPath(d) << " #s\n"; } } piForeachC (SPair & s, senders) { if (s.second == 0) continue; PIString prefix = "sender." + s.second->name(); for (int i = 0; i < s.second->devices.size_s(); ++i) ret << prefix << ".device." << i << " = " << devPath(s.second->devices[i]) << " #s\n"; double int_ = s.second->int_; if (int_ > 0.) ret << prefix << ".frequency = " << (1000. / int_) << " #f\n"; if (!s.second->sdata.isEmpty()) ret << prefix << ".fixedData = " << s.second->sdata.toString() << " #s\n"; } ret << "[]\n"; return ret; } PIIODevice * PIConnection::addDevice(const PIString & full_path, PIIODevice::DeviceMode mode, bool start) { PIString fp(PIIODevice::normalizeFullPath(full_path)); PIIODevice * dev = __device_pool__->addDevice(this, fp, mode, start); if (dev) { dev->setName(name() + ".device." + fp); device_modes[dev] = mode; __device_pool__->lock(); if (diags_.value(dev, 0) == 0) { PIDiagnostics * d = new PIDiagnostics(false); d->setInterval(10.); diags_[dev] = d; CONNECTU(d, qualityChanged, this, diagQualityChanged); __device_pool__->init(); } __device_pool__->unlock(); } return dev; } PIStringList PIConnection::deviceNames(const PIIODevice *dev) const { PIStringList ret; piForeachC (DNPair & s, device_names) if (s.second == dev) ret << s.first; return ret; } bool PIConnection::removeDevice(const PIString & full_path) { PIString fp(PIIODevice::normalizeFullPath(full_path)); PIIODevice * dev = __device_pool__->device(fp); if (dev == 0) return false; PIStringList dntd(deviceNames(dev)); piForeachC (PIString & n, dntd) device_names.removeOne(n); piForeachC (SPair & s, senders) { if (s.second == 0) continue; s.second->lock(); s.second->devices.removeAll(dev); s.second->unlock(); } device_modes.remove(dev); piForeachC (PEPair & i, extractors) { if (i.second == 0) continue; i.second->devices.removeAll(dev); } bounded_extractors.remove(dev); channels_.remove(dev); for (PIMap >::iterator it = channels_.begin(); it != channels_.end(); ++it) it.value().removeAll(dev); __device_pool__->lock(); if (diags_.value(dev, 0) != 0) delete diags_.value(dev); diags_.remove(dev); __device_pool__->unlock(); return __device_pool__->removeDevice(this, fp); } void PIConnection::removeAllDevices() { device_names.clear(); PIVector bdevs(__device_pool__->boundedDevices(this)); __device_pool__->lock(); piForeach (PIIODevice * d, bdevs) { piForeachC (SPair & s, senders) { if (s.second == 0) continue; s.second->lock(); s.second->devices.removeAll(d); s.second->unlock(); } channels_.remove(d); for (PIMap >::iterator it = channels_.begin(); it != channels_.end(); ++it) it.value().removeAll(d); if (diags_.value(d, 0) != 0) delete diags_.value(d); diags_.remove(d); } __device_pool__->unboundConnection(this); __device_pool__->unlock(); device_modes.clear(); bounded_extractors.clear(); piForeachC (PEPair & i, extractors) { if (i.second == 0) continue; i.second->devices.clear(); } } PIIODevice * PIConnection::deviceByFullPath(const PIString & full_path) const { PIString fp(PIIODevice::normalizeFullPath(full_path)); DevicePool::DeviceData * dd = __device_pool__->devices.value(fp); if (dd == 0) return 0; if (dd->dev == 0) return 0; if (!dd->listeners.contains(const_cast(this))) return 0; return dd->dev; } PIIODevice * PIConnection::deviceByName(const PIString & name) const { return device_names.value(name, 0); } PIVector PIConnection::boundedDevices() const { return __device_pool__->boundedDevices(this); } PIPacketExtractor * PIConnection::addFilter(const PIString & name_, const PIString & full_path, PIPacketExtractor::SplitMode mode) { PIString fp(PIIODevice::normalizeFullPath(full_path)); PIString fname_ = name_.trimmed(); Extractor * e = extractors.value(fname_); if (full_path.isEmpty()) return (e == 0 ? 0 : e->extractor); PIIODevice * dev = deviceByFullPath(fp); PIPacketExtractor * pe(0); if (extractors.value(full_path) != 0) pe = extractors.value(full_path)->extractor; if (pe != 0) dev = pe; if (dev == 0) { piCoutObj << "\"addFilter\" error: no such device \"" << full_path << "\"!"; return 0; } if (e == 0) { e = new Extractor(); extractors[fname_] = e; } if (e->extractor == 0) { e->extractor = new PIPacketExtractor(0, mode); e->extractor->setName(fname_); e->extractor->setThreadedReadData(new PIPair(this, fname_)); e->extractor->setHeaderCheckSlot(filterValidateHeaderS); e->extractor->setFooterCheckSlot(filterValidateFooterS); e->extractor->setPayloadCheckSlot(filterValidatePayloadS); __device_pool__->lock(); if (diags_.value(e->extractor, 0) == 0) { PIDiagnostics * d = new PIDiagnostics(false); d->setInterval(10.); diags_[e->extractor] = d; CONNECTU(d, qualityChanged, this, diagQualityChanged); } __device_pool__->unlock(); CONNECT2(void, uchar * , int, e->extractor, packetReceived, this, packetExtractorReceived) } if (!e->devices.contains(dev)) { bounded_extractors[dev] << e->extractor; //if (PIString(dev->className()) == "PIPacketExtractor") dev->setThreadSafe(false); e->devices << dev; } return e->extractor; } PIPacketExtractor * PIConnection::addFilter(PIPacketExtractor * filter, const PIString & full_path) { PIString fp(PIIODevice::normalizeFullPath(full_path)); Extractor * e = 0; if (full_path.isEmpty()) return (e == 0 ? 0 : e->extractor); PIIODevice * dev = deviceByFullPath(fp); PIPacketExtractor * pe(0); if (extractors.value(full_path) != 0) pe = extractors.value(full_path)->extractor; if (pe != 0) dev = pe; if (dev == 0) { piCoutObj << "\"addFilter\" error: no such device \"" << full_path << "\"!"; return 0; } if (e == 0) { e = new Extractor(); extractors[filter->name()] = e; } if (e->extractor == 0) { e->extractor = filter; e->extractor->setThreadedReadData(new PIPair(this, filter->name())); __device_pool__->lock(); if (diags_.value(e->extractor, 0) == 0) { PIDiagnostics * d = new PIDiagnostics(false); d->setInterval(10.); diags_[e->extractor] = d; CONNECTU(d, qualityChanged, this, diagQualityChanged); } __device_pool__->unlock(); CONNECT2(void, uchar * , int, e->extractor, packetReceived, this, packetExtractorReceived) } if (!e->devices.contains(dev)) { bounded_extractors[dev] << e->extractor; //if (PIString(dev->className()) == "PIPacketExtractor") dev->setThreadSafe(false); e->devices << dev; } return e->extractor; } bool PIConnection::removeFilter(const PIString & name_, const PIString & full_path) { PIString fp(PIIODevice::normalizeFullPath(full_path)); Extractor * p = extractors.value(name_.trimmed()); if (p == 0) return false; bool ret = false; for (int i = 0; i < p->devices.size_s(); ++i) { if (devFPath(p->devices[i]) == fp || devFPath(p->devices[i]) == full_path) { bounded_extractors[p->devices[i]].removeAll(p->extractor); p->devices.remove(i); --i; ret = true; } } if (p->devices.isEmpty()) { unboundExtractor(p->extractor); delete p; } return ret; } bool PIConnection::removeFilter(const PIString & name, const PIIODevice * dev) { if (dev == 0) return false; return removeFilter(name, devFPath(dev)); } bool PIConnection::removeFilter(const PIString & name_) { Extractor * p = extractors.value(name_.trimmed()); if (p == 0) return false; unboundExtractor(p->extractor); delete p; return true; } void PIConnection::removeAllFilters() { __device_pool__->lock(); piForeachC (PEPair & i, extractors) { if (i.second == 0) continue; channels_.remove(i.second->extractor); for (PIMap >::iterator it = channels_.begin(); it != channels_.end(); ++it) it.value().removeAll(i.second->extractor); if (diags_.value(i.second->extractor, 0) != 0) delete diags_.value(i.second->extractor); diags_.remove(i.second->extractor); delete i.second; } extractors.clear(); bounded_extractors.clear(); __device_pool__->unlock(); } PIVector PIConnection::filters() const { PIVector ret; piForeachC (PEPair & i, extractors) if (i.second != 0) if (i.second->extractor != 0) ret << i.second->extractor; return ret; } PIStringList PIConnection::filterNames() const { PIStringList ret; piForeachC (PEPair & i, extractors) if (i.second != 0) if (i.second->extractor != 0) ret << i.first; return ret; } PIPacketExtractor * PIConnection::filter(const PIString & name_) const { PIString fname_ = name_.trimmed(); piForeachC (PEPair & i, extractors) if (i.second != 0) if (i.second->extractor != 0 && i.first == fname_) return i.second->extractor; return 0; } PIVector PIConnection::filterBoundedDevices(const PIString & name_) const { PIVector ret; Extractor * p = extractors.value(name_.trimmed()); if (p == 0) return ret; return p->devices; } bool PIConnection::addChannel(const PIString & name0, const PIString & name1) { //piCout << "addChannel" << name0 << name1; if (name0.isEmpty() || name1.isEmpty()) return false; PIIODevice * dev0 = deviceByFullPath(name0), * dev1 = deviceByFullPath(name1); PIPacketExtractor * pe0(0), * pe1(0); if (extractors.value(name0) != 0) pe0 = extractors.value(name0)->extractor; if (extractors.value(name1) != 0) pe1 = extractors.value(name1)->extractor; if (pe0 != 0) dev0 = pe0; if (pe1 != 0) dev1 = pe1; if (dev0 == 0 || dev1 == 0) { if (dev0 == 0) piCoutObj << "\"addChannel\" error: no such device \"" << name0 << "\"!"; if (dev1 == 0) piCoutObj << "\"addChannel\" error: no such device \"" << name1 << "\"!"; return false; } if (!channels_[dev0].contains(dev1)) channels_[dev0] << dev1; return true; } bool PIConnection::removeChannel(const PIString & name0, const PIString & name1) { PIIODevice * dev0 = deviceByFullPath(name0), * dev1 = deviceByFullPath(name1); PIPacketExtractor * pe0(0), * pe1(0); if (extractors.value(name0) != 0) pe0 = extractors.value(name0)->extractor; if (extractors.value(name1) != 0) pe1 = extractors.value(name1)->extractor; if (pe0 != 0) dev0 = pe0; if (pe1 != 0) dev1 = pe1; if (dev0 == 0 || dev1 == 0) return false; channels_[dev0].removeAll(dev1); return true; } bool PIConnection::removeChannel(const PIString & name0) { PIIODevice * dev0 = deviceByFullPath(name0); PIPacketExtractor * pe0(0); if (extractors.value(name0) != 0) pe0 = extractors.value(name0)->extractor; if (pe0 != 0) dev0 = pe0; if (dev0 == 0) return false; channels_.remove(dev0); for (PIMap >::iterator it = channels_.begin(); it != channels_.end(); ++it) it.value().removeAll(dev0); return true; } void PIConnection::removeAllChannels() { channels_.clear(); } PIString PIConnection::devPath(const PIIODevice * d) const { if (d == 0) return PIString(); if (strcmp(d->className(), "PIPacketExtractor") == 0) return d->name(); return d->constructFullPath(); } PIString PIConnection::devFPath(const PIIODevice * d) const { if (d == 0) return PIString(); if (d->isPropertyExists("__fullPath__")) return d->property("__fullPath__").toString(); return d->name(); } PIVector > PIConnection::channels() const { PIVector > ret; piForeachC (CPair & i, channels_) { PIString fp0(devFPath(i.first)); piForeachC (PIIODevice * d, i.second) ret << PIPair(fp0, devFPath(d)); } return ret; } void PIConnection::addSender(const PIString & name_, const PIString & full_path, float frequency, bool start_) { PIString fp(PIIODevice::normalizeFullPath(full_path)); PIString fname_ = name_.trimmed(); if (fp.isEmpty() || frequency <= 0.) return; Sender * s = senders.value(fname_); PIIODevice * dev = deviceByFullPath(fp); if (s == 0) { s = new Sender(this); s->setName(fname_); s->int_ = 1000. / frequency; senders[fname_] = s; } if (dev == 0) { piCoutObj << "\"addSender\" error: no such device \"" << full_path << "\"!"; return; } if (!s->isRunning() && start_) { //piCoutObj << name_ << "start" << 1000. / frequency; if (!__device_pool__->fake) s->start(s->int_); } s->lock(); if (!s->devices.contains(dev)) s->devices << dev; s->unlock(); } bool PIConnection::removeSender(const PIString & name, const PIString & full_path) { PIString fp(PIIODevice::normalizeFullPath(full_path)); Sender * s = senders.value(name, 0); PIIODevice * d = deviceByFullPath(fp); if (s == 0 || d == 0) return false; s->lock(); bool ret = s->devices.contains(d); if (ret) s->devices.removeAll(d); s->unlock(); return ret; } bool PIConnection::removeSender(const PIString & name) { Sender * s = senders.value(name, 0); if (s == 0) return false; delete s; senders.remove(name); return true; } bool PIConnection::setSenderFixedData(const PIString & name, const PIByteArray & data) { Sender * s = senders.value(name, 0); if (s == 0) return false; s->lock(); s->sdata = data; s->unlock(); return true; } bool PIConnection::clearSenderFixedData(const PIString & name) { Sender * s = senders.value(name, 0); if (s == 0) return false; s->lock(); s->sdata.clear(); s->unlock(); return true; } PIByteArray PIConnection::senderFixedData(const PIString & name) const { Sender * s = senders.value(name, 0); if (s == 0) return PIByteArray(); return s->sdata; } float PIConnection::senderFrequency(const PIString & name) const { Sender * s = senders.value(name, 0); if (s == 0) return -1.f; double i = s->interval(); if (i == 0.) return 0.f; return 1000. / s->interval(); } void PIConnection::removeAllSenders() { piForeachC (SPair & s, senders) if (s.second != 0) delete s.second; senders.clear(); } void PIConnection::startThreadedRead(const PIString & full_path) { PIString fp(PIIODevice::normalizeFullPath(full_path)); DevicePool::DeviceData * dd = __device_pool__->devices.value(fp, 0); if (dd == 0) return; if (dd->dev == 0) return; if (dd->started || dd->dev->mode() == PIIODevice::WriteOnly) return; if (!__device_pool__->fake) dd->rthread->start(); dd->started = true; } void PIConnection::startAllThreadedReads() { piForeachC (DevicePool::DDPair & d, __device_pool__->devices) startThreadedRead(d.first); } void PIConnection::startSender(const PIString & name) { Sender * s = senders.value(name, 0); if (s == 0) return; if (!s->isRunning() && !__device_pool__->fake) s->start(s->int_); } void PIConnection::startAllSenders() { piForeachC (SPair & s, senders) { if (s.second == 0) continue; if (!s.second->isRunning() && !__device_pool__->fake) s.second->start(s.second->int_); } } void PIConnection::stopThreadedRead(const PIString & full_path) { PIString fp(PIIODevice::normalizeFullPath(full_path)); DevicePool::DeviceData * dd = __device_pool__->devices.value(fp, 0); if (dd == 0) return; if (dd->dev == 0) return; if (!dd->started || dd->dev->mode() == PIIODevice::WriteOnly) return; dd->rthread->stop(); dd->started = false; } void PIConnection::stopAllThreadedReads() { piForeachC (DevicePool::DDPair & d, __device_pool__->devices) stopThreadedRead(d.first); } void PIConnection::stopSender(const PIString & name) { Sender * s = senders.value(name, 0); if (s == 0) return; if (s->isRunning()) s->stop(); } void PIConnection::stopAllSenders() { piForeachC (SPair & s, senders) { if (s.second == 0) continue; if (s.second->isRunning()) s.second->stop(); } } PIDiagnostics * PIConnection::diagnostic(const PIString & full_path_name) const { PIIODevice * dev = deviceByFullPath(full_path_name); if (dev == 0) dev = device_names.value(full_path_name, 0); PIPacketExtractor * pe(0); if (extractors.value(full_path_name) != 0) pe = extractors.value(full_path_name)->extractor; if (pe != 0) dev = pe; if (dev == 0) return 0; return diags_.value(dev, 0); } int PIConnection::writeByFullPath(const PIString & full_path, const PIByteArray & data) { PIString fp = PIIODevice::normalizeFullPath(full_path); PIIODevice * dev = __device_pool__->device(fp); //piCout << "SEND" << full_path << fp; return write(dev, data); } int PIConnection::writeByName(const PIString & name, const PIByteArray & data) { PIIODevice * dev = deviceByName(name); return write(dev, data); } int PIConnection::write(PIIODevice * dev, const PIByteArray & data) { if (dev == 0) { piCoutObj << "Null Device!"; return -1; } if (!dev->isOpened()) return -1; if (!dev->canWrite()) { piCoutObj << "Device \"" << dev->constructFullPath() << "\" can`t write!"; return -1; } int ret = dev->write(data); PIDiagnostics * diag = diags_.value(dev); if (diag != 0 && ret > 0) diag->sended(ret); return ret; } PIVector< PIConnection * > PIConnection::allConnections() { return _connections; } PIVector< PIIODevice * > PIConnection::allDevices() { return __device_pool__->boundedDevices(); } bool PIConnection::setFakeMode(bool yes) { bool ret = isFakeMode(); __device_pool__->fake = yes; return ret; } bool PIConnection::isFakeMode() { return __device_pool__->fake; } PIConnection::DevicePool::DevicePool(): PIThread(false, 10) { setName("PIConnection::DevicePool"); needLockRun(true); fake = false; } void PIConnection::DevicePool::init() { if (!isRunning()) start(10); } PIIODevice * PIConnection::DevicePool::addDevice(PIConnection * parent, const PIString & fp, PIIODevice::DeviceMode mode, bool start) { DeviceData * dd = devices[fp]; int pmode(0); bool need_start = false; if (dd == 0) { dd = new DeviceData(); devices[fp] = dd; } if (dd->dev == 0) { //piCout << "new device" << fp; dd->dev = PIIODevice::createFromFullPath(fp); if (dd->dev == 0) { piCoutObj << "Error: can`t create device \"" << fp << "\"!"; //:" << errorString(); return 0; } dd->dev->setProperty("__fullPath__", fp); } else pmode = dd->dev->mode(); if (!dd->listeners.contains(parent)) dd->listeners << parent; if (pmode == mode || pmode == PIIODevice::ReadWrite) return dd->dev; if ((mode & PIIODevice::ReadOnly) > 0) { if (dd->rthread != 0) { delete dd->rthread; dd->rthread = 0; dd->started = false; } dd->rthread = new PIThread(dd, __DevicePool_threadReadDP); dd->rthread->setName("__S__connection_" + fp + "_read_thread"); need_start = true; pmode |= PIIODevice::ReadOnly; } if ((mode & PIIODevice::WriteOnly) > 0) pmode |= PIIODevice::WriteOnly; if (!fake) { dd->dev->close(); dd->dev->open((PIIODevice::DeviceMode)pmode); } else dd->dev->setMode((PIIODevice::DeviceMode)pmode); if (need_start && start) { if (!fake) dd->rthread->start(); dd->started = true; } return dd->dev; } bool PIConnection::DevicePool::removeDevice(PIConnection * parent, const PIString & fp) { DeviceData * dd = devices.value(fp); if (dd == 0) return false; if (dd->dev == 0) return false; bool ok = dd->listeners.contains(parent); dd->listeners.removeAll(parent); if (dd->listeners.isEmpty()) { delete dd; devices.remove(fp); } return ok; } void PIConnection::DevicePool::unboundConnection(PIConnection * parent) { PIStringList rem; piForeachC (DDPair & i, devices) { if (i.second == 0) { rem << i.first; continue; } i.second->listeners.removeAll(parent); if (i.second->listeners.isEmpty()) rem << i.first; } piForeachC (PIString & i, rem) { DeviceData * dd = devices.value(i); if (dd == 0) continue; delete dd; devices.remove(i); } } PIIODevice * PIConnection::DevicePool::device(const PIString & fp) const { DeviceData * dd = devices.value(fp); if (dd == 0) return 0; return dd->dev; } PIVector PIConnection::DevicePool::boundedConnections() const { PIVector ret; piForeachC (DDPair & i, devices) { if (i.second == 0) continue; ret << i.second->listeners; } for (int i = 0; i < ret.size_s(); ++i) for (int j = i + 1; j < ret.size_s(); ++j) if (ret[i] == ret[j]) { ret.remove(j); --j; } return ret; } PIVector< PIIODevice * > PIConnection::DevicePool::boundedDevices() const { PIVector ret; piForeachC (DDPair & i, devices) { if (i.second == 0) continue; if (i.second->dev == 0) continue; ret << i.second->dev; } return ret; } PIVector PIConnection::DevicePool::boundedDevices(const PIConnection * parent) const { PIVector ret; piForeachC (DDPair & i, devices) { if (i.second == 0) continue; if (i.second->dev == 0) continue; if (i.second->listeners.contains(const_cast(parent))) ret << i.second->dev; } return ret; } PIConnection::DevicePool::DeviceData::~DeviceData() { if (rthread != 0) { rthread->stop(); if (!rthread->waitForFinish(1000)) rthread->terminate(); delete rthread; rthread = 0; } if (dev != 0) { dev->close(); delete dev; dev = 0; } } void PIConnection::DevicePool::run() { PIVector conns(PIConnection::allConnections()); piForeach (PIConnection * c, conns) { piForeachC (PIConnection::DPair & d, c->diags_) { if (d.second == 0) continue; d.second->tick(0, 1); } } } void __DevicePool_threadReadDP(void * ddp) { PIConnection::DevicePool::DeviceData * dd((PIConnection::DevicePool::DeviceData * )ddp); if (dd->dev == 0) {piMSleep(100); return;} if (dd->dev->isClosed()) if (!dd->dev->open()) {piMSleep(dd->dev->reopenTimeout()); return;} PIByteArray ba; ba = dd->dev->read(dd->dev->threadedReadBufferSize()); // dd->dev->threadedRead(ba.data(), ba.size()); if (ba.isEmpty()) {piMSleep(10); return;} dd->dev->threadedRead(ba.data(), ba.size_s()); //piCout << "Readed from" << dd->dev->path() << Hex << ba; __device_pool__->deviceReaded(dd, ba); } void PIConnection::DevicePool::deviceReaded(PIConnection::DevicePool::DeviceData * dd, const PIByteArray & data) { PIString from = dd->dev->property("__fullPath__").toString(); piForeach (PIConnection * ld, dd->listeners) ld->rawReceived(dd->dev, from, data); } bool PIConnection::filterValidateHeaderS(void * c, uchar * src, uchar * rec, int size) { PIPair * p((PIPair * )c); return p->first->filterValidateHeader(p->second, src, rec, size); } bool PIConnection::filterValidateFooterS(void * c, uchar * src, uchar * rec, int size) { PIPair * p((PIPair * )c); return p->first->filterValidateFooter(p->second, src, rec, size); } bool PIConnection::filterValidatePayloadS(void * c, uchar * rec, int size) { PIPair * p((PIPair * )c); return p->first->filterValidatePayload(p->second, rec, size); } void PIConnection::rawReceived(PIIODevice * dev, const PIString & from, const PIByteArray & data) { dataReceived(from, data); dataReceivedEvent(from, data); PIVector be(bounded_extractors.value(dev)); //piCout << be; piForeach (PIPacketExtractor * i, be) i->threadedRead(const_cast(data.data()), data.size_s()); PIVector chd(channels_.value(dev)); piForeach (PIIODevice * d, chd) { int ret = d->write(data); PIDiagnostics * diag = diags_.value(d); if (diag != 0 && ret > 0) diag->sended(ret); } PIDiagnostics * diag = diags_.value(dev); if (diag != 0) diag->received(data.size_s()); } bool PIConnection::filterValidateHeader(const PIString & filter_name, uchar * src, uchar * rec, int size) { for (int i = 0; i < size; ++i) if (src[i] != rec[i]) return false; return true; } bool PIConnection::filterValidateFooter(const PIString & filter_name, uchar * src, uchar * rec, int size) { for (int i = 0; i < size; ++i) if (src[i] != rec[i]) return false; return true; } bool PIConnection::filterValidatePayload(const PIString & filter_name, uchar * rec, int size) { return true; } PIByteArray PIConnection::senderData(const PIString & sender_name) { return PIByteArray(); } PIConnection::Extractor::~Extractor() { if (extractor != 0) { if (extractor->threadedReadData() != 0) delete (PIPair * )(extractor->threadedReadData()); delete extractor; extractor = 0; } } void PIConnection::Sender::tick(void * , int) { if (parent == 0) return; PIByteArray data; if (!sdata.isEmpty()) data = sdata; else data = parent->senderData(name()); if (data.isEmpty()) return; //piCoutObj << "write"<write(data); PIDiagnostics * diag = parent->diags_.value(d); if (diag != 0 && ret > 0) diag->sended(ret); } } void PIConnection::unboundExtractor(PIPacketExtractor * pe) { if (pe == 0) return; channels_.remove(pe); for (PIMap >::iterator it = channels_.begin(); it != channels_.end(); ++it) it.value().removeAll(pe); bounded_extractors.remove(pe); PIVector k = bounded_extractors.keys(); piForeach (PIIODevice * i, k) { PIVector & be(bounded_extractors[i]); be.removeAll(pe); if (be.isEmpty()) bounded_extractors.remove(i); } __device_pool__->lock(); if (diags_.value(pe, 0) != 0) delete diags_.value(pe); diags_.remove(pe); extractors.remove(pe->name()); __device_pool__->unlock(); } void PIConnection::packetExtractorReceived(uchar * data, int size) { PIString from(emitter() == 0 ? "" : emitter()->name()); PIIODevice * cd = (PIIODevice * )emitter(); // piCout << "packetExtractorReceived" << from << cd; if (cd != 0) { PIVector be(bounded_extractors.value(cd)); //piCout << be << (void*)data << size; piForeach (PIPacketExtractor * i, be) i->threadedRead(data, size); PIVector chd(channels_.value(cd)); piForeach (PIIODevice * d, chd) { int ret = d->write(data, size); PIDiagnostics * diag = diags_.value(d); if (diag != 0) diag->sended(ret); } PIDiagnostics * diag = diags_.value(cd); if (diag != 0) diag->received(size); } packetReceived(from, PIByteArray(data, size)); packetReceivedEvent(from, PIByteArray(data, size)); } void PIConnection::diagQualityChanged(PIDiagnostics::Quality new_quality, PIDiagnostics::Quality old_quality) { qualityChanged(diags_.key((PIDiagnostics*)emitter()), new_quality, old_quality); } PIConnection::DevicePool * __device_pool__; bool __DevicePoolContainer__::inited_(false); __DevicePoolContainer__::__DevicePoolContainer__() { if (inited_) return; inited_ = true; __device_pool__ = new PIConnection::DevicePool(); }