Commit 3615f9fd authored by Pavel Vainerman's avatar Pavel Vainerman

(MQTTPublisher): реализовал возможность преобразовывать value

в text публикуемых событий
parent 93ae5782
...@@ -274,7 +274,13 @@ ...@@ -274,7 +274,13 @@
<item id="109" iotype="DI" name="MM1_Not_Respond_S" priority="Medium" textname="multimaster test sensor"/> <item id="109" iotype="DI" name="MM1_Not_Respond_S" priority="Medium" textname="multimaster test sensor"/>
<item id="110" iotype="DI" name="MM2_Not_Respond_S" priority="Medium" textname="multimaster test sensor"/> <item id="110" iotype="DI" name="MM2_Not_Respond_S" priority="Medium" textname="multimaster test sensor"/>
<item id="111" iotype="AI" name="MQTT_AI_AS" textname="MQTT test AI" mqtt="1"/> <item id="111" iotype="AI" name="MQTT_AI_AS" textname="MQTT test AI" mqtt="1">
<mqtt>
<msg value="1" text="Hello %v"/>
<range min="2" max="5" text="Range Hello %v %r (min=%rmin max=%rmax)"/>
<range min="2" max="3" text="Range Hello %t = %v"/>
</mqtt>
</item>
<item id="112" iotype="DI" name="MQTT_DI_S" textname="MQTT test DI" mqtt="1"/> <item id="112" iotype="DI" name="MQTT_DI_S" textname="MQTT test DI" mqtt="1"/>
</sensors> </sensors>
......
...@@ -75,6 +75,16 @@ MQTTPublisher::MQTTPublisher(UniSetTypes::ObjectId objId, xmlNode* cnode, UniSet ...@@ -75,6 +75,16 @@ MQTTPublisher::MQTTPublisher(UniSetTypes::ObjectId objId, xmlNode* cnode, UniSet
if( smTestID == DefaultObjectId ) if( smTestID == DefaultObjectId )
smTestID = sid; smTestID = sid;
UniXML::iterator i(sit);
if( !i.goChildren() )
continue;
if( !i.find("mqtt") )
continue;
MQTTTextInfo mi(topicsensors, sit, i);
textpublist.emplace(sid,std::move(mi) );
} }
if( publist.empty() ) if( publist.empty() )
...@@ -132,6 +142,36 @@ void MQTTPublisher::sysCommand(const SystemMessage* sm) ...@@ -132,6 +142,36 @@ void MQTTPublisher::sysCommand(const SystemMessage* sm)
} }
} }
//-------------------------------------------------------------------------------- //--------------------------------------------------------------------------------
string MQTTPublisher::replace( const std::string& text, MQTTPublisher::MQTTTextInfo* ti, MQTTPublisher::RangeInfo* ri, long value )
{
std::string txt(text);
ostringstream v;
v << value;
ostringstream id;
id << ti->sid;
ostringstream r;
r << "[" << ri->rmin << ":" << ri->rmax << "]";
ostringstream smin;
smin << ri->rmin;
ostringstream smax;
smax << ri->rmax;
txt = replace_all(txt,"%v",v.str());
txt = replace_all(txt,"%n",ti->xmlnode.getProp("name"));
txt = replace_all(txt,"%t",ti->xmlnode.getProp("textname"));
txt = replace_all(txt,"%i",id.str());
txt = replace_all(txt,"%rmin",smin.str());
txt = replace_all(txt,"%rmax",smax.str());
txt = replace_all(txt,"%r",r.str());
return std::move(txt);
}
//--------------------------------------------------------------------------------
void MQTTPublisher::help_print( int argc, const char* const* argv ) void MQTTPublisher::help_print( int argc, const char* const* argv )
{ {
cout << " Default prefix='mqtt'" << endl; cout << " Default prefix='mqtt'" << endl;
...@@ -243,23 +283,100 @@ void MQTTPublisher::askSensors( UniversalIO::UIOCommand cmd ) ...@@ -243,23 +283,100 @@ void MQTTPublisher::askSensors( UniversalIO::UIOCommand cmd )
void MQTTPublisher::sensorInfo( const UniSetTypes::SensorMessage* sm ) void MQTTPublisher::sensorInfo( const UniSetTypes::SensorMessage* sm )
{ {
auto i = publist.find(sm->id); auto i = publist.find(sm->id);
if( i != publist.end() )
{
ostringstream m;
m << sm->value;
if( i == publist.end() ) string tmsg(m.str());
return;
ostringstream m; //subscribe(NULL, i.second.pubname.c_str());
m << sm->value; myinfo << "(sensorInfo): publish: topic='" << i->second.pubname << "' msg='" << tmsg.c_str() << "'" << endl;
string tmsg(m.str()); int ret = publish(NULL, i->second.pubname.c_str(), tmsg.size(), tmsg.c_str(), 1, false);
//subscribe(NULL, i.second.pubname.c_str()); if( ret != MOSQ_ERR_SUCCESS )
myinfo << "(sensorInfo): publish: topic='" << i->second.pubname << "' msg='" << tmsg.c_str() << "'" << endl; {
mycrit << myname << "(sensorInfo): PUBLISH FAILED: err(" << ret << "): " << mosqpp::strerror(ret) << endl;
}
}
int ret = publish(NULL, i->second.pubname.c_str(), tmsg.size(), tmsg.c_str(), 1, false); auto t = textpublist.find(sm->id);
if( t != textpublist.end() )
{
auto rlist = t->second.rlist;
for( auto&& r: rlist )
{
if( r.check(sm->value) )
{
string tmsg = replace(r.text,&(t->second),&r,sm->value);
//subscribe(NULL, i.second.pubname.c_str());
myinfo << "(sensorInfo): publish: topic='" << t->second.pubname << "' msg='" << tmsg << "'" << endl;
int ret = publish(NULL, t->second.pubname.c_str(), tmsg.size(), tmsg.c_str(), 1, false);
if( ret != MOSQ_ERR_SUCCESS ) if( ret != MOSQ_ERR_SUCCESS )
{
mycrit << myname << "(sensorInfo): PUBLISH FAILED: err(" << ret << "): " << mosqpp::strerror(ret) << endl;
}
}
}
}
}
// -----------------------------------------------------------------------------
MQTTPublisher::MQTTTextInfo::MQTTTextInfo( const string& rootsec, UniXML::iterator s, UniXML::iterator i ):
xmlnode(s)
{
auto conf = uniset_conf();
sid = conf->getSensorID(s.getProp("name"));
string sname(s.getProp("name"));
if( sid == DefaultObjectId )
{ {
mycrit << myname << "(sensorInfo): PUBLISH FAILED: err(" << ret << "): " << mosqpp::strerror(ret) << endl; ostringstream err;
err << "(MQTTTextInfo): Unknown ID for " << sname;
throw SystemError(err.str());
} }
std::string subtopic(i.getProp("subtopic"));
if( !subtopic.empty() )
pubname = rootsec + "/" + subtopic;
else
pubname = rootsec + "/" + sname + "/textevent";
if( !i.goChildren() )
{
ostringstream err;
err << "(MQTTTextInfo): INIT FAIL! empty list <mqtt> for " << sname;
throw SystemError(err.str());
}
for( ; i.getCurrent(); i++ )
{
long min = 0;
long max = 0;
if( i.getName() == "range" )
{
min = i.getIntProp("min");
max = i.getIntProp("max");
if( min > max )
std::swap(min,max);
}
else
{
min = max = i.getIntProp("value");
}
RangeInfo r(min,max,i.getProp("text"));
rlist.push_back( std::move(r) );
}
}
// -----------------------------------------------------------------------------
bool MQTTPublisher::RangeInfo::check( long val ) const
{
return ( val >= rmin && val <= rmax );
} }
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
- \ref sec_MQTT_Comm - \ref sec_MQTT_Comm
- \ref sec_MQTT_Conf - \ref sec_MQTT_Conf
- \ref sec_MQTT_Text
\section sec_MQTT_Comm Общее описание MQTTPublisher \section sec_MQTT_Comm Общее описание MQTTPublisher
...@@ -52,6 +53,36 @@ ...@@ -52,6 +53,36 @@
Для запуска издателя, неоходимо наличие в configure.xml секции: <ObjectName name="ObjectName" ...параметры">. Для запуска издателя, неоходимо наличие в configure.xml секции: <ObjectName name="ObjectName" ...параметры">.
\todo Доделать контрольный таймер (контроль наличия соединения с сервером) \todo Доделать контрольный таймер (контроль наличия соединения с сервером)
\section sec_MQTT_Text Генерирование текстовых сообщений
В данном классе реализована возможность сопоставлять значения датчиков текстовым сообщениям, посылаемым на сервер.
Для этого необходимо в настроечной секции для датчика создать секцию <mqtt>
\code
<item id="10" name="MySensor1" .....>
<mqtt subtopic="myevent">
<msg value="12" text="My text for value %v"/>
<msg value="13" text="My text for value %v"/>
<msg value="14" text="My text for value %v"/>
<range min="10" max="20" text="My text for value %r. Value = %v"
<mqtt>
</item>
\endcode
- \b range - задаёт диапазон включающий [min,max]
При этом в тексте можно применять следующие "подстановки":
- \b %v - текущее значение
- \b %n - name
- \b %t - textname
- \b %i - ID
- \b %r - заданный диапазон (range). Заменяется на "[min:max]". Действует только для диапазонов.
- \b %rmin - минимальное значение диапазона (range min). Действует только для диапазонов.
- \b %rmax - максимальное значение диапазона (range max). Действует только для диапазонов.
\note Если заданные "одиночные" значения совпадают с диапазоном, то будет сгенерировано несколько сообщений.
Поле \b subtopic - задаёт подраздел в корневом топике (см. topicsensors). Т.е. полный топик для публикации текстовых сообщений
будет иметь вид ROOTPROJECT/topicsensors/sensorname/textevent или если задано поле \subtopic то
события будут опубликованы в ROOTPROJECT/topicsensors/subtopic
*/ */
class MQTTPublisher: class MQTTPublisher:
protected mosqpp::mosquittopp, protected mosqpp::mosquittopp,
...@@ -79,7 +110,6 @@ class MQTTPublisher: ...@@ -79,7 +110,6 @@ class MQTTPublisher:
return mylog; return mylog;
} }
virtual void on_connect(int rc) override; virtual void on_connect(int rc) override;
virtual void on_message(const struct mosquitto_message* message) override; virtual void on_message(const struct mosquitto_message* message) override;
virtual void on_subscribe(int mid, int qos_count, const int* granted_qos) override; virtual void on_subscribe(int mid, int qos_count, const int* granted_qos) override;
...@@ -106,7 +136,34 @@ class MQTTPublisher: ...@@ -106,7 +136,34 @@ class MQTTPublisher:
typedef std::unordered_map<UniSetTypes::ObjectId, MQTTInfo> MQTTMap; typedef std::unordered_map<UniSetTypes::ObjectId, MQTTInfo> MQTTMap;
struct RangeInfo
{
RangeInfo( long min, long max, const std::string& t ): rmin(min), rmax(max), text(t){}
long rmin;
long rmax;
std::string text;
bool check( long val ) const;
};
struct MQTTTextInfo
{
UniSetTypes::ObjectId sid;
std::string pubname;
UniXML::iterator xmlnode;
MQTTTextInfo( const std::string& rootsec, UniXML::iterator s, UniXML::iterator i );
// одиночные сообщения просто имитируются min=max=val
std::list<RangeInfo> rlist; // список сообщений..
};
typedef std::unordered_map<UniSetTypes::ObjectId, MQTTTextInfo> MQTTTextMap;
std::string replace( const std::string& text, MQTTTextInfo* ti, RangeInfo* r, long value );
MQTTMap publist; MQTTMap publist;
MQTTTextMap textpublist;
private: private:
......
...@@ -201,6 +201,12 @@ namespace UniSetTypes ...@@ -201,6 +201,12 @@ namespace UniSetTypes
/*! проверка является текст в строке - числом..*/ /*! проверка является текст в строке - числом..*/
bool is_digit( const std::string& s ); bool is_digit( const std::string& s );
/*! замена всех вхождений подстроки
* \param src - исходная строка
* \param from - подстрока которая ищется (для замены)
* \param to - строка на которую будет сделана замена
*/
std::string replace_all( const std::string& src, const std::string& from, const std::string& to );
// --------------------------------------------------------------- // ---------------------------------------------------------------
// Работа с командной строкой // Работа с командной строкой
......
...@@ -572,3 +572,21 @@ std::ostream& UniSetTypes::operator<<( std::ostream& os, const IONotifyControlle ...@@ -572,3 +572,21 @@ std::ostream& UniSetTypes::operator<<( std::ostream& os, const IONotifyControlle
return os << "Unknown"; return os << "Unknown";
} }
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
std::string UniSetTypes::replace_all( const std::string& src, const std::string& from, const std::string& to )
{
string res(src);
if( from.empty() )
return std::move(res);
size_t pos = res.find(from,pos);
while( pos != std::string::npos )
{
res.replace(pos, from.length(), to);
pos += to.length();
pos = res.find(from,pos);
}
return std::move(res);
}
// -------------------------------------------------------------------------
...@@ -156,3 +156,21 @@ TEST_CASE("UniSetTypes: getObjectsList", "[utypes][getolist]" ) ...@@ -156,3 +156,21 @@ TEST_CASE("UniSetTypes: getObjectsList", "[utypes][getolist]" )
REQUIRE( v[4].node == 1001 ); REQUIRE( v[4].node == 1001 );
} }
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
TEST_CASE("UniSetTypes: replace_all", "[utypes][replace_all]" )
{
const std::string str1("Text %p test text %p");
std::string res = UniSetTypes::replace_all(str1,"%p","my");
REQUIRE( res == "Text my test text my" );
const std::string str2("Text %rlong test text %rlong");
res = UniSetTypes::replace_all(str2,"%rlong","2");
REQUIRE( res == "Text 2 test text 2" );
res = UniSetTypes::replace_all(str2,"","my");
REQUIRE( res == str2 );
res = UniSetTypes::replace_all(str2,"not found","my");
REQUIRE( res == str2 );
}
// -----------------------------------------------------------------------------
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