Metadata-Version: 2.1
Name: dynos-sentry-domain
Version: 0.1.4
Summary: Public Sentry-vehicle survey domain for DYNOS
License: Apache-2.0
Requires-Python: >=3.8
Description-Content-Type: text/markdown
Requires-Dist: dynos-client>=0.1.3

# dynos-sentry-domain

The Sentry-AUV survey vocabulary for DYNOS. Mostly `Zone`, `Coordinate`, `survey`,
`goto`, `full_coverage_of`. Importing this package gives you the words to use
when constructing goals for a Sentry mission.

The package is backend-agnostic. It only declares the public domain. The
hardware extensions (thrusters, weights, descent sequencing, control modes)
live behind the backend boundary and are not shipped on PyPI; the planner knows
about them when it runs, but your code never references them.

Treat this repository like a static API for Sentry. `dynos-client` lets you
talk to a backend, `dynos-sentry-domain` gives you the words to use. You
usually install both together.

## Install

```bash
pip install dynos-sentry-domain
```

Pulls in `dynos-client` (and `dynos-core`) transitively.

## Concepts

A short refresher of `dynos-core`:

- Symbolic state is a set of named facts (fluents) about typed objects.
  `at(wp_end)` and `phase_at_depth()` are atoms; the current world is the set
  of atoms that are true now.
- Goals are states not commands. You tell the system *what should be true*; the
  planner derives the action sequence that makes it true.
- Transitions are action schemas. `survey(zone)` is the symbolic side; the
  executable side is on the backend.

### `Zone`

A polygonal survey region. The planner expands `full_coverage_of(zone)` into
per-trackline `fly_trackline(...)` actions using the zone's geometry; you do
not enumerate the tracklines yourself.

| Field              | Description                                | Example                                   |
|--------------------|--------------------------------------------|-------------------------------------------|
| `vertices`         | Polygon corners as `[lon, lat]`            | `[[-70.67, 41.52], [-70.66, 41.52], ...]` |
| `coordinate_frame` | Currently only `"geographic"` is supported | `"geographic"`                            |
| `altitude`         | Survey depth (m)                           | `65.0`                                    |
| `speed`            | Vehicle speed (m/s)                        | `1.0`                                     |
| `coverage_width`   | Sensor swath width (m)                     | `170.0` (multibeam), `5.0` (camera)       |
| `robot_width`      | Vehicle width (m)                          | `0.5`                                     |
| `envelope`         | Altitude tolerance (m)                     | `20.0`                                    |

### `Coordinate`

A named waypoint. The planner uses it as a goto target. Pass the name
positionally (as with `Zone`) so the object's name is its identity -- a goal
atom like `at(wp_end)` then refers to it correctly.

```python
Coordinate("wp_end", latitude=41.525, longitude=-70.66, coordinate_frame="geographic")
```

### Lifecycle phases

Sentry has internal mission-lifecycle state (descending, at-depth, ascending,
recovered). **These are not part of the goal vocabulary and you never set
them.** The planner derives descent from your survey/goto goal automatically,
and ascent/recovery is owned by the mission bookends and the on-vehicle abort
safety system (and, when needed, a skilled operator). A client never asserts a
phase and never invokes an abort. Your goal is *what you want surveyed or
reached*, nothing about how the vehicle gets down or comes home (that gets
handled internally).

The complete client goal vocabulary is small: `full_coverage_of(zone)`,
`at(waypoint)`, and the sensing-coverage goals. That is all you express
(intentionally: it's all you need, since the backend handles the rest).

## 60-second example: a single-zone survey

```python
from dynos_client import RemoteOrchestrator
from dynos_sentry.sentry import Zone, full_coverage_of

orch = RemoteOrchestrator.from_config(timeout_s=3600)

site_alpha = Zone("site_alpha",
    coordinate_frame="geographic",
    vertices=[[-70.67, 41.52], [-70.66, 41.52],
              [-70.66, 41.525], [-70.67, 41.525]],
    altitude=70.0,
    envelope=20.0,
    speed=0.8,
    coverage_width=170.0,
    robot_width=0.5,
)
orch.create_object(site_alpha)
orch.set_goal([full_coverage_of(site_alpha)])
orch.execute_plan()
```

Your goal is just `full_coverage_of(site_alpha)`. The planner inserts the
descent for you; bringing the vehicle home is handled by the mission bookends
and the abort safety system, not by anything you put in the goal.

CLI equivalent (using a `zone.json` file):

```bash
dynos call create zone.json
dynos call goal "full_coverage_of(site_alpha)"
dynos call plan        # optional: print the action sequence
dynos call execute     # synchronous; will time out the CLI for long surveys
```

After it finishes, pull a GeoJSON of the executed survey for inspection:

```bash
dynos call geojson -o survey.geojson
```

## Goto: a goal, not a primitive

"Go to a waypoint" in DYNOS is a goal, not a command. You declare the world
state you want (`at(wp_end)`) and the planner derives the prerequisite sequence
(takeover, descent setup, the descent itself). There is deliberately no "just
go" button: routing through the planner is what keeps the symbolic world
consistent and what makes replanning on failure work. You do not add a depth or
phase term -- the planner already descends because reaching the waypoint
requires it.

```python
from dynos_sentry.sentry import Coordinate, at

# Pass the name positionally (as with Zone) so the object's name is its
# identity; the goal atom at(wp_end) then refers to it correctly.
wp_end = Coordinate("wp_end", latitude=41.525, longitude=-70.66,
                    coordinate_frame="geographic")
orch.create_object(wp_end)
orch.set_goal([at(wp_end)])
orch.execute_plan()
```

`dynos call plan` will show something like `takeover_control, setup_descent, descent_sequence(target=wp_end)`.

## Public API

| Symbol                   | Purpose                                                                           |
|--------------------------|-----------------------------------------------------------------------------------|
| `Zone`                   | The polygonal survey region (object type with `ValueField` descriptors).          |
| `Coordinate`             | A named waypoint (object type).                                                   |
| `survey`                 | Transition for surveying a zone (used as a building block of `full_coverage_of`). |
| `goto`                   | Transition for moving between coordinates.                                        |
| `full_coverage_of(zone)` | Goal-side fluent: "this zone has been fully surveyed." Use this in `set_goal`.    |
| `at(coordinate)`         | Goal-side fluent: "vehicle is at this waypoint." The goto goal.                   |
| `visited(coordinate)`    | Goal-side fluent: "vehicle has visited this waypoint."                            |
| `SurveyParams`           | Parameter dataclass for `survey`.                                                 |
| `CoordinateGotoParams`   | Parameter dataclass for `goto`.                                                   |

Lifecycle phase predicates (`phase_at_depth`, `phase_ascending`,
`phase_surface`, ...) are internal mission-sequencing state. They are not goal
vocabulary: the planner sets them while sequencing descent, and ascent/recovery
is owned by the mission bookends and the abort safety system. Do not put a
phase in a goal.

## What is intentionally not exposed

Hardware fluents (`Thruster`, `Servo`, `controller_survey`,
`bottom_follow_configured`, ...) and internal transitions (`takeover_control`,
`descent_sequence`, ...) live in the backend extension layer, not here. They
are stripped from the public release; user code must not depend on them. If you
find yourself wanting one, the right move is usually to phrase it as a goal
that the planner can satisfy, or to file an issue describing the use case.

## Next

For a complete worked example with a custom action and an end-to-end mission,
see `dynos-adaptive-resampling`. For the cross-package install / login /
first-survey walkthrough, see `user_guide.md`.
