File Coverage

blib/lib/Lab/MultiChannelInstrument.pm
Criterion Covered Total %
statement 26 151 17.2
branch 0 50 0.0
condition 0 15 0.0
subroutine 9 19 47.3
pod 0 6 0.0
total 35 241 14.5


line stmt bran cond sub pod time code
1             package Lab::MultiChannelInstrument;
2             $Lab::MultiChannelInstrument::VERSION = '3.881';
3             #ABSTRACT: Multi-channel instrument base class
4              
5 1     1   2031 use v5.20;
  1         4  
6              
7 1     1   6 use strict;
  1         2  
  1         22  
8 1     1   7 use Lab::Generic;
  1         1  
  1         27  
9 1     1   660 use List::MoreUtils qw{ any };
  1         17048  
  1         6  
10 1     1   1142 use Carp qw(cluck croak);
  1         3  
  1         62  
11 1     1   7 use Class::ISA qw(self_and_super_path);
  1         2  
  1         26  
12 1     1   5 use Clone qw(clone);
  1         3  
  1         37  
13 1     1   6 use Data::Dumper;
  1         3  
  1         663  
14              
15             our $AUTOLOAD;
16             our @ISA = ('Lab::Generic');
17              
18             our %fields = (
19             supported_connections => [],
20              
21             connection_settings => {},
22              
23             device_settings => {
24             channels => {
25             'A' => undef,
26             'B' => undef,
27             },
28             channel_default => undef,
29              
30             },
31              
32             device_cache => {},
33              
34             device_cache_order => [],
35             );
36              
37             sub new {
38 0     0 0   my $proto = shift;
39 0   0       my $class = ref($proto) || $proto;
40              
41             # create generic class:
42 0           my $self = ${ \(__PACKAGE__) }->SUPER::new(@_);
  0            
43 0           $self->${ \( __PACKAGE__ . '::_construct' ) }( __PACKAGE__, $class );
  0            
44              
45             # merge user config data with default config
46 0           my $config = {};
47 0 0         if ( ref $_[0] eq 'HASH' ) { %{$config} = ( %{$config}, %{ $_[0] } ) }
  0            
  0            
  0            
  0            
48 0           else { $config = {@_} }
49 0           $self->{config} = $config;
50 0           $self->_construct( $self->{config} );
51              
52             # load parent class lib
53 0           my @isa = Class::ISA::super_path($class);
54 0           our @ISA = $isa[2];
55 0 0         eval "require $ISA[0]; @ISA->import(); 1;"
56             or do Lab::Exception::Warning->throw( error => $@ );
57              
58             # create instrument channels:
59 0           while ( my ( $channel, $value )
60 0           = each %{ $self->{device_settings}->{channels} } ) {
61 0           $self->{channels}->{$channel} = $class->SUPER::new( $self->{config} );
62              
63 0           $self->{channels}->{$channel}->${ \( $ISA[0] . '::_construct' ) }
  0            
64             ($class);
65              
66 0           $self->{channels}->{$channel}->{channel} = $value;
67              
68             # link shared cache values to the same cache-address in order to keep these parameters up to date for all channels (using a tied Hash):
69 0           my $device_cache;
70 0           tie %$device_cache, 'Lab::MultiChannelInstrument::DeviceCache', $self;
71              
72 0           while ( my ( $k, $v )
73 0           = each %{ $self->{channels}->{$channel}->device_cache() } ) {
74 0           $device_cache->{$k} = $v;
75             }
76 0           $self->{channels}->{$channel}->{device_cache} = $device_cache;
77              
78 0           $self->{channels}->{$channel}->unregister_instrument();
79              
80             }
81              
82 0 0 0       if ( not defined $self->{device_settings}->{channel_default}
83             or not exists $self->{channels}
84             ->{ $self->{device_settings}->{channel_default} } ) {
85             Lab::Exception::Warning->throw(
86             error => "\n\nMultiChannelDevice: default channel '"
87             . $self->{device_settings}->{channel_default}
88 0           . "' is not defined or does not exist!\n\n" );
89             }
90              
91 0           $self->register_instrument();
92              
93 0           our @ISA = ('Lab::Generic');
94 0           return $self;
95             }
96              
97             sub _construct { # _construct(__PACKAGE__);
98 0     0     ( my $self, my $package ) = ( shift, shift );
99 0           my $class = shift;
100 0           my $fields = undef;
101              
102 0 0         if ( ref($package) ne 'HASH' ) {
103 0           my @isa = Class::ISA::self_and_super_path($class);
104 0           my $device_class = $isa[0];
105             {
106 1     1   8 no strict 'refs';
  1         1  
  1         1369  
  0            
107 0           $fields = *${ \( $package . '::fields' ) }{HASH};
  0            
108             $fields
109 0           = ( $fields, *${ \( $device_class . '::fields' ) }{HASH} );
  0            
110             }
111             }
112             else {
113 0           $fields = $package;
114             }
115              
116 0           foreach my $element ( keys %{$fields} ) {
  0            
117              
118             # handle special subarrays
119 0 0         if ( $element eq 'device_settings' ) {
    0          
    0          
120              
121             # # don't overwrite filled hash from ancestor
122             $self->{device_settings} = {}
123 0 0         if !exists( $self->{device_settings} );
124 0           for my $s_key ( keys %{ $fields->{'device_settings'} } ) {
  0            
125             $self->{device_settings}->{$s_key}
126 0           = clone( $fields->{device_settings}->{$s_key} );
127             }
128             }
129             elsif ( $element eq 'connection_settings' ) {
130              
131             # don't overwrite filled hash from ancestor
132             $self->{connection_settings} = {}
133 0 0         if !exists( $self->{connection_settings} );
134 0           for my $s_key ( keys %{ $fields->{connection_settings} } ) {
  0            
135             $self->{connection_settings}->{$s_key}
136 0           = clone( $fields->{connection_settings}->{$s_key} );
137             }
138             }
139             elsif ( $element eq 'channels' ) {
140 0           $self->{device_settings}->{channels} = $fields->{$element};
141             }
142             else {
143             # handle the normal fields - can also be hash refs etc, so use clone to get a deep copy
144 0           $self->{$element} = clone( $fields->{$element} );
145              
146             #warn "here comes\n" if($element eq 'device_cache');
147             #warn Dumper($Lab::Instrument::DummySource::fields) if($element eq 'device_cache');
148             }
149 0           $self->{_permitted}->{$element} = 1;
150             }
151              
152             }
153              
154             sub channel {
155 0     0 0   my $self = shift;
156 0           my $channel = shift;
157              
158 0 0         if ( exists $self->{channels}->{$channel} ) {
159 0           return $self->{channels}->{$channel};
160             }
161             else {
162 0           Lab::Exception::CorruptParameter->throw(
163             error => "\n\nMultiChannelInstrument: Channel '"
164             . $channel
165             . "' is not defined.\n\n" );
166             }
167             }
168              
169             sub sprint_config {
170 0     0 0   my $self = shift;
171 0           my $config;
172              
173             my $device_cache;
174 0           $Data::Dumper::Varname = "device_cache_";
175              
176 0           while ( my ( $k, $v ) = each %{ $self->{device_cache} } ) {
  0            
177 0 0   0     if ( any { $_ eq $k } @{ $self->{multichannel_shared_cache} } ) {
  0            
  0            
178 0           $device_cache->{'shared_variables'}->{$k} = $v;
179             }
180             }
181              
182 0           while ( my ( $chk, $chv ) = each %{ $self->{channels} } ) {
  0            
183 0           $device_cache->{'multichannel_variables'}->{$chk}->{'name'}
184             = $chv->get_name();
185 0           while ( my ( $k, $v )
186 0           = each %{ $self->{channels}->{$chk}->{device_cache} } ) {
187 0 0   0     if ( any { $_ eq $k } @{ $self->{multichannel_shared_cache} } ) {
  0            
  0            
188              
189             }
190             else {
191 0           $device_cache->{'multichannel_variables'}->{$chk}->{$k} = $v;
192             }
193             }
194             }
195              
196 0           $config .= Dumper $device_cache;
197              
198 0           $Data::Dumper::Varname = "connection_settings_";
199 0           $Data::Dumper::Maxdepth = 1;
200 0 0         if ( defined $self->connection() ) {
201 0           $config .= Dumper $self->connection();
202             }
203 0           return $config;
204             }
205              
206             sub register_instrument {
207 0     0 0   my $self = shift;
208              
209 0           push( @{Lab::Instrument::REGISTERED_INSTRUMENTS}, $self );
210              
211             }
212              
213             sub unregister_instrument {
214 0     0 0   my $self = shift;
215              
216             @{Lab::Instrument::REGISTERED_INSTRUMENTS}
217 0           = grep { $_ ne $self } @{Lab::Instrument::REGISTERED_INSTRUMENTS};
  0            
218              
219             }
220              
221             sub AUTOLOAD {
222 0     0     my $self = shift;
223 0 0         my $type = ref($self) or croak "\$self is not an object";
224 0           my $value = undef;
225              
226 0           my $name = $AUTOLOAD;
227 0           $name =~ s/.*://; # strip fully qualified portion
228              
229 0 0 0       if ( exists $self->{_permitted}->{$name} ) {
    0 0        
    0          
    0          
    0          
230              
231 0 0         if (@_) {
232 0           return $self->{$name} = shift;
233             }
234             else {
235 0           return $self->{$name};
236             }
237             }
238             elsif ( exists $self->{channels}->{$name} ) {
239 0           return $self->{channels}->{$name};
240             }
241             elsif ( exists $self->{'device_settings'}->{$name} ) {
242 0 0         if (@_) {
243 0           return $self->{'device_settings'}->{$name} = shift;
244             }
245             else {
246 0           return $self->{'device_settings'}->{$name};
247             }
248             }
249             elsif (
250             defined $self->{channels}
251             ->{ $self->{device_settings}->{channel_default} }
252             and $self->{channels}->{ $self->{device_settings}->{channel_default} }
253             ->can($name) ) {
254             return $self->{channels}
255 0           ->{ $self->{device_settings}->{channel_default} }->$name(@_);
256             }
257             elsif (
258             defined $self->{channels}
259             ->{ $self->{device_settings}->{channel_default} }
260             and exists $self->{channels}
261             ->{ $self->{device_settings}->{channel_default} }->{$name} ) {
262             return $self->{channels}
263 0           ->{ $self->{device_settings}->{channel_default} }->{$name};
264             }
265             else {
266 0           Lab::Exception::Warning->throw( error => "AUTOLOAD in "
267             . __PACKAGE__
268             . " couldn't access field '${name}'.\n" );
269             }
270             }
271              
272             sub device_cache {
273 0     0 0   my $self = shift;
274 0           my $value = undef;
275              
276             #warn "device_cache got this:\n" . Dumper(@_) . "\n";
277              
278 0 0 0       if ( scalar(@_) == 0 )
    0          
    0          
279             { # empty parameters - return whole device_settings hash
280 0           return $self->{'device_cache'};
281             }
282             elsif ( scalar(@_) == 1 )
283             { # one parm - either a scalar (key) or a hashref (try to merge)
284 0           $value = shift;
285             }
286             elsif ( scalar(@_) > 1 && scalar(@_) % 2 == 0 )
287             { # even sized list - assume it's keys and values and try to merge it
288 0           $value = {@_};
289             }
290             else { # uneven sized list - don't know what to do with that one
291 0           Lab::Exception::CorruptParameter->throw(
292             error => "Corrupt parameters given to "
293             . __PACKAGE__
294             . "::device_cache().\n" );
295             }
296              
297             #warn "Keys present: \n" . Dumper($self->{device_settings}) . "\n";
298              
299 0 0         if ( ref($value) =~ /HASH/ ) { # it's a hash - merge into current settings
300 0           for my $ext_key ( keys %{$value} ) {
  0            
301 0           $self->{'device_cache'}->{$ext_key} = $value->{$ext_key}
302             ; # if( exists($self->device_cache()->{$ext_key}) );
303             }
304 0           return $self->{'device_cache'};
305             }
306             else { # it's a key - return the corresponding value
307 0           return $self->{'device_cache'}->{$value};
308             }
309             }
310              
311             1;
312              
313             __END__
314              
315             =pod
316              
317             =encoding UTF-8
318              
319             =head1 NAME
320              
321             Lab::MultiChannelInstrument - Multi-channel instrument base class
322              
323             =head1 VERSION
324              
325             version 3.881
326              
327             =head1 COPYRIGHT AND LICENSE
328              
329             This software is copyright (c) 2023 by the Lab::Measurement team; in detail:
330              
331             Copyright 2013 Christian Butschkow, Stefan Geissler
332             2014 Andreas K. Huettel, Christian Butschkow
333             2015 Andreas K. Huettel
334             2016 Simon Reinhardt
335             2017 Andreas K. Huettel
336             2019 Simon Reinhardt
337             2020 Andreas K. Huettel
338              
339              
340             This is free software; you can redistribute it and/or modify it under
341             the same terms as the Perl 5 programming language system itself.
342              
343             =cut