[smartmeter] Fix Undelivered IOException (#17133)

Signed-off-by: Leo Siepel <leosiepel@gmail.com>
Signed-off-by: Ciprian Pascu <contact@ciprianpascu.ro>
This commit is contained in:
lsiepel 2024-09-07 10:06:56 +02:00 committed by Ciprian Pascu
parent caea49ffc9
commit c48c77d82b
17 changed files with 164 additions and 124 deletions

View File

@ -12,19 +12,25 @@
*/ */
package org.openhab.binding.smartmeter; package org.openhab.binding.smartmeter;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
/** /**
* The {@link SmartMeterConfiguration} is the class used to match the * The {@link SmartMeterConfiguration} is the class used to match the
* thing configuration. * thing configuration.
* *
* @author Matthias Steigenberger - Initial contribution * @author Matthias Steigenberger - Initial contribution
*/ */
@NonNullByDefault
public class SmartMeterConfiguration { public class SmartMeterConfiguration {
@Nullable
public String port; public String port;
public Integer refresh; public Integer refresh = 10;
public Integer baudrateChangeDelay; public Integer baudrateChangeDelay = 0;
@Nullable
public String initMessage; public String initMessage;
public String baudrate; public String baudrate = "AUTO";
public String mode; public String mode = "SML";
public String conformity; public String conformity = "NONE";
} }

View File

@ -12,6 +12,7 @@
*/ */
package org.openhab.binding.smartmeter.internal; package org.openhab.binding.smartmeter.internal;
import java.io.IOException;
import java.time.Duration; import java.time.Duration;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
@ -37,6 +38,7 @@ import org.slf4j.LoggerFactory;
import io.reactivex.Flowable; import io.reactivex.Flowable;
import io.reactivex.disposables.Disposable; import io.reactivex.disposables.Disposable;
import io.reactivex.exceptions.UndeliverableException;
import io.reactivex.plugins.RxJavaPlugins; import io.reactivex.plugins.RxJavaPlugins;
import io.reactivex.schedulers.Schedulers; import io.reactivex.schedulers.Schedulers;
@ -83,7 +85,18 @@ public abstract class MeterDevice<T> {
this.connector = createConnector(serialPortManagerSupplier, serialPort, baudrate, baudrateChangeDelay, this.connector = createConnector(serialPortManagerSupplier, serialPort, baudrate, baudrateChangeDelay,
protocolMode); protocolMode);
RxJavaPlugins.setErrorHandler(error -> { RxJavaPlugins.setErrorHandler(error -> {
logger.error("Fatal error occured", error); if (error == null) {
logger.warn("Fatal but unknown error occurred");
return;
}
if (error instanceof UndeliverableException) {
error = error.getCause();
}
if (error instanceof IOException) {
logger.warn("Connection related issue occurred: {}", error.getMessage());
return;
}
logger.warn("Fatal error occurred", error);
}); });
} }

View File

@ -17,7 +17,7 @@ import java.util.function.Supplier;
import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.smartmeter.internal.helper.ProtocolMode; import org.openhab.binding.smartmeter.internal.helper.ProtocolMode;
import org.openhab.binding.smartmeter.internal.iec62056.Iec62056_21MeterReader; import org.openhab.binding.smartmeter.internal.iec62056.MeterReader;
import org.openhab.binding.smartmeter.internal.sml.SmlMeterReader; import org.openhab.binding.smartmeter.internal.sml.SmlMeterReader;
import org.openhab.core.io.transport.serial.SerialPortManager; import org.openhab.core.io.transport.serial.SerialPortManager;
@ -49,8 +49,8 @@ public class MeterDeviceFactory {
switch (protocolMode) { switch (protocolMode) {
case D: case D:
case ABC: case ABC:
return new Iec62056_21MeterReader(serialPortManagerSupplier, deviceId, serialPort, initMessage, return new MeterReader(serialPortManagerSupplier, deviceId, serialPort, initMessage, baudrate,
baudrate, baudrateChangeDelay, protocolMode); baudrateChangeDelay, protocolMode);
case SML: case SML:
return SmlMeterReader.createInstance(serialPortManagerSupplier, deviceId, serialPort, initMessage, return SmlMeterReader.createInstance(serialPortManagerSupplier, deviceId, serialPort, initMessage,
baudrate, baudrateChangeDelay); baudrate, baudrateChangeDelay);

View File

@ -66,11 +66,15 @@ public class MeterValue<Q extends Quantity<Q>> {
@Override @Override
public int hashCode() { public int hashCode() {
final int prime = 31; final int prime = 31;
final String status = this.status;
final Unit<? extends Q> unit = this.unit;
final String value = this.value;
int result = 1; int result = 1;
result = prime * result + ((obis == null) ? 0 : obis.hashCode()); result = prime * result + obis.hashCode();
result = prime * result + ((status == null) ? 0 : status.hashCode()); result = prime * result + (status == null ? 0 : status.hashCode());
result = prime * result + ((unit == null) ? 0 : unit.hashCode()); result = prime * result + (unit == null ? 0 : unit.hashCode());
result = prime * result + ((value == null) ? 0 : value.hashCode()); result = prime * result + value.hashCode();
return result; return result;
} }
@ -90,6 +94,7 @@ public class MeterValue<Q extends Quantity<Q>> {
if (!obis.equals(other.obis)) { if (!obis.equals(other.obis)) {
return false; return false;
} }
String status = this.status;
if (status == null) { if (status == null) {
if (other.status != null) { if (other.status != null) {
return false; return false;
@ -97,6 +102,7 @@ public class MeterValue<Q extends Quantity<Q>> {
} else if (!status.equals(other.status)) { } else if (!status.equals(other.status)) {
return false; return false;
} }
Unit<? extends Q> unit = this.unit;
if (unit == null) { if (unit == null) {
if (other.unit != null) { if (other.unit != null) {
return false; return false;

View File

@ -82,6 +82,10 @@ public class ObisCode {
* @return the obis as string. * @return the obis as string.
*/ */
public String asDecimalString() { public String asDecimalString() {
Byte a = this.a;
Byte b = this.b;
Byte c = this.c;
Byte f = this.f;
try (Formatter format = new Formatter()) { try (Formatter format = new Formatter()) {
format.format(SmartMeterBindingConstants.OBIS_FORMAT, a != null ? a & 0xFF : 0, b != null ? b & 0xFF : 0, format.format(SmartMeterBindingConstants.OBIS_FORMAT, a != null ? a & 0xFF : 0, b != null ? b & 0xFF : 0,
c & 0xFF, d & 0xFF, e & 0xFF, f != null ? f & 0xFF : 0); c & 0xFF, d & 0xFF, e & 0xFF, f != null ? f & 0xFF : 0);
@ -118,10 +122,15 @@ public class ObisCode {
return asDecimalString(); return asDecimalString();
} }
public boolean matches(@Nullable Byte a, @Nullable Byte b, Byte c, Byte d, Byte e, @Nullable Byte f) { public boolean matches(@Nullable Byte otherA, @Nullable Byte otherB, Byte otherC, Byte d, Byte e,
return (this.a == null || a == null || this.a.equals(a)) && (this.b == null || b == null || this.b.equals(b)) @Nullable Byte otherF) {
&& this.c.equals(c) && this.d.equals(d) && this.e.equals(e) Byte a = this.a;
&& (this.f == null || f == null || this.f.equals(f)); Byte b = this.b;
Byte c = this.c;
Byte f = this.f;
return (a == null || otherA == null || a.equals(otherA)) && (b == null || otherB == null || b.equals(otherB))
&& c.equals(otherC) && this.d.equals(d) && this.e.equals(e)
&& (f == null || otherF == null || f.equals(otherF));
} }
public boolean matches(Byte c, Byte d, Byte e) { public boolean matches(Byte c, Byte d, Byte e) {

View File

@ -21,7 +21,7 @@ import java.util.concurrent.ConcurrentHashMap;
import javax.measure.Quantity; import javax.measure.Quantity;
import javax.measure.Unit; import javax.measure.Unit;
import org.eclipse.jdt.annotation.NonNull; import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.smartmeter.SmartMeterBindingConstants; import org.openhab.binding.smartmeter.SmartMeterBindingConstants;
import org.openhab.core.library.CoreItemFactory; import org.openhab.core.library.CoreItemFactory;
@ -44,6 +44,7 @@ import org.slf4j.LoggerFactory;
* @author Matthias Steigenberger - Initial contribution * @author Matthias Steigenberger - Initial contribution
* *
*/ */
@NonNullByDefault
@Component(service = { ChannelTypeProvider.class, SmartMeterChannelTypeProvider.class }) @Component(service = { ChannelTypeProvider.class, SmartMeterChannelTypeProvider.class })
public class SmartMeterChannelTypeProvider implements ChannelTypeProvider, MeterValueListener { public class SmartMeterChannelTypeProvider implements ChannelTypeProvider, MeterValueListener {
@ -68,14 +69,14 @@ public class SmartMeterChannelTypeProvider implements ChannelTypeProvider, Meter
} }
@Override @Override
public <Q extends @NonNull Quantity<Q>> void valueChanged(MeterValue<Q> value) { public <Q extends Quantity<Q>> void valueChanged(MeterValue<Q> value) {
if (!obisChannelMap.containsKey(value.getObisCode())) { if (!obisChannelMap.containsKey(value.getObisCode())) {
logger.debug("Creating ChannelType for OBIS {}", value.getObisCode()); logger.debug("Creating ChannelType for OBIS {}", value.getObisCode());
obisChannelMap.put(value.getObisCode(), getChannelType(value.getUnit(), value.getObisCode())); obisChannelMap.put(value.getObisCode(), getChannelType(value.getUnit(), value.getObisCode()));
} }
} }
private ChannelType getChannelType(Unit<?> unit, String obis) { private ChannelType getChannelType(@Nullable Unit<?> unit, String obis) {
String obisChannelId = SmartMeterBindingConstants.getObisChannelId(obis); String obisChannelId = SmartMeterBindingConstants.getObisChannelId(obis);
StateChannelTypeBuilder stateDescriptionBuilder; StateChannelTypeBuilder stateDescriptionBuilder;
if (unit != null) { if (unit != null) {
@ -96,7 +97,7 @@ public class SmartMeterChannelTypeProvider implements ChannelTypeProvider, Meter
} }
@Override @Override
public <Q extends @NonNull Quantity<Q>> void valueRemoved(MeterValue<Q> value) { public <Q extends Quantity<Q>> void valueRemoved(MeterValue<Q> value) {
obisChannelMap.remove(value.getObisCode()); obisChannelMap.remove(value.getObisCode());
} }
@ -106,7 +107,7 @@ public class SmartMeterChannelTypeProvider implements ChannelTypeProvider, Meter
* @param obis The obis code. * @param obis The obis code.
* @return The {@link ChannelTypeUID} or null. * @return The {@link ChannelTypeUID} or null.
*/ */
public ChannelTypeUID getChannelTypeIdForObis(String obis) { public @Nullable ChannelTypeUID getChannelTypeIdForObis(String obis) {
ChannelType channeltype = obisChannelMap.get(obis); ChannelType channeltype = obisChannelMap.get(obis);
return channeltype != null ? channeltype.getUID() : null; return channeltype != null ? channeltype.getUID() : null;
} }

View File

@ -68,14 +68,13 @@ import io.reactivex.disposables.Disposable;
public class SmartMeterHandler extends BaseThingHandler { public class SmartMeterHandler extends BaseThingHandler {
private static final long DEFAULT_TIMEOUT = 30000; private static final long DEFAULT_TIMEOUT = 30000;
private static final int DEFAULT_REFRESH_PERIOD = 30;
private Logger logger = LoggerFactory.getLogger(SmartMeterHandler.class); private Logger logger = LoggerFactory.getLogger(SmartMeterHandler.class);
private MeterDevice<?> smlDevice; private MeterDevice<?> smlDevice;
private Disposable valueReader; private Disposable valueReader;
private Conformity conformity; private Conformity conformity;
private MeterValueListener valueChangeListener; private MeterValueListener valueChangeListener;
private SmartMeterChannelTypeProvider channelTypeProvider; private SmartMeterChannelTypeProvider channelTypeProvider;
private @NonNull Supplier<SerialPortManager> serialPortManagerSupplier; private Supplier<SerialPortManager> serialPortManagerSupplier;
public SmartMeterHandler(Thing thing, SmartMeterChannelTypeProvider channelProvider, public SmartMeterHandler(Thing thing, SmartMeterChannelTypeProvider channelProvider,
Supplier<SerialPortManager> serialPortManagerSupplier) { Supplier<SerialPortManager> serialPortManagerSupplier) {
@ -99,11 +98,10 @@ public class SmartMeterHandler extends BaseThingHandler {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
"Parameter 'port' is mandatory and must be configured"); "Parameter 'port' is mandatory and must be configured");
} else { } else {
byte[] pullSequence = config.initMessage == null ? null String initMessage = config.initMessage;
: HexUtils.hexToBytes(config.initMessage.replaceAll("\\s+", "")); byte[] pullSequence = initMessage == null ? null : HexUtils.hexToBytes(initMessage.replaceAll("\\s+", ""));
int baudrate = config.baudrate == null ? Baudrate.AUTO.getBaudrate() int baudrate = Baudrate.fromString(config.baudrate).getBaudrate();
: Baudrate.fromString(config.baudrate).getBaudrate(); this.conformity = Conformity.valueOf(config.conformity);
this.conformity = config.conformity == null ? Conformity.NONE : Conformity.valueOf(config.conformity);
this.smlDevice = MeterDeviceFactory.getDevice(serialPortManagerSupplier, config.mode, this.smlDevice = MeterDeviceFactory.getDevice(serialPortManagerSupplier, config.mode,
this.thing.getUID().getAsString(), port, pullSequence, baudrate, config.baudrateChangeDelay); this.thing.getUID().getAsString(), port, pullSequence, baudrate, config.baudrateChangeDelay);
updateStatus(ThingStatus.UNKNOWN, ThingStatusDetail.HANDLER_CONFIGURATION_PENDING, updateStatus(ThingStatus.UNKNOWN, ThingStatusDetail.HANDLER_CONFIGURATION_PENDING,
@ -158,10 +156,18 @@ public class SmartMeterHandler extends BaseThingHandler {
String obisChannelString = SmartMeterBindingConstants.getObisChannelId(obis); String obisChannelString = SmartMeterBindingConstants.getObisChannelId(obis);
Channel channel = thing.getChannel(obisChannelString); Channel channel = thing.getChannel(obisChannelString);
ChannelTypeUID channelTypeId = channelTypeProvider.getChannelTypeIdForObis(obis); ChannelTypeUID channelTypeId = channelTypeProvider.getChannelTypeIdForObis(obis);
if (channelTypeId == null) {
logger.warn("No ChannelTypeId found for OBIS {}", obis);
return;
}
ChannelType channelType = channelTypeProvider.getChannelType(channelTypeId, null); ChannelType channelType = channelTypeProvider.getChannelType(channelTypeId, null);
if (channelType != null) { if (channelType == null) {
logger.warn("No ChannelType found for OBIS {}", obis);
return;
}
String itemType = channelType.getItemType(); String itemType = channelType.getItemType();
State state = getStateForObisValue(value, channel); State state = getStateForObisValue(value, channel);
@ -202,9 +208,6 @@ public class SmartMeterHandler extends BaseThingHandler {
} }
updateStatus(ThingStatus.ONLINE, ThingStatusDetail.NONE); updateStatus(ThingStatus.ONLINE, ThingStatusDetail.NONE);
} else {
logger.warn("No ChannelType found for OBIS {}", obis);
}
} }
private void addObisPropertyToChannel(String obis, Channel channel) { private void addObisPropertyToChannel(String obis, Channel channel) {
@ -238,8 +241,7 @@ public class SmartMeterHandler extends BaseThingHandler {
this.smlDevice.addValueChangeListener(valueChangeListener); this.smlDevice.addValueChangeListener(valueChangeListener);
SmartMeterConfiguration config = getConfigAs(SmartMeterConfiguration.class); SmartMeterConfiguration config = getConfigAs(SmartMeterConfiguration.class);
int delay = config.refresh != null ? config.refresh : DEFAULT_REFRESH_PERIOD; valueReader = this.smlDevice.readValues(DEFAULT_TIMEOUT, this.scheduler, Duration.ofSeconds(config.refresh));
valueReader = this.smlDevice.readValues(DEFAULT_TIMEOUT, this.scheduler, Duration.ofSeconds(delay));
} }
private void updateOBISChannel(ChannelUID channelId) { private void updateOBISChannel(ChannelUID channelId) {

View File

@ -28,7 +28,6 @@ import org.openhab.core.library.unit.Units;
import org.openhab.core.thing.Channel; import org.openhab.core.thing.Channel;
import org.openhab.core.thing.Thing; import org.openhab.core.thing.Thing;
import org.openhab.core.types.State; import org.openhab.core.types.State;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
/** /**
@ -89,7 +88,8 @@ public enum Conformity {
} }
} }
} catch (Exception e) { } catch (Exception e) {
logger.warn("Failed to check negate status for obis {}", obis, e); LoggerFactory.getLogger(Conformity.class)
.warn("Failed to check negate status for obis {}", obis, e);
} }
} }
} }
@ -99,8 +99,6 @@ public enum Conformity {
} }
}; };
private static final Logger logger = LoggerFactory.getLogger(Conformity.class);
/** /**
* Applies the overwritten negation setting for the channel. * Applies the overwritten negation setting for the channel.
* *

View File

@ -17,7 +17,6 @@ import java.util.function.Function;
import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.smartmeter.internal.MeterValue; import org.openhab.binding.smartmeter.internal.MeterValue;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
/** /**
@ -28,7 +27,6 @@ import org.slf4j.LoggerFactory;
*/ */
@NonNullByDefault @NonNullByDefault
public class NegateHandler { public class NegateHandler {
private static final Logger LOGGER = LoggerFactory.getLogger(NegateHandler.class);
/** /**
* Gets whether negation should be applied for the given <code>negateProperty</code> and the {@link MeterValue} * Gets whether negation should be applied for the given <code>negateProperty</code> and the {@link MeterValue}
@ -71,7 +69,8 @@ public class NegateHandler {
try { try {
longValue = (long) Double.parseDouble(value); longValue = (long) Double.parseDouble(value);
} catch (NumberFormatException e) { } catch (NumberFormatException e) {
LOGGER.warn("Failed to parse value: {} when determining isNegateSet, assuming false", value); LoggerFactory.getLogger(NegateHandler.class)
.warn("Failed to parse value: {} when determining isNegateSet, assuming false", value);
return false; return false;
} }
return (longValue & (1L << negatePosition)) != 0; return (longValue & (1L << negatePosition)) != 0;

View File

@ -16,7 +16,6 @@ import java.util.function.Supplier;
import javax.measure.Quantity; import javax.measure.Quantity;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.smartmeter.connectors.IMeterReaderConnector; import org.openhab.binding.smartmeter.connectors.IMeterReaderConnector;
@ -34,11 +33,10 @@ import org.openmuc.j62056.DataSet;
* *
*/ */
@NonNullByDefault @NonNullByDefault
public class Iec62056_21MeterReader extends MeterDevice<DataMessage> { public class MeterReader extends MeterDevice<DataMessage> {
public Iec62056_21MeterReader(Supplier<SerialPortManager> serialPortManagerSupplier, String deviceId, public MeterReader(Supplier<SerialPortManager> serialPortManagerSupplier, String deviceId, String serialPort,
String serialPort, byte @Nullable [] initMessage, int baudrate, int baudrateChangeDelay, byte @Nullable [] initMessage, int baudrate, int baudrateChangeDelay, ProtocolMode protocolMode) {
ProtocolMode protocolMode) {
super(serialPortManagerSupplier, deviceId, serialPort, initMessage, baudrate, baudrateChangeDelay, super(serialPortManagerSupplier, deviceId, serialPort, initMessage, baudrate, baudrateChangeDelay,
protocolMode); protocolMode);
} }
@ -46,17 +44,15 @@ public class Iec62056_21MeterReader extends MeterDevice<DataMessage> {
@Override @Override
protected IMeterReaderConnector<DataMessage> createConnector(Supplier<SerialPortManager> serialPortManagerSupplier, protected IMeterReaderConnector<DataMessage> createConnector(Supplier<SerialPortManager> serialPortManagerSupplier,
String serialPort, int baudrate, int baudrateChangeDelay, ProtocolMode protocolMode) { String serialPort, int baudrate, int baudrateChangeDelay, ProtocolMode protocolMode) {
return new Iec62056_21SerialConnector(serialPortManagerSupplier, serialPort, baudrate, baudrateChangeDelay, return new SerialConnector(serialPortManagerSupplier, serialPort, baudrate, baudrateChangeDelay, protocolMode);
protocolMode);
} }
@Override @Override
protected <Q extends @NonNull Quantity<Q>> void populateValueCache(DataMessage smlFile) { protected <Q extends Quantity<Q>> void populateValueCache(DataMessage smlFile) {
for (DataSet dataSet : smlFile.getDataSets()) { for (DataSet dataSet : smlFile.getDataSets()) {
String address = dataSet.getAddress(); String address = dataSet.getAddress();
if (address != null && !address.isEmpty()) { if (address != null && !address.isEmpty()) {
addObisCache(new MeterValue<Q>(address, dataSet.getValue(), addObisCache(new MeterValue<Q>(address, dataSet.getValue(), UnitConversion.getUnit(dataSet.getUnit())));
Iec62056_21UnitConversion.getUnit(dataSet.getUnit())));
} }
} }
} }

View File

@ -40,17 +40,17 @@ import io.reactivex.FlowableEmitter;
* *
*/ */
@NonNullByDefault @NonNullByDefault
public class Iec62056_21SerialConnector extends ConnectorBase<DataMessage> { public class SerialConnector extends ConnectorBase<DataMessage> {
private final Logger logger = LoggerFactory.getLogger(Iec62056_21SerialConnector.class); private final Logger logger = LoggerFactory.getLogger(SerialConnector.class);
private int baudrate; private int baudrate;
private int baudrateChangeDelay; private int baudrateChangeDelay;
private ProtocolMode protocolMode; private ProtocolMode protocolMode;
@Nullable @Nullable
private Iec21Port iec21Port; private Iec21Port iec21Port;
public Iec62056_21SerialConnector(Supplier<SerialPortManager> serialPortManagerSupplier, String portName, public SerialConnector(Supplier<SerialPortManager> serialPortManagerSupplier, String portName, int baudrate,
int baudrate, int baudrateChangeDelay, ProtocolMode protocolMode) { int baudrateChangeDelay, ProtocolMode protocolMode) {
super(portName); super(portName);
this.baudrate = baudrate; this.baudrate = baudrate;
this.baudrateChangeDelay = baudrateChangeDelay; this.baudrateChangeDelay = baudrateChangeDelay;
@ -78,6 +78,7 @@ public class Iec62056_21SerialConnector extends ConnectorBase<DataMessage> {
@Override @Override
protected DataMessage readNext(byte @Nullable [] initMessage) throws IOException { protected DataMessage readNext(byte @Nullable [] initMessage) throws IOException {
Iec21Port iec21Port = this.iec21Port;
if (iec21Port != null) { if (iec21Port != null) {
DataMessage dataMessage = iec21Port.read(); DataMessage dataMessage = iec21Port.read();
logger.debug("Datamessage read: {}", dataMessage); logger.debug("Datamessage read: {}", dataMessage);
@ -94,6 +95,7 @@ public class Iec62056_21SerialConnector extends ConnectorBase<DataMessage> {
super.emitValues(initMessage, emitter); super.emitValues(initMessage, emitter);
break; break;
case D: case D:
Iec21Port iec21Port = this.iec21Port;
if (iec21Port != null) { if (iec21Port != null) {
iec21Port.listen(new ModeDListener() { iec21Port.listen(new ModeDListener() {
@ -108,6 +110,7 @@ public class Iec62056_21SerialConnector extends ConnectorBase<DataMessage> {
logger.warn("Exception while listening for mode D data message", e); logger.warn("Exception while listening for mode D data message", e);
} }
}); });
this.iec21Port = iec21Port;
} }
break; break;
case SML: case SML:
@ -128,6 +131,7 @@ public class Iec62056_21SerialConnector extends ConnectorBase<DataMessage> {
@Override @Override
public void closeConnection() { public void closeConnection() {
Iec21Port iec21Port = this.iec21Port;
if (iec21Port != null) { if (iec21Port != null) {
iec21Port.close(); iec21Port.close();
} }

View File

@ -18,7 +18,6 @@ import javax.measure.Unit;
import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jdt.annotation.Nullable;
import org.openhab.core.types.util.UnitUtils; import org.openhab.core.types.util.UnitUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
/** /**
@ -28,9 +27,7 @@ import org.slf4j.LoggerFactory;
* *
*/ */
@NonNullByDefault @NonNullByDefault
public class Iec62056_21UnitConversion { public class UnitConversion {
private static final Logger logger = LoggerFactory.getLogger(Iec62056_21UnitConversion.class);
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public static @Nullable <Q extends Quantity<Q>> Unit<Q> getUnit(String unit) { public static @Nullable <Q extends Quantity<Q>> Unit<Q> getUnit(String unit) {
@ -38,7 +35,7 @@ public class Iec62056_21UnitConversion {
try { try {
return (Unit<Q>) UnitUtils.parseUnit(" " + unit); return (Unit<Q>) UnitUtils.parseUnit(" " + unit);
} catch (Exception e) { } catch (Exception e) {
logger.warn("Failed to parse unit {}: {}", unit, e.getMessage()); LoggerFactory.getLogger(UnitConversion.class).warn("Failed to parse unit {}: {}", unit, e.getMessage());
return null; return null;
} }
} }

View File

@ -105,48 +105,48 @@ public class SmlFileDebugOutput {
private static void parseGetListResponse(SmlMessage smlMessage, Consumer<String> consumer) { private static void parseGetListResponse(SmlMessage smlMessage, Consumer<String> consumer) {
consumer.accept("Got GetListResponse"); consumer.accept("Got GetListResponse");
SmlGetListRes sml_listRes = (SmlGetListRes) smlMessage.getMessageBody().getChoice(); SmlGetListRes smlListRes = (SmlGetListRes) smlMessage.getMessageBody().getChoice();
// consumer.accept(sml_listRes.toString()); // consumer.accept(sml_listRes.toString());
consumer.accept(sml_listRes.toStringIndent(" ")); consumer.accept(smlListRes.toStringIndent(" "));
} }
private static void parseAttentionResponse(SmlMessage smlMessage, Consumer<String> consumer) { private static void parseAttentionResponse(SmlMessage smlMessage, Consumer<String> consumer) {
consumer.accept("Got AttentionResponse"); consumer.accept("Got AttentionResponse");
SmlAttentionRes sml_attentionRes = (SmlAttentionRes) smlMessage.getMessageBody().getChoice(); SmlAttentionRes smlAttentionRes = (SmlAttentionRes) smlMessage.getMessageBody().getChoice();
consumer.accept(sml_attentionRes.toString()); consumer.accept(smlAttentionRes.toString());
} }
private static void parseGetProcParameterResponse(SmlMessage smlMessage, Consumer<String> consumer) { private static void parseGetProcParameterResponse(SmlMessage smlMessage, Consumer<String> consumer) {
consumer.accept("Got GetProcParameterResponse"); consumer.accept("Got GetProcParameterResponse");
SmlGetProcParameterRes sml_getProcParameterRes = (SmlGetProcParameterRes) smlMessage.getMessageBody() SmlGetProcParameterRes smlGetProcParameterRes = (SmlGetProcParameterRes) smlMessage.getMessageBody()
.getChoice(); .getChoice();
consumer.accept(sml_getProcParameterRes.toString()); consumer.accept(smlGetProcParameterRes.toString());
} }
private static void parseGetProfileListResponse(SmlMessage smlMessage, Consumer<String> consumer) { private static void parseGetProfileListResponse(SmlMessage smlMessage, Consumer<String> consumer) {
consumer.accept("Got GetProfileListResponse"); consumer.accept("Got GetProfileListResponse");
SmlGetProfileListRes sml_getProfileListRes = (SmlGetProfileListRes) smlMessage.getMessageBody().getChoice(); SmlGetProfileListRes smlGetProfileListRes = (SmlGetProfileListRes) smlMessage.getMessageBody().getChoice();
consumer.accept(sml_getProfileListRes.toString()); consumer.accept(smlGetProfileListRes.toString());
} }
private static void parseOpenResponse(SmlMessage smlMessage, Consumer<String> consumer) { private static void parseOpenResponse(SmlMessage smlMessage, Consumer<String> consumer) {
consumer.accept("Got OpenResponse"); consumer.accept("Got OpenResponse");
SmlPublicOpenRes sml_PublicOpenRes = (SmlPublicOpenRes) smlMessage.getMessageBody().getChoice(); SmlPublicOpenRes smlPublicOpenRes = (SmlPublicOpenRes) smlMessage.getMessageBody().getChoice();
consumer.accept(sml_PublicOpenRes.toString()); consumer.accept(smlPublicOpenRes.toString());
} }
private static void parseCloseResponse(SmlMessage smlMessage, Consumer<String> consumer) { private static void parseCloseResponse(SmlMessage smlMessage, Consumer<String> consumer) {
consumer.accept("Got CloseResponse"); consumer.accept("Got CloseResponse");
SmlPublicCloseRes sml_PublicCloseRes = (SmlPublicCloseRes) smlMessage.getMessageBody().getChoice(); SmlPublicCloseRes smlPublicCloseRes = (SmlPublicCloseRes) smlMessage.getMessageBody().getChoice();
consumer.accept(sml_PublicCloseRes.toString()); consumer.accept(smlPublicCloseRes.toString());
} }
private static void parseGetProfilePackResponse(SmlMessage smlMessage, Consumer<String> consumer) { private static void parseGetProfilePackResponse(SmlMessage smlMessage, Consumer<String> consumer) {
consumer.accept("Got GetProfilePackResponse"); consumer.accept("Got GetProfilePackResponse");
SmlGetProfilePackRes sml_getProfilePackRes = (SmlGetProfilePackRes) smlMessage.getMessageBody().getChoice(); SmlGetProfilePackRes smlGetProfilePackRes = (SmlGetProfilePackRes) smlMessage.getMessageBody().getChoice();
consumer.accept(sml_getProfilePackRes.toString()); consumer.accept(smlGetProfilePackRes.toString());
} }
// ========================= Requests ================================= // ========================= Requests =================================

View File

@ -81,6 +81,7 @@ public final class SmlSerialConnector extends ConnectorBase<SmlFile> {
protected SmlFile readNext(byte @Nullable [] initMessage) throws IOException { protected SmlFile readNext(byte @Nullable [] initMessage) throws IOException {
if (initMessage != null) { if (initMessage != null) {
logger.debug("Writing init message: {}", HexUtils.bytesToHex(initMessage, " ")); logger.debug("Writing init message: {}", HexUtils.bytesToHex(initMessage, " "));
DataOutputStream os = this.os;
if (os != null) { if (os != null) {
os.write(initMessage); os.write(initMessage);
os.flush(); os.flush();
@ -89,6 +90,7 @@ public final class SmlSerialConnector extends ConnectorBase<SmlFile> {
// read out the whole buffer. We are only interested in the most recent SML file. // read out the whole buffer. We are only interested in the most recent SML file.
Stack<SmlFile> smlFiles = new Stack<>(); Stack<SmlFile> smlFiles = new Stack<>();
DataInputStream is = this.is;
do { do {
logger.trace("Reading {}. SML message", smlFiles.size() + 1); logger.trace("Reading {}. SML message", smlFiles.size() + 1);
smlFiles.push(TRANSPORT.getSMLFile(is)); smlFiles.push(TRANSPORT.getSMLFile(is));
@ -137,12 +139,10 @@ public final class SmlSerialConnector extends ConnectorBase<SmlFile> {
} }
} }
/**
* {@inheritDoc}
*/
@Override @Override
public void closeConnection() { public void closeConnection() {
try { try {
DataInputStream is = this.is;
if (is != null) { if (is != null) {
is.close(); is.close();
is = null; is = null;
@ -151,6 +151,7 @@ public final class SmlSerialConnector extends ConnectorBase<SmlFile> {
logger.error("Failed to close serial input stream", e); logger.error("Failed to close serial input stream", e);
} }
try { try {
DataOutputStream os = this.os;
if (os != null) { if (os != null) {
os.close(); os.close();
os = null; os = null;

View File

@ -15,6 +15,8 @@ package org.openhab.binding.smartmeter;
import java.io.IOException; import java.io.IOException;
import java.util.function.Supplier; import java.util.function.Supplier;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.smartmeter.connectors.ConnectorBase; import org.openhab.binding.smartmeter.connectors.ConnectorBase;
/** /**
@ -22,6 +24,7 @@ import org.openhab.binding.smartmeter.connectors.ConnectorBase;
* @author Matthias Steigenberger - Initial contribution * @author Matthias Steigenberger - Initial contribution
* *
*/ */
@NonNullByDefault
public class MockMeterReaderConnector extends ConnectorBase<Object> { public class MockMeterReaderConnector extends ConnectorBase<Object> {
private boolean applyRetry; private boolean applyRetry;
@ -42,12 +45,12 @@ public class MockMeterReaderConnector extends ConnectorBase<Object> {
} }
@Override @Override
protected Object readNext(byte[] initMessage) throws IOException { protected Object readNext(byte @Nullable [] initMessage) throws IOException {
try { try {
return readNextSupplier.get(); return readNextSupplier.get();
} catch (RuntimeException e) { } catch (RuntimeException e) {
if (e.getCause() instanceof IOException) { if (e.getCause() instanceof IOException cause) {
throw (IOException) e.getCause(); throw cause;
} }
throw e; throw e;
} }

View File

@ -23,7 +23,7 @@ import java.util.function.Supplier;
import javax.measure.Quantity; import javax.measure.Quantity;
import org.eclipse.jdt.annotation.NonNull; import org.eclipse.jdt.annotation.NonNullByDefault;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.mockito.ArgumentMatchers; import org.mockito.ArgumentMatchers;
import org.mockito.Mockito; import org.mockito.Mockito;
@ -44,6 +44,7 @@ import io.reactivex.plugins.RxJavaPlugins;
* @author Matthias Steigenberger - Initial contribution * @author Matthias Steigenberger - Initial contribution
* *
*/ */
@NonNullByDefault
public class TestMeterReading { public class TestMeterReading {
@Test @Test
@ -118,6 +119,7 @@ public class TestMeterReading {
throw new RuntimeException(new IOException("fucked up")); throw new RuntimeException(new IOException("fucked up"));
})); }));
MeterDevice<Object> meter = getMeterDevice(connector); MeterDevice<Object> meter = getMeterDevice(connector);
@SuppressWarnings("unchecked")
Consumer<Throwable> errorHandler = mock(Consumer.class); Consumer<Throwable> errorHandler = mock(Consumer.class);
RxJavaPlugins.setErrorHandler(errorHandler); RxJavaPlugins.setErrorHandler(errorHandler);
MeterValueListener changeListener = Mockito.mock(MeterValueListener.class); MeterValueListener changeListener = Mockito.mock(MeterValueListener.class);
@ -139,14 +141,15 @@ public class TestMeterReading {
return new MeterDevice<>(() -> mock(SerialPortManager.class), "id", "port", null, 9600, 0, ProtocolMode.SML) { return new MeterDevice<>(() -> mock(SerialPortManager.class), "id", "port", null, 9600, 0, ProtocolMode.SML) {
@Override @Override
protected @NonNull IMeterReaderConnector<Object> createConnector( protected IMeterReaderConnector<Object> createConnector(
@NonNull Supplier<@NonNull SerialPortManager> serialPortManagerSupplier, @NonNull String serialPort, Supplier<SerialPortManager> serialPortManagerSupplier, String serialPort, int baudrate,
int baudrate, int baudrateChangeDelay, @NonNull ProtocolMode protocolMode) { int baudrateChangeDelay, ProtocolMode protocolMode) {
return connector; return connector;
} }
@SuppressWarnings({ "rawtypes", "unchecked" })
@Override @Override
protected <Q extends @NonNull Quantity<Q>> void populateValueCache(Object smlFile) { protected <Q extends Quantity<Q>> void populateValueCache(Object smlFile) {
addObisCache(new MeterValue("123", "333", null)); addObisCache(new MeterValue("123", "333", null));
} }
}; };

View File

@ -14,6 +14,7 @@ package org.openhab.binding.smartmeter;
import static org.junit.jupiter.api.Assertions.*; import static org.junit.jupiter.api.Assertions.*;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.openhab.binding.smartmeter.internal.MeterValue; import org.openhab.binding.smartmeter.internal.MeterValue;
import org.openhab.binding.smartmeter.internal.conformity.negate.NegateBitModel; import org.openhab.binding.smartmeter.internal.conformity.negate.NegateBitModel;
@ -25,6 +26,7 @@ import org.openhab.binding.smartmeter.internal.conformity.negate.NegateHandler;
* @author Matthias Steigenberger - Initial contribution * @author Matthias Steigenberger - Initial contribution
* *
*/ */
@NonNullByDefault
public class TestNegateBit { public class TestNegateBit {
@Test @Test