2018-04-01 06:53:37 +00:00
/*
2021-02-20 10:08:23 +00:00
Copyright ( C ) 2017 - 2021 Fredrik Ö hrstr ö m
2018-04-01 06:53:37 +00:00
This program is free software : you can redistribute it and / or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation , either version 3 of the License , or
( at your option ) any later version .
This program is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
GNU General Public License for more details .
You should have received a copy of the GNU General Public License
along with this program . If not , see < http : //www.gnu.org/licenses/>.
*/
2017-08-09 10:00:11 +00:00
2021-02-20 10:08:23 +00:00
# include "config.h"
2017-08-09 10:00:11 +00:00
# include "meters.h"
2021-03-07 19:10:44 +00:00
# include "meter_detection.h"
2018-03-05 10:29:25 +00:00
# include "meters_common_implementation.h"
2019-05-04 11:07:37 +00:00
# include "units.h"
2019-05-04 15:38:10 +00:00
# include "wmbus.h"
# include "wmbus_utils.h"
2017-08-09 10:00:11 +00:00
2019-05-04 14:37:35 +00:00
# include <algorithm>
2018-03-05 10:29:25 +00:00
# include <memory.h>
2021-01-30 16:58:00 +00:00
# include <numeric>
2020-04-21 16:07:46 +00:00
# include <time.h>
2020-09-24 19:23:06 +00:00
# include <cmath>
2017-08-09 10:00:11 +00:00
2020-09-07 08:36:39 +00:00
struct MeterManagerImplementation : public virtual MeterManager
{
2021-02-20 21:21:01 +00:00
private :
bool is_daemon_ { } ;
vector < MeterInfo > meter_templates_ ;
vector < shared_ptr < Meter > > meters_ ;
function < void ( AboutTelegram & , vector < uchar > ) > on_telegram_ ;
function < void ( Telegram * t , Meter * ) > on_meter_updated_ ;
public :
void addMeterTemplate ( MeterInfo & mi )
{
meter_templates_ . push_back ( mi ) ;
}
2020-09-21 19:55:21 +00:00
void addMeter ( shared_ptr < Meter > meter )
2020-09-07 08:36:39 +00:00
{
2020-09-21 19:55:21 +00:00
meters_ . push_back ( meter ) ;
2021-02-20 21:21:01 +00:00
meter - > setIndex ( meters_ . size ( ) ) ;
2021-03-08 16:14:03 +00:00
meter - > onUpdate ( on_meter_updated_ ) ;
2020-09-07 08:36:39 +00:00
}
2020-09-08 20:11:32 +00:00
Meter * lastAddedMeter ( )
{
return meters_ . back ( ) . get ( ) ;
}
2020-09-07 08:36:39 +00:00
void removeAllMeters ( )
{
meters_ . clear ( ) ;
}
void forEachMeter ( std : : function < void ( Meter * ) > cb )
{
for ( auto & meter : meters_ )
{
cb ( meter . get ( ) ) ;
}
}
bool hasAllMetersReceivedATelegram ( )
{
for ( auto & meter : meters_ )
{
if ( meter - > numUpdates ( ) = = 0 ) return false ;
}
return true ;
}
2020-09-08 12:55:01 +00:00
bool hasMeters ( )
{
2021-02-20 21:21:01 +00:00
return meters_ . size ( ) ! = 0 | | meter_templates_ . size ( ) ! = 0 ;
2020-09-08 12:55:01 +00:00
}
2021-03-07 19:10:44 +00:00
void warnForUnknownDriver ( string name , Telegram * t )
{
int mfct = t - > dll_mfct ;
int media = t - > dll_type ;
int version = t - > dll_version ;
uchar * id_b = t - > dll_id_b ;
if ( t - > tpl_id_found )
{
2021-03-07 19:40:41 +00:00
mfct = t - > tpl_mfct ;
media = t - > tpl_type ;
version = t - > tpl_version ;
2021-03-07 19:10:44 +00:00
id_b = t - > tpl_id_b ;
}
warning ( " (meter) %s: meter detection could not find driver for "
" id: %02x%02x%02x%02x mfct: (%s) %s (0x%02x) type: %s (0x%02x) ver: 0x%02x \n " ,
name . c_str ( ) ,
id_b [ 3 ] , id_b [ 2 ] , id_b [ 1 ] , id_b [ 0 ] ,
manufacturerFlag ( mfct ) . c_str ( ) ,
manufacturer ( mfct ) . c_str ( ) ,
mfct ,
mediaType ( media , mfct ) . c_str ( ) , media ,
version ) ;
warning ( " (meter) please consider opening an issue at https://github.com/weetmuts/wmbusmeters/ \n " ) ;
warning ( " (meter) to add support for this unknown mfct,media,version combination \n " ) ;
}
2021-02-20 21:21:01 +00:00
bool handleTelegram ( AboutTelegram & about , vector < uchar > input_frame , bool simulated )
2020-09-07 08:36:39 +00:00
{
2020-09-08 12:55:01 +00:00
if ( ! hasMeters ( ) )
{
if ( on_telegram_ )
{
2021-02-20 21:21:01 +00:00
on_telegram_ ( about , input_frame ) ;
2020-09-08 12:55:01 +00:00
}
return true ;
}
2020-09-07 08:36:39 +00:00
bool handled = false ;
2021-02-20 21:21:01 +00:00
bool exact_id_match = false ;
2020-09-07 08:36:39 +00:00
2021-01-30 16:58:00 +00:00
string ids ;
2020-09-07 08:36:39 +00:00
for ( auto & m : meters_ )
{
2021-02-20 21:21:01 +00:00
bool h = m - > handleTelegram ( about , input_frame , simulated , & ids , & exact_id_match ) ;
2020-09-07 08:36:39 +00:00
if ( h ) handled = true ;
}
2021-02-20 21:21:01 +00:00
// If not properly handled, and there was no exact id match.
// then lets check if there is a template that can create a meter for it.
if ( ! handled & & ! exact_id_match )
{
debug ( " (meter) no meter handled %s checking %d templates. \n " , ids . c_str ( ) , meter_templates_ . size ( ) ) ;
// Not handled, maybe we have a template to create a new meter instance for this telegram?
Telegram t ;
t . about = about ;
bool ok = t . parseHeader ( input_frame ) ;
if ( simulated ) t . markAsSimulated ( ) ;
if ( ok )
{
ids = t . idsc ;
for ( auto & mi : meter_templates_ )
{
if ( MeterCommonImplementation : : isTelegramForMeter ( & t , NULL , & mi ) )
{
// We found a match, make a copy of the meter info.
MeterInfo tmp = mi ;
// Overwrite the wildcard pattern with the highest level id.
// The last id in the t.ids is the highest level id.
// For example: a telegram can have dll_id,tpl_id
// This will pick the tpl_id.
// Or a telegram can have a single dll_id,
// then the dll_id will be picked.
vector < string > tmp_ids ;
tmp_ids . push_back ( t . ids . back ( ) ) ;
tmp . ids = tmp_ids ;
tmp . idsc = t . ids . back ( ) ;
2021-03-07 18:52:21 +00:00
2021-03-08 07:40:48 +00:00
if ( tmp . driver = = MeterDriver : : AUTO )
2021-03-07 18:52:21 +00:00
{
// Look up the proper meter driver!
2021-03-08 07:40:48 +00:00
tmp . driver = pickMeterDriver ( & t ) ;
if ( tmp . driver = = MeterDriver : : UNKNOWN )
2021-03-07 19:10:44 +00:00
{
warnForUnknownDriver ( mi . name , & t ) ;
}
2021-03-07 18:52:21 +00:00
}
2021-02-20 21:21:01 +00:00
// Now build a meter object with for this exact id.
auto meter = createMeter ( & tmp ) ;
addMeter ( meter ) ;
string idsc = toIdsCommaSeparated ( t . ids ) ;
verbose ( " (meter) used meter template %s %s %s to match %s \n " ,
mi . name . c_str ( ) ,
mi . idsc . c_str ( ) ,
2021-03-13 17:35:47 +00:00
toString ( mi . driver ) . c_str ( ) ,
2021-02-20 21:21:01 +00:00
idsc . c_str ( ) ) ;
if ( is_daemon_ )
{
notice ( " (wmbusmeters) started meter %d (%s %s %s) \n " ,
meter - > index ( ) ,
mi . name . c_str ( ) ,
tmp . idsc . c_str ( ) ,
2021-03-13 17:35:47 +00:00
toString ( mi . driver ) . c_str ( ) ) ;
2021-02-20 21:21:01 +00:00
}
else
{
verbose ( " (meter) started meter %d (%s %s %s) \n " ,
meter - > index ( ) ,
mi . name . c_str ( ) ,
tmp . idsc . c_str ( ) ,
2021-03-13 17:35:47 +00:00
toString ( mi . driver ) . c_str ( ) ) ;
2021-02-20 21:21:01 +00:00
}
bool match = false ;
bool h = meter - > handleTelegram ( about , input_frame , simulated , & ids , & match ) ;
if ( ! match )
{
// Oups, we added a new meter object tailored for this telegram
// but it still did not match! This is probably an error in wmbusmeters!
warning ( " (meter) newly created meter (%s %s %s) did not match telegram! " ,
" Please open an issue at https://github.com/weetmuts/wmbusmeters/ \n " ,
2021-03-13 17:35:47 +00:00
meter - > name ( ) . c_str ( ) , meter - > idsc ( ) . c_str ( ) , toString ( meter - > driver ( ) ) . c_str ( ) ) ;
2021-02-20 21:21:01 +00:00
}
else if ( ! h )
{
// Oups, we added a new meter object tailored for this telegram
// but it still did not handle it! This can happen if the wrong
// decryption key was used.
warning ( " (meter) newly created meter (%s %s %s) did not handle telegram! \n " ,
2021-03-13 17:35:47 +00:00
meter - > name ( ) . c_str ( ) , meter - > idsc ( ) . c_str ( ) , toString ( meter - > driver ( ) ) . c_str ( ) ) ;
2021-02-20 21:21:01 +00:00
}
else
{
handled = true ;
}
}
}
}
}
2020-09-07 08:36:39 +00:00
if ( isVerboseEnabled ( ) & & ! handled )
{
2021-01-30 16:58:00 +00:00
verbose ( " (wmbus) telegram from %s ignored by all configured meters! \n " , ids . c_str ( ) ) ;
2020-09-07 08:36:39 +00:00
}
return handled ;
}
2020-09-08 12:55:01 +00:00
2020-10-14 18:59:14 +00:00
void onTelegram ( function < void ( AboutTelegram & about , vector < uchar > ) > cb )
2020-09-08 12:55:01 +00:00
{
on_telegram_ = cb ;
}
2021-03-08 16:14:03 +00:00
2021-02-20 21:21:01 +00:00
void whenMeterUpdated ( std : : function < void ( Telegram * t , Meter * ) > cb )
{
on_meter_updated_ = cb ;
}
2020-09-07 08:36:39 +00:00
2021-03-14 08:41:35 +00:00
void pollMeters ( shared_ptr < BusManager > bus )
2021-03-08 16:14:03 +00:00
{
for ( auto & m : meters_ )
{
2021-03-14 08:41:35 +00:00
m - > poll ( bus ) ;
2021-03-08 16:14:03 +00:00
}
}
2021-02-20 21:21:01 +00:00
MeterManagerImplementation ( bool daemon ) : is_daemon_ ( daemon ) { }
~ MeterManagerImplementation ( ) { }
2020-09-07 08:36:39 +00:00
} ;
2021-02-20 21:21:01 +00:00
shared_ptr < MeterManager > createMeterManager ( bool daemon )
2020-09-07 08:36:39 +00:00
{
2021-02-20 21:21:01 +00:00
return shared_ptr < MeterManager > ( new MeterManagerImplementation ( daemon ) ) ;
2020-09-07 08:36:39 +00:00
}
2020-08-30 19:33:48 +00:00
MeterCommonImplementation : : MeterCommonImplementation ( MeterInfo & mi ,
2021-03-08 07:40:48 +00:00
MeterDriver driver ) :
2021-03-14 08:41:35 +00:00
driver_ ( driver ) , bus_ ( mi . bus ) , name_ ( mi . name )
2018-03-05 10:29:25 +00:00
{
2021-02-20 21:21:01 +00:00
ids_ = mi . ids ;
idsc_ = toIdsCommaSeparated ( ids_ ) ;
2020-02-06 12:14:46 +00:00
if ( mi . key . length ( ) > 0 )
{
2020-01-27 08:29:40 +00:00
hex2bin ( mi . key , & meter_keys_ . confidentiality_key ) ;
2018-03-05 10:29:25 +00:00
}
2019-05-21 12:19:54 +00:00
for ( auto s : mi . shells ) {
addShell ( s ) ;
}
2019-10-20 17:19:17 +00:00
for ( auto j : mi . jsons ) {
addJson ( j ) ;
}
2018-03-05 10:29:25 +00:00
}
2019-05-04 11:55:52 +00:00
void MeterCommonImplementation : : addConversions ( std : : vector < Unit > cs )
{
for ( Unit c : cs )
{
conversions_ . push_back ( c ) ;
}
}
2019-05-21 12:19:54 +00:00
void MeterCommonImplementation : : addShell ( string cmdline )
{
shell_cmdlines_ . push_back ( cmdline ) ;
}
2019-10-20 17:19:17 +00:00
void MeterCommonImplementation : : addJson ( string json )
{
jsons_ . push_back ( json ) ;
}
2019-05-21 12:19:54 +00:00
vector < string > & MeterCommonImplementation : : shellCmdlines ( )
{
return shell_cmdlines_ ;
}
2019-10-20 17:19:17 +00:00
vector < string > & MeterCommonImplementation : : additionalJsons ( )
{
return jsons_ ;
}
2021-03-08 07:40:48 +00:00
MeterDriver MeterCommonImplementation : : driver ( )
2018-03-05 10:29:25 +00:00
{
2021-03-08 07:40:48 +00:00
return driver_ ;
2018-03-05 10:29:25 +00:00
}
2019-06-06 15:28:20 +00:00
void MeterCommonImplementation : : addLinkMode ( LinkMode lm )
{
link_modes_ . addLinkMode ( lm ) ;
}
2019-05-04 11:55:52 +00:00
void MeterCommonImplementation : : addPrint ( string vname , Quantity vquantity ,
2019-05-04 17:56:17 +00:00
function < double ( Unit ) > getValueFunc , string help , bool field , bool json )
2019-05-04 11:55:52 +00:00
{
2020-05-09 21:43:30 +00:00
string default_unit = unitToStringLowerCase ( defaultUnitForQuantity ( vquantity ) ) ;
2020-09-08 20:11:32 +00:00
string field_name = vname + " _ " + default_unit ;
fields_ . push_back ( field_name ) ;
prints_ . push_back ( { vname , vquantity , defaultUnitForQuantity ( vquantity ) , getValueFunc , NULL , help , field , json , field_name } ) ;
2019-05-04 17:56:17 +00:00
}
2020-04-22 20:38:40 +00:00
void MeterCommonImplementation : : addPrint ( string vname , Quantity vquantity , Unit unit ,
function < double ( Unit ) > getValueFunc , string help , bool field , bool json )
{
2020-09-08 20:11:32 +00:00
string default_unit = unitToStringLowerCase ( defaultUnitForQuantity ( vquantity ) ) ;
string field_name = vname + " _ " + default_unit ;
fields_ . push_back ( field_name ) ;
prints_ . push_back ( { vname , vquantity , unit , getValueFunc , NULL , help , field , json , field_name } ) ;
2020-04-22 20:38:40 +00:00
}
2019-05-04 17:56:17 +00:00
void MeterCommonImplementation : : addPrint ( string vname , Quantity vquantity ,
function < string ( ) > getValueFunc ,
string help , bool field , bool json )
{
2020-09-08 20:11:32 +00:00
prints_ . push_back ( { vname , vquantity , defaultUnitForQuantity ( vquantity ) , NULL , getValueFunc , help , field , json , vname } ) ;
2019-05-04 11:55:52 +00:00
}
2021-03-14 08:41:35 +00:00
void MeterCommonImplementation : : poll ( shared_ptr < BusManager > bus )
2021-03-08 16:14:03 +00:00
{
}
2021-02-20 21:21:01 +00:00
vector < string > & MeterCommonImplementation : : ids ( )
2019-03-05 20:19:05 +00:00
{
return ids_ ;
2018-03-05 10:29:25 +00:00
}
2021-02-20 21:21:01 +00:00
string MeterCommonImplementation : : idsc ( )
{
return idsc_ ;
}
2020-05-09 21:43:30 +00:00
vector < string > MeterCommonImplementation : : fields ( )
{
return fields_ ;
}
2020-09-08 20:11:32 +00:00
vector < Print > MeterCommonImplementation : : prints ( )
{
return prints_ ;
}
2018-03-05 10:29:25 +00:00
string MeterCommonImplementation : : name ( )
{
return name_ ;
}
2019-03-05 20:19:05 +00:00
void MeterCommonImplementation : : onUpdate ( function < void ( Telegram * , Meter * ) > cb )
2018-03-05 10:29:25 +00:00
{
on_update_ . push_back ( cb ) ;
}
int MeterCommonImplementation : : numUpdates ( )
{
return num_updates_ ;
}
string MeterCommonImplementation : : datetimeOfUpdateHumanReadable ( )
{
char datetime [ 40 ] ;
memset ( datetime , 0 , sizeof ( datetime ) ) ;
strftime ( datetime , 20 , " %Y-%m-%d %H:%M.%S " , localtime ( & datetime_of_update_ ) ) ;
return string ( datetime ) ;
}
string MeterCommonImplementation : : datetimeOfUpdateRobot ( )
{
char datetime [ 40 ] ;
memset ( datetime , 0 , sizeof ( datetime ) ) ;
// This is the date time in the Greenwich timezone (Zulu time), dont get surprised!
strftime ( datetime , sizeof ( datetime ) , " %FT%TZ " , gmtime ( & datetime_of_update_ ) ) ;
return string ( datetime ) ;
}
2021-08-01 16:24:19 +00:00
string MeterCommonImplementation : : unixTimestampOfUpdate ( )
{
char ut [ 40 ] ;
memset ( ut , 0 , sizeof ( ut ) ) ;
2021-08-01 21:12:52 +00:00
snprintf ( ut , sizeof ( ut ) - 1 , " %zu " , datetime_of_update_ ) ;
2021-08-01 16:24:19 +00:00
return string ( ut ) ;
}
2021-03-08 16:14:03 +00:00
bool needsPolling ( MeterDriver d )
{
# define X(mname,linkmodes,info,driver,cname) if (d == MeterDriver::driver && 0 != ((linkmodes) & MBUS_bit)) return true;
LIST_OF_METERS
# undef X
return false ;
}
2021-03-13 17:35:47 +00:00
string toString ( MeterDriver mt )
2019-05-04 08:52:18 +00:00
{
2021-03-08 07:40:48 +00:00
# define X(mname,link,info,type,cname) if (mt == MeterDriver::type) return #mname;
2019-05-04 08:52:18 +00:00
LIST_OF_METERS
# undef X
return " unknown " ;
}
2021-03-08 07:40:48 +00:00
MeterDriver toMeterDriver ( string & t )
2019-05-04 08:52:18 +00:00
{
2021-03-08 07:40:48 +00:00
# define X(mname,linkmodes,info,type,cname) if (t == #mname) return MeterDriver::type;
2019-05-04 08:52:18 +00:00
LIST_OF_METERS
# undef X
2021-03-08 07:40:48 +00:00
return MeterDriver : : UNKNOWN ;
2018-11-01 16:17:23 +00:00
}
2019-06-06 15:28:20 +00:00
LinkModeSet toMeterLinkModeSet ( string & t )
2019-05-04 08:52:18 +00:00
{
2019-06-06 15:28:20 +00:00
# define X(mname,linkmodes,info,type,cname) if (t == #mname) return LinkModeSet(linkmodes);
2019-05-04 08:52:18 +00:00
LIST_OF_METERS
# undef X
2019-06-06 15:28:20 +00:00
return LinkModeSet ( ) ;
2019-05-04 08:52:18 +00:00
}
2018-03-05 10:29:25 +00:00
2021-03-13 17:35:47 +00:00
LinkModeSet toMeterLinkModeSet ( MeterDriver d )
{
# define X(mname,linkmodes,info,driver,cname) if (d == MeterDriver::driver) return LinkModeSet(linkmodes);
LIST_OF_METERS
# undef X
return LinkModeSet ( ) ;
}
2021-02-20 21:21:01 +00:00
bool MeterCommonImplementation : : isTelegramForMeter ( Telegram * t , Meter * meter , MeterInfo * mi )
2018-03-05 10:29:25 +00:00
{
2021-02-20 21:21:01 +00:00
string name ;
vector < string > ids ;
string idsc ;
2021-03-08 07:40:48 +00:00
MeterDriver driver ;
2021-02-20 21:21:01 +00:00
assert ( ( meter & & ! mi ) | |
( ! meter & & mi ) ) ;
if ( meter )
{
name = meter - > name ( ) ;
ids = meter - > ids ( ) ;
idsc = meter - > idsc ( ) ;
2021-03-08 07:40:48 +00:00
driver = meter - > driver ( ) ;
2021-02-20 21:21:01 +00:00
}
else
{
name = mi - > name ;
ids = mi - > ids ;
idsc = mi - > idsc ;
2021-03-08 07:40:48 +00:00
driver = mi - > driver ;
2021-02-20 21:21:01 +00:00
}
2021-03-14 11:15:49 +00:00
debug ( " (meter) %s: for me? %s in %s \n " , name . c_str ( ) , t - > idsc . c_str ( ) , idsc . c_str ( ) ) ;
2019-03-05 20:19:05 +00:00
2021-01-24 12:35:44 +00:00
bool used_wildcard = false ;
2021-02-20 21:21:01 +00:00
bool id_match = doesIdsMatchExpressions ( t - > ids , ids , & used_wildcard ) ;
2019-03-05 20:19:05 +00:00
if ( ! id_match ) {
2019-03-19 20:32:28 +00:00
// The id must match.
2021-02-20 21:21:01 +00:00
debug ( " (meter) %s: not for me: not my id \n " , name . c_str ( ) ) ;
2019-03-05 20:19:05 +00:00
return false ;
}
2021-03-08 07:40:48 +00:00
bool valid_driver = isMeterDriverValid ( driver , t - > dll_mfct , t - > dll_type , t - > dll_version ) ;
2021-01-30 16:58:00 +00:00
if ( ! valid_driver & & t - > tpl_id_found )
{
2021-03-08 07:40:48 +00:00
valid_driver = isMeterDriverValid ( driver , t - > tpl_mfct , t - > tpl_type , t - > tpl_version ) ;
2021-01-30 16:58:00 +00:00
}
2021-01-24 12:35:44 +00:00
2021-03-08 07:40:48 +00:00
if ( ! valid_driver & & driver ! = MeterDriver : : AUTO )
2020-02-23 12:55:37 +00:00
{
2020-09-04 09:31:49 +00:00
// Are we using the right driver? Perhaps not since
// this particular driver, mfct, media, version combo
// is not registered in the METER_DETECTION list in meters.h
2021-01-24 12:35:44 +00:00
if ( used_wildcard )
{
// The match for the id was not exact, thus the user is listening using a wildcard
// to many meters and some received matched meter telegrams are not from the right meter type,
// ie their driver does not match. Lets just ignore telegrams that probably cannot be decoded properly.
verbose ( " (meter) ignoring telegram from %s since it matched a wildcard id rule but driver does not match. \n " ,
2021-01-30 16:58:00 +00:00
t - > idsc . c_str ( ) ) ;
2021-01-24 12:35:44 +00:00
return false ;
}
// The match was exact, ie the user has actually specified 12345678 and foo as driver even
// though they do not match. Lets warn and then proceed. It is common that a user tries a
// new version of a meter with the old driver, thus it might not be a real error.
2021-01-24 20:44:20 +00:00
if ( isVerboseEnabled ( ) | | isDebugEnabled ( ) | | ! warned_for_telegram_before ( t , t - > dll_a ) )
2020-09-07 08:36:39 +00:00
{
2021-01-06 12:46:12 +00:00
string possible_drivers = t - > autoDetectPossibleDrivers ( ) ;
warning ( " (meter) %s: meter detection did not match the selected driver %s! correct driver is: %s \n "
" (meter) Not printing this warning agin for id: %02x%02x%02x%02x mfct: (%s) %s (0x%02x) type: %s (0x%02x) ver: 0x%02x \n " ,
2021-02-20 21:21:01 +00:00
name . c_str ( ) ,
2021-03-13 17:35:47 +00:00
toString ( driver ) . c_str ( ) ,
2021-01-06 12:46:12 +00:00
possible_drivers . c_str ( ) ,
t - > dll_id_b [ 3 ] , t - > dll_id_b [ 2 ] , t - > dll_id_b [ 1 ] , t - > dll_id_b [ 0 ] ,
manufacturerFlag ( t - > dll_mfct ) . c_str ( ) ,
manufacturer ( t - > dll_mfct ) . c_str ( ) ,
t - > dll_mfct ,
mediaType ( t - > dll_type , t - > dll_mfct ) . c_str ( ) , t - > dll_type ,
t - > dll_version ) ;
if ( possible_drivers = = " unknown! " )
{
warning ( " (meter) please consider opening an issue at https://github.com/weetmuts/wmbusmeters/ \n " ) ;
warning ( " (meter) to add support for this unknown mfct,media,version combination \n " ) ;
}
2020-09-07 08:36:39 +00:00
}
2019-03-05 17:38:54 +00:00
}
2019-03-05 20:19:05 +00:00
2021-02-20 21:21:01 +00:00
debug ( " (meter) %s: yes for me \n " , name . c_str ( ) ) ;
2019-03-05 20:19:05 +00:00
return true ;
2018-03-05 10:29:25 +00:00
}
2020-01-27 08:29:40 +00:00
MeterKeys * MeterCommonImplementation : : meterKeys ( )
2018-03-05 10:29:25 +00:00
{
2020-01-27 08:29:40 +00:00
return & meter_keys_ ;
2018-03-05 10:29:25 +00:00
}
2018-04-20 09:42:46 +00:00
vector < string > MeterCommonImplementation : : getRecords ( )
{
vector < string > recs ;
for ( auto & p : values_ )
{
recs . push_back ( p . first ) ;
}
return recs ;
}
double MeterCommonImplementation : : getRecordAsDouble ( string record )
{
return 0.0 ;
}
uint16_t MeterCommonImplementation : : getRecordAsUInt16 ( string record )
{
return 0 ;
}
2021-02-20 21:21:01 +00:00
int MeterCommonImplementation : : index ( )
{
return index_ ;
}
void MeterCommonImplementation : : setIndex ( int i )
{
index_ = i ;
}
2021-03-14 08:41:35 +00:00
string MeterCommonImplementation : : bus ( )
2021-03-08 16:14:03 +00:00
{
return bus_ ;
}
2018-03-05 10:29:25 +00:00
void MeterCommonImplementation : : triggerUpdate ( Telegram * t )
{
datetime_of_update_ = time ( NULL ) ;
num_updates_ + + ;
2019-03-05 20:19:05 +00:00
for ( auto & cb : on_update_ ) if ( cb ) cb ( t , this ) ;
2018-03-05 10:29:25 +00:00
t - > handled = true ;
}
2019-05-04 14:37:35 +00:00
2020-05-09 21:43:30 +00:00
string concatAllFields ( Meter * m , Telegram * t , char c , vector < Print > & prints , vector < Unit > & cs , bool hr )
2019-05-04 14:37:35 +00:00
{
string s ;
s = " " ;
s + = m - > name ( ) + c ;
2021-01-30 16:58:00 +00:00
if ( t - > ids . size ( ) > 0 )
{
s + = t - > ids . back ( ) + c ;
}
else
{
s + = c ;
}
2019-05-04 14:37:35 +00:00
for ( Print p : prints )
{
if ( p . field )
{
2019-05-04 17:56:17 +00:00
if ( p . getValueDouble )
{
Unit u = replaceWithConversionUnit ( p . default_unit , cs ) ;
double v = p . getValueDouble ( u ) ;
if ( hr ) {
s + = valueToString ( v , u ) ;
s + = " " + unitToStringHR ( u ) ;
} else {
s + = to_string ( v ) ;
}
}
if ( p . getValueString )
{
s + = p . getValueString ( ) ;
2019-05-04 14:37:35 +00:00
}
s + = c ;
}
}
s + = m - > datetimeOfUpdateHumanReadable ( ) ;
return s ;
}
2020-05-09 21:43:30 +00:00
string concatFields ( Meter * m , Telegram * t , char c , vector < Print > & prints , vector < Unit > & cs , bool hr ,
vector < string > * selected_fields )
{
if ( selected_fields = = NULL | | selected_fields - > size ( ) = = 0 )
{
return concatAllFields ( m , t , c , prints , cs , hr ) ;
}
string s ;
s = " " ;
for ( string field : * selected_fields )
{
if ( field = = " name " )
{
s + = m - > name ( ) + c ;
continue ;
}
if ( field = = " id " )
{
2021-01-30 16:58:00 +00:00
s + = t - > ids . back ( ) + c ;
2020-05-09 21:43:30 +00:00
continue ;
}
if ( field = = " timestamp " )
{
s + = m - > datetimeOfUpdateHumanReadable ( ) + c ;
continue ;
}
2021-08-01 21:12:52 +00:00
if ( field = = " timestamp_lt " )
{
s + = m - > datetimeOfUpdateHumanReadable ( ) + c ;
continue ;
}
if ( field = = " timestamp_utc " )
{
s + = m - > datetimeOfUpdateRobot ( ) + c ;
continue ;
}
2021-08-01 16:24:19 +00:00
if ( field = = " timestamp_ut " )
{
s + = m - > unixTimestampOfUpdate ( ) + c ;
continue ;
}
2020-11-12 08:56:51 +00:00
if ( field = = " device " )
{
s + = t - > about . device + c ;
continue ;
}
if ( field = = " rssi_dbm " )
{
s + = to_string ( t - > about . rssi_dbm ) + c ;
continue ;
}
2020-05-09 21:43:30 +00:00
2020-11-12 08:56:51 +00:00
bool handled = false ;
2020-05-09 21:43:30 +00:00
for ( Print p : prints )
{
if ( p . getValueString )
{
if ( field = = p . vname )
{
s + = p . getValueString ( ) + c ;
2020-11-12 08:56:51 +00:00
handled = true ;
2020-05-09 21:43:30 +00:00
}
}
else if ( p . getValueDouble )
{
string default_unit = unitToStringLowerCase ( p . default_unit ) ;
string var = p . vname + " _ " + default_unit ;
if ( field = = var )
{
s + = valueToString ( p . getValueDouble ( p . default_unit ) , p . default_unit ) + c ;
2020-11-12 08:56:51 +00:00
handled = true ;
2020-05-09 21:43:30 +00:00
}
else
{
Unit u = replaceWithConversionUnit ( p . default_unit , cs ) ;
if ( u ! = p . default_unit )
{
string unit = unitToStringLowerCase ( u ) ;
string var = p . vname + " _ " + unit ;
if ( field = = var )
{
s + = valueToString ( p . getValueDouble ( u ) , u ) + c ;
2020-11-12 08:56:51 +00:00
handled = true ;
2020-05-09 21:43:30 +00:00
}
}
}
}
}
2020-11-12 08:56:51 +00:00
if ( ! handled )
{
s + = " ? " + field + " ? " + c ;
}
2020-05-09 21:43:30 +00:00
}
if ( s . back ( ) = = c ) s . pop_back ( ) ;
return s ;
}
2021-02-20 21:21:01 +00:00
bool MeterCommonImplementation : : handleTelegram ( AboutTelegram & about , vector < uchar > input_frame , bool simulated , string * ids , bool * id_match )
2019-05-04 15:38:10 +00:00
{
2020-01-27 08:29:40 +00:00
Telegram t ;
2020-10-14 18:59:14 +00:00
t . about = about ;
2020-01-27 08:29:40 +00:00
bool ok = t . parseHeader ( input_frame ) ;
2020-09-08 18:50:51 +00:00
if ( simulated ) t . markAsSimulated ( ) ;
2021-01-30 16:58:00 +00:00
* ids = t . idsc ;
2020-09-25 17:14:34 +00:00
2021-02-20 21:21:01 +00:00
if ( ! ok | | ! isTelegramForMeter ( & t , this , NULL ) )
2020-01-27 08:29:40 +00:00
{
2019-05-04 15:38:10 +00:00
// This telegram is not intended for this meter.
2020-01-27 08:29:40 +00:00
return false ;
2019-05-04 15:38:10 +00:00
}
2021-02-20 21:21:01 +00:00
* id_match = true ;
2021-02-24 19:50:45 +00:00
verbose ( " (meter) %s %s handling telegram from %s \n " , name ( ) . c_str ( ) , meterDriver ( ) . c_str ( ) , t . ids . back ( ) . c_str ( ) ) ;
2020-09-25 17:14:34 +00:00
2020-02-06 18:01:48 +00:00
if ( isDebugEnabled ( ) )
{
string msg = bin2hex ( input_frame ) ;
2021-01-30 16:58:00 +00:00
debug ( " (meter) %s %s \" %s \" \n " , name ( ) . c_str ( ) , t . ids . back ( ) . c_str ( ) , msg . c_str ( ) ) ;
2020-02-06 18:01:48 +00:00
}
2021-01-30 16:58:00 +00:00
ok = t . parse ( input_frame , & meter_keys_ , true ) ;
2020-02-03 17:39:07 +00:00
if ( ! ok )
{
// Ignoring telegram since it could not be parsed.
return false ;
}
2019-05-04 15:38:10 +00:00
char log_prefix [ 256 ] ;
2021-02-24 19:50:45 +00:00
snprintf ( log_prefix , 255 , " (%s) log " , meterDriver ( ) . c_str ( ) ) ;
2021-02-07 22:06:44 +00:00
logTelegram ( t . original , t . frame , t . header_size , t . suffix_size ) ;
2019-05-04 15:38:10 +00:00
// Invoke meter specific parsing!
2020-01-27 08:29:40 +00:00
processContent ( & t ) ;
2019-05-04 15:38:10 +00:00
// All done....
if ( isDebugEnabled ( ) )
{
char log_prefix [ 256 ] ;
2021-02-24 19:50:45 +00:00
snprintf ( log_prefix , 255 , " (%s) " , meterDriver ( ) . c_str ( ) ) ;
2020-01-27 08:29:40 +00:00
t . explainParse ( log_prefix , 0 ) ;
2019-05-04 15:38:10 +00:00
}
2020-01-27 08:29:40 +00:00
triggerUpdate ( & t ) ;
return true ;
2019-05-04 15:38:10 +00:00
}
2019-05-04 14:37:35 +00:00
void MeterCommonImplementation : : printMeter ( Telegram * t ,
string * human_readable ,
string * fields , char separator ,
string * json ,
2019-10-20 17:19:17 +00:00
vector < string > * envs ,
2020-05-09 21:43:30 +00:00
vector < string > * more_json ,
2021-08-01 21:12:52 +00:00
vector < string > * selected_fields ,
vector < string > * added_fields )
2019-05-04 14:37:35 +00:00
{
2020-05-09 21:43:30 +00:00
* human_readable = concatFields ( this , t , ' \t ' , prints_ , conversions_ , true , selected_fields ) ;
* fields = concatFields ( this , t , separator , prints_ , conversions_ , false , selected_fields ) ;
2019-05-04 14:37:35 +00:00
2021-02-24 19:50:45 +00:00
string media ;
2021-01-30 16:58:00 +00:00
if ( t - > tpl_id_found )
{
2021-02-24 19:50:45 +00:00
media = mediaTypeJSON ( t - > tpl_type , t - > tpl_mfct ) ;
2021-01-30 16:58:00 +00:00
}
else if ( t - > ell_id_found )
{
2021-02-24 19:50:45 +00:00
media = mediaTypeJSON ( t - > ell_type , t - > ell_mfct ) ;
2021-01-30 16:58:00 +00:00
}
else
{
2021-02-24 19:50:45 +00:00
media = mediaTypeJSON ( t - > dll_type , t - > dll_mfct ) ;
2021-01-30 16:58:00 +00:00
}
2019-05-04 14:37:35 +00:00
string s ;
s + = " { " ;
2021-02-24 19:50:45 +00:00
s + = " \" media \" : \" " + media + " \" , " ;
s + = " \" meter \" : \" " + meterDriver ( ) + " \" , " ;
2019-05-04 14:37:35 +00:00
s + = " \" name \" : \" " + name ( ) + " \" , " ;
2021-01-30 16:58:00 +00:00
if ( t - > ids . size ( ) > 0 )
{
s + = " \" id \" : \" " + t - > ids . back ( ) + " \" , " ;
}
else
{
s + = " \" id \" : \" \" , " ;
}
2019-05-04 14:37:35 +00:00
for ( Print p : prints_ )
{
2019-05-04 17:56:17 +00:00
if ( p . json )
2019-05-04 14:37:35 +00:00
{
string default_unit = unitToStringLowerCase ( p . default_unit ) ;
string var = p . vname ;
2019-05-04 17:56:17 +00:00
if ( p . getValueString ) {
s + = " \" " + var + " \" : \" " + p . getValueString ( ) + " \" , " ;
}
if ( p . getValueDouble ) {
s + = " \" " + var + " _ " + default_unit + " \" : " + valueToString ( p . getValueDouble ( p . default_unit ) , p . default_unit ) + " , " ;
Unit u = replaceWithConversionUnit ( p . default_unit , conversions_ ) ;
if ( u ! = p . default_unit )
{
string unit = unitToStringLowerCase ( u ) ;
s + = " \" " + var + " _ " + unit + " \" : " + valueToString ( p . getValueDouble ( u ) , u ) + " , " ;
}
2019-05-04 14:37:35 +00:00
}
}
}
s + = " \" timestamp \" : \" " + datetimeOfUpdateRobot ( ) + " \" " ;
2020-10-14 18:59:14 +00:00
if ( t - > about . device ! = " " )
{
s + = " , " ;
s + = " \" device \" : \" " + t - > about . device + " \" , " ;
s + = " \" rssi_dbm \" : " + to_string ( t - > about . rssi_dbm ) ;
}
2019-10-20 17:19:17 +00:00
for ( string add_json : additionalJsons ( ) )
{
s + = " , " ;
s + = makeQuotedJson ( add_json ) ;
}
for ( string add_json : * more_json )
{
s + = " , " ;
s + = makeQuotedJson ( add_json ) ;
}
2019-05-04 14:37:35 +00:00
s + = " } " ;
* json = s ;
envs - > push_back ( string ( " METER_JSON= " ) + * json ) ;
2021-01-30 16:58:00 +00:00
if ( t - > ids . size ( ) > 0 )
{
envs - > push_back ( string ( " METER_ID= " ) + t - > ids . back ( ) ) ;
}
else
{
envs - > push_back ( string ( " METER_ID= " ) ) ;
}
2021-02-24 19:50:45 +00:00
envs - > push_back ( string ( " METER_NAME= " ) + name ( ) ) ;
envs - > push_back ( string ( " METER_MEDIA= " ) + media ) ;
envs - > push_back ( string ( " METER_TYPE= " ) + meterDriver ( ) ) ;
envs - > push_back ( string ( " METER_TIMESTAMP= " ) + datetimeOfUpdateRobot ( ) ) ;
2021-08-01 21:12:52 +00:00
envs - > push_back ( string ( " METER_TIMESTAMP_UTC= " ) + datetimeOfUpdateRobot ( ) ) ;
envs - > push_back ( string ( " METER_TIMESTAMP_UT= " ) + unixTimestampOfUpdate ( ) ) ;
envs - > push_back ( string ( " METER_TIMESTAMP_LT= " ) + datetimeOfUpdateHumanReadable ( ) ) ;
2021-02-24 19:50:45 +00:00
if ( t - > about . device ! = " " )
{
envs - > push_back ( string ( " METER_DEVICE= " ) + t - > about . device ) ;
envs - > push_back ( string ( " METER_RSSI_DBM= " ) + to_string ( t - > about . rssi_dbm ) ) ;
}
2019-05-04 14:37:35 +00:00
for ( Print p : prints_ )
{
2019-05-04 17:56:17 +00:00
if ( p . json )
2019-05-04 14:37:35 +00:00
{
string default_unit = unitToStringUpperCase ( p . default_unit ) ;
string var = p . vname ;
2019-05-04 18:52:05 +00:00
std : : transform ( var . begin ( ) , var . end ( ) , var . begin ( ) , : : toupper ) ;
2019-05-04 17:56:17 +00:00
if ( p . getValueString ) {
2019-05-04 18:52:05 +00:00
string envvar = " METER_ " + var + " = " + p . getValueString ( ) ;
envs - > push_back ( envvar ) ;
2019-05-04 17:56:17 +00:00
}
if ( p . getValueDouble ) {
string envvar = " METER_ " + var + " _ " + default_unit + " = " + valueToString ( p . getValueDouble ( p . default_unit ) , p . default_unit ) ;
2019-05-04 14:37:35 +00:00
envs - > push_back ( envvar ) ;
2019-05-04 17:56:17 +00:00
Unit u = replaceWithConversionUnit ( p . default_unit , conversions_ ) ;
if ( u ! = p . default_unit )
{
string unit = unitToStringUpperCase ( u ) ;
string envvar = " METER_ " + var + " _ " + unit + " = " + valueToString ( p . getValueDouble ( u ) , u ) ;
envs - > push_back ( envvar ) ;
}
2019-05-04 14:37:35 +00:00
}
}
}
2021-02-24 19:50:45 +00:00
2019-10-20 17:19:17 +00:00
// If the configuration has supplied json_address=Roodroad 123
// then the env variable METER_address will available and have the content "Roodroad 123"
for ( string add_json : additionalJsons ( ) )
{
envs - > push_back ( string ( " METER_ " ) + add_json ) ;
}
for ( string add_json : * more_json )
{
envs - > push_back ( string ( " METER_ " ) + add_json ) ;
}
2019-05-04 14:37:35 +00:00
}
2019-05-04 15:38:10 +00:00
2020-09-24 19:23:06 +00:00
double WaterMeter : : totalWaterConsumption ( Unit u ) { return - NAN ; }
2019-05-04 15:38:10 +00:00
bool WaterMeter : : hasTotalWaterConsumption ( ) { return false ; }
2020-09-24 19:23:06 +00:00
double WaterMeter : : targetWaterConsumption ( Unit u ) { return - NAN ; }
2019-05-04 15:38:10 +00:00
bool WaterMeter : : hasTargetWaterConsumption ( ) { return false ; }
2020-09-24 19:23:06 +00:00
double WaterMeter : : maxFlow ( Unit u ) { return - NAN ; }
2019-05-04 15:38:10 +00:00
bool WaterMeter : : hasMaxFlow ( ) { return false ; }
2020-09-24 19:23:06 +00:00
double WaterMeter : : flowTemperature ( Unit u ) { return - NAN ; }
2019-05-04 15:38:10 +00:00
bool WaterMeter : : hasFlowTemperature ( ) { return false ; }
2020-09-24 19:23:06 +00:00
double WaterMeter : : externalTemperature ( Unit u ) { return - NAN ; }
2019-05-04 15:38:10 +00:00
bool WaterMeter : : hasExternalTemperature ( ) { return false ; }
2020-09-24 19:23:06 +00:00
string WaterMeter : : statusHumanReadable ( ) { return " -NAN " ; }
string WaterMeter : : status ( ) { return " -NAN " ; }
string WaterMeter : : timeDry ( ) { return " -NAN " ; }
string WaterMeter : : timeReversed ( ) { return " -NAN " ; }
string WaterMeter : : timeLeaking ( ) { return " -NAN " ; }
string WaterMeter : : timeBursting ( ) { return " -NAN " ; }
double HeatMeter : : totalEnergyConsumption ( Unit u ) { return - NAN ; }
double HeatMeter : : currentPeriodEnergyConsumption ( Unit u ) { return - NAN ; }
double HeatMeter : : previousPeriodEnergyConsumption ( Unit u ) { return - NAN ; }
double HeatMeter : : currentPowerConsumption ( Unit u ) { return - NAN ; }
double HeatMeter : : totalVolume ( Unit u ) { return - NAN ; }
double ElectricityMeter : : totalEnergyConsumption ( Unit u ) { return - NAN ; }
double ElectricityMeter : : totalEnergyProduction ( Unit u ) { return - NAN ; }
double ElectricityMeter : : totalReactiveEnergyConsumption ( Unit u ) { return - NAN ; }
double ElectricityMeter : : totalReactiveEnergyProduction ( Unit u ) { return - NAN ; }
double ElectricityMeter : : totalApparentEnergyConsumption ( Unit u ) { return - NAN ; }
double ElectricityMeter : : totalApparentEnergyProduction ( Unit u ) { return - NAN ; }
double ElectricityMeter : : currentPowerConsumption ( Unit u ) { return - NAN ; }
double ElectricityMeter : : currentPowerProduction ( Unit u ) { return - NAN ; }
2020-11-14 09:48:49 +00:00
double HeatCostAllocationMeter : : currentConsumption ( Unit u ) { return - NAN ; }
string HeatCostAllocationMeter : : setDate ( ) { return " NAN " ; }
double HeatCostAllocationMeter : : consumptionAtSetDate ( Unit u ) { return - NAN ; }
2019-05-04 17:56:17 +00:00
2020-01-27 08:29:40 +00:00
void MeterCommonImplementation : : setExpectedTPLSecurityMode ( TPLSecurityMode tsm )
{
expected_tpl_sec_mode_ = tsm ;
}
void MeterCommonImplementation : : setExpectedELLSecurityMode ( ELLSecurityMode dsm )
{
expected_ell_sec_mode_ = dsm ;
}
TPLSecurityMode MeterCommonImplementation : : expectedTPLSecurityMode ( )
2019-05-04 17:56:17 +00:00
{
2020-01-27 08:29:40 +00:00
return expected_tpl_sec_mode_ ;
2019-05-04 17:56:17 +00:00
}
2020-01-27 08:29:40 +00:00
ELLSecurityMode MeterCommonImplementation : : expectedELLSecurityMode ( )
2019-05-04 17:56:17 +00:00
{
2020-01-27 08:29:40 +00:00
return expected_ell_sec_mode_ ;
2019-05-04 17:56:17 +00:00
}
2021-03-07 18:52:21 +00:00
void detectMeterDrivers ( int manufacturer , int media , int version , vector < string > * drivers )
2019-05-04 17:56:17 +00:00
{
2021-03-13 17:35:47 +00:00
# define X(TY,MA,ME,VE) { if (manufacturer == MA && (media == ME || ME == -1) && (version == VE || VE == -1)) { drivers->push_back(toString(MeterDriver::TY)); }}
2020-09-04 09:31:49 +00:00
METER_DETECTION
# undef X
2019-05-04 17:56:17 +00:00
}
2021-03-08 07:40:48 +00:00
bool isMeterDriverValid ( MeterDriver type , int manufacturer , int media , int version )
2019-05-04 17:56:17 +00:00
{
2021-03-08 07:40:48 +00:00
# define X(TY,MA,ME,VE) { if (type == MeterDriver::TY && manufacturer == MA && (media == ME || ME == -1) && (version == VE || VE == -1)) { return true; }}
2020-09-04 09:31:49 +00:00
METER_DETECTION
# undef X
return false ;
2019-05-04 17:56:17 +00:00
}
2021-02-20 10:08:23 +00:00
2021-03-08 07:40:48 +00:00
MeterDriver pickMeterDriver ( Telegram * t )
2021-03-07 18:52:21 +00:00
{
int manufacturer = t - > dll_mfct ;
int media = t - > dll_type ;
int version = t - > dll_version ;
if ( t - > tpl_id_found )
{
2021-03-07 19:40:41 +00:00
manufacturer = t - > tpl_mfct ;
media = t - > tpl_type ;
version = t - > tpl_version ;
2021-03-07 18:52:21 +00:00
}
2021-03-08 07:40:48 +00:00
# define X(TY,MA,ME,VE) { if (manufacturer == MA && (media == ME || ME == -1) && (version == VE || VE == -1)) { return MeterDriver::TY; }}
2021-03-07 18:52:21 +00:00
METER_DETECTION
# undef X
2021-03-08 07:40:48 +00:00
return MeterDriver : : UNKNOWN ;
2021-03-07 18:52:21 +00:00
}
2021-02-20 21:21:01 +00:00
shared_ptr < Meter > createMeter ( MeterInfo * mi )
2021-02-20 10:08:23 +00:00
{
shared_ptr < Meter > newm ;
const char * keymsg = ( mi - > key [ 0 ] = = 0 ) ? " not-encrypted " : " encrypted " ;
2021-03-08 07:40:48 +00:00
switch ( mi - > driver )
2021-02-20 10:08:23 +00:00
{
2021-03-08 07:40:48 +00:00
# define X(mname,link,info,driver,cname) \
case MeterDriver : : driver : \
2021-02-20 21:21:01 +00:00
{ \
2021-02-20 10:08:23 +00:00
newm = create # # cname ( * mi ) ; \
2021-02-20 21:21:01 +00:00
newm - > addConversions ( mi - > conversions ) ; \
verbose ( " (meter) created \" %s \" \" " # mname " \" \" %s \" %s \n " , \
mi - > name . c_str ( ) , mi - > idsc . c_str ( ) , keymsg ) ; \
2021-02-20 10:08:23 +00:00
return newm ; \
} \
break ;
LIST_OF_METERS
# undef X
}
return newm ;
}
2021-03-13 17:35:47 +00:00
bool is_driver_extras ( string t , MeterDriver * out_driver , string * out_extras )
{
// piigth(jump=foo)
// multical21
size_t ps = t . find ( ' ( ' ) ;
size_t pe = t . find ( ' ) ' ) ;
size_t te = 0 ; // Position after type end.
bool found_parentheses = ( ps ! = string : : npos & & pe ! = string : : npos ) ;
if ( ! found_parentheses )
{
// No brackets nor parentheses found, is t a known wmbus device? like im871a amb8465 etc....
MeterDriver md = toMeterDriver ( t ) ;
if ( md = = MeterDriver : : UNKNOWN ) return false ;
* out_driver = md ;
* out_extras = " " ;
return true ;
}
// Parentheses must be last.
if ( ! ( ps > 0 & & ps < pe & & pe = = t . length ( ) - 1 ) ) return false ;
te = ps ;
string type = t . substr ( 0 , te ) ;
MeterDriver md = toMeterDriver ( type ) ;
if ( md = = MeterDriver : : UNKNOWN ) return false ;
* out_driver = md ;
string extras = t . substr ( ps + 1 , pe - ps - 1 ) ;
* out_extras = extras ;
return true ;
}
string MeterInfo : : str ( )
{
string r ;
r + = toString ( driver ) ;
if ( extras ! = " " )
{
r + = " ( " + extras + " ) " ;
}
r + = " : " ;
if ( bus ! = " " ) r + = bus + " : " ;
if ( bps ! = 0 ) r + = bps + " : " ;
if ( ! link_modes . empty ( ) ) r + = link_modes . hr ( ) + " : " ;
if ( r . size ( ) > 0 ) r . pop_back ( ) ;
return r ;
}
bool MeterInfo : : parse ( string n , string d , string i , string k )
{
clear ( ) ;
name = n ;
ids = splitMatchExpressions ( i ) ;
key = k ;
bool driverextras_checked = false ;
bool bus_checked = false ;
bool bps_checked = false ;
bool link_modes_checked = false ;
// For the moment the colon : is forbidden in file names and commands.
// It cannot occur in type,fq or bps.
vector < string > parts = splitString ( d , ' : ' ) ;
// Example piigth:MAIN:2400
// multical21:c1
// telco:BUS2:c2
// driver ( extras ) : bus_alias : bps : linkmodes
for ( auto & p : parts )
{
if ( ! driverextras_checked & & is_driver_extras ( p , & driver , & extras ) )
{
driverextras_checked = true ;
}
else if ( ! bus_checked & & isValidAlias ( p ) & & ! isValidBps ( p ) & & ! isValidLinkModes ( p ) )
{
driverextras_checked = true ;
bus_checked = true ;
bus = p ;
}
else if ( ! bps_checked & & isValidBps ( p ) & & ! isValidLinkModes ( p ) )
{
driverextras_checked = true ;
bus_checked = true ;
bps_checked = true ;
bps = atoi ( p . c_str ( ) ) ;
}
else if ( ! link_modes_checked & & isValidLinkModes ( p ) )
{
driverextras_checked = true ;
bus_checked = true ;
bps_checked = true ;
link_modes_checked = true ;
link_modes = parseLinkModes ( p ) ;
}
else
{
// Unknown part....
return false ;
}
}
if ( ! link_modes_checked )
{
// No explicit link mode set, set to the default link modes
// that the meter can transmit on.
link_modes = toMeterLinkModeSet ( driver ) ;
}
return true ;
}
2021-03-14 08:41:35 +00:00
bool isValidKey ( string & key , MeterDriver mt )
{
if ( key . length ( ) = = 0 ) return true ;
if ( key = = " NOKEY " ) {
key = " " ;
return true ;
}
if ( mt = = MeterDriver : : IZAR | |
mt = = MeterDriver : : HYDRUS )
{
// These meters can either be OMS compatible 128 bit key (32 hex).
// Or using an older proprietary encryption with 64 bit keys (16 hex)
if ( key . length ( ) ! = 16 & & key . length ( ) ! = 32 ) return false ;
}
else
{
// OMS compliant meters have 128 bit AES keys (32 hex).
// There is a deprecated DES mode, but I have not yet
// seen any telegram using that mode.
if ( key . length ( ) ! = 32 ) return false ;
}
vector < uchar > tmp ;
return hex2bin ( key , & tmp ) ;
}