Skip to content

WIP: UI can be sent over udp#1004

Open
penfold42 wants to merge 3 commits intoprobonopd:mainfrom
penfold42:ui-udp
Open

WIP: UI can be sent over udp#1004
penfold42 wants to merge 3 commits intoprobonopd:mainfrom
penfold42:ui-udp

Conversation

@penfold42
Copy link
Contributor

@penfold42 penfold42 commented Dec 23, 2025

For your review only at this stage.

VERY! incomplete, but it does work.

If you're interested in getting this in I will:

  • Make it configurable!
  • Look to eliminate Msg2. I may have to tweak the existing menu display code to explicitly move the cursor to the second line.

Summary by Sourcery

Add support for mirroring the LCD user interface over UDP when networking is available.

New Features:

  • Send the rendered two-line LCD menu display over UDP to a configured destination when the network stack becomes ready.

Enhancements:

  • Extend the user interface component with UDP socket initialization and write helpers tied into the existing network startup flow.

@sourcery-ai
Copy link

sourcery-ai bot commented Dec 23, 2025

Reviewer's Guide

Adds UDP-based mirroring of the UI text output alongside the existing LCD display, wiring it into the network initialization path and introducing minimal socket-based send logic.

Sequence diagram for DisplayWrite mirroring to LCD and UDP

sequenceDiagram
    actor User
    participant CMiniDexed
    participant CUserInterface
    participant LCDDevice as CLCD
    participant UDPSocket as CSocket

    User ->> CMiniDexed: triggers UI update
    CMiniDexed ->> CUserInterface: DisplayWrite(pMenu, pParam, pValue)

    CUserInterface->>CUserInterface: Build Msg (LCD escape codes + text)
    CUserInterface->>CUserInterface: Build Msg2 (Msg + newline, second line, clear-to-end)

    CUserInterface ->> CLCD: LCDWrite(Msg)
    CUserInterface ->> UDPSocket: UDPWrite(Msg2)
    alt UDP socket available
        UDPSocket ->> UDPSocket: SendTo(Msg2, length, 0, m_UDPDestAddress, m_UDPDestPort)
    else UDP socket not available
        CUserInterface ->> CUserInterface: Skip UDP send
    end
Loading

Sequence diagram for network initialization and UDP UI setup

sequenceDiagram
    participant CMiniDexed
    participant CUserInterface
    participant NetSubSystem as CNetSubSystem
    participant UDPSocket as CSocket
    participant CIPAddress

    CMiniDexed ->> CMiniDexed: UpdateNetwork()
    CMiniDexed ->> CUserInterface: InitUDP()

    CUserInterface ->> CIPAddress: Set(0x2c00000a)
    CUserInterface ->> CUserInterface: m_UDPDestPort = 1306
    CUserInterface ->> CIPAddress: IsNull()
    alt destination address is valid
        CUserInterface ->> NetSubSystem: Get()
        NetSubSystem -->> CUserInterface: CNetSubSystem*
        CUserInterface ->> UDPSocket: new CSocket(pNet, IPPROTO_UDP)
        CUserInterface ->> UDPSocket: Connect(m_UDPDestAddress, m_UDPDestPort)
        CUserInterface ->> UDPSocket: SetOptionBroadcast(TRUE)
    else destination address is null
        CUserInterface ->> CUserInterface: UDP display disabled
    end

    CUserInterface -->> CMiniDexed: InitUDP() returns true
Loading

Class diagram for updated CUserInterface UDP display support

classDiagram
    class CMiniDexed {
        +void UpdateNetwork()
        -bool m_bNetworkReady
        -CUserInterface m_UI
    }

    class CUserInterface {
        +bool Initialize()
        +bool InitUDP()
        +void Process()
        +void ParameterChanged()
        +void DisplayWrite(const char* pMenu, const char* pParam, const char* pValue)
        -void LCDWrite(const char* pString)
        -void UDPWrite(const char* pString)
        -CMiniDexed* m_pMiniDexed
        -CConfig* m_pConfig
        -CCharacterDevice* m_pLCD
        -CWriteBufferDevice* m_pLCDBuffered
        -CSocket* m_pUDPSendSocket
        -CIPAddress m_UDPDestAddress
        -unsigned m_UDPDestPort
    }

    class CNetSubSystem {
        +static CNetSubSystem* Get()
    }

    class CSocket {
        +CSocket(CNetSubSystem* pNet, unsigned protocol)
        +bool Connect(CIPAddress address, unsigned port)
        +bool SetOptionBroadcast(bool enable)
        +int SendTo(const void* buffer, unsigned length, unsigned flags, CIPAddress address, unsigned port)
    }

    class CIPAddress {
        +void Set(unsigned address)
        +void Format(CString* pString)
        +bool IsNull()
    }

    CMiniDexed --> CUserInterface : owns
    CUserInterface ..> CNetSubSystem : uses
    CUserInterface ..> CSocket : uses
    CUserInterface ..> CIPAddress : uses
    CSocket ..> CNetSubSystem : constructed_with
Loading

File-Level Changes

Change Details Files
Introduce UDP initialization and socket management in the user interface to send display data over the network.
  • Add InitUDP method that configures a UDP destination address/port and creates a broadcast-capable UDP socket when networking is ready
  • Store UDP socket, destination IP address, and port as new CUserInterface members with default port 1306
  • Invoke InitUDP from CMiniDexed::UpdateNetwork once the network subsystem is up
src/userinterface.cpp
src/userinterface.h
src/minidexed.cpp
Mirror LCD text output to UDP by constructing a newline-terminated message and sending it via the new UDP socket.
  • Extend DisplayWrite to build a second CString (Msg2) that concatenates the first and second display lines plus a newline and matching control sequences
  • Introduce UDPWrite helper that sends the constructed string using the UDP socket and logs on failure
  • Call UDPWrite from DisplayWrite in parallel with the existing LCDWrite call
src/userinterface.cpp
src/userinterface.h

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 3 issues, and left some high level feedback:

  • Consider avoiding the hard-coded UDP destination (0x2c00000a:1306) and aligning behavior with the existing configuration model and the 0.0.0.0 ‘disabled’ convention, so runtime configuration and default broadcast work as described in the comments.
  • m_pUDPSendSocket is allocated in InitUDP but never freed; it would be good to either own and clean it up in CUserInterface’s destructor or switch to a RAII-style wrapper to avoid leaking the socket.
  • DisplayWrite currently builds two largely duplicated strings (Msg and Msg2) with slightly different control sequences; consider refactoring to share the common formatting logic or clearly separate the terminal vs UDP protocols to avoid them drifting out of sync.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- Consider avoiding the hard-coded UDP destination (0x2c00000a:1306) and aligning behavior with the existing configuration model and the 0.0.0.0 ‘disabled’ convention, so runtime configuration and default broadcast work as described in the comments.
- m_pUDPSendSocket is allocated in InitUDP but never freed; it would be good to either own and clean it up in CUserInterface’s destructor or switch to a RAII-style wrapper to avoid leaking the socket.
- DisplayWrite currently builds two largely duplicated strings (Msg and Msg2) with slightly different control sequences; consider refactoring to share the common formatting logic or clearly separate the terminal vs UDP protocols to avoid them drifting out of sync.

## Individual Comments

### Comment 1
<location> `src/userinterface.cpp:59` </location>
<code_context>
+{
+	// UDP MIDI send socket setup (default: broadcast 255.255.255.255:1999)
+	//m_UDPDestAddress.Set(0xFFFFFFFF); // Broadcast by default
+	m_UDPDestAddress.Set(0x2c00000a); // 
+	m_UDPDestPort = 1306;
+#if 0
</code_context>

<issue_to_address>
**suggestion:** The hard-coded IP literal 0x2c00000a is a magic number and makes the target address harder to understand or change.

This raw literal obscures which IP is used and hard-codes it in the logic. Please either source the address from configuration (as the commented-out code implies) or introduce a named constant/helper (e.g., `constexpr uint32_t kDefaultUdpDest = ...;`) so the intent is clear and the value is easy to change later.

Suggested implementation:

```cpp
LOGMODULE ("ui");

	delete m_pLCD;
}

namespace {
constexpr uint32_t kDefaultUdpDestAddress = 0x2c00000a; // 10.0.0.44
constexpr uint16_t kDefaultUdpDestPort = 1306;
}

bool CUserInterface::InitUDP (void)

```

```cpp
	// UDP MIDI send socket setup (default: broadcast 255.255.255.255:1999)
	//m_UDPDestAddress.Set(0xFFFFFFFF); // Broadcast by default
	m_UDPDestAddress.Set(kDefaultUdpDestAddress);
	m_UDPDestPort = kDefaultUdpDestPort;

```
</issue_to_address>

### Comment 2
<location> `src/userinterface.cpp:327` </location>
<code_context>
 	}
 }

+void CUserInterface::UDPWrite (const char *pString)
+{
+	if (m_pUDPSendSocket) {
</code_context>

<issue_to_address>
**nitpick (bug_risk):** UDPWrite re-computes strlen and uses `%u` with a `size_t` value, which can be tightened up.

Two small tweaks:
- Cache `strlen(pString)` in a local `size_t len` and reuse it for both `SendTo` and logging.
- Since `strlen` returns `size_t`, use `%zu` in the log (or cast to `unsigned`) to avoid a type/format mismatch on some platforms.
</issue_to_address>

### Comment 3
<location> `src/userinterface.cpp:52` </location>
<code_context>
 CUserInterface::~CUserInterface (void)
</code_context>

<issue_to_address>
**issue (bug_risk):** The UDP socket allocated in InitUDP is not released in the destructor, leading to a leak.

Please also delete `m_pUDPSendSocket` in the destructor (with a null check if needed) so the UDP socket is properly released.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

}
}

void CUserInterface::UDPWrite (const char *pString)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nitpick (bug_risk): UDPWrite re-computes strlen and uses %u with a size_t value, which can be tightened up.

Two small tweaks:

  • Cache strlen(pString) in a local size_t len and reuse it for both SendTo and logging.
  • Since strlen returns size_t, use %zu in the log (or cast to unsigned) to avoid a type/format mismatch on some platforms.

@@ -50,6 +52,37 @@ CUserInterface::~CUserInterface (void)
delete m_pLCD;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (bug_risk): The UDP socket allocated in InitUDP is not released in the destructor, leading to a leak.

Please also delete m_pUDPSendSocket in the destructor (with a null check if needed) so the UDP socket is properly released.

@github-actions
Copy link

Build for testing:
MiniDexed_1313_2025-12-23-650a449_32bit
MiniDexed_1313_2025-12-23-650a449_64bit
Use at your own risk.

The autoreboot feature can be abused as you dont need a valid login to trigger the reboot.

This fix will only auto reboot after you quit a logged in session.
explicitly move the cursor to row 2 when drawing menu so I can remove Msg2 and write the same strings to local displays and the udp display

UDP remote display can now be configured:
UDPDisplayEnabled=0
UDPDisplayIPAddress=10.0.0.44
@github-actions
Copy link

Build for testing:
MiniDexed_1316_2025-12-23-a892ece_32bit
MiniDexed_1316_2025-12-23-a892ece_64bit
Use at your own risk.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant