Table of Contents

Class SqliteNcResultWriter

Namespace
Hi.SqliteUtils
Assembly
HiNc.dll

Internal Use Only. The writer of one result file (the NC play result recording's P2), opened by Create(string, string): the recorder of the plan's P3 adds the records of a play as they come – a strip position, a published step with the id of its sentence's piece, a step's two briefs, an executed piece with its id – and Complete(bool) turns the file into a single artifact; disposing the writer without completing it abandons the recording (the file stays, not completed, for the recorder to delete). The writer is not the pipeline's sink (it does not implement IMachiningResultSink): it blocks on its own lock and on SQLite, so the recorder keeps the play's threads off it and owns the back-pressure. Any thread may call it, one call at a time: every method, Complete(bool) and Dispose() included, runs under the writer's lock and waits there for a call in progress.

The step-indexed records go into chunks of StepsPerChunk steps, each kind (positions, steps, briefs, node keys) on its own: a record for a later chunk closes the open chunk of its kind, so a chunk once closed takes no more records -- a record with a negative index, for a closed chunk, for a chunk below the open one, or for an index already stored is refused before a byte is written. Within the open chunk the order is free; the kinds are not ordered against each other. A step takes one record of each kind (there is no amend), and the writer knows no runs: a replay that counts its steps from 0 again goes to a new file. The pieces go into chunks of up to PiecesPerChunk pieces, closed at PieceChunkRawBytes raw bytes, in any order of ids. The file is committed every time a chunk closes (every 1,024 steps of a kind, every 256 pieces or 4 MB of them) and at completion, so a crash loses at most the open chunk of each kind. The kinds share one batch, so a row of a step or a piece may be committed by another kind's chunk before its own chunk is; the reader reads such a row as no record. A commit is not synced to the disk (WAL with synchronous=NORMAL): a power cut may also lose the commits since the last checkpoint, the file staying consistent.

Exceptions: an argument the file's content refuses -- a null record, a record that breaks the chunk order, a piece id stored before, a record the wire formats refuse (more than six machine coordinates, a null collided pair) -- is an ArgumentException (an ArgumentNullException for a null) thrown before anything of the record is written, and the writer goes on. A SqliteException (the disk full, the file locked, an I/O error), or an InvalidOperationException from a chunk the Brotli encoder refuses, leaves the writer in an unknown state: dispose it. Complete(bool) throws InvalidOperationException when another connection holds the file, the writer then disposed and the file not finalized. After Complete(bool) or Dispose(), every method but Dispose() throws ObjectDisposedException once its arguments pass the checks that need no file (a null record or argument, a brief without its sequential physics state, a hash that is not 32 bytes); DatabasePath stays readable.

public sealed class SqliteNcResultWriter : IDisposable
Inheritance
SqliteNcResultWriter
Implements
Inherited Members
Extension Methods

Properties

DatabasePath

The full path of the result file.

public string DatabasePath { get; }

Property Value

string

Methods

AddClStripPos(in ClStripPosRecord)

Adds a strip position's record to the position chunk StepIndex / 1024: its program CL, the point and the normal, as the 48 raw bytes of the dvec3d_t (NaN payloads and signed zeros kept). A position for a later chunk first closes the open position chunk and commits the file. Any thread may call it; the calls are serialized on the writer's lock.

public void AddClStripPos(in ClStripPosRecord pos)

Parameters

pos ClStripPosRecord

The position's record.

Exceptions

ArgumentException

The position breaks the chunk order of the positions (a negative index, a closed chunk, a chunk below the open one, an index stored before); nothing is written and the writer goes on.

ObjectDisposedException

The writer was completed or disposed.

SqliteException

SQLite failed to write the closing chunk or to commit; the writer is in an unknown state: dispose it.

AddMachiningStep(MachiningStepRecord, int)

Adds a published step's record: its narrow row (the columns of NcResultStepRow – the id of its sentence's piece and its sentence's index NULL for a step without them – and the pool ids of its two program-zero matrices) and, in the step chunk StepIndex / 1024, its other bytes: the pair on the workpiece geometry coordinate when the motion carries one (a step without one reads back without one), the motion (WriteBin(BinaryWriter)), the removal scalars, the collided pairs (a null array as the wire format's 5-byte null record) and the measured time when the step has one (no bytes at all when it has none). The two matrices of the motion's ProgramZeroLegs go into the matrix pool, a row a distinct matrix. A step for a later chunk first closes the open step chunk and commits the file. Any thread may call it; the calls are serialized on the writer's lock.

public void AddMachiningStep(MachiningStepRecord step, int pieceId)

Parameters

step MachiningStepRecord

The step's record.

pieceId int

The id the recorder gave the step's sentence (the piece built from it); below 0 for a step without one, stored as NULL. The writer does not check it against the pieces.

Exceptions

ArgumentNullException

step is null.

ArgumentException

The step breaks the chunk order of the steps (a negative index, a closed chunk, a chunk below the open one, a step stored before), or its record cannot be written (more than six machine coordinates, a null collided pair); nothing is written and the writer goes on.

ObjectDisposedException

The writer was completed or disposed.

SqliteException

SQLite failed to write the row, a pooled matrix, the closing chunk or the commit; the writer is in an unknown state: dispose it.

AddNodeAttachments(ReadOnlySpan<long>)

Adds the finished stock's node map (schema version 2; the NC play result recording's P5): for every strip position i, keys[i] is the native attachment pointer the stock's nodes carry for that position in the finish.wct written at the same moment (the position's CbtrPickable.NativeAttachmentHandle), as the 8 raw bytes of the long in the node attachment chunk i / 1024; a 0 is a position without one and leaves its slot empty. Meant to be called once, at the completion, with the whole strip (the recorder does): the slots follow the chunk order contract of the other kinds, so a second call for indices at or below a closed chunk is refused. The count of keys written is the file's NodeAttachCount once completed. Any thread may call it; the calls are serialized on the writer's lock.

public void AddNodeAttachments(ReadOnlySpan<long> keys)

Parameters

keys ReadOnlySpan<long>

The node keys by strip position index; 0 for none.

Exceptions

ArgumentException

A key breaks the chunk order of the node keys (a chunk closed by an earlier call, an index stored before); the keys before it are written and the writer goes on.

ObjectDisposedException

The writer was completed or disposed.

SqliteException

SQLite failed to write a closing chunk or to commit; the writer is in an unknown state: dispose it.

AddStepBrief(StepBrief)

Adds a published step's two briefs to the brief chunk StepIndex / 1024: the force brief's tri-state record (WriteBin(BinaryWriter, MillingForceBrief?); a null brief, the physics not run on the step, as the 2-byte null record) and the sequential physics state's record. The recorder takes them off the step's consignment as the play writes it, or off the step cache (FindStepBriefs(int, int)) at completion. A step's briefs are recorded once: there is no amend, and a second record for the step is refused. A brief for a later chunk first closes the open brief chunk and commits the file. Any thread may call it; the calls are serialized on the writer's lock.

public void AddStepBrief(StepBrief brief)

Parameters

brief StepBrief

The step's briefs: its force brief null for the null record, its sequential physics state not null.

Exceptions

ArgumentNullException

brief is null.

ArgumentException

The sequential physics state is null, or the step breaks the chunk order of the briefs (a negative index, a closed chunk, a chunk below the open one, a step stored before); nothing is written and the writer goes on.

ObjectDisposedException

The writer was completed or disposed.

SqliteException

SQLite failed to write the closing chunk or to commit; the writer is in an unknown state: dispose it.

AddSyntaxPiece(SyntaxPieceRecord, int)

Adds an executed piece's record: its index row – its address (the file and line index TryGetFileLineIndex(out int, out int) answers, both NULL when it answers false), its sentence's index and block text, where its entry lies – and, in the open piece chunk, its entry: the sentence snapshot's record (WriteBin(BinaryWriter)) followed by the piece's compact JSON (nothing for a piece without one, whose JsonLen is NULL). A sentence with an address and a file path adds the file to the NcFile table the first time its file index is seen: the first path recorded for an index wins. The ids come in any order; the writer does not check them against the steps'. A piece the open chunk has no room for first closes it and commits the file, and the chunk's 256th piece, or its 4 MB, closes it after the append. Any thread may call it; the calls are serialized on the writer's lock.

public void AddSyntaxPiece(SyntaxPieceRecord piece, int pieceId)

Parameters

piece SyntaxPieceRecord

The piece's record.

pieceId int

The id the recorder gave the piece: the key of its row, unique in the file.

Exceptions

ArgumentNullException

piece is null.

ArgumentException

A piece with pieceId was stored before: refused at its index row, before its entry reaches the chunk, so the piece leaves nothing in the file (a chunk closed to make room for it stays closed) and the writer goes on.

ObjectDisposedException

The writer was completed or disposed.

SqliteException

SQLite failed to write the rows, the closing chunk or the commit; the writer is in an unknown state: dispose it.

Complete(bool)

Completes the file without a finish.wct: see Complete(bool, string, byte[]). A play the user stopped is completed too, with playCompleted false, when its file is to stay reviewable; disposing the writer abandons the file.

public void Complete(bool playCompleted)

Parameters

playCompleted bool

Whether the play ran to its end (true) or was stopped (false), for the file's Meta.

Exceptions

InvalidOperationException

Another connection holds the file, so it could not be checkpointed (a connection in a read transaction) or taken out of WAL mode (a connection that merely has it open; the switch is retried for about two seconds, not the driver's 30), or the Brotli encoder refused a chunk; the writer is disposed and the file left not finalized.

SqliteException

SQLite failed to write a chunk or the Meta rows, or to commit; the writer is disposed and the file left not finalized.

ObjectDisposedException

The writer was completed or disposed.

Complete(bool, string, byte[])

Completes the file: closes the open chunks of the five kinds, writes the counts of the records the writer took, whether the play ran to its end, the completion time and the .wct reference to Meta, commits, checkpoints the WAL and takes the file out of WAL mode, and only then marks it Finalized – so a file marked completed is one file in rollback-journal mode that needs no -wal or -shm beside it (those left are deleted where they can be) and can be copied as it is. No other connection, of this process or another, may hold the file meanwhile. Past its argument checks the call disposes the writer, successfully or not. A failure leaves the file not finalized, with what the call had committed: a refused checkpoint or switch leaves the counts, the completion time and the .wct reference in Meta, the file in WAL mode.

public void Complete(bool playCompleted, string wctRelPath, byte[] wctSha256)

Parameters

playCompleted bool

Whether the play ran to its end (true) or was stopped (false), for the file's Meta.

wctRelPath string

The path of the finish.wct relative to the file; not null.

wctSha256 byte[]

The SHA-256 of that .wct, 32 bytes, stored as lower-case hex; not null.

Exceptions

ArgumentNullException

wctRelPath or wctSha256 is null; the writer is left as it was.

ArgumentException

wctSha256 is not 32 bytes; the writer is left as it was.

InvalidOperationException

Another connection holds the file, so it could not be checkpointed (a connection in a read transaction) or taken out of WAL mode (a connection that merely has it open; the switch is retried for about two seconds, not the driver's 30), or the Brotli encoder refused a chunk; the writer is disposed and the file left not finalized.

SqliteException

SQLite failed to write a chunk or the Meta rows, or to commit; the writer is disposed and the file left not finalized.

ObjectDisposedException

The writer was completed or disposed.

Dispose()

Closes the writer. Without Complete(bool) before it this abandons the recording: the open batch is rolled back (the open chunks are lost, with the rows written since the last commit), the file stays where it is, not completed – Finalized 0, as Create(string, string) wrote it – for the recorder to delete. A play whose file is to stay reviewable is completed instead. Idempotent, and a no-op after Complete(bool); it waits for a call in progress on another thread.

public void Dispose()