Reading Property-Channels at startup
This commit is contained in:
parent
b3d5b3cdd2
commit
3a5cfad535
@ -1,8 +1,11 @@
|
|||||||
package de.ph87.homeautomation.channel;
|
package de.ph87.homeautomation.channel;
|
||||||
|
|
||||||
import de.ph87.homeautomation.property.Property;
|
import de.ph87.homeautomation.property.Property;
|
||||||
|
import de.ph87.homeautomation.property.PropertyReadService;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.boot.context.event.ApplicationStartedEvent;
|
||||||
|
import org.springframework.context.event.EventListener;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
@ -17,6 +20,20 @@ public class ChannelService {
|
|||||||
|
|
||||||
private final List<IChannelOwner> channelOwners;
|
private final List<IChannelOwner> channelOwners;
|
||||||
|
|
||||||
|
private final PropertyReadService propertyReadService;
|
||||||
|
|
||||||
|
@EventListener(ApplicationStartedEvent.class)
|
||||||
|
public void readAllPropertyChannels() {
|
||||||
|
propertyReadService.findAllByReadChannelNotNull().forEach(property -> {
|
||||||
|
final Optional<IChannelOwner> ownerOptional = findByChannel(property.getReadChannel());
|
||||||
|
if (ownerOptional.isPresent()) {
|
||||||
|
ownerOptional.get().read(property.getReadChannel());
|
||||||
|
} else {
|
||||||
|
log.error("No Owner for Property: {}", property);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
public Optional<IChannelOwner> findByChannel(final Channel channel) {
|
public Optional<IChannelOwner> findByChannel(final Channel channel) {
|
||||||
return channelOwners.stream().filter(owner -> channel.getChannelOwnerClass().isInstance(owner)).findFirst();
|
return channelOwners.stream().filter(owner -> channel.getChannelOwnerClass().isInstance(owner)).findFirst();
|
||||||
}
|
}
|
||||||
@ -30,7 +47,7 @@ public class ChannelService {
|
|||||||
if (channel == null) {
|
if (channel == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
getByChannel(channel).write(property, value);
|
getByChannel(channel).write(property.getWriteChannel(), value);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,9 +1,9 @@
|
|||||||
package de.ph87.homeautomation.channel;
|
package de.ph87.homeautomation.channel;
|
||||||
|
|
||||||
import de.ph87.homeautomation.property.Property;
|
|
||||||
|
|
||||||
public interface IChannelOwner {
|
public interface IChannelOwner {
|
||||||
|
|
||||||
void write(final Property property, final double value);
|
void read(final Channel channel);
|
||||||
|
|
||||||
|
void write(final Channel channel, final double value);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -31,6 +31,8 @@ import java.time.ZonedDateTime;
|
|||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
public class KnxThreadService extends AbstractThreadService implements NetworkLinkListener, ProcessListener {
|
public class KnxThreadService extends AbstractThreadService implements NetworkLinkListener, ProcessListener {
|
||||||
|
|
||||||
|
public static final Duration RESPONSE_TIMEOUT = Duration.ofMillis(500);
|
||||||
|
|
||||||
private final KnxGroupWriteService knxGroupWriteService;
|
private final KnxGroupWriteService knxGroupWriteService;
|
||||||
|
|
||||||
private final KnxGroupLinkService knxGroupLinkService;
|
private final KnxGroupLinkService knxGroupLinkService;
|
||||||
@ -89,10 +91,12 @@ public class KnxThreadService extends AbstractThreadService implements NetworkLi
|
|||||||
private void connect() throws KNXException, InterruptedException, IOException {
|
private void connect() throws KNXException, InterruptedException, IOException {
|
||||||
final Inet4Address localAddress = Router.getLocalInet4AddressForRemoteAddress(remoteAddress);
|
final Inet4Address localAddress = Router.getLocalInet4AddressForRemoteAddress(remoteAddress);
|
||||||
log.debug("Connecting KNX link: {} -> {}", localAddress, remoteAddress);
|
log.debug("Connecting KNX link: {} -> {}", localAddress, remoteAddress);
|
||||||
link = KNXNetworkLinkIP.newTunnelingLink(new InetSocketAddress(localAddress, 0), new InetSocketAddress(remoteAddress, 3671), false, new TPSettings());
|
final TPSettings settings = new TPSettings();
|
||||||
|
link = KNXNetworkLinkIP.newTunnelingLink(new InetSocketAddress(localAddress, 0), new InetSocketAddress(remoteAddress, 3671), false, settings);
|
||||||
link.addLinkListener(this);
|
link.addLinkListener(this);
|
||||||
processCommunicator = new ProcessCommunicatorImpl(link);
|
processCommunicator = new ProcessCommunicatorImpl(link);
|
||||||
processCommunicator.addProcessListener(this);
|
processCommunicator.addProcessListener(this);
|
||||||
|
processCommunicator.responseTimeout(RESPONSE_TIMEOUT);
|
||||||
log.info("KNX link established.");
|
log.info("KNX link established.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
package de.ph87.homeautomation.knx.group;
|
package de.ph87.homeautomation.knx.group;
|
||||||
|
|
||||||
|
import de.ph87.homeautomation.channel.Channel;
|
||||||
import de.ph87.homeautomation.channel.IChannelOwner;
|
import de.ph87.homeautomation.channel.IChannelOwner;
|
||||||
import de.ph87.homeautomation.property.Property;
|
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
@ -16,12 +16,21 @@ public class KnxGroupChannelOwnerService implements IChannelOwner {
|
|||||||
private final KnxGroupWriteService knxGroupWriteService;
|
private final KnxGroupWriteService knxGroupWriteService;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void write(final Property property, final double value) {
|
public void read(final Channel channel) {
|
||||||
if (!(property.getWriteChannel() instanceof KnxGroup)) {
|
if (!(channel instanceof KnxGroup)) {
|
||||||
throw new RuntimeException();
|
throw new RuntimeException();
|
||||||
}
|
}
|
||||||
final KnxGroup knxGroup = (KnxGroup) property.getWriteChannel();
|
final KnxGroup knxGroup = (KnxGroup) channel;
|
||||||
knxGroupWriteService.setSendValue(knxGroup, value);
|
knxGroupWriteService.requestRead(knxGroup);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(final Channel channel, final double value) {
|
||||||
|
if (!(channel instanceof KnxGroup)) {
|
||||||
|
throw new RuntimeException();
|
||||||
|
}
|
||||||
|
final KnxGroup knxGroup = (KnxGroup) channel;
|
||||||
|
knxGroupWriteService.requestWrite(knxGroup, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,18 +3,18 @@ package de.ph87.homeautomation.knx.group;
|
|||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
import lombok.ToString;
|
import lombok.ToString;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
import javax.persistence.Embeddable;
|
import javax.persistence.Embeddable;
|
||||||
import java.time.ZonedDateTime;
|
import java.time.ZonedDateTime;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
@Getter
|
@Getter
|
||||||
@Setter
|
@Setter
|
||||||
@ToString
|
@ToString
|
||||||
@Embeddable
|
@Embeddable
|
||||||
public class KnxGroupLinkInfo {
|
public class KnxGroupLinkInfo {
|
||||||
|
|
||||||
private boolean able = true;
|
|
||||||
|
|
||||||
private ZonedDateTime nextTimestamp = null;
|
private ZonedDateTime nextTimestamp = null;
|
||||||
|
|
||||||
private int errorCount = 0;
|
private int errorCount = 0;
|
||||||
|
|||||||
@ -7,12 +7,11 @@ import org.springframework.transaction.annotation.Transactional;
|
|||||||
import tuwien.auto.calimero.GroupAddress;
|
import tuwien.auto.calimero.GroupAddress;
|
||||||
import tuwien.auto.calimero.KNXException;
|
import tuwien.auto.calimero.KNXException;
|
||||||
import tuwien.auto.calimero.KNXFormatException;
|
import tuwien.auto.calimero.KNXFormatException;
|
||||||
|
import tuwien.auto.calimero.KNXTimeoutException;
|
||||||
import tuwien.auto.calimero.datapoint.StateDP;
|
import tuwien.auto.calimero.datapoint.StateDP;
|
||||||
import tuwien.auto.calimero.dptxlator.TranslatorTypes;
|
import tuwien.auto.calimero.dptxlator.TranslatorTypes;
|
||||||
import tuwien.auto.calimero.process.ProcessCommunicatorImpl;
|
import tuwien.auto.calimero.process.ProcessCommunicatorImpl;
|
||||||
|
|
||||||
import java.time.Instant;
|
|
||||||
import java.time.ZoneId;
|
|
||||||
import java.time.ZonedDateTime;
|
import java.time.ZonedDateTime;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
@ -22,38 +21,43 @@ import java.util.Optional;
|
|||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
public class KnxGroupLinkService {
|
public class KnxGroupLinkService {
|
||||||
|
|
||||||
|
public static final long ERROR_DELAY_FACTOR = 500 * 1000 * 1000L;
|
||||||
|
|
||||||
private final KnxGroupRepository knxGroupRepository;
|
private final KnxGroupRepository knxGroupRepository;
|
||||||
|
|
||||||
public boolean sendNext(final ProcessCommunicatorImpl processCommunicator) throws KNXException {
|
public boolean sendNext(final ProcessCommunicatorImpl processCommunicator) throws KNXException {
|
||||||
final Optional<KnxGroup> knxGroupOptional = knxGroupRepository.findFirstBySend_NextTimestampNotNullOrderBySend_NextTimestampAsc();
|
final Optional<KnxGroup> nextOptional = knxGroupRepository.findFirstBySend_NextTimestampNotNullOrderBySend_NextTimestampAsc();
|
||||||
if (knxGroupOptional.isEmpty()) {
|
if (nextOptional.isEmpty()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return send(processCommunicator, knxGroupOptional.get());
|
return send(processCommunicator, nextOptional.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean readNext(final ProcessCommunicatorImpl processCommunicator) throws KNXException, InterruptedException {
|
public boolean readNext(final ProcessCommunicatorImpl processCommunicator) throws KNXException, InterruptedException {
|
||||||
final Optional<KnxGroup> knxGroupOptional = knxGroupRepository.findFirstByRead_NextTimestampLessThanEqualOrderByRead_NextTimestampAsc(ZonedDateTime.now());
|
final Optional<KnxGroup> nextOptional = knxGroupRepository.findFirstByRead_NextTimestampLessThanEqualOrderByRead_NextTimestampAsc(ZonedDateTime.now());
|
||||||
if (knxGroupOptional.isEmpty()) {
|
if (nextOptional.isEmpty()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return read(processCommunicator, knxGroupOptional.get());
|
return read(processCommunicator, nextOptional.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean send(final ProcessCommunicatorImpl processCommunicator, final KnxGroup knxGroup) throws KNXException {
|
private boolean send(final ProcessCommunicatorImpl processCommunicator, final KnxGroup knxGroup) throws KNXException {
|
||||||
try {
|
try {
|
||||||
log.debug("Sending KnxGroup: {}", knxGroup);
|
log.debug("Sending KnxGroup: {}", knxGroup);
|
||||||
processCommunicator.write(knxGroup.getAddress(), TranslatorTypes.createTranslator(knxGroup.getDptMain(), knxGroup.getDptSub(), knxGroup.getSendValue()));
|
processCommunicator.write(knxGroup.getAddress(), TranslatorTypes.createTranslator(knxGroup.getDptMain(), knxGroup.getDptSub(), knxGroup.getSendValue()));
|
||||||
|
if (knxGroup.getSend().getErrorCount() > 0) {
|
||||||
|
log.info("KnxGroup recovered from send-error: {}", knxGroup);
|
||||||
|
}
|
||||||
knxGroup.getSend().setErrorCount(0);
|
knxGroup.getSend().setErrorCount(0);
|
||||||
knxGroup.getSend().setErrorMessage(null);
|
knxGroup.getSend().setErrorMessage(null);
|
||||||
knxGroup.getSend().setNextTimestamp(null);
|
knxGroup.getSend().setNextTimestamp(null);
|
||||||
log.info("Successfully sent KnxGroup: {}", knxGroup);
|
log.debug("Successfully sent KnxGroup: {}", knxGroup);
|
||||||
return true;
|
return true;
|
||||||
} catch (KNXFormatException e) {
|
} catch (KNXTimeoutException | KNXFormatException e) {
|
||||||
log.error(e.toString());
|
log.error("Failed to send KnxGroup {}", knxGroup);
|
||||||
knxGroup.getSend().setErrorCount(knxGroup.getSend().getErrorCount() + 1);
|
knxGroup.getSend().setErrorCount(knxGroup.getSend().getErrorCount() + 1);
|
||||||
knxGroup.getSend().setErrorMessage(e.toString());
|
knxGroup.getSend().setErrorMessage(e.getMessage());
|
||||||
knxGroup.getSend().setNextTimestamp(ZonedDateTime.now().plusSeconds(knxGroup.getSend().getErrorCount()));
|
knxGroup.getSend().setNextTimestamp(ZonedDateTime.now().plusNanos(knxGroup.getSend().getErrorCount() * knxGroup.getSend().getErrorCount() * ERROR_DELAY_FACTOR));
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -62,26 +66,23 @@ public class KnxGroupLinkService {
|
|||||||
try {
|
try {
|
||||||
log.debug("Reading KnxGroup: {}", knxGroup);
|
log.debug("Reading KnxGroup: {}", knxGroup);
|
||||||
processCommunicator.read(createStateDP(knxGroup));
|
processCommunicator.read(createStateDP(knxGroup));
|
||||||
|
if (knxGroup.getRead().getErrorCount() > 0) {
|
||||||
|
log.info("KnxGroup recovered from read-error: {}", knxGroup);
|
||||||
|
}
|
||||||
knxGroup.getRead().setErrorCount(0);
|
knxGroup.getRead().setErrorCount(0);
|
||||||
knxGroup.getRead().setErrorMessage(null);
|
knxGroup.getRead().setErrorMessage(null);
|
||||||
knxGroup.getRead().setNextTimestamp(null);
|
knxGroup.getRead().setNextTimestamp(null);
|
||||||
|
log.debug("Successfully sent KnxGroup: {}", knxGroup);
|
||||||
return true;
|
return true;
|
||||||
} catch (KNXFormatException e) {
|
} catch (KNXTimeoutException | KNXFormatException e) {
|
||||||
log.error(e.toString());
|
log.error("Failed to read KnxGroup {}", knxGroup);
|
||||||
knxGroup.getRead().setErrorCount(knxGroup.getRead().getErrorCount() + 1);
|
knxGroup.getRead().setErrorCount(knxGroup.getRead().getErrorCount() + 1);
|
||||||
knxGroup.getRead().setErrorMessage(e.toString());
|
knxGroup.getRead().setErrorMessage(e.getMessage());
|
||||||
knxGroup.getRead().setNextTimestamp(ZonedDateTime.now().plusSeconds(knxGroup.getRead().getErrorCount()));
|
knxGroup.getRead().setNextTimestamp(ZonedDateTime.now().plusNanos(knxGroup.getRead().getErrorCount() * knxGroup.getRead().getErrorCount() * ERROR_DELAY_FACTOR));
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private ZonedDateTime align(final int interval) {
|
|
||||||
final ZonedDateTime now = ZonedDateTime.now();
|
|
||||||
final long nextEpochAlignment = (long) Math.ceil(now.toEpochSecond() / (double) interval) * interval;
|
|
||||||
final ZonedDateTime nextUTC = ZonedDateTime.ofInstant(Instant.ofEpochSecond(nextEpochAlignment, 0), ZoneId.of("Z"));
|
|
||||||
return nextUTC.withZoneSameInstant(now.getZone());
|
|
||||||
}
|
|
||||||
|
|
||||||
private StateDP createStateDP(final KnxGroup knxGroup) {
|
private StateDP createStateDP(final KnxGroup knxGroup) {
|
||||||
final GroupAddress groupAddress = knxGroup.getAddress();
|
final GroupAddress groupAddress = knxGroup.getAddress();
|
||||||
return new StateDP(groupAddress, groupAddress.toString(), knxGroup.getDptMain(), knxGroup.getDpt());
|
return new StateDP(groupAddress, groupAddress.toString(), knxGroup.getDptMain(), knxGroup.getDpt());
|
||||||
|
|||||||
@ -26,7 +26,13 @@ public class KnxGroupWriteService {
|
|||||||
|
|
||||||
private final ApplicationEventPublisher applicationEventPublisher;
|
private final ApplicationEventPublisher applicationEventPublisher;
|
||||||
|
|
||||||
public void setSendValue(final KnxGroup knxGroup, final double value) {
|
public void requestRead(final KnxGroup knxGroup) {
|
||||||
|
knxGroup.getRead().setNextTimestamp(ZonedDateTime.now());
|
||||||
|
log.debug("Requesting read for KnxGroup: {}", knxGroup);
|
||||||
|
sendThreadWakeUpEvent();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void requestWrite(final KnxGroup knxGroup, final double value) {
|
||||||
findTranslator(knxGroup).ifPresent(translator -> {
|
findTranslator(knxGroup).ifPresent(translator -> {
|
||||||
try {
|
try {
|
||||||
if (translator instanceof DPTXlatorBoolean) {
|
if (translator instanceof DPTXlatorBoolean) {
|
||||||
@ -38,7 +44,7 @@ public class KnxGroupWriteService {
|
|||||||
}
|
}
|
||||||
knxGroup.getSend().setNextTimestamp(ZonedDateTime.now());
|
knxGroup.getSend().setNextTimestamp(ZonedDateTime.now());
|
||||||
knxGroup.setSendValue(translator.getData());
|
knxGroup.setSendValue(translator.getData());
|
||||||
applicationEventPublisher.publishEvent(new KnxThreadWakeUpEvent());
|
sendThreadWakeUpEvent();
|
||||||
} catch (KNXFormatException e) {
|
} catch (KNXFormatException e) {
|
||||||
log.error("Failed set value \"{}\" to DptXlator {} for KnxGroup {}", value, translator, knxGroup);
|
log.error("Failed set value \"{}\" to DptXlator {} for KnxGroup {}", value, translator, knxGroup);
|
||||||
}
|
}
|
||||||
@ -56,6 +62,10 @@ public class KnxGroupWriteService {
|
|||||||
translate(knxGroup);
|
translate(knxGroup);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void sendThreadWakeUpEvent() {
|
||||||
|
applicationEventPublisher.publishEvent(new KnxThreadWakeUpEvent());
|
||||||
|
}
|
||||||
|
|
||||||
private void translate(final KnxGroup knxGroup) {
|
private void translate(final KnxGroup knxGroup) {
|
||||||
findTranslator(knxGroup).ifPresent(translator -> {
|
findTranslator(knxGroup).ifPresent(translator -> {
|
||||||
translator.setData(knxGroup.getLastTelegram().getData());
|
translator.setData(knxGroup.getLastTelegram().getData());
|
||||||
|
|||||||
@ -42,4 +42,8 @@ public class PropertyReadService {
|
|||||||
return propertyRepository.findAll().stream().map(propertyMapper::toDto).collect(Collectors.toList());
|
return propertyRepository.findAll().stream().map(propertyMapper::toDto).collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<Property> findAllByReadChannelNotNull() {
|
||||||
|
return propertyRepository.findAllByReadChannelNotNull();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -17,4 +17,6 @@ public interface PropertyRepository extends CrudRepository<Property, Long> {
|
|||||||
|
|
||||||
boolean existsByName(String name);
|
boolean existsByName(String name);
|
||||||
|
|
||||||
|
List<Property> findAllByReadChannelNotNull();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,12 +0,0 @@
|
|||||||
package de.ph87.homeautomation.property;
|
|
||||||
|
|
||||||
import lombok.Data;
|
|
||||||
|
|
||||||
@Data
|
|
||||||
public class PropertySetDto {
|
|
||||||
|
|
||||||
private final String name;
|
|
||||||
|
|
||||||
private final double value;
|
|
||||||
|
|
||||||
}
|
|
||||||
Loading…
Reference in New Issue
Block a user