extracted some methods to CoapEfentoUtils, added tests

This commit is contained in:
dashevchenko 2024-08-02 17:28:26 +03:00
parent 569ab0b87b
commit 560708328e
3 changed files with 183 additions and 38 deletions

View File

@ -28,18 +28,17 @@ import org.eclipse.californium.core.network.Exchange;
import org.eclipse.californium.core.server.resources.CoapExchange;
import org.eclipse.californium.core.server.resources.Resource;
import org.springframework.util.CollectionUtils;
import org.thingsboard.server.common.adaptor.AdaptorException;
import org.thingsboard.server.common.adaptor.ProtoConverter;
import org.thingsboard.server.common.data.DeviceProfile;
import org.thingsboard.server.common.data.DeviceTransportType;
import org.thingsboard.server.common.data.device.profile.CoapDeviceProfileTransportConfiguration;
import org.thingsboard.server.common.data.device.profile.DeviceProfileTransportConfiguration;
import org.thingsboard.server.common.data.device.profile.EfentoCoapDeviceTypeConfiguration;
import org.thingsboard.server.common.adaptor.AdaptorException;
import org.thingsboard.server.common.transport.auth.SessionInfoCreator;
import org.thingsboard.server.gen.transport.TransportProtos;
import org.thingsboard.server.gen.transport.coap.ConfigProtos;
import org.thingsboard.server.gen.transport.coap.DeviceInfoProtos;
import org.thingsboard.server.gen.transport.coap.MeasurementTypeProtos;
import org.thingsboard.server.gen.transport.coap.MeasurementsProtos;
import org.thingsboard.server.gen.transport.coap.MeasurementsProtos.ProtoChannel;
import org.thingsboard.server.transport.coap.AbstractCoapTransportResource;
@ -59,13 +58,12 @@ import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import static com.google.gson.JsonParser.parseString;
import static org.thingsboard.server.gen.transport.coap.MeasurementTypeProtos.MeasurementType.MEASUREMENT_TYPE_FLOODING;
import static org.thingsboard.server.gen.transport.coap.MeasurementTypeProtos.MeasurementType.MEASUREMENT_TYPE_OK_ALARM;
import static org.thingsboard.server.gen.transport.coap.MeasurementTypeProtos.MeasurementType.MEASUREMENT_TYPE_OUTPUT_CONTROL;
import static org.thingsboard.server.transport.coap.CoapTransportService.CONFIGURATION;
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.isBinarySensor;
import static org.thingsboard.server.transport.coap.efento.utils.CoapEfentoUtils.isSensorError;
@Slf4j
public class CoapEfentoTransportResource extends AbstractCoapTransportResource {
@ -224,7 +222,7 @@ public class CoapEfentoTransportResource extends AbstractCoapTransportResource {
}
}
private List<EfentoTelemetry> getEfentoMeasurements(MeasurementsProtos.ProtoMeasurements protoMeasurements, UUID sessionId) {
public List<EfentoTelemetry> getEfentoMeasurements(MeasurementsProtos.ProtoMeasurements protoMeasurements, UUID sessionId) {
String serialNumber = CoapEfentoUtils.convertByteArrayToString(protoMeasurements.getSerialNum().toByteArray());
boolean batteryStatus = protoMeasurements.getBatteryStatus();
int measurementPeriodBase = protoMeasurements.getMeasurementPeriodBase();
@ -240,17 +238,16 @@ public class CoapEfentoTransportResource extends AbstractCoapTransportResource {
Map<Long, JsonObject> valuesMap = new TreeMap<>();
for (int channel = 0; channel < channelsList.size(); channel++) {
ProtoChannel protoChannel = channelsList.get(channel);
List<Integer> sampleOffsetsList = protoChannel.getSampleOffsetsList();
if (CollectionUtils.isEmpty(sampleOffsetsList)) {
log.trace("[{}][{}] sampleOffsetsList list is empty!", sessionId, protoChannel.getType().name());
continue;
}
boolean isBinarySensor = isBinarySensor(protoChannel.getType());
int channelPeriodFactor = (measurementPeriodFactor == 0 ? (isBinarySensor ? 14 : 1) : measurementPeriodFactor);
int measurementPeriod = measurementPeriodBase * channelPeriodFactor;
long measurementPeriodMillis = TimeUnit.SECONDS.toMillis(measurementPeriod);
long startTimestampMillis = TimeUnit.SECONDS.toMillis(protoChannel.getTimestamp());
List<Integer> sampleOffsetsList = protoChannel.getSampleOffsetsList();
if (CollectionUtils.isEmpty(sampleOffsetsList)) {
log.trace("[{}][{}] sampleOffsetsList list is empty!", sessionId, protoChannel.getType().name());
continue;
}
for (int i = 0; i < sampleOffsetsList.size(); i++) {
int sampleOffset = sampleOffsetsList.get(i);
@ -261,17 +258,19 @@ public class CoapEfentoTransportResource extends AbstractCoapTransportResource {
JsonObject values;
if (isBinarySensor) {
long sampleOffsetMillis = TimeUnit.SECONDS.toMillis(sampleOffset);
long measurementTimestamp = startTimestampMillis + Math.abs(sampleOffsetMillis) - 1;
values = valuesMap.computeIfAbsent(measurementTimestamp - 1000, k ->
CoapEfentoUtils.setDefaultMeasurements(serialNumber, batteryStatus, measurementPeriod, nextTransmissionAtMillis, signal, k));
boolean currentIsOk = sampleOffset < 0;
Integer previousSampleOffset = i > 0 ? sampleOffsetsList.get(i - 1) : null;
boolean previousValueIsOk = previousSampleOffset != null && previousSampleOffset < 0;
boolean valueIsOk = sampleOffset < 0;
if (binaryValueNotChanged(valueIsOk, previousValueIsOk)) {
if (previousSampleOffset != null) { //compare with previous value
boolean previousIsOk = previousSampleOffset < 0;
if (currentIsOk == previousIsOk) {
break;
}
addBinarySample(protoChannel, valueIsOk, values, channel + 1);
}
long sampleOffsetMillis = TimeUnit.SECONDS.toMillis(sampleOffset);
long measurementTimestamp = startTimestampMillis + Math.abs(sampleOffsetMillis);
values = valuesMap.computeIfAbsent(measurementTimestamp - 1000, k ->
CoapEfentoUtils.setDefaultMeasurements(serialNumber, batteryStatus, measurementPeriod, nextTransmissionAtMillis, signal, k));
addBinarySample(protoChannel, currentIsOk, values, channel + 1, sessionId);
} else {
long timestampMillis = startTimestampMillis + i * measurementPeriodMillis;
values = valuesMap.computeIfAbsent(timestampMillis, k -> CoapEfentoUtils.setDefaultMeasurements(
@ -290,14 +289,6 @@ public class CoapEfentoTransportResource extends AbstractCoapTransportResource {
.collect(Collectors.toList());
}
private boolean isBinarySensor(MeasurementTypeProtos.MeasurementType type) {
return type == MEASUREMENT_TYPE_OK_ALARM || type == MEASUREMENT_TYPE_FLOODING || type == MEASUREMENT_TYPE_OUTPUT_CONTROL;
}
private boolean isSensorError(int sampleOffset) {
return sampleOffset >= 8355840 && sampleOffset <= 8388607;
}
private void addContinuesSample(ProtoChannel protoChannel, int sampleOffset, JsonObject values, int channelNumber, UUID sessionId) {
int startPoint = protoChannel.getStartPoint();
@ -396,26 +387,23 @@ public class CoapEfentoTransportResource extends AbstractCoapTransportResource {
}
}
private void addBinarySample(ProtoChannel protoChannel, boolean valueIsOk, JsonObject values, int channel) {
private void addBinarySample(ProtoChannel protoChannel, boolean valueIsOk, JsonObject values, int channel, UUID sessionId) {
switch (protoChannel.getType()) {
case MEASUREMENT_TYPE_OK_ALARM:
values.addProperty("ok_alarm_" + channel, valueIsOk ? "OK" : "ALARM");
break;
case MEASUREMENT_TYPE_FLOODING:
values.addProperty("flooding_" + channel, valueIsOk ? "OK" : "WATER_DETECTED");
break;
case MEASUREMENT_TYPE_OUTPUT_CONTROL:
values.addProperty("output_control_" + channel, valueIsOk ? "OFF" : "ON");
break;
default:
log.trace("[{}],[{}] Unsupported binary measurementType! Ignoring.", sessionId, protoChannel.getType().name());
break;
}
}
private static boolean binaryValueNotChanged(boolean currentIsOk, boolean previousIsOk) {
boolean isOk = previousIsOk && currentIsOk;
boolean isAlarm = !previousIsOk && !currentIsOk;
if (isOk || isAlarm) {
return true;
}
return false;
}
private EfentoTelemetry getEfentoDeviceInfo(DeviceInfoProtos.ProtoDeviceInfo protoDeviceInfo) {
JsonObject values = new JsonObject();
values.addProperty("sw_version", protoDeviceInfo.getSwVersion());

View File

@ -16,11 +16,16 @@
package org.thingsboard.server.transport.coap.efento.utils;
import com.google.gson.JsonObject;
import org.thingsboard.server.gen.transport.coap.MeasurementTypeProtos.MeasurementType;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.TimeZone;
import static org.thingsboard.server.gen.transport.coap.MeasurementTypeProtos.MeasurementType.MEASUREMENT_TYPE_FLOODING;
import static org.thingsboard.server.gen.transport.coap.MeasurementTypeProtos.MeasurementType.MEASUREMENT_TYPE_OK_ALARM;
import static org.thingsboard.server.gen.transport.coap.MeasurementTypeProtos.MeasurementType.MEASUREMENT_TYPE_OUTPUT_CONTROL;
public class CoapEfentoUtils {
public static String convertByteArrayToString(byte[] a) {
@ -50,4 +55,12 @@ public class CoapEfentoUtils {
return values;
}
public static boolean isBinarySensor(MeasurementType type) {
return type == MEASUREMENT_TYPE_OK_ALARM || type == MEASUREMENT_TYPE_FLOODING || type == MEASUREMENT_TYPE_OUTPUT_CONTROL;
}
public static boolean isSensorError(int sampleOffset) {
return sampleOffset >= 8355840 && sampleOffset <= 8388607;
}
}

View File

@ -0,0 +1,144 @@
/**
* 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;
import com.google.protobuf.ByteString;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.thingsboard.server.gen.transport.coap.MeasurementTypeProtos;
import org.thingsboard.server.gen.transport.coap.MeasurementsProtos;
import org.thingsboard.server.transport.coap.CoapTransportContext;
import java.nio.ByteBuffer;
import java.time.Instant;
import java.util.List;
import java.util.UUID;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
class CoapEfentTransportResourceTest {
private static CoapEfentoTransportResource coapEfentoTransportResource;
@BeforeAll
static void setUp() {
var ctxMock = mock(CoapTransportContext.class);
coapEfentoTransportResource = new CoapEfentoTransportResource(ctxMock, "testName");
}
@Test
void checkContinuousSensor() {
long tsInSec = Instant.now().getEpochSecond();
MeasurementsProtos.ProtoMeasurements measurements = MeasurementsProtos.ProtoMeasurements.newBuilder()
.setSerialNum(integerToByteString(1234))
.setCloudToken("test_token")
.setMeasurementPeriodBase(180)
.setMeasurementPeriodFactor(1)
.setBatteryStatus(true)
.setSignal(0)
.setNextTransmissionAt(1000)
.setTransferReason(0)
.setHash(0)
.addAllChannels(List.of(MeasurementsProtos.ProtoChannel.newBuilder()
.setType(MeasurementTypeProtos.MeasurementType.MEASUREMENT_TYPE_TEMPERATURE)
.setTimestamp(Math.toIntExact(tsInSec))
.addAllSampleOffsets(List.of(223, 224))
.build(),
MeasurementsProtos.ProtoChannel.newBuilder()
.setType(MeasurementTypeProtos.MeasurementType.MEASUREMENT_TYPE_HUMIDITY)
.setTimestamp(Math.toIntExact(tsInSec))
.addAllSampleOffsets(List.of(20, 30))
.build()
))
.build();
List<CoapEfentoTransportResource.EfentoTelemetry> efentoMeasurements = coapEfentoTransportResource.getEfentoMeasurements(measurements, UUID.randomUUID());
assertThat(efentoMeasurements).hasSize(2);
assertThat(efentoMeasurements.get(0).getTs()).isEqualTo(tsInSec * 1000);
assertThat(efentoMeasurements.get(0).getValues().getAsJsonObject().get("temperature_1").getAsDouble()).isEqualTo(22.3);
assertThat(efentoMeasurements.get(0).getValues().getAsJsonObject().get("humidity_2").getAsDouble()).isEqualTo(20);
assertThat(efentoMeasurements.get(1).getTs()).isEqualTo((tsInSec + 180) * 1000);
assertThat(efentoMeasurements.get(1).getValues().getAsJsonObject().get("temperature_1").getAsDouble()).isEqualTo(22.4);
assertThat(efentoMeasurements.get(1).getValues().getAsJsonObject().get("humidity_2").getAsDouble()).isEqualTo(30);
}
@Test
void checkBinarySensor() {
long tsInSec = Instant.now().getEpochSecond();
MeasurementsProtos.ProtoMeasurements measurements = MeasurementsProtos.ProtoMeasurements.newBuilder()
.setSerialNum(integerToByteString(1234))
.setCloudToken("test_token")
.setMeasurementPeriodBase(180)
.setMeasurementPeriodFactor(1)
.setBatteryStatus(true)
.setSignal(0)
.setNextTransmissionAt(1000)
.setTransferReason(0)
.setHash(0)
.addChannels(MeasurementsProtos.ProtoChannel.newBuilder()
.setType(MeasurementTypeProtos.MeasurementType.MEASUREMENT_TYPE_OK_ALARM)
.setTimestamp(Math.toIntExact(tsInSec))
.addAllSampleOffsets(List.of(1, 1))
.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("ok_alarm_1").getAsString()).isEqualTo("ALARM");
}
@Test
void checkBinarySensorWhenValueIsVarying() {
long tsInSec = Instant.now().getEpochSecond();
MeasurementsProtos.ProtoMeasurements measurements = MeasurementsProtos.ProtoMeasurements.newBuilder()
.setSerialNum(integerToByteString(1234))
.setCloudToken("test_token")
.setMeasurementPeriodBase(180)
.setMeasurementPeriodFactor(1)
.setBatteryStatus(true)
.setSignal(0)
.setNextTransmissionAt(1000)
.setTransferReason(0)
.setHash(0)
.addChannels(MeasurementsProtos.ProtoChannel.newBuilder()
.setType(MeasurementTypeProtos.MeasurementType.MEASUREMENT_TYPE_OK_ALARM)
.setTimestamp(Math.toIntExact(tsInSec))
.addAllSampleOffsets(List.of(1, -10))
.build())
.build();
List<CoapEfentoTransportResource.EfentoTelemetry> efentoMeasurements = coapEfentoTransportResource.getEfentoMeasurements(measurements, UUID.randomUUID());
assertThat(efentoMeasurements).hasSize(2);
assertThat(efentoMeasurements.get(0).getTs()).isEqualTo(tsInSec * 1000);
assertThat(efentoMeasurements.get(0).getValues().getAsJsonObject().get("ok_alarm_1").getAsString()).isEqualTo("ALARM");
assertThat(efentoMeasurements.get(1).getTs()).isEqualTo((tsInSec + 9) * 1000);
assertThat(efentoMeasurements.get(1).getValues().getAsJsonObject().get("ok_alarm_1").getAsString()).isEqualTo("OK");
}
public static ByteString integerToByteString(Integer intValue) {
// Allocate a ByteBuffer with the size of an integer (4 bytes)
ByteBuffer buffer = ByteBuffer.allocate(Integer.BYTES);
// Put the integer value into the ByteBuffer
buffer.putInt(intValue);
// Convert the ByteBuffer to a byte array
byte[] byteArray = buffer.array();
// Create a ByteString from the byte array
return ByteString.copyFrom(byteArray);
}
}