Commit 326960c7 authored by Pavel Vainerman's avatar Pavel Vainerman Committed by Pavel Vainerman

[modbus master][reload config]: reload by HTTP API

parent cc15c224
......@@ -1200,16 +1200,18 @@ int apiRequest( const string& args, UInterface& ui, const string& query )
}
string q = query;
if( q.rfind("/api/", 0) != 0 )
{
q = "/api/" + uniset::UHttp::UHTTP_API_VERSION;
if( query[0] != '/' )
q += "/";
q += query;
}
for( auto && it : sl )
for( auto&& it : sl )
{
if( it.node == DefaultObjectId )
it.node = conf->getLocalNode();
......
......@@ -1147,6 +1147,11 @@ namespace uniset
// -----------------------------------------------------------------------------
std::string MBConfig::RTUDevice::getShortInfo() const
{
size_t regs = 0;
for( const auto& p : pollmap )
regs += p.second->size();
ostringstream s;
s << "mbaddr=" << ModbusRTU::addr2str(mbaddr) << ":"
......@@ -1157,8 +1162,31 @@ namespace uniset
<< " timeout=" << resp_Delay.getOffDelay()
<< " type=" << dtype
<< " ask_every_reg=" << ask_every_reg
<< " regs=" << regs
<< ")" << endl;
return s.str();
}
// -----------------------------------------------------------------------------
std::string MBConfig::getShortInfo() const
{
ostringstream s;
s << " recv_timeout=" << recv_timeout
<< " default_timeout=" << default_timeout
<< " aftersend_pause=" << aftersend_pause
<< " polltime=" << polltime
<< " sleepPause_msec=" << sleepPause_msec
<< " maxQueryCount=" << maxQueryCount
<< " noQueryOptimization=" << noQueryOptimization
<< " prefix=" << prefix
<< " prop_prefix=" << prop_prefix
<< " s_field=" << s_field
<< " s_fvalue=" << s_fvalue
<< " defaultMBtype=" << defaultMBtype
<< " defaultMBaddr=" << defaultMBaddr
<< " mbregFromID=" << mbregFromID
<< " defaultMBinitOK=" << defaultMBinitOK;
return s.str();
}
// -----------------------------------------------------------------------------
} // end of namespace uniset
......@@ -268,6 +268,7 @@ namespace uniset
std::shared_ptr<SMInterface> shm;
void cloneParams( const std::shared_ptr<MBConfig>& conf );
std::string getShortInfo() const;
protected:
......
......@@ -100,10 +100,7 @@ namespace uniset
ptStatistic.setTiming(stat_time * 1000);
mbconf->recv_timeout = conf->getArgPInt("--" + prefix + "-recv-timeout", it.getProp("recv_timeout"), 500);
// vmonit(recv_timeout);
mbconf->default_timeout = conf->getArgPInt("--" + prefix + "-timeout", it.getProp("timeout"), 5000);
// vmonit(default_timeout);
int tout = conf->getArgPInt("--" + prefix + "-reopen-timeout", it.getProp("reopen_timeout"), mbconf->default_timeout * 2);
ptReopen.setTiming(tout);
......@@ -112,18 +109,11 @@ namespace uniset
ptInitChannel.setTiming(reinit_tout);
mbconf->aftersend_pause = conf->getArgPInt("--" + prefix + "-aftersend-pause", it.getProp("aftersend_pause"), 0);
// vmonit(aftersend_pause);
mbconf->noQueryOptimization = conf->getArgInt("--" + prefix + "-no-query-optimization", it.getProp("no_query_optimization"));
// vmonit(noQueryOptimization);
mbconf->mbregFromID = conf->getArgInt("--" + prefix + "-reg-from-id", it.getProp("reg_from_id"));
mbinfo << myname << "(init): mbregFromID=" << mbconf->mbregFromID << endl;
// vmonit(mbregFromID);
mbconf->polltime = conf->getArgPInt("--" + prefix + "-polltime", it.getProp("polltime"), 100);
// vmonit(polltime);
initPause = conf->getArgPInt("--" + prefix + "-initPause", it.getProp("initPause"), 3000);
mbconf->sleepPause_msec = conf->getArgPInt("--" + prefix + "-sleepPause-msec", it.getProp("sleepPause"), 10);
......@@ -2539,6 +2529,7 @@ namespace uniset
inf << "activated: " << activated << endl;
inf << "LogServer: " << logserv_host << ":" << logserv_port << endl;
inf << "Parameters: reopenTimeout=" << ptReopen.getInterval()
<< mbconf->getShortInfo()
<< endl;
if( stat_time > 0 )
......@@ -2553,4 +2544,58 @@ namespace uniset
return i._retn();
}
// ----------------------------------------------------------------------------
#ifndef DISABLE_REST_API
Poco::JSON::Object::Ptr MBExchange::httpHelp( const Poco::URI::QueryParameters& p )
{
uniset::json::help::object myhelp(myname, UniSetObject::httpHelp(p));
{
// 'reconfigure'
uniset::json::help::item cmd("reconfigure", "reload config from file");
cmd.param("confile", "Absolute path for config file. Optional parameter.");
myhelp.add(cmd);
}
return myhelp;
}
// ----------------------------------------------------------------------------
Poco::JSON::Object::Ptr MBExchange::httpRequest( const string& req, const Poco::URI::QueryParameters& p )
{
mbinfo << myname << "(httpRequest): " << req << endl;
if( req == "reconfigure" )
{
std::string confile = uniset_conf()->getConfFileName();
if( p.size() > 0 && p[0].first == "confile" )
{
confile = p[0].second;
if( !uniset::file_exist(confile) )
{
ostringstream err;
err << myname << "(reconfigure): Not found config file '" << confile << "'";
throw uniset::SystemError(err.str());
}
}
if( reconfigure(confile) )
{
Poco::JSON::Object::Ptr json = new Poco::JSON::Object();
json->set("result", "OK");
json->set("config", confile);
return json;
}
ostringstream err;
err << myname << "(reconfigure): Failed reconfigure from '" << confile << "'";
throw uniset::SystemError(err.str());
}
return UniSetObject::httpRequest(req, p);
}
// ----------------------------------------------------------------------------
#endif
} // end of namespace uniset
......@@ -96,7 +96,11 @@ namespace uniset
virtual bool activateObject() override;
virtual void initIterators();
virtual void initValues();
#ifndef DISABLE_REST_API
// http API
virtual Poco::JSON::Object::Ptr httpHelp( const Poco::URI::QueryParameters& p ) override;
virtual Poco::JSON::Object::Ptr httpRequest( const std::string& req, const Poco::URI::QueryParameters& p ) override;
#endif
void firstInitRegisters();
bool preInitRead( MBConfig::InitList::iterator& p );
bool initSMValue( ModbusRTU::ModbusData* data, int count, MBConfig::RSProperty* p );
......@@ -125,7 +129,6 @@ namespace uniset
void setProcActive( bool st );
bool waitSMReady();
// void readConfiguration();
bool readItem( const std::shared_ptr<UniXML>& xml, UniXML::iterator& it, xmlNode* sec );
bool initItem( UniXML::iterator& it );
void initOffsetList();
......@@ -167,21 +170,6 @@ namespace uniset
PassiveTimer ptInitChannel; /*!< задержка не инициализацию связи */
// т.к. пороговые датчики не связаны напрямую с обменом, создаём для них отдельный список
// и отдельно его проверяем потом
typedef std::list<IOBase> ThresholdList;
ThresholdList thrlist;
std::string defaultMBtype;
std::string defaultMBaddr;
bool defaultMBinitOK = { false }; // флаг определяющий нужно ли ждать "первого обмена" или при запуске сохранять в SM значение default.
std::shared_ptr<LogAgregator> loga;
std::shared_ptr<DebugStream> mblog;
std::shared_ptr<LogServer> logserv;
std::string logserv_host = {""};
int logserv_port = {0};
const std::shared_ptr<SharedMemory> ic;
std::shared_ptr<LogAgregator> loga;
std::shared_ptr<DebugStream> mblog;
std::shared_ptr<LogServer> logserv;
......
......@@ -35,6 +35,8 @@ namespace uniset
- \ref sec_MBTCP_Conf
- \ref sec_MBTCP_ConfList
- \ref sec_MBTCP_ExchangeMode
- \ref sec_MBTCP_ReloadConfig
- \ref sec_MBTCP_REST_API
\section sec_MBTCP_Comm Общее описание ModbusTCP master
Класс реализует процесс обмена (опрос/запись) с RTU-устройствами,
......@@ -207,6 +209,34 @@ namespace uniset
Если указан и параметр \a safemodeSensor=".." и \a safemodeResetIfNotRespond="1", то будет использован
режим \b safeExternalControl (как более приоритетный).
\section sec_MBTCP_ReloadConfig Переконфигурирование "на ходу"
В процессе реализована возможность перечитать конфигурацию "на ходу". Для этого достаточно процессу
послать команду SystemMessage::Reconfigure или воспользоваться HTTP API, где можно указать файл из
которого произвести загрузку (см. \ref sec_MBTCP_REST_API).
При этом процесс приостанавливает обмен и перезагружает конфигурационный файл с которым был запущен.
Переконфигурировать можно регистры, список устройств, адреса и любые свойства. В том числе возможно
добавление новых регистров в список или уменьшение списка существующих.
\warning Если во время загрузки новой конфигурации будет найдена какая-то ошибка, то конфигурация не будет применена.
Ошибку можно будет увидеть в логах процесса.
\warning Важно понимать, что это перезагрузка только настроек касающихся ModbusMaster, поэтому список датчиков
и другие базовые настройки должны совпадать с исходным файлом. Т.е. возможно только переопределение параметров
касающихся обмена, а не всего конфига в целом.
\section sec_MBTCP_REST_API ModbusMaster HTTP API
\code
/help - Получение списка доступных команд
/ - получение стандартной информации
/reconfigure?confile=/path/to/confile - Перезагрузить конфигурацию
confile - абсолютный путь до файла с конфигурацией. Не обязательный параметр.
\endcode
\warning Важно иметь ввиду, что если указывается confile, то он должен совпадать с базовым configure.xml
в идентификаторах датчиков. И как минимум должен содержать соответствующую секцию настроек для процесса
и те датчики, которые участвуют в обмене. Т.к. реальный config (глобальный) не подменяется, из указанного
файла только загружаются необходимые для инициализации обмена параметры.
*/
// -----------------------------------------------------------------------------
/*!
......
......@@ -567,6 +567,7 @@ void MBTCPMultiMaster::check_thread()
void MBTCPMultiMaster::initIterators()
{
MBExchange::initIterators();
for( auto&& it : mblist )
shm->initIterator(it->respond_it);
}
......@@ -625,6 +626,7 @@ void MBTCPMultiMaster::initCheckConnectionParameters()
for( const auto& d : mbconf->devices )
{
checkAddr = d.second->mbaddr;
if( d.second->pollmap.empty() )
continue;
......
......@@ -16,7 +16,7 @@
--mbtcp-persistent-connection 1 \
--ulog-add-levels system \
--mbtcp-run-logserver \
--mbtcp-log-add-levels any \
--mbtcp-log-add-levels info,warn,crit \
$*
#--mbtcp-log-add-levels level4,level3 \
......
......@@ -788,6 +788,48 @@ TEST_CASE("MBTCPMaster: reload config", "[modbus][reload][mbmaster][mbtcpmaster]
REQUIRE( ui->getValue(1080) == 160 );
}
// -----------------------------------------------------------------------------
TEST_CASE("MBTCPMaster: reload config (HTTP API)", "[modbus][reload-api][mbmaster][mbtcpmaster]")
{
InitTest();
// default reconfigure
std::string request = "/api/v01/reconfigure";
uniset::SimpleInfo_var ret = mbm->apiRequest(request.c_str());
ostringstream sinfo;
sinfo << ret->info;
std::string info = sinfo.str();
REQUIRE( ret->id == mbm->getId() );
REQUIRE_FALSE( info.empty() );
REQUIRE( info.find("OK") != std::string::npos );
// reconfigure from other file
request = "/api/v01/reconfigure?confile=" + confile2;
ret = mbm->apiRequest(request.c_str());
sinfo.str("");
sinfo << ret->info;
info = sinfo.str();
REQUIRE( ret->id == mbm->getId() );
REQUIRE_FALSE( info.empty() );
REQUIRE( info.find("OK") != std::string::npos );
REQUIRE( info.find(confile2) != std::string::npos );
// reconfigure FAIL
request = "/api/v01/reconfigure?confile=BADFILE";
ret = mbm->apiRequest(request.c_str());
sinfo.str("");
sinfo << ret->info;
info = sinfo.str();
REQUIRE( ret->id == mbm->getId() );
REQUIRE_FALSE( info.empty() );
REQUIRE( info.find("OK") == std::string::npos );
}
// -----------------------------------------------------------------------------
#if 0
// -----------------------------------------------------------------------------
......
......@@ -707,38 +707,44 @@ Poco::JSON::Object::Ptr UObject_SK::request_conf_set( const std::string& req, co
Poco::JSON::Object::Ptr jret = new Poco::JSON::Object();
Poco::JSON::Array::Ptr jupdated = uniset::json::make_child_array(jret, "updated");
for( const auto& p: params )
for( const auto& p : params )
{
if( p.first == "sleep_msec" )
{
int val = uni_atoi(p.second);
if( val > 0 )
{
sleep_msec = uni_atoi(p.second);
jupdated->add(p.first);
}
continue;
}
if( p.first == "resetMsgTime" )
{
int val = uni_atoi(p.second);
if( val > 0 )
{
resetMsgTime = uni_atoi(p.second);
jupdated->add(p.first);
}
continue;
}
if( p.first == "forceOut" )
{
int val = uni_atoi(p.second);
if( val > 0 )
{
forceOut = uni_atoi(p.second);
jupdated->add(p.first);
}
continue;
}
......
......@@ -988,6 +988,7 @@ namespace uniset
return getInfo(request);
#else
SimpleInfo* ret = new SimpleInfo();
ret->id = getId();
ostringstream err;
try
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment