Pipe endpoints join the connection pickers; no more COM11/COM12 auto-bind
Each device row now serves one endpoint at a time, picked from a list of pipe:vrio / pipe:vplasma (first, default) plus the machine's COM ports — nothing opens automatically at startup. The pipe services gained an IsListening property and are started/stopped by the row's Open/Close button instead of running for the whole app lifetime, which also retires the dual-transport warning. Status line shows the active endpoint; OFFLINE when none. Standalone vPLASMA is unchanged (hardwired COM12 + permanent pipe). Also fixes the xUnit1031 warnings in the plasma pipe tests. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -106,10 +106,11 @@ a null-modem cable work the same way.
|
|||||||
## Named pipes for DOSBox-X (no com0com)
|
## Named pipes for DOSBox-X (no com0com)
|
||||||
|
|
||||||
When the game runs in the patched DOSBox-X (the fork's `namedpipe` serial
|
When the game runs in the patched DOSBox-X (the fork's `namedpipe` serial
|
||||||
backend), the virtual ports aren't needed at all: both devices also serve a
|
backend), the virtual ports aren't needed at all: each device's connection
|
||||||
named pipe for the whole app lifetime — `\\.\pipe\vrio` and
|
picker lists its named-pipe endpoint — `pipe:vrio`, `pipe:vplasma` —
|
||||||
`\\.\pipe\vplasma` — and DOSBox-X connects as the client, retrying in the
|
alongside the machine's COM ports. Select it and **Open** to serve
|
||||||
background until the pipe exists:
|
`\\.\pipe\vrio` / `\\.\pipe\vplasma`; DOSBox-X connects as the client,
|
||||||
|
retrying in the background until the pipe exists:
|
||||||
|
|
||||||
```ini
|
```ini
|
||||||
[serial]
|
[serial]
|
||||||
@@ -124,11 +125,11 @@ reads as all-lines-low (cable unplugged). The contract lives in
|
|||||||
`src/VRio.Core/Device/PipeFraming.cs` on this side and `serialnamedpipe.h`
|
`src/VRio.Core/Device/PipeFraming.cs` on this side and `serialnamedpipe.h`
|
||||||
on the fork's. TX is paced exactly like the COM path — with the 9600-baud
|
on the fork's. TX is paced exactly like the COM path — with the 9600-baud
|
||||||
wire gone, the pacer is the only thing between the host and an impossible
|
wire gone, the pacer is the only thing between the host and an impossible
|
||||||
burst. The COM rows keep working unchanged (RIOJoy, real pods); if a pipe
|
burst. Each row serves one endpoint at a time — the RIO is a single-host
|
||||||
client connects while a COM host is active the wire log warns, since the
|
device — and nothing opens automatically at startup; the COM path is
|
||||||
RIO is a single-host device. vRIO's built-in glass and the standalone
|
unchanged for RIOJoy and real pods. vRIO's built-in glass and the
|
||||||
vPLASMA share the `vplasma` pipe name — whoever starts second retries until
|
standalone vPLASMA share the `vplasma` pipe name — whoever starts second
|
||||||
the name frees up.
|
retries until the name frees up.
|
||||||
|
|
||||||
## vPLASMA — the companion plasma display
|
## vPLASMA — the companion plasma display
|
||||||
|
|
||||||
@@ -136,8 +137,8 @@ The cockpit's second serial device is the **plasma display**: a 128×32
|
|||||||
dot-matrix panel on COM2 (9600 8N1, no flow control) that the game draws
|
dot-matrix panel on COM2 (9600 8N1, no flow control) that the game draws
|
||||||
mission text and status graphics on. The software replica comes in two
|
mission text and status graphics on. The software replica comes in two
|
||||||
forms. **Built into vRIO**: the panel's encoder strip hosts the glass at
|
forms. **Built into vRIO**: the panel's encoder strip hosts the glass at
|
||||||
top left, with its own port row in the control strip (label colour = port
|
top left, with its own connection row in the control strip (label colour =
|
||||||
status; auto-opens **COM12** at startup when present). And standalone,
|
connection status; picker offers `pipe:vplasma` and the COM ports). And standalone,
|
||||||
`VPlasma.App`: a bare-glass window that opens **COM12** (the device end of
|
`VPlasma.App`: a bare-glass window that opens **COM12** (the device end of
|
||||||
the plasma's null-modem pair) on startup — retrying while the port is
|
the plasma's null-modem pair) on startup — retrying while the port is
|
||||||
missing or busy, port status in the title bar. Both decode the display's
|
missing or busy, port status in the title bar. Both decode the display's
|
||||||
|
|||||||
@@ -48,6 +48,9 @@ public sealed class VPlasmaPipeService : IDisposable
|
|||||||
/// <summary>The served pipe name (without the <c>\\.\pipe\</c> prefix).</summary>
|
/// <summary>The served pipe name (without the <c>\\.\pipe\</c> prefix).</summary>
|
||||||
public string PipeName => _pipeName;
|
public string PipeName => _pipeName;
|
||||||
|
|
||||||
|
/// <summary>True while the server is up (whether or not a client is connected).</summary>
|
||||||
|
public bool IsListening => _running;
|
||||||
|
|
||||||
/// <summary>True while a client is connected.</summary>
|
/// <summary>True while a client is connected.</summary>
|
||||||
public bool IsClientConnected => _clientConnected;
|
public bool IsClientConnected => _clientConnected;
|
||||||
|
|
||||||
|
|||||||
+86
-90
@@ -10,32 +10,30 @@ namespace VRio.App;
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// vRIO main window: the interactive cockpit panel on the left (the same
|
/// vRIO main window: the interactive cockpit panel on the left (the same
|
||||||
/// functional map RIOJoy's profile editor shows) and a control strip on the
|
/// functional map RIOJoy's profile editor shows) and a control strip on the
|
||||||
/// right — COM ports, device settings, and a live wire log. The panel's
|
/// right — connection rows, device settings, and a live wire log. The
|
||||||
/// encoder strip also hosts the built-in plasma glass (the vPLASMA display
|
/// panel's encoder strip also hosts the built-in plasma glass (the vPLASMA
|
||||||
/// emulator) on its own COM port. At startup the usual ports
|
/// display emulator) on its own connection row. Each row's picker offers
|
||||||
/// (<see cref="PreferredPort"/>, <see cref="PreferredPlasmaPort"/>) are
|
/// the device's named-pipe endpoint (the DOSBox-X fork's namedpipe backend
|
||||||
/// opened automatically when available; otherwise open them by hand. Point
|
/// — the com0com-free path) alongside the machine's COM ports; nothing
|
||||||
/// RIOJoy at the other end of the null-modem pair, and every click here
|
/// opens automatically — pick an endpoint and Open. Point RIOJoy at the
|
||||||
/// arrives at RIOJoy exactly as if the physical cockpit sent it; point the
|
/// other end of a null-modem pair and every click here arrives exactly as
|
||||||
/// game's COM2 passthrough at the plasma pair and the glass lights up.
|
/// if the physical cockpit sent it; point the game at
|
||||||
/// Both devices also serve the DOSBox-X fork's named-pipe transport
|
/// <see cref="RioPipeEndpoint"/>/<see cref="PlasmaPipeEndpoint"/> (or the
|
||||||
/// (<c>\\.\pipe\vrio</c>, <c>\\.\pipe\vplasma</c>) for the whole app
|
/// COM2 passthrough at the plasma pair) and the glass lights up.
|
||||||
/// lifetime — the com0com-free path.
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal sealed class MainForm : Form
|
internal sealed class MainForm : Form
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// vRIO's usual port: the device end of the COM1⇄COM11 com0com pair.
|
/// The RIO's pipe endpoint as listed in the picker. Matches the
|
||||||
/// Auto-opened at startup when present and free; the picker still allows
|
/// DOSBox-X conf syntax: <c>serial1=namedpipe pipe:vrio</c>.
|
||||||
/// any other port.
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private const string PreferredPort = "COM11";
|
private static readonly string RioPipeEndpoint = $"pipe:{VRioPipeService.DefaultPipeName}";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The built-in plasma's usual port: the device end of the COM2⇄COM12
|
/// The plasma's pipe endpoint as listed in the picker
|
||||||
/// com0com pair (the game's COM2 passthrough opens the other end).
|
/// (<c>serial2=namedpipe pipe:vplasma</c>).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private const string PreferredPlasmaPort = "COM12";
|
private static readonly string PlasmaPipeEndpoint = $"pipe:{VPlasmaPipeService.DefaultPipeName}";
|
||||||
|
|
||||||
private readonly VRioDevice _device = new();
|
private readonly VRioDevice _device = new();
|
||||||
private readonly VRioSerialService _service;
|
private readonly VRioSerialService _service;
|
||||||
@@ -195,9 +193,8 @@ internal sealed class MainForm : Form
|
|||||||
|
|
||||||
_service = new VRioSerialService(_device);
|
_service = new VRioSerialService(_device);
|
||||||
_plasmaService = new VPlasmaSerialService(_plasmaDevice);
|
_plasmaService = new VPlasmaSerialService(_plasmaDevice);
|
||||||
// The named-pipe endpoints for DOSBox-X's namedpipe serial backend.
|
// The named-pipe endpoints for DOSBox-X's namedpipe serial backend,
|
||||||
// Unlike a COM port, a listening pipe costs nothing and conflicts with
|
// offered in the connection pickers alongside the COM ports.
|
||||||
// nothing, so both servers run for the whole app lifetime.
|
|
||||||
_pipeService = new VRioPipeService(_device);
|
_pipeService = new VRioPipeService(_device);
|
||||||
_plasmaPipeService = new VPlasmaPipeService(_plasmaDevice);
|
_plasmaPipeService = new VPlasmaPipeService(_plasmaDevice);
|
||||||
_router = new InputRouter(_device);
|
_router = new InputRouter(_device);
|
||||||
@@ -231,19 +228,12 @@ internal sealed class MainForm : Form
|
|||||||
_device.Logged += line => RunOnUi(() => PrependLog(line));
|
_device.Logged += line => RunOnUi(() => PrependLog(line));
|
||||||
_service.Logged += line => RunOnUi(() => PrependLog(line));
|
_service.Logged += line => RunOnUi(() => PrependLog(line));
|
||||||
_lampMirror.Logged += line => RunOnUi(() => PrependLog(line));
|
_lampMirror.Logged += line => RunOnUi(() => PrependLog(line));
|
||||||
_service.ConnectionChanged += open => RunOnUi(() => OnConnectionChanged(open));
|
_service.ConnectionChanged += _ => RunOnUi(UpdateRioEndpointUi);
|
||||||
_service.HostHandshake += high => RunOnUi(() =>
|
_service.HostHandshake += high => RunOnUi(() =>
|
||||||
PrependLog(high ? "Host raised DTR (board-reset handshake)" : "Host dropped DTR"));
|
PrependLog(high ? "Host raised DTR (board-reset handshake)" : "Host dropped DTR"));
|
||||||
_pipeService.Logged += line => RunOnUi(() => PrependLog($"pipe: {line}"));
|
_pipeService.Logged += line => RunOnUi(() => PrependLog($"pipe: {line}"));
|
||||||
_pipeService.HostHandshake += high => RunOnUi(() =>
|
_pipeService.HostHandshake += high => RunOnUi(() =>
|
||||||
PrependLog(high ? "Pipe host raised DTR (board-reset handshake)" : "Pipe host dropped DTR"));
|
PrependLog(high ? "Pipe host raised DTR (board-reset handshake)" : "Pipe host dropped DTR"));
|
||||||
_pipeService.ClientChanged += connected => RunOnUi(() =>
|
|
||||||
{
|
|
||||||
// The RIO is a single-host device; two live transports means two
|
|
||||||
// hosts fighting over one board.
|
|
||||||
if (connected && _service.IsOpen)
|
|
||||||
PrependLog($"Warning: a pipe client connected while {_service.PortName} is open — close one transport.");
|
|
||||||
});
|
|
||||||
_plasmaPipeService.Logged += line => RunOnUi(() => PrependLog($"vPLASMA pipe: {line}"));
|
_plasmaPipeService.Logged += line => RunOnUi(() => PrependLog($"vPLASMA pipe: {line}"));
|
||||||
|
|
||||||
// Built-in plasma glass: a pure listener on its own port, same gestures
|
// Built-in plasma glass: a pure listener on its own port, same gestures
|
||||||
@@ -251,7 +241,7 @@ internal sealed class MainForm : Form
|
|||||||
// right-click resets the display to its power-on state.
|
// right-click resets the display to its power-on state.
|
||||||
_plasmaDevice.Updated += () => RunOnUi(() => _plasmaCanvas.UpdateFrom(_plasmaDevice));
|
_plasmaDevice.Updated += () => RunOnUi(() => _plasmaCanvas.UpdateFrom(_plasmaDevice));
|
||||||
_plasmaService.Logged += line => RunOnUi(() => PrependLog($"vPLASMA: {line}"));
|
_plasmaService.Logged += line => RunOnUi(() => PrependLog($"vPLASMA: {line}"));
|
||||||
_plasmaService.ConnectionChanged += open => RunOnUi(() => OnPlasmaConnectionChanged(open));
|
_plasmaService.ConnectionChanged += _ => RunOnUi(UpdatePlasmaEndpointUi);
|
||||||
_plasmaCanvas.DoubleClick += (_, _) => RunPlasmaSelfTest();
|
_plasmaCanvas.DoubleClick += (_, _) => RunPlasmaSelfTest();
|
||||||
_plasmaCanvas.MouseClick += (_, e) =>
|
_plasmaCanvas.MouseClick += (_, e) =>
|
||||||
{
|
{
|
||||||
@@ -332,12 +322,10 @@ internal sealed class MainForm : Form
|
|||||||
|
|
||||||
RefreshPorts();
|
RefreshPorts();
|
||||||
UpdateStatus();
|
UpdateStatus();
|
||||||
PrependLog("vRIO ready. Open a COM port, then point RIOJoy at the other end of the pair.");
|
PrependLog($"vRIO ready. Pick an endpoint per device — {RioPipeEndpoint} / {PlasmaPipeEndpoint} " +
|
||||||
|
"for DOSBox-X, or a COM port for RIOJoy — and Open.");
|
||||||
LoadBindings();
|
LoadBindings();
|
||||||
_plasmaCanvas.UpdateFrom(_plasmaDevice); // paint the power-on frame
|
_plasmaCanvas.UpdateFrom(_plasmaDevice); // paint the power-on frame
|
||||||
AutoOpenPreferredPorts();
|
|
||||||
_pipeService.Start();
|
|
||||||
_plasmaPipeService.Start();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Panel BuildControlStrip()
|
private Panel BuildControlStrip()
|
||||||
@@ -384,111 +372,116 @@ internal sealed class MainForm : Form
|
|||||||
return panel;
|
return panel;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- Port handling -----------------------------------------------------
|
// ---- Endpoint handling ---------------------------------------------------
|
||||||
|
// Each device row serves one endpoint at a time — the RIO is a
|
||||||
|
// single-host device, so its pipe server and COM port never run together.
|
||||||
|
|
||||||
|
private bool RioOpen => _service.IsOpen || _pipeService.IsListening;
|
||||||
|
private bool PlasmaOpen => _plasmaService.IsOpen || _plasmaPipeService.IsListening;
|
||||||
|
|
||||||
private void RefreshPorts()
|
private void RefreshPorts()
|
||||||
{
|
{
|
||||||
string[] names = SerialPort.GetPortNames().Distinct()
|
string[] names = SerialPort.GetPortNames().Distinct()
|
||||||
.OrderBy(n => n, StringComparer.OrdinalIgnoreCase).ToArray();
|
.OrderBy(n => n, StringComparer.OrdinalIgnoreCase).ToArray();
|
||||||
// An open service's picker is disabled and shows the served port;
|
// An open row's picker is disabled and shows the served endpoint;
|
||||||
// leave it alone so the display can't drift from reality.
|
// leave it alone so the display can't drift from reality.
|
||||||
if (!_service.IsOpen)
|
if (!RioOpen)
|
||||||
RefreshPicker(_portBox, names);
|
RefreshPicker(_portBox, RioPipeEndpoint, names);
|
||||||
if (!_plasmaService.IsOpen)
|
if (!PlasmaOpen)
|
||||||
RefreshPicker(_plasmaPortBox, names);
|
RefreshPicker(_plasmaPortBox, PlasmaPipeEndpoint, names);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void RefreshPicker(ComboBox box, string[] portNames)
|
private static void RefreshPicker(ComboBox box, string pipeEndpoint, string[] portNames)
|
||||||
{
|
{
|
||||||
string? current = box.SelectedItem?.ToString();
|
string? current = box.SelectedItem?.ToString();
|
||||||
box.Items.Clear();
|
box.Items.Clear();
|
||||||
|
box.Items.Add(pipeEndpoint); // always present — a pipe server needs no hardware
|
||||||
foreach (string name in portNames)
|
foreach (string name in portNames)
|
||||||
box.Items.Add(name);
|
box.Items.Add(name);
|
||||||
|
|
||||||
if (box.Items.Count == 0)
|
|
||||||
return;
|
|
||||||
int idx = current is null ? -1 : box.Items.IndexOf(current);
|
int idx = current is null ? -1 : box.Items.IndexOf(current);
|
||||||
box.SelectedIndex = idx >= 0 ? idx : 0;
|
box.SelectedIndex = idx >= 0 ? idx : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Startup convenience: if a device's usual port exists, select it and try
|
|
||||||
/// to open it. Failures (port missing, or busy because another vRIO/app
|
|
||||||
/// holds it) just log — no modal box at launch — and leave the manual
|
|
||||||
/// pickers in charge.
|
|
||||||
/// </summary>
|
|
||||||
private void AutoOpenPreferredPorts()
|
|
||||||
{
|
|
||||||
AutoOpen(_portBox, PreferredPort, _service.Open);
|
|
||||||
AutoOpen(_plasmaPortBox, PreferredPlasmaPort, _plasmaService.Open);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void AutoOpen(ComboBox box, string port, Action<string> open)
|
|
||||||
{
|
|
||||||
int idx = box.Items.IndexOf(port);
|
|
||||||
if (idx < 0)
|
|
||||||
{
|
|
||||||
PrependLog($"{port} not present — pick a port and open it manually.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
box.SelectedIndex = idx;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
open(port);
|
|
||||||
}
|
|
||||||
catch (Exception ex) when (ex is IOException or UnauthorizedAccessException or InvalidOperationException or ArgumentException)
|
|
||||||
{
|
|
||||||
PrependLog($"{port} is present but could not be opened ({ex.Message.TrimEnd('.')}) — open it manually once it frees up.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ToggleOpen()
|
private void ToggleOpen()
|
||||||
{
|
{
|
||||||
if (_service.IsOpen)
|
if (RioOpen)
|
||||||
_service.Close();
|
{
|
||||||
|
_service.Close(); // idempotent —
|
||||||
|
_pipeService.Stop(); // whichever was serving closes
|
||||||
|
UpdateRioEndpointUi();
|
||||||
|
}
|
||||||
else
|
else
|
||||||
OpenFromPicker(_portBox, _service.Open);
|
{
|
||||||
|
OpenFromPicker(_portBox, OpenRioEndpoint);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void TogglePlasmaOpen()
|
private void TogglePlasmaOpen()
|
||||||
{
|
{
|
||||||
if (_plasmaService.IsOpen)
|
if (PlasmaOpen)
|
||||||
|
{
|
||||||
_plasmaService.Close();
|
_plasmaService.Close();
|
||||||
|
_plasmaPipeService.Stop();
|
||||||
|
UpdatePlasmaEndpointUi();
|
||||||
|
}
|
||||||
else
|
else
|
||||||
OpenFromPicker(_plasmaPortBox, _plasmaService.Open);
|
{
|
||||||
|
OpenFromPicker(_plasmaPortBox, OpenPlasmaEndpoint);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OpenRioEndpoint(string endpoint)
|
||||||
|
{
|
||||||
|
if (endpoint == RioPipeEndpoint)
|
||||||
|
_pipeService.Start();
|
||||||
|
else
|
||||||
|
_service.Open(endpoint);
|
||||||
|
UpdateRioEndpointUi();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OpenPlasmaEndpoint(string endpoint)
|
||||||
|
{
|
||||||
|
if (endpoint == PlasmaPipeEndpoint)
|
||||||
|
_plasmaPipeService.Start();
|
||||||
|
else
|
||||||
|
_plasmaService.Open(endpoint);
|
||||||
|
UpdatePlasmaEndpointUi();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OpenFromPicker(ComboBox box, Action<string> open)
|
private void OpenFromPicker(ComboBox box, Action<string> open)
|
||||||
{
|
{
|
||||||
if (box.SelectedItem?.ToString() is not { } port)
|
if (box.SelectedItem?.ToString() is not { } endpoint)
|
||||||
{
|
{
|
||||||
MessageBox.Show(this, "No COM port selected. On a single PC, install a com0com virtual " +
|
MessageBox.Show(this, "No endpoint selected. Pick the device's named pipe (for " +
|
||||||
"null-modem pair and open one end here.", "vRIO", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
"DOSBox-X's namedpipe backend) or a COM port (for RIOJoy through a com0com " +
|
||||||
|
"null-modem pair).", "vRIO", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
open(port);
|
open(endpoint);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
MessageBox.Show(this, $"Could not open {port}:\n{ex.Message}", "vRIO",
|
MessageBox.Show(this, $"Could not open {endpoint}:\n{ex.Message}", "vRIO",
|
||||||
MessageBoxButtons.OK, MessageBoxIcon.Warning);
|
MessageBoxButtons.OK, MessageBoxIcon.Warning);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnConnectionChanged(bool open)
|
private void UpdateRioEndpointUi()
|
||||||
{
|
{
|
||||||
|
bool open = RioOpen;
|
||||||
_openClose.Text = open ? "Close" : "Open";
|
_openClose.Text = open ? "Close" : "Open";
|
||||||
_portBox.Enabled = !open;
|
_portBox.Enabled = !open;
|
||||||
_rioLabel.ForeColor = open ? Color.ForestGreen : Color.Gray;
|
_rioLabel.ForeColor = open ? Color.ForestGreen : Color.Gray;
|
||||||
UpdateStatus();
|
UpdateStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnPlasmaConnectionChanged(bool open)
|
private void UpdatePlasmaEndpointUi()
|
||||||
{
|
{
|
||||||
|
bool open = PlasmaOpen;
|
||||||
_plasmaOpenClose.Text = open ? "Close" : "Open";
|
_plasmaOpenClose.Text = open ? "Close" : "Open";
|
||||||
_plasmaPortBox.Enabled = !open;
|
_plasmaPortBox.Enabled = !open;
|
||||||
_plasmaLabel.ForeColor = open ? Color.ForestGreen : Color.Gray;
|
_plasmaLabel.ForeColor = open ? Color.ForestGreen : Color.Gray;
|
||||||
@@ -715,12 +708,15 @@ internal sealed class MainForm : Form
|
|||||||
$"Analog polls dropped: {dropped}\n" +
|
$"Analog polls dropped: {dropped}\n" +
|
||||||
$"Bad checksums: {bad}";
|
$"Bad checksums: {bad}";
|
||||||
|
|
||||||
_canvas.StatusText = _service.IsOpen
|
string? endpoint = _service.IsOpen ? _service.PortName
|
||||||
? $"vRIO {_device.VersionMajor}.{_device.VersionMinor} on {_service.PortName}\n" +
|
: _pipeService.IsListening ? RioPipeEndpoint
|
||||||
|
: null;
|
||||||
|
_canvas.StatusText = endpoint is not null
|
||||||
|
? $"vRIO {_device.VersionMajor}.{_device.VersionMinor} on {endpoint}\n" +
|
||||||
(wedged
|
(wedged
|
||||||
? "** ANALOG WEDGED (v4.2 bug) — awaiting host reset **"
|
? "** ANALOG WEDGED (v4.2 bug) — awaiting host reset **"
|
||||||
: $"{polls} analog polls" + (polls > 0 ? " (host is alive)" : " (no host traffic yet)"))
|
: $"{polls} analog polls" + (polls > 0 ? " (host is alive)" : " (no host traffic yet)"))
|
||||||
: "PORT CLOSED\nOpen a COM port to go live.";
|
: "OFFLINE\nOpen an endpoint (named pipe or COM port) to go live.";
|
||||||
}
|
}
|
||||||
|
|
||||||
private void PrependLog(string line)
|
private void PrependLog(string line)
|
||||||
|
|||||||
@@ -67,6 +67,9 @@ public sealed class VRioPipeService : IDisposable
|
|||||||
/// <summary>The served pipe name (without the <c>\\.\pipe\</c> prefix).</summary>
|
/// <summary>The served pipe name (without the <c>\\.\pipe\</c> prefix).</summary>
|
||||||
public string PipeName => _pipeName;
|
public string PipeName => _pipeName;
|
||||||
|
|
||||||
|
/// <summary>True while the server is up (whether or not a client is connected).</summary>
|
||||||
|
public bool IsListening => _running;
|
||||||
|
|
||||||
/// <summary>True while a client is connected.</summary>
|
/// <summary>True while a client is connected.</summary>
|
||||||
public bool IsClientConnected => _clientConnected;
|
public bool IsClientConnected => _clientConnected;
|
||||||
|
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ public class VPlasmaPipeServiceTests
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Unknown_frame_type_drops_the_client_and_server_recovers()
|
public async Task Unknown_frame_type_drops_the_client_and_server_recovers()
|
||||||
{
|
{
|
||||||
string name = UniqueName();
|
string name = UniqueName();
|
||||||
var device = new VPlasmaDevice();
|
var device = new VPlasmaDevice();
|
||||||
@@ -85,8 +85,9 @@ public class VPlasmaPipeServiceTests
|
|||||||
try { return bad.Read(new byte[1], 0, 1) == 0; }
|
try { return bad.Read(new byte[1], 0, 1) == 0; }
|
||||||
catch (IOException) { return true; }
|
catch (IOException) { return true; }
|
||||||
});
|
});
|
||||||
Assert.True(eof.Wait(2000), "server did not drop the connection");
|
Assert.True(await Task.WhenAny(eof, Task.Delay(2000)) == eof,
|
||||||
Assert.True(eof.Result);
|
"server did not drop the connection");
|
||||||
|
Assert.True(await eof);
|
||||||
}
|
}
|
||||||
|
|
||||||
using var good = Connect(name);
|
using var good = Connect(name);
|
||||||
|
|||||||
Reference in New Issue
Block a user