File Coverage

blib/lib/Schedule/Activity/Attribute.pm
Criterion Covered Total %
statement 140 146 95.8
branch 38 44 86.3
condition 24 39 61.5
subroutine 18 18 100.0
pod 8 10 80.0
total 228 257 88.7


line stmt bran cond sub pod time code
1             package Schedule::Activity::Attribute;
2              
3 4     4   6214 use strict;
  4         9  
  4         154  
4 4     4   23 use warnings;
  4         11  
  4         220  
5 4     4   647 use Ref::Util qw/is_hashref is_ref/;
  4         2923  
  4         10426  
6              
7             our $VERSION='0.3.0';
8              
9             my %types=(
10             int=>{
11             change =>\&_changeInt,
12             average=>\&_avgInt,
13             changes=>{map {$_=>undef} qw/type value set incr decr tm note/},
14             },
15             bool=>{
16             change =>\&_changeBool,
17             average=>\&_avgBool,
18             changes=>{map {$_=>undef} qw/type value set tm note/},
19             },
20             );
21              
22             sub new {
23 129     129 0 535979 my ($ref,%opt)=@_;
24 129   33     599 my $class=is_ref($ref)||$ref;
25 129   100     897 my ($tm,$y)=($opt{tm}//0,$opt{value}//0);
      100        
26             my %self=(
27 129   100     1438 type =>$opt{type}//'int',
28             value=>$y,
29             log =>{$tm=>$y},
30             aog =>{$tm=>$y},
31             tmmax=>$tm,
32             avg =>$y,
33             tmsum=>0,
34             );
35 129 100       441 if(!defined($types{$self{type}})) { die "Attribute invalid type: $self{type}" }
  1         20  
36 128         608 return bless(\%self,$class);
37             }
38              
39             sub validateConfig {
40 114     114 1 285 my ($self,%opt)=@_;
41 114         302 my $C=$types{$$self{type}}{changes};
42 114         285 my @errors=grep {!exists($$C{$_})} keys %opt;
  137         353  
43 114 50       295 if(@errors) { return "Invalid attribute options/commands: ".join(' ',@errors) }
  0         0  
44 114         429 return;
45             }
46              
47             sub log {
48 856421     856421 1 1605056 my ($self,$tm)=@_;
49 856421 50 33     3121799 if(defined($tm)&&($tm>=$$self{tmmax})) { $$self{log}{$tm}=$$self{value}; $$self{aog}{$tm}=$$self{avg}//$$self{value}; $$self{tmmax}=$tm }
  856421   100     2040617  
  856421         2408109  
  856421         1504983  
50             # historic entry is not currently supported
51 856421         1500354 return $self;
52             }
53              
54             sub change {
55 720577     720577 1 1806944 my ($self,%opt)=@_;
56 720577   66     1756147 my $tm=$opt{tm}//$$self{tmmax};
57 720577 100       1633269 if($tm<$$self{tmmax}) { return $self } # historic entry is not currently supported
  14268         37830  
58             #
59 706309         1332511 &{$types{$$self{type}}{change}}($self,%opt);
  706309         2211605  
60 706309         2025752 $self->log($tm); # updates tmmax
61 706309 50       1489099 if(!defined($$self{avg})) { $self->average() }
  0         0  
62 706309         1804601 return $self;
63             }
64              
65             sub report {
66 35770     35770 1 65572 my ($self)=@_;
67             return (
68             y =>$$self{value},
69 35770         98644 xy =>[$self->_xy()],
70             avg=>$self->average(),
71             );
72             }
73              
74             sub value {
75 22     22 1 21171 my ($self)=@_;
76 22 100       81 if($$self{type} eq 'int') { return 0+$$self{value} }
  16         138  
77 6 50       26 if($$self{type} eq 'bool') { return !!$$self{value} }
  6         37  
78             }
79              
80             sub average {
81 185894     185894 1 342084 my ($self)=@_;
82 185894 100       408940 if(defined($$self{avg})) { return $$self{avg} }
  35782         170319  
83 150112         267018 ($$self{avg},$$self{tmsum})=&{$types{$$self{type}}{average}}($$self{log});
  150112         365603  
84 150112         295838 return $$self{avg};
85             }
86              
87             sub reset {
88 2840     2840 0 6004 my ($self)=@_;
89 2840         5409 foreach my $tm (sort {$a<=>$b} keys %{$$self{log}}) { $$self{tmmax}=$tm; $$self{value}=$$self{log}{$tm}; last }
  1         6  
  2840         9094  
  2840         7296  
  2840         6250  
  2840         6804  
90 2840         9161 my ($y,$tm)=@$self{qw/value tmmax/};
91 2840         5258 %{$$self{log}}=($tm=>$y);
  2840         8999  
92 2840         5386 %{$$self{aog}}=($tm=>$y);
  2840         6437  
93 2840         5599 $$self{avg}=$y;
94 2840         5774 $$self{tmsum}=0;
95 2840         8182 return $self;
96             }
97              
98             sub dump {
99 385337     385337 1 694014 my ($self)=@_;
100             my %res=(
101 385337         1497753 log=>{ %{$$self{log}} },
102 385337         1570225 aog=>{ %{$$self{aog}} },
103 385337         607320 (map {$_=>$$self{$_}} qw/type value tmmax avg tmsum/),
  1926685         4948665  
104             );
105 385337         2269539 return %res;
106             }
107              
108             sub restore {
109 352446     352446 1 1378519 my ($ref,%opt)=@_;
110 352446 100       807320 if(is_hashref($ref)) {
111 1         5 foreach my $k (keys %opt) { $$ref{$k}=$opt{$k} }
  7         18  
112 1         5 return $ref;
113             }
114 352445   50     826524 my $y=$opt{value}//0;
115             my %self=(
116             type =>$opt{type}//'int',
117             value=>$y,
118             log =>$opt{log}//{},
119             aog =>$opt{aog}//{},
120             tmmax=>$opt{tmmax}//0,
121             avg =>$opt{avg}//$y,
122 352445   50     2933116 tmsum=>$opt{tmsum}//0,
      50        
      50        
      50        
      33        
      50        
123             );
124 352445         2252561 return bless(\%self,$ref);
125             }
126              
127             sub _xy {
128 35770     35770   66480 my ($self)=@_;
129 35770         61237 return map {[$_,$$self{log}{$_},$$self{aog}{$_}]} sort {$a<=>$b} keys %{$$self{log}};
  221923         763348  
  407390         724515  
  35770         186709  
130             }
131              
132             # set=>value
133             # incr=>value
134             # decr=>value
135             # tm=>tm # optional, will create a log entry
136             sub _changeInt {
137 706239     706239   1550826 my ($self,%opt)=@_;
138 706239         1272880 my $ya=$$self{value};
139 706239 100       1566668 if(defined($opt{set})) { $$self{value}=$opt{set} }
  75036         140317  
140 706239 100       1694801 if($opt{incr}) { $$self{value}+=$opt{incr} }
  193432         387381  
141 706239 100       1522405 if($opt{decr}) { $$self{value}-=$opt{decr} }
  1         4  
142 706239 100       1476164 if($opt{_log}) { }
143             #
144 706239   66     1869271 my $dt=($opt{tm}//$$self{tmmax})-$$self{tmmax};
145 706239 100       1908270 if($dt==0) { $$self{avg}=$$self{tmsum}=undef; $self->log($$self{tmmax})->average() }
  150094 50       321490  
  150094         397555  
146             elsif(defined($$self{avg})) {
147 556145         1963394 $$self{avg}=$$self{avg}*($$self{tmsum}/($$self{tmsum}+$dt))+0.5*($ya+$$self{value})*($dt/($$self{tmsum}+$dt));
148 556145         932659 $$self{tmsum}+=$dt;
149             }
150 0         0 else { $$self{avg}=0.5*($ya+$$self{value}); $$self{tmsum}+=$dt }
  0         0  
151 706239         1526036 return $self;
152             }
153              
154             # set=>value
155             # tm=>tm # optional, will create a log entry
156             sub _changeBool {
157 70     70   135 my ($self,%opt)=@_;
158 70         105 my $ya=$$self{value};
159 70 100       178 if(defined($opt{set})) { $$self{value}=$opt{set} }
  44         96  
160 70 100       193 if($opt{_log}) { }
161             #
162 70   66     151 my $dt=($opt{tm}//$$self{tmmax})-$$self{tmmax};
163 70 100       148 if($dt==0) { $$self{avg}=$$self{tmsum}=undef; $self->log($$self{tmmax})->average() }
  18 50       59  
  18         39  
164             elsif(defined($$self{avg})) {
165 52         150 $$self{avg}=$$self{avg}*$$self{tmsum}/($$self{tmsum}+$dt)+$dt*$ya/($$self{tmsum}+$dt);
166 52         80 $$self{tmsum}+=$dt;
167             }
168 0         0 else { $$self{avg}=$ya; $$self{tmsum}+=$dt }
  0         0  
169 70         124 return $self;
170             }
171              
172             sub _avgInt {
173 150094     150094   267941 my ($log)=@_;
174 150094         328451 my ($avg,$weight,$lasttm,$lasty)=(0,0);
175 150094         432017 foreach my $tm (sort {$a<=>$b} keys(%$log)) {
  29462         53381  
176 163632 100       342798 if(!defined($lasttm)) { ($lasttm,$lasty,$avg,$weight)=($tm,$$log{$tm},$$log{$tm},0); next }
  150094         385546  
  150094         312357  
177 13538         19691 my $dt=$tm-$lasttm;
178 13538         34418 $avg=$weight/($weight+$dt)*$avg+0.5*$dt/($weight+$dt)*($lasty+$$log{$tm});
179 13538         20009 $weight+=$dt;
180 13538         20241 $lasttm=$tm;
181 13538         24433 $lasty=$$log{$tm};
182             }
183 150094         483060 return ($avg,$weight);
184             }
185              
186             sub _avgBool {
187 18     18   37 my ($log)=@_;
188 18         32 my ($sum,$weight,$lasttm,$lasty)=(0,0);
189 18         50 foreach my $tm (sort {$a<=>$b} keys(%$log)) {
  30         52  
190 38 100       76 if(!defined($lasttm)) { ($lasttm,$lasty,$sum,$weight)=($tm,$$log{$tm},$$log{$tm},0); next }
  18         39  
  18         37  
191 20         29 my $dt=$tm-$lasttm;
192 20         30 $sum+=$lasty*($tm-$lasttm);
193 20         26 $weight+=$dt;
194 20         26 $lasttm=$tm;
195 20         31 $lasty=$$log{$tm};
196             }
197 18 100       40 if($weight==0) { return ($sum,$weight) }
  10         24  
198 8         21 return ($sum/$weight,$weight);
199             }
200              
201             1;
202              
203             __END__