Skip to content

Liconic backend (Liconic STX series automated incubator support)#846

Open
sam-adaptyv wants to merge 41 commits intoPyLabRobot:mainfrom
sam-adaptyv:liconic-backend
Open

Liconic backend (Liconic STX series automated incubator support)#846
sam-adaptyv wants to merge 41 commits intoPyLabRobot:mainfrom
sam-adaptyv:liconic-backend

Conversation

@sam-adaptyv
Copy link

Added a new backend for Liconic STX series of automated incubators. It is compatible with existing incubator backend commands also added a number of new incubator backend commands. Also added a new pice of hardware barcode_scanner which can be optionally initialized with a Liconic incubator to read barcodes with optional read_barcode variables added to current and added incubator commands. Right now the barcode is just printed to terminal as the incubator backed typically only returns the Plate object or nothing. If there was a barcode field added to the Plate resource that seems to be obvious place to return the scanned barcode but that could be done in the future. For now the Keyence BL-1300 barcode scanner is the only model support but other models could be easily integrated. Documentation in user_guide > 01_material-handling > liconic.ipynb is also provided.

sam-adaptyv and others added 11 commits January 28, 2026 10:44
- Add BarcodeScanner frontend that wraps BarcodeScannerBackend
- Export BarcodeScanner and BarcodeScannerError from __init__.py
- Fix type hints in KeyenceBarcodeScannerBackend (callable -> Callable)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Parameter was annotated as str but used as Plate object.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- LiconicBackend now accepts optional BarcodeScanner instance instead of
  creating KeyenceBarcodeScannerBackend internally
- Backend methods set plate.barcode instead of returning barcode strings
- Incubator frontend uses **backend_kwargs pattern to pass read_barcode
  to backend methods without changing signature
- Add Keyence-specific error handling (NG/ERR99) to KeyenceBarcodeScannerBackend
- Update abstract method signatures in IncubatorBackend

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- shaker_status: raise NotImplementedError (missing PLC command)
- get_shaker_speed: add int() conversion before division
- check_shovel_sensor: add missing await on asyncio.sleep

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Copy link
Member

Choose a reason for hiding this comment

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

to clarify, are these in mm or steps? Ideally all resources in PLR are in mm so they can be used in a hardware agnostic setting and make it easy for people to use/make their own labware.

Copy link
Author

Choose a reason for hiding this comment

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

So pitch and site_height are for sure in mm. The step variable is the number of steps the motors turns (i.e. steps of the motor) which moves the plate shuttle in z. So its specific to the motors used by Liconic. The conversion of pitch to steps is: step = pitch * ( 1713 / 50 ) rounded to the nearest integer. The steps value is what needs to sent directly to the memory in the liconic to move correctly ("WR DM23 x"). So we could calculate the steps value by the pitch value (which is mm) to meet this ideal.

}

@classmethod
def deserialize(cls, data: dict):
Copy link
Member

Choose a reason for hiding this comment

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

scan_barcode() calls ST 1910 to move the shovel to the barcode reading position but:

  1. Doesn't call _wait_ready() before scanning — is the shovel guaranteed to have finished moving?
  2. Doesn't call RS 1910 afterward to return the shovel, unlike read_barcode_inline() which does both.

Is this intentional (e.g. caller is expected to handle reset), or should this match the read_barcode_inline pattern?

Copy link
Author

Choose a reason for hiding this comment

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

  1. No the shovel isn't guaranteed to have finished moving. Often the barcodes can be read even while the shovel is moving so hasn't been found to be an issue. Only downside is it could slow down the speed of barcode scanning.
  2. Should call "RS 1910" and thus should match the read_barcode_inline pattern will ensure subsequent commands don't cause errors.

Copy link
Member

Choose a reason for hiding this comment

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

thanks, I'll implement that!

53: 2021, # pitch=59, site_height=53
66: 2467, # pitch=72, site_height=66
104: 3563, # pitch=110, site_height=104
}
Copy link
Member

Choose a reason for hiding this comment

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

The 104mm rack (pitch 110mm) claims motor steps = 3563, but the formula round(pitch * 1713 / 50) that holds for all other sizes gives 3769. That is a 5.5% difference — too large for rounding.

All other sizes match the formula exactly:

pitch  claimed  computed
   11      377       377  OK
   17      582       582  OK
   ...
   72     2467      2467  OK
  110     3563      3769  FAIL

Is 3563 empirically determined from the hardware, or a data entry error? Added a test that documents this discrepancy.

Copy link
Author

Choose a reason for hiding this comment

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

It is a data entry error (aka typo) 104mm was used to compute 3563 but that is the plate height in mm when it should be calculated using the pitch value in mm which is 110 which would equals 3769 steps.

Copy link
Author

Choose a reason for hiding this comment

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

Should be 110 for pitch and 3769 for steps

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