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"
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
{
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 ) ;
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 ( )
{
return meters_ . size ( ) ! = 0 ;
}
2020-10-14 18:59:14 +00:00
bool handleTelegram ( AboutTelegram & about , vector < uchar > data , bool simulated )
2020-09-07 08:36:39 +00:00
{
2020-09-08 12:55:01 +00:00
if ( ! hasMeters ( ) )
{
if ( on_telegram_ )
{
2020-10-14 18:59:14 +00:00
on_telegram_ ( about , data ) ;
2020-09-08 12:55:01 +00:00
}
return true ;
}
2020-09-07 08:36:39 +00:00
bool handled = false ;
2021-01-30 16:58:00 +00:00
string ids ;
2020-09-07 08:36:39 +00:00
for ( auto & m : meters_ )
{
2021-01-30 16:58:00 +00:00
bool h = m - > handleTelegram ( about , data , simulated , & ids ) ;
2020-09-07 08:36:39 +00:00
if ( h ) handled = true ;
}
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 ;
}
2020-09-07 08:36:39 +00:00
~ MeterManagerImplementation ( ) { }
private :
2020-09-21 19:55:21 +00:00
vector < shared_ptr < Meter > > meters_ ;
2020-10-14 18:59:14 +00:00
function < void ( AboutTelegram & , vector < uchar > ) > on_telegram_ ;
2020-09-07 08:36:39 +00:00
} ;
2020-09-21 19:55:21 +00:00
shared_ptr < MeterManager > createMeterManager ( )
2020-09-07 08:36:39 +00:00
{
2020-09-21 19:55:21 +00:00
return shared_ptr < MeterManager > ( new MeterManagerImplementation ) ;
2020-09-07 08:36:39 +00:00
}
2020-08-30 19:33:48 +00:00
MeterCommonImplementation : : MeterCommonImplementation ( MeterInfo & mi ,
2020-09-04 09:31:49 +00:00
MeterType type ) :
2020-08-30 19:33:48 +00:00
type_ ( type ) , name_ ( mi . name )
2018-03-05 10:29:25 +00:00
{
2019-09-16 14:32:24 +00:00
ids_ = splitMatchExpressions ( mi . id ) ;
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
}
2020-10-04 20:52:05 +00:00
/*if (bus->type() == DEVICE_SIMULATION)
2020-02-06 12:14:46 +00:00
{
meter_keys_ . simulation = true ;
2020-09-04 11:17:09 +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_ ;
}
2018-03-05 10:29:25 +00:00
MeterType MeterCommonImplementation : : type ( )
{
return type_ ;
}
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
}
2019-03-05 20:19:05 +00:00
vector < string > MeterCommonImplementation : : ids ( )
{
return ids_ ;
2018-03-05 10:29:25 +00:00
}
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 ) ;
}
2019-05-04 08:52:18 +00:00
string toMeterName ( MeterType mt )
{
# define X(mname,link,info,type,cname) if (mt == MeterType::type) return #mname;
LIST_OF_METERS
# undef X
return " unknown " ;
}
MeterType toMeterType ( string & t )
{
2019-06-06 15:28:20 +00:00
# define X(mname,linkmodes,info,type,cname) if (t == #mname) return MeterType::type;
2019-05-04 08:52:18 +00:00
LIST_OF_METERS
# undef X
return MeterType : : 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
bool MeterCommonImplementation : : isTelegramForMe ( Telegram * t )
{
2021-01-30 16:58:00 +00:00
debug ( " (meter) %s: for me? %s \n " , name_ . c_str ( ) , t - > idsc . c_str ( ) ) ;
2019-03-05 20:19:05 +00:00
2021-01-24 12:35:44 +00:00
bool used_wildcard = false ;
2021-01-30 16:58:00 +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.
2019-03-05 20:19:05 +00:00
debug ( " (meter) %s: not for me: not my id \n " , name_ . c_str ( ) ) ;
return false ;
}
2021-01-24 12:35:44 +00:00
bool valid_driver = isMeterDriverValid ( type_ , t - > dll_mfct , t - > dll_type , t - > dll_version ) ;
2021-01-30 16:58:00 +00:00
if ( ! valid_driver & & t - > tpl_id_found )
{
valid_driver = isMeterDriverValid ( type_ , t - > tpl_mfct , t - > tpl_type , t - > tpl_version ) ;
}
2021-01-24 12:35:44 +00:00
if ( ! valid_driver )
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 " ,
name_ . c_str ( ) ,
toMeterName ( type ( ) ) . 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 " ) ;
}
2020-09-07 08:36:39 +00:00
}
2019-03-05 17:38:54 +00:00
}
2019-03-05 20:19:05 +00:00
debug ( " (meter) %s: yes for me \n " , name_ . c_str ( ) ) ;
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 ;
}
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 ;
}
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-01-30 16:58:00 +00:00
bool MeterCommonImplementation : : handleTelegram ( AboutTelegram & about , vector < uchar > input_frame , bool simulated , string * ids )
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
2020-01-27 08:29:40 +00:00
if ( ! ok | | ! isTelegramForMe ( & t ) )
{
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-01-30 16:58:00 +00:00
verbose ( " (meter) %s %s handling telegram from %s \n " , name ( ) . c_str ( ) , meterName ( ) . 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 ] ;
snprintf ( log_prefix , 255 , " (%s) log " , meterName ( ) . 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 ] ;
snprintf ( log_prefix , 255 , " (%s) " , meterName ( ) . 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 ,
vector < string > * selected_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-01-30 16:58:00 +00:00
string mfct ;
if ( t - > tpl_id_found )
{
mfct = mediaTypeJSON ( t - > tpl_type , t - > tpl_mfct ) ;
}
else if ( t - > ell_id_found )
{
mfct = mediaTypeJSON ( t - > ell_type , t - > ell_mfct ) ;
}
else
{
mfct = mediaTypeJSON ( t - > dll_type , t - > dll_mfct ) ;
}
2019-05-04 14:37:35 +00:00
string s ;
s + = " { " ;
2021-01-30 16:58:00 +00:00
s + = " \" media \" : \" " + mfct + " \" , " ;
2019-05-04 14:37:35 +00:00
s + = " \" meter \" : \" " + meterName ( ) + " \" , " ;
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 ) ;
envs - > push_back ( string ( " METER_TYPE= " ) + meterName ( ) ) ;
2019-11-21 08:25:39 +00:00
envs - > push_back ( string ( " METER_NAME= " ) + name ( ) ) ;
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= " ) ) ;
}
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
}
}
}
envs - > push_back ( string ( " METER_TIMESTAMP= " ) + datetimeOfUpdateRobot ( ) ) ;
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
}
2020-09-04 09:31:49 +00:00
void detectMeterDriver ( int manufacturer , int media , int version , vector < string > * drivers )
2019-05-04 17:56:17 +00:00
{
2020-09-04 09:31:49 +00:00
# define X(TY,MA,ME,VE) { if (manufacturer == MA && (media == ME || ME == -1) && (version == VE || VE == -1)) { drivers->push_back(toMeterName(MeterType::TY)); }}
METER_DETECTION
# undef X
2019-05-04 17:56:17 +00:00
}
2020-09-04 09:31:49 +00:00
bool isMeterDriverValid ( MeterType type , int manufacturer , int media , int version )
2019-05-04 17:56:17 +00:00
{
2020-09-04 09:31:49 +00:00
# define X(TY,MA,ME,VE) { if (type == MeterType::TY && manufacturer == MA && (media == ME || ME == -1) && (version == VE || VE == -1)) { return true; }}
METER_DETECTION
# undef X
return false ;
2019-05-04 17:56:17 +00:00
}
2021-02-20 10:08:23 +00:00
shared_ptr < Meter > createMeter ( Configuration * config , MeterType type , MeterInfo * mi )
{
shared_ptr < Meter > newm ;
const char * keymsg = ( mi - > key [ 0 ] = = 0 ) ? " not-encrypted " : " encrypted " ;
switch ( type )
{
# define X(mname,link,info,type,cname) \
case MeterType : : type : \
{ \
newm = create # # cname ( * mi ) ; \
newm - > addConversions ( config - > conversions ) ; \
verbose ( " (main) configured \" %s \" \" " # mname " \" \" %s \" %s \n " , \
mi - > name . c_str ( ) , mi - > id . c_str ( ) , keymsg ) ; \
return newm ; \
} \
break ;
LIST_OF_METERS
# undef X
case MeterType : : UNKNOWN :
error ( " No such meter type \" %s \" \n " , mi - > type . c_str ( ) ) ;
break ;
}
return newm ;
}