Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions docs/explanations/converters.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Converters

The queue contains ulims experiment definitions, which need to be converted to blueapi calls for anything to run. This conversion is beamline specific and configurable. The default converter simply passes the parameters of the experiment definition directly to blueapi with no conversion taking place.
The queue contains ulims experiments and/or TaskRequests. While TaskRequests are already in the right format for blueapi to receive them, experiments need to be converted to blueapi calls for anything to run. This conversion is beamline specific and configurable. The default converter simply passes the parameters of the experiment definition directly to blueapi with no conversion taking place.

Converters take the list of queued tasks, the queue history, and the blueapi call history (previous calls made to blueapi) and use this information to construct a list of future blueapi calls. Not all of this information needs to be used by the converter (for example, the default converter only uses the list of queued tasks to inform its 'conversion') but the interface of a converter function needs to be as follows:

Expand All @@ -13,7 +13,9 @@ def my_converter(
...
```

Conversion is run any time the queue changes and right before a call is made to blueapi, so the blueapi call list stays up to date. This means converters can make use of current information on a beamline such as PV values to inform the conversion.
Conversion is run any time the queue changes and right before a call is made to blueapi, so the blueapi call list stays in sync. This means converters can make use of current information on a beamline such as PV values to inform the conversion.

Note that since the queue can contain TaskRequest objects, a converter should be able to handle this. It should almost always pass them through with no conversion

In config, under the converter section, add the path and name of the converter you want to use. For example:

Expand Down
10 changes: 0 additions & 10 deletions docs/explanations/queue.md

This file was deleted.

7 changes: 7 additions & 0 deletions docs/explanations/queue_items.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Queue Items

The items inside the queue contain either an `Experiment` object or a `TaskRequest` object. These determine which bluesky plans will run. Experiment objects match the format of an experiment in ulims, and can involve multiple bluesky plans. It always contains a sample, experiment definition and instrument session. A TaskRequest object matches the format blueapi expects to receive to run a single plan.

Allowing both in the queue means you can queue up whole experiments as well as individual bluesky plans.

The queue will convert the entire list of experiments and task requests to a list of blueapi calls (the task request objects are already in the right format). See [converters](./converters.md) on how this is done.
14 changes: 0 additions & 14 deletions docs/explanations/tasks.md

This file was deleted.

112 changes: 92 additions & 20 deletions docs/reference/rest_api.json
Original file line number Diff line number Diff line change
Expand Up @@ -186,9 +186,16 @@
"schema": {
"type": "array",
"items": {
"$ref": "#/components/schemas/ExperimentDefinition"
"anyOf": [
{
"$ref": "#/components/schemas/TaskRequest"
},
{
"$ref": "#/components/schemas/Experiment"
}
]
},
"title": "Experiment Definitions"
"title": "Experiments"
}
}
}
Expand Down Expand Up @@ -1001,32 +1008,53 @@
"title": "EnvironmentConfig",
"description": "Config for the RunEngine environment"
},
"Experiment": {
"properties": {
"name": {
"type": "string",
"title": "Name"
},
"instrument_session": {
"type": "string",
"title": "Instrument Session"
},
"sample": {
"$ref": "#/components/schemas/Sample"
},
"experiment_definition": {
"$ref": "#/components/schemas/ExperimentDefinition"
}
},
"type": "object",
"required": [
"name",
"instrument_session",
"sample",
"experiment_definition"
],
"title": "Experiment"
},
"ExperimentDefinition": {
"properties": {
"plan_name": {
"name": {
"type": "string",
"title": "Plan Name"
"title": "Name"
},
"sample_id": {
"id": {
"type": "string",
"title": "Sample Id"
"title": "Id"
},
"params": {
"data": {
"additionalProperties": true,
"type": "object",
"title": "Params",
"description": "Values for parameters to plan, if any"
},
"instrument_session": {
"type": "string",
"title": "Instrument Session"
"title": "Data"
}
},
"type": "object",
"required": [
"plan_name",
"sample_id",
"instrument_session"
"name",
"id",
"data"
],
"title": "ExperimentDefinition"
},
Expand Down Expand Up @@ -1232,6 +1260,30 @@
"type": "object",
"title": "RestConfig"
},
"Sample": {
"properties": {
"name": {
"type": "string",
"title": "Name"
},
"id": {
"type": "string",
"title": "Id"
},
"data": {
"additionalProperties": true,
"type": "object",
"title": "Data"
}
},
"type": "object",
"required": [
"name",
"id",
"data"
],
"title": "Sample"
},
"ScratchConfig": {
"properties": {
"root": {
Expand Down Expand Up @@ -1396,6 +1448,14 @@
"title": "TaskError",
"description": "Wrapper around an exception raised by a plan"
},
"TaskKind": {
"type": "string",
"enum": [
"Experiment",
"Plan"
],
"title": "TaskKind"
},
"TaskRequest": {
"properties": {
"name": {
Expand Down Expand Up @@ -1450,8 +1510,16 @@
},
"TaskWithPosition": {
"properties": {
"experiment_definition": {
"$ref": "#/components/schemas/ExperimentDefinition"
"experiment": {
"anyOf": [
{
"$ref": "#/components/schemas/Experiment"
},
{
"$ref": "#/components/schemas/TaskRequest"
}
],
"title": "Experiment"
},
"id": {
"type": "string",
Expand All @@ -1477,15 +1545,19 @@
}
],
"title": "Position"
},
"kind": {
"$ref": "#/components/schemas/TaskKind"
}
},
"type": "object",
"required": [
"experiment_definition",
"experiment",
"id",
"status",
"blueapi_calls",
"position"
"position",
"kind"
],
"title": "TaskWithPosition"
},
Expand Down
10 changes: 4 additions & 6 deletions src/daq_queuing_service/api/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,15 @@
import json
from collections.abc import AsyncGenerator

from blueapi.service.model import TaskRequest
from fastapi import APIRouter, Request, Response
from fastapi.responses import EventSourceResponse
from pydantic import BaseModel

from daq_queuing_service.app._config import AppConfig
from daq_queuing_service.blueapi_interaction.blueapi_call import BlueapiCallResponse
from daq_queuing_service.broadcaster import Broadcaster
from daq_queuing_service.task import ExperimentDefinition, Status, Task
from daq_queuing_service.task import Experiment, Status, Task
from daq_queuing_service.task_queue.queue import (
QUEUE_EVENTS,
QueueState,
Expand Down Expand Up @@ -72,13 +73,10 @@ async def get_queued_tasks(status: Status | None = None) -> list[TaskWithPositio

@router.post("/queue")
async def add_tasks_to_queue(
experiment_definitions: list[ExperimentDefinition],
experiments: list[TaskRequest | Experiment],
position: int | None = None,
) -> list[str]:
tasks = [
Task(experiment_definition=experiment_definition)
for experiment_definition in experiment_definitions
]
tasks = [Task(experiment=experiment) for experiment in experiments]
task_ids = [task.id for task in tasks]
await queue.add_tasks(tasks, position)
return task_ids
Expand Down
20 changes: 12 additions & 8 deletions src/daq_queuing_service/plugins/construct_task_request.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,21 @@
from blueapi.service.model import TaskRequest

from daq_queuing_service.blueapi_interaction.blueapi_call import BlueapiCall
from daq_queuing_service.task import ExperimentDefinition, TaskWithPosition
from daq_queuing_service.task import Experiment, TaskWithPosition


def _construct_blueapi_task_request(
experiment_definition: ExperimentDefinition,
experiment: Experiment | TaskRequest,
) -> TaskRequest:
return TaskRequest(
name=experiment_definition.plan_name,
params=experiment_definition.params,
instrument_session=experiment_definition.instrument_session,
)
match experiment:
case TaskRequest():
return experiment
case Experiment():
return TaskRequest(
instrument_session=experiment.instrument_session,
name=experiment.experiment_definition.name,
params=experiment.experiment_definition.data,
)


def construct_blueapi_call_list(
Expand All @@ -23,7 +27,7 @@ def construct_blueapi_call_list(
call_list = [
BlueapiCall(
parent_task_id=task.id,
task_request=_construct_blueapi_task_request(task.experiment_definition),
task_request=_construct_blueapi_task_request(task.experiment),
)
for task in queue
]
Expand Down
36 changes: 20 additions & 16 deletions src/daq_queuing_service/plugins/i15_1_converter.py
Original file line number Diff line number Diff line change
@@ -1,31 +1,31 @@
from blueapi.service.model import TaskRequest

from daq_queuing_service.blueapi_interaction.blueapi_call import BlueapiCall
from daq_queuing_service.task import ExperimentDefinition, TaskWithPosition
from daq_queuing_service.task import Experiment, TaskWithPosition


def construct_blueapi_tasks_from_i15_1_experiment(
experiment_definition: ExperimentDefinition,
experiment: Experiment,
) -> list[TaskRequest]:
sample_name = experiment_definition.params["sampleName"]
sample_name = experiment.sample.name
# Assume sample name is of form test_8_1 to load from position 8 on puck 1
_, position, puck = sample_name.split("_")

return [
TaskRequest(
name="robot_load",
params={"puck": puck, "position": position},
instrument_session=experiment_definition.instrument_session,
instrument_session=experiment.instrument_session,
),
TaskRequest(
name="centre_sample",
params={"start_z": -5, "end_z": 5, "steps": 20, "exposure_time": 0.01},
instrument_session=experiment_definition.instrument_session,
instrument_session=experiment.instrument_session,
),
TaskRequest(
name="robot_unload",
params={},
instrument_session=experiment_definition.instrument_session,
instrument_session=experiment.instrument_session,
),
]

Expand All @@ -39,15 +39,19 @@ def construct_i15_1_blueapi_call_list(
call_list: list[BlueapiCall] = []

for task in queue:
# Remove plan name from here and UI in https://github.com/DiamondLightSource/daq-queuing-service/issues/51
if task.experiment_definition.plan_name == "run_full_collection":
call_list.extend(
[
BlueapiCall(task_request=blueapi_task, parent_task_id=task.id)
for blueapi_task in construct_blueapi_tasks_from_i15_1_experiment(
task.experiment_definition
)
]
)
match task.experiment:
case TaskRequest():
call_list.append(
BlueapiCall(task_request=task.experiment, parent_task_id=task.id)
)
case Experiment():
call_list.extend(
[
BlueapiCall(task_request=b_api_task, parent_task_id=task.id)
for b_api_task in construct_blueapi_tasks_from_i15_1_experiment(
task.experiment
)
]
)

return call_list
Loading
Loading