rushabhcodes/ESP32-E-Reader

A compact ESP32-C3 e-reader PCB integrating LiPo charging/protection, USB-C, 3.3 V regulation, e-paper display bias generation, microSD storage, and tactile controls.

Version
1.0.85
License
unset
Stars
3

scripts/build-enclosure-inventory.py

"""Join tsci PCB placements with modeled component envelopes for case review."""

import csv
import json
import math
from pathlib import Path

ROOT = Path(__file__).resolve().parents[1]
DATA = json.loads((ROOT / 'dist/index/circuit.json').read_text())
SOURCE = {x['source_component_id']: x['name'] for x in DATA if x['type'] == 'source_component'}
MODELS = {x['model_name']: x for x in csv.DictReader((ROOT / 'assets/enclosure/component-envelopes.csv').open())}
OUTPUT = ROOT / 'assets/enclosure/component-inventory.csv'


def usb_obj_bounds():
    cad = next(x for x in DATA if x['type'] == 'cad_component' and x.get('source_component_id') == next(
        source_id for source_id, name in SOURCE.items() if name == 'J1'))
    pos, origin = cad['position'], cad['model_origin_position']
    angle = math.radians(cad['rotation']['z'])
    cosine, sine = math.cos(angle), math.sin(angle)
    coords = []
    for line in (ROOT / cad['model_obj_url'].removeprefix('./')).read_text().splitlines():
        if line.startswith('v '):
            x, y, z = (float(value) for value in line.split()[1:4])
            x, y, z = x - origin['x'], y - origin['y'], z - origin['z']
            coords.append((pos['x'] + cosine*x - sine*y,
                           pos['y'] + sine*x + cosine*y,
                           pos['z'] + z))
    return [round(max(p[i] for p in coords) - min(p[i] for p in coords), 3) for i in (0, 1)] + [
        round(min(p[2] for p in coords), 3), round(max(p[2] for p in coords), 3)]


J1_BOUNDS = usb_obj_bounds()

with OUTPUT.open('w', newline='') as file:
    w = csv.writer(file, lineterminator='\n')
    w.writerow(['refdes', 'pcb_x_mm', 'pcb_y_mm', 'side', 'pcb_footprint_width_mm', 'pcb_footprint_height_mm',
                'model_width_mm', 'model_length_mm', 'model_z_min_mm', 'model_z_max_mm', 'source'])
    for p in sorted((x for x in DATA if x['type'] == 'pcb_component'), key=lambda x: SOURCE[x['source_component_id']]):
        name = SOURCE[p['source_component_id']]
        model = MODELS.get(name)
        dims = ['', '', '', ''] if model is None else [
            round(float(model['x_max_mm'])-float(model['x_min_mm']), 3),
            round(float(model['y_max_mm'])-float(model['y_min_mm']), 3),
            model['z_min_mm'], model['z_max_mm'],
        ]
        if name == 'J1':
            dims = J1_BOUNDS
        w.writerow([name, round(p['center']['x'], 3), round(p['center']['y'], 3), p['layer'],
                    round(p['width'], 3), round(p['height'], 3), *dims,
                    'HRO supplier OBJ, nominal' if name == 'J1' else
                    'tsci tessellated model, nominal' if model else 'PCB footprint only; no 3D body model'])
print(OUTPUT)