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