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. Every Warning and Error of the four sinks is also copied into the service log as it arrives, capped per message id, where it outlives both clearings — see Log Viewer Page.
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 — keeps a row whose notification, id, detail or anchor holds the text, with letter case, diacritics and character width ignored and katakana found by their hiragana (Typing and Searching)
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 none. ACollision--Detectedis anchored to the index its pose's step takes and said at the detection, before that step is built — a play that pauses on failure pauses on it — so it can name an index the strip holds no step at, or, after a stop and a start without a reset, an index a later step took; so can a licence or lease refusal said before a step is granted (MachiningStep--LicenseCapReachedand its kin, which hold the play), aPlayer-Run--Fault, and aStep-Task--Fault, whose step's building failed. Their rows check the step: when the step does not carry the message's sentence, the cell shows the message's own NC line instead, marked step not built (<file>:<line> · step not built, orSn <n> · step not builtwhen the sentence has no file line), and its tooltip says which case it is — the play paused before building the step, the run ended before building it, a stop dropped it or building it failed, or a later step took the index — and ends with theS<step> · Sn <n>form; a row asksGET api/NcProgram/sentence-of-steponce it comes into view, and asks again as the strip moves. A step the pipeline is still filling, or the one a running play is building, keeps itsS<step> · Sn <n>. The other step messages — a script's, a tool change's, a line end's, an empty spindle's — are anchored to the next built step by design and keepS<step> · Sn <n>unchecked. A stroke-limit report names no sentence: it is anchored to the step it checked, or to the next built step when the motion it checked built none, and its row showsS<step>alone, unchecked too. The CSV export and the text filter keep the row's anchor —S<step> · Sn <n>, orS<step>on a row that names no sentence, such as a stroke-limit report's — rather than the step not built text.Message— 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/useStepAnchorStatus.ts (whether a step row's step carries the message's sentence)
- 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