diff --git a/pom.xml b/pom.xml index d89b013..b690daa 100644 --- a/pom.xml +++ b/pom.xml @@ -7,13 +7,16 @@ test.com 1.0-SNAPSHOT - com.fasterxml.jackson.core jackson-databind 2.11.1 - + + org.slf4j + slf4j-api + 1.7.32 + com.google.guava guava diff --git a/src/main/java/home/automation/modbus/ModbusCachedWrapper.java b/src/main/java/home/automation/modbus/ModbusCachedWrapper.java index 71716e3..983b84f 100644 --- a/src/main/java/home/automation/modbus/ModbusCachedWrapper.java +++ b/src/main/java/home/automation/modbus/ModbusCachedWrapper.java @@ -2,25 +2,27 @@ package home.automation.modbus; import com.google.common.cache.Cache; import com.google.common.cache.CacheBuilder; +import lombok.extern.slf4j.Slf4j; +import java.time.Duration; import java.util.Collections; import java.util.List; import java.util.concurrent.ExecutionException; -import java.util.concurrent.TimeUnit; -public class ModbusCachedWrapper implements ModbusRepository { +@Slf4j +public class ModbusCachedWrapper implements Repository { private final Cache repositoryCache; private final Cache> repositoryCacheAllElements; - private final ModbusRepository repository; + private final Repository repository; public static final int ALL_ELEMENTS = 1; - public ModbusCachedWrapper(ModbusRepository repository) { + public ModbusCachedWrapper(Repository repository, Duration prevenstionTime) { this.repository = repository; repositoryCacheAllElements = CacheBuilder.newBuilder() - .expireAfterWrite(1, TimeUnit.MINUTES) + .expireAfterWrite(prevenstionTime) .build(); repositoryCache = CacheBuilder.newBuilder() - .expireAfterWrite(1, TimeUnit.MINUTES) + .expireAfterWrite(prevenstionTime) .build(); } @@ -29,7 +31,7 @@ public class ModbusCachedWrapper implements ModbusRepository { try { return repositoryCacheAllElements.get(ALL_ELEMENTS, repository::getAll); } catch (ExecutionException e) { - e.printStackTrace(); + log.error("Cannot get all keys due to: ", e); return Collections.singletonList(Registry.builder().registry(0).desc("").value("").build()); } } @@ -39,7 +41,7 @@ public class ModbusCachedWrapper implements ModbusRepository { try { return repositoryCache.get(register, () -> repository.get(register)); } catch (ExecutionException e) { - e.printStackTrace(); + log.error("Cannot get" + register + "due to: ", e); return Registry.builder().registry(0).desc("").value("").build(); } } diff --git a/src/main/java/home/automation/modbus/ModbusConnection.java b/src/main/java/home/automation/modbus/ModbusConnection.java index c4c079a..00484f6 100644 --- a/src/main/java/home/automation/modbus/ModbusConnection.java +++ b/src/main/java/home/automation/modbus/ModbusConnection.java @@ -16,8 +16,8 @@ public class ModbusConnection { public ModbusConnection(ConfigurationStrategy config) { this.config = config; - Runtime.getRuntime().addShutdownHook(new Thread(()->{ - if(Objects.nonNull(connection)){ + Runtime.getRuntime().addShutdownHook(new Thread(() -> { + if (Objects.nonNull(connection)) { connection.destroy(); } })); @@ -29,7 +29,7 @@ public class ModbusConnection { .orElseThrow(() -> new RuntimeException("Missing configuration")); } - public ModbusMaster getConnection() throws Exception { + synchronized public ModbusMaster getConnection() throws Exception { if (isNull(connection) || !connection.isConnected()) { if (Objects.nonNull(connection)) { connection.destroy(); diff --git a/src/main/java/home/automation/modbus/ModbusRtuTcpRepository.java b/src/main/java/home/automation/modbus/ModbusRtuTcpRepository.java index 3e9612b..06bf731 100644 --- a/src/main/java/home/automation/modbus/ModbusRtuTcpRepository.java +++ b/src/main/java/home/automation/modbus/ModbusRtuTcpRepository.java @@ -14,10 +14,10 @@ import java.util.stream.Collectors; import static com.serotonin.modbus4j.code.DataType.TWO_BYTE_INT_SIGNED; -public class ModbusRtuTcpRepository implements ModbusRepository { +public class ModbusRtuTcpRepository implements Repository { - public static final int ZERO = 0; - public static final int HALF_SHORT_INT = 32768; + private static final int ZERO = 0; + private static final int HALF_SHORT_INT = 32768; private final ModbusConnection connection; private final List registryMappings; @@ -63,7 +63,7 @@ public class ModbusRtuTcpRepository implements ModbusRepository { @Override public Registry get(int register) { try { - ReadHoldingRegistersResponse response = gerRegistry(register); + ReadHoldingRegistersResponse response = getRegistry(register); final String value = normalizeRegistryValue(response.getShortData()[0]); final RegistryMapping mapping = findRegistryMapping(register); @@ -77,7 +77,7 @@ public class ModbusRtuTcpRepository implements ModbusRepository { } @NotNull - private ReadHoldingRegistersResponse gerRegistry(int register) throws Exception { + private ReadHoldingRegistersResponse getRegistry(int register) throws Exception { ReadHoldingRegistersRequest request = new ReadHoldingRegistersRequest(1, register, 1); ReadHoldingRegistersResponse response = (ReadHoldingRegistersResponse) connection.getConnection().send(request); diff --git a/src/main/java/home/automation/modbus/ModbusService.java b/src/main/java/home/automation/modbus/ModbusService.java index 31dd3bc..56a246d 100644 --- a/src/main/java/home/automation/modbus/ModbusService.java +++ b/src/main/java/home/automation/modbus/ModbusService.java @@ -4,9 +4,9 @@ import java.util.List; public class ModbusService { - private final ModbusRtuTcpRepository repository; + private final Repository repository; - public ModbusService(ModbusRtuTcpRepository repository) { + public ModbusService(Repository repository) { this.repository = repository; } diff --git a/src/main/java/home/automation/modbus/ModbusRepository.java b/src/main/java/home/automation/modbus/Repository.java similarity index 77% rename from src/main/java/home/automation/modbus/ModbusRepository.java rename to src/main/java/home/automation/modbus/Repository.java index f227170..bb095eb 100644 --- a/src/main/java/home/automation/modbus/ModbusRepository.java +++ b/src/main/java/home/automation/modbus/Repository.java @@ -2,7 +2,7 @@ package home.automation.modbus; import java.util.List; -public interface ModbusRepository { +public interface Repository { List getAll(); Registry get(int register); diff --git a/src/test/java/home/automation/configuration/JsonFileBasedConfigurationTest.java b/src/test/java/home/automation/configuration/JsonFileBasedConfigurationTest.java index 25aacea..b9c1e88 100644 --- a/src/test/java/home/automation/configuration/JsonFileBasedConfigurationTest.java +++ b/src/test/java/home/automation/configuration/JsonFileBasedConfigurationTest.java @@ -11,6 +11,5 @@ class JsonFileBasedConfigurationTest { ConfigurationStrategy configuration = new JsonFileBasedConfiguration(); final Optional config = configuration.getConfig(); assert (config.isPresent()); - System.out.println(config.get()); } } \ No newline at end of file diff --git a/src/test/java/home/automation/modbus/ModbusCachedWrapperTest.java b/src/test/java/home/automation/modbus/ModbusCachedWrapperTest.java new file mode 100644 index 0000000..ab170f0 --- /dev/null +++ b/src/test/java/home/automation/modbus/ModbusCachedWrapperTest.java @@ -0,0 +1,81 @@ +package home.automation.modbus; + +import org.junit.jupiter.api.Test; + +import java.time.Duration; +import java.util.List; + +import static java.lang.Thread.sleep; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; + +class ModbusCachedWrapperTest { + + public static final int MAX_TESTS = 10; + private final RepositoryInMemory repository = new RepositoryInMemory(); + private final ModbusCachedWrapper cachedRepository = new ModbusCachedWrapper(repository, Duration.ofSeconds(1)); + + @Test + public void givenRepositoryInMemoryAndCacheDurationOf1Seconds_WhenGetRegistryFromCachedRepository2times_ThenValuesAreTheSame() { + final Registry firstTry = cachedRepository.get(4065); + final Registry secondTry = cachedRepository.get(4065); + assert (firstTry.getValue().equals(secondTry.getValue())); + } + + @Test + public void givenRepositoryInMemoryAndCacheDurationOf1Seconds_WhenGetRegistryFromCachedRepository2timesWithDelayMoreThan1Sec_ThenValuesAreTheDifferent() throws InterruptedException { + final Registry firstTry = cachedRepository.get(4065); + final long currentTimeMillis = System.currentTimeMillis(); + final long desireTimeoutInMs = 1000; + while (desireTimeoutInMs > System.currentTimeMillis() - currentTimeMillis) { + sleep(510); + } + final Registry secondTry = cachedRepository.get(4065); + assert (!firstTry.getValue().equals(secondTry.getValue())); + } + + @Test + public void givenRepositoryInMemoryAndCacheDurationOf3Seconds_WhenGetRegistryFromCachedRepositoryMultipleTimesWithIn3SecondDelay_ThenAllValuesAreTheSame() throws InterruptedException { + final Registry firstTry = cachedRepository.get(4065); + final long currentTimeMillis = System.currentTimeMillis(); + final long desireTimeoutInMs = 500; + int count = 0; + while (desireTimeoutInMs > System.currentTimeMillis() - currentTimeMillis || count == MAX_TESTS) { + count++; + final Registry secondTry = cachedRepository.get(4065); + assert (firstTry.getValue().equals(secondTry.getValue())); + } + } + + @Test + public void givenRepositoryInMemoryAndCacheDurationOf3Seconds_WhenGetAllFromCachedRepository2times_ThenValuesAreTheSame() { + final List firstTry = cachedRepository.getAll(); + final List secondTry = cachedRepository.getAll(); + assertEquals(firstTry, secondTry); + } + + @Test + public void givenRepositoryInMemoryAndCacheDurationOf3Seconds_WhenGetAllFromCachedRepository2timesWith4SecondDelay_ThenValuesAreTheDifferent() throws InterruptedException { + final List firstTry = cachedRepository.getAll(); + final long currentTimeMillis = System.currentTimeMillis(); + final long desireTimeoutInMs = 1000; + while (desireTimeoutInMs > System.currentTimeMillis() - currentTimeMillis) { + sleep(510); + } + final List secondTry = cachedRepository.getAll(); + assertNotEquals(firstTry, secondTry); + } + + @Test + public void givenRepositoryInMemoryAndCacheDurationOf3Seconds_WhenGetAllFromCachedRepositoryMultipleTimesWithIn3SecondDelay_ThenAllValuesAreTheSame() throws InterruptedException { + final List firstTry = cachedRepository.getAll(); + final long currentTimeMillis = System.currentTimeMillis(); + final long desireTimeoutInMs = 500; + int count = 0; + while (desireTimeoutInMs > System.currentTimeMillis() - currentTimeMillis || count == MAX_TESTS) { + count++; + final List secondTry = cachedRepository.getAll(); + assertEquals(firstTry, secondTry); + } + } +} \ No newline at end of file diff --git a/src/test/java/home/automation/modbus/ModbusServiceTest.java b/src/test/java/home/automation/modbus/ModbusServiceTest.java new file mode 100644 index 0000000..f7c25d7 --- /dev/null +++ b/src/test/java/home/automation/modbus/ModbusServiceTest.java @@ -0,0 +1,28 @@ +package home.automation.modbus; + +import org.junit.jupiter.api.Test; + +import java.util.List; +import java.util.Objects; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class ModbusServiceTest { + + @Test + public void givenRegistryWhenGetRegistryThenValueFromRepositoryIsReturned(){ + ModbusService service = new ModbusService(new RepositoryInMemory()); + int givenRegistry = 4065; + final Registry registry = service.get(givenRegistry); + assertTrue(Objects.nonNull(registry)); + } + + @Test + public void givenNoRegistryWhenGetAllRegistryThenListOfValuesFromRepositoryIsReturned(){ + ModbusService service = new ModbusService(new RepositoryInMemory()); + final List registry = service.readAll(); + assertTrue(Objects.nonNull(registry)); + assertFalse(registry.isEmpty()); + } +} diff --git a/src/test/java/home/automation/modbus/RepositoryInMemory.java b/src/test/java/home/automation/modbus/RepositoryInMemory.java new file mode 100644 index 0000000..6ccc6c6 --- /dev/null +++ b/src/test/java/home/automation/modbus/RepositoryInMemory.java @@ -0,0 +1,31 @@ +package home.automation.modbus; + +import java.util.Arrays; +import java.util.List; +import java.util.Random; +import java.util.stream.Collectors; + +public class RepositoryInMemory implements Repository { + private final List DEFINED_REGISTRY_FOR_TEST = + Arrays.asList(4065, 4070, 4071, 4072, 4067, 4202, 4069, 4203, 4066, 4201, 4199); + + @Override + public List getAll() { + return DEFINED_REGISTRY_FOR_TEST.stream() + .map(this::generateRandomResponse) + .collect(Collectors.toList()); + } + + @Override + public Registry get(int register) { + return generateRandomResponse(register); + } + + private Registry generateRandomResponse(int registry) { + final Random random = new Random(); + return Registry.builder() + .registry(registry) + .value(Double.toString(random.nextDouble())) + .desc("TEST").build(); + } +}