Merge pull request #11773 from dashevchenko/efento_fix

Fixed pulse counter telemetry calculation
This commit is contained in:
Andrew Shvayka 2024-10-02 11:21:32 +03:00 committed by GitHub
commit 97243cad43
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 160 additions and 28 deletions

View File

@ -46,12 +46,15 @@ import org.thingsboard.server.transport.coap.CoapTransportContext;
import org.thingsboard.server.transport.coap.callback.CoapDeviceAuthCallback;
import org.thingsboard.server.transport.coap.callback.CoapEfentoCallback;
import org.thingsboard.server.transport.coap.efento.utils.CoapEfentoUtils;
import org.thingsboard.server.transport.coap.efento.utils.PulseCounterType;
import java.nio.ByteBuffer;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.TreeMap;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
@ -62,6 +65,19 @@ import static org.thingsboard.server.transport.coap.CoapTransportService.CONFIGU
import static org.thingsboard.server.transport.coap.CoapTransportService.CURRENT_TIMESTAMP;
import static org.thingsboard.server.transport.coap.CoapTransportService.DEVICE_INFO;
import static org.thingsboard.server.transport.coap.CoapTransportService.MEASUREMENTS;
import static org.thingsboard.server.transport.coap.efento.utils.CoapEfentoUtils.BREATH_VOC_METADATA_FACTOR;
import static org.thingsboard.server.transport.coap.efento.utils.CoapEfentoUtils.CO2_EQUIVALENT_METADATA_FACTOR;
import static org.thingsboard.server.transport.coap.efento.utils.CoapEfentoUtils.CO2_GAS_METADATA_FACTOR;
import static org.thingsboard.server.transport.coap.efento.utils.CoapEfentoUtils.ELEC_METER_ACC_MAJOR_METADATA_FACTOR;
import static org.thingsboard.server.transport.coap.efento.utils.CoapEfentoUtils.ELEC_METER_ACC_MINOR_METADATA_FACTOR;
import static org.thingsboard.server.transport.coap.efento.utils.CoapEfentoUtils.IAQ_METADATA_FACTOR;
import static org.thingsboard.server.transport.coap.efento.utils.CoapEfentoUtils.PULSE_CNT_ACC_MAJOR_METADATA_FACTOR;
import static org.thingsboard.server.transport.coap.efento.utils.CoapEfentoUtils.PULSE_CNT_ACC_MINOR_METADATA_FACTOR;
import static org.thingsboard.server.transport.coap.efento.utils.CoapEfentoUtils.PULSE_CNT_ACC_WIDE_MAJOR_METADATA_FACTOR;
import static org.thingsboard.server.transport.coap.efento.utils.CoapEfentoUtils.PULSE_CNT_ACC_WIDE_MINOR_METADATA_FACTOR;
import static org.thingsboard.server.transport.coap.efento.utils.CoapEfentoUtils.STATIC_IAQ_METADATA_FACTOR;
import static org.thingsboard.server.transport.coap.efento.utils.CoapEfentoUtils.WATER_METER_ACC_MAJOR_METADATA_FACTOR;
import static org.thingsboard.server.transport.coap.efento.utils.CoapEfentoUtils.WATER_METER_ACC_MINOR_METADATA_FACTOR;
import static org.thingsboard.server.transport.coap.efento.utils.CoapEfentoUtils.isBinarySensor;
import static org.thingsboard.server.transport.coap.efento.utils.CoapEfentoUtils.isSensorError;
@ -280,6 +296,12 @@ public class CoapEfentoTransportResource extends AbstractCoapTransportResource {
}
}
valuesMap.values().forEach(jsonObject -> {
for (PulseCounterType pulseCounterType : PulseCounterType.values()) {
calculatePulseCounterTotalValue(jsonObject, pulseCounterType);
}
});
if (CollectionUtils.isEmpty(valuesMap)) {
throw new IllegalStateException("[" + sessionId + "]: Failed to collect Efento measurements, reason, values map is empty!");
}
@ -312,7 +334,7 @@ public class CoapEfentoTransportResource extends AbstractCoapTransportResource {
values.addProperty("pulse_cnt_" + channelNumber, (double) (startPoint + sampleOffset));
break;
case MEASUREMENT_TYPE_IAQ:
values.addProperty("iaq_" + channelNumber, (startPoint + sampleOffset));
addPulseCounterProperties(values, "iaq_", channelNumber, startPoint + sampleOffset, IAQ_METADATA_FACTOR);
break;
case MEASUREMENT_TYPE_ELECTRICITY_METER:
values.addProperty("watt_hour_" + channelNumber, (double) (startPoint + sampleOffset));
@ -330,22 +352,25 @@ public class CoapEfentoTransportResource extends AbstractCoapTransportResource {
values.addProperty("distance_mm_" + channelNumber, (double) (startPoint + sampleOffset));
break;
case MEASUREMENT_TYPE_WATER_METER_ACC_MINOR:
values.addProperty("acc_counter_water_minor_" + channelNumber, (double) (startPoint + sampleOffset));
addPulseCounterProperties(values, "water_cnt_acc_minor_", channelNumber, startPoint + sampleOffset, WATER_METER_ACC_MINOR_METADATA_FACTOR);
break;
case MEASUREMENT_TYPE_WATER_METER_ACC_MAJOR:
values.addProperty("acc_counter_water_major_" + channelNumber, (double) (startPoint + sampleOffset));
addPulseCounterProperties(values, "water_cnt_acc_major_", channelNumber, startPoint + sampleOffset, WATER_METER_ACC_MAJOR_METADATA_FACTOR);
break;
case MEASUREMENT_TYPE_HUMIDITY_ACCURATE:
values.addProperty("humidity_relative_" + channelNumber, (double) (startPoint + sampleOffset) / 10f);
break;
case MEASUREMENT_TYPE_STATIC_IAQ:
values.addProperty("static_iaq_" + channelNumber, (double) (startPoint + sampleOffset));
addPulseCounterProperties(values, "static_iaq_", channelNumber, startPoint + sampleOffset, STATIC_IAQ_METADATA_FACTOR);
break;
case MEASUREMENT_TYPE_CO2_GAS:
addPulseCounterProperties(values, "co2_gas_", channelNumber, startPoint + sampleOffset, CO2_GAS_METADATA_FACTOR);
break;
case MEASUREMENT_TYPE_CO2_EQUIVALENT:
values.addProperty("co2_ppm_" + channelNumber, (double) (startPoint + sampleOffset));
addPulseCounterProperties(values, "co2_", channelNumber, startPoint + sampleOffset, CO2_EQUIVALENT_METADATA_FACTOR);
break;
case MEASUREMENT_TYPE_BREATH_VOC:
values.addProperty("breath_voc_ppm_" + channelNumber, (double) (startPoint + sampleOffset));
addPulseCounterProperties(values, "breath_voc_", channelNumber, startPoint + sampleOffset, BREATH_VOC_METADATA_FACTOR);
break;
case MEASUREMENT_TYPE_PERCENTAGE:
values.addProperty("percentage_" + channelNumber, (double) (startPoint + sampleOffset) / 100f);
@ -357,22 +382,22 @@ public class CoapEfentoTransportResource extends AbstractCoapTransportResource {
values.addProperty("current_" + channelNumber, (double) (startPoint + sampleOffset) / 100f);
break;
case MEASUREMENT_TYPE_PULSE_CNT_ACC_MINOR:
values.addProperty("pulse_cnt_minor_" + channelNumber, (double) (startPoint + sampleOffset));
addPulseCounterProperties(values, "pulse_cnt_acc_minor_", channelNumber, startPoint + sampleOffset, PULSE_CNT_ACC_MINOR_METADATA_FACTOR);
break;
case MEASUREMENT_TYPE_PULSE_CNT_ACC_MAJOR:
values.addProperty("pulse_cnt_major_" + channelNumber, (double) (startPoint + sampleOffset));
addPulseCounterProperties(values, "pulse_cnt_acc_major_", channelNumber, startPoint + sampleOffset, PULSE_CNT_ACC_MAJOR_METADATA_FACTOR);
break;
case MEASUREMENT_TYPE_ELEC_METER_ACC_MINOR:
values.addProperty("elec_meter_minor_" + channelNumber, (double) (startPoint + sampleOffset));
addPulseCounterProperties(values, "elec_meter_acc_minor_", channelNumber, startPoint + sampleOffset, ELEC_METER_ACC_MINOR_METADATA_FACTOR);
break;
case MEASUREMENT_TYPE_ELEC_METER_ACC_MAJOR:
values.addProperty("elec_meter_major_" + channelNumber, (double) (startPoint + sampleOffset));
addPulseCounterProperties(values, "elec_meter_acc_major_", channelNumber, startPoint + sampleOffset, ELEC_METER_ACC_MAJOR_METADATA_FACTOR);
break;
case MEASUREMENT_TYPE_PULSE_CNT_ACC_WIDE_MINOR:
values.addProperty("pulse_cnt_wide_minor_" + channelNumber, (double) (startPoint + sampleOffset));
addPulseCounterProperties(values, "pulse_cnt_acc_wide_minor_", channelNumber, startPoint + sampleOffset, PULSE_CNT_ACC_WIDE_MINOR_METADATA_FACTOR);
break;
case MEASUREMENT_TYPE_PULSE_CNT_ACC_WIDE_MAJOR:
values.addProperty("pulse_cnt_wide_major_" + channelNumber, (double) (startPoint + sampleOffset));
addPulseCounterProperties(values, "pulse_cnt_acc_wide_major_", channelNumber, startPoint + sampleOffset, PULSE_CNT_ACC_WIDE_MAJOR_METADATA_FACTOR);
break;
case MEASUREMENT_TYPE_CURRENT_PRECISE:
values.addProperty("current_precise_" + channelNumber, (double) (startPoint + sampleOffset) / 1000f);
@ -387,6 +412,20 @@ public class CoapEfentoTransportResource extends AbstractCoapTransportResource {
}
}
private void addPulseCounterProperties(JsonObject values, String prefix, int channelNumber, int value, int metadataFactor) {
values.addProperty(prefix + channelNumber, value / metadataFactor);
values.addProperty(prefix + "metadata_" + channelNumber, value % metadataFactor);
}
private void calculatePulseCounterTotalValue(JsonObject value, PulseCounterType pulseCounterType) {
Set<String> keys = value.keySet();
Optional<String> major = keys.stream().filter(s -> s.startsWith(pulseCounterType.getPrefix() + "major_")).findAny();
Optional<String> minor = keys.stream().filter(s -> s.startsWith(pulseCounterType.getPrefix() + "minor_")).findAny();
if (major.isPresent() && minor.isPresent()) {
value.addProperty(pulseCounterType.getPrefix() + "total_value", value.get(major.get()).getAsInt() * pulseCounterType.getMajorResolution() + value.get(minor.get()).getAsInt());
}
}
private void addBinarySample(ProtoChannel protoChannel, boolean valueIsOk, JsonObject values, int channel, UUID sessionId) {
switch (protoChannel.getType()) {
case MEASUREMENT_TYPE_OK_ALARM:

View File

@ -28,6 +28,21 @@ import static org.thingsboard.server.gen.transport.coap.MeasurementTypeProtos.Me
public class CoapEfentoUtils {
public static final int PULSE_CNT_ACC_MINOR_METADATA_FACTOR = 6;
public static final int PULSE_CNT_ACC_MAJOR_METADATA_FACTOR = 4;
public static final int ELEC_METER_ACC_MINOR_METADATA_FACTOR = 6;
public static final int ELEC_METER_ACC_MAJOR_METADATA_FACTOR = 4;
public static final int PULSE_CNT_ACC_WIDE_MINOR_METADATA_FACTOR = 6;
public static final int PULSE_CNT_ACC_WIDE_MAJOR_METADATA_FACTOR = 4;
public static final int WATER_METER_ACC_MINOR_METADATA_FACTOR = 6;
public static final int WATER_METER_ACC_MAJOR_METADATA_FACTOR = 4;
public static final int IAQ_METADATA_FACTOR = 3;
public static final int STATIC_IAQ_METADATA_FACTOR = 3;
public static final int CO2_GAS_METADATA_FACTOR = 3;
public static final int CO2_EQUIVALENT_METADATA_FACTOR = 3;
public static final int BREATH_VOC_METADATA_FACTOR = 3;
public static String convertByteArrayToString(byte[] a) {
StringBuilder out = new StringBuilder();
for (byte b : a) {

View File

@ -0,0 +1,40 @@
/**
* Copyright © 2016-2024 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.transport.coap.efento.utils;
public enum PulseCounterType {
WATER_CNT_ACC("water_cnt_acc_", 100),
PULSE_CNT_ACC("pulse_cnt_acc_", 1000),
ELEC_METER_ACC("elec_meter_acc_", 1000),
PULSE_CNT_ACC_WIDE("pulse_cnt_acc_wide_", 1000000);
private final String prefix;
private final int majorResolution;
PulseCounterType(String prefix, int majorResolution) {
this.prefix = prefix;
this.majorResolution = majorResolution;
}
public String getPrefix() {
return prefix;
}
public int getMajorResolution() {
return majorResolution;
}
}

View File

@ -70,7 +70,7 @@ import static org.thingsboard.server.gen.transport.coap.MeasurementTypeProtos.Me
import static org.thingsboard.server.gen.transport.coap.MeasurementTypeProtos.MeasurementType.MEASUREMENT_TYPE_WATER_METER_ACC_MINOR;
import static org.thingsboard.server.transport.coap.efento.utils.CoapEfentoUtils.convertTimestampToUtcString;
class CoapEfentTransportResourceTest {
class CoapEfentoTransportResourceTest {
private static CoapEfentoTransportResource coapEfentoTransportResource;
@ -152,31 +152,69 @@ class CoapEfentTransportResourceTest {
Arguments.of(MEASUREMENT_TYPE_ATMOSPHERIC_PRESSURE, List.of(1013), "pressure_1", 101.3),
Arguments.of(MEASUREMENT_TYPE_DIFFERENTIAL_PRESSURE, List.of(500), "pressure_diff_1", 500),
Arguments.of(MEASUREMENT_TYPE_PULSE_CNT, List.of(300), "pulse_cnt_1", 300),
Arguments.of(MEASUREMENT_TYPE_IAQ, List.of(150), "iaq_1", 150),
Arguments.of(MEASUREMENT_TYPE_IAQ, List.of(150), "iaq_1", 50.0),
Arguments.of(MEASUREMENT_TYPE_ELECTRICITY_METER, List.of(1200), "watt_hour_1", 1200),
Arguments.of(MEASUREMENT_TYPE_SOIL_MOISTURE, List.of(35), "soil_moisture_1", 35),
Arguments.of(MEASUREMENT_TYPE_AMBIENT_LIGHT, List.of(500), "ambient_light_1", 50),
Arguments.of(MEASUREMENT_TYPE_HIGH_PRESSURE, List.of(200000), "high_pressure_1", 200000),
Arguments.of(MEASUREMENT_TYPE_DISTANCE_MM, List.of(1500), "distance_mm_1", 1500),
Arguments.of(MEASUREMENT_TYPE_WATER_METER_ACC_MINOR, List.of(125), "acc_counter_water_minor_1", 125),
Arguments.of(MEASUREMENT_TYPE_WATER_METER_ACC_MAJOR, List.of(2500), "acc_counter_water_major_1", 2500),
Arguments.of(MEASUREMENT_TYPE_HUMIDITY_ACCURATE, List.of(525), "humidity_relative_1", 52.5),
Arguments.of(MEASUREMENT_TYPE_STATIC_IAQ, List.of(110), "static_iaq_1", 110),
Arguments.of(MEASUREMENT_TYPE_CO2_EQUIVALENT, List.of(450), "co2_ppm_1", 450),
Arguments.of(MEASUREMENT_TYPE_BREATH_VOC, List.of(220), "breath_voc_ppm_1", 220),
Arguments.of(MEASUREMENT_TYPE_PERCENTAGE, List.of(80), "percentage_1", 0.80),
Arguments.of(MEASUREMENT_TYPE_VOLTAGE, List.of(2400), "voltage_1", 240),
Arguments.of(MEASUREMENT_TYPE_STATIC_IAQ, List.of(110), "static_iaq_1", 36),
Arguments.of(MEASUREMENT_TYPE_CO2_EQUIVALENT, List.of(450), "co2_1", 150),
Arguments.of(MEASUREMENT_TYPE_BREATH_VOC, List.of(220), "breath_voc_1", 73),
Arguments.of(MEASUREMENT_TYPE_PERCENTAGE, List.of(80), "percentage_1", 0.8),
Arguments.of(MEASUREMENT_TYPE_VOLTAGE, List.of(2400), "voltage_1", 240.0),
Arguments.of(MEASUREMENT_TYPE_CURRENT, List.of(550), "current_1", 5.5),
Arguments.of(MEASUREMENT_TYPE_PULSE_CNT_ACC_MINOR, List.of(180), "pulse_cnt_minor_1", 180),
Arguments.of(MEASUREMENT_TYPE_PULSE_CNT_ACC_MAJOR, List.of(1200), "pulse_cnt_major_1", 1200),
Arguments.of(MEASUREMENT_TYPE_ELEC_METER_ACC_MINOR, List.of(550), "elec_meter_minor_1", 550),
Arguments.of(MEASUREMENT_TYPE_ELEC_METER_ACC_MAJOR, List.of(5500), "elec_meter_major_1", 5500),
Arguments.of(MEASUREMENT_TYPE_PULSE_CNT_ACC_WIDE_MINOR, List.of(230), "pulse_cnt_wide_minor_1", 230),
Arguments.of(MEASUREMENT_TYPE_PULSE_CNT_ACC_WIDE_MAJOR, List.of(1700), "pulse_cnt_wide_major_1", 1700),
Arguments.of(MEASUREMENT_TYPE_CURRENT_PRECISE, List.of(275), "current_precise_1", 0.275)
);
}
@ParameterizedTest
@MethodSource
void checkPulseCounterSensors(MeasurementType minorType, List<Integer> minorSampleOffsets, MeasurementType majorType, List<Integer> majorSampleOffsets,
String propertyPrefix, double expectedValue) {
long tsInSec = Instant.now().getEpochSecond();
ProtoMeasurements measurements = ProtoMeasurements.newBuilder()
.setSerialNum(integerToByteString(1234))
.setCloudToken("test_token")
.setMeasurementPeriodBase(180)
.setMeasurementPeriodFactor(0)
.setBatteryStatus(true)
.setSignal(0)
.setNextTransmissionAt(1000)
.setTransferReason(0)
.setHash(0)
.addAllChannels(List.of(MeasurementsProtos.ProtoChannel.newBuilder()
.setType(minorType)
.setTimestamp(Math.toIntExact(tsInSec))
.addAllSampleOffsets(minorSampleOffsets)
.build(),
MeasurementsProtos.ProtoChannel.newBuilder()
.setType(majorType)
.setTimestamp(Math.toIntExact(tsInSec))
.addAllSampleOffsets(majorSampleOffsets)
.build()))
.build();
List<CoapEfentoTransportResource.EfentoTelemetry> efentoMeasurements = coapEfentoTransportResource.getEfentoMeasurements(measurements, UUID.randomUUID());
assertThat(efentoMeasurements).hasSize(1);
assertThat(efentoMeasurements.get(0).getTs()).isEqualTo(tsInSec * 1000);
assertThat(efentoMeasurements.get(0).getValues().getAsJsonObject().get(propertyPrefix + "_total_value").getAsDouble()).isEqualTo(expectedValue);
checkDefaultMeasurements(measurements, efentoMeasurements, 180, false);
}
private static Stream<Arguments> checkPulseCounterSensors() {
return Stream.of(
Arguments.of(MEASUREMENT_TYPE_WATER_METER_ACC_MINOR, List.of(125), MEASUREMENT_TYPE_WATER_METER_ACC_MAJOR,
List.of(2500), "water_cnt_acc", 62520.0),
Arguments.of(MEASUREMENT_TYPE_PULSE_CNT_ACC_MINOR, List.of(180), MEASUREMENT_TYPE_PULSE_CNT_ACC_MAJOR,
List.of(1200), "pulse_cnt_acc", 300030.0),
Arguments.of(MEASUREMENT_TYPE_ELEC_METER_ACC_MINOR, List.of(550), MEASUREMENT_TYPE_ELEC_METER_ACC_MAJOR,
List.of(5500), "elec_meter_acc", 1375091.0),
Arguments.of(MEASUREMENT_TYPE_PULSE_CNT_ACC_WIDE_MINOR, List.of(230), MEASUREMENT_TYPE_PULSE_CNT_ACC_WIDE_MAJOR,
List.of(1700), "pulse_cnt_acc_wide", 425000038.0));
}
@Test
void checkBinarySensor() {
long tsInSec = Instant.now().getEpochSecond();