Session Message Panel
Session messages are partitioned by kind into four sinks on LocalProjectService (obtained via dependency injection):
- ShellProgress — session-level routine / lifecycle messages (ShellProgress). Session-scoped: the property is null outside
BeginSession/EndSession. - NcDiagnosticProgress — play-time NC-pipeline diagnostics (NcDiagnosticProgress), anchored to the NC source block that raised them wherever the diagnostic has one.
- StepDiagnosticProgress — diagnostics anchored to a motion step (StepDiagnosticProgress).
- NcManipulationDiagnosticProgress — NC-manipulation diagnostics: a second NcDiagnosticProgress, written by ConvertClToNcFiles and OptimizeNcFiles, held apart from the play-time sink so writeback findings never mix into the play pipeline's.
The panel surfaces each sink in its own tab, so one tab holds one message kind. ResetRuntime — which a project change runs — empties the first three, so each of them holds the current project only; the manipulation sink is outside that sweep, cleared only at the start of the conversion or optimization run that fills it, and its rows therefore survive a project change.
Layout
- Tab Bar (each tab carries a floating count badge, drawn only while its sink holds messages)
- Shell Tab
- NC Diagnostics Tab
- Step Diagnostics Tab
- NC Manipulation Tab
- Per-Tab Content
- Filter Toolbar
SeverityFilter DropdownCategoryFilter Dropdown- Message Text Filter Input
ResetButton (this tab's three filters, nothing else)ExportButton- Matched / Total Badge (also the tab's hub connection indicator)
- Message Table
- Filter Toolbar
Message Table (per tab)
Each tab renders its sink's message list — Messages, Messages, or Diagnostics for the two NC-diagnostic sinks — as rows of:
Severity(colour-coded via GetSeverity())Anchor— the kind-specific position: none for shell messages, the NC sentence ordinal (Sn <n>) for both the play-time and the manipulation NC diagnostics, and the motion step with its sentence ordinal (S<step> · Sn <n>) for step diagnostics; an NC diagnostic raised at pipeline level rather than at a source block carries noneMessage— GetCategory(), GetId() and GetNotification()
Only take the last filtered elements (e.g. 500–1000) for user experience. Find the usage example in the code:
internal static void DemoUseSessionMessageHost(LocalProjectService localProjectService)
{
// Session messages are partitioned by kind into three sinks on LocalProjectService:
// - ShellProgress: session-level routine / lifecycle messages
// (session-scoped: null outside BeginSession/EndSession).
// - NcDiagnosticProgress: NC-pipeline diagnostics, anchored to the NC source sentence.
// - StepDiagnosticProgress: diagnostics anchored to a motion step.
ShellProgress shellProgress = localProjectService.ShellProgress;
List<IMessage> shellMessages = shellProgress == null
? new List<IMessage>() : shellProgress.Messages.ToList();
foreach (IMessage message in shellMessages)
Console.WriteLine(
$"Shell [{message.GetSeverity()}] {message.GetId()}: {message.GetNotification()}");
foreach (NcDiagnostic diagnostic in
localProjectService.NcDiagnosticProgress.Diagnostics.ToList())
{
var ncLine = diagnostic.SentenceCarrier?.GetSentence()?.FirstIndexedFileLine;
Console.WriteLine(
$"NC [{diagnostic.GetSeverity()}] {diagnostic.GetId()}: {diagnostic.GetNotification()}; " +
$"File: {ncLine?.FilePath}; LineNo: {ncLine?.GetLineNo()}; NC: {ncLine?.Line}");
}
foreach (StepDiagnostic diagnostic in
localProjectService.StepDiagnosticProgress.Messages.ToList())
Console.WriteLine(
$"Step {diagnostic.StepIndex} [{diagnostic.GetSeverity()}] " +
$"{diagnostic.GetId()}: {diagnostic.GetNotification()}");
File.WriteAllLines("output-session-messages.txt",
shellMessages.Select(m =>
$"[{m.GetSeverity()}] {m.GetId()}: {m.GetNotification()}"));
}
Add the update-table event per sink: MessageAdded / MessageAdded, and for the session-scoped shell sink the app-lifetime bridge OnShellMessageAdded (with the matching Cleared events). The updating process has to be called by Loose Manner for user experience.
Note
The message display should be real-time.
Behavior of Export Button
Every tab has its own Export, and it carries that tab's sink alone. It writes the rows the tab is holding — the recent window pulled from that sink, narrowed by that tab's severity, category and text filters — as a CSV of Index,Count,Severity,Category,Id,Anchor,Notification,Detail, named after the sink it came from. It is disabled while the filters match nothing.
SignalR Implementation (Webapi Only)
One hub per sink — /shellMessageHub, /ncDiagnosticHub, /stepDiagnosticHub, /ncManipulationDiagnosticHub — each with a GetMessages(int limit) pull returning MessagesUpdate { messages, totalCount }. A per-sink broadcast service subscribes its sink's MessageAdded/Cleared and raises a coalesced MessagesChanged notification via LooseRunner; the client re-pulls the recent window on each notification (loss-free regardless of how many appends coalesced). The client components connect to the hubs they display to receive real-time updates.
Source Code Path
See HiNC App Anatomy for git repository links.
- wwwroot-src/src/components/execution/SessionMessagePanel.vue (tabbed panel)
- wwwroot-src/src/components/execution/SessionMessageTab.vue + MessageRow.vue (per-tab list)
- wwwroot-src/src/composables/useSessionSinkHub.ts (the four hub composables)
- Execution/SessionSinkHub.cs (the SignalR hub base; one hub per sink)
- Execution/SessionSinkBroadcastService.cs + ShellMessageBroadcastService.cs, NcDiagnosticBroadcastService.cs, StepDiagnosticBroadcastService.cs, NcManipulationDiagnosticBroadcastService.cs (subscribe-and-rebroadcast)
- Execution/SessionSinkDtos.cs (typed DTOs)
See Also
- Execution Page — the page whose main column hosts this panel
- Bottom Message Bar — the app-level notification bar, as distinct from these session sinks