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.
Tip
On window desktop application (WPF), consider use textarea instead of datagrid to MessageTable for better performance. Use padding to show the different columns. And use the font in the textarea that with consistent width.
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