## ## The cuttlefish Visualization Tool. ## Copyright (C) 2006 The Regents of the University of California. ## ## This program is free software; you can redistribute it and/or modify it ## under the terms of the GNU General Public License version 2 as published ## by the Free Software Foundation. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, write to the Free Software ## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ## ## written by Bradley Huffaker ## ## documention by Joshua Polterock ## Bradley Huffaker ## Marina Fomenkova ## ## # Control # This code parses the configuration file and holds the settings # that are found there. These can be reset by command line arguments, # but their new values are stored here. # package Control; use Time::Local; use Image; use Carp; use Math::Trig; use Math::BigFloat; use Map; use Histogram; use Legend; use Util; require Exporter; @ISA = qw( Exporter ); @EXPORT = qw(); @EXPORT_OK = qw(); use strict; sub new { my ($this) = @_; my %map_keys = $this->CreateKeyHash( qw(image show-names image-geo x y width height label), qw(bar-height node-size node-type node-size-scale), qw(night-color day-color foreground-color background-color static), qw(font name-font label-font) ); my %image_keys = $this->CreateKeyHash( qw(image x y width height) ); my %graph_keys = $this->CreateKeyHash( qw(x y width height font title x-axis y-axis tick-color) ); my %global_keys = $this->CreateKeyHash( qw(output frame-rate nodelist frame delay) ); my %integer_keys = $this->CreateKeyHash( qw(delay frame-rate) ); my $global = new Map(0); my $undef; my $hash = { "frame-rate" => 1, "delay" => 60, "current-time" => $undef, "object" => "global", "global-keys" => \%global_keys, "integer-keys" => \%integer_keys, "map-keys" => \%map_keys, "image-keys" => \%image_keys, "graph-keys" => \%graph_keys, "output" => $undef, "error-num" => 0, "global" => $global, "map-current" => $global, "frames" => {}, "objects" => [$global], "maps" => [$global], "verbose" => $undef, "working-dir" => ".cuttlefish" }; my $control = bless $hash, $this; return $control; } sub CreateKeyHash { my ($this, @keys) = @_; my %hash; foreach my $key (@keys) { $hash{$key} = 1; } return %hash; } sub HandleError { my ($this, $file, $linenum, @msg) = @_; foreach my $msg (@msg) { if (defined $msg) { $this->ErrorFile($file, $linenum, $msg); } } } sub ErrorFile { my ($this, $file, $linenum, $msg) = @_; print STDERR "$file\[$linenum\] $msg\n"; $this->{"error-num"}++; } sub ReadConfig { my ($this, @files) = @_; my %key2values; my $error_num = 0; foreach my $file (@files) { open (IN, "<$file") || die("Unable to open $file:$!"); my $linenum = 0; my $last_continued; while () { $linenum++; chop; s/#.*//g; s/^\s*//; next unless (/[^\s]/); if (defined $last_continued) { $last_continued .= $_; $_ = $last_continued; undef $last_continued; } if (/(.*)\\\s*$/) { $last_continued = $1; next; } if (/^\s*\d+/) { if ($this->{object} eq "nodelist") { unless ($this->ParseNodes($_)) { $this->ErrorFile($file, $linenum, "failed to parse \"$_\""); } } elsif ($this->{object} eq "frame") { $this->ParseFramePoint($_); } } elsif (/^([^:]*):\s*(.*)/) { my ($key, $value) = ($1, $2); $this->SetValueFile($file,$linenum, $key, $value); } else { chop; $this->ErrorFile($file, $linenum, "Failed to parse \"$_\""); } } close IN; } ############################################################# my @objects; foreach my $object (@{$this->{"objects"}}) { my @errors = $object->check(); if ($#errors >= 0) { my $type = $object->{"type"}; my $linenum = $object->{"linenum"}; die("$type defined on $linenum failed checks: ". join ("\n\t",@errors)."\n\t"); } else { push @objects, $object; } } $this->{objects} = \@objects; foreach my $object (@{$this->{"objects"}}) { my $type = $object->{"type"}; if ($type eq "Map" || $type eq "Image") { $object->loadCanvas(); } } ############################################################# $this->setWidthHeight(); ############################################################# if ($this->{"error_num"} > 0) { die("File errors discovered, terminating"); } unless (defined $this->{output}) { $this->SetOutput(); } } sub setWidthHeight { my ($this) = @_; my @objects = @{$this->{objects}}; my $global = $this->{global}; my ($width,$height) = ($global->{width},$global->{height}); return if (defined $width && defined $height); $width = 5 unless (defined $width); $height = 5 unless (defined $width); foreach my $object (@objects) { my $w = $object->{x} + $object->{width}; my $h = $object->{y} + $object->{height}; if ($w > $width) { $width = $w; } if ($h > $height) { $height = $h; } } $global->setValue("width",$width) unless (defined $global->{width}); $global->setValue("height",$height) unless (defined $global->{height}); } sub getWidthHieght { my ($this) = @_; my $global = $this->{global}; return ($global->{width},$global->{height}); } sub GetValue { my ($this, $key) = @_; return $this->{$key}; } sub SetValue { my ($this, $key ,$value) = @_; $this->{$key} = $value; } sub SetValueFile { my ($this, $file, $linenum, $key, $value) = @_; foreach my $v ($key, $value) { $v =~ s/^\s*//; $v =~ s/\s*$//; } if ($key eq "object") { my ($object, @type_values) = split /\s+/, $value; $this->SetValueFileObject($file, $linenum, $object, @type_values); $value = $object; $this->{$key} = $value; return; } else { if ($this->{"object"} eq "histogram" || $this->{"object"} eq "legend") { $this->SetValueGraph($file,$linenum,$key,$value); return; } if ($this->{"object"} eq "image") { $this->SetValueImage($file,$linenum,$key,$value); return; } if (defined $this->{"map-keys"}{$key}) { $this->SetValueMap($file,$linenum,$key,$value); return; } } if (defined $this->{"integer-keys"}{$key} && !$key =~ /^\d+/) { $this->ErrorFile($file, $linenum, "is not an integer \"$key\""); return; } unless (defined $this->{"global-keys"}{$key}) { $this->ErrorFile($file, $linenum, "key:$key is not a valid key"); return; } $this->{$key} = $value; return 1; } sub SetValueGraph { my ($this, $file, $linenum, $key, $value) = @_; unless (defined $this->{"graph-keys"}{$key}) { $this->ErrorFile($file, $linenum, "$key is not defined in object graph"); return; } my @objects = @{$this->{"objects"}}; $this->HandleError($file, $linenum, $objects[$#objects]->setValue($key, $value)); } sub SetValueImage { my ($this, $file, $linenum, $key, $value) = @_; unless (defined $this->{"image-keys"}{$key}) { $this->ErrorFile($file, $linenum , "$key is not defined in object image"); return; } my @objects = @{$this->{"objects"}}; $this->HandleError($file, $linenum, $objects[$#objects]->setValue($key, $value)); } sub SetValueMap { my ($this, $file, $linenum, $key, $value) = @_; my $map = $this->{"map-current"}; my @errors; if ($key eq "image-geo") { my @values = split /\s+/, $value; unless ($#values == 3) { $this->ErrorFile($file, $linenum, "$key does not contain 4 values"); return; } my ($lat_min, $long_min, $lat_max, $long_max) = @values; push @errors, $map->setLatLong($lat_min, $long_min, $lat_max, $long_max); } elsif ($key eq "image") { push @errors, $map->setImage($value); } else { push @errors, $map->setValue($key, $value); } $this->HandleError($file, $linenum, @errors); } sub SetValueFileMultiValue { my ($this, $file, $linenum, $object, $values, @valid) = @_; my @types = split /\s+/,$values; my %valid_types = $this->CreateKeyHash(@valid); foreach my $type (@types) { if (defined $valid_types{$type}) { $this->{$object}{$type} = 1; } else { $this->ErrorFile($file, $linenum, "unknown $object type:\"$type\""); } } } sub SetValueFileObject { my ($this, $file, $linenum, $object, @type_value) = @_; if ($object eq "frame") { my $current_time; my %frame_types = $this->CreateKeyHash(qw(file sum)); foreach my $type_val (@type_value) { my ($type, $val) = ($type_val =~ /^([^:]+):(.*)/); if ($type eq "date") { if ($val =~ /(\d{4})\/(\d\d)\/(\d\d):(\d\d):(\d\d):(\d\d)/) { my ($year,$mon,$day,$hour,$min,$sec) = ($1,$2,$3,$4,$5, $6); $current_time = Time::Local::timelocal( $sec,$min,$hour,$day,$mon-1,$year); } else { $this->ErrorFile($file, $linenum, "did not find date:YYYY/MM/DD:HH:MM:SS"); return; } } elsif ($type eq "time") { if ($val =~ /^(\d+)$/) { $current_time = $1; } else { $this->ErrorFile($file, $linenum, "did not find time:\d+"); return; } } else { $this->ErrorFile($file, $linenum, "unknown frame type $type:\"$_\""); return; } } unless (defined $current_time) { $this->ErrorFile($file, $linenum, "no time found\"$_\""); return; } $this->{"current-time"} = $current_time; $this->{frames}{$current_time} = {}; } elsif ($object eq "nodelist") { } elsif ($object eq "map" || $object eq "global") { if ($object eq "global") { $this->{"map-current"} = $this->{"global"}; } else { my $map = $this->{"map-current"} = new Map($linenum, $this->{global}); push @{$this->{"objects"}}, $map; push @{$this->{"maps"}}, $map; } } elsif ($object eq "image") { my $image = $this->{"map-current"} = new Image($linenum); push @{$this->{"objects"}}, $image; } elsif ($object eq "histogram" || $object eq "legend") { my $graph; if ($object eq "histogram") { $graph = new Histogram; } elsif ($object eq "legend") { $graph = new Legend; } foreach my $key (qw(background-color foreground-color)) { $graph->setValue($key, join(" ", @{$this->{global}->{$key}})); } foreach my $key (qw(node-type node-size node-size-scale)) { $graph->setValue($key, $this->{global}->{$key}); } foreach my $key (qw(font)) { $graph->{$key} = $this->{global}->{$key}; } push @{$this->{"objects"}}, $graph; } else { $this->ErrorFile($file, $linenum, "unknown object\"$object\""); return; } } sub ParseNodes { my ($this, $line) = @_; $line =~ s/\n//g; if ($line =~ /([^\s]+)\s+([^\s]+)\s+([^\s]+)\s*(.*)/) { my ($index, $lat, $long, $name) = split /\s+/; $this->{nodelist}{$index} = { "long" => $long, "lat" => $lat, "name" => $name }; return 1; } return; } sub ParseFramePoint { my ($this, $line) = @_; $line =~ s/\n//g; my ($key, $value) = split /\s+/; my $time = $this->{"current-time"}; $this->{frames}{$time}{$key} = $value; } sub SetOutput() { my ($this) = @_; my @times = sort {$a<=>$b} keys %{$this->{frames}}; my $date_start = Time2Date($times[0]); my $date_end = Time2Date($times[$#times]); my $num_frames = @times; my $frame_rate = $this->{frame_rate}; my $output = join(".","cuttlefish","$date_start-$num_frames-$date_end", $frame_rate,"gif"); $this->{output} = $output; } 1;