driveAtlas: redraw large1 as a Rosewill RSV-L4500U cutaway
Lint / Shell (shellcheck) (push) Successful in 9s

large1 lives in a Rosewill RSV-L4500U 4U case, not a generic "3x5 grid".
The old render was a plain placeholder that said little about the machine.

This chassis is awkward to draw honestly: the drives are NOT visible from
the front. The full-width perforated door drops down to reveal a wall of
120mm fans, and the 15 bays sit behind it in three removable 5-drive
modules serviced by taking the TOP COVER off. So a literal front elevation
would show zero drives.

Draw a front elevation with the fan wall and dust door CUT AWAY instead:
the chassis silhouette stays recognisable - rack ears carrying the big
vertical D-handles, the perforated drop-down door with its centred barrel
lock, the slim right-hand I/O strip - while the cutaway (dashed tear edges,
ghosted arcs of the outer two 120mm fans) exposes all 15 bays.

Geometry per Rosewill's manual: 3 modules side by side, 5 drives stacked
FLAT in each, long axis front-to-back with connectors facing the rear. So
from the front every bay is a WIDE, SHORT slot and the grid reads 5 rows x
3 columns - the inverse of the Sliger's tall on-edge slots.

The chassis documents no bay numbering and has no backplane and no per-bay
LEDs, so numbering is stated in the output as a driveAtlas LOCAL convention
(1-5 left module top->bottom, 6-10 middle, 11-15 right) with a reminder to
identify a drive by serial, plus a note that service is top-cover-off.

Fixed 96-column canvas, identical display width on every line in monochrome
and colour, colour runs coalesced (max 17 escapes per line, never changing
inside a repeated glyph run), SAFE width-1 glyphs only.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-20 22:55:16 -04:00
co-authored by Claude Opus 4.8
parent e474ec546c
commit d5e476fb72
+167 -32
View File
@@ -828,44 +828,179 @@ generate_nuc_layout() {
#------------------------------------------------------------------------------ #------------------------------------------------------------------------------
# generate_large1_layout # generate_large1_layout
# #
# Generates ASCII art representation of a large1 chassis (Rosewill RSV-L4500U). # Rosewill RSV-L4500U (4U, 427x178x635 mm) -- FRONT ELEVATION with the 3x 120 mm
# Shows storage controllers, 2 M.2 NVMe slots, and 15 front bays in 3x5 grid. # fan wall and the perforated drop-down door CUT AWAY, exposing all 15 internal
# 3.5" bays: 3 removable modules side by side, 5 drives stacked FLAT in each, so
# from the front every bay reads as a WIDE, SHORT slot -> a 5-row x 3-column grid.
# Rack ears with D-handles at both ends; slim I/O strip on the right.
#
# Bay numbering is a driveAtlas LOCAL convention (the chassis documents none):
# 1-5 = LEFT module top->bottom, 6-10 = MIDDLE, 11-15 = RIGHT.
# #
# Args: # Args:
# $1 - Hostname to display in the layout header # $1 - Hostname to display in the layout header
# #
# Side effects: Calls build_drive_map() to populate DRIVE_MAP # Side effects: calls build_drive_map() (populates DRIVE_MAP) then da_color_init.
#------------------------------------------------------------------------------ # Every emitted line is exactly 96 display columns wide, in colour or mono.
#
# The art printfs embed our own ANSI colour vars (DA_*) and literal box-drawing;
# device names are always passed as %s args and pre-truncated, so SC2059 does
# not apply here.
# shellcheck disable=SC2059
generate_large1_layout() { generate_large1_layout() {
local hostname="$1" local host="${1:-unknown}"
build_drive_map
# large1 has 3 stacks of 5 bays at front (15 total) + 2 M.2 slots if declare -F build_drive_map >/dev/null 2>&1; then
# Physical bay mapping TBD - current mapping is by controller order build_drive_map || true # log_* goes to stderr; stdout stays clean
printf "┌─────────────────────────────────────────────────────────────────────────┐\n" fi
printf "│ %-69s │\n" "$hostname - Rosewill RSV-L4500U (15x 3.5\" Bays)" da_color_init
printf "│ │\n"
printf "│ Storage Controllers: │\n" # ---- local helpers ---------------------------------------------------
while IFS= read -r ctrl; do _l1_rep() { # _l1_rep GLYPH N
[[ -n "$ctrl" ]] && printf "│ %-69.69s│\n" "$ctrl" if declare -F da_repeat >/dev/null 2>&1; then
done < <(get_storage_controllers) da_repeat "$1" "$2"
printf "│ │\n" else
printf "│ M.2 NVMe: M1: %-10s M2: %-10s │\n" "${DRIVE_MAP[m2-1]:-EMPTY}" "${DRIVE_MAP[m2-2]:-EMPTY}" local g="$1" n="$2" o=''
printf "│ │\n" while [ "$n" -gt 0 ]; do o+="$g"; n=$((n - 1)); done
printf "│ Front Bays (3 stacks x 5 rows): [slot 5 of each stack MUST stay empty] │\n" printf '%s' "$o"
printf "│ Stack A Stack B Stack C │\n" fi
printf "│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │\n" }
printf "│ │1:%-8s│ │2:%-8s│ │3:%-8s│ │\n" "${DRIVE_MAP[1]:-EMPTY}" "${DRIVE_MAP[2]:-EMPTY}" "${DRIVE_MAP[3]:-EMPTY}" # DRIVE_MAP may be undeclared, empty, or (defensively) not associative
printf "│ ├──────────┤ ├──────────┤ ├──────────┤ │\n" local has_map=0
printf "│ │4:%-8s│ │5:%-8s│ │6:%-8s│ │\n" "${DRIVE_MAP[4]:-EMPTY}" "${DRIVE_MAP[5]:-EMPTY}" "${DRIVE_MAP[6]:-EMPTY}" case "$(declare -p DRIVE_MAP 2>/dev/null)" in *'declare -A'*) has_map=1 ;; esac
printf "│ ├──────────┤ ├──────────┤ ├──────────┤ │\n" _l1_map() { # _l1_map KEY -> value or ''
printf "│ │7:%-8s│ │8:%-8s│ │9:%-8s│ │\n" "${DRIVE_MAP[7]:-EMPTY}" "${DRIVE_MAP[8]:-EMPTY}" "${DRIVE_MAP[9]:-EMPTY}" [ "$has_map" = 1 ] || return 0
printf "│ ├──────────┤ ├──────────┤ ├──────────┤ │\n" printf '%s' "${DRIVE_MAP[$1]:-}"
printf "│ │10:%-7s│ │11:%-7s│ │12:%-7s│ │\n" "${DRIVE_MAP[10]:-EMPTY}" "${DRIVE_MAP[11]:-EMPTY}" "${DRIVE_MAP[12]:-EMPTY}" }
printf "│ ├──────────┤ ├──────────┤ ├──────────┤ │\n" _l1_txt() { # _l1_txt TEXT [COLOUR] -- plain 96-col line
printf "│ │13:%-7s│ │14:%-7s│ │15:%-7s│ │\n" "${DRIVE_MAP[13]:-EMPTY}" "${DRIVE_MAP[14]:-EMPTY}" "${DRIVE_MAP[15]:-EMPTY}" printf ' %s%-92.92s%s \n' "${2:-}" "$1" "${DA_RST}"
printf "│ └──────────┘ └──────────┘ └──────────┘ │\n" }
printf "└─────────────────────────────────────────────────────────────────────────┘\n" _l1_band() { # _l1_band TEXT WIDTH -- text centred in ▒
local t="$1" w="$2" l r
[ "${#t}" -gt "$w" ] && t="${t:0:w}"
l=$(( (w - ${#t}) / 2 )); r=$(( w - ${#t} - l ))
printf '%s%s%s' "$(_l1_rep '▒' "$l")" "$t" "$(_l1_rep '▒' "$r")"
}
# ---- geometry --------------------------------------------------------
# 96 cols = 2 pad + 5 ear + 1 + 80 body + 1 + 5 ear + 2 pad
# interior = 78 = 6 fan sliver + 1 tear + 56 grid + 1 tear + 6 fan + 8 I/O
# grid 56 = 3 modules of 18 (16-col bay cell + 2 borders) + 2 gaps
local IW=78
local i n p v rule16
rule16="$(_l1_rep '─' 16)"
# ---- bay cells: ONE colour run each, no churn inside a glyph run ------
local -a cells=()
for ((i = 1; i <= 15; i++)); do
v="$(_l1_map "$i")"
printf -v n '%2d' "$i"
if [ -n "$v" ]; then
printf -v p '%-12.12s' "$v"
cells[i]="${DA_OK}${n} ${p}${DA_ST}"
else
printf -v p '%-12.12s' 'empty'
cells[i]="${DA_DIM}${n} ${p}${DA_ST}"
fi
done
# ---- module box pieces (18 wide) -------------------------------------
local mtop="${rule16}" mmid="${rule16}" mbot="${rule16}"
local gtop="${mtop} ${mtop} ${mtop}"
local gmid="${mmid} ${mmid} ${mmid}"
local gbot="${mbot} ${mbot} ${mbot}"
# ---- ghosted 120 mm fan rings, sliced open by the cutaway -------------
local -a fanL=(
' ╭─' ' ╭─╯ ' ' ╭╯ ' ' │ ' ' │ ' ' │ '
' │ ' ' │ ' ' ╰╮ ' ' ╰─╮ ' ' ╰─'
)
local -a fanR=(
'─╮ ' ' ╰─╮ ' ' ╰╮ ' ' │ ' ' │ ' ' │ '
' │ ' ' │ ' ' ╭╯ ' ' ╭─╯ ' '─╯ '
)
# ---- slim front I/O strip (right-hand edge of the front face) --------
local -a io=(
' ╭────╮ ' ' │I/O │ ' ' │▄pwr│ ' ' │▄rst│ ' ' │▀led│ ' ' ├────┤ '
' │▄▄▄▄│ ' ' │USB3│ ' ' │▄▄▄▄│ ' ' │USB3│ ' ' ╰────╯ '
)
# ---- banner, door band, top-cover caption ----------------------------
local ban=' FAN WALL + DUST DOOR CUT AWAY - 15 BAYS EXPOSED ' banner lp rp
lp=$(( (IW - ${#ban}) / 2 )); rp=$(( IW - ${#ban} - lp ))
banner="$(_l1_rep '╌' "$lp")${ban}$(_l1_rep '╌' "$rp")"
# perforated door band: two captions in a field of punched holes, keyed
# barrel lock dead centre on the door's lower edge
local door dlw=$(( (IW - 3) / 2 ))
door="$(_l1_band ' perforated drop-down door ' "$dlw")"
door="${door}${DA_HL}▐█▌${DA_DIM}$(_l1_band ' washable dust filter ' $(( IW - dlw - 3 )))"
local tlbl='[ TOP COVER - modules release from above ]' ttop
ttop="┌──${tlbl}$(_l1_rep '─' $(( IW - 2 - ${#tlbl} )))"
# ---- header ----------------------------------------------------------
local hp mp
printf -v hp '%-38.38s' "$host"
printf -v mp '%40.40s' 'Rosewill RSV-L4500U 4U 15x 3.5" bays'
printf '%s\n' " ${DA_CYN}driveAtlas${DA_ST} :: ${DA_HL}${hp}${DA_SR}${mp}${DA_RST} "
_l1_txt 'FRONT ELEVATION, fan wall cut away - 5 bays tall x 3 modules wide, drives lie FLAT' "${DA_DIM}"
printf '%96s\n' ''
# ---- chassis: rack ears carry the big vertical D-handles -------------
local eartop='┌───┐' earhole='│ ▄ │' earcapT='│╭─╮│' earbody='││ ││'
local earcapB='│╰─╯│' earbot='└───┘'
local labs l1 l2 l3
printf -v l1 '%*s%-*s' 5 '' 13 'LEFT 1-5'
printf -v l2 '%*s%-*s' 3 '' 15 'MIDDLE 6-10'
printf -v l3 '%*s%-*s' 3 '' 15 'RIGHT 11-15'
labs="${l1} ${l2} ${l3}"
local d grid E
printf '%s\n' " ${DA_HL}${eartop}${DA_SR} ${ttop} ${DA_HL}${eartop}${DA_RST} "
printf '%s\n' " ${DA_HL}${earhole}${DA_SR}${DA_DIM}${banner}${DA_ST}${DA_HL}${earhole}${DA_RST} "
printf '%s\n' " ${DA_HL}${earcapT}${DA_SR}${DA_DIM}${DA_HL}${labs}${DA_DIM}${DA_ST}${DA_HL}${earcapT}${DA_RST} "
for ((d = 0; d < 11; d++)); do
case "$d" in
0) grid="$gtop" ;;
10) grid="$gbot" ;;
1|3|5|7|9)
i=$(( (d + 1) / 2 ))
grid="${cells[$i]}│ │${cells[$(( i + 5 ))]}│ │${cells[$(( i + 10 ))]}" ;;
*) grid="$gmid" ;;
esac
E="${DA_HL}${earbody}${DA_SR}"
printf '%s\n' " ${E}${DA_DIM}${fanL[$d]}${DA_ST}${grid}${DA_DIM}${fanR[$d]}${io[$d]}${DA_ST}${E}${DA_RST} "
done
printf '%s\n' " ${DA_HL}${earcapB}${DA_SR}${DA_DIM}$(_l1_rep '╌' "$IW")${DA_ST}${DA_HL}${earcapB}${DA_RST} "
printf '%s\n' " ${DA_HL}${earhole}${DA_SR}${DA_DIM}${door}${DA_ST}${DA_HL}${earhole}${DA_RST} "
printf '%s\n' " ${DA_HL}${earbot}${DA_SR}$(_l1_rep '─' "$IW")${DA_HL}${earbot}${DA_RST} "
printf '%96s\n' ''
# ---- legend + the three things you must know -------------------------
local m1 m2 extra m1p m2p exp
m1="$(_l1_map m2-1)"; [ -n "$m1" ] || m1='-'
m2="$(_l1_map m2-2)"; [ -n "$m2" ] || m2='-'
extra=''
for ((i = 1; i <= 6; i++)); do
v="$(_l1_map "int-$i")"
[ -n "$v" ] && extra="${extra}int-${i} ${v} "
done
[ -n "$extra" ] || extra='-'
printf -v m1p '%-12.12s' "$m1"
printf -v m2p '%-12.12s' "$m2"
printf -v exp '%-29.29s' "$extra"
printf '%s\n' " ${DA_ST}LEGEND ${DA_OK}${DA_ST} drive present ${DA_DIM}${DA_ST} bay empty$(_l1_rep ' ' 50)${DA_RST} "
printf '%s\n' " ${DA_ST}NOT IN GRID m2-1 ${DA_HL}${m1p}${DA_SR} m2-2 ${DA_HL}${m2p}${DA_SR} internal ${DA_HL}${exp}${DA_RST} "
_l1_txt 'BAYS numbering is a driveAtlas LOCAL convention (the chassis documents none): 1-5' "${DA_ST}"
_l1_txt ' LEFT module top->bottom, 6-10 MIDDLE, 11-15 RIGHT -- identify a drive by SERIAL.' "${DA_ST}"
_l1_txt 'NOTE no backplane, no per-bay LEDs, not hot-swap: every drive is cabled direct to the' "${DA_DIM}"
_l1_txt ' board/HBA, and is serviced by removing the TOP COVER, not through the front door.' "${DA_DIM}"
unset -f _l1_rep _l1_map _l1_txt _l1_band
} }
#------------------------------------------------------------------------------ #------------------------------------------------------------------------------