Commit 326960c7 authored by Pavel Vainerman's avatar Pavel Vainerman Committed by Pavel Vainerman

[modbus master][reload config]: reload by HTTP API

parent cc15c224
...@@ -1185,54 +1185,56 @@ int oinfo(const string& args, UInterface& ui, const string& userparam ) ...@@ -1185,54 +1185,56 @@ int oinfo(const string& args, UInterface& ui, const string& userparam )
// -------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------
int apiRequest( const string& args, UInterface& ui, const string& query ) int apiRequest( const string& args, UInterface& ui, const string& query )
{ {
auto conf = uniset_conf(); auto conf = uniset_conf();
auto sl = uniset::getObjectsList( args, conf ); auto sl = uniset::getObjectsList( args, conf );
// if( verb ) // if( verb )
// cout << "apiRequest: query: " << query << endl; // cout << "apiRequest: query: " << query << endl;
if( query.size() < 1 ) if( query.size() < 1 )
{ {
if( !quiet ) if( !quiet )
cerr << "query is too small '" << query << "'" << endl; cerr << "query is too small '" << query << "'" << endl;
return 1; return 1;
} }
string q = query; string q = query;
if( q.rfind("/api/", 0) != 0 )
{ if( q.rfind("/api/", 0) != 0 )
q = "/api/" + uniset::UHttp::UHTTP_API_VERSION; {
if( query[0] != '/' ) q = "/api/" + uniset::UHttp::UHTTP_API_VERSION;
q += "/";
if( query[0] != '/' )
q += query; q += "/";
}
q += query;
for( auto && it : sl ) }
{
if( it.node == DefaultObjectId ) for( auto&& it : sl )
it.node = conf->getLocalNode(); {
if( it.node == DefaultObjectId )
try it.node = conf->getLocalNode();
{
cout << ui.apiRequest(it.id, q, it.node) << endl; try
} {
catch( const std::exception& ex ) cout << ui.apiRequest(it.id, q, it.node) << endl;
{ }
if( !quiet ) catch( const std::exception& ex )
cerr << "std::exception: " << ex.what() << endl; {
} if( !quiet )
catch(...) cerr << "std::exception: " << ex.what() << endl;
{ }
if( !quiet ) catch(...)
cerr << "Unknown exception.." << endl; {
} if( !quiet )
cerr << "Unknown exception.." << endl;
cout << endl << endl; }
}
cout << endl << endl;
return 0; }
return 0;
} }
// -------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------
......
...@@ -26,1139 +26,1167 @@ ...@@ -26,1139 +26,1167 @@
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
namespace uniset namespace uniset
{ {
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
using namespace std; using namespace std;
using namespace uniset::extensions; using namespace uniset::extensions;
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
MBConfig::MBConfig(const std::shared_ptr<uniset::Configuration>& _conf MBConfig::MBConfig(const std::shared_ptr<uniset::Configuration>& _conf
, xmlNode* _cnode , xmlNode* _cnode
, std::shared_ptr<SMInterface> _shm ): , std::shared_ptr<SMInterface> _shm ):
cnode(_cnode), cnode(_cnode),
conf(_conf), conf(_conf),
shm(_shm) shm(_shm)
{ {
} }
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
MBConfig::~MBConfig() MBConfig::~MBConfig()
{ {
} }
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
void MBConfig::cloneParams( const std::shared_ptr<MBConfig>& mbconf ) void MBConfig::cloneParams( const std::shared_ptr<MBConfig>& mbconf )
{ {
s_field = mbconf->s_field; s_field = mbconf->s_field;
s_fvalue = mbconf->s_fvalue; s_fvalue = mbconf->s_fvalue;
defaultMBtype = mbconf->defaultMBtype; defaultMBtype = mbconf->defaultMBtype;
defaultMBaddr = mbconf->defaultMBaddr; defaultMBaddr = mbconf->defaultMBaddr;
defaultMBinitOK = mbconf->defaultMBinitOK; defaultMBinitOK = mbconf->defaultMBinitOK;
mblog = mbconf->mblog; mblog = mbconf->mblog;
myname = mbconf->myname; myname = mbconf->myname;
noQueryOptimization = mbconf->noQueryOptimization; noQueryOptimization = mbconf->noQueryOptimization;
maxQueryCount = mbconf->maxQueryCount; maxQueryCount = mbconf->maxQueryCount;
recv_timeout = mbconf->recv_timeout; recv_timeout = mbconf->recv_timeout;
default_timeout = mbconf->default_timeout; default_timeout = mbconf->default_timeout;
aftersend_pause = mbconf->aftersend_pause; aftersend_pause = mbconf->aftersend_pause;
polltime = mbconf->polltime; polltime = mbconf->polltime;
sleepPause_msec = mbconf->sleepPause_msec; sleepPause_msec = mbconf->sleepPause_msec;
prefix = mbconf->prefix; prefix = mbconf->prefix;
prop_prefix = mbconf->prop_prefix; prop_prefix = mbconf->prop_prefix;
mbregFromID = mbconf->mbregFromID; mbregFromID = mbconf->mbregFromID;
} }
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
void MBConfig::loadConfig( const std::shared_ptr<uniset::UniXML>& xml, UniXML::iterator sensorsSection ) void MBConfig::loadConfig( const std::shared_ptr<uniset::UniXML>& xml, UniXML::iterator sensorsSection )
{ {
readConfiguration(xml, sensorsSection); readConfiguration(xml, sensorsSection);
if( !noQueryOptimization ) if( !noQueryOptimization )
rtuQueryOptimization(devices, maxQueryCount); rtuQueryOptimization(devices, maxQueryCount);
initDeviceList(xml); initDeviceList(xml);
} }
// ------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------
void MBConfig::readConfiguration( const std::shared_ptr<uniset::UniXML>& xml, UniXML::iterator sensorsSection ) void MBConfig::readConfiguration( const std::shared_ptr<uniset::UniXML>& xml, UniXML::iterator sensorsSection )
{ {
UniXML::iterator it = sensorsSection; UniXML::iterator it = sensorsSection;
if( !it.getCurrent() ) if( !it.getCurrent() )
{ {
it = xml->findNode(xml->getFirstNode(), "sensors"); it = xml->findNode(xml->getFirstNode(), "sensors");
if(!it) if(!it)
{ {
ostringstream err; ostringstream err;
err << myname << "(readConfiguration): не нашли корневого раздела <sensors>"; err << myname << "(readConfiguration): не нашли корневого раздела <sensors>";
throw SystemError(err.str()); throw SystemError(err.str());
} }
} }
if( !it.goChildren() ) if( !it.goChildren() )
{ {
mbcrit << myname << "(readConfiguration): раздел <sensors> не содержит секций ?!!\n"; mbcrit << myname << "(readConfiguration): раздел <sensors> не содержит секций ?!!\n";
return; return;
} }
for( ; it.getCurrent(); it.goNext() ) for( ; it.getCurrent(); it.goNext() )
{ {
if( uniset::check_filter(it, s_field, s_fvalue) ) if( uniset::check_filter(it, s_field, s_fvalue) )
initItem(it); initItem(it);
} }
// readconf_ok = true; // readconf_ok = true;
} }
// ------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------
MBConfig::DeviceType MBConfig::getDeviceType( const std::string& dtype ) noexcept MBConfig::DeviceType MBConfig::getDeviceType( const std::string& dtype ) noexcept
{ {
if( dtype.empty() ) if( dtype.empty() )
return dtUnknown; return dtUnknown;
if( dtype == "mtr" || dtype == "MTR" ) if( dtype == "mtr" || dtype == "MTR" )
return dtMTR; return dtMTR;
if( dtype == "rtu" || dtype == "RTU" ) if( dtype == "rtu" || dtype == "RTU" )
return dtRTU; return dtRTU;
if( dtype == "rtu188" || dtype == "RTU188" ) if( dtype == "rtu188" || dtype == "RTU188" )
return dtRTU188; return dtRTU188;
return dtUnknown; return dtUnknown;
} }
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
void MBConfig::printMap( MBConfig::RTUDeviceMap& m ) void MBConfig::printMap( MBConfig::RTUDeviceMap& m )
{ {
cout << "devices: " << endl; cout << "devices: " << endl;
for( auto it = m.begin(); it != m.end(); ++it ) for( auto it = m.begin(); it != m.end(); ++it )
cout << " " << *(it->second) << endl; cout << " " << *(it->second) << endl;
} }
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
std::ostream& operator<<( std::ostream& os, MBConfig::RTUDeviceMap& m ) std::ostream& operator<<( std::ostream& os, MBConfig::RTUDeviceMap& m )
{ {
os << "devices: " << endl; os << "devices: " << endl;
for( auto it = m.begin(); it != m.end(); ++it ) for( auto it = m.begin(); it != m.end(); ++it )
os << " " << *(it->second) << endl; os << " " << *(it->second) << endl;
return os; return os;
} }
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
std::ostream& operator<<( std::ostream& os, MBConfig::RTUDevice& d ) std::ostream& operator<<( std::ostream& os, MBConfig::RTUDevice& d )
{ {
os << "addr=" << ModbusRTU::addr2str(d.mbaddr) os << "addr=" << ModbusRTU::addr2str(d.mbaddr)
<< " type=" << d.dtype << " type=" << d.dtype
<< " respond_id=" << d.resp_id << " respond_id=" << d.resp_id
<< " respond_timeout=" << d.resp_Delay.getOffDelay() << " respond_timeout=" << d.resp_Delay.getOffDelay()
<< " respond_state=" << d.resp_state << " respond_state=" << d.resp_state
<< " respond_invert=" << d.resp_invert << " respond_invert=" << d.resp_invert
<< " safeMode=" << (MBConfig::SafeMode)d.safeMode << " safeMode=" << (MBConfig::SafeMode)d.safeMode
<< endl; << endl;
os << " regs: " << endl; os << " regs: " << endl;
for( const auto& m : d.pollmap ) for( const auto& m : d.pollmap )
{ {
for( const auto& it : * (m.second) ) for( const auto& it : * (m.second) )
os << " " << it.second << endl; os << " " << it.second << endl;
} }
return os; return os;
} }
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
std::ostream& operator<<( std::ostream& os, const MBConfig::RegInfo* r ) std::ostream& operator<<( std::ostream& os, const MBConfig::RegInfo* r )
{ {
return os << (*r); return os << (*r);
} }
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
std::ostream& operator<<( std::ostream& os, const MBConfig::RegInfo& r ) std::ostream& operator<<( std::ostream& os, const MBConfig::RegInfo& r )
{ {
os << " id=" << r.regID os << " id=" << r.regID
<< " mbreg=" << ModbusRTU::dat2str(r.mbreg) << " mbreg=" << ModbusRTU::dat2str(r.mbreg)
<< " mbfunc=" << r.mbfunc << " mbfunc=" << r.mbfunc
<< " q_num=" << r.q_num << " q_num=" << r.q_num
<< " q_count=" << r.q_count << " q_count=" << r.q_count
<< " value=" << ModbusRTU::dat2str(r.mbval) << "(" << (int)r.mbval << ")" << " value=" << ModbusRTU::dat2str(r.mbval) << "(" << (int)r.mbval << ")"
<< " mtrType=" << MTR::type2str(r.mtrType) << " mtrType=" << MTR::type2str(r.mtrType)
<< endl; << endl;
for( const auto& s : r.slst ) for( const auto& s : r.slst )
os << " " << s << endl; os << " " << s << endl;
return os; return os;
} }
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
void MBConfig::rtuQueryOptimization( RTUDeviceMap& dm, size_t maxQueryCount ) void MBConfig::rtuQueryOptimization( RTUDeviceMap& dm, size_t maxQueryCount )
{ {
// mbinfo << myname << "(rtuQueryOptimization): optimization..." << endl; // mbinfo << myname << "(rtuQueryOptimization): optimization..." << endl;
for( const auto& d : dm ) for( const auto& d : dm )
rtuQueryOptimizationForDevice(d.second, maxQueryCount); rtuQueryOptimizationForDevice(d.second, maxQueryCount);
} }
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
void MBConfig::rtuQueryOptimizationForDevice( const std::shared_ptr<RTUDevice>& d, size_t maxQueryCount ) void MBConfig::rtuQueryOptimizationForDevice( const std::shared_ptr<RTUDevice>& d, size_t maxQueryCount )
{ {
// mbinfo << myname << "(rtuQueryOptimizationForDevice): dev addr=" // mbinfo << myname << "(rtuQueryOptimizationForDevice): dev addr="
// << ModbusRTU::addr2str(d->mbaddr) << " optimization..." << endl; // << ModbusRTU::addr2str(d->mbaddr) << " optimization..." << endl;
for( const auto& m : d->pollmap ) for( const auto& m : d->pollmap )
rtuQueryOptimizationForRegMap(m.second, maxQueryCount); rtuQueryOptimizationForRegMap(m.second, maxQueryCount);
} }
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
void MBConfig::rtuQueryOptimizationForRegMap( const std::shared_ptr<RegMap>& regmap, size_t maxQueryCount ) void MBConfig::rtuQueryOptimizationForRegMap( const std::shared_ptr<RegMap>& regmap, size_t maxQueryCount )
{ {
if( regmap->size() <= 1 ) if( regmap->size() <= 1 )
return; return;
// Вообще в map они уже лежат в нужном порядке, т.е. функция genRegID() гарантирует // Вообще в map они уже лежат в нужном порядке, т.е. функция genRegID() гарантирует
// что регистры идущие подряд с одниковой функцией чтения/записи получат подряд идущие RegID. // что регистры идущие подряд с одниковой функцией чтения/записи получат подряд идущие RegID.
// так что оптимтизация это просто нахождение мест где RegID идут не подряд... // так что оптимтизация это просто нахождение мест где RegID идут не подряд...
for( auto it = regmap->begin(); it != regmap->end(); ++it ) for( auto it = regmap->begin(); it != regmap->end(); ++it )
{ {
auto& beg = it->second; auto& beg = it->second;
ModbusRTU::RegID regID = beg->regID; ModbusRTU::RegID regID = beg->regID;
beg->q_count = 1; beg->q_count = 1;
beg->q_num = 0; beg->q_num = 0;
++it; ++it;
// склеиваем регистры идущие подряд // склеиваем регистры идущие подряд
for( ; it != regmap->end(); ++it ) for( ; it != regmap->end(); ++it )
{ {
if( (it->second->regID - regID) > 1 ) if( (it->second->regID - regID) > 1 )
{ {
// этот регистр должен войти уже в следующий запрос, // этот регистр должен войти уже в следующий запрос,
// надо вернуть на шаг обратно.. // надо вернуть на шаг обратно..
--it; --it;
break; break;
} }
beg->q_count++; beg->q_count++;
regID = it->second->regID; regID = it->second->regID;
it->second->q_num = beg->q_count - 1; it->second->q_num = beg->q_count - 1;
it->second->q_count = 0; it->second->q_count = 0;
if( beg->q_count >= maxQueryCount ) if( beg->q_count >= maxQueryCount )
break; break;
} }
// Корректировка типа функции, в случае необходимости... // Корректировка типа функции, в случае необходимости...
if( beg->q_count > 1 && beg->mbfunc == ModbusRTU::fnWriteOutputSingleRegister ) if( beg->q_count > 1 && beg->mbfunc == ModbusRTU::fnWriteOutputSingleRegister )
{ {
// mbwarn << myname << "(rtuQueryOptimization): " // mbwarn << myname << "(rtuQueryOptimization): "
// << " optimization change func=" << ModbusRTU::fnWriteOutputSingleRegister // << " optimization change func=" << ModbusRTU::fnWriteOutputSingleRegister
// << " <--> func=" << ModbusRTU::fnWriteOutputRegisters // << " <--> func=" << ModbusRTU::fnWriteOutputRegisters
// << " for mbaddr=" << ModbusRTU::addr2str(beg->dev->mbaddr) // << " for mbaddr=" << ModbusRTU::addr2str(beg->dev->mbaddr)
// << " mbreg=" << ModbusRTU::dat2str(beg->mbreg); // << " mbreg=" << ModbusRTU::dat2str(beg->mbreg);
beg->mbfunc = ModbusRTU::fnWriteOutputRegisters; beg->mbfunc = ModbusRTU::fnWriteOutputRegisters;
} }
else if( beg->q_count > 1 && beg->mbfunc == ModbusRTU::fnForceSingleCoil ) else if( beg->q_count > 1 && beg->mbfunc == ModbusRTU::fnForceSingleCoil )
{ {
// mbwarn << myname << "(rtuQueryOptimization): " // mbwarn << myname << "(rtuQueryOptimization): "
// << " optimization change func=" << ModbusRTU::fnForceSingleCoil // << " optimization change func=" << ModbusRTU::fnForceSingleCoil
// << " <--> func=" << ModbusRTU::fnForceMultipleCoils // << " <--> func=" << ModbusRTU::fnForceMultipleCoils
// << " for mbaddr=" << ModbusRTU::addr2str(beg->dev->mbaddr) // << " for mbaddr=" << ModbusRTU::addr2str(beg->dev->mbaddr)
// << " mbreg=" << ModbusRTU::dat2str(beg->mbreg); // << " mbreg=" << ModbusRTU::dat2str(beg->mbreg);
beg->mbfunc = ModbusRTU::fnForceMultipleCoils; beg->mbfunc = ModbusRTU::fnForceMultipleCoils;
} }
// надо до внешнего цикла, где будет ++it // надо до внешнего цикла, где будет ++it
// проверить условие.. (т.к. мы во внутреннем цикле итерировались // проверить условие.. (т.к. мы во внутреннем цикле итерировались
if( it == regmap->end() ) if( it == regmap->end() )
break; break;
} }
} }
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
//std::ostream& operator<<( std::ostream& os, MBConfig::PList& lst ) //std::ostream& operator<<( std::ostream& os, MBConfig::PList& lst )
std::ostream& MBConfig::print_plist( std::ostream& os, const MBConfig::PList& lst ) std::ostream& MBConfig::print_plist( std::ostream& os, const MBConfig::PList& lst )
{ {
os << "[ "; os << "[ ";
for( const auto& p : lst ) for( const auto& p : lst )
os << "(" << p.si.id << ")" << conf->oind->getBaseName(conf->oind->getMapName(p.si.id)) << " "; os << "(" << p.si.id << ")" << conf->oind->getBaseName(conf->oind->getMapName(p.si.id)) << " ";
os << "]"; os << "]";
return os; return os;
} }
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
std::shared_ptr<MBConfig::RTUDevice> MBConfig::addDev( RTUDeviceMap& mp, ModbusRTU::ModbusAddr a, UniXML::iterator& xmlit ) std::shared_ptr<MBConfig::RTUDevice> MBConfig::addDev( RTUDeviceMap& mp, ModbusRTU::ModbusAddr a, UniXML::iterator& xmlit )
{ {
auto it = mp.find(a); auto it = mp.find(a);
if( it != mp.end() ) if( it != mp.end() )
{ {
string s_dtype(xmlit.getProp(prop_prefix + "mbtype")); string s_dtype(xmlit.getProp(prop_prefix + "mbtype"));
if( s_dtype.empty() ) if( s_dtype.empty() )
s_dtype = defaultMBtype; s_dtype = defaultMBtype;
DeviceType dtype = getDeviceType(s_dtype); DeviceType dtype = getDeviceType(s_dtype);
if( it->second->dtype != dtype ) if( it->second->dtype != dtype )
{ {
mbcrit << myname << "(addDev): OTHER mbtype=" << dtype << " for " << xmlit.getProp("name") mbcrit << myname << "(addDev): OTHER mbtype=" << dtype << " for " << xmlit.getProp("name")
<< " prop='" << prop_prefix + "mbtype" << "'" << " prop='" << prop_prefix + "mbtype" << "'"
<< ". Already used devtype=" << it->second->dtype << ". Already used devtype=" << it->second->dtype
<< " for mbaddr=" << ModbusRTU::addr2str(it->second->mbaddr) << " for mbaddr=" << ModbusRTU::addr2str(it->second->mbaddr)
<< endl; << endl;
return 0; return 0;
} }
mbinfo << myname << "(addDev): device for addr=" << ModbusRTU::addr2str(a) mbinfo << myname << "(addDev): device for addr=" << ModbusRTU::addr2str(a)
<< " already added. Ignore device params for " << xmlit.getProp("name") << " ..." << endl; << " already added. Ignore device params for " << xmlit.getProp("name") << " ..." << endl;
return it->second; return it->second;
} }
auto d = make_shared<MBConfig::RTUDevice>(); auto d = make_shared<MBConfig::RTUDevice>();
d->mbaddr = a; d->mbaddr = a;
if( !initRTUDevice(d, xmlit) ) if( !initRTUDevice(d, xmlit) )
{ {
d.reset(); d.reset();
return 0; return 0;
} }
mp.insert( std::make_pair(a, d) ); mp.insert( std::make_pair(a, d) );
return d; return d;
} }
// ------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------
std::shared_ptr<MBConfig::RegInfo> MBConfig::addReg( std::shared_ptr<RegMap>& mp, ModbusRTU::RegID regID, ModbusRTU::ModbusData r, 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 ) UniXML::iterator& xmlit, std::shared_ptr<MBConfig::RTUDevice> dev )
{ {
auto it = mp->find(regID); auto it = mp->find(regID);
if( it != mp->end() ) if( it != mp->end() )
{ {
if( !it->second->dev ) if( !it->second->dev )
{ {
mbcrit << myname << "(addReg): for " << xmlit.getProp("name") mbcrit << myname << "(addReg): for " << xmlit.getProp("name")
<< " dev=0!!!! " << endl; << " dev=0!!!! " << endl;
return 0; return 0;
} }
if( it->second->dev->dtype != dev->dtype ) if( it->second->dev->dtype != dev->dtype )
{ {
mbcrit << myname << "(addReg): OTHER mbtype=" << dev->dtype << " for " << xmlit.getProp("name") mbcrit << myname << "(addReg): OTHER mbtype=" << dev->dtype << " for " << xmlit.getProp("name")
<< ". Already used devtype=" << it->second->dev->dtype << " for " << it->second->dev << endl; << ". Already used devtype=" << it->second->dev->dtype << " for " << it->second->dev << endl;
return 0; return 0;
} }
mbinfo << myname << "(addReg): reg=" << ModbusRTU::dat2str(r) mbinfo << myname << "(addReg): reg=" << ModbusRTU::dat2str(r)
<< "(regID=" << regID << ")" << "(regID=" << regID << ")"
<< " already added for " << (*it->second) << " already added for " << (*it->second)
<< " Ignore register params for " << xmlit.getProp("name") << " ..." << endl; << " Ignore register params for " << xmlit.getProp("name") << " ..." << endl;
it->second->rit = it; it->second->rit = it;
return it->second; return it->second;
} }
auto ri = make_shared<MBConfig::RegInfo>(); auto ri = make_shared<MBConfig::RegInfo>();
if( !initRegInfo(ri, xmlit, dev) ) if( !initRegInfo(ri, xmlit, dev) )
return 0; return 0;
ri->mbreg = r; ri->mbreg = r;
ri->regID = regID; ri->regID = regID;
mp->insert( std::make_pair(regID, ri) ); mp->insert( std::make_pair(regID, ri) );
ri->rit = mp->find(regID); ri->rit = mp->find(regID);
mbinfo << myname << "(addReg): reg=" << ModbusRTU::dat2str(r) << "(regID=" << regID << ")" << endl; mbinfo << myname << "(addReg): reg=" << ModbusRTU::dat2str(r) << "(regID=" << regID << ")" << endl;
return ri; return ri;
} }
// ------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------
MBConfig::RSProperty* MBConfig::addProp( PList& plist, RSProperty&& p ) MBConfig::RSProperty* MBConfig::addProp( PList& plist, RSProperty&& p )
{ {
for( auto&& it : plist ) for( auto&& it : plist )
{ {
if( it.si.id == p.si.id && it.si.node == p.si.node ) if( it.si.id == p.si.id && it.si.node == p.si.node )
return &it; return &it;
} }
plist.emplace_back( std::move(p) ); plist.emplace_back( std::move(p) );
auto it = plist.end(); auto it = plist.end();
--it; --it;
return &(*it); return &(*it);
} }
// ------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------
bool MBConfig::initRSProperty( RSProperty& p, UniXML::iterator& it ) bool MBConfig::initRSProperty( RSProperty& p, UniXML::iterator& it )
{ {
if( !IOBase::initItem(&p, it, shm, prop_prefix, false, mblog, myname) ) if( !IOBase::initItem(&p, it, shm, prop_prefix, false, mblog, myname) )
return false; return false;
// проверяем не пороговый ли это датчик (т.е. не связанный с обменом) // проверяем не пороговый ли это датчик (т.е. не связанный с обменом)
// тогда заносим его в отдельный список // тогда заносим его в отдельный список
if( p.t_ai != DefaultObjectId ) if( p.t_ai != DefaultObjectId )
{ {
thrlist.emplace_back( std::move(p) ); thrlist.emplace_back( std::move(p) );
return true; return true;
} }
const string sbit(IOBase::initProp(it, "nbit", prop_prefix, false)); const string sbit(IOBase::initProp(it, "nbit", prop_prefix, false));
if( !sbit.empty() ) if( !sbit.empty() )
{ {
p.nbit = uniset::uni_atoi(sbit.c_str()); p.nbit = uniset::uni_atoi(sbit.c_str());
if( p.nbit < 0 || p.nbit >= ModbusRTU::BitsPerData ) if( p.nbit < 0 || p.nbit >= ModbusRTU::BitsPerData )
{ {
mbcrit << myname << "(initRSProperty): BAD nbit=" << (int)p.nbit mbcrit << myname << "(initRSProperty): BAD nbit=" << (int)p.nbit
<< ". (0 >= nbit < " << ModbusRTU::BitsPerData << ")." << endl; << ". (0 >= nbit < " << ModbusRTU::BitsPerData << ")." << endl;
return false; return false;
} }
} }
if( p.nbit > 0 && if( p.nbit > 0 &&
( p.stype == UniversalIO::AI || ( p.stype == UniversalIO::AI ||
p.stype == UniversalIO::AO ) ) p.stype == UniversalIO::AO ) )
{ {
mbwarn << "(initRSProperty): (ignore) uncorrect param`s nbit!=0(" << p.nbit << ")" mbwarn << "(initRSProperty): (ignore) uncorrect param`s nbit!=0(" << p.nbit << ")"
<< " for iotype=" << p.stype << " for " << it.getProp("name") << endl; << " for iotype=" << p.stype << " for " << it.getProp("name") << endl;
} }
const string sbyte(IOBase::initProp(it, "nbyte", prop_prefix, false) ); const string sbyte(IOBase::initProp(it, "nbyte", prop_prefix, false) );
if( !sbyte.empty() ) if( !sbyte.empty() )
{ {
p.nbyte = uniset::uni_atoi(sbyte.c_str()); p.nbyte = uniset::uni_atoi(sbyte.c_str());
if( p.nbyte > VTypes::Byte::bsize ) if( p.nbyte > VTypes::Byte::bsize )
{ {
mbwarn << myname << "(initRSProperty): BAD nbyte=" << p.nbyte mbwarn << myname << "(initRSProperty): BAD nbyte=" << p.nbyte
<< ". (0 >= nbyte < " << VTypes::Byte::bsize << ")." << endl; << ". (0 >= nbyte < " << VTypes::Byte::bsize << ")." << endl;
return false; return false;
} }
} }
const string vt( IOBase::initProp(it, "vtype", prop_prefix, false) ); const string vt( IOBase::initProp(it, "vtype", prop_prefix, false) );
if( vt.empty() ) if( vt.empty() )
{ {
p.rnum = VTypes::wsize(VTypes::vtUnknown); p.rnum = VTypes::wsize(VTypes::vtUnknown);
p.vType = VTypes::vtUnknown; p.vType = VTypes::vtUnknown;
} }
else else
{ {
VTypes::VType v(VTypes::str2type(vt)); VTypes::VType v(VTypes::str2type(vt));
if( v == VTypes::vtUnknown ) if( v == VTypes::vtUnknown )
{ {
mbcrit << myname << "(initRSProperty): Unknown tcp_vtype='" << vt << "' for " mbcrit << myname << "(initRSProperty): Unknown tcp_vtype='" << vt << "' for "
<< it.getProp("name") << it.getProp("name")
<< endl; << endl;
return false; return false;
} }
p.vType = v; p.vType = v;
p.rnum = VTypes::wsize(v); p.rnum = VTypes::wsize(v);
} }
return true; return true;
} }
// ------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------
bool MBConfig::initRegInfo( std::shared_ptr<RegInfo>& r, UniXML::iterator& it, std::shared_ptr<MBConfig::RTUDevice>& dev ) bool MBConfig::initRegInfo( std::shared_ptr<RegInfo>& r, UniXML::iterator& it, std::shared_ptr<MBConfig::RTUDevice>& dev )
{ {
r->dev = dev; r->dev = dev;
r->mbval = IOBase::initIntProp(it, "default", prop_prefix, false); r->mbval = IOBase::initIntProp(it, "default", prop_prefix, false);
if( dev->dtype == MBConfig::dtRTU ) if( dev->dtype == MBConfig::dtRTU )
{ {
// mblog.info() << myname << "(initRegInfo): init RTU.." // mblog.info() << myname << "(initRegInfo): init RTU.."
} }
else if( dev->dtype == MBConfig::dtMTR ) else if( dev->dtype == MBConfig::dtMTR )
{ {
// only for MTR // only for MTR
if( !initMTRitem(it, r) ) if( !initMTRitem(it, r) )
return false; return false;
} }
else if( dev->dtype == MBConfig::dtRTU188 ) else if( dev->dtype == MBConfig::dtRTU188 )
{ {
// only for RTU188 // only for RTU188
if( !initRTU188item(it, r) ) if( !initRTU188item(it, r) )
return false; return false;
UniversalIO::IOType t = uniset::getIOType(IOBase::initProp(it, "iotype", prop_prefix, false)); UniversalIO::IOType t = uniset::getIOType(IOBase::initProp(it, "iotype", prop_prefix, false));
r->mbreg = RTUStorage::getRegister(r->rtuJack, r->rtuChan, t); r->mbreg = RTUStorage::getRegister(r->rtuJack, r->rtuChan, t);
r->mbfunc = RTUStorage::getFunction(r->rtuJack, r->rtuChan, t); r->mbfunc = RTUStorage::getFunction(r->rtuJack, r->rtuChan, t);
// т.к. с RTU188 свой обмен // т.к. с RTU188 свой обмен
// mbreg и mbfunc поля не используются // mbreg и mbfunc поля не используются
return true; return true;
} }
else else
{ {
mbcrit << myname << "(initRegInfo): Unknown mbtype='" << dev->dtype mbcrit << myname << "(initRegInfo): Unknown mbtype='" << dev->dtype
<< "' for " << it.getProp("name") << endl; << "' for " << it.getProp("name") << endl;
return false; return false;
} }
if( mbregFromID ) if( mbregFromID )
{ {
if( it.getProp("id").empty() ) if( it.getProp("id").empty() )
r->mbreg = conf->getSensorID(it.getProp("name")); r->mbreg = conf->getSensorID(it.getProp("name"));
else else
r->mbreg = it.getIntProp("id"); r->mbreg = it.getIntProp("id");
} }
else else
{ {
const string sr( IOBase::initProp(it, "mbreg", prop_prefix, false) ); const string sr( IOBase::initProp(it, "mbreg", prop_prefix, false) );
if( sr.empty() ) if( sr.empty() )
{ {
mbcrit << myname << "(initItem): Unknown 'mbreg' for " << it.getProp("name") << endl; mbcrit << myname << "(initItem): Unknown 'mbreg' for " << it.getProp("name") << endl;
return false; return false;
} }
r->mbreg = ModbusRTU::str2mbData(sr); r->mbreg = ModbusRTU::str2mbData(sr);
} }
r->mbfunc = ModbusRTU::fnUnknown; r->mbfunc = ModbusRTU::fnUnknown;
const string f( IOBase::initProp(it, "mbfunc", prop_prefix, false) ); const string f( IOBase::initProp(it, "mbfunc", prop_prefix, false) );
if( !f.empty() ) if( !f.empty() )
{ {
r->mbfunc = (ModbusRTU::SlaveFunctionCode)uniset::uni_atoi(f.c_str()); r->mbfunc = (ModbusRTU::SlaveFunctionCode)uniset::uni_atoi(f.c_str());
if( r->mbfunc == ModbusRTU::fnUnknown ) if( r->mbfunc == ModbusRTU::fnUnknown )
{ {
mbcrit << myname << "(initRegInfo): Unknown mbfunc ='" << f mbcrit << myname << "(initRegInfo): Unknown mbfunc ='" << f
<< "' for " << it.getProp("name") << endl; << "' for " << it.getProp("name") << endl;
return false; return false;
} }
} }
return true; return true;
} }
// ------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------
bool MBConfig::initRTUDevice( std::shared_ptr<RTUDevice>& d, UniXML::iterator& it ) bool MBConfig::initRTUDevice( std::shared_ptr<RTUDevice>& d, UniXML::iterator& it )
{ {
string mbtype(IOBase::initProp(it, "mbtype", prop_prefix, false)); string mbtype(IOBase::initProp(it, "mbtype", prop_prefix, false));
if(mbtype.empty()) if(mbtype.empty())
mbtype = defaultMBtype; mbtype = defaultMBtype;
d->dtype = getDeviceType(mbtype); d->dtype = getDeviceType(mbtype);
if( d->dtype == dtUnknown ) if( d->dtype == dtUnknown )
{ {
mbcrit << myname << "(initRTUDevice): Unknown tcp_mbtype='" << mbtype << "'" mbcrit << myname << "(initRTUDevice): Unknown tcp_mbtype='" << mbtype << "'"
<< ". Use: rtu " << ". Use: rtu "
<< " for " << it.getProp("name") << endl; << " for " << it.getProp("name") << endl;
return false; return false;
} }
string addr( IOBase::initProp(it, "mbaddr", prop_prefix, false) ); string addr( IOBase::initProp(it, "mbaddr", prop_prefix, false) );
if( addr.empty() ) if( addr.empty() )
addr = defaultMBaddr; addr = defaultMBaddr;
if( addr.empty() ) if( addr.empty() )
{ {
mbcrit << myname << "(initRTUDevice): Unknown mbaddr for " << it.getProp("name") << endl; mbcrit << myname << "(initRTUDevice): Unknown mbaddr for " << it.getProp("name") << endl;
return false; return false;
} }
d->mbaddr = ModbusRTU::str2mbAddr(addr); d->mbaddr = ModbusRTU::str2mbAddr(addr);
if( d->dtype == MBConfig::dtRTU188 ) if( d->dtype == MBConfig::dtRTU188 )
{ {
if( !d->rtu188 ) if( !d->rtu188 )
d->rtu188 = make_shared<RTUStorage>(d->mbaddr); d->rtu188 = make_shared<RTUStorage>(d->mbaddr);
} }
return true; return true;
} }
// ------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------
bool MBConfig::initItem( UniXML::iterator& it ) bool MBConfig::initItem( UniXML::iterator& it )
{ {
RSProperty p; RSProperty p;
if( !initRSProperty(p, it) ) if( !initRSProperty(p, it) )
return false; return false;
if( p.t_ai != DefaultObjectId ) // пороговые датчики в список обмена вносить не надо. if( p.t_ai != DefaultObjectId ) // пороговые датчики в список обмена вносить не надо.
return true; return true;
string addr( IOBase::initProp(it, "mbaddr", prop_prefix, false) ); string addr( IOBase::initProp(it, "mbaddr", prop_prefix, false) );
if( addr.empty() ) if( addr.empty() )
addr = defaultMBaddr; addr = defaultMBaddr;
if( addr.empty() ) if( addr.empty() )
{ {
mbcrit << myname << "(initItem): Unknown mbaddr(" << IOBase::initProp(it, "mbaddr", prop_prefix, false) << ")='" << addr << "' for " << it.getProp("name") << endl; mbcrit << myname << "(initItem): Unknown mbaddr(" << IOBase::initProp(it, "mbaddr", prop_prefix, false) << ")='" << addr << "' for " << it.getProp("name") << endl;
return false; return false;
} }
ModbusRTU::ModbusAddr mbaddr = ModbusRTU::str2mbAddr(addr); ModbusRTU::ModbusAddr mbaddr = ModbusRTU::str2mbAddr(addr);
auto dev = addDev(devices, mbaddr, it); auto dev = addDev(devices, mbaddr, it);
if( !dev ) if( !dev )
{ {
mbcrit << myname << "(initItem): " << it.getProp("name") << " CAN`T ADD for polling!" << endl; mbcrit << myname << "(initItem): " << it.getProp("name") << " CAN`T ADD for polling!" << endl;
return false; return false;
} }
ModbusRTU::ModbusData mbreg = 0; ModbusRTU::ModbusData mbreg = 0;
int fn = IOBase::initIntProp(it, "mbfunc", prop_prefix, false); int fn = IOBase::initIntProp(it, "mbfunc", prop_prefix, false);
if( dev->dtype == dtRTU188 ) if( dev->dtype == dtRTU188 )
{ {
auto r_tmp = make_shared<RegInfo>(); auto r_tmp = make_shared<RegInfo>();
if( !initRTU188item(it, r_tmp) ) if( !initRTU188item(it, r_tmp) )
{ {
mbcrit << myname << "(initItem): init RTU188 failed for " << it.getProp("name") << endl; mbcrit << myname << "(initItem): init RTU188 failed for " << it.getProp("name") << endl;
r_tmp.reset(); r_tmp.reset();
return false; return false;
} }
mbreg = RTUStorage::getRegister(r_tmp->rtuJack, r_tmp->rtuChan, p.stype); mbreg = RTUStorage::getRegister(r_tmp->rtuJack, r_tmp->rtuChan, p.stype);
fn = RTUStorage::getFunction(r_tmp->rtuJack, r_tmp->rtuChan, p.stype); fn = RTUStorage::getFunction(r_tmp->rtuJack, r_tmp->rtuChan, p.stype);
} }
else else
{ {
if( mbregFromID ) if( mbregFromID )
mbreg = p.si.id; // conf->getSensorID(it.getProp("name")); mbreg = p.si.id; // conf->getSensorID(it.getProp("name"));
else else
{ {
const string reg( IOBase::initProp(it, "mbreg", prop_prefix, false) ); const string reg( IOBase::initProp(it, "mbreg", prop_prefix, false) );
if( reg.empty() ) if( reg.empty() )
{ {
mbcrit << myname << "(initItem): unknown mbreg(" << prop_prefix << ") for " << it.getProp("name") << endl; mbcrit << myname << "(initItem): unknown mbreg(" << prop_prefix << ") for " << it.getProp("name") << endl;
return false; return false;
} }
mbreg = ModbusRTU::str2mbData(reg); mbreg = ModbusRTU::str2mbData(reg);
} }
if( p.nbit != -1 ) if( p.nbit != -1 )
{ {
if( fn == ModbusRTU::fnReadCoilStatus || fn == ModbusRTU::fnReadInputStatus ) if( fn == ModbusRTU::fnReadCoilStatus || fn == ModbusRTU::fnReadInputStatus )
{ {
mbcrit << myname << "(initItem): MISMATCHED CONFIGURATION! nbit=" << (int)p.nbit << " func=" << fn mbcrit << myname << "(initItem): MISMATCHED CONFIGURATION! nbit=" << (int)p.nbit << " func=" << fn
<< " for " << it.getProp("name") << endl; << " for " << it.getProp("name") << endl;
return false; return false;
} }
} }
} }
/*! приоритет опроса: /*! приоритет опроса:
* 1...n - задаёт "частоту" опроса. Т.е. каждые 1...n циклов * 1...n - задаёт "частоту" опроса. Т.е. каждые 1...n циклов
*/ */
size_t pollfactor = IOBase::initIntProp(it, "pollfactor", prop_prefix, false, 0); size_t pollfactor = IOBase::initIntProp(it, "pollfactor", prop_prefix, false, 0);
std::shared_ptr<RegMap> rmap; std::shared_ptr<RegMap> rmap;
auto rit = dev->pollmap.find(pollfactor); auto rit = dev->pollmap.find(pollfactor);
if( rit == dev->pollmap.end() ) if( rit == dev->pollmap.end() )
{ {
rmap = make_shared<RegMap>(); rmap = make_shared<RegMap>();
dev->pollmap.emplace(pollfactor, rmap); dev->pollmap.emplace(pollfactor, rmap);
} }
else else
rmap = rit->second; rmap = rit->second;
// формула для вычисления ID // формула для вычисления ID
// требования: // требования:
// - ID > диапазона возможных регитров // - ID > диапазона возможных регитров
// - разные функции должны давать разный ID // - разные функции должны давать разный ID
ModbusRTU::RegID rID = ModbusRTU::genRegID(mbreg, fn); ModbusRTU::RegID rID = ModbusRTU::genRegID(mbreg, fn);
auto ri = addReg(rmap, rID, mbreg, it, dev); auto ri = addReg(rmap, rID, mbreg, it, dev);
if( dev->dtype == dtMTR ) if( dev->dtype == dtMTR )
{ {
p.rnum = MTR::wsize(ri->mtrType); p.rnum = MTR::wsize(ri->mtrType);
if( p.rnum <= 0 ) if( p.rnum <= 0 )
{ {
mbcrit << myname << "(initItem): unknown word size for " << it.getProp("name") << endl; mbcrit << myname << "(initItem): unknown word size for " << it.getProp("name") << endl;
return false; return false;
} }
} }
if( !ri ) if( !ri )
return false; return false;
ri->dev = dev; ri->dev = dev;
// ПРОВЕРКА! // ПРОВЕРКА!
// если функция на запись, то надо проверить // если функция на запись, то надо проверить
// что один и тотже регистр не перезапишут несколько датчиков // что один и тотже регистр не перезапишут несколько датчиков
// это возможно только, если они пишут биты!! // это возможно только, если они пишут биты!!
// ИТОГ: // ИТОГ:
// Если для функций записи список датчиков для регистра > 1 // Если для функций записи список датчиков для регистра > 1
// значит в списке могут быть только битовые датчики // значит в списке могут быть только битовые датчики
// и если идёт попытка внести в список не битовый датчик то ОШИБКА! // и если идёт попытка внести в список не битовый датчик то ОШИБКА!
// И наоборот: если идёт попытка внести битовый датчик, а в списке // И наоборот: если идёт попытка внести битовый датчик, а в списке
// уже сидит датчик занимающий целый регистр, то тоже ОШИБКА! // уже сидит датчик занимающий целый регистр, то тоже ОШИБКА!
if( ModbusRTU::isWriteFunction(ri->mbfunc) ) if( ModbusRTU::isWriteFunction(ri->mbfunc) )
{ {
if( p.nbit < 0 && ri->slst.size() > 1 ) if( p.nbit < 0 && ri->slst.size() > 1 )
{ {
ostringstream sl; ostringstream sl;
sl << "[ "; sl << "[ ";
for( const auto& i : ri->slst ) for( const auto& i : ri->slst )
sl << ORepHelpers::getShortName(conf->oind->getMapName(i.si.id)) << ","; sl << ORepHelpers::getShortName(conf->oind->getMapName(i.si.id)) << ",";
sl << "]"; sl << "]";
ostringstream err; ostringstream err;
err << myname << "(initItem): FAILED! Sharing SAVE (not bit saving) to " err << myname << "(initItem): FAILED! Sharing SAVE (not bit saving) to "
<< " tcp_mbreg=" << ModbusRTU::dat2str(ri->mbreg) << "(" << (int)ri->mbreg << ")" << " tcp_mbreg=" << ModbusRTU::dat2str(ri->mbreg) << "(" << (int)ri->mbreg << ")"
<< " conflict with sensors " << sl.str(); << " conflict with sensors " << sl.str();
mbcrit << err.str() << endl; mbcrit << err.str() << endl;
throw uniset::SystemError(err.str()); throw uniset::SystemError(err.str());
} }
if( p.nbit >= 0 && ri->slst.size() == 1 ) if( p.nbit >= 0 && ri->slst.size() == 1 )
{ {
auto it2 = ri->slst.begin(); auto it2 = ri->slst.begin();
if( it2->nbit < 0 ) if( it2->nbit < 0 )
{ {
ostringstream err; ostringstream err;
err << myname << "(initItem): FAILED! Sharing SAVE (mbreg=" err << myname << "(initItem): FAILED! Sharing SAVE (mbreg="
<< ModbusRTU::dat2str(ri->mbreg) << "(" << (int)ri->mbreg << ") already used)!" << ModbusRTU::dat2str(ri->mbreg) << "(" << (int)ri->mbreg << ") already used)!"
<< " IGNORE --> " << it.getProp("name"); << " IGNORE --> " << it.getProp("name");
mbcrit << err.str() << endl; mbcrit << err.str() << endl;
throw uniset::SystemError(err.str()); throw uniset::SystemError(err.str());
} }
} }
// Раз это регистр для записи, то как минимум надо сперва // Раз это регистр для записи, то как минимум надо сперва
// инициализировать значением из SM // инициализировать значением из SM
ri->sm_initOK = IOBase::initIntProp(it, "sm_initOK", prop_prefix, false); ri->sm_initOK = IOBase::initIntProp(it, "sm_initOK", prop_prefix, false);
ri->mb_initOK = true; ri->mb_initOK = true;
} }
else else
{ {
ri->mb_initOK = defaultMBinitOK; ri->mb_initOK = defaultMBinitOK;
ri->sm_initOK = false; ri->sm_initOK = false;
} }
RSProperty* p1 = addProp(ri->slst, std::move(p) ); RSProperty* p1 = addProp(ri->slst, std::move(p) );
if( !p1 ) if( !p1 )
return false; return false;
p1->reg = ri; p1->reg = ri;
if( p1->rnum > 1 ) if( p1->rnum > 1 )
{ {
ri->q_count = p1->rnum; ri->q_count = p1->rnum;
ri->q_num = 1; ri->q_num = 1;
for( auto i = 1; i < p1->rnum; i++ ) for( auto i = 1; i < p1->rnum; i++ )
{ {
ModbusRTU::RegID id1 = ModbusRTU::genRegID(mbreg + i, ri->mbfunc); ModbusRTU::RegID id1 = ModbusRTU::genRegID(mbreg + i, ri->mbfunc);
auto r = addReg(rmap, id1, mbreg + i, it, dev); auto r = addReg(rmap, id1, mbreg + i, it, dev);
r->q_num = i + 1; r->q_num = i + 1;
r->q_count = 1; r->q_count = 1;
r->mbfunc = ri->mbfunc; r->mbfunc = ri->mbfunc;
r->mb_initOK = defaultMBinitOK; r->mb_initOK = defaultMBinitOK;
r->sm_initOK = false; r->sm_initOK = false;
if( ModbusRTU::isWriteFunction(ri->mbfunc) ) if( ModbusRTU::isWriteFunction(ri->mbfunc) )
{ {
// Если занимает несколько регистров, а указана функция записи "одного", // Если занимает несколько регистров, а указана функция записи "одного",
// то это ошибка.. // то это ошибка..
if( ri->mbfunc != ModbusRTU::fnWriteOutputRegisters && if( ri->mbfunc != ModbusRTU::fnWriteOutputRegisters &&
ri->mbfunc != ModbusRTU::fnForceMultipleCoils ) ri->mbfunc != ModbusRTU::fnForceMultipleCoils )
{ {
ostringstream err; ostringstream err;
err << myname << "(initItem): Bad write function ='" << ModbusRTU::fnWriteOutputSingleRegister err << myname << "(initItem): Bad write function ='" << ModbusRTU::fnWriteOutputSingleRegister
<< "' for vtype='" << p1->vType << "'" << "' for vtype='" << p1->vType << "'"
<< " tcp_mbreg=" << ModbusRTU::dat2str(ri->mbreg) << " tcp_mbreg=" << ModbusRTU::dat2str(ri->mbreg)
<< " for " << it.getProp("name"); << " for " << it.getProp("name");
mbcrit << err.str() << endl; mbcrit << err.str() << endl;
throw uniset::SystemError(err.str()); throw uniset::SystemError(err.str());
} }
} }
} }
} }
// Фомируем список инициализации // Фомируем список инициализации
bool need_init = IOBase::initIntProp(it, "preinit", prop_prefix, false); bool need_init = IOBase::initIntProp(it, "preinit", prop_prefix, false);
if( need_init && ModbusRTU::isWriteFunction(ri->mbfunc) ) if( need_init && ModbusRTU::isWriteFunction(ri->mbfunc) )
{ {
InitRegInfo ii; InitRegInfo ii;
ii.p = std::move(p); ii.p = std::move(p);
ii.dev = dev; ii.dev = dev;
ii.ri = ri; ii.ri = ri;
const string s_reg(IOBase::initProp(it, "init_mbreg", prop_prefix, false)); const string s_reg(IOBase::initProp(it, "init_mbreg", prop_prefix, false));
if( !s_reg.empty() ) if( !s_reg.empty() )
ii.mbreg = ModbusRTU::str2mbData(s_reg); ii.mbreg = ModbusRTU::str2mbData(s_reg);
else else
ii.mbreg = ri->mbreg; ii.mbreg = ri->mbreg;
string s_mbfunc(it.getProp(prop_prefix + "init_mbfunc")); string s_mbfunc(it.getProp(prop_prefix + "init_mbfunc"));
if( !s_mbfunc.empty() ) if( !s_mbfunc.empty() )
{ {
ii.mbfunc = (ModbusRTU::SlaveFunctionCode)uniset::uni_atoi(s_mbfunc); ii.mbfunc = (ModbusRTU::SlaveFunctionCode)uniset::uni_atoi(s_mbfunc);
if( ii.mbfunc == ModbusRTU::fnUnknown ) if( ii.mbfunc == ModbusRTU::fnUnknown )
{ {
mbcrit << myname << "(initItem): Unknown tcp_init_mbfunc ='" << s_mbfunc mbcrit << myname << "(initItem): Unknown tcp_init_mbfunc ='" << s_mbfunc
<< "' for " << it.getProp("name") << endl; << "' for " << it.getProp("name") << endl;
return false; return false;
} }
} }
else else
{ {
switch(ri->mbfunc) switch(ri->mbfunc)
{ {
case ModbusRTU::fnWriteOutputSingleRegister: case ModbusRTU::fnWriteOutputSingleRegister:
ii.mbfunc = ModbusRTU::fnReadOutputRegisters; ii.mbfunc = ModbusRTU::fnReadOutputRegisters;
break; break;
case ModbusRTU::fnForceSingleCoil: case ModbusRTU::fnForceSingleCoil:
ii.mbfunc = ModbusRTU::fnReadCoilStatus; ii.mbfunc = ModbusRTU::fnReadCoilStatus;
break; break;
case ModbusRTU::fnWriteOutputRegisters: case ModbusRTU::fnWriteOutputRegisters:
ii.mbfunc = ModbusRTU::fnReadOutputRegisters; ii.mbfunc = ModbusRTU::fnReadOutputRegisters;
break; break;
case ModbusRTU::fnForceMultipleCoils: case ModbusRTU::fnForceMultipleCoils:
ii.mbfunc = ModbusRTU::fnReadCoilStatus; ii.mbfunc = ModbusRTU::fnReadCoilStatus;
break; break;
default: default:
ii.mbfunc = ModbusRTU::fnReadOutputRegisters; ii.mbfunc = ModbusRTU::fnReadOutputRegisters;
break; break;
} }
} }
initRegList.emplace_back( std::move(ii) ); initRegList.emplace_back( std::move(ii) );
ri->mb_initOK = false; ri->mb_initOK = false;
ri->sm_initOK = false; ri->sm_initOK = false;
} }
return true; return true;
} }
// ------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------
bool MBConfig::initMTRitem( UniXML::iterator& it, std::shared_ptr<RegInfo>& p ) bool MBConfig::initMTRitem( UniXML::iterator& it, std::shared_ptr<RegInfo>& p )
{ {
p->mtrType = MTR::str2type(it.getProp(prop_prefix + "mtrtype")); p->mtrType = MTR::str2type(it.getProp(prop_prefix + "mtrtype"));
if( p->mtrType == MTR::mtUnknown ) if( p->mtrType == MTR::mtUnknown )
{ {
mbcrit << myname << "(readMTRItem): Unknown mtrtype '" mbcrit << myname << "(readMTRItem): Unknown mtrtype '"
<< it.getProp(prop_prefix + "mtrtype") << it.getProp(prop_prefix + "mtrtype")
<< "' for " << it.getProp("name") << endl; << "' for " << it.getProp("name") << endl;
return false; return false;
} }
return true; return true;
} }
// ------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------
bool MBConfig::initRTU188item( UniXML::iterator& it, std::shared_ptr<RegInfo>& p ) bool MBConfig::initRTU188item( UniXML::iterator& it, std::shared_ptr<RegInfo>& p )
{ {
const string jack(IOBase::initProp(it, "jack", prop_prefix, false)); const string jack(IOBase::initProp(it, "jack", prop_prefix, false));
const string chan(IOBase::initProp(it, "channel", prop_prefix, false)); const string chan(IOBase::initProp(it, "channel", prop_prefix, false));
if( jack.empty() ) if( jack.empty() )
{ {
mbcrit << myname << "(readRTU188Item): Unknown " << prop_prefix << "jack='' " mbcrit << myname << "(readRTU188Item): Unknown " << prop_prefix << "jack='' "
<< " for " << it.getProp("name") << endl; << " for " << it.getProp("name") << endl;
return false; return false;
} }
p->rtuJack = RTUStorage::s2j(jack); p->rtuJack = RTUStorage::s2j(jack);
if( p->rtuJack == RTUStorage::nUnknown ) if( p->rtuJack == RTUStorage::nUnknown )
{ {
mbcrit << myname << "(readRTU188Item): Unknown " << prop_prefix << "jack=" << jack mbcrit << myname << "(readRTU188Item): Unknown " << prop_prefix << "jack=" << jack
<< " for " << it.getProp("name") << endl; << " for " << it.getProp("name") << endl;
return false; return false;
} }
if( chan.empty() ) if( chan.empty() )
{ {
mbcrit << myname << "(readRTU188Item): Unknown channel='' " mbcrit << myname << "(readRTU188Item): Unknown channel='' "
<< " for " << it.getProp("name") << endl; << " for " << it.getProp("name") << endl;
return false; return false;
} }
p->rtuChan = uniset::uni_atoi(chan); p->rtuChan = uniset::uni_atoi(chan);
mblog2 << myname << "(readRTU188Item): add jack='" << jack << "'" mblog2 << myname << "(readRTU188Item): add jack='" << jack << "'"
<< " channel='" << p->rtuChan << "'" << endl; << " channel='" << p->rtuChan << "'" << endl;
return true; return true;
} }
// ------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------
std::ostream& operator<<( std::ostream& os, const MBConfig::DeviceType& dt ) std::ostream& operator<<( std::ostream& os, const MBConfig::DeviceType& dt )
{ {
switch(dt) switch(dt)
{ {
case MBConfig::dtRTU: case MBConfig::dtRTU:
os << "RTU"; os << "RTU";
break; break;
case MBConfig::dtMTR: case MBConfig::dtMTR:
os << "MTR"; os << "MTR";
break; break;
case MBConfig::dtRTU188: case MBConfig::dtRTU188:
os << "RTU188"; os << "RTU188";
break; break;
default: default:
os << "Unknown device type (" << (int)dt << ")"; os << "Unknown device type (" << (int)dt << ")";
break; break;
} }
return os; return os;
} }
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
std::ostream& operator<<( std::ostream& os, const MBConfig::RSProperty& p ) std::ostream& operator<<( std::ostream& os, const MBConfig::RSProperty& p )
{ {
os << " (" << ModbusRTU::dat2str(p.reg->mbreg) << ")" os << " (" << ModbusRTU::dat2str(p.reg->mbreg) << ")"
<< " sid=" << p.si.id << " sid=" << p.si.id
<< " stype=" << p.stype << " stype=" << p.stype
<< " nbit=" << (int)p.nbit << " nbit=" << (int)p.nbit
<< " nbyte=" << p.nbyte << " nbyte=" << p.nbyte
<< " rnum=" << p.rnum << " rnum=" << p.rnum
<< " safeval=" << p.safeval << " safeval=" << p.safeval
<< " invert=" << p.invert; << " invert=" << p.invert;
if( p.stype == UniversalIO::AI || p.stype == UniversalIO::AO ) if( p.stype == UniversalIO::AI || p.stype == UniversalIO::AO )
{ {
os << p.cal os << p.cal
<< " cdiagram=" << ( p.cdiagram ? "yes" : "no" ); << " cdiagram=" << ( p.cdiagram ? "yes" : "no" );
} }
return os; return os;
} }
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
void MBConfig::initDeviceList(const std::shared_ptr<UniXML>& xml ) void MBConfig::initDeviceList(const std::shared_ptr<UniXML>& xml )
{ {
xmlNode* respNode = 0; xmlNode* respNode = 0;
if( xml ) if( xml )
respNode = xml->extFindNode(cnode, 1, 1, "DeviceList"); respNode = xml->extFindNode(cnode, 1, 1, "DeviceList");
if( respNode ) if( respNode )
{ {
UniXML::iterator it1(respNode); UniXML::iterator it1(respNode);
if( it1.goChildren() ) if( it1.goChildren() )
{ {
for(; it1.getCurrent(); it1.goNext() ) for(; it1.getCurrent(); it1.goNext() )
{ {
ModbusRTU::ModbusAddr a = ModbusRTU::str2mbAddr(it1.getProp("addr")); ModbusRTU::ModbusAddr a = ModbusRTU::str2mbAddr(it1.getProp("addr"));
initDeviceInfo(devices, a, it1); initDeviceInfo(devices, a, it1);
} }
} }
else else
mbwarn << myname << "(init): <DeviceList> empty section..." << endl; mbwarn << myname << "(init): <DeviceList> empty section..." << endl;
} }
else else
mbwarn << myname << "(init): <DeviceList> not found..." << endl; mbwarn << myname << "(init): <DeviceList> not found..." << endl;
} }
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
bool MBConfig::initDeviceInfo( RTUDeviceMap& m, ModbusRTU::ModbusAddr a, UniXML::iterator& it ) bool MBConfig::initDeviceInfo( RTUDeviceMap& m, ModbusRTU::ModbusAddr a, UniXML::iterator& it )
{ {
auto d = m.find(a); auto d = m.find(a);
if( d == m.end() ) if( d == m.end() )
{ {
mbwarn << myname << "(initDeviceInfo): not found device for addr=" << ModbusRTU::addr2str(a) << endl; mbwarn << myname << "(initDeviceInfo): not found device for addr=" << ModbusRTU::addr2str(a) << endl;
return false; return false;
} }
auto& dev = d->second; auto& dev = d->second;
dev->ask_every_reg = it.getIntProp("ask_every_reg"); dev->ask_every_reg = it.getIntProp("ask_every_reg");
mbinfo << myname << "(initDeviceInfo): add addr=" << ModbusRTU::addr2str(a) mbinfo << myname << "(initDeviceInfo): add addr=" << ModbusRTU::addr2str(a)
<< " ask_every_reg=" << dev->ask_every_reg << endl; << " ask_every_reg=" << dev->ask_every_reg << endl;
string s(it.getProp("respondSensor")); string s(it.getProp("respondSensor"));
if( !s.empty() ) if( !s.empty() )
{ {
dev->resp_id = conf->getSensorID(s); dev->resp_id = conf->getSensorID(s);
if( dev->resp_id == DefaultObjectId ) if( dev->resp_id == DefaultObjectId )
{ {
mbinfo << myname << "(initDeviceInfo): not found ID for respondSensor=" << s << endl; mbinfo << myname << "(initDeviceInfo): not found ID for respondSensor=" << s << endl;
return false; return false;
} }
} }
const string mod(it.getProp("modeSensor")); const string mod(it.getProp("modeSensor"));
if( !mod.empty() ) if( !mod.empty() )
{ {
dev->mode_id = conf->getSensorID(mod); dev->mode_id = conf->getSensorID(mod);
if( dev->mode_id == DefaultObjectId ) if( dev->mode_id == DefaultObjectId )
{ {
mbcrit << myname << "(initDeviceInfo): not found ID for modeSensor=" << mod << endl; mbcrit << myname << "(initDeviceInfo): not found ID for modeSensor=" << mod << endl;
return false; return false;
} }
UniversalIO::IOType m_iotype = conf->getIOType(dev->mode_id); UniversalIO::IOType m_iotype = conf->getIOType(dev->mode_id);
if( m_iotype != UniversalIO::AI ) if( m_iotype != UniversalIO::AI )
{ {
mbcrit << myname << "(initDeviceInfo): modeSensor='" << mod << "' must be 'AI'" << endl; mbcrit << myname << "(initDeviceInfo): modeSensor='" << mod << "' must be 'AI'" << endl;
return false; return false;
} }
} }
// сперва проверим не задан ли режим "safemodeResetIfNotRespond" // сперва проверим не задан ли режим "safemodeResetIfNotRespond"
if( it.getIntProp("safemodeResetIfNotRespond") ) if( it.getIntProp("safemodeResetIfNotRespond") )
dev->safeMode = MBConfig::safeResetIfNotRespond; dev->safeMode = MBConfig::safeResetIfNotRespond;
// потом проверим датчик для "safeExternalControl" // потом проверим датчик для "safeExternalControl"
const string safemode = it.getProp("safemodeSensor"); const string safemode = it.getProp("safemodeSensor");
if( !safemode.empty() ) if( !safemode.empty() )
{ {
dev->safemode_id = conf->getSensorID(safemode); dev->safemode_id = conf->getSensorID(safemode);
if( dev->safemode_id == DefaultObjectId ) if( dev->safemode_id == DefaultObjectId )
{ {
mbcrit << myname << "(initDeviceInfo): not found ID for safemodeSensor=" << safemode << endl; mbcrit << myname << "(initDeviceInfo): not found ID for safemodeSensor=" << safemode << endl;
return false; return false;
} }
const string safemodeValue(it.getProp("safemodeValue")); const string safemodeValue(it.getProp("safemodeValue"));
if( !safemodeValue.empty() ) if( !safemodeValue.empty() )
dev->safemode_value = uni_atoi(safemodeValue); dev->safemode_value = uni_atoi(safemodeValue);
dev->safeMode = MBConfig::safeExternalControl; dev->safeMode = MBConfig::safeExternalControl;
} }
mbinfo << myname << "(initDeviceInfo): add addr=" << ModbusRTU::addr2str(a) << endl; mbinfo << myname << "(initDeviceInfo): add addr=" << ModbusRTU::addr2str(a) << endl;
int tout = it.getPIntProp("timeout", default_timeout ); int tout = it.getPIntProp("timeout", default_timeout );
dev->resp_Delay.set(0, tout); dev->resp_Delay.set(0, tout);
dev->resp_invert = it.getIntProp("invert"); dev->resp_invert = it.getIntProp("invert");
dev->resp_force = it.getIntProp("force"); dev->resp_force = it.getIntProp("force");
int init_tout = it.getPIntProp("respondInitTimeout", tout); int init_tout = it.getPIntProp("respondInitTimeout", tout);
dev->resp_ptInit.setTiming(init_tout); dev->resp_ptInit.setTiming(init_tout);
s = it.getProp("speed"); s = it.getProp("speed");
if( !s.empty() ) if( !s.empty() )
{ {
d->second->speed = ComPort::getSpeed(s); d->second->speed = ComPort::getSpeed(s);
if( d->second->speed == ComPort::ComSpeed0 ) if( d->second->speed == ComPort::ComSpeed0 )
{ {
// d->second->speed = defSpeed; // d->second->speed = defSpeed;
mbcrit << myname << "(initDeviceInfo): Unknown speed=" << s << mbcrit << myname << "(initDeviceInfo): Unknown speed=" << s <<
" for addr=" << ModbusRTU::addr2str(a) << endl; " for addr=" << ModbusRTU::addr2str(a) << endl;
return false; return false;
} }
} }
return true; return true;
} }
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
std::ostream& operator<<( std::ostream& os, const MBConfig::ExchangeMode& em ) std::ostream& operator<<( std::ostream& os, const MBConfig::ExchangeMode& em )
{ {
if( em == MBConfig::emNone ) if( em == MBConfig::emNone )
return os << "emNone"; return os << "emNone";
if( em == MBConfig::emWriteOnly ) if( em == MBConfig::emWriteOnly )
return os << "emWriteOnly"; return os << "emWriteOnly";
if( em == MBConfig::emReadOnly ) if( em == MBConfig::emReadOnly )
return os << "emReadOnly"; return os << "emReadOnly";
if( em == MBConfig::emSkipSaveToSM ) if( em == MBConfig::emSkipSaveToSM )
return os << "emSkipSaveToSM"; return os << "emSkipSaveToSM";
if( em == MBConfig::emSkipExchange ) if( em == MBConfig::emSkipExchange )
return os << "emSkipExchange"; return os << "emSkipExchange";
return os; return os;
} }
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
std::string to_string( const MBConfig::SafeMode& m ) std::string to_string( const MBConfig::SafeMode& m )
{ {
if( m == MBConfig::safeNone ) if( m == MBConfig::safeNone )
return "safeNone"; return "safeNone";
if( m == MBConfig::safeResetIfNotRespond ) if( m == MBConfig::safeResetIfNotRespond )
return "safeResetIfNotRespond"; return "safeResetIfNotRespond";
if( m == MBConfig::safeExternalControl ) if( m == MBConfig::safeExternalControl )
return "safeExternalControl"; return "safeExternalControl";
return ""; return "";
} }
ostream& operator<<( ostream& os, const MBConfig::SafeMode& m ) ostream& operator<<( ostream& os, const MBConfig::SafeMode& m )
{ {
return os << to_string(m); return os << to_string(m);
} }
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
bool MBConfig::RTUDevice::checkRespond( std::shared_ptr<DebugStream>& mblog ) bool MBConfig::RTUDevice::checkRespond( std::shared_ptr<DebugStream>& mblog )
{ {
bool prev = resp_state; bool prev = resp_state;
resp_state = resp_Delay.check( prev_numreply != numreply ) && numreply != 0; resp_state = resp_Delay.check( prev_numreply != numreply ) && numreply != 0;
mblog4 << "(checkRespond): addr=" << ModbusRTU::addr2str(mbaddr) mblog4 << "(checkRespond): addr=" << ModbusRTU::addr2str(mbaddr)
<< " respond_id=" << resp_id << " respond_id=" << resp_id
<< " state=" << resp_state << " state=" << resp_state
<< " check=" << (prev_numreply != numreply) << " check=" << (prev_numreply != numreply)
<< " delay_check=" << resp_Delay.get() << " delay_check=" << resp_Delay.get()
<< " [ timeout=" << resp_Delay.getOffDelay() << " [ timeout=" << resp_Delay.getOffDelay()
<< " numreply=" << numreply << " numreply=" << numreply
<< " prev_numreply=" << prev_numreply << " prev_numreply=" << prev_numreply
<< " resp_ptInit=" << resp_ptInit.checkTime() << " resp_ptInit=" << resp_ptInit.checkTime()
<< " ]" << " ]"
<< endl; << endl;
// если только что прошла "инициализация" возвращаем true // если только что прошла "инициализация" возвращаем true
// чтобы датчик в SM обновился.. // чтобы датчик в SM обновился..
if( trInitOK.hi(resp_ptInit.checkTime()) ) if( trInitOK.hi(resp_ptInit.checkTime()) )
return true; return true;
return ((prev != resp_state || resp_force ) && trInitOK.get() ); return ((prev != resp_state || resp_force ) && trInitOK.get() );
} }
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
std::string MBConfig::RTUDevice::getShortInfo() const std::string MBConfig::RTUDevice::getShortInfo() const
{ {
ostringstream s; size_t regs = 0;
s << "mbaddr=" << ModbusRTU::addr2str(mbaddr) << ":" for( const auto& p : pollmap )
<< " resp_state=" << resp_state regs += p.second->size();
<< " (resp_id=" << resp_id << " resp_force=" << resp_force
<< " resp_invert=" << resp_invert ostringstream s;
<< " numreply=" << numreply
<< " timeout=" << resp_Delay.getOffDelay() s << "mbaddr=" << ModbusRTU::addr2str(mbaddr) << ":"
<< " type=" << dtype << " resp_state=" << resp_state
<< " ask_every_reg=" << ask_every_reg << " (resp_id=" << resp_id << " resp_force=" << resp_force
<< ")" << endl; << " resp_invert=" << resp_invert
return s.str(); << " numreply=" << numreply
} << " timeout=" << resp_Delay.getOffDelay()
// ----------------------------------------------------------------------------- << " type=" << dtype
<< " ask_every_reg=" << ask_every_reg
<< " regs=" << regs
<< ")" << endl;
return s.str();
}
// -----------------------------------------------------------------------------
std::string MBConfig::getShortInfo() const
{
ostringstream s;
s << " recv_timeout=" << recv_timeout
<< " default_timeout=" << default_timeout
<< " aftersend_pause=" << aftersend_pause
<< " polltime=" << polltime
<< " sleepPause_msec=" << sleepPause_msec
<< " maxQueryCount=" << maxQueryCount
<< " noQueryOptimization=" << noQueryOptimization
<< " prefix=" << prefix
<< " prop_prefix=" << prop_prefix
<< " s_field=" << s_field
<< " s_fvalue=" << s_fvalue
<< " defaultMBtype=" << defaultMBtype
<< " defaultMBaddr=" << defaultMBaddr
<< " mbregFromID=" << mbregFromID
<< " defaultMBinitOK=" << defaultMBinitOK;
return s.str();
}
// -----------------------------------------------------------------------------
} // end of namespace uniset } // end of namespace uniset
...@@ -36,258 +36,259 @@ ...@@ -36,258 +36,259 @@
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
namespace uniset namespace uniset
{ {
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
/*! Конфигурация для ModbusMaster */ /*! Конфигурация для ModbusMaster */
class MBConfig class MBConfig
{ {
public: public:
MBConfig(const std::shared_ptr<uniset::Configuration>& conf MBConfig(const std::shared_ptr<uniset::Configuration>& conf
, xmlNode* cnode , xmlNode* cnode
, std::shared_ptr<SMInterface> _shm ); , std::shared_ptr<SMInterface> _shm );
~MBConfig(); ~MBConfig();
/*! Режимы работы процесса обмена */ /*! Режимы работы процесса обмена */
enum ExchangeMode enum ExchangeMode
{ {
emNone = 0, /*!< нормальная работа (по умолчанию) */ emNone = 0, /*!< нормальная работа (по умолчанию) */
emWriteOnly = 1, /*!< "только посылка данных" (работают только write-функции) */ emWriteOnly = 1, /*!< "только посылка данных" (работают только write-функции) */
emReadOnly = 2, /*!< "только чтение" (работают только read-функции) */ emReadOnly = 2, /*!< "только чтение" (работают только read-функции) */
emSkipSaveToSM = 3, /*!< не писать данные в SM (при этом работают и read и write функции) */ emSkipSaveToSM = 3, /*!< не писать данные в SM (при этом работают и read и write функции) */
emSkipExchange = 4 /*!< отключить обмен */ emSkipExchange = 4 /*!< отключить обмен */
}; };
friend std::ostream& operator<<( std::ostream& os, const ExchangeMode& em ); friend std::ostream& operator<<( std::ostream& os, const ExchangeMode& em );
/*! Режимы работы процесса обмена */ /*! Режимы работы процесса обмена */
enum SafeMode enum SafeMode
{ {
safeNone = 0, /*!< не использовать безопасный режим (по умолчанию) */ safeNone = 0, /*!< не использовать безопасный режим (по умолчанию) */
safeResetIfNotRespond = 1, /*!< выставлять безопасное значение, если пропала связь с устройством */ safeResetIfNotRespond = 1, /*!< выставлять безопасное значение, если пропала связь с устройством */
safeExternalControl = 2 /*!< управление сбросом по внешнему датчику */ safeExternalControl = 2 /*!< управление сбросом по внешнему датчику */
}; };
friend std::string to_string( const SafeMode& m ); friend std::string to_string( const SafeMode& m );
friend std::ostream& operator<<( std::ostream& os, const SafeMode& m ); friend std::ostream& operator<<( std::ostream& os, const SafeMode& m );
enum DeviceType enum DeviceType
{ {
dtUnknown, /*!< неизвестный */ dtUnknown, /*!< неизвестный */
dtRTU, /*!< RTU (default) */ dtRTU, /*!< RTU (default) */
dtMTR, /*!< MTR (DEIF) */ dtMTR, /*!< MTR (DEIF) */
dtRTU188 /*!< RTU188 (Fastwell) */ dtRTU188 /*!< RTU188 (Fastwell) */
}; };
static DeviceType getDeviceType( const std::string& dtype ) noexcept; static DeviceType getDeviceType( const std::string& dtype ) noexcept;
friend std::ostream& operator<<( std::ostream& os, const DeviceType& dt ); friend std::ostream& operator<<( std::ostream& os, const DeviceType& dt );
struct RTUDevice; struct RTUDevice;
struct RegInfo; struct RegInfo;
struct RSProperty: struct RSProperty:
public IOBase public IOBase
{ {
// only for RTU // only for RTU
int8_t nbit; /*!< bit number (-1 - not used) */ int8_t nbit; /*!< bit number (-1 - not used) */
VTypes::VType vType; /*!< type of value */ VTypes::VType vType; /*!< type of value */
uint16_t rnum; /*!< count of registers */ uint16_t rnum; /*!< count of registers */
uint8_t nbyte; /*!< byte number (1-2) */ uint8_t nbyte; /*!< byte number (1-2) */
RSProperty(): RSProperty():
nbit(-1), vType(VTypes::vtUnknown), nbit(-1), vType(VTypes::vtUnknown),
rnum(VTypes::wsize(VTypes::vtUnknown)), rnum(VTypes::wsize(VTypes::vtUnknown)),
nbyte(0) nbyte(0)
{} {}
// т.к. IOBase содержит rwmutex с запрещённым конструктором копирования // т.к. IOBase содержит rwmutex с запрещённым конструктором копирования
// приходится здесь тоже объявлять разрешенными только операции "перемещения" // приходится здесь тоже объявлять разрешенными только операции "перемещения"
RSProperty( const RSProperty& r ) = delete; RSProperty( const RSProperty& r ) = delete;
RSProperty& operator=(const RSProperty& r) = delete; RSProperty& operator=(const RSProperty& r) = delete;
RSProperty( RSProperty&& r ) = default; RSProperty( RSProperty&& r ) = default;
RSProperty& operator=(RSProperty&& r) = default; RSProperty& operator=(RSProperty&& r) = default;
std::shared_ptr<RegInfo> reg; std::shared_ptr<RegInfo> reg;
}; };
friend std::ostream& operator<<( std::ostream& os, const RSProperty& p ); friend std::ostream& operator<<( std::ostream& os, const RSProperty& p );
typedef std::list<RSProperty> PList; typedef std::list<RSProperty> PList;
std::ostream& print_plist( std::ostream& os, const PList& p ); std::ostream& print_plist( std::ostream& os, const PList& p );
typedef std::map<ModbusRTU::RegID, std::shared_ptr<RegInfo>> RegMap; typedef std::map<ModbusRTU::RegID, std::shared_ptr<RegInfo>> RegMap;
struct RegInfo struct RegInfo
{ {
// т.к. RSProperty содержит rwmutex с запрещённым конструктором копирования // т.к. RSProperty содержит rwmutex с запрещённым конструктором копирования
// приходится здесь тоже объявлять разрешенными только операции "перемещения" // приходится здесь тоже объявлять разрешенными только операции "перемещения"
RegInfo( const RegInfo& r ) = default; RegInfo( const RegInfo& r ) = default;
RegInfo& operator=(const RegInfo& r) = delete; RegInfo& operator=(const RegInfo& r) = delete;
RegInfo( RegInfo&& r ) = delete; RegInfo( RegInfo&& r ) = delete;
RegInfo& operator=(RegInfo&& r) = default; RegInfo& operator=(RegInfo&& r) = default;
RegInfo() = default; RegInfo() = default;
ModbusRTU::ModbusData mbval = { 0 }; ModbusRTU::ModbusData mbval = { 0 };
ModbusRTU::ModbusData mbreg = { 0 }; /*!< регистр */ ModbusRTU::ModbusData mbreg = { 0 }; /*!< регистр */
ModbusRTU::SlaveFunctionCode mbfunc = { ModbusRTU::fnUnknown }; /*!< функция для чтения/записи */ ModbusRTU::SlaveFunctionCode mbfunc = { ModbusRTU::fnUnknown }; /*!< функция для чтения/записи */
PList slst; PList slst;
ModbusRTU::RegID regID = { 0 }; ModbusRTU::RegID regID = { 0 };
std::shared_ptr<RTUDevice> dev; std::shared_ptr<RTUDevice> dev;
// only for RTU188 // only for RTU188
RTUStorage::RTUJack rtuJack = { RTUStorage::nUnknown }; RTUStorage::RTUJack rtuJack = { RTUStorage::nUnknown };
int rtuChan = { 0 }; int rtuChan = { 0 };
// only for MTR // only for MTR
MTR::MTRType mtrType = { MTR::mtUnknown }; /*!< тип регистра (согласно спецификации на MTR) */ MTR::MTRType mtrType = { MTR::mtUnknown }; /*!< тип регистра (согласно спецификации на MTR) */
// optimization // optimization
size_t q_num = { 0 }; /*!< number in query */ size_t q_num = { 0 }; /*!< number in query */
size_t q_count = { 1 }; /*!< count registers for query */ size_t q_count = { 1 }; /*!< count registers for query */
RegMap::iterator rit; RegMap::iterator rit;
// начальная инициализация для "записываемых" регистров // начальная инициализация для "записываемых" регистров
// Механизм: // Механизм:
// Если tcp_preinit="1", то сперва будет сделано чтение значения из устройства. // Если tcp_preinit="1", то сперва будет сделано чтение значения из устройства.
// при этом флаг mb_init=false пока не пройдёт успешной инициализации // при этом флаг mb_init=false пока не пройдёт успешной инициализации
// Если tcp_preinit="0", то флаг mb_init сразу выставляется в true. // Если tcp_preinit="0", то флаг mb_init сразу выставляется в true.
bool mb_initOK = { false }; /*!< инициализировалось ли значение из устройства */ bool mb_initOK = { false }; /*!< инициализировалось ли значение из устройства */
// Флаг sm_init означает, что писать в устройство нельзя, т.к. значение в "карте регистров" // Флаг sm_init означает, что писать в устройство нельзя, т.к. значение в "карте регистров"
// ещё не инициализировано из SM // ещё не инициализировано из SM
bool sm_initOK = { false }; /*!< инициализировалось ли значение из 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 );
friend std::ostream& operator<<( std::ostream& os, const RegInfo* r ); friend std::ostream& operator<<( std::ostream& os, const RegInfo* r );
struct RTUDevice struct RTUDevice
{ {
ModbusRTU::ModbusAddr mbaddr = { 0 }; /*!< адрес устройства */ ModbusRTU::ModbusAddr mbaddr = { 0 }; /*!< адрес устройства */
std::unordered_map<size_t, std::shared_ptr<RegMap>> pollmap; std::unordered_map<size_t, std::shared_ptr<RegMap>> pollmap;
DeviceType dtype = { dtUnknown }; /*!< тип устройства */ DeviceType dtype = { dtUnknown }; /*!< тип устройства */
// resp - respond..(контроль наличия связи) // resp - respond..(контроль наличия связи)
uniset::ObjectId resp_id = { uniset::DefaultObjectId }; uniset::ObjectId resp_id = { uniset::DefaultObjectId };
IOController::IOStateList::iterator resp_it; IOController::IOStateList::iterator resp_it;
DelayTimer resp_Delay; // таймер для формирования задержки на отпускание (пропадание связи) DelayTimer resp_Delay; // таймер для формирования задержки на отпускание (пропадание связи)
PassiveTimer resp_ptInit; // таймер для формирования задержки на инициализацию связи (задержка на выставление датчика связи после запуска) PassiveTimer resp_ptInit; // таймер для формирования задержки на инициализацию связи (задержка на выставление датчика связи после запуска)
bool resp_state = { false }; bool resp_state = { false };
bool resp_invert = { false }; bool resp_invert = { false };
bool resp_force = { false }; bool resp_force = { false };
Trigger trInitOK; // триггер для "инициализации" Trigger trInitOK; // триггер для "инициализации"
std::atomic<size_t> numreply = { 0 }; // количество успешных запросов.. std::atomic<size_t> numreply = { 0 }; // количество успешных запросов..
std::atomic<size_t> prev_numreply = { 0 }; std::atomic<size_t> prev_numreply = { 0 };
// //
bool ask_every_reg = { false }; /*!< опрашивать ли каждый регистр, независимо от результата опроса предыдущего. По умолчанию false - прервать опрос при первом же timeout */ bool ask_every_reg = { false }; /*!< опрашивать ли каждый регистр, независимо от результата опроса предыдущего. По умолчанию false - прервать опрос при первом же timeout */
// режим работы // режим работы
uniset::ObjectId mode_id = { uniset::DefaultObjectId }; uniset::ObjectId mode_id = { uniset::DefaultObjectId };
IOController::IOStateList::iterator mode_it; IOController::IOStateList::iterator mode_it;
long mode = { emNone }; // режим работы с устройством (см. ExchangeMode) long mode = { emNone }; // режим работы с устройством (см. ExchangeMode)
// safe mode // safe mode
long safeMode = { safeNone }; /*!< режим безопасного состояния см. SafeMode */ long safeMode = { safeNone }; /*!< режим безопасного состояния см. SafeMode */
uniset::ObjectId safemode_id = { uniset::DefaultObjectId }; /*!< идентификатор для датчика безопасного режима */ uniset::ObjectId safemode_id = { uniset::DefaultObjectId }; /*!< идентификатор для датчика безопасного режима */
IOController::IOStateList::iterator safemode_it; IOController::IOStateList::iterator safemode_it;
long safemode_value = { 1 }; long safemode_value = { 1 };
// return TRUE if state changed // return TRUE if state changed
bool checkRespond( std::shared_ptr<DebugStream>& log ); bool checkRespond( std::shared_ptr<DebugStream>& log );
// специфические поля для RS // специфические поля для RS
ComPort::Speed speed = { ComPort::ComSpeed38400 }; ComPort::Speed speed = { ComPort::ComSpeed38400 };
std::shared_ptr<RTUStorage> rtu188; std::shared_ptr<RTUStorage> rtu188;
std::string getShortInfo() const; std::string getShortInfo() const;
}; };
friend std::ostream& operator<<( std::ostream& os, RTUDevice& d ); friend std::ostream& operator<<( std::ostream& os, RTUDevice& d );
typedef std::unordered_map<ModbusRTU::ModbusAddr, std::shared_ptr<RTUDevice>> RTUDeviceMap; typedef std::unordered_map<ModbusRTU::ModbusAddr, std::shared_ptr<RTUDevice>> RTUDeviceMap;
friend std::ostream& operator<<( std::ostream& os, RTUDeviceMap& d ); friend std::ostream& operator<<( std::ostream& os, RTUDeviceMap& d );
static void printMap(RTUDeviceMap& d); static void printMap(RTUDeviceMap& d);
typedef std::list<IOBase> ThresholdList; typedef std::list<IOBase> ThresholdList;
struct InitRegInfo struct InitRegInfo
{ {
InitRegInfo(): InitRegInfo():
dev(0), mbreg(0), dev(0), mbreg(0),
mbfunc(ModbusRTU::fnUnknown), mbfunc(ModbusRTU::fnUnknown),
initOK(false) initOK(false)
{} {}
RSProperty p; RSProperty p;
std::shared_ptr<RTUDevice> dev; std::shared_ptr<RTUDevice> dev;
ModbusRTU::ModbusData mbreg; ModbusRTU::ModbusData mbreg;
ModbusRTU::SlaveFunctionCode mbfunc; ModbusRTU::SlaveFunctionCode mbfunc;
bool initOK; bool initOK;
std::shared_ptr<RegInfo> ri; std::shared_ptr<RegInfo> ri;
}; };
typedef std::list<InitRegInfo> InitList; typedef std::list<InitRegInfo> InitList;
static void rtuQueryOptimization( RTUDeviceMap& m, size_t maxQueryCount ); static void rtuQueryOptimization( RTUDeviceMap& m, size_t maxQueryCount );
static void rtuQueryOptimizationForDevice( const std::shared_ptr<RTUDevice>& d, 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 ); static void rtuQueryOptimizationForRegMap( const std::shared_ptr<RegMap>& regmap, size_t maxQueryCount );
// т.к. пороговые датчики не связаны напрямую с обменом, создаём для них отдельный список // т.к. пороговые датчики не связаны напрямую с обменом, создаём для них отдельный список
// и отдельно его проверяем потом // и отдельно его проверяем потом
ThresholdList thrlist; ThresholdList thrlist;
RTUDeviceMap devices; RTUDeviceMap devices;
InitList initRegList; /*!< список регистров для инициализации */ InitList initRegList; /*!< список регистров для инициализации */
void loadConfig( const std::shared_ptr<uniset::UniXML>& xml, UniXML::iterator sensorsSection ); void loadConfig( const std::shared_ptr<uniset::UniXML>& xml, UniXML::iterator sensorsSection );
void initDeviceList( const std::shared_ptr<UniXML>& xml ); void initDeviceList( const std::shared_ptr<UniXML>& xml );
bool initItem( UniXML::iterator& it ); bool initItem( UniXML::iterator& it );
std::string s_field; std::string s_field;
std::string s_fvalue; std::string s_fvalue;
// определение timeout для соединения // определение timeout для соединения
timeout_t recv_timeout = { 500 }; // msec timeout_t recv_timeout = { 500 }; // msec
timeout_t default_timeout = { 5000 }; // msec timeout_t default_timeout = { 5000 }; // msec
timeout_t aftersend_pause = { 0 }; timeout_t aftersend_pause = { 0 };
timeout_t polltime = { 100 }; /*!< периодичность обновления данных, [мсек] */ timeout_t polltime = { 100 }; /*!< периодичность обновления данных, [мсек] */
timeout_t sleepPause_msec = { 10 }; timeout_t sleepPause_msec = { 10 };
size_t maxQueryCount = { ModbusRTU::MAXDATALEN }; /*!< максимальное количество регистров для одного запроса */ size_t maxQueryCount = { ModbusRTU::MAXDATALEN }; /*!< максимальное количество регистров для одного запроса */
xmlNode* cnode = { 0 }; xmlNode* cnode = { 0 };
std::shared_ptr<DebugStream> mblog; std::shared_ptr<DebugStream> mblog;
std::string myname; std::string myname;
std::string prefix; std::string prefix;
std::string prop_prefix; /*!< префикс для считывания параметров обмена */ std::string prop_prefix; /*!< префикс для считывания параметров обмена */
std::string defaultMBtype; std::string defaultMBtype;
std::string defaultMBaddr; std::string defaultMBaddr;
bool mbregFromID = { false }; bool mbregFromID = { false };
bool defaultMBinitOK = { false }; // флаг определяющий нужно ли ждать "первого обмена" или при запуске сохранять в SM значение default. bool defaultMBinitOK = { false }; // флаг определяющий нужно ли ждать "первого обмена" или при запуске сохранять в SM значение default.
bool noQueryOptimization = { false }; bool noQueryOptimization = { false };
std::shared_ptr<uniset::Configuration> conf; std::shared_ptr<uniset::Configuration> conf;
std::shared_ptr<SMInterface> shm; std::shared_ptr<SMInterface> shm;
void cloneParams( const std::shared_ptr<MBConfig>& conf ); void cloneParams( const std::shared_ptr<MBConfig>& conf );
std::string getShortInfo() const;
protected:
protected:
bool initSMValue( ModbusRTU::ModbusData* data, int count, RSProperty* p );
bool initSMValue( ModbusRTU::ModbusData* data, int count, RSProperty* p );
void readConfiguration(const std::shared_ptr<uniset::UniXML>& xml, UniXML::iterator sensorsSection );
void initOffsetList(); 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 ); std::shared_ptr<RTUDevice> addDev( RTUDeviceMap& dmap, ModbusRTU::ModbusAddr a, UniXML::iterator& it );
RSProperty* addProp( PList& plist, RSProperty&& p ); 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 initMTRitem(UniXML::iterator& it, std::shared_ptr<RegInfo>& p );
bool initRSProperty( RSProperty& p, UniXML::iterator& it ); bool initRTU188item(UniXML::iterator& it, std::shared_ptr<RegInfo>& p );
bool initRegInfo(std::shared_ptr<RegInfo>& r, UniXML::iterator& it, std::shared_ptr<RTUDevice>& dev ); bool initRSProperty( RSProperty& p, UniXML::iterator& it );
bool initRTUDevice( std::shared_ptr<RTUDevice>& d, UniXML::iterator& it ); bool initRegInfo(std::shared_ptr<RegInfo>& r, UniXML::iterator& it, std::shared_ptr<RTUDevice>& dev );
bool initDeviceInfo( RTUDeviceMap& m, ModbusRTU::ModbusAddr a, UniXML::iterator& it ); bool initRTUDevice( std::shared_ptr<RTUDevice>& d, UniXML::iterator& it );
}; bool initDeviceInfo( RTUDeviceMap& m, ModbusRTU::ModbusAddr a, UniXML::iterator& it );
// -------------------------------------------------------------------------- };
// --------------------------------------------------------------------------
} // end of namespace uniset } // end of namespace uniset
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
#endif // _MBConfig_H_ #endif // _MBConfig_H_
......
This source diff could not be displayed because it is too large. You can view the blob instead.
...@@ -49,132 +49,126 @@ ...@@ -49,132 +49,126 @@
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
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 Timer enum Timer
{ {
tmExchange tmExchange
}; };
void execute(); void execute();
inline std::shared_ptr<LogAgregator> getLogAggregator() inline std::shared_ptr<LogAgregator> getLogAggregator()
{ {
return loga; return loga;
} }
inline std::shared_ptr<DebugStream> log() inline std::shared_ptr<DebugStream> log()
{ {
return mblog; return mblog;
} }
virtual uniset::SimpleInfo* getInfo( const char* userparam = 0 ) override; virtual uniset::SimpleInfo* getInfo( const char* userparam = 0 ) override;
bool reconfigure( const std::string& confile ); bool reconfigure( const std::string& confile );
protected: protected:
virtual void step(); virtual void step();
virtual void sysCommand( const uniset::SystemMessage* msg ) override; virtual void sysCommand( const uniset::SystemMessage* msg ) override;
virtual void sensorInfo( const uniset::SensorMessage* sm ) override; virtual void sensorInfo( const uniset::SensorMessage* sm ) override;
virtual void timerInfo( const uniset::TimerMessage* tm ) override; virtual void timerInfo( const uniset::TimerMessage* tm ) override;
virtual void askSensors( UniversalIO::UIOCommand cmd ); virtual void askSensors( UniversalIO::UIOCommand cmd );
virtual void initOutput(); virtual void initOutput();
virtual bool deactivateObject() override; virtual bool deactivateObject() override;
virtual bool activateObject() override; virtual bool activateObject() override;
virtual void initIterators(); virtual void initIterators();
virtual void initValues(); virtual void initValues();
#ifndef DISABLE_REST_API
void firstInitRegisters(); // http API
bool preInitRead( MBConfig::InitList::iterator& p ); virtual Poco::JSON::Object::Ptr httpHelp( const Poco::URI::QueryParameters& p ) override;
bool initSMValue( ModbusRTU::ModbusData* data, int count, MBConfig::RSProperty* p ); virtual Poco::JSON::Object::Ptr httpRequest( const std::string& req, const Poco::URI::QueryParameters& p ) override;
bool allInitOK; #endif
void firstInitRegisters();
virtual std::shared_ptr<ModbusClient> initMB( bool reopen = false ) = 0; bool preInitRead( MBConfig::InitList::iterator& p );
bool initSMValue( ModbusRTU::ModbusData* data, int count, MBConfig::RSProperty* p );
virtual bool poll(); bool allInitOK;
bool pollRTU( std::shared_ptr<MBConfig::RTUDevice>& dev, MBConfig::RegMap::iterator& it );
void updateSM();
// в функции передаётся итератор,
// т.к. в них идёт итерирование в случае если запрос в несколько регистров
void updateRTU(MBConfig::RegMap::iterator& it);
void updateMTR(MBConfig::RegMap::iterator& it);
void updateRTU188(MBConfig::RegMap::iterator& it);
void updateRSProperty( MBConfig::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<MBConfig::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 initOffsetList();
std::string initPropPrefix( const std::string& s_filed, const std::string& def_prop_prefix );
xmlNode* cnode = { 0 };
std::shared_ptr<SMInterface> shm;
timeout_t initPause = { 3000 };
uniset::uniset_rwmutex mutex_start;
bool force = { false }; /*!< флаг означающий, что надо сохранять в SM, даже если значение не менялось */
bool force_out = { false }; /*!< флаг означающий, принудительного чтения выходов */
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 = { MBConfig::emNone }; /*!< режим работы см. ExchangeMode */
std::atomic_bool activated = { false };
std::atomic_bool canceled = { false };
timeout_t activateTimeout = { 20000 }; // msec
bool notUseExchangeTimer = { false };
timeout_t stat_time = { 0 }; /*!< время сбора статистики обмена, 0 - отключена */
size_t poll_count = { 0 };
PassiveTimer ptStatistic; /*!< таймер для сбора статистики обмена */
std::string statInfo = { "" };
std::shared_ptr<ModbusClient> mb;
PassiveTimer ptReopen; /*!< таймер для переоткрытия соединения */
Trigger trReopen;
PassiveTimer ptInitChannel; /*!< задержка не инициализацию связи */ virtual std::shared_ptr<ModbusClient> initMB( bool reopen = false ) = 0;
virtual bool poll();
bool pollRTU( std::shared_ptr<MBConfig::RTUDevice>& dev, MBConfig::RegMap::iterator& it );
void updateSM();
// в функции передаётся итератор,
// т.к. в них идёт итерирование в случае если запрос в несколько регистров
void updateRTU(MBConfig::RegMap::iterator& it);
void updateMTR(MBConfig::RegMap::iterator& it);
void updateRTU188(MBConfig::RegMap::iterator& it);
void updateRSProperty( MBConfig::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<MBConfig::RTUDevice>& dev ) const noexcept;
bool isProcActive() const;
void setProcActive( bool st );
bool waitSMReady();
bool readItem( const std::shared_ptr<UniXML>& xml, UniXML::iterator& it, xmlNode* sec );
bool initItem( UniXML::iterator& it );
void initOffsetList();
std::string initPropPrefix( const std::string& s_filed, const std::string& def_prop_prefix );
xmlNode* cnode = { 0 };
std::shared_ptr<SMInterface> shm;
// т.к. пороговые датчики не связаны напрямую с обменом, создаём для них отдельный список timeout_t initPause = { 3000 };
// и отдельно его проверяем потом uniset::uniset_rwmutex mutex_start;
typedef std::list<IOBase> ThresholdList;
ThresholdList thrlist;
std::string defaultMBtype; bool force = { false }; /*!< флаг означающий, что надо сохранять в SM, даже если значение не менялось */
std::string defaultMBaddr; bool force_out = { false }; /*!< флаг означающий, принудительного чтения выходов */
bool defaultMBinitOK = { false }; // флаг определяющий нужно ли ждать "первого обмена" или при запуске сохранять в SM значение default.
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 = { MBConfig::emNone }; /*!< режим работы см. ExchangeMode */
std::atomic_bool activated = { false };
std::atomic_bool canceled = { false };
timeout_t activateTimeout = { 20000 }; // msec
bool notUseExchangeTimer = { false };
timeout_t stat_time = { 0 }; /*!< время сбора статистики обмена, 0 - отключена */
size_t poll_count = { 0 };
PassiveTimer ptStatistic; /*!< таймер для сбора статистики обмена */
std::string statInfo = { "" };
std::shared_ptr<ModbusClient> mb;
PassiveTimer ptReopen; /*!< таймер для переоткрытия соединения */
Trigger trReopen;
PassiveTimer ptInitChannel; /*!< задержка не инициализацию связи */
std::shared_ptr<LogAgregator> loga; std::shared_ptr<LogAgregator> loga;
std::shared_ptr<DebugStream> mblog; std::shared_ptr<DebugStream> mblog;
...@@ -182,22 +176,16 @@ namespace uniset ...@@ -182,22 +176,16 @@ 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 }; /*!< текущий номер цикла опроса */
std::shared_ptr<uniset::MBConfig> mbconf; std::shared_ptr<uniset::MBConfig> mbconf;
uniset::uniset_rwmutex mutex_conf; uniset::uniset_rwmutex mutex_conf;
private: private:
MBExchange(); 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_";
mbconf->prop_prefix = initPropPrefix(mbconf->s_field, "tcp_"); mbconf->prop_prefix = initPropPrefix(mbconf->s_field, "tcp_");
mbinfo << myname << "(init): prop_prefix=" << mbconf->prop_prefix << endl; mbinfo << myname << "(init): prop_prefix=" << mbconf->prop_prefix << endl;
UniXML::iterator it(cnode); UniXML::iterator it(cnode);
...@@ -61,16 +61,16 @@ MBTCPMaster::MBTCPMaster(uniset::ObjectId objId, uniset::ObjectId shmId, ...@@ -61,16 +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()); mbconf->loadConfig(conf->getConfXML(), conf->getXMLSensorsSection());
else else
ic->addReadItem( sigc::mem_fun(this, &MBTCPMaster::readItem) ); 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() )
MBConfig::printMap(mbconf->devices); MBConfig::printMap(mbconf->devices);
} }
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
MBTCPMaster::~MBTCPMaster() MBTCPMaster::~MBTCPMaster()
...@@ -105,11 +105,11 @@ std::shared_ptr<ModbusClient> MBTCPMaster::initMB( bool reopen ) ...@@ -105,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( mbconf->recv_timeout > 0 ) if( mbconf->recv_timeout > 0 )
mbtcp->setTimeout(mbconf->recv_timeout); mbtcp->setTimeout(mbconf->recv_timeout);
mbtcp->setSleepPause(mbconf->sleepPause_msec); mbtcp->setSleepPause(mbconf->sleepPause_msec);
mbtcp->setAfterSendPause(mbconf->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;
...@@ -140,45 +140,45 @@ void MBTCPMaster::final_thread() ...@@ -140,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(mbconf->polltime); msleep(mbconf->polltime);
} }
dinfo << myname << "(poll_thread): thread finished.." << endl; dinfo << myname << "(poll_thread): thread finished.." << endl;
} }
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
bool MBTCPMaster::deactivateObject() bool MBTCPMaster::deactivateObject()
......
...@@ -35,6 +35,8 @@ namespace uniset ...@@ -35,6 +35,8 @@ namespace uniset
- \ref sec_MBTCP_Conf - \ref sec_MBTCP_Conf
- \ref sec_MBTCP_ConfList - \ref sec_MBTCP_ConfList
- \ref sec_MBTCP_ExchangeMode - \ref sec_MBTCP_ExchangeMode
- \ref sec_MBTCP_ReloadConfig
- \ref sec_MBTCP_REST_API
\section sec_MBTCP_Comm Общее описание ModbusTCP master \section sec_MBTCP_Comm Общее описание ModbusTCP master
Класс реализует процесс обмена (опрос/запись) с RTU-устройствами, Класс реализует процесс обмена (опрос/запись) с RTU-устройствами,
...@@ -207,6 +209,34 @@ namespace uniset ...@@ -207,6 +209,34 @@ namespace uniset
Если указан и параметр \a safemodeSensor=".." и \a safemodeResetIfNotRespond="1", то будет использован Если указан и параметр \a safemodeSensor=".." и \a safemodeResetIfNotRespond="1", то будет использован
режим \b safeExternalControl (как более приоритетный). режим \b safeExternalControl (как более приоритетный).
\section sec_MBTCP_ReloadConfig Переконфигурирование "на ходу"
В процессе реализована возможность перечитать конфигурацию "на ходу". Для этого достаточно процессу
послать команду SystemMessage::Reconfigure или воспользоваться HTTP API, где можно указать файл из
которого произвести загрузку (см. \ref sec_MBTCP_REST_API).
При этом процесс приостанавливает обмен и перезагружает конфигурационный файл с которым был запущен.
Переконфигурировать можно регистры, список устройств, адреса и любые свойства. В том числе возможно
добавление новых регистров в список или уменьшение списка существующих.
\warning Если во время загрузки новой конфигурации будет найдена какая-то ошибка, то конфигурация не будет применена.
Ошибку можно будет увидеть в логах процесса.
\warning Важно понимать, что это перезагрузка только настроек касающихся ModbusMaster, поэтому список датчиков
и другие базовые настройки должны совпадать с исходным файлом. Т.е. возможно только переопределение параметров
касающихся обмена, а не всего конфига в целом.
\section sec_MBTCP_REST_API ModbusMaster HTTP API
\code
/help - Получение списка доступных команд
/ - получение стандартной информации
/reconfigure?confile=/path/to/confile - Перезагрузить конфигурацию
confile - абсолютный путь до файла с конфигурацией. Не обязательный параметр.
\endcode
\warning Важно иметь ввиду, что если указывается confile, то он должен совпадать с базовым configure.xml
в идентификаторах датчиков. И как минимум должен содержать соответствующую секцию настроек для процесса
и те датчики, которые участвуют в обмене. Т.к. реальный config (глобальный) не подменяется, из указанного
файла только загружаются необходимые для инициализации обмена параметры.
*/ */
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
/*! /*!
......
...@@ -33,168 +33,168 @@ MBTCPMultiMaster::MBTCPMultiMaster( uniset::ObjectId objId, uniset::ObjectId shm ...@@ -33,168 +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();
mbconf->prop_prefix = initPropPrefix(mbconf->s_field, "tcp_"); mbconf->prop_prefix = initPropPrefix(mbconf->s_field, "tcp_");
mbinfo << myname << "(init): prop_prefix=" << mbconf->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"), mbconf->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", mbconf->recv_timeout); sinf->recv_timeout = it1.getPIntProp("recv_timeout", mbconf->recv_timeout);
sinf->aftersend_pause = it1.getPIntProp("aftersend_pause", mbconf->aftersend_pause); sinf->aftersend_pause = it1.getPIntProp("aftersend_pause", mbconf->aftersend_pause);
sinf->sleepPause_usec = it1.getPIntProp("sleepPause_msec", mbconf->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()); mbconf->loadConfig(conf->getConfXML(), conf->getXMLSensorsSection());
else else
ic->addReadItem( sigc::mem_fun(this, &MBTCPMultiMaster::readItem) ); ic->addReadItem( sigc::mem_fun(this, &MBTCPMultiMaster::readItem) );
pollThread = unisetstd::make_unique<ThreadCreator<MBTCPMultiMaster>>(this, &MBTCPMultiMaster::poll_thread); pollThread = unisetstd::make_unique<ThreadCreator<MBTCPMultiMaster>>(this, &MBTCPMultiMaster::poll_thread);
pollThread->setFinalAction(this, &MBTCPMultiMaster::final_thread); pollThread->setFinalAction(this, &MBTCPMultiMaster::final_thread);
checkThread = unisetstd::make_unique<ThreadCreator<MBTCPMultiMaster>>(this, &MBTCPMultiMaster::check_thread); checkThread = unisetstd::make_unique<ThreadCreator<MBTCPMultiMaster>>(this, &MBTCPMultiMaster::check_thread);
checkThread->setFinalAction(this, &MBTCPMultiMaster::final_thread); checkThread->setFinalAction(this, &MBTCPMultiMaster::final_thread);
// Т.к. при "многоканальном" доступе к slave, смена канала должна происходит сразу после // Т.к. при "многоканальном" доступе к slave, смена канала должна происходит сразу после
// неудачной попытки запросов по одному из каналов, то ПЕРЕОПРЕДЕЛЯЕМ reopen, на channel-timeout.. // неудачной попытки запросов по одному из каналов, то ПЕРЕОПРЕДЕЛЯЕМ reopen, на channel-timeout..
int tout = conf->getArgPInt("--" + prefix + "-default-channel-timeout", it.getProp("channelTimeout"), mbconf->default_timeout); int tout = conf->getArgPInt("--" + prefix + "-default-channel-timeout", it.getProp("channelTimeout"), mbconf->default_timeout);
ptReopen.setTiming(tout); ptReopen.setTiming(tout);
if( mblog->is_info() ) if( mblog->is_info() )
MBConfig::printMap(mbconf->devices); MBConfig::printMap(mbconf->devices);
} }
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
MBTCPMultiMaster::~MBTCPMultiMaster() MBTCPMultiMaster::~MBTCPMultiMaster()
...@@ -226,111 +226,111 @@ MBTCPMultiMaster::~MBTCPMultiMaster() ...@@ -226,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()
...@@ -455,120 +455,121 @@ void MBTCPMultiMaster::sysCommand( const uniset::SystemMessage* sm ) ...@@ -455,120 +455,121 @@ 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(mbconf->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 )
shm->initIterator(it->respond_it); for( auto&& it : mblist )
shm->initIterator(it->respond_it);
} }
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
bool MBTCPMultiMaster::deactivateObject() bool MBTCPMultiMaster::deactivateObject()
...@@ -599,7 +600,7 @@ void MBTCPMultiMaster::initCheckConnectionParameters() ...@@ -599,7 +600,7 @@ void MBTCPMultiMaster::initCheckConnectionParameters()
{ {
auto conf = uniset_conf(); auto conf = uniset_conf();
bool initFromRegMap = ( findArgParam("--" + mbconf->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;
...@@ -615,16 +616,17 @@ void MBTCPMultiMaster::initCheckConnectionParameters() ...@@ -615,16 +616,17 @@ void MBTCPMultiMaster::initCheckConnectionParameters()
ModbusRTU::ModbusAddr checkAddr = { 0x00 }; ModbusRTU::ModbusAddr checkAddr = { 0x00 };
ModbusRTU::ModbusData checkReg = { 0 }; ModbusRTU::ModbusData checkReg = { 0 };
if( mbconf->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 : mbconf->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;
...@@ -667,13 +669,13 @@ void MBTCPMultiMaster::initCheckConnectionParameters() ...@@ -667,13 +669,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;
} }
} }
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
......
...@@ -33,63 +33,63 @@ RTUExchange::RTUExchange(uniset::ObjectId objId, uniset::ObjectId shmId, const s ...@@ -33,63 +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 --" + mbconf->prefix + "-name" ); throw uniset::SystemError("(RTUExchange): objId=-1?!! Use --" + mbconf->prefix + "-name" );
auto conf = uniset_conf(); auto conf = uniset_conf();
// префикс для "свойств" - по умолчанию // префикс для "свойств" - по умолчанию
mbconf->prop_prefix = ""; mbconf->prop_prefix = "";
// если задано поле для "фильтрации" // если задано поле для "фильтрации"
// то в качестве префикса используем его // то в качестве префикса используем его
if( !mbconf->s_field.empty() ) if( !mbconf->s_field.empty() )
mbconf->prop_prefix = mbconf->s_field + "_"; mbconf->prop_prefix = mbconf->s_field + "_";
// если "принудительно" задан префикс // если "принудительно" задан префикс
// используем его. // используем его.
{ {
string p("--" + mbconf->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] != '-' ) if( !v.empty() && v[0] != '-' )
mbconf->prop_prefix = v; mbconf->prop_prefix = v;
// если параметр всё-таки указан, считаем, что это попытка задать "пустой" префикс // если параметр всё-таки указан, считаем, что это попытка задать "пустой" префикс
else if( findArgParam(p, conf->getArgc(), conf->getArgv()) != -1 ) else if( findArgParam(p, conf->getArgc(), conf->getArgv()) != -1 )
mbconf->prop_prefix = ""; mbconf->prop_prefix = "";
} }
mbinfo << myname << "(init): prop_prefix=" << mbconf->prop_prefix << endl; mbinfo << myname << "(init): prop_prefix=" << mbconf->prop_prefix << endl;
UniXML::iterator it(cnode); UniXML::iterator it(cnode);
// ---------- init RS ---------- // ---------- init RS ----------
devname = conf->getArgParam("--" + mbconf->prefix + "-dev", it.getProp("device")); 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("--" + mbconf->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("--" + mbconf->prefix + "-use485F", it.getProp("use485F")); use485F = conf->getArgInt("--" + mbconf->prefix + "-use485F", it.getProp("use485F"));
transmitCtl = conf->getArgInt("--" + mbconf->prefix + "-transmit-ctl", it.getProp("transmitCtl")); transmitCtl = conf->getArgInt("--" + mbconf->prefix + "-transmit-ctl", it.getProp("transmitCtl"));
defSpeed = ComPort::getSpeed(speed); defSpeed = ComPort::getSpeed(speed);
mbconf->sleepPause_msec = conf->getArgPInt("--" + mbconf->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("--" + mbconf->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()); mbconf->loadConfig(conf->getConfXML(), conf->getXMLSensorsSection());
else else
ic->addReadItem( sigc::mem_fun(this, &RTUExchange::readItem) ); ic->addReadItem( sigc::mem_fun(this, &RTUExchange::readItem) );
initMB(false); initMB(false);
if( dlog()->is_info() ) if( dlog()->is_info() )
MBConfig::printMap(mbconf->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 )
...@@ -112,68 +112,68 @@ RTUExchange::~RTUExchange() ...@@ -112,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( mbconf->recv_timeout > 0 ) if( mbconf->recv_timeout > 0 )
mbrtu->setTimeout(mbconf->recv_timeout); mbrtu->setTimeout(mbconf->recv_timeout);
mbrtu->setSleepPause(mbconf->sleepPause_msec); mbrtu->setSleepPause(mbconf->sleepPause_msec);
mbrtu->setAfterSendPause(mbconf->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()
...@@ -206,142 +206,142 @@ void RTUExchange::step() ...@@ -206,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 : mbconf->devices ) for( auto it1 : mbconf->devices )
{ {
auto d = it1.second; auto d = it1.second;
if( d->mode_id != DefaultObjectId && d->mode == MBConfig::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 == MBConfig::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 == MBConfig::dtRTU || d->dtype == MBConfig::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: ";
mbconf->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 : mbconf->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,
......
...@@ -53,7 +53,7 @@ namespace uniset ...@@ -53,7 +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;
private: private:
RTUExchange(); RTUExchange();
......
...@@ -16,7 +16,7 @@ ...@@ -16,7 +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 info,warn,crit \
$* $*
#--mbtcp-log-add-levels level4,level3 \ #--mbtcp-log-add-levels level4,level3 \
......
...@@ -10,127 +10,127 @@ ...@@ -10,127 +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;
} }
void execute(); /*!< основной цикл работы */ void execute(); /*!< основной цикл работы */
void setLog( std::shared_ptr<DebugStream> dlog ); void setLog( std::shared_ptr<DebugStream> dlog );
inline bool isRunning() inline bool isRunning()
{ {
return ( sslot && sslot->isActive() ); return ( sslot && sslot->isActive() );
} }
inline void disableExchange( bool set = true ) inline void disableExchange( bool set = true )
{ {
disabled = set; disabled = set;
} }
inline bool getForceSingleCoilCmd() inline bool getForceSingleCoilCmd()
{ {
return forceSingleCoilCmd; return forceSingleCoilCmd;
} }
inline int16_t getLastWriteOutputSingleRegister() inline int16_t getLastWriteOutputSingleRegister()
{ {
return lastWriteOutputSingleRegister; return lastWriteOutputSingleRegister;
} }
inline uniset::ModbusRTU::ForceCoilsMessage getLastForceCoilsQ() inline uniset::ModbusRTU::ForceCoilsMessage getLastForceCoilsQ()
{ {
return lastForceCoilsQ; return lastForceCoilsQ;
} }
inline uniset::ModbusRTU::WriteOutputMessage getLastWriteOutput() inline uniset::ModbusRTU::WriteOutputMessage getLastWriteOutput()
{ {
return lastWriteOutputQ; return lastWriteOutputQ;
} }
friend std::ostream& operator<<(std::ostream& os, const MBTCPTestServer* m ); friend std::ostream& operator<<(std::ostream& os, const MBTCPTestServer* m );
inline float getF2TestValue() inline float getF2TestValue()
{ {
return f2_test_value; return f2_test_value;
} }
protected: protected:
// действия при завершении работы // действия при завершении работы
void sigterm( int signo ); void sigterm( int signo );
/*! обработка 0x01 */ /*! обработка 0x01 */
uniset::ModbusRTU::mbErrCode readCoilStatus( uniset::ModbusRTU::ReadCoilMessage& query, uniset::ModbusRTU::mbErrCode readCoilStatus( uniset::ModbusRTU::ReadCoilMessage& query,
uniset::ModbusRTU::ReadCoilRetMessage& reply ); uniset::ModbusRTU::ReadCoilRetMessage& reply );
/*! обработка 0x02 */ /*! обработка 0x02 */
uniset::ModbusRTU::mbErrCode readInputStatus( uniset::ModbusRTU::ReadInputStatusMessage& query, uniset::ModbusRTU::mbErrCode readInputStatus( uniset::ModbusRTU::ReadInputStatusMessage& query,
uniset::ModbusRTU::ReadInputStatusRetMessage& reply ); uniset::ModbusRTU::ReadInputStatusRetMessage& reply );
/*! обработка 0x03 */ /*! обработка 0x03 */
uniset::ModbusRTU::mbErrCode readOutputRegisters( uniset::ModbusRTU::ReadOutputMessage& query, uniset::ModbusRTU::mbErrCode readOutputRegisters( uniset::ModbusRTU::ReadOutputMessage& query,
uniset::ModbusRTU::ReadOutputRetMessage& reply ); uniset::ModbusRTU::ReadOutputRetMessage& reply );
/*! обработка 0x04 */ /*! обработка 0x04 */
uniset::ModbusRTU::mbErrCode readInputRegisters( uniset::ModbusRTU::ReadInputMessage& query, uniset::ModbusRTU::mbErrCode readInputRegisters( uniset::ModbusRTU::ReadInputMessage& query,
uniset::ModbusRTU::ReadInputRetMessage& reply ); uniset::ModbusRTU::ReadInputRetMessage& reply );
/*! обработка 0x05 */ /*! обработка 0x05 */
uniset::ModbusRTU::mbErrCode forceSingleCoil( uniset::ModbusRTU::ForceSingleCoilMessage& query, uniset::ModbusRTU::mbErrCode forceSingleCoil( uniset::ModbusRTU::ForceSingleCoilMessage& query,
uniset::ModbusRTU::ForceSingleCoilRetMessage& reply ); uniset::ModbusRTU::ForceSingleCoilRetMessage& reply );
/*! обработка 0x0F */ /*! обработка 0x0F */
uniset::ModbusRTU::mbErrCode forceMultipleCoils( uniset::ModbusRTU::ForceCoilsMessage& query, uniset::ModbusRTU::mbErrCode forceMultipleCoils( uniset::ModbusRTU::ForceCoilsMessage& query,
uniset::ModbusRTU::ForceCoilsRetMessage& reply ); uniset::ModbusRTU::ForceCoilsRetMessage& reply );
/*! обработка 0x10 */ /*! обработка 0x10 */
uniset::ModbusRTU::mbErrCode writeOutputRegisters( uniset::ModbusRTU::WriteOutputMessage& query, uniset::ModbusRTU::mbErrCode writeOutputRegisters( uniset::ModbusRTU::WriteOutputMessage& query,
uniset::ModbusRTU::WriteOutputRetMessage& reply ); uniset::ModbusRTU::WriteOutputRetMessage& reply );
/*! обработка 0x06 */ /*! обработка 0x06 */
uniset::ModbusRTU::mbErrCode writeOutputSingleRegister( uniset::ModbusRTU::WriteSingleOutputMessage& query, uniset::ModbusRTU::mbErrCode writeOutputSingleRegister( uniset::ModbusRTU::WriteSingleOutputMessage& query,
uniset::ModbusRTU::WriteSingleOutputRetMessage& reply ); uniset::ModbusRTU::WriteSingleOutputRetMessage& reply );
uniset::ModbusRTU::mbErrCode diagnostics( uniset::ModbusRTU::DiagnosticMessage& query, uniset::ModbusRTU::mbErrCode diagnostics( uniset::ModbusRTU::DiagnosticMessage& query,
uniset::ModbusRTU::DiagnosticRetMessage& reply ); uniset::ModbusRTU::DiagnosticRetMessage& reply );
uniset::ModbusRTU::mbErrCode read4314( uniset::ModbusRTU::MEIMessageRDI& query, uniset::ModbusRTU::mbErrCode read4314( uniset::ModbusRTU::MEIMessageRDI& query,
uniset::ModbusRTU::MEIMessageRetRDI& reply ); uniset::ModbusRTU::MEIMessageRetRDI& reply );
/*! обработка запросов на чтение ошибок */ /*! обработка запросов на чтение ошибок */
uniset::ModbusRTU::mbErrCode journalCommand( uniset::ModbusRTU::JournalCommandMessage& query, uniset::ModbusRTU::mbErrCode journalCommand( uniset::ModbusRTU::JournalCommandMessage& query,
uniset::ModbusRTU::JournalCommandRetMessage& reply ); uniset::ModbusRTU::JournalCommandRetMessage& reply );
/*! обработка запроса на установку времени */ /*! обработка запроса на установку времени */
uniset::ModbusRTU::mbErrCode setDateTime( uniset::ModbusRTU::SetDateTimeMessage& query, uniset::ModbusRTU::mbErrCode setDateTime( uniset::ModbusRTU::SetDateTimeMessage& query,
uniset::ModbusRTU::SetDateTimeRetMessage& reply ); uniset::ModbusRTU::SetDateTimeRetMessage& reply );
/*! обработка запроса удалённого сервиса */ /*! обработка запроса удалённого сервиса */
uniset::ModbusRTU::mbErrCode remoteService( uniset::ModbusRTU::RemoteServiceMessage& query, uniset::ModbusRTU::mbErrCode remoteService( uniset::ModbusRTU::RemoteServiceMessage& query,
uniset::ModbusRTU::RemoteServiceRetMessage& reply ); uniset::ModbusRTU::RemoteServiceRetMessage& reply );
uniset::ModbusRTU::mbErrCode fileTransfer( uniset::ModbusRTU::FileTransferMessage& query, uniset::ModbusRTU::mbErrCode fileTransfer( uniset::ModbusRTU::FileTransferMessage& query,
uniset::ModbusRTU::FileTransferRetMessage& reply ); uniset::ModbusRTU::FileTransferRetMessage& reply );
/*! интерфейс ModbusSlave для обмена по RS */ /*! интерфейс ModbusSlave для обмена по RS */
uniset::ModbusTCPServerSlot* sslot; uniset::ModbusTCPServerSlot* sslot;
std::unordered_set<uniset::ModbusRTU::ModbusAddr> vaddr; /*!< адреса данного узла */ std::unordered_set<uniset::ModbusRTU::ModbusAddr> vaddr; /*!< адреса данного узла */
bool verbose; bool verbose;
uint32_t replyVal; uint32_t replyVal;
bool forceSingleCoilCmd; bool forceSingleCoilCmd;
int16_t lastWriteOutputSingleRegister; int16_t lastWriteOutputSingleRegister;
uniset::ModbusRTU::ForceCoilsMessage lastForceCoilsQ; uniset::ModbusRTU::ForceCoilsMessage lastForceCoilsQ;
uniset::ModbusRTU::WriteOutputMessage lastWriteOutputQ; uniset::ModbusRTU::WriteOutputMessage lastWriteOutputQ;
float f2_test_value = {0.0}; 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;
......
...@@ -47,14 +47,14 @@ int main( int argc, const char* argv[] ) ...@@ -47,14 +47,14 @@ int main( int argc, const char* argv[] )
if( !shm ) if( !shm )
return 1; return 1;
mbm = MBTCPMaster::init_mbmaster(argc, argv, shm->getId(), (apart ? nullptr : shm )); mbm = MBTCPMaster::init_mbmaster(argc, argv, shm->getId(), (apart ? nullptr : shm ));
if( !mbm ) 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(mbm);
SystemMessage sm(SystemMessage::StartUp); SystemMessage sm(SystemMessage::StartUp);
act->broadcast( sm.transport_msg() ); act->broadcast( sm.transport_msg() );
......
...@@ -32,61 +32,61 @@ extern std::shared_ptr<MBTCPMaster> mbm; ...@@ -32,61 +32,61 @@ 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 ); REQUIRE( mbm != nullptr );
} }
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
TEST_CASE("MBTCPMaster: reconnect", "[modbus][mbmaster][mbtcpmaster]") TEST_CASE("MBTCPMaster: reconnect", "[modbus][mbmaster][mbtcpmaster]")
...@@ -492,139 +492,139 @@ TEST_CASE("MBTCPMaster: 0x10 (write register outputs or memories)", "[modbus][0x ...@@ -492,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, MBConfig::emWriteOnly ); ui->setValue(exchangeMode, MBConfig::emWriteOnly );
REQUIRE( ui->getValue(exchangeMode) == MBConfig::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, MBConfig::emReadOnly ); ui->setValue(exchangeMode, MBConfig::emReadOnly );
REQUIRE( ui->getValue(exchangeMode) == MBConfig::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, MBConfig::emSkipSaveToSM ); ui->setValue(exchangeMode, MBConfig::emSkipSaveToSM );
REQUIRE( ui->getValue(exchangeMode) == MBConfig::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, MBConfig::emSkipExchange ); ui->setValue(exchangeMode, MBConfig::emSkipExchange );
REQUIRE( ui->getValue(exchangeMode) == MBConfig::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, MBConfig::emNone ); ui->setValue(exchangeMode, MBConfig::emNone );
REQUIRE( ui->getValue(exchangeMode) == MBConfig::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]")
...@@ -774,18 +774,60 @@ TEST_CASE("MBTCPMaster: udefined value", "[modbus][undefined][mbmaster][mbtcpmas ...@@ -774,18 +774,60 @@ TEST_CASE("MBTCPMaster: udefined value", "[modbus][undefined][mbmaster][mbtcpmas
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
TEST_CASE("MBTCPMaster: reload config", "[modbus][reload][mbmaster][mbtcpmaster]") TEST_CASE("MBTCPMaster: reload config", "[modbus][reload][mbmaster][mbtcpmaster]")
{ {
InitTest(); 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 );
}
// -----------------------------------------------------------------------------
TEST_CASE("MBTCPMaster: reload config (HTTP API)", "[modbus][reload-api][mbmaster][mbtcpmaster]")
{
InitTest();
// default reconfigure
std::string request = "/api/v01/reconfigure";
uniset::SimpleInfo_var ret = mbm->apiRequest(request.c_str());
ostringstream sinfo;
sinfo << ret->info;
std::string info = sinfo.str();
REQUIRE( ret->id == mbm->getId() );
REQUIRE_FALSE( info.empty() );
REQUIRE( info.find("OK") != std::string::npos );
// reconfigure from other file
request = "/api/v01/reconfigure?confile=" + confile2;
ret = mbm->apiRequest(request.c_str());
sinfo.str("");
sinfo << ret->info;
info = sinfo.str();
mbs->setReply(160); REQUIRE( ret->id == mbm->getId() );
msleep(polltime + 200); REQUIRE_FALSE( info.empty() );
REQUIRE( ui->getValue(1070) == 160 ); REQUIRE( info.find("OK") != std::string::npos );
REQUIRE( ui->getValue(1080) == 1080 ); REQUIRE( info.find(confile2) != std::string::npos );
// add new mbreg // reconfigure FAIL
REQUIRE( mbm->reconfigure(confile2) ); request = "/api/v01/reconfigure?confile=BADFILE";
ret = mbm->apiRequest(request.c_str());
sinfo.str("");
sinfo << ret->info;
info = sinfo.str();
msleep(polltime + 600); REQUIRE( ret->id == mbm->getId() );
REQUIRE( ui->getValue(1080) == 160 ); REQUIRE_FALSE( info.empty() );
REQUIRE( info.find("OK") == std::string::npos );
} }
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
......
...@@ -706,43 +706,49 @@ Poco::JSON::Object::Ptr UObject_SK::request_conf_set( const std::string& req, co ...@@ -706,43 +706,49 @@ Poco::JSON::Object::Ptr UObject_SK::request_conf_set( const std::string& req, co
{ {
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"); 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" ) if( p.first == "sleep_msec" )
{ {
int val = uni_atoi(p.second); int val = uni_atoi(p.second);
if( val > 0 ) if( val > 0 )
{ {
sleep_msec = uni_atoi(p.second); sleep_msec = uni_atoi(p.second);
jupdated->add(p.first); jupdated->add(p.first);
} }
continue; continue;
} }
if( p.first == "resetMsgTime" ) if( p.first == "resetMsgTime" )
{ {
int val = uni_atoi(p.second); int val = uni_atoi(p.second);
if( val > 0 ) if( val > 0 )
{ {
resetMsgTime = uni_atoi(p.second); resetMsgTime = uni_atoi(p.second);
jupdated->add(p.first); jupdated->add(p.first);
} }
continue; continue;
} }
if( p.first == "forceOut" ) if( p.first == "forceOut" )
{ {
int val = uni_atoi(p.second); int val = uni_atoi(p.second);
if( val > 0 ) if( val > 0 )
{ {
forceOut = uni_atoi(p.second); forceOut = uni_atoi(p.second);
jupdated->add(p.first); jupdated->add(p.first);
} }
continue; continue;
} }
} }
jret->set("Result", (jupdated->size() > 0 ? "OK" : "FAIL") ); jret->set("Result", (jupdated->size() > 0 ? "OK" : "FAIL") );
......
...@@ -204,12 +204,12 @@ namespace uniset ...@@ -204,12 +204,12 @@ namespace uniset
void setActive( bool set ); void setActive( bool set );
#ifndef DISABLE_REST_API #ifndef DISABLE_REST_API
// вспомогательные функции // вспомогательные функции
virtual Poco::JSON::Object::Ptr httpGetMyInfo( Poco::JSON::Object::Ptr root ); virtual Poco::JSON::Object::Ptr httpGetMyInfo( Poco::JSON::Object::Ptr root );
Poco::JSON::Object::Ptr request_conf( const std::string& req, const Poco::URI::QueryParameters& p ); Poco::JSON::Object::Ptr request_conf( const std::string& req, const Poco::URI::QueryParameters& p );
virtual Poco::JSON::Object::Ptr request_conf_get( const std::string& req, const Poco::URI::QueryParameters& p ); virtual Poco::JSON::Object::Ptr request_conf_get( 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 ); virtual Poco::JSON::Object::Ptr request_conf_set( const std::string& req, const Poco::URI::QueryParameters& p );
Poco::JSON::Object::Ptr request_conf_name( const std::string& name, const std::string& props ); Poco::JSON::Object::Ptr request_conf_name( const std::string& name, const std::string& props );
#endif #endif
private: private:
......
...@@ -60,12 +60,12 @@ namespace uniset ...@@ -60,12 +60,12 @@ namespace uniset
/*! текущее количество подключений */ /*! текущее количество подключений */
size_t getCountSessions() const noexcept; size_t getCountSessions() const noexcept;
// Сбор статистики по соединениям... // Сбор статистики по соединениям...
struct SessionInfo struct SessionInfo
{ {
SessionInfo( const std::string& a, size_t ask ): iaddr(a), askCount(ask) {} SessionInfo( const std::string& a, size_t ask ): iaddr(a), askCount(ask) {}
std::string iaddr; std::string iaddr;
size_t askCount; size_t askCount;
}; };
......
...@@ -988,6 +988,7 @@ namespace uniset ...@@ -988,6 +988,7 @@ namespace uniset
return getInfo(request); return getInfo(request);
#else #else
SimpleInfo* ret = new SimpleInfo(); SimpleInfo* ret = new SimpleInfo();
ret->id = getId();
ostringstream err; ostringstream err;
try try
......
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