From 8615a0199b595d7933add65d65df896440800db7 Mon Sep 17 00:00:00 2001 From: Simon Bigelmayr <simon.bigelmayr@mll.com> Date: Fri, 22 Jul 2022 18:46:33 +0200 Subject: [PATCH 1/2] feat: Add predefined classes that implement `Rack`- and `LiquidClass`-interface feat: Add `fileName`-method on `TecanProtocol` BREAKING CHANGE: - delete superfluous semicolon after TipMask - `PositionLocation` returns `$rack->name` - `BarcodeLocation` does not return `$rack->name` - Move `Rack`- and `LiquidClass`-interface to subdirectory - Change signature of `TransferWithAutoWash`-class --- CHANGELOG.md | 15 ++ src/Tecan/BasicCommands/Aspirate.php | 2 +- .../BasicPipettingActionCommand.php | 6 +- src/Tecan/BasicCommands/BreakCommand.php | 7 +- src/Tecan/BasicCommands/Command.php | 4 +- src/Tecan/BasicCommands/Dispense.php | 4 +- src/Tecan/BasicCommands/Wash.php | 7 +- .../CustomCommand/TransferWithAutoWash.php | 22 ++- src/Tecan/LiquidClass/CustomLiquidClass.php | 18 +++ src/Tecan/{ => LiquidClass}/LiquidClass.php | 2 +- src/Tecan/LiquidClass/MllLiquidClass.php | 28 ++++ src/Tecan/Location/BarcodeLocation.php | 6 +- src/Tecan/Location/PositionLocation.php | 4 +- src/Tecan/Rack/CustomRack.php | 26 ++++ src/Tecan/Rack/MllLabWareRack.php | 63 ++++++++ src/Tecan/{ => Rack}/Rack.php | 2 +- src/Tecan/TecanProtocol.php | 15 +- tests/TestImplentations/TestLiquidClass.php | 13 -- tests/TestImplentations/TestRack.php | 18 --- .../Unit/Tecan/BasicCommands/AspirateTest.php | 17 ++- tests/Unit/Tecan/BasicCommands/BreakTest.php | 2 +- .../Unit/Tecan/BasicCommands/DispenseTest.php | 17 ++- tests/Unit/Tecan/BasicCommands/WashTest.php | 2 +- .../TransferWithAutoWashTest.php | 25 ++-- .../Tecan/LiquidClass/MllLiquidClassTest.php | 18 +++ tests/Unit/Tecan/Rack/MllLabWareRackTest.php | 35 +++++ tests/Unit/Tecan/TecanProtocolTest.php | 140 ++++++++++-------- 27 files changed, 350 insertions(+), 168 deletions(-) create mode 100644 src/Tecan/LiquidClass/CustomLiquidClass.php rename src/Tecan/{ => LiquidClass}/LiquidClass.php (62%) create mode 100644 src/Tecan/LiquidClass/MllLiquidClass.php create mode 100644 src/Tecan/Rack/CustomRack.php create mode 100644 src/Tecan/Rack/MllLabWareRack.php rename src/Tecan/{ => Rack}/Rack.php (71%) delete mode 100644 tests/TestImplentations/TestLiquidClass.php delete mode 100644 tests/TestImplentations/TestRack.php create mode 100644 tests/Unit/Tecan/LiquidClass/MllLiquidClassTest.php create mode 100644 tests/Unit/Tecan/Rack/MllLabWareRackTest.php diff --git a/CHANGELOG.md b/CHANGELOG.md index 780cc13..2ec7b84 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,21 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased +## v2.0.0 + +### Added + +- Add predefined classes that implement `Rack`- and `LiquidClass`-interface +- Add `fileName`-method on `TecanProtocol` + +### Breaking + +- delete superfluous semicolon after TipMask +- `PositionLocation` returns `$rack->name` +- `BarcodeLocation` does not return `$rack->name` +- Move `Rack`- and `LiquidClass`-interface to subdirectory +- Change signature of `TransferWithAutoWash`-class + ## v1.1.0 ### Added diff --git a/src/Tecan/BasicCommands/Aspirate.php b/src/Tecan/BasicCommands/Aspirate.php index 87baab3..52cd0a8 100644 --- a/src/Tecan/BasicCommands/Aspirate.php +++ b/src/Tecan/BasicCommands/Aspirate.php @@ -2,7 +2,7 @@ namespace Mll\LiquidHandlingRobotics\Tecan\BasicCommands; -use Mll\LiquidHandlingRobotics\Tecan\LiquidClass; +use Mll\LiquidHandlingRobotics\Tecan\LiquidClass\LiquidClass; use Mll\LiquidHandlingRobotics\Tecan\Location\Location; final class Aspirate extends BasicPipettingActionCommand implements Command diff --git a/src/Tecan/BasicCommands/BasicPipettingActionCommand.php b/src/Tecan/BasicCommands/BasicPipettingActionCommand.php index 09e5864..5b50f9a 100644 --- a/src/Tecan/BasicCommands/BasicPipettingActionCommand.php +++ b/src/Tecan/BasicCommands/BasicPipettingActionCommand.php @@ -2,7 +2,7 @@ namespace Mll\LiquidHandlingRobotics\Tecan\BasicCommands; -use Mll\LiquidHandlingRobotics\Tecan\LiquidClass; +use Mll\LiquidHandlingRobotics\Tecan\LiquidClass\LiquidClass; use Mll\LiquidHandlingRobotics\Tecan\Location\Location; abstract class BasicPipettingActionCommand implements PipettingActionCommand @@ -17,7 +17,7 @@ abstract class BasicPipettingActionCommand implements PipettingActionCommand abstract public static function commandLetter(): string; - public function toString(): string + public function formatToString(): string { return static::commandLetter() . ';' @@ -29,7 +29,7 @@ public function toString(): string . $this->volume . ';' . $this->liquidClass->name() . ';' . ';' // tipType - . $this->getTipMask() . ';' + . $this->getTipMask() ; } diff --git a/src/Tecan/BasicCommands/BreakCommand.php b/src/Tecan/BasicCommands/BreakCommand.php index 2f018e6..c61fff7 100644 --- a/src/Tecan/BasicCommands/BreakCommand.php +++ b/src/Tecan/BasicCommands/BreakCommand.php @@ -4,13 +4,8 @@ final class BreakCommand implements Command { - public static function commandLetter(): string + public function formatToString(): string { return 'B;'; } - - public function toString(): string - { - return static::commandLetter(); - } } diff --git a/src/Tecan/BasicCommands/Command.php b/src/Tecan/BasicCommands/Command.php index 78575e5..fd378cd 100644 --- a/src/Tecan/BasicCommands/Command.php +++ b/src/Tecan/BasicCommands/Command.php @@ -4,7 +4,5 @@ interface Command { - public static function commandLetter(): string; - - public function toString(): string; + public function formatToString(): string; } diff --git a/src/Tecan/BasicCommands/Dispense.php b/src/Tecan/BasicCommands/Dispense.php index ad1a364..96cd870 100644 --- a/src/Tecan/BasicCommands/Dispense.php +++ b/src/Tecan/BasicCommands/Dispense.php @@ -2,12 +2,12 @@ namespace Mll\LiquidHandlingRobotics\Tecan\BasicCommands; -use Mll\LiquidHandlingRobotics\Tecan\LiquidClass; +use Mll\LiquidHandlingRobotics\Tecan\LiquidClass\LiquidClass; use Mll\LiquidHandlingRobotics\Tecan\Location\Location; final class Dispense extends BasicPipettingActionCommand implements Command { - public function __construct(int $volume, Location $location, LiquidClass $liquidClass) + public function __construct(float $volume, Location $location, LiquidClass $liquidClass) { $this->volume = $volume; $this->location = $location; diff --git a/src/Tecan/BasicCommands/Wash.php b/src/Tecan/BasicCommands/Wash.php index 8df8b06..9b412f0 100644 --- a/src/Tecan/BasicCommands/Wash.php +++ b/src/Tecan/BasicCommands/Wash.php @@ -4,13 +4,8 @@ final class Wash implements Command { - public static function commandLetter(): string + public function formatToString(): string { return 'W;'; } - - public function toString(): string - { - return static::commandLetter(); - } } diff --git a/src/Tecan/CustomCommand/TransferWithAutoWash.php b/src/Tecan/CustomCommand/TransferWithAutoWash.php index c56fa11..0cf0e55 100644 --- a/src/Tecan/CustomCommand/TransferWithAutoWash.php +++ b/src/Tecan/CustomCommand/TransferWithAutoWash.php @@ -5,6 +5,9 @@ use Mll\LiquidHandlingRobotics\Tecan\BasicCommands\Aspirate; use Mll\LiquidHandlingRobotics\Tecan\BasicCommands\Dispense; use Mll\LiquidHandlingRobotics\Tecan\BasicCommands\PipettingActionCommand; +use Mll\LiquidHandlingRobotics\Tecan\BasicCommands\Wash; +use Mll\LiquidHandlingRobotics\Tecan\LiquidClass\LiquidClass; +use Mll\LiquidHandlingRobotics\Tecan\Location\Location; use Mll\LiquidHandlingRobotics\Tecan\TecanProtocol; final class TransferWithAutoWash implements PipettingActionCommand @@ -13,25 +16,20 @@ final class TransferWithAutoWash implements PipettingActionCommand private Dispense $dispense; - public function __construct(Aspirate $aspirate, Dispense $dispense) + public function __construct(float $volume, LiquidClass $liquidClass, Location $aspirateLocation, Location $dispenseLocation) { - $this->aspirate = $aspirate; - $this->dispense = $dispense; + $this->aspirate = new Aspirate($volume, $aspirateLocation, $liquidClass); + $this->dispense = new Dispense($volume, $dispenseLocation, $liquidClass); } - public static function commandLetter(): string - { - return 'W;'; - } - - public function toString(): string + public function formatToString(): string { return - $this->aspirate->toString() + $this->aspirate->formatToString() . TecanProtocol::WINDOWS_NEW_LINE - . $this->dispense->toString() + . $this->dispense->formatToString() . TecanProtocol::WINDOWS_NEW_LINE - . static::commandLetter(); + . (new Wash())->formatToString(); } public function setTipMask(int $tipMask): void diff --git a/src/Tecan/LiquidClass/CustomLiquidClass.php b/src/Tecan/LiquidClass/CustomLiquidClass.php new file mode 100644 index 0000000..aed1ebb --- /dev/null +++ b/src/Tecan/LiquidClass/CustomLiquidClass.php @@ -0,0 +1,18 @@ +<?php declare(strict_types=1); + +namespace Mll\LiquidHandlingRobotics\Tecan\LiquidClass; + +final class CustomLiquidClass implements LiquidClass +{ + private string $name; + + public function __construct(string $name) + { + $this->name = $name; + } + + public function name(): string + { + return $this->name; + } +} diff --git a/src/Tecan/LiquidClass.php b/src/Tecan/LiquidClass/LiquidClass.php similarity index 62% rename from src/Tecan/LiquidClass.php rename to src/Tecan/LiquidClass/LiquidClass.php index 6691192..7b45246 100644 --- a/src/Tecan/LiquidClass.php +++ b/src/Tecan/LiquidClass/LiquidClass.php @@ -1,6 +1,6 @@ <?php declare(strict_types=1); -namespace Mll\LiquidHandlingRobotics\Tecan; +namespace Mll\LiquidHandlingRobotics\Tecan\LiquidClass; interface LiquidClass { diff --git a/src/Tecan/LiquidClass/MllLiquidClass.php b/src/Tecan/LiquidClass/MllLiquidClass.php new file mode 100644 index 0000000..5b6cd5c --- /dev/null +++ b/src/Tecan/LiquidClass/MllLiquidClass.php @@ -0,0 +1,28 @@ +<?php declare(strict_types=1); + +namespace Mll\LiquidHandlingRobotics\Tecan\LiquidClass; + +use BenSampo\Enum\Enum; + +/** + * @method static static DNA_DILUTION() + * @method static static DNA_DILUTION_WATER() + * @method static static TRANSFER_PCR_PRODUKT() + * @method static static TRANSFER_MASTERMIX_MP() + * @method static static TRANSFER_TEMPLATE() + */ +final class MllLiquidClass extends Enum implements LiquidClass +{ + public const DNA_DILUTION = 'DNA_Dilution'; + public const DNA_DILUTION_WATER = 'DNA_Dilution_Water'; + public const TRANSFER_PCR_PRODUKT = 'Transfer_PCR_Produkt'; + public const TRANSFER_MASTERMIX_MP = 'Transfer_Mastermix_MP'; + public const TRANSFER_TEMPLATE = 'Transfer_Template'; // DNA-templates and BUFFER! + + public function name(): string + { + assert(is_string($this->value)); + + return $this->value; + } +} diff --git a/src/Tecan/Location/BarcodeLocation.php b/src/Tecan/Location/BarcodeLocation.php index 1cea65a..af1fa88 100644 --- a/src/Tecan/Location/BarcodeLocation.php +++ b/src/Tecan/Location/BarcodeLocation.php @@ -2,7 +2,7 @@ namespace Mll\LiquidHandlingRobotics\Tecan\Location; -use Mll\LiquidHandlingRobotics\Tecan\Rack; +use Mll\LiquidHandlingRobotics\Tecan\Rack\Rack; final class BarcodeLocation implements Location { @@ -26,9 +26,9 @@ public function position(): ?string return null; } - public function rackName(): string + public function rackName(): ?string { - return $this->rack->name(); + return null; } public function rackType(): string diff --git a/src/Tecan/Location/PositionLocation.php b/src/Tecan/Location/PositionLocation.php index 1f7ab15..b4ac229 100644 --- a/src/Tecan/Location/PositionLocation.php +++ b/src/Tecan/Location/PositionLocation.php @@ -2,7 +2,7 @@ namespace Mll\LiquidHandlingRobotics\Tecan\Location; -use Mll\LiquidHandlingRobotics\Tecan\Rack; +use Mll\LiquidHandlingRobotics\Tecan\Rack\Rack; final class PositionLocation implements Location { @@ -28,7 +28,7 @@ public function position(): string public function rackName(): ?string { - return null; + return $this->rack->name(); } public function rackType(): string diff --git a/src/Tecan/Rack/CustomRack.php b/src/Tecan/Rack/CustomRack.php new file mode 100644 index 0000000..2682814 --- /dev/null +++ b/src/Tecan/Rack/CustomRack.php @@ -0,0 +1,26 @@ +<?php declare(strict_types=1); + +namespace Mll\LiquidHandlingRobotics\Tecan\Rack; + +final class CustomRack implements Rack +{ + private string $name; + + private string $type; + + public function __construct(string $name, string $type) + { + $this->type = $type; + $this->name = $name; + } + + public function name(): string + { + return $this->name; + } + + public function type(): string + { + return $this->type; + } +} diff --git a/src/Tecan/Rack/MllLabWareRack.php b/src/Tecan/Rack/MllLabWareRack.php new file mode 100644 index 0000000..51c75dc --- /dev/null +++ b/src/Tecan/Rack/MllLabWareRack.php @@ -0,0 +1,63 @@ +<?php declare(strict_types=1); + +namespace Mll\LiquidHandlingRobotics\Tecan\Rack; + +use BenSampo\Enum\Enum; +use Exception; + +/** + * @method static static A() + * @method static static MP_CDNA() + * @method static static MP_SAMPLE() + * @method static static MP_WATER() + * @method static static FLUID_X() + * @method static static MM() + * @method static static DEST_LC() + * @method static static DEST_PCR() + * @method static static DEST_TAQMAN() + */ +final class MllLabWareRack extends Enum implements Rack +{ + public const A = 'A'; + public const MP_CDNA = 'MPCDNA'; + public const MP_SAMPLE = 'MPSample'; + public const MP_WATER = 'MPWasser'; + public const FLUID_X = 'FluidX'; + public const MM = 'MM'; + public const DEST_LC = 'DestLC'; + public const DEST_PCR = 'DestPCR'; + public const DEST_TAQMAN = 'DestTaqMan'; + + public function type(): string + { + switch ($this->value) { + case self::A: + return 'Eppis 24x0.5 ml Cooled'; + case self::MP_CDNA: + return 'MP cDNA'; + case self::MP_SAMPLE: + return 'MP Microplate'; + case self::MP_WATER: + return 'Trough 300ml MCA Portrait'; + case self::FLUID_X: + return '96FluidX'; + case self::MM: + return 'Eppis 32x1.5 ml Cooled'; + case self::DEST_LC: + return '96 Well MP LightCycler480'; + case self::DEST_PCR: + return '96 Well PCR ABI semi-skirted'; + case self::DEST_TAQMAN: + return '96 Well PCR TaqMan'; + default: + throw new Exception('Type not defined for ' . $this->value); + } + } + + public function name(): string + { + assert(is_string($this->value)); + + return $this->value; + } +} diff --git a/src/Tecan/Rack.php b/src/Tecan/Rack/Rack.php similarity index 71% rename from src/Tecan/Rack.php rename to src/Tecan/Rack/Rack.php index ac46c81..a1fe20c 100644 --- a/src/Tecan/Rack.php +++ b/src/Tecan/Rack/Rack.php @@ -1,6 +1,6 @@ <?php declare(strict_types=1); -namespace Mll\LiquidHandlingRobotics\Tecan; +namespace Mll\LiquidHandlingRobotics\Tecan\Rack; interface Rack { diff --git a/src/Tecan/TecanProtocol.php b/src/Tecan/TecanProtocol.php index addd8a9..01f618b 100644 --- a/src/Tecan/TecanProtocol.php +++ b/src/Tecan/TecanProtocol.php @@ -3,6 +3,7 @@ namespace Mll\LiquidHandlingRobotics\Tecan; use Illuminate\Support\Collection; +use Illuminate\Support\Str; use Mll\LiquidHandlingRobotics\Tecan\BasicCommands\BreakCommand; use Mll\LiquidHandlingRobotics\Tecan\BasicCommands\Command; use Mll\LiquidHandlingRobotics\Tecan\BasicCommands\PipettingActionCommand; @@ -15,6 +16,8 @@ final class TecanProtocol */ public const WINDOWS_NEW_LINE = "\r\n"; + public const GEMINI_WORKLIST_FILENAME_SUFFIX = '.gwl'; + /** * @var Collection<int, Command> */ @@ -22,10 +25,13 @@ final class TecanProtocol private TipMask $tipMask; - public function __construct(TipMask $tipMask) + private string $protocolName; + + public function __construct(TipMask $tipMask, string $protocolName = null) { $this->commands = new Collection([]); $this->tipMask = $tipMask; + $this->protocolName = $protocolName ?? Str::uuid()->toString(); } public function addCommandCurrentTip(Command $command): void @@ -52,8 +58,13 @@ public function addCommandForNextTip(Command $command): void public function buildProtocol(): string { return $this->commands - ->map(fn (Command $command): string => $command->toString()) + ->map(fn (Command $command): string => $command->formatToString()) ->join(self::WINDOWS_NEW_LINE) . self::WINDOWS_NEW_LINE; } + + public function fileName(): string + { + return $this->protocolName . self::GEMINI_WORKLIST_FILENAME_SUFFIX; + } } diff --git a/tests/TestImplentations/TestLiquidClass.php b/tests/TestImplentations/TestLiquidClass.php deleted file mode 100644 index ed222db..0000000 --- a/tests/TestImplentations/TestLiquidClass.php +++ /dev/null @@ -1,13 +0,0 @@ -<?php declare(strict_types=1); - -namespace Mll\LiquidHandlingRobotics\Tests\TestImplentations; - -use Mll\LiquidHandlingRobotics\Tecan\LiquidClass; - -final class TestLiquidClass implements LiquidClass -{ - public function name(): string - { - return 'TestLiquidClassName'; - } -} diff --git a/tests/TestImplentations/TestRack.php b/tests/TestImplentations/TestRack.php deleted file mode 100644 index 7aa166e..0000000 --- a/tests/TestImplentations/TestRack.php +++ /dev/null @@ -1,18 +0,0 @@ -<?php declare(strict_types=1); - -namespace Mll\LiquidHandlingRobotics\Tests\TestImplentations; - -use Mll\LiquidHandlingRobotics\Tecan\Rack; - -final class TestRack implements Rack -{ - public function name(): string - { - return 'TestRackName'; - } - - public function type(): string - { - return 'TestRackType'; - } -} diff --git a/tests/Unit/Tecan/BasicCommands/AspirateTest.php b/tests/Unit/Tecan/BasicCommands/AspirateTest.php index 9897c61..79bdca5 100644 --- a/tests/Unit/Tecan/BasicCommands/AspirateTest.php +++ b/tests/Unit/Tecan/BasicCommands/AspirateTest.php @@ -3,10 +3,12 @@ namespace Mll\LiquidHandlingRobotics\Tests\Unit\Tecan\BasicCommands; use Mll\LiquidHandlingRobotics\Tecan\BasicCommands\Aspirate; +use Mll\LiquidHandlingRobotics\Tecan\LiquidClass\CustomLiquidClass; +use Mll\LiquidHandlingRobotics\Tecan\LiquidClass\MllLiquidClass; use Mll\LiquidHandlingRobotics\Tecan\Location\BarcodeLocation; use Mll\LiquidHandlingRobotics\Tecan\Location\PositionLocation; -use Mll\LiquidHandlingRobotics\Tests\TestImplentations\TestLiquidClass; -use Mll\LiquidHandlingRobotics\Tests\TestImplentations\TestRack; +use Mll\LiquidHandlingRobotics\Tecan\Rack\CustomRack; +use Mll\LiquidHandlingRobotics\Tecan\Rack\MllLabWareRack; use PHPUnit\Framework\TestCase; final class AspirateTest extends TestCase @@ -14,18 +16,19 @@ final class AspirateTest extends TestCase public function testAspirateWithBarcodeLocation(): void { $barcode = 'barcode'; - $aspirate = new Aspirate(100, new BarcodeLocation($barcode, new TestRack()), new TestLiquidClass()); + $aspirate = new Aspirate(100, new BarcodeLocation($barcode, new CustomRack('TestRackName', 'TestRackType')), new CustomLiquidClass('TestLiquidClassName')); self::assertSame($barcode, $aspirate->location->tubeId()); self::assertNull($aspirate->location->position()); - self::assertSame('A;TestRackName;;TestRackType;;barcode;100;TestLiquidClassName;;;', $aspirate->toString()); + self::assertSame('A;;;TestRackType;;barcode;100;TestLiquidClassName;;', $aspirate->formatToString()); } - public function testAspirateWithLabelLocation(): void + public function testAspirateWithPositionLocation(): void { $position = 7; - $aspirate = new Aspirate(100, new PositionLocation($position, new TestRack()), new TestLiquidClass()); + $volume = 2.2; + $aspirate = new Aspirate($volume, new PositionLocation($position, MllLabWareRack::DEST_PCR()), MllLiquidClass::TRANSFER_TEMPLATE()); self::assertNull($aspirate->location->tubeId()); self::assertSame((string) $position, $aspirate->location->position()); - self::assertSame('A;;;TestRackType;7;;100;TestLiquidClassName;;;', $aspirate->toString()); + self::assertSame('A;DestPCR;;96 Well PCR ABI semi-skirted;' . $position . ';;2.2;Transfer_Template;;', $aspirate->formatToString()); } } diff --git a/tests/Unit/Tecan/BasicCommands/BreakTest.php b/tests/Unit/Tecan/BasicCommands/BreakTest.php index 56932a6..c46dd61 100644 --- a/tests/Unit/Tecan/BasicCommands/BreakTest.php +++ b/tests/Unit/Tecan/BasicCommands/BreakTest.php @@ -9,6 +9,6 @@ final class BreakTest extends TestCase { public function testBreakCommand(): void { - self::assertSame('B;', (new BreakCommand())->toString()); + self::assertSame('B;', (new BreakCommand())->formatToString()); } } diff --git a/tests/Unit/Tecan/BasicCommands/DispenseTest.php b/tests/Unit/Tecan/BasicCommands/DispenseTest.php index f4be0a0..4a68d22 100644 --- a/tests/Unit/Tecan/BasicCommands/DispenseTest.php +++ b/tests/Unit/Tecan/BasicCommands/DispenseTest.php @@ -3,10 +3,12 @@ namespace Mll\LiquidHandlingRobotics\Tests\Unit\Tecan\BasicCommands; use Mll\LiquidHandlingRobotics\Tecan\BasicCommands\Dispense; +use Mll\LiquidHandlingRobotics\Tecan\LiquidClass\CustomLiquidClass; +use Mll\LiquidHandlingRobotics\Tecan\LiquidClass\MllLiquidClass; use Mll\LiquidHandlingRobotics\Tecan\Location\BarcodeLocation; use Mll\LiquidHandlingRobotics\Tecan\Location\PositionLocation; -use Mll\LiquidHandlingRobotics\Tests\TestImplentations\TestLiquidClass; -use Mll\LiquidHandlingRobotics\Tests\TestImplentations\TestRack; +use Mll\LiquidHandlingRobotics\Tecan\Rack\CustomRack; +use Mll\LiquidHandlingRobotics\Tecan\Rack\MllLabWareRack; use PHPUnit\Framework\TestCase; final class DispenseTest extends TestCase @@ -14,18 +16,19 @@ final class DispenseTest extends TestCase public function testDispenseWithBarcodeLocation(): void { $barcode = 'barcode'; - $aspirate = new Dispense(100, new BarcodeLocation($barcode, new TestRack()), new TestLiquidClass()); + $aspirate = new Dispense(100, new BarcodeLocation($barcode, new CustomRack('TestRackName', 'TestRackType')), new CustomLiquidClass('TestLiquidClassName')); self::assertSame($barcode, $aspirate->location->tubeId()); self::assertNull($aspirate->location->position()); - self::assertSame('D;TestRackName;;TestRackType;;barcode;100;TestLiquidClassName;;;', $aspirate->toString()); + self::assertSame('D;;;TestRackType;;barcode;100;TestLiquidClassName;;', $aspirate->formatToString()); } - public function testDispenseWithLabelLocation(): void + public function testDispenseWithPositionLocation(): void { $position = 7; - $aspirate = new Dispense(100, new PositionLocation($position, new TestRack()), new TestLiquidClass()); + $volume = 2.2; + $aspirate = new Dispense($volume, new PositionLocation($position, MllLabWareRack::DEST_LC()), MllLiquidClass::TRANSFER_TEMPLATE()); self::assertNull($aspirate->location->tubeId()); self::assertSame((string) $position, $aspirate->location->position()); - self::assertSame('D;;;TestRackType;7;;100;TestLiquidClassName;;;', $aspirate->toString()); + self::assertSame("D;DestLC;;96 Well MP LightCycler480;{$position};;{$volume};Transfer_Template;;", $aspirate->formatToString()); } } diff --git a/tests/Unit/Tecan/BasicCommands/WashTest.php b/tests/Unit/Tecan/BasicCommands/WashTest.php index 6f4bd73..703d5e8 100644 --- a/tests/Unit/Tecan/BasicCommands/WashTest.php +++ b/tests/Unit/Tecan/BasicCommands/WashTest.php @@ -9,6 +9,6 @@ final class WashTest extends TestCase { public function testWashCommand(): void { - self::assertSame('W;', (new Wash())->toString()); + self::assertSame('W;', (new Wash())->formatToString()); } } diff --git a/tests/Unit/Tecan/CustomCommands/TransferWithAutoWashTest.php b/tests/Unit/Tecan/CustomCommands/TransferWithAutoWashTest.php index f0af635..2d175bf 100644 --- a/tests/Unit/Tecan/CustomCommands/TransferWithAutoWashTest.php +++ b/tests/Unit/Tecan/CustomCommands/TransferWithAutoWashTest.php @@ -2,12 +2,10 @@ namespace Mll\LiquidHandlingRobotics\Tests\Unit\Tecan\CustomCommands; -use Mll\LiquidHandlingRobotics\Tecan\BasicCommands\Aspirate; -use Mll\LiquidHandlingRobotics\Tecan\BasicCommands\Dispense; use Mll\LiquidHandlingRobotics\Tecan\CustomCommand\TransferWithAutoWash; +use Mll\LiquidHandlingRobotics\Tecan\LiquidClass\CustomLiquidClass; use Mll\LiquidHandlingRobotics\Tecan\Location\BarcodeLocation; -use Mll\LiquidHandlingRobotics\Tests\TestImplentations\TestLiquidClass; -use Mll\LiquidHandlingRobotics\Tests\TestImplentations\TestRack; +use Mll\LiquidHandlingRobotics\Tecan\Rack\CustomRack; use MLL\Utils\StringUtil; use PHPUnit\Framework\TestCase; @@ -15,23 +13,20 @@ final class TransferWithAutoWashTest extends TestCase { public function testTransferWithAutoWashCommand(): void { - $liquidClass = new TestLiquidClass(); - $rack = new TestRack(); - $barcodeLocation = new BarcodeLocation('barcode', $rack); - $location = new BarcodeLocation('barcode1', $rack); + $liquidClass = new CustomLiquidClass('TestLiquidClassName'); + $rack = new CustomRack('TestRackName', 'TestRackType'); + $aspirateLocation = new BarcodeLocation('barcode', $rack); + $dispenseLocation = new BarcodeLocation('barcode1', $rack); - $transfer = new TransferWithAutoWash( - new Aspirate(100, $barcodeLocation, $liquidClass), - new Dispense(100, $location, $liquidClass) - ); + $transfer = new TransferWithAutoWash(100, $liquidClass, $aspirateLocation, $dispenseLocation); self::assertSame( StringUtil::normalizeLineEndings( - 'A;TestRackName;;TestRackType;;barcode;100;TestLiquidClassName;;; -D;TestRackName;;TestRackType;;barcode1;100;TestLiquidClassName;;; + 'A;;;TestRackType;;barcode;100;TestLiquidClassName;; +D;;;TestRackType;;barcode1;100;TestLiquidClassName;; W;' ), - $transfer->toString() + $transfer->formatToString() ); } } diff --git a/tests/Unit/Tecan/LiquidClass/MllLiquidClassTest.php b/tests/Unit/Tecan/LiquidClass/MllLiquidClassTest.php new file mode 100644 index 0000000..0601ff6 --- /dev/null +++ b/tests/Unit/Tecan/LiquidClass/MllLiquidClassTest.php @@ -0,0 +1,18 @@ +<?php declare(strict_types=1); + +namespace Mll\LiquidHandlingRobotics\Tests\Unit\Tecan\LiquidClass; + +use Mll\LiquidHandlingRobotics\Tecan\LiquidClass\MllLiquidClass; +use PHPUnit\Framework\TestCase; + +final class MllLiquidClassTest extends TestCase +{ + public function testNameOfEnum(): void + { + self::assertSame('DNA_Dilution', MllLiquidClass::DNA_DILUTION()->name()); + self::assertSame('DNA_Dilution_Water', MllLiquidClass::DNA_DILUTION_WATER()->name()); + self::assertSame('Transfer_PCR_Produkt', MllLiquidClass::TRANSFER_PCR_PRODUKT()->name()); + self::assertSame('Transfer_Mastermix_MP', MllLiquidClass::TRANSFER_MASTERMIX_MP()->name()); + self::assertSame('Transfer_Template', MllLiquidClass::TRANSFER_TEMPLATE()->name()); + } +} diff --git a/tests/Unit/Tecan/Rack/MllLabWareRackTest.php b/tests/Unit/Tecan/Rack/MllLabWareRackTest.php new file mode 100644 index 0000000..9416680 --- /dev/null +++ b/tests/Unit/Tecan/Rack/MllLabWareRackTest.php @@ -0,0 +1,35 @@ +<?php declare(strict_types=1); + +namespace Mll\LiquidHandlingRobotics\Tests\Unit\Tecan\Rack; + +use Mll\LiquidHandlingRobotics\Tecan\Rack\MllLabWareRack; +use PHPUnit\Framework\TestCase; + +final class MllLabWareRackTest extends TestCase +{ + public function testNameOfEnum(): void + { + self::assertSame('A', MllLabWareRack::A()->name()); + self::assertSame('MPCDNA', MllLabWareRack::MP_CDNA()->name()); + self::assertSame('MPSample', MllLabWareRack::MP_SAMPLE()->name()); + self::assertSame('MPWasser', MllLabWareRack::MP_WATER()->name()); + self::assertSame('FluidX', MllLabWareRack::FLUID_X()->name()); + self::assertSame('MM', MllLabWareRack::MM()->name()); + self::assertSame('DestLC', MllLabWareRack::DEST_LC()->name()); + self::assertSame('DestPCR', MllLabWareRack::DEST_PCR()->name()); + self::assertSame('DestTaqMan', MllLabWareRack::DEST_TAQMAN()->name()); + } + + public function testValueOfEnum(): void + { + self::assertSame('Eppis 24x0.5 ml Cooled', MllLabWareRack::A()->type()); + self::assertSame('MP cDNA', MllLabWareRack::MP_CDNA()->type()); + self::assertSame('MP Microplate', MllLabWareRack::MP_SAMPLE()->type()); + self::assertSame('Trough 300ml MCA Portrait', MllLabWareRack::MP_WATER()->type()); + self::assertSame('96FluidX', MllLabWareRack::FLUID_X()->type()); + self::assertSame('Eppis 32x1.5 ml Cooled', MllLabWareRack::MM()->type()); + self::assertSame('96 Well MP LightCycler480', MllLabWareRack::DEST_LC()->type()); + self::assertSame('96 Well PCR ABI semi-skirted', MllLabWareRack::DEST_PCR()->type()); + self::assertSame('96 Well PCR TaqMan', MllLabWareRack::DEST_TAQMAN()->type()); + } +} diff --git a/tests/Unit/Tecan/TecanProtocolTest.php b/tests/Unit/Tecan/TecanProtocolTest.php index 92d3bd6..5e10f54 100644 --- a/tests/Unit/Tecan/TecanProtocolTest.php +++ b/tests/Unit/Tecan/TecanProtocolTest.php @@ -2,55 +2,70 @@ namespace Mll\LiquidHandlingRobotics\Tests\Unit\Tecan; +use Illuminate\Support\Str; use Mll\LiquidHandlingRobotics\Tecan\BasicCommands\Aspirate; use Mll\LiquidHandlingRobotics\Tecan\BasicCommands\Dispense; use Mll\LiquidHandlingRobotics\Tecan\BasicCommands\Wash; use Mll\LiquidHandlingRobotics\Tecan\CustomCommand\TransferWithAutoWash; +use Mll\LiquidHandlingRobotics\Tecan\LiquidClass\CustomLiquidClass; use Mll\LiquidHandlingRobotics\Tecan\Location\BarcodeLocation; +use Mll\LiquidHandlingRobotics\Tecan\Rack\CustomRack; use Mll\LiquidHandlingRobotics\Tecan\TecanProtocol; use Mll\LiquidHandlingRobotics\Tecan\TipMask\TipMask; -use Mll\LiquidHandlingRobotics\Tests\TestImplentations\TestLiquidClass; -use Mll\LiquidHandlingRobotics\Tests\TestImplentations\TestRack; use MLL\Utils\StringUtil; use PHPUnit\Framework\TestCase; final class TecanProtocolTest extends TestCase { + public function testProtocolCustomName(): void + { + $tecanProtocol = new TecanProtocol(TipMask::FOUR_TIPS(), 'testProtocol'); + self::assertSame('testProtocol.gwl', $tecanProtocol->fileName()); + } + + public function testProtocolUuidName(): void + { + $tecanProtocol = new TecanProtocol(TipMask::FOUR_TIPS()); + + $fileName = Str::before($tecanProtocol->fileName(), TecanProtocol::GEMINI_WORKLIST_FILENAME_SUFFIX); + self::assertTrue(Str::isUuid($fileName)); + + $fileSuffix = Str::after($tecanProtocol->fileName(), $fileName); + self::assertSame($fileSuffix, TecanProtocol::GEMINI_WORKLIST_FILENAME_SUFFIX); + } + public function testProtocolWithForFourTips(): void { $tecanProtocol = new TecanProtocol(TipMask::FOUR_TIPS()); - $liquidClass = new TestLiquidClass(); - $rack = new TestRack(); - $barcodeLocation = new BarcodeLocation('barcode', $rack); - $location = new BarcodeLocation('barcode1', $rack); + $liquidClass = new CustomLiquidClass('TestLiquidClassName'); + $rack = new CustomRack('TestRackName', 'TestRackType'); + $aspirateLocation = new BarcodeLocation('barcode', $rack); + $dispenseLocation = new BarcodeLocation('barcode1', $rack); foreach (range(1, 5) as $_) { $tecanProtocol->addCommandForNextTip( - new TransferWithAutoWash( - new Aspirate(100, $barcodeLocation, $liquidClass), - new Dispense(100, $location, $liquidClass) - ) + new TransferWithAutoWash(100, $liquidClass, $aspirateLocation, $dispenseLocation) ); } self::assertSame( StringUtil::normalizeLineEndings( - 'A;TestRackName;;TestRackType;;barcode;100;TestLiquidClassName;;1; -D;TestRackName;;TestRackType;;barcode1;100;TestLiquidClassName;;1; + 'A;;;TestRackType;;barcode;100;TestLiquidClassName;;1 +D;;;TestRackType;;barcode1;100;TestLiquidClassName;;1 W; -A;TestRackName;;TestRackType;;barcode;100;TestLiquidClassName;;2; -D;TestRackName;;TestRackType;;barcode1;100;TestLiquidClassName;;2; +A;;;TestRackType;;barcode;100;TestLiquidClassName;;2 +D;;;TestRackType;;barcode1;100;TestLiquidClassName;;2 W; -A;TestRackName;;TestRackType;;barcode;100;TestLiquidClassName;;4; -D;TestRackName;;TestRackType;;barcode1;100;TestLiquidClassName;;4; +A;;;TestRackType;;barcode;100;TestLiquidClassName;;4 +D;;;TestRackType;;barcode1;100;TestLiquidClassName;;4 W; -A;TestRackName;;TestRackType;;barcode;100;TestLiquidClassName;;8; -D;TestRackName;;TestRackType;;barcode1;100;TestLiquidClassName;;8; +A;;;TestRackType;;barcode;100;TestLiquidClassName;;8 +D;;;TestRackType;;barcode1;100;TestLiquidClassName;;8 W; B; -A;TestRackName;;TestRackType;;barcode;100;TestLiquidClassName;;1; -D;TestRackName;;TestRackType;;barcode1;100;TestLiquidClassName;;1; +A;;;TestRackType;;barcode;100;TestLiquidClassName;;1 +D;;;TestRackType;;barcode1;100;TestLiquidClassName;;1 W; ' ), @@ -62,17 +77,17 @@ public function testProtocolWithForForTipsAndManualWash(): void { $tecanProtocol = new TecanProtocol(TipMask::FOUR_TIPS()); - $liquidClass = new TestLiquidClass(); - $rack = new TestRack(); - $barcodeLocation = new BarcodeLocation('barcode', $rack); - $location = new BarcodeLocation('barcode1', $rack); + $liquidClass = new CustomLiquidClass('TestLiquidClassName'); + $rack = new CustomRack('TestRackName', 'TestRackType'); + $aspirateLocation = new BarcodeLocation('barcode', $rack); + $dispenseLocation = new BarcodeLocation('barcode1', $rack); foreach (range(1, 5) as $_) { $tecanProtocol->addCommandForNextTip( - new Aspirate(100, $barcodeLocation, $liquidClass), + new Aspirate(100, $aspirateLocation, $liquidClass), ); $tecanProtocol->addCommandCurrentTip( - new Dispense(100, $location, $liquidClass) + new Dispense(100, $dispenseLocation, $liquidClass) ); $tecanProtocol->addCommandCurrentTip( new Wash() @@ -81,21 +96,21 @@ public function testProtocolWithForForTipsAndManualWash(): void self::assertSame( StringUtil::normalizeLineEndings( - 'A;TestRackName;;TestRackType;;barcode;100;TestLiquidClassName;;1; -D;TestRackName;;TestRackType;;barcode1;100;TestLiquidClassName;;1; + 'A;;;TestRackType;;barcode;100;TestLiquidClassName;;1 +D;;;TestRackType;;barcode1;100;TestLiquidClassName;;1 W; -A;TestRackName;;TestRackType;;barcode;100;TestLiquidClassName;;2; -D;TestRackName;;TestRackType;;barcode1;100;TestLiquidClassName;;2; +A;;;TestRackType;;barcode;100;TestLiquidClassName;;2 +D;;;TestRackType;;barcode1;100;TestLiquidClassName;;2 W; -A;TestRackName;;TestRackType;;barcode;100;TestLiquidClassName;;4; -D;TestRackName;;TestRackType;;barcode1;100;TestLiquidClassName;;4; +A;;;TestRackType;;barcode;100;TestLiquidClassName;;4 +D;;;TestRackType;;barcode1;100;TestLiquidClassName;;4 W; -A;TestRackName;;TestRackType;;barcode;100;TestLiquidClassName;;8; -D;TestRackName;;TestRackType;;barcode1;100;TestLiquidClassName;;8; +A;;;TestRackType;;barcode;100;TestLiquidClassName;;8 +D;;;TestRackType;;barcode1;100;TestLiquidClassName;;8 W; B; -A;TestRackName;;TestRackType;;barcode;100;TestLiquidClassName;;1; -D;TestRackName;;TestRackType;;barcode1;100;TestLiquidClassName;;1; +A;;;TestRackType;;barcode;100;TestLiquidClassName;;1 +D;;;TestRackType;;barcode1;100;TestLiquidClassName;;1 W; ' ), @@ -107,52 +122,49 @@ public function testProtocolWithForEightTips(): void { $tecanProtocol = new TecanProtocol(TipMask::EIGHT_TIPS()); - $liquidClass = new TestLiquidClass(); - $rack = new TestRack(); - $barcodeLocation = new BarcodeLocation('barcode', $rack); - $location = new BarcodeLocation('barcode1', $rack); + $liquidClass = new CustomLiquidClass('TestLiquidClassName'); + $rack = new CustomRack('TestRackName', 'TestRackType'); + $aspirateLocation = new BarcodeLocation('barcode', $rack); + $dispenseLocation = new BarcodeLocation('barcode1', $rack); foreach (range(1, 10) as $_) { $tecanProtocol->addCommandForNextTip( - new TransferWithAutoWash( - new Aspirate(100, $barcodeLocation, $liquidClass), - new Dispense(100, $location, $liquidClass) - ) + new TransferWithAutoWash(100, $liquidClass, $aspirateLocation, $dispenseLocation) ); } self::assertSame( StringUtil::normalizeLineEndings( - 'A;TestRackName;;TestRackType;;barcode;100;TestLiquidClassName;;1; -D;TestRackName;;TestRackType;;barcode1;100;TestLiquidClassName;;1; + 'A;;;TestRackType;;barcode;100;TestLiquidClassName;;1 +D;;;TestRackType;;barcode1;100;TestLiquidClassName;;1 W; -A;TestRackName;;TestRackType;;barcode;100;TestLiquidClassName;;2; -D;TestRackName;;TestRackType;;barcode1;100;TestLiquidClassName;;2; +A;;;TestRackType;;barcode;100;TestLiquidClassName;;2 +D;;;TestRackType;;barcode1;100;TestLiquidClassName;;2 W; -A;TestRackName;;TestRackType;;barcode;100;TestLiquidClassName;;4; -D;TestRackName;;TestRackType;;barcode1;100;TestLiquidClassName;;4; +A;;;TestRackType;;barcode;100;TestLiquidClassName;;4 +D;;;TestRackType;;barcode1;100;TestLiquidClassName;;4 W; -A;TestRackName;;TestRackType;;barcode;100;TestLiquidClassName;;8; -D;TestRackName;;TestRackType;;barcode1;100;TestLiquidClassName;;8; +A;;;TestRackType;;barcode;100;TestLiquidClassName;;8 +D;;;TestRackType;;barcode1;100;TestLiquidClassName;;8 W; -A;TestRackName;;TestRackType;;barcode;100;TestLiquidClassName;;16; -D;TestRackName;;TestRackType;;barcode1;100;TestLiquidClassName;;16; +A;;;TestRackType;;barcode;100;TestLiquidClassName;;16 +D;;;TestRackType;;barcode1;100;TestLiquidClassName;;16 W; -A;TestRackName;;TestRackType;;barcode;100;TestLiquidClassName;;32; -D;TestRackName;;TestRackType;;barcode1;100;TestLiquidClassName;;32; +A;;;TestRackType;;barcode;100;TestLiquidClassName;;32 +D;;;TestRackType;;barcode1;100;TestLiquidClassName;;32 W; -A;TestRackName;;TestRackType;;barcode;100;TestLiquidClassName;;64; -D;TestRackName;;TestRackType;;barcode1;100;TestLiquidClassName;;64; +A;;;TestRackType;;barcode;100;TestLiquidClassName;;64 +D;;;TestRackType;;barcode1;100;TestLiquidClassName;;64 W; -A;TestRackName;;TestRackType;;barcode;100;TestLiquidClassName;;128; -D;TestRackName;;TestRackType;;barcode1;100;TestLiquidClassName;;128; +A;;;TestRackType;;barcode;100;TestLiquidClassName;;128 +D;;;TestRackType;;barcode1;100;TestLiquidClassName;;128 W; B; -A;TestRackName;;TestRackType;;barcode;100;TestLiquidClassName;;1; -D;TestRackName;;TestRackType;;barcode1;100;TestLiquidClassName;;1; +A;;;TestRackType;;barcode;100;TestLiquidClassName;;1 +D;;;TestRackType;;barcode1;100;TestLiquidClassName;;1 W; -A;TestRackName;;TestRackType;;barcode;100;TestLiquidClassName;;2; -D;TestRackName;;TestRackType;;barcode1;100;TestLiquidClassName;;2; +A;;;TestRackType;;barcode;100;TestLiquidClassName;;2 +D;;;TestRackType;;barcode1;100;TestLiquidClassName;;2 W; ' ), From 8738be4113a952a234196b8fb46c6ddda4382f2f Mon Sep 17 00:00:00 2001 From: Simon Bigelmayr <simon.bigelmayr@mll.com> Date: Mon, 25 Jul 2022 09:58:23 +0200 Subject: [PATCH 2/2] add php doc comment for volume-param --- src/Tecan/BasicCommands/Aspirate.php | 5 +++++ src/Tecan/BasicCommands/Dispense.php | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/src/Tecan/BasicCommands/Aspirate.php b/src/Tecan/BasicCommands/Aspirate.php index 52cd0a8..4ba7243 100644 --- a/src/Tecan/BasicCommands/Aspirate.php +++ b/src/Tecan/BasicCommands/Aspirate.php @@ -7,6 +7,11 @@ final class Aspirate extends BasicPipettingActionCommand implements Command { + /** + * @param float $volume Floating point values are accepted and do not cause an error, + * but they will be rounded before being used. In such cases, it is recommended to use + * integer calculations to avoid unexpected results. + */ public function __construct(float $volume, Location $location, LiquidClass $liquidClass) { $this->volume = $volume; diff --git a/src/Tecan/BasicCommands/Dispense.php b/src/Tecan/BasicCommands/Dispense.php index 96cd870..9192e4b 100644 --- a/src/Tecan/BasicCommands/Dispense.php +++ b/src/Tecan/BasicCommands/Dispense.php @@ -7,6 +7,11 @@ final class Dispense extends BasicPipettingActionCommand implements Command { + /** + * @param float $volume Floating point values are accepted and do not cause an error, + * but they will be rounded before being used. In such cases, it is recommended to use + * integer calculations to avoid unexpected results. + */ public function __construct(float $volume, Location $location, LiquidClass $liquidClass) { $this->volume = $volume;