Resources Numerical accuracy Calculation policy

Cable routes in trays and conduits on the installation plan

Turns the routes drawn on the plan and the circuits into cables with a route, and into the fill rows of each tray and conduit.

How the precision is kept

This file is the adapter in front of the shared network and carrier engines. It scales plan coordinates to millimeters with one multiplication, asks the engine for a route from the board to every device of a circuit and keeps the longest. The accuracy of a length is set by the reach distance used to tie a board or device to a route and by the slack and end allowances passed to the engine, not by the arithmetic here.

Calculations under this policy

3 calculations in 1 source file. The code is read from the released app when this page loads. It is the calculation people run, not a copy kept for this page.

addons/electrical/lib/carriers.js CadCraft 0.8.2 64-bit 3 calculations Reviewed

Tests None found by the review

Cables and their routes

64-bit

One cable per circuit with its route length, plus the fill of every tray and conduit it runs in.

point in mm = plan point × (meters per unit × 1000); cable route = the longest of the board-to-device routes

Coordinates are multiplied by one 64-bit factor; a missing meters-per-unit value is taken as 0.001. Of the routes found from the board to each device, the one with the greatest length is kept by a plain comparison. Lengths, slack and fill come from the engine files.

  • addons/electrical/lib/carriers.js
  • lines 46 to 76
  • CadCraft 0.8.2
export function planCarriers({ routes, inst, mpu, cableOd = {}, reachMm = 500, limits = FILL_DEFAULTS.conduit, trayPct = FILL_DEFAULTS.tray, slack = 0.1, endsMm = 1000 }) {
  const mm = (mpu || 0.001) * 1000;
  const cables = [], routeOut = new Map(), runInfo = {}, unrouted = [], noOd = new Set();
  const bySpace = new Map(), labelOf = new Map();
  for (const rr of routes) { const spec = parseCarrierLayer(rr.layer); if (spec) runInfo['R' + rr.id] = { ...spec, label: String(rr.layer).replace(/^EL-ROUTE-/i, '') }; }
  for (const r of routes) { const k = r.space?.key ?? ''; if (!bySpace.has(k)) bySpace.set(k, []); bySpace.get(k).push(r); }
  for (const c of inst.circuits.values()) {
    if (!c.board || !c.loads.length) continue;
    const spaceKey = c.board.space?.key ?? '';
    const rs = bySpace.get(spaceKey) || [];
    const runs = rs.map((r) => ({ id: 'R' + r.id, verts: r.pts.map((p) => [p.x * mm, p.y * mm, 0]) }));
    const bKey = 'board:' + c.board.uid;
    const loads = c.loads.filter((l) => (l.space?.key ?? '') === spaceKey);
    const points = [{ key: bKey, p: [c.board.at.x * mm, c.board.at.y * mm, 0] }, ...loads.map((l) => ({ key: 'load:' + l.uid, p: [l.at.x * mm, l.at.y * mm, 0] }))];
    const graph = graphFromRuns(runs, points, { tol: reachMm });
    for (const l of loads) labelOf.set('load:' + l.uid, [l.label, l.attrs?.TAG].filter(Boolean).join(' ') || String(l.id));
    const legs = loads.map((l) => ({ tag: c.name + '|' + l.uid, from: bKey, to: 'load:' + l.uid }));
    const r = routeCables(graph, legs);
    let best = null;
    for (const leg of legs) { const x = r.get(leg.tag); if (x?.found && (!best || x.lengthMm > best.x.lengthMm)) best = { leg, x }; }
    if (!best) { unrouted.push(c.name); cables.push({ tag: c.name, circuit: c.name, type: c.cable, odMm: odOf(c.cable, cableOd), n: 1, from: c.board.tag || 'board', to: '', _none: true }); continue; }
    const cable = { tag: c.name, circuit: c.name, type: c.cable, size: '', odMm: odOf(c.cable, cableOd), n: 1, from: c.board.tag || 'board', to: labelOf.get(best.leg.to) || '' };
    if (!cable.odMm) noOd.add(c.cable || c.name);
    cables.push(cable);
    routeOut.set(c.name, best.x);
  }
  const routed = cables.filter((c) => !c._none);
  const rows = scheduleRows(cables, new Map([...routeOut, ...cables.filter((c) => c._none).map((c) => [c.tag, { found: false, runs: [], lengthMm: 0, why: 'ends' }])]), { slack, endsMm });
  const fills = fillByRun(routed, routeOut, runInfo, { limits, trayPct }).map((f) => ({ ...f, noDiameter: f.cables.some((tag) => !routed.find((c) => c.tag === tag)?.odMm) }));
  return { cables, routes: routeOut, rows, fills, unrouted, noOd: [...noOd] };
}

Fill rows for the list

64-bit

The rows of the tray and conduit fill list, with the fill percentage and whether it is within the limit.

pct = round(fill % × 10) ÷ 10

The percentage is rounded to one decimal for the row. The pass or fail flag is copied from the engine's result and is not recomputed from the rounded percentage, so a row can show a percentage equal to the limit and still fail.

  • addons/electrical/lib/carriers.js
  • lines 78 to 80
  • CadCraft 0.8.2
export function fillRows(plan) {
  return plan.fills.map((f) => ({ run: f.run.replace(/^R/, ''), kind: f.kind, size: f.label, cables: f.cables.join(' '), pct: Math.round(f.fill.pct * 10) / 10, limit: f.fill.limitPct, ok: f.noDiameter ? null : f.fill.ok, bigger: f.bigger ? f.bigger.size : '' , noDiameter: f.noDiameter }));
}

Tray size in the layer name

64-bit

The layer name that records what a route is: a tray of a width and depth, or a conduit of a type and trade size.

EL-ROUTE-TRAY-round(width)Xround(depth)

The layer name is where a route's size is stored, and the tray width and depth are rounded to whole millimeters when it is written. The reader accepts 2 to 4 digits for each.

  • addons/electrical/lib/carriers.js
  • lines 36 to 41
  • CadCraft 0.8.2
export function carrierLayer(spec) {
  if (!spec) return null;
  if (spec.kind === 'tray' && spec.widthMm > 0 && spec.depthMm > 0) return `EL-ROUTE-TRAY-${Math.round(spec.widthMm)}X${Math.round(spec.depthMm)}`;
  if (spec.kind === 'conduit' && NAME_OF[spec.type] && CODE_OF[spec.size] && conduitSize(spec.type, spec.size)) return `EL-ROUTE-CONDUIT-${NAME_OF[spec.type]}-${CODE_OF[spec.size]}`;
  return null;
}

In detail

Method
Each circuit with a board is one cable, routed along the drawn routes from its board to its farthest device. A route's size is read from its layer name.
Number types
64-bit numbers. Tray sizes in a layer name are whole millimeters.
Tolerances
A board or device is tied to a route within 500 mm by default (absolute). The schedule adds 10 % slack and 1000 mm at each end by default. The fill percentage in a row is rounded to one decimal.
Limits
A cable type with no typed outside diameter is reported as having none; its fill is not judged.

Approach: 64-bit, stated limit

The arithmetic is 64-bit, but the method or the data sets the accuracy: a solver tolerance, a mesh, a step size, a datum shift, a table. The limit is stated where the result is shown.

Every policy that takes this approach