From be1e17ab3c6c81ffb5bab301c896bee1f1ee8779 Mon Sep 17 00:00:00 2001 From: Miro Wengner Date: Mon, 14 Oct 2024 21:39:09 +0200 Subject: [PATCH 01/15] [75] removal robo application --- .../main/java/com/robo4j/RoboApplication.java | 128 ------------------ .../com/robo4j/RoboApplicationException.java | 34 ----- .../java/com/robo4j/RoboApplicationTests.java | 90 ------------ 3 files changed, 252 deletions(-) delete mode 100644 robo4j-core/src/main/java/com/robo4j/RoboApplication.java delete mode 100644 robo4j-core/src/main/java/com/robo4j/RoboApplicationException.java delete mode 100644 robo4j-core/src/test/java/com/robo4j/RoboApplicationTests.java diff --git a/robo4j-core/src/main/java/com/robo4j/RoboApplication.java b/robo4j-core/src/main/java/com/robo4j/RoboApplication.java deleted file mode 100644 index 5c8d139d..00000000 --- a/robo4j-core/src/main/java/com/robo4j/RoboApplication.java +++ /dev/null @@ -1,128 +0,0 @@ -/* - * Copyright (c) 2014, 2024, Marcus Hirt, Miroslav Wengner - * - * Robo4J is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Robo4J is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Robo4J. If not, see . - */ -package com.robo4j; - -import com.robo4j.scheduler.RoboThreadFactory; -import com.robo4j.util.SystemUtil; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.IOException; -import java.io.InputStream; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.Executors; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.TimeUnit; - -import static com.robo4j.util.SystemUtil.BREAK; -import static com.robo4j.util.SystemUtil.DELIMITER_HORIZONTAL; - -/** - * RoboApplication used for launchWithExit the application from the command line - * - * @author Marcus Hirt (@hirt) - * @author Miroslav Wengner (@miragemiko) - */ -public final class RoboApplication { - private static final Logger LOGGER = LoggerFactory.getLogger(RoboApplication.class); - - private static final class ShutdownThread extends Thread { - - private final CountDownLatch latch; - - private ShutdownThread(CountDownLatch latch) { - this.latch = latch; - } - - @Override - public void run() { - latch.countDown(); - } - } - - private static final ScheduledExecutorService executor = Executors.newScheduledThreadPool(1, - new RoboThreadFactory(new ThreadGroup("Robo4J-Launcher"), "Robo4J-App-", false)); - private static final CountDownLatch appLatch = new CountDownLatch(1); - - - public RoboApplication() { - } - - /** - * the method is called by standalone robo launcher. - * - * @param context robo context - */ - public void launch(RoboContext context) { - // Create a new Launcher thread and then wait for that thread to finish - Runtime.getRuntime().addShutdownHook(new ShutdownThread(appLatch)); - try { - // TODO : review, exception runtime? - Thread daemon = new Thread(() -> { - try { - System.in.read(); - appLatch.countDown(); - } catch (IOException e) { - LOGGER.error("launch", e); - } - - }); - daemon.setName("Robo4J-Launcher-listener"); - daemon.setDaemon(true); - daemon.start(); - context.start(); - - String logo = getBanner(Thread.currentThread().getContextClassLoader()); - LOGGER.info(logo); - LOGGER.info(SystemUtil.printStateReport(context)); - LOGGER.info("Press ..."); - // TODO : introduce timeout - appLatch.await(); - LOGGER.info("Going down..."); - context.shutdown(); - LOGGER.info("Bye!"); - } catch (InterruptedException e) { - throw new RoboApplicationException("unexpected", e); - } - } - - public void launchWithExit(RoboContext context, long delay, TimeUnit timeUnit) { - executor.schedule(() -> Runtime.getRuntime().exit(0), delay, timeUnit); - launch(context); - - } - - public void launchNoExit(RoboContext context, long delay, TimeUnit timeUnit) { - executor.schedule(appLatch::countDown, delay, timeUnit); - launch(context); - } - - private String getBanner(ClassLoader classLoader) { - final InputStream is = classLoader.getResourceAsStream("banner.txt"); - final byte[] logoBytes; - try { - logoBytes = is == null ? new byte[0] : is.readAllBytes(); - } catch (IOException e) { - throw new IllegalStateException("not allowed"); - } - - return new StringBuilder().append(BREAK).append(DELIMITER_HORIZONTAL) - .append(new String(logoBytes)).append(BREAK).append(DELIMITER_HORIZONTAL) - .toString(); - } - -} diff --git a/robo4j-core/src/main/java/com/robo4j/RoboApplicationException.java b/robo4j-core/src/main/java/com/robo4j/RoboApplicationException.java deleted file mode 100644 index f6ae2df0..00000000 --- a/robo4j-core/src/main/java/com/robo4j/RoboApplicationException.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (c) 2014, 2024, Marcus Hirt, Miroslav Wengner - * - * Robo4J is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Robo4J is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Robo4J. If not, see . - */ -package com.robo4j; - -import java.io.Serial; - -/** - * RoboApplicationException {@link RoboApplication} - * - * @author Marcus Hirt (@hirt) - * @author Miroslav Wengner (@miragemiko) - */ -public class RoboApplicationException extends RuntimeException { - @Serial - private static final long serialVersionUID = 1L; - - public RoboApplicationException(String message, Throwable cause) { - super(message, cause); - } -} diff --git a/robo4j-core/src/test/java/com/robo4j/RoboApplicationTests.java b/robo4j-core/src/test/java/com/robo4j/RoboApplicationTests.java deleted file mode 100644 index 001a014e..00000000 --- a/robo4j-core/src/test/java/com/robo4j/RoboApplicationTests.java +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright (c) 2014, 2024, Marcus Hirt, Miroslav Wengner - * - * Robo4J is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Robo4J is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Robo4J. If not, see . - */ -package com.robo4j; - -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Test; - -import java.io.InputStream; -import java.util.concurrent.TimeUnit; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -/** - * RoboApplicationTests contains tests for {@link RoboApplication} - * - * @author Marcus Hirt (@hirt) - * @author Miroslav Wengner (@miragemiko) - */ -@Disabled("individual test") -class RoboApplicationTests { - - /** - * Build and shutdown the system - * - * @throws RoboBuilderException - * unexpected - */ - @Test - void roboApplicationLifeCycleTestNoExit() throws RoboBuilderException { - final InputStream contextIS = Thread.currentThread().getContextClassLoader().getResourceAsStream("test.xml"); - final RoboBuilder builder = new RoboBuilder(); - builder.add(contextIS); - - final RoboContext system = builder.build(); - final RoboApplication roboApp = new RoboApplication(); - roboApp.launchNoExit(system, 3, TimeUnit.SECONDS); - - assertEquals(LifecycleState.SHUTDOWN, system.getState()); - } - - /** - * Build and shutdown the system - * - * @throws RoboBuilderException - * unexpected - */ - @Test - void roboApplicationLifeCycleTestWithExit() throws RoboBuilderException { - final InputStream contextIS = Thread.currentThread().getContextClassLoader().getResourceAsStream("test.xml"); - final RoboBuilder builder = new RoboBuilder(); - builder.add(contextIS); - - final RoboContext system = builder.build(); - final RoboApplication roboApp = new RoboApplication(); - roboApp.launchWithExit(system, 3, TimeUnit.SECONDS); - - System.setSecurityManager(new SecurityManager() { - @Override - public void checkExit(int status) { - assertEquals(Integer.valueOf(0), Integer.valueOf(status)); - } - }); - } - - @Disabled("individual test") - @Test - void roboApplicationLifeCycle() throws RoboBuilderException { - final InputStream contextIS = Thread.currentThread().getContextClassLoader().getResourceAsStream("test.xml"); - final RoboBuilder builder = new RoboBuilder(); - builder.add(contextIS); - - final RoboContext system = builder.build(); - final RoboApplication roboApp = new RoboApplication(); - roboApp.launch(system); - } -} From 105f776968868e8ff7805dade2fb5689bdb027c6 Mon Sep 17 00:00:00 2001 From: Miro Wengner Date: Mon, 21 Oct 2024 23:01:40 +0200 Subject: [PATCH 02/15] [75] partial sleep removal tests improvements --- .../java/com/robo4j/net/MessageClient.java | 6 +- .../java/com/robo4j/units/CounterUnit.java | 91 ++++- .../java/com/robo4j/CounterUnitTests.java | 84 ---- .../java/com/robo4j/RoboBuilderTests.java | 34 +- .../RunnableProcessCounterUnitTests.java | 159 ++++++++ .../com/robo4j/net/MessageServerTest.java | 10 +- .../com/robo4j/net/RemoteContextTests.java | 22 +- .../com/robo4j/units/IntegerConsumer.java | 21 +- .../CalibratedMagnetometerExample.java | 7 +- .../MagnetometerLSM303Example.java | 7 +- .../com/robo4j/hw/rpi/imu/Bno055Example.java | 7 +- .../com/robo4j/hw/rpi/i2c/gps/TitanX1GPS.java | 11 +- .../robo4j/hw/rpi/imu/bno/Bno080Device.java | 2 +- .../imu/bno/impl/AbstractBno080Device.java | 379 +++++++++--------- .../hw/rpi/imu/bno/impl/Bno080SPIDevice.java | 40 +- .../robo4j/hw/rpi/serial/gps/MTK3339GPS.java | 15 +- .../test/units/RoboDatagramClientTest.java | 26 +- .../http/test/units/RoboHttpDynamicTests.java | 11 +- 18 files changed, 560 insertions(+), 372 deletions(-) delete mode 100644 robo4j-core/src/test/java/com/robo4j/CounterUnitTests.java create mode 100644 robo4j-core/src/test/java/com/robo4j/RunnableProcessCounterUnitTests.java diff --git a/robo4j-core/src/main/java/com/robo4j/net/MessageClient.java b/robo4j-core/src/main/java/com/robo4j/net/MessageClient.java index 6e657ef1..3489e543 100644 --- a/robo4j-core/src/main/java/com/robo4j/net/MessageClient.java +++ b/robo4j-core/src/main/java/com/robo4j/net/MessageClient.java @@ -39,10 +39,10 @@ */ public class MessageClient { private static final Logger LOGGER = LoggerFactory.getLogger(MessageClient.class); - public final static String KEY_SO_TIMEOUT = "timeout"; + public final static String KEY_SO_TIMEOUT_MILLS = "timeout"; public final static String KEY_KEEP_ALIVE = "keepAlive"; public final static String KEY_RETRIES = "retries"; - public final static int DEFAULT_SO_TIMEOUT = 2000000; + public final static int DEFAULT_SO_TIMEOUT_MILLS = 2000000; public final static boolean DEFAULT_KEEP_ALIVE = true; /* @@ -128,7 +128,7 @@ public void connect() throws UnknownHostException, IOException { // socket = new Socket(messageServerURI.getHost(), messageServerURI.getPort()); socket = new Socket(messageServerURI.getHost(), messageServerURI.getPort()); socket.setKeepAlive(configuration.getBoolean(KEY_KEEP_ALIVE, DEFAULT_KEEP_ALIVE)); - socket.setSoTimeout(configuration.getInteger(KEY_SO_TIMEOUT, DEFAULT_SO_TIMEOUT)); + socket.setSoTimeout(configuration.getInteger(KEY_SO_TIMEOUT_MILLS, DEFAULT_SO_TIMEOUT_MILLS)); } objectOutputStream = new ObjectOutputStream(new BufferedOutputStream(socket.getOutputStream())); objectOutputStream.writeShort(MessageProtocolConstants.MAGIC); diff --git a/robo4j-core/src/main/java/com/robo4j/units/CounterUnit.java b/robo4j-core/src/main/java/com/robo4j/units/CounterUnit.java index e536aa15..1413ef92 100644 --- a/robo4j-core/src/main/java/com/robo4j/units/CounterUnit.java +++ b/robo4j-core/src/main/java/com/robo4j/units/CounterUnit.java @@ -16,11 +16,17 @@ */ package com.robo4j.units; -import com.robo4j.*; +import com.robo4j.AttributeDescriptor; +import com.robo4j.ConfigurationException; +import com.robo4j.DefaultAttributeDescriptor; +import com.robo4j.RoboContext; +import com.robo4j.RoboReference; +import com.robo4j.RoboUnit; import com.robo4j.configuration.Configuration; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.util.concurrent.CountDownLatch; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; @@ -33,21 +39,57 @@ * @author Miro Wengner (@miragemiko) */ public class CounterUnit extends RoboUnit { + private final class CounterRunner implements Runnable { + private final RoboReference target; + private final CountDownLatch latchReportMessages; + + private CounterRunner(RoboReference target, CountDownLatch latchReportMessages) { + this.target = target; + this.latchReportMessages = latchReportMessages; + } + + @Override + public void run() { + if (target != null) { + LOGGER.debug("send message:{} to unit:{}", counter.get(), target); + target.sendMessage(counter.getAndIncrement()); + latchReportMessages.countDown(); + } else { + LOGGER.error("The target {} for the CounterUnit does not exist! Could not send count!", target); + } + } + } + private static final Logger LOGGER = LoggerFactory.getLogger(CounterUnit.class); - private final AtomicInteger counter = new AtomicInteger(0); + private final AtomicInteger counter = new AtomicInteger(0); private int interval; + private int reportReceivedMessagesNumber; + + + public static final String ATTR_COUNTER = "Counter"; + public static final String ATTR_REPORT_RECEIVED_MESSAGES_LATCH = "reportMessagesLatch"; + + public static final DefaultAttributeDescriptor DESCRIPTOR_REPORT_RECEIVED_MESSAGES_LATCH = DefaultAttributeDescriptor + .create(CountDownLatch.class, ATTR_REPORT_RECEIVED_MESSAGES_LATCH); /** * This configuration key controls the interval between the updates, in ms. */ public static final String KEY_INTERVAL = "interval"; + public static final String KEY_RECEIVED_MESSAGE = "reportMessages"; + /** * The default period, if no period is configured. */ public static final int DEFAULT_INTERVAL = 1000; + /** + * The report latch when requested number of message is received + */ + public static final int DEFAULT_RECEIVED_MESSAGE = 2; + /** * This configuration key controls the target of the counter updates. This * configuration key is mandatory. Also, the target must exist when the @@ -55,6 +97,16 @@ public class CounterUnit extends RoboUnit { */ public static final String KEY_TARGET = "target"; + /** + * latch helps to identify the STOP state, + */ + private CountDownLatch unitLatch = new CountDownLatch(1); + + /** + * latch for reporting specific number of messages are received, by default it signal that any message was received + */ + private CountDownLatch latchReportReceivedMessages = new CountDownLatch(1); + /* * The currently running timer updater. */ @@ -65,22 +117,6 @@ public class CounterUnit extends RoboUnit { */ private String targetId; - private final class CounterUnitAction implements Runnable { - private final RoboReference target; - - public CounterUnitAction(RoboReference target) { - this.target = target; - } - - @Override - public void run() { - if (target != null) { - target.sendMessage(counter.getAndIncrement()); - } else { - LOGGER.error("The target {} for the CounterUnit does not exist! Could not send count!", targetId); - } - } - } /** * Constructor. @@ -95,6 +131,8 @@ public CounterUnit(RoboContext context, String id) { @Override protected void onInitialization(Configuration configuration) throws ConfigurationException { interval = configuration.getInteger(KEY_INTERVAL, DEFAULT_INTERVAL); + reportReceivedMessagesNumber = configuration.getInteger(KEY_RECEIVED_MESSAGE, DEFAULT_RECEIVED_MESSAGE); + latchReportReceivedMessages = new CountDownLatch(reportReceivedMessagesNumber); targetId = configuration.getString(KEY_TARGET, null); if (targetId == null) { throw ConfigurationException.createMissingConfigNameException(KEY_TARGET); @@ -107,13 +145,20 @@ public void onMessage(CounterCommand message) { super.onMessage(message); switch (message) { case START: + var counterUnitAction = new CounterRunner(getContext().getReference(targetId), latchReportReceivedMessages); scheduledFuture = getContext().getScheduler().scheduleAtFixedRate( - new CounterUnitAction(getContext().getReference(targetId)), 0, interval, TimeUnit.MILLISECONDS); + counterUnitAction, 0, interval, TimeUnit.MILLISECONDS); break; case STOP: - scheduledFuture.cancel(false); + if (scheduledFuture.cancel(false)) { + unitLatch.countDown(); + } else { + scheduledFuture.cancel(true); + LOGGER.error("scheduled feature could not be properly cancelled!"); + } break; case RESET: + unitLatch = new CountDownLatch(reportReceivedMessagesNumber); counter.set(0); break; } @@ -123,9 +168,13 @@ public void onMessage(CounterCommand message) { @SuppressWarnings("unchecked") @Override public synchronized R onGetAttribute(AttributeDescriptor attribute) { - if (attribute.getAttributeName().equals("Counter") && attribute.getAttributeType() == Integer.class) { + if (attribute.getAttributeName().equals(ATTR_COUNTER) && attribute.getAttributeType() == Integer.class) { return (R) (Integer) counter.get(); } + if (attribute.getAttributeName().equals(ATTR_REPORT_RECEIVED_MESSAGES_LATCH) + && attribute.getAttributeType() == CountDownLatch.class) { + return (R) latchReportReceivedMessages; + } return null; } diff --git a/robo4j-core/src/test/java/com/robo4j/CounterUnitTests.java b/robo4j-core/src/test/java/com/robo4j/CounterUnitTests.java deleted file mode 100644 index 1a86041f..00000000 --- a/robo4j-core/src/test/java/com/robo4j/CounterUnitTests.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright (c) 2014, 2024, Marcus Hirt, Miroslav Wengner - * - * Robo4J is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Robo4J is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Robo4J. If not, see . - */ -package com.robo4j; - -import com.robo4j.configuration.Configuration; -import com.robo4j.configuration.ConfigurationBuilder; -import com.robo4j.units.CounterCommand; -import com.robo4j.units.CounterUnit; -import com.robo4j.units.IntegerConsumer; -import org.junit.jupiter.api.Test; - -import java.util.ArrayList; -import java.util.concurrent.ExecutionException; - -import static org.junit.jupiter.api.Assertions.*; - -/** - * Test for the CounterUnit. - * - * @author Marcus Hirt (@hirt) - * @author Miroslav Wengner (@miragemiko) - */ -class CounterUnitTests { - private static final String ID_COUNTER = "counter"; - private static final String ID_CONSUMER = "consumer"; - private static final AttributeDescriptor NUMBER_OF_MESSAGES = new DefaultAttributeDescriptor<>(Integer.class, - "NumberOfReceivedMessages"); - private static final AttributeDescriptor COUNTER = new DefaultAttributeDescriptor<>(Integer.class, "Counter"); - - @SuppressWarnings({"unchecked", "rawtypes"}) - private static final AttributeDescriptor> MESSAGES = new DefaultAttributeDescriptor>( - (Class>) new ArrayList().getClass(), "ReceivedMessages"); - - - // TODO : review the test and comment, simplify it - @Test - void test() throws RoboBuilderException, InterruptedException, ExecutionException { - // FIXME(Marcus/Aug 20, 2017): We really should get rid of the sleeps - // here and use waits with timeouts... - RoboBuilder builder = new RoboBuilder(); - builder.add(IntegerConsumer.class, ID_CONSUMER); - builder.add(CounterUnit.class, getCounterConfiguration(ID_CONSUMER, 1000), ID_COUNTER); - RoboContext context = builder.build(); - context.start(); - assertEquals(LifecycleState.STARTED, context.getState()); - RoboReference counter = context.getReference(ID_COUNTER); - RoboReference consumer = context.getReference(ID_CONSUMER); - counter.sendMessage(CounterCommand.START); - Thread.sleep(2500); - assertTrue(consumer.getAttribute(NUMBER_OF_MESSAGES).get() > 2); - counter.sendMessage(CounterCommand.STOP); - Thread.sleep(200); - Integer count = consumer.getAttribute(NUMBER_OF_MESSAGES).get(); - Thread.sleep(2500); - assertEquals(count, consumer.getAttribute(NUMBER_OF_MESSAGES).get()); - ArrayList messages = consumer.getAttribute(MESSAGES).get(); - assertNotEquals(0, messages.size()); - assertNotEquals(0, (int) messages.get(messages.size() - 1)); - counter.sendMessage(CounterCommand.RESET); - Thread.sleep(1000); - assertEquals(0, (int) counter.getAttribute(COUNTER).get()); - } - - private Configuration getCounterConfiguration(String target, int interval) { - Configuration configuration = new ConfigurationBuilder().addString(CounterUnit.KEY_TARGET, target) - .addInteger(CounterUnit.KEY_INTERVAL, interval).build(); - return configuration; - } - -} diff --git a/robo4j-core/src/test/java/com/robo4j/RoboBuilderTests.java b/robo4j-core/src/test/java/com/robo4j/RoboBuilderTests.java index 920fa60f..b0d15112 100644 --- a/robo4j-core/src/test/java/com/robo4j/RoboBuilderTests.java +++ b/robo4j-core/src/test/java/com/robo4j/RoboBuilderTests.java @@ -22,14 +22,18 @@ import com.robo4j.units.StringProducer; import com.robo4j.util.SystemUtil; import org.junit.jupiter.api.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; -import static com.robo4j.RoboUnitTestUtils.getAttributeOrTimeout; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertTrue; /** * Test(s) for the builder. @@ -38,8 +42,9 @@ * @author Miroslav Wengner (@miragemiko) */ public class RoboBuilderTests { + private static final Logger LOGGER = LoggerFactory.getLogger(RoboBuilderTests.class); private static final int MESSAGES = 10; - private static final int TIMEOUT = 100; + private static final int TIMEOUT_MIN = 2; private static final String SYSTEM_CONFIG_NAME = "mySystem"; private static final String PRODUCER_UNIT_NAME = "producer"; private static final String CONSUMER_UNIT_NAME = "consumer"; @@ -89,12 +94,12 @@ void testParsingFileWithSystemConfig() for (int i = 0; i < MESSAGES; i++) { producer.sendMessage("sendRandomMessage"); } - var messagesProduced = producerLatch.await(TIMEOUT, TimeUnit.MINUTES); + var messagesProduced = producerLatch.await(TIMEOUT_MIN, TimeUnit.MINUTES); var totalProducedMessages = getAttributeOrTimeout(producer, StringProducer.DESCRIPTOR_TOTAL_MESSAGES); RoboReference consumer = system.getReference(CONSUMER_UNIT_NAME); CountDownLatch countDownLatchConsumer = getAttributeOrTimeout(consumer, StringConsumer.DESCRIPTOR_COUNT_DOWN_LATCH); - var messageReceived = countDownLatchConsumer.await(TIMEOUT, TimeUnit.MINUTES); + var messageReceived = countDownLatchConsumer.await(TIMEOUT_MIN, TimeUnit.MINUTES); var totalReceivedMessages = consumer.getAttribute(StringConsumer.DESCRIPTOR_TOTAL_MESSAGES).get(); system.stop(); @@ -125,7 +130,7 @@ void testParsingFile() RoboReference consumer = system.getReference(CONSUMER_UNIT_NAME); CountDownLatch countDownLatchConsumer = getAttributeOrTimeout(consumer, StringConsumer.DESCRIPTOR_COUNT_DOWN_LATCH); - var receivedMessages = countDownLatchConsumer.await(TIMEOUT, TimeUnit.MINUTES); + var receivedMessages = countDownLatchConsumer.await(TIMEOUT_MIN, TimeUnit.MINUTES); var totalReceivedMessages = getAttributeOrTimeout(consumer, StringConsumer.DESCRIPTOR_TOTAL_MESSAGES); system.stop(); @@ -152,13 +157,13 @@ void testSeparateSystemUnitsSystemConfig() for (int i = 0; i < MESSAGES; i++) { producer.sendMessage("sendRandomMessage"); } - var messagesProduced = producerLatch.await(TIMEOUT, TimeUnit.MINUTES); + var messagesProduced = producerLatch.await(TIMEOUT_MIN, TimeUnit.MINUTES); var totalProducedMessages = producer.getAttribute(StringProducer.DESCRIPTOR_TOTAL_MESSAGES).get(); RoboReference consumer = system.getReference(CONSUMER_UNIT_NAME); CountDownLatch countDownLatchConsumer = getAttributeOrTimeout(consumer, StringProducer.DESCRIPTOR_COUNT_DOWN_LATCH); - var receivedMessages = countDownLatchConsumer.await(TIMEOUT, TimeUnit.MINUTES); + var receivedMessages = countDownLatchConsumer.await(TIMEOUT_MIN, TimeUnit.MINUTES); var totalReceivedMessages = getAttributeOrTimeout(consumer, StringConsumer.DESCRIPTOR_TOTAL_MESSAGES); system.stop(); @@ -224,14 +229,14 @@ void testProgrammaticConfiguration() throws RoboBuilderException, InterruptedExc for (int i = 0; i < MESSAGES; i++) { producer.sendMessage(StringProducer.PROPERTY_SEND_RANDOM_MESSAGE); } - var producedMessage = producerLatch.await(TIMEOUT, TimeUnit.MINUTES); + var producedMessage = producerLatch.await(TIMEOUT_MIN, TimeUnit.MINUTES); var totalProducedMessages = producer.getAttribute(StringProducer.DESCRIPTOR_TOTAL_MESSAGES).get(); RoboReference consumer = system.getReference(CONSUMER_UNIT_NAME); // We need to fix these tests so that we can get a callback. CountDownLatch countDownLatchConsumer = getAttributeOrTimeout(consumer, StringConsumer.DESCRIPTOR_COUNT_DOWN_LATCH); - var messageReceived = countDownLatchConsumer.await(TIMEOUT, TimeUnit.MINUTES); + var messageReceived = countDownLatchConsumer.await(TIMEOUT_MIN, TimeUnit.MINUTES); int totalReceivedMessages = getAttributeOrTimeout(consumer, StringConsumer.DESCRIPTOR_TOTAL_MESSAGES); @@ -247,4 +252,13 @@ void testProgrammaticConfiguration() throws RoboBuilderException, InterruptedExc system.shutdown(); } + private static R getAttributeOrTimeout(RoboReference roboReference, AttributeDescriptor attributeDescriptor) throws InterruptedException, ExecutionException, TimeoutException { + var attribute = roboReference.getAttribute(attributeDescriptor).get(TIMEOUT_MIN, TimeUnit.MINUTES); + if (attribute == null) { + attribute = roboReference.getAttribute(attributeDescriptor).get(TIMEOUT_MIN, TimeUnit.MINUTES); + LOGGER.error("roboReference:{}, no attribute:{}", roboReference.getId(), attributeDescriptor.getAttributeName()); + } + return attribute; + } + } diff --git a/robo4j-core/src/test/java/com/robo4j/RunnableProcessCounterUnitTests.java b/robo4j-core/src/test/java/com/robo4j/RunnableProcessCounterUnitTests.java new file mode 100644 index 00000000..1761a519 --- /dev/null +++ b/robo4j-core/src/test/java/com/robo4j/RunnableProcessCounterUnitTests.java @@ -0,0 +1,159 @@ +/* + * Copyright (c) 2014, 2024, Marcus Hirt, Miroslav Wengner + * + * Robo4J is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Robo4J is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Robo4J. If not, see . + */ +package com.robo4j; + +import com.robo4j.configuration.Configuration; +import com.robo4j.configuration.ConfigurationBuilder; +import com.robo4j.units.CounterCommand; +import com.robo4j.units.CounterUnit; +import com.robo4j.units.IntegerConsumer; +import org.junit.jupiter.api.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * Test for the CounterUnit. + * + * @author Marcus Hirt (@hirt) + * @author Miroslav Wengner (@miragemiko) + */ +class RunnableProcessCounterUnitTests { + private static final Logger LOGGER = LoggerFactory.getLogger(RunnableProcessCounterUnitTests.class); + private static final int TIMEOUT_MIN = 2; + private static final String COUNTER_PRODUCER_ID = "counter"; + private static final String CONSUMER_ID = "consumer"; + private static final AttributeDescriptor NUMBER_OF_MESSAGES = new DefaultAttributeDescriptor<>(Integer.class, + "NumberOfReceivedMessages"); + private static final AttributeDescriptor COUNTER = new DefaultAttributeDescriptor<>(Integer.class, CounterUnit.ATTR_COUNTER); + + @SuppressWarnings({"unchecked"}) + private static final AttributeDescriptor> MESSAGES = new DefaultAttributeDescriptor<>( + (Class>) new ArrayList().getClass(), + "ReceivedMessages"); + + @Test + void runnableProcessStartTest() throws RoboBuilderException { + RoboBuilder builder = new RoboBuilder(); + builder.add(IntegerConsumer.class, CONSUMER_ID); + builder.add(CounterUnit.class, getCounterConfiguration(CONSUMER_ID, 1000), COUNTER_PRODUCER_ID); + RoboContext context = builder.build(); + + context.start(); + + assertEquals(LifecycleState.STARTED, context.getState()); + } + + @Test + void runnableGetProcessReferenceTest() throws RoboBuilderException, ExecutionException, InterruptedException, TimeoutException { + var counterMessageProducerInterval = 1000; + RoboBuilder builder = new RoboBuilder(); + builder.add(IntegerConsumer.class, CONSUMER_ID); + builder.add(CounterUnit.class, getCounterConfiguration(CONSUMER_ID, counterMessageProducerInterval), COUNTER_PRODUCER_ID); + RoboContext context = builder.build(); + context.start(); + + RoboReference messageProducer = context.getReference(COUNTER_PRODUCER_ID); + messageProducer.sendMessage(CounterCommand.START); + var latchCreatedMessagesInInterval = getAttributeOrTimeout(messageProducer, CounterUnit.DESCRIPTOR_REPORT_RECEIVED_MESSAGES_LATCH); + var createdMessages = latchCreatedMessagesInInterval.await(TIMEOUT_MIN, TimeUnit.MINUTES); + + RoboReference consumer = context.getReference(CONSUMER_ID); + + var receivedMessages = getAttributeOrTimeout(consumer, NUMBER_OF_MESSAGES); + + assertTrue(createdMessages); + assertEquals(CounterUnit.DEFAULT_RECEIVED_MESSAGE, receivedMessages); + } + + @Test + void runnableGetStoppedProcessReferenceTest() throws RoboBuilderException, ExecutionException, InterruptedException, TimeoutException { + var counterMessageProducerInterval = 1000; + RoboBuilder builder = new RoboBuilder(); + builder.add(IntegerConsumer.class, CONSUMER_ID); + builder.add(CounterUnit.class, getCounterConfiguration(CONSUMER_ID, counterMessageProducerInterval), COUNTER_PRODUCER_ID); + RoboContext context = builder.build(); + context.start(); + + RoboReference messageProducer = context.getReference(COUNTER_PRODUCER_ID); + messageProducer.sendMessage(CounterCommand.START); + var latchCreatedMessagesInInterval = getAttributeOrTimeout(messageProducer, CounterUnit.DESCRIPTOR_REPORT_RECEIVED_MESSAGES_LATCH); + var createdMessages = latchCreatedMessagesInInterval.await(TIMEOUT_MIN, TimeUnit.MINUTES); + messageProducer.sendMessage(CounterCommand.STOP); + + RoboReference consumer = context.getReference(CONSUMER_ID); + var receivedMessages = getAttributeOrTimeout(consumer, NUMBER_OF_MESSAGES); + + + assertTrue(createdMessages); + assertEquals(CounterUnit.DEFAULT_RECEIVED_MESSAGE, receivedMessages); + assertEquals(LifecycleState.STARTED, context.getState()); + } + + @Test + void test() throws RoboBuilderException, InterruptedException, ExecutionException { + // FIXME(Marcus/Aug 20, 2017): We really should get rid of the sleeps + // here and use waits with timeouts... + RoboBuilder builder = new RoboBuilder(); + builder.add(IntegerConsumer.class, CONSUMER_ID); + builder.add(CounterUnit.class, getCounterConfiguration(CONSUMER_ID, 1000), COUNTER_PRODUCER_ID); + RoboContext context = builder.build(); + context.start(); + assertEquals(LifecycleState.STARTED, context.getState()); + RoboReference counter = context.getReference(COUNTER_PRODUCER_ID); + RoboReference consumer = context.getReference(CONSUMER_ID); + counter.sendMessage(CounterCommand.START); + Thread.sleep(2500); + assertTrue(consumer.getAttribute(NUMBER_OF_MESSAGES).get() > 2); + counter.sendMessage(CounterCommand.STOP); + Thread.sleep(200); + Integer count = consumer.getAttribute(NUMBER_OF_MESSAGES).get(); + Thread.sleep(2500); + assertEquals(count, consumer.getAttribute(NUMBER_OF_MESSAGES).get()); + ArrayList messages = consumer.getAttribute(MESSAGES).get(); + assertNotEquals(0, messages.size()); + assertNotEquals(0, (int) messages.get(messages.size() - 1)); + counter.sendMessage(CounterCommand.RESET); + Thread.sleep(1000); + assertEquals(0, (int) counter.getAttribute(COUNTER).get()); + } + + private Configuration getCounterConfiguration(String target, int interval) { + return new ConfigurationBuilder().addString(CounterUnit.KEY_TARGET, target) + .addInteger(CounterUnit.KEY_INTERVAL, interval) + .addInteger(CounterUnit.KEY_RECEIVED_MESSAGE, CounterUnit.DEFAULT_RECEIVED_MESSAGE) + .build(); + } + + private static R getAttributeOrTimeout(RoboReference roboReference, AttributeDescriptor attributeDescriptor) throws InterruptedException, ExecutionException, TimeoutException { + var attribute = roboReference.getAttribute(attributeDescriptor).get(TIMEOUT_MIN, TimeUnit.MINUTES); + if (attribute == null) { + attribute = roboReference.getAttribute(attributeDescriptor).get(TIMEOUT_MIN, TimeUnit.MINUTES); + LOGGER.error("roboReference:{}, no attribute:{}", roboReference.getId(), attributeDescriptor.getAttributeName()); + } + return attribute; + } + +} diff --git a/robo4j-core/src/test/java/com/robo4j/net/MessageServerTest.java b/robo4j-core/src/test/java/com/robo4j/net/MessageServerTest.java index 0839826f..9060174b 100644 --- a/robo4j-core/src/test/java/com/robo4j/net/MessageServerTest.java +++ b/robo4j-core/src/test/java/com/robo4j/net/MessageServerTest.java @@ -32,7 +32,10 @@ import java.util.stream.Collectors; import java.util.stream.Stream; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; /** * @author Marcus Hirt (@hirt) @@ -41,6 +44,7 @@ // TODO : remove thread sleep public class MessageServerTest { private static final Logger LOGGER = LoggerFactory.getLogger(MessageServerTest.class); + private static final int TIMEOUT_SEC = 30; private static final String CONST_MYUUID = "myuuid"; private static final String PROPERTY_SERVER_NAME = "ServerName"; private volatile Exception exception = null; @@ -89,7 +93,7 @@ void testClientServerMessagePassing() throws Exception { client.sendMessage("test", message); } - var receivedMessages = messageLatch.await(2, TimeUnit.SECONDS); + var receivedMessages = messageLatch.await(TIMEOUT_SEC, TimeUnit.SECONDS); assertTrue(receivedMessages); assertEquals(testMessage.size(), messages.size()); @@ -152,7 +156,7 @@ void testMessageTypes() throws Exception { client.sendMessage("test6", Long.valueOf(6)); client.sendMessage("test7", Double.valueOf(7)); client.sendMessage("test8", new TestMessageType(8, messageText, null)); - messageLatch.await(24, TimeUnit.HOURS); + messageLatch.await(TIMEOUT_SEC, TimeUnit.SECONDS); assertEquals(messagesNumber, messages.size()); if (messages.get(0) instanceof Byte) { diff --git a/robo4j-core/src/test/java/com/robo4j/net/RemoteContextTests.java b/robo4j-core/src/test/java/com/robo4j/net/RemoteContextTests.java index c05eacd6..67c1c871 100644 --- a/robo4j-core/src/test/java/com/robo4j/net/RemoteContextTests.java +++ b/robo4j-core/src/test/java/com/robo4j/net/RemoteContextTests.java @@ -16,7 +16,11 @@ */ package com.robo4j.net; -import com.robo4j.*; +import com.robo4j.ConfigurationException; +import com.robo4j.RoboBuilder; +import com.robo4j.RoboBuilderException; +import com.robo4j.RoboContext; +import com.robo4j.RoboReference; import com.robo4j.configuration.Configuration; import com.robo4j.configuration.ConfigurationBuilder; import com.robo4j.units.StringConsumer; @@ -30,8 +34,12 @@ import java.io.IOException; import java.util.List; import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; /** * Note that on Mac OS X, it seems the easiest way to get this test to run is to @@ -43,6 +51,7 @@ class RemoteContextTests { private static final Logger LOGGER = LoggerFactory.getLogger(RemoteContextTests.class); + private static final int TIMEOUT_SEC = 30; private static final String ACK_CONSUMER = "ackConsumer"; private static final String REMOTE_UNIT_EMITTER = "remoteEmitter"; private static final int NUMBER_ITERATIONS = 10; @@ -193,7 +202,7 @@ void messageToDiscoveredContextAndReferenceToDiscoveredContextTest() throws Exce remoteTestMessageProducer.sendMessage(RemoteTestMessageProducer.ATTR_SEND_MESSAGE); CountDownLatch ackConsumerCountDownLatch = ackConsumer .getAttribute(AckingStringConsumer.DESCRIPTOR_ACK_LATCH).get(); - ackConsumerCountDownLatch.await(); + var ackConsumerLatch = ackConsumerCountDownLatch.await(TIMEOUT_SEC, TimeUnit.SECONDS); List receivedMessages = (List) ackConsumer .getAttribute(AckingStringConsumer.DESCRIPTOR_MESSAGES).get(); @@ -201,13 +210,16 @@ void messageToDiscoveredContextAndReferenceToDiscoveredContextTest() throws Exce CountDownLatch producerCountDownLatch = remoteTestMessageProducer .getAttribute(RemoteTestMessageProducer.DESCRIPTOR_COUNT_DOWN_LATCH).get(); - producerCountDownLatch.await(); + var producedMessagesLatch = producerCountDownLatch.await(TIMEOUT_SEC, TimeUnit.SECONDS); CountDownLatch producerAckLatch = remoteTestMessageProducer .getAttribute(RemoteTestMessageProducer.DESCRIPTOR_ACK_LATCH).get(); - producerAckLatch.await(); + var producedMessagesAckLatch = producerAckLatch.await(TIMEOUT_SEC, TimeUnit.SECONDS); Integer producerAcknowledge = remoteTestMessageProducer .getAttribute(RemoteTestMessageProducer.DESCRIPTOR_TOTAL_ACK).get(); assertTrue(producerAcknowledge > 0); + assertTrue(ackConsumerLatch); + assertTrue(producedMessagesLatch); + assertTrue(producedMessagesAckLatch); } @Disabled("for individual testing") diff --git a/robo4j-core/src/test/java/com/robo4j/units/IntegerConsumer.java b/robo4j-core/src/test/java/com/robo4j/units/IntegerConsumer.java index bcdf1bcb..722b5cac 100644 --- a/robo4j-core/src/test/java/com/robo4j/units/IntegerConsumer.java +++ b/robo4j-core/src/test/java/com/robo4j/units/IntegerConsumer.java @@ -16,14 +16,12 @@ */ package com.robo4j.units; -import java.util.ArrayList; -import java.util.List; - import com.robo4j.AttributeDescriptor; -import com.robo4j.ConfigurationException; import com.robo4j.RoboContext; import com.robo4j.RoboUnit; -import com.robo4j.configuration.Configuration; + +import java.util.ArrayList; +import java.util.List; /** * @@ -31,11 +29,11 @@ * @author Miroslav Wengner (@miragemiko) */ public class IntegerConsumer extends RoboUnit { - private List receivedMessages = new ArrayList<>(); + private final List receivedMessages = new ArrayList<>(); /** - * @param context - * @param id + * @param context robo-context + * @param id unit id */ public IntegerConsumer(RoboContext context, String id) { super(Integer.class, context, id); @@ -50,12 +48,7 @@ public synchronized void onMessage(Integer message) { receivedMessages.add(message); } - @Override - protected void onInitialization(Configuration configuration) throws ConfigurationException { - - } - - @SuppressWarnings("unchecked") + @SuppressWarnings("unchecked") @Override public synchronized R onGetAttribute(AttributeDescriptor attribute) { if (attribute.getAttributeName().equals("NumberOfReceivedMessages") && attribute.getAttributeType() == Integer.class) { diff --git a/robo4j-hw-rpi/src/examples/java/com/robo4j/hw/rpi/i2c/magnetometer/CalibratedMagnetometerExample.java b/robo4j-hw-rpi/src/examples/java/com/robo4j/hw/rpi/i2c/magnetometer/CalibratedMagnetometerExample.java index acd7f71a..eb786024 100644 --- a/robo4j-hw-rpi/src/examples/java/com/robo4j/hw/rpi/i2c/magnetometer/CalibratedMagnetometerExample.java +++ b/robo4j-hw-rpi/src/examples/java/com/robo4j/hw/rpi/i2c/magnetometer/CalibratedMagnetometerExample.java @@ -37,6 +37,7 @@ */ public class CalibratedMagnetometerExample { private static final Logger LOGGER = LoggerFactory.getLogger(CalibratedMagnetometerExample.class); + private static final int TIMEOUT_SEC = 1; private final static ScheduledExecutorService EXECUTOR = Executors.newScheduledThreadPool(1); private static class MagnetometerPoller implements Runnable { @@ -71,7 +72,11 @@ public static void main(String[] args) throws IOException, InterruptedException EXECUTOR.scheduleAtFixedRate(new MagnetometerPoller(magnetometer), 100, 500, TimeUnit.MILLISECONDS); System.in.read(); EXECUTOR.shutdown(); - EXECUTOR.awaitTermination(1, TimeUnit.SECONDS); + var terminated = EXECUTOR.awaitTermination(TIMEOUT_SEC, TimeUnit.SECONDS); + if(!terminated){ + LOGGER.warn("example not terminated properly."); + EXECUTOR.shutdownNow(); + } LOGGER.info("Goodbye!"); } } diff --git a/robo4j-hw-rpi/src/examples/java/com/robo4j/hw/rpi/i2c/magnetometer/MagnetometerLSM303Example.java b/robo4j-hw-rpi/src/examples/java/com/robo4j/hw/rpi/i2c/magnetometer/MagnetometerLSM303Example.java index 95732def..1220f788 100644 --- a/robo4j-hw-rpi/src/examples/java/com/robo4j/hw/rpi/i2c/magnetometer/MagnetometerLSM303Example.java +++ b/robo4j-hw-rpi/src/examples/java/com/robo4j/hw/rpi/i2c/magnetometer/MagnetometerLSM303Example.java @@ -41,6 +41,7 @@ */ public class MagnetometerLSM303Example { private static final Logger LOGGER = LoggerFactory.getLogger(MagnetometerLSM303Example.class); + private static final int TIMEOUT_SEC = 1; private final static ScheduledExecutorService EXECUTOR_SERVICE = Executors.newSingleThreadScheduledExecutor(); enum PrintStyle { @@ -126,7 +127,11 @@ public static void main(String[] args) throws IOException, InterruptedException EXECUTOR_SERVICE.scheduleAtFixedRate(dg, 0, period, TimeUnit.MILLISECONDS); System.in.read(); EXECUTOR_SERVICE.shutdown(); - EXECUTOR_SERVICE.awaitTermination(1, TimeUnit.SECONDS); + var terminated = EXECUTOR_SERVICE.awaitTermination(TIMEOUT_SEC, TimeUnit.SECONDS); + if (!terminated) { + LOGGER.warn("note terminated properly"); + EXECUTOR_SERVICE.shutdownNow(); + } printMessage(printStyle, "Collected " + dg.getCount() + " values!"); } diff --git a/robo4j-hw-rpi/src/examples/java/com/robo4j/hw/rpi/imu/Bno055Example.java b/robo4j-hw-rpi/src/examples/java/com/robo4j/hw/rpi/imu/Bno055Example.java index 578ecea5..6c43fd17 100644 --- a/robo4j-hw-rpi/src/examples/java/com/robo4j/hw/rpi/imu/Bno055Example.java +++ b/robo4j-hw-rpi/src/examples/java/com/robo4j/hw/rpi/imu/Bno055Example.java @@ -38,6 +38,7 @@ */ public class Bno055Example { private static final Logger LOGGER = LoggerFactory.getLogger(Bno055Example.class); + private static final int TIMEOUT_SEC = 1; private final static ScheduledExecutorService EXECUTOR = Executors.newScheduledThreadPool(1); private final static class BNOPrinter implements Runnable { @@ -105,7 +106,11 @@ public static void main(String[] args) throws IOException, InterruptedException EXECUTOR.scheduleAtFixedRate(new BNOPrinter(bno), 40, 500, TimeUnit.MILLISECONDS); System.in.read(); EXECUTOR.shutdown(); - EXECUTOR.awaitTermination(1000, TimeUnit.MILLISECONDS); + var terminated = EXECUTOR.awaitTermination(TIMEOUT_SEC, TimeUnit.SECONDS); + if (!terminated) { + LOGGER.warn("not terminated properly"); + EXECUTOR.shutdownNow(); + } LOGGER.info("Bye, bye!"); bno.shutdown(); } diff --git a/robo4j-hw-rpi/src/main/java/com/robo4j/hw/rpi/i2c/gps/TitanX1GPS.java b/robo4j-hw-rpi/src/main/java/com/robo4j/hw/rpi/i2c/gps/TitanX1GPS.java index 552dfda5..0a8657e0 100644 --- a/robo4j-hw-rpi/src/main/java/com/robo4j/hw/rpi/i2c/gps/TitanX1GPS.java +++ b/robo4j-hw-rpi/src/main/java/com/robo4j/hw/rpi/i2c/gps/TitanX1GPS.java @@ -34,6 +34,8 @@ */ public class TitanX1GPS implements GPS { private static final Logger LOGGER = LoggerFactory.getLogger(TitanX1GPS.class); + // TODO : consider configurable + private static final int TIMEOUT_MILLS = 10; private static final long READ_INTERVAL = 1000; private final XA1110Device device; @@ -146,8 +148,13 @@ private void notifyVelocity(VelocityEvent event) { private void awaitTermination() { try { - internalExecutor.awaitTermination(10, TimeUnit.MILLISECONDS); - internalExecutor.shutdown(); + var terminated = internalExecutor.awaitTermination(TIMEOUT_MILLS, TimeUnit.MILLISECONDS); + if(terminated){ + internalExecutor.shutdown(); + } else { + LOGGER.warn("unit not terminated properly"); + internalExecutor.shutdownNow(); + } } catch (InterruptedException e) { LOGGER.error("awaitTermination", e); // Don't care if we were interrupted. diff --git a/robo4j-hw-rpi/src/main/java/com/robo4j/hw/rpi/imu/bno/Bno080Device.java b/robo4j-hw-rpi/src/main/java/com/robo4j/hw/rpi/imu/bno/Bno080Device.java index a8cfd4f1..6e80d479 100644 --- a/robo4j-hw-rpi/src/main/java/com/robo4j/hw/rpi/imu/bno/Bno080Device.java +++ b/robo4j-hw-rpi/src/main/java/com/robo4j/hw/rpi/imu/bno/Bno080Device.java @@ -68,7 +68,7 @@ public interface Bno080Device { /** * Do calibration cycle. Will block until calibration results are good - * enough, or the time out is reached. + * enough, or the timeout is reached. unit milliseconds */ void calibrate(long timeout); } diff --git a/robo4j-hw-rpi/src/main/java/com/robo4j/hw/rpi/imu/bno/impl/AbstractBno080Device.java b/robo4j-hw-rpi/src/main/java/com/robo4j/hw/rpi/imu/bno/impl/AbstractBno080Device.java index 8c7dc079..81cd9103 100644 --- a/robo4j-hw-rpi/src/main/java/com/robo4j/hw/rpi/imu/bno/impl/AbstractBno080Device.java +++ b/robo4j-hw-rpi/src/main/java/com/robo4j/hw/rpi/imu/bno/impl/AbstractBno080Device.java @@ -17,6 +17,14 @@ package com.robo4j.hw.rpi.imu.bno.impl; +import com.robo4j.hw.rpi.imu.bno.Bno080Device; +import com.robo4j.hw.rpi.imu.bno.DataListener; +import com.robo4j.hw.rpi.imu.bno.shtp.ControlReportId; +import com.robo4j.hw.rpi.imu.bno.shtp.ShtpChannel; +import com.robo4j.hw.rpi.imu.bno.shtp.ShtpPacketRequest; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import java.util.HashMap; import java.util.List; import java.util.Map; @@ -27,12 +35,6 @@ import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; -import com.robo4j.hw.rpi.imu.bno.Bno080Device; -import com.robo4j.hw.rpi.imu.bno.DataListener; -import com.robo4j.hw.rpi.imu.bno.shtp.ControlReportId; -import com.robo4j.hw.rpi.imu.bno.shtp.ShtpChannel; -import com.robo4j.hw.rpi.imu.bno.shtp.ShtpPacketRequest; - /** * AbstractBNO080Device base functionality for BNO080 Devices * @@ -40,26 +42,26 @@ * @author Miroslav Wengner (@miragemiko) */ public abstract class AbstractBno080Device implements Bno080Device { - public static final int SHTP_HEADER_SIZE = 4; - - static byte RECEIVE_WRITE_BYTE = (byte) 0xFF; - static byte RECEIVE_WRITE_BYTE_CONTINUAL = (byte) 0; - final List listeners = new CopyOnWriteArrayList<>(); - final AtomicBoolean active = new AtomicBoolean(false); - final AtomicBoolean ready = new AtomicBoolean(false); - final AtomicInteger commandSequenceNumber = new AtomicInteger(0); - - private static final short CHANNEL_COUNT = 6; // BNO080 supports 6 channels - private static final int AWAIT_TERMINATION = 10; - private final int[] sequenceNumberByChannel = new int[CHANNEL_COUNT]; - - /** - * Record IDs (figure 29, page 29 reference manual). These are used to read - * the metadata for each sensor type. - */ - enum FrsRecord { - - //@formatter:off + public static final int SHTP_HEADER_SIZE = 4; + static byte RECEIVE_WRITE_BYTE = (byte) 0xFF; + static byte RECEIVE_WRITE_BYTE_CONTINUAL = (byte) 0; + private static final Logger LOGGER = LoggerFactory.getLogger(AbstractBno080Device.class); + final List listeners = new CopyOnWriteArrayList<>(); + final AtomicBoolean active = new AtomicBoolean(false); + final AtomicBoolean ready = new AtomicBoolean(false); + final AtomicInteger commandSequenceNumber = new AtomicInteger(0); + + private static final short CHANNEL_COUNT = 6; // BNO080 supports 6 channels + private static final int AWAIT_TERMINATION = 10; + private final int[] sequenceNumberByChannel = new int[CHANNEL_COUNT]; + + /** + * Record IDs (figure 29, page 29 reference manual). These are used to read + * the metadata for each sensor type. + */ + enum FrsRecord { + + //@formatter:off NONE (-1), ACCELEROMETER (0xE302), GYROSCOPE_CALIBRATED (0xE306), @@ -67,32 +69,32 @@ enum FrsRecord { ROTATION_VECTOR (0xE30B); //@formatter:on - private final int id; - - FrsRecord(int recordId) { - this.id = recordId; - } - - public int getId() { - return id; - } - - public static FrsRecord getById(int id) { - for (FrsRecord r : values()) { - if (id == r.getId()) { - return r; - } - } - return NONE; - } - } - - /** - * Command IDs (section 6.4, page 42 in the manual). These are used to - * calibrate, initialize, set orientation, tare etc the sensor. - */ - public enum CommandId { - //@formatter:off + private final int id; + + FrsRecord(int recordId) { + this.id = recordId; + } + + public int getId() { + return id; + } + + public static FrsRecord getById(int id) { + for (FrsRecord r : values()) { + if (id == r.getId()) { + return r; + } + } + return NONE; + } + } + + /** + * Command IDs (section 6.4, page 42 in the manual). These are used to + * calibrate, initialize, set orientation, tare etc the sensor. + */ + public enum CommandId { + //@formatter:off NONE (0), ERRORS (1), COUNTER (2), @@ -105,36 +107,36 @@ public enum CommandId { CLEAR_DCD (11); //@formatter:on - private static Map map = getMap(); - private final int id; - - CommandId(int id) { - this.id = id; - } - - public int getId() { - return id; - } - - public static CommandId getById(int id) { - CommandId command = map.get(id); - return command == null ? NONE : command; - } - - private static Map getMap() { - Map map = new HashMap<>(); - for (CommandId c : values()) { - map.put(c.id, c); - } - return map; - } - } - - /** - * Sensor calibration targets. - */ - enum DeviceCalibrate { - //@formatter:off + private static Map map = getMap(); + private final int id; + + CommandId(int id) { + this.id = id; + } + + public int getId() { + return id; + } + + public static CommandId getById(int id) { + CommandId command = map.get(id); + return command == null ? NONE : command; + } + + private static Map getMap() { + Map map = new HashMap<>(); + for (CommandId c : values()) { + map.put(c.id, c); + } + return map; + } + } + + /** + * Sensor calibration targets. + */ + enum DeviceCalibrate { + //@formatter:off NONE (-1), ACCEL (0), GYRO (1), @@ -144,109 +146,112 @@ enum DeviceCalibrate { STOP (5); //@formatter:on - private int id; - - DeviceCalibrate(int id) { - this.id = id; - } - - public int getId() { - return id; - } - - public static DeviceCalibrate getById(int id) { - for (DeviceCalibrate r : values()) { - if (id == r.getId()) { - return r; - } - } - return NONE; - } - } - - static class ShtpPacketBodyBuilder { - private int[] body; - private final AtomicInteger counter = new AtomicInteger(); - - ShtpPacketBodyBuilder(int size) { - body = new int[size]; - } - - ShtpPacketBodyBuilder addElement(int value) { - this.body[counter.getAndIncrement()] = value; - return this; - } - - int[] build() { - return body; - } - } - - final ScheduledExecutorService executor = Executors.newScheduledThreadPool(1, (r) -> { - Thread t = new Thread(r, "Bno080 Internal Executor"); - t.setDaemon(true); - return t; - }); - - @Override - public void shutdown() { - synchronized (executor) { - active.set(false); - ready.set(false); - awaitTermination(); - } - } - - @Override - public void addListener(DataListener listener) { - listeners.add(listener); - } - - @Override - public void removeListener(DataListener listener) { - listeners.remove(listener); - } - - /** - * SHTP packet contains 1 byte to get Error report. Packet is sent to the - * COMMAND channel - * - * @return error request packet - */ - public ShtpPacketRequest getErrorRequest() { - ShtpPacketRequest result = prepareShtpPacketRequest(ShtpChannel.COMMAND, 1); - result.addBody(0, 0x01 & 0xFF); - return result; - } - - ShtpPacketRequest prepareShtpPacketRequest(ShtpChannel shtpChannel, int size) { - ShtpPacketRequest packet = new ShtpPacketRequest(size, sequenceNumberByChannel[shtpChannel.getChannel()]++); - packet.createHeader(shtpChannel); - return packet; - } - - ShtpPacketRequest getProductIdRequest() { - // Check communication with device - // bytes: Request the product ID and reset info, Reserved - ShtpPacketRequest result = prepareShtpPacketRequest(ShtpChannel.CONTROL, 2); - result.addBody(0, ControlReportId.PRODUCT_ID_REQUEST.getId()); - result.addBody(1, 0); - return result; - } - - ShtpPacketRequest getSoftResetPacket() { - ShtpChannel shtpChannel = ShtpChannel.EXECUTABLE; - ShtpPacketRequest packet = prepareShtpPacketRequest(shtpChannel, 1); - packet.addBody(0, 1); - return packet; - } - - private void awaitTermination() { - try { - executor.awaitTermination(AWAIT_TERMINATION, TimeUnit.MILLISECONDS); - executor.shutdown(); - } catch (InterruptedException e) { - System.err.println(String.format("awaitTermination e: %s", e)); - } - } + private int id; + + DeviceCalibrate(int id) { + this.id = id; + } + + public int getId() { + return id; + } + + public static DeviceCalibrate getById(int id) { + for (DeviceCalibrate r : values()) { + if (id == r.getId()) { + return r; + } + } + return NONE; + } + } + + static class ShtpPacketBodyBuilder { + private int[] body; + private final AtomicInteger counter = new AtomicInteger(); + + ShtpPacketBodyBuilder(int size) { + body = new int[size]; + } + + ShtpPacketBodyBuilder addElement(int value) { + this.body[counter.getAndIncrement()] = value; + return this; + } + + int[] build() { + return body; + } + } + + final ScheduledExecutorService executor = Executors.newScheduledThreadPool(1, (r) -> { + Thread t = new Thread(r, "Bno080 Internal Executor"); + t.setDaemon(true); + return t; + }); + + @Override + public void shutdown() { + synchronized (executor) { + active.set(false); + ready.set(false); + awaitTermination(); + } + } + + @Override + public void addListener(DataListener listener) { + listeners.add(listener); + } + + @Override + public void removeListener(DataListener listener) { + listeners.remove(listener); + } + + /** + * SHTP packet contains 1 byte to get Error report. Packet is sent to the + * COMMAND channel + * + * @return error request packet + */ + public ShtpPacketRequest getErrorRequest() { + ShtpPacketRequest result = prepareShtpPacketRequest(ShtpChannel.COMMAND, 1); + result.addBody(0, 0x01 & 0xFF); + return result; + } + + ShtpPacketRequest prepareShtpPacketRequest(ShtpChannel shtpChannel, int size) { + ShtpPacketRequest packet = new ShtpPacketRequest(size, sequenceNumberByChannel[shtpChannel.getChannel()]++); + packet.createHeader(shtpChannel); + return packet; + } + + ShtpPacketRequest getProductIdRequest() { + // Check communication with device + // bytes: Request the product ID and reset info, Reserved + ShtpPacketRequest result = prepareShtpPacketRequest(ShtpChannel.CONTROL, 2); + result.addBody(0, ControlReportId.PRODUCT_ID_REQUEST.getId()); + result.addBody(1, 0); + return result; + } + + ShtpPacketRequest getSoftResetPacket() { + ShtpChannel shtpChannel = ShtpChannel.EXECUTABLE; + ShtpPacketRequest packet = prepareShtpPacketRequest(shtpChannel, 1); + packet.addBody(0, 1); + return packet; + } + + private void awaitTermination() { + try { + var terminated = executor.awaitTermination(AWAIT_TERMINATION, TimeUnit.MILLISECONDS); + if (!terminated) { + LOGGER.warn("reached termination timeout"); + executor.shutdown(); + } + } catch (InterruptedException e) { + LOGGER.error("awaitTermination e: {}", e.getMessage(), e); + } + } } diff --git a/robo4j-hw-rpi/src/main/java/com/robo4j/hw/rpi/imu/bno/impl/Bno080SPIDevice.java b/robo4j-hw-rpi/src/main/java/com/robo4j/hw/rpi/imu/bno/impl/Bno080SPIDevice.java index 5024161b..32b8114d 100644 --- a/robo4j-hw-rpi/src/main/java/com/robo4j/hw/rpi/imu/bno/impl/Bno080SPIDevice.java +++ b/robo4j-hw-rpi/src/main/java/com/robo4j/hw/rpi/imu/bno/impl/Bno080SPIDevice.java @@ -28,7 +28,16 @@ import com.robo4j.hw.rpi.imu.bno.DataEventType; import com.robo4j.hw.rpi.imu.bno.DataListener; import com.robo4j.hw.rpi.imu.bno.VectorEvent; -import com.robo4j.hw.rpi.imu.bno.shtp.*; +import com.robo4j.hw.rpi.imu.bno.shtp.ControlReportId; +import com.robo4j.hw.rpi.imu.bno.shtp.SensorReportId; +import com.robo4j.hw.rpi.imu.bno.shtp.ShtpChannel; +import com.robo4j.hw.rpi.imu.bno.shtp.ShtpOperation; +import com.robo4j.hw.rpi.imu.bno.shtp.ShtpOperationBuilder; +import com.robo4j.hw.rpi.imu.bno.shtp.ShtpOperationResponse; +import com.robo4j.hw.rpi.imu.bno.shtp.ShtpPacketRequest; +import com.robo4j.hw.rpi.imu.bno.shtp.ShtpPacketResponse; +import com.robo4j.hw.rpi.imu.bno.shtp.ShtpReportIds; +import com.robo4j.hw.rpi.imu.bno.shtp.ShtpUtils; import com.robo4j.hw.rpi.utils.GpioPin; import com.robo4j.math.geometry.Tuple3f; import org.slf4j.Logger; @@ -39,7 +48,10 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; -import static com.robo4j.hw.rpi.imu.bno.shtp.ShtpUtils.*; +import static com.robo4j.hw.rpi.imu.bno.shtp.ShtpUtils.EMPTY_EVENT; +import static com.robo4j.hw.rpi.imu.bno.shtp.ShtpUtils.calculateNumberOfBytesInPacket; +import static com.robo4j.hw.rpi.imu.bno.shtp.ShtpUtils.intToFloat; +import static com.robo4j.hw.rpi.imu.bno.shtp.ShtpUtils.toInt8U; /** * Abstraction for a BNO080 absolute orientation device. @@ -69,7 +81,7 @@ public class Bno080SPIDevice extends AbstractBno080Device { public static final SpiChipSelect DEFAULT_SPI_CHANNEL = SpiChipSelect.CS_0; public static final int MAX_PACKET_SIZE = 32762; - public static final int DEFAULT_TIMEOUT_MS = 1000; + public static final int TIMEOUT_SEC = 1; public static final int UNIT_TICK_MICRO = 100; public static final int TIMEBASE_REFER_DELTA = 120; public static final int MAX_SPI_COUNT = 255; @@ -78,10 +90,6 @@ public class Bno080SPIDevice extends AbstractBno080Device { private final AtomicInteger spiWaitCounter = new AtomicInteger(); private final Spi spiDevice; - // private GpioPinDigitalInput intGpio; - // private GpioPinDigitalOutput wakeGpio; - // private GpioPinDigitalOutput rstGpio; - // private GpioPinDigitalOutput csGpio; // select slave SS = chip select CS private DigitalOutput intGpio; private DigitalOutput wakeGpio; @@ -344,22 +352,18 @@ private DataEvent3f processReceivedPacket() { } private ShtpReportIds getReportType(ShtpChannel channel, ShtpPacketResponse response) { - switch (channel) { - case CONTROL: - return ControlReportId.getById(response.getBodyFirst()); - case REPORTS: - return SensorReportId.getById(response.getBodyFirst()); - default: - return ControlReportId.NONE; - } + return switch (channel) { + case CONTROL -> ControlReportId.getById(response.getBodyFirst()); + case REPORTS -> SensorReportId.getById(response.getBodyFirst()); + default -> ControlReportId.NONE; + }; } private boolean waitForLatch(CountDownLatch latch) { try { - latch.await(DEFAULT_TIMEOUT_MS, TimeUnit.MILLISECONDS); - return true; + return latch.await(TIMEOUT_SEC, TimeUnit.SECONDS); } catch (InterruptedException e) { - LOGGER.debug("waitForLatch e: {}", e.getMessage()); + LOGGER.warn("waitForLatch e: {}", e.getMessage()); return false; } } diff --git a/robo4j-hw-rpi/src/main/java/com/robo4j/hw/rpi/serial/gps/MTK3339GPS.java b/robo4j-hw-rpi/src/main/java/com/robo4j/hw/rpi/serial/gps/MTK3339GPS.java index 9fdf81bd..0dd1212a 100644 --- a/robo4j-hw-rpi/src/main/java/com/robo4j/hw/rpi/serial/gps/MTK3339GPS.java +++ b/robo4j-hw-rpi/src/main/java/com/robo4j/hw/rpi/serial/gps/MTK3339GPS.java @@ -29,7 +29,11 @@ import java.nio.charset.StandardCharsets; import java.util.List; import java.util.StringTokenizer; -import java.util.concurrent.*; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; /** * Code to talk to the Adafruit "ultimate GPS" over the serial port. @@ -41,7 +45,6 @@ * @author Miro Wengner (@miragemiko) */ public class MTK3339GPS implements GPS { - private static final Logger LOGGER = LoggerFactory.getLogger(MTK3339GPS.class); /** * The position accuracy without any @@ -61,6 +64,8 @@ public class MTK3339GPS implements GPS { */ public static final String DEFAULT_GPS_PORT = "/dev/serial0"; + private static final Logger LOGGER = LoggerFactory.getLogger(MTK3339GPS.class); + private static final int TIMEOUT_SEC = 1; private static final String POSITION_TAG = "$GPGGA"; private static final String VELOCITY_TAG = "$GPVTG"; @@ -150,10 +155,14 @@ public void shutdown() { private void awaitTermination() { try { - internalExecutor.awaitTermination(10, TimeUnit.MILLISECONDS); + var terminated = internalExecutor.awaitTermination(TIMEOUT_SEC, TimeUnit.SECONDS); + if (!terminated) { + LOGGER.warn("not terminated"); + } internalExecutor.shutdown(); } catch (InterruptedException e) { // Don't care if we were interrupted. + LOGGER.error("termination:{}", e.getMessage(), e); } } diff --git a/robo4j-socket-http/src/test/java/com/robo4j/socket/http/test/units/RoboDatagramClientTest.java b/robo4j-socket-http/src/test/java/com/robo4j/socket/http/test/units/RoboDatagramClientTest.java index 2d81c632..a2781a95 100644 --- a/robo4j-socket-http/src/test/java/com/robo4j/socket/http/test/units/RoboDatagramClientTest.java +++ b/robo4j-socket-http/src/test/java/com/robo4j/socket/http/test/units/RoboDatagramClientTest.java @@ -16,7 +16,6 @@ */ package com.robo4j.socket.http.test.units; -import com.robo4j.RoboContext; import com.robo4j.RoboReference; import com.robo4j.socket.http.test.units.config.StringConsumer; import com.robo4j.util.SystemUtil; @@ -28,7 +27,7 @@ import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; -import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; /** * Testing Datagram client/server decorated messages @@ -38,20 +37,20 @@ */ class RoboDatagramClientTest { private static final Logger LOGGER = LoggerFactory.getLogger(RoboDatagramClientTest.class); - private static final int TIMEOUT = 10; - private static final TimeUnit TIME_UNIT = TimeUnit.HOURS; + private static final int TIMEOUT_SEC = 10; + private static final TimeUnit TIME_UNIT = TimeUnit.SECONDS; private static final int MAX_NUMBER = 42; private static final int DEFAULT_TIMEOUT = 5; @Disabled - @Test - void datagramClientServerTest() throws Exception { + @Test + void datagramClientServerTest() throws Exception { - var producerSystem = RoboContextUtils.loadRoboContextByXml("robo_datagram_client_request_producer_text.xml"); - var consumerSystem = RoboContextUtils.loadRoboContextByXml("robo_datagram_client_request_consumer_text.xml"); + var producerSystem = RoboContextUtils.loadRoboContextByXml("robo_datagram_client_request_producer_text.xml"); + var consumerSystem = RoboContextUtils.loadRoboContextByXml("robo_datagram_client_request_consumer_text.xml"); - consumerSystem.start(); - producerSystem.start(); + consumerSystem.start(); + producerSystem.start(); LOGGER.info(SystemUtil.printStateReport(consumerSystem)); LOGGER.info(SystemUtil.printStateReport(producerSystem)); @@ -63,15 +62,16 @@ void datagramClientServerTest() throws Exception { CountDownLatch countDownLatchStringProducer = stringConsumerProducer .getAttribute(StringConsumer.DESCRIPTOR_MESSAGES_LATCH).get(DEFAULT_TIMEOUT, TimeUnit.MINUTES); - countDownLatchStringProducer.await(TIMEOUT, TIME_UNIT); + var produced = countDownLatchStringProducer.await(TIMEOUT_SEC, TimeUnit.SECONDS); final int consumerTotalNumber = stringConsumerProducer .getAttribute(StringConsumer.DESCRIPTOR_MESSAGES_TOTAL).get(DEFAULT_TIMEOUT, TimeUnit.MINUTES); producerSystem.shutdown(); consumerSystem.shutdown(); - assertNotNull(consumerTotalNumber); + assertTrue(produced); + assertTrue(consumerTotalNumber > 0); - } + } } diff --git a/robo4j-socket-http/src/test/java/com/robo4j/socket/http/test/units/RoboHttpDynamicTests.java b/robo4j-socket-http/src/test/java/com/robo4j/socket/http/test/units/RoboHttpDynamicTests.java index 337bef0a..568aefa5 100644 --- a/robo4j-socket-http/src/test/java/com/robo4j/socket/http/test/units/RoboHttpDynamicTests.java +++ b/robo4j-socket-http/src/test/java/com/robo4j/socket/http/test/units/RoboHttpDynamicTests.java @@ -59,7 +59,7 @@ */ class RoboHttpDynamicTests { private static final Logger LOGGER = LoggerFactory.getLogger(RoboHttpDynamicTests.class); - private static final int TIMEOUT = 20; + private static final int TIMEOUT_MIN = 1; private static final TimeUnit TIME_UNIT = TimeUnit.HOURS; private static final String ID_HTTP_SERVER = "http"; private static final int PORT = 8025; @@ -98,10 +98,10 @@ void simpleHttpNonUnitTest() throws Exception { // TODO: review how to receiving attributes var countDownLatchDecoratedProducer = getAttributeOrTimeout(decoratedProducer, SocketMessageDecoratedProducerUnit.DESCRIPTOR_MESSAGES_LATCH); - var messagesProduced = countDownLatchDecoratedProducer.await(TIMEOUT, TIME_UNIT); + var messagesProduced = countDownLatchDecoratedProducer.await(TIMEOUT_MIN, TIME_UNIT); var stringConsumer = mainSystem.getReference(StringConsumer.NAME); var countDownLatch = getAttributeOrTimeout(stringConsumer, StringConsumer.DESCRIPTOR_MESSAGES_LATCH); - var messagesReceived = countDownLatch.await(TIMEOUT, TIME_UNIT); + var messagesReceived = countDownLatch.await(TIMEOUT_MIN, TIME_UNIT); var receivedMessages = getAttributeOrTimeout(stringConsumer, StringConsumer.DESCRIPTOR_MESSAGES_TOTAL); clientSystem.shutdown(); @@ -203,10 +203,11 @@ private RoboContext getClientRoboSystem() throws Exception { return result; } + // TODO: maybe some duplication private static R getAttributeOrTimeout(RoboReference roboReference, AttributeDescriptor attributeDescriptor) throws InterruptedException, ExecutionException, TimeoutException { - var attribute = roboReference.getAttribute(attributeDescriptor).get(TIMEOUT, TimeUnit.MINUTES); + var attribute = roboReference.getAttribute(attributeDescriptor).get(TIMEOUT_MIN, TimeUnit.MINUTES); if (attribute == null) { - attribute = roboReference.getAttribute(attributeDescriptor).get(TIMEOUT, TimeUnit.MINUTES); + attribute = roboReference.getAttribute(attributeDescriptor).get(TIMEOUT_MIN, TimeUnit.MINUTES); LOGGER.error("roboReference:{}, no attribute:{}", roboReference.getId(), attributeDescriptor.getAttributeName()); } return attribute; From 2d66559126fb31dbc0bce1a349eadf547bf15c43 Mon Sep 17 00:00:00 2001 From: Miro Wengner Date: Wed, 23 Oct 2024 23:22:34 +0200 Subject: [PATCH 03/15] [75] sleeps removal --- .../src/main/java/com/robo4j/RoboSystem.java | 8 +- .../java/com/robo4j/net/MessageServer.java | 114 ++++++++---------- .../com/robo4j/net/ReferenceDescriptor.java | 2 +- .../RunnableProcessCounterUnitTests.java | 49 +++----- .../com/robo4j/net/MessageServerTest.java | 47 ++++---- .../com/robo4j/net/RemoteContextTests.java | 41 +------ .../java/com/robo4j/units/CounterCommand.java | 2 +- .../java/com/robo4j/units/CounterUnit.java | 35 ++++-- .../test/units/RoboDatagramPingPongTest.java | 14 ++- 9 files changed, 143 insertions(+), 169 deletions(-) rename robo4j-core/src/{main => test}/java/com/robo4j/units/CounterCommand.java (96%) rename robo4j-core/src/{main => test}/java/com/robo4j/units/CounterUnit.java (81%) diff --git a/robo4j-core/src/main/java/com/robo4j/RoboSystem.java b/robo4j-core/src/main/java/com/robo4j/RoboSystem.java index aad75d80..10ddc265 100644 --- a/robo4j-core/src/main/java/com/robo4j/RoboSystem.java +++ b/robo4j-core/src/main/java/com/robo4j/RoboSystem.java @@ -57,6 +57,8 @@ final class RoboSystem implements RoboContext { private static final EnumSet MESSAGE_DELIVERY_CRITERIA = EnumSet.of(LifecycleState.STARTED, LifecycleState.STOPPED, LifecycleState.STOPPING); + private static final int SERVER_LISTEN_URI_MILLIS = 100; + private static final int SERVER_LISTEN_REPEATS = 5; private final AtomicReference state = new AtomicReference<>(LifecycleState.UNINITIALIZED); private final Map> units = new HashMap<>(); @@ -517,14 +519,16 @@ private Map toStringMap(Configuration configuration) { private static URI getListeningURI(MessageServer server) { if (server != null) { - for (int i = 0; i < 5; i++) { + for (int i = 0; i < SERVER_LISTEN_REPEATS; i++) { URI uri = server.getListeningURI(); if (uri != null) { return uri; } - SystemUtil.sleep(100); + SystemUtil.sleep(SERVER_LISTEN_URI_MILLIS); } + LOGGER.warn("getListeningURI server:{}, not found", server); } + LOGGER.warn("getListeningURI undefined server"); return null; } } diff --git a/robo4j-core/src/main/java/com/robo4j/net/MessageServer.java b/robo4j-core/src/main/java/com/robo4j/net/MessageServer.java index 586742af..b8ff2778 100644 --- a/robo4j-core/src/main/java/com/robo4j/net/MessageServer.java +++ b/robo4j-core/src/main/java/com/robo4j/net/MessageServer.java @@ -17,13 +17,21 @@ package com.robo4j.net; import com.robo4j.configuration.Configuration; +import com.robo4j.scheduler.RoboThreadFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.BufferedInputStream; import java.io.IOException; import java.io.ObjectInputStream; -import java.net.*; +import java.net.InetAddress; +import java.net.ServerSocket; +import java.net.Socket; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.atomic.AtomicBoolean; /** * This is a server that listens on messages, and sends them off to the @@ -35,35 +43,22 @@ * @author Miroslav Wengner (@miragemiko) */ public class MessageServer { - private static final Logger LOGGER = LoggerFactory.getLogger(MessageServer.class); - public final static String KEY_HOST_NAME = "hostname"; + public static final String KEY_HOST_NAME = "hostname"; public static final String KEY_PORT = "port"; - private volatile int listeningPort = 0; - private volatile String listeningHost; - private volatile boolean running = false; - private volatile Thread startingThread = null; - private final MessageCallback callback; - private final Configuration configuration; - - private class MessageHandler implements Runnable { - private final Socket socket; - - public MessageHandler(Socket socket) { - this.socket = socket; - } + private record MessageHandler(Socket socket, MessageCallback callback, + AtomicBoolean serverActive) implements Runnable { @Override public void run() { - try (ObjectInputStream objectInputStream = new ObjectInputStream( - new BufferedInputStream(socket.getInputStream()))) { + try (ObjectInputStream objectInputStream = new ObjectInputStream(new BufferedInputStream(socket.getInputStream()))) { // Init protocol. First check magic... if (checkMagic(objectInputStream.readShort())) { - final String uuid = objectInputStream.readUTF(); - final ServerRemoteRoboContext context = new ServerRemoteRoboContext(uuid, socket.getOutputStream()); + final var uuid = objectInputStream.readUTF(); + final var serverRemoteContext = new ServerRemoteRoboContext(uuid, socket.getOutputStream()); // Then keep reading string, byte, data triplets until dead - ReferenceDescriptor.setCurrentContext(context); - while (running) { + ReferenceDescriptor.setCurrentContext(serverRemoteContext); + while (serverActive.get()) { String id = objectInputStream.readUTF(); Object message = decodeMessage(objectInputStream); callback.handleMessage(uuid, id, message); @@ -83,29 +78,18 @@ public void run() { private Object decodeMessage(ObjectInputStream objectInputStream) throws IOException, ClassNotFoundException { byte dataType = objectInputStream.readByte(); - // TODO: replace by better switch - switch (dataType) { - case MessageProtocolConstants.OBJECT: - return objectInputStream.readObject(); - case MessageProtocolConstants.MOD_UTF8: - return objectInputStream.readUTF(); - case MessageProtocolConstants.BYTE: - return objectInputStream.readByte(); - case MessageProtocolConstants.SHORT: - return objectInputStream.readShort(); - case MessageProtocolConstants.FLOAT: - return objectInputStream.readFloat(); - case MessageProtocolConstants.INT: - return objectInputStream.readInt(); - case MessageProtocolConstants.DOUBLE: - return objectInputStream.readDouble(); - case MessageProtocolConstants.LONG: - return objectInputStream.readLong(); - case MessageProtocolConstants.CHAR: - return objectInputStream.readChar(); - default: - throw new IOException("The type with id " + dataType + " is not supported!"); - } + return switch (dataType) { + case MessageProtocolConstants.OBJECT -> objectInputStream.readObject(); + case MessageProtocolConstants.MOD_UTF8 -> objectInputStream.readUTF(); + case MessageProtocolConstants.BYTE -> objectInputStream.readByte(); + case MessageProtocolConstants.SHORT -> objectInputStream.readShort(); + case MessageProtocolConstants.FLOAT -> objectInputStream.readFloat(); + case MessageProtocolConstants.INT -> objectInputStream.readInt(); + case MessageProtocolConstants.DOUBLE -> objectInputStream.readDouble(); + case MessageProtocolConstants.LONG -> objectInputStream.readLong(); + case MessageProtocolConstants.CHAR -> objectInputStream.readChar(); + default -> throw new IOException("The type with id " + dataType + " is not supported!"); + }; } private boolean checkMagic(short magic) { @@ -113,6 +97,18 @@ private boolean checkMagic(short magic) { } } + private static final Logger LOGGER = LoggerFactory.getLogger(MessageServer.class); + private static final String NAME_COMMUNICATION_WORKER_POOL = "Robo4J Communication Worker Pool"; + private static final String NAME_COMMUNICATION_THREAD_PREFIX = "Robo4J-Communication-Worker"; + + private volatile int listeningPort = 0; + private volatile String listeningHost; + private final AtomicBoolean serverActive = new AtomicBoolean(false); + private final MessageCallback callback; + private final Configuration configuration; + private final ExecutorService executors; + + /** * Constructor * @@ -122,6 +118,8 @@ private boolean checkMagic(short magic) { public MessageServer(MessageCallback callback, Configuration configuration) { this.callback = callback; this.configuration = configuration; + // TODO : consider to have configurable thread-pool + this.executors = Executors.newCachedThreadPool(new RoboThreadFactory(new ThreadGroup(NAME_COMMUNICATION_WORKER_POOL), NAME_COMMUNICATION_THREAD_PREFIX, true)); } /** @@ -131,7 +129,6 @@ public MessageServer(MessageCallback callback, Configuration configuration) { * @throws IOException exception */ public void start() throws IOException { - startingThread = Thread.currentThread(); String host = configuration.getString(KEY_HOST_NAME, null); InetAddress bindAddress = null; if (host != null) { @@ -142,26 +139,21 @@ public void start() throws IOException { configuration.getInteger("backlog", 20), bindAddress)) { listeningHost = serverSocket.getInetAddress().getHostAddress(); listeningPort = serverSocket.getLocalPort(); - ThreadGroup g = new ThreadGroup("Robo4J communication threads"); - running = true; - while (running) { - MessageHandler handler = new MessageHandler(serverSocket.accept()); - Thread t = new Thread(g, handler, "Communication [" + handler.socket.getRemoteSocketAddress() + "]"); - t.setDaemon(true); - t.start(); + var threadGroup = new ThreadGroup("Robo4J communication threads"); + serverActive.set(true); + while (serverActive.get()) { + var messageHandler = new MessageHandler(serverSocket.accept(), callback, serverActive); + executors.submit(messageHandler); } } finally { - running = false; - startingThread = null; + serverActive.set(false); + executors.shutdown(); } } public void stop() { - running = false; - Thread startingThread = this.startingThread; - if (startingThread != null) { - startingThread.interrupt(); - } + serverActive.set(false); + executors.shutdown(); } public int getListeningPort() { @@ -174,7 +166,7 @@ public int getListeningPort() { * configured. */ public URI getListeningURI() { - if (!running) { + if (!serverActive.get()) { return null; } diff --git a/robo4j-core/src/main/java/com/robo4j/net/ReferenceDescriptor.java b/robo4j-core/src/main/java/com/robo4j/net/ReferenceDescriptor.java index 05735c1e..08f67426 100644 --- a/robo4j-core/src/main/java/com/robo4j/net/ReferenceDescriptor.java +++ b/robo4j-core/src/main/java/com/robo4j/net/ReferenceDescriptor.java @@ -49,7 +49,7 @@ public ReferenceDescriptor(String ctxId, String id, String fqn) { Object readResolve() throws ObjectStreamException { ServerRemoteRoboContext remoteRoboContext = ACTIVE_CONTEXT.get(); if (remoteRoboContext == null) { - LOGGER.error("No remote context set!"); + LOGGER.warn("No remote context set!"); return null; } return remoteRoboContext.getRoboReference(ctxId, id, fqn); diff --git a/robo4j-core/src/test/java/com/robo4j/RunnableProcessCounterUnitTests.java b/robo4j-core/src/test/java/com/robo4j/RunnableProcessCounterUnitTests.java index 1761a519..dbdc61a7 100644 --- a/robo4j-core/src/test/java/com/robo4j/RunnableProcessCounterUnitTests.java +++ b/robo4j-core/src/test/java/com/robo4j/RunnableProcessCounterUnitTests.java @@ -31,7 +31,7 @@ import java.util.concurrent.TimeoutException; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; /** @@ -90,6 +90,7 @@ void runnableGetProcessReferenceTest() throws RoboBuilderException, ExecutionExc @Test void runnableGetStoppedProcessReferenceTest() throws RoboBuilderException, ExecutionException, InterruptedException, TimeoutException { + var updatedMessageOffsetAfterStop = CounterUnit.DEFAULT_RECEIVED_MESSAGE + 2; var counterMessageProducerInterval = 1000; RoboBuilder builder = new RoboBuilder(); builder.add(IntegerConsumer.class, CONSUMER_ID); @@ -100,45 +101,31 @@ void runnableGetStoppedProcessReferenceTest() throws RoboBuilderException, Execu RoboReference messageProducer = context.getReference(COUNTER_PRODUCER_ID); messageProducer.sendMessage(CounterCommand.START); var latchCreatedMessagesInInterval = getAttributeOrTimeout(messageProducer, CounterUnit.DESCRIPTOR_REPORT_RECEIVED_MESSAGES_LATCH); - var createdMessages = latchCreatedMessagesInInterval.await(TIMEOUT_MIN, TimeUnit.MINUTES); + var unitActiveInternalProcessDone = getAttributeOrTimeout(messageProducer, CounterUnit.DESCRIPTOR_PROCESS_DONE); + var createdMessagesAfterStart = latchCreatedMessagesInInterval.await(TIMEOUT_MIN, TimeUnit.MINUTES); messageProducer.sendMessage(CounterCommand.STOP); + messageProducer.sendMessage(CounterCommand.COUNTER_INC); + messageProducer.sendMessage(CounterCommand.COUNTER_INC); + messageProducer.sendMessage(CounterCommand.RESET); + var latchCreatedMessagesAfterStop = getAttributeOrTimeout(messageProducer, CounterUnit.DESCRIPTOR_REPORT_RECEIVED_MESSAGES_LATCH); + + var activeAfterStopLatch = latchCreatedMessagesAfterStop.await(TIMEOUT_MIN, TimeUnit.MINUTES); + var updatedReceivedMessageOffset = getAttributeOrTimeout(messageProducer, CounterUnit.DESCRIPTOR_RECEIVED_MESSAGE_OFFSET); + var internalProcessDone = getAttributeOrTimeout(messageProducer, CounterUnit.DESCRIPTOR_PROCESS_DONE); RoboReference consumer = context.getReference(CONSUMER_ID); var receivedMessages = getAttributeOrTimeout(consumer, NUMBER_OF_MESSAGES); - assertTrue(createdMessages); - assertEquals(CounterUnit.DEFAULT_RECEIVED_MESSAGE, receivedMessages); + assertFalse(unitActiveInternalProcessDone); + assertTrue(createdMessagesAfterStart); assertEquals(LifecycleState.STARTED, context.getState()); + assertTrue(activeAfterStopLatch); + assertTrue(internalProcessDone); + assertEquals(CounterUnit.DEFAULT_RECEIVED_MESSAGE, receivedMessages); + assertEquals(updatedMessageOffsetAfterStop, updatedReceivedMessageOffset); } - @Test - void test() throws RoboBuilderException, InterruptedException, ExecutionException { - // FIXME(Marcus/Aug 20, 2017): We really should get rid of the sleeps - // here and use waits with timeouts... - RoboBuilder builder = new RoboBuilder(); - builder.add(IntegerConsumer.class, CONSUMER_ID); - builder.add(CounterUnit.class, getCounterConfiguration(CONSUMER_ID, 1000), COUNTER_PRODUCER_ID); - RoboContext context = builder.build(); - context.start(); - assertEquals(LifecycleState.STARTED, context.getState()); - RoboReference counter = context.getReference(COUNTER_PRODUCER_ID); - RoboReference consumer = context.getReference(CONSUMER_ID); - counter.sendMessage(CounterCommand.START); - Thread.sleep(2500); - assertTrue(consumer.getAttribute(NUMBER_OF_MESSAGES).get() > 2); - counter.sendMessage(CounterCommand.STOP); - Thread.sleep(200); - Integer count = consumer.getAttribute(NUMBER_OF_MESSAGES).get(); - Thread.sleep(2500); - assertEquals(count, consumer.getAttribute(NUMBER_OF_MESSAGES).get()); - ArrayList messages = consumer.getAttribute(MESSAGES).get(); - assertNotEquals(0, messages.size()); - assertNotEquals(0, (int) messages.get(messages.size() - 1)); - counter.sendMessage(CounterCommand.RESET); - Thread.sleep(1000); - assertEquals(0, (int) counter.getAttribute(COUNTER).get()); - } private Configuration getCounterConfiguration(String target, int interval) { return new ConfigurationBuilder().addString(CounterUnit.KEY_TARGET, target) diff --git a/robo4j-core/src/test/java/com/robo4j/net/MessageServerTest.java b/robo4j-core/src/test/java/com/robo4j/net/MessageServerTest.java index 9060174b..69109c54 100644 --- a/robo4j-core/src/test/java/com/robo4j/net/MessageServerTest.java +++ b/robo4j-core/src/test/java/com/robo4j/net/MessageServerTest.java @@ -47,57 +47,60 @@ public class MessageServerTest { private static final int TIMEOUT_SEC = 30; private static final String CONST_MYUUID = "myuuid"; private static final String PROPERTY_SERVER_NAME = "ServerName"; + private static final int SERVER_LISTEN_DELAY_MILLIS = 250; private volatile Exception exception = null; @Test void testClientServerMessagePassing() throws Exception { - final List messages = new ArrayList<>(); - final CountDownLatch messageLatch = new CountDownLatch(3); - - Configuration serverConfig = new ConfigurationBuilder().addString(PROPERTY_SERVER_NAME, "Server Name") - .addString(MessageServer.KEY_HOST_NAME, "localhost").build(); - MessageServer server = new MessageServer((uuid, id, message) -> { + final var messageCache = new ArrayList<>(); + final var messagesLatch = new CountDownLatch(3); + + var messageServerConfig = new ConfigurationBuilder() + .addString(PROPERTY_SERVER_NAME, "Server Name") + .addString(MessageServer.KEY_HOST_NAME, "localhost") + .build(); + var messageServer = new MessageServer((uuid, id, message) -> { printInfo(uuid, id, message); - messages.add(String.valueOf(message)); - messageLatch.countDown(); - }, serverConfig); + messageCache.add(String.valueOf(message)); + messagesLatch.countDown(); + }, messageServerConfig); - Thread t = new Thread(() -> { + var serverListenerThread = new Thread(() -> { try { - server.start(); + messageServer.start(); } catch (IOException e) { exception = e; fail(e.getMessage()); } }, "Server Listener"); - t.setDaemon(true); - t.start(); + serverListenerThread.setDaemon(true); + serverListenerThread.start(); for (int i = 0; i < 10; i++) { - if (server.getListeningPort() == 0) { - Thread.sleep(250); + if (messageServer.getListeningPort() == 0) { + Thread.sleep(SERVER_LISTEN_DELAY_MILLIS); } else { break; } } - Configuration clientConfig = ConfigurationFactory.createEmptyConfiguration(); - MessageClient client = new MessageClient(server.getListeningURI(), CONST_MYUUID, clientConfig); + var messageReceiverConfig = ConfigurationFactory.createEmptyConfiguration(); + var messageReceiver = new MessageClient(messageServer.getListeningURI(), CONST_MYUUID, messageReceiverConfig); if (exception != null) { throw exception; } List testMessage = getOrderedTestMessage("My First Little Message!", "My Second Little Message!", "My Third Little Message!"); - client.connect(); + messageReceiver.connect(); for (String message : testMessage) { - client.sendMessage("test", message); + messageReceiver.sendMessage("test", message); } - var receivedMessages = messageLatch.await(TIMEOUT_SEC, TimeUnit.SECONDS); + var receivedMessages = messagesLatch.await(TIMEOUT_SEC, TimeUnit.SECONDS); assertTrue(receivedMessages); - assertEquals(testMessage.size(), messages.size()); - assertArrayEquals(testMessage.toArray(), messages.toArray()); + assertEquals(testMessage.size(), messageCache.size()); + assertArrayEquals(testMessage.toArray(), messageCache.toArray()); } private List getOrderedTestMessage(String... messages) { diff --git a/robo4j-core/src/test/java/com/robo4j/net/RemoteContextTests.java b/robo4j-core/src/test/java/com/robo4j/net/RemoteContextTests.java index 67c1c871..0ca15632 100644 --- a/robo4j-core/src/test/java/com/robo4j/net/RemoteContextTests.java +++ b/robo4j-core/src/test/java/com/robo4j/net/RemoteContextTests.java @@ -52,6 +52,7 @@ class RemoteContextTests { private static final Logger LOGGER = LoggerFactory.getLogger(RemoteContextTests.class); private static final int TIMEOUT_SEC = 30; + private static final int LOOKUP_DELAY_MILLIS = 100; private static final String ACK_CONSUMER = "ackConsumer"; private static final String REMOTE_UNIT_EMITTER = "remoteEmitter"; private static final int NUMBER_ITERATIONS = 10; @@ -69,7 +70,7 @@ void discoveryOfDiscoveryEnabledRoboContextTest() throws RoboBuilderException, I service.start(); for (int i = 0; i < NUMBER_ITERATIONS && (service.getDescriptor("6") == null); i++) { - SystemUtil.sleep(200); + SystemUtil.sleep(LOOKUP_DELAY_MILLIS); } RoboContextDescriptor descriptor = service.getDescriptor("6"); @@ -99,7 +100,7 @@ void messageToDiscoveredContextTest() throws RoboBuilderException, IOException, service.start(); for (int i = 0; i < NUMBER_ITERATIONS && (service.getDescriptor("7") == null); i++) { - SystemUtil.sleep(200); + SystemUtil.sleep(LOOKUP_DELAY_MILLIS); } assertFalse(service.getDiscoveredContexts().isEmpty()); RoboContextDescriptor descriptor = service.getDescriptor("7"); @@ -117,7 +118,7 @@ void messageToDiscoveredContextTest() throws RoboBuilderException, IOException, remoteStringProducer.sendMessage("sendRandomMessage"); for (int i = 0; i < NUMBER_ITERATIONS && consumer.getReceivedMessages().isEmpty(); i++) { - SystemUtil.sleep(200); + SystemUtil.sleep(LOOKUP_DELAY_MILLIS); } printMessagesInfo(consumer.getReceivedMessages()); @@ -143,7 +144,7 @@ void messageIncludingReferenceToDiscoveredContextTest() service.start(); for (int i = 0; i < NUMBER_ITERATIONS && (service.getDescriptor("9") == null); i++) { - SystemUtil.sleep(200); + SystemUtil.sleep(LOOKUP_DELAY_MILLIS); } assertFalse(service.getDiscoveredContexts().isEmpty()); RoboContextDescriptor descriptor = service.getDescriptor("9"); @@ -235,38 +236,6 @@ void startRemoteReceiverTest() throws Exception { System.in.read(); } - @Disabled("for individual testing") - @Test - void startRemoteSenderTest() throws Exception { - // Note that all this cludging about with local lookup service - // implementations etc would normally not be needed. - // This is just to isolate this test from other tests. - final LocalLookupServiceImpl localLookup = new LocalLookupServiceImpl(); - final LookupService service = LookupServiceTests.getLookupService(localLookup); - - LookupServiceProvider.setDefaultLookupService(service); - service.start(); - - // context has been discovered - RoboContextDescriptor descriptor = getRoboContextDescriptor(service, CONTEXT_ID_REMOTE_RECEIVER); - assertFalse(service.getDiscoveredContexts().isEmpty()); - assertNotNull(descriptor); - - // build the producer system - RoboContext producerEmitterSystem = buildEmitterContext(String.class, UNIT_STRING_CONSUMER, - REMOTE_UNIT_EMITTER); - localLookup.addContext(producerEmitterSystem); - - producerEmitterSystem.start(); - RoboReference remoteTestMessageProducer = producerEmitterSystem.getReference(REMOTE_UNIT_EMITTER); - - for (int i = 0; i < 10; i++) { - remoteTestMessageProducer.sendMessage("REMOTE MESSAGE :" + i); - } - - System.in.read(); - } - private RoboContext buildEmitterContext(Class clazz, String target, String unitName) throws Exception { RoboBuilder builder = new RoboBuilder( SystemUtil.getInputStreamByResourceName("testMessageEmitterSystem_8.xml")); diff --git a/robo4j-core/src/main/java/com/robo4j/units/CounterCommand.java b/robo4j-core/src/test/java/com/robo4j/units/CounterCommand.java similarity index 96% rename from robo4j-core/src/main/java/com/robo4j/units/CounterCommand.java rename to robo4j-core/src/test/java/com/robo4j/units/CounterCommand.java index fd586ec9..7e7e6e42 100644 --- a/robo4j-core/src/main/java/com/robo4j/units/CounterCommand.java +++ b/robo4j-core/src/test/java/com/robo4j/units/CounterCommand.java @@ -23,5 +23,5 @@ * @author Miro Wengner (@miragemiko) */ public enum CounterCommand { - START, STOP, RESET + START, STOP, RESET, COUNTER_INC } diff --git a/robo4j-core/src/main/java/com/robo4j/units/CounterUnit.java b/robo4j-core/src/test/java/com/robo4j/units/CounterUnit.java similarity index 81% rename from robo4j-core/src/main/java/com/robo4j/units/CounterUnit.java rename to robo4j-core/src/test/java/com/robo4j/units/CounterUnit.java index 1413ef92..a016fea1 100644 --- a/robo4j-core/src/main/java/com/robo4j/units/CounterUnit.java +++ b/robo4j-core/src/test/java/com/robo4j/units/CounterUnit.java @@ -64,14 +64,20 @@ public void run() { private final AtomicInteger counter = new AtomicInteger(0); private int interval; - private int reportReceivedMessagesNumber; + private int receivedMessagesOffset; + public static final String ATTR_RECEIVED_MESSAGES_OFFSET = "receivedMessagesOffset"; public static final String ATTR_COUNTER = "Counter"; public static final String ATTR_REPORT_RECEIVED_MESSAGES_LATCH = "reportMessagesLatch"; + public static final String ATTR_PROCESS_ACTIVE = "processActive"; public static final DefaultAttributeDescriptor DESCRIPTOR_REPORT_RECEIVED_MESSAGES_LATCH = DefaultAttributeDescriptor .create(CountDownLatch.class, ATTR_REPORT_RECEIVED_MESSAGES_LATCH); + public static final DefaultAttributeDescriptor DESCRIPTOR_RECEIVED_MESSAGE_OFFSET = DefaultAttributeDescriptor + .create(Integer.class, ATTR_RECEIVED_MESSAGES_OFFSET); + public static final DefaultAttributeDescriptor DESCRIPTOR_PROCESS_DONE = DefaultAttributeDescriptor + .create(Boolean.class, ATTR_PROCESS_ACTIVE); /** * This configuration key controls the interval between the updates, in ms. @@ -131,8 +137,8 @@ public CounterUnit(RoboContext context, String id) { @Override protected void onInitialization(Configuration configuration) throws ConfigurationException { interval = configuration.getInteger(KEY_INTERVAL, DEFAULT_INTERVAL); - reportReceivedMessagesNumber = configuration.getInteger(KEY_RECEIVED_MESSAGE, DEFAULT_RECEIVED_MESSAGE); - latchReportReceivedMessages = new CountDownLatch(reportReceivedMessagesNumber); + receivedMessagesOffset = configuration.getInteger(KEY_RECEIVED_MESSAGE, DEFAULT_RECEIVED_MESSAGE); + latchReportReceivedMessages = new CountDownLatch(receivedMessagesOffset); targetId = configuration.getString(KEY_TARGET, null); if (targetId == null) { throw ConfigurationException.createMissingConfigNameException(KEY_TARGET); @@ -144,23 +150,25 @@ public void onMessage(CounterCommand message) { synchronized (this) { super.onMessage(message); switch (message) { - case START: + case START -> { var counterUnitAction = new CounterRunner(getContext().getReference(targetId), latchReportReceivedMessages); scheduledFuture = getContext().getScheduler().scheduleAtFixedRate( counterUnitAction, 0, interval, TimeUnit.MILLISECONDS); - break; - case STOP: + } + + case STOP -> { if (scheduledFuture.cancel(false)) { unitLatch.countDown(); } else { scheduledFuture.cancel(true); LOGGER.error("scheduled feature could not be properly cancelled!"); } - break; - case RESET: - unitLatch = new CountDownLatch(reportReceivedMessagesNumber); + } + case RESET -> { counter.set(0); - break; + } + case COUNTER_INC -> receivedMessagesOffset++; + } } } @@ -168,6 +176,9 @@ public void onMessage(CounterCommand message) { @SuppressWarnings("unchecked") @Override public synchronized R onGetAttribute(AttributeDescriptor attribute) { + if (attribute.getAttributeName().equals(ATTR_RECEIVED_MESSAGES_OFFSET) && attribute.getAttributeType() == Integer.class) { + return (R) (Integer) receivedMessagesOffset; + } if (attribute.getAttributeName().equals(ATTR_COUNTER) && attribute.getAttributeType() == Integer.class) { return (R) (Integer) counter.get(); } @@ -175,6 +186,10 @@ public synchronized R onGetAttribute(AttributeDescriptor attribute) { && attribute.getAttributeType() == CountDownLatch.class) { return (R) latchReportReceivedMessages; } + if (attribute.getAttributeName().equals(ATTR_PROCESS_ACTIVE) + && attribute.getAttributeType() == Boolean.class) { + return (R) (Boolean) scheduledFuture.isDone(); + } return null; } diff --git a/robo4j-socket-http/src/test/java/com/robo4j/socket/http/test/units/RoboDatagramPingPongTest.java b/robo4j-socket-http/src/test/java/com/robo4j/socket/http/test/units/RoboDatagramPingPongTest.java index 6c6c5faa..cb6d917a 100644 --- a/robo4j-socket-http/src/test/java/com/robo4j/socket/http/test/units/RoboDatagramPingPongTest.java +++ b/robo4j-socket-http/src/test/java/com/robo4j/socket/http/test/units/RoboDatagramPingPongTest.java @@ -37,7 +37,10 @@ import java.util.concurrent.TimeUnit; import static com.robo4j.socket.http.test.units.HttpUnitTests.CODECS_UNITS_TEST_PACKAGE; -import static com.robo4j.socket.http.util.RoboHttpUtils.*; +import static com.robo4j.socket.http.util.RoboHttpUtils.PROPERTY_CODEC_PACKAGES; +import static com.robo4j.socket.http.util.RoboHttpUtils.PROPERTY_HOST; +import static com.robo4j.socket.http.util.RoboHttpUtils.PROPERTY_SOCKET_PORT; +import static com.robo4j.socket.http.util.RoboHttpUtils.PROPERTY_UNIT_PATHS_CONFIG; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -56,7 +59,7 @@ class RoboDatagramPingPongTest { @Test void datagramPingPongTest() throws Exception { - var pongSystem = configurePongSystem(TOTAL_NUMBER); + var pongSystem = configurePongSystem(); var pingSystem = configurePingSystem(); pongSystem.start(); @@ -78,7 +81,7 @@ void datagramPingPongTest() throws Exception { udpClient.sendMessage(request); } - totalMessageLatch.await(TIMEOUT, TIME_UNIT); + var messagesReceived = totalMessageLatch.await(TIMEOUT, TIME_UNIT); final int pongConsumerTotalNumber = pongStringConsumerReference.getAttribute(StringConsumer.DESCRIPTOR_MESSAGES_TOTAL).get(); @@ -89,6 +92,7 @@ void datagramPingPongTest() throws Exception { pingSystem.shutdown(); pongSystem.shutdown(); + assertTrue(messagesReceived); assertTrue(pongConsumerTotalNumber > 0 && pongConsumerTotalNumber <= TOTAL_NUMBER); } @@ -113,13 +117,13 @@ private RoboContext configurePingSystem() throws Exception { * @return roboContext * @throws Exception exception */ - private RoboContext configurePongSystem(int totalNumberOfMessage) throws Exception { + private RoboContext configurePongSystem() throws Exception { RoboBuilder builder = new RoboBuilder(); Configuration config = new ConfigurationBuilder().addString(PROPERTY_CODEC_PACKAGES, CODECS_UNITS_TEST_PACKAGE) .addString(PROPERTY_UNIT_PATHS_CONFIG, "[{\"roboUnit\":\"stringConsumer\",\"filters\":[]}]").build(); builder.add(DatagramServerUnit.class, config, UDP_SERVER); - config = new ConfigurationBuilder().addInteger(StringConsumer.PROP_TOTAL_NUMBER_MESSAGES, totalNumberOfMessage).build(); + config = new ConfigurationBuilder().addInteger(StringConsumer.PROP_TOTAL_NUMBER_MESSAGES, RoboDatagramPingPongTest.TOTAL_NUMBER).build(); builder.add(StringConsumer.class, config, StringConsumer.NAME); return builder.build(); From bf8aed2f26266b0f42356548c329b59ea6436206 Mon Sep 17 00:00:00 2001 From: Miro Wengner Date: Thu, 7 Nov 2024 00:02:15 +0100 Subject: [PATCH 04/15] [75] cleanup --- .../com/robo4j/ConfigurationException.java | 3 + .../robo4j/DefaultAttributeDescriptor.java | 2 + .../com/robo4j/LocalReferenceAdapter.java | 6 +- .../src/main/java/com/robo4j/RoboBuilder.java | 2 +- .../java/com/robo4j/RoboBuilderException.java | 3 + .../main/java/com/robo4j/RoboReference.java | 2 +- .../src/main/java/com/robo4j/RoboSystem.java | 16 +- .../src/main/java/com/robo4j/RoboUnit.java | 6 +- .../ConfigurationFactoryException.java | 3 + .../configuration/DefaultConfiguration.java | 278 +++++++++--------- .../robo4j/net/ClientRemoteRoboContext.java | 230 +++++++-------- .../com/robo4j/net/HearbeatMessageCodec.java | 6 +- .../com/robo4j/net/LookupServiceImpl.java | 20 +- .../java/com/robo4j/net/MessageServer.java | 1 - .../robo4j/net/ServerRemoteRoboContext.java | 4 +- .../reflect/ReflectionScanException.java | 3 + .../robo4j/scheduler/DefaultScheduler.java | 9 +- .../src/main/java/com/robo4j/util/IOUtil.java | 8 +- .../java/com/robo4j/util/StreamException.java | 5 +- .../main/java/com/robo4j/util/SystemUtil.java | 6 +- .../java/com/robo4j/RoboBuilderTests.java | 2 +- .../test/java/com/robo4j/RoboUnitTests.java | 2 +- .../RunnableProcessCounterUnitTests.java | 2 +- .../net/MessageProducerConsumerTest.java} | 18 +- .../robo4j/net/RemoteTestMessageProducer.java | 2 +- .../java/com/robo4j/net/RoboTestContext.java | 2 +- .../com/robo4j/net/RoboTestReference.java | 2 +- .../http/request/RoboRequestCallable.java | 8 +- .../http/request/RoboRequestFactory.java | 4 +- .../http/test/units/RoboHttpDynamicTests.java | 2 +- 30 files changed, 335 insertions(+), 322 deletions(-) rename robo4j-core/src/{main/java/com/robo4j/net/ContextMessage.java => test/java/com/robo4j/net/MessageProducerConsumerTest.java} (62%) diff --git a/robo4j-core/src/main/java/com/robo4j/ConfigurationException.java b/robo4j-core/src/main/java/com/robo4j/ConfigurationException.java index 2f31ba86..31fbcb85 100644 --- a/robo4j-core/src/main/java/com/robo4j/ConfigurationException.java +++ b/robo4j-core/src/main/java/com/robo4j/ConfigurationException.java @@ -16,6 +16,8 @@ */ package com.robo4j; +import java.io.Serial; + /** * Exception thrown when a problem occurs configuring a RoboUnit. * @@ -23,6 +25,7 @@ * @author Miroslav Wengner (@miragemiko) */ public class ConfigurationException extends Exception { + @Serial private static final long serialVersionUID = 1L; /** diff --git a/robo4j-core/src/main/java/com/robo4j/DefaultAttributeDescriptor.java b/robo4j-core/src/main/java/com/robo4j/DefaultAttributeDescriptor.java index a53751e6..3a72dc6b 100644 --- a/robo4j-core/src/main/java/com/robo4j/DefaultAttributeDescriptor.java +++ b/robo4j-core/src/main/java/com/robo4j/DefaultAttributeDescriptor.java @@ -16,6 +16,7 @@ */ package com.robo4j; +import java.io.Serial; import java.io.Serializable; /** @@ -25,6 +26,7 @@ * @author Miroslav Wengner (@miragemiko) */ public class DefaultAttributeDescriptor implements AttributeDescriptor, Serializable { + @Serial private static final long serialVersionUID = 1L; private final Class attributeType; private final String attributeName; diff --git a/robo4j-core/src/main/java/com/robo4j/LocalReferenceAdapter.java b/robo4j-core/src/main/java/com/robo4j/LocalReferenceAdapter.java index 13d446c5..71faa4e2 100644 --- a/robo4j-core/src/main/java/com/robo4j/LocalReferenceAdapter.java +++ b/robo4j-core/src/main/java/com/robo4j/LocalReferenceAdapter.java @@ -16,12 +16,12 @@ */ package com.robo4j; +import com.robo4j.configuration.Configuration; + import java.util.Collection; import java.util.Map; import java.util.concurrent.Future; -import com.robo4j.configuration.Configuration; - /** * This is a useful adapter to hand off a RoboReference to a unit which will * only use it as a callback. Note that this has several serious limits. First, @@ -56,7 +56,7 @@ public Configuration getConfiguration() { } @Override - public String getId() { + public String id() { return null; } diff --git a/robo4j-core/src/main/java/com/robo4j/RoboBuilder.java b/robo4j-core/src/main/java/com/robo4j/RoboBuilder.java index 178e9007..7a1f90fd 100644 --- a/robo4j-core/src/main/java/com/robo4j/RoboBuilder.java +++ b/robo4j-core/src/main/java/com/robo4j/RoboBuilder.java @@ -443,7 +443,7 @@ private void internalAddUnit(RoboUnit unit) throws RoboBuilderException { throw new RoboBuilderException("Cannot add the null unit! Skipping"); } else if (units.contains(unit)) { throw new RoboBuilderException( - "Only one unit with the id " + unit.getId() + " can be active at a time. Skipping " + unit.toString()); + "Only one unit with the id " + unit.id() + " can be active at a time. Skipping " + unit.toString()); } units.add(unit); } diff --git a/robo4j-core/src/main/java/com/robo4j/RoboBuilderException.java b/robo4j-core/src/main/java/com/robo4j/RoboBuilderException.java index c808b7ca..edfcc8ae 100644 --- a/robo4j-core/src/main/java/com/robo4j/RoboBuilderException.java +++ b/robo4j-core/src/main/java/com/robo4j/RoboBuilderException.java @@ -16,6 +16,8 @@ */ package com.robo4j; +import java.io.Serial; + /** * Exception thrown from the RoboBuilder. * @@ -23,6 +25,7 @@ * @author Miroslav Wengner (@miragemiko) */ public class RoboBuilderException extends Exception { + @Serial private static final long serialVersionUID = 1L; /** diff --git a/robo4j-core/src/main/java/com/robo4j/RoboReference.java b/robo4j-core/src/main/java/com/robo4j/RoboReference.java index 96aaa089..ef5bc16f 100644 --- a/robo4j-core/src/main/java/com/robo4j/RoboReference.java +++ b/robo4j-core/src/main/java/com/robo4j/RoboReference.java @@ -34,7 +34,7 @@ public interface RoboReference { * * @return the id for the unit. */ - String getId(); + String id(); /** * Returns the life cycle state (@see {@link LifecycleState}) of this unit. diff --git a/robo4j-core/src/main/java/com/robo4j/RoboSystem.java b/robo4j-core/src/main/java/com/robo4j/RoboSystem.java index 10ddc265..0b5eee7a 100644 --- a/robo4j-core/src/main/java/com/robo4j/RoboSystem.java +++ b/robo4j-core/src/main/java/com/robo4j/RoboSystem.java @@ -67,6 +67,7 @@ final class RoboSystem implements RoboContext { private final Scheduler systemScheduler; private final ThreadPoolExecutor workExecutor; + // TODO: review usage of workQueue and blockingQueue, maybe better abstraction private final LinkedBlockingQueue workQueue = new LinkedBlockingQueue<>(); private final ThreadPoolExecutor blockingExecutor; @@ -121,8 +122,8 @@ private DeliveryPolicy deriveDeliveryPolicy(Class> clazz) } @Override - public String getId() { - return unit.getId(); + public String id() { + return unit.id(); } @Override @@ -155,7 +156,7 @@ public void sendMessage(T message) { @Override public String toString() { - return "LocalReference id: " + unit.getId() + " (system: " + uid + ")"; + return "LocalReference id: " + unit.id() + " (system: " + uid + ")"; } private void deliverOnQueue(T message) { @@ -196,7 +197,7 @@ public Class getMessageType() { @Serial Object writeReplace() throws ObjectStreamException { - return new ReferenceDescriptor(RoboSystem.this.getId(), getId(), getMessageType().getName()); + return new ReferenceDescriptor(RoboSystem.this.getId(), id(), getMessageType().getName()); } } @@ -216,7 +217,7 @@ public void run() { try { unit.onMessage(message); } catch (Throwable t) { - LOGGER_MESSENGER.error("Error processing message, unit:{}", unit.getId(), t); + LOGGER_MESSENGER.error("Error processing message, unit:{}", unit.id(), t); } } } @@ -449,13 +450,13 @@ private RoboReference createReference(RoboUnit roboUnit) { } private void addToMap(Set> unitSet) { - unitSet.forEach(unit -> units.put(unit.getId(), unit)); + unitSet.forEach(unit -> units.put(unit.id(), unit)); } private void addToMap(RoboUnit... unitArray) { // NOTE(Marcus/Aug 9, 2017): Do not streamify... for (RoboUnit unit : unitArray) { - units.put(unit.getId(), unit); + units.put(unit.id(), unit); } } @@ -476,6 +477,7 @@ private MessageServer initServer(Configuration serverConfiguration) { return new MessageServer(new MessageCallback() { @Override public void handleMessage(String sourceUuid, String id, Object message) { + // TODO: save message null message or not registered id getReference(id).sendMessage(message); } }, serverConfiguration); diff --git a/robo4j-core/src/main/java/com/robo4j/RoboUnit.java b/robo4j-core/src/main/java/com/robo4j/RoboUnit.java index 570ee9e1..ea36fc55 100644 --- a/robo4j-core/src/main/java/com/robo4j/RoboUnit.java +++ b/robo4j-core/src/main/java/com/robo4j/RoboUnit.java @@ -63,7 +63,7 @@ public RoboUnit(Class messageType, RoboContext context, String id) { * @return the {@link RoboSystem} unique identifier for this unit. */ @Override - public String getId() { + public String id() { return id; } @@ -250,7 +250,7 @@ protected R onGetAttribute(AttributeDescriptor descriptor) { RoboReference internalGetReference() { // NOTE(Marcus/Jan 27, 2017): Can we avoid this? if (reference == null) { - return getContext().getReference(getId()); + return getContext().getReference(id()); } else { return reference; } @@ -287,6 +287,6 @@ public boolean equals(Object obj) { */ @Override public String toString() { - return String.format("%s [id=%s]", getClass().getName(), getId()); + return String.format("%s [id=%s]", getClass().getName(), id()); } } diff --git a/robo4j-core/src/main/java/com/robo4j/configuration/ConfigurationFactoryException.java b/robo4j-core/src/main/java/com/robo4j/configuration/ConfigurationFactoryException.java index d3fde59b..6e575c85 100644 --- a/robo4j-core/src/main/java/com/robo4j/configuration/ConfigurationFactoryException.java +++ b/robo4j-core/src/main/java/com/robo4j/configuration/ConfigurationFactoryException.java @@ -16,6 +16,8 @@ */ package com.robo4j.configuration; +import java.io.Serial; + /** * Exception for when failing to create a configuration. * @@ -23,6 +25,7 @@ * @author Miroslav Wengner (@miragemiko) */ public class ConfigurationFactoryException extends Exception { + @Serial private static final long serialVersionUID = 1L; /** diff --git a/robo4j-core/src/main/java/com/robo4j/configuration/DefaultConfiguration.java b/robo4j-core/src/main/java/com/robo4j/configuration/DefaultConfiguration.java index 8360b464..3faa2eae 100644 --- a/robo4j-core/src/main/java/com/robo4j/configuration/DefaultConfiguration.java +++ b/robo4j-core/src/main/java/com/robo4j/configuration/DefaultConfiguration.java @@ -16,164 +16,148 @@ */ package com.robo4j.configuration; +import java.io.Serial; import java.util.HashMap; import java.util.Map; +import java.util.Objects; import java.util.Set; /** * Default implementation of a {@link Configuration}. - * + * *

* Internal Use Only *

- * + * * @author Marcus Hirt (@hirt) * @author Miroslav Wengner (@miragemiko) */ class DefaultConfiguration implements Configuration { - private static final long serialVersionUID = 1L; - private final Map settings = new HashMap<>(); - private final Map configurations = new HashMap<>(); - - @Override - public Configuration getChildConfiguration(String name) { - return configurations.get(name); - } - - @Override - public Double getDouble(String name, Double defaultValue) { - return (Double) getVal(name, defaultValue); - } - - @Override - public Long getLong(String name, Long defaultValue) { - return (Long) getVal(name, defaultValue); - } - - @Override - public String getString(String name, String defaultValue) { - return (String) getVal(name, defaultValue); - } - - public void setString(String name, String s) { - settings.put(name, s); - } - - @Override - public Character getCharacter(String name, Character defaultValue) { - return (Character) getVal(name, defaultValue); - } - - @Override - public Integer getInteger(String name, Integer defaultValue) { - return (Integer) getVal(name, defaultValue); - } - - @Override - public Float getFloat(String name, Float defaultValue) { - return (Float) getVal(name, defaultValue); - } - - @Override - public Set getValueNames() { - return settings.keySet(); - } - - @Override - public Set getChildNames() { - return configurations.keySet(); - } - - @Override - public Object getValue(String name, Object defaultValue) { - return getVal(name, defaultValue); - } - - @Override - public Boolean getBoolean(String name, Boolean defaultValue) { - return (Boolean) getVal(name, defaultValue); - } - - public Configuration createChildConfiguration(String name) { - DefaultConfiguration config = new DefaultConfiguration(); - configurations.put(name, config); - return config; - } - - public void setBoolean(String name, Boolean b) { - settings.put(name, b); - } - - public void setCharacter(String name, Character s) { - settings.put(name, s); - } - - public void setLong(String name, Long l) { - settings.put(name, l); - } - - public void setDouble(String name, Double d) { - settings.put(name, d); - } - - public void setInteger(String name, Integer i) { - settings.put(name, i); - } - - public void setFloat(String name, Float f) { - settings.put(name, f); - } - - - private Object getVal(String name, Object defaultValue) { - Object val = settings.get(name); - if (val == null) { - return defaultValue; - } - return val; - } - - // TODO : we may have it wrong here hashCode, equals - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((configurations == null) ? 0 : configurations.hashCode()); - result = prime * result + ((settings == null) ? 0 : settings.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - DefaultConfiguration other = (DefaultConfiguration) obj; - if (configurations == null) { - if (other.configurations != null) - return false; - } else if (!configurations.equals(other.configurations)) - return false; - if (settings == null) { - if (other.settings != null) - return false; - } else if (!settings.equals(other.settings)) - return false; - return true; - } - - @Override - public String toString() { - return "Settings: " + settings.toString() + " Configurations: " + configurations.toString(); - } - - /* - * Package local, to be used by the builder. - */ - void addChildConfiguration(String name, Configuration config) { - configurations.put(name, config); - } + @Serial + private static final long serialVersionUID = 1L; + private final Map settings = new HashMap<>(); + private final Map configurations = new HashMap<>(); + + @Override + public Configuration getChildConfiguration(String name) { + return configurations.get(name); + } + + @Override + public Double getDouble(String name, Double defaultValue) { + return (Double) getVal(name, defaultValue); + } + + @Override + public Long getLong(String name, Long defaultValue) { + return (Long) getVal(name, defaultValue); + } + + @Override + public String getString(String name, String defaultValue) { + return (String) getVal(name, defaultValue); + } + + public void setString(String name, String s) { + settings.put(name, s); + } + + @Override + public Character getCharacter(String name, Character defaultValue) { + return (Character) getVal(name, defaultValue); + } + + @Override + public Integer getInteger(String name, Integer defaultValue) { + return (Integer) getVal(name, defaultValue); + } + + @Override + public Float getFloat(String name, Float defaultValue) { + return (Float) getVal(name, defaultValue); + } + + @Override + public Set getValueNames() { + return settings.keySet(); + } + + @Override + public Set getChildNames() { + return configurations.keySet(); + } + + @Override + public Object getValue(String name, Object defaultValue) { + return getVal(name, defaultValue); + } + + @Override + public Boolean getBoolean(String name, Boolean defaultValue) { + return (Boolean) getVal(name, defaultValue); + } + + public Configuration createChildConfiguration(String name) { + DefaultConfiguration config = new DefaultConfiguration(); + configurations.put(name, config); + return config; + } + + public void setBoolean(String name, Boolean b) { + settings.put(name, b); + } + + public void setCharacter(String name, Character s) { + settings.put(name, s); + } + + public void setLong(String name, Long l) { + settings.put(name, l); + } + + public void setDouble(String name, Double d) { + settings.put(name, d); + } + + public void setInteger(String name, Integer i) { + settings.put(name, i); + } + + public void setFloat(String name, Float f) { + settings.put(name, f); + } + + + private Object getVal(String name, Object defaultValue) { + Object val = settings.get(name); + if (val == null) { + return defaultValue; + } + return val; + } + + @Override + public boolean equals(Object object) { + if (this == object) return true; + if (object == null || getClass() != object.getClass()) return false; + DefaultConfiguration that = (DefaultConfiguration) object; + return Objects.equals(settings, that.settings) && Objects.equals(configurations, that.configurations); + } + + @Override + public int hashCode() { + return Objects.hash(settings, configurations); + } + + @Override + public String toString() { + return "Settings: " + settings.toString() + " Configurations: " + configurations.toString(); + } + + /* + * Package local, to be used by the builder. + */ + void addChildConfiguration(String name, Configuration config) { + configurations.put(name, config); + } } diff --git a/robo4j-core/src/main/java/com/robo4j/net/ClientRemoteRoboContext.java b/robo4j-core/src/main/java/com/robo4j/net/ClientRemoteRoboContext.java index eda8ffa1..cbcefe4c 100644 --- a/robo4j-core/src/main/java/com/robo4j/net/ClientRemoteRoboContext.java +++ b/robo4j-core/src/main/java/com/robo4j/net/ClientRemoteRoboContext.java @@ -23,6 +23,8 @@ import com.robo4j.configuration.Configuration; import com.robo4j.configuration.ConfigurationFactory; import com.robo4j.scheduler.Scheduler; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.io.IOException; import java.net.InetAddress; @@ -32,128 +34,116 @@ import java.util.concurrent.Future; /** - * * @author Marcus Hirt (@hirt) * @author Miroslav Wengner (@miragemiko) */ public class ClientRemoteRoboContext implements RoboContext { - private final RoboContextDescriptorEntry descriptorEntry; - private final MessageClient client; - - private class ClientRemoteRoboReference implements RoboReference { - - private final String id; - - public ClientRemoteRoboReference(String id) { - this.id = id; - } - - @Override - public String getId() { - return id; - } - - @Override - public LifecycleState getState() { - throw new UnsupportedOperationException("Not supported yet!"); - } - - @Override - public void sendMessage(Object message) { - try { - if (!client.isConnected()) { - client.connect(); - } - client.sendMessage(id, message); - } catch (IOException e) { - // TODO: Error handling - e.printStackTrace(); - } - } - - @Override - public Class getMessageType() { - throw new UnsupportedOperationException("Not supported yet!"); - } - - @Override - public Configuration getConfiguration() { - throw new UnsupportedOperationException("Not supported yet!"); - } - - @Override - public Future getAttribute(AttributeDescriptor attribute) { - throw new UnsupportedOperationException("Not supported yet!"); - } - - @Override - public Collection> getKnownAttributes() { - throw new UnsupportedOperationException("Not supported yet!"); - } - - @Override - public Future, Object>> getAttributes() { - throw new UnsupportedOperationException("Not supported yet!"); - } - - } - - public ClientRemoteRoboContext(RoboContextDescriptorEntry descriptorEntry) { - this.descriptorEntry = descriptorEntry; - client = initializeClient(descriptorEntry); - } - - private static MessageClient initializeClient(RoboContextDescriptorEntry descriptorEntry) { - MessageClient client = new MessageClient(URI.create(descriptorEntry.descriptor.getMetadata().get(RoboContextDescriptor.KEY_URI)), - descriptorEntry.descriptor.getId(), ConfigurationFactory.createEmptyConfiguration()); - return client; - } - - @Override - public LifecycleState getState() { - return null; - } - - @Override - public void shutdown() { - } - - @Override - public void stop() { - } - - @Override - public void start() { - } - - @Override - public RoboReference getReference(String id) { - return new ClientRemoteRoboReference<>(id); - } - - @Override - public Collection> getUnits() { - // TODO Auto-generated method stub - return null; - } - - @Override - public Scheduler getScheduler() { - throw new UnsupportedOperationException("Accessing the Scheduler remotely is not supported. Use the local scheduler."); - } - - @Override - public String getId() { - return descriptorEntry.descriptor.getId(); - } - - public InetAddress getAddress() { - return descriptorEntry.address; - } - - @Override - public Configuration getConfiguration() { - // TODO Auto-generated method stub - return null; - } + private record ClientRemoteRoboReference(String id, MessageClient client) implements RoboReference { + private static final Logger LOGGER = LoggerFactory.getLogger(ClientRemoteRoboReference.class); + + + @Override + public LifecycleState getState() { + throw new UnsupportedOperationException("Not supported yet!"); + } + + @Override + public void sendMessage(Object message) { + try { + if (!client.isConnected()) { + client.connect(); + } + client.sendMessage(id, message); + } catch (IOException e) { + LOGGER.error(e.getMessage(), e); + } + } + + @Override + public Class getMessageType() { + throw new UnsupportedOperationException("Not supported yet!"); + } + + @Override + public Configuration getConfiguration() { + throw new UnsupportedOperationException("Not supported yet!"); + } + + @Override + public Future getAttribute(AttributeDescriptor attribute) { + throw new UnsupportedOperationException("Not supported yet!"); + } + + @Override + public Collection> getKnownAttributes() { + throw new UnsupportedOperationException("Not supported yet!"); + } + + @Override + public Future, Object>> getAttributes() { + throw new UnsupportedOperationException("Not supported yet!"); + } + + } + + private static MessageClient initializeClient(RoboContextDescriptorEntry descriptorEntry) { + return new MessageClient(URI.create(descriptorEntry.descriptor.getMetadata().get(RoboContextDescriptor.KEY_URI)), + descriptorEntry.descriptor.getId(), ConfigurationFactory.createEmptyConfiguration()); + } + + private final RoboContextDescriptorEntry descriptorEntry; + private final MessageClient client; + + ClientRemoteRoboContext(RoboContextDescriptorEntry descriptorEntry) { + this.descriptorEntry = descriptorEntry; + client = initializeClient(descriptorEntry); + } + + @Override + public LifecycleState getState() { + return null; + } + + @Override + public void shutdown() { + } + + @Override + public void stop() { + } + + @Override + public void start() { + } + + @Override + public RoboReference getReference(String id) { + return new ClientRemoteRoboReference<>(id, client); + } + + @Override + public Collection> getUnits() { + // TODO Auto-generated method stub + return null; + } + + @Override + public Scheduler getScheduler() { + throw new UnsupportedOperationException("Accessing the Scheduler remotely is not supported. Use the local scheduler."); + } + + @Override + public String getId() { + return descriptorEntry.descriptor.getId(); + } + + public InetAddress getAddress() { + return descriptorEntry.address; + } + + @Override + public Configuration getConfiguration() { + // TODO Auto-generated method stub + return null; + } } diff --git a/robo4j-core/src/main/java/com/robo4j/net/HearbeatMessageCodec.java b/robo4j-core/src/main/java/com/robo4j/net/HearbeatMessageCodec.java index f0d3a018..7d72c47a 100644 --- a/robo4j-core/src/main/java/com/robo4j/net/HearbeatMessageCodec.java +++ b/robo4j-core/src/main/java/com/robo4j/net/HearbeatMessageCodec.java @@ -55,7 +55,7 @@ public static byte[] encode(RoboContextDescriptor entry) { } public static RoboContextDescriptor decode(byte[] message) { - if (!isHeartBeatMessage(message)) { + if (notHeartBeatMessage(message)) { throw new IllegalArgumentException("Invalid message! Must start with the proper magic."); } int version = getSupportedVersion(message); @@ -73,8 +73,8 @@ public static boolean isSupportedVersion(byte[] data) { return data[2] == PROTOCOL_VERSION; } - public static boolean isHeartBeatMessage(byte[] message) { - return message[0] == MAGIC[0] && message[1] == MAGIC[1]; + public static boolean notHeartBeatMessage(byte[] message) { + return message[0] != MAGIC[0] || message[1] != MAGIC[1]; } public static String parseId(byte[] message) { diff --git a/robo4j-core/src/main/java/com/robo4j/net/LookupServiceImpl.java b/robo4j-core/src/main/java/com/robo4j/net/LookupServiceImpl.java index 570491f6..f8519a45 100644 --- a/robo4j-core/src/main/java/com/robo4j/net/LookupServiceImpl.java +++ b/robo4j-core/src/main/java/com/robo4j/net/LookupServiceImpl.java @@ -22,13 +22,21 @@ import org.slf4j.LoggerFactory; import java.io.IOException; -import java.net.*; +import java.net.DatagramPacket; +import java.net.InetSocketAddress; +import java.net.MulticastSocket; +import java.net.NetworkInterface; +import java.net.SocketAddress; +import java.net.SocketException; +import java.net.UnknownHostException; import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; import java.util.concurrent.ConcurrentHashMap; +import static com.robo4j.net.HearbeatMessageCodec.notHeartBeatMessage; + /** * Package local default implementation of the {@link LookupService}. Will * listen on the broadcast address for lookup service related packets and @@ -41,6 +49,8 @@ */ class LookupServiceImpl implements LookupService { private static final Logger LOGGER = LoggerFactory.getLogger(LookupServiceImpl.class); + private static final int PORT_ZERO = 0; + private static final NetworkInterface LOCAL_NETWORK_INTERFACE_NULL = null; // FIXME(marcus/6 Nov 2017): This should be calculated, and used when // constructing the packet private final static int MAX_PACKET_SIZE = 1500; @@ -72,7 +82,7 @@ public void run() { private void process(DatagramPacket packet) { // First a few quick checks. We want to reject updating anything as // early as possible - if (!HearbeatMessageCodec.isHeartBeatMessage(packet.getData())) { + if (notHeartBeatMessage(packet.getData())) { LOGGER.debug("Non-heartbeat packet sent to LookupService! Ignoring."); return; } @@ -122,8 +132,7 @@ public void stop() { } } - public LookupServiceImpl(String address, int port, float missedHeartbeatsBeforeRemoval, LocalLookupServiceImpl localContexts) - throws SocketException, UnknownHostException { + public LookupServiceImpl(String address, int port, float missedHeartbeatsBeforeRemoval, LocalLookupServiceImpl localContexts) throws SocketException, UnknownHostException { this.address = address; this.port = port; this.localContexts = localContexts; @@ -155,7 +164,8 @@ public RoboContext getContext(String id) { public synchronized void start() throws IOException { stop(); socket = new MulticastSocket(port); - socket.joinGroup(InetAddress.getByName(address)); + socket.joinGroup(new InetSocketAddress(address, PORT_ZERO), LOCAL_NETWORK_INTERFACE_NULL); +// socket.joinGroup(InetAddress.getByName(address)); currentUpdater = new Updater(); Thread t = new Thread(currentUpdater, "LookupService listener"); t.setDaemon(true); diff --git a/robo4j-core/src/main/java/com/robo4j/net/MessageServer.java b/robo4j-core/src/main/java/com/robo4j/net/MessageServer.java index b8ff2778..07db8d4a 100644 --- a/robo4j-core/src/main/java/com/robo4j/net/MessageServer.java +++ b/robo4j-core/src/main/java/com/robo4j/net/MessageServer.java @@ -139,7 +139,6 @@ public void start() throws IOException { configuration.getInteger("backlog", 20), bindAddress)) { listeningHost = serverSocket.getInetAddress().getHostAddress(); listeningPort = serverSocket.getLocalPort(); - var threadGroup = new ThreadGroup("Robo4J communication threads"); serverActive.set(true); while (serverActive.get()) { var messageHandler = new MessageHandler(serverSocket.accept(), callback, serverActive); diff --git a/robo4j-core/src/main/java/com/robo4j/net/ServerRemoteRoboContext.java b/robo4j-core/src/main/java/com/robo4j/net/ServerRemoteRoboContext.java index cbb23c32..e23171f9 100644 --- a/robo4j-core/src/main/java/com/robo4j/net/ServerRemoteRoboContext.java +++ b/robo4j-core/src/main/java/com/robo4j/net/ServerRemoteRoboContext.java @@ -65,7 +65,7 @@ public String getTargetContextId() { } @Override - public String getId() { + public String id() { return id; } @@ -103,7 +103,7 @@ public void sendMessage(Object message) { // FIXME: Change the serialization to be the same as for the // client to server outputStream.writeUTF(getTargetContextId()); - outputStream.writeUTF(getId()); + outputStream.writeUTF(id()); outputStream.writeObject(message); } catch (IOException e) { LOGGER.error("send message:{}", message, e); diff --git a/robo4j-core/src/main/java/com/robo4j/reflect/ReflectionScanException.java b/robo4j-core/src/main/java/com/robo4j/reflect/ReflectionScanException.java index 1a096503..8b7574d3 100644 --- a/robo4j-core/src/main/java/com/robo4j/reflect/ReflectionScanException.java +++ b/robo4j-core/src/main/java/com/robo4j/reflect/ReflectionScanException.java @@ -16,6 +16,8 @@ */ package com.robo4j.reflect; +import java.io.Serial; + /** * ReflectionScanException. * @@ -23,6 +25,7 @@ * @author Miro Wengner (@miragemiko) */ public class ReflectionScanException extends RuntimeException { + @Serial private static final long serialVersionUID = 1L; public ReflectionScanException(String message, Throwable cause) { diff --git a/robo4j-core/src/main/java/com/robo4j/scheduler/DefaultScheduler.java b/robo4j-core/src/main/java/com/robo4j/scheduler/DefaultScheduler.java index c0e644b9..72647b6a 100644 --- a/robo4j-core/src/main/java/com/robo4j/scheduler/DefaultScheduler.java +++ b/robo4j-core/src/main/java/com/robo4j/scheduler/DefaultScheduler.java @@ -22,7 +22,12 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.util.concurrent.*; +import java.util.concurrent.Callable; +import java.util.concurrent.Future; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.ScheduledThreadPoolExecutor; +import java.util.concurrent.TimeUnit; /** * This is the default scheduler used in Robo4J. @@ -30,7 +35,7 @@ * @author Marcus Hirt (@hirt) * @author Miroslav Wengner (@miragemiko) */ -public class DefaultScheduler implements Scheduler { +public final class DefaultScheduler implements Scheduler { private static final Logger LOGGER = LoggerFactory.getLogger(DefaultScheduler.class); private static final int DEFAULT_NUMBER_OF_THREADS = 2; private static final int TERMINATION_TIMEOUT_SEC = 4; diff --git a/robo4j-core/src/main/java/com/robo4j/util/IOUtil.java b/robo4j-core/src/main/java/com/robo4j/util/IOUtil.java index 434802f8..6ad58c32 100644 --- a/robo4j-core/src/main/java/com/robo4j/util/IOUtil.java +++ b/robo4j-core/src/main/java/com/robo4j/util/IOUtil.java @@ -16,6 +16,9 @@ */ package com.robo4j.util; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import java.io.BufferedInputStream; import java.io.ByteArrayOutputStream; import java.io.Closeable; @@ -34,9 +37,10 @@ * @author Miroslav Wengner (@miragemiko) */ public final class IOUtil { + private static final Logger LOGGER = LoggerFactory.getLogger(IOUtil.class); private IOUtil() { - throw new UnsupportedOperationException("Toolkit!"); + throw new UnsupportedOperationException("Not allowed!"); } public static String readStringFromUTF8Stream(InputStream is) throws IOException { @@ -66,7 +70,7 @@ public static void close(Closeable c) { try { c.close(); } catch (IOException e) { - // Ignore + LOGGER.error(e.getMessage(), e); } } diff --git a/robo4j-core/src/main/java/com/robo4j/util/StreamException.java b/robo4j-core/src/main/java/com/robo4j/util/StreamException.java index 2c242969..62c44975 100644 --- a/robo4j-core/src/main/java/com/robo4j/util/StreamException.java +++ b/robo4j-core/src/main/java/com/robo4j/util/StreamException.java @@ -16,12 +16,15 @@ */ package com.robo4j.util; +import java.io.Serial; + /** * @author Marcus Hirt (@hirt) * @author Miro Wengner (@miragemiko) */ public class StreamException extends RuntimeException { - private static final long serialVersionUID = 1L; + @Serial + private static final long serialVersionUID = 1L; public StreamException(String message) { super(message); diff --git a/robo4j-core/src/main/java/com/robo4j/util/SystemUtil.java b/robo4j-core/src/main/java/com/robo4j/util/SystemUtil.java index adb2acb0..c51686d1 100644 --- a/robo4j-core/src/main/java/com/robo4j/util/SystemUtil.java +++ b/robo4j-core/src/main/java/com/robo4j/util/SystemUtil.java @@ -42,7 +42,7 @@ private SystemUtil() { // no instances } - public static final Comparator> ID_COMPARATOR = Comparator.comparing(RoboReference::getId); + public static final Comparator> ID_COMPARATOR = Comparator.comparing(RoboReference::id); public static InputStream getInputStreamByResourceName(String resourceName) { return Thread.currentThread().getContextClassLoader().getResourceAsStream(resourceName); @@ -58,7 +58,7 @@ public static String printStateReport(RoboContext ctx) { .append(DELIMITER_HORIZONTAL).append(BREAK); for (RoboReference reference : references) { builder.append( - String.format(" %-25s %13s", reference.getId(), reference.getState().getLocalizedName())) + String.format(" %-25s %13s", reference.id(), reference.getState().getLocalizedName())) .append(BREAK); } // formatter:on @@ -79,7 +79,7 @@ public static String printSocketEndPoint(RoboReference point, RoboReference R getAttributeOrTimeout(RoboReference roboReference, At var attribute = roboReference.getAttribute(attributeDescriptor).get(TIMEOUT_MIN, TimeUnit.MINUTES); if (attribute == null) { attribute = roboReference.getAttribute(attributeDescriptor).get(TIMEOUT_MIN, TimeUnit.MINUTES); - LOGGER.error("roboReference:{}, no attribute:{}", roboReference.getId(), attributeDescriptor.getAttributeName()); + LOGGER.error("roboReference:{}, no attribute:{}", roboReference.id(), attributeDescriptor.getAttributeName()); } return attribute; } diff --git a/robo4j-core/src/test/java/com/robo4j/RoboUnitTests.java b/robo4j-core/src/test/java/com/robo4j/RoboUnitTests.java index a95730b8..fa3c2045 100644 --- a/robo4j-core/src/test/java/com/robo4j/RoboUnitTests.java +++ b/robo4j-core/src/test/java/com/robo4j/RoboUnitTests.java @@ -66,7 +66,7 @@ void testReferences() throws Exception { assertTrue(system.getState() == LifecycleState.STARTING || system.getState() == LifecycleState.STARTED); - RoboReference ref = system.getReference(consumer.getId()); + RoboReference ref = system.getReference(consumer.id()); consumer.sendMessage("Lalalala"); ref.sendMessage("Lalala"); system.shutdown(); diff --git a/robo4j-core/src/test/java/com/robo4j/RunnableProcessCounterUnitTests.java b/robo4j-core/src/test/java/com/robo4j/RunnableProcessCounterUnitTests.java index dbdc61a7..4e3d3d65 100644 --- a/robo4j-core/src/test/java/com/robo4j/RunnableProcessCounterUnitTests.java +++ b/robo4j-core/src/test/java/com/robo4j/RunnableProcessCounterUnitTests.java @@ -138,7 +138,7 @@ private static R getAttributeOrTimeout(RoboReference roboReference, At var attribute = roboReference.getAttribute(attributeDescriptor).get(TIMEOUT_MIN, TimeUnit.MINUTES); if (attribute == null) { attribute = roboReference.getAttribute(attributeDescriptor).get(TIMEOUT_MIN, TimeUnit.MINUTES); - LOGGER.error("roboReference:{}, no attribute:{}", roboReference.getId(), attributeDescriptor.getAttributeName()); + LOGGER.error("roboReference:{}, no attribute:{}", roboReference.id(), attributeDescriptor.getAttributeName()); } return attribute; } diff --git a/robo4j-core/src/main/java/com/robo4j/net/ContextMessage.java b/robo4j-core/src/test/java/com/robo4j/net/MessageProducerConsumerTest.java similarity index 62% rename from robo4j-core/src/main/java/com/robo4j/net/ContextMessage.java rename to robo4j-core/src/test/java/com/robo4j/net/MessageProducerConsumerTest.java index 34c7f8ca..a793add7 100644 --- a/robo4j-core/src/main/java/com/robo4j/net/ContextMessage.java +++ b/robo4j-core/src/test/java/com/robo4j/net/MessageProducerConsumerTest.java @@ -14,14 +14,16 @@ * You should have received a copy of the GNU General Public License * along with Robo4J. If not, see . */ + package com.robo4j.net; -/** - * Context messages. Used internally. - * - * @author Marcus Hirt (@hirt) - * @author Miroslav Wengner (@miragemiko) - */ -enum ContextMessage { - Start, End, Shutdown +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class MessageProducerConsumerTest { + private static final Logger LOGGER = LoggerFactory.getLogger(MessageProducerConsumerTest.class); + private static final int TIMEOUT_SEC = 30; + private static final String CONST_MYUUID = "myuuid"; + private static final String PROPERTY_SERVER_NAME = "ServerName"; + private static final int SERVER_LISTEN_DELAY_MILLIS = 250; } diff --git a/robo4j-core/src/test/java/com/robo4j/net/RemoteTestMessageProducer.java b/robo4j-core/src/test/java/com/robo4j/net/RemoteTestMessageProducer.java index b010a6df..f5acc001 100644 --- a/robo4j-core/src/test/java/com/robo4j/net/RemoteTestMessageProducer.java +++ b/robo4j-core/src/test/java/com/robo4j/net/RemoteTestMessageProducer.java @@ -133,7 +133,7 @@ public void sendRandomMessage() { final String text = StringToolkit.getRandomMessage(10); // We're sending a reference to ourself for getting the acks... - final TestMessageType message = new TestMessageType(number, text, getContext().getReference(getId())); + final TestMessageType message = new TestMessageType(number, text, getContext().getReference(id())); RoboContext ctx = LookupServiceProvider.getDefaultLookupService().getContext(targetContext); ctx.getReference(target).sendMessage(message); } diff --git a/robo4j-core/src/test/java/com/robo4j/net/RoboTestContext.java b/robo4j-core/src/test/java/com/robo4j/net/RoboTestContext.java index f58a56fd..4268e271 100644 --- a/robo4j-core/src/test/java/com/robo4j/net/RoboTestContext.java +++ b/robo4j-core/src/test/java/com/robo4j/net/RoboTestContext.java @@ -93,7 +93,7 @@ public Configuration getConfiguration() { } public void addRef(RoboTestReference roboTestReference) { - referenceMap.put(roboTestReference.getId(), roboTestReference); + referenceMap.put(roboTestReference.id(), roboTestReference); } } diff --git a/robo4j-core/src/test/java/com/robo4j/net/RoboTestReference.java b/robo4j-core/src/test/java/com/robo4j/net/RoboTestReference.java index 910b14b5..41ed1371 100644 --- a/robo4j-core/src/test/java/com/robo4j/net/RoboTestReference.java +++ b/robo4j-core/src/test/java/com/robo4j/net/RoboTestReference.java @@ -44,7 +44,7 @@ public RoboTestReference(String id, Configuration configuration) { } @Override - public String getId() { + public String id() { return id; } diff --git a/robo4j-socket-http/src/main/java/com/robo4j/socket/http/request/RoboRequestCallable.java b/robo4j-socket-http/src/main/java/com/robo4j/socket/http/request/RoboRequestCallable.java index 3ed8fc8e..e4d801b8 100644 --- a/robo4j-socket-http/src/main/java/com/robo4j/socket/http/request/RoboRequestCallable.java +++ b/robo4j-socket-http/src/main/java/com/robo4j/socket/http/request/RoboRequestCallable.java @@ -81,7 +81,7 @@ public HttpResponseProcess call() throws Exception { resultBuilder.setResult(factory.processGet(context)); } else { - resultBuilder.setTarget(pathConfig.getRoboUnit().getId()); + resultBuilder.setTarget(pathConfig.getRoboUnit().id()); final Object unitDescription; // the system needs to have one more worker thread to evaluate Future get final HttpRequestDenominator denominator = (HttpRequestDenominator) decoratedRequest @@ -91,7 +91,7 @@ public HttpResponseProcess call() throws Exception { if (requestAttributes == null) { unitDescription = factory.processGet(pathConfig); } else if (requestAttributes.isEmpty()) { - RoboReference unit = context.getReference(pathConfig.getRoboUnit().getId()); + RoboReference unit = context.getReference(pathConfig.getRoboUnit().id()); PathAttributeListDTO pathAttributes = new PathAttributeListDTO(); unit.getKnownAttributes().forEach(a -> { @@ -102,7 +102,7 @@ public HttpResponseProcess call() throws Exception { }); unitDescription = ReflectUtils.createJson(pathAttributes); } else { - RoboReference unit = context.getReference(pathConfig.getRoboUnit().getId()); + RoboReference unit = context.getReference(pathConfig.getRoboUnit().id()); List attributes = new ArrayList<>(); for (AttributeDescriptor attr : unit.getKnownAttributes()) { @@ -131,7 +131,7 @@ public HttpResponseProcess call() throws Exception { if (pathConfig.getPath().equals(UTF8_SOLIDUS)) { resultBuilder.setCode(StatusCode.BAD_REQUEST); } else { - resultBuilder.setTarget(pathConfig.getRoboUnit().getId()); + resultBuilder.setTarget(pathConfig.getRoboUnit().id()); Object respObj = factory.processPost(pathConfig.getRoboUnit(), decoratedRequest.getMessage()); if (respObj == null) { resultBuilder.setCode(StatusCode.BAD_REQUEST); diff --git a/robo4j-socket-http/src/main/java/com/robo4j/socket/http/request/RoboRequestFactory.java b/robo4j-socket-http/src/main/java/com/robo4j/socket/http/request/RoboRequestFactory.java index 9ee800c5..2233738c 100644 --- a/robo4j-socket-http/src/main/java/com/robo4j/socket/http/request/RoboRequestFactory.java +++ b/robo4j-socket-http/src/main/java/com/robo4j/socket/http/request/RoboRequestFactory.java @@ -64,7 +64,7 @@ public Object processGet(RoboContext context) { if (!context.getUnits().isEmpty()) { final List unitList = context.getUnits().stream() - .map(u -> new ResponseUnitDTO(u.getId(), u.getState())).collect(Collectors.toList()); + .map(u -> new ResponseUnitDTO(u.id(), u.getState())).collect(Collectors.toList()); unitList.add(0, new ResponseUnitDTO(context.getId(), context.getState())); return JsonUtil.toJsonArray(unitList); } else { @@ -106,7 +106,7 @@ public Object processGet(ServerPathConfig pathConfig) { } else { final ResponseDecoderUnitDTO result = new ResponseDecoderUnitDTO(); - result.setId(unitRef.getId()); + result.setId(unitRef.id()); result.setCodec(decoder.getDecodedClass().getName()); result.setMethods(GET_POST_METHODS); return ReflectUtils.createJson(result); diff --git a/robo4j-socket-http/src/test/java/com/robo4j/socket/http/test/units/RoboHttpDynamicTests.java b/robo4j-socket-http/src/test/java/com/robo4j/socket/http/test/units/RoboHttpDynamicTests.java index 568aefa5..94b8002b 100644 --- a/robo4j-socket-http/src/test/java/com/robo4j/socket/http/test/units/RoboHttpDynamicTests.java +++ b/robo4j-socket-http/src/test/java/com/robo4j/socket/http/test/units/RoboHttpDynamicTests.java @@ -208,7 +208,7 @@ private static R getAttributeOrTimeout(RoboReference roboReference, At var attribute = roboReference.getAttribute(attributeDescriptor).get(TIMEOUT_MIN, TimeUnit.MINUTES); if (attribute == null) { attribute = roboReference.getAttribute(attributeDescriptor).get(TIMEOUT_MIN, TimeUnit.MINUTES); - LOGGER.error("roboReference:{}, no attribute:{}", roboReference.getId(), attributeDescriptor.getAttributeName()); + LOGGER.error("roboReference:{}, no attribute:{}", roboReference.id(), attributeDescriptor.getAttributeName()); } return attribute; } From 92d3366d6015b0268b2f467db9549ffa81db3dbd Mon Sep 17 00:00:00 2001 From: Miro Wengner Date: Fri, 8 Nov 2024 23:41:51 +0100 Subject: [PATCH 05/15] [75] improvements --- .../src/main/java/com/robo4j/RoboUnit.java | 447 ++++--- .../i2c/adafruitoled/SSD1306DeviceTest.java | 4 +- .../robo4j/hw/rpi/i2c/pwm/ServoTester.java | 4 +- .../hw/rpi/camera/CameraClientException.java | 3 + .../com/robo4j/hw/rpi/camera/RaspiDevice.java | 1 + .../java/com/robo4j/hw/rpi/gps/NmeaUtils.java | 120 +- .../AccelerometerLSM303Device.java | 8 +- .../adafruitbackpack/AbstractBackpack.java | 27 +- .../adafruitbackpack/AlphanumericDevice.java | 2 +- .../BiColor8x8MatrixDevice.java | 1 + .../adafruitbackpack/LedBackpackFactory.java | 21 +- .../i2c/adafruitbackpack/MatrixRotation.java | 36 +- .../robo4j/hw/rpi/i2c/adafruitlcd/Button.java | 92 +- .../hw/rpi/i2c/adafruitlcd/Message.java | 8 +- .../i2c/adafruitlcd/impl/AdafruitLcdImpl.java | 1118 ++++++++--------- .../adafruitlcd/mockup/AdafruitLcdMockup.java | 27 +- .../rpi/i2c/adafruitoled/SSD1306Device.java | 2 +- .../hw/rpi/i2c/gps/XA1110PositionEvent.java | 10 +- .../hw/rpi/i2c/gps/XA1110VelocityEvent.java | 10 +- .../robo4j/hw/rpi/i2c/pwm/PCA9685Servo.java | 2 +- .../hw/rpi/i2c/pwm/PWMPCA9685Device.java | 418 +++--- .../rpi/imu/bno/Bno055CalibrationStatus.java | 2 +- .../robo4j/hw/rpi/imu/bno/Bno055Device.java | 2 +- .../robo4j/hw/rpi/imu/bno/Bno080Device.java | 2 +- .../robo4j/hw/rpi/imu/bno/Bno080Factory.java | 8 +- .../robo4j/hw/rpi/imu/bno/DataEvent3f.java | 2 + .../robo4j/hw/rpi/imu/bno/VectorEvent.java | 2 + .../imu/bno/impl/AbstractBno055Device.java | 865 +++++++------ .../imu/bno/impl/AbstractBno080Device.java | 4 +- .../rpi/imu/bno/impl/Bno055SerialDevice.java | 44 +- .../hw/rpi/imu/bno/impl/Bno080SPIDevice.java | 8 +- .../hw/rpi/imu/bno/shtp/ShtpChannel.java | 2 +- .../rpi/imu/bno/shtp/ShtpPacketRequest.java | 2 +- .../robo4j/hw/rpi/imu/bno/shtp/ShtpUtils.java | 2 +- .../com/robo4j/hw/rpi/lcd/StringUtils.java | 10 +- .../hw/rpi/pad/LF710ButtonObserver.java | 217 ++-- .../com/robo4j/hw/rpi/pad/LF710Exception.java | 5 +- .../java/com/robo4j/hw/rpi/pad/LF710Part.java | 3 +- .../com/robo4j/hw/rpi/pad/LF710State.java | 2 +- .../com/robo4j/hw/rpi/serial/SerialUtil.java | 2 - .../robo4j/hw/rpi/serial/ydlidar/Command.java | 18 +- .../hw/rpi/serial/ydlidar/DataHeader.java | 9 +- .../hw/rpi/serial/ydlidar/DeviceInfo.java | 2 +- .../hw/rpi/serial/ydlidar/HealthInfo.java | 2 +- .../hw/rpi/serial/ydlidar/ResponseHeader.java | 25 +- .../hw/rpi/serial/ydlidar/YDLidarDevice.java | 23 +- 46 files changed, 1781 insertions(+), 1843 deletions(-) diff --git a/robo4j-core/src/main/java/com/robo4j/RoboUnit.java b/robo4j-core/src/main/java/com/robo4j/RoboUnit.java index ea36fc55..dca09210 100644 --- a/robo4j-core/src/main/java/com/robo4j/RoboUnit.java +++ b/robo4j-core/src/main/java/com/robo4j/RoboUnit.java @@ -22,271 +22,252 @@ import java.util.Collections; import java.util.HashMap; import java.util.Map; +import java.util.Objects; import java.util.concurrent.Future; /** * The core component. Subclass this to provide a messaging capable agent for a * robot component. - * + * * @author Marcus Hirt (@hirt) * @author Miroslav Wengner (@miragemiko) */ public abstract class RoboUnit implements RoboReference { - // Yay for erasure - private final Class messageType; - private final RoboContext context; - private final String id; - private volatile LifecycleState state = LifecycleState.UNINITIALIZED; - private RoboReference reference; - private Configuration configuration; + // Yay for erasure + private final Class messageType; + private final RoboContext context; + private final String id; + private volatile LifecycleState state = LifecycleState.UNINITIALIZED; + private RoboReference reference; + private Configuration configuration; - /** - * Constructor. - * - * @param messageType - * messageType - * @param context - * desired Robo context - * @param id - * id of RoboUnit - */ - public RoboUnit(Class messageType, RoboContext context, String id) { - this.messageType = messageType; - this.context = context; - this.id = id; - if (context instanceof RoboSystem) { - reference = ((RoboSystem) context).getReference(this); - } - } + /** + * Constructor. + * + * @param messageType messageType + * @param context desired Robo context + * @param id id of RoboUnit + */ + public RoboUnit(Class messageType, RoboContext context, String id) { + this.messageType = messageType; + this.context = context; + this.id = id; + if (context instanceof RoboSystem) { + reference = ((RoboSystem) context).getReference(this); + } + } - /** - * @return the {@link RoboSystem} unique identifier for this unit. - */ - @Override - public String id() { - return id; - } + /** + * @return the {@link RoboSystem} unique identifier for this unit. + */ + @Override + public String id() { + return id; + } - /** - * @return the {@link RoboContext} associated with this unit. - */ - public RoboContext getContext() { - return context; - } + /** + * @return the {@link RoboContext} associated with this unit. + */ + public RoboContext getContext() { + return context; + } - /** - * If initializing the unit programmatically, call unit with the proper - * configuration. - * - * @param configuration - * the {@link Configuration} provided. - * @throws ConfigurationException - * possible exception - */ - public void initialize(Configuration configuration) throws ConfigurationException { - setConfiguration(configuration); - onInitialization(configuration); - setState(LifecycleState.INITIALIZED); - } + /** + * If initializing the unit programmatically, call unit with the proper + * configuration. + * + * @param configuration the {@link Configuration} provided. + * @throws ConfigurationException possible exception + */ + public void initialize(Configuration configuration) throws ConfigurationException { + setConfiguration(configuration); + onInitialization(configuration); + setState(LifecycleState.INITIALIZED); + } - /** - * Should be implemented by subclasses to do the actual Unit specific part of - * the initialization. - * - * @param configuration - * the {@link Configuration} provided. - * @throws ConfigurationException - * possible exception - */ - protected void onInitialization(Configuration configuration) throws ConfigurationException { - } + /** + * Should be implemented by subclasses to do the actual Unit specific part of + * the initialization. + * + * @param configuration the {@link Configuration} provided. + * @throws ConfigurationException possible exception + */ + protected void onInitialization(Configuration configuration) throws ConfigurationException { + } - /** - * Should be overridden in subclasses which need to do some initialization on - * start. - */ - public void start() { - } + /** + * Should be overridden in subclasses which need to do some initialization on + * start. + */ + public void start() { + } - /** - * Should be overridden in subclasses needing to do some work when stopping. - */ - public void stop() { - } + /** + * Should be overridden in subclasses needing to do some work when stopping. + */ + public void stop() { + } - /** - * Should be overridden in subclasses needing to do some work when shutting - * down. - */ - public void shutdown() { - } + /** + * Should be overridden in subclasses needing to do some work when shutting + * down. + */ + public void shutdown() { + } - /** - * Returns the state of this unit. - * - * @return the state in the life cycle of this unit. - */ - @Override - public LifecycleState getState() { - return state; - } + /** + * Returns the state of this unit. + * + * @return the state in the life cycle of this unit. + */ + @Override + public LifecycleState getState() { + return state; + } - /** - * It is considered good form to return the types that you can respond to. This - * method should be overriden in subclasses. Note that it is allowed for an - * agent to return the empty set. Returning null is not allowed. - * - * @return the message types accepted by this unit. - */ - public Collection> getAcceptedMessageTypes() { - return Collections.emptySet(); - } + /** + * It is considered good form to return the types that you can respond to. This + * method should be overriden in subclasses. Note that it is allowed for an + * agent to return the empty set. Returning null is not allowed. + * + * @return the message types accepted by this unit. + */ + public Collection> getAcceptedMessageTypes() { + return Collections.emptySet(); + } - /** - * Changes the {@link LifecycleState}. - * - * @param state - * the state to change to. - * - * @see LifecycleState for allowable transitions. - */ - public void setState(LifecycleState state) { - this.state = state; - } + /** + * Changes the {@link LifecycleState}. + * + * @param state the state to change to. + * @see LifecycleState for allowable transitions. + */ + public void setState(LifecycleState state) { + this.state = state; + } - @Override - public Configuration getConfiguration() { - return configuration; - } + @Override + public Configuration getConfiguration() { + return configuration; + } - /** - * Sends a message to this unit by posting a message on the message bus. - * - * @see #onMessage(Object) - */ - @Override - public void sendMessage(T message) { - reference.sendMessage(message); - } + /** + * Sends a message to this unit by posting a message on the message bus. + * + * @see #onMessage(Object) + */ + @Override + public void sendMessage(T message) { + reference.sendMessage(message); + } - /** - * Will post a message to get the attributes on the message queue. - * - * @see #onGetAttributes() - */ - @Override - public Future, Object>> getAttributes() { - return reference.getAttributes(); - } + /** + * Will post a message to get the attributes on the message queue. + * + * @see #onGetAttributes() + */ + @Override + public Future, Object>> getAttributes() { + return reference.getAttributes(); + } - /** - * Retrieves an attribute from this unit. - * - * @see #getAttribute(AttributeDescriptor) - */ - @Override - public Future getAttribute(AttributeDescriptor attribute) { - return reference.getAttribute(attribute); - } + /** + * Retrieves an attribute from this unit. + * + * @see #getAttribute(AttributeDescriptor) + */ + @Override + public Future getAttribute(AttributeDescriptor attribute) { + return reference.getAttribute(attribute); + } - /** - * Override in subclasses to expose the attributes known. - */ - @Override - public Collection> getKnownAttributes() { - return Collections.emptyList(); - } + /** + * Override in subclasses to expose the attributes known. + */ + @Override + public Collection> getKnownAttributes() { + return Collections.emptyList(); + } - @Override - public Class getMessageType() { - return messageType; - } + @Override + public Class getMessageType() { + return messageType; + } - /** - * Should be overridden in subclasses to define the behaviour of the unit. This - * method should normally not be called directly, unless you have a very good - * reason. It is used by the system to deliver messages to the unit. - * - * @param message - * the message received by this unit. - * - */ - public void onMessage(T message) { - // Note that this method is public so the scheduler has access. We may - // want to consider other means of accessing it to keep it protected. - } + /** + * Should be overridden in subclasses to define the behaviour of the unit. This + * method should normally not be called directly, unless you have a very good + * reason. It is used by the system to deliver messages to the unit. + * + * @param message the message received by this unit. + */ + public void onMessage(T message) { + // Note that this method is public so the scheduler has access. We may + // want to consider other means of accessing it to keep it protected. + } - /** - * May be overridden in subclasses for more performance. The default - * implementation will get the job done though. - * - * @return the map of all the attributes. - */ - protected Map, Object> onGetAttributes() { - Map, Object> result = new HashMap<>(); - Collection> knownAttributes = getKnownAttributes(); - for (AttributeDescriptor descriptor : knownAttributes) { - result.put(descriptor, getAttribute(descriptor)); - } - return result; - } + /** + * May be overridden in subclasses for more performance. The default + * implementation will get the job done though. + * + * @return the map of all the attributes. + */ + protected Map, Object> onGetAttributes() { + var result = new HashMap, Object>(); + Collection> knownAttributes = getKnownAttributes(); + for (AttributeDescriptor descriptor : knownAttributes) { + result.put(descriptor, getAttribute(descriptor)); + } + return result; + } - /** - * Should be overridden in subclasses to provide attributes. - * - * @param descriptor - * the descriptor for which to return the attribute. - * @param - * attribute descriptor - * @return the attribute value. - */ - protected R onGetAttribute(AttributeDescriptor descriptor) { - return null; - } + /** + * Should be overridden in subclasses to provide attributes. + * + * @param descriptor the descriptor for which to return the attribute. + * @param attribute descriptor + * @return the attribute value. + */ + protected R onGetAttribute(AttributeDescriptor descriptor) { + return null; + } - /** - * @return a RoboReference. Internal use only. - */ - RoboReference internalGetReference() { - // NOTE(Marcus/Jan 27, 2017): Can we avoid this? - if (reference == null) { - return getContext().getReference(id()); - } else { - return reference; - } - } + /** + * @return a RoboReference. Internal use only. + */ + RoboReference internalGetReference() { + // NOTE(Marcus/Jan 27, 2017): Can we avoid this? + if (reference == null) { + return getContext().getReference(id()); + } else { + return reference; + } + } - private void setConfiguration(Configuration configuration) { - this.configuration = configuration; - } + private void setConfiguration(Configuration configuration) { + this.configuration = configuration; + } - @Override - public int hashCode() { - return id.hashCode(); - } + @Override + public boolean equals(Object object) { + if (this == object) return true; + if (object == null) return false; + RoboUnit roboUnit = (RoboUnit) object; + return Objects.equals(id, roboUnit.id); + } - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - RoboUnit other = (RoboUnit) obj; - if (id == null) { - if (other.id != null) - return false; - } else if (!id.equals(other.id)) - return false; - return true; - } + @Override + public int hashCode() { + return Objects.hashCode(id); + } - /* - * (non-Javadoc) - * - * @see java.lang.Object#toString() - */ - @Override - public String toString() { - return String.format("%s [id=%s]", getClass().getName(), id()); - } + /* + * (non-Javadoc) + * + * @see java.lang.Object#toString() + */ + @Override + public String toString() { + return String.format("%s [id=%s]", getClass().getName(), id()); + } } diff --git a/robo4j-hw-rpi/src/examples/java/com/robo4j/hw/rpi/i2c/adafruitoled/SSD1306DeviceTest.java b/robo4j-hw-rpi/src/examples/java/com/robo4j/hw/rpi/i2c/adafruitoled/SSD1306DeviceTest.java index 585683eb..9dff0655 100644 --- a/robo4j-hw-rpi/src/examples/java/com/robo4j/hw/rpi/i2c/adafruitoled/SSD1306DeviceTest.java +++ b/robo4j-hw-rpi/src/examples/java/com/robo4j/hw/rpi/i2c/adafruitoled/SSD1306DeviceTest.java @@ -24,6 +24,8 @@ import java.awt.*; import java.io.IOException; +import static com.robo4j.hw.rpi.lcd.StringUtils.STRING_SPACE; + /** * Example which prints Hello World and draws a little. It also shows the image * in a JFrame, so that it is easy to know what to expect. @@ -57,7 +59,7 @@ public static void main(String[] args) throws IOException { LOGGER.info("If the number of lines do not match your device,"); LOGGER.info("please add the number of lines as the first argument!"); - String text = args.length > 0 ? String.join(" ", args) : "Hello Maxi!"; + String text = args.length > 0 ? String.join(STRING_SPACE, args) : "Hello Maxi!"; Graphics2D gc = oled.getGraphicsContext(); gc.setColor(Color.white); gc.setBackground(Color.black); diff --git a/robo4j-hw-rpi/src/examples/java/com/robo4j/hw/rpi/i2c/pwm/ServoTester.java b/robo4j-hw-rpi/src/examples/java/com/robo4j/hw/rpi/i2c/pwm/ServoTester.java index 6c0f2a7d..82fbc303 100644 --- a/robo4j-hw-rpi/src/examples/java/com/robo4j/hw/rpi/i2c/pwm/ServoTester.java +++ b/robo4j-hw-rpi/src/examples/java/com/robo4j/hw/rpi/i2c/pwm/ServoTester.java @@ -23,6 +23,8 @@ import java.io.IOException; import java.util.Scanner; +import static com.robo4j.hw.rpi.lcd.StringUtils.STRING_SPACE; + /** * This is a simple example allowing you to try out the servos connected to a * PCA9685. @@ -50,7 +52,7 @@ public static void main(String[] args) throws IOException, InterruptedException printPrompt(); while (!"q".equals(lastCommand = scanner.nextLine())) { lastCommand = lastCommand.trim(); - String[] split = lastCommand.split(" "); + String[] split = lastCommand.split(STRING_SPACE); if (split.length != 2) { LOGGER.debug("Could not parse {}. Please try again!", lastCommand); continue; diff --git a/robo4j-hw-rpi/src/main/java/com/robo4j/hw/rpi/camera/CameraClientException.java b/robo4j-hw-rpi/src/main/java/com/robo4j/hw/rpi/camera/CameraClientException.java index e39a693e..e70e8c4d 100644 --- a/robo4j-hw-rpi/src/main/java/com/robo4j/hw/rpi/camera/CameraClientException.java +++ b/robo4j-hw-rpi/src/main/java/com/robo4j/hw/rpi/camera/CameraClientException.java @@ -16,11 +16,14 @@ */ package com.robo4j.hw.rpi.camera; +import java.io.Serial; + /** * @author Marcus Hirt (@hirt) * @author Miro Wengner (@miragemiko) */ public class CameraClientException extends RuntimeException { + @Serial private static final long serialVersionUID = 1L; public CameraClientException(String message, Throwable cause) { diff --git a/robo4j-hw-rpi/src/main/java/com/robo4j/hw/rpi/camera/RaspiDevice.java b/robo4j-hw-rpi/src/main/java/com/robo4j/hw/rpi/camera/RaspiDevice.java index 4c78eba5..0033a6a1 100644 --- a/robo4j-hw-rpi/src/main/java/com/robo4j/hw/rpi/camera/RaspiDevice.java +++ b/robo4j-hw-rpi/src/main/java/com/robo4j/hw/rpi/camera/RaspiDevice.java @@ -43,6 +43,7 @@ public RaspiDevice() { public byte[] executeCommandRaspistill(String command) { final Runtime runtime = Runtime.getRuntime(); try (final ByteArrayOutputStream baos = new ByteArrayOutputStream()) { + // TODO: solve deprecated Process process = runtime.exec(command); InputStream imageArray = process.getInputStream(); diff --git a/robo4j-hw-rpi/src/main/java/com/robo4j/hw/rpi/gps/NmeaUtils.java b/robo4j-hw-rpi/src/main/java/com/robo4j/hw/rpi/gps/NmeaUtils.java index 77a4b86a..235ca718 100644 --- a/robo4j-hw-rpi/src/main/java/com/robo4j/hw/rpi/gps/NmeaUtils.java +++ b/robo4j-hw-rpi/src/main/java/com/robo4j/hw/rpi/gps/NmeaUtils.java @@ -18,71 +18,75 @@ /** * Utilities to help with Nmea messages. - * + * * @author Marcus Hirt (@hirt) * @author Miro Wengner (@miragemiko) */ public final class NmeaUtils { - /** - * Checks the NEMA line checksum. - * - * @param data - * a line of NEMA data. - * @return true if the checksum is valid. - */ - public final static boolean hasValidCheckSum(String data) { - if (!data.startsWith("$")) { - return false; - } - try { - int indexOfStar = data.indexOf('*'); - if (indexOfStar <= 0 || indexOfStar >= data.length()) { - return false; - } - String chk = data.substring(1, indexOfStar); - String checksumStr = data.substring(indexOfStar + 1); - int valid = Integer.parseInt(checksumStr.trim(), 16); - int checksum = 0; - for (int i = 0; i < chk.length(); i++) { - checksum = checksum ^ chk.charAt(i); - } - return checksum == valid; - } catch (Exception e) { - return false; - } - } - public static int getInt(String string) { - if (string == null || "".equals(string)) { - return -1; - } - return Integer.parseInt(string); - } + private NmeaUtils() { + } + + /** + * Checks the NEMA line checksum. + * + * @param data a line of NEMA data. + * @return true if the checksum is valid. + */ + public static boolean hasValidCheckSum(String data) { + if (!data.startsWith("$")) { + return false; + } + try { + int indexOfStar = data.indexOf('*'); + // TODO review the statement + if (indexOfStar <= 0 || indexOfStar >= data.length()) { + return false; + } + String chk = data.substring(1, indexOfStar); + String checksumStr = data.substring(indexOfStar + 1); + int valid = Integer.parseInt(checksumStr.trim(), 16); + int checksum = 0; + for (int i = 0; i < chk.length(); i++) { + checksum = checksum ^ chk.charAt(i); + } + return checksum == valid; + } catch (Exception e) { + return false; + } + } + + public static int getInt(String string) { + if (string == null || string.isEmpty()) { + return -1; + } + return Integer.parseInt(string); + } - public static float getFloat(String string) { - if (string == null || "".equals(string)) { - return Float.NaN; - } - return Float.parseFloat(string); - } + public static float getFloat(String string) { + if (string == null || string.isEmpty()) { + return Float.NaN; + } + return Float.parseFloat(string); + } - public static float parseNmeaFormatCoordinate(String string) { - int index = string.indexOf('.'); - if (index < 0) { - return Float.NaN; - } - float minutes = Float.parseFloat(string.substring(index - 2)); - float degrees = Integer.parseInt(string.substring(0, index - 2)); - return degrees + minutes / 60.0f; - } + public static float parseNmeaFormatCoordinate(String string) { + int index = string.indexOf('.'); + if (index < 0) { + return Float.NaN; + } + float minutes = Float.parseFloat(string.substring(index - 2)); + float degrees = Integer.parseInt(string.substring(0, index - 2)); + return degrees + minutes / 60.0f; + } - public static String cleanLine(String dataLine) { - // We only attempt to recover the first part, and only if it looks - // like it could be an ok string. - if (dataLine.startsWith("$")) { - return dataLine.substring(0, Math.min(dataLine.indexOf('*') + 3, dataLine.length())); - } - return dataLine; - } + public static String cleanLine(String dataLine) { + // We only attempt to recover the first part, and only if it looks + // like it could be an ok string. + if (dataLine.startsWith("$")) { + return dataLine.substring(0, Math.min(dataLine.indexOf('*') + 3, dataLine.length())); + } + return dataLine; + } } diff --git a/robo4j-hw-rpi/src/main/java/com/robo4j/hw/rpi/i2c/accelerometer/AccelerometerLSM303Device.java b/robo4j-hw-rpi/src/main/java/com/robo4j/hw/rpi/i2c/accelerometer/AccelerometerLSM303Device.java index bac90ce2..8e11ac8d 100644 --- a/robo4j-hw-rpi/src/main/java/com/robo4j/hw/rpi/i2c/accelerometer/AccelerometerLSM303Device.java +++ b/robo4j-hw-rpi/src/main/java/com/robo4j/hw/rpi/i2c/accelerometer/AccelerometerLSM303Device.java @@ -55,7 +55,7 @@ public enum DataRate { POWER_DOWN(0x0), HZ_1(0x10), HZ_10(0x20), HZ_25(0x30), HZ_50(0x40), HZ_100(0x50), HZ_200(0x60), HZ_400( 0x70), HZ_LP_1620(0x80), HZ_N_1354_LP_5376(0x81); - private int ctrlCode; + private final int ctrlCode; DataRate(int ctrlCode) { this.ctrlCode = ctrlCode; @@ -69,7 +69,7 @@ public int getCtrlCode() { public enum PowerMode { NORMAL(0x0), LOW_POWER(0x8); - private int ctrlCode; + private final int ctrlCode; PowerMode(int ctrlCode) { this.ctrlCode = ctrlCode; @@ -83,8 +83,8 @@ public int getCtrlCode() { public enum FullScale { G_2(0x0, 1), G_4(0x10, 1), G_8(0x20, 4), G_16(0x30, 12); - private int ctrlCode; - private int sensitivity; + private final int ctrlCode; + private final int sensitivity; FullScale(int ctrlCode, int sensitivity) { this.ctrlCode = ctrlCode; diff --git a/robo4j-hw-rpi/src/main/java/com/robo4j/hw/rpi/i2c/adafruitbackpack/AbstractBackpack.java b/robo4j-hw-rpi/src/main/java/com/robo4j/hw/rpi/i2c/adafruitbackpack/AbstractBackpack.java index 1e14d227..145a792d 100644 --- a/robo4j-hw-rpi/src/main/java/com/robo4j/hw/rpi/i2c/adafruitbackpack/AbstractBackpack.java +++ b/robo4j-hw-rpi/src/main/java/com/robo4j/hw/rpi/i2c/adafruitbackpack/AbstractBackpack.java @@ -23,6 +23,7 @@ import org.slf4j.LoggerFactory; import java.io.IOException; +import java.util.Arrays; /** * AbstractBackpack is the abstraction for all Adafruit Backpack devices @@ -84,20 +85,20 @@ void setColorByMatrixToBuffer(short x, short y, BiColor color) { // Turn on red LED. buffer[y] |= _BV(intToShort(x + 8)); // Turn off green LED. - buffer[y] &= ~_BV(x); + buffer[y] &= (short) ~_BV(x); break; case YELLOW: // Turn on green and red LED. - buffer[y] |= _BV(intToShort(x + 8)) | _BV(x); + buffer[y] |= (short) (_BV(intToShort(x + 8)) | _BV(x)); break; case GREEN: // Turn on green LED. buffer[y] |= _BV(x); // Turn off red LED. - buffer[y] &= ~_BV(intToShort(x + 8)); + buffer[y] &= (short) ~_BV(intToShort(x + 8)); break; case OFF: - buffer[y] &= ~_BV(x) & ~_BV(intToShort(x + 8)); + buffer[y] &= (short) (~_BV(x) & ~_BV(intToShort(x + 8))); break; default: LOGGER.warn("setColorByMatrixToBuffer: {}", color); @@ -137,21 +138,21 @@ void setColorToBarBuffer(short a, short c, BiColor color) { // Turn on red LED. buffer[c] |= _BV(a); // Turn off green LED. - buffer[c] &= ~_BV(intToShort(a + 8)); + buffer[c] &= (short) ~_BV(intToShort(a + 8)); break; case YELLOW: // Turn on red and green LED. - buffer[c] |= _BV(a) | _BV(intToShort(a + 8)); + buffer[c] |= (short) (_BV(a) | _BV(intToShort(a + 8))); break; case GREEN: // Turn on green LED. buffer[c] |= _BV(intToShort(a + 8)); // Turn off red LED. - buffer[c] &= ~_BV(a); + buffer[c] &= (short) ~_BV(a); break; case OFF: // Turn off red and green LED. - buffer[c] &= ~_BV(a) & ~_BV(intToShort(a + 8)); + buffer[c] &= (short) (~_BV(a) & ~_BV(intToShort(a + 8))); break; default: LOGGER.warn("setColorToBarBuffer: {}", color); @@ -170,18 +171,16 @@ private void initiate(int brightness) throws IOException { private void writeDisplay() throws IOException { int address = 0; - for (int i = 0; i < buffer.length; i++) { + for (short value : buffer) { // i2CConfig.write(address++, (byte) (buffer[i] & 0xFF)); // i2CConfig.write(address++, (byte) (buffer[i] >> 8)); - writeByte(address++, (byte) (buffer[i] & 0xFF)); - writeByte(address++, (byte) (buffer[i] >> 8)); + writeByte(address++, (byte) (value & 0xFF)); + writeByte(address++, (byte) (value >> 8)); } } private void clearBuffer() throws IOException { - for (int i = 0; i < buffer.length; i++) { - buffer[i] = 0; - } + Arrays.fill(buffer, (short) 0); } private short _BV(short i) { diff --git a/robo4j-hw-rpi/src/main/java/com/robo4j/hw/rpi/i2c/adafruitbackpack/AlphanumericDevice.java b/robo4j-hw-rpi/src/main/java/com/robo4j/hw/rpi/i2c/adafruitbackpack/AlphanumericDevice.java index 153f8924..8d89aca0 100644 --- a/robo4j-hw-rpi/src/main/java/com/robo4j/hw/rpi/i2c/adafruitbackpack/AlphanumericDevice.java +++ b/robo4j-hw-rpi/src/main/java/com/robo4j/hw/rpi/i2c/adafruitbackpack/AlphanumericDevice.java @@ -39,7 +39,7 @@ public class AlphanumericDevice extends AbstractBackpack { public static final int POSITION_START = 0; public static final int POSITION_MAX = 3; - private static int[] FONTS = { 0b0000000000000001, 0b0000000000000010, 0b0000000000000100, 0b0000000000001000, 0b0000000000010000, + private static final int[] FONTS = { 0b0000000000000001, 0b0000000000000010, 0b0000000000000100, 0b0000000000001000, 0b0000000000010000, 0b0000000000100000, 0b0000000001000000, 0b0000000010000000, 0b0000000100000000, 0b0000001000000000, 0b0000010000000000, 0b0000100000000000, 0b0001000000000000, 0b0010000000000000, 0b0100000000000000, 0b1000000000000000, 0b0000000000000000, 0b0000000000000000, 0b0000000000000000, 0b0000000000000000, 0b0000000000000000, 0b0000000000000000, 0b0000000000000000, diff --git a/robo4j-hw-rpi/src/main/java/com/robo4j/hw/rpi/i2c/adafruitbackpack/BiColor8x8MatrixDevice.java b/robo4j-hw-rpi/src/main/java/com/robo4j/hw/rpi/i2c/adafruitbackpack/BiColor8x8MatrixDevice.java index e2bba390..901b05d6 100644 --- a/robo4j-hw-rpi/src/main/java/com/robo4j/hw/rpi/i2c/adafruitbackpack/BiColor8x8MatrixDevice.java +++ b/robo4j-hw-rpi/src/main/java/com/robo4j/hw/rpi/i2c/adafruitbackpack/BiColor8x8MatrixDevice.java @@ -65,6 +65,7 @@ public void drawPixel(short x, short y, BiColor color) { throw new IllegalArgumentException("x and/or y out of bounds. x=" + x + " y=" + y); } + // TODO : correct switch switch (rotation) { case DEFAULT_X_Y: break; diff --git a/robo4j-hw-rpi/src/main/java/com/robo4j/hw/rpi/i2c/adafruitbackpack/LedBackpackFactory.java b/robo4j-hw-rpi/src/main/java/com/robo4j/hw/rpi/i2c/adafruitbackpack/LedBackpackFactory.java index 02ce90f6..73599128 100644 --- a/robo4j-hw-rpi/src/main/java/com/robo4j/hw/rpi/i2c/adafruitbackpack/LedBackpackFactory.java +++ b/robo4j-hw-rpi/src/main/java/com/robo4j/hw/rpi/i2c/adafruitbackpack/LedBackpackFactory.java @@ -29,17 +29,12 @@ */ public final class LedBackpackFactory { - public static AbstractBackpack createDevice(I2cBus bus, int address, LedBackpackType type, int brightness) - throws IOException { - switch (type) { - case BI_COLOR_BAR_24: - return new BiColor24BarDevice(bus, address, brightness); - case BI_COLOR_MATRIX_8x8: - return new BiColor8x8MatrixDevice(bus, address, brightness); - case ALPHANUMERIC: - return new AlphanumericDevice(bus, address, brightness); - default: - throw new IllegalStateException("not available backpack: " + type); - } - } + public static AbstractBackpack createDevice(I2cBus bus, int address, LedBackpackType type, int brightness) + throws IOException { + return switch (type) { + case BI_COLOR_BAR_24 -> new BiColor24BarDevice(bus, address, brightness); + case BI_COLOR_MATRIX_8x8 -> new BiColor8x8MatrixDevice(bus, address, brightness); + case ALPHANUMERIC -> new AlphanumericDevice(bus, address, brightness); + }; + } } diff --git a/robo4j-hw-rpi/src/main/java/com/robo4j/hw/rpi/i2c/adafruitbackpack/MatrixRotation.java b/robo4j-hw-rpi/src/main/java/com/robo4j/hw/rpi/i2c/adafruitbackpack/MatrixRotation.java index db95041d..7af38e92 100644 --- a/robo4j-hw-rpi/src/main/java/com/robo4j/hw/rpi/i2c/adafruitbackpack/MatrixRotation.java +++ b/robo4j-hw-rpi/src/main/java/com/robo4j/hw/rpi/i2c/adafruitbackpack/MatrixRotation.java @@ -24,7 +24,7 @@ * @author Miroslav Wengner (@miragemiko) */ public enum MatrixRotation { - //@formatter:off + //@formatter:off NONE (0, "none"), DEFAULT_X_Y (1,"default setup to pins"), RIGHT_90 (2, "90d to default, right"), @@ -36,24 +36,24 @@ public enum MatrixRotation { LEFT_270 (8, "270d to default, left"); //@formatter:on - private int id; - private final String note; + private final int id; + private final String note; - MatrixRotation(int id, String note) { - this.id = id; - this.note = note; - } + MatrixRotation(int id, String note) { + this.id = id; + this.note = note; + } - public String getNote() { - return note; - } + public String getNote() { + return note; + } - public static MatrixRotation getById(int code) { - for (MatrixRotation r : values()) { - if (code == r.id) { - return r; - } - } - return NONE; - } + public static MatrixRotation getById(int code) { + for (MatrixRotation r : values()) { + if (code == r.id) { + return r; + } + } + return NONE; + } } diff --git a/robo4j-hw-rpi/src/main/java/com/robo4j/hw/rpi/i2c/adafruitlcd/Button.java b/robo4j-hw-rpi/src/main/java/com/robo4j/hw/rpi/i2c/adafruitlcd/Button.java index b138d077..d9aa3b5b 100644 --- a/robo4j-hw-rpi/src/main/java/com/robo4j/hw/rpi/i2c/adafruitlcd/Button.java +++ b/robo4j-hw-rpi/src/main/java/com/robo4j/hw/rpi/i2c/adafruitlcd/Button.java @@ -16,20 +16,20 @@ */ package com.robo4j.hw.rpi.i2c.adafruitlcd; +import com.robo4j.hw.rpi.i2c.adafruitlcd.impl.AdafruitLcdImpl; + import java.util.HashSet; import java.util.Set; -import com.robo4j.hw.rpi.i2c.adafruitlcd.impl.AdafruitLcdImpl; - /** * Enumeration of the Buttons on the LCD shield. - * + * * @author Marcus Hirt (@hirt) * @author Miro Wengner (@miragemiko) */ public enum Button { - // @formatter:off + // @formatter:off SELECT (0), RIGHT (1), DOWN (2), @@ -37,52 +37,48 @@ public enum Button { LEFT (4); // @formatter:on - // Port expander input pin definition - private final int pin; + // Port expander input pin definition + private final int pin; - Button(int pin) { - this.pin = pin; - } + Button(int pin) { + this.pin = pin; + } - /** - * The pin corresponding to the button. - * - * @return the pin of the button. - */ - public int getPin() { - return pin; - } + /** + * The pin corresponding to the button. + * + * @return the pin of the button. + */ + public int getPin() { + return pin; + } - /** - * Checks if a button is pressed, given an input mask. - * - * @param mask - * the input mask. - * @return true if the button is pressed, false otherwise. - * - * @see AdafruitLcdImpl#buttonsPressedBitmask() - */ - public boolean isButtonPressed(int mask) { - return ((mask >> getPin()) & 1) > 0; - } + /** + * Checks if a button is pressed, given an input mask. + * + * @param mask the input mask. + * @return true if the button is pressed, false otherwise. + * @see AdafruitLcdImpl#buttonsPressedBitmask() + */ + public boolean isButtonPressed(int mask) { + return ((mask >> getPin()) & 1) > 0; + } - /** - * Returns a set of the buttons that are pressed, according to the input - * mask. - * - * @param mask - * the input mask. - * @return a set of the buttons pressed. - * - * @see AdafruitLcdImpl#buttonsPressedBitmask() - */ - public static Set