Commit 01a03deb authored by Pavel Vainerman's avatar Pavel Vainerman

(Modbus): работы над ModbusMaster умеющим работать с несколькими

каналами связи
parent cf98efd1
......@@ -50,6 +50,16 @@
<item addr="0x01" invert="0" respondSensor="RespondRTU_S" timeout="5000" modeSensor="MB1_Mode_AS"/>
</DeviceList>
</MBMaster1>
<MBMultiMaster1 levels="info,warn,crit" name="MBMaster1" poll_time="200" reply_timeout="60">
<DeviceList>
<item addr="0x01" invert="0" respondSensor="RespondRTU_S" timeout="5000" modeSensor="MB1_Mode_AS"/>
</DeviceList>
<SlaveList>
<item ip="127.0.0.1" port="2049" recv_timeout="200"/>
<item ip="127.0.0.1" port="2048" recv_timeout="200"/>
</SlaveList>
</MBMultiMaster1>
<MBSlave1 addr="0x31" aftersend-pause="0" dev="/dev/ttyS0" levels="info,warn,crit" name="MBSlave1" poll_time="200" reply_timeout="60" speed="9600">
<filelist>
<!-- Список файлов разрешённых для передачи по modbus
......@@ -219,6 +229,7 @@
<item id="6007" name="UDPExchange2"/>
<item id="6008" name="UNetExchange"/>
<item id="6009" name="MBMaster2"/>
<item id="6010" name="MBMultiMaster1"/>
</objects>
</ObjectsMap>
<messages idfromfile="1" name="messages">
......
// -----------------------------------------------------------------------------
#include <cmath>
#include <limits>
#include <sstream>
#include <Exceptions.h>
#include <extensions/Extensions.h>
#include "MBTCPMultiMaster.h"
// -----------------------------------------------------------------------------
using namespace std;
using namespace UniSetTypes;
using namespace UniSetExtensions;
// -----------------------------------------------------------------------------
MBTCPMultiMaster::MBTCPMultiMaster( UniSetTypes::ObjectId objId, UniSetTypes::ObjectId shmId,
SharedMemory* ic, const std::string prefix ):
MBExchange(objId,shmId,ic,prefix),
force_disconnect(true),
pollThread(0),
checkThread(0)
{
if( objId == DefaultObjectId )
throw UniSetTypes::SystemError("(MBTCPMultiMaster): objId=-1?!! Use --" + prefix + "-name" );
// префикс для "свойств" - по умолчанию
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;
UniXML_iterator it(cnode);
checktime = conf->getArgPInt("--" + prefix + "-checktime",it.getProp("checktime"), 5000);
UniXML_iterator it1(it);
if( !it1.find("SlaveList") )
{
ostringstream err;
err << myname << "(init): not found <SlaveList>";
dlog[Debug::CRIT] << err.str() << endl;
throw UniSetTypes::SystemError(err.str());
}
if( !it1.goChildren() )
{
ostringstream err;
err << myname << "(init): empty <SlaveList> ?!";
dlog[Debug::CRIT] << err.str() << endl;
throw UniSetTypes::SystemError(err.str());
}
for( ;it1.getCurrent(); it1++ )
{
MBSlaveInfo sinf;
sinf.ip = it1.getProp("ip");
if( sinf.ip.empty() )
{
ostringstream err;
err << myname << "(init): ip='' in <SlaveList>";
dlog[Debug::CRIT] << err.str() << endl;
throw UniSetTypes::SystemError(err.str());
}
sinf.port = it1.getIntProp("port");
if( sinf.port <=0 )
{
ostringstream err;
err << myname << "(init): ERROR: port=''" << sinf.port << " for ip='" << sinf.ip << "' in <SlaveList>";
dlog[Debug::CRIT] << err.str() << endl;
throw UniSetTypes::SystemError(err.str());
}
if( !it1.getProp("respond").empty() )
{
sinf.respond_id = conf->getSensorID( it1.getProp("respond") );
if( sinf.respond_id == DefaultObjectId )
{
ostringstream err;
err << myname << "(init): ERROR: Unknown SensorID for '" << it1.getProp("respond") << "' in <SlaveList>";
dlog[Debug::CRIT] << err.str() << endl;
throw UniSetTypes::SystemError(err.str());
}
}
sinf.priority = it1.getIntProp("priority");
sinf.mbtcp = new ModbusTCPMaster();
sinf.recv_timeout = it1.getPIntProp("recv_timeout",recv_timeout);
sinf.aftersend_pause = it1.getPIntProp("aftersend_pause",aftersend_pause);
sinf.sleepPause_usec = it1.getPIntProp("sleepPause_usec",sleepPause_usec);
sinf.respond_invert = it1.getPIntProp("respond_invert",0);
// sinf.force_disconnect = it.getProp("persistent_connection") ? false : true;
ostringstream n;
n << sinf.ip << ":" << sinf.port;
sinf.myname = n.str();
mblist.push_back(sinf);
if( dlog.debugging(Debug::INFO) )
dlog[Debug::INFO] << myname << "(init): add slave channel " << sinf.myname << endl;
}
if( mblist.empty() )
{
ostringstream err;
err << myname << "(init): empty <SlaveList>!";
dlog[Debug::CRIT] << err.str() << endl;
throw UniSetTypes::SystemError(err.str());
}
mblist.sort();
mbi = mblist.begin();
if( shm->isLocalwork() )
{
readConfiguration();
rtuQueryOptimization(rmap);
initDeviceList();
}
else
ic->addReadItem( sigc::mem_fun(this,&MBTCPMultiMaster::readItem) );
pollThread = new ThreadCreator<MBTCPMultiMaster>(this, &MBTCPMultiMaster::poll_thread);
checkThread = new ThreadCreator<MBTCPMultiMaster>(this, &MBTCPMultiMaster::check_thread);
if( dlog.debugging(Debug::INFO) )
printMap(rmap);
}
// -----------------------------------------------------------------------------
MBTCPMultiMaster::~MBTCPMultiMaster()
{
delete pollThread;
delete checkThread;
for( MBSlaveList::iterator it=mblist.begin(); it!=mblist.end(); ++it )
{
delete it->mbtcp;
it->mbtcp = 0;
mbi = mblist.end();
}
}
// -----------------------------------------------------------------------------
ModbusClient* MBTCPMultiMaster::initMB( bool reopen )
{
if( mbi!=mblist.end() && mbi->respond )
{
cerr << "******** initMB(0): " << mbi->myname << endl;
mb = mbi->mbtcp;
return mbi->mbtcp;
}
if( mbi != mblist.end() )
mbi->mbtcp->disconnect();
// проходим по списку (в обратном порядке, т.к. самый приоритетный в конце)
for( MBSlaveList::reverse_iterator it=mblist.rbegin(); it!=mblist.rend(); ++it )
{
if( it->respond )
{
mb = mbi->mbtcp;
mbi = it.base();
cerr << "******** initMB(L): " << mbi->myname << endl;
return it->mbtcp;
}
}
cout << myname << "(initMB): return 0..." << endl;
mbi = mblist.end();
mb = 0;
return 0;
}
// -----------------------------------------------------------------------------
bool MBTCPMultiMaster::MBSlaveInfo::check()
{
return mbtcp->checkConnection(ip,port);
}
// -----------------------------------------------------------------------------
bool MBTCPMultiMaster::MBSlaveInfo::init()
{
try
{
ost::Thread::setException(ost::Thread::throwException);
ost::InetAddress ia(ip.c_str());
mbtcp->connect(ia,port);
// mbtcp->setForceDisconnect(force_disconnect);
if( recv_timeout > 0 )
mbtcp->setTimeout(recv_timeout);
mbtcp->setSleepPause(sleepPause_usec);
mbtcp->setAfterSendPause(aftersend_pause);
if( dlog.debugging(Debug::INFO) )
dlog[Debug::INFO] << "(init): " << myname << endl;
if( dlog.debugging(Debug::LEVEL9) )
mbtcp->setLog(dlog);
return true;
}
catch( ModbusRTU::mbException& ex )
{
if( dlog.debugging(Debug::WARN) )
dlog[Debug::WARN] << "(init): " << ex << endl;
}
catch(...)
{
if( dlog.debugging(Debug::WARN) )
dlog[Debug::WARN] << "(init): " << myname << " catch ..." << endl;
}
return false;
}
// -----------------------------------------------------------------------------
void MBTCPMultiMaster::sysCommand( UniSetTypes::SystemMessage *sm )
{
MBExchange::sysCommand(sm);
if( sm->command == SystemMessage::StartUp )
{
pollThread->start();
checkThread->start();
}
}
// -----------------------------------------------------------------------------
void MBTCPMultiMaster::poll_thread()
{
{
uniset_mutex_lock l(pollMutex,300);
ptTimeout.reset();
}
while( checkProcActive() )
{
try
{
if( sidExchangeMode != DefaultObjectId && force )
exchangeMode = shm->localGetValue(aitExchangeMode,sidExchangeMode);
}
catch(...){}
try
{
poll();
}
catch(...){}
if( !checkProcActive() )
break;
msleep(polltime);
}
}
// -----------------------------------------------------------------------------
void MBTCPMultiMaster::check_thread()
{
while( checkProcActive() )
{
for( MBSlaveList::iterator it=mblist.begin(); it!=mblist.end(); ++it )
{
try
{
bool r = it->check();
if( dlog.debugging(Debug::INFO) )
dlog[Debug::INFO] << myname << "(check): " << it->myname << " " << ( r ? "OK" : "FAIL" ) << endl;
try
{
if( it->respond_id != DefaultObjectId && (force_out || r != it->respond) )
{
bool set = it->respond_invert ? !it->respond : it->respond;
shm->localSaveState(it->respond_dit,it->respond_id,set,getId());
}
}
catch( Exception& ex )
{
if( dlog.debugging(Debug::CRIT) )
dlog[Debug::CRIT] << myname << "(check): (respond) " << ex << std::endl;
}
catch(...){}
it->respond = r;
}
catch(...){}
if( !checkProcActive() )
break;
}
if( !checkProcActive() )
break;
msleep(checktime);
}
}
// -----------------------------------------------------------------------------
void MBTCPMultiMaster::initIterators()
{
MBExchange::initIterators();
for( MBSlaveList::iterator it=mblist.begin(); it!=mblist.end(); ++it )
shm->initDIterator(it->respond_dit);
}
// -----------------------------------------------------------------------------
void MBTCPMultiMaster::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-iaddr hostname,IP - IP опрашиваемого узла" << endl;
cout << "--prefix-gateway-port num - port на опрашиваемом узле" << endl;
cout << "--prefix-persistent-connection 0,1 - Не закрывать соединение на каждом цикле опроса" << endl;
}
// -----------------------------------------------------------------------------
MBTCPMultiMaster* MBTCPMultiMaster::init_mbmaster( int argc, const char* const* argv,
UniSetTypes::ObjectId icID, SharedMemory* ic,
const std::string prefix )
{
string name = conf->getArgParam("--" + prefix + "-name","MBTCPMultiMaster1");
if( name.empty() )
{
dlog[Debug::CRIT] << "(MBTCPMultiMaster): Не задан name'" << endl;
return 0;
}
ObjectId ID = conf->getObjectID(name);
if( ID == UniSetTypes::DefaultObjectId )
{
dlog[Debug::CRIT] << "(MBTCPMultiMaster): идентификатор '" << name
<< "' не найден в конф. файле!"
<< " в секции " << conf->getObjectsSection() << endl;
return 0;
}
dlog[Debug::INFO] << "(MBTCPMultiMaster): name = " << name << "(" << ID << ")" << endl;
return new MBTCPMultiMaster(ID,icID,ic,prefix);
}
// -----------------------------------------------------------------------------
#ifndef _MBTCPMultiMaster_H_
#define _MBTCPMultiMaster_H_
// -----------------------------------------------------------------------------
#include <ostream>
#include <string>
#include <map>
#include <vector>
#include "MBExchange.h"
#include "modbus/ModbusTCPMaster.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 Секция с настройками
При своём старте, в конфигурационном файле ищётся секция с названием объекта,
в которой указываются настроечные параметры по умолчанию.
Пример:
\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>
<SlaveList>
<item ip="" port="" respond_id="" priority=""/>
<item ip="" port="" respond_id="" priority=""/>
<SlaveList>
</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.
Секция <SlaveList> позволяет задать несколько каналов связи со Slave-устройством. Это удобно для случая, когда Slave имеет
более одного канала связи с ним (основной и резервный например).
- \b ip - ip-адрес
- \b port - порт
- \b respond - датчик связи по данному каналу (помимо обобщённого)
- \b priority - приоритет канала (чем больше число, тем выше приоритет)
\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 - таймаут на определение отсутсвия связи
(после этого идёт попытка реинициализировать соединение)
По умолчанию 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]
- 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 - при наличии выведет кол-во посланных запросов за этот промежуток времени.
\b --xxx-set-prop-prefix [str] - Использовать 'str' в качестве префикса для свойств.
Если не указать 'str' будет использован пустой префикс.
Если параметры не заданы, будет произведена попытка загрузить все датчики, у которых
присутствуют необходимые настроечные параметры.
\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
\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_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.
\section sec_MBTCP_ExchangeMode Управление режимом работы MBTCPMultiMaster
В MBTCPMultiMaster заложена возможность управлять режимом работы процесса. Поддерживаются
следующие режимы:
- \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="". Константы определяющие режимы объявлены в MBTCPMultiMaster::ExchangeMode.
*/
// -----------------------------------------------------------------------------
/*!
\par Реализация Modbus TCP Master для обмена с многими ModbusRTU устройствами
через один modbus tcp шлюз.
\par Чтобы не зависеть от таймаутов TCP соединений, которые могут неопределённо зависать
на создании соединения с недоступным хостом. Обмен вынесен в отдельный поток.
При этом в этом же потоке обновляются данные в SM. В свою очередь информация о датчиках
связи обновляется в основном потоке (чтобы не зависеть от TCP).
*/
class MBTCPMultiMaster:
public MBExchange
{
public:
MBTCPMultiMaster( UniSetTypes::ObjectId objId, UniSetTypes::ObjectId shmID, SharedMemory* ic=0,
const std::string prefix="mbtcp" );
virtual ~MBTCPMultiMaster();
/*! глобальная функция для инициализации объекта */
static MBTCPMultiMaster* 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 );
virtual void initIterators();
UniSetTypes::uniset_mutex mbMutex;
int recv_timeout;
virtual ModbusClient* initMB( bool reopen=false );
void poll_thread();
void check_thread();
bool force_disconnect;
int checktime;
private:
MBTCPMultiMaster();
struct MBSlaveInfo
{
MBSlaveInfo():ip(""),port(0),mbtcp(0),priority(0),
respond(false),respond_id(UniSetTypes::DefaultObjectId),respond_invert(false),
recv_timeout(200),aftersend_pause(0),sleepPause_usec(100),myname(""){}
std::string ip;
int port;
ModbusTCPMaster* mbtcp;
int priority;
bool respond;
UniSetTypes::ObjectId respond_id;
IOController::DIOStateList::iterator respond_dit;
bool respond_invert;
inline bool operator < ( const MBSlaveInfo& mbs ) const
{
return priority < mbs.priority;
}
bool init();
bool check();
int recv_timeout;
int aftersend_pause;
int sleepPause_usec;
std::string myname;
};
typedef std::list<MBSlaveInfo> MBSlaveList;
MBSlaveList mblist;
MBSlaveList::iterator mbi;
// т.к. TCP может "зависнуть" на подключении к недоступному узлу
// делаем опрос в отдельном потоке
ThreadCreator<MBTCPMultiMaster>* pollThread; /*!< поток опроса */
UniSetTypes::uniset_mutex tcpMutex;
ThreadCreator<MBTCPMultiMaster>* checkThread; /*!< поток проверки связи по другим каналам */
};
// -----------------------------------------------------------------------------
#endif // _MBTCPMultiMaster_H_
// -----------------------------------------------------------------------------
......@@ -6,7 +6,7 @@ libMBMaster_la_LIBADD = $(top_builddir)/lib/libUniSet.la \
libMBMaster_la_CXXFLAGS = -I$(top_builddir)/extensions/include -I$(top_builddir)/extensions/SharedMemory $(SIGC_CFLAGS)
libMBMaster_la_SOURCES = MBExchange.cc RTUStorage.cc
bin_PROGRAMS = @PACKAGE@-mbtcpmaster @PACKAGE@-rtuexchange @PACKAGE@-mtr-conv @PACKAGE@-rtu188-state @PACKAGE@-vtconv @PACKAGE@-mtr-setup @PACKAGE@-mtr-read
bin_PROGRAMS = @PACKAGE@-mbtcpmaster @PACKAGE@-mbtcpmultimaster @PACKAGE@-rtuexchange @PACKAGE@-mtr-conv @PACKAGE@-rtu188-state @PACKAGE@-vtconv @PACKAGE@-mtr-setup @PACKAGE@-mtr-read
lib_LTLIBRARIES = libUniSetMBTCPMaster.la libUniSetRTU.la
# -------------------- Mobus TCP -------------------
......@@ -20,7 +20,7 @@ libUniSetMBTCPMaster_la_LIBADD = libMBMaster.la $(top_builddir)/lib/libUniSet.l
$(top_builddir)/extensions/lib/libUniSetExtensions.la \
$(SIGC_LIBS)
libUniSetMBTCPMaster_la_CXXFLAGS = -I$(top_builddir)/extensions/include -I$(top_builddir)/extensions/SharedMemory $(SIGC_CFLAGS)
libUniSetMBTCPMaster_la_SOURCES = MBTCPMaster.cc
libUniSetMBTCPMaster_la_SOURCES = MBTCPMaster.cc MBTCPMultiMaster.cc
@PACKAGE@_mbtcpmaster_SOURCES = main.cc
@PACKAGE@_mbtcpmaster_LDADD = libUniSetMBTCPMaster.la libMBMaster.la $(top_builddir)/lib/libUniSet.la \
......@@ -29,6 +29,13 @@ libUniSetMBTCPMaster_la_SOURCES = MBTCPMaster.cc
$(SIGC_LIBS)
@PACKAGE@_mbtcpmaster_CXXFLAGS = -I$(top_builddir)/extensions/include -I$(top_builddir)/extensions/SharedMemory $(SIGC_CFLAGS)
@PACKAGE@_mbtcpmultimaster_SOURCES = multi-main.cc
@PACKAGE@_mbtcpmultimaster_LDADD = libUniSetMBTCPMaster.la libMBMaster.la $(top_builddir)/lib/libUniSet.la \
$(top_builddir)/extensions/SharedMemory/libUniSetSharedMemory.la \
$(top_builddir)/extensions/lib/libUniSetExtensions.la \
$(SIGC_LIBS)
@PACKAGE@_mbtcpmultimaster_CXXFLAGS = -I$(top_builddir)/extensions/include -I$(top_builddir)/extensions/SharedMemory $(SIGC_CFLAGS)
# -------------------- Mobus RTU -------------------
# не забывайте править версию в pc-файле
URTU_VER=2:1:0
......
../../Utilities/scripts/uniset-start.sh
\ No newline at end of file
......@@ -21,6 +21,8 @@ class ModbusTCPMaster:
void disconnect();
bool isConnection();
static bool checkConnection( const std::string ip, int port, int timeout_msec=100 );
inline void setForceDisconnect( bool s )
{
force_disconnect = s;
......
......@@ -55,7 +55,7 @@ mbErrCode ModbusTCPMaster::query( ModbusAddr addr, ModbusMessage& msg,
if( iaddr.empty() )
{
dlog[Debug::WARN] << "(query): unknown ip address for server..." << endl;
return erHardwareError;
return erTimeOut; // erHardwareError
}
if( !isConnection() )
......@@ -248,7 +248,7 @@ mbErrCode ModbusTCPMaster::query( ModbusAddr addr, ModbusMessage& msg,
dlog[Debug::WARN] << "(query): cath..." << endl;
}
return erHardwareError;
return erTimeOut; // erHardwareError
}
// -------------------------------------------------------------------------
void ModbusTCPMaster::cleanInputStream()
......@@ -262,6 +262,28 @@ void ModbusTCPMaster::cleanInputStream()
while( ret > 0);
}
// -------------------------------------------------------------------------
bool ModbusTCPMaster::checkConnection( const std::string ip, int port, int timeout_msec )
{
try
{
ostringstream s;
s << ip << ":" << port;
// Проверяем просто попыткой создать соединение..
// ost::Thread::setException(ost::Thread::throwException);
// TCPStream (const char *name, Family family=IPV4, unsigned mss=536, bool throwflag=false, timeout_t timer=0)
ost::TCPStream t(s.str().c_str(),ost::Socket::IPV4,536,true,timeout_msec);
t.disconnect();
return true;
}
catch(...)
{
}
return false;
}
// -------------------------------------------------------------------------
void ModbusTCPMaster::reconnect()
{
if( dlog.debugging(Debug::INFO) )
......@@ -330,15 +352,21 @@ void ModbusTCPMaster::connect( ost::InetAddress addr, int port )
}
catch( std::exception& e )
{
ostringstream s;
s << "(ModbusTCPMaster): connection " << s.str() << " error: " << e.what();
dlog[Debug::CRIT] << s.str() << std::endl;
if( dlog.debugging(Debug::CRIT) )
{
ostringstream s;
s << "(ModbusTCPMaster): connection " << s.str() << " error: " << e.what();
dlog[Debug::CRIT] << s.str() << std::endl;
}
}
catch( ... )
{
ostringstream s;
s << "(ModbusTCPMaster): connection " << s.str() << " error: catch ...";
dlog[Debug::CRIT] << s.str() << std::endl;
if( dlog.debugging(Debug::CRIT) )
{
ostringstream s;
s << "(ModbusTCPMaster): connection " << s.str() << " error: catch ...";
dlog[Debug::CRIT] << s.str() << std::endl;
}
}
// }
}
......
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