Plant view: inline optical QC station at every press
Deploy test site / deploy (push) Successful in 58s

User, 18 Oct 2026: add the new inline inspection machine to the factory.

- Fleet calculator, isometric plant view: qcStation() draws the concept
  station (1.9 x 0.65 x 1.5 m: aluminium conveyor table with the two rim
  belts, light-tight tunnel, stack light, record on the infeed, reject
  bin) in front of the operator in every Duplex cell (after the trimmer)
  and at the unload side of every Classic, in the hall and in phase 2;
  own ids qc-duplex-n / qc-classic-n with tooltips from
  inline-optical-qc.md (Classic hand-off open with Newbilt). Cell floor
  extended to take the bin.
- The finishing block's end-of-line optical station becomes the release
  inspection: the same station measuring cooled samples (records settle
  ~24 h); corridor, block and conveyor tooltips reworded.
- Machine park schematic: a QC bar in every Duplex cell and Classic, an
  "inline QC" legend entry; the legend moved to the bottom of the hall
  box (it collided with the hall heading).
- Docs: inline-optical-qc.md (release station location, plant view),
  site-candidates.md block ground floor, crane-workshop-tours.md tour
  stop 6.

Checks: node --check on all scripts; pw_bp_calc.py 0 fails;
pw_iso_view_check.py OK; every data-id has info at all presets, levels
and rotations; legend clear of cells and heading up to 30M records;
zoomed shots in both themes and two rotations. GF node count at the full
preset 2,523 (was 2,388), 8M records ~2,980; rebuild under 60 ms.
This commit is contained in:
grabowski committed 2026-10-18 14:56:08 +07:00
1 parent 705690eb99
commit 9ffcfe62f1
4 files changed
+63 -23

No files matched your search

+3 -1
View File
@@ -290,7 +290,9 @@ to run along the corridor:
3. Stamper prep
4. Material lab and PVC handling
5. **Press hall**, the long glazed stretch and the centre of the tour
6. 100% optical inspection (the presses trim inline)
6. Release inspection of cooled samples (every record is already
checked by the optical QC station at its press, and the presses trim
inline)
7. QC and listening, with a headphone listening point
8. Finishing and packaging, where the records leave production
+6 -1
View File
@@ -305,7 +305,12 @@ By group, per station:
([Stamper Discs](https://stamperdiscs.com/horns-and-quality-issues-in-vinyl-record-pressing)).
The warp reading at the press is therefore an early warning. Release on
warp uses sampled cooled records, measured on a second station of the
same design in the QC lab.
same design next to the QC lab in the finishing block (the room the
layout had for end-of-line optical inspection).
- **Plant view.** The [plant and business plan calculator](../fleet-calculator.html)
draws a station in front of the operator in every Duplex cell, one at
the unload side of every Classic, and the release station in the
finishing block; the machine park schematic shows them too.
## Two software routes
+50 -19
View File
@@ -38,7 +38,7 @@ document.documentElement.setAttribute("data-theme",t);})();
.pk-press { fill: var(--pk-press); stroke: var(--pk-pressS); stroke-width: 1.2; }
.pk-col { fill: var(--pk-pressS); }
.pk-ex50 { fill: var(--dstd); } .pk-ex35 { fill: var(--dspec); }
.pk-op { fill: #e8b400; } .pk-trim { fill: var(--mute); } .pk-disc { fill: #111; stroke: var(--mute); }
.pk-op { fill: #e8b400; } .pk-trim { fill: var(--mute); } .pk-qc { fill: #c9d0d8; stroke: #5d6878; } .pk-qct { fill: #2c3442; } .pk-disc { fill: #111; stroke: var(--mute); }
.pk-glyph { fill: var(--pk-glyph); stroke: var(--pk-pressS); }
.pk-feed { stroke: var(--mute); stroke-width: 1; fill: none; stroke-dasharray: 2 3; }
.pk-t { fill: var(--fg); font-size: 12px; font-weight: 600; }
@@ -473,6 +473,7 @@ html[data-theme=light] .iso-svg .iso-lamp { fill: #c9ced6; }
.iso-z-stock { --b: color-mix(in srgb, var(--iso-floor) 76%, #e0cc90); }
.iso-z-packmat { --b: color-mix(in srgb, var(--iso-floor) 78%, #90d0a0); }
.iso-svg .iso-part { fill: none; stroke: var(--iso-part); stroke-width: .9; stroke-linecap: square; }
.iso-svg .iso-qcbelt { fill: none; stroke: #2b313b; stroke-width: .8; stroke-linecap: round; }
.iso-svg .iso-mark { fill: none; stroke: var(--iso-mark); stroke-width: .7; stroke-dasharray: 3 2; }
.iso-svg .iso-trench { fill: var(--iso-trench); stroke: none; }
.iso-svg .iso-trenchl { fill: none; stroke: var(--iso-trench); stroke-width: 2.2; }
@@ -807,8 +808,9 @@ html[data-theme=light] .iso-svg .iso-glint { display: inline; }
<section class="card">
<h2>Machine park and stamper production</h2>
<p class="note" style="margin-top:0">The fleet above as a schematic, not to scale: each Duplex cell as
in the cell sketch (presses either side of the operator, EX 50 and EX 35 behind him, trimmer in
front), each Classic with its built-in extruder; above it the stamper line that feeds the presses,
in the cell sketch (presses either side of the operator, EX 50 and EX 35 behind him, trimmer and
inline optical QC station in front), each Classic with its built-in extruder and a QC station at its
unload side; above it the stamper line that feeds the presses,
sized from the cycle times in galvanics.md; below it the central utilities. Bars show how busy
each station is; an amber box exceeds the plant as planned today.</p>
<div class="tscroll"><svg id="park" viewBox="0 0 1200 600" role="img" aria-label="Machine park and stamper production schematic"></svg></div>
@@ -1391,6 +1393,26 @@ html[data-theme=light] .iso-svg .iso-glint { display: inline; }
s.led(cx, (y0 + y1) / 2, z + h + 0.02, "iso-ledo");
return s;
}
// inline optical QC station (equipment/inline-optical-qc.md, FreeCAD concept model): 1.9 x 0.65 x 1.5 m
// aluminium profile frame with the rim-belt conveyor, a light-tight tunnel over the cameras, stack light on
// the tunnel exit and the reject bin beside the outfeed; records run toward -x, the bin stands on the -y side
function qcStation(sc, id, xa, ya, z, tag) {
var s = sc.sprite(id), xb = xa + 1.9, yb = ya + 0.65;
s.seq([
[xa + 0.25, ya - 0.2, function (q) { q.box(xa + 0.05, ya - 0.4, xa + 0.45, ya, z, z + 0.55, "iso-bin"); }],
[xa + 0.95, ya + 0.33, function (q) {
q.box(xa, ya, xb, yb, z, z + 0.9, "iso-optic");
q.lines([[xa + 0.03, ya + 0.18, xb - 0.03, ya + 0.18], [xa + 0.03, yb - 0.18, xb - 0.03, yb - 0.18]], z + 0.905, "iso-qcbelt");
q.seq([[xa + 0.92, ya + 0.33, function (r) { r.box(xa + 0.45, ya + 0.02, xa + 1.4, yb - 0.02, z + 0.9, z + 1.5, "iso-dome"); r.led(xa + 0.55, yb - 0.1, z + 1.52, "iso-ledg"); }],
[xa + 1.65, ya + 0.33, function (r) { r.disc(xa + 1.65, ya + 0.33, z + 0.91, 0.15, "iso-vinylc"); }]]);
}]
]);
if (tag) sc.tag(xa + 0.95, ya + 0.33, z + 2.1, tag, 3, id);
return s;
}
var QC_INFO = "Checks every record on both faces in one turn: surface (a line-scan camera per side), label (a colour camera per side), " +
"warp (two lasers) and centring; a failed record is pushed into the bin. 1.9 x 0.65 x 1.5 m on a 40 x 40 aluminium profile frame, " +
"cycle about 5 to 6 s, hardware about USD 16k to 33k (estimate), inside the metrology budget (inline-optical-qc.md)";
function duplexCell(sc, n, cx, cy, z, running) {
var id = "duplex-" + n;
pressM(sc, id, cx - 1.75, cy - 0.1, z, true, running);
@@ -1401,8 +1423,12 @@ html[data-theme=light] .iso-svg .iso-glint { display: inline; }
t.box(cx + 0.75, cy - 1.65, cx + 1.35, cy - 1.05, z, z + 1.0, "iso-steel");
t.disc(cx + 1.05, cy - 1.35, z + 1.02, 0.22, "iso-vinylc");
sc.sprite(id).person(cx, cy - 0.35, z);
// inline QC in front of the operator: trimmed record on the infeed (trimmer side), good records out at the far end
qcStation(sc, "qc-" + id, cx - 1.25, cy - 1.85, z, "Inline QC");
sc.inf("qc-" + id, "Inline optical QC, Duplex " + n,
"After the trimmer, in front of the operator: he puts each trimmed record on the infeed and still sees every record. " + QC_INFO);
sc.inf(id, "Duplex system " + n + ": 2 HRP 12 presses, EX 50 + EX 35",
"One operator's cell (~5 x 3 m): presses turned 45 degrees to the operator, EX 50 main and EX 35 colour extruder behind him, trimmer front-left. HRP 12 ~150 t; steam 150 to 180 kg/h and cooling up to 125 kW per press (Newbilt).");
"One operator's cell (~5 x 3 m): presses turned 45 degrees to the operator, EX 50 main and EX 35 colour extruder behind him, trimmer front-left, inline optical QC station in front of him. HRP 12 ~150 t; steam 150 to 180 kg/h and cooling up to 125 kW per press (Newbilt).");
}
function classicM(sc, n, cx, cy, z, running) {
var id = "classic-" + n, s = sc.sprite(id), P = sc.P;
@@ -1416,7 +1442,10 @@ html[data-theme=light] .iso-svg .iso-glint { display: inline; }
s.led(p, cy, z + 1.47, "iso-ledg");
if (running) s.puffs(cx - 0.55, cy, z + 2.6, "iso-puff", 2, 3.9);
sc.inf(id, "Classic " + n + ": Newbilt Classic, fully automatic",
"Built-in extruder, automatic cake loading, label feed and spindle stacking; 28 s cycle at 180 g, ~128 records/h (vendor flyer).");
"Built-in extruder, automatic cake loading, label feed and spindle stacking; 28 s cycle at 180 g, ~128 records/h (vendor flyer). Inline optical QC station in front of it.");
qcStation(sc, "qc-" + id, cx - 1.2, cy - 1.6, z, "Inline QC");
sc.inf("qc-" + id, "Inline optical QC, Classic " + n,
"In front of the Classic's unload side. Open point for Newbilt: can the unloader set the record down on the station's infeed instead of the spindle? If not, a fixed camera over the unload position covers surface and label, and warp and centring go to samples. " + QC_INFO);
}
// ------------------------------------------------------------ the site
@@ -1551,7 +1580,7 @@ html[data-theme=light] .iso-svg .iso-glint { display: inline; }
// ------------------------------------------------------------ factory
// finishing and print block, 14 x 25 m on two levels at fx 92..106 (site-candidates.md, block floor plans):
// ground floor optical inspection, QC lab, stair + goods lift along the corridor, packaging behind; upper floor at +3.8 m
// ground floor release inspection, QC lab, stair + goods lift along the corridor, packaging behind; upper floor at +3.8 m
var BLK = { fx0: 92, fx1: 106, up: 3.8 };
function zoneList(cfg, up) {
var z = [
@@ -1589,14 +1618,14 @@ html[data-theme=light] .iso-svg .iso-glint { display: inline; }
sc.inf("colourstore", "Colour store, ~180 m2", "About 100 pallet positions on four levels for tens of colours (estimate); weighing, mixing and vacuum conveying");
sc.inf("plantroom", "Central hydraulic and air plant room, ~62 m2", "Pump sets on one manifold with the accumulators, one compressed-air plant; ring mains with a valved tee at every press position (pressing-line.md)");
sc.inf("aisle", "Crane and forklift aisle, 4.5 m", "Along the back wall from the machine door to the finishing block: heavy moves need a floor-level path");
sc.inf("trim", "100 % optical inspection", "Optical station at the end of the record path (quality-automation.md). No separate trimmer: every Duplex cell and the Classic trim inline (pressing-line.md)");
sc.inf("trim", "Release inspection, cooled samples", "Every record is already inspected inline at its press. Records settle for about 24 h, so warp and centring for the release are measured here on cooled samples, on a second station of the same design (inline-optical-qc.md). No separate trimmer: every Duplex cell and the Classic trim inline (pressing-line.md)");
sc.inf("qc", "QC and listening", "Sampled audio comparison (about 1 in 50) and the listening point on the tour (quality-automation.md)");
sc.inf("packaging", "Finishing and packaging", "Packing tables, shrink wrap and pallet wrapper, ~170 m2 on the ground floor of the finishing and print block (14 x 25 m on two levels, print room reserved upstairs)");
sc.inf("block-stair", "Stair and goods lift", "Connect the two levels of the finishing and print block. The crane runway ends at the press hall, so loads here move by forklift and goods lift; heavy compound stays on the ground floor (site-candidates.md, crane-workshop-tours.md)");
if (cfg.cassette) sc.inf("cassette", "Cassette duplication", "Bench line, ~40 to 80 m2 (project-timeline.md)");
if (cfg.cdr) sc.inf("cdr", "CD-R duplication, short run", "Tower duplicator and printer bench, ~20 to 40 m2 (project-timeline.md)");
sc.inf("printblock", "Finishing and print block, 14 x 25 m",
"Two levels: optical inspection, the QC lab and packaging (~170 m2) on the ground floor; print room (reserve), print stock and packaging materials on the upper floor at +3.8 m (site-candidates.md)");
"Two levels: release inspection, the QC lab and packaging (~170 m2) on the ground floor; print room (reserve), print stock and packaging materials on the upper floor at +3.8 m (site-candidates.md)");
sc.inf("printroom", "Print room (kept in reserve)", "Upper floor at +3.8 m. In-house printing is not decided; jackets come from a print partner today (site-candidates.md, production-timeline.md)");
sc.inf("printstock", "Print stock", "Paper and board, upper floor at +3.8 m (site-candidates.md)");
sc.inf("packmat", "Packaging materials", "Sleeves, inner bags, mailers and cartons, upper floor at +3.8 m (site-candidates.md)");
@@ -1607,12 +1636,12 @@ html[data-theme=light] .iso-svg .iso-glint { display: inline; }
sc.inf("wc-w", "Toilets at the staff entry", "Women: 2 WC and a basin; men: 1 WC, 1 urinal and a basin; an accessible WC. Thai minimum for a factory, per up to 15 workers of each sex (Ministerial Regulation No. 39, B.E. 2537, table 2); separate toilets for women, men and disabled staff (labour welfare regulation B.E. 2548). Showers are in the office changing rooms.");
sc.inf("wc-e", "Toilets, finishing block", "Second set at the east end of the press hall, so nobody walks ~90 m back to the staff entry: women 2 WC and a basin, men 1 WC, 1 urinal and a basin. Proposed (October 2026), not yet in the parametric floor plans.");
sc.inf("corridor", cfg.corridor ? "Visitor corridor, glazed, ~98 m" : "Visitor corridor: later",
cfg.corridor ? "Along the car-park wall in process order: cutting, galvanics, stamper prep, material lab, press hall, optical inspection, QC and listening, finishing. No PPE, filtered air." :
cfg.corridor ? "Along the car-park wall in process order: cutting, galvanics, stamper prep, material lab, press hall with an optical QC station at every press, release inspection, QC and listening, finishing. No PPE, filtered air." :
"The strip along the car-park wall stays free; the glazed corridor is built later.");
sc.inf("crane", cfg.crane === "buy" ? "Overhead crane, 5 t" : "Crane-ready frame (crane rented)",
cfg.crane === "buy" ? "EOT crane over the production compartment, runway to the end of the press hall, rail at ~5.6 m; parks over the workshop (crane-workshop-tours.md)" :
"Runway brackets and rails in the frame; a crane is rented for installations");
sc.inf("conveyor", "Records to finishing", "Schematic: trimmed records travel from the presses to optical inspection");
sc.inf("conveyor", "Records to finishing", "Schematic: inspected records travel from the presses to finishing");
sc.inf("pipebridge", "Pipe bridge", "Steam, condensate and cooling water from the yard to the service trench along the press rows");
}
function factoryRoof(sc, cfg, pv) {
@@ -1700,7 +1729,7 @@ html[data-theme=light] .iso-svg .iso-glint { display: inline; }
} else fl.flat(pts, z + 0.015, "iso-slot", sl.t === "D" ? "duplex-" + sl.n : "classic-" + sl.n);
if (!sl.e && sl.t === "D") {
var cx = (sl.x0 + sl.x1) / 2, cy = (sl.y0 + sl.y1) / 2;
fl.flat(rect(cx - 2.6, cy - 2.0, cx + 2.6, cy + 2.15), z + 0.02, "iso-cell", "duplex-" + sl.n);
fl.flat(rect(cx - 2.6, cy - 2.4, cx + 2.6, cy + 2.15), z + 0.02, "iso-cell", "duplex-" + sl.n);
}
});
// walls cut: back walls taller than front walls (cutaway)
@@ -1885,7 +1914,7 @@ html[data-theme=light] .iso-svg .iso-glint { display: inline; }
sc.sprite("plantroom").box(X(45.4), Y(17.3), X(47.3), Y(19.0), z, z + 1.5, "iso-comp");
sc.sprite("plantroom").box(X(41), Y(16.0), X(43.6), Y(17.2), z, z + 1.2, "iso-oil");
sc.tag(X(44), Y(16.5), z + 3, "Plant room", 4, "plantroom");
// finishing and print block, ground floor (block floor plans): along the corridor optical inspection, QC lab,
// finishing and print block, ground floor (block floor plans): along the corridor release inspection, QC lab,
// stair + goods lift; packaging behind with the cassette and CD-R benches; the upper floor is drawn at 1F
if (!up) finishingGF(sc, cfg, z);
// warehouse: three racking rows; dispatch: staged pallets and a forklift
@@ -1908,11 +1937,10 @@ html[data-theme=light] .iso-svg .iso-glint { display: inline; }
}
// finishing and print block, ground floor (fx 92..106)
function finishingGF(sc, cfg, z) {
// optical inspection (fx 92..96): the record path ends at the optical station; no trimmer here,
// the presses trim inline (user, October 2026)
var op = sc.sprite("trim"); op.box(X(93.0), Y(2.95), X(94.4), Y(4.3), z, z + 1.5, "iso-optic"); op.cyl(X(93.7), Y(3.6), 0.35, z + 1.5, z + 1.8, "iso-dome"); op.led(X(93.7), Y(3.6), z + 1.82, "iso-ledg");
sc.sprite("trim").box(X(94.8), Y(3.2), X(95.7), Y(4.0), z, z + 0.8, "iso-bin");
sc.tag(X(94), Y(5), z + 2.6, "Optical inspection", 4, "trim");
// release inspection (fx 92..96): every record is inspected inline at its press (October 2026); here a second
// station of the same design measures cooled samples before release. No trimmer: the presses trim inline
qcStation(sc, "trim", X(93.1), Y(5.2), z, "");
sc.tag(X(94), Y(5.5), z + 2.6, "Release inspection", 4, "trim");
// QC lab (fx 96..100): bench on the packaging side
var qb = sc.sprite("qc"); qb.box(X(96.3), Y(6.7), X(99.7), Y(7.5), z, z + 0.85, "iso-bench");
qb.seq([[X(97), Y(7.1), function (q) { q.box(X(96.5), Y(6.8), X(97.5), Y(7.4), z + 0.85, z + 1.0, "iso-instr"); q.disc(X(97), Y(7.1), z + 1.02, 0.24, "iso-vinylc"); }],
@@ -3882,10 +3910,11 @@ function drawPark(r, b) {
const y1 = y0 + bh + 44, hh = Math.max(230, 46 + L.h + 18);
box(hx, y1, hw, hh, "pk-hall", 8);
T(hx + 14, y1 + 20, "pk-lane", `PRESS HALL · ${r.D} DUPLEX SYSTEM${r.D === 1 ? "" : "S"} + ${r.C} CLASSIC${r.C === 1 ? "" : "S"} · ${r.P} PRESSES · ~${f0(r.floor)} M² PRESS FLOOR`);
o.push(`<g transform="translate(${hx + hw - 300},${y1 + 10})"><rect width="10" height="10" class="pk-ex50"/><text x="14" y="9" class="pk-s">EX 50</text>` +
o.push(`<g transform="translate(${hx + hw - 375},${y1 + hh - 22})"><rect width="10" height="10" class="pk-ex50"/><text x="14" y="9" class="pk-s">EX 50</text>` +
`<rect x="62" width="10" height="10" class="pk-ex35"/><text x="76" y="9" class="pk-s">EX 35 colour</text>` +
`<circle cx="160" cy="5" r="5" class="pk-op"/><text x="169" y="9" class="pk-s">operator</text>` +
`<rect x="225" width="10" height="10" class="pk-trim"/><text x="239" y="9" class="pk-s">trimmer</text></g>`);
`<rect x="225" width="10" height="10" class="pk-trim"/><text x="239" y="9" class="pk-s">trimmer</text>` +
`<rect x="290" y="1" width="16" height="8" class="pk-qc"/><rect x="295" y="1" width="6" height="8" class="pk-qct"/><text x="310" y="9" class="pk-s">inline QC</text></g>`);
if (!items.length) T(hx + hw / 2, y1 + hh / 2, "pk-s", "no presses", "middle");
items.forEach(([k, n], i) => {
const [px, py] = L.pos[i], tx = hx + 15 + px, ty = y1 + 34 + py;
@@ -3898,6 +3927,7 @@ function drawPark(r, b) {
`<rect x="77" y="14" width="11" height="24" rx="2" class="pk-ex35"/>` +
`<circle cx="73" cy="57" r="5.5" class="pk-op"/>` +
`<rect x="48" y="66" width="13" height="13" rx="1" class="pk-trim"/>` +
`<rect x="65" y="68" width="40" height="11" class="pk-qc"/><rect x="78" y="68" width="16" height="11" class="pk-qct"/>` +
`<text x="75" y="96" class="pk-s" text-anchor="middle">Duplex ${n} · ${pct(r.dUse)}</text></g>`);
else o.push(`<g transform="translate(${tx},${ty}) scale(${sc})">` +
`<rect width="84" height="104" rx="6" class="pk-cell"/>` +
@@ -3905,6 +3935,7 @@ function drawPark(r, b) {
`<rect x="28" y="22" width="36" height="36" rx="3" class="pk-press"/>` +
`<circle cx="33" cy="27" r="3.5" class="pk-col"/><circle cx="59" cy="27" r="3.5" class="pk-col"/><circle cx="46" cy="53" r="3.5" class="pk-col"/>` +
`<circle cx="66" cy="73" r="7" class="pk-disc"/>` +
`<rect x="12" y="68" width="40" height="11" class="pk-qc"/><rect x="24" y="68" width="16" height="11" class="pk-qct"/>` +
`<text x="42" y="96" class="pk-s" text-anchor="middle">Classic ${n} · ${pct(r.cUse)}</text></g>`);
});
// compound in
+4 -2
View File
@@ -199,8 +199,10 @@ and every equipment envelope is an estimate.
- **Finishing and print block**, 14 × 25 m on two levels, beyond the
end of the crane runway
([plans](cnx_vinyl_parametric_payap-block.png)).
- Ground floor: optical inspection, the QC lab, and packaging
(~170 m²) with packing tables, shrink wrap and a pallet wrapper next
- Ground floor: the release inspection station (cooled samples;
every record is already inspected inline at its press, see
[inline optical QC](equipment/inline-optical-qc.md#where-it-sits)),
the QC lab, and packaging (~170 m²) with packing tables, shrink wrap and a pallet wrapper next
to the fire shutter into the warehouse. There is no separate
trimmer: every Duplex cell and the Classic trim inline
([pressing line](equipment/pressing-line.md)). Toilets for women