Commit 1ff72a00 authored by Pavel Vainerman's avatar Pavel Vainerman

(ModbusTCPMultiMaster): переделал механизм проверки соединения.

Теперь можно указать функцию,регистр и адрес устройства для проверки наличия соединения. Либо можно указать, чтобы параметы брались из списка обмена. При этом если эти параметры не указаны, проверка производится (как раньше) просто попыткой создания соединения.
parent a9dc289d
...@@ -78,10 +78,11 @@ MBTCPMultiMaster::MBTCPMultiMaster( UniSetTypes::ObjectId objId, UniSetTypes::Ob ...@@ -78,10 +78,11 @@ MBTCPMultiMaster::MBTCPMultiMaster( UniSetTypes::ObjectId objId, UniSetTypes::Ob
continue; continue;
} }
MBSlaveInfo sinf; auto sinf = make_shared<MBSlaveInfo>();
sinf.ip = it1.getProp("ip");
if( sinf.ip.empty() ) sinf->ip = it1.getProp("ip");
if( sinf->ip.empty() )
{ {
ostringstream err; ostringstream err;
err << myname << "(init): ip='' in <GateList>"; err << myname << "(init): ip='' in <GateList>";
...@@ -89,21 +90,21 @@ MBTCPMultiMaster::MBTCPMultiMaster( UniSetTypes::ObjectId objId, UniSetTypes::Ob ...@@ -89,21 +90,21 @@ MBTCPMultiMaster::MBTCPMultiMaster( UniSetTypes::ObjectId objId, UniSetTypes::Ob
throw UniSetTypes::SystemError(err.str()); throw UniSetTypes::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 UniSetTypes::SystemError(err.str()); throw UniSetTypes::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>";
...@@ -112,33 +113,51 @@ MBTCPMultiMaster::MBTCPMultiMaster( UniSetTypes::ObjectId objId, UniSetTypes::Ob ...@@ -112,33 +113,51 @@ MBTCPMultiMaster::MBTCPMultiMaster( UniSetTypes::ObjectId objId, UniSetTypes::Ob
} }
} }
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->recv_timeout = it1.getPIntProp("recv_timeout", recv_timeout);
sinf->aftersend_pause = it1.getPIntProp("aftersend_pause", aftersend_pause);
sinf->sleepPause_usec = it1.getPIntProp("sleepPause_msec", sleepPause_msec);
sinf->respond_invert = it1.getPIntProp("invert", 0);
sinf->respond_force = it1.getPIntProp("force", 0);
int fn = conf->getArgPInt("--" + prefix + "-check-func", it.getProp("checkFunc"), ModbusRTU::fnUnknown);
if( fn != ModbusRTU::fnUnknown &&
fn != ModbusRTU::fnReadCoilStatus &&
fn != ModbusRTU::fnReadInputStatus &&
fn != ModbusRTU::fnReadOutputRegisters &&
fn != ModbusRTU::fnReadInputRegisters )
{
ostringstream err;
err << myname << "(init): BAD check function ='" << fn << "'. Must be [1,2,3,4]";
mbcrit << err.str() << endl;
throw SystemError(err.str());
}
sinf->checkFunc = (ModbusRTU::SlaveFunctionCode)fn;
sinf.ptIgnoreTimeout.setTiming( it1.getPIntProp("ignore_timeout", ignore_timeout) ); sinf->checkAddr = conf->getArgPInt("--" + prefix + "-check-addr", it.getProp("checkAddr"), 0);
sinf.recv_timeout = it1.getPIntProp("recv_timeout", recv_timeout); sinf->checkReg = conf->getArgPInt("--" + prefix + "-check-reg", it.getProp("checkReg"), 0);
sinf.aftersend_pause = it1.getPIntProp("aftersend_pause", aftersend_pause);
sinf.sleepPause_usec = it1.getPIntProp("sleepPause_msec", sleepPause_msec);
sinf.respond_invert = it1.getPIntProp("invert", 0);
sinf.respond_force = it1.getPIntProp("force", 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(std::move(sinf)); mblist.emplace_back(sinf);
} }
if( ic ) if( ic )
...@@ -154,7 +173,8 @@ MBTCPMultiMaster::MBTCPMultiMaster( UniSetTypes::ObjectId objId, UniSetTypes::Ob ...@@ -154,7 +173,8 @@ MBTCPMultiMaster::MBTCPMultiMaster( UniSetTypes::ObjectId objId, UniSetTypes::Ob
} }
mblist.sort(); mblist.sort();
mbi = mblist.rbegin(); mbi = mblist.rbegin(); // т.к. mbi это reverse_iterator
(*mbi)->setUse(true);
if( shm->isLocalwork() ) if( shm->isLocalwork() )
{ {
...@@ -214,64 +234,69 @@ std::shared_ptr<ModbusClient> MBTCPMultiMaster::initMB( bool reopen ) ...@@ -214,64 +234,69 @@ std::shared_ptr<ModbusClient> MBTCPMultiMaster::initMB( bool reopen )
if( mbi == mblist.rend() ) if( mbi == mblist.rend() )
mbi = mblist.rbegin(); mbi = mblist.rbegin();
mbi->init(mblog); auto m = (*mbi);
m->init(mblog);
// переопределяем timeout на данный канал // переопределяем timeout на данный канал
ptReopen.setTiming( mbi->channel_timeout ); ptReopen.setTiming( m->channel_timeout );
mb = mbi->mbtcp; m->setUse(true);
return mbi->mbtcp; mb = m->mbtcp;
return m->mbtcp;
} }
{ {
// сперва надо обновить все ignore // сперва надо обновить все ignore
// т.к. фактически флаги выставляются и сбрасываются только здесь // т.к. фактически флаги выставляются и сбрасываются только здесь
for( auto it = mblist.rbegin(); it != mblist.rend(); ++it ) 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 )
{ {
mbi->setUse(false); auto m = (*mbi);
mbi->ignore = true; m->setUse(false);
mbi->ptIgnoreTimeout.reset(); m->ignore = true;
mbwarn << myname << "(initMB): set ignore=true for " << mbi->ip << ":" << mbi->port << endl; m->ptIgnoreTimeout.reset();
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);
// ещё раз проверим соединение (в неблокирующем режиме) // ещё раз проверим соединение (в неблокирующем режиме)
mbi->respond = mbi->check(); m->respond = m->check();
if( mbi->respond && (mbi->mbtcp->isConnection() || mbi->init(mblog)) ) if( m->respond && (m->mbtcp->isConnection() || m->init(mblog)) )
{ {
mblog4 << myname << "(initMB): SELECT CHANNEL " << mbi->ip << ":" << mbi->port << endl; mblog4 << myname << "(initMB): SELECT CHANNEL " << m->ip << ":" << m->port << endl;
mb = mbi->mbtcp; mb = m->mbtcp;
mbi->setUse(true); m->setUse(true);
ptReopen.setTiming( mbi->channel_timeout ); ptReopen.setTiming( m->channel_timeout );
return mbi->mbtcp; return m->mbtcp;
} }
mbi->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 )
{ {
if( it->respond && !it->ignore && it->init(mblog) ) auto m = (*it);
if( m->respond && !m->ignore && m->init(mblog) )
{ {
mbi = it; mbi = it;
mb = mbi->mbtcp; mb = m->mbtcp;
mbi->setUse(true); m->setUse(true);
ptReopen.setTiming( mbi->channel_timeout ); ptReopen.setTiming( m->channel_timeout );
mblog4 << myname << "(initMB): SELECT CHANNEL " << mbi->ip << ":" << mbi->port << endl; mblog4 << myname << "(initMB): SELECT CHANNEL " << m->ip << ":" << m->port << endl;
return it->mbtcp; return m->mbtcp;
} }
} }
...@@ -280,15 +305,16 @@ std::shared_ptr<ModbusClient> MBTCPMultiMaster::initMB( bool reopen ) ...@@ -280,15 +305,16 @@ std::shared_ptr<ModbusClient> MBTCPMultiMaster::initMB( bool reopen )
// значит сейчас просто находим первый у кого есть связь и делаем его главным // значит сейчас просто находим первый у кого есть связь и делаем его главным
for( auto it = mblist.rbegin(); it != mblist.rend(); ++it ) for( auto it = mblist.rbegin(); it != mblist.rend(); ++it )
{ {
if( it->respond && it->check() && it->init(mblog) ) auto& m = (*it);
if( m->respond && m->check() && m->init(mblog) )
{ {
mbi = it; mbi = it;
mb = mbi->mbtcp; mb = m->mbtcp;
mbi->ignore = false; m->ignore = false;
mbi->setUse(true); m->setUse(true);
ptReopen.setTiming( mbi->channel_timeout ); ptReopen.setTiming( m->channel_timeout );
mblog4 << myname << "(initMB): SELECT CHANNEL " << mbi->ip << ":" << mbi->port << endl; mblog4 << myname << "(initMB): SELECT CHANNEL " << m->ip << ":" << m->port << endl;
return it->mbtcp; return m->mbtcp;
} }
} }
...@@ -307,13 +333,80 @@ void MBTCPMultiMaster::final_thread() ...@@ -307,13 +333,80 @@ void MBTCPMultiMaster::final_thread()
} }
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
bool MBTCPMultiMaster::MBSlaveInfo::check() const bool MBTCPMultiMaster::MBSlaveInfo::check()
{ {
return mbtcp->checkConnection(ip, port, recv_timeout); std::unique_lock<std::mutex> lock(mutInit, std::try_to_lock);
// т.к. check вызывается периодически, то нем не страшно сделать return
// и в следующий раз проверить ещё раз..
if( !lock.owns_lock() )
return use; // возвращаем 'use' т.к. если use=1 то считается что связь есть..
if( !mbtcp )
return false;
if( use )
return true;
// cerr << myname << "(check): check connection..." << ip << ":" << port
// << " mbfunc=" << checkFunc
// << " mbaddr=" << ModbusRTU::addr2str(checkAddr)
// << " mbreg=" << (int)checkReg << "(" << ModbusRTU::dat2str(checkReg) << ")"
// << endl;
try
{
mbtcp->connect(ip,port,false);
switch(checkFunc)
{
case ModbusRTU::fnReadCoilStatus:
{
auto ret = mbtcp->read01(checkAddr,checkReg,1);
return true;
}
break;
case ModbusRTU::fnReadInputStatus:
{
auto ret = mbtcp->read02(checkAddr,checkReg,1);
return true;
}
break;
case ModbusRTU::fnReadOutputRegisters:
{
auto ret = mbtcp->read03(checkAddr,checkReg,1);
return true;
}
break;
case ModbusRTU::fnReadInputRegisters:
{
auto ret = mbtcp->read04(checkAddr,checkReg,1);
return true;
}
break;
default: // просто проверка..
return mbtcp->checkConnection(ip, port, recv_timeout);
}
}
catch(...){}
return false;
}
// -----------------------------------------------------------------------------
void MBTCPMultiMaster::MBSlaveInfo::setUse( bool st )
{
std::lock_guard<std::mutex> l(mutInit);
respond_init = !( st && !use );
use = st;
} }
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
bool MBTCPMultiMaster::MBSlaveInfo::init( std::shared_ptr<DebugStream>& mblog ) bool MBTCPMultiMaster::MBSlaveInfo::init( std::shared_ptr<DebugStream>& mblog )
{ {
std::lock_guard<std::mutex> l(mutInit);
mbinfo << myname << "(init): connect..." << endl; mbinfo << myname << "(init): connect..." << endl;
if( initOK ) if( initOK )
...@@ -339,6 +432,8 @@ void MBTCPMultiMaster::sysCommand( const UniSetTypes::SystemMessage* sm ) ...@@ -339,6 +432,8 @@ void MBTCPMultiMaster::sysCommand( const UniSetTypes::SystemMessage* sm )
if( sm->command == SystemMessage::StartUp ) if( sm->command == SystemMessage::StartUp )
{ {
initCheckConnectionParameters();
pollThread->start(); pollThread->start();
if( checktime > 0 ) if( checktime > 0 )
...@@ -387,7 +482,7 @@ void MBTCPMultiMaster::check_thread() ...@@ -387,7 +482,7 @@ void MBTCPMultiMaster::check_thread()
{ {
while( checkProcActive() ) while( checkProcActive() )
{ {
for( auto it = mblist.begin(); it != mblist.end(); ++it ) for( auto&& it: mblist )
{ {
try try
{ {
...@@ -409,6 +504,9 @@ void MBTCPMultiMaster::check_thread() ...@@ -409,6 +504,9 @@ void MBTCPMultiMaster::check_thread()
if( it->respond_init ) if( it->respond_init )
r = it->respondDelay.check( r ); r = it->respondDelay.check( r );
if( !checkProcActive() )
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) )
...@@ -450,7 +548,7 @@ void MBTCPMultiMaster::initIterators() ...@@ -450,7 +548,7 @@ 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);
} }
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
void MBTCPMultiMaster::sigterm( int signo ) void MBTCPMultiMaster::sigterm( int signo )
...@@ -509,6 +607,87 @@ bool MBTCPMultiMaster::deactivateObject() ...@@ -509,6 +607,87 @@ bool MBTCPMultiMaster::deactivateObject()
return MBExchange::deactivateObject(); return MBExchange::deactivateObject();
} }
// -----------------------------------------------------------------------------
void MBTCPMultiMaster::initCheckConnectionParameters()
{
auto conf = uniset_conf();
bool initFromRegMap = ( findArgParam("--" + prefix + "-check-init-from-regmap", conf->getArgc(), conf->getArgv()) != -1 );
if( !initFromRegMap )
return;
mbinfo << myname << "(init): init check connection parameters from regmap.." << endl;
// берём первый попавшийся read-регистр из списка
// от первого попавшегося устройства..
ModbusRTU::SlaveFunctionCode checkFunc = ModbusRTU::fnUnknown;
ModbusRTU::ModbusAddr checkAddr = { 0x00 };
ModbusRTU::ModbusData checkReg = { 0 };
if( devices.empty() )
{
mbwarn << myname << "(init): devices list empty?!" << endl;
return;
}
// идём по устройствам
for( const auto& d: devices )
{
checkAddr = d.second->mbaddr;
if( d.second->pollmap.empty() )
continue;
// идём по списку опрашиваемых регистров
for( auto p = d.second->pollmap.begin(); p != d.second->pollmap.end(); ++p )
{
for( auto r = p->second->begin(); r!=p->second->end(); ++r )
{
if( ModbusRTU::isReadFunction(r->second->mbfunc) )
{
checkFunc = r->second->mbfunc;
checkReg = r->second->mbreg;
break;
}
}
if( checkFunc != ModbusRTU::fnUnknown )
break;
}
if( checkFunc != ModbusRTU::fnUnknown )
break;
}
if( checkFunc == ModbusRTU::fnUnknown )
{
ostringstream err;
err << myname << "(init): init check connection parameters: ERROR: "
<< " NOT FOUND read-registers for check connection!"
<< endl;
mbcrit << err.str() << endl;
throw SystemError(err.str());
}
mbinfo << myname << "(init): init check connection parameters: "
<< " mbfunc=" << checkFunc
<< " mbaddr=" << ModbusRTU::addr2str(checkAddr)
<< " mbreg=" << (int)checkReg << "(" << ModbusRTU::dat2str(checkReg) << ")"
<< endl;
// инициализируем..
for( auto&& m: mblist )
{
m->checkFunc = checkFunc;
m->checkAddr = checkAddr;
m->checkReg = checkReg;
}
}
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
void MBTCPMultiMaster::help_print( int argc, const char* const* argv ) void MBTCPMultiMaster::help_print( int argc, const char* const* argv )
...@@ -520,6 +699,11 @@ void MBTCPMultiMaster::help_print( int argc, const char* const* argv ) ...@@ -520,6 +699,11 @@ void MBTCPMultiMaster::help_print( int argc, const char* const* argv )
cout << "--prefix-persistent-connection 0,1 - Не закрывать соединение на каждом цикле опроса" << endl; cout << "--prefix-persistent-connection 0,1 - Не закрывать соединение на каждом цикле опроса" << endl;
cout << "--prefix-checktime msec - период проверки связи по каналам (<GateList>)" << endl; cout << "--prefix-checktime msec - период проверки связи по каналам (<GateList>)" << endl;
cout << "--prefix-ignore-timeout msec - Timeout на повторную попытку использования канала после 'reopen-timeout'. По умолчанию: reopen-timeout * 3" << endl; cout << "--prefix-ignore-timeout msec - Timeout на повторную попытку использования канала после 'reopen-timeout'. По умолчанию: reopen-timeout * 3" << endl;
cout << "--prefix-check-func [1,2,3,4] - Номер функции для проверки соединения" << endl;
cout << "--prefix-check-addr [1..255 ] - Адрес устройства для проверки соединения" << endl;
cout << "--prefix-check-reg [1..65535] - Регистр для проверки соединения" << endl;
cout << endl; cout << endl;
cout << " ВНИМАНИЕ! '--prefix-reopen-timeout' для MBTCPMultiMaster НЕ ДЕЙСТВУЕТ! " << endl; cout << " ВНИМАНИЕ! '--prefix-reopen-timeout' для MBTCPMultiMaster НЕ ДЕЙСТВУЕТ! " << endl;
cout << " Смена канала происходит по --prefix-timeout. " << endl; cout << " Смена канала происходит по --prefix-timeout. " << endl;
...@@ -581,7 +765,7 @@ UniSetTypes::SimpleInfo* MBTCPMultiMaster::getInfo( CORBA::Long userparam ) ...@@ -581,7 +765,7 @@ UniSetTypes::SimpleInfo* MBTCPMultiMaster::getInfo( CORBA::Long userparam )
inf << "Gates: " << (checktime <= 0 ? "/ check connections DISABLED /" : "") << endl; inf << "Gates: " << (checktime <= 0 ? "/ check connections DISABLED /" : "") << endl;
for( const auto& m : mblist ) for( const auto& m : mblist )
inf << " " << m.getShortInfo() << endl; inf << " " << m->getShortInfo() << endl;
inf << endl; inf << endl;
......
...@@ -31,6 +31,7 @@ ...@@ -31,6 +31,7 @@
- \ref sec_MBTCPM_Conf - \ref sec_MBTCPM_Conf
- \ref sec_MBTCPM_ConfList - \ref sec_MBTCPM_ConfList
- \ref sec_MBTCPM_ExchangeMode - \ref sec_MBTCPM_ExchangeMode
- \ref sec_MBTCPM_CheckConnection
\section sec_MBTCPM_Comm Общее описание ModbusTCPMultiMaster \section sec_MBTCPM_Comm Общее описание ModbusTCPMultiMaster
Класс реализует процесс обмена (опрос/запись) с RTU-устройствами, Класс реализует процесс обмена (опрос/запись) с RTU-устройствами,
...@@ -85,6 +86,9 @@ ...@@ -85,6 +86,9 @@
- \b respond_invert - инвертировать датчик связи (DI) - \b respond_invert - инвертировать датчик связи (DI)
- \b force [1,0] - "1" - обновлять значение датчика связи в SM принудительно на каждом цикле проверки ("0" - только по изменению). - \b force [1,0] - "1" - обновлять значение датчика связи в SM принудительно на каждом цикле проверки ("0" - только по изменению).
- \b timeout - таймаут на определение отсутсвия связи для данного канала. По умолчанию берётся глобальный. - \b timeout - таймаут на определение отсутсвия связи для данного канала. По умолчанию берётся глобальный.
- \b checkFunc - Номер функции для проверки соединения
- \b checkAddr - Адрес устройства для проверки соединения
- \b checkReg - Регистр для проверки соединения
\par Параметры запуска \par Параметры запуска
...@@ -143,6 +147,11 @@ ...@@ -143,6 +147,11 @@
\b --xxx-activate-timeout msec . По умолчанию 2000. - время ожидания готовности SharedMemory к работе. \b --xxx-activate-timeout msec . По умолчанию 2000. - время ожидания готовности SharedMemory к работе.
\b --xxx-check-func [1,2,3,4] - Номер функции для проверки соединения
\b --xxx-check-addr [1..255 ] - Адрес устройства для проверки соединения
\b --xxx-check-reg [1..65535] - Регистр для проверки соединения
\b --xxx-check-init-from-regmap - Взять адрес, функцию и регистр для проверки связи из списка опроса
\section sec_MBTCPM_ConfList Конфигурирование списка регистров для ModbusTCP master \section sec_MBTCPM_ConfList Конфигурирование списка регистров для ModbusTCP master
Конфигурационные параметры задаются в секции <sensors> конфигурационного файла. Конфигурационные параметры задаются в секции <sensors> конфигурационного файла.
Список обрабатываемых регистров задаётся при помощи двух параметров командной строки Список обрабатываемых регистров задаётся при помощи двух параметров командной строки
...@@ -207,8 +216,6 @@ ...@@ -207,8 +216,6 @@
регистры в устройство писатся не будут. Чтобы отключить это поведение, можно указать параметр регистры в устройство писатся не будут. Чтобы отключить это поведение, можно указать параметр
- \b tcp_sm_initOK - [0|1] Игнорировать начальную инициализацию из SM (сразу писать в устройство) - \b tcp_sm_initOK - [0|1] Игнорировать начальную инициализацию из SM (сразу писать в устройство)
При этом будет записывыться значение "default". При этом будет записывыться значение "default".
\warning Регистр должен быть уникальным. И может повторятся только если указан параметр \a nbit или \a nbyte. \warning Регистр должен быть уникальным. И может повторятся только если указан параметр \a nbit или \a nbyte.
...@@ -228,10 +235,43 @@ ...@@ -228,10 +235,43 @@
Режимы переключаются при помощи датчика, который можно задать либо аргументом командной строки Режимы переключаются при помощи датчика, который можно задать либо аргументом командной строки
\b --prefix-exchange-mode-id либо в конф. файле параметром \b exchangeModeID="". Константы определяющие режимы объявлены в MBTCPMultiMaster::ExchangeMode. \b --prefix-exchange-mode-id либо в конф. файле параметром \b exchangeModeID="". Константы определяющие режимы объявлены в MBTCPMultiMaster::ExchangeMode.
\section sec_MBTCPM_CheckConnection Проверка соединения
Для контроля состояния связи по "резервным" каналам создаётся специальный поток (check_thread), в котором
происходит периодическая проверка связи по всем "пассивным"(резервным) в данный момент каналам. Это используется
как для общей диагностики в системе, так и при выборе на какой канал переключаться в случае пропажи связи в основном канале.
Т.е. будет выбран ближайший приоритетный канал у которого выставлен признак что есть связь.
Период проверки связи по "резервным" каналам задаётся при помощи --prefix-checktime или параметром checktime="" в конфигурационном файле.
В MBTCPMultiMaster реализовано два механизма проверки связи.
- По умолчанию используется простая установка соединения и тут же его разрыв. Т.е. данные никакие не посылаются,
но проверяется что host и port доступны для подключения.
- Второй способ: это проверка соединения с посылкой modbus-запроса. Для этого имеется два способа
указать адрес устройства, регистр и функция опроса для проверки.
Либо в секции <GateList> для каждого канала можно указать:
- адрес устройства \b checkAddr=""
- функцию проверки \b checkFunc="" - функция может быть только [01,02,03,04] (т.е. функции чтения).
- регистр \b checkReg
Либо в командной строке \b задать параметры --prefix-check-addr, --prefix-check-func, --prefix-check-reg,
которые будут одинаковыми для \b ВСЕХ \b КАНАЛОВ.
Помимо этого если указать в командной строке аргумент --prefix-check-init-from-regmap, то для тестирования
соединения будет взят первый попавшийся регистр из списка обмена.
\warning Способ проверки при помощи "modbus-запроса" имеет ряд проблем: Если фактически производится
обмен с несколькими устройствами (несколько mbaddr) через TCP-шлюз, то может быть "ложное" срабатвание,
т.к. фактически состояние канала будет определяться только под связи с каким-то одним конкретным устройством.
И получается, что если обмен ведётся например с тремя устройствами, но
проверка канала происходит только по связи с первым, то если оно перестанет отвечать, это будет считаться
сбоем всего канала и этот канал будет исключён из обмена (!). Если ведётся обмен только с одним устройством,
такой проблеммы не возникает.
Но к плюсам данного способа проверки связи ("modbus-запросом") является то, что соедиенение поддерживается
постоянным, в отличие от "первого способа" при котором оно создаётся и сразу рвётся и если проверка
настроена достаточно часто ( < TIME_WAIT для сокетов), то при длительной работе могут закончится дескрипторы
на создание сокетов.
*/ */
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
/*! /*!
\par Реализация Modbus TCP Multi Master для обмена с многими ModbusRTU устройствами \par Реализация Modbus TCP MultiMaster для обмена с многими ModbusRTU устройствами
через один modbus tcp шлюз, доступный по нескольким ip-адресам. через один modbus tcp шлюз, доступный по нескольким ip-адресам.
\par Чтобы не зависеть от таймаутов TCP соединений, которые могут неопределённо зависать \par Чтобы не зависеть от таймаутов TCP соединений, которые могут неопределённо зависать
...@@ -263,6 +303,7 @@ class MBTCPMultiMaster: ...@@ -263,6 +303,7 @@ class MBTCPMultiMaster:
virtual std::shared_ptr<ModbusClient> initMB( bool reopen = false ) override; virtual std::shared_ptr<ModbusClient> initMB( bool reopen = false ) override;
virtual void sigterm( int signo ) override; virtual void sigterm( int signo ) override;
virtual bool deactivateObject() override; virtual bool deactivateObject() override;
void initCheckConnectionParameters();
void poll_thread(); void poll_thread();
void check_thread(); void check_thread();
...@@ -288,6 +329,11 @@ class MBTCPMultiMaster: ...@@ -288,6 +329,11 @@ class MBTCPMultiMaster:
std::shared_ptr<ModbusTCPMaster> mbtcp; std::shared_ptr<ModbusTCPMaster> mbtcp;
int priority; int priority;
// параметры для проверки соединения..
ModbusRTU::SlaveFunctionCode checkFunc = { ModbusRTU::fnUnknown };
ModbusRTU::ModbusAddr checkAddr = { 0x00 };
ModbusRTU::ModbusData checkReg = { 0 };
bool respond; bool respond;
UniSetTypes::ObjectId respond_id; UniSetTypes::ObjectId respond_id;
IOController::IOStateList::iterator respond_it; IOController::IOStateList::iterator respond_it;
...@@ -297,18 +343,14 @@ class MBTCPMultiMaster: ...@@ -297,18 +343,14 @@ class MBTCPMultiMaster:
DelayTimer respondDelay; DelayTimer respondDelay;
timeout_t channel_timeout = { 0 }; timeout_t channel_timeout = { 0 };
inline bool operator < ( const MBSlaveInfo& mbs ) const inline bool operator < ( const MBSlaveInfo& mbs ) const noexcept
{ {
return priority < mbs.priority; return priority < mbs.priority;
} }
bool init( std::shared_ptr<DebugStream>& mblog ); bool init( std::shared_ptr<DebugStream>& mblog );
bool check() const; bool check();
inline void setUse( bool st ) void setUse( bool st );
{
respond_init = !( st && !use );
use = st;
}
timeout_t recv_timeout; timeout_t recv_timeout;
timeout_t aftersend_pause; timeout_t aftersend_pause;
...@@ -323,9 +365,11 @@ class MBTCPMultiMaster: ...@@ -323,9 +365,11 @@ class MBTCPMultiMaster:
PassiveTimer ptIgnoreTimeout; PassiveTimer ptIgnoreTimeout;
const std::string getShortInfo() const; const std::string getShortInfo() const;
std::mutex mutInit;
}; };
typedef std::list<MBSlaveInfo> MBGateList; typedef std::list<std::shared_ptr<MBSlaveInfo>> MBGateList;
MBGateList mblist; MBGateList mblist;
MBGateList::reverse_iterator mbi; MBGateList::reverse_iterator mbi;
......
...@@ -4,7 +4,7 @@ MBPARAM= ...@@ -4,7 +4,7 @@ MBPARAM=
for N in `seq 1 100`; do for N in `seq 1 100`; do
MBPARAM="$MBPARAM --mbtcp${N}-name MBTCP${N} --mbtcp${N}-confnode MBPerfTestMaster --mbtcp${N}-filter-field mbperf MBPARAM="$MBPARAM --mbtcp${N}-name MBTCP${N} --mbtcp${N}-confnode MBPerfTestMaster --mbtcp${N}-filter-field mbperf
--mbtcp${N}-filter-value $N --mbtcp${N}-persistent-connection 1 --mbtcp${N}-log-add-levels crit,warn" --mbtcp${N}-filter-value $N --mbtcp${N}-persistent-connection 1 --mbtcp${N}-check-init-from-regmap --mbtcp${N}-log-add-levels warn,crit"
done done
#echo "$MBPARAM" #echo "$MBPARAM"
......
...@@ -14,6 +14,10 @@ ...@@ -14,6 +14,10 @@
--mbtcp-log-add-levels level4,warn,crit \ --mbtcp-log-add-levels level4,warn,crit \
--mbtcp-persistent-connection 1 \ --mbtcp-persistent-connection 1 \
--mbtcp-run-logserver \ --mbtcp-run-logserver \
--mbtcpX-check-init-from-regmap \
--mbtcpX-check-addr 1 \
--mbtcpX-check-reg 66 \
--mbtcpX-check-func 1 \
$* $*
#--dlog-add-levels info,crit,warn,level4,level3,level9 \ #--dlog-add-levels info,crit,warn,level4,level3,level9 \
......
...@@ -150,6 +150,7 @@ namespace ModbusRTU ...@@ -150,6 +150,7 @@ namespace ModbusRTU
float dat2f( const ModbusData dat1, const ModbusData dat2 ); float dat2f( const ModbusData dat1, const ModbusData dat2 );
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
bool isWriteFunction( SlaveFunctionCode c ); bool isWriteFunction( SlaveFunctionCode c );
bool isReadFunction( SlaveFunctionCode c );
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
/*! Заголовок сообщений */ /*! Заголовок сообщений */
struct ModbusHeader struct ModbusHeader
......
...@@ -194,6 +194,18 @@ bool ModbusRTU::isWriteFunction( SlaveFunctionCode c ) ...@@ -194,6 +194,18 @@ bool ModbusRTU::isWriteFunction( SlaveFunctionCode c )
return false; return false;
} }
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
bool ModbusRTU::isReadFunction( SlaveFunctionCode c )
{
if( c == fnReadCoilStatus ||
c == fnReadInputStatus ||
c == fnReadOutputRegisters ||
c == fnReadInputRegisters )
return true;
return false;
}
// -------------------------------------------------------------------------
std::ostream& ModbusRTU::mbPrintMessage( std::ostream& os, ModbusByte* m, size_t len ) std::ostream& ModbusRTU::mbPrintMessage( std::ostream& os, ModbusByte* m, size_t len )
{ {
// Чтобы не менять настройки 'os' // Чтобы не менять настройки 'os'
......
...@@ -16,17 +16,23 @@ static atomic_bool cancel = {false}; ...@@ -16,17 +16,23 @@ static atomic_bool cancel = {false};
// -------------------------------------------------------- // --------------------------------------------------------
bool run_test_server() bool run_test_server()
{ {
UTCPSocket sock(host, port); try
while( !cancel )
{ {
if( sock.poll(PassiveTimer::millisecToPoco(500), Poco::Net::Socket::SELECT_READ) ) UTCPSocket sock(host, port);
while( !cancel )
{ {
if( sock.poll(PassiveTimer::millisecToPoco(500), Poco::Net::Socket::SELECT_READ) )
{
}
} }
return true;
} }
catch(...){}
return true; return false;
} }
// -------------------------------------------------------- // --------------------------------------------------------
// вспомогательный класс для гарантированного завершения потока.. // вспомогательный класс для гарантированного завершения потока..
......
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