File Coverage

blib/lib/Acme/FishFarm/WaterConditionMonitor.pm
Criterion Covered Total %
statement 122 132 92.4
branch 62 100 62.0
condition 5 6 83.3
subroutine 27 31 87.1
pod 26 26 100.0
total 242 295 82.0


line stmt bran cond sub pod time code
1             package Acme::FishFarm::WaterConditionMonitor;
2              
3 3     3   69995 use 5.006;
  3         21  
4 3     3   17 use strict;
  3         7  
  3         74  
5 3     3   15 use warnings;
  3         6  
  3         111  
6 3     3   20 use Carp "croak";
  3         14  
  3         5506  
7              
8             =head1 NAME
9              
10             Acme::FishFarm::WaterConditionMonitor - Water Condition Monitor for Acme::FishFarm
11              
12             =head1 VERSION
13              
14             Version 1.01
15              
16             =cut
17              
18             our $VERSION = '1.01';
19              
20              
21             =head1 SYNOPSIS
22              
23             use 5.010;
24              
25             use Acme::FishFarm::WaterConditionMonitor;
26             use Acme::FishFarm::OxygenMaintainer;
27              
28             my $water_monitor = Acme::FishFarm::WaterConditionMonitor->install;
29             my $oxygen = Acme::FishFarm::OxygenMaintainer->install( DO_generation_volume => 1.92 );
30              
31             $water_monitor->add_oxygen_maintainer( $oxygen );
32            
33             # always check water conditions before checking LEDs and buzzers
34             # also, these four method will return 1 or 0, upon calling them, the status of LEDs and buzzers will also be updated
35             $water_monitor->ph_is_normal;
36             $water_monitor->temperature_is_normal;
37             $water_monitor->lacking_oxygen;
38             $water_monitor->water_dirty;
39            
40             if ( $water_monitor->is_on_LED_DO ) {
41             # do something, same goes to the rest of the LEDs
42             }
43              
44             if ( $water_monitor->is_on_buzzer_short ) {
45             # do something
46             } elsif ( $water_monitor->is_on_buzzer_long ) {
47             # do something
48             }
49              
50             =head1 EXPORT
51              
52             None
53              
54             =head1 NOTES
55              
56             Some of the methods in this module can be confusing expecially when it comes to checking abnormal water conditions.
57              
58             B
59              
60             C contains subroutines to check all the abnormal water conditions to ease this job.
61              
62             =head1 CREATION RELATED SUBROUTINES/METHODS
63              
64             Only 3 sensors are built-in. However, there is a 4th socket for the oxygen maintainer. For this socket, you'll need to manuall connect an Acme::FishFarm::OxygenMaintainer object by calling the C method.
65              
66             More sockets might be available in the future.
67              
68             =head2 install ( %sensors )
69              
70             Installs a water condition monitoring system.
71              
72             The C<%sensors> included are:
73              
74             =over 4
75              
76             =item pH
77              
78             Optional. The default threshold range is C<[6.5, 7.5]> and the default pH is C<7.0>.
79              
80             This will set the threshold value of the water pH. Please pass in an array reference to this key
81             in the form of C<[min_pH, max_pH]>
82              
83             The values are in the range of C<1-14>. However, this range is not checked for incorrect values.
84              
85             =item temperature
86              
87             Optional. The default threshold range is C<[20, 25]> degree celcius and the default temprature is C<25>.
88              
89             This will set the threshold value of the water temperature. Please pass in an array reference to this key
90             in the form of C<[min_temperature, max_temperature]>
91              
92             The ranges of values are between C<0> and C<50> degree B. However, this range is not checked for incorrect values. The unit C is just a unit, it doesn't show up if you call any of it's related getters.
93              
94             =item turbidity
95              
96             Optional. The default threshold is C<180 ntu> and the default turbidity is set to C<10 ntu>.
97              
98             This will set the threshold of the turbidity of the water.
99              
100             The range of values are between C<0 ntu> and C<300 ntu>. However, this range is not checked for incorrect values. The unit C is just a unit, it doesn't show up if you call any of it's related getters.
101              
102             =back
103              
104             =cut
105              
106             sub install {
107 3     3 1 91 my $class = shift;
108 3         8 my %sensors = @_;
109            
110 3 50       10 if ( not $sensors{pH} ) {
111 3         11 $sensors{pH_range} = [6.5, 7.5];
112 3         8 $sensors{current_pH} = 7.0;
113 3         7 $sensors{pH_LED_on} = 0;
114             }
115            
116 3 50       9 if ( not $sensors{temperature} ) {
117 3         10 $sensors{temperature_range} = [20, 25];
118 3         7 $sensors{current_temperature} = 23;
119 3         19 $sensors{temperature_LED_on} = 0;
120             }
121            
122 3 50       11 if ( not $sensors{turbidity} ) {
123 3         6 $sensors{turbidity_threshold} = 180;
124 3         8 $sensors{current_turbidity} = 10;
125 3         7 $sensors{turbidity_LED_on} = 0;
126             }
127            
128             # low DO led, use Acme::FishFarm::OxygenMaintainer to determine
129 3         6 $sensors{DO_LED_on} = 0;
130            
131 3         6 $sensors{lighted_LED_count} = 0;
132 3         5 $sensors{short_buzzer_on} = 0;
133 3         7 $sensors{long_buzzer_on} = 0;
134            
135 3         11 bless \%sensors, "Acme::FishFarm::WaterConditionMonitor";
136             }
137              
138              
139             =head2 add_oxygen_maintainer ( $oxygen_maintainer )
140              
141             Connects the oxygen maintainer ie C system to this monitoring system.
142              
143             For now, this module can only check if the oxygen is lacking or not. This module contains a user friendly method compared to the standard terminology used in the C module. Other user friendly methods will be added in the future.
144              
145             =cut
146              
147             sub add_oxygen_maintainer {
148 3 50   3 1 15 ref( my $self = shift ) or croak "Please use this the OO way";
149 3 50       11 if ( ref( my $oxygen_maintainer = shift ) ne "Acme::FishFarm::OxygenMaintainer") {
150 0         0 croak "Please pass in an Acme::FishFarm::OxygenMaintainer object!";
151             } else {
152 3         14 $self->{oxygen_maintainer} = $oxygen_maintainer;
153             }
154             }
155              
156             =head1 WATER CONDITIONS RELATED SUBROUTINES/METHODS
157              
158             =head2 current_ph ( $new_ph )
159              
160             Sets / returns the current pH of the water.
161              
162             C<$new_pH> is optional. If present, the current pH will be set to C<$new_ph>. Otherwise, returns the current pH reading.
163              
164             =cut
165              
166             sub current_ph {
167 7 50   7 1 1117 ref( my $self = shift ) or croak "Please use this the OO way";
168 7 100       21 if ( @_ ) {
169 4         10 $self->{current_pH} = shift;
170             } else {
171 3         49 $self->{current_pH};
172             }
173             }
174              
175             =head2 ph_threshold
176              
177             Returns the pH threshold as an array ref.
178              
179             =cut
180              
181             sub ph_threshold {
182 3 50   3 1 817 ref( my $self = shift ) or croak "Please use this the OO way";
183 3         14 $self->{pH_range};
184             }
185              
186             =head2 set_ph_threshold ( $ph_value )
187              
188             Sets the pH threshold.
189              
190             =cut
191              
192             sub set_ph_threshold {
193 1 50   1 1 1113 ref( my $self = shift ) or croak "Please use this the OO way";
194 1         2 my $new_ph_range_ref = shift;
195 1 50       5 croak "Please supply an array ref to set new pH range" if ref( $new_ph_range_ref ) ne ref( [] );
196 1         5 $self->{pH_range} = $new_ph_range_ref;
197             }
198              
199             =head2 ph_is_normal
200              
201             Returns true if the current pH is within the set range of threshold.
202              
203             The pH LED will light up and a short buzzer will be turned on if B the pH is not normal.
204              
205             Don't worry about the long buzzer as it will be taken care of behind the scene.
206              
207             =cut
208              
209             sub ph_is_normal {
210 7 50   7 1 572 ref( my $self = shift ) or croak "Please use this the OO way";
211              
212 7         26 _tweak_buzzers( $self );
213            
214 7 100 100     40 if ( $self->{current_pH} >= $self->{pH_range}[0] and $self->{current_pH} <= $self->{pH_range}[1] ) {
215            
216             # if still on, switch it off, it's normal now
217 2 100       6 if ( $self->is_on_LED_pH ) {
218 1         2 $self->{pH_LED_on} = 0;
219             }
220            
221 2         10 return 1;
222             } else {
223 5         12 $self->{pH_LED_on} = 1;
224 5         17 return 0;
225             }
226             }
227              
228             =head2 current_temperature ( $new_temperature )
229              
230             Sets / returns the current temperature of the water.
231              
232             C<$new_temperature> is optional. If present, the current temperature will be set to C<$new_temperature>. Otherwise, returns the current temperature reading.
233              
234             =cut
235              
236             sub current_temperature {
237 5 50   5 1 1118 ref( my $self = shift ) or croak "Please use this the OO way";
238            
239 5 100       16 if ( @_ ) {
240 3         10 $self->{current_temperature} = shift;
241             } else {
242 2         41 $self->{current_temperature};
243             }
244            
245             }
246              
247             =head2 temperature_threshold
248              
249             Returns the acceptable temperature range as an array ref.
250              
251             =cut
252              
253             sub temperature_threshold {
254 3 50   3 1 12 ref( my $self = shift ) or croak "Please use this the OO way";
255 3         9 $self->{temperature_range};
256             }
257              
258             =head2 set_temperature_threshold ( $new_temperature )
259              
260             Sets the water temperature threshold.
261              
262             =cut
263              
264             sub set_temperature_threshold {
265 1 50   1 1 1061 ref( my $self = shift ) or croak "Please use this the OO way";
266 1         3 my $new_temperature_range_ref = shift;
267 1 50       4 croak "Please supply an array ref to set new temperature range"
268             if ref( $new_temperature_range_ref ) ne ref( [] );
269 1         6 $self->{temperature_range} = $new_temperature_range_ref;
270             }
271              
272             =head2 temperature_is_normal
273              
274             Returns true if the current temperature is within the set range of threshold.
275              
276             The temperature LED will light up and a short buzzer will be turned on if B the temperature is not normal.
277              
278             Don't worry about the long buzzer as it will be taken care of behind the scene.
279              
280             =cut
281              
282             sub temperature_is_normal {
283 6 50   6 1 539 ref( my $self = shift ) or croak "Please use this the OO way";
284              
285 6         20 _tweak_buzzers( $self );
286            
287 6 100 66     38 if ( $self->{current_temperature} >= $self->{temperature_range}[0]
288             and $self->{current_temperature} <= $self->{temperature_range}[1] ) {
289            
290             # if still on, switch it off, it's normal now
291 4 100       15 if ( $self->is_on_LED_temperature ) {
292 1         3 $self->{temperature_LED_on} = 0;
293             }
294            
295 4         41 return 1;
296             } else {
297 2         5 $self->{temperature_LED_on} = 1;
298 2         10 return 0;
299             }
300             }
301              
302             =head2 lacking_oxygen
303              
304             Returns true if the current DO content is lower than the threshold.
305              
306             =cut
307              
308             sub lacking_oxygen {
309 4 50   4 1 18 ref( my $self = shift ) or croak "Please use this the OO way";
310            
311 4         24 _tweak_buzzers( $self );
312            
313 4 100       37 if ( $self->{oxygen_maintainer}->is_low_DO) {
314 2         5 $self->{DO_LED_on} = 1;
315 2         9 return 1;
316             } else {
317             # if still on, switch it off, it's normal now
318 2 100       8 if ( $self->is_on_LED_DO ) {
319 1         2 $self->{DO_LED_on} = 0;
320             }
321            
322 2         9 return 0;
323             }
324            
325             }
326              
327             =head2 current_turbidity ( $new_turbidity )
328              
329             Sets / returns the current turbidity of the water.
330              
331             C<$new_turbidity> is optional. If present, the current turbidity will be set to C<$new_turbidity>. Otherwise, returns the current turbidity reading.
332              
333             =cut
334              
335             sub current_turbidity {
336 7 50   7 1 26 ref( my $self = shift ) or croak "Please use this the OO way";
337            
338 7 100       21 if ( @_ ) {
339 4         10 $self->{current_turbidity} = shift;
340             } else {
341 3         23 $self->{current_turbidity};
342             }
343            
344             }
345              
346             =head2 turbidity_threshold
347              
348             Returns the turbidity threshold.
349              
350             =cut
351              
352             sub turbidity_threshold {
353 3 50   3 1 13 ref( my $self = shift ) or croak "Please use this the OO way";
354 3         12 $self->{turbidity_threshold};
355             }
356              
357             =head2 set_turbidity_threshold ( $new_turbidity_threshold )
358              
359             Sets the turbidity threshold to C<$new_turbidity_threshold>.
360              
361             =cut
362              
363             sub set_turbidity_threshold {
364 2 50   2 1 9 ref( my $self = shift ) or croak "Please use this the OO way";
365              
366 2         5 $self->{turbidity_threshold} = shift;
367             }
368              
369             =head2 water_dirty
370              
371             Returns true if the current turbidity is highter then the threshold.
372              
373             The turbidity LED will light up and a short buzzer will be turned on if B the turbidity is not normal.
374              
375             Don't worry about the long buzzer as it will be taken care of behind the scene.
376              
377             =cut
378              
379             sub water_dirty {
380 5 50   5 1 16 ref( my $self = shift ) or croak "Please use this the OO way";
381            
382 5         15 _tweak_buzzers( $self );
383            
384 5 100       17 if ( $self->{current_turbidity} >= $self->{turbidity_threshold} ) {
385            
386 4         9 $self->{turbidity_LED_on} = 1;
387 4         23 return 1;
388             } else {
389              
390             # if still on, switch it off, it's normal now
391 1 50       3 if ( $self->is_on_LED_turbidity ) {
392 1         2 $self->{turbidity_LED_on} = 0;
393             }
394            
395 1         5 return 0;
396             }
397             }
398              
399              
400             # these 2 should be wrappers of something in the future
401             =head1 BUZZER RELATED SUBROUTINES/METHODS
402              
403             =head2 is_on_buzzer_short
404              
405             Returns true if the short buzzer is turned on.
406              
407             A short buzzer will buzz ie turned on if there is 1 abnormal condition. If more than 1 abnormal conditions are present, the long buzzer will be turned on and this short buzzer will be turned off so that it's not too noisy :)
408              
409             =cut
410              
411             sub is_on_buzzer_short {
412 12 50   12 1 42 ref( my $self = shift ) or croak "Please use this the OO way";
413 12         37 _tweak_buzzers( $self );
414 12         50 return $self->{short_buzzer_on};
415             }
416              
417              
418             =head2 is_on_buzzer_long
419              
420             Returns true if the long buzzer is turned on and also turns off the short buzzer to reduce noise.
421              
422             =cut
423              
424             sub is_on_buzzer_long {
425 13 50   13 1 49 ref( my $self = shift ) or croak "Please use this the OO way";
426 13         35 _tweak_buzzers( $self );
427 13         55 return $self->{long_buzzer_on};
428             }
429              
430             =head1 Private Methods for Buzzers
431              
432             =over 4
433              
434             =item &_tweak_buzzers ( $self )
435              
436             Tweak the buzzers. It's either the short buzzer or the long buzzer switched on only. This subroutine will be called whenever a condition checking method is called in order to update the buzzers status.
437              
438             =back
439              
440             =cut
441              
442             sub _tweak_buzzers {
443 47 50   47   104 ref( my $self = shift ) or croak "Please use this the OO way";
444            
445             # short buzzer
446 47 100       103 if ( $self->lighted_LED_count == 1 ) {
    100          
    50          
447 13         24 $self->{short_buzzer_on} = 1;
448 13         22 $self->{long_buzzer_on} = 0;
449            
450             # long buzzer
451             } elsif ( $self->lighted_LED_count > 1 ) {
452 27         42 $self->{short_buzzer_on} = 0;
453 27         48 $self->{long_buzzer_on} = 1;
454            
455             # no buzzer
456             } elsif ( $self->lighted_LED_count == 0 ) {
457 7         13 $self->{short_buzzer_on} = 0;
458 7         14 $self->{long_buzzer_on} = 0;
459            
460             # somthing's wrong
461             } else {
462 0         0 die "Something's wrong with LED count";
463             }
464             }
465              
466             =head1 LED LIGHTS RELATED SUBROUTINES/METHODS
467              
468             An LED is lighted up if the corresponding parameter is in abnormal state.
469              
470             =head2 on_LED_pH
471              
472             Lights up the LED for pH sensor, indicating abnormal pH.
473              
474             =head2 is_on_LED_pH
475              
476             Returns true if the LED of pH is lighted up.
477              
478             =cut
479              
480             sub on_LED_pH {
481 0 0   0 1 0 ref( my $self = shift ) or croak "Please use this the OO way";
482 0         0 $self->{pH_LED_on} = 1;
483             }
484              
485             sub is_on_LED_pH {
486 5 50   5 1 17 ref( my $self = shift ) or croak "Please use this the OO way";
487 5         19 return $self->{pH_LED_on};
488             }
489              
490             =head2 on_LED_temperature
491              
492             Lights up the LED for temperature sensor, indicating abnormal water temperature.
493              
494             =head2 is_on_LED_temperature
495              
496             Returns true if the LED of temperature is lighted up.
497              
498             =cut
499              
500             sub on_LED_temperature {
501 0 0   0 1 0 ref( my $self = shift ) or croak "Please use this the OO way";
502 0         0 $self->{temperature_LED_on} = 1;
503             }
504              
505             sub is_on_LED_temperature {
506 6 50   6 1 19 ref( my $self = shift ) or croak "Please use this the OO way";
507 6         23 return $self->{temperature_LED_on};
508             }
509              
510             =head2 on_LED_DO
511              
512             Lights up the LED for dissolved oxygen sensor, indicating low DO content. You fish might die :)
513              
514             =head2 is_on_LED_DO
515              
516             Returns true if the LED of DO is lighted up.
517              
518             =cut
519              
520             sub on_LED_DO {
521 0 0   0 1 0 ref( my $self = shift ) or croak "Please use this the OO way";
522 0         0 $self->{DO_LED_on} = 1;
523             }
524              
525             sub is_on_LED_DO {
526 3 50   3 1 21 ref( my $self = shift ) or croak "Please use this the OO way";
527 3         12 return $self->{DO_LED_on};
528             }
529              
530             =head2 on_LED_turbidity
531              
532             Light up the LED for turbidity sensor, indicating high level of waste etc. Fish might die :)
533              
534             =head2 is_on_LED_turbidity
535              
536             Returns true if the LED of DO is lighted up.
537              
538             =cut
539              
540             sub on_LED_turbidity {
541 0 0   0 1 0 ref( my $self = shift ) or croak "Please use this the OO way";
542 0         0 $self->{turbidity_LED_on} = 1;
543             }
544              
545             sub is_on_LED_turbidity {
546 4 50   4 1 14 ref( my $self = shift ) or croak "Please use this the OO way";
547 4         17 return $self->{turbidity_LED_on};
548             }
549              
550             =head2 lighted_LED_count
551              
552             Returns the number of LEDs lighted up currently
553              
554             =cut
555              
556             sub lighted_LED_count {
557 101 50   101 1 217 ref( my $self = shift ) or croak "Please use this the OO way";
558            
559 101         152 my $total_led = 0;
560 101 100       205 $total_led++ if $self->{pH_LED_on};
561 101 100       194 $total_led++ if $self->{temperature_LED_on};
562 101 100       183 $total_led++ if $self->{DO_LED_on};
563 101 100       185 $total_led++ if $self->{turbidity_LED_on};
564            
565 101         309 return $total_led;
566             }
567              
568             =head1 AUTHOR
569              
570             Raphael Jong Jun Jie, C<< >>
571              
572             =head1 BUGS
573              
574             Please report any bugs or feature requests to C, or through
575             the web interface at L. I will be notified, and then you'll
576             automatically be notified of progress on your bug as I make changes.
577              
578              
579              
580              
581             =head1 SUPPORT
582              
583             You can find documentation for this module with the perldoc command.
584              
585             perldoc Acme::FishFarm::WaterConditionMonitor
586              
587              
588             You can also look for information at:
589              
590             =over 4
591              
592             =item * RT: CPAN's request tracker (report bugs here)
593              
594             L
595              
596             =item * CPAN Ratings
597              
598             L
599              
600             =item * Search CPAN
601              
602             L
603              
604             =back
605              
606              
607             =head1 ACKNOWLEDGEMENTS
608              
609             Besiyata d'shmaya
610              
611             =head1 LICENSE AND COPYRIGHT
612              
613             This software is Copyright (c) 2021 by Raphael Jong Jun Jie.
614              
615             This is free software, licensed under:
616              
617             The Artistic License 2.0 (GPL Compatible)
618              
619              
620             =cut
621              
622             1; # End of Acme::FishFarm::WaterConditionMonitor