d8cb0dc06d
Web tool for designing nickel/copper busbars over cylindrical-cell battery packs (21700, 18650) in hex holders. Flask + build123d backend exports STEP/DXF/SVG; vanilla JS frontend with live preview, multi-project SQLite persistence, snapshot history. Deploy scripts in deploy/ (proxmox-lxc.sh, install.sh, update.sh).
197 lines
8.2 KiB
Markdown
197 lines
8.2 KiB
Markdown
# Busbar Designer
|
||
|
||
Web tool for designing nickel/copper **busbars** over cylindrical-cell battery packs (21700, 18650, ...) built with hex-shaped cell holders.
|
||
|
||
Workflow:
|
||
1. Import cell-center coordinates (paste from OpenSCAD console, CSV, JSON — or generate from `cell_dia / wall / rows / cols`).
|
||
2. View cells on a 2D canvas at real mm scale.
|
||
3. Click cells to select; group selected cells into named **busbars** (parallel groups), then chain busbars in series.
|
||
4. Each busbar is drawn as a strip of configurable width with circular pads + welding-window holes over each cell center.
|
||
5. Export busbar geometry to **STEP** (mandatory), plus **DXF** and **SVG** for laser/waterjet cutting.
|
||
|
||
## Quick start
|
||
|
||
Requires **Python 3.10+**. On Windows / PowerShell:
|
||
|
||
```powershell
|
||
cd busbar-designer
|
||
python -m venv .venv
|
||
.\.venv\Scripts\Activate.ps1
|
||
pip install -r requirements.txt
|
||
python app.py
|
||
```
|
||
|
||
Then open <http://localhost:5000> in your browser.
|
||
|
||
> First-time install pulls **build123d** (~ 200 MB; bundles OpenCASCADE via OCP) and may take several minutes.
|
||
|
||
### Linux / macOS
|
||
|
||
```bash
|
||
cd busbar-designer
|
||
python3 -m venv .venv
|
||
source .venv/bin/activate
|
||
pip install -r requirements.txt
|
||
python app.py
|
||
```
|
||
|
||
## Geometry source of truth
|
||
|
||
All cell-center math matches Addy's `Hex-Cell-Holder/hex_cell.scad`:
|
||
|
||
| Variable | Formula | 21700 default |
|
||
|-----------------|------------------------------------------|---------------|
|
||
| `hex_w` | `cell_dia + 2*wall` | `22.8 mm` |
|
||
| `hex_pt` | `hex_w / 2 / cos(30°)` | `13.164 mm` |
|
||
| `opening_dia` | `cell_dia − 2*cell_top_overlap` | `15.2 mm` |
|
||
| row pitch (Y) | `1.5 * hex_pt` | `19.746 mm` |
|
||
| col pitch (X) | `hex_w` | `22.8 mm` |
|
||
| `rect` row shift | `0` (even) / `0.5*hex_w` (odd) | `11.4 mm` |
|
||
|
||
## Import formats
|
||
|
||
The importer auto-detects:
|
||
|
||
- **OpenSCAD ECHO**: `ECHO: "Cell 1: x = 0.0, y = 0.0"` (paste straight from console).
|
||
- If your `hex_cell.scad` doesn't print these, drop `scad_snippet.scad` (in this repo) at the bottom of the file.
|
||
- **CSV**: `index,x,y` (header optional).
|
||
- **JSON**: `[{"id":1,"x":0,"y":0}, ...]` or `[[x,y], ...]`.
|
||
- **Generator** tab: enter `cell_dia / wall / rows / cols / pack_style` — coordinates are computed with the exact OpenSCAD formulas.
|
||
|
||
## Persistence
|
||
|
||
State is stored in a SQLite file (`data/busbar.db` by default; override with `BUSBAR_DB` env var):
|
||
|
||
| Table | What |
|
||
|-------------|------------------------------------------------------------------------|
|
||
| `projects` | Full editor state (cells + busbars + params) per project |
|
||
| `presets` | Named param bundles you save from the params panel |
|
||
| `snapshots` | Auto-history per project; max `SNAPSHOT_RETENTION` (default 20) kept |
|
||
|
||
**REST API** (all JSON):
|
||
|
||
| Method | Path | What |
|
||
|--------|-----------------------------------------------|-------------------------------|
|
||
| GET | `/api/projects` | list projects |
|
||
| POST | `/api/projects` | create `{name, data}` |
|
||
| GET | `/api/projects/{id}` | full project |
|
||
| PUT | `/api/projects/{id}` | update `{name?, data?, snapshot?, note?}` |
|
||
| DELETE | `/api/projects/{id}` | delete (cascades to snapshots) |
|
||
| GET | `/api/projects/{id}/snapshots` | list snapshots |
|
||
| GET | `/api/snapshots/{sid}` | full snapshot |
|
||
| POST | `/api/snapshots/{sid}/restore` | roll project back |
|
||
| GET | `/api/presets` | list presets |
|
||
| POST | `/api/presets` | create `{name, params}` |
|
||
| PUT | `/api/presets/{id}` | update |
|
||
| DELETE | `/api/presets/{id}` | delete |
|
||
|
||
**UI behaviour**:
|
||
- Active project ID is in the URL: `?p=42`. Bookmark or open from another device → same state.
|
||
- Auto-save fires 1.5 s after the last change. The first save in any 60 s window also creates a history snapshot.
|
||
- "History" button shows the snapshot list with a Restore action.
|
||
- Presets are saved server-side and apply to any project with one click.
|
||
|
||
**Backup**: the entire DB is one file. Copy `data/busbar.db` (or the `data/` folder in Docker) for offline backup.
|
||
|
||
## Export
|
||
|
||
| Format | Use | Backend |
|
||
|--------|---------------------------------------------|--------------------------|
|
||
| STEP | CAD (FreeCAD, Fusion, SolidWorks) | `build123d` → OpenCASCADE |
|
||
| DXF | Laser cutter / CNC (2D) | `build123d` → ezdxf |
|
||
| SVG | Quick preview, illustrator | `build123d` |
|
||
|
||
STEP files are written as **flat 3D faces** (not solids) so a fab shop can lay them out before cutting. If you need a thick solid (e.g., 0.2 mm nickel) toggle "Extrude busbars" — the backend will extrude each strip to the configured thickness.
|
||
|
||
## Project layout
|
||
|
||
```
|
||
busbar-designer/
|
||
├── app.py # Flask server: static files + /api/export/{step,dxf,svg}
|
||
├── busbar_export.py # build123d → STEP / DXF / SVG
|
||
├── requirements.txt
|
||
├── scad_snippet.scad # paste into hex_cell.scad to echo per-cell coordinates
|
||
├── CLAUDE.md # architecture notes for Claude / future contributors
|
||
├── tests/
|
||
│ └── test_export.py
|
||
└── static/
|
||
├── index.html
|
||
├── styles.css
|
||
└── js/
|
||
├── app.js
|
||
├── importer.js
|
||
├── viewport.js
|
||
├── groups.js
|
||
├── geometry.js
|
||
└── exporter.js
|
||
```
|
||
|
||
## Deploy to a home server
|
||
|
||
Three supported paths — pick one. Full docs in [`deploy/README.md`](deploy/README.md).
|
||
|
||
### Option A — Proxmox VE (one-liner from the PVE host)
|
||
|
||
```bash
|
||
bash -c "$(curl -fsSL https://gitea.local/me/busbar-designer/raw/branch/main/deploy/proxmox-lxc.sh)"
|
||
```
|
||
|
||
Creates a Debian 12 LXC and installs the service inside. Whiptail prompts for container ID, hostname, disk/CPU/RAM, network, repo URL, branch. Defaults to 4 GB disk, 2 cores, 1 GB RAM, dhcp.
|
||
|
||
### Option B — Inside an existing Debian/Ubuntu (LXC, VM, bare)
|
||
|
||
```bash
|
||
REPO_URL=https://gitea.local/me/busbar-designer.git \
|
||
bash -c "$(curl -fsSL https://gitea.local/me/busbar-designer/raw/branch/main/deploy/install.sh)"
|
||
```
|
||
|
||
Sets up the venv, installs deps, drops a systemd unit, starts the service. Update with `bash /opt/busbar-designer/deploy/update.sh`.
|
||
|
||
### Option C — Docker
|
||
|
||
```bash
|
||
git clone <repo> busbar-designer && cd busbar-designer
|
||
mkdir -p data
|
||
docker compose up -d --build
|
||
```
|
||
|
||
The `data/` folder is mounted into the container at `/app/data` so the SQLite DB survives `docker compose down`.
|
||
|
||
### Reverse proxy (optional)
|
||
|
||
If you want it behind your existing nginx / Caddy / Traefik:
|
||
|
||
```nginx
|
||
location / {
|
||
proxy_pass http://127.0.0.1:5000;
|
||
proxy_set_header Host $host;
|
||
proxy_set_header X-Forwarded-For $remote_addr;
|
||
proxy_read_timeout 120s; # STEP export can take a few seconds
|
||
client_max_body_size 5m;
|
||
}
|
||
```
|
||
|
||
For Caddy:
|
||
|
||
```
|
||
busbar.example.com {
|
||
reverse_proxy 127.0.0.1:5000
|
||
}
|
||
```
|
||
|
||
### Security
|
||
|
||
The app has no authentication. Either:
|
||
- expose only on your LAN (default), or
|
||
- put it behind an authenticating reverse proxy (Caddy `basic_auth`, Authelia, Pocket-ID, etc.).
|
||
|
||
## Troubleshooting
|
||
|
||
- **`pip install build123d` fails on Windows / Python 3.13** — the prebuilt OCP wheels lag the latest Python release. If your `python --version` is 3.13, install Python 3.12 from python.org and use it for the venv: `py -3.12 -m venv .venv`.
|
||
- **STEP opens empty in FreeCAD** — make sure at least one busbar exists and contains ≥ 1 cell. The exporter writes only assigned busbars.
|
||
- **Port 5000 in use** — set `PORT=5050 python app.py`.
|
||
|
||
## License
|
||
|
||
MIT.
|