line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package CDR::Parser::SI3000; |
2
|
|
|
|
|
|
|
|
3
|
2
|
|
|
2
|
|
52036
|
use 5.10.0; |
|
2
|
|
|
|
|
41
|
|
|
2
|
|
|
|
|
127
|
|
4
|
2
|
|
|
2
|
|
12
|
use strict; |
|
2
|
|
|
|
|
4
|
|
|
2
|
|
|
|
|
86
|
|
5
|
2
|
|
|
2
|
|
11
|
use warnings FATAL => 'all'; |
|
2
|
|
|
|
|
10
|
|
|
2
|
|
|
|
|
134
|
|
6
|
2
|
|
|
2
|
|
2512
|
use Data::Dumper; |
|
2
|
|
|
|
|
25668
|
|
|
2
|
|
|
|
|
172
|
|
7
|
2
|
|
|
2
|
|
7844
|
use IO::File (); |
|
2
|
|
|
|
|
23780
|
|
|
2
|
|
|
|
|
7248
|
|
8
|
|
|
|
|
|
|
|
9
|
|
|
|
|
|
|
=head1 NAME |
10
|
|
|
|
|
|
|
|
11
|
|
|
|
|
|
|
CDR::Parser::SI3000 - parser for binary CDR files (*.ama) produced by Iskratel SI3000 MSCN telephony product |
12
|
|
|
|
|
|
|
|
13
|
|
|
|
|
|
|
CDR = Call Detail Records |
14
|
|
|
|
|
|
|
|
15
|
|
|
|
|
|
|
=head1 VERSION |
16
|
|
|
|
|
|
|
|
17
|
|
|
|
|
|
|
Version 0.01 |
18
|
|
|
|
|
|
|
|
19
|
|
|
|
|
|
|
=cut |
20
|
|
|
|
|
|
|
|
21
|
|
|
|
|
|
|
our $VERSION = '0.02'; |
22
|
|
|
|
|
|
|
|
23
|
|
|
|
|
|
|
our $VERBOSE = 0; |
24
|
|
|
|
|
|
|
|
25
|
|
|
|
|
|
|
=head1 SYNOPSIS |
26
|
|
|
|
|
|
|
|
27
|
|
|
|
|
|
|
Whis module parses the binary file format and returns it as Perl data. |
28
|
|
|
|
|
|
|
|
29
|
|
|
|
|
|
|
Usage example |
30
|
|
|
|
|
|
|
|
31
|
|
|
|
|
|
|
use CDR::Parser::SI3000; |
32
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
my ($cdr_list, $num_failed) = CDR::Parser::SI3000->parse_file('somefile.ama'); |
34
|
|
|
|
|
|
|
|
35
|
|
|
|
|
|
|
There |
36
|
|
|
|
|
|
|
$cdr_list is a array-reference containing individual records as hash-ref. |
37
|
|
|
|
|
|
|
$num_failed is a number of unparseable records |
38
|
|
|
|
|
|
|
|
39
|
|
|
|
|
|
|
|
40
|
|
|
|
|
|
|
=head1 SUBROUTINES/METHODS |
41
|
|
|
|
|
|
|
|
42
|
|
|
|
|
|
|
=head2 parse_file |
43
|
|
|
|
|
|
|
|
44
|
|
|
|
|
|
|
Get filename as input, open it, read it, returns the parsed result |
45
|
|
|
|
|
|
|
|
46
|
|
|
|
|
|
|
=cut |
47
|
|
|
|
|
|
|
|
48
|
|
|
|
|
|
|
|
49
|
|
|
|
|
|
|
#-- |
50
|
|
|
|
|
|
|
|
51
|
|
|
|
|
|
|
# $| = 1; |
52
|
|
|
|
|
|
|
|
53
|
|
|
|
|
|
|
sub _log { |
54
|
901
|
|
|
901
|
|
2226
|
my($format, @args) = @_; |
55
|
901
|
50
|
|
|
|
3028
|
return if(! $VERBOSE); |
56
|
0
|
|
|
|
|
0
|
printf $format."\n", @args; |
57
|
|
|
|
|
|
|
} |
58
|
|
|
|
|
|
|
|
59
|
|
|
|
|
|
|
# public |
60
|
|
|
|
|
|
|
sub parse_file { |
61
|
1
|
|
|
1
|
1
|
22
|
my($class, $filename) = @_; |
62
|
1
|
50
|
|
|
|
7
|
die "No filename argument" if(! $filename); |
63
|
1
|
|
|
|
|
6
|
_log('Parsing file %s', $filename); |
64
|
|
|
|
|
|
|
|
65
|
1
|
|
50
|
|
|
12
|
my $fh = IO::File->new($filename) || die "Failed to open $filename - $!"; |
66
|
1
|
|
|
|
|
149
|
binmode($fh, ':bytes'); |
67
|
|
|
|
|
|
|
|
68
|
1
|
|
|
|
|
4
|
my @records = (); |
69
|
|
|
|
|
|
|
|
70
|
1
|
|
|
|
|
2
|
my $rows = 0; |
71
|
1
|
|
|
|
|
3
|
my $failed = 0; |
72
|
|
|
|
|
|
|
|
73
|
1
|
|
|
|
|
2
|
while(1) { |
74
|
16
|
|
|
|
|
66
|
my $call = parse_record($fh); |
75
|
16
|
100
|
|
|
|
40
|
if($call) { |
76
|
15
|
|
|
|
|
52
|
push @records, $call; |
77
|
|
|
|
|
|
|
} |
78
|
|
|
|
|
|
|
else { |
79
|
1
|
50
|
|
|
|
6
|
last if($call == 0); |
80
|
0
|
|
|
|
|
0
|
$failed++; |
81
|
|
|
|
|
|
|
} |
82
|
|
|
|
|
|
|
} |
83
|
|
|
|
|
|
|
|
84
|
1
|
|
|
|
|
17
|
$fh->close; |
85
|
|
|
|
|
|
|
|
86
|
1
|
|
|
|
|
35
|
return (\@records, $failed); |
87
|
|
|
|
|
|
|
} |
88
|
|
|
|
|
|
|
|
89
|
|
|
|
|
|
|
#------ private implementation ------ |
90
|
|
|
|
|
|
|
|
91
|
|
|
|
|
|
|
# 100. Called number |
92
|
|
|
|
|
|
|
sub block_100 { |
93
|
15
|
|
|
15
|
0
|
24
|
my($call,$variable) = @_; |
94
|
|
|
|
|
|
|
|
95
|
15
|
|
|
|
|
30
|
_log('100. Called number'); |
96
|
15
|
|
|
|
|
25
|
my $cld_len; |
97
|
15
|
|
|
|
|
92
|
($cld_len, $$variable) = unpack('C a*', $$variable); |
98
|
15
|
|
|
|
|
76
|
my $cut = $cld_len; |
99
|
15
|
50
|
|
|
|
62
|
$cut++ if($cld_len % 2 == 1); |
100
|
15
|
|
|
|
|
19
|
my $cld; |
101
|
15
|
|
|
|
|
93
|
($cld, $$variable) = unpack("H$cut a*", $$variable); |
102
|
15
|
50
|
|
|
|
55
|
if($cut > $cld_len) { |
103
|
15
|
|
|
|
|
42
|
$cld = substr($cld, 0, -1); |
104
|
|
|
|
|
|
|
} |
105
|
15
|
|
|
|
|
42
|
_log(' CLD: %s', $cld); |
106
|
15
|
|
|
|
|
119
|
$call->{cld} = $cld; |
107
|
|
|
|
|
|
|
} |
108
|
|
|
|
|
|
|
|
109
|
|
|
|
|
|
|
# 101. Call accepting party number |
110
|
|
|
|
|
|
|
# 102. Start Date and Time |
111
|
|
|
|
|
|
|
sub block_102 { |
112
|
15
|
|
|
15
|
0
|
26
|
my($call, $var) = @_; |
113
|
15
|
|
|
|
|
35
|
_log("102. Start Date and Time"); |
114
|
15
|
|
|
|
|
17
|
my($year,$month,$day,$hour,$min,$sec,$msec,$reserved); |
115
|
15
|
|
|
|
|
245
|
($year,$month,$day,$hour,$min,$sec,$msec,$reserved,$$var) = unpack('CCCCCCC H2 a*', $$var); |
116
|
15
|
|
|
|
|
67
|
$year += 2000; |
117
|
15
|
|
|
|
|
106
|
my $start_time = sprintf "%04d-%02d-%02d %02d:%02d:%02d.%02d", $year,$month,$day,$hour,$min,$sec,$msec; |
118
|
15
|
|
|
|
|
38
|
_log(' Start Time: %s', $start_time); |
119
|
15
|
|
|
|
|
126
|
$call->{start_time} = $start_time; |
120
|
|
|
|
|
|
|
} |
121
|
|
|
|
|
|
|
# 103. End Date and Time |
122
|
|
|
|
|
|
|
sub block_103 { |
123
|
15
|
|
|
15
|
0
|
27
|
my($call, $var) = @_; |
124
|
15
|
|
|
|
|
28
|
_log("103. End Date and Time"); |
125
|
15
|
|
|
|
|
26
|
my($year,$month,$day,$hour,$min,$sec,$msec,$reliable); |
126
|
15
|
|
|
|
|
176
|
($year,$month,$day,$hour,$min,$sec,$msec,$reliable,$$var) = unpack('CCCCCCC H2 a*', $$var); |
127
|
15
|
|
|
|
|
66
|
$year += 2000; |
128
|
15
|
|
|
|
|
83
|
my $end_time = sprintf "%04d-%02d-%02d %02d:%02d:%02d.%02d", $year,$month,$day,$hour,$min,$sec,$msec; |
129
|
15
|
|
|
|
|
61
|
_log(' End Time: %s', $end_time); |
130
|
15
|
|
|
|
|
115
|
$call->{end_time} = $end_time; |
131
|
|
|
|
|
|
|
} |
132
|
|
|
|
|
|
|
# 104. Number of charging units |
133
|
|
|
|
|
|
|
sub block_104 { |
134
|
15
|
|
|
15
|
0
|
32
|
my($call, $var) = @_; |
135
|
15
|
|
|
|
|
25
|
_log("104. Number of charging units"); |
136
|
15
|
|
|
|
|
24
|
my($unit1,$unit2,$unit3); |
137
|
15
|
|
|
|
|
114
|
($unit1,$unit2,$unit3,$$var) = unpack('CCC a*', $$var); |
138
|
15
|
|
|
|
|
126
|
my $units = ($unit1 << 16) + ($unit2 << 8) + $unit3; |
139
|
15
|
|
|
|
|
45
|
_log(' Units: %d', $units); |
140
|
15
|
|
|
|
|
101
|
$call->{charging_units} = $units; |
141
|
|
|
|
|
|
|
} |
142
|
|
|
|
|
|
|
# 105. Basic service |
143
|
|
|
|
|
|
|
sub block_105 { |
144
|
15
|
|
|
15
|
0
|
26
|
my($call, $var) = @_; |
145
|
15
|
|
|
|
|
34
|
_log("105. Basic service"); |
146
|
15
|
|
|
|
|
66
|
my %bearer_label = ( |
147
|
|
|
|
|
|
|
0 => '64 kbit/s for speech information transfer', |
148
|
|
|
|
|
|
|
8 => '64 kbit/s unrestricted', |
149
|
|
|
|
|
|
|
16 => '64 kbit/s for 3.1kHz audio information transfer', |
150
|
|
|
|
|
|
|
); |
151
|
15
|
|
|
|
|
31
|
my %service_label = ( |
152
|
|
|
|
|
|
|
1 => 'Telephony', |
153
|
|
|
|
|
|
|
# .... TODO |
154
|
|
|
|
|
|
|
); |
155
|
15
|
|
|
|
|
19
|
my($bearer,$service); |
156
|
15
|
|
|
|
|
92
|
($bearer,$service,$$var) = unpack('C C a*', $$var); |
157
|
15
|
|
50
|
|
|
94
|
_log(" Bearer: %d / %s", $bearer, $bearer_label{ $bearer } // 'UNKNOWN'); |
158
|
15
|
|
50
|
|
|
76
|
_log(" Service: %d / %s", $service, $service_label{ $service } // 'UNKNOWN'); |
159
|
15
|
|
50
|
|
|
107
|
$call->{bearer} = $bearer_label{ $bearer } // 'UNKNOWN'; |
160
|
15
|
|
50
|
|
|
145
|
$call->{service} = $service_label{ $service } // 'UNKNOWN'; |
161
|
|
|
|
|
|
|
} |
162
|
|
|
|
|
|
|
# 106. Supplementary service used by calling subscriber |
163
|
|
|
|
|
|
|
sub block_106 { |
164
|
0
|
|
|
0
|
0
|
0
|
my($call, $var) = @_; |
165
|
0
|
|
|
|
|
0
|
_log('106. Supplementary service used by calling subscriber'); |
166
|
0
|
|
|
|
|
0
|
my($sup_service); |
167
|
0
|
|
|
|
|
0
|
($sup_service, $$var) = unpack('C a*', $$var); |
168
|
0
|
|
|
|
|
0
|
_log(' Supplementary calling service: %d', $sup_service); |
169
|
0
|
|
|
|
|
0
|
$call->{supplementary_calling_service} = $sup_service; |
170
|
|
|
|
|
|
|
} |
171
|
|
|
|
|
|
|
# 107. Supplementary service used by called subscriber |
172
|
|
|
|
|
|
|
sub block_107 { |
173
|
0
|
|
|
0
|
0
|
0
|
my($call, $var) = @_; |
174
|
0
|
|
|
|
|
0
|
_log('106. Supplementary service used by called subscriber'); |
175
|
0
|
|
|
|
|
0
|
my($sup_service); |
176
|
0
|
|
|
|
|
0
|
($sup_service, $$var) = unpack('C a*', $$var); |
177
|
0
|
|
|
|
|
0
|
_log(' Supplementary called service: %d', $sup_service); |
178
|
0
|
|
|
|
|
0
|
$call->{supplementary_called_service} = $sup_service; |
179
|
|
|
|
|
|
|
} |
180
|
|
|
|
|
|
|
# 108. Subscriber controlled input |
181
|
|
|
|
|
|
|
# 109. Dialed digits |
182
|
|
|
|
|
|
|
# 110. Origin category |
183
|
|
|
|
|
|
|
sub block_110 { |
184
|
15
|
|
|
15
|
0
|
26
|
my($call, $var) = @_; |
185
|
15
|
|
|
|
|
32
|
_log("110. Origin category"); |
186
|
15
|
|
|
|
|
19
|
my($origin); |
187
|
15
|
|
|
|
|
89
|
($origin,$$var) = unpack('C a*', $$var); |
188
|
15
|
|
|
|
|
48
|
_log(" Origin category: %d", $origin); |
189
|
15
|
|
|
|
|
94
|
$call->{origin_category} = $origin; |
190
|
|
|
|
|
|
|
} |
191
|
|
|
|
|
|
|
# 111. Tariff direction |
192
|
|
|
|
|
|
|
sub block_111 { |
193
|
15
|
|
|
15
|
0
|
28
|
my($call, $var) = @_; |
194
|
15
|
|
|
|
|
35
|
_log("111. Tariff direction"); |
195
|
15
|
|
|
|
|
22
|
my($tariff); |
196
|
15
|
|
|
|
|
73
|
($tariff,$$var) = unpack('C a*', $$var); |
197
|
15
|
|
|
|
|
48
|
_log(" Tariff direction: %d", $tariff); |
198
|
15
|
|
|
|
|
91
|
$call->{tariff_direction} = $tariff; |
199
|
|
|
|
|
|
|
} |
200
|
|
|
|
|
|
|
# 112. Call failure cause |
201
|
|
|
|
|
|
|
# 113. Incoming trunk data |
202
|
|
|
|
|
|
|
sub block_113 { |
203
|
15
|
|
|
15
|
0
|
29
|
my($call, $var) = @_; |
204
|
15
|
|
|
|
|
33
|
_log("113. Incoming trunk data"); |
205
|
15
|
|
|
|
|
26
|
my($group,$id,$shelf,$port,$channel); |
206
|
15
|
|
|
|
|
128
|
($group,$id,$shelf,$port,$channel,$$var) = unpack('n n C n C a*', $$var); |
207
|
15
|
|
|
|
|
64
|
_log(" Trunk group: %d\n Trunk identification: %d\n Shelf identification: %d\n Port identification: %d\n Channel identification: %d", |
208
|
|
|
|
|
|
|
$group,$id,$shelf,$port,$channel); |
209
|
15
|
|
|
|
|
90
|
$call->{incoming_trunk_group} = $group; |
210
|
15
|
|
|
|
|
44
|
$call->{incoming_trunk} = $id; |
211
|
15
|
|
|
|
|
49
|
$call->{incoming_shelf} = $shelf; |
212
|
15
|
|
|
|
|
34
|
$call->{incoming_port} = $port; |
213
|
15
|
|
|
|
|
105
|
$call->{incoming_channel} = $channel; |
214
|
|
|
|
|
|
|
} |
215
|
|
|
|
|
|
|
# 114. Outgoing trunk data |
216
|
|
|
|
|
|
|
sub block_114 { |
217
|
15
|
|
|
15
|
0
|
30
|
my($call, $var) = @_; |
218
|
15
|
|
|
|
|
28
|
_log("114. Outgoing trunk data"); |
219
|
15
|
|
|
|
|
28
|
my($group,$id,$shelf,$port,$channel); |
220
|
15
|
|
|
|
|
155
|
($group,$id,$shelf,$port,$channel,$$var) = unpack('n n C n C a*', $$var); |
221
|
15
|
|
|
|
|
60
|
_log(" Trunk group: %d\n Trunk identification: %d\n Shelf identification: %d\n Port identification: %d\n Channel identification: %d", |
222
|
|
|
|
|
|
|
$group,$id,$shelf,$port,$channel); |
223
|
15
|
|
|
|
|
45
|
$call->{outgoing_trunk_group} = $group; |
224
|
15
|
|
|
|
|
31
|
$call->{outgoing_trunk} = $id; |
225
|
15
|
|
|
|
|
35
|
$call->{outgoing_shelf} = $shelf; |
226
|
15
|
|
|
|
|
30
|
$call->{outgoing_port} = $port; |
227
|
15
|
|
|
|
|
97
|
$call->{outgoing_channel} = $channel; |
228
|
|
|
|
|
|
|
} |
229
|
|
|
|
|
|
|
# 115. Call duration |
230
|
|
|
|
|
|
|
sub block_115 { |
231
|
15
|
|
|
15
|
0
|
24
|
my($call, $var) = @_; |
232
|
15
|
|
|
|
|
30
|
_log("115. Call duration"); |
233
|
15
|
|
|
|
|
21
|
my($duration); |
234
|
15
|
|
|
|
|
76
|
($duration,$$var) = unpack('N a*', $$var); |
235
|
15
|
|
|
|
|
130
|
_log(" Call duration: %d msec / %.5f sec", $duration, $duration / 1000.0); |
236
|
15
|
|
|
|
|
184
|
$call->{call_duration} = sprintf '%.5f', $duration / 1000.0; |
237
|
15
|
|
|
|
|
103
|
$call->{call_duration_ms} = $duration; |
238
|
|
|
|
|
|
|
} |
239
|
|
|
|
|
|
|
# 116. Checksum |
240
|
|
|
|
|
|
|
sub block_116 { |
241
|
15
|
|
|
15
|
0
|
28
|
my($call, $var) = @_; |
242
|
15
|
|
|
|
|
30
|
_log("116. Checksum"); |
243
|
15
|
|
|
|
|
22
|
my($len,$checksum); |
244
|
15
|
|
|
|
|
130
|
($len,$checksum,$$var) = unpack('C a2 a*', $$var); |
245
|
15
|
|
|
|
|
65
|
_log(" Checksum: 0x%s", unpack('H*', $checksum));; |
246
|
15
|
|
|
|
|
114
|
$call->{checksum} = unpack('H*', $checksum); |
247
|
|
|
|
|
|
|
} |
248
|
|
|
|
|
|
|
# 117. Business and Centrex group ID |
249
|
|
|
|
|
|
|
sub block_117 { |
250
|
0
|
|
|
0
|
0
|
0
|
my($call, $var) = @_; |
251
|
0
|
|
|
|
|
0
|
_log("117. Business and Centrex group ID"); |
252
|
0
|
|
|
|
|
0
|
my($len,$business,$centrex); |
253
|
0
|
|
|
|
|
0
|
($len,$business,$centrex,$$var) = unpack('C N N a*', $$var); |
254
|
0
|
|
|
|
|
0
|
_log(" Business group ID: %d", $business); |
255
|
0
|
|
|
|
|
0
|
_log(" Centrex group ID: %d", $centrex); |
256
|
0
|
|
|
|
|
0
|
$call->{business_group} = $business; |
257
|
0
|
|
|
|
|
0
|
$call->{centrex_group} = $centrex; |
258
|
|
|
|
|
|
|
} |
259
|
|
|
|
|
|
|
# 118. Carrier access code |
260
|
|
|
|
|
|
|
# 119. Original calling party number |
261
|
|
|
|
|
|
|
sub block_119 { |
262
|
0
|
|
|
0
|
0
|
0
|
my($call, $var) = @_; |
263
|
0
|
|
|
|
|
0
|
_log("119. Original calling party number"); |
264
|
0
|
|
|
|
|
0
|
my($len,$num_len); |
265
|
0
|
|
|
|
|
0
|
($len,$num_len,$$var) = unpack('C C a*', $$var); |
266
|
0
|
|
|
|
|
0
|
my $cut = $num_len; |
267
|
0
|
0
|
|
|
|
0
|
$cut++ if($num_len %2 == 1); |
268
|
0
|
|
|
|
|
0
|
my $num; |
269
|
0
|
|
|
|
|
0
|
($num,$$var) = unpack("H$cut a*", $$var); |
270
|
0
|
0
|
|
|
|
0
|
if($cut > $num_len) { |
271
|
0
|
|
|
|
|
0
|
$num = substr($num, 0, -1); |
272
|
|
|
|
|
|
|
} |
273
|
0
|
|
|
|
|
0
|
_log(" Original CLI: %s", $num); |
274
|
0
|
|
|
|
|
0
|
$call->{original_cli} = $num; |
275
|
|
|
|
|
|
|
} |
276
|
|
|
|
|
|
|
# 120. Prepaid account recharge data |
277
|
|
|
|
|
|
|
# 121. Call release cause |
278
|
|
|
|
|
|
|
sub block_121 { |
279
|
15
|
|
|
15
|
0
|
35
|
my($call, $var) = @_; |
280
|
15
|
|
|
|
|
29
|
_log("121. Call release cause"); |
281
|
15
|
|
|
|
|
60
|
my %cause_label = ( |
282
|
|
|
|
|
|
|
16 => 'normal call clearing', |
283
|
|
|
|
|
|
|
41 => 'temporary failure', |
284
|
|
|
|
|
|
|
); |
285
|
15
|
|
|
|
|
29
|
my %coding_label = ( |
286
|
|
|
|
|
|
|
0 => 'ITU-T standard', |
287
|
|
|
|
|
|
|
); |
288
|
15
|
|
|
|
|
137
|
my %location_label = ( |
289
|
|
|
|
|
|
|
0 => 'user', |
290
|
|
|
|
|
|
|
1 => 'private network serving the local user', |
291
|
|
|
|
|
|
|
2 => 'public network serving the local user', |
292
|
|
|
|
|
|
|
3 => 'transit network', |
293
|
|
|
|
|
|
|
4 => 'public network serving the remote user', |
294
|
|
|
|
|
|
|
5 => 'private network serving the remote user', |
295
|
|
|
|
|
|
|
7 => 'international network', |
296
|
|
|
|
|
|
|
10 => 'network beyond interworking point', |
297
|
|
|
|
|
|
|
); |
298
|
15
|
|
|
|
|
19
|
my($len,$cause,$flag); |
299
|
15
|
|
|
|
|
121
|
($len,$cause,$flag,$$var) = unpack('C n C a*', $$var); |
300
|
15
|
|
|
|
|
64
|
my $coding = (($flag & 0xF0) >> 4); |
301
|
15
|
|
|
|
|
34
|
my $location = ($flag & 0x0F); |
302
|
15
|
|
50
|
|
|
61
|
_log(" Cause: %d / %s", $cause, $cause_label{ $cause } // 'UNKNOWN');; |
303
|
15
|
|
50
|
|
|
63
|
_log(" Coding standard: %d / %s", $coding, $coding_label{ $coding } // 'UNKNOWN'); |
304
|
15
|
|
50
|
|
|
65
|
_log(" Location: %d / %s", $location, $location_label{ $location } // 'UNKNOWN'); |
305
|
15
|
|
50
|
|
|
59
|
$call->{call_release_cause} = $cause_label{ $cause } // 'UNKNOWN'; |
306
|
15
|
|
|
|
|
41
|
$call->{call_release_cause_code} = $cause; |
307
|
15
|
|
50
|
|
|
70
|
$call->{call_coding_standard} = $coding_label{ $coding } // 'UNKNOWN'; |
308
|
15
|
|
50
|
|
|
167
|
$call->{call_location} = $location_label{ $location } // 'UNKNOWN'; |
309
|
|
|
|
|
|
|
} |
310
|
|
|
|
|
|
|
# 122. CBNO (Charge Band Number) |
311
|
|
|
|
|
|
|
# 123. Common call ID |
312
|
|
|
|
|
|
|
# 124. Durations before answer |
313
|
|
|
|
|
|
|
# 125. VoIP Info (old) |
314
|
|
|
|
|
|
|
# 126. Amount of Transferred Data (old) |
315
|
|
|
|
|
|
|
# 127. IP Address |
316
|
|
|
|
|
|
|
sub block_127 { |
317
|
15
|
|
|
15
|
0
|
31
|
my($call, $var) = @_; |
318
|
15
|
|
|
|
|
33
|
_log("127. IP Address"); |
319
|
15
|
|
|
|
|
26
|
my($len,$ip_data); |
320
|
15
|
|
|
|
|
77
|
($len,$$var) = unpack('C a*', $$var); |
321
|
15
|
|
|
|
|
46
|
$len -= 2; |
322
|
15
|
|
|
|
|
94
|
($ip_data,$$var) = unpack("a$len a*", $$var); |
323
|
15
|
|
|
|
|
34
|
my($flag,$reserved,@ip); |
324
|
15
|
|
|
|
|
79
|
($flag,$reserved,@ip) = unpack('C C N*', $ip_data); |
325
|
15
|
|
|
|
|
43
|
@ip = map { join '.', unpack 'C4', pack 'N', $_ } @ip; |
|
30
|
|
|
|
|
328
|
|
326
|
15
|
50
|
|
|
|
85
|
if($flag & 0x1) { |
327
|
15
|
|
|
|
|
37
|
my $ip = shift(@ip); |
328
|
15
|
|
|
|
|
39
|
_log(" Origin side remote RTP IP: %s", $ip); |
329
|
15
|
|
|
|
|
67
|
$call->{origin_remote_rtp} = $ip; |
330
|
|
|
|
|
|
|
} |
331
|
15
|
50
|
|
|
|
46
|
if($flag & 0x2) { |
332
|
0
|
|
|
|
|
0
|
my $ip = shift(@ip); |
333
|
0
|
|
|
|
|
0
|
_log(" Origin side local RTP IP: %s", $ip); |
334
|
0
|
|
|
|
|
0
|
$call->{origin_local_rtp} = $ip; |
335
|
|
|
|
|
|
|
} |
336
|
15
|
50
|
|
|
|
45
|
if($flag & 0x4) { |
337
|
15
|
|
|
|
|
33
|
my $ip = shift(@ip); |
338
|
15
|
|
|
|
|
39
|
_log(" Terminating side remote RTP IP: %s", $ip); |
339
|
15
|
|
|
|
|
60
|
$call->{terminating_remote_rtp} = $ip; |
340
|
|
|
|
|
|
|
} |
341
|
15
|
50
|
|
|
|
48
|
if($flag & 0x8) { |
342
|
0
|
|
|
|
|
0
|
my $ip = shift(@ip); |
343
|
0
|
|
|
|
|
0
|
_log(" Terminating side local RTP IP: %s", $ip); |
344
|
0
|
|
|
|
|
0
|
$call->{terminating_local_rtp} = $ip; |
345
|
|
|
|
|
|
|
} |
346
|
15
|
50
|
|
|
|
43
|
if($flag & 0x10) { |
347
|
0
|
|
|
|
|
0
|
my $ip = shift(@ip); |
348
|
0
|
|
|
|
|
0
|
_log(' Origin side remote signaling IP: %s', $ip); |
349
|
0
|
|
|
|
|
0
|
$call->{origin_remote_signaling} = $ip; |
350
|
|
|
|
|
|
|
} |
351
|
15
|
50
|
|
|
|
45
|
if($flag & 0x20) { |
352
|
0
|
|
|
|
|
0
|
my $ip = shift(@ip); |
353
|
0
|
|
|
|
|
0
|
_log(' Origin side local signaling IP: %s', $ip); |
354
|
0
|
|
|
|
|
0
|
$call->{origin_local_signaling} = $ip; |
355
|
|
|
|
|
|
|
} |
356
|
15
|
50
|
|
|
|
47
|
if($flag & 0x40) { |
357
|
0
|
|
|
|
|
0
|
my $ip = shift(@ip); |
358
|
0
|
|
|
|
|
0
|
_log(' Terminating side remote signaling IP: %s', $ip); |
359
|
0
|
|
|
|
|
0
|
$call->{terminating_remote_signaling} = $ip; |
360
|
|
|
|
|
|
|
} |
361
|
15
|
50
|
|
|
|
128
|
if($flag & 0x80) { |
362
|
0
|
|
|
|
|
0
|
my $ip = shift(@ip); |
363
|
0
|
|
|
|
|
0
|
_log(' Terminating side local signaling IP: %s', $ip); |
364
|
0
|
|
|
|
|
0
|
$call->{terminating_local_signaling} = $ip; |
365
|
|
|
|
|
|
|
} |
366
|
|
|
|
|
|
|
} |
367
|
|
|
|
|
|
|
# 128. VoIP info |
368
|
|
|
|
|
|
|
sub block_128 { |
369
|
15
|
|
|
15
|
0
|
27
|
my($call, $var) = @_; |
370
|
15
|
|
|
|
|
34
|
_log("128. VoIP info"); |
371
|
15
|
|
|
|
|
25
|
my($len,$rx_codec,$tx_codec,$rx_period,$tx_period,$rx_bandwidth,$tx_bandwidth,$max_jitter,$flag); |
372
|
15
|
|
|
|
|
249
|
($len,$rx_codec,$tx_codec,$rx_period,$tx_period,$rx_bandwidth,$tx_bandwidth,$max_jitter,$flag,$$var) = unpack('CCCCCnnnC a*', $$var); |
373
|
15
|
|
|
|
|
184
|
my %codec_label = ( |
374
|
|
|
|
|
|
|
0 => 'Undefined', |
375
|
|
|
|
|
|
|
8 => 'G711Alaw64k', |
376
|
|
|
|
|
|
|
9 => 'G711Ulaw64k', |
377
|
|
|
|
|
|
|
66 => 'G728', |
378
|
|
|
|
|
|
|
67 => 'G729', |
379
|
|
|
|
|
|
|
68 => 'G729annexA', |
380
|
|
|
|
|
|
|
70 => 'G729wAnnexB', |
381
|
|
|
|
|
|
|
71 => 'G729AnnexAwAnnexB', |
382
|
|
|
|
|
|
|
72 => 'GsmFullRate', |
383
|
|
|
|
|
|
|
80 => 'G7231A5_3k', |
384
|
|
|
|
|
|
|
81 => 'G7231A6_3k', |
385
|
|
|
|
|
|
|
129 => 'FaxT38', |
386
|
|
|
|
|
|
|
); |
387
|
15
|
|
|
|
|
41
|
my %side_label = ( 0 => 'origin side', 1 => 'terminating side'); |
388
|
15
|
|
|
|
|
51
|
my %type_label = ( |
389
|
|
|
|
|
|
|
0 => 'Undefined', |
390
|
|
|
|
|
|
|
1 => 'Audio', |
391
|
|
|
|
|
|
|
2 => 'Data', |
392
|
|
|
|
|
|
|
3 => 'Fax', |
393
|
|
|
|
|
|
|
); |
394
|
|
|
|
|
|
|
|
395
|
15
|
|
50
|
|
|
67
|
_log(" Rx codec: %d / %s", $rx_codec, $codec_label{ $rx_codec } // 'UNKNOWN'); |
396
|
15
|
|
50
|
|
|
122
|
_log(" Tx codec: %d / %s", $tx_codec, $codec_label{ $tx_codec } // 'UNKNOWN'); |
397
|
15
|
|
|
|
|
36
|
_log(" Rx packetization period: %d ms", $rx_period); |
398
|
15
|
|
|
|
|
31
|
_log(" Tx packetization period: %d ms", $tx_period); |
399
|
15
|
|
|
|
|
33
|
_log(" Rx bandwidth: %d kbit/s", $rx_bandwidth); |
400
|
15
|
|
|
|
|
34
|
_log(" Tx bandwidth: %d kbit/s", $tx_bandwidth); |
401
|
15
|
|
|
|
|
34
|
_log(" Max. jitter buffer size: %d ms", $max_jitter); |
402
|
15
|
|
|
|
|
47
|
my $call_side = ($flag & 0x80) >> 7; |
403
|
15
|
|
|
|
|
34
|
my $call_type = $flag & 0x7F; |
404
|
15
|
|
50
|
|
|
60
|
_log(" Call side: %d / %s", $call_side, $side_label{ $call_side } // 'UNKNOWN'); |
405
|
15
|
|
50
|
|
|
61
|
_log(" VoIP call type: %d / %s", $call_type, $type_label{ $call_type } // 'UNKNOWN'); |
406
|
|
|
|
|
|
|
|
407
|
15
|
|
50
|
|
|
137
|
$call->{voip_rx_codec} = $codec_label{ $rx_codec } // 'UNKNOWN'; |
408
|
15
|
|
50
|
|
|
61
|
$call->{voip_tx_codec} = $codec_label{ $tx_codec } // 'UNKNOWN'; |
409
|
15
|
|
|
|
|
63
|
$call->{voip_rx_packetization} = $rx_period; |
410
|
15
|
|
|
|
|
51
|
$call->{voip_tx_packetization} = $tx_period; |
411
|
15
|
|
|
|
|
38
|
$call->{voip_rx_bandwidth} = $rx_bandwidth; |
412
|
15
|
|
|
|
|
37
|
$call->{voip_tx_bandwidth} = $tx_bandwidth; |
413
|
15
|
|
|
|
|
33
|
$call->{voip_max_jitter} = $max_jitter; |
414
|
15
|
|
50
|
|
|
50
|
$call->{voip_call_size} = $side_label{ $call_side } // 'UNKNOWN'; |
415
|
15
|
|
50
|
|
|
207
|
$call->{voip_call_type} = $type_label{ $call_type } // 'UNKNOWN'; |
416
|
|
|
|
|
|
|
} |
417
|
|
|
|
|
|
|
# 129. Amount of transferred data |
418
|
|
|
|
|
|
|
sub block_129 { |
419
|
15
|
|
|
15
|
0
|
59
|
my($call, $var) = @_; |
420
|
15
|
|
|
|
|
30
|
_log("129. Amount of transferred data"); |
421
|
15
|
|
|
|
|
23
|
my($len,$side,$rx_packets,$tx_packets,$rx_octets,$tx_octets,$lost,$jitter,$latency); |
422
|
15
|
|
|
|
|
190
|
($len,$side,$rx_packets,$tx_packets,$rx_octets,$tx_octets,$lost,$jitter,$latency,$$var) = unpack('CCNNNNNCC a*', $$var); |
423
|
15
|
|
|
|
|
89
|
my %side_label = ( 0 => 'origin side', 1 => 'terminating side'); |
424
|
15
|
|
50
|
|
|
61
|
_log(" Call side: %d / %s", $side, $side_label{ $side } // 'UNKNOWN'); |
425
|
15
|
|
|
|
|
63
|
_log(" Rx packets: %d", $rx_packets); |
426
|
15
|
|
|
|
|
36
|
_log(" Tx packets: %d", $tx_packets); |
427
|
15
|
|
|
|
|
32
|
_log(" Rx octets: %d", $rx_octets); |
428
|
15
|
|
|
|
|
41
|
_log(" Tx octets: %d", $tx_octets); |
429
|
15
|
|
|
|
|
35
|
_log(" Packets lost: %d", $lost); |
430
|
15
|
|
|
|
|
42
|
_log(" Average jitter: %d ms", $jitter); |
431
|
15
|
|
|
|
|
33
|
_log(" Average latency: %d ms", $latency); |
432
|
15
|
|
50
|
|
|
64
|
$call->{call_side} = $side_label{ $side } // 'UNKNOWN'; |
433
|
15
|
|
|
|
|
46
|
$call->{rx_packets} = $rx_packets; |
434
|
15
|
|
|
|
|
38
|
$call->{tx_packets} = $tx_packets; |
435
|
15
|
|
|
|
|
36
|
$call->{rx_octets} = $rx_octets; |
436
|
15
|
|
|
|
|
31
|
$call->{tx_octets} = $tx_octets; |
437
|
15
|
|
|
|
|
34
|
$call->{packets_lost} = $lost; |
438
|
15
|
|
|
|
|
31
|
$call->{average_jitter} = $jitter; |
439
|
15
|
|
|
|
|
130
|
$call->{everage_latency} = $latency; |
440
|
|
|
|
|
|
|
} |
441
|
|
|
|
|
|
|
# 130. Service control data |
442
|
|
|
|
|
|
|
# 131. New destination number |
443
|
|
|
|
|
|
|
# 132. VoIP Quality of Service data (QoS VoIP Data) |
444
|
|
|
|
|
|
|
# 133. Additional Centrex data |
445
|
|
|
|
|
|
|
# 134. Additional statistics data |
446
|
|
|
|
|
|
|
sub block_134 { |
447
|
15
|
|
|
15
|
0
|
32
|
my($call, $var) = @_; |
448
|
15
|
|
|
|
|
33
|
_log("134. Additional statistics data"); |
449
|
15
|
|
|
|
|
21
|
my($len,$stats); |
450
|
15
|
|
|
|
|
75
|
($len,$$var) = unpack('C a*', $$var); |
451
|
15
|
|
|
|
|
40
|
$len -= 2; |
452
|
15
|
|
|
|
|
96
|
($stats,$$var) = unpack("a$len a*", $$var); |
453
|
15
|
|
|
|
|
66
|
_log(" Stats: 0x%s (len %d)", unpack('H*', $stats), $len); |
454
|
|
|
|
|
|
|
# no useful info? |
455
|
|
|
|
|
|
|
} |
456
|
|
|
|
|
|
|
# TODO NOT IMPLEMENTED YET: |
457
|
|
|
|
|
|
|
# 135. IMS charging identifier |
458
|
|
|
|
|
|
|
# 136. Inter Operator Identifiers – IOI) |
459
|
|
|
|
|
|
|
# 137. Supplementary service additional info |
460
|
|
|
|
|
|
|
# 138. Calling Party Number |
461
|
|
|
|
|
|
|
# 139. Additional calling number |
462
|
|
|
|
|
|
|
# 140. Called party number |
463
|
|
|
|
|
|
|
# 141. Sent called party number |
464
|
|
|
|
|
|
|
# 142. Third party number |
465
|
|
|
|
|
|
|
# 143. Redirecting party number |
466
|
|
|
|
|
|
|
# 144. Incoming trunk data - Name |
467
|
|
|
|
|
|
|
# 145. Outgoing trunk data - name |
468
|
|
|
|
|
|
|
# 146. Node info |
469
|
|
|
|
|
|
|
# 147. Global call reference |
470
|
|
|
|
|
|
|
# 148. MLPP Data |
471
|
|
|
|
|
|
|
# 149. Customer Data |
472
|
|
|
|
|
|
|
# 150. Received Called Party Number |
473
|
|
|
|
|
|
|
# 151. Call Type |
474
|
|
|
|
|
|
|
# 152. IN Service Data |
475
|
|
|
|
|
|
|
# 153. URI (Universal Resource Identification) |
476
|
|
|
|
|
|
|
# 154. Free Format Operator Specific Data |
477
|
|
|
|
|
|
|
# 155. ----- |
478
|
|
|
|
|
|
|
# 156. Additional Numbers |
479
|
|
|
|
|
|
|
|
480
|
|
|
|
|
|
|
sub dump_var { |
481
|
0
|
|
|
0
|
0
|
0
|
my $var = shift; |
482
|
0
|
|
|
|
|
0
|
_log("---- Variable data (%d) ----", length($var)); |
483
|
0
|
|
|
|
|
0
|
_log('%s', unpack('H*', $var)); |
484
|
|
|
|
|
|
|
} |
485
|
|
|
|
|
|
|
|
486
|
|
|
|
|
|
|
# Parse individual record |
487
|
|
|
|
|
|
|
# |
488
|
|
|
|
|
|
|
# Each record has fixed part 16+(2..19) bytes |
489
|
|
|
|
|
|
|
# and optional set of additional blocks |
490
|
|
|
|
|
|
|
sub parse_record { |
491
|
16
|
|
|
16
|
0
|
25
|
my $fh = shift; |
492
|
|
|
|
|
|
|
|
493
|
16
|
|
|
|
|
20
|
my $type_id; |
494
|
16
|
100
|
|
|
|
321
|
sysread($fh, $type_id, 1) || return 0; |
495
|
15
|
|
|
|
|
47
|
my $code = unpack('H2', $type_id); |
496
|
|
|
|
|
|
|
|
497
|
|
|
|
|
|
|
# Recort type: |
498
|
|
|
|
|
|
|
# d2 -- Record at date and time changes (parsed but ignored) |
499
|
|
|
|
|
|
|
# d3 -- Record of the loss of a certain amount of records (NOT SUPPORTED) |
500
|
|
|
|
|
|
|
# d4 -- Restart record (NOT SUPPORTED) |
501
|
|
|
|
|
|
|
# c8 -- Call record |
502
|
15
|
50
|
|
|
|
73
|
if($code eq 'd2') { |
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
503
|
0
|
|
|
|
|
0
|
parse_time_change_record($fh, $code); |
504
|
|
|
|
|
|
|
# next |
505
|
0
|
|
|
|
|
0
|
return parse_record($fh); |
506
|
|
|
|
|
|
|
} |
507
|
|
|
|
|
|
|
elsif($code eq 'd4') { |
508
|
0
|
|
|
|
|
0
|
parse_reboot_record($fh, $code); |
509
|
0
|
|
|
|
|
0
|
return parse_record($fh); |
510
|
|
|
|
|
|
|
} |
511
|
|
|
|
|
|
|
elsif($code ne 'c8') { |
512
|
0
|
|
|
|
|
0
|
die "Unknown record type: $code"; |
513
|
|
|
|
|
|
|
} |
514
|
15
|
|
|
|
|
49
|
_log('Found Call Record marker %s', $code); |
515
|
|
|
|
|
|
|
|
516
|
15
|
|
|
|
|
37
|
my %call = (); |
517
|
|
|
|
|
|
|
|
518
|
|
|
|
|
|
|
# Statis header |
519
|
|
|
|
|
|
|
# 1 - c8 |
520
|
|
|
|
|
|
|
# 2 - record length |
521
|
|
|
|
|
|
|
# 4 - record index (in file?) |
522
|
|
|
|
|
|
|
# 4 - call identifier (sequentially incremented number, unique - |
523
|
|
|
|
|
|
|
# but incomplete calls can have call-id repeated again in later file) |
524
|
|
|
|
|
|
|
# 3 - flags |
525
|
|
|
|
|
|
|
# 1 - Sequence (4bits) / Charge status (4bits) |
526
|
|
|
|
|
|
|
# 1 - Area code length (3bits) / Subscriber number length (5bits) |
527
|
|
|
|
|
|
|
# ... - Area code and subscriber number of record owner |
528
|
15
|
|
|
|
|
19
|
my $len; |
529
|
15
|
50
|
|
|
|
108
|
sysread($fh, $len, 2) || die $!; |
530
|
15
|
|
|
|
|
45
|
$len = unpack('n', $len); |
531
|
|
|
|
|
|
|
#printf "Record lengh: %d bytes\n", $len; |
532
|
|
|
|
|
|
|
|
533
|
15
|
|
|
|
|
31
|
my $data; |
534
|
15
|
50
|
|
|
|
126
|
sysread($fh, $data, $len - 3) || die $!; |
535
|
|
|
|
|
|
|
#print unpack('H*', $data), "\n"; |
536
|
|
|
|
|
|
|
|
537
|
15
|
|
|
|
|
122
|
my($rec_index,$call_id,$flags,$seq,$area,$variable) = unpack('N N H6 H2 C a*', $data); |
538
|
15
|
|
|
|
|
50
|
_log("Header"); |
539
|
15
|
|
|
|
|
37
|
_log(" Record index: %d", $rec_index); |
540
|
15
|
|
|
|
|
65
|
_log(" Call ID: %d / %s", $call_id, unpack('H*', pack('N', $call_id))); |
541
|
15
|
|
|
|
|
66
|
_log(" Flags: 0x%s", unpack('H6', pack('H6', $flags))); |
542
|
15
|
|
|
|
|
56
|
_log(" Record sequence: %d", (($seq & 0xF0) >> 4)); |
543
|
15
|
|
|
|
|
41
|
_log(" Charge status: %d", ($seq & 0x0F)); |
544
|
|
|
|
|
|
|
|
545
|
15
|
|
|
|
|
53
|
$call{record_index} = $rec_index; |
546
|
15
|
|
|
|
|
44
|
$call{call_id} = $call_id; |
547
|
15
|
|
|
|
|
29
|
$call{flags} = $flags; |
548
|
15
|
|
|
|
|
31
|
$call{record_sequence} = (($seq & 0xF0) >> 4); |
549
|
15
|
|
|
|
|
28
|
$call{charge_status} = ($seq & 0x0F); |
550
|
|
|
|
|
|
|
|
551
|
15
|
|
|
|
|
62
|
my($area_len) = ($area & 0xE0) >> 5; |
552
|
15
|
|
|
|
|
39
|
my($subscriber_len) = ($area & 0x1F); |
553
|
|
|
|
|
|
|
#my($subscriber_len) = ($area & 0x07); |
554
|
|
|
|
|
|
|
#printf "Area/Subscriber: %s / %d . %d\n", $area, $area_len, $subscriber_len; |
555
|
|
|
|
|
|
|
#printf " Area code length: %d\n", $area_len; |
556
|
|
|
|
|
|
|
#printf " Subscriber number length: %d\n", $subscriber_len; |
557
|
|
|
|
|
|
|
|
558
|
|
|
|
|
|
|
#printf "---- Variable data (%d) ----\n", length($variable); |
559
|
|
|
|
|
|
|
#print unpack('H*', $variable), "\n"; |
560
|
|
|
|
|
|
|
|
561
|
15
|
|
|
|
|
29
|
my $area_code = ''; |
562
|
15
|
50
|
|
|
|
43
|
if($area_len) { |
563
|
0
|
|
|
|
|
0
|
($area_code,$variable) = unpack("H[$area_len] a*", $variable); |
564
|
0
|
|
|
|
|
0
|
_log(' Aread: %s', $area_code); |
565
|
|
|
|
|
|
|
} |
566
|
|
|
|
|
|
|
|
567
|
15
|
|
|
|
|
30
|
my $cli_len = $subscriber_len; |
568
|
15
|
50
|
|
|
|
54
|
if($subscriber_len % 2 == 1) { |
569
|
15
|
|
|
|
|
28
|
$cli_len++; |
570
|
|
|
|
|
|
|
} |
571
|
15
|
|
|
|
|
18
|
my $cli; |
572
|
15
|
|
|
|
|
95
|
($cli, $variable) = unpack("H$cli_len a*", $variable); |
573
|
15
|
50
|
|
|
|
96
|
if($cli_len > $subscriber_len) { |
574
|
15
|
|
|
|
|
45
|
$cli = substr($cli, 0, -1); |
575
|
|
|
|
|
|
|
} |
576
|
15
|
|
|
|
|
61
|
_log(" CLI: %s", $cli); |
577
|
15
|
|
|
|
|
33
|
$call{cli_area} = $area_code; |
578
|
15
|
|
|
|
|
24
|
$call{cli} = $cli; |
579
|
|
|
|
|
|
|
|
580
|
|
|
|
|
|
|
# dynamic part: |
581
|
15
|
|
|
|
|
20
|
my $block_marker; |
582
|
15
|
|
|
|
|
43
|
while( length($variable) > 0) { |
583
|
|
|
|
|
|
|
# each block has type marker + variable data, which depends on type |
584
|
240
|
|
|
|
|
1268
|
($block_marker, $variable) = unpack('C a*', $variable); |
585
|
|
|
|
|
|
|
#_log('Found block marker: %s', $block_marker); |
586
|
|
|
|
|
|
|
{ |
587
|
2
|
|
|
2
|
|
22
|
no strict 'refs'; |
|
2
|
|
|
|
|
4
|
|
|
2
|
|
|
|
|
862
|
|
|
240
|
|
|
|
|
537
|
|
588
|
240
|
|
|
|
|
652
|
my $sub = 'block_' . $block_marker; |
589
|
240
|
|
|
|
|
808
|
$sub->(\%call, \$variable); |
590
|
|
|
|
|
|
|
} |
591
|
|
|
|
|
|
|
#dump_var($variable); |
592
|
|
|
|
|
|
|
} |
593
|
|
|
|
|
|
|
|
594
|
15
|
|
|
|
|
32
|
_log(''); |
595
|
15
|
|
|
|
|
115
|
return \%call; |
596
|
|
|
|
|
|
|
} |
597
|
|
|
|
|
|
|
|
598
|
|
|
|
|
|
|
# d2 - time change event |
599
|
|
|
|
|
|
|
sub parse_time_change_record { |
600
|
0
|
|
|
0
|
0
|
|
my ($fh, $code) = @_; |
601
|
|
|
|
|
|
|
|
602
|
0
|
|
|
|
|
|
_log('Found Date Time Change marker %s', $code); |
603
|
|
|
|
|
|
|
|
604
|
0
|
|
|
|
|
|
my $data; |
605
|
|
|
|
|
|
|
# 7 - old date and time |
606
|
|
|
|
|
|
|
# 7 - new date and time |
607
|
|
|
|
|
|
|
# 1 - cause of change |
608
|
0
|
0
|
|
|
|
|
sysread($fh, $data, 15) || die $!; |
609
|
0
|
|
|
|
|
|
my($year,$month,$day,$hour,$min,$sec,$msec,$reason); |
610
|
0
|
|
|
|
|
|
($year,$month,$day,$hour,$min,$sec,$msec,$data) = unpack('CCCCCCC a*', $data); |
611
|
0
|
|
|
|
|
|
$year += 2000; |
612
|
0
|
|
|
|
|
|
my $dtime = sprintf "%04d-%02d-%02d %02d:%02d:%02d.%02d", $year,$month,$day,$hour,$min,$sec,$msec; |
613
|
0
|
|
|
|
|
|
_log(' Old Time: %s', $dtime); |
614
|
|
|
|
|
|
|
|
615
|
0
|
|
|
|
|
|
($year,$month,$day,$hour,$min,$sec,$msec,$reason) = unpack('CCCCCCC C', $data); |
616
|
0
|
|
|
|
|
|
$year += 2000; |
617
|
0
|
|
|
|
|
|
$dtime = sprintf "%04d-%02d-%02d %02d:%02d:%02d.%02d", $year,$month,$day,$hour,$min,$sec,$msec; |
618
|
0
|
|
|
|
|
|
_log(' New Time: %s', $dtime); |
619
|
|
|
|
|
|
|
|
620
|
|
|
|
|
|
|
# 1 - The real-time clock correction |
621
|
|
|
|
|
|
|
# 2 - Summer / winter time changes |
622
|
0
|
|
|
|
|
|
_log(' Change reason: %s', $reason); |
623
|
|
|
|
|
|
|
} |
624
|
|
|
|
|
|
|
|
625
|
|
|
|
|
|
|
# d4 - System was rebooted |
626
|
|
|
|
|
|
|
sub parse_reboot_record { |
627
|
0
|
|
|
0
|
0
|
|
my ($fh, $code) = @_; |
628
|
0
|
|
|
|
|
|
_log('Found Restart marker %s', $code); |
629
|
|
|
|
|
|
|
|
630
|
0
|
|
|
|
|
|
my $data; |
631
|
|
|
|
|
|
|
# 7 - restart date and time |
632
|
|
|
|
|
|
|
# 4 - reserved |
633
|
0
|
0
|
|
|
|
|
sysread($fh, $data, 7) || die $!; |
634
|
0
|
|
|
|
|
|
my($year,$month,$day,$hour,$min,$sec,$msec); |
635
|
0
|
|
|
|
|
|
($year,$month,$day,$hour,$min,$sec,$msec) = unpack('CCCCCCC', $data); |
636
|
0
|
|
|
|
|
|
$year += 2000; |
637
|
0
|
|
|
|
|
|
my $reboot_time = sprintf "%04d-%02d-%02d %02d:%02d:%02d.%02d", $year,$month,$day,$hour,$min,$sec,$msec; |
638
|
0
|
|
|
|
|
|
_log(' Restart time: %s', $reboot_time); |
639
|
|
|
|
|
|
|
|
640
|
|
|
|
|
|
|
# ignored... |
641
|
0
|
0
|
|
|
|
|
sysread($fh, $data, 4) || die $!; |
642
|
|
|
|
|
|
|
} |
643
|
|
|
|
|
|
|
|
644
|
|
|
|
|
|
|
1; |
645
|
|
|
|
|
|
|
#-- |
646
|
|
|
|
|
|
|
|
647
|
|
|
|
|
|
|
=head1 AUTHOR |
648
|
|
|
|
|
|
|
|
649
|
|
|
|
|
|
|
Sergey Leschenko, C<< >> |
650
|
|
|
|
|
|
|
|
651
|
|
|
|
|
|
|
=head1 BUGS |
652
|
|
|
|
|
|
|
|
653
|
|
|
|
|
|
|
Please note that some blocks are not implemented, as I haven't seen them in real data files. |
654
|
|
|
|
|
|
|
|
655
|
|
|
|
|
|
|
Please report any bugs or feature requests to C, or through |
656
|
|
|
|
|
|
|
the web interface at L. I will be notified, and then you'll |
657
|
|
|
|
|
|
|
automatically be notified of progress on your bug as I make changes. |
658
|
|
|
|
|
|
|
|
659
|
|
|
|
|
|
|
|
660
|
|
|
|
|
|
|
|
661
|
|
|
|
|
|
|
|
662
|
|
|
|
|
|
|
=head1 SUPPORT |
663
|
|
|
|
|
|
|
|
664
|
|
|
|
|
|
|
You can find documentation for this module with the perldoc command. |
665
|
|
|
|
|
|
|
|
666
|
|
|
|
|
|
|
perldoc CDR::Parser::SI3000 |
667
|
|
|
|
|
|
|
|
668
|
|
|
|
|
|
|
|
669
|
|
|
|
|
|
|
You can also look for information at: |
670
|
|
|
|
|
|
|
|
671
|
|
|
|
|
|
|
=over 4 |
672
|
|
|
|
|
|
|
|
673
|
|
|
|
|
|
|
=item * RT: CPAN's request tracker (report bugs here) |
674
|
|
|
|
|
|
|
|
675
|
|
|
|
|
|
|
L |
676
|
|
|
|
|
|
|
|
677
|
|
|
|
|
|
|
=item * AnnoCPAN: Annotated CPAN documentation |
678
|
|
|
|
|
|
|
|
679
|
|
|
|
|
|
|
L |
680
|
|
|
|
|
|
|
|
681
|
|
|
|
|
|
|
=item * CPAN Ratings |
682
|
|
|
|
|
|
|
|
683
|
|
|
|
|
|
|
L |
684
|
|
|
|
|
|
|
|
685
|
|
|
|
|
|
|
=item * Search CPAN |
686
|
|
|
|
|
|
|
|
687
|
|
|
|
|
|
|
L |
688
|
|
|
|
|
|
|
|
689
|
|
|
|
|
|
|
=back |
690
|
|
|
|
|
|
|
|
691
|
|
|
|
|
|
|
|
692
|
|
|
|
|
|
|
=head1 ACKNOWLEDGEMENTS |
693
|
|
|
|
|
|
|
|
694
|
|
|
|
|
|
|
|
695
|
|
|
|
|
|
|
=head1 LICENSE AND COPYRIGHT |
696
|
|
|
|
|
|
|
|
697
|
|
|
|
|
|
|
Copyright 2013 Sergey Leschenko. |
698
|
|
|
|
|
|
|
|
699
|
|
|
|
|
|
|
This program is free software; you can redistribute it and/or modify it |
700
|
|
|
|
|
|
|
under the terms of the the Artistic License (2.0). You may obtain a |
701
|
|
|
|
|
|
|
copy of the full license at: |
702
|
|
|
|
|
|
|
|
703
|
|
|
|
|
|
|
L |
704
|
|
|
|
|
|
|
|
705
|
|
|
|
|
|
|
Any use, modification, and distribution of the Standard or Modified |
706
|
|
|
|
|
|
|
Versions is governed by this Artistic License. By using, modifying or |
707
|
|
|
|
|
|
|
distributing the Package, you accept this license. Do not use, modify, |
708
|
|
|
|
|
|
|
or distribute the Package, if you do not accept this license. |
709
|
|
|
|
|
|
|
|
710
|
|
|
|
|
|
|
If your Modified Version has been derived from a Modified Version made |
711
|
|
|
|
|
|
|
by someone other than you, you are nevertheless required to ensure that |
712
|
|
|
|
|
|
|
your Modified Version complies with the requirements of this license. |
713
|
|
|
|
|
|
|
|
714
|
|
|
|
|
|
|
This license does not grant you the right to use any trademark, service |
715
|
|
|
|
|
|
|
mark, tradename, or logo of the Copyright Holder. |
716
|
|
|
|
|
|
|
|
717
|
|
|
|
|
|
|
This license includes the non-exclusive, worldwide, free-of-charge |
718
|
|
|
|
|
|
|
patent license to make, have made, use, offer to sell, sell, import and |
719
|
|
|
|
|
|
|
otherwise transfer the Package with respect to any patent claims |
720
|
|
|
|
|
|
|
licensable by the Copyright Holder that are necessarily infringed by the |
721
|
|
|
|
|
|
|
Package. If you institute patent litigation (including a cross-claim or |
722
|
|
|
|
|
|
|
counterclaim) against any party alleging that the Package constitutes |
723
|
|
|
|
|
|
|
direct or contributory patent infringement, then this Artistic License |
724
|
|
|
|
|
|
|
to you shall terminate on the date that such litigation is filed. |
725
|
|
|
|
|
|
|
|
726
|
|
|
|
|
|
|
Disclaimer of Warranty: THE PACKAGE IS PROVIDED BY THE COPYRIGHT HOLDER |
727
|
|
|
|
|
|
|
AND CONTRIBUTORS "AS IS' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES. |
728
|
|
|
|
|
|
|
THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR |
729
|
|
|
|
|
|
|
PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED TO THE EXTENT PERMITTED BY |
730
|
|
|
|
|
|
|
YOUR LOCAL LAW. UNLESS REQUIRED BY LAW, NO COPYRIGHT HOLDER OR |
731
|
|
|
|
|
|
|
CONTRIBUTOR WILL BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, OR |
732
|
|
|
|
|
|
|
CONSEQUENTIAL DAMAGES ARISING IN ANY WAY OUT OF THE USE OF THE PACKAGE, |
733
|
|
|
|
|
|
|
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
734
|
|
|
|
|
|
|
|
735
|
|
|
|
|
|
|
|
736
|
|
|
|
|
|
|
=cut |
737
|
|
|
|
|
|
|
|
738
|
|
|
|
|
|
|
1; # End of CDR::Parser::SI3000 |