Skip to content

Commit

Permalink
Add append support to pmac
Browse files Browse the repository at this point in the history
  • Loading branch information
coretl committed Nov 1, 2016
1 parent 193b897 commit a094230
Show file tree
Hide file tree
Showing 4 changed files with 130 additions and 42 deletions.
20 changes: 20 additions & 0 deletions malcolm/blocks/pmac/PMACTrajectory.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,19 @@
rbv: $(prefix):ProfileExecuteMessage_RBV
widget: textinput

- parts.ca.CAActionPart:
name: appendProfile
description: command to append the profile once built
pv: $(prefix):ProfileAppend
statusPv: $(prefix):ProfileAppendStatus_RBV
goodStatus: Success

- parts.ca.CAStringPart:
name: appendMessage
description: message of last profile append
rbv: $(prefix):ProfileAppendMessage_RBV
widget: textinput

- parts.ca.CALongPart:
name: scanPercentage
description: percentage complete on current scan
Expand Down Expand Up @@ -84,6 +97,13 @@
rbvSuff: _RBV
widget: textinput

- parts.ca.CALongPart:
name: pointsToBuild
description: Number of points to build or append
pv: $(prefix):ProfilePointsToBuild
rbvSuff: _RBV
widget: textinput

- parts.ca.CAChoicePart:
name: useA
description: is this axis is used in the trajectory scan? yes/no
Expand Down
6 changes: 3 additions & 3 deletions malcolm/parts/ADCore/positionlabellerpart.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,13 @@
class PositionLabellerPart(ChildPart):
# Stored generator for positions
generator = None
# Next position we need to generate
# The last index we have loaded
end_index = 0
# Where we should stop loading points
steps_up_to = 0
# Future for plugin run
start_future = None
# If we are currently loading
# If we are currently loading then block loading more points
loading = False

def _make_xml(self, start_index):
Expand Down Expand Up @@ -98,7 +98,7 @@ def run(self, task, update_completed_steps):

def load_more_positions(self, number_left, task):
if not self.loading and number_left < POSITIONS_PER_XML and \
self.end_index < self.steps_up_to:
self.end_index < self.steps_up_to:
self.loading = True
xml, self.end_index = self._make_xml(self.end_index)
task.put(self.child["xml"], xml)
Expand Down
102 changes: 69 additions & 33 deletions malcolm/parts/pmac/pmactrajectorypart.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@
TRIG_LIVE_FRAME = 3 # Capture 0, Frame 1, Detector 1
TRIG_ZERO = 8 # Capture 0, Frame 0, Detector 0

# How many generator points to load each time
POINTS_PER_BUILD = 1000

# All possible PMAC CS axis assignment
cs_axis_names = list("ABCUVWXYZ")

Expand All @@ -48,10 +51,20 @@ def __init__(self, cs_axis, cs_port, acceleration_time, resolution, offset,


class PMACTrajectoryPart(ChildPart):
# Stored between functions
# Axis information stored from validate
# {scannable_name: MotorInfo}
axis_mapping = None
# CS asyn port name to do trajectory on
cs_port = None
# Lookup of the completed_step value for each point
completed_steps_lookup = []
# If we are currently loading then block loading more points
loading = False
# The last index we have loaded
end_index = 0
# Where we should stop loading points
steps_up_to = 0
# Stored generator for positions
generator = None

@RunnableController.Reset
Expand Down Expand Up @@ -98,17 +111,21 @@ def configure(self, task, completed_steps, steps_to_do, part_info, params):
self.cs_port, self.axis_mapping = self.validate(
task, completed_steps, steps_to_do, part_info, params)
futures = self.move_to_start(task, completed_steps)
self.completed_steps_lookup, profile = self.build_generator_profile(
completed_steps, steps_to_do)
self.steps_up_to = completed_steps + steps_to_do
self.completed_steps_lookup = []
profile = self.build_generator_profile(completed_steps)
task.wait_all(futures)
self.build_profile(task, **profile)
self.write_profile_points(task, **profile)
# Max size of array
task.put(self.child["numPoints"], self.generator.num * 3)
task.post(self.child["buildProfile"])

@RunnableController.Run
@RunnableController.Resume
def run(self, task, update_completed_steps):
task.subscribe(
self.child["pointsScanned"], self.update_step,
update_completed_steps)
self.loading = False
task.subscribe(self.child["pointsScanned"], self.update_step,
update_completed_steps, task)
task.post(self.child["executeProfile"])

@RunnableController.Abort
Expand All @@ -130,10 +147,18 @@ def get_move_time(self, axis_name, demand, current=None):
time = accl_time + full_speed_dist / motor_info.max_velocity
return time

def update_step(self, scanned, update_completed_steps):
def update_step(self, scanned, update_completed_steps, task):
if scanned > 0:
completed_steps = self.completed_steps_lookup[scanned - 1]
update_completed_steps(completed_steps, self)
if not self.loading and self.end_index < self.steps_up_to and \
self.end_index - completed_steps < POINTS_PER_BUILD:
self.loading = True
profile = self.build_generator_profile(
self.end_index, do_run_up=False)
self.write_profile_points(task, **profile)
task.post(self.child["appendProfile"])
self.loading = False

def run_up_positions(self, point):
"""Generate a dict of axis run up distances given a time
Expand Down Expand Up @@ -194,13 +219,14 @@ def move_to_start(self, task, start_index):
velocity_mode = [ZERO_VELOCITY]
user_programs = [NO_PROGRAM]

self.build_profile(task, time_array, velocity_mode, trajectory,
user_programs)
self.write_profile_points(task, time_array, velocity_mode, trajectory,
user_programs)
task.post(self.child["buildProfile"])
futures = task.post_async(self.child["executeProfile"])
return futures

def build_profile(self, task, time_array, velocity_mode, trajectory,
user_programs):
def write_profile_points(self, task, time_array, velocity_mode, trajectory,
user_programs):
"""Build profile using part_tasks
Args:
Expand Down Expand Up @@ -270,25 +296,34 @@ def build_profile(self, task, time_array, velocity_mode, trajectory,
timeArray=time_array_ticks,
velocityMode=velocity_mode,
userPrograms=user_programs,
numPoints=len(time_array)
pointsToBuild=len(time_array)
)
for axis_name in trajectory:
motor_info = self.axis_mapping[axis_name]
cs_axis = motor_info.cs_axis
attr_dict["positions%s" % cs_axis] = trajectory[axis_name]
task.put_many(self.child, attr_dict)
task.post(self.child["buildProfile"])

def build_generator_profile(self, start_index, steps_to_build):
def build_generator_profile(self, start_index, do_run_up=True):
acceleration_time = self.calculate_acceleration_time()
trajectory = {}
time_array = []
velocity_mode = []
user_programs = []
completed_steps_lookup = []
last_point = None

for i in range(start_index, start_index + steps_to_build):
# Cap last point to steps_up_to
self.end_index = start_index + POINTS_PER_BUILD
if self.end_index > self.steps_up_to:
self.end_index = self.steps_up_to

# If we are doing the first build, do_run_up will be called so flag
# that we need a runup, else just continue from the previous point
if do_run_up:
last_point = None
else:
last_point = self.generator.get_point(start_index - 1)

for i in range(start_index, self.end_index):
point = self.generator.get_point(i)

# Check if we need to insert the lower bound point
Expand All @@ -309,26 +344,26 @@ def build_generator_profile(self, start_index, steps_to_build):
time_array.append(lower_move_time)
velocity_mode.append(PREV_TO_NEXT)
user_programs.append(TRIG_ZERO)
completed_steps_lookup.append(i)
self.completed_steps_lookup.append(i)

if lower_move_time:
# Add lower bound
time_array.append(lower_move_time)
velocity_mode.append(CURRENT_TO_NEXT)
user_programs.append(TRIG_LIVE_FRAME)
completed_steps_lookup.append(i)
self.completed_steps_lookup.append(i)

# Add position
time_array.append(point.duration / 2.0)
velocity_mode.append(PREV_TO_NEXT)
user_programs.append(TRIG_CAPTURE)
completed_steps_lookup.append(i)
self.completed_steps_lookup.append(i)

# Add upper bound
time_array.append(point.duration / 2.0)
velocity_mode.append(PREV_TO_NEXT)
user_programs.append(TRIG_LIVE_FRAME)
completed_steps_lookup.append(i + 1)
self.completed_steps_lookup.append(i + 1)

# Add the axis positions
for axis_name, cs_def in self.axis_mapping.items():
Expand All @@ -343,20 +378,21 @@ def build_generator_profile(self, start_index, steps_to_build):
last_point = point

# Add the last tail off point
time_array.append(acceleration_time)
velocity_mode[-1] = PREV_TO_CURRENT
velocity_mode.append(ZERO_VELOCITY)
user_programs[-1] = TRIG_DEAD_FRAME
user_programs.append(TRIG_ZERO)
completed_steps_lookup.append(i + 1)

for axis_name, tail_off in self.run_up_positions(last_point).items():
positions = trajectory[axis_name]
positions.append(positions[-1] + tail_off)
if self.end_index == self.steps_up_to:
time_array.append(acceleration_time)
velocity_mode[-1] = PREV_TO_CURRENT
velocity_mode.append(ZERO_VELOCITY)
user_programs[-1] = TRIG_DEAD_FRAME
user_programs.append(TRIG_ZERO)
self.completed_steps_lookup.append(i + 1)

for axis_name, tail_off in self.run_up_positions(last_point).items():
positions = trajectory[axis_name]
positions.append(positions[-1] + tail_off)

profile = dict(time_array=time_array, velocity_mode=velocity_mode,
trajectory=trajectory, user_programs=user_programs)
return completed_steps_lookup, profile
return profile

def _same_sign(self, a, b):
return a*b >= 0
Expand Down
44 changes: 38 additions & 6 deletions tests/test_parts/test_pmac/test_pmactrajectorypart.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import setup_malcolm_paths

import unittest
from mock import MagicMock, call
from mock import MagicMock, call, patch

Mock = MagicMock

Expand Down Expand Up @@ -93,7 +93,7 @@ def test_configure(self):
timeArray=[100000, 437500, 100000],
velocityMode=[2, 1, 3],
userPrograms=[0, 0, 0],
numPoints=3,
pointsToBuild=3,
positionsA=[0.45, -0.087500000000000008, -0.1375],
positionsB=[0.0, 0.0, 0.0]))
self.assertEqual(task.post.call_args_list[0],
Expand All @@ -113,20 +113,52 @@ def test_configure(self):
userPrograms=[
3, 4, 3, 4, 3, 4, 2, 8,
3, 4, 3, 4, 3, 4, 2, 8],
numPoints=16,
pointsToBuild=16,
positionsA=[
-0.125, 0.0, 0.125, 0.25, 0.375, 0.5, 0.625, 0.6375,
0.625, 0.5, 0.375, 0.25, 0.125, 0.0, -0.125, -0.1375],
positionsB=[
0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.05,
0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1]))
task.put.assert_called_once_with(self.child["numPoints"], 18)

@patch("malcolm.parts.pmac.pmactrajectorypart.POINTS_PER_BUILD", 4)
def test_update_step(self):
task = self.do_configure(axes_to_scan=["x", "y"])
positionsA = task.put_many.call_args_list[3][0][1]["positionsA"]
self.assertEqual(len(positionsA), 11)
self.assertEqual(positionsA[-1], 0.375)
self.assertEqual(self.o.end_index, 4)
self.assertEqual(len(self.o.completed_steps_lookup), 11)
update_completed_steps = MagicMock()
task = MagicMock()
self.o.update_step(3, update_completed_steps, task)
update_completed_steps.assert_called_once_with(1, self.o)
self.assertEqual(self.o.loading, False)
self.assertEqual(task.put_many.call_count, 2)
self.assertEqual(task.post.call_count, 1)
self.check_resolutions_and_use(task.put_many.call_args_list[0][0][1])
self.assertEqual(task.post.call_args_list[0],
call(self.child["appendProfile"]))
self.assertEqual(task.put_many.call_args_list[1][0][1], dict(
timeArray=[
500000, 500000, 500000, 500000, 100000],
velocityMode=[
0, 0, 0, 1, 3],
userPrograms=[
4, 3, 4, 2, 8],
pointsToBuild=5,
positionsA=[
0.25, 0.125, 0.0, -0.125, -0.1375],
positionsB=[
0.1, 0.1, 0.1, 0.1, 0.1]))

def test_run(self):
task = Mock()
update = Mock()
self.o.run(task, update)
task.subscribe.assert_called_once_with(
self.child["pointsScanned"], self.o.update_step, update)
self.child["pointsScanned"], self.o.update_step, update, task)
task.post.assert_called_once_with(self.child["executeProfile"])

def test_multi_run(self):
Expand All @@ -144,7 +176,7 @@ def test_multi_run(self):
timeArray=[100000, 500000, 500000, 500000, 500000, 500000, 500000, 100000],
velocityMode=[2, 0, 0, 0, 0, 0, 1, 3],
userPrograms=[3, 4, 3, 4, 3, 4, 2, 8],
numPoints=8,
pointsToBuild=8,
positionsA=[0.625, 0.5, 0.375, 0.25, 0.125, 0.0,
-0.125, -0.1375],
))
Expand All @@ -158,7 +190,7 @@ def test_long_move(self):
2, 1, 1, 1, 3],
userPrograms=[
0, 0, 0, 0, 0],
numPoints=5,
pointsToBuild=5,
positionsA=[
-10.087499999999999, -6.7875, -3.4875, -0.1875, -0.1375],
))
Expand Down

0 comments on commit a094230

Please sign in to comment.