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