Table of Contents

Class NcResultRecorder

Namespace
Hi.MachiningProcs
Assembly
HiNc.dll

Internal Use Only. The recorder of one play's result file (the NC play result recording's P3): the IMachiningResultSink the play's three threads hand their records to, a queue, and one writer thread of its own that feeds a SqliteNcResultWriter (Create(string, string)). The recorder is begun armed, with no file: the file and its folder (Output/results/[NcName]-[timestamp]/result.db by default, the template relative to the project folder, the name the first recorded piece's program) are created on the writer thread when the first record reaches it, so a section that records nothing leaves nothing behind. The play's threads never wait on it: a record is offered to a bounded queue, and a record the full queue refuses – the disk stalled, or a play faster than the writer – is dropped and counted, the recording going on (the file's order contract allows a gap; the recorder is Degraded from the first drop and completes its file as a play that did not run to its end); a failure of the storage turns it Failed, which leaves the file an orphan. Nothing a record does reaches the play: every exception is caught here, counted and said once on the progress.

The piece ids (the PieceId of the file) are issued here, by the piece's sentence snapshot (Of(ISentenceCarrier), the same object the steps built from the piece carry) in execution order from 0, at the piece's event; a step whose sentence had no piece event (a legacy HardNc play, whose events carry no piece) gets no id. A piece's bytes are taken once the pipeline has frozen the piece: the pieces wait in a queue of the recorder's own, the frozen ones at its head are recorded at each piece event, and a piece that has waited past twice the pipeline's freeze lag, or is still live when the recording ends (a stop, a reset, freezing turned off), is serialized by the recorder itself.

The host (LocalProjectService) wires it: NotePosAdded(ClStripPos) to PosAdded, NoteSyntaxPieceRan(SyntaxPiece) to SyntaxPieceRan, the recorder itself to ResultSink, NotePlayEnded(bool) to the play's end, and calls Complete(bool, Action<string>) or Abort() when the section ends.

public sealed class NcResultRecorder : IMachiningResultSink, IDisposable
Inheritance
NcResultRecorder
Implements
Inherited Members
Extension Methods

Constructors

NcResultRecorder(string, string, string, IProgress<IMessage>, int)

Begins a recorder: armed, no file yet. The file is created when the first record reaches the writer thread, in the folder relDirectoryTemplate names.

public NcResultRecorder(string baseDirectory, string relDirectoryTemplate, string fallbackNcName, IProgress<IMessage> progress, int queueCapacity = 131072)

Parameters

baseDirectory string

The project folder a relative template is taken from; null for the current directory.

relDirectoryTemplate string

The result folder's path: relative to baseDirectory (an absolute path is used as it is), with NcNameToken and TimestampToken replaced; null for DefaultRelDirectoryTemplate. A folder that exists already gets -2, -3, ... appended.

fallbackNcName string

The name for NcNameToken when no piece names a program before the file is created (a legacy HardNc play, a play of acts); null for play.

progress IProgress<IMessage>

Where the recorder says what it did and what it could not; null for silence.

queueCapacity int

The records the queue holds at most (DefaultQueueCapacity); at least 1.

Exceptions

ArgumentOutOfRangeException

queueCapacity is below 1.

Fields

DatabaseFileName

The result file's name in its folder.

public const string DatabaseFileName = "result.db"

Field Value

string

DefaultAbortDrainTimeout

The default of AbortDrainTimeout: 5 s.

public static readonly TimeSpan DefaultAbortDrainTimeout

Field Value

TimeSpan

DefaultCompleteDrainTimeout

The default of CompleteDrainTimeout: 60 s.

public static readonly TimeSpan DefaultCompleteDrainTimeout

Field Value

TimeSpan

DefaultQueueCapacity

The records the queue to the writer thread holds at most: about eight seconds of a play at the size spike's rate (three records a step, a step every 170-190 us) before records are dropped.

public const int DefaultQueueCapacity = 131072

Field Value

int

DefaultRelDirectoryTemplate

The default template of the result folder, relative to the project folder.

public const string DefaultRelDirectoryTemplate = "Output/results/[NcName]-[timestamp]"

Field Value

string

NcNameToken

The template token the recorded program's file name replaces (the first recorded piece's program, the project's name without one).

public const string NcNameToken = "[NcName]"

Field Value

string

TimestampToken

The template token the file's creation time replaces, yyyyMMdd-HHmmss local.

public const string TimestampToken = "[timestamp]"

Field Value

string

WctFileName

The finished stock's file name in the result folder.

public const string WctFileName = "finish.wct"

Field Value

string

Properties

AbortDrainTimeout

How long Abort() waits for the writer thread before it leaves it behind; DefaultAbortDrainTimeout unless set at the construction.

public TimeSpan AbortDrainTimeout { get; init; }

Property Value

TimeSpan

CompleteDrainTimeout

How long Complete(bool, Action<string>) waits for the writer thread to drain the queue before it gives the file up as failed; DefaultCompleteDrainTimeout unless set at the construction.

public TimeSpan CompleteDrainTimeout { get; init; }

Property Value

TimeSpan

Counts

The counts so far.

public NcResultRecorderCounts Counts { get; }

Property Value

NcResultRecorderCounts

DatabasePath

The full path of the result.db; null until the file is created.

public string DatabasePath { get; }

Property Value

string

Failure

The failure that turned the recorder Failed; null otherwise.

public Exception Failure { get; }

Property Value

Exception

FirstStepIndex

The lowest step index the file took (kept by the writer thread, so it lags the play by the queue); -1 without a step.

public int FirstStepIndex { get; }

Property Value

int

IsEmpty

Whether nothing was handed to the recorder that it would record: no record taken into its queue and no piece waiting for its freeze. A recorder that is empty when its section ends leaves no folder behind.

public bool IsEmpty { get; }

Property Value

bool

LastStepIndex

The highest step index the file took (kept by the writer thread, so it lags the play by the queue); -1 without a step.

public int LastStepIndex { get; }

Property Value

int

PlayCutShort

Whether a recorded play was cut short – the licence's step cap, a lease that expired or a step grant used up (NotePlayEnded(bool) with ranToEnd false) – which makes the file's PlayCompleted false whatever else happened.

public bool PlayCutShort { get; }

Property Value

bool

ResultDirectory

The result folder's full path; null until the file is created.

public string ResultDirectory { get; }

Property Value

string

State

Where the recorder stands.

public NcResultRecorderState State { get; }

Property Value

NcResultRecorderState

StepRecordedSincePlayEnd

Whether a step was recorded since the last NotePlayEnded(bool): false when every recorded step belongs to a play that ran to its end, which is what makes the file's PlayCompleted true at Complete(bool, Action<string>) unless the host says the run was stopped.

public bool StepRecordedSincePlayEnd { get; }

Property Value

bool

Methods

Abort()

Abandons the recording: no record is taken from here on, the writer thread ends, the writer is disposed without completing, and the file, if one was created, stays not completed, an orphan (DeleteOrphanNcResults deletes it). Idempotent; a no-op after Complete(bool, Action<string>).

public void Abort()

AddClStripPos(in ClStripPosRecord)

A strip position, on PosAdded: the shell thread, outside the pipeline's submit lock; the position has no step yet.

public void AddClStripPos(in ClStripPosRecord pos)

Parameters

pos ClStripPosRecord

The position's record.

AddMachiningStep(MachiningStepRecord)

A published step, on the pump thread, after the step pipeline's fourth stage set the step on its position: the step's values of that moment.

public void AddMachiningStep(MachiningStepRecord step)

Parameters

step MachiningStepRecord

The step's record.

AddStepBrief(StepBrief)

A published step's two briefs, on the pump thread, right after AddMachiningStep(MachiningStepRecord) for the same step: the force brief the force stage built (null when the physics did not run on the step) and the sequential physics state, never null for a published step. The briefs of the moment the step was published, taken once: what the step-physics recompute writes onto the step afterwards reaches a recording by the recorder's amend, never through this method.

public void AddStepBrief(StepBrief brief)

Parameters

brief StepBrief

The step's briefs, keyed by the step index.

AddSyntaxPiece(SyntaxPieceRecord)

A piece record taken already by the caller: its sentence gets an id when it has none yet, and the record goes to the file. The pipeline's pieces come through NoteSyntaxPieceRan(SyntaxPiece) instead, which waits for the freeze.

public void AddSyntaxPiece(SyntaxPieceRecord piece)

Parameters

piece SyntaxPieceRecord

The piece's record.

Complete(bool, Action<string>)

Completes the recording: no record is taken from here on; the pieces still waiting are recorded (the frozen ones as they are, the live ones serialized now); the queue is drained and the writer thread ended; the finished stock is written by writeWct to finish.wct in the result folder and hashed; then the file is completed (Complete(bool, string, byte[])) with playCompleted, forced false when the recorder degraded. A failure of the stock's write is said and the file completed without a stock. A completion that throws is told by the file's Meta: refused at its checkpoint or its switch out of WAL – another connection looking at the file – it has its records and counts in, and its tail is run again (TryFinalize(string)) up to three times, a fifth of a second apart; a file still refused is said and left not completed (NotFinalized: DeleteOrphanNcResults completes it once no other program holds it). A completion that failed before its counts were written leaves an orphan (Failed, said as such). Blocks the caller for the stock's write and the completion; the host says so on the progress. Idempotent: a second call answers the first's result.

public NcResultCompletion Complete(bool playCompleted, Action<string> writeWct)

Parameters

playCompleted bool

Whether the recorded plays ran to their ends (the host decides, from the run's stop flag and StepRecordedSincePlayEnd); forced false when the recorder degraded or a play was cut short (PlayCutShort).

writeWct Action<string>

Writes the finished stock to the full path it is given, or leaves no file there when there is no stock to write; null writes none (the service's dispose). A throw out of it is caught and said.

Returns

NcResultCompletion

The outcome.

Complete(bool, Action<string>, Func<long[]>)

Complete(bool, Action<string>), with the finished stock's node map (the NC play result recording's P5): once the stock is written and hashed, captureNodeAttachments is called – on this thread, in the same still moment, the pipeline drained and the stock just written – for the native attachment pointer of every strip position (index = step index, 0 for none), and the keys go into the file (AddNodeAttachments(ReadOnlySpan<long>), NcResult-Record–NodeMapWritten) before the completion. A review binds the stock's nodes to its rebuilt positions by them, so the stock is coloured and picked by the steps as in the play. No stock written (skipped or failed): the capture is not called. A throw out of the capture is said (NcResult-Record–NodeMapFailed) and the file completes without a map: the stock shows plain under review; the writer's own failure inside the write (SqliteException, a disposed writer) is said as NcResult-Record–NodeMapWriteFailed, no completion is attempted on that writer, and the outcome is a refused completion's (Failed or NotFinalized).

public NcResultCompletion Complete(bool playCompleted, Action<string> writeWct, Func<long[]> captureNodeAttachments)

Parameters

playCompleted bool

As Complete(bool, Action<string>).

writeWct Action<string>

As Complete(bool, Action<string>).

captureNodeAttachments Func<long[]>

Answers the node keys by strip position index (0 for none), or null for no map; null writes no map.

Returns

NcResultCompletion

The outcome.

Dispose()

Abort() when the recording was not completed. The queue is not disposed under a producer that may still offer to it; it goes with the recorder.

public void Dispose()

NotePlayEnded(bool)

A play ended (NcFileRan): the steps recorded so far belong to a finished play when it ran to its end; a play the licence cut short (the act runner's step refusal standing at the end) marks the file as not completed for good.

public void NotePlayEnded(bool ranToEnd = true)

Parameters

ranToEnd bool

Whether the play ran to its end rather than being cut short.

NotePosAdded(ClStripPos)

A strip position off PosAdded: its record is taken here (Of(ClStripPos)). A null position is ignored.

public void NotePosAdded(ClStripPos pos)

Parameters

pos ClStripPos

The position the strip added.

NoteSyntaxPieceRan(SyntaxPiece)

A piece the session ran (SyntaxPieceRan, the player's thread): its sentence's id is issued now, the program's name is taken off the first piece, and the piece waits in the recorder's queue until the pipeline freezes it – the frozen pieces at the queue's head are recorded now, and a piece that has waited past twice FreezeLagPieceCount boundaries is serialized live. A null piece (a legacy HardNc play) is counted and skipped.

public void NoteSyntaxPieceRan(SyntaxPiece piece)

Parameters

piece SyntaxPiece

The piece; null for a legacy line.