line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Device::Regulator::Plasmatronic; |
2
|
1
|
|
|
1
|
|
5
|
use strict; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
28
|
|
3
|
1
|
|
|
1
|
|
976
|
use IO::File; |
|
1
|
|
|
|
|
11210
|
|
|
1
|
|
|
|
|
178
|
|
4
|
1
|
|
|
1
|
|
1219
|
use IO::Select; |
|
1
|
|
|
|
|
1704
|
|
|
1
|
|
|
|
|
49
|
|
5
|
1
|
|
|
1
|
|
7
|
use Carp; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
65
|
|
6
|
1
|
|
|
1
|
|
5
|
use vars qw($AUTOLOAD); |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
38
|
|
7
|
1
|
|
|
1
|
|
1113
|
use Time::HiRes qw(usleep); |
|
1
|
|
|
|
|
1957
|
|
|
1
|
|
|
|
|
5
|
|
8
|
1
|
|
|
1
|
|
1114
|
use POSIX; |
|
1
|
|
|
|
|
7526
|
|
|
1
|
|
|
|
|
8
|
|
9
|
1
|
|
|
1
|
|
3184
|
use Fcntl; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
2612
|
|
10
|
|
|
|
|
|
|
|
11
|
|
|
|
|
|
|
our $VERSION = "0.03"; |
12
|
|
|
|
|
|
|
|
13
|
|
|
|
|
|
|
my $TEMP_DELAY = 100; # Microsecond (I think...) |
14
|
|
|
|
|
|
|
my $TEMP_TIMEOUT = 2; # Max time for an entry (seconds) |
15
|
|
|
|
|
|
|
|
16
|
|
|
|
|
|
|
=head1 NAME |
17
|
|
|
|
|
|
|
|
18
|
|
|
|
|
|
|
Plasmatronics - Plasmatronics PL regulator controller |
19
|
|
|
|
|
|
|
|
20
|
|
|
|
|
|
|
=head1 SYNOPSIS |
21
|
|
|
|
|
|
|
|
22
|
|
|
|
|
|
|
use Device::Regulator::Plasmatronic; |
23
|
|
|
|
|
|
|
my $r = Device::Regulator::Plasmatronic; |
24
|
|
|
|
|
|
|
print "Current state of charge = " . $r->pl_dsoc . "\n"; |
25
|
|
|
|
|
|
|
|
26
|
|
|
|
|
|
|
=head1 DESCRIPTION |
27
|
|
|
|
|
|
|
|
28
|
|
|
|
|
|
|
This is an interface library via the serial port to a Plasmatronics Regulator. |
29
|
|
|
|
|
|
|
(http://www.plasmatronics.com.au/) |
30
|
|
|
|
|
|
|
|
31
|
|
|
|
|
|
|
=head1 MAJOR LIMITATIONS |
32
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
=head2 Serial Port |
34
|
|
|
|
|
|
|
|
35
|
|
|
|
|
|
|
I have to replace the serial port driver - currently I use the unix only version, |
36
|
|
|
|
|
|
|
but I have written |
37
|
|
|
|
|
|
|
|
38
|
|
|
|
|
|
|
=head2 Hard Coded Multiplier |
39
|
|
|
|
|
|
|
|
40
|
|
|
|
|
|
|
The multiplier used for voltages etc is hard coded (currently 4 = 48 Volt system). |
41
|
|
|
|
|
|
|
This can be read from the system, so I will have to do that as part of the |
42
|
|
|
|
|
|
|
initialisation. |
43
|
|
|
|
|
|
|
|
44
|
|
|
|
|
|
|
=head2 Combined Values |
45
|
|
|
|
|
|
|
|
46
|
|
|
|
|
|
|
Load and other things combine values from multiple locations to allow for larger |
47
|
|
|
|
|
|
|
numbers. I know that I have got this wrong in a number of places. Work to be done |
48
|
|
|
|
|
|
|
to test these for large numbers (eg: > 25 Amps etc). |
49
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
=head1 FUTURE |
51
|
|
|
|
|
|
|
|
52
|
|
|
|
|
|
|
=head2 Fix Limitations |
53
|
|
|
|
|
|
|
|
54
|
|
|
|
|
|
|
As above, look at each limitation and try and fix it up. |
55
|
|
|
|
|
|
|
|
56
|
|
|
|
|
|
|
=head2 CGI Scripts |
57
|
|
|
|
|
|
|
|
58
|
|
|
|
|
|
|
Write a number of example CGI scripts |
59
|
|
|
|
|
|
|
|
60
|
|
|
|
|
|
|
=head2 Graphing |
61
|
|
|
|
|
|
|
|
62
|
|
|
|
|
|
|
Include a graph of the history, or even daily history of the system. |
63
|
|
|
|
|
|
|
|
64
|
|
|
|
|
|
|
=head2 Learning Kit |
65
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
|
Put together the whole kit of files above so that it can be used in learning |
67
|
|
|
|
|
|
|
environments etc to demonstrate logging, power use etc. |
68
|
|
|
|
|
|
|
|
69
|
|
|
|
|
|
|
=head2 Power Control Link |
70
|
|
|
|
|
|
|
|
71
|
|
|
|
|
|
|
My house has most lights and equipment controlled by the computer, which means |
72
|
|
|
|
|
|
|
combined with current load we get a really good idea how much power is used |
73
|
|
|
|
|
|
|
when things are switched on. This also means we can work out how much power is |
74
|
|
|
|
|
|
|
used by which piece of equipment (over time), and monitor the standard load (eg: |
75
|
|
|
|
|
|
|
what is on all the time like the Fridge). |
76
|
|
|
|
|
|
|
|
77
|
|
|
|
|
|
|
=head1 TOOLS |
78
|
|
|
|
|
|
|
|
79
|
|
|
|
|
|
|
I have documented here the tools that come with this, although they are not part |
80
|
|
|
|
|
|
|
of this library, it is a convenient place to put them. |
81
|
|
|
|
|
|
|
|
82
|
|
|
|
|
|
|
=head2 plbackup / plrestore |
83
|
|
|
|
|
|
|
|
84
|
|
|
|
|
|
|
This allows you to backup all the data currently in the regulator. |
85
|
|
|
|
|
|
|
This is very handy if you want to work on the regulator which involves disconnecting |
86
|
|
|
|
|
|
|
the power. You then loose all the data for the current day. This allows you to |
87
|
|
|
|
|
|
|
keep that information, not even loosing any data (except for the period it is off). |
88
|
|
|
|
|
|
|
|
89
|
|
|
|
|
|
|
=head2 plhistory |
90
|
|
|
|
|
|
|
|
91
|
|
|
|
|
|
|
Display the history. |
92
|
|
|
|
|
|
|
|
93
|
|
|
|
|
|
|
=head2 plload |
94
|
|
|
|
|
|
|
|
95
|
|
|
|
|
|
|
A simple example of some load variables displayed. A good one to look at on how |
96
|
|
|
|
|
|
|
you would write your own code. |
97
|
|
|
|
|
|
|
|
98
|
|
|
|
|
|
|
=head2 pllogger |
99
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
This writes all daily entries to a log file, good for long term logging accross |
101
|
|
|
|
|
|
|
long periods. You could adapt this to log any data in the system at any interval. |
102
|
|
|
|
|
|
|
|
103
|
|
|
|
|
|
|
=head2 plloopback |
104
|
|
|
|
|
|
|
|
105
|
|
|
|
|
|
|
Just test the loopback. You can run this to make sure the unit and code is working. |
106
|
|
|
|
|
|
|
Handy to put in a test script, you could for example trigger an alarm if the |
107
|
|
|
|
|
|
|
systems goes down. |
108
|
|
|
|
|
|
|
|
109
|
|
|
|
|
|
|
=head2 plread / plwrite |
110
|
|
|
|
|
|
|
|
111
|
|
|
|
|
|
|
Read and write to any variable in the system. Your raw access tool. |
112
|
|
|
|
|
|
|
|
113
|
|
|
|
|
|
|
=head2 pltest |
114
|
|
|
|
|
|
|
|
115
|
|
|
|
|
|
|
Another test code - not really necessary but I use it mostly to generally test |
116
|
|
|
|
|
|
|
my changes. |
117
|
|
|
|
|
|
|
|
118
|
|
|
|
|
|
|
=head2 pltime |
119
|
|
|
|
|
|
|
|
120
|
|
|
|
|
|
|
Read and write the time on the system. You can setup a job to set the time |
121
|
|
|
|
|
|
|
correctly from your server on regular basis, or call it after a plrestore. |
122
|
|
|
|
|
|
|
|
123
|
|
|
|
|
|
|
=head1 EXAMPLE CODE |
124
|
|
|
|
|
|
|
|
125
|
|
|
|
|
|
|
=head2 Initialisation |
126
|
|
|
|
|
|
|
|
127
|
|
|
|
|
|
|
my $pl = Plasmatronics->new(); |
128
|
|
|
|
|
|
|
if ($pl->pl_loopback) { |
129
|
|
|
|
|
|
|
print "Cool\n"; |
130
|
|
|
|
|
|
|
} else { |
131
|
|
|
|
|
|
|
print "Not so cool\n"; |
132
|
|
|
|
|
|
|
} |
133
|
|
|
|
|
|
|
|
134
|
|
|
|
|
|
|
=head2 Read |
135
|
|
|
|
|
|
|
|
136
|
|
|
|
|
|
|
# Init above |
137
|
|
|
|
|
|
|
print "Current load = " . $pl->pl_load . "\n"; |
138
|
|
|
|
|
|
|
|
139
|
|
|
|
|
|
|
=head2 Write |
140
|
|
|
|
|
|
|
|
141
|
|
|
|
|
|
|
# Change the hour |
142
|
|
|
|
|
|
|
print "New hour = " . $pl->pl_hour(15) . "\n"; |
143
|
|
|
|
|
|
|
|
144
|
|
|
|
|
|
|
=head2 Full Example Used for remote display |
145
|
|
|
|
|
|
|
|
146
|
|
|
|
|
|
|
# This example could be used for an app, cgi or remote display |
147
|
|
|
|
|
|
|
|
148
|
|
|
|
|
|
|
use Plasmatronics; |
149
|
|
|
|
|
|
|
|
150
|
|
|
|
|
|
|
my $pl = Plasmatronics->new() || die "Can't connect to PL"; |
151
|
|
|
|
|
|
|
|
152
|
|
|
|
|
|
|
my $soc = $pl->pl_dsoc; |
153
|
|
|
|
|
|
|
print $soc . "%\n"; |
154
|
|
|
|
|
|
|
my $load = $pl->pl_load; |
155
|
|
|
|
|
|
|
my $charge = $pl->pl_charge; |
156
|
|
|
|
|
|
|
print "OUT $load, IN $charge\n"; |
157
|
|
|
|
|
|
|
|
158
|
|
|
|
|
|
|
=head1 DATA FILE |
159
|
|
|
|
|
|
|
|
160
|
|
|
|
|
|
|
The data file (plasmatronics.dat) contains all the clever information. |
161
|
|
|
|
|
|
|
|
162
|
|
|
|
|
|
|
So why a data file and not hard coded. Well theoretically I want to be able |
163
|
|
|
|
|
|
|
to write alternate versions of this software in other languages (eg: Java, |
164
|
|
|
|
|
|
|
Python, or even a windows DLL/OLE). By keeping any of the non language |
165
|
|
|
|
|
|
|
specific intelligence in the data file, this can be shared, it is also |
166
|
|
|
|
|
|
|
a much neater way of doing development. |
167
|
|
|
|
|
|
|
|
168
|
|
|
|
|
|
|
=head2 Parameters |
169
|
|
|
|
|
|
|
|
170
|
|
|
|
|
|
|
- Short Name |
171
|
|
|
|
|
|
|
- Number |
172
|
|
|
|
|
|
|
- Full description |
173
|
|
|
|
|
|
|
- Divide by (number) |
174
|
|
|
|
|
|
|
- Multiply by (number or BM) |
175
|
|
|
|
|
|
|
- Unit |
176
|
|
|
|
|
|
|
- ShiftLeft by (other name or NA) |
177
|
|
|
|
|
|
|
- Write flag |
178
|
|
|
|
|
|
|
- Non NV Backup/Restore (should it be backed up) |
179
|
|
|
|
|
|
|
|
180
|
|
|
|
|
|
|
The combination of these allows us to do most of the intelligent calculations |
181
|
|
|
|
|
|
|
in the data file. |
182
|
|
|
|
|
|
|
|
183
|
|
|
|
|
|
|
=head2 Mapping to methods |
184
|
|
|
|
|
|
|
|
185
|
|
|
|
|
|
|
Each of the short names maps to the equivellent method starting with 'pl_'. |
186
|
|
|
|
|
|
|
The nice part about this is it means you can write code with that name that |
187
|
|
|
|
|
|
|
is used in place of the generic code. This is kind of useful when you want |
188
|
|
|
|
|
|
|
to do more complicated calculations which can not be covered in the data file. |
189
|
|
|
|
|
|
|
|
190
|
|
|
|
|
|
|
=head1 METHODS |
191
|
|
|
|
|
|
|
|
192
|
|
|
|
|
|
|
Here are the methods... |
193
|
|
|
|
|
|
|
|
194
|
|
|
|
|
|
|
=cut |
195
|
|
|
|
|
|
|
|
196
|
|
|
|
|
|
|
# ============================================================================== |
197
|
|
|
|
|
|
|
# Configuration |
198
|
|
|
|
|
|
|
# ============================================================================== |
199
|
|
|
|
|
|
|
# TODO: Check if these change per model. |
200
|
|
|
|
|
|
|
my $commands = { |
201
|
|
|
|
|
|
|
readproc => 20, # Read from processor location |
202
|
|
|
|
|
|
|
readnvram => 72, # Read from NV Ram |
203
|
|
|
|
|
|
|
writeproc => 152, # Write to processor location |
204
|
|
|
|
|
|
|
writenvram => 202, # Write to NV Ram |
205
|
|
|
|
|
|
|
loopback => 187 |
206
|
|
|
|
|
|
|
}; |
207
|
|
|
|
|
|
|
|
208
|
|
|
|
|
|
|
# ============================================================================== |
209
|
|
|
|
|
|
|
# INITIALISATION |
210
|
|
|
|
|
|
|
# ============================================================================== |
211
|
|
|
|
|
|
|
# XXX: How to work out the device? |
212
|
|
|
|
|
|
|
# - Arguments |
213
|
|
|
|
|
|
|
# - Configuration file |
214
|
|
|
|
|
|
|
# - Default |
215
|
|
|
|
|
|
|
# TODO: Change the default port (via serial port driver) to windows version on |
216
|
|
|
|
|
|
|
# windows, etc. |
217
|
|
|
|
|
|
|
sub new { |
218
|
0
|
|
|
0
|
0
|
|
my ($class, %args) = @_; |
219
|
0
|
|
0
|
|
|
|
my $this = bless {}, ref($class) || $class; |
220
|
0
|
|
0
|
|
|
|
$this->{PORT}{NAME} = $args{port} || "/dev/plasmatronic"; |
221
|
0
|
|
0
|
|
|
|
$this->{PORT}{TYPE} = $args{type} || "FILE"; |
222
|
0
|
|
|
|
|
|
$this->_port_init; |
223
|
0
|
|
|
|
|
|
$this->_read_dat; |
224
|
0
|
|
|
|
|
|
return $this; |
225
|
|
|
|
|
|
|
} |
226
|
|
|
|
|
|
|
|
227
|
|
|
|
|
|
|
# Read in plasmatronics.dat |
228
|
|
|
|
|
|
|
sub _read_dat { |
229
|
0
|
|
|
0
|
|
|
my ($this) = @_; |
230
|
0
|
|
|
|
|
|
my %h = (); |
231
|
|
|
|
|
|
|
# XXX: How do you get the location of this file? |
232
|
|
|
|
|
|
|
# (temp symlink from etc !!!) |
233
|
0
|
|
|
|
|
|
close IN; |
234
|
0
|
|
|
|
|
|
foreach my $f ('plasmatronic.dat', '/etc/plasmatronic.dat'){ |
235
|
0
|
0
|
|
|
|
|
next if (! -f $f); |
236
|
0
|
0
|
|
|
|
|
open (IN, $f) || die "Can't open found file $f"; |
237
|
|
|
|
|
|
|
} |
238
|
|
|
|
|
|
|
# XXX: Check it is open? |
239
|
0
|
|
|
|
|
|
while () { |
240
|
0
|
|
|
|
|
|
chomp; |
241
|
0
|
0
|
|
|
|
|
if (! /^#/) { |
242
|
0
|
|
|
|
|
|
my @arr = split(/ *\t+ */, $_); |
243
|
|
|
|
|
|
|
# Convert hex values |
244
|
0
|
0
|
|
|
|
|
if (substr($arr[1], 0,1) eq "h") { |
245
|
0
|
|
|
|
|
|
$arr[1] = hex(substr($arr[1], 1,2)); |
246
|
|
|
|
|
|
|
} |
247
|
0
|
|
|
|
|
|
$h{$arr[0]}{number} = $arr[1]; |
248
|
0
|
|
|
|
|
|
$h{$arr[0]}{note} = $arr[2]; |
249
|
0
|
|
|
|
|
|
$h{$arr[0]}{divider} = $arr[3]; |
250
|
0
|
|
|
|
|
|
$h{$arr[0]}{multiplier} = $arr[4]; |
251
|
0
|
|
|
|
|
|
$h{$arr[0]}{unit} = $arr[5]; |
252
|
0
|
|
|
|
|
|
$h{$arr[0]}{shiftleft} = $arr[6]; |
253
|
0
|
|
|
|
|
|
$h{$arr[0]}{write} = $arr[7]; |
254
|
0
|
|
|
|
|
|
$h{$arr[0]}{backup} = $arr[8]; |
255
|
|
|
|
|
|
|
} |
256
|
|
|
|
|
|
|
} |
257
|
0
|
|
|
|
|
|
close IN; |
258
|
0
|
|
|
|
|
|
$this->{DAT} = \%h; |
259
|
|
|
|
|
|
|
} |
260
|
|
|
|
|
|
|
|
261
|
|
|
|
|
|
|
# Serial port |
262
|
|
|
|
|
|
|
sub _port_init { |
263
|
0
|
|
|
0
|
|
|
my ($this) = @_; |
264
|
|
|
|
|
|
|
|
265
|
0
|
0
|
|
|
|
|
if ($this->{PORT}{TYPE} eq "FILE") { |
266
|
|
|
|
|
|
|
# XXX - Errors here ? |
267
|
0
|
|
|
|
|
|
$this->{PORT}{REF} = new IO::File "+< " . $this->{PORT}{NAME}; |
268
|
|
|
|
|
|
|
|
269
|
0
|
|
|
|
|
|
my $DisplayFD = fileno ($this->{PORT}{REF}) ; |
270
|
|
|
|
|
|
|
|
271
|
0
|
|
|
|
|
|
my $DisplayTermios = POSIX::Termios->new () ; |
272
|
0
|
|
|
|
|
|
$DisplayTermios->getattr ($DisplayFD) ; |
273
|
|
|
|
|
|
|
|
274
|
0
|
|
|
|
|
|
$DisplayTermios->setispeed (B9600) ; # serial input speed (19200bps) |
275
|
0
|
|
|
|
|
|
$DisplayTermios->setospeed (B9600) ; # serial output speed (19200bps) |
276
|
|
|
|
|
|
|
|
277
|
0
|
|
|
|
|
|
my $CFlag = $DisplayTermios->getcflag () ; |
278
|
0
|
|
|
|
|
|
my $LFlag = $DisplayTermios->getlflag () ; |
279
|
0
|
|
|
|
|
|
my $OFlag = $DisplayTermios->getoflag () ; |
280
|
0
|
|
|
|
|
|
my $IFlag = $DisplayTermios->getiflag () ; |
281
|
|
|
|
|
|
|
|
282
|
0
|
|
|
|
|
|
$IFlag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL|IXON) ; # raw IO |
283
|
0
|
|
|
|
|
|
$OFlag &= ~(OPOST) ; |
284
|
0
|
|
|
|
|
|
$LFlag &= ~(ECHO|ECHONL|ICANON|ISIG) ; |
285
|
0
|
|
|
|
|
|
$CFlag &= ~(CSIZE|PARENB|HUPCL) ; |
286
|
0
|
|
|
|
|
|
$CFlag |= (CREAD|CS8|CLOCAL) ; |
287
|
|
|
|
|
|
|
|
288
|
0
|
|
|
|
|
|
$DisplayTermios->setcflag ($CFlag) ; # update serial settings |
289
|
0
|
|
|
|
|
|
$DisplayTermios->setlflag ($LFlag) ; |
290
|
0
|
|
|
|
|
|
$DisplayTermios->setoflag ($OFlag) ; |
291
|
0
|
|
|
|
|
|
$DisplayTermios->setiflag ($IFlag) ; |
292
|
0
|
|
|
|
|
|
$DisplayTermios->setattr ($DisplayFD, TCSANOW) ; # update serial device |
293
|
|
|
|
|
|
|
|
294
|
|
|
|
|
|
|
} else { |
295
|
|
|
|
|
|
|
# Serial device |
296
|
|
|
|
|
|
|
# XXX: The device driver should know the lock file, why does it |
297
|
|
|
|
|
|
|
# insist on each bit of code calcuating the code !!! |
298
|
0
|
|
|
|
|
|
my $lock = $this->{PORT}{NAME}; |
299
|
0
|
|
|
|
|
|
$lock =~ s/\/dev\///; |
300
|
0
|
|
|
|
|
|
$lock = "/var/lock/LCK..$lock"; |
301
|
|
|
|
|
|
|
# 1 = quiet |
302
|
0
|
|
|
|
|
|
eval q{use Device::SerialPort;}; |
303
|
0
|
0
|
|
|
|
|
die "Failed to load Device::SerialPort - $@" if ($@); |
304
|
0
|
|
0
|
|
|
|
$this->{PORT}{REF} = new Device::SerialPort ($this->{PORT}{NAME}, 0, $lock) |
305
|
|
|
|
|
|
|
|| die "Can't open " . $this->{PORT}{REF} . ": $!\n"; |
306
|
0
|
|
|
|
|
|
$this->_port->baudrate(9600); |
307
|
0
|
|
|
|
|
|
$this->_port->parity("none"); |
308
|
0
|
|
|
|
|
|
$this->_port->databits(8); |
309
|
0
|
|
|
|
|
|
$this->_port->stopbits(1); |
310
|
|
|
|
|
|
|
} |
311
|
|
|
|
|
|
|
# XXX Check this works for Device::SerialPort too. |
312
|
0
|
|
|
|
|
|
$this->{SELECT} = new IO::Select; |
313
|
0
|
|
|
|
|
|
$this->_select->add($this->_port()); |
314
|
|
|
|
|
|
|
} |
315
|
|
|
|
|
|
|
|
316
|
|
|
|
|
|
|
sub _port { |
317
|
0
|
|
|
0
|
|
|
my ($this) = @_; |
318
|
0
|
|
|
|
|
|
return $this->{PORT}{REF}; |
319
|
|
|
|
|
|
|
} |
320
|
|
|
|
|
|
|
|
321
|
|
|
|
|
|
|
sub _select { |
322
|
0
|
|
|
0
|
|
|
my ($this) = @_; |
323
|
0
|
|
|
|
|
|
return $this->{SELECT}; |
324
|
|
|
|
|
|
|
} |
325
|
|
|
|
|
|
|
|
326
|
|
|
|
|
|
|
# ============================================================================== |
327
|
|
|
|
|
|
|
# COMMANDS |
328
|
|
|
|
|
|
|
# ============================================================================== |
329
|
|
|
|
|
|
|
# Match a list |
330
|
|
|
|
|
|
|
sub list { |
331
|
0
|
|
|
0
|
0
|
|
my ($this, $match) = @_; |
332
|
0
|
|
|
|
|
|
my @ret = (); |
333
|
0
|
|
|
|
|
|
MATCH: foreach my $key (sort {$a cmp $b} keys %{$this->{DAT}}) { |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
334
|
0
|
0
|
|
|
|
|
if (defined($match)) { |
335
|
0
|
|
|
|
|
|
warn "Doing matches"; |
336
|
0
|
|
|
|
|
|
foreach my $m (keys %{$match}) { |
|
0
|
|
|
|
|
|
|
337
|
0
|
|
|
|
|
|
warn "\tMatching on $m"; |
338
|
0
|
|
|
|
|
|
warn "\t\tDAT = " . $this->{DAT}{$key}{$m}; |
339
|
0
|
|
|
|
|
|
warn "\t\tMATCH = " . $match->{$m}; |
340
|
0
|
0
|
|
|
|
|
if ($match->{$m} ne $this->{DAT}{$key}{$m}) { |
341
|
0
|
|
|
|
|
|
warn "\t\tNO MATCH for $m"; |
342
|
0
|
|
|
|
|
|
next MATCH; |
343
|
|
|
|
|
|
|
} |
344
|
|
|
|
|
|
|
} |
345
|
|
|
|
|
|
|
} |
346
|
0
|
|
|
|
|
|
warn "ADDING $key"; |
347
|
0
|
|
|
|
|
|
push @ret, $key; |
348
|
|
|
|
|
|
|
} |
349
|
0
|
0
|
|
|
|
|
if (wantarray()) { |
350
|
0
|
|
|
|
|
|
return @ret; |
351
|
|
|
|
|
|
|
} else { |
352
|
0
|
|
|
|
|
|
return \@ret; |
353
|
|
|
|
|
|
|
} |
354
|
|
|
|
|
|
|
} |
355
|
|
|
|
|
|
|
|
356
|
|
|
|
|
|
|
sub pl_loopback { |
357
|
0
|
|
|
0
|
0
|
|
my ($this) = @_; |
358
|
0
|
|
|
|
|
|
$this->_write( |
359
|
|
|
|
|
|
|
$commands->{'loopback'}, |
360
|
|
|
|
|
|
|
0, |
361
|
|
|
|
|
|
|
0, |
362
|
|
|
|
|
|
|
255 - $commands->{'loopback'} |
363
|
|
|
|
|
|
|
); |
364
|
0
|
|
|
|
|
|
my $buf = $this->_read(1); |
365
|
0
|
|
|
|
|
|
return (ord($buf) == 128); |
366
|
|
|
|
|
|
|
} |
367
|
|
|
|
|
|
|
|
368
|
|
|
|
|
|
|
=head2 data |
369
|
|
|
|
|
|
|
|
370
|
|
|
|
|
|
|
Read or write to a processor or nvram location. |
371
|
|
|
|
|
|
|
|
372
|
|
|
|
|
|
|
Returned values are always adjusted to make your life easier, but that is not |
373
|
|
|
|
|
|
|
so easy for writting, so that has not yet been implemented. |
374
|
|
|
|
|
|
|
|
375
|
|
|
|
|
|
|
=cut |
376
|
|
|
|
|
|
|
|
377
|
|
|
|
|
|
|
sub data { |
378
|
0
|
|
|
0
|
1
|
|
my ($this, $name, $value) = @_; |
379
|
0
|
|
|
|
|
|
$this->initparams; |
380
|
0
|
0
|
|
|
|
|
if (!defined($this->{DAT}{$name})) { |
381
|
0
|
|
|
|
|
|
carp "Invalid data requested - " . $name; |
382
|
|
|
|
|
|
|
} |
383
|
|
|
|
|
|
|
|
384
|
|
|
|
|
|
|
# Write |
385
|
0
|
0
|
|
|
|
|
if (defined($value)) { |
386
|
0
|
0
|
|
|
|
|
if (! $this->{DAT}{$name}{write}) { |
387
|
0
|
|
|
|
|
|
croak "Trying to write to a read only value - " . $name; |
388
|
|
|
|
|
|
|
} |
389
|
0
|
|
|
|
|
|
my $cmd = $commands->{'writeproc'}; |
390
|
0
|
0
|
|
|
|
|
if ($name =~ /^nv/) { |
391
|
0
|
|
|
|
|
|
$cmd = $commands->{'writenvram'}; |
392
|
|
|
|
|
|
|
} |
393
|
|
|
|
|
|
|
$this->_write( |
394
|
0
|
|
|
|
|
|
$cmd, |
395
|
|
|
|
|
|
|
$this->{DAT}{$name}{number}, |
396
|
|
|
|
|
|
|
$value, |
397
|
|
|
|
|
|
|
255 - $cmd |
398
|
|
|
|
|
|
|
); |
399
|
|
|
|
|
|
|
} |
400
|
|
|
|
|
|
|
|
401
|
|
|
|
|
|
|
# Proc or NV read |
402
|
0
|
|
|
|
|
|
my $cmd = $commands->{'readproc'}; |
403
|
0
|
0
|
|
|
|
|
if ($name =~ /^nv/) { |
404
|
0
|
|
|
|
|
|
$cmd = $commands->{'readnvram'}; |
405
|
|
|
|
|
|
|
} |
406
|
|
|
|
|
|
|
|
407
|
|
|
|
|
|
|
# Send command |
408
|
|
|
|
|
|
|
$this->_write( |
409
|
0
|
|
|
|
|
|
$cmd, |
410
|
|
|
|
|
|
|
$this->{DAT}{$name}{number}, |
411
|
|
|
|
|
|
|
0, |
412
|
|
|
|
|
|
|
255 - $cmd |
413
|
|
|
|
|
|
|
); |
414
|
|
|
|
|
|
|
|
415
|
|
|
|
|
|
|
# Get results |
416
|
0
|
0
|
|
|
|
|
my $buf = $this->_read(2) |
417
|
|
|
|
|
|
|
or return undef; |
418
|
0
|
|
|
|
|
|
my $out = ord(substr($buf, 1, 1)); |
419
|
|
|
|
|
|
|
|
420
|
|
|
|
|
|
|
# Check value |
421
|
0
|
0
|
0
|
|
|
|
if (defined($value) && ($out != $value)) { |
422
|
0
|
|
|
|
|
|
warn "Received value was not what was written"; |
423
|
|
|
|
|
|
|
} |
424
|
|
|
|
|
|
|
|
425
|
|
|
|
|
|
|
# Shift left by? |
426
|
|
|
|
|
|
|
# Not quite right. |
427
|
|
|
|
|
|
|
# 8 bits = * by 10 |
428
|
|
|
|
|
|
|
# 32 bits = * |
429
|
0
|
0
|
|
|
|
|
if ($this->{DAT}{$name}{shiftleft} ne "NA") { |
430
|
0
|
|
|
|
|
|
my $sl = $this->data($this->{DAT}{$name}{shiftleft}); |
431
|
0
|
|
|
|
|
|
$out = $out << $sl; |
432
|
|
|
|
|
|
|
} |
433
|
|
|
|
|
|
|
|
434
|
|
|
|
|
|
|
# Multiplier and Divider |
435
|
0
|
0
|
|
|
|
|
if ($this->{DAT}{$name}{multiplier} eq "BM") { |
436
|
0
|
|
|
|
|
|
$out = $out * $this->{PARAMS}{MULTIPLIER}; |
437
|
|
|
|
|
|
|
} else { |
438
|
0
|
|
|
|
|
|
$out = $out * $this->{DAT}{$name}{multiplier}; |
439
|
|
|
|
|
|
|
} |
440
|
0
|
|
|
|
|
|
$out = $out / $this->{DAT}{$name}{divider}; |
441
|
0
|
|
|
|
|
|
return $out; |
442
|
|
|
|
|
|
|
} |
443
|
|
|
|
|
|
|
|
444
|
|
|
|
|
|
|
sub exists { |
445
|
0
|
|
|
0
|
0
|
|
my ($this, $name) = @_; |
446
|
0
|
|
|
|
|
|
return exists($this->{DAT}{$name}); |
447
|
|
|
|
|
|
|
} |
448
|
|
|
|
|
|
|
|
449
|
|
|
|
|
|
|
sub unit { |
450
|
0
|
|
|
0
|
0
|
|
my ($this, $name) = @_; |
451
|
0
|
|
|
|
|
|
return $this->{DAT}{$name}{unit}; |
452
|
|
|
|
|
|
|
} |
453
|
|
|
|
|
|
|
|
454
|
|
|
|
|
|
|
sub note { |
455
|
0
|
|
|
0
|
0
|
|
my ($this, $name) = @_; |
456
|
0
|
|
|
|
|
|
return $this->{DAT}{$name}{note}; |
457
|
|
|
|
|
|
|
} |
458
|
|
|
|
|
|
|
|
459
|
|
|
|
|
|
|
# Autoload for all methods (all others) |
460
|
|
|
|
|
|
|
sub AUTOLOAD { |
461
|
0
|
|
|
0
|
|
|
my ($this, $val) = @_; |
462
|
0
|
0
|
0
|
|
|
|
if ($AUTOLOAD =~ /::pl_(.*)$/ && $this->exists($1)) { |
463
|
0
|
|
|
|
|
|
return $this->data($1, $val); |
464
|
|
|
|
|
|
|
} else { |
465
|
0
|
|
|
|
|
|
carp "Invalid method called - $AUTOLOAD"; |
466
|
|
|
|
|
|
|
} |
467
|
|
|
|
|
|
|
} |
468
|
|
|
|
|
|
|
|
469
|
0
|
|
|
0
|
|
|
sub DESTROY { |
470
|
|
|
|
|
|
|
} |
471
|
|
|
|
|
|
|
|
472
|
|
|
|
|
|
|
# ============================================================================== |
473
|
|
|
|
|
|
|
# SPECIAL HELPERS (not defined by autoloader, usually because to complicated) |
474
|
|
|
|
|
|
|
# ============================================================================== |
475
|
|
|
|
|
|
|
sub pl_out { |
476
|
0
|
|
|
0
|
0
|
|
my ($this) = @_; |
477
|
|
|
|
|
|
|
# Need high byte too |
478
|
0
|
|
|
|
|
|
return $this->data('leahl') + $this->data('liahl'); |
479
|
|
|
|
|
|
|
} |
480
|
|
|
|
|
|
|
sub pl_in { |
481
|
0
|
|
|
0
|
0
|
|
my ($this) = @_; |
482
|
0
|
|
|
|
|
|
return $this->data('ciahl') + $this->data('ceahl'); |
483
|
|
|
|
|
|
|
} |
484
|
|
|
|
|
|
|
sub pl_load { |
485
|
0
|
|
|
0
|
0
|
|
my ($this) = @_; |
486
|
0
|
|
|
|
|
|
return $this->data('lint') + $this->data('lext'); |
487
|
|
|
|
|
|
|
} |
488
|
|
|
|
|
|
|
sub pl_charge { |
489
|
0
|
|
|
0
|
0
|
|
my ($this) = @_; |
490
|
0
|
|
|
|
|
|
return $this->data('cint') + $this->data('cext'); |
491
|
|
|
|
|
|
|
} |
492
|
|
|
|
|
|
|
|
493
|
|
|
|
|
|
|
|
494
|
|
|
|
|
|
|
# ============================================================================== |
495
|
|
|
|
|
|
|
# INITIALISATION INTERNALLY (batv divider etc.) |
496
|
|
|
|
|
|
|
# ============================================================================== |
497
|
|
|
|
|
|
|
sub initparams { |
498
|
0
|
|
|
0
|
0
|
|
my ($this) = @_; |
499
|
|
|
|
|
|
|
# XXX: Get this somehow, only if we don't already have it |
500
|
0
|
|
|
|
|
|
$this->{PARAMS}{MULTIPLIER} = "4"; |
501
|
|
|
|
|
|
|
} |
502
|
|
|
|
|
|
|
|
503
|
|
|
|
|
|
|
# ============================================================================== |
504
|
|
|
|
|
|
|
# READ and WRITE to Serial Port |
505
|
|
|
|
|
|
|
# ============================================================================== |
506
|
|
|
|
|
|
|
# XXX: Arbitrary sleeps to cope with no flow control. Parameterise and |
507
|
|
|
|
|
|
|
# otherwise work out better ways to deal with. |
508
|
|
|
|
|
|
|
sub _write { |
509
|
0
|
|
|
0
|
|
|
my ($this, @arr) = @_; |
510
|
0
|
|
|
|
|
|
my $out = ""; |
511
|
0
|
0
|
|
|
|
|
if ($this->{PORT}{TYPE} eq "FILE") { |
512
|
0
|
|
|
|
|
|
foreach my $bit (@arr) { |
513
|
0
|
|
|
|
|
|
usleep $TEMP_DELAY; |
514
|
|
|
|
|
|
|
# $out .= chr($bit); |
515
|
0
|
|
|
|
|
|
my @ready = $this->_select->can_write($TEMP_TIMEOUT); |
516
|
0
|
0
|
|
|
|
|
if (scalar(@ready) < 1) { |
517
|
0
|
|
|
|
|
|
croak "Timeout on write"; |
518
|
|
|
|
|
|
|
} |
519
|
0
|
|
|
|
|
|
$this->_port->syswrite(chr($bit), 1); |
520
|
|
|
|
|
|
|
} |
521
|
|
|
|
|
|
|
# return $this->_port->syswrite($out, length($out)); |
522
|
|
|
|
|
|
|
} else { |
523
|
0
|
|
|
|
|
|
my @ready = $this->_select->can_write($TEMP_TIMEOUT); |
524
|
0
|
0
|
|
|
|
|
if (scalar(@ready) < 1) { |
525
|
0
|
|
|
|
|
|
croak "Timeout on write"; |
526
|
|
|
|
|
|
|
} |
527
|
0
|
|
|
|
|
|
return $this->_port->write($out); |
528
|
|
|
|
|
|
|
} |
529
|
|
|
|
|
|
|
} |
530
|
|
|
|
|
|
|
|
531
|
|
|
|
|
|
|
sub _read { |
532
|
0
|
|
|
0
|
|
|
my ($this, $len) = @_; |
533
|
0
|
|
|
|
|
|
my $buf = ""; |
534
|
0
|
|
|
|
|
|
my $tmp = ""; |
535
|
0
|
|
|
|
|
|
eval { |
536
|
|
|
|
|
|
|
# local $SIG{__DIE__} = sub {die $_[0];}; |
537
|
|
|
|
|
|
|
# local $SIG{ALRM} = sub {die "timeout";}; |
538
|
0
|
|
|
|
|
|
for (my $i = 0; $i < $len; $i++) { |
539
|
0
|
|
|
|
|
|
usleep $TEMP_DELAY; |
540
|
0
|
|
|
|
|
|
my @ready = $this->_select->can_read($TEMP_TIMEOUT); |
541
|
0
|
0
|
|
|
|
|
if (scalar(@ready) < 1) { |
542
|
0
|
|
|
|
|
|
croak "Timeout on read"; |
543
|
|
|
|
|
|
|
} |
544
|
0
|
0
|
|
|
|
|
if ($this->{PORT}{TYPE} eq "FILE") { |
545
|
0
|
|
|
|
|
|
my $num = $this->_port->sysread($tmp, 1); |
546
|
0
|
|
|
|
|
|
$buf .= $tmp; |
547
|
|
|
|
|
|
|
} else { |
548
|
0
|
|
|
|
|
|
my ($num, $tmp) = $this->_port->read(1); |
549
|
0
|
|
|
|
|
|
$buf .= $tmp; |
550
|
|
|
|
|
|
|
} |
551
|
|
|
|
|
|
|
} |
552
|
|
|
|
|
|
|
}; |
553
|
0
|
0
|
|
|
|
|
if ($@) { |
554
|
0
|
|
|
|
|
|
print STDERR "Failed read (request $len)\n"; |
555
|
0
|
|
|
|
|
|
return undef; |
556
|
|
|
|
|
|
|
} |
557
|
0
|
|
|
|
|
|
return $buf; |
558
|
|
|
|
|
|
|
} |
559
|
|
|
|
|
|
|
|
560
|
|
|
|
|
|
|
# ============================================================================== |
561
|
|
|
|
|
|
|
# END |
562
|
|
|
|
|
|
|
# ============================================================================== |
563
|
|
|
|
|
|
|
|
564
|
|
|
|
|
|
|
1; |
565
|
|
|
|
|
|
|
|