| 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
|
|
|
|
|
|
|
|