Skip to content

Commit

Permalink
Revert "removed soft ico (#57)" (#60)
Browse files Browse the repository at this point in the history
This reverts commit 3202e1b.
  • Loading branch information
Relm-Arrowny authored Oct 25, 2024
1 parent c8fe835 commit 5c162e4
Show file tree
Hide file tree
Showing 4 changed files with 279 additions and 0 deletions.
62 changes: 62 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import asyncio
import subprocess
import sys
import time
from collections.abc import Callable
from pathlib import Path
from typing import Any
Expand Down Expand Up @@ -50,6 +53,42 @@ def clean_event_loop():
return RE


@pytest.fixture(scope="module", params=["pva"])
def pva():
processes = [
subprocess.Popen(
[
sys.executable,
"-m",
"epicscorelibs.ioc",
"-m",
macros,
"-d",
RECORD,
],
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
universal_newlines=True,
)
for macros in [
"INCLUDE_EXTRA_BLOCK=,INCLUDE_EXTRA_SIGNAL=",
"EXCLUDE_WIDTH=#,IOC_NAME=PANDAQSRVIB",
"EXCLUDE_PCAP=#,IOC_NAME=PANDAQSRVI",
]
]
time.sleep(2)

for p in processes:
assert not p.poll() # , p.stdout.read()

yield processes

for p in processes:
p.terminate()
p.communicate()


@pytest.fixture
async def normal_coroutine() -> Callable[[], Any]:
async def inner_coroutine():
Expand All @@ -67,6 +106,29 @@ async def inner_coroutine():
return inner_coroutine


A_BIT = 0.5


@pytest.fixture(scope="session")
async def fake_99():
p = subprocess.Popen(
[
"python",
"tests/epics/soft_ioc/p99_softioc.py",
],
)

# Give the server time to start
await asyncio.sleep(A_BIT)
# Check it started successfully
print("setup")
yield p
p.terminate()
# Shut it down at the
p.wait()
await asyncio.sleep(A_BIT)


@pytest.fixture(scope="session")
def xyz_motor(fake_99):
with DeviceCollector(mock=False):
Expand Down
56 changes: 56 additions & 0 deletions tests/epics/soft_ioc/p99_softioc.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import asyncio

from softioc import asyncio_dispatcher, builder, softioc
from softsignal import soft_mbb, soft_motor, soft_signal


async def p99_fake() -> None:
async def _delay_move(signal, v, vel, dmov):
diff = signal.get() - v.get()
if abs(diff) < vel.get() * 0.04:
signal.set(v.get())
dmov.set(True)

elif diff < 0:
dmov.set(False)
signal.set(signal.get() + vel.get() * 0.01)
elif diff > 0:
dmov.set(False)
signal.set(signal.get() - vel.get() * 0.01)

# Sample AngleStage softioc
dispatcher = asyncio_dispatcher.AsyncioDispatcher()
soft_signal("p99-MO-TABLE-01", "WRITETHETA", "WRITETHETA:RBV")
soft_signal("p99-MO-TABLE-01", "WRITEROLL", "WRITEROLL:RBV")
soft_signal("p99-MO-TABLE-01", "WRITEPITCH", "WRITEPITCH:RBV")
# sample selection staged
soft_mbb("p99-MO-STAGE-02", "MP:SELECT")
# xyz stage
x_set, x_vel, x_rbv, x_dmov = await soft_motor(
prefix="p99-MO-STAGE-02", name="X", unit="mm"
)
y_set, y_vel, y_rbv, y_dmov = await soft_motor(
prefix="p99-MO-STAGE-02", name="Y", unit="mm"
)
z_set, z_vel, z_rbv, z_dmov = await soft_motor(
prefix="p99-MO-STAGE-02", name="Z", unit="mm"
)
# build the ioc
builder.LoadDatabase()
softioc.iocInit(dispatcher)

# print(softioc.dbnr(), softioc.dbl()) # type: ignore

async def update(y_rbv, y_set, y_vel, y_dmov):
await _delay_move(y_rbv, y_set, y_vel, y_dmov)

while True:
dispatcher(update, [z_rbv, z_set, z_vel, z_dmov])
dispatcher(update, [y_rbv, y_set, y_vel, y_dmov])
dispatcher(update, [x_rbv, x_set, x_vel, x_dmov])
await asyncio.sleep(0.01)
# softioc.interactive_ioc(globals())


if __name__ == "__main__":
asyncio.run(p99_fake())
94 changes: 94 additions & 0 deletions tests/epics/soft_ioc/softsignal.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
from softioc import builder


def soft_signal(prefix: str, input_name: str, readback_name: str) -> None:
# Create some records
builder.SetDeviceName(prefix)
rbv = builder.aIn(readback_name, initial_value=0)
# rbv.append(temp)
builder.aOut(
input_name,
initial_value=0.1,
always_update=True,
on_update=lambda v: rbv.set(v),
)


def soft_mbb(prefix: str, name: str, *option):
builder.SetDeviceName(prefix)
# temp = builder.mbbIn(readback_name, initial_value=0)
builder.mbbOut(
name,
"Empty",
"Mn 5um",
"Fe (empty)",
"Co 5um",
"Ni 5um",
"Cu 5um",
"Zn 5um",
"Zr (empty)",
"Mo (empty)",
"Rh (empty)",
"Pd (empty)",
"Ag (empty)",
"Cd 25um",
"W (empty)",
"Pt (empty)",
"User",
)


async def soft_motor(prefix: str, name: str, unit: str = "mm"):
builder.SetDeviceName(prefix)
builder.aOut(
name,
initial_value=1.1,
EGU=unit,
VAL=1.1,
PREC=0,
)
rbv = builder.aOut(
name + "RBV",
initial_value=0.0,
)
vel = builder.aOut(
name + "VELO",
initial_value=1000,
)
dmov = builder.boolOut(
name + "DMOV",
initial_value=True,
)
ai = builder.aOut(
name + "VAL",
initial_value=0.0,
always_update=True,
on_update=lambda v: dmov.set(False),
)

builder.aOut(
name + "VMAX",
initial_value=200,
)
builder.aOut(
name + "ACCL",
initial_value=0.01,
)
builder.aOut(
name + "RDBD",
initial_value=0.1,
)

builder.aOut(
name + "LLM",
initial_value=-100,
)
builder.aOut(
name + "HLM",
initial_value=100,
)
builder.aOut(
name + "STOP",
initial_value=0,
)
return ai, vel, rbv, dmov
67 changes: 67 additions & 0 deletions tests/test_p99_stages_softioc.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import asyncio
from collections import defaultdict

from bluesky.plans import scan
from bluesky.run_engine import RunEngine
from ophyd_async.core import DeviceCollector, assert_emitted

from p99_bluesky.devices.p99.sample_stage import (
FilterMotor,
SampleAngleStage,
p99StageSelections,
)

# Long enough for multiple asyncio event loop cycles to run so
# all the tasks have a chance to run
A_BIT = 0.5


async def test_fake_p99(RE: RunEngine, xyz_motor) -> None:
docs = defaultdict(list)

def capture_emitted(name, doc):
docs[name].append(doc)

await asyncio.sleep(A_BIT)
with DeviceCollector(mock=False):
mock_sampleAngleStage = SampleAngleStage(
"p99-MO-TABLE-01:", name="mock_sampleAngleStage"
)
mock_filter_wheel = FilterMotor(
"p99-MO-STAGE-02:MP:SELECT", name="mock_filter_wheel"
)

assert mock_sampleAngleStage.roll.name == "mock_sampleAngleStage-roll"
assert mock_sampleAngleStage.pitch.name == "mock_sampleAngleStage-pitch"
assert mock_filter_wheel.user_setpoint.name == "mock_filter_wheel-user_setpoint"

await asyncio.gather(
mock_sampleAngleStage.theta.set(2),
mock_sampleAngleStage.pitch.set(3.1),
mock_sampleAngleStage.roll.set(4),
mock_filter_wheel.user_setpoint.set(p99StageSelections.Cd25um),
)
await asyncio.sleep(A_BIT)
result = asyncio.gather(
mock_sampleAngleStage.theta.get_value(),
mock_sampleAngleStage.pitch.get_value(),
mock_sampleAngleStage.roll.get_value(),
mock_filter_wheel.user_setpoint.get_value(),
)
await asyncio.wait_for(result, timeout=2)
assert result.result() == [2.0, 3.1, 4.0, p99StageSelections.Cd25um]

RE(
scan(
[mock_sampleAngleStage.theta],
xyz_motor.z,
-3,
3,
10,
),
[
capture_emitted,
],
)
assert_emitted(docs, start=1, descriptor=1, event=10, stop=1)
await asyncio.sleep(A_BIT)

0 comments on commit 5c162e4

Please sign in to comment.