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

[modbusmaster][reload config]: first prototype

parent b16c96bf
/*
* Copyright (c) 2020 Pavel Vainerman.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation, version 2.1.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Lesser Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
// -------------------------------------------------------------------------
#include <cmath>
#include <limits>
#include <sstream>
#include <Exceptions.h>
#include <UniSetTypes.h>
#include <extensions/Extensions.h>
#include <ORepHelpers.h>
#include "MBConfig.h"
#include "modbus/MBLogSugar.h"
// -------------------------------------------------------------------------
namespace uniset
{
// -----------------------------------------------------------------------------
using namespace std;
using namespace uniset::extensions;
// -----------------------------------------------------------------------------
MBConfig::MBConfig(const std::shared_ptr<uniset::Configuration>& _conf
, xmlNode* _cnode
, std::shared_ptr<SMInterface> _shm ):
cnode(_cnode),
conf(_conf),
shm(_shm)
{
}
// -----------------------------------------------------------------------------
MBConfig::~MBConfig()
{
}
// -----------------------------------------------------------------------------
void MBConfig::cloneParams( const std::shared_ptr<MBConfig>& mbconf )
{
s_field = mbconf->s_field;
s_fvalue = mbconf->s_fvalue;
defaultMBtype = mbconf->defaultMBtype;
defaultMBaddr = mbconf->defaultMBaddr;
defaultMBinitOK = mbconf->defaultMBinitOK;
mblog = mbconf->mblog;
myname = mbconf->myname;
noQueryOptimization = mbconf->noQueryOptimization;
maxQueryCount = mbconf->maxQueryCount;
recv_timeout = mbconf->recv_timeout;
default_timeout = mbconf->default_timeout;
aftersend_pause = mbconf->aftersend_pause;
polltime = mbconf->polltime;
sleepPause_msec = mbconf->sleepPause_msec;
prefix = mbconf->prefix;
prop_prefix = mbconf->prop_prefix;
mbregFromID = mbconf->mbregFromID;
}
// -----------------------------------------------------------------------------
void MBConfig::loadConfig( const std::shared_ptr<uniset::UniXML>& xml, UniXML::iterator sensorsSection )
{
readConfiguration(xml, sensorsSection);
if( !noQueryOptimization )
rtuQueryOptimization(devices, maxQueryCount);
initDeviceList(xml);
}
// ------------------------------------------------------------------------------------------
void MBConfig::readConfiguration( const std::shared_ptr<uniset::UniXML>& xml, UniXML::iterator sensorsSection )
{
UniXML::iterator it = sensorsSection;
if( !it.getCurrent() )
{
it = xml->findNode(xml->getFirstNode(), "sensors");
if(!it)
{
ostringstream err;
err << myname << "(readConfiguration): не нашли корневого раздела <sensors>";
throw SystemError(err.str());
}
}
if( !it.goChildren() )
{
mbcrit << myname << "(readConfiguration): раздел <sensors> не содержит секций ?!!\n";
return;
}
for( ; it.getCurrent(); it.goNext() )
{
if( uniset::check_filter(it, s_field, s_fvalue) )
initItem(it);
}
// readconf_ok = true;
}
// ------------------------------------------------------------------------------------------
MBConfig::DeviceType MBConfig::getDeviceType( const std::string& dtype ) noexcept
{
if( dtype.empty() )
return dtUnknown;
if( dtype == "mtr" || dtype == "MTR" )
return dtMTR;
if( dtype == "rtu" || dtype == "RTU" )
return dtRTU;
if( dtype == "rtu188" || dtype == "RTU188" )
return dtRTU188;
return dtUnknown;
}
// -----------------------------------------------------------------------------
void MBConfig::printMap( MBConfig::RTUDeviceMap& m )
{
cout << "devices: " << endl;
for( auto it = m.begin(); it != m.end(); ++it )
cout << " " << *(it->second) << endl;
}
// -----------------------------------------------------------------------------
std::ostream& operator<<( std::ostream& os, MBConfig::RTUDeviceMap& m )
{
os << "devices: " << endl;
for( auto it = m.begin(); it != m.end(); ++it )
os << " " << *(it->second) << endl;
return os;
}
// -----------------------------------------------------------------------------
std::ostream& operator<<( std::ostream& os, MBConfig::RTUDevice& d )
{
os << "addr=" << ModbusRTU::addr2str(d.mbaddr)
<< " type=" << d.dtype
<< " respond_id=" << d.resp_id
<< " respond_timeout=" << d.resp_Delay.getOffDelay()
<< " respond_state=" << d.resp_state
<< " respond_invert=" << d.resp_invert
<< " safeMode=" << (MBConfig::SafeMode)d.safeMode
<< endl;
os << " regs: " << endl;
for( const auto& m : d.pollmap )
{
for( const auto& it : * (m.second) )
os << " " << it.second << endl;
}
return os;
}
// -----------------------------------------------------------------------------
std::ostream& operator<<( std::ostream& os, const MBConfig::RegInfo* r )
{
return os << (*r);
}
// -----------------------------------------------------------------------------
std::ostream& operator<<( std::ostream& os, const MBConfig::RegInfo& r )
{
os << " id=" << r.regID
<< " mbreg=" << ModbusRTU::dat2str(r.mbreg)
<< " mbfunc=" << r.mbfunc
<< " q_num=" << r.q_num
<< " q_count=" << r.q_count
<< " value=" << ModbusRTU::dat2str(r.mbval) << "(" << (int)r.mbval << ")"
<< " mtrType=" << MTR::type2str(r.mtrType)
<< endl;
for( const auto& s : r.slst )
os << " " << s << endl;
return os;
}
// -----------------------------------------------------------------------------
void MBConfig::rtuQueryOptimization( RTUDeviceMap& dm, size_t maxQueryCount )
{
// mbinfo << myname << "(rtuQueryOptimization): optimization..." << endl;
for( const auto& d : dm )
rtuQueryOptimizationForDevice(d.second, maxQueryCount);
}
// -----------------------------------------------------------------------------
void MBConfig::rtuQueryOptimizationForDevice( const std::shared_ptr<RTUDevice>& d, size_t maxQueryCount )
{
// mbinfo << myname << "(rtuQueryOptimizationForDevice): dev addr="
// << ModbusRTU::addr2str(d->mbaddr) << " optimization..." << endl;
for( const auto& m : d->pollmap )
rtuQueryOptimizationForRegMap(m.second, maxQueryCount);
}
// -----------------------------------------------------------------------------
void MBConfig::rtuQueryOptimizationForRegMap( const std::shared_ptr<RegMap>& regmap, size_t maxQueryCount )
{
if( regmap->size() <= 1 )
return;
// Вообще в map они уже лежат в нужном порядке, т.е. функция genRegID() гарантирует
// что регистры идущие подряд с одниковой функцией чтения/записи получат подряд идущие RegID.
// так что оптимтизация это просто нахождение мест где RegID идут не подряд...
for( auto it = regmap->begin(); it != regmap->end(); ++it )
{
auto& beg = it->second;
ModbusRTU::RegID regID = beg->regID;
beg->q_count = 1;
beg->q_num = 0;
++it;
// склеиваем регистры идущие подряд
for( ; it != regmap->end(); ++it )
{
if( (it->second->regID - regID) > 1 )
{
// этот регистр должен войти уже в следующий запрос,
// надо вернуть на шаг обратно..
--it;
break;
}
beg->q_count++;
regID = it->second->regID;
it->second->q_num = beg->q_count - 1;
it->second->q_count = 0;
if( beg->q_count >= maxQueryCount )
break;
}
// Корректировка типа функции, в случае необходимости...
if( beg->q_count > 1 && beg->mbfunc == ModbusRTU::fnWriteOutputSingleRegister )
{
// mbwarn << myname << "(rtuQueryOptimization): "
// << " optimization change func=" << ModbusRTU::fnWriteOutputSingleRegister
// << " <--> func=" << ModbusRTU::fnWriteOutputRegisters
// << " for mbaddr=" << ModbusRTU::addr2str(beg->dev->mbaddr)
// << " mbreg=" << ModbusRTU::dat2str(beg->mbreg);
beg->mbfunc = ModbusRTU::fnWriteOutputRegisters;
}
else if( beg->q_count > 1 && beg->mbfunc == ModbusRTU::fnForceSingleCoil )
{
// mbwarn << myname << "(rtuQueryOptimization): "
// << " optimization change func=" << ModbusRTU::fnForceSingleCoil
// << " <--> func=" << ModbusRTU::fnForceMultipleCoils
// << " for mbaddr=" << ModbusRTU::addr2str(beg->dev->mbaddr)
// << " mbreg=" << ModbusRTU::dat2str(beg->mbreg);
beg->mbfunc = ModbusRTU::fnForceMultipleCoils;
}
// надо до внешнего цикла, где будет ++it
// проверить условие.. (т.к. мы во внутреннем цикле итерировались
if( it == regmap->end() )
break;
}
}
// -----------------------------------------------------------------------------
//std::ostream& operator<<( std::ostream& os, MBConfig::PList& lst )
std::ostream& MBConfig::print_plist( std::ostream& os, const MBConfig::PList& lst )
{
os << "[ ";
for( const auto& p : lst )
os << "(" << p.si.id << ")" << conf->oind->getBaseName(conf->oind->getMapName(p.si.id)) << " ";
os << "]";
return os;
}
// -----------------------------------------------------------------------------
std::shared_ptr<MBConfig::RTUDevice> MBConfig::addDev( RTUDeviceMap& mp, ModbusRTU::ModbusAddr a, UniXML::iterator& xmlit )
{
auto it = mp.find(a);
if( it != mp.end() )
{
string s_dtype(xmlit.getProp(prop_prefix + "mbtype"));
if( s_dtype.empty() )
s_dtype = defaultMBtype;
DeviceType dtype = getDeviceType(s_dtype);
if( it->second->dtype != dtype )
{
mbcrit << myname << "(addDev): OTHER mbtype=" << dtype << " for " << xmlit.getProp("name")
<< " prop='" << prop_prefix + "mbtype" << "'"
<< ". Already used devtype=" << it->second->dtype
<< " for mbaddr=" << ModbusRTU::addr2str(it->second->mbaddr)
<< endl;
return 0;
}
mbinfo << myname << "(addDev): device for addr=" << ModbusRTU::addr2str(a)
<< " already added. Ignore device params for " << xmlit.getProp("name") << " ..." << endl;
return it->second;
}
auto d = make_shared<MBConfig::RTUDevice>();
d->mbaddr = a;
if( !initRTUDevice(d, xmlit) )
{
d.reset();
return 0;
}
mp.insert( std::make_pair(a, d) );
return d;
}
// ------------------------------------------------------------------------------------------
std::shared_ptr<MBConfig::RegInfo> MBConfig::addReg( std::shared_ptr<RegMap>& mp, ModbusRTU::RegID regID, ModbusRTU::ModbusData r,
UniXML::iterator& xmlit, std::shared_ptr<MBConfig::RTUDevice> dev )
{
auto it = mp->find(regID);
if( it != mp->end() )
{
if( !it->second->dev )
{
mbcrit << myname << "(addReg): for " << xmlit.getProp("name")
<< " dev=0!!!! " << endl;
return 0;
}
if( it->second->dev->dtype != dev->dtype )
{
mbcrit << myname << "(addReg): OTHER mbtype=" << dev->dtype << " for " << xmlit.getProp("name")
<< ". Already used devtype=" << it->second->dev->dtype << " for " << it->second->dev << endl;
return 0;
}
mbinfo << myname << "(addReg): reg=" << ModbusRTU::dat2str(r)
<< "(regID=" << regID << ")"
<< " already added for " << (*it->second)
<< " Ignore register params for " << xmlit.getProp("name") << " ..." << endl;
it->second->rit = it;
return it->second;
}
auto ri = make_shared<MBConfig::RegInfo>();
if( !initRegInfo(ri, xmlit, dev) )
return 0;
ri->mbreg = r;
ri->regID = regID;
mp->insert( std::make_pair(regID, ri) );
ri->rit = mp->find(regID);
mbinfo << myname << "(addReg): reg=" << ModbusRTU::dat2str(r) << "(regID=" << regID << ")" << endl;
return ri;
}
// ------------------------------------------------------------------------------------------
MBConfig::RSProperty* MBConfig::addProp( PList& plist, RSProperty&& p )
{
for( auto&& it : plist )
{
if( it.si.id == p.si.id && it.si.node == p.si.node )
return &it;
}
plist.emplace_back( std::move(p) );
auto it = plist.end();
--it;
return &(*it);
}
// ------------------------------------------------------------------------------------------
bool MBConfig::initRSProperty( RSProperty& p, UniXML::iterator& it )
{
if( !IOBase::initItem(&p, it, shm, prop_prefix, false, mblog, myname) )
return false;
// проверяем не пороговый ли это датчик (т.е. не связанный с обменом)
// тогда заносим его в отдельный список
if( p.t_ai != DefaultObjectId )
{
thrlist.emplace_back( std::move(p) );
return true;
}
const string sbit(IOBase::initProp(it, "nbit", prop_prefix, false));
if( !sbit.empty() )
{
p.nbit = uniset::uni_atoi(sbit.c_str());
if( p.nbit < 0 || p.nbit >= ModbusRTU::BitsPerData )
{
mbcrit << myname << "(initRSProperty): BAD nbit=" << (int)p.nbit
<< ". (0 >= nbit < " << ModbusRTU::BitsPerData << ")." << endl;
return false;
}
}
if( p.nbit > 0 &&
( p.stype == UniversalIO::AI ||
p.stype == UniversalIO::AO ) )
{
mbwarn << "(initRSProperty): (ignore) uncorrect param`s nbit!=0(" << p.nbit << ")"
<< " for iotype=" << p.stype << " for " << it.getProp("name") << endl;
}
const string sbyte(IOBase::initProp(it, "nbyte", prop_prefix, false) );
if( !sbyte.empty() )
{
p.nbyte = uniset::uni_atoi(sbyte.c_str());
if( p.nbyte > VTypes::Byte::bsize )
{
mbwarn << myname << "(initRSProperty): BAD nbyte=" << p.nbyte
<< ". (0 >= nbyte < " << VTypes::Byte::bsize << ")." << endl;
return false;
}
}
const string vt( IOBase::initProp(it, "vtype", prop_prefix, false) );
if( vt.empty() )
{
p.rnum = VTypes::wsize(VTypes::vtUnknown);
p.vType = VTypes::vtUnknown;
}
else
{
VTypes::VType v(VTypes::str2type(vt));
if( v == VTypes::vtUnknown )
{
mbcrit << myname << "(initRSProperty): Unknown tcp_vtype='" << vt << "' for "
<< it.getProp("name")
<< endl;
return false;
}
p.vType = v;
p.rnum = VTypes::wsize(v);
}
return true;
}
// ------------------------------------------------------------------------------------------
bool MBConfig::initRegInfo( std::shared_ptr<RegInfo>& r, UniXML::iterator& it, std::shared_ptr<MBConfig::RTUDevice>& dev )
{
r->dev = dev;
r->mbval = IOBase::initIntProp(it, "default", prop_prefix, false);
if( dev->dtype == MBConfig::dtRTU )
{
// mblog.info() << myname << "(initRegInfo): init RTU.."
}
else if( dev->dtype == MBConfig::dtMTR )
{
// only for MTR
if( !initMTRitem(it, r) )
return false;
}
else if( dev->dtype == MBConfig::dtRTU188 )
{
// only for RTU188
if( !initRTU188item(it, r) )
return false;
UniversalIO::IOType t = uniset::getIOType(IOBase::initProp(it, "iotype", prop_prefix, false));
r->mbreg = RTUStorage::getRegister(r->rtuJack, r->rtuChan, t);
r->mbfunc = RTUStorage::getFunction(r->rtuJack, r->rtuChan, t);
// т.к. с RTU188 свой обмен
// mbreg и mbfunc поля не используются
return true;
}
else
{
mbcrit << myname << "(initRegInfo): Unknown mbtype='" << dev->dtype
<< "' for " << it.getProp("name") << endl;
return false;
}
if( mbregFromID )
{
if( it.getProp("id").empty() )
r->mbreg = conf->getSensorID(it.getProp("name"));
else
r->mbreg = it.getIntProp("id");
}
else
{
const string sr( IOBase::initProp(it, "mbreg", prop_prefix, false) );
if( sr.empty() )
{
mbcrit << myname << "(initItem): Unknown 'mbreg' for " << it.getProp("name") << endl;
return false;
}
r->mbreg = ModbusRTU::str2mbData(sr);
}
r->mbfunc = ModbusRTU::fnUnknown;
const string f( IOBase::initProp(it, "mbfunc", prop_prefix, false) );
if( !f.empty() )
{
r->mbfunc = (ModbusRTU::SlaveFunctionCode)uniset::uni_atoi(f.c_str());
if( r->mbfunc == ModbusRTU::fnUnknown )
{
mbcrit << myname << "(initRegInfo): Unknown mbfunc ='" << f
<< "' for " << it.getProp("name") << endl;
return false;
}
}
return true;
}
// ------------------------------------------------------------------------------------------
bool MBConfig::initRTUDevice( std::shared_ptr<RTUDevice>& d, UniXML::iterator& it )
{
string mbtype(IOBase::initProp(it, "mbtype", prop_prefix, false));
if(mbtype.empty())
mbtype = defaultMBtype;
d->dtype = getDeviceType(mbtype);
if( d->dtype == dtUnknown )
{
mbcrit << myname << "(initRTUDevice): Unknown tcp_mbtype='" << mbtype << "'"
<< ". Use: rtu "
<< " for " << it.getProp("name") << endl;
return false;
}
string addr( IOBase::initProp(it, "mbaddr", prop_prefix, false) );
if( addr.empty() )
addr = defaultMBaddr;
if( addr.empty() )
{
mbcrit << myname << "(initRTUDevice): Unknown mbaddr for " << it.getProp("name") << endl;
return false;
}
d->mbaddr = ModbusRTU::str2mbAddr(addr);
if( d->dtype == MBConfig::dtRTU188 )
{
if( !d->rtu188 )
d->rtu188 = make_shared<RTUStorage>(d->mbaddr);
}
return true;
}
// ------------------------------------------------------------------------------------------
bool MBConfig::initItem( UniXML::iterator& it )
{
RSProperty p;
if( !initRSProperty(p, it) )
return false;
if( p.t_ai != DefaultObjectId ) // пороговые датчики в список обмена вносить не надо.
return true;
string addr( IOBase::initProp(it, "mbaddr", prop_prefix, false) );
if( addr.empty() )
addr = defaultMBaddr;
if( addr.empty() )
{
mbcrit << myname << "(initItem): Unknown mbaddr(" << IOBase::initProp(it, "mbaddr", prop_prefix, false) << ")='" << addr << "' for " << it.getProp("name") << endl;
return false;
}
ModbusRTU::ModbusAddr mbaddr = ModbusRTU::str2mbAddr(addr);
auto dev = addDev(devices, mbaddr, it);
if( !dev )
{
mbcrit << myname << "(initItem): " << it.getProp("name") << " CAN`T ADD for polling!" << endl;
return false;
}
ModbusRTU::ModbusData mbreg = 0;
int fn = IOBase::initIntProp(it, "mbfunc", prop_prefix, false);
if( dev->dtype == dtRTU188 )
{
auto r_tmp = make_shared<RegInfo>();
if( !initRTU188item(it, r_tmp) )
{
mbcrit << myname << "(initItem): init RTU188 failed for " << it.getProp("name") << endl;
r_tmp.reset();
return false;
}
mbreg = RTUStorage::getRegister(r_tmp->rtuJack, r_tmp->rtuChan, p.stype);
fn = RTUStorage::getFunction(r_tmp->rtuJack, r_tmp->rtuChan, p.stype);
}
else
{
if( mbregFromID )
mbreg = p.si.id; // conf->getSensorID(it.getProp("name"));
else
{
const string reg( IOBase::initProp(it, "mbreg", prop_prefix, false) );
if( reg.empty() )
{
mbcrit << myname << "(initItem): unknown mbreg(" << prop_prefix << ") for " << it.getProp("name") << endl;
return false;
}
mbreg = ModbusRTU::str2mbData(reg);
}
if( p.nbit != -1 )
{
if( fn == ModbusRTU::fnReadCoilStatus || fn == ModbusRTU::fnReadInputStatus )
{
mbcrit << myname << "(initItem): MISMATCHED CONFIGURATION! nbit=" << (int)p.nbit << " func=" << fn
<< " for " << it.getProp("name") << endl;
return false;
}
}
}
/*! приоритет опроса:
* 1...n - задаёт "частоту" опроса. Т.е. каждые 1...n циклов
*/
size_t pollfactor = IOBase::initIntProp(it, "pollfactor", prop_prefix, false, 0);
std::shared_ptr<RegMap> rmap;
auto rit = dev->pollmap.find(pollfactor);
if( rit == dev->pollmap.end() )
{
rmap = make_shared<RegMap>();
dev->pollmap.emplace(pollfactor, rmap);
}
else
rmap = rit->second;
// формула для вычисления ID
// требования:
// - ID > диапазона возможных регитров
// - разные функции должны давать разный ID
ModbusRTU::RegID rID = ModbusRTU::genRegID(mbreg, fn);
auto ri = addReg(rmap, rID, mbreg, it, dev);
if( dev->dtype == dtMTR )
{
p.rnum = MTR::wsize(ri->mtrType);
if( p.rnum <= 0 )
{
mbcrit << myname << "(initItem): unknown word size for " << it.getProp("name") << endl;
return false;
}
}
if( !ri )
return false;
ri->dev = dev;
// ПРОВЕРКА!
// если функция на запись, то надо проверить
// что один и тотже регистр не перезапишут несколько датчиков
// это возможно только, если они пишут биты!!
// ИТОГ:
// Если для функций записи список датчиков для регистра > 1
// значит в списке могут быть только битовые датчики
// и если идёт попытка внести в список не битовый датчик то ОШИБКА!
// И наоборот: если идёт попытка внести битовый датчик, а в списке
// уже сидит датчик занимающий целый регистр, то тоже ОШИБКА!
if( ModbusRTU::isWriteFunction(ri->mbfunc) )
{
if( p.nbit < 0 && ri->slst.size() > 1 )
{
ostringstream sl;
sl << "[ ";
for( const auto& i : ri->slst )
sl << ORepHelpers::getShortName(conf->oind->getMapName(i.si.id)) << ",";
sl << "]";
ostringstream err;
err << myname << "(initItem): FAILED! Sharing SAVE (not bit saving) to "
<< " tcp_mbreg=" << ModbusRTU::dat2str(ri->mbreg) << "(" << (int)ri->mbreg << ")"
<< " conflict with sensors " << sl.str();
mbcrit << err.str() << endl;
throw uniset::SystemError(err.str());
}
if( p.nbit >= 0 && ri->slst.size() == 1 )
{
auto it2 = ri->slst.begin();
if( it2->nbit < 0 )
{
ostringstream err;
err << myname << "(initItem): FAILED! Sharing SAVE (mbreg="
<< ModbusRTU::dat2str(ri->mbreg) << "(" << (int)ri->mbreg << ") already used)!"
<< " IGNORE --> " << it.getProp("name");
mbcrit << err.str() << endl;
throw uniset::SystemError(err.str());
}
}
// Раз это регистр для записи, то как минимум надо сперва
// инициализировать значением из SM
ri->sm_initOK = IOBase::initIntProp(it, "sm_initOK", prop_prefix, false);
ri->mb_initOK = true;
}
else
{
ri->mb_initOK = defaultMBinitOK;
ri->sm_initOK = false;
}
RSProperty* p1 = addProp(ri->slst, std::move(p) );
if( !p1 )
return false;
p1->reg = ri;
if( p1->rnum > 1 )
{
ri->q_count = p1->rnum;
ri->q_num = 1;
for( auto i = 1; i < p1->rnum; i++ )
{
ModbusRTU::RegID id1 = ModbusRTU::genRegID(mbreg + i, ri->mbfunc);
auto r = addReg(rmap, id1, mbreg + i, it, dev);
r->q_num = i + 1;
r->q_count = 1;
r->mbfunc = ri->mbfunc;
r->mb_initOK = defaultMBinitOK;
r->sm_initOK = false;
if( ModbusRTU::isWriteFunction(ri->mbfunc) )
{
// Если занимает несколько регистров, а указана функция записи "одного",
// то это ошибка..
if( ri->mbfunc != ModbusRTU::fnWriteOutputRegisters &&
ri->mbfunc != ModbusRTU::fnForceMultipleCoils )
{
ostringstream err;
err << myname << "(initItem): Bad write function ='" << ModbusRTU::fnWriteOutputSingleRegister
<< "' for vtype='" << p1->vType << "'"
<< " tcp_mbreg=" << ModbusRTU::dat2str(ri->mbreg)
<< " for " << it.getProp("name");
mbcrit << err.str() << endl;
throw uniset::SystemError(err.str());
}
}
}
}
// Фомируем список инициализации
bool need_init = IOBase::initIntProp(it, "preinit", prop_prefix, false);
if( need_init && ModbusRTU::isWriteFunction(ri->mbfunc) )
{
InitRegInfo ii;
ii.p = std::move(p);
ii.dev = dev;
ii.ri = ri;
const string s_reg(IOBase::initProp(it, "init_mbreg", prop_prefix, false));
if( !s_reg.empty() )
ii.mbreg = ModbusRTU::str2mbData(s_reg);
else
ii.mbreg = ri->mbreg;
string s_mbfunc(it.getProp(prop_prefix + "init_mbfunc"));
if( !s_mbfunc.empty() )
{
ii.mbfunc = (ModbusRTU::SlaveFunctionCode)uniset::uni_atoi(s_mbfunc);
if( ii.mbfunc == ModbusRTU::fnUnknown )
{
mbcrit << myname << "(initItem): Unknown tcp_init_mbfunc ='" << s_mbfunc
<< "' for " << it.getProp("name") << endl;
return false;
}
}
else
{
switch(ri->mbfunc)
{
case ModbusRTU::fnWriteOutputSingleRegister:
ii.mbfunc = ModbusRTU::fnReadOutputRegisters;
break;
case ModbusRTU::fnForceSingleCoil:
ii.mbfunc = ModbusRTU::fnReadCoilStatus;
break;
case ModbusRTU::fnWriteOutputRegisters:
ii.mbfunc = ModbusRTU::fnReadOutputRegisters;
break;
case ModbusRTU::fnForceMultipleCoils:
ii.mbfunc = ModbusRTU::fnReadCoilStatus;
break;
default:
ii.mbfunc = ModbusRTU::fnReadOutputRegisters;
break;
}
}
initRegList.emplace_back( std::move(ii) );
ri->mb_initOK = false;
ri->sm_initOK = false;
}
return true;
}
// ------------------------------------------------------------------------------------------
bool MBConfig::initMTRitem( UniXML::iterator& it, std::shared_ptr<RegInfo>& p )
{
p->mtrType = MTR::str2type(it.getProp(prop_prefix + "mtrtype"));
if( p->mtrType == MTR::mtUnknown )
{
mbcrit << myname << "(readMTRItem): Unknown mtrtype '"
<< it.getProp(prop_prefix + "mtrtype")
<< "' for " << it.getProp("name") << endl;
return false;
}
return true;
}
// ------------------------------------------------------------------------------------------
bool MBConfig::initRTU188item( UniXML::iterator& it, std::shared_ptr<RegInfo>& p )
{
const string jack(IOBase::initProp(it, "jack", prop_prefix, false));
const string chan(IOBase::initProp(it, "channel", prop_prefix, false));
if( jack.empty() )
{
mbcrit << myname << "(readRTU188Item): Unknown " << prop_prefix << "jack='' "
<< " for " << it.getProp("name") << endl;
return false;
}
p->rtuJack = RTUStorage::s2j(jack);
if( p->rtuJack == RTUStorage::nUnknown )
{
mbcrit << myname << "(readRTU188Item): Unknown " << prop_prefix << "jack=" << jack
<< " for " << it.getProp("name") << endl;
return false;
}
if( chan.empty() )
{
mbcrit << myname << "(readRTU188Item): Unknown channel='' "
<< " for " << it.getProp("name") << endl;
return false;
}
p->rtuChan = uniset::uni_atoi(chan);
mblog2 << myname << "(readRTU188Item): add jack='" << jack << "'"
<< " channel='" << p->rtuChan << "'" << endl;
return true;
}
// ------------------------------------------------------------------------------------------
std::ostream& operator<<( std::ostream& os, const MBConfig::DeviceType& dt )
{
switch(dt)
{
case MBConfig::dtRTU:
os << "RTU";
break;
case MBConfig::dtMTR:
os << "MTR";
break;
case MBConfig::dtRTU188:
os << "RTU188";
break;
default:
os << "Unknown device type (" << (int)dt << ")";
break;
}
return os;
}
// -----------------------------------------------------------------------------
std::ostream& operator<<( std::ostream& os, const MBConfig::RSProperty& p )
{
os << " (" << ModbusRTU::dat2str(p.reg->mbreg) << ")"
<< " sid=" << p.si.id
<< " stype=" << p.stype
<< " nbit=" << (int)p.nbit
<< " nbyte=" << p.nbyte
<< " rnum=" << p.rnum
<< " safeval=" << p.safeval
<< " invert=" << p.invert;
if( p.stype == UniversalIO::AI || p.stype == UniversalIO::AO )
{
os << p.cal
<< " cdiagram=" << ( p.cdiagram ? "yes" : "no" );
}
return os;
}
// -----------------------------------------------------------------------------
void MBConfig::initDeviceList(const std::shared_ptr<UniXML>& xml )
{
xmlNode* respNode = 0;
if( xml )
respNode = xml->extFindNode(cnode, 1, 1, "DeviceList");
if( respNode )
{
UniXML::iterator it1(respNode);
if( it1.goChildren() )
{
for(; it1.getCurrent(); it1.goNext() )
{
ModbusRTU::ModbusAddr a = ModbusRTU::str2mbAddr(it1.getProp("addr"));
initDeviceInfo(devices, a, it1);
}
}
else
mbwarn << myname << "(init): <DeviceList> empty section..." << endl;
}
else
mbwarn << myname << "(init): <DeviceList> not found..." << endl;
}
// -----------------------------------------------------------------------------
bool MBConfig::initDeviceInfo( RTUDeviceMap& m, ModbusRTU::ModbusAddr a, UniXML::iterator& it )
{
auto d = m.find(a);
if( d == m.end() )
{
mbwarn << myname << "(initDeviceInfo): not found device for addr=" << ModbusRTU::addr2str(a) << endl;
return false;
}
auto& dev = d->second;
dev->ask_every_reg = it.getIntProp("ask_every_reg");
mbinfo << myname << "(initDeviceInfo): add addr=" << ModbusRTU::addr2str(a)
<< " ask_every_reg=" << dev->ask_every_reg << endl;
string s(it.getProp("respondSensor"));
if( !s.empty() )
{
dev->resp_id = conf->getSensorID(s);
if( dev->resp_id == DefaultObjectId )
{
mbinfo << myname << "(initDeviceInfo): not found ID for respondSensor=" << s << endl;
return false;
}
}
const string mod(it.getProp("modeSensor"));
if( !mod.empty() )
{
dev->mode_id = conf->getSensorID(mod);
if( dev->mode_id == DefaultObjectId )
{
mbcrit << myname << "(initDeviceInfo): not found ID for modeSensor=" << mod << endl;
return false;
}
UniversalIO::IOType m_iotype = conf->getIOType(dev->mode_id);
if( m_iotype != UniversalIO::AI )
{
mbcrit << myname << "(initDeviceInfo): modeSensor='" << mod << "' must be 'AI'" << endl;
return false;
}
}
// сперва проверим не задан ли режим "safemodeResetIfNotRespond"
if( it.getIntProp("safemodeResetIfNotRespond") )
dev->safeMode = MBConfig::safeResetIfNotRespond;
// потом проверим датчик для "safeExternalControl"
const string safemode = it.getProp("safemodeSensor");
if( !safemode.empty() )
{
dev->safemode_id = conf->getSensorID(safemode);
if( dev->safemode_id == DefaultObjectId )
{
mbcrit << myname << "(initDeviceInfo): not found ID for safemodeSensor=" << safemode << endl;
return false;
}
const string safemodeValue(it.getProp("safemodeValue"));
if( !safemodeValue.empty() )
dev->safemode_value = uni_atoi(safemodeValue);
dev->safeMode = MBConfig::safeExternalControl;
}
mbinfo << myname << "(initDeviceInfo): add addr=" << ModbusRTU::addr2str(a) << endl;
int tout = it.getPIntProp("timeout", default_timeout );
dev->resp_Delay.set(0, tout);
dev->resp_invert = it.getIntProp("invert");
dev->resp_force = it.getIntProp("force");
int init_tout = it.getPIntProp("respondInitTimeout", tout);
dev->resp_ptInit.setTiming(init_tout);
s = it.getProp("speed");
if( !s.empty() )
{
d->second->speed = ComPort::getSpeed(s);
if( d->second->speed == ComPort::ComSpeed0 )
{
// d->second->speed = defSpeed;
mbcrit << myname << "(initDeviceInfo): Unknown speed=" << s <<
" for addr=" << ModbusRTU::addr2str(a) << endl;
return false;
}
}
return true;
}
// -----------------------------------------------------------------------------
std::ostream& operator<<( std::ostream& os, const MBConfig::ExchangeMode& em )
{
if( em == MBConfig::emNone )
return os << "emNone";
if( em == MBConfig::emWriteOnly )
return os << "emWriteOnly";
if( em == MBConfig::emReadOnly )
return os << "emReadOnly";
if( em == MBConfig::emSkipSaveToSM )
return os << "emSkipSaveToSM";
if( em == MBConfig::emSkipExchange )
return os << "emSkipExchange";
return os;
}
// -----------------------------------------------------------------------------
std::string to_string( const MBConfig::SafeMode& m )
{
if( m == MBConfig::safeNone )
return "safeNone";
if( m == MBConfig::safeResetIfNotRespond )
return "safeResetIfNotRespond";
if( m == MBConfig::safeExternalControl )
return "safeExternalControl";
return "";
}
ostream& operator<<( ostream& os, const MBConfig::SafeMode& m )
{
return os << to_string(m);
}
// -----------------------------------------------------------------------------
bool MBConfig::RTUDevice::checkRespond( std::shared_ptr<DebugStream>& mblog )
{
bool prev = resp_state;
resp_state = resp_Delay.check( prev_numreply != numreply ) && numreply != 0;
mblog4 << "(checkRespond): addr=" << ModbusRTU::addr2str(mbaddr)
<< " respond_id=" << resp_id
<< " state=" << resp_state
<< " check=" << (prev_numreply != numreply)
<< " delay_check=" << resp_Delay.get()
<< " [ timeout=" << resp_Delay.getOffDelay()
<< " numreply=" << numreply
<< " prev_numreply=" << prev_numreply
<< " resp_ptInit=" << resp_ptInit.checkTime()
<< " ]"
<< endl;
// если только что прошла "инициализация" возвращаем true
// чтобы датчик в SM обновился..
if( trInitOK.hi(resp_ptInit.checkTime()) )
return true;
return ((prev != resp_state || resp_force ) && trInitOK.get() );
}
// -----------------------------------------------------------------------------
std::string MBConfig::RTUDevice::getShortInfo() const
{
ostringstream s;
s << "mbaddr=" << ModbusRTU::addr2str(mbaddr) << ":"
<< " resp_state=" << resp_state
<< " (resp_id=" << resp_id << " resp_force=" << resp_force
<< " resp_invert=" << resp_invert
<< " numreply=" << numreply
<< " timeout=" << resp_Delay.getOffDelay()
<< " type=" << dtype
<< " ask_every_reg=" << ask_every_reg
<< ")" << endl;
return s.str();
}
// -----------------------------------------------------------------------------
} // end of namespace uniset
/*
* Copyright (c) 2020 Pavel Vainerman.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation, version 2.1.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Lesser Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
// -----------------------------------------------------------------------------
#ifndef _MBConfig_H_
#define _MBConfig_H_
// -----------------------------------------------------------------------------
#include <ostream>
#include <string>
#include <map>
#include <unordered_map>
#include <memory>
#include "IONotifyController.h"
#include "Calibration.h"
#include "DelayTimer.h"
#include "SMInterface.h"
#include "SharedMemory.h"
#include "IOBase.h"
#include "VTypes.h"
#include "MTR.h"
#include "RTUStorage.h"
#include "modbus/ModbusClient.h"
#include "Configuration.h"
// -------------------------------------------------------------------------
namespace uniset
{
// -----------------------------------------------------------------------------
/*! Конфигурация для ModbusMaster */
class MBConfig
{
public:
MBConfig(const std::shared_ptr<uniset::Configuration>& conf
, xmlNode* cnode
, std::shared_ptr<SMInterface> _shm );
~MBConfig();
/*! Режимы работы процесса обмена */
enum ExchangeMode
{
emNone = 0, /*!< нормальная работа (по умолчанию) */
emWriteOnly = 1, /*!< "только посылка данных" (работают только write-функции) */
emReadOnly = 2, /*!< "только чтение" (работают только read-функции) */
emSkipSaveToSM = 3, /*!< не писать данные в SM (при этом работают и read и write функции) */
emSkipExchange = 4 /*!< отключить обмен */
};
friend std::ostream& operator<<( std::ostream& os, const ExchangeMode& em );
/*! Режимы работы процесса обмена */
enum SafeMode
{
safeNone = 0, /*!< не использовать безопасный режим (по умолчанию) */
safeResetIfNotRespond = 1, /*!< выставлять безопасное значение, если пропала связь с устройством */
safeExternalControl = 2 /*!< управление сбросом по внешнему датчику */
};
friend std::string to_string( const SafeMode& m );
friend std::ostream& operator<<( std::ostream& os, const SafeMode& m );
enum DeviceType
{
dtUnknown, /*!< неизвестный */
dtRTU, /*!< RTU (default) */
dtMTR, /*!< MTR (DEIF) */
dtRTU188 /*!< RTU188 (Fastwell) */
};
static DeviceType getDeviceType( const std::string& dtype ) noexcept;
friend std::ostream& operator<<( std::ostream& os, const DeviceType& dt );
struct RTUDevice;
struct RegInfo;
struct RSProperty:
public IOBase
{
// only for RTU
int8_t nbit; /*!< bit number (-1 - not used) */
VTypes::VType vType; /*!< type of value */
uint16_t rnum; /*!< count of registers */
uint8_t nbyte; /*!< byte number (1-2) */
RSProperty():
nbit(-1), vType(VTypes::vtUnknown),
rnum(VTypes::wsize(VTypes::vtUnknown)),
nbyte(0)
{}
// т.к. IOBase содержит rwmutex с запрещённым конструктором копирования
// приходится здесь тоже объявлять разрешенными только операции "перемещения"
RSProperty( const RSProperty& r ) = delete;
RSProperty& operator=(const RSProperty& r) = delete;
RSProperty( RSProperty&& r ) = default;
RSProperty& operator=(RSProperty&& r) = default;
std::shared_ptr<RegInfo> reg;
};
friend std::ostream& operator<<( std::ostream& os, const RSProperty& p );
typedef std::list<RSProperty> PList;
std::ostream& print_plist( std::ostream& os, const PList& p );
typedef std::map<ModbusRTU::RegID, std::shared_ptr<RegInfo>> RegMap;
struct RegInfo
{
// т.к. RSProperty содержит rwmutex с запрещённым конструктором копирования
// приходится здесь тоже объявлять разрешенными только операции "перемещения"
RegInfo( const RegInfo& r ) = default;
RegInfo& operator=(const RegInfo& r) = delete;
RegInfo( RegInfo&& r ) = delete;
RegInfo& operator=(RegInfo&& r) = default;
RegInfo() = default;
ModbusRTU::ModbusData mbval = { 0 };
ModbusRTU::ModbusData mbreg = { 0 }; /*!< регистр */
ModbusRTU::SlaveFunctionCode mbfunc = { ModbusRTU::fnUnknown }; /*!< функция для чтения/записи */
PList slst;
ModbusRTU::RegID regID = { 0 };
std::shared_ptr<RTUDevice> dev;
// only for RTU188
RTUStorage::RTUJack rtuJack = { RTUStorage::nUnknown };
int rtuChan = { 0 };
// only for MTR
MTR::MTRType mtrType = { MTR::mtUnknown }; /*!< тип регистра (согласно спецификации на MTR) */
// optimization
size_t q_num = { 0 }; /*!< number in query */
size_t q_count = { 1 }; /*!< count registers for query */
RegMap::iterator rit;
// начальная инициализация для "записываемых" регистров
// Механизм:
// Если tcp_preinit="1", то сперва будет сделано чтение значения из устройства.
// при этом флаг mb_init=false пока не пройдёт успешной инициализации
// Если tcp_preinit="0", то флаг mb_init сразу выставляется в true.
bool mb_initOK = { false }; /*!< инициализировалось ли значение из устройства */
// Флаг sm_init означает, что писать в устройство нельзя, т.к. значение в "карте регистров"
// ещё не инициализировано из SM
bool sm_initOK = { false }; /*!< инициализировалось ли значение из SM */
};
friend std::ostream& operator<<( std::ostream& os, const RegInfo& r );
friend std::ostream& operator<<( std::ostream& os, const RegInfo* r );
struct RTUDevice
{
ModbusRTU::ModbusAddr mbaddr = { 0 }; /*!< адрес устройства */
std::unordered_map<size_t, std::shared_ptr<RegMap>> pollmap;
DeviceType dtype = { dtUnknown }; /*!< тип устройства */
// resp - respond..(контроль наличия связи)
uniset::ObjectId resp_id = { uniset::DefaultObjectId };
IOController::IOStateList::iterator resp_it;
DelayTimer resp_Delay; // таймер для формирования задержки на отпускание (пропадание связи)
PassiveTimer resp_ptInit; // таймер для формирования задержки на инициализацию связи (задержка на выставление датчика связи после запуска)
bool resp_state = { false };
bool resp_invert = { false };
bool resp_force = { false };
Trigger trInitOK; // триггер для "инициализации"
std::atomic<size_t> numreply = { 0 }; // количество успешных запросов..
std::atomic<size_t> prev_numreply = { 0 };
//
bool ask_every_reg = { false }; /*!< опрашивать ли каждый регистр, независимо от результата опроса предыдущего. По умолчанию false - прервать опрос при первом же timeout */
// режим работы
uniset::ObjectId mode_id = { uniset::DefaultObjectId };
IOController::IOStateList::iterator mode_it;
long mode = { emNone }; // режим работы с устройством (см. ExchangeMode)
// safe mode
long safeMode = { safeNone }; /*!< режим безопасного состояния см. SafeMode */
uniset::ObjectId safemode_id = { uniset::DefaultObjectId }; /*!< идентификатор для датчика безопасного режима */
IOController::IOStateList::iterator safemode_it;
long safemode_value = { 1 };
// return TRUE if state changed
bool checkRespond( std::shared_ptr<DebugStream>& log );
// специфические поля для RS
ComPort::Speed speed = { ComPort::ComSpeed38400 };
std::shared_ptr<RTUStorage> rtu188;
std::string getShortInfo() const;
};
friend std::ostream& operator<<( std::ostream& os, RTUDevice& d );
typedef std::unordered_map<ModbusRTU::ModbusAddr, std::shared_ptr<RTUDevice>> RTUDeviceMap;
friend std::ostream& operator<<( std::ostream& os, RTUDeviceMap& d );
static void printMap(RTUDeviceMap& d);
typedef std::list<IOBase> ThresholdList;
struct InitRegInfo
{
InitRegInfo():
dev(0), mbreg(0),
mbfunc(ModbusRTU::fnUnknown),
initOK(false)
{}
RSProperty p;
std::shared_ptr<RTUDevice> dev;
ModbusRTU::ModbusData mbreg;
ModbusRTU::SlaveFunctionCode mbfunc;
bool initOK;
std::shared_ptr<RegInfo> ri;
};
typedef std::list<InitRegInfo> InitList;
static void rtuQueryOptimization( RTUDeviceMap& m, size_t maxQueryCount );
static void rtuQueryOptimizationForDevice( const std::shared_ptr<RTUDevice>& d, size_t maxQueryCount );
static void rtuQueryOptimizationForRegMap( const std::shared_ptr<RegMap>& regmap, size_t maxQueryCount );
// т.к. пороговые датчики не связаны напрямую с обменом, создаём для них отдельный список
// и отдельно его проверяем потом
ThresholdList thrlist;
RTUDeviceMap devices;
InitList initRegList; /*!< список регистров для инициализации */
void loadConfig( const std::shared_ptr<uniset::UniXML>& xml, UniXML::iterator sensorsSection );
void initDeviceList( const std::shared_ptr<UniXML>& xml );
bool initItem( UniXML::iterator& it );
std::string s_field;
std::string s_fvalue;
// определение timeout для соединения
timeout_t recv_timeout = { 500 }; // msec
timeout_t default_timeout = { 5000 }; // msec
timeout_t aftersend_pause = { 0 };
timeout_t polltime = { 100 }; /*!< периодичность обновления данных, [мсек] */
timeout_t sleepPause_msec = { 10 };
size_t maxQueryCount = { ModbusRTU::MAXDATALEN }; /*!< максимальное количество регистров для одного запроса */
xmlNode* cnode = { 0 };
std::shared_ptr<DebugStream> mblog;
std::string myname;
std::string prefix;
std::string prop_prefix; /*!< префикс для считывания параметров обмена */
std::string defaultMBtype;
std::string defaultMBaddr;
bool mbregFromID = { false };
bool defaultMBinitOK = { false }; // флаг определяющий нужно ли ждать "первого обмена" или при запуске сохранять в SM значение default.
bool noQueryOptimization = { false };
std::shared_ptr<uniset::Configuration> conf;
std::shared_ptr<SMInterface> shm;
void cloneParams( const std::shared_ptr<MBConfig>& conf );
protected:
bool initSMValue( ModbusRTU::ModbusData* data, int count, RSProperty* p );
void readConfiguration(const std::shared_ptr<uniset::UniXML>& xml, UniXML::iterator sensorsSection );
void initOffsetList();
std::shared_ptr<RTUDevice> addDev( RTUDeviceMap& dmap, ModbusRTU::ModbusAddr a, UniXML::iterator& it );
std::shared_ptr<RegInfo> addReg(std::shared_ptr<RegMap>& devices, ModbusRTU::RegID id, ModbusRTU::ModbusData r, UniXML::iterator& it, std::shared_ptr<RTUDevice> dev );
RSProperty* addProp( PList& plist, RSProperty&& p );
bool initMTRitem(UniXML::iterator& it, std::shared_ptr<RegInfo>& p );
bool initRTU188item(UniXML::iterator& it, std::shared_ptr<RegInfo>& p );
bool initRSProperty( RSProperty& p, UniXML::iterator& it );
bool initRegInfo(std::shared_ptr<RegInfo>& r, UniXML::iterator& it, std::shared_ptr<RTUDevice>& dev );
bool initRTUDevice( std::shared_ptr<RTUDevice>& d, UniXML::iterator& it );
bool initDeviceInfo( RTUDeviceMap& m, ModbusRTU::ModbusAddr a, UniXML::iterator& it );
};
// --------------------------------------------------------------------------
} // end of namespace uniset
// -----------------------------------------------------------------------------
#endif // _MBConfig_H_
// -----------------------------------------------------------------------------
......@@ -34,18 +34,13 @@ namespace uniset
const std::shared_ptr<SharedMemory>& _ic, const std::string& prefix ):
UniSetObject(objId),
allInitOK(false),
initPause(3000),
force(false),
force_out(false),
mbregFromID(false),
sidExchangeMode(DefaultObjectId),
exchangeMode(emNone),
exchangeMode(MBConfig::emNone),
activated(false),
noQueryOptimization(false),
notUseExchangeTimer(false),
prefix(prefix),
poll_count(0),
prop_prefix(""),
mb(nullptr),
ic(_ic)
{
......@@ -53,7 +48,7 @@ namespace uniset
throw uniset::SystemError("(MBExchange): objId=-1?!! Use --" + prefix + "-name" );
auto conf = uniset_conf();
mutex_start.setName(myname + "_mutex_start");
mutex_start.setName(myname + "_mutgc7vdgchex_start");
string conf_name(conf->getArgParam("--" + prefix + "-confnode", myname));
......@@ -63,10 +58,13 @@ namespace uniset
throw uniset::SystemError("(MBExchange): Not found node <" + conf_name + " ...> for " + myname );
shm = make_shared<SMInterface>(shmId, ui, objId, ic);
mbconf = make_shared<MBConfig>(conf, cnode, shm);
mblog = make_shared<DebugStream>();
mblog->setLogName(myname);
conf->initLogStream(mblog, prefix + "-log");
mbconf->mblog = mblog;
mbconf->myname = myname;
loga = make_shared<LogAgregator>(myname + "-loga");
loga->add(mblog);
......@@ -88,16 +86,12 @@ namespace uniset
ic->logAgregator()->add(loga);
// определяем фильтр
s_field = conf->getArgParam("--" + prefix + "-filter-field");
s_fvalue = conf->getArgParam("--" + prefix + "-filter-value");
mbinfo << myname << "(init): read fileter-field='" << s_field
<< "' filter-value='" << s_fvalue << "'" << endl;
mbconf->s_field = conf->getArgParam("--" + prefix + "-filter-field");
mbconf->s_fvalue = conf->getArgParam("--" + prefix + "-filter-value");
mbinfo << myname << "(init): read fileter-field='" << mbconf->s_field
<< "' filter-value='" << mbconf->s_fvalue << "'" << endl;
vmonit(s_field);
vmonit(s_fvalue);
prop_prefix = initPropPrefix();
vmonit(prop_prefix);
mbconf->prop_prefix = initPropPrefix(mbconf->s_field, "");
stat_time = conf->getArgPInt("--" + prefix + "-statistic-sec", it.getProp("statistic_sec"), 0);
vmonit(stat_time);
......@@ -105,50 +99,44 @@ namespace uniset
if( stat_time > 0 )
ptStatistic.setTiming(stat_time * 1000);
recv_timeout = conf->getArgPInt("--" + prefix + "-recv-timeout", it.getProp("recv_timeout"), 500);
vmonit(recv_timeout);
mbconf->recv_timeout = conf->getArgPInt("--" + prefix + "-recv-timeout", it.getProp("recv_timeout"), 500);
// vmonit(recv_timeout);
default_timeout = conf->getArgPInt("--" + prefix + "-timeout", it.getProp("timeout"), 5000);
vmonit(default_timeout);
mbconf->default_timeout = conf->getArgPInt("--" + prefix + "-timeout", it.getProp("timeout"), 5000);
// vmonit(default_timeout);
int tout = conf->getArgPInt("--" + prefix + "-reopen-timeout", it.getProp("reopen_timeout"), default_timeout * 2);
int tout = conf->getArgPInt("--" + prefix + "-reopen-timeout", it.getProp("reopen_timeout"), mbconf->default_timeout * 2);
ptReopen.setTiming(tout);
int reinit_tout = conf->getArgPInt("--" + prefix + "-reinit-timeout", it.getProp("reinit_timeout"), default_timeout);
int reinit_tout = conf->getArgPInt("--" + prefix + "-reinit-timeout", it.getProp("reinit_timeout"), mbconf->default_timeout);
ptInitChannel.setTiming(reinit_tout);
aftersend_pause = conf->getArgPInt("--" + prefix + "-aftersend-pause", it.getProp("aftersend_pause"), 0);
vmonit(aftersend_pause);
mbconf->aftersend_pause = conf->getArgPInt("--" + prefix + "-aftersend-pause", it.getProp("aftersend_pause"), 0);
// vmonit(aftersend_pause);
noQueryOptimization = conf->getArgInt("--" + prefix + "-no-query-optimization", it.getProp("no_query_optimization"));
vmonit(noQueryOptimization);
mbconf->noQueryOptimization = conf->getArgInt("--" + prefix + "-no-query-optimization", it.getProp("no_query_optimization"));
// vmonit(noQueryOptimization);
mbregFromID = conf->getArgInt("--" + prefix + "-reg-from-id", it.getProp("reg_from_id"));
mbinfo << myname << "(init): mbregFromID=" << mbregFromID << endl;
vmonit(mbregFromID);
mbconf->mbregFromID = conf->getArgInt("--" + prefix + "-reg-from-id", it.getProp("reg_from_id"));
mbinfo << myname << "(init): mbregFromID=" << mbconf->mbregFromID << endl;
// vmonit(mbregFromID);
polltime = conf->getArgPInt("--" + prefix + "-polltime", it.getProp("polltime"), 100);
vmonit(polltime);
mbconf->polltime = conf->getArgPInt("--" + prefix + "-polltime", it.getProp("polltime"), 100);
// vmonit(polltime);
initPause = conf->getArgPInt("--" + prefix + "-initPause", it.getProp("initPause"), 3000);
sleepPause_msec = conf->getArgPInt("--" + prefix + "-sleepPause-msec", it.getProp("sleepPause"), 10);
mbconf->sleepPause_msec = conf->getArgPInt("--" + prefix + "-sleepPause-msec", it.getProp("sleepPause"), 10);
force = conf->getArgInt("--" + prefix + "-force", it.getProp("force"));
force_out = conf->getArgInt("--" + prefix + "-force-out", it.getProp("force_out"));
vmonit(force);
vmonit(force_out);
defaultMBtype = conf->getArg2Param("--" + prefix + "-default-mbtype", it.getProp("default_mbtype"), "rtu");
defaultMBaddr = conf->getArg2Param("--" + prefix + "-default-mbaddr", it.getProp("default_mbaddr"), "");
vmonit(defaultMBtype);
vmonit(defaultMBaddr);
defaultMBinitOK = conf->getArgPInt("--" + prefix + "-default-mbinit-ok", it.getProp("default_mbinitOK"), 0);
maxQueryCount = conf->getArgPInt("--" + prefix + "-query-max-count", it.getProp("queryMaxCount"), ModbusRTU::MAXDATALEN);
vmonit(defaultMBinitOK);
mbconf->defaultMBtype = conf->getArg2Param("--" + prefix + "-default-mbtype", it.getProp("default_mbtype"), "rtu");
mbconf->defaultMBaddr = conf->getArg2Param("--" + prefix + "-default-mbaddr", it.getProp("default_mbaddr"), "");
mbconf->defaultMBinitOK = conf->getArgPInt("--" + prefix + "-default-mbinit-ok", it.getProp("default_mbinitOK"), 0);
mbconf->maxQueryCount = conf->getArgPInt("--" + prefix + "-query-max-count", it.getProp("queryMaxCount"), ModbusRTU::MAXDATALEN);
// ********** HEARTBEAT *************
string heart = conf->getArgParam("--" + prefix + "-heartbeat-id", it.getProp("heartbeat_id"));
......@@ -210,29 +198,27 @@ namespace uniset
vmonit(allInitOK);
}
// -----------------------------------------------------------------------------
std::string MBExchange::initPropPrefix( const std::string& def_prop_prefix )
std::string MBExchange::initPropPrefix( const std::string& s_field,
const std::string& def_prop_prefix )
{
auto conf = uniset_conf();
string pp(def_prop_prefix);
string pp = def_prop_prefix;
// если задано поле для "фильтрации"
// то в качестве префикса используем его
if( !s_field.empty() )
pp = s_field + "_";
// если "принудительно" задан префикс
// используем его.
{
string p("--" + prefix + "-set-prop-prefix");
string v = conf->getArgParam(p, "");
// если "принудительно" задан префикс используем его.
const string p = "--" + mbconf->prefix + "-set-prop-prefix";
const string v = conf->getArgParam(p, "");
if( !v.empty() && v[0] != '-' )
pp = v;
// если параметр всё-таки указан, считаем, что это попытка задать "пустой" префикс
else if( findArgParam(p, conf->getArgc(), conf->getArgv()) != -1 )
pp = "";
}
return pp;
}
......@@ -287,7 +273,7 @@ namespace uniset
bool MBExchange::waitSMReady()
{
// waiting for SM is ready...
int tout = uniset_conf()->getArgInt("--" + prefix + "-sm-ready-timeout", "");
int tout = uniset_conf()->getArgInt("--" + mbconf->prefix + "-sm-ready-timeout", "");
timeout_t ready_timeout = uniset_conf()->getNCReadyTimeout();
if( tout > 0 )
......@@ -354,67 +340,30 @@ namespace uniset
return UniSetObject::deactivateObject();
}
// ------------------------------------------------------------------------------------------
void MBExchange::readConfiguration()
bool MBExchange::readItem( const std::shared_ptr<UniXML>& xml, UniXML::iterator& it, xmlNode* sec )
{
// readconf_ok = false;
xmlNode* root = uniset_conf()->getXMLSensorsSection();
if(!root)
if( uniset::check_filter(it, mbconf->s_field, mbconf->s_fvalue) )
{
ostringstream err;
err << myname << "(readConfiguration): не нашли корневого раздела <sensors>";
throw SystemError(err.str());
}
UniXML::iterator it(root);
if( !it.goChildren() )
try
{
mbcrit << myname << "(readConfiguration): раздел <sensors> не содержит секций ?!!\n";
return;
mbconf->initItem(it);
}
for( ; it.getCurrent(); it.goNext() )
catch( std::exception& ex )
{
if( uniset::check_filter(it, s_field, s_fvalue) )
initItem(it);
cerr << ex.what() << endl;
std::abort();
}
// readconf_ok = true;
}
// ------------------------------------------------------------------------------------------
bool MBExchange::readItem( const std::shared_ptr<UniXML>& xml, UniXML::iterator& it, xmlNode* sec )
{
if( uniset::check_filter(it, s_field, s_fvalue) )
initItem(it);
return true;
}
// ------------------------------------------------------------------------------------------
MBExchange::DeviceType MBExchange::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 MBExchange::initIterators()
{
shm->initIterator(itHeartBeat);
shm->initIterator(itExchangeMode);
for( auto it1 = devices.begin(); it1 != devices.end(); ++it1 )
for( auto it1 = mbconf->devices.begin(); it1 != mbconf->devices.end(); ++it1 )
{
auto d = it1->second;
shm->initIterator(d->resp_it);
......@@ -435,7 +384,7 @@ namespace uniset
}
}
for( auto t = thrlist.begin(); t != thrlist.end(); ++t )
for( auto t = mbconf->thrlist.begin(); t != mbconf->thrlist.end(); ++t )
{
shm->initIterator(t->ioit);
shm->initIterator(t->t_ait);
......@@ -444,7 +393,7 @@ namespace uniset
// -----------------------------------------------------------------------------
void MBExchange::initValues()
{
for( auto it1 = devices.begin(); it1 != devices.end(); ++it1 )
for( auto it1 = mbconf->devices.begin(); it1 != mbconf->devices.end(); ++it1 )
{
auto d = it1->second;
......@@ -464,7 +413,7 @@ namespace uniset
}
}
for( auto t = thrlist.begin(); t != thrlist.end(); ++t )
for( auto t = mbconf->thrlist.begin(); t != mbconf->thrlist.end(); ++t )
{
t->value = shm->localGetValue(t->ioit, t->si.id);
}
......@@ -472,7 +421,7 @@ namespace uniset
// -----------------------------------------------------------------------------
bool MBExchange::isUpdateSM( bool wrFunc, long mdev ) const noexcept
{
if( exchangeMode == emSkipExchange || mdev == emSkipExchange )
if( exchangeMode == MBConfig::emSkipExchange || mdev == MBConfig::emSkipExchange )
{
if( wrFunc )
return true; // данные для посылки, должны обновляться всегда (чтобы быть актуальными, когда режим переключиться обратно..)
......@@ -482,21 +431,21 @@ namespace uniset
return false;
}
if( wrFunc && (exchangeMode == emReadOnly || mdev == emReadOnly) )
if( wrFunc && (exchangeMode == MBConfig::emReadOnly || mdev == MBConfig::emReadOnly) )
{
mblog3 << "(checkUpdateSM):"
<< " skip... mode='emReadOnly' " << endl;
return false;
}
if( !wrFunc && (exchangeMode == emWriteOnly || mdev == emWriteOnly) )
if( !wrFunc && (exchangeMode == MBConfig::emWriteOnly || mdev == MBConfig::emWriteOnly) )
{
mblog3 << "(checkUpdateSM):"
<< " skip... mode='emWriteOnly' " << endl;
return false;
}
if( !wrFunc && (exchangeMode == emSkipSaveToSM || mdev == emSkipSaveToSM) )
if( !wrFunc && (exchangeMode == MBConfig::emSkipSaveToSM || mdev == MBConfig::emSkipSaveToSM) )
{
mblog3 << "(checkUpdateSM):"
<< " skip... mode='emSkipSaveToSM' " << endl;
......@@ -508,19 +457,19 @@ namespace uniset
// -----------------------------------------------------------------------------
bool MBExchange::isPollEnabled( bool wrFunc ) const noexcept
{
if( exchangeMode == emWriteOnly && !wrFunc )
if( exchangeMode == MBConfig::emWriteOnly && !wrFunc )
{
mblog3 << myname << "(checkPoll): skip.. mode='emWriteOnly'" << endl;
return false;
}
if( exchangeMode == emReadOnly && wrFunc )
if( exchangeMode == MBConfig::emReadOnly && wrFunc )
{
mblog3 << myname << "(checkPoll): skip.. poll mode='emReadOnly'" << endl;
return false;
}
if( exchangeMode == emSkipExchange )
if( exchangeMode == MBConfig::emSkipExchange )
{
mblog3 << myname << "(checkPoll): skip.. poll mode='emSkipExchange'" << endl;
return false;
......@@ -529,7 +478,7 @@ namespace uniset
return true;
}
// -----------------------------------------------------------------------------
bool MBExchange::isSafeMode( std::shared_ptr<MBExchange::RTUDevice>& dev ) const noexcept
bool MBExchange::isSafeMode( std::shared_ptr<MBConfig::RTUDevice>& dev ) const noexcept
{
if( !dev )
return false;
......@@ -538,173 +487,10 @@ namespace uniset
// то проверяем таймер
// resp_Delay - это задержка на отпускание "пропадание" связи,
// поэтому проверка на "0" (0 - связи нет), а значит должен включиться safeMode
if( dev->safeMode == safeResetIfNotRespond )
if( dev->safeMode == MBConfig::safeResetIfNotRespond )
return !dev->resp_Delay.get();
return ( dev->safeMode != safeNone );
}
// -----------------------------------------------------------------------------
void MBExchange::printMap( MBExchange::RTUDeviceMap& m )
{
cout << "devices: " << endl;
for( auto it = m.begin(); it != m.end(); ++it )
cout << " " << *(it->second) << endl;
}
// -----------------------------------------------------------------------------
std::ostream& operator<<( std::ostream& os, MBExchange::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, MBExchange::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=" << (MBExchange::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 MBExchange::RegInfo* r )
{
return os << (*r);
}
// -----------------------------------------------------------------------------
std::ostream& operator<<( std::ostream& os, const MBExchange::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 MBExchange::rtuQueryOptimization( RTUDeviceMap& dm )
{
mbinfo << myname << "(rtuQueryOptimization): optimization..." << endl;
for( const auto& d : dm )
rtuQueryOptimizationForDevice(d.second);
// printMap(dm);
// assert(false);
}
// -----------------------------------------------------------------------------
void MBExchange::rtuQueryOptimizationForDevice( const std::shared_ptr<RTUDevice>& d )
{
mbinfo << myname << "(rtuQueryOptimizationForDevice): dev addr="
<< ModbusRTU::addr2str(d->mbaddr) << " optimization..." << endl;
for( const auto& m : d->pollmap )
rtuQueryOptimizationForRegMap(m.second);
}
// -----------------------------------------------------------------------------
void MBExchange::rtuQueryOptimizationForRegMap( const std::shared_ptr<RegMap>& regmap )
{
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, MBExchange::PList& lst )
std::ostream& MBExchange::print_plist( std::ostream& os, const MBExchange::PList& lst )
{
auto conf = uniset_conf();
os << "[ ";
for( const auto& p : lst )
os << "(" << p.si.id << ")" << conf->oind->getBaseName(conf->oind->getMapName(p.si.id)) << " ";
os << "]";
return os;
return ( dev->safeMode != MBConfig::safeNone );
}
// -----------------------------------------------------------------------------
void MBExchange::firstInitRegisters()
......@@ -712,7 +498,7 @@ namespace uniset
// если все вернут TRUE, значит OK.
allInitOK = true;
for( auto it = initRegList.begin(); it != initRegList.end(); ++it )
for( auto it = mbconf->initRegList.begin(); it != mbconf->initRegList.end(); ++it )
{
try
{
......@@ -732,8 +518,10 @@ namespace uniset
}
}
// -----------------------------------------------------------------------------
bool MBExchange::preInitRead( InitList::iterator& p )
bool MBExchange::preInitRead( MBConfig::InitList::iterator& p )
{
mbinfo << "init mbreg=" << ModbusRTU::dat2str(p->mbreg) << " initOK=" << p->initOK << endl;
if( p->initOK )
return true;
......@@ -749,10 +537,10 @@ namespace uniset
<< " q_count=" << q_count
<< endl;
if( q_count > maxQueryCount /* ModbusRTU::MAXDATALEN */ )
if( q_count > mbconf->maxQueryCount /* ModbusRTU::MAXDATALEN */ )
{
mblog3 << myname << "(preInitRead): count(" << q_count
<< ") > MAXDATALEN(" << maxQueryCount /* ModbusRTU::MAXDATALEN */
<< ") > MAXDATALEN(" << mbconf->maxQueryCount /* ModbusRTU::MAXDATALEN */
<< " ..ignore..."
<< endl;
}
......@@ -829,7 +617,7 @@ namespace uniset
return p->initOK;
}
// -----------------------------------------------------------------------------
bool MBExchange::initSMValue( ModbusRTU::ModbusData* data, int count, RSProperty* p )
bool MBExchange::initSMValue( ModbusRTU::ModbusData* data, int count, MBConfig::RSProperty* p )
{
using namespace ModbusRTU;
......@@ -968,11 +756,11 @@ namespace uniset
return false;
}
// -----------------------------------------------------------------------------
bool MBExchange::pollRTU( std::shared_ptr<RTUDevice>& dev, RegMap::iterator& it )
bool MBExchange::pollRTU( std::shared_ptr<MBConfig::RTUDevice>& dev, MBConfig::RegMap::iterator& it )
{
auto p = it->second;
if( dev->mode == emSkipExchange )
if( dev->mode == MBConfig::emSkipExchange )
{
mblog3 << myname << "(pollRTU): SKIP EXCHANGE (mode=emSkipExchange) "
<< " mbaddr=" << ModbusRTU::addr2str(dev->mbaddr)
......@@ -992,10 +780,10 @@ namespace uniset
<< " mbval=" << p->mbval
<< endl;
if( p->q_count > maxQueryCount /* ModbusRTU::MAXDATALEN */ )
if( p->q_count > mbconf->maxQueryCount /* ModbusRTU::MAXDATALEN */ )
{
mblog3 << myname << "(pollRTU): count(" << p->q_count
<< ") > MAXDATALEN(" << maxQueryCount /* ModbusRTU::MAXDATALEN */
<< ") > MAXDATALEN(" << mbconf->maxQueryCount /* ModbusRTU::MAXDATALEN */
<< " ..ignore..."
<< endl;
}
......@@ -1188,7 +976,7 @@ namespace uniset
// -----------------------------------------------------------------------------
void MBExchange::updateSM()
{
for( auto it1 = devices.begin(); it1 != devices.end(); ++it1 )
for( auto it1 = mbconf->devices.begin(); it1 != mbconf->devices.end(); ++it1 )
{
auto d = it1->second;
......@@ -1232,9 +1020,9 @@ namespace uniset
if( !shm->isLocalwork() )
{
if( shm->localGetValue(d->safemode_it, d->safemode_id) == d->safemode_value )
d->safeMode = safeExternalControl;
d->safeMode = MBConfig::safeExternalControl;
else
d->safeMode = safeNone;
d->safeMode = MBConfig::safeNone;
}
}
catch(IOController_i::NameNotFound& ex)
......@@ -1278,11 +1066,11 @@ namespace uniset
{
try
{
if( d->dtype == dtRTU )
if( d->dtype == MBConfig::dtRTU )
updateRTU(it);
else if( d->dtype == dtMTR )
else if( d->dtype == MBConfig::dtMTR )
updateMTR(it);
else if( d->dtype == dtRTU188 )
else if( d->dtype == MBConfig::dtRTU188 )
updateRTU188(it);
}
catch(IOController_i::NameNotFound& ex)
......@@ -1320,7 +1108,7 @@ namespace uniset
}
// -----------------------------------------------------------------------------
void MBExchange::updateRTU( RegMap::iterator& rit )
void MBExchange::updateRTU( MBConfig::RegMap::iterator& rit )
{
auto& r = rit->second;
......@@ -1329,7 +1117,7 @@ namespace uniset
}
// -----------------------------------------------------------------------------
void MBExchange::updateRSProperty( RSProperty* p, bool write_only )
void MBExchange::updateRSProperty( MBConfig::RSProperty* p, bool write_only )
{
using namespace ModbusRTU;
auto& r = p->reg->rit->second;
......@@ -1781,7 +1569,7 @@ namespace uniset
r->sm_initOK = false;
}
// -----------------------------------------------------------------------------
void MBExchange::updateMTR( RegMap::iterator& rit )
void MBExchange::updateMTR( MBConfig::RegMap::iterator& rit )
{
auto& r = rit->second;
......@@ -2118,7 +1906,7 @@ namespace uniset
}
}
// -----------------------------------------------------------------------------
void MBExchange::updateRTU188( RegMap::iterator& rit )
void MBExchange::updateRTU188( MBConfig::RegMap::iterator& rit )
{
auto& r = rit->second;
......@@ -2185,925 +1973,204 @@ namespace uniset
}
}
// -----------------------------------------------------------------------------
std::shared_ptr<MBExchange::RTUDevice> MBExchange::addDev( RTUDeviceMap& mp, ModbusRTU::ModbusAddr a, UniXML::iterator& xmlit )
{
auto it = mp.find(a);
if( it != mp.end() )
bool MBExchange::activateObject()
{
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 )
// блокирование обработки Starsp
// пока не пройдёт инициализация датчиков
// см. sysCommand()
{
mbcrit << myname << "(addDev): OTHER mbtype=" << dtype << " for " << xmlit.getProp("name")
<< ". 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;
}
setProcActive(false);
uniset::uniset_rwmutex_rlock l(mutex_start);
UniSetObject::activateObject();
auto d = make_shared<MBExchange::RTUDevice>();
d->mbaddr = a;
if( !shm->isLocalwork() )
MBConfig::rtuQueryOptimization(mbconf->devices, mbconf->maxQueryCount);
if( !initRTUDevice(d, xmlit) )
{
d.reset();
return 0;
initIterators();
setProcActive(true);
}
mp.insert( std::make_pair(a, d) );
return d;
return true;
}
// ------------------------------------------------------------------------------------------
std::shared_ptr<MBExchange::RegInfo> MBExchange::addReg( std::shared_ptr<RegMap>& mp, ModbusRTU::RegID regID, ModbusRTU::ModbusData r,
UniXML::iterator& xmlit, std::shared_ptr<MBExchange::RTUDevice> dev )
void MBExchange::sysCommand( const uniset::SystemMessage* sm )
{
auto it = mp->find(regID);
if( it != mp->end() )
switch( sm->command )
{
if( !it->second->dev )
case SystemMessage::StartUp:
{
mbcrit << myname << "(addReg): for " << xmlit.getProp("name")
<< " dev=0!!!! " << endl;
return 0;
}
if( it->second->dev->dtype != dev->dtype )
if( !logserv_host.empty() && logserv_port != 0 && !logserv->isRunning() )
{
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 << "(init): run log server " << logserv_host << ":" << logserv_port << endl;
logserv->async_run(logserv_host, logserv_port);
}
mbinfo << myname << "(addReg): reg=" << ModbusRTU::dat2str(r)
<< "(id=" << regID << ")"
<< " already added for " << (*it->second)
<< " Ignore register params for " << xmlit.getProp("name") << " ..." << endl;
it->second->rit = it;
return it->second;
if( mbconf->devices.empty() )
{
mbcrit << myname << "(sysCommand): ************* ITEM MAP EMPTY! terminated... *************" << endl << flush;
// std::terminate();
uterminate();
return;
}
auto ri = make_shared<MBExchange::RegInfo>();
mbinfo << myname << "(sysCommand): device map size= " << mbconf->devices.size() << endl;
if( !initRegInfo(ri, xmlit, dev) )
return 0;
if( !shm->isLocalwork() )
mbconf->initDeviceList(uniset_conf()->getConfXML());
ri->mbreg = r;
ri->regID = regID;
if( !waitSMReady() )
{
if( !canceled )
uterminate();
mp->insert( std::make_pair(regID, ri) );
ri->rit = mp->find(regID);
return;
}
mbinfo << myname << "(addReg): reg=" << ModbusRTU::dat2str(r) << "(id=" << regID << ")" << endl;
// подождать пока пройдёт инициализация датчиков
// см. activateObject()
msleep(initPause);
PassiveTimer ptAct(activateTimeout);
return ri;
}
// ------------------------------------------------------------------------------------------
MBExchange::RSProperty* MBExchange::addProp( PList& plist, RSProperty&& p )
{
for( auto&& it : plist )
while( !isProcActive() && !ptAct.checkTime() )
{
if( it.si.id == p.si.id && it.si.node == p.si.node )
return &it;
}
cout << myname << "(sysCommand): wait activate..." << endl;
msleep(300);
plist.emplace_back( std::move(p) );
auto it = plist.end();
--it;
return &(*it);
if( isProcActive() )
break;
}
// ------------------------------------------------------------------------------------------
bool MBExchange::initRSProperty( RSProperty& p, UniXML::iterator& it )
{
if( !IOBase::initItem(&p, it, shm, prop_prefix, false, mblog, myname) )
return false;
// проверяем не пороговый ли это датчик (т.е. не связанный с обменом)
// тогда заносим его в отдельный список
if( p.t_ai != DefaultObjectId )
if( !isProcActive() )
{
thrlist.emplace_back( std::move(p) );
return true;
mbcrit << myname << "(sysCommand): ************* don`t activate?! ************" << endl << flush;
uterminate();
return;
}
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;
}
uniset::uniset_rwmutex_rlock l(mutex_start);
askSensors(UniversalIO::UIONotify);
initOutput();
}
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;
initValues();
updateSM();
askTimer(tmExchange, mbconf->polltime);
break;
}
string sbyte(IOBase::initProp(it, "nbyte", prop_prefix, false) );
case SystemMessage::FoldUp:
case SystemMessage::Finish:
askSensors(UniversalIO::UIODontNotify);
break;
if( !sbyte.empty() )
case SystemMessage::WatchDog:
{
p.nbyte = uniset::uni_atoi(sbyte.c_str());
// ОПТИМИЗАЦИЯ (защита от двойного перезаказа при старте)
// Если идёт локальная работа
// (т.е. MBExchange запущен в одном процессе с SharedMemory2)
// то обрабатывать WatchDog не надо, т.к. мы и так ждём готовности SM
// при заказе датчиков, а если SM вылетит, то вместе с этим процессом(MBExchange)
if( shm->isLocalwork() )
break;
askSensors(UniversalIO::UIONotify);
initOutput();
initValues();
if( p.nbyte > VTypes::Byte::bsize )
if( !force )
{
mbwarn << myname << "(initRSProperty): BAD nbyte=" << p.nbyte
<< ". (0 >= nbyte < " << VTypes::Byte::bsize << ")." << endl;
return false;
force = true;
poll();
force = false;
}
}
break;
string vt( IOBase::initProp(it, "vtype", prop_prefix, false) );
case SystemMessage::LogRotate:
{
mblogany << myname << "(sysCommand): logRotate" << std::endl;
string fname = mblog->getLogFile();
if( vt.empty() )
if( !fname.empty() )
{
p.rnum = VTypes::wsize(VTypes::vtUnknown);
p.vType = VTypes::vtUnknown;
mblog->logFile(fname, true);
mblogany << myname << "(sysCommand): ***************** mblog LOG ROTATE *****************" << std::endl;
}
else
{
VTypes::VType v(VTypes::str2type(vt));
}
break;
if( v == VTypes::vtUnknown )
case SystemMessage::ReConfiguration:
{
mbcrit << myname << "(initRSProperty): Unknown tcp_vtype='" << vt << "' for "
<< it.getProp("name")
<< endl;
return false;
mblogany << myname << "(sysCommand): reconfig" << std::endl;
reconfigure(uniset_conf()->getConfFileName());
}
break;
p.vType = v;
p.rnum = VTypes::wsize(v);
default:
break;
}
return true;
}
// ------------------------------------------------------------------------------------------
bool MBExchange::initRegInfo( std::shared_ptr<RegInfo>& r, UniXML::iterator& it, std::shared_ptr<MBExchange::RTUDevice>& dev )
bool MBExchange::reconfigure( const std::string& confile )
{
r->dev = dev;
r->mbval = IOBase::initIntProp(it, "default", prop_prefix, false);
mbinfo << myname << ": reconfigure from " << confile << endl;
if( dev->dtype == MBExchange::dtRTU )
{
// mblog.info() << myname << "(initRegInfo): init RTU.."
}
else if( dev->dtype == MBExchange::dtMTR )
{
// only for MTR
if( !initMTRitem(it, r) )
return false;
}
else if( dev->dtype == MBExchange::dtRTU188 )
{
// only for RTU188
if( !initRTU188item(it, r) )
return false;
uniset::uniset_rwmutex_wrlock lock(mutex_conf);
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);
auto oldConf = mbconf;
// т.к. с RTU188 свой обмен
// mbreg и mbfunc поля не используются
return true;
std::shared_ptr<MBConfig> newConf;
}
else
try
{
mbcrit << myname << "(initRegInfo): Unknown mbtype='" << dev->dtype
<< "' for " << it.getProp("name") << endl;
return false;
}
auto xml = make_shared<UniXML>(confile);
if( mbregFromID )
{
if( it.getProp("id").empty() )
r->mbreg = uniset_conf()->getSensorID(it.getProp("name"));
else
r->mbreg = it.getIntProp("id");
}
else
{
string sr( IOBase::initProp(it, "mbreg", prop_prefix, false) );
string conf_name(uniset_conf()->getArgParam("--" + mbconf->prefix + "-confnode", myname));
xmlNode* newCnode = xml->findNode(xml->getFirstNode(), conf_name);
if( sr.empty() )
if( newCnode == NULL )
{
mbcrit << myname << "(initItem): Unknown 'mbreg' for " << it.getProp("name") << endl;
mbcrit << myname << "(reconfigure): reload config from '" << confile
<< "' FAILED: not found conf node '" << conf_name
<< "'" << endl;
return false;
}
r->mbreg = ModbusRTU::str2mbData(sr);
newConf = make_shared<MBConfig>(mbconf->conf, newCnode, mbconf->shm);
newConf->cloneParams(mbconf);
newConf->loadConfig(xml, nullptr);
MBConfig::rtuQueryOptimization(newConf->devices, newConf->maxQueryCount);
if( newConf->devices.empty() )
{
mbcrit << myname << "(reconfigure): reload config from '" << confile << "' FAILED: empty devices list" << std::endl;
return false;
}
r->mbfunc = ModbusRTU::fnUnknown;
string f( IOBase::initProp(it, "mbfunc", prop_prefix, false) );
mbconf = newConf;
if( !f.empty() )
{
r->mbfunc = (ModbusRTU::SlaveFunctionCode)uniset::uni_atoi(f.c_str());
if( !shm->isLocalwork() )
initIterators();
if( r->mbfunc == ModbusRTU::fnUnknown )
{
mbcrit << myname << "(initRegInfo): Unknown mbfunc ='" << f
<< "' for " << it.getProp("name") << endl;
return false;
allInitOK = false;
initOutput();
initValues();
updateSM();
ptInitChannel.reset();
ptReopen.reset();
return true;
}
catch( std::exception& ex )
{
mbcrit << myname << "(sysCommand): reload config from '" << confile << "' FAILED: " << ex.what() << std::endl;
}
return true;
mbconf = oldConf;
return false;
}
// ------------------------------------------------------------------------------------------
bool MBExchange::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 == MBExchange::dtRTU188 )
{
if( !d->rtu188 )
d->rtu188 = make_shared<RTUStorage>(d->mbaddr);
}
return true;
}
// ------------------------------------------------------------------------------------------
bool MBExchange::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
{
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 )
{
auto conf = uniset_conf();
ostringstream sl;
sl << "[ ";
for( const auto& i : ri->slst )
sl << ORepHelpers::getShortName(conf->oind->getMapName(i.si.id)) << ",";
sl << "]";
mbcrit << myname << "(initItem): FAILED! Sharing SAVE (not bit saving) to "
<< " tcp_mbreg=" << ModbusRTU::dat2str(ri->mbreg) << "(" << (int)ri->mbreg << ")"
<< " conflict with sensors " << sl.str() << endl;
std::abort(); // ABORT PROGRAM!!!!
return false;
}
if( p.nbit >= 0 && ri->slst.size() == 1 )
{
auto it2 = ri->slst.begin();
if( it2->nbit < 0 )
{
mbcrit << myname << "(initItem): FAILED! Sharing SAVE (mbreg="
<< ModbusRTU::dat2str(ri->mbreg) << "(" << (int)ri->mbreg << ") already used)!"
<< " IGNORE --> " << it.getProp("name") << endl;
std::abort(); // ABORT PROGRAM!!!!
return false;
}
}
// Раз это регистр для записи, то как минимум надо сперва
// инициализировать значением из 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 )
{
mbcrit << myname << "(initItem): Bad write function ='" << ModbusRTU::fnWriteOutputSingleRegister
<< "' for vtype='" << p1->vType << "'"
<< " tcp_mbreg=" << ModbusRTU::dat2str(ri->mbreg)
<< " for " << it.getProp("name") << endl;
abort(); // ABORT PROGRAM!!!!
return false;
}
}
}
}
// Фомируем список инициализации
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;
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 MBExchange::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 MBExchange::initRTU188item( UniXML::iterator& it, std::shared_ptr<RegInfo>& p )
{
string jack(IOBase::initProp(it, "jakc", prop_prefix, false));
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 MBExchange::DeviceType& dt )
{
switch(dt)
{
case MBExchange::dtRTU:
os << "RTU";
break;
case MBExchange::dtMTR:
os << "MTR";
break;
case MBExchange::dtRTU188:
os << "RTU188";
break;
default:
os << "Unknown device type (" << (int)dt << ")";
break;
}
return os;
}
// -----------------------------------------------------------------------------
std::ostream& operator<<( std::ostream& os, const MBExchange::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 MBExchange::initDeviceList()
{
auto conf = uniset_conf();
xmlNode* respNode = 0;
const std::shared_ptr<UniXML> xml = conf->getConfXML();
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 MBExchange::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 = uniset_conf()->getSensorID(s);
if( dev->resp_id == DefaultObjectId )
{
mbinfo << myname << "(initDeviceInfo): not found ID for respondSensor=" << s << endl;
return false;
}
}
auto conf = uniset_conf();
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 = MBExchange::safeResetIfNotRespond;
// потом проверим датчик для "safeExternalControl"
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;
}
string safemodeValue(it.getProp("safemodeValue"));
if( !safemodeValue.empty() )
dev->safemode_value = uni_atoi(safemodeValue);
dev->safeMode = MBExchange::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);
return true;
}
// -----------------------------------------------------------------------------
bool MBExchange::activateObject()
{
// блокирование обработки Starsp
// пока не пройдёт инициализация датчиков
// см. sysCommand()
{
setProcActive(false);
uniset::uniset_rwmutex_rlock l(mutex_start);
UniSetObject::activateObject();
if( !shm->isLocalwork() )
rtuQueryOptimization(devices);
initIterators();
setProcActive(true);
}
return true;
}
// ------------------------------------------------------------------------------------------
void MBExchange::sysCommand( const uniset::SystemMessage* sm )
{
switch( sm->command )
{
case SystemMessage::StartUp:
{
if( !logserv_host.empty() && logserv_port != 0 && !logserv->isRunning() )
{
mbinfo << myname << "(init): run log server " << logserv_host << ":" << logserv_port << endl;
logserv->async_run(logserv_host, logserv_port);
}
if( devices.empty() )
{
mbcrit << myname << "(sysCommand): ************* ITEM MAP EMPTY! terminated... *************" << endl << flush;
// std::terminate();
uterminate();
return;
}
mbinfo << myname << "(sysCommand): device map size= " << devices.size() << endl;
if( !shm->isLocalwork() )
initDeviceList();
if( !waitSMReady() )
{
if( !canceled )
uterminate();
return;
}
// подождать пока пройдёт инициализация датчиков
// см. activateObject()
msleep(initPause);
PassiveTimer ptAct(activateTimeout);
while( !isProcActive() && !ptAct.checkTime() )
{
cout << myname << "(sysCommand): wait activate..." << endl;
msleep(300);
if( isProcActive() )
break;
}
if( !isProcActive() )
{
mbcrit << myname << "(sysCommand): ************* don`t activate?! ************" << endl << flush;
uterminate();
return;
}
{
uniset::uniset_rwmutex_rlock l(mutex_start);
askSensors(UniversalIO::UIONotify);
initOutput();
}
initValues();
updateSM();
askTimer(tmExchange, polltime);
break;
}
case SystemMessage::FoldUp:
case SystemMessage::Finish:
askSensors(UniversalIO::UIODontNotify);
break;
case SystemMessage::WatchDog:
{
// ОПТИМИЗАЦИЯ (защита от двойного перезаказа при старте)
// Если идёт локальная работа
// (т.е. MBExchange запущен в одном процессе с SharedMemory2)
// то обрабатывать WatchDog не надо, т.к. мы и так ждём готовности SM
// при заказе датчиков, а если SM вылетит, то вместе с этим процессом(MBExchange)
if( shm->isLocalwork() )
break;
askSensors(UniversalIO::UIONotify);
initOutput();
initValues();
if( !force )
{
force = true;
poll();
force = false;
}
}
break;
case SystemMessage::LogRotate:
{
mblogany << myname << "(sysCommand): logRotate" << std::endl;
string fname = mblog->getLogFile();
if( !fname.empty() )
{
mblog->logFile(fname, true);
mblogany << myname << "(sysCommand): ***************** mblog LOG ROTATE *****************" << std::endl;
}
}
break;
default:
break;
}
}
// ------------------------------------------------------------------------------------------
void MBExchange::initOutput()
void MBExchange::initOutput()
{
}
// ------------------------------------------------------------------------------------------
......@@ -3136,7 +2203,7 @@ namespace uniset
mbwarn << myname << "(askSensors): 'sidExchangeMode' catch..." << std::endl;
}
for( auto it1 = devices.begin(); it1 != devices.end(); ++it1 )
for( auto it1 = mbconf->devices.begin(); it1 != mbconf->devices.end(); ++it1 )
{
auto d = it1->second;
......@@ -3202,7 +2269,7 @@ namespace uniset
//return; // этот датчик может встречаться и в списке обмена.. поэтому делать return нельзя.
}
for( auto it1 = devices.begin(); it1 != devices.end(); ++it1 )
for( auto it1 = mbconf->devices.begin(); it1 != mbconf->devices.end(); ++it1 )
{
auto& d(it1->second);
......@@ -3212,9 +2279,9 @@ namespace uniset
if( sm->id == d->safemode_id )
{
if( sm->value == d->safemode_value )
d->safeMode = safeExternalControl;
d->safeMode = MBConfig::safeExternalControl;
else
d->safeMode = safeNone;
d->safeMode = MBConfig::safeNone;
}
if( force_out )
......@@ -3264,6 +2331,8 @@ namespace uniset
// -----------------------------------------------------------------------------
bool MBExchange::poll()
{
uniset::uniset_rwmutex_rlock lock(mutex_conf);
if( !mb )
{
mb = initMB(false);
......@@ -3277,7 +2346,7 @@ namespace uniset
if( !mb )
return false;
for( auto it1 = devices.begin(); it1 != devices.end(); ++it1 )
for( auto it1 = mbconf->devices.begin(); it1 != mbconf->devices.end(); ++it1 )
it1->second->resp_ptInit.reset();
}
......@@ -3290,11 +2359,11 @@ namespace uniset
ncycle++;
bool allNotRespond = true;
for( auto it1 = devices.begin(); it1 != devices.end(); ++it1 )
for( auto it1 = mbconf->devices.begin(); it1 != mbconf->devices.end(); ++it1 )
{
auto d(it1->second);
if( d->mode_id != DefaultObjectId && d->mode == emSkipExchange )
if( d->mode_id != DefaultObjectId && d->mode == MBConfig::emSkipExchange )
continue;
mblog3 << myname << "(poll): ask addr=" << ModbusRTU::addr2str(d->mbaddr)
......@@ -3314,12 +2383,12 @@ namespace uniset
if( !isProcActive() )
return false;
if( exchangeMode == emSkipExchange )
if( exchangeMode == MBConfig::emSkipExchange )
continue;
try
{
if( d->dtype == MBExchange::dtRTU || d->dtype == MBExchange::dtMTR )
if( d->dtype == MBConfig::dtRTU || d->dtype == MBConfig::dtMTR )
{
if( pollRTU(d, it) )
{
......@@ -3333,7 +2402,7 @@ namespace uniset
mblog3 << myname << "(poll): FAILED ask addr=" << ModbusRTU::addr2str(d->mbaddr)
<< " reg=" << ModbusRTU::dat2str(it->second->mbreg)
<< " for sensors: ";
print_plist(mblog->level3(), it->second->slst)
mbconf->print_plist( (*mblog)(Debug::LEVEL3), it->second->slst)
<< endl << " err: " << ex << endl;
if( ex.err == ModbusRTU::erTimeOut && !d->ask_every_reg )
......@@ -3369,7 +2438,7 @@ namespace uniset
updateSM();
// check thresholds
for( auto t = thrlist.begin(); t != thrlist.end(); ++t )
for( auto t = mbconf->thrlist.begin(); t != mbconf->thrlist.end(); ++t )
{
if( !isProcActive() )
return false;
......@@ -3377,10 +2446,10 @@ namespace uniset
IOBase::processingThreshold(&(*t), shm, force);
}
if( trReopen.hi(allNotRespond && exchangeMode != emSkipExchange) )
if( trReopen.hi(allNotRespond && exchangeMode != MBConfig::emSkipExchange) )
ptReopen.reset();
if( allNotRespond && exchangeMode != emSkipExchange && ptReopen.checkTime() )
if( allNotRespond && exchangeMode != MBConfig::emSkipExchange && ptReopen.checkTime() )
{
mbwarn << myname << ": REOPEN timeout..(" << ptReopen.getInterval() << ")" << endl;
......@@ -3388,39 +2457,14 @@ namespace uniset
ptReopen.reset();
}
// printMap(rmap);
return !allNotRespond;
}
// -----------------------------------------------------------------------------
bool MBExchange::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() );
}
// -----------------------------------------------------------------------------
void MBExchange::updateRespondSensors()
{
for( const auto& it1 : devices )
uniset::uniset_rwmutex_rlock lock(mutex_conf);
for( const auto& it1 : mbconf->devices )
{
auto d(it1.second);
......@@ -3480,47 +2524,8 @@ namespace uniset
mbcrit << myname << "(execute): catch: " << ex.what() << endl;
}
msleep(polltime);
}
msleep(mbconf->polltime);
}
// -----------------------------------------------------------------------------
std::ostream& operator<<( std::ostream& os, const MBExchange::ExchangeMode& em )
{
if( em == MBExchange::emNone )
return os << "emNone";
if( em == MBExchange::emWriteOnly )
return os << "emWriteOnly";
if( em == MBExchange::emReadOnly )
return os << "emReadOnly";
if( em == MBExchange::emSkipSaveToSM )
return os << "emSkipSaveToSM";
if( em == MBExchange::emSkipExchange )
return os << "emSkipExchange";
return os;
}
// -----------------------------------------------------------------------------
std::string to_string( const MBExchange::SafeMode& m )
{
if( m == MBExchange::safeNone )
return "safeNone";
if( m == MBExchange::safeResetIfNotRespond )
return "safeResetIfNotRespond";
if( m == MBExchange::safeExternalControl )
return "safeExternalControl";
return "";
}
ostream& operator<<( ostream& os, const MBExchange::SafeMode& m )
{
return os << to_string(m);
}
// -----------------------------------------------------------------------------
uniset::SimpleInfo* MBExchange::getInfo( const char* userparam )
......@@ -3541,27 +2546,11 @@ namespace uniset
inf << "Devices: " << endl;
for( const auto& it : devices )
for( const auto& it : mbconf->devices )
inf << " " << it.second->getShortInfo() << endl;
i->info = inf.str().c_str();
return i._retn();
}
// ----------------------------------------------------------------------------
std::string MBExchange::RTUDevice::getShortInfo() const
{
ostringstream s;
s << "mbaddr=" << ModbusRTU::addr2str(mbaddr) << ":"
<< " resp_state=" << resp_state
<< " (resp_id=" << resp_id << " resp_force=" << resp_force
<< " resp_invert=" << resp_invert
<< " numreply=" << numreply
<< " timeout=" << resp_Delay.getOffDelay()
<< " type=" << dtype
<< " ask_every_reg=" << ask_every_reg
<< ")" << endl;
return s.str();
}
// ----------------------------------------------------------------------------
} // end of namespace uniset
......@@ -41,6 +41,7 @@
#include "LogServer.h"
#include "LogAgregator.h"
#include "VMonitor.h"
#include "MBConfig.h"
// -----------------------------------------------------------------------------
#ifndef vmonit
#define vmonit( var ) vmon.add( #var, var )
......@@ -63,170 +64,6 @@ namespace uniset
/*! глобальная функция для вывода help-а */
static void help_print( int argc, const char* const* argv );
/*! Режимы работы процесса обмена */
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;
static 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 );
void printMap(RTUDeviceMap& d);
// ----------------------------------
enum Timer
{
......@@ -246,6 +83,8 @@ namespace uniset
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;
......@@ -258,80 +97,41 @@ namespace uniset
virtual void initIterators();
virtual void initValues();
struct InitRegInfo
{
InitRegInfo():
dev(0), mbreg(0),
mbfunc(ModbusRTU::fnUnknown),
initOK(false)
{}
RSProperty p;
std::shared_ptr<RTUDevice> dev;
ModbusRTU::ModbusData mbreg;
ModbusRTU::SlaveFunctionCode mbfunc;
bool initOK;
std::shared_ptr<RegInfo> ri;
};
typedef std::list<InitRegInfo> InitList;
void firstInitRegisters();
bool preInitRead( InitList::iterator& p );
bool initSMValue( ModbusRTU::ModbusData* data, int count, RSProperty* p );
bool preInitRead( MBConfig::InitList::iterator& p );
bool initSMValue( ModbusRTU::ModbusData* data, int count, MBConfig::RSProperty* p );
bool allInitOK;
RTUDeviceMap devices;
InitList initRegList; /*!< список регистров для инициализации */
virtual std::shared_ptr<ModbusClient> initMB( bool reopen = false ) = 0;
virtual bool poll();
bool pollRTU( std::shared_ptr<RTUDevice>& dev, RegMap::iterator& it );
bool pollRTU( std::shared_ptr<MBConfig::RTUDevice>& dev, MBConfig::RegMap::iterator& it );
void updateSM();
// в функции передаётся итератор,
// т.к. в них идёт итерирование в случае если запрос в несколько регистров
void updateRTU(RegMap::iterator& it);
void updateMTR(RegMap::iterator& it);
void updateRTU188(RegMap::iterator& it);
void updateRSProperty( RSProperty* p, bool write_only = false );
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<RTUDevice>& dev ) const noexcept;
bool isSafeMode( std::shared_ptr<MBConfig::RTUDevice>& dev ) const noexcept;
bool isProcActive() const;
void setProcActive( bool st );
bool waitSMReady();
void readConfiguration();
// void readConfiguration();
bool readItem( const std::shared_ptr<UniXML>& xml, UniXML::iterator& it, xmlNode* sec );
bool initItem( UniXML::iterator& it );
void initDeviceList();
void initOffsetList();
std::shared_ptr<RTUDevice> addDev( RTUDeviceMap& dmap, ModbusRTU::ModbusAddr a, UniXML::iterator& it );
std::shared_ptr<RegInfo> addReg(std::shared_ptr<RegMap>& devices, ModbusRTU::RegID id, ModbusRTU::ModbusData r, UniXML::iterator& it, std::shared_ptr<RTUDevice> dev );
RSProperty* addProp( PList& plist, RSProperty&& p );
bool initMTRitem(UniXML::iterator& it, std::shared_ptr<RegInfo>& p );
bool initRTU188item(UniXML::iterator& it, std::shared_ptr<RegInfo>& p );
bool initRSProperty( RSProperty& p, UniXML::iterator& it );
bool initRegInfo(std::shared_ptr<RegInfo>& r, UniXML::iterator& it, std::shared_ptr<RTUDevice>& dev );
bool initRTUDevice( std::shared_ptr<RTUDevice>& d, UniXML::iterator& it );
virtual bool initDeviceInfo( RTUDeviceMap& m, ModbusRTU::ModbusAddr a, UniXML::iterator& it );
std::string initPropPrefix( const std::string& def_prop_prefix = "" );
void rtuQueryOptimization( RTUDeviceMap& m );
void rtuQueryOptimizationForDevice( const std::shared_ptr<RTUDevice>& d );
void rtuQueryOptimizationForRegMap( const std::shared_ptr<RegMap>& regmap );
std::string initPropPrefix( const std::string& s_filed, const std::string& def_prop_prefix );
xmlNode* cnode = { 0 };
std::string s_field;
std::string s_fvalue;
std::shared_ptr<SMInterface> shm;
timeout_t initPause = { 3000 };
......@@ -339,10 +139,6 @@ namespace uniset
bool force = { false }; /*!< флаг означающий, что надо сохранять в SM, даже если значение не менялось */
bool force_out = { false }; /*!< флаг означающий, принудительного чтения выходов */
bool mbregFromID = { false };
timeout_t polltime = { 100 }; /*!< периодичность обновления данных, [мсек] */
timeout_t sleepPause_msec = { 10 };
size_t maxQueryCount = { ModbusRTU::MAXDATALEN }; /*!< максимальное количество регистров для одного запроса */
PassiveTimer ptHeartBeat;
uniset::ObjectId sidHeartBeat = { uniset::DefaultObjectId };
......@@ -352,31 +148,20 @@ namespace uniset
uniset::ObjectId sidExchangeMode = { uniset::DefaultObjectId }; /*!< идентификатор для датчика режима работы */
IOController::IOStateList::iterator itExchangeMode;
long exchangeMode = { emNone }; /*!< режим работы см. ExchangeMode */
long exchangeMode = { MBConfig::emNone }; /*!< режим работы см. ExchangeMode */
std::atomic_bool activated = { false };
std::atomic_bool canceled = { false };
timeout_t activateTimeout = { 20000 }; // msec
bool noQueryOptimization = { false };
bool notUseExchangeTimer = { false };
std::string prefix;
timeout_t stat_time = { 0 }; /*!< время сбора статистики обмена, 0 - отключена */
size_t poll_count = { 0 };
PassiveTimer ptStatistic; /*!< таймер для сбора статистики обмена */
std::string statInfo = { "" };
std::string prop_prefix; /*!< префикс для считывания параметров обмена */
std::shared_ptr<ModbusClient> mb;
// определение timeout для соединения
timeout_t recv_timeout = { 500 }; // msec
timeout_t default_timeout = { 5000 }; // msec
timeout_t aftersend_pause = { 0 };
PassiveTimer ptReopen; /*!< таймер для переоткрытия соединения */
Trigger trReopen;
......@@ -397,11 +182,20 @@ 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;
size_t ncycle = { 0 }; /*!< текущий номер цикла опроса */
std::shared_ptr<uniset::MBConfig> mbconf;
uniset::uniset_rwmutex mutex_conf;
private:
MBExchange();
......
......@@ -38,8 +38,8 @@ MBTCPMaster::MBTCPMaster(uniset::ObjectId objId, uniset::ObjectId shmId,
auto conf = uniset_conf();
// префикс для "свойств" - по умолчанию "tcp_";
prop_prefix = initPropPrefix("tcp_");
mbinfo << myname << "(init): prop_prefix=" << prop_prefix << endl;
mbconf->prop_prefix = initPropPrefix(mbconf->s_field, "tcp_");
mbinfo << myname << "(init): prop_prefix=" << mbconf->prop_prefix << endl;
UniXML::iterator it(cnode);
......@@ -62,14 +62,7 @@ MBTCPMaster::MBTCPMaster(uniset::ObjectId objId, uniset::ObjectId shmId,
mbinfo << myname << "(init): persisten-connection=" << (!force_disconnect) << endl;
if( shm->isLocalwork() )
{
readConfiguration();
if( !noQueryOptimization )
rtuQueryOptimization(devices);
initDeviceList();
}
mbconf->loadConfig(conf->getConfXML(), conf->getXMLSensorsSection());
else
ic->addReadItem( sigc::mem_fun(this, &MBTCPMaster::readItem) );
......@@ -77,7 +70,7 @@ MBTCPMaster::MBTCPMaster(uniset::ObjectId objId, uniset::ObjectId shmId,
pollThread->setFinalAction(this, &MBTCPMaster::final_thread);
if( mblog->is_info() )
printMap(devices);
MBConfig::printMap(mbconf->devices);
}
// -----------------------------------------------------------------------------
MBTCPMaster::~MBTCPMaster()
......@@ -112,11 +105,11 @@ std::shared_ptr<ModbusClient> MBTCPMaster::initMB( bool reopen )
mbtcp->connect(iaddr, port);
mbtcp->setForceDisconnect(force_disconnect);
if( recv_timeout > 0 )
mbtcp->setTimeout(recv_timeout);
if( mbconf->recv_timeout > 0 )
mbtcp->setTimeout(mbconf->recv_timeout);
mbtcp->setSleepPause(sleepPause_msec);
mbtcp->setAfterSendPause(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;
......@@ -182,7 +175,7 @@ void MBTCPMaster::poll_thread()
if( !isProcActive() )
break;
msleep(polltime);
msleep(mbconf->polltime);
}
dinfo << myname << "(poll_thread): thread finished.." << endl;
......
......@@ -38,8 +38,8 @@ MBTCPMultiMaster::MBTCPMultiMaster( uniset::ObjectId objId, uniset::ObjectId shm
auto conf = uniset_conf();
prop_prefix = initPropPrefix("tcp_");
mbinfo << myname << "(init): prop_prefix=" << prop_prefix << endl;
mbconf->prop_prefix = initPropPrefix(mbconf->s_field, "tcp_");
mbinfo << myname << "(init): prop_prefix=" << mbconf->prop_prefix << endl;
UniXML::iterator it(cnode);
......@@ -50,7 +50,7 @@ MBTCPMultiMaster::MBTCPMultiMaster( uniset::ObjectId objId, uniset::ObjectId shm
// Т.к. при "многоканальном" доступе к slave, смена канала должна происходит сразу после
// неудачной попытки запросов по одному из каналов, то ПЕРЕОПРЕДЕЛЯЕМ reopen, на channel-timeout..
int channelTimeout = conf->getArgPInt("--" + prefix + "-default-channel-timeout", it.getProp("channelTimeout"), default_timeout);
int channelTimeout = conf->getArgPInt("--" + prefix + "-default-channel-timeout", it.getProp("channelTimeout"), mbconf->default_timeout);
ptReopen.setTiming(channelTimeout);
UniXML::iterator it1(it);
......@@ -118,9 +118,9 @@ MBTCPMultiMaster::MBTCPMultiMaster( uniset::ObjectId objId, uniset::ObjectId shm
sinf->mbtcp = std::make_shared<ModbusTCPMaster>();
sinf->ptIgnoreTimeout.setTiming( it1.getPIntProp("ignore_timeout", ignore_timeout) );
sinf->recv_timeout = it1.getPIntProp("recv_timeout", recv_timeout);
sinf->aftersend_pause = it1.getPIntProp("aftersend_pause", aftersend_pause);
sinf->sleepPause_usec = it1.getPIntProp("sleepPause_msec", sleepPause_msec);
sinf->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);
......@@ -179,14 +179,7 @@ MBTCPMultiMaster::MBTCPMultiMaster( uniset::ObjectId objId, uniset::ObjectId shm
(*mbi)->setUse(true);
if( shm->isLocalwork() )
{
readConfiguration();
if( !noQueryOptimization )
rtuQueryOptimization(devices);
initDeviceList();
}
mbconf->loadConfig(conf->getConfXML(), conf->getXMLSensorsSection());
else
ic->addReadItem( sigc::mem_fun(this, &MBTCPMultiMaster::readItem) );
......@@ -197,11 +190,11 @@ MBTCPMultiMaster::MBTCPMultiMaster( uniset::ObjectId objId, uniset::ObjectId shm
// Т.к. при "многоканальном" доступе к slave, смена канала должна происходит сразу после
// неудачной попытки запросов по одному из каналов, то ПЕРЕОПРЕДЕЛЯЕМ reopen, на channel-timeout..
int tout = conf->getArgPInt("--" + prefix + "-default-channel-timeout", it.getProp("channelTimeout"), default_timeout);
int tout = conf->getArgPInt("--" + prefix + "-default-channel-timeout", it.getProp("channelTimeout"), mbconf->default_timeout);
ptReopen.setTiming(tout);
if( mblog->is_info() )
printMap(devices);
MBConfig::printMap(mbconf->devices);
}
// -----------------------------------------------------------------------------
MBTCPMultiMaster::~MBTCPMultiMaster()
......@@ -493,7 +486,7 @@ void MBTCPMultiMaster::poll_thread()
if( !isProcActive() )
break;
msleep(polltime);
msleep(mbconf->polltime);
}
}
// -----------------------------------------------------------------------------
......@@ -574,7 +567,6 @@ void MBTCPMultiMaster::check_thread()
void MBTCPMultiMaster::initIterators()
{
MBExchange::initIterators();
for( auto&& it : mblist )
shm->initIterator(it->respond_it);
}
......@@ -607,7 +599,7 @@ void MBTCPMultiMaster::initCheckConnectionParameters()
{
auto conf = uniset_conf();
bool initFromRegMap = ( findArgParam("--" + prefix + "-check-init-from-regmap", conf->getArgc(), conf->getArgv()) != -1 );
bool initFromRegMap = ( findArgParam("--" + mbconf->prefix + "-check-init-from-regmap", conf->getArgc(), conf->getArgv()) != -1 );
if( !initFromRegMap )
return;
......@@ -623,18 +615,16 @@ void MBTCPMultiMaster::initCheckConnectionParameters()
ModbusRTU::ModbusAddr checkAddr = { 0x00 };
ModbusRTU::ModbusData checkReg = { 0 };
if( devices.empty() )
if( mbconf->devices.empty() )
{
mbwarn << myname << "(init): devices list empty?!" << endl;
return;
}
// идём по устройствам
for( const auto& d : devices )
for( const auto& d : mbconf->devices )
{
checkAddr = d.second->mbaddr;
if( d.second->pollmap.empty() )
continue;
......
......@@ -4,7 +4,7 @@ libMBMaster_la_LIBADD = $(top_builddir)/lib/libUniSet2.la \
$(top_builddir)/extensions/lib/libUniSet2Extensions.la \
$(SIGC_LIBS)
libMBMaster_la_CXXFLAGS = -I$(top_builddir)/extensions/include -I$(top_builddir)/extensions/SharedMemory $(SIGC_CFLAGS)
libMBMaster_la_SOURCES = MBExchange.cc RTUStorage.cc
libMBMaster_la_SOURCES = MBExchange.cc MBConfig.cc RTUStorage.cc
bin_PROGRAMS = @PACKAGE@-mbtcpmaster @PACKAGE@-mbtcpmultimaster @PACKAGE@-rtuexchange @PACKAGE@-mtr-conv @PACKAGE@-rtu188-state @PACKAGE@-vtconv @PACKAGE@-mtr-setup @PACKAGE@-mtr-read
lib_LTLIBRARIES = libUniSet2MBTCPMaster.la libUniSet2RTU.la
......
......@@ -34,70 +34,62 @@ RTUExchange::RTUExchange(uniset::ObjectId objId, uniset::ObjectId shmId, const s
rs_pre_clean(false)
{
if( objId == DefaultObjectId )
throw uniset::SystemError("(RTUExchange): objId=-1?!! Use --" + prefix + "-name" );
throw uniset::SystemError("(RTUExchange): objId=-1?!! Use --" + mbconf->prefix + "-name" );
auto conf = uniset_conf();
// префикс для "свойств" - по умолчанию
prop_prefix = "";
mbconf->prop_prefix = "";
// если задано поле для "фильтрации"
// то в качестве префикса используем его
if( !s_field.empty() )
prop_prefix = s_field + "_";
if( !mbconf->s_field.empty() )
mbconf->prop_prefix = mbconf->s_field + "_";
// если "принудительно" задан префикс
// используем его.
{
string p("--" + prefix + "-set-prop-prefix");
string p("--" + mbconf->prefix + "-set-prop-prefix");
string v = conf->getArgParam(p, "");
if( !v.empty() && v[0] != '-' )
prop_prefix = v;
mbconf->prop_prefix = v;
// если параметр всё-таки указан, считаем, что это попытка задать "пустой" префикс
else if( findArgParam(p, conf->getArgc(), conf->getArgv()) != -1 )
prop_prefix = "";
mbconf->prop_prefix = "";
}
mbinfo << myname << "(init): prop_prefix=" << prop_prefix << endl;
mbinfo << myname << "(init): prop_prefix=" << mbconf->prop_prefix << endl;
UniXML::iterator it(cnode);
// ---------- init RS ----------
devname = conf->getArgParam("--" + prefix + "-dev", it.getProp("device"));
devname = conf->getArgParam("--" + mbconf->prefix + "-dev", it.getProp("device"));
if( devname.empty() )
throw uniset::SystemError(myname + "(RTUExchange): Unknown device..." );
string speed = conf->getArgParam("--" + prefix + "-speed", it.getProp("speed"));
string speed = conf->getArgParam("--" + mbconf->prefix + "-speed", it.getProp("speed"));
if( speed.empty() )
speed = "38400";
use485F = conf->getArgInt("--" + prefix + "-use485F", it.getProp("use485F"));
transmitCtl = conf->getArgInt("--" + prefix + "-transmit-ctl", it.getProp("transmitCtl"));
use485F = conf->getArgInt("--" + mbconf->prefix + "-use485F", it.getProp("use485F"));
transmitCtl = conf->getArgInt("--" + mbconf->prefix + "-transmit-ctl", it.getProp("transmitCtl"));
defSpeed = ComPort::getSpeed(speed);
sleepPause_msec = conf->getArgPInt("--" + prefix + "-sleepPause-usec", it.getProp("slepePause"), 100);
mbconf->sleepPause_msec = conf->getArgPInt("--" + mbconf->prefix + "-sleepPause-usec", it.getProp("slepePause"), 100);
rs_pre_clean = conf->getArgInt("--" + prefix + "-pre-clean", it.getProp("pre_clean"));
rs_pre_clean = conf->getArgInt("--" + mbconf->prefix + "-pre-clean", it.getProp("pre_clean"));
if( shm->isLocalwork() )
{
readConfiguration();
if( !noQueryOptimization )
rtuQueryOptimization(devices);
initDeviceList();
}
mbconf->loadConfig(conf->getConfXML(), conf->getXMLSensorsSection());
else
ic->addReadItem( sigc::mem_fun(this, &RTUExchange::readItem) );
initMB(false);
if( dlog()->is_info() )
printMap(devices);
MBConfig::printMap(mbconf->devices);
}
// -----------------------------------------------------------------------------
void RTUExchange::help_print( int argc, const char* const* argv )
......@@ -155,11 +147,11 @@ std::shared_ptr<ModbusClient> RTUExchange::initMB( bool reopen )
if( ic )
ic->logAgregator()->add(loga);
if( recv_timeout > 0 )
mbrtu->setTimeout(recv_timeout);
if( mbconf->recv_timeout > 0 )
mbrtu->setTimeout(mbconf->recv_timeout);
mbrtu->setSleepPause(sleepPause_msec);
mbrtu->setAfterSendPause(aftersend_pause);
mbrtu->setSleepPause(mbconf->sleepPause_msec);
mbrtu->setAfterSendPause(mbconf->aftersend_pause);
mbinfo << myname << "(init): dev=" << devname << " speed=" << ComPort::getSpeed( mbrtu->getSpeed() ) << endl;
}
......@@ -236,11 +228,11 @@ bool RTUExchange::poll()
bool allNotRespond = true;
ComPort::Speed s = mbrtu->getSpeed();
for( auto it1 : devices )
for( auto it1 : mbconf->devices )
{
auto d = it1.second;
if( d->mode_id != DefaultObjectId && d->mode == emSkipExchange )
if( d->mode_id != DefaultObjectId && d->mode == MBConfig::emSkipExchange )
continue;
if( d->speed != s )
......@@ -251,7 +243,7 @@ bool RTUExchange::poll()
d->prev_numreply.store(d->numreply);
if( d->dtype == MBExchange::dtRTU188 )
if( d->dtype == MBConfig::dtRTU188 )
{
if( !d->rtu188 )
continue;
......@@ -294,7 +286,7 @@ bool RTUExchange::poll()
{
try
{
if( d->dtype == RTUExchange::dtRTU || d->dtype == RTUExchange::dtMTR )
if( d->dtype == MBConfig::dtRTU || d->dtype == MBConfig::dtMTR )
{
if( rs_pre_clean )
mb->cleanupChannel();
......@@ -311,7 +303,7 @@ bool RTUExchange::poll()
dlog3 << myname << "(poll): FAILED ask addr=" << ModbusRTU::addr2str(d->mbaddr)
<< " reg=" << ModbusRTU::dat2str(it->second->mbreg)
<< " for sensors: ";
print_plist(dlog()->level3(), it->second->slst);
mbconf->print_plist(dlog()->level3(), it->second->slst);
dlog()->level3(false) << " err: " << ex << endl;
}
......@@ -329,7 +321,7 @@ bool RTUExchange::poll()
updateSM();
// check thresholds
for( auto&& t : thrlist )
for( auto&& t : mbconf->thrlist )
{
if( !isProcActive() )
return false;
......@@ -379,36 +371,3 @@ std::shared_ptr<RTUExchange> RTUExchange::init_rtuexchange(int argc, const char*
return make_shared<RTUExchange>(ID, icID, ic, prefix);
}
// -----------------------------------------------------------------------------
bool RTUExchange::initDeviceInfo( RTUDeviceMap& m, ModbusRTU::ModbusAddr a, UniXML::iterator& it )
{
if( !MBExchange::initDeviceInfo(m, a, it) )
return false;
auto d = m.find(a);
if( d == m.end() )
{
mbwarn << myname << "(initDeviceInfo): not found device for addr=" << ModbusRTU::addr2str(a) << endl;
return false;
}
string s = it.getProp("speed");
if( !s.empty() )
{
d->second->speed = ComPort::getSpeed(s);
if( d->second->speed == ComPort::ComSpeed0 )
{
d->second->speed = defSpeed;
mbcrit << myname << "(initDeviceInfo): Unknown speed=" << s <<
" for addr=" << ModbusRTU::addr2str(a) << endl;
return false;
}
}
else
d->second->speed = defSpeed;
return true;
}
// -----------------------------------------------------------------------------
......@@ -53,9 +53,7 @@ namespace uniset
virtual void step() override;
virtual bool poll() override;
virtual std::shared_ptr<ModbusClient> initMB( bool reopen = false ) override;
virtual bool initDeviceInfo( RTUDeviceMap& m, ModbusRTU::ModbusAddr a, UniXML::iterator& it ) override;
private:
RTUExchange();
......
......@@ -16,6 +16,7 @@
--mbtcp-persistent-connection 1 \
--ulog-add-levels system \
--mbtcp-run-logserver \
--mbtcp-log-add-levels any \
$*
#--mbtcp-log-add-levels level4,level3 \
......
......@@ -24,12 +24,6 @@ class MBTCPTestServer
replyVal = val;
}
inline void setIgnoreAddrMode( bool state )
{
if( sslot )
sslot->setIgnoreAddrMode(state);
}
void execute(); /*!< основной цикл работы */
void setLog( std::shared_ptr<DebugStream> dlog );
......
......@@ -134,6 +134,8 @@
<!-- undefined state -->
<item id="1070" mb="1" mbtype="rtu" mbaddr="0x01" mbreg="240" mbfunc="0x03" default="150" undefined_value="65535" breaklim="100" name="UndefinedState_FS" iotype="AI" textname="Тестирование неопределённого состояния"/>
<item id="1080" default="1080" name="Reload1_FS" iotype="AI" textname="Тестирование перезагрузки конфига"/>
<item id="10000" iotype="DI" name="TestMode_S" textname="Тестовый датчик"/>
</sensors>
......
<?xml version="1.0" encoding="utf-8"?>
<UNISETPLC xmlns:xi="http://www.w3.org/2001/XInclude">
<UserData/>
<!-- Общие(стартовые) параметры по UniSet -->
<UniSet>
<NameService host="localhost" port="2809"/>
<LocalNode name="LocalhostNode"/>
<RootSection name="UNISET_PLC"/>
<CountOfNet name="1"/>
<RepeatCount name="3"/>
<RepeatTimeoutMS name="50"/>
<WatchDogTime name="0"/>
<PingNodeTime name="0"/>
<AutoStartUpTime name="1"/>
<DumpStateTime name="10"/>
<SleepTickMS name="500"/>
<UniSetDebug levels="" name="ulog"/>
<ConfDir name="./"/>
<DataDir name="./"/>
<BinDir name="./"/>
<LogDir name="./"/>
<DocDir name="./"/>
<LockDir name="./"/>
<Services></Services>
</UniSet>
<dlog name="dlog"/>
<settings>
<SharedMemory name="SharedMemory" shmID="SharedMemory"/>
<MBTCPMaster1 name="MBTCPMaster1" exchangeModeID="MBTCPMaster_Mode_AS">
<DeviceList>
<item addr="0x01" timeout="1000" invert="1" respondSensor="Slave_Not_Respond_S" safemodeSensor="Slave1_SafemodeSensor_S" safemodeValue="42"/>
<item addr="0x02" timeout="1000" safemodeResetIfNotRespond="1"/>
<item addr="0x03" timeout="500"/>
</DeviceList>
</MBTCPMaster1>
<MBTCPMultiMaster1 name="MBTCPMultiMaster1" poll_time="200" reply_timeout="60">
<DeviceList>
<item addr="0x01" invert="1" respondSensor="Slave_Not_Respond_S" timeout="1000" safemodeSensor="Slave1_SafemodeSensor_S" safemodeValue="42" />
<item addr="0x02" timeout="1000" safemodeResetIfNotRespond="1"/>
</DeviceList>
<GateList>
<item ip="127.0.0.1" port="20053" recv_timeout="200" invert="1" respondSensor="Slave1_Not_Respond_S"/>
<item ip="127.0.0.1" port="20055" recv_timeout="200" invert="1" respondSensor="Slave2_Not_Respond_S"/>
</GateList>
</MBTCPMultiMaster1>
</settings>
<ObjectsMap idfromfile="1">
<!--
Краткие пояснения к полям секции 'sensors'
==========================================
node - узел на котором физически находится данный датчик
iotype - тип датчика
priority - приоритет сообщения об изменении данного датчика
textname - текстовое имя датчика
-->
<nodes port="2809">
<item id="3000" infserver="InfoServer" ip="127.0.0.1" name="LocalhostNode" textname="Локальный узел"/>
</nodes>
<!-- ************************ Датчики ********************** -->
<sensors name="Sensors">
<!-- reload conf -->
<item id="1080" mb="1" mbtype="rtu" mbaddr="0x03" mbreg="340" mbfunc="0x03" name="Reload1_FS" iotype="AI" textname="Тестирование перезагрузки конфига"/>
</sensors>
<thresholds/>
<controllers name="Controllers">
<item id="5000" name="SharedMemory"/>
</controllers>
<!-- ******************* Идентификаторы сервисов ***************** -->
<services name="Services">
</services>
<!-- ******************* Идентификаторы объектов ***************** -->
<objects name="UniObjects">
<item id="6000" name="TestProc"/>
<item id="6004" name="MBTCPMaster1"/>
<item id="6005" name="MBTCPMultiMaster1"/>
</objects>
</ObjectsMap>
<messages idfromfile="1" name="messages"/>
</UNISETPLC>
......@@ -15,6 +15,7 @@ using namespace uniset;
using namespace uniset::extensions;
// --------------------------------------------------------------------------
std::shared_ptr<SharedMemory> shm;
std::shared_ptr<MBTCPMaster> mbm;
// --------------------------------------------------------------------------
int main( int argc, const char* argv[] )
{
......@@ -46,15 +47,14 @@ int main( int argc, const char* argv[] )
if( !shm )
return 1;
auto mb = MBTCPMaster::init_mbmaster(argc, argv, shm->getId(), (apart ? nullptr : shm ));
mbm = MBTCPMaster::init_mbmaster(argc, argv, shm->getId(), (apart ? nullptr : shm ));
if( !mb )
if( !mbm )
return 1;
auto act = UniSetActivator::Instance();
act->add(shm);
act->add(mb);
act->add(mbm);
SystemMessage sm(SystemMessage::StartUp);
act->broadcast( sm.transport_msg() );
......
......@@ -16,7 +16,7 @@ cd -
--mbtcp-gateway-iaddr localhost \
--mbtcp-gateway-port 20048 \
--mbtcp-polltime 50 --mbtcp-recv-timeout 500
#--mbtcp-log-add-levels any
# --mbtcp-log-add-levels any
#--mbtcp-default-mbinit-ok 1
#--dlog-add-levels any
......
......@@ -8,6 +8,8 @@
#include "UniSetTypes.h"
#include "MBTCPTestServer.h"
#include "MBTCPMultiMaster.h"
#include "MBTCPMaster.h"
#include "UniSetActivator.h"
// -----------------------------------------------------------------------------
using namespace std;
using namespace uniset;
......@@ -15,7 +17,7 @@ using namespace uniset;
static ModbusRTU::ModbusAddr slaveADDR = 0x01; // conf->getArgInt("--mbs-my-addr");
static int port = 20048; // conf->getArgInt("--mbs-inet-port");
static string iaddr("127.0.0.1"); // conf->getArgParam("--mbs-inet-addr");
static unordered_set<ModbusRTU::ModbusAddr> vaddr = { slaveADDR, 0x02 };
static unordered_set<ModbusRTU::ModbusAddr> vaddr = { slaveADDR, 0x02, 0x03 };
static shared_ptr<MBTCPTestServer> mbs;
static shared_ptr<UInterface> ui;
static std::shared_ptr<SMInterface> smi;
......@@ -23,8 +25,10 @@ static ObjectId mbID = 6004; // MBTCPMaster1
static int polltime = 100; // conf->getArgInt("--mbtcp-polltime");
static ObjectId slaveNotRespond = 10; // Slave_Not_Respond_S
static const ObjectId exchangeMode = 11; // MBTCPMaster_Mode_AS
static const string confile2 = "mbmaster-test-configure2.xml";
// -----------------------------------------------------------------------------
extern std::shared_ptr<SharedMemory> shm;
extern std::shared_ptr<MBTCPMaster> mbm;
// -----------------------------------------------------------------------------
static void InitTest()
{
......@@ -81,6 +85,8 @@ static void InitTest()
msleep(2000);
CHECK( ui->getValue(slaveNotRespond) == 0 );
}
REQUIRE( mbm != nullptr );
}
// -----------------------------------------------------------------------------
TEST_CASE("MBTCPMaster: reconnect", "[modbus][mbmaster][mbtcpmaster]")
......@@ -508,8 +514,8 @@ TEST_CASE("MBTCPMaster: exchangeMode", "[modbus][exchangemode][mbmaster][mbtcpma
SECTION("WriteOnly")
{
// emWriteOnly=1, /*!< "только посылка данных" (работают только write-функции) */
ui->setValue(exchangeMode, MBExchange::emWriteOnly );
REQUIRE( ui->getValue(exchangeMode) == MBExchange::emWriteOnly );
ui->setValue(exchangeMode, MBConfig::emWriteOnly );
REQUIRE( ui->getValue(exchangeMode) == MBConfig::emWriteOnly );
SECTION("read")
{
......@@ -537,8 +543,8 @@ TEST_CASE("MBTCPMaster: exchangeMode", "[modbus][exchangemode][mbmaster][mbtcpma
SECTION("ReadOnly")
{
// emReadOnly=2, /*!< "только чтение" (работают только read-функции) */
ui->setValue(exchangeMode, MBExchange::emReadOnly );
REQUIRE( ui->getValue(exchangeMode) == MBExchange::emReadOnly );
ui->setValue(exchangeMode, MBConfig::emReadOnly );
REQUIRE( ui->getValue(exchangeMode) == MBConfig::emReadOnly );
SECTION("read")
{
......@@ -566,8 +572,8 @@ TEST_CASE("MBTCPMaster: exchangeMode", "[modbus][exchangemode][mbmaster][mbtcpma
SECTION("SkipSaveToSM")
{
// emSkipSaveToSM=3, /*!< не писать данные в SM (при этом работают и read и write функции */
ui->setValue(exchangeMode, MBExchange::emSkipSaveToSM );
REQUIRE( ui->getValue(exchangeMode) == MBExchange::emSkipSaveToSM );
ui->setValue(exchangeMode, MBConfig::emSkipSaveToSM );
REQUIRE( ui->getValue(exchangeMode) == MBConfig::emSkipSaveToSM );
SECTION("read")
{
......@@ -592,8 +598,8 @@ TEST_CASE("MBTCPMaster: exchangeMode", "[modbus][exchangemode][mbmaster][mbtcpma
SECTION("SkipExchange")
{
// emSkipExchange=4 /*!< отключить обмен */
ui->setValue(exchangeMode, MBExchange::emSkipExchange );
REQUIRE( ui->getValue(exchangeMode) == MBExchange::emSkipExchange );
ui->setValue(exchangeMode, MBConfig::emSkipExchange );
REQUIRE( ui->getValue(exchangeMode) == MBConfig::emSkipExchange );
SECTION("read")
{
......@@ -613,8 +619,8 @@ TEST_CASE("MBTCPMaster: exchangeMode", "[modbus][exchangemode][mbmaster][mbtcpma
{
msleep(1100);
CHECK( ui->getValue(slaveNotRespond) == 1 );
ui->setValue(exchangeMode, MBExchange::emNone );
REQUIRE( ui->getValue(exchangeMode) == MBExchange::emNone );
ui->setValue(exchangeMode, MBConfig::emNone );
REQUIRE( ui->getValue(exchangeMode) == MBConfig::emNone );
msleep(1100);
CHECK( ui->getValue(slaveNotRespond) == 0 );
}
......@@ -766,6 +772,23 @@ TEST_CASE("MBTCPMaster: udefined value", "[modbus][undefined][mbmaster][mbtcpmas
REQUIRE( ui->getValue(1070) == 120 );
}
// -----------------------------------------------------------------------------
TEST_CASE("MBTCPMaster: reload config", "[modbus][reload][mbmaster][mbtcpmaster]")
{
InitTest();
mbs->setReply(160);
msleep(polltime + 200);
REQUIRE( ui->getValue(1070) == 160 );
REQUIRE( ui->getValue(1080) == 1080 );
// add new mbreg
REQUIRE( mbm->reconfigure(confile2) );
msleep(polltime + 600);
REQUIRE( ui->getValue(1080) == 160 );
}
// -----------------------------------------------------------------------------
#if 0
// -----------------------------------------------------------------------------
static bool init_iobase( IOBase* ib, const std::string& sensor )
......
......@@ -187,7 +187,7 @@ class UObject_SK:
virtual void httpGetUserData( Poco::JSON::Object::Ptr& jdata ) {} /*!< для пользовательских данных в httpGet() */
virtual Poco::JSON::Object::Ptr httpDumpIO();
virtual Poco::JSON::Object::Ptr httpRequestLog( const Poco::URI::QueryParameters& p );
virtual Poco::JSON::Object::Ptr request_conf_set( const std::string& req, const Poco::URI::QueryParameters& p );
virtual Poco::JSON::Object::Ptr request_conf_set( const std::string& req, const Poco::URI::QueryParameters& p ) override;
#endif
// Выполнение очередного шага программы
......
......@@ -705,12 +705,47 @@ Poco::JSON::Object::Ptr UObject_SK::httpRequestLog( const Poco::URI::QueryParame
Poco::JSON::Object::Ptr UObject_SK::request_conf_set( const std::string& req, const Poco::URI::QueryParameters& params )
{
Poco::JSON::Object::Ptr jret = new Poco::JSON::Object();
Poco::JSON::Array::Ptr jupdated = uniset::json::make_child_array(jret, "updated");
for( const auto& p: params )
{
if( p.first == "sleep_msec" )
{
int val = uni_atoi(p.second);
if( val > 0 )
{
sleep_msec = uni_atoi(p.second);
jupdated->add(p.first);
}
continue;
}
if( p.first == "resetMsgTime" )
{
int val = uni_atoi(p.second);
if( val > 0 )
{
resetMsgTime = uni_atoi(p.second);
jupdated->add(p.first);
}
continue;
}
if( p.first == "forceOut" )
{
int val = uni_atoi(p.second);
if( val > 0 )
{
forceOut = uni_atoi(p.second);
jupdated->add(p.first);
}
continue;
}
}
jret->set("Result","OK");
jret->set("Result", (jupdated->size() > 0 ? "OK" : "FAIL") );
return jret;
}
#endif
......
......@@ -60,9 +60,6 @@ namespace uniset
/*! текущее количество подключений */
size_t getCountSessions() const noexcept;
void setIgnoreAddrMode( bool st );
bool getIgnoreAddrMode() const noexcept;
// Сбор статистики по соединениям...
struct SessionInfo
{
......
......@@ -81,16 +81,6 @@ namespace uniset
return sessCount;
}
// -------------------------------------------------------------------------
void ModbusTCPServer::setIgnoreAddrMode(bool st)
{
ignoreAddr = st;
}
// -------------------------------------------------------------------------
bool ModbusTCPServer::getIgnoreAddrMode() const noexcept
{
return ignoreAddr;
}
// -------------------------------------------------------------------------
void ModbusTCPServer::setSessionTimeout( timeout_t msec )
{
sessTimeout = msec;
......
./cov-int/emit/pvbook.localdomain/config/130ac2dc34760443befdd3f2a25f8d40/g++cc-config-0/coverity-compiler-compat.h
./cov-int/emit/pvbook.localdomain/config/130ac2dc34760443befdd3f2a25f8d40/gcc-config-0/coverity-compiler-compat.h
./cov-int/emit/pvbook.localdomain/config/130ac2dc34760443befdd3f2a25f8d40/g++cc-config-0/coverity-macro-compat.h
./cov-int/emit/pvbook.localdomain/config/130ac2dc34760443befdd3f2a25f8d40/gcc-config-0/coverity-macro-compat.h
./cov-int/emit/pvbook.localdomain/config/130ac2dc34760443befdd3f2a25f8d40/g++cc-config-1/coverity-compiler-compat.h
./cov-int/emit/pvbook.localdomain/config/130ac2dc34760443befdd3f2a25f8d40/g++cc-config-1/coverity-macro-compat.h
./cov-int/emit/pvbook.localdomain/config/130ac2dc34760443befdd3f2a25f8d40/g++-config-0/coverity-compiler-compat.h
./cov-int/emit/pvbook.localdomain/config/130ac2dc34760443befdd3f2a25f8d40/g++-config-0/coverity-macro-compat.h
./cov-int/emit/pvbook.localdomain/config/130ac2dc34760443befdd3f2a25f8d40/g++-config-1/coverity-compiler-compat.h
./cov-int/emit/pvbook.localdomain/config/130ac2dc34760443befdd3f2a25f8d40/g++-config-1/coverity-macro-compat.h
./cov-int/emit/pvbook.localdomain/config/30c6834cc1e9802742e33de4efa3917c/g++cc-config-0/coverity-compiler-compat.h
./cov-int/emit/pvbook.localdomain/config/30c6834cc1e9802742e33de4efa3917c/g++cc-config-0/coverity-macro-compat.h
./cov-int/emit/pvbook.localdomain/config/30c6834cc1e9802742e33de4efa3917c/g++-config-0/coverity-compiler-compat.h
./cov-int/emit/pvbook.localdomain/config/30c6834cc1e9802742e33de4efa3917c/g++-config-0/coverity-macro-compat.h
./cov-int/emit/pvbook.localdomain/config/4d02eb73c555e8f07e4124fe33c80de2/g++cc-config-0/coverity-compiler-compat.h
./cov-int/emit/pvbook.localdomain/config/4d02eb73c555e8f07e4124fe33c80de2/g++cc-config-0/coverity-macro-compat.h
./cov-int/emit/pvbook.localdomain/config/4d02eb73c555e8f07e4124fe33c80de2/g++-config-0/coverity-compiler-compat.h
./cov-int/emit/pvbook.localdomain/config/4d02eb73c555e8f07e4124fe33c80de2/g++-config-0/coverity-macro-compat.h
./cov-int/emit/pvbook.localdomain/config/4db70a7a9c958929fa315f4c9243d800/g++cc-config-0/coverity-compiler-compat.h
./cov-int/emit/pvbook.localdomain/config/4db70a7a9c958929fa315f4c9243d800/g++cc-config-0/coverity-macro-compat.h
./cov-int/emit/pvbook.localdomain/config/4db70a7a9c958929fa315f4c9243d800/g++-config-0/coverity-compiler-compat.h
./cov-int/emit/pvbook.localdomain/config/4db70a7a9c958929fa315f4c9243d800/g++-config-0/coverity-macro-compat.h
./docs/Makefile.am
./extensions/Backend-OpenTSDB/BackendOpenTSDB.cc
./extensions/Backend-OpenTSDB/BackendOpenTSDB.h
......@@ -132,6 +110,8 @@
./extensions/Makefile.am
./extensions/ModbusMaster/main.cc
./extensions/ModbusMaster/Makefile.am
./extensions/ModbusMaster/MBConfig.cc
./extensions/ModbusMaster/MBConfig.h
./extensions/ModbusMaster/MBExchange.cc
./extensions/ModbusMaster/MBExchange.h
./extensions/ModbusMaster/mb-perf-test.cc
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment