Commit bbdd555e authored by Pavel Vainerman's avatar Pavel Vainerman

(ModbusMaster): перенёс реализацию ModbusMaster TCP и RTU в один каталог ModbusMaster

parent df497744
......@@ -179,13 +179,11 @@ AC_CONFIG_FILES([Makefile
extensions/DBServer-MySQL/libUniSetMySQL.pc
extensions/IOControl/Makefile
extensions/IOControl/libUniSetIOControl.pc
extensions/RTUExchange/Makefile
extensions/RTUExchange/libUniSetRTU.pc
extensions/ModbusMaster/Makefile
extensions/ModbusMaster/libUniSetRTU.pc
extensions/ModbusMaster/libUniSetMBTCPMaster.pc
extensions/ModbusSlave/Makefile
extensions/ModbusSlave/libUniSetMBSlave.pc
extensions/MBTCPMaster/Makefile
extensions/MBTCPMaster/libUniSetMBTCPMaster.pc
extensions/LogicProcessor/Makefile
extensions/LogicProcessor/libUniSetLogicProcessor.pc
extensions/SMViewer/Makefile
......
// -----------------------------------------------------------------------------
#include <cmath>
#include <limits>
#include <sstream>
#include <Exceptions.h>
#include <extensions/Extensions.h>
#include "MBTCPMaster.h"
// -----------------------------------------------------------------------------
using namespace std;
using namespace UniSetTypes;
using namespace UniSetExtensions;
// -----------------------------------------------------------------------------
MBTCPMaster::MBTCPMaster( UniSetTypes::ObjectId objId, UniSetTypes::ObjectId shmId,
SharedMemory* ic, const std::string prefix ):
MBExchange(objId,shmId,ic,prefix),
force_disconnect(true),
mbtcp(0),
pollThread(0)
{
// cout << "$ $" << endl;
prop_prefix = "tcp_";
if( objId == DefaultObjectId )
throw UniSetTypes::SystemError("(MBTCPMaster): objId=-1?!! Use --" + prefix + "-name" );
UniXML_iterator it(cnode);
// ---------- init MBTCP ----------
string pname("--" + prefix + "-gateway-iaddr");
iaddr = conf->getArgParam(pname,it.getProp("gateway_iaddr"));
if( iaddr.empty() )
throw UniSetTypes::SystemError(myname+"(MBMaster): Unknown inet addr...(Use: " + pname +")" );
string tmp("--" + prefix + "-gateway-port");
port = conf->getArgInt(tmp,it.getProp("gateway_port"));
if( port <= 0 )
throw UniSetTypes::SystemError(myname+"(MBMaster): Unknown inet port...(Use: " + tmp +")" );
force_disconnect = conf->getArgInt("--" + prefix + "-persistent-connection",it.getProp("persistent_connection")) ? false : true;
dlog[Debug::INFO] << myname << "(init): persisten-connection=" << (!force_disconnect) << endl;
recv_timeout = conf->getArgPInt("--" + prefix + "-recv-timeout",it.getProp("recv_timeout"), 500);
int tout = conf->getArgPInt("--" + prefix + "-timeout",it.getProp("timeout"), 5000);
ptTimeout.setTiming(tout);
if( shm->isLocalwork() )
{
readConfiguration();
rtuQueryOptimization(rmap);
initDeviceList();
}
else
ic->addReadItem( sigc::mem_fun(this,&MBTCPMaster::readItem) );
pollThread = new ThreadCreator<MBTCPMaster>(this, &MBTCPMaster::poll_thread);
}
// -----------------------------------------------------------------------------
MBTCPMaster::~MBTCPMaster()
{
for( MBTCPMaster::RTUDeviceMap::iterator it1=rmap.begin(); it1!=rmap.end(); ++it1 )
{
RTUDevice* d(it1->second);
for( MBTCPMaster::RegMap::iterator it=d->regmap.begin(); it!=d->regmap.end(); ++it )
delete it->second;
delete it1->second;
}
delete pollThread;
delete mbtcp;
}
// -----------------------------------------------------------------------------
ModbusClient* MBTCPMaster::initMB( bool reopen )
{
if( mbtcp )
{
if( !reopen )
return mbtcp;
delete mbtcp;
mb = 0;
mbtcp = 0;
}
try
{
ost::Thread::setException(ost::Thread::throwException);
mbtcp = new ModbusTCPMaster();
ost::InetAddress ia(iaddr.c_str());
mbtcp->connect(ia,port);
mbtcp->setForceDisconnect(force_disconnect);
if( recv_timeout > 0 )
mbtcp->setTimeout(recv_timeout);
mbtcp->setSleepPause(sleepPause_usec);
dlog[Debug::INFO] << myname << "(init): ipaddr=" << iaddr << " port=" << port << endl;
if( dlog.debugging(Debug::LEVEL9) )
mbtcp->setLog(dlog);
}
catch( ModbusRTU::mbException& ex )
{
dlog[Debug::WARN] << "(init): " << ex << endl;
}
catch(...)
{
if( mbtcp )
delete mbtcp;
mb = 0;
mbtcp = 0;
}
mb = mbtcp;
return mbtcp;
}
// -----------------------------------------------------------------------------
void MBTCPMaster::sysCommand( UniSetTypes::SystemMessage *sm )
{
MBExchange::sysCommand(sm);
if( sm->command == SystemMessage::StartUp )
pollThread->start();
}
// -----------------------------------------------------------------------------
void MBTCPMaster::step()
{
updateRespondSensors();
MBExchange::step();
}
// -----------------------------------------------------------------------------
void MBTCPMaster::updateRespondSensors()
{
bool tcpIsTimeout = false;
{
uniset_mutex_lock l(tcpMutex);
tcpIsTimeout = pollActivated && ptTimeout.checkTime();
}
if( dlog.debugging(Debug::LEVEL4) )
dlog[Debug::LEVEL4] << myname << ": tcpTimeout=" << tcpIsTimeout << endl;
for( MBTCPMaster::RTUDeviceMap::iterator it1=rmap.begin(); it1!=rmap.end(); ++it1 )
{
RTUDevice* d(it1->second);
if( tcpIsTimeout )
d->resp_real = false;
if( dlog.debugging(Debug::LEVEL4) )
{
dlog[Debug::LEVEL4] << myname << ": check respond addr=" << ModbusRTU::addr2str(d->mbaddr)
<< " respond_id=" << d->resp_id
<< " real=" << d->resp_real
<< " state=" << d->resp_state
<< endl;
}
if( d->checkRespond() && d->resp_id != DefaultObjectId )
{
try
{
bool set = d->resp_invert ? !d->resp_state : d->resp_state;
shm->localSaveState(d->resp_dit,d->resp_id,set,getId());
}
catch( Exception& ex )
{
dlog[Debug::CRIT] << myname << "(step): (respond) " << ex << std::endl;
}
}
}
}
// -----------------------------------------------------------------------------
void MBTCPMaster::poll_thread()
{
{
uniset_mutex_lock l(pollMutex,300);
ptTimeout.reset();
}
while( checkProcActive() )
{
try
{
if( sidExchangeMode != DefaultObjectId && force_out )
exchangeMode = shm->localGetValue(aitExchangeMode,sidExchangeMode);
}
catch(...){}
try
{
poll();
}
catch(...){}
if( !checkProcActive() )
break;
msleep(polltime);
}
}
// -----------------------------------------------------------------------------
bool MBTCPMaster::RTUDevice::checkRespond()
{
bool prev = resp_state;
if( resp_ptTimeout.getInterval() <= 0 )
{
resp_state = resp_real;
return (prev != resp_state);
}
if( resp_trTimeout.hi(resp_state && !resp_real) )
resp_ptTimeout.reset();
if( resp_real )
resp_state = true;
else if( resp_state && !resp_real && resp_ptTimeout.checkTime() )
resp_state = false;
// если ещё не инициализировали значение в SM
// то возвращаем true, чтобы оно принудительно сохранилось
if( !resp_init )
{
resp_state = resp_real;
resp_init = true;
prev = resp_state;
return true;
}
return ( prev != resp_state );
}
// -----------------------------------------------------------------------------
void MBTCPMaster::sigterm( int signo )
{
dlog[Debug::WARN] << myname << ": ********* SIGTERM(" << signo <<") ********" << endl;
setProcActive(false);
/*! \todo Доделать выставление безопасного состояния на выходы.
И нужно ли это. Ведь может не хватить времени на "обмен"
*/
// выставление безопасного состояния на выходы....
/*
RSMap::iterator it=rsmap.begin();
for( ; it!=rsmap.end(); ++it )
{
// if( it->stype!=UniversalIO::DigitalOutput && it->stype!=UniversalIO::AnalogOutput )
// continue;
if( it->safety == NoSafetyState )
continue;
try
{
}
catch( UniSetTypes::Exception& ex )
{
dlog[Debug::WARN] << myname << "(sigterm): " << ex << std::endl;
}
catch(...){}
}
*/
UniSetObject_LT::sigterm(signo);
}
// ------------------------------------------------------------------------------------------
void MBTCPMaster::help_print( int argc, const char* const* argv )
{
cout << "Default: prefix='mbtcp'" << endl;
MBExchange::help_print(argc,argv);
// ---------- init MBTCP ----------
// cout << "--prefix-sm-ready-timeout - время на ожидание старта SM" << endl;
cout << " Настройки протокола TCP: " << endl;
cout << "--prefix-gateway hostname,IP - IP опрашиваемого узла" << endl;
cout << "--prefix-gateway-port num - port на опрашиваемом узле" << endl;
cout << "--prefix-recv-timeout msec - Таймаут на приём одного сообщения" << endl;
cout << "--prefix-timeout msec - Таймаут для определения отсутсвия соединения" << endl;
cout << "--prefix-persistent-connection 0,1 - Не закрывать соединение на каждом цикле опроса" << endl;
}
// -----------------------------------------------------------------------------
MBTCPMaster* MBTCPMaster::init_mbmaster( int argc, const char* const* argv,
UniSetTypes::ObjectId icID, SharedMemory* ic,
const std::string prefix )
{
string name = conf->getArgParam("--" + prefix + "-name","MBTCPMaster1");
if( name.empty() )
{
dlog[Debug::CRIT] << "(MBTCPMaster): Не задан name'" << endl;
return 0;
}
ObjectId ID = conf->getObjectID(name);
if( ID == UniSetTypes::DefaultObjectId )
{
dlog[Debug::CRIT] << "(MBTCPMaster): идентификатор '" << name
<< "' не найден в конф. файле!"
<< " в секции " << conf->getObjectsSection() << endl;
return 0;
}
dlog[Debug::INFO] << "(MBTCPMaster): name = " << name << "(" << ID << ")" << endl;
return new MBTCPMaster(ID,icID,ic,prefix);
}
// -----------------------------------------------------------------------------
void MBTCPMaster::execute()
{
no_extimer = true;
try
{
askTimer(tmExchange,0);
}
catch(...){}
initMB(false);
while(1)
{
try
{
step();
}
catch( Exception& ex )
{
dlog[Debug::CRIT] << myname << "(execute): " << ex << std::endl;
}
catch(...)
{
dlog[Debug::CRIT] << myname << "(execute): catch ..." << endl;
}
msleep(polltime);
}
}
// -----------------------------------------------------------------------------
#ifndef _MBTCPMaster_H_
#define _MBTCPMaster_H_
// -----------------------------------------------------------------------------
#include <ostream>
#include <string>
#include <map>
#include <vector>
#include "IONotifyController.h"
#include "UniSetObject_LT.h"
#include "modbus/ModbusTCPMaster.h"
#include "PassiveTimer.h"
#include "Trigger.h"
#include "Mutex.h"
#include "Calibration.h"
#include "SMInterface.h"
#include "SharedMemory.h"
#include "IOBase.h"
#include "VTypes.h"
#include "MTR.h"
// -----------------------------------------------------------------------------
/*!
\page page_ModbusTCP Реализация ModbusTCP master
- \ref sec_MBTCP_Comm
- \ref sec_MBTCP_Conf
- \ref sec_MBTCP_ConfList
\section sec_MBTCP_Comm Общее описание ModbusTCP master
Класс реализует процесс обмена (опрос/запись) с RTU-устройствами,
через TCP-шлюз. Список регистров с которыми работает процесс задаётся в конфигурационном файле
в секции \b <sensors>. см. \ref sec_MBTCP_Conf
\section sec_MBTCP_Conf Конфигурирование ModbusTCP master
Конфигурирование процесса осуществляется либо параметрами командной строки либо
через настроечную секцию.
\par Секция с настройками
При своём старте, в конфигурационном файле ищётся секция с названием объекта,
в которой указываются настроечные параметры по умолчанию.
Пример:
\code
<MBMaster1 name="MBMaster1" gateway_iaddr="127.0.0.1" gateway_port="30000" polltime="200">
<DeviceList>
<item addr="0x01" respondSensor="RTU1_Not_Respond_FS" timeout="2000" invert="1"/>
<item addr="0x02" respondSensor="RTU2_Respond_FS" timeout="2000" invert="0"/>
</DeviceList>
</MBMaster1>
\endcode
Секция <DeviceList> позволяет задать параметры обмена с конкретным RTU-устройством.
- \b addr - адрес устройства для которого, задаются параметры
- \b timeout msec - таймаут, для определения отсутствия связи
- \b invert - инвертировать логику. По умолчанию датчик выставляется в "1" при \b наличии связи.
- \b respondSensor - идентификатор датчика связи.
- \b ask_every_reg - 1 - опрашивать ВСЕ регистры подряд, не обращая внимания на timeout. По умолчанию - "0" Т.е. опрос устройства (на текущем шаге цикла опроса), прерывается на первом же регистре, при опросе которого возникнет timeout.
\par Параметры запуска
При создании объекта в конструкторе передаётся префикс для определения параметров командной строки.
По умолчанию \b xxx="mbtcp".
Далее приведены основные параметры:
\b --xxx-name ID - идентификатор процесса.
IP-адрес шлюза задаётся параметром в конфигурационном файле \b gateway_iaddr или
параметром командной строки \b --xxx-gateway-iaddr.
Порт задаётся в конфигурационном файле параметром \b gateway_port или
параметром командной строки \b --xxx-gateway-port. По умолчанию используется порт \b 502.
\b --xxx-recv-timeout или \b recv_timeout msec - таймаут на приём сообщений. По умолчанию 2000 мсек.
\b --xxx-all-timeout или \b all_timeout msec - таймаут на определение отсутсвия связи
(после этого идёт попытка реинициализировать соединение)
\b --xxx-no-query-optimization или \b no_query_optimization - [1|0] отключить оптимизацию запросов
Оптимизация заключается в том, что регистры идущие подряд автоматически запрашиваются/записываются одним запросом.
В связи с чем, функция указанная в качестве \b mbfunc игнорируется и подменяется на работающую с многими регистрами.
\b --xxx-poll-time или \b poll_time msec - пауза между опросами. По умолчанию 100 мсек.
\b --xxx-initPause или \b initPause msec - пауза перед началом работы, после активации. По умолчанию 50 мсек.
\b --xxx-force или \b force [1|0]
- 1 - перечитывать значения входов из SharedMemory на каждом цикле
- 0 - обновлять значения только по изменению
\b --xxx-persistent-connection или \b persistent_connection - НЕ закрывать соединение после каждого запроса.
\b --xxx-force-out или \b force_out [1|0]
- 1 - перечитывать значения выходов из SharedMemory на каждом цикле
- 0 - обновлять значения только по изменению
\b --xxx-reg-from-id или \b reg_from_id [1|0]
- 1 - в качестве регистра использовать идентификатор датчика
- 0 - регистр брать из поля tcp_mbreg
\b --xxx-heartbeat-id или \b heartbeat_id ID - идентификатор датчика "сердцебиения" (см. \ref sec_SM_HeartBeat)
\b --xxx-heartbeat-max или \b heartbeat_max val - сохраняемое значение счётчика "сердцебиения".
\b --xxx-activate-timeout msec . По умолчанию 2000. - время ожидания готовности SharedMemory к работе.
\section sec_MBTCP_ConfList Конфигурирование списка регистров для ModbusTCP master
Конфигурационные параметры задаются в секции <sensors> конфигурационного файла.
Список обрабатываемых регистров задаётся при помощи двух параметров командной строки
\b --xxx-filter-field - задаёт фильтрующее поле для датчиков
\b --xxx-filter-value - задаёт значение фильтрующего поля. Необязательный параметр.
\b --xxx-statistic-sec sec - при наличии выведет кол-во посланных запросов за этот промежуток времени.
Если параметры не заданы, будет произведена попытка загрузить все датчики, у которых
присутствуют необходимые настроечные параметры.
\warning Если в результате список будет пустым, процесс завершает работу.
Пример конфигурационных параметров:
\code
<sensors name="Sensors">
...
<item name="MySensor_S" textname="my sesnsor" iotype="DI"
tcp_mbtype="rtu" tcp_mbaddr="0x01" tcp_mbfunc="0x04" tcp_mbreg="0x02" my_tcp="1"
/>
...
</sensors>
\endcode
К основным параметрам относятся следующие:
- \b tcp_mbtype - [rtu] - пока едиственный разрешённый тип.
- \b tcp_mbaddr - адрес RTU-устройства.
- \b tcp_mbreg - запрашиваемый/записываемый регистр.
- \b tcp_mbfunc - [0x1,0x2,0x3,...] функция опроса/записи. Разрешённые см. ModbusRTU::SlaveFunctionCode.
Помимо этого можно задавать следующие параметры:
- \b tcp_vtype - тип переменной. см VTypes::VType.
- \b tcp_rawdata - [0|1] - игнорировать или нет параметры калибровки
- \b tcp_iotype - [DI,DO,AI,AO] - переназначить тип датчика. По умолчанию используется поле iotype.
- \b tcp_nbit - номер бита в слове. Используется для DI,DO в случае когда для опроса используется
функция читающая слова (03
- \b tcp_nbyte - [1|2] номер байта. Используется если tcp_vtype="byte".
- \b tcp_mboffset - "сдвиг"(может быть отрицательным) при опросе/записи.
Т.е. фактически будет опрошен/записан регистр "mbreg+mboffset".
Для инициализации "выходов" (регистров которые пишутся) можно использовать поля:
- \b tcp_preinit - [0|1] считать регистр перед использованием (при запуске процесса)
- \b tcp_init_mbfunc - Номер функции для инициализации. Если не указана, будет определена автоматически исходя из tcp_mbfunc.
- \b tcp_init_mbreg - Номер регистра откуда считывать значение для инициализации. Если это поле не указано используется tcp_mbreg.
Если указано tcp_preinit="1", то прежде чем начать писать регистр в устройство, будет произведено его чтение.
По умолчанию все "записываемые" регистры инициализируются значением из SM. Т.е. пока не будет первый раз считано значение из SM,
регистры в устройство писатся не будут. Чтобы отключить это поведение, можно указать параметр
- \b tcp_sm_initOK - [0|1] Игнорировать начальную инициализацию из SM (сразу писать в устройство)
При этом будет записывыться значение "default".
\warning Регистр должен быть уникальным. И может повторятся только если указан параметр \a nbit или \a nbyte.
*/
// -----------------------------------------------------------------------------
/*!
Реализация Modbus TCP Master для обмена с многими ModbusRTU устройствами
через один modbus tcp шлюз.
*/
class MBTCPMaster:
public UniSetObject_LT
{
public:
MBTCPMaster( UniSetTypes::ObjectId objId, UniSetTypes::ObjectId shmID, SharedMemory* ic=0,
const std::string prefix="mbtcp" );
virtual ~MBTCPMaster();
/*! глобальная функция для инициализации объекта */
static MBTCPMaster* init_mbmaster( int argc, const char* const* argv,
UniSetTypes::ObjectId shmID, SharedMemory* ic=0,
const std::string prefix="mbtcp" );
/*! глобальная функция для вывода help-а */
static void help_print( int argc, const char* const* argv );
void execute();
static const int NoSafetyState=-1;
enum Timer
{
tmExchange
};
enum DeviceType
{
dtUnknown, /*!< неизвестный */
dtRTU, /*!< RTU (default) */
dtMTR /*!< MTR (DEIF) */
};
static DeviceType getDeviceType( const std::string dtype );
friend std::ostream& operator<<( std::ostream& os, const DeviceType& dt );
// -------------------------------------------------------------------------------
struct RTUDevice;
struct RegInfo;
struct RSProperty:
public IOBase
{
// only for RTU
short nbit; /*!< bit number) */
VTypes::VType vType; /*!< type of value */
short rnum; /*!< count of registers */
short nbyte; /*!< byte number (1-2) */
RSProperty():
nbit(-1),vType(VTypes::vtUnknown),
rnum(VTypes::wsize(VTypes::vtUnknown)),
nbyte(0),reg(0)
{}
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, PList& p );
typedef unsigned long RegID;
typedef std::map<RegID,RegInfo*> RegMap;
struct RegInfo
{
RegInfo():
mbval(0),mbreg(0),mbfunc(ModbusRTU::fnUnknown),
id(0),dev(0),mtrType(MTR::mtUnknown),
q_num(0),q_count(1),mb_initOK(true),sm_initOK(true)
{}
ModbusRTU::ModbusData mbval;
ModbusRTU::ModbusData mbreg; /*!< регистр */
ModbusRTU::SlaveFunctionCode mbfunc; /*!< функция для чтения/записи */
PList slst;
RegID id;
RTUDevice* dev;
// only for MTR
MTR::MTRType mtrType; /*!< тип регистра (согласно спецификации на MTR) */
// optimization
int q_num; /*!< number in query */
int q_count; /*!< count registers for query */
RegMap::iterator rit;
// начальная инициалиазция для "записываемых" регистров
// Механизм:
// Если tcp_preinit="1", то сперва будет сделано чтение значения из устройства.
// при этом флаг mb_init=false пока не пройдёт успешной инициализации
// Если tcp_preinit="0", то флаг mb_init сразу выставляется в true.
bool mb_initOK; /*!< инициализировалось ли значение из устройства */
// Флаг sm_init означает, что писать в устройство нельзя, т.к. значение в "карте регистров"
// ещё не инициализировано из SM
bool sm_initOK; /*!< инициализировалось ли значение из SM */
};
friend std::ostream& operator<<( std::ostream& os, RegInfo& r );
struct RTUDevice
{
RTUDevice():
respnond(false),
mbaddr(0),
dtype(dtUnknown),
resp_id(UniSetTypes::DefaultObjectId),
resp_state(false),
resp_invert(false),
resp_real(false),
resp_init(false),
ask_every_reg(false)
{
resp_trTimeout.change(false);
}
bool respnond;
ModbusRTU::ModbusAddr mbaddr; /*!< адрес устройства */
RegMap regmap;
DeviceType dtype; /*!< тип устройства */
UniSetTypes::ObjectId resp_id;
IOController::DIOStateList::iterator resp_dit;
PassiveTimer resp_ptTimeout;
Trigger resp_trTimeout;
bool resp_state;
bool resp_invert;
bool resp_real;
bool resp_init;
bool ask_every_reg;
// return TRUE if state changed
bool checkRespond();
};
friend std::ostream& operator<<( std::ostream& os, RTUDevice& d );
typedef std::map<ModbusRTU::ModbusAddr,RTUDevice*> RTUDeviceMap;
friend std::ostream& operator<<( std::ostream& os, RTUDeviceMap& d );
void printMap(RTUDeviceMap& d);
// ----------------------------------
static RegID genRegID( const ModbusRTU::ModbusData r, const int fn );
protected:
struct InitRegInfo
{
InitRegInfo():
dev(0),mbreg(0),
mbfunc(ModbusRTU::fnUnknown),
initOK(false),ri(0)
{}
RSProperty p;
RTUDevice* dev;
ModbusRTU::ModbusData mbreg;
ModbusRTU::SlaveFunctionCode mbfunc;
bool initOK;
RegInfo* ri;
};
typedef std::list<InitRegInfo> InitList;
void firstInitRegisters();
bool preInitRead( InitList::iterator& p );
bool initSMValue( ModbusRTU::ModbusData* data, int count, RSProperty* p );
bool allInitOK;
RTUDeviceMap rmap;
InitList initRegList; /*!< список регистров для инициализации */
ModbusTCPMaster* mb;
UniSetTypes::uniset_mutex mbMutex;
std::string iaddr;
// ost::InetAddress* ia;
int port;
int recv_timeout;
xmlNode* cnode;
std::string s_field;
std::string s_fvalue;
SMInterface* shm;
void step();
void poll();
bool pollRTU( RTUDevice* dev, RegMap::iterator& it );
void updateSM();
void updateRTU(RegMap::iterator& it);
void updateMTR(RegMap::iterator& it);
void updateRSProperty( RSProperty* p, bool write_only=false );
virtual void processingMessage( UniSetTypes::VoidMessage *msg );
void sysCommand( UniSetTypes::SystemMessage *msg );
void sensorInfo( UniSetTypes::SensorMessage*sm );
void timerInfo( UniSetTypes::TimerMessage *tm );
void askSensors( UniversalIO::UIOCommand cmd );
void initOutput();
void waitSMReady();
virtual bool activateObject();
// действия при завершении работы
virtual void sigterm( int signo );
void initMB( bool reopen=false );
void initIterators();
bool initItem( UniXML_iterator& it );
bool readItem( UniXML& xml, UniXML_iterator& it, xmlNode* sec );
void initDeviceList();
void initOffsetList();
RTUDevice* addDev( RTUDeviceMap& dmap, ModbusRTU::ModbusAddr a, UniXML_iterator& it );
RegInfo* addReg( RegMap& rmap, RegID id, ModbusRTU::ModbusData r, UniXML_iterator& it,
RTUDevice* dev, RegInfo* rcopy=0 );
RSProperty* addProp( PList& plist, RSProperty& p );
bool initMTRitem( UniXML_iterator& it, RegInfo* p );
bool initRSProperty( RSProperty& p, UniXML_iterator& it );
bool initRegInfo( RegInfo* r, UniXML_iterator& it, RTUDevice* dev );
bool initRTUDevice( RTUDevice* d, UniXML_iterator& it );
bool initDeviceInfo( RTUDeviceMap& m, ModbusRTU::ModbusAddr a, UniXML_iterator& it );
void rtuQueryOptimization( RTUDeviceMap& m );
void readConfiguration();
private:
MBTCPMaster();
bool initPause;
UniSetTypes::uniset_mutex mutex_start;
bool force; /*!< флаг означающий, что надо сохранять в SM, даже если значение не менялось */
bool force_out; /*!< флаг означающий, принудительного чтения выходов */
bool mbregFromID;
int polltime; /*!< переодичность обновления данных, [мсек] */
timeout_t sleepPause_usec;
PassiveTimer ptHeartBeat;
UniSetTypes::ObjectId sidHeartBeat;
int maxHeartBeat;
IOController::AIOStateList::iterator aitHeartBeat;
UniSetTypes::ObjectId test_id;
UniSetTypes::uniset_mutex pollMutex;
bool activated;
int activateTimeout;
bool noQueryOptimization;
bool force_disconnect;
bool allNotRespond;
Trigger trAllNotRespond;
PassiveTimer ptAllNotRespond;
std::string prefix;
bool no_extimer;
timeout_t stat_time; /*!< время сбора статистики обмена */
int poll_count;
PassiveTimer ptStatistic; /*!< таймер для сбора статистики обмена */
};
// -----------------------------------------------------------------------------
#endif // _MBTCPMaster_H_
// -----------------------------------------------------------------------------
bin_PROGRAMS = @PACKAGE@-mbtcpmaster
# не забывайте править версию в pc-файле
UMBTCP_VER=1:2:1
lib_LTLIBRARIES = libUniSetMBTCPMaster.la
libUniSetMBTCPMaster_la_LDFLAGS = -version-info $(UMBTCP_VER)
libUniSetMBTCPMaster_la_LIBADD = $(top_builddir)/lib/libUniSet.la \
$(top_builddir)/extensions/SharedMemory/libUniSetSharedMemory.la \
$(top_builddir)/extensions/lib/libUniSetExtensions.la \
$(top_builddir)/extensions/ModbusMaster/libMBMaster.la \
$(SIGC_LIBS)
libUniSetMBTCPMaster_la_CXXFLAGS = -I$(top_builddir)/extensions/include -I$(top_builddir)/extensions/SharedMemory -I$(top_builddir)/extensions/ModbusMaster $(SIGC_CFLAGS)
libUniSetMBTCPMaster_la_SOURCES = MBTCPMaster.cc
@PACKAGE@_mbtcpmaster_SOURCES = main.cc
@PACKAGE@_mbtcpmaster_LDADD = libUniSetMBTCPMaster.la $(top_builddir)/lib/libUniSet.la \
$(top_builddir)/extensions/SharedMemory/libUniSetSharedMemory.la \
$(top_builddir)/extensions/lib/libUniSetExtensions.la \
$(top_builddir)/extensions/ModbusMaster/libMBMaster.la \
$(SIGC_LIBS)
@PACKAGE@_mbtcpmaster_CXXFLAGS = -I$(top_builddir)/extensions/include -I$(top_builddir)/extensions/SharedMemory -I$(top_builddir)/extensions/ModbusMaster $(SIGC_CFLAGS)
# install
devel_include_HEADERS = *.h
devel_includedir = $(pkgincludedir)/extensions
pkgconfigdir = $(libdir)/pkgconfig
pkgconfig_DATA = libUniSetMBTCPMaster.pc
all-local:
ln -sf ../MBTCPMaster/$(devel_include_HEADERS) ../include
prefix=@prefix@
exec_prefix=@exec_prefix@
libdir=@libdir@
includedir=@includedir@
Name: libUniSetMBTCPMaster
Description: Support library for UniSetModbusTCPMaster
Requires: libUniSetExtensions libUniSetSharedMemory
Version: 1.2.1
Libs: -L${libdir} -lUniSetMBTCPMaster
Cflags: -I${includedir}/uniset
#include <sys/wait.h>
#include <sstream>
#include "MBTCPMaster.h"
#include "Configuration.h"
#include "Debug.h"
#include "ObjectsActivator.h"
#include "Extensions.h"
// -----------------------------------------------------------------------------
using namespace std;
using namespace UniSetTypes;
using namespace UniSetExtensions;
// -----------------------------------------------------------------------------
int main( int argc, const char** argv )
{
if( argc>1 && (!strcmp(argv[1],"--help") || !strcmp(argv[1],"-h")) )
{
cout << "--smemory-id objectName - SharedMemory objectID. Default: autodetect" << endl;
cout << "--confile filename - configuration file. Default: configure.xml" << endl;
cout << "--mbtcp-logfile filename - logfilename. Default: mbtcpmaster.log" << endl;
cout << endl;
MBTCPMaster::help_print(argc, argv);
return 0;
}
try
{
string confile=UniSetTypes::getArgParam("--confile",argc, argv, "configure.xml");
conf = new Configuration( argc, argv, confile );
string logfilename(conf->getArgParam("--mbtcp-logfile"));
if( logfilename.empty() )
logfilename = "mbtcpmaster.log";
conf->initDebug(dlog,"dlog");
std::ostringstream logname;
string dir(conf->getLogDir());
logname << dir << logfilename;
unideb.logFile( logname.str() );
dlog.logFile( logname.str() );
ObjectId shmID = DefaultObjectId;
string sID = conf->getArgParam("--smemory-id");
if( !sID.empty() )
shmID = conf->getControllerID(sID);
else
shmID = getSharedMemoryID();
if( shmID == DefaultObjectId )
{
cerr << sID << "? SharedMemoryID not found in " << conf->getControllersSection() << " section" << endl;
return 1;
}
MBTCPMaster* mb = MBTCPMaster::init_mbmaster(argc,argv,shmID);
if( !mb )
{
dlog[Debug::CRIT] << "(mbmaster): init MBTCPMaster failed." << endl;
return 1;
}
ObjectsActivator act;
act.addObject(static_cast<class UniSetObject*>(mb));
SystemMessage sm(SystemMessage::StartUp);
act.broadcast( sm.transport_msg() );
unideb(Debug::ANY) << "\n\n\n";
unideb[Debug::ANY] << "(main): -------------- MBTCP Exchange START -------------------------\n\n";
dlog(Debug::ANY) << "\n\n\n";
dlog[Debug::ANY] << "(main): -------------- MBTCP Exchange START -------------------------\n\n";
act.run(false);
while( waitpid(-1, 0, 0) > 0 );
return 0;
}
catch( Exception& ex )
{
dlog[Debug::CRIT] << "(mbtcpmaster): " << ex << std::endl;
}
catch(...)
{
dlog[Debug::CRIT] << "(mbtcpmaster): catch ..." << std::endl;
}
while( waitpid(-1, 0, 0) > 0 );
return 1;
}
#!/bin/sh
./uniset-start.sh -f ./uniset-mbtcpmaster \
--confile test.xml \
--mbtcp-name MBMaster1 \
--smemory-id SharedMemory \
--dlog-add-levels info,crit,warn,level4,level3 \
--mbtcp-filter-field mbtcp \
--mbtcp-filter-value 1 \
--mbtcp-gateway-iaddr 127.0.0.1 \
--mbtcp-gateway-port 2048 \
--mbtcp-recv-timeout 5000 \
--mbtcp-force-disconnect 1
#--mbtcp-filter-field mbtcp --mbtcp-filter-value 1
......@@ -55,10 +55,10 @@ pollActivated(false)
if( stat_time > 0 )
ptStatistic.setTiming(stat_time*1000);
// recv_timeout = conf->getArgPInt("--" + prefix + "-recv-timeout",it.getProp("recv_timeout"), 500);
//
// int tout = conf->getArgPInt("--" + prefix + "-timeout",it.getProp("timeout"), 5000);
// ptTimeout.setTiming(tout);
recv_timeout = conf->getArgPInt("--" + prefix + "-recv-timeout",it.getProp("recv_timeout"), 500);
int tout = conf->getArgPInt("--" + prefix + "-timeout",it.getProp("timeout"), 5000);
ptTimeout.setTiming(tout);
noQueryOptimization = conf->getArgInt("--" + prefix + "-no-query-optimization",it.getProp("no_query_optimization"));
......@@ -148,6 +148,21 @@ void MBExchange::help_print( int argc, const char* const* argv )
// -----------------------------------------------------------------------------
MBExchange::~MBExchange()
{
for( RTUDeviceMap::iterator it1=rmap.begin(); it1!=rmap.end(); ++it1 )
{
if( it1->second->rtu )
{
delete it1->second->rtu;
it1->second->rtu = 0;
}
RTUDevice* d(it1->second);
for( RegMap::iterator it=d->regmap.begin(); it!=d->regmap.end(); ++it )
delete it->second;
delete it1->second;
}
delete shm;
}
// -----------------------------------------------------------------------------
......@@ -2613,3 +2628,34 @@ void MBExchange::poll()
// printMap(rmap);
}
// -----------------------------------------------------------------------------
bool MBExchange::RTUDevice::checkRespond()
{
bool prev = resp_state;
if( resp_ptTimeout.getInterval() <= 0 )
{
resp_state = resp_real;
return (prev != resp_state);
}
if( resp_trTimeout.hi(resp_state && !resp_real) )
resp_ptTimeout.reset();
if( resp_real )
resp_state = true;
else if( resp_state && !resp_real && resp_ptTimeout.checkTime() )
resp_state = false;
// если ещё не инициализировали значение в SM
// то возвращаем true, чтобы оно принудительно сохранилось
if( !resp_init )
{
resp_state = resp_real;
resp_init = true;
prev = resp_state;
return true;
}
return ( prev != resp_state );
}
// -----------------------------------------------------------------------------
......@@ -10,35 +10,18 @@ using namespace std;
using namespace UniSetTypes;
using namespace UniSetExtensions;
// -----------------------------------------------------------------------------
MBTCPMaster::MBTCPMaster( UniSetTypes::ObjectId objId, UniSetTypes::ObjectId shmId,
MBTCPMaster::MBTCPMaster( UniSetTypes::ObjectId objId, UniSetTypes::ObjectId shmId,
SharedMemory* ic, const std::string prefix ):
MBExchange(objId,shmId,ic,prefix),
force_disconnect(true),
mbtcp(0),
pollThread(0)
{
if( objId == DefaultObjectId )
throw UniSetTypes::SystemError("(MBTCPMaster): objId=-1?!! Use --" + prefix + "-name" );
// префикс для "свойств" - по умолчанию
// cout << "$ $" << endl;
prop_prefix = "tcp_";
// если задано поле для "фильтрации"
// то в качестве префикса используем его
if( !s_field.empty() )
prop_prefix = s_field + "_";
// если "принудительно" задан префикс
// используем его.
{
string p("--" + prefix + "-set-prop-prefix");
string v = conf->getArgParam(p,"");
if( !v.empty() && v[0] != '-' )
prop_prefix = v;
// если параметр всё-таки указан, считаем, что это попытка задать "пустой" префикс
else if( findArgParam(p,conf->getArgc(),conf->getArgv()) != -1 )
prop_prefix = "";
}
dlog[Debug::INFO] << myname << "(init): prop_prefix=" << prop_prefix << endl;
if( objId == DefaultObjectId )
throw UniSetTypes::SystemError("(MBTCPMaster): objId=-1?!! Use --" + prefix + "-name" );
UniXML_iterator it(cnode);
......@@ -57,6 +40,11 @@ pollThread(0)
force_disconnect = conf->getArgInt("--" + prefix + "-persistent-connection",it.getProp("persistent_connection")) ? false : true;
dlog[Debug::INFO] << myname << "(init): persisten-connection=" << (!force_disconnect) << endl;
recv_timeout = conf->getArgPInt("--" + prefix + "-recv-timeout",it.getProp("recv_timeout"), 500);
int tout = conf->getArgPInt("--" + prefix + "-timeout",it.getProp("timeout"), 5000);
ptTimeout.setTiming(tout);
if( shm->isLocalwork() )
{
readConfiguration();
......@@ -67,9 +55,6 @@ pollThread(0)
ic->addReadItem( sigc::mem_fun(this,&MBTCPMaster::readItem) );
pollThread = new ThreadCreator<MBTCPMaster>(this, &MBTCPMaster::poll_thread);
if( dlog.debugging(Debug::INFO) )
printMap(rmap);
}
// -----------------------------------------------------------------------------
MBTCPMaster::~MBTCPMaster()
......@@ -84,7 +69,7 @@ ModbusClient* MBTCPMaster::initMB( bool reopen )
{
if( !reopen )
return mbtcp;
delete mbtcp;
mb = 0;
mbtcp = 0;
......@@ -94,7 +79,7 @@ ModbusClient* MBTCPMaster::initMB( bool reopen )
{
ost::Thread::setException(ost::Thread::throwException);
mbtcp = new ModbusTCPMaster();
ost::InetAddress ia(iaddr.c_str());
mbtcp->connect(ia,port);
mbtcp->setForceDisconnect(force_disconnect);
......@@ -103,11 +88,9 @@ ModbusClient* MBTCPMaster::initMB( bool reopen )
mbtcp->setTimeout(recv_timeout);
mbtcp->setSleepPause(sleepPause_usec);
mbtcp->setAfterSendPause(aftersend_pause);
dlog[Debug::INFO] << myname << "(init): ipaddr=" << iaddr << " port=" << port << endl;
if( dlog.debugging(Debug::LEVEL9) )
mbtcp->setLog(dlog);
}
......@@ -134,6 +117,54 @@ void MBTCPMaster::sysCommand( UniSetTypes::SystemMessage *sm )
pollThread->start();
}
// -----------------------------------------------------------------------------
void MBTCPMaster::step()
{
updateRespondSensors();
MBExchange::step();
}
// -----------------------------------------------------------------------------
void MBTCPMaster::updateRespondSensors()
{
bool tcpIsTimeout = false;
{
uniset_mutex_lock l(tcpMutex);
tcpIsTimeout = pollActivated && ptTimeout.checkTime();
}
if( dlog.debugging(Debug::LEVEL4) )
dlog[Debug::LEVEL4] << myname << ": tcpTimeout=" << tcpIsTimeout << endl;
for( MBTCPMaster::RTUDeviceMap::iterator it1=rmap.begin(); it1!=rmap.end(); ++it1 )
{
RTUDevice* d(it1->second);
if( tcpIsTimeout )
d->resp_real = false;
if( dlog.debugging(Debug::LEVEL4) )
{
dlog[Debug::LEVEL4] << myname << ": check respond addr=" << ModbusRTU::addr2str(d->mbaddr)
<< " respond_id=" << d->resp_id
<< " real=" << d->resp_real
<< " state=" << d->resp_state
<< endl;
}
if( d->checkRespond() && d->resp_id != DefaultObjectId )
{
try
{
bool set = d->resp_invert ? !d->resp_state : d->resp_state;
shm->localSaveState(d->resp_dit,d->resp_id,set,getId());
}
catch( Exception& ex )
{
dlog[Debug::CRIT] << myname << "(step): (respond) " << ex << std::endl;
}
}
}
}
// -----------------------------------------------------------------------------
void MBTCPMaster::poll_thread()
{
{
......@@ -145,7 +176,7 @@ void MBTCPMaster::poll_thread()
{
try
{
if( sidExchangeMode != DefaultObjectId && force )
if( sidExchangeMode != DefaultObjectId && force_out )
exchangeMode = shm->localGetValue(aitExchangeMode,sidExchangeMode);
}
catch(...){}
......@@ -162,19 +193,55 @@ void MBTCPMaster::poll_thread()
}
}
// -----------------------------------------------------------------------------
void MBTCPMaster::sigterm( int signo )
{
dlog[Debug::WARN] << myname << ": ********* SIGTERM(" << signo <<") ********" << endl;
setProcActive(false);
/*! \todo Доделать выставление безопасного состояния на выходы.
И нужно ли это. Ведь может не хватить времени на "обмен"
*/
// выставление безопасного состояния на выходы....
/*
RSMap::iterator it=rsmap.begin();
for( ; it!=rsmap.end(); ++it )
{
// if( it->stype!=UniversalIO::DigitalOutput && it->stype!=UniversalIO::AnalogOutput )
// continue;
if( it->safety == NoSafetyState )
continue;
try
{
}
catch( UniSetTypes::Exception& ex )
{
dlog[Debug::WARN] << myname << "(sigterm): " << ex << std::endl;
}
catch(...){}
}
*/
UniSetObject_LT::sigterm(signo);
}
// ------------------------------------------------------------------------------------------
void MBTCPMaster::help_print( int argc, const char* const* argv )
{
cout << "Default: prefix='mbtcp'" << endl;
MBExchange::help_print(argc,argv);
cout << endl;
// ---------- init MBTCP ----------
// cout << "--prefix-sm-ready-timeout - время на ожидание старта SM" << endl;
cout << " Настройки протокола TCP: " << endl;
cout << "--prefix-gateway-iaddr hostname,IP - IP опрашиваемого узла" << endl;
cout << "--prefix-gateway hostname,IP - IP опрашиваемого узла" << endl;
cout << "--prefix-gateway-port num - port на опрашиваемом узле" << endl;
cout << "--prefix-recv-timeout msec - Таймаут на приём одного сообщения" << endl;
cout << "--prefix-timeout msec - Таймаут для определения отсутсвия соединения" << endl;
cout << "--prefix-persistent-connection 0,1 - Не закрывать соединение на каждом цикле опроса" << endl;
}
// -----------------------------------------------------------------------------
MBTCPMaster* MBTCPMaster::init_mbmaster( int argc, const char* const* argv,
UniSetTypes::ObjectId icID, SharedMemory* ic,
MBTCPMaster* MBTCPMaster::init_mbmaster( int argc, const char* const* argv,
UniSetTypes::ObjectId icID, SharedMemory* ic,
const std::string prefix )
{
string name = conf->getArgParam("--" + prefix + "-name","MBTCPMaster1");
......@@ -187,7 +254,7 @@ MBTCPMaster* MBTCPMaster::init_mbmaster( int argc, const char* const* argv,
ObjectId ID = conf->getObjectID(name);
if( ID == UniSetTypes::DefaultObjectId )
{
dlog[Debug::CRIT] << "(MBTCPMaster): идентификатор '" << name
dlog[Debug::CRIT] << "(MBTCPMaster): идентификатор '" << name
<< "' не найден в конф. файле!"
<< " в секции " << conf->getObjectsSection() << endl;
return 0;
......@@ -197,3 +264,34 @@ MBTCPMaster* MBTCPMaster::init_mbmaster( int argc, const char* const* argv,
return new MBTCPMaster(ID,icID,ic,prefix);
}
// -----------------------------------------------------------------------------
void MBTCPMaster::execute()
{
no_extimer = true;
try
{
askTimer(tmExchange,0);
}
catch(...){}
initMB(false);
while(1)
{
try
{
step();
}
catch( Exception& ex )
{
dlog[Debug::CRIT] << myname << "(execute): " << ex << std::endl;
}
catch(...)
{
dlog[Debug::CRIT] << myname << "(execute): catch ..." << endl;
}
msleep(polltime);
}
}
// -----------------------------------------------------------------------------
......@@ -5,26 +5,35 @@
#include <string>
#include <map>
#include <vector>
#include "MBExchange.h"
#include "IONotifyController.h"
#include "UniSetObject_LT.h"
#include "modbus/ModbusTCPMaster.h"
#include "PassiveTimer.h"
#include "Trigger.h"
#include "Mutex.h"
#include "Calibration.h"
#include "SMInterface.h"
#include "SharedMemory.h"
#include "IOBase.h"
#include "VTypes.h"
#include "MTR.h"
// -----------------------------------------------------------------------------
/*!
\page page_ModbusTCP Реализация ModbusTCP master
- \ref sec_MBTCP_Comm
- \ref sec_MBTCP_Conf
- \ref sec_MBTCP_ConfList
- \ref sec_MBTCP_ExchangeMode
\section sec_MBTCP_Comm Общее описание ModbusTCP master
Класс реализует процесс обмена (опрос/запись) с RTU-устройствами,
через TCP-шлюз. Список регистров с которыми работает процесс задаётся в конфигурационном файле
в секции \b <sensors>. см. \ref sec_MBTCP_Conf
\section sec_MBTCP_Conf Конфигурирование ModbusTCP master
Конфигурирование процесса осуществляется либо параметрами командной строки либо
через настроечную секцию.
через настроечную секцию.
\par Секция с настройками
При своём старте, в конфигурационном файле ищётся секция с названием объекта,
......@@ -39,45 +48,43 @@
</MBMaster1>
\endcode
Секция <DeviceList> позволяет задать параметры обмена с конкретным RTU-устройством.
- \b addr - адрес устройства для которого, задаются параметры
- \b timeout msec - таймаут, для определения отсутствия связи
- \b invert - инвертировать логику. По умолчанию датчик выставляется в "1" при \b наличии связи.
- \b respondSensor - идентификатор датчика связи.
- \b modeSensor - идентификатор датчика режима работы (см. MBExchange::ExchangeMode).
- \b ask_every_reg - 1 - опрашивать ВСЕ регистры подряд, не обращая внимания на timeout. По умолчанию - "0" Т.е. опрос устройства (на текущем шаге цикла опроса), прерывается на первом же регистре, при опросе которого возникнет timeout.
\par Параметры запуска
При создании объекта в конструкторе передаётся префикс для определения параметров командной строки.
По умолчанию \b xxx="mbtcp".
Далее приведены основные параметры:
\b --xxx-name ID - идентификатор процесса.
IP-адрес шлюза задаётся параметром в конфигурационном файле \b gateway_iaddr или
параметром командной строки \b --xxx-gateway-iaddr.
Порт задаётся в конфигурационном файле параметром \b gateway_port или
параметром командной строки \b --xxx-gateway-port. По умолчанию используется порт \b 502.
\b --xxx-recv-timeout или \b recv_timeout msec - таймаут на приём одного сообщения. По умолчанию 100 мсек.
\b --xxx-timeout или \b timeout msec - таймаут на определение отсутсвия связи
\b --xxx-recv-timeout или \b recv_timeout msec - таймаут на приём сообщений. По умолчанию 2000 мсек.
\b --xxx-all-timeout или \b all_timeout msec - таймаут на определение отсутсвия связи
(после этого идёт попытка реинициализировать соединение)
По умолчанию 5000 мсек.
\b --xxx-no-query-optimization или \b no_query_optimization - [1|0] отключить оптимизацию запросов
Оптимизация заключается в том, что регистры идущие подряд автоматически запрашиваются/записываются одним запросом.
В связи с чем, функция указанная в качестве \b mbfunc игнорируется и подменяется на работающую с многими регистрами.
\b --xxx-poll-time или \b poll_time msec - пауза между опросами. По умолчанию 100 мсек.
\b --xxx-initPause или \b initPause msec - пауза перед началом работы, после активации. По умолчанию 50 мсек.
\b --xxx-force или \b force [1|0]
\b --xxx-force или \b force [1|0]
- 1 - перечитывать значения входов из SharedMemory на каждом цикле
- 0 - обновлять значения только по изменению
......@@ -87,56 +94,48 @@
- 1 - перечитывать значения выходов из SharedMemory на каждом цикле
- 0 - обновлять значения только по изменению
\b --xxx-reg-from-id или \b reg_from_id [1|0]
\b --xxx-reg-from-id или \b reg_from_id [1|0]
- 1 - в качестве регистра использовать идентификатор датчика
- 0 - регистр брать из поля tcp_mbreg
\b --xxx-heartbeat-id или \b heartbeat_id ID - идентификатор датчика "сердцебиения" (см. \ref sec_SM_HeartBeat)
\b --xxx-heartbeat-max или \b heartbeat_max val - сохраняемое значение счётчика "сердцебиения".
\b --xxx-activate-timeout msec . По умолчанию 2000. - время ожидания готовности SharedMemory к работе.
\section sec_MBTCP_ConfList Конфигурирование списка регистров для ModbusTCP master
Конфигурационные параметры задаются в секции <sensors> конфигурационного файла.
Список обрабатываемых регистров задаётся при помощи двух параметров командной строки
\b --xxx-filter-field - задаёт фильтрующее поле для датчиков
\b --xxx-filter-value - задаёт значение фильтрующего поля. Необязательный параметр.
\b --xxx-statistic-sec sec - при наличии выведет кол-во посланных запросов за этот промежуток времени.
\b --xxx-set-prop-prefix [str] - Использовать 'str' в качестве префикса для свойств.
Если не указать 'str' будет использован пустой префикс.
Если параметры не заданы, будет произведена попытка загрузить все датчики, у которых
присутствуют необходимые настроечные параметры.
\warning Если в результате список будет пустым, процесс завершает работу.
Пример конфигурационных параметров:
\code
\code
<sensors name="Sensors">
...
<item name="MySensor_S" textname="my sesnsor" iotype="DI"
tcp_mbtype="rtu" tcp_mbaddr="0x01" tcp_mbfunc="0x04" tcp_mbreg="0x02" my_tcp="1"
<item name="MySensor_S" textname="my sesnsor" iotype="DI"
tcp_mbtype="rtu" tcp_mbaddr="0x01" tcp_mbfunc="0x04" tcp_mbreg="0x02" my_tcp="1"
/>
...
</sensors>
\endcode
\warning По умолчанию для свойств используется префикс "tcp_". Но если задано поле \b filter-field,
то для свойств будет использован префикс <b>"filter-fileld"_</b>.
При этом при помощи --xxx-set-prop-prefix val можно принудительно задать префикс.
Если просто указать ключ --xxx-set-prop-prefix - будет использован "пустой" префикс (свойства без префикса).
К основным параметрам относятся следующие (префикс \b tcp_ - для примера):
К основным параметрам относятся следующие:
- \b tcp_mbtype - [rtu] - пока едиственный разрешённый тип.
- \b tcp_mbaddr - адрес RTU-устройства.
- \b tcp_mbreg - запрашиваемый/записываемый регистр.
- \b tcp_mbreg - запрашиваемый/записываемый регистр.
- \b tcp_mbfunc - [0x1,0x2,0x3,...] функция опроса/записи. Разрешённые см. ModbusRTU::SlaveFunctionCode.
Помимо этого можно задавать следующие параметры:
- \b tcp_vtype - тип переменной. см VTypes::VType.
- \b tcp_rawdata - [0|1] - игнорировать или нет параметры калибровки
......@@ -144,7 +143,7 @@
- \b tcp_nbit - номер бита в слове. Используется для DI,DO в случае когда для опроса используется
функция читающая слова (03
- \b tcp_nbyte - [1|2] номер байта. Используется если tcp_vtype="byte".
- \b tcp_mboffset - "сдвиг"(может быть отрицательным) при опросе/записи.
- \b tcp_mboffset - "сдвиг"(может быть отрицательным) при опросе/записи.
Т.е. фактически будет опрошен/записан регистр "mbreg+mboffset".
Для инициализации "выходов" (регистров которые пишутся) можно использовать поля:
......@@ -160,74 +159,283 @@
При этом будет записывыться значение "default".
\warning Регистр должен быть уникальным. И может повторятся только если указан параметр \a nbit или \a nbyte.
\section sec_MBTCP_ExchangeMode Управление режимом работы MBTCPMaster
В MBTCPMaster заложена возможность управлять режимом работы процесса. Поддерживаются
следующие режимы:
- \b emNone - нормальная работа (по умолчанию)
- \b emWriteOnly - "только посылка данных" (работают только write-функции)
- \b emReadOnly - "только чтение" (работают только read-функции)
- \b emSkipSaveToSM - "не записывать данные в SM", это особый режим, похожий на \b emWriteOnly,
но отличие в том, что при этом режиме ведётся полноценый обмен (и read и write),
только реально данные не записываются в SharedMemory(SM).
- \b emSkipExchnage - отключить обмен (при этом данные "из SM" обновляются).
Режимы переключаются при помощи датчика, который можно задать либо аргументом командной строки
\b --prefix-exchange-mode-id либо в конф. файле параметром \b echangeModeID="". Константы определяющие режимы объявлены в MBTCPMaster::ExchangeMode.
\warning Регистр должен быть уникальным. И может повторятся только если указан параметр \a nbit или \a nbyte.
*/
// -----------------------------------------------------------------------------
/*!
\par Реализация Modbus TCP Master для обмена с многими ModbusRTU устройствами
Реализация Modbus TCP Master для обмена с многими ModbusRTU устройствами
через один modbus tcp шлюз.
\par Чтобы не зависеть от таймаутов TCP соединений, которые могут неопределённо зависать
на создании соединения с недоступным хостом. Обмен вынесен в отдельный поток.
При этом в этом же потоке обновляются данные в SM. В свою очередь информация о датчиках
связи обновляется в основном потоке (чтобы не зависеть от TCP).
*/
class MBTCPMaster:
public MBExchange
public UniSetObject_LT
{
public:
MBTCPMaster( UniSetTypes::ObjectId objId, UniSetTypes::ObjectId shmID, SharedMemory* ic=0,
const std::string prefix="mbtcp" );
virtual ~MBTCPMaster();
/*! глобальная функция для инициализации объекта */
static MBTCPMaster* init_mbmaster( int argc, const char* const* argv,
static MBTCPMaster* init_mbmaster( int argc, const char* const* argv,
UniSetTypes::ObjectId shmID, SharedMemory* ic=0,
const std::string prefix="mbtcp" );
/*! глобальная функция для вывода help-а */
static void help_print( int argc, const char* const* argv );
protected:
virtual void sysCommand( UniSetTypes::SystemMessage *sm );
void execute();
static const int NoSafetyState=-1;
enum Timer
{
tmExchange
};
enum DeviceType
{
dtUnknown, /*!< неизвестный */
dtRTU, /*!< RTU (default) */
dtMTR /*!< MTR (DEIF) */
};
static DeviceType getDeviceType( const std::string dtype );
friend std::ostream& operator<<( std::ostream& os, const DeviceType& dt );
// -------------------------------------------------------------------------------
struct RTUDevice;
struct RegInfo;
struct RSProperty:
public IOBase
{
// only for RTU
short nbit; /*!< bit number) */
VTypes::VType vType; /*!< type of value */
short rnum; /*!< count of registers */
short nbyte; /*!< byte number (1-2) */
RSProperty():
nbit(-1),vType(VTypes::vtUnknown),
rnum(VTypes::wsize(VTypes::vtUnknown)),
nbyte(0),reg(0)
{}
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, PList& p );
typedef unsigned long RegID;
typedef std::map<RegID,RegInfo*> RegMap;
struct RegInfo
{
RegInfo():
mbval(0),mbreg(0),mbfunc(ModbusRTU::fnUnknown),
id(0),dev(0),mtrType(MTR::mtUnknown),
q_num(0),q_count(1),mb_initOK(true),sm_initOK(true)
{}
ModbusRTU::ModbusData mbval;
ModbusRTU::ModbusData mbreg; /*!< регистр */
ModbusRTU::SlaveFunctionCode mbfunc; /*!< функция для чтения/записи */
PList slst;
RegID id;
RTUDevice* dev;
// only for MTR
MTR::MTRType mtrType; /*!< тип регистра (согласно спецификации на MTR) */
// optimization
int q_num; /*!< number in query */
int q_count; /*!< count registers for query */
RegMap::iterator rit;
// начальная инициалиазция для "записываемых" регистров
// Механизм:
// Если tcp_preinit="1", то сперва будет сделано чтение значения из устройства.
// при этом флаг mb_init=false пока не пройдёт успешной инициализации
// Если tcp_preinit="0", то флаг mb_init сразу выставляется в true.
bool mb_initOK; /*!< инициализировалось ли значение из устройства */
// Флаг sm_init означает, что писать в устройство нельзя, т.к. значение в "карте регистров"
// ещё не инициализировано из SM
bool sm_initOK; /*!< инициализировалось ли значение из SM */
};
friend std::ostream& operator<<( std::ostream& os, RegInfo& r );
struct RTUDevice
{
RTUDevice():
respnond(false),
mbaddr(0),
dtype(dtUnknown),
resp_id(UniSetTypes::DefaultObjectId),
resp_state(false),
resp_invert(false),
resp_real(false),
resp_init(false),
ask_every_reg(false)
{
resp_trTimeout.change(false);
}
bool respnond;
ModbusRTU::ModbusAddr mbaddr; /*!< адрес устройства */
RegMap regmap;
DeviceType dtype; /*!< тип устройства */
UniSetTypes::ObjectId resp_id;
IOController::DIOStateList::iterator resp_dit;
PassiveTimer resp_ptTimeout;
Trigger resp_trTimeout;
bool resp_state;
bool resp_invert;
bool resp_real;
bool resp_init;
bool ask_every_reg;
// return TRUE if state changed
bool checkRespond();
};
friend std::ostream& operator<<( std::ostream& os, RTUDevice& d );
typedef std::map<ModbusRTU::ModbusAddr,RTUDevice*> RTUDeviceMap;
friend std::ostream& operator<<( std::ostream& os, RTUDeviceMap& d );
void printMap(RTUDeviceMap& d);
// ----------------------------------
static RegID genRegID( const ModbusRTU::ModbusData r, const int fn );
protected:
struct InitRegInfo
{
InitRegInfo():
dev(0),mbreg(0),
mbfunc(ModbusRTU::fnUnknown),
initOK(false),ri(0)
{}
RSProperty p;
RTUDevice* dev;
ModbusRTU::ModbusData mbreg;
ModbusRTU::SlaveFunctionCode mbfunc;
bool initOK;
RegInfo* ri;
};
typedef std::list<InitRegInfo> InitList;
void firstInitRegisters();
bool preInitRead( InitList::iterator& p );
bool initSMValue( ModbusRTU::ModbusData* data, int count, RSProperty* p );
bool allInitOK;
RTUDeviceMap rmap;
InitList initRegList; /*!< список регистров для инициализации */
ModbusTCPMaster* mb;
UniSetTypes::uniset_mutex mbMutex;
std::string iaddr;
// ost::InetAddress* ia;
int port;
int recv_timeout;
// virtual void step();
virtual ModbusClient* initMB( bool reopen=false );
void poll_thread();
bool force_disconnect;
xmlNode* cnode;
std::string s_field;
std::string s_fvalue;
SMInterface* shm;
void step();
void poll();
bool pollRTU( RTUDevice* dev, RegMap::iterator& it );
void updateSM();
void updateRTU(RegMap::iterator& it);
void updateMTR(RegMap::iterator& it);
void updateRSProperty( RSProperty* p, bool write_only=false );
virtual void processingMessage( UniSetTypes::VoidMessage *msg );
void sysCommand( UniSetTypes::SystemMessage *msg );
void sensorInfo( UniSetTypes::SensorMessage*sm );
void timerInfo( UniSetTypes::TimerMessage *tm );
void askSensors( UniversalIO::UIOCommand cmd );
void initOutput();
void waitSMReady();
virtual bool activateObject();
// действия при завершении работы
virtual void sigterm( int signo );
void initMB( bool reopen=false );
void initIterators();
bool initItem( UniXML_iterator& it );
bool readItem( UniXML& xml, UniXML_iterator& it, xmlNode* sec );
void initDeviceList();
void initOffsetList();
RTUDevice* addDev( RTUDeviceMap& dmap, ModbusRTU::ModbusAddr a, UniXML_iterator& it );
RegInfo* addReg( RegMap& rmap, RegID id, ModbusRTU::ModbusData r, UniXML_iterator& it,
RTUDevice* dev, RegInfo* rcopy=0 );
RSProperty* addProp( PList& plist, RSProperty& p );
bool initMTRitem( UniXML_iterator& it, RegInfo* p );
bool initRSProperty( RSProperty& p, UniXML_iterator& it );
bool initRegInfo( RegInfo* r, UniXML_iterator& it, RTUDevice* dev );
bool initRTUDevice( RTUDevice* d, UniXML_iterator& it );
bool initDeviceInfo( RTUDeviceMap& m, ModbusRTU::ModbusAddr a, UniXML_iterator& it );
void rtuQueryOptimization( RTUDeviceMap& m );
void readConfiguration();
private:
MBTCPMaster();
bool initPause;
UniSetTypes::uniset_mutex mutex_start;
bool force; /*!< флаг означающий, что надо сохранять в SM, даже если значение не менялось */
bool force_out; /*!< флаг означающий, принудительного чтения выходов */
bool mbregFromID;
int polltime; /*!< переодичность обновления данных, [мсек] */
timeout_t sleepPause_usec;
PassiveTimer ptHeartBeat;
UniSetTypes::ObjectId sidHeartBeat;
int maxHeartBeat;
IOController::AIOStateList::iterator aitHeartBeat;
UniSetTypes::ObjectId test_id;
UniSetTypes::uniset_mutex pollMutex;
bool activated;
int activateTimeout;
bool noQueryOptimization;
bool force_disconnect;
bool allNotRespond;
Trigger trAllNotRespond;
PassiveTimer ptAllNotRespond;
std::string prefix;
ModbusTCPMaster* mbtcp;
bool no_extimer;
// т.к. TCP может "зависнуть" на подключении к недоступному узлу
// делаем опрос в отдельном потоке
ThreadCreator<MBTCPMaster>* pollThread; /*!< поток опроса */
UniSetTypes::uniset_mutex tcpMutex;
timeout_t stat_time; /*!< время сбора статистики обмена */
int poll_count;
PassiveTimer ptStatistic; /*!< таймер для сбора статистики обмена */
};
// -----------------------------------------------------------------------------
#endif // _MBTCPMaster_H_
......
// -----------------------------------------------------------------------------
#include <cmath>
#include <sstream>
#include "Exceptions.h"
#include "Extensions.h"
#include "RTUExchange.h"
// -----------------------------------------------------------------------------
......@@ -10,39 +11,42 @@ using namespace UniSetExtensions;
// -----------------------------------------------------------------------------
RTUExchange::RTUExchange( UniSetTypes::ObjectId objId, UniSetTypes::ObjectId shmId, SharedMemory* ic,
const std::string prefix_ ):
MBExchange(objId,shmId,ic,prefix_),
mbrtu(0),
defSpeed(ComPort::ComSpeed38400),
UniSetObject_LT(objId),
mb(0),
defSpeed(ComPort::ComSpeed0),
use485F(false),
transmitCtl(false),
rs_pre_clean(false)
shm(0),
initPause(0),
force(false),
force_out(false),
mbregFromID(false),
activated(false),
rs_pre_clean(false),
noQueryOptimization(false),
allNotRespond(false),
prefix(prefix_)
{
if( objId == DefaultObjectId )
throw UniSetTypes::SystemError("(RTUExchange): objId=-1?!! Use --" + prefix + "-name" );
// префикс для "свойств" - по умолчанию
prop_prefix = "";
// если задано поле для "фильтрации"
// то в качестве префикса используем его
if( !s_field.empty() )
prop_prefix = s_field + "_";
// если "принудительно" задан префикс
// используем его.
{
string p("--" + prefix + "-set-prop-prefix");
string v = conf->getArgParam(p,"");
if( !v.empty() && v[0] != '-' )
prop_prefix = v;
// если параметр всё-таки указан, считаем, что это попытка задать "пустой" префикс
else if( findArgParam(p,conf->getArgc(),conf->getArgv()) != -1 )
prop_prefix = "";
}
// xmlNode* cnode = conf->getNode(myname);
cnode = conf->getNode(myname);
if( cnode == NULL )
throw UniSetTypes::SystemError("(RTUExchange): Not find conf-node for " + myname );
dlog[Debug::INFO] << myname << "(init): prop_prefix=" << prop_prefix << endl;
shm = new SMInterface(shmId,&ui,objId,ic);
UniXML_iterator it(cnode);
// определяем фильтр
s_field = conf->getArgParam("--"+prefix+"-filter-field");
s_fvalue = conf->getArgParam("--"+prefix+"-filter-value");
dlog[Debug::INFO] << myname << "(init): read fileter-field='" << s_field
<< "' filter-value='" << s_fvalue << "'" << endl;
// ---------- init RS ----------
// UniXML_iterator it(cnode);
devname = conf->getArgParam("--"+prefix+"-dev",it.getProp("device"));
if( devname.empty() )
throw UniSetTypes::SystemError(myname+"(RTUExchange): Unknown device..." );
......@@ -55,9 +59,25 @@ rs_pre_clean(false)
transmitCtl = conf->getArgInt("--"+prefix+"-transmit-ctl",it.getProp("transmitCtl"));
defSpeed = ComPort::getSpeed(speed);
sleepPause_usec = conf->getArgPInt("--" + prefix + "-sleepPause-usec",it.getProp("slepePause"), 100);
recv_timeout = conf->getArgPInt("--"+prefix+"-recv-timeout",it.getProp("recv_timeout"), 50);
int alltout = conf->getArgPInt("--"+prefix+"-all-timeout",it.getProp("all_timeout"), 2000);
ptAllNotRespond.setTiming(alltout);
rs_pre_clean = conf->getArgInt("--"+prefix+"-pre-clean",it.getProp("pre_clean"));
noQueryOptimization = conf->getArgInt("--"+prefix+"-no-query-optimization",it.getProp("no_query_optimization"));
mbregFromID = conf->getArgInt("--mbs-reg-from-id",it.getProp("reg_from_id"));
dlog[Debug::INFO] << myname << "(init): mbregFromID=" << mbregFromID << endl;
polltime = conf->getArgPInt("--"+prefix+"-polltime",it.getProp("polltime"), 100);
initPause = conf->getArgPInt("--"+prefix+"-initPause",it.getProp("initPause"), 3000);
force = conf->getArgInt("--"+prefix+"-force",it.getProp("force"));
force_out = conf->getArgInt("--"+prefix+"-force-out",it.getProp("force_out"));
if( shm->isLocalwork() )
{
readConfiguration();
......@@ -67,165 +87,210 @@ rs_pre_clean(false)
else
ic->addReadItem( sigc::mem_fun(this,&RTUExchange::readItem) );
// ********** HEARTBEAT *************
string heart = conf->getArgParam("--"+prefix+"-heartbeat-id",it.getProp("heartbeat_id"));
if( !heart.empty() )
{
sidHeartBeat = conf->getSensorID(heart);
if( sidHeartBeat == DefaultObjectId )
{
ostringstream err;
err << myname << ": ID not found ('HeartBeat') for " << heart;
dlog[Debug::CRIT] << myname << "(init): " << err.str() << endl;
throw SystemError(err.str());
}
int heartbeatTime = getHeartBeatTime();
if( heartbeatTime )
ptHeartBeat.setTiming(heartbeatTime);
else
ptHeartBeat.setTiming(UniSetTimer::WaitUpTime);
maxHeartBeat = conf->getArgPInt("--"+prefix+"-heartbeat-max",it.getProp("heartbeat_max"), 10);
test_id = sidHeartBeat;
}
else
{
test_id = conf->getSensorID("TestMode_S");
if( test_id == DefaultObjectId )
{
ostringstream err;
err << myname << "(init): test_id unknown. 'TestMode_S' not found...";
dlog[Debug::CRIT] << myname << "(init): " << err.str() << endl;
throw SystemError(err.str());
}
}
dlog[Debug::INFO] << myname << "(init): test_id=" << test_id << endl;
activateTimeout = conf->getArgPInt("--"+prefix+"-activate-timeout", 20000);
initMB(false);
if( dlog.debugging(Debug::INFO) )
printMap(rmap);
}
// -----------------------------------------------------------------------------
void RTUExchange::help_print( int argc, const char* const* argv )
{
cout << "Default: prefix='rs'" << endl;
MBExchange::help_print(argc,argv);
// cout << " Настройки протокола RS: " << endl;
cout << "--prefix-dev devname - файл устройства" << endl;
cout << "--prefix-speed - Скорость обмена (9600,19920,38400,57600,115200)." << endl;
cout << "--prefix-my-addr - адрес текущего узла" << endl;
cout << "--prefix-recv-timeout - Таймаут на ожидание ответа." << endl;
cout << "--prefix-pre-clean - Очищать буфер перед каждым запросом" << endl;
cout << "--prefix-sleepPause-usec - Таймаут на ожидание очередного байта" << endl;
printMap(rmap);
// abort();
}
// -----------------------------------------------------------------------------
RTUExchange::~RTUExchange()
{
delete mbrtu;
for( RTUExchange::RTUDeviceMap::iterator it1=rmap.begin(); it1!=rmap.end(); ++it1 )
{
if( it1->second->rtu )
{
delete it1->second->rtu;
it1->second->rtu = 0;
}
RTUDevice* d(it1->second);
for( RTUExchange::RegMap::iterator it=d->regmap.begin(); it!=d->regmap.end(); ++it )
delete it->second;
delete it1->second;
}
delete mb;
delete shm;
}
// -----------------------------------------------------------------------------
ModbusClient* RTUExchange::initMB( bool reopen )
void RTUExchange::initMB( bool reopen )
{
if( !file_exist(devname) )
{
if( mbrtu )
if( mb )
{
delete mbrtu;
delete mb;
mb = 0;
mbrtu = 0;
}
return mbrtu;
return;
}
if( mbrtu )
if( mb )
{
if( !reopen )
return mbrtu;
delete mbrtu;
mbrtu = 0;
return;
delete mb;
mb = 0;
}
try
{
mbrtu = new ModbusRTUMaster(devname,use485F,transmitCtl);
mb = new ModbusRTUMaster(devname,use485F,transmitCtl);
if( defSpeed != ComPort::ComSpeed0 )
mbrtu->setSpeed(defSpeed);
mb->setSpeed(defSpeed);
if( dlog.debugging(Debug::LEVEL9) )
mbrtu->setLog(dlog);
// mb->setLog(dlog);
if( recv_timeout > 0 )
mbrtu->setTimeout(recv_timeout);
mbrtu->setSleepPause(sleepPause_usec);
mbrtu->setAfterSendPause(aftersend_pause);
mb->setTimeout(recv_timeout);
dlog[Debug::INFO] << myname << "(init): dev=" << devname << " speed=" << ComPort::getSpeed( mbrtu->getSpeed() ) << endl;
}
catch( Exception& ex )
{
if( mbrtu )
delete mbrtu;
mbrtu = 0;
dlog[Debug::WARN] << myname << "(init): " << ex << endl;
dlog[Debug::INFO] << myname << "(init): dev=" << devname << " speed=" << ComPort::getSpeed(defSpeed) << endl;
}
catch(...)
{
if( mbrtu )
delete mbrtu;
mbrtu = 0;
dlog[Debug::INFO] << myname << "(init): catch...." << endl;
if( mb )
delete mb;
mb = 0;
}
}
// -----------------------------------------------------------------------------
void RTUExchange::waitSMReady()
{
// waiting for SM is ready...
int ready_timeout = conf->getArgInt("--"+prefix+"-sm-ready-timeout","15000");
if( ready_timeout == 0 )
ready_timeout = 15000;
else if( ready_timeout < 0 )
ready_timeout = UniSetTimer::WaitUpTime;
mb = mbrtu;
return mbrtu;
if( !shm->waitSMready(ready_timeout,50) )
{
ostringstream err;
err << myname << "(waitSMReady): Не дождались готовности SharedMemory к работе в течение " << ready_timeout << " мсек";
dlog[Debug::CRIT] << err.str() << endl;
throw SystemError(err.str());
}
}
// -----------------------------------------------------------------------------
void RTUExchange::timerInfo( TimerMessage *tm )
{
if( tm->id == tmExchange )
step();
}
// -----------------------------------------------------------------------------
void RTUExchange::step()
{
try
{
if( sidExchangeMode != DefaultObjectId && force )
exchangeMode = shm->localGetValue(aitExchangeMode,sidExchangeMode);
uniset_mutex_lock l(pollMutex,2000);
poll();
}
catch(...){}
poll();
if( !activated )
return;
MBExchange::step();
if( sidHeartBeat!=DefaultObjectId && ptHeartBeat.checkTime() )
{
try
{
shm->localSaveValue(aitHeartBeat,sidHeartBeat,maxHeartBeat,getId());
ptHeartBeat.reset();
}
catch(Exception& ex)
{
dlog[Debug::CRIT] << myname
<< "(step): (hb) " << ex << std::endl;
}
}
}
// -----------------------------------------------------------------------------
void RTUExchange::poll()
{
if( trAllNotRespond.hi(allNotRespond) )
ptAllNotRespond.reset();
if( allNotRespond && mb && ptAllNotRespond.checkTime() )
{
ptAllNotRespond.reset();
initMB(true);
}
if( !mb )
{
initMB(false);
if( !mb )
{
uniset_mutex_lock l(pollMutex, 300);
pollActivated = false;
mb = initMB(false);
if( !mb )
{
for( MBExchange::RTUDeviceMap::iterator it=rmap.begin(); it!=rmap.end(); ++it )
it->second->resp_real = false;
}
for( RTUExchange::RTUDeviceMap::iterator it=rmap.begin(); it!=rmap.end(); ++it )
it->second->resp_real = false;
}
if( !checkProcActive() )
return;
updateSM();
allInitOK = false;
return;
}
{
uniset_mutex_lock l(pollMutex,200);
pollActivated = true;
ptTimeout.reset();
}
if( !allInitOK )
firstInitRegisters();
if( !checkProcActive() )
return;
ComPort::Speed s = mb->getSpeed();
bool allNotRespond = true;
ComPort::Speed s = mbrtu->getSpeed();
for( MBExchange::RTUDeviceMap::iterator it1=rmap.begin(); it1!=rmap.end(); ++it1 )
for( RTUExchange::RTUDeviceMap::iterator it1=rmap.begin(); it1!=rmap.end(); ++it1 )
{
RTUDevice* d(it1->second);
if( d->mode_id != DefaultObjectId && d->mode == emSkipExchange )
continue;
if( d->speed != s )
{
s = d->speed;
mbrtu->setSpeed(d->speed);
mb->setSpeed(d->speed);
}
if( d->dtype == MBExchange::dtRTU188 )
if( dlog.debugging(Debug::INFO) )
dlog[Debug::INFO] << myname << "(poll): ask addr=" << ModbusRTU::addr2str(d->mbaddr) << endl;
if( d->dtype==RTUExchange::dtRTU188 )
{
if( !d->rtu )
continue;
if( dlog.debugging(Debug::LEVEL3) )
if( dlog.debugging(Debug::INFO) )
{
dlog[Debug::LEVEL3] << myname << "(pollRTU188): poll RTU188 "
dlog[Debug::INFO] << myname << "(pollRTU188): poll RTU188 "
<< " mbaddr=" << ModbusRTU::addr2str(d->mbaddr)
<< endl;
}
......@@ -235,28 +300,24 @@ void RTUExchange::poll()
if( rs_pre_clean )
mb->cleanupChannel();
d->rtu->poll(mbrtu);
d->rtu->poll(mb);
d->resp_real = true;
}
catch( ModbusRTU::mbException& ex )
{
{
if( d->resp_real )
{
if( dlog.debugging(Debug::LEVEL3) )
{
dlog[Debug::CRIT] << myname << "(poll): FAILED ask addr=" << ModbusRTU::addr2str(d->mbaddr)
dlog[Debug::CRIT] << myname << "(poll): FAILED ask addr=" << ModbusRTU::addr2str(d->mbaddr)
<< " -> " << ex << endl;
}
}
d->resp_real = false;
}
}
}
else
else
{
if( dlog.debugging(Debug::LEVEL3) )
dlog[Debug::LEVEL3] << myname << "(poll): ask addr=" << ModbusRTU::addr2str(d->mbaddr)
<< " regs=" << d->regmap.size() << endl;
d->resp_real = false;
for( RTUExchange::RegMap::iterator it=d->regmap.begin(); it!=d->regmap.end(); ++it )
{
......@@ -271,7 +332,7 @@ void RTUExchange::poll()
}
}
catch( ModbusRTU::mbException& ex )
{
{
// if( d->resp_real )
// {
if( dlog.debugging(Debug::LEVEL3) )
......@@ -287,94 +348,1921 @@ void RTUExchange::poll()
if( it==d->regmap.end() )
break;
if( !checkProcActive() )
return;
}
}
if( d->resp_real )
allNotRespond = false;
}
// update SharedMemory...
updateSM();
// check thresholds
for( ThresholdList::iterator t=thrlist.begin(); t!=thrlist.end(); ++t )
for( RTUExchange::RTUDeviceMap::iterator it1=rmap.begin(); it1!=rmap.end(); ++it1 )
{
if( !checkProcActive() )
return;
RTUDevice* d(it1->second);
for( RTUExchange::RegMap::iterator it=d->regmap.begin(); it!=d->regmap.end(); ++it )
{
RegInfo* r(it->second);
for( PList::iterator i=r->slst.begin(); i!=r->slst.end(); ++i )
IOBase::processingThreshold( &(*i),shm,force);
}
}
// printMap(rmap);
}
// -----------------------------------------------------------------------------
bool RTUExchange::pollRTU( RTUDevice* dev, RegMap::iterator& it )
{
RegInfo* p(it->second);
if( dlog.debugging(Debug::LEVEL3) )
{
dlog[Debug::LEVEL3] << myname << "(pollRTU): poll "
<< " mbaddr=" << ModbusRTU::addr2str(dev->mbaddr)
<< " mbreg=" << ModbusRTU::dat2str(p->mbreg)
<< " mboffset=" << p->offset
<< " mbfunc=" << p->mbfunc
<< " q_count=" << p->q_count
<< " mb_init=" << p->mb_init
<< endl;
}
IOBase::processingThreshold(&(*t),shm,force);
if( p->q_count == 0 )
{
if( dlog.debugging(Debug::INFO) )
dlog[Debug::INFO] << myname << "(pollRTU): q_count=0 for mbreg=" << ModbusRTU::dat2str(p->mbreg)
<< " IGNORE register..." << endl;
return false;
}
if( trReopen.hi(allNotRespond) )
ptReopen.reset();
if( allNotRespond && ptReopen.checkTime() )
switch( p->mbfunc )
{
uniset_mutex_lock l(pollMutex, 300);
if( dlog.debugging(Debug::WARN) )
dlog[Debug::WARN] << myname << ": REOPEN timeout..(" << ptReopen.getInterval() << ")" << endl;
case ModbusRTU::fnReadInputRegisters:
{
ModbusRTU::ReadInputRetMessage ret = mb->read04(dev->mbaddr,p->mbreg+p->offset,p->q_count);
for( int i=0; i<p->q_count; i++,it++ )
it->second->mbval = ret.data[i];
it--;
}
break;
case ModbusRTU::fnReadOutputRegisters:
{
ModbusRTU::ReadOutputRetMessage ret = mb->read03(dev->mbaddr, p->mbreg+p->offset,p->q_count);
for( int i=0; i<p->q_count; i++,it++ )
it->second->mbval = ret.data[i];
it--;
}
break;
case ModbusRTU::fnReadInputStatus:
{
ModbusRTU::ReadInputStatusRetMessage ret = mb->read02(dev->mbaddr,p->mbreg+p->offset,p->q_count);
int m=0;
for( int i=0; i<ret.bcnt; i++ )
{
ModbusRTU::DataBits b(ret.data[i]);
for( int k=0;k<ModbusRTU::BitsPerByte && m<p->q_count; k++,it++,m++ )
it->second->mbval = b[k];
}
it--;
}
break;
case ModbusRTU::fnReadCoilStatus:
{
ModbusRTU::ReadCoilRetMessage ret = mb->read01(dev->mbaddr,p->mbreg+p->offset,p->q_count);
int m = 0;
for( int i=0; i<ret.bcnt; i++ )
{
ModbusRTU::DataBits b(ret.data[i]);
for( int k=0;k<ModbusRTU::BitsPerByte && m<p->q_count; k++,it++,m++ )
it->second->mbval = b[k] ? 1 : 0;
}
it--;
}
break;
case ModbusRTU::fnWriteOutputSingleRegister:
{
if( p->q_count != 1 )
{
dlog[Debug::CRIT] << myname << "(pollRTU): mbreg=" << ModbusRTU::dat2str(p->mbreg)
<< " IGNORE WRITE SINGLE REGISTER (0x06) q_count=" << p->q_count << " ..." << endl;
return false;
}
if( !p->mb_init )
{
// cerr << "******* mb_init: mbreg=" << ModbusRTU::dat2str(p->mbreg) << endl;
ModbusRTU::ReadInputRetMessage ret1 = mb->read04(dev->mbaddr,p->mb_init_mbreg,1);
p->mbval = ret1.data[0];
p->sm_init = true;
return true;
}
// cerr << "**** mbreg=" << ModbusRTU::dat2str(p->mbreg) << " val=" << ModbusRTU::dat2str(p->mbval) << endl;
ModbusRTU::WriteSingleOutputRetMessage ret = mb->write06(dev->mbaddr,p->mbreg+p->offset,p->mbval);
}
break;
case ModbusRTU::fnWriteOutputRegisters:
{
ModbusRTU::WriteOutputMessage msg(dev->mbaddr,p->mbreg+p->offset);
for( int i=0; i<p->q_count; i++,it++ )
{
if( !it->second->mb_init )
{
// cerr << "******* mb_init: mbreg=" << ModbusRTU::dat2str(it->second->mbreg)
// << " mb_init mbreg=" << ModbusRTU::dat2str(it->second->mb_init_mbreg) << endl;
ModbusRTU::ReadOutputRetMessage ret1 = mb->read03(dev->mbaddr,it->second->mb_init_mbreg,1);
// cerr << "******* mb_init: mbreg=" << ModbusRTU::dat2str(it->second->mbreg)
// << " mb_init mbreg=" << ModbusRTU::dat2str(it->second->mb_init_mbreg)
// << " mbval=" << ret1.data[0] << endl;
it->second->mbval = ret1.data[0];
it->second->sm_init = true;
}
msg.addData(it->second->mbval);
}
it--;
ModbusRTU::WriteOutputRetMessage ret = mb->write10(msg);
}
break;
case ModbusRTU::fnForceSingleCoil:
{
if( p->q_count != 1 )
{
dlog[Debug::CRIT] << myname << "(pollRTU): mbreg=" << ModbusRTU::dat2str(p->mbreg)
<< " IGNORE FORCE SINGLE COIL (0x05) q_count=" << p->q_count << " ..." << endl;
return false;
}
if( !p->mb_init )
{
// cerr << "******* mb_init: mbreg=" << ModbusRTU::dat2str(p->mbreg)
// << " init mbreg=" << ModbusRTU::dat2str(p->mb_init_mbreg) << endl;
ModbusRTU::ReadInputStatusRetMessage ret1 = mb->read02(dev->mbaddr,p->mb_init_mbreg,1);
ModbusRTU::DataBits b(ret1.data[0]);
// cerr << "******* mb_init_mbreg=" << ModbusRTU::dat2str(p->mb_init_mbreg)
// << " read val=" << (int)b[0] << endl;
p->mbval = b[0];
p->sm_init = true;
return true;
}
// cerr << "****(coil) mbreg=" << ModbusRTU::dat2str(p->mbreg) << " val=" << ModbusRTU::dat2str(p->mbval) << endl;
ModbusRTU::ForceSingleCoilRetMessage ret = mb->write05(dev->mbaddr,p->mbreg+p->offset,p->mbval);
}
break;
case ModbusRTU::fnForceMultipleCoils:
{
if( !p->mb_init )
{
// every register ask... (for mb_init_mbreg no some)
for( int i=0; i<p->q_count; i++,it++ )
{
ModbusRTU::ReadInputStatusRetMessage ret1 = mb->read02(dev->mbaddr,it->second->mb_init_mbreg,1);
ModbusRTU::DataBits b(ret1.data[0]);
it->second->mbval = b[0] ? 1 : 0;
it->second->sm_init = true;
}
/*
// alone query for all register (if mb_init_mbreg ++ )
ModbusRTU::ReadInputStatusRetMessage ret1 = mb->read02(dev->mbaddr,p->mb_init_mbreg,p->q_count);
int m=0;
for( int i=0; i<ret1.bcnt; i++ )
{
ModbusRTU::DataBits b(ret1.data[i]);
for( int k=0;k<ModbusRTU::BitsPerByte && m<p->q_count; k++,it++,m++ )
{
it->second->mbval = b[k] ? 1 : 0;
it->second->sm_init = true;
}
}
*/
p->sm_init = true;
it--;
return true;
}
ModbusRTU::ForceCoilsMessage msg(dev->mbaddr,p->mbreg+p->offset);
for( int i=0; i<p->q_count; i++,it++ )
msg.addBit( (it->second->mbval ? true : false) );
it--;
// cerr << "*********** (write multiple): " << msg << endl;
ModbusRTU::ForceCoilsRetMessage ret = mb->write0F(msg);
}
break;
mb = initMB(true);
ptReopen.reset();
default:
{
if( dlog.debugging(Debug::WARN) )
dlog[Debug::WARN] << myname << "(pollRTU): mbreg=" << ModbusRTU::dat2str(p->mbreg)
<< " IGNORE mfunc=" << (int)p->mbfunc << " ..." << endl;
return false;
}
break;
}
// printMap(rmap);
return true;
}
// -----------------------------------------------------------------------------
RTUExchange* RTUExchange::init_rtuexchange( int argc, const char* const* argv, UniSetTypes::ObjectId icID,
SharedMemory* ic, const std::string prefix )
bool RTUExchange::RTUDevice::checkRespond()
{
string name = conf->getArgParam("--" + prefix + "-name","RTUExchange1");
if( name.empty() )
bool prev = resp_state;
if( resp_trTimeout.hi(resp_real) )
{
cerr << "(rtuexchange): Unknown 'name'. Use --" << prefix << "-name" << endl;
return 0;
if( resp_real )
resp_state = true;
resp_ptTimeout.reset();
}
ObjectId ID = conf->getObjectID(name);
if( ID == UniSetTypes::DefaultObjectId )
if( resp_state && !resp_real && resp_ptTimeout.checkTime() )
resp_state = false;
// если ещё не инициализировали значение в SM
// то возвращаем true, чтобы оно принудительно сохранилось
if( !resp_init )
{
cerr << "(rtuexchange): Not found ID for '" << name
<< "'!"
<< " in section <" << conf->getObjectsSection() << ">" << endl;
return 0;
resp_state = resp_real;
resp_init = true;
prev = resp_state;
return true;
}
dlog[Debug::INFO] << "(rtuexchange): name = " << name << "(" << ID << ")" << endl;
return new RTUExchange(ID,icID,ic,prefix);
return ( prev != resp_state );
}
// -----------------------------------------------------------------------------
bool RTUExchange::initDeviceInfo( RTUDeviceMap& m, ModbusRTU::ModbusAddr a, UniXML_iterator& it )
void RTUExchange::updateSM()
{
if( !MBExchange::initDeviceInfo(m,a,it) )
return false;
RTUDeviceMap::iterator d = m.find(a);
if( d == m.end() )
allNotRespond = true;
for( RTUExchange::RTUDeviceMap::iterator it1=rmap.begin(); it1!=rmap.end(); ++it1 )
{
dlog[Debug::WARN] << myname << "(initDeviceInfo): not found device for addr=" << ModbusRTU::addr2str(a) << endl;
return false;
RTUDevice* d(it1->second);
if( dlog.debugging(Debug::LEVEL4) )
{
dlog[Debug::LEVEL4] << "check respond addr=" << ModbusRTU::addr2str(d->mbaddr)
<< " respond_id=" << d->resp_id
<< " real=" << d->resp_real
<< " state=" << d->resp_state
<< endl;
}
if( d->resp_real )
allNotRespond = false;
// update respond sensors...
if( d->checkRespond() && d->resp_id != DefaultObjectId )
{
try
{
bool set = d->resp_invert ? !d->resp_state : d->resp_state;
shm->localSaveState(d->resp_dit,d->resp_id,set,getId());
}
catch(Exception& ex)
{
dlog[Debug::CRIT] << myname
<< "(step): (respond) " << ex << std::endl;
}
}
// cerr << "*********** allNotRespond=" << allNotRespond << endl;
// update values...
for( RTUExchange::RegMap::iterator it=d->regmap.begin(); it!=d->regmap.end(); ++it )
{
try
{
if( d->dtype == dtRTU )
updateRTU(it);
else if( d->dtype == dtMTR )
updateMTR(it);
else if( d->dtype == dtRTU188 )
updateRTU188(it);
}
catch(IOController_i::NameNotFound &ex)
{
dlog[Debug::LEVEL3] << myname << "(updateSM):(NameNotFound) " << ex.err << endl;
}
catch(IOController_i::IOBadParam& ex )
{
dlog[Debug::LEVEL3] << myname << "(updateSM):(IOBadParam) " << ex.err << endl;
}
catch(IONotifyController_i::BadRange )
{
dlog[Debug::LEVEL3] << myname << "(updateSM): (BadRange)..." << endl;
}
catch( Exception& ex )
{
dlog[Debug::LEVEL3] << myname << "(updateSM): " << ex << endl;
}
catch(CORBA::SystemException& ex)
{
dlog[Debug::LEVEL3] << myname << "(updateSM): CORBA::SystemException: "
<< ex.NP_minorString() << endl;
}
catch(...)
{
dlog[Debug::LEVEL3] << myname << "(updateSM): catch ..." << endl;
}
if( it==d->regmap.end() )
break;
}
}
string s = it.getProp("speed");
if( !s.empty() )
}
// -----------------------------------------------------------------------------
void RTUExchange::processingMessage(UniSetTypes::VoidMessage *msg)
{
try
{
d->second->speed = ComPort::getSpeed(s);
if( d->second->speed == ComPort::ComSpeed0 )
switch(msg->type)
{
d->second->speed = defSpeed;
dlog[Debug::CRIT] << myname << "(initDeviceInfo): Unknown speed=" << s <<
" for addr=" << ModbusRTU::addr2str(a) << endl;
return false;
case UniSetTypes::Message::SysCommand:
{
UniSetTypes::SystemMessage sm( msg );
sysCommand( &sm );
}
break;
case Message::Timer:
{
TimerMessage tm(msg);
timerInfo(&tm);
}
break;
case Message::SensorInfo:
{
SensorMessage sm( msg );
sensorInfo(&sm);
}
break;
default:
break;
}
}
else
d->second->speed = defSpeed;
return true;
catch( SystemError& ex )
{
dlog[Debug::CRIT] << myname << "(SystemError): " << ex << std::endl;
// throw SystemError(ex);
}
catch( Exception& ex )
{
dlog[Debug::CRIT] << myname << "(processingMessage): " << ex << std::endl;
}
catch(...)
{
dlog[Debug::CRIT] << myname << "(processingMessage): catch ...\n";
}
}
// -----------------------------------------------------------------------------
void RTUExchange::sysCommand( UniSetTypes::SystemMessage *sm )
{
switch( sm->command )
{
case SystemMessage::StartUp:
{
if( rmap.empty() )
{
dlog[Debug::CRIT] << myname << "(sysCommand): ************* rmap EMPTY! terminated... *************" << endl;
raise(SIGTERM);
return;
}
if( dlog.debugging(Debug::INFO) )
dlog[Debug::INFO] << myname << "(sysCommand): rmap size= " << rmap.size() << endl;
if( !shm->isLocalwork() )
initDeviceList();
waitSMReady();
// подождать пока пройдёт инициализация датчиков
// см. activateObject()
msleep(initPause);
PassiveTimer ptAct(activateTimeout);
while( !activated && !ptAct.checkTime() )
{
cout << myname << "(sysCommand): wait activate..." << endl;
msleep(300);
if( activated )
break;
}
if( !activated )
dlog[Debug::CRIT] << myname << "(sysCommand): ************* don`t activate?! ************" << endl;
{
UniSetTypes::uniset_mutex_lock l(mutex_start, 10000);
askSensors(UniversalIO::UIONotify);
initOutput();
}
// начальная инициализация
if( !force )
{
uniset_mutex_lock l(pollMutex,2000);
force = true;
poll();
force = false;
}
askTimer(tmExchange,polltime);
break;
}
case SystemMessage::FoldUp:
case SystemMessage::Finish:
askSensors(UniversalIO::UIODontNotify);
break;
case SystemMessage::WatchDog:
{
// ОПТИМИЗАЦИЯ (защита от двойного перезаказа при старте)
// Если идёт локальная работа
// (т.е. RTUExchange запущен в одном процессе с SharedMemory2)
// то обрабатывать WatchDog не надо, т.к. мы и так ждём готовности SM
// при заказе датчиков, а если SM вылетит, то вместе с этим процессом(RTUExchange)
if( shm->isLocalwork() )
break;
askSensors(UniversalIO::UIONotify);
initOutput();
if( !force )
{
uniset_mutex_lock l(pollMutex,2000);
force = true;
poll();
force = false;
}
}
break;
case SystemMessage::LogRotate:
{
// переоткрываем логи
unideb << myname << "(sysCommand): logRotate" << std::endl;
string fname = unideb.getLogFile();
if( !fname.empty() )
{
unideb.logFile(fname);
unideb << myname << "(sysCommand): ***************** UNIDEB LOG ROTATE *****************" << std::endl;
}
dlog << myname << "(sysCommand): logRotate" << std::endl;
fname = dlog.getLogFile();
if( !fname.empty() )
{
dlog.logFile(fname);
dlog << myname << "(sysCommand): ***************** dlog LOG ROTATE *****************" << std::endl;
}
}
break;
default:
break;
}
}
// ------------------------------------------------------------------------------------------
void RTUExchange::initOutput()
{
}
// ------------------------------------------------------------------------------------------
void RTUExchange::askSensors( UniversalIO::UIOCommand cmd )
{
if( !shm->waitSMworking(test_id,activateTimeout,50) )
{
ostringstream err;
err << myname
<< "(askSensors): Не дождались готовности(work) SharedMemory к работе в течение "
<< activateTimeout << " мсек";
dlog[Debug::CRIT] << err.str() << endl;
kill(SIGTERM,getpid()); // прерываем (перезапускаем) процесс...
throw SystemError(err.str());
}
if( force_out )
return;
for( RTUExchange::RTUDeviceMap::iterator it1=rmap.begin(); it1!=rmap.end(); ++it1 )
{
RTUDevice* d(it1->second);
for( RTUExchange::RegMap::iterator it=d->regmap.begin(); it!=d->regmap.end(); ++it )
{
if( !isWriteFunction(it->second->mbfunc) )
continue;
for( PList::iterator i=it->second->slst.begin(); i!=it->second->slst.end(); ++i )
{
try
{
shm->askSensor(i->si.id,cmd);
}
catch( UniSetTypes::Exception& ex )
{
dlog[Debug::WARN] << myname << "(askSensors): " << ex << std::endl;
}
catch(...)
{
dlog[Debug::WARN] << myname << "(askSensors): catch..." << std::endl;
}
}
}
}
}
// ------------------------------------------------------------------------------------------
void RTUExchange::sensorInfo( UniSetTypes::SensorMessage* sm )
{
if( force_out )
return;
for( RTUExchange::RTUDeviceMap::iterator it1=rmap.begin(); it1!=rmap.end(); ++it1 )
{
RTUDevice* d(it1->second);
for( RTUExchange::RegMap::iterator it=d->regmap.begin(); it!=d->regmap.end(); ++it )
{
if( !isWriteFunction(it->second->mbfunc) )
continue;
for( PList::iterator i=it->second->slst.begin(); i!=it->second->slst.end(); ++i )
{
if( sm->id == i->si.id && sm->node == i->si.node )
{
if( dlog.debugging(Debug::INFO) )
{
dlog[Debug::INFO] << myname<< "(sensorInfo): si.id=" << sm->id
<< " reg=" << ModbusRTU::dat2str(i->reg->mbreg)
<< " val=" << sm->value
<< " mb_init=" << i->reg->mb_init << endl;
}
if( !i->reg->mb_init )
continue;
i->value = sm->value;
updateRSProperty( &(*i),true);
return;
}
}
}
}
}
// ------------------------------------------------------------------------------------------
bool RTUExchange::activateObject()
{
// блокирование обработки Starsp
// пока не пройдёт инициализация датчиков
// см. sysCommand()
{
activated = false;
UniSetTypes::uniset_mutex_lock l(mutex_start, 5000);
UniSetObject_LT::activateObject();
if( !shm->isLocalwork() )
rtuQueryOptimization(rmap);
initIterators();
activated = true;
}
return true;
}
// ------------------------------------------------------------------------------------------
void RTUExchange::sigterm( int signo )
{
cerr << myname << ": ********* SIGTERM(" << signo <<") ********" << endl;
activated = false;
/*! \todo Нужно ли выставлять безопасное состояние. МОжно ведь не успеть совершить "обемен" */
// выставление безопасного состояния на выходы....
/*
RSMap::iterator it=rsmap.begin();
for( ; it!=rsmap.end(); ++it )
{
// if( it->stype!=UniversalIO::DigitalOutput && it->stype!=UniversalIO::AnalogOutput )
// continue;
if( it->safety == NoSafetyState )
continue;
try
{
}
catch( UniSetTypes::Exception& ex )
{
dlog[Debug::WARN] << myname << "(sigterm): " << ex << std::endl;
}
catch(...){}
}
*/
UniSetObject_LT::sigterm(signo);
}
// ------------------------------------------------------------------------------------------
void RTUExchange::readConfiguration()
{
// readconf_ok = false;
xmlNode* root = conf->getXMLSensorsSection();
if(!root)
{
ostringstream err;
err << myname << "(readConfiguration): не нашли корневого раздела <sensors>";
throw SystemError(err.str());
}
UniXML_iterator it(root);
if( !it.goChildren() )
{
std::cerr << myname << "(readConfiguration): раздел <sensors> не содержит секций ?!!\n";
return;
}
for( ;it.getCurrent(); it.goNext() )
{
if( UniSetTypes::check_filter(it,s_field,s_fvalue) )
initItem(it);
}
// readconf_ok = true;
}
// ------------------------------------------------------------------------------------------
bool RTUExchange::readItem( UniXML& xml, UniXML_iterator& it, xmlNode* sec )
{
if( UniSetTypes::check_filter(it,s_field,s_fvalue) )
initItem(it);
return true;
}
// ------------------------------------------------------------------------------------------
RTUExchange::RTUDevice* RTUExchange::addDev( RTUDeviceMap& mp, ModbusRTU::ModbusAddr a, UniXML_iterator& xmlit )
{
RTUDeviceMap::iterator it = mp.find(a);
if( it != mp.end() )
{
DeviceType dtype = getDeviceType(xmlit.getProp("mbtype"));
if( it->second->dtype != dtype )
{
dlog[Debug::CRIT] << 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;
}
dlog[Debug::INFO] << myname << "(addDev): device for addr=" << ModbusRTU::addr2str(a)
<< " already added. Ignore device params for " << xmlit.getProp("name") << " ..." << endl;
return it->second;
}
RTUExchange::RTUDevice* d = new RTUExchange::RTUDevice();
d->mbaddr = a;
if( !initRTUDevice(d,xmlit) )
{
delete d;
return 0;
}
mp.insert(RTUDeviceMap::value_type(a,d));
return d;
}
// ------------------------------------------------------------------------------------------
RTUExchange::RegInfo* RTUExchange::addReg( RegMap& mp, ModbusRTU::ModbusData r,
UniXML_iterator& xmlit, RTUExchange::RTUDevice* dev,
RTUExchange::RegInfo* rcopy )
{
RegMap::iterator it = mp.find(r);
if( it != mp.end() )
{
if( !it->second->dev )
{
dlog[Debug::CRIT] << myname << "(addReg): for reg=" << ModbusRTU::dat2str(r)
<< " dev=0!!!! " << endl;
return 0;
}
if( it->second->dev->dtype != dev->dtype )
{
dlog[Debug::CRIT] << myname << "(addReg): OTHER mbtype=" << dev->dtype << " for reg=" << ModbusRTU::dat2str(r)
<< ". Already used devtype=" << it->second->dev->dtype << " for " << it->second->dev << endl;
return 0;
}
if( dlog.debugging(Debug::INFO) )
{
dlog[Debug::INFO] << myname << "(addReg): reg=" << ModbusRTU::dat2str(r)
<< " already added. Ignore register params for " << xmlit.getProp("name") << " ..." << endl;
}
it->second->rit = it;
return it->second;
}
RTUExchange::RegInfo* ri;
if( rcopy )
{
ri = new RTUExchange::RegInfo(*rcopy);
ri->slst.clear();
ri->mbreg = r;
}
else
{
ri = new RTUExchange::RegInfo();
if( !initRegInfo(ri,xmlit,dev) )
{
delete ri;
return 0;
}
ri->mbreg = r;
}
mp.insert(RegMap::value_type(r,ri));
ri->rit = mp.find(r);
return ri;
}
// ------------------------------------------------------------------------------------------
RTUExchange::RSProperty* RTUExchange::addProp( PList& plist, RSProperty& p )
{
for( PList::iterator it=plist.begin(); it!=plist.end(); ++it )
{
if( it->si.id == p.si.id && it->si.node == p.si.node )
return &(*it);
}
plist.push_back(p);
PList::iterator it = plist.end();
it--;
return &(*it);
}
// ------------------------------------------------------------------------------------------
bool RTUExchange::initRSProperty( RSProperty& p, UniXML_iterator& it )
{
if( !IOBase::initItem(&p,it,shm,&dlog,myname) )
return false;
string sbit(it.getProp("nbit"));
if( !sbit.empty() )
{
p.nbit = UniSetTypes::uni_atoi(sbit);
if( p.nbit < 0 || p.nbit >= ModbusRTU::BitsPerData )
{
dlog[Debug::CRIT] << myname << "(initRSProperty): BAD nbit=" << p.nbit
<< ". (0 >= nbit < " << ModbusRTU::BitsPerData <<")." << endl;
return false;
}
}
if( p.nbit > 0 &&
( p.stype == UniversalIO::AnalogInput ||
p.stype == UniversalIO::AnalogOutput ) )
{
dlog[Debug::WARN] << "(initRSProperty): (ignore) uncorrect param`s nbit>1 (" << p.nbit << ")"
<< " but iotype=" << p.stype << " for " << it.getProp("name") << endl;
}
string sbyte(it.getProp("nbyte"));
if( !sbyte.empty() )
{
p.nbyte = UniSetTypes::uni_atoi(sbyte);
if( p.nbyte < 0 || p.nbyte > VTypes::Byte::bsize )
{
dlog[Debug::CRIT] << myname << "(initRSProperty): BAD nbyte=" << p.nbyte
<< ". (0 >= nbyte < " << VTypes::Byte::bsize << ")." << endl;
return false;
}
}
string vt(it.getProp("vtype"));
if( vt.empty() )
{
p.rnum = VTypes::wsize(VTypes::vtUnknown);
p.vType = VTypes::vtUnknown;
}
else
{
VTypes::VType v(VTypes::str2type(vt));
if( v == VTypes::vtUnknown )
{
dlog[Debug::CRIT] << myname << "(initRSProperty): Unknown rtuVType=" << vt << " for "
<< it.getProp("name")
<< endl;
return false;
}
p.vType = v;
p.rnum = VTypes::wsize(v);
}
return true;
}
// ------------------------------------------------------------------------------------------
bool RTUExchange::initRegInfo( RegInfo* r, UniXML_iterator& it, RTUExchange::RTUDevice* dev )
{
r->dev = dev;
r->mbval = it.getIntProp("default");
r->offset= it.getIntProp("mboffset");
r->mb_init = it.getIntProp("mbinit");
if( dev->dtype == RTUExchange::dtMTR )
{
// only for MTR
if( !initMTRitem(it,r) )
return false;
}
else if( dev->dtype == RTUExchange::dtRTU188 )
{ // only for RTU188
if( !initRTU188item(it,r) )
return false;
}
else if( dev->dtype == RTUExchange::dtRTU )
{
}
else
{
dlog[Debug::CRIT] << myname << "(initRegInfo): Unknown mbtype='" << dev->dtype
<< "' for " << it.getProp("name") << endl;
return false;
}
if( mbregFromID )
r->mbreg = conf->getSensorID(it.getProp("name"));
else if( dev->dtype != RTUExchange::dtRTU188 )
{
string reg = it.getProp("mbreg");
if( reg.empty() )
{
dlog[Debug::CRIT] << myname << "(initRegInfo): unknown mbreg for " << it.getProp("name") << endl;
return false;
}
r->mbreg = ModbusRTU::str2mbData(reg);
}
else // if( dev->dtype == RTUExchange::dtRTU188 )
{
UniversalIO::IOTypes stype = UniSetTypes::getIOType(it.getProp("iotype"));
r->mbreg = RTUStorage::getRegister(r->rtuJack,r->rtuChan,stype);
if( r->mbreg == -1 )
{
dlog[Debug::CRIT] << myname << "(initRegInfo): (RTU188) unknown mbreg for " << it.getProp("name") << endl;
return false;
}
}
{
string sr = it.getProp("init_mbreg");
if( sr == "-1" )
{
r->mb_init = true; // OFF mb_init
r->sm_init = true;
}
else if( sr.empty() )
r->mb_init_mbreg = r->mbreg;
else
r->mb_init_mbreg = ModbusRTU::str2mbData(sr);
}
r->mbfunc = ModbusRTU::fnUnknown;
string f = it.getProp("mbfunc");
if( !f.empty() )
{
r->mbfunc = (ModbusRTU::SlaveFunctionCode)UniSetTypes::uni_atoi(f);
if( r->mbfunc == ModbusRTU::fnUnknown )
{
dlog[Debug::CRIT] << myname << "(initRegInfo): Unknown mbfunc ='" << f
<< "' for " << it.getProp("name") << endl;
return false;
}
}
return true;
}
// ------------------------------------------------------------------------------------------
RTUExchange::DeviceType RTUExchange::getDeviceType( const std::string dtype )
{
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;
}
// ------------------------------------------------------------------------------------------
bool RTUExchange::initRTUDevice( RTUDevice* d, UniXML_iterator& it )
{
d->dtype = getDeviceType(it.getProp("mbtype"));
if( d->dtype == dtUnknown )
{
dlog[Debug::CRIT] << myname << "(initRTUDevice): Unknown mbtype=" << it.getProp("mbtype")
<< ". Use: rtu | mtr | rtu188"
<< " for " << it.getProp("name") << endl;
return false;
}
string addr = it.getProp("mbaddr");
if( addr.empty() )
{
dlog[Debug::CRIT] << myname << "(initRTUDevice): Unknown mbaddr for " << it.getProp("name") << endl;
return false;
}
d->speed = defSpeed;
d->mbaddr = ModbusRTU::str2mbAddr(addr);
return true;
}
// ------------------------------------------------------------------------------------------
bool RTUExchange::initItem( UniXML_iterator& it )
{
RSProperty p;
if( !initRSProperty(p,it) )
return false;
string addr = it.getProp("mbaddr");
if( addr.empty() )
{
dlog[Debug::CRIT] << myname << "(initItem): Unknown mbaddr='" << addr << " for " << it.getProp("name") << endl;
return false;
}
ModbusRTU::ModbusAddr mbaddr = ModbusRTU::str2mbAddr(addr);
RTUDevice* dev = addDev(rmap,mbaddr,it);
if( !dev )
{
dlog[Debug::CRIT] << myname << "(initItem): " << it.getProp("name") << " CAN`T ADD for polling!" << endl;
return false;
}
ModbusRTU::ModbusData mbreg;
if( mbregFromID )
mbreg = p.si.id; // conf->getSensorID(it.getProp("name"));
else if( dev->dtype != RTUExchange::dtRTU188 )
{
string reg = it.getProp("mbreg");
if( reg.empty() )
{
dlog[Debug::CRIT] << myname << "(initRegInfo): unknown mbreg for " << it.getProp("name") << endl;
return false;
}
mbreg = ModbusRTU::str2mbData(reg);
}
else // if( dev->dtype == RTUExchange::dtRTU188 )
{
RegInfo rr;
initRegInfo(&rr,it,dev);
mbreg = RTUStorage::getRegister(rr.rtuJack,rr.rtuChan,p.stype);
if( mbreg == -1 )
{
dlog[Debug::CRIT] << myname << "(initItem): unknown mbreg for " << it.getProp("name") << endl;
return false;
}
}
RegInfo* ri = addReg(dev->regmap,mbreg,it,dev);
if( dev->dtype == dtMTR )
{
p.rnum = MTR::wsize(ri->mtrType);
if( p.rnum <= 0 )
{
dlog[Debug::CRIT] << 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 )
{
dlog[Debug::CRIT] << myname << "(initItem): FAILED! Sharing SAVE (not bit saving) to "
<< " mbreg=" << ModbusRTU::dat2str(ri->mbreg)
<< " for " << it.getProp("name") << endl;
abort(); // ABORT PROGRAM!!!!
return false;
}
if( p.nbit >= 0 && ri->slst.size() == 1 )
{
PList::iterator it2 = ri->slst.begin();
if( it2->nbit < 0 )
{
dlog[Debug::CRIT] << myname << "(initItem): FAILED! Sharing SAVE (mbreg="
<< ModbusRTU::dat2str(ri->mbreg) << " already used)!"
<< " IGNORE --> " << it.getProp("name") << endl;
abort(); // ABORT PROGRAM!!!!
return false;
}
}
}
RSProperty* p1 = addProp(ri->slst,p);
if( !p1 )
return false;
p1->reg = ri;
if( p1->rnum > 1 )
{
for( int i=1; i<p1->rnum; i++ )
{
RTUExchange::RegInfo* ri1 = addReg(dev->regmap,mbreg+i,it,dev,ri);
ri1->mb_init_mbreg = ri->mb_init_mbreg+i;
}
}
if( dev->dtype == dtRTU188 )
{
if( !dev->rtu )
dev->rtu = new RTUStorage(mbaddr);
}
return true;
}
// ------------------------------------------------------------------------------------------
bool RTUExchange::initMTRitem( UniXML_iterator& it, RegInfo* p )
{
p->mtrType = MTR::str2type(it.getProp("mtrtype"));
if( p->mtrType == MTR::mtUnknown )
{
dlog[Debug::CRIT] << myname << "(readMTRItem): Unknown mtrtype '"
<< it.getProp("mtrtype")
<< "' for " << it.getProp("name") << endl;
return false;
}
return true;
}
// ------------------------------------------------------------------------------------------
bool RTUExchange::initRTU188item( UniXML_iterator& it, RegInfo* p )
{
string jack = it.getProp("jack");
string chan = it.getProp("channel");
if( jack.empty() )
{
dlog[Debug::CRIT] << myname << "(readRTU188Item): Unknown jack='' "
<< " for " << it.getProp("name") << endl;
return false;
}
p->rtuJack = RTUStorage::s2j(jack);
if( p->rtuJack == RTUStorage::nUnknown )
{
dlog[Debug::CRIT] << myname << "(readRTU188Item): Unknown jack=" << jack
<< " for " << it.getProp("name") << endl;
return false;
}
if( chan.empty() )
{
dlog[Debug::CRIT] << myname << "(readRTU188Item): Unknown channel='' "
<< " for " << it.getProp("name") << endl;
return false;
}
p->rtuChan = UniSetTypes::uni_atoi(chan);
if( dlog.debugging(Debug::LEVEL2) )
dlog[Debug::LEVEL2] << myname << "(readRTU188Item): " << p << endl;
return true;
}
// -----------------------------------------------------------------------------
void RTUExchange::initIterators()
{
shm->initAIterator(aitHeartBeat);
for( RTUExchange::RTUDeviceMap::iterator it1=rmap.begin(); it1!=rmap.end(); ++it1 )
{
RTUDevice* d(it1->second);
shm->initDIterator(d->resp_dit);
for( RTUExchange::RegMap::iterator it=d->regmap.begin(); it!=d->regmap.end(); ++it )
{
for( PList::iterator it2=it->second->slst.begin();it2!=it->second->slst.end(); ++it2 )
{
shm->initDIterator(it2->dit);
shm->initAIterator(it2->ait);
}
}
}
}
// -----------------------------------------------------------------------------
void RTUExchange::help_print( int argc, const char* const* argv )
{
cout << "Default: prefix='rs'" << endl;
cout << "--prefix-polltime msec - Пауза между опросаом карт. По умолчанию 200 мсек." << endl;
cout << "--prefix-heartbeat-id - Данный процесс связан с указанным аналоговым heartbeat-дачиком." << endl;
cout << "--prefix-heartbeat-max - Максимальное значение heartbeat-счётчика для данного процесса. По умолчанию 10." << endl;
cout << "--prefix-ready-timeout - Время ожидания готовности SM к работе, мсек. (-1 - ждать 'вечно')" << endl;
cout << "--prefix-force - Сохранять значения в SM, независимо от, того менялось ли значение" << endl;
cout << "--prefix-initPause - Задержка перед инициализацией (время на активизация процесса)" << endl;
cout << "--prefix-sm-ready-timeout - время на ожидание старта SM" << endl;
cout << " Настройки протокола RS: " << endl;
cout << "--prefix-dev devname - файл устройства" << endl;
cout << "--prefix-speed - Скорость обмена (9600,19920,38400,57600,115200)." << endl;
cout << "--prefix-my-addr - адрес текущего узла" << endl;
cout << "--prefix-recv-timeout - Таймаут на ожидание ответа." << endl;
}
// -----------------------------------------------------------------------------
RTUExchange* RTUExchange::init_rtuexchange( int argc, const char* const* argv, UniSetTypes::ObjectId icID,
SharedMemory* ic, const std::string prefix )
{
string name = conf->getArgParam("--" + prefix + "-name","RTUExchange1");
if( name.empty() )
{
cerr << "(rtuexchange): Unknown 'name'. Use --" << prefix << "-name" << endl;
return 0;
}
ObjectId ID = conf->getObjectID(name);
if( ID == UniSetTypes::DefaultObjectId )
{
cerr << "(rtuexchange): Not found ID for '" << name
<< "'!"
<< " in section <" << conf->getObjectsSection() << ">" << endl;
return 0;
}
dlog[Debug::INFO] << "(rtuexchange): name = " << name << "(" << ID << ")" << endl;
return new RTUExchange(ID,icID,ic,prefix);
}
// -----------------------------------------------------------------------------
std::ostream& operator<<( std::ostream& os, const RTUExchange::DeviceType& dt )
{
switch(dt)
{
case RTUExchange::dtRTU:
os << "RTU";
break;
case RTUExchange::dtRTU188:
os << "RTU188";
break;
case RTUExchange::dtMTR:
os << "MTR";
break;
default:
os << "Unknown device type (" << (int)dt << ")";
break;
}
return os;
}
// -----------------------------------------------------------------------------
std::ostream& operator<<( std::ostream& os, const RTUExchange::RSProperty& p )
{
os << " (" << ModbusRTU::dat2str(p.reg->mbreg) << ")"
<< " sid=" << p.si.id
<< " stype=" << p.stype
<< " nbit=" << p.nbit
<< " nbyte=" << p.nbyte
<< " rnum=" << p.rnum
<< " safety=" << p.safety
<< " invert=" << p.invert;
if( p.stype == UniversalIO::AnalogInput || p.stype == UniversalIO::AnalogOutput )
{
os << p.cal
<< " cdiagram=" << ( p.cdiagram ? "yes" : "no" );
}
return os;
}
// -----------------------------------------------------------------------------
void RTUExchange::initDeviceList()
{
xmlNode* respNode = conf->findNode(cnode,"DeviceList");
if( respNode )
{
UniXML_iterator it1(respNode);
if( it1.goChildren() )
{
for(;it1.getCurrent(); it1.goNext() )
{
ModbusRTU::ModbusAddr a = ModbusRTU::str2mbAddr(it1.getProp("addr"));
initDeviceInfo(rmap,a,it1);
}
}
else
dlog[Debug::WARN] << myname << "(init): <DeviceList> empty section..." << endl;
}
else
dlog[Debug::WARN] << myname << "(init): <DeviceList> not found..." << endl;
}
// -----------------------------------------------------------------------------
bool RTUExchange::initDeviceInfo( RTUDeviceMap& m, ModbusRTU::ModbusAddr a, UniXML_iterator& it )
{
RTUDeviceMap::iterator d = m.find(a);
if( d == m.end() )
{
dlog[Debug::WARN] << myname << "(initDeviceInfo): not found device for addr=" << ModbusRTU::addr2str(a) << endl;
return false;
}
if( !it.getProp("respondSensor").empty() )
{
d->second->resp_id = conf->getSensorID(it.getProp("respondSensor"));
if( d->second->resp_id == DefaultObjectId )
{
dlog[Debug::CRIT] << myname << "(initDeviceInfo): not found ID for noRespondSensor=" << it.getProp("respondSensor") << endl;
return false;
}
}
dlog[Debug::INFO] << myname << "(initDeviceInfo): add addr=" << ModbusRTU::addr2str(a) << endl;
int tout = it.getPIntProp("timeout", UniSetTimer::WaitUpTime);
d->second->resp_ptTimeout.setTiming(tout);
d->second->resp_invert = it.getIntProp("invert");
string s = it.getProp("speed");
if( !s.empty() )
{
d->second->speed = ComPort::getSpeed(s);
if( d->second->speed == ComPort::ComSpeed0 )
{
d->second->speed = defSpeed;
dlog[Debug::CRIT] << myname << "(initDeviceInfo): Unknown speed=" << s <<
" for addr=" << ModbusRTU::addr2str(a) << endl;
return false;
}
}
return true;
}
// -----------------------------------------------------------------------------
void RTUExchange::printMap( RTUExchange::RTUDeviceMap& m )
{
cout << "devices: num=" << m.size() << endl;
for( RTUExchange::RTUDeviceMap::iterator it=m.begin(); it!=m.end(); ++it )
{
cout << " " << *(it->second) << endl;
}
}
// -----------------------------------------------------------------------------
std::ostream& operator<<( std::ostream& os, RTUExchange::RTUDeviceMap& m )
{
os << "devices: " << endl;
for( RTUExchange::RTUDeviceMap::iterator it=m.begin(); it!=m.end(); ++it )
{
os << " " << *(it->second) << endl;
}
return os;
}
// -----------------------------------------------------------------------------
std::ostream& operator<<( std::ostream& os, RTUExchange::RTUDevice& d )
{
os << "addr=" << ModbusRTU::addr2str(d.mbaddr)
<< " type=" << d.dtype;
os << " rtu=" << (d.rtu ? "yes" : "no" );
os << " respond_id=" << d.resp_id
<< " respond_timeout=" << d.resp_ptTimeout.getInterval()
<< " respond_state=" << d.resp_state
<< " respond_invert=" << d.resp_invert
<< endl;
os << " regs: " << endl;
for( RTUExchange::RegMap::iterator it=d.regmap.begin(); it!=d.regmap.end(); ++it )
os << " " << *(it->second) << endl;
return os;
}
// -----------------------------------------------------------------------------
std::ostream& operator<<( std::ostream& os, RTUExchange::RegInfo& r )
{
os << " mbreg=" << ModbusRTU::dat2str(r.mbreg)
<< " mbfunc=" << r.mbfunc
<< " mtrType=" << MTR::type2str(r.mtrType)
<< " jack=" << RTUStorage::j2s(r.rtuJack)
<< " chan=" << r.rtuChan
<< " q_num=" << r.q_num
<< " q_count=" << r.q_count
<< " value=" << ModbusRTU::dat2str(r.mbval) << "(" << (int)r.mbval << ")"
<< endl;
for( RTUExchange::PList::iterator it=r.slst.begin(); it!=r.slst.end(); ++it )
os << " " << (*it) << endl;
return os;
}
// -----------------------------------------------------------------------------
void RTUExchange::rtuQueryOptimization( RTUDeviceMap& m )
{
if( noQueryOptimization )
return;
dlog[Debug::INFO] << myname << "(rtuQueryOptimization): optimization..." << endl;
for( RTUExchange::RTUDeviceMap::iterator it1=m.begin(); it1!=m.end(); ++it1 )
{
RTUDevice* d(it1->second);
for( RTUExchange::RegMap::iterator it=d->regmap.begin(); it!=d->regmap.end(); ++it )
{
RTUExchange::RegMap::iterator beg = it;
ModbusRTU::ModbusData reg = it->second->mbreg + it->second->offset;
beg->second->q_num = 1;
beg->second->q_count = 1;
it++;
for( ;it!=d->regmap.end(); ++it )
{
if( (it->second->mbreg + it->second->offset - reg) > 1 )
break;
if( beg->second->mbfunc != it->second->mbfunc )
break;
beg->second->q_count++;
if( beg->second->q_count >= ModbusRTU::MAXDATALEN )
break;
reg = it->second->mbreg + it->second->offset;
it->second->q_num = beg->second->q_count;
it->second->q_count = 0;
}
// check correct function...
if( beg->second->q_count>1 && beg->second->mbfunc==ModbusRTU::fnWriteOutputSingleRegister )
{
dlog[Debug::WARN] << myname << "(rtuQueryOptimization): "
<< " optimization change func=" << ModbusRTU::fnWriteOutputSingleRegister
<< " <--> func=" << ModbusRTU::fnWriteOutputRegisters
<< " for mbaddr=" << ModbusRTU::addr2str(d->mbaddr)
<< " mbreg=" << ModbusRTU::dat2str(beg->second->mbreg);
beg->second->mbfunc = ModbusRTU::fnWriteOutputRegisters;
}
else if( beg->second->q_count>1 && beg->second->mbfunc==ModbusRTU::fnForceSingleCoil )
{
dlog[Debug::WARN] << myname << "(rtuQueryOptimization): "
<< " optimization change func=" << ModbusRTU::fnForceSingleCoil
<< " <--> func=" << ModbusRTU::fnForceMultipleCoils
<< " for mbaddr=" << ModbusRTU::addr2str(d->mbaddr)
<< " mbreg=" << ModbusRTU::dat2str(beg->second->mbreg);
beg->second->mbfunc = ModbusRTU::fnForceMultipleCoils;
}
if( it==d->regmap.end() )
break;
it--;
}
}
}
// -----------------------------------------------------------------------------
void RTUExchange::updateRTU( RegMap::iterator& rit )
{
RegInfo* r(rit->second);
for( PList::iterator it=r->slst.begin(); it!=r->slst.end(); ++it )
updateRSProperty( &(*it),false );
if( r->sm_init )
r->mb_init = true;
}
// -----------------------------------------------------------------------------
void RTUExchange::updateRSProperty( RSProperty* p, bool write_only )
{
using namespace ModbusRTU;
RegInfo* r(p->reg->rit->second);
bool save = isWriteFunction( r->mbfunc );
if( !save && write_only )
return;
try
{
if( p->vType == VTypes::vtUnknown )
{
ModbusRTU::DataBits16 b(r->mbval);
if( p->nbit >= 0 )
{
if( save && r->mb_init )
{
bool set = IOBase::processingAsDO( p, shm, force_out );
b.set(p->nbit,set);
r->mbval = b.mdata();
}
else
{
bool set = b[p->nbit];
IOBase::processingAsDI( p, set, shm, force );
}
return;
}
if( p->rnum <= 1 )
{
if( save && r->mb_init )
{
if( p->stype == UniversalIO::DigitalInput ||
p->stype == UniversalIO::DigitalOutput )
{
r->mbval = IOBase::processingAsDO( p, shm, force_out );
}
else
r->mbval = IOBase::processingAsAO( p, shm, force_out );
}
else
{
if( p->stype == UniversalIO::DigitalInput ||
p->stype == UniversalIO::DigitalOutput )
{
IOBase::processingAsDI( p, r->mbval, shm, force );
}
else
IOBase::processingAsAI( p, (signed short)r->mbval, shm, force );
}
return;
}
dlog[Debug::CRIT] << myname << "(updateRSProperty): IGNORE item: rnum=" << p->rnum
<< " > 1 ?!! for id=" << p->si.id << endl;
return;
}
else if( p->vType == VTypes::vtSigned )
{
if( save && r->mb_init )
{
if( p->stype == UniversalIO::DigitalInput ||
p->stype == UniversalIO::DigitalOutput )
{
r->mbval = (signed short)IOBase::processingAsDO( p, shm, force_out );
}
else
r->mbval = (signed short)IOBase::processingAsAO( p, shm, force_out );
}
else
{
if( p->stype == UniversalIO::DigitalInput ||
p->stype == UniversalIO::DigitalOutput )
{
IOBase::processingAsDI( p, r->mbval, shm, force );
}
else
{
IOBase::processingAsAI( p, (signed short)(r->mbval), shm, force );
}
}
return;
}
else if( p->vType == VTypes::vtUnsigned )
{
if( save && r->mb_init )
{
if( p->stype == UniversalIO::DigitalInput ||
p->stype == UniversalIO::DigitalOutput )
{
r->mbval = (unsigned short)IOBase::processingAsDO( p, shm, force_out );
}
else
r->mbval = (unsigned short)IOBase::processingAsAO( p, shm, force_out );
}
else
{
if( p->stype == UniversalIO::DigitalInput ||
p->stype == UniversalIO::DigitalOutput )
{
IOBase::processingAsDI( p, r->mbval, shm, force );
}
else
{
IOBase::processingAsAI( p, (unsigned short)r->mbval, shm, force );
}
}
return;
}
else if( p->vType == VTypes::vtByte )
{
if( p->nbyte <= 0 || p->nbyte > VTypes::Byte::bsize )
{
dlog[Debug::CRIT] << myname << "(updateRSProperty): IGNORE item: reg=" << ModbusRTU::dat2str(r->mbreg)
<< " vtype=" << p->vType << " but nbyte=" << p->nbyte << endl;
return;
}
if( save && r->mb_init )
{
long v = IOBase::processingAsAO( p, shm, force_out );
VTypes::Byte b(r->mbval);
b.raw.b[p->nbyte-1] = v;
r->mbval = b.raw.w;
}
else
{
VTypes::Byte b(r->mbval);
IOBase::processingAsAI( p, b.raw.b[p->nbyte-1], shm, force );
}
return;
}
else if( p->vType == VTypes::vtF2 )
{
RegMap::iterator i(p->reg->rit);
if( save && r->mb_init )
{
float f = IOBase::processingFasAO( p, shm, force_out );
VTypes::F2 f2(f);
for( int k=0; k<VTypes::F2::wsize(); k++, i++ )
i->second->mbval = f2.raw.v[k];
}
else
{
ModbusRTU::ModbusData* data = new ModbusRTU::ModbusData[VTypes::F2::wsize()];
for( int k=0; k<VTypes::F2::wsize(); k++, i++ )
data[k] = i->second->mbval;
VTypes::F2 f(data,VTypes::F2::wsize());
delete[] data;
IOBase::processingFasAI( p, (float)f, shm, force );
}
}
else if( p->vType == VTypes::vtF4 )
{
RegMap::iterator i(p->reg->rit);
if( save && r->mb_init )
{
float f = IOBase::processingFasAO( p, shm, force_out );
VTypes::F4 f4(f);
for( int k=0; k<VTypes::F4::wsize(); k++, i++ )
i->second->mbval = f4.raw.v[k];
}
else
{
ModbusRTU::ModbusData* data = new ModbusRTU::ModbusData[VTypes::F4::wsize()];
for( int k=0; k<VTypes::F4::wsize(); k++, i++ )
data[k] = i->second->mbval;
VTypes::F4 f(data,VTypes::F4::wsize());
delete[] data;
IOBase::processingFasAI( p, (float)f, shm, force );
}
}
}
catch(IOController_i::NameNotFound &ex)
{
dlog[Debug::LEVEL3] << myname << "(updateRSProperty):(NameNotFound) " << ex.err << endl;
}
catch(IOController_i::IOBadParam& ex )
{
dlog[Debug::LEVEL3] << myname << "(updateRSProperty):(IOBadParam) " << ex.err << endl;
}
catch(IONotifyController_i::BadRange )
{
dlog[Debug::LEVEL3] << myname << "(updateRSProperty): (BadRange)..." << endl;
}
catch( Exception& ex )
{
dlog[Debug::LEVEL3] << myname << "(updateRSProperty): " << ex << endl;
}
catch(CORBA::SystemException& ex)
{
dlog[Debug::LEVEL3] << myname << "(updateRSProperty): CORBA::SystemException: "
<< ex.NP_minorString() << endl;
}
catch(...)
{
dlog[Debug::LEVEL3] << myname << "(updateRSProperty): catch ..." << endl;
}
}
// -----------------------------------------------------------------------------
void RTUExchange::updateMTR( RegMap::iterator& rit )
{
RegInfo* r(rit->second);
using namespace ModbusRTU;
bool save = isWriteFunction( r->mbfunc );
{
for( PList::iterator it=r->slst.begin(); it!=r->slst.end(); ++it )
{
try
{
if( r->mtrType == MTR::mtT1 )
{
if( save )
r->mbval = IOBase::processingAsAO( &(*it), shm, force_out );
else
{
MTR::T1 t(r->mbval);
IOBase::processingAsAI( &(*it), t.val, shm, force );
}
continue;
}
if( r->mtrType == MTR::mtT2 )
{
if( save )
{
MTR::T2 t(IOBase::processingAsAO( &(*it), shm, force_out ));
r->mbval = t.val;
}
else
{
MTR::T2 t(r->mbval);
IOBase::processingAsAI( &(*it), t.val, shm, force );
}
continue;
}
if( r->mtrType == MTR::mtT3 )
{
RegMap::iterator i(rit);
if( save )
{
MTR::T3 t(IOBase::processingAsAO( &(*it), shm, force_out ));
for( int k=0; k<MTR::T3::wsize(); k++, i++ )
i->second->mbval = t.raw.v[k];
}
else
{
ModbusRTU::ModbusData* data = new ModbusRTU::ModbusData[MTR::T3::wsize()];
for( int k=0; k<MTR::T3::wsize(); k++, i++ )
data[k] = i->second->mbval;
MTR::T3 t(data,MTR::T3::wsize());
delete[] data;
IOBase::processingAsAI( &(*it), (long)t, shm, force );
}
continue;
}
if( r->mtrType == MTR::mtT4 )
{
if( save )
cerr << myname << "(updateMTR): write (T4) reg(" << dat2str(r->mbreg) << ") to MTR NOT YET!!!" << endl;
else
{
MTR::T4 t(r->mbval);
IOBase::processingAsAI( &(*it), uni_atoi(t.sval), shm, force );
}
continue;
}
if( r->mtrType == MTR::mtT5 )
{
RegMap::iterator i(rit);
if( save )
{
MTR::T5 t(IOBase::processingAsAO( &(*it), shm, force_out ));
for( int k=0; k<MTR::T5::wsize(); k++, i++ )
i->second->mbval = t.raw.v[k];
}
else
{
ModbusRTU::ModbusData* data = new ModbusRTU::ModbusData[MTR::T5::wsize()];
for( int k=0; k<MTR::T5::wsize(); k++, i++ )
data[k] = i->second->mbval;
MTR::T5 t(data,MTR::T5::wsize());
delete[] data;
IOBase::processingFasAI( &(*it), (float)t.val, shm, force );
}
continue;
}
if( r->mtrType == MTR::mtT6 )
{
RegMap::iterator i(rit);
if( save )
{
MTR::T6 t(IOBase::processingAsAO( &(*it), shm, force_out ));
for( int k=0; k<MTR::T6::wsize(); k++, i++ )
i->second->mbval = t.raw.v[k];
}
else
{
ModbusRTU::ModbusData* data = new ModbusRTU::ModbusData[MTR::T6::wsize()];
for( int k=0; k<MTR::T6::wsize(); k++, i++ )
data[k] = i->second->mbval;
MTR::T6 t(data,MTR::T6::wsize());
delete[] data;
IOBase::processingFasAI( &(*it), (float)t.val, shm, force );
}
continue;
}
if( r->mtrType == MTR::mtT7 )
{
RegMap::iterator i(rit);
if( save )
{
MTR::T7 t(IOBase::processingAsAO( &(*it), shm, force_out ));
for( int k=0; k<MTR::T7::wsize(); k++, i++ )
i->second->mbval = t.raw.v[k];
}
else
{
ModbusRTU::ModbusData* data = new ModbusRTU::ModbusData[MTR::T7::wsize()];
for( int k=0; k<MTR::T7::wsize(); k++, i++ )
data[k] = i->second->mbval;
MTR::T7 t(data,MTR::T7::wsize());
delete[] data;
IOBase::processingFasAI( &(*it), (float)t.val, shm, force );
}
continue;
}
if( r->mtrType == MTR::mtT16 )
{
if( save )
{
MTR::T16 t(IOBase::processingFasAO( &(*it), shm, force_out ));
r->mbval = t.val;
}
else
{
MTR::T16 t(r->mbval);
IOBase::processingFasAI( &(*it), t.fval, shm, force );
}
continue;
}
if( r->mtrType == MTR::mtT17 )
{
if( save )
{
MTR::T17 t(IOBase::processingFasAO( &(*it), shm, force_out ));
r->mbval = t.val;
}
else
{
MTR::T17 t(r->mbval);
IOBase::processingFasAI( &(*it), t.fval, shm, force );
}
continue;
}
if( r->mtrType == MTR::mtF1 )
{
RegMap::iterator i(rit);
if( save )
{
float f = IOBase::processingFasAO( &(*it), shm, force_out );
MTR::F1 f1(f);
for( int k=0; k<MTR::F1::wsize(); k++, i++ )
i->second->mbval = f1.raw.v[k];
}
else
{
ModbusRTU::ModbusData* data = new ModbusRTU::ModbusData[MTR::F1::wsize()];
for( int k=0; k<MTR::F1::wsize(); k++, i++ )
data[k] = i->second->mbval;
MTR::F1 t(data,MTR::F1::wsize());
delete[] data;
IOBase::processingFasAI( &(*it), (float)t, shm, force );
}
continue;
}
}
catch(IOController_i::NameNotFound &ex)
{
dlog[Debug::LEVEL3] << myname << "(updateMTR):(NameNotFound) " << ex.err << endl;
}
catch(IOController_i::IOBadParam& ex )
{
dlog[Debug::LEVEL3] << myname << "(updateMTR):(IOBadParam) " << ex.err << endl;
}
catch(IONotifyController_i::BadRange )
{
dlog[Debug::LEVEL3] << myname << "(updateMTR): (BadRange)..." << endl;
}
catch( Exception& ex )
{
dlog[Debug::LEVEL3] << myname << "(updateMTR): " << ex << endl;
}
catch(CORBA::SystemException& ex)
{
dlog[Debug::LEVEL3] << myname << "(updateMTR): CORBA::SystemException: "
<< ex.NP_minorString() << endl;
}
catch(...)
{
dlog[Debug::LEVEL3] << myname << "(updateMTR): catch ..." << endl;
}
}
}
}
// -----------------------------------------------------------------------------
void RTUExchange::updateRTU188( RegMap::iterator& it )
{
RegInfo* r(it->second);
if( !r->dev->rtu )
return;
using namespace ModbusRTU;
// bool save = false;
if( isWriteFunction(r->mbfunc) )
{
// save = true;
cerr << myname << "(updateRTU188): write reg(" << dat2str(r->mbreg) << ") to RTU188 NOT YET!!!" << endl;
return;
}
for( PList::iterator it=r->slst.begin(); it!=r->slst.end(); ++it )
{
try
{
if( it->stype == UniversalIO::DigitalInput )
{
bool set = r->dev->rtu->getState(r->rtuJack,r->rtuChan,it->stype);
IOBase::processingAsDI( &(*it), set, shm, force );
continue;
}
if( it->stype == UniversalIO::AnalogInput )
{
long val = r->dev->rtu->getInt(r->rtuJack,r->rtuChan,it->stype);
IOBase::processingAsAI( &(*it),val, shm, force );
continue;
}
}
catch(IOController_i::NameNotFound &ex)
{
dlog[Debug::LEVEL3] << myname << "(updateRTU188):(NameNotFound) " << ex.err << endl;
}
catch(IOController_i::IOBadParam& ex )
{
dlog[Debug::LEVEL3] << myname << "(updateRTU188):(IOBadParam) " << ex.err << endl;
}
catch(IONotifyController_i::BadRange )
{
dlog[Debug::LEVEL3] << myname << "(updateRTU188): (BadRange)..." << endl;
}
catch( Exception& ex )
{
dlog[Debug::LEVEL3] << myname << "(updateRTU188): " << ex << endl;
}
catch(CORBA::SystemException& ex)
{
dlog[Debug::LEVEL3] << myname << "(updateRTU188): CORBA::SystemException: "
<< ex.NP_minorString() << endl;
}
catch(...)
{
dlog[Debug::LEVEL3] << myname << "(updateRTU188): catch ..." << endl;
}
}
}
// -----------------------------------------------------------------------------
//std::ostream& operator<<( std::ostream& os, MBTCPMaster::PList& lst )
std::ostream& RTUExchange::print_plist( std::ostream& os, RTUExchange::PList& lst )
{
os << "[ ";
for( RTUExchange::PList::const_iterator it=lst.begin(); it!=lst.end(); ++it )
os << "(" << it->si.id << ")" << conf->oind->getBaseName(conf->oind->getMapName(it->si.id)) << " ";
os << "]";
return os;
}
// -----------------------------------------------------------------------------
......@@ -5,18 +5,28 @@
#include <string>
#include <map>
#include <vector>
#include "MBExchange.h"
#include "IONotifyController.h"
#include "UniSetObject_LT.h"
#include "modbus/ModbusRTUMaster.h"
#include "PassiveTimer.h"
#include "Trigger.h"
#include "Mutex.h"
#include "Calibration.h"
#include "SMInterface.h"
#include "SharedMemory.h"
#include "MTR.h"
#include "RTUStorage.h"
#include "IOBase.h"
#include "VTypes.h"
// -----------------------------------------------------------------------------
class RTUExchange:
public MBExchange
public UniSetObject_LT
{
public:
RTUExchange( UniSetTypes::ObjectId objId, UniSetTypes::ObjectId shmID,
SharedMemory* ic=0, const std::string prefix="rs" );
virtual ~RTUExchange();
/*! глобальная функция для инициализации объекта */
static RTUExchange* init_rtuexchange( int argc, const char* const* argv,
UniSetTypes::ObjectId shmID, SharedMemory* ic=0,
......@@ -25,25 +35,232 @@ class RTUExchange:
/*! глобальная функция для вывода help-а */
static void help_print( int argc, const char* const* argv );
static const int NoSafetyState=-1;
enum Timer
{
tmExchange
};
enum DeviceType
{
dtUnknown, /*!< неизвестный */
dtRTU, /*!< RTU (default) */
dtRTU188, /*!< RTU188 (Fastwell) */
dtMTR /*!< MTR (DEIF) */
};
static DeviceType getDeviceType( const std::string dtype );
friend std::ostream& operator<<( std::ostream& os, const DeviceType& dt );
// -------------------------------------------------------------------------------
struct RTUDevice;
struct RegInfo;
struct RSProperty:
public IOBase
{
// only for RTU
short nbit; /*!< bit number) */
VTypes::VType vType; /*!< type of value */
short rnum; /*!< count of registers */
short nbyte; /*!< byte number (1-2) */
RSProperty():
nbit(-1),vType(VTypes::vtUnknown),
rnum(VTypes::wsize(VTypes::vtUnknown)),
nbyte(0),reg(0)
{}
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, PList& p );
typedef std::map<ModbusRTU::ModbusData,RegInfo*> RegMap;
struct RegInfo
{
RegInfo():
mbval(0),mbreg(0),mbfunc(ModbusRTU::fnUnknown),
mtrType(MTR::mtUnknown),
rtuJack(RTUStorage::nUnknown),rtuChan(0),
dev(0),offset(0),
q_num(0),q_count(1),mb_init(false),sm_init(false),
mb_init_mbreg(0)
{}
ModbusRTU::ModbusData mbval;
ModbusRTU::ModbusData mbreg; /*!< регистр */
ModbusRTU::SlaveFunctionCode mbfunc; /*!< функция для чтения/записи */
PList slst;
// only for MTR
MTR::MTRType mtrType; /*!< тип регистра (согласно спецификации на MTR) */
// only for RTU188
RTUStorage::RTUJack rtuJack;
int rtuChan;
RTUDevice* dev;
int offset;
// optimization
int q_num; /*! number in query */
int q_count; /*! count registers for query */
RegMap::iterator rit;
bool mb_init; /*!< init before use */
bool sm_init; /*!< SM init value */
ModbusRTU::ModbusData mb_init_mbreg; /*!< mb_init register */
};
friend std::ostream& operator<<( std::ostream& os, RegInfo& r );
struct RTUDevice
{
RTUDevice():
speed(ComPort::ComSpeed38400),
respnond(false),
mbaddr(0),
dtype(dtUnknown),
resp_id(UniSetTypes::DefaultObjectId),
resp_state(false),
resp_invert(false),
resp_real(false),
resp_init(false),
rtu(0)
{
resp_trTimeout.change(false);
}
ComPort::Speed speed;
bool respnond;
ModbusRTU::ModbusAddr mbaddr; /*!< адрес устройства */
RegMap regmap;
DeviceType dtype; /*!< тип устройства */
UniSetTypes::ObjectId resp_id;
IOController::DIOStateList::iterator resp_dit;
PassiveTimer resp_ptTimeout;
Trigger resp_trTimeout;
bool resp_state;
bool resp_invert;
bool resp_real;
bool resp_init;
RTUStorage* rtu;
// return TRUE if state changed
bool checkRespond();
};
friend std::ostream& operator<<( std::ostream& os, RTUDevice& d );
typedef std::map<ModbusRTU::ModbusAddr,RTUDevice*> RTUDeviceMap;
friend std::ostream& operator<<( std::ostream& os, RTUDeviceMap& d );
void printMap(RTUDeviceMap& d);
// ----------------------------------
protected:
ModbusRTUMaster* mbrtu;
RTUDeviceMap rmap;
ModbusRTUMaster* mb;
UniSetTypes::uniset_mutex mbMutex;
std::string devname;
ComPort::Speed defSpeed;
bool use485F;
bool transmitCtl;
virtual void step();
virtual void poll();
xmlNode* cnode;
std::string s_field;
std::string s_fvalue;
SMInterface* shm;
void step();
void poll();
bool pollRTU( RTUDevice* dev, RegMap::iterator& it );
void updateSM();
void updateRTU(RegMap::iterator& it);
void updateMTR(RegMap::iterator& it);
void updateRTU188(RegMap::iterator& it);
void updateRSProperty( RSProperty* p, bool write_only=false );
virtual void processingMessage( UniSetTypes::VoidMessage *msg );
void sysCommand( UniSetTypes::SystemMessage *msg );
void sensorInfo( UniSetTypes::SensorMessage*sm );
void timerInfo( UniSetTypes::TimerMessage *tm );
void askSensors( UniversalIO::UIOCommand cmd );
void initOutput();
void waitSMReady();
virtual bool activateObject();
virtual ModbusClient* initMB( bool reopen=false );
virtual bool initDeviceInfo( RTUDeviceMap& m, ModbusRTU::ModbusAddr a, UniXML_iterator& it );
// действия при завершении работы
virtual void sigterm( int signo );
void initMB( bool reopen=false );
void initIterators();
bool initItem( UniXML_iterator& it );
bool readItem( UniXML& xml, UniXML_iterator& it, xmlNode* sec );
void initDeviceList();
void initOffsetList();
RTUDevice* addDev( RTUDeviceMap& dmap, ModbusRTU::ModbusAddr a, UniXML_iterator& it );
RegInfo* addReg( RegMap& rmap, ModbusRTU::ModbusData r, UniXML_iterator& it,
RTUDevice* dev, RegInfo* rcopy=0 );
RSProperty* addProp( PList& plist, RSProperty& p );
bool initMTRitem( UniXML_iterator& it, RegInfo* p );
bool initRTU188item( UniXML_iterator& it, RegInfo* p );
bool initRSProperty( RSProperty& p, UniXML_iterator& it );
bool initRegInfo( RegInfo* r, UniXML_iterator& it, RTUDevice* dev );
bool initRTUDevice( RTUDevice* d, UniXML_iterator& it );
bool initDeviceInfo( RTUDeviceMap& m, ModbusRTU::ModbusAddr a, UniXML_iterator& it );
void rtuQueryOptimization( RTUDeviceMap& m );
void readConfiguration();
private:
RTUExchange();
bool initPause;
UniSetTypes::uniset_mutex mutex_start;
bool force; /*!< флаг означающий, что надо сохранять в SM, даже если значение не менялось */
bool force_out; /*!< флаг означающий, принудительного чтения выходов */
bool mbregFromID;
int polltime; /*!< переодичность обновления данных, [мсек] */
timeout_t sleepPause_usec;
PassiveTimer ptHeartBeat;
UniSetTypes::ObjectId sidHeartBeat;
int maxHeartBeat;
IOController::AIOStateList::iterator aitHeartBeat;
UniSetTypes::ObjectId test_id;
UniSetTypes::uniset_mutex pollMutex;
bool activated;
int activateTimeout;
bool rs_pre_clean;
bool noQueryOptimization;
bool allNotRespond;
Trigger trAllNotRespond;
PassiveTimer ptAllNotRespond;
std::string prefix;
};
// -----------------------------------------------------------------------------
#endif // _RS_EXCHANGE_H_
......
......@@ -6,6 +6,6 @@ includedir=@includedir@
Name: libUniSetMBTCPMaster
Description: Support library for UniSetModbusTCPMaster
Requires: libUniSetExtensions libUniSetSharedMemory
Version: 1.3.1
Version: 1.2.1
Libs: -L${libdir} -lUniSetMBTCPMaster
Cflags: -I${includedir}/uniset
......@@ -6,6 +6,6 @@ includedir=@includedir@
Name: libUniSetRTU
Description: Support library for UniSetRTUExchange
Requires: libUniSetExtensions
Version: 2.1.0
Version: 2.0.0
Libs: -L${libdir} -lUniSetRTU
Cflags: -I${includedir}/uniset
......@@ -30,9 +30,9 @@ int main( int argc, const char** argv )
string logfilename(conf->getArgParam("--mbtcp-logfile"));
if( logfilename.empty() )
logfilename = "mbtcpmaster.log";
conf->initDebug(dlog,"dlog");
std::ostringstream logname;
string dir(conf->getLogDir());
logname << dir << logfilename;
......@@ -62,7 +62,7 @@ int main( int argc, const char** argv )
ObjectsActivator act;
act.addObject(static_cast<class UniSetObject*>(mb));
SystemMessage sm(SystemMessage::StartUp);
SystemMessage sm(SystemMessage::StartUp);
act.broadcast( sm.transport_msg() );
unideb(Debug::ANY) << "\n\n\n";
......@@ -70,7 +70,7 @@ int main( int argc, const char** argv )
dlog(Debug::ANY) << "\n\n\n";
dlog[Debug::ANY] << "(main): -------------- MBTCP Exchange START -------------------------\n\n";
act.run(false);
while( waitpid(-1, 0, 0) > 0 );
while( waitpid(-1, 0, 0) > 0 );
return 0;
}
catch( Exception& ex )
......@@ -82,6 +82,6 @@ int main( int argc, const char** argv )
dlog[Debug::CRIT] << "(mbtcpmaster): catch ..." << std::endl;
}
while( waitpid(-1, 0, 0) > 0 );
while( waitpid(-1, 0, 0) > 0 );
return 1;
}
......@@ -72,7 +72,7 @@ enum Command
static char* checkArg( int ind, int argc, char* argv[] );
// --------------------------------------------------------------------------
int main( int argc, char **argv )
{
{
Command cmd = cmdNOP;
int optindex = 0;
int opt = 0;
......@@ -88,21 +88,21 @@ int main( int argc, char **argv )
ModbusRTU::ModbusAddr end = 255;
int tout = 20;
DebugStream dlog;
//string tofile("");
string tofile("");
int use485 = 0;
ComPort::StopBits sbits = ComPort::OneBit;
ComPort::Parity parity = ComPort::NoParity;
// ModbusRTU::ModbusAddr b=255;
//
//
// cout << "b=" << (int)b << " b++=" << (int)(b++) << endl;
// return 0;
try
{
while( (opt = getopt_long(argc, argv, "hvw:r:x:d:s:t:l:n:yb:e:x:z:",longopts,&optindex)) != -1 )
while( (opt = getopt_long(argc, argv, "hvw:r:x:d:s:t:l:n:yb:e:x:z:",longopts,&optindex)) != -1 )
{
switch (opt)
switch (opt)
{
case 'h':
print_help();
......@@ -112,7 +112,7 @@ int main( int argc, char **argv )
cmd = cmdRead;
slaveaddr = ModbusRTU::str2mbAddr(optarg);
break;
case 'w':
cmd = cmdSave;
slaveaddr = ModbusRTU::str2mbAddr( optarg );
......@@ -134,7 +134,7 @@ int main( int argc, char **argv )
cmd = cmdGetSerial;
slaveaddr = ModbusRTU::str2mbAddr(optarg);
break;
case 'y':
use485 = 1;
break;
......@@ -168,7 +168,7 @@ int main( int argc, char **argv )
case 'b':
beg = atoi(optarg);
break;
case 'e':
end = atoi(optarg);
break;
......@@ -180,12 +180,12 @@ int main( int argc, char **argv )
case 'v':
verb = 1;
break;
case 'l':
{
if( cmd == cmdNOP )
cmd = cmdDetectSlave;
if( !checkArg(optind,argc,argv) )
break;
reg = ModbusRTU::str2mbData(argv[optind+2]);
......@@ -200,9 +200,9 @@ int main( int argc, char **argv )
{
if( cmd == cmdNOP )
cmd = cmdDetectSpeed;
slaveaddr = ModbusRTU::str2mbAddr(optarg);
if( !checkArg(optind,argc,argv) )
break;
......@@ -226,20 +226,20 @@ int main( int argc, char **argv )
{
cout << "(init): dev=" << dev << " speed=" << speed
<< " timeout=" << tout << " msec "
<< endl;
}
<< endl;
}
ModbusRTUMaster mb(dev,use485);
if( verb )
dlog.addLevel( Debug::type(Debug::CRIT | Debug::WARN | Debug::INFO) );
mb.setTimeout(tout);
mb.setSpeed(speed);
mb.setParity(parity);
mb.setStopBits(sbits);
mb.setLog(dlog);
switch(cmd)
{
case cmdRead:
......@@ -290,11 +290,11 @@ int main( int argc, char **argv )
cout << "(mtr-setup): autodetect slave: "
<< " beg=" << ModbusRTU::addr2str(beg)
<< " end=" << ModbusRTU::addr2str(end)
<< " reg=" << ModbusRTU::dat2str(reg)
<< " fn=" << ModbusRTU::b2str(fn)
<< " reg=" << ModbusRTU::dat2str(reg)
<< " fn=" << ModbusRTU::b2str(fn)
<< endl;
}
try
{
ModbusRTU::ModbusAddr a = ModbusHelpers::autodetectSlave(&mb,beg,end,reg,fn);
......@@ -312,20 +312,20 @@ int main( int argc, char **argv )
if( verb )
{
cout << "(mtr-setup): autodetect speed: slaveaddr=" << ModbusRTU::addr2str(slaveaddr)
<< " reg=" << ModbusRTU::dat2str(reg)
<< " fn=" << ModbusRTU::b2str(fn)
<< " reg=" << ModbusRTU::dat2str(reg)
<< " fn=" << ModbusRTU::b2str(fn)
<< endl;
}
try
{
ComPort::Speed s = ModbusHelpers::autodetectSpeed(&mb,slaveaddr,reg,fn);
cout << "(mtr-setup): autodetect: slaveaddr=" << ModbusRTU::addr2str(slaveaddr)
cout << "(mtr-setup): autodetect: slaveaddr=" << ModbusRTU::addr2str(slaveaddr)
<< " speed=" << ComPort::getSpeed(s) << endl;
}
catch( UniSetTypes::TimeOut )
{
cout << "(mtr-setup): speed not autodetect for slaveaddr="
cout << "(mtr-setup): speed not autodetect for slaveaddr="
<< ModbusRTU::addr2str(slaveaddr) << endl;
}
}
......@@ -339,11 +339,11 @@ int main( int argc, char **argv )
<< " slaveaddr=" << ModbusRTU::addr2str(slaveaddr)
<< endl;
}
cout << "model: " << MTR::getModelNumber(&mb,slaveaddr) << endl;
}
break;
case cmdGetSerial:
{
if( verb )
......@@ -352,18 +352,17 @@ int main( int argc, char **argv )
<< " slaveaddr=" << ModbusRTU::addr2str(slaveaddr)
<< endl;
}
cout << "serial: " << MTR::getSerialNumber(&mb,slaveaddr) << endl;
}
break;
case cmdNOP:
default:
cerr << "No command. Use -h for help." << endl;
return 1;
}
return 0;
}
catch( ModbusRTU::mbException& ex )
{
......@@ -382,14 +381,14 @@ int main( int argc, char **argv )
cerr << "(mtr-setup): catch(...)" << endl;
}
return 1;
return 0;
}
// --------------------------------------------------------------------------
char* checkArg( int i, int argc, char* argv[] )
{
if( i<argc && (argv[i])[0]!='-' )
return argv[i];
return 0;
}
// --------------------------------------------------------------------------
......@@ -28,9 +28,9 @@ int main( int argc, char** argv )
string logfilename(conf->getArgParam("--rs-logfile"));
if( logfilename.empty() )
logfilename = "rtuexchange.log";
conf->initDebug(dlog,"dlog");
std::ostringstream logname;
string dir(conf->getLogDir());
logname << dir << logfilename;
......@@ -60,7 +60,7 @@ int main( int argc, char** argv )
ObjectsActivator act;
act.addObject(static_cast<class UniSetObject*>(rs));
SystemMessage sm(SystemMessage::StartUp);
SystemMessage sm(SystemMessage::StartUp);
act.broadcast( sm.transport_msg() );
unideb(Debug::ANY) << "\n\n\n";
......@@ -69,8 +69,8 @@ int main( int argc, char** argv )
dlog[Debug::ANY] << "(main): -------------- RTU Exchange START -------------------------\n\n";
act.run(false);
while( waitpid(-1, 0, 0) > 0 );
while( waitpid(-1, 0, 0) > 0 );
return 0;
}
catch( Exception& ex )
......@@ -82,6 +82,6 @@ int main( int argc, char** argv )
dlog[Debug::CRIT] << "(rtuexchange): catch ..." << std::endl;
}
while( waitpid(-1, 0, 0) > 0 );
while( waitpid(-1, 0, 0) > 0 );
return 1;
}
......@@ -30,7 +30,7 @@ static void print_help()
}
// --------------------------------------------------------------------------
int main( int argc, char **argv )
{
{
int optindex = 0;
int opt = 0;
int verb = 0;
......@@ -43,38 +43,38 @@ int main( int argc, char **argv )
try
{
while( (opt = getopt_long(argc, argv, "hva:d:s:t:q:",longopts,&optindex)) != -1 )
while( (opt = getopt_long(argc, argv, "hva:d:s:t:q:",longopts,&optindex)) != -1 )
{
switch (opt)
switch (opt)
{
case 'h':
print_help();
print_help();
return 0;
case 'd':
case 'd':
dev = string(optarg);
break;
case 's':
case 's':
speed = string(optarg);
break;
case 't':
case 't':
tout = uni_atoi(optarg);
break;
case 'q':
case 'q':
slaveaddr = ModbusRTU::str2mbAddr(optarg);
break;
case 'v':
case 'v':
verb = 1;
break;
case 'y':
use485 = 1;
break;
case '?':
default:
printf("? argumnet\n");
......@@ -84,31 +84,31 @@ int main( int argc, char **argv )
if( verb )
{
cout << "(init): dev=" << dev
cout << "(init): dev=" << dev
<< " speed=" << speed
<< " timeout=" << tout << " msec "
<< endl;
}
<< endl;
}
ModbusRTUMaster mb(dev,use485);
if( verb )
dlog.addLevel( Debug::type(Debug::CRIT | Debug::WARN | Debug::INFO) );
mb.setTimeout(tout);
mb.setSpeed(speed);
mb.setLog(dlog);
RTUStorage rtu(slaveaddr);
rtu.poll(&mb);
cout << rtu << endl;
for( int i=0; i<24; i++ )
cout << "UNIO1 AI" << i << ": " << rtu.getFloat( RTUStorage::nJ1, i, UniversalIO::AnalogInput ) << endl;
for( int i=0; i<24; i++ )
cout << "UNIO1 DI" << i << ": " << rtu.getState( RTUStorage::nJ1, i, UniversalIO::DigitalInput ) << endl;
return 0;
}
catch( ModbusRTU::mbException& ex )
......
......@@ -4,14 +4,11 @@ uniset-start.sh -f ./uniset-rtuexchange --confile test.xml \
--smemory-id SharedMemory \
--rs-dev /dev/cbsideA0 \
--rs-name RTUExchange \
--rs-speed 115200 \
--rs-speed 38400 \
--rs-filter-field rs \
--rs-filter-value 4 \
--rs-filter-value 1 \
--dlog-add-levels info,crit,warn,level4,level3 \
--rs-force 0 \
--rs-force-out 0 \
--rs-polltime 500 \
--rs-set-prop-prefix \
#,level3
# --rs-force 1 \
......@@ -4,19 +4,11 @@
--confile test.xml \
--mbtcp-name MBMaster1 \
--smemory-id SharedMemory \
--dlog-add-levels info,crit,warn,level4,level3,level9 \
--mbtcp-set-prop-prefix \
--mbtcp-filter-field rs \
--mbtcp-filter-value 5 \
--mbtcp-gateway-iaddr 192.168.1.42 \
--dlog-add-levels info,crit,warn,level4,level3 \
--mbtcp-filter-field mbtcp \
--mbtcp-filter-value 1 \
--mbtcp-gateway-iaddr 127.0.0.1 \
--mbtcp-gateway-port 2048 \
--mbtcp-recv-timeout 5000 \
--mbtcp-force-disconnect 1 \
--mbtcp-polltime 3000 \
--mbtcp-force-out 1 \
$*
#--mbtcp-exchange-mode-id MB1_Mode_AS \
--mbtcp-force-disconnect 1
#--mbtcp-filter-field mbtcp --mbtcp-filter-value 1
#--mbtcp-set-prop-prefix rs_ \
......@@ -9,15 +9,15 @@ using namespace VTypes;
// --------------------------------------------------------------------------
static void print_help()
{
printf("Usage: vtconv TYPE[F2|F2r|F4|I2|U2|I2r|U2r] hex1 hex2 [hex3 hex4]\n");
printf("Usage: vtconv TYPE[F2|F4|I2|U2] hex1 hex2 [hex3 hex4]\n");
}
// --------------------------------------------------------------------------
int main( int argc, const char **argv )
{
/*
VTypes::F2 f2;
f2.raw.val = 2.345;
cout << "Example(F2): float=" << f2.raw.val
<< " regs:"
<< " v[0]=" << f2.raw.v[0]
......@@ -26,7 +26,7 @@ int main( int argc, const char **argv )
VTypes::F4 f4;
f4.raw.val = 2.345123123;
cout << "Example(F4): float=" << f4.raw.val
<< " regs:"
<< " v[0]=" << f4.raw.v[0]
......@@ -34,7 +34,7 @@ int main( int argc, const char **argv )
<< " v[2]=" << f4.raw.v[2]
<< " v[3]=" << f4.raw.v[3]
<< endl;
cout << "-------------" << endl << endl;
VTypes::I2 i2;
......@@ -44,7 +44,7 @@ int main( int argc, const char **argv )
<< " v[0]=" << i2.raw.v[0]
<< " v[1]=" << i2.raw.v[1]
<< endl;
cout << "-------------" << endl << endl;
VTypes::U2 u2;
......@@ -54,11 +54,12 @@ int main( int argc, const char **argv )
<< " v[0]=" << u2.raw.v[0]
<< " v[1]=" << u2.raw.v[1]
<< endl;
cout << "-------------" << endl << endl;
// return 0;
*/
unsigned short v[4];
memset(v,0,sizeof(v));
......@@ -68,11 +69,11 @@ int main( int argc, const char **argv )
{
print_help();
return 1;
}
}
type = argv[1];
v[0] = UniSetTypes::uni_atoi(argv[2]);
if( argc>3 )
v[1] = UniSetTypes::uni_atoi(argv[3]);
......@@ -85,21 +86,14 @@ int main( int argc, const char **argv )
if( !strcmp(type,"F2") )
{
VTypes::F2 f(v,sizeof(v));
cout << "(F2): v[0]=" << v[0]
<< " v[1]=" << v[1]
<< " --> (float) " << (float)f << endl;
}
else if( !strcmp(type,"F2r") )
{
VTypes::F2r f(v,sizeof(v));
cout << "(F2r): v[0]=" << v[0]
cout << "(F2): v[0]=" << v[0]
<< " v[1]=" << v[1]
<< " --> (float) " << (float)f << endl;
}
else if( !strcmp(type,"F4") )
{
VTypes::F4 f(v,sizeof(v));
cout << "(F4): v[0]=" << v[0]
cout << "(F4): v[0]=" << v[0]
<< " v[1]=" << v[1]
<< " v[2]=" << v[2]
<< " v[3]=" << v[3]
......@@ -108,32 +102,18 @@ int main( int argc, const char **argv )
else if( !strcmp(type,"I2") )
{
VTypes::I2 i(v,sizeof(v));
cout << "(I2): v[0]=" << v[0]
<< " v[1]=" << v[1]
<< " --> (int) " << (int)i << endl;
}
else if( !strcmp(type,"I2r") )
{
VTypes::I2r i(v,sizeof(v));
cout << "(I2r): v[0]=" << v[0]
cout << "(I2): v[0]=" << v[0]
<< " v[1]=" << v[1]
<< " --> (int) " << (int)i << endl;
}
else if( !strcmp(type,"U2") )
{
VTypes::U2 i(v,sizeof(v));
cout << "(U2): v[0]=" << v[0]
<< " v[1]=" << v[1]
<< " --> (unsigned int) " << (unsigned int)i << endl;
}
else if( !strcmp(type,"U2r") )
{
VTypes::U2r i(v,sizeof(v));
cout << "(U2r): v[0]=" << v[0]
cout << "(U2): v[0]=" << v[0]
<< " v[1]=" << v[1]
<< " --> (unsigned int) " << (unsigned int)i << endl;
}
else
else
{
cout << " Unknown type: " << type << endl;
}
......
// -----------------------------------------------------------------------------
#include <cmath>
#include <sstream>
#include "Exceptions.h"
#include "Extensions.h"
#include "RTUExchange.h"
// -----------------------------------------------------------------------------
using namespace std;
using namespace UniSetTypes;
using namespace UniSetExtensions;
// -----------------------------------------------------------------------------
RTUExchange::RTUExchange( UniSetTypes::ObjectId objId, UniSetTypes::ObjectId shmId, SharedMemory* ic,
const std::string prefix_ ):
UniSetObject_LT(objId),
mb(0),
defSpeed(ComPort::ComSpeed0),
use485F(false),
transmitCtl(false),
shm(0),
initPause(0),
force(false),
force_out(false),
mbregFromID(false),
activated(false),
rs_pre_clean(false),
noQueryOptimization(false),
allNotRespond(false),
prefix(prefix_)
{
if( objId == DefaultObjectId )
throw UniSetTypes::SystemError("(RTUExchange): objId=-1?!! Use --" + prefix + "-name" );
// xmlNode* cnode = conf->getNode(myname);
cnode = conf->getNode(myname);
if( cnode == NULL )
throw UniSetTypes::SystemError("(RTUExchange): Not find conf-node for " + myname );
shm = new SMInterface(shmId,&ui,objId,ic);
UniXML_iterator it(cnode);
// определяем фильтр
s_field = conf->getArgParam("--"+prefix+"-filter-field");
s_fvalue = conf->getArgParam("--"+prefix+"-filter-value");
dlog[Debug::INFO] << myname << "(init): read fileter-field='" << s_field
<< "' filter-value='" << s_fvalue << "'" << endl;
// ---------- init RS ----------
// UniXML_iterator it(cnode);
devname = conf->getArgParam("--"+prefix+"-dev",it.getProp("device"));
if( devname.empty() )
throw UniSetTypes::SystemError(myname+"(RTUExchange): Unknown device..." );
string speed = conf->getArgParam("--"+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"));
defSpeed = ComPort::getSpeed(speed);
recv_timeout = conf->getArgPInt("--"+prefix+"-recv-timeout",it.getProp("recv_timeout"), 50);
int alltout = conf->getArgPInt("--"+prefix+"-all-timeout",it.getProp("all_timeout"), 2000);
ptAllNotRespond.setTiming(alltout);
rs_pre_clean = conf->getArgInt("--"+prefix+"-pre-clean",it.getProp("pre_clean"));
noQueryOptimization = conf->getArgInt("--"+prefix+"-no-query-optimization",it.getProp("no_query_optimization"));
mbregFromID = conf->getArgInt("--mbs-reg-from-id",it.getProp("reg_from_id"));
dlog[Debug::INFO] << myname << "(init): mbregFromID=" << mbregFromID << endl;
polltime = conf->getArgPInt("--"+prefix+"-polltime",it.getProp("polltime"), 100);
initPause = conf->getArgPInt("--"+prefix+"-initPause",it.getProp("initPause"), 3000);
force = conf->getArgInt("--"+prefix+"-force",it.getProp("force"));
force_out = conf->getArgInt("--"+prefix+"-force-out",it.getProp("force_out"));
if( shm->isLocalwork() )
{
readConfiguration();
rtuQueryOptimization(rmap);
initDeviceList();
}
else
ic->addReadItem( sigc::mem_fun(this,&RTUExchange::readItem) );
// ********** HEARTBEAT *************
string heart = conf->getArgParam("--"+prefix+"-heartbeat-id",it.getProp("heartbeat_id"));
if( !heart.empty() )
{
sidHeartBeat = conf->getSensorID(heart);
if( sidHeartBeat == DefaultObjectId )
{
ostringstream err;
err << myname << ": ID not found ('HeartBeat') for " << heart;
dlog[Debug::CRIT] << myname << "(init): " << err.str() << endl;
throw SystemError(err.str());
}
int heartbeatTime = getHeartBeatTime();
if( heartbeatTime )
ptHeartBeat.setTiming(heartbeatTime);
else
ptHeartBeat.setTiming(UniSetTimer::WaitUpTime);
maxHeartBeat = conf->getArgPInt("--"+prefix+"-heartbeat-max",it.getProp("heartbeat_max"), 10);
test_id = sidHeartBeat;
}
else
{
test_id = conf->getSensorID("TestMode_S");
if( test_id == DefaultObjectId )
{
ostringstream err;
err << myname << "(init): test_id unknown. 'TestMode_S' not found...";
dlog[Debug::CRIT] << myname << "(init): " << err.str() << endl;
throw SystemError(err.str());
}
}
dlog[Debug::INFO] << myname << "(init): test_id=" << test_id << endl;
activateTimeout = conf->getArgPInt("--"+prefix+"-activate-timeout", 20000);
initMB(false);
printMap(rmap);
// abort();
}
// -----------------------------------------------------------------------------
RTUExchange::~RTUExchange()
{
for( RTUExchange::RTUDeviceMap::iterator it1=rmap.begin(); it1!=rmap.end(); ++it1 )
{
if( it1->second->rtu )
{
delete it1->second->rtu;
it1->second->rtu = 0;
}
RTUDevice* d(it1->second);
for( RTUExchange::RegMap::iterator it=d->regmap.begin(); it!=d->regmap.end(); ++it )
delete it->second;
delete it1->second;
}
delete mb;
delete shm;
}
// -----------------------------------------------------------------------------
void RTUExchange::initMB( bool reopen )
{
if( !file_exist(devname) )
{
if( mb )
{
delete mb;
mb = 0;
}
return;
}
if( mb )
{
if( !reopen )
return;
delete mb;
mb = 0;
}
try
{
mb = new ModbusRTUMaster(devname,use485F,transmitCtl);
if( defSpeed != ComPort::ComSpeed0 )
mb->setSpeed(defSpeed);
// mb->setLog(dlog);
if( recv_timeout > 0 )
mb->setTimeout(recv_timeout);
dlog[Debug::INFO] << myname << "(init): dev=" << devname << " speed=" << ComPort::getSpeed(defSpeed) << endl;
}
catch(...)
{
if( mb )
delete mb;
mb = 0;
}
}
// -----------------------------------------------------------------------------
void RTUExchange::waitSMReady()
{
// waiting for SM is ready...
int ready_timeout = conf->getArgInt("--"+prefix+"-sm-ready-timeout","15000");
if( ready_timeout == 0 )
ready_timeout = 15000;
else if( ready_timeout < 0 )
ready_timeout = UniSetTimer::WaitUpTime;
if( !shm->waitSMready(ready_timeout,50) )
{
ostringstream err;
err << myname << "(waitSMReady): Не дождались готовности SharedMemory к работе в течение " << ready_timeout << " мсек";
dlog[Debug::CRIT] << err.str() << endl;
throw SystemError(err.str());
}
}
// -----------------------------------------------------------------------------
void RTUExchange::timerInfo( TimerMessage *tm )
{
if( tm->id == tmExchange )
step();
}
// -----------------------------------------------------------------------------
void RTUExchange::step()
{
{
uniset_mutex_lock l(pollMutex,2000);
poll();
}
if( !activated )
return;
if( sidHeartBeat!=DefaultObjectId && ptHeartBeat.checkTime() )
{
try
{
shm->localSaveValue(aitHeartBeat,sidHeartBeat,maxHeartBeat,getId());
ptHeartBeat.reset();
}
catch(Exception& ex)
{
dlog[Debug::CRIT] << myname
<< "(step): (hb) " << ex << std::endl;
}
}
}
// -----------------------------------------------------------------------------
void RTUExchange::poll()
{
if( trAllNotRespond.hi(allNotRespond) )
ptAllNotRespond.reset();
if( allNotRespond && mb && ptAllNotRespond.checkTime() )
{
ptAllNotRespond.reset();
initMB(true);
}
if( !mb )
{
initMB(false);
if( !mb )
{
for( RTUExchange::RTUDeviceMap::iterator it=rmap.begin(); it!=rmap.end(); ++it )
it->second->resp_real = false;
}
updateSM();
return;
}
ComPort::Speed s = mb->getSpeed();
for( RTUExchange::RTUDeviceMap::iterator it1=rmap.begin(); it1!=rmap.end(); ++it1 )
{
RTUDevice* d(it1->second);
if( d->speed != s )
{
s = d->speed;
mb->setSpeed(d->speed);
}
if( dlog.debugging(Debug::INFO) )
dlog[Debug::INFO] << myname << "(poll): ask addr=" << ModbusRTU::addr2str(d->mbaddr) << endl;
if( d->dtype==RTUExchange::dtRTU188 )
{
if( !d->rtu )
continue;
if( dlog.debugging(Debug::INFO) )
{
dlog[Debug::INFO] << myname << "(pollRTU188): poll RTU188 "
<< " mbaddr=" << ModbusRTU::addr2str(d->mbaddr)
<< endl;
}
try
{
if( rs_pre_clean )
mb->cleanupChannel();
d->rtu->poll(mb);
d->resp_real = true;
}
catch( ModbusRTU::mbException& ex )
{
if( d->resp_real )
{
if( dlog.debugging(Debug::LEVEL3) )
{
dlog[Debug::CRIT] << myname << "(poll): FAILED ask addr=" << ModbusRTU::addr2str(d->mbaddr)
<< " -> " << ex << endl;
}
d->resp_real = false;
}
}
}
else
{
d->resp_real = false;
for( RTUExchange::RegMap::iterator it=d->regmap.begin(); it!=d->regmap.end(); ++it )
{
try
{
if( d->dtype==RTUExchange::dtRTU || d->dtype==RTUExchange::dtMTR )
{
if( rs_pre_clean )
mb->cleanupChannel();
if( pollRTU(d,it) )
d->resp_real = true;
}
}
catch( ModbusRTU::mbException& ex )
{
// if( d->resp_real )
// {
if( dlog.debugging(Debug::LEVEL3) )
{
dlog[Debug::LEVEL3] << myname << "(poll): FAILED ask addr=" << ModbusRTU::addr2str(d->mbaddr)
<< " reg=" << ModbusRTU::dat2str(it->second->mbreg)
<< " for sensors: "; print_plist(dlog(Debug::LEVEL3), it->second->slst);
dlog(Debug::LEVEL3) << " err: " << ex << endl;
}
// d->resp_real = false;
// }
}
if( it==d->regmap.end() )
break;
}
}
}
// update SharedMemory...
updateSM();
// check thresholds
for( RTUExchange::RTUDeviceMap::iterator it1=rmap.begin(); it1!=rmap.end(); ++it1 )
{
RTUDevice* d(it1->second);
for( RTUExchange::RegMap::iterator it=d->regmap.begin(); it!=d->regmap.end(); ++it )
{
RegInfo* r(it->second);
for( PList::iterator i=r->slst.begin(); i!=r->slst.end(); ++i )
IOBase::processingThreshold( &(*i),shm,force);
}
}
// printMap(rmap);
}
// -----------------------------------------------------------------------------
bool RTUExchange::pollRTU( RTUDevice* dev, RegMap::iterator& it )
{
RegInfo* p(it->second);
if( dlog.debugging(Debug::LEVEL3) )
{
dlog[Debug::LEVEL3] << myname << "(pollRTU): poll "
<< " mbaddr=" << ModbusRTU::addr2str(dev->mbaddr)
<< " mbreg=" << ModbusRTU::dat2str(p->mbreg)
<< " mboffset=" << p->offset
<< " mbfunc=" << p->mbfunc
<< " q_count=" << p->q_count
<< " mb_init=" << p->mb_init
<< endl;
}
if( p->q_count == 0 )
{
if( dlog.debugging(Debug::INFO) )
dlog[Debug::INFO] << myname << "(pollRTU): q_count=0 for mbreg=" << ModbusRTU::dat2str(p->mbreg)
<< " IGNORE register..." << endl;
return false;
}
switch( p->mbfunc )
{
case ModbusRTU::fnReadInputRegisters:
{
ModbusRTU::ReadInputRetMessage ret = mb->read04(dev->mbaddr,p->mbreg+p->offset,p->q_count);
for( int i=0; i<p->q_count; i++,it++ )
it->second->mbval = ret.data[i];
it--;
}
break;
case ModbusRTU::fnReadOutputRegisters:
{
ModbusRTU::ReadOutputRetMessage ret = mb->read03(dev->mbaddr, p->mbreg+p->offset,p->q_count);
for( int i=0; i<p->q_count; i++,it++ )
it->second->mbval = ret.data[i];
it--;
}
break;
case ModbusRTU::fnReadInputStatus:
{
ModbusRTU::ReadInputStatusRetMessage ret = mb->read02(dev->mbaddr,p->mbreg+p->offset,p->q_count);
int m=0;
for( int i=0; i<ret.bcnt; i++ )
{
ModbusRTU::DataBits b(ret.data[i]);
for( int k=0;k<ModbusRTU::BitsPerByte && m<p->q_count; k++,it++,m++ )
it->second->mbval = b[k];
}
it--;
}
break;
case ModbusRTU::fnReadCoilStatus:
{
ModbusRTU::ReadCoilRetMessage ret = mb->read01(dev->mbaddr,p->mbreg+p->offset,p->q_count);
int m = 0;
for( int i=0; i<ret.bcnt; i++ )
{
ModbusRTU::DataBits b(ret.data[i]);
for( int k=0;k<ModbusRTU::BitsPerByte && m<p->q_count; k++,it++,m++ )
it->second->mbval = b[k] ? 1 : 0;
}
it--;
}
break;
case ModbusRTU::fnWriteOutputSingleRegister:
{
if( p->q_count != 1 )
{
dlog[Debug::CRIT] << myname << "(pollRTU): mbreg=" << ModbusRTU::dat2str(p->mbreg)
<< " IGNORE WRITE SINGLE REGISTER (0x06) q_count=" << p->q_count << " ..." << endl;
return false;
}
if( !p->mb_init )
{
// cerr << "******* mb_init: mbreg=" << ModbusRTU::dat2str(p->mbreg) << endl;
ModbusRTU::ReadInputRetMessage ret1 = mb->read04(dev->mbaddr,p->mb_init_mbreg,1);
p->mbval = ret1.data[0];
p->sm_init = true;
return true;
}
// cerr << "**** mbreg=" << ModbusRTU::dat2str(p->mbreg) << " val=" << ModbusRTU::dat2str(p->mbval) << endl;
ModbusRTU::WriteSingleOutputRetMessage ret = mb->write06(dev->mbaddr,p->mbreg+p->offset,p->mbval);
}
break;
case ModbusRTU::fnWriteOutputRegisters:
{
ModbusRTU::WriteOutputMessage msg(dev->mbaddr,p->mbreg+p->offset);
for( int i=0; i<p->q_count; i++,it++ )
{
if( !it->second->mb_init )
{
// cerr << "******* mb_init: mbreg=" << ModbusRTU::dat2str(it->second->mbreg)
// << " mb_init mbreg=" << ModbusRTU::dat2str(it->second->mb_init_mbreg) << endl;
ModbusRTU::ReadOutputRetMessage ret1 = mb->read03(dev->mbaddr,it->second->mb_init_mbreg,1);
// cerr << "******* mb_init: mbreg=" << ModbusRTU::dat2str(it->second->mbreg)
// << " mb_init mbreg=" << ModbusRTU::dat2str(it->second->mb_init_mbreg)
// << " mbval=" << ret1.data[0] << endl;
it->second->mbval = ret1.data[0];
it->second->sm_init = true;
}
msg.addData(it->second->mbval);
}
it--;
ModbusRTU::WriteOutputRetMessage ret = mb->write10(msg);
}
break;
case ModbusRTU::fnForceSingleCoil:
{
if( p->q_count != 1 )
{
dlog[Debug::CRIT] << myname << "(pollRTU): mbreg=" << ModbusRTU::dat2str(p->mbreg)
<< " IGNORE FORCE SINGLE COIL (0x05) q_count=" << p->q_count << " ..." << endl;
return false;
}
if( !p->mb_init )
{
// cerr << "******* mb_init: mbreg=" << ModbusRTU::dat2str(p->mbreg)
// << " init mbreg=" << ModbusRTU::dat2str(p->mb_init_mbreg) << endl;
ModbusRTU::ReadInputStatusRetMessage ret1 = mb->read02(dev->mbaddr,p->mb_init_mbreg,1);
ModbusRTU::DataBits b(ret1.data[0]);
// cerr << "******* mb_init_mbreg=" << ModbusRTU::dat2str(p->mb_init_mbreg)
// << " read val=" << (int)b[0] << endl;
p->mbval = b[0];
p->sm_init = true;
return true;
}
// cerr << "****(coil) mbreg=" << ModbusRTU::dat2str(p->mbreg) << " val=" << ModbusRTU::dat2str(p->mbval) << endl;
ModbusRTU::ForceSingleCoilRetMessage ret = mb->write05(dev->mbaddr,p->mbreg+p->offset,p->mbval);
}
break;
case ModbusRTU::fnForceMultipleCoils:
{
if( !p->mb_init )
{
// every register ask... (for mb_init_mbreg no some)
for( int i=0; i<p->q_count; i++,it++ )
{
ModbusRTU::ReadInputStatusRetMessage ret1 = mb->read02(dev->mbaddr,it->second->mb_init_mbreg,1);
ModbusRTU::DataBits b(ret1.data[0]);
it->second->mbval = b[0] ? 1 : 0;
it->second->sm_init = true;
}
/*
// alone query for all register (if mb_init_mbreg ++ )
ModbusRTU::ReadInputStatusRetMessage ret1 = mb->read02(dev->mbaddr,p->mb_init_mbreg,p->q_count);
int m=0;
for( int i=0; i<ret1.bcnt; i++ )
{
ModbusRTU::DataBits b(ret1.data[i]);
for( int k=0;k<ModbusRTU::BitsPerByte && m<p->q_count; k++,it++,m++ )
{
it->second->mbval = b[k] ? 1 : 0;
it->second->sm_init = true;
}
}
*/
p->sm_init = true;
it--;
return true;
}
ModbusRTU::ForceCoilsMessage msg(dev->mbaddr,p->mbreg+p->offset);
for( int i=0; i<p->q_count; i++,it++ )
msg.addBit( (it->second->mbval ? true : false) );
it--;
// cerr << "*********** (write multiple): " << msg << endl;
ModbusRTU::ForceCoilsRetMessage ret = mb->write0F(msg);
}
break;
default:
{
if( dlog.debugging(Debug::WARN) )
dlog[Debug::WARN] << myname << "(pollRTU): mbreg=" << ModbusRTU::dat2str(p->mbreg)
<< " IGNORE mfunc=" << (int)p->mbfunc << " ..." << endl;
return false;
}
break;
}
return true;
}
// -----------------------------------------------------------------------------
bool RTUExchange::RTUDevice::checkRespond()
{
bool prev = resp_state;
if( resp_trTimeout.hi(resp_real) )
{
if( resp_real )
resp_state = true;
resp_ptTimeout.reset();
}
if( resp_state && !resp_real && resp_ptTimeout.checkTime() )
resp_state = false;
// если ещё не инициализировали значение в SM
// то возвращаем true, чтобы оно принудительно сохранилось
if( !resp_init )
{
resp_state = resp_real;
resp_init = true;
prev = resp_state;
return true;
}
return ( prev != resp_state );
}
// -----------------------------------------------------------------------------
void RTUExchange::updateSM()
{
allNotRespond = true;
for( RTUExchange::RTUDeviceMap::iterator it1=rmap.begin(); it1!=rmap.end(); ++it1 )
{
RTUDevice* d(it1->second);
if( dlog.debugging(Debug::LEVEL4) )
{
dlog[Debug::LEVEL4] << "check respond addr=" << ModbusRTU::addr2str(d->mbaddr)
<< " respond_id=" << d->resp_id
<< " real=" << d->resp_real
<< " state=" << d->resp_state
<< endl;
}
if( d->resp_real )
allNotRespond = false;
// update respond sensors...
if( d->checkRespond() && d->resp_id != DefaultObjectId )
{
try
{
bool set = d->resp_invert ? !d->resp_state : d->resp_state;
shm->localSaveState(d->resp_dit,d->resp_id,set,getId());
}
catch(Exception& ex)
{
dlog[Debug::CRIT] << myname
<< "(step): (respond) " << ex << std::endl;
}
}
// cerr << "*********** allNotRespond=" << allNotRespond << endl;
// update values...
for( RTUExchange::RegMap::iterator it=d->regmap.begin(); it!=d->regmap.end(); ++it )
{
try
{
if( d->dtype == dtRTU )
updateRTU(it);
else if( d->dtype == dtMTR )
updateMTR(it);
else if( d->dtype == dtRTU188 )
updateRTU188(it);
}
catch(IOController_i::NameNotFound &ex)
{
dlog[Debug::LEVEL3] << myname << "(updateSM):(NameNotFound) " << ex.err << endl;
}
catch(IOController_i::IOBadParam& ex )
{
dlog[Debug::LEVEL3] << myname << "(updateSM):(IOBadParam) " << ex.err << endl;
}
catch(IONotifyController_i::BadRange )
{
dlog[Debug::LEVEL3] << myname << "(updateSM): (BadRange)..." << endl;
}
catch( Exception& ex )
{
dlog[Debug::LEVEL3] << myname << "(updateSM): " << ex << endl;
}
catch(CORBA::SystemException& ex)
{
dlog[Debug::LEVEL3] << myname << "(updateSM): CORBA::SystemException: "
<< ex.NP_minorString() << endl;
}
catch(...)
{
dlog[Debug::LEVEL3] << myname << "(updateSM): catch ..." << endl;
}
if( it==d->regmap.end() )
break;
}
}
}
// -----------------------------------------------------------------------------
void RTUExchange::processingMessage(UniSetTypes::VoidMessage *msg)
{
try
{
switch(msg->type)
{
case UniSetTypes::Message::SysCommand:
{
UniSetTypes::SystemMessage sm( msg );
sysCommand( &sm );
}
break;
case Message::Timer:
{
TimerMessage tm(msg);
timerInfo(&tm);
}
break;
case Message::SensorInfo:
{
SensorMessage sm( msg );
sensorInfo(&sm);
}
break;
default:
break;
}
}
catch( SystemError& ex )
{
dlog[Debug::CRIT] << myname << "(SystemError): " << ex << std::endl;
// throw SystemError(ex);
}
catch( Exception& ex )
{
dlog[Debug::CRIT] << myname << "(processingMessage): " << ex << std::endl;
}
catch(...)
{
dlog[Debug::CRIT] << myname << "(processingMessage): catch ...\n";
}
}
// -----------------------------------------------------------------------------
void RTUExchange::sysCommand( UniSetTypes::SystemMessage *sm )
{
switch( sm->command )
{
case SystemMessage::StartUp:
{
if( rmap.empty() )
{
dlog[Debug::CRIT] << myname << "(sysCommand): ************* rmap EMPTY! terminated... *************" << endl;
raise(SIGTERM);
return;
}
if( dlog.debugging(Debug::INFO) )
dlog[Debug::INFO] << myname << "(sysCommand): rmap size= " << rmap.size() << endl;
if( !shm->isLocalwork() )
initDeviceList();
waitSMReady();
// подождать пока пройдёт инициализация датчиков
// см. activateObject()
msleep(initPause);
PassiveTimer ptAct(activateTimeout);
while( !activated && !ptAct.checkTime() )
{
cout << myname << "(sysCommand): wait activate..." << endl;
msleep(300);
if( activated )
break;
}
if( !activated )
dlog[Debug::CRIT] << myname << "(sysCommand): ************* don`t activate?! ************" << endl;
{
UniSetTypes::uniset_mutex_lock l(mutex_start, 10000);
askSensors(UniversalIO::UIONotify);
initOutput();
}
// начальная инициализация
if( !force )
{
uniset_mutex_lock l(pollMutex,2000);
force = true;
poll();
force = false;
}
askTimer(tmExchange,polltime);
break;
}
case SystemMessage::FoldUp:
case SystemMessage::Finish:
askSensors(UniversalIO::UIODontNotify);
break;
case SystemMessage::WatchDog:
{
// ОПТИМИЗАЦИЯ (защита от двойного перезаказа при старте)
// Если идёт локальная работа
// (т.е. RTUExchange запущен в одном процессе с SharedMemory2)
// то обрабатывать WatchDog не надо, т.к. мы и так ждём готовности SM
// при заказе датчиков, а если SM вылетит, то вместе с этим процессом(RTUExchange)
if( shm->isLocalwork() )
break;
askSensors(UniversalIO::UIONotify);
initOutput();
if( !force )
{
uniset_mutex_lock l(pollMutex,2000);
force = true;
poll();
force = false;
}
}
break;
case SystemMessage::LogRotate:
{
// переоткрываем логи
unideb << myname << "(sysCommand): logRotate" << std::endl;
string fname = unideb.getLogFile();
if( !fname.empty() )
{
unideb.logFile(fname);
unideb << myname << "(sysCommand): ***************** UNIDEB LOG ROTATE *****************" << std::endl;
}
dlog << myname << "(sysCommand): logRotate" << std::endl;
fname = dlog.getLogFile();
if( !fname.empty() )
{
dlog.logFile(fname);
dlog << myname << "(sysCommand): ***************** dlog LOG ROTATE *****************" << std::endl;
}
}
break;
default:
break;
}
}
// ------------------------------------------------------------------------------------------
void RTUExchange::initOutput()
{
}
// ------------------------------------------------------------------------------------------
void RTUExchange::askSensors( UniversalIO::UIOCommand cmd )
{
if( !shm->waitSMworking(test_id,activateTimeout,50) )
{
ostringstream err;
err << myname
<< "(askSensors): Не дождались готовности(work) SharedMemory к работе в течение "
<< activateTimeout << " мсек";
dlog[Debug::CRIT] << err.str() << endl;
kill(SIGTERM,getpid()); // прерываем (перезапускаем) процесс...
throw SystemError(err.str());
}
if( force_out )
return;
for( RTUExchange::RTUDeviceMap::iterator it1=rmap.begin(); it1!=rmap.end(); ++it1 )
{
RTUDevice* d(it1->second);
for( RTUExchange::RegMap::iterator it=d->regmap.begin(); it!=d->regmap.end(); ++it )
{
if( !isWriteFunction(it->second->mbfunc) )
continue;
for( PList::iterator i=it->second->slst.begin(); i!=it->second->slst.end(); ++i )
{
try
{
shm->askSensor(i->si.id,cmd);
}
catch( UniSetTypes::Exception& ex )
{
dlog[Debug::WARN] << myname << "(askSensors): " << ex << std::endl;
}
catch(...)
{
dlog[Debug::WARN] << myname << "(askSensors): catch..." << std::endl;
}
}
}
}
}
// ------------------------------------------------------------------------------------------
void RTUExchange::sensorInfo( UniSetTypes::SensorMessage* sm )
{
if( force_out )
return;
for( RTUExchange::RTUDeviceMap::iterator it1=rmap.begin(); it1!=rmap.end(); ++it1 )
{
RTUDevice* d(it1->second);
for( RTUExchange::RegMap::iterator it=d->regmap.begin(); it!=d->regmap.end(); ++it )
{
if( !isWriteFunction(it->second->mbfunc) )
continue;
for( PList::iterator i=it->second->slst.begin(); i!=it->second->slst.end(); ++i )
{
if( sm->id == i->si.id && sm->node == i->si.node )
{
if( dlog.debugging(Debug::INFO) )
{
dlog[Debug::INFO] << myname<< "(sensorInfo): si.id=" << sm->id
<< " reg=" << ModbusRTU::dat2str(i->reg->mbreg)
<< " val=" << sm->value
<< " mb_init=" << i->reg->mb_init << endl;
}
if( !i->reg->mb_init )
continue;
i->value = sm->value;
updateRSProperty( &(*i),true);
return;
}
}
}
}
}
// ------------------------------------------------------------------------------------------
bool RTUExchange::activateObject()
{
// блокирование обработки Starsp
// пока не пройдёт инициализация датчиков
// см. sysCommand()
{
activated = false;
UniSetTypes::uniset_mutex_lock l(mutex_start, 5000);
UniSetObject_LT::activateObject();
if( !shm->isLocalwork() )
rtuQueryOptimization(rmap);
initIterators();
activated = true;
}
return true;
}
// ------------------------------------------------------------------------------------------
void RTUExchange::sigterm( int signo )
{
cerr << myname << ": ********* SIGTERM(" << signo <<") ********" << endl;
activated = false;
/*! \todo Нужно ли выставлять безопасное состояние. МОжно ведь не успеть совершить "обемен" */
// выставление безопасного состояния на выходы....
/*
RSMap::iterator it=rsmap.begin();
for( ; it!=rsmap.end(); ++it )
{
// if( it->stype!=UniversalIO::DigitalOutput && it->stype!=UniversalIO::AnalogOutput )
// continue;
if( it->safety == NoSafetyState )
continue;
try
{
}
catch( UniSetTypes::Exception& ex )
{
dlog[Debug::WARN] << myname << "(sigterm): " << ex << std::endl;
}
catch(...){}
}
*/
UniSetObject_LT::sigterm(signo);
}
// ------------------------------------------------------------------------------------------
void RTUExchange::readConfiguration()
{
// readconf_ok = false;
xmlNode* root = conf->getXMLSensorsSection();
if(!root)
{
ostringstream err;
err << myname << "(readConfiguration): не нашли корневого раздела <sensors>";
throw SystemError(err.str());
}
UniXML_iterator it(root);
if( !it.goChildren() )
{
std::cerr << myname << "(readConfiguration): раздел <sensors> не содержит секций ?!!\n";
return;
}
for( ;it.getCurrent(); it.goNext() )
{
if( UniSetTypes::check_filter(it,s_field,s_fvalue) )
initItem(it);
}
// readconf_ok = true;
}
// ------------------------------------------------------------------------------------------
bool RTUExchange::readItem( UniXML& xml, UniXML_iterator& it, xmlNode* sec )
{
if( UniSetTypes::check_filter(it,s_field,s_fvalue) )
initItem(it);
return true;
}
// ------------------------------------------------------------------------------------------
RTUExchange::RTUDevice* RTUExchange::addDev( RTUDeviceMap& mp, ModbusRTU::ModbusAddr a, UniXML_iterator& xmlit )
{
RTUDeviceMap::iterator it = mp.find(a);
if( it != mp.end() )
{
DeviceType dtype = getDeviceType(xmlit.getProp("mbtype"));
if( it->second->dtype != dtype )
{
dlog[Debug::CRIT] << 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;
}
dlog[Debug::INFO] << myname << "(addDev): device for addr=" << ModbusRTU::addr2str(a)
<< " already added. Ignore device params for " << xmlit.getProp("name") << " ..." << endl;
return it->second;
}
RTUExchange::RTUDevice* d = new RTUExchange::RTUDevice();
d->mbaddr = a;
if( !initRTUDevice(d,xmlit) )
{
delete d;
return 0;
}
mp.insert(RTUDeviceMap::value_type(a,d));
return d;
}
// ------------------------------------------------------------------------------------------
RTUExchange::RegInfo* RTUExchange::addReg( RegMap& mp, ModbusRTU::ModbusData r,
UniXML_iterator& xmlit, RTUExchange::RTUDevice* dev,
RTUExchange::RegInfo* rcopy )
{
RegMap::iterator it = mp.find(r);
if( it != mp.end() )
{
if( !it->second->dev )
{
dlog[Debug::CRIT] << myname << "(addReg): for reg=" << ModbusRTU::dat2str(r)
<< " dev=0!!!! " << endl;
return 0;
}
if( it->second->dev->dtype != dev->dtype )
{
dlog[Debug::CRIT] << myname << "(addReg): OTHER mbtype=" << dev->dtype << " for reg=" << ModbusRTU::dat2str(r)
<< ". Already used devtype=" << it->second->dev->dtype << " for " << it->second->dev << endl;
return 0;
}
if( dlog.debugging(Debug::INFO) )
{
dlog[Debug::INFO] << myname << "(addReg): reg=" << ModbusRTU::dat2str(r)
<< " already added. Ignore register params for " << xmlit.getProp("name") << " ..." << endl;
}
it->second->rit = it;
return it->second;
}
RTUExchange::RegInfo* ri;
if( rcopy )
{
ri = new RTUExchange::RegInfo(*rcopy);
ri->slst.clear();
ri->mbreg = r;
}
else
{
ri = new RTUExchange::RegInfo();
if( !initRegInfo(ri,xmlit,dev) )
{
delete ri;
return 0;
}
ri->mbreg = r;
}
mp.insert(RegMap::value_type(r,ri));
ri->rit = mp.find(r);
return ri;
}
// ------------------------------------------------------------------------------------------
RTUExchange::RSProperty* RTUExchange::addProp( PList& plist, RSProperty& p )
{
for( PList::iterator it=plist.begin(); it!=plist.end(); ++it )
{
if( it->si.id == p.si.id && it->si.node == p.si.node )
return &(*it);
}
plist.push_back(p);
PList::iterator it = plist.end();
it--;
return &(*it);
}
// ------------------------------------------------------------------------------------------
bool RTUExchange::initRSProperty( RSProperty& p, UniXML_iterator& it )
{
if( !IOBase::initItem(&p,it,shm,&dlog,myname) )
return false;
string sbit(it.getProp("nbit"));
if( !sbit.empty() )
{
p.nbit = UniSetTypes::uni_atoi(sbit);
if( p.nbit < 0 || p.nbit >= ModbusRTU::BitsPerData )
{
dlog[Debug::CRIT] << myname << "(initRSProperty): BAD nbit=" << p.nbit
<< ". (0 >= nbit < " << ModbusRTU::BitsPerData <<")." << endl;
return false;
}
}
if( p.nbit > 0 &&
( p.stype == UniversalIO::AnalogInput ||
p.stype == UniversalIO::AnalogOutput ) )
{
dlog[Debug::WARN] << "(initRSProperty): (ignore) uncorrect param`s nbit>1 (" << p.nbit << ")"
<< " but iotype=" << p.stype << " for " << it.getProp("name") << endl;
}
string sbyte(it.getProp("nbyte"));
if( !sbyte.empty() )
{
p.nbyte = UniSetTypes::uni_atoi(sbyte);
if( p.nbyte < 0 || p.nbyte > VTypes::Byte::bsize )
{
dlog[Debug::CRIT] << myname << "(initRSProperty): BAD nbyte=" << p.nbyte
<< ". (0 >= nbyte < " << VTypes::Byte::bsize << ")." << endl;
return false;
}
}
string vt(it.getProp("vtype"));
if( vt.empty() )
{
p.rnum = VTypes::wsize(VTypes::vtUnknown);
p.vType = VTypes::vtUnknown;
}
else
{
VTypes::VType v(VTypes::str2type(vt));
if( v == VTypes::vtUnknown )
{
dlog[Debug::CRIT] << myname << "(initRSProperty): Unknown rtuVType=" << vt << " for "
<< it.getProp("name")
<< endl;
return false;
}
p.vType = v;
p.rnum = VTypes::wsize(v);
}
return true;
}
// ------------------------------------------------------------------------------------------
bool RTUExchange::initRegInfo( RegInfo* r, UniXML_iterator& it, RTUExchange::RTUDevice* dev )
{
r->dev = dev;
r->mbval = it.getIntProp("default");
r->offset= it.getIntProp("mboffset");
r->mb_init = it.getIntProp("mbinit");
if( dev->dtype == RTUExchange::dtMTR )
{
// only for MTR
if( !initMTRitem(it,r) )
return false;
}
else if( dev->dtype == RTUExchange::dtRTU188 )
{ // only for RTU188
if( !initRTU188item(it,r) )
return false;
}
else if( dev->dtype == RTUExchange::dtRTU )
{
}
else
{
dlog[Debug::CRIT] << myname << "(initRegInfo): Unknown mbtype='" << dev->dtype
<< "' for " << it.getProp("name") << endl;
return false;
}
if( mbregFromID )
r->mbreg = conf->getSensorID(it.getProp("name"));
else if( dev->dtype != RTUExchange::dtRTU188 )
{
string reg = it.getProp("mbreg");
if( reg.empty() )
{
dlog[Debug::CRIT] << myname << "(initRegInfo): unknown mbreg for " << it.getProp("name") << endl;
return false;
}
r->mbreg = ModbusRTU::str2mbData(reg);
}
else // if( dev->dtype == RTUExchange::dtRTU188 )
{
UniversalIO::IOTypes stype = UniSetTypes::getIOType(it.getProp("iotype"));
r->mbreg = RTUStorage::getRegister(r->rtuJack,r->rtuChan,stype);
if( r->mbreg == -1 )
{
dlog[Debug::CRIT] << myname << "(initRegInfo): (RTU188) unknown mbreg for " << it.getProp("name") << endl;
return false;
}
}
{
string sr = it.getProp("init_mbreg");
if( sr == "-1" )
{
r->mb_init = true; // OFF mb_init
r->sm_init = true;
}
else if( sr.empty() )
r->mb_init_mbreg = r->mbreg;
else
r->mb_init_mbreg = ModbusRTU::str2mbData(sr);
}
r->mbfunc = ModbusRTU::fnUnknown;
string f = it.getProp("mbfunc");
if( !f.empty() )
{
r->mbfunc = (ModbusRTU::SlaveFunctionCode)UniSetTypes::uni_atoi(f);
if( r->mbfunc == ModbusRTU::fnUnknown )
{
dlog[Debug::CRIT] << myname << "(initRegInfo): Unknown mbfunc ='" << f
<< "' for " << it.getProp("name") << endl;
return false;
}
}
return true;
}
// ------------------------------------------------------------------------------------------
RTUExchange::DeviceType RTUExchange::getDeviceType( const std::string dtype )
{
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;
}
// ------------------------------------------------------------------------------------------
bool RTUExchange::initRTUDevice( RTUDevice* d, UniXML_iterator& it )
{
d->dtype = getDeviceType(it.getProp("mbtype"));
if( d->dtype == dtUnknown )
{
dlog[Debug::CRIT] << myname << "(initRTUDevice): Unknown mbtype=" << it.getProp("mbtype")
<< ". Use: rtu | mtr | rtu188"
<< " for " << it.getProp("name") << endl;
return false;
}
string addr = it.getProp("mbaddr");
if( addr.empty() )
{
dlog[Debug::CRIT] << myname << "(initRTUDevice): Unknown mbaddr for " << it.getProp("name") << endl;
return false;
}
d->speed = defSpeed;
d->mbaddr = ModbusRTU::str2mbAddr(addr);
return true;
}
// ------------------------------------------------------------------------------------------
bool RTUExchange::initItem( UniXML_iterator& it )
{
RSProperty p;
if( !initRSProperty(p,it) )
return false;
string addr = it.getProp("mbaddr");
if( addr.empty() )
{
dlog[Debug::CRIT] << myname << "(initItem): Unknown mbaddr='" << addr << " for " << it.getProp("name") << endl;
return false;
}
ModbusRTU::ModbusAddr mbaddr = ModbusRTU::str2mbAddr(addr);
RTUDevice* dev = addDev(rmap,mbaddr,it);
if( !dev )
{
dlog[Debug::CRIT] << myname << "(initItem): " << it.getProp("name") << " CAN`T ADD for polling!" << endl;
return false;
}
ModbusRTU::ModbusData mbreg;
if( mbregFromID )
mbreg = p.si.id; // conf->getSensorID(it.getProp("name"));
else if( dev->dtype != RTUExchange::dtRTU188 )
{
string reg = it.getProp("mbreg");
if( reg.empty() )
{
dlog[Debug::CRIT] << myname << "(initRegInfo): unknown mbreg for " << it.getProp("name") << endl;
return false;
}
mbreg = ModbusRTU::str2mbData(reg);
}
else // if( dev->dtype == RTUExchange::dtRTU188 )
{
RegInfo rr;
initRegInfo(&rr,it,dev);
mbreg = RTUStorage::getRegister(rr.rtuJack,rr.rtuChan,p.stype);
if( mbreg == -1 )
{
dlog[Debug::CRIT] << myname << "(initItem): unknown mbreg for " << it.getProp("name") << endl;
return false;
}
}
RegInfo* ri = addReg(dev->regmap,mbreg,it,dev);
if( dev->dtype == dtMTR )
{
p.rnum = MTR::wsize(ri->mtrType);
if( p.rnum <= 0 )
{
dlog[Debug::CRIT] << 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 )
{
dlog[Debug::CRIT] << myname << "(initItem): FAILED! Sharing SAVE (not bit saving) to "
<< " mbreg=" << ModbusRTU::dat2str(ri->mbreg)
<< " for " << it.getProp("name") << endl;
abort(); // ABORT PROGRAM!!!!
return false;
}
if( p.nbit >= 0 && ri->slst.size() == 1 )
{
PList::iterator it2 = ri->slst.begin();
if( it2->nbit < 0 )
{
dlog[Debug::CRIT] << myname << "(initItem): FAILED! Sharing SAVE (mbreg="
<< ModbusRTU::dat2str(ri->mbreg) << " already used)!"
<< " IGNORE --> " << it.getProp("name") << endl;
abort(); // ABORT PROGRAM!!!!
return false;
}
}
}
RSProperty* p1 = addProp(ri->slst,p);
if( !p1 )
return false;
p1->reg = ri;
if( p1->rnum > 1 )
{
for( int i=1; i<p1->rnum; i++ )
{
RTUExchange::RegInfo* ri1 = addReg(dev->regmap,mbreg+i,it,dev,ri);
ri1->mb_init_mbreg = ri->mb_init_mbreg+i;
}
}
if( dev->dtype == dtRTU188 )
{
if( !dev->rtu )
dev->rtu = new RTUStorage(mbaddr);
}
return true;
}
// ------------------------------------------------------------------------------------------
bool RTUExchange::initMTRitem( UniXML_iterator& it, RegInfo* p )
{
p->mtrType = MTR::str2type(it.getProp("mtrtype"));
if( p->mtrType == MTR::mtUnknown )
{
dlog[Debug::CRIT] << myname << "(readMTRItem): Unknown mtrtype '"
<< it.getProp("mtrtype")
<< "' for " << it.getProp("name") << endl;
return false;
}
return true;
}
// ------------------------------------------------------------------------------------------
bool RTUExchange::initRTU188item( UniXML_iterator& it, RegInfo* p )
{
string jack = it.getProp("jack");
string chan = it.getProp("channel");
if( jack.empty() )
{
dlog[Debug::CRIT] << myname << "(readRTU188Item): Unknown jack='' "
<< " for " << it.getProp("name") << endl;
return false;
}
p->rtuJack = RTUStorage::s2j(jack);
if( p->rtuJack == RTUStorage::nUnknown )
{
dlog[Debug::CRIT] << myname << "(readRTU188Item): Unknown jack=" << jack
<< " for " << it.getProp("name") << endl;
return false;
}
if( chan.empty() )
{
dlog[Debug::CRIT] << myname << "(readRTU188Item): Unknown channel='' "
<< " for " << it.getProp("name") << endl;
return false;
}
p->rtuChan = UniSetTypes::uni_atoi(chan);
if( dlog.debugging(Debug::LEVEL2) )
dlog[Debug::LEVEL2] << myname << "(readRTU188Item): " << p << endl;
return true;
}
// -----------------------------------------------------------------------------
void RTUExchange::initIterators()
{
shm->initAIterator(aitHeartBeat);
for( RTUExchange::RTUDeviceMap::iterator it1=rmap.begin(); it1!=rmap.end(); ++it1 )
{
RTUDevice* d(it1->second);
shm->initDIterator(d->resp_dit);
for( RTUExchange::RegMap::iterator it=d->regmap.begin(); it!=d->regmap.end(); ++it )
{
for( PList::iterator it2=it->second->slst.begin();it2!=it->second->slst.end(); ++it2 )
{
shm->initDIterator(it2->dit);
shm->initAIterator(it2->ait);
}
}
}
}
// -----------------------------------------------------------------------------
void RTUExchange::help_print( int argc, const char* const* argv )
{
cout << "Default: prefix='rs'" << endl;
cout << "--prefix-polltime msec - Пауза между опросаом карт. По умолчанию 200 мсек." << endl;
cout << "--prefix-heartbeat-id - Данный процесс связан с указанным аналоговым heartbeat-дачиком." << endl;
cout << "--prefix-heartbeat-max - Максимальное значение heartbeat-счётчика для данного процесса. По умолчанию 10." << endl;
cout << "--prefix-ready-timeout - Время ожидания готовности SM к работе, мсек. (-1 - ждать 'вечно')" << endl;
cout << "--prefix-force - Сохранять значения в SM, независимо от, того менялось ли значение" << endl;
cout << "--prefix-initPause - Задержка перед инициализацией (время на активизация процесса)" << endl;
cout << "--prefix-sm-ready-timeout - время на ожидание старта SM" << endl;
cout << " Настройки протокола RS: " << endl;
cout << "--prefix-dev devname - файл устройства" << endl;
cout << "--prefix-speed - Скорость обмена (9600,19920,38400,57600,115200)." << endl;
cout << "--prefix-my-addr - адрес текущего узла" << endl;
cout << "--prefix-recv-timeout - Таймаут на ожидание ответа." << endl;
}
// -----------------------------------------------------------------------------
RTUExchange* RTUExchange::init_rtuexchange( int argc, const char* const* argv, UniSetTypes::ObjectId icID,
SharedMemory* ic, const std::string prefix )
{
string name = conf->getArgParam("--" + prefix + "-name","RTUExchange1");
if( name.empty() )
{
cerr << "(rtuexchange): Unknown 'name'. Use --" << prefix << "-name" << endl;
return 0;
}
ObjectId ID = conf->getObjectID(name);
if( ID == UniSetTypes::DefaultObjectId )
{
cerr << "(rtuexchange): Not found ID for '" << name
<< "'!"
<< " in section <" << conf->getObjectsSection() << ">" << endl;
return 0;
}
dlog[Debug::INFO] << "(rtuexchange): name = " << name << "(" << ID << ")" << endl;
return new RTUExchange(ID,icID,ic,prefix);
}
// -----------------------------------------------------------------------------
std::ostream& operator<<( std::ostream& os, const RTUExchange::DeviceType& dt )
{
switch(dt)
{
case RTUExchange::dtRTU:
os << "RTU";
break;
case RTUExchange::dtRTU188:
os << "RTU188";
break;
case RTUExchange::dtMTR:
os << "MTR";
break;
default:
os << "Unknown device type (" << (int)dt << ")";
break;
}
return os;
}
// -----------------------------------------------------------------------------
std::ostream& operator<<( std::ostream& os, const RTUExchange::RSProperty& p )
{
os << " (" << ModbusRTU::dat2str(p.reg->mbreg) << ")"
<< " sid=" << p.si.id
<< " stype=" << p.stype
<< " nbit=" << p.nbit
<< " nbyte=" << p.nbyte
<< " rnum=" << p.rnum
<< " safety=" << p.safety
<< " invert=" << p.invert;
if( p.stype == UniversalIO::AnalogInput || p.stype == UniversalIO::AnalogOutput )
{
os << p.cal
<< " cdiagram=" << ( p.cdiagram ? "yes" : "no" );
}
return os;
}
// -----------------------------------------------------------------------------
void RTUExchange::initDeviceList()
{
xmlNode* respNode = conf->findNode(cnode,"DeviceList");
if( respNode )
{
UniXML_iterator it1(respNode);
if( it1.goChildren() )
{
for(;it1.getCurrent(); it1.goNext() )
{
ModbusRTU::ModbusAddr a = ModbusRTU::str2mbAddr(it1.getProp("addr"));
initDeviceInfo(rmap,a,it1);
}
}
else
dlog[Debug::WARN] << myname << "(init): <DeviceList> empty section..." << endl;
}
else
dlog[Debug::WARN] << myname << "(init): <DeviceList> not found..." << endl;
}
// -----------------------------------------------------------------------------
bool RTUExchange::initDeviceInfo( RTUDeviceMap& m, ModbusRTU::ModbusAddr a, UniXML_iterator& it )
{
RTUDeviceMap::iterator d = m.find(a);
if( d == m.end() )
{
dlog[Debug::WARN] << myname << "(initDeviceInfo): not found device for addr=" << ModbusRTU::addr2str(a) << endl;
return false;
}
if( !it.getProp("respondSensor").empty() )
{
d->second->resp_id = conf->getSensorID(it.getProp("respondSensor"));
if( d->second->resp_id == DefaultObjectId )
{
dlog[Debug::CRIT] << myname << "(initDeviceInfo): not found ID for noRespondSensor=" << it.getProp("respondSensor") << endl;
return false;
}
}
dlog[Debug::INFO] << myname << "(initDeviceInfo): add addr=" << ModbusRTU::addr2str(a) << endl;
int tout = it.getPIntProp("timeout", UniSetTimer::WaitUpTime);
d->second->resp_ptTimeout.setTiming(tout);
d->second->resp_invert = it.getIntProp("invert");
string s = it.getProp("speed");
if( !s.empty() )
{
d->second->speed = ComPort::getSpeed(s);
if( d->second->speed == ComPort::ComSpeed0 )
{
d->second->speed = defSpeed;
dlog[Debug::CRIT] << myname << "(initDeviceInfo): Unknown speed=" << s <<
" for addr=" << ModbusRTU::addr2str(a) << endl;
return false;
}
}
return true;
}
// -----------------------------------------------------------------------------
void RTUExchange::printMap( RTUExchange::RTUDeviceMap& m )
{
cout << "devices: num=" << m.size() << endl;
for( RTUExchange::RTUDeviceMap::iterator it=m.begin(); it!=m.end(); ++it )
{
cout << " " << *(it->second) << endl;
}
}
// -----------------------------------------------------------------------------
std::ostream& operator<<( std::ostream& os, RTUExchange::RTUDeviceMap& m )
{
os << "devices: " << endl;
for( RTUExchange::RTUDeviceMap::iterator it=m.begin(); it!=m.end(); ++it )
{
os << " " << *(it->second) << endl;
}
return os;
}
// -----------------------------------------------------------------------------
std::ostream& operator<<( std::ostream& os, RTUExchange::RTUDevice& d )
{
os << "addr=" << ModbusRTU::addr2str(d.mbaddr)
<< " type=" << d.dtype;
os << " rtu=" << (d.rtu ? "yes" : "no" );
os << " respond_id=" << d.resp_id
<< " respond_timeout=" << d.resp_ptTimeout.getInterval()
<< " respond_state=" << d.resp_state
<< " respond_invert=" << d.resp_invert
<< endl;
os << " regs: " << endl;
for( RTUExchange::RegMap::iterator it=d.regmap.begin(); it!=d.regmap.end(); ++it )
os << " " << *(it->second) << endl;
return os;
}
// -----------------------------------------------------------------------------
std::ostream& operator<<( std::ostream& os, RTUExchange::RegInfo& r )
{
os << " mbreg=" << ModbusRTU::dat2str(r.mbreg)
<< " mbfunc=" << r.mbfunc
<< " mtrType=" << MTR::type2str(r.mtrType)
<< " jack=" << RTUStorage::j2s(r.rtuJack)
<< " chan=" << r.rtuChan
<< " q_num=" << r.q_num
<< " q_count=" << r.q_count
<< " value=" << ModbusRTU::dat2str(r.mbval) << "(" << (int)r.mbval << ")"
<< endl;
for( RTUExchange::PList::iterator it=r.slst.begin(); it!=r.slst.end(); ++it )
os << " " << (*it) << endl;
return os;
}
// -----------------------------------------------------------------------------
void RTUExchange::rtuQueryOptimization( RTUDeviceMap& m )
{
if( noQueryOptimization )
return;
dlog[Debug::INFO] << myname << "(rtuQueryOptimization): optimization..." << endl;
for( RTUExchange::RTUDeviceMap::iterator it1=m.begin(); it1!=m.end(); ++it1 )
{
RTUDevice* d(it1->second);
for( RTUExchange::RegMap::iterator it=d->regmap.begin(); it!=d->regmap.end(); ++it )
{
RTUExchange::RegMap::iterator beg = it;
ModbusRTU::ModbusData reg = it->second->mbreg + it->second->offset;
beg->second->q_num = 1;
beg->second->q_count = 1;
it++;
for( ;it!=d->regmap.end(); ++it )
{
if( (it->second->mbreg + it->second->offset - reg) > 1 )
break;
if( beg->second->mbfunc != it->second->mbfunc )
break;
beg->second->q_count++;
if( beg->second->q_count >= ModbusRTU::MAXDATALEN )
break;
reg = it->second->mbreg + it->second->offset;
it->second->q_num = beg->second->q_count;
it->second->q_count = 0;
}
// check correct function...
if( beg->second->q_count>1 && beg->second->mbfunc==ModbusRTU::fnWriteOutputSingleRegister )
{
dlog[Debug::WARN] << myname << "(rtuQueryOptimization): "
<< " optimization change func=" << ModbusRTU::fnWriteOutputSingleRegister
<< " <--> func=" << ModbusRTU::fnWriteOutputRegisters
<< " for mbaddr=" << ModbusRTU::addr2str(d->mbaddr)
<< " mbreg=" << ModbusRTU::dat2str(beg->second->mbreg);
beg->second->mbfunc = ModbusRTU::fnWriteOutputRegisters;
}
else if( beg->second->q_count>1 && beg->second->mbfunc==ModbusRTU::fnForceSingleCoil )
{
dlog[Debug::WARN] << myname << "(rtuQueryOptimization): "
<< " optimization change func=" << ModbusRTU::fnForceSingleCoil
<< " <--> func=" << ModbusRTU::fnForceMultipleCoils
<< " for mbaddr=" << ModbusRTU::addr2str(d->mbaddr)
<< " mbreg=" << ModbusRTU::dat2str(beg->second->mbreg);
beg->second->mbfunc = ModbusRTU::fnForceMultipleCoils;
}
if( it==d->regmap.end() )
break;
it--;
}
}
}
// -----------------------------------------------------------------------------
void RTUExchange::updateRTU( RegMap::iterator& rit )
{
RegInfo* r(rit->second);
for( PList::iterator it=r->slst.begin(); it!=r->slst.end(); ++it )
updateRSProperty( &(*it),false );
if( r->sm_init )
r->mb_init = true;
}
// -----------------------------------------------------------------------------
void RTUExchange::updateRSProperty( RSProperty* p, bool write_only )
{
using namespace ModbusRTU;
RegInfo* r(p->reg->rit->second);
bool save = isWriteFunction( r->mbfunc );
if( !save && write_only )
return;
try
{
if( p->vType == VTypes::vtUnknown )
{
ModbusRTU::DataBits16 b(r->mbval);
if( p->nbit >= 0 )
{
if( save && r->mb_init )
{
bool set = IOBase::processingAsDO( p, shm, force_out );
b.set(p->nbit,set);
r->mbval = b.mdata();
}
else
{
bool set = b[p->nbit];
IOBase::processingAsDI( p, set, shm, force );
}
return;
}
if( p->rnum <= 1 )
{
if( save && r->mb_init )
{
if( p->stype == UniversalIO::DigitalInput ||
p->stype == UniversalIO::DigitalOutput )
{
r->mbval = IOBase::processingAsDO( p, shm, force_out );
}
else
r->mbval = IOBase::processingAsAO( p, shm, force_out );
}
else
{
if( p->stype == UniversalIO::DigitalInput ||
p->stype == UniversalIO::DigitalOutput )
{
IOBase::processingAsDI( p, r->mbval, shm, force );
}
else
IOBase::processingAsAI( p, (signed short)r->mbval, shm, force );
}
return;
}
dlog[Debug::CRIT] << myname << "(updateRSProperty): IGNORE item: rnum=" << p->rnum
<< " > 1 ?!! for id=" << p->si.id << endl;
return;
}
else if( p->vType == VTypes::vtSigned )
{
if( save && r->mb_init )
{
if( p->stype == UniversalIO::DigitalInput ||
p->stype == UniversalIO::DigitalOutput )
{
r->mbval = (signed short)IOBase::processingAsDO( p, shm, force_out );
}
else
r->mbval = (signed short)IOBase::processingAsAO( p, shm, force_out );
}
else
{
if( p->stype == UniversalIO::DigitalInput ||
p->stype == UniversalIO::DigitalOutput )
{
IOBase::processingAsDI( p, r->mbval, shm, force );
}
else
{
IOBase::processingAsAI( p, (signed short)(r->mbval), shm, force );
}
}
return;
}
else if( p->vType == VTypes::vtUnsigned )
{
if( save && r->mb_init )
{
if( p->stype == UniversalIO::DigitalInput ||
p->stype == UniversalIO::DigitalOutput )
{
r->mbval = (unsigned short)IOBase::processingAsDO( p, shm, force_out );
}
else
r->mbval = (unsigned short)IOBase::processingAsAO( p, shm, force_out );
}
else
{
if( p->stype == UniversalIO::DigitalInput ||
p->stype == UniversalIO::DigitalOutput )
{
IOBase::processingAsDI( p, r->mbval, shm, force );
}
else
{
IOBase::processingAsAI( p, (unsigned short)r->mbval, shm, force );
}
}
return;
}
else if( p->vType == VTypes::vtByte )
{
if( p->nbyte <= 0 || p->nbyte > VTypes::Byte::bsize )
{
dlog[Debug::CRIT] << myname << "(updateRSProperty): IGNORE item: reg=" << ModbusRTU::dat2str(r->mbreg)
<< " vtype=" << p->vType << " but nbyte=" << p->nbyte << endl;
return;
}
if( save && r->mb_init )
{
long v = IOBase::processingAsAO( p, shm, force_out );
VTypes::Byte b(r->mbval);
b.raw.b[p->nbyte-1] = v;
r->mbval = b.raw.w;
}
else
{
VTypes::Byte b(r->mbval);
IOBase::processingAsAI( p, b.raw.b[p->nbyte-1], shm, force );
}
return;
}
else if( p->vType == VTypes::vtF2 )
{
RegMap::iterator i(p->reg->rit);
if( save && r->mb_init )
{
float f = IOBase::processingFasAO( p, shm, force_out );
VTypes::F2 f2(f);
for( int k=0; k<VTypes::F2::wsize(); k++, i++ )
i->second->mbval = f2.raw.v[k];
}
else
{
ModbusRTU::ModbusData* data = new ModbusRTU::ModbusData[VTypes::F2::wsize()];
for( int k=0; k<VTypes::F2::wsize(); k++, i++ )
data[k] = i->second->mbval;
VTypes::F2 f(data,VTypes::F2::wsize());
delete[] data;
IOBase::processingFasAI( p, (float)f, shm, force );
}
}
else if( p->vType == VTypes::vtF4 )
{
RegMap::iterator i(p->reg->rit);
if( save && r->mb_init )
{
float f = IOBase::processingFasAO( p, shm, force_out );
VTypes::F4 f4(f);
for( int k=0; k<VTypes::F4::wsize(); k++, i++ )
i->second->mbval = f4.raw.v[k];
}
else
{
ModbusRTU::ModbusData* data = new ModbusRTU::ModbusData[VTypes::F4::wsize()];
for( int k=0; k<VTypes::F4::wsize(); k++, i++ )
data[k] = i->second->mbval;
VTypes::F4 f(data,VTypes::F4::wsize());
delete[] data;
IOBase::processingFasAI( p, (float)f, shm, force );
}
}
}
catch(IOController_i::NameNotFound &ex)
{
dlog[Debug::LEVEL3] << myname << "(updateRSProperty):(NameNotFound) " << ex.err << endl;
}
catch(IOController_i::IOBadParam& ex )
{
dlog[Debug::LEVEL3] << myname << "(updateRSProperty):(IOBadParam) " << ex.err << endl;
}
catch(IONotifyController_i::BadRange )
{
dlog[Debug::LEVEL3] << myname << "(updateRSProperty): (BadRange)..." << endl;
}
catch( Exception& ex )
{
dlog[Debug::LEVEL3] << myname << "(updateRSProperty): " << ex << endl;
}
catch(CORBA::SystemException& ex)
{
dlog[Debug::LEVEL3] << myname << "(updateRSProperty): CORBA::SystemException: "
<< ex.NP_minorString() << endl;
}
catch(...)
{
dlog[Debug::LEVEL3] << myname << "(updateRSProperty): catch ..." << endl;
}
}
// -----------------------------------------------------------------------------
void RTUExchange::updateMTR( RegMap::iterator& rit )
{
RegInfo* r(rit->second);
using namespace ModbusRTU;
bool save = isWriteFunction( r->mbfunc );
{
for( PList::iterator it=r->slst.begin(); it!=r->slst.end(); ++it )
{
try
{
if( r->mtrType == MTR::mtT1 )
{
if( save )
r->mbval = IOBase::processingAsAO( &(*it), shm, force_out );
else
{
MTR::T1 t(r->mbval);
IOBase::processingAsAI( &(*it), t.val, shm, force );
}
continue;
}
if( r->mtrType == MTR::mtT2 )
{
if( save )
{
MTR::T2 t(IOBase::processingAsAO( &(*it), shm, force_out ));
r->mbval = t.val;
}
else
{
MTR::T2 t(r->mbval);
IOBase::processingAsAI( &(*it), t.val, shm, force );
}
continue;
}
if( r->mtrType == MTR::mtT3 )
{
RegMap::iterator i(rit);
if( save )
{
MTR::T3 t(IOBase::processingAsAO( &(*it), shm, force_out ));
for( int k=0; k<MTR::T3::wsize(); k++, i++ )
i->second->mbval = t.raw.v[k];
}
else
{
ModbusRTU::ModbusData* data = new ModbusRTU::ModbusData[MTR::T3::wsize()];
for( int k=0; k<MTR::T3::wsize(); k++, i++ )
data[k] = i->second->mbval;
MTR::T3 t(data,MTR::T3::wsize());
delete[] data;
IOBase::processingAsAI( &(*it), (long)t, shm, force );
}
continue;
}
if( r->mtrType == MTR::mtT4 )
{
if( save )
cerr << myname << "(updateMTR): write (T4) reg(" << dat2str(r->mbreg) << ") to MTR NOT YET!!!" << endl;
else
{
MTR::T4 t(r->mbval);
IOBase::processingAsAI( &(*it), uni_atoi(t.sval), shm, force );
}
continue;
}
if( r->mtrType == MTR::mtT5 )
{
RegMap::iterator i(rit);
if( save )
{
MTR::T5 t(IOBase::processingAsAO( &(*it), shm, force_out ));
for( int k=0; k<MTR::T5::wsize(); k++, i++ )
i->second->mbval = t.raw.v[k];
}
else
{
ModbusRTU::ModbusData* data = new ModbusRTU::ModbusData[MTR::T5::wsize()];
for( int k=0; k<MTR::T5::wsize(); k++, i++ )
data[k] = i->second->mbval;
MTR::T5 t(data,MTR::T5::wsize());
delete[] data;
IOBase::processingFasAI( &(*it), (float)t.val, shm, force );
}
continue;
}
if( r->mtrType == MTR::mtT6 )
{
RegMap::iterator i(rit);
if( save )
{
MTR::T6 t(IOBase::processingAsAO( &(*it), shm, force_out ));
for( int k=0; k<MTR::T6::wsize(); k++, i++ )
i->second->mbval = t.raw.v[k];
}
else
{
ModbusRTU::ModbusData* data = new ModbusRTU::ModbusData[MTR::T6::wsize()];
for( int k=0; k<MTR::T6::wsize(); k++, i++ )
data[k] = i->second->mbval;
MTR::T6 t(data,MTR::T6::wsize());
delete[] data;
IOBase::processingFasAI( &(*it), (float)t.val, shm, force );
}
continue;
}
if( r->mtrType == MTR::mtT7 )
{
RegMap::iterator i(rit);
if( save )
{
MTR::T7 t(IOBase::processingAsAO( &(*it), shm, force_out ));
for( int k=0; k<MTR::T7::wsize(); k++, i++ )
i->second->mbval = t.raw.v[k];
}
else
{
ModbusRTU::ModbusData* data = new ModbusRTU::ModbusData[MTR::T7::wsize()];
for( int k=0; k<MTR::T7::wsize(); k++, i++ )
data[k] = i->second->mbval;
MTR::T7 t(data,MTR::T7::wsize());
delete[] data;
IOBase::processingFasAI( &(*it), (float)t.val, shm, force );
}
continue;
}
if( r->mtrType == MTR::mtT16 )
{
if( save )
{
MTR::T16 t(IOBase::processingFasAO( &(*it), shm, force_out ));
r->mbval = t.val;
}
else
{
MTR::T16 t(r->mbval);
IOBase::processingFasAI( &(*it), t.fval, shm, force );
}
continue;
}
if( r->mtrType == MTR::mtT17 )
{
if( save )
{
MTR::T17 t(IOBase::processingFasAO( &(*it), shm, force_out ));
r->mbval = t.val;
}
else
{
MTR::T17 t(r->mbval);
IOBase::processingFasAI( &(*it), t.fval, shm, force );
}
continue;
}
if( r->mtrType == MTR::mtF1 )
{
RegMap::iterator i(rit);
if( save )
{
float f = IOBase::processingFasAO( &(*it), shm, force_out );
MTR::F1 f1(f);
for( int k=0; k<MTR::F1::wsize(); k++, i++ )
i->second->mbval = f1.raw.v[k];
}
else
{
ModbusRTU::ModbusData* data = new ModbusRTU::ModbusData[MTR::F1::wsize()];
for( int k=0; k<MTR::F1::wsize(); k++, i++ )
data[k] = i->second->mbval;
MTR::F1 t(data,MTR::F1::wsize());
delete[] data;
IOBase::processingFasAI( &(*it), (float)t, shm, force );
}
continue;
}
}
catch(IOController_i::NameNotFound &ex)
{
dlog[Debug::LEVEL3] << myname << "(updateMTR):(NameNotFound) " << ex.err << endl;
}
catch(IOController_i::IOBadParam& ex )
{
dlog[Debug::LEVEL3] << myname << "(updateMTR):(IOBadParam) " << ex.err << endl;
}
catch(IONotifyController_i::BadRange )
{
dlog[Debug::LEVEL3] << myname << "(updateMTR): (BadRange)..." << endl;
}
catch( Exception& ex )
{
dlog[Debug::LEVEL3] << myname << "(updateMTR): " << ex << endl;
}
catch(CORBA::SystemException& ex)
{
dlog[Debug::LEVEL3] << myname << "(updateMTR): CORBA::SystemException: "
<< ex.NP_minorString() << endl;
}
catch(...)
{
dlog[Debug::LEVEL3] << myname << "(updateMTR): catch ..." << endl;
}
}
}
}
// -----------------------------------------------------------------------------
void RTUExchange::updateRTU188( RegMap::iterator& it )
{
RegInfo* r(it->second);
if( !r->dev->rtu )
return;
using namespace ModbusRTU;
// bool save = false;
if( isWriteFunction(r->mbfunc) )
{
// save = true;
cerr << myname << "(updateRTU188): write reg(" << dat2str(r->mbreg) << ") to RTU188 NOT YET!!!" << endl;
return;
}
for( PList::iterator it=r->slst.begin(); it!=r->slst.end(); ++it )
{
try
{
if( it->stype == UniversalIO::DigitalInput )
{
bool set = r->dev->rtu->getState(r->rtuJack,r->rtuChan,it->stype);
IOBase::processingAsDI( &(*it), set, shm, force );
continue;
}
if( it->stype == UniversalIO::AnalogInput )
{
long val = r->dev->rtu->getInt(r->rtuJack,r->rtuChan,it->stype);
IOBase::processingAsAI( &(*it),val, shm, force );
continue;
}
}
catch(IOController_i::NameNotFound &ex)
{
dlog[Debug::LEVEL3] << myname << "(updateRTU188):(NameNotFound) " << ex.err << endl;
}
catch(IOController_i::IOBadParam& ex )
{
dlog[Debug::LEVEL3] << myname << "(updateRTU188):(IOBadParam) " << ex.err << endl;
}
catch(IONotifyController_i::BadRange )
{
dlog[Debug::LEVEL3] << myname << "(updateRTU188): (BadRange)..." << endl;
}
catch( Exception& ex )
{
dlog[Debug::LEVEL3] << myname << "(updateRTU188): " << ex << endl;
}
catch(CORBA::SystemException& ex)
{
dlog[Debug::LEVEL3] << myname << "(updateRTU188): CORBA::SystemException: "
<< ex.NP_minorString() << endl;
}
catch(...)
{
dlog[Debug::LEVEL3] << myname << "(updateRTU188): catch ..." << endl;
}
}
}
// -----------------------------------------------------------------------------
//std::ostream& operator<<( std::ostream& os, MBTCPMaster::PList& lst )
std::ostream& RTUExchange::print_plist( std::ostream& os, RTUExchange::PList& lst )
{
os << "[ ";
for( RTUExchange::PList::const_iterator it=lst.begin(); it!=lst.end(); ++it )
os << "(" << it->si.id << ")" << conf->oind->getBaseName(conf->oind->getMapName(it->si.id)) << " ";
os << "]";
return os;
}
// -----------------------------------------------------------------------------
#ifndef _RTUEXCHANGE_H_
#define _RTUEXCHANGE_H_
// -----------------------------------------------------------------------------
#include <ostream>
#include <string>
#include <map>
#include <vector>
#include "IONotifyController.h"
#include "UniSetObject_LT.h"
#include "modbus/ModbusRTUMaster.h"
#include "PassiveTimer.h"
#include "Trigger.h"
#include "Mutex.h"
#include "Calibration.h"
#include "SMInterface.h"
#include "SharedMemory.h"
#include "MTR.h"
#include "RTUStorage.h"
#include "IOBase.h"
#include "VTypes.h"
// -----------------------------------------------------------------------------
class RTUExchange:
public UniSetObject_LT
{
public:
RTUExchange( UniSetTypes::ObjectId objId, UniSetTypes::ObjectId shmID,
SharedMemory* ic=0, const std::string prefix="rs" );
virtual ~RTUExchange();
/*! глобальная функция для инициализации объекта */
static RTUExchange* init_rtuexchange( int argc, const char* const* argv,
UniSetTypes::ObjectId shmID, SharedMemory* ic=0,
const std::string prefix="rs" );
/*! глобальная функция для вывода help-а */
static void help_print( int argc, const char* const* argv );
static const int NoSafetyState=-1;
enum Timer
{
tmExchange
};
enum DeviceType
{
dtUnknown, /*!< неизвестный */
dtRTU, /*!< RTU (default) */
dtRTU188, /*!< RTU188 (Fastwell) */
dtMTR /*!< MTR (DEIF) */
};
static DeviceType getDeviceType( const std::string dtype );
friend std::ostream& operator<<( std::ostream& os, const DeviceType& dt );
// -------------------------------------------------------------------------------
struct RTUDevice;
struct RegInfo;
struct RSProperty:
public IOBase
{
// only for RTU
short nbit; /*!< bit number) */
VTypes::VType vType; /*!< type of value */
short rnum; /*!< count of registers */
short nbyte; /*!< byte number (1-2) */
RSProperty():
nbit(-1),vType(VTypes::vtUnknown),
rnum(VTypes::wsize(VTypes::vtUnknown)),
nbyte(0),reg(0)
{}
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, PList& p );
typedef std::map<ModbusRTU::ModbusData,RegInfo*> RegMap;
struct RegInfo
{
RegInfo():
mbval(0),mbreg(0),mbfunc(ModbusRTU::fnUnknown),
mtrType(MTR::mtUnknown),
rtuJack(RTUStorage::nUnknown),rtuChan(0),
dev(0),offset(0),
q_num(0),q_count(1),mb_init(false),sm_init(false),
mb_init_mbreg(0)
{}
ModbusRTU::ModbusData mbval;
ModbusRTU::ModbusData mbreg; /*!< регистр */
ModbusRTU::SlaveFunctionCode mbfunc; /*!< функция для чтения/записи */
PList slst;
// only for MTR
MTR::MTRType mtrType; /*!< тип регистра (согласно спецификации на MTR) */
// only for RTU188
RTUStorage::RTUJack rtuJack;
int rtuChan;
RTUDevice* dev;
int offset;
// optimization
int q_num; /*! number in query */
int q_count; /*! count registers for query */
RegMap::iterator rit;
bool mb_init; /*!< init before use */
bool sm_init; /*!< SM init value */
ModbusRTU::ModbusData mb_init_mbreg; /*!< mb_init register */
};
friend std::ostream& operator<<( std::ostream& os, RegInfo& r );
struct RTUDevice
{
RTUDevice():
speed(ComPort::ComSpeed38400),
respnond(false),
mbaddr(0),
dtype(dtUnknown),
resp_id(UniSetTypes::DefaultObjectId),
resp_state(false),
resp_invert(false),
resp_real(false),
resp_init(false),
rtu(0)
{
resp_trTimeout.change(false);
}
ComPort::Speed speed;
bool respnond;
ModbusRTU::ModbusAddr mbaddr; /*!< адрес устройства */
RegMap regmap;
DeviceType dtype; /*!< тип устройства */
UniSetTypes::ObjectId resp_id;
IOController::DIOStateList::iterator resp_dit;
PassiveTimer resp_ptTimeout;
Trigger resp_trTimeout;
bool resp_state;
bool resp_invert;
bool resp_real;
bool resp_init;
RTUStorage* rtu;
// return TRUE if state changed
bool checkRespond();
};
friend std::ostream& operator<<( std::ostream& os, RTUDevice& d );
typedef std::map<ModbusRTU::ModbusAddr,RTUDevice*> RTUDeviceMap;
friend std::ostream& operator<<( std::ostream& os, RTUDeviceMap& d );
void printMap(RTUDeviceMap& d);
// ----------------------------------
protected:
RTUDeviceMap rmap;
ModbusRTUMaster* mb;
UniSetTypes::uniset_mutex mbMutex;
std::string devname;
ComPort::Speed defSpeed;
int recv_timeout;
bool use485F;
bool transmitCtl;
xmlNode* cnode;
std::string s_field;
std::string s_fvalue;
SMInterface* shm;
void step();
void poll();
bool pollRTU( RTUDevice* dev, RegMap::iterator& it );
void updateSM();
void updateRTU(RegMap::iterator& it);
void updateMTR(RegMap::iterator& it);
void updateRTU188(RegMap::iterator& it);
void updateRSProperty( RSProperty* p, bool write_only=false );
virtual void processingMessage( UniSetTypes::VoidMessage *msg );
void sysCommand( UniSetTypes::SystemMessage *msg );
void sensorInfo( UniSetTypes::SensorMessage*sm );
void timerInfo( UniSetTypes::TimerMessage *tm );
void askSensors( UniversalIO::UIOCommand cmd );
void initOutput();
void waitSMReady();
virtual bool activateObject();
// действия при завершении работы
virtual void sigterm( int signo );
void initMB( bool reopen=false );
void initIterators();
bool initItem( UniXML_iterator& it );
bool readItem( UniXML& xml, UniXML_iterator& it, xmlNode* sec );
void initDeviceList();
void initOffsetList();
RTUDevice* addDev( RTUDeviceMap& dmap, ModbusRTU::ModbusAddr a, UniXML_iterator& it );
RegInfo* addReg( RegMap& rmap, ModbusRTU::ModbusData r, UniXML_iterator& it,
RTUDevice* dev, RegInfo* rcopy=0 );
RSProperty* addProp( PList& plist, RSProperty& p );
bool initMTRitem( UniXML_iterator& it, RegInfo* p );
bool initRTU188item( UniXML_iterator& it, RegInfo* p );
bool initRSProperty( RSProperty& p, UniXML_iterator& it );
bool initRegInfo( RegInfo* r, UniXML_iterator& it, RTUDevice* dev );
bool initRTUDevice( RTUDevice* d, UniXML_iterator& it );
bool initDeviceInfo( RTUDeviceMap& m, ModbusRTU::ModbusAddr a, UniXML_iterator& it );
void rtuQueryOptimization( RTUDeviceMap& m );
void readConfiguration();
private:
RTUExchange();
bool initPause;
UniSetTypes::uniset_mutex mutex_start;
bool force; /*!< флаг означающий, что надо сохранять в SM, даже если значение не менялось */
bool force_out; /*!< флаг означающий, принудительного чтения выходов */
bool mbregFromID;
int polltime; /*!< переодичность обновления данных, [мсек] */
timeout_t sleepPause_usec;
PassiveTimer ptHeartBeat;
UniSetTypes::ObjectId sidHeartBeat;
int maxHeartBeat;
IOController::AIOStateList::iterator aitHeartBeat;
UniSetTypes::ObjectId test_id;
UniSetTypes::uniset_mutex pollMutex;
bool activated;
int activateTimeout;
bool rs_pre_clean;
bool noQueryOptimization;
bool allNotRespond;
Trigger trAllNotRespond;
PassiveTimer ptAllNotRespond;
std::string prefix;
};
// -----------------------------------------------------------------------------
#endif // _RS_EXCHANGE_H_
// -----------------------------------------------------------------------------
prefix=@prefix@
exec_prefix=@exec_prefix@
libdir=@libdir@
includedir=@includedir@
Name: libUniSetRTU
Description: Support library for UniSetRTUExchange
Requires: libUniSetExtensions
Version: 2.0.0
Libs: -L${libdir} -lUniSetRTU
Cflags: -I${includedir}/uniset
// --------------------------------------------------------------------------
#include <string>
#include <map>
#include <list>
#include <fstream>
#include <iomanip>
#include <getopt.h>
#include <math.h>
#include "Debug.h"
#include "modbus/ModbusRTUMaster.h"
#include "modbus/ModbusHelpers.h"
#include "extensions/MTR.h"
// --------------------------------------------------------------------------
using namespace UniSetTypes;
using namespace std;
// --------------------------------------------------------------------------
static struct option longopts[] = {
{ "help", no_argument, 0, 'h' },
{ "read", required_argument, 0, 'r' },
{ "save", required_argument, 0, 'w' },
{ "timeout", required_argument, 0, 't' },
{ "autodetect-slave", required_argument, 0, 'l' },
{ "autodetect-speed", required_argument, 0, 'n' },
{ "device", required_argument, 0, 'd' },
{ "verbose", no_argument, 0, 'v' },
{ "speed", required_argument, 0, 's' },
{ "stop-bits", required_argument, 0, 'i' },
{ "parity", required_argument, 0, 'p' },
{ "use485F", no_argument, 0, 'y' },
{ "min-addr", required_argument, 0, 'b' },
{ "max-addr", required_argument, 0, 'e' },
{ "model", required_argument, 0, 'x' },
{ "serial", required_argument, 0, 'z' },
{ NULL, 0, 0, 0 }
};
// --------------------------------------------------------------------------
static void print_help()
{
printf("-h|--help - this message\n");
printf("[--read] mtraddr - read configuration from MTR\n");
printf("[--save] mtraddr confile - save configureation to MTR\n");
printf(" mtraddr=0x00 - autodetect addr\n");
printf("[-d|--device] dev - use device dev. Default: /dev/ttyS0\n");
printf("[-s|--speed] speed - 9600,14400,19200,38400,57600,115200. Default: 38400.\n");
printf("[--stop-bits] n - stop bits [1,2]. Default: 1\n");
printf("[--parity] par - parity [odd,even,no]. Default: no\n");
printf("[-t|--timeout] msec - Timeout. Default: 2000.\n");
printf("[-v|--verbose] - Print all messages to stdout\n");
printf("[-y|--use485F] - use RS485 Fastwel.\n");
printf("[--autodetect-speed] slaveaddr [reg fn] - detect speed\n");
printf(" reg - register of test. Default: 0\n");
printf(" fn - function of test [0x01,0x02,0x03,0x04]. Default: 0x04\n");
printf("[--autodetect-slave] [reg fn] - find slave\n");
printf(" reg - register of test. Default: 0\n");
printf(" fn - function of test [0x01,0x02,0x03,0x04]. Default: 0x04\n");
printf("[--min-addr] - start addres for autodetect. Default: 0\n");
printf("[--max-addr] - end addres for autodetect. Default: 255\n");
printf("\n");
}
// --------------------------------------------------------------------------
enum Command
{
cmdNOP,
cmdRead,
cmdSave,
cmdDetectSpeed,
cmdDetectSlave,
cmdGetModel,
cmdGetSerial
};
// --------------------------------------------------------------------------
static char* checkArg( int ind, int argc, char* argv[] );
// --------------------------------------------------------------------------
int main( int argc, char **argv )
{
Command cmd = cmdNOP;
int optindex = 0;
int opt = 0;
int verb = 0;
string dev("/dev/ttyS0");
string speed("38400");
string mtrconfile("");
string par("");
ModbusRTU::ModbusData reg = 0;
ModbusRTU::ModbusAddr slaveaddr = 0x00;
ModbusRTU::SlaveFunctionCode fn = ModbusRTU::fnReadInputRegisters;
ModbusRTU::ModbusAddr beg = 0;
ModbusRTU::ModbusAddr end = 255;
int tout = 20;
DebugStream dlog;
string tofile("");
int use485 = 0;
ComPort::StopBits sbits = ComPort::OneBit;
ComPort::Parity parity = ComPort::NoParity;
// ModbusRTU::ModbusAddr b=255;
//
// cout << "b=" << (int)b << " b++=" << (int)(b++) << endl;
// return 0;
try
{
while( (opt = getopt_long(argc, argv, "hvw:r:x:d:s:t:l:n:yb:e:x:z:",longopts,&optindex)) != -1 )
{
switch (opt)
{
case 'h':
print_help();
return 0;
case 'r':
cmd = cmdRead;
slaveaddr = ModbusRTU::str2mbAddr(optarg);
break;
case 'w':
cmd = cmdSave;
slaveaddr = ModbusRTU::str2mbAddr( optarg );
if( !checkArg(optind,argc,argv) )
{
cerr << "read command error: bad or no arguments..." << endl;
return 1;
}
else
mtrconfile = string(argv[optind]);
break;
case 'x':
cmd = cmdGetModel;
slaveaddr = ModbusRTU::str2mbAddr(optarg);
break;
case 'z':
cmd = cmdGetSerial;
slaveaddr = ModbusRTU::str2mbAddr(optarg);
break;
case 'y':
use485 = 1;
break;
case 'd':
dev = string(optarg);
break;
case 's':
speed = string(optarg);
break;
case 'p':
par = string(optarg);
if( !par.compare("odd") )
parity = ComPort::Odd;
else if( !par.compare("even") )
parity = ComPort::Even;
break;
#undef atoi
case 't':
tout = atoi(optarg);
break;
case 'i':
if( atoi(optarg) == 2 )
sbits = ComPort::TwoBits;
break;
case 'b':
beg = atoi(optarg);
break;
case 'e':
end = atoi(optarg);
break;
// case 'a':
// myaddr = ModbusRTU::str2mbAddr(optarg);
// break;
case 'v':
verb = 1;
break;
case 'l':
{
if( cmd == cmdNOP )
cmd = cmdDetectSlave;
if( !checkArg(optind,argc,argv) )
break;
reg = ModbusRTU::str2mbData(argv[optind+2]);
if( !checkArg(optind+1,argc,argv) )
break;
fn = (ModbusRTU::SlaveFunctionCode)UniSetTypes::uni_atoi(argv[optind+3]);
}
break;
case 'n':
{
if( cmd == cmdNOP )
cmd = cmdDetectSpeed;
slaveaddr = ModbusRTU::str2mbAddr(optarg);
if( !checkArg(optind,argc,argv) )
break;
reg = ModbusRTU::str2mbData(argv[optind]);
if( !checkArg(optind+1,argc,argv) )
break;
fn = (ModbusRTU::SlaveFunctionCode)UniSetTypes::uni_atoi(argv[optind+1]);
}
break;
case '?':
default:
printf("? argumnet\n");
return 0;
}
}
if( verb )
{
cout << "(init): dev=" << dev << " speed=" << speed
<< " timeout=" << tout << " msec "
<< endl;
}
ModbusRTUMaster mb(dev,use485);
if( verb )
dlog.addLevel( Debug::type(Debug::CRIT | Debug::WARN | Debug::INFO) );
mb.setTimeout(tout);
mb.setSpeed(speed);
mb.setParity(parity);
mb.setStopBits(sbits);
mb.setLog(dlog);
switch(cmd)
{
case cmdRead:
{
if( verb )
cout << "(mtr-setup): read: slaveaddr=" << ModbusRTU::addr2str(slaveaddr) << endl;
}
break;
case cmdSave:
{
if( slaveaddr == 0x00 )
{
if( verb )
cout << "(mtr-setup): save: autodetect slave addr... (speed=" << speed << ")" << endl;
mb.setTimeout(50);
slaveaddr = ModbusHelpers::autodetectSlave(&mb,beg,end,MTR::regModelNumber,ModbusRTU::fnReadInputRegisters);
mb.setTimeout(tout);
}
if( speed.empty() )
{
if( verb )
cout << "(mtr-setup): save: autodetect speed... (addr=" << ModbusRTU::addr2str(slaveaddr) << ")" << endl;
mb.setTimeout(50);
ComPort::Speed s = ModbusHelpers::autodetectSpeed(&mb,slaveaddr,MTR::regModelNumber,ModbusRTU::fnReadInputRegisters);
mb.setSpeed(s);
mb.setTimeout(tout);
}
if( verb )
cout << "(mtr-setup): save: "
<< " slaveaddr=" << ModbusRTU::addr2str(slaveaddr)
<< " confile=" << mtrconfile
<< " speed=" << speed
<< endl;
return MTR::update_configuration(&mb,slaveaddr,mtrconfile,verb) ? 0 : 1;
}
break;
case cmdDetectSlave:
{
if( verb )
{
cout << "(mtr-setup): autodetect slave: "
<< " beg=" << ModbusRTU::addr2str(beg)
<< " end=" << ModbusRTU::addr2str(end)
<< " reg=" << ModbusRTU::dat2str(reg)
<< " fn=" << ModbusRTU::b2str(fn)
<< endl;
}
try
{
ModbusRTU::ModbusAddr a = ModbusHelpers::autodetectSlave(&mb,beg,end,reg,fn);
cout << "(mtr-setup): autodetect modbus slave: " << ModbusRTU::addr2str(a) << endl;
}
catch( UniSetTypes::TimeOut )
{
cout << "(mtr-setup): slave not autodetect..." << endl;
}
break;
}
case cmdDetectSpeed:
{
if( verb )
{
cout << "(mtr-setup): autodetect speed: slaveaddr=" << ModbusRTU::addr2str(slaveaddr)
<< " reg=" << ModbusRTU::dat2str(reg)
<< " fn=" << ModbusRTU::b2str(fn)
<< endl;
}
try
{
ComPort::Speed s = ModbusHelpers::autodetectSpeed(&mb,slaveaddr,reg,fn);
cout << "(mtr-setup): autodetect: slaveaddr=" << ModbusRTU::addr2str(slaveaddr)
<< " speed=" << ComPort::getSpeed(s) << endl;
}
catch( UniSetTypes::TimeOut )
{
cout << "(mtr-setup): speed not autodetect for slaveaddr="
<< ModbusRTU::addr2str(slaveaddr) << endl;
}
}
break;
case cmdGetModel:
{
if( verb )
{
cout << "(mtr-setup): model: "
<< " slaveaddr=" << ModbusRTU::addr2str(slaveaddr)
<< endl;
}
cout << "model: " << MTR::getModelNumber(&mb,slaveaddr) << endl;
}
break;
case cmdGetSerial:
{
if( verb )
{
cout << "(mtr-setup): serial: "
<< " slaveaddr=" << ModbusRTU::addr2str(slaveaddr)
<< endl;
}
cout << "serial: " << MTR::getSerialNumber(&mb,slaveaddr) << endl;
}
break;
case cmdNOP:
default:
cerr << "No command. Use -h for help." << endl;
return 1;
}
}
catch( ModbusRTU::mbException& ex )
{
cerr << "(mtr-setup): " << ex << endl;
}
catch(SystemError& err)
{
cerr << "(mtr-setup): " << err << endl;
}
catch(Exception& ex)
{
cerr << "(mtr-setup): " << ex << endl;
}
catch(...)
{
cerr << "(mtr-setup): catch(...)" << endl;
}
return 0;
}
// --------------------------------------------------------------------------
char* checkArg( int i, int argc, char* argv[] )
{
if( i<argc && (argv[i])[0]!='-' )
return argv[i];
return 0;
}
// --------------------------------------------------------------------------
#include <sys/wait.h>
#include <sstream>
#include "ObjectsActivator.h"
#include "Extensions.h"
#include "RTUExchange.h"
// -----------------------------------------------------------------------------
using namespace std;
using namespace UniSetTypes;
using namespace UniSetExtensions;
// -----------------------------------------------------------------------------
int main( int argc, char** argv )
{
try
{
if( argc>1 && (!strcmp(argv[1],"--help") || !strcmp(argv[1],"-h")) )
{
cout << "--smemory-id objectName - SharedMemory objectID. Default: read from <SharedMemory>" << endl;
cout << "--confile filename - configuration file. Default: configure.xml" << endl;
cout << "--rs-logfile filename - logfilename. Default: rtuexchange.log" << endl;
cout << endl;
RTUExchange::help_print(argc, argv);
return 0;
}
string confile=UniSetTypes::getArgParam("--confile", argc, argv, "configure.xml");
conf = new Configuration( argc, argv, confile );
string logfilename(conf->getArgParam("--rs-logfile"));
if( logfilename.empty() )
logfilename = "rtuexchange.log";
conf->initDebug(dlog,"dlog");
std::ostringstream logname;
string dir(conf->getLogDir());
logname << dir << logfilename;
unideb.logFile( logname.str() );
dlog.logFile( logname.str() );
ObjectId shmID = DefaultObjectId;
string sID = conf->getArgParam("--smemory-id");
if( !sID.empty() )
shmID = conf->getControllerID(sID);
else
shmID = getSharedMemoryID();
if( shmID == DefaultObjectId )
{
cerr << sID << "? SharedMemoryID not found in " << conf->getControllersSection() << " section" << endl;
return 1;
}
RTUExchange* rs = RTUExchange::init_rtuexchange(argc,argv,shmID,0,"rs");
if( !rs )
{
dlog[Debug::CRIT] << "(rtuexchange): init не прошёл..." << endl;
return 1;
}
ObjectsActivator act;
act.addObject(static_cast<class UniSetObject*>(rs));
SystemMessage sm(SystemMessage::StartUp);
act.broadcast( sm.transport_msg() );
unideb(Debug::ANY) << "\n\n\n";
unideb[Debug::ANY] << "(main): -------------- RTU Exchange START -------------------------\n\n";
dlog(Debug::ANY) << "\n\n\n";
dlog[Debug::ANY] << "(main): -------------- RTU Exchange START -------------------------\n\n";
act.run(false);
while( waitpid(-1, 0, 0) > 0 );
return 0;
}
catch( Exception& ex )
{
dlog[Debug::CRIT] << "(rtuexchange): " << ex << std::endl;
}
catch(...)
{
dlog[Debug::CRIT] << "(rtuexchange): catch ..." << std::endl;
}
while( waitpid(-1, 0, 0) > 0 );
return 1;
}
// --------------------------------------------------------------------------
#include <string>
#include <getopt.h>
#include "modbus/ModbusRTUMaster.h"
#include "RTUStorage.h"
// --------------------------------------------------------------------------
using namespace UniSetTypes;
using namespace std;
// --------------------------------------------------------------------------
static struct option longopts[] = {
{ "help", no_argument, 0, 'h' },
{ "slave", required_argument, 0, 'q' },
{ "device", required_argument, 0, 'd' },
{ "verbose", no_argument, 0, 'v' },
{ "speed", required_argument, 0, 's' },
{ "use485F", no_argument, 0, 'y' },
{ NULL, 0, 0, 0 }
};
// --------------------------------------------------------------------------
static void print_help()
{
printf("Usage: rtustate -q addr\n");
printf("-h|--help - this message\n");
printf("[-q|--slave] addr - Slave address. Default: 0x01.\n");
printf("[-d|--device] dev - use device dev. Default: /dev/ttyS0\n");
printf("[-s|--speed] speed - 9600,14400,19200,38400,57600,115200. Default: 38400.\n");
printf("[-t|--timeout] msec - Timeout. Default: 2000.\n");
printf("[-v|--verbose] - Print all messages to stdout\n");
printf("[-y|--use485F] - use RS485 Fastwel.\n");
}
// --------------------------------------------------------------------------
int main( int argc, char **argv )
{
int optindex = 0;
int opt = 0;
int verb = 0;
string dev("/dev/ttyS0");
string speed("38400");
ModbusRTU::ModbusAddr slaveaddr = 0x01;
int tout = 2000;
DebugStream dlog;
int use485 = 0;
try
{
while( (opt = getopt_long(argc, argv, "hva:d:s:t:q:",longopts,&optindex)) != -1 )
{
switch (opt)
{
case 'h':
print_help();
return 0;
case 'd':
dev = string(optarg);
break;
case 's':
speed = string(optarg);
break;
case 't':
tout = uni_atoi(optarg);
break;
case 'q':
slaveaddr = ModbusRTU::str2mbAddr(optarg);
break;
case 'v':
verb = 1;
break;
case 'y':
use485 = 1;
break;
case '?':
default:
printf("? argumnet\n");
return 0;
}
}
if( verb )
{
cout << "(init): dev=" << dev
<< " speed=" << speed
<< " timeout=" << tout << " msec "
<< endl;
}
ModbusRTUMaster mb(dev,use485);
if( verb )
dlog.addLevel( Debug::type(Debug::CRIT | Debug::WARN | Debug::INFO) );
mb.setTimeout(tout);
mb.setSpeed(speed);
mb.setLog(dlog);
RTUStorage rtu(slaveaddr);
rtu.poll(&mb);
cout << rtu << endl;
for( int i=0; i<24; i++ )
cout << "UNIO1 AI" << i << ": " << rtu.getFloat( RTUStorage::nJ1, i, UniversalIO::AnalogInput ) << endl;
for( int i=0; i<24; i++ )
cout << "UNIO1 DI" << i << ": " << rtu.getState( RTUStorage::nJ1, i, UniversalIO::DigitalInput ) << endl;
return 0;
}
catch( ModbusRTU::mbException& ex )
{
cerr << "(rtustate): " << ex << endl;
}
catch(SystemError& err)
{
cerr << "(rtustate): " << err << endl;
}
catch(Exception& ex)
{
cerr << "(rtustate): " << ex << endl;
}
catch(...)
{
cerr << "(rtustate): catch(...)" << endl;
}
return 1;
}
// --------------------------------------------------------------------------
#!/bin/sh
uniset-start.sh -f ./uniset-rtuexchange --confile test.xml \
--smemory-id SharedMemory \
--rs-dev /dev/cbsideA0 \
--rs-name RTUExchange \
--rs-speed 38400 \
--rs-filter-field rs \
--rs-filter-value 1 \
--dlog-add-levels info,crit,warn,level4,level3 \
--rs-force 0 \
--rs-force-out 0 \
#,level3
# --rs-force 1 \
// --------------------------------------------------------------------------
#include <iostream>
#include <iomanip>
#include "UniSetTypes.h"
#include "VTypes.h"
// --------------------------------------------------------------------------
using namespace std;
using namespace VTypes;
// --------------------------------------------------------------------------
static void print_help()
{
printf("Usage: vtconv TYPE[F2|F4|I2|U2] hex1 hex2 [hex3 hex4]\n");
}
// --------------------------------------------------------------------------
int main( int argc, const char **argv )
{
VTypes::F2 f2;
f2.raw.val = 2.345;
cout << "Example(F2): float=" << f2.raw.val
<< " regs:"
<< " v[0]=" << f2.raw.v[0]
<< " v[1]=" << f2.raw.v[1]
<< endl;
VTypes::F4 f4;
f4.raw.val = 2.345123123;
cout << "Example(F4): float=" << f4.raw.val
<< " regs:"
<< " v[0]=" << f4.raw.v[0]
<< " v[1]=" << f4.raw.v[1]
<< " v[2]=" << f4.raw.v[2]
<< " v[3]=" << f4.raw.v[3]
<< endl;
cout << "-------------" << endl << endl;
VTypes::I2 i2;
i2.raw.val = -6553004;
cout << "Example(I2): int=" << i2.raw.val
<< " regs:"
<< " v[0]=" << i2.raw.v[0]
<< " v[1]=" << i2.raw.v[1]
<< endl;
cout << "-------------" << endl << endl;
VTypes::U2 u2;
u2.raw.val = 655300400;
cout << "Example(U2): unsigned int=" << u2.raw.val
<< " regs:"
<< " v[0]=" << u2.raw.v[0]
<< " v[1]=" << u2.raw.v[1]
<< endl;
cout << "-------------" << endl << endl;
// return 0;
unsigned short v[4];
memset(v,0,sizeof(v));
const char* type="";
if( argc<3 )
{
print_help();
return 1;
}
type = argv[1];
v[0] = UniSetTypes::uni_atoi(argv[2]);
if( argc>3 )
v[1] = UniSetTypes::uni_atoi(argv[3]);
if( argc>4 )
v[2] = UniSetTypes::uni_atoi(argv[4]);
if( argc>5 )
v[3] = UniSetTypes::uni_atoi(argv[5]);
if( !strcmp(type,"F2") )
{
VTypes::F2 f(v,sizeof(v));
cout << "(F2): v[0]=" << v[0]
<< " v[1]=" << v[1]
<< " --> (float) " << (float)f << endl;
}
else if( !strcmp(type,"F4") )
{
VTypes::F4 f(v,sizeof(v));
cout << "(F4): v[0]=" << v[0]
<< " v[1]=" << v[1]
<< " v[2]=" << v[2]
<< " v[3]=" << v[3]
<< " --> (float) " << (float)f << endl;
}
else if( !strcmp(type,"I2") )
{
VTypes::I2 i(v,sizeof(v));
cout << "(I2): v[0]=" << v[0]
<< " v[1]=" << v[1]
<< " --> (int) " << (int)i << endl;
}
else if( !strcmp(type,"U2") )
{
VTypes::U2 i(v,sizeof(v));
cout << "(U2): v[0]=" << v[0]
<< " v[1]=" << v[1]
<< " --> (unsigned int) " << (unsigned int)i << endl;
}
else
{
cout << " Unknown type: " << type << endl;
}
return 0;
}
// --------------------------------------------------------------------------
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