[gce] Avoid conflicts with things file defined (#13526)

* Avoiding dynamic creation of channels when channels already exists

Signed-off-by: clinique <gael@lhopital.org>
This commit is contained in:
Gaël L'hopital 2022-10-10 18:42:49 +02:00 committed by GitHub
parent c63e596680
commit e3d0039675
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 82 additions and 93 deletions

View File

@ -14,7 +14,6 @@ package org.openhab.binding.gce.internal;
import static org.openhab.binding.gce.internal.GCEBindingConstants.IPXV3_THING_TYPE; import static org.openhab.binding.gce.internal.GCEBindingConstants.IPXV3_THING_TYPE;
import java.util.Collections;
import java.util.Set; import java.util.Set;
import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.NonNullByDefault;
@ -36,7 +35,7 @@ import org.osgi.service.component.annotations.Component;
@NonNullByDefault @NonNullByDefault
@Component(service = ThingHandlerFactory.class, configurationPid = "binding.gce") @Component(service = ThingHandlerFactory.class, configurationPid = "binding.gce")
public class GCEHandlerFactory extends BaseThingHandlerFactory { public class GCEHandlerFactory extends BaseThingHandlerFactory {
private static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Collections.singleton(IPXV3_THING_TYPE); private static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Set.of(IPXV3_THING_TYPE);
@Override @Override
public boolean supportsThingType(ThingTypeUID thingTypeUID) { public boolean supportsThingType(ThingTypeUID thingTypeUID) {

View File

@ -42,7 +42,7 @@ public class Ipx800DeviceConnector extends Thread {
private static final String ENDL = "\r\n"; private static final String ENDL = "\r\n";
private final String hostname; private final String hostname;
public final int portNumber; private final int portNumber;
private @Nullable M2MMessageParser parser; private @Nullable M2MMessageParser parser;
private @NonNullByDefault({}) Socket client; private @NonNullByDefault({}) Socket client;

View File

@ -24,6 +24,7 @@ import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ScheduledFuture; import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
@ -79,11 +80,12 @@ public class Ipx800v3Handler extends BaseThingHandler implements Ipx800EventList
private @NonNullByDefault({}) Ipx800Configuration configuration; private @NonNullByDefault({}) Ipx800Configuration configuration;
private @NonNullByDefault({}) Ipx800DeviceConnector connector; private @NonNullByDefault({}) Ipx800DeviceConnector connector;
private @Nullable M2MMessageParser parser;
private @NonNullByDefault({}) StatusFileInterpreter statusFile; private @NonNullByDefault({}) StatusFileInterpreter statusFile;
private @Nullable ScheduledFuture<?> refreshJob;
private final Map<String, PortData> portDatas = new HashMap<>(); private Optional<M2MMessageParser> parser = Optional.empty();
private Optional<ScheduledFuture<?>> refreshJob = Optional.empty();
private final Map<String, @Nullable PortData> portDatas = new HashMap<>();
private class LongPressEvaluator implements Runnable { private class LongPressEvaluator implements Runnable {
private final ZonedDateTime referenceTime; private final ZonedDateTime referenceTime;
@ -97,10 +99,10 @@ public class Ipx800v3Handler extends BaseThingHandler implements Ipx800EventList
} }
@Override @Override
@SuppressWarnings("PMD.CompareObjectsWithEquals")
public void run() { public void run() {
PortData currentData = portDatas.get(port); PortData currentData = portDatas.get(port);
if (currentData != null && currentData.getValue() == 1 && currentData.getTimestamp() == referenceTime) { if (currentData != null && currentData.getValue() == 1
&& referenceTime.equals(currentData.getTimestamp())) {
triggerChannel(eventChannelId, EVENT_LONG_PRESS); triggerChannel(eventChannelId, EVENT_LONG_PRESS);
} }
} }
@ -113,7 +115,6 @@ public class Ipx800v3Handler extends BaseThingHandler implements Ipx800EventList
@Override @Override
public void initialize() { public void initialize() {
configuration = getConfigAs(Ipx800Configuration.class); configuration = getConfigAs(Ipx800Configuration.class);
logger.debug("Initializing IPX800 handler for uid '{}'", getThing().getUID()); logger.debug("Initializing IPX800 handler for uid '{}'", getThing().getUID());
@ -124,55 +125,57 @@ public class Ipx800v3Handler extends BaseThingHandler implements Ipx800EventList
discoverAttributes(); discoverAttributes();
} }
connector = new Ipx800DeviceConnector(configuration.hostname, configuration.portNumber, getThing().getUID());
parser = new M2MMessageParser(connector, this);
updateStatus(ThingStatus.UNKNOWN);
refreshJob = scheduler.scheduleWithFixedDelay(statusFile::read, 3000, configuration.pullInterval,
TimeUnit.MILLISECONDS);
connector.start();
}
@Override
public void dispose() {
if (refreshJob != null) {
refreshJob.cancel(true);
refreshJob = null;
}
if (connector != null) {
connector.destroyAndExit();
}
parser = null;
portDatas.values().stream().forEach(portData -> {
portData.destroy();
});
super.dispose();
}
protected void discoverAttributes() {
final Map<String, String> properties = new HashMap<>();
properties.put(Thing.PROPERTY_VENDOR, "GCE Electronics");
properties.put(Thing.PROPERTY_FIRMWARE_VERSION, statusFile.getElement(StatusEntry.VERSION));
properties.put(Thing.PROPERTY_MAC_ADDRESS, statusFile.getElement(StatusEntry.CONFIG_MAC));
updateProperties(properties);
ThingBuilder thingBuilder = editThing();
List<Channel> channels = new ArrayList<>(getThing().getChannels()); List<Channel> channels = new ArrayList<>(getThing().getChannels());
ThingBuilder thingBuilder = editThing();
PortDefinition.asStream().forEach(portDefinition -> { PortDefinition.asStream().forEach(portDefinition -> {
int nbElements = statusFile.getMaxNumberofNodeType(portDefinition); int nbElements = statusFile.getMaxNumberofNodeType(portDefinition);
for (int i = 0; i < nbElements; i++) { for (int i = 0; i < nbElements; i++) {
createChannels(portDefinition, i, channels); createChannels(portDefinition, i, channels);
} }
}); });
thingBuilder.withChannels(channels); thingBuilder.withChannels(channels);
updateThing(thingBuilder.build()); updateThing(thingBuilder.build());
connector = new Ipx800DeviceConnector(configuration.hostname, configuration.portNumber, getThing().getUID());
parser = Optional.of(new M2MMessageParser(connector, this));
updateStatus(ThingStatus.UNKNOWN);
refreshJob = Optional.of(scheduler.scheduleWithFixedDelay(statusFile::read, 3000, configuration.pullInterval,
TimeUnit.MILLISECONDS));
connector.start();
}
@Override
public void dispose() {
refreshJob.ifPresent(job -> job.cancel(true));
refreshJob = Optional.empty();
if (connector != null) {
connector.destroyAndExit();
}
parser = Optional.empty();
portDatas.values().stream().forEach(portData -> {
if (portData != null) {
portData.dispose();
}
});
super.dispose();
}
protected void discoverAttributes() {
updateProperties(Map.of(Thing.PROPERTY_VENDOR, "GCE Electronics", Thing.PROPERTY_FIRMWARE_VERSION,
statusFile.getElement(StatusEntry.VERSION), Thing.PROPERTY_MAC_ADDRESS,
statusFile.getElement(StatusEntry.CONFIG_MAC)));
}
private void addIfChannelAbsent(ChannelBuilder channelBuilder, List<Channel> channels) {
Channel newChannel = channelBuilder.build();
if (channels.stream().noneMatch(c -> c.getUID().equals(newChannel.getUID()))) {
channels.add(newChannel);
}
} }
private void createChannels(PortDefinition portDefinition, int portIndex, List<Channel> channels) { private void createChannels(PortDefinition portDefinition, int portIndex, List<Channel> channels) {
@ -184,32 +187,32 @@ public class Ipx800v3Handler extends BaseThingHandler implements Ipx800EventList
ChannelTypeUID channelType = new ChannelTypeUID(BINDING_ID, advancedChannelTypeName); ChannelTypeUID channelType = new ChannelTypeUID(BINDING_ID, advancedChannelTypeName);
switch (portDefinition) { switch (portDefinition) {
case ANALOG: case ANALOG:
channels.add(ChannelBuilder.create(mainChannelUID, CoreItemFactory.NUMBER) addIfChannelAbsent(ChannelBuilder.create(mainChannelUID, CoreItemFactory.NUMBER)
.withLabel("Analog Input " + ndx).withType(channelType).build()); .withLabel("Analog Input " + ndx).withType(channelType), channels);
channels.add(ChannelBuilder addIfChannelAbsent(
.create(new ChannelUID(groupUID, ndx + "-voltage"), "Number:ElectricPotential") ChannelBuilder.create(new ChannelUID(groupUID, ndx + "-voltage"), "Number:ElectricPotential")
.withLabel("Voltage " + ndx).withType(new ChannelTypeUID(BINDING_ID, CHANNEL_VOLTAGE)).build()); .withType(new ChannelTypeUID(BINDING_ID, CHANNEL_VOLTAGE)).withLabel("Voltage " + ndx),
channels);
break; break;
case CONTACT: case CONTACT:
channels.add(ChannelBuilder.create(mainChannelUID, CoreItemFactory.CONTACT).withLabel("Contact " + ndx) addIfChannelAbsent(ChannelBuilder.create(mainChannelUID, CoreItemFactory.CONTACT)
.withType(channelType).build()); .withLabel("Contact " + ndx).withType(channelType), channels);
channels.add(ChannelBuilder.create(new ChannelUID(groupUID, ndx + "-event"), null) addIfChannelAbsent(ChannelBuilder.create(new ChannelUID(groupUID, ndx + "-event"), null)
.withLabel("Contact " + ndx + " Event").withKind(ChannelKind.TRIGGER)
.withType(new ChannelTypeUID(BINDING_ID, TRIGGER_CONTACT + (portIndex < 8 ? "" : "Advanced"))) .withType(new ChannelTypeUID(BINDING_ID, TRIGGER_CONTACT + (portIndex < 8 ? "" : "Advanced")))
.build()); .withLabel("Contact " + ndx + " Event").withKind(ChannelKind.TRIGGER), channels);
break; break;
case COUNTER: case COUNTER:
channels.add(ChannelBuilder.create(mainChannelUID, CoreItemFactory.NUMBER).withLabel("Counter " + ndx) addIfChannelAbsent(ChannelBuilder.create(mainChannelUID, CoreItemFactory.NUMBER)
.withType(channelType).build()); .withLabel("Counter " + ndx).withType(channelType), channels);
break; break;
case RELAY: case RELAY:
channels.add(ChannelBuilder.create(mainChannelUID, CoreItemFactory.SWITCH).withLabel("Relay " + ndx) addIfChannelAbsent(ChannelBuilder.create(mainChannelUID, CoreItemFactory.SWITCH)
.withType(channelType).build()); .withLabel("Relay " + ndx).withType(channelType), channels);
break; break;
} }
channels.add(ChannelBuilder.create(new ChannelUID(groupUID, ndx + "-duration"), "Number:Time") addIfChannelAbsent(ChannelBuilder.create(new ChannelUID(groupUID, ndx + "-duration"), "Number:Time")
.withLabel("Previous state duration " + ndx) .withType(new ChannelTypeUID(BINDING_ID, CHANNEL_LAST_STATE_DURATION))
.withType(new ChannelTypeUID(BINDING_ID, CHANNEL_LAST_STATE_DURATION)).build()); .withLabel("Previous state duration " + ndx), channels);
} }
@Override @Override
@ -227,16 +230,11 @@ public class Ipx800v3Handler extends BaseThingHandler implements Ipx800EventList
if (portDefinition == PortDefinition.ANALOG) { // For analog values, check histeresis if (portDefinition == PortDefinition.ANALOG) { // For analog values, check histeresis
AnalogInputConfiguration config = configuration.as(AnalogInputConfiguration.class); AnalogInputConfiguration config = configuration.as(AnalogInputConfiguration.class);
long hysteresis = config.hysteresis / 2; long hysteresis = config.hysteresis / 2;
if (newValue <= prevValue + hysteresis && newValue >= prevValue - hysteresis) { return (newValue <= prevValue + hysteresis && newValue >= prevValue - hysteresis);
return true; } else if (portDefinition == PortDefinition.CONTACT) { // For contact values, check debounce
}
}
if (portDefinition == PortDefinition.CONTACT) { // For contact values, check debounce
DigitalInputConfiguration config = configuration.as(DigitalInputConfiguration.class); DigitalInputConfiguration config = configuration.as(DigitalInputConfiguration.class);
if (config.debouncePeriod != 0 return (config.debouncePeriod != 0
&& now.isBefore(portData.getTimestamp().plus(config.debouncePeriod, ChronoUnit.MILLIS))) { && now.isBefore(portData.getTimestamp().plus(config.debouncePeriod, ChronoUnit.MILLIS)));
return true;
}
} }
} }
return false; return false;
@ -338,9 +336,7 @@ public class Ipx800v3Handler extends BaseThingHandler implements Ipx800EventList
&& PortDefinition.fromGroupId(groupId) == PortDefinition.RELAY) { && PortDefinition.fromGroupId(groupId) == PortDefinition.RELAY) {
RelayOutputConfiguration config = channel.getConfiguration().as(RelayOutputConfiguration.class); RelayOutputConfiguration config = channel.getConfiguration().as(RelayOutputConfiguration.class);
String id = channelUID.getIdWithoutGroup(); String id = channelUID.getIdWithoutGroup();
if (parser != null) { parser.ifPresent(p -> p.setOutput(id, (OnOffType) command == OnOffType.ON ? 1 : 0, config.pulse));
parser.setOutput(id, (OnOffType) command == OnOffType.ON ? 1 : 0, config.pulse);
}
return; return;
} }
logger.debug("Can not handle command '{}' on channel '{}'", command, channelUID); logger.debug("Can not handle command '{}' on channel '{}'", command, channelUID);
@ -368,20 +364,16 @@ public class Ipx800v3Handler extends BaseThingHandler implements Ipx800EventList
super.channelUnlinked(channelUID); super.channelUnlinked(channelUID);
PortData portData = portDatas.remove(channelUID.getId()); PortData portData = portDatas.remove(channelUID.getId());
if (portData != null) { if (portData != null) {
portData.destroy(); portData.dispose();
} }
} }
public void resetCounter(int counter) { public void resetCounter(int counter) {
if (parser != null) { parser.ifPresent(p -> p.resetCounter(counter));
parser.resetCounter(counter);
}
} }
public void reset() { public void reset() {
if (parser != null) { parser.ifPresent(M2MMessageParser::resetPLC);
parser.resetPLC();
}
} }
@Override @Override

View File

@ -13,10 +13,10 @@
package org.openhab.binding.gce.internal.model; package org.openhab.binding.gce.internal.model;
import java.time.ZonedDateTime; import java.time.ZonedDateTime;
import java.util.Optional;
import java.util.concurrent.ScheduledFuture; import java.util.concurrent.ScheduledFuture;
import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
/** /**
* The {@link PortData} is responsible for holding data regarding current status of a port. * The {@link PortData} is responsible for holding data regarding current status of a port.
@ -27,16 +27,14 @@ import org.eclipse.jdt.annotation.Nullable;
public class PortData { public class PortData {
private double value = -1; private double value = -1;
private ZonedDateTime timestamp = ZonedDateTime.now(); private ZonedDateTime timestamp = ZonedDateTime.now();
private @Nullable ScheduledFuture<?> pulsing; private Optional<ScheduledFuture<?>> pulsing = Optional.empty();
public void cancelPulsing() { public void cancelPulsing() {
if (pulsing != null) { pulsing.ifPresent(pulse -> pulse.cancel(true));
pulsing.cancel(true); pulsing = Optional.empty();
}
pulsing = null;
} }
public void destroy() { public void dispose() {
cancelPulsing(); cancelPulsing();
} }
@ -54,7 +52,7 @@ public class PortData {
} }
public void setPulsing(ScheduledFuture<?> pulsing) { public void setPulsing(ScheduledFuture<?> pulsing) {
this.pulsing = pulsing; this.pulsing = Optional.of(pulsing);
} }
public boolean isInitializing() { public boolean isInitializing() {