// ============================================================
// charging_dock_receiver_505.scad — Robot-Side Charging Receiver (24V)
// Issue: #505  Agent: sl-mechanical  Date: 2026-03-06
// ============================================================
//
// Robot-side contact plate that mates with the 24V charging dock pogo pins.
// Forked from Issue #159 receiver (contact geometry unchanged; 12 AWG wire bore).
// Each robot variant has a different mounting interface; the contact
// geometry is identical across all variants (same pogo pin spacing).
//
// Variants:
//   A — lab_receiver()    SaltyLab — mounts to underside of stem base ring
//   B — rover_receiver()  SaltyRover — mounts to chassis belly (M4 deck holes)
//   C — tank_receiver()   SaltyTank — mounts to skid plate / hull floor
//
// Contact geometry (common across variants):
//   2× brass contact pads, Ø12 mm × 2 mm (press-fit into PETG housing)
//   Pad spacing: 20 mm CL-to-CL (matches dock POGO_SPACING exactly)
//   Contact face Z height matches dock pogo pin Z when robot is level
//   Polarity: marked + on top pin (conventional: positive = right when
//   facing dock; negative = left) — must match dock wiring.
//
// Approach guide nose:
//   A chamfered V-nose on the forward face guides the receiver block
//   into the dock's V-funnel.  Taper half-angle ≈ 14° matches guide rails.
//   Nose width = RECV_W = 30 mm (matches dock EXIT_GAP - 2 mm clearance).
//
// Coordinate convention:
//   Z = 0 at receiver mounting face (against robot chassis/deck underside).
//   +Z points downward (toward dock floor).
//   Contact pads face +Y (toward dock back wall when docked).
//   Receiver centred on X = 0 (robot centreline).
//
// RENDER options:
//   "assembly"         all 3 receivers side by side
//   "lab_stl"          SaltyLab receiver (print 1×)
//   "rover_stl"        SaltyRover receiver (print 1×)
//   "tank_stl"         SaltyTank receiver (print 1×)
//   "contact_pad_2d"   DXF — Ø12 mm brass pad profile (order from metal shop)
//
// Export (Issue #505 24V variant):
//   openscad charging_dock_receiver_505.scad -D 'RENDER="lab_stl"'   -o receiver_505_lab.stl
//   openscad charging_dock_receiver_505.scad -D 'RENDER="rover_stl"' -o receiver_505_rover.stl
//   openscad charging_dock_receiver_505.scad -D 'RENDER="tank_stl"'  -o receiver_505_tank.stl
//   openscad charging_dock_receiver_505.scad -D 'RENDER="contact_pad_2d"' -o contact_pad_505.dxf
// ============================================================

$fn = 64;
e   = 0.01;

// ── Contact geometry (must match charging_dock.scad) ─────────────────────────
POGO_SPACING   = 20.0;   // CL-to-CL (dock POGO_SPACING)
PAD_D          = 12.0;   // contact pad OD (brass disc)
PAD_T          =  2.0;   // contact pad thickness
PAD_RECESS     =  1.8;   // pad pressed into housing (0.2 mm proud for contact)
PAD_PROUD      =  0.2;   // pad face protrudes from housing face

// ── Common receiver body geometry ────────────────────────────────────────────
RECV_W         = 30.0;   // receiver body width (X) — matches dock EXIT_GAP inner
RECV_D         = 25.0;   // receiver body depth (Y, docking direction)
RECV_H         = 12.0;   // receiver body height (Z, from mount face down)
RECV_R         =  3.0;   // corner radius
// V-nose geometry (front Y face — faces dock back wall)
NOSE_CHAMFER   = 10.0;   // chamfer depth on X corners of front face

// Polarity indicator slot (on top/mount face: + on right, - on left)
POL_SLOT_W     =  4.0;
POL_SLOT_D     =  8.0;
POL_SLOT_H     =  1.0;

// Fasteners
M2_D  = 2.4;
M3_D  = 3.3;
M4_D  = 4.3;

// ── Mounting patterns ─────────────────────────────────────────────────────────
// SaltyLab stem base ring (Ø25 mm stem, 4× M3 in ring at Ø40 mm BC)
LAB_BC_D       = 40.0;
LAB_BOLT_D     = M3_D;
LAB_COLLAR_H   = 15.0;   // collar height above receiver body

// SaltyRover deck (M4 grid pattern, 30.5×30.5 mm matching FC pattern on deck)
// Receiver uses 4× M4 holes at ±20 mm from centre (clear of deck electronics)
ROVER_BOLT_SPC = 40.0;

// SaltyTank skid plate (M4 holes matching skid plate bolt pattern)
// Uses 4× M4 at ±20 mm X, ±10 mm Y (inset from skid plate M4 positions)
TANK_BOLT_SPC_X = 40.0;
TANK_BOLT_SPC_Y = 20.0;
TANK_NOSE_L     = 20.0;   // extended nose for tank (wider hull)

// ============================================================
// RENDER DISPATCH
// ============================================================
RENDER = "assembly";

if      (RENDER == "assembly")       assembly();
else if (RENDER == "lab_stl")        lab_receiver();
else if (RENDER == "rover_stl")      rover_receiver();
else if (RENDER == "tank_stl")       tank_receiver();
else if (RENDER == "contact_pad_2d") {
    projection(cut = true) translate([0, 0, -0.5])
        linear_extrude(1) circle(d = PAD_D);
}

// ============================================================
// ASSEMBLY PREVIEW
// ============================================================
module assembly() {
    // SaltyLab receiver
    color("RoyalBlue", 0.85)
        translate([-80, 0, 0])
            lab_receiver();

    // SaltyRover receiver
    color("OliveDrab", 0.85)
        translate([0, 0, 0])
            rover_receiver();

    // SaltyTank receiver
    color("SaddleBrown", 0.85)
        translate([80, 0, 0])
            tank_receiver();
}

// ============================================================
// COMMON RECEIVER BODY
// ============================================================
// Internal helper: the shared contact housing + V-nose.
// Orientation: mount face = +Z top; contact face = +Y front.
// All variant-specific modules call this, then add their mount interface.
module _receiver_body() {
    difference() {
        union() {
            // ── Main housing block (rounded) ─────────────────────────
            linear_extrude(RECV_H)
                _recv_profile_2d();

            // ── V-nose chamfer reinforcement ribs ─────────────────────
            // Two diagonal ribs at 45° reinforce the chamfered corners
            for (sx = [-1, 1])
                hull() {
                    translate([sx*(RECV_W/2 - NOSE_CHAMFER),
                               RECV_D/2, 0])
                        cylinder(d = 3, h = RECV_H * 0.6);
                    translate([sx*(RECV_W/2), RECV_D/2 - NOSE_CHAMFER, 0])
                        cylinder(d = 3, h = RECV_H * 0.6);
                }
        }

        // ── Contact pad bores (2× Ø12 mm, press-fit) ─────────────────
        // Pads face +Y; bores from Y face into housing
        for (px = [-POGO_SPACING/2, POGO_SPACING/2])
            translate([px, RECV_D/2 + e, RECV_H/2])
                rotate([90, 0, 0]) {
                    // Pad press-fit bore
                    cylinder(d = PAD_D + 0.1,
                             h = PAD_RECESS + e);
                    // Wire bore (behind pad, to mount face)
                    translate([0, 0, PAD_RECESS])
                        cylinder(d = 3.0,
                                 h = RECV_D + 2*e);
                }

        // ── Polarity indicator slots on top face ──────────────────────
        // "+" slot: right pad (+X side)
        translate([POGO_SPACING/2, 0, -e])
            cube([POL_SLOT_W, POL_SLOT_D, POL_SLOT_H + e], center = true);
        // "-" indent: left pad (no slot = negative)

        // ── Wire routing channel (on mount face / underside) ──────────
        // Trough connecting both pad bores for neat wire run
        translate([0, RECV_D/2 - POGO_SPACING/2, RECV_H - 3])
            cube([POGO_SPACING + 6, POGO_SPACING, 4], center = true);
    }
}

// ── 2D profile of receiver body with chamfered V-nose ────────────────────────
module _recv_profile_2d() {
    hull() {
        // Rear corners (full width)
        for (sx = [-1, 1])
            translate([sx*(RECV_W/2 - RECV_R), -RECV_D/2 + RECV_R])
                circle(r = RECV_R);
        // Front corners (chamfered — narrowed by NOSE_CHAMFER)
        for (sx = [-1, 1])
            translate([sx*(RECV_W/2 - NOSE_CHAMFER - RECV_R),
                       RECV_D/2 - RECV_R])
                circle(r = RECV_R);
    }
}

// ============================================================
// PART A — SALTYLAB RECEIVER
// ============================================================
// Mounts to the underside of the SaltyLab chassis stem base ring.
// Split collar grips Ø25 mm stem; receiver body hangs below collar.
// Z height set so contact pads align with dock pogo pins when robot
// rests on flat surface (robot wheel-to-contact-pad height calibrated).
//
// Receiver height above floor: tune LAB_CONTACT_Z in firmware (UWB/ArUco
// approach). Mechanically: receiver sits ~35 mm above ground (stem base
// height), matching dock POGO_Z = 35 mm.
module lab_receiver() {
    collar_od = 46.0;   // matches sensor_rail.scad STEM_COL_OD
    collar_h  = LAB_COLLAR_H;

    union() {
        // ── Common receiver body ────────────────────────────────────
        _receiver_body();

        // ── Stem collar (split, 2 halves joined with M4 bolts) ───────
        // Only the front half printed here; rear half is mirror.
        translate([0, -RECV_D/2, RECV_H])
            difference() {
                // Half-collar cylinder
                rotate_extrude(angle = 180)
                    translate([collar_od/2 - 8, 0, 0])
                        square([8, collar_h]);

                // Stem bore clearance
                translate([0, 0, -e])
                    cylinder(d = 25.5, h = collar_h + 2*e);

                // 2× M4 clamping bolt bores (through collar flanges)
                for (cx = [-collar_od/2 + 4, collar_od/2 - 4])
                    translate([cx, 0, collar_h/2])
                        rotate([90, 0, 0])
                            cylinder(d = M4_D,
                                     h = collar_od + 2*e,
                                     center = true);
            }

        // ── M3 receiver-to-collar bolts ───────────────────────────────
        // 4× M3 holes connecting collar flange to receiver body top
        // (These are mounting holes for assembly; not holes in the part)
    }
}

// ============================================================
// PART B — SALTYOVER RECEIVER
// ============================================================
// Mounts to the underside of the SaltyRover deck plate.
// 4× M4 bolts into deck underside (blind holes tapped in deck).
// Receiver sits flush with deck belly; contact pads protrude 5 mm below.
// Dock pogo Z = 35 mm must equal ground-to-deck-belly height for rover
// (approximately 60 mm chassis clearance — shim with spacer if needed).
module rover_receiver() {
    mount_h = 5.0;   // mounting flange thickness

    union() {
        // ── Common receiver body ────────────────────────────────────
        _receiver_body();

        // ── Mounting flange (attaches to deck belly) ─────────────────
        difference() {
            translate([-(ROVER_BOLT_SPC/2 + 12),
                       -RECV_D/2 - 10,
                       RECV_H])
                cube([ROVER_BOLT_SPC + 24,
                      RECV_D + 20,
                      mount_h]);

            // 4× M4 bolt holes
            for (fx = [-1, 1]) for (fy = [-1, 1])
                translate([fx*ROVER_BOLT_SPC/2,
                           fy*(RECV_D/2 + 5),
                           RECV_H - e])
                    cylinder(d = M4_D,
                             h = mount_h + 2*e);

            // Weight-reduction pockets
            for (sx = [-1, 1])
                translate([sx*(ROVER_BOLT_SPC/4 + 6),
                           0, RECV_H + 1])
                    cube([ROVER_BOLT_SPC/2 - 4, RECV_D - 4, mount_h],
                         center = true);
        }
    }
}

// ============================================================
// PART C — SALTYTANK RECEIVER
// ============================================================
// Mounts to SaltyTank hull floor or replaces a section of skid plate.
// Extended front nose (TANK_NOSE_L) for tank's wider hull approach.
// Contact pads exposed through skid plate via a 30×16 mm slot.
// Ground clearance: tank chassis = 90 mm; dock POGO_Z = 35 mm.
// Use ramp shim (see BOM) under dock base to elevate pogo pins to 90 mm
// OR set POGO_Z = 90 in dock for a tank-specific dock configuration.
// ⚠ Cross-variant dock: set POGO_Z per robot if heights differ.
//   Compromise: POGO_Z = 60 mm with 25 mm ramp for tank, 25 mm spacer for lab.
module tank_receiver() {
    mount_h = 4.0;
    nose_l  = RECV_D/2 + TANK_NOSE_L;

    union() {
        // ── Common receiver body ────────────────────────────────────
        _receiver_body();

        // ── Extended nose for tank approach ──────────────────────────
        // Additional chamfered wedge ahead of standard receiver body
        hull() {
            // Receiver front face corners
            for (sx = [-1, 1])
                translate([sx*(RECV_W/2 - NOSE_CHAMFER), RECV_D/2, 0])
                    cylinder(d = 2*RECV_R, h = RECV_H * 0.5);
            // Extended nose tip (narrowed to 20 mm)
            for (sx = [-1, 1])
                translate([sx*10, RECV_D/2 + TANK_NOSE_L, 0])
                    cylinder(d = 2*RECV_R, h = RECV_H * 0.4);
        }

        // ── Mounting flange (bolts to tank skid plate) ────────────────
        difference() {
            translate([-(TANK_BOLT_SPC_X/2 + 10),
                       -RECV_D/2 - 8,
                       RECV_H])
                cube([TANK_BOLT_SPC_X + 20,
                      RECV_D + 16,
                      mount_h]);

            // 4× M4 bolt holes
            for (fx = [-1, 1]) for (fy = [-1, 1])
                translate([fx*TANK_BOLT_SPC_X/2,
                           fy*TANK_BOLT_SPC_Y/2,
                           RECV_H - e])
                    cylinder(d = M4_D,
                             h = mount_h + 2*e);
        }
    }
}
