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;