Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

121 dodal integration #171

Merged
merged 24 commits into from
May 15, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
8d933a6
Add dodal git dependency
abbiemery Apr 19, 2023
a5c1f82
Add initial dodal device loading to context
abbiemery Apr 26, 2023
1ab4c12
Allow only dodal style device modules
abbiemery Apr 27, 2023
d450234
Allow list of device and plan sources
abbiemery Apr 27, 2023
2183e3c
Enable context to search multiple modules for devices and plans
abbiemery Apr 27, 2023
ffd3e3b
Update to use dodal function format
abbiemery May 2, 2023
8b5e205
Add checking when adding devices to context
abbiemery May 4, 2023
50d0c75
Update p45 config to use dodal
abbiemery May 4, 2023
913990b
Update adsim.yaml to use multiple sources
abbiemery May 4, 2023
49a0e55
Update worker config for multiple sources
abbiemery May 4, 2023
db00d28
Add module loading tests for context
abbiemery May 5, 2023
ae2d1db
Correct ignore pre-commit comment
abbiemery May 5, 2023
1ada7d9
Make module name qualification explicit
abbiemery May 5, 2023
ea128e3
Swap typedict for basemodel in config
abbiemery May 5, 2023
6371306
Test device functions with dependencies
abbiemery May 5, 2023
034851d
Make source type an enum
abbiemery May 11, 2023
8ea3759
Allocate device and plan adding based on source kind
abbiemery May 11, 2023
8c984d4
Change source type to kind
abbiemery May 11, 2023
9103513
Remove unused imports
abbiemery May 11, 2023
ad6aecd
Make test context use default environment config
abbiemery May 11, 2023
adac983
Update source format
abbiemery May 11, 2023
5f069f8
Update enum case to abide by pep8
abbiemery May 12, 2023
7b03b80
Update dodal dependency format
abbiemery May 15, 2023
c4ef315
Exclude dodal from lockfile
abbiemery May 15, 2023
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
3 changes: 1 addition & 2 deletions .github/actions/install_requirements/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ runs:
- name: Create lockfile
run: |
mkdir -p lockfiles
pip freeze --exclude-editable > lockfiles/${{ inputs.requirements_file }}
pip freeze --exclude-editable --exclude dodal > lockfiles/${{ inputs.requirements_file }}
# delete the self referencing line and make sure it isn't blank
sed -i '/file:/d' lockfiles/${{ inputs.requirements_file }}
shell: bash
Expand All @@ -55,4 +55,3 @@ runs:
fi
fi
shell: bash

6 changes: 5 additions & 1 deletion config/adsim.yaml
Original file line number Diff line number Diff line change
@@ -1,2 +1,6 @@
env:
startupScript: blueapi.startup.adsim
sources:
- kind: deviceFunctions
module: blueapi.startup.adsim
- kind: planFunctions
module: blueapi.startup.adsim
6 changes: 5 additions & 1 deletion config/bl45p.yaml
Original file line number Diff line number Diff line change
@@ -1,2 +1,6 @@
env:
startupScript: blueapi.startup.bl45p
sources:
- kind: dodal
module: dodal.p45
- kind: planFunctions
module: blueapi.plans
6 changes: 5 additions & 1 deletion config/defaults.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
env:
startupScript: blueapi.startup.example
sources:
- kind: deviceFunctions
module: blueapi.startup.example
- kind: planFunctions
module: blueapi.plans
stomp:
host: localhost
port: 61613
Expand Down
6 changes: 5 additions & 1 deletion helm/blueapi/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,11 @@ affinity: {}

worker:
env:
startupScript: blueapi.startup.example
sources:
- kind: deviceFunctions
module: blueapi.startup.example
- kind: planFunctions
module: blueapi.plans
stomp:
host: activemq
port: 61613
Expand Down
3 changes: 3 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ dependencies = [
"fastapi[all]",
"uvicorn",
"requests",
"dodal @ git+https://github.com/DiamondLightSource/dodal.git",

]
dynamic = ["version"]
license.file = "LICENSE"
Expand Down Expand Up @@ -69,6 +71,7 @@ write_to = "src/blueapi/_version.py"

[tool.mypy]
ignore_missing_imports = true # Ignore missing stubs in imported modules
namespace_packages = false # rely only on __init__ files to determine fully qualified module names.

[tool.isort]
float_to_top = true
Expand Down
21 changes: 18 additions & 3 deletions src/blueapi/config.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from enum import Enum
from pathlib import Path
from typing import Any, Generic, Literal, Mapping, Type, TypeVar, Union

Expand All @@ -9,7 +10,18 @@
LogLevel = Literal["NOTSET", "DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"]


class StompConfig(BlueapiBaseModel):
class SourceKind(str, Enum):
PLAN_FUNCTIONS = "planFunctions"
DEVICE_FUNCTIONS = "deviceFunctions"
DODAL = "dodal"


class Source(BaseModel):
kind: SourceKind
module: Union[Path, str]


class StompConfig(BaseModel):
"""
Config for connecting to stomp broker
"""
Expand All @@ -23,11 +35,14 @@ class EnvironmentConfig(BlueapiBaseModel):
Config for the RunEngine environment
"""

startup_script: Union[Path, str] = "blueapi.startup.example"
sources: list[Source] = [
callumforrester marked this conversation as resolved.
Show resolved Hide resolved
Source(kind=SourceKind.DEVICE_FUNCTIONS, module="blueapi.startup.example"),
Source(kind=SourceKind.PLAN_FUNCTIONS, module="blueapi.plans"),
]

def __eq__(self, other: object) -> bool:
if isinstance(other, EnvironmentConfig):
return str(self.startup_script) == str(other.startup_script)
return str(self.sources) == str(other.sources)
return False


Expand Down
27 changes: 17 additions & 10 deletions src/blueapi/core/context.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
from dataclasses import dataclass, field
from importlib import import_module
from inspect import Parameter, signature
from pathlib import Path
from types import ModuleType
from typing import (
Any,
Expand All @@ -21,6 +20,7 @@
from bluesky import RunEngine
from pydantic import create_model

from blueapi.config import EnvironmentConfig, SourceKind
from blueapi.utils import BlueapiPlanModelConfig, load_module_all

from .bluesky_types import (
Expand Down Expand Up @@ -70,13 +70,16 @@ def find_device(self, addr: Union[str, List[str]]) -> Optional[Device]:
else:
return find_component(self.devices, addr)

def with_startup_script(self, path: Union[Path, str]) -> None:
mod = import_module(str(path))
self.with_module(mod)
def with_config(self, config: EnvironmentConfig) -> None:
for source in config.sources:
mod = import_module(str(source.module))

def with_module(self, module: ModuleType) -> None:
self.with_plan_module(module)
self.with_device_module(module)
if source.kind is SourceKind.PLAN_FUNCTIONS:
self.with_plan_module(mod)
elif source.kind is SourceKind.DEVICE_FUNCTIONS:
self.with_device_module(mod)
elif source.kind is SourceKind.DODAL:
self.with_dodal_module(mod)

def with_plan_module(self, module: ModuleType) -> None:
"""
Expand All @@ -103,9 +106,13 @@ def plan_2(...):
self.plan(obj)

def with_device_module(self, module: ModuleType) -> None:
for obj in load_module_all(module):
if is_bluesky_compatible_device(obj):
self.device(obj)
self.with_dodal_module(module)

def with_dodal_module(self, module: ModuleType) -> None:
from dodal.utils import make_all_devices

for device in make_all_devices(module).values():
self.device(device)

def plan(self, plan: PlanGenerator) -> PlanGenerator:
"""
Expand Down
2 changes: 1 addition & 1 deletion src/blueapi/service/handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ def __init__(self, config: Optional[ApplicationConfig] = None) -> None:

logging.basicConfig(level=self.config.logging.level)

self.context.with_startup_script(self.config.env.startup_script)
self.context.with_config(self.config.env)

self.worker = RunEngineWorker(self.context)
self.message_bus = StompMessagingTemplate.autoconfigured(self.config.stomp)
Expand Down
113 changes: 0 additions & 113 deletions src/blueapi/startup/bl45p.py

This file was deleted.

98 changes: 67 additions & 31 deletions src/blueapi/startup/example.py
Original file line number Diff line number Diff line change
@@ -1,38 +1,74 @@
from ophyd.sim import Syn2DGauss, SynGauss, SynSignal

from blueapi.plans import * # noqa: F401, F403

from .simmotor import BrokenSynAxis, SynAxisWithMotionEvents

x = SynAxisWithMotionEvents(name="x", delay=1.0, events_per_move=8)
y = SynAxisWithMotionEvents(name="y", delay=3.0, events_per_move=24)
z = SynAxisWithMotionEvents(name="z", delay=2.0, events_per_move=16)
theta = SynAxisWithMotionEvents(
name="theta", delay=0.2, events_per_move=12, egu="degrees"
)
x_err = BrokenSynAxis(name="x_err", timeout=1.0)
sample_pressure = SynAxisWithMotionEvents(
name="sample_pressure", delay=30.0, events_per_move=128, egu="MPa", value=0.101
)
sample_temperature = SynSignal(
func=lambda: ((x.position + y.position + z.position) / 1000.0) + 20.0,

def x(name="x") -> SynAxisWithMotionEvents:
abbiemery marked this conversation as resolved.
Show resolved Hide resolved
return SynAxisWithMotionEvents(name=name, delay=1.0, events_per_move=8)


def y(name="y") -> SynAxisWithMotionEvents:
return SynAxisWithMotionEvents(name=name, delay=3.0, events_per_move=24)


def z(name="z") -> SynAxisWithMotionEvents:
return SynAxisWithMotionEvents(name=name, delay=2.0, events_per_move=16)


def theta(name="theta") -> SynAxisWithMotionEvents:
return SynAxisWithMotionEvents(
name=name, delay=0.2, events_per_move=12, egu="degrees"
)


def x_err(name="x_err") -> BrokenSynAxis:
return BrokenSynAxis(name=name, timeout=1.0)


def sample_pressure(name="sample_pressure") -> SynAxisWithMotionEvents:
return SynAxisWithMotionEvents(
name=name, delay=30.0, events_per_move=128, egu="MPa", value=0.101
)


def sample_temperature(
x: SynAxisWithMotionEvents,
y: SynAxisWithMotionEvents,
z: SynAxisWithMotionEvents,
name="sample_temperature",
)
image_det = Syn2DGauss(
) -> SynSignal:
return SynSignal(
func=lambda: ((x.position + y.position + z.position) / 1000.0) + 20.0,
name=name,
)


def image_det(
x: SynAxisWithMotionEvents,
y: SynAxisWithMotionEvents,
name="image_det",
motor0=x,
motor_field0="x",
motor1=y,
motor_field1="y",
center=(0, 0),
Imax=1,
labels={"detectors"},
)
current_det = SynGauss(
) -> Syn2DGauss:
return Syn2DGauss(
name=name,
motor0=x,
motor_field0="x",
motor1=y,
motor_field1="y",
center=(0, 0),
Imax=1,
labels={"detectors"},
)


def current_det(
x: SynAxisWithMotionEvents,
name="current_det",
motor=x,
motor_field="x",
center=0.0,
Imax=1,
labels={"detectors"},
)
) -> SynGauss:
return SynGauss(
name=name,
motor=x,
motor_field="x",
center=0.0,
Imax=1,
labels={"detectors"},
)
Empty file added tests/__init__.py
Empty file.
Loading