also looking at this
feat: add scoped cargo receiving mission tasks
#102
21 files
+1186
-15
| @@ -0,0 +1,108 @@ | ||
| 1 | # Modeled cargo station handling | |
| 2 | ||
| 3 | `createCargoStationHandler` in `resources/functions/drone/cargo/station.ts` owns one | |
| 4 | existing point-parcel record through free fall, surface capture, a finite powered rail | |
| 5 | and one departure. This is a software simulation prerequisite for | |
| 6 | [distinct-vehicle cargo handover](cargo-delivery.md#remaining-issue-scope). | |
| 7 | Physical outputs remain disabled. | |
| 8 | ||
| 9 | ## Use an actual parcel | |
| 10 | ||
| 11 | Pass the parcel produced by a carrier's actual `separateCarriedLoad()` event, adding | |
| 12 | its declared id. An empty receiving body's `intakePoint()` supplies a possible rail | |
| 13 | endpoint; it does not supply a parcel. The world coordinator must remove the previous | |
| 14 | owner when it creates the station and consume the departure exactly once. Independent | |
| 15 | station instances cannot detect a caller cloning a parcel into multiple worlds. | |
| 16 | ||
| 17 | ```ts | |
| 18 | import { createCargoStationHandler } from '../resources/functions/drone/cargo/station' | |
| 19 | ||
| 20 | const station = createCargoStationHandler({ | |
| 21 | atMs: 4, | |
| 22 | parcel: { id: 'parcel-a', massKg: 0.1, positionM: [0.08, 0, -1], velocityMps: [0, 0, 0] }, | |
| 23 | pickup: { centerM: [0.08, 0, 0], radiusM: 0.1 }, | |
| 24 | destinationM: [1.08, 0, 0], | |
| 25 | maxSpeedMps: 0.5, | |
| 26 | maxAccelerationMps2: 1, | |
| 27 | }) | |
| 28 | ``` | |
| 29 | ||
| 30 | The literal parcel above is an isolated numerical example. The integration test uses | |
| 31 | real source separation and real receiving dynamics rather than manufacturing a parcel | |
| 32 | from the receiving joint. All positions, velocities and forces use fixed-origin LOCAL_NED: | |
| 33 | metres, metres per second and newtons, with down positive. Mass is kilograms and time | |
| 34 | is one modeled local clock in milliseconds. | |
| 35 | ||
| 36 | ## Lifecycle and clock | |
| 37 | ||
| 38 | The constructor captures strict own-data records and dense finite three-element arrays. | |
| 39 | Unknown fields, accessors and invalid declarations throw `cargo-station-constructor`. | |
| 40 | Later caller mutation cannot alter retained state. Bounds are: | |
| 41 | ||
| 42 | - Safe nonnegative initial epoch aligned to 4 ms, leaving 180,000 ms of safe clock range. | |
| 43 | - Parcel id matching `[a-z][a-z0-9-]{0,39}`, mass greater than zero and at most 0.25 kg. | |
| 44 | - Declared coordinates within +/-1,000 m; initial velocity components within +/-5 m/s. | |
| 45 | - Horizontal pickup radius 0.05 to 2 m. The infinite rough capture plane has down in [-1, 0] m; | |
| 46 | the parcel starts on that plane or up to 3 m above it. | |
| 47 | - Destination on or up to 1 m above the plane. Center-to-destination distance plus pickup | |
| 48 | radius is at most 20 m, with worst declared rail duration at most 120,000 ms. | |
| 49 | - Spatial speed 0.05 to 1 m/s and acceleration 0.05 to 2 m/s². | |
| 50 | ||
| 51 | Call `step(previousAtMs + 4)` to advance the actor. Repeated, skipped, backward, fractional | |
| 52 | or exhausted epochs throw `cargo-station-clock` before mutation. The lifetime is at most | |
| 53 | 180,000 ms from construction; the owning world must bound its run accordingly. | |
| 54 | ||
| 55 | Free fall uses exact constant-gravity kinematics and a quadratic within-step contact time. | |
| 56 | The ideal rough plane stops all velocity components. The retained impact contains its | |
| 57 | fractional contact epoch, actual point, incoming velocity, impulse and kinetic loss. | |
| 58 | The snapshot epoch is the enclosing 4 ms step endpoint. No force peak or impact duration | |
| 59 | is invented. A point outside the declared horizontal disc becomes `missed` at its actual | |
| 60 | contact location and cannot start transport. | |
| 61 | ||
| 62 | `start()` accepts only `held`, with enough lifetime for the full rail path. It never | |
| 63 | retries, moves a missed point onto the rail or starts during free fall. Rail motion uses | |
| 64 | a rest-to-rest quintic position law with derived duration respecting both declared spatial | |
| 65 | limits. The parcel follows the whole path; its final analytic endpoint is the declared | |
| 66 | position with zero velocity. A zero-length route still has a finite 504 ms duration. | |
| 67 | ||
| 68 | `cancel()` is one-way. A moving parcel enters `braking`, with constant deceleration at | |
| 69 | the declared acceleration limit until it stops at the physically integrated location. | |
| 70 | A falling parcel continues under gravity until plane contact. These requests return | |
| 71 | `cancelling`; `cancelled` means it was already at rest. Snapshots distinguish the request | |
| 72 | epoch, current phase and fractional stop epoch. Cancellation retains the parcel and | |
| 73 | prevents transfer; it does not restore an aircraft's authority or land a vehicle. | |
| 74 | ||
| 75 | `takeReadyParcel()` accepts only `ready`, returns a detached parcel and clears the | |
| 76 | station's owned mass. Further departure attempts are refused. The receiving body's | |
| 77 | `attachParcel()` still independently checks its actual current joint. A stale destination | |
| 78 | can therefore fail intake; the world must retain the departed parcel and an uncertain | |
| 79 | outcome rather than moving or duplicating it to make receipt succeed. | |
| 80 | ||
| 81 | ## Mechanical accounting | |
| 82 | ||
| 83 | Snapshots retain current and departed mass/energy, rail force, impact loss and work. | |
| 84 | Mechanical energy is `0.5 * mass * speed² - mass * gravity * down`. Free fall conserves | |
| 85 | that quantity to binary64 accumulation tolerance. Surface capture dissipates the incoming | |
| 86 | kinetic energy. Rail force includes gravity support; lifting increases potential energy. | |
| 87 | `stationNetWorkJ` accumulates the represented mechanical-energy change during rail motion | |
| 88 | and braking. Positive and negative work fields sum positive and signed negative **sampled | |
| 89 | energy increments**, not a continuous actuator-efficiency or power integration. | |
| 90 | ||
| 91 | The residual is `current + departed + planeDissipation - initial - stationNetWork`. | |
| 92 | It exposes numerical accumulation instead of substituting a declared battery budget. | |
| 93 | This model has no motor, gearbox, battery, measured efficiency, station recoil, parcel | |
| 94 | spin, deformation or physical mechanism qualification. | |
| 95 | ||
| 96 | ## Evidence and remaining integration | |
| 97 | ||
| 98 | `tests/unit/cargo-station.test.ts` exercises actual source/station/receiver mass transfer, | |
| 99 | finite displacement, a worked midpoint, ballistic contact time/impulse/energy, pickup miss, | |
| 100 | cancellation during rising flight and powered motion, spatial speed/acceleration bounds, | |
| 101 | vertical work, strict admission, detached state and lifetime rejection. | |
| 102 | ||
| 103 | This actor does not grant a mechanism lease, authenticate a recipient, produce a custody | |
| 104 | ACK or coordinate two live vehicle mission engines. Those operations must use each | |
| 105 | vehicle's own sensor, estimator and current authority scope on the common clock. The | |
| 106 | [receiving mission task](cargo-delivery.md#receiving-mission-task-prerequisite) now holds | |
| 107 | a declared pad under the shared engine. Its physical-intake acknowledgement port, the | |
| 108 | two-vehicle custody protocol, cancellation/uncertainty matrix and bounded fleet CLI remain open under [#25](https://github.com/stacksjs/drone/issues/25). | |