App-Device-Chip-sensor

 view release on metacpan or  search on metacpan

lib/App/Device/Chip/sensor.pm  view on Meta::CPAN

            elsif( $opts =~ s/^-M:(.*?)=(.*)(?:$|,)// ) {
               $config{mountopts}{$1} = $2;
            }
            else {
               croak "Unable to parse chip configuration options '$opts' for $chiptype'";
            }
         }

         $self->add_chip( %config );
      },
   ) or exit 1;

   return $self;
}

=head2 add_chip

   $app->add_chip( %config );

I<Since version 0.05.>

Adds a new chip to the stored configuration, as if it had been given as a
commandline argument. Takes the following named arguments:

=over 4

=item type => STR

Required string that gives the name of the chip class.

=item adapter => Device::Chip::Adapter

Required L<Device::Chip::Adapter> instance.

=item mountopts => HASH

Optional HASH reference containing extra mount parameters.

=item config => HASH

Optional HASH reference containing extra chip configuration to set up using
the C<configure> method once mounted.

=back

=cut

extended method add_chip ( :$type, :$adapter, %config )
{
   push @_CHIPCONFIGS, {
      type    => $type,
      adapter => $adapter,
      pairgrep { defined $b } %config{qw( mountopts config )}
   };
}

=head2 chips

   @chips = await $app->chips;

An asynchronous memoized lazy accessor for the list of L<Device::Chip>
instances, whose class names are taken from the remaining commandline
arguments after the options are parsed.

=cut

field $_chips; # arrayref
async method chips
{
   return @$_chips if $_chips;

   foreach my $chipconfig ( @_CHIPCONFIGS ) {
      my $chiptype = $chipconfig->{type};
      my $adapter  = $chipconfig->{adapter};

      my $class = "Device::Chip::$chiptype";

      require ( "$class.pm" ) =~ s(::)(/)gr;

      my $chip = $class->new;

      my %mountopts;
      %mountopts = $chipconfig->{mountopts}->%* if $chipconfig->{mountopts};

      await $chip->mount( $adapter, %mountopts );

      if( $chipconfig->{config} ) {
         await $chip->change_config( $chipconfig->{config}->%* );
      }

      await $chip->protocol->power(1);

      if( $chip->can( "initialize_sensors" ) ) {
         await $chip->initialize_sensors;
      }

      push @$_chips, $chip;
   }

   return @$_chips;
}

=head2 sensors

   @sensors = await $app->sensors;

An asynchronous memoized lazy accessor for the list of L<Device::Chip::Sensor>
instances of each of the configured chips (from the L</chips> method).

=cut

field $_sensors; # arrayref

field $_chipname_width;
field $_sensorname_width;

sub _chipname ( $chip ) { return ( ref $chip ) =~ s/^Device::Chip:://r }

async method sensors
{
   return @$_sensors if $_sensors;

   @$_sensors = map { $_->list_sensors } await $self->chips;

   $_chipname_width   = max map { length _chipname $_ } @$_chips;
   $_sensorname_width = max map { length $_->name } @$_sensors;

   await $self->after_sensors( @$_sensors );

   return @$_sensors;
}

async method after_sensors ( @sensors ) { }

=head2 run

   await $app->run;

An asynchronous method which performs the actual run loop of the sensor
application. This implements the main application logic, of regular collection
of values from all of the sensor instances and reporting them to the
L</output_readings> method.

In normal circumstances the L<Future> instance returned by this method would
remain pending for the lifetime of the program, and not complete. For an
application that has nothing else to perform concurrently it can simply
C<await> this future to run the logic. If it has other logic to perform as
well it could combine this with other futures using a C<< Future->needs_all >>
or similar techniques.

=cut

field %filters_by_sensor;

async method run ()
{
   my @chips = await $self->chips;

   $SIG{INT} = $SIG{TERM} = sub { exit 1; };

   defer {
      try {
         $chips[0] and $chips[0]->protocol->power(0)->get;
      }
      catch ($e) {
         warn "Failed to turn off power while shutting down: $e";
      }
   }

   my @sensors = await $self->sensors;

   my $waittime = Time::HiRes::time();
   while(1) {
      # Read concurrently
      my $now = Time::HiRes::time();

      my @values = await Future->needs_all(
         map {
            my $sensor = $_;
            my $f = $sensor->read;
            $f = $f->then(
               async sub ($reading) {
                  $self->on_sensor_ok( $sensor );
                  return $reading;
               },
               async sub ($failure, @) {
                  $self->on_sensor_fail( $sensor, $failure );
                  return undef;
               },
            ) if $_best_effort;
            $f;
         } @sensors
      );

      foreach my $idx ( 0 .. $#sensors ) {
         my $sensor = $sensors[$idx];

         my $filter = $filters_by_sensor{ refaddr $sensor } //= $self->make_filter_for_sensor( $sensor );



( run in 1.517 second using v1.01-cache-2.11-cpan-df04353d9ac )