Commit 1ff72a00 authored by Pavel Vainerman's avatar Pavel Vainerman

(ModbusTCPMultiMaster): переделал механизм проверки соединения.

Теперь можно указать функцию,регистр и адрес устройства для проверки наличия соединения. Либо можно указать, чтобы параметы брались из списка обмена. При этом если эти параметры не указаны, проверка производится (как раньше) просто попыткой создания соединения.
parent a9dc289d
......@@ -31,6 +31,7 @@
- \ref sec_MBTCPM_Conf
- \ref sec_MBTCPM_ConfList
- \ref sec_MBTCPM_ExchangeMode
- \ref sec_MBTCPM_CheckConnection
\section sec_MBTCPM_Comm Общее описание ModbusTCPMultiMaster
Класс реализует процесс обмена (опрос/запись) с RTU-устройствами,
......@@ -85,6 +86,9 @@
- \b respond_invert - инвертировать датчик связи (DI)
- \b force [1,0] - "1" - обновлять значение датчика связи в SM принудительно на каждом цикле проверки ("0" - только по изменению).
- \b timeout - таймаут на определение отсутсвия связи для данного канала. По умолчанию берётся глобальный.
- \b checkFunc - Номер функции для проверки соединения
- \b checkAddr - Адрес устройства для проверки соединения
- \b checkReg - Регистр для проверки соединения
\par Параметры запуска
......@@ -143,6 +147,11 @@
\b --xxx-activate-timeout msec . По умолчанию 2000. - время ожидания готовности SharedMemory к работе.
\b --xxx-check-func [1,2,3,4] - Номер функции для проверки соединения
\b --xxx-check-addr [1..255 ] - Адрес устройства для проверки соединения
\b --xxx-check-reg [1..65535] - Регистр для проверки соединения
\b --xxx-check-init-from-regmap - Взять адрес, функцию и регистр для проверки связи из списка опроса
\section sec_MBTCPM_ConfList Конфигурирование списка регистров для ModbusTCP master
Конфигурационные параметры задаются в секции <sensors> конфигурационного файла.
Список обрабатываемых регистров задаётся при помощи двух параметров командной строки
......@@ -207,8 +216,6 @@
регистры в устройство писатся не будут. Чтобы отключить это поведение, можно указать параметр
- \b tcp_sm_initOK - [0|1] Игнорировать начальную инициализацию из SM (сразу писать в устройство)
При этом будет записывыться значение "default".
\warning Регистр должен быть уникальным. И может повторятся только если указан параметр \a nbit или \a nbyte.
......@@ -228,10 +235,43 @@
Режимы переключаются при помощи датчика, который можно задать либо аргументом командной строки
\b --prefix-exchange-mode-id либо в конф. файле параметром \b exchangeModeID="". Константы определяющие режимы объявлены в MBTCPMultiMaster::ExchangeMode.
\section sec_MBTCPM_CheckConnection Проверка соединения
Для контроля состояния связи по "резервным" каналам создаётся специальный поток (check_thread), в котором
происходит периодическая проверка связи по всем "пассивным"(резервным) в данный момент каналам. Это используется
как для общей диагностики в системе, так и при выборе на какой канал переключаться в случае пропажи связи в основном канале.
Т.е. будет выбран ближайший приоритетный канал у которого выставлен признак что есть связь.
Период проверки связи по "резервным" каналам задаётся при помощи --prefix-checktime или параметром checktime="" в конфигурационном файле.
В MBTCPMultiMaster реализовано два механизма проверки связи.
- По умолчанию используется простая установка соединения и тут же его разрыв. Т.е. данные никакие не посылаются,
но проверяется что host и port доступны для подключения.
- Второй способ: это проверка соединения с посылкой modbus-запроса. Для этого имеется два способа
указать адрес устройства, регистр и функция опроса для проверки.
Либо в секции <GateList> для каждого канала можно указать:
- адрес устройства \b checkAddr=""
- функцию проверки \b checkFunc="" - функция может быть только [01,02,03,04] (т.е. функции чтения).
- регистр \b checkReg
Либо в командной строке \b задать параметры --prefix-check-addr, --prefix-check-func, --prefix-check-reg,
которые будут одинаковыми для \b ВСЕХ \b КАНАЛОВ.
Помимо этого если указать в командной строке аргумент --prefix-check-init-from-regmap, то для тестирования
соединения будет взят первый попавшийся регистр из списка обмена.
\warning Способ проверки при помощи "modbus-запроса" имеет ряд проблем: Если фактически производится
обмен с несколькими устройствами (несколько mbaddr) через TCP-шлюз, то может быть "ложное" срабатвание,
т.к. фактически состояние канала будет определяться только под связи с каким-то одним конкретным устройством.
И получается, что если обмен ведётся например с тремя устройствами, но
проверка канала происходит только по связи с первым, то если оно перестанет отвечать, это будет считаться
сбоем всего канала и этот канал будет исключён из обмена (!). Если ведётся обмен только с одним устройством,
такой проблеммы не возникает.
Но к плюсам данного способа проверки связи ("modbus-запросом") является то, что соедиенение поддерживается
постоянным, в отличие от "первого способа" при котором оно создаётся и сразу рвётся и если проверка
настроена достаточно часто ( < TIME_WAIT для сокетов), то при длительной работе могут закончится дескрипторы
на создание сокетов.
*/
// -----------------------------------------------------------------------------
/*!
\par Реализация Modbus TCP Multi Master для обмена с многими ModbusRTU устройствами
\par Реализация Modbus TCP MultiMaster для обмена с многими ModbusRTU устройствами
через один modbus tcp шлюз, доступный по нескольким ip-адресам.
\par Чтобы не зависеть от таймаутов TCP соединений, которые могут неопределённо зависать
......@@ -263,6 +303,7 @@ class MBTCPMultiMaster:
virtual std::shared_ptr<ModbusClient> initMB( bool reopen = false ) override;
virtual void sigterm( int signo ) override;
virtual bool deactivateObject() override;
void initCheckConnectionParameters();
void poll_thread();
void check_thread();
......@@ -288,6 +329,11 @@ class MBTCPMultiMaster:
std::shared_ptr<ModbusTCPMaster> mbtcp;
int priority;
// параметры для проверки соединения..
ModbusRTU::SlaveFunctionCode checkFunc = { ModbusRTU::fnUnknown };
ModbusRTU::ModbusAddr checkAddr = { 0x00 };
ModbusRTU::ModbusData checkReg = { 0 };
bool respond;
UniSetTypes::ObjectId respond_id;
IOController::IOStateList::iterator respond_it;
......@@ -297,18 +343,14 @@ class MBTCPMultiMaster:
DelayTimer respondDelay;
timeout_t channel_timeout = { 0 };
inline bool operator < ( const MBSlaveInfo& mbs ) const
inline bool operator < ( const MBSlaveInfo& mbs ) const noexcept
{
return priority < mbs.priority;
}
bool init( std::shared_ptr<DebugStream>& mblog );
bool check() const;
inline void setUse( bool st )
{
respond_init = !( st && !use );
use = st;
}
bool check();
void setUse( bool st );
timeout_t recv_timeout;
timeout_t aftersend_pause;
......@@ -323,9 +365,11 @@ class MBTCPMultiMaster:
PassiveTimer ptIgnoreTimeout;
const std::string getShortInfo() const;
std::mutex mutInit;
};
typedef std::list<MBSlaveInfo> MBGateList;
typedef std::list<std::shared_ptr<MBSlaveInfo>> MBGateList;
MBGateList mblist;
MBGateList::reverse_iterator mbi;
......
......@@ -4,7 +4,7 @@ MBPARAM=
for N in `seq 1 100`; do
MBPARAM="$MBPARAM --mbtcp${N}-name MBTCP${N} --mbtcp${N}-confnode MBPerfTestMaster --mbtcp${N}-filter-field mbperf
--mbtcp${N}-filter-value $N --mbtcp${N}-persistent-connection 1 --mbtcp${N}-log-add-levels crit,warn"
--mbtcp${N}-filter-value $N --mbtcp${N}-persistent-connection 1 --mbtcp${N}-check-init-from-regmap --mbtcp${N}-log-add-levels warn,crit"
done
#echo "$MBPARAM"
......
......@@ -14,6 +14,10 @@
--mbtcp-log-add-levels level4,warn,crit \
--mbtcp-persistent-connection 1 \
--mbtcp-run-logserver \
--mbtcpX-check-init-from-regmap \
--mbtcpX-check-addr 1 \
--mbtcpX-check-reg 66 \
--mbtcpX-check-func 1 \
$*
#--dlog-add-levels info,crit,warn,level4,level3,level9 \
......
......@@ -150,6 +150,7 @@ namespace ModbusRTU
float dat2f( const ModbusData dat1, const ModbusData dat2 );
// -------------------------------------------------------------------------
bool isWriteFunction( SlaveFunctionCode c );
bool isReadFunction( SlaveFunctionCode c );
// -------------------------------------------------------------------------
/*! Заголовок сообщений */
struct ModbusHeader
......
......@@ -194,6 +194,18 @@ bool ModbusRTU::isWriteFunction( SlaveFunctionCode c )
return false;
}
// -------------------------------------------------------------------------
bool ModbusRTU::isReadFunction( SlaveFunctionCode c )
{
if( c == fnReadCoilStatus ||
c == fnReadInputStatus ||
c == fnReadOutputRegisters ||
c == fnReadInputRegisters )
return true;
return false;
}
// -------------------------------------------------------------------------
std::ostream& ModbusRTU::mbPrintMessage( std::ostream& os, ModbusByte* m, size_t len )
{
// Чтобы не менять настройки 'os'
......
......@@ -16,17 +16,23 @@ static atomic_bool cancel = {false};
// --------------------------------------------------------
bool run_test_server()
{
UTCPSocket sock(host, port);
while( !cancel )
try
{
if( sock.poll(PassiveTimer::millisecToPoco(500), Poco::Net::Socket::SELECT_READ) )
UTCPSocket sock(host, port);
while( !cancel )
{
if( sock.poll(PassiveTimer::millisecToPoco(500), Poco::Net::Socket::SELECT_READ) )
{
}
}
return true;
}
catch(...){}
return true;
return false;
}
// --------------------------------------------------------
// вспомогательный класс для гарантированного завершения потока..
......
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