File Coverage

blib/lib/Lab/Connection/Trace.pm
Criterion Covered Total %
statement 29 111 26.1
branch 0 46 0.0
condition 0 3 0.0
subroutine 10 19 52.6
pod 8 8 100.0
total 47 187 25.1


line stmt bran cond sub pod time code
1             package Lab::Connection::Trace;
2             #ABSTRACT: Trace communication with instruments
3             $Lab::Connection::Trace::VERSION = '3.880';
4 1     1   1919 use v5.20;
  1         3  
5              
6 1     1   6 use warnings;
  1         3  
  1         25  
7 1     1   5 use strict;
  1         3  
  1         19  
8              
9 1     1   5 use Role::Tiny;
  1         3  
  1         7  
10              
11 1     1   180 use YAML::XS;
  1         3  
  1         52  
12 1     1   7 use Data::Dumper;
  1         3  
  1         49  
13 1     1   7 use autodie;
  1         3  
  1         7  
14 1     1   5717 use Carp;
  1         3  
  1         77  
15 1     1   13 use IO::File;
  1         4  
  1         194  
16 1     1   13 use Exporter 'import';
  1         3  
  1         1989  
17              
18             our @EXPORT = qw( OpenTraceFile Comment SetRun StartRun StopRun
19             NextRun NextEvent MuteTrace );
20              
21             our $TraceChannels = 0;
22             our $TraceFH;
23             our $TraceRun = 0;
24             our $TraceEvent = 0;
25             our $TraceMute = 0; # global mute
26              
27             #our $TraceFile;
28              
29              
30             sub OpenTraceFile {
31 0     0 1   my $file = shift;
32 0 0         $file = shift
33             if ( ref($file) ne '' ); # in case called $self->OpenTraceFile
34              
35 0           $Lab::Connection::Trace::TraceChannels = 0;
36 0           $Lab::Connection::Trace::TraceFH = IO::File->new("> $file");
37              
38             # $Trace::TraceFile = $file;
39             }
40              
41              
42             around 'new' => sub {
43             my $orig = shift;
44             my $proto = shift;
45             my $class = ref($proto) || $proto;
46             my $twin = undef;
47              
48             # getting fields and _permitted from parent class
49             my $self = $class->$orig(@_);
50              
51             $self->_construct($class);
52              
53             if ( defined($Lab::Connection::Trace::TraceFH) ) {
54             $Lab::Connection::Trace::TraceChannels++;
55             $self->{TraceChan} = $Lab::Connection::Trace::TraceChannels;
56             $self->{TraceMute} = 0; # per-channel mute
57              
58             # this is where the info about what instrument is
59             # using which connection is written.
60              
61             my $parent = $class;
62             $parent =~ s/::Trace$//;
63             local $Data::Dumper::Terse = 1;
64             local $Data::Dumper::Indent = 0;
65             local $Data::Dumper::Useqq = 1;
66              
67             #$self->_trace('*',"new $parent (".Dumper(@_).")");
68              
69             for ( my $i = 0; $i < 10; $i++ ) {
70             my ( $pack, $file, $line, $subr ) = caller($i);
71             next unless $subr =~ /^Lab::Instrument::(\w+)::new$/i;
72             $self->_trace( '*', "$subr (" . Dumper(@_) . ")" );
73             last;
74             }
75              
76             }
77              
78             return $self;
79              
80             };
81              
82             around Write => sub {
83             my $orig = shift;
84             my $self = shift;
85             my $options = undef;
86             if ( ref $_[0] eq 'HASH' ) { $options = shift }
87             else { $options = {@_} }
88             $self->_trace( '>', $options->{command} );
89             return $self->$orig($options);
90             };
91              
92             around Read => sub {
93             my $orig = shift;
94             my $self = shift;
95             my $options = undef;
96             if ( ref $_[0] eq 'HASH' ) { $options = shift }
97             else { $options = {@_} }
98             my $retval = $self->$orig($options);
99             $self->_trace( '<', $retval );
100             return $retval;
101             };
102              
103             around BrutalRead => sub {
104             my $orig = shift;
105             my $self = shift;
106             my $options = undef;
107             if ( ref $_[0] eq 'HASH' ) { $options = shift }
108             else { $options = {@_} }
109              
110             my $retval = $self->$orig($options);
111             $self->_trace( '<', $retval );
112             return $retval;
113              
114             };
115              
116             around Clear => sub {
117             my $orig = shift;
118             my $self = shift;
119             $self->_trace( '*', 'CLEAR' );
120              
121             return $self->$orig(@_);
122             };
123              
124             sub _trace {
125 0     0     my $self = shift;
126 0           my $direction = shift;
127 0           my $text = shift;
128             return
129             unless defined($Lab::Connection::Trace::TraceFH)
130 0 0 0       && defined( $self->{TraceChan} );
131 0 0         return unless defined($text);
132 0 0         return if $self->{TraceMute};
133 0 0         return if $Lab::Connection::Trace::TraceMute;
134              
135             # text could be binary, encapsulate if needed
136 0           local $Data::Dumper::Terse = 1;
137 0           local $Data::Dumper::Indent = 0;
138 0           local $Data::Dumper::Useqq = 1;
139              
140             print $Lab::Connection::Trace::TraceFH sprintf(
141 0           '%02d%s%s' . "\n", $self->{TraceChan}, $direction,
142             Dumper($text)
143             );
144             }
145              
146              
147             sub Comment {
148 0     0 1   my $self = shift;
149 0           my $text;
150 0           my $chan = 0;
151              
152             # fail quietly if trace not set up
153              
154 0 0         if ( ref($self) ne '' ) {
155 0           $chan = $self->{TraceChan};
156 0 0         return unless defined $chan;
157 0           $text = shift;
158             }
159             else {
160 0           $text = $self;
161             }
162 0 0         return unless defined $text;
163              
164 0 0         return unless defined $Lab::Connection::Trace::TraceFH;
165              
166 0           chomp($text);
167 0           local $Data::Dumper::Terse = 1;
168 0           local $Data::Dumper::Indent = 0;
169 0           local $Data::Dumper::Useqq = 1;
170 0           print $Lab::Connection::Trace::TraceFH
171             sprintf( '%02d%s%s' . "\n", $chan, '|', Dumper($text) );
172             }
173              
174              
175              
176             sub SetRun {
177 0     0 1   my $run = shift;
178 0 0         $run = shift if ref($run) ne ''; # in case of $self->SetRun($run);
179              
180 0           $Lab::Connection::Trace::TraceRun = $run - 1; # increment when started
181 0           $Lab::Connection::Trace::TraceEvent = 0;
182             }
183              
184              
185             sub StartRun {
186 0 0   0 1   return unless defined $Lab::Connection::Trace::TraceFH;
187              
188 0           my $text = shift;
189 0 0         $text = '' unless defined($text);
190 0 0         $text = shift if ( ref($text) ne '' ); # $self->RunStart($text)
191 0           my $run = shift;
192 0 0         if ( !defined($run) ) {
193 0           $run = ++$Lab::Connection::Trace::TraceRun;
194             }
195 0           $Lab::Connection::Trace::TraceEvent = 0;
196 0           local $Data::Dumper::Terse = 1;
197 0           local $Data::Dumper::Indent = 0;
198 0           local $Data::Dumper::Useqq = 1;
199              
200 0           $text = sprintf(
201             'START RUN%04d @%d %s',
202             $run, time(), $text
203             );
204 0           print $Lab::Connection::Trace::TraceFH
205             sprintf( '%02d%s%s' . "\n", 0, '*', Dumper($text) );
206             }
207              
208              
209             sub StopRun {
210 0 0   0 1   return unless defined $Lab::Connection::Trace::TraceFH;
211              
212 0           my $run = $Lab::Connection::Trace::TraceRun;
213 0           my $event = $Lab::Connection::Trace::TraceEvent;
214 0           local $Data::Dumper::Terse = 1;
215 0           local $Data::Dumper::Indent = 0;
216 0           local $Data::Dumper::Useqq = 1;
217              
218 0           my $text = sprintf(
219             'STOP RUN%04d after %d events @%d',
220             $run, $event, time()
221             );
222 0           print $Lab::Connection::Trace::TraceFH
223             sprintf( '%02d%s%s' . "\n", 0, '*', Dumper($text) );
224             }
225              
226              
227             sub NextRun {
228 0     0 1   my $comment = shift;
229 0 0         $comment = '' unless defined $comment;
230 0 0         $comment = shift unless ref($comment) eq '';
231              
232 0 0         return unless defined $Lab::Connection::Trace::TraceFH;
233 0           StopRun();
234 0           StartRun($comment);
235             }
236              
237              
238             sub NextEvent {
239 0 0   0 1   return unless defined $Lab::Connection::Trace::TraceFH;
240              
241 0           my $ev = ++$Lab::Connection::Trace::TraceEvent;
242 0           my $run = $Lab::Connection::Trace::TraceRun;
243              
244 0           local $Data::Dumper::Terse = 1;
245 0           local $Data::Dumper::Indent = 0;
246 0           local $Data::Dumper::Useqq = 1;
247 0           my $text = sprintf(
248             'EVENT %04d RUN%04d @%d',
249             $ev, $run, time()
250             );
251 0           print $Lab::Connection::Trace::TraceFH
252             sprintf( '%02d%s%s' . "\n", 0, '*', Dumper($text) );
253 0           return $ev;
254             }
255              
256              
257             sub MuteTrace {
258 0     0 1   my $self = shift;
259 0           my $in = $self;
260 0 0         $in = shift if ref($self) ne '';
261              
262 0           my $mute;
263 0 0         if ( $in =~ /^\s*(T|Y|ON|[1-9])/i ) {
    0          
264 0           $mute = 1;
265             }
266             elsif ( $in =~ /^\s*(F|N|0|OF)/i ) {
267 0           $mute = 0;
268             }
269             else {
270 0           carp("MuteTrace boolean '$in' invalid, ignored");
271 0           return;
272             }
273              
274 0 0         if ( ref($self) ne '' ) {
275 0           $self->{TraceMute} = $mute;
276             }
277             else {
278 0           $Lab::Connection::Trace::TraceMute = $mute;
279             }
280             }
281              
282             1;
283              
284             __END__
285              
286             =pod
287              
288             =encoding UTF-8
289              
290             =head1 NAME
291              
292             Lab::Connection::Trace - Trace communication with instruments
293              
294             =head1 VERSION
295              
296             version 3.880
297              
298             =head1 SYNOPSIS
299              
300             This module
301             is designed to provide a simple, compact record of messages sent to and
302             received from an instrument, suitable for later analysis. The trace file
303             has one line per message. Examples:
304              
305             01>"DAT:STAR 1"
306             01>"HEAD 1"
307             01>"DAT?"
308             01<":DATA:ENCDG RPBINARY;DESTINATION REFC;SOURCE CH1;START 1;STOP 2500;WIDTH 1"
309             01>"HEAD 0"
310             01>"DAT:SOU?"
311             01<CH1
312             01>"*RST"
313              
314             Each connection gets a 'connection number' prefix (01 in example above),
315             followed by a single character to indicate commands written TO the
316             instrument (>), replies read FROM the instrument (<), communication setup
317             commands (*) or user comments (|). The quoting is provided by
318             Data::Dumper, with Useqq=1, so that included spaces, nonprintible chars,
319             etc are properly escaped and quoted.
320              
321             This module is mostly useful for instruments that return a lot of data
322             with complex configuration, so that an optimum DAQ strategy is "record
323             it all, sort it out later". Digital oscilloscopes, for example, although
324             the module is set up so that one can combine oscilloscopes, pulse
325             generators, power suppplies, meters, etc., and yield a single trace
326             file.
327              
328             Perl modules for parsing/decoding the trace file are needed, and
329             may be specific to particular instruments.
330              
331             =head2 OpenTraceFile
332              
333             use Lab::Connection::Trace;
334              
335             OpenTraceFile('tracefilename');
336              
337             Opens a new trace file, reseting the trace channel count. You should
338             call this routine before opening device channels to instruments, so that
339             the connections get logged to the trace file.
340              
341             =head2 Opening Connections
342              
343             use Lab::Instrument::HP34401A;
344              
345             my $m = new Lab::Instrument::HP34401A(
346             connection_type => 'LinuxGPIB::Trace',
347             ...
348             );
349              
350             =head2 Comment
351              
352             use Lab::Connection::Trace;
353              
354             Comment('global comment');
355              
356             puts 00|"global comment" in trace file
357              
358             $m = new Lab::Instrument:HP34401A (connection_type=>'LinuxGPIB::Trace',..);
359              
360             $m->connection->Comment('meter comment');
361              
362             puts 01|"meter comment" in the trace file, if the HP34410A is the
363             first instrument using a 'Trace' connection.
364              
365             =head1 Data organization
366              
367             The trace data file can be divided into 'runs' and 'events',
368             where run = 1..Nrun and event = 1..Nevnts
369              
370             Use SetRun(nrun) to set an initial run number,
371             StartRun('comment') to start a run (and reset event number)
372             NextEvent() to go to the next event, and
373             StopRun() to end a run.
374              
375             NextRun() increments run number and resets event number.
376              
377             Example:
378             SetRun(12); # first run is number 12
379             StartRun('test run dozen'); # comment stored in start run marker
380             NextEvent();
381             ...take data # run 12 event 1
382             NextEvent();
383             ... take data # run 12 event 2
384             NextEvent();
385             ... take data # run 12 event 3
386             NextRun('dozen+1'); # end run 12, start run 13. with comment
387             ... take data # run 13 event 1
388             NextEvent();
389             ... take data # run 13 event 2
390             StopRun(); # write end of run marker.
391              
392             These routines are provided for convenience when using Trace output
393             as a means of storing measurement data.
394              
395             =head2 SetRun
396              
397             SetRun($n);
398              
399             Set the run number that will be used for the next
400             run to be started.
401              
402             =head2 StartRun
403              
404             Insert a global comment to indicate the start of an acquisition
405             run sequence
406              
407             StartRun($comment[,$runnum]);
408              
409             If $runnum is provided, does a SetRun($runnum) first, otherwise
410             the current run is incremented and the event number reset.
411              
412             =head2 StopRun
413              
414             StopRun();
415              
416             Insert a line in the trace file indicating that the run has stopped.
417              
418             =head2 NextRun
419              
420             NextRun($comment);
421              
422             Stop current run, starts new run.
423              
424             =head2 NextEvent
425              
426             $thisevent = NextEvent();
427              
428             Puts an 'event' marker in the trace file, increments event number
429             and returns the event number that was just started.
430              
431             =head2 MuteTrace
432              
433             $instrument->connection->MuteTrace($mute); # per-instrument
434              
435             MuteTrace($mute); # global
436              
437             Mute the tracing or unmute; $mute = 'True/1-9/Y/On' gives muted,
438             $mute = 'False/0/N/Off' turns off muting.
439              
440             Muting does not apply to Comment entries, or to Run Start/Stop
441             or Event entries.
442              
443             =head1 COPYRIGHT AND LICENSE
444              
445             This software is copyright (c) 2023 by the Lab::Measurement team; in detail:
446              
447             Copyright 2016 Charles Lane
448             2017 Andreas K. Huettel
449             2020 Andreas K. Huettel
450              
451              
452             This is free software; you can redistribute it and/or modify it under
453             the same terms as the Perl 5 programming language system itself.
454              
455             =cut