diff --git a/robo4j-core/src/main/java/com/robo4j/logging/SimpleLoggingUtil.java b/robo4j-core/src/main/java/com/robo4j/logging/SimpleLoggingUtil.java deleted file mode 100644 index e54abee4..00000000 --- a/robo4j-core/src/main/java/com/robo4j/logging/SimpleLoggingUtil.java +++ /dev/null @@ -1,67 +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.logging; - -import com.robo4j.util.StringConstants; - -import java.util.logging.Level; -import java.util.logging.Logger; -import java.util.stream.Stream; - -/** - * Simple toolkit for logging. - * - * @author Marcus Hirt (@hirt) - * @author Miroslav Wengner (@miragemiko) - */ -@Deprecated -public final class SimpleLoggingUtil { - // TODO(Marcus/Sep 6, 2017): Decide if we want to kill this... - public static void print(Class clazz, String message) { - Logger.getLogger(clazz.getName()).log(Level.INFO, message); - } - - public static void debug(Class clazz, String message) { - Logger.getLogger(clazz.getName()).log(Level.FINE, message); - } - - public static void debug(Class clazz, String... message) { - debug(clazz, clazz.getSimpleName() + " : " - + Stream.of(message).reduce(StringConstants.EMPTY, (l, r) -> l.concat(StringConstants.SPACE).concat(r))); - } - - public static void debug(Class clazz, String message, Throwable e) { - Logger.getLogger(clazz.getName()).log(Level.INFO, message, e); - } - - public static void info(Class clazz, String message) { - Logger.getLogger(clazz.getName()).log(Level.INFO, message); - } - - public static void info(Class clazz, String message, Throwable e) { - Logger.getLogger(clazz.getName()).log(Level.INFO, message, e); - } - - public static void error(Class clazz, String message) { - Logger.getLogger(clazz.getName()).log(Level.SEVERE, message); - } - - public static void error(Class clazz, String string, Throwable e) { - Logger.getLogger(clazz.getName()).log(Level.SEVERE, string, e); - } - -} diff --git a/robo4j-core/src/main/java/module-info.java b/robo4j-core/src/main/java/module-info.java index 58faca17..a3274969 100644 --- a/robo4j-core/src/main/java/module-info.java +++ b/robo4j-core/src/main/java/module-info.java @@ -26,7 +26,6 @@ exports com.robo4j; exports com.robo4j.util; exports com.robo4j.configuration; - exports com.robo4j.logging; exports com.robo4j.reflect; exports com.robo4j.scheduler; exports com.robo4j.net; diff --git a/robo4j-units-rpi-http/src/main/java/com/robo4j/units/rpi/http/camera/ImageDecoratorUnit.java b/robo4j-units-rpi-http/src/main/java/com/robo4j/units/rpi/http/camera/ImageDecoratorUnit.java index 5fb24d66..06651478 100644 --- a/robo4j-units-rpi-http/src/main/java/com/robo4j/units/rpi/http/camera/ImageDecoratorUnit.java +++ b/robo4j-units-rpi-http/src/main/java/com/robo4j/units/rpi/http/camera/ImageDecoratorUnit.java @@ -21,12 +21,13 @@ import com.robo4j.RoboContext; import com.robo4j.RoboUnit; import com.robo4j.configuration.Configuration; -import com.robo4j.logging.SimpleLoggingUtil; import com.robo4j.socket.http.codec.CameraMessage; import com.robo4j.socket.http.enums.SystemPath; import com.robo4j.socket.http.units.ClientMessageWrapper; import com.robo4j.socket.http.util.HttpPathUtils; import com.robo4j.socket.http.util.JsonUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.util.Objects; import java.util.concurrent.atomic.AtomicInteger; @@ -41,6 +42,7 @@ */ @CriticalSectionTrait public class ImageDecoratorUnit extends RoboUnit { + private static final Logger LOGGER = LoggerFactory.getLogger(ImageDecoratorUnit.class); private static final String PROPERTY_TARGET = "target"; private final AtomicInteger imageNumber = new AtomicInteger(0); @@ -69,7 +71,7 @@ public void onMessage(CameraImageDTO image) { String.valueOf(imageNumber.incrementAndGet()), imageBase64); final ClientMessageWrapper resultMessage = new ClientMessageWrapper( HttpPathUtils.toPath(SystemPath.UNITS.getPath(), httpTarget), CameraMessage.class, cameraMessage); - SimpleLoggingUtil.debug(ImageDecoratorUnit.class, "image target: " + target + " resultMessage: " + resultMessage.getPath()); + LOGGER.info("image target:{},resultMessage:{}", target, resultMessage.getPath()); getContext().getReference(target).sendMessage(resultMessage); } diff --git a/robo4j-units-rpi-http/src/main/java/module-info.java b/robo4j-units-rpi-http/src/main/java/module-info.java index a3c28603..026860b0 100644 --- a/robo4j-units-rpi-http/src/main/java/module-info.java +++ b/robo4j-units-rpi-http/src/main/java/module-info.java @@ -1,5 +1,6 @@ module robo4j.units.rpi.http { requires robo4j.http; + requires org.slf4j; exports com.robo4j.units.rpi.http.camera to robo4j.core; } \ No newline at end of file diff --git a/robo4j-units-rpi-http/src/test/java/com/robo4j/units/rpi/http/camera/CameraImageProducerDesTestUnit.java b/robo4j-units-rpi-http/src/test/java/com/robo4j/units/rpi/http/camera/CameraImageProducerDesTestUnit.java index 8e214127..a5e53004 100644 --- a/robo4j-units-rpi-http/src/test/java/com/robo4j/units/rpi/http/camera/CameraImageProducerDesTestUnit.java +++ b/robo4j-units-rpi-http/src/test/java/com/robo4j/units/rpi/http/camera/CameraImageProducerDesTestUnit.java @@ -23,13 +23,14 @@ import com.robo4j.RoboContext; import com.robo4j.RoboUnit; import com.robo4j.configuration.Configuration; -import com.robo4j.logging.SimpleLoggingUtil; import com.robo4j.socket.http.codec.CameraMessage; import com.robo4j.socket.http.enums.SystemPath; import com.robo4j.socket.http.units.ClientMessageWrapper; import com.robo4j.socket.http.util.HttpPathUtils; import com.robo4j.socket.http.util.JsonUtil; import com.robo4j.util.StreamUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.util.EnumSet; import java.util.concurrent.CountDownLatch; @@ -43,85 +44,85 @@ public class CameraImageProducerDesTestUnit extends RoboUnit { - public static final String ATTRIBUTE_NUMBER_OF_SENT_IMAGES_NAME = "numberOfSentImages"; - public static final String ATTR_TOTAL_IMAGES = "numberOfImages"; - public static final String ATTR_GENERATED_IMAGES_LATCH = "generatedImagesLatch"; - public static final DefaultAttributeDescriptor DESCRIPTOR_GENERATED_IMAGES_LATCH = DefaultAttributeDescriptor - .create(CountDownLatch.class, ATTR_GENERATED_IMAGES_LATCH); - public static final AttributeDescriptor DESCRIPTOR_TOTAL_IMAGES = new DefaultAttributeDescriptor<>(Integer.class, - CameraImageProducerDesTestUnit.ATTR_TOTAL_IMAGES); - protected static final String IMAGE_ENCODING = "jpg"; + public static final String ATTRIBUTE_NUMBER_OF_SENT_IMAGES_NAME = "numberOfSentImages"; + public static final String ATTR_TOTAL_IMAGES = "numberOfImages"; + public static final String ATTR_GENERATED_IMAGES_LATCH = "generatedImagesLatch"; + public static final DefaultAttributeDescriptor DESCRIPTOR_GENERATED_IMAGES_LATCH = DefaultAttributeDescriptor + .create(CountDownLatch.class, ATTR_GENERATED_IMAGES_LATCH); + public static final AttributeDescriptor DESCRIPTOR_TOTAL_IMAGES = new DefaultAttributeDescriptor<>(Integer.class, + CameraImageProducerDesTestUnit.ATTR_TOTAL_IMAGES); + protected static final String IMAGE_ENCODING = "jpg"; + private static final Logger LOGGER = LoggerFactory.getLogger(CameraImageProducerDesTestUnit.class); + protected final AtomicBoolean progress = new AtomicBoolean(false); + protected String target; + protected String httpTarget; + protected String fileName; + private volatile AtomicInteger counter = new AtomicInteger(0); + protected volatile CountDownLatch generatedImagesLatch; + private Integer numberOfImages; - protected final AtomicBoolean progress = new AtomicBoolean(false); - protected String target; - protected String httpTarget; - protected String fileName; - private volatile AtomicInteger counter = new AtomicInteger(0); - protected volatile CountDownLatch generatedImagesLatch; - private Integer numberOfImages; + public CameraImageProducerDesTestUnit(RoboContext context, String id) { + super(Boolean.class, context, id); + } - public CameraImageProducerDesTestUnit(RoboContext context, String id) { - super(Boolean.class, context, id); - } + @Override + protected void onInitialization(Configuration configuration) throws ConfigurationException { + target = configuration.getString("target", null); + httpTarget = configuration.getString("httpTarget", null); + fileName = configuration.getString("fileName", null); + numberOfImages = configuration.getInteger(ATTR_TOTAL_IMAGES, null); + generatedImagesLatch = new CountDownLatch(numberOfImages); + } - @Override - protected void onInitialization(Configuration configuration) throws ConfigurationException { - target = configuration.getString("target", null); - httpTarget = configuration.getString("httpTarget", null); - fileName = configuration.getString("fileName", null); - numberOfImages = configuration.getInteger(ATTR_TOTAL_IMAGES, null); - generatedImagesLatch = new CountDownLatch(numberOfImages); - } + @Override + public void onMessage(Boolean message) { + if (message) { + LOGGER.info("message:{}", message); + createImage(counter.get()); + } + } - @Override - public void onMessage(Boolean message) { - if (message) { - SimpleLoggingUtil.debug(getClass(), "message: " + message); - createImage(counter.get()); - } - } + @Override + public void start() { + EnumSet acceptedStates = EnumSet.of(LifecycleState.STARTING, LifecycleState.STARTED); + getContext().getScheduler().execute(() -> { + while (acceptedStates.contains(getState())) { + if (progress.compareAndSet(false, true) && counter.getAndIncrement() < numberOfImages) { + createImage(counter.get()); + } + } + }); + } - @Override - public void start() { - EnumSet acceptedStates = EnumSet.of(LifecycleState.STARTING, LifecycleState.STARTED); - getContext().getScheduler().execute(() -> { - while (acceptedStates.contains(getState())) { - if (progress.compareAndSet(false, true) && counter.getAndIncrement() < numberOfImages) { - createImage(counter.get()); - } - } - }); - } + @SuppressWarnings("unchecked") + @Override + protected synchronized R onGetAttribute(AttributeDescriptor descriptor) { + if (descriptor.getAttributeType() == Integer.class) { + if (descriptor.getAttributeName().equals(ATTR_TOTAL_IMAGES)) { + return (R) numberOfImages; + } else if (descriptor.getAttributeName().equals(ATTRIBUTE_NUMBER_OF_SENT_IMAGES_NAME)) { + return (R) Integer.valueOf(counter.get()); + } + } + if (descriptor.getAttributeName().equals(ATTR_GENERATED_IMAGES_LATCH) && descriptor.getAttributeType() == CountDownLatch.class) { + return (R) generatedImagesLatch; + } + return super.onGetAttribute(descriptor); + } - @SuppressWarnings("unchecked") - @Override - protected synchronized R onGetAttribute(AttributeDescriptor descriptor) { - if (descriptor.getAttributeType() == Integer.class) { - if (descriptor.getAttributeName().equals(ATTR_TOTAL_IMAGES)) { - return (R) numberOfImages; - } else if (descriptor.getAttributeName().equals(ATTRIBUTE_NUMBER_OF_SENT_IMAGES_NAME)) { - return (R) Integer.valueOf(counter.get()); - } - } - if(descriptor.getAttributeName().equals(ATTR_GENERATED_IMAGES_LATCH) && descriptor.getAttributeType() == CountDownLatch.class ){ - return (R) generatedImagesLatch; - } - return super.onGetAttribute(descriptor); - } + protected void countDonwSentImage() { - protected void countDonwSentImage(){ + } - } - - protected void createImage(int imageNumber) { - final byte[] image = StreamUtils - .inputStreamToByteArray(Thread.currentThread().getContextClassLoader().getResourceAsStream(fileName)); - final CameraMessage cameraMessage = new CameraMessage(IMAGE_ENCODING, String.valueOf(imageNumber), - JsonUtil.toBase64String(image)); - final ClientMessageWrapper resultMessage = new ClientMessageWrapper( - HttpPathUtils.toPath(SystemPath.UNITS.getPath(), httpTarget), CameraMessage.class, cameraMessage); - getContext().getReference(target).sendMessage(resultMessage); - generatedImagesLatch.countDown(); - progress.set(false); - } + protected void createImage(int imageNumber) { + final byte[] image = StreamUtils + .inputStreamToByteArray(Thread.currentThread().getContextClassLoader().getResourceAsStream(fileName)); + final CameraMessage cameraMessage = new CameraMessage(IMAGE_ENCODING, String.valueOf(imageNumber), + JsonUtil.toBase64String(image)); + final ClientMessageWrapper resultMessage = new ClientMessageWrapper( + HttpPathUtils.toPath(SystemPath.UNITS.getPath(), httpTarget), CameraMessage.class, cameraMessage); + getContext().getReference(target).sendMessage(resultMessage); + generatedImagesLatch.countDown(); + progress.set(false); + } } diff --git a/robo4j-units-rpi/src/main/java/com/robo4j/units/rpi/gps/MtkGPSUnit.java b/robo4j-units-rpi/src/main/java/com/robo4j/units/rpi/gps/MtkGPSUnit.java index 3eaa7a93..2a31b4d6 100644 --- a/robo4j-units-rpi/src/main/java/com/robo4j/units/rpi/gps/MtkGPSUnit.java +++ b/robo4j-units-rpi/src/main/java/com/robo4j/units/rpi/gps/MtkGPSUnit.java @@ -16,16 +16,6 @@ */ package com.robo4j.units.rpi.gps; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.List; -import java.util.concurrent.Future; -import java.util.concurrent.ScheduledFuture; -import java.util.concurrent.TimeUnit; - import com.robo4j.AttributeDescriptor; import com.robo4j.ConfigurationException; import com.robo4j.DefaultAttributeDescriptor; @@ -38,163 +28,175 @@ import com.robo4j.hw.rpi.gps.PositionEvent; import com.robo4j.hw.rpi.gps.VelocityEvent; import com.robo4j.hw.rpi.serial.gps.MTK3339GPS; -import com.robo4j.logging.SimpleLoggingUtil; import com.robo4j.math.geometry.Tuple3f; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.Future; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; /** * Unit for getting GPS data. - * + * * @author Marcus Hirt (@hirt) * @author Miroslav Wengner (@miragemiko) */ public class MtkGPSUnit extends RoboUnit { - /** - * This key configures the approximate update interval, or how often to - * schedule reads from the serial port. - */ - public static final String PROPERTY_KEY_READ_INTERVAL = "readInterval"; - - /** - * This key configures the scheduler to use for scheduling reads. Either - * PLATFORM or INTERNAL. Use INTERNAL if the reads take too long and start - * disrupting the platform scheduler too much. - */ - public static final String PROPERTY_KEY_SCHEDULER = "scheduler"; - - /** - * Value for the scheduler key for using the platform scheduler. - */ - public static final String PROPERTY_VALUE_PLATFORM_SCHEDULER = "platform"; - - /** - * Value for the scheduler key for using the internal scheduler. - */ - public static final String PROPERTY_VALUE_INTERNAL_SCHEDULER = "internal"; - - /** - * This is the default value for the read interval. - */ - public static final int DEFAULT_READ_INTERVAL = MTK3339GPS.DEFAULT_READ_INTERVAL; - - /** - * This is the default value for the serial port. - */ - public static final String DEFAULT_SERIAL_PORT = MTK3339GPS.DEFAULT_GPS_PORT; - - /** - * This attribute will provide the state of the read interval. - */ - public static final String ATTRIBUTE_NAME_READ_INTERVAL = "readInterval"; - - public static final Collection> KNOWN_ATTRIBUTES = Collections - .unmodifiableCollection(Arrays.asList(DefaultAttributeDescriptor.create(Tuple3f.class, ATTRIBUTE_NAME_READ_INTERVAL))); - - private MTK3339GPS mtk3339gps; - private String serialPort; - private int readInterval = DEFAULT_READ_INTERVAL; - private List listeners = new ArrayList<>(); - - // The future, if scheduled with the platform scheduler - private volatile ScheduledFuture scheduledFuture; - - private static class GPSEventListener implements GPSListener { - private RoboReference target; - - GPSEventListener(RoboReference target) { - this.target = target; - } - - @Override - public void onPosition(PositionEvent event) { - target.sendMessage(event); - } - - @Override - public void onVelocity(VelocityEvent event) { - target.sendMessage(event); - } - } - - public MtkGPSUnit(RoboContext context, String id) { - super(GPSRequest.class, context, id); - } - - @Override - protected void onInitialization(Configuration configuration) throws ConfigurationException { - serialPort = configuration.getString("serialPort", DEFAULT_SERIAL_PORT); - readInterval = configuration.getInteger("readInterval", DEFAULT_READ_INTERVAL); - String scheduler = configuration.getString("scheduler", PROPERTY_VALUE_PLATFORM_SCHEDULER); - boolean usePlatformScheduler = PROPERTY_VALUE_PLATFORM_SCHEDULER.equals(scheduler); - - try { - mtk3339gps = new MTK3339GPS(serialPort, readInterval); - } catch (IOException e) { - throw new ConfigurationException("Could not instantiate GPS!", e); - } - if (usePlatformScheduler) { - scheduledFuture = getContext().getScheduler().scheduleAtFixedRate(() -> mtk3339gps.update(), 10, readInterval, - TimeUnit.MILLISECONDS); - } else { - mtk3339gps.start(); - } - } - - @Override - public Future getAttribute(AttributeDescriptor attribute) { - return super.getAttribute(attribute); - } - - @Override - public void onMessage(GPSRequest message) { - super.onMessage(message); - RoboReference targetReference = message.getTarget(); - switch (message.getOperation()) { - case REGISTER: - register(targetReference); - break; - case UNREGISTER: - unregister(targetReference); - break; - default: - SimpleLoggingUtil.error(getClass(), "Unknown operation: " + message.getOperation()); - break; - } - } - - @SuppressWarnings("unchecked") - @Override - protected R onGetAttribute(AttributeDescriptor descriptor) { - if (descriptor.getAttributeType() == Integer.class && descriptor.getAttributeName().equals(ATTRIBUTE_NAME_READ_INTERVAL)) { - return (R) Integer.valueOf(readInterval); - } - return super.onGetAttribute(descriptor); - } - - @Override - public void shutdown() { - if (scheduledFuture != null) { - scheduledFuture.cancel(false); - } - mtk3339gps.shutdown(); - super.shutdown(); - } - - // Private Methods - private synchronized void unregister(RoboReference targetReference) { - List copy = new ArrayList<>(listeners); - for (GPSEventListener listener : copy) { - if (targetReference.equals(listener.target)) { - listeners.remove(listener); - mtk3339gps.removeListener(listener); - // I guess you could theoretically have several registered to - // the same target, so let's keep checking... - } - } - } - - private synchronized void register(RoboReference targetReference) { - GPSEventListener listener = new GPSEventListener(targetReference); - listeners.add(listener); - mtk3339gps.addListener(listener); - } + /** + * This key configures the approximate update interval, or how often to + * schedule reads from the serial port. + */ + public static final String PROPERTY_KEY_READ_INTERVAL = "readInterval"; + + /** + * This key configures the scheduler to use for scheduling reads. Either + * PLATFORM or INTERNAL. Use INTERNAL if the reads take too long and start + * disrupting the platform scheduler too much. + */ + public static final String PROPERTY_KEY_SCHEDULER = "scheduler"; + + /** + * Value for the scheduler key for using the platform scheduler. + */ + public static final String PROPERTY_VALUE_PLATFORM_SCHEDULER = "platform"; + + /** + * Value for the scheduler key for using the internal scheduler. + */ + public static final String PROPERTY_VALUE_INTERNAL_SCHEDULER = "internal"; + + /** + * This is the default value for the read interval. + */ + public static final int DEFAULT_READ_INTERVAL = MTK3339GPS.DEFAULT_READ_INTERVAL; + + /** + * This is the default value for the serial port. + */ + public static final String DEFAULT_SERIAL_PORT = MTK3339GPS.DEFAULT_GPS_PORT; + + /** + * This attribute will provide the state of the read interval. + */ + public static final String ATTRIBUTE_NAME_READ_INTERVAL = "readInterval"; + + public static final Collection> KNOWN_ATTRIBUTES = Collections + .unmodifiableCollection(Arrays.asList(DefaultAttributeDescriptor.create(Tuple3f.class, ATTRIBUTE_NAME_READ_INTERVAL))); + private static final Logger LOGGER = LoggerFactory.getLogger(MtkGPSUnit.class); + + private MTK3339GPS mtk3339gps; + private String serialPort; + private int readInterval = DEFAULT_READ_INTERVAL; + private List listeners = new ArrayList<>(); + + // The future, if scheduled with the platform scheduler + private volatile ScheduledFuture scheduledFuture; + + private static class GPSEventListener implements GPSListener { + private RoboReference target; + + GPSEventListener(RoboReference target) { + this.target = target; + } + + @Override + public void onPosition(PositionEvent event) { + target.sendMessage(event); + } + + @Override + public void onVelocity(VelocityEvent event) { + target.sendMessage(event); + } + } + + public MtkGPSUnit(RoboContext context, String id) { + super(GPSRequest.class, context, id); + } + + @Override + protected void onInitialization(Configuration configuration) throws ConfigurationException { + serialPort = configuration.getString("serialPort", DEFAULT_SERIAL_PORT); + readInterval = configuration.getInteger("readInterval", DEFAULT_READ_INTERVAL); + String scheduler = configuration.getString("scheduler", PROPERTY_VALUE_PLATFORM_SCHEDULER); + boolean usePlatformScheduler = PROPERTY_VALUE_PLATFORM_SCHEDULER.equals(scheduler); + + try { + mtk3339gps = new MTK3339GPS(serialPort, readInterval); + } catch (IOException e) { + throw new ConfigurationException("Could not instantiate GPS!", e); + } + if (usePlatformScheduler) { + scheduledFuture = getContext().getScheduler().scheduleAtFixedRate(() -> mtk3339gps.update(), 10, readInterval, + TimeUnit.MILLISECONDS); + } else { + mtk3339gps.start(); + } + } + + @Override + public Future getAttribute(AttributeDescriptor attribute) { + return super.getAttribute(attribute); + } + + @Override + public void onMessage(GPSRequest message) { + super.onMessage(message); + RoboReference targetReference = message.getTarget(); + switch (message.getOperation()) { + case REGISTER: + register(targetReference); + break; + case UNREGISTER: + unregister(targetReference); + break; + default: + LOGGER.warn("Unknown operation:{}", message.getOperation()); + break; + } + } + + @SuppressWarnings("unchecked") + @Override + protected R onGetAttribute(AttributeDescriptor descriptor) { + if (descriptor.getAttributeType() == Integer.class && descriptor.getAttributeName().equals(ATTRIBUTE_NAME_READ_INTERVAL)) { + return (R) Integer.valueOf(readInterval); + } + return super.onGetAttribute(descriptor); + } + + @Override + public void shutdown() { + if (scheduledFuture != null) { + scheduledFuture.cancel(false); + } + mtk3339gps.shutdown(); + super.shutdown(); + } + + // Private Methods + private synchronized void unregister(RoboReference targetReference) { + List copy = new ArrayList<>(listeners); + for (GPSEventListener listener : copy) { + if (targetReference.equals(listener.target)) { + listeners.remove(listener); + mtk3339gps.removeListener(listener); + // I guess you could theoretically have several registered to + // the same target, so let's keep checking... + } + } + } + + private synchronized void register(RoboReference targetReference) { + GPSEventListener listener = new GPSEventListener(targetReference); + listeners.add(listener); + mtk3339gps.addListener(listener); + } } diff --git a/robo4j-units-rpi/src/main/java/com/robo4j/units/rpi/gps/TitanGPSUnit.java b/robo4j-units-rpi/src/main/java/com/robo4j/units/rpi/gps/TitanGPSUnit.java index e527f129..1ef2140f 100644 --- a/robo4j-units-rpi/src/main/java/com/robo4j/units/rpi/gps/TitanGPSUnit.java +++ b/robo4j-units-rpi/src/main/java/com/robo4j/units/rpi/gps/TitanGPSUnit.java @@ -16,12 +16,6 @@ */ package com.robo4j.units.rpi.gps; -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.Future; -import java.util.concurrent.ScheduledFuture; - import com.robo4j.AttributeDescriptor; import com.robo4j.ConfigurationException; import com.robo4j.RoboContext; @@ -33,106 +27,114 @@ import com.robo4j.hw.rpi.gps.PositionEvent; import com.robo4j.hw.rpi.gps.VelocityEvent; import com.robo4j.hw.rpi.i2c.gps.TitanX1GPS; -import com.robo4j.logging.SimpleLoggingUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.Future; +import java.util.concurrent.ScheduledFuture; /** * Unit for getting GPS data. - * + * * @author Marcus Hirt (@hirt) * @author Miroslav Wengner (@miragemiko) */ public class TitanGPSUnit extends RoboUnit { - private final List listeners = new ArrayList<>(); - private TitanX1GPS titangps; - - // The future, if scheduled with the platform scheduler - private volatile ScheduledFuture scheduledFuture; - - private static class GPSEventListener implements GPSListener { - private final RoboReference target; - - private GPSEventListener(RoboReference target) { - this.target = target; - } - - @Override - public void onPosition(PositionEvent event) { - target.sendMessage(event); - } - - @Override - public void onVelocity(VelocityEvent event) { - target.sendMessage(event); - } - } - - public TitanGPSUnit(RoboContext context, String id) { - super(GPSRequest.class, context, id); - } - - @Override - protected void onInitialization(Configuration configuration) throws ConfigurationException { - // TODO: Configuration for bus, address etc - try { - titangps = new TitanX1GPS(); - } catch (IOException e) { - throw new ConfigurationException("Could not instantiate GPS!", e); - } - titangps.start(); - } - - @Override - public Future getAttribute(AttributeDescriptor attribute) { - return super.getAttribute(attribute); - } - - @Override - public void onMessage(GPSRequest message) { - super.onMessage(message); - RoboReference targetReference = message.getTarget(); - switch (message.getOperation()) { - case REGISTER: - register(targetReference); - break; - case UNREGISTER: - unregister(targetReference); - break; - default: - SimpleLoggingUtil.error(getClass(), "Unknown operation: " + message.getOperation()); - break; - } - } - - @Override - protected R onGetAttribute(AttributeDescriptor descriptor) { - return super.onGetAttribute(descriptor); - } - - @Override - public void shutdown() { - if (scheduledFuture != null) { - scheduledFuture.cancel(false); - } - titangps.shutdown(); - super.shutdown(); - } - - // Private Methods - private synchronized void unregister(RoboReference targetReference) { - List copy = new ArrayList<>(listeners); - for (GPSEventListener listener : copy) { - if (targetReference.equals(listener.target)) { - listeners.remove(listener); - titangps.removeListener(listener); - // I guess you could theoretically have several registered to - // the same target, so let's keep checking... - } - } - } - - private synchronized void register(RoboReference targetReference) { - var listener = new GPSEventListener(targetReference); - listeners.add(listener); - titangps.addListener(listener); - } + private static final Logger LOGGER = LoggerFactory.getLogger(TitanGPSUnit.class); + private final List listeners = new ArrayList<>(); + private TitanX1GPS titangps; + + // The future, if scheduled with the platform scheduler + private volatile ScheduledFuture scheduledFuture; + + private static class GPSEventListener implements GPSListener { + private final RoboReference target; + + private GPSEventListener(RoboReference target) { + this.target = target; + } + + @Override + public void onPosition(PositionEvent event) { + target.sendMessage(event); + } + + @Override + public void onVelocity(VelocityEvent event) { + target.sendMessage(event); + } + } + + public TitanGPSUnit(RoboContext context, String id) { + super(GPSRequest.class, context, id); + } + + @Override + protected void onInitialization(Configuration configuration) throws ConfigurationException { + // TODO: Configuration for bus, address etc + try { + titangps = new TitanX1GPS(); + } catch (IOException e) { + throw new ConfigurationException("Could not instantiate GPS!", e); + } + titangps.start(); + } + + @Override + public Future getAttribute(AttributeDescriptor attribute) { + return super.getAttribute(attribute); + } + + @Override + public void onMessage(GPSRequest message) { + super.onMessage(message); + RoboReference targetReference = message.getTarget(); + switch (message.getOperation()) { + case REGISTER: + register(targetReference); + break; + case UNREGISTER: + unregister(targetReference); + break; + default: + LOGGER.error("Unknown operation:{}", message.getOperation()); + break; + } + } + + @Override + protected R onGetAttribute(AttributeDescriptor descriptor) { + return super.onGetAttribute(descriptor); + } + + @Override + public void shutdown() { + if (scheduledFuture != null) { + scheduledFuture.cancel(false); + } + titangps.shutdown(); + super.shutdown(); + } + + // Private Methods + private synchronized void unregister(RoboReference targetReference) { + List copy = new ArrayList<>(listeners); + for (GPSEventListener listener : copy) { + if (targetReference.equals(listener.target)) { + listeners.remove(listener); + titangps.removeListener(listener); + // I guess you could theoretically have several registered to + // the same target, so let's keep checking... + } + } + } + + private synchronized void register(RoboReference targetReference) { + var listener = new GPSEventListener(targetReference); + listeners.add(listener); + titangps.addListener(listener); + } } diff --git a/robo4j-units-rpi/src/main/java/com/robo4j/units/rpi/gyro/GyroL3GD20Unit.java b/robo4j-units-rpi/src/main/java/com/robo4j/units/rpi/gyro/GyroL3GD20Unit.java index 35f4923e..352b767f 100644 --- a/robo4j-units-rpi/src/main/java/com/robo4j/units/rpi/gyro/GyroL3GD20Unit.java +++ b/robo4j-units-rpi/src/main/java/com/robo4j/units/rpi/gyro/GyroL3GD20Unit.java @@ -16,15 +16,6 @@ */ package com.robo4j.units.rpi.gyro; -import java.io.IOException; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; -import java.util.concurrent.ScheduledFuture; -import java.util.concurrent.TimeUnit; - import com.robo4j.AttributeDescriptor; import com.robo4j.ConfigurationException; import com.robo4j.DefaultAttributeDescriptor; @@ -35,203 +26,211 @@ import com.robo4j.hw.rpi.i2c.gyro.CalibratedGyro; import com.robo4j.hw.rpi.i2c.gyro.GyroL3GD20Device; import com.robo4j.hw.rpi.i2c.gyro.GyroL3GD20Device.Sensitivity; -import com.robo4j.logging.SimpleLoggingUtil; import com.robo4j.math.geometry.Tuple3f; import com.robo4j.units.rpi.I2CRoboUnit; import com.robo4j.units.rpi.gyro.GyroRequest.GyroAction; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; /** * Gyro unit based on the {@link GyroL3GD20Device}. Note that there can only be * ONE active notification threshold per target. If a new one is registered * before it has triggered, the new one will replace the old one. - * + * * @author Marcus Hirt (@hirt) * @author Miroslav Wengner (@miragemiko) */ @WorkTrait public class GyroL3GD20Unit extends I2CRoboUnit { - /** - * This key configures the sensitivity of the gyro. Use the name of the - * Sensitivity enum. Default is DPS_245. - * - * @see Sensitivity - */ - public static final String PROPERTY_KEY_SENSITIVITY = "sensitivity"; - - /** - * This key configures the high pass filter. Set to true to enable. Default - * is true. - * - * @see GyroL3GD20Device - */ - public static final String PROPERTY_KEY_HIGH_PASS_FILTER = "enableHighPass"; - - /** - * This key configures how often to read the gyro in ms. Default is 10. - */ - public static final String PROPERTY_KEY_PERIOD = "period"; - - /** - * This attribute will provide the state of the gyro as a {@link Tuple3f}. - */ - public static final String ATTRIBUTE_NAME_STATE = "state"; - - public static final Collection> KNOWN_ATTRIBUTES = Collections - .unmodifiableCollection(Arrays.asList(DefaultAttributeDescriptor.create(Tuple3f.class, ATTRIBUTE_NAME_STATE))); - - private final Map, GyroNotificationEntry> activeThresholds = new HashMap<>(); - - private final GyroScanner scanner = new GyroScanner(); - - // TODO review field purpose - private Sensitivity sensitivity; - private boolean highPassFilter; - private int period; - private CalibratedGyro gyro; - private volatile ScheduledFuture readings; - - private class GyroScanner implements Runnable { - private long lastReadingTime = System.currentTimeMillis(); - private Tuple3f lastReading = new Tuple3f(0f, 0f, 0f); - - @Override - public void run() { - Tuple3f data = read(); - long newTime = System.currentTimeMillis(); - - // Trapezoid - Tuple3f tmp = new Tuple3f(data); - long deltaTime = newTime - lastReadingTime; - data.add(lastReading); - data.multiplyScalar(deltaTime / 2000.0f); - - lastReading.set(tmp); - addToDeltas(data); - lastReadingTime = newTime; - } - - private void addToDeltas(Tuple3f data) { - synchronized (GyroL3GD20Unit.this) { - for (GyroNotificationEntry notificationEntry : activeThresholds.values()) { - notificationEntry.addDelta(data); - } - } - } - - private void reset() { - lastReadingTime = System.currentTimeMillis(); - lastReading = read(); - } - - private Tuple3f read() { - try { - return gyro.read(); - } catch (IOException e) { - SimpleLoggingUtil.error(getClass(), "Could not read gyro, aborting.", e); - return null; - } - } - } - - /** - * Constructor. - * - * @param context - * the robo context. - * @param id - * the robo unit id. - */ - public GyroL3GD20Unit(RoboContext context, String id) { - super(GyroRequest.class, context, id); - } - - @Override - protected void onInitialization(Configuration configuration) throws ConfigurationException { - super.onInitialization(configuration); - sensitivity = Sensitivity.valueOf(configuration.getString(PROPERTY_KEY_SENSITIVITY, "DPS_245")); - period = configuration.getInteger(PROPERTY_KEY_PERIOD, 10); - highPassFilter = configuration.getBoolean(PROPERTY_KEY_HIGH_PASS_FILTER, true); - try { - gyro = new CalibratedGyro(new GyroL3GD20Device(getBus(), getAddress(), sensitivity, highPassFilter)); - } catch (IOException e) { - throw new ConfigurationException(String.format( - "Failed to initialize lidar device. Make sure it is hooked up to bus: %d address: %xd", getBus(), getAddress()), e); - } - } - - @Override - public Collection> getKnownAttributes() { - return KNOWN_ATTRIBUTES; - } - - @Override - public void onMessage(GyroRequest message) { - RoboReference notificationTarget = message.getTarget(); - switch (message.getAction()) { - case CALIBRATE: - try { - gyro.calibrate(); - scanner.reset(); - if (notificationTarget != null) { - notificationTarget.sendMessage(new GyroEvent(new Tuple3f(0, 0, 0))); - } - } catch (IOException e) { - SimpleLoggingUtil.error(getClass(), "Failed to calibrate!", e); - } - break; - case STOP: - stopForNotificationTarget(notificationTarget); - break; - case ONCE: - case CONTINUOUS: - if (message.getNotificationThreshold() != null) { - setUpNotification(message); - } - break; - } - } - - private void stopForNotificationTarget(RoboReference notificationTarget) { - synchronized (this) { - if (notificationTarget == null) { - activeThresholds.clear(); - } else { - activeThresholds.remove(notificationTarget); - } - if (activeThresholds.isEmpty()) { - if (readings != null) { - readings.cancel(false); - readings = null; - } - } - } - } - - @SuppressWarnings("unchecked") - @Override - protected R onGetAttribute(AttributeDescriptor descriptor) { - if (descriptor.getAttributeType() == Tuple3f.class && descriptor.getAttributeName().equals(ATTRIBUTE_NAME_STATE)) { - try { - return (R) gyro.read(); - } catch (IOException e) { - SimpleLoggingUtil.error(getClass(), "Failed to read the gyro!", e); - } - } - return super.onGetAttribute(descriptor); - } - - private void setUpNotification(GyroRequest request) { - synchronized (this) { - if (request.getAction() == GyroAction.CONTINUOUS) { - activeThresholds.put(request.getTarget(), new ContinuousGyroNotificationEntry(request.getTarget(), request.getNotificationThreshold())); - } else { - activeThresholds.put(request.getTarget(), new FixedGyroNotificationEntry(request.getTarget(), request.getNotificationThreshold())); - } - } - if (readings == null) { - synchronized (this) { - readings = getContext().getScheduler().scheduleAtFixedRate(scanner, 0, period, TimeUnit.MILLISECONDS); - } - } - } + /** + * This key configures the sensitivity of the gyro. Use the name of the + * Sensitivity enum. Default is DPS_245. + * + * @see Sensitivity + */ + public static final String PROPERTY_KEY_SENSITIVITY = "sensitivity"; + + /** + * This key configures the high pass filter. Set to true to enable. Default + * is true. + * + * @see GyroL3GD20Device + */ + public static final String PROPERTY_KEY_HIGH_PASS_FILTER = "enableHighPass"; + + /** + * This key configures how often to read the gyro in ms. Default is 10. + */ + public static final String PROPERTY_KEY_PERIOD = "period"; + + /** + * This attribute will provide the state of the gyro as a {@link Tuple3f}. + */ + public static final String ATTRIBUTE_NAME_STATE = "state"; + + public static final Collection> KNOWN_ATTRIBUTES = Collections + .unmodifiableCollection(Arrays.asList(DefaultAttributeDescriptor.create(Tuple3f.class, ATTRIBUTE_NAME_STATE))); + private static final Logger LOGGER = LoggerFactory.getLogger(GyroL3GD20Unit.class); + private final Map, GyroNotificationEntry> activeThresholds = new HashMap<>(); + + private final GyroScanner scanner = new GyroScanner(); + + // TODO review field purpose + private Sensitivity sensitivity; + private boolean highPassFilter; + private int period; + private CalibratedGyro gyro; + private volatile ScheduledFuture readings; + + private class GyroScanner implements Runnable { + private long lastReadingTime = System.currentTimeMillis(); + private Tuple3f lastReading = new Tuple3f(0f, 0f, 0f); + + @Override + public void run() { + Tuple3f data = read(); + long newTime = System.currentTimeMillis(); + + // Trapezoid + Tuple3f tmp = new Tuple3f(data); + long deltaTime = newTime - lastReadingTime; + data.add(lastReading); + data.multiplyScalar(deltaTime / 2000.0f); + + lastReading.set(tmp); + addToDeltas(data); + lastReadingTime = newTime; + } + + private void addToDeltas(Tuple3f data) { + synchronized (GyroL3GD20Unit.this) { + for (GyroNotificationEntry notificationEntry : activeThresholds.values()) { + notificationEntry.addDelta(data); + } + } + } + + private void reset() { + lastReadingTime = System.currentTimeMillis(); + lastReading = read(); + } + + private Tuple3f read() { + try { + return gyro.read(); + } catch (IOException e) { + LOGGER.error("Could not read gyro, aborting:{}", e.getMessage(), e); + return null; + } + } + } + + /** + * Constructor. + * + * @param context the robo context. + * @param id the robo unit id. + */ + public GyroL3GD20Unit(RoboContext context, String id) { + super(GyroRequest.class, context, id); + } + + @Override + protected void onInitialization(Configuration configuration) throws ConfigurationException { + super.onInitialization(configuration); + sensitivity = Sensitivity.valueOf(configuration.getString(PROPERTY_KEY_SENSITIVITY, "DPS_245")); + period = configuration.getInteger(PROPERTY_KEY_PERIOD, 10); + highPassFilter = configuration.getBoolean(PROPERTY_KEY_HIGH_PASS_FILTER, true); + try { + gyro = new CalibratedGyro(new GyroL3GD20Device(getBus(), getAddress(), sensitivity, highPassFilter)); + } catch (IOException e) { + throw new ConfigurationException(String.format( + "Failed to initialize lidar device. Make sure it is hooked up to bus: %d address: %xd", getBus(), getAddress()), e); + } + } + + @Override + public Collection> getKnownAttributes() { + return KNOWN_ATTRIBUTES; + } + + @Override + public void onMessage(GyroRequest message) { + RoboReference notificationTarget = message.getTarget(); + switch (message.getAction()) { + case CALIBRATE: + try { + gyro.calibrate(); + scanner.reset(); + if (notificationTarget != null) { + notificationTarget.sendMessage(new GyroEvent(new Tuple3f(0, 0, 0))); + } + } catch (IOException e) { + LOGGER.error("Failed to calibrate:{}", e.getMessage(), e); + } + break; + case STOP: + stopForNotificationTarget(notificationTarget); + break; + case ONCE: + case CONTINUOUS: + if (message.getNotificationThreshold() != null) { + setUpNotification(message); + } + break; + } + } + + private void stopForNotificationTarget(RoboReference notificationTarget) { + synchronized (this) { + if (notificationTarget == null) { + activeThresholds.clear(); + } else { + activeThresholds.remove(notificationTarget); + } + if (activeThresholds.isEmpty()) { + if (readings != null) { + readings.cancel(false); + readings = null; + } + } + } + } + + @SuppressWarnings("unchecked") + @Override + protected R onGetAttribute(AttributeDescriptor descriptor) { + if (descriptor.getAttributeType() == Tuple3f.class && descriptor.getAttributeName().equals(ATTRIBUTE_NAME_STATE)) { + try { + return (R) gyro.read(); + } catch (IOException e) { + LOGGER.error("Failed to read the gyro:{}", e.getMessage(), e); + } + } + return super.onGetAttribute(descriptor); + } + + private void setUpNotification(GyroRequest request) { + synchronized (this) { + if (request.getAction() == GyroAction.CONTINUOUS) { + activeThresholds.put(request.getTarget(), new ContinuousGyroNotificationEntry(request.getTarget(), request.getNotificationThreshold())); + } else { + activeThresholds.put(request.getTarget(), new FixedGyroNotificationEntry(request.getTarget(), request.getNotificationThreshold())); + } + } + if (readings == null) { + synchronized (this) { + readings = getContext().getScheduler().scheduleAtFixedRate(scanner, 0, period, TimeUnit.MILLISECONDS); + } + } + } } diff --git a/robo4j-units-rpi/src/main/java/com/robo4j/units/rpi/imu/Bno080Unit.java b/robo4j-units-rpi/src/main/java/com/robo4j/units/rpi/imu/Bno080Unit.java index 00d449fb..d01a9ee3 100644 --- a/robo4j-units-rpi/src/main/java/com/robo4j/units/rpi/imu/Bno080Unit.java +++ b/robo4j-units-rpi/src/main/java/com/robo4j/units/rpi/imu/Bno080Unit.java @@ -28,7 +28,8 @@ import com.robo4j.hw.rpi.imu.bno.DataListener; import com.robo4j.hw.rpi.imu.bno.impl.Bno080SPIDevice; import com.robo4j.hw.rpi.imu.bno.shtp.SensorReportId; -import com.robo4j.logging.SimpleLoggingUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.io.IOException; import java.util.ArrayList; @@ -43,91 +44,92 @@ */ public class Bno080Unit extends RoboUnit { - public static final String PROPERTY_REPORT_TYPE = "reportType"; - public static final String PROPERTY_REPORT_DELAY = "reportDelay"; - - private static final class BnoListenerEvent implements DataListener { - private final RoboReference target; - - BnoListenerEvent(RoboReference target) { - this.target = target; - } - - @Override - public void onResponse(DataEvent3f event) { - target.sendMessage(event); - } - } - - private final List listeners = new ArrayList<>(); - private Bno080Device device; - - public Bno080Unit(RoboContext context, String id) { - super(BnoRequest.class, context, id); - } - - // TODO review field purpose - private int reportDelay; - private SensorReportId report; - - @Override - protected void onInitialization(Configuration configuration) throws ConfigurationException { - - final String reportType = configuration.getString(PROPERTY_REPORT_TYPE, null); - report = SensorReportId.valueOf(reportType.toUpperCase()); - if (report.equals(SensorReportId.NONE)) { - throw new ConfigurationException(PROPERTY_REPORT_TYPE); - } - - final Integer delay = configuration.getInteger(PROPERTY_REPORT_DELAY, null); - if (delay == null || delay <= 0) { - throw new ConfigurationException(PROPERTY_REPORT_DELAY); - } - this.reportDelay = delay; - - try { - // TODO: make device configurable - device = Bno080Factory.createDefaultSPIDevice(); - } catch (IOException | InterruptedException e) { - throw new ConfigurationException("Could not initiate device", e); - } - device.start(report, reportDelay); - } - - @Override - public void onMessage(BnoRequest message) { - RoboReference target = message.getTarget(); - switch (message.getListenerAction()) { - case REGISTER: - register(target); - break; - case UNREGISTER: - unregister(target); - break; - default: - SimpleLoggingUtil.error(getClass(), String.format("Unknown operation: %s", message)); - } - - } - - @Override - public void shutdown() { - super.shutdown(); - device.shutdown(); - } - - private synchronized void register(RoboReference target) { - BnoListenerEvent event = new BnoListenerEvent(target); - listeners.add(event); - device.addListener(event); - } - - private synchronized void unregister(RoboReference target) { - for (BnoListenerEvent l : new ArrayList<>(listeners)) { - if (target.equals(l.target)) { - listeners.remove(l); - device.removeListener(l); - } - } - } + public static final String PROPERTY_REPORT_TYPE = "reportType"; + public static final String PROPERTY_REPORT_DELAY = "reportDelay"; + private static final Logger LOGGER = LoggerFactory.getLogger(Bno080Unit.class); + + private static final class BnoListenerEvent implements DataListener { + private final RoboReference target; + + BnoListenerEvent(RoboReference target) { + this.target = target; + } + + @Override + public void onResponse(DataEvent3f event) { + target.sendMessage(event); + } + } + + private final List listeners = new ArrayList<>(); + private Bno080Device device; + + public Bno080Unit(RoboContext context, String id) { + super(BnoRequest.class, context, id); + } + + // TODO review field purpose + private int reportDelay; + private SensorReportId report; + + @Override + protected void onInitialization(Configuration configuration) throws ConfigurationException { + + final String reportType = configuration.getString(PROPERTY_REPORT_TYPE, null); + report = SensorReportId.valueOf(reportType.toUpperCase()); + if (report.equals(SensorReportId.NONE)) { + throw new ConfigurationException(PROPERTY_REPORT_TYPE); + } + + final Integer delay = configuration.getInteger(PROPERTY_REPORT_DELAY, null); + if (delay == null || delay <= 0) { + throw new ConfigurationException(PROPERTY_REPORT_DELAY); + } + this.reportDelay = delay; + + try { + // TODO: make device configurable + device = Bno080Factory.createDefaultSPIDevice(); + } catch (IOException | InterruptedException e) { + throw new ConfigurationException("Could not initiate device", e); + } + device.start(report, reportDelay); + } + + @Override + public void onMessage(BnoRequest message) { + RoboReference target = message.getTarget(); + switch (message.getListenerAction()) { + case REGISTER: + register(target); + break; + case UNREGISTER: + unregister(target); + break; + default: + LOGGER.error("Unknown operation: {}", message); + } + + } + + @Override + public void shutdown() { + super.shutdown(); + device.shutdown(); + } + + private synchronized void register(RoboReference target) { + BnoListenerEvent event = new BnoListenerEvent(target); + listeners.add(event); + device.addListener(event); + } + + private synchronized void unregister(RoboReference target) { + for (BnoListenerEvent l : new ArrayList<>(listeners)) { + if (target.equals(l.target)) { + listeners.remove(l); + device.removeListener(l); + } + } + } } diff --git a/robo4j-units-rpi/src/main/java/com/robo4j/units/rpi/imu/DataEventListenerUnit.java b/robo4j-units-rpi/src/main/java/com/robo4j/units/rpi/imu/DataEventListenerUnit.java index 4389cfbf..4c6cd526 100644 --- a/robo4j-units-rpi/src/main/java/com/robo4j/units/rpi/imu/DataEventListenerUnit.java +++ b/robo4j-units-rpi/src/main/java/com/robo4j/units/rpi/imu/DataEventListenerUnit.java @@ -20,7 +20,8 @@ import com.robo4j.RoboContext; import com.robo4j.RoboUnit; import com.robo4j.hw.rpi.imu.bno.DataEvent3f; -import com.robo4j.logging.SimpleLoggingUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * DataEventListener unit listens to {@link DataEvent3f} event types produced by @@ -30,13 +31,14 @@ * @author Miroslav Wengner (@miragemiko) */ public class DataEventListenerUnit extends RoboUnit { + private static final Logger LOGGER = LoggerFactory.getLogger(DataEventListenerUnit.class); - public DataEventListenerUnit(RoboContext context, String id) { - super(DataEvent3f.class, context, id); - } + public DataEventListenerUnit(RoboContext context, String id) { + super(DataEvent3f.class, context, id); + } - @Override - public void onMessage(DataEvent3f message) { - SimpleLoggingUtil.info(getClass(), "received:" + message); - } + @Override + public void onMessage(DataEvent3f message) { + LOGGER.info("received:{}", message); + } } diff --git a/robo4j-units-rpi/src/main/java/com/robo4j/units/rpi/imu/VectorEventListenerUnit.java b/robo4j-units-rpi/src/main/java/com/robo4j/units/rpi/imu/VectorEventListenerUnit.java index 1fde88a2..a0505f83 100644 --- a/robo4j-units-rpi/src/main/java/com/robo4j/units/rpi/imu/VectorEventListenerUnit.java +++ b/robo4j-units-rpi/src/main/java/com/robo4j/units/rpi/imu/VectorEventListenerUnit.java @@ -23,8 +23,9 @@ import com.robo4j.RoboUnit; import com.robo4j.configuration.Configuration; import com.robo4j.hw.rpi.imu.bno.VectorEvent; -import com.robo4j.logging.SimpleLoggingUtil; import com.robo4j.net.LookupServiceProvider; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * BNOVectorEventListenerUnit listen to VectorEvent events produced by {@link Bno080Unit} @@ -33,34 +34,34 @@ * @author Miroslav Wengner (@miragemiko) */ public class VectorEventListenerUnit extends RoboUnit { - - public static final String ATTR_TARGET_CONTEXT = "targetContext"; - public static final String ATTR_REMOTE_UNIT = "remoteUnit"; + public static final String ATTR_TARGET_CONTEXT = "targetContext"; + public static final String ATTR_REMOTE_UNIT = "remoteUnit"; + private static final Logger LOGGER = LoggerFactory.getLogger(VectorEventListenerUnit.class); - public VectorEventListenerUnit(RoboContext context, String id) { - super(VectorEvent.class, context, id); - } + public VectorEventListenerUnit(RoboContext context, String id) { + super(VectorEvent.class, context, id); + } - private String targetContext; - private String remoteUnit; + private String targetContext; + private String remoteUnit; - @Override - protected void onInitialization(Configuration configuration) throws ConfigurationException { - targetContext = configuration.getString(ATTR_TARGET_CONTEXT, null); - remoteUnit = configuration.getString(ATTR_REMOTE_UNIT, null); - } + @Override + protected void onInitialization(Configuration configuration) throws ConfigurationException { + targetContext = configuration.getString(ATTR_TARGET_CONTEXT, null); + remoteUnit = configuration.getString(ATTR_REMOTE_UNIT, null); + } - @Override - public void onMessage(VectorEvent message) { - SimpleLoggingUtil.info(getClass(), "received:" + message); - RoboContext remoteContext = LookupServiceProvider.getDefaultLookupService().getContext(targetContext); - if(remoteContext != null){ - RoboReference roboReference = remoteContext.getReference(remoteUnit); - if(roboReference != null){ - roboReference.sendMessage(message); - } - } else { - SimpleLoggingUtil.info(getClass(), String.format("context not found: %s", targetContext)); - } - } + @Override + public void onMessage(VectorEvent message) { + LOGGER.info("received:{}", message); + RoboContext remoteContext = LookupServiceProvider.getDefaultLookupService().getContext(targetContext); + if (remoteContext != null) { + RoboReference roboReference = remoteContext.getReference(remoteUnit); + if (roboReference != null) { + roboReference.sendMessage(message); + } + } else { + LOGGER.warn("context not found: {}", targetContext); + } + } } diff --git a/robo4j-units-rpi/src/main/java/com/robo4j/units/rpi/lcd/AdafruitButtonUnit.java b/robo4j-units-rpi/src/main/java/com/robo4j/units/rpi/lcd/AdafruitButtonUnit.java index c3b8f0d1..47d75dcf 100644 --- a/robo4j-units-rpi/src/main/java/com/robo4j/units/rpi/lcd/AdafruitButtonUnit.java +++ b/robo4j-units-rpi/src/main/java/com/robo4j/units/rpi/lcd/AdafruitButtonUnit.java @@ -16,8 +16,6 @@ */ package com.robo4j.units.rpi.lcd; -import java.io.IOException; - import com.robo4j.ConfigurationException; import com.robo4j.LifecycleState; import com.robo4j.RoboContext; @@ -28,101 +26,105 @@ import com.robo4j.hw.rpi.i2c.adafruitlcd.Button; import com.robo4j.hw.rpi.i2c.adafruitlcd.ButtonListener; import com.robo4j.hw.rpi.i2c.adafruitlcd.ButtonPressedObserver; -import com.robo4j.logging.SimpleLoggingUtil; import com.robo4j.units.rpi.I2CRoboUnit; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; /** * A {@link RoboUnit} for the button part of the Adafruit 16x2 character LCD * shield. Having a separate unit allows the buttons to be used independently of * the LCD. - * + * * @author Marcus Hirt (@hirt) * @author Miroslav Wengner (@miragemiko) */ public class AdafruitButtonUnit extends I2CRoboUnit { - private AdafruitLcd lcd; - private ButtonPressedObserver observer; - private String target; - private ButtonListener buttonListener; + private static final Logger LOGGER = LoggerFactory.getLogger(AdafruitButtonUnit.class); + private AdafruitLcd lcd; + private ButtonPressedObserver observer; + private String target; + private ButtonListener buttonListener; - public AdafruitButtonUnit(RoboContext context, String id) { - super(Object.class, context, id); - } + public AdafruitButtonUnit(RoboContext context, String id) { + super(Object.class, context, id); + } - @Override - protected void onInitialization(Configuration configuration) throws ConfigurationException { - super.onInitialization(configuration); - target = configuration.getString("target", null); - if (target == null) { - throw ConfigurationException.createMissingConfigNameException("target"); - } - try { - lcd = AdafruitLcdUnit.getLCD(getBus(), getAddress()); - } catch (IOException e) { - throw new ConfigurationException("Could not initialize LCD shield", e); - } - setState(LifecycleState.INITIALIZED); - } + @Override + protected void onInitialization(Configuration configuration) throws ConfigurationException { + super.onInitialization(configuration); + target = configuration.getString("target", null); + if (target == null) { + throw ConfigurationException.createMissingConfigNameException("target"); + } + try { + lcd = AdafruitLcdUnit.getLCD(getBus(), getAddress()); + } catch (IOException e) { + throw new ConfigurationException("Could not initialize LCD shield", e); + } + setState(LifecycleState.INITIALIZED); + } - @Override - public void start() { - final RoboReference targetRef = getContext().getReference(target); - setState(LifecycleState.STARTING); - observer = new ButtonPressedObserver(lcd); - buttonListener = (Button button) -> { - if (getState() == LifecycleState.STARTED) { - try { - switch (button) { - case UP: - targetRef.sendMessage(AdafruitButtonEnum.UP); - break; - case DOWN: - targetRef.sendMessage(AdafruitButtonEnum.DOWN); - break; - case RIGHT: - targetRef.sendMessage(AdafruitButtonEnum.LEFT); - break; - case LEFT: - targetRef.sendMessage(AdafruitButtonEnum.RIGHT); - break; - case SELECT: - targetRef.sendMessage(AdafruitButtonEnum.SELECT); - break; - default: - lcd.clear(); - lcd.setText(String.format("Button %s\nis not in use...", button.toString())); - } - } catch (IOException e) { - handleException(e); - } - } - }; + @Override + public void start() { + final RoboReference targetRef = getContext().getReference(target); + setState(LifecycleState.STARTING); + observer = new ButtonPressedObserver(lcd); + buttonListener = (Button button) -> { + if (getState() == LifecycleState.STARTED) { + try { + switch (button) { + case UP: + targetRef.sendMessage(AdafruitButtonEnum.UP); + break; + case DOWN: + targetRef.sendMessage(AdafruitButtonEnum.DOWN); + break; + case RIGHT: + targetRef.sendMessage(AdafruitButtonEnum.LEFT); + break; + case LEFT: + targetRef.sendMessage(AdafruitButtonEnum.RIGHT); + break; + case SELECT: + targetRef.sendMessage(AdafruitButtonEnum.SELECT); + break; + default: + lcd.clear(); + lcd.setText(String.format("Button %s\nis not in use...", button.toString())); + } + } catch (IOException e) { + handleException(e); + } + } + }; - observer.addButtonListener(buttonListener); - setState(LifecycleState.STARTED); - } + observer.addButtonListener(buttonListener); + setState(LifecycleState.STARTED); + } - @Override - public void stop() { - observer.removeButtonListener(buttonListener); - observer = null; - buttonListener = null; - } + @Override + public void stop() { + observer.removeButtonListener(buttonListener); + observer = null; + buttonListener = null; + } - @Override - public void shutdown() { - try { - lcd.stop(); - } catch (IOException e) { - SimpleLoggingUtil.error(getClass(), "Failed to disconnect LCD", e); - } - } + @Override + public void shutdown() { + try { + lcd.stop(); + } catch (IOException e) { + LOGGER.error("Failed to disconnect LCD:{}", e.getMessage(), e); + } + } - //Private Methods - private void handleException(IOException e) { - setState(LifecycleState.STOPPING); - shutdown(); - setState(LifecycleState.FAILED); - } + //Private Methods + private void handleException(IOException e) { + setState(LifecycleState.STOPPING); + shutdown(); + setState(LifecycleState.FAILED); + } } \ No newline at end of file diff --git a/robo4j-units-rpi/src/main/java/com/robo4j/units/rpi/lcd/AdafruitLcdUnit.java b/robo4j-units-rpi/src/main/java/com/robo4j/units/rpi/lcd/AdafruitLcdUnit.java index bc64a605..983339a1 100644 --- a/robo4j-units-rpi/src/main/java/com/robo4j/units/rpi/lcd/AdafruitLcdUnit.java +++ b/robo4j-units-rpi/src/main/java/com/robo4j/units/rpi/lcd/AdafruitLcdUnit.java @@ -28,11 +28,12 @@ import com.robo4j.hw.rpi.i2c.adafruitlcd.LcdFactory; import com.robo4j.hw.rpi.i2c.adafruitlcd.impl.AdafruitLcdImpl.Direction; import com.robo4j.hw.rpi.utils.I2cBus; -import com.robo4j.logging.SimpleLoggingUtil; import com.robo4j.units.rpi.I2CEndPoint; import com.robo4j.units.rpi.I2CRegistry; import com.robo4j.units.rpi.I2CRoboUnit; import com.robo4j.util.StringConstants; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.io.IOException; import java.util.Arrays; @@ -42,176 +43,164 @@ /** * A {@link RoboUnit} for the Adafruit 16x2 character LCD shield. - * + * * @author Marcus Hirt (@hirt) * @author Miroslav Wengner (@miragemiko) */ public class AdafruitLcdUnit extends I2CRoboUnit { - private static final String ATTRIBUTE_NAME_COLOR = "color"; - private static final String ATTRIBUTE_NAME_TEXT = "text"; + private static final Logger LOGGER = LoggerFactory.getLogger(AdafruitLcdUnit.class); + private static final String ATTRIBUTE_NAME_COLOR = "color"; + private static final String ATTRIBUTE_NAME_TEXT = "text"; - public static final Collection> KNOWN_ATTRIBUTES = Collections - .unmodifiableCollection(Arrays.asList(DefaultAttributeDescriptor.create(String.class, ATTRIBUTE_NAME_TEXT), - DefaultAttributeDescriptor.create(Color.class, ATTRIBUTE_NAME_COLOR))); + public static final Collection> KNOWN_ATTRIBUTES = Collections + .unmodifiableCollection(Arrays.asList(DefaultAttributeDescriptor.create(String.class, ATTRIBUTE_NAME_TEXT), + DefaultAttributeDescriptor.create(Color.class, ATTRIBUTE_NAME_COLOR))); - private final AtomicReference stringMessage = new AtomicReference<>(StringConstants.EMPTY); - private AdafruitLcd lcd; + private final AtomicReference stringMessage = new AtomicReference<>(StringConstants.EMPTY); + private AdafruitLcd lcd; - public AdafruitLcdUnit(RoboContext context, String id) { - super(LcdMessage.class, context, id); - } + public AdafruitLcdUnit(RoboContext context, String id) { + super(LcdMessage.class, context, id); + } - /** - * - * @param bus - * used bus - * @param address - * desired address + /** + * @param bus used bus + * @param address desired address */ - static AdafruitLcd getLCD(I2cBus bus, int address) throws IOException { - Object lcd = I2CRegistry.getI2CDeviceByEndPoint(new I2CEndPoint(bus, address)); - if (lcd == null) { - try { - lcd = LcdFactory.createLCD(bus, address); - // Note that we cannot catch hardware specific exceptions here, - // since they will be loaded when we run as mocked. - } catch (Exception e) { - throw new IOException(e); - } - I2CRegistry.registerI2CDevice(lcd, new I2CEndPoint(bus, address)); - } - return (AdafruitLcd) lcd; - } + static AdafruitLcd getLCD(I2cBus bus, int address) throws IOException { + Object lcd = I2CRegistry.getI2CDeviceByEndPoint(new I2CEndPoint(bus, address)); + if (lcd == null) { + try { + lcd = LcdFactory.createLCD(bus, address); + // Note that we cannot catch hardware specific exceptions here, + // since they will be loaded when we run as mocked. + } catch (Exception e) { + throw new IOException(e); + } + I2CRegistry.registerI2CDevice(lcd, new I2CEndPoint(bus, address)); + } + return (AdafruitLcd) lcd; + } - /** - * - * @param message - * the message received by this unit. - * - */ - @Override - public void onMessage(LcdMessage message) { - try { - processLcdMessage(message); - } catch (Exception e) { - SimpleLoggingUtil.debug(getClass(), "Could not accept message" + message.toString(), e); - } - } + /** + * @param message the message received by this unit. + */ + @Override + public void onMessage(LcdMessage message) { + try { + processLcdMessage(message); + } catch (Exception e) { + LOGGER.error("Could not accept message:{}", message, e); + } + } - /** - * - * @param configuration - * - unit configuration - * @throws ConfigurationException - * exception - */ - @Override - protected void onInitialization(Configuration configuration) throws ConfigurationException { - super.onInitialization(configuration); - try { - lcd = getLCD(getBus(), getAddress()); - } catch (IOException e) { - throw new ConfigurationException("Could not initialize LCD", e); - } - } + /** + * @param configuration - unit configuration + * @throws ConfigurationException exception + */ + @Override + protected void onInitialization(Configuration configuration) throws ConfigurationException { + super.onInitialization(configuration); + try { + lcd = getLCD(getBus(), getAddress()); + } catch (IOException e) { + throw new ConfigurationException("Could not initialize LCD", e); + } + } - @Override - public void stop() { - setState(LifecycleState.STOPPING); - try { - lcd.clear(); - lcd.setDisplayEnabled(false); - lcd.stop(); - } catch (IOException e) { - throw new AdafruitException("Could not disconnect LCD", e); - } - setState(LifecycleState.STOPPED); - } + @Override + public void stop() { + setState(LifecycleState.STOPPING); + try { + lcd.clear(); + lcd.setDisplayEnabled(false); + lcd.stop(); + } catch (IOException e) { + throw new AdafruitException("Could not disconnect LCD", e); + } + setState(LifecycleState.STOPPED); + } - /** - * @param message - * accepted message type - * @throws IOException - */ - private void processLcdMessage(LcdMessage message) throws IOException { - switch (message.getType()) { - case CLEAR: - lcd.clear(); - break; - case DISPLAY_ENABLE: - final boolean disen = Boolean.valueOf(message.getText().trim()); - lcd.setDisplayEnabled(disen); - break; - case SCROLL: - // TODO: consider enum as the constant - switch (message.getText().trim()) { - case "left": - lcd.scrollDisplay(Direction.LEFT); - break; - case "right": - lcd.scrollDisplay(Direction.RIGHT); - break; - default: - SimpleLoggingUtil.error(getClass(), "Scroll direction " + message.getText() + " is unknown"); - break; - } - break; - case SET_TEXT: - if (message.getColor() != null) { - lcd.setBacklight(message.getColor()); - } - if (message.getText() != null) { - String text = message.getText(); - lcd.setText(text); - stringMessage.set(text); - } - break; - case STOP: - lcd.stop(); - break; - default: - SimpleLoggingUtil.error(getClass(), message.getType() + "demo not supported!"); - break; - } - } + /** + * @param message accepted message type + * @throws IOException + */ + private void processLcdMessage(LcdMessage message) throws IOException { + switch (message.getType()) { + case CLEAR: + lcd.clear(); + break; + case DISPLAY_ENABLE: + final boolean disen = Boolean.valueOf(message.getText().trim()); + lcd.setDisplayEnabled(disen); + break; + case SCROLL: + // TODO: consider enum as the constant + switch (message.getText().trim()) { + case "left": + lcd.scrollDisplay(Direction.LEFT); + break; + case "right": + lcd.scrollDisplay(Direction.RIGHT); + break; + default: + LOGGER.warn("unknown scroll direction:{}", message.getText()); + break; + } + break; + case SET_TEXT: + if (message.getColor() != null) { + lcd.setBacklight(message.getColor()); + } + if (message.getText() != null) { + String text = message.getText(); + lcd.setText(text); + stringMessage.set(text); + } + break; + case STOP: + lcd.stop(); + break; + default: + LOGGER.warn("demo not supported:{}", message.getType()); + break; + } + } - @SuppressWarnings("unchecked") - @Override - public R onGetAttribute(AttributeDescriptor attribute) { - if (ATTRIBUTE_NAME_TEXT.equals(attribute.getAttributeName())) { - return (R) stringMessage.get(); - } else if (ATTRIBUTE_NAME_COLOR.equals(attribute.getAttributeName())) { - try { - return (R) lcd.getBacklight(); - } catch (IOException e) { - SimpleLoggingUtil.error(getClass(), "Failed to read the color", e); - } - } - return null; - } + @SuppressWarnings("unchecked") + @Override + public R onGetAttribute(AttributeDescriptor attribute) { + if (ATTRIBUTE_NAME_TEXT.equals(attribute.getAttributeName())) { + return (R) stringMessage.get(); + } else if (ATTRIBUTE_NAME_COLOR.equals(attribute.getAttributeName())) { + try { + return (R) lcd.getBacklight(); + } catch (IOException e) { + LOGGER.error("Failed to read the color:{}", e.getMessage(), e); + } + } + return null; + } - @Override - public Collection> getKnownAttributes() { - return KNOWN_ATTRIBUTES; - } + @Override + public Collection> getKnownAttributes() { + return KNOWN_ATTRIBUTES; + } - /** - * Fill one of the first 8 CGRAM locations with custom characters. The location - * parameter should be between 0 and 7 and pattern should provide an array of 8 - * bytes containing the pattern. e.g. you can design your custom character at - * <a - * href=http://www.quinapalus.com/hd44780udg.html> http://www.quinapalus.com/hd44780udg.html<a/> . - * To show your custom character obtain the string representation for the - * location e.g. String.format("custom char=%c", 0). - * - * @param location - * storage location for this character, between 0 and 7 - * @param pattern - * array of 8 bytes containing the character's pattern - * @throws IOException - * exception - */ - public void createChar(final int location, final byte[] pattern) throws IOException { - lcd.createChar(location, pattern); - } + /** + * Fill one of the first 8 CGRAM locations with custom characters. The location + * parameter should be between 0 and 7 and pattern should provide an array of 8 + * bytes containing the pattern. e.g. you can design your custom character at + * <a + * href=http://www.quinapalus.com/hd44780udg.html> http://www.quinapalus.com/hd44780udg.html<a/> . + * To show your custom character obtain the string representation for the + * location e.g. String.format("custom char=%c", 0). + * + * @param location storage location for this character, between 0 and 7 + * @param pattern array of 8 bytes containing the character's pattern + * @throws IOException exception + */ + public void createChar(final int location, final byte[] pattern) throws IOException { + lcd.createChar(location, pattern); + } } diff --git a/robo4j-units-rpi/src/main/java/com/robo4j/units/rpi/led/AbstractI2CBackpackUnit.java b/robo4j-units-rpi/src/main/java/com/robo4j/units/rpi/led/AbstractI2CBackpackUnit.java index de075033..f287c32c 100644 --- a/robo4j-units-rpi/src/main/java/com/robo4j/units/rpi/led/AbstractI2CBackpackUnit.java +++ b/robo4j-units-rpi/src/main/java/com/robo4j/units/rpi/led/AbstractI2CBackpackUnit.java @@ -19,44 +19,42 @@ import com.robo4j.RoboContext; import com.robo4j.hw.rpi.i2c.adafruitbackpack.AbstractBackpack; -import com.robo4j.logging.SimpleLoggingUtil; import com.robo4j.units.rpi.I2CRoboUnit; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * AbstractI2CBackpackUnit abstract I2C LedBackpack unit * - * @param - * backpack unit type - * * @author Marcus Hirt (@hirt) * @author Miroslav Wengner (@miragemiko) */ abstract class AbstractI2CBackpackUnit extends I2CRoboUnit { + static final String ATTRIBUTE_ADDRESS = "address"; + static final String ATTRIBUTE_BUS = "bus"; + static final String ATTRIBUTE_BRIGHTNESS = "brightness"; + private static final Logger LOGGER = LoggerFactory.getLogger(AbstractI2CBackpackUnit.class); - static final String ATTRIBUTE_ADDRESS = "address"; - static final String ATTRIBUTE_BUS = "bus"; - static final String ATTRIBUTE_BRIGHTNESS = "brightness"; - - AbstractI2CBackpackUnit(Class messageType, RoboContext context, String id) { - super(messageType, context, id); - } + AbstractI2CBackpackUnit(Class messageType, RoboContext context, String id) { + super(messageType, context, id); + } - void processMessage(AbstractBackpack device, DrawMessage message) { - switch (message.getType()) { - case CLEAR: - device.clear(); - break; - case PAINT: - paint(message); - break; - case DISPLAY: - paint(message); - device.display(); - break; - default: - SimpleLoggingUtil.error(getClass(), String.format("Illegal message: %s", message)); - } - } + void processMessage(AbstractBackpack device, DrawMessage message) { + switch (message.getType()) { + case CLEAR: + device.clear(); + break; + case PAINT: + paint(message); + break; + case DISPLAY: + paint(message); + device.display(); + break; + default: + LOGGER.warn("Illegal message: {}", message); + } + } - abstract void paint(DrawMessage message); + abstract void paint(DrawMessage message); } diff --git a/robo4j-units-rpi/src/main/java/com/robo4j/units/rpi/led/AdafruitAlphanumericUnit.java b/robo4j-units-rpi/src/main/java/com/robo4j/units/rpi/led/AdafruitAlphanumericUnit.java index 487bcb40..76ba7cf0 100644 --- a/robo4j-units-rpi/src/main/java/com/robo4j/units/rpi/led/AdafruitAlphanumericUnit.java +++ b/robo4j-units-rpi/src/main/java/com/robo4j/units/rpi/led/AdafruitAlphanumericUnit.java @@ -17,19 +17,20 @@ package com.robo4j.units.rpi.led; -import java.io.IOException; - import com.robo4j.ConfigurationException; import com.robo4j.RoboContext; import com.robo4j.configuration.Configuration; import com.robo4j.hw.rpi.i2c.adafruitbackpack.AbstractBackpack; import com.robo4j.hw.rpi.i2c.adafruitbackpack.AlphanumericDevice; -import com.robo4j.logging.SimpleLoggingUtil; import com.robo4j.units.rpi.I2CRoboUnit; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; /** * AdafruitAlphanumericUnit - * + *

* This version of the LED backpack is designed for two dual 14-segment * "Alphanumeric" displays. These 14-segment displays normally require 18 pins * (4 'characters' and 14 total segments each) This backpack solves the @@ -38,60 +39,61 @@ * controller chip takes care of everything, drawing all the LEDs in the * background. All you have to do is write data to it using the 2-pin I2C * interface - * + *

* see https://learn.adafruit.com/adafruit-led-backpack/0-54-alphanumeric * * @author Marcus Hirt (@hirt) * @author Miroslav Wengner (@miragemiko) */ public class AdafruitAlphanumericUnit extends I2CRoboUnit { - private AlphanumericDevice device; + private static final Logger LOGGER = LoggerFactory.getLogger(AdafruitAlphanumericUnit.class); + private AlphanumericDevice device; - public AdafruitAlphanumericUnit(RoboContext context, String id) { - super(AlphaNumericMessage.class, context, id); - } + public AdafruitAlphanumericUnit(RoboContext context, String id) { + super(AlphaNumericMessage.class, context, id); + } - @Override - protected void onInitialization(Configuration configuration) throws ConfigurationException { - super.onInitialization(configuration); - int brightness = configuration.getInteger(AbstractI2CBackpackUnit.ATTRIBUTE_BRIGHTNESS, AbstractBackpack.DEFAULT_BRIGHTNESS); + @Override + protected void onInitialization(Configuration configuration) throws ConfigurationException { + super.onInitialization(configuration); + int brightness = configuration.getInteger(AbstractI2CBackpackUnit.ATTRIBUTE_BRIGHTNESS, AbstractBackpack.DEFAULT_BRIGHTNESS); - try { - device = new AlphanumericDevice(getBus(), getAddress(), brightness); - } catch (IOException e) { - throw new ConfigurationException("Failed to instantiate device", e); - } - } + try { + device = new AlphanumericDevice(getBus(), getAddress(), brightness); + } catch (IOException e) { + throw new ConfigurationException("Failed to instantiate device", e); + } + } - @Override - public void onMessage(AlphaNumericMessage message) { - switch (message.getCommand()) { - case CLEAR: - device.clear(); - break; - case PAINT: - render(message); - break; - case DISPLAY: - render(message); - device.display(); - break; - default: - SimpleLoggingUtil.error(getClass(), String.format("Illegal message: %s", message)); - } - } + @Override + public void onMessage(AlphaNumericMessage message) { + switch (message.getCommand()) { + case CLEAR: + device.clear(); + break; + case PAINT: + render(message); + break; + case DISPLAY: + render(message); + device.display(); + break; + default: + LOGGER.error("Illegal message: {}", message); + } + } - private void render(AlphaNumericMessage message) { - if (message.getStartPosition() == -1) { - for (int i = 0; i < message.getCharacters().length; i++) { - device.addCharacter((char) message.getCharacters()[i], message.getDots()[i]); - } - } else { - for (int i = 0; i < message.getCharacters().length; i++) { - device.setCharacter((message.getStartPosition() + i) % device.getNumberOfCharacters(), (char) message.getCharacters()[i], - message.getDots()[i]); - } - } + private void render(AlphaNumericMessage message) { + if (message.getStartPosition() == -1) { + for (int i = 0; i < message.getCharacters().length; i++) { + device.addCharacter((char) message.getCharacters()[i], message.getDots()[i]); + } + } else { + for (int i = 0; i < message.getCharacters().length; i++) { + device.setCharacter((message.getStartPosition() + i) % device.getNumberOfCharacters(), (char) message.getCharacters()[i], + message.getDots()[i]); + } + } - } + } } diff --git a/robo4j-units-rpi/src/main/java/com/robo4j/units/rpi/lidarlite/LaserScanner.java b/robo4j-units-rpi/src/main/java/com/robo4j/units/rpi/lidarlite/LaserScanner.java index ba7cc4b9..3918911b 100644 --- a/robo4j-units-rpi/src/main/java/com/robo4j/units/rpi/lidarlite/LaserScanner.java +++ b/robo4j-units-rpi/src/main/java/com/robo4j/units/rpi/lidarlite/LaserScanner.java @@ -16,18 +16,11 @@ */ package com.robo4j.units.rpi.lidarlite; -import java.io.IOException; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.function.Predicate; - import com.robo4j.ConfigurationException; import com.robo4j.RoboContext; import com.robo4j.RoboReference; import com.robo4j.configuration.Configuration; import com.robo4j.hw.rpi.i2c.lidar.LidarLiteDevice; -import com.robo4j.logging.SimpleLoggingUtil; import com.robo4j.math.geometry.Point2f; import com.robo4j.math.geometry.ScanResult2D; import com.robo4j.math.geometry.impl.ScanResultImpl; @@ -35,325 +28,331 @@ import com.robo4j.math.jfr.ScanEvent; import com.robo4j.units.rpi.I2CRoboUnit; import com.robo4j.units.rpi.pwm.PCA9685ServoUnit; -import jdk.jfr.Event; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Predicate; /** * This unit controls a servo to do laser range sweep. - * + *

* <p> Configuration: </p> <li> <ul> pan: the servo to * use for panning the laser. Defaults to "laserscanner.servo". Set to "null" to * not use a servo for panning. </ul> <ul> servoRange: the range in * degrees that send 1.0 (full) to a servo will result in. This must be measured * by testing your hardware. Changing configuration on your servo will change * this too. </ul> </li> - * + *

* FIXME(Marcus/Mar 10, 2017): Only supports the pan servo for now. - * + * * @author Marcus Hirt (@hirt) * @author Miroslav Wengner (@miragemiko) */ public class LaserScanner extends I2CRoboUnit { - /** - * Since the lidar lite will sometimes give some abnormal readings with very - * short range, we need to be able to filter out readings. Set this - * depending on your hardware and needs. - */ - private final static float DEFAULT_FILTER_MIN_RANGE = 0.08f; - private Predicate pointFilter; - private String pan; - private LidarLiteDevice lidar; - private float servoRange; - private float angularSpeed; - private float minimumAcquisitionTime; - private float trim; - - /** - * Filter for filtering out mis-reads. Anything closer than minRange will be - * filtered out. - */ - private static class MinRangePointFilter implements Predicate { - private final float minRange; - - private MinRangePointFilter(float minRange) { - this.minRange = minRange; - } - - @Override - public boolean test(Point2f t) { - return t.getRange() >= minRange; - } - } - - private final static class ScanJob implements Runnable { - private final AtomicInteger invokeCount = new AtomicInteger(0); - private final ScanResultImpl scanResult; - private final ScanRequest request; - private final RoboReference recipient; - private final RoboReference servo; - private final boolean lowToHigh; - private final int numberOfScans; - private long delayMicros; - private final float trim; - private final float servoRange; - private final LidarLiteDevice lidar; - private volatile float currentAngle; - private volatile boolean finished = false; - private final ScanEvent scanEvent; - - /** - * - * @param lowToHigh - * @param minimumServoMovementTime - * the minimum time for the servo to complete the movement - * over the range, in seconds - * @param minimumAcquisitionTime - * @param trim - * @param request - * @param servo - * @param servoRange - * @param lidar - * @param recipient - */ - public ScanJob(boolean lowToHigh, float minimumServoMovementTime, float minimumAcquisitionTime, float trim, ScanRequest request, - RoboReference servo, float servoRange, LidarLiteDevice lidar, RoboReference recipient, - Predicate pointFilter) { - this.lowToHigh = lowToHigh; - this.trim = trim; - this.request = request; - this.servo = servo; - this.servoRange = servoRange; - this.lidar = lidar; - this.recipient = recipient; - // one move, one first acquisition - this.numberOfScans = calculateNumberOfScans() + 1; - this.delayMicros = calculateDelay(minimumAcquisitionTime, minimumServoMovementTime); - this.currentAngle = lowToHigh ? request.getStartAngle() : request.getStartAngle() + request.getRange(); - this.scanResult = new ScanResultImpl(100, request.getStep(), pointFilter); - scanEvent = new ScanEvent(scanResult.getScanID(), getScanInfo()); - scanEvent.setScanLeftRight(lowToHigh); - JfrUtils.begin(scanEvent); - } - - @Override - public void run() { - int currentRun = invokeCount.incrementAndGet(); - if (currentRun == 1) { - // On first step, only move servo to start position - float normalizedServoTarget = getNormalizedAngle(); - servo.sendMessage(normalizedServoTarget); - return; - } else if (currentRun == 2) { - // On second, just start acquisition (no point to read yet) - startAcquisition(); - } else if (currentRun > numberOfScans) { - doScan(); - finish(); - } else { - doScan(); - } - // FIXME(Marcus/Mar 10, 2017): May want to synchronize this... - updateTargetAngle(); - } - - private void startAcquisition() { - try { - lidar.acquireRange(); - servo.sendMessage(getNormalizedAngle()); - } catch (IOException e) { - SimpleLoggingUtil.error(getClass(), "Could not read laser!", e); - } - } - - private float getNormalizedAngle() { - return this.currentAngle / this.servoRange; - } - - private void doScan() { - // Read previous acquisition - try { - float readDistance = lidar.readDistance(); - // Laser was actually shooting at the previous angle, before - // moving - recalculate for that angle - float lastAngle = currentAngle + (lowToHigh ? -request.getStep() - trim : request.getStep() + trim); - scanResult.addPoint(Point2f.fromPolar(readDistance, (float) Math.toRadians(lastAngle))); - servo.sendMessage(getNormalizedAngle()); - lidar.acquireRange(); - } catch (IOException e) { - SimpleLoggingUtil.error(getClass(), "Could not read laser!", e); - } - } - - private void finish() { - if (!finished) { - recipient.sendMessage(scanResult); - finished = true; - JfrUtils.end(scanEvent); - JfrUtils.commit(scanEvent); - } else { - SimpleLoggingUtil.error(getClass(), "Tried to scan more laser points after being finished!"); - } - } - - private void updateTargetAngle() { - if (lowToHigh) { - currentAngle += request.getStep(); - } else { - currentAngle -= request.getStep(); - } - } - - private int calculateNumberOfScans() { - return Math.round(request.getRange() / request.getStep()); - } - - // FIXME(Marcus/Mar 10, 2017): Calculate the required delay later from - // physical model. - private long calculateDelay(float minimumAcquisitionTime, float minimumServoMovementTime) { - float delayPerStep = minimumServoMovementTime * 1000 / calculateNumberOfScans(); - // If we have a slow servo, we will need to wait for the servo to - // move. If we have a slow acquisition, we will need to wait for the - // laser before continuing - float actualDelay = Math.max(delayPerStep, minimumAcquisitionTime); - return Math.round(actualDelay * 1000.0d); - } - - private String getScanInfo() { - return String.format("start: %2.1f, end: %2.1f, step:%2.1f", request.getStartAngle(), - request.getStartAngle() + request.getRange(), request.getStep()); - } - } - - private final static class ScanFixedAngleJob implements Runnable { - private final RoboReference recipient; - private final LidarLiteDevice lidar; - private final float angle; - private final Predicate filter; - private volatile boolean firstRun = true; - - public ScanFixedAngleJob(RoboReference recipient, LidarLiteDevice lidar, float angle, Predicate filter) { - this.recipient = recipient; - this.lidar = lidar; - this.angle = angle; - this.filter = filter; - } - - @Override - public void run() { - try { - if (firstRun) { - firstRun = false; - lidar.acquireRange(); - } else { - ScanResultImpl scan = new ScanResultImpl(1, 0f, filter); - scan.addPoint(lidar.readDistance(), (float) Math.toRadians(angle)); - recipient.sendMessage(scan); - } - } catch (IOException e) { - SimpleLoggingUtil.debug(getClass(), "Failed to read lidar", e); - } - } - } - - public LaserScanner(RoboContext context, String id) { - super(ScanRequest.class, context, id); - } - - @Override - protected void onInitialization(Configuration configuration) throws ConfigurationException { - super.onInitialization(configuration); - pan = configuration.getString("servo", "laserscanner.servo"); - - // Using degrees for convenience - servoRange = (float) configuration.getFloat("servoRange", 45.0f); - - // Using angular degrees per second. - angularSpeed = configuration.getFloat("angularSpeed", 90.0f); - - // Minimum acquisition time, in ms - minimumAcquisitionTime = configuration.getFloat("minAquisitionTime", 2.0f); - - // Trim to align left to right and right to left scans (in degrees) - trim = configuration.getFloat("trim", 0.0f); - - // Set the default minimal range to filter out - float minFilterRange = configuration.getFloat("minFilterRange", DEFAULT_FILTER_MIN_RANGE); - pointFilter = new MinRangePointFilter(minFilterRange); - - try { - lidar = new LidarLiteDevice(getBus(), getAddress()); - } catch (IOException e) { - throw new ConfigurationException(String.format( - "Failed to initialize lidar device. Make sure it is hooked up to bus: %d address: %xd", getBus(), getAddress()), e); - } - } - - @Override - public void onMessage(ScanRequest message) { - RoboReference servo = getPanServo(); - if (message.getRange() == 0 || message.getStep() == 0) { - scheduleFixPanScan(message, servo); - } else { - scheduleScan(message, servo); - } - } - - private void scheduleFixPanScan(ScanRequest message, RoboReference servo) { - servo.sendMessage(message.getStartAngle() / servoRange); - ScanFixedAngleJob fixedAngleJob = new ScanFixedAngleJob(message.getReceiver(), lidar, message.getStartAngle(), pointFilter); - // FIXME(Marcus/Sep 5, 2017): We should try to calculate this - we are - // now assuming that it will take little time to move the servo to the - // "new" position, however, the fact is that the new position will - // usually be the old position. - getContext().getScheduler().schedule(fixedAngleJob, 31, TimeUnit.MILLISECONDS); - getContext().getScheduler().schedule(fixedAngleJob, (long) (31 + minimumAcquisitionTime), TimeUnit.MILLISECONDS); - } - - private RoboReference getPanServo() { - RoboReference servo = getReference(pan); - return servo; - } - - private void scheduleScan(ScanRequest message, RoboReference servo) { - float currentInput = getCurrentInput(servo); - float midPoint = message.getStartAngle() + message.getRange() / 2; - boolean lowToHigh = false; - if (inputToAngle(currentInput) <= midPoint) { - lowToHigh = true; - } - float minimumServoMovementTime = message.getRange() / angularSpeed; - ScanJob job = new ScanJob(lowToHigh, minimumServoMovementTime, minimumAcquisitionTime, trim, message, servo, servoRange, lidar, - message.getReceiver(), pointFilter); - schedule(job); - } - - private float inputToAngle(float currentInput) { - return servoRange * currentInput; - } - - private void schedule(ScanJob job) { - long actualDelayMicros = job.delayMicros; - // One extra for first servo move. - for (int i = 0; i < job.numberOfScans + 1; i++) { - // FIXME(Marcus/Apr 4, 2017): Simplified - need to take angular - // speed of the servo into account. - getContext().getScheduler().schedule(job, actualDelayMicros, TimeUnit.MICROSECONDS); - actualDelayMicros += job.delayMicros; - } - } - - private float getCurrentInput(RoboReference servo) { - try { - return servo.getAttribute(PCA9685ServoUnit.ATTRIBUTE_SERVO_INPUT).get(); - } catch (InterruptedException | ExecutionException e) { - SimpleLoggingUtil.error(getClass(), "Could not read servo input!", e); - return 0; - } - } - - private RoboReference getReference(String unit) { - if (unit.equals("null")) { - return null; - } - return getContext().getReference(unit); - } + private static final Logger LOGGER = LoggerFactory.getLogger(LaserScanner.class); + /** + * Since the lidar lite will sometimes give some abnormal readings with very + * short range, we need to be able to filter out readings. Set this + * depending on your hardware and needs. + */ + private final static float DEFAULT_FILTER_MIN_RANGE = 0.08f; + private Predicate pointFilter; + private String pan; + private LidarLiteDevice lidar; + private float servoRange; + private float angularSpeed; + private float minimumAcquisitionTime; + private float trim; + + /** + * Filter for filtering out mis-reads. Anything closer than minRange will be + * filtered out. + */ + private static class MinRangePointFilter implements Predicate { + private final float minRange; + + private MinRangePointFilter(float minRange) { + this.minRange = minRange; + } + + @Override + public boolean test(Point2f t) { + return t.getRange() >= minRange; + } + } + + private final static class ScanJob implements Runnable { + private final AtomicInteger invokeCount = new AtomicInteger(0); + private final ScanResultImpl scanResult; + private final ScanRequest request; + private final RoboReference recipient; + private final RoboReference servo; + private final boolean lowToHigh; + private final int numberOfScans; + private long delayMicros; + private final float trim; + private final float servoRange; + private final LidarLiteDevice lidar; + private volatile float currentAngle; + private volatile boolean finished = false; + private final ScanEvent scanEvent; + + /** + * @param lowToHigh + * @param minimumServoMovementTime the minimum time for the servo to complete the movement + * over the range, in seconds + * @param minimumAcquisitionTime + * @param trim + * @param request + * @param servo + * @param servoRange + * @param lidar + * @param recipient + */ + public ScanJob(boolean lowToHigh, float minimumServoMovementTime, float minimumAcquisitionTime, float trim, ScanRequest request, + RoboReference servo, float servoRange, LidarLiteDevice lidar, RoboReference recipient, + Predicate pointFilter) { + this.lowToHigh = lowToHigh; + this.trim = trim; + this.request = request; + this.servo = servo; + this.servoRange = servoRange; + this.lidar = lidar; + this.recipient = recipient; + // one move, one first acquisition + this.numberOfScans = calculateNumberOfScans() + 1; + this.delayMicros = calculateDelay(minimumAcquisitionTime, minimumServoMovementTime); + this.currentAngle = lowToHigh ? request.getStartAngle() : request.getStartAngle() + request.getRange(); + this.scanResult = new ScanResultImpl(100, request.getStep(), pointFilter); + scanEvent = new ScanEvent(scanResult.getScanID(), getScanInfo()); + scanEvent.setScanLeftRight(lowToHigh); + JfrUtils.begin(scanEvent); + } + + @Override + public void run() { + int currentRun = invokeCount.incrementAndGet(); + if (currentRun == 1) { + // On first step, only move servo to start position + float normalizedServoTarget = getNormalizedAngle(); + servo.sendMessage(normalizedServoTarget); + return; + } else if (currentRun == 2) { + // On second, just start acquisition (no point to read yet) + startAcquisition(); + } else if (currentRun > numberOfScans) { + doScan(); + finish(); + } else { + doScan(); + } + // FIXME(Marcus/Mar 10, 2017): May want to synchronize this... + updateTargetAngle(); + } + + private void startAcquisition() { + try { + lidar.acquireRange(); + servo.sendMessage(getNormalizedAngle()); + } catch (IOException e) { + LOGGER.error("Could not read laser:{}", e.getMessage(), e); + } + } + + private float getNormalizedAngle() { + return this.currentAngle / this.servoRange; + } + + private void doScan() { + // Read previous acquisition + try { + float readDistance = lidar.readDistance(); + // Laser was actually shooting at the previous angle, before + // moving - recalculate for that angle + float lastAngle = currentAngle + (lowToHigh ? -request.getStep() - trim : request.getStep() + trim); + scanResult.addPoint(Point2f.fromPolar(readDistance, (float) Math.toRadians(lastAngle))); + servo.sendMessage(getNormalizedAngle()); + lidar.acquireRange(); + } catch (IOException e) { + LOGGER.error("Could not read laser:{}", e.getMessage(), e); + } + } + + private void finish() { + if (!finished) { + recipient.sendMessage(scanResult); + finished = true; + JfrUtils.end(scanEvent); + JfrUtils.commit(scanEvent); + } else { + LOGGER.warn("Tried to scan more laser points after being finished!"); + } + } + + private void updateTargetAngle() { + if (lowToHigh) { + currentAngle += request.getStep(); + } else { + currentAngle -= request.getStep(); + } + } + + private int calculateNumberOfScans() { + return Math.round(request.getRange() / request.getStep()); + } + + // FIXME(Marcus/Mar 10, 2017): Calculate the required delay later from + // physical model. + private long calculateDelay(float minimumAcquisitionTime, float minimumServoMovementTime) { + float delayPerStep = minimumServoMovementTime * 1000 / calculateNumberOfScans(); + // If we have a slow servo, we will need to wait for the servo to + // move. If we have a slow acquisition, we will need to wait for the + // laser before continuing + float actualDelay = Math.max(delayPerStep, minimumAcquisitionTime); + return Math.round(actualDelay * 1000.0d); + } + + private String getScanInfo() { + return String.format("start: %2.1f, end: %2.1f, step:%2.1f", request.getStartAngle(), + request.getStartAngle() + request.getRange(), request.getStep()); + } + } + + private final static class ScanFixedAngleJob implements Runnable { + private final RoboReference recipient; + private final LidarLiteDevice lidar; + private final float angle; + private final Predicate filter; + private volatile boolean firstRun = true; + + public ScanFixedAngleJob(RoboReference recipient, LidarLiteDevice lidar, float angle, Predicate filter) { + this.recipient = recipient; + this.lidar = lidar; + this.angle = angle; + this.filter = filter; + } + + @Override + public void run() { + try { + if (firstRun) { + firstRun = false; + lidar.acquireRange(); + } else { + ScanResultImpl scan = new ScanResultImpl(1, 0f, filter); + scan.addPoint(lidar.readDistance(), (float) Math.toRadians(angle)); + recipient.sendMessage(scan); + } + } catch (IOException e) { + LOGGER.error("Failed to read lidar:{}", e.getMessage(), e); + } + } + } + + public LaserScanner(RoboContext context, String id) { + super(ScanRequest.class, context, id); + } + + @Override + protected void onInitialization(Configuration configuration) throws ConfigurationException { + super.onInitialization(configuration); + pan = configuration.getString("servo", "laserscanner.servo"); + + // Using degrees for convenience + servoRange = (float) configuration.getFloat("servoRange", 45.0f); + + // Using angular degrees per second. + angularSpeed = configuration.getFloat("angularSpeed", 90.0f); + + // Minimum acquisition time, in ms + minimumAcquisitionTime = configuration.getFloat("minAquisitionTime", 2.0f); + + // Trim to align left to right and right to left scans (in degrees) + trim = configuration.getFloat("trim", 0.0f); + + // Set the default minimal range to filter out + float minFilterRange = configuration.getFloat("minFilterRange", DEFAULT_FILTER_MIN_RANGE); + pointFilter = new MinRangePointFilter(minFilterRange); + + try { + lidar = new LidarLiteDevice(getBus(), getAddress()); + } catch (IOException e) { + throw new ConfigurationException(String.format( + "Failed to initialize lidar device. Make sure it is hooked up to bus: %d address: %xd", getBus(), getAddress()), e); + } + } + + @Override + public void onMessage(ScanRequest message) { + RoboReference servo = getPanServo(); + if (message.getRange() == 0 || message.getStep() == 0) { + scheduleFixPanScan(message, servo); + } else { + scheduleScan(message, servo); + } + } + + private void scheduleFixPanScan(ScanRequest message, RoboReference servo) { + servo.sendMessage(message.getStartAngle() / servoRange); + ScanFixedAngleJob fixedAngleJob = new ScanFixedAngleJob(message.getReceiver(), lidar, message.getStartAngle(), pointFilter); + // FIXME(Marcus/Sep 5, 2017): We should try to calculate this - we are + // now assuming that it will take little time to move the servo to the + // "new" position, however, the fact is that the new position will + // usually be the old position. + getContext().getScheduler().schedule(fixedAngleJob, 31, TimeUnit.MILLISECONDS); + getContext().getScheduler().schedule(fixedAngleJob, (long) (31 + minimumAcquisitionTime), TimeUnit.MILLISECONDS); + } + + private RoboReference getPanServo() { + RoboReference servo = getReference(pan); + return servo; + } + + private void scheduleScan(ScanRequest message, RoboReference servo) { + float currentInput = getCurrentInput(servo); + float midPoint = message.getStartAngle() + message.getRange() / 2; + boolean lowToHigh = false; + if (inputToAngle(currentInput) <= midPoint) { + lowToHigh = true; + } + float minimumServoMovementTime = message.getRange() / angularSpeed; + ScanJob job = new ScanJob(lowToHigh, minimumServoMovementTime, minimumAcquisitionTime, trim, message, servo, servoRange, lidar, + message.getReceiver(), pointFilter); + schedule(job); + } + + private float inputToAngle(float currentInput) { + return servoRange * currentInput; + } + + private void schedule(ScanJob job) { + long actualDelayMicros = job.delayMicros; + // One extra for first servo move. + for (int i = 0; i < job.numberOfScans + 1; i++) { + // FIXME(Marcus/Apr 4, 2017): Simplified - need to take angular + // speed of the servo into account. + getContext().getScheduler().schedule(job, actualDelayMicros, TimeUnit.MICROSECONDS); + actualDelayMicros += job.delayMicros; + } + } + + private float getCurrentInput(RoboReference servo) { + try { + return servo.getAttribute(PCA9685ServoUnit.ATTRIBUTE_SERVO_INPUT).get(); + } catch (InterruptedException | ExecutionException e) { + LOGGER.error("Could not read servo input:{}", e.getMessage(), e); + return 0; + } + } + + private RoboReference getReference(String unit) { + if (unit.equals("null")) { + return null; + } + return getContext().getReference(unit); + } } diff --git a/robo4j-units-rpi/src/main/java/com/robo4j/units/rpi/pad/LF710PadUnit.java b/robo4j-units-rpi/src/main/java/com/robo4j/units/rpi/pad/LF710PadUnit.java index 08620608..2a51e070 100644 --- a/robo4j-units-rpi/src/main/java/com/robo4j/units/rpi/pad/LF710PadUnit.java +++ b/robo4j-units-rpi/src/main/java/com/robo4j/units/rpi/pad/LF710PadUnit.java @@ -27,7 +27,8 @@ import com.robo4j.hw.rpi.pad.LF710Pad; import com.robo4j.hw.rpi.pad.PadInputResponseListener; import com.robo4j.hw.rpi.pad.RoboControlPad; -import com.robo4j.logging.SimpleLoggingUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.nio.file.Paths; @@ -37,8 +38,8 @@ * @author Marcus Hirt (@hirt) * @author Miro Wengner (@miragemiko) */ -public class LF710PadUnit extends RoboUnit{ - +public class LF710PadUnit extends RoboUnit { + private static final Logger LOGGER = LoggerFactory.getLogger(LF710PadUnit.class); private RoboControlPad pad; private LF710ButtonObserver observer; private PadInputResponseListener listener; @@ -54,7 +55,7 @@ protected void onInitialization(Configuration configuration) throws Configuratio var input = configuration.getString("input", null); target = configuration.getString("target", null); - if(input == null){ + if (input == null) { throw ConfigurationException.createMissingConfigNameException("input"); } if (target == null) { @@ -71,9 +72,9 @@ public void start() { pad.connect(); observer = new LF710ButtonObserver(pad); listener = (LF710Message response) -> { - if(getState() == LifecycleState.STARTED){ - if(targetRef == null){ - SimpleLoggingUtil.info(getClass(), "PAD pressed: " + response); + if (getState() == LifecycleState.STARTED) { + if (targetRef == null) { + LOGGER.warn("PAD pressed:{}", response); } else { targetRef.sendMessage(response); } diff --git a/robo4j-units-rpi/src/main/java/com/robo4j/units/rpi/pwm/MC33926HBridgeUnit.java b/robo4j-units-rpi/src/main/java/com/robo4j/units/rpi/pwm/MC33926HBridgeUnit.java index 9ed0a7cf..1b4db626 100644 --- a/robo4j-units-rpi/src/main/java/com/robo4j/units/rpi/pwm/MC33926HBridgeUnit.java +++ b/robo4j-units-rpi/src/main/java/com/robo4j/units/rpi/pwm/MC33926HBridgeUnit.java @@ -16,106 +16,106 @@ */ package com.robo4j.units.rpi.pwm; -import java.io.IOException; - import com.robo4j.ConfigurationException; import com.robo4j.RoboContext; import com.robo4j.configuration.Configuration; import com.robo4j.hw.rpi.i2c.pwm.HBridgeMC33926Device; import com.robo4j.hw.rpi.i2c.pwm.PWMPCA9685Device; import com.robo4j.hw.rpi.utils.GpioPin; -import com.robo4j.logging.SimpleLoggingUtil; import com.robo4j.units.rpi.I2CRegistry; import com.robo4j.units.rpi.I2CRoboUnit; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; /** * Motor unit associated with the {@link HBridgeMC33926Device} driver. - * + * * @author Marcus Hirt (@hirt) * @author Miroslav Wengner (@miragemiko) */ public class MC33926HBridgeUnit extends I2CRoboUnit { - /** - * The key used to configure which channel to use. - */ - public static final String CONFIGURATION_KEY_CHANNEL = "channel"; - - /** - * The key used to configure the symbolic name. - */ - public static final String CONFIGURATION_KEY_NAME = "name"; - - /** - * The key used to configure the gpio RaspiPin to use for "IN1". Use the - * name, such as GPIO_01. - */ - public static final String CONFIGURATION_KEY_GPIO_IN_1 = "in1"; - - /** - * The key used to configure the gpio pin to use for "IN2". Use the name, - * such as GPIO_02. - */ - public static final String CONFIGURATION_KEY_GPIO_IN_2 = "in2"; - - /** - * The key used to invert the direction of the motor. The value if the key - * is not present defaults to false. - */ - public static final String CONFIGURATION_KEY_INVERT = "invert"; - - /** - * The engine. - */ - private HBridgeMC33926Device engine; - - public MC33926HBridgeUnit(RoboContext context, String id) { - super(Float.class, context, id); - } - - /** - * - * @param configuration - * unit configuration - * @throws ConfigurationException - * exception - */ - @Override - protected void onInitialization(Configuration configuration) throws ConfigurationException { - super.onInitialization(configuration); - - PWMPCA9685Device pcaDevice = I2CRegistry.createAndRegisterIfAbsent(getBus(), getAddress(), - () -> PWMPCA9685Device.createDevice(getBus(), getAddress())); - - int channel = configuration.getInteger(CONFIGURATION_KEY_CHANNEL, -1); - if (channel == -1) { - throw ConfigurationException.createMissingConfigNameException(CONFIGURATION_KEY_CHANNEL); - } - - String in1Name = configuration.getString(CONFIGURATION_KEY_GPIO_IN_1, null); - if (in1Name == null) { - throw ConfigurationException.createMissingConfigNameException(CONFIGURATION_KEY_GPIO_IN_1); - } - var gpioIn1 = GpioPin.getByName(in1Name); - - String in2Name = configuration.getString(CONFIGURATION_KEY_GPIO_IN_1, null); - if (in2Name == null) { - throw ConfigurationException.createMissingConfigNameException(CONFIGURATION_KEY_GPIO_IN_1); - } - var gpioIn2 = GpioPin.getByName(in2Name);; - - boolean invert = configuration.getBoolean(CONFIGURATION_KEY_INVERT, false); - - // TODO: improve configuraiton - engine = new HBridgeMC33926Device(configuration.getString(CONFIGURATION_KEY_NAME, "MC33926"), pcaDevice.getChannel(channel), gpioIn1, - gpioIn2, invert); - } - - @Override - public void onMessage(Float message) { - try { - engine.setSpeed(message); - } catch (IOException e) { - SimpleLoggingUtil.error(getClass(), "Failed to set motor speed to " + message, e); - } - } + /** + * The key used to configure which channel to use. + */ + public static final String CONFIGURATION_KEY_CHANNEL = "channel"; + + /** + * The key used to configure the symbolic name. + */ + public static final String CONFIGURATION_KEY_NAME = "name"; + + /** + * The key used to configure the gpio RaspiPin to use for "IN1". Use the + * name, such as GPIO_01. + */ + public static final String CONFIGURATION_KEY_GPIO_IN_1 = "in1"; + + /** + * The key used to configure the gpio pin to use for "IN2". Use the name, + * such as GPIO_02. + */ + public static final String CONFIGURATION_KEY_GPIO_IN_2 = "in2"; + + /** + * The key used to invert the direction of the motor. The value if the key + * is not present defaults to false. + */ + public static final String CONFIGURATION_KEY_INVERT = "invert"; + + private static final Logger LOGGER = LoggerFactory.getLogger(MC33926HBridgeUnit.class); + /** + * The engine. + */ + private HBridgeMC33926Device engine; + + public MC33926HBridgeUnit(RoboContext context, String id) { + super(Float.class, context, id); + } + + /** + * @param configuration unit configuration + * @throws ConfigurationException exception + */ + @Override + protected void onInitialization(Configuration configuration) throws ConfigurationException { + super.onInitialization(configuration); + + PWMPCA9685Device pcaDevice = I2CRegistry.createAndRegisterIfAbsent(getBus(), getAddress(), + () -> PWMPCA9685Device.createDevice(getBus(), getAddress())); + + int channel = configuration.getInteger(CONFIGURATION_KEY_CHANNEL, -1); + if (channel == -1) { + throw ConfigurationException.createMissingConfigNameException(CONFIGURATION_KEY_CHANNEL); + } + + String in1Name = configuration.getString(CONFIGURATION_KEY_GPIO_IN_1, null); + if (in1Name == null) { + throw ConfigurationException.createMissingConfigNameException(CONFIGURATION_KEY_GPIO_IN_1); + } + var gpioIn1 = GpioPin.getByName(in1Name); + + String in2Name = configuration.getString(CONFIGURATION_KEY_GPIO_IN_1, null); + if (in2Name == null) { + throw ConfigurationException.createMissingConfigNameException(CONFIGURATION_KEY_GPIO_IN_1); + } + var gpioIn2 = GpioPin.getByName(in2Name); + ; + + boolean invert = configuration.getBoolean(CONFIGURATION_KEY_INVERT, false); + + // TODO: improve configuraiton + engine = new HBridgeMC33926Device(configuration.getString(CONFIGURATION_KEY_NAME, "MC33926"), pcaDevice.getChannel(channel), gpioIn1, + gpioIn2, invert); + } + + @Override + public void onMessage(Float message) { + try { + engine.setSpeed(message); + } catch (IOException e) { + LOGGER.error("Failed to set motor speed to:{}", message, e); + } + } } diff --git a/robo4j-units-rpi/src/main/java/com/robo4j/units/rpi/pwm/PCA9685ServoUnit.java b/robo4j-units-rpi/src/main/java/com/robo4j/units/rpi/pwm/PCA9685ServoUnit.java index d41b7e58..dc9932a5 100644 --- a/robo4j-units-rpi/src/main/java/com/robo4j/units/rpi/pwm/PCA9685ServoUnit.java +++ b/robo4j-units-rpi/src/main/java/com/robo4j/units/rpi/pwm/PCA9685ServoUnit.java @@ -16,10 +16,6 @@ */ package com.robo4j.units.rpi.pwm; -import java.io.IOException; -import java.util.Collection; -import java.util.Collections; - import com.robo4j.AttributeDescriptor; import com.robo4j.ConfigurationException; import com.robo4j.DefaultAttributeDescriptor; @@ -27,129 +23,127 @@ import com.robo4j.configuration.Configuration; import com.robo4j.hw.rpi.i2c.pwm.PCA9685Servo; import com.robo4j.hw.rpi.i2c.pwm.PWMPCA9685Device; -import com.robo4j.logging.SimpleLoggingUtil; import com.robo4j.units.rpi.I2CRegistry; import com.robo4j.units.rpi.I2CRoboUnit; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.util.Collection; +import java.util.Collections; /** * Servo unit associated with the PCA9685 PWM driver. - * + * * @author Marcus Hirt (@hirt) * @author Miroslav Wengner (@miragemiko) */ public class PCA9685ServoUnit extends I2CRoboUnit { - /** - * The key used to configure which channel to use. - */ - public static final String CONFIGURATION_KEY_CHANNEL = "channel"; - /** - * The key used to configure how much trim to use. - */ - public static final String CONFIGURATION_KEY_TRIM = "trim"; - /** - * The key used to configure if the servo should be inverted. - */ - public static final String CONFIGURATION_KEY_INVERTED = "inverted"; - /** - * The key used to configure the dual rate to use. - */ - public static final String CONFIGURATION_KEY_DUAL_RATE = "dualRate"; - /** - * The key used to configure the expo to use. - */ - public static final String CONFIGURATION_KEY_EXPO = "expo"; - /** - * The setting to reset to on shutdown. If this is not set, nothing will - * happen on shutdown. - */ - public static final String CONFIGURATION_KEY_SHUTDOWN_VALUE = "shutdownValue"; - - public static final AttributeDescriptor ATTRIBUTE_SERVO_INPUT = DefaultAttributeDescriptor.create(Float.class, "input"); - public static final Collection> KNOWN_ATTRIBUTES = Collections.singleton(ATTRIBUTE_SERVO_INPUT); + /** + * The key used to configure which channel to use. + */ + public static final String CONFIGURATION_KEY_CHANNEL = "channel"; + /** + * The key used to configure how much trim to use. + */ + public static final String CONFIGURATION_KEY_TRIM = "trim"; + /** + * The key used to configure if the servo should be inverted. + */ + public static final String CONFIGURATION_KEY_INVERTED = "inverted"; + /** + * The key used to configure the dual rate to use. + */ + public static final String CONFIGURATION_KEY_DUAL_RATE = "dualRate"; + /** + * The key used to configure the expo to use. + */ + public static final String CONFIGURATION_KEY_EXPO = "expo"; + /** + * The setting to reset to on shutdown. If this is not set, nothing will + * happen on shutdown. + */ + public static final String CONFIGURATION_KEY_SHUTDOWN_VALUE = "shutdownValue"; - private PCA9685Servo servo; - private Integer channel; - private Float shutdownValue; + public static final AttributeDescriptor ATTRIBUTE_SERVO_INPUT = DefaultAttributeDescriptor.create(Float.class, "input"); + public static final Collection> KNOWN_ATTRIBUTES = Collections.singleton(ATTRIBUTE_SERVO_INPUT); + private static final Logger LOGGER = LoggerFactory.getLogger(PCA9685ServoUnit.class); + private PCA9685Servo servo; + private Integer channel; + private Float shutdownValue; - /** - * Constructor. - * - * @param context - * the RoboContext in which to define the unit - * @param id - * the id of the unit - */ - public PCA9685ServoUnit(RoboContext context, String id) { - super(Float.class, context, id); - } + /** + * Constructor. + * + * @param context the RoboContext in which to define the unit + * @param id the id of the unit + */ + public PCA9685ServoUnit(RoboContext context, String id) { + super(Float.class, context, id); + } - /** - * - * @param configuration - * unit configuration - * @throws ConfigurationException - * exception - */ - @Override - protected void onInitialization(Configuration configuration) throws ConfigurationException { - super.onInitialization(configuration); - PWMPCA9685Device pcaDevice = I2CRegistry.createAndRegisterIfAbsent(getBus(), getAddress(), - () -> PWMPCA9685Device.createDevice(getBus(), getAddress())); - channel = configuration.getInteger(CONFIGURATION_KEY_CHANNEL, -1); - if (channel == -1) { - throw ConfigurationException.createMissingConfigNameException(CONFIGURATION_KEY_CHANNEL); - } - servo = new PCA9685Servo(pcaDevice.getChannel(channel)); - servo.setTrim(configuration.getFloat(CONFIGURATION_KEY_TRIM, 0f)); - servo.setInverted(configuration.getBoolean(CONFIGURATION_KEY_INVERTED, false)); - servo.setDualRate(configuration.getFloat(CONFIGURATION_KEY_DUAL_RATE, 1.0f)); - servo.setExpo(configuration.getFloat(CONFIGURATION_KEY_EXPO, 0.0f)); - shutdownValue = configuration.getFloat(CONFIGURATION_KEY_SHUTDOWN_VALUE, null); - } + /** + * @param configuration unit configuration + * @throws ConfigurationException exception + */ + @Override + protected void onInitialization(Configuration configuration) throws ConfigurationException { + super.onInitialization(configuration); + PWMPCA9685Device pcaDevice = I2CRegistry.createAndRegisterIfAbsent(getBus(), getAddress(), + () -> PWMPCA9685Device.createDevice(getBus(), getAddress())); + channel = configuration.getInteger(CONFIGURATION_KEY_CHANNEL, -1); + if (channel == -1) { + throw ConfigurationException.createMissingConfigNameException(CONFIGURATION_KEY_CHANNEL); + } + servo = new PCA9685Servo(pcaDevice.getChannel(channel)); + servo.setTrim(configuration.getFloat(CONFIGURATION_KEY_TRIM, 0f)); + servo.setInverted(configuration.getBoolean(CONFIGURATION_KEY_INVERTED, false)); + servo.setDualRate(configuration.getFloat(CONFIGURATION_KEY_DUAL_RATE, 1.0f)); + servo.setExpo(configuration.getFloat(CONFIGURATION_KEY_EXPO, 0.0f)); + shutdownValue = configuration.getFloat(CONFIGURATION_KEY_SHUTDOWN_VALUE, null); + } - /** - * -1 to 1 - * - * @param message - * the message received by this unit. - * - */ - @Override - public void onMessage(Float message) { - try { - servo.setInput(message); - } catch (IOException e) { - SimpleLoggingUtil.error(getClass(), "Could not set servo input", e); - } - } + /** + * -1 to 1 + * + * @param message the message received by this unit. + */ + @Override + public void onMessage(Float message) { + try { + servo.setInput(message); + } catch (IOException e) { + LOGGER.error("Could not set servo input:{}", e.getMessage(), e); + } + } - @SuppressWarnings("unchecked") - @Override - protected R onGetAttribute(AttributeDescriptor descriptor) { - if (descriptor.getAttributeName().equals("input") && descriptor.getAttributeType() == Float.class) { - try { - return (R) Float.valueOf(servo.getInput()); - } catch (IOException e) { - SimpleLoggingUtil.error(getClass(), "Failed to read servo input", e); - } - } - return super.onGetAttribute(descriptor); - } + @SuppressWarnings("unchecked") + @Override + protected R onGetAttribute(AttributeDescriptor descriptor) { + if (descriptor.getAttributeName().equals("input") && descriptor.getAttributeType() == Float.class) { + try { + return (R) Float.valueOf(servo.getInput()); + } catch (IOException e) { + LOGGER.error("Failed to read servo input:{}", e.getMessage(), e); + } + } + return super.onGetAttribute(descriptor); + } - @Override - public Collection> getKnownAttributes() { - return KNOWN_ATTRIBUTES; - } + @Override + public Collection> getKnownAttributes() { + return KNOWN_ATTRIBUTES; + } - @Override - public void shutdown() { - if (shutdownValue != null) { - try { - servo.setInput(shutdownValue.floatValue()); - } catch (IOException e) { - SimpleLoggingUtil.debug(PCA9685ServoUnit.class, "Failed to set the shutdown value!", e); - } - } - super.shutdown(); - } + @Override + public void shutdown() { + if (shutdownValue != null) { + try { + servo.setInput(shutdownValue.floatValue()); + } catch (IOException e) { + LOGGER.error("Failed to set the shutdown value:{}", e.getMessage(), e); + } + } + super.shutdown(); + } } diff --git a/robo4j-units-rpi/src/main/java/com/robo4j/units/rpi/roboclaw/RoboClawRCTankUnit.java b/robo4j-units-rpi/src/main/java/com/robo4j/units/rpi/roboclaw/RoboClawRCTankUnit.java index a64b5cb3..e2ec62c6 100644 --- a/robo4j-units-rpi/src/main/java/com/robo4j/units/rpi/roboclaw/RoboClawRCTankUnit.java +++ b/robo4j-units-rpi/src/main/java/com/robo4j/units/rpi/roboclaw/RoboClawRCTankUnit.java @@ -16,97 +16,97 @@ */ package com.robo4j.units.rpi.roboclaw; -import java.io.IOException; - import com.robo4j.ConfigurationException; import com.robo4j.RoboContext; import com.robo4j.configuration.Configuration; import com.robo4j.hw.rpi.i2c.pwm.PCA9685Servo; import com.robo4j.hw.rpi.i2c.pwm.PWMPCA9685Device; import com.robo4j.hw.rpi.pwm.roboclaw.RoboClawRCTank; -import com.robo4j.logging.SimpleLoggingUtil; import com.robo4j.units.rpi.I2CRegistry; import com.robo4j.units.rpi.I2CRoboUnit; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; /** * Configurable unit for a RoboClaw configured with two engines, controlled * using PWM signals from a PCA9685. - * + * * @author Marcus Hirt (@hirt) * @author Miroslav Wengner (@miragemiko) */ public class RoboClawRCTankUnit extends I2CRoboUnit { - /** - * The key used to configure which channel to use for the left engine. - */ - public static String CONFIGURATION_KEY_LEFT_CHANNEL = "leftChannel"; + /** + * The key used to configure which channel to use for the left engine. + */ + public static String CONFIGURATION_KEY_LEFT_CHANNEL = "leftChannel"; - /** - * The key used to configure if the channel for the left engine needs to be - * inverted. - */ - public static String CONFIGURATION_KEY_LEFT_INVERTED = "leftInverted"; + /** + * The key used to configure if the channel for the left engine needs to be + * inverted. + */ + public static String CONFIGURATION_KEY_LEFT_INVERTED = "leftInverted"; - /** - * The key used to configure which channel to use for the right engine. - */ - public static String CONFIGURATION_KEY_RIGHT_CHANNEL = "rightChannel"; + /** + * The key used to configure which channel to use for the right engine. + */ + public static String CONFIGURATION_KEY_RIGHT_CHANNEL = "rightChannel"; - /** - * The key used to configure if the channel for the left engine needs to be - * inverted. - */ - public static String CONFIGURATION_KEY_RIGHT_INVERTED = "rightInverted"; + /** + * The key used to configure if the channel for the left engine needs to be + * inverted. + */ + public static String CONFIGURATION_KEY_RIGHT_INVERTED = "rightInverted"; - private RoboClawRCTank tank; + private static final Logger LOGGER = LoggerFactory.getLogger(RoboClawRCTankUnit.class); + private RoboClawRCTank tank; - /** - * Constructor. - * - * @param context - * the RoboContext in which to define the unit - * @param id - * the id of the unit - */ - public RoboClawRCTankUnit(RoboContext context, String id) { - super(MotionEvent.class, context, id); - } + /** + * Constructor. + * + * @param context the RoboContext in which to define the unit + * @param id the id of the unit + */ + public RoboClawRCTankUnit(RoboContext context, String id) { + super(MotionEvent.class, context, id); + } - @Override - protected void onInitialization(Configuration configuration) throws ConfigurationException { - super.onInitialization(configuration); - PWMPCA9685Device pcaDevice = I2CRegistry.createAndRegisterIfAbsent(getBus(), getAddress(), - () -> PWMPCA9685Device.createDevice(getBus(), getAddress())); - int leftChannel = configuration.getInteger(CONFIGURATION_KEY_LEFT_CHANNEL, -1); - if (leftChannel == -1) { - throw ConfigurationException.createMissingConfigNameException(CONFIGURATION_KEY_LEFT_CHANNEL); - } - int rightChannel = configuration.getInteger(CONFIGURATION_KEY_RIGHT_CHANNEL, -1); - if (rightChannel == -1) { - throw ConfigurationException.createMissingConfigNameException(CONFIGURATION_KEY_RIGHT_CHANNEL); - } - boolean leftInvert = configuration.getBoolean(CONFIGURATION_KEY_LEFT_INVERTED, false); - boolean rightInvert = configuration.getBoolean(CONFIGURATION_KEY_RIGHT_INVERTED, false); + @Override + protected void onInitialization(Configuration configuration) throws ConfigurationException { + super.onInitialization(configuration); + PWMPCA9685Device pcaDevice = I2CRegistry.createAndRegisterIfAbsent(getBus(), getAddress(), + () -> PWMPCA9685Device.createDevice(getBus(), getAddress())); + int leftChannel = configuration.getInteger(CONFIGURATION_KEY_LEFT_CHANNEL, -1); + if (leftChannel == -1) { + throw ConfigurationException.createMissingConfigNameException(CONFIGURATION_KEY_LEFT_CHANNEL); + } + int rightChannel = configuration.getInteger(CONFIGURATION_KEY_RIGHT_CHANNEL, -1); + if (rightChannel == -1) { + throw ConfigurationException.createMissingConfigNameException(CONFIGURATION_KEY_RIGHT_CHANNEL); + } + boolean leftInvert = configuration.getBoolean(CONFIGURATION_KEY_LEFT_INVERTED, false); + boolean rightInvert = configuration.getBoolean(CONFIGURATION_KEY_RIGHT_INVERTED, false); - PCA9685Servo leftServo = new PCA9685Servo(pcaDevice.getChannel(leftChannel)); - leftServo.setInverted(leftInvert); - PCA9685Servo rightServo = new PCA9685Servo(pcaDevice.getChannel(rightChannel)); - rightServo.setInverted(rightInvert); - try { - tank = new RoboClawRCTank(leftServo, rightServo); - } catch (IOException e) { - throw new ConfigurationException("Could not initiate device!", e); - } - } + PCA9685Servo leftServo = new PCA9685Servo(pcaDevice.getChannel(leftChannel)); + leftServo.setInverted(leftInvert); + PCA9685Servo rightServo = new PCA9685Servo(pcaDevice.getChannel(rightChannel)); + rightServo.setInverted(rightInvert); + try { + tank = new RoboClawRCTank(leftServo, rightServo); + } catch (IOException e) { + throw new ConfigurationException("Could not initiate device!", e); + } + } - @Override - public void onMessage(MotionEvent message) { - super.onMessage(message); - try { - tank.setDirection(message.getDirection()); - tank.setSpeed(message.getSpeed()); - } catch (IOException e) { - SimpleLoggingUtil.error(getClass(), "Could not set speed and/or direction to " + message, e); - } - } + @Override + public void onMessage(MotionEvent message) { + super.onMessage(message); + try { + tank.setDirection(message.getDirection()); + tank.setSpeed(message.getSpeed()); + } catch (IOException e) { + LOGGER.error("Could not set speed and/or direction to:{}", message, e); + } + } }