From 32a9c2ba1de3557eb41baf73c93cb49df87abffd Mon Sep 17 00:00:00 2001 From: cameron Date: Fri, 25 Sep 2026 13:39:10 -0700 Subject: [PATCH 1/2] Add CanBroker.setFdFrames/isFdFrames, mirroring setLoopback/isLoopback --- .../tel/schich/javacan/util/CanBroker.java | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/epoll/src/main/java/tel/schich/javacan/util/CanBroker.java b/epoll/src/main/java/tel/schich/javacan/util/CanBroker.java index f970099..c3a79fe 100644 --- a/epoll/src/main/java/tel/schich/javacan/util/CanBroker.java +++ b/epoll/src/main/java/tel/schich/javacan/util/CanBroker.java @@ -48,6 +48,7 @@ import tel.schich.javacan.select.SelectorRegistration; import static java.time.Duration.ofMinutes; +import static tel.schich.javacan.CanSocketOptions.FD_FRAMES; import static tel.schich.javacan.CanSocketOptions.FILTER; import static tel.schich.javacan.CanSocketOptions.LOOPBACK; @@ -73,6 +74,7 @@ public class CanBroker extends EventLoop { private final Object filterLock = new Object(); private volatile boolean loopback = true; + private volatile boolean fdFrames = false; public CanBroker(ThreadFactory threadFactory, IOSelector selector) { this(threadFactory, selector, DEFAULT_TIMEOUT); @@ -134,6 +136,26 @@ public boolean isLoopback() { return loopback; } + /** + * Sets whether all known devices accept and send CAN FD frames. + * + * @param enable whether to enable CAN FD frames + * @throws IOException if the native call fails + */ + public synchronized void setFdFrames(boolean enable) throws IOException { + this.fdFrames = enable; + this.updateOption(FD_FRAMES, enable); + } + + /** + * Checks if the devices of this broker accept and send CAN FD frames. + * + * @return true if the devices accept and send CAN FD frames + */ + public boolean isFdFrames() { + return fdFrames; + } + /** * Adds a filter that will be added to all underlying channels. * @@ -228,6 +250,7 @@ public void addDevice(NetworkDevice device, FrameHandler handler) throws IOExcep ch.configureBlocking(false); ch.setOption(FILTER, filterArray); ch.setOption(LOOPBACK, loopback); + ch.setOption(FD_FRAMES, fdFrames); register(ch, EnumSet.of(SelectorRegistration.Operation.READ)); this.handlerMap.put(ch, handler); this.channelMap.put(device, ch); From ba8f25ec07c3961ef302d5c17e53ccd5270a730e Mon Sep 17 00:00:00 2001 From: cameron Date: Mon, 28 Sep 2026 09:41:51 -0700 Subject: [PATCH 2/2] Add CanBroker tests for FD, internal and external. --- .../javacan/test/util/CanBrokerTest.java | 55 +++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/epoll/src/test/java/tel/schich/javacan/test/util/CanBrokerTest.java b/epoll/src/test/java/tel/schich/javacan/test/util/CanBrokerTest.java index f88498c..6dfa22c 100644 --- a/epoll/src/test/java/tel/schich/javacan/test/util/CanBrokerTest.java +++ b/epoll/src/test/java/tel/schich/javacan/test/util/CanBrokerTest.java @@ -96,4 +96,59 @@ void testExternal() throws Exception { assertNotNull(actual, "CAN frame should have been captured!"); assertEquals(expected, actual, "What goes in should come out!"); } + + @Test + void testFdFrames() throws Exception { + final int id = 0x7E1; + byte[] data = {0x00, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x22, 0x22, 0x22, 0x22}; + CanFrame expected = CanFrame.create(id, CanFrame.FD_FLAG_FD_FRAME, data); + CanFilter filter = new CanFilter(id); + + CanBroker brokerA = new CanBroker(FACTORY, EPollSelector.open()); + CanBroker brokerB = new CanBroker(FACTORY, EPollSelector.open()); + + brokerA.setFdFrames(true); + brokerB.setFdFrames(true); + brokerA.addFilter(filter); + brokerB.addFilter(filter); + + CompletableFuture f = new CompletableFuture<>(); + brokerA.addDevice(CanTestHelper.CAN_INTERFACE, (dev, frame) -> { + f.complete(frame); + try { + brokerA.removeDevice(CanTestHelper.CAN_INTERFACE); + } catch (IOException e) { + fail("Removing the device from brokerA should not fail: " + e.getLocalizedMessage()); + } + }); + + brokerB.addDevice(CanTestHelper.CAN_INTERFACE, (d, frame) -> LOGGER.debug(String.valueOf(frame))); + brokerB.send(expected); + + CanFrame actual = f.get(2, SECONDS); + assertNotNull(actual, "CAN FD frame should have been captured!"); + assertTrue(actual.isFDFrame(), "Captured frame should be an FD frame"); + assertEquals(expected, actual, "What goes in should come out!"); + } + + @Test + void testExternalFdFrame() throws Exception { + final int id = 0x7E2; + byte[] data = {0x00, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x22, 0x22, 0x22, 0x22}; + CanFrame expected = CanFrame.create(id, CanFrame.FD_FLAG_FD_FRAME, data); + CompletableFuture f = new CompletableFuture<>(); + + CanBroker can = new CanBroker(FACTORY, EPollSelector.open()); + can.setFdFrames(true); + can.addFilter(new CanFilter(id)); + can.addDevice(CanTestHelper.CAN_INTERFACE, (ch, frame) -> f.complete(frame)); + + CanTestHelper.sendFrameViaUtils(CanTestHelper.CAN_INTERFACE, expected); + + CanFrame actual = f.get(2, SECONDS); + + assertNotNull(actual, "CAN FD frame should have been captured!"); + assertTrue(actual.isFDFrame(), "Captured frame should be an FD frame"); + assertEquals(expected, actual, "What goes in should come out!"); + } }