Skip to content

Commit d3c990c

Browse files
authored
Add command SetDiTiType to tecan worklist
1 parent d7bf63f commit d3c990c

File tree

5 files changed

+181
-10
lines changed

5 files changed

+181
-10
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,12 @@ See [GitHub releases](https://github.com/mll-lab/php-utils/releases).
99

1010
## Unreleased
1111

12+
## v5.4.0
13+
14+
### Added
15+
16+
- Add command `SetDiTiType` to tecan worklist
17+
1218
## v5.3.0
1319

1420
### Added
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<?php declare(strict_types=1);
2+
3+
namespace MLL\Utils\Tecan\BasicCommands;
4+
5+
/**
6+
* Can only be used at the very beginning of the worklist or directly after a Break command.
7+
*
8+
* @see BreakCommand
9+
*/
10+
class SetDiTiType extends Command
11+
{
12+
private int $indexOfDiTi;
13+
14+
public function __construct(int $indexOfDiTi)
15+
{
16+
$this->indexOfDiTi = $indexOfDiTi;
17+
}
18+
19+
public function toString(): string
20+
{
21+
return "S;{$this->indexOfDiTi}";
22+
}
23+
}

src/Tecan/TecanProtocol.php

Lines changed: 64 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
use MLL\Utils\Tecan\BasicCommands\BreakCommand;
1111
use MLL\Utils\Tecan\BasicCommands\Command;
1212
use MLL\Utils\Tecan\BasicCommands\Comment;
13+
use MLL\Utils\Tecan\BasicCommands\SetDiTiType;
1314
use MLL\Utils\Tecan\BasicCommands\UsesTipMask;
1415
use MLL\Utils\Tecan\TipMask\TipMask;
1516

@@ -20,19 +21,28 @@ class TecanProtocol
2021

2122
public const GEMINI_WORKLIST_FILENAME_SUFFIX = '.gwl';
2223

24+
private TipMask $tipMask;
25+
26+
private string $protocolName;
27+
2328
/** @var Collection<int, Command> */
2429
private Collection $commands;
2530

26-
private TipMask $tipMask;
31+
public ?int $defaultDiTiTypeIndex;
2732

28-
private string $protocolName;
33+
public ?int $currentDiTiTypeIndex;
2934

30-
public function __construct(TipMask $tipMask, ?string $protocolName = null, ?string $userName = null)
31-
{
32-
$this->protocolName = $protocolName ?? Str::uuid()->toString();
35+
public function __construct(
36+
TipMask $tipMask,
37+
?string $protocolName = null,
38+
?string $userName = null,
39+
?int $defaultDiTiTypeIndex = null
40+
) {
3341
$this->tipMask = $tipMask;
34-
35-
$this->commands = $this->initHeader($userName, $protocolName);
42+
$this->protocolName = $protocolName ?? Str::uuid()->toString();
43+
$this->commands = $this->buildHeader($userName, $protocolName);
44+
$this->defaultDiTiTypeIndex = $defaultDiTiTypeIndex;
45+
$this->currentDiTiTypeIndex = $defaultDiTiTypeIndex;
3646
}
3747

3848
public function addCommand(Command $command): void
@@ -43,7 +53,8 @@ public function addCommand(Command $command): void
4353
/** @param Command&UsesTipMask $command */
4454
public function addCommandCurrentTip(Command $command): void
4555
{
46-
$command->setTipMask($this->tipMask->currentTip ?? TipMask::firstTip());
56+
$tip = $this->tipMask->currentTip ?? TipMask::firstTip();
57+
$this->setTipMask($command, $tip);
4758

4859
$this->commands->add($command);
4960
}
@@ -55,11 +66,37 @@ public function addCommandForNextTip(Command $command): void
5566
$this->commands->add(new BreakCommand());
5667
}
5768

58-
$command->setTipMask($this->tipMask->nextTip());
69+
$this->setTipMask($command, $this->tipMask->nextTip());
5970

6071
$this->commands->add($command);
6172
}
6273

74+
private function shouldUseDifferentTipTypeIndex(): bool
75+
{
76+
return $this->defaultDiTiTypeIndex !== null
77+
&& $this->defaultDiTiTypeIndex !== $this->currentDiTiTypeIndex;
78+
}
79+
80+
private function setTipMask(UsesTipMask $command, int $tip): void
81+
{
82+
$command->setTipMask($tip);
83+
84+
if (! $this->shouldUseDifferentTipTypeIndex()) {
85+
return;
86+
}
87+
88+
if ($this->currentDiTiTypeIndex === null) {
89+
return;
90+
}
91+
92+
if ($this->commands->isEmpty()
93+
|| $this->commandsAreOnlyComments()
94+
|| $this->commands->last() instanceof BreakCommand
95+
) {
96+
$this->commands->add(new SetDiTiType($this->currentDiTiTypeIndex));
97+
}
98+
}
99+
63100
public function buildProtocol(): string
64101
{
65102
return $this->commands
@@ -73,8 +110,24 @@ public function fileName(): string
73110
return $this->protocolName . self::GEMINI_WORKLIST_FILENAME_SUFFIX;
74111
}
75112

113+
public function setCurrentDiTiTypeIndex(int $currentDiTiTypeIndex): void
114+
{
115+
if (! $this->commandsAreOnlyComments()
116+
&& ! $this->commands->last() instanceof BreakCommand
117+
) {
118+
throw new TecanException('Cannot change the DiTi type index if the last command is not a break command.');
119+
}
120+
121+
$this->currentDiTiTypeIndex = $currentDiTiTypeIndex;
122+
}
123+
124+
private function commandsAreOnlyComments(): bool
125+
{
126+
return $this->commands->every(fn (Command $command): bool => $command instanceof Comment);
127+
}
128+
76129
/** @return Collection<int, Command> */
77-
private function initHeader(?string $userName, ?string $protocolName): Collection
130+
private function buildHeader(?string $userName, ?string $protocolName): Collection
78131
{
79132
$package = Meta::PACKAGE_NAME;
80133
$version = InstalledVersions::getPrettyVersion($package);
@@ -90,6 +143,7 @@ private function initHeader(?string $userName, ?string $protocolName): Collectio
90143
if ($userName !== null) {
91144
$commentCommands->add(new Comment("User: {$userName}"));
92145
}
146+
93147
if ($protocolName !== null) {
94148
$commentCommands->add(new Comment("Protocol name: {$protocolName}"));
95149
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<?php declare(strict_types=1);
2+
3+
namespace MLL\Utils\Tests\Tecan\BasicCommands;
4+
5+
use MLL\Utils\Tecan\BasicCommands\SetDiTiType;
6+
use PHPUnit\Framework\TestCase;
7+
8+
final class SetDiTiTypeTest extends TestCase
9+
{
10+
public function testFormatToString(): void
11+
{
12+
$index = 7;
13+
$setDiTiType = new SetDiTiType($index);
14+
15+
self::assertSame("S;{$index}", $setDiTiType->toString());
16+
}
17+
}

tests/Tecan/TecanProtocolTest.php

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,77 @@ public function testProtocolWithForFourTips(): void
101101
D;;;96FluidX;;barcode1;100;TestLiquidClassName;;1
102102
W;
103103
104+
CSV),
105+
$tecanProtocol->buildProtocol()
106+
);
107+
}
108+
109+
public function testProtocolWithChangingCurrentDiTiTypeIndex(): void
110+
{
111+
$tecanProtocol = new TecanProtocol(TipMask::FOUR_TIPS(), null, null, 1);
112+
113+
$liquidClass = new CustomLiquidClass('TestLiquidClassName');
114+
$rack = new FluidXRack();
115+
$aspirateLocation = new BarcodeLocation('barcode', $rack);
116+
$dispenseLocation = new BarcodeLocation('barcode1', $rack);
117+
118+
foreach (range(1, 4) as $_) {
119+
$tecanProtocol->addCommandForNextTip(
120+
new TransferWithAutoWash(100, $liquidClass, $aspirateLocation, $dispenseLocation)
121+
);
122+
}
123+
124+
$tecanProtocol->currentDiTiTypeIndex = 2;
125+
$tecanProtocol->addCommandForNextTip(
126+
new TransferWithAutoWash(100, $liquidClass, $aspirateLocation, $dispenseLocation)
127+
);
128+
129+
self::assertSame(
130+
StringUtil::normalizeLineEndings(<<<CSV
131+
{$this->initComment()}A;;;96FluidX;;barcode;100;TestLiquidClassName;;1
132+
D;;;96FluidX;;barcode1;100;TestLiquidClassName;;1
133+
W;
134+
A;;;96FluidX;;barcode;100;TestLiquidClassName;;2
135+
D;;;96FluidX;;barcode1;100;TestLiquidClassName;;2
136+
W;
137+
A;;;96FluidX;;barcode;100;TestLiquidClassName;;4
138+
D;;;96FluidX;;barcode1;100;TestLiquidClassName;;4
139+
W;
140+
A;;;96FluidX;;barcode;100;TestLiquidClassName;;8
141+
D;;;96FluidX;;barcode1;100;TestLiquidClassName;;8
142+
W;
143+
B;
144+
S;2
145+
A;;;96FluidX;;barcode;100;TestLiquidClassName;;1
146+
D;;;96FluidX;;barcode1;100;TestLiquidClassName;;1
147+
W;
148+
149+
CSV),
150+
$tecanProtocol->buildProtocol()
151+
);
152+
}
153+
154+
public function testProtocolWithStartingNotDefaultDiTiTypeIndex(): void
155+
{
156+
$tecanProtocol = new TecanProtocol(TipMask::FOUR_TIPS(), null, null, 1);
157+
158+
$liquidClass = new CustomLiquidClass('TestLiquidClassName');
159+
$rack = new FluidXRack();
160+
$aspirateLocation = new BarcodeLocation('barcode', $rack);
161+
$dispenseLocation = new BarcodeLocation('barcode1', $rack);
162+
163+
$tecanProtocol->currentDiTiTypeIndex = 2;
164+
$tecanProtocol->addCommandForNextTip(
165+
new TransferWithAutoWash(100, $liquidClass, $aspirateLocation, $dispenseLocation)
166+
);
167+
168+
self::assertSame(
169+
StringUtil::normalizeLineEndings(<<<CSV
170+
{$this->initComment()}S;2
171+
A;;;96FluidX;;barcode;100;TestLiquidClassName;;1
172+
D;;;96FluidX;;barcode1;100;TestLiquidClassName;;1
173+
W;
174+
104175
CSV),
105176
$tecanProtocol->buildProtocol()
106177
);

0 commit comments

Comments
 (0)