File Coverage

blib/lib/Lab/Instrument/ProStep4.pm
Criterion Covered Total %
statement 11 296 3.7
branch 0 86 0.0
condition 0 54 0.0
subroutine 4 21 19.0
pod 1 15 6.6
total 16 472 3.3


line stmt bran cond sub pod time code
1             package Lab::Instrument::ProStep4;
2             #ABSTRACT: ProStep4 step motor
3             $Lab::Instrument::ProStep4::VERSION = '3.880';
4 1     1   1754 use v5.20;
  1         4  
5              
6 1     1   6 use strict;
  1         2  
  1         31  
7 1     1   5 use Time::HiRes qw/usleep/, qw/time/;
  1         12  
  1         6  
8 1     1   103 use Lab::Instrument;
  1         2  
  1         3984  
9              
10             our $AXIS = 1;
11             our $RESOLUTION = 1024;
12             our $GETRIEBEMULTIPLIKATOR = 0.015;
13             our $NULL = 32768;
14             our $STEPS_PER_ROUND = 400;
15             our $BACKLASH = 200;
16             our $AA = 200;
17             our $AE = 200;
18             our $VA = 0;
19             our $VE = 30;
20             our $VM = 30;
21              
22             our %fields = (
23             supported_connections => [ 'VISA', 'VISA_RS232', 'RS232', 'DEBUG' ],
24             connection_settings => {
25             baudrate => 9600,
26             databits => 8,
27             stopbits => 1,
28             parity => 'none',
29             handshake => 'none',
30             termchar => '\r',
31             timeout => 2,
32             inipath => '',
33             logpath => '',
34             },
35              
36             device_settings => {
37             read_default => 'device',
38             pos_mode => 'ABS',
39             speed_max => 180,
40             upper_limit => 180,
41             lower_limit => -180,
42              
43             },
44              
45             device_cache => {
46             position => undef,
47             target => undef,
48             },
49              
50             device_cache_order => ['id'],
51             );
52              
53             our @ISA = ("Lab::Instrument");
54              
55             sub new {
56 0     0 1   my $proto = shift;
57 0   0       my $class = ref($proto) || $proto;
58 0           my $self = $class->SUPER::new(@_);
59 0           $self->${ \( __PACKAGE__ . '::_construct' ) }(__PACKAGE__);
  0            
60              
61             #$self->init();
62              
63 0           $self->{active} = 0;
64 0           return $self;
65             }
66              
67             sub _device_init {
68 0     0     my $self = shift;
69              
70 0           $self->query("C:\r\n");
71 0           $self->InitEncoder( $STEPS_PER_ROUND, $RESOLUTION, $NULL );
72 0           $self->InitRamp( $AXIS, $AA, $AE, $VA, $VE, $VM );
73 0           $self->clear();
74              
75 0           $self->init_limits();
76             }
77              
78             sub InitEncoder {
79 0     0 0   my $self = shift;
80 0           my ( $steps, $res, $null )
81             = $self->_check_args( \@_, [ 'steps', 'res', 'null' ] );
82              
83 0           $self->write("encoder: $steps $res $null\r\n");
84 0           my $result = $self->read( { read_length => 300 } );
85              
86 0           return $result;
87             }
88              
89             sub InitRamp {
90 0     0 0   my $self = shift;
91              
92 0           my ( $axis, $aa, $ae, $va, $ve, $vm )
93             = $self->_check_args( \@_, [ 'axis', 'aa', 'ae', 'va', 've', 'vm' ] );
94              
95 0           $self->write("tp: $axis $aa $ae $va $ve $vm\r\n");
96 0           my $result = $self->read( { read_length => 100 } );
97              
98 0           return $result;
99             }
100              
101             sub move {
102 0     0 0   my $self = shift;
103              
104 0           my ( $position, $speed, $mode )
105             = $self->_check_args( \@_, [ 'position', 'speed', 'mode' ] );
106              
107 0 0         if ( not defined $mode ) {
108 0           $mode = $self->device_settings()->{pos_mode};
109             }
110 0 0         if ( not $mode =~ /ABS|abs|REL|rel/ ) {
111 0           Lab::Exception::CorruptParameter->throw( error =>
112             "unexpected value for <MODE> in sub move. expected values are ABS and REL."
113             );
114             }
115 0 0         if ( not defined $speed ) {
116 0           $speed = $self->device_settings()->{speed_max};
117             }
118 0 0         if ( not defined $position ) {
    0          
119 0           Lab::Exception::CorruptParameter->throw(
120             error => $self->get_id() . ": No target given in sub move! " );
121             }
122             elsif (
123             not $position =~ /^([+-]?)(?=\d|\.\d)\d*(\.\d*)?([Ee]([+-]?\d+))?$/ )
124             {
125 0           Lab::Exception::CorruptParameter->throw( error => $self->get_id()
126             . ": Illegal Value given for POSITION in sub move!" );
127             }
128              
129             # this sets the upper limit for the positioning speed:
130 0           $speed = abs($speed);
131 0 0         if ( $speed > $self->device_settings()->{speed_max} ) {
132 0           print new Lab::Exception::CorruptParameter( error =>
133             "Warning in sub move: <SPEED> = $speed is too high. Reduce <SPEED> to its maximum value defined by internal limit settings of $self->device_settings()->{speed_max}"
134             );
135 0           $speed = $self->device_settings()->{speed_max};
136             }
137              
138 0           $speed = $self->angle2steps($speed) / 60;
139              
140             # Moving in ABS or REL mode:
141 0           my $CP = $self->get_position(); # get current position
142 0 0 0       if ( $mode eq "ABS"
    0 0        
      0        
      0        
      0        
      0        
143             or $mode eq "abs"
144             or $mode eq "ABSOLUTE"
145             or $mode eq "absolute" ) {
146 0 0 0       if ( $position < $self->device_settings()->{lower_limit}
147             or $position > $self->device_settings()->{upper_limit} ) {
148             Lab::Exception::CorruptParameter->throw( error =>
149             "unexpected value for NEW POSITION in sub move. Expected values are between "
150             . $self->device_settings()->{lower_limit} . " ... "
151 0           . $self->device_settings()->{upper_limit} );
152             }
153 0           $self->device_cache()->{target} = $position;
154 0           $self->_save_motorlog( $CP, $position );
155 0           $self->save_motorinitdata();
156 0           $self->query(
157             "ma$AXIS " . $self->angle2steps($position) . " $speed\r\n" );
158             }
159             elsif ($mode eq "REL"
160             or $mode eq "rel"
161             or $mode eq "RELATIVE"
162             or $mode eq "relative" ) {
163 0 0 0       if ( $CP + $position < $self->device_settings()->{lower_limit}
164             or $CP + $position > $self->device_settings()->{upper_limit} ) {
165             Lab::Exception::CorruptParameter->throw( error =>
166             "ERROR in sub move.Can't execute move; TARGET POSITION ("
167             . ( $CP + $position )
168             . ") is out of valid limits ("
169             . $self->device_settings()->{lower_limit} . " ... "
170             . $self->device_settings()->{upper_limit}
171 0           . ")" );
172             }
173 0           $self->device_cache()->{target} = $CP + $position;
174 0           $self->_save_motorlog( $CP, $CP + $position );
175 0           $self->save_motorinitdata();
176 0           $self->query(
177             "mr$AXIS " . $self->angle2steps($position) . " $speed\r\n" );
178              
179             }
180              
181 0           return 1;
182              
183             }
184              
185             sub active {
186 0     0 0   my $self = shift;
187              
188 0           my $result = $self->get_position();
189              
190 0           return $self->{active};
191              
192             }
193              
194             sub wait {
195 0     0 0   my $self = shift;
196              
197 0           my $flag = 1;
198 0           local $| = 1;
199              
200 0           while ( $self->active() ) {
201 0           my $current = $self->device_cache()->{position};
202 0 0 0       if ( $flag <= 1.1 and $flag >= 0.9 ) {
    0          
203 0           print $self->get_id()
204             . sprintf( " is sweeping (%.2f\370)\r", $current );
205             }
206             elsif ( $flag <= 0 ) {
207 0           print $self->get_id()
208             . sprintf( " is (%.2f\370)\r", $current );
209 0           $flag = 2;
210             }
211 0           $flag -= 0.1;
212 0           usleep(2e3);
213             }
214              
215 0           print "\t\t\t\t\t\t\t\t\t\r";
216 0           $| = 0;
217              
218 0           return 1;
219              
220             }
221              
222             sub abort {
223 0     0 0   my ($self) = @_;
224 0           while (1) {
225 0           $self->query("sa$AXIS\r\n");
226 0 0         if ( not $self->active() ) {
227 0           last;
228             }
229             }
230 0           print "Motor stoped at " . $self->get_position() . "\n";
231 0           return;
232             }
233              
234             sub init_limits {
235 0     0 0   my $self = shift;
236 0           my $lowerlimit;
237             my $upperlimit;
238              
239 0 0         if ( $self->read_motorinitdata() ) {
240 0           while (1) {
241 0           print
242             "Motor-Init data found. Do you want to keep the reference point and the limits? (y/n) ";
243 0           my $input = <>;
244 0           chomp $input;
245 0 0         if ( $input =~ /YES|yes|Y|y/ ) {
    0          
246 0           return 1;
247             }
248             elsif ( $input =~ /NO|no|N|n/ ) {
249 0           my $result = $self->query("sa$AXIS\r\n");
250 0           my $result = $self->query("nullen\r\n");
251 0           $self->device_settings->{lower_limit} = -360;
252 0           $self->device_settings->{upper_limit} = 360;
253 0           last;
254             }
255             }
256             }
257              
258 0           print "\n\n";
259 0           print "----------------------------------------------\n";
260 0           print "----------- Init Motor ProStep4 -------------\n";
261 0           print "----------------------------------------------\n";
262 0           print "\n";
263 0           print
264             "This procedure will help you to initialize the Motor ProStep4 correctly.\n\n";
265 0           print "Steps to go:\n";
266 0           print " 1.) Define the REFERENCE POINT.\n";
267 0           print " 2.) Define the LOWER and UPPER LIMITS for rotation.\n";
268 0           print " 3.) Confirm the LOWER and UPPER LIMITS.\n";
269 0           print "\n\n";
270              
271 0           print "----------------------------\n";
272 0           print "1.) Define the REFERENCE POINT:\n\n";
273 0           print "--> Move the motor position to the REFERENCE POINT.\n";
274 0           print "--> Enter a (relative) angle between -180 ... +180 deg.\n";
275 0           print
276             "--> Repeat until you have reached the position you want to define as the REFERENCE POINT.\n";
277 0           print
278             "--> Enter 'REF' to confirm the actual position as the REFERENCE POINT.\n\n";
279              
280 0           while (1) {
281 0           print "MOVE: ";
282 0           my $value = <>;
283 0           chomp $value;
284 0 0 0       if ( $value eq "REF" or $value eq "ref" ) {
    0 0        
      0        
285              
286             # set actual position as reference point Zero
287 0           $self->device_cache()->{position} = 0; # for testing only
288 0           my $result = $self->query("sa$AXIS\r\n");
289 0           my $result = $self->query("nullen\r\n");
290 0           last;
291             }
292             elsif ( $value =~ /^[+-]?\d+$/ and $value >= -180 and $value <= 180 )
293             {
294 0           $self->move( $value, { mode => 'REL' } );
295 0           $self->wait();
296             }
297             else {
298 0           print
299             "Please move the motor position to the REFERENCE POINT. Enter an angle between -188\370 ... +180\370.\n";
300             }
301             }
302              
303 0           print "----------------------------\n";
304 0           print "2.) Define the LOWER and UPPER LIMITS for rotation:\n\n";
305 0           print "--> Enter LOWER LIMIT\n";
306 0           print "--> Enter UPPER LIMIT\n\n";
307              
308 0           while (1) {
309 0           print "LOWER LIMIT: ";
310 0           my $value = <>;
311 0           chomp $value;
312 0           $lowerlimit = $value;
313 0           $self->device_settings()->{lower_limit} = $lowerlimit;
314 0           print "UPPER LIMIT: ";
315 0           my $value = <>;
316 0           chomp $value;
317 0           $upperlimit = $value;
318 0           $self->device_settings()->{upper_limit} = $upperlimit;
319              
320 0 0         if ( $lowerlimit < $upperlimit ) {
321 0           last;
322             }
323             else {
324 0           print "LOWER LIMIT >= UPPER LIMIT. Try again!\n";
325             }
326             }
327              
328 0           print "----------------------------\n";
329 0           print "3.) Confirm the LOWER and UPPER LIMITS:\n\n";
330 0           print "--> Motor will move to LOWER LIMIT in steps of 10 deg\n";
331 0           print "--> Motor will move to UPPER LIMIT in steps of 10 deg\n";
332 0           print
333             "--> Confirm each step with ENTER or type <STOP> to take the actual position as the limit value. \n\n";
334              
335 0           print "Moving to LOWER LIMIT ...\n";
336 0           while (1) {
337 0           print "MOVE +/-10: Please press <ENTER> to confirm.";
338 0           my $input = <>;
339 0           chomp $input;
340 0 0         if ( $input =~ /stop|STOP/ ) {
341 0           $lowerlimit = $self->get_position();
342 0           last;
343             }
344 0 0         if ( abs( $self->get_position() - $lowerlimit ) >= 10 ) {
345 0 0         if ( $lowerlimit <= 0 ) {
346 0           $self->move( -10, { mode => 'REL' } );
347 0           $self->wait();
348             }
349             else {
350 0           $self->move( 10, { mode => 'REL' } );
351 0           $self->wait();
352             }
353             }
354             else {
355 0           $self->move( $lowerlimit, { mode => 'ABS' } );
356 0           $self->wait();
357 0           last;
358             }
359              
360             }
361 0           print "Reached LOWER LIMIT\n";
362 0           print "Please confirm the position of the LOWER LIMIT: ";
363 0           <>;
364 0           $self->device_settings()->{'lower_limit'} = $lowerlimit;
365 0           print "\n\n";
366 0           print "Moving to REFERENCE POINT ... \n";
367 0           $self->move( 0, { mode => 'ABS' } ) . "\n";
368 0           $self->wait();
369 0           print "Moving to UPPER LIMIT ...\n";
370              
371 0           while (1) {
372 0           print "MOVE +/-10: Please press <ENTER> to confirm.";
373 0           my $input = <>;
374 0           chomp $input;
375 0 0         if ( $input =~ /stop|STOP/ ) {
376 0           $upperlimit = $self->get_position();
377 0           last;
378             }
379 0 0         if ( abs( $upperlimit - $self->get_position() ) >= 10 ) {
380 0           $self->move( 10, { mode => 'REL' } );
381 0           $self->wait();
382             }
383             else {
384 0           $self->move( $upperlimit, { mode => 'ABS' } );
385 0           $self->wait();
386 0           last;
387             }
388              
389             }
390 0           print "Reached UPPER LIMIT\n";
391 0           print "Please confirm the position of the UPPER LIMIT: ";
392 0           <>;
393 0           $self->device_settings()->{'upper_limit'} = $upperlimit;
394 0           print "\n\n";
395 0           $self->save_motorinitdata();
396              
397 0           print "moving to the reference point.\n";
398 0           $self->move( 0, { mode => 'ABS' } );
399 0           $self->wait();
400 0           print "------------------------------------------------------\n";
401 0           print "------------ Motor ProStep4 initialized --------------\n";
402 0           print "------------------------------------------------------\n";
403 0           print "\n\n";
404              
405             }
406              
407             sub steps2angle {
408 0     0 0   my $self = shift;
409 0           my ($steps) = $self->_check_args( \@_, ['value'] );
410              
411 0           my $angle = $steps * $GETRIEBEMULTIPLIKATOR;
412 0           return $angle;
413             }
414              
415             sub angle2steps {
416 0     0 0   my $self = shift;
417 0           my ($angle) = $self->_check_args( \@_, ['value'] );
418              
419 0           my $steps = $angle / $GETRIEBEMULTIPLIKATOR;
420 0           return sprintf( "%0.f", $steps );
421              
422             }
423              
424             sub get_value {
425 0     0 0   my $self = shift;
426 0           my ($read_mode) = $self->_check_args( \@_, ['read_mode'] );
427 0           return $self->get_position( { read_mode => $read_mode } );
428             }
429              
430             sub get_position {
431 0     0 0   my $self = shift;
432 0           my ($read_mode) = $self->_check_args( \@_, ['read_mode'] );
433              
434 0           my $cmd = "a" . $AXIS . "?\r\n";
435 0           my $result;
436              
437 0 0 0       if ( not defined $read_mode
438             or not $read_mode =~ /device|cache|request|fetch/ ) {
439 0           $read_mode = $self->device_settings()->{read_default};
440             }
441              
442 0 0 0       if ( $read_mode eq 'cache'
    0 0        
    0 0        
    0 0        
443             and defined $self->{'device_cache'}->{'position'} ) {
444 0           return $self->{'device_cache'}->{'position'};
445             }
446             elsif ( $read_mode eq 'request' and $self->{request} == 0 ) {
447 0           $self->{request} = 1;
448 0           $self->write($cmd);
449 0           return;
450             }
451             elsif ( $read_mode eq 'request' and $self->{request} == 1 ) {
452 0           $result = $self->read();
453 0           $self->write($cmd);
454 0           return;
455             }
456             elsif ( $read_mode eq 'fetch' and $self->{request} == 1 ) {
457 0           $self->{request} = 0;
458 0           $result = $self->read();
459             }
460             else {
461 0 0         if ( $self->{request} == 1 ) {
462 0           $result = $self->read();
463 0           $self->{request} = 0;
464 0           $result = $self->query($cmd);
465             }
466             else {
467 0           $result = $self->query($cmd);
468             }
469             }
470              
471 0           for ( 0 .. 2 ) {
472 0 0         if ( $result =~ m/Posi_$AXIS:\s+([+-]?\d+)/ ) {
    0          
473 0           $self->device_cache()->{position} = $self->steps2angle($1);
474 0           $self->{active} = 0;
475 0           last;
476             }
477             elsif ( $result
478             =~ m/Soll\/Ist\/Speed_$AXIS:\s+([+-]?\d+)\s+([+-]?\d+)\s+([+-]?\d+)/
479             ) {
480 0           $self->device_cache()->{position} = $self->steps2angle($2);
481 0           $self->{active} = 1;
482 0           last;
483             }
484             else {
485 0           $result
486             = $self->connection()->BrutalRead( { read_length => 100 } );
487             }
488             }
489              
490 0           $self->save_motorinitdata();
491 0           $self->{value} = $self->device_cache()->{position};
492 0           return $self->device_cache()->{position};
493              
494             }
495              
496             sub save_motorinitdata {
497 0     0 0   my $self = shift;
498              
499 0           open( DUMP, ">C:\\Perl\\site\\lib\\Lab\\Instrument\\ProStep4.ini" )
500             ; #open for write, overwrite
501              
502 0           print DUMP "POSITION: " . $self->device_cache()->{position} . "\n";
503 0           print DUMP "TARGET: " . $self->device_cache()->{target} . "\n";
504 0           print DUMP "SPEED_MAX: " . $self->device_settings()->{speed_max} . "\n";
505             print DUMP "UPPER_LIMIT: "
506 0           . $self->device_settings()->{upper_limit} . "\n";
507             print DUMP "LOWER_LIMIT: "
508 0           . $self->device_settings()->{lower_limit} . "\n";
509 0           print DUMP "TIMESTAMP: " . time() . "\n";
510              
511 0           close(DUMP);
512             }
513              
514             sub _save_motorlog {
515 0     0     my $self = shift;
516 0           my ( $init_pos, $end_pos )
517             = $self->_check_args( \@_, [ 'init_pos', 'end_pos' ] );
518 0           open( DUMP, ">>C:\\Perl\\site\\lib\\Lab\\Instrument\\ProStep4.log" )
519             ; #open for write, overwrite
520              
521 0           print DUMP ( my_timestamp() ) . "\t move: $init_pos -> $end_pos \n";
522              
523 0           close(DUMP);
524             }
525              
526             sub read_motorinitdata {
527 0     0 0   my $self = shift;
528              
529 0 0         if (
530             not
531             open( DUMP, "<C:\\Perl\\site\\lib\\Lab\\Instrument\\ProStep4.ini" ) )
532             {
533 0           return 0;
534             }
535 0           while (<DUMP>) {
536 0           chomp($_);
537 0           my @line = split( /: /, $_ );
538 0 0         if ( $line[0] eq 'POSITION' ) {
    0          
    0          
    0          
    0          
539 0           $self->device_cache()->{position} = $line[1];
540             }
541             elsif ( $line[0] eq 'TARGET' ) {
542 0           $self->device_cache()->{target} = $line[1];
543             }
544             elsif ( $line[0] eq 'SPEED_MAX' ) {
545 0           $self->device_settings()->{speed_max} = $line[1];
546             }
547             elsif ( $line[0] eq 'UPPER_LIMIT' ) {
548 0           $self->device_settings()->{upper_limit} = $line[1];
549             }
550             elsif ( $line[0] eq 'LOWER_LIMIT' ) {
551 0           $self->device_settings()->{lower_limit} = $line[1];
552             }
553              
554             }
555              
556 0           print "\nread MOTOR-INIT-DATA\n";
557 0           print "--------------------\n";
558 0           print "POSITION: " . $self->device_cache()->{position} . "\n";
559 0           print "TARGET: " . $self->device_cache()->{target} . "\n";
560 0           print "SPEED_MAX: " . $self->device_settings()->{speed_max} . "\n";
561 0           print "UPPER_LIMIT: " . $self->device_settings()->{upper_limit} . "\n";
562 0           print "LOWER_LIMIT: " . $self->device_settings()->{lower_limit} . "\n";
563 0           print "--------------------\n";
564 0           return 1;
565             }
566              
567             sub my_timestamp {
568              
569             my (
570 0     0 0   $Sekunden, $Minuten, $Stunden, $Monatstag, $Monat,
571             $Jahr, $Wochentag, $Jahrestag, $Sommerzeit
572             ) = localtime(time);
573              
574 0           $Monat += 1;
575 0           $Jahrestag += 1;
576 0 0         $Monat = $Monat < 10 ? $Monat = "0" . $Monat : $Monat;
577 0 0         $Monatstag = $Monatstag < 10 ? $Monatstag = "0" . $Monatstag : $Monatstag;
578 0 0         $Stunden = $Stunden < 10 ? $Stunden = "0" . $Stunden : $Stunden;
579 0 0         $Minuten = $Minuten < 10 ? $Minuten = "0" . $Minuten : $Minuten;
580 0 0         $Sekunden = $Sekunden < 10 ? $Sekunden = "0" . $Sekunden : $Sekunden;
581 0           $Jahr += 1900;
582              
583 0           return "$Stunden:$Minuten:$Sekunden $Monatstag.$Monat.$Jahr\n";
584              
585             }
586              
587             1;
588              
589             __END__
590              
591             =pod
592              
593             =encoding UTF-8
594              
595             =head1 NAME
596              
597             Lab::Instrument::ProStep4 - ProStep4 step motor
598              
599             =head1 VERSION
600              
601             version 3.880
602              
603             =head1 COPYRIGHT AND LICENSE
604              
605             This software is copyright (c) 2023 by the Lab::Measurement team; in detail:
606              
607             Copyright 2013 Christian Butschkow, Stefan Geissler
608             2014-2015 Christian Butschkow
609             2016 Simon Reinhardt
610             2017 Andreas K. Huettel
611             2020 Andreas K. Huettel
612              
613              
614             This is free software; you can redistribute it and/or modify it under
615             the same terms as the Perl 5 programming language system itself.
616              
617             =cut