From d39a031b931bf41716a958215be62f3ac0aa072f Mon Sep 17 00:00:00 2001 From: Teo Koon Peng Date: Wed, 5 Jun 2024 14:32:09 +0800 Subject: [PATCH 1/5] api-server: Support generic labels (#949) * save generic labels Signed-off-by: Teo Koon Peng * document problems with tortoise-orm Signed-off-by: Teo Koon Peng * cleanup Signed-off-by: Teo Koon Peng * cleanup Signed-off-by: Teo Koon Peng * port generic label filtering from main Signed-off-by: Teo Koon Peng * successfully ported generic label sorting Signed-off-by: Teo Koon Peng * fix test leaking to other tests Signed-off-by: Teo Koon Peng * add TODO Signed-off-by: Teo Koon Peng * update client api Signed-off-by: Teo Koon Peng * uncomment out test Signed-off-by: Teo Koon Peng * fix api call Signed-off-by: Teo Koon Peng --------- Signed-off-by: Teo Koon Peng --- packages/api-client/lib/openapi/api.ts | 86 +++++------ packages/api-client/lib/version.ts | 2 +- packages/api-client/schema/index.ts | 49 +++--- .../api-server/api_server/models/__init__.py | 1 + .../api-server/api_server/models/labels.py | 25 +++ .../api_server/models/task_booking_label.py | 26 +--- packages/api-server/api_server/query.py | 34 ++++- .../api_server/repositories/tasks.py | 47 +++--- .../api_server/routes/tasks/tasks.py | 77 +++++++--- .../api_server/routes/tasks/test_tasks.py | 142 +++++++++++++++++- .../api-server/api_server/test/__init__.py | 1 - .../api-server/api_server/test/test_client.py | 31 +--- .../api-server/api_server/test/test_data.py | 31 ++-- .../api_server/test/test_fixtures.py | 10 +- .../api-server/api_server/test_sio_auth.py | 3 +- packages/api-server/scripts/test.py | 4 - .../src/components/tasks/tasks-app.tsx | 2 + 17 files changed, 371 insertions(+), 200 deletions(-) create mode 100644 packages/api-server/api_server/models/labels.py diff --git a/packages/api-client/lib/openapi/api.ts b/packages/api-client/lib/openapi/api.ts index 6e9353747..54371dacc 100644 --- a/packages/api-client/lib/openapi/api.ts +++ b/packages/api-client/lib/openapi/api.ts @@ -727,6 +727,12 @@ export interface DeliveryAlert { */ message: string; } +/** + * + * @export + * @interface Description + */ +export interface Description {} /** * Detailed information about a task, phase, or event * @export @@ -2403,47 +2409,10 @@ export interface Task { export interface TaskBookingLabel { /** * - * @type {TaskBookingLabelDescription} + * @type {{ [key: string]: Description; }} * @memberof TaskBookingLabel */ - description: TaskBookingLabelDescription; -} -/** - * This description holds several fields that could be useful for frontend dashboards when dispatching a task, to then be identified or rendered accordingly back on the same frontend. - * @export - * @interface TaskBookingLabelDescription - */ -export interface TaskBookingLabelDescription { - /** - * - * @type {string} - * @memberof TaskBookingLabelDescription - */ - task_definition_id: string; - /** - * - * @type {number} - * @memberof TaskBookingLabelDescription - */ - unix_millis_warn_time?: number; - /** - * - * @type {string} - * @memberof TaskBookingLabelDescription - */ - pickup?: string; - /** - * - * @type {string} - * @memberof TaskBookingLabelDescription - */ - destination?: string; - /** - * - * @type {string} - * @memberof TaskBookingLabelDescription - */ - cart_id?: string; + description: { [key: string]: Description }; } /** * Response to a request to cancel a task @@ -9529,15 +9498,16 @@ export const TasksApiAxiosParamCreator = function (configuration?: Configuration }; }, /** - * + * Note that sorting by `pickup` and `destination` is mutually exclusive and sorting by either of them will filter only tasks which has those labels. * @summary Query Task States * @param {string} [taskId] comma separated list of task ids * @param {string} [category] comma separated list of task categories * @param {string} [requester] comma separated list of requester names - * @param {string} [pickup] comma separated list of pickup names - * @param {string} [destination] comma separated list of destination names + * @param {string} [pickup] comma separated list of pickup names. [deprecated] use `label` instead + * @param {string} [destination] comma separated list of destination names, [deprecated] use `label` instead * @param {string} [assignedTo] comma separated list of assigned robot names * @param {string} [status] comma separated list of statuses + * @param {string} [label] comma separated list of labels, each item must be in the form <key>=<value>, multiple items will filter tasks with all the labels * @param {string} [requestTimeBetween] The period of request time to fetch, in unix millis. This must be a comma separated string, \'X,Y\' to fetch between X millis and Y millis inclusive. Example: \"1000,2000\" - Fetches logs between unix millis 1000 and 2000. * @param {string} [startTimeBetween] The period of starting time to fetch, in unix millis. This must be a comma separated string, \'X,Y\' to fetch between X millis and Y millis inclusive. Example: \"1000,2000\" - Fetches logs between unix millis 1000 and 2000. * @param {string} [finishTimeBetween] The period of finishing time to fetch, in unix millis. This must be a comma separated string, \'X,Y\' to fetch between X millis and Y millis inclusive. Example: \"1000,2000\" - Fetches logs between unix millis 1000 and 2000. \"-60000\" - Fetches logs in the last minute. @@ -9555,6 +9525,7 @@ export const TasksApiAxiosParamCreator = function (configuration?: Configuration destination?: string, assignedTo?: string, status?: string, + label?: string, requestTimeBetween?: string, startTimeBetween?: string, finishTimeBetween?: string, @@ -9603,6 +9574,10 @@ export const TasksApiAxiosParamCreator = function (configuration?: Configuration localVarQueryParameter['status'] = status; } + if (label !== undefined) { + localVarQueryParameter['label'] = label; + } + if (requestTimeBetween !== undefined) { localVarQueryParameter['request_time_between'] = requestTimeBetween; } @@ -10163,15 +10138,16 @@ export const TasksApiFp = function (configuration?: Configuration) { return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, /** - * + * Note that sorting by `pickup` and `destination` is mutually exclusive and sorting by either of them will filter only tasks which has those labels. * @summary Query Task States * @param {string} [taskId] comma separated list of task ids * @param {string} [category] comma separated list of task categories * @param {string} [requester] comma separated list of requester names - * @param {string} [pickup] comma separated list of pickup names - * @param {string} [destination] comma separated list of destination names + * @param {string} [pickup] comma separated list of pickup names. [deprecated] use `label` instead + * @param {string} [destination] comma separated list of destination names, [deprecated] use `label` instead * @param {string} [assignedTo] comma separated list of assigned robot names * @param {string} [status] comma separated list of statuses + * @param {string} [label] comma separated list of labels, each item must be in the form <key>=<value>, multiple items will filter tasks with all the labels * @param {string} [requestTimeBetween] The period of request time to fetch, in unix millis. This must be a comma separated string, \'X,Y\' to fetch between X millis and Y millis inclusive. Example: \"1000,2000\" - Fetches logs between unix millis 1000 and 2000. * @param {string} [startTimeBetween] The period of starting time to fetch, in unix millis. This must be a comma separated string, \'X,Y\' to fetch between X millis and Y millis inclusive. Example: \"1000,2000\" - Fetches logs between unix millis 1000 and 2000. * @param {string} [finishTimeBetween] The period of finishing time to fetch, in unix millis. This must be a comma separated string, \'X,Y\' to fetch between X millis and Y millis inclusive. Example: \"1000,2000\" - Fetches logs between unix millis 1000 and 2000. \"-60000\" - Fetches logs in the last minute. @@ -10189,6 +10165,7 @@ export const TasksApiFp = function (configuration?: Configuration) { destination?: string, assignedTo?: string, status?: string, + label?: string, requestTimeBetween?: string, startTimeBetween?: string, finishTimeBetween?: string, @@ -10205,6 +10182,7 @@ export const TasksApiFp = function (configuration?: Configuration) { destination, assignedTo, status, + label, requestTimeBetween, startTimeBetween, finishTimeBetween, @@ -10625,15 +10603,16 @@ export const TasksApiFactory = function ( .then((request) => request(axios, basePath)); }, /** - * + * Note that sorting by `pickup` and `destination` is mutually exclusive and sorting by either of them will filter only tasks which has those labels. * @summary Query Task States * @param {string} [taskId] comma separated list of task ids * @param {string} [category] comma separated list of task categories * @param {string} [requester] comma separated list of requester names - * @param {string} [pickup] comma separated list of pickup names - * @param {string} [destination] comma separated list of destination names + * @param {string} [pickup] comma separated list of pickup names. [deprecated] use `label` instead + * @param {string} [destination] comma separated list of destination names, [deprecated] use `label` instead * @param {string} [assignedTo] comma separated list of assigned robot names * @param {string} [status] comma separated list of statuses + * @param {string} [label] comma separated list of labels, each item must be in the form <key>=<value>, multiple items will filter tasks with all the labels * @param {string} [requestTimeBetween] The period of request time to fetch, in unix millis. This must be a comma separated string, \'X,Y\' to fetch between X millis and Y millis inclusive. Example: \"1000,2000\" - Fetches logs between unix millis 1000 and 2000. * @param {string} [startTimeBetween] The period of starting time to fetch, in unix millis. This must be a comma separated string, \'X,Y\' to fetch between X millis and Y millis inclusive. Example: \"1000,2000\" - Fetches logs between unix millis 1000 and 2000. * @param {string} [finishTimeBetween] The period of finishing time to fetch, in unix millis. This must be a comma separated string, \'X,Y\' to fetch between X millis and Y millis inclusive. Example: \"1000,2000\" - Fetches logs between unix millis 1000 and 2000. \"-60000\" - Fetches logs in the last minute. @@ -10651,6 +10630,7 @@ export const TasksApiFactory = function ( destination?: string, assignedTo?: string, status?: string, + label?: string, requestTimeBetween?: string, startTimeBetween?: string, finishTimeBetween?: string, @@ -10668,6 +10648,7 @@ export const TasksApiFactory = function ( destination, assignedTo, status, + label, requestTimeBetween, startTimeBetween, finishTimeBetween, @@ -11114,15 +11095,16 @@ export class TasksApi extends BaseAPI { } /** - * + * Note that sorting by `pickup` and `destination` is mutually exclusive and sorting by either of them will filter only tasks which has those labels. * @summary Query Task States * @param {string} [taskId] comma separated list of task ids * @param {string} [category] comma separated list of task categories * @param {string} [requester] comma separated list of requester names - * @param {string} [pickup] comma separated list of pickup names - * @param {string} [destination] comma separated list of destination names + * @param {string} [pickup] comma separated list of pickup names. [deprecated] use `label` instead + * @param {string} [destination] comma separated list of destination names, [deprecated] use `label` instead * @param {string} [assignedTo] comma separated list of assigned robot names * @param {string} [status] comma separated list of statuses + * @param {string} [label] comma separated list of labels, each item must be in the form <key>=<value>, multiple items will filter tasks with all the labels * @param {string} [requestTimeBetween] The period of request time to fetch, in unix millis. This must be a comma separated string, \'X,Y\' to fetch between X millis and Y millis inclusive. Example: \"1000,2000\" - Fetches logs between unix millis 1000 and 2000. * @param {string} [startTimeBetween] The period of starting time to fetch, in unix millis. This must be a comma separated string, \'X,Y\' to fetch between X millis and Y millis inclusive. Example: \"1000,2000\" - Fetches logs between unix millis 1000 and 2000. * @param {string} [finishTimeBetween] The period of finishing time to fetch, in unix millis. This must be a comma separated string, \'X,Y\' to fetch between X millis and Y millis inclusive. Example: \"1000,2000\" - Fetches logs between unix millis 1000 and 2000. \"-60000\" - Fetches logs in the last minute. @@ -11141,6 +11123,7 @@ export class TasksApi extends BaseAPI { destination?: string, assignedTo?: string, status?: string, + label?: string, requestTimeBetween?: string, startTimeBetween?: string, finishTimeBetween?: string, @@ -11158,6 +11141,7 @@ export class TasksApi extends BaseAPI { destination, assignedTo, status, + label, requestTimeBetween, startTimeBetween, finishTimeBetween, diff --git a/packages/api-client/lib/version.ts b/packages/api-client/lib/version.ts index d44ff17a7..8f08dd1f7 100644 --- a/packages/api-client/lib/version.ts +++ b/packages/api-client/lib/version.ts @@ -3,6 +3,6 @@ import { version as rmfModelVer } from 'rmf-models'; export const version = { rmfModels: rmfModelVer, - rmfServer: '98741b14ceca74208ca98e4bb0c3ca9e41ca1e3c', + rmfServer: 'd536f9525f277088015d827b6b7198035d1a856b', openapiGenerator: '6.2.1', }; diff --git a/packages/api-client/schema/index.ts b/packages/api-client/schema/index.ts index 37ea1f1b5..9d0215111 100644 --- a/packages/api-client/schema/index.ts +++ b/packages/api-client/schema/index.ts @@ -709,6 +709,8 @@ export default { get: { tags: ['Tasks'], summary: 'Query Task States', + description: + 'Note that sorting by `pickup` and `destination` is mutually exclusive and sorting\nby either of them will filter only tasks which has those labels.', operationId: 'query_task_states_tasks_get', parameters: [ { @@ -745,23 +747,27 @@ export default { in: 'query', }, { - description: 'comma separated list of pickup names', + description: 'comma separated list of pickup names. [deprecated] use `label` instead', required: false, + deprecated: true, schema: { title: 'Pickup', type: 'string', - description: 'comma separated list of pickup names', + description: 'comma separated list of pickup names. [deprecated] use `label` instead', }, name: 'pickup', in: 'query', }, { - description: 'comma separated list of destination names', + description: + 'comma separated list of destination names, [deprecated] use `label` instead', required: false, + deprecated: true, schema: { title: 'Destination', type: 'string', - description: 'comma separated list of destination names', + description: + 'comma separated list of destination names, [deprecated] use `label` instead', }, name: 'destination', in: 'query', @@ -788,6 +794,19 @@ export default { name: 'status', in: 'query', }, + { + description: + 'comma separated list of labels, each item must be in the form =, multiple items will filter tasks with all the labels', + required: false, + schema: { + title: 'Label', + type: 'string', + description: + 'comma separated list of labels, each item must be in the form =, multiple items will filter tasks with all the labels', + }, + name: 'label', + in: 'query', + }, { description: '\n The period of request time to fetch, in unix millis.\n\n This must be a comma separated string, \'X,Y\' to fetch between X millis and Y millis inclusive.\n\n Example:\n "1000,2000" - Fetches logs between unix millis 1000 and 2000.\n ', @@ -3675,23 +3694,17 @@ export default { title: 'TaskBookingLabel', required: ['description'], type: 'object', - properties: { description: { $ref: '#/components/schemas/TaskBookingLabelDescription' } }, - description: - 'This label is to be populated by any frontend during a task dispatch, by\nbeing added to TaskRequest.labels, which in turn populates\nTaskState.booking.labels, and can be used to display relevant information\nneeded for any frontends.', - }, - TaskBookingLabelDescription: { - title: 'TaskBookingLabelDescription', - required: ['task_definition_id'], - type: 'object', properties: { - task_definition_id: { title: 'Task Definition Id', type: 'string' }, - unix_millis_warn_time: { title: 'Unix Millis Warn Time', type: 'integer' }, - pickup: { title: 'Pickup', type: 'string' }, - destination: { title: 'Destination', type: 'string' }, - cart_id: { title: 'Cart Id', type: 'string' }, + description: { + title: 'Description', + type: 'object', + additionalProperties: { + anyOf: [{ type: 'string' }, { type: 'integer' }, { type: 'number' }], + }, + }, }, description: - 'This description holds several fields that could be useful for frontend\ndashboards when dispatching a task, to then be identified or rendered\naccordingly back on the same frontend.', + 'This label is to be populated by any frontend during a task dispatch, by\nbeing added to TaskRequest.labels, which in turn populates\nTaskState.booking.labels, and can be used to display relevant information\nneeded for any frontends.', }, TaskCancelResponse: { title: 'TaskCancelResponse', diff --git a/packages/api-server/api_server/models/__init__.py b/packages/api-server/api_server/models/__init__.py index 238fdf04b..414a446d0 100644 --- a/packages/api-server/api_server/models/__init__.py +++ b/packages/api-server/api_server/models/__init__.py @@ -6,6 +6,7 @@ from .doors import * from .health import * from .ingestors import * +from .labels import * from .lifts import * from .pagination import * from .rmf_api.activity_discovery_request import ActivityDiscoveryRequest diff --git a/packages/api-server/api_server/models/labels.py b/packages/api-server/api_server/models/labels.py new file mode 100644 index 000000000..209a8ff59 --- /dev/null +++ b/packages/api-server/api_server/models/labels.py @@ -0,0 +1,25 @@ +from typing import Sequence + +from pydantic import BaseModel + + +class Labels(BaseModel): + """ + Labels for a resource. + """ + + __root__: dict[str, str] + + @staticmethod + def _parse_label(s: str) -> tuple[str, str]: + sep = s.find("=") + if sep == -1: + return s, "" + return s[:sep], s[sep + 1 :] + + @staticmethod + def from_strings(labels: Sequence[str]) -> "Labels": + return Labels(__root__=dict(Labels._parse_label(s) for s in labels)) + + def to_strings(self) -> list[str]: + return [f"{k}={v}" for k, v in self.__root__.items()] diff --git a/packages/api-server/api_server/models/task_booking_label.py b/packages/api-server/api_server/models/task_booking_label.py index 1e59d77ab..3e5602680 100644 --- a/packages/api-server/api_server/models/task_booking_label.py +++ b/packages/api-server/api_server/models/task_booking_label.py @@ -3,30 +3,6 @@ import pydantic from pydantic import BaseModel -# NOTE: This label model needs to exactly match the fields that are defined and -# populated by the dashboard. Any changes to either side will require syncing. - - -class TaskBookingLabelDescription(BaseModel): - """ - This description holds several fields that could be useful for frontend - dashboards when dispatching a task, to then be identified or rendered - accordingly back on the same frontend. - """ - - task_definition_id: str - unix_millis_warn_time: Optional[int] - pickup: Optional[str] - destination: Optional[str] - cart_id: Optional[str] - - @staticmethod - def from_json_string(json_str: str) -> Optional["TaskBookingLabelDescription"]: - try: - return TaskBookingLabelDescription.parse_raw(json_str) - except pydantic.error_wrappers.ValidationError: - return None - class TaskBookingLabel(BaseModel): """ @@ -36,7 +12,7 @@ class TaskBookingLabel(BaseModel): needed for any frontends. """ - description: TaskBookingLabelDescription + description: dict[str, str | int | float] @staticmethod def from_json_string(json_str: str) -> Optional["TaskBookingLabel"]: diff --git a/packages/api-server/api_server/query.py b/packages/api-server/api_server/query.py index b04c3d565..35dcb62c1 100644 --- a/packages/api-server/api_server/query.py +++ b/packages/api-server/api_server/query.py @@ -1,5 +1,7 @@ from typing import Dict, Optional +import tortoise.functions as tfuncs +from tortoise.expressions import Q from tortoise.queryset import MODEL, QuerySet from api_server.models.pagination import Pagination @@ -9,24 +11,46 @@ def add_pagination( query: QuerySet[MODEL], pagination: Pagination, field_mappings: Optional[Dict[str, str]] = None, + group_by: str | None = None, ) -> QuerySet[MODEL]: """ - Adds pagination and ordering to a query. + Adds pagination and ordering to a query. If the order field starts with `label=`, it is + assumed to be a label and label sorting will used. In this case, the model must have + a reverse relation named "labels" and the `group_by` param is required. :param field_mapping: A dict mapping the order fields to the fields used to build the query. e.g. a url of `?order_by=order_field` and a field mapping of `{"order_field": "db_field"}` will order the query result according to `db_field`. + :param group_by: Required when sorting by labels, must be the foreign key column of the label table. """ field_mappings = field_mappings or {} + annotations = {} query = query.limit(pagination.limit).offset(pagination.offset) if pagination.order_by is not None: order_fields = [] order_values = pagination.order_by.split(",") for v in order_values: + # perform the mapping after stripping the order prefix + order_prefix = "" + order_field = v if v[0] in ["-", "+"]: - stripped = v[1:] - order_fields.append(v[0] + field_mappings.get(stripped, stripped)) - else: - order_fields.append(field_mappings.get(v, v)) + order_prefix = v[0] + order_field = v[1:] + order_field = field_mappings.get(order_field, order_field) + + # add annotations required for sorting by labels + if order_field.startswith("label="): + f = order_field[6:] + annotations[f"label_sort_{f}"] = tfuncs.Max( + "labels__label_value_str", + _filter=Q(labels__label_name=f), + ) + order_field = f"label_sort_{f}" + + order_fields.append(order_prefix + order_field) + + query = query.annotate(**annotations) + if group_by is not None: + query = query.group_by(group_by) query = query.order_by(*order_fields) return query diff --git a/packages/api-server/api_server/repositories/tasks.py b/packages/api-server/api_server/repositories/tasks.py index c857abfea..5fb1ee963 100644 --- a/packages/api-server/api_server/repositories/tasks.py +++ b/packages/api-server/api_server/repositories/tasks.py @@ -117,31 +117,40 @@ async def save_task_state(self, task_state: TaskState) -> None: # Here we generate the labels required for server-side sorting and # filtering. - if booking_label.description.pickup is not None: - await ttm.TaskLabel.create( - state=state, - label_name="pickup", - label_value_str=booking_label.description.pickup, - ) - if booking_label.description.destination is not None: - await ttm.TaskLabel.create( - state=state, - label_name="destination", - label_value_str=booking_label.description.destination, - ) - if booking_label.description.unix_millis_warn_time is not None: - await ttm.TaskLabel.create( - state=state, - label_name="unix_millis_warn_time", - label_value_num=booking_label.description.unix_millis_warn_time, - ) + async with in_transaction(): + for k, v in booking_label.description.items(): + if isinstance(v, str): + await ttm.TaskLabel.create( + state=state, label_name=k, label_value_str=v + ) + elif isinstance(v, int): + await ttm.TaskLabel.create( + state=state, + label_name=k, + label_value_num=v, + label_value_float=v, # also store float to make querying easier + ) + elif isinstance(v, float): + exact_val = int(v) if v.is_integer else None + await ttm.TaskLabel.create( + state=state, + label_name=k, + label_value_float=v, + label_value_num=exact_val, # also store int to make querying easier + ) async def query_task_states( self, query: QuerySet[DbTaskState], pagination: Optional[Pagination] = None ) -> List[TaskState]: try: if pagination: - query = add_pagination(query, pagination) + query = add_pagination( + query, + pagination, + # TODO(koonpeng): remove this mapping after `pickup` and `destination` query is removed. + {"pickup": "label=pickup", "destination": "label=destination"}, + group_by="labels__state_id", + ) # TODO: enforce with authz results = await query.values_list("data", flat=True) return [TaskState(**r) for r in results] diff --git a/packages/api-server/api_server/routes/tasks/tasks.py b/packages/api-server/api_server/routes/tasks/tasks.py index 2a8030c91..11a43326f 100644 --- a/packages/api-server/api_server/routes/tasks/tasks.py +++ b/packages/api-server/api_server/routes/tasks/tasks.py @@ -1,8 +1,10 @@ from datetime import datetime from typing import List, Optional, Tuple, cast +import tortoise.functions as tfuncs from fastapi import Body, Depends, HTTPException, Path, Query from rx import operators as rxops +from tortoise.expressions import Q from api_server import models as mdl from api_server.dependencies import ( @@ -99,10 +101,14 @@ async def query_task_states( None, description="comma separated list of requester names" ), pickup: Optional[str] = Query( - None, description="comma separated list of pickup names" + None, + description="comma separated list of pickup names. [deprecated] use `label` instead", + deprecated=True, ), destination: Optional[str] = Query( - None, description="comma separated list of destination names" + None, + description="comma separated list of destination names, [deprecated] use `label` instead", + deprecated=True, ), assigned_to: Optional[str] = Query( None, description="comma separated list of assigned robot names" @@ -114,8 +120,17 @@ async def query_task_states( finish_time_between_query ), status: Optional[str] = Query(None, description="comma separated list of statuses"), + label: str + | None = Query( + None, + description="comma separated list of labels, each item must be in the form =, multiple items will filter tasks with all the labels", + ), pagination: mdl.Pagination = Depends(pagination_query), ): + """ + Note that sorting by `pickup` and `destination` is mutually exclusive and sorting + by either of them will filter only tasks which has those labels. + """ filters = {} if task_id is not None: filters["id___in"] = task_id.split(",") @@ -141,33 +156,47 @@ async def query_task_states( if status_string not in valid_values: continue filters["status__in"].append(mdl.Status(status_string)) + query = DbTaskState.filter(**filters) - # NOTE: in order to perform filtering based on the values in labels, a - # filter on the label_name will need to be applied as well as a filter on - # the label_value. + label_filters = {} if pickup is not None: - filters["labels__label_name"] = "pickup" - filters["labels__label_value_str__in"] = pickup.split(",") + label_filters["label_filter_pickup"] = tfuncs.Count( + "id_", + _filter=Q( + labels__label_name="pickup", + labels__label_value_str__in=pickup.split(","), + ), + ) if destination is not None: - filters["labels__label_name"] = "destination" - filters["labels__label_value_str__in"] = destination.split(",") - - # NOTE: In order to perform sorting based on the values in labels, a filter - # on the label_name has to be performed first. A side-effect of this would - # be that states that do not contain this field will not be returned. - if pagination.order_by is not None: - labels_fields = ["pickup", "destination"] - new_order = pagination.order_by - for field in labels_fields: - if field in pagination.order_by: - filters["labels__label_name"] = field - new_order = pagination.order_by.replace( - field, "labels__label_value_str" + label_filters["label_filter_destination"] = tfuncs.Count( + "id_", + _filter=Q( + labels__label_name="destination", + labels__label_value_str__in=destination.split(","), + ), + ) + if label is not None: + labels = mdl.Labels.from_strings(label.split(",")) + label_filters.update( + { + f"label_filter_{k}": tfuncs.Count( + "id_", _filter=Q(labels__label_name=k, labels__label_value_str=v) ) - break - pagination.order_by = new_order + for k, v in labels.__root__.items() + } + ) + + if len(label_filters) > 0: + filter_gt = {f"{f}__gt": 0 for f in label_filters} + query = ( + query.annotate(**label_filters) + .group_by( + "labels__state_id" + ) # need to group by a related field to make tortoise-orm generate joins + .filter(**filter_gt) + ) - return await task_repo.query_task_states(DbTaskState.filter(**filters), pagination) + return await task_repo.query_task_states(query, pagination) @router.get("/{task_id}/state", response_model=mdl.TaskState) diff --git a/packages/api-server/api_server/routes/tasks/test_tasks.py b/packages/api-server/api_server/routes/tasks/test_tasks.py index ffa78e9db..5c3c7f673 100644 --- a/packages/api-server/api_server/routes/tasks/test_tasks.py +++ b/packages/api-server/api_server/routes/tasks/test_tasks.py @@ -1,6 +1,8 @@ from unittest.mock import patch from uuid import uuid4 +import pydantic + from api_server import models as mdl from api_server.rmf_io import tasks_service from api_server.test import ( @@ -15,8 +17,32 @@ class TestTasksRoute(AppFixture): @classmethod def setUpClass(cls): super().setUpClass() - task_ids = [uuid4()] - cls.task_states = [make_task_state(task_id=f"test_{x}") for x in task_ids] + booking_labels = make_task_booking_label() + booking_labels.description["test_single"] = "" + booking_labels.description["test_single_2"] = "" + booking_labels.description["test_kv"] = "value" + booking_labels.description["test_label_sort"] = "zzz" + booking_labels.description["test_label_sort_2"] = "aaa" + booking_labels.description["test_label_sort_3"] = "bbb" + booking_labels_2 = make_task_booking_label() + booking_labels_2.description["test_label_sort"] = "aaa" + booking_labels_2.description["test_label_sort_3"] = "bbb" + + task_ids = [uuid4(), uuid4()] + cls.task_states = [ + make_task_state( + task_id=f"test_{task_ids[0]}", + booking_labels=[ + "dummy_label_1", + "dummy_label_2", + booking_labels.json(), + ], + ), + make_task_state( + task_id=f"test_{task_ids[1]}", + booking_labels=[booking_labels_2.json()], + ), + ] cls.task_logs = [make_task_log(task_id=f"test_{x}") for x in task_ids] with cls.client.websocket_connect("/_internal") as ws: @@ -44,6 +70,107 @@ def test_query_task_states(self): self.assertEqual(1, len(results)) self.assertEqual(self.task_states[0].booking.id, results[0]["booking"]["id"]) + test_cases = [ + ({"pickup": "Kitchen"}, [self.task_states[0].booking.id]), + ({"destination": "room_203"}, [self.task_states[0].booking.id]), + ( + {"pickup": "Kitchen", "destination": "room_203"}, + [self.task_states[0].booking.id], + ), + ( + {"pickup": "Kitchen", "destination": "room_202"}, + [], + ), + ] + for tc in test_cases: + q = "&".join(f"{k}={v}" for k, v in tc[0].items()) + resp = self.client.get( + f"/tasks?task_id={self.task_states[0].booking.id}&{q}" + ) + self.assertEqual(200, resp.status_code, tc) + results = resp.json() + self.assertEqual(len(tc[1]), len(results), tc) + for a, b in zip(tc[1], results): + self.assertEqual(a, b["booking"]["id"], tc) + + def test_query_task_states_filter_by_label(self): + resp = self.client.get("/tasks?label=not_existing") + self.assertEqual(200, resp.status_code) + results = pydantic.parse_raw_as(list[mdl.TaskState], resp.content) + self.assertEqual(0, len(results)) + + resp = self.client.get("/tasks?label=test_single") + self.assertEqual(200, resp.status_code) + results = pydantic.parse_raw_as(list[mdl.TaskState], resp.content) + self.assertEqual(1, len(results)) + self.assertEqual(self.task_states[0].booking.id, results[0].booking.id) + + resp = self.client.get("/tasks?label=test_single=wrong_value") + self.assertEqual(200, resp.status_code) + results = pydantic.parse_raw_as(list[mdl.TaskState], resp.content) + self.assertEqual(0, len(results)) + + resp = self.client.get("/tasks?label=test_single_2=") + self.assertEqual(200, resp.status_code) + results = pydantic.parse_raw_as(list[mdl.TaskState], resp.content) + self.assertEqual(1, len(results)) + self.assertEqual(self.task_states[0].booking.id, results[0].booking.id) + + resp = self.client.get("/tasks?label=test_kv=value") + self.assertEqual(200, resp.status_code) + results = pydantic.parse_raw_as(list[mdl.TaskState], resp.content) + self.assertEqual(1, len(results)) + self.assertEqual(self.task_states[0].booking.id, results[0].booking.id) + + resp = self.client.get("/tasks?label=test_kv=wrong_value") + self.assertEqual(200, resp.status_code) + results = pydantic.parse_raw_as(list[mdl.TaskState], resp.content) + self.assertEqual(0, len(results)) + + resp = self.client.get("/tasks?label=test_single,test_kv=value") + self.assertEqual(200, resp.status_code) + results = pydantic.parse_raw_as(list[mdl.TaskState], resp.content) + self.assertEqual(1, len(results)) + self.assertEqual(self.task_states[0].booking.id, results[0].booking.id) + + resp = self.client.get("/tasks?label=test_single,test_kv=wrong_value") + self.assertEqual(200, resp.status_code) + results = pydantic.parse_raw_as(list[mdl.TaskState], resp.content) + self.assertEqual(0, len(results)) + + def test_query_task_states_sort_by_label(self): + resp = self.client.get("/tasks?order_by=-label=test_label_sort") + self.assertEqual(200, resp.status_code) + results = pydantic.parse_raw_as(list[mdl.TaskState], resp.content) + self.assertEqual(2, len(results)) + for a, b in zip(self.task_states, results): + self.assertEqual(a, b) + + resp = self.client.get("/tasks?order_by=label=test_label_sort") + self.assertEqual(200, resp.status_code) + results = pydantic.parse_raw_as(list[mdl.TaskState], resp.content) + self.assertEqual(2, len(results)) + for a, b in zip(self.task_states[::-1], results): + self.assertEqual(a, b) + + # test sorting by multiple labels + resp = self.client.get( + "/tasks?order_by=label=test_label_sort,label=test_label_sort_3" + ) + self.assertEqual(200, resp.status_code) + results = pydantic.parse_raw_as(list[mdl.TaskState], resp.content) + self.assertEqual(2, len(results)) + for a, b in zip(self.task_states[::-1], results): + self.assertEqual(a, b) + + # test that tasks without the label are not filtered out + # we don't test the result order because different db has different behavior + # of sorting NULL. + resp = self.client.get("/tasks?order_by=label=test_label_sort_not_existing") + self.assertEqual(200, resp.status_code) + results = pydantic.parse_raw_as(list[mdl.TaskState], resp.content) + self.assertEqual(2, len(results)) + def test_sub_task_state(self): task_id = self.task_states[0].booking.id gen = self.subscribe_sio(f"/tasks/{task_id}/state") @@ -59,10 +186,13 @@ def test_sub_task_state(self): def test_get_task_booking_label(self): resp = self.client.get(f"/tasks/{self.task_states[0].booking.id}/booking_label") self.assertEqual(200, resp.status_code) - self.assertEqual( - make_task_booking_label(), - mdl.TaskBookingLabel(**resp.json()), - ) + labels = mdl.TaskBookingLabel.parse_raw(resp.content) + expected = make_task_booking_label() + for k, v in expected.description.items(): + self.assertEqual( + v, + labels.description[k], + ) def test_get_task_log(self): resp = self.client.get( diff --git a/packages/api-server/api_server/test/__init__.py b/packages/api-server/api_server/test/__init__.py index cf62c9c9f..0cfa7abe6 100644 --- a/packages/api-server/api_server/test/__init__.py +++ b/packages/api-server/api_server/test/__init__.py @@ -2,7 +2,6 @@ from api_server.models import User from .mocks import * -from .test_client import client from .test_data import * from .test_fixtures import * from .test_utils import * diff --git a/packages/api-server/api_server/test/test_client.py b/packages/api-server/api_server/test/test_client.py index 20975db06..0539a7235 100644 --- a/packages/api-server/api_server/test/test_client.py +++ b/packages/api-server/api_server/test/test_client.py @@ -33,38 +33,15 @@ def _generate_token(username: str): class TestClient(BaseTestClient): - _admin_token: Optional[str] = None - def __init__(self): super().__init__(app) + self.current_user: str + self.set_user("admin") @classmethod def token(cls, username: str) -> str: - if username == "admin": - if cls._admin_token is None: - cls._admin_token = _generate_token("admin") - return cls._admin_token - return _generate_token(username) - def set_user(self, user): + def set_user(self, user: str): + self.current_user = user self.headers["Authorization"] = f"bearer {self.token(user)}" - - -_client: Optional[TestClient] = None - - -def client(user="admin") -> TestClient: - global _client - if _client is None: - _client = TestClient() - _client.__enter__() - _client.headers["Content-Type"] = "application/json" - _client.set_user(user) - return _client - - -def shutdown(): - global _client - if _client is not None: - _client.__exit__() diff --git a/packages/api-server/api_server/test/test_data.py b/packages/api-server/api_server/test/test_data.py index cf0f693d2..3a1970c5c 100644 --- a/packages/api-server/api_server/test/test_data.py +++ b/packages/api-server/api_server/test/test_data.py @@ -24,7 +24,6 @@ LiftState, RobotState, TaskBookingLabel, - TaskBookingLabelDescription, TaskEventLog, TaskFavorite, TaskState, @@ -134,17 +133,20 @@ def make_fleet_log() -> FleetLog: def make_task_booking_label() -> TaskBookingLabel: return TaskBookingLabel( - description=TaskBookingLabelDescription( - task_definition_id="multi-delivery", - unix_millis_warn_time=1636388400000, - pickup="Kitchen", - destination="room_203", - cart_id="soda", - ) + description={ + "task_definition_id": "multi-delivery", + "unix_millis_warn_time": 1636388400000, + "pickup": "Kitchen", + "destination": "room_203", + "cart_id": "soda", + } ) -def make_task_state(task_id: str = "test_task") -> TaskState: +def make_task_state( + task_id: str = "test_task", + booking_labels: list[str] | None = None, +) -> TaskState: # from https://raw.githubusercontent.com/open-rmf/rmf_api_msgs/960b286d9849fc716a3043b8e1f5fb341bdf5778/rmf_api_msgs/samples/task_state/multi_dropoff_delivery.json sample_task = json.loads( """ @@ -444,11 +446,12 @@ def make_task_state(task_id: str = "test_task") -> TaskState: ) sample_task["booking"]["id"] = task_id - booking_labels = [ - "dummy_label_1", - "dummy_label_2", - make_task_booking_label().json(), - ] + if booking_labels is None: + booking_labels = [ + "dummy_label_1", + "dummy_label_2", + make_task_booking_label().json(), + ] sample_task["booking"]["labels"] = booking_labels return TaskState(**sample_task) diff --git a/packages/api-server/api_server/test/test_fixtures.py b/packages/api-server/api_server/test/test_fixtures.py index 32a2e6d2f..261bdb923 100644 --- a/packages/api-server/api_server/test/test_fixtures.py +++ b/packages/api-server/api_server/test/test_fixtures.py @@ -9,9 +9,10 @@ from uuid import uuid4 from api_server.app import app, on_sio_connect +from api_server.models import User from .mocks import patch_sio -from .test_client import client +from .test_client import TestClient T = TypeVar("T") @@ -79,8 +80,11 @@ async def async_try_until( class AppFixture(unittest.TestCase): @classmethod def setUpClass(cls): - cls.client = client() - cls.client.set_user("admin") + cls.admin_user = User(username="admin", is_admin=True) + cls.client = TestClient() + cls.client.headers["Content-Type"] = "application/json" + cls.client.__enter__() + cls.addClassCleanup(cls.client.__exit__) def subscribe_sio(self, room: str, *, user="admin"): """ diff --git a/packages/api-server/api_server/test_sio_auth.py b/packages/api-server/api_server/test_sio_auth.py index c48baa611..0ddbda633 100644 --- a/packages/api-server/api_server/test_sio_auth.py +++ b/packages/api-server/api_server/test_sio_auth.py @@ -4,7 +4,6 @@ from api_server.app import app, on_sio_connect -from .test import client from .test.test_fixtures import AppFixture @@ -33,4 +32,4 @@ def test_fail_with_invalid_token(self): self.assertFalse(self.try_connect("invalid")) def test_success_with_valid_token(self): - self.assertTrue(self.try_connect(client().token("admin"))) + self.assertTrue(self.try_connect(self.client.token("admin"))) diff --git a/packages/api-server/scripts/test.py b/packages/api-server/scripts/test.py index 4acdf5e90..e164368e6 100644 --- a/packages/api-server/scripts/test.py +++ b/packages/api-server/scripts/test.py @@ -6,9 +6,5 @@ import unittest -from api_server.test import test_client - -test_client.client() result = unittest.main(module=None, exit=False) -test_client.shutdown() exit(1 if not result.result.wasSuccessful() else 0) diff --git a/packages/dashboard/src/components/tasks/tasks-app.tsx b/packages/dashboard/src/components/tasks/tasks-app.tsx index 2716e99cc..3980d3e37 100644 --- a/packages/dashboard/src/components/tasks/tasks-app.tsx +++ b/packages/dashboard/src/components/tasks/tasks-app.tsx @@ -195,6 +195,7 @@ export const TasksApp = React.memo( filterColumn && filterColumn === 'destination' ? filterValue : undefined, filterColumn && filterColumn === 'assigned_to' ? filterValue : undefined, filterColumn && filterColumn === 'status' ? filterValue : undefined, + undefined, filterColumn && filterColumn === 'unix_millis_request_time' ? filterValue : undefined, filterColumn && filterColumn === 'unix_millis_start_time' ? filterValue : undefined, filterColumn && filterColumn === 'unix_millis_finish_time' ? filterValue : undefined, @@ -263,6 +264,7 @@ export const TasksApp = React.memo( undefined, undefined, undefined, + undefined, `${currentMillis - oneMonthMillis},${currentMillis}`, undefined, QueryLimit, From bd06c2d8db30ff0dd3e5fb0eba4514ca2d2a811e Mon Sep 17 00:00:00 2001 From: Aaron Chong Date: Wed, 12 Jun 2024 10:33:54 +0800 Subject: [PATCH 2/5] Hammer/fix warning time (#953) * Remove rogue warn time workaround from models Signed-off-by: Aaron Chong * Regenerated API Signed-off-by: Aaron Chong * Warning time to only display color and use labels Signed-off-by: Aaron Chong * Clean up tooltips, use less function calls Signed-off-by: Aaron Chong * use do_not_use on pickup and destination as well Signed-off-by: Aaron Chong --------- Signed-off-by: Aaron Chong --- packages/api-client/lib/openapi/api.ts | 20 +-- packages/api-client/lib/version.ts | 2 +- packages/api-client/schema/index.ts | 9 +- .../api_server/models/rmf_api/task_request.py | 4 - .../api_server/models/rmf_api/task_state.py | 1 - .../models/tortoise_models/tasks.py | 10 +- .../api-server/api_server/routes/internal.py | 11 -- .../api_server/routes/tasks/tasks.py | 4 - packages/dashboard/src/components/appbar.tsx | 14 +- .../src/components/tasks/task-alert.tsx | 29 ---- .../lib/tasks/create-task.tsx | 48 +++--- .../lib/tasks/task-booking-label-utils.tsx | 22 ++- .../lib/tasks/task-table-datagrid.tsx | 139 +++++++++++++++--- 13 files changed, 187 insertions(+), 126 deletions(-) diff --git a/packages/api-client/lib/openapi/api.ts b/packages/api-client/lib/openapi/api.ts index 54371dacc..ddfa3647a 100644 --- a/packages/api-client/lib/openapi/api.ts +++ b/packages/api-client/lib/openapi/api.ts @@ -2851,12 +2851,6 @@ export interface TaskRequest { * @memberof TaskRequest */ fleet_name?: string; - /** - * (Optional) The time at which a warning will be issued if the estimated completion time is later than expected - * @type {number} - * @memberof TaskRequest - */ - unix_millis_warn_time?: number; } /** * @@ -3076,12 +3070,6 @@ export interface TaskState { * @memberof TaskState */ killed?: Killed1; - /** - * - * @type {number} - * @memberof TaskState - */ - unix_millis_warn_time?: number; } /** * @@ -5916,7 +5904,7 @@ export const DefaultApiAxiosParamCreator = function (configuration?: Configurati }; }, /** - * # NOTE: This endpoint is here for documentation purposes only, this is _not_ a REST endpoint. ## About This exposes a minimal pubsub system built on top of socket.io. It works similar to a normal socket.io endpoint, except that are 2 special rooms which control subscriptions. ## Rooms ### subscribe Clients must send a message to this room to start receiving messages on other rooms. The message must be of the form: ``` { \"room\": \"\" } ``` ### unsubscribe Clients can send a message to this room to stop receiving messages on other rooms. The message must be of the form: ``` { \"room\": \"\" } ``` ### /alerts ``` { \"title\": \"Alert\", \"description\": \"General alert that can be triggered by events.\", \"type\": \"object\", \"properties\": { \"id\": { \"title\": \"Id\", \"maxLength\": 255, \"type\": \"string\" }, \"original_id\": { \"title\": \"Original Id\", \"maxLength\": 255, \"type\": \"string\" }, \"category\": { \"title\": \"Category\", \"description\": \"Default: default
Task: task
Fleet: fleet
Robot: robot\", \"maxLength\": 7, \"type\": \"string\" }, \"unix_millis_created_time\": { \"title\": \"Unix Millis Created Time\", \"minimum\": -9223372036854775808, \"maximum\": 9223372036854775807, \"type\": \"integer\" }, \"acknowledged_by\": { \"title\": \"Acknowledged By\", \"maxLength\": 255, \"nullable\": true, \"type\": \"string\" }, \"unix_millis_acknowledged_time\": { \"title\": \"Unix Millis Acknowledged Time\", \"minimum\": -9223372036854775808, \"maximum\": 9223372036854775807, \"nullable\": true, \"type\": \"integer\" } }, \"required\": [ \"id\", \"original_id\", \"category\", \"unix_millis_created_time\" ], \"additionalProperties\": false } ``` ### /beacons ``` { \"title\": \"BeaconState\", \"type\": \"object\", \"properties\": { \"id\": { \"title\": \"Id\", \"maxLength\": 255, \"type\": \"string\" }, \"online\": { \"title\": \"Online\", \"type\": \"boolean\" }, \"category\": { \"title\": \"Category\", \"maxLength\": 255, \"nullable\": true, \"type\": \"string\" }, \"activated\": { \"title\": \"Activated\", \"type\": \"boolean\" }, \"level\": { \"title\": \"Level\", \"maxLength\": 255, \"nullable\": true, \"type\": \"string\" } }, \"required\": [ \"id\", \"online\", \"activated\" ], \"additionalProperties\": false } ``` ### /building_map ``` { \"title\": \"BuildingMap\", \"type\": \"object\", \"properties\": { \"name\": { \"title\": \"Name\", \"default\": \"\", \"type\": \"string\" }, \"levels\": { \"title\": \"Levels\", \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/Level\" } }, \"lifts\": { \"title\": \"Lifts\", \"default\": [], \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/Lift\" } } }, \"required\": [ \"name\", \"levels\", \"lifts\" ], \"definitions\": { \"AffineImage\": { \"title\": \"AffineImage\", \"type\": \"object\", \"properties\": { \"name\": { \"title\": \"Name\", \"default\": \"\", \"type\": \"string\" }, \"x_offset\": { \"title\": \"X Offset\", \"default\": 0, \"type\": \"number\" }, \"y_offset\": { \"title\": \"Y Offset\", \"default\": 0, \"type\": \"number\" }, \"yaw\": { \"title\": \"Yaw\", \"default\": 0, \"type\": \"number\" }, \"scale\": { \"title\": \"Scale\", \"default\": 0, \"type\": \"number\" }, \"encoding\": { \"title\": \"Encoding\", \"default\": \"\", \"type\": \"string\" }, \"data\": { \"title\": \"Data\", \"type\": \"string\" } }, \"required\": [ \"name\", \"x_offset\", \"y_offset\", \"yaw\", \"scale\", \"encoding\", \"data\" ] }, \"Place\": { \"title\": \"Place\", \"type\": \"object\", \"properties\": { \"name\": { \"title\": \"Name\", \"default\": \"\", \"type\": \"string\" }, \"x\": { \"title\": \"X\", \"default\": 0, \"type\": \"number\" }, \"y\": { \"title\": \"Y\", \"default\": 0, \"type\": \"number\" }, \"yaw\": { \"title\": \"Yaw\", \"default\": 0, \"type\": \"number\" }, \"position_tolerance\": { \"title\": \"Position Tolerance\", \"default\": 0, \"type\": \"number\" }, \"yaw_tolerance\": { \"title\": \"Yaw Tolerance\", \"default\": 0, \"type\": \"number\" } }, \"required\": [ \"name\", \"x\", \"y\", \"yaw\", \"position_tolerance\", \"yaw_tolerance\" ] }, \"Door\": { \"title\": \"Door\", \"type\": \"object\", \"properties\": { \"name\": { \"title\": \"Name\", \"default\": \"\", \"type\": \"string\" }, \"v1_x\": { \"title\": \"V1 X\", \"default\": 0, \"type\": \"number\" }, \"v1_y\": { \"title\": \"V1 Y\", \"default\": 0, \"type\": \"number\" }, \"v2_x\": { \"title\": \"V2 X\", \"default\": 0, \"type\": \"number\" }, \"v2_y\": { \"title\": \"V2 Y\", \"default\": 0, \"type\": \"number\" }, \"door_type\": { \"title\": \"Door Type\", \"default\": 0, \"minimum\": 0, \"maximum\": 255, \"type\": \"integer\" }, \"motion_range\": { \"title\": \"Motion Range\", \"default\": 0, \"type\": \"number\" }, \"motion_direction\": { \"title\": \"Motion Direction\", \"default\": 0, \"minimum\": -2147483648, \"maximum\": 2147483647, \"type\": \"integer\" } }, \"required\": [ \"name\", \"v1_x\", \"v1_y\", \"v2_x\", \"v2_y\", \"door_type\", \"motion_range\", \"motion_direction\" ] }, \"Param\": { \"title\": \"Param\", \"type\": \"object\", \"properties\": { \"name\": { \"title\": \"Name\", \"default\": \"\", \"type\": \"string\" }, \"type\": { \"title\": \"Type\", \"default\": 0, \"minimum\": 0, \"maximum\": 4294967295, \"type\": \"integer\" }, \"value_int\": { \"title\": \"Value Int\", \"default\": 0, \"minimum\": -2147483648, \"maximum\": 2147483647, \"type\": \"integer\" }, \"value_float\": { \"title\": \"Value Float\", \"default\": 0, \"type\": \"number\" }, \"value_string\": { \"title\": \"Value String\", \"default\": \"\", \"type\": \"string\" }, \"value_bool\": { \"title\": \"Value Bool\", \"default\": false, \"type\": \"boolean\" } }, \"required\": [ \"name\", \"type\", \"value_int\", \"value_float\", \"value_string\", \"value_bool\" ] }, \"GraphNode\": { \"title\": \"GraphNode\", \"type\": \"object\", \"properties\": { \"x\": { \"title\": \"X\", \"default\": 0, \"type\": \"number\" }, \"y\": { \"title\": \"Y\", \"default\": 0, \"type\": \"number\" }, \"name\": { \"title\": \"Name\", \"default\": \"\", \"type\": \"string\" }, \"params\": { \"title\": \"Params\", \"default\": [], \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/Param\" } } }, \"required\": [ \"x\", \"y\", \"name\", \"params\" ] }, \"GraphEdge\": { \"title\": \"GraphEdge\", \"type\": \"object\", \"properties\": { \"v1_idx\": { \"title\": \"V1 Idx\", \"default\": 0, \"minimum\": 0, \"maximum\": 4294967295, \"type\": \"integer\" }, \"v2_idx\": { \"title\": \"V2 Idx\", \"default\": 0, \"minimum\": 0, \"maximum\": 4294967295, \"type\": \"integer\" }, \"params\": { \"title\": \"Params\", \"default\": [], \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/Param\" } }, \"edge_type\": { \"title\": \"Edge Type\", \"default\": 0, \"minimum\": 0, \"maximum\": 255, \"type\": \"integer\" } }, \"required\": [ \"v1_idx\", \"v2_idx\", \"params\", \"edge_type\" ] }, \"Graph\": { \"title\": \"Graph\", \"type\": \"object\", \"properties\": { \"name\": { \"title\": \"Name\", \"default\": \"\", \"type\": \"string\" }, \"vertices\": { \"title\": \"Vertices\", \"default\": [], \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/GraphNode\" } }, \"edges\": { \"title\": \"Edges\", \"default\": [], \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/GraphEdge\" } }, \"params\": { \"title\": \"Params\", \"default\": [], \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/Param\" } } }, \"required\": [ \"name\", \"vertices\", \"edges\", \"params\" ] }, \"Level\": { \"title\": \"Level\", \"type\": \"object\", \"properties\": { \"name\": { \"title\": \"Name\", \"default\": \"\", \"type\": \"string\" }, \"elevation\": { \"title\": \"Elevation\", \"default\": 0, \"type\": \"number\" }, \"images\": { \"title\": \"Images\", \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/AffineImage\" } }, \"places\": { \"title\": \"Places\", \"default\": [], \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/Place\" } }, \"doors\": { \"title\": \"Doors\", \"default\": [], \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/Door\" } }, \"nav_graphs\": { \"title\": \"Nav Graphs\", \"default\": [], \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/Graph\" } }, \"wall_graph\": { \"title\": \"Wall Graph\", \"default\": { \"name\": \"\", \"vertices\": [], \"edges\": [], \"params\": [] }, \"allOf\": [ { \"$ref\": \"#/definitions/Graph\" } ] } }, \"required\": [ \"name\", \"elevation\", \"images\", \"places\", \"doors\", \"nav_graphs\", \"wall_graph\" ] }, \"Lift\": { \"title\": \"Lift\", \"type\": \"object\", \"properties\": { \"name\": { \"title\": \"Name\", \"default\": \"\", \"type\": \"string\" }, \"levels\": { \"title\": \"Levels\", \"default\": [], \"type\": \"array\", \"items\": { \"type\": \"string\" } }, \"doors\": { \"title\": \"Doors\", \"default\": [], \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/Door\" } }, \"wall_graph\": { \"title\": \"Wall Graph\", \"default\": { \"name\": \"\", \"vertices\": [], \"edges\": [], \"params\": [] }, \"allOf\": [ { \"$ref\": \"#/definitions/Graph\" } ] }, \"ref_x\": { \"title\": \"Ref X\", \"default\": 0, \"type\": \"number\" }, \"ref_y\": { \"title\": \"Ref Y\", \"default\": 0, \"type\": \"number\" }, \"ref_yaw\": { \"title\": \"Ref Yaw\", \"default\": 0, \"type\": \"number\" }, \"width\": { \"title\": \"Width\", \"default\": 0, \"type\": \"number\" }, \"depth\": { \"title\": \"Depth\", \"default\": 0, \"type\": \"number\" } }, \"required\": [ \"name\", \"levels\", \"doors\", \"wall_graph\", \"ref_x\", \"ref_y\", \"ref_yaw\", \"width\", \"depth\" ] } } } ``` ### /building_map/fire_alarm_trigger ``` { \"title\": \"FireAlarmTriggerState\", \"type\": \"object\", \"properties\": { \"unix_millis_time\": { \"title\": \"Unix Millis Time\", \"type\": \"integer\" }, \"trigger\": { \"title\": \"Trigger\", \"type\": \"boolean\" } }, \"required\": [ \"unix_millis_time\", \"trigger\" ] } ``` ### /delivery_alerts ``` { \"title\": \"DeliveryAlert\", \"type\": \"object\", \"properties\": { \"id\": { \"title\": \"Id\", \"type\": \"string\" }, \"category\": { \"$ref\": \"#/definitions/Category\" }, \"tier\": { \"$ref\": \"#/definitions/Tier\" }, \"action\": { \"$ref\": \"#/definitions/Action\" }, \"task_id\": { \"title\": \"Task Id\", \"type\": \"string\" }, \"message\": { \"title\": \"Message\", \"type\": \"string\" } }, \"required\": [ \"id\", \"category\", \"tier\", \"action\", \"task_id\", \"message\" ], \"definitions\": { \"Category\": { \"title\": \"Category\", \"description\": \"An enumeration.\", \"enum\": [ \"missing\", \"wrong\", \"obstructed\", \"cancelled\" ], \"type\": \"string\" }, \"Tier\": { \"title\": \"Tier\", \"description\": \"An enumeration.\", \"enum\": [ \"warning\", \"error\" ], \"type\": \"string\" }, \"Action\": { \"title\": \"Action\", \"description\": \"An enumeration.\", \"enum\": [ \"waiting\", \"cancelled\", \"override\", \"resume\" ], \"type\": \"string\" } } } ``` ### /doors/{door_name}/state ``` { \"title\": \"DoorState\", \"type\": \"object\", \"properties\": { \"door_time\": { \"title\": \"Door Time\", \"default\": { \"sec\": 0, \"nanosec\": 0 }, \"allOf\": [ { \"$ref\": \"#/definitions/Time\" } ] }, \"door_name\": { \"title\": \"Door Name\", \"default\": \"\", \"type\": \"string\" }, \"current_mode\": { \"title\": \"Current Mode\", \"default\": { \"value\": 0 }, \"allOf\": [ { \"$ref\": \"#/definitions/DoorMode\" } ] } }, \"required\": [ \"door_time\", \"door_name\", \"current_mode\" ], \"definitions\": { \"Time\": { \"title\": \"Time\", \"type\": \"object\", \"properties\": { \"sec\": { \"title\": \"Sec\", \"default\": 0, \"minimum\": -2147483648, \"maximum\": 2147483647, \"type\": \"integer\" }, \"nanosec\": { \"title\": \"Nanosec\", \"default\": 0, \"minimum\": 0, \"maximum\": 4294967295, \"type\": \"integer\" } }, \"required\": [ \"sec\", \"nanosec\" ] }, \"DoorMode\": { \"title\": \"DoorMode\", \"type\": \"object\", \"properties\": { \"value\": { \"title\": \"Value\", \"default\": 0, \"minimum\": 0, \"maximum\": 4294967295, \"type\": \"integer\" } }, \"required\": [ \"value\" ] } } } ``` ### /doors/{door_name}/health ``` { \"title\": \"DoorHealth\", \"type\": \"object\", \"properties\": { \"health_status\": { \"title\": \"Health Status\", \"maxLength\": 255, \"nullable\": true, \"type\": \"string\" }, \"health_message\": { \"title\": \"Health Message\", \"nullable\": true, \"type\": \"string\" }, \"id_\": { \"title\": \"Id \", \"maxLength\": 255, \"type\": \"string\" } }, \"required\": [ \"health_status\", \"id_\" ], \"additionalProperties\": false } ``` ### /lifts/{lift_name}/state ``` { \"title\": \"LiftState\", \"type\": \"object\", \"properties\": { \"lift_time\": { \"title\": \"Lift Time\", \"default\": { \"sec\": 0, \"nanosec\": 0 }, \"allOf\": [ { \"$ref\": \"#/definitions/Time\" } ] }, \"lift_name\": { \"title\": \"Lift Name\", \"default\": \"\", \"type\": \"string\" }, \"available_floors\": { \"title\": \"Available Floors\", \"default\": [], \"type\": \"array\", \"items\": { \"type\": \"string\" } }, \"current_floor\": { \"title\": \"Current Floor\", \"default\": \"\", \"type\": \"string\" }, \"destination_floor\": { \"title\": \"Destination Floor\", \"default\": \"\", \"type\": \"string\" }, \"door_state\": { \"title\": \"Door State\", \"default\": 0, \"minimum\": 0, \"maximum\": 255, \"type\": \"integer\" }, \"motion_state\": { \"title\": \"Motion State\", \"default\": 0, \"minimum\": 0, \"maximum\": 255, \"type\": \"integer\" }, \"available_modes\": { \"title\": \"Available Modes\", \"type\": \"array\", \"items\": { \"type\": \"integer\" } }, \"current_mode\": { \"title\": \"Current Mode\", \"default\": 0, \"minimum\": 0, \"maximum\": 255, \"type\": \"integer\" }, \"session_id\": { \"title\": \"Session Id\", \"default\": \"\", \"type\": \"string\" } }, \"required\": [ \"lift_time\", \"lift_name\", \"available_floors\", \"current_floor\", \"destination_floor\", \"door_state\", \"motion_state\", \"available_modes\", \"current_mode\", \"session_id\" ], \"definitions\": { \"Time\": { \"title\": \"Time\", \"type\": \"object\", \"properties\": { \"sec\": { \"title\": \"Sec\", \"default\": 0, \"minimum\": -2147483648, \"maximum\": 2147483647, \"type\": \"integer\" }, \"nanosec\": { \"title\": \"Nanosec\", \"default\": 0, \"minimum\": 0, \"maximum\": 4294967295, \"type\": \"integer\" } }, \"required\": [ \"sec\", \"nanosec\" ] } } } ``` ### /lifts/{lift_name}/health ``` { \"title\": \"LiftHealth\", \"type\": \"object\", \"properties\": { \"health_status\": { \"title\": \"Health Status\", \"maxLength\": 255, \"nullable\": true, \"type\": \"string\" }, \"health_message\": { \"title\": \"Health Message\", \"nullable\": true, \"type\": \"string\" }, \"id_\": { \"title\": \"Id \", \"maxLength\": 255, \"type\": \"string\" } }, \"required\": [ \"health_status\", \"id_\" ], \"additionalProperties\": false } ``` ### /tasks/{task_id}/state ``` { \"title\": \"TaskState\", \"type\": \"object\", \"properties\": { \"booking\": { \"$ref\": \"#/definitions/Booking\" }, \"category\": { \"$ref\": \"#/definitions/Category\" }, \"detail\": { \"$ref\": \"#/definitions/Detail\" }, \"unix_millis_start_time\": { \"title\": \"Unix Millis Start Time\", \"type\": \"integer\" }, \"unix_millis_finish_time\": { \"title\": \"Unix Millis Finish Time\", \"type\": \"integer\" }, \"original_estimate_millis\": { \"$ref\": \"#/definitions/EstimateMillis\" }, \"estimate_millis\": { \"$ref\": \"#/definitions/EstimateMillis\" }, \"assigned_to\": { \"title\": \"Assigned To\", \"description\": \"Which agent (robot) is the task assigned to\", \"allOf\": [ { \"$ref\": \"#/definitions/AssignedTo\" } ] }, \"status\": { \"$ref\": \"#/definitions/Status\" }, \"dispatch\": { \"$ref\": \"#/definitions/Dispatch\" }, \"phases\": { \"title\": \"Phases\", \"description\": \"A dictionary of the states of the phases of the task. The keys (property names) are phase IDs, which are integers.\", \"type\": \"object\", \"additionalProperties\": { \"$ref\": \"#/definitions/Phase\" } }, \"completed\": { \"title\": \"Completed\", \"description\": \"An array of the IDs of completed phases of this task\", \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/Id\" } }, \"active\": { \"title\": \"Active\", \"description\": \"The ID of the active phase for this task\", \"allOf\": [ { \"$ref\": \"#/definitions/Id\" } ] }, \"pending\": { \"title\": \"Pending\", \"description\": \"An array of the pending phases of this task\", \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/Id\" } }, \"interruptions\": { \"title\": \"Interruptions\", \"description\": \"A dictionary of interruptions that have been applied to this task. The keys (property names) are the unique token of the interruption request.\", \"type\": \"object\", \"additionalProperties\": { \"$ref\": \"#/definitions/Interruption\" } }, \"cancellation\": { \"title\": \"Cancellation\", \"description\": \"If the task was cancelled, this will describe information about the request.\", \"allOf\": [ { \"$ref\": \"#/definitions/Cancellation\" } ] }, \"killed\": { \"title\": \"Killed\", \"description\": \"If the task was killed, this will describe information about the request.\", \"allOf\": [ { \"$ref\": \"#/definitions/Killed\" } ] }, \"unix_millis_warn_time\": { \"title\": \"Unix Millis Warn Time\", \"type\": \"integer\" } }, \"required\": [ \"booking\" ], \"definitions\": { \"Booking\": { \"title\": \"Booking\", \"type\": \"object\", \"properties\": { \"id\": { \"title\": \"Id\", \"description\": \"The unique identifier for this task\", \"type\": \"string\" }, \"unix_millis_earliest_start_time\": { \"title\": \"Unix Millis Earliest Start Time\", \"type\": \"integer\" }, \"unix_millis_request_time\": { \"title\": \"Unix Millis Request Time\", \"type\": \"integer\" }, \"priority\": { \"title\": \"Priority\", \"description\": \"Priority information about this task\", \"anyOf\": [ { \"type\": \"object\" }, { \"type\": \"string\" } ] }, \"labels\": { \"title\": \"Labels\", \"description\": \"Information about how and why this task was booked\", \"type\": \"array\", \"items\": { \"type\": \"string\" } }, \"requester\": { \"title\": \"Requester\", \"description\": \"(Optional) An identifier for the entity that requested this task\", \"type\": \"string\" } }, \"required\": [ \"id\" ] }, \"Category\": { \"title\": \"Category\", \"description\": \"The category of this task or phase\", \"type\": \"string\" }, \"Detail\": { \"title\": \"Detail\", \"description\": \"Detailed information about a task, phase, or event\", \"anyOf\": [ { \"type\": \"object\" }, { \"type\": \"array\", \"items\": {} }, { \"type\": \"string\" } ] }, \"EstimateMillis\": { \"title\": \"EstimateMillis\", \"description\": \"An estimate, in milliseconds, of how long the subject will take to complete\", \"minimum\": 0, \"type\": \"integer\" }, \"AssignedTo\": { \"title\": \"AssignedTo\", \"type\": \"object\", \"properties\": { \"group\": { \"title\": \"Group\", \"type\": \"string\" }, \"name\": { \"title\": \"Name\", \"type\": \"string\" } }, \"required\": [ \"group\", \"name\" ] }, \"Status\": { \"title\": \"Status\", \"description\": \"An enumeration.\", \"enum\": [ \"uninitialized\", \"blocked\", \"error\", \"failed\", \"queued\", \"standby\", \"underway\", \"delayed\", \"skipped\", \"canceled\", \"killed\", \"completed\" ] }, \"Status1\": { \"title\": \"Status1\", \"description\": \"An enumeration.\", \"enum\": [ \"queued\", \"selected\", \"dispatched\", \"failed_to_assign\", \"canceled_in_flight\" ] }, \"Assignment\": { \"title\": \"Assignment\", \"type\": \"object\", \"properties\": { \"fleet_name\": { \"title\": \"Fleet Name\", \"type\": \"string\" }, \"expected_robot_name\": { \"title\": \"Expected Robot Name\", \"type\": \"string\" } } }, \"Error\": { \"title\": \"Error\", \"type\": \"object\", \"properties\": { \"code\": { \"title\": \"Code\", \"description\": \"A standard code for the kind of error that has occurred\", \"minimum\": 0, \"type\": \"integer\" }, \"category\": { \"title\": \"Category\", \"description\": \"The category of the error\", \"type\": \"string\" }, \"detail\": { \"title\": \"Detail\", \"description\": \"Details about the error\", \"type\": \"string\" } } }, \"Dispatch\": { \"title\": \"Dispatch\", \"type\": \"object\", \"properties\": { \"status\": { \"$ref\": \"#/definitions/Status1\" }, \"assignment\": { \"$ref\": \"#/definitions/Assignment\" }, \"errors\": { \"title\": \"Errors\", \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/Error\" } } }, \"required\": [ \"status\" ] }, \"Id\": { \"title\": \"Id\", \"minimum\": 0, \"type\": \"integer\" }, \"EventState\": { \"title\": \"EventState\", \"type\": \"object\", \"properties\": { \"id\": { \"$ref\": \"#/definitions/Id\" }, \"status\": { \"$ref\": \"#/definitions/Status\" }, \"name\": { \"title\": \"Name\", \"description\": \"The brief name of the event\", \"type\": \"string\" }, \"detail\": { \"title\": \"Detail\", \"description\": \"Detailed information about the event\", \"allOf\": [ { \"$ref\": \"#/definitions/Detail\" } ] }, \"deps\": { \"title\": \"Deps\", \"description\": \"This event may depend on other events. This array contains the IDs of those other event dependencies.\", \"type\": \"array\", \"items\": { \"type\": \"integer\", \"minimum\": 0 } } }, \"required\": [ \"id\" ] }, \"Undo\": { \"title\": \"Undo\", \"type\": \"object\", \"properties\": { \"unix_millis_request_time\": { \"title\": \"Unix Millis Request Time\", \"description\": \"The time that the undo skip request arrived\", \"type\": \"integer\" }, \"labels\": { \"title\": \"Labels\", \"description\": \"Labels to describe the undo skip request\", \"type\": \"array\", \"items\": { \"type\": \"string\" } } }, \"required\": [ \"unix_millis_request_time\", \"labels\" ] }, \"SkipPhaseRequest\": { \"title\": \"SkipPhaseRequest\", \"type\": \"object\", \"properties\": { \"unix_millis_request_time\": { \"title\": \"Unix Millis Request Time\", \"description\": \"The time that the skip request arrived\", \"type\": \"integer\" }, \"labels\": { \"title\": \"Labels\", \"description\": \"Labels to describe the purpose of the skip request\", \"type\": \"array\", \"items\": { \"type\": \"string\" } }, \"undo\": { \"title\": \"Undo\", \"description\": \"Information about an undo skip request that applied to this request\", \"allOf\": [ { \"$ref\": \"#/definitions/Undo\" } ] } }, \"required\": [ \"unix_millis_request_time\", \"labels\" ] }, \"Phase\": { \"title\": \"Phase\", \"type\": \"object\", \"properties\": { \"id\": { \"$ref\": \"#/definitions/Id\" }, \"category\": { \"$ref\": \"#/definitions/Category\" }, \"detail\": { \"$ref\": \"#/definitions/Detail\" }, \"unix_millis_start_time\": { \"title\": \"Unix Millis Start Time\", \"type\": \"integer\" }, \"unix_millis_finish_time\": { \"title\": \"Unix Millis Finish Time\", \"type\": \"integer\" }, \"original_estimate_millis\": { \"$ref\": \"#/definitions/EstimateMillis\" }, \"estimate_millis\": { \"$ref\": \"#/definitions/EstimateMillis\" }, \"final_event_id\": { \"$ref\": \"#/definitions/Id\" }, \"events\": { \"title\": \"Events\", \"description\": \"A dictionary of events for this phase. The keys (property names) are the event IDs, which are integers.\", \"type\": \"object\", \"additionalProperties\": { \"$ref\": \"#/definitions/EventState\" } }, \"skip_requests\": { \"title\": \"Skip Requests\", \"description\": \"Information about any skip requests that have been received\", \"type\": \"object\", \"additionalProperties\": { \"$ref\": \"#/definitions/SkipPhaseRequest\" } } }, \"required\": [ \"id\" ] }, \"ResumedBy\": { \"title\": \"ResumedBy\", \"type\": \"object\", \"properties\": { \"unix_millis_request_time\": { \"title\": \"Unix Millis Request Time\", \"description\": \"The time that the resume request arrived\", \"type\": \"integer\" }, \"labels\": { \"title\": \"Labels\", \"description\": \"Labels to describe the resume request\", \"type\": \"array\", \"items\": { \"type\": \"string\" } } }, \"required\": [ \"labels\" ] }, \"Interruption\": { \"title\": \"Interruption\", \"type\": \"object\", \"properties\": { \"unix_millis_request_time\": { \"title\": \"Unix Millis Request Time\", \"description\": \"The time that the interruption request arrived\", \"type\": \"integer\" }, \"labels\": { \"title\": \"Labels\", \"description\": \"Labels to describe the purpose of the interruption\", \"type\": \"array\", \"items\": { \"type\": \"string\" } }, \"resumed_by\": { \"title\": \"Resumed By\", \"description\": \"Information about the resume request that ended this interruption. This field will be missing if the interruption is still active.\", \"allOf\": [ { \"$ref\": \"#/definitions/ResumedBy\" } ] } }, \"required\": [ \"unix_millis_request_time\", \"labels\" ] }, \"Cancellation\": { \"title\": \"Cancellation\", \"type\": \"object\", \"properties\": { \"unix_millis_request_time\": { \"title\": \"Unix Millis Request Time\", \"description\": \"The time that the cancellation request arrived\", \"type\": \"integer\" }, \"labels\": { \"title\": \"Labels\", \"description\": \"Labels to describe the cancel request\", \"type\": \"array\", \"items\": { \"type\": \"string\" } } }, \"required\": [ \"unix_millis_request_time\", \"labels\" ] }, \"Killed\": { \"title\": \"Killed\", \"type\": \"object\", \"properties\": { \"unix_millis_request_time\": { \"title\": \"Unix Millis Request Time\", \"description\": \"The time that the cancellation request arrived\", \"type\": \"integer\" }, \"labels\": { \"title\": \"Labels\", \"description\": \"Labels to describe the kill request\", \"type\": \"array\", \"items\": { \"type\": \"string\" } } }, \"required\": [ \"unix_millis_request_time\", \"labels\" ] } } } ``` ### /tasks/{task_id}/log ``` { \"title\": \"TaskEventLog\", \"type\": \"object\", \"properties\": { \"task_id\": { \"title\": \"Task Id\", \"type\": \"string\" }, \"log\": { \"title\": \"Log\", \"description\": \"Log entries related to the overall task\", \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/LogEntry\" } }, \"phases\": { \"title\": \"Phases\", \"description\": \"A dictionary whose keys (property names) are the indices of a phase\", \"type\": \"object\", \"additionalProperties\": { \"$ref\": \"#/definitions/Phases\" } } }, \"required\": [ \"task_id\" ], \"additionalProperties\": false, \"definitions\": { \"Tier\": { \"title\": \"Tier\", \"description\": \"An enumeration.\", \"enum\": [ \"uninitialized\", \"info\", \"warning\", \"error\" ] }, \"LogEntry\": { \"title\": \"LogEntry\", \"type\": \"object\", \"properties\": { \"seq\": { \"title\": \"Seq\", \"description\": \"Sequence number for this entry. Each entry has a unique sequence number which monotonically increase, until integer overflow causes a wrap around.\", \"exclusiveMaximum\": 4294967296, \"minimum\": 0, \"type\": \"integer\" }, \"tier\": { \"description\": \"The importance level of the log entry\", \"allOf\": [ { \"$ref\": \"#/definitions/Tier\" } ] }, \"unix_millis_time\": { \"title\": \"Unix Millis Time\", \"type\": \"integer\" }, \"text\": { \"title\": \"Text\", \"description\": \"The text of the log entry\", \"type\": \"string\" } }, \"required\": [ \"seq\", \"tier\", \"unix_millis_time\", \"text\" ] }, \"Phases\": { \"title\": \"Phases\", \"type\": \"object\", \"properties\": { \"log\": { \"title\": \"Log\", \"description\": \"Log entries related to the overall phase\", \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/LogEntry\" } }, \"events\": { \"title\": \"Events\", \"description\": \"A dictionary whose keys (property names) are the indices of an event in the phase\", \"type\": \"object\", \"additionalProperties\": { \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/LogEntry\" } } } }, \"additionalProperties\": false } } } ``` ### /dispensers/{guid}/state ``` { \"title\": \"DispenserState\", \"type\": \"object\", \"properties\": { \"time\": { \"title\": \"Time\", \"default\": { \"sec\": 0, \"nanosec\": 0 }, \"allOf\": [ { \"$ref\": \"#/definitions/Time\" } ] }, \"guid\": { \"title\": \"Guid\", \"default\": \"\", \"type\": \"string\" }, \"mode\": { \"title\": \"Mode\", \"default\": 0, \"minimum\": -2147483648, \"maximum\": 2147483647, \"type\": \"integer\" }, \"request_guid_queue\": { \"title\": \"Request Guid Queue\", \"default\": [], \"type\": \"array\", \"items\": { \"type\": \"string\" } }, \"seconds_remaining\": { \"title\": \"Seconds Remaining\", \"default\": 0, \"type\": \"number\" } }, \"required\": [ \"time\", \"guid\", \"mode\", \"request_guid_queue\", \"seconds_remaining\" ], \"definitions\": { \"Time\": { \"title\": \"Time\", \"type\": \"object\", \"properties\": { \"sec\": { \"title\": \"Sec\", \"default\": 0, \"minimum\": -2147483648, \"maximum\": 2147483647, \"type\": \"integer\" }, \"nanosec\": { \"title\": \"Nanosec\", \"default\": 0, \"minimum\": 0, \"maximum\": 4294967295, \"type\": \"integer\" } }, \"required\": [ \"sec\", \"nanosec\" ] } } } ``` ### /dispensers/{guid}/health ``` { \"title\": \"DispenserHealth\", \"type\": \"object\", \"properties\": { \"health_status\": { \"title\": \"Health Status\", \"maxLength\": 255, \"nullable\": true, \"type\": \"string\" }, \"health_message\": { \"title\": \"Health Message\", \"nullable\": true, \"type\": \"string\" }, \"id_\": { \"title\": \"Id \", \"maxLength\": 255, \"type\": \"string\" } }, \"required\": [ \"health_status\", \"id_\" ], \"additionalProperties\": false } ``` ### /ingestors/{guid}/state ``` { \"title\": \"IngestorState\", \"type\": \"object\", \"properties\": { \"time\": { \"title\": \"Time\", \"default\": { \"sec\": 0, \"nanosec\": 0 }, \"allOf\": [ { \"$ref\": \"#/definitions/Time\" } ] }, \"guid\": { \"title\": \"Guid\", \"default\": \"\", \"type\": \"string\" }, \"mode\": { \"title\": \"Mode\", \"default\": 0, \"minimum\": -2147483648, \"maximum\": 2147483647, \"type\": \"integer\" }, \"request_guid_queue\": { \"title\": \"Request Guid Queue\", \"default\": [], \"type\": \"array\", \"items\": { \"type\": \"string\" } }, \"seconds_remaining\": { \"title\": \"Seconds Remaining\", \"default\": 0, \"type\": \"number\" } }, \"required\": [ \"time\", \"guid\", \"mode\", \"request_guid_queue\", \"seconds_remaining\" ], \"definitions\": { \"Time\": { \"title\": \"Time\", \"type\": \"object\", \"properties\": { \"sec\": { \"title\": \"Sec\", \"default\": 0, \"minimum\": -2147483648, \"maximum\": 2147483647, \"type\": \"integer\" }, \"nanosec\": { \"title\": \"Nanosec\", \"default\": 0, \"minimum\": 0, \"maximum\": 4294967295, \"type\": \"integer\" } }, \"required\": [ \"sec\", \"nanosec\" ] } } } ``` ### /ingestors/{guid}/health ``` { \"title\": \"IngestorHealth\", \"type\": \"object\", \"properties\": { \"health_status\": { \"title\": \"Health Status\", \"maxLength\": 255, \"nullable\": true, \"type\": \"string\" }, \"health_message\": { \"title\": \"Health Message\", \"nullable\": true, \"type\": \"string\" }, \"id_\": { \"title\": \"Id \", \"maxLength\": 255, \"type\": \"string\" } }, \"required\": [ \"health_status\", \"id_\" ], \"additionalProperties\": false } ``` ### /fleets/{name}/state ``` { \"title\": \"FleetState\", \"type\": \"object\", \"properties\": { \"name\": { \"title\": \"Name\", \"type\": \"string\" }, \"robots\": { \"title\": \"Robots\", \"description\": \"A dictionary of the states of the robots that belong to this fleet\", \"type\": \"object\", \"additionalProperties\": { \"$ref\": \"#/definitions/RobotState\" } } }, \"definitions\": { \"Status\": { \"title\": \"Status\", \"description\": \"An enumeration.\", \"enum\": [ \"uninitialized\", \"offline\", \"shutdown\", \"idle\", \"charging\", \"working\", \"error\" ] }, \"Location2D\": { \"title\": \"Location2D\", \"type\": \"object\", \"properties\": { \"map\": { \"title\": \"Map\", \"type\": \"string\" }, \"x\": { \"title\": \"X\", \"type\": \"number\" }, \"y\": { \"title\": \"Y\", \"type\": \"number\" }, \"yaw\": { \"title\": \"Yaw\", \"type\": \"number\" } }, \"required\": [ \"map\", \"x\", \"y\", \"yaw\" ] }, \"Issue\": { \"title\": \"Issue\", \"type\": \"object\", \"properties\": { \"category\": { \"title\": \"Category\", \"description\": \"Category of the robot\'s issue\", \"type\": \"string\" }, \"detail\": { \"title\": \"Detail\", \"description\": \"Detailed information about the issue\", \"anyOf\": [ { \"type\": \"object\" }, { \"type\": \"array\", \"items\": {} }, { \"type\": \"string\" } ] } } }, \"Commission\": { \"title\": \"Commission\", \"type\": \"object\", \"properties\": { \"dispatch_tasks\": { \"title\": \"Dispatch Tasks\", \"description\": \"Should the robot accept dispatched tasks, true/false. When used in a request, leave this unset to not change the robot\'s current value.\", \"type\": \"boolean\" }, \"direct_tasks\": { \"title\": \"Direct Tasks\", \"description\": \"Should the robot accept direct task requests, true/false. When used in a request, leave this unset to not change the robot\'s current value.\", \"type\": \"boolean\" }, \"idle_behavior\": { \"title\": \"Idle Behavior\", \"description\": \"Should the robot perform its idle behavior, true/false. When used in a request, leave this unset to not change the robot\'s current value.\", \"type\": \"boolean\" } } }, \"MutexGroups\": { \"title\": \"MutexGroups\", \"type\": \"object\", \"properties\": { \"locked\": { \"title\": \"Locked\", \"description\": \"A list of mutex groups that this robot has currently locked\", \"type\": \"array\", \"items\": { \"type\": \"string\" } }, \"requesting\": { \"title\": \"Requesting\", \"description\": \"A list of the mutex groups that this robot is currently requesting but has not lockd yet\", \"type\": \"array\", \"items\": { \"type\": \"string\" } } } }, \"RobotState\": { \"title\": \"RobotState\", \"type\": \"object\", \"properties\": { \"name\": { \"title\": \"Name\", \"type\": \"string\" }, \"status\": { \"description\": \"A simple token representing the status of the robot\", \"allOf\": [ { \"$ref\": \"#/definitions/Status\" } ] }, \"task_id\": { \"title\": \"Task Id\", \"description\": \"The ID of the task this robot is currently working on. Empty string if the robot is not working on a task.\", \"type\": \"string\" }, \"unix_millis_time\": { \"title\": \"Unix Millis Time\", \"type\": \"integer\" }, \"location\": { \"$ref\": \"#/definitions/Location2D\" }, \"battery\": { \"title\": \"Battery\", \"description\": \"State of charge of the battery. Values range from 0.0 (depleted) to 1.0 (fully charged)\", \"minimum\": 0.0, \"maximum\": 1.0, \"type\": \"number\" }, \"issues\": { \"title\": \"Issues\", \"description\": \"A list of issues with the robot that operators need to address\", \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/Issue\" } }, \"commission\": { \"$ref\": \"#/definitions/Commission\" }, \"mutex_groups\": { \"title\": \"Mutex Groups\", \"description\": \"Information about the mutex groups that this robot is interacting with\", \"allOf\": [ { \"$ref\": \"#/definitions/MutexGroups\" } ] } } } } } ``` ### /fleets/{name}/log ``` { \"title\": \"FleetLog\", \"type\": \"object\", \"properties\": { \"name\": { \"title\": \"Name\", \"type\": \"string\" }, \"log\": { \"title\": \"Log\", \"description\": \"Log for the overall fleet\", \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/LogEntry\" } }, \"robots\": { \"title\": \"Robots\", \"description\": \"Dictionary of logs for the individual robots. The keys (property names) are the robot names.\", \"type\": \"object\", \"additionalProperties\": { \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/LogEntry\" } } } }, \"definitions\": { \"Tier\": { \"title\": \"Tier\", \"description\": \"An enumeration.\", \"enum\": [ \"uninitialized\", \"info\", \"warning\", \"error\" ] }, \"LogEntry\": { \"title\": \"LogEntry\", \"type\": \"object\", \"properties\": { \"seq\": { \"title\": \"Seq\", \"description\": \"Sequence number for this entry. Each entry has a unique sequence number which monotonically increase, until integer overflow causes a wrap around.\", \"exclusiveMaximum\": 4294967296, \"minimum\": 0, \"type\": \"integer\" }, \"tier\": { \"description\": \"The importance level of the log entry\", \"allOf\": [ { \"$ref\": \"#/definitions/Tier\" } ] }, \"unix_millis_time\": { \"title\": \"Unix Millis Time\", \"type\": \"integer\" }, \"text\": { \"title\": \"Text\", \"description\": \"The text of the log entry\", \"type\": \"string\" } }, \"required\": [ \"seq\", \"tier\", \"unix_millis_time\", \"text\" ] } } } ``` + * # NOTE: This endpoint is here for documentation purposes only, this is _not_ a REST endpoint. ## About This exposes a minimal pubsub system built on top of socket.io. It works similar to a normal socket.io endpoint, except that are 2 special rooms which control subscriptions. ## Rooms ### subscribe Clients must send a message to this room to start receiving messages on other rooms. The message must be of the form: ``` { \"room\": \"\" } ``` ### unsubscribe Clients can send a message to this room to stop receiving messages on other rooms. The message must be of the form: ``` { \"room\": \"\" } ``` ### /alerts ``` { \"title\": \"Alert\", \"description\": \"General alert that can be triggered by events.\", \"type\": \"object\", \"properties\": { \"id\": { \"title\": \"Id\", \"maxLength\": 255, \"type\": \"string\" }, \"original_id\": { \"title\": \"Original Id\", \"maxLength\": 255, \"type\": \"string\" }, \"category\": { \"title\": \"Category\", \"description\": \"Default: default
Task: task
Fleet: fleet
Robot: robot\", \"maxLength\": 7, \"type\": \"string\" }, \"unix_millis_created_time\": { \"title\": \"Unix Millis Created Time\", \"minimum\": -9223372036854775808, \"maximum\": 9223372036854775807, \"type\": \"integer\" }, \"acknowledged_by\": { \"title\": \"Acknowledged By\", \"maxLength\": 255, \"nullable\": true, \"type\": \"string\" }, \"unix_millis_acknowledged_time\": { \"title\": \"Unix Millis Acknowledged Time\", \"minimum\": -9223372036854775808, \"maximum\": 9223372036854775807, \"nullable\": true, \"type\": \"integer\" } }, \"required\": [ \"id\", \"original_id\", \"category\", \"unix_millis_created_time\" ], \"additionalProperties\": false } ``` ### /beacons ``` { \"title\": \"BeaconState\", \"type\": \"object\", \"properties\": { \"id\": { \"title\": \"Id\", \"maxLength\": 255, \"type\": \"string\" }, \"online\": { \"title\": \"Online\", \"type\": \"boolean\" }, \"category\": { \"title\": \"Category\", \"maxLength\": 255, \"nullable\": true, \"type\": \"string\" }, \"activated\": { \"title\": \"Activated\", \"type\": \"boolean\" }, \"level\": { \"title\": \"Level\", \"maxLength\": 255, \"nullable\": true, \"type\": \"string\" } }, \"required\": [ \"id\", \"online\", \"activated\" ], \"additionalProperties\": false } ``` ### /building_map ``` { \"title\": \"BuildingMap\", \"type\": \"object\", \"properties\": { \"name\": { \"title\": \"Name\", \"default\": \"\", \"type\": \"string\" }, \"levels\": { \"title\": \"Levels\", \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/Level\" } }, \"lifts\": { \"title\": \"Lifts\", \"default\": [], \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/Lift\" } } }, \"required\": [ \"name\", \"levels\", \"lifts\" ], \"definitions\": { \"AffineImage\": { \"title\": \"AffineImage\", \"type\": \"object\", \"properties\": { \"name\": { \"title\": \"Name\", \"default\": \"\", \"type\": \"string\" }, \"x_offset\": { \"title\": \"X Offset\", \"default\": 0, \"type\": \"number\" }, \"y_offset\": { \"title\": \"Y Offset\", \"default\": 0, \"type\": \"number\" }, \"yaw\": { \"title\": \"Yaw\", \"default\": 0, \"type\": \"number\" }, \"scale\": { \"title\": \"Scale\", \"default\": 0, \"type\": \"number\" }, \"encoding\": { \"title\": \"Encoding\", \"default\": \"\", \"type\": \"string\" }, \"data\": { \"title\": \"Data\", \"type\": \"string\" } }, \"required\": [ \"name\", \"x_offset\", \"y_offset\", \"yaw\", \"scale\", \"encoding\", \"data\" ] }, \"Place\": { \"title\": \"Place\", \"type\": \"object\", \"properties\": { \"name\": { \"title\": \"Name\", \"default\": \"\", \"type\": \"string\" }, \"x\": { \"title\": \"X\", \"default\": 0, \"type\": \"number\" }, \"y\": { \"title\": \"Y\", \"default\": 0, \"type\": \"number\" }, \"yaw\": { \"title\": \"Yaw\", \"default\": 0, \"type\": \"number\" }, \"position_tolerance\": { \"title\": \"Position Tolerance\", \"default\": 0, \"type\": \"number\" }, \"yaw_tolerance\": { \"title\": \"Yaw Tolerance\", \"default\": 0, \"type\": \"number\" } }, \"required\": [ \"name\", \"x\", \"y\", \"yaw\", \"position_tolerance\", \"yaw_tolerance\" ] }, \"Door\": { \"title\": \"Door\", \"type\": \"object\", \"properties\": { \"name\": { \"title\": \"Name\", \"default\": \"\", \"type\": \"string\" }, \"v1_x\": { \"title\": \"V1 X\", \"default\": 0, \"type\": \"number\" }, \"v1_y\": { \"title\": \"V1 Y\", \"default\": 0, \"type\": \"number\" }, \"v2_x\": { \"title\": \"V2 X\", \"default\": 0, \"type\": \"number\" }, \"v2_y\": { \"title\": \"V2 Y\", \"default\": 0, \"type\": \"number\" }, \"door_type\": { \"title\": \"Door Type\", \"default\": 0, \"minimum\": 0, \"maximum\": 255, \"type\": \"integer\" }, \"motion_range\": { \"title\": \"Motion Range\", \"default\": 0, \"type\": \"number\" }, \"motion_direction\": { \"title\": \"Motion Direction\", \"default\": 0, \"minimum\": -2147483648, \"maximum\": 2147483647, \"type\": \"integer\" } }, \"required\": [ \"name\", \"v1_x\", \"v1_y\", \"v2_x\", \"v2_y\", \"door_type\", \"motion_range\", \"motion_direction\" ] }, \"Param\": { \"title\": \"Param\", \"type\": \"object\", \"properties\": { \"name\": { \"title\": \"Name\", \"default\": \"\", \"type\": \"string\" }, \"type\": { \"title\": \"Type\", \"default\": 0, \"minimum\": 0, \"maximum\": 4294967295, \"type\": \"integer\" }, \"value_int\": { \"title\": \"Value Int\", \"default\": 0, \"minimum\": -2147483648, \"maximum\": 2147483647, \"type\": \"integer\" }, \"value_float\": { \"title\": \"Value Float\", \"default\": 0, \"type\": \"number\" }, \"value_string\": { \"title\": \"Value String\", \"default\": \"\", \"type\": \"string\" }, \"value_bool\": { \"title\": \"Value Bool\", \"default\": false, \"type\": \"boolean\" } }, \"required\": [ \"name\", \"type\", \"value_int\", \"value_float\", \"value_string\", \"value_bool\" ] }, \"GraphNode\": { \"title\": \"GraphNode\", \"type\": \"object\", \"properties\": { \"x\": { \"title\": \"X\", \"default\": 0, \"type\": \"number\" }, \"y\": { \"title\": \"Y\", \"default\": 0, \"type\": \"number\" }, \"name\": { \"title\": \"Name\", \"default\": \"\", \"type\": \"string\" }, \"params\": { \"title\": \"Params\", \"default\": [], \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/Param\" } } }, \"required\": [ \"x\", \"y\", \"name\", \"params\" ] }, \"GraphEdge\": { \"title\": \"GraphEdge\", \"type\": \"object\", \"properties\": { \"v1_idx\": { \"title\": \"V1 Idx\", \"default\": 0, \"minimum\": 0, \"maximum\": 4294967295, \"type\": \"integer\" }, \"v2_idx\": { \"title\": \"V2 Idx\", \"default\": 0, \"minimum\": 0, \"maximum\": 4294967295, \"type\": \"integer\" }, \"params\": { \"title\": \"Params\", \"default\": [], \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/Param\" } }, \"edge_type\": { \"title\": \"Edge Type\", \"default\": 0, \"minimum\": 0, \"maximum\": 255, \"type\": \"integer\" } }, \"required\": [ \"v1_idx\", \"v2_idx\", \"params\", \"edge_type\" ] }, \"Graph\": { \"title\": \"Graph\", \"type\": \"object\", \"properties\": { \"name\": { \"title\": \"Name\", \"default\": \"\", \"type\": \"string\" }, \"vertices\": { \"title\": \"Vertices\", \"default\": [], \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/GraphNode\" } }, \"edges\": { \"title\": \"Edges\", \"default\": [], \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/GraphEdge\" } }, \"params\": { \"title\": \"Params\", \"default\": [], \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/Param\" } } }, \"required\": [ \"name\", \"vertices\", \"edges\", \"params\" ] }, \"Level\": { \"title\": \"Level\", \"type\": \"object\", \"properties\": { \"name\": { \"title\": \"Name\", \"default\": \"\", \"type\": \"string\" }, \"elevation\": { \"title\": \"Elevation\", \"default\": 0, \"type\": \"number\" }, \"images\": { \"title\": \"Images\", \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/AffineImage\" } }, \"places\": { \"title\": \"Places\", \"default\": [], \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/Place\" } }, \"doors\": { \"title\": \"Doors\", \"default\": [], \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/Door\" } }, \"nav_graphs\": { \"title\": \"Nav Graphs\", \"default\": [], \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/Graph\" } }, \"wall_graph\": { \"title\": \"Wall Graph\", \"default\": { \"name\": \"\", \"vertices\": [], \"edges\": [], \"params\": [] }, \"allOf\": [ { \"$ref\": \"#/definitions/Graph\" } ] } }, \"required\": [ \"name\", \"elevation\", \"images\", \"places\", \"doors\", \"nav_graphs\", \"wall_graph\" ] }, \"Lift\": { \"title\": \"Lift\", \"type\": \"object\", \"properties\": { \"name\": { \"title\": \"Name\", \"default\": \"\", \"type\": \"string\" }, \"levels\": { \"title\": \"Levels\", \"default\": [], \"type\": \"array\", \"items\": { \"type\": \"string\" } }, \"doors\": { \"title\": \"Doors\", \"default\": [], \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/Door\" } }, \"wall_graph\": { \"title\": \"Wall Graph\", \"default\": { \"name\": \"\", \"vertices\": [], \"edges\": [], \"params\": [] }, \"allOf\": [ { \"$ref\": \"#/definitions/Graph\" } ] }, \"ref_x\": { \"title\": \"Ref X\", \"default\": 0, \"type\": \"number\" }, \"ref_y\": { \"title\": \"Ref Y\", \"default\": 0, \"type\": \"number\" }, \"ref_yaw\": { \"title\": \"Ref Yaw\", \"default\": 0, \"type\": \"number\" }, \"width\": { \"title\": \"Width\", \"default\": 0, \"type\": \"number\" }, \"depth\": { \"title\": \"Depth\", \"default\": 0, \"type\": \"number\" } }, \"required\": [ \"name\", \"levels\", \"doors\", \"wall_graph\", \"ref_x\", \"ref_y\", \"ref_yaw\", \"width\", \"depth\" ] } } } ``` ### /building_map/fire_alarm_trigger ``` { \"title\": \"FireAlarmTriggerState\", \"type\": \"object\", \"properties\": { \"unix_millis_time\": { \"title\": \"Unix Millis Time\", \"type\": \"integer\" }, \"trigger\": { \"title\": \"Trigger\", \"type\": \"boolean\" } }, \"required\": [ \"unix_millis_time\", \"trigger\" ] } ``` ### /delivery_alerts ``` { \"title\": \"DeliveryAlert\", \"type\": \"object\", \"properties\": { \"id\": { \"title\": \"Id\", \"type\": \"string\" }, \"category\": { \"$ref\": \"#/definitions/Category\" }, \"tier\": { \"$ref\": \"#/definitions/Tier\" }, \"action\": { \"$ref\": \"#/definitions/Action\" }, \"task_id\": { \"title\": \"Task Id\", \"type\": \"string\" }, \"message\": { \"title\": \"Message\", \"type\": \"string\" } }, \"required\": [ \"id\", \"category\", \"tier\", \"action\", \"task_id\", \"message\" ], \"definitions\": { \"Category\": { \"title\": \"Category\", \"description\": \"An enumeration.\", \"enum\": [ \"missing\", \"wrong\", \"obstructed\", \"cancelled\" ], \"type\": \"string\" }, \"Tier\": { \"title\": \"Tier\", \"description\": \"An enumeration.\", \"enum\": [ \"warning\", \"error\" ], \"type\": \"string\" }, \"Action\": { \"title\": \"Action\", \"description\": \"An enumeration.\", \"enum\": [ \"waiting\", \"cancelled\", \"override\", \"resume\" ], \"type\": \"string\" } } } ``` ### /doors/{door_name}/state ``` { \"title\": \"DoorState\", \"type\": \"object\", \"properties\": { \"door_time\": { \"title\": \"Door Time\", \"default\": { \"sec\": 0, \"nanosec\": 0 }, \"allOf\": [ { \"$ref\": \"#/definitions/Time\" } ] }, \"door_name\": { \"title\": \"Door Name\", \"default\": \"\", \"type\": \"string\" }, \"current_mode\": { \"title\": \"Current Mode\", \"default\": { \"value\": 0 }, \"allOf\": [ { \"$ref\": \"#/definitions/DoorMode\" } ] } }, \"required\": [ \"door_time\", \"door_name\", \"current_mode\" ], \"definitions\": { \"Time\": { \"title\": \"Time\", \"type\": \"object\", \"properties\": { \"sec\": { \"title\": \"Sec\", \"default\": 0, \"minimum\": -2147483648, \"maximum\": 2147483647, \"type\": \"integer\" }, \"nanosec\": { \"title\": \"Nanosec\", \"default\": 0, \"minimum\": 0, \"maximum\": 4294967295, \"type\": \"integer\" } }, \"required\": [ \"sec\", \"nanosec\" ] }, \"DoorMode\": { \"title\": \"DoorMode\", \"type\": \"object\", \"properties\": { \"value\": { \"title\": \"Value\", \"default\": 0, \"minimum\": 0, \"maximum\": 4294967295, \"type\": \"integer\" } }, \"required\": [ \"value\" ] } } } ``` ### /doors/{door_name}/health ``` { \"title\": \"DoorHealth\", \"type\": \"object\", \"properties\": { \"health_status\": { \"title\": \"Health Status\", \"maxLength\": 255, \"nullable\": true, \"type\": \"string\" }, \"health_message\": { \"title\": \"Health Message\", \"nullable\": true, \"type\": \"string\" }, \"id_\": { \"title\": \"Id \", \"maxLength\": 255, \"type\": \"string\" } }, \"required\": [ \"health_status\", \"id_\" ], \"additionalProperties\": false } ``` ### /lifts/{lift_name}/state ``` { \"title\": \"LiftState\", \"type\": \"object\", \"properties\": { \"lift_time\": { \"title\": \"Lift Time\", \"default\": { \"sec\": 0, \"nanosec\": 0 }, \"allOf\": [ { \"$ref\": \"#/definitions/Time\" } ] }, \"lift_name\": { \"title\": \"Lift Name\", \"default\": \"\", \"type\": \"string\" }, \"available_floors\": { \"title\": \"Available Floors\", \"default\": [], \"type\": \"array\", \"items\": { \"type\": \"string\" } }, \"current_floor\": { \"title\": \"Current Floor\", \"default\": \"\", \"type\": \"string\" }, \"destination_floor\": { \"title\": \"Destination Floor\", \"default\": \"\", \"type\": \"string\" }, \"door_state\": { \"title\": \"Door State\", \"default\": 0, \"minimum\": 0, \"maximum\": 255, \"type\": \"integer\" }, \"motion_state\": { \"title\": \"Motion State\", \"default\": 0, \"minimum\": 0, \"maximum\": 255, \"type\": \"integer\" }, \"available_modes\": { \"title\": \"Available Modes\", \"type\": \"array\", \"items\": { \"type\": \"integer\" } }, \"current_mode\": { \"title\": \"Current Mode\", \"default\": 0, \"minimum\": 0, \"maximum\": 255, \"type\": \"integer\" }, \"session_id\": { \"title\": \"Session Id\", \"default\": \"\", \"type\": \"string\" } }, \"required\": [ \"lift_time\", \"lift_name\", \"available_floors\", \"current_floor\", \"destination_floor\", \"door_state\", \"motion_state\", \"available_modes\", \"current_mode\", \"session_id\" ], \"definitions\": { \"Time\": { \"title\": \"Time\", \"type\": \"object\", \"properties\": { \"sec\": { \"title\": \"Sec\", \"default\": 0, \"minimum\": -2147483648, \"maximum\": 2147483647, \"type\": \"integer\" }, \"nanosec\": { \"title\": \"Nanosec\", \"default\": 0, \"minimum\": 0, \"maximum\": 4294967295, \"type\": \"integer\" } }, \"required\": [ \"sec\", \"nanosec\" ] } } } ``` ### /lifts/{lift_name}/health ``` { \"title\": \"LiftHealth\", \"type\": \"object\", \"properties\": { \"health_status\": { \"title\": \"Health Status\", \"maxLength\": 255, \"nullable\": true, \"type\": \"string\" }, \"health_message\": { \"title\": \"Health Message\", \"nullable\": true, \"type\": \"string\" }, \"id_\": { \"title\": \"Id \", \"maxLength\": 255, \"type\": \"string\" } }, \"required\": [ \"health_status\", \"id_\" ], \"additionalProperties\": false } ``` ### /tasks/{task_id}/state ``` { \"title\": \"TaskState\", \"type\": \"object\", \"properties\": { \"booking\": { \"$ref\": \"#/definitions/Booking\" }, \"category\": { \"$ref\": \"#/definitions/Category\" }, \"detail\": { \"$ref\": \"#/definitions/Detail\" }, \"unix_millis_start_time\": { \"title\": \"Unix Millis Start Time\", \"type\": \"integer\" }, \"unix_millis_finish_time\": { \"title\": \"Unix Millis Finish Time\", \"type\": \"integer\" }, \"original_estimate_millis\": { \"$ref\": \"#/definitions/EstimateMillis\" }, \"estimate_millis\": { \"$ref\": \"#/definitions/EstimateMillis\" }, \"assigned_to\": { \"title\": \"Assigned To\", \"description\": \"Which agent (robot) is the task assigned to\", \"allOf\": [ { \"$ref\": \"#/definitions/AssignedTo\" } ] }, \"status\": { \"$ref\": \"#/definitions/Status\" }, \"dispatch\": { \"$ref\": \"#/definitions/Dispatch\" }, \"phases\": { \"title\": \"Phases\", \"description\": \"A dictionary of the states of the phases of the task. The keys (property names) are phase IDs, which are integers.\", \"type\": \"object\", \"additionalProperties\": { \"$ref\": \"#/definitions/Phase\" } }, \"completed\": { \"title\": \"Completed\", \"description\": \"An array of the IDs of completed phases of this task\", \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/Id\" } }, \"active\": { \"title\": \"Active\", \"description\": \"The ID of the active phase for this task\", \"allOf\": [ { \"$ref\": \"#/definitions/Id\" } ] }, \"pending\": { \"title\": \"Pending\", \"description\": \"An array of the pending phases of this task\", \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/Id\" } }, \"interruptions\": { \"title\": \"Interruptions\", \"description\": \"A dictionary of interruptions that have been applied to this task. The keys (property names) are the unique token of the interruption request.\", \"type\": \"object\", \"additionalProperties\": { \"$ref\": \"#/definitions/Interruption\" } }, \"cancellation\": { \"title\": \"Cancellation\", \"description\": \"If the task was cancelled, this will describe information about the request.\", \"allOf\": [ { \"$ref\": \"#/definitions/Cancellation\" } ] }, \"killed\": { \"title\": \"Killed\", \"description\": \"If the task was killed, this will describe information about the request.\", \"allOf\": [ { \"$ref\": \"#/definitions/Killed\" } ] } }, \"required\": [ \"booking\" ], \"definitions\": { \"Booking\": { \"title\": \"Booking\", \"type\": \"object\", \"properties\": { \"id\": { \"title\": \"Id\", \"description\": \"The unique identifier for this task\", \"type\": \"string\" }, \"unix_millis_earliest_start_time\": { \"title\": \"Unix Millis Earliest Start Time\", \"type\": \"integer\" }, \"unix_millis_request_time\": { \"title\": \"Unix Millis Request Time\", \"type\": \"integer\" }, \"priority\": { \"title\": \"Priority\", \"description\": \"Priority information about this task\", \"anyOf\": [ { \"type\": \"object\" }, { \"type\": \"string\" } ] }, \"labels\": { \"title\": \"Labels\", \"description\": \"Information about how and why this task was booked\", \"type\": \"array\", \"items\": { \"type\": \"string\" } }, \"requester\": { \"title\": \"Requester\", \"description\": \"(Optional) An identifier for the entity that requested this task\", \"type\": \"string\" } }, \"required\": [ \"id\" ] }, \"Category\": { \"title\": \"Category\", \"description\": \"The category of this task or phase\", \"type\": \"string\" }, \"Detail\": { \"title\": \"Detail\", \"description\": \"Detailed information about a task, phase, or event\", \"anyOf\": [ { \"type\": \"object\" }, { \"type\": \"array\", \"items\": {} }, { \"type\": \"string\" } ] }, \"EstimateMillis\": { \"title\": \"EstimateMillis\", \"description\": \"An estimate, in milliseconds, of how long the subject will take to complete\", \"minimum\": 0, \"type\": \"integer\" }, \"AssignedTo\": { \"title\": \"AssignedTo\", \"type\": \"object\", \"properties\": { \"group\": { \"title\": \"Group\", \"type\": \"string\" }, \"name\": { \"title\": \"Name\", \"type\": \"string\" } }, \"required\": [ \"group\", \"name\" ] }, \"Status\": { \"title\": \"Status\", \"description\": \"An enumeration.\", \"enum\": [ \"uninitialized\", \"blocked\", \"error\", \"failed\", \"queued\", \"standby\", \"underway\", \"delayed\", \"skipped\", \"canceled\", \"killed\", \"completed\" ] }, \"Status1\": { \"title\": \"Status1\", \"description\": \"An enumeration.\", \"enum\": [ \"queued\", \"selected\", \"dispatched\", \"failed_to_assign\", \"canceled_in_flight\" ] }, \"Assignment\": { \"title\": \"Assignment\", \"type\": \"object\", \"properties\": { \"fleet_name\": { \"title\": \"Fleet Name\", \"type\": \"string\" }, \"expected_robot_name\": { \"title\": \"Expected Robot Name\", \"type\": \"string\" } } }, \"Error\": { \"title\": \"Error\", \"type\": \"object\", \"properties\": { \"code\": { \"title\": \"Code\", \"description\": \"A standard code for the kind of error that has occurred\", \"minimum\": 0, \"type\": \"integer\" }, \"category\": { \"title\": \"Category\", \"description\": \"The category of the error\", \"type\": \"string\" }, \"detail\": { \"title\": \"Detail\", \"description\": \"Details about the error\", \"type\": \"string\" } } }, \"Dispatch\": { \"title\": \"Dispatch\", \"type\": \"object\", \"properties\": { \"status\": { \"$ref\": \"#/definitions/Status1\" }, \"assignment\": { \"$ref\": \"#/definitions/Assignment\" }, \"errors\": { \"title\": \"Errors\", \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/Error\" } } }, \"required\": [ \"status\" ] }, \"Id\": { \"title\": \"Id\", \"minimum\": 0, \"type\": \"integer\" }, \"EventState\": { \"title\": \"EventState\", \"type\": \"object\", \"properties\": { \"id\": { \"$ref\": \"#/definitions/Id\" }, \"status\": { \"$ref\": \"#/definitions/Status\" }, \"name\": { \"title\": \"Name\", \"description\": \"The brief name of the event\", \"type\": \"string\" }, \"detail\": { \"title\": \"Detail\", \"description\": \"Detailed information about the event\", \"allOf\": [ { \"$ref\": \"#/definitions/Detail\" } ] }, \"deps\": { \"title\": \"Deps\", \"description\": \"This event may depend on other events. This array contains the IDs of those other event dependencies.\", \"type\": \"array\", \"items\": { \"type\": \"integer\", \"minimum\": 0 } } }, \"required\": [ \"id\" ] }, \"Undo\": { \"title\": \"Undo\", \"type\": \"object\", \"properties\": { \"unix_millis_request_time\": { \"title\": \"Unix Millis Request Time\", \"description\": \"The time that the undo skip request arrived\", \"type\": \"integer\" }, \"labels\": { \"title\": \"Labels\", \"description\": \"Labels to describe the undo skip request\", \"type\": \"array\", \"items\": { \"type\": \"string\" } } }, \"required\": [ \"unix_millis_request_time\", \"labels\" ] }, \"SkipPhaseRequest\": { \"title\": \"SkipPhaseRequest\", \"type\": \"object\", \"properties\": { \"unix_millis_request_time\": { \"title\": \"Unix Millis Request Time\", \"description\": \"The time that the skip request arrived\", \"type\": \"integer\" }, \"labels\": { \"title\": \"Labels\", \"description\": \"Labels to describe the purpose of the skip request\", \"type\": \"array\", \"items\": { \"type\": \"string\" } }, \"undo\": { \"title\": \"Undo\", \"description\": \"Information about an undo skip request that applied to this request\", \"allOf\": [ { \"$ref\": \"#/definitions/Undo\" } ] } }, \"required\": [ \"unix_millis_request_time\", \"labels\" ] }, \"Phase\": { \"title\": \"Phase\", \"type\": \"object\", \"properties\": { \"id\": { \"$ref\": \"#/definitions/Id\" }, \"category\": { \"$ref\": \"#/definitions/Category\" }, \"detail\": { \"$ref\": \"#/definitions/Detail\" }, \"unix_millis_start_time\": { \"title\": \"Unix Millis Start Time\", \"type\": \"integer\" }, \"unix_millis_finish_time\": { \"title\": \"Unix Millis Finish Time\", \"type\": \"integer\" }, \"original_estimate_millis\": { \"$ref\": \"#/definitions/EstimateMillis\" }, \"estimate_millis\": { \"$ref\": \"#/definitions/EstimateMillis\" }, \"final_event_id\": { \"$ref\": \"#/definitions/Id\" }, \"events\": { \"title\": \"Events\", \"description\": \"A dictionary of events for this phase. The keys (property names) are the event IDs, which are integers.\", \"type\": \"object\", \"additionalProperties\": { \"$ref\": \"#/definitions/EventState\" } }, \"skip_requests\": { \"title\": \"Skip Requests\", \"description\": \"Information about any skip requests that have been received\", \"type\": \"object\", \"additionalProperties\": { \"$ref\": \"#/definitions/SkipPhaseRequest\" } } }, \"required\": [ \"id\" ] }, \"ResumedBy\": { \"title\": \"ResumedBy\", \"type\": \"object\", \"properties\": { \"unix_millis_request_time\": { \"title\": \"Unix Millis Request Time\", \"description\": \"The time that the resume request arrived\", \"type\": \"integer\" }, \"labels\": { \"title\": \"Labels\", \"description\": \"Labels to describe the resume request\", \"type\": \"array\", \"items\": { \"type\": \"string\" } } }, \"required\": [ \"labels\" ] }, \"Interruption\": { \"title\": \"Interruption\", \"type\": \"object\", \"properties\": { \"unix_millis_request_time\": { \"title\": \"Unix Millis Request Time\", \"description\": \"The time that the interruption request arrived\", \"type\": \"integer\" }, \"labels\": { \"title\": \"Labels\", \"description\": \"Labels to describe the purpose of the interruption\", \"type\": \"array\", \"items\": { \"type\": \"string\" } }, \"resumed_by\": { \"title\": \"Resumed By\", \"description\": \"Information about the resume request that ended this interruption. This field will be missing if the interruption is still active.\", \"allOf\": [ { \"$ref\": \"#/definitions/ResumedBy\" } ] } }, \"required\": [ \"unix_millis_request_time\", \"labels\" ] }, \"Cancellation\": { \"title\": \"Cancellation\", \"type\": \"object\", \"properties\": { \"unix_millis_request_time\": { \"title\": \"Unix Millis Request Time\", \"description\": \"The time that the cancellation request arrived\", \"type\": \"integer\" }, \"labels\": { \"title\": \"Labels\", \"description\": \"Labels to describe the cancel request\", \"type\": \"array\", \"items\": { \"type\": \"string\" } } }, \"required\": [ \"unix_millis_request_time\", \"labels\" ] }, \"Killed\": { \"title\": \"Killed\", \"type\": \"object\", \"properties\": { \"unix_millis_request_time\": { \"title\": \"Unix Millis Request Time\", \"description\": \"The time that the cancellation request arrived\", \"type\": \"integer\" }, \"labels\": { \"title\": \"Labels\", \"description\": \"Labels to describe the kill request\", \"type\": \"array\", \"items\": { \"type\": \"string\" } } }, \"required\": [ \"unix_millis_request_time\", \"labels\" ] } } } ``` ### /tasks/{task_id}/log ``` { \"title\": \"TaskEventLog\", \"type\": \"object\", \"properties\": { \"task_id\": { \"title\": \"Task Id\", \"type\": \"string\" }, \"log\": { \"title\": \"Log\", \"description\": \"Log entries related to the overall task\", \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/LogEntry\" } }, \"phases\": { \"title\": \"Phases\", \"description\": \"A dictionary whose keys (property names) are the indices of a phase\", \"type\": \"object\", \"additionalProperties\": { \"$ref\": \"#/definitions/Phases\" } } }, \"required\": [ \"task_id\" ], \"additionalProperties\": false, \"definitions\": { \"Tier\": { \"title\": \"Tier\", \"description\": \"An enumeration.\", \"enum\": [ \"uninitialized\", \"info\", \"warning\", \"error\" ] }, \"LogEntry\": { \"title\": \"LogEntry\", \"type\": \"object\", \"properties\": { \"seq\": { \"title\": \"Seq\", \"description\": \"Sequence number for this entry. Each entry has a unique sequence number which monotonically increase, until integer overflow causes a wrap around.\", \"exclusiveMaximum\": 4294967296, \"minimum\": 0, \"type\": \"integer\" }, \"tier\": { \"description\": \"The importance level of the log entry\", \"allOf\": [ { \"$ref\": \"#/definitions/Tier\" } ] }, \"unix_millis_time\": { \"title\": \"Unix Millis Time\", \"type\": \"integer\" }, \"text\": { \"title\": \"Text\", \"description\": \"The text of the log entry\", \"type\": \"string\" } }, \"required\": [ \"seq\", \"tier\", \"unix_millis_time\", \"text\" ] }, \"Phases\": { \"title\": \"Phases\", \"type\": \"object\", \"properties\": { \"log\": { \"title\": \"Log\", \"description\": \"Log entries related to the overall phase\", \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/LogEntry\" } }, \"events\": { \"title\": \"Events\", \"description\": \"A dictionary whose keys (property names) are the indices of an event in the phase\", \"type\": \"object\", \"additionalProperties\": { \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/LogEntry\" } } } }, \"additionalProperties\": false } } } ``` ### /dispensers/{guid}/state ``` { \"title\": \"DispenserState\", \"type\": \"object\", \"properties\": { \"time\": { \"title\": \"Time\", \"default\": { \"sec\": 0, \"nanosec\": 0 }, \"allOf\": [ { \"$ref\": \"#/definitions/Time\" } ] }, \"guid\": { \"title\": \"Guid\", \"default\": \"\", \"type\": \"string\" }, \"mode\": { \"title\": \"Mode\", \"default\": 0, \"minimum\": -2147483648, \"maximum\": 2147483647, \"type\": \"integer\" }, \"request_guid_queue\": { \"title\": \"Request Guid Queue\", \"default\": [], \"type\": \"array\", \"items\": { \"type\": \"string\" } }, \"seconds_remaining\": { \"title\": \"Seconds Remaining\", \"default\": 0, \"type\": \"number\" } }, \"required\": [ \"time\", \"guid\", \"mode\", \"request_guid_queue\", \"seconds_remaining\" ], \"definitions\": { \"Time\": { \"title\": \"Time\", \"type\": \"object\", \"properties\": { \"sec\": { \"title\": \"Sec\", \"default\": 0, \"minimum\": -2147483648, \"maximum\": 2147483647, \"type\": \"integer\" }, \"nanosec\": { \"title\": \"Nanosec\", \"default\": 0, \"minimum\": 0, \"maximum\": 4294967295, \"type\": \"integer\" } }, \"required\": [ \"sec\", \"nanosec\" ] } } } ``` ### /dispensers/{guid}/health ``` { \"title\": \"DispenserHealth\", \"type\": \"object\", \"properties\": { \"health_status\": { \"title\": \"Health Status\", \"maxLength\": 255, \"nullable\": true, \"type\": \"string\" }, \"health_message\": { \"title\": \"Health Message\", \"nullable\": true, \"type\": \"string\" }, \"id_\": { \"title\": \"Id \", \"maxLength\": 255, \"type\": \"string\" } }, \"required\": [ \"health_status\", \"id_\" ], \"additionalProperties\": false } ``` ### /ingestors/{guid}/state ``` { \"title\": \"IngestorState\", \"type\": \"object\", \"properties\": { \"time\": { \"title\": \"Time\", \"default\": { \"sec\": 0, \"nanosec\": 0 }, \"allOf\": [ { \"$ref\": \"#/definitions/Time\" } ] }, \"guid\": { \"title\": \"Guid\", \"default\": \"\", \"type\": \"string\" }, \"mode\": { \"title\": \"Mode\", \"default\": 0, \"minimum\": -2147483648, \"maximum\": 2147483647, \"type\": \"integer\" }, \"request_guid_queue\": { \"title\": \"Request Guid Queue\", \"default\": [], \"type\": \"array\", \"items\": { \"type\": \"string\" } }, \"seconds_remaining\": { \"title\": \"Seconds Remaining\", \"default\": 0, \"type\": \"number\" } }, \"required\": [ \"time\", \"guid\", \"mode\", \"request_guid_queue\", \"seconds_remaining\" ], \"definitions\": { \"Time\": { \"title\": \"Time\", \"type\": \"object\", \"properties\": { \"sec\": { \"title\": \"Sec\", \"default\": 0, \"minimum\": -2147483648, \"maximum\": 2147483647, \"type\": \"integer\" }, \"nanosec\": { \"title\": \"Nanosec\", \"default\": 0, \"minimum\": 0, \"maximum\": 4294967295, \"type\": \"integer\" } }, \"required\": [ \"sec\", \"nanosec\" ] } } } ``` ### /ingestors/{guid}/health ``` { \"title\": \"IngestorHealth\", \"type\": \"object\", \"properties\": { \"health_status\": { \"title\": \"Health Status\", \"maxLength\": 255, \"nullable\": true, \"type\": \"string\" }, \"health_message\": { \"title\": \"Health Message\", \"nullable\": true, \"type\": \"string\" }, \"id_\": { \"title\": \"Id \", \"maxLength\": 255, \"type\": \"string\" } }, \"required\": [ \"health_status\", \"id_\" ], \"additionalProperties\": false } ``` ### /fleets/{name}/state ``` { \"title\": \"FleetState\", \"type\": \"object\", \"properties\": { \"name\": { \"title\": \"Name\", \"type\": \"string\" }, \"robots\": { \"title\": \"Robots\", \"description\": \"A dictionary of the states of the robots that belong to this fleet\", \"type\": \"object\", \"additionalProperties\": { \"$ref\": \"#/definitions/RobotState\" } } }, \"definitions\": { \"Status\": { \"title\": \"Status\", \"description\": \"An enumeration.\", \"enum\": [ \"uninitialized\", \"offline\", \"shutdown\", \"idle\", \"charging\", \"working\", \"error\" ] }, \"Location2D\": { \"title\": \"Location2D\", \"type\": \"object\", \"properties\": { \"map\": { \"title\": \"Map\", \"type\": \"string\" }, \"x\": { \"title\": \"X\", \"type\": \"number\" }, \"y\": { \"title\": \"Y\", \"type\": \"number\" }, \"yaw\": { \"title\": \"Yaw\", \"type\": \"number\" } }, \"required\": [ \"map\", \"x\", \"y\", \"yaw\" ] }, \"Issue\": { \"title\": \"Issue\", \"type\": \"object\", \"properties\": { \"category\": { \"title\": \"Category\", \"description\": \"Category of the robot\'s issue\", \"type\": \"string\" }, \"detail\": { \"title\": \"Detail\", \"description\": \"Detailed information about the issue\", \"anyOf\": [ { \"type\": \"object\" }, { \"type\": \"array\", \"items\": {} }, { \"type\": \"string\" } ] } } }, \"Commission\": { \"title\": \"Commission\", \"type\": \"object\", \"properties\": { \"dispatch_tasks\": { \"title\": \"Dispatch Tasks\", \"description\": \"Should the robot accept dispatched tasks, true/false. When used in a request, leave this unset to not change the robot\'s current value.\", \"type\": \"boolean\" }, \"direct_tasks\": { \"title\": \"Direct Tasks\", \"description\": \"Should the robot accept direct task requests, true/false. When used in a request, leave this unset to not change the robot\'s current value.\", \"type\": \"boolean\" }, \"idle_behavior\": { \"title\": \"Idle Behavior\", \"description\": \"Should the robot perform its idle behavior, true/false. When used in a request, leave this unset to not change the robot\'s current value.\", \"type\": \"boolean\" } } }, \"MutexGroups\": { \"title\": \"MutexGroups\", \"type\": \"object\", \"properties\": { \"locked\": { \"title\": \"Locked\", \"description\": \"A list of mutex groups that this robot has currently locked\", \"type\": \"array\", \"items\": { \"type\": \"string\" } }, \"requesting\": { \"title\": \"Requesting\", \"description\": \"A list of the mutex groups that this robot is currently requesting but has not lockd yet\", \"type\": \"array\", \"items\": { \"type\": \"string\" } } } }, \"RobotState\": { \"title\": \"RobotState\", \"type\": \"object\", \"properties\": { \"name\": { \"title\": \"Name\", \"type\": \"string\" }, \"status\": { \"description\": \"A simple token representing the status of the robot\", \"allOf\": [ { \"$ref\": \"#/definitions/Status\" } ] }, \"task_id\": { \"title\": \"Task Id\", \"description\": \"The ID of the task this robot is currently working on. Empty string if the robot is not working on a task.\", \"type\": \"string\" }, \"unix_millis_time\": { \"title\": \"Unix Millis Time\", \"type\": \"integer\" }, \"location\": { \"$ref\": \"#/definitions/Location2D\" }, \"battery\": { \"title\": \"Battery\", \"description\": \"State of charge of the battery. Values range from 0.0 (depleted) to 1.0 (fully charged)\", \"minimum\": 0.0, \"maximum\": 1.0, \"type\": \"number\" }, \"issues\": { \"title\": \"Issues\", \"description\": \"A list of issues with the robot that operators need to address\", \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/Issue\" } }, \"commission\": { \"$ref\": \"#/definitions/Commission\" }, \"mutex_groups\": { \"title\": \"Mutex Groups\", \"description\": \"Information about the mutex groups that this robot is interacting with\", \"allOf\": [ { \"$ref\": \"#/definitions/MutexGroups\" } ] } } } } } ``` ### /fleets/{name}/log ``` { \"title\": \"FleetLog\", \"type\": \"object\", \"properties\": { \"name\": { \"title\": \"Name\", \"type\": \"string\" }, \"log\": { \"title\": \"Log\", \"description\": \"Log for the overall fleet\", \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/LogEntry\" } }, \"robots\": { \"title\": \"Robots\", \"description\": \"Dictionary of logs for the individual robots. The keys (property names) are the robot names.\", \"type\": \"object\", \"additionalProperties\": { \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/LogEntry\" } } } }, \"definitions\": { \"Tier\": { \"title\": \"Tier\", \"description\": \"An enumeration.\", \"enum\": [ \"uninitialized\", \"info\", \"warning\", \"error\" ] }, \"LogEntry\": { \"title\": \"LogEntry\", \"type\": \"object\", \"properties\": { \"seq\": { \"title\": \"Seq\", \"description\": \"Sequence number for this entry. Each entry has a unique sequence number which monotonically increase, until integer overflow causes a wrap around.\", \"exclusiveMaximum\": 4294967296, \"minimum\": 0, \"type\": \"integer\" }, \"tier\": { \"description\": \"The importance level of the log entry\", \"allOf\": [ { \"$ref\": \"#/definitions/Tier\" } ] }, \"unix_millis_time\": { \"title\": \"Unix Millis Time\", \"type\": \"integer\" }, \"text\": { \"title\": \"Text\", \"description\": \"The text of the log entry\", \"type\": \"string\" } }, \"required\": [ \"seq\", \"tier\", \"unix_millis_time\", \"text\" ] } } } ``` * @summary Socket.io endpoint * @param {*} [options] Override http request option. * @throws {RequiredError} @@ -5995,7 +5983,7 @@ export const DefaultApiFp = function (configuration?: Configuration) { return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, /** - * # NOTE: This endpoint is here for documentation purposes only, this is _not_ a REST endpoint. ## About This exposes a minimal pubsub system built on top of socket.io. It works similar to a normal socket.io endpoint, except that are 2 special rooms which control subscriptions. ## Rooms ### subscribe Clients must send a message to this room to start receiving messages on other rooms. The message must be of the form: ``` { \"room\": \"\" } ``` ### unsubscribe Clients can send a message to this room to stop receiving messages on other rooms. The message must be of the form: ``` { \"room\": \"\" } ``` ### /alerts ``` { \"title\": \"Alert\", \"description\": \"General alert that can be triggered by events.\", \"type\": \"object\", \"properties\": { \"id\": { \"title\": \"Id\", \"maxLength\": 255, \"type\": \"string\" }, \"original_id\": { \"title\": \"Original Id\", \"maxLength\": 255, \"type\": \"string\" }, \"category\": { \"title\": \"Category\", \"description\": \"Default: default
Task: task
Fleet: fleet
Robot: robot\", \"maxLength\": 7, \"type\": \"string\" }, \"unix_millis_created_time\": { \"title\": \"Unix Millis Created Time\", \"minimum\": -9223372036854775808, \"maximum\": 9223372036854775807, \"type\": \"integer\" }, \"acknowledged_by\": { \"title\": \"Acknowledged By\", \"maxLength\": 255, \"nullable\": true, \"type\": \"string\" }, \"unix_millis_acknowledged_time\": { \"title\": \"Unix Millis Acknowledged Time\", \"minimum\": -9223372036854775808, \"maximum\": 9223372036854775807, \"nullable\": true, \"type\": \"integer\" } }, \"required\": [ \"id\", \"original_id\", \"category\", \"unix_millis_created_time\" ], \"additionalProperties\": false } ``` ### /beacons ``` { \"title\": \"BeaconState\", \"type\": \"object\", \"properties\": { \"id\": { \"title\": \"Id\", \"maxLength\": 255, \"type\": \"string\" }, \"online\": { \"title\": \"Online\", \"type\": \"boolean\" }, \"category\": { \"title\": \"Category\", \"maxLength\": 255, \"nullable\": true, \"type\": \"string\" }, \"activated\": { \"title\": \"Activated\", \"type\": \"boolean\" }, \"level\": { \"title\": \"Level\", \"maxLength\": 255, \"nullable\": true, \"type\": \"string\" } }, \"required\": [ \"id\", \"online\", \"activated\" ], \"additionalProperties\": false } ``` ### /building_map ``` { \"title\": \"BuildingMap\", \"type\": \"object\", \"properties\": { \"name\": { \"title\": \"Name\", \"default\": \"\", \"type\": \"string\" }, \"levels\": { \"title\": \"Levels\", \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/Level\" } }, \"lifts\": { \"title\": \"Lifts\", \"default\": [], \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/Lift\" } } }, \"required\": [ \"name\", \"levels\", \"lifts\" ], \"definitions\": { \"AffineImage\": { \"title\": \"AffineImage\", \"type\": \"object\", \"properties\": { \"name\": { \"title\": \"Name\", \"default\": \"\", \"type\": \"string\" }, \"x_offset\": { \"title\": \"X Offset\", \"default\": 0, \"type\": \"number\" }, \"y_offset\": { \"title\": \"Y Offset\", \"default\": 0, \"type\": \"number\" }, \"yaw\": { \"title\": \"Yaw\", \"default\": 0, \"type\": \"number\" }, \"scale\": { \"title\": \"Scale\", \"default\": 0, \"type\": \"number\" }, \"encoding\": { \"title\": \"Encoding\", \"default\": \"\", \"type\": \"string\" }, \"data\": { \"title\": \"Data\", \"type\": \"string\" } }, \"required\": [ \"name\", \"x_offset\", \"y_offset\", \"yaw\", \"scale\", \"encoding\", \"data\" ] }, \"Place\": { \"title\": \"Place\", \"type\": \"object\", \"properties\": { \"name\": { \"title\": \"Name\", \"default\": \"\", \"type\": \"string\" }, \"x\": { \"title\": \"X\", \"default\": 0, \"type\": \"number\" }, \"y\": { \"title\": \"Y\", \"default\": 0, \"type\": \"number\" }, \"yaw\": { \"title\": \"Yaw\", \"default\": 0, \"type\": \"number\" }, \"position_tolerance\": { \"title\": \"Position Tolerance\", \"default\": 0, \"type\": \"number\" }, \"yaw_tolerance\": { \"title\": \"Yaw Tolerance\", \"default\": 0, \"type\": \"number\" } }, \"required\": [ \"name\", \"x\", \"y\", \"yaw\", \"position_tolerance\", \"yaw_tolerance\" ] }, \"Door\": { \"title\": \"Door\", \"type\": \"object\", \"properties\": { \"name\": { \"title\": \"Name\", \"default\": \"\", \"type\": \"string\" }, \"v1_x\": { \"title\": \"V1 X\", \"default\": 0, \"type\": \"number\" }, \"v1_y\": { \"title\": \"V1 Y\", \"default\": 0, \"type\": \"number\" }, \"v2_x\": { \"title\": \"V2 X\", \"default\": 0, \"type\": \"number\" }, \"v2_y\": { \"title\": \"V2 Y\", \"default\": 0, \"type\": \"number\" }, \"door_type\": { \"title\": \"Door Type\", \"default\": 0, \"minimum\": 0, \"maximum\": 255, \"type\": \"integer\" }, \"motion_range\": { \"title\": \"Motion Range\", \"default\": 0, \"type\": \"number\" }, \"motion_direction\": { \"title\": \"Motion Direction\", \"default\": 0, \"minimum\": -2147483648, \"maximum\": 2147483647, \"type\": \"integer\" } }, \"required\": [ \"name\", \"v1_x\", \"v1_y\", \"v2_x\", \"v2_y\", \"door_type\", \"motion_range\", \"motion_direction\" ] }, \"Param\": { \"title\": \"Param\", \"type\": \"object\", \"properties\": { \"name\": { \"title\": \"Name\", \"default\": \"\", \"type\": \"string\" }, \"type\": { \"title\": \"Type\", \"default\": 0, \"minimum\": 0, \"maximum\": 4294967295, \"type\": \"integer\" }, \"value_int\": { \"title\": \"Value Int\", \"default\": 0, \"minimum\": -2147483648, \"maximum\": 2147483647, \"type\": \"integer\" }, \"value_float\": { \"title\": \"Value Float\", \"default\": 0, \"type\": \"number\" }, \"value_string\": { \"title\": \"Value String\", \"default\": \"\", \"type\": \"string\" }, \"value_bool\": { \"title\": \"Value Bool\", \"default\": false, \"type\": \"boolean\" } }, \"required\": [ \"name\", \"type\", \"value_int\", \"value_float\", \"value_string\", \"value_bool\" ] }, \"GraphNode\": { \"title\": \"GraphNode\", \"type\": \"object\", \"properties\": { \"x\": { \"title\": \"X\", \"default\": 0, \"type\": \"number\" }, \"y\": { \"title\": \"Y\", \"default\": 0, \"type\": \"number\" }, \"name\": { \"title\": \"Name\", \"default\": \"\", \"type\": \"string\" }, \"params\": { \"title\": \"Params\", \"default\": [], \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/Param\" } } }, \"required\": [ \"x\", \"y\", \"name\", \"params\" ] }, \"GraphEdge\": { \"title\": \"GraphEdge\", \"type\": \"object\", \"properties\": { \"v1_idx\": { \"title\": \"V1 Idx\", \"default\": 0, \"minimum\": 0, \"maximum\": 4294967295, \"type\": \"integer\" }, \"v2_idx\": { \"title\": \"V2 Idx\", \"default\": 0, \"minimum\": 0, \"maximum\": 4294967295, \"type\": \"integer\" }, \"params\": { \"title\": \"Params\", \"default\": [], \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/Param\" } }, \"edge_type\": { \"title\": \"Edge Type\", \"default\": 0, \"minimum\": 0, \"maximum\": 255, \"type\": \"integer\" } }, \"required\": [ \"v1_idx\", \"v2_idx\", \"params\", \"edge_type\" ] }, \"Graph\": { \"title\": \"Graph\", \"type\": \"object\", \"properties\": { \"name\": { \"title\": \"Name\", \"default\": \"\", \"type\": \"string\" }, \"vertices\": { \"title\": \"Vertices\", \"default\": [], \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/GraphNode\" } }, \"edges\": { \"title\": \"Edges\", \"default\": [], \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/GraphEdge\" } }, \"params\": { \"title\": \"Params\", \"default\": [], \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/Param\" } } }, \"required\": [ \"name\", \"vertices\", \"edges\", \"params\" ] }, \"Level\": { \"title\": \"Level\", \"type\": \"object\", \"properties\": { \"name\": { \"title\": \"Name\", \"default\": \"\", \"type\": \"string\" }, \"elevation\": { \"title\": \"Elevation\", \"default\": 0, \"type\": \"number\" }, \"images\": { \"title\": \"Images\", \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/AffineImage\" } }, \"places\": { \"title\": \"Places\", \"default\": [], \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/Place\" } }, \"doors\": { \"title\": \"Doors\", \"default\": [], \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/Door\" } }, \"nav_graphs\": { \"title\": \"Nav Graphs\", \"default\": [], \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/Graph\" } }, \"wall_graph\": { \"title\": \"Wall Graph\", \"default\": { \"name\": \"\", \"vertices\": [], \"edges\": [], \"params\": [] }, \"allOf\": [ { \"$ref\": \"#/definitions/Graph\" } ] } }, \"required\": [ \"name\", \"elevation\", \"images\", \"places\", \"doors\", \"nav_graphs\", \"wall_graph\" ] }, \"Lift\": { \"title\": \"Lift\", \"type\": \"object\", \"properties\": { \"name\": { \"title\": \"Name\", \"default\": \"\", \"type\": \"string\" }, \"levels\": { \"title\": \"Levels\", \"default\": [], \"type\": \"array\", \"items\": { \"type\": \"string\" } }, \"doors\": { \"title\": \"Doors\", \"default\": [], \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/Door\" } }, \"wall_graph\": { \"title\": \"Wall Graph\", \"default\": { \"name\": \"\", \"vertices\": [], \"edges\": [], \"params\": [] }, \"allOf\": [ { \"$ref\": \"#/definitions/Graph\" } ] }, \"ref_x\": { \"title\": \"Ref X\", \"default\": 0, \"type\": \"number\" }, \"ref_y\": { \"title\": \"Ref Y\", \"default\": 0, \"type\": \"number\" }, \"ref_yaw\": { \"title\": \"Ref Yaw\", \"default\": 0, \"type\": \"number\" }, \"width\": { \"title\": \"Width\", \"default\": 0, \"type\": \"number\" }, \"depth\": { \"title\": \"Depth\", \"default\": 0, \"type\": \"number\" } }, \"required\": [ \"name\", \"levels\", \"doors\", \"wall_graph\", \"ref_x\", \"ref_y\", \"ref_yaw\", \"width\", \"depth\" ] } } } ``` ### /building_map/fire_alarm_trigger ``` { \"title\": \"FireAlarmTriggerState\", \"type\": \"object\", \"properties\": { \"unix_millis_time\": { \"title\": \"Unix Millis Time\", \"type\": \"integer\" }, \"trigger\": { \"title\": \"Trigger\", \"type\": \"boolean\" } }, \"required\": [ \"unix_millis_time\", \"trigger\" ] } ``` ### /delivery_alerts ``` { \"title\": \"DeliveryAlert\", \"type\": \"object\", \"properties\": { \"id\": { \"title\": \"Id\", \"type\": \"string\" }, \"category\": { \"$ref\": \"#/definitions/Category\" }, \"tier\": { \"$ref\": \"#/definitions/Tier\" }, \"action\": { \"$ref\": \"#/definitions/Action\" }, \"task_id\": { \"title\": \"Task Id\", \"type\": \"string\" }, \"message\": { \"title\": \"Message\", \"type\": \"string\" } }, \"required\": [ \"id\", \"category\", \"tier\", \"action\", \"task_id\", \"message\" ], \"definitions\": { \"Category\": { \"title\": \"Category\", \"description\": \"An enumeration.\", \"enum\": [ \"missing\", \"wrong\", \"obstructed\", \"cancelled\" ], \"type\": \"string\" }, \"Tier\": { \"title\": \"Tier\", \"description\": \"An enumeration.\", \"enum\": [ \"warning\", \"error\" ], \"type\": \"string\" }, \"Action\": { \"title\": \"Action\", \"description\": \"An enumeration.\", \"enum\": [ \"waiting\", \"cancelled\", \"override\", \"resume\" ], \"type\": \"string\" } } } ``` ### /doors/{door_name}/state ``` { \"title\": \"DoorState\", \"type\": \"object\", \"properties\": { \"door_time\": { \"title\": \"Door Time\", \"default\": { \"sec\": 0, \"nanosec\": 0 }, \"allOf\": [ { \"$ref\": \"#/definitions/Time\" } ] }, \"door_name\": { \"title\": \"Door Name\", \"default\": \"\", \"type\": \"string\" }, \"current_mode\": { \"title\": \"Current Mode\", \"default\": { \"value\": 0 }, \"allOf\": [ { \"$ref\": \"#/definitions/DoorMode\" } ] } }, \"required\": [ \"door_time\", \"door_name\", \"current_mode\" ], \"definitions\": { \"Time\": { \"title\": \"Time\", \"type\": \"object\", \"properties\": { \"sec\": { \"title\": \"Sec\", \"default\": 0, \"minimum\": -2147483648, \"maximum\": 2147483647, \"type\": \"integer\" }, \"nanosec\": { \"title\": \"Nanosec\", \"default\": 0, \"minimum\": 0, \"maximum\": 4294967295, \"type\": \"integer\" } }, \"required\": [ \"sec\", \"nanosec\" ] }, \"DoorMode\": { \"title\": \"DoorMode\", \"type\": \"object\", \"properties\": { \"value\": { \"title\": \"Value\", \"default\": 0, \"minimum\": 0, \"maximum\": 4294967295, \"type\": \"integer\" } }, \"required\": [ \"value\" ] } } } ``` ### /doors/{door_name}/health ``` { \"title\": \"DoorHealth\", \"type\": \"object\", \"properties\": { \"health_status\": { \"title\": \"Health Status\", \"maxLength\": 255, \"nullable\": true, \"type\": \"string\" }, \"health_message\": { \"title\": \"Health Message\", \"nullable\": true, \"type\": \"string\" }, \"id_\": { \"title\": \"Id \", \"maxLength\": 255, \"type\": \"string\" } }, \"required\": [ \"health_status\", \"id_\" ], \"additionalProperties\": false } ``` ### /lifts/{lift_name}/state ``` { \"title\": \"LiftState\", \"type\": \"object\", \"properties\": { \"lift_time\": { \"title\": \"Lift Time\", \"default\": { \"sec\": 0, \"nanosec\": 0 }, \"allOf\": [ { \"$ref\": \"#/definitions/Time\" } ] }, \"lift_name\": { \"title\": \"Lift Name\", \"default\": \"\", \"type\": \"string\" }, \"available_floors\": { \"title\": \"Available Floors\", \"default\": [], \"type\": \"array\", \"items\": { \"type\": \"string\" } }, \"current_floor\": { \"title\": \"Current Floor\", \"default\": \"\", \"type\": \"string\" }, \"destination_floor\": { \"title\": \"Destination Floor\", \"default\": \"\", \"type\": \"string\" }, \"door_state\": { \"title\": \"Door State\", \"default\": 0, \"minimum\": 0, \"maximum\": 255, \"type\": \"integer\" }, \"motion_state\": { \"title\": \"Motion State\", \"default\": 0, \"minimum\": 0, \"maximum\": 255, \"type\": \"integer\" }, \"available_modes\": { \"title\": \"Available Modes\", \"type\": \"array\", \"items\": { \"type\": \"integer\" } }, \"current_mode\": { \"title\": \"Current Mode\", \"default\": 0, \"minimum\": 0, \"maximum\": 255, \"type\": \"integer\" }, \"session_id\": { \"title\": \"Session Id\", \"default\": \"\", \"type\": \"string\" } }, \"required\": [ \"lift_time\", \"lift_name\", \"available_floors\", \"current_floor\", \"destination_floor\", \"door_state\", \"motion_state\", \"available_modes\", \"current_mode\", \"session_id\" ], \"definitions\": { \"Time\": { \"title\": \"Time\", \"type\": \"object\", \"properties\": { \"sec\": { \"title\": \"Sec\", \"default\": 0, \"minimum\": -2147483648, \"maximum\": 2147483647, \"type\": \"integer\" }, \"nanosec\": { \"title\": \"Nanosec\", \"default\": 0, \"minimum\": 0, \"maximum\": 4294967295, \"type\": \"integer\" } }, \"required\": [ \"sec\", \"nanosec\" ] } } } ``` ### /lifts/{lift_name}/health ``` { \"title\": \"LiftHealth\", \"type\": \"object\", \"properties\": { \"health_status\": { \"title\": \"Health Status\", \"maxLength\": 255, \"nullable\": true, \"type\": \"string\" }, \"health_message\": { \"title\": \"Health Message\", \"nullable\": true, \"type\": \"string\" }, \"id_\": { \"title\": \"Id \", \"maxLength\": 255, \"type\": \"string\" } }, \"required\": [ \"health_status\", \"id_\" ], \"additionalProperties\": false } ``` ### /tasks/{task_id}/state ``` { \"title\": \"TaskState\", \"type\": \"object\", \"properties\": { \"booking\": { \"$ref\": \"#/definitions/Booking\" }, \"category\": { \"$ref\": \"#/definitions/Category\" }, \"detail\": { \"$ref\": \"#/definitions/Detail\" }, \"unix_millis_start_time\": { \"title\": \"Unix Millis Start Time\", \"type\": \"integer\" }, \"unix_millis_finish_time\": { \"title\": \"Unix Millis Finish Time\", \"type\": \"integer\" }, \"original_estimate_millis\": { \"$ref\": \"#/definitions/EstimateMillis\" }, \"estimate_millis\": { \"$ref\": \"#/definitions/EstimateMillis\" }, \"assigned_to\": { \"title\": \"Assigned To\", \"description\": \"Which agent (robot) is the task assigned to\", \"allOf\": [ { \"$ref\": \"#/definitions/AssignedTo\" } ] }, \"status\": { \"$ref\": \"#/definitions/Status\" }, \"dispatch\": { \"$ref\": \"#/definitions/Dispatch\" }, \"phases\": { \"title\": \"Phases\", \"description\": \"A dictionary of the states of the phases of the task. The keys (property names) are phase IDs, which are integers.\", \"type\": \"object\", \"additionalProperties\": { \"$ref\": \"#/definitions/Phase\" } }, \"completed\": { \"title\": \"Completed\", \"description\": \"An array of the IDs of completed phases of this task\", \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/Id\" } }, \"active\": { \"title\": \"Active\", \"description\": \"The ID of the active phase for this task\", \"allOf\": [ { \"$ref\": \"#/definitions/Id\" } ] }, \"pending\": { \"title\": \"Pending\", \"description\": \"An array of the pending phases of this task\", \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/Id\" } }, \"interruptions\": { \"title\": \"Interruptions\", \"description\": \"A dictionary of interruptions that have been applied to this task. The keys (property names) are the unique token of the interruption request.\", \"type\": \"object\", \"additionalProperties\": { \"$ref\": \"#/definitions/Interruption\" } }, \"cancellation\": { \"title\": \"Cancellation\", \"description\": \"If the task was cancelled, this will describe information about the request.\", \"allOf\": [ { \"$ref\": \"#/definitions/Cancellation\" } ] }, \"killed\": { \"title\": \"Killed\", \"description\": \"If the task was killed, this will describe information about the request.\", \"allOf\": [ { \"$ref\": \"#/definitions/Killed\" } ] }, \"unix_millis_warn_time\": { \"title\": \"Unix Millis Warn Time\", \"type\": \"integer\" } }, \"required\": [ \"booking\" ], \"definitions\": { \"Booking\": { \"title\": \"Booking\", \"type\": \"object\", \"properties\": { \"id\": { \"title\": \"Id\", \"description\": \"The unique identifier for this task\", \"type\": \"string\" }, \"unix_millis_earliest_start_time\": { \"title\": \"Unix Millis Earliest Start Time\", \"type\": \"integer\" }, \"unix_millis_request_time\": { \"title\": \"Unix Millis Request Time\", \"type\": \"integer\" }, \"priority\": { \"title\": \"Priority\", \"description\": \"Priority information about this task\", \"anyOf\": [ { \"type\": \"object\" }, { \"type\": \"string\" } ] }, \"labels\": { \"title\": \"Labels\", \"description\": \"Information about how and why this task was booked\", \"type\": \"array\", \"items\": { \"type\": \"string\" } }, \"requester\": { \"title\": \"Requester\", \"description\": \"(Optional) An identifier for the entity that requested this task\", \"type\": \"string\" } }, \"required\": [ \"id\" ] }, \"Category\": { \"title\": \"Category\", \"description\": \"The category of this task or phase\", \"type\": \"string\" }, \"Detail\": { \"title\": \"Detail\", \"description\": \"Detailed information about a task, phase, or event\", \"anyOf\": [ { \"type\": \"object\" }, { \"type\": \"array\", \"items\": {} }, { \"type\": \"string\" } ] }, \"EstimateMillis\": { \"title\": \"EstimateMillis\", \"description\": \"An estimate, in milliseconds, of how long the subject will take to complete\", \"minimum\": 0, \"type\": \"integer\" }, \"AssignedTo\": { \"title\": \"AssignedTo\", \"type\": \"object\", \"properties\": { \"group\": { \"title\": \"Group\", \"type\": \"string\" }, \"name\": { \"title\": \"Name\", \"type\": \"string\" } }, \"required\": [ \"group\", \"name\" ] }, \"Status\": { \"title\": \"Status\", \"description\": \"An enumeration.\", \"enum\": [ \"uninitialized\", \"blocked\", \"error\", \"failed\", \"queued\", \"standby\", \"underway\", \"delayed\", \"skipped\", \"canceled\", \"killed\", \"completed\" ] }, \"Status1\": { \"title\": \"Status1\", \"description\": \"An enumeration.\", \"enum\": [ \"queued\", \"selected\", \"dispatched\", \"failed_to_assign\", \"canceled_in_flight\" ] }, \"Assignment\": { \"title\": \"Assignment\", \"type\": \"object\", \"properties\": { \"fleet_name\": { \"title\": \"Fleet Name\", \"type\": \"string\" }, \"expected_robot_name\": { \"title\": \"Expected Robot Name\", \"type\": \"string\" } } }, \"Error\": { \"title\": \"Error\", \"type\": \"object\", \"properties\": { \"code\": { \"title\": \"Code\", \"description\": \"A standard code for the kind of error that has occurred\", \"minimum\": 0, \"type\": \"integer\" }, \"category\": { \"title\": \"Category\", \"description\": \"The category of the error\", \"type\": \"string\" }, \"detail\": { \"title\": \"Detail\", \"description\": \"Details about the error\", \"type\": \"string\" } } }, \"Dispatch\": { \"title\": \"Dispatch\", \"type\": \"object\", \"properties\": { \"status\": { \"$ref\": \"#/definitions/Status1\" }, \"assignment\": { \"$ref\": \"#/definitions/Assignment\" }, \"errors\": { \"title\": \"Errors\", \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/Error\" } } }, \"required\": [ \"status\" ] }, \"Id\": { \"title\": \"Id\", \"minimum\": 0, \"type\": \"integer\" }, \"EventState\": { \"title\": \"EventState\", \"type\": \"object\", \"properties\": { \"id\": { \"$ref\": \"#/definitions/Id\" }, \"status\": { \"$ref\": \"#/definitions/Status\" }, \"name\": { \"title\": \"Name\", \"description\": \"The brief name of the event\", \"type\": \"string\" }, \"detail\": { \"title\": \"Detail\", \"description\": \"Detailed information about the event\", \"allOf\": [ { \"$ref\": \"#/definitions/Detail\" } ] }, \"deps\": { \"title\": \"Deps\", \"description\": \"This event may depend on other events. This array contains the IDs of those other event dependencies.\", \"type\": \"array\", \"items\": { \"type\": \"integer\", \"minimum\": 0 } } }, \"required\": [ \"id\" ] }, \"Undo\": { \"title\": \"Undo\", \"type\": \"object\", \"properties\": { \"unix_millis_request_time\": { \"title\": \"Unix Millis Request Time\", \"description\": \"The time that the undo skip request arrived\", \"type\": \"integer\" }, \"labels\": { \"title\": \"Labels\", \"description\": \"Labels to describe the undo skip request\", \"type\": \"array\", \"items\": { \"type\": \"string\" } } }, \"required\": [ \"unix_millis_request_time\", \"labels\" ] }, \"SkipPhaseRequest\": { \"title\": \"SkipPhaseRequest\", \"type\": \"object\", \"properties\": { \"unix_millis_request_time\": { \"title\": \"Unix Millis Request Time\", \"description\": \"The time that the skip request arrived\", \"type\": \"integer\" }, \"labels\": { \"title\": \"Labels\", \"description\": \"Labels to describe the purpose of the skip request\", \"type\": \"array\", \"items\": { \"type\": \"string\" } }, \"undo\": { \"title\": \"Undo\", \"description\": \"Information about an undo skip request that applied to this request\", \"allOf\": [ { \"$ref\": \"#/definitions/Undo\" } ] } }, \"required\": [ \"unix_millis_request_time\", \"labels\" ] }, \"Phase\": { \"title\": \"Phase\", \"type\": \"object\", \"properties\": { \"id\": { \"$ref\": \"#/definitions/Id\" }, \"category\": { \"$ref\": \"#/definitions/Category\" }, \"detail\": { \"$ref\": \"#/definitions/Detail\" }, \"unix_millis_start_time\": { \"title\": \"Unix Millis Start Time\", \"type\": \"integer\" }, \"unix_millis_finish_time\": { \"title\": \"Unix Millis Finish Time\", \"type\": \"integer\" }, \"original_estimate_millis\": { \"$ref\": \"#/definitions/EstimateMillis\" }, \"estimate_millis\": { \"$ref\": \"#/definitions/EstimateMillis\" }, \"final_event_id\": { \"$ref\": \"#/definitions/Id\" }, \"events\": { \"title\": \"Events\", \"description\": \"A dictionary of events for this phase. The keys (property names) are the event IDs, which are integers.\", \"type\": \"object\", \"additionalProperties\": { \"$ref\": \"#/definitions/EventState\" } }, \"skip_requests\": { \"title\": \"Skip Requests\", \"description\": \"Information about any skip requests that have been received\", \"type\": \"object\", \"additionalProperties\": { \"$ref\": \"#/definitions/SkipPhaseRequest\" } } }, \"required\": [ \"id\" ] }, \"ResumedBy\": { \"title\": \"ResumedBy\", \"type\": \"object\", \"properties\": { \"unix_millis_request_time\": { \"title\": \"Unix Millis Request Time\", \"description\": \"The time that the resume request arrived\", \"type\": \"integer\" }, \"labels\": { \"title\": \"Labels\", \"description\": \"Labels to describe the resume request\", \"type\": \"array\", \"items\": { \"type\": \"string\" } } }, \"required\": [ \"labels\" ] }, \"Interruption\": { \"title\": \"Interruption\", \"type\": \"object\", \"properties\": { \"unix_millis_request_time\": { \"title\": \"Unix Millis Request Time\", \"description\": \"The time that the interruption request arrived\", \"type\": \"integer\" }, \"labels\": { \"title\": \"Labels\", \"description\": \"Labels to describe the purpose of the interruption\", \"type\": \"array\", \"items\": { \"type\": \"string\" } }, \"resumed_by\": { \"title\": \"Resumed By\", \"description\": \"Information about the resume request that ended this interruption. This field will be missing if the interruption is still active.\", \"allOf\": [ { \"$ref\": \"#/definitions/ResumedBy\" } ] } }, \"required\": [ \"unix_millis_request_time\", \"labels\" ] }, \"Cancellation\": { \"title\": \"Cancellation\", \"type\": \"object\", \"properties\": { \"unix_millis_request_time\": { \"title\": \"Unix Millis Request Time\", \"description\": \"The time that the cancellation request arrived\", \"type\": \"integer\" }, \"labels\": { \"title\": \"Labels\", \"description\": \"Labels to describe the cancel request\", \"type\": \"array\", \"items\": { \"type\": \"string\" } } }, \"required\": [ \"unix_millis_request_time\", \"labels\" ] }, \"Killed\": { \"title\": \"Killed\", \"type\": \"object\", \"properties\": { \"unix_millis_request_time\": { \"title\": \"Unix Millis Request Time\", \"description\": \"The time that the cancellation request arrived\", \"type\": \"integer\" }, \"labels\": { \"title\": \"Labels\", \"description\": \"Labels to describe the kill request\", \"type\": \"array\", \"items\": { \"type\": \"string\" } } }, \"required\": [ \"unix_millis_request_time\", \"labels\" ] } } } ``` ### /tasks/{task_id}/log ``` { \"title\": \"TaskEventLog\", \"type\": \"object\", \"properties\": { \"task_id\": { \"title\": \"Task Id\", \"type\": \"string\" }, \"log\": { \"title\": \"Log\", \"description\": \"Log entries related to the overall task\", \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/LogEntry\" } }, \"phases\": { \"title\": \"Phases\", \"description\": \"A dictionary whose keys (property names) are the indices of a phase\", \"type\": \"object\", \"additionalProperties\": { \"$ref\": \"#/definitions/Phases\" } } }, \"required\": [ \"task_id\" ], \"additionalProperties\": false, \"definitions\": { \"Tier\": { \"title\": \"Tier\", \"description\": \"An enumeration.\", \"enum\": [ \"uninitialized\", \"info\", \"warning\", \"error\" ] }, \"LogEntry\": { \"title\": \"LogEntry\", \"type\": \"object\", \"properties\": { \"seq\": { \"title\": \"Seq\", \"description\": \"Sequence number for this entry. Each entry has a unique sequence number which monotonically increase, until integer overflow causes a wrap around.\", \"exclusiveMaximum\": 4294967296, \"minimum\": 0, \"type\": \"integer\" }, \"tier\": { \"description\": \"The importance level of the log entry\", \"allOf\": [ { \"$ref\": \"#/definitions/Tier\" } ] }, \"unix_millis_time\": { \"title\": \"Unix Millis Time\", \"type\": \"integer\" }, \"text\": { \"title\": \"Text\", \"description\": \"The text of the log entry\", \"type\": \"string\" } }, \"required\": [ \"seq\", \"tier\", \"unix_millis_time\", \"text\" ] }, \"Phases\": { \"title\": \"Phases\", \"type\": \"object\", \"properties\": { \"log\": { \"title\": \"Log\", \"description\": \"Log entries related to the overall phase\", \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/LogEntry\" } }, \"events\": { \"title\": \"Events\", \"description\": \"A dictionary whose keys (property names) are the indices of an event in the phase\", \"type\": \"object\", \"additionalProperties\": { \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/LogEntry\" } } } }, \"additionalProperties\": false } } } ``` ### /dispensers/{guid}/state ``` { \"title\": \"DispenserState\", \"type\": \"object\", \"properties\": { \"time\": { \"title\": \"Time\", \"default\": { \"sec\": 0, \"nanosec\": 0 }, \"allOf\": [ { \"$ref\": \"#/definitions/Time\" } ] }, \"guid\": { \"title\": \"Guid\", \"default\": \"\", \"type\": \"string\" }, \"mode\": { \"title\": \"Mode\", \"default\": 0, \"minimum\": -2147483648, \"maximum\": 2147483647, \"type\": \"integer\" }, \"request_guid_queue\": { \"title\": \"Request Guid Queue\", \"default\": [], \"type\": \"array\", \"items\": { \"type\": \"string\" } }, \"seconds_remaining\": { \"title\": \"Seconds Remaining\", \"default\": 0, \"type\": \"number\" } }, \"required\": [ \"time\", \"guid\", \"mode\", \"request_guid_queue\", \"seconds_remaining\" ], \"definitions\": { \"Time\": { \"title\": \"Time\", \"type\": \"object\", \"properties\": { \"sec\": { \"title\": \"Sec\", \"default\": 0, \"minimum\": -2147483648, \"maximum\": 2147483647, \"type\": \"integer\" }, \"nanosec\": { \"title\": \"Nanosec\", \"default\": 0, \"minimum\": 0, \"maximum\": 4294967295, \"type\": \"integer\" } }, \"required\": [ \"sec\", \"nanosec\" ] } } } ``` ### /dispensers/{guid}/health ``` { \"title\": \"DispenserHealth\", \"type\": \"object\", \"properties\": { \"health_status\": { \"title\": \"Health Status\", \"maxLength\": 255, \"nullable\": true, \"type\": \"string\" }, \"health_message\": { \"title\": \"Health Message\", \"nullable\": true, \"type\": \"string\" }, \"id_\": { \"title\": \"Id \", \"maxLength\": 255, \"type\": \"string\" } }, \"required\": [ \"health_status\", \"id_\" ], \"additionalProperties\": false } ``` ### /ingestors/{guid}/state ``` { \"title\": \"IngestorState\", \"type\": \"object\", \"properties\": { \"time\": { \"title\": \"Time\", \"default\": { \"sec\": 0, \"nanosec\": 0 }, \"allOf\": [ { \"$ref\": \"#/definitions/Time\" } ] }, \"guid\": { \"title\": \"Guid\", \"default\": \"\", \"type\": \"string\" }, \"mode\": { \"title\": \"Mode\", \"default\": 0, \"minimum\": -2147483648, \"maximum\": 2147483647, \"type\": \"integer\" }, \"request_guid_queue\": { \"title\": \"Request Guid Queue\", \"default\": [], \"type\": \"array\", \"items\": { \"type\": \"string\" } }, \"seconds_remaining\": { \"title\": \"Seconds Remaining\", \"default\": 0, \"type\": \"number\" } }, \"required\": [ \"time\", \"guid\", \"mode\", \"request_guid_queue\", \"seconds_remaining\" ], \"definitions\": { \"Time\": { \"title\": \"Time\", \"type\": \"object\", \"properties\": { \"sec\": { \"title\": \"Sec\", \"default\": 0, \"minimum\": -2147483648, \"maximum\": 2147483647, \"type\": \"integer\" }, \"nanosec\": { \"title\": \"Nanosec\", \"default\": 0, \"minimum\": 0, \"maximum\": 4294967295, \"type\": \"integer\" } }, \"required\": [ \"sec\", \"nanosec\" ] } } } ``` ### /ingestors/{guid}/health ``` { \"title\": \"IngestorHealth\", \"type\": \"object\", \"properties\": { \"health_status\": { \"title\": \"Health Status\", \"maxLength\": 255, \"nullable\": true, \"type\": \"string\" }, \"health_message\": { \"title\": \"Health Message\", \"nullable\": true, \"type\": \"string\" }, \"id_\": { \"title\": \"Id \", \"maxLength\": 255, \"type\": \"string\" } }, \"required\": [ \"health_status\", \"id_\" ], \"additionalProperties\": false } ``` ### /fleets/{name}/state ``` { \"title\": \"FleetState\", \"type\": \"object\", \"properties\": { \"name\": { \"title\": \"Name\", \"type\": \"string\" }, \"robots\": { \"title\": \"Robots\", \"description\": \"A dictionary of the states of the robots that belong to this fleet\", \"type\": \"object\", \"additionalProperties\": { \"$ref\": \"#/definitions/RobotState\" } } }, \"definitions\": { \"Status\": { \"title\": \"Status\", \"description\": \"An enumeration.\", \"enum\": [ \"uninitialized\", \"offline\", \"shutdown\", \"idle\", \"charging\", \"working\", \"error\" ] }, \"Location2D\": { \"title\": \"Location2D\", \"type\": \"object\", \"properties\": { \"map\": { \"title\": \"Map\", \"type\": \"string\" }, \"x\": { \"title\": \"X\", \"type\": \"number\" }, \"y\": { \"title\": \"Y\", \"type\": \"number\" }, \"yaw\": { \"title\": \"Yaw\", \"type\": \"number\" } }, \"required\": [ \"map\", \"x\", \"y\", \"yaw\" ] }, \"Issue\": { \"title\": \"Issue\", \"type\": \"object\", \"properties\": { \"category\": { \"title\": \"Category\", \"description\": \"Category of the robot\'s issue\", \"type\": \"string\" }, \"detail\": { \"title\": \"Detail\", \"description\": \"Detailed information about the issue\", \"anyOf\": [ { \"type\": \"object\" }, { \"type\": \"array\", \"items\": {} }, { \"type\": \"string\" } ] } } }, \"Commission\": { \"title\": \"Commission\", \"type\": \"object\", \"properties\": { \"dispatch_tasks\": { \"title\": \"Dispatch Tasks\", \"description\": \"Should the robot accept dispatched tasks, true/false. When used in a request, leave this unset to not change the robot\'s current value.\", \"type\": \"boolean\" }, \"direct_tasks\": { \"title\": \"Direct Tasks\", \"description\": \"Should the robot accept direct task requests, true/false. When used in a request, leave this unset to not change the robot\'s current value.\", \"type\": \"boolean\" }, \"idle_behavior\": { \"title\": \"Idle Behavior\", \"description\": \"Should the robot perform its idle behavior, true/false. When used in a request, leave this unset to not change the robot\'s current value.\", \"type\": \"boolean\" } } }, \"MutexGroups\": { \"title\": \"MutexGroups\", \"type\": \"object\", \"properties\": { \"locked\": { \"title\": \"Locked\", \"description\": \"A list of mutex groups that this robot has currently locked\", \"type\": \"array\", \"items\": { \"type\": \"string\" } }, \"requesting\": { \"title\": \"Requesting\", \"description\": \"A list of the mutex groups that this robot is currently requesting but has not lockd yet\", \"type\": \"array\", \"items\": { \"type\": \"string\" } } } }, \"RobotState\": { \"title\": \"RobotState\", \"type\": \"object\", \"properties\": { \"name\": { \"title\": \"Name\", \"type\": \"string\" }, \"status\": { \"description\": \"A simple token representing the status of the robot\", \"allOf\": [ { \"$ref\": \"#/definitions/Status\" } ] }, \"task_id\": { \"title\": \"Task Id\", \"description\": \"The ID of the task this robot is currently working on. Empty string if the robot is not working on a task.\", \"type\": \"string\" }, \"unix_millis_time\": { \"title\": \"Unix Millis Time\", \"type\": \"integer\" }, \"location\": { \"$ref\": \"#/definitions/Location2D\" }, \"battery\": { \"title\": \"Battery\", \"description\": \"State of charge of the battery. Values range from 0.0 (depleted) to 1.0 (fully charged)\", \"minimum\": 0.0, \"maximum\": 1.0, \"type\": \"number\" }, \"issues\": { \"title\": \"Issues\", \"description\": \"A list of issues with the robot that operators need to address\", \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/Issue\" } }, \"commission\": { \"$ref\": \"#/definitions/Commission\" }, \"mutex_groups\": { \"title\": \"Mutex Groups\", \"description\": \"Information about the mutex groups that this robot is interacting with\", \"allOf\": [ { \"$ref\": \"#/definitions/MutexGroups\" } ] } } } } } ``` ### /fleets/{name}/log ``` { \"title\": \"FleetLog\", \"type\": \"object\", \"properties\": { \"name\": { \"title\": \"Name\", \"type\": \"string\" }, \"log\": { \"title\": \"Log\", \"description\": \"Log for the overall fleet\", \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/LogEntry\" } }, \"robots\": { \"title\": \"Robots\", \"description\": \"Dictionary of logs for the individual robots. The keys (property names) are the robot names.\", \"type\": \"object\", \"additionalProperties\": { \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/LogEntry\" } } } }, \"definitions\": { \"Tier\": { \"title\": \"Tier\", \"description\": \"An enumeration.\", \"enum\": [ \"uninitialized\", \"info\", \"warning\", \"error\" ] }, \"LogEntry\": { \"title\": \"LogEntry\", \"type\": \"object\", \"properties\": { \"seq\": { \"title\": \"Seq\", \"description\": \"Sequence number for this entry. Each entry has a unique sequence number which monotonically increase, until integer overflow causes a wrap around.\", \"exclusiveMaximum\": 4294967296, \"minimum\": 0, \"type\": \"integer\" }, \"tier\": { \"description\": \"The importance level of the log entry\", \"allOf\": [ { \"$ref\": \"#/definitions/Tier\" } ] }, \"unix_millis_time\": { \"title\": \"Unix Millis Time\", \"type\": \"integer\" }, \"text\": { \"title\": \"Text\", \"description\": \"The text of the log entry\", \"type\": \"string\" } }, \"required\": [ \"seq\", \"tier\", \"unix_millis_time\", \"text\" ] } } } ``` + * # NOTE: This endpoint is here for documentation purposes only, this is _not_ a REST endpoint. ## About This exposes a minimal pubsub system built on top of socket.io. It works similar to a normal socket.io endpoint, except that are 2 special rooms which control subscriptions. ## Rooms ### subscribe Clients must send a message to this room to start receiving messages on other rooms. The message must be of the form: ``` { \"room\": \"\" } ``` ### unsubscribe Clients can send a message to this room to stop receiving messages on other rooms. The message must be of the form: ``` { \"room\": \"\" } ``` ### /alerts ``` { \"title\": \"Alert\", \"description\": \"General alert that can be triggered by events.\", \"type\": \"object\", \"properties\": { \"id\": { \"title\": \"Id\", \"maxLength\": 255, \"type\": \"string\" }, \"original_id\": { \"title\": \"Original Id\", \"maxLength\": 255, \"type\": \"string\" }, \"category\": { \"title\": \"Category\", \"description\": \"Default: default
Task: task
Fleet: fleet
Robot: robot\", \"maxLength\": 7, \"type\": \"string\" }, \"unix_millis_created_time\": { \"title\": \"Unix Millis Created Time\", \"minimum\": -9223372036854775808, \"maximum\": 9223372036854775807, \"type\": \"integer\" }, \"acknowledged_by\": { \"title\": \"Acknowledged By\", \"maxLength\": 255, \"nullable\": true, \"type\": \"string\" }, \"unix_millis_acknowledged_time\": { \"title\": \"Unix Millis Acknowledged Time\", \"minimum\": -9223372036854775808, \"maximum\": 9223372036854775807, \"nullable\": true, \"type\": \"integer\" } }, \"required\": [ \"id\", \"original_id\", \"category\", \"unix_millis_created_time\" ], \"additionalProperties\": false } ``` ### /beacons ``` { \"title\": \"BeaconState\", \"type\": \"object\", \"properties\": { \"id\": { \"title\": \"Id\", \"maxLength\": 255, \"type\": \"string\" }, \"online\": { \"title\": \"Online\", \"type\": \"boolean\" }, \"category\": { \"title\": \"Category\", \"maxLength\": 255, \"nullable\": true, \"type\": \"string\" }, \"activated\": { \"title\": \"Activated\", \"type\": \"boolean\" }, \"level\": { \"title\": \"Level\", \"maxLength\": 255, \"nullable\": true, \"type\": \"string\" } }, \"required\": [ \"id\", \"online\", \"activated\" ], \"additionalProperties\": false } ``` ### /building_map ``` { \"title\": \"BuildingMap\", \"type\": \"object\", \"properties\": { \"name\": { \"title\": \"Name\", \"default\": \"\", \"type\": \"string\" }, \"levels\": { \"title\": \"Levels\", \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/Level\" } }, \"lifts\": { \"title\": \"Lifts\", \"default\": [], \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/Lift\" } } }, \"required\": [ \"name\", \"levels\", \"lifts\" ], \"definitions\": { \"AffineImage\": { \"title\": \"AffineImage\", \"type\": \"object\", \"properties\": { \"name\": { \"title\": \"Name\", \"default\": \"\", \"type\": \"string\" }, \"x_offset\": { \"title\": \"X Offset\", \"default\": 0, \"type\": \"number\" }, \"y_offset\": { \"title\": \"Y Offset\", \"default\": 0, \"type\": \"number\" }, \"yaw\": { \"title\": \"Yaw\", \"default\": 0, \"type\": \"number\" }, \"scale\": { \"title\": \"Scale\", \"default\": 0, \"type\": \"number\" }, \"encoding\": { \"title\": \"Encoding\", \"default\": \"\", \"type\": \"string\" }, \"data\": { \"title\": \"Data\", \"type\": \"string\" } }, \"required\": [ \"name\", \"x_offset\", \"y_offset\", \"yaw\", \"scale\", \"encoding\", \"data\" ] }, \"Place\": { \"title\": \"Place\", \"type\": \"object\", \"properties\": { \"name\": { \"title\": \"Name\", \"default\": \"\", \"type\": \"string\" }, \"x\": { \"title\": \"X\", \"default\": 0, \"type\": \"number\" }, \"y\": { \"title\": \"Y\", \"default\": 0, \"type\": \"number\" }, \"yaw\": { \"title\": \"Yaw\", \"default\": 0, \"type\": \"number\" }, \"position_tolerance\": { \"title\": \"Position Tolerance\", \"default\": 0, \"type\": \"number\" }, \"yaw_tolerance\": { \"title\": \"Yaw Tolerance\", \"default\": 0, \"type\": \"number\" } }, \"required\": [ \"name\", \"x\", \"y\", \"yaw\", \"position_tolerance\", \"yaw_tolerance\" ] }, \"Door\": { \"title\": \"Door\", \"type\": \"object\", \"properties\": { \"name\": { \"title\": \"Name\", \"default\": \"\", \"type\": \"string\" }, \"v1_x\": { \"title\": \"V1 X\", \"default\": 0, \"type\": \"number\" }, \"v1_y\": { \"title\": \"V1 Y\", \"default\": 0, \"type\": \"number\" }, \"v2_x\": { \"title\": \"V2 X\", \"default\": 0, \"type\": \"number\" }, \"v2_y\": { \"title\": \"V2 Y\", \"default\": 0, \"type\": \"number\" }, \"door_type\": { \"title\": \"Door Type\", \"default\": 0, \"minimum\": 0, \"maximum\": 255, \"type\": \"integer\" }, \"motion_range\": { \"title\": \"Motion Range\", \"default\": 0, \"type\": \"number\" }, \"motion_direction\": { \"title\": \"Motion Direction\", \"default\": 0, \"minimum\": -2147483648, \"maximum\": 2147483647, \"type\": \"integer\" } }, \"required\": [ \"name\", \"v1_x\", \"v1_y\", \"v2_x\", \"v2_y\", \"door_type\", \"motion_range\", \"motion_direction\" ] }, \"Param\": { \"title\": \"Param\", \"type\": \"object\", \"properties\": { \"name\": { \"title\": \"Name\", \"default\": \"\", \"type\": \"string\" }, \"type\": { \"title\": \"Type\", \"default\": 0, \"minimum\": 0, \"maximum\": 4294967295, \"type\": \"integer\" }, \"value_int\": { \"title\": \"Value Int\", \"default\": 0, \"minimum\": -2147483648, \"maximum\": 2147483647, \"type\": \"integer\" }, \"value_float\": { \"title\": \"Value Float\", \"default\": 0, \"type\": \"number\" }, \"value_string\": { \"title\": \"Value String\", \"default\": \"\", \"type\": \"string\" }, \"value_bool\": { \"title\": \"Value Bool\", \"default\": false, \"type\": \"boolean\" } }, \"required\": [ \"name\", \"type\", \"value_int\", \"value_float\", \"value_string\", \"value_bool\" ] }, \"GraphNode\": { \"title\": \"GraphNode\", \"type\": \"object\", \"properties\": { \"x\": { \"title\": \"X\", \"default\": 0, \"type\": \"number\" }, \"y\": { \"title\": \"Y\", \"default\": 0, \"type\": \"number\" }, \"name\": { \"title\": \"Name\", \"default\": \"\", \"type\": \"string\" }, \"params\": { \"title\": \"Params\", \"default\": [], \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/Param\" } } }, \"required\": [ \"x\", \"y\", \"name\", \"params\" ] }, \"GraphEdge\": { \"title\": \"GraphEdge\", \"type\": \"object\", \"properties\": { \"v1_idx\": { \"title\": \"V1 Idx\", \"default\": 0, \"minimum\": 0, \"maximum\": 4294967295, \"type\": \"integer\" }, \"v2_idx\": { \"title\": \"V2 Idx\", \"default\": 0, \"minimum\": 0, \"maximum\": 4294967295, \"type\": \"integer\" }, \"params\": { \"title\": \"Params\", \"default\": [], \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/Param\" } }, \"edge_type\": { \"title\": \"Edge Type\", \"default\": 0, \"minimum\": 0, \"maximum\": 255, \"type\": \"integer\" } }, \"required\": [ \"v1_idx\", \"v2_idx\", \"params\", \"edge_type\" ] }, \"Graph\": { \"title\": \"Graph\", \"type\": \"object\", \"properties\": { \"name\": { \"title\": \"Name\", \"default\": \"\", \"type\": \"string\" }, \"vertices\": { \"title\": \"Vertices\", \"default\": [], \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/GraphNode\" } }, \"edges\": { \"title\": \"Edges\", \"default\": [], \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/GraphEdge\" } }, \"params\": { \"title\": \"Params\", \"default\": [], \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/Param\" } } }, \"required\": [ \"name\", \"vertices\", \"edges\", \"params\" ] }, \"Level\": { \"title\": \"Level\", \"type\": \"object\", \"properties\": { \"name\": { \"title\": \"Name\", \"default\": \"\", \"type\": \"string\" }, \"elevation\": { \"title\": \"Elevation\", \"default\": 0, \"type\": \"number\" }, \"images\": { \"title\": \"Images\", \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/AffineImage\" } }, \"places\": { \"title\": \"Places\", \"default\": [], \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/Place\" } }, \"doors\": { \"title\": \"Doors\", \"default\": [], \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/Door\" } }, \"nav_graphs\": { \"title\": \"Nav Graphs\", \"default\": [], \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/Graph\" } }, \"wall_graph\": { \"title\": \"Wall Graph\", \"default\": { \"name\": \"\", \"vertices\": [], \"edges\": [], \"params\": [] }, \"allOf\": [ { \"$ref\": \"#/definitions/Graph\" } ] } }, \"required\": [ \"name\", \"elevation\", \"images\", \"places\", \"doors\", \"nav_graphs\", \"wall_graph\" ] }, \"Lift\": { \"title\": \"Lift\", \"type\": \"object\", \"properties\": { \"name\": { \"title\": \"Name\", \"default\": \"\", \"type\": \"string\" }, \"levels\": { \"title\": \"Levels\", \"default\": [], \"type\": \"array\", \"items\": { \"type\": \"string\" } }, \"doors\": { \"title\": \"Doors\", \"default\": [], \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/Door\" } }, \"wall_graph\": { \"title\": \"Wall Graph\", \"default\": { \"name\": \"\", \"vertices\": [], \"edges\": [], \"params\": [] }, \"allOf\": [ { \"$ref\": \"#/definitions/Graph\" } ] }, \"ref_x\": { \"title\": \"Ref X\", \"default\": 0, \"type\": \"number\" }, \"ref_y\": { \"title\": \"Ref Y\", \"default\": 0, \"type\": \"number\" }, \"ref_yaw\": { \"title\": \"Ref Yaw\", \"default\": 0, \"type\": \"number\" }, \"width\": { \"title\": \"Width\", \"default\": 0, \"type\": \"number\" }, \"depth\": { \"title\": \"Depth\", \"default\": 0, \"type\": \"number\" } }, \"required\": [ \"name\", \"levels\", \"doors\", \"wall_graph\", \"ref_x\", \"ref_y\", \"ref_yaw\", \"width\", \"depth\" ] } } } ``` ### /building_map/fire_alarm_trigger ``` { \"title\": \"FireAlarmTriggerState\", \"type\": \"object\", \"properties\": { \"unix_millis_time\": { \"title\": \"Unix Millis Time\", \"type\": \"integer\" }, \"trigger\": { \"title\": \"Trigger\", \"type\": \"boolean\" } }, \"required\": [ \"unix_millis_time\", \"trigger\" ] } ``` ### /delivery_alerts ``` { \"title\": \"DeliveryAlert\", \"type\": \"object\", \"properties\": { \"id\": { \"title\": \"Id\", \"type\": \"string\" }, \"category\": { \"$ref\": \"#/definitions/Category\" }, \"tier\": { \"$ref\": \"#/definitions/Tier\" }, \"action\": { \"$ref\": \"#/definitions/Action\" }, \"task_id\": { \"title\": \"Task Id\", \"type\": \"string\" }, \"message\": { \"title\": \"Message\", \"type\": \"string\" } }, \"required\": [ \"id\", \"category\", \"tier\", \"action\", \"task_id\", \"message\" ], \"definitions\": { \"Category\": { \"title\": \"Category\", \"description\": \"An enumeration.\", \"enum\": [ \"missing\", \"wrong\", \"obstructed\", \"cancelled\" ], \"type\": \"string\" }, \"Tier\": { \"title\": \"Tier\", \"description\": \"An enumeration.\", \"enum\": [ \"warning\", \"error\" ], \"type\": \"string\" }, \"Action\": { \"title\": \"Action\", \"description\": \"An enumeration.\", \"enum\": [ \"waiting\", \"cancelled\", \"override\", \"resume\" ], \"type\": \"string\" } } } ``` ### /doors/{door_name}/state ``` { \"title\": \"DoorState\", \"type\": \"object\", \"properties\": { \"door_time\": { \"title\": \"Door Time\", \"default\": { \"sec\": 0, \"nanosec\": 0 }, \"allOf\": [ { \"$ref\": \"#/definitions/Time\" } ] }, \"door_name\": { \"title\": \"Door Name\", \"default\": \"\", \"type\": \"string\" }, \"current_mode\": { \"title\": \"Current Mode\", \"default\": { \"value\": 0 }, \"allOf\": [ { \"$ref\": \"#/definitions/DoorMode\" } ] } }, \"required\": [ \"door_time\", \"door_name\", \"current_mode\" ], \"definitions\": { \"Time\": { \"title\": \"Time\", \"type\": \"object\", \"properties\": { \"sec\": { \"title\": \"Sec\", \"default\": 0, \"minimum\": -2147483648, \"maximum\": 2147483647, \"type\": \"integer\" }, \"nanosec\": { \"title\": \"Nanosec\", \"default\": 0, \"minimum\": 0, \"maximum\": 4294967295, \"type\": \"integer\" } }, \"required\": [ \"sec\", \"nanosec\" ] }, \"DoorMode\": { \"title\": \"DoorMode\", \"type\": \"object\", \"properties\": { \"value\": { \"title\": \"Value\", \"default\": 0, \"minimum\": 0, \"maximum\": 4294967295, \"type\": \"integer\" } }, \"required\": [ \"value\" ] } } } ``` ### /doors/{door_name}/health ``` { \"title\": \"DoorHealth\", \"type\": \"object\", \"properties\": { \"health_status\": { \"title\": \"Health Status\", \"maxLength\": 255, \"nullable\": true, \"type\": \"string\" }, \"health_message\": { \"title\": \"Health Message\", \"nullable\": true, \"type\": \"string\" }, \"id_\": { \"title\": \"Id \", \"maxLength\": 255, \"type\": \"string\" } }, \"required\": [ \"health_status\", \"id_\" ], \"additionalProperties\": false } ``` ### /lifts/{lift_name}/state ``` { \"title\": \"LiftState\", \"type\": \"object\", \"properties\": { \"lift_time\": { \"title\": \"Lift Time\", \"default\": { \"sec\": 0, \"nanosec\": 0 }, \"allOf\": [ { \"$ref\": \"#/definitions/Time\" } ] }, \"lift_name\": { \"title\": \"Lift Name\", \"default\": \"\", \"type\": \"string\" }, \"available_floors\": { \"title\": \"Available Floors\", \"default\": [], \"type\": \"array\", \"items\": { \"type\": \"string\" } }, \"current_floor\": { \"title\": \"Current Floor\", \"default\": \"\", \"type\": \"string\" }, \"destination_floor\": { \"title\": \"Destination Floor\", \"default\": \"\", \"type\": \"string\" }, \"door_state\": { \"title\": \"Door State\", \"default\": 0, \"minimum\": 0, \"maximum\": 255, \"type\": \"integer\" }, \"motion_state\": { \"title\": \"Motion State\", \"default\": 0, \"minimum\": 0, \"maximum\": 255, \"type\": \"integer\" }, \"available_modes\": { \"title\": \"Available Modes\", \"type\": \"array\", \"items\": { \"type\": \"integer\" } }, \"current_mode\": { \"title\": \"Current Mode\", \"default\": 0, \"minimum\": 0, \"maximum\": 255, \"type\": \"integer\" }, \"session_id\": { \"title\": \"Session Id\", \"default\": \"\", \"type\": \"string\" } }, \"required\": [ \"lift_time\", \"lift_name\", \"available_floors\", \"current_floor\", \"destination_floor\", \"door_state\", \"motion_state\", \"available_modes\", \"current_mode\", \"session_id\" ], \"definitions\": { \"Time\": { \"title\": \"Time\", \"type\": \"object\", \"properties\": { \"sec\": { \"title\": \"Sec\", \"default\": 0, \"minimum\": -2147483648, \"maximum\": 2147483647, \"type\": \"integer\" }, \"nanosec\": { \"title\": \"Nanosec\", \"default\": 0, \"minimum\": 0, \"maximum\": 4294967295, \"type\": \"integer\" } }, \"required\": [ \"sec\", \"nanosec\" ] } } } ``` ### /lifts/{lift_name}/health ``` { \"title\": \"LiftHealth\", \"type\": \"object\", \"properties\": { \"health_status\": { \"title\": \"Health Status\", \"maxLength\": 255, \"nullable\": true, \"type\": \"string\" }, \"health_message\": { \"title\": \"Health Message\", \"nullable\": true, \"type\": \"string\" }, \"id_\": { \"title\": \"Id \", \"maxLength\": 255, \"type\": \"string\" } }, \"required\": [ \"health_status\", \"id_\" ], \"additionalProperties\": false } ``` ### /tasks/{task_id}/state ``` { \"title\": \"TaskState\", \"type\": \"object\", \"properties\": { \"booking\": { \"$ref\": \"#/definitions/Booking\" }, \"category\": { \"$ref\": \"#/definitions/Category\" }, \"detail\": { \"$ref\": \"#/definitions/Detail\" }, \"unix_millis_start_time\": { \"title\": \"Unix Millis Start Time\", \"type\": \"integer\" }, \"unix_millis_finish_time\": { \"title\": \"Unix Millis Finish Time\", \"type\": \"integer\" }, \"original_estimate_millis\": { \"$ref\": \"#/definitions/EstimateMillis\" }, \"estimate_millis\": { \"$ref\": \"#/definitions/EstimateMillis\" }, \"assigned_to\": { \"title\": \"Assigned To\", \"description\": \"Which agent (robot) is the task assigned to\", \"allOf\": [ { \"$ref\": \"#/definitions/AssignedTo\" } ] }, \"status\": { \"$ref\": \"#/definitions/Status\" }, \"dispatch\": { \"$ref\": \"#/definitions/Dispatch\" }, \"phases\": { \"title\": \"Phases\", \"description\": \"A dictionary of the states of the phases of the task. The keys (property names) are phase IDs, which are integers.\", \"type\": \"object\", \"additionalProperties\": { \"$ref\": \"#/definitions/Phase\" } }, \"completed\": { \"title\": \"Completed\", \"description\": \"An array of the IDs of completed phases of this task\", \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/Id\" } }, \"active\": { \"title\": \"Active\", \"description\": \"The ID of the active phase for this task\", \"allOf\": [ { \"$ref\": \"#/definitions/Id\" } ] }, \"pending\": { \"title\": \"Pending\", \"description\": \"An array of the pending phases of this task\", \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/Id\" } }, \"interruptions\": { \"title\": \"Interruptions\", \"description\": \"A dictionary of interruptions that have been applied to this task. The keys (property names) are the unique token of the interruption request.\", \"type\": \"object\", \"additionalProperties\": { \"$ref\": \"#/definitions/Interruption\" } }, \"cancellation\": { \"title\": \"Cancellation\", \"description\": \"If the task was cancelled, this will describe information about the request.\", \"allOf\": [ { \"$ref\": \"#/definitions/Cancellation\" } ] }, \"killed\": { \"title\": \"Killed\", \"description\": \"If the task was killed, this will describe information about the request.\", \"allOf\": [ { \"$ref\": \"#/definitions/Killed\" } ] } }, \"required\": [ \"booking\" ], \"definitions\": { \"Booking\": { \"title\": \"Booking\", \"type\": \"object\", \"properties\": { \"id\": { \"title\": \"Id\", \"description\": \"The unique identifier for this task\", \"type\": \"string\" }, \"unix_millis_earliest_start_time\": { \"title\": \"Unix Millis Earliest Start Time\", \"type\": \"integer\" }, \"unix_millis_request_time\": { \"title\": \"Unix Millis Request Time\", \"type\": \"integer\" }, \"priority\": { \"title\": \"Priority\", \"description\": \"Priority information about this task\", \"anyOf\": [ { \"type\": \"object\" }, { \"type\": \"string\" } ] }, \"labels\": { \"title\": \"Labels\", \"description\": \"Information about how and why this task was booked\", \"type\": \"array\", \"items\": { \"type\": \"string\" } }, \"requester\": { \"title\": \"Requester\", \"description\": \"(Optional) An identifier for the entity that requested this task\", \"type\": \"string\" } }, \"required\": [ \"id\" ] }, \"Category\": { \"title\": \"Category\", \"description\": \"The category of this task or phase\", \"type\": \"string\" }, \"Detail\": { \"title\": \"Detail\", \"description\": \"Detailed information about a task, phase, or event\", \"anyOf\": [ { \"type\": \"object\" }, { \"type\": \"array\", \"items\": {} }, { \"type\": \"string\" } ] }, \"EstimateMillis\": { \"title\": \"EstimateMillis\", \"description\": \"An estimate, in milliseconds, of how long the subject will take to complete\", \"minimum\": 0, \"type\": \"integer\" }, \"AssignedTo\": { \"title\": \"AssignedTo\", \"type\": \"object\", \"properties\": { \"group\": { \"title\": \"Group\", \"type\": \"string\" }, \"name\": { \"title\": \"Name\", \"type\": \"string\" } }, \"required\": [ \"group\", \"name\" ] }, \"Status\": { \"title\": \"Status\", \"description\": \"An enumeration.\", \"enum\": [ \"uninitialized\", \"blocked\", \"error\", \"failed\", \"queued\", \"standby\", \"underway\", \"delayed\", \"skipped\", \"canceled\", \"killed\", \"completed\" ] }, \"Status1\": { \"title\": \"Status1\", \"description\": \"An enumeration.\", \"enum\": [ \"queued\", \"selected\", \"dispatched\", \"failed_to_assign\", \"canceled_in_flight\" ] }, \"Assignment\": { \"title\": \"Assignment\", \"type\": \"object\", \"properties\": { \"fleet_name\": { \"title\": \"Fleet Name\", \"type\": \"string\" }, \"expected_robot_name\": { \"title\": \"Expected Robot Name\", \"type\": \"string\" } } }, \"Error\": { \"title\": \"Error\", \"type\": \"object\", \"properties\": { \"code\": { \"title\": \"Code\", \"description\": \"A standard code for the kind of error that has occurred\", \"minimum\": 0, \"type\": \"integer\" }, \"category\": { \"title\": \"Category\", \"description\": \"The category of the error\", \"type\": \"string\" }, \"detail\": { \"title\": \"Detail\", \"description\": \"Details about the error\", \"type\": \"string\" } } }, \"Dispatch\": { \"title\": \"Dispatch\", \"type\": \"object\", \"properties\": { \"status\": { \"$ref\": \"#/definitions/Status1\" }, \"assignment\": { \"$ref\": \"#/definitions/Assignment\" }, \"errors\": { \"title\": \"Errors\", \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/Error\" } } }, \"required\": [ \"status\" ] }, \"Id\": { \"title\": \"Id\", \"minimum\": 0, \"type\": \"integer\" }, \"EventState\": { \"title\": \"EventState\", \"type\": \"object\", \"properties\": { \"id\": { \"$ref\": \"#/definitions/Id\" }, \"status\": { \"$ref\": \"#/definitions/Status\" }, \"name\": { \"title\": \"Name\", \"description\": \"The brief name of the event\", \"type\": \"string\" }, \"detail\": { \"title\": \"Detail\", \"description\": \"Detailed information about the event\", \"allOf\": [ { \"$ref\": \"#/definitions/Detail\" } ] }, \"deps\": { \"title\": \"Deps\", \"description\": \"This event may depend on other events. This array contains the IDs of those other event dependencies.\", \"type\": \"array\", \"items\": { \"type\": \"integer\", \"minimum\": 0 } } }, \"required\": [ \"id\" ] }, \"Undo\": { \"title\": \"Undo\", \"type\": \"object\", \"properties\": { \"unix_millis_request_time\": { \"title\": \"Unix Millis Request Time\", \"description\": \"The time that the undo skip request arrived\", \"type\": \"integer\" }, \"labels\": { \"title\": \"Labels\", \"description\": \"Labels to describe the undo skip request\", \"type\": \"array\", \"items\": { \"type\": \"string\" } } }, \"required\": [ \"unix_millis_request_time\", \"labels\" ] }, \"SkipPhaseRequest\": { \"title\": \"SkipPhaseRequest\", \"type\": \"object\", \"properties\": { \"unix_millis_request_time\": { \"title\": \"Unix Millis Request Time\", \"description\": \"The time that the skip request arrived\", \"type\": \"integer\" }, \"labels\": { \"title\": \"Labels\", \"description\": \"Labels to describe the purpose of the skip request\", \"type\": \"array\", \"items\": { \"type\": \"string\" } }, \"undo\": { \"title\": \"Undo\", \"description\": \"Information about an undo skip request that applied to this request\", \"allOf\": [ { \"$ref\": \"#/definitions/Undo\" } ] } }, \"required\": [ \"unix_millis_request_time\", \"labels\" ] }, \"Phase\": { \"title\": \"Phase\", \"type\": \"object\", \"properties\": { \"id\": { \"$ref\": \"#/definitions/Id\" }, \"category\": { \"$ref\": \"#/definitions/Category\" }, \"detail\": { \"$ref\": \"#/definitions/Detail\" }, \"unix_millis_start_time\": { \"title\": \"Unix Millis Start Time\", \"type\": \"integer\" }, \"unix_millis_finish_time\": { \"title\": \"Unix Millis Finish Time\", \"type\": \"integer\" }, \"original_estimate_millis\": { \"$ref\": \"#/definitions/EstimateMillis\" }, \"estimate_millis\": { \"$ref\": \"#/definitions/EstimateMillis\" }, \"final_event_id\": { \"$ref\": \"#/definitions/Id\" }, \"events\": { \"title\": \"Events\", \"description\": \"A dictionary of events for this phase. The keys (property names) are the event IDs, which are integers.\", \"type\": \"object\", \"additionalProperties\": { \"$ref\": \"#/definitions/EventState\" } }, \"skip_requests\": { \"title\": \"Skip Requests\", \"description\": \"Information about any skip requests that have been received\", \"type\": \"object\", \"additionalProperties\": { \"$ref\": \"#/definitions/SkipPhaseRequest\" } } }, \"required\": [ \"id\" ] }, \"ResumedBy\": { \"title\": \"ResumedBy\", \"type\": \"object\", \"properties\": { \"unix_millis_request_time\": { \"title\": \"Unix Millis Request Time\", \"description\": \"The time that the resume request arrived\", \"type\": \"integer\" }, \"labels\": { \"title\": \"Labels\", \"description\": \"Labels to describe the resume request\", \"type\": \"array\", \"items\": { \"type\": \"string\" } } }, \"required\": [ \"labels\" ] }, \"Interruption\": { \"title\": \"Interruption\", \"type\": \"object\", \"properties\": { \"unix_millis_request_time\": { \"title\": \"Unix Millis Request Time\", \"description\": \"The time that the interruption request arrived\", \"type\": \"integer\" }, \"labels\": { \"title\": \"Labels\", \"description\": \"Labels to describe the purpose of the interruption\", \"type\": \"array\", \"items\": { \"type\": \"string\" } }, \"resumed_by\": { \"title\": \"Resumed By\", \"description\": \"Information about the resume request that ended this interruption. This field will be missing if the interruption is still active.\", \"allOf\": [ { \"$ref\": \"#/definitions/ResumedBy\" } ] } }, \"required\": [ \"unix_millis_request_time\", \"labels\" ] }, \"Cancellation\": { \"title\": \"Cancellation\", \"type\": \"object\", \"properties\": { \"unix_millis_request_time\": { \"title\": \"Unix Millis Request Time\", \"description\": \"The time that the cancellation request arrived\", \"type\": \"integer\" }, \"labels\": { \"title\": \"Labels\", \"description\": \"Labels to describe the cancel request\", \"type\": \"array\", \"items\": { \"type\": \"string\" } } }, \"required\": [ \"unix_millis_request_time\", \"labels\" ] }, \"Killed\": { \"title\": \"Killed\", \"type\": \"object\", \"properties\": { \"unix_millis_request_time\": { \"title\": \"Unix Millis Request Time\", \"description\": \"The time that the cancellation request arrived\", \"type\": \"integer\" }, \"labels\": { \"title\": \"Labels\", \"description\": \"Labels to describe the kill request\", \"type\": \"array\", \"items\": { \"type\": \"string\" } } }, \"required\": [ \"unix_millis_request_time\", \"labels\" ] } } } ``` ### /tasks/{task_id}/log ``` { \"title\": \"TaskEventLog\", \"type\": \"object\", \"properties\": { \"task_id\": { \"title\": \"Task Id\", \"type\": \"string\" }, \"log\": { \"title\": \"Log\", \"description\": \"Log entries related to the overall task\", \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/LogEntry\" } }, \"phases\": { \"title\": \"Phases\", \"description\": \"A dictionary whose keys (property names) are the indices of a phase\", \"type\": \"object\", \"additionalProperties\": { \"$ref\": \"#/definitions/Phases\" } } }, \"required\": [ \"task_id\" ], \"additionalProperties\": false, \"definitions\": { \"Tier\": { \"title\": \"Tier\", \"description\": \"An enumeration.\", \"enum\": [ \"uninitialized\", \"info\", \"warning\", \"error\" ] }, \"LogEntry\": { \"title\": \"LogEntry\", \"type\": \"object\", \"properties\": { \"seq\": { \"title\": \"Seq\", \"description\": \"Sequence number for this entry. Each entry has a unique sequence number which monotonically increase, until integer overflow causes a wrap around.\", \"exclusiveMaximum\": 4294967296, \"minimum\": 0, \"type\": \"integer\" }, \"tier\": { \"description\": \"The importance level of the log entry\", \"allOf\": [ { \"$ref\": \"#/definitions/Tier\" } ] }, \"unix_millis_time\": { \"title\": \"Unix Millis Time\", \"type\": \"integer\" }, \"text\": { \"title\": \"Text\", \"description\": \"The text of the log entry\", \"type\": \"string\" } }, \"required\": [ \"seq\", \"tier\", \"unix_millis_time\", \"text\" ] }, \"Phases\": { \"title\": \"Phases\", \"type\": \"object\", \"properties\": { \"log\": { \"title\": \"Log\", \"description\": \"Log entries related to the overall phase\", \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/LogEntry\" } }, \"events\": { \"title\": \"Events\", \"description\": \"A dictionary whose keys (property names) are the indices of an event in the phase\", \"type\": \"object\", \"additionalProperties\": { \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/LogEntry\" } } } }, \"additionalProperties\": false } } } ``` ### /dispensers/{guid}/state ``` { \"title\": \"DispenserState\", \"type\": \"object\", \"properties\": { \"time\": { \"title\": \"Time\", \"default\": { \"sec\": 0, \"nanosec\": 0 }, \"allOf\": [ { \"$ref\": \"#/definitions/Time\" } ] }, \"guid\": { \"title\": \"Guid\", \"default\": \"\", \"type\": \"string\" }, \"mode\": { \"title\": \"Mode\", \"default\": 0, \"minimum\": -2147483648, \"maximum\": 2147483647, \"type\": \"integer\" }, \"request_guid_queue\": { \"title\": \"Request Guid Queue\", \"default\": [], \"type\": \"array\", \"items\": { \"type\": \"string\" } }, \"seconds_remaining\": { \"title\": \"Seconds Remaining\", \"default\": 0, \"type\": \"number\" } }, \"required\": [ \"time\", \"guid\", \"mode\", \"request_guid_queue\", \"seconds_remaining\" ], \"definitions\": { \"Time\": { \"title\": \"Time\", \"type\": \"object\", \"properties\": { \"sec\": { \"title\": \"Sec\", \"default\": 0, \"minimum\": -2147483648, \"maximum\": 2147483647, \"type\": \"integer\" }, \"nanosec\": { \"title\": \"Nanosec\", \"default\": 0, \"minimum\": 0, \"maximum\": 4294967295, \"type\": \"integer\" } }, \"required\": [ \"sec\", \"nanosec\" ] } } } ``` ### /dispensers/{guid}/health ``` { \"title\": \"DispenserHealth\", \"type\": \"object\", \"properties\": { \"health_status\": { \"title\": \"Health Status\", \"maxLength\": 255, \"nullable\": true, \"type\": \"string\" }, \"health_message\": { \"title\": \"Health Message\", \"nullable\": true, \"type\": \"string\" }, \"id_\": { \"title\": \"Id \", \"maxLength\": 255, \"type\": \"string\" } }, \"required\": [ \"health_status\", \"id_\" ], \"additionalProperties\": false } ``` ### /ingestors/{guid}/state ``` { \"title\": \"IngestorState\", \"type\": \"object\", \"properties\": { \"time\": { \"title\": \"Time\", \"default\": { \"sec\": 0, \"nanosec\": 0 }, \"allOf\": [ { \"$ref\": \"#/definitions/Time\" } ] }, \"guid\": { \"title\": \"Guid\", \"default\": \"\", \"type\": \"string\" }, \"mode\": { \"title\": \"Mode\", \"default\": 0, \"minimum\": -2147483648, \"maximum\": 2147483647, \"type\": \"integer\" }, \"request_guid_queue\": { \"title\": \"Request Guid Queue\", \"default\": [], \"type\": \"array\", \"items\": { \"type\": \"string\" } }, \"seconds_remaining\": { \"title\": \"Seconds Remaining\", \"default\": 0, \"type\": \"number\" } }, \"required\": [ \"time\", \"guid\", \"mode\", \"request_guid_queue\", \"seconds_remaining\" ], \"definitions\": { \"Time\": { \"title\": \"Time\", \"type\": \"object\", \"properties\": { \"sec\": { \"title\": \"Sec\", \"default\": 0, \"minimum\": -2147483648, \"maximum\": 2147483647, \"type\": \"integer\" }, \"nanosec\": { \"title\": \"Nanosec\", \"default\": 0, \"minimum\": 0, \"maximum\": 4294967295, \"type\": \"integer\" } }, \"required\": [ \"sec\", \"nanosec\" ] } } } ``` ### /ingestors/{guid}/health ``` { \"title\": \"IngestorHealth\", \"type\": \"object\", \"properties\": { \"health_status\": { \"title\": \"Health Status\", \"maxLength\": 255, \"nullable\": true, \"type\": \"string\" }, \"health_message\": { \"title\": \"Health Message\", \"nullable\": true, \"type\": \"string\" }, \"id_\": { \"title\": \"Id \", \"maxLength\": 255, \"type\": \"string\" } }, \"required\": [ \"health_status\", \"id_\" ], \"additionalProperties\": false } ``` ### /fleets/{name}/state ``` { \"title\": \"FleetState\", \"type\": \"object\", \"properties\": { \"name\": { \"title\": \"Name\", \"type\": \"string\" }, \"robots\": { \"title\": \"Robots\", \"description\": \"A dictionary of the states of the robots that belong to this fleet\", \"type\": \"object\", \"additionalProperties\": { \"$ref\": \"#/definitions/RobotState\" } } }, \"definitions\": { \"Status\": { \"title\": \"Status\", \"description\": \"An enumeration.\", \"enum\": [ \"uninitialized\", \"offline\", \"shutdown\", \"idle\", \"charging\", \"working\", \"error\" ] }, \"Location2D\": { \"title\": \"Location2D\", \"type\": \"object\", \"properties\": { \"map\": { \"title\": \"Map\", \"type\": \"string\" }, \"x\": { \"title\": \"X\", \"type\": \"number\" }, \"y\": { \"title\": \"Y\", \"type\": \"number\" }, \"yaw\": { \"title\": \"Yaw\", \"type\": \"number\" } }, \"required\": [ \"map\", \"x\", \"y\", \"yaw\" ] }, \"Issue\": { \"title\": \"Issue\", \"type\": \"object\", \"properties\": { \"category\": { \"title\": \"Category\", \"description\": \"Category of the robot\'s issue\", \"type\": \"string\" }, \"detail\": { \"title\": \"Detail\", \"description\": \"Detailed information about the issue\", \"anyOf\": [ { \"type\": \"object\" }, { \"type\": \"array\", \"items\": {} }, { \"type\": \"string\" } ] } } }, \"Commission\": { \"title\": \"Commission\", \"type\": \"object\", \"properties\": { \"dispatch_tasks\": { \"title\": \"Dispatch Tasks\", \"description\": \"Should the robot accept dispatched tasks, true/false. When used in a request, leave this unset to not change the robot\'s current value.\", \"type\": \"boolean\" }, \"direct_tasks\": { \"title\": \"Direct Tasks\", \"description\": \"Should the robot accept direct task requests, true/false. When used in a request, leave this unset to not change the robot\'s current value.\", \"type\": \"boolean\" }, \"idle_behavior\": { \"title\": \"Idle Behavior\", \"description\": \"Should the robot perform its idle behavior, true/false. When used in a request, leave this unset to not change the robot\'s current value.\", \"type\": \"boolean\" } } }, \"MutexGroups\": { \"title\": \"MutexGroups\", \"type\": \"object\", \"properties\": { \"locked\": { \"title\": \"Locked\", \"description\": \"A list of mutex groups that this robot has currently locked\", \"type\": \"array\", \"items\": { \"type\": \"string\" } }, \"requesting\": { \"title\": \"Requesting\", \"description\": \"A list of the mutex groups that this robot is currently requesting but has not lockd yet\", \"type\": \"array\", \"items\": { \"type\": \"string\" } } } }, \"RobotState\": { \"title\": \"RobotState\", \"type\": \"object\", \"properties\": { \"name\": { \"title\": \"Name\", \"type\": \"string\" }, \"status\": { \"description\": \"A simple token representing the status of the robot\", \"allOf\": [ { \"$ref\": \"#/definitions/Status\" } ] }, \"task_id\": { \"title\": \"Task Id\", \"description\": \"The ID of the task this robot is currently working on. Empty string if the robot is not working on a task.\", \"type\": \"string\" }, \"unix_millis_time\": { \"title\": \"Unix Millis Time\", \"type\": \"integer\" }, \"location\": { \"$ref\": \"#/definitions/Location2D\" }, \"battery\": { \"title\": \"Battery\", \"description\": \"State of charge of the battery. Values range from 0.0 (depleted) to 1.0 (fully charged)\", \"minimum\": 0.0, \"maximum\": 1.0, \"type\": \"number\" }, \"issues\": { \"title\": \"Issues\", \"description\": \"A list of issues with the robot that operators need to address\", \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/Issue\" } }, \"commission\": { \"$ref\": \"#/definitions/Commission\" }, \"mutex_groups\": { \"title\": \"Mutex Groups\", \"description\": \"Information about the mutex groups that this robot is interacting with\", \"allOf\": [ { \"$ref\": \"#/definitions/MutexGroups\" } ] } } } } } ``` ### /fleets/{name}/log ``` { \"title\": \"FleetLog\", \"type\": \"object\", \"properties\": { \"name\": { \"title\": \"Name\", \"type\": \"string\" }, \"log\": { \"title\": \"Log\", \"description\": \"Log for the overall fleet\", \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/LogEntry\" } }, \"robots\": { \"title\": \"Robots\", \"description\": \"Dictionary of logs for the individual robots. The keys (property names) are the robot names.\", \"type\": \"object\", \"additionalProperties\": { \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/LogEntry\" } } } }, \"definitions\": { \"Tier\": { \"title\": \"Tier\", \"description\": \"An enumeration.\", \"enum\": [ \"uninitialized\", \"info\", \"warning\", \"error\" ] }, \"LogEntry\": { \"title\": \"LogEntry\", \"type\": \"object\", \"properties\": { \"seq\": { \"title\": \"Seq\", \"description\": \"Sequence number for this entry. Each entry has a unique sequence number which monotonically increase, until integer overflow causes a wrap around.\", \"exclusiveMaximum\": 4294967296, \"minimum\": 0, \"type\": \"integer\" }, \"tier\": { \"description\": \"The importance level of the log entry\", \"allOf\": [ { \"$ref\": \"#/definitions/Tier\" } ] }, \"unix_millis_time\": { \"title\": \"Unix Millis Time\", \"type\": \"integer\" }, \"text\": { \"title\": \"Text\", \"description\": \"The text of the log entry\", \"type\": \"string\" } }, \"required\": [ \"seq\", \"tier\", \"unix_millis_time\", \"text\" ] } } } ``` * @summary Socket.io endpoint * @param {*} [options] Override http request option. * @throws {RequiredError} @@ -6050,7 +6038,7 @@ export const DefaultApiFactory = function ( return localVarFp.getUserUserGet(options).then((request) => request(axios, basePath)); }, /** - * # NOTE: This endpoint is here for documentation purposes only, this is _not_ a REST endpoint. ## About This exposes a minimal pubsub system built on top of socket.io. It works similar to a normal socket.io endpoint, except that are 2 special rooms which control subscriptions. ## Rooms ### subscribe Clients must send a message to this room to start receiving messages on other rooms. The message must be of the form: ``` { \"room\": \"\" } ``` ### unsubscribe Clients can send a message to this room to stop receiving messages on other rooms. The message must be of the form: ``` { \"room\": \"\" } ``` ### /alerts ``` { \"title\": \"Alert\", \"description\": \"General alert that can be triggered by events.\", \"type\": \"object\", \"properties\": { \"id\": { \"title\": \"Id\", \"maxLength\": 255, \"type\": \"string\" }, \"original_id\": { \"title\": \"Original Id\", \"maxLength\": 255, \"type\": \"string\" }, \"category\": { \"title\": \"Category\", \"description\": \"Default: default
Task: task
Fleet: fleet
Robot: robot\", \"maxLength\": 7, \"type\": \"string\" }, \"unix_millis_created_time\": { \"title\": \"Unix Millis Created Time\", \"minimum\": -9223372036854775808, \"maximum\": 9223372036854775807, \"type\": \"integer\" }, \"acknowledged_by\": { \"title\": \"Acknowledged By\", \"maxLength\": 255, \"nullable\": true, \"type\": \"string\" }, \"unix_millis_acknowledged_time\": { \"title\": \"Unix Millis Acknowledged Time\", \"minimum\": -9223372036854775808, \"maximum\": 9223372036854775807, \"nullable\": true, \"type\": \"integer\" } }, \"required\": [ \"id\", \"original_id\", \"category\", \"unix_millis_created_time\" ], \"additionalProperties\": false } ``` ### /beacons ``` { \"title\": \"BeaconState\", \"type\": \"object\", \"properties\": { \"id\": { \"title\": \"Id\", \"maxLength\": 255, \"type\": \"string\" }, \"online\": { \"title\": \"Online\", \"type\": \"boolean\" }, \"category\": { \"title\": \"Category\", \"maxLength\": 255, \"nullable\": true, \"type\": \"string\" }, \"activated\": { \"title\": \"Activated\", \"type\": \"boolean\" }, \"level\": { \"title\": \"Level\", \"maxLength\": 255, \"nullable\": true, \"type\": \"string\" } }, \"required\": [ \"id\", \"online\", \"activated\" ], \"additionalProperties\": false } ``` ### /building_map ``` { \"title\": \"BuildingMap\", \"type\": \"object\", \"properties\": { \"name\": { \"title\": \"Name\", \"default\": \"\", \"type\": \"string\" }, \"levels\": { \"title\": \"Levels\", \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/Level\" } }, \"lifts\": { \"title\": \"Lifts\", \"default\": [], \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/Lift\" } } }, \"required\": [ \"name\", \"levels\", \"lifts\" ], \"definitions\": { \"AffineImage\": { \"title\": \"AffineImage\", \"type\": \"object\", \"properties\": { \"name\": { \"title\": \"Name\", \"default\": \"\", \"type\": \"string\" }, \"x_offset\": { \"title\": \"X Offset\", \"default\": 0, \"type\": \"number\" }, \"y_offset\": { \"title\": \"Y Offset\", \"default\": 0, \"type\": \"number\" }, \"yaw\": { \"title\": \"Yaw\", \"default\": 0, \"type\": \"number\" }, \"scale\": { \"title\": \"Scale\", \"default\": 0, \"type\": \"number\" }, \"encoding\": { \"title\": \"Encoding\", \"default\": \"\", \"type\": \"string\" }, \"data\": { \"title\": \"Data\", \"type\": \"string\" } }, \"required\": [ \"name\", \"x_offset\", \"y_offset\", \"yaw\", \"scale\", \"encoding\", \"data\" ] }, \"Place\": { \"title\": \"Place\", \"type\": \"object\", \"properties\": { \"name\": { \"title\": \"Name\", \"default\": \"\", \"type\": \"string\" }, \"x\": { \"title\": \"X\", \"default\": 0, \"type\": \"number\" }, \"y\": { \"title\": \"Y\", \"default\": 0, \"type\": \"number\" }, \"yaw\": { \"title\": \"Yaw\", \"default\": 0, \"type\": \"number\" }, \"position_tolerance\": { \"title\": \"Position Tolerance\", \"default\": 0, \"type\": \"number\" }, \"yaw_tolerance\": { \"title\": \"Yaw Tolerance\", \"default\": 0, \"type\": \"number\" } }, \"required\": [ \"name\", \"x\", \"y\", \"yaw\", \"position_tolerance\", \"yaw_tolerance\" ] }, \"Door\": { \"title\": \"Door\", \"type\": \"object\", \"properties\": { \"name\": { \"title\": \"Name\", \"default\": \"\", \"type\": \"string\" }, \"v1_x\": { \"title\": \"V1 X\", \"default\": 0, \"type\": \"number\" }, \"v1_y\": { \"title\": \"V1 Y\", \"default\": 0, \"type\": \"number\" }, \"v2_x\": { \"title\": \"V2 X\", \"default\": 0, \"type\": \"number\" }, \"v2_y\": { \"title\": \"V2 Y\", \"default\": 0, \"type\": \"number\" }, \"door_type\": { \"title\": \"Door Type\", \"default\": 0, \"minimum\": 0, \"maximum\": 255, \"type\": \"integer\" }, \"motion_range\": { \"title\": \"Motion Range\", \"default\": 0, \"type\": \"number\" }, \"motion_direction\": { \"title\": \"Motion Direction\", \"default\": 0, \"minimum\": -2147483648, \"maximum\": 2147483647, \"type\": \"integer\" } }, \"required\": [ \"name\", \"v1_x\", \"v1_y\", \"v2_x\", \"v2_y\", \"door_type\", \"motion_range\", \"motion_direction\" ] }, \"Param\": { \"title\": \"Param\", \"type\": \"object\", \"properties\": { \"name\": { \"title\": \"Name\", \"default\": \"\", \"type\": \"string\" }, \"type\": { \"title\": \"Type\", \"default\": 0, \"minimum\": 0, \"maximum\": 4294967295, \"type\": \"integer\" }, \"value_int\": { \"title\": \"Value Int\", \"default\": 0, \"minimum\": -2147483648, \"maximum\": 2147483647, \"type\": \"integer\" }, \"value_float\": { \"title\": \"Value Float\", \"default\": 0, \"type\": \"number\" }, \"value_string\": { \"title\": \"Value String\", \"default\": \"\", \"type\": \"string\" }, \"value_bool\": { \"title\": \"Value Bool\", \"default\": false, \"type\": \"boolean\" } }, \"required\": [ \"name\", \"type\", \"value_int\", \"value_float\", \"value_string\", \"value_bool\" ] }, \"GraphNode\": { \"title\": \"GraphNode\", \"type\": \"object\", \"properties\": { \"x\": { \"title\": \"X\", \"default\": 0, \"type\": \"number\" }, \"y\": { \"title\": \"Y\", \"default\": 0, \"type\": \"number\" }, \"name\": { \"title\": \"Name\", \"default\": \"\", \"type\": \"string\" }, \"params\": { \"title\": \"Params\", \"default\": [], \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/Param\" } } }, \"required\": [ \"x\", \"y\", \"name\", \"params\" ] }, \"GraphEdge\": { \"title\": \"GraphEdge\", \"type\": \"object\", \"properties\": { \"v1_idx\": { \"title\": \"V1 Idx\", \"default\": 0, \"minimum\": 0, \"maximum\": 4294967295, \"type\": \"integer\" }, \"v2_idx\": { \"title\": \"V2 Idx\", \"default\": 0, \"minimum\": 0, \"maximum\": 4294967295, \"type\": \"integer\" }, \"params\": { \"title\": \"Params\", \"default\": [], \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/Param\" } }, \"edge_type\": { \"title\": \"Edge Type\", \"default\": 0, \"minimum\": 0, \"maximum\": 255, \"type\": \"integer\" } }, \"required\": [ \"v1_idx\", \"v2_idx\", \"params\", \"edge_type\" ] }, \"Graph\": { \"title\": \"Graph\", \"type\": \"object\", \"properties\": { \"name\": { \"title\": \"Name\", \"default\": \"\", \"type\": \"string\" }, \"vertices\": { \"title\": \"Vertices\", \"default\": [], \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/GraphNode\" } }, \"edges\": { \"title\": \"Edges\", \"default\": [], \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/GraphEdge\" } }, \"params\": { \"title\": \"Params\", \"default\": [], \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/Param\" } } }, \"required\": [ \"name\", \"vertices\", \"edges\", \"params\" ] }, \"Level\": { \"title\": \"Level\", \"type\": \"object\", \"properties\": { \"name\": { \"title\": \"Name\", \"default\": \"\", \"type\": \"string\" }, \"elevation\": { \"title\": \"Elevation\", \"default\": 0, \"type\": \"number\" }, \"images\": { \"title\": \"Images\", \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/AffineImage\" } }, \"places\": { \"title\": \"Places\", \"default\": [], \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/Place\" } }, \"doors\": { \"title\": \"Doors\", \"default\": [], \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/Door\" } }, \"nav_graphs\": { \"title\": \"Nav Graphs\", \"default\": [], \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/Graph\" } }, \"wall_graph\": { \"title\": \"Wall Graph\", \"default\": { \"name\": \"\", \"vertices\": [], \"edges\": [], \"params\": [] }, \"allOf\": [ { \"$ref\": \"#/definitions/Graph\" } ] } }, \"required\": [ \"name\", \"elevation\", \"images\", \"places\", \"doors\", \"nav_graphs\", \"wall_graph\" ] }, \"Lift\": { \"title\": \"Lift\", \"type\": \"object\", \"properties\": { \"name\": { \"title\": \"Name\", \"default\": \"\", \"type\": \"string\" }, \"levels\": { \"title\": \"Levels\", \"default\": [], \"type\": \"array\", \"items\": { \"type\": \"string\" } }, \"doors\": { \"title\": \"Doors\", \"default\": [], \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/Door\" } }, \"wall_graph\": { \"title\": \"Wall Graph\", \"default\": { \"name\": \"\", \"vertices\": [], \"edges\": [], \"params\": [] }, \"allOf\": [ { \"$ref\": \"#/definitions/Graph\" } ] }, \"ref_x\": { \"title\": \"Ref X\", \"default\": 0, \"type\": \"number\" }, \"ref_y\": { \"title\": \"Ref Y\", \"default\": 0, \"type\": \"number\" }, \"ref_yaw\": { \"title\": \"Ref Yaw\", \"default\": 0, \"type\": \"number\" }, \"width\": { \"title\": \"Width\", \"default\": 0, \"type\": \"number\" }, \"depth\": { \"title\": \"Depth\", \"default\": 0, \"type\": \"number\" } }, \"required\": [ \"name\", \"levels\", \"doors\", \"wall_graph\", \"ref_x\", \"ref_y\", \"ref_yaw\", \"width\", \"depth\" ] } } } ``` ### /building_map/fire_alarm_trigger ``` { \"title\": \"FireAlarmTriggerState\", \"type\": \"object\", \"properties\": { \"unix_millis_time\": { \"title\": \"Unix Millis Time\", \"type\": \"integer\" }, \"trigger\": { \"title\": \"Trigger\", \"type\": \"boolean\" } }, \"required\": [ \"unix_millis_time\", \"trigger\" ] } ``` ### /delivery_alerts ``` { \"title\": \"DeliveryAlert\", \"type\": \"object\", \"properties\": { \"id\": { \"title\": \"Id\", \"type\": \"string\" }, \"category\": { \"$ref\": \"#/definitions/Category\" }, \"tier\": { \"$ref\": \"#/definitions/Tier\" }, \"action\": { \"$ref\": \"#/definitions/Action\" }, \"task_id\": { \"title\": \"Task Id\", \"type\": \"string\" }, \"message\": { \"title\": \"Message\", \"type\": \"string\" } }, \"required\": [ \"id\", \"category\", \"tier\", \"action\", \"task_id\", \"message\" ], \"definitions\": { \"Category\": { \"title\": \"Category\", \"description\": \"An enumeration.\", \"enum\": [ \"missing\", \"wrong\", \"obstructed\", \"cancelled\" ], \"type\": \"string\" }, \"Tier\": { \"title\": \"Tier\", \"description\": \"An enumeration.\", \"enum\": [ \"warning\", \"error\" ], \"type\": \"string\" }, \"Action\": { \"title\": \"Action\", \"description\": \"An enumeration.\", \"enum\": [ \"waiting\", \"cancelled\", \"override\", \"resume\" ], \"type\": \"string\" } } } ``` ### /doors/{door_name}/state ``` { \"title\": \"DoorState\", \"type\": \"object\", \"properties\": { \"door_time\": { \"title\": \"Door Time\", \"default\": { \"sec\": 0, \"nanosec\": 0 }, \"allOf\": [ { \"$ref\": \"#/definitions/Time\" } ] }, \"door_name\": { \"title\": \"Door Name\", \"default\": \"\", \"type\": \"string\" }, \"current_mode\": { \"title\": \"Current Mode\", \"default\": { \"value\": 0 }, \"allOf\": [ { \"$ref\": \"#/definitions/DoorMode\" } ] } }, \"required\": [ \"door_time\", \"door_name\", \"current_mode\" ], \"definitions\": { \"Time\": { \"title\": \"Time\", \"type\": \"object\", \"properties\": { \"sec\": { \"title\": \"Sec\", \"default\": 0, \"minimum\": -2147483648, \"maximum\": 2147483647, \"type\": \"integer\" }, \"nanosec\": { \"title\": \"Nanosec\", \"default\": 0, \"minimum\": 0, \"maximum\": 4294967295, \"type\": \"integer\" } }, \"required\": [ \"sec\", \"nanosec\" ] }, \"DoorMode\": { \"title\": \"DoorMode\", \"type\": \"object\", \"properties\": { \"value\": { \"title\": \"Value\", \"default\": 0, \"minimum\": 0, \"maximum\": 4294967295, \"type\": \"integer\" } }, \"required\": [ \"value\" ] } } } ``` ### /doors/{door_name}/health ``` { \"title\": \"DoorHealth\", \"type\": \"object\", \"properties\": { \"health_status\": { \"title\": \"Health Status\", \"maxLength\": 255, \"nullable\": true, \"type\": \"string\" }, \"health_message\": { \"title\": \"Health Message\", \"nullable\": true, \"type\": \"string\" }, \"id_\": { \"title\": \"Id \", \"maxLength\": 255, \"type\": \"string\" } }, \"required\": [ \"health_status\", \"id_\" ], \"additionalProperties\": false } ``` ### /lifts/{lift_name}/state ``` { \"title\": \"LiftState\", \"type\": \"object\", \"properties\": { \"lift_time\": { \"title\": \"Lift Time\", \"default\": { \"sec\": 0, \"nanosec\": 0 }, \"allOf\": [ { \"$ref\": \"#/definitions/Time\" } ] }, \"lift_name\": { \"title\": \"Lift Name\", \"default\": \"\", \"type\": \"string\" }, \"available_floors\": { \"title\": \"Available Floors\", \"default\": [], \"type\": \"array\", \"items\": { \"type\": \"string\" } }, \"current_floor\": { \"title\": \"Current Floor\", \"default\": \"\", \"type\": \"string\" }, \"destination_floor\": { \"title\": \"Destination Floor\", \"default\": \"\", \"type\": \"string\" }, \"door_state\": { \"title\": \"Door State\", \"default\": 0, \"minimum\": 0, \"maximum\": 255, \"type\": \"integer\" }, \"motion_state\": { \"title\": \"Motion State\", \"default\": 0, \"minimum\": 0, \"maximum\": 255, \"type\": \"integer\" }, \"available_modes\": { \"title\": \"Available Modes\", \"type\": \"array\", \"items\": { \"type\": \"integer\" } }, \"current_mode\": { \"title\": \"Current Mode\", \"default\": 0, \"minimum\": 0, \"maximum\": 255, \"type\": \"integer\" }, \"session_id\": { \"title\": \"Session Id\", \"default\": \"\", \"type\": \"string\" } }, \"required\": [ \"lift_time\", \"lift_name\", \"available_floors\", \"current_floor\", \"destination_floor\", \"door_state\", \"motion_state\", \"available_modes\", \"current_mode\", \"session_id\" ], \"definitions\": { \"Time\": { \"title\": \"Time\", \"type\": \"object\", \"properties\": { \"sec\": { \"title\": \"Sec\", \"default\": 0, \"minimum\": -2147483648, \"maximum\": 2147483647, \"type\": \"integer\" }, \"nanosec\": { \"title\": \"Nanosec\", \"default\": 0, \"minimum\": 0, \"maximum\": 4294967295, \"type\": \"integer\" } }, \"required\": [ \"sec\", \"nanosec\" ] } } } ``` ### /lifts/{lift_name}/health ``` { \"title\": \"LiftHealth\", \"type\": \"object\", \"properties\": { \"health_status\": { \"title\": \"Health Status\", \"maxLength\": 255, \"nullable\": true, \"type\": \"string\" }, \"health_message\": { \"title\": \"Health Message\", \"nullable\": true, \"type\": \"string\" }, \"id_\": { \"title\": \"Id \", \"maxLength\": 255, \"type\": \"string\" } }, \"required\": [ \"health_status\", \"id_\" ], \"additionalProperties\": false } ``` ### /tasks/{task_id}/state ``` { \"title\": \"TaskState\", \"type\": \"object\", \"properties\": { \"booking\": { \"$ref\": \"#/definitions/Booking\" }, \"category\": { \"$ref\": \"#/definitions/Category\" }, \"detail\": { \"$ref\": \"#/definitions/Detail\" }, \"unix_millis_start_time\": { \"title\": \"Unix Millis Start Time\", \"type\": \"integer\" }, \"unix_millis_finish_time\": { \"title\": \"Unix Millis Finish Time\", \"type\": \"integer\" }, \"original_estimate_millis\": { \"$ref\": \"#/definitions/EstimateMillis\" }, \"estimate_millis\": { \"$ref\": \"#/definitions/EstimateMillis\" }, \"assigned_to\": { \"title\": \"Assigned To\", \"description\": \"Which agent (robot) is the task assigned to\", \"allOf\": [ { \"$ref\": \"#/definitions/AssignedTo\" } ] }, \"status\": { \"$ref\": \"#/definitions/Status\" }, \"dispatch\": { \"$ref\": \"#/definitions/Dispatch\" }, \"phases\": { \"title\": \"Phases\", \"description\": \"A dictionary of the states of the phases of the task. The keys (property names) are phase IDs, which are integers.\", \"type\": \"object\", \"additionalProperties\": { \"$ref\": \"#/definitions/Phase\" } }, \"completed\": { \"title\": \"Completed\", \"description\": \"An array of the IDs of completed phases of this task\", \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/Id\" } }, \"active\": { \"title\": \"Active\", \"description\": \"The ID of the active phase for this task\", \"allOf\": [ { \"$ref\": \"#/definitions/Id\" } ] }, \"pending\": { \"title\": \"Pending\", \"description\": \"An array of the pending phases of this task\", \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/Id\" } }, \"interruptions\": { \"title\": \"Interruptions\", \"description\": \"A dictionary of interruptions that have been applied to this task. The keys (property names) are the unique token of the interruption request.\", \"type\": \"object\", \"additionalProperties\": { \"$ref\": \"#/definitions/Interruption\" } }, \"cancellation\": { \"title\": \"Cancellation\", \"description\": \"If the task was cancelled, this will describe information about the request.\", \"allOf\": [ { \"$ref\": \"#/definitions/Cancellation\" } ] }, \"killed\": { \"title\": \"Killed\", \"description\": \"If the task was killed, this will describe information about the request.\", \"allOf\": [ { \"$ref\": \"#/definitions/Killed\" } ] }, \"unix_millis_warn_time\": { \"title\": \"Unix Millis Warn Time\", \"type\": \"integer\" } }, \"required\": [ \"booking\" ], \"definitions\": { \"Booking\": { \"title\": \"Booking\", \"type\": \"object\", \"properties\": { \"id\": { \"title\": \"Id\", \"description\": \"The unique identifier for this task\", \"type\": \"string\" }, \"unix_millis_earliest_start_time\": { \"title\": \"Unix Millis Earliest Start Time\", \"type\": \"integer\" }, \"unix_millis_request_time\": { \"title\": \"Unix Millis Request Time\", \"type\": \"integer\" }, \"priority\": { \"title\": \"Priority\", \"description\": \"Priority information about this task\", \"anyOf\": [ { \"type\": \"object\" }, { \"type\": \"string\" } ] }, \"labels\": { \"title\": \"Labels\", \"description\": \"Information about how and why this task was booked\", \"type\": \"array\", \"items\": { \"type\": \"string\" } }, \"requester\": { \"title\": \"Requester\", \"description\": \"(Optional) An identifier for the entity that requested this task\", \"type\": \"string\" } }, \"required\": [ \"id\" ] }, \"Category\": { \"title\": \"Category\", \"description\": \"The category of this task or phase\", \"type\": \"string\" }, \"Detail\": { \"title\": \"Detail\", \"description\": \"Detailed information about a task, phase, or event\", \"anyOf\": [ { \"type\": \"object\" }, { \"type\": \"array\", \"items\": {} }, { \"type\": \"string\" } ] }, \"EstimateMillis\": { \"title\": \"EstimateMillis\", \"description\": \"An estimate, in milliseconds, of how long the subject will take to complete\", \"minimum\": 0, \"type\": \"integer\" }, \"AssignedTo\": { \"title\": \"AssignedTo\", \"type\": \"object\", \"properties\": { \"group\": { \"title\": \"Group\", \"type\": \"string\" }, \"name\": { \"title\": \"Name\", \"type\": \"string\" } }, \"required\": [ \"group\", \"name\" ] }, \"Status\": { \"title\": \"Status\", \"description\": \"An enumeration.\", \"enum\": [ \"uninitialized\", \"blocked\", \"error\", \"failed\", \"queued\", \"standby\", \"underway\", \"delayed\", \"skipped\", \"canceled\", \"killed\", \"completed\" ] }, \"Status1\": { \"title\": \"Status1\", \"description\": \"An enumeration.\", \"enum\": [ \"queued\", \"selected\", \"dispatched\", \"failed_to_assign\", \"canceled_in_flight\" ] }, \"Assignment\": { \"title\": \"Assignment\", \"type\": \"object\", \"properties\": { \"fleet_name\": { \"title\": \"Fleet Name\", \"type\": \"string\" }, \"expected_robot_name\": { \"title\": \"Expected Robot Name\", \"type\": \"string\" } } }, \"Error\": { \"title\": \"Error\", \"type\": \"object\", \"properties\": { \"code\": { \"title\": \"Code\", \"description\": \"A standard code for the kind of error that has occurred\", \"minimum\": 0, \"type\": \"integer\" }, \"category\": { \"title\": \"Category\", \"description\": \"The category of the error\", \"type\": \"string\" }, \"detail\": { \"title\": \"Detail\", \"description\": \"Details about the error\", \"type\": \"string\" } } }, \"Dispatch\": { \"title\": \"Dispatch\", \"type\": \"object\", \"properties\": { \"status\": { \"$ref\": \"#/definitions/Status1\" }, \"assignment\": { \"$ref\": \"#/definitions/Assignment\" }, \"errors\": { \"title\": \"Errors\", \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/Error\" } } }, \"required\": [ \"status\" ] }, \"Id\": { \"title\": \"Id\", \"minimum\": 0, \"type\": \"integer\" }, \"EventState\": { \"title\": \"EventState\", \"type\": \"object\", \"properties\": { \"id\": { \"$ref\": \"#/definitions/Id\" }, \"status\": { \"$ref\": \"#/definitions/Status\" }, \"name\": { \"title\": \"Name\", \"description\": \"The brief name of the event\", \"type\": \"string\" }, \"detail\": { \"title\": \"Detail\", \"description\": \"Detailed information about the event\", \"allOf\": [ { \"$ref\": \"#/definitions/Detail\" } ] }, \"deps\": { \"title\": \"Deps\", \"description\": \"This event may depend on other events. This array contains the IDs of those other event dependencies.\", \"type\": \"array\", \"items\": { \"type\": \"integer\", \"minimum\": 0 } } }, \"required\": [ \"id\" ] }, \"Undo\": { \"title\": \"Undo\", \"type\": \"object\", \"properties\": { \"unix_millis_request_time\": { \"title\": \"Unix Millis Request Time\", \"description\": \"The time that the undo skip request arrived\", \"type\": \"integer\" }, \"labels\": { \"title\": \"Labels\", \"description\": \"Labels to describe the undo skip request\", \"type\": \"array\", \"items\": { \"type\": \"string\" } } }, \"required\": [ \"unix_millis_request_time\", \"labels\" ] }, \"SkipPhaseRequest\": { \"title\": \"SkipPhaseRequest\", \"type\": \"object\", \"properties\": { \"unix_millis_request_time\": { \"title\": \"Unix Millis Request Time\", \"description\": \"The time that the skip request arrived\", \"type\": \"integer\" }, \"labels\": { \"title\": \"Labels\", \"description\": \"Labels to describe the purpose of the skip request\", \"type\": \"array\", \"items\": { \"type\": \"string\" } }, \"undo\": { \"title\": \"Undo\", \"description\": \"Information about an undo skip request that applied to this request\", \"allOf\": [ { \"$ref\": \"#/definitions/Undo\" } ] } }, \"required\": [ \"unix_millis_request_time\", \"labels\" ] }, \"Phase\": { \"title\": \"Phase\", \"type\": \"object\", \"properties\": { \"id\": { \"$ref\": \"#/definitions/Id\" }, \"category\": { \"$ref\": \"#/definitions/Category\" }, \"detail\": { \"$ref\": \"#/definitions/Detail\" }, \"unix_millis_start_time\": { \"title\": \"Unix Millis Start Time\", \"type\": \"integer\" }, \"unix_millis_finish_time\": { \"title\": \"Unix Millis Finish Time\", \"type\": \"integer\" }, \"original_estimate_millis\": { \"$ref\": \"#/definitions/EstimateMillis\" }, \"estimate_millis\": { \"$ref\": \"#/definitions/EstimateMillis\" }, \"final_event_id\": { \"$ref\": \"#/definitions/Id\" }, \"events\": { \"title\": \"Events\", \"description\": \"A dictionary of events for this phase. The keys (property names) are the event IDs, which are integers.\", \"type\": \"object\", \"additionalProperties\": { \"$ref\": \"#/definitions/EventState\" } }, \"skip_requests\": { \"title\": \"Skip Requests\", \"description\": \"Information about any skip requests that have been received\", \"type\": \"object\", \"additionalProperties\": { \"$ref\": \"#/definitions/SkipPhaseRequest\" } } }, \"required\": [ \"id\" ] }, \"ResumedBy\": { \"title\": \"ResumedBy\", \"type\": \"object\", \"properties\": { \"unix_millis_request_time\": { \"title\": \"Unix Millis Request Time\", \"description\": \"The time that the resume request arrived\", \"type\": \"integer\" }, \"labels\": { \"title\": \"Labels\", \"description\": \"Labels to describe the resume request\", \"type\": \"array\", \"items\": { \"type\": \"string\" } } }, \"required\": [ \"labels\" ] }, \"Interruption\": { \"title\": \"Interruption\", \"type\": \"object\", \"properties\": { \"unix_millis_request_time\": { \"title\": \"Unix Millis Request Time\", \"description\": \"The time that the interruption request arrived\", \"type\": \"integer\" }, \"labels\": { \"title\": \"Labels\", \"description\": \"Labels to describe the purpose of the interruption\", \"type\": \"array\", \"items\": { \"type\": \"string\" } }, \"resumed_by\": { \"title\": \"Resumed By\", \"description\": \"Information about the resume request that ended this interruption. This field will be missing if the interruption is still active.\", \"allOf\": [ { \"$ref\": \"#/definitions/ResumedBy\" } ] } }, \"required\": [ \"unix_millis_request_time\", \"labels\" ] }, \"Cancellation\": { \"title\": \"Cancellation\", \"type\": \"object\", \"properties\": { \"unix_millis_request_time\": { \"title\": \"Unix Millis Request Time\", \"description\": \"The time that the cancellation request arrived\", \"type\": \"integer\" }, \"labels\": { \"title\": \"Labels\", \"description\": \"Labels to describe the cancel request\", \"type\": \"array\", \"items\": { \"type\": \"string\" } } }, \"required\": [ \"unix_millis_request_time\", \"labels\" ] }, \"Killed\": { \"title\": \"Killed\", \"type\": \"object\", \"properties\": { \"unix_millis_request_time\": { \"title\": \"Unix Millis Request Time\", \"description\": \"The time that the cancellation request arrived\", \"type\": \"integer\" }, \"labels\": { \"title\": \"Labels\", \"description\": \"Labels to describe the kill request\", \"type\": \"array\", \"items\": { \"type\": \"string\" } } }, \"required\": [ \"unix_millis_request_time\", \"labels\" ] } } } ``` ### /tasks/{task_id}/log ``` { \"title\": \"TaskEventLog\", \"type\": \"object\", \"properties\": { \"task_id\": { \"title\": \"Task Id\", \"type\": \"string\" }, \"log\": { \"title\": \"Log\", \"description\": \"Log entries related to the overall task\", \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/LogEntry\" } }, \"phases\": { \"title\": \"Phases\", \"description\": \"A dictionary whose keys (property names) are the indices of a phase\", \"type\": \"object\", \"additionalProperties\": { \"$ref\": \"#/definitions/Phases\" } } }, \"required\": [ \"task_id\" ], \"additionalProperties\": false, \"definitions\": { \"Tier\": { \"title\": \"Tier\", \"description\": \"An enumeration.\", \"enum\": [ \"uninitialized\", \"info\", \"warning\", \"error\" ] }, \"LogEntry\": { \"title\": \"LogEntry\", \"type\": \"object\", \"properties\": { \"seq\": { \"title\": \"Seq\", \"description\": \"Sequence number for this entry. Each entry has a unique sequence number which monotonically increase, until integer overflow causes a wrap around.\", \"exclusiveMaximum\": 4294967296, \"minimum\": 0, \"type\": \"integer\" }, \"tier\": { \"description\": \"The importance level of the log entry\", \"allOf\": [ { \"$ref\": \"#/definitions/Tier\" } ] }, \"unix_millis_time\": { \"title\": \"Unix Millis Time\", \"type\": \"integer\" }, \"text\": { \"title\": \"Text\", \"description\": \"The text of the log entry\", \"type\": \"string\" } }, \"required\": [ \"seq\", \"tier\", \"unix_millis_time\", \"text\" ] }, \"Phases\": { \"title\": \"Phases\", \"type\": \"object\", \"properties\": { \"log\": { \"title\": \"Log\", \"description\": \"Log entries related to the overall phase\", \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/LogEntry\" } }, \"events\": { \"title\": \"Events\", \"description\": \"A dictionary whose keys (property names) are the indices of an event in the phase\", \"type\": \"object\", \"additionalProperties\": { \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/LogEntry\" } } } }, \"additionalProperties\": false } } } ``` ### /dispensers/{guid}/state ``` { \"title\": \"DispenserState\", \"type\": \"object\", \"properties\": { \"time\": { \"title\": \"Time\", \"default\": { \"sec\": 0, \"nanosec\": 0 }, \"allOf\": [ { \"$ref\": \"#/definitions/Time\" } ] }, \"guid\": { \"title\": \"Guid\", \"default\": \"\", \"type\": \"string\" }, \"mode\": { \"title\": \"Mode\", \"default\": 0, \"minimum\": -2147483648, \"maximum\": 2147483647, \"type\": \"integer\" }, \"request_guid_queue\": { \"title\": \"Request Guid Queue\", \"default\": [], \"type\": \"array\", \"items\": { \"type\": \"string\" } }, \"seconds_remaining\": { \"title\": \"Seconds Remaining\", \"default\": 0, \"type\": \"number\" } }, \"required\": [ \"time\", \"guid\", \"mode\", \"request_guid_queue\", \"seconds_remaining\" ], \"definitions\": { \"Time\": { \"title\": \"Time\", \"type\": \"object\", \"properties\": { \"sec\": { \"title\": \"Sec\", \"default\": 0, \"minimum\": -2147483648, \"maximum\": 2147483647, \"type\": \"integer\" }, \"nanosec\": { \"title\": \"Nanosec\", \"default\": 0, \"minimum\": 0, \"maximum\": 4294967295, \"type\": \"integer\" } }, \"required\": [ \"sec\", \"nanosec\" ] } } } ``` ### /dispensers/{guid}/health ``` { \"title\": \"DispenserHealth\", \"type\": \"object\", \"properties\": { \"health_status\": { \"title\": \"Health Status\", \"maxLength\": 255, \"nullable\": true, \"type\": \"string\" }, \"health_message\": { \"title\": \"Health Message\", \"nullable\": true, \"type\": \"string\" }, \"id_\": { \"title\": \"Id \", \"maxLength\": 255, \"type\": \"string\" } }, \"required\": [ \"health_status\", \"id_\" ], \"additionalProperties\": false } ``` ### /ingestors/{guid}/state ``` { \"title\": \"IngestorState\", \"type\": \"object\", \"properties\": { \"time\": { \"title\": \"Time\", \"default\": { \"sec\": 0, \"nanosec\": 0 }, \"allOf\": [ { \"$ref\": \"#/definitions/Time\" } ] }, \"guid\": { \"title\": \"Guid\", \"default\": \"\", \"type\": \"string\" }, \"mode\": { \"title\": \"Mode\", \"default\": 0, \"minimum\": -2147483648, \"maximum\": 2147483647, \"type\": \"integer\" }, \"request_guid_queue\": { \"title\": \"Request Guid Queue\", \"default\": [], \"type\": \"array\", \"items\": { \"type\": \"string\" } }, \"seconds_remaining\": { \"title\": \"Seconds Remaining\", \"default\": 0, \"type\": \"number\" } }, \"required\": [ \"time\", \"guid\", \"mode\", \"request_guid_queue\", \"seconds_remaining\" ], \"definitions\": { \"Time\": { \"title\": \"Time\", \"type\": \"object\", \"properties\": { \"sec\": { \"title\": \"Sec\", \"default\": 0, \"minimum\": -2147483648, \"maximum\": 2147483647, \"type\": \"integer\" }, \"nanosec\": { \"title\": \"Nanosec\", \"default\": 0, \"minimum\": 0, \"maximum\": 4294967295, \"type\": \"integer\" } }, \"required\": [ \"sec\", \"nanosec\" ] } } } ``` ### /ingestors/{guid}/health ``` { \"title\": \"IngestorHealth\", \"type\": \"object\", \"properties\": { \"health_status\": { \"title\": \"Health Status\", \"maxLength\": 255, \"nullable\": true, \"type\": \"string\" }, \"health_message\": { \"title\": \"Health Message\", \"nullable\": true, \"type\": \"string\" }, \"id_\": { \"title\": \"Id \", \"maxLength\": 255, \"type\": \"string\" } }, \"required\": [ \"health_status\", \"id_\" ], \"additionalProperties\": false } ``` ### /fleets/{name}/state ``` { \"title\": \"FleetState\", \"type\": \"object\", \"properties\": { \"name\": { \"title\": \"Name\", \"type\": \"string\" }, \"robots\": { \"title\": \"Robots\", \"description\": \"A dictionary of the states of the robots that belong to this fleet\", \"type\": \"object\", \"additionalProperties\": { \"$ref\": \"#/definitions/RobotState\" } } }, \"definitions\": { \"Status\": { \"title\": \"Status\", \"description\": \"An enumeration.\", \"enum\": [ \"uninitialized\", \"offline\", \"shutdown\", \"idle\", \"charging\", \"working\", \"error\" ] }, \"Location2D\": { \"title\": \"Location2D\", \"type\": \"object\", \"properties\": { \"map\": { \"title\": \"Map\", \"type\": \"string\" }, \"x\": { \"title\": \"X\", \"type\": \"number\" }, \"y\": { \"title\": \"Y\", \"type\": \"number\" }, \"yaw\": { \"title\": \"Yaw\", \"type\": \"number\" } }, \"required\": [ \"map\", \"x\", \"y\", \"yaw\" ] }, \"Issue\": { \"title\": \"Issue\", \"type\": \"object\", \"properties\": { \"category\": { \"title\": \"Category\", \"description\": \"Category of the robot\'s issue\", \"type\": \"string\" }, \"detail\": { \"title\": \"Detail\", \"description\": \"Detailed information about the issue\", \"anyOf\": [ { \"type\": \"object\" }, { \"type\": \"array\", \"items\": {} }, { \"type\": \"string\" } ] } } }, \"Commission\": { \"title\": \"Commission\", \"type\": \"object\", \"properties\": { \"dispatch_tasks\": { \"title\": \"Dispatch Tasks\", \"description\": \"Should the robot accept dispatched tasks, true/false. When used in a request, leave this unset to not change the robot\'s current value.\", \"type\": \"boolean\" }, \"direct_tasks\": { \"title\": \"Direct Tasks\", \"description\": \"Should the robot accept direct task requests, true/false. When used in a request, leave this unset to not change the robot\'s current value.\", \"type\": \"boolean\" }, \"idle_behavior\": { \"title\": \"Idle Behavior\", \"description\": \"Should the robot perform its idle behavior, true/false. When used in a request, leave this unset to not change the robot\'s current value.\", \"type\": \"boolean\" } } }, \"MutexGroups\": { \"title\": \"MutexGroups\", \"type\": \"object\", \"properties\": { \"locked\": { \"title\": \"Locked\", \"description\": \"A list of mutex groups that this robot has currently locked\", \"type\": \"array\", \"items\": { \"type\": \"string\" } }, \"requesting\": { \"title\": \"Requesting\", \"description\": \"A list of the mutex groups that this robot is currently requesting but has not lockd yet\", \"type\": \"array\", \"items\": { \"type\": \"string\" } } } }, \"RobotState\": { \"title\": \"RobotState\", \"type\": \"object\", \"properties\": { \"name\": { \"title\": \"Name\", \"type\": \"string\" }, \"status\": { \"description\": \"A simple token representing the status of the robot\", \"allOf\": [ { \"$ref\": \"#/definitions/Status\" } ] }, \"task_id\": { \"title\": \"Task Id\", \"description\": \"The ID of the task this robot is currently working on. Empty string if the robot is not working on a task.\", \"type\": \"string\" }, \"unix_millis_time\": { \"title\": \"Unix Millis Time\", \"type\": \"integer\" }, \"location\": { \"$ref\": \"#/definitions/Location2D\" }, \"battery\": { \"title\": \"Battery\", \"description\": \"State of charge of the battery. Values range from 0.0 (depleted) to 1.0 (fully charged)\", \"minimum\": 0.0, \"maximum\": 1.0, \"type\": \"number\" }, \"issues\": { \"title\": \"Issues\", \"description\": \"A list of issues with the robot that operators need to address\", \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/Issue\" } }, \"commission\": { \"$ref\": \"#/definitions/Commission\" }, \"mutex_groups\": { \"title\": \"Mutex Groups\", \"description\": \"Information about the mutex groups that this robot is interacting with\", \"allOf\": [ { \"$ref\": \"#/definitions/MutexGroups\" } ] } } } } } ``` ### /fleets/{name}/log ``` { \"title\": \"FleetLog\", \"type\": \"object\", \"properties\": { \"name\": { \"title\": \"Name\", \"type\": \"string\" }, \"log\": { \"title\": \"Log\", \"description\": \"Log for the overall fleet\", \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/LogEntry\" } }, \"robots\": { \"title\": \"Robots\", \"description\": \"Dictionary of logs for the individual robots. The keys (property names) are the robot names.\", \"type\": \"object\", \"additionalProperties\": { \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/LogEntry\" } } } }, \"definitions\": { \"Tier\": { \"title\": \"Tier\", \"description\": \"An enumeration.\", \"enum\": [ \"uninitialized\", \"info\", \"warning\", \"error\" ] }, \"LogEntry\": { \"title\": \"LogEntry\", \"type\": \"object\", \"properties\": { \"seq\": { \"title\": \"Seq\", \"description\": \"Sequence number for this entry. Each entry has a unique sequence number which monotonically increase, until integer overflow causes a wrap around.\", \"exclusiveMaximum\": 4294967296, \"minimum\": 0, \"type\": \"integer\" }, \"tier\": { \"description\": \"The importance level of the log entry\", \"allOf\": [ { \"$ref\": \"#/definitions/Tier\" } ] }, \"unix_millis_time\": { \"title\": \"Unix Millis Time\", \"type\": \"integer\" }, \"text\": { \"title\": \"Text\", \"description\": \"The text of the log entry\", \"type\": \"string\" } }, \"required\": [ \"seq\", \"tier\", \"unix_millis_time\", \"text\" ] } } } ``` + * # NOTE: This endpoint is here for documentation purposes only, this is _not_ a REST endpoint. ## About This exposes a minimal pubsub system built on top of socket.io. It works similar to a normal socket.io endpoint, except that are 2 special rooms which control subscriptions. ## Rooms ### subscribe Clients must send a message to this room to start receiving messages on other rooms. The message must be of the form: ``` { \"room\": \"\" } ``` ### unsubscribe Clients can send a message to this room to stop receiving messages on other rooms. The message must be of the form: ``` { \"room\": \"\" } ``` ### /alerts ``` { \"title\": \"Alert\", \"description\": \"General alert that can be triggered by events.\", \"type\": \"object\", \"properties\": { \"id\": { \"title\": \"Id\", \"maxLength\": 255, \"type\": \"string\" }, \"original_id\": { \"title\": \"Original Id\", \"maxLength\": 255, \"type\": \"string\" }, \"category\": { \"title\": \"Category\", \"description\": \"Default: default
Task: task
Fleet: fleet
Robot: robot\", \"maxLength\": 7, \"type\": \"string\" }, \"unix_millis_created_time\": { \"title\": \"Unix Millis Created Time\", \"minimum\": -9223372036854775808, \"maximum\": 9223372036854775807, \"type\": \"integer\" }, \"acknowledged_by\": { \"title\": \"Acknowledged By\", \"maxLength\": 255, \"nullable\": true, \"type\": \"string\" }, \"unix_millis_acknowledged_time\": { \"title\": \"Unix Millis Acknowledged Time\", \"minimum\": -9223372036854775808, \"maximum\": 9223372036854775807, \"nullable\": true, \"type\": \"integer\" } }, \"required\": [ \"id\", \"original_id\", \"category\", \"unix_millis_created_time\" ], \"additionalProperties\": false } ``` ### /beacons ``` { \"title\": \"BeaconState\", \"type\": \"object\", \"properties\": { \"id\": { \"title\": \"Id\", \"maxLength\": 255, \"type\": \"string\" }, \"online\": { \"title\": \"Online\", \"type\": \"boolean\" }, \"category\": { \"title\": \"Category\", \"maxLength\": 255, \"nullable\": true, \"type\": \"string\" }, \"activated\": { \"title\": \"Activated\", \"type\": \"boolean\" }, \"level\": { \"title\": \"Level\", \"maxLength\": 255, \"nullable\": true, \"type\": \"string\" } }, \"required\": [ \"id\", \"online\", \"activated\" ], \"additionalProperties\": false } ``` ### /building_map ``` { \"title\": \"BuildingMap\", \"type\": \"object\", \"properties\": { \"name\": { \"title\": \"Name\", \"default\": \"\", \"type\": \"string\" }, \"levels\": { \"title\": \"Levels\", \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/Level\" } }, \"lifts\": { \"title\": \"Lifts\", \"default\": [], \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/Lift\" } } }, \"required\": [ \"name\", \"levels\", \"lifts\" ], \"definitions\": { \"AffineImage\": { \"title\": \"AffineImage\", \"type\": \"object\", \"properties\": { \"name\": { \"title\": \"Name\", \"default\": \"\", \"type\": \"string\" }, \"x_offset\": { \"title\": \"X Offset\", \"default\": 0, \"type\": \"number\" }, \"y_offset\": { \"title\": \"Y Offset\", \"default\": 0, \"type\": \"number\" }, \"yaw\": { \"title\": \"Yaw\", \"default\": 0, \"type\": \"number\" }, \"scale\": { \"title\": \"Scale\", \"default\": 0, \"type\": \"number\" }, \"encoding\": { \"title\": \"Encoding\", \"default\": \"\", \"type\": \"string\" }, \"data\": { \"title\": \"Data\", \"type\": \"string\" } }, \"required\": [ \"name\", \"x_offset\", \"y_offset\", \"yaw\", \"scale\", \"encoding\", \"data\" ] }, \"Place\": { \"title\": \"Place\", \"type\": \"object\", \"properties\": { \"name\": { \"title\": \"Name\", \"default\": \"\", \"type\": \"string\" }, \"x\": { \"title\": \"X\", \"default\": 0, \"type\": \"number\" }, \"y\": { \"title\": \"Y\", \"default\": 0, \"type\": \"number\" }, \"yaw\": { \"title\": \"Yaw\", \"default\": 0, \"type\": \"number\" }, \"position_tolerance\": { \"title\": \"Position Tolerance\", \"default\": 0, \"type\": \"number\" }, \"yaw_tolerance\": { \"title\": \"Yaw Tolerance\", \"default\": 0, \"type\": \"number\" } }, \"required\": [ \"name\", \"x\", \"y\", \"yaw\", \"position_tolerance\", \"yaw_tolerance\" ] }, \"Door\": { \"title\": \"Door\", \"type\": \"object\", \"properties\": { \"name\": { \"title\": \"Name\", \"default\": \"\", \"type\": \"string\" }, \"v1_x\": { \"title\": \"V1 X\", \"default\": 0, \"type\": \"number\" }, \"v1_y\": { \"title\": \"V1 Y\", \"default\": 0, \"type\": \"number\" }, \"v2_x\": { \"title\": \"V2 X\", \"default\": 0, \"type\": \"number\" }, \"v2_y\": { \"title\": \"V2 Y\", \"default\": 0, \"type\": \"number\" }, \"door_type\": { \"title\": \"Door Type\", \"default\": 0, \"minimum\": 0, \"maximum\": 255, \"type\": \"integer\" }, \"motion_range\": { \"title\": \"Motion Range\", \"default\": 0, \"type\": \"number\" }, \"motion_direction\": { \"title\": \"Motion Direction\", \"default\": 0, \"minimum\": -2147483648, \"maximum\": 2147483647, \"type\": \"integer\" } }, \"required\": [ \"name\", \"v1_x\", \"v1_y\", \"v2_x\", \"v2_y\", \"door_type\", \"motion_range\", \"motion_direction\" ] }, \"Param\": { \"title\": \"Param\", \"type\": \"object\", \"properties\": { \"name\": { \"title\": \"Name\", \"default\": \"\", \"type\": \"string\" }, \"type\": { \"title\": \"Type\", \"default\": 0, \"minimum\": 0, \"maximum\": 4294967295, \"type\": \"integer\" }, \"value_int\": { \"title\": \"Value Int\", \"default\": 0, \"minimum\": -2147483648, \"maximum\": 2147483647, \"type\": \"integer\" }, \"value_float\": { \"title\": \"Value Float\", \"default\": 0, \"type\": \"number\" }, \"value_string\": { \"title\": \"Value String\", \"default\": \"\", \"type\": \"string\" }, \"value_bool\": { \"title\": \"Value Bool\", \"default\": false, \"type\": \"boolean\" } }, \"required\": [ \"name\", \"type\", \"value_int\", \"value_float\", \"value_string\", \"value_bool\" ] }, \"GraphNode\": { \"title\": \"GraphNode\", \"type\": \"object\", \"properties\": { \"x\": { \"title\": \"X\", \"default\": 0, \"type\": \"number\" }, \"y\": { \"title\": \"Y\", \"default\": 0, \"type\": \"number\" }, \"name\": { \"title\": \"Name\", \"default\": \"\", \"type\": \"string\" }, \"params\": { \"title\": \"Params\", \"default\": [], \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/Param\" } } }, \"required\": [ \"x\", \"y\", \"name\", \"params\" ] }, \"GraphEdge\": { \"title\": \"GraphEdge\", \"type\": \"object\", \"properties\": { \"v1_idx\": { \"title\": \"V1 Idx\", \"default\": 0, \"minimum\": 0, \"maximum\": 4294967295, \"type\": \"integer\" }, \"v2_idx\": { \"title\": \"V2 Idx\", \"default\": 0, \"minimum\": 0, \"maximum\": 4294967295, \"type\": \"integer\" }, \"params\": { \"title\": \"Params\", \"default\": [], \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/Param\" } }, \"edge_type\": { \"title\": \"Edge Type\", \"default\": 0, \"minimum\": 0, \"maximum\": 255, \"type\": \"integer\" } }, \"required\": [ \"v1_idx\", \"v2_idx\", \"params\", \"edge_type\" ] }, \"Graph\": { \"title\": \"Graph\", \"type\": \"object\", \"properties\": { \"name\": { \"title\": \"Name\", \"default\": \"\", \"type\": \"string\" }, \"vertices\": { \"title\": \"Vertices\", \"default\": [], \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/GraphNode\" } }, \"edges\": { \"title\": \"Edges\", \"default\": [], \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/GraphEdge\" } }, \"params\": { \"title\": \"Params\", \"default\": [], \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/Param\" } } }, \"required\": [ \"name\", \"vertices\", \"edges\", \"params\" ] }, \"Level\": { \"title\": \"Level\", \"type\": \"object\", \"properties\": { \"name\": { \"title\": \"Name\", \"default\": \"\", \"type\": \"string\" }, \"elevation\": { \"title\": \"Elevation\", \"default\": 0, \"type\": \"number\" }, \"images\": { \"title\": \"Images\", \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/AffineImage\" } }, \"places\": { \"title\": \"Places\", \"default\": [], \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/Place\" } }, \"doors\": { \"title\": \"Doors\", \"default\": [], \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/Door\" } }, \"nav_graphs\": { \"title\": \"Nav Graphs\", \"default\": [], \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/Graph\" } }, \"wall_graph\": { \"title\": \"Wall Graph\", \"default\": { \"name\": \"\", \"vertices\": [], \"edges\": [], \"params\": [] }, \"allOf\": [ { \"$ref\": \"#/definitions/Graph\" } ] } }, \"required\": [ \"name\", \"elevation\", \"images\", \"places\", \"doors\", \"nav_graphs\", \"wall_graph\" ] }, \"Lift\": { \"title\": \"Lift\", \"type\": \"object\", \"properties\": { \"name\": { \"title\": \"Name\", \"default\": \"\", \"type\": \"string\" }, \"levels\": { \"title\": \"Levels\", \"default\": [], \"type\": \"array\", \"items\": { \"type\": \"string\" } }, \"doors\": { \"title\": \"Doors\", \"default\": [], \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/Door\" } }, \"wall_graph\": { \"title\": \"Wall Graph\", \"default\": { \"name\": \"\", \"vertices\": [], \"edges\": [], \"params\": [] }, \"allOf\": [ { \"$ref\": \"#/definitions/Graph\" } ] }, \"ref_x\": { \"title\": \"Ref X\", \"default\": 0, \"type\": \"number\" }, \"ref_y\": { \"title\": \"Ref Y\", \"default\": 0, \"type\": \"number\" }, \"ref_yaw\": { \"title\": \"Ref Yaw\", \"default\": 0, \"type\": \"number\" }, \"width\": { \"title\": \"Width\", \"default\": 0, \"type\": \"number\" }, \"depth\": { \"title\": \"Depth\", \"default\": 0, \"type\": \"number\" } }, \"required\": [ \"name\", \"levels\", \"doors\", \"wall_graph\", \"ref_x\", \"ref_y\", \"ref_yaw\", \"width\", \"depth\" ] } } } ``` ### /building_map/fire_alarm_trigger ``` { \"title\": \"FireAlarmTriggerState\", \"type\": \"object\", \"properties\": { \"unix_millis_time\": { \"title\": \"Unix Millis Time\", \"type\": \"integer\" }, \"trigger\": { \"title\": \"Trigger\", \"type\": \"boolean\" } }, \"required\": [ \"unix_millis_time\", \"trigger\" ] } ``` ### /delivery_alerts ``` { \"title\": \"DeliveryAlert\", \"type\": \"object\", \"properties\": { \"id\": { \"title\": \"Id\", \"type\": \"string\" }, \"category\": { \"$ref\": \"#/definitions/Category\" }, \"tier\": { \"$ref\": \"#/definitions/Tier\" }, \"action\": { \"$ref\": \"#/definitions/Action\" }, \"task_id\": { \"title\": \"Task Id\", \"type\": \"string\" }, \"message\": { \"title\": \"Message\", \"type\": \"string\" } }, \"required\": [ \"id\", \"category\", \"tier\", \"action\", \"task_id\", \"message\" ], \"definitions\": { \"Category\": { \"title\": \"Category\", \"description\": \"An enumeration.\", \"enum\": [ \"missing\", \"wrong\", \"obstructed\", \"cancelled\" ], \"type\": \"string\" }, \"Tier\": { \"title\": \"Tier\", \"description\": \"An enumeration.\", \"enum\": [ \"warning\", \"error\" ], \"type\": \"string\" }, \"Action\": { \"title\": \"Action\", \"description\": \"An enumeration.\", \"enum\": [ \"waiting\", \"cancelled\", \"override\", \"resume\" ], \"type\": \"string\" } } } ``` ### /doors/{door_name}/state ``` { \"title\": \"DoorState\", \"type\": \"object\", \"properties\": { \"door_time\": { \"title\": \"Door Time\", \"default\": { \"sec\": 0, \"nanosec\": 0 }, \"allOf\": [ { \"$ref\": \"#/definitions/Time\" } ] }, \"door_name\": { \"title\": \"Door Name\", \"default\": \"\", \"type\": \"string\" }, \"current_mode\": { \"title\": \"Current Mode\", \"default\": { \"value\": 0 }, \"allOf\": [ { \"$ref\": \"#/definitions/DoorMode\" } ] } }, \"required\": [ \"door_time\", \"door_name\", \"current_mode\" ], \"definitions\": { \"Time\": { \"title\": \"Time\", \"type\": \"object\", \"properties\": { \"sec\": { \"title\": \"Sec\", \"default\": 0, \"minimum\": -2147483648, \"maximum\": 2147483647, \"type\": \"integer\" }, \"nanosec\": { \"title\": \"Nanosec\", \"default\": 0, \"minimum\": 0, \"maximum\": 4294967295, \"type\": \"integer\" } }, \"required\": [ \"sec\", \"nanosec\" ] }, \"DoorMode\": { \"title\": \"DoorMode\", \"type\": \"object\", \"properties\": { \"value\": { \"title\": \"Value\", \"default\": 0, \"minimum\": 0, \"maximum\": 4294967295, \"type\": \"integer\" } }, \"required\": [ \"value\" ] } } } ``` ### /doors/{door_name}/health ``` { \"title\": \"DoorHealth\", \"type\": \"object\", \"properties\": { \"health_status\": { \"title\": \"Health Status\", \"maxLength\": 255, \"nullable\": true, \"type\": \"string\" }, \"health_message\": { \"title\": \"Health Message\", \"nullable\": true, \"type\": \"string\" }, \"id_\": { \"title\": \"Id \", \"maxLength\": 255, \"type\": \"string\" } }, \"required\": [ \"health_status\", \"id_\" ], \"additionalProperties\": false } ``` ### /lifts/{lift_name}/state ``` { \"title\": \"LiftState\", \"type\": \"object\", \"properties\": { \"lift_time\": { \"title\": \"Lift Time\", \"default\": { \"sec\": 0, \"nanosec\": 0 }, \"allOf\": [ { \"$ref\": \"#/definitions/Time\" } ] }, \"lift_name\": { \"title\": \"Lift Name\", \"default\": \"\", \"type\": \"string\" }, \"available_floors\": { \"title\": \"Available Floors\", \"default\": [], \"type\": \"array\", \"items\": { \"type\": \"string\" } }, \"current_floor\": { \"title\": \"Current Floor\", \"default\": \"\", \"type\": \"string\" }, \"destination_floor\": { \"title\": \"Destination Floor\", \"default\": \"\", \"type\": \"string\" }, \"door_state\": { \"title\": \"Door State\", \"default\": 0, \"minimum\": 0, \"maximum\": 255, \"type\": \"integer\" }, \"motion_state\": { \"title\": \"Motion State\", \"default\": 0, \"minimum\": 0, \"maximum\": 255, \"type\": \"integer\" }, \"available_modes\": { \"title\": \"Available Modes\", \"type\": \"array\", \"items\": { \"type\": \"integer\" } }, \"current_mode\": { \"title\": \"Current Mode\", \"default\": 0, \"minimum\": 0, \"maximum\": 255, \"type\": \"integer\" }, \"session_id\": { \"title\": \"Session Id\", \"default\": \"\", \"type\": \"string\" } }, \"required\": [ \"lift_time\", \"lift_name\", \"available_floors\", \"current_floor\", \"destination_floor\", \"door_state\", \"motion_state\", \"available_modes\", \"current_mode\", \"session_id\" ], \"definitions\": { \"Time\": { \"title\": \"Time\", \"type\": \"object\", \"properties\": { \"sec\": { \"title\": \"Sec\", \"default\": 0, \"minimum\": -2147483648, \"maximum\": 2147483647, \"type\": \"integer\" }, \"nanosec\": { \"title\": \"Nanosec\", \"default\": 0, \"minimum\": 0, \"maximum\": 4294967295, \"type\": \"integer\" } }, \"required\": [ \"sec\", \"nanosec\" ] } } } ``` ### /lifts/{lift_name}/health ``` { \"title\": \"LiftHealth\", \"type\": \"object\", \"properties\": { \"health_status\": { \"title\": \"Health Status\", \"maxLength\": 255, \"nullable\": true, \"type\": \"string\" }, \"health_message\": { \"title\": \"Health Message\", \"nullable\": true, \"type\": \"string\" }, \"id_\": { \"title\": \"Id \", \"maxLength\": 255, \"type\": \"string\" } }, \"required\": [ \"health_status\", \"id_\" ], \"additionalProperties\": false } ``` ### /tasks/{task_id}/state ``` { \"title\": \"TaskState\", \"type\": \"object\", \"properties\": { \"booking\": { \"$ref\": \"#/definitions/Booking\" }, \"category\": { \"$ref\": \"#/definitions/Category\" }, \"detail\": { \"$ref\": \"#/definitions/Detail\" }, \"unix_millis_start_time\": { \"title\": \"Unix Millis Start Time\", \"type\": \"integer\" }, \"unix_millis_finish_time\": { \"title\": \"Unix Millis Finish Time\", \"type\": \"integer\" }, \"original_estimate_millis\": { \"$ref\": \"#/definitions/EstimateMillis\" }, \"estimate_millis\": { \"$ref\": \"#/definitions/EstimateMillis\" }, \"assigned_to\": { \"title\": \"Assigned To\", \"description\": \"Which agent (robot) is the task assigned to\", \"allOf\": [ { \"$ref\": \"#/definitions/AssignedTo\" } ] }, \"status\": { \"$ref\": \"#/definitions/Status\" }, \"dispatch\": { \"$ref\": \"#/definitions/Dispatch\" }, \"phases\": { \"title\": \"Phases\", \"description\": \"A dictionary of the states of the phases of the task. The keys (property names) are phase IDs, which are integers.\", \"type\": \"object\", \"additionalProperties\": { \"$ref\": \"#/definitions/Phase\" } }, \"completed\": { \"title\": \"Completed\", \"description\": \"An array of the IDs of completed phases of this task\", \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/Id\" } }, \"active\": { \"title\": \"Active\", \"description\": \"The ID of the active phase for this task\", \"allOf\": [ { \"$ref\": \"#/definitions/Id\" } ] }, \"pending\": { \"title\": \"Pending\", \"description\": \"An array of the pending phases of this task\", \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/Id\" } }, \"interruptions\": { \"title\": \"Interruptions\", \"description\": \"A dictionary of interruptions that have been applied to this task. The keys (property names) are the unique token of the interruption request.\", \"type\": \"object\", \"additionalProperties\": { \"$ref\": \"#/definitions/Interruption\" } }, \"cancellation\": { \"title\": \"Cancellation\", \"description\": \"If the task was cancelled, this will describe information about the request.\", \"allOf\": [ { \"$ref\": \"#/definitions/Cancellation\" } ] }, \"killed\": { \"title\": \"Killed\", \"description\": \"If the task was killed, this will describe information about the request.\", \"allOf\": [ { \"$ref\": \"#/definitions/Killed\" } ] } }, \"required\": [ \"booking\" ], \"definitions\": { \"Booking\": { \"title\": \"Booking\", \"type\": \"object\", \"properties\": { \"id\": { \"title\": \"Id\", \"description\": \"The unique identifier for this task\", \"type\": \"string\" }, \"unix_millis_earliest_start_time\": { \"title\": \"Unix Millis Earliest Start Time\", \"type\": \"integer\" }, \"unix_millis_request_time\": { \"title\": \"Unix Millis Request Time\", \"type\": \"integer\" }, \"priority\": { \"title\": \"Priority\", \"description\": \"Priority information about this task\", \"anyOf\": [ { \"type\": \"object\" }, { \"type\": \"string\" } ] }, \"labels\": { \"title\": \"Labels\", \"description\": \"Information about how and why this task was booked\", \"type\": \"array\", \"items\": { \"type\": \"string\" } }, \"requester\": { \"title\": \"Requester\", \"description\": \"(Optional) An identifier for the entity that requested this task\", \"type\": \"string\" } }, \"required\": [ \"id\" ] }, \"Category\": { \"title\": \"Category\", \"description\": \"The category of this task or phase\", \"type\": \"string\" }, \"Detail\": { \"title\": \"Detail\", \"description\": \"Detailed information about a task, phase, or event\", \"anyOf\": [ { \"type\": \"object\" }, { \"type\": \"array\", \"items\": {} }, { \"type\": \"string\" } ] }, \"EstimateMillis\": { \"title\": \"EstimateMillis\", \"description\": \"An estimate, in milliseconds, of how long the subject will take to complete\", \"minimum\": 0, \"type\": \"integer\" }, \"AssignedTo\": { \"title\": \"AssignedTo\", \"type\": \"object\", \"properties\": { \"group\": { \"title\": \"Group\", \"type\": \"string\" }, \"name\": { \"title\": \"Name\", \"type\": \"string\" } }, \"required\": [ \"group\", \"name\" ] }, \"Status\": { \"title\": \"Status\", \"description\": \"An enumeration.\", \"enum\": [ \"uninitialized\", \"blocked\", \"error\", \"failed\", \"queued\", \"standby\", \"underway\", \"delayed\", \"skipped\", \"canceled\", \"killed\", \"completed\" ] }, \"Status1\": { \"title\": \"Status1\", \"description\": \"An enumeration.\", \"enum\": [ \"queued\", \"selected\", \"dispatched\", \"failed_to_assign\", \"canceled_in_flight\" ] }, \"Assignment\": { \"title\": \"Assignment\", \"type\": \"object\", \"properties\": { \"fleet_name\": { \"title\": \"Fleet Name\", \"type\": \"string\" }, \"expected_robot_name\": { \"title\": \"Expected Robot Name\", \"type\": \"string\" } } }, \"Error\": { \"title\": \"Error\", \"type\": \"object\", \"properties\": { \"code\": { \"title\": \"Code\", \"description\": \"A standard code for the kind of error that has occurred\", \"minimum\": 0, \"type\": \"integer\" }, \"category\": { \"title\": \"Category\", \"description\": \"The category of the error\", \"type\": \"string\" }, \"detail\": { \"title\": \"Detail\", \"description\": \"Details about the error\", \"type\": \"string\" } } }, \"Dispatch\": { \"title\": \"Dispatch\", \"type\": \"object\", \"properties\": { \"status\": { \"$ref\": \"#/definitions/Status1\" }, \"assignment\": { \"$ref\": \"#/definitions/Assignment\" }, \"errors\": { \"title\": \"Errors\", \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/Error\" } } }, \"required\": [ \"status\" ] }, \"Id\": { \"title\": \"Id\", \"minimum\": 0, \"type\": \"integer\" }, \"EventState\": { \"title\": \"EventState\", \"type\": \"object\", \"properties\": { \"id\": { \"$ref\": \"#/definitions/Id\" }, \"status\": { \"$ref\": \"#/definitions/Status\" }, \"name\": { \"title\": \"Name\", \"description\": \"The brief name of the event\", \"type\": \"string\" }, \"detail\": { \"title\": \"Detail\", \"description\": \"Detailed information about the event\", \"allOf\": [ { \"$ref\": \"#/definitions/Detail\" } ] }, \"deps\": { \"title\": \"Deps\", \"description\": \"This event may depend on other events. This array contains the IDs of those other event dependencies.\", \"type\": \"array\", \"items\": { \"type\": \"integer\", \"minimum\": 0 } } }, \"required\": [ \"id\" ] }, \"Undo\": { \"title\": \"Undo\", \"type\": \"object\", \"properties\": { \"unix_millis_request_time\": { \"title\": \"Unix Millis Request Time\", \"description\": \"The time that the undo skip request arrived\", \"type\": \"integer\" }, \"labels\": { \"title\": \"Labels\", \"description\": \"Labels to describe the undo skip request\", \"type\": \"array\", \"items\": { \"type\": \"string\" } } }, \"required\": [ \"unix_millis_request_time\", \"labels\" ] }, \"SkipPhaseRequest\": { \"title\": \"SkipPhaseRequest\", \"type\": \"object\", \"properties\": { \"unix_millis_request_time\": { \"title\": \"Unix Millis Request Time\", \"description\": \"The time that the skip request arrived\", \"type\": \"integer\" }, \"labels\": { \"title\": \"Labels\", \"description\": \"Labels to describe the purpose of the skip request\", \"type\": \"array\", \"items\": { \"type\": \"string\" } }, \"undo\": { \"title\": \"Undo\", \"description\": \"Information about an undo skip request that applied to this request\", \"allOf\": [ { \"$ref\": \"#/definitions/Undo\" } ] } }, \"required\": [ \"unix_millis_request_time\", \"labels\" ] }, \"Phase\": { \"title\": \"Phase\", \"type\": \"object\", \"properties\": { \"id\": { \"$ref\": \"#/definitions/Id\" }, \"category\": { \"$ref\": \"#/definitions/Category\" }, \"detail\": { \"$ref\": \"#/definitions/Detail\" }, \"unix_millis_start_time\": { \"title\": \"Unix Millis Start Time\", \"type\": \"integer\" }, \"unix_millis_finish_time\": { \"title\": \"Unix Millis Finish Time\", \"type\": \"integer\" }, \"original_estimate_millis\": { \"$ref\": \"#/definitions/EstimateMillis\" }, \"estimate_millis\": { \"$ref\": \"#/definitions/EstimateMillis\" }, \"final_event_id\": { \"$ref\": \"#/definitions/Id\" }, \"events\": { \"title\": \"Events\", \"description\": \"A dictionary of events for this phase. The keys (property names) are the event IDs, which are integers.\", \"type\": \"object\", \"additionalProperties\": { \"$ref\": \"#/definitions/EventState\" } }, \"skip_requests\": { \"title\": \"Skip Requests\", \"description\": \"Information about any skip requests that have been received\", \"type\": \"object\", \"additionalProperties\": { \"$ref\": \"#/definitions/SkipPhaseRequest\" } } }, \"required\": [ \"id\" ] }, \"ResumedBy\": { \"title\": \"ResumedBy\", \"type\": \"object\", \"properties\": { \"unix_millis_request_time\": { \"title\": \"Unix Millis Request Time\", \"description\": \"The time that the resume request arrived\", \"type\": \"integer\" }, \"labels\": { \"title\": \"Labels\", \"description\": \"Labels to describe the resume request\", \"type\": \"array\", \"items\": { \"type\": \"string\" } } }, \"required\": [ \"labels\" ] }, \"Interruption\": { \"title\": \"Interruption\", \"type\": \"object\", \"properties\": { \"unix_millis_request_time\": { \"title\": \"Unix Millis Request Time\", \"description\": \"The time that the interruption request arrived\", \"type\": \"integer\" }, \"labels\": { \"title\": \"Labels\", \"description\": \"Labels to describe the purpose of the interruption\", \"type\": \"array\", \"items\": { \"type\": \"string\" } }, \"resumed_by\": { \"title\": \"Resumed By\", \"description\": \"Information about the resume request that ended this interruption. This field will be missing if the interruption is still active.\", \"allOf\": [ { \"$ref\": \"#/definitions/ResumedBy\" } ] } }, \"required\": [ \"unix_millis_request_time\", \"labels\" ] }, \"Cancellation\": { \"title\": \"Cancellation\", \"type\": \"object\", \"properties\": { \"unix_millis_request_time\": { \"title\": \"Unix Millis Request Time\", \"description\": \"The time that the cancellation request arrived\", \"type\": \"integer\" }, \"labels\": { \"title\": \"Labels\", \"description\": \"Labels to describe the cancel request\", \"type\": \"array\", \"items\": { \"type\": \"string\" } } }, \"required\": [ \"unix_millis_request_time\", \"labels\" ] }, \"Killed\": { \"title\": \"Killed\", \"type\": \"object\", \"properties\": { \"unix_millis_request_time\": { \"title\": \"Unix Millis Request Time\", \"description\": \"The time that the cancellation request arrived\", \"type\": \"integer\" }, \"labels\": { \"title\": \"Labels\", \"description\": \"Labels to describe the kill request\", \"type\": \"array\", \"items\": { \"type\": \"string\" } } }, \"required\": [ \"unix_millis_request_time\", \"labels\" ] } } } ``` ### /tasks/{task_id}/log ``` { \"title\": \"TaskEventLog\", \"type\": \"object\", \"properties\": { \"task_id\": { \"title\": \"Task Id\", \"type\": \"string\" }, \"log\": { \"title\": \"Log\", \"description\": \"Log entries related to the overall task\", \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/LogEntry\" } }, \"phases\": { \"title\": \"Phases\", \"description\": \"A dictionary whose keys (property names) are the indices of a phase\", \"type\": \"object\", \"additionalProperties\": { \"$ref\": \"#/definitions/Phases\" } } }, \"required\": [ \"task_id\" ], \"additionalProperties\": false, \"definitions\": { \"Tier\": { \"title\": \"Tier\", \"description\": \"An enumeration.\", \"enum\": [ \"uninitialized\", \"info\", \"warning\", \"error\" ] }, \"LogEntry\": { \"title\": \"LogEntry\", \"type\": \"object\", \"properties\": { \"seq\": { \"title\": \"Seq\", \"description\": \"Sequence number for this entry. Each entry has a unique sequence number which monotonically increase, until integer overflow causes a wrap around.\", \"exclusiveMaximum\": 4294967296, \"minimum\": 0, \"type\": \"integer\" }, \"tier\": { \"description\": \"The importance level of the log entry\", \"allOf\": [ { \"$ref\": \"#/definitions/Tier\" } ] }, \"unix_millis_time\": { \"title\": \"Unix Millis Time\", \"type\": \"integer\" }, \"text\": { \"title\": \"Text\", \"description\": \"The text of the log entry\", \"type\": \"string\" } }, \"required\": [ \"seq\", \"tier\", \"unix_millis_time\", \"text\" ] }, \"Phases\": { \"title\": \"Phases\", \"type\": \"object\", \"properties\": { \"log\": { \"title\": \"Log\", \"description\": \"Log entries related to the overall phase\", \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/LogEntry\" } }, \"events\": { \"title\": \"Events\", \"description\": \"A dictionary whose keys (property names) are the indices of an event in the phase\", \"type\": \"object\", \"additionalProperties\": { \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/LogEntry\" } } } }, \"additionalProperties\": false } } } ``` ### /dispensers/{guid}/state ``` { \"title\": \"DispenserState\", \"type\": \"object\", \"properties\": { \"time\": { \"title\": \"Time\", \"default\": { \"sec\": 0, \"nanosec\": 0 }, \"allOf\": [ { \"$ref\": \"#/definitions/Time\" } ] }, \"guid\": { \"title\": \"Guid\", \"default\": \"\", \"type\": \"string\" }, \"mode\": { \"title\": \"Mode\", \"default\": 0, \"minimum\": -2147483648, \"maximum\": 2147483647, \"type\": \"integer\" }, \"request_guid_queue\": { \"title\": \"Request Guid Queue\", \"default\": [], \"type\": \"array\", \"items\": { \"type\": \"string\" } }, \"seconds_remaining\": { \"title\": \"Seconds Remaining\", \"default\": 0, \"type\": \"number\" } }, \"required\": [ \"time\", \"guid\", \"mode\", \"request_guid_queue\", \"seconds_remaining\" ], \"definitions\": { \"Time\": { \"title\": \"Time\", \"type\": \"object\", \"properties\": { \"sec\": { \"title\": \"Sec\", \"default\": 0, \"minimum\": -2147483648, \"maximum\": 2147483647, \"type\": \"integer\" }, \"nanosec\": { \"title\": \"Nanosec\", \"default\": 0, \"minimum\": 0, \"maximum\": 4294967295, \"type\": \"integer\" } }, \"required\": [ \"sec\", \"nanosec\" ] } } } ``` ### /dispensers/{guid}/health ``` { \"title\": \"DispenserHealth\", \"type\": \"object\", \"properties\": { \"health_status\": { \"title\": \"Health Status\", \"maxLength\": 255, \"nullable\": true, \"type\": \"string\" }, \"health_message\": { \"title\": \"Health Message\", \"nullable\": true, \"type\": \"string\" }, \"id_\": { \"title\": \"Id \", \"maxLength\": 255, \"type\": \"string\" } }, \"required\": [ \"health_status\", \"id_\" ], \"additionalProperties\": false } ``` ### /ingestors/{guid}/state ``` { \"title\": \"IngestorState\", \"type\": \"object\", \"properties\": { \"time\": { \"title\": \"Time\", \"default\": { \"sec\": 0, \"nanosec\": 0 }, \"allOf\": [ { \"$ref\": \"#/definitions/Time\" } ] }, \"guid\": { \"title\": \"Guid\", \"default\": \"\", \"type\": \"string\" }, \"mode\": { \"title\": \"Mode\", \"default\": 0, \"minimum\": -2147483648, \"maximum\": 2147483647, \"type\": \"integer\" }, \"request_guid_queue\": { \"title\": \"Request Guid Queue\", \"default\": [], \"type\": \"array\", \"items\": { \"type\": \"string\" } }, \"seconds_remaining\": { \"title\": \"Seconds Remaining\", \"default\": 0, \"type\": \"number\" } }, \"required\": [ \"time\", \"guid\", \"mode\", \"request_guid_queue\", \"seconds_remaining\" ], \"definitions\": { \"Time\": { \"title\": \"Time\", \"type\": \"object\", \"properties\": { \"sec\": { \"title\": \"Sec\", \"default\": 0, \"minimum\": -2147483648, \"maximum\": 2147483647, \"type\": \"integer\" }, \"nanosec\": { \"title\": \"Nanosec\", \"default\": 0, \"minimum\": 0, \"maximum\": 4294967295, \"type\": \"integer\" } }, \"required\": [ \"sec\", \"nanosec\" ] } } } ``` ### /ingestors/{guid}/health ``` { \"title\": \"IngestorHealth\", \"type\": \"object\", \"properties\": { \"health_status\": { \"title\": \"Health Status\", \"maxLength\": 255, \"nullable\": true, \"type\": \"string\" }, \"health_message\": { \"title\": \"Health Message\", \"nullable\": true, \"type\": \"string\" }, \"id_\": { \"title\": \"Id \", \"maxLength\": 255, \"type\": \"string\" } }, \"required\": [ \"health_status\", \"id_\" ], \"additionalProperties\": false } ``` ### /fleets/{name}/state ``` { \"title\": \"FleetState\", \"type\": \"object\", \"properties\": { \"name\": { \"title\": \"Name\", \"type\": \"string\" }, \"robots\": { \"title\": \"Robots\", \"description\": \"A dictionary of the states of the robots that belong to this fleet\", \"type\": \"object\", \"additionalProperties\": { \"$ref\": \"#/definitions/RobotState\" } } }, \"definitions\": { \"Status\": { \"title\": \"Status\", \"description\": \"An enumeration.\", \"enum\": [ \"uninitialized\", \"offline\", \"shutdown\", \"idle\", \"charging\", \"working\", \"error\" ] }, \"Location2D\": { \"title\": \"Location2D\", \"type\": \"object\", \"properties\": { \"map\": { \"title\": \"Map\", \"type\": \"string\" }, \"x\": { \"title\": \"X\", \"type\": \"number\" }, \"y\": { \"title\": \"Y\", \"type\": \"number\" }, \"yaw\": { \"title\": \"Yaw\", \"type\": \"number\" } }, \"required\": [ \"map\", \"x\", \"y\", \"yaw\" ] }, \"Issue\": { \"title\": \"Issue\", \"type\": \"object\", \"properties\": { \"category\": { \"title\": \"Category\", \"description\": \"Category of the robot\'s issue\", \"type\": \"string\" }, \"detail\": { \"title\": \"Detail\", \"description\": \"Detailed information about the issue\", \"anyOf\": [ { \"type\": \"object\" }, { \"type\": \"array\", \"items\": {} }, { \"type\": \"string\" } ] } } }, \"Commission\": { \"title\": \"Commission\", \"type\": \"object\", \"properties\": { \"dispatch_tasks\": { \"title\": \"Dispatch Tasks\", \"description\": \"Should the robot accept dispatched tasks, true/false. When used in a request, leave this unset to not change the robot\'s current value.\", \"type\": \"boolean\" }, \"direct_tasks\": { \"title\": \"Direct Tasks\", \"description\": \"Should the robot accept direct task requests, true/false. When used in a request, leave this unset to not change the robot\'s current value.\", \"type\": \"boolean\" }, \"idle_behavior\": { \"title\": \"Idle Behavior\", \"description\": \"Should the robot perform its idle behavior, true/false. When used in a request, leave this unset to not change the robot\'s current value.\", \"type\": \"boolean\" } } }, \"MutexGroups\": { \"title\": \"MutexGroups\", \"type\": \"object\", \"properties\": { \"locked\": { \"title\": \"Locked\", \"description\": \"A list of mutex groups that this robot has currently locked\", \"type\": \"array\", \"items\": { \"type\": \"string\" } }, \"requesting\": { \"title\": \"Requesting\", \"description\": \"A list of the mutex groups that this robot is currently requesting but has not lockd yet\", \"type\": \"array\", \"items\": { \"type\": \"string\" } } } }, \"RobotState\": { \"title\": \"RobotState\", \"type\": \"object\", \"properties\": { \"name\": { \"title\": \"Name\", \"type\": \"string\" }, \"status\": { \"description\": \"A simple token representing the status of the robot\", \"allOf\": [ { \"$ref\": \"#/definitions/Status\" } ] }, \"task_id\": { \"title\": \"Task Id\", \"description\": \"The ID of the task this robot is currently working on. Empty string if the robot is not working on a task.\", \"type\": \"string\" }, \"unix_millis_time\": { \"title\": \"Unix Millis Time\", \"type\": \"integer\" }, \"location\": { \"$ref\": \"#/definitions/Location2D\" }, \"battery\": { \"title\": \"Battery\", \"description\": \"State of charge of the battery. Values range from 0.0 (depleted) to 1.0 (fully charged)\", \"minimum\": 0.0, \"maximum\": 1.0, \"type\": \"number\" }, \"issues\": { \"title\": \"Issues\", \"description\": \"A list of issues with the robot that operators need to address\", \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/Issue\" } }, \"commission\": { \"$ref\": \"#/definitions/Commission\" }, \"mutex_groups\": { \"title\": \"Mutex Groups\", \"description\": \"Information about the mutex groups that this robot is interacting with\", \"allOf\": [ { \"$ref\": \"#/definitions/MutexGroups\" } ] } } } } } ``` ### /fleets/{name}/log ``` { \"title\": \"FleetLog\", \"type\": \"object\", \"properties\": { \"name\": { \"title\": \"Name\", \"type\": \"string\" }, \"log\": { \"title\": \"Log\", \"description\": \"Log for the overall fleet\", \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/LogEntry\" } }, \"robots\": { \"title\": \"Robots\", \"description\": \"Dictionary of logs for the individual robots. The keys (property names) are the robot names.\", \"type\": \"object\", \"additionalProperties\": { \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/LogEntry\" } } } }, \"definitions\": { \"Tier\": { \"title\": \"Tier\", \"description\": \"An enumeration.\", \"enum\": [ \"uninitialized\", \"info\", \"warning\", \"error\" ] }, \"LogEntry\": { \"title\": \"LogEntry\", \"type\": \"object\", \"properties\": { \"seq\": { \"title\": \"Seq\", \"description\": \"Sequence number for this entry. Each entry has a unique sequence number which monotonically increase, until integer overflow causes a wrap around.\", \"exclusiveMaximum\": 4294967296, \"minimum\": 0, \"type\": \"integer\" }, \"tier\": { \"description\": \"The importance level of the log entry\", \"allOf\": [ { \"$ref\": \"#/definitions/Tier\" } ] }, \"unix_millis_time\": { \"title\": \"Unix Millis Time\", \"type\": \"integer\" }, \"text\": { \"title\": \"Text\", \"description\": \"The text of the log entry\", \"type\": \"string\" } }, \"required\": [ \"seq\", \"tier\", \"unix_millis_time\", \"text\" ] } } } ``` * @summary Socket.io endpoint * @param {*} [options] Override http request option. * @throws {RequiredError} @@ -6108,7 +6096,7 @@ export class DefaultApi extends BaseAPI { } /** - * # NOTE: This endpoint is here for documentation purposes only, this is _not_ a REST endpoint. ## About This exposes a minimal pubsub system built on top of socket.io. It works similar to a normal socket.io endpoint, except that are 2 special rooms which control subscriptions. ## Rooms ### subscribe Clients must send a message to this room to start receiving messages on other rooms. The message must be of the form: ``` { \"room\": \"\" } ``` ### unsubscribe Clients can send a message to this room to stop receiving messages on other rooms. The message must be of the form: ``` { \"room\": \"\" } ``` ### /alerts ``` { \"title\": \"Alert\", \"description\": \"General alert that can be triggered by events.\", \"type\": \"object\", \"properties\": { \"id\": { \"title\": \"Id\", \"maxLength\": 255, \"type\": \"string\" }, \"original_id\": { \"title\": \"Original Id\", \"maxLength\": 255, \"type\": \"string\" }, \"category\": { \"title\": \"Category\", \"description\": \"Default: default
Task: task
Fleet: fleet
Robot: robot\", \"maxLength\": 7, \"type\": \"string\" }, \"unix_millis_created_time\": { \"title\": \"Unix Millis Created Time\", \"minimum\": -9223372036854775808, \"maximum\": 9223372036854775807, \"type\": \"integer\" }, \"acknowledged_by\": { \"title\": \"Acknowledged By\", \"maxLength\": 255, \"nullable\": true, \"type\": \"string\" }, \"unix_millis_acknowledged_time\": { \"title\": \"Unix Millis Acknowledged Time\", \"minimum\": -9223372036854775808, \"maximum\": 9223372036854775807, \"nullable\": true, \"type\": \"integer\" } }, \"required\": [ \"id\", \"original_id\", \"category\", \"unix_millis_created_time\" ], \"additionalProperties\": false } ``` ### /beacons ``` { \"title\": \"BeaconState\", \"type\": \"object\", \"properties\": { \"id\": { \"title\": \"Id\", \"maxLength\": 255, \"type\": \"string\" }, \"online\": { \"title\": \"Online\", \"type\": \"boolean\" }, \"category\": { \"title\": \"Category\", \"maxLength\": 255, \"nullable\": true, \"type\": \"string\" }, \"activated\": { \"title\": \"Activated\", \"type\": \"boolean\" }, \"level\": { \"title\": \"Level\", \"maxLength\": 255, \"nullable\": true, \"type\": \"string\" } }, \"required\": [ \"id\", \"online\", \"activated\" ], \"additionalProperties\": false } ``` ### /building_map ``` { \"title\": \"BuildingMap\", \"type\": \"object\", \"properties\": { \"name\": { \"title\": \"Name\", \"default\": \"\", \"type\": \"string\" }, \"levels\": { \"title\": \"Levels\", \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/Level\" } }, \"lifts\": { \"title\": \"Lifts\", \"default\": [], \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/Lift\" } } }, \"required\": [ \"name\", \"levels\", \"lifts\" ], \"definitions\": { \"AffineImage\": { \"title\": \"AffineImage\", \"type\": \"object\", \"properties\": { \"name\": { \"title\": \"Name\", \"default\": \"\", \"type\": \"string\" }, \"x_offset\": { \"title\": \"X Offset\", \"default\": 0, \"type\": \"number\" }, \"y_offset\": { \"title\": \"Y Offset\", \"default\": 0, \"type\": \"number\" }, \"yaw\": { \"title\": \"Yaw\", \"default\": 0, \"type\": \"number\" }, \"scale\": { \"title\": \"Scale\", \"default\": 0, \"type\": \"number\" }, \"encoding\": { \"title\": \"Encoding\", \"default\": \"\", \"type\": \"string\" }, \"data\": { \"title\": \"Data\", \"type\": \"string\" } }, \"required\": [ \"name\", \"x_offset\", \"y_offset\", \"yaw\", \"scale\", \"encoding\", \"data\" ] }, \"Place\": { \"title\": \"Place\", \"type\": \"object\", \"properties\": { \"name\": { \"title\": \"Name\", \"default\": \"\", \"type\": \"string\" }, \"x\": { \"title\": \"X\", \"default\": 0, \"type\": \"number\" }, \"y\": { \"title\": \"Y\", \"default\": 0, \"type\": \"number\" }, \"yaw\": { \"title\": \"Yaw\", \"default\": 0, \"type\": \"number\" }, \"position_tolerance\": { \"title\": \"Position Tolerance\", \"default\": 0, \"type\": \"number\" }, \"yaw_tolerance\": { \"title\": \"Yaw Tolerance\", \"default\": 0, \"type\": \"number\" } }, \"required\": [ \"name\", \"x\", \"y\", \"yaw\", \"position_tolerance\", \"yaw_tolerance\" ] }, \"Door\": { \"title\": \"Door\", \"type\": \"object\", \"properties\": { \"name\": { \"title\": \"Name\", \"default\": \"\", \"type\": \"string\" }, \"v1_x\": { \"title\": \"V1 X\", \"default\": 0, \"type\": \"number\" }, \"v1_y\": { \"title\": \"V1 Y\", \"default\": 0, \"type\": \"number\" }, \"v2_x\": { \"title\": \"V2 X\", \"default\": 0, \"type\": \"number\" }, \"v2_y\": { \"title\": \"V2 Y\", \"default\": 0, \"type\": \"number\" }, \"door_type\": { \"title\": \"Door Type\", \"default\": 0, \"minimum\": 0, \"maximum\": 255, \"type\": \"integer\" }, \"motion_range\": { \"title\": \"Motion Range\", \"default\": 0, \"type\": \"number\" }, \"motion_direction\": { \"title\": \"Motion Direction\", \"default\": 0, \"minimum\": -2147483648, \"maximum\": 2147483647, \"type\": \"integer\" } }, \"required\": [ \"name\", \"v1_x\", \"v1_y\", \"v2_x\", \"v2_y\", \"door_type\", \"motion_range\", \"motion_direction\" ] }, \"Param\": { \"title\": \"Param\", \"type\": \"object\", \"properties\": { \"name\": { \"title\": \"Name\", \"default\": \"\", \"type\": \"string\" }, \"type\": { \"title\": \"Type\", \"default\": 0, \"minimum\": 0, \"maximum\": 4294967295, \"type\": \"integer\" }, \"value_int\": { \"title\": \"Value Int\", \"default\": 0, \"minimum\": -2147483648, \"maximum\": 2147483647, \"type\": \"integer\" }, \"value_float\": { \"title\": \"Value Float\", \"default\": 0, \"type\": \"number\" }, \"value_string\": { \"title\": \"Value String\", \"default\": \"\", \"type\": \"string\" }, \"value_bool\": { \"title\": \"Value Bool\", \"default\": false, \"type\": \"boolean\" } }, \"required\": [ \"name\", \"type\", \"value_int\", \"value_float\", \"value_string\", \"value_bool\" ] }, \"GraphNode\": { \"title\": \"GraphNode\", \"type\": \"object\", \"properties\": { \"x\": { \"title\": \"X\", \"default\": 0, \"type\": \"number\" }, \"y\": { \"title\": \"Y\", \"default\": 0, \"type\": \"number\" }, \"name\": { \"title\": \"Name\", \"default\": \"\", \"type\": \"string\" }, \"params\": { \"title\": \"Params\", \"default\": [], \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/Param\" } } }, \"required\": [ \"x\", \"y\", \"name\", \"params\" ] }, \"GraphEdge\": { \"title\": \"GraphEdge\", \"type\": \"object\", \"properties\": { \"v1_idx\": { \"title\": \"V1 Idx\", \"default\": 0, \"minimum\": 0, \"maximum\": 4294967295, \"type\": \"integer\" }, \"v2_idx\": { \"title\": \"V2 Idx\", \"default\": 0, \"minimum\": 0, \"maximum\": 4294967295, \"type\": \"integer\" }, \"params\": { \"title\": \"Params\", \"default\": [], \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/Param\" } }, \"edge_type\": { \"title\": \"Edge Type\", \"default\": 0, \"minimum\": 0, \"maximum\": 255, \"type\": \"integer\" } }, \"required\": [ \"v1_idx\", \"v2_idx\", \"params\", \"edge_type\" ] }, \"Graph\": { \"title\": \"Graph\", \"type\": \"object\", \"properties\": { \"name\": { \"title\": \"Name\", \"default\": \"\", \"type\": \"string\" }, \"vertices\": { \"title\": \"Vertices\", \"default\": [], \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/GraphNode\" } }, \"edges\": { \"title\": \"Edges\", \"default\": [], \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/GraphEdge\" } }, \"params\": { \"title\": \"Params\", \"default\": [], \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/Param\" } } }, \"required\": [ \"name\", \"vertices\", \"edges\", \"params\" ] }, \"Level\": { \"title\": \"Level\", \"type\": \"object\", \"properties\": { \"name\": { \"title\": \"Name\", \"default\": \"\", \"type\": \"string\" }, \"elevation\": { \"title\": \"Elevation\", \"default\": 0, \"type\": \"number\" }, \"images\": { \"title\": \"Images\", \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/AffineImage\" } }, \"places\": { \"title\": \"Places\", \"default\": [], \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/Place\" } }, \"doors\": { \"title\": \"Doors\", \"default\": [], \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/Door\" } }, \"nav_graphs\": { \"title\": \"Nav Graphs\", \"default\": [], \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/Graph\" } }, \"wall_graph\": { \"title\": \"Wall Graph\", \"default\": { \"name\": \"\", \"vertices\": [], \"edges\": [], \"params\": [] }, \"allOf\": [ { \"$ref\": \"#/definitions/Graph\" } ] } }, \"required\": [ \"name\", \"elevation\", \"images\", \"places\", \"doors\", \"nav_graphs\", \"wall_graph\" ] }, \"Lift\": { \"title\": \"Lift\", \"type\": \"object\", \"properties\": { \"name\": { \"title\": \"Name\", \"default\": \"\", \"type\": \"string\" }, \"levels\": { \"title\": \"Levels\", \"default\": [], \"type\": \"array\", \"items\": { \"type\": \"string\" } }, \"doors\": { \"title\": \"Doors\", \"default\": [], \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/Door\" } }, \"wall_graph\": { \"title\": \"Wall Graph\", \"default\": { \"name\": \"\", \"vertices\": [], \"edges\": [], \"params\": [] }, \"allOf\": [ { \"$ref\": \"#/definitions/Graph\" } ] }, \"ref_x\": { \"title\": \"Ref X\", \"default\": 0, \"type\": \"number\" }, \"ref_y\": { \"title\": \"Ref Y\", \"default\": 0, \"type\": \"number\" }, \"ref_yaw\": { \"title\": \"Ref Yaw\", \"default\": 0, \"type\": \"number\" }, \"width\": { \"title\": \"Width\", \"default\": 0, \"type\": \"number\" }, \"depth\": { \"title\": \"Depth\", \"default\": 0, \"type\": \"number\" } }, \"required\": [ \"name\", \"levels\", \"doors\", \"wall_graph\", \"ref_x\", \"ref_y\", \"ref_yaw\", \"width\", \"depth\" ] } } } ``` ### /building_map/fire_alarm_trigger ``` { \"title\": \"FireAlarmTriggerState\", \"type\": \"object\", \"properties\": { \"unix_millis_time\": { \"title\": \"Unix Millis Time\", \"type\": \"integer\" }, \"trigger\": { \"title\": \"Trigger\", \"type\": \"boolean\" } }, \"required\": [ \"unix_millis_time\", \"trigger\" ] } ``` ### /delivery_alerts ``` { \"title\": \"DeliveryAlert\", \"type\": \"object\", \"properties\": { \"id\": { \"title\": \"Id\", \"type\": \"string\" }, \"category\": { \"$ref\": \"#/definitions/Category\" }, \"tier\": { \"$ref\": \"#/definitions/Tier\" }, \"action\": { \"$ref\": \"#/definitions/Action\" }, \"task_id\": { \"title\": \"Task Id\", \"type\": \"string\" }, \"message\": { \"title\": \"Message\", \"type\": \"string\" } }, \"required\": [ \"id\", \"category\", \"tier\", \"action\", \"task_id\", \"message\" ], \"definitions\": { \"Category\": { \"title\": \"Category\", \"description\": \"An enumeration.\", \"enum\": [ \"missing\", \"wrong\", \"obstructed\", \"cancelled\" ], \"type\": \"string\" }, \"Tier\": { \"title\": \"Tier\", \"description\": \"An enumeration.\", \"enum\": [ \"warning\", \"error\" ], \"type\": \"string\" }, \"Action\": { \"title\": \"Action\", \"description\": \"An enumeration.\", \"enum\": [ \"waiting\", \"cancelled\", \"override\", \"resume\" ], \"type\": \"string\" } } } ``` ### /doors/{door_name}/state ``` { \"title\": \"DoorState\", \"type\": \"object\", \"properties\": { \"door_time\": { \"title\": \"Door Time\", \"default\": { \"sec\": 0, \"nanosec\": 0 }, \"allOf\": [ { \"$ref\": \"#/definitions/Time\" } ] }, \"door_name\": { \"title\": \"Door Name\", \"default\": \"\", \"type\": \"string\" }, \"current_mode\": { \"title\": \"Current Mode\", \"default\": { \"value\": 0 }, \"allOf\": [ { \"$ref\": \"#/definitions/DoorMode\" } ] } }, \"required\": [ \"door_time\", \"door_name\", \"current_mode\" ], \"definitions\": { \"Time\": { \"title\": \"Time\", \"type\": \"object\", \"properties\": { \"sec\": { \"title\": \"Sec\", \"default\": 0, \"minimum\": -2147483648, \"maximum\": 2147483647, \"type\": \"integer\" }, \"nanosec\": { \"title\": \"Nanosec\", \"default\": 0, \"minimum\": 0, \"maximum\": 4294967295, \"type\": \"integer\" } }, \"required\": [ \"sec\", \"nanosec\" ] }, \"DoorMode\": { \"title\": \"DoorMode\", \"type\": \"object\", \"properties\": { \"value\": { \"title\": \"Value\", \"default\": 0, \"minimum\": 0, \"maximum\": 4294967295, \"type\": \"integer\" } }, \"required\": [ \"value\" ] } } } ``` ### /doors/{door_name}/health ``` { \"title\": \"DoorHealth\", \"type\": \"object\", \"properties\": { \"health_status\": { \"title\": \"Health Status\", \"maxLength\": 255, \"nullable\": true, \"type\": \"string\" }, \"health_message\": { \"title\": \"Health Message\", \"nullable\": true, \"type\": \"string\" }, \"id_\": { \"title\": \"Id \", \"maxLength\": 255, \"type\": \"string\" } }, \"required\": [ \"health_status\", \"id_\" ], \"additionalProperties\": false } ``` ### /lifts/{lift_name}/state ``` { \"title\": \"LiftState\", \"type\": \"object\", \"properties\": { \"lift_time\": { \"title\": \"Lift Time\", \"default\": { \"sec\": 0, \"nanosec\": 0 }, \"allOf\": [ { \"$ref\": \"#/definitions/Time\" } ] }, \"lift_name\": { \"title\": \"Lift Name\", \"default\": \"\", \"type\": \"string\" }, \"available_floors\": { \"title\": \"Available Floors\", \"default\": [], \"type\": \"array\", \"items\": { \"type\": \"string\" } }, \"current_floor\": { \"title\": \"Current Floor\", \"default\": \"\", \"type\": \"string\" }, \"destination_floor\": { \"title\": \"Destination Floor\", \"default\": \"\", \"type\": \"string\" }, \"door_state\": { \"title\": \"Door State\", \"default\": 0, \"minimum\": 0, \"maximum\": 255, \"type\": \"integer\" }, \"motion_state\": { \"title\": \"Motion State\", \"default\": 0, \"minimum\": 0, \"maximum\": 255, \"type\": \"integer\" }, \"available_modes\": { \"title\": \"Available Modes\", \"type\": \"array\", \"items\": { \"type\": \"integer\" } }, \"current_mode\": { \"title\": \"Current Mode\", \"default\": 0, \"minimum\": 0, \"maximum\": 255, \"type\": \"integer\" }, \"session_id\": { \"title\": \"Session Id\", \"default\": \"\", \"type\": \"string\" } }, \"required\": [ \"lift_time\", \"lift_name\", \"available_floors\", \"current_floor\", \"destination_floor\", \"door_state\", \"motion_state\", \"available_modes\", \"current_mode\", \"session_id\" ], \"definitions\": { \"Time\": { \"title\": \"Time\", \"type\": \"object\", \"properties\": { \"sec\": { \"title\": \"Sec\", \"default\": 0, \"minimum\": -2147483648, \"maximum\": 2147483647, \"type\": \"integer\" }, \"nanosec\": { \"title\": \"Nanosec\", \"default\": 0, \"minimum\": 0, \"maximum\": 4294967295, \"type\": \"integer\" } }, \"required\": [ \"sec\", \"nanosec\" ] } } } ``` ### /lifts/{lift_name}/health ``` { \"title\": \"LiftHealth\", \"type\": \"object\", \"properties\": { \"health_status\": { \"title\": \"Health Status\", \"maxLength\": 255, \"nullable\": true, \"type\": \"string\" }, \"health_message\": { \"title\": \"Health Message\", \"nullable\": true, \"type\": \"string\" }, \"id_\": { \"title\": \"Id \", \"maxLength\": 255, \"type\": \"string\" } }, \"required\": [ \"health_status\", \"id_\" ], \"additionalProperties\": false } ``` ### /tasks/{task_id}/state ``` { \"title\": \"TaskState\", \"type\": \"object\", \"properties\": { \"booking\": { \"$ref\": \"#/definitions/Booking\" }, \"category\": { \"$ref\": \"#/definitions/Category\" }, \"detail\": { \"$ref\": \"#/definitions/Detail\" }, \"unix_millis_start_time\": { \"title\": \"Unix Millis Start Time\", \"type\": \"integer\" }, \"unix_millis_finish_time\": { \"title\": \"Unix Millis Finish Time\", \"type\": \"integer\" }, \"original_estimate_millis\": { \"$ref\": \"#/definitions/EstimateMillis\" }, \"estimate_millis\": { \"$ref\": \"#/definitions/EstimateMillis\" }, \"assigned_to\": { \"title\": \"Assigned To\", \"description\": \"Which agent (robot) is the task assigned to\", \"allOf\": [ { \"$ref\": \"#/definitions/AssignedTo\" } ] }, \"status\": { \"$ref\": \"#/definitions/Status\" }, \"dispatch\": { \"$ref\": \"#/definitions/Dispatch\" }, \"phases\": { \"title\": \"Phases\", \"description\": \"A dictionary of the states of the phases of the task. The keys (property names) are phase IDs, which are integers.\", \"type\": \"object\", \"additionalProperties\": { \"$ref\": \"#/definitions/Phase\" } }, \"completed\": { \"title\": \"Completed\", \"description\": \"An array of the IDs of completed phases of this task\", \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/Id\" } }, \"active\": { \"title\": \"Active\", \"description\": \"The ID of the active phase for this task\", \"allOf\": [ { \"$ref\": \"#/definitions/Id\" } ] }, \"pending\": { \"title\": \"Pending\", \"description\": \"An array of the pending phases of this task\", \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/Id\" } }, \"interruptions\": { \"title\": \"Interruptions\", \"description\": \"A dictionary of interruptions that have been applied to this task. The keys (property names) are the unique token of the interruption request.\", \"type\": \"object\", \"additionalProperties\": { \"$ref\": \"#/definitions/Interruption\" } }, \"cancellation\": { \"title\": \"Cancellation\", \"description\": \"If the task was cancelled, this will describe information about the request.\", \"allOf\": [ { \"$ref\": \"#/definitions/Cancellation\" } ] }, \"killed\": { \"title\": \"Killed\", \"description\": \"If the task was killed, this will describe information about the request.\", \"allOf\": [ { \"$ref\": \"#/definitions/Killed\" } ] }, \"unix_millis_warn_time\": { \"title\": \"Unix Millis Warn Time\", \"type\": \"integer\" } }, \"required\": [ \"booking\" ], \"definitions\": { \"Booking\": { \"title\": \"Booking\", \"type\": \"object\", \"properties\": { \"id\": { \"title\": \"Id\", \"description\": \"The unique identifier for this task\", \"type\": \"string\" }, \"unix_millis_earliest_start_time\": { \"title\": \"Unix Millis Earliest Start Time\", \"type\": \"integer\" }, \"unix_millis_request_time\": { \"title\": \"Unix Millis Request Time\", \"type\": \"integer\" }, \"priority\": { \"title\": \"Priority\", \"description\": \"Priority information about this task\", \"anyOf\": [ { \"type\": \"object\" }, { \"type\": \"string\" } ] }, \"labels\": { \"title\": \"Labels\", \"description\": \"Information about how and why this task was booked\", \"type\": \"array\", \"items\": { \"type\": \"string\" } }, \"requester\": { \"title\": \"Requester\", \"description\": \"(Optional) An identifier for the entity that requested this task\", \"type\": \"string\" } }, \"required\": [ \"id\" ] }, \"Category\": { \"title\": \"Category\", \"description\": \"The category of this task or phase\", \"type\": \"string\" }, \"Detail\": { \"title\": \"Detail\", \"description\": \"Detailed information about a task, phase, or event\", \"anyOf\": [ { \"type\": \"object\" }, { \"type\": \"array\", \"items\": {} }, { \"type\": \"string\" } ] }, \"EstimateMillis\": { \"title\": \"EstimateMillis\", \"description\": \"An estimate, in milliseconds, of how long the subject will take to complete\", \"minimum\": 0, \"type\": \"integer\" }, \"AssignedTo\": { \"title\": \"AssignedTo\", \"type\": \"object\", \"properties\": { \"group\": { \"title\": \"Group\", \"type\": \"string\" }, \"name\": { \"title\": \"Name\", \"type\": \"string\" } }, \"required\": [ \"group\", \"name\" ] }, \"Status\": { \"title\": \"Status\", \"description\": \"An enumeration.\", \"enum\": [ \"uninitialized\", \"blocked\", \"error\", \"failed\", \"queued\", \"standby\", \"underway\", \"delayed\", \"skipped\", \"canceled\", \"killed\", \"completed\" ] }, \"Status1\": { \"title\": \"Status1\", \"description\": \"An enumeration.\", \"enum\": [ \"queued\", \"selected\", \"dispatched\", \"failed_to_assign\", \"canceled_in_flight\" ] }, \"Assignment\": { \"title\": \"Assignment\", \"type\": \"object\", \"properties\": { \"fleet_name\": { \"title\": \"Fleet Name\", \"type\": \"string\" }, \"expected_robot_name\": { \"title\": \"Expected Robot Name\", \"type\": \"string\" } } }, \"Error\": { \"title\": \"Error\", \"type\": \"object\", \"properties\": { \"code\": { \"title\": \"Code\", \"description\": \"A standard code for the kind of error that has occurred\", \"minimum\": 0, \"type\": \"integer\" }, \"category\": { \"title\": \"Category\", \"description\": \"The category of the error\", \"type\": \"string\" }, \"detail\": { \"title\": \"Detail\", \"description\": \"Details about the error\", \"type\": \"string\" } } }, \"Dispatch\": { \"title\": \"Dispatch\", \"type\": \"object\", \"properties\": { \"status\": { \"$ref\": \"#/definitions/Status1\" }, \"assignment\": { \"$ref\": \"#/definitions/Assignment\" }, \"errors\": { \"title\": \"Errors\", \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/Error\" } } }, \"required\": [ \"status\" ] }, \"Id\": { \"title\": \"Id\", \"minimum\": 0, \"type\": \"integer\" }, \"EventState\": { \"title\": \"EventState\", \"type\": \"object\", \"properties\": { \"id\": { \"$ref\": \"#/definitions/Id\" }, \"status\": { \"$ref\": \"#/definitions/Status\" }, \"name\": { \"title\": \"Name\", \"description\": \"The brief name of the event\", \"type\": \"string\" }, \"detail\": { \"title\": \"Detail\", \"description\": \"Detailed information about the event\", \"allOf\": [ { \"$ref\": \"#/definitions/Detail\" } ] }, \"deps\": { \"title\": \"Deps\", \"description\": \"This event may depend on other events. This array contains the IDs of those other event dependencies.\", \"type\": \"array\", \"items\": { \"type\": \"integer\", \"minimum\": 0 } } }, \"required\": [ \"id\" ] }, \"Undo\": { \"title\": \"Undo\", \"type\": \"object\", \"properties\": { \"unix_millis_request_time\": { \"title\": \"Unix Millis Request Time\", \"description\": \"The time that the undo skip request arrived\", \"type\": \"integer\" }, \"labels\": { \"title\": \"Labels\", \"description\": \"Labels to describe the undo skip request\", \"type\": \"array\", \"items\": { \"type\": \"string\" } } }, \"required\": [ \"unix_millis_request_time\", \"labels\" ] }, \"SkipPhaseRequest\": { \"title\": \"SkipPhaseRequest\", \"type\": \"object\", \"properties\": { \"unix_millis_request_time\": { \"title\": \"Unix Millis Request Time\", \"description\": \"The time that the skip request arrived\", \"type\": \"integer\" }, \"labels\": { \"title\": \"Labels\", \"description\": \"Labels to describe the purpose of the skip request\", \"type\": \"array\", \"items\": { \"type\": \"string\" } }, \"undo\": { \"title\": \"Undo\", \"description\": \"Information about an undo skip request that applied to this request\", \"allOf\": [ { \"$ref\": \"#/definitions/Undo\" } ] } }, \"required\": [ \"unix_millis_request_time\", \"labels\" ] }, \"Phase\": { \"title\": \"Phase\", \"type\": \"object\", \"properties\": { \"id\": { \"$ref\": \"#/definitions/Id\" }, \"category\": { \"$ref\": \"#/definitions/Category\" }, \"detail\": { \"$ref\": \"#/definitions/Detail\" }, \"unix_millis_start_time\": { \"title\": \"Unix Millis Start Time\", \"type\": \"integer\" }, \"unix_millis_finish_time\": { \"title\": \"Unix Millis Finish Time\", \"type\": \"integer\" }, \"original_estimate_millis\": { \"$ref\": \"#/definitions/EstimateMillis\" }, \"estimate_millis\": { \"$ref\": \"#/definitions/EstimateMillis\" }, \"final_event_id\": { \"$ref\": \"#/definitions/Id\" }, \"events\": { \"title\": \"Events\", \"description\": \"A dictionary of events for this phase. The keys (property names) are the event IDs, which are integers.\", \"type\": \"object\", \"additionalProperties\": { \"$ref\": \"#/definitions/EventState\" } }, \"skip_requests\": { \"title\": \"Skip Requests\", \"description\": \"Information about any skip requests that have been received\", \"type\": \"object\", \"additionalProperties\": { \"$ref\": \"#/definitions/SkipPhaseRequest\" } } }, \"required\": [ \"id\" ] }, \"ResumedBy\": { \"title\": \"ResumedBy\", \"type\": \"object\", \"properties\": { \"unix_millis_request_time\": { \"title\": \"Unix Millis Request Time\", \"description\": \"The time that the resume request arrived\", \"type\": \"integer\" }, \"labels\": { \"title\": \"Labels\", \"description\": \"Labels to describe the resume request\", \"type\": \"array\", \"items\": { \"type\": \"string\" } } }, \"required\": [ \"labels\" ] }, \"Interruption\": { \"title\": \"Interruption\", \"type\": \"object\", \"properties\": { \"unix_millis_request_time\": { \"title\": \"Unix Millis Request Time\", \"description\": \"The time that the interruption request arrived\", \"type\": \"integer\" }, \"labels\": { \"title\": \"Labels\", \"description\": \"Labels to describe the purpose of the interruption\", \"type\": \"array\", \"items\": { \"type\": \"string\" } }, \"resumed_by\": { \"title\": \"Resumed By\", \"description\": \"Information about the resume request that ended this interruption. This field will be missing if the interruption is still active.\", \"allOf\": [ { \"$ref\": \"#/definitions/ResumedBy\" } ] } }, \"required\": [ \"unix_millis_request_time\", \"labels\" ] }, \"Cancellation\": { \"title\": \"Cancellation\", \"type\": \"object\", \"properties\": { \"unix_millis_request_time\": { \"title\": \"Unix Millis Request Time\", \"description\": \"The time that the cancellation request arrived\", \"type\": \"integer\" }, \"labels\": { \"title\": \"Labels\", \"description\": \"Labels to describe the cancel request\", \"type\": \"array\", \"items\": { \"type\": \"string\" } } }, \"required\": [ \"unix_millis_request_time\", \"labels\" ] }, \"Killed\": { \"title\": \"Killed\", \"type\": \"object\", \"properties\": { \"unix_millis_request_time\": { \"title\": \"Unix Millis Request Time\", \"description\": \"The time that the cancellation request arrived\", \"type\": \"integer\" }, \"labels\": { \"title\": \"Labels\", \"description\": \"Labels to describe the kill request\", \"type\": \"array\", \"items\": { \"type\": \"string\" } } }, \"required\": [ \"unix_millis_request_time\", \"labels\" ] } } } ``` ### /tasks/{task_id}/log ``` { \"title\": \"TaskEventLog\", \"type\": \"object\", \"properties\": { \"task_id\": { \"title\": \"Task Id\", \"type\": \"string\" }, \"log\": { \"title\": \"Log\", \"description\": \"Log entries related to the overall task\", \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/LogEntry\" } }, \"phases\": { \"title\": \"Phases\", \"description\": \"A dictionary whose keys (property names) are the indices of a phase\", \"type\": \"object\", \"additionalProperties\": { \"$ref\": \"#/definitions/Phases\" } } }, \"required\": [ \"task_id\" ], \"additionalProperties\": false, \"definitions\": { \"Tier\": { \"title\": \"Tier\", \"description\": \"An enumeration.\", \"enum\": [ \"uninitialized\", \"info\", \"warning\", \"error\" ] }, \"LogEntry\": { \"title\": \"LogEntry\", \"type\": \"object\", \"properties\": { \"seq\": { \"title\": \"Seq\", \"description\": \"Sequence number for this entry. Each entry has a unique sequence number which monotonically increase, until integer overflow causes a wrap around.\", \"exclusiveMaximum\": 4294967296, \"minimum\": 0, \"type\": \"integer\" }, \"tier\": { \"description\": \"The importance level of the log entry\", \"allOf\": [ { \"$ref\": \"#/definitions/Tier\" } ] }, \"unix_millis_time\": { \"title\": \"Unix Millis Time\", \"type\": \"integer\" }, \"text\": { \"title\": \"Text\", \"description\": \"The text of the log entry\", \"type\": \"string\" } }, \"required\": [ \"seq\", \"tier\", \"unix_millis_time\", \"text\" ] }, \"Phases\": { \"title\": \"Phases\", \"type\": \"object\", \"properties\": { \"log\": { \"title\": \"Log\", \"description\": \"Log entries related to the overall phase\", \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/LogEntry\" } }, \"events\": { \"title\": \"Events\", \"description\": \"A dictionary whose keys (property names) are the indices of an event in the phase\", \"type\": \"object\", \"additionalProperties\": { \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/LogEntry\" } } } }, \"additionalProperties\": false } } } ``` ### /dispensers/{guid}/state ``` { \"title\": \"DispenserState\", \"type\": \"object\", \"properties\": { \"time\": { \"title\": \"Time\", \"default\": { \"sec\": 0, \"nanosec\": 0 }, \"allOf\": [ { \"$ref\": \"#/definitions/Time\" } ] }, \"guid\": { \"title\": \"Guid\", \"default\": \"\", \"type\": \"string\" }, \"mode\": { \"title\": \"Mode\", \"default\": 0, \"minimum\": -2147483648, \"maximum\": 2147483647, \"type\": \"integer\" }, \"request_guid_queue\": { \"title\": \"Request Guid Queue\", \"default\": [], \"type\": \"array\", \"items\": { \"type\": \"string\" } }, \"seconds_remaining\": { \"title\": \"Seconds Remaining\", \"default\": 0, \"type\": \"number\" } }, \"required\": [ \"time\", \"guid\", \"mode\", \"request_guid_queue\", \"seconds_remaining\" ], \"definitions\": { \"Time\": { \"title\": \"Time\", \"type\": \"object\", \"properties\": { \"sec\": { \"title\": \"Sec\", \"default\": 0, \"minimum\": -2147483648, \"maximum\": 2147483647, \"type\": \"integer\" }, \"nanosec\": { \"title\": \"Nanosec\", \"default\": 0, \"minimum\": 0, \"maximum\": 4294967295, \"type\": \"integer\" } }, \"required\": [ \"sec\", \"nanosec\" ] } } } ``` ### /dispensers/{guid}/health ``` { \"title\": \"DispenserHealth\", \"type\": \"object\", \"properties\": { \"health_status\": { \"title\": \"Health Status\", \"maxLength\": 255, \"nullable\": true, \"type\": \"string\" }, \"health_message\": { \"title\": \"Health Message\", \"nullable\": true, \"type\": \"string\" }, \"id_\": { \"title\": \"Id \", \"maxLength\": 255, \"type\": \"string\" } }, \"required\": [ \"health_status\", \"id_\" ], \"additionalProperties\": false } ``` ### /ingestors/{guid}/state ``` { \"title\": \"IngestorState\", \"type\": \"object\", \"properties\": { \"time\": { \"title\": \"Time\", \"default\": { \"sec\": 0, \"nanosec\": 0 }, \"allOf\": [ { \"$ref\": \"#/definitions/Time\" } ] }, \"guid\": { \"title\": \"Guid\", \"default\": \"\", \"type\": \"string\" }, \"mode\": { \"title\": \"Mode\", \"default\": 0, \"minimum\": -2147483648, \"maximum\": 2147483647, \"type\": \"integer\" }, \"request_guid_queue\": { \"title\": \"Request Guid Queue\", \"default\": [], \"type\": \"array\", \"items\": { \"type\": \"string\" } }, \"seconds_remaining\": { \"title\": \"Seconds Remaining\", \"default\": 0, \"type\": \"number\" } }, \"required\": [ \"time\", \"guid\", \"mode\", \"request_guid_queue\", \"seconds_remaining\" ], \"definitions\": { \"Time\": { \"title\": \"Time\", \"type\": \"object\", \"properties\": { \"sec\": { \"title\": \"Sec\", \"default\": 0, \"minimum\": -2147483648, \"maximum\": 2147483647, \"type\": \"integer\" }, \"nanosec\": { \"title\": \"Nanosec\", \"default\": 0, \"minimum\": 0, \"maximum\": 4294967295, \"type\": \"integer\" } }, \"required\": [ \"sec\", \"nanosec\" ] } } } ``` ### /ingestors/{guid}/health ``` { \"title\": \"IngestorHealth\", \"type\": \"object\", \"properties\": { \"health_status\": { \"title\": \"Health Status\", \"maxLength\": 255, \"nullable\": true, \"type\": \"string\" }, \"health_message\": { \"title\": \"Health Message\", \"nullable\": true, \"type\": \"string\" }, \"id_\": { \"title\": \"Id \", \"maxLength\": 255, \"type\": \"string\" } }, \"required\": [ \"health_status\", \"id_\" ], \"additionalProperties\": false } ``` ### /fleets/{name}/state ``` { \"title\": \"FleetState\", \"type\": \"object\", \"properties\": { \"name\": { \"title\": \"Name\", \"type\": \"string\" }, \"robots\": { \"title\": \"Robots\", \"description\": \"A dictionary of the states of the robots that belong to this fleet\", \"type\": \"object\", \"additionalProperties\": { \"$ref\": \"#/definitions/RobotState\" } } }, \"definitions\": { \"Status\": { \"title\": \"Status\", \"description\": \"An enumeration.\", \"enum\": [ \"uninitialized\", \"offline\", \"shutdown\", \"idle\", \"charging\", \"working\", \"error\" ] }, \"Location2D\": { \"title\": \"Location2D\", \"type\": \"object\", \"properties\": { \"map\": { \"title\": \"Map\", \"type\": \"string\" }, \"x\": { \"title\": \"X\", \"type\": \"number\" }, \"y\": { \"title\": \"Y\", \"type\": \"number\" }, \"yaw\": { \"title\": \"Yaw\", \"type\": \"number\" } }, \"required\": [ \"map\", \"x\", \"y\", \"yaw\" ] }, \"Issue\": { \"title\": \"Issue\", \"type\": \"object\", \"properties\": { \"category\": { \"title\": \"Category\", \"description\": \"Category of the robot\'s issue\", \"type\": \"string\" }, \"detail\": { \"title\": \"Detail\", \"description\": \"Detailed information about the issue\", \"anyOf\": [ { \"type\": \"object\" }, { \"type\": \"array\", \"items\": {} }, { \"type\": \"string\" } ] } } }, \"Commission\": { \"title\": \"Commission\", \"type\": \"object\", \"properties\": { \"dispatch_tasks\": { \"title\": \"Dispatch Tasks\", \"description\": \"Should the robot accept dispatched tasks, true/false. When used in a request, leave this unset to not change the robot\'s current value.\", \"type\": \"boolean\" }, \"direct_tasks\": { \"title\": \"Direct Tasks\", \"description\": \"Should the robot accept direct task requests, true/false. When used in a request, leave this unset to not change the robot\'s current value.\", \"type\": \"boolean\" }, \"idle_behavior\": { \"title\": \"Idle Behavior\", \"description\": \"Should the robot perform its idle behavior, true/false. When used in a request, leave this unset to not change the robot\'s current value.\", \"type\": \"boolean\" } } }, \"MutexGroups\": { \"title\": \"MutexGroups\", \"type\": \"object\", \"properties\": { \"locked\": { \"title\": \"Locked\", \"description\": \"A list of mutex groups that this robot has currently locked\", \"type\": \"array\", \"items\": { \"type\": \"string\" } }, \"requesting\": { \"title\": \"Requesting\", \"description\": \"A list of the mutex groups that this robot is currently requesting but has not lockd yet\", \"type\": \"array\", \"items\": { \"type\": \"string\" } } } }, \"RobotState\": { \"title\": \"RobotState\", \"type\": \"object\", \"properties\": { \"name\": { \"title\": \"Name\", \"type\": \"string\" }, \"status\": { \"description\": \"A simple token representing the status of the robot\", \"allOf\": [ { \"$ref\": \"#/definitions/Status\" } ] }, \"task_id\": { \"title\": \"Task Id\", \"description\": \"The ID of the task this robot is currently working on. Empty string if the robot is not working on a task.\", \"type\": \"string\" }, \"unix_millis_time\": { \"title\": \"Unix Millis Time\", \"type\": \"integer\" }, \"location\": { \"$ref\": \"#/definitions/Location2D\" }, \"battery\": { \"title\": \"Battery\", \"description\": \"State of charge of the battery. Values range from 0.0 (depleted) to 1.0 (fully charged)\", \"minimum\": 0.0, \"maximum\": 1.0, \"type\": \"number\" }, \"issues\": { \"title\": \"Issues\", \"description\": \"A list of issues with the robot that operators need to address\", \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/Issue\" } }, \"commission\": { \"$ref\": \"#/definitions/Commission\" }, \"mutex_groups\": { \"title\": \"Mutex Groups\", \"description\": \"Information about the mutex groups that this robot is interacting with\", \"allOf\": [ { \"$ref\": \"#/definitions/MutexGroups\" } ] } } } } } ``` ### /fleets/{name}/log ``` { \"title\": \"FleetLog\", \"type\": \"object\", \"properties\": { \"name\": { \"title\": \"Name\", \"type\": \"string\" }, \"log\": { \"title\": \"Log\", \"description\": \"Log for the overall fleet\", \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/LogEntry\" } }, \"robots\": { \"title\": \"Robots\", \"description\": \"Dictionary of logs for the individual robots. The keys (property names) are the robot names.\", \"type\": \"object\", \"additionalProperties\": { \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/LogEntry\" } } } }, \"definitions\": { \"Tier\": { \"title\": \"Tier\", \"description\": \"An enumeration.\", \"enum\": [ \"uninitialized\", \"info\", \"warning\", \"error\" ] }, \"LogEntry\": { \"title\": \"LogEntry\", \"type\": \"object\", \"properties\": { \"seq\": { \"title\": \"Seq\", \"description\": \"Sequence number for this entry. Each entry has a unique sequence number which monotonically increase, until integer overflow causes a wrap around.\", \"exclusiveMaximum\": 4294967296, \"minimum\": 0, \"type\": \"integer\" }, \"tier\": { \"description\": \"The importance level of the log entry\", \"allOf\": [ { \"$ref\": \"#/definitions/Tier\" } ] }, \"unix_millis_time\": { \"title\": \"Unix Millis Time\", \"type\": \"integer\" }, \"text\": { \"title\": \"Text\", \"description\": \"The text of the log entry\", \"type\": \"string\" } }, \"required\": [ \"seq\", \"tier\", \"unix_millis_time\", \"text\" ] } } } ``` + * # NOTE: This endpoint is here for documentation purposes only, this is _not_ a REST endpoint. ## About This exposes a minimal pubsub system built on top of socket.io. It works similar to a normal socket.io endpoint, except that are 2 special rooms which control subscriptions. ## Rooms ### subscribe Clients must send a message to this room to start receiving messages on other rooms. The message must be of the form: ``` { \"room\": \"\" } ``` ### unsubscribe Clients can send a message to this room to stop receiving messages on other rooms. The message must be of the form: ``` { \"room\": \"\" } ``` ### /alerts ``` { \"title\": \"Alert\", \"description\": \"General alert that can be triggered by events.\", \"type\": \"object\", \"properties\": { \"id\": { \"title\": \"Id\", \"maxLength\": 255, \"type\": \"string\" }, \"original_id\": { \"title\": \"Original Id\", \"maxLength\": 255, \"type\": \"string\" }, \"category\": { \"title\": \"Category\", \"description\": \"Default: default
Task: task
Fleet: fleet
Robot: robot\", \"maxLength\": 7, \"type\": \"string\" }, \"unix_millis_created_time\": { \"title\": \"Unix Millis Created Time\", \"minimum\": -9223372036854775808, \"maximum\": 9223372036854775807, \"type\": \"integer\" }, \"acknowledged_by\": { \"title\": \"Acknowledged By\", \"maxLength\": 255, \"nullable\": true, \"type\": \"string\" }, \"unix_millis_acknowledged_time\": { \"title\": \"Unix Millis Acknowledged Time\", \"minimum\": -9223372036854775808, \"maximum\": 9223372036854775807, \"nullable\": true, \"type\": \"integer\" } }, \"required\": [ \"id\", \"original_id\", \"category\", \"unix_millis_created_time\" ], \"additionalProperties\": false } ``` ### /beacons ``` { \"title\": \"BeaconState\", \"type\": \"object\", \"properties\": { \"id\": { \"title\": \"Id\", \"maxLength\": 255, \"type\": \"string\" }, \"online\": { \"title\": \"Online\", \"type\": \"boolean\" }, \"category\": { \"title\": \"Category\", \"maxLength\": 255, \"nullable\": true, \"type\": \"string\" }, \"activated\": { \"title\": \"Activated\", \"type\": \"boolean\" }, \"level\": { \"title\": \"Level\", \"maxLength\": 255, \"nullable\": true, \"type\": \"string\" } }, \"required\": [ \"id\", \"online\", \"activated\" ], \"additionalProperties\": false } ``` ### /building_map ``` { \"title\": \"BuildingMap\", \"type\": \"object\", \"properties\": { \"name\": { \"title\": \"Name\", \"default\": \"\", \"type\": \"string\" }, \"levels\": { \"title\": \"Levels\", \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/Level\" } }, \"lifts\": { \"title\": \"Lifts\", \"default\": [], \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/Lift\" } } }, \"required\": [ \"name\", \"levels\", \"lifts\" ], \"definitions\": { \"AffineImage\": { \"title\": \"AffineImage\", \"type\": \"object\", \"properties\": { \"name\": { \"title\": \"Name\", \"default\": \"\", \"type\": \"string\" }, \"x_offset\": { \"title\": \"X Offset\", \"default\": 0, \"type\": \"number\" }, \"y_offset\": { \"title\": \"Y Offset\", \"default\": 0, \"type\": \"number\" }, \"yaw\": { \"title\": \"Yaw\", \"default\": 0, \"type\": \"number\" }, \"scale\": { \"title\": \"Scale\", \"default\": 0, \"type\": \"number\" }, \"encoding\": { \"title\": \"Encoding\", \"default\": \"\", \"type\": \"string\" }, \"data\": { \"title\": \"Data\", \"type\": \"string\" } }, \"required\": [ \"name\", \"x_offset\", \"y_offset\", \"yaw\", \"scale\", \"encoding\", \"data\" ] }, \"Place\": { \"title\": \"Place\", \"type\": \"object\", \"properties\": { \"name\": { \"title\": \"Name\", \"default\": \"\", \"type\": \"string\" }, \"x\": { \"title\": \"X\", \"default\": 0, \"type\": \"number\" }, \"y\": { \"title\": \"Y\", \"default\": 0, \"type\": \"number\" }, \"yaw\": { \"title\": \"Yaw\", \"default\": 0, \"type\": \"number\" }, \"position_tolerance\": { \"title\": \"Position Tolerance\", \"default\": 0, \"type\": \"number\" }, \"yaw_tolerance\": { \"title\": \"Yaw Tolerance\", \"default\": 0, \"type\": \"number\" } }, \"required\": [ \"name\", \"x\", \"y\", \"yaw\", \"position_tolerance\", \"yaw_tolerance\" ] }, \"Door\": { \"title\": \"Door\", \"type\": \"object\", \"properties\": { \"name\": { \"title\": \"Name\", \"default\": \"\", \"type\": \"string\" }, \"v1_x\": { \"title\": \"V1 X\", \"default\": 0, \"type\": \"number\" }, \"v1_y\": { \"title\": \"V1 Y\", \"default\": 0, \"type\": \"number\" }, \"v2_x\": { \"title\": \"V2 X\", \"default\": 0, \"type\": \"number\" }, \"v2_y\": { \"title\": \"V2 Y\", \"default\": 0, \"type\": \"number\" }, \"door_type\": { \"title\": \"Door Type\", \"default\": 0, \"minimum\": 0, \"maximum\": 255, \"type\": \"integer\" }, \"motion_range\": { \"title\": \"Motion Range\", \"default\": 0, \"type\": \"number\" }, \"motion_direction\": { \"title\": \"Motion Direction\", \"default\": 0, \"minimum\": -2147483648, \"maximum\": 2147483647, \"type\": \"integer\" } }, \"required\": [ \"name\", \"v1_x\", \"v1_y\", \"v2_x\", \"v2_y\", \"door_type\", \"motion_range\", \"motion_direction\" ] }, \"Param\": { \"title\": \"Param\", \"type\": \"object\", \"properties\": { \"name\": { \"title\": \"Name\", \"default\": \"\", \"type\": \"string\" }, \"type\": { \"title\": \"Type\", \"default\": 0, \"minimum\": 0, \"maximum\": 4294967295, \"type\": \"integer\" }, \"value_int\": { \"title\": \"Value Int\", \"default\": 0, \"minimum\": -2147483648, \"maximum\": 2147483647, \"type\": \"integer\" }, \"value_float\": { \"title\": \"Value Float\", \"default\": 0, \"type\": \"number\" }, \"value_string\": { \"title\": \"Value String\", \"default\": \"\", \"type\": \"string\" }, \"value_bool\": { \"title\": \"Value Bool\", \"default\": false, \"type\": \"boolean\" } }, \"required\": [ \"name\", \"type\", \"value_int\", \"value_float\", \"value_string\", \"value_bool\" ] }, \"GraphNode\": { \"title\": \"GraphNode\", \"type\": \"object\", \"properties\": { \"x\": { \"title\": \"X\", \"default\": 0, \"type\": \"number\" }, \"y\": { \"title\": \"Y\", \"default\": 0, \"type\": \"number\" }, \"name\": { \"title\": \"Name\", \"default\": \"\", \"type\": \"string\" }, \"params\": { \"title\": \"Params\", \"default\": [], \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/Param\" } } }, \"required\": [ \"x\", \"y\", \"name\", \"params\" ] }, \"GraphEdge\": { \"title\": \"GraphEdge\", \"type\": \"object\", \"properties\": { \"v1_idx\": { \"title\": \"V1 Idx\", \"default\": 0, \"minimum\": 0, \"maximum\": 4294967295, \"type\": \"integer\" }, \"v2_idx\": { \"title\": \"V2 Idx\", \"default\": 0, \"minimum\": 0, \"maximum\": 4294967295, \"type\": \"integer\" }, \"params\": { \"title\": \"Params\", \"default\": [], \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/Param\" } }, \"edge_type\": { \"title\": \"Edge Type\", \"default\": 0, \"minimum\": 0, \"maximum\": 255, \"type\": \"integer\" } }, \"required\": [ \"v1_idx\", \"v2_idx\", \"params\", \"edge_type\" ] }, \"Graph\": { \"title\": \"Graph\", \"type\": \"object\", \"properties\": { \"name\": { \"title\": \"Name\", \"default\": \"\", \"type\": \"string\" }, \"vertices\": { \"title\": \"Vertices\", \"default\": [], \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/GraphNode\" } }, \"edges\": { \"title\": \"Edges\", \"default\": [], \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/GraphEdge\" } }, \"params\": { \"title\": \"Params\", \"default\": [], \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/Param\" } } }, \"required\": [ \"name\", \"vertices\", \"edges\", \"params\" ] }, \"Level\": { \"title\": \"Level\", \"type\": \"object\", \"properties\": { \"name\": { \"title\": \"Name\", \"default\": \"\", \"type\": \"string\" }, \"elevation\": { \"title\": \"Elevation\", \"default\": 0, \"type\": \"number\" }, \"images\": { \"title\": \"Images\", \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/AffineImage\" } }, \"places\": { \"title\": \"Places\", \"default\": [], \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/Place\" } }, \"doors\": { \"title\": \"Doors\", \"default\": [], \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/Door\" } }, \"nav_graphs\": { \"title\": \"Nav Graphs\", \"default\": [], \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/Graph\" } }, \"wall_graph\": { \"title\": \"Wall Graph\", \"default\": { \"name\": \"\", \"vertices\": [], \"edges\": [], \"params\": [] }, \"allOf\": [ { \"$ref\": \"#/definitions/Graph\" } ] } }, \"required\": [ \"name\", \"elevation\", \"images\", \"places\", \"doors\", \"nav_graphs\", \"wall_graph\" ] }, \"Lift\": { \"title\": \"Lift\", \"type\": \"object\", \"properties\": { \"name\": { \"title\": \"Name\", \"default\": \"\", \"type\": \"string\" }, \"levels\": { \"title\": \"Levels\", \"default\": [], \"type\": \"array\", \"items\": { \"type\": \"string\" } }, \"doors\": { \"title\": \"Doors\", \"default\": [], \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/Door\" } }, \"wall_graph\": { \"title\": \"Wall Graph\", \"default\": { \"name\": \"\", \"vertices\": [], \"edges\": [], \"params\": [] }, \"allOf\": [ { \"$ref\": \"#/definitions/Graph\" } ] }, \"ref_x\": { \"title\": \"Ref X\", \"default\": 0, \"type\": \"number\" }, \"ref_y\": { \"title\": \"Ref Y\", \"default\": 0, \"type\": \"number\" }, \"ref_yaw\": { \"title\": \"Ref Yaw\", \"default\": 0, \"type\": \"number\" }, \"width\": { \"title\": \"Width\", \"default\": 0, \"type\": \"number\" }, \"depth\": { \"title\": \"Depth\", \"default\": 0, \"type\": \"number\" } }, \"required\": [ \"name\", \"levels\", \"doors\", \"wall_graph\", \"ref_x\", \"ref_y\", \"ref_yaw\", \"width\", \"depth\" ] } } } ``` ### /building_map/fire_alarm_trigger ``` { \"title\": \"FireAlarmTriggerState\", \"type\": \"object\", \"properties\": { \"unix_millis_time\": { \"title\": \"Unix Millis Time\", \"type\": \"integer\" }, \"trigger\": { \"title\": \"Trigger\", \"type\": \"boolean\" } }, \"required\": [ \"unix_millis_time\", \"trigger\" ] } ``` ### /delivery_alerts ``` { \"title\": \"DeliveryAlert\", \"type\": \"object\", \"properties\": { \"id\": { \"title\": \"Id\", \"type\": \"string\" }, \"category\": { \"$ref\": \"#/definitions/Category\" }, \"tier\": { \"$ref\": \"#/definitions/Tier\" }, \"action\": { \"$ref\": \"#/definitions/Action\" }, \"task_id\": { \"title\": \"Task Id\", \"type\": \"string\" }, \"message\": { \"title\": \"Message\", \"type\": \"string\" } }, \"required\": [ \"id\", \"category\", \"tier\", \"action\", \"task_id\", \"message\" ], \"definitions\": { \"Category\": { \"title\": \"Category\", \"description\": \"An enumeration.\", \"enum\": [ \"missing\", \"wrong\", \"obstructed\", \"cancelled\" ], \"type\": \"string\" }, \"Tier\": { \"title\": \"Tier\", \"description\": \"An enumeration.\", \"enum\": [ \"warning\", \"error\" ], \"type\": \"string\" }, \"Action\": { \"title\": \"Action\", \"description\": \"An enumeration.\", \"enum\": [ \"waiting\", \"cancelled\", \"override\", \"resume\" ], \"type\": \"string\" } } } ``` ### /doors/{door_name}/state ``` { \"title\": \"DoorState\", \"type\": \"object\", \"properties\": { \"door_time\": { \"title\": \"Door Time\", \"default\": { \"sec\": 0, \"nanosec\": 0 }, \"allOf\": [ { \"$ref\": \"#/definitions/Time\" } ] }, \"door_name\": { \"title\": \"Door Name\", \"default\": \"\", \"type\": \"string\" }, \"current_mode\": { \"title\": \"Current Mode\", \"default\": { \"value\": 0 }, \"allOf\": [ { \"$ref\": \"#/definitions/DoorMode\" } ] } }, \"required\": [ \"door_time\", \"door_name\", \"current_mode\" ], \"definitions\": { \"Time\": { \"title\": \"Time\", \"type\": \"object\", \"properties\": { \"sec\": { \"title\": \"Sec\", \"default\": 0, \"minimum\": -2147483648, \"maximum\": 2147483647, \"type\": \"integer\" }, \"nanosec\": { \"title\": \"Nanosec\", \"default\": 0, \"minimum\": 0, \"maximum\": 4294967295, \"type\": \"integer\" } }, \"required\": [ \"sec\", \"nanosec\" ] }, \"DoorMode\": { \"title\": \"DoorMode\", \"type\": \"object\", \"properties\": { \"value\": { \"title\": \"Value\", \"default\": 0, \"minimum\": 0, \"maximum\": 4294967295, \"type\": \"integer\" } }, \"required\": [ \"value\" ] } } } ``` ### /doors/{door_name}/health ``` { \"title\": \"DoorHealth\", \"type\": \"object\", \"properties\": { \"health_status\": { \"title\": \"Health Status\", \"maxLength\": 255, \"nullable\": true, \"type\": \"string\" }, \"health_message\": { \"title\": \"Health Message\", \"nullable\": true, \"type\": \"string\" }, \"id_\": { \"title\": \"Id \", \"maxLength\": 255, \"type\": \"string\" } }, \"required\": [ \"health_status\", \"id_\" ], \"additionalProperties\": false } ``` ### /lifts/{lift_name}/state ``` { \"title\": \"LiftState\", \"type\": \"object\", \"properties\": { \"lift_time\": { \"title\": \"Lift Time\", \"default\": { \"sec\": 0, \"nanosec\": 0 }, \"allOf\": [ { \"$ref\": \"#/definitions/Time\" } ] }, \"lift_name\": { \"title\": \"Lift Name\", \"default\": \"\", \"type\": \"string\" }, \"available_floors\": { \"title\": \"Available Floors\", \"default\": [], \"type\": \"array\", \"items\": { \"type\": \"string\" } }, \"current_floor\": { \"title\": \"Current Floor\", \"default\": \"\", \"type\": \"string\" }, \"destination_floor\": { \"title\": \"Destination Floor\", \"default\": \"\", \"type\": \"string\" }, \"door_state\": { \"title\": \"Door State\", \"default\": 0, \"minimum\": 0, \"maximum\": 255, \"type\": \"integer\" }, \"motion_state\": { \"title\": \"Motion State\", \"default\": 0, \"minimum\": 0, \"maximum\": 255, \"type\": \"integer\" }, \"available_modes\": { \"title\": \"Available Modes\", \"type\": \"array\", \"items\": { \"type\": \"integer\" } }, \"current_mode\": { \"title\": \"Current Mode\", \"default\": 0, \"minimum\": 0, \"maximum\": 255, \"type\": \"integer\" }, \"session_id\": { \"title\": \"Session Id\", \"default\": \"\", \"type\": \"string\" } }, \"required\": [ \"lift_time\", \"lift_name\", \"available_floors\", \"current_floor\", \"destination_floor\", \"door_state\", \"motion_state\", \"available_modes\", \"current_mode\", \"session_id\" ], \"definitions\": { \"Time\": { \"title\": \"Time\", \"type\": \"object\", \"properties\": { \"sec\": { \"title\": \"Sec\", \"default\": 0, \"minimum\": -2147483648, \"maximum\": 2147483647, \"type\": \"integer\" }, \"nanosec\": { \"title\": \"Nanosec\", \"default\": 0, \"minimum\": 0, \"maximum\": 4294967295, \"type\": \"integer\" } }, \"required\": [ \"sec\", \"nanosec\" ] } } } ``` ### /lifts/{lift_name}/health ``` { \"title\": \"LiftHealth\", \"type\": \"object\", \"properties\": { \"health_status\": { \"title\": \"Health Status\", \"maxLength\": 255, \"nullable\": true, \"type\": \"string\" }, \"health_message\": { \"title\": \"Health Message\", \"nullable\": true, \"type\": \"string\" }, \"id_\": { \"title\": \"Id \", \"maxLength\": 255, \"type\": \"string\" } }, \"required\": [ \"health_status\", \"id_\" ], \"additionalProperties\": false } ``` ### /tasks/{task_id}/state ``` { \"title\": \"TaskState\", \"type\": \"object\", \"properties\": { \"booking\": { \"$ref\": \"#/definitions/Booking\" }, \"category\": { \"$ref\": \"#/definitions/Category\" }, \"detail\": { \"$ref\": \"#/definitions/Detail\" }, \"unix_millis_start_time\": { \"title\": \"Unix Millis Start Time\", \"type\": \"integer\" }, \"unix_millis_finish_time\": { \"title\": \"Unix Millis Finish Time\", \"type\": \"integer\" }, \"original_estimate_millis\": { \"$ref\": \"#/definitions/EstimateMillis\" }, \"estimate_millis\": { \"$ref\": \"#/definitions/EstimateMillis\" }, \"assigned_to\": { \"title\": \"Assigned To\", \"description\": \"Which agent (robot) is the task assigned to\", \"allOf\": [ { \"$ref\": \"#/definitions/AssignedTo\" } ] }, \"status\": { \"$ref\": \"#/definitions/Status\" }, \"dispatch\": { \"$ref\": \"#/definitions/Dispatch\" }, \"phases\": { \"title\": \"Phases\", \"description\": \"A dictionary of the states of the phases of the task. The keys (property names) are phase IDs, which are integers.\", \"type\": \"object\", \"additionalProperties\": { \"$ref\": \"#/definitions/Phase\" } }, \"completed\": { \"title\": \"Completed\", \"description\": \"An array of the IDs of completed phases of this task\", \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/Id\" } }, \"active\": { \"title\": \"Active\", \"description\": \"The ID of the active phase for this task\", \"allOf\": [ { \"$ref\": \"#/definitions/Id\" } ] }, \"pending\": { \"title\": \"Pending\", \"description\": \"An array of the pending phases of this task\", \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/Id\" } }, \"interruptions\": { \"title\": \"Interruptions\", \"description\": \"A dictionary of interruptions that have been applied to this task. The keys (property names) are the unique token of the interruption request.\", \"type\": \"object\", \"additionalProperties\": { \"$ref\": \"#/definitions/Interruption\" } }, \"cancellation\": { \"title\": \"Cancellation\", \"description\": \"If the task was cancelled, this will describe information about the request.\", \"allOf\": [ { \"$ref\": \"#/definitions/Cancellation\" } ] }, \"killed\": { \"title\": \"Killed\", \"description\": \"If the task was killed, this will describe information about the request.\", \"allOf\": [ { \"$ref\": \"#/definitions/Killed\" } ] } }, \"required\": [ \"booking\" ], \"definitions\": { \"Booking\": { \"title\": \"Booking\", \"type\": \"object\", \"properties\": { \"id\": { \"title\": \"Id\", \"description\": \"The unique identifier for this task\", \"type\": \"string\" }, \"unix_millis_earliest_start_time\": { \"title\": \"Unix Millis Earliest Start Time\", \"type\": \"integer\" }, \"unix_millis_request_time\": { \"title\": \"Unix Millis Request Time\", \"type\": \"integer\" }, \"priority\": { \"title\": \"Priority\", \"description\": \"Priority information about this task\", \"anyOf\": [ { \"type\": \"object\" }, { \"type\": \"string\" } ] }, \"labels\": { \"title\": \"Labels\", \"description\": \"Information about how and why this task was booked\", \"type\": \"array\", \"items\": { \"type\": \"string\" } }, \"requester\": { \"title\": \"Requester\", \"description\": \"(Optional) An identifier for the entity that requested this task\", \"type\": \"string\" } }, \"required\": [ \"id\" ] }, \"Category\": { \"title\": \"Category\", \"description\": \"The category of this task or phase\", \"type\": \"string\" }, \"Detail\": { \"title\": \"Detail\", \"description\": \"Detailed information about a task, phase, or event\", \"anyOf\": [ { \"type\": \"object\" }, { \"type\": \"array\", \"items\": {} }, { \"type\": \"string\" } ] }, \"EstimateMillis\": { \"title\": \"EstimateMillis\", \"description\": \"An estimate, in milliseconds, of how long the subject will take to complete\", \"minimum\": 0, \"type\": \"integer\" }, \"AssignedTo\": { \"title\": \"AssignedTo\", \"type\": \"object\", \"properties\": { \"group\": { \"title\": \"Group\", \"type\": \"string\" }, \"name\": { \"title\": \"Name\", \"type\": \"string\" } }, \"required\": [ \"group\", \"name\" ] }, \"Status\": { \"title\": \"Status\", \"description\": \"An enumeration.\", \"enum\": [ \"uninitialized\", \"blocked\", \"error\", \"failed\", \"queued\", \"standby\", \"underway\", \"delayed\", \"skipped\", \"canceled\", \"killed\", \"completed\" ] }, \"Status1\": { \"title\": \"Status1\", \"description\": \"An enumeration.\", \"enum\": [ \"queued\", \"selected\", \"dispatched\", \"failed_to_assign\", \"canceled_in_flight\" ] }, \"Assignment\": { \"title\": \"Assignment\", \"type\": \"object\", \"properties\": { \"fleet_name\": { \"title\": \"Fleet Name\", \"type\": \"string\" }, \"expected_robot_name\": { \"title\": \"Expected Robot Name\", \"type\": \"string\" } } }, \"Error\": { \"title\": \"Error\", \"type\": \"object\", \"properties\": { \"code\": { \"title\": \"Code\", \"description\": \"A standard code for the kind of error that has occurred\", \"minimum\": 0, \"type\": \"integer\" }, \"category\": { \"title\": \"Category\", \"description\": \"The category of the error\", \"type\": \"string\" }, \"detail\": { \"title\": \"Detail\", \"description\": \"Details about the error\", \"type\": \"string\" } } }, \"Dispatch\": { \"title\": \"Dispatch\", \"type\": \"object\", \"properties\": { \"status\": { \"$ref\": \"#/definitions/Status1\" }, \"assignment\": { \"$ref\": \"#/definitions/Assignment\" }, \"errors\": { \"title\": \"Errors\", \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/Error\" } } }, \"required\": [ \"status\" ] }, \"Id\": { \"title\": \"Id\", \"minimum\": 0, \"type\": \"integer\" }, \"EventState\": { \"title\": \"EventState\", \"type\": \"object\", \"properties\": { \"id\": { \"$ref\": \"#/definitions/Id\" }, \"status\": { \"$ref\": \"#/definitions/Status\" }, \"name\": { \"title\": \"Name\", \"description\": \"The brief name of the event\", \"type\": \"string\" }, \"detail\": { \"title\": \"Detail\", \"description\": \"Detailed information about the event\", \"allOf\": [ { \"$ref\": \"#/definitions/Detail\" } ] }, \"deps\": { \"title\": \"Deps\", \"description\": \"This event may depend on other events. This array contains the IDs of those other event dependencies.\", \"type\": \"array\", \"items\": { \"type\": \"integer\", \"minimum\": 0 } } }, \"required\": [ \"id\" ] }, \"Undo\": { \"title\": \"Undo\", \"type\": \"object\", \"properties\": { \"unix_millis_request_time\": { \"title\": \"Unix Millis Request Time\", \"description\": \"The time that the undo skip request arrived\", \"type\": \"integer\" }, \"labels\": { \"title\": \"Labels\", \"description\": \"Labels to describe the undo skip request\", \"type\": \"array\", \"items\": { \"type\": \"string\" } } }, \"required\": [ \"unix_millis_request_time\", \"labels\" ] }, \"SkipPhaseRequest\": { \"title\": \"SkipPhaseRequest\", \"type\": \"object\", \"properties\": { \"unix_millis_request_time\": { \"title\": \"Unix Millis Request Time\", \"description\": \"The time that the skip request arrived\", \"type\": \"integer\" }, \"labels\": { \"title\": \"Labels\", \"description\": \"Labels to describe the purpose of the skip request\", \"type\": \"array\", \"items\": { \"type\": \"string\" } }, \"undo\": { \"title\": \"Undo\", \"description\": \"Information about an undo skip request that applied to this request\", \"allOf\": [ { \"$ref\": \"#/definitions/Undo\" } ] } }, \"required\": [ \"unix_millis_request_time\", \"labels\" ] }, \"Phase\": { \"title\": \"Phase\", \"type\": \"object\", \"properties\": { \"id\": { \"$ref\": \"#/definitions/Id\" }, \"category\": { \"$ref\": \"#/definitions/Category\" }, \"detail\": { \"$ref\": \"#/definitions/Detail\" }, \"unix_millis_start_time\": { \"title\": \"Unix Millis Start Time\", \"type\": \"integer\" }, \"unix_millis_finish_time\": { \"title\": \"Unix Millis Finish Time\", \"type\": \"integer\" }, \"original_estimate_millis\": { \"$ref\": \"#/definitions/EstimateMillis\" }, \"estimate_millis\": { \"$ref\": \"#/definitions/EstimateMillis\" }, \"final_event_id\": { \"$ref\": \"#/definitions/Id\" }, \"events\": { \"title\": \"Events\", \"description\": \"A dictionary of events for this phase. The keys (property names) are the event IDs, which are integers.\", \"type\": \"object\", \"additionalProperties\": { \"$ref\": \"#/definitions/EventState\" } }, \"skip_requests\": { \"title\": \"Skip Requests\", \"description\": \"Information about any skip requests that have been received\", \"type\": \"object\", \"additionalProperties\": { \"$ref\": \"#/definitions/SkipPhaseRequest\" } } }, \"required\": [ \"id\" ] }, \"ResumedBy\": { \"title\": \"ResumedBy\", \"type\": \"object\", \"properties\": { \"unix_millis_request_time\": { \"title\": \"Unix Millis Request Time\", \"description\": \"The time that the resume request arrived\", \"type\": \"integer\" }, \"labels\": { \"title\": \"Labels\", \"description\": \"Labels to describe the resume request\", \"type\": \"array\", \"items\": { \"type\": \"string\" } } }, \"required\": [ \"labels\" ] }, \"Interruption\": { \"title\": \"Interruption\", \"type\": \"object\", \"properties\": { \"unix_millis_request_time\": { \"title\": \"Unix Millis Request Time\", \"description\": \"The time that the interruption request arrived\", \"type\": \"integer\" }, \"labels\": { \"title\": \"Labels\", \"description\": \"Labels to describe the purpose of the interruption\", \"type\": \"array\", \"items\": { \"type\": \"string\" } }, \"resumed_by\": { \"title\": \"Resumed By\", \"description\": \"Information about the resume request that ended this interruption. This field will be missing if the interruption is still active.\", \"allOf\": [ { \"$ref\": \"#/definitions/ResumedBy\" } ] } }, \"required\": [ \"unix_millis_request_time\", \"labels\" ] }, \"Cancellation\": { \"title\": \"Cancellation\", \"type\": \"object\", \"properties\": { \"unix_millis_request_time\": { \"title\": \"Unix Millis Request Time\", \"description\": \"The time that the cancellation request arrived\", \"type\": \"integer\" }, \"labels\": { \"title\": \"Labels\", \"description\": \"Labels to describe the cancel request\", \"type\": \"array\", \"items\": { \"type\": \"string\" } } }, \"required\": [ \"unix_millis_request_time\", \"labels\" ] }, \"Killed\": { \"title\": \"Killed\", \"type\": \"object\", \"properties\": { \"unix_millis_request_time\": { \"title\": \"Unix Millis Request Time\", \"description\": \"The time that the cancellation request arrived\", \"type\": \"integer\" }, \"labels\": { \"title\": \"Labels\", \"description\": \"Labels to describe the kill request\", \"type\": \"array\", \"items\": { \"type\": \"string\" } } }, \"required\": [ \"unix_millis_request_time\", \"labels\" ] } } } ``` ### /tasks/{task_id}/log ``` { \"title\": \"TaskEventLog\", \"type\": \"object\", \"properties\": { \"task_id\": { \"title\": \"Task Id\", \"type\": \"string\" }, \"log\": { \"title\": \"Log\", \"description\": \"Log entries related to the overall task\", \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/LogEntry\" } }, \"phases\": { \"title\": \"Phases\", \"description\": \"A dictionary whose keys (property names) are the indices of a phase\", \"type\": \"object\", \"additionalProperties\": { \"$ref\": \"#/definitions/Phases\" } } }, \"required\": [ \"task_id\" ], \"additionalProperties\": false, \"definitions\": { \"Tier\": { \"title\": \"Tier\", \"description\": \"An enumeration.\", \"enum\": [ \"uninitialized\", \"info\", \"warning\", \"error\" ] }, \"LogEntry\": { \"title\": \"LogEntry\", \"type\": \"object\", \"properties\": { \"seq\": { \"title\": \"Seq\", \"description\": \"Sequence number for this entry. Each entry has a unique sequence number which monotonically increase, until integer overflow causes a wrap around.\", \"exclusiveMaximum\": 4294967296, \"minimum\": 0, \"type\": \"integer\" }, \"tier\": { \"description\": \"The importance level of the log entry\", \"allOf\": [ { \"$ref\": \"#/definitions/Tier\" } ] }, \"unix_millis_time\": { \"title\": \"Unix Millis Time\", \"type\": \"integer\" }, \"text\": { \"title\": \"Text\", \"description\": \"The text of the log entry\", \"type\": \"string\" } }, \"required\": [ \"seq\", \"tier\", \"unix_millis_time\", \"text\" ] }, \"Phases\": { \"title\": \"Phases\", \"type\": \"object\", \"properties\": { \"log\": { \"title\": \"Log\", \"description\": \"Log entries related to the overall phase\", \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/LogEntry\" } }, \"events\": { \"title\": \"Events\", \"description\": \"A dictionary whose keys (property names) are the indices of an event in the phase\", \"type\": \"object\", \"additionalProperties\": { \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/LogEntry\" } } } }, \"additionalProperties\": false } } } ``` ### /dispensers/{guid}/state ``` { \"title\": \"DispenserState\", \"type\": \"object\", \"properties\": { \"time\": { \"title\": \"Time\", \"default\": { \"sec\": 0, \"nanosec\": 0 }, \"allOf\": [ { \"$ref\": \"#/definitions/Time\" } ] }, \"guid\": { \"title\": \"Guid\", \"default\": \"\", \"type\": \"string\" }, \"mode\": { \"title\": \"Mode\", \"default\": 0, \"minimum\": -2147483648, \"maximum\": 2147483647, \"type\": \"integer\" }, \"request_guid_queue\": { \"title\": \"Request Guid Queue\", \"default\": [], \"type\": \"array\", \"items\": { \"type\": \"string\" } }, \"seconds_remaining\": { \"title\": \"Seconds Remaining\", \"default\": 0, \"type\": \"number\" } }, \"required\": [ \"time\", \"guid\", \"mode\", \"request_guid_queue\", \"seconds_remaining\" ], \"definitions\": { \"Time\": { \"title\": \"Time\", \"type\": \"object\", \"properties\": { \"sec\": { \"title\": \"Sec\", \"default\": 0, \"minimum\": -2147483648, \"maximum\": 2147483647, \"type\": \"integer\" }, \"nanosec\": { \"title\": \"Nanosec\", \"default\": 0, \"minimum\": 0, \"maximum\": 4294967295, \"type\": \"integer\" } }, \"required\": [ \"sec\", \"nanosec\" ] } } } ``` ### /dispensers/{guid}/health ``` { \"title\": \"DispenserHealth\", \"type\": \"object\", \"properties\": { \"health_status\": { \"title\": \"Health Status\", \"maxLength\": 255, \"nullable\": true, \"type\": \"string\" }, \"health_message\": { \"title\": \"Health Message\", \"nullable\": true, \"type\": \"string\" }, \"id_\": { \"title\": \"Id \", \"maxLength\": 255, \"type\": \"string\" } }, \"required\": [ \"health_status\", \"id_\" ], \"additionalProperties\": false } ``` ### /ingestors/{guid}/state ``` { \"title\": \"IngestorState\", \"type\": \"object\", \"properties\": { \"time\": { \"title\": \"Time\", \"default\": { \"sec\": 0, \"nanosec\": 0 }, \"allOf\": [ { \"$ref\": \"#/definitions/Time\" } ] }, \"guid\": { \"title\": \"Guid\", \"default\": \"\", \"type\": \"string\" }, \"mode\": { \"title\": \"Mode\", \"default\": 0, \"minimum\": -2147483648, \"maximum\": 2147483647, \"type\": \"integer\" }, \"request_guid_queue\": { \"title\": \"Request Guid Queue\", \"default\": [], \"type\": \"array\", \"items\": { \"type\": \"string\" } }, \"seconds_remaining\": { \"title\": \"Seconds Remaining\", \"default\": 0, \"type\": \"number\" } }, \"required\": [ \"time\", \"guid\", \"mode\", \"request_guid_queue\", \"seconds_remaining\" ], \"definitions\": { \"Time\": { \"title\": \"Time\", \"type\": \"object\", \"properties\": { \"sec\": { \"title\": \"Sec\", \"default\": 0, \"minimum\": -2147483648, \"maximum\": 2147483647, \"type\": \"integer\" }, \"nanosec\": { \"title\": \"Nanosec\", \"default\": 0, \"minimum\": 0, \"maximum\": 4294967295, \"type\": \"integer\" } }, \"required\": [ \"sec\", \"nanosec\" ] } } } ``` ### /ingestors/{guid}/health ``` { \"title\": \"IngestorHealth\", \"type\": \"object\", \"properties\": { \"health_status\": { \"title\": \"Health Status\", \"maxLength\": 255, \"nullable\": true, \"type\": \"string\" }, \"health_message\": { \"title\": \"Health Message\", \"nullable\": true, \"type\": \"string\" }, \"id_\": { \"title\": \"Id \", \"maxLength\": 255, \"type\": \"string\" } }, \"required\": [ \"health_status\", \"id_\" ], \"additionalProperties\": false } ``` ### /fleets/{name}/state ``` { \"title\": \"FleetState\", \"type\": \"object\", \"properties\": { \"name\": { \"title\": \"Name\", \"type\": \"string\" }, \"robots\": { \"title\": \"Robots\", \"description\": \"A dictionary of the states of the robots that belong to this fleet\", \"type\": \"object\", \"additionalProperties\": { \"$ref\": \"#/definitions/RobotState\" } } }, \"definitions\": { \"Status\": { \"title\": \"Status\", \"description\": \"An enumeration.\", \"enum\": [ \"uninitialized\", \"offline\", \"shutdown\", \"idle\", \"charging\", \"working\", \"error\" ] }, \"Location2D\": { \"title\": \"Location2D\", \"type\": \"object\", \"properties\": { \"map\": { \"title\": \"Map\", \"type\": \"string\" }, \"x\": { \"title\": \"X\", \"type\": \"number\" }, \"y\": { \"title\": \"Y\", \"type\": \"number\" }, \"yaw\": { \"title\": \"Yaw\", \"type\": \"number\" } }, \"required\": [ \"map\", \"x\", \"y\", \"yaw\" ] }, \"Issue\": { \"title\": \"Issue\", \"type\": \"object\", \"properties\": { \"category\": { \"title\": \"Category\", \"description\": \"Category of the robot\'s issue\", \"type\": \"string\" }, \"detail\": { \"title\": \"Detail\", \"description\": \"Detailed information about the issue\", \"anyOf\": [ { \"type\": \"object\" }, { \"type\": \"array\", \"items\": {} }, { \"type\": \"string\" } ] } } }, \"Commission\": { \"title\": \"Commission\", \"type\": \"object\", \"properties\": { \"dispatch_tasks\": { \"title\": \"Dispatch Tasks\", \"description\": \"Should the robot accept dispatched tasks, true/false. When used in a request, leave this unset to not change the robot\'s current value.\", \"type\": \"boolean\" }, \"direct_tasks\": { \"title\": \"Direct Tasks\", \"description\": \"Should the robot accept direct task requests, true/false. When used in a request, leave this unset to not change the robot\'s current value.\", \"type\": \"boolean\" }, \"idle_behavior\": { \"title\": \"Idle Behavior\", \"description\": \"Should the robot perform its idle behavior, true/false. When used in a request, leave this unset to not change the robot\'s current value.\", \"type\": \"boolean\" } } }, \"MutexGroups\": { \"title\": \"MutexGroups\", \"type\": \"object\", \"properties\": { \"locked\": { \"title\": \"Locked\", \"description\": \"A list of mutex groups that this robot has currently locked\", \"type\": \"array\", \"items\": { \"type\": \"string\" } }, \"requesting\": { \"title\": \"Requesting\", \"description\": \"A list of the mutex groups that this robot is currently requesting but has not lockd yet\", \"type\": \"array\", \"items\": { \"type\": \"string\" } } } }, \"RobotState\": { \"title\": \"RobotState\", \"type\": \"object\", \"properties\": { \"name\": { \"title\": \"Name\", \"type\": \"string\" }, \"status\": { \"description\": \"A simple token representing the status of the robot\", \"allOf\": [ { \"$ref\": \"#/definitions/Status\" } ] }, \"task_id\": { \"title\": \"Task Id\", \"description\": \"The ID of the task this robot is currently working on. Empty string if the robot is not working on a task.\", \"type\": \"string\" }, \"unix_millis_time\": { \"title\": \"Unix Millis Time\", \"type\": \"integer\" }, \"location\": { \"$ref\": \"#/definitions/Location2D\" }, \"battery\": { \"title\": \"Battery\", \"description\": \"State of charge of the battery. Values range from 0.0 (depleted) to 1.0 (fully charged)\", \"minimum\": 0.0, \"maximum\": 1.0, \"type\": \"number\" }, \"issues\": { \"title\": \"Issues\", \"description\": \"A list of issues with the robot that operators need to address\", \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/Issue\" } }, \"commission\": { \"$ref\": \"#/definitions/Commission\" }, \"mutex_groups\": { \"title\": \"Mutex Groups\", \"description\": \"Information about the mutex groups that this robot is interacting with\", \"allOf\": [ { \"$ref\": \"#/definitions/MutexGroups\" } ] } } } } } ``` ### /fleets/{name}/log ``` { \"title\": \"FleetLog\", \"type\": \"object\", \"properties\": { \"name\": { \"title\": \"Name\", \"type\": \"string\" }, \"log\": { \"title\": \"Log\", \"description\": \"Log for the overall fleet\", \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/LogEntry\" } }, \"robots\": { \"title\": \"Robots\", \"description\": \"Dictionary of logs for the individual robots. The keys (property names) are the robot names.\", \"type\": \"object\", \"additionalProperties\": { \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/LogEntry\" } } } }, \"definitions\": { \"Tier\": { \"title\": \"Tier\", \"description\": \"An enumeration.\", \"enum\": [ \"uninitialized\", \"info\", \"warning\", \"error\" ] }, \"LogEntry\": { \"title\": \"LogEntry\", \"type\": \"object\", \"properties\": { \"seq\": { \"title\": \"Seq\", \"description\": \"Sequence number for this entry. Each entry has a unique sequence number which monotonically increase, until integer overflow causes a wrap around.\", \"exclusiveMaximum\": 4294967296, \"minimum\": 0, \"type\": \"integer\" }, \"tier\": { \"description\": \"The importance level of the log entry\", \"allOf\": [ { \"$ref\": \"#/definitions/Tier\" } ] }, \"unix_millis_time\": { \"title\": \"Unix Millis Time\", \"type\": \"integer\" }, \"text\": { \"title\": \"Text\", \"description\": \"The text of the log entry\", \"type\": \"string\" } }, \"required\": [ \"seq\", \"tier\", \"unix_millis_time\", \"text\" ] } } } ``` * @summary Socket.io endpoint * @param {*} [options] Override http request option. * @throws {RequiredError} diff --git a/packages/api-client/lib/version.ts b/packages/api-client/lib/version.ts index 8f08dd1f7..889b1a0fa 100644 --- a/packages/api-client/lib/version.ts +++ b/packages/api-client/lib/version.ts @@ -3,6 +3,6 @@ import { version as rmfModelVer } from 'rmf-models'; export const version = { rmfModels: rmfModelVer, - rmfServer: 'd536f9525f277088015d827b6b7198035d1a856b', + rmfServer: '76d155a524e21ec399b1cb7ac058086a154a373c', openapiGenerator: '6.2.1', }; diff --git a/packages/api-client/schema/index.ts b/packages/api-client/schema/index.ts index 9d0215111..80f8bbc52 100644 --- a/packages/api-client/schema/index.ts +++ b/packages/api-client/schema/index.ts @@ -6,7 +6,7 @@ export default { get: { summary: 'Socket.io endpoint', description: - '\n# NOTE: This endpoint is here for documentation purposes only, this is _not_ a REST endpoint.\n\n## About\nThis exposes a minimal pubsub system built on top of socket.io.\nIt works similar to a normal socket.io endpoint, except that are 2 special\nrooms which control subscriptions.\n\n## Rooms\n### subscribe\nClients must send a message to this room to start receiving messages on other rooms.\nThe message must be of the form:\n\n```\n{\n "room": ""\n}\n```\n\n### unsubscribe\nClients can send a message to this room to stop receiving messages on other rooms.\nThe message must be of the form:\n\n```\n{\n "room": ""\n}\n```\n \n### /alerts\n\n\n```\n{\n "title": "Alert",\n "description": "General alert that can be triggered by events.",\n "type": "object",\n "properties": {\n "id": {\n "title": "Id",\n "maxLength": 255,\n "type": "string"\n },\n "original_id": {\n "title": "Original Id",\n "maxLength": 255,\n "type": "string"\n },\n "category": {\n "title": "Category",\n "description": "Default: default
Task: task
Fleet: fleet
Robot: robot",\n "maxLength": 7,\n "type": "string"\n },\n "unix_millis_created_time": {\n "title": "Unix Millis Created Time",\n "minimum": -9223372036854775808,\n "maximum": 9223372036854775807,\n "type": "integer"\n },\n "acknowledged_by": {\n "title": "Acknowledged By",\n "maxLength": 255,\n "nullable": true,\n "type": "string"\n },\n "unix_millis_acknowledged_time": {\n "title": "Unix Millis Acknowledged Time",\n "minimum": -9223372036854775808,\n "maximum": 9223372036854775807,\n "nullable": true,\n "type": "integer"\n }\n },\n "required": [\n "id",\n "original_id",\n "category",\n "unix_millis_created_time"\n ],\n "additionalProperties": false\n}\n```\n\n\n### /beacons\n\n\n```\n{\n "title": "BeaconState",\n "type": "object",\n "properties": {\n "id": {\n "title": "Id",\n "maxLength": 255,\n "type": "string"\n },\n "online": {\n "title": "Online",\n "type": "boolean"\n },\n "category": {\n "title": "Category",\n "maxLength": 255,\n "nullable": true,\n "type": "string"\n },\n "activated": {\n "title": "Activated",\n "type": "boolean"\n },\n "level": {\n "title": "Level",\n "maxLength": 255,\n "nullable": true,\n "type": "string"\n }\n },\n "required": [\n "id",\n "online",\n "activated"\n ],\n "additionalProperties": false\n}\n```\n\n\n### /building_map\n\n\n```\n{\n "title": "BuildingMap",\n "type": "object",\n "properties": {\n "name": {\n "title": "Name",\n "default": "",\n "type": "string"\n },\n "levels": {\n "title": "Levels",\n "type": "array",\n "items": {\n "$ref": "#/definitions/Level"\n }\n },\n "lifts": {\n "title": "Lifts",\n "default": [],\n "type": "array",\n "items": {\n "$ref": "#/definitions/Lift"\n }\n }\n },\n "required": [\n "name",\n "levels",\n "lifts"\n ],\n "definitions": {\n "AffineImage": {\n "title": "AffineImage",\n "type": "object",\n "properties": {\n "name": {\n "title": "Name",\n "default": "",\n "type": "string"\n },\n "x_offset": {\n "title": "X Offset",\n "default": 0,\n "type": "number"\n },\n "y_offset": {\n "title": "Y Offset",\n "default": 0,\n "type": "number"\n },\n "yaw": {\n "title": "Yaw",\n "default": 0,\n "type": "number"\n },\n "scale": {\n "title": "Scale",\n "default": 0,\n "type": "number"\n },\n "encoding": {\n "title": "Encoding",\n "default": "",\n "type": "string"\n },\n "data": {\n "title": "Data",\n "type": "string"\n }\n },\n "required": [\n "name",\n "x_offset",\n "y_offset",\n "yaw",\n "scale",\n "encoding",\n "data"\n ]\n },\n "Place": {\n "title": "Place",\n "type": "object",\n "properties": {\n "name": {\n "title": "Name",\n "default": "",\n "type": "string"\n },\n "x": {\n "title": "X",\n "default": 0,\n "type": "number"\n },\n "y": {\n "title": "Y",\n "default": 0,\n "type": "number"\n },\n "yaw": {\n "title": "Yaw",\n "default": 0,\n "type": "number"\n },\n "position_tolerance": {\n "title": "Position Tolerance",\n "default": 0,\n "type": "number"\n },\n "yaw_tolerance": {\n "title": "Yaw Tolerance",\n "default": 0,\n "type": "number"\n }\n },\n "required": [\n "name",\n "x",\n "y",\n "yaw",\n "position_tolerance",\n "yaw_tolerance"\n ]\n },\n "Door": {\n "title": "Door",\n "type": "object",\n "properties": {\n "name": {\n "title": "Name",\n "default": "",\n "type": "string"\n },\n "v1_x": {\n "title": "V1 X",\n "default": 0,\n "type": "number"\n },\n "v1_y": {\n "title": "V1 Y",\n "default": 0,\n "type": "number"\n },\n "v2_x": {\n "title": "V2 X",\n "default": 0,\n "type": "number"\n },\n "v2_y": {\n "title": "V2 Y",\n "default": 0,\n "type": "number"\n },\n "door_type": {\n "title": "Door Type",\n "default": 0,\n "minimum": 0,\n "maximum": 255,\n "type": "integer"\n },\n "motion_range": {\n "title": "Motion Range",\n "default": 0,\n "type": "number"\n },\n "motion_direction": {\n "title": "Motion Direction",\n "default": 0,\n "minimum": -2147483648,\n "maximum": 2147483647,\n "type": "integer"\n }\n },\n "required": [\n "name",\n "v1_x",\n "v1_y",\n "v2_x",\n "v2_y",\n "door_type",\n "motion_range",\n "motion_direction"\n ]\n },\n "Param": {\n "title": "Param",\n "type": "object",\n "properties": {\n "name": {\n "title": "Name",\n "default": "",\n "type": "string"\n },\n "type": {\n "title": "Type",\n "default": 0,\n "minimum": 0,\n "maximum": 4294967295,\n "type": "integer"\n },\n "value_int": {\n "title": "Value Int",\n "default": 0,\n "minimum": -2147483648,\n "maximum": 2147483647,\n "type": "integer"\n },\n "value_float": {\n "title": "Value Float",\n "default": 0,\n "type": "number"\n },\n "value_string": {\n "title": "Value String",\n "default": "",\n "type": "string"\n },\n "value_bool": {\n "title": "Value Bool",\n "default": false,\n "type": "boolean"\n }\n },\n "required": [\n "name",\n "type",\n "value_int",\n "value_float",\n "value_string",\n "value_bool"\n ]\n },\n "GraphNode": {\n "title": "GraphNode",\n "type": "object",\n "properties": {\n "x": {\n "title": "X",\n "default": 0,\n "type": "number"\n },\n "y": {\n "title": "Y",\n "default": 0,\n "type": "number"\n },\n "name": {\n "title": "Name",\n "default": "",\n "type": "string"\n },\n "params": {\n "title": "Params",\n "default": [],\n "type": "array",\n "items": {\n "$ref": "#/definitions/Param"\n }\n }\n },\n "required": [\n "x",\n "y",\n "name",\n "params"\n ]\n },\n "GraphEdge": {\n "title": "GraphEdge",\n "type": "object",\n "properties": {\n "v1_idx": {\n "title": "V1 Idx",\n "default": 0,\n "minimum": 0,\n "maximum": 4294967295,\n "type": "integer"\n },\n "v2_idx": {\n "title": "V2 Idx",\n "default": 0,\n "minimum": 0,\n "maximum": 4294967295,\n "type": "integer"\n },\n "params": {\n "title": "Params",\n "default": [],\n "type": "array",\n "items": {\n "$ref": "#/definitions/Param"\n }\n },\n "edge_type": {\n "title": "Edge Type",\n "default": 0,\n "minimum": 0,\n "maximum": 255,\n "type": "integer"\n }\n },\n "required": [\n "v1_idx",\n "v2_idx",\n "params",\n "edge_type"\n ]\n },\n "Graph": {\n "title": "Graph",\n "type": "object",\n "properties": {\n "name": {\n "title": "Name",\n "default": "",\n "type": "string"\n },\n "vertices": {\n "title": "Vertices",\n "default": [],\n "type": "array",\n "items": {\n "$ref": "#/definitions/GraphNode"\n }\n },\n "edges": {\n "title": "Edges",\n "default": [],\n "type": "array",\n "items": {\n "$ref": "#/definitions/GraphEdge"\n }\n },\n "params": {\n "title": "Params",\n "default": [],\n "type": "array",\n "items": {\n "$ref": "#/definitions/Param"\n }\n }\n },\n "required": [\n "name",\n "vertices",\n "edges",\n "params"\n ]\n },\n "Level": {\n "title": "Level",\n "type": "object",\n "properties": {\n "name": {\n "title": "Name",\n "default": "",\n "type": "string"\n },\n "elevation": {\n "title": "Elevation",\n "default": 0,\n "type": "number"\n },\n "images": {\n "title": "Images",\n "type": "array",\n "items": {\n "$ref": "#/definitions/AffineImage"\n }\n },\n "places": {\n "title": "Places",\n "default": [],\n "type": "array",\n "items": {\n "$ref": "#/definitions/Place"\n }\n },\n "doors": {\n "title": "Doors",\n "default": [],\n "type": "array",\n "items": {\n "$ref": "#/definitions/Door"\n }\n },\n "nav_graphs": {\n "title": "Nav Graphs",\n "default": [],\n "type": "array",\n "items": {\n "$ref": "#/definitions/Graph"\n }\n },\n "wall_graph": {\n "title": "Wall Graph",\n "default": {\n "name": "",\n "vertices": [],\n "edges": [],\n "params": []\n },\n "allOf": [\n {\n "$ref": "#/definitions/Graph"\n }\n ]\n }\n },\n "required": [\n "name",\n "elevation",\n "images",\n "places",\n "doors",\n "nav_graphs",\n "wall_graph"\n ]\n },\n "Lift": {\n "title": "Lift",\n "type": "object",\n "properties": {\n "name": {\n "title": "Name",\n "default": "",\n "type": "string"\n },\n "levels": {\n "title": "Levels",\n "default": [],\n "type": "array",\n "items": {\n "type": "string"\n }\n },\n "doors": {\n "title": "Doors",\n "default": [],\n "type": "array",\n "items": {\n "$ref": "#/definitions/Door"\n }\n },\n "wall_graph": {\n "title": "Wall Graph",\n "default": {\n "name": "",\n "vertices": [],\n "edges": [],\n "params": []\n },\n "allOf": [\n {\n "$ref": "#/definitions/Graph"\n }\n ]\n },\n "ref_x": {\n "title": "Ref X",\n "default": 0,\n "type": "number"\n },\n "ref_y": {\n "title": "Ref Y",\n "default": 0,\n "type": "number"\n },\n "ref_yaw": {\n "title": "Ref Yaw",\n "default": 0,\n "type": "number"\n },\n "width": {\n "title": "Width",\n "default": 0,\n "type": "number"\n },\n "depth": {\n "title": "Depth",\n "default": 0,\n "type": "number"\n }\n },\n "required": [\n "name",\n "levels",\n "doors",\n "wall_graph",\n "ref_x",\n "ref_y",\n "ref_yaw",\n "width",\n "depth"\n ]\n }\n }\n}\n```\n\n\n### /building_map/fire_alarm_trigger\n\n\n```\n{\n "title": "FireAlarmTriggerState",\n "type": "object",\n "properties": {\n "unix_millis_time": {\n "title": "Unix Millis Time",\n "type": "integer"\n },\n "trigger": {\n "title": "Trigger",\n "type": "boolean"\n }\n },\n "required": [\n "unix_millis_time",\n "trigger"\n ]\n}\n```\n\n\n### /delivery_alerts\n\n\n```\n{\n "title": "DeliveryAlert",\n "type": "object",\n "properties": {\n "id": {\n "title": "Id",\n "type": "string"\n },\n "category": {\n "$ref": "#/definitions/Category"\n },\n "tier": {\n "$ref": "#/definitions/Tier"\n },\n "action": {\n "$ref": "#/definitions/Action"\n },\n "task_id": {\n "title": "Task Id",\n "type": "string"\n },\n "message": {\n "title": "Message",\n "type": "string"\n }\n },\n "required": [\n "id",\n "category",\n "tier",\n "action",\n "task_id",\n "message"\n ],\n "definitions": {\n "Category": {\n "title": "Category",\n "description": "An enumeration.",\n "enum": [\n "missing",\n "wrong",\n "obstructed",\n "cancelled"\n ],\n "type": "string"\n },\n "Tier": {\n "title": "Tier",\n "description": "An enumeration.",\n "enum": [\n "warning",\n "error"\n ],\n "type": "string"\n },\n "Action": {\n "title": "Action",\n "description": "An enumeration.",\n "enum": [\n "waiting",\n "cancelled",\n "override",\n "resume"\n ],\n "type": "string"\n }\n }\n}\n```\n\n\n### /doors/{door_name}/state\n\n\n```\n{\n "title": "DoorState",\n "type": "object",\n "properties": {\n "door_time": {\n "title": "Door Time",\n "default": {\n "sec": 0,\n "nanosec": 0\n },\n "allOf": [\n {\n "$ref": "#/definitions/Time"\n }\n ]\n },\n "door_name": {\n "title": "Door Name",\n "default": "",\n "type": "string"\n },\n "current_mode": {\n "title": "Current Mode",\n "default": {\n "value": 0\n },\n "allOf": [\n {\n "$ref": "#/definitions/DoorMode"\n }\n ]\n }\n },\n "required": [\n "door_time",\n "door_name",\n "current_mode"\n ],\n "definitions": {\n "Time": {\n "title": "Time",\n "type": "object",\n "properties": {\n "sec": {\n "title": "Sec",\n "default": 0,\n "minimum": -2147483648,\n "maximum": 2147483647,\n "type": "integer"\n },\n "nanosec": {\n "title": "Nanosec",\n "default": 0,\n "minimum": 0,\n "maximum": 4294967295,\n "type": "integer"\n }\n },\n "required": [\n "sec",\n "nanosec"\n ]\n },\n "DoorMode": {\n "title": "DoorMode",\n "type": "object",\n "properties": {\n "value": {\n "title": "Value",\n "default": 0,\n "minimum": 0,\n "maximum": 4294967295,\n "type": "integer"\n }\n },\n "required": [\n "value"\n ]\n }\n }\n}\n```\n\n\n### /doors/{door_name}/health\n\n\n```\n{\n "title": "DoorHealth",\n "type": "object",\n "properties": {\n "health_status": {\n "title": "Health Status",\n "maxLength": 255,\n "nullable": true,\n "type": "string"\n },\n "health_message": {\n "title": "Health Message",\n "nullable": true,\n "type": "string"\n },\n "id_": {\n "title": "Id ",\n "maxLength": 255,\n "type": "string"\n }\n },\n "required": [\n "health_status",\n "id_"\n ],\n "additionalProperties": false\n}\n```\n\n\n### /lifts/{lift_name}/state\n\n\n```\n{\n "title": "LiftState",\n "type": "object",\n "properties": {\n "lift_time": {\n "title": "Lift Time",\n "default": {\n "sec": 0,\n "nanosec": 0\n },\n "allOf": [\n {\n "$ref": "#/definitions/Time"\n }\n ]\n },\n "lift_name": {\n "title": "Lift Name",\n "default": "",\n "type": "string"\n },\n "available_floors": {\n "title": "Available Floors",\n "default": [],\n "type": "array",\n "items": {\n "type": "string"\n }\n },\n "current_floor": {\n "title": "Current Floor",\n "default": "",\n "type": "string"\n },\n "destination_floor": {\n "title": "Destination Floor",\n "default": "",\n "type": "string"\n },\n "door_state": {\n "title": "Door State",\n "default": 0,\n "minimum": 0,\n "maximum": 255,\n "type": "integer"\n },\n "motion_state": {\n "title": "Motion State",\n "default": 0,\n "minimum": 0,\n "maximum": 255,\n "type": "integer"\n },\n "available_modes": {\n "title": "Available Modes",\n "type": "array",\n "items": {\n "type": "integer"\n }\n },\n "current_mode": {\n "title": "Current Mode",\n "default": 0,\n "minimum": 0,\n "maximum": 255,\n "type": "integer"\n },\n "session_id": {\n "title": "Session Id",\n "default": "",\n "type": "string"\n }\n },\n "required": [\n "lift_time",\n "lift_name",\n "available_floors",\n "current_floor",\n "destination_floor",\n "door_state",\n "motion_state",\n "available_modes",\n "current_mode",\n "session_id"\n ],\n "definitions": {\n "Time": {\n "title": "Time",\n "type": "object",\n "properties": {\n "sec": {\n "title": "Sec",\n "default": 0,\n "minimum": -2147483648,\n "maximum": 2147483647,\n "type": "integer"\n },\n "nanosec": {\n "title": "Nanosec",\n "default": 0,\n "minimum": 0,\n "maximum": 4294967295,\n "type": "integer"\n }\n },\n "required": [\n "sec",\n "nanosec"\n ]\n }\n }\n}\n```\n\n\n### /lifts/{lift_name}/health\n\n\n```\n{\n "title": "LiftHealth",\n "type": "object",\n "properties": {\n "health_status": {\n "title": "Health Status",\n "maxLength": 255,\n "nullable": true,\n "type": "string"\n },\n "health_message": {\n "title": "Health Message",\n "nullable": true,\n "type": "string"\n },\n "id_": {\n "title": "Id ",\n "maxLength": 255,\n "type": "string"\n }\n },\n "required": [\n "health_status",\n "id_"\n ],\n "additionalProperties": false\n}\n```\n\n\n### /tasks/{task_id}/state\n\n\n```\n{\n "title": "TaskState",\n "type": "object",\n "properties": {\n "booking": {\n "$ref": "#/definitions/Booking"\n },\n "category": {\n "$ref": "#/definitions/Category"\n },\n "detail": {\n "$ref": "#/definitions/Detail"\n },\n "unix_millis_start_time": {\n "title": "Unix Millis Start Time",\n "type": "integer"\n },\n "unix_millis_finish_time": {\n "title": "Unix Millis Finish Time",\n "type": "integer"\n },\n "original_estimate_millis": {\n "$ref": "#/definitions/EstimateMillis"\n },\n "estimate_millis": {\n "$ref": "#/definitions/EstimateMillis"\n },\n "assigned_to": {\n "title": "Assigned To",\n "description": "Which agent (robot) is the task assigned to",\n "allOf": [\n {\n "$ref": "#/definitions/AssignedTo"\n }\n ]\n },\n "status": {\n "$ref": "#/definitions/Status"\n },\n "dispatch": {\n "$ref": "#/definitions/Dispatch"\n },\n "phases": {\n "title": "Phases",\n "description": "A dictionary of the states of the phases of the task. The keys (property names) are phase IDs, which are integers.",\n "type": "object",\n "additionalProperties": {\n "$ref": "#/definitions/Phase"\n }\n },\n "completed": {\n "title": "Completed",\n "description": "An array of the IDs of completed phases of this task",\n "type": "array",\n "items": {\n "$ref": "#/definitions/Id"\n }\n },\n "active": {\n "title": "Active",\n "description": "The ID of the active phase for this task",\n "allOf": [\n {\n "$ref": "#/definitions/Id"\n }\n ]\n },\n "pending": {\n "title": "Pending",\n "description": "An array of the pending phases of this task",\n "type": "array",\n "items": {\n "$ref": "#/definitions/Id"\n }\n },\n "interruptions": {\n "title": "Interruptions",\n "description": "A dictionary of interruptions that have been applied to this task. The keys (property names) are the unique token of the interruption request.",\n "type": "object",\n "additionalProperties": {\n "$ref": "#/definitions/Interruption"\n }\n },\n "cancellation": {\n "title": "Cancellation",\n "description": "If the task was cancelled, this will describe information about the request.",\n "allOf": [\n {\n "$ref": "#/definitions/Cancellation"\n }\n ]\n },\n "killed": {\n "title": "Killed",\n "description": "If the task was killed, this will describe information about the request.",\n "allOf": [\n {\n "$ref": "#/definitions/Killed"\n }\n ]\n },\n "unix_millis_warn_time": {\n "title": "Unix Millis Warn Time",\n "type": "integer"\n }\n },\n "required": [\n "booking"\n ],\n "definitions": {\n "Booking": {\n "title": "Booking",\n "type": "object",\n "properties": {\n "id": {\n "title": "Id",\n "description": "The unique identifier for this task",\n "type": "string"\n },\n "unix_millis_earliest_start_time": {\n "title": "Unix Millis Earliest Start Time",\n "type": "integer"\n },\n "unix_millis_request_time": {\n "title": "Unix Millis Request Time",\n "type": "integer"\n },\n "priority": {\n "title": "Priority",\n "description": "Priority information about this task",\n "anyOf": [\n {\n "type": "object"\n },\n {\n "type": "string"\n }\n ]\n },\n "labels": {\n "title": "Labels",\n "description": "Information about how and why this task was booked",\n "type": "array",\n "items": {\n "type": "string"\n }\n },\n "requester": {\n "title": "Requester",\n "description": "(Optional) An identifier for the entity that requested this task",\n "type": "string"\n }\n },\n "required": [\n "id"\n ]\n },\n "Category": {\n "title": "Category",\n "description": "The category of this task or phase",\n "type": "string"\n },\n "Detail": {\n "title": "Detail",\n "description": "Detailed information about a task, phase, or event",\n "anyOf": [\n {\n "type": "object"\n },\n {\n "type": "array",\n "items": {}\n },\n {\n "type": "string"\n }\n ]\n },\n "EstimateMillis": {\n "title": "EstimateMillis",\n "description": "An estimate, in milliseconds, of how long the subject will take to complete",\n "minimum": 0,\n "type": "integer"\n },\n "AssignedTo": {\n "title": "AssignedTo",\n "type": "object",\n "properties": {\n "group": {\n "title": "Group",\n "type": "string"\n },\n "name": {\n "title": "Name",\n "type": "string"\n }\n },\n "required": [\n "group",\n "name"\n ]\n },\n "Status": {\n "title": "Status",\n "description": "An enumeration.",\n "enum": [\n "uninitialized",\n "blocked",\n "error",\n "failed",\n "queued",\n "standby",\n "underway",\n "delayed",\n "skipped",\n "canceled",\n "killed",\n "completed"\n ]\n },\n "Status1": {\n "title": "Status1",\n "description": "An enumeration.",\n "enum": [\n "queued",\n "selected",\n "dispatched",\n "failed_to_assign",\n "canceled_in_flight"\n ]\n },\n "Assignment": {\n "title": "Assignment",\n "type": "object",\n "properties": {\n "fleet_name": {\n "title": "Fleet Name",\n "type": "string"\n },\n "expected_robot_name": {\n "title": "Expected Robot Name",\n "type": "string"\n }\n }\n },\n "Error": {\n "title": "Error",\n "type": "object",\n "properties": {\n "code": {\n "title": "Code",\n "description": "A standard code for the kind of error that has occurred",\n "minimum": 0,\n "type": "integer"\n },\n "category": {\n "title": "Category",\n "description": "The category of the error",\n "type": "string"\n },\n "detail": {\n "title": "Detail",\n "description": "Details about the error",\n "type": "string"\n }\n }\n },\n "Dispatch": {\n "title": "Dispatch",\n "type": "object",\n "properties": {\n "status": {\n "$ref": "#/definitions/Status1"\n },\n "assignment": {\n "$ref": "#/definitions/Assignment"\n },\n "errors": {\n "title": "Errors",\n "type": "array",\n "items": {\n "$ref": "#/definitions/Error"\n }\n }\n },\n "required": [\n "status"\n ]\n },\n "Id": {\n "title": "Id",\n "minimum": 0,\n "type": "integer"\n },\n "EventState": {\n "title": "EventState",\n "type": "object",\n "properties": {\n "id": {\n "$ref": "#/definitions/Id"\n },\n "status": {\n "$ref": "#/definitions/Status"\n },\n "name": {\n "title": "Name",\n "description": "The brief name of the event",\n "type": "string"\n },\n "detail": {\n "title": "Detail",\n "description": "Detailed information about the event",\n "allOf": [\n {\n "$ref": "#/definitions/Detail"\n }\n ]\n },\n "deps": {\n "title": "Deps",\n "description": "This event may depend on other events. This array contains the IDs of those other event dependencies.",\n "type": "array",\n "items": {\n "type": "integer",\n "minimum": 0\n }\n }\n },\n "required": [\n "id"\n ]\n },\n "Undo": {\n "title": "Undo",\n "type": "object",\n "properties": {\n "unix_millis_request_time": {\n "title": "Unix Millis Request Time",\n "description": "The time that the undo skip request arrived",\n "type": "integer"\n },\n "labels": {\n "title": "Labels",\n "description": "Labels to describe the undo skip request",\n "type": "array",\n "items": {\n "type": "string"\n }\n }\n },\n "required": [\n "unix_millis_request_time",\n "labels"\n ]\n },\n "SkipPhaseRequest": {\n "title": "SkipPhaseRequest",\n "type": "object",\n "properties": {\n "unix_millis_request_time": {\n "title": "Unix Millis Request Time",\n "description": "The time that the skip request arrived",\n "type": "integer"\n },\n "labels": {\n "title": "Labels",\n "description": "Labels to describe the purpose of the skip request",\n "type": "array",\n "items": {\n "type": "string"\n }\n },\n "undo": {\n "title": "Undo",\n "description": "Information about an undo skip request that applied to this request",\n "allOf": [\n {\n "$ref": "#/definitions/Undo"\n }\n ]\n }\n },\n "required": [\n "unix_millis_request_time",\n "labels"\n ]\n },\n "Phase": {\n "title": "Phase",\n "type": "object",\n "properties": {\n "id": {\n "$ref": "#/definitions/Id"\n },\n "category": {\n "$ref": "#/definitions/Category"\n },\n "detail": {\n "$ref": "#/definitions/Detail"\n },\n "unix_millis_start_time": {\n "title": "Unix Millis Start Time",\n "type": "integer"\n },\n "unix_millis_finish_time": {\n "title": "Unix Millis Finish Time",\n "type": "integer"\n },\n "original_estimate_millis": {\n "$ref": "#/definitions/EstimateMillis"\n },\n "estimate_millis": {\n "$ref": "#/definitions/EstimateMillis"\n },\n "final_event_id": {\n "$ref": "#/definitions/Id"\n },\n "events": {\n "title": "Events",\n "description": "A dictionary of events for this phase. The keys (property names) are the event IDs, which are integers.",\n "type": "object",\n "additionalProperties": {\n "$ref": "#/definitions/EventState"\n }\n },\n "skip_requests": {\n "title": "Skip Requests",\n "description": "Information about any skip requests that have been received",\n "type": "object",\n "additionalProperties": {\n "$ref": "#/definitions/SkipPhaseRequest"\n }\n }\n },\n "required": [\n "id"\n ]\n },\n "ResumedBy": {\n "title": "ResumedBy",\n "type": "object",\n "properties": {\n "unix_millis_request_time": {\n "title": "Unix Millis Request Time",\n "description": "The time that the resume request arrived",\n "type": "integer"\n },\n "labels": {\n "title": "Labels",\n "description": "Labels to describe the resume request",\n "type": "array",\n "items": {\n "type": "string"\n }\n }\n },\n "required": [\n "labels"\n ]\n },\n "Interruption": {\n "title": "Interruption",\n "type": "object",\n "properties": {\n "unix_millis_request_time": {\n "title": "Unix Millis Request Time",\n "description": "The time that the interruption request arrived",\n "type": "integer"\n },\n "labels": {\n "title": "Labels",\n "description": "Labels to describe the purpose of the interruption",\n "type": "array",\n "items": {\n "type": "string"\n }\n },\n "resumed_by": {\n "title": "Resumed By",\n "description": "Information about the resume request that ended this interruption. This field will be missing if the interruption is still active.",\n "allOf": [\n {\n "$ref": "#/definitions/ResumedBy"\n }\n ]\n }\n },\n "required": [\n "unix_millis_request_time",\n "labels"\n ]\n },\n "Cancellation": {\n "title": "Cancellation",\n "type": "object",\n "properties": {\n "unix_millis_request_time": {\n "title": "Unix Millis Request Time",\n "description": "The time that the cancellation request arrived",\n "type": "integer"\n },\n "labels": {\n "title": "Labels",\n "description": "Labels to describe the cancel request",\n "type": "array",\n "items": {\n "type": "string"\n }\n }\n },\n "required": [\n "unix_millis_request_time",\n "labels"\n ]\n },\n "Killed": {\n "title": "Killed",\n "type": "object",\n "properties": {\n "unix_millis_request_time": {\n "title": "Unix Millis Request Time",\n "description": "The time that the cancellation request arrived",\n "type": "integer"\n },\n "labels": {\n "title": "Labels",\n "description": "Labels to describe the kill request",\n "type": "array",\n "items": {\n "type": "string"\n }\n }\n },\n "required": [\n "unix_millis_request_time",\n "labels"\n ]\n }\n }\n}\n```\n\n\n### /tasks/{task_id}/log\n\n\n```\n{\n "title": "TaskEventLog",\n "type": "object",\n "properties": {\n "task_id": {\n "title": "Task Id",\n "type": "string"\n },\n "log": {\n "title": "Log",\n "description": "Log entries related to the overall task",\n "type": "array",\n "items": {\n "$ref": "#/definitions/LogEntry"\n }\n },\n "phases": {\n "title": "Phases",\n "description": "A dictionary whose keys (property names) are the indices of a phase",\n "type": "object",\n "additionalProperties": {\n "$ref": "#/definitions/Phases"\n }\n }\n },\n "required": [\n "task_id"\n ],\n "additionalProperties": false,\n "definitions": {\n "Tier": {\n "title": "Tier",\n "description": "An enumeration.",\n "enum": [\n "uninitialized",\n "info",\n "warning",\n "error"\n ]\n },\n "LogEntry": {\n "title": "LogEntry",\n "type": "object",\n "properties": {\n "seq": {\n "title": "Seq",\n "description": "Sequence number for this entry. Each entry has a unique sequence number which monotonically increase, until integer overflow causes a wrap around.",\n "exclusiveMaximum": 4294967296,\n "minimum": 0,\n "type": "integer"\n },\n "tier": {\n "description": "The importance level of the log entry",\n "allOf": [\n {\n "$ref": "#/definitions/Tier"\n }\n ]\n },\n "unix_millis_time": {\n "title": "Unix Millis Time",\n "type": "integer"\n },\n "text": {\n "title": "Text",\n "description": "The text of the log entry",\n "type": "string"\n }\n },\n "required": [\n "seq",\n "tier",\n "unix_millis_time",\n "text"\n ]\n },\n "Phases": {\n "title": "Phases",\n "type": "object",\n "properties": {\n "log": {\n "title": "Log",\n "description": "Log entries related to the overall phase",\n "type": "array",\n "items": {\n "$ref": "#/definitions/LogEntry"\n }\n },\n "events": {\n "title": "Events",\n "description": "A dictionary whose keys (property names) are the indices of an event in the phase",\n "type": "object",\n "additionalProperties": {\n "type": "array",\n "items": {\n "$ref": "#/definitions/LogEntry"\n }\n }\n }\n },\n "additionalProperties": false\n }\n }\n}\n```\n\n\n### /dispensers/{guid}/state\n\n\n```\n{\n "title": "DispenserState",\n "type": "object",\n "properties": {\n "time": {\n "title": "Time",\n "default": {\n "sec": 0,\n "nanosec": 0\n },\n "allOf": [\n {\n "$ref": "#/definitions/Time"\n }\n ]\n },\n "guid": {\n "title": "Guid",\n "default": "",\n "type": "string"\n },\n "mode": {\n "title": "Mode",\n "default": 0,\n "minimum": -2147483648,\n "maximum": 2147483647,\n "type": "integer"\n },\n "request_guid_queue": {\n "title": "Request Guid Queue",\n "default": [],\n "type": "array",\n "items": {\n "type": "string"\n }\n },\n "seconds_remaining": {\n "title": "Seconds Remaining",\n "default": 0,\n "type": "number"\n }\n },\n "required": [\n "time",\n "guid",\n "mode",\n "request_guid_queue",\n "seconds_remaining"\n ],\n "definitions": {\n "Time": {\n "title": "Time",\n "type": "object",\n "properties": {\n "sec": {\n "title": "Sec",\n "default": 0,\n "minimum": -2147483648,\n "maximum": 2147483647,\n "type": "integer"\n },\n "nanosec": {\n "title": "Nanosec",\n "default": 0,\n "minimum": 0,\n "maximum": 4294967295,\n "type": "integer"\n }\n },\n "required": [\n "sec",\n "nanosec"\n ]\n }\n }\n}\n```\n\n\n### /dispensers/{guid}/health\n\n\n```\n{\n "title": "DispenserHealth",\n "type": "object",\n "properties": {\n "health_status": {\n "title": "Health Status",\n "maxLength": 255,\n "nullable": true,\n "type": "string"\n },\n "health_message": {\n "title": "Health Message",\n "nullable": true,\n "type": "string"\n },\n "id_": {\n "title": "Id ",\n "maxLength": 255,\n "type": "string"\n }\n },\n "required": [\n "health_status",\n "id_"\n ],\n "additionalProperties": false\n}\n```\n\n\n### /ingestors/{guid}/state\n\n\n```\n{\n "title": "IngestorState",\n "type": "object",\n "properties": {\n "time": {\n "title": "Time",\n "default": {\n "sec": 0,\n "nanosec": 0\n },\n "allOf": [\n {\n "$ref": "#/definitions/Time"\n }\n ]\n },\n "guid": {\n "title": "Guid",\n "default": "",\n "type": "string"\n },\n "mode": {\n "title": "Mode",\n "default": 0,\n "minimum": -2147483648,\n "maximum": 2147483647,\n "type": "integer"\n },\n "request_guid_queue": {\n "title": "Request Guid Queue",\n "default": [],\n "type": "array",\n "items": {\n "type": "string"\n }\n },\n "seconds_remaining": {\n "title": "Seconds Remaining",\n "default": 0,\n "type": "number"\n }\n },\n "required": [\n "time",\n "guid",\n "mode",\n "request_guid_queue",\n "seconds_remaining"\n ],\n "definitions": {\n "Time": {\n "title": "Time",\n "type": "object",\n "properties": {\n "sec": {\n "title": "Sec",\n "default": 0,\n "minimum": -2147483648,\n "maximum": 2147483647,\n "type": "integer"\n },\n "nanosec": {\n "title": "Nanosec",\n "default": 0,\n "minimum": 0,\n "maximum": 4294967295,\n "type": "integer"\n }\n },\n "required": [\n "sec",\n "nanosec"\n ]\n }\n }\n}\n```\n\n\n### /ingestors/{guid}/health\n\n\n```\n{\n "title": "IngestorHealth",\n "type": "object",\n "properties": {\n "health_status": {\n "title": "Health Status",\n "maxLength": 255,\n "nullable": true,\n "type": "string"\n },\n "health_message": {\n "title": "Health Message",\n "nullable": true,\n "type": "string"\n },\n "id_": {\n "title": "Id ",\n "maxLength": 255,\n "type": "string"\n }\n },\n "required": [\n "health_status",\n "id_"\n ],\n "additionalProperties": false\n}\n```\n\n\n### /fleets/{name}/state\n\n\n```\n{\n "title": "FleetState",\n "type": "object",\n "properties": {\n "name": {\n "title": "Name",\n "type": "string"\n },\n "robots": {\n "title": "Robots",\n "description": "A dictionary of the states of the robots that belong to this fleet",\n "type": "object",\n "additionalProperties": {\n "$ref": "#/definitions/RobotState"\n }\n }\n },\n "definitions": {\n "Status": {\n "title": "Status",\n "description": "An enumeration.",\n "enum": [\n "uninitialized",\n "offline",\n "shutdown",\n "idle",\n "charging",\n "working",\n "error"\n ]\n },\n "Location2D": {\n "title": "Location2D",\n "type": "object",\n "properties": {\n "map": {\n "title": "Map",\n "type": "string"\n },\n "x": {\n "title": "X",\n "type": "number"\n },\n "y": {\n "title": "Y",\n "type": "number"\n },\n "yaw": {\n "title": "Yaw",\n "type": "number"\n }\n },\n "required": [\n "map",\n "x",\n "y",\n "yaw"\n ]\n },\n "Issue": {\n "title": "Issue",\n "type": "object",\n "properties": {\n "category": {\n "title": "Category",\n "description": "Category of the robot\'s issue",\n "type": "string"\n },\n "detail": {\n "title": "Detail",\n "description": "Detailed information about the issue",\n "anyOf": [\n {\n "type": "object"\n },\n {\n "type": "array",\n "items": {}\n },\n {\n "type": "string"\n }\n ]\n }\n }\n },\n "Commission": {\n "title": "Commission",\n "type": "object",\n "properties": {\n "dispatch_tasks": {\n "title": "Dispatch Tasks",\n "description": "Should the robot accept dispatched tasks, true/false. When used in a request, leave this unset to not change the robot\'s current value.",\n "type": "boolean"\n },\n "direct_tasks": {\n "title": "Direct Tasks",\n "description": "Should the robot accept direct task requests, true/false. When used in a request, leave this unset to not change the robot\'s current value.",\n "type": "boolean"\n },\n "idle_behavior": {\n "title": "Idle Behavior",\n "description": "Should the robot perform its idle behavior, true/false. When used in a request, leave this unset to not change the robot\'s current value.",\n "type": "boolean"\n }\n }\n },\n "MutexGroups": {\n "title": "MutexGroups",\n "type": "object",\n "properties": {\n "locked": {\n "title": "Locked",\n "description": "A list of mutex groups that this robot has currently locked",\n "type": "array",\n "items": {\n "type": "string"\n }\n },\n "requesting": {\n "title": "Requesting",\n "description": "A list of the mutex groups that this robot is currently requesting but has not lockd yet",\n "type": "array",\n "items": {\n "type": "string"\n }\n }\n }\n },\n "RobotState": {\n "title": "RobotState",\n "type": "object",\n "properties": {\n "name": {\n "title": "Name",\n "type": "string"\n },\n "status": {\n "description": "A simple token representing the status of the robot",\n "allOf": [\n {\n "$ref": "#/definitions/Status"\n }\n ]\n },\n "task_id": {\n "title": "Task Id",\n "description": "The ID of the task this robot is currently working on. Empty string if the robot is not working on a task.",\n "type": "string"\n },\n "unix_millis_time": {\n "title": "Unix Millis Time",\n "type": "integer"\n },\n "location": {\n "$ref": "#/definitions/Location2D"\n },\n "battery": {\n "title": "Battery",\n "description": "State of charge of the battery. Values range from 0.0 (depleted) to 1.0 (fully charged)",\n "minimum": 0.0,\n "maximum": 1.0,\n "type": "number"\n },\n "issues": {\n "title": "Issues",\n "description": "A list of issues with the robot that operators need to address",\n "type": "array",\n "items": {\n "$ref": "#/definitions/Issue"\n }\n },\n "commission": {\n "$ref": "#/definitions/Commission"\n },\n "mutex_groups": {\n "title": "Mutex Groups",\n "description": "Information about the mutex groups that this robot is interacting with",\n "allOf": [\n {\n "$ref": "#/definitions/MutexGroups"\n }\n ]\n }\n }\n }\n }\n}\n```\n\n\n### /fleets/{name}/log\n\n\n```\n{\n "title": "FleetLog",\n "type": "object",\n "properties": {\n "name": {\n "title": "Name",\n "type": "string"\n },\n "log": {\n "title": "Log",\n "description": "Log for the overall fleet",\n "type": "array",\n "items": {\n "$ref": "#/definitions/LogEntry"\n }\n },\n "robots": {\n "title": "Robots",\n "description": "Dictionary of logs for the individual robots. The keys (property names) are the robot names.",\n "type": "object",\n "additionalProperties": {\n "type": "array",\n "items": {\n "$ref": "#/definitions/LogEntry"\n }\n }\n }\n },\n "definitions": {\n "Tier": {\n "title": "Tier",\n "description": "An enumeration.",\n "enum": [\n "uninitialized",\n "info",\n "warning",\n "error"\n ]\n },\n "LogEntry": {\n "title": "LogEntry",\n "type": "object",\n "properties": {\n "seq": {\n "title": "Seq",\n "description": "Sequence number for this entry. Each entry has a unique sequence number which monotonically increase, until integer overflow causes a wrap around.",\n "exclusiveMaximum": 4294967296,\n "minimum": 0,\n "type": "integer"\n },\n "tier": {\n "description": "The importance level of the log entry",\n "allOf": [\n {\n "$ref": "#/definitions/Tier"\n }\n ]\n },\n "unix_millis_time": {\n "title": "Unix Millis Time",\n "type": "integer"\n },\n "text": {\n "title": "Text",\n "description": "The text of the log entry",\n "type": "string"\n }\n },\n "required": [\n "seq",\n "tier",\n "unix_millis_time",\n "text"\n ]\n }\n }\n}\n```\n\n', + '\n# NOTE: This endpoint is here for documentation purposes only, this is _not_ a REST endpoint.\n\n## About\nThis exposes a minimal pubsub system built on top of socket.io.\nIt works similar to a normal socket.io endpoint, except that are 2 special\nrooms which control subscriptions.\n\n## Rooms\n### subscribe\nClients must send a message to this room to start receiving messages on other rooms.\nThe message must be of the form:\n\n```\n{\n "room": ""\n}\n```\n\n### unsubscribe\nClients can send a message to this room to stop receiving messages on other rooms.\nThe message must be of the form:\n\n```\n{\n "room": ""\n}\n```\n \n### /alerts\n\n\n```\n{\n "title": "Alert",\n "description": "General alert that can be triggered by events.",\n "type": "object",\n "properties": {\n "id": {\n "title": "Id",\n "maxLength": 255,\n "type": "string"\n },\n "original_id": {\n "title": "Original Id",\n "maxLength": 255,\n "type": "string"\n },\n "category": {\n "title": "Category",\n "description": "Default: default
Task: task
Fleet: fleet
Robot: robot",\n "maxLength": 7,\n "type": "string"\n },\n "unix_millis_created_time": {\n "title": "Unix Millis Created Time",\n "minimum": -9223372036854775808,\n "maximum": 9223372036854775807,\n "type": "integer"\n },\n "acknowledged_by": {\n "title": "Acknowledged By",\n "maxLength": 255,\n "nullable": true,\n "type": "string"\n },\n "unix_millis_acknowledged_time": {\n "title": "Unix Millis Acknowledged Time",\n "minimum": -9223372036854775808,\n "maximum": 9223372036854775807,\n "nullable": true,\n "type": "integer"\n }\n },\n "required": [\n "id",\n "original_id",\n "category",\n "unix_millis_created_time"\n ],\n "additionalProperties": false\n}\n```\n\n\n### /beacons\n\n\n```\n{\n "title": "BeaconState",\n "type": "object",\n "properties": {\n "id": {\n "title": "Id",\n "maxLength": 255,\n "type": "string"\n },\n "online": {\n "title": "Online",\n "type": "boolean"\n },\n "category": {\n "title": "Category",\n "maxLength": 255,\n "nullable": true,\n "type": "string"\n },\n "activated": {\n "title": "Activated",\n "type": "boolean"\n },\n "level": {\n "title": "Level",\n "maxLength": 255,\n "nullable": true,\n "type": "string"\n }\n },\n "required": [\n "id",\n "online",\n "activated"\n ],\n "additionalProperties": false\n}\n```\n\n\n### /building_map\n\n\n```\n{\n "title": "BuildingMap",\n "type": "object",\n "properties": {\n "name": {\n "title": "Name",\n "default": "",\n "type": "string"\n },\n "levels": {\n "title": "Levels",\n "type": "array",\n "items": {\n "$ref": "#/definitions/Level"\n }\n },\n "lifts": {\n "title": "Lifts",\n "default": [],\n "type": "array",\n "items": {\n "$ref": "#/definitions/Lift"\n }\n }\n },\n "required": [\n "name",\n "levels",\n "lifts"\n ],\n "definitions": {\n "AffineImage": {\n "title": "AffineImage",\n "type": "object",\n "properties": {\n "name": {\n "title": "Name",\n "default": "",\n "type": "string"\n },\n "x_offset": {\n "title": "X Offset",\n "default": 0,\n "type": "number"\n },\n "y_offset": {\n "title": "Y Offset",\n "default": 0,\n "type": "number"\n },\n "yaw": {\n "title": "Yaw",\n "default": 0,\n "type": "number"\n },\n "scale": {\n "title": "Scale",\n "default": 0,\n "type": "number"\n },\n "encoding": {\n "title": "Encoding",\n "default": "",\n "type": "string"\n },\n "data": {\n "title": "Data",\n "type": "string"\n }\n },\n "required": [\n "name",\n "x_offset",\n "y_offset",\n "yaw",\n "scale",\n "encoding",\n "data"\n ]\n },\n "Place": {\n "title": "Place",\n "type": "object",\n "properties": {\n "name": {\n "title": "Name",\n "default": "",\n "type": "string"\n },\n "x": {\n "title": "X",\n "default": 0,\n "type": "number"\n },\n "y": {\n "title": "Y",\n "default": 0,\n "type": "number"\n },\n "yaw": {\n "title": "Yaw",\n "default": 0,\n "type": "number"\n },\n "position_tolerance": {\n "title": "Position Tolerance",\n "default": 0,\n "type": "number"\n },\n "yaw_tolerance": {\n "title": "Yaw Tolerance",\n "default": 0,\n "type": "number"\n }\n },\n "required": [\n "name",\n "x",\n "y",\n "yaw",\n "position_tolerance",\n "yaw_tolerance"\n ]\n },\n "Door": {\n "title": "Door",\n "type": "object",\n "properties": {\n "name": {\n "title": "Name",\n "default": "",\n "type": "string"\n },\n "v1_x": {\n "title": "V1 X",\n "default": 0,\n "type": "number"\n },\n "v1_y": {\n "title": "V1 Y",\n "default": 0,\n "type": "number"\n },\n "v2_x": {\n "title": "V2 X",\n "default": 0,\n "type": "number"\n },\n "v2_y": {\n "title": "V2 Y",\n "default": 0,\n "type": "number"\n },\n "door_type": {\n "title": "Door Type",\n "default": 0,\n "minimum": 0,\n "maximum": 255,\n "type": "integer"\n },\n "motion_range": {\n "title": "Motion Range",\n "default": 0,\n "type": "number"\n },\n "motion_direction": {\n "title": "Motion Direction",\n "default": 0,\n "minimum": -2147483648,\n "maximum": 2147483647,\n "type": "integer"\n }\n },\n "required": [\n "name",\n "v1_x",\n "v1_y",\n "v2_x",\n "v2_y",\n "door_type",\n "motion_range",\n "motion_direction"\n ]\n },\n "Param": {\n "title": "Param",\n "type": "object",\n "properties": {\n "name": {\n "title": "Name",\n "default": "",\n "type": "string"\n },\n "type": {\n "title": "Type",\n "default": 0,\n "minimum": 0,\n "maximum": 4294967295,\n "type": "integer"\n },\n "value_int": {\n "title": "Value Int",\n "default": 0,\n "minimum": -2147483648,\n "maximum": 2147483647,\n "type": "integer"\n },\n "value_float": {\n "title": "Value Float",\n "default": 0,\n "type": "number"\n },\n "value_string": {\n "title": "Value String",\n "default": "",\n "type": "string"\n },\n "value_bool": {\n "title": "Value Bool",\n "default": false,\n "type": "boolean"\n }\n },\n "required": [\n "name",\n "type",\n "value_int",\n "value_float",\n "value_string",\n "value_bool"\n ]\n },\n "GraphNode": {\n "title": "GraphNode",\n "type": "object",\n "properties": {\n "x": {\n "title": "X",\n "default": 0,\n "type": "number"\n },\n "y": {\n "title": "Y",\n "default": 0,\n "type": "number"\n },\n "name": {\n "title": "Name",\n "default": "",\n "type": "string"\n },\n "params": {\n "title": "Params",\n "default": [],\n "type": "array",\n "items": {\n "$ref": "#/definitions/Param"\n }\n }\n },\n "required": [\n "x",\n "y",\n "name",\n "params"\n ]\n },\n "GraphEdge": {\n "title": "GraphEdge",\n "type": "object",\n "properties": {\n "v1_idx": {\n "title": "V1 Idx",\n "default": 0,\n "minimum": 0,\n "maximum": 4294967295,\n "type": "integer"\n },\n "v2_idx": {\n "title": "V2 Idx",\n "default": 0,\n "minimum": 0,\n "maximum": 4294967295,\n "type": "integer"\n },\n "params": {\n "title": "Params",\n "default": [],\n "type": "array",\n "items": {\n "$ref": "#/definitions/Param"\n }\n },\n "edge_type": {\n "title": "Edge Type",\n "default": 0,\n "minimum": 0,\n "maximum": 255,\n "type": "integer"\n }\n },\n "required": [\n "v1_idx",\n "v2_idx",\n "params",\n "edge_type"\n ]\n },\n "Graph": {\n "title": "Graph",\n "type": "object",\n "properties": {\n "name": {\n "title": "Name",\n "default": "",\n "type": "string"\n },\n "vertices": {\n "title": "Vertices",\n "default": [],\n "type": "array",\n "items": {\n "$ref": "#/definitions/GraphNode"\n }\n },\n "edges": {\n "title": "Edges",\n "default": [],\n "type": "array",\n "items": {\n "$ref": "#/definitions/GraphEdge"\n }\n },\n "params": {\n "title": "Params",\n "default": [],\n "type": "array",\n "items": {\n "$ref": "#/definitions/Param"\n }\n }\n },\n "required": [\n "name",\n "vertices",\n "edges",\n "params"\n ]\n },\n "Level": {\n "title": "Level",\n "type": "object",\n "properties": {\n "name": {\n "title": "Name",\n "default": "",\n "type": "string"\n },\n "elevation": {\n "title": "Elevation",\n "default": 0,\n "type": "number"\n },\n "images": {\n "title": "Images",\n "type": "array",\n "items": {\n "$ref": "#/definitions/AffineImage"\n }\n },\n "places": {\n "title": "Places",\n "default": [],\n "type": "array",\n "items": {\n "$ref": "#/definitions/Place"\n }\n },\n "doors": {\n "title": "Doors",\n "default": [],\n "type": "array",\n "items": {\n "$ref": "#/definitions/Door"\n }\n },\n "nav_graphs": {\n "title": "Nav Graphs",\n "default": [],\n "type": "array",\n "items": {\n "$ref": "#/definitions/Graph"\n }\n },\n "wall_graph": {\n "title": "Wall Graph",\n "default": {\n "name": "",\n "vertices": [],\n "edges": [],\n "params": []\n },\n "allOf": [\n {\n "$ref": "#/definitions/Graph"\n }\n ]\n }\n },\n "required": [\n "name",\n "elevation",\n "images",\n "places",\n "doors",\n "nav_graphs",\n "wall_graph"\n ]\n },\n "Lift": {\n "title": "Lift",\n "type": "object",\n "properties": {\n "name": {\n "title": "Name",\n "default": "",\n "type": "string"\n },\n "levels": {\n "title": "Levels",\n "default": [],\n "type": "array",\n "items": {\n "type": "string"\n }\n },\n "doors": {\n "title": "Doors",\n "default": [],\n "type": "array",\n "items": {\n "$ref": "#/definitions/Door"\n }\n },\n "wall_graph": {\n "title": "Wall Graph",\n "default": {\n "name": "",\n "vertices": [],\n "edges": [],\n "params": []\n },\n "allOf": [\n {\n "$ref": "#/definitions/Graph"\n }\n ]\n },\n "ref_x": {\n "title": "Ref X",\n "default": 0,\n "type": "number"\n },\n "ref_y": {\n "title": "Ref Y",\n "default": 0,\n "type": "number"\n },\n "ref_yaw": {\n "title": "Ref Yaw",\n "default": 0,\n "type": "number"\n },\n "width": {\n "title": "Width",\n "default": 0,\n "type": "number"\n },\n "depth": {\n "title": "Depth",\n "default": 0,\n "type": "number"\n }\n },\n "required": [\n "name",\n "levels",\n "doors",\n "wall_graph",\n "ref_x",\n "ref_y",\n "ref_yaw",\n "width",\n "depth"\n ]\n }\n }\n}\n```\n\n\n### /building_map/fire_alarm_trigger\n\n\n```\n{\n "title": "FireAlarmTriggerState",\n "type": "object",\n "properties": {\n "unix_millis_time": {\n "title": "Unix Millis Time",\n "type": "integer"\n },\n "trigger": {\n "title": "Trigger",\n "type": "boolean"\n }\n },\n "required": [\n "unix_millis_time",\n "trigger"\n ]\n}\n```\n\n\n### /delivery_alerts\n\n\n```\n{\n "title": "DeliveryAlert",\n "type": "object",\n "properties": {\n "id": {\n "title": "Id",\n "type": "string"\n },\n "category": {\n "$ref": "#/definitions/Category"\n },\n "tier": {\n "$ref": "#/definitions/Tier"\n },\n "action": {\n "$ref": "#/definitions/Action"\n },\n "task_id": {\n "title": "Task Id",\n "type": "string"\n },\n "message": {\n "title": "Message",\n "type": "string"\n }\n },\n "required": [\n "id",\n "category",\n "tier",\n "action",\n "task_id",\n "message"\n ],\n "definitions": {\n "Category": {\n "title": "Category",\n "description": "An enumeration.",\n "enum": [\n "missing",\n "wrong",\n "obstructed",\n "cancelled"\n ],\n "type": "string"\n },\n "Tier": {\n "title": "Tier",\n "description": "An enumeration.",\n "enum": [\n "warning",\n "error"\n ],\n "type": "string"\n },\n "Action": {\n "title": "Action",\n "description": "An enumeration.",\n "enum": [\n "waiting",\n "cancelled",\n "override",\n "resume"\n ],\n "type": "string"\n }\n }\n}\n```\n\n\n### /doors/{door_name}/state\n\n\n```\n{\n "title": "DoorState",\n "type": "object",\n "properties": {\n "door_time": {\n "title": "Door Time",\n "default": {\n "sec": 0,\n "nanosec": 0\n },\n "allOf": [\n {\n "$ref": "#/definitions/Time"\n }\n ]\n },\n "door_name": {\n "title": "Door Name",\n "default": "",\n "type": "string"\n },\n "current_mode": {\n "title": "Current Mode",\n "default": {\n "value": 0\n },\n "allOf": [\n {\n "$ref": "#/definitions/DoorMode"\n }\n ]\n }\n },\n "required": [\n "door_time",\n "door_name",\n "current_mode"\n ],\n "definitions": {\n "Time": {\n "title": "Time",\n "type": "object",\n "properties": {\n "sec": {\n "title": "Sec",\n "default": 0,\n "minimum": -2147483648,\n "maximum": 2147483647,\n "type": "integer"\n },\n "nanosec": {\n "title": "Nanosec",\n "default": 0,\n "minimum": 0,\n "maximum": 4294967295,\n "type": "integer"\n }\n },\n "required": [\n "sec",\n "nanosec"\n ]\n },\n "DoorMode": {\n "title": "DoorMode",\n "type": "object",\n "properties": {\n "value": {\n "title": "Value",\n "default": 0,\n "minimum": 0,\n "maximum": 4294967295,\n "type": "integer"\n }\n },\n "required": [\n "value"\n ]\n }\n }\n}\n```\n\n\n### /doors/{door_name}/health\n\n\n```\n{\n "title": "DoorHealth",\n "type": "object",\n "properties": {\n "health_status": {\n "title": "Health Status",\n "maxLength": 255,\n "nullable": true,\n "type": "string"\n },\n "health_message": {\n "title": "Health Message",\n "nullable": true,\n "type": "string"\n },\n "id_": {\n "title": "Id ",\n "maxLength": 255,\n "type": "string"\n }\n },\n "required": [\n "health_status",\n "id_"\n ],\n "additionalProperties": false\n}\n```\n\n\n### /lifts/{lift_name}/state\n\n\n```\n{\n "title": "LiftState",\n "type": "object",\n "properties": {\n "lift_time": {\n "title": "Lift Time",\n "default": {\n "sec": 0,\n "nanosec": 0\n },\n "allOf": [\n {\n "$ref": "#/definitions/Time"\n }\n ]\n },\n "lift_name": {\n "title": "Lift Name",\n "default": "",\n "type": "string"\n },\n "available_floors": {\n "title": "Available Floors",\n "default": [],\n "type": "array",\n "items": {\n "type": "string"\n }\n },\n "current_floor": {\n "title": "Current Floor",\n "default": "",\n "type": "string"\n },\n "destination_floor": {\n "title": "Destination Floor",\n "default": "",\n "type": "string"\n },\n "door_state": {\n "title": "Door State",\n "default": 0,\n "minimum": 0,\n "maximum": 255,\n "type": "integer"\n },\n "motion_state": {\n "title": "Motion State",\n "default": 0,\n "minimum": 0,\n "maximum": 255,\n "type": "integer"\n },\n "available_modes": {\n "title": "Available Modes",\n "type": "array",\n "items": {\n "type": "integer"\n }\n },\n "current_mode": {\n "title": "Current Mode",\n "default": 0,\n "minimum": 0,\n "maximum": 255,\n "type": "integer"\n },\n "session_id": {\n "title": "Session Id",\n "default": "",\n "type": "string"\n }\n },\n "required": [\n "lift_time",\n "lift_name",\n "available_floors",\n "current_floor",\n "destination_floor",\n "door_state",\n "motion_state",\n "available_modes",\n "current_mode",\n "session_id"\n ],\n "definitions": {\n "Time": {\n "title": "Time",\n "type": "object",\n "properties": {\n "sec": {\n "title": "Sec",\n "default": 0,\n "minimum": -2147483648,\n "maximum": 2147483647,\n "type": "integer"\n },\n "nanosec": {\n "title": "Nanosec",\n "default": 0,\n "minimum": 0,\n "maximum": 4294967295,\n "type": "integer"\n }\n },\n "required": [\n "sec",\n "nanosec"\n ]\n }\n }\n}\n```\n\n\n### /lifts/{lift_name}/health\n\n\n```\n{\n "title": "LiftHealth",\n "type": "object",\n "properties": {\n "health_status": {\n "title": "Health Status",\n "maxLength": 255,\n "nullable": true,\n "type": "string"\n },\n "health_message": {\n "title": "Health Message",\n "nullable": true,\n "type": "string"\n },\n "id_": {\n "title": "Id ",\n "maxLength": 255,\n "type": "string"\n }\n },\n "required": [\n "health_status",\n "id_"\n ],\n "additionalProperties": false\n}\n```\n\n\n### /tasks/{task_id}/state\n\n\n```\n{\n "title": "TaskState",\n "type": "object",\n "properties": {\n "booking": {\n "$ref": "#/definitions/Booking"\n },\n "category": {\n "$ref": "#/definitions/Category"\n },\n "detail": {\n "$ref": "#/definitions/Detail"\n },\n "unix_millis_start_time": {\n "title": "Unix Millis Start Time",\n "type": "integer"\n },\n "unix_millis_finish_time": {\n "title": "Unix Millis Finish Time",\n "type": "integer"\n },\n "original_estimate_millis": {\n "$ref": "#/definitions/EstimateMillis"\n },\n "estimate_millis": {\n "$ref": "#/definitions/EstimateMillis"\n },\n "assigned_to": {\n "title": "Assigned To",\n "description": "Which agent (robot) is the task assigned to",\n "allOf": [\n {\n "$ref": "#/definitions/AssignedTo"\n }\n ]\n },\n "status": {\n "$ref": "#/definitions/Status"\n },\n "dispatch": {\n "$ref": "#/definitions/Dispatch"\n },\n "phases": {\n "title": "Phases",\n "description": "A dictionary of the states of the phases of the task. The keys (property names) are phase IDs, which are integers.",\n "type": "object",\n "additionalProperties": {\n "$ref": "#/definitions/Phase"\n }\n },\n "completed": {\n "title": "Completed",\n "description": "An array of the IDs of completed phases of this task",\n "type": "array",\n "items": {\n "$ref": "#/definitions/Id"\n }\n },\n "active": {\n "title": "Active",\n "description": "The ID of the active phase for this task",\n "allOf": [\n {\n "$ref": "#/definitions/Id"\n }\n ]\n },\n "pending": {\n "title": "Pending",\n "description": "An array of the pending phases of this task",\n "type": "array",\n "items": {\n "$ref": "#/definitions/Id"\n }\n },\n "interruptions": {\n "title": "Interruptions",\n "description": "A dictionary of interruptions that have been applied to this task. The keys (property names) are the unique token of the interruption request.",\n "type": "object",\n "additionalProperties": {\n "$ref": "#/definitions/Interruption"\n }\n },\n "cancellation": {\n "title": "Cancellation",\n "description": "If the task was cancelled, this will describe information about the request.",\n "allOf": [\n {\n "$ref": "#/definitions/Cancellation"\n }\n ]\n },\n "killed": {\n "title": "Killed",\n "description": "If the task was killed, this will describe information about the request.",\n "allOf": [\n {\n "$ref": "#/definitions/Killed"\n }\n ]\n }\n },\n "required": [\n "booking"\n ],\n "definitions": {\n "Booking": {\n "title": "Booking",\n "type": "object",\n "properties": {\n "id": {\n "title": "Id",\n "description": "The unique identifier for this task",\n "type": "string"\n },\n "unix_millis_earliest_start_time": {\n "title": "Unix Millis Earliest Start Time",\n "type": "integer"\n },\n "unix_millis_request_time": {\n "title": "Unix Millis Request Time",\n "type": "integer"\n },\n "priority": {\n "title": "Priority",\n "description": "Priority information about this task",\n "anyOf": [\n {\n "type": "object"\n },\n {\n "type": "string"\n }\n ]\n },\n "labels": {\n "title": "Labels",\n "description": "Information about how and why this task was booked",\n "type": "array",\n "items": {\n "type": "string"\n }\n },\n "requester": {\n "title": "Requester",\n "description": "(Optional) An identifier for the entity that requested this task",\n "type": "string"\n }\n },\n "required": [\n "id"\n ]\n },\n "Category": {\n "title": "Category",\n "description": "The category of this task or phase",\n "type": "string"\n },\n "Detail": {\n "title": "Detail",\n "description": "Detailed information about a task, phase, or event",\n "anyOf": [\n {\n "type": "object"\n },\n {\n "type": "array",\n "items": {}\n },\n {\n "type": "string"\n }\n ]\n },\n "EstimateMillis": {\n "title": "EstimateMillis",\n "description": "An estimate, in milliseconds, of how long the subject will take to complete",\n "minimum": 0,\n "type": "integer"\n },\n "AssignedTo": {\n "title": "AssignedTo",\n "type": "object",\n "properties": {\n "group": {\n "title": "Group",\n "type": "string"\n },\n "name": {\n "title": "Name",\n "type": "string"\n }\n },\n "required": [\n "group",\n "name"\n ]\n },\n "Status": {\n "title": "Status",\n "description": "An enumeration.",\n "enum": [\n "uninitialized",\n "blocked",\n "error",\n "failed",\n "queued",\n "standby",\n "underway",\n "delayed",\n "skipped",\n "canceled",\n "killed",\n "completed"\n ]\n },\n "Status1": {\n "title": "Status1",\n "description": "An enumeration.",\n "enum": [\n "queued",\n "selected",\n "dispatched",\n "failed_to_assign",\n "canceled_in_flight"\n ]\n },\n "Assignment": {\n "title": "Assignment",\n "type": "object",\n "properties": {\n "fleet_name": {\n "title": "Fleet Name",\n "type": "string"\n },\n "expected_robot_name": {\n "title": "Expected Robot Name",\n "type": "string"\n }\n }\n },\n "Error": {\n "title": "Error",\n "type": "object",\n "properties": {\n "code": {\n "title": "Code",\n "description": "A standard code for the kind of error that has occurred",\n "minimum": 0,\n "type": "integer"\n },\n "category": {\n "title": "Category",\n "description": "The category of the error",\n "type": "string"\n },\n "detail": {\n "title": "Detail",\n "description": "Details about the error",\n "type": "string"\n }\n }\n },\n "Dispatch": {\n "title": "Dispatch",\n "type": "object",\n "properties": {\n "status": {\n "$ref": "#/definitions/Status1"\n },\n "assignment": {\n "$ref": "#/definitions/Assignment"\n },\n "errors": {\n "title": "Errors",\n "type": "array",\n "items": {\n "$ref": "#/definitions/Error"\n }\n }\n },\n "required": [\n "status"\n ]\n },\n "Id": {\n "title": "Id",\n "minimum": 0,\n "type": "integer"\n },\n "EventState": {\n "title": "EventState",\n "type": "object",\n "properties": {\n "id": {\n "$ref": "#/definitions/Id"\n },\n "status": {\n "$ref": "#/definitions/Status"\n },\n "name": {\n "title": "Name",\n "description": "The brief name of the event",\n "type": "string"\n },\n "detail": {\n "title": "Detail",\n "description": "Detailed information about the event",\n "allOf": [\n {\n "$ref": "#/definitions/Detail"\n }\n ]\n },\n "deps": {\n "title": "Deps",\n "description": "This event may depend on other events. This array contains the IDs of those other event dependencies.",\n "type": "array",\n "items": {\n "type": "integer",\n "minimum": 0\n }\n }\n },\n "required": [\n "id"\n ]\n },\n "Undo": {\n "title": "Undo",\n "type": "object",\n "properties": {\n "unix_millis_request_time": {\n "title": "Unix Millis Request Time",\n "description": "The time that the undo skip request arrived",\n "type": "integer"\n },\n "labels": {\n "title": "Labels",\n "description": "Labels to describe the undo skip request",\n "type": "array",\n "items": {\n "type": "string"\n }\n }\n },\n "required": [\n "unix_millis_request_time",\n "labels"\n ]\n },\n "SkipPhaseRequest": {\n "title": "SkipPhaseRequest",\n "type": "object",\n "properties": {\n "unix_millis_request_time": {\n "title": "Unix Millis Request Time",\n "description": "The time that the skip request arrived",\n "type": "integer"\n },\n "labels": {\n "title": "Labels",\n "description": "Labels to describe the purpose of the skip request",\n "type": "array",\n "items": {\n "type": "string"\n }\n },\n "undo": {\n "title": "Undo",\n "description": "Information about an undo skip request that applied to this request",\n "allOf": [\n {\n "$ref": "#/definitions/Undo"\n }\n ]\n }\n },\n "required": [\n "unix_millis_request_time",\n "labels"\n ]\n },\n "Phase": {\n "title": "Phase",\n "type": "object",\n "properties": {\n "id": {\n "$ref": "#/definitions/Id"\n },\n "category": {\n "$ref": "#/definitions/Category"\n },\n "detail": {\n "$ref": "#/definitions/Detail"\n },\n "unix_millis_start_time": {\n "title": "Unix Millis Start Time",\n "type": "integer"\n },\n "unix_millis_finish_time": {\n "title": "Unix Millis Finish Time",\n "type": "integer"\n },\n "original_estimate_millis": {\n "$ref": "#/definitions/EstimateMillis"\n },\n "estimate_millis": {\n "$ref": "#/definitions/EstimateMillis"\n },\n "final_event_id": {\n "$ref": "#/definitions/Id"\n },\n "events": {\n "title": "Events",\n "description": "A dictionary of events for this phase. The keys (property names) are the event IDs, which are integers.",\n "type": "object",\n "additionalProperties": {\n "$ref": "#/definitions/EventState"\n }\n },\n "skip_requests": {\n "title": "Skip Requests",\n "description": "Information about any skip requests that have been received",\n "type": "object",\n "additionalProperties": {\n "$ref": "#/definitions/SkipPhaseRequest"\n }\n }\n },\n "required": [\n "id"\n ]\n },\n "ResumedBy": {\n "title": "ResumedBy",\n "type": "object",\n "properties": {\n "unix_millis_request_time": {\n "title": "Unix Millis Request Time",\n "description": "The time that the resume request arrived",\n "type": "integer"\n },\n "labels": {\n "title": "Labels",\n "description": "Labels to describe the resume request",\n "type": "array",\n "items": {\n "type": "string"\n }\n }\n },\n "required": [\n "labels"\n ]\n },\n "Interruption": {\n "title": "Interruption",\n "type": "object",\n "properties": {\n "unix_millis_request_time": {\n "title": "Unix Millis Request Time",\n "description": "The time that the interruption request arrived",\n "type": "integer"\n },\n "labels": {\n "title": "Labels",\n "description": "Labels to describe the purpose of the interruption",\n "type": "array",\n "items": {\n "type": "string"\n }\n },\n "resumed_by": {\n "title": "Resumed By",\n "description": "Information about the resume request that ended this interruption. This field will be missing if the interruption is still active.",\n "allOf": [\n {\n "$ref": "#/definitions/ResumedBy"\n }\n ]\n }\n },\n "required": [\n "unix_millis_request_time",\n "labels"\n ]\n },\n "Cancellation": {\n "title": "Cancellation",\n "type": "object",\n "properties": {\n "unix_millis_request_time": {\n "title": "Unix Millis Request Time",\n "description": "The time that the cancellation request arrived",\n "type": "integer"\n },\n "labels": {\n "title": "Labels",\n "description": "Labels to describe the cancel request",\n "type": "array",\n "items": {\n "type": "string"\n }\n }\n },\n "required": [\n "unix_millis_request_time",\n "labels"\n ]\n },\n "Killed": {\n "title": "Killed",\n "type": "object",\n "properties": {\n "unix_millis_request_time": {\n "title": "Unix Millis Request Time",\n "description": "The time that the cancellation request arrived",\n "type": "integer"\n },\n "labels": {\n "title": "Labels",\n "description": "Labels to describe the kill request",\n "type": "array",\n "items": {\n "type": "string"\n }\n }\n },\n "required": [\n "unix_millis_request_time",\n "labels"\n ]\n }\n }\n}\n```\n\n\n### /tasks/{task_id}/log\n\n\n```\n{\n "title": "TaskEventLog",\n "type": "object",\n "properties": {\n "task_id": {\n "title": "Task Id",\n "type": "string"\n },\n "log": {\n "title": "Log",\n "description": "Log entries related to the overall task",\n "type": "array",\n "items": {\n "$ref": "#/definitions/LogEntry"\n }\n },\n "phases": {\n "title": "Phases",\n "description": "A dictionary whose keys (property names) are the indices of a phase",\n "type": "object",\n "additionalProperties": {\n "$ref": "#/definitions/Phases"\n }\n }\n },\n "required": [\n "task_id"\n ],\n "additionalProperties": false,\n "definitions": {\n "Tier": {\n "title": "Tier",\n "description": "An enumeration.",\n "enum": [\n "uninitialized",\n "info",\n "warning",\n "error"\n ]\n },\n "LogEntry": {\n "title": "LogEntry",\n "type": "object",\n "properties": {\n "seq": {\n "title": "Seq",\n "description": "Sequence number for this entry. Each entry has a unique sequence number which monotonically increase, until integer overflow causes a wrap around.",\n "exclusiveMaximum": 4294967296,\n "minimum": 0,\n "type": "integer"\n },\n "tier": {\n "description": "The importance level of the log entry",\n "allOf": [\n {\n "$ref": "#/definitions/Tier"\n }\n ]\n },\n "unix_millis_time": {\n "title": "Unix Millis Time",\n "type": "integer"\n },\n "text": {\n "title": "Text",\n "description": "The text of the log entry",\n "type": "string"\n }\n },\n "required": [\n "seq",\n "tier",\n "unix_millis_time",\n "text"\n ]\n },\n "Phases": {\n "title": "Phases",\n "type": "object",\n "properties": {\n "log": {\n "title": "Log",\n "description": "Log entries related to the overall phase",\n "type": "array",\n "items": {\n "$ref": "#/definitions/LogEntry"\n }\n },\n "events": {\n "title": "Events",\n "description": "A dictionary whose keys (property names) are the indices of an event in the phase",\n "type": "object",\n "additionalProperties": {\n "type": "array",\n "items": {\n "$ref": "#/definitions/LogEntry"\n }\n }\n }\n },\n "additionalProperties": false\n }\n }\n}\n```\n\n\n### /dispensers/{guid}/state\n\n\n```\n{\n "title": "DispenserState",\n "type": "object",\n "properties": {\n "time": {\n "title": "Time",\n "default": {\n "sec": 0,\n "nanosec": 0\n },\n "allOf": [\n {\n "$ref": "#/definitions/Time"\n }\n ]\n },\n "guid": {\n "title": "Guid",\n "default": "",\n "type": "string"\n },\n "mode": {\n "title": "Mode",\n "default": 0,\n "minimum": -2147483648,\n "maximum": 2147483647,\n "type": "integer"\n },\n "request_guid_queue": {\n "title": "Request Guid Queue",\n "default": [],\n "type": "array",\n "items": {\n "type": "string"\n }\n },\n "seconds_remaining": {\n "title": "Seconds Remaining",\n "default": 0,\n "type": "number"\n }\n },\n "required": [\n "time",\n "guid",\n "mode",\n "request_guid_queue",\n "seconds_remaining"\n ],\n "definitions": {\n "Time": {\n "title": "Time",\n "type": "object",\n "properties": {\n "sec": {\n "title": "Sec",\n "default": 0,\n "minimum": -2147483648,\n "maximum": 2147483647,\n "type": "integer"\n },\n "nanosec": {\n "title": "Nanosec",\n "default": 0,\n "minimum": 0,\n "maximum": 4294967295,\n "type": "integer"\n }\n },\n "required": [\n "sec",\n "nanosec"\n ]\n }\n }\n}\n```\n\n\n### /dispensers/{guid}/health\n\n\n```\n{\n "title": "DispenserHealth",\n "type": "object",\n "properties": {\n "health_status": {\n "title": "Health Status",\n "maxLength": 255,\n "nullable": true,\n "type": "string"\n },\n "health_message": {\n "title": "Health Message",\n "nullable": true,\n "type": "string"\n },\n "id_": {\n "title": "Id ",\n "maxLength": 255,\n "type": "string"\n }\n },\n "required": [\n "health_status",\n "id_"\n ],\n "additionalProperties": false\n}\n```\n\n\n### /ingestors/{guid}/state\n\n\n```\n{\n "title": "IngestorState",\n "type": "object",\n "properties": {\n "time": {\n "title": "Time",\n "default": {\n "sec": 0,\n "nanosec": 0\n },\n "allOf": [\n {\n "$ref": "#/definitions/Time"\n }\n ]\n },\n "guid": {\n "title": "Guid",\n "default": "",\n "type": "string"\n },\n "mode": {\n "title": "Mode",\n "default": 0,\n "minimum": -2147483648,\n "maximum": 2147483647,\n "type": "integer"\n },\n "request_guid_queue": {\n "title": "Request Guid Queue",\n "default": [],\n "type": "array",\n "items": {\n "type": "string"\n }\n },\n "seconds_remaining": {\n "title": "Seconds Remaining",\n "default": 0,\n "type": "number"\n }\n },\n "required": [\n "time",\n "guid",\n "mode",\n "request_guid_queue",\n "seconds_remaining"\n ],\n "definitions": {\n "Time": {\n "title": "Time",\n "type": "object",\n "properties": {\n "sec": {\n "title": "Sec",\n "default": 0,\n "minimum": -2147483648,\n "maximum": 2147483647,\n "type": "integer"\n },\n "nanosec": {\n "title": "Nanosec",\n "default": 0,\n "minimum": 0,\n "maximum": 4294967295,\n "type": "integer"\n }\n },\n "required": [\n "sec",\n "nanosec"\n ]\n }\n }\n}\n```\n\n\n### /ingestors/{guid}/health\n\n\n```\n{\n "title": "IngestorHealth",\n "type": "object",\n "properties": {\n "health_status": {\n "title": "Health Status",\n "maxLength": 255,\n "nullable": true,\n "type": "string"\n },\n "health_message": {\n "title": "Health Message",\n "nullable": true,\n "type": "string"\n },\n "id_": {\n "title": "Id ",\n "maxLength": 255,\n "type": "string"\n }\n },\n "required": [\n "health_status",\n "id_"\n ],\n "additionalProperties": false\n}\n```\n\n\n### /fleets/{name}/state\n\n\n```\n{\n "title": "FleetState",\n "type": "object",\n "properties": {\n "name": {\n "title": "Name",\n "type": "string"\n },\n "robots": {\n "title": "Robots",\n "description": "A dictionary of the states of the robots that belong to this fleet",\n "type": "object",\n "additionalProperties": {\n "$ref": "#/definitions/RobotState"\n }\n }\n },\n "definitions": {\n "Status": {\n "title": "Status",\n "description": "An enumeration.",\n "enum": [\n "uninitialized",\n "offline",\n "shutdown",\n "idle",\n "charging",\n "working",\n "error"\n ]\n },\n "Location2D": {\n "title": "Location2D",\n "type": "object",\n "properties": {\n "map": {\n "title": "Map",\n "type": "string"\n },\n "x": {\n "title": "X",\n "type": "number"\n },\n "y": {\n "title": "Y",\n "type": "number"\n },\n "yaw": {\n "title": "Yaw",\n "type": "number"\n }\n },\n "required": [\n "map",\n "x",\n "y",\n "yaw"\n ]\n },\n "Issue": {\n "title": "Issue",\n "type": "object",\n "properties": {\n "category": {\n "title": "Category",\n "description": "Category of the robot\'s issue",\n "type": "string"\n },\n "detail": {\n "title": "Detail",\n "description": "Detailed information about the issue",\n "anyOf": [\n {\n "type": "object"\n },\n {\n "type": "array",\n "items": {}\n },\n {\n "type": "string"\n }\n ]\n }\n }\n },\n "Commission": {\n "title": "Commission",\n "type": "object",\n "properties": {\n "dispatch_tasks": {\n "title": "Dispatch Tasks",\n "description": "Should the robot accept dispatched tasks, true/false. When used in a request, leave this unset to not change the robot\'s current value.",\n "type": "boolean"\n },\n "direct_tasks": {\n "title": "Direct Tasks",\n "description": "Should the robot accept direct task requests, true/false. When used in a request, leave this unset to not change the robot\'s current value.",\n "type": "boolean"\n },\n "idle_behavior": {\n "title": "Idle Behavior",\n "description": "Should the robot perform its idle behavior, true/false. When used in a request, leave this unset to not change the robot\'s current value.",\n "type": "boolean"\n }\n }\n },\n "MutexGroups": {\n "title": "MutexGroups",\n "type": "object",\n "properties": {\n "locked": {\n "title": "Locked",\n "description": "A list of mutex groups that this robot has currently locked",\n "type": "array",\n "items": {\n "type": "string"\n }\n },\n "requesting": {\n "title": "Requesting",\n "description": "A list of the mutex groups that this robot is currently requesting but has not lockd yet",\n "type": "array",\n "items": {\n "type": "string"\n }\n }\n }\n },\n "RobotState": {\n "title": "RobotState",\n "type": "object",\n "properties": {\n "name": {\n "title": "Name",\n "type": "string"\n },\n "status": {\n "description": "A simple token representing the status of the robot",\n "allOf": [\n {\n "$ref": "#/definitions/Status"\n }\n ]\n },\n "task_id": {\n "title": "Task Id",\n "description": "The ID of the task this robot is currently working on. Empty string if the robot is not working on a task.",\n "type": "string"\n },\n "unix_millis_time": {\n "title": "Unix Millis Time",\n "type": "integer"\n },\n "location": {\n "$ref": "#/definitions/Location2D"\n },\n "battery": {\n "title": "Battery",\n "description": "State of charge of the battery. Values range from 0.0 (depleted) to 1.0 (fully charged)",\n "minimum": 0.0,\n "maximum": 1.0,\n "type": "number"\n },\n "issues": {\n "title": "Issues",\n "description": "A list of issues with the robot that operators need to address",\n "type": "array",\n "items": {\n "$ref": "#/definitions/Issue"\n }\n },\n "commission": {\n "$ref": "#/definitions/Commission"\n },\n "mutex_groups": {\n "title": "Mutex Groups",\n "description": "Information about the mutex groups that this robot is interacting with",\n "allOf": [\n {\n "$ref": "#/definitions/MutexGroups"\n }\n ]\n }\n }\n }\n }\n}\n```\n\n\n### /fleets/{name}/log\n\n\n```\n{\n "title": "FleetLog",\n "type": "object",\n "properties": {\n "name": {\n "title": "Name",\n "type": "string"\n },\n "log": {\n "title": "Log",\n "description": "Log for the overall fleet",\n "type": "array",\n "items": {\n "$ref": "#/definitions/LogEntry"\n }\n },\n "robots": {\n "title": "Robots",\n "description": "Dictionary of logs for the individual robots. The keys (property names) are the robot names.",\n "type": "object",\n "additionalProperties": {\n "type": "array",\n "items": {\n "$ref": "#/definitions/LogEntry"\n }\n }\n }\n },\n "definitions": {\n "Tier": {\n "title": "Tier",\n "description": "An enumeration.",\n "enum": [\n "uninitialized",\n "info",\n "warning",\n "error"\n ]\n },\n "LogEntry": {\n "title": "LogEntry",\n "type": "object",\n "properties": {\n "seq": {\n "title": "Seq",\n "description": "Sequence number for this entry. Each entry has a unique sequence number which monotonically increase, until integer overflow causes a wrap around.",\n "exclusiveMaximum": 4294967296,\n "minimum": 0,\n "type": "integer"\n },\n "tier": {\n "description": "The importance level of the log entry",\n "allOf": [\n {\n "$ref": "#/definitions/Tier"\n }\n ]\n },\n "unix_millis_time": {\n "title": "Unix Millis Time",\n "type": "integer"\n },\n "text": {\n "title": "Text",\n "description": "The text of the log entry",\n "type": "string"\n }\n },\n "required": [\n "seq",\n "tier",\n "unix_millis_time",\n "text"\n ]\n }\n }\n}\n```\n\n', operationId: '_lambda__socket_io_get', responses: { '200': { @@ -3942,12 +3942,6 @@ export default { description: '(Optional) The name of the fleet that should perform this task. If specified, other fleets will not bid for this task.', }, - unix_millis_warn_time: { - title: 'Unix Millis Warn Time', - type: 'integer', - description: - '(Optional) The time at which a warning will be issued if the estimated completion time is later than expected', - }, }, }, TaskResumeRequest: { @@ -4080,7 +4074,6 @@ export default { description: 'If the task was killed, this will describe information about the request.', }, - unix_millis_warn_time: { title: 'Unix Millis Warn Time', type: 'integer' }, }, }, Time: { diff --git a/packages/api-server/api_server/models/rmf_api/task_request.py b/packages/api-server/api_server/models/rmf_api/task_request.py index 98c0a61df..91e0c6c46 100644 --- a/packages/api-server/api_server/models/rmf_api/task_request.py +++ b/packages/api-server/api_server/models/rmf_api/task_request.py @@ -35,7 +35,3 @@ class TaskRequest(BaseModel): None, description="(Optional) The name of the fleet that should perform this task. If specified, other fleets will not bid for this task.", ) - unix_millis_warn_time: Optional[int] = Field( - None, - description="(Optional) The time at which a warning will be issued if the estimated completion time is later than expected", - ) diff --git a/packages/api-server/api_server/models/rmf_api/task_state.py b/packages/api-server/api_server/models/rmf_api/task_state.py index 1a17f7cc6..997fe1ed2 100644 --- a/packages/api-server/api_server/models/rmf_api/task_state.py +++ b/packages/api-server/api_server/models/rmf_api/task_state.py @@ -212,4 +212,3 @@ class TaskState(BaseModel): None, description="If the task was killed, this will describe information about the request.", ) - unix_millis_warn_time: Optional[int] = None diff --git a/packages/api-server/api_server/models/tortoise_models/tasks.py b/packages/api-server/api_server/models/tortoise_models/tasks.py index 6e3f3e665..4cad99989 100644 --- a/packages/api-server/api_server/models/tortoise_models/tasks.py +++ b/packages/api-server/api_server/models/tortoise_models/tasks.py @@ -28,10 +28,14 @@ class TaskState(Model): status = CharField(255, null=True, index=True) unix_millis_request_time = DatetimeField(null=True, index=True) requester = CharField(255, null=True, index=True) - unix_millis_warn_time = DatetimeField(null=True, index=True) - pickup = CharField(255, null=True, index=True) - destination = CharField(255, null=True, index=True) + _do_not_use_pickup = CharField(255, null=True, index=True, source_field="pickup") + _do_not_use_destination = CharField( + 255, null=True, index=True, source_field="destination" + ) labels = ReverseRelation["TaskLabel"] + _do_not_use_unix_millis_warn_time = DatetimeField( + null=True, index=True, source_field="unix_millis_warn_time" + ) class TaskLabel(Model): diff --git a/packages/api-server/api_server/routes/internal.py b/packages/api-server/api_server/routes/internal.py index 0e6b94226..cca37fbc7 100644 --- a/packages/api-server/api_server/routes/internal.py +++ b/packages/api-server/api_server/routes/internal.py @@ -104,17 +104,6 @@ async def process_msg( alert = await alert_repo.create_alert(task_state.booking.id, "task") if alert is not None: alert_events.alerts.on_next(alert) - elif ( - task_state.unix_millis_finish_time - and task_state.unix_millis_warn_time - and task_state.unix_millis_finish_time > task_state.unix_millis_warn_time - ): - # TODO(AC): Perhaps set a late alert as its own category - late_alert_id = f"{task_state.booking.id}__late" - if not await alert_repo.alert_original_id_exists(late_alert_id): - alert = await alert_repo.create_alert(late_alert_id, "task") - if alert is not None: - alert_events.alerts.on_next(alert) elif payload_type == "task_log_update": task_log = mdl.TaskEventLog(**msg["data"]) diff --git a/packages/api-server/api_server/routes/tasks/tasks.py b/packages/api-server/api_server/routes/tasks/tasks.py index 11a43326f..b5b7671dd 100644 --- a/packages/api-server/api_server/routes/tasks/tasks.py +++ b/packages/api-server/api_server/routes/tasks/tasks.py @@ -295,8 +295,6 @@ async def post_dispatch_task( task_repo: TaskRepository = Depends(TaskRepository), logger: LoggerAdapter = Depends(get_logger), ): - task_warn_time = request.request.unix_millis_warn_time - # FIXME: In order to accommodate changing cancellation lots over time, and # avoiding updating all the saved scheduled tasks in the database, we only # insert cancellation lots as part of the cancellation behavior before @@ -344,8 +342,6 @@ async def post_dispatch_task( if not resp.__root__.success: return RawJSONResponse(resp.json(), 400) new_state = cast(mdl.TaskDispatchResponseItem, resp.__root__).state - if task_warn_time is not None: - new_state.unix_millis_warn_time = task_warn_time await task_repo.save_task_state(new_state) await task_repo.save_task_request(new_state, request.request) return resp.__root__ diff --git a/packages/dashboard/src/components/appbar.tsx b/packages/dashboard/src/components/appbar.tsx index 76a3f5224..464be49a2 100644 --- a/packages/dashboard/src/components/appbar.tsx +++ b/packages/dashboard/src/components/appbar.tsx @@ -474,10 +474,16 @@ export const AppBar = React.memo(({ extraToolbarItems }: AppBarProps): React.Rea key={alert.id} title={ - Alert - ID: {alert.original_id} - Type: {alert.category.toUpperCase()} - + + Alert + + + ID: {alert.original_id} + + + Type: {alert.category.toUpperCase()} + + Created: {new Date(alert.unix_millis_created_time).toLocaleString()} diff --git a/packages/dashboard/src/components/tasks/task-alert.tsx b/packages/dashboard/src/components/tasks/task-alert.tsx index 84af097ff..3319abf0b 100644 --- a/packages/dashboard/src/components/tasks/task-alert.tsx +++ b/packages/dashboard/src/components/tasks/task-alert.tsx @@ -62,13 +62,6 @@ export function TaskAlertDialog({ alert, removeAlert }: TaskAlertDialogProps): J if (errorLogEntries.length !== 0) { return 'Task error'; } - if ( - state.unix_millis_finish_time && - state.unix_millis_warn_time && - state.unix_millis_finish_time > state.unix_millis_warn_time - ) { - return 'Task warning'; - } return 'Task alert'; }; @@ -145,20 +138,6 @@ export function TaskAlertDialog({ alert, removeAlert }: TaskAlertDialogProps): J value: `${completionTimeString}Task completed!`, }, ]; - } else if ( - state.unix_millis_finish_time && - state.unix_millis_warn_time && - state.unix_millis_finish_time > state.unix_millis_warn_time - ) { - const completionTimeString = `${new Date(state.unix_millis_finish_time).toLocaleString()}`; - const warningTimeString = `${new Date(state.unix_millis_warn_time).toLocaleString()}`; - content = [ - ...content, - { - title: 'Late', - value: `Task is estimated to complete at ${completionTimeString}, later than the expected ${warningTimeString}.`, - }, - ]; } return content; @@ -183,14 +162,6 @@ export function TaskAlertDialog({ alert, removeAlert }: TaskAlertDialogProps): J return base.palette.error.dark; } - if ( - state.unix_millis_finish_time && - state.unix_millis_warn_time && - state.unix_millis_finish_time > state.unix_millis_warn_time - ) { - return base.palette.warning.dark; - } - return base.palette.background.default; }; diff --git a/packages/react-components/lib/tasks/create-task.tsx b/packages/react-components/lib/tasks/create-task.tsx index fc62722c0..4635b2e76 100644 --- a/packages/react-components/lib/tasks/create-task.tsx +++ b/packages/react-components/lib/tasks/create-task.tsx @@ -43,7 +43,10 @@ import React from 'react'; import { Loading } from '..'; import { ConfirmationDialog, ConfirmationDialogProps } from '../confirmation-dialog'; import { PositiveIntField } from '../form-inputs'; -import { serializeTaskBookingLabel } from './task-booking-label-utils'; +import { + getTaskBookingLabelFromTaskRequest, + serializeTaskBookingLabel, +} from './task-booking-label-utils'; interface TaskDefinition { task_definition_id: string; @@ -1403,9 +1406,23 @@ export function CreateTaskForm({ setScheduleUntilValue(event.target.value); }; - const [warnTimeChecked, setWarnTimeChecked] = React.useState(false); + const existingBookingLabel = requestTask + ? getTaskBookingLabelFromTaskRequest(requestTask) + : undefined; + let existingWarnTime: Date | null = null; + if (existingBookingLabel && existingBookingLabel.description.unix_millis_warn_time) { + const warnTimeInt = parseInt(existingBookingLabel.description.unix_millis_warn_time as string); + if (!Number.isNaN(warnTimeInt)) { + existingWarnTime = new Date(warnTimeInt); + } + } + const [warnTime, setWarnTime] = React.useState(existingWarnTime); const handleWarnTimeCheckboxChange = (event: React.ChangeEvent) => { - setWarnTimeChecked(event.target.checked); + if (event.target.checked) { + setWarnTime(new Date()); + } else { + setWarnTime(null); + } }; const handleTaskDescriptionChange = (newCategory: string, newDesc: TaskDescription) => { @@ -1570,6 +1587,10 @@ export function CreateTaskForm({ return; } + if (warnTime !== null) { + requestBookingLabel.description.unix_millis_warn_time = `${warnTime.valueOf()}`; + } + const labelString = serializeTaskBookingLabel(requestBookingLabel); if (labelString) { request.labels = [labelString]; @@ -1779,7 +1800,7 @@ export function CreateTaskForm({ { - if (!date || !warnTimeChecked) { - return; - } - taskRequest.unix_millis_warn_time = date.valueOf(); - setTaskRequest((prev) => { - return { - ...prev, - unix_millis_warn_time: date.valueOf(), - }; - }); + setWarnTime(date); }} label="Warn Time" renderInput={(props) => ( diff --git a/packages/react-components/lib/tasks/task-booking-label-utils.tsx b/packages/react-components/lib/tasks/task-booking-label-utils.tsx index bfd4b0150..1f9bc0d94 100644 --- a/packages/react-components/lib/tasks/task-booking-label-utils.tsx +++ b/packages/react-components/lib/tasks/task-booking-label-utils.tsx @@ -1,6 +1,6 @@ import { ajv } from '../utils/schema-utils'; import schema from 'api-client/dist/schema'; -import type { TaskBookingLabel, TaskState } from 'api-client'; +import type { TaskBookingLabel, TaskRequest, TaskState } from 'api-client'; const validateTaskBookingLabel = ajv.compile(schema.components.schemas.TaskBookingLabel); @@ -44,3 +44,23 @@ export function getTaskBookingLabelFromTaskState(taskState: TaskState): TaskBook } return requestLabel; } + +export function getTaskBookingLabelFromTaskRequest( + taskRequest: TaskRequest, +): TaskBookingLabel | null { + let requestLabel: TaskBookingLabel | null = null; + if (taskRequest.labels) { + for (const label of taskRequest.labels) { + try { + const parsedLabel = getTaskBookingLabelFromJsonString(label); + if (parsedLabel) { + requestLabel = parsedLabel; + break; + } + } catch (e) { + continue; + } + } + } + return requestLabel; +} diff --git a/packages/react-components/lib/tasks/task-table-datagrid.tsx b/packages/react-components/lib/tasks/task-table-datagrid.tsx index d63c3cc12..75befa93a 100644 --- a/packages/react-components/lib/tasks/task-table-datagrid.tsx +++ b/packages/react-components/lib/tasks/task-table-datagrid.tsx @@ -11,9 +11,22 @@ import { GridFilterModel, GridSortModel, } from '@mui/x-data-grid'; -import { styled, Stack, Typography, Tooltip, useMediaQuery, SxProps, Theme } from '@mui/material'; +import { + Box, + styled, + Stack, + Typography, + Tooltip, + useMediaQuery, + SxProps, + Theme, +} from '@mui/material'; import * as React from 'react'; -import { TaskState, ApiServerModelsRmfApiTaskStateStatus as Status } from 'api-client'; +import { + TaskState, + ApiServerModelsRmfApiTaskStateStatus as Status, + TaskBookingLabel, +} from 'api-client'; import { InsertInvitation as ScheduleIcon, Person as UserIcon } from '@mui/icons-material/'; import { getTaskBookingLabelFromTaskState } from './task-booking-label-utils'; @@ -25,6 +38,7 @@ const classes = { taskPendingCell: 'MuiDataGrid-cell-pending-cell', taskQueuedCell: 'MuiDataGrid-cell-queued-cell', taskUnknownCell: 'MuiDataGrid-cell-unknown-cell', + taskLateCell: 'MuiDataGrid-cell-late-cell', }; const StyledDataGrid = styled(DataGrid)(({ theme }) => ({ @@ -52,6 +66,10 @@ const StyledDataGrid = styled(DataGrid)(({ theme }) => ({ backgroundColor: theme.palette.warning.main, color: theme.palette.getContrastText(theme.palette.warning.main), }, + [`& .${classes.taskLateCell}`]: { + backgroundColor: theme.palette.warning.main, + color: theme.palette.getContrastText(theme.palette.warning.main), + }, })); export interface Tasks { @@ -62,6 +80,11 @@ export interface Tasks { pageSize: number; } +interface TaskData { + state: TaskState; + requestLabel: TaskBookingLabel | null; +} + export interface FilterFields { model: GridFilterModel | undefined; } @@ -138,7 +161,7 @@ export function TaskDataGridTable({ event: MuiMouseEvent, ) => { if (onTaskClick) { - onTaskClick(event, params.row); + onTaskClick(event, params.row.state); } }; @@ -158,11 +181,13 @@ export function TaskDataGridTable({ width: 150, editable: false, renderCell: (cellValues) => { - const date = new Date(cellValues.row.booking.unix_millis_request_time); + const date = new Date(cellValues.row.state.booking.unix_millis_request_time); const day = date.toLocaleDateString(undefined, { day: 'numeric' }); const month = date.toLocaleDateString(undefined, { month: 'short' }); const year = date.toLocaleDateString(undefined, { year: 'numeric' }); - return cellValues.row.booking.unix_millis_request_time ? `${day} ${month} ${year}` : 'n/a'; + return cellValues.row.state.booking.unix_millis_request_time + ? `${day} ${month} ${year}` + : 'n/a'; }, flex: 1, filterOperators: getMinimalDateOperators, @@ -173,7 +198,7 @@ export function TaskDataGridTable({ headerName: 'Requester', width: 150, editable: false, - renderCell: (cellValues) => TaskRequester(cellValues.row.booking.requester, sxProp), + renderCell: (cellValues) => TaskRequester(cellValues.row.state.booking.requester, sxProp), flex: 1, filterOperators: getMinimalStringFilterOperators, filterable: true, @@ -184,9 +209,8 @@ export function TaskDataGridTable({ width: 150, editable: false, valueGetter: (params: GridValueGetterParams) => { - const requestLabel = getTaskBookingLabelFromTaskState(params.row); - if (requestLabel && requestLabel.description.pickup) { - return requestLabel.description.pickup; + if (params.row.requestLabel && params.row.requestLabel.description.pickup) { + return params.row.requestLabel.description.pickup; } return 'n/a'; }, @@ -200,9 +224,8 @@ export function TaskDataGridTable({ width: 150, editable: false, valueGetter: (params: GridValueGetterParams) => { - const requestLabel = getTaskBookingLabelFromTaskState(params.row); - if (requestLabel && requestLabel.description.destination) { - return requestLabel.description.destination; + if (params.row.requestLabel && params.row.requestLabel.description.destination) { + return params.row.requestLabel.description.destination; } return 'n/a'; }, @@ -216,7 +239,7 @@ export function TaskDataGridTable({ width: 100, editable: false, valueGetter: (params: GridValueGetterParams) => - params.row.assigned_to ? params.row.assigned_to.name : 'n/a', + params.row.state.assigned_to ? params.row.state.assigned_to.name : 'n/a', flex: 1, filterOperators: getMinimalStringFilterOperators, filterable: true, @@ -226,10 +249,23 @@ export function TaskDataGridTable({ headerName: 'Start Time', width: 150, editable: false, - renderCell: (cellValues) => - cellValues.row.unix_millis_start_time - ? `${new Date(cellValues.row.unix_millis_start_time).toLocaleTimeString()}` - : 'n/a', + renderCell: (cellValues) => { + const startDateTime = cellValues.row.state.unix_millis_start_time + ? new Date(cellValues.row.state.unix_millis_start_time) + : undefined; + const startTimeString = startDateTime ? `${startDateTime.toLocaleTimeString()}` : 'n/a'; + return ( + + Start time: {startDateTime ? startDateTime.toLocaleString() : 'n/a'} + + } + > + {startTimeString} + + ); + }, flex: 1, filterOperators: getMinimalDateOperators, filterable: true, @@ -239,10 +275,38 @@ export function TaskDataGridTable({ headerName: 'End Time', width: 150, editable: false, - renderCell: (cellValues) => - cellValues.row.unix_millis_finish_time - ? `${new Date(cellValues.row.unix_millis_finish_time).toLocaleTimeString()}` - : 'n/a', + renderCell: (cellValues) => { + let warnDateTime: Date | undefined = undefined; + if (cellValues.row.requestLabel?.description.unix_millis_warn_time) { + const warnMillisNum = parseInt( + cellValues.row.requestLabel.description.unix_millis_warn_time as string, + ); + if (!Number.isNaN(warnMillisNum)) { + warnDateTime = new Date(warnMillisNum); + } + } + + const finishDateTime = cellValues.row.state.unix_millis_finish_time + ? new Date(cellValues.row.state.unix_millis_finish_time) + : undefined; + const finishTimeString = finishDateTime ? `${finishDateTime.toLocaleTimeString()}` : 'n/a'; + return ( + + + Warning time: {warnDateTime ? warnDateTime.toLocaleString() : 'n/a'} + + + Finish time: {finishDateTime ? finishDateTime.toLocaleString() : 'n/a'} + + + } + > + {finishTimeString} + + ); + }, flex: 1, filterOperators: getMinimalDateOperators, filterable: true, @@ -252,7 +316,7 @@ export function TaskDataGridTable({ headerName: 'State', editable: false, valueGetter: (params: GridValueGetterParams) => - params.row.status ? params.row.status : 'unknown', + params.row.state.status ? params.row.state.status : 'unknown', flex: 1, filterOperators: getMinimalStringFilterOperators, filterable: true, @@ -273,12 +337,19 @@ export function TaskDataGridTable({ [setSortFields], ); + const taskData: TaskData[] = tasks.data.map((state) => { + return { + state, + requestLabel: getTaskBookingLabelFromTaskState(state), + }; + }); + return (
r.booking.id} - rows={tasks.data} + getRowId={(r) => r.state.booking.id} + rows={taskData} rowCount={tasks.total} loading={tasks.isLoading} pageSize={tasks.pageSize} @@ -313,6 +384,26 @@ export function TaskDataGridTable({ default: return classes.taskUnknownCell; } + } else if (params.field === 'unix_millis_finish_time') { + if (!params.value) { + return classes.taskUnknownCell; + } + + let warnDateTime: Date | undefined = undefined; + if (params.row.requestLabel?.description.unix_millis_warn_time) { + const warnMillisNum = parseInt( + params.row.requestLabel.description.unix_millis_warn_time as string, + ); + if (!Number.isNaN(warnMillisNum)) { + warnDateTime = new Date(warnMillisNum); + } + } + + const finishDateTime = params.value ? new Date(params.value) : undefined; + + if (warnDateTime && finishDateTime && finishDateTime > warnDateTime) { + return classes.taskLateCell; + } } return ''; }} From 6ad01a4e552d570cafd091f9c8763964db9f5da2 Mon Sep 17 00:00:00 2001 From: Aaron Chong Date: Wed, 12 Jun 2024 12:15:10 +0800 Subject: [PATCH 3/5] Shows outdated tasks as stale (#954) * Shows outdated tasks as interrupted Signed-off-by: Aaron Chong * Use stale instead of interrupted Signed-off-by: Aaron Chong * Remove config parameter, change status display string only Signed-off-by: Aaron Chong * Remove unused resource config Signed-off-by: Aaron Chong * Fix merge conflict bug where statuses and finish time are always warning colored Signed-off-by: Aaron Chong --------- Signed-off-by: Aaron Chong --- .../lib/tasks/task-table-datagrid.tsx | 47 ++++++++++++++++++- 1 file changed, 45 insertions(+), 2 deletions(-) diff --git a/packages/react-components/lib/tasks/task-table-datagrid.tsx b/packages/react-components/lib/tasks/task-table-datagrid.tsx index 75befa93a..9b2ad8952 100644 --- a/packages/react-components/lib/tasks/task-table-datagrid.tsx +++ b/packages/react-components/lib/tasks/task-table-datagrid.tsx @@ -72,6 +72,20 @@ const StyledDataGrid = styled(DataGrid)(({ theme }) => ({ }, })); +function isTaskOutdated(taskState: TaskState): boolean { + if ( + !taskState.unix_millis_finish_time || + !taskState.status || + (taskState.status !== Status.Underway && taskState.status !== Status.Queued) + ) { + return false; + } + + const finishDateTime = new Date(taskState.unix_millis_finish_time); + const nowDateTime = new Date(); + return nowDateTime > finishDateTime; +} + export interface Tasks { isLoading: boolean; data: TaskState[]; @@ -317,6 +331,29 @@ export function TaskDataGridTable({ editable: false, valueGetter: (params: GridValueGetterParams) => params.row.state.status ? params.row.state.status : 'unknown', + renderCell: (cellValues) => { + const statusString = cellValues.row.state.status ? cellValues.row.state.status : 'unknown'; + if (isTaskOutdated(cellValues.row.state)) { + return ( + + + Finish time is in the past, but task is still queued or underway. + + + The task may have been interrupted or stalled during the execution. + + + } + > + {`${statusString} (stale)`} + + ); + } + + return {statusString}; + }, flex: 1, filterOperators: getMinimalStringFilterOperators, filterable: true, @@ -370,6 +407,10 @@ export function TaskDataGridTable({ onRowClick={handleEvent} getCellClassName={(params: GridCellParams) => { if (params.field === 'status') { + if (isTaskOutdated(params.row.state)) { + return classes.taskUnknownCell; + } + switch (params.value) { case Status.Underway: return classes.taskActiveCell; @@ -385,7 +426,7 @@ export function TaskDataGridTable({ return classes.taskUnknownCell; } } else if (params.field === 'unix_millis_finish_time') { - if (!params.value) { + if (!params.row.state.unix_millis_finish_time) { return classes.taskUnknownCell; } @@ -399,7 +440,9 @@ export function TaskDataGridTable({ } } - const finishDateTime = params.value ? new Date(params.value) : undefined; + const finishDateTime = params.row.state.unix_millis_finish_time + ? new Date(params.row.state.unix_millis_finish_time) + : undefined; if (warnDateTime && finishDateTime && finishDateTime > warnDateTime) { return classes.taskLateCell; From d2e56f3e1711d370c638180189bdbe1c1e6ac97e Mon Sep 17 00:00:00 2001 From: Teo Koon Peng Date: Mon, 24 Jun 2024 16:06:27 +0800 Subject: [PATCH 4/5] api-server: port postgres fix to deploy/hammer (#958) * port postgres fix by avoiding changes to Pipfile Signed-off-by: Teo Koon Peng * fix lint Signed-off-by: Teo Koon Peng * revert pnpm-lock Signed-off-by: Teo Koon Peng * Changes to UI to make filters and sorting work (#959) Signed-off-by: Aaron Chong --------- Signed-off-by: Teo Koon Peng Signed-off-by: Aaron Chong Co-authored-by: Aaron Chong --- packages/api-server/.pylintrc | 1 + packages/api-server/README.md | 12 ++- .../api-server/api_server/dependencies.py | 6 +- .../api_server/models/pagination.py | 4 +- packages/api-server/api_server/query.py | 53 ++------- .../api_server/repositories/tasks.py | 102 +++++++++++++++--- .../routes/tasks/scheduled_tasks.py | 2 +- .../api_server/routes/tasks/tasks.py | 97 ++++------------- .../api_server/routes/tasks/test_tasks.py | 48 ++++----- .../api_server/routes/test_building_map.py | 3 +- .../api_server/routes/test_dispensers.py | 14 +-- .../api_server/routes/test_doors.py | 13 ++- .../api_server/routes/test_fleets.py | 60 ++++++----- .../api_server/routes/test_ingestors.py | 14 +-- .../api_server/routes/test_lifts.py | 14 +-- .../api-server/api_server/test/__init__.py | 1 - .../api_server/test/test_fixtures.py | 93 ++++++++++------ .../api-server/api_server/test/test_stress.py | 5 +- .../api-server/api_server/test/test_utils.py | 12 --- .../api-server/api_server/test_sio_auth.py | 31 ++---- .../api-server/scripts/sqlite_test_config.py | 8 -- packages/api-server/scripts/test.py | 4 +- .../{base_test_config.py => test_config.py} | 4 +- .../src/components/tasks/tasks-app.tsx | 4 +- .../lib/tasks/task-table-datagrid.tsx | 4 +- 25 files changed, 301 insertions(+), 308 deletions(-) delete mode 100644 packages/api-server/api_server/test/test_utils.py delete mode 100644 packages/api-server/scripts/sqlite_test_config.py rename packages/api-server/scripts/{base_test_config.py => test_config.py} (60%) diff --git a/packages/api-server/.pylintrc b/packages/api-server/.pylintrc index 8a1354566..dff6626a9 100644 --- a/packages/api-server/.pylintrc +++ b/packages/api-server/.pylintrc @@ -152,6 +152,7 @@ disable=print-statement, logging-fstring-interpolation, line-too-long, too-many-lines, + no-self-use, # fastapi heavily uses singletons, disallowing global statements just ends up with things # using nonlocal as singletons which are functionally the same as globals. diff --git a/packages/api-server/README.md b/packages/api-server/README.md index 405ef6aa3..3c2548a6c 100644 --- a/packages/api-server/README.md +++ b/packages/api-server/README.md @@ -252,18 +252,24 @@ Restart the `api-server` and the changes to the databse should be reflected. ### Running unit tests ```bash -npm test +pnpm test +``` + +By default in-memory sqlite database is used for testing, to test on another database, set the `RMF_API_SERVER_TEST_DB_URL` environment variable. + +```bash +RMF_API_SERVER_TEST_DB_URL= pnpm test ``` ### Collecting code coverage ```bash -npm run test:cov +pnpm run test:cov ``` Generate coverage report ```bash -npm run test:report +pnpm run test:report ``` ## Live reload diff --git a/packages/api-server/api_server/dependencies.py b/packages/api-server/api_server/dependencies.py index 199c86061..8706a42e7 100644 --- a/packages/api-server/api_server/dependencies.py +++ b/packages/api-server/api_server/dependencies.py @@ -20,7 +20,11 @@ def pagination_query( ) -> Pagination: limit = limit or 100 offset = offset or 0 - return Pagination(limit=limit, offset=offset, order_by=order_by) + return Pagination( + limit=limit, + offset=offset, + order_by=order_by.split(",") if order_by else [], + ) # hacky way to get the sio user diff --git a/packages/api-server/api_server/models/pagination.py b/packages/api-server/api_server/models/pagination.py index b88a78b4c..9832a379d 100644 --- a/packages/api-server/api_server/models/pagination.py +++ b/packages/api-server/api_server/models/pagination.py @@ -1,9 +1,7 @@ -from typing import Optional - from pydantic import BaseModel class Pagination(BaseModel): limit: int offset: int - order_by: Optional[str] + order_by: list[str] diff --git a/packages/api-server/api_server/query.py b/packages/api-server/api_server/query.py index 35dcb62c1..c0ea9db33 100644 --- a/packages/api-server/api_server/query.py +++ b/packages/api-server/api_server/query.py @@ -1,7 +1,3 @@ -from typing import Dict, Optional - -import tortoise.functions as tfuncs -from tortoise.expressions import Q from tortoise.queryset import MODEL, QuerySet from api_server.models.pagination import Pagination @@ -10,47 +6,10 @@ def add_pagination( query: QuerySet[MODEL], pagination: Pagination, - field_mappings: Optional[Dict[str, str]] = None, - group_by: str | None = None, ) -> QuerySet[MODEL]: - """ - Adds pagination and ordering to a query. If the order field starts with `label=`, it is - assumed to be a label and label sorting will used. In this case, the model must have - a reverse relation named "labels" and the `group_by` param is required. - - :param field_mapping: A dict mapping the order fields to the fields used to build the - query. e.g. a url of `?order_by=order_field` and a field mapping of `{"order_field": "db_field"}` - will order the query result according to `db_field`. - :param group_by: Required when sorting by labels, must be the foreign key column of the label table. - """ - field_mappings = field_mappings or {} - annotations = {} - query = query.limit(pagination.limit).offset(pagination.offset) - if pagination.order_by is not None: - order_fields = [] - order_values = pagination.order_by.split(",") - for v in order_values: - # perform the mapping after stripping the order prefix - order_prefix = "" - order_field = v - if v[0] in ["-", "+"]: - order_prefix = v[0] - order_field = v[1:] - order_field = field_mappings.get(order_field, order_field) - - # add annotations required for sorting by labels - if order_field.startswith("label="): - f = order_field[6:] - annotations[f"label_sort_{f}"] = tfuncs.Max( - "labels__label_value_str", - _filter=Q(labels__label_name=f), - ) - order_field = f"label_sort_{f}" - - order_fields.append(order_prefix + order_field) - - query = query.annotate(**annotations) - if group_by is not None: - query = query.group_by(group_by) - query = query.order_by(*order_fields) - return query + """Adds pagination and ordering to a query""" + return ( + query.limit(pagination.limit) + .offset(pagination.offset) + .order_by(*pagination.order_by) + ) diff --git a/packages/api-server/api_server/repositories/tasks.py b/packages/api-server/api_server/repositories/tasks.py index 5fb1ee963..f47b33453 100644 --- a/packages/api-server/api_server/repositories/tasks.py +++ b/packages/api-server/api_server/repositories/tasks.py @@ -2,18 +2,18 @@ from datetime import datetime from typing import Dict, List, Optional, Sequence, Tuple, cast +import tortoise.functions as tfuncs from fastapi import Depends, HTTPException from tortoise.exceptions import FieldError, IntegrityError +from tortoise.expressions import Expression, Q from tortoise.query_utils import Prefetch -from tortoise.queryset import QuerySet from tortoise.transactions import in_transaction from api_server.authenticator import user_dep from api_server.logging import LoggerAdapter, get_logger +from api_server.models import Labels, LogEntry, Pagination, Phases +from api_server.models import Status as TaskStatus from api_server.models import ( - LogEntry, - Pagination, - Phases, TaskBookingLabel, TaskEventLog, TaskRequest, @@ -25,7 +25,6 @@ from api_server.models.rmf_api.task_state import Category, Id, Phase from api_server.models.tortoise_models import TaskRequest as DbTaskRequest from api_server.models.tortoise_models import TaskState as DbTaskState -from api_server.query import add_pagination from api_server.rmf_io import task_events @@ -140,17 +139,92 @@ async def save_task_state(self, task_state: TaskState) -> None: ) async def query_task_states( - self, query: QuerySet[DbTaskState], pagination: Optional[Pagination] = None + self, + task_id: list[str] | None = None, + category: list[str] | None = None, + assigned_to: list[str] | None = None, + start_time_between: tuple[datetime, datetime] | None = None, + finish_time_between: tuple[datetime, datetime] | None = None, + request_time_between: tuple[datetime, datetime] | None = None, + requester: list[str] | None = None, + status: list[str] | None = None, + label: Labels | None = None, + pagination: Optional[Pagination] = None, ) -> List[TaskState]: + filters = {} + if task_id is not None: + filters["id___in"] = task_id + if category is not None: + filters["category__in"] = category + if assigned_to is not None: + filters["assigned_to__in"] = assigned_to + if start_time_between is not None: + filters["unix_millis_start_time__gte"] = start_time_between[0] + filters["unix_millis_start_time__lte"] = start_time_between[1] + if finish_time_between is not None: + filters["unix_millis_finish_time__gte"] = finish_time_between[0] + filters["unix_millis_finish_time__lte"] = finish_time_between[1] + if request_time_between is not None: + filters["unix_millis_request_time__gte"] = request_time_between[0] + filters["unix_millis_request_time__lte"] = request_time_between[1] + if requester is not None: + filters["requester__in"] = requester + if status is not None: + valid_values = [member.value for member in TaskStatus] + filters["status__in"] = [] + for status_string in status: + if status_string not in valid_values: + continue + filters["status__in"].append(TaskStatus(status_string)) + query = DbTaskState.filter(**filters) + + need_group_by = False + label_filters = {} + if label is not None: + label_filters.update( + { + f"label_filter_{k}": tfuncs.Count( + "id_", + _filter=Q(labels__label_name=k, labels__label_value_str=v), + ) + for k, v in label.__root__.items() + } + ) + + if len(label_filters) > 0: + filter_gt = {f"{f}__gt": 0 for f in label_filters} + query = query.annotate(**label_filters).filter(**filter_gt) + need_group_by = True + + if pagination: + order_fields: list[str] = [] + annotations: dict[str, Expression] = {} + # add annotations required for sorting by labels + for f in pagination.order_by: + order_prefix = f[0] if f[0] == "-" else "" + order_field = f[1:] if order_prefix == "-" else f + if order_field.startswith("label="): + f = order_field[6:] + annotations[f"label_sort_{f}"] = tfuncs.Max( + "labels__label_value_str", + _filter=Q(labels__label_name=f), + ) + order_field = f"label_sort_{f}" + + order_fields.append(order_prefix + order_field) + + query = ( + query.annotate(**annotations) + .limit(pagination.limit) + .offset(pagination.offset) + .order_by(*order_fields) + ) + need_group_by = True + + if need_group_by: + query = query.group_by("id_", "labels__state_id") + try: - if pagination: - query = add_pagination( - query, - pagination, - # TODO(koonpeng): remove this mapping after `pickup` and `destination` query is removed. - {"pickup": "label=pickup", "destination": "label=destination"}, - group_by="labels__state_id", - ) # TODO: enforce with authz results = await query.values_list("data", flat=True) return [TaskState(**r) for r in results] diff --git a/packages/api-server/api_server/routes/tasks/scheduled_tasks.py b/packages/api-server/api_server/routes/tasks/scheduled_tasks.py index a0e5a455f..58fed9199 100644 --- a/packages/api-server/api_server/routes/tasks/scheduled_tasks.py +++ b/packages/api-server/api_server/routes/tasks/scheduled_tasks.py @@ -182,7 +182,7 @@ async def get_scheduled_tasks( .offset(pagination.offset) ) if pagination.order_by: - q.order_by(*pagination.order_by.split(",")) + q.order_by(*pagination.order_by) return await ttm.ScheduledTaskPydanticList.from_queryset(q) diff --git a/packages/api-server/api_server/routes/tasks/tasks.py b/packages/api-server/api_server/routes/tasks/tasks.py index b5b7671dd..89940ad4b 100644 --- a/packages/api-server/api_server/routes/tasks/tasks.py +++ b/packages/api-server/api_server/routes/tasks/tasks.py @@ -1,10 +1,8 @@ from datetime import datetime from typing import List, Optional, Tuple, cast -import tortoise.functions as tfuncs from fastapi import Body, Depends, HTTPException, Path, Query from rx import operators as rxops -from tortoise.expressions import Q from api_server import models as mdl from api_server.dependencies import ( @@ -17,7 +15,6 @@ ) from api_server.fast_io import FastIORouter, SubscriptionRequest from api_server.logging import LoggerAdapter, get_logger -from api_server.models.tortoise_models import TaskState as DbTaskState from api_server.repositories import RmfRepository, TaskRepository from api_server.response import RawJSONResponse from api_server.rmf_io import task_events, tasks_service @@ -102,12 +99,12 @@ async def query_task_states( ), pickup: Optional[str] = Query( None, - description="comma separated list of pickup names. [deprecated] use `label` instead", + description="pickup name. [deprecated] use `label` instead", deprecated=True, ), destination: Optional[str] = Query( None, - description="comma separated list of destination names, [deprecated] use `label` instead", + description="destination name, [deprecated] use `label` instead", deprecated=True, ), assigned_to: Optional[str] = Query( @@ -127,76 +124,26 @@ async def query_task_states( ), pagination: mdl.Pagination = Depends(pagination_query), ): - """ - Note that sorting by `pickup` and `destination` is mutually exclusive and sorting - by either of them will filter only tasks which has those labels. - """ - filters = {} - if task_id is not None: - filters["id___in"] = task_id.split(",") - if category is not None: - filters["category__in"] = category.split(",") - if request_time_between is not None: - filters["unix_millis_request_time__gte"] = request_time_between[0] - filters["unix_millis_request_time__lte"] = request_time_between[1] - if requester is not None: - filters["requester__in"] = requester.split(",") - if assigned_to is not None: - filters["assigned_to__in"] = assigned_to.split(",") - if start_time_between is not None: - filters["unix_millis_start_time__gte"] = start_time_between[0] - filters["unix_millis_start_time__lte"] = start_time_between[1] - if finish_time_between is not None: - filters["unix_millis_finish_time__gte"] = finish_time_between[0] - filters["unix_millis_finish_time__lte"] = finish_time_between[1] - if status is not None: - valid_values = [member.value for member in mdl.Status] - filters["status__in"] = [] - for status_string in status.split(","): - if status_string not in valid_values: - continue - filters["status__in"].append(mdl.Status(status_string)) - query = DbTaskState.filter(**filters) - - label_filters = {} - if pickup is not None: - label_filters["label_filter_pickup"] = tfuncs.Count( - "id_", - _filter=Q( - labels__label_name="pickup", - labels__label_value_str__in=pickup.split(","), - ), - ) - if destination is not None: - label_filters["label_filter_destination"] = tfuncs.Count( - "id_", - _filter=Q( - labels__label_name="destination", - labels__label_value_str__in=destination.split(","), - ), - ) - if label is not None: - labels = mdl.Labels.from_strings(label.split(",")) - label_filters.update( - { - f"label_filter_{k}": tfuncs.Count( - "id_", _filter=Q(labels__label_name=k, labels__label_value_str=v) - ) - for k, v in labels.__root__.items() - } - ) - - if len(label_filters) > 0: - filter_gt = {f"{f}__gt": 0 for f in label_filters} - query = ( - query.annotate(**label_filters) - .group_by( - "labels__state_id" - ) # need to group by a related field to make tortoise-orm generate joins - .filter(**filter_gt) - ) - - return await task_repo.query_task_states(query, pagination) + labels = ( + mdl.Labels.from_strings(label.split(",")) if label else mdl.Labels(__root__={}) + ) + if pickup: + labels.__root__["pickup"] = pickup + if destination: + labels.__root__["destination"] = destination + + return await task_repo.query_task_states( + task_id=task_id.split(",") if task_id else None, + category=category.split(",") if category else None, + assigned_to=assigned_to.split(",") if assigned_to else None, + start_time_between=start_time_between, + finish_time_between=finish_time_between, + request_time_between=request_time_between, + requester=requester.split(",") if requester else None, + status=status.split(",") if status else None, + label=labels, + pagination=pagination, + ) @router.get("/{task_id}/state", response_model=mdl.TaskState) diff --git a/packages/api-server/api_server/routes/tasks/test_tasks.py b/packages/api-server/api_server/routes/tasks/test_tasks.py index 5c3c7f673..ed7385c7a 100644 --- a/packages/api-server/api_server/routes/tasks/test_tasks.py +++ b/packages/api-server/api_server/routes/tasks/test_tasks.py @@ -1,10 +1,13 @@ +from typing import cast from unittest.mock import patch from uuid import uuid4 import pydantic from api_server import models as mdl -from api_server.rmf_io import tasks_service +from api_server.models import TaskEventLog, TaskState +from api_server.repositories import TaskRepository +from api_server.rmf_io import task_events, tasks_service from api_server.test import ( AppFixture, make_task_booking_label, @@ -45,15 +48,12 @@ def setUpClass(cls): ] cls.task_logs = [make_task_log(task_id=f"test_{x}") for x in task_ids] - with cls.client.websocket_connect("/_internal") as ws: - for x in cls.task_states: - ws.send_text( - mdl.TaskStateUpdate(type="task_state_update", data=x).json() - ) - for x in cls.task_logs: - ws.send_text( - mdl.TaskEventLogUpdate(type="task_log_update", data=x).json() - ) + portal = cls.get_portal() + repo = TaskRepository(cls.admin_user) + for x in cls.task_states: + portal.call(repo.save_task_state, x) + for x in cls.task_logs: + portal.call(repo.save_task_log, x) def test_get_task_state(self): resp = self.client.get(f"/tasks/{self.task_states[0].booking.id}/state") @@ -95,7 +95,7 @@ def test_query_task_states(self): def test_query_task_states_filter_by_label(self): resp = self.client.get("/tasks?label=not_existing") - self.assertEqual(200, resp.status_code) + self.assertEqual(200, resp.status_code, resp.content) results = pydantic.parse_raw_as(list[mdl.TaskState], resp.content) self.assertEqual(0, len(results)) @@ -173,15 +173,10 @@ def test_query_task_states_sort_by_label(self): def test_sub_task_state(self): task_id = self.task_states[0].booking.id - gen = self.subscribe_sio(f"/tasks/{task_id}/state") - with self.client.websocket_connect("/_internal") as ws: - ws.send_text( - mdl.TaskStateUpdate( - type="task_state_update", data=self.task_states[0] - ).json() - ) - state = next(gen) - self.assertEqual(task_id, state.booking.id) # type: ignore + with self.subscribe_sio(f"/tasks/{task_id}/state") as sub: + task_events.task_states.on_next(self.task_states[0]) + state = TaskState(**next(sub)) + self.assertEqual(task_id, cast(TaskState, state).booking.id) def test_get_task_booking_label(self): resp = self.client.get(f"/tasks/{self.task_states[0].booking.id}/booking_label") @@ -264,15 +259,10 @@ def test_get_task_log(self): def test_sub_task_log(self): task_id = self.task_logs[0].task_id - gen = self.subscribe_sio(f"/tasks/{task_id}/log") - with self.client.websocket_connect("/_internal") as ws: - ws.send_text( - mdl.TaskEventLogUpdate( - type="task_log_update", data=self.task_logs[0] - ).json() - ) - log = next(gen) - self.assertEqual(task_id, log.task_id) # type: ignore + with self.subscribe_sio(f"/tasks/{task_id}/log") as sub: + task_events.task_event_logs.on_next(self.task_logs[0]) + log = TaskEventLog(**next(sub)) + self.assertEqual(task_id, cast(TaskEventLog, log).task_id) def test_activity_discovery(self): with patch.object(tasks_service(), "call") as mock: diff --git a/packages/api-server/api_server/routes/test_building_map.py b/packages/api-server/api_server/routes/test_building_map.py index e73a65495..dbc0e75e2 100644 --- a/packages/api-server/api_server/routes/test_building_map.py +++ b/packages/api-server/api_server/routes/test_building_map.py @@ -10,7 +10,8 @@ class TestBuildingMapRoute(AppFixture): def test_get_building_map(self): building_map = make_building_map() - rmf_events.building_map.on_next(building_map) + portal = self.get_portal() + portal.call(building_map.save) resp = try_until( lambda: self.client.get("/building_map"), lambda x: x.status_code == 200 diff --git a/packages/api-server/api_server/routes/test_dispensers.py b/packages/api-server/api_server/routes/test_dispensers.py index 7d453593d..0e135b17b 100644 --- a/packages/api-server/api_server/routes/test_dispensers.py +++ b/packages/api-server/api_server/routes/test_dispensers.py @@ -1,7 +1,7 @@ from typing import List from uuid import uuid4 -from api_server.rmf_io import rmf_events +from api_server.models import DispenserState from api_server.test import AppFixture, make_dispenser_state @@ -11,8 +11,9 @@ def setUpClass(cls): super().setUpClass() cls.dispenser_states = [make_dispenser_state(f"test_{uuid4()}")] + portal = cls.get_portal() for x in cls.dispenser_states: - rmf_events.dispenser_states.on_next(x) + portal.call(x.save) def test_get_dispensers(self): resp = self.client.get("/dispensers") @@ -31,7 +32,8 @@ def test_get_dispenser_state(self): self.assertEqual(self.dispenser_states[0].guid, state["guid"]) def test_sub_dispenser_state(self): - msg = next( - self.subscribe_sio(f"/dispensers/{self.dispenser_states[0].guid}/state") - ) - self.assertEqual(self.dispenser_states[0].guid, msg.guid) # type: ignore + with self.subscribe_sio( + f"/dispensers/{self.dispenser_states[0].guid}/state" + ) as sub: + msg = DispenserState(**next(sub)) + self.assertEqual(self.dispenser_states[0].guid, msg.guid) diff --git a/packages/api-server/api_server/routes/test_doors.py b/packages/api-server/api_server/routes/test_doors.py index 0bae2681a..ec4a29f28 100644 --- a/packages/api-server/api_server/routes/test_doors.py +++ b/packages/api-server/api_server/routes/test_doors.py @@ -2,7 +2,7 @@ from rmf_door_msgs.msg import DoorMode as RmfDoorMode -from api_server.rmf_io import rmf_events +from api_server.models import DoorState from api_server.test import AppFixture, make_building_map, make_door_state @@ -11,11 +11,13 @@ class TestDoorsRoute(AppFixture): def setUpClass(cls): super().setUpClass() cls.building_map = make_building_map() + portal = cls.get_portal() + portal.call(cls.building_map.save) + cls.door_states = [make_door_state(f"test_{uuid4()}")] - rmf_events.building_map.on_next(cls.building_map) for x in cls.door_states: - rmf_events.door_states.on_next(x) + portal.call(x.save) def test_get_doors(self): resp = self.client.get("/doors") @@ -31,8 +33,9 @@ def test_get_door_state(self): self.assertEqual(self.door_states[0].door_name, state["door_name"]) def test_sub_door_state(self): - msg = next(self.subscribe_sio(f"/doors/{self.door_states[0].door_name}/state")) - self.assertEqual(self.door_states[0].door_name, msg.door_name) # type: ignore + with self.subscribe_sio(f"/doors/{self.door_states[0].door_name}/state") as sub: + msg = DoorState(**next(sub)) + self.assertEqual(self.door_states[0].door_name, msg.door_name) def test_post_door_request(self): resp = self.client.post( diff --git a/packages/api-server/api_server/routes/test_fleets.py b/packages/api-server/api_server/routes/test_fleets.py index 4ed0c77f4..3b55b640d 100644 --- a/packages/api-server/api_server/routes/test_fleets.py +++ b/packages/api-server/api_server/routes/test_fleets.py @@ -1,6 +1,13 @@ from urllib.parse import urlencode -from api_server.models import FleetLogUpdate, FleetStateUpdate, MutexGroups +from api_server.models import ( + FleetLog, + FleetLogUpdate, + FleetState, + FleetStateUpdate, + MutexGroups, +) +from api_server.rmf_io import fleet_events from api_server.test import ( AppFixture, make_fleet_log, @@ -11,45 +18,48 @@ class TestFleetsRoute(AppFixture): def test_fleet_states(self): - # subscribe to fleet states fleet_state = make_fleet_state("test_fleet") - gen = self.subscribe_sio(f"/fleets/{fleet_state.name}/state") - with self.client.websocket_connect("/_internal") as ws: + with self.client.websocket_connect("/_internal") as ws, self.subscribe_sio( + f"/fleets/{fleet_state.name}/state" + ) as sub: ws.send_text( FleetStateUpdate(type="fleet_state_update", data=fleet_state).json() ) - msg = next(gen) - self.assertEqual(fleet_state.name, msg.name) # type: ignore + msg = FleetState(**next(sub)) + self.assertEqual(fleet_state.name, msg.name) - # get fleet state - resp = self.client.get(f"/fleets/{fleet_state.name}/state") - self.assertEqual(200, resp.status_code) - state = resp.json() - self.assertEqual(fleet_state.name, state["name"]) + # get fleet state + resp = self.client.get(f"/fleets/{fleet_state.name}/state") + self.assertEqual(200, resp.status_code) + state = resp.json() + self.assertEqual(fleet_state.name, state["name"]) - # query fleets - resp = self.client.get(f"/fleets?fleet_name={fleet_state.name}") - self.assertEqual(200, resp.status_code) - resp_json = resp.json() - self.assertEqual(1, len(resp_json)) - self.assertEqual(fleet_state.name, resp_json[0]["name"]) + # query fleets + resp = self.client.get(f"/fleets?fleet_name={fleet_state.name}") + self.assertEqual(200, resp.status_code) + resp_json = resp.json() + self.assertEqual(1, len(resp_json), resp_json) + self.assertEqual(fleet_state.name, resp_json[0]["name"]) def test_fleet_logs(self): fleet_log = make_fleet_log() - gen = self.subscribe_sio(f"/fleets/{fleet_log.name}/log") - with self.client.websocket_connect("/_internal") as ws: + with self.client.websocket_connect("/_internal") as ws, self.subscribe_sio( + f"/fleets/{fleet_log.name}/log" + ) as sub: + fleet_events.fleet_logs.on_next(fleet_log) + ws.send_text(FleetLogUpdate(type="fleet_log_update", data=fleet_log).json()) - msg = next(gen) - self.assertEqual(fleet_log.name, msg.name) # type: ignore + msg = FleetLog(**next(sub)) + self.assertEqual(fleet_log.name, msg.name) - # Since there are no sample fleet logs, we cannot check the log contents - resp = self.client.get(f"/fleets/{fleet_log.name}/log") - self.assertEqual(200, resp.status_code) - self.assertEqual(fleet_log.name, resp.json()["name"]) + # Since there are no sample fleet logs, we cannot check the log contents + resp = self.client.get(f"/fleets/{fleet_log.name}/log") + self.assertEqual(200, resp.status_code) + self.assertEqual(fleet_log.name, resp.json()["name"]) def test_decommission_robot(self): # add a new robot diff --git a/packages/api-server/api_server/routes/test_ingestors.py b/packages/api-server/api_server/routes/test_ingestors.py index ecf3c90d5..810180508 100644 --- a/packages/api-server/api_server/routes/test_ingestors.py +++ b/packages/api-server/api_server/routes/test_ingestors.py @@ -1,7 +1,7 @@ from typing import List from uuid import uuid4 -from api_server.rmf_io import rmf_events +from api_server.models import IngestorState from api_server.test import AppFixture, make_ingestor_state @@ -11,8 +11,9 @@ def setUpClass(cls): super().setUpClass() cls.ingestor_states = [make_ingestor_state(f"test_{uuid4()}")] + portal = cls.get_portal() for x in cls.ingestor_states: - rmf_events.ingestor_states.on_next(x) + portal.call(x.save) def test_get_ingestors(self): resp = self.client.get("/ingestors") @@ -31,7 +32,8 @@ def test_get_ingestor_state(self): self.assertEqual(self.ingestor_states[0].guid, state["guid"]) def test_sub_ingestor_state(self): - msg = next( - self.subscribe_sio(f"/ingestors/{self.ingestor_states[0].guid}/state") - ) - self.assertEqual(self.ingestor_states[0].guid, msg.guid) # type: ignore + with self.subscribe_sio( + f"/ingestors/{self.ingestor_states[0].guid}/state" + ) as sub: + msg = IngestorState(**next(sub)) + self.assertEqual(self.ingestor_states[0].guid, msg.guid) diff --git a/packages/api-server/api_server/routes/test_lifts.py b/packages/api-server/api_server/routes/test_lifts.py index 2d11a2c10..4473dc03a 100644 --- a/packages/api-server/api_server/routes/test_lifts.py +++ b/packages/api-server/api_server/routes/test_lifts.py @@ -2,7 +2,7 @@ from rmf_lift_msgs.msg import LiftRequest as RmfLiftRequest -from api_server.rmf_io import rmf_events +from api_server.models import LiftState from api_server.test import AppFixture, make_building_map, make_lift_state @@ -11,11 +11,12 @@ class TestLiftsRoute(AppFixture): def setUpClass(cls): super().setUpClass() cls.building_map = make_building_map() - cls.lift_states = [make_lift_state(f"test_{uuid4()}")] + portal = cls.get_portal() + portal.call(cls.building_map.save) - rmf_events.building_map.on_next(cls.building_map) + cls.lift_states = [make_lift_state(f"test_{uuid4()}")] for x in cls.lift_states: - rmf_events.lift_states.on_next(x) + portal.call(x.save) def test_get_lifts(self): resp = self.client.get("/lifts") @@ -31,8 +32,9 @@ def test_get_lift_state(self): self.assertEqual(self.lift_states[0].lift_name, state["lift_name"]) def test_sub_lift_state(self): - msg = next(self.subscribe_sio(f"/lifts/{self.lift_states[0].lift_name}/state")) - self.assertEqual(self.lift_states[0].lift_name, msg.lift_name) # type: ignore + with self.subscribe_sio(f"/lifts/{self.lift_states[0].lift_name}/state") as sub: + msg = LiftState(**next(sub)) + self.assertEqual(self.lift_states[0].lift_name, msg.lift_name) def test_request_lift(self): resp = self.client.post( diff --git a/packages/api-server/api_server/test/__init__.py b/packages/api-server/api_server/test/__init__.py index 0cfa7abe6..10fa57f6c 100644 --- a/packages/api-server/api_server/test/__init__.py +++ b/packages/api-server/api_server/test/__init__.py @@ -4,6 +4,5 @@ from .mocks import * from .test_data import * from .test_fixtures import * -from .test_utils import * test_user = User(username="test_user", is_admin=True) diff --git a/packages/api-server/api_server/test/test_fixtures.py b/packages/api-server/api_server/test/test_fixtures.py index 261bdb923..c63c9695d 100644 --- a/packages/api-server/api_server/test/test_fixtures.py +++ b/packages/api-server/api_server/test/test_fixtures.py @@ -1,14 +1,19 @@ import asyncio +import contextlib import inspect import os import os.path import time import unittest import unittest.mock -from typing import Awaitable, Callable, Optional, TypeVar, Union +from typing import Awaitable, Callable, Generator, Optional, TypeVar, Union from uuid import uuid4 -from api_server.app import app, on_sio_connect +import pydantic +from anyio.abc import BlockingPortal +from tortoise import Tortoise + +from api_server.app import app, app_config from api_server.models import User from .mocks import patch_sio @@ -80,20 +85,62 @@ async def async_try_until( class AppFixture(unittest.TestCase): @classmethod def setUpClass(cls): + async def clean_db(): + # connect to the db to drop it + await Tortoise.init(db_url=app_config.db_url, modules={"models": []}) + await Tortoise._drop_databases() # pylint: disable=protected-access + # connect to it again to recreate it + await Tortoise.init( + db_url=app_config.db_url, modules={"models": []}, _create_db=True + ) + await Tortoise.close_connections() + + asyncio.run(clean_db()) + cls.admin_user = User(username="admin", is_admin=True) cls.client = TestClient() cls.client.headers["Content-Type"] = "application/json" cls.client.__enter__() cls.addClassCleanup(cls.client.__exit__) + @classmethod + def get_portal(cls) -> BlockingPortal: + if not cls.client.portal: + raise AssertionError( + "missing client portal, is the client context entered?" + ) + return cls.client.portal + + @contextlib.contextmanager def subscribe_sio(self, room: str, *, user="admin"): """ Subscribes to a socketio room and return a generator of messages Returns a tuple of (success: bool, messages: Any). """ - - def impl(): - with patch_sio() as mock_sio: + if self.client.portal is None: + raise AssertionError( + "self.client.portal is None, make sure this is called within a test context" + ) + portal = self.client.portal + + on_sio_connect = app.sio.handlers["/"]["connect"] + on_subscribe = app.sio.handlers["/"]["subscribe"] + on_disconnect = app.sio.handlers["/"]["disconnect"] + + def gen() -> Generator[dict, None, None]: + async def wait_for_msgs(): + async with condition: + if len(msgs) == 0: + await condition.wait() + return msgs.pop(0) + + while True: + # TODO: type check is ignored because pyright is outdated + yield portal.call(asyncio.wait_for, wait_for_msgs(), 5) # type: ignore + + with patch_sio() as mock_sio: + connected = False + try: msgs = [] condition = asyncio.Condition() @@ -102,39 +149,23 @@ async def handle_resp(emit_room, msg, *_args, **_kwargs): raise Exception("Failed to subscribe") if emit_room == room: async with condition: + if isinstance(msg, pydantic.BaseModel): + msg = msg.dict() msgs.append(msg) condition.notify() mock_sio.emit.side_effect = handle_resp - self.assertIsNotNone(self.client.portal) - assert self.client.portal is not None - - self.client.portal.call( + portal.call( on_sio_connect, "test", {}, {"token": self.client.token(user)} ) - # pylint: disable=protected-access - self.client.portal.call(app._on_subscribe, "test", {"room": room}) - - yield - - async def wait_for_msgs(): - async with condition: - if len(msgs) == 0: - await condition.wait() - return msgs.pop(0) - - try: - while True: - yield self.client.portal.call( - asyncio.wait_for, wait_for_msgs(), 5 - ) - finally: - self.client.portal.call(app._on_disconnect, "test") - - gen = impl() - next(gen) - return gen + connected = True + portal.call(on_subscribe, "test", {"room": room}) + + yield gen() + finally: + if connected: + portal.call(on_disconnect, "test") def setUp(self): self.test_time = 0 diff --git a/packages/api-server/api_server/test/test_stress.py b/packages/api-server/api_server/test/test_stress.py index a18ad6ea2..5b1a51ee6 100644 --- a/packages/api-server/api_server/test/test_stress.py +++ b/packages/api-server/api_server/test/test_stress.py @@ -18,6 +18,5 @@ def test_stress(self): rmf_events.door_states.on_next(door_state) while True: - gen = self.subscribe_sio(f"/doors/{door_state.door_name}/state") - next(gen) - gen.close() + with self.subscribe_sio(f"/doors/{door_state.door_name}/state") as sub: + next(sub) diff --git a/packages/api-server/api_server/test/test_utils.py b/packages/api-server/api_server/test/test_utils.py deleted file mode 100644 index 71ae280f1..000000000 --- a/packages/api-server/api_server/test/test_utils.py +++ /dev/null @@ -1,12 +0,0 @@ -from typing import Optional, Sequence - -from tortoise import Tortoise - - -async def init_db(models: Optional[Sequence[str]] = None): - models = models or ["api_server.models.tortoise_models"] - await Tortoise.init( - db_url="sqlite://:memory:", - modules={"models": models}, - ) - await Tortoise.generate_schemas() diff --git a/packages/api-server/api_server/test_sio_auth.py b/packages/api-server/api_server/test_sio_auth.py index 0ddbda633..a47f4085a 100644 --- a/packages/api-server/api_server/test_sio_auth.py +++ b/packages/api-server/api_server/test_sio_auth.py @@ -1,35 +1,20 @@ import asyncio -from typing import Optional from unittest.mock import AsyncMock, patch from api_server.app import app, on_sio_connect +from api_server.authenticator import authenticator from .test.test_fixtures import AppFixture class TestSioAuth(AppFixture): - @staticmethod - def try_connect(token: Optional[str]): - with patch.object(app, "sio") as mock: + def test_token_is_verified(self): + with patch.object( + authenticator, "verify_token" + ) as mock_verify_token, patch.object(app, "sio") as mock_sio: # set up mocks session = {} - mock.get_session = AsyncMock(return_value=session) + mock_sio.get_session = AsyncMock(return_value=session) - loop = asyncio.new_event_loop() - fut = asyncio.Future(loop=loop) - - async def result(): - fut.set_result(await on_sio_connect("test", {}, {"token": token})) - - loop.run_until_complete(result()) - loop.close() - return fut.result() - - def test_fail_with_no_token(self): - self.assertFalse(self.try_connect(None)) - - def test_fail_with_invalid_token(self): - self.assertFalse(self.try_connect("invalid")) - - def test_success_with_valid_token(self): - self.assertTrue(self.try_connect(self.client.token("admin"))) + asyncio.run(on_sio_connect("test", {}, {"token": "test-token"})) + mock_verify_token.assert_awaited_once_with("test-token") diff --git a/packages/api-server/scripts/sqlite_test_config.py b/packages/api-server/scripts/sqlite_test_config.py deleted file mode 100644 index 2d15de704..000000000 --- a/packages/api-server/scripts/sqlite_test_config.py +++ /dev/null @@ -1,8 +0,0 @@ -from base_test_config import config - -config.update( - { - "db_url": "sqlite://:memory:", - "timezone": "Asia/Singapore", - } -) diff --git a/packages/api-server/scripts/test.py b/packages/api-server/scripts/test.py index e164368e6..77e8dc11f 100644 --- a/packages/api-server/scripts/test.py +++ b/packages/api-server/scripts/test.py @@ -1,8 +1,6 @@ import os -os.environ[ - "RMF_API_SERVER_CONFIG" -] = f"{os.path.dirname(__file__)}/sqlite_test_config.py" +os.environ["RMF_API_SERVER_CONFIG"] = f"{os.path.dirname(__file__)}/test_config.py" import unittest diff --git a/packages/api-server/scripts/base_test_config.py b/packages/api-server/scripts/test_config.py similarity index 60% rename from packages/api-server/scripts/base_test_config.py rename to packages/api-server/scripts/test_config.py index 35fce9a44..0e3ef18cb 100644 --- a/packages/api-server/scripts/base_test_config.py +++ b/packages/api-server/scripts/test_config.py @@ -4,7 +4,7 @@ here = os.path.dirname(__file__) -test_port = os.environ.get("RMF_SERVER_TEST_PORT", "8000") +test_port = os.environ.get("RMF_API_SERVER_TEST_PORT", "8000") config.update( { "host": "127.0.0.1", @@ -12,5 +12,7 @@ "log_level": "CRITICAL", "jwt_public_key": f"{here}/test.pub", "iss": "test", + "db_url": os.environ.get("RMF_API_SERVER_TEST_DB_URL", "sqlite://:memory:"), + "timezone": "Asia/Singapore", } ) diff --git a/packages/dashboard/src/components/tasks/tasks-app.tsx b/packages/dashboard/src/components/tasks/tasks-app.tsx index 3980d3e37..ae9812988 100644 --- a/packages/dashboard/src/components/tasks/tasks-app.tsx +++ b/packages/dashboard/src/components/tasks/tasks-app.tsx @@ -191,8 +191,8 @@ export const TasksApp = React.memo( filterColumn && filterColumn === 'id_' ? filterValue : undefined, filterColumn && filterColumn === 'category' ? filterValue : undefined, filterColumn && filterColumn === 'requester' ? filterValue : undefined, - filterColumn && filterColumn === 'pickup' ? filterValue : undefined, - filterColumn && filterColumn === 'destination' ? filterValue : undefined, + filterColumn && filterColumn === 'label=pickup' ? filterValue : undefined, + filterColumn && filterColumn === 'label=destination' ? filterValue : undefined, filterColumn && filterColumn === 'assigned_to' ? filterValue : undefined, filterColumn && filterColumn === 'status' ? filterValue : undefined, undefined, diff --git a/packages/react-components/lib/tasks/task-table-datagrid.tsx b/packages/react-components/lib/tasks/task-table-datagrid.tsx index 9b2ad8952..506975169 100644 --- a/packages/react-components/lib/tasks/task-table-datagrid.tsx +++ b/packages/react-components/lib/tasks/task-table-datagrid.tsx @@ -218,7 +218,7 @@ export function TaskDataGridTable({ filterable: true, }, { - field: 'pickup', + field: 'label=pickup', headerName: 'Pickup', width: 150, editable: false, @@ -233,7 +233,7 @@ export function TaskDataGridTable({ filterable: true, }, { - field: 'destination', + field: 'label=destination', headerName: 'Destination', width: 150, editable: false, From 105628baf994bb174e8d62c4a39f2dc267d73ce4 Mon Sep 17 00:00:00 2001 From: Aaron Chong Date: Wed, 26 Jun 2024 14:57:35 +0800 Subject: [PATCH 5/5] Hammer/demo tasks (#925) * Moved custom deliveries to separate file naively and import naively Signed-off-by: Aaron Chong * Moved patrol Signed-off-by: Aaron Chong * Moved custom-compose Signed-off-by: Aaron Chong * Added clean and delivery Signed-off-by: Aaron Chong * Added delivery, renamed to SimpleDelivery Signed-off-by: Aaron Chong * Clean task added Signed-off-by: Aaron Chong * Moved delivery-custom tests, added return type for forms Signed-off-by: Aaron Chong * Configurable supported tasks and name remapping Signed-off-by: Aaron Chong * Changed directory to types, since it doesn't just handle descriptions Signed-off-by: Aaron Chong * Fix test imports Signed-off-by: Aaron Chong * Using temporary task definition Signed-off-by: Aaron Chong * Refactoring new rename changes Signed-off-by: Aaron Chong * Clean up Signed-off-by: Aaron Chong * Removed problematic and unsused component and test Signed-off-by: Aaron Chong * Lint Signed-off-by: Aaron Chong * Updating pnpm version in github workflow Signed-off-by: Aaron Chong * Reverting update to pnpm version Signed-off-by: Aaron Chong * Fix build now that we use key value strings for labels Signed-off-by: Aaron Chong * Refactored last parts of hard coding categories and rendering forms Signed-off-by: Aaron Chong * Refactor callback names and error handling for misconfigs Signed-off-by: Aaron Chong * Display error as well Signed-off-by: Aaron Chong * Fixed more checks and failures Signed-off-by: Aaron Chong * Split configuration and definition, only handle configurations in resource manager level Signed-off-by: Aaron Chong * Lint Signed-off-by: Aaron Chong * Not using object as a type Signed-off-by: Aaron Chong * Address feedback Signed-off-by: Aaron Chong * Render using validTasks instead Signed-off-by: Aaron Chong * Use useMemo Signed-off-by: Aaron Chong --------- Signed-off-by: Aaron Chong --- packages/dashboard/src/components/appbar.tsx | 2 + .../components/tasks/task-schedule-utils.ts | 31 +- .../src/components/tasks/task-schedule.tsx | 8 +- .../src/managers/resource-manager-tasks.ts | 32 + .../src/managers/resource-manager.ts | 19 + packages/react-components/lib/index.ts | 1 - .../lib/simple-filter.spec.tsx | 16 - .../lib/simple-filter.stories.tsx | 22 - .../react-components/lib/simple-filter.tsx | 49 - .../lib/tasks/create-task.tsx | 1439 ++++------------- packages/react-components/lib/tasks/index.ts | 1 + .../lib/tasks/task-booking-label-utils.tsx | 22 +- .../lib/tasks/types/compose-clean.tsx | 155 ++ .../lib/tasks/types/custom-compose.tsx | 73 + .../delivery-custom.spec.tsx} | 56 +- .../lib/tasks/types/delivery-custom.tsx | 832 ++++++++++ .../lib/tasks/types/delivery.tsx | 261 +++ .../react-components/lib/tasks/types/index.ts | 6 + .../lib/tasks/types/patrol.tsx | 192 +++ .../react-components/lib/tasks/types/utils.ts | 132 ++ 20 files changed, 2049 insertions(+), 1300 deletions(-) create mode 100644 packages/dashboard/src/managers/resource-manager-tasks.ts delete mode 100644 packages/react-components/lib/simple-filter.spec.tsx delete mode 100644 packages/react-components/lib/simple-filter.stories.tsx delete mode 100644 packages/react-components/lib/simple-filter.tsx create mode 100644 packages/react-components/lib/tasks/types/compose-clean.tsx create mode 100644 packages/react-components/lib/tasks/types/custom-compose.tsx rename packages/react-components/lib/tasks/{create-task.spec.tsx => types/delivery-custom.spec.tsx} (88%) create mode 100644 packages/react-components/lib/tasks/types/delivery-custom.tsx create mode 100644 packages/react-components/lib/tasks/types/delivery.tsx create mode 100644 packages/react-components/lib/tasks/types/index.ts create mode 100644 packages/react-components/lib/tasks/types/patrol.tsx create mode 100644 packages/react-components/lib/tasks/types/utils.ts diff --git a/packages/dashboard/src/components/appbar.tsx b/packages/dashboard/src/components/appbar.tsx index 464be49a2..5ef0e56ad 100644 --- a/packages/dashboard/src/components/appbar.tsx +++ b/packages/dashboard/src/components/appbar.tsx @@ -164,6 +164,7 @@ export const AppBar = React.memo(({ extraToolbarItems }: AppBarProps): React.Rea const location = useLocation(); const tabValue = React.useMemo(() => locationToTabValue(location.pathname), [location]); const logoResourcesContext = React.useContext(ResourcesContext)?.logos; + const taskResourcesContext = React.useContext(ResourcesContext)?.tasks; const [anchorEl, setAnchorEl] = React.useState(null); const { authenticator } = React.useContext(AppConfigContext); const profile = React.useContext(UserProfileContext); @@ -617,6 +618,7 @@ export const AppBar = React.memo(({ extraToolbarItems }: AppBarProps): React.Rea {openCreateTaskForm && ( }; }; -export const getScheduledTaskTitle = (task: ScheduledTask): string => { - const shortDescription = getShortDescription(task.task_request); +export const getScheduledTaskTitle = ( + task: ScheduledTask, + supportedTasks?: TaskDefinition[], +): string => { + const taskBookingLabel = getTaskBookingLabelFromTaskRequest(task.task_request); + + let remappedTaskName: string | undefined = undefined; + if ( + supportedTasks && + taskBookingLabel && + taskBookingLabel.description.task_definition_id && + typeof taskBookingLabel.description.task_definition_id === 'string' + ) { + for (const s of supportedTasks) { + if (s.taskDefinitionId === taskBookingLabel.description.task_definition_id) { + remappedTaskName = s.taskDisplayName; + } + } + } + + const shortDescription = getShortDescription(task.task_request, remappedTaskName); if (!task.task_request || !task.task_request.category || !shortDescription) { return `[${task.id}] Unknown`; } diff --git a/packages/dashboard/src/components/tasks/task-schedule.tsx b/packages/dashboard/src/components/tasks/task-schedule.tsx index d8d789a68..9f0f1172e 100644 --- a/packages/dashboard/src/components/tasks/task-schedule.tsx +++ b/packages/dashboard/src/components/tasks/task-schedule.tsx @@ -23,7 +23,7 @@ import { } from 'react-components'; import { useCreateTaskFormData } from '../../hooks/useCreateTaskForm'; import useGetUsername from '../../hooks/useFetchUser'; -import { AppControllerContext } from '../app-contexts'; +import { AppControllerContext, ResourcesContext } from '../app-contexts'; import { UserProfileContext } from 'rmf-auth'; import { AppEvents } from '../app-events'; import { RmfAppContext } from '../rmf-app'; @@ -69,6 +69,7 @@ const disablingCellsWithoutEvents = ( export const TaskSchedule = () => { const rmf = React.useContext(RmfAppContext); + const taskResourcesContext = React.useContext(ResourcesContext)?.tasks; const { showAlert } = React.useContext(AppControllerContext); const profile = React.useContext(UserProfileContext); @@ -136,7 +137,7 @@ export const TaskSchedule = () => { return tasks.flatMap((t: ScheduledTask) => t.schedules.flatMap((s: ApiSchedule) => { const events = scheduleToEvents(params.start, params.end, s, t, getEventId, () => - getScheduledTaskTitle(t), + getScheduledTaskTitle(t, taskResourcesContext?.tasks), ); events.forEach((ev) => { eventsMap.current[Number(ev.event_id)] = t; @@ -146,7 +147,7 @@ export const TaskSchedule = () => { }), ); }, - [rmf], + [rmf, taskResourcesContext], ); const CustomCalendarEditor = ({ scheduler, value, onChange }: CustomCalendarEditorProps) => { @@ -319,6 +320,7 @@ export const TaskSchedule = () => { {openCreateTaskForm && ( { - const mockOnChange = jasmine.createSpy(); - render(); - - userEvent.paste( - screen.getByLabelText('text-input').childNodes[1].childNodes[0] as TargetElement, - 'new text', - ); - - expect(mockOnChange).toHaveBeenCalled(); -}); diff --git a/packages/react-components/lib/simple-filter.stories.tsx b/packages/react-components/lib/simple-filter.stories.tsx deleted file mode 100644 index f77f8da3d..000000000 --- a/packages/react-components/lib/simple-filter.stories.tsx +++ /dev/null @@ -1,22 +0,0 @@ -import { Meta, Story } from '@storybook/react'; -import React from 'react'; -import { OnFilterChangeEvent, SimpleFilter } from './simple-filter'; - -export default { - title: 'Simple Filter', - component: SimpleFilter, -} as Meta; - -function SimpleFilterHandler(): JSX.Element { - const [filter, setFilter] = React.useState(''); - - const onChange = (e: React.ChangeEvent) => { - setFilter(e.target.value); - }; - - return ; -} - -export const SimpleFilterStory: Story = (args) => { - return ; -}; diff --git a/packages/react-components/lib/simple-filter.tsx b/packages/react-components/lib/simple-filter.tsx deleted file mode 100644 index 972741019..000000000 --- a/packages/react-components/lib/simple-filter.tsx +++ /dev/null @@ -1,49 +0,0 @@ -import React from 'react'; -import { TextField, Divider, styled } from '@mui/material'; - -export interface OnFilterChangeEvent { - name?: string | undefined; - value: string; -} - -export interface SimpleFilterProps { - onChange?: (e: React.ChangeEvent) => void; - value: string; -} - -const classes = { - simpleFilter: 'simple-filter-root', - filterBar: 'simple-filter-filterbar', - divider: 'simple-filter-divider', -}; -const StyledDiv = styled('div')(({ theme }) => ({ - [`&.${classes.simpleFilter}`]: { - margin: '1rem', - borderColor: theme.palette.success.main, - }, - [`& .${classes.filterBar}`]: { - width: '100%', - }, - [`& .${classes.divider}`]: { - margin: '1.5rem 0', - }, -})); - -export const SimpleFilter = (props: SimpleFilterProps): JSX.Element => { - const { onChange, value } = props; - - return ( - - - - - ); -}; diff --git a/packages/react-components/lib/tasks/create-task.tsx b/packages/react-components/lib/tasks/create-task.tsx index 4635b2e76..c8031fc38 100644 --- a/packages/react-components/lib/tasks/create-task.tsx +++ b/packages/react-components/lib/tasks/create-task.tsx @@ -5,9 +5,7 @@ import UpdateIcon from '@mui/icons-material/Create'; import DeleteIcon from '@mui/icons-material/Delete'; -import PlaceOutlined from '@mui/icons-material/PlaceOutlined'; import { - Autocomplete, Box, Button, Checkbox, @@ -25,7 +23,6 @@ import { IconButton, List, ListItem, - ListItemIcon, ListItemSecondaryAction, ListItemText, MenuItem, @@ -42,284 +39,59 @@ import type { TaskBookingLabel, TaskFavorite, TaskRequest } from 'api-client'; import React from 'react'; import { Loading } from '..'; import { ConfirmationDialog, ConfirmationDialogProps } from '../confirmation-dialog'; -import { PositiveIntField } from '../form-inputs'; +import { + ComposeCleanTaskDescription, + ComposeCleanTaskForm, + ComposeCleanTaskDefinition, + makeComposeCleanTaskBookingLabel, +} from './types/compose-clean'; +import { + CustomComposeTaskDefinition, + CustomComposeTaskDescription, + CustomComposeTaskForm, + makeCustomComposeTaskBookingLabel, +} from './types/custom-compose'; +import { + DeliveryTaskDefinition, + DeliveryTaskDescription, + DeliveryTaskForm, + makeDeliveryTaskBookingLabel, +} from './types/delivery'; +import { + DeliveryCustomTaskForm, + DeliveryCustomTaskDescription, + DeliveryPickupTaskDescription, + DeliveryPickupTaskForm, + makeDeliveryCustomTaskBookingLabel, + makeDeliveryPickupTaskBookingLabel, + DeliveryPickupTaskDefinition, + DeliverySequentialLotPickupTaskDefinition, + DeliveryAreaPickupTaskDefinition, +} from './types/delivery-custom'; +import { + makePatrolTaskBookingLabel, + PatrolTaskDefinition, + PatrolTaskDescription, + PatrolTaskForm, +} from './types/patrol'; +import { getDefaultTaskDescription, getTaskRequestCategory } from './types/utils'; import { getTaskBookingLabelFromTaskRequest, serializeTaskBookingLabel, } from './task-booking-label-utils'; -interface TaskDefinition { - task_definition_id: string; - task_display_name: string; -} - -// If no task definition id is found in a past task (scheduled or favorite) -const DefaultTaskDefinitionId = 'custom_compose'; - -// FIXME: This is the order of the task type dropdown, and will be migrated out -// as a build-time configuration in a subsequent patch. -const SupportedTaskDefinitions: TaskDefinition[] = [ - { - task_definition_id: 'delivery_pickup', - task_display_name: 'Delivery - 1:1', - }, - { - task_definition_id: 'delivery_sequential_lot_pickup', - task_display_name: 'Delivery - Sequential lot pick up', - }, - { - task_definition_id: 'delivery_area_pickup', - task_display_name: 'Delivery - Area pick up', - }, - { - task_definition_id: 'patrol', - task_display_name: 'Patrol', - }, - { - task_definition_id: 'custom_compose', - task_display_name: 'Custom Compose Task', - }, -]; - -function makeDeliveryTaskBookingLabel(task_description: DeliveryTaskDescription): TaskBookingLabel { - const pickupDescription = - task_description.phases[0].activity.description.activities[1].description.description; - return { - description: { - task_definition_id: task_description.category, - pickup: pickupDescription.pickup_lot, - destination: task_description.phases[1].activity.description.activities[0].description, - cart_id: pickupDescription.cart_id, - }, - }; -} - -function makeDeliveryCustomTaskBookingLabel( - task_description: DeliveryCustomTaskDescription, -): TaskBookingLabel { - const pickupDescription = - task_description.phases[0].activity.description.activities[1].description.description; - return { - description: { - task_definition_id: task_description.category, - pickup: pickupDescription.pickup_zone, - destination: task_description.phases[1].activity.description.activities[0].description, - cart_id: pickupDescription.cart_id, - }, - }; -} - -function makePatrolTaskBookingLabel(task_description: PatrolTaskDescription): TaskBookingLabel { - return { - description: { - task_definition_id: 'patrol', - destination: task_description.places[task_description.places.length - 1], - }, - }; -} - -function makeCustomComposeTaskBookingLabel(): TaskBookingLabel { - return { - description: { - task_definition_id: 'custom_compose', - }, - }; -} - -// A bunch of manually defined descriptions to avoid using `any`. -export interface PatrolTaskDescription { - places: string[]; - rounds: number; -} - -interface LotPickupActivity { - category: string; - description: { - unix_millis_action_duration_estimate: number; - category: string; - description: { - cart_id: string; - pickup_lot: string; - }; - }; -} - -interface ZonePickupActivity { - category: string; - description: { - unix_millis_action_duration_estimate: number; - category: string; - description: { - cart_id: string; - pickup_zone: string; - }; - }; -} - -interface GoToPlaceActivity { - category: string; - description: string; -} - -interface CartCustomPickupPhase { - activity: { - category: string; - description: { - activities: [go_to_pickup: GoToPlaceActivity, pickup_cart: ZonePickupActivity]; - }; - }; -} - -interface CartPickupPhase { - activity: { - category: string; - description: { - activities: [go_to_pickup: GoToPlaceActivity, pickup_cart: LotPickupActivity]; - }; - }; -} - -interface DropoffActivity { - category: string; - description: { - unix_millis_action_duration_estimate: number; - category: string; - description: {}; - }; -} - -interface OneOfWaypoint { - waypoint: string; -} - -interface GoToOneOfThePlacesActivity { - category: string; - description: { - one_of: OneOfWaypoint[]; - constraints: [ - { - category: string; - description: string; - }, - ]; - }; -} - -interface OnCancelDropoff { - category: string; - description: [ - go_to_one_of_the_places: GoToOneOfThePlacesActivity, - delivery_dropoff: DropoffActivity, - ]; -} - -interface DeliveryWithCancellationPhase { - activity: { - category: string; - description: { - activities: [go_to_place: GoToPlaceActivity]; - }; - }; - on_cancel: OnCancelDropoff[]; -} - -interface CartDropoffPhase { - activity: { - category: string; - description: { - activities: [delivery_dropoff: DropoffActivity]; - }; - }; -} - -export interface DeliveryCustomTaskDescription { - category: string; - phases: [ - pickup_phase: CartCustomPickupPhase, - delivery_phase: DeliveryWithCancellationPhase, - dropoff_phase: CartDropoffPhase, - ]; -} - -export interface DeliveryTaskDescription { - category: string; - phases: [ - pickup_phase: CartPickupPhase, - delivery_phase: DeliveryWithCancellationPhase, - dropoff_phase: CartDropoffPhase, - ]; +export interface TaskDefinition { + taskDefinitionId: string; + taskDisplayName: string; + requestCategory: string; } -type CustomComposeTaskDescription = string; - -type TaskDescription = - | DeliveryTaskDescription +export type TaskDescription = + | DeliveryPickupTaskDescription | DeliveryCustomTaskDescription - | PatrolTaskDescription; - -const isNonEmptyString = (value: string): boolean => value.length > 0; - -const isDeliveryTaskDescriptionValid = ( - taskDescription: DeliveryTaskDescription, - pickupPoints: Record, - dropoffPoints: Record, -): boolean => { - const goToPickup = taskDescription.phases[0].activity.description.activities[0]; - const pickup = taskDescription.phases[0].activity.description.activities[1]; - const goToDropoff = taskDescription.phases[1].activity.description.activities[0]; - return ( - isNonEmptyString(goToPickup.description) && - Object.keys(pickupPoints).includes(goToPickup.description) && - pickupPoints[goToPickup.description] === pickup.description.description.pickup_lot && - isNonEmptyString(pickup.description.description.cart_id) && - isNonEmptyString(goToDropoff.description) && - Object.keys(dropoffPoints).includes(goToDropoff.description) - ); -}; - -const isDeliveryCustomTaskDescriptionValid = ( - taskDescription: DeliveryCustomTaskDescription, - pickupZones: string[], - dropoffPoints: string[], -): boolean => { - const goToPickup = taskDescription.phases[0].activity.description.activities[0]; - const pickup = taskDescription.phases[0].activity.description.activities[1]; - const goToDropoff = taskDescription.phases[1].activity.description.activities[0]; - return ( - isNonEmptyString(goToPickup.description) && - isNonEmptyString(pickup.description.description.pickup_zone) && - pickupZones.includes(pickup.description.description.pickup_zone) && - isNonEmptyString(pickup.description.description.cart_id) && - isNonEmptyString(goToDropoff.description) && - dropoffPoints.includes(goToDropoff.description) - ); -}; - -const isPatrolTaskDescriptionValid = (taskDescription: PatrolTaskDescription): boolean => { - if (taskDescription.places.length === 0) { - return false; - } - for (const place of taskDescription.places) { - if (place.length === 0) { - return false; - } - } - return taskDescription.rounds > 0; -}; - -const isCustomTaskDescriptionValid = (taskDescription: string): boolean => { - if (taskDescription.length === 0) { - return false; - } - - try { - JSON.parse(taskDescription); - } catch (e) { - return false; - } - - return true; -}; + | PatrolTaskDescription + | DeliveryTaskDescription + | ComposeCleanTaskDescription; const classes = { title: 'dialogue-info-value', @@ -349,646 +121,6 @@ const StyledDialog = styled((props: DialogProps) => )(({ th }, })); -export function getShortDescription(taskRequest: TaskRequest): string | undefined { - if (taskRequest.category === 'patrol') { - const formattedPlaces = taskRequest.description.places.map((place: string) => `[${place}]`); - return `[Patrol] [${taskRequest.description.rounds}] round/s, along ${formattedPlaces.join( - ', ', - )}`; - } - - // This section is only valid for custom delivery types - // FIXME: This block looks like it makes assumptions about the structure of - // the task description in order to parse it, but it is following the - // statically defined description (object) at the top of this file. The - // descriptions should be replaced by a schema in general, however the better - // approach now should be to make each task description testable and in charge - // of their own short descriptions. - try { - const goToPickup: GoToPlaceActivity = - taskRequest.description.phases[0].activity.description.activities[0]; - const pickup: LotPickupActivity = - taskRequest.description.phases[0].activity.description.activities[1]; - const cartId = pickup.description.description.cart_id; - const goToDropoff: GoToPlaceActivity = - taskRequest.description.phases[1].activity.description.activities[0]; - - switch (taskRequest.description.category) { - case 'delivery_pickup': { - return `[Delivery - 1:1] payload [${cartId}] from [${goToPickup.description}] to [${goToDropoff.description}]`; - } - case 'delivery_sequential_lot_pickup': { - return `[Delivery - Sequential lot pick up] payload [${cartId}] from [${goToPickup.description}] to [${goToDropoff.description}]`; - } - case 'delivery_area_pickup': { - return `[Delivery - Area pick up] payload [${cartId}] from [${goToPickup.description}] to [${goToDropoff.description}]`; - } - default: - return `[Unknown] type "${taskRequest.description.category}"`; - } - } catch (e) { - if (e instanceof TypeError) { - console.error(`Failed to parse custom delivery: ${e.message}`); - } else { - console.error( - `Failed to generate short description from task of category: ${taskRequest.category}: ${ - (e as Error).message - }`, - ); - } - - try { - const descriptionString = JSON.stringify(taskRequest.description); - console.error(descriptionString); - return descriptionString; - } catch (e) { - console.error( - `Failed to parse description of task of category: ${taskRequest.category}: ${ - (e as Error).message - }`, - ); - return undefined; - } - } -} - -export function deliveryInsertPickup( - taskDescription: DeliveryTaskDescription, - pickupPlace: string, - pickupLot: string, -): DeliveryTaskDescription { - taskDescription.phases[0].activity.description.activities[0].description = pickupPlace; - taskDescription.phases[0].activity.description.activities[1].description.description.pickup_lot = - pickupLot; - return taskDescription; -} - -export function deliveryInsertCartId( - taskDescription: DeliveryTaskDescription, - cartId: string, -): DeliveryTaskDescription { - taskDescription.phases[0].activity.description.activities[1].description.description.cart_id = - cartId; - return taskDescription; -} - -export function deliveryInsertDropoff( - taskDescription: DeliveryTaskDescription, - dropoffPlace: string, -): DeliveryTaskDescription { - taskDescription.phases[1].activity.description.activities[0].description = dropoffPlace; - return taskDescription; -} - -export function deliveryInsertOnCancel( - taskDescription: DeliveryTaskDescription, - onCancelPlaces: string[], -): DeliveryTaskDescription { - const goToOneOfThePlaces: GoToOneOfThePlacesActivity = { - category: 'go_to_place', - description: { - one_of: onCancelPlaces.map((placeName) => { - return { - waypoint: placeName, - }; - }), - constraints: [ - { - category: 'prefer_same_map', - description: '', - }, - ], - }, - }; - const deliveryDropoff: DropoffActivity = { - category: 'perform_action', - description: { - unix_millis_action_duration_estimate: 60000, - category: 'delivery_dropoff', - description: {}, - }, - }; - const onCancelDropoff: OnCancelDropoff = { - category: 'sequence', - description: [goToOneOfThePlaces, deliveryDropoff], - }; - taskDescription.phases[1].on_cancel = [onCancelDropoff]; - return taskDescription; -} - -interface DeliveryTaskFormProps { - taskDesc: DeliveryTaskDescription; - pickupPoints: Record; - cartIds: string[]; - dropoffPoints: Record; - onChange(taskDesc: TaskDescription): void; - allowSubmit(allow: boolean): void; -} - -function DeliveryTaskForm({ - taskDesc, - pickupPoints = {}, - cartIds = [], - dropoffPoints = {}, - onChange, - allowSubmit, -}: DeliveryTaskFormProps) { - const theme = useTheme(); - const isScreenHeightLessThan800 = useMediaQuery('(max-height:800px)'); - const onInputChange = (desc: DeliveryTaskDescription) => { - allowSubmit(isDeliveryTaskDescriptionValid(desc, pickupPoints, dropoffPoints)); - onChange(desc); - }; - - return ( - - - { - const pickupLot = pickupPoints[newValue] ?? ''; - let newTaskDesc = { ...taskDesc }; - newTaskDesc = deliveryInsertPickup(newTaskDesc, newValue, pickupLot); - onInputChange(newTaskDesc); - }} - onBlur={(ev) => { - const place = (ev.target as HTMLInputElement).value; - const pickupLot = pickupPoints[place] ?? ''; - let newTaskDesc = { ...taskDesc }; - newTaskDesc = deliveryInsertPickup(newTaskDesc, place, pickupLot); - onInputChange(newTaskDesc); - }} - sx={{ - '& .MuiOutlinedInput-root': { - height: isScreenHeightLessThan800 ? '3rem' : '3.5rem', - fontSize: isScreenHeightLessThan800 ? 14 : 20, - }, - }} - renderInput={(params) => ( - - )} - /> - - - option} - onInputChange={(_ev, newValue) => { - let newTaskDesc = { ...taskDesc }; - newTaskDesc = deliveryInsertCartId(newTaskDesc, newValue); - onInputChange(newTaskDesc); - }} - onBlur={(ev) => { - let newTaskDesc = { ...taskDesc }; - newTaskDesc = deliveryInsertCartId(newTaskDesc, (ev.target as HTMLInputElement).value); - onInputChange(newTaskDesc); - }} - sx={{ - '& .MuiOutlinedInput-root': { - height: isScreenHeightLessThan800 ? '3rem' : '3.5rem', - fontSize: isScreenHeightLessThan800 ? 14 : 20, - }, - }} - renderInput={(params) => ( - - )} - /> - - - { - let newTaskDesc = { ...taskDesc }; - newTaskDesc = deliveryInsertDropoff(newTaskDesc, newValue); - onInputChange(newTaskDesc); - }} - onBlur={(ev) => { - let newTaskDesc = { ...taskDesc }; - newTaskDesc = deliveryInsertDropoff(newTaskDesc, (ev.target as HTMLInputElement).value); - onInputChange(newTaskDesc); - }} - sx={{ - '& .MuiOutlinedInput-root': { - height: isScreenHeightLessThan800 ? '3rem' : '3.5rem', - fontSize: isScreenHeightLessThan800 ? 14 : 20, - }, - }} - renderInput={(params) => ( - - )} - /> - - - ); -} - -export function deliveryCustomInsertPickup( - taskDescription: DeliveryCustomTaskDescription, - pickupPlace: string, - pickupZone: string, -): DeliveryCustomTaskDescription { - taskDescription.phases[0].activity.description.activities[0].description = pickupPlace; - taskDescription.phases[0].activity.description.activities[1].description.description.pickup_zone = - pickupZone; - return taskDescription; -} - -export function deliveryCustomInsertCartId( - taskDescription: DeliveryCustomTaskDescription, - cartId: string, -): DeliveryCustomTaskDescription { - taskDescription.phases[0].activity.description.activities[1].description.description.cart_id = - cartId; - return taskDescription; -} - -export function deliveryCustomInsertDropoff( - taskDescription: DeliveryCustomTaskDescription, - dropoffPlace: string, -): DeliveryCustomTaskDescription { - taskDescription.phases[1].activity.description.activities[0].description = dropoffPlace; - return taskDescription; -} - -export function deliveryCustomInsertOnCancel( - taskDescription: DeliveryCustomTaskDescription, - onCancelPlaces: string[], -): DeliveryCustomTaskDescription { - const goToOneOfThePlaces: GoToOneOfThePlacesActivity = { - category: 'go_to_place', - description: { - one_of: onCancelPlaces.map((placeName) => { - return { - waypoint: placeName, - }; - }), - constraints: [ - { - category: 'prefer_same_map', - description: '', - }, - ], - }, - }; - const deliveryDropoff: DropoffActivity = { - category: 'perform_action', - description: { - unix_millis_action_duration_estimate: 60000, - category: 'delivery_dropoff', - description: {}, - }, - }; - const onCancelDropoff: OnCancelDropoff = { - category: 'sequence', - description: [goToOneOfThePlaces, deliveryDropoff], - }; - taskDescription.phases[1].on_cancel = [onCancelDropoff]; - return taskDescription; -} - -interface DeliveryCustomProps { - taskDesc: DeliveryCustomTaskDescription; - pickupZones: string[]; - cartIds: string[]; - dropoffPoints: string[]; - onChange(taskDesc: DeliveryCustomTaskDescription): void; - allowSubmit(allow: boolean): void; -} - -function DeliveryCustomTaskForm({ - taskDesc, - pickupZones = [], - cartIds = [], - dropoffPoints = [], - onChange, - allowSubmit, -}: DeliveryCustomProps) { - const theme = useTheme(); - const isScreenHeightLessThan800 = useMediaQuery('(max-height:800px)'); - const onInputChange = (desc: DeliveryCustomTaskDescription) => { - allowSubmit(isDeliveryCustomTaskDescriptionValid(desc, pickupZones, dropoffPoints)); - onChange(desc); - }; - - return ( - - - { - let newTaskDesc = { ...taskDesc }; - newTaskDesc = deliveryCustomInsertPickup(newTaskDesc, newValue, newValue); - onInputChange(newTaskDesc); - }} - onBlur={(ev) => { - const zone = (ev.target as HTMLInputElement).value; - let newTaskDesc = { ...taskDesc }; - newTaskDesc = deliveryCustomInsertPickup(newTaskDesc, zone, zone); - onInputChange(newTaskDesc); - }} - sx={{ - '& .MuiOutlinedInput-root': { - height: isScreenHeightLessThan800 ? '3rem' : '3.5rem', - fontSize: isScreenHeightLessThan800 ? 14 : 20, - }, - }} - renderInput={(params) => ( - - )} - /> - - - option} - onInputChange={(_ev, newValue) => { - let newTaskDesc = { ...taskDesc }; - newTaskDesc = deliveryCustomInsertCartId(newTaskDesc, newValue); - onInputChange(newTaskDesc); - }} - onBlur={(ev) => { - let newTaskDesc = { ...taskDesc }; - newTaskDesc = deliveryCustomInsertCartId( - newTaskDesc, - (ev.target as HTMLInputElement).value, - ); - onInputChange(newTaskDesc); - }} - sx={{ - '& .MuiOutlinedInput-root': { - height: isScreenHeightLessThan800 ? '3rem' : '3.5rem', - fontSize: isScreenHeightLessThan800 ? 14 : 20, - }, - }} - renderInput={(params) => ( - - )} - /> - - - { - let newTaskDesc = { ...taskDesc }; - newTaskDesc = deliveryCustomInsertDropoff(newTaskDesc, newValue); - onInputChange(newTaskDesc); - }} - onBlur={(ev) => { - let newTaskDesc = { ...taskDesc }; - newTaskDesc = deliveryCustomInsertDropoff( - newTaskDesc, - (ev.target as HTMLInputElement).value, - ); - onInputChange(newTaskDesc); - }} - sx={{ - '& .MuiOutlinedInput-root': { - height: isScreenHeightLessThan800 ? '3rem' : '3.5rem', - fontSize: isScreenHeightLessThan800 ? 14 : 20, - }, - }} - renderInput={(params) => ( - - )} - /> - - - ); -} - -interface PlaceListProps { - places: string[]; - onClick(places_index: number): void; -} - -function PlaceList({ places, onClick }: PlaceListProps) { - const theme = useTheme(); - return ( - - {places.map((value, index) => ( - onClick(index)}> - - - } - > - - - - - - ))} - - ); -} - -interface PatrolTaskFormProps { - taskDesc: PatrolTaskDescription; - patrolWaypoints: string[]; - onChange(patrolTaskDescription: PatrolTaskDescription): void; - allowSubmit(allow: boolean): void; -} - -function PatrolTaskForm({ taskDesc, patrolWaypoints, onChange, allowSubmit }: PatrolTaskFormProps) { - const theme = useTheme(); - const isScreenHeightLessThan800 = useMediaQuery('(max-height:800px)'); - const onInputChange = (desc: PatrolTaskDescription) => { - allowSubmit(isPatrolTaskDescriptionValid(desc)); - onChange(desc); - }; - allowSubmit(isPatrolTaskDescriptionValid(taskDesc)); - - return ( - - - - newValue !== null && - onInputChange({ - ...taskDesc, - places: taskDesc.places.concat(newValue).filter((el: string) => el), - }) - } - sx={{ - '& .MuiOutlinedInput-root': { - height: isScreenHeightLessThan800 ? '3rem' : '3.5rem', - fontSize: isScreenHeightLessThan800 ? 14 : 20, - }, - }} - renderInput={(params) => ( - - )} - /> - - - { - onInputChange({ - ...taskDesc, - rounds: val, - }); - }} - /> - - - - taskDesc.places.splice(places_index, 1) && - onInputChange({ - ...taskDesc, - }) - } - /> - - - ); -} - -interface CustomComposeTaskFormProps { - taskDesc: CustomComposeTaskDescription; - onChange(customComposeTaskDescription: CustomComposeTaskDescription): void; - allowSubmit(allow: boolean): void; -} - -function CustomComposeTaskForm({ taskDesc, onChange, allowSubmit }: CustomComposeTaskFormProps) { - const theme = useTheme(); - const onInputChange = (desc: CustomComposeTaskDescription) => { - allowSubmit(isCustomTaskDescriptionValid(desc)); - onChange(desc); - }; - - return ( - - - { - onInputChange(ev.target.value); - }} - /> - - - ); -} - interface FavoriteTaskProps { listItemText: string; listItemClick: () => void; @@ -1058,164 +190,31 @@ function FavoriteTask({ ); } -export function defaultDeliveryTaskDescription(): DeliveryTaskDescription { - return { - category: 'delivery_pickup', - phases: [ - { - activity: { - category: 'sequence', - description: { - activities: [ - { - category: 'go_to_place', - description: '', - }, - { - category: 'perform_action', - description: { - unix_millis_action_duration_estimate: 60000, - category: 'delivery_pickup', - description: { - cart_id: '', - pickup_lot: '', - }, - }, - }, - ], - }, - }, - }, - { - activity: { - category: 'sequence', - description: { - activities: [ - { - category: 'go_to_place', - description: '', - }, - ], - }, - }, - on_cancel: [], - }, - { - activity: { - category: 'sequence', - description: { - activities: [ - { - category: 'perform_action', - description: { - unix_millis_action_duration_estimate: 60000, - category: 'delivery_dropoff', - description: {}, - }, - }, - ], - }, - }, - }, - ], - }; -} +function getDefaultTaskRequest(taskDefinitionId: string): TaskRequest | null { + const category = getTaskRequestCategory(taskDefinitionId); + const description = getDefaultTaskDescription(taskDefinitionId); -export function defaultDeliveryCustomTaskDescription( - taskCategory: string, -): DeliveryCustomTaskDescription { - return { - category: taskCategory, - phases: [ - { - activity: { - category: 'sequence', - description: { - activities: [ - { - category: 'go_to_place', - description: '', - }, - { - category: 'perform_action', - description: { - unix_millis_action_duration_estimate: 60000, - category: taskCategory, - description: { - cart_id: '', - pickup_zone: '', - }, - }, - }, - ], - }, - }, - }, - { - activity: { - category: 'sequence', - description: { - activities: [ - { - category: 'go_to_place', - description: '', - }, - ], - }, - }, - on_cancel: [], - }, - { - activity: { - category: 'sequence', - description: { - activities: [ - { - category: 'perform_action', - description: { - unix_millis_action_duration_estimate: 60000, - category: 'delivery_dropoff', - description: {}, - }, - }, - ], - }, - }, - }, - ], - }; -} - -export function defaultPatrolTask(): PatrolTaskDescription { - return { - places: [], - rounds: 1, - }; -} + if (category === undefined) { + console.error(`Unable to retrieve task category for task definition of id ${taskDefinitionId}`); + } + if (description === undefined) { + console.error( + `Unable to retrieve task description for task definition of id ${taskDefinitionId}`, + ); + } -function defaultTaskDescription(taskCategory: string): TaskDescription | undefined { - switch (taskCategory) { - case 'delivery_pickup': - return defaultDeliveryTaskDescription(); - case 'delivery_sequential_lot_pickup': - case 'delivery_area_pickup': - return defaultDeliveryCustomTaskDescription(taskCategory); - case 'patrol': - return defaultPatrolTask(); - default: - return undefined; + if (category !== undefined && description !== undefined) { + return { + category, + description, + unix_millis_earliest_start_time: 0, + unix_millis_request_time: Date.now(), + priority: { type: 'binary', value: 0 }, + requester: '', + }; } -} -function defaultTask(): TaskRequest { - return { - category: 'compose', - description: defaultDeliveryTaskDescription(), - unix_millis_earliest_start_time: 0, - unix_millis_request_time: Date.now(), - priority: { type: 'binary', value: 0 }, - requester: '', - }; + return null; } export type RecurringDays = [boolean, boolean, boolean, boolean, boolean, boolean, boolean]; @@ -1281,25 +280,13 @@ const DaySelectorSwitch: React.VFC = ({ disabled, onChan ); }; -const defaultFavoriteTask = (): TaskFavorite => { - return { - id: '', - name: '', - category: 'compose', - description: defaultDeliveryTaskDescription(), - unix_millis_earliest_start_time: 0, - priority: { type: 'binary', value: 0 }, - user: '', - task_definition_id: DefaultTaskDefinitionId, - }; -}; - export interface CreateTaskFormProps extends Omit { /** * Shows extra UI elements suitable for submittng batched tasks. Default to 'false'. */ user: string; + tasksToDisplay?: TaskDefinition[]; allowBatch?: boolean; cleaningZones?: string[]; patrolWaypoints?: string[]; @@ -1325,6 +312,12 @@ export interface CreateTaskFormProps export function CreateTaskForm({ user, + tasksToDisplay = [ + PatrolTaskDefinition, + DeliveryTaskDefinition, + ComposeCleanTaskDefinition, + CustomComposeTaskDefinition, + ], /* eslint-disable @typescript-eslint/no-unused-vars */ cleaningZones = [], /* eslint-disable @typescript-eslint/no-unused-vars */ @@ -1358,17 +351,69 @@ export function CreateTaskForm({ const isScreenHeightLessThan800 = useMediaQuery('(max-height:800px)'); - const [favoriteTaskBuffer, setFavoriteTaskBuffer] = React.useState( - defaultFavoriteTask(), - ); + // Note that we are not checking if the number of supported tasks is larger + // than 0, this will cause the dashboard to fail when the create task form is + // opened. This is intentional as it is a misconfiguration and will require + // the build-time configuration to be fixed. + const { defaultTaskDescription, defaultTaskRequest, validTasks } = React.useMemo(() => { + let defaultTaskDescription: string | TaskDescription | null = null; + let defaultTaskRequest: TaskRequest | null = null; + const validTasks: TaskDefinition[] = []; + + tasksToDisplay.forEach((supportedTask: TaskDefinition) => { + const definitionId = supportedTask.taskDefinitionId; + const desc = getDefaultTaskDescription(definitionId); + const req = getDefaultTaskRequest(definitionId); + + if (desc === undefined) { + console.error(`Failed to retrieve task description for definition ID: [${definitionId}]`); + } + if (req === null) { + console.error(`Failed to create task request for definition ID: [${definitionId}]`); + } + if (desc && req) { + validTasks.push(supportedTask); + + if (!defaultTaskDescription && !defaultTaskRequest) { + defaultTaskDescription = desc; + defaultTaskRequest = req; + } + } + }); + return { defaultTaskDescription, defaultTaskRequest, validTasks }; + }, [tasksToDisplay]); + + if (!defaultTaskDescription || !defaultTaskRequest) { + // We should never reach this state unless a misconfiguration happened. + const err = Error('Default task could not be generated, this might be a configuration error'); + onFail && onFail(err, []); + console.error(err.message); + throw new TypeError(err.message); + } + + const [favoriteTaskBuffer, setFavoriteTaskBuffer] = React.useState({ + id: '', + name: '', + category: tasksToDisplay[0].requestCategory, + description: defaultTaskDescription, + unix_millis_earliest_start_time: 0, + priority: { type: 'binary', value: 0 }, + user: '', + task_definition_id: tasksToDisplay[0].taskDefinitionId, + }); const [favoriteTaskTitleError, setFavoriteTaskTitleError] = React.useState(false); const [savingFavoriteTask, setSavingFavoriteTask] = React.useState(false); - const [taskDefinitionId, setTaskDefinitionId] = React.useState( - SupportedTaskDefinitions[0].task_definition_id, - ); const [taskRequest, setTaskRequest] = React.useState( - () => requestTask ?? defaultTask(), + requestTask ?? defaultTaskRequest, + ); + const initialBookingLabel = requestTask ? getTaskBookingLabelFromTaskRequest(requestTask) : null; + const [taskDefinitionId, setTaskDefinitionId] = React.useState( + initialBookingLabel && + initialBookingLabel.description.task_definition_id && + typeof initialBookingLabel.description.task_definition_id === 'string' + ? initialBookingLabel.description.task_definition_id + : tasksToDisplay[0].taskDefinitionId, ); const [submitting, setSubmitting] = React.useState(false); @@ -1436,60 +481,95 @@ export function CreateTaskForm({ setFavoriteTaskBuffer({ ...favoriteTaskBuffer, description: newDesc, category: newCategory }); }; - // FIXME: Custom compose task descriptions are currently not allowed to be - // saved as favorite tasks. This will probably require a re-write of - // FavoriteTask's pydantic model with better typing. + // FIXME: Favorite tasks are disabled for custom compose tasks for now, as it + // will require a re-write of FavoriteTask's pydantic model with better typing. const handleCustomComposeTaskDescriptionChange = (newDesc: CustomComposeTaskDescription) => { setTaskRequest((prev) => { return { ...prev, - category: 'custom_compose', + category: CustomComposeTaskDefinition.requestCategory, description: newDesc, }; }); }; - const renderTaskDescriptionForm = () => { - if (taskRequest.category === 'patrol') { - return ( - handleTaskDescriptionChange('patrol', desc)} - allowSubmit={allowSubmit} - /> - ); - } else if (taskRequest.category === 'custom_compose') { - return ( - handleCustomComposeTaskDescriptionChange(desc)} - allowSubmit={allowSubmit} - /> - ); - } + const onValidate = (valid: boolean) => { + setFormFullyFilled(valid); + }; - switch (taskRequest.description.category) { - case 'delivery_pickup': + const renderTaskDescriptionForm = (definitionId: string) => { + switch (definitionId) { + case PatrolTaskDefinition.taskDefinitionId: + return ( + + handleTaskDescriptionChange(PatrolTaskDefinition.requestCategory, desc) + } + onValidate={onValidate} + /> + ); + case DeliveryTaskDefinition.taskDefinitionId: return ( + handleTaskDescriptionChange(DeliveryTaskDefinition.requestCategory, desc) + } + onValidate={onValidate} + /> + ); + case ComposeCleanTaskDefinition.taskDefinitionId: + return ( + { + desc.category = taskRequest.description.category; + handleTaskDescriptionChange(ComposeCleanTaskDefinition.requestCategory, desc); + }} + onValidate={onValidate} + /> + ); + case DeliveryPickupTaskDefinition.taskDefinitionId: + return ( + { + onChange={(desc: DeliveryPickupTaskDescription) => { + desc.category = taskRequest.description.category; + desc.phases[0].activity.description.activities[1].description.category = + taskRequest.description.category; + handleTaskDescriptionChange(DeliveryPickupTaskDefinition.requestCategory, desc); + }} + onValidate={onValidate} + /> + ); + case DeliverySequentialLotPickupTaskDefinition.taskDefinitionId: + return ( + { desc.category = taskRequest.description.category; desc.phases[0].activity.description.activities[1].description.category = taskRequest.description.category; - handleTaskDescriptionChange('compose', desc); - const pickupPerformAction = - desc.phases[0].activity.description.activities[1].description.description; + handleTaskDescriptionChange( + DeliverySequentialLotPickupTaskDefinition.requestCategory, + desc, + ); }} - allowSubmit={allowSubmit} + onValidate={onValidate} /> ); - case 'delivery_sequential_lot_pickup': - case 'delivery_area_pickup': + case DeliveryAreaPickupTaskDefinition.taskDefinitionId: return ( + ); + case CustomComposeTaskDefinition.taskDefinitionId: + return ( + { + handleCustomComposeTaskDescriptionChange(desc); + }} + onValidate={onValidate} /> ); - default: - return null; } }; const handleTaskTypeChange = (ev: React.ChangeEvent) => { - const newType = ev.target.value; - setTaskDefinitionId(newType); + const newTaskDefinitionId = ev.target.value; + setTaskDefinitionId(newTaskDefinitionId); - if (newType === 'custom_compose') { - taskRequest.category = 'custom_compose'; - taskRequest.description = ''; - } else { - const newDesc = defaultTaskDescription(newType); - if (newDesc === undefined) { - return; - } - taskRequest.description = newDesc; - const category = newType === 'patrol' ? 'patrol' : 'compose'; - taskRequest.category = category; - - setFavoriteTaskBuffer({ ...favoriteTaskBuffer, category, description: newDesc }); + const category = getTaskRequestCategory(newTaskDefinitionId); + if (category === undefined) { + const err = Error( + `Failed to retrieve task request category for task [${newTaskDefinitionId}], there might be a misconfiguration.`, + ); + console.error(err.message); + onFail && onFail(err, []); + return; } - }; + taskRequest.category = category; - const allowSubmit = (allow: boolean) => { - setFormFullyFilled(allow); + const description = getDefaultTaskDescription(newTaskDefinitionId) ?? ''; + taskRequest.description = description; + + if ( + newTaskDefinitionId !== CustomComposeTaskDefinition.taskDefinitionId && + typeof description === 'object' + ) { + setFavoriteTaskBuffer({ ...favoriteTaskBuffer, category, description }); + } }; // no memo because deps would likely change @@ -1548,10 +636,10 @@ export function CreateTaskForm({ request.requester = requester; request.unix_millis_request_time = Date.now(); - if (taskDefinitionId === 'custom_compose') { + if (taskDefinitionId === CustomComposeTaskDefinition.taskDefinitionId) { try { const obj = JSON.parse(request.description); - request.category = 'compose'; + request.category = CustomComposeTaskDefinition.requestCategory; request.description = obj; } catch (e) { console.error('Invalid custom compose task description'); @@ -1564,17 +652,23 @@ export function CreateTaskForm({ try { let requestBookingLabel: TaskBookingLabel | null = null; switch (taskDefinitionId) { - case 'delivery_pickup': - requestBookingLabel = makeDeliveryTaskBookingLabel(request.description); + case DeliveryPickupTaskDefinition.taskDefinitionId: + requestBookingLabel = makeDeliveryPickupTaskBookingLabel(request.description); break; - case 'delivery_sequential_lot_pickup': - case 'delivery_area_pickup': + case DeliverySequentialLotPickupTaskDefinition.taskDefinitionId: + case DeliveryAreaPickupTaskDefinition.taskDefinitionId: requestBookingLabel = makeDeliveryCustomTaskBookingLabel(request.description); break; - case 'patrol': + case PatrolTaskDefinition.taskDefinitionId: requestBookingLabel = makePatrolTaskBookingLabel(request.description); break; - case 'custom_compose': + case DeliveryTaskDefinition.taskDefinitionId: + requestBookingLabel = makeDeliveryTaskBookingLabel(request.description); + break; + case ComposeCleanTaskDefinition.taskDefinitionId: + requestBookingLabel = makeComposeCleanTaskBookingLabel(request.description); + break; + case CustomComposeTaskDefinition.taskDefinitionId: requestBookingLabel = makeCustomComposeTaskBookingLabel(); break; } @@ -1647,7 +741,7 @@ export function CreateTaskForm({ setSavingFavoriteTask(true); const favoriteTask = favoriteTaskBuffer; - favoriteTask.task_definition_id = taskDefinitionId ?? DefaultTaskDefinitionId; + favoriteTask.task_definition_id = taskDefinitionId ?? tasksToDisplay[0].taskDefinitionId; await submitFavoriteTask(favoriteTask); setSavingFavoriteTask(false); @@ -1677,7 +771,15 @@ export function CreateTaskForm({ onSuccessFavoriteTask && onSuccessFavoriteTask('Deleted favorite task successfully', favoriteTaskBuffer); - setTaskRequest(defaultTask()); + const defaultTaskRequest = getDefaultTaskRequest(tasksToDisplay[0].taskDefinitionId); + if (!defaultTaskRequest) { + // We should never reach this area as we have already validated that + // each supported task have a valid task request for generation + console.error('Failed to reset task request buffer after deleting favorite task'); + return; + } + + setTaskRequest(defaultTaskRequest); setOpenFavoriteDialog(false); setCallToDeleteFavoriteTask(false); setCallToUpdateFavoriteTask(false); @@ -1752,11 +854,7 @@ export function CreateTaskForm({ variant="outlined" fullWidth margin="normal" - value={ - taskRequest.category !== 'compose' - ? taskRequest.category - : taskRequest.description.category - } + value={taskDefinitionId} onChange={handleTaskTypeChange} sx={{ '& .MuiInputBase-input': { @@ -1766,14 +864,16 @@ export function CreateTaskForm({ }} InputLabelProps={{ style: { fontSize: isScreenHeightLessThan800 ? 16 : 20 } }} > - {SupportedTaskDefinitions.map((taskDefinition) => ( - - {taskDefinition.task_display_name} - - ))} + {validTasks.map((taskDefinition) => { + return ( + + {taskDefinition.taskDisplayName} + + ); + })} @@ -1838,7 +938,7 @@ export function CreateTaskForm({ flexItem style={{ marginTop: theme.spacing(2), marginBottom: theme.spacing(2) }} /> - {renderTaskDescriptionForm()} + {renderTaskDescriptionForm(taskDefinitionId)} diff --git a/packages/react-components/lib/tasks/index.ts b/packages/react-components/lib/tasks/index.ts index 2214ea0ea..5e21dadf7 100644 --- a/packages/react-components/lib/tasks/index.ts +++ b/packages/react-components/lib/tasks/index.ts @@ -1,4 +1,5 @@ export * from './create-task'; +export * from './types'; export * from './task-info'; export * from './task-logs'; export * from './task-booking-label-utils'; diff --git a/packages/react-components/lib/tasks/task-booking-label-utils.tsx b/packages/react-components/lib/tasks/task-booking-label-utils.tsx index 1f9bc0d94..82b8983e4 100644 --- a/packages/react-components/lib/tasks/task-booking-label-utils.tsx +++ b/packages/react-components/lib/tasks/task-booking-label-utils.tsx @@ -48,18 +48,20 @@ export function getTaskBookingLabelFromTaskState(taskState: TaskState): TaskBook export function getTaskBookingLabelFromTaskRequest( taskRequest: TaskRequest, ): TaskBookingLabel | null { + if (!taskRequest.labels) { + return null; + } + let requestLabel: TaskBookingLabel | null = null; - if (taskRequest.labels) { - for (const label of taskRequest.labels) { - try { - const parsedLabel = getTaskBookingLabelFromJsonString(label); - if (parsedLabel) { - requestLabel = parsedLabel; - break; - } - } catch (e) { - continue; + for (const label of taskRequest.labels) { + try { + const parsedLabel = getTaskBookingLabelFromJsonString(label); + if (parsedLabel) { + requestLabel = parsedLabel; + break; } + } catch (e) { + continue; } } return requestLabel; diff --git a/packages/react-components/lib/tasks/types/compose-clean.tsx b/packages/react-components/lib/tasks/types/compose-clean.tsx new file mode 100644 index 000000000..38781c4d8 --- /dev/null +++ b/packages/react-components/lib/tasks/types/compose-clean.tsx @@ -0,0 +1,155 @@ +import { Autocomplete, TextField } from '@mui/material'; +import React from 'react'; +import type { TaskBookingLabel } from 'api-client'; +import { TaskDefinition } from '../create-task'; + +export const ComposeCleanTaskDefinition: TaskDefinition = { + taskDefinitionId: 'compose-clean', + taskDisplayName: 'Clean', + requestCategory: 'compose', +}; + +interface GoToPlaceActivity { + category: string; + description: string; +} + +interface CleanActivity { + category: string; + description: { + unix_millis_action_duration_estimate: number; + category: string; + expected_finish_location: string; + description: { + zone: string; + }; + use_tool_sink: boolean; + }; +} + +export interface ComposeCleanTaskDescription { + category: string; + phases: [ + cleanPhase: { + activity: { + category: string; + description: { + activities: [goToPlaceActivity: GoToPlaceActivity, cleanActivity: CleanActivity]; + }; + }; + }, + ]; +} + +export function makeComposeCleanTaskBookingLabel( + task_description: ComposeCleanTaskDescription, +): TaskBookingLabel { + return { + description: { + task_definition_id: ComposeCleanTaskDefinition.taskDefinitionId, + destination: + task_description.phases[0].activity.description.activities[1].description.description.zone, + }, + }; +} + +export function isComposeCleanTaskDescriptionValid( + taskDescription: ComposeCleanTaskDescription, +): boolean { + const goToPlaceActivity = taskDescription.phases[0].activity.description.activities[0]; + const cleanActivity = taskDescription.phases[0].activity.description.activities[1]; + return ( + goToPlaceActivity.description.length !== 0 && + cleanActivity.description.description.zone.length !== 0 && + goToPlaceActivity.description === cleanActivity.description.description.zone + ); +} + +export function makeDefaultComposeCleanTaskDescription(): ComposeCleanTaskDescription { + return { + category: 'clean', + phases: [ + { + activity: { + category: 'sequence', + description: { + activities: [ + { + category: 'go_to_place', + description: '', + }, + { + category: 'perform_action', + description: { + unix_millis_action_duration_estimate: 60000, + category: 'clean', + expected_finish_location: '', + description: { + zone: '', + }, + use_tool_sink: true, + }, + }, + ], + }, + }, + }, + ], + }; +} + +export function makeComposeCleanTaskShortDescription( + desc: ComposeCleanTaskDescription, + displayName: string | undefined, +): string { + const cleanActivity = desc.phases[0].activity.description.activities[1]; + return `[${displayName ?? ComposeCleanTaskDefinition.taskDisplayName}] zone [${ + cleanActivity.description.description.zone + }]`; +} + +interface ComposeCleanTaskFormProps { + taskDesc: ComposeCleanTaskDescription; + cleaningZones: string[]; + onChange(cleanTaskDescription: ComposeCleanTaskDescription): void; + onValidate(valid: boolean): void; +} + +export function ComposeCleanTaskForm({ + taskDesc, + cleaningZones, + onChange, + onValidate, +}: ComposeCleanTaskFormProps): React.JSX.Element { + const onInputChange = (desc: ComposeCleanTaskDescription) => { + onValidate(isComposeCleanTaskDescriptionValid(desc)); + onChange(desc); + }; + + return ( + { + const zone = newValue ?? ''; + taskDesc.phases[0].activity.description.activities[0].description = zone; + taskDesc.phases[0].activity.description.activities[1].description.expected_finish_location = + zone; + taskDesc.phases[0].activity.description.activities[1].description.description.zone = zone; + onInputChange(taskDesc); + }} + onBlur={(ev) => { + const zone = (ev.target as HTMLInputElement).value; + taskDesc.phases[0].activity.description.activities[0].description = zone; + taskDesc.phases[0].activity.description.activities[1].description.expected_finish_location = + zone; + taskDesc.phases[0].activity.description.activities[1].description.description.zone = zone; + onInputChange(taskDesc); + }} + renderInput={(params) => } + /> + ); +} diff --git a/packages/react-components/lib/tasks/types/custom-compose.tsx b/packages/react-components/lib/tasks/types/custom-compose.tsx new file mode 100644 index 000000000..348e226d7 --- /dev/null +++ b/packages/react-components/lib/tasks/types/custom-compose.tsx @@ -0,0 +1,73 @@ +import { Grid, TextField, useTheme } from '@mui/material'; +import React from 'react'; +import type { TaskBookingLabel } from 'api-client'; +import { TaskDefinition } from '../create-task'; + +export const CustomComposeTaskDefinition: TaskDefinition = { + taskDefinitionId: 'custom_compose', + taskDisplayName: 'Custom Compose Task', + requestCategory: 'compose', +}; + +export type CustomComposeTaskDescription = string; + +export function makeCustomComposeTaskBookingLabel(): TaskBookingLabel { + return { + description: { + task_definition_id: CustomComposeTaskDefinition.taskDefinitionId, + }, + }; +} + +export function makeCustomComposeTaskShortDescription(desc: CustomComposeTaskDescription): string { + return desc; +} + +const isCustomTaskDescriptionValid = (taskDescription: string): boolean => { + if (taskDescription.length === 0) { + return false; + } + + try { + JSON.parse(taskDescription); + } catch (e) { + return false; + } + + return true; +}; + +interface CustomComposeTaskFormProps { + taskDesc: CustomComposeTaskDescription; + onChange(customComposeTaskDescription: CustomComposeTaskDescription): void; + onValidate(valid: boolean): void; +} + +export function CustomComposeTaskForm({ + taskDesc, + onChange, + onValidate, +}: CustomComposeTaskFormProps): React.JSX.Element { + const theme = useTheme(); + const onInputChange = (desc: CustomComposeTaskDescription) => { + onValidate(isCustomTaskDescriptionValid(desc)); + onChange(desc); + }; + + return ( + + + { + onInputChange(ev.target.value); + }} + /> + + + ); +} diff --git a/packages/react-components/lib/tasks/create-task.spec.tsx b/packages/react-components/lib/tasks/types/delivery-custom.spec.tsx similarity index 88% rename from packages/react-components/lib/tasks/create-task.spec.tsx rename to packages/react-components/lib/tasks/types/delivery-custom.spec.tsx index 0d3d268b9..06cb112cc 100644 --- a/packages/react-components/lib/tasks/create-task.spec.tsx +++ b/packages/react-components/lib/tasks/types/delivery-custom.spec.tsx @@ -1,23 +1,23 @@ import { - defaultDeliveryTaskDescription, - defaultDeliveryCustomTaskDescription, + deliveryCustomInsertCartId, + deliveryCustomInsertDropoff, + deliveryCustomInsertOnCancel, + deliveryCustomInsertPickup, DeliveryCustomTaskDescription, deliveryInsertCartId, deliveryInsertDropoff, deliveryInsertOnCancel, deliveryInsertPickup, - DeliveryTaskDescription, - deliveryCustomInsertPickup, - deliveryCustomInsertCartId, - deliveryCustomInsertDropoff, - deliveryCustomInsertOnCancel, -} from './create-task'; + DeliveryPickupTaskDescription, + makeDefaultDeliveryCustomTaskDescription, + makeDefaultDeliveryPickupTaskDescription, +} from '.'; describe('Custom deliveries', () => { - it('delivery 1:1', () => { - let deliveryTaskDescription: DeliveryTaskDescription | null = null; + it('delivery pickup', () => { + let deliveryPickupTaskDescription: DeliveryPickupTaskDescription | null = null; try { - deliveryTaskDescription = JSON.parse(`{ + deliveryPickupTaskDescription = JSON.parse(`{ "category": "delivery_pickup", "phases": [ { @@ -113,13 +113,13 @@ describe('Custom deliveries', () => { } ] } - `) as DeliveryTaskDescription; + `) as DeliveryPickupTaskDescription; } catch (e) { - deliveryTaskDescription = null; + deliveryPickupTaskDescription = null; } - expect(deliveryTaskDescription).not.toEqual(null); + expect(deliveryPickupTaskDescription).not.toEqual(null); - let description = defaultDeliveryTaskDescription(); + let description = makeDefaultDeliveryPickupTaskDescription(); description = deliveryInsertPickup(description, 'test_pickup_place', 'test_pickup_lot'); description = deliveryInsertCartId(description, 'test_cart_id'); description = deliveryInsertDropoff(description, 'test_dropoff_place'); @@ -128,13 +128,13 @@ describe('Custom deliveries', () => { 'test_waypoint_2', 'test_waypoint_3', ]); - expect(deliveryTaskDescription).toEqual(description); + expect(deliveryPickupTaskDescription).toEqual(description); }); it('delivery_sequential_lot_pickup', () => { - let deliveryTaskDescription: DeliveryCustomTaskDescription | null = null; + let deliveryCustomTaskDescription: DeliveryCustomTaskDescription | null = null; try { - deliveryTaskDescription = JSON.parse(`{ + deliveryCustomTaskDescription = JSON.parse(`{ "category": "delivery_sequential_lot_pickup", "phases": [ { @@ -232,11 +232,11 @@ describe('Custom deliveries', () => { } `) as DeliveryCustomTaskDescription; } catch (e) { - deliveryTaskDescription = null; + deliveryCustomTaskDescription = null; } - expect(deliveryTaskDescription).not.toEqual(null); + expect(deliveryCustomTaskDescription).not.toEqual(null); - let description: DeliveryCustomTaskDescription = defaultDeliveryCustomTaskDescription( + let description: DeliveryCustomTaskDescription = makeDefaultDeliveryCustomTaskDescription( 'delivery_sequential_lot_pickup', ); description = deliveryCustomInsertPickup(description, 'test_pickup_place', 'test_pickup_zone'); @@ -247,13 +247,13 @@ describe('Custom deliveries', () => { 'test_waypoint_2', 'test_waypoint_3', ]); - expect(deliveryTaskDescription).toEqual(description); + expect(deliveryCustomTaskDescription).toEqual(description); }); it('delivery_area_pickup', () => { - let deliveryTaskDescription: DeliveryCustomTaskDescription | null = null; + let deliveryCustomTaskDescription: DeliveryCustomTaskDescription | null = null; try { - deliveryTaskDescription = JSON.parse(`{ + deliveryCustomTaskDescription = JSON.parse(`{ "category": "delivery_area_pickup", "phases": [ { @@ -351,12 +351,12 @@ describe('Custom deliveries', () => { } `) as DeliveryCustomTaskDescription; } catch (e) { - deliveryTaskDescription = null; + deliveryCustomTaskDescription = null; } - expect(deliveryTaskDescription).not.toEqual(null); + expect(deliveryCustomTaskDescription).not.toEqual(null); let description: DeliveryCustomTaskDescription = - defaultDeliveryCustomTaskDescription('delivery_area_pickup'); + makeDefaultDeliveryCustomTaskDescription('delivery_area_pickup'); description = deliveryCustomInsertPickup(description, 'test_pickup_place', 'test_pickup_zone'); description = deliveryCustomInsertCartId(description, 'test_cart_id'); description = deliveryCustomInsertDropoff(description, 'test_dropoff_place'); @@ -365,6 +365,6 @@ describe('Custom deliveries', () => { 'test_waypoint_2', 'test_waypoint_3', ]); - expect(deliveryTaskDescription).toEqual(description); + expect(deliveryCustomTaskDescription).toEqual(description); }); }); diff --git a/packages/react-components/lib/tasks/types/delivery-custom.tsx b/packages/react-components/lib/tasks/types/delivery-custom.tsx new file mode 100644 index 000000000..5bebe359c --- /dev/null +++ b/packages/react-components/lib/tasks/types/delivery-custom.tsx @@ -0,0 +1,832 @@ +import { Autocomplete, Grid, TextField, useMediaQuery, useTheme } from '@mui/material'; +import React from 'react'; +import { isNonEmptyString } from './utils'; +import type { TaskBookingLabel } from 'api-client'; +import { TaskDefinition } from '../create-task'; + +export const DeliveryPickupTaskDefinition: TaskDefinition = { + taskDefinitionId: 'delivery_pickup', + taskDisplayName: 'Delivery - 1:1', + requestCategory: 'compose', +}; + +export const DeliverySequentialLotPickupTaskDefinition: TaskDefinition = { + taskDefinitionId: 'delivery_sequential_lot_pickup', + taskDisplayName: 'Delivery - Sequential lot pick up', + requestCategory: 'compose', +}; + +export const DeliveryAreaPickupTaskDefinition: TaskDefinition = { + taskDefinitionId: 'delivery_area_pickup', + taskDisplayName: 'Delivery - Area pick up', + requestCategory: 'compose', +}; + +export interface LotPickupActivity { + category: string; + description: { + unix_millis_action_duration_estimate: number; + category: string; + description: { + cart_id: string; + pickup_lot: string; + }; + }; +} + +interface ZonePickupActivity { + category: string; + description: { + unix_millis_action_duration_estimate: number; + category: string; + description: { + cart_id: string; + pickup_zone: string; + }; + }; +} + +export interface GoToPlaceActivity { + category: string; + description: string; +} + +interface CartCustomPickupPhase { + activity: { + category: string; + description: { + activities: [go_to_pickup: GoToPlaceActivity, pickup_cart: ZonePickupActivity]; + }; + }; +} + +interface CartPickupPhase { + activity: { + category: string; + description: { + activities: [go_to_pickup: GoToPlaceActivity, pickup_cart: LotPickupActivity]; + }; + }; +} + +interface DropoffActivity { + category: string; + description: { + unix_millis_action_duration_estimate: number; + category: string; + description: {}; + }; +} + +interface OneOfWaypoint { + waypoint: string; +} + +interface GoToOneOfThePlacesActivity { + category: string; + description: { + one_of: OneOfWaypoint[]; + constraints: [ + { + category: string; + description: string; + }, + ]; + }; +} + +interface OnCancelDropoff { + category: string; + description: [ + go_to_one_of_the_places: GoToOneOfThePlacesActivity, + delivery_dropoff: DropoffActivity, + ]; +} + +interface DeliveryWithCancellationPhase { + activity: { + category: string; + description: { + activities: [go_to_place: GoToPlaceActivity]; + }; + }; + on_cancel: OnCancelDropoff[]; +} + +interface CartDropoffPhase { + activity: { + category: string; + description: { + activities: [delivery_dropoff: DropoffActivity]; + }; + }; +} + +export interface DeliveryCustomTaskDescription { + category: string; + phases: [ + pickup_phase: CartCustomPickupPhase, + delivery_phase: DeliveryWithCancellationPhase, + dropoff_phase: CartDropoffPhase, + ]; +} + +export interface DeliveryPickupTaskDescription { + category: string; + phases: [ + pickup_phase: CartPickupPhase, + delivery_phase: DeliveryWithCancellationPhase, + dropoff_phase: CartDropoffPhase, + ]; +} + +export function makeDeliveryPickupTaskBookingLabel( + task_description: DeliveryPickupTaskDescription, +): TaskBookingLabel { + const pickupDescription = + task_description.phases[0].activity.description.activities[1].description.description; + return { + description: { + task_definition_id: task_description.category, + pickup: pickupDescription.pickup_lot, + destination: task_description.phases[1].activity.description.activities[0].description, + cart_id: pickupDescription.cart_id, + }, + }; +} + +export function makeDeliveryCustomTaskBookingLabel( + task_description: DeliveryCustomTaskDescription, +): TaskBookingLabel { + const pickupDescription = + task_description.phases[0].activity.description.activities[1].description.description; + return { + description: { + task_definition_id: task_description.category, + pickup: pickupDescription.pickup_zone, + destination: task_description.phases[1].activity.description.activities[0].description, + cart_id: pickupDescription.cart_id, + }, + }; +} + +export function makeDeliveryPickupTaskShortDescription( + desc: DeliveryPickupTaskDescription, + taskDisplayName: string | undefined, +): string { + try { + const goToPickup: GoToPlaceActivity = desc.phases[0].activity.description.activities[0]; + const pickup: LotPickupActivity = desc.phases[0].activity.description.activities[1]; + const cartId = pickup.description.description.cart_id; + const goToDropoff: GoToPlaceActivity = desc.phases[1].activity.description.activities[0]; + + return `[${ + taskDisplayName ?? DeliveryPickupTaskDefinition.taskDisplayName + }] payload [${cartId}] from [${goToPickup.description}] to [${goToDropoff.description}]`; + } catch (e) { + try { + const descriptionString = JSON.stringify(desc); + console.error(descriptionString); + return descriptionString; + } catch (e) { + console.error( + `Failed to parse task description of delivery pickup task: ${(e as Error).message}`, + ); + } + } + + return '[Unknown] delivery pickup task'; +} + +export function makeDeliveryCustomTaskShortDescription( + desc: DeliveryCustomTaskDescription, + taskDisplayName: string | undefined, +): string { + try { + const goToPickup: GoToPlaceActivity = desc.phases[0].activity.description.activities[0]; + const pickup: ZonePickupActivity = desc.phases[0].activity.description.activities[1]; + const cartId = pickup.description.description.cart_id; + const goToDropoff: GoToPlaceActivity = desc.phases[1].activity.description.activities[0]; + + switch (desc.category) { + case DeliverySequentialLotPickupTaskDefinition.taskDefinitionId: { + return `[${ + taskDisplayName ?? DeliverySequentialLotPickupTaskDefinition.taskDisplayName + }] payload [${cartId}] from [${goToPickup.description}] to [${goToDropoff.description}]`; + } + case DeliveryAreaPickupTaskDefinition.taskDefinitionId: { + return `[${ + taskDisplayName ?? DeliveryAreaPickupTaskDefinition.taskDisplayName + }] payload [${cartId}] from [${goToPickup.description}] to [${goToDropoff.description}]`; + } + default: + return `[Unknown] type "${desc.category}"`; + } + } catch (e) { + try { + const descriptionString = JSON.stringify(desc); + console.error(descriptionString); + return descriptionString; + } catch (e) { + console.error( + `Failed to parse task description of delivery pickup task: ${(e as Error).message}`, + ); + } + } + + return '[Unknown] delivery pickup task'; +} + +const isDeliveryPickupTaskDescriptionValid = ( + taskDescription: DeliveryPickupTaskDescription, + pickupPoints: Record, + dropoffPoints: Record, +): boolean => { + const goToPickup = taskDescription.phases[0].activity.description.activities[0]; + const pickup = taskDescription.phases[0].activity.description.activities[1]; + const goToDropoff = taskDescription.phases[1].activity.description.activities[0]; + return ( + isNonEmptyString(goToPickup.description) && + Object.keys(pickupPoints).includes(goToPickup.description) && + pickupPoints[goToPickup.description] === pickup.description.description.pickup_lot && + isNonEmptyString(pickup.description.description.cart_id) && + isNonEmptyString(goToDropoff.description) && + Object.keys(dropoffPoints).includes(goToDropoff.description) + ); +}; + +const isDeliveryCustomTaskDescriptionValid = ( + taskDescription: DeliveryCustomTaskDescription, + pickupZones: string[], + dropoffPoints: string[], +): boolean => { + const goToPickup = taskDescription.phases[0].activity.description.activities[0]; + const pickup = taskDescription.phases[0].activity.description.activities[1]; + const goToDropoff = taskDescription.phases[1].activity.description.activities[0]; + return ( + isNonEmptyString(goToPickup.description) && + isNonEmptyString(pickup.description.description.pickup_zone) && + pickupZones.includes(pickup.description.description.pickup_zone) && + isNonEmptyString(pickup.description.description.cart_id) && + isNonEmptyString(goToDropoff.description) && + dropoffPoints.includes(goToDropoff.description) + ); +}; + +export function deliveryInsertPickup( + taskDescription: DeliveryPickupTaskDescription, + pickupPlace: string, + pickupLot: string, +): DeliveryPickupTaskDescription { + taskDescription.phases[0].activity.description.activities[0].description = pickupPlace; + taskDescription.phases[0].activity.description.activities[1].description.description.pickup_lot = + pickupLot; + return taskDescription; +} + +export function deliveryInsertCartId( + taskDescription: DeliveryPickupTaskDescription, + cartId: string, +): DeliveryPickupTaskDescription { + taskDescription.phases[0].activity.description.activities[1].description.description.cart_id = + cartId; + return taskDescription; +} + +export function deliveryInsertDropoff( + taskDescription: DeliveryPickupTaskDescription, + dropoffPlace: string, +): DeliveryPickupTaskDescription { + taskDescription.phases[1].activity.description.activities[0].description = dropoffPlace; + return taskDescription; +} + +export function deliveryInsertOnCancel( + taskDescription: DeliveryPickupTaskDescription, + onCancelPlaces: string[], +): DeliveryPickupTaskDescription { + const goToOneOfThePlaces: GoToOneOfThePlacesActivity = { + category: 'go_to_place', + description: { + one_of: onCancelPlaces.map((placeName) => { + return { + waypoint: placeName, + }; + }), + constraints: [ + { + category: 'prefer_same_map', + description: '', + }, + ], + }, + }; + const deliveryDropoff: DropoffActivity = { + category: 'perform_action', + description: { + unix_millis_action_duration_estimate: 60000, + category: 'delivery_dropoff', + description: {}, + }, + }; + const onCancelDropoff: OnCancelDropoff = { + category: 'sequence', + description: [goToOneOfThePlaces, deliveryDropoff], + }; + taskDescription.phases[1].on_cancel = [onCancelDropoff]; + return taskDescription; +} + +interface DeliveryPickupTaskFormProps { + taskDesc: DeliveryPickupTaskDescription; + pickupPoints: Record; + cartIds: string[]; + dropoffPoints: Record; + onChange(taskDesc: DeliveryPickupTaskDescription): void; + onValidate(valid: boolean): void; +} + +export function DeliveryPickupTaskForm({ + taskDesc, + pickupPoints = {}, + cartIds = [], + dropoffPoints = {}, + onChange, + onValidate, +}: DeliveryPickupTaskFormProps): React.JSX.Element { + const theme = useTheme(); + const isScreenHeightLessThan800 = useMediaQuery('(max-height:800px)'); + const onInputChange = (desc: DeliveryPickupTaskDescription) => { + onValidate(isDeliveryPickupTaskDescriptionValid(desc, pickupPoints, dropoffPoints)); + onChange(desc); + }; + + return ( + + + { + const pickupLot = pickupPoints[newValue] ?? ''; + let newTaskDesc = { ...taskDesc }; + newTaskDesc = deliveryInsertPickup(newTaskDesc, newValue, pickupLot); + onInputChange(newTaskDesc); + }} + onBlur={(ev) => { + const place = (ev.target as HTMLInputElement).value; + const pickupLot = pickupPoints[place] ?? ''; + let newTaskDesc = { ...taskDesc }; + newTaskDesc = deliveryInsertPickup(newTaskDesc, place, pickupLot); + onInputChange(newTaskDesc); + }} + sx={{ + '& .MuiOutlinedInput-root': { + height: isScreenHeightLessThan800 ? '3rem' : '3.5rem', + fontSize: isScreenHeightLessThan800 ? 14 : 20, + }, + }} + renderInput={(params) => ( + + )} + /> + + + option} + onInputChange={(_ev, newValue) => { + let newTaskDesc = { ...taskDesc }; + newTaskDesc = deliveryInsertCartId(newTaskDesc, newValue); + onInputChange(newTaskDesc); + }} + onBlur={(ev) => { + let newTaskDesc = { ...taskDesc }; + newTaskDesc = deliveryInsertCartId(newTaskDesc, (ev.target as HTMLInputElement).value); + onInputChange(newTaskDesc); + }} + sx={{ + '& .MuiOutlinedInput-root': { + height: isScreenHeightLessThan800 ? '3rem' : '3.5rem', + fontSize: isScreenHeightLessThan800 ? 14 : 20, + }, + }} + renderInput={(params) => ( + + )} + /> + + + { + let newTaskDesc = { ...taskDesc }; + newTaskDesc = deliveryInsertDropoff(newTaskDesc, newValue); + onInputChange(newTaskDesc); + }} + onBlur={(ev) => { + let newTaskDesc = { ...taskDesc }; + newTaskDesc = deliveryInsertDropoff(newTaskDesc, (ev.target as HTMLInputElement).value); + onInputChange(newTaskDesc); + }} + sx={{ + '& .MuiOutlinedInput-root': { + height: isScreenHeightLessThan800 ? '3rem' : '3.5rem', + fontSize: isScreenHeightLessThan800 ? 14 : 20, + }, + }} + renderInput={(params) => ( + + )} + /> + + + ); +} + +export function deliveryCustomInsertPickup( + taskDescription: DeliveryCustomTaskDescription, + pickupPlace: string, + pickupZone: string, +): DeliveryCustomTaskDescription { + taskDescription.phases[0].activity.description.activities[0].description = pickupPlace; + taskDescription.phases[0].activity.description.activities[1].description.description.pickup_zone = + pickupZone; + return taskDescription; +} + +export function deliveryCustomInsertCartId( + taskDescription: DeliveryCustomTaskDescription, + cartId: string, +): DeliveryCustomTaskDescription { + taskDescription.phases[0].activity.description.activities[1].description.description.cart_id = + cartId; + return taskDescription; +} + +export function deliveryCustomInsertDropoff( + taskDescription: DeliveryCustomTaskDescription, + dropoffPlace: string, +): DeliveryCustomTaskDescription { + taskDescription.phases[1].activity.description.activities[0].description = dropoffPlace; + return taskDescription; +} + +export function deliveryCustomInsertOnCancel( + taskDescription: DeliveryCustomTaskDescription, + onCancelPlaces: string[], +): DeliveryCustomTaskDescription { + const goToOneOfThePlaces: GoToOneOfThePlacesActivity = { + category: 'go_to_place', + description: { + one_of: onCancelPlaces.map((placeName) => { + return { + waypoint: placeName, + }; + }), + constraints: [ + { + category: 'prefer_same_map', + description: '', + }, + ], + }, + }; + const deliveryDropoff: DropoffActivity = { + category: 'perform_action', + description: { + unix_millis_action_duration_estimate: 60000, + category: 'delivery_dropoff', + description: {}, + }, + }; + const onCancelDropoff: OnCancelDropoff = { + category: 'sequence', + description: [goToOneOfThePlaces, deliveryDropoff], + }; + taskDescription.phases[1].on_cancel = [onCancelDropoff]; + return taskDescription; +} + +interface DeliveryCustomProps { + taskDesc: DeliveryCustomTaskDescription; + pickupZones: string[]; + cartIds: string[]; + dropoffPoints: string[]; + onChange(taskDesc: DeliveryCustomTaskDescription): void; + onValidate(valid: boolean): void; +} + +export function DeliveryCustomTaskForm({ + taskDesc, + pickupZones = [], + cartIds = [], + dropoffPoints = [], + onChange, + onValidate, +}: DeliveryCustomProps): React.JSX.Element { + const theme = useTheme(); + const isScreenHeightLessThan800 = useMediaQuery('(max-height:800px)'); + const onInputChange = (desc: DeliveryCustomTaskDescription) => { + onValidate(isDeliveryCustomTaskDescriptionValid(desc, pickupZones, dropoffPoints)); + onChange(desc); + }; + + return ( + + + { + let newTaskDesc = { ...taskDesc }; + newTaskDesc = deliveryCustomInsertPickup(newTaskDesc, newValue, newValue); + onInputChange(newTaskDesc); + }} + onBlur={(ev) => { + const zone = (ev.target as HTMLInputElement).value; + let newTaskDesc = { ...taskDesc }; + newTaskDesc = deliveryCustomInsertPickup(newTaskDesc, zone, zone); + onInputChange(newTaskDesc); + }} + sx={{ + '& .MuiOutlinedInput-root': { + height: isScreenHeightLessThan800 ? '3rem' : '3.5rem', + fontSize: isScreenHeightLessThan800 ? 14 : 20, + }, + }} + renderInput={(params) => ( + + )} + /> + + + option} + onInputChange={(_ev, newValue) => { + let newTaskDesc = { ...taskDesc }; + newTaskDesc = deliveryCustomInsertCartId(newTaskDesc, newValue); + onInputChange(newTaskDesc); + }} + onBlur={(ev) => { + let newTaskDesc = { ...taskDesc }; + newTaskDesc = deliveryCustomInsertCartId( + newTaskDesc, + (ev.target as HTMLInputElement).value, + ); + onInputChange(newTaskDesc); + }} + sx={{ + '& .MuiOutlinedInput-root': { + height: isScreenHeightLessThan800 ? '3rem' : '3.5rem', + fontSize: isScreenHeightLessThan800 ? 14 : 20, + }, + }} + renderInput={(params) => ( + + )} + /> + + + { + let newTaskDesc = { ...taskDesc }; + newTaskDesc = deliveryCustomInsertDropoff(newTaskDesc, newValue); + onInputChange(newTaskDesc); + }} + onBlur={(ev) => { + let newTaskDesc = { ...taskDesc }; + newTaskDesc = deliveryCustomInsertDropoff( + newTaskDesc, + (ev.target as HTMLInputElement).value, + ); + onInputChange(newTaskDesc); + }} + sx={{ + '& .MuiOutlinedInput-root': { + height: isScreenHeightLessThan800 ? '3rem' : '3.5rem', + fontSize: isScreenHeightLessThan800 ? 14 : 20, + }, + }} + renderInput={(params) => ( + + )} + /> + + + ); +} + +export function makeDefaultDeliveryPickupTaskDescription(): DeliveryPickupTaskDescription { + return { + category: 'delivery_pickup', + phases: [ + { + activity: { + category: 'sequence', + description: { + activities: [ + { + category: 'go_to_place', + description: '', + }, + { + category: 'perform_action', + description: { + unix_millis_action_duration_estimate: 60000, + category: 'delivery_pickup', + description: { + cart_id: '', + pickup_lot: '', + }, + }, + }, + ], + }, + }, + }, + { + activity: { + category: 'sequence', + description: { + activities: [ + { + category: 'go_to_place', + description: '', + }, + ], + }, + }, + on_cancel: [], + }, + { + activity: { + category: 'sequence', + description: { + activities: [ + { + category: 'perform_action', + description: { + unix_millis_action_duration_estimate: 60000, + category: 'delivery_dropoff', + description: {}, + }, + }, + ], + }, + }, + }, + ], + }; +} + +export function makeDefaultDeliveryCustomTaskDescription( + taskCategory: string, +): DeliveryCustomTaskDescription { + return { + category: taskCategory, + phases: [ + { + activity: { + category: 'sequence', + description: { + activities: [ + { + category: 'go_to_place', + description: '', + }, + { + category: 'perform_action', + description: { + unix_millis_action_duration_estimate: 60000, + category: taskCategory, + description: { + cart_id: '', + pickup_zone: '', + }, + }, + }, + ], + }, + }, + }, + { + activity: { + category: 'sequence', + description: { + activities: [ + { + category: 'go_to_place', + description: '', + }, + ], + }, + }, + on_cancel: [], + }, + { + activity: { + category: 'sequence', + description: { + activities: [ + { + category: 'perform_action', + description: { + unix_millis_action_duration_estimate: 60000, + category: 'delivery_dropoff', + description: {}, + }, + }, + ], + }, + }, + }, + ], + }; +} diff --git a/packages/react-components/lib/tasks/types/delivery.tsx b/packages/react-components/lib/tasks/types/delivery.tsx new file mode 100644 index 000000000..fdc32e24c --- /dev/null +++ b/packages/react-components/lib/tasks/types/delivery.tsx @@ -0,0 +1,261 @@ +import { isNonEmptyString, isPositiveNumber } from './utils'; +import { Autocomplete, Grid, TextField, useTheme } from '@mui/material'; +import { PositiveIntField } from '../../form-inputs'; +import React from 'react'; +import type { TaskBookingLabel } from 'api-client'; +import { TaskDefinition } from '../create-task'; + +export const DeliveryTaskDefinition: TaskDefinition = { + taskDefinitionId: 'delivery', + taskDisplayName: 'Delivery', + requestCategory: 'delivery', +}; + +interface TaskPlace { + place: string; + handler: string; + payload: { + sku: string; + quantity: number; + }; +} + +export interface DeliveryTaskDescription { + pickup: TaskPlace; + dropoff: TaskPlace; +} + +export function makeDeliveryTaskBookingLabel( + task_description: DeliveryTaskDescription, +): TaskBookingLabel { + return { + description: { + task_definition_id: DeliveryTaskDefinition.taskDefinitionId, + pickup: task_description.pickup.place, + destination: task_description.dropoff.place, + cart_id: task_description.pickup.payload.sku, + }, + }; +} + +function isTaskPlaceValid(place: TaskPlace): boolean { + return ( + isNonEmptyString(place.place) && + isNonEmptyString(place.handler) && + isNonEmptyString(place.payload.sku) && + isPositiveNumber(place.payload.quantity) + ); +} + +function isDeliveryTaskDescriptionValid(taskDescription: DeliveryTaskDescription): boolean { + return isTaskPlaceValid(taskDescription.pickup) && isTaskPlaceValid(taskDescription.dropoff); +} + +export function makeDefaultDeliveryTaskDescription(): DeliveryTaskDescription { + return { + pickup: { + place: '', + handler: '', + payload: { + sku: '', + quantity: 1, + }, + }, + dropoff: { + place: '', + handler: '', + payload: { + sku: '', + quantity: 1, + }, + }, + }; +} + +export function makeDeliveryTaskShortDescription( + desc: DeliveryTaskDescription, + displayName?: string, +): string { + return `[${displayName ?? DeliveryTaskDefinition.taskDisplayName}] Pickup [${ + desc.pickup.payload.sku + }] from [${desc.pickup.place}], dropoff [${desc.dropoff.payload.sku}] at [${desc.dropoff.place}]`; +} + +export interface DeliveryTaskFormProps { + taskDesc: DeliveryTaskDescription; + pickupPoints: Record; + dropoffPoints: Record; + onChange(taskDesc: DeliveryTaskDescription): void; + onValidate(valid: boolean): void; +} + +export function DeliveryTaskForm({ + taskDesc, + pickupPoints = {}, + dropoffPoints = {}, + onChange, + onValidate, +}: DeliveryTaskFormProps): React.JSX.Element { + const theme = useTheme(); + const onInputChange = (desc: DeliveryTaskDescription) => { + onValidate(isDeliveryTaskDescriptionValid(desc)); + onChange(desc); + }; + + return ( + + + { + const place = newValue ?? ''; + const handler = + newValue !== null && pickupPoints[newValue] ? pickupPoints[newValue] : ''; + onInputChange({ + ...taskDesc, + pickup: { + ...taskDesc.pickup, + place: place, + handler: handler, + }, + }); + }} + onBlur={(ev) => + pickupPoints[(ev.target as HTMLInputElement).value] && + onInputChange({ + ...taskDesc, + pickup: { + ...taskDesc.pickup, + place: (ev.target as HTMLInputElement).value, + handler: pickupPoints[(ev.target as HTMLInputElement).value], + }, + }) + } + renderInput={(params) => ( + + )} + /> + + + { + onInputChange({ + ...taskDesc, + pickup: { + ...taskDesc.pickup, + payload: { + ...taskDesc.pickup.payload, + sku: ev.target.value, + }, + }, + }); + }} + /> + + + { + onInputChange({ + ...taskDesc, + pickup: { + ...taskDesc.pickup, + payload: { + ...taskDesc.pickup.payload, + quantity: val, + }, + }, + }); + }} + /> + + + { + const place = newValue ?? ''; + const handler = + newValue !== null && dropoffPoints[newValue] ? dropoffPoints[newValue] : ''; + onInputChange({ + ...taskDesc, + dropoff: { + ...taskDesc.dropoff, + place: place, + handler: handler, + }, + }); + }} + onBlur={(ev) => + dropoffPoints[(ev.target as HTMLInputElement).value] && + onInputChange({ + ...taskDesc, + dropoff: { + ...taskDesc.dropoff, + place: (ev.target as HTMLInputElement).value, + handler: dropoffPoints[(ev.target as HTMLInputElement).value], + }, + }) + } + renderInput={(params) => ( + + )} + /> + + + { + onInputChange({ + ...taskDesc, + dropoff: { + ...taskDesc.dropoff, + payload: { + ...taskDesc.dropoff.payload, + sku: ev.target.value, + }, + }, + }); + }} + /> + + + { + onInputChange({ + ...taskDesc, + dropoff: { + ...taskDesc.dropoff, + payload: { + ...taskDesc.dropoff.payload, + quantity: val, + }, + }, + }); + }} + /> + + + ); +} diff --git a/packages/react-components/lib/tasks/types/index.ts b/packages/react-components/lib/tasks/types/index.ts new file mode 100644 index 000000000..e00fcdb75 --- /dev/null +++ b/packages/react-components/lib/tasks/types/index.ts @@ -0,0 +1,6 @@ +export * from './compose-clean'; +export * from './custom-compose'; +export * from './delivery-custom'; +export * from './delivery'; +export * from './patrol'; +export * from './utils'; diff --git a/packages/react-components/lib/tasks/types/patrol.tsx b/packages/react-components/lib/tasks/types/patrol.tsx new file mode 100644 index 000000000..0ae445fa0 --- /dev/null +++ b/packages/react-components/lib/tasks/types/patrol.tsx @@ -0,0 +1,192 @@ +import DeleteIcon from '@mui/icons-material/Delete'; +import PlaceOutlined from '@mui/icons-material/PlaceOutlined'; +import { + Autocomplete, + Grid, + IconButton, + List, + ListItem, + ListItemIcon, + ListItemText, + TextField, + useMediaQuery, + useTheme, +} from '@mui/material'; +import React from 'react'; +import { PositiveIntField } from '../../form-inputs'; +import { TaskDefinition } from '../create-task'; +import type { TaskBookingLabel } from 'api-client'; + +export const PatrolTaskDefinition: TaskDefinition = { + taskDefinitionId: 'patrol', + taskDisplayName: 'Patrol', + requestCategory: 'patrol', +}; + +export interface PatrolTaskDescription { + places: string[]; + rounds: number; +} + +export function makePatrolTaskBookingLabel( + task_description: PatrolTaskDescription, +): TaskBookingLabel { + return { + description: { + task_definition_id: PatrolTaskDefinition.taskDefinitionId, + destination: task_description.places[task_description.places.length - 1], + }, + }; +} + +export const isPatrolTaskDescriptionValid = (taskDescription: PatrolTaskDescription): boolean => { + if (taskDescription.places.length === 0) { + return false; + } + for (const place of taskDescription.places) { + if (place.length === 0) { + return false; + } + } + return taskDescription.rounds > 0; +}; + +export function makeDefaultPatrolTaskDescription(): PatrolTaskDescription { + return { + places: [], + rounds: 1, + }; +} + +export function makePatrolTaskShortDescription( + desc: PatrolTaskDescription, + displayName?: string, +): string { + console.log(desc); + + const formattedPlaces = desc.places.map((place: string) => `[${place}]`); + return `[${displayName ?? PatrolTaskDefinition.taskDisplayName}] [${ + desc.rounds + }] round/s, along ${formattedPlaces.join(', ')}`; +} + +interface PlaceListProps { + places: string[]; + onClick(places_index: number): void; +} + +function PlaceList({ places, onClick }: PlaceListProps) { + const theme = useTheme(); + return ( + + {places.map((value, index) => ( + onClick(index)}> + + + } + > + + + + + + ))} + + ); +} + +interface PatrolTaskFormProps { + taskDesc: PatrolTaskDescription; + patrolWaypoints: string[]; + onChange(patrolTaskDescription: PatrolTaskDescription): void; + onValidate(valid: boolean): void; +} + +export function PatrolTaskForm({ + taskDesc, + patrolWaypoints, + onChange, + onValidate, +}: PatrolTaskFormProps): React.JSX.Element { + const theme = useTheme(); + const isScreenHeightLessThan800 = useMediaQuery('(max-height:800px)'); + const onInputChange = (desc: PatrolTaskDescription) => { + onValidate(isPatrolTaskDescriptionValid(desc)); + onChange(desc); + }; + onValidate(isPatrolTaskDescriptionValid(taskDesc)); + + return ( + + + + newValue !== null && + onInputChange({ + ...taskDesc, + places: taskDesc.places.concat(newValue).filter((el: string) => el), + }) + } + sx={{ + '& .MuiOutlinedInput-root': { + height: isScreenHeightLessThan800 ? '3rem' : '3.5rem', + fontSize: isScreenHeightLessThan800 ? 14 : 20, + }, + }} + renderInput={(params) => ( + + )} + /> + + + { + onInputChange({ + ...taskDesc, + rounds: val, + }); + }} + /> + + + + taskDesc.places.splice(places_index, 1) && + onInputChange({ + ...taskDesc, + }) + } + /> + + + ); +} diff --git a/packages/react-components/lib/tasks/types/utils.ts b/packages/react-components/lib/tasks/types/utils.ts new file mode 100644 index 000000000..d91285bf7 --- /dev/null +++ b/packages/react-components/lib/tasks/types/utils.ts @@ -0,0 +1,132 @@ +import { TaskRequest } from 'api-client'; +import { + PatrolTaskDefinition, + makePatrolTaskShortDescription, + makeDefaultPatrolTaskDescription, +} from './patrol'; +import { + DeliveryAreaPickupTaskDefinition, + DeliveryPickupTaskDefinition, + DeliverySequentialLotPickupTaskDefinition, + makeDeliveryPickupTaskShortDescription, + makeDeliveryCustomTaskShortDescription, + makeDefaultDeliveryCustomTaskDescription, + makeDefaultDeliveryPickupTaskDescription, +} from './delivery-custom'; +import { getTaskBookingLabelFromTaskRequest } from '../task-booking-label-utils'; +import { + ComposeCleanTaskDefinition, + makeComposeCleanTaskShortDescription, + makeDefaultComposeCleanTaskDescription, +} from './compose-clean'; +import { + DeliveryTaskDefinition, + makeDeliveryTaskShortDescription, + makeDefaultDeliveryTaskDescription, +} from './delivery'; +import { + CustomComposeTaskDefinition, + makeCustomComposeTaskShortDescription, +} from './custom-compose'; +import { TaskDefinition, TaskDescription } from '../create-task'; + +export function isNonEmptyString(value: string): boolean { + return value.length > 0; +} + +export function isPositiveNumber(value: number): boolean { + return value > 0; +} + +function rawStringFromJsonRequest(taskRequest: TaskRequest): string | undefined { + try { + const requestString = JSON.stringify(taskRequest); + console.error( + `Task does not have a identifying label, failed to generate short description of task: ${requestString}`, + ); + return requestString; + } catch (e) { + console.error( + `Failed to parse description of task of category: ${taskRequest.category}: ${ + (e as Error).message + }`, + ); + return undefined; + } +} + +export function getShortDescription( + taskRequest: TaskRequest, + taskDisplayName?: string, +): string | undefined { + const bookingLabel = getTaskBookingLabelFromTaskRequest(taskRequest); + if (!bookingLabel) { + return rawStringFromJsonRequest(taskRequest); + } + + const taskDefinitionId = bookingLabel.description.task_definition_id; + switch (taskDefinitionId) { + case PatrolTaskDefinition.taskDefinitionId: + return makePatrolTaskShortDescription(taskRequest.description, taskDisplayName); + case DeliveryTaskDefinition.taskDefinitionId: + return makeDeliveryTaskShortDescription(taskRequest.description, taskDisplayName); + case ComposeCleanTaskDefinition.taskDefinitionId: + return makeComposeCleanTaskShortDescription(taskRequest.description, taskDisplayName); + case DeliveryPickupTaskDefinition.taskDefinitionId: + return makeDeliveryPickupTaskShortDescription(taskRequest.description, taskDisplayName); + case DeliverySequentialLotPickupTaskDefinition.taskDefinitionId: + case DeliveryAreaPickupTaskDefinition.taskDefinitionId: + return makeDeliveryCustomTaskShortDescription(taskRequest.description, taskDisplayName); + case CustomComposeTaskDefinition.taskDefinitionId: + return makeCustomComposeTaskShortDescription(taskRequest.description); + default: + return `[Unknown] type "${taskRequest.description.category}"`; + } +} + +export function getDefaultTaskDefinition(taskDefinitionId: string): TaskDefinition | undefined { + switch (taskDefinitionId) { + case ComposeCleanTaskDefinition.taskDefinitionId: + return ComposeCleanTaskDefinition; + case DeliveryPickupTaskDefinition.taskDefinitionId: + return DeliveryPickupTaskDefinition; + case DeliverySequentialLotPickupTaskDefinition.taskDefinitionId: + return DeliverySequentialLotPickupTaskDefinition; + case DeliveryAreaPickupTaskDefinition.taskDefinitionId: + return DeliveryAreaPickupTaskDefinition; + case DeliveryTaskDefinition.taskDefinitionId: + return DeliveryTaskDefinition; + case PatrolTaskDefinition.taskDefinitionId: + return PatrolTaskDefinition; + case CustomComposeTaskDefinition.taskDefinitionId: + return CustomComposeTaskDefinition; + } + return undefined; +} + +export function getDefaultTaskDescription( + taskDefinitionId: string, +): TaskDescription | string | undefined { + switch (taskDefinitionId) { + case ComposeCleanTaskDefinition.taskDefinitionId: + return makeDefaultComposeCleanTaskDescription(); + case DeliveryPickupTaskDefinition.taskDefinitionId: + return makeDefaultDeliveryPickupTaskDescription(); + case DeliverySequentialLotPickupTaskDefinition.taskDefinitionId: + case DeliveryAreaPickupTaskDefinition.taskDefinitionId: + return makeDefaultDeliveryCustomTaskDescription(taskDefinitionId); + case DeliveryTaskDefinition.taskDefinitionId: + return makeDefaultDeliveryTaskDescription(); + case PatrolTaskDefinition.taskDefinitionId: + return makeDefaultPatrolTaskDescription(); + case CustomComposeTaskDefinition.taskDefinitionId: + return ''; + default: + return undefined; + } +} + +export function getTaskRequestCategory(taskDefinitionId: string): string | undefined { + const definition = getDefaultTaskDefinition(taskDefinitionId); + return definition !== undefined ? definition.requestCategory : undefined; +}