Commit cc15c224 authored by Pavel Vainerman's avatar Pavel Vainerman Committed by Pavel Vainerman

[modbusmaster][reload config]: first prototype

parent b16c96bf
/*
* Copyright (c) 2020 Pavel Vainerman.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation, version 2.1.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Lesser Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
// -------------------------------------------------------------------------
#include <cmath>
#include <limits>
#include <sstream>
#include <Exceptions.h>
#include <UniSetTypes.h>
#include <extensions/Extensions.h>
#include <ORepHelpers.h>
#include "MBConfig.h"
#include "modbus/MBLogSugar.h"
// -------------------------------------------------------------------------
namespace uniset
{
// -----------------------------------------------------------------------------
using namespace std;
using namespace uniset::extensions;
// -----------------------------------------------------------------------------
MBConfig::MBConfig(const std::shared_ptr<uniset::Configuration>& _conf
, xmlNode* _cnode
, std::shared_ptr<SMInterface> _shm ):
cnode(_cnode),
conf(_conf),
shm(_shm)
{
}
// -----------------------------------------------------------------------------
MBConfig::~MBConfig()
{
}
// -----------------------------------------------------------------------------
void MBConfig::cloneParams( const std::shared_ptr<MBConfig>& mbconf )
{
s_field = mbconf->s_field;
s_fvalue = mbconf->s_fvalue;
defaultMBtype = mbconf->defaultMBtype;
defaultMBaddr = mbconf->defaultMBaddr;
defaultMBinitOK = mbconf->defaultMBinitOK;
mblog = mbconf->mblog;
myname = mbconf->myname;
noQueryOptimization = mbconf->noQueryOptimization;
maxQueryCount = mbconf->maxQueryCount;
recv_timeout = mbconf->recv_timeout;
default_timeout = mbconf->default_timeout;
aftersend_pause = mbconf->aftersend_pause;
polltime = mbconf->polltime;
sleepPause_msec = mbconf->sleepPause_msec;
prefix = mbconf->prefix;
prop_prefix = mbconf->prop_prefix;
mbregFromID = mbconf->mbregFromID;
}
// -----------------------------------------------------------------------------
void MBConfig::loadConfig( const std::shared_ptr<uniset::UniXML>& xml, UniXML::iterator sensorsSection )
{
readConfiguration(xml, sensorsSection);
if( !noQueryOptimization )
rtuQueryOptimization(devices, maxQueryCount);
initDeviceList(xml);
}
// ------------------------------------------------------------------------------------------
void MBConfig::readConfiguration( const std::shared_ptr<uniset::UniXML>& xml, UniXML::iterator sensorsSection )
{
UniXML::iterator it = sensorsSection;
if( !it.getCurrent() )
{
it = xml->findNode(xml->getFirstNode(), "sensors");
if(!it)
{
ostringstream err;
err << myname << "(readConfiguration): не нашли корневого раздела <sensors>";
throw SystemError(err.str());
}
}
if( !it.goChildren() )
{
mbcrit << myname << "(readConfiguration): раздел <sensors> не содержит секций ?!!\n";
return;
}
for( ; it.getCurrent(); it.goNext() )
{
if( uniset::check_filter(it, s_field, s_fvalue) )
initItem(it);
}
// readconf_ok = true;
}
// ------------------------------------------------------------------------------------------
MBConfig::DeviceType MBConfig::getDeviceType( const std::string& dtype ) noexcept
{
if( dtype.empty() )
return dtUnknown;
if( dtype == "mtr" || dtype == "MTR" )
return dtMTR;
if( dtype == "rtu" || dtype == "RTU" )
return dtRTU;
if( dtype == "rtu188" || dtype == "RTU188" )
return dtRTU188;
return dtUnknown;
}
// -----------------------------------------------------------------------------
void MBConfig::printMap( MBConfig::RTUDeviceMap& m )
{
cout << "devices: " << endl;
for( auto it = m.begin(); it != m.end(); ++it )
cout << " " << *(it->second) << endl;
}
// -----------------------------------------------------------------------------
std::ostream& operator<<( std::ostream& os, MBConfig::RTUDeviceMap& m )
{
os << "devices: " << endl;
for( auto it = m.begin(); it != m.end(); ++it )
os << " " << *(it->second) << endl;
return os;
}
// -----------------------------------------------------------------------------
std::ostream& operator<<( std::ostream& os, MBConfig::RTUDevice& d )
{
os << "addr=" << ModbusRTU::addr2str(d.mbaddr)
<< " type=" << d.dtype
<< " respond_id=" << d.resp_id
<< " respond_timeout=" << d.resp_Delay.getOffDelay()
<< " respond_state=" << d.resp_state
<< " respond_invert=" << d.resp_invert
<< " safeMode=" << (MBConfig::SafeMode)d.safeMode
<< endl;
os << " regs: " << endl;
for( const auto& m : d.pollmap )
{
for( const auto& it : * (m.second) )
os << " " << it.second << endl;
}
return os;
}
// -----------------------------------------------------------------------------
std::ostream& operator<<( std::ostream& os, const MBConfig::RegInfo* r )
{
return os << (*r);
}
// -----------------------------------------------------------------------------
std::ostream& operator<<( std::ostream& os, const MBConfig::RegInfo& r )
{
os << " id=" << r.regID
<< " mbreg=" << ModbusRTU::dat2str(r.mbreg)
<< " mbfunc=" << r.mbfunc
<< " q_num=" << r.q_num
<< " q_count=" << r.q_count
<< " value=" << ModbusRTU::dat2str(r.mbval) << "(" << (int)r.mbval << ")"
<< " mtrType=" << MTR::type2str(r.mtrType)
<< endl;
for( const auto& s : r.slst )
os << " " << s << endl;
return os;
}
// -----------------------------------------------------------------------------
void MBConfig::rtuQueryOptimization( RTUDeviceMap& dm, size_t maxQueryCount )
{
// mbinfo << myname << "(rtuQueryOptimization): optimization..." << endl;
for( const auto& d : dm )
rtuQueryOptimizationForDevice(d.second, maxQueryCount);
}
// -----------------------------------------------------------------------------
void MBConfig::rtuQueryOptimizationForDevice( const std::shared_ptr<RTUDevice>& d, size_t maxQueryCount )
{
// mbinfo << myname << "(rtuQueryOptimizationForDevice): dev addr="
// << ModbusRTU::addr2str(d->mbaddr) << " optimization..." << endl;
for( const auto& m : d->pollmap )
rtuQueryOptimizationForRegMap(m.second, maxQueryCount);
}
// -----------------------------------------------------------------------------
void MBConfig::rtuQueryOptimizationForRegMap( const std::shared_ptr<RegMap>& regmap, size_t maxQueryCount )
{
if( regmap->size() <= 1 )
return;
// Вообще в map они уже лежат в нужном порядке, т.е. функция genRegID() гарантирует
// что регистры идущие подряд с одниковой функцией чтения/записи получат подряд идущие RegID.
// так что оптимтизация это просто нахождение мест где RegID идут не подряд...
for( auto it = regmap->begin(); it != regmap->end(); ++it )
{
auto& beg = it->second;
ModbusRTU::RegID regID = beg->regID;
beg->q_count = 1;
beg->q_num = 0;
++it;
// склеиваем регистры идущие подряд
for( ; it != regmap->end(); ++it )
{
if( (it->second->regID - regID) > 1 )
{
// этот регистр должен войти уже в следующий запрос,
// надо вернуть на шаг обратно..
--it;
break;
}
beg->q_count++;
regID = it->second->regID;
it->second->q_num = beg->q_count - 1;
it->second->q_count = 0;
if( beg->q_count >= maxQueryCount )
break;
}
// Корректировка типа функции, в случае необходимости...
if( beg->q_count > 1 && beg->mbfunc == ModbusRTU::fnWriteOutputSingleRegister )
{
// mbwarn << myname << "(rtuQueryOptimization): "
// << " optimization change func=" << ModbusRTU::fnWriteOutputSingleRegister
// << " <--> func=" << ModbusRTU::fnWriteOutputRegisters
// << " for mbaddr=" << ModbusRTU::addr2str(beg->dev->mbaddr)
// << " mbreg=" << ModbusRTU::dat2str(beg->mbreg);
beg->mbfunc = ModbusRTU::fnWriteOutputRegisters;
}
else if( beg->q_count > 1 && beg->mbfunc == ModbusRTU::fnForceSingleCoil )
{
// mbwarn << myname << "(rtuQueryOptimization): "
// << " optimization change func=" << ModbusRTU::fnForceSingleCoil
// << " <--> func=" << ModbusRTU::fnForceMultipleCoils
// << " for mbaddr=" << ModbusRTU::addr2str(beg->dev->mbaddr)
// << " mbreg=" << ModbusRTU::dat2str(beg->mbreg);
beg->mbfunc = ModbusRTU::fnForceMultipleCoils;
}
// надо до внешнего цикла, где будет ++it
// проверить условие.. (т.к. мы во внутреннем цикле итерировались
if( it == regmap->end() )
break;
}
}
// -----------------------------------------------------------------------------
//std::ostream& operator<<( std::ostream& os, MBConfig::PList& lst )
std::ostream& MBConfig::print_plist( std::ostream& os, const MBConfig::PList& lst )
{
os << "[ ";
for( const auto& p : lst )
os << "(" << p.si.id << ")" << conf->oind->getBaseName(conf->oind->getMapName(p.si.id)) << " ";
os << "]";
return os;
}
// -----------------------------------------------------------------------------
std::shared_ptr<MBConfig::RTUDevice> MBConfig::addDev( RTUDeviceMap& mp, ModbusRTU::ModbusAddr a, UniXML::iterator& xmlit )
{
auto it = mp.find(a);
if( it != mp.end() )
{
string s_dtype(xmlit.getProp(prop_prefix + "mbtype"));
if( s_dtype.empty() )
s_dtype = defaultMBtype;
DeviceType dtype = getDeviceType(s_dtype);
if( it->second->dtype != dtype )
{
mbcrit << myname << "(addDev): OTHER mbtype=" << dtype << " for " << xmlit.getProp("name")
<< " prop='" << prop_prefix + "mbtype" << "'"
<< ". Already used devtype=" << it->second->dtype
<< " for mbaddr=" << ModbusRTU::addr2str(it->second->mbaddr)
<< endl;
return 0;
}
mbinfo << myname << "(addDev): device for addr=" << ModbusRTU::addr2str(a)
<< " already added. Ignore device params for " << xmlit.getProp("name") << " ..." << endl;
return it->second;
}
auto d = make_shared<MBConfig::RTUDevice>();
d->mbaddr = a;
if( !initRTUDevice(d, xmlit) )
{
d.reset();
return 0;
}
mp.insert( std::make_pair(a, d) );
return d;
}
// ------------------------------------------------------------------------------------------
std::shared_ptr<MBConfig::RegInfo> MBConfig::addReg( std::shared_ptr<RegMap>& mp, ModbusRTU::RegID regID, ModbusRTU::ModbusData r,
UniXML::iterator& xmlit, std::shared_ptr<MBConfig::RTUDevice> dev )
{
auto it = mp->find(regID);
if( it != mp->end() )
{
if( !it->second->dev )
{
mbcrit << myname << "(addReg): for " << xmlit.getProp("name")
<< " dev=0!!!! " << endl;
return 0;
}
if( it->second->dev->dtype != dev->dtype )
{
mbcrit << myname << "(addReg): OTHER mbtype=" << dev->dtype << " for " << xmlit.getProp("name")
<< ". Already used devtype=" << it->second->dev->dtype << " for " << it->second->dev << endl;
return 0;
}
mbinfo << myname << "(addReg): reg=" << ModbusRTU::dat2str(r)
<< "(regID=" << regID << ")"
<< " already added for " << (*it->second)
<< " Ignore register params for " << xmlit.getProp("name") << " ..." << endl;
it->second->rit = it;
return it->second;
}
auto ri = make_shared<MBConfig::RegInfo>();
if( !initRegInfo(ri, xmlit, dev) )
return 0;
ri->mbreg = r;
ri->regID = regID;
mp->insert( std::make_pair(regID, ri) );
ri->rit = mp->find(regID);
mbinfo << myname << "(addReg): reg=" << ModbusRTU::dat2str(r) << "(regID=" << regID << ")" << endl;
return ri;
}
// ------------------------------------------------------------------------------------------
MBConfig::RSProperty* MBConfig::addProp( PList& plist, RSProperty&& p )
{
for( auto&& it : plist )
{
if( it.si.id == p.si.id && it.si.node == p.si.node )
return &it;
}
plist.emplace_back( std::move(p) );
auto it = plist.end();
--it;
return &(*it);
}
// ------------------------------------------------------------------------------------------
bool MBConfig::initRSProperty( RSProperty& p, UniXML::iterator& it )
{
if( !IOBase::initItem(&p, it, shm, prop_prefix, false, mblog, myname) )
return false;
// проверяем не пороговый ли это датчик (т.е. не связанный с обменом)
// тогда заносим его в отдельный список
if( p.t_ai != DefaultObjectId )
{
thrlist.emplace_back( std::move(p) );
return true;
}
const string sbit(IOBase::initProp(it, "nbit", prop_prefix, false));
if( !sbit.empty() )
{
p.nbit = uniset::uni_atoi(sbit.c_str());
if( p.nbit < 0 || p.nbit >= ModbusRTU::BitsPerData )
{
mbcrit << myname << "(initRSProperty): BAD nbit=" << (int)p.nbit
<< ". (0 >= nbit < " << ModbusRTU::BitsPerData << ")." << endl;
return false;
}
}
if( p.nbit > 0 &&
( p.stype == UniversalIO::AI ||
p.stype == UniversalIO::AO ) )
{
mbwarn << "(initRSProperty): (ignore) uncorrect param`s nbit!=0(" << p.nbit << ")"
<< " for iotype=" << p.stype << " for " << it.getProp("name") << endl;
}
const string sbyte(IOBase::initProp(it, "nbyte", prop_prefix, false) );
if( !sbyte.empty() )
{
p.nbyte = uniset::uni_atoi(sbyte.c_str());
if( p.nbyte > VTypes::Byte::bsize )
{
mbwarn << myname << "(initRSProperty): BAD nbyte=" << p.nbyte
<< ". (0 >= nbyte < " << VTypes::Byte::bsize << ")." << endl;
return false;
}
}
const string vt( IOBase::initProp(it, "vtype", prop_prefix, false) );
if( vt.empty() )
{
p.rnum = VTypes::wsize(VTypes::vtUnknown);
p.vType = VTypes::vtUnknown;
}
else
{
VTypes::VType v(VTypes::str2type(vt));
if( v == VTypes::vtUnknown )
{
mbcrit << myname << "(initRSProperty): Unknown tcp_vtype='" << vt << "' for "
<< it.getProp("name")
<< endl;
return false;
}
p.vType = v;
p.rnum = VTypes::wsize(v);
}
return true;
}
// ------------------------------------------------------------------------------------------
bool MBConfig::initRegInfo( std::shared_ptr<RegInfo>& r, UniXML::iterator& it, std::shared_ptr<MBConfig::RTUDevice>& dev )
{
r->dev = dev;
r->mbval = IOBase::initIntProp(it, "default", prop_prefix, false);
if( dev->dtype == MBConfig::dtRTU )
{
// mblog.info() << myname << "(initRegInfo): init RTU.."
}
else if( dev->dtype == MBConfig::dtMTR )
{
// only for MTR
if( !initMTRitem(it, r) )
return false;
}
else if( dev->dtype == MBConfig::dtRTU188 )
{
// only for RTU188
if( !initRTU188item(it, r) )
return false;
UniversalIO::IOType t = uniset::getIOType(IOBase::initProp(it, "iotype", prop_prefix, false));
r->mbreg = RTUStorage::getRegister(r->rtuJack, r->rtuChan, t);
r->mbfunc = RTUStorage::getFunction(r->rtuJack, r->rtuChan, t);
// т.к. с RTU188 свой обмен
// mbreg и mbfunc поля не используются
return true;
}
else
{
mbcrit << myname << "(initRegInfo): Unknown mbtype='" << dev->dtype
<< "' for " << it.getProp("name") << endl;
return false;
}
if( mbregFromID )
{
if( it.getProp("id").empty() )
r->mbreg = conf->getSensorID(it.getProp("name"));
else
r->mbreg = it.getIntProp("id");
}
else
{
const string sr( IOBase::initProp(it, "mbreg", prop_prefix, false) );
if( sr.empty() )
{
mbcrit << myname << "(initItem): Unknown 'mbreg' for " << it.getProp("name") << endl;
return false;
}
r->mbreg = ModbusRTU::str2mbData(sr);
}
r->mbfunc = ModbusRTU::fnUnknown;
const string f( IOBase::initProp(it, "mbfunc", prop_prefix, false) );
if( !f.empty() )
{
r->mbfunc = (ModbusRTU::SlaveFunctionCode)uniset::uni_atoi(f.c_str());
if( r->mbfunc == ModbusRTU::fnUnknown )
{
mbcrit << myname << "(initRegInfo): Unknown mbfunc ='" << f
<< "' for " << it.getProp("name") << endl;
return false;
}
}
return true;
}
// ------------------------------------------------------------------------------------------
bool MBConfig::initRTUDevice( std::shared_ptr<RTUDevice>& d, UniXML::iterator& it )
{
string mbtype(IOBase::initProp(it, "mbtype", prop_prefix, false));
if(mbtype.empty())
mbtype = defaultMBtype;
d->dtype = getDeviceType(mbtype);
if( d->dtype == dtUnknown )
{
mbcrit << myname << "(initRTUDevice): Unknown tcp_mbtype='" << mbtype << "'"
<< ". Use: rtu "
<< " for " << it.getProp("name") << endl;
return false;
}
string addr( IOBase::initProp(it, "mbaddr", prop_prefix, false) );
if( addr.empty() )
addr = defaultMBaddr;
if( addr.empty() )
{
mbcrit << myname << "(initRTUDevice): Unknown mbaddr for " << it.getProp("name") << endl;
return false;
}
d->mbaddr = ModbusRTU::str2mbAddr(addr);
if( d->dtype == MBConfig::dtRTU188 )
{
if( !d->rtu188 )
d->rtu188 = make_shared<RTUStorage>(d->mbaddr);
}
return true;
}
// ------------------------------------------------------------------------------------------
bool MBConfig::initItem( UniXML::iterator& it )
{
RSProperty p;
if( !initRSProperty(p, it) )
return false;
if( p.t_ai != DefaultObjectId ) // пороговые датчики в список обмена вносить не надо.
return true;
string addr( IOBase::initProp(it, "mbaddr", prop_prefix, false) );
if( addr.empty() )
addr = defaultMBaddr;
if( addr.empty() )
{
mbcrit << myname << "(initItem): Unknown mbaddr(" << IOBase::initProp(it, "mbaddr", prop_prefix, false) << ")='" << addr << "' for " << it.getProp("name") << endl;
return false;
}
ModbusRTU::ModbusAddr mbaddr = ModbusRTU::str2mbAddr(addr);
auto dev = addDev(devices, mbaddr, it);
if( !dev )
{
mbcrit << myname << "(initItem): " << it.getProp("name") << " CAN`T ADD for polling!" << endl;
return false;
}
ModbusRTU::ModbusData mbreg = 0;
int fn = IOBase::initIntProp(it, "mbfunc", prop_prefix, false);
if( dev->dtype == dtRTU188 )
{
auto r_tmp = make_shared<RegInfo>();
if( !initRTU188item(it, r_tmp) )
{
mbcrit << myname << "(initItem): init RTU188 failed for " << it.getProp("name") << endl;
r_tmp.reset();
return false;
}
mbreg = RTUStorage::getRegister(r_tmp->rtuJack, r_tmp->rtuChan, p.stype);
fn = RTUStorage::getFunction(r_tmp->rtuJack, r_tmp->rtuChan, p.stype);
}
else
{
if( mbregFromID )
mbreg = p.si.id; // conf->getSensorID(it.getProp("name"));
else
{
const string reg( IOBase::initProp(it, "mbreg", prop_prefix, false) );
if( reg.empty() )
{
mbcrit << myname << "(initItem): unknown mbreg(" << prop_prefix << ") for " << it.getProp("name") << endl;
return false;
}
mbreg = ModbusRTU::str2mbData(reg);
}
if( p.nbit != -1 )
{
if( fn == ModbusRTU::fnReadCoilStatus || fn == ModbusRTU::fnReadInputStatus )
{
mbcrit << myname << "(initItem): MISMATCHED CONFIGURATION! nbit=" << (int)p.nbit << " func=" << fn
<< " for " << it.getProp("name") << endl;
return false;
}
}
}
/*! приоритет опроса:
* 1...n - задаёт "частоту" опроса. Т.е. каждые 1...n циклов
*/
size_t pollfactor = IOBase::initIntProp(it, "pollfactor", prop_prefix, false, 0);
std::shared_ptr<RegMap> rmap;
auto rit = dev->pollmap.find(pollfactor);
if( rit == dev->pollmap.end() )
{
rmap = make_shared<RegMap>();
dev->pollmap.emplace(pollfactor, rmap);
}
else
rmap = rit->second;
// формула для вычисления ID
// требования:
// - ID > диапазона возможных регитров
// - разные функции должны давать разный ID
ModbusRTU::RegID rID = ModbusRTU::genRegID(mbreg, fn);
auto ri = addReg(rmap, rID, mbreg, it, dev);
if( dev->dtype == dtMTR )
{
p.rnum = MTR::wsize(ri->mtrType);
if( p.rnum <= 0 )
{
mbcrit << myname << "(initItem): unknown word size for " << it.getProp("name") << endl;
return false;
}
}
if( !ri )
return false;
ri->dev = dev;
// ПРОВЕРКА!
// если функция на запись, то надо проверить
// что один и тотже регистр не перезапишут несколько датчиков
// это возможно только, если они пишут биты!!
// ИТОГ:
// Если для функций записи список датчиков для регистра > 1
// значит в списке могут быть только битовые датчики
// и если идёт попытка внести в список не битовый датчик то ОШИБКА!
// И наоборот: если идёт попытка внести битовый датчик, а в списке
// уже сидит датчик занимающий целый регистр, то тоже ОШИБКА!
if( ModbusRTU::isWriteFunction(ri->mbfunc) )
{
if( p.nbit < 0 && ri->slst.size() > 1 )
{
ostringstream sl;
sl << "[ ";
for( const auto& i : ri->slst )
sl << ORepHelpers::getShortName(conf->oind->getMapName(i.si.id)) << ",";
sl << "]";
ostringstream err;
err << myname << "(initItem): FAILED! Sharing SAVE (not bit saving) to "
<< " tcp_mbreg=" << ModbusRTU::dat2str(ri->mbreg) << "(" << (int)ri->mbreg << ")"
<< " conflict with sensors " << sl.str();
mbcrit << err.str() << endl;
throw uniset::SystemError(err.str());
}
if( p.nbit >= 0 && ri->slst.size() == 1 )
{
auto it2 = ri->slst.begin();
if( it2->nbit < 0 )
{
ostringstream err;
err << myname << "(initItem): FAILED! Sharing SAVE (mbreg="
<< ModbusRTU::dat2str(ri->mbreg) << "(" << (int)ri->mbreg << ") already used)!"
<< " IGNORE --> " << it.getProp("name");
mbcrit << err.str() << endl;
throw uniset::SystemError(err.str());
}
}
// Раз это регистр для записи, то как минимум надо сперва
// инициализировать значением из SM
ri->sm_initOK = IOBase::initIntProp(it, "sm_initOK", prop_prefix, false);
ri->mb_initOK = true;
}
else
{
ri->mb_initOK = defaultMBinitOK;
ri->sm_initOK = false;
}
RSProperty* p1 = addProp(ri->slst, std::move(p) );
if( !p1 )
return false;
p1->reg = ri;
if( p1->rnum > 1 )
{
ri->q_count = p1->rnum;
ri->q_num = 1;
for( auto i = 1; i < p1->rnum; i++ )
{
ModbusRTU::RegID id1 = ModbusRTU::genRegID(mbreg + i, ri->mbfunc);
auto r = addReg(rmap, id1, mbreg + i, it, dev);
r->q_num = i + 1;
r->q_count = 1;
r->mbfunc = ri->mbfunc;
r->mb_initOK = defaultMBinitOK;
r->sm_initOK = false;
if( ModbusRTU::isWriteFunction(ri->mbfunc) )
{
// Если занимает несколько регистров, а указана функция записи "одного",
// то это ошибка..
if( ri->mbfunc != ModbusRTU::fnWriteOutputRegisters &&
ri->mbfunc != ModbusRTU::fnForceMultipleCoils )
{
ostringstream err;
err << myname << "(initItem): Bad write function ='" << ModbusRTU::fnWriteOutputSingleRegister
<< "' for vtype='" << p1->vType << "'"
<< " tcp_mbreg=" << ModbusRTU::dat2str(ri->mbreg)
<< " for " << it.getProp("name");
mbcrit << err.str() << endl;
throw uniset::SystemError(err.str());
}
}
}
}
// Фомируем список инициализации
bool need_init = IOBase::initIntProp(it, "preinit", prop_prefix, false);
if( need_init && ModbusRTU::isWriteFunction(ri->mbfunc) )
{
InitRegInfo ii;
ii.p = std::move(p);
ii.dev = dev;
ii.ri = ri;
const string s_reg(IOBase::initProp(it, "init_mbreg", prop_prefix, false));
if( !s_reg.empty() )
ii.mbreg = ModbusRTU::str2mbData(s_reg);
else
ii.mbreg = ri->mbreg;
string s_mbfunc(it.getProp(prop_prefix + "init_mbfunc"));
if( !s_mbfunc.empty() )
{
ii.mbfunc = (ModbusRTU::SlaveFunctionCode)uniset::uni_atoi(s_mbfunc);
if( ii.mbfunc == ModbusRTU::fnUnknown )
{
mbcrit << myname << "(initItem): Unknown tcp_init_mbfunc ='" << s_mbfunc
<< "' for " << it.getProp("name") << endl;
return false;
}
}
else
{
switch(ri->mbfunc)
{
case ModbusRTU::fnWriteOutputSingleRegister:
ii.mbfunc = ModbusRTU::fnReadOutputRegisters;
break;
case ModbusRTU::fnForceSingleCoil:
ii.mbfunc = ModbusRTU::fnReadCoilStatus;
break;
case ModbusRTU::fnWriteOutputRegisters:
ii.mbfunc = ModbusRTU::fnReadOutputRegisters;
break;
case ModbusRTU::fnForceMultipleCoils:
ii.mbfunc = ModbusRTU::fnReadCoilStatus;
break;
default:
ii.mbfunc = ModbusRTU::fnReadOutputRegisters;
break;
}
}
initRegList.emplace_back( std::move(ii) );
ri->mb_initOK = false;
ri->sm_initOK = false;
}
return true;
}
// ------------------------------------------------------------------------------------------
bool MBConfig::initMTRitem( UniXML::iterator& it, std::shared_ptr<RegInfo>& p )
{
p->mtrType = MTR::str2type(it.getProp(prop_prefix + "mtrtype"));
if( p->mtrType == MTR::mtUnknown )
{
mbcrit << myname << "(readMTRItem): Unknown mtrtype '"
<< it.getProp(prop_prefix + "mtrtype")
<< "' for " << it.getProp("name") << endl;
return false;
}
return true;
}
// ------------------------------------------------------------------------------------------
bool MBConfig::initRTU188item( UniXML::iterator& it, std::shared_ptr<RegInfo>& p )
{
const string jack(IOBase::initProp(it, "jack", prop_prefix, false));
const string chan(IOBase::initProp(it, "channel", prop_prefix, false));
if( jack.empty() )
{
mbcrit << myname << "(readRTU188Item): Unknown " << prop_prefix << "jack='' "
<< " for " << it.getProp("name") << endl;
return false;
}
p->rtuJack = RTUStorage::s2j(jack);
if( p->rtuJack == RTUStorage::nUnknown )
{
mbcrit << myname << "(readRTU188Item): Unknown " << prop_prefix << "jack=" << jack
<< " for " << it.getProp("name") << endl;
return false;
}
if( chan.empty() )
{
mbcrit << myname << "(readRTU188Item): Unknown channel='' "
<< " for " << it.getProp("name") << endl;
return false;
}
p->rtuChan = uniset::uni_atoi(chan);
mblog2 << myname << "(readRTU188Item): add jack='" << jack << "'"
<< " channel='" << p->rtuChan << "'" << endl;
return true;
}
// ------------------------------------------------------------------------------------------
std::ostream& operator<<( std::ostream& os, const MBConfig::DeviceType& dt )
{
switch(dt)
{
case MBConfig::dtRTU:
os << "RTU";
break;
case MBConfig::dtMTR:
os << "MTR";
break;
case MBConfig::dtRTU188:
os << "RTU188";
break;
default:
os << "Unknown device type (" << (int)dt << ")";
break;
}
return os;
}
// -----------------------------------------------------------------------------
std::ostream& operator<<( std::ostream& os, const MBConfig::RSProperty& p )
{
os << " (" << ModbusRTU::dat2str(p.reg->mbreg) << ")"
<< " sid=" << p.si.id
<< " stype=" << p.stype
<< " nbit=" << (int)p.nbit
<< " nbyte=" << p.nbyte
<< " rnum=" << p.rnum
<< " safeval=" << p.safeval
<< " invert=" << p.invert;
if( p.stype == UniversalIO::AI || p.stype == UniversalIO::AO )
{
os << p.cal
<< " cdiagram=" << ( p.cdiagram ? "yes" : "no" );
}
return os;
}
// -----------------------------------------------------------------------------
void MBConfig::initDeviceList(const std::shared_ptr<UniXML>& xml )
{
xmlNode* respNode = 0;
if( xml )
respNode = xml->extFindNode(cnode, 1, 1, "DeviceList");
if( respNode )
{
UniXML::iterator it1(respNode);
if( it1.goChildren() )
{
for(; it1.getCurrent(); it1.goNext() )
{
ModbusRTU::ModbusAddr a = ModbusRTU::str2mbAddr(it1.getProp("addr"));
initDeviceInfo(devices, a, it1);
}
}
else
mbwarn << myname << "(init): <DeviceList> empty section..." << endl;
}
else
mbwarn << myname << "(init): <DeviceList> not found..." << endl;
}
// -----------------------------------------------------------------------------
bool MBConfig::initDeviceInfo( RTUDeviceMap& m, ModbusRTU::ModbusAddr a, UniXML::iterator& it )
{
auto d = m.find(a);
if( d == m.end() )
{
mbwarn << myname << "(initDeviceInfo): not found device for addr=" << ModbusRTU::addr2str(a) << endl;
return false;
}
auto& dev = d->second;
dev->ask_every_reg = it.getIntProp("ask_every_reg");
mbinfo << myname << "(initDeviceInfo): add addr=" << ModbusRTU::addr2str(a)
<< " ask_every_reg=" << dev->ask_every_reg << endl;
string s(it.getProp("respondSensor"));
if( !s.empty() )
{
dev->resp_id = conf->getSensorID(s);
if( dev->resp_id == DefaultObjectId )
{
mbinfo << myname << "(initDeviceInfo): not found ID for respondSensor=" << s << endl;
return false;
}
}
const string mod(it.getProp("modeSensor"));
if( !mod.empty() )
{
dev->mode_id = conf->getSensorID(mod);
if( dev->mode_id == DefaultObjectId )
{
mbcrit << myname << "(initDeviceInfo): not found ID for modeSensor=" << mod << endl;
return false;
}
UniversalIO::IOType m_iotype = conf->getIOType(dev->mode_id);
if( m_iotype != UniversalIO::AI )
{
mbcrit << myname << "(initDeviceInfo): modeSensor='" << mod << "' must be 'AI'" << endl;
return false;
}
}
// сперва проверим не задан ли режим "safemodeResetIfNotRespond"
if( it.getIntProp("safemodeResetIfNotRespond") )
dev->safeMode = MBConfig::safeResetIfNotRespond;
// потом проверим датчик для "safeExternalControl"
const string safemode = it.getProp("safemodeSensor");
if( !safemode.empty() )
{
dev->safemode_id = conf->getSensorID(safemode);
if( dev->safemode_id == DefaultObjectId )
{
mbcrit << myname << "(initDeviceInfo): not found ID for safemodeSensor=" << safemode << endl;
return false;
}
const string safemodeValue(it.getProp("safemodeValue"));
if( !safemodeValue.empty() )
dev->safemode_value = uni_atoi(safemodeValue);
dev->safeMode = MBConfig::safeExternalControl;
}
mbinfo << myname << "(initDeviceInfo): add addr=" << ModbusRTU::addr2str(a) << endl;
int tout = it.getPIntProp("timeout", default_timeout );
dev->resp_Delay.set(0, tout);
dev->resp_invert = it.getIntProp("invert");
dev->resp_force = it.getIntProp("force");
int init_tout = it.getPIntProp("respondInitTimeout", tout);
dev->resp_ptInit.setTiming(init_tout);
s = it.getProp("speed");
if( !s.empty() )
{
d->second->speed = ComPort::getSpeed(s);
if( d->second->speed == ComPort::ComSpeed0 )
{
// d->second->speed = defSpeed;
mbcrit << myname << "(initDeviceInfo): Unknown speed=" << s <<
" for addr=" << ModbusRTU::addr2str(a) << endl;
return false;
}
}
return true;
}
// -----------------------------------------------------------------------------
std::ostream& operator<<( std::ostream& os, const MBConfig::ExchangeMode& em )
{
if( em == MBConfig::emNone )
return os << "emNone";
if( em == MBConfig::emWriteOnly )
return os << "emWriteOnly";
if( em == MBConfig::emReadOnly )
return os << "emReadOnly";
if( em == MBConfig::emSkipSaveToSM )
return os << "emSkipSaveToSM";
if( em == MBConfig::emSkipExchange )
return os << "emSkipExchange";
return os;
}
// -----------------------------------------------------------------------------
std::string to_string( const MBConfig::SafeMode& m )
{
if( m == MBConfig::safeNone )
return "safeNone";
if( m == MBConfig::safeResetIfNotRespond )
return "safeResetIfNotRespond";
if( m == MBConfig::safeExternalControl )
return "safeExternalControl";
return "";
}
ostream& operator<<( ostream& os, const MBConfig::SafeMode& m )
{
return os << to_string(m);
}
// -----------------------------------------------------------------------------
bool MBConfig::RTUDevice::checkRespond( std::shared_ptr<DebugStream>& mblog )
{
bool prev = resp_state;
resp_state = resp_Delay.check( prev_numreply != numreply ) && numreply != 0;
mblog4 << "(checkRespond): addr=" << ModbusRTU::addr2str(mbaddr)
<< " respond_id=" << resp_id
<< " state=" << resp_state
<< " check=" << (prev_numreply != numreply)
<< " delay_check=" << resp_Delay.get()
<< " [ timeout=" << resp_Delay.getOffDelay()
<< " numreply=" << numreply
<< " prev_numreply=" << prev_numreply
<< " resp_ptInit=" << resp_ptInit.checkTime()
<< " ]"
<< endl;
// если только что прошла "инициализация" возвращаем true
// чтобы датчик в SM обновился..
if( trInitOK.hi(resp_ptInit.checkTime()) )
return true;
return ((prev != resp_state || resp_force ) && trInitOK.get() );
}
// -----------------------------------------------------------------------------
std::string MBConfig::RTUDevice::getShortInfo() const
{
ostringstream s;
s << "mbaddr=" << ModbusRTU::addr2str(mbaddr) << ":"
<< " resp_state=" << resp_state
<< " (resp_id=" << resp_id << " resp_force=" << resp_force
<< " resp_invert=" << resp_invert
<< " numreply=" << numreply
<< " timeout=" << resp_Delay.getOffDelay()
<< " type=" << dtype
<< " ask_every_reg=" << ask_every_reg
<< ")" << endl;
return s.str();
}
// -----------------------------------------------------------------------------
} // end of namespace uniset
/*
* Copyright (c) 2020 Pavel Vainerman.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation, version 2.1.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Lesser Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
// -----------------------------------------------------------------------------
#ifndef _MBConfig_H_
#define _MBConfig_H_
// -----------------------------------------------------------------------------
#include <ostream>
#include <string>
#include <map>
#include <unordered_map>
#include <memory>
#include "IONotifyController.h"
#include "Calibration.h"
#include "DelayTimer.h"
#include "SMInterface.h"
#include "SharedMemory.h"
#include "IOBase.h"
#include "VTypes.h"
#include "MTR.h"
#include "RTUStorage.h"
#include "modbus/ModbusClient.h"
#include "Configuration.h"
// -------------------------------------------------------------------------
namespace uniset
{
// -----------------------------------------------------------------------------
/*! Конфигурация для ModbusMaster */
class MBConfig
{
public:
MBConfig(const std::shared_ptr<uniset::Configuration>& conf
, xmlNode* cnode
, std::shared_ptr<SMInterface> _shm );
~MBConfig();
/*! Режимы работы процесса обмена */
enum ExchangeMode
{
emNone = 0, /*!< нормальная работа (по умолчанию) */
emWriteOnly = 1, /*!< "только посылка данных" (работают только write-функции) */
emReadOnly = 2, /*!< "только чтение" (работают только read-функции) */
emSkipSaveToSM = 3, /*!< не писать данные в SM (при этом работают и read и write функции) */
emSkipExchange = 4 /*!< отключить обмен */
};
friend std::ostream& operator<<( std::ostream& os, const ExchangeMode& em );
/*! Режимы работы процесса обмена */
enum SafeMode
{
safeNone = 0, /*!< не использовать безопасный режим (по умолчанию) */
safeResetIfNotRespond = 1, /*!< выставлять безопасное значение, если пропала связь с устройством */
safeExternalControl = 2 /*!< управление сбросом по внешнему датчику */
};
friend std::string to_string( const SafeMode& m );
friend std::ostream& operator<<( std::ostream& os, const SafeMode& m );
enum DeviceType
{
dtUnknown, /*!< неизвестный */
dtRTU, /*!< RTU (default) */
dtMTR, /*!< MTR (DEIF) */
dtRTU188 /*!< RTU188 (Fastwell) */
};
static DeviceType getDeviceType( const std::string& dtype ) noexcept;
friend std::ostream& operator<<( std::ostream& os, const DeviceType& dt );
struct RTUDevice;
struct RegInfo;
struct RSProperty:
public IOBase
{
// only for RTU
int8_t nbit; /*!< bit number (-1 - not used) */
VTypes::VType vType; /*!< type of value */
uint16_t rnum; /*!< count of registers */
uint8_t nbyte; /*!< byte number (1-2) */
RSProperty():
nbit(-1), vType(VTypes::vtUnknown),
rnum(VTypes::wsize(VTypes::vtUnknown)),
nbyte(0)
{}
// т.к. IOBase содержит rwmutex с запрещённым конструктором копирования
// приходится здесь тоже объявлять разрешенными только операции "перемещения"
RSProperty( const RSProperty& r ) = delete;
RSProperty& operator=(const RSProperty& r) = delete;
RSProperty( RSProperty&& r ) = default;
RSProperty& operator=(RSProperty&& r) = default;
std::shared_ptr<RegInfo> reg;
};
friend std::ostream& operator<<( std::ostream& os, const RSProperty& p );
typedef std::list<RSProperty> PList;
std::ostream& print_plist( std::ostream& os, const PList& p );
typedef std::map<ModbusRTU::RegID, std::shared_ptr<RegInfo>> RegMap;
struct RegInfo
{
// т.к. RSProperty содержит rwmutex с запрещённым конструктором копирования
// приходится здесь тоже объявлять разрешенными только операции "перемещения"
RegInfo( const RegInfo& r ) = default;
RegInfo& operator=(const RegInfo& r) = delete;
RegInfo( RegInfo&& r ) = delete;
RegInfo& operator=(RegInfo&& r) = default;
RegInfo() = default;
ModbusRTU::ModbusData mbval = { 0 };
ModbusRTU::ModbusData mbreg = { 0 }; /*!< регистр */
ModbusRTU::SlaveFunctionCode mbfunc = { ModbusRTU::fnUnknown }; /*!< функция для чтения/записи */
PList slst;
ModbusRTU::RegID regID = { 0 };
std::shared_ptr<RTUDevice> dev;
// only for RTU188
RTUStorage::RTUJack rtuJack = { RTUStorage::nUnknown };
int rtuChan = { 0 };
// only for MTR
MTR::MTRType mtrType = { MTR::mtUnknown }; /*!< тип регистра (согласно спецификации на MTR) */
// optimization
size_t q_num = { 0 }; /*!< number in query */
size_t q_count = { 1 }; /*!< count registers for query */
RegMap::iterator rit;
// начальная инициализация для "записываемых" регистров
// Механизм:
// Если tcp_preinit="1", то сперва будет сделано чтение значения из устройства.
// при этом флаг mb_init=false пока не пройдёт успешной инициализации
// Если tcp_preinit="0", то флаг mb_init сразу выставляется в true.
bool mb_initOK = { false }; /*!< инициализировалось ли значение из устройства */
// Флаг sm_init означает, что писать в устройство нельзя, т.к. значение в "карте регистров"
// ещё не инициализировано из SM
bool sm_initOK = { false }; /*!< инициализировалось ли значение из SM */
};
friend std::ostream& operator<<( std::ostream& os, const RegInfo& r );
friend std::ostream& operator<<( std::ostream& os, const RegInfo* r );
struct RTUDevice
{
ModbusRTU::ModbusAddr mbaddr = { 0 }; /*!< адрес устройства */
std::unordered_map<size_t, std::shared_ptr<RegMap>> pollmap;
DeviceType dtype = { dtUnknown }; /*!< тип устройства */
// resp - respond..(контроль наличия связи)
uniset::ObjectId resp_id = { uniset::DefaultObjectId };
IOController::IOStateList::iterator resp_it;
DelayTimer resp_Delay; // таймер для формирования задержки на отпускание (пропадание связи)
PassiveTimer resp_ptInit; // таймер для формирования задержки на инициализацию связи (задержка на выставление датчика связи после запуска)
bool resp_state = { false };
bool resp_invert = { false };
bool resp_force = { false };
Trigger trInitOK; // триггер для "инициализации"
std::atomic<size_t> numreply = { 0 }; // количество успешных запросов..
std::atomic<size_t> prev_numreply = { 0 };
//
bool ask_every_reg = { false }; /*!< опрашивать ли каждый регистр, независимо от результата опроса предыдущего. По умолчанию false - прервать опрос при первом же timeout */
// режим работы
uniset::ObjectId mode_id = { uniset::DefaultObjectId };
IOController::IOStateList::iterator mode_it;
long mode = { emNone }; // режим работы с устройством (см. ExchangeMode)
// safe mode
long safeMode = { safeNone }; /*!< режим безопасного состояния см. SafeMode */
uniset::ObjectId safemode_id = { uniset::DefaultObjectId }; /*!< идентификатор для датчика безопасного режима */
IOController::IOStateList::iterator safemode_it;
long safemode_value = { 1 };
// return TRUE if state changed
bool checkRespond( std::shared_ptr<DebugStream>& log );
// специфические поля для RS
ComPort::Speed speed = { ComPort::ComSpeed38400 };
std::shared_ptr<RTUStorage> rtu188;
std::string getShortInfo() const;
};
friend std::ostream& operator<<( std::ostream& os, RTUDevice& d );
typedef std::unordered_map<ModbusRTU::ModbusAddr, std::shared_ptr<RTUDevice>> RTUDeviceMap;
friend std::ostream& operator<<( std::ostream& os, RTUDeviceMap& d );
static void printMap(RTUDeviceMap& d);
typedef std::list<IOBase> ThresholdList;
struct InitRegInfo
{
InitRegInfo():
dev(0), mbreg(0),
mbfunc(ModbusRTU::fnUnknown),
initOK(false)
{}
RSProperty p;
std::shared_ptr<RTUDevice> dev;
ModbusRTU::ModbusData mbreg;
ModbusRTU::SlaveFunctionCode mbfunc;
bool initOK;
std::shared_ptr<RegInfo> ri;
};
typedef std::list<InitRegInfo> InitList;
static void rtuQueryOptimization( RTUDeviceMap& m, size_t maxQueryCount );
static void rtuQueryOptimizationForDevice( const std::shared_ptr<RTUDevice>& d, size_t maxQueryCount );
static void rtuQueryOptimizationForRegMap( const std::shared_ptr<RegMap>& regmap, size_t maxQueryCount );
// т.к. пороговые датчики не связаны напрямую с обменом, создаём для них отдельный список
// и отдельно его проверяем потом
ThresholdList thrlist;
RTUDeviceMap devices;
InitList initRegList; /*!< список регистров для инициализации */
void loadConfig( const std::shared_ptr<uniset::UniXML>& xml, UniXML::iterator sensorsSection );
void initDeviceList( const std::shared_ptr<UniXML>& xml );
bool initItem( UniXML::iterator& it );
std::string s_field;
std::string s_fvalue;
// определение timeout для соединения
timeout_t recv_timeout = { 500 }; // msec
timeout_t default_timeout = { 5000 }; // msec
timeout_t aftersend_pause = { 0 };
timeout_t polltime = { 100 }; /*!< периодичность обновления данных, [мсек] */
timeout_t sleepPause_msec = { 10 };
size_t maxQueryCount = { ModbusRTU::MAXDATALEN }; /*!< максимальное количество регистров для одного запроса */
xmlNode* cnode = { 0 };
std::shared_ptr<DebugStream> mblog;
std::string myname;
std::string prefix;
std::string prop_prefix; /*!< префикс для считывания параметров обмена */
std::string defaultMBtype;
std::string defaultMBaddr;
bool mbregFromID = { false };
bool defaultMBinitOK = { false }; // флаг определяющий нужно ли ждать "первого обмена" или при запуске сохранять в SM значение default.
bool noQueryOptimization = { false };
std::shared_ptr<uniset::Configuration> conf;
std::shared_ptr<SMInterface> shm;
void cloneParams( const std::shared_ptr<MBConfig>& conf );
protected:
bool initSMValue( ModbusRTU::ModbusData* data, int count, RSProperty* p );
void readConfiguration(const std::shared_ptr<uniset::UniXML>& xml, UniXML::iterator sensorsSection );
void initOffsetList();
std::shared_ptr<RTUDevice> addDev( RTUDeviceMap& dmap, ModbusRTU::ModbusAddr a, UniXML::iterator& it );
std::shared_ptr<RegInfo> addReg(std::shared_ptr<RegMap>& devices, ModbusRTU::RegID id, ModbusRTU::ModbusData r, UniXML::iterator& it, std::shared_ptr<RTUDevice> dev );
RSProperty* addProp( PList& plist, RSProperty&& p );
bool initMTRitem(UniXML::iterator& it, std::shared_ptr<RegInfo>& p );
bool initRTU188item(UniXML::iterator& it, std::shared_ptr<RegInfo>& p );
bool initRSProperty( RSProperty& p, UniXML::iterator& it );
bool initRegInfo(std::shared_ptr<RegInfo>& r, UniXML::iterator& it, std::shared_ptr<RTUDevice>& dev );
bool initRTUDevice( std::shared_ptr<RTUDevice>& d, UniXML::iterator& it );
bool initDeviceInfo( RTUDeviceMap& m, ModbusRTU::ModbusAddr a, UniXML::iterator& it );
};
// --------------------------------------------------------------------------
} // end of namespace uniset
// -----------------------------------------------------------------------------
#endif // _MBConfig_H_
// -----------------------------------------------------------------------------
This source diff could not be displayed because it is too large. You can view the blob instead.
...@@ -41,6 +41,7 @@ ...@@ -41,6 +41,7 @@
#include "LogServer.h" #include "LogServer.h"
#include "LogAgregator.h" #include "LogAgregator.h"
#include "VMonitor.h" #include "VMonitor.h"
#include "MBConfig.h"
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
#ifndef vmonit #ifndef vmonit
#define vmonit( var ) vmon.add( #var, var ) #define vmonit( var ) vmon.add( #var, var )
...@@ -48,337 +49,121 @@ ...@@ -48,337 +49,121 @@
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
namespace uniset namespace uniset
{ {
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
/*! /*!
\par Базовый класс для реализация обмена по протоколу Modbus [RTU|TCP]. \par Базовый класс для реализация обмена по протоколу Modbus [RTU|TCP].
*/ */
class MBExchange: class MBExchange:
public UniSetObject public UniSetObject
{ {
public: public:
MBExchange( uniset::ObjectId objId, uniset::ObjectId shmID, const std::shared_ptr<SharedMemory>& ic = nullptr, MBExchange( uniset::ObjectId objId, uniset::ObjectId shmID, const std::shared_ptr<SharedMemory>& ic = nullptr,
const std::string& prefix = "mb" ); const std::string& prefix = "mb" );
virtual ~MBExchange(); virtual ~MBExchange();
/*! глобальная функция для вывода help-а */ /*! глобальная функция для вывода help-а */
static void help_print( int argc, const char* const* argv ); static void help_print( int argc, const char* const* argv );
/*! Режимы работы процесса обмена */ // ----------------------------------
enum ExchangeMode enum Timer
{ {
emNone = 0, /*!< нормальная работа (по умолчанию) */ tmExchange
emWriteOnly = 1, /*!< "только посылка данных" (работают только write-функции) */ };
emReadOnly = 2, /*!< "только чтение" (работают только read-функции) */
emSkipSaveToSM = 3, /*!< не писать данные в SM (при этом работают и read и write функции) */ void execute();
emSkipExchange = 4 /*!< отключить обмен */
}; inline std::shared_ptr<LogAgregator> getLogAggregator()
friend std::ostream& operator<<( std::ostream& os, const ExchangeMode& em ); {
return loga;
/*! Режимы работы процесса обмена */ }
enum SafeMode inline std::shared_ptr<DebugStream> log()
{ {
safeNone = 0, /*!< не использовать безопасный режим (по умолчанию) */ return mblog;
safeResetIfNotRespond = 1, /*!< выставлять безопасное значение, если пропала связь с устройством */ }
safeExternalControl = 2 /*!< управление сбросом по внешнему датчику */
}; virtual uniset::SimpleInfo* getInfo( const char* userparam = 0 ) override;
bool reconfigure( const std::string& confile );
friend std::string to_string( const SafeMode& m );
friend std::ostream& operator<<( std::ostream& os, const SafeMode& m ); protected:
virtual void step();
enum DeviceType virtual void sysCommand( const uniset::SystemMessage* msg ) override;
{ virtual void sensorInfo( const uniset::SensorMessage* sm ) override;
dtUnknown, /*!< неизвестный */ virtual void timerInfo( const uniset::TimerMessage* tm ) override;
dtRTU, /*!< RTU (default) */ virtual void askSensors( UniversalIO::UIOCommand cmd );
dtMTR, /*!< MTR (DEIF) */ virtual void initOutput();
dtRTU188 /*!< RTU188 (Fastwell) */ virtual bool deactivateObject() override;
}; virtual bool activateObject() override;
virtual void initIterators();
static DeviceType getDeviceType( const std::string& dtype ) noexcept; virtual void initValues();
friend std::ostream& operator<<( std::ostream& os, const DeviceType& dt );
void firstInitRegisters();
struct RTUDevice; bool preInitRead( MBConfig::InitList::iterator& p );
struct RegInfo; bool initSMValue( ModbusRTU::ModbusData* data, int count, MBConfig::RSProperty* p );
bool allInitOK;
struct RSProperty:
public IOBase virtual std::shared_ptr<ModbusClient> initMB( bool reopen = false ) = 0;
{
// only for RTU virtual bool poll();
int8_t nbit; /*!< bit number (-1 - not used) */ bool pollRTU( std::shared_ptr<MBConfig::RTUDevice>& dev, MBConfig::RegMap::iterator& it );
VTypes::VType vType; /*!< type of value */
uint16_t rnum; /*!< count of registers */ void updateSM();
uint8_t nbyte; /*!< byte number (1-2) */
// в функции передаётся итератор,
RSProperty(): // т.к. в них идёт итерирование в случае если запрос в несколько регистров
nbit(-1), vType(VTypes::vtUnknown), void updateRTU(MBConfig::RegMap::iterator& it);
rnum(VTypes::wsize(VTypes::vtUnknown)), void updateMTR(MBConfig::RegMap::iterator& it);
nbyte(0) void updateRTU188(MBConfig::RegMap::iterator& it);
{} void updateRSProperty( MBConfig::RSProperty* p, bool write_only = false );
virtual void updateRespondSensors();
// т.к. IOBase содержит rwmutex с запрещённым конструктором копирования
// приходится здесь тоже объявлять разрешенными только операции "перемещения" bool isUpdateSM( bool wrFunc, long devMode ) const noexcept;
RSProperty( const RSProperty& r ) = delete; bool isPollEnabled( bool wrFunc ) const noexcept;
RSProperty& operator=(const RSProperty& r) = delete; bool isSafeMode( std::shared_ptr<MBConfig::RTUDevice>& dev ) const noexcept;
RSProperty( RSProperty&& r ) = default;
RSProperty& operator=(RSProperty&& r) = default; bool isProcActive() const;
void setProcActive( bool st );
std::shared_ptr<RegInfo> reg; bool waitSMReady();
};
// void readConfiguration();
friend std::ostream& operator<<( std::ostream& os, const RSProperty& p ); bool readItem( const std::shared_ptr<UniXML>& xml, UniXML::iterator& it, xmlNode* sec );
bool initItem( UniXML::iterator& it );
typedef std::list<RSProperty> PList; void initOffsetList();
static std::ostream& print_plist( std::ostream& os, const PList& p ); std::string initPropPrefix( const std::string& s_filed, const std::string& def_prop_prefix );
typedef std::map<ModbusRTU::RegID, std::shared_ptr<RegInfo>> RegMap; xmlNode* cnode = { 0 };
struct RegInfo std::shared_ptr<SMInterface> shm;
{
// т.к. RSProperty содержит rwmutex с запрещённым конструктором копирования timeout_t initPause = { 3000 };
// приходится здесь тоже объявлять разрешенными только операции "перемещения" uniset::uniset_rwmutex mutex_start;
RegInfo( const RegInfo& r ) = default;
RegInfo& operator=(const RegInfo& r) = delete; bool force = { false }; /*!< флаг означающий, что надо сохранять в SM, даже если значение не менялось */
RegInfo( RegInfo&& r ) = delete; bool force_out = { false }; /*!< флаг означающий, принудительного чтения выходов */
RegInfo& operator=(RegInfo&& r) = default;
RegInfo() = default; PassiveTimer ptHeartBeat;
uniset::ObjectId sidHeartBeat = { uniset::DefaultObjectId };
ModbusRTU::ModbusData mbval = { 0 }; long maxHeartBeat = { 10 };
ModbusRTU::ModbusData mbreg = { 0 }; /*!< регистр */ IOController::IOStateList::iterator itHeartBeat;
ModbusRTU::SlaveFunctionCode mbfunc = { ModbusRTU::fnUnknown }; /*!< функция для чтения/записи */ uniset::ObjectId test_id = { uniset::DefaultObjectId };
PList slst;
ModbusRTU::RegID regID = { 0 }; uniset::ObjectId sidExchangeMode = { uniset::DefaultObjectId }; /*!< идентификатор для датчика режима работы */
IOController::IOStateList::iterator itExchangeMode;
std::shared_ptr<RTUDevice> dev; long exchangeMode = { MBConfig::emNone }; /*!< режим работы см. ExchangeMode */
// only for RTU188 std::atomic_bool activated = { false };
RTUStorage::RTUJack rtuJack = { RTUStorage::nUnknown }; std::atomic_bool canceled = { false };
int rtuChan = { 0 }; timeout_t activateTimeout = { 20000 }; // msec
bool notUseExchangeTimer = { false };
// only for MTR
MTR::MTRType mtrType = { MTR::mtUnknown }; /*!< тип регистра (согласно спецификации на MTR) */ timeout_t stat_time = { 0 }; /*!< время сбора статистики обмена, 0 - отключена */
size_t poll_count = { 0 };
// optimization PassiveTimer ptStatistic; /*!< таймер для сбора статистики обмена */
size_t q_num = { 0 }; /*!< number in query */ std::string statInfo = { "" };
size_t q_count = { 1 }; /*!< count registers for query */
std::shared_ptr<ModbusClient> mb;
RegMap::iterator rit;
PassiveTimer ptReopen; /*!< таймер для переоткрытия соединения */
// начальная инициализация для "записываемых" регистров Trigger trReopen;
// Механизм:
// Если tcp_preinit="1", то сперва будет сделано чтение значения из устройства.
// при этом флаг mb_init=false пока не пройдёт успешной инициализации
// Если tcp_preinit="0", то флаг mb_init сразу выставляется в true.
bool mb_initOK = { false }; /*!< инициализировалось ли значение из устройства */
// Флаг sm_init означает, что писать в устройство нельзя, т.к. значение в "карте регистров"
// ещё не инициализировано из SM
bool sm_initOK = { false }; /*!< инициализировалось ли значение из SM */
};
friend std::ostream& operator<<( std::ostream& os, const RegInfo& r );
friend std::ostream& operator<<( std::ostream& os, const RegInfo* r );
struct RTUDevice
{
ModbusRTU::ModbusAddr mbaddr = { 0 }; /*!< адрес устройства */
std::unordered_map<size_t, std::shared_ptr<RegMap>> pollmap;
DeviceType dtype = { dtUnknown }; /*!< тип устройства */
// resp - respond..(контроль наличия связи)
uniset::ObjectId resp_id = { uniset::DefaultObjectId };
IOController::IOStateList::iterator resp_it;
DelayTimer resp_Delay; // таймер для формирования задержки на отпускание (пропадание связи)
PassiveTimer resp_ptInit; // таймер для формирования задержки на инициализацию связи (задержка на выставление датчика связи после запуска)
bool resp_state = { false };
bool resp_invert = { false };
bool resp_force = { false };
Trigger trInitOK; // триггер для "инициализации"
std::atomic<size_t> numreply = { 0 }; // количество успешных запросов..
std::atomic<size_t> prev_numreply = { 0 };
//
bool ask_every_reg = { false }; /*!< опрашивать ли каждый регистр, независимо от результата опроса предыдущего. По умолчанию false - прервать опрос при первом же timeout */
// режим работы
uniset::ObjectId mode_id = { uniset::DefaultObjectId };
IOController::IOStateList::iterator mode_it;
long mode = { emNone }; // режим работы с устройством (см. ExchangeMode)
// safe mode
long safeMode = { safeNone }; /*!< режим безопасного состояния см. SafeMode */
uniset::ObjectId safemode_id = { uniset::DefaultObjectId }; /*!< идентификатор для датчика безопасного режима */
IOController::IOStateList::iterator safemode_it;
long safemode_value = { 1 };
// return TRUE if state changed
bool checkRespond( std::shared_ptr<DebugStream>& log );
// специфические поля для RS
ComPort::Speed speed = { ComPort::ComSpeed38400 };
std::shared_ptr<RTUStorage> rtu188;
std::string getShortInfo() const;
};
friend std::ostream& operator<<( std::ostream& os, RTUDevice& d );
typedef std::unordered_map<ModbusRTU::ModbusAddr, std::shared_ptr<RTUDevice>> RTUDeviceMap;
friend std::ostream& operator<<( std::ostream& os, RTUDeviceMap& d );
void printMap(RTUDeviceMap& d);
// ----------------------------------
enum Timer
{
tmExchange
};
void execute();
inline std::shared_ptr<LogAgregator> getLogAggregator()
{
return loga;
}
inline std::shared_ptr<DebugStream> log()
{
return mblog;
}
virtual uniset::SimpleInfo* getInfo( const char* userparam = 0 ) override;
protected:
virtual void step();
virtual void sysCommand( const uniset::SystemMessage* msg ) override;
virtual void sensorInfo( const uniset::SensorMessage* sm ) override;
virtual void timerInfo( const uniset::TimerMessage* tm ) override;
virtual void askSensors( UniversalIO::UIOCommand cmd );
virtual void initOutput();
virtual bool deactivateObject() override;
virtual bool activateObject() override;
virtual void initIterators();
virtual void initValues();
struct InitRegInfo
{
InitRegInfo():
dev(0), mbreg(0),
mbfunc(ModbusRTU::fnUnknown),
initOK(false)
{}
RSProperty p;
std::shared_ptr<RTUDevice> dev;
ModbusRTU::ModbusData mbreg;
ModbusRTU::SlaveFunctionCode mbfunc;
bool initOK;
std::shared_ptr<RegInfo> ri;
};
typedef std::list<InitRegInfo> InitList;
void firstInitRegisters();
bool preInitRead( InitList::iterator& p );
bool initSMValue( ModbusRTU::ModbusData* data, int count, RSProperty* p );
bool allInitOK;
RTUDeviceMap devices;
InitList initRegList; /*!< список регистров для инициализации */
virtual std::shared_ptr<ModbusClient> initMB( bool reopen = false ) = 0;
virtual bool poll();
bool pollRTU( std::shared_ptr<RTUDevice>& dev, RegMap::iterator& it );
void updateSM();
// в функции передаётся итератор,
// т.к. в них идёт итерирование в случае если запрос в несколько регистров
void updateRTU(RegMap::iterator& it);
void updateMTR(RegMap::iterator& it);
void updateRTU188(RegMap::iterator& it);
void updateRSProperty( RSProperty* p, bool write_only = false );
virtual void updateRespondSensors();
bool isUpdateSM( bool wrFunc, long devMode ) const noexcept;
bool isPollEnabled( bool wrFunc ) const noexcept;
bool isSafeMode( std::shared_ptr<RTUDevice>& dev ) const noexcept;
bool isProcActive() const;
void setProcActive( bool st );
bool waitSMReady();
void readConfiguration();
bool readItem( const std::shared_ptr<UniXML>& xml, UniXML::iterator& it, xmlNode* sec );
bool initItem( UniXML::iterator& it );
void initDeviceList();
void initOffsetList();
std::shared_ptr<RTUDevice> addDev( RTUDeviceMap& dmap, ModbusRTU::ModbusAddr a, UniXML::iterator& it );
std::shared_ptr<RegInfo> addReg(std::shared_ptr<RegMap>& devices, ModbusRTU::RegID id, ModbusRTU::ModbusData r, UniXML::iterator& it, std::shared_ptr<RTUDevice> dev );
RSProperty* addProp( PList& plist, RSProperty&& p );
bool initMTRitem(UniXML::iterator& it, std::shared_ptr<RegInfo>& p );
bool initRTU188item(UniXML::iterator& it, std::shared_ptr<RegInfo>& p );
bool initRSProperty( RSProperty& p, UniXML::iterator& it );
bool initRegInfo(std::shared_ptr<RegInfo>& r, UniXML::iterator& it, std::shared_ptr<RTUDevice>& dev );
bool initRTUDevice( std::shared_ptr<RTUDevice>& d, UniXML::iterator& it );
virtual bool initDeviceInfo( RTUDeviceMap& m, ModbusRTU::ModbusAddr a, UniXML::iterator& it );
std::string initPropPrefix( const std::string& def_prop_prefix = "" );
void rtuQueryOptimization( RTUDeviceMap& m );
void rtuQueryOptimizationForDevice( const std::shared_ptr<RTUDevice>& d );
void rtuQueryOptimizationForRegMap( const std::shared_ptr<RegMap>& regmap );
xmlNode* cnode = { 0 };
std::string s_field;
std::string s_fvalue;
std::shared_ptr<SMInterface> shm;
timeout_t initPause = { 3000 };
uniset::uniset_rwmutex mutex_start;
bool force = { false }; /*!< флаг означающий, что надо сохранять в SM, даже если значение не менялось */
bool force_out = { false }; /*!< флаг означающий, принудительного чтения выходов */
bool mbregFromID = { false };
timeout_t polltime = { 100 }; /*!< периодичность обновления данных, [мсек] */
timeout_t sleepPause_msec = { 10 };
size_t maxQueryCount = { ModbusRTU::MAXDATALEN }; /*!< максимальное количество регистров для одного запроса */
PassiveTimer ptHeartBeat;
uniset::ObjectId sidHeartBeat = { uniset::DefaultObjectId };
long maxHeartBeat = { 10 };
IOController::IOStateList::iterator itHeartBeat;
uniset::ObjectId test_id = { uniset::DefaultObjectId };
uniset::ObjectId sidExchangeMode = { uniset::DefaultObjectId }; /*!< идентификатор для датчика режима работы */
IOController::IOStateList::iterator itExchangeMode;
long exchangeMode = { emNone }; /*!< режим работы см. ExchangeMode */
std::atomic_bool activated = { false };
std::atomic_bool canceled = { false };
timeout_t activateTimeout = { 20000 }; // msec
bool noQueryOptimization = { false };
bool notUseExchangeTimer = { false };
std::string prefix;
timeout_t stat_time = { 0 }; /*!< время сбора статистики обмена, 0 - отключена */
size_t poll_count = { 0 };
PassiveTimer ptStatistic; /*!< таймер для сбора статистики обмена */
std::string statInfo = { "" };
std::string prop_prefix; /*!< префикс для считывания параметров обмена */
std::shared_ptr<ModbusClient> mb;
// определение timeout для соединения
timeout_t recv_timeout = { 500 }; // msec
timeout_t default_timeout = { 5000 }; // msec
timeout_t aftersend_pause = { 0 };
PassiveTimer ptReopen; /*!< таймер для переоткрытия соединения */
Trigger trReopen;
PassiveTimer ptInitChannel; /*!< задержка не инициализацию связи */ PassiveTimer ptInitChannel; /*!< задержка не инициализацию связи */
...@@ -397,13 +182,22 @@ namespace uniset ...@@ -397,13 +182,22 @@ namespace uniset
std::string logserv_host = {""}; std::string logserv_host = {""};
int logserv_port = {0}; int logserv_port = {0};
const std::shared_ptr<SharedMemory> ic; const std::shared_ptr<SharedMemory> ic;
std::shared_ptr<LogAgregator> loga;
std::shared_ptr<DebugStream> mblog;
std::shared_ptr<LogServer> logserv;
std::string logserv_host = {""};
int logserv_port = {0};
const std::shared_ptr<SharedMemory> ic;
VMonitor vmon; VMonitor vmon;
size_t ncycle = { 0 }; /*!< текущий номер цикла опроса */ size_t ncycle = { 0 }; /*!< текущий номер цикла опроса */
private: std::shared_ptr<uniset::MBConfig> mbconf;
MBExchange(); uniset::uniset_rwmutex mutex_conf;
private:
MBExchange();
}; };
// -------------------------------------------------------------------------- // --------------------------------------------------------------------------
......
...@@ -37,9 +37,9 @@ MBTCPMaster::MBTCPMaster(uniset::ObjectId objId, uniset::ObjectId shmId, ...@@ -37,9 +37,9 @@ MBTCPMaster::MBTCPMaster(uniset::ObjectId objId, uniset::ObjectId shmId,
auto conf = uniset_conf(); auto conf = uniset_conf();
// префикс для "свойств" - по умолчанию "tcp_"; // префикс для "свойств" - по умолчанию "tcp_";
prop_prefix = initPropPrefix("tcp_"); mbconf->prop_prefix = initPropPrefix(mbconf->s_field, "tcp_");
mbinfo << myname << "(init): prop_prefix=" << prop_prefix << endl; mbinfo << myname << "(init): prop_prefix=" << mbconf->prop_prefix << endl;
UniXML::iterator it(cnode); UniXML::iterator it(cnode);
...@@ -61,23 +61,16 @@ MBTCPMaster::MBTCPMaster(uniset::ObjectId objId, uniset::ObjectId shmId, ...@@ -61,23 +61,16 @@ MBTCPMaster::MBTCPMaster(uniset::ObjectId objId, uniset::ObjectId shmId,
force_disconnect = conf->getArgInt("--" + prefix + "-persistent-connection", it.getProp("persistent_connection")) ? false : true; force_disconnect = conf->getArgInt("--" + prefix + "-persistent-connection", it.getProp("persistent_connection")) ? false : true;
mbinfo << myname << "(init): persisten-connection=" << (!force_disconnect) << endl; mbinfo << myname << "(init): persisten-connection=" << (!force_disconnect) << endl;
if( shm->isLocalwork() ) if( shm->isLocalwork() )
{ mbconf->loadConfig(conf->getConfXML(), conf->getXMLSensorsSection());
readConfiguration(); else
ic->addReadItem( sigc::mem_fun(this, &MBTCPMaster::readItem) );
if( !noQueryOptimization )
rtuQueryOptimization(devices);
initDeviceList();
}
else
ic->addReadItem( sigc::mem_fun(this, &MBTCPMaster::readItem) );
pollThread = unisetstd::make_unique<ThreadCreator<MBTCPMaster>>(this, &MBTCPMaster::poll_thread); pollThread = unisetstd::make_unique<ThreadCreator<MBTCPMaster>>(this, &MBTCPMaster::poll_thread);
pollThread->setFinalAction(this, &MBTCPMaster::final_thread); pollThread->setFinalAction(this, &MBTCPMaster::final_thread);
if( mblog->is_info() ) if( mblog->is_info() )
printMap(devices); MBConfig::printMap(mbconf->devices);
} }
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
MBTCPMaster::~MBTCPMaster() MBTCPMaster::~MBTCPMaster()
...@@ -112,11 +105,11 @@ std::shared_ptr<ModbusClient> MBTCPMaster::initMB( bool reopen ) ...@@ -112,11 +105,11 @@ std::shared_ptr<ModbusClient> MBTCPMaster::initMB( bool reopen )
mbtcp->connect(iaddr, port); mbtcp->connect(iaddr, port);
mbtcp->setForceDisconnect(force_disconnect); mbtcp->setForceDisconnect(force_disconnect);
if( recv_timeout > 0 ) if( mbconf->recv_timeout > 0 )
mbtcp->setTimeout(recv_timeout); mbtcp->setTimeout(mbconf->recv_timeout);
mbtcp->setSleepPause(sleepPause_msec); mbtcp->setSleepPause(mbconf->sleepPause_msec);
mbtcp->setAfterSendPause(aftersend_pause); mbtcp->setAfterSendPause(mbconf->aftersend_pause);
mbinfo << myname << "(init): ipaddr=" << iaddr << " port=" << port mbinfo << myname << "(init): ipaddr=" << iaddr << " port=" << port
<< " connection=" << (mbtcp->isConnection() ? "OK" : "FAIL" ) << endl; << " connection=" << (mbtcp->isConnection() ? "OK" : "FAIL" ) << endl;
...@@ -147,45 +140,45 @@ void MBTCPMaster::final_thread() ...@@ -147,45 +140,45 @@ void MBTCPMaster::final_thread()
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
void MBTCPMaster::poll_thread() void MBTCPMaster::poll_thread()
{ {
// ждём начала работы..(см. MBExchange::activateObject) // ждём начала работы..(см. MBExchange::activateObject)
while( !isProcActive() && !canceled ) while( !isProcActive() && !canceled )
{ {
uniset::uniset_rwmutex_rlock l(mutex_start); uniset::uniset_rwmutex_rlock l(mutex_start);
} }
// if( canceled ) // if( canceled )
// return; // return;
// работаем // работаем
while( isProcActive() ) while( isProcActive() )
{ {
try try
{ {
if( sidExchangeMode != DefaultObjectId && force ) if( sidExchangeMode != DefaultObjectId && force )
exchangeMode = shm->localGetValue(itExchangeMode, sidExchangeMode); exchangeMode = shm->localGetValue(itExchangeMode, sidExchangeMode);
} }
catch(...) catch(...)
{ {
throw; throw;
} }
try try
{ {
poll(); poll();
} }
catch(...) catch(...)
{ {
// if( !checkProcActive() ) // if( !checkProcActive() )
throw; throw;
} }
if( !isProcActive() ) if( !isProcActive() )
break; break;
msleep(polltime); msleep(mbconf->polltime);
} }
dinfo << myname << "(poll_thread): thread finished.." << endl; dinfo << myname << "(poll_thread): thread finished.." << endl;
} }
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
bool MBTCPMaster::deactivateObject() bool MBTCPMaster::deactivateObject()
......
...@@ -33,175 +33,168 @@ MBTCPMultiMaster::MBTCPMultiMaster( uniset::ObjectId objId, uniset::ObjectId shm ...@@ -33,175 +33,168 @@ MBTCPMultiMaster::MBTCPMultiMaster( uniset::ObjectId objId, uniset::ObjectId shm
MBExchange(objId, shmId, ic, prefix), MBExchange(objId, shmId, ic, prefix),
force_disconnect(true) force_disconnect(true)
{ {
if( objId == DefaultObjectId ) if( objId == DefaultObjectId )
throw uniset::SystemError("(MBTCPMultiMaster): objId=-1?!! Use --" + prefix + "-name" ); throw uniset::SystemError("(MBTCPMultiMaster): objId=-1?!! Use --" + prefix + "-name" );
auto conf = uniset_conf(); auto conf = uniset_conf();
prop_prefix = initPropPrefix("tcp_"); mbconf->prop_prefix = initPropPrefix(mbconf->s_field, "tcp_");
mbinfo << myname << "(init): prop_prefix=" << prop_prefix << endl; mbinfo << myname << "(init): prop_prefix=" << mbconf->prop_prefix << endl;
UniXML::iterator it(cnode); UniXML::iterator it(cnode);
checktime = conf->getArgPInt("--" + prefix + "-checktime", it.getProp("checktime"), 5000); checktime = conf->getArgPInt("--" + prefix + "-checktime", it.getProp("checktime"), 5000);
force_disconnect = conf->getArgInt("--" + prefix + "-persistent-connection", it.getProp("persistent_connection")) ? false : true; force_disconnect = conf->getArgInt("--" + prefix + "-persistent-connection", it.getProp("persistent_connection")) ? false : true;
int ignore_timeout = conf->getArgPInt("--" + prefix + "-ignore-timeout", it.getProp("ignore_timeout"), ptReopen.getInterval()); int ignore_timeout = conf->getArgPInt("--" + prefix + "-ignore-timeout", it.getProp("ignore_timeout"), ptReopen.getInterval());
// Т.к. при "многоканальном" доступе к slave, смена канала должна происходит сразу после // Т.к. при "многоканальном" доступе к slave, смена канала должна происходит сразу после
// неудачной попытки запросов по одному из каналов, то ПЕРЕОПРЕДЕЛЯЕМ reopen, на channel-timeout.. // неудачной попытки запросов по одному из каналов, то ПЕРЕОПРЕДЕЛЯЕМ reopen, на channel-timeout..
int channelTimeout = conf->getArgPInt("--" + prefix + "-default-channel-timeout", it.getProp("channelTimeout"), default_timeout); int channelTimeout = conf->getArgPInt("--" + prefix + "-default-channel-timeout", it.getProp("channelTimeout"), mbconf->default_timeout);
ptReopen.setTiming(channelTimeout); ptReopen.setTiming(channelTimeout);
UniXML::iterator it1(it); UniXML::iterator it1(it);
if( !it1.find("GateList") ) if( !it1.find("GateList") )
{ {
ostringstream err; ostringstream err;
err << myname << "(init): not found <GateList>"; err << myname << "(init): not found <GateList>";
mbcrit << err.str() << endl; mbcrit << err.str() << endl;
throw uniset::SystemError(err.str()); throw uniset::SystemError(err.str());
} }
if( !it1.goChildren() ) if( !it1.goChildren() )
{ {
ostringstream err; ostringstream err;
err << myname << "(init): empty <GateList> ?!"; err << myname << "(init): empty <GateList> ?!";
mbcrit << err.str() << endl; mbcrit << err.str() << endl;
throw uniset::SystemError(err.str()); throw uniset::SystemError(err.str());
} }
for( ; it1.getCurrent(); it1++ ) for( ; it1.getCurrent(); it1++ )
{ {
if( it1.getIntProp("ignore") ) if( it1.getIntProp("ignore") )
{ {
mbinfo << myname << "(init): IGNORE " << it1.getProp("ip") << ":" << it1.getProp("port") << endl; mbinfo << myname << "(init): IGNORE " << it1.getProp("ip") << ":" << it1.getProp("port") << endl;
continue; continue;
} }
auto sinf = make_shared<MBSlaveInfo>(); auto sinf = make_shared<MBSlaveInfo>();
sinf->ip = it1.getProp("ip"); sinf->ip = it1.getProp("ip");
if( sinf->ip.empty() ) if( sinf->ip.empty() )
{ {
ostringstream err; ostringstream err;
err << myname << "(init): ip='' in <GateList>"; err << myname << "(init): ip='' in <GateList>";
mbcrit << err.str() << endl; mbcrit << err.str() << endl;
throw uniset::SystemError(err.str()); throw uniset::SystemError(err.str());
} }
sinf->port = it1.getIntProp("port"); sinf->port = it1.getIntProp("port");
if( sinf->port <= 0 ) if( sinf->port <= 0 )
{ {
ostringstream err; ostringstream err;
err << myname << "(init): ERROR: port=''" << sinf->port << " for ip='" << sinf->ip << "' in <GateList>"; err << myname << "(init): ERROR: port=''" << sinf->port << " for ip='" << sinf->ip << "' in <GateList>";
mbcrit << err.str() << endl; mbcrit << err.str() << endl;
throw uniset::SystemError(err.str()); throw uniset::SystemError(err.str());
} }
if( !it1.getProp("respondSensor").empty() ) if( !it1.getProp("respondSensor").empty() )
{ {
sinf->respond_id = conf->getSensorID( it1.getProp("respondSensor") ); sinf->respond_id = conf->getSensorID( it1.getProp("respondSensor") );
if( sinf->respond_id == DefaultObjectId ) if( sinf->respond_id == DefaultObjectId )
{ {
ostringstream err; ostringstream err;
err << myname << "(init): ERROR: Unknown SensorID for '" << it1.getProp("respondSensor") << "' in <GateList>"; err << myname << "(init): ERROR: Unknown SensorID for '" << it1.getProp("respondSensor") << "' in <GateList>";
mbcrit << err.str() << endl; mbcrit << err.str() << endl;
throw uniset::SystemError(err.str()); throw uniset::SystemError(err.str());
} }
} }
sinf->priority = it1.getIntProp("priority"); sinf->priority = it1.getIntProp("priority");
sinf->mbtcp = std::make_shared<ModbusTCPMaster>(); sinf->mbtcp = std::make_shared<ModbusTCPMaster>();
sinf->ptIgnoreTimeout.setTiming( it1.getPIntProp("ignore_timeout", ignore_timeout) ); sinf->ptIgnoreTimeout.setTiming( it1.getPIntProp("ignore_timeout", ignore_timeout) );
sinf->recv_timeout = it1.getPIntProp("recv_timeout", recv_timeout); sinf->recv_timeout = it1.getPIntProp("recv_timeout", mbconf->recv_timeout);
sinf->aftersend_pause = it1.getPIntProp("aftersend_pause", aftersend_pause); sinf->aftersend_pause = it1.getPIntProp("aftersend_pause", mbconf->aftersend_pause);
sinf->sleepPause_usec = it1.getPIntProp("sleepPause_msec", sleepPause_msec); sinf->sleepPause_usec = it1.getPIntProp("sleepPause_msec", mbconf->sleepPause_msec);
sinf->respond_invert = it1.getPIntProp("invert", 0); sinf->respond_invert = it1.getPIntProp("invert", 0);
sinf->respond_force = it1.getPIntProp("force", 0); sinf->respond_force = it1.getPIntProp("force", 0);
int fn = conf->getArgPInt("--" + prefix + "-check-func", it.getProp("checkFunc"), ModbusRTU::fnUnknown); int fn = conf->getArgPInt("--" + prefix + "-check-func", it.getProp("checkFunc"), ModbusRTU::fnUnknown);
if( fn != ModbusRTU::fnUnknown && if( fn != ModbusRTU::fnUnknown &&
fn != ModbusRTU::fnReadCoilStatus && fn != ModbusRTU::fnReadCoilStatus &&
fn != ModbusRTU::fnReadInputStatus && fn != ModbusRTU::fnReadInputStatus &&
fn != ModbusRTU::fnReadOutputRegisters && fn != ModbusRTU::fnReadOutputRegisters &&
fn != ModbusRTU::fnReadInputRegisters ) fn != ModbusRTU::fnReadInputRegisters )
{ {
ostringstream err; ostringstream err;
err << myname << "(init): BAD check function ='" << fn << "'. Must be [1,2,3,4]"; err << myname << "(init): BAD check function ='" << fn << "'. Must be [1,2,3,4]";
mbcrit << err.str() << endl; mbcrit << err.str() << endl;
throw SystemError(err.str()); throw SystemError(err.str());
} }
sinf->checkFunc = (ModbusRTU::SlaveFunctionCode)fn; sinf->checkFunc = (ModbusRTU::SlaveFunctionCode)fn;
sinf->checkAddr = conf->getArgPInt("--" + prefix + "-check-addr", it.getProp("checkAddr"), 0); sinf->checkAddr = conf->getArgPInt("--" + prefix + "-check-addr", it.getProp("checkAddr"), 0);
sinf->checkReg = conf->getArgPInt("--" + prefix + "-check-reg", it.getProp("checkReg"), 0); sinf->checkReg = conf->getArgPInt("--" + prefix + "-check-reg", it.getProp("checkReg"), 0);
int tout = it1.getPIntProp("timeout", channelTimeout); int tout = it1.getPIntProp("timeout", channelTimeout);
sinf->channel_timeout = (tout >= 0 ? tout : channelTimeout); sinf->channel_timeout = (tout >= 0 ? tout : channelTimeout);
// делаем только задержку на отпускание.. // делаем только задержку на отпускание..
sinf->respondDelay.set(0, sinf->channel_timeout); sinf->respondDelay.set(0, sinf->channel_timeout);
sinf->force_disconnect = it.getPIntProp("persistent_connection", !force_disconnect) ? false : true; sinf->force_disconnect = it.getPIntProp("persistent_connection", !force_disconnect) ? false : true;
ostringstream n; ostringstream n;
n << sinf->ip << ":" << sinf->port; n << sinf->ip << ":" << sinf->port;
sinf->myname = n.str(); sinf->myname = n.str();
auto l = loga->create(sinf->myname); auto l = loga->create(sinf->myname);
sinf->mbtcp->setLog(l); sinf->mbtcp->setLog(l);
mbinfo << myname << "(init): add slave channel " << sinf->myname << endl; mbinfo << myname << "(init): add slave channel " << sinf->myname << endl;
mblist.emplace_back(sinf); mblist.emplace_back(sinf);
} }
if( ic ) if( ic )
ic->logAgregator()->add(loga); ic->logAgregator()->add(loga);
if( mblist.empty() ) if( mblist.empty() )
{ {
ostringstream err; ostringstream err;
err << myname << "(init): empty <GateList>!"; err << myname << "(init): empty <GateList>!";
mbcrit << err.str() << endl; mbcrit << err.str() << endl;
throw uniset::SystemError(err.str()); throw uniset::SystemError(err.str());
} }
mblist.sort(); mblist.sort();
mbi = mblist.rbegin(); // т.к. mbi это reverse_iterator mbi = mblist.rbegin(); // т.к. mbi это reverse_iterator
(*mbi)->setUse(true); (*mbi)->setUse(true);
if( shm->isLocalwork() ) if( shm->isLocalwork() )
{ mbconf->loadConfig(conf->getConfXML(), conf->getXMLSensorsSection());
readConfiguration(); else
ic->addReadItem( sigc::mem_fun(this, &MBTCPMultiMaster::readItem) );
if( !noQueryOptimization )
rtuQueryOptimization(devices); pollThread = unisetstd::make_unique<ThreadCreator<MBTCPMultiMaster>>(this, &MBTCPMultiMaster::poll_thread);
pollThread->setFinalAction(this, &MBTCPMultiMaster::final_thread);
initDeviceList(); checkThread = unisetstd::make_unique<ThreadCreator<MBTCPMultiMaster>>(this, &MBTCPMultiMaster::check_thread);
} checkThread->setFinalAction(this, &MBTCPMultiMaster::final_thread);
else
ic->addReadItem( sigc::mem_fun(this, &MBTCPMultiMaster::readItem) ); // Т.к. при "многоканальном" доступе к slave, смена канала должна происходит сразу после
// неудачной попытки запросов по одному из каналов, то ПЕРЕОПРЕДЕЛЯЕМ reopen, на channel-timeout..
pollThread = unisetstd::make_unique<ThreadCreator<MBTCPMultiMaster>>(this, &MBTCPMultiMaster::poll_thread); int tout = conf->getArgPInt("--" + prefix + "-default-channel-timeout", it.getProp("channelTimeout"), mbconf->default_timeout);
pollThread->setFinalAction(this, &MBTCPMultiMaster::final_thread); ptReopen.setTiming(tout);
checkThread = unisetstd::make_unique<ThreadCreator<MBTCPMultiMaster>>(this, &MBTCPMultiMaster::check_thread);
checkThread->setFinalAction(this, &MBTCPMultiMaster::final_thread); if( mblog->is_info() )
MBConfig::printMap(mbconf->devices);
// Т.к. при "многоканальном" доступе к slave, смена канала должна происходит сразу после
// неудачной попытки запросов по одному из каналов, то ПЕРЕОПРЕДЕЛЯЕМ reopen, на channel-timeout..
int tout = conf->getArgPInt("--" + prefix + "-default-channel-timeout", it.getProp("channelTimeout"), default_timeout);
ptReopen.setTiming(tout);
if( mblog->is_info() )
printMap(devices);
} }
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
MBTCPMultiMaster::~MBTCPMultiMaster() MBTCPMultiMaster::~MBTCPMultiMaster()
...@@ -233,111 +226,111 @@ MBTCPMultiMaster::~MBTCPMultiMaster() ...@@ -233,111 +226,111 @@ MBTCPMultiMaster::~MBTCPMultiMaster()
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
std::shared_ptr<ModbusClient> MBTCPMultiMaster::initMB( bool reopen ) std::shared_ptr<ModbusClient> MBTCPMultiMaster::initMB( bool reopen )
{ {
if( mb ) if( mb )
ptInitChannel.reset(); ptInitChannel.reset();
// просто движемся по кругу (т.к. связь не проверяется) // просто движемся по кругу (т.к. связь не проверяется)
// движемся в обратном порядке, т.к. сортировка по возрастанию приоритета // движемся в обратном порядке, т.к. сортировка по возрастанию приоритета
if( checktime <= 0 ) if( checktime <= 0 )
{ {
++mbi; ++mbi;
if( mbi == mblist.rend() ) if( mbi == mblist.rend() )
mbi = mblist.rbegin(); mbi = mblist.rbegin();
auto m = (*mbi); auto m = (*mbi);
m->init(mblog); m->init(mblog);
// переопределяем timeout на данный канал // переопределяем timeout на данный канал
ptReopen.setTiming( m->channel_timeout ); ptReopen.setTiming( m->channel_timeout );
m->setUse(true); m->setUse(true);
mb = m->mbtcp; mb = m->mbtcp;
return m->mbtcp; return m->mbtcp;
} }
{ {
// сперва надо обновить все ignore // сперва надо обновить все ignore
// т.к. фактически флаги выставляются и сбрасываются только здесь // т.к. фактически флаги выставляются и сбрасываются только здесь
for( auto&& it : mblist ) for( auto&& it : mblist )
it->ignore = !it->ptIgnoreTimeout.checkTime(); it->ignore = !it->ptIgnoreTimeout.checkTime();
// если reopen=true - значит почему-то по текущему каналу связи нет (хотя соединение есть) // если reopen=true - значит почему-то по текущему каналу связи нет (хотя соединение есть)
// тогда выставляем ему признак игнорирования // тогда выставляем ему признак игнорирования
if( mbi != mblist.rend() && reopen ) if( mbi != mblist.rend() && reopen )
{ {
auto m = (*mbi); auto m = (*mbi);
m->setUse(false); m->setUse(false);
m->ignore = true; m->ignore = true;
m->ptIgnoreTimeout.reset(); m->ptIgnoreTimeout.reset();
mbwarn << myname << "(initMB): set ignore=true for " << m->ip << ":" << m->port << endl; mbwarn << myname << "(initMB): set ignore=true for " << m->ip << ":" << m->port << endl;
} }
// Если по текущему каналу связь есть (и мы его не игнорируем), то возвращаем его // Если по текущему каналу связь есть (и мы его не игнорируем), то возвращаем его
if( mbi != mblist.rend() && !(*mbi)->ignore && (*mbi)->respond ) if( mbi != mblist.rend() && !(*mbi)->ignore && (*mbi)->respond )
{ {
auto m = (*mbi); auto m = (*mbi);
// ещё раз проверим соединение (в неблокирующем режиме) // ещё раз проверим соединение (в неблокирующем режиме)
m->respond = m->check(); m->respond = m->check();
if( m->respond && (m->mbtcp->isConnection() || m->init(mblog)) ) if( m->respond && (m->mbtcp->isConnection() || m->init(mblog)) )
{ {
mblog4 << myname << "(initMB): SELECT CHANNEL " << m->ip << ":" << m->port << endl; mblog4 << myname << "(initMB): SELECT CHANNEL " << m->ip << ":" << m->port << endl;
mb = m->mbtcp; mb = m->mbtcp;
m->setUse(true); m->setUse(true);
ptReopen.setTiming( m->channel_timeout ); ptReopen.setTiming( m->channel_timeout );
return m->mbtcp; return m->mbtcp;
} }
m->setUse(false); m->setUse(false);
} }
if( mbi != mblist.rend() ) if( mbi != mblist.rend() )
(*mbi)->mbtcp->forceDisconnect(); (*mbi)->mbtcp->forceDisconnect();
} }
// проходим по списку (в обратном порядке, т.к. самый приоритетный в конце) // проходим по списку (в обратном порядке, т.к. самый приоритетный в конце)
for( auto it = mblist.rbegin(); it != mblist.rend(); ++it ) for( auto it = mblist.rbegin(); it != mblist.rend(); ++it )
{ {
auto m = (*it); auto m = (*it);
if( m->respond && !m->ignore && m->init(mblog) ) if( m->respond && !m->ignore && m->init(mblog) )
{ {
mbi = it; mbi = it;
mb = m->mbtcp; mb = m->mbtcp;
m->setUse(true); m->setUse(true);
ptReopen.setTiming( m->channel_timeout ); ptReopen.setTiming( m->channel_timeout );
mblog4 << myname << "(initMB): SELECT CHANNEL " << m->ip << ":" << m->port << endl; mblog4 << myname << "(initMB): SELECT CHANNEL " << m->ip << ":" << m->port << endl;
return m->mbtcp; return m->mbtcp;
} }
} }
// если дошли сюда.. значит не нашли ни одного канала.. // если дошли сюда.. значит не нашли ни одного канала..
// но т.к. мы пропускали те, которые в ignore // но т.к. мы пропускали те, которые в ignore
// значит сейчас просто находим первый у кого есть связь и делаем его главным // значит сейчас просто находим первый у кого есть связь и делаем его главным
for( auto it = mblist.rbegin(); it != mblist.rend(); ++it ) for( auto it = mblist.rbegin(); it != mblist.rend(); ++it )
{ {
auto& m = (*it); auto& m = (*it);
if( m->respond && m->check() && m->init(mblog) ) if( m->respond && m->check() && m->init(mblog) )
{ {
mbi = it; mbi = it;
mb = m->mbtcp; mb = m->mbtcp;
m->ignore = false; m->ignore = false;
m->setUse(true); m->setUse(true);
ptReopen.setTiming( m->channel_timeout ); ptReopen.setTiming( m->channel_timeout );
mblog4 << myname << "(initMB): SELECT CHANNEL " << m->ip << ":" << m->port << endl; mblog4 << myname << "(initMB): SELECT CHANNEL " << m->ip << ":" << m->port << endl;
return m->mbtcp; return m->mbtcp;
} }
} }
// значит всё-таки связи реально нет... // значит всё-таки связи реально нет...
{ {
mbi = mblist.rend(); mbi = mblist.rend();
mb = nullptr; mb = nullptr;
} }
return 0; return 0;
} }
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
void MBTCPMultiMaster::final_thread() void MBTCPMultiMaster::final_thread()
...@@ -462,121 +455,120 @@ void MBTCPMultiMaster::sysCommand( const uniset::SystemMessage* sm ) ...@@ -462,121 +455,120 @@ void MBTCPMultiMaster::sysCommand( const uniset::SystemMessage* sm )
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
void MBTCPMultiMaster::poll_thread() void MBTCPMultiMaster::poll_thread()
{ {
// ждём начала работы..(см. MBExchange::activateObject) // ждём начала работы..(см. MBExchange::activateObject)
while( !isProcActive() && !canceled ) while( !isProcActive() && !canceled )
{ {
uniset::uniset_rwmutex_rlock l(mutex_start); uniset::uniset_rwmutex_rlock l(mutex_start);
} }
// работаем.. // работаем..
while( isProcActive() ) while( isProcActive() )
{ {
try try
{ {
if( sidExchangeMode != DefaultObjectId && force ) if( sidExchangeMode != DefaultObjectId && force )
exchangeMode = shm->localGetValue(itExchangeMode, sidExchangeMode); exchangeMode = shm->localGetValue(itExchangeMode, sidExchangeMode);
} }
catch( std::exception& ex ) catch( std::exception& ex )
{ {
mbwarn << myname << "(poll_thread): " << ex.what() << endl; mbwarn << myname << "(poll_thread): " << ex.what() << endl;
} }
try try
{ {
poll(); poll();
} }
catch( std::exception& ex) catch( std::exception& ex)
{ {
mbwarn << myname << "(poll_thread): " << ex.what() << endl; mbwarn << myname << "(poll_thread): " << ex.what() << endl;
} }
if( !isProcActive() ) if( !isProcActive() )
break; break;
msleep(polltime); msleep(mbconf->polltime);
} }
} }
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
void MBTCPMultiMaster::check_thread() void MBTCPMultiMaster::check_thread()
{ {
while( isProcActive() ) while( isProcActive() )
{ {
for( auto&& it : mblist ) for( auto&& it : mblist )
{ {
try try
{ {
// сбрасываем флаг ignore..раз время вышло. // сбрасываем флаг ignore..раз время вышло.
it->ignore = !it->ptIgnoreTimeout.checkTime(); it->ignore = !it->ptIgnoreTimeout.checkTime();
// Если use=1" связь не проверяем и считаем что связь есть.. // Если use=1" связь не проверяем и считаем что связь есть..
bool r = ( it->use ? true : it->check() ); bool r = ( it->use ? true : it->check() );
mblog4 << myname << "(check): " << it->myname << " " << setw(4) << ( r ? "OK" : "FAIL" ) mblog4 << myname << "(check): " << it->myname << " " << setw(4) << ( r ? "OK" : "FAIL" )
<< " [ respondDelay=" << it->respondDelay.check( r ) << " [ respondDelay=" << it->respondDelay.check( r )
<< " timeout=" << it->channel_timeout << " timeout=" << it->channel_timeout
<< " use=" << it->use << " use=" << it->use
<< " ignore=" << it->ignore << " ignore=" << it->ignore
<< " respond_id=" << it->respond_id << " respond_id=" << it->respond_id
<< " respond_force=" << it->respond_force << " respond_force=" << it->respond_force
<< " respond=" << it->respond << " respond=" << it->respond
<< " respond_invert=" << it->respond_invert << " respond_invert=" << it->respond_invert
<< " activated=" << isProcActive() << " activated=" << isProcActive()
<< " ]" << " ]"
<< endl; << endl;
// задержка на выставление "пропажи связи" // задержка на выставление "пропажи связи"
if( it->respond_init ) if( it->respond_init )
r = it->respondDelay.check( r ); r = it->respondDelay.check( r );
if( !isProcActive() ) if( !isProcActive() )
break; break;
try try
{ {
if( it->respond_id != DefaultObjectId && (it->respond_force || !it->respond_init || r != it->respond) ) if( it->respond_id != DefaultObjectId && (it->respond_force || !it->respond_init || r != it->respond) )
{ {
bool set = it->respond_invert ? !r : r; bool set = it->respond_invert ? !r : r;
shm->localSetValue(it->respond_it, it->respond_id, (set ? 1 : 0), getId()); shm->localSetValue(it->respond_it, it->respond_id, (set ? 1 : 0), getId());
{ {
std::lock_guard<std::mutex> l(it->mutInit); std::lock_guard<std::mutex> l(it->mutInit);
it->respond_init = true; it->respond_init = true;
} }
} }
} }
catch( const uniset::Exception& ex ) catch( const uniset::Exception& ex )
{ {
mbcrit << myname << "(check): (respond) " << it->myname << " : " << ex << std::endl; mbcrit << myname << "(check): (respond) " << it->myname << " : " << ex << std::endl;
} }
catch( const std::exception& ex ) catch( const std::exception& ex )
{ {
mbcrit << myname << "(check): (respond) " << it->myname << " : " << ex.what() << std::endl; mbcrit << myname << "(check): (respond) " << it->myname << " : " << ex.what() << std::endl;
} }
it->respond = r; it->respond = r;
} }
catch( const std::exception& ex ) catch( const std::exception& ex )
{ {
mbcrit << myname << "(check): (respond) " << it->myname << " : " << ex.what() << std::endl; mbcrit << myname << "(check): (respond) " << it->myname << " : " << ex.what() << std::endl;
} }
if( !isProcActive() ) if( !isProcActive() )
break; break;
} }
if( !isProcActive() ) if( !isProcActive() )
break; break;
msleep(checktime); msleep(checktime);
} }
} }
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
void MBTCPMultiMaster::initIterators() void MBTCPMultiMaster::initIterators()
{ {
MBExchange::initIterators(); MBExchange::initIterators();
for( auto&& it : mblist )
for( auto&& it : mblist ) shm->initIterator(it->respond_it);
shm->initIterator(it->respond_it);
} }
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
bool MBTCPMultiMaster::deactivateObject() bool MBTCPMultiMaster::deactivateObject()
...@@ -607,7 +599,7 @@ void MBTCPMultiMaster::initCheckConnectionParameters() ...@@ -607,7 +599,7 @@ void MBTCPMultiMaster::initCheckConnectionParameters()
{ {
auto conf = uniset_conf(); auto conf = uniset_conf();
bool initFromRegMap = ( findArgParam("--" + prefix + "-check-init-from-regmap", conf->getArgc(), conf->getArgv()) != -1 ); bool initFromRegMap = ( findArgParam("--" + mbconf->prefix + "-check-init-from-regmap", conf->getArgc(), conf->getArgv()) != -1 );
if( !initFromRegMap ) if( !initFromRegMap )
return; return;
...@@ -623,18 +615,16 @@ void MBTCPMultiMaster::initCheckConnectionParameters() ...@@ -623,18 +615,16 @@ void MBTCPMultiMaster::initCheckConnectionParameters()
ModbusRTU::ModbusAddr checkAddr = { 0x00 }; ModbusRTU::ModbusAddr checkAddr = { 0x00 };
ModbusRTU::ModbusData checkReg = { 0 }; ModbusRTU::ModbusData checkReg = { 0 };
if( devices.empty() ) if( mbconf->devices.empty() )
{ {
mbwarn << myname << "(init): devices list empty?!" << endl; mbwarn << myname << "(init): devices list empty?!" << endl;
return; return;
} }
// идём по устройствам // идём по устройствам
for( const auto& d : devices ) for( const auto& d : mbconf->devices )
{ {
checkAddr = d.second->mbaddr; checkAddr = d.second->mbaddr;
if( d.second->pollmap.empty() ) if( d.second->pollmap.empty() )
continue; continue;
...@@ -677,13 +667,13 @@ void MBTCPMultiMaster::initCheckConnectionParameters() ...@@ -677,13 +667,13 @@ void MBTCPMultiMaster::initCheckConnectionParameters()
<< " mbreg=" << (int)checkReg << "(" << ModbusRTU::dat2str(checkReg) << ")" << " mbreg=" << (int)checkReg << "(" << ModbusRTU::dat2str(checkReg) << ")"
<< endl; << endl;
// инициализируем.. // инициализируем..
for( auto&& m : mblist ) for( auto&& m : mblist )
{ {
m->checkFunc = checkFunc; m->checkFunc = checkFunc;
m->checkAddr = checkAddr; m->checkAddr = checkAddr;
m->checkReg = checkReg; m->checkReg = checkReg;
} }
} }
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
......
...@@ -4,7 +4,7 @@ libMBMaster_la_LIBADD = $(top_builddir)/lib/libUniSet2.la \ ...@@ -4,7 +4,7 @@ libMBMaster_la_LIBADD = $(top_builddir)/lib/libUniSet2.la \
$(top_builddir)/extensions/lib/libUniSet2Extensions.la \ $(top_builddir)/extensions/lib/libUniSet2Extensions.la \
$(SIGC_LIBS) $(SIGC_LIBS)
libMBMaster_la_CXXFLAGS = -I$(top_builddir)/extensions/include -I$(top_builddir)/extensions/SharedMemory $(SIGC_CFLAGS) libMBMaster_la_CXXFLAGS = -I$(top_builddir)/extensions/include -I$(top_builddir)/extensions/SharedMemory $(SIGC_CFLAGS)
libMBMaster_la_SOURCES = MBExchange.cc RTUStorage.cc libMBMaster_la_SOURCES = MBExchange.cc MBConfig.cc RTUStorage.cc
bin_PROGRAMS = @PACKAGE@-mbtcpmaster @PACKAGE@-mbtcpmultimaster @PACKAGE@-rtuexchange @PACKAGE@-mtr-conv @PACKAGE@-rtu188-state @PACKAGE@-vtconv @PACKAGE@-mtr-setup @PACKAGE@-mtr-read bin_PROGRAMS = @PACKAGE@-mbtcpmaster @PACKAGE@-mbtcpmultimaster @PACKAGE@-rtuexchange @PACKAGE@-mtr-conv @PACKAGE@-rtu188-state @PACKAGE@-vtconv @PACKAGE@-mtr-setup @PACKAGE@-mtr-read
lib_LTLIBRARIES = libUniSet2MBTCPMaster.la libUniSet2RTU.la lib_LTLIBRARIES = libUniSet2MBTCPMaster.la libUniSet2RTU.la
......
...@@ -33,71 +33,63 @@ RTUExchange::RTUExchange(uniset::ObjectId objId, uniset::ObjectId shmId, const s ...@@ -33,71 +33,63 @@ RTUExchange::RTUExchange(uniset::ObjectId objId, uniset::ObjectId shmId, const s
transmitCtl(false), transmitCtl(false),
rs_pre_clean(false) rs_pre_clean(false)
{ {
if( objId == DefaultObjectId ) if( objId == DefaultObjectId )
throw uniset::SystemError("(RTUExchange): objId=-1?!! Use --" + prefix + "-name" ); throw uniset::SystemError("(RTUExchange): objId=-1?!! Use --" + mbconf->prefix + "-name" );
auto conf = uniset_conf(); auto conf = uniset_conf();
// префикс для "свойств" - по умолчанию // префикс для "свойств" - по умолчанию
prop_prefix = ""; mbconf->prop_prefix = "";
// если задано поле для "фильтрации" // если задано поле для "фильтрации"
// то в качестве префикса используем его // то в качестве префикса используем его
if( !s_field.empty() ) if( !mbconf->s_field.empty() )
prop_prefix = s_field + "_"; mbconf->prop_prefix = mbconf->s_field + "_";
// если "принудительно" задан префикс // если "принудительно" задан префикс
// используем его. // используем его.
{ {
string p("--" + prefix + "-set-prop-prefix"); string p("--" + mbconf->prefix + "-set-prop-prefix");
string v = conf->getArgParam(p, ""); string v = conf->getArgParam(p, "");
if( !v.empty() && v[0] != '-' )
prop_prefix = v;
// если параметр всё-таки указан, считаем, что это попытка задать "пустой" префикс
else if( findArgParam(p, conf->getArgc(), conf->getArgv()) != -1 )
prop_prefix = "";
}
mbinfo << myname << "(init): prop_prefix=" << prop_prefix << endl; if( !v.empty() && v[0] != '-' )
mbconf->prop_prefix = v;
// если параметр всё-таки указан, считаем, что это попытка задать "пустой" префикс
else if( findArgParam(p, conf->getArgc(), conf->getArgv()) != -1 )
mbconf->prop_prefix = "";
}
UniXML::iterator it(cnode); mbinfo << myname << "(init): prop_prefix=" << mbconf->prop_prefix << endl;
// ---------- init RS ---------- UniXML::iterator it(cnode);
devname = conf->getArgParam("--" + prefix + "-dev", it.getProp("device")); // ---------- init RS ----------
devname = conf->getArgParam("--" + mbconf->prefix + "-dev", it.getProp("device"));
if( devname.empty() ) if( devname.empty() )
throw uniset::SystemError(myname + "(RTUExchange): Unknown device..." ); throw uniset::SystemError(myname + "(RTUExchange): Unknown device..." );
string speed = conf->getArgParam("--" + prefix + "-speed", it.getProp("speed")); string speed = conf->getArgParam("--" + mbconf->prefix + "-speed", it.getProp("speed"));
if( speed.empty() ) if( speed.empty() )
speed = "38400"; speed = "38400";
use485F = conf->getArgInt("--" + prefix + "-use485F", it.getProp("use485F")); use485F = conf->getArgInt("--" + mbconf->prefix + "-use485F", it.getProp("use485F"));
transmitCtl = conf->getArgInt("--" + prefix + "-transmit-ctl", it.getProp("transmitCtl")); transmitCtl = conf->getArgInt("--" + mbconf->prefix + "-transmit-ctl", it.getProp("transmitCtl"));
defSpeed = ComPort::getSpeed(speed); defSpeed = ComPort::getSpeed(speed);
sleepPause_msec = conf->getArgPInt("--" + prefix + "-sleepPause-usec", it.getProp("slepePause"), 100); mbconf->sleepPause_msec = conf->getArgPInt("--" + mbconf->prefix + "-sleepPause-usec", it.getProp("slepePause"), 100);
rs_pre_clean = conf->getArgInt("--" + prefix + "-pre-clean", it.getProp("pre_clean")); rs_pre_clean = conf->getArgInt("--" + mbconf->prefix + "-pre-clean", it.getProp("pre_clean"));
if( shm->isLocalwork() ) if( shm->isLocalwork() )
{ mbconf->loadConfig(conf->getConfXML(), conf->getXMLSensorsSection());
readConfiguration(); else
ic->addReadItem( sigc::mem_fun(this, &RTUExchange::readItem) );
if( !noQueryOptimization )
rtuQueryOptimization(devices);
initDeviceList();
}
else
ic->addReadItem( sigc::mem_fun(this, &RTUExchange::readItem) );
initMB(false); initMB(false);
if( dlog()->is_info() ) if( dlog()->is_info() )
printMap(devices); MBConfig::printMap(mbconf->devices);
} }
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
void RTUExchange::help_print( int argc, const char* const* argv ) void RTUExchange::help_print( int argc, const char* const* argv )
...@@ -120,68 +112,68 @@ RTUExchange::~RTUExchange() ...@@ -120,68 +112,68 @@ RTUExchange::~RTUExchange()
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
std::shared_ptr<ModbusClient> RTUExchange::initMB( bool reopen ) std::shared_ptr<ModbusClient> RTUExchange::initMB( bool reopen )
{ {
if( !file_exist(devname) ) if( !file_exist(devname) )
{ {
if( mbrtu ) if( mbrtu )
{ {
// delete mbrtu; // delete mbrtu;
mb = 0; mb = 0;
mbrtu = 0; mbrtu = 0;
} }
return mbrtu; return mbrtu;
} }
if( mbrtu ) if( mbrtu )
{ {
if( !reopen ) if( !reopen )
return mbrtu; return mbrtu;
// delete mbrtu; // delete mbrtu;
mbrtu = 0; mbrtu = 0;
mb = 0; mb = 0;
} }
try try
{ {
mbrtu = std::make_shared<ModbusRTUMaster>(devname, use485F, transmitCtl); mbrtu = std::make_shared<ModbusRTUMaster>(devname, use485F, transmitCtl);
if( defSpeed != ComPort::ComSpeed0 ) if( defSpeed != ComPort::ComSpeed0 )
mbrtu->setSpeed(defSpeed); mbrtu->setSpeed(defSpeed);
auto l = loga->create(myname + "-exchangelog"); auto l = loga->create(myname + "-exchangelog");
mbrtu->setLog(l); mbrtu->setLog(l);
if( ic ) if( ic )
ic->logAgregator()->add(loga); ic->logAgregator()->add(loga);
if( recv_timeout > 0 ) if( mbconf->recv_timeout > 0 )
mbrtu->setTimeout(recv_timeout); mbrtu->setTimeout(mbconf->recv_timeout);
mbrtu->setSleepPause(sleepPause_msec); mbrtu->setSleepPause(mbconf->sleepPause_msec);
mbrtu->setAfterSendPause(aftersend_pause); mbrtu->setAfterSendPause(mbconf->aftersend_pause);
mbinfo << myname << "(init): dev=" << devname << " speed=" << ComPort::getSpeed( mbrtu->getSpeed() ) << endl; mbinfo << myname << "(init): dev=" << devname << " speed=" << ComPort::getSpeed( mbrtu->getSpeed() ) << endl;
} }
catch( const uniset::Exception& ex ) catch( const uniset::Exception& ex )
{ {
//if( mbrtu ) //if( mbrtu )
// delete mbrtu; // delete mbrtu;
mbrtu = 0; mbrtu = 0;
mbwarn << myname << "(init): " << ex << endl; mbwarn << myname << "(init): " << ex << endl;
} }
catch(...) catch(...)
{ {
// if( mbrtu ) // if( mbrtu )
// delete mbrtu; // delete mbrtu;
mbrtu = 0; mbrtu = 0;
mbinfo << myname << "(init): catch...." << endl; mbinfo << myname << "(init): catch...." << endl;
} }
mb = mbrtu; mb = mbrtu;
return mbrtu; return mbrtu;
} }
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
void RTUExchange::step() void RTUExchange::step()
...@@ -214,142 +206,142 @@ void RTUExchange::step() ...@@ -214,142 +206,142 @@ void RTUExchange::step()
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
bool RTUExchange::poll() bool RTUExchange::poll()
{ {
if( !mb ) if( !mb )
{ {
mb = initMB(false); mb = initMB(false);
if( !isProcActive() ) if( !isProcActive() )
return false; return false;
updateSM(); updateSM();
allInitOK = false; allInitOK = false;
return false; return false;
} }
if( !allInitOK ) if( !allInitOK )
firstInitRegisters(); firstInitRegisters();
if( !isProcActive() ) if( !isProcActive() )
return false; return false;
ncycle++; ncycle++;
bool allNotRespond = true; bool allNotRespond = true;
ComPort::Speed s = mbrtu->getSpeed(); ComPort::Speed s = mbrtu->getSpeed();
for( auto it1 : devices ) for( auto it1 : mbconf->devices )
{ {
auto d = it1.second; auto d = it1.second;
if( d->mode_id != DefaultObjectId && d->mode == emSkipExchange ) if( d->mode_id != DefaultObjectId && d->mode == MBConfig::emSkipExchange )
continue; continue;
if( d->speed != s ) if( d->speed != s )
{ {
s = d->speed; s = d->speed;
mbrtu->setSpeed(d->speed); mbrtu->setSpeed(d->speed);
} }
d->prev_numreply.store(d->numreply); d->prev_numreply.store(d->numreply);
if( d->dtype == MBExchange::dtRTU188 ) if( d->dtype == MBConfig::dtRTU188 )
{ {
if( !d->rtu188 ) if( !d->rtu188 )
continue; continue;
dlog3 << myname << "(pollRTU188): poll RTU188 " dlog3 << myname << "(pollRTU188): poll RTU188 "
<< " mbaddr=" << ModbusRTU::addr2str(d->mbaddr) << " mbaddr=" << ModbusRTU::addr2str(d->mbaddr)
<< endl; << endl;
try try
{ {
if( rs_pre_clean ) if( rs_pre_clean )
mb->cleanupChannel(); mb->cleanupChannel();
d->rtu188->poll(mbrtu); d->rtu188->poll(mbrtu);
d->numreply++; d->numreply++;
allNotRespond = false; allNotRespond = false;
} }
catch( ModbusRTU::mbException& ex ) catch( ModbusRTU::mbException& ex )
{ {
if( d->numreply != d->prev_numreply ) if( d->numreply != d->prev_numreply )
{ {
dlog3 << myname << "(poll): FAILED ask addr=" << ModbusRTU::addr2str(d->mbaddr) dlog3 << myname << "(poll): FAILED ask addr=" << ModbusRTU::addr2str(d->mbaddr)
<< " -> " << ex << endl; << " -> " << ex << endl;
} }
} }
} }
else else
{ {
dlog3 << myname << "(poll): ask addr=" << ModbusRTU::addr2str(d->mbaddr) dlog3 << myname << "(poll): ask addr=" << ModbusRTU::addr2str(d->mbaddr)
<< " regs=" << d->pollmap.size() << endl; << " regs=" << d->pollmap.size() << endl;
for( auto&& m : d->pollmap ) for( auto&& m : d->pollmap )
{ {
if( m.first != 0 && (ncycle % m.first) != 0 ) if( m.first != 0 && (ncycle % m.first) != 0 )
continue; continue;
auto rmap = m.second; auto rmap = m.second;
for( auto&& it = rmap->begin(); it != rmap->end(); ++it ) for( auto&& it = rmap->begin(); it != rmap->end(); ++it )
{ {
try try
{ {
if( d->dtype == RTUExchange::dtRTU || d->dtype == RTUExchange::dtMTR ) if( d->dtype == MBConfig::dtRTU || d->dtype == MBConfig::dtMTR )
{ {
if( rs_pre_clean ) if( rs_pre_clean )
mb->cleanupChannel(); mb->cleanupChannel();
if( pollRTU(d, it) ) if( pollRTU(d, it) )
{ {
d->numreply++; d->numreply++;
allNotRespond = false; allNotRespond = false;
} }
} }
} }
catch( ModbusRTU::mbException& ex ) catch( ModbusRTU::mbException& ex )
{ {
dlog3 << myname << "(poll): FAILED ask addr=" << ModbusRTU::addr2str(d->mbaddr) dlog3 << myname << "(poll): FAILED ask addr=" << ModbusRTU::addr2str(d->mbaddr)
<< " reg=" << ModbusRTU::dat2str(it->second->mbreg) << " reg=" << ModbusRTU::dat2str(it->second->mbreg)
<< " for sensors: "; << " for sensors: ";
print_plist(dlog()->level3(), it->second->slst); mbconf->print_plist(dlog()->level3(), it->second->slst);
dlog()->level3(false) << " err: " << ex << endl; dlog()->level3(false) << " err: " << ex << endl;
} }
if( it == rmap->end() ) if( it == rmap->end() )
break; break;
if( !isProcActive() ) if( !isProcActive() )
return false; return false;
} }
} }
} }
} }
// update SharedMemory... // update SharedMemory...
updateSM(); updateSM();
// check thresholds // check thresholds
for( auto&& t : thrlist ) for( auto&& t : mbconf->thrlist )
{ {
if( !isProcActive() ) if( !isProcActive() )
return false; return false;
IOBase::processingThreshold(&t, shm, force); IOBase::processingThreshold(&t, shm, force);
} }
if( trReopen.hi(allNotRespond) ) if( trReopen.hi(allNotRespond) )
ptReopen.reset(); ptReopen.reset();
if( allNotRespond && ptReopen.checkTime() ) if( allNotRespond && ptReopen.checkTime() )
{ {
mbwarn << myname << ": REOPEN timeout..(" << ptReopen.getInterval() << ")" << endl; mbwarn << myname << ": REOPEN timeout..(" << ptReopen.getInterval() << ")" << endl;
mb = initMB(true); mb = initMB(true);
ptReopen.reset(); ptReopen.reset();
} }
// printMap(rmap); // printMap(rmap);
return !allNotRespond; return !allNotRespond;
} }
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
std::shared_ptr<RTUExchange> RTUExchange::init_rtuexchange(int argc, const char* const* argv, uniset::ObjectId icID, std::shared_ptr<RTUExchange> RTUExchange::init_rtuexchange(int argc, const char* const* argv, uniset::ObjectId icID,
...@@ -379,36 +371,3 @@ std::shared_ptr<RTUExchange> RTUExchange::init_rtuexchange(int argc, const char* ...@@ -379,36 +371,3 @@ std::shared_ptr<RTUExchange> RTUExchange::init_rtuexchange(int argc, const char*
return make_shared<RTUExchange>(ID, icID, ic, prefix); return make_shared<RTUExchange>(ID, icID, ic, prefix);
} }
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
bool RTUExchange::initDeviceInfo( RTUDeviceMap& m, ModbusRTU::ModbusAddr a, UniXML::iterator& it )
{
if( !MBExchange::initDeviceInfo(m, a, it) )
return false;
auto d = m.find(a);
if( d == m.end() )
{
mbwarn << myname << "(initDeviceInfo): not found device for addr=" << ModbusRTU::addr2str(a) << endl;
return false;
}
string s = it.getProp("speed");
if( !s.empty() )
{
d->second->speed = ComPort::getSpeed(s);
if( d->second->speed == ComPort::ComSpeed0 )
{
d->second->speed = defSpeed;
mbcrit << myname << "(initDeviceInfo): Unknown speed=" << s <<
" for addr=" << ModbusRTU::addr2str(a) << endl;
return false;
}
}
else
d->second->speed = defSpeed;
return true;
}
// -----------------------------------------------------------------------------
...@@ -53,9 +53,7 @@ namespace uniset ...@@ -53,9 +53,7 @@ namespace uniset
virtual void step() override; virtual void step() override;
virtual bool poll() override; virtual bool poll() override;
virtual std::shared_ptr<ModbusClient> initMB( bool reopen = false ) override;
virtual std::shared_ptr<ModbusClient> initMB( bool reopen = false ) override;
virtual bool initDeviceInfo( RTUDeviceMap& m, ModbusRTU::ModbusAddr a, UniXML::iterator& it ) override;
private: private:
RTUExchange(); RTUExchange();
......
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
--mbtcp-persistent-connection 1 \ --mbtcp-persistent-connection 1 \
--ulog-add-levels system \ --ulog-add-levels system \
--mbtcp-run-logserver \ --mbtcp-run-logserver \
--mbtcp-log-add-levels any \
$* $*
#--mbtcp-log-add-levels level4,level3 \ #--mbtcp-log-add-levels level4,level3 \
......
...@@ -10,133 +10,127 @@ ...@@ -10,133 +10,127 @@
/*! Реализация MBTCPTestServer для тестирования */ /*! Реализация MBTCPTestServer для тестирования */
class MBTCPTestServer class MBTCPTestServer
{ {
public: public:
MBTCPTestServer( const std::unordered_set<uniset::ModbusRTU::ModbusAddr>& vaddr, const std::string& inetaddr, int port = 502, bool verbose = false ); MBTCPTestServer( const std::unordered_set<uniset::ModbusRTU::ModbusAddr>& vaddr, const std::string& inetaddr, int port = 502, bool verbose = false );
~MBTCPTestServer(); ~MBTCPTestServer();
inline void setVerbose( bool state ) inline void setVerbose( bool state )
{ {
verbose = state; verbose = state;
} }
inline void setReply( uint32_t val ) inline void setReply( uint32_t val )
{ {
replyVal = val; replyVal = val;
} }
inline void setIgnoreAddrMode( bool state ) void execute(); /*!< основной цикл работы */
{ void setLog( std::shared_ptr<DebugStream> dlog );
if( sslot )
sslot->setIgnoreAddrMode(state); inline bool isRunning()
} {
return ( sslot && sslot->isActive() );
void execute(); /*!< основной цикл работы */ }
void setLog( std::shared_ptr<DebugStream> dlog );
inline void disableExchange( bool set = true )
inline bool isRunning() {
{ disabled = set;
return ( sslot && sslot->isActive() ); }
}
inline bool getForceSingleCoilCmd()
inline void disableExchange( bool set = true ) {
{ return forceSingleCoilCmd;
disabled = set; }
} inline int16_t getLastWriteOutputSingleRegister()
{
inline bool getForceSingleCoilCmd() return lastWriteOutputSingleRegister;
{ }
return forceSingleCoilCmd; inline uniset::ModbusRTU::ForceCoilsMessage getLastForceCoilsQ()
} {
inline int16_t getLastWriteOutputSingleRegister() return lastForceCoilsQ;
{ }
return lastWriteOutputSingleRegister; inline uniset::ModbusRTU::WriteOutputMessage getLastWriteOutput()
} {
inline uniset::ModbusRTU::ForceCoilsMessage getLastForceCoilsQ() return lastWriteOutputQ;
{ }
return lastForceCoilsQ;
} friend std::ostream& operator<<(std::ostream& os, const MBTCPTestServer* m );
inline uniset::ModbusRTU::WriteOutputMessage getLastWriteOutput()
{ inline float getF2TestValue()
return lastWriteOutputQ; {
} return f2_test_value;
}
friend std::ostream& operator<<(std::ostream& os, const MBTCPTestServer* m );
protected:
inline float getF2TestValue() // действия при завершении работы
{ void sigterm( int signo );
return f2_test_value;
} /*! обработка 0x01 */
uniset::ModbusRTU::mbErrCode readCoilStatus( uniset::ModbusRTU::ReadCoilMessage& query,
protected: uniset::ModbusRTU::ReadCoilRetMessage& reply );
// действия при завершении работы /*! обработка 0x02 */
void sigterm( int signo ); uniset::ModbusRTU::mbErrCode readInputStatus( uniset::ModbusRTU::ReadInputStatusMessage& query,
uniset::ModbusRTU::ReadInputStatusRetMessage& reply );
/*! обработка 0x01 */
uniset::ModbusRTU::mbErrCode readCoilStatus( uniset::ModbusRTU::ReadCoilMessage& query, /*! обработка 0x03 */
uniset::ModbusRTU::ReadCoilRetMessage& reply ); uniset::ModbusRTU::mbErrCode readOutputRegisters( uniset::ModbusRTU::ReadOutputMessage& query,
/*! обработка 0x02 */ uniset::ModbusRTU::ReadOutputRetMessage& reply );
uniset::ModbusRTU::mbErrCode readInputStatus( uniset::ModbusRTU::ReadInputStatusMessage& query,
uniset::ModbusRTU::ReadInputStatusRetMessage& reply ); /*! обработка 0x04 */
uniset::ModbusRTU::mbErrCode readInputRegisters( uniset::ModbusRTU::ReadInputMessage& query,
/*! обработка 0x03 */ uniset::ModbusRTU::ReadInputRetMessage& reply );
uniset::ModbusRTU::mbErrCode readOutputRegisters( uniset::ModbusRTU::ReadOutputMessage& query,
uniset::ModbusRTU::ReadOutputRetMessage& reply ); /*! обработка 0x05 */
uniset::ModbusRTU::mbErrCode forceSingleCoil( uniset::ModbusRTU::ForceSingleCoilMessage& query,
/*! обработка 0x04 */ uniset::ModbusRTU::ForceSingleCoilRetMessage& reply );
uniset::ModbusRTU::mbErrCode readInputRegisters( uniset::ModbusRTU::ReadInputMessage& query,
uniset::ModbusRTU::ReadInputRetMessage& reply ); /*! обработка 0x0F */
uniset::ModbusRTU::mbErrCode forceMultipleCoils( uniset::ModbusRTU::ForceCoilsMessage& query,
/*! обработка 0x05 */ uniset::ModbusRTU::ForceCoilsRetMessage& reply );
uniset::ModbusRTU::mbErrCode forceSingleCoil( uniset::ModbusRTU::ForceSingleCoilMessage& query,
uniset::ModbusRTU::ForceSingleCoilRetMessage& reply );
/*! обработка 0x10 */
/*! обработка 0x0F */ uniset::ModbusRTU::mbErrCode writeOutputRegisters( uniset::ModbusRTU::WriteOutputMessage& query,
uniset::ModbusRTU::mbErrCode forceMultipleCoils( uniset::ModbusRTU::ForceCoilsMessage& query, uniset::ModbusRTU::WriteOutputRetMessage& reply );
uniset::ModbusRTU::ForceCoilsRetMessage& reply );
/*! обработка 0x06 */
uniset::ModbusRTU::mbErrCode writeOutputSingleRegister( uniset::ModbusRTU::WriteSingleOutputMessage& query,
/*! обработка 0x10 */ uniset::ModbusRTU::WriteSingleOutputRetMessage& reply );
uniset::ModbusRTU::mbErrCode writeOutputRegisters( uniset::ModbusRTU::WriteOutputMessage& query,
uniset::ModbusRTU::WriteOutputRetMessage& reply );
uniset::ModbusRTU::mbErrCode diagnostics( uniset::ModbusRTU::DiagnosticMessage& query,
/*! обработка 0x06 */ uniset::ModbusRTU::DiagnosticRetMessage& reply );
uniset::ModbusRTU::mbErrCode writeOutputSingleRegister( uniset::ModbusRTU::WriteSingleOutputMessage& query,
uniset::ModbusRTU::WriteSingleOutputRetMessage& reply ); uniset::ModbusRTU::mbErrCode read4314( uniset::ModbusRTU::MEIMessageRDI& query,
uniset::ModbusRTU::MEIMessageRetRDI& reply );
uniset::ModbusRTU::mbErrCode diagnostics( uniset::ModbusRTU::DiagnosticMessage& query, /*! обработка запросов на чтение ошибок */
uniset::ModbusRTU::DiagnosticRetMessage& reply ); uniset::ModbusRTU::mbErrCode journalCommand( uniset::ModbusRTU::JournalCommandMessage& query,
uniset::ModbusRTU::JournalCommandRetMessage& reply );
uniset::ModbusRTU::mbErrCode read4314( uniset::ModbusRTU::MEIMessageRDI& query,
uniset::ModbusRTU::MEIMessageRetRDI& reply ); /*! обработка запроса на установку времени */
uniset::ModbusRTU::mbErrCode setDateTime( uniset::ModbusRTU::SetDateTimeMessage& query,
/*! обработка запросов на чтение ошибок */ uniset::ModbusRTU::SetDateTimeRetMessage& reply );
uniset::ModbusRTU::mbErrCode journalCommand( uniset::ModbusRTU::JournalCommandMessage& query,
uniset::ModbusRTU::JournalCommandRetMessage& reply ); /*! обработка запроса удалённого сервиса */
uniset::ModbusRTU::mbErrCode remoteService( uniset::ModbusRTU::RemoteServiceMessage& query,
/*! обработка запроса на установку времени */ uniset::ModbusRTU::RemoteServiceRetMessage& reply );
uniset::ModbusRTU::mbErrCode setDateTime( uniset::ModbusRTU::SetDateTimeMessage& query,
uniset::ModbusRTU::SetDateTimeRetMessage& reply ); uniset::ModbusRTU::mbErrCode fileTransfer( uniset::ModbusRTU::FileTransferMessage& query,
uniset::ModbusRTU::FileTransferRetMessage& reply );
/*! обработка запроса удалённого сервиса */
uniset::ModbusRTU::mbErrCode remoteService( uniset::ModbusRTU::RemoteServiceMessage& query,
uniset::ModbusRTU::RemoteServiceRetMessage& reply ); /*! интерфейс ModbusSlave для обмена по RS */
uniset::ModbusTCPServerSlot* sslot;
uniset::ModbusRTU::mbErrCode fileTransfer( uniset::ModbusRTU::FileTransferMessage& query, std::unordered_set<uniset::ModbusRTU::ModbusAddr> vaddr; /*!< адреса данного узла */
uniset::ModbusRTU::FileTransferRetMessage& reply );
bool verbose;
uint32_t replyVal;
/*! интерфейс ModbusSlave для обмена по RS */ bool forceSingleCoilCmd;
uniset::ModbusTCPServerSlot* sslot; int16_t lastWriteOutputSingleRegister;
std::unordered_set<uniset::ModbusRTU::ModbusAddr> vaddr; /*!< адреса данного узла */ uniset::ModbusRTU::ForceCoilsMessage lastForceCoilsQ;
uniset::ModbusRTU::WriteOutputMessage lastWriteOutputQ;
bool verbose; float f2_test_value = {0.0};
uint32_t replyVal;
bool forceSingleCoilCmd;
int16_t lastWriteOutputSingleRegister;
uniset::ModbusRTU::ForceCoilsMessage lastForceCoilsQ;
uniset::ModbusRTU::WriteOutputMessage lastWriteOutputQ;
float f2_test_value = {0.0};
#if 0 #if 0
typedef std::map<uniset::ModbusRTU::mbErrCode, unsigned int> ExchangeErrorMap; typedef std::map<uniset::ModbusRTU::mbErrCode, unsigned int> ExchangeErrorMap;
......
...@@ -124,15 +124,17 @@ ...@@ -124,15 +124,17 @@
<item id="1054" safeval="1" mb="1" mbtype="rtu" mbaddr="0x02" mbreg="11" mbfunc="0x02" iotype="DI" name="SafeMode2_TestRead02" textname="(safemode): Тестовый регистр для 0x02"/> <item id="1054" safeval="1" mb="1" mbtype="rtu" mbaddr="0x02" mbreg="11" mbfunc="0x02" iotype="DI" name="SafeMode2_TestRead02" textname="(safemode): Тестовый регистр для 0x02"/>
<!-- query optimization test --> <!-- query optimization test -->
<item id="1060" mb="1" mbtype="rtu" mbaddr="0x01" mbreg="200" mbfunc="0x03" vtype="F2" iotype="AI" name="TestQueryOptimization1_F2" textname="F2: Тестовый регистр для 0x03"/> <item id="1060" mb="1" mbtype="rtu" mbaddr="0x01" mbreg="200" mbfunc="0x03" vtype="F2" iotype="AI" name="TestQueryOptimization1_F2" textname="F2: Тестовый регистр для 0x03"/>
<item id="1061" mb="1" mbtype="rtu" mbaddr="0x01" mbreg="202" mbfunc="0x03" vtype="F2" iotype="AI" name="TestQueryOptimization2_F2" textname="F2: Тестовый регистр для 0x03"/> <item id="1061" mb="1" mbtype="rtu" mbaddr="0x01" mbreg="202" mbfunc="0x03" vtype="F2" iotype="AI" name="TestQueryOptimization2_F2" textname="F2: Тестовый регистр для 0x03"/>
<item id="1062" mb="1" mbtype="rtu" mbaddr="0x01" mbreg="204" mbfunc="0x03" vtype="F2" iotype="AI" name="TestQueryOptimization3_F2" textname="F2: Тестовый регистр для 0x03"/> <item id="1062" mb="1" mbtype="rtu" mbaddr="0x01" mbreg="204" mbfunc="0x03" vtype="F2" iotype="AI" name="TestQueryOptimization3_F2" textname="F2: Тестовый регистр для 0x03"/>
<item id="1063" mb="1" mbtype="rtu" mbaddr="0x01" mbreg="206" mbfunc="0x03" vtype="F2" iotype="AI" name="TestQueryOptimization4_F2" textname="F2: Тестовый регистр для 0x03"/> <item id="1063" mb="1" mbtype="rtu" mbaddr="0x01" mbreg="206" mbfunc="0x03" vtype="F2" iotype="AI" name="TestQueryOptimization4_F2" textname="F2: Тестовый регистр для 0x03"/>
<item id="1064" mb="1" mbtype="rtu" mbaddr="0x01" mbreg="208" mbfunc="0x03" vtype="F2" iotype="AI" name="TestQueryOptimization5_F2" textname="F2: Тестовый регистр для 0x03"/> <item id="1064" mb="1" mbtype="rtu" mbaddr="0x01" mbreg="208" mbfunc="0x03" vtype="F2" iotype="AI" name="TestQueryOptimization5_F2" textname="F2: Тестовый регистр для 0x03"/>
<item id="1065" mb="1" mbtype="rtu" mbaddr="0x01" mbreg="210" mbfunc="0x03" vtype="F2" iotype="AI" name="TestQueryOptimization6_F2" textname="F2: Тестовый регистр для 0x03"/> <item id="1065" mb="1" mbtype="rtu" mbaddr="0x01" mbreg="210" mbfunc="0x03" vtype="F2" iotype="AI" name="TestQueryOptimization6_F2" textname="F2: Тестовый регистр для 0x03"/>
<!-- undefined state --> <!-- undefined state -->
<item id="1070" mb="1" mbtype="rtu" mbaddr="0x01" mbreg="240" mbfunc="0x03" default="150" undefined_value="65535" breaklim="100" name="UndefinedState_FS" iotype="AI" textname="Тестирование неопределённого состояния"/> <item id="1070" mb="1" mbtype="rtu" mbaddr="0x01" mbreg="240" mbfunc="0x03" default="150" undefined_value="65535" breaklim="100" name="UndefinedState_FS" iotype="AI" textname="Тестирование неопределённого состояния"/>
<item id="1080" default="1080" name="Reload1_FS" iotype="AI" textname="Тестирование перезагрузки конфига"/>
<item id="10000" iotype="DI" name="TestMode_S" textname="Тестовый датчик"/> <item id="10000" iotype="DI" name="TestMode_S" textname="Тестовый датчик"/>
......
<?xml version="1.0" encoding="utf-8"?>
<UNISETPLC xmlns:xi="http://www.w3.org/2001/XInclude">
<UserData/>
<!-- Общие(стартовые) параметры по UniSet -->
<UniSet>
<NameService host="localhost" port="2809"/>
<LocalNode name="LocalhostNode"/>
<RootSection name="UNISET_PLC"/>
<CountOfNet name="1"/>
<RepeatCount name="3"/>
<RepeatTimeoutMS name="50"/>
<WatchDogTime name="0"/>
<PingNodeTime name="0"/>
<AutoStartUpTime name="1"/>
<DumpStateTime name="10"/>
<SleepTickMS name="500"/>
<UniSetDebug levels="" name="ulog"/>
<ConfDir name="./"/>
<DataDir name="./"/>
<BinDir name="./"/>
<LogDir name="./"/>
<DocDir name="./"/>
<LockDir name="./"/>
<Services></Services>
</UniSet>
<dlog name="dlog"/>
<settings>
<SharedMemory name="SharedMemory" shmID="SharedMemory"/>
<MBTCPMaster1 name="MBTCPMaster1" exchangeModeID="MBTCPMaster_Mode_AS">
<DeviceList>
<item addr="0x01" timeout="1000" invert="1" respondSensor="Slave_Not_Respond_S" safemodeSensor="Slave1_SafemodeSensor_S" safemodeValue="42"/>
<item addr="0x02" timeout="1000" safemodeResetIfNotRespond="1"/>
<item addr="0x03" timeout="500"/>
</DeviceList>
</MBTCPMaster1>
<MBTCPMultiMaster1 name="MBTCPMultiMaster1" poll_time="200" reply_timeout="60">
<DeviceList>
<item addr="0x01" invert="1" respondSensor="Slave_Not_Respond_S" timeout="1000" safemodeSensor="Slave1_SafemodeSensor_S" safemodeValue="42" />
<item addr="0x02" timeout="1000" safemodeResetIfNotRespond="1"/>
</DeviceList>
<GateList>
<item ip="127.0.0.1" port="20053" recv_timeout="200" invert="1" respondSensor="Slave1_Not_Respond_S"/>
<item ip="127.0.0.1" port="20055" recv_timeout="200" invert="1" respondSensor="Slave2_Not_Respond_S"/>
</GateList>
</MBTCPMultiMaster1>
</settings>
<ObjectsMap idfromfile="1">
<!--
Краткие пояснения к полям секции 'sensors'
==========================================
node - узел на котором физически находится данный датчик
iotype - тип датчика
priority - приоритет сообщения об изменении данного датчика
textname - текстовое имя датчика
-->
<nodes port="2809">
<item id="3000" infserver="InfoServer" ip="127.0.0.1" name="LocalhostNode" textname="Локальный узел"/>
</nodes>
<!-- ************************ Датчики ********************** -->
<sensors name="Sensors">
<!-- reload conf -->
<item id="1080" mb="1" mbtype="rtu" mbaddr="0x03" mbreg="340" mbfunc="0x03" name="Reload1_FS" iotype="AI" textname="Тестирование перезагрузки конфига"/>
</sensors>
<thresholds/>
<controllers name="Controllers">
<item id="5000" name="SharedMemory"/>
</controllers>
<!-- ******************* Идентификаторы сервисов ***************** -->
<services name="Services">
</services>
<!-- ******************* Идентификаторы объектов ***************** -->
<objects name="UniObjects">
<item id="6000" name="TestProc"/>
<item id="6004" name="MBTCPMaster1"/>
<item id="6005" name="MBTCPMultiMaster1"/>
</objects>
</ObjectsMap>
<messages idfromfile="1" name="messages"/>
</UNISETPLC>
...@@ -15,6 +15,7 @@ using namespace uniset; ...@@ -15,6 +15,7 @@ using namespace uniset;
using namespace uniset::extensions; using namespace uniset::extensions;
// -------------------------------------------------------------------------- // --------------------------------------------------------------------------
std::shared_ptr<SharedMemory> shm; std::shared_ptr<SharedMemory> shm;
std::shared_ptr<MBTCPMaster> mbm;
// -------------------------------------------------------------------------- // --------------------------------------------------------------------------
int main( int argc, const char* argv[] ) int main( int argc, const char* argv[] )
{ {
...@@ -46,15 +47,14 @@ int main( int argc, const char* argv[] ) ...@@ -46,15 +47,14 @@ int main( int argc, const char* argv[] )
if( !shm ) if( !shm )
return 1; return 1;
auto mb = MBTCPMaster::init_mbmaster(argc, argv, shm->getId(), (apart ? nullptr : shm )); mbm = MBTCPMaster::init_mbmaster(argc, argv, shm->getId(), (apart ? nullptr : shm ));
if( !mb ) if( !mbm )
return 1; return 1;
auto act = UniSetActivator::Instance(); auto act = UniSetActivator::Instance();
act->add(shm);
act->add(shm); act->add(mbm);
act->add(mb);
SystemMessage sm(SystemMessage::StartUp); SystemMessage sm(SystemMessage::StartUp);
act->broadcast( sm.transport_msg() ); act->broadcast( sm.transport_msg() );
......
...@@ -15,8 +15,8 @@ cd - ...@@ -15,8 +15,8 @@ cd -
--mbtcp-filter-value 1 \ --mbtcp-filter-value 1 \
--mbtcp-gateway-iaddr localhost \ --mbtcp-gateway-iaddr localhost \
--mbtcp-gateway-port 20048 \ --mbtcp-gateway-port 20048 \
--mbtcp-polltime 50 --mbtcp-recv-timeout 500 --mbtcp-polltime 50 --mbtcp-recv-timeout 500
#--mbtcp-log-add-levels any # --mbtcp-log-add-levels any
#--mbtcp-default-mbinit-ok 1 #--mbtcp-default-mbinit-ok 1
#--dlog-add-levels any #--dlog-add-levels any
......
...@@ -8,6 +8,8 @@ ...@@ -8,6 +8,8 @@
#include "UniSetTypes.h" #include "UniSetTypes.h"
#include "MBTCPTestServer.h" #include "MBTCPTestServer.h"
#include "MBTCPMultiMaster.h" #include "MBTCPMultiMaster.h"
#include "MBTCPMaster.h"
#include "UniSetActivator.h"
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
using namespace std; using namespace std;
using namespace uniset; using namespace uniset;
...@@ -15,7 +17,7 @@ using namespace uniset; ...@@ -15,7 +17,7 @@ using namespace uniset;
static ModbusRTU::ModbusAddr slaveADDR = 0x01; // conf->getArgInt("--mbs-my-addr"); static ModbusRTU::ModbusAddr slaveADDR = 0x01; // conf->getArgInt("--mbs-my-addr");
static int port = 20048; // conf->getArgInt("--mbs-inet-port"); static int port = 20048; // conf->getArgInt("--mbs-inet-port");
static string iaddr("127.0.0.1"); // conf->getArgParam("--mbs-inet-addr"); static string iaddr("127.0.0.1"); // conf->getArgParam("--mbs-inet-addr");
static unordered_set<ModbusRTU::ModbusAddr> vaddr = { slaveADDR, 0x02 }; static unordered_set<ModbusRTU::ModbusAddr> vaddr = { slaveADDR, 0x02, 0x03 };
static shared_ptr<MBTCPTestServer> mbs; static shared_ptr<MBTCPTestServer> mbs;
static shared_ptr<UInterface> ui; static shared_ptr<UInterface> ui;
static std::shared_ptr<SMInterface> smi; static std::shared_ptr<SMInterface> smi;
...@@ -23,64 +25,68 @@ static ObjectId mbID = 6004; // MBTCPMaster1 ...@@ -23,64 +25,68 @@ static ObjectId mbID = 6004; // MBTCPMaster1
static int polltime = 100; // conf->getArgInt("--mbtcp-polltime"); static int polltime = 100; // conf->getArgInt("--mbtcp-polltime");
static ObjectId slaveNotRespond = 10; // Slave_Not_Respond_S static ObjectId slaveNotRespond = 10; // Slave_Not_Respond_S
static const ObjectId exchangeMode = 11; // MBTCPMaster_Mode_AS static const ObjectId exchangeMode = 11; // MBTCPMaster_Mode_AS
static const string confile2 = "mbmaster-test-configure2.xml";
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
extern std::shared_ptr<SharedMemory> shm; extern std::shared_ptr<SharedMemory> shm;
extern std::shared_ptr<MBTCPMaster> mbm;
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
static void InitTest() static void InitTest()
{ {
auto conf = uniset_conf(); auto conf = uniset_conf();
CHECK( conf != nullptr ); CHECK( conf != nullptr );
if( !ui ) if( !ui )
{ {
ui = make_shared<UInterface>(); ui = make_shared<UInterface>();
// UI понадобиться для проверки записанных в SM значений. // UI понадобиться для проверки записанных в SM значений.
CHECK( ui->getObjectIndex() != nullptr ); CHECK( ui->getObjectIndex() != nullptr );
CHECK( ui->getConf() == conf ); CHECK( ui->getConf() == conf );
CHECK( ui->waitReady(slaveNotRespond, 8000) ); CHECK( ui->waitReady(slaveNotRespond, 8000) );
} }
if( !smi ) if( !smi )
{ {
if( shm == nullptr ) if( shm == nullptr )
throw SystemError("SharedMemory don`t initialize.."); throw SystemError("SharedMemory don`t initialize..");
if( ui == nullptr ) if( ui == nullptr )
throw SystemError("UInterface don`t initialize.."); throw SystemError("UInterface don`t initialize..");
smi = make_shared<SMInterface>(shm->getId(), ui, mbID, shm ); smi = make_shared<SMInterface>(shm->getId(), ui, mbID, shm );
} }
if( !mbs ) if( !mbs )
{ {
try try
{ {
mbs = make_shared<MBTCPTestServer>(vaddr, iaddr, port, false); mbs = make_shared<MBTCPTestServer>(vaddr, iaddr, port, false);
} }
catch( const Poco::Net::NetException& e ) catch( const Poco::Net::NetException& e )
{ {
ostringstream err; ostringstream err;
err << "(mbs): Can`t create socket " << iaddr << ":" << port << " err: " << e.message() << endl; err << "(mbs): Can`t create socket " << iaddr << ":" << port << " err: " << e.message() << endl;
cerr << err.str() << endl; cerr << err.str() << endl;
throw SystemError(err.str()); throw SystemError(err.str());
} }
catch( const std::exception& ex ) catch( const std::exception& ex )
{ {
cerr << "(mbs): Can`t create socket " << iaddr << ":" << port << " err: " << ex.what() << endl; cerr << "(mbs): Can`t create socket " << iaddr << ":" << port << " err: " << ex.what() << endl;
throw; throw;
} }
// mbs->setVerbose(true); // mbs->setVerbose(true);
CHECK( mbs != nullptr ); CHECK( mbs != nullptr );
mbs->execute(); mbs->execute();
for( int i = 0; !mbs->isRunning() && i < 10; i++ ) for( int i = 0; !mbs->isRunning() && i < 10; i++ )
msleep(200); msleep(200);
CHECK( mbs->isRunning() ); CHECK( mbs->isRunning() );
msleep(2000); msleep(2000);
CHECK( ui->getValue(slaveNotRespond) == 0 ); CHECK( ui->getValue(slaveNotRespond) == 0 );
} }
REQUIRE( mbm != nullptr );
} }
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
TEST_CASE("MBTCPMaster: reconnect", "[modbus][mbmaster][mbtcpmaster]") TEST_CASE("MBTCPMaster: reconnect", "[modbus][mbmaster][mbtcpmaster]")
...@@ -486,139 +492,139 @@ TEST_CASE("MBTCPMaster: 0x10 (write register outputs or memories)", "[modbus][0x ...@@ -486,139 +492,139 @@ TEST_CASE("MBTCPMaster: 0x10 (write register outputs or memories)", "[modbus][0x
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
TEST_CASE("MBTCPMaster: exchangeMode", "[modbus][exchangemode][mbmaster][mbtcpmaster]") TEST_CASE("MBTCPMaster: exchangeMode", "[modbus][exchangemode][mbmaster][mbtcpmaster]")
{ {
InitTest(); InitTest();
SECTION("None") SECTION("None")
{ {
SECTION("read") SECTION("read")
{ {
mbs->setReply(10); mbs->setReply(10);
msleep(polltime + 200); msleep(polltime + 200);
REQUIRE( ui->getValue(1003) == 10 ); REQUIRE( ui->getValue(1003) == 10 );
} }
SECTION("write") SECTION("write")
{ {
ui->setValue(1018, 10); ui->setValue(1018, 10);
REQUIRE( ui->getValue(1018) == 10 ); REQUIRE( ui->getValue(1018) == 10 );
msleep(polltime + 200); msleep(polltime + 200);
REQUIRE( mbs->getLastWriteOutputSingleRegister() == 10 ); REQUIRE( mbs->getLastWriteOutputSingleRegister() == 10 );
} }
} }
SECTION("WriteOnly") SECTION("WriteOnly")
{ {
// emWriteOnly=1, /*!< "только посылка данных" (работают только write-функции) */ // emWriteOnly=1, /*!< "только посылка данных" (работают только write-функции) */
ui->setValue(exchangeMode, MBExchange::emWriteOnly ); ui->setValue(exchangeMode, MBConfig::emWriteOnly );
REQUIRE( ui->getValue(exchangeMode) == MBExchange::emWriteOnly ); REQUIRE( ui->getValue(exchangeMode) == MBConfig::emWriteOnly );
SECTION("read") SECTION("read")
{ {
mbs->setReply(150); mbs->setReply(150);
msleep(2 * polltime + 200); msleep(2 * polltime + 200);
REQUIRE( ui->getValue(1003) != 150 ); REQUIRE( ui->getValue(1003) != 150 );
mbs->setReply(-10); mbs->setReply(-10);
msleep(2 * polltime + 200); msleep(2 * polltime + 200);
REQUIRE( ui->getValue(1003) != -10 ); REQUIRE( ui->getValue(1003) != -10 );
REQUIRE( ui->getValue(1003) != 150 ); REQUIRE( ui->getValue(1003) != 150 );
} }
SECTION("write") SECTION("write")
{ {
ui->setValue(1018, 150); ui->setValue(1018, 150);
REQUIRE( ui->getValue(1018) == 150 ); REQUIRE( ui->getValue(1018) == 150 );
msleep(polltime + 200); msleep(polltime + 200);
REQUIRE( mbs->getLastWriteOutputSingleRegister() == 150 ); REQUIRE( mbs->getLastWriteOutputSingleRegister() == 150 );
ui->setValue(1018, 155); ui->setValue(1018, 155);
REQUIRE( ui->getValue(1018) == 155 ); REQUIRE( ui->getValue(1018) == 155 );
msleep(polltime + 200); msleep(polltime + 200);
REQUIRE( mbs->getLastWriteOutputSingleRegister() == 155 ); REQUIRE( mbs->getLastWriteOutputSingleRegister() == 155 );
} }
} }
SECTION("ReadOnly") SECTION("ReadOnly")
{ {
// emReadOnly=2, /*!< "только чтение" (работают только read-функции) */ // emReadOnly=2, /*!< "только чтение" (работают только read-функции) */
ui->setValue(exchangeMode, MBExchange::emReadOnly ); ui->setValue(exchangeMode, MBConfig::emReadOnly );
REQUIRE( ui->getValue(exchangeMode) == MBExchange::emReadOnly ); REQUIRE( ui->getValue(exchangeMode) == MBConfig::emReadOnly );
SECTION("read") SECTION("read")
{ {
mbs->setReply(150); mbs->setReply(150);
msleep(polltime + 200); msleep(polltime + 200);
REQUIRE( ui->getValue(1003) == 150 ); REQUIRE( ui->getValue(1003) == 150 );
mbs->setReply(-100); mbs->setReply(-100);
msleep(polltime + 200); msleep(polltime + 200);
REQUIRE( ui->getValue(1003) == -100 ); REQUIRE( ui->getValue(1003) == -100 );
} }
SECTION("write") SECTION("write")
{ {
ui->setValue(1018, 50); ui->setValue(1018, 50);
REQUIRE( ui->getValue(1018) == 50 ); REQUIRE( ui->getValue(1018) == 50 );
msleep(2 * polltime + 200); msleep(2 * polltime + 200);
REQUIRE( mbs->getLastWriteOutputSingleRegister() != 50 ); REQUIRE( mbs->getLastWriteOutputSingleRegister() != 50 );
ui->setValue(1018, 55); ui->setValue(1018, 55);
REQUIRE( ui->getValue(1018) == 55 ); REQUIRE( ui->getValue(1018) == 55 );
msleep(2 * polltime + 200); msleep(2 * polltime + 200);
REQUIRE( mbs->getLastWriteOutputSingleRegister() != 55 ); REQUIRE( mbs->getLastWriteOutputSingleRegister() != 55 );
REQUIRE( mbs->getLastWriteOutputSingleRegister() != 50 ); REQUIRE( mbs->getLastWriteOutputSingleRegister() != 50 );
} }
} }
SECTION("SkipSaveToSM") SECTION("SkipSaveToSM")
{ {
// emSkipSaveToSM=3, /*!< не писать данные в SM (при этом работают и read и write функции */ // emSkipSaveToSM=3, /*!< не писать данные в SM (при этом работают и read и write функции */
ui->setValue(exchangeMode, MBExchange::emSkipSaveToSM ); ui->setValue(exchangeMode, MBConfig::emSkipSaveToSM );
REQUIRE( ui->getValue(exchangeMode) == MBExchange::emSkipSaveToSM ); REQUIRE( ui->getValue(exchangeMode) == MBConfig::emSkipSaveToSM );
SECTION("read") SECTION("read")
{ {
mbs->setReply(50); mbs->setReply(50);
msleep(polltime + 200); msleep(polltime + 200);
REQUIRE( ui->getValue(1003) != 50 ); REQUIRE( ui->getValue(1003) != 50 );
} }
SECTION("write") SECTION("write")
{ {
// а write работает в этом режиме.. (а чем отличается от writeOnly?) // а write работает в этом режиме.. (а чем отличается от writeOnly?)
ui->setValue(1018, 60); ui->setValue(1018, 60);
REQUIRE( ui->getValue(1018) == 60 ); REQUIRE( ui->getValue(1018) == 60 );
msleep(polltime + 200); msleep(polltime + 200);
REQUIRE( mbs->getLastWriteOutputSingleRegister() == 60 ); REQUIRE( mbs->getLastWriteOutputSingleRegister() == 60 );
ui->setValue(1018, 65); ui->setValue(1018, 65);
REQUIRE( ui->getValue(1018) == 65 ); REQUIRE( ui->getValue(1018) == 65 );
msleep(polltime + 200); msleep(polltime + 200);
REQUIRE( mbs->getLastWriteOutputSingleRegister() == 65 ); REQUIRE( mbs->getLastWriteOutputSingleRegister() == 65 );
} }
} }
SECTION("SkipExchange") SECTION("SkipExchange")
{ {
// emSkipExchange=4 /*!< отключить обмен */ // emSkipExchange=4 /*!< отключить обмен */
ui->setValue(exchangeMode, MBExchange::emSkipExchange ); ui->setValue(exchangeMode, MBConfig::emSkipExchange );
REQUIRE( ui->getValue(exchangeMode) == MBExchange::emSkipExchange ); REQUIRE( ui->getValue(exchangeMode) == MBConfig::emSkipExchange );
SECTION("read") SECTION("read")
{ {
mbs->setReply(70); mbs->setReply(70);
msleep(polltime + 200); msleep(polltime + 200);
REQUIRE( ui->getValue(1003) != 70 ); REQUIRE( ui->getValue(1003) != 70 );
} }
SECTION("write") SECTION("write")
{ {
ui->setValue(1018, 70); ui->setValue(1018, 70);
REQUIRE( ui->getValue(1018) == 70 ); REQUIRE( ui->getValue(1018) == 70 );
msleep(polltime + 200); msleep(polltime + 200);
REQUIRE( mbs->getLastWriteOutputSingleRegister() != 70 ); REQUIRE( mbs->getLastWriteOutputSingleRegister() != 70 );
} }
SECTION("check connection") SECTION("check connection")
{ {
msleep(1100); msleep(1100);
CHECK( ui->getValue(slaveNotRespond) == 1 ); CHECK( ui->getValue(slaveNotRespond) == 1 );
ui->setValue(exchangeMode, MBExchange::emNone ); ui->setValue(exchangeMode, MBConfig::emNone );
REQUIRE( ui->getValue(exchangeMode) == MBExchange::emNone ); REQUIRE( ui->getValue(exchangeMode) == MBConfig::emNone );
msleep(1100); msleep(1100);
CHECK( ui->getValue(slaveNotRespond) == 0 ); CHECK( ui->getValue(slaveNotRespond) == 0 );
} }
} }
} }
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
TEST_CASE("MBTCPMaster: check respond resnsor", "[modbus][respond][mbmaster][mbtcpmaster]") TEST_CASE("MBTCPMaster: check respond resnsor", "[modbus][respond][mbmaster][mbtcpmaster]")
...@@ -766,6 +772,23 @@ TEST_CASE("MBTCPMaster: udefined value", "[modbus][undefined][mbmaster][mbtcpmas ...@@ -766,6 +772,23 @@ TEST_CASE("MBTCPMaster: udefined value", "[modbus][undefined][mbmaster][mbtcpmas
REQUIRE( ui->getValue(1070) == 120 ); REQUIRE( ui->getValue(1070) == 120 );
} }
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
TEST_CASE("MBTCPMaster: reload config", "[modbus][reload][mbmaster][mbtcpmaster]")
{
InitTest();
mbs->setReply(160);
msleep(polltime + 200);
REQUIRE( ui->getValue(1070) == 160 );
REQUIRE( ui->getValue(1080) == 1080 );
// add new mbreg
REQUIRE( mbm->reconfigure(confile2) );
msleep(polltime + 600);
REQUIRE( ui->getValue(1080) == 160 );
}
// -----------------------------------------------------------------------------
#if 0 #if 0
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
static bool init_iobase( IOBase* ib, const std::string& sensor ) static bool init_iobase( IOBase* ib, const std::string& sensor )
......
...@@ -187,7 +187,7 @@ class UObject_SK: ...@@ -187,7 +187,7 @@ class UObject_SK:
virtual void httpGetUserData( Poco::JSON::Object::Ptr& jdata ) {} /*!< для пользовательских данных в httpGet() */ virtual void httpGetUserData( Poco::JSON::Object::Ptr& jdata ) {} /*!< для пользовательских данных в httpGet() */
virtual Poco::JSON::Object::Ptr httpDumpIO(); virtual Poco::JSON::Object::Ptr httpDumpIO();
virtual Poco::JSON::Object::Ptr httpRequestLog( const Poco::URI::QueryParameters& p ); virtual Poco::JSON::Object::Ptr httpRequestLog( const Poco::URI::QueryParameters& p );
virtual Poco::JSON::Object::Ptr request_conf_set( const std::string& req, const Poco::URI::QueryParameters& p ); virtual Poco::JSON::Object::Ptr request_conf_set( const std::string& req, const Poco::URI::QueryParameters& p ) override;
#endif #endif
// Выполнение очередного шага программы // Выполнение очередного шага программы
......
...@@ -705,12 +705,47 @@ Poco::JSON::Object::Ptr UObject_SK::httpRequestLog( const Poco::URI::QueryParame ...@@ -705,12 +705,47 @@ Poco::JSON::Object::Ptr UObject_SK::httpRequestLog( const Poco::URI::QueryParame
Poco::JSON::Object::Ptr UObject_SK::request_conf_set( const std::string& req, const Poco::URI::QueryParameters& params ) Poco::JSON::Object::Ptr UObject_SK::request_conf_set( const std::string& req, const Poco::URI::QueryParameters& params )
{ {
Poco::JSON::Object::Ptr jret = new Poco::JSON::Object(); Poco::JSON::Object::Ptr jret = new Poco::JSON::Object();
Poco::JSON::Array::Ptr jupdated = uniset::json::make_child_array(jret, "updated");
for( const auto& p: params ) for( const auto& p: params )
{ {
if( p.first == "sleep_msec" )
{
int val = uni_atoi(p.second);
if( val > 0 )
{
sleep_msec = uni_atoi(p.second);
jupdated->add(p.first);
}
continue;
}
if( p.first == "resetMsgTime" )
{
int val = uni_atoi(p.second);
if( val > 0 )
{
resetMsgTime = uni_atoi(p.second);
jupdated->add(p.first);
}
continue;
}
if( p.first == "forceOut" )
{
int val = uni_atoi(p.second);
if( val > 0 )
{
forceOut = uni_atoi(p.second);
jupdated->add(p.first);
}
continue;
}
} }
jret->set("Result","OK"); jret->set("Result", (jupdated->size() > 0 ? "OK" : "FAIL") );
return jret; return jret;
} }
#endif #endif
......
...@@ -60,15 +60,12 @@ namespace uniset ...@@ -60,15 +60,12 @@ namespace uniset
/*! текущее количество подключений */ /*! текущее количество подключений */
size_t getCountSessions() const noexcept; size_t getCountSessions() const noexcept;
void setIgnoreAddrMode( bool st ); // Сбор статистики по соединениям...
bool getIgnoreAddrMode() const noexcept; struct SessionInfo
{
SessionInfo( const std::string& a, size_t ask ): iaddr(a), askCount(ask) {}
// Сбор статистики по соединениям... std::string iaddr;
struct SessionInfo
{
SessionInfo( const std::string& a, size_t ask ): iaddr(a), askCount(ask) {}
std::string iaddr;
size_t askCount; size_t askCount;
}; };
......
...@@ -81,16 +81,6 @@ namespace uniset ...@@ -81,16 +81,6 @@ namespace uniset
return sessCount; return sessCount;
} }
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
void ModbusTCPServer::setIgnoreAddrMode(bool st)
{
ignoreAddr = st;
}
// -------------------------------------------------------------------------
bool ModbusTCPServer::getIgnoreAddrMode() const noexcept
{
return ignoreAddr;
}
// -------------------------------------------------------------------------
void ModbusTCPServer::setSessionTimeout( timeout_t msec ) void ModbusTCPServer::setSessionTimeout( timeout_t msec )
{ {
sessTimeout = msec; sessTimeout = msec;
......
./cov-int/emit/pvbook.localdomain/config/130ac2dc34760443befdd3f2a25f8d40/g++cc-config-0/coverity-compiler-compat.h
./cov-int/emit/pvbook.localdomain/config/130ac2dc34760443befdd3f2a25f8d40/gcc-config-0/coverity-compiler-compat.h
./cov-int/emit/pvbook.localdomain/config/130ac2dc34760443befdd3f2a25f8d40/g++cc-config-0/coverity-macro-compat.h
./cov-int/emit/pvbook.localdomain/config/130ac2dc34760443befdd3f2a25f8d40/gcc-config-0/coverity-macro-compat.h
./cov-int/emit/pvbook.localdomain/config/130ac2dc34760443befdd3f2a25f8d40/g++cc-config-1/coverity-compiler-compat.h
./cov-int/emit/pvbook.localdomain/config/130ac2dc34760443befdd3f2a25f8d40/g++cc-config-1/coverity-macro-compat.h
./cov-int/emit/pvbook.localdomain/config/130ac2dc34760443befdd3f2a25f8d40/g++-config-0/coverity-compiler-compat.h
./cov-int/emit/pvbook.localdomain/config/130ac2dc34760443befdd3f2a25f8d40/g++-config-0/coverity-macro-compat.h
./cov-int/emit/pvbook.localdomain/config/130ac2dc34760443befdd3f2a25f8d40/g++-config-1/coverity-compiler-compat.h
./cov-int/emit/pvbook.localdomain/config/130ac2dc34760443befdd3f2a25f8d40/g++-config-1/coverity-macro-compat.h
./cov-int/emit/pvbook.localdomain/config/30c6834cc1e9802742e33de4efa3917c/g++cc-config-0/coverity-compiler-compat.h
./cov-int/emit/pvbook.localdomain/config/30c6834cc1e9802742e33de4efa3917c/g++cc-config-0/coverity-macro-compat.h
./cov-int/emit/pvbook.localdomain/config/30c6834cc1e9802742e33de4efa3917c/g++-config-0/coverity-compiler-compat.h
./cov-int/emit/pvbook.localdomain/config/30c6834cc1e9802742e33de4efa3917c/g++-config-0/coverity-macro-compat.h
./cov-int/emit/pvbook.localdomain/config/4d02eb73c555e8f07e4124fe33c80de2/g++cc-config-0/coverity-compiler-compat.h
./cov-int/emit/pvbook.localdomain/config/4d02eb73c555e8f07e4124fe33c80de2/g++cc-config-0/coverity-macro-compat.h
./cov-int/emit/pvbook.localdomain/config/4d02eb73c555e8f07e4124fe33c80de2/g++-config-0/coverity-compiler-compat.h
./cov-int/emit/pvbook.localdomain/config/4d02eb73c555e8f07e4124fe33c80de2/g++-config-0/coverity-macro-compat.h
./cov-int/emit/pvbook.localdomain/config/4db70a7a9c958929fa315f4c9243d800/g++cc-config-0/coverity-compiler-compat.h
./cov-int/emit/pvbook.localdomain/config/4db70a7a9c958929fa315f4c9243d800/g++cc-config-0/coverity-macro-compat.h
./cov-int/emit/pvbook.localdomain/config/4db70a7a9c958929fa315f4c9243d800/g++-config-0/coverity-compiler-compat.h
./cov-int/emit/pvbook.localdomain/config/4db70a7a9c958929fa315f4c9243d800/g++-config-0/coverity-macro-compat.h
./docs/Makefile.am ./docs/Makefile.am
./extensions/Backend-OpenTSDB/BackendOpenTSDB.cc ./extensions/Backend-OpenTSDB/BackendOpenTSDB.cc
./extensions/Backend-OpenTSDB/BackendOpenTSDB.h ./extensions/Backend-OpenTSDB/BackendOpenTSDB.h
...@@ -132,6 +110,8 @@ ...@@ -132,6 +110,8 @@
./extensions/Makefile.am ./extensions/Makefile.am
./extensions/ModbusMaster/main.cc ./extensions/ModbusMaster/main.cc
./extensions/ModbusMaster/Makefile.am ./extensions/ModbusMaster/Makefile.am
./extensions/ModbusMaster/MBConfig.cc
./extensions/ModbusMaster/MBConfig.h
./extensions/ModbusMaster/MBExchange.cc ./extensions/ModbusMaster/MBExchange.cc
./extensions/ModbusMaster/MBExchange.h ./extensions/ModbusMaster/MBExchange.h
./extensions/ModbusMaster/mb-perf-test.cc ./extensions/ModbusMaster/mb-perf-test.cc
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment