Skip to content

Agilent plateloc serial control, target v1b1#1040

Open
alexjamesgodfrey wants to merge 6 commits into
PyLabRobot:v1b1from
alexjamesgodfrey:agilent-plateloc-v1b1
Open

Agilent plateloc serial control, target v1b1#1040
alexjamesgodfrey wants to merge 6 commits into
PyLabRobot:v1b1from
alexjamesgodfrey:agilent-plateloc-v1b1

Conversation

@alexjamesgodfrey
Copy link
Copy Markdown

This PR adds v1b1-style support for the Agilent PlateLoc Thermal Microplate Sealer through PLR's standard Sealer capability plus PlateLoc-specific setpoint/status helpers.

The implementation talks directly to the PlateLoc over RS-232 using PLR's pylabrobot.io.Serial wrapper around pyserial. It does not require Agilent ActiveX, VWorks, a vendor HTTP server, Windows-only binaries, or bundled native code. Users install the optional PLR serial extra (pylabrobot[serial]) when they want to use the hardware.

What changed

  • Added pylabrobot.agilent.PlateLoc, a Device exposing sealer as the user-facing capability.
  • Added PlateLocDriver, a direct serial driver for PlateLoc firmware commands.
  • Added PlateLocSealerBackend, mapping PLR sealer operations to direct hardware commands.
  • Added independent setpoint helpers: set_sealing_temperature() and set_sealing_time() on both PlateLoc and PlateLocDriver.
  • Added PlateLocStatus plus status_snapshot() and request_status() for the target temperature, sealing time, stage position, cycle-complete state, and last serial response.
  • Added PlateLocSerialProfile, so serial settings and command codes stay configurable without changing the PLR frontend.
  • Added docs under docs/user_guide/agilent/plateloc/hello-world.md and linked PlateLoc from the Agilent docs index and sealer docs.
  • Added unit tests using a fake serial transport

Protocol shape

The live instrument tested here uses 19200 8N1 and carriage-return-terminated ASCII frames:

Operation Frame
Set sealing temperature ST 0.{temperature_celsius:03d}\r
Set sealing time SS 0.{seconds_x10:02d}\r
Start cycle GO 00\r
Stop cycle AC 00\r
Move stage out SO 00\r
Move stage in SI 00\r
Apply seal AS 00\r
Clear error CL 00\r
Check cycle complete CC 00\r

The setpoint payloads use the firmware's fractional convention: ST 0.175\r means 175 C, ST 0.030\r means 30 C, and SS 0.12\r means 1.2 s. Acknowledgements such as STAK\r, SOAK\r, and SIAK\r are accepted. Negative acknowledgements such as STNK(Desired Temperature is Out of Range) are raised as PlateLocError.

request_status() issues the live CC 00\r cycle-complete query and combines that result with PLR's best-known local state. The decoded direct protocol does not expose actual block-temperature reads or stored-time reads, so target temperature and sealing time are the last successfully written setpoints.

Agilent documents the PlateLoc as supporting robotic integration with an RS-232 serial port and ActiveX control; this PR keeps PLR on the serial side and avoids taking a runtime dependency on the ActiveX layer. See the official PlateLoc ActiveX user guide for the vendor automation context.

Example

from pylabrobot.agilent import PlateLoc

plateloc = PlateLoc(name="plateloc", port="COM6")

await plateloc.setup()

await plateloc.set_sealing_temperature(175)
await plateloc.set_sealing_time(1.2)
status = await plateloc.request_status()

await plateloc.sealer.open()
await plateloc.sealer.close()
await plateloc.sealer.seal(temperature=175, duration=1.2)
complete = await plateloc.driver.check_cycle_complete()

await plateloc.stop()

Custom serial settings or command codes can be supplied through PlateLocSerialProfile while the frontend remains the same.

Validation

  • python3 -m unittest pylabrobot.agilent.plateloc.plateloc_tests
  • uvx ruff==0.15.4 check pylabrobot/agilent/plateloc pylabrobot/agilent/__init__.py
  • Unit test run against the checkout
  • Live test on an Agilent PlateLoc connected as, including stop, clear error, stage in, a high-temperature seal cycle, independent temperature/time setpoint writes, and status polling

Comment on lines +27 to +38
DEFAULT_PLATELOC_COMMANDS: Mapping[str, str] = {
"set_sealing_temperature": "ST",
"set_sealing_time": "SS",
"move_stage_out": "SO",
"move_stage_in": "SI",
"start_cycle": "GO",
"stop_cycle": "AC",
"apply_seal": "AS",
"clear_error": "CL",
"check_cycle_complete": "CC",
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

does this need to be parameterized? does it change between machines? why not have the methods send the command directly?

Comment on lines +134 to +135
serial_cls=Serial,
) -> None:
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why does serial_cls need to be parameterized?

Comment on lines +198 to +199
if hasattr(self.io, "reset_input_buffer"):
await self.io.reset_input_buffer()
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why hasattr?

Comment on lines +398 to +408
async def set_sealing_temperature(self, temperature: float):
return await self.driver.set_sealing_temperature(temperature)

async def set_sealing_time(self, duration: float):
return await self.driver.set_sealing_time(duration)

def status_snapshot(self, cycle_complete: Optional[bool] = None) -> PlateLocStatus:
return self.driver.status_snapshot(cycle_complete=cycle_complete)

async def request_status(self, query_cycle_complete: bool = True) -> PlateLocStatus:
return await self.driver.request_status(query_cycle_complete=query_cycle_complete)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the pattern in v1b1 is to just have the driver/capabilities have these methods rather than duplicating them on the Device layer

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@rickwierenga
Copy link
Copy Markdown
Member

converted the hello world from a markdown to notebook so people can easily just download and run it!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants