2021-09-23 10:06:12 +00:00
|
|
|
/*
|
2022-02-12 14:28:17 +00:00
|
|
|
Copyright (C) 2021 Fredrik Öhrström (gpl-3.0-or-later)
|
2021-09-23 10:06:12 +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/>.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include"dvparser.h"
|
|
|
|
#include"meters.h"
|
|
|
|
#include"meters_common_implementation.h"
|
|
|
|
#include"wmbus.h"
|
|
|
|
#include"wmbus_utils.h"
|
|
|
|
#include"util.h"
|
|
|
|
|
2021-09-25 10:03:44 +00:00
|
|
|
#include<algorithm>
|
|
|
|
|
2021-09-23 10:06:12 +00:00
|
|
|
using namespace std;
|
|
|
|
|
2021-12-31 15:47:29 +00:00
|
|
|
struct MeterUnismart : public virtual MeterCommonImplementation {
|
2021-09-23 10:06:12 +00:00
|
|
|
MeterUnismart(MeterInfo &mi);
|
|
|
|
|
|
|
|
// Total gas counted through the meter
|
|
|
|
double totalGasConsumption(Unit u);
|
|
|
|
bool hasTotalGasConsumption();
|
2021-09-25 10:03:44 +00:00
|
|
|
// Consumption at the beginning of this month.
|
|
|
|
double targetGasConsumption(Unit u);
|
2021-09-23 10:06:12 +00:00
|
|
|
|
|
|
|
private:
|
|
|
|
void processContent(Telegram *t);
|
|
|
|
|
2021-09-25 10:03:44 +00:00
|
|
|
string fabrication_no_;
|
|
|
|
string total_date_time_;
|
2021-09-23 10:06:12 +00:00
|
|
|
double total_gas_consumption_m3_ {};
|
2021-09-25 10:03:44 +00:00
|
|
|
string target_date_time_;
|
|
|
|
double target_gas_consumption_m3_ {};
|
|
|
|
string version_;
|
|
|
|
string device_date_time_;
|
|
|
|
|
|
|
|
string supplier_info_;
|
|
|
|
string status_;
|
|
|
|
string parameter_set_;
|
2021-09-26 08:18:36 +00:00
|
|
|
uint8_t other_;
|
2021-09-23 10:06:12 +00:00
|
|
|
};
|
|
|
|
|
2021-12-31 11:27:57 +00:00
|
|
|
shared_ptr<Meter> createUnismart(MeterInfo &mi)
|
2021-09-23 10:06:12 +00:00
|
|
|
{
|
2021-12-31 15:47:29 +00:00
|
|
|
return shared_ptr<Meter>(new MeterUnismart(mi));
|
2021-09-23 10:06:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
MeterUnismart::MeterUnismart(MeterInfo &mi) :
|
2022-01-02 12:08:36 +00:00
|
|
|
MeterCommonImplementation(mi, "unismart")
|
2021-09-23 10:06:12 +00:00
|
|
|
{
|
2021-12-31 15:47:29 +00:00
|
|
|
setMeterType(MeterType::GasMeter);
|
|
|
|
|
2021-09-23 10:06:12 +00:00
|
|
|
setExpectedTPLSecurityMode(TPLSecurityMode::AES_CBC_IV);
|
|
|
|
|
|
|
|
addLinkMode(LinkMode::T1);
|
|
|
|
|
2021-09-25 10:03:44 +00:00
|
|
|
addPrint("fabrication_no", Quantity::Text,
|
|
|
|
[&](){ return fabrication_no_; },
|
|
|
|
"Static fabrication no information.",
|
2022-04-11 12:01:54 +00:00
|
|
|
PrintProperty::JSON);
|
2021-09-25 10:03:44 +00:00
|
|
|
|
|
|
|
addPrint("total_date_time", Quantity::Text,
|
|
|
|
[&](){ return total_date_time_; },
|
|
|
|
"Timestamp for this total measurement.",
|
2022-04-11 12:01:54 +00:00
|
|
|
PrintProperty::JSON);
|
2021-09-25 10:03:44 +00:00
|
|
|
|
2021-09-23 10:06:12 +00:00
|
|
|
addPrint("total", Quantity::Volume,
|
|
|
|
[&](Unit u){ return totalGasConsumption(u); },
|
|
|
|
"The total gas consumption recorded by this meter.",
|
2022-04-11 12:01:54 +00:00
|
|
|
PrintProperty::FIELD | PrintProperty::JSON);
|
2021-09-23 10:06:12 +00:00
|
|
|
|
2021-09-25 10:03:44 +00:00
|
|
|
addPrint("target_date_time", Quantity::Text,
|
|
|
|
[&](){ return target_date_time_; },
|
|
|
|
"Timestamp for gas consumption recorded at the beginning of this month.",
|
2022-04-11 12:01:54 +00:00
|
|
|
PrintProperty::JSON);
|
2021-09-25 10:03:44 +00:00
|
|
|
|
|
|
|
addPrint("target", Quantity::Volume,
|
|
|
|
[&](Unit u){ return targetGasConsumption(u); },
|
|
|
|
"The total gas consumption recorded by this meter at the beginning of this month.",
|
2022-04-11 12:01:54 +00:00
|
|
|
PrintProperty::FIELD | PrintProperty::JSON);
|
2021-09-25 10:03:44 +00:00
|
|
|
|
|
|
|
addPrint("version", Quantity::Text,
|
|
|
|
[&](){ return version_; },
|
|
|
|
"Model/version a reported by meter.",
|
2022-04-11 12:01:54 +00:00
|
|
|
PrintProperty::JSON);
|
2021-09-25 10:03:44 +00:00
|
|
|
|
|
|
|
addPrint("device_date_time", Quantity::Text,
|
|
|
|
[&](){ return device_date_time_; },
|
|
|
|
"Device date time? Seems to be the same as total date time.",
|
2022-04-11 12:01:54 +00:00
|
|
|
PrintProperty::JSON);
|
2021-09-25 10:03:44 +00:00
|
|
|
|
|
|
|
addPrint("suppler_info", Quantity::Text,
|
|
|
|
[&](){ return supplier_info_; },
|
|
|
|
"?",
|
2022-04-11 12:01:54 +00:00
|
|
|
PrintProperty::JSON);
|
2021-09-25 10:03:44 +00:00
|
|
|
|
|
|
|
addPrint("status", Quantity::Text,
|
|
|
|
[&](){ return status_; },
|
|
|
|
"?",
|
2022-04-11 12:01:54 +00:00
|
|
|
PrintProperty::JSON);
|
2021-09-25 10:03:44 +00:00
|
|
|
|
|
|
|
addPrint("parameter_set", Quantity::Text,
|
|
|
|
[&](){ return parameter_set_; },
|
|
|
|
"?",
|
2022-04-11 12:01:54 +00:00
|
|
|
PrintProperty::JSON);
|
2021-09-25 10:03:44 +00:00
|
|
|
|
2021-09-26 08:18:36 +00:00
|
|
|
addPrint("other", Quantity::Counter,
|
|
|
|
[&](Unit u){ return other_; },
|
2021-09-25 10:03:44 +00:00
|
|
|
"?",
|
2022-04-11 12:01:54 +00:00
|
|
|
PrintProperty::JSON);
|
2021-09-25 10:03:44 +00:00
|
|
|
|
2021-09-23 10:06:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void MeterUnismart::processContent(Telegram *t)
|
|
|
|
{
|
2021-09-25 09:01:51 +00:00
|
|
|
/*
|
|
|
|
(unismart) 11: 0C dif (8 digit BCD Instantaneous value)
|
|
|
|
(unismart) 12: 78 vif (Fabrication no)
|
|
|
|
(unismart) 13: 96221603
|
|
|
|
(unismart) 17: 04 dif (32 Bit Integer/Binary Instantaneous value)
|
|
|
|
(unismart) 18: 6D vif (Date and time type)
|
|
|
|
(unismart) 19: 122DAF29
|
|
|
|
(unismart) 1d: 0C dif (8 digit BCD Instantaneous value)
|
|
|
|
(unismart) 1e: 94 vif (Volume 10⁻² m³)
|
|
|
|
(unismart) 1f: 3A vife (uncorrected meter unit)
|
|
|
|
(unismart) 20: * 00170900 total consumption (917.000000 m3)
|
|
|
|
(unismart) 24: 44 dif (32 Bit Integer/Binary Instantaneous value storagenr=1)
|
|
|
|
(unismart) 25: 6D vif (Date and time type)
|
|
|
|
(unismart) 26: 0026A129
|
|
|
|
(unismart) 2a: 4C dif (8 digit BCD Instantaneous value storagenr=1)
|
|
|
|
(unismart) 2b: 94 vif (Volume 10⁻² m³)
|
|
|
|
(unismart) 2c: 3A vife (uncorrected meter unit)
|
|
|
|
(unismart) 2d: 32110900
|
|
|
|
(unismart) 31: 01 dif (8 Bit Integer/Binary Instantaneous value)
|
|
|
|
(unismart) 32: FD vif (Second extension FD of VIF-codes)
|
|
|
|
(unismart) 33: 67 vife (Special supplier information)
|
|
|
|
(unismart) 34: 00
|
|
|
|
(unismart) 35: 02 dif (16 Bit Integer/Binary Instantaneous value)
|
|
|
|
(unismart) 36: FD vif (Second extension FD of VIF-codes)
|
|
|
|
(unismart) 37: 74 vife (Reserved)
|
|
|
|
(unismart) 38: F00C
|
|
|
|
(unismart) 3a: 0D dif (variable length Instantaneous value)
|
|
|
|
(unismart) 3b: FD vif (Second extension FD of VIF-codes)
|
|
|
|
(unismart) 3c: 0C vife (Model/Version)
|
|
|
|
(unismart) 3d: 06 varlen=6
|
|
|
|
(unismart) 3e: 554747342020
|
|
|
|
(unismart) 44: 01 dif (8 Bit Integer/Binary Instantaneous value)
|
|
|
|
(unismart) 45: FD vif (Second extension FD of VIF-codes)
|
|
|
|
(unismart) 46: 0B vife (Parameter set identification)
|
|
|
|
(unismart) 47: 02
|
|
|
|
(unismart) 48: 01 dif (8 Bit Integer/Binary Instantaneous value)
|
|
|
|
(unismart) 49: 7F vif (Manufacturer specific)
|
|
|
|
(unismart) 4a: 14
|
|
|
|
(unismart) 4b: 06 dif (48 Bit Integer/Binary Instantaneous value)
|
|
|
|
(unismart) 4c: 6D vif (Date and time type)
|
|
|
|
(unismart) 4d: 1E120DAF296D
|
|
|
|
(unismart) 53: 2F skip
|
|
|
|
(unismart) 54: 2F skip
|
|
|
|
(unismart) 55: 2F skip
|
|
|
|
(unismart) 56: 2F skip
|
|
|
|
(unismart) 57: 2F skip
|
|
|
|
(unismart) 58: 2F skip
|
|
|
|
(unismart) 59: 2F skip
|
|
|
|
(unismart) 5a: 2F skip
|
|
|
|
(unismart) 5b: 2F skip
|
|
|
|
(unismart) 5c: 2F skip
|
|
|
|
(unismart) 5d: 2F skip
|
|
|
|
(unismart) 5e: 2F skip
|
|
|
|
|
|
|
|
*/
|
2021-09-25 10:03:44 +00:00
|
|
|
int offset;
|
2021-09-23 10:06:12 +00:00
|
|
|
string key;
|
|
|
|
|
2021-09-25 10:03:44 +00:00
|
|
|
uint64_t v {};
|
|
|
|
if (extractDVlong(&t->values, "0C78", &offset, &v))
|
|
|
|
{
|
|
|
|
fabrication_no_ = to_string(v);
|
|
|
|
t->addMoreExplanation(offset, " fabrication no (%zu)", v);
|
|
|
|
}
|
|
|
|
|
2022-04-16 15:47:20 +00:00
|
|
|
if (findKey(MeasurementType::Instantaneous, VIFRange::DateTime, 0, 0, &key, &t->values)) {
|
2021-09-25 10:03:44 +00:00
|
|
|
struct tm datetime;
|
|
|
|
extractDVdate(&t->values, key, &offset, &datetime);
|
|
|
|
total_date_time_ = strdatetime(&datetime);
|
|
|
|
t->addMoreExplanation(offset, " total datetime (%s)", total_date_time_.c_str());
|
|
|
|
}
|
|
|
|
|
2022-04-16 15:47:20 +00:00
|
|
|
if (findKey(MeasurementType::Instantaneous, VIFRange::Volume, 0, 0, &key, &t->values))
|
2021-09-25 10:03:44 +00:00
|
|
|
{
|
2021-09-23 10:06:12 +00:00
|
|
|
extractDVdouble(&t->values, key, &offset, &total_gas_consumption_m3_);
|
|
|
|
t->addMoreExplanation(offset, " total consumption (%f m3)", total_gas_consumption_m3_);
|
|
|
|
}
|
2021-09-25 10:03:44 +00:00
|
|
|
|
2022-04-16 15:47:20 +00:00
|
|
|
if (findKey(MeasurementType::Instantaneous, VIFRange::DateTime, 1, 0, &key, &t->values)) {
|
2021-09-25 10:03:44 +00:00
|
|
|
struct tm datetime;
|
|
|
|
extractDVdate(&t->values, key, &offset, &datetime);
|
|
|
|
target_date_time_ = strdatetime(&datetime);
|
|
|
|
t->addMoreExplanation(offset, " target datetime (%s)", target_date_time_.c_str());
|
|
|
|
}
|
|
|
|
|
2022-04-16 15:47:20 +00:00
|
|
|
if (findKey(MeasurementType::Instantaneous, VIFRange::Volume, 1, 0, &key, &t->values))
|
2021-09-25 10:03:44 +00:00
|
|
|
{
|
|
|
|
extractDVdouble(&t->values, key, &offset, &target_gas_consumption_m3_);
|
|
|
|
t->addMoreExplanation(offset, " target consumption (%f m3)", target_gas_consumption_m3_);
|
|
|
|
}
|
|
|
|
|
|
|
|
string tmp;
|
2022-02-06 10:21:15 +00:00
|
|
|
if (extractDVHexString(&t->values, "0DFD0C", &offset, &tmp))
|
2021-09-25 10:03:44 +00:00
|
|
|
{
|
|
|
|
vector<uchar> bin;
|
|
|
|
hex2bin(tmp, &bin);
|
|
|
|
version_ = safeString(bin);
|
|
|
|
trimWhitespace(&version_);
|
|
|
|
t->addMoreExplanation(offset, " version (%s)", version_.c_str());
|
|
|
|
}
|
|
|
|
|
|
|
|
struct tm datetime;
|
|
|
|
if (extractDVdate(&t->values, "066D", &offset, &datetime))
|
|
|
|
{
|
|
|
|
device_date_time_ = strdatetime(&datetime);
|
|
|
|
t->addMoreExplanation(offset, " device datetime (%s)", device_date_time_.c_str());
|
|
|
|
}
|
|
|
|
|
2022-02-06 10:21:15 +00:00
|
|
|
if (extractDVHexString(&t->values, "01FD67", &offset, &supplier_info_))
|
2021-09-25 10:03:44 +00:00
|
|
|
{
|
|
|
|
t->addMoreExplanation(offset, " suppler info (%s)", supplier_info_.c_str());
|
|
|
|
}
|
|
|
|
|
2022-02-06 10:21:15 +00:00
|
|
|
if (extractDVHexString(&t->values, "02FD74", &offset, &status_))
|
2021-09-25 10:03:44 +00:00
|
|
|
{
|
|
|
|
t->addMoreExplanation(offset, " status (%s)", status_.c_str());
|
|
|
|
}
|
|
|
|
|
2022-02-06 10:21:15 +00:00
|
|
|
if (extractDVHexString(&t->values, "01FD0B", &offset, ¶meter_set_))
|
2021-09-25 10:03:44 +00:00
|
|
|
{
|
|
|
|
t->addMoreExplanation(offset, " parameter set (%s)", parameter_set_.c_str());
|
|
|
|
}
|
|
|
|
|
2021-09-26 08:18:36 +00:00
|
|
|
if (extractDVuint8(&t->values, "017F", &offset, &other_))
|
2021-09-25 10:03:44 +00:00
|
|
|
{
|
2021-09-26 08:18:36 +00:00
|
|
|
t->addMoreExplanation(offset, " status2 (%d)", other_);
|
2021-09-25 10:03:44 +00:00
|
|
|
}
|
2021-09-23 10:06:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
double MeterUnismart::totalGasConsumption(Unit u)
|
|
|
|
{
|
|
|
|
assertQuantity(u, Quantity::Volume);
|
|
|
|
return convert(total_gas_consumption_m3_, Unit::M3, u);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool MeterUnismart::hasTotalGasConsumption()
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
2021-09-25 10:03:44 +00:00
|
|
|
|
|
|
|
double MeterUnismart::targetGasConsumption(Unit u)
|
|
|
|
{
|
|
|
|
assertQuantity(u, Quantity::Volume);
|
|
|
|
return convert(target_gas_consumption_m3_, Unit::M3, u);
|
|
|
|
}
|