Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 | 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 6x 6x 6x 1x 1x 1x 1x 6x 1x 1x 4x 4x 4x 4x 6x 9x 9x 8x 8x 9x 1x 1x 7x 7x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x | /**
* v2 print-ticket assembly for the abacus (Phase 2b, Gitea #9).
*
* Builds the `PrintTicketV2` that rides the multipart submit next to the 3MF.
* The filament list mirrors the model's extruder assignment: `meshesToThreeMf`
* groups same-colour bodies onto one extruder in first-appearance order, so the
* ticket lists one spool per DISTINCT body colour, in that same order — a spool
* per extruder, never a spool per role.
*
* The style block is the settings editor's controlled value, passed through
* verbatim (v2 discipline: nothing here injects or defaults process keys).
* Only a THH-backed catalog can produce a ticket — its spool ids are the real
* AMS `slotId`s the service resolves; the params stand-in catalog has no
* physical slots to name.
*/
import type {
PrintTicketV2,
TicketFilament,
TicketSource,
TicketStartPolicy,
TicketStyle,
} from '@eink/print-dialog'
import { PRINT_SOURCE_APP } from '@/lib/abacus/print/source-app'
import type { SpoolBodySummary } from './abacus-3mf'
import type { FilamentCatalog } from './abacus-catalog'
export interface AbacusTicketArgs {
/** Job name shown on the service, e.g. "Abacus — 13 columns". */
name: string
/** Provenance minus `app`, which this module owns. */
source: Omit<TicketSource, 'app'>
/** The bodies that actually went into the 3MF (ascending slot order). */
bodies: readonly SpoolBodySummary[]
/** Must be a 'thh-ams' catalog — spool ids are the AMS slotIds. */
catalog: FilamentCatalog
/** The settings editor's controlled value, verbatim. */
style: TicketStyle
startPolicy: TicketStartPolicy
/** Dedup key — mint one per submit intent, reuse across retries. */
idempotencyKey: string
}
export function buildAbacusTicket(args: AbacusTicketArgs): PrintTicketV2 {
const { name, source, bodies, catalog, style, startPolicy, idempotencyKey } = args
if (catalog.source !== 'thh-ams') {
throw new Error(
'print submission needs the AMS filament roster — the params catalog has no real slot ids'
)
}
if (bodies.length === 0) {
throw new Error('nothing to print — the 3MF has no bodies')
}
// One filament per distinct body colour, in body (= extruder) order.
const seenColors = new Set<string>()
const filaments: TicketFilament[] = []
for (const body of bodies) {
const color = body.colorHex.toLowerCase()
if (seenColors.has(color)) continue
seenColors.add(color)
const spool = catalog.spools[body.slot]
if (!spool) {
throw new Error(`3MF body "${body.label}" references slot ${body.slot}, not in the catalog`)
}
filaments.push({ slotId: spool.id })
}
return {
name,
source: { ...source, app: PRINT_SOURCE_APP },
filaments,
style,
start: { policy: startPolicy },
idempotencyKey,
}
}
|