From 7b125ebc65f769d763631075f0289238c0f224ca Mon Sep 17 00:00:00 2001 From: Matthew Buckton Date: Fri, 7 Aug 2026 11:17:36 +1000 Subject: [PATCH 1/2] Populate drone status text from MAVLink --- .../mavlink/listener/StatusTextListener.java | 33 ++++++++++++------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/src/main/java/io/mapsmessaging/state/mavlink/listener/StatusTextListener.java b/src/main/java/io/mapsmessaging/state/mavlink/listener/StatusTextListener.java index d46def701..3bfd9e472 100644 --- a/src/main/java/io/mapsmessaging/state/mavlink/listener/StatusTextListener.java +++ b/src/main/java/io/mapsmessaging/state/mavlink/listener/StatusTextListener.java @@ -19,34 +19,45 @@ package io.mapsmessaging.state.mavlink.listener; +import static io.mapsmessaging.state.mavlink.packet.MavlinkMessageIds.STATUSTEXT; + import io.mapsmessaging.state.drone.core.TwinManager; import io.mapsmessaging.state.drone.core.TwinUpdateContext; +import io.mapsmessaging.state.drone.drone.DroneTwin; import io.mapsmessaging.state.mavlink.packet.MavlinkPacket; import io.mapsmessaging.state.mavlink.packet.StatusTextPacket; +import java.time.Instant; -import static io.mapsmessaging.state.mavlink.packet.MavlinkMessageIds.STATUSTEXT; - -/** - * Listener for STATUSTEXT. - */ +/** Listener for STATUSTEXT. */ public class StatusTextListener implements Listener { public static final int LISTENER_ID = STATUSTEXT; + private final TwinManager twinManager; public StatusTextListener(TwinManager twinManager) { - + this.twinManager = twinManager; } @Override public void handle(String twinId, MavlinkPacket pkt, TwinUpdateContext context) { - - if (!(pkt instanceof StatusTextPacket packet)) { + if (!(pkt instanceof StatusTextPacket packet) || !packet.isValid()) { return; } - if (packet.isValid() && packet.getText() != null) { - // log it + String text = packet.getText(); + if (text == null || text.isBlank()) { + return; } + + Instant now = context != null && context.getReceivedTime() != null ? context.getReceivedTime() : Instant.now(); + twinManager.updateTwin( + twinId, + twin -> { + DroneTwin droneTwin = (DroneTwin) twin; + droneTwin.setLastStatusText(text.trim()); + droneTwin.setOperationalUpdatedAt(now); + }, + context); } -} \ No newline at end of file +} From da4b4a873f5c81123e7fa13bab807aab7b7fdf44 Mon Sep 17 00:00:00 2001 From: Matthew Buckton Date: Fri, 7 Aug 2026 11:17:50 +1000 Subject: [PATCH 2/2] Test MAVLink status text twin updates --- .../listener/StatusTextListenerTest.java | 65 +++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 src/test/java/io/mapsmessaging/state/mavlink/listener/StatusTextListenerTest.java diff --git a/src/test/java/io/mapsmessaging/state/mavlink/listener/StatusTextListenerTest.java b/src/test/java/io/mapsmessaging/state/mavlink/listener/StatusTextListenerTest.java new file mode 100644 index 000000000..78cdc7391 --- /dev/null +++ b/src/test/java/io/mapsmessaging/state/mavlink/listener/StatusTextListenerTest.java @@ -0,0 +1,65 @@ +/* + * + * Copyright [ 2020 - 2024 ] Matthew Buckton + * Copyright [ 2024 - 2026 ] MapsMessaging B.V. + * + * Licensed under the Apache License, Version 2.0 with the Commons Clause + * (the "License"); you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * https://commonsclause.com/ + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.mapsmessaging.state.mavlink.listener; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import io.mapsmessaging.state.drone.core.TwinManager; +import io.mapsmessaging.state.drone.drone.DroneTwin; +import io.mapsmessaging.state.mavlink.packet.StatusTextPacket; +import org.junit.jupiter.api.Test; + +class StatusTextListenerTest { + + @Test + void handle_updatesDroneTwinWithLatestStatusText() { + TwinManager twinManager = new TwinManager(); + DroneTwin droneTwin = new DroneTwin("test-drone"); + twinManager.registerTwin(droneTwin, null); + + StatusTextPacket packet = mock(StatusTextPacket.class); + when(packet.isValid()).thenReturn(true); + when(packet.getText()).thenReturn(" Ready to fly "); + + new StatusTextListener(twinManager).handle(droneTwin.getTwinId(), packet, null); + + assertEquals("Ready to fly", droneTwin.getLastStatusText()); + assertNotNull(droneTwin.getOperationalUpdatedAt()); + } + + @Test + void handle_ignoresBlankStatusText() { + TwinManager twinManager = new TwinManager(); + DroneTwin droneTwin = new DroneTwin("test-drone"); + droneTwin.setLastStatusText("Existing status"); + twinManager.registerTwin(droneTwin, null); + + StatusTextPacket packet = mock(StatusTextPacket.class); + when(packet.isValid()).thenReturn(true); + when(packet.getText()).thenReturn(" "); + + new StatusTextListener(twinManager).handle(droneTwin.getTwinId(), packet, null); + + assertEquals("Existing status", droneTwin.getLastStatusText()); + } +}