3D rooms: rail posts, solid servers of unknown height, a word when no cabling is known
Through the see-through cabinet both rail faces showed as two grey boards, and a server of unknown height (drawn 1U, see-through) read as an empty shelf. Racks now have their four rail posts instead of panels; every server is solid, one of unknown height carries an amber outline (the elevation's ?U). A room whose machines' LLDP saw no switch and that NetBox has no cables for says so in the side panel, and how to enter cables by hand. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
1d41a83f27
commit
6a7b158644
2 files changed
+15
-11
No files matched your search
@@ -90,7 +90,7 @@ function build(site, room, racks, blocks) {
|
||||
const mat = (k, make) => mats[k] || (mats[k] = keep(make()));
|
||||
const stateMat = (cls, known) => mat(cls + known, () => {
|
||||
const c = cv({ ok: '--ok', warn: '--warn', bad: '--danger' }[cls] || '--muted');
|
||||
return new T.MeshStandardMaterial({ color: c, emissive: c, emissiveIntensity: 0.25, roughness: 0.55, transparent: !known, opacity: known ? 1 : 0.55 });
|
||||
return new T.MeshStandardMaterial({ color: c, emissive: c, emissiveIntensity: 0.25, roughness: 0.55 });
|
||||
});
|
||||
const lineMat = name => mat('l' + name, () => new T.LineBasicMaterial({ color: cv(name) }));
|
||||
const box = (w, h, d) => keep(new T.BoxGeometry(w, h, d));
|
||||
@@ -127,11 +127,10 @@ function build(site, room, racks, blocks) {
|
||||
body.userData = { rack: r.name, tip: `${r.name} · ${st.ms.length} machines · ${st.used}/${H}U used${r.auto ? ' · not placed yet' : ''}` };
|
||||
const edges = new T.LineSegments(keep(new T.EdgesGeometry(body.geometry)), lineMat(sel ? '--accent' : '--muted'));
|
||||
edges.position.copy(body.position);
|
||||
// the rails' face, lighter than the cabinet: where the front is and what is empty
|
||||
const face = new T.Mesh(box(RAIL, H * U_M, 0.004), mat('face', () => new T.MeshStandardMaterial({ color: 0x59626d, roughness: 0.9, transparent: true, opacity: 0.55, depthWrite: false })));
|
||||
face.position.set(0, PLINTH + H * U_M / 2, -TILE + 0.008);
|
||||
const back = face.clone(); back.position.z = TILE - 0.008;
|
||||
g.add(body, edges, face, back); R3.pick.push(body);
|
||||
// the four rail posts, front and back: where a unit is, without a panel to look through
|
||||
const post = mat('post', () => new T.MeshStandardMaterial({ color: 0x4b535e, roughness: 0.6, metalness: 0.5 }));
|
||||
const posts = [-1, 1].flatMap(sx => [-1, 1].map(sz => { const p = new T.Mesh(box(0.016, H * U_M, 0.016), post); p.position.set(sx * (RAIL / 2 + 0.01), PLINTH + H * U_M / 2, sz * (TILE - 0.014)); return p; }));
|
||||
g.add(body, edges, ...posts); R3.pick.push(body);
|
||||
g.updateMatrixWorld(true);
|
||||
rackAt[lc(r.name)] = { g, H, h, r, used: blocks[i].covered };
|
||||
for (const b of Object.values(blocks[i].top)) {
|
||||
@@ -154,17 +153,21 @@ function build(site, room, racks, blocks) {
|
||||
if (front) { // the drawn front on the face towards the aisle (-z), the cabinet colour elsewhere
|
||||
let tex = front.tex;
|
||||
if (n > 1) { tex = keep(front.tex.clone()); tex.repeat.set(sw / RAIL, 1); tex.offset.set((cuts[k] + RAIL / 2) / RAIL, 0); tex.needsUpdate = true; }
|
||||
const face = keep(new T.MeshStandardMaterial({ map: tex, emissiveMap: tex, emissive: 0xffffff, emissiveIntensity: 0.3, roughness: 0.7, transparent: !b.known, opacity: b.known ? 1 : 0.55 }));
|
||||
const side = mat('side' + b.known, () => new T.MeshStandardMaterial({ color: 0x1c1e22, roughness: 0.8, transparent: !b.known, opacity: b.known ? 1 : 0.55 }));
|
||||
const face = keep(new T.MeshStandardMaterial({ map: tex, emissiveMap: tex, emissive: 0xffffff, emissiveIntensity: 0.3, roughness: 0.7 }));
|
||||
const side = mat('side', () => new T.MeshStandardMaterial({ color: 0x1c1e22, roughness: 0.8 }));
|
||||
look = [side, side, side, side, side, face];
|
||||
}
|
||||
const s = new T.Mesh(box(sw - (front ? 0.001 : 0.006), hh, 0.03), look);
|
||||
s.position.set(-(cuts[k] + cuts[k + 1]) / 2, y, -TILE - 0.006); // seen from the aisle, local +x is on the left
|
||||
s.userData = { rack: r.name, id: m.id, tip: `${nameOf(m)} · ${m.state} · U${b.unit}${span > 1 ? '–' + (b.unit + span - 1) : ''}${b.known ? '' : ' (height unknown)'} · ${[m.manufacturer, m.product].filter(Boolean).join(' ')}` };
|
||||
g.add(s); R3.pick.push(s);
|
||||
if (!b.known) { // height unknown (drawn as 1U): an amber outline, as "?U" in the elevation
|
||||
const o = new T.LineSegments(keep(new T.EdgesGeometry(s.geometry)), mat('unknownU', () => new T.LineBasicMaterial({ color: new T.Color(colorOf.warn) })));
|
||||
o.position.copy(s.position); o.scale.set(1.01, 1.08, 1.2); g.add(o);
|
||||
}
|
||||
});
|
||||
// the chassis between front and back: solid and dark, apart from the cabinet
|
||||
const chassis = new T.Mesh(box(RAIL - 0.004, hh - 0.002, 2 * TILE - 0.07), mat('chassis' + b.known, () => new T.MeshStandardMaterial({ color: 0x202328, roughness: 0.7, metalness: 0.4, transparent: !b.known, opacity: b.known ? 1 : 0.5 })));
|
||||
const chassis = new T.Mesh(box(RAIL - 0.004, hh - 0.002, 2 * TILE - 0.07), mat('chassis', () => new T.MeshStandardMaterial({ color: 0x202328, roughness: 0.7, metalness: 0.4 })));
|
||||
chassis.position.set(0, y, -0.006);
|
||||
g.add(chassis);
|
||||
// the back: PSUs, the BMC port and the network cards with their ports
|
||||
|
||||
@@ -218,7 +218,7 @@ function pageRoom(siteName, roomArg) {
|
||||
${SITE.edit ? `<div class="actions" style="margin-bottom:8px"><button class="btn sm" id="rk-rotate">Rotate</button></div>` : ''}
|
||||
${rackElev(selRack)}${cablingTable(site, selRack)}</div>`
|
||||
: `<div class="card floor-side"><span class="muted">Click a rack for its front.${autoN ? `<br><br>${autoN} rack(s) have no place yet and are laid out automatically (${v3 ? 'see-through' : 'dashed'}): Edit layout to place them.` : ''}</span>${v3 ? switchTable() : ''}</div>`;
|
||||
const legend = v3 ? `<span>drag: turn · right or shift drag: move · wheel: zoom · double click: go to a rack · a click on a selected rack\'s server opens it · see-through: height unknown${frontView.get() === 'real' ? ' · fronts: the drive bays of the model, the strip on the left ear is the state' : ''}${cablesView.get() ? ' · the cables run at the back: Turn around' : ''}</span>`
|
||||
const legend = v3 ? `<span>drag: turn · right or shift drag: move · wheel: zoom · double click: go to a rack · a click on a selected rack\'s server opens it · amber outline: height unknown (rack_units)${frontView.get() === 'real' ? ' · fronts: the drive bays of the model, the strip on the left ear is the state' : ''}${cablesView.get() ? ' · the cables run at the back: Turn around' : ''}</span>`
|
||||
: `<span><b class="lg vacant"></b>empty</span><span>thick edge: the front · bar: units used${SITE.edit ? ' · drag to move, select and Rotate' : ''}</span>`;
|
||||
return `<div class="page-head"><h1><a href="#/sites" class="muted" style="font-weight:400">Sites</a> / <a href="#/sites/${encodeURIComponent(site.name)}" class="muted" style="font-weight:400">${esc(site.name)}</a> / ${esc(room || '(no room)')}</h1>
|
||||
<span class="sub">${racks.length} racks · floor tiles of 600 mm</span>
|
||||
@@ -333,7 +333,8 @@ async function editCable(id, port) {
|
||||
|
||||
// switchTable: the switches this room's cables lead to, where they stand
|
||||
function switchTable() {
|
||||
const sws = (typeof R3 !== 'undefined' && R3.switches) || []; if (!sws.length) return '';
|
||||
const sws = (typeof R3 !== 'undefined' && R3.switches) || [];
|
||||
if (!sws.length) return '<p class="muted" style="margin-top:12px">No cabling known in this room: the machines\' LLDP saw no switch, and NetBox records no cables for them. Cables can be entered per port (select a rack, ✎ in its cabling list).</p>';
|
||||
const where = s => s.away ? `in ${esc(s.away)}` : s.at ? `${esc(rackLabel(s.at.rack))} U${s.at.unit}${s.at.estimated ? ' <span class="warn">estimated</span>' : ` <span class="muted">${esc(s.source)}</span>`}` : '';
|
||||
return `<h3 style="margin-top:12px">Switches</h3><div class="sw-list">${sws.map(s => `<div class="sw-row"><b class="sw-dot" style="background:${esc(s.color)}"></b>
|
||||
<div><div><b>${esc(s.name)}</b>${s.role === 'oob' ? ' <span class="pill tag" title="out-of-band: BMC ports">OOB</span>' : ''}${s.model ? ` <span class="muted">${esc(s.model)}</span>` : ''} ${s.source === 'netbox' ? nbLink('/dcim/devices/' + nbQ({ name: s.name }), '↗') : ''}</div><div class="muted">${where(s)} · ${s.here} cables</div></div>
|
||||
|
||||
Reference in new issue
Block a user