2018-04-01 06:53:37 +00:00
/*
2022-02-12 14:28:17 +00:00
Copyright ( C ) 2017 - 2022 Fredrik Ö hrstr ö m ( gpl - 3.0 - or - later )
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
2022-01-02 12:08:36 +00:00
2022-01-06 17:28:22 +00:00
map < string , DriverInfo > all_registered_drivers_ ;
vector < DriverInfo > all_registered_drivers_list_ ;
2022-01-02 12:08:36 +00:00
2022-01-06 17:28:22 +00:00
bool DriverInfo : : detect ( uint16_t mfct , uchar type , uchar version )
2022-01-02 12:08:36 +00:00
{
2022-01-06 17:28:22 +00:00
for ( auto & dd : detect_ )
{
if ( dd . mfct = = 0 & & dd . type = = 0 & & dd . version = = 0 ) continue ; // Ignore drivers with no detection.
if ( dd . mfct = = mfct & & dd . type = = type & & dd . version = = version ) return true ;
}
return false ;
}
2022-01-25 20:21:17 +00:00
bool DriverInfo : : isValidMedia ( uchar type )
{
for ( auto & dd : detect_ )
{
if ( dd . type = = type ) return true ;
}
return false ;
}
2022-02-08 19:51:13 +00:00
bool DriverInfo : : isCloseEnoughMedia ( uchar type )
{
for ( auto & dd : detect_ )
{
if ( isCloseEnough ( dd . type , type ) ) return true ;
}
return false ;
}
2022-01-06 17:28:22 +00:00
void DriverInfo : : setExpectedELLSecurityMode ( ELLSecurityMode dsm )
{
// TODO should check that the telegram is encrypted using the same mode.
}
2022-01-02 12:08:36 +00:00
2022-01-06 17:28:22 +00:00
void DriverInfo : : setExpectedTPLSecurityMode ( TPLSecurityMode tsm )
{
// TODO should check that the telegram is encrypted using the same mode.
}
bool registerDriver ( function < void ( DriverInfo & ) > setup )
{
DriverInfo di ;
setup ( di ) ;
// Check that the driver name is unique.
assert ( all_registered_drivers_ . count ( di . name ( ) . str ( ) ) = = 0 ) ;
// Check that no other driver also triggers on the same detection values.
for ( auto & d : di . detect ( ) )
{
for ( auto & p : all_registered_drivers_ )
{
bool foo = p . second . detect ( d . mfct , d . type , d . version ) ;
2022-02-09 13:52:13 +00:00
if ( foo )
{
error ( " Internal error: driver %s tried to register the same auto detect combo as driver %s alread has taken! \n " ,
di . name ( ) . str ( ) . c_str ( ) , p . second . name ( ) . str ( ) . c_str ( ) ) ;
}
2022-01-06 17:28:22 +00:00
}
2022-01-25 19:42:46 +00:00
}
2022-01-06 17:28:22 +00:00
// Everything looks, good install this driver.
all_registered_drivers_ [ di . name ( ) . str ( ) ] = di ;
all_registered_drivers_list_ . push_back ( di ) ;
2022-01-02 12:08:36 +00:00
// This code is invoked from the static initializers of DriverInfos when starting
// wmbusmeters. Thus we do not yet know if the user has supplied --debug or similar setting.
// To debug this you have to uncomment the printf below.
// fprintf(stderr, "(STATIC) added driver: %s\n", n.c_str());
2022-01-06 17:28:22 +00:00
return true ;
}
bool lookupDriverInfo ( string & driver , DriverInfo * out_di )
{
if ( all_registered_drivers_ . count ( driver ) = = 0 )
{
return false ;
}
* out_di = all_registered_drivers_ [ driver ] ;
return true ;
2022-01-02 12:08:36 +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_ { } ;
2021-12-07 18:51:26 +00:00
bool should_analyze_ { } ;
2021-12-07 22:56:29 +00:00
OutputFormat analyze_format_ { } ;
2022-02-06 17:49:55 +00:00
string analyze_driver_ ;
string analyze_key_ ;
bool analyze_verbose_ ;
2021-02-20 21:21:01 +00:00
vector < MeterInfo > meter_templates_ ;
vector < shared_ptr < Meter > > meters_ ;
2022-03-06 21:42:44 +00:00
vector < function < bool ( AboutTelegram & , vector < uchar > ) > > telegram_listeners_ ;
2021-02-20 21:21:01 +00:00
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 ( )
{
2022-02-08 21:02:46 +00:00
if ( meters_ . size ( ) < meter_templates_ . size ( ) ) return false ;
2020-09-07 08:36:39 +00:00
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
{
2021-12-07 18:51:26 +00:00
if ( should_analyze_ )
{
analyzeTelegram ( about , input_frame , simulated ) ;
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.
2022-01-06 17:28:22 +00:00
MeterInfo meter_info = mi ;
2021-02-20 21:21:01 +00:00
// 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 ( ) ) ;
2022-01-06 17:28:22 +00:00
meter_info . ids = tmp_ids ;
meter_info . idsc = t . ids . back ( ) ;
2021-03-07 18:52:21 +00:00
2022-01-06 17:28:22 +00:00
if ( meter_info . driver = = MeterDriver : : AUTO )
2021-03-07 18:52:21 +00:00
{
// Look up the proper meter driver!
2022-01-06 17:28:22 +00:00
DriverInfo di = pickMeterDriver ( & t ) ;
if ( di . driver ( ) = = MeterDriver : : UNKNOWN & & di . name ( ) . str ( ) = = " " )
2021-03-07 19:10:44 +00:00
{
2021-12-07 22:56:29 +00:00
if ( should_analyze_ = = false )
{
// We are not analyzing, so warn here.
warnForUnknownDriver ( mi . name , & t ) ;
}
2021-03-07 19:10:44 +00:00
}
2022-01-06 17:28:22 +00:00
else
{
meter_info . driver = di . driver ( ) ;
meter_info . driver_name = di . name ( ) ;
}
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.
2022-01-06 17:28:22 +00:00
auto meter = createMeter ( & meter_info ) ;
2021-02-20 21:21:01 +00:00
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 ( ) ,
2022-01-06 17:28:22 +00:00
meter_info . 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 ( ) ,
2022-01-06 17:28:22 +00:00
meter_info . 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 " ,
2022-01-08 17:52:06 +00:00
meter - > name ( ) . c_str ( ) , meter - > idsc ( ) . c_str ( ) , meter - > driverName ( ) . str ( ) . 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 " ,
2022-01-08 17:52:06 +00:00
meter - > name ( ) . c_str ( ) , meter - > idsc ( ) . c_str ( ) , meter - > driverName ( ) . str ( ) . c_str ( ) ) ;
2021-02-20 21:21:01 +00:00
}
else
{
handled = true ;
}
}
}
}
}
2022-03-06 21:42:44 +00:00
for ( auto f : telegram_listeners_ )
{
f ( about , input_frame ) ;
}
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
2022-03-06 21:42:44 +00:00
void onTelegram ( function < bool ( AboutTelegram & about , vector < uchar > ) > cb )
2020-09-08 12:55:01 +00:00
{
2022-03-06 21:42:44 +00:00
telegram_listeners_ . push_back ( cb ) ;
2020-09-08 12:55:01 +00:00
}
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
}
}
2022-02-06 17:49:55 +00:00
void analyzeEnabled ( bool b , OutputFormat f , string force_driver , string key , bool verbose )
2021-12-07 18:51:26 +00:00
{
should_analyze_ = b ;
2021-12-07 22:56:29 +00:00
analyze_format_ = f ;
2022-03-27 20:17:28 +00:00
if ( force_driver ! = " auto " )
{
analyze_driver_ = force_driver ;
}
2022-02-06 17:49:55 +00:00
analyze_key_ = key ;
analyze_verbose_ = verbose ;
2021-12-07 18:51:26 +00:00
}
2022-02-06 17:49:55 +00:00
string findBestOldStyleDriver ( MeterInfo & mi ,
int * best_length ,
int * best_understood ,
Telegram & t ,
AboutTelegram & about ,
vector < uchar > & input_frame ,
bool simulated ,
2022-03-27 12:43:10 +00:00
string force )
2021-12-07 18:51:26 +00:00
{
2022-02-06 17:49:55 +00:00
vector < MeterDriver > old_drivers ;
# define X(mname,linkmode,info,type,cname) old_drivers.push_back(MeterDriver::type);
2021-12-07 22:56:29 +00:00
LIST_OF_METERS
# undef X
2022-02-06 17:49:55 +00:00
string best_driver = " " ;
for ( MeterDriver odr : old_drivers )
2021-12-07 22:56:29 +00:00
{
2022-02-06 17:49:55 +00:00
if ( odr = = MeterDriver : : AUTO ) continue ;
if ( odr = = MeterDriver : : UNKNOWN ) continue ;
string driver_name = toString ( odr ) ;
2022-03-27 12:43:10 +00:00
if ( force ! = " " )
{
if ( driver_name ! = force ) continue ;
return driver_name ;
}
2021-12-07 22:56:29 +00:00
2022-03-27 12:43:10 +00:00
if ( force = = " " & &
2022-03-27 10:09:18 +00:00
! isMeterDriverReasonableForMedia ( odr , " " , t . dll_type ) & &
2022-02-06 17:49:55 +00:00
! isMeterDriverReasonableForMedia ( odr , " " , t . tpl_type ) )
2022-01-25 20:21:17 +00:00
{
2022-02-06 17:49:55 +00:00
// Sanity check, skip this driver since it is not relevant for this media.
2022-01-25 20:21:17 +00:00
continue ;
}
2022-02-06 17:49:55 +00:00
debug ( " Testing old style driver %s... \n " , driver_name . c_str ( ) ) ;
mi . driver = odr ;
mi . driver_name = DriverName ( " " ) ;
2021-12-07 22:56:29 +00:00
auto meter = createMeter ( & mi ) ;
2022-02-06 17:49:55 +00:00
2021-12-07 22:56:29 +00:00
bool match = false ;
string id ;
bool h = meter - > handleTelegram ( about , input_frame , simulated , & id , & match , & t ) ;
if ( ! match )
{
2022-02-06 17:49:55 +00:00
debug ( " no match! \n " ) ;
2022-01-25 19:42:46 +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. But it is ok if analyzing....
2022-02-06 17:49:55 +00:00
debug ( " Newly created meter (%s %s %s) did not handle telegram! \n " ,
2022-01-25 19:42:46 +00:00
meter - > name ( ) . c_str ( ) , meter - > idsc ( ) . c_str ( ) , meter - > driverName ( ) . str ( ) . c_str ( ) ) ;
}
else
{
int l = 0 ;
int u = 0 ;
t . analyzeParse ( OutputFormat : : NONE , & l , & u ) ;
2022-03-27 12:43:10 +00:00
if ( analyze_verbose_ & & force = = " " ) printf ( " (verbose) old %02d/%02d %s \n " , u , l , driver_name . c_str ( ) ) ;
2022-02-06 17:49:55 +00:00
if ( u > * best_understood )
2022-01-25 19:42:46 +00:00
{
2022-02-06 17:49:55 +00:00
* best_understood = u ;
* best_length = l ;
best_driver = driver_name ;
2022-03-27 12:43:10 +00:00
if ( analyze_verbose_ & & force = = " " ) printf ( " (verbose) old best so far: %s %02d/%02d \n " , best_driver . c_str ( ) , u , l ) ;
2022-01-25 19:42:46 +00:00
}
}
}
2022-02-06 17:49:55 +00:00
return best_driver ;
}
string findBestNewStyleDriver ( MeterInfo & mi ,
int * best_length ,
int * best_understood ,
Telegram & t ,
AboutTelegram & about ,
vector < uchar > & input_frame ,
bool simulated ,
string only )
{
string best_driver = " " ;
for ( DriverInfo ndr : all_registered_drivers_list_ )
2022-01-25 19:42:46 +00:00
{
2022-02-06 17:49:55 +00:00
string driver_name = toString ( ndr ) ;
2022-03-27 12:43:10 +00:00
if ( only ! = " " )
{
if ( driver_name ! = only ) continue ;
return driver_name ;
}
2022-02-06 17:49:55 +00:00
2022-03-27 10:09:18 +00:00
if ( only = = " " & &
! isMeterDriverReasonableForMedia ( MeterDriver : : AUTO , driver_name , t . dll_type ) & &
! isMeterDriverReasonableForMedia ( MeterDriver : : AUTO , driver_name , t . tpl_type ) )
{
// Sanity check, skip this driver since it is not relevant for this media.
continue ;
}
2022-02-06 17:49:55 +00:00
debug ( " Testing new style driver %s... \n " , driver_name . c_str ( ) ) ;
mi . driver = MeterDriver : : UNKNOWN ;
2022-01-25 19:42:46 +00:00
mi . driver_name = driver_name ;
2022-02-06 17:49:55 +00:00
2022-01-25 19:42:46 +00:00
auto meter = createMeter ( & mi ) ;
2022-02-06 17:49:55 +00:00
2022-01-25 19:42:46 +00:00
bool match = false ;
string id ;
bool h = meter - > handleTelegram ( about , input_frame , simulated , & id , & match , & t ) ;
2022-02-06 17:49:55 +00:00
2022-01-25 19:42:46 +00:00
if ( ! match )
{
2022-02-06 17:49:55 +00:00
debug ( " no match! \n " ) ;
2021-12-07 22:56:29 +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
2021-12-27 13:30:54 +00:00
// decryption key was used. But it is ok if analyzing....
2022-02-06 17:49:55 +00:00
debug ( " Newly created meter (%s %s %s) did not handle telegram! \n " ,
2022-01-08 17:52:06 +00:00
meter - > name ( ) . c_str ( ) , meter - > idsc ( ) . c_str ( ) , meter - > driverName ( ) . str ( ) . c_str ( ) ) ;
2021-12-07 22:56:29 +00:00
}
else
{
int l = 0 ;
int u = 0 ;
2022-01-25 19:42:46 +00:00
t . analyzeParse ( OutputFormat : : NONE , & l , & u ) ;
2022-02-06 17:49:55 +00:00
if ( analyze_verbose_ & & only = = " " ) printf ( " (verbose) new %02d/%02d %s \n " , u , l , driver_name . c_str ( ) ) ;
if ( u > * best_understood )
2021-12-07 22:56:29 +00:00
{
2022-02-06 17:49:55 +00:00
* best_understood = u ;
* best_length = l ;
best_driver = ndr . name ( ) . str ( ) ;
if ( analyze_verbose_ & & only = = " " ) printf ( " (verbose) new best so far: %s %02d/%02d \n " , best_driver . c_str ( ) , u , l ) ;
2021-12-07 22:56:29 +00:00
}
}
}
2022-02-06 17:49:55 +00:00
return best_driver ;
}
void analyzeTelegram ( AboutTelegram & about , vector < uchar > & input_frame , bool simulated )
{
Telegram t ;
t . about = about ;
bool ok = t . parseHeader ( input_frame ) ;
if ( simulated ) t . markAsSimulated ( ) ;
t . markAsBeingAnalyzed ( ) ;
if ( ! ok )
{
printf ( " Could not even analyze header, giving up. \n " ) ;
return ;
}
if ( meter_templates_ . size ( ) > 0 )
{
error ( " You cannot specify a meter quadruple when analyzing. \n "
" Instead use --analyze=<format>:<driver>:<key> \n "
" where <formt> <driver> <key> are all optional. \n "
" E.g. --analyze=terminal:multical21:001122334455667788001122334455667788 \n "
" --analyze=001122334455667788001122334455667788 \n "
" --analyze \n " ) ;
}
// Overwrite the id with the id from the telegram to be analyzed.
MeterInfo mi ;
mi . key = analyze_key_ ;
mi . ids . clear ( ) ;
mi . ids . push_back ( t . ids . back ( ) ) ;
mi . idsc = t . ids . back ( ) ;
// This will be the driver that will actually decode and print with.
string using_driver = " " ;
int using_length = 0 ;
int using_understood = 0 ;
// Driver that understands most of the telegram content.
string best_driver = " " ;
int best_length = 0 ;
int best_understood = 0 ;
int old_best_length = 0 ;
int old_best_understood = 0 ;
string best_old_driver = findBestOldStyleDriver ( mi , & old_best_length , & old_best_understood , t , about , input_frame , simulated , " " ) ;
int new_best_length = 0 ;
int new_best_understood = 0 ;
string best_new_driver = findBestNewStyleDriver ( mi , & new_best_length , & new_best_understood , t , about , input_frame , simulated , " " ) ;
mi . driver = MeterDriver : : UNKNOWN ;
mi . driver_name = DriverName ( " " ) ;
// Use the existing mapping from mfct/media/version to driver.
DriverInfo auto_di = pickMeterDriver ( & t ) ;
string auto_driver = auto_di . name ( ) . str ( ) ;
if ( auto_driver = = " " )
{
auto_driver = toString ( auto_di . driver ( ) ) ;
2022-02-09 13:52:13 +00:00
if ( auto_driver = = " unknown " )
{
auto_driver = " " ;
}
2022-02-06 17:49:55 +00:00
}
// Will be non-empty if an explicit driver has been selected.
string force_driver = analyze_driver_ ;
int force_length = 0 ;
int force_understood = 0 ;
2022-02-09 13:52:13 +00:00
// If an auto driver is found and no other driver has been forced, use the auto driver.
if ( force_driver = = " " & & auto_driver ! = " " )
2022-02-06 17:49:55 +00:00
{
force_driver = auto_driver ;
}
if ( force_driver ! = " " )
2021-12-07 22:56:29 +00:00
{
2022-02-06 17:49:55 +00:00
using_driver = findBestOldStyleDriver ( mi , & force_length , & force_understood , t , about , input_frame , simulated ,
force_driver ) ;
if ( using_driver ! = " " )
2021-12-07 23:55:50 +00:00
{
2022-02-06 17:49:55 +00:00
mi . driver = toMeterDriver ( using_driver ) ;
mi . driver_name = DriverName ( " " ) ;
2022-03-27 10:09:18 +00:00
using_driver + = " (driver should be upgraded) " ;
2021-12-07 23:55:50 +00:00
}
else
{
2022-02-06 17:49:55 +00:00
using_driver = findBestNewStyleDriver ( mi , & force_length , & force_understood , t , about , input_frame , simulated ,
force_driver ) ;
mi . driver_name = using_driver ;
mi . driver = MeterDriver : : UNKNOWN ;
2021-12-07 23:55:50 +00:00
}
2022-02-06 17:49:55 +00:00
using_length = force_length ;
using_understood = force_understood ;
}
2021-12-07 23:55:50 +00:00
2022-02-06 17:49:55 +00:00
if ( old_best_understood > new_best_understood )
{
best_length = old_best_length ;
best_understood = old_best_understood ;
2022-03-27 10:09:18 +00:00
best_driver = best_old_driver + " (driver should be upgraded) " ;
2022-02-06 17:49:55 +00:00
if ( using_driver = = " " )
{
mi . driver = toMeterDriver ( best_old_driver ) ;
mi . driver_name = DriverName ( " " ) ;
2022-03-27 10:09:18 +00:00
using_driver = best_driver ;
2022-02-06 17:49:55 +00:00
using_length = best_length ;
using_understood = best_understood ;
}
2021-12-07 22:56:29 +00:00
}
2022-02-09 13:30:06 +00:00
else if ( new_best_understood > = old_best_understood )
2021-12-07 22:56:29 +00:00
{
2022-02-06 17:49:55 +00:00
best_length = new_best_length ;
best_understood = new_best_understood ;
best_driver = best_new_driver ;
if ( using_driver = = " " )
{
mi . driver_name = best_new_driver ;
mi . driver = MeterDriver : : UNKNOWN ;
using_driver = best_new_driver ;
using_length = best_length ;
using_understood = best_understood ;
}
2021-12-07 22:56:29 +00:00
}
2022-02-06 17:49:55 +00:00
auto meter = createMeter ( & mi ) ;
bool match = false ;
string id ;
meter - > handleTelegram ( about , input_frame , simulated , & id , & match , & t ) ;
int u = 0 ;
int l = 0 ;
string output = t . analyzeParse ( analyze_format_ , & u , & l ) ;
string hr , fields , json ;
vector < string > envs , more_json , selected_fields ;
meter - > printMeter ( & t , & hr , & fields , ' \t ' , & json ,
& envs , & more_json , & selected_fields , true ) ;
if ( auto_driver = = " " )
{
auto_driver = " not found! " ;
}
printf ( " Auto driver : %s \n " , auto_driver . c_str ( ) ) ;
printf ( " Best driver : %s %02d/%02d \n " , best_driver . c_str ( ) , best_understood , best_length ) ;
printf ( " Using driver : %s %02d/%02d \n " , using_driver . c_str ( ) , using_understood , using_length ) ;
printf ( " %s \n " , output . c_str ( ) ) ;
printf ( " %s \n " , json . c_str ( ) ) ;
2021-12-07 18:51:26 +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 ,
2022-01-02 12:08:36 +00:00
string 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 ) ;
}
2021-08-01 22:22:13 +00:00
for ( auto j : mi . extra_constant_fields ) {
addExtraConstantField ( j ) ;
2019-10-20 17:19:17 +00:00
}
2018-03-05 10:29:25 +00:00
}
2022-01-06 17:28:22 +00:00
MeterCommonImplementation : : MeterCommonImplementation ( MeterInfo & mi ,
DriverInfo & di ) :
2022-01-08 17:52:06 +00:00
type_ ( di . type ( ) ) , driver_ ( di . name ( ) . str ( ) ) , driver_name_ ( di . name ( ) ) , bus_ ( mi . bus ) , name_ ( mi . name )
2022-01-06 17:28:22 +00:00
{
ids_ = mi . ids ;
idsc_ = toIdsCommaSeparated ( ids_ ) ;
if ( mi . key . length ( ) > 0 )
{
hex2bin ( mi . key , & meter_keys_ . confidentiality_key ) ;
}
for ( auto s : mi . shells ) {
addShell ( s ) ;
}
for ( auto j : mi . extra_constant_fields ) {
addExtraConstantField ( j ) ;
}
2022-01-08 17:52:06 +00:00
link_modes_ . unionLinkModeSet ( di . linkModes ( ) ) ;
2022-01-06 17:28:22 +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 ) ;
}
2021-08-01 22:22:13 +00:00
void MeterCommonImplementation : : addExtraConstantField ( string ecf )
2019-10-20 17:19:17 +00:00
{
2021-08-01 22:22:13 +00:00
extra_constant_fields_ . push_back ( ecf ) ;
2019-10-20 17:19:17 +00:00
}
2019-05-21 12:19:54 +00:00
vector < string > & MeterCommonImplementation : : shellCmdlines ( )
{
return shell_cmdlines_ ;
}
2021-08-01 22:22:13 +00:00
vector < string > & MeterCommonImplementation : : meterExtraConstantFields ( )
2019-10-20 17:19:17 +00:00
{
2021-08-01 22:22:13 +00:00
return extra_constant_fields_ ;
2019-10-20 17:19:17 +00:00
}
2021-03-08 07:40:48 +00:00
MeterDriver MeterCommonImplementation : : driver ( )
2018-03-05 10:29:25 +00:00
{
2022-01-02 12:08:36 +00:00
return toMeterDriver ( driver_ ) ;
2022-01-06 17:28:22 +00:00
}
2022-01-02 12:08:36 +00:00
2022-01-06 17:28:22 +00:00
DriverName MeterCommonImplementation : : driverName ( )
{
2022-01-08 17:52:06 +00:00
if ( driver_name_ . str ( ) = = " " )
{
return DriverName ( toString ( driver ( ) ) ) ;
}
2022-01-06 17:28:22 +00:00
return driver_name_ ;
2018-03-05 10:29:25 +00:00
}
2021-12-31 11:27:57 +00:00
void MeterCommonImplementation : : setMeterType ( MeterType mt )
{
type_ = mt ;
}
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 ,
2022-04-11 12:01:54 +00:00
function < double ( Unit ) > getValueFunc , string help , PrintProperties pprops )
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 ) ;
2022-01-06 17:28:22 +00:00
prints_ . push_back (
FieldInfo ( vname ,
vquantity ,
defaultUnitForQuantity ( vquantity ) ,
NoDifVifKey ,
VifScaling : : Auto ,
2022-04-17 09:23:45 +00:00
MeasurementType : : Instantaneous ,
2022-04-16 15:47:20 +00:00
VIFRange : : None ,
2022-01-06 17:28:22 +00:00
AnyStorageNr ,
AnyTariffNr ,
IndexNr ( 1 ) ,
help ,
2022-04-11 12:01:54 +00:00
pprops ,
2022-01-06 17:28:22 +00:00
field_name ,
getValueFunc ,
NULL ,
NULL ,
NULL ,
2022-01-08 14:50:15 +00:00
NoLookup
2022-01-06 17:28:22 +00:00
) ) ;
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 ,
2022-04-11 12:01:54 +00:00
function < double ( Unit ) > getValueFunc , string help , PrintProperties pprops )
2020-04-22 20:38:40 +00:00
{
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 ) ;
2022-01-06 17:28:22 +00:00
prints_ . push_back (
FieldInfo ( vname ,
vquantity ,
unit ,
NoDifVifKey ,
VifScaling : : Auto ,
2022-04-17 09:23:45 +00:00
MeasurementType : : Instantaneous ,
2022-04-16 15:47:20 +00:00
VIFRange : : None ,
2022-01-06 17:28:22 +00:00
AnyStorageNr ,
AnyTariffNr ,
IndexNr ( 1 ) ,
help ,
2022-04-11 12:01:54 +00:00
pprops ,
2022-01-06 17:28:22 +00:00
field_name ,
getValueFunc ,
NULL ,
NULL ,
NULL ,
2022-01-08 14:50:15 +00:00
NoLookup
2022-01-06 17:28:22 +00:00
) ) ;
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 ,
2022-04-11 12:01:54 +00:00
string help , PrintProperties pprops )
2019-05-04 17:56:17 +00:00
{
2022-01-06 17:28:22 +00:00
prints_ . push_back (
FieldInfo ( vname ,
vquantity ,
defaultUnitForQuantity ( vquantity ) ,
NoDifVifKey ,
VifScaling : : Auto ,
2022-04-17 09:23:45 +00:00
MeasurementType : : Instantaneous ,
2022-04-16 15:47:20 +00:00
VIFRange : : None ,
2022-01-06 17:28:22 +00:00
AnyStorageNr ,
AnyTariffNr ,
IndexNr ( 1 ) ,
help ,
2022-04-11 12:01:54 +00:00
pprops ,
2022-01-06 17:28:22 +00:00
vname ,
NULL ,
getValueFunc ,
NULL ,
NULL ,
2022-01-08 14:50:15 +00:00
NoLookup
2022-01-06 17:28:22 +00:00
) ) ;
}
2022-04-16 18:23:37 +00:00
void MeterCommonImplementation : : addNumericFieldWithExtractor (
2022-01-06 17:28:22 +00:00
string vname ,
Quantity vquantity ,
DifVifKey dif_vif_key ,
VifScaling vif_scaling ,
MeasurementType mt ,
2022-04-16 15:47:20 +00:00
VIFRange vi ,
2022-01-06 17:28:22 +00:00
StorageNr s ,
TariffNr t ,
IndexNr i ,
2022-04-11 12:01:54 +00:00
PrintProperties print_properties ,
2022-01-06 17:28:22 +00:00
string help ,
function < void ( Unit , double ) > setValueFunc ,
function < double ( Unit ) > getValueFunc )
{
string default_unit = unitToStringLowerCase ( defaultUnitForQuantity ( vquantity ) ) ;
string field_name = vname + " _ " + default_unit ;
fields_ . push_back ( field_name ) ;
prints_ . push_back (
FieldInfo ( vname ,
vquantity ,
defaultUnitForQuantity ( vquantity ) ,
dif_vif_key ,
vif_scaling ,
mt ,
vi ,
s ,
t ,
i ,
help ,
2022-04-11 12:01:54 +00:00
print_properties ,
2022-01-06 17:28:22 +00:00
field_name ,
getValueFunc ,
NULL ,
setValueFunc ,
NULL ,
2022-01-08 14:50:15 +00:00
NoLookup
2022-01-06 17:28:22 +00:00
) ) ;
}
2022-04-16 18:23:37 +00:00
void MeterCommonImplementation : : addNumericField (
2022-02-06 17:49:55 +00:00
string vname ,
Quantity vquantity ,
2022-04-11 12:01:54 +00:00
PrintProperties print_properties ,
2022-02-06 17:49:55 +00:00
string help ,
function < void ( Unit , double ) > setValueFunc ,
function < double ( Unit ) > getValueFunc )
{
string default_unit = unitToStringLowerCase ( defaultUnitForQuantity ( vquantity ) ) ;
string field_name = vname + " _ " + default_unit ;
fields_ . push_back ( field_name ) ;
prints_ . push_back (
FieldInfo ( vname ,
vquantity ,
defaultUnitForQuantity ( vquantity ) ,
DifVifKey ( " " ) ,
VifScaling : : None ,
2022-04-17 09:23:45 +00:00
MeasurementType : : Instantaneous ,
2022-04-16 15:47:20 +00:00
VIFRange : : Volume ,
2022-02-06 17:49:55 +00:00
StorageNr ( 0 ) ,
TariffNr ( 0 ) ,
0 ,
help ,
2022-04-11 12:01:54 +00:00
print_properties ,
2022-02-06 17:49:55 +00:00
field_name ,
getValueFunc ,
NULL ,
setValueFunc ,
NULL ,
NoLookup
) ) ;
}
2022-01-06 17:28:22 +00:00
void MeterCommonImplementation : : addStringFieldWithExtractor (
string vname ,
Quantity vquantity ,
DifVifKey dif_vif_key ,
MeasurementType mt ,
2022-04-16 15:47:20 +00:00
VIFRange vi ,
2022-01-06 17:28:22 +00:00
StorageNr s ,
TariffNr t ,
IndexNr i ,
2022-04-11 12:01:54 +00:00
PrintProperties print_properties ,
2022-01-06 17:28:22 +00:00
string help ,
function < void ( string ) > setValueFunc ,
function < string ( ) > getValueFunc )
{
string default_unit = unitToStringLowerCase ( defaultUnitForQuantity ( vquantity ) ) ;
string field_name = vname + " _ " + default_unit ;
fields_ . push_back ( field_name ) ;
prints_ . push_back (
FieldInfo ( vname ,
vquantity ,
defaultUnitForQuantity ( vquantity ) ,
dif_vif_key ,
VifScaling : : None ,
mt ,
vi ,
s ,
t ,
i ,
help ,
2022-04-11 12:01:54 +00:00
print_properties ,
2022-01-06 17:28:22 +00:00
field_name ,
NULL ,
getValueFunc ,
NULL ,
setValueFunc ,
2022-01-08 14:50:15 +00:00
NoLookup
) ) ;
}
void MeterCommonImplementation : : addStringFieldWithExtractorAndLookup (
string vname ,
Quantity vquantity ,
DifVifKey dif_vif_key ,
MeasurementType mt ,
2022-04-16 15:47:20 +00:00
VIFRange vi ,
2022-01-08 14:50:15 +00:00
StorageNr s ,
TariffNr t ,
IndexNr i ,
2022-04-11 12:01:54 +00:00
PrintProperties print_properties ,
2022-01-08 14:50:15 +00:00
string help ,
function < void ( string ) > setValueFunc ,
function < string ( ) > getValueFunc ,
Translate : : Lookup lookup )
{
string default_unit = unitToStringLowerCase ( defaultUnitForQuantity ( vquantity ) ) ;
string field_name = vname + " _ " + default_unit ;
fields_ . push_back ( field_name ) ;
prints_ . push_back (
FieldInfo ( vname ,
vquantity ,
defaultUnitForQuantity ( vquantity ) ,
dif_vif_key ,
VifScaling : : None ,
mt ,
vi ,
s ,
t ,
i ,
help ,
2022-04-11 12:01:54 +00:00
print_properties ,
2022-01-08 14:50:15 +00:00
field_name ,
NULL ,
getValueFunc ,
NULL ,
setValueFunc ,
lookup
2022-01-06 17:28:22 +00:00
) ) ;
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_ ;
}
2022-01-06 17:28:22 +00:00
vector < FieldInfo > MeterCommonImplementation : : prints ( )
2020-09-08 20:11:32 +00:00
{
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-12 17:36:26 +00:00
snprintf ( ut , sizeof ( ut ) - 1 , " %lu " , datetime_of_update_ ) ;
2021-08-01 16:24:19 +00:00
return string ( ut ) ;
}
2022-02-17 18:59:51 +00:00
bool needsPolling ( MeterDriver d , DriverName & dn )
2021-03-08 16:14:03 +00:00
{
2022-02-17 18:59:51 +00:00
if ( d ! = MeterDriver : : UNKNOWN & & d ! = MeterDriver : : AUTO )
{
2021-03-08 16:14:03 +00:00
# define X(mname,linkmodes,info,driver,cname) if (d == MeterDriver::driver && 0 != ((linkmodes) & MBUS_bit)) return true;
LIST_OF_METERS
# undef X
return false ;
2022-02-17 18:59:51 +00:00
}
if ( all_registered_drivers_ . count ( dn . str ( ) ) = = 0 ) return false ;
DriverInfo & di = all_registered_drivers_ [ dn . str ( ) ] ;
// Return true for MBUS,S2,C2,T2 meters. Currently only mbus is implemented.
return di . linkModes ( ) . has ( LinkMode : : MBUS ) ;
2021-03-08 16:14:03 +00:00
}
2022-01-06 17:28:22 +00:00
const char * toString ( MeterType type )
{
# define X(tname) if (type == MeterType::tname) return #tname;
LIST_OF_METER_TYPES
# undef X
return " unknown " ;
}
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 " ;
}
2022-01-06 17:28:22 +00:00
string toString ( DriverInfo & di )
{
2022-01-08 21:04:18 +00:00
if ( di . driver ( ) ! = MeterDriver : : UNKNOWN & &
di . driver ( ) ! = MeterDriver : : AUTO ) return toString ( di . driver ( ) ) ;
2022-01-06 17:28:22 +00:00
return di . name ( ) . str ( ) ;
}
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 ( ) ;
2021-12-07 22:56:29 +00:00
if ( t - > beingAnalyzed ( ) = = false )
2021-01-06 12:46:12 +00:00
{
2021-12-07 22:56:29 +00:00
warning ( " (meter) %s: meter detection did not match the selected driver %s! correct driver is: %s \n "
" (meter) Not printing this warning again for id: %02x%02x%02x%02x mfct: (%s) %s (0x%02x) type: %s (0x%02x) ver: 0x%02x \n " ,
name . c_str ( ) ,
toString ( driver ) . c_str ( ) ,
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 " ) ;
}
2021-01-06 12:46:12 +00:00
}
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
}
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
2022-01-06 17:28:22 +00:00
string concatAllFields ( Meter * m , Telegram * t , char c , vector < FieldInfo > & prints , vector < Unit > & cs , bool hr ,
2021-08-08 15:16:52 +00:00
vector < string > * extra_constant_fields )
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 ;
}
2022-01-06 17:28:22 +00:00
for ( FieldInfo p : prints )
2019-05-04 14:37:35 +00:00
{
2022-04-11 12:01:54 +00:00
if ( p . printProperties ( ) . hasFIELD ( ) )
2019-05-04 14:37:35 +00:00
{
2022-01-06 17:28:22 +00:00
if ( p . hasGetValueDouble ( ) )
2019-05-04 17:56:17 +00:00
{
2022-01-06 17:28:22 +00:00
Unit u = replaceWithConversionUnit ( p . defaultUnit ( ) , cs ) ;
2019-05-04 17:56:17 +00:00
double v = p . getValueDouble ( u ) ;
if ( hr ) {
s + = valueToString ( v , u ) ;
s + = " " + unitToStringHR ( u ) ;
} else {
s + = to_string ( v ) ;
}
}
2022-01-06 17:28:22 +00:00
if ( p . hasGetValueString ( ) )
2019-05-04 17:56:17 +00:00
{
s + = p . getValueString ( ) ;
2019-05-04 14:37:35 +00:00
}
s + = c ;
}
}
s + = m - > datetimeOfUpdateHumanReadable ( ) ;
return s ;
}
2021-08-08 14:50:25 +00:00
string findField ( string key , vector < string > * extra_constant_fields )
2020-05-09 21:43:30 +00:00
{
2021-08-08 14:50:25 +00:00
key = key + " = " ;
for ( string ecf : * extra_constant_fields )
2020-05-09 21:43:30 +00:00
{
2021-08-08 14:50:25 +00:00
if ( startsWith ( ecf , key ) )
{
return ecf . substr ( key . length ( ) ) ;
}
2020-05-09 21:43:30 +00:00
}
2021-08-08 14:50:25 +00:00
return " " ;
}
2020-05-09 21:43:30 +00:00
2021-08-08 14:50:25 +00:00
// Is the desired field one of the fields common to all meters and telegrams?
bool checkCommonField ( string * buf , string field , Meter * m , Telegram * t , char c )
{
if ( field = = " name " )
2020-05-09 21:43:30 +00:00
{
2021-08-08 14:50:25 +00:00
* buf + = m - > name ( ) + c ;
return true ;
}
if ( field = = " id " )
{
* buf + = t - > ids . back ( ) + c ;
return true ;
}
if ( field = = " timestamp " )
{
* buf + = m - > datetimeOfUpdateHumanReadable ( ) + c ;
return true ;
}
if ( field = = " timestamp_lt " )
{
* buf + = m - > datetimeOfUpdateHumanReadable ( ) + c ;
return true ;
}
if ( field = = " timestamp_utc " )
{
* buf + = m - > datetimeOfUpdateRobot ( ) + c ;
return true ;
}
if ( field = = " timestamp_ut " )
{
* buf + = m - > unixTimestampOfUpdate ( ) + c ;
return true ;
}
if ( field = = " device " )
{
* buf + = t - > about . device + c ;
return true ;
}
if ( field = = " rssi_dbm " )
{
* buf + = to_string ( t - > about . rssi_dbm ) + c ;
return true ;
}
return false ;
}
// Is the desired field one of the meter printable fields?
bool checkPrintableField ( string * buf , string field , Meter * m , Telegram * t , char c ,
2022-01-06 17:28:22 +00:00
vector < FieldInfo > & prints , vector < Unit > & cs )
2021-08-08 14:50:25 +00:00
{
2022-01-06 17:28:22 +00:00
for ( FieldInfo p : prints )
2021-08-08 14:50:25 +00:00
{
2022-01-06 17:28:22 +00:00
if ( p . hasGetValueString ( ) )
2020-11-12 08:56:51 +00:00
{
2021-08-08 14:50:25 +00:00
// Strings are simply just print them.
2022-01-06 17:28:22 +00:00
if ( field = = p . vname ( ) )
2021-08-08 14:50:25 +00:00
{
* buf + = p . getValueString ( ) + c ;
return true ;
}
2020-11-12 08:56:51 +00:00
}
2022-01-06 17:28:22 +00:00
else if ( p . hasGetValueDouble ( ) )
2020-05-09 21:43:30 +00:00
{
2021-08-08 14:50:25 +00:00
// Doubles have to be converted into the proper unit.
2022-01-06 17:28:22 +00:00
string default_unit = unitToStringLowerCase ( p . defaultUnit ( ) ) ;
string var = p . vname ( ) + " _ " + default_unit ;
2021-08-08 14:50:25 +00:00
if ( field = = var )
2020-05-09 21:43:30 +00:00
{
2021-08-08 14:50:25 +00:00
// Default unit.
2022-01-06 17:28:22 +00:00
* buf + = valueToString ( p . getValueDouble ( p . defaultUnit ( ) ) , p . defaultUnit ( ) ) + c ;
2021-08-08 14:50:25 +00:00
return true ;
2020-05-09 21:43:30 +00:00
}
2021-08-08 14:50:25 +00:00
else
2020-05-09 21:43:30 +00:00
{
2021-08-08 14:50:25 +00:00
// Added conversion unit.
2022-01-06 17:28:22 +00:00
Unit u = replaceWithConversionUnit ( p . defaultUnit ( ) , cs ) ;
if ( u ! = p . defaultUnit ( ) )
2020-05-09 21:43:30 +00:00
{
2021-08-08 14:50:25 +00:00
string unit = unitToStringLowerCase ( u ) ;
2022-01-06 17:28:22 +00:00
string var = p . vname ( ) + " _ " + unit ;
2021-08-08 14:50:25 +00:00
if ( field = = var )
2020-05-09 21:43:30 +00:00
{
2021-08-08 14:50:25 +00:00
* buf + = valueToString ( p . getValueDouble ( u ) , u ) + c ;
return true ;
2020-05-09 21:43:30 +00:00
}
}
}
}
2021-08-08 14:50:25 +00:00
}
return false ;
}
// Is the desired field one of the constant fields?
bool checkConstantField ( string * buf , string field , char c , vector < string > * extra_constant_fields )
{
// Ok, lets look for extra constant fields and print any such static information.
string v = findField ( field , extra_constant_fields ) ;
if ( v ! = " " )
{
* buf + = v + c ;
return true ;
}
return false ;
}
2022-01-06 17:28:22 +00:00
string concatFields ( Meter * m , Telegram * t , char c , vector < FieldInfo > & prints , vector < Unit > & cs , bool hr ,
2021-08-08 15:16:52 +00:00
vector < string > * selected_fields , vector < string > * extra_constant_fields )
2021-08-08 14:50:25 +00:00
{
if ( selected_fields = = NULL | | selected_fields - > size ( ) = = 0 )
{
2021-08-08 15:16:52 +00:00
return concatAllFields ( m , t , c , prints , cs , hr , extra_constant_fields ) ;
2021-08-08 14:50:25 +00:00
}
string buf = " " ;
for ( string field : * selected_fields )
{
bool handled = checkCommonField ( & buf , field , m , t , c ) ;
if ( handled ) continue ;
handled = checkPrintableField ( & buf , field , m , t , c , prints , cs ) ;
if ( handled ) continue ;
handled = checkConstantField ( & buf , field , c , extra_constant_fields ) ;
if ( handled ) continue ;
2020-11-12 08:56:51 +00:00
if ( ! handled )
{
2021-08-08 14:50:25 +00:00
buf + = " ? " + field + " ? " + c ;
2020-11-12 08:56:51 +00:00
}
2020-05-09 21:43:30 +00:00
}
2021-08-08 14:50:25 +00:00
if ( buf . back ( ) = = c ) buf . pop_back ( ) ;
return buf ;
2020-05-09 21:43:30 +00:00
}
2021-12-07 22:56:29 +00:00
bool MeterCommonImplementation : : handleTelegram ( AboutTelegram & about , vector < uchar > input_frame ,
bool simulated , string * ids , bool * id_match , Telegram * out_analyzed )
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-12-07 22:56:29 +00:00
if ( out_analyzed ! = NULL ) t . markAsBeingAnalyzed ( ) ;
2020-09-08 18:50:51 +00:00
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 )
{
2022-02-06 17:49:55 +00:00
if ( out_analyzed ! = NULL ) * out_analyzed = t ;
2020-02-03 17:39:07 +00:00
// 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
2022-01-06 17:28:22 +00:00
// Invoke standardized field extractors!
processFieldExtractors ( & t ) ;
// Invoke tailor made 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 ) ;
2021-12-07 22:56:29 +00:00
if ( out_analyzed ! = NULL ) * out_analyzed = t ;
2020-01-27 08:29:40 +00:00
return true ;
2019-05-04 15:38:10 +00:00
}
2022-01-06 17:28:22 +00:00
void MeterCommonImplementation : : processFieldExtractors ( Telegram * t )
2021-12-31 11:27:57 +00:00
{
2022-04-17 10:47:06 +00:00
/*
for ( auto i = t - > dv_entries_ordered . begin ( ) ; i ! = t - > dv_entries_ordered . end ( ) ; + + i )
{
printf ( " GURKA %s \n " , ( * i ) - > dif_vif_key . str ( ) . c_str ( ) ) ;
}
*/
for ( FieldInfo & fi : prints_ )
2021-12-31 11:27:57 +00:00
{
2022-01-06 17:28:22 +00:00
fi . performExtraction ( this , t ) ;
}
}
void MeterCommonImplementation : : processContent ( Telegram * t )
{
}
FieldInfo * MeterCommonImplementation : : findFieldInfo ( string vname )
{
FieldInfo * found = NULL ;
for ( FieldInfo & p : prints_ )
{
if ( p . vname ( ) = = vname )
2021-12-31 11:27:57 +00:00
{
found = & p ;
break ;
}
}
2022-01-06 17:28:22 +00:00
return found ;
}
string MeterCommonImplementation : : renderJsonOnlyDefaultUnit ( string vname )
{
FieldInfo * fi = findFieldInfo ( vname ) ;
if ( fi = = NULL ) return " unknown field " + vname ;
return fi - > renderJsonOnlyDefaultUnit ( ) ;
}
string FieldInfo : : renderJsonOnlyDefaultUnit ( )
{
return renderJson ( NULL ) ;
}
2021-12-31 11:27:57 +00:00
2022-01-06 17:28:22 +00:00
string FieldInfo : : renderJsonText ( )
{
return renderJson ( NULL ) ;
2021-12-31 11:27:57 +00:00
}
2022-01-06 17:28:22 +00:00
string FieldInfo : : renderJson ( vector < Unit > * conversions )
2021-12-31 11:27:57 +00:00
{
string s ;
2022-01-06 17:28:22 +00:00
string default_unit = unitToStringLowerCase ( defaultUnit ( ) ) ;
string var = vname ( ) ;
if ( hasGetValueString ( ) )
{
s + = " \" " + var + " \" : \" " + getValueString ( ) + " \" " ;
2021-12-31 11:27:57 +00:00
}
2022-01-06 17:28:22 +00:00
else if ( hasGetValueDouble ( ) )
{
s + = " \" " + var + " _ " + default_unit + " \" : " + valueToString ( getValueDouble ( defaultUnit ( ) ) , defaultUnit ( ) ) ;
2021-12-31 11:27:57 +00:00
2022-01-06 17:28:22 +00:00
if ( conversions ! = NULL )
2021-12-31 11:27:57 +00:00
{
2022-01-06 17:28:22 +00:00
Unit u = replaceWithConversionUnit ( defaultUnit ( ) , * conversions ) ;
if ( u ! = defaultUnit ( ) )
{
string unit = unitToStringLowerCase ( u ) ;
// Appending extra conversion unit.
s + = " , \" " + var + " _ " + unit + " \" : " + valueToString ( getValueDouble ( u ) , u ) ;
}
2021-12-31 11:27:57 +00:00
}
}
2022-01-06 17:28:22 +00:00
else
{
s = " ? " ;
}
2021-12-31 11:27:57 +00:00
return s ;
}
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 ,
2021-08-01 22:22:13 +00:00
vector < string > * extra_constant_fields ,
2022-01-25 19:10:38 +00:00
vector < string > * selected_fields ,
bool pretty_print_json )
2019-05-04 14:37:35 +00:00
{
2021-08-08 15:16:52 +00:00
* human_readable = concatFields ( this , t , ' \t ' , prints_ , conversions_ , true , selected_fields , extra_constant_fields ) ;
* fields = concatFields ( this , t , separator , prints_ , conversions_ , false , selected_fields , extra_constant_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
}
2022-01-25 19:10:38 +00:00
string indent = " " ;
string newline = " " ;
if ( pretty_print_json )
{
indent = " " ;
newline = " \n " ;
}
2019-05-04 14:37:35 +00:00
string s ;
2022-01-25 19:10:38 +00:00
s + = " { " + newline ;
s + = indent + " \" media \" : \" " + media + " \" , " + newline ;
s + = indent + " \" meter \" : \" " + meterDriver ( ) + " \" , " + newline ;
s + = indent + " \" name \" : \" " + name ( ) + " \" , " + newline ;
2021-01-30 16:58:00 +00:00
if ( t - > ids . size ( ) > 0 )
{
2022-01-25 19:10:38 +00:00
s + = indent + " \" id \" : \" " + t - > ids . back ( ) + " \" , " + newline ;
2021-01-30 16:58:00 +00:00
}
else
{
2022-01-25 19:10:38 +00:00
s + = indent + " \" id \" : \" \" , " + newline ;
2021-01-30 16:58:00 +00:00
}
2022-01-06 17:28:22 +00:00
for ( FieldInfo & p : prints_ )
2019-05-04 14:37:35 +00:00
{
2022-04-11 12:01:54 +00:00
if ( p . printProperties ( ) . hasJSON ( ) )
2019-05-04 14:37:35 +00:00
{
2022-01-25 19:10:38 +00:00
s + = indent + p . renderJson ( & conversions ( ) ) + " , " + newline ;
2019-05-04 14:37:35 +00:00
}
}
2022-01-25 19:10:38 +00:00
s + = indent + " \" timestamp \" : \" " + datetimeOfUpdateRobot ( ) + " \" " ;
2021-08-08 15:16:52 +00:00
2020-10-14 18:59:14 +00:00
if ( t - > about . device ! = " " )
{
2022-01-25 19:10:38 +00:00
s + = " , " + newline ;
s + = indent + " \" device \" : \" " + t - > about . device + " \" , " + newline ;
s + = indent + " \" rssi_dbm \" : " + to_string ( t - > about . rssi_dbm ) ;
2020-10-14 18:59:14 +00:00
}
2021-08-01 22:22:13 +00:00
for ( string extra_field : meterExtraConstantFields ( ) )
2019-10-20 17:19:17 +00:00
{
2022-01-25 19:10:38 +00:00
s + = " , " + newline ;
s + = indent + makeQuotedJson ( extra_field ) ;
2019-10-20 17:19:17 +00:00
}
2021-08-01 22:22:13 +00:00
for ( string extra_field : * extra_constant_fields )
2019-10-20 17:19:17 +00:00
{
2022-01-25 19:10:38 +00:00
s + = " , " + newline ;
s + = indent + makeQuotedJson ( extra_field ) ;
2019-10-20 17:19:17 +00:00
}
2022-01-25 19:10:38 +00:00
s + = newline ;
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
2022-01-06 17:28:22 +00:00
for ( FieldInfo p : prints_ )
2019-05-04 14:37:35 +00:00
{
2022-04-11 12:01:54 +00:00
if ( p . printProperties ( ) . hasJSON ( ) )
2019-05-04 14:37:35 +00:00
{
2022-01-06 17:28:22 +00:00
string default_unit = unitToStringUpperCase ( p . defaultUnit ( ) ) ;
string var = p . vname ( ) ;
2019-05-04 18:52:05 +00:00
std : : transform ( var . begin ( ) , var . end ( ) , var . begin ( ) , : : toupper ) ;
2022-01-06 17:28:22 +00:00
if ( p . hasGetValueString ( ) ) {
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
}
2022-01-06 17:28:22 +00:00
if ( p . hasGetValueDouble ( ) ) {
string envvar = " METER_ " + var + " _ " + default_unit + " = " + valueToString ( p . getValueDouble ( p . defaultUnit ( ) ) , p . defaultUnit ( ) ) ;
2019-05-04 14:37:35 +00:00
envs - > push_back ( envvar ) ;
2019-05-04 17:56:17 +00:00
2022-01-06 17:28:22 +00:00
Unit u = replaceWithConversionUnit ( p . defaultUnit ( ) , conversions_ ) ;
if ( u ! = p . defaultUnit ( ) )
2019-05-04 17:56:17 +00:00
{
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"
2021-08-01 22:22:13 +00:00
for ( string add_json : meterExtraConstantFields ( ) )
2019-10-20 17:19:17 +00:00
{
envs - > push_back ( string ( " METER_ " ) + add_json ) ;
}
2021-08-01 22:22:13 +00:00
for ( string extra_field : * extra_constant_fields )
2019-10-20 17:19:17 +00:00
{
2021-08-01 22:22:13 +00:00
envs - > push_back ( string ( " METER_ " ) + extra_field ) ;
2019-10-20 17:19:17 +00:00
}
2019-05-04 14:37:35 +00:00
}
2019-05-04 15:38:10 +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
2022-01-06 17:28:22 +00:00
for ( auto & p : all_registered_drivers_ )
{
if ( p . second . detect ( manufacturer , media , version ) )
{
drivers - > push_back ( p . second . name ( ) . str ( ) ) ;
}
}
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
2022-01-06 17:28:22 +00:00
for ( auto & p : all_registered_drivers_ )
{
if ( p . second . detect ( manufacturer , media , version ) )
{
return true ;
}
}
2020-09-04 09:31:49 +00:00
return false ;
2019-05-04 17:56:17 +00:00
}
2021-02-20 10:08:23 +00:00
2022-01-25 20:21:17 +00:00
bool isMeterDriverReasonableForMedia ( MeterDriver type , string driver_name , int media )
{
if ( media = = 0x37 ) return false ; // Skip converter meter side since they do not give any useful information.
2022-03-27 10:09:18 +00:00
if ( driver_name = = " " )
{
2022-02-08 19:51:13 +00:00
# define X(TY,MA,ME,VE) { if (type == MeterDriver::TY && isCloseEnough(media, ME)) { return true; }}
2022-01-25 20:21:17 +00:00
METER_DETECTION
# undef X
2022-03-27 10:09:18 +00:00
}
2022-01-25 20:21:17 +00:00
for ( auto & p : all_registered_drivers_ )
{
2022-03-27 10:09:18 +00:00
if ( p . first = = driver_name & & p . second . isValidMedia ( media ) )
2022-01-25 20:21:17 +00:00
{
return true ;
}
}
return false ;
}
2022-01-06 17:28:22 +00:00
vector < DriverInfo > & allRegisteredDrivers ( )
{
return all_registered_drivers_list_ ;
}
DriverInfo 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
2022-01-06 17:28:22 +00:00
for ( auto & p : all_registered_drivers_ )
{
if ( p . second . detect ( manufacturer , media , version ) )
{
return p . second ;
}
}
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 " ;
2022-01-06 17:28:22 +00:00
if ( all_registered_drivers_ . count ( mi - > driver_name . str ( ) ) ! = 0 )
{
DriverInfo & di = all_registered_drivers_ [ mi - > driver_name . str ( ) ] ;
shared_ptr < Meter > newm = di . construct ( * mi ) ;
newm - > addConversions ( mi - > conversions ) ;
verbose ( " (meter) constructed \" %s \" \" %s \" \" %s \" %s \n " ,
mi - > name . c_str ( ) ,
di . name ( ) . str ( ) . c_str ( ) ,
mi - > idsc . c_str ( ) ,
keymsg ) ;
return newm ;
}
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
2022-01-06 17:28:22 +00:00
bool is_driver_extras ( string t , MeterDriver * out_driver , DriverName * out_driver_name , string * out_extras )
2021-03-13 17:35:47 +00:00
{
// piigth(jump=foo)
// multical21
2022-01-06 17:28:22 +00:00
DriverInfo di ;
2021-03-13 17:35:47 +00:00
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 )
{
2022-01-06 17:28:22 +00:00
if ( lookupDriverInfo ( t , & di ) )
{
* out_driver_name = di . name ( ) ;
// We found a registered driver.
* out_driver = MeterDriver : : AUTO ; // To go away!
* out_extras = " " ;
return true ;
}
2021-03-13 17:35:47 +00:00
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 ) ;
2022-01-06 17:28:22 +00:00
bool found = lookupDriverInfo ( type , & di ) ;
2021-03-13 17:35:47 +00:00
MeterDriver md = toMeterDriver ( type ) ;
2022-01-06 17:28:22 +00:00
if ( found )
{
* out_driver_name = di . name ( ) ;
}
else
{
if ( md = = MeterDriver : : UNKNOWN ) return false ;
}
2021-03-13 17:35:47 +00:00
* 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 ;
2022-04-11 16:53:50 +00:00
// The : colon is forbidden inside the parts.
2021-03-13 17:35:47 +00:00
vector < string > parts = splitString ( d , ' : ' ) ;
// Example piigth:MAIN:2400
// multical21:c1
// telco:BUS2:c2
// driver ( extras ) : bus_alias : bps : linkmodes
for ( auto & p : parts )
{
2022-01-06 17:28:22 +00:00
if ( ! driverextras_checked & & is_driver_extras ( p , & driver , & driver_name , & extras ) )
2021-03-13 17:35:47 +00:00
{
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 ) ;
}
2022-01-06 17:28:22 +00:00
void FieldInfo : : performExtraction ( Meter * m , Telegram * t )
{
2022-04-16 18:23:37 +00:00
if ( xuantity_ = = Quantity : : Text )
2022-01-06 17:28:22 +00:00
{
2022-04-16 18:23:37 +00:00
extractString ( m , t ) ;
2022-01-06 17:28:22 +00:00
}
2022-04-16 18:23:37 +00:00
else
2022-01-06 17:28:22 +00:00
{
2022-04-16 18:23:37 +00:00
extractNumeric ( m , t ) ;
2022-01-06 17:28:22 +00:00
}
}
2022-01-08 17:52:06 +00:00
DriverName MeterInfo : : driverName ( )
{
if ( driver_name . str ( ) = = " " )
{
return DriverName ( toString ( driver ) ) ;
}
return driver_name ;
}
2022-04-16 18:23:37 +00:00
bool FieldInfo : : extractNumeric ( Meter * m , Telegram * t )
{
bool found = false ;
string key = difVifKey ( ) . str ( ) ;
int offset { } ;
if ( key = = " " )
{
// Search for key.
bool ok = findKeyWithNr ( measurementType ( ) ,
vifRange ( ) ,
storageNr ( ) . intValue ( ) ,
tariffNr ( ) . intValue ( ) ,
indexNr ( ) . intValue ( ) ,
& key ,
2022-04-16 19:26:51 +00:00
& t - > dv_entries ) ;
2022-04-16 18:23:37 +00:00
if ( ! ok ) return false ;
}
double extracted_double_value = NAN ;
2022-04-16 19:26:51 +00:00
if ( extractDVdouble ( & t - > dv_entries ,
2022-04-16 18:23:37 +00:00
key ,
& offset ,
& extracted_double_value ,
vifScaling ( ) = = VifScaling : : Auto | |
vifScaling ( ) = = VifScaling : : AutoSigned ,
vifScaling ( ) = = VifScaling : : NoneSigned | |
vifScaling ( ) = = VifScaling : : AutoSigned ) )
{
Unit decoded_unit = defaultUnit ( ) ;
if ( vifRange ( ) ! = VIFRange : : Any & &
vifRange ( ) ! = VIFRange : : AnyVolumeVIF & &
vifRange ( ) ! = VIFRange : : AnyEnergyVIF & &
vifRange ( ) ! = VIFRange : : AnyPowerVIF & &
vifRange ( ) ! = VIFRange : : None )
{
decoded_unit = toDefaultUnit ( vifRange ( ) ) ;
}
setValueDouble ( decoded_unit , extracted_double_value ) ;
t - > addMoreExplanation ( offset , renderJson ( & m - > conversions ( ) ) ) ;
found = true ;
}
return found ;
}
bool FieldInfo : : extractString ( Meter * m , Telegram * t )
{
bool found = false ;
string key = difVifKey ( ) . str ( ) ;
int offset { } ;
if ( key = = " " )
{
// Search for key.
bool ok = findKeyWithNr ( measurementType ( ) ,
vifRange ( ) ,
storageNr ( ) . intValue ( ) ,
tariffNr ( ) . intValue ( ) ,
indexNr ( ) . intValue ( ) ,
& key ,
2022-04-16 19:26:51 +00:00
& t - > dv_entries ) ;
2022-04-16 18:23:37 +00:00
if ( ! ok ) return false ;
}
uint64_t extracted_bits { } ;
if ( lookup_ . hasLookups ( ) )
{
2022-04-16 19:26:51 +00:00
if ( extractDVlong ( & t - > dv_entries , key , & offset , & extracted_bits ) )
2022-04-16 18:23:37 +00:00
{
string translated_bits = lookup ( ) . translate ( extracted_bits ) ;
setValueString ( translated_bits ) ;
t - > addMoreExplanation ( offset , renderJsonText ( ) ) ;
found = true ;
}
}
else if ( vifRange ( ) = = VIFRange : : DateTime )
{
struct tm datetime ;
2022-04-16 19:26:51 +00:00
extractDVdate ( & t - > dv_entries , key , & offset , & datetime ) ;
2022-04-16 18:23:37 +00:00
string extracted_device_date_time = strdatetime ( & datetime ) ;
setValueString ( extracted_device_date_time ) ;
t - > addMoreExplanation ( offset , renderJsonText ( ) ) ;
found = true ;
}
else if ( vifRange ( ) = = VIFRange : : Date )
{
struct tm date ;
2022-04-16 19:26:51 +00:00
extractDVdate ( & t - > dv_entries , key , & offset , & date ) ;
2022-04-16 18:23:37 +00:00
string extracted_device_date = strdate ( & date ) ;
setValueString ( extracted_device_date ) ;
t - > addMoreExplanation ( offset , renderJsonText ( ) ) ;
found = true ;
}
else if ( vifRange ( ) = = VIFRange : : EnhancedIdentification | |
vifRange ( ) = = VIFRange : : FabricationNo )
{
string extracted_id ;
2022-04-16 19:26:51 +00:00
extractDVReadableString ( & t - > dv_entries , key , & offset , & extracted_id ) ;
2022-04-16 18:23:37 +00:00
setValueString ( extracted_id ) ;
t - > addMoreExplanation ( offset , renderJsonText ( ) ) ;
found = true ;
}
else
{
error ( " Internal error: Cannot extract text string from vif %s in %s:%d \n " ,
toString ( vifRange ( ) ) ,
__FILE__ , __LINE__ ) ;
}
return found ;
}