has
'position'
=> (
is
=>
'rw'
,
isa
=>
'ArrayRef'
,
default
=>
sub
{ [] }, );
has
'plungerMin'
=> (
is
=>
'rw'
,
isa
=>
'Int'
,
default
=> 0 );
has
'plungerMax'
=> (
is
=>
'rw'
,
isa
=>
'Int'
,
default
=> 3150 );
has
'MAX_STEP'
=> (
is
=>
'rw'
,
isa
=>
'ArrayRef'
,
default
=>
sub
{ [] }, );
my
$Debug
= 1;
sub
getPipettePosition {
my
(
$self
,
%params
) =
@_
;
my
$position
;
my
$tip
=
$params
{
"tip"
};
my
$code
=
$self
->compile_xp(
"GET_STATUS"
,
tip
=>
$tip
,
parameter
=> 0);
$self
->DATAPATH()->
write
(
$code
);
my
$reply
=
$self
->DATAPATH()->
read
();
if
(!(
$reply
=~ /^0/)) {
warn
"!! "
. __PACKAGE__.
" expected '0' got '$reply' from hardware\n"
;
return
-1;
}
$position
= (
split
(/[,;]/,
$reply
))[1];
if
(!
defined
(
$position
) ||
$position
< 0) {
warn
__PACKAGE__.
" - tip position error in reply '$reply'\n"
;
return
-1;
}
warn
__PACKAGE__.
"Position of tip=$tip is "
.
$position
.
"\n"
if
$Debug
>2;
$self
->position()->[
$tip
] =
$position
;
return
$position
;
}
sub
checkPipetteSteps {
my
(
$self
,
%params
) =
@_
;
my
$start
=
$params
{
"start"
};
my
$stop
=
$params
{
"stop"
};
my
$tip
=
$params
{
"tip"
};
my
$dir
=
$params
{
"dir"
};
if
(0) {
my
$max
=
$self
->MAX_STEPS()->[
$tip
];
if
(!
$max
< 1) {
my
$code
=
$self
->compile_xp(
"RPP"
,
tip
=>
$tip
,
parameter
=> 5);
$self
->DATAPATH()->
write
(
$code
);
my
$reply
=
$self
->DATAPATH()->
read
();
if
(!(
$reply
=~ /^0/)) {
warn
"!! "
. __PACKAGE__.
" expected '0' got '$reply' from hardware\n"
;
return
-1;
}
$max
= (
split
(/[,;]/,
$reply
))[1];
if
(!
defined
(
$max
) ||
$max
< 0) {
warn
__PACKAGE__.
" - tip max error in reply '$reply'\n"
;
return
-1;
}
warn
__PACKAGE__.
"Max of tip=$tip is "
.
$max
.
"\n"
if
$Debug
;
$self
->MAX_STEPS()->[
$tip
] =
$max
;
}
}
if
(
$dir
eq
"a"
) {
if
(
$stop
>
$self
->plungerMax ||
$stop
<
$self
->plungerMin) {
return
0;
}
if
(
$stop
< 1) {
return
0;
}
if
(
$start
>
$stop
) {
return
0;
}
if
(
$start
eq
$stop
) {
carp
"plunger start eq stop"
;
}
return
1;
}
elsif
(
$dir
eq
"d"
) {
if
(
$stop
>
$self
->plungerMax ||
$stop
<
$self
->plungerMin) {
return
0;
}
if
(
$stop
< 0) {
return
0;
}
if
(
$start
<
$stop
) {
return
0;
}
if
(
$start
eq
$stop
) {
carp
"plunger start eq stop"
;
}
return
1;
}
else
{
die
"bad dir '$dir' for tip '$tip'"
;
}
}
sub
getPipetteSpeed {
my
(
$self
,
%params
) =
@_
;
my
$tip
=
$params
{
"tip"
};
return
(900, 1400, 2400);
}
sub
getPipetteDelay {
my
(
$self
,
%params
) =
@_
;
my
$tip
=
$params
{
"tip"
};
return
0;
}
sub
convertVolToSteps {
my
(
$self
,
$vol
) =
@_
;
my
$maxvol
= 1000;
my
$maxstep
= 3150;
my
$step
;
$vol
=~ /^(\d+)([munp]*)/i;
if
($2 eq
"u"
) {
$vol
= $1;
}
elsif
($2 eq
"n"
) {
$vol
= $1 * 1000;
}
elsif
($2 eq
"p"
) {
$vol
= $1 * 1000 * 1000;
}
elsif
($2 eq
"m"
) {
$vol
= $1 / 1000;
}
if
(
$vol
>
$maxvol
) {
carp __PACKAGE__.
" improper volume '$vol'"
;
return
-1;
}
$step
=
int
(
$vol
*
$maxstep
/
$maxvol
);
warn
__PACKAGE__.
" new steps for vol $vol "
.
$step
.
"\n"
if
$Debug
;
return
$step
;
}
sub
tip_aspirate {
my
$self
=
shift
;
my
%param
=
@_
;
$self
->tip_aspirate_type0(
@_
);
}
sub
tip_aspirate_type0 {
my
(
$self
,
%params
) =
@_
;
my
$motor
=
$params
{
"name"
} ||
"liha"
;
my
$tips
=
$params
{
"tips"
} ||
"1"
;
my
$volume
=
$params
{
"volume"
} ||
"10"
;
my
$location
=
$params
{
"at"
} || 0;
my
$liquid
=
$params
{
"liquid"
} ||
"Water"
;
my
$grid
=
$params
{
"grid"
} ||
"11"
;
my
$site
=
$params
{
"site"
} ||
"0"
;
my
$tipdist
=
$params
{
"tipdistance"
} ||
"1"
;
my
$flags
=
$params
{
"flags"
} ||
""
;
my
$flagarg
=
$params
{
"flagarg"
} ||
""
;
my
@tiparray
= _tipStringToArray8(
$tips
);
my
$reply
;
for
my
$tip
(
@tiparray
) {
my
$code
;
my
$position
=
$self
->getPipettePosition(
tip
=>
$tip
);
if
(
$position
< 0) {
warn
__PACKAGE__.
" bad tip position, ignoring operation\n"
;
next
;
}
my
$step
=
$self
->convertVolToSteps(
$volume
);
if
(
$step
< 1) {
carp __PACKAGE__.
" cant aspirate $volume"
;
return
""
;
}
my
$newposition
=
$step
+
$position
;
if
(!
$self
->checkPipetteSteps(
tip
=>
$tip
,
start
=>
$position
,
stop
=>
$newposition
,
dir
=>
'a'
)) {
carp(
"Robotics plunger error, cant aspirate $step steps"
);
return
""
;
}
my
(
$ss
,
$se
,
$cutoff
) =
$self
->getPipetteSpeed(
tip
=>
$tip
);
my
$delay
=
$self
->getPipetteDelay(
tip
=>
$tip
);
$code
=
$self
->compile_xp(
"SET_SPEED"
,
endspeed
=>
$se
,
startspeed
=>
$ss
,
tip
=>
$tip
);
$code
=
$self
->compile_xp(
"ASPIRATE"
,
position
=>
$step
,
tip
=>
$tip
,
chain
=>
$code
);
$code
=
$self
->compile_xp(
"DELAY"
,
msec
=>
$delay
,
tip
=>
$tip
,
chain
=>
$code
);
$code
=
$self
->compile_xp(
"EXECUTE"
,
tip
=>
$tip
,
chain
=>
$code
);
$self
->DATAPATH()->
write
(
$code
)
if
$code
;
my
$replyref
=
$self
->DATAPATH()->EXPECT_RECV();
if
(
$replyref
) {
if
(
$Debug
>1) {
my
$debug_str
= YAML::XS::Dump(
$replyref
);
$debug_str
=~ s/\n/ /g;
warn
__PACKAGE__.
" expected reply $debug_str\n"
;
}
$reply
=
$self
->DATAPATH()->
read
();
warn
__PACKAGE__.
" got reply "
.
$self
->decompile_reply_xp(
$reply
).
"\n"
if
$Debug
;
if
(
$reply
=~ /^[^0]/) {
carp
"Robotics cmd error: $reply\n"
;
return
0;
}
}
}
return
$self
->decompile_reply_xp(
$reply
);
}
sub
tip_aspirate_type2 {
my
$self
=
shift
;
if
(0) {
my
$motor
=
shift
||
"liha"
;
my
$tips
=
shift
||
"1"
;
my
$volume
=
shift
||
"10"
;
my
$location
=
shift
||
'"0C0810000000000000"'
;
my
$liquid
=
shift
||
"Water"
;
my
$grid
=
shift
||
"11"
;
my
$site
=
shift
||
"0"
;
my
$tipdist
=
shift
||
"1"
;
my
$flags
=
shift
||
"0"
;
my
$flagarg
=
shift
||
""
;
my
$tipMask
= _tipStringToMask8(
$tips
);
my
@volumes
=
split
(
","
,
$volume
.
",0,0,0,0,0,0,0,0,0,0"
);
for
$volume
(0..7) {
if
(
$volumes
[
$volume
] > 0) {
$volumes
[
$volume
] .=
'"'
.
$volumes
[
$volume
].
'"'
;
}
}
my
$volumestring
=
join
(
","
,
@volumes
[0..7]);
my
$wellstring
=
$location
;
my
$loopoption
=
"0"
;
my
$loopname
=
""
;
my
$loopaction
=
""
;
my
$loopindex
=
""
;
if
(
$flags
) {
$loopoption
=
$flags
;
}
my
$cmd
=
"B;ASPIRATE("
;
$liquid
=~ s/"//g;
$liquid
=
'"'
.
$liquid
.
'"'
;
if
(
$loopoption
> 0) {
$cmd
.=
join
(
","
, (
$tipMask
,
$liquid
,
$volume
,
$grid
,
$site
,
$tipdist
,
$wellstring
,
$loopoption
,
$loopname
,
$loopaction
,
$loopindex
)).
");"
;
}
else
{
$cmd
.=
join
(
","
, (
$tipMask
,
$liquid
,
$volume
,
$grid
,
$site
,
$tipdist
,
$wellstring
,
"0"
)).
");"
;
}
$self
->Write(
$cmd
);
return
$self
->Read();
}
my
$reply
;
return
$reply
;
}
sub
tip_dispense_type2 {
if
(0) {
my
$motor
=
shift
||
"liha"
;
my
$tips
=
shift
||
"1"
;
my
$volume
=
shift
||
"10"
;
my
$location
=
shift
||
"0C0810000000000000"
;
my
$liquid
=
shift
||
"Water"
;
my
$grid
=
shift
||
"11"
;
my
$site
=
shift
||
"0"
;
my
$tipdist
=
shift
||
"1"
;
my
$flags
=
shift
||
""
;
my
$flagarg
=
shift
||
""
;
my
$tipMask
= _tipStringToMask8(
$tips
);
my
@volumes
=
split
(
","
,
$volume
.
","
x 7);
my
$volumestring
=
join
(
","
,
@volumes
[0..7]);
my
$wellstring
=
$location
;
my
$loopoption
;
my
$loopname
;
my
$loopaction
;
my
$loopindex
;
if
(
$flags
) {
$loopoption
=
$flags
;
}
my
$cmd
=
"B;DISPENSE("
.
join
(
","
, (
$tipMask
,
$liquid
,
$volume
,
$grid
,
$site
,
$tipdist
,
$wellstring
));
if
(
$loopoption
> 0) {
$cmd
.=
join
(
","
, (
$loopoption
,
$loopname
,
$loopaction
,
$loopindex
));
}
else
{
$cmd
.=
"0"
;
}
$cmd
.=
");"
;
}
}
sub
tip_dispense {
my
$self
=
shift
;
my
%param
=
@_
;
return
$self
->tip_dispense_type0(
@_
);
}
sub
tip_dispense_type0 {
my
(
$self
,
%params
) =
@_
;
my
$motor
=
$params
{
"name"
} ||
"liha"
;
my
$tips
=
$params
{
"tips"
} ||
"1"
;
my
$volume
=
$params
{
"volume"
} ||
"10"
;
my
$location
=
$params
{
"at"
} || 0;
my
$liquid
=
$params
{
"liquid"
} ||
"Water"
;
my
$grid
=
$params
{
"grid"
} ||
"11"
;
my
$site
=
$params
{
"site"
} ||
"0"
;
my
$tipdist
=
$params
{
"tipdistance"
} ||
"1"
;
my
$flags
=
$params
{
"flags"
} ||
""
;
my
$flagarg
=
$params
{
"flagarg"
} ||
""
;
my
@tiparray
= _tipStringToArray8(
$tips
);
my
$reply
;
for
my
$tip
(
@tiparray
) {
my
$code
;
my
$position
=
$self
->getPipettePosition(
tip
=>
$tip
);
if
(
$position
< 0) {
warn
__PACKAGE__.
" bad tip position, ignoring operation\n"
;
next
;
}
my
$step
=
$self
->convertVolToSteps(
$volume
);
if
(
$step
< 1) {
carp __PACKAGE__.
" cant dispense $volume"
;
return
""
;
}
my
$newposition
=
$position
-
$step
;
if
(!
$self
->checkPipetteSteps(
tip
=>
$tip
,
start
=>
$position
,
stop
=>
$newposition
,
dir
=>
'd'
)) {
carp(__PACKAGE__.
" plunger error, cant dispense $step steps"
);
return
""
;
}
my
(
$ss
,
$se
,
$cutoff
) =
$self
->getPipetteSpeed(
tip
=>
$tip
);
my
$delay
=
$self
->getPipetteDelay(
tip
=>
$tip
);
$code
=
$self
->compile_xp(
"SET_SPEED_END"
,
speed
=>
$se
,
tip
=>
$tip
);
$code
=
$self
-> compile_xp(
"SET_CUTOFF"
,
steps
=>
$cutoff
,
tip
=>
$tip
,
chain
=>
$code
);
$code
=
$self
->compile_xp(
"DISPENSE"
,
position
=>
$step
,
tip
=>
$tip
,
chain
=>
$code
);
$code
=
$self
->compile_xp(
"DELAY"
,
msec
=>
$delay
,
tip
=>
$tip
,
chain
=>
$code
);
$code
=
$self
->compile_xp(
"EXECUTE"
,
tip
=>
$tip
,
chain
=>
$code
);
$self
->DATAPATH()->
write
(
$code
)
if
$code
;
my
$replyref
=
$self
->DATAPATH()->EXPECT_RECV();
if
(
$replyref
) {
if
(
$Debug
>1) {
my
$debug_str
= YAML::XS::Dump(
$replyref
);
$debug_str
=~ s/\n/ /g;
warn
__PACKAGE__.
" expected reply $debug_str\n"
;
}
$reply
=
$self
->DATAPATH()->
read
();
warn
__PACKAGE__.
" got reply "
.
$self
->decompile_reply_xp(
$reply
).
"\n"
if
$Debug
;
if
(
$reply
=~ /^[^0]/) {
carp
"Robotics cmd error: $reply\n"
;
return
0;
}
}
}
return
$self
->decompile_reply_xp(
$reply
);
}
sub
tip_set_supply {
my
$self
=
shift
;
my
$type
=
shift
||
"1"
;
my
$grid
=
shift
||
"1"
;
my
$site
=
shift
||
"0"
;
my
$position
=
shift
||
"0"
;
my
$cmd
=
join
(
";"
, (
"set_diti"
,
$type
,
$grid
,
$site
,
$position
));
$self
->Write(
$cmd
);
return
$self
->Read();
}
sub
tip_query {
my
$self
=
shift
;
my
$type
=
shift
||
"1"
;
my
$cmd
=
join
(
";"
, (
"get_diti"
,
$type
));
$self
->Write(
$cmd
);
return
$self
->Read();
}
sub
tip_query_usage {
my
$self
=
shift
;
my
$type
=
shift
||
"1"
;
my
$cmd
=
join
(
";"
, (
"get_used_ditis"
,
$type
));
$self
->Write(
$cmd
);
return
$self
->Read();
}
sub
tip_pause {
my
$self
=
shift
;
$self
->Write(
"pause_pipetting"
);
return
$self
->Read();
}
sub
tip_coupleWorklist {
my
$self
=
shift
;
my
$motor
=
shift
||
"liha"
;
my
$tiparg
=
shift
||
"1"
;
my
$type
=
shift
|| 0;
my
$flag
=
shift
|| 0;
my
$tipmask
= _tipStringToMask8(
$tiparg
);
my
$cmd
=
"B;GetDITI("
.
join
(
','
, (
$tipmask
,
$type
,
$flag
)) .
");"
;
$cmd
=
'LOAD_WORKLIST;Worklist(0,c:\temp\genesis.gwl,15,"Water");'
.
'Wash(1,255,255,255,255,"2.0",500,"1.0",500,10,70,30,0,0,1000);'
;
$self
->Write(
$cmd
);
my
$reply
=
$self
->Read();
sleep
(5);
return
$reply
;
}
sub
tip_coupleFirmware {
my
$self
=
shift
;
my
$tiparg
=
shift
||
"1"
;
my
$type
=
shift
|| 0;
my
$flag
=
shift
|| 1;
my
$tipmask
= _tipStringToMask8(
$tiparg
);
my
$cmd
=
"#A1AGT"
.
join
(
','
, (
$tipmask
,
$type
,
$flag
)) .
");"
;
$self
->Write(
$cmd
);
return
$self
->Read();
}
sub
tip_uncoupleFirmware {
my
$self
=
shift
;
my
$tiparg
=
shift
||
"1"
;
my
$type
=
shift
|| 0;
my
$flag
=
shift
|| 1;
my
$tipmask
= _tipStringToMask8(
$tiparg
);
my
$cmd
=
"#A1AGT"
.
$tipmask
;
$self
->Write(
$cmd
);
return
$self
->Read();
}
sub
_tipStringToMask8 {
my
$s
=
shift
||
"1"
;
return
$s
=~ m/all/i ? 255 : _convertStringRangeToMask8(
$s
, 1);
}
sub
_convertStringRangeToMask8 {
my
$index
=
pop
;
my
$s
=
shift
;
$s
=~ s/(\d+)-(\d+)/
join
','
, $1 .. $2/eg;
return
_convertArrayToMask8(
$s
=~ /\d+/g,
$index
);
}
sub
_convertArrayToMask8 {
my
$n
= 0;
my
$base
=
pop
@_
;
my
@bits
=
@_
;
$n
|= 1 << (
$_
-
$base
)
for
grep
{
$_
>=
$base
&&
$_
<= 7+
$base
}
@bits
;
return
$n
;
}
sub
_tipStringToArray8 {
my
$s
=
shift
||
"1"
;
if
(
$s
=~ /all/i) {
return
(1,2,3,4,5,6,7,8);
}
$s
=~ s/(\d+)-(\d+)/
join
','
, $1 .. $2/eg;
return
(
$s
=~ /\d+/g);
}
sub
_convertWellToXY {
my
(
$self
,
%param
) =
@_
;
my
%coord
;
if
(
$param
{
"wellname"
}) {
(
$coord
{
"x"
},
$coord
{
"y"
}) =
Robotics::convertWellStringToXY(
$param
{
"wellname"
});
}
elsif
(
$param
{
"wellnum"
}) {
(
$coord
{
"x"
},
$coord
{
"y"
}) =
Robotics::convertWellNumberToXY(
$param
{
"wellnum"
});
}
if
(
$coord
{
"x"
} < 1 ||
$coord
{
"y"
} < 1) {
carp
"cant calculate well number from "
.
$param
{
"wellname"
}.
$param
{
"wellnum"
};
return
undef
;
}
$coord
{
"ys"
} =
$param
{
"tips"
} || 1;
return
%coord
;
}
1;