line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package FIX::Lite; |
2
|
|
|
|
|
|
|
|
3
|
1
|
|
|
1
|
|
21047
|
use vars qw($VERSION @ISA); |
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
60
|
|
4
|
1
|
|
|
1
|
|
5
|
use warnings; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
29
|
|
5
|
1
|
|
|
1
|
|
5
|
use strict; |
|
1
|
|
|
|
|
5
|
|
|
1
|
|
|
|
|
20
|
|
6
|
|
|
|
|
|
|
|
7
|
1
|
|
|
1
|
|
807
|
use IO::Socket; |
|
1
|
|
|
|
|
37068
|
|
|
1
|
|
|
|
|
4
|
|
8
|
1
|
|
|
1
|
|
1384
|
use POSIX qw(strftime); |
|
1
|
|
|
|
|
6750
|
|
|
1
|
|
|
|
|
5
|
|
9
|
|
|
|
|
|
|
#use Net::Cmd; |
10
|
1
|
|
|
1
|
|
1898
|
use FIX::Lite::Dictionary; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
28
|
|
11
|
1
|
|
|
1
|
|
2576
|
use IO::Select; |
|
1
|
|
|
|
|
1660
|
|
|
1
|
|
|
|
|
51
|
|
12
|
1
|
|
|
1
|
|
909
|
use Time::HiRes qw(gettimeofday); |
|
1
|
|
|
|
|
1517
|
|
|
1
|
|
|
|
|
5
|
|
13
|
|
|
|
|
|
|
|
14
|
|
|
|
|
|
|
#@ISA = qw(Net::Cmd IO::Socket::INET); |
15
|
|
|
|
|
|
|
@ISA = qw(IO::Socket::INET); |
16
|
|
|
|
|
|
|
$VERSION = "0.04"; |
17
|
|
|
|
|
|
|
|
18
|
|
|
|
|
|
|
my $fixDict; |
19
|
|
|
|
|
|
|
my $MsgSeqNum = 0; |
20
|
|
|
|
|
|
|
my %fieldDefaults = ( |
21
|
|
|
|
|
|
|
EncryptMethod => 0, |
22
|
|
|
|
|
|
|
HeartBtInt => 30, |
23
|
|
|
|
|
|
|
); |
24
|
|
|
|
|
|
|
my $sel; |
25
|
|
|
|
|
|
|
|
26
|
|
|
|
|
|
|
sub new { |
27
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
28
|
0
|
|
0
|
|
|
|
my $type = ref($self) || $self; |
29
|
0
|
|
|
|
|
|
my %arg = @_; |
30
|
0
|
|
|
|
|
|
my $obj; |
31
|
|
|
|
|
|
|
$obj = $type->SUPER::new( |
32
|
|
|
|
|
|
|
PeerHost => defined $arg{Host} ? $arg{Host} : '127.0.0.1', |
33
|
|
|
|
|
|
|
PeerPort => defined $arg{Port} ? $arg{Port} : '5201', |
34
|
0
|
0
|
|
|
|
|
Timeout => defined $arg{Timeout} ? $arg{Timeout} : 60, |
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
35
|
|
|
|
|
|
|
Proto => 'tcp', |
36
|
|
|
|
|
|
|
); |
37
|
0
|
|
|
|
|
|
$sel = IO::Select->new( $obj ); |
38
|
|
|
|
|
|
|
|
39
|
|
|
|
|
|
|
return undef |
40
|
0
|
0
|
|
|
|
|
unless defined $obj; |
41
|
|
|
|
|
|
|
|
42
|
0
|
|
|
|
|
|
$obj->autoflush(1); |
43
|
|
|
|
|
|
|
|
44
|
|
|
|
|
|
|
#$obj->debug(exists $arg{Debug} ? $arg{Debug} : undef); |
45
|
|
|
|
|
|
|
|
46
|
|
|
|
|
|
|
# Initialize $fixDict |
47
|
|
|
|
|
|
|
|
48
|
0
|
0
|
|
|
|
|
if ( defined $arg{version} ) { |
49
|
0
|
|
|
|
|
|
FIX::Lite::Dictionary::load( $arg{version} ); |
50
|
|
|
|
|
|
|
} |
51
|
|
|
|
|
|
|
else { |
52
|
0
|
|
|
|
|
|
FIX::Lite::Dictionary::load('FIX44'); |
53
|
|
|
|
|
|
|
} |
54
|
0
|
|
|
|
|
|
$fixDict = FIX::Lite::Dictionary->new(); |
55
|
|
|
|
|
|
|
|
56
|
0
|
|
|
|
|
|
$obj; |
57
|
|
|
|
|
|
|
} |
58
|
|
|
|
|
|
|
|
59
|
|
|
|
|
|
|
sub logon { |
60
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
61
|
0
|
|
|
|
|
|
my %arg = @_; |
62
|
|
|
|
|
|
|
|
63
|
0
|
|
|
|
|
|
$arg{ResetSeqNumFlag} = 'Y'; |
64
|
0
|
|
|
|
|
|
$MsgSeqNum=0; |
65
|
|
|
|
|
|
|
|
66
|
0
|
|
|
|
|
|
my $msgBody = constructMessage('Logon',\%arg); |
67
|
0
|
0
|
|
|
|
|
print "----\nPrepared Logon FIX Message:\n".readableFix($msgBody)."\n" if ($arg{Debug}); |
68
|
|
|
|
|
|
|
|
69
|
0
|
|
|
|
|
|
my $size = $self->send($msgBody); |
70
|
0
|
0
|
|
|
|
|
print " Sent data of length $size\n" if ($arg{Debug}); |
71
|
|
|
|
|
|
|
|
72
|
|
|
|
|
|
|
# receive a response of up to 1024 characters from server |
73
|
0
|
|
|
|
|
|
my $response = ""; |
74
|
0
|
|
|
|
|
|
$self->recv($response, 1024); |
75
|
0
|
0
|
|
|
|
|
print "----\nReceived Logon response:\n".readableFix($response)."\n" if ($arg{Debug}); |
76
|
0
|
|
|
|
|
|
my $parsedResp; |
77
|
0
|
0
|
|
|
|
|
$parsedResp = parseFixMessage($response) if ($response); |
78
|
0
|
|
|
|
|
|
${*$self}->{logon}=$parsedResp; |
|
0
|
|
|
|
|
|
|
79
|
0
|
|
|
|
|
|
${*$self}->{args}=\%arg; |
|
0
|
|
|
|
|
|
|
80
|
0
|
|
|
|
|
|
return $parsedResp; |
81
|
|
|
|
|
|
|
} |
82
|
|
|
|
|
|
|
|
83
|
|
|
|
|
|
|
sub request { |
84
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
85
|
0
|
|
|
|
|
|
my %arg = @_; |
86
|
|
|
|
|
|
|
|
87
|
0
|
|
0
|
|
|
|
$arg{SenderCompID} ||= ${*$self}->{args}->{SenderCompID}; |
|
0
|
|
|
|
|
|
|
88
|
0
|
|
0
|
|
|
|
$arg{TargetCompID} ||= ${*$self}->{args}->{TargetCompID}; |
|
0
|
|
|
|
|
|
|
89
|
0
|
0
|
0
|
|
|
|
$arg{TargetSubID} ||= (${*$self}->{args}->{TargetSubID}) ? ${*$self}->{args}->{TargetSubID} : undef; |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
90
|
|
|
|
|
|
|
|
91
|
0
|
|
|
|
|
|
my $msgBody = constructMessage($arg{MsgType},\%arg); |
92
|
0
|
0
|
|
|
|
|
print "----\nPrepared FIX Message:\n".readableFix($msgBody)."\n" if ($arg{Debug}); |
93
|
|
|
|
|
|
|
|
94
|
0
|
|
|
|
|
|
my $size = $self->send($msgBody); |
95
|
0
|
0
|
|
|
|
|
print " Sent data of length $size\n" if ($arg{Debug}); |
96
|
|
|
|
|
|
|
|
97
|
0
|
|
|
|
|
|
my $response = ""; |
98
|
|
|
|
|
|
|
|
99
|
0
|
|
|
|
|
|
$self->recv($response, 4096); |
100
|
|
|
|
|
|
|
|
101
|
0
|
0
|
|
|
|
|
print "----\nReceived response:\n".readableFix($response)."\n" if ($arg{Debug}); |
102
|
0
|
|
|
|
|
|
my $parsedResp; |
103
|
0
|
0
|
|
|
|
|
$parsedResp = parseFixMessage($response) if ($response); |
104
|
0
|
|
|
|
|
|
${*$self}->{request}=$parsedResp; |
|
0
|
|
|
|
|
|
|
105
|
|
|
|
|
|
|
|
106
|
0
|
|
|
|
|
|
return $parsedResp; |
107
|
|
|
|
|
|
|
} |
108
|
|
|
|
|
|
|
|
109
|
|
|
|
|
|
|
sub heartbeat { |
110
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
111
|
0
|
|
|
|
|
|
my %arg = @_; |
112
|
|
|
|
|
|
|
|
113
|
0
|
|
0
|
|
|
|
$arg{SenderCompID} ||= ${*$self}->{args}->{SenderCompID}; |
|
0
|
|
|
|
|
|
|
114
|
0
|
|
0
|
|
|
|
$arg{TargetCompID} ||= ${*$self}->{args}->{TargetCompID}; |
|
0
|
|
|
|
|
|
|
115
|
0
|
0
|
0
|
|
|
|
$arg{TargetSubID} ||= (${*$self}->{args}->{TargetSubID}) ? ${*$self}->{args}->{TargetSubID} : undef; |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
116
|
|
|
|
|
|
|
|
117
|
0
|
|
|
|
|
|
my $msgBody = constructMessage('Heartbeat',\%arg); |
118
|
0
|
0
|
|
|
|
|
print "----\nPrepared FIX Heartbeat:\n".readableFix($msgBody)."\n" if ($arg{Debug}); |
119
|
0
|
|
|
|
|
|
my $size = $self->send($msgBody); |
120
|
0
|
0
|
|
|
|
|
print " Sent data of length $size\n" if ($arg{Debug}); |
121
|
|
|
|
|
|
|
} |
122
|
|
|
|
|
|
|
|
123
|
|
|
|
|
|
|
sub listen { |
124
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
125
|
0
|
|
|
|
|
|
my $handler = shift; |
126
|
0
|
|
|
|
|
|
my %arg = @_; |
127
|
|
|
|
|
|
|
|
128
|
0
|
|
0
|
|
|
|
my $HeartBtInt = $arg{HeartBtInt} || $fieldDefaults{HeartBtInt}; |
129
|
0
|
|
|
|
|
|
my $response; |
130
|
0
|
|
|
|
|
|
my $lastHbTime = time; |
131
|
0
|
|
|
|
|
|
while (1) { |
132
|
0
|
|
|
|
|
|
my @ready = $sel->can_read(0); |
133
|
0
|
0
|
|
|
|
|
if (scalar(@ready)) { |
134
|
0
|
|
|
|
|
|
my $sock = $ready[0]; |
135
|
0
|
0
|
|
|
|
|
if (! sysread($ready[0], $response, 4096)) { |
136
|
0
|
|
|
|
|
|
print "recv failed :$!\n"; |
137
|
0
|
|
|
|
|
|
return 1; |
138
|
|
|
|
|
|
|
} else { |
139
|
0
|
0
|
|
|
|
|
print "----\nReceived FIX message:\n".readableFix($response)."\n" if ($arg{Debug}); |
140
|
|
|
|
|
|
|
|
141
|
|
|
|
|
|
|
#Split into each single msg |
142
|
0
|
|
|
|
|
|
for my $fixMsg ( split /8=FIX.4.4\x{01}/, $response ) { # Split on FIX version |
143
|
0
|
0
|
|
|
|
|
next if (length($fixMsg)<=0); |
144
|
|
|
|
|
|
|
|
145
|
0
|
0
|
|
|
|
|
print " Splitted FIX message:\n".readableFix($fixMsg)."\n" if ($arg{Debug}); |
146
|
|
|
|
|
|
|
|
147
|
0
|
|
|
|
|
|
my $parsedResp = parseFixMessage($fixMsg); |
148
|
|
|
|
|
|
|
|
149
|
0
|
0
|
|
|
|
|
if ( ! defined $parsedResp->{MsgType} ) { |
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
150
|
0
|
0
|
|
|
|
|
print " Cannot parse message\n" if ($arg{Debug}); |
151
|
|
|
|
|
|
|
} |
152
|
|
|
|
|
|
|
elsif ( $parsedResp->{MsgType} eq '0' ) { |
153
|
0
|
0
|
|
|
|
|
print " This is heartbeat. Will not pass it to handler\n" if ($arg{Debug}); |
154
|
|
|
|
|
|
|
} |
155
|
|
|
|
|
|
|
elsif ( $parsedResp->{MsgType} eq '1' ) { |
156
|
0
|
0
|
|
|
|
|
my $TestReqID = (defined $parsedResp->{TestReqID})?$parsedResp->{TestReqID}:'TEST'; |
157
|
0
|
0
|
|
|
|
|
print " This is TestRequest. Will send heartbeat with TestReqID $TestReqID\n" if ($arg{Debug}); |
158
|
|
|
|
|
|
|
$self->heartbeat( |
159
|
|
|
|
|
|
|
TestReqID => $TestReqID, |
160
|
|
|
|
|
|
|
Debug => $arg{Debug} |
161
|
0
|
|
|
|
|
|
); |
162
|
|
|
|
|
|
|
} |
163
|
|
|
|
|
|
|
else { |
164
|
0
|
|
|
|
|
|
$handler->($parsedResp); |
165
|
|
|
|
|
|
|
} |
166
|
|
|
|
|
|
|
|
167
|
|
|
|
|
|
|
} |
168
|
|
|
|
|
|
|
} |
169
|
|
|
|
|
|
|
} |
170
|
|
|
|
|
|
|
|
171
|
0
|
0
|
|
|
|
|
if ( time - $lastHbTime > $HeartBtInt ) { |
172
|
0
|
|
|
|
|
|
$lastHbTime = time; |
173
|
0
|
|
|
|
|
|
$self->heartbeat( Debug => $arg{Debug} ); |
174
|
|
|
|
|
|
|
} |
175
|
0
|
|
|
|
|
|
select(undef, undef, undef, 0.002); |
176
|
|
|
|
|
|
|
|
177
|
|
|
|
|
|
|
} |
178
|
|
|
|
|
|
|
} |
179
|
|
|
|
|
|
|
|
180
|
|
|
|
|
|
|
sub loggedIn { |
181
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
182
|
0
|
0
|
0
|
|
|
|
return 1 if (defined ${*$self}->{logon}->{'MsgType'} && ${*$self}->{logon}->{'MsgType'} eq getMessageType('Logon')); |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
183
|
0
|
|
|
|
|
|
return 0; |
184
|
|
|
|
|
|
|
} |
185
|
|
|
|
|
|
|
|
186
|
|
|
|
|
|
|
sub lastRequest { |
187
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
188
|
0
|
|
|
|
|
|
my $field = shift; |
189
|
0
|
|
|
|
|
|
return getFieldDescription($field, ${*$self}->{request}->{$field}); |
|
0
|
|
|
|
|
|
|
190
|
|
|
|
|
|
|
} |
191
|
|
|
|
|
|
|
|
192
|
|
|
|
|
|
|
sub constructMessage($$) { |
193
|
|
|
|
|
|
|
|
194
|
0
|
|
|
0
|
0
|
|
my $msgtype = shift; |
195
|
0
|
|
|
|
|
|
my $arg = shift; |
196
|
0
|
|
|
|
|
|
my @fields; |
197
|
0
|
|
|
|
|
|
undef $arg->{MsgType}; |
198
|
0
|
|
|
|
|
|
$MsgSeqNum++; |
199
|
|
|
|
|
|
|
|
200
|
0
|
|
|
|
|
|
my $time = strftime "%Y%m%d-%H:%M:%S.".getMilliseconds(), gmtime; |
201
|
0
|
|
|
|
|
|
push @fields, getFieldNumber('MsgType')."=".getMessageType($msgtype); |
202
|
0
|
|
|
|
|
|
push @fields, getFieldNumber('SendingTime')."=".$time; |
203
|
0
|
|
|
|
|
|
push @fields, getFieldNumber('MsgSeqNum')."=".$MsgSeqNum; |
204
|
|
|
|
|
|
|
|
205
|
0
|
|
|
|
|
|
my @allFields = ( @{getMessageHeader()}, @{getMessageFields($msgtype)} ); |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
206
|
|
|
|
|
|
|
|
207
|
0
|
|
|
|
|
|
foreach my $field ( @allFields ) { |
208
|
0
|
0
|
0
|
|
|
|
if ( defined $arg->{$field->{name}} ) { |
|
|
0
|
0
|
|
|
|
|
|
|
0
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
209
|
0
|
0
|
|
|
|
|
if (ref($arg->{$field->{name}}) eq "HASH") { |
210
|
0
|
|
|
|
|
|
my @tmpFields; |
211
|
0
|
|
|
|
|
|
my $count=0; |
212
|
0
|
|
|
|
|
|
foreach my $component ( keys %{$arg->{$field->{name}}} ) { |
|
0
|
|
|
|
|
|
|
213
|
0
|
0
|
|
|
|
|
if (isComponent($component)) { |
214
|
0
|
|
|
|
|
|
my @componentFields = @{getComponentFields($component)}; |
|
0
|
|
|
|
|
|
|
215
|
0
|
|
|
|
|
|
foreach ( @componentFields ) { |
216
|
0
|
0
|
|
|
|
|
if ( defined $arg->{$field->{name}}->{$component}->{$_->{name}} ){ |
217
|
0
|
|
|
|
|
|
my $componentField = $arg->{$field->{name}}->{$component}->{$_->{name}}; |
218
|
0
|
0
|
|
|
|
|
if ( ref($componentField) eq "ARRAY" ) { |
219
|
0
|
|
|
|
|
|
foreach my $entry ( @{$componentField} ) { |
|
0
|
|
|
|
|
|
|
220
|
0
|
|
|
|
|
|
push @tmpFields, getFieldNumber($_->{name})."=".getFieldValue($_->{name},$entry); |
221
|
0
|
|
|
|
|
|
$count++ |
222
|
|
|
|
|
|
|
} |
223
|
|
|
|
|
|
|
} else { |
224
|
0
|
|
|
|
|
|
push @tmpFields, getFieldNumber($_->{name})."=".getFieldValue($_->{name},$componentField); |
225
|
0
|
|
|
|
|
|
$count++; |
226
|
|
|
|
|
|
|
} |
227
|
|
|
|
|
|
|
} |
228
|
|
|
|
|
|
|
} |
229
|
|
|
|
|
|
|
} else { |
230
|
0
|
|
|
|
|
|
my $componentField = $arg->{$field->{name}}->{$component}; |
231
|
0
|
0
|
|
|
|
|
if ( ref($componentField) eq "ARRAY" ) { |
232
|
0
|
|
|
|
|
|
foreach my $entry ( @{$componentField} ) { |
|
0
|
|
|
|
|
|
|
233
|
0
|
|
|
|
|
|
push @tmpFields, getFieldNumber($component)."=".getFieldValue($component,$entry); |
234
|
0
|
|
|
|
|
|
$count++ |
235
|
|
|
|
|
|
|
} |
236
|
|
|
|
|
|
|
} else { |
237
|
0
|
|
|
|
|
|
push @tmpFields, getFieldNumber($component)."=".getFieldValue($component,$componentField); |
238
|
|
|
|
|
|
|
} |
239
|
|
|
|
|
|
|
} |
240
|
|
|
|
|
|
|
|
241
|
|
|
|
|
|
|
} |
242
|
0
|
|
|
|
|
|
push @fields, getFieldNumber($field->{name})."=".$count; |
243
|
0
|
|
|
|
|
|
@fields = ( @fields, @tmpFields ); |
244
|
|
|
|
|
|
|
} |
245
|
|
|
|
|
|
|
|
246
|
0
|
0
|
|
|
|
|
next if (ref($arg->{$field->{name}}) eq "HASH"); |
247
|
0
|
|
|
|
|
|
push @fields, getFieldNumber($field->{name})."=".getFieldValue($field->{name},$arg->{$field->{name}}); |
248
|
|
|
|
|
|
|
} |
249
|
|
|
|
|
|
|
elsif ( $field->{required} eq 'Y' && defined $fieldDefaults{$field->{name}} ) { |
250
|
|
|
|
|
|
|
push @fields, getFieldNumber($field->{name})."=".$fieldDefaults{$field->{name}} |
251
|
0
|
|
|
|
|
|
} |
252
|
|
|
|
|
|
|
elsif ( $field->{required} eq 'Y' && $field->{name} ne 'BeginString' and $field->{name} ne 'BodyLength' |
253
|
|
|
|
|
|
|
and $field->{name} ne 'MsgType' and $field->{name} ne 'MsgSeqNum' and $field->{name} ne 'SendingTime') { |
254
|
0
|
0
|
|
|
|
|
if ($field->{name} eq "MDReqID") { |
255
|
0
|
|
|
|
|
|
push @fields, getFieldNumber($field->{name})."=".randomString(); |
256
|
|
|
|
|
|
|
} else { |
257
|
0
|
|
|
|
|
|
print "ERROR: $field->{name}\n"; |
258
|
|
|
|
|
|
|
} |
259
|
|
|
|
|
|
|
} |
260
|
|
|
|
|
|
|
} |
261
|
|
|
|
|
|
|
|
262
|
0
|
|
|
|
|
|
my $req = join "\x01",@fields; |
263
|
0
|
|
|
|
|
|
$req .= "\x01"; |
264
|
0
|
|
|
|
|
|
$req = getFieldNumber('BeginString')."=FIX.4.4\x01".getFieldNumber('BodyLength')."=".length($req)."\x01".$req; |
265
|
0
|
|
|
|
|
|
my $checksum = unpack("%8C*", $req) % 256; |
266
|
0
|
|
|
|
|
|
$checksum = sprintf( "%03d", $checksum ); |
267
|
0
|
|
|
|
|
|
$req .= getFieldNumber('CheckSum')."=$checksum\x01"; |
268
|
0
|
|
|
|
|
|
return $req."\n"; |
269
|
|
|
|
|
|
|
} |
270
|
|
|
|
|
|
|
|
271
|
|
|
|
|
|
|
sub getField($) { |
272
|
0
|
|
|
0
|
0
|
|
my $f = shift; |
273
|
0
|
|
|
|
|
|
return $fixDict->{hFields}->{$f}; |
274
|
|
|
|
|
|
|
} |
275
|
|
|
|
|
|
|
|
276
|
|
|
|
|
|
|
# returns 1 if given field is a group header field |
277
|
|
|
|
|
|
|
# isGroup('NoAllocs') -> returns 1 |
278
|
|
|
|
|
|
|
# isGroup('Symbol') -> returns 0 |
279
|
|
|
|
|
|
|
sub isGroup($) { |
280
|
0
|
|
|
0
|
0
|
|
my $f = shift; |
281
|
0
|
|
|
|
|
|
my $ff = getField($f); |
282
|
0
|
0
|
|
|
|
|
return defined $ff ? $ff->{type} eq 'NUMINGROUP' : 0; |
283
|
|
|
|
|
|
|
} |
284
|
|
|
|
|
|
|
|
285
|
|
|
|
|
|
|
# returns true if given field is a member of the given group of given message. |
286
|
|
|
|
|
|
|
sub isFieldInGroup($$$) { |
287
|
0
|
|
|
0
|
0
|
|
my ( $m, $g, $f ) = @_; |
288
|
|
|
|
|
|
|
|
289
|
0
|
|
|
|
|
|
my $gn = getFieldName($g); |
290
|
0
|
0
|
|
|
|
|
return 0 if ! defined $gn; |
291
|
0
|
0
|
|
|
|
|
return 0 if ! isGroup($gn); |
292
|
|
|
|
|
|
|
|
293
|
0
|
|
|
|
|
|
my $msg = getGroupInMessage($m, $g); |
294
|
0
|
0
|
|
|
|
|
return 0 if ! defined $msg; |
295
|
0
|
|
|
|
|
|
return _isFieldInStructure($msg, $f); |
296
|
|
|
|
|
|
|
} |
297
|
|
|
|
|
|
|
|
298
|
|
|
|
|
|
|
# return a ref on group of a message, this then allows us to work on the group elements. |
299
|
|
|
|
|
|
|
# $d->getGroupInMessage('D','NoAllocs') |
300
|
|
|
|
|
|
|
# will return a ref on the NoAllocs group allowing us to then parse it |
301
|
|
|
|
|
|
|
# |
302
|
|
|
|
|
|
|
# Looks recursively into groups of groups if needed. |
303
|
|
|
|
|
|
|
sub getGroupInMessage($$) { |
304
|
0
|
|
|
0
|
0
|
|
my ( $m, $g ) = @_; |
305
|
0
|
|
|
|
|
|
my $s = getMessageFields($m); |
306
|
0
|
0
|
|
|
|
|
return undef if ! defined $s; |
307
|
0
|
|
|
|
|
|
my $gn = getFieldName($g); |
308
|
0
|
0
|
|
|
|
|
return undef if ! defined($gn); |
309
|
|
|
|
|
|
|
|
310
|
0
|
0
|
|
|
|
|
return undef if ! isGroup($g); |
311
|
|
|
|
|
|
|
|
312
|
0
|
|
|
|
|
|
return _getGroupInStructure( $s, $gn ); |
313
|
|
|
|
|
|
|
} |
314
|
|
|
|
|
|
|
|
315
|
|
|
|
|
|
|
# returns true if given field is found in the structure. |
316
|
|
|
|
|
|
|
sub _isFieldInStructure($$); |
317
|
|
|
|
|
|
|
|
318
|
|
|
|
|
|
|
sub _isFieldInStructure($$) { |
319
|
0
|
|
|
0
|
|
|
my ( $m, $f ) = @_; |
320
|
0
|
0
|
0
|
|
|
|
return 0 unless ( defined $m && defined $f ); |
321
|
0
|
|
|
|
|
|
my $fn = getFieldName($f); |
322
|
0
|
0
|
|
|
|
|
return 0 if ! defined $fn; |
323
|
|
|
|
|
|
|
|
324
|
0
|
|
|
|
|
|
for my $f2 ( @{$m} ) { |
|
0
|
|
|
|
|
|
|
325
|
|
|
|
|
|
|
# found the field? return 1. Beware that if the element is a component then we don't accept |
326
|
|
|
|
|
|
|
# it as a valid field of the structure. |
327
|
0
|
0
|
0
|
|
|
|
return 1 if ( $f2->{name} eq $fn && !defined $f2->{component} ); |
328
|
|
|
|
|
|
|
|
329
|
|
|
|
|
|
|
# if the field is a group then scan all elements of the group |
330
|
0
|
0
|
|
|
|
|
if ( defined $f2->{group} ) { |
331
|
0
|
0
|
|
|
|
|
return 1 if _isFieldInStructure( $f2->{group}, $fn ) == 1; |
332
|
|
|
|
|
|
|
} |
333
|
|
|
|
|
|
|
|
334
|
|
|
|
|
|
|
# if the field is a component, we need to go to the component hash and check out its |
335
|
|
|
|
|
|
|
# composition. |
336
|
0
|
0
|
|
|
|
|
if ( defined $f2->{component} ) { |
337
|
0
|
0
|
|
|
|
|
return 1 if _isFieldInStructure( getComponentFields($f2->{name}), $fn ) == 1; |
338
|
|
|
|
|
|
|
} |
339
|
|
|
|
|
|
|
} |
340
|
0
|
|
|
|
|
|
return 0; |
341
|
|
|
|
|
|
|
} |
342
|
|
|
|
|
|
|
|
343
|
|
|
|
|
|
|
sub _getGroupInStructure($$); |
344
|
|
|
|
|
|
|
|
345
|
|
|
|
|
|
|
sub _getGroupInStructure($$) { |
346
|
0
|
|
|
0
|
|
|
my ($s, $gn) = @_; |
347
|
|
|
|
|
|
|
|
348
|
0
|
|
|
|
|
|
my $ret; |
349
|
|
|
|
|
|
|
# parse each field in the structure, and .... |
350
|
0
|
|
|
|
|
|
for my $e ( @{$s} ) { |
|
0
|
|
|
|
|
|
|
351
|
|
|
|
|
|
|
# we found the group name |
352
|
0
|
0
|
0
|
|
|
|
return $e->{group} if ($e->{name} eq $gn && defined $e->{group}); |
353
|
|
|
|
|
|
|
|
354
|
|
|
|
|
|
|
# stop at each group header |
355
|
0
|
0
|
|
|
|
|
if (defined $e->{group}) { |
356
|
|
|
|
|
|
|
# and research recursively |
357
|
0
|
|
|
|
|
|
$ret = _getGroupInStructure($e->{group},$gn); |
358
|
0
|
0
|
|
|
|
|
return $ret if defined $ret; |
359
|
|
|
|
|
|
|
} |
360
|
|
|
|
|
|
|
|
361
|
|
|
|
|
|
|
# if we run into a component we need to check that out too |
362
|
0
|
0
|
|
|
|
|
if (defined $e->{component}) { |
363
|
0
|
|
|
|
|
|
$ret = _getGroupInStructure(getComponentFields($e->{name}), $gn); |
364
|
0
|
0
|
|
|
|
|
return $ret if defined $ret; |
365
|
|
|
|
|
|
|
} |
366
|
|
|
|
|
|
|
} |
367
|
0
|
|
|
|
|
|
undef; |
368
|
|
|
|
|
|
|
} |
369
|
|
|
|
|
|
|
|
370
|
|
|
|
|
|
|
|
371
|
|
|
|
|
|
|
sub getFieldName($) { |
372
|
0
|
|
|
0
|
0
|
|
my $f = shift; |
373
|
0
|
|
|
|
|
|
my $fh = getField($f); |
374
|
0
|
0
|
|
|
|
|
return defined $fh ? $fh->{name} : undef; |
375
|
|
|
|
|
|
|
} |
376
|
|
|
|
|
|
|
|
377
|
|
|
|
|
|
|
sub getTagById { |
378
|
0
|
|
|
0
|
1
|
|
my ($self, $f) = @_; |
379
|
0
|
|
|
|
|
|
return getFieldName($f); |
380
|
|
|
|
|
|
|
} |
381
|
|
|
|
|
|
|
|
382
|
|
|
|
|
|
|
sub getFieldNumber($) { |
383
|
0
|
|
|
0
|
0
|
|
my $f = shift; |
384
|
0
|
0
|
|
|
|
|
return $f if ( $f =~ /^[0-9]+$/ ); |
385
|
0
|
|
|
|
|
|
my $fh = getField($f); |
386
|
0
|
0
|
|
|
|
|
warn("getFieldNumber($f) returning undef") if !defined $fh; |
387
|
0
|
0
|
|
|
|
|
return defined $fh ? $fh->{number} : undef; |
388
|
|
|
|
|
|
|
} |
389
|
|
|
|
|
|
|
|
390
|
|
|
|
|
|
|
sub getFieldValue($$) { |
391
|
0
|
|
|
0
|
0
|
|
my $f = shift; |
392
|
0
|
|
|
|
|
|
my $v = shift; |
393
|
0
|
0
|
|
|
|
|
return $v if ( $v =~ /^[0-9]+$/ ); |
394
|
0
|
|
|
|
|
|
my $fh = getField($f); |
395
|
0
|
0
|
|
|
|
|
warn("getField($f) returning undef") if !defined $fh; |
396
|
0
|
0
|
|
|
|
|
if ($fh->{enum}) { |
397
|
0
|
|
|
|
|
|
foreach ( @{$fh->{enum}} ) { |
|
0
|
|
|
|
|
|
|
398
|
0
|
0
|
|
|
|
|
if ($_->{description} eq $v) { |
399
|
0
|
|
|
|
|
|
return $_->{name}; |
400
|
|
|
|
|
|
|
} |
401
|
|
|
|
|
|
|
} |
402
|
|
|
|
|
|
|
} |
403
|
0
|
|
|
|
|
|
return $v; |
404
|
|
|
|
|
|
|
} |
405
|
|
|
|
|
|
|
|
406
|
|
|
|
|
|
|
sub getFieldDescription($$) { |
407
|
0
|
|
|
0
|
0
|
|
my $f = shift; |
408
|
0
|
|
|
|
|
|
my $v = shift; |
409
|
0
|
|
|
|
|
|
my $fh = getField($f); |
410
|
0
|
0
|
|
|
|
|
warn("getField($f) returning undef") if !defined $fh; |
411
|
0
|
0
|
|
|
|
|
if ($fh->{enum}) { |
412
|
0
|
|
|
|
|
|
foreach ( @{$fh->{enum}} ) { |
|
0
|
|
|
|
|
|
|
413
|
0
|
0
|
|
|
|
|
if ($_->{name} eq $v) { |
414
|
0
|
|
|
|
|
|
return $_->{description}; |
415
|
|
|
|
|
|
|
} |
416
|
|
|
|
|
|
|
} |
417
|
|
|
|
|
|
|
} |
418
|
0
|
|
|
|
|
|
return $v; |
419
|
|
|
|
|
|
|
} |
420
|
|
|
|
|
|
|
|
421
|
|
|
|
|
|
|
sub getMessage($) { |
422
|
0
|
|
|
0
|
0
|
|
my $f = shift; |
423
|
0
|
|
|
|
|
|
return $fixDict->{hMessages}->{$f}; |
424
|
|
|
|
|
|
|
} |
425
|
|
|
|
|
|
|
|
426
|
|
|
|
|
|
|
sub getMessageType($) { |
427
|
0
|
|
|
0
|
0
|
|
my $f = shift; |
428
|
0
|
0
|
|
|
|
|
return $f if ( $f =~ /^[0-9]+$/ ); |
429
|
0
|
|
|
|
|
|
my $fh = getMessage($f); |
430
|
0
|
0
|
|
|
|
|
warn("getMessage($f) returning undef") if !defined $fh; |
431
|
0
|
0
|
|
|
|
|
return defined $fh ? $fh->{msgtype} : undef; |
432
|
|
|
|
|
|
|
} |
433
|
|
|
|
|
|
|
|
434
|
|
|
|
|
|
|
sub getMessageName($) { |
435
|
0
|
|
|
0
|
0
|
|
my $f = shift; |
436
|
0
|
|
|
|
|
|
my $fh = getMessage($f); |
437
|
0
|
0
|
|
|
|
|
warn("getMessage($f) returning undef") if !defined $fh; |
438
|
0
|
0
|
|
|
|
|
return defined $fh ? $fh->{name} : undef; |
439
|
|
|
|
|
|
|
} |
440
|
|
|
|
|
|
|
|
441
|
|
|
|
|
|
|
sub getMsgByType { |
442
|
0
|
|
|
0
|
1
|
|
my ($self, $f) = @_; |
443
|
0
|
|
|
|
|
|
return getMessageName($f); |
444
|
|
|
|
|
|
|
} |
445
|
|
|
|
|
|
|
|
446
|
|
|
|
|
|
|
sub getMessageFields($) { |
447
|
0
|
|
|
0
|
0
|
|
my $f = shift; |
448
|
0
|
|
|
|
|
|
my $fh = getMessage($f); |
449
|
0
|
0
|
|
|
|
|
warn("getMessage($f) returning undef") if !defined $fh; |
450
|
0
|
0
|
|
|
|
|
return defined $fh ? $fh->{fields} : undef; |
451
|
|
|
|
|
|
|
} |
452
|
|
|
|
|
|
|
|
453
|
|
|
|
|
|
|
sub getMessageHeader { |
454
|
0
|
|
|
0
|
0
|
|
return $fixDict->{header}; |
455
|
|
|
|
|
|
|
} |
456
|
|
|
|
|
|
|
|
457
|
|
|
|
|
|
|
sub getComponent($) { |
458
|
0
|
|
|
0
|
0
|
|
my $f = shift; |
459
|
0
|
|
|
|
|
|
return $fixDict->{hComponents}->{$f}; |
460
|
|
|
|
|
|
|
} |
461
|
|
|
|
|
|
|
|
462
|
|
|
|
|
|
|
sub isComponent($) { |
463
|
0
|
|
|
0
|
0
|
|
my $f = shift; |
464
|
0
|
|
|
|
|
|
return defined $fixDict->{hComponents}->{$f}; |
465
|
|
|
|
|
|
|
} |
466
|
|
|
|
|
|
|
|
467
|
|
|
|
|
|
|
sub getComponentFields($) { |
468
|
0
|
|
|
0
|
0
|
|
my $f = shift; |
469
|
0
|
|
|
|
|
|
my $fh = getComponent($f); |
470
|
0
|
0
|
|
|
|
|
warn("getComponent($f) returning undef") if !defined $fh; |
471
|
0
|
0
|
|
|
|
|
return defined $fh ? $fh->{fields} : undef; |
472
|
|
|
|
|
|
|
} |
473
|
|
|
|
|
|
|
|
474
|
|
|
|
|
|
|
sub parseFixMessage { |
475
|
0
|
|
|
0
|
0
|
|
my $message = shift; |
476
|
0
|
0
|
|
|
|
|
return unless defined $message; |
477
|
0
|
|
|
|
|
|
my $parsedMsg; |
478
|
|
|
|
|
|
|
|
479
|
0
|
|
|
|
|
|
my @fields = split /\x01/, $message; # Split on "SOH" |
480
|
0
|
|
|
|
|
|
_parseFixArray( \$parsedMsg, undef, undef, 0, \@fields ); |
481
|
|
|
|
|
|
|
|
482
|
0
|
|
|
|
|
|
return $parsedMsg; |
483
|
|
|
|
|
|
|
} |
484
|
|
|
|
|
|
|
|
485
|
|
|
|
|
|
|
sub _parseFixArray($$$$$); |
486
|
|
|
|
|
|
|
|
487
|
|
|
|
|
|
|
sub _parseFixArray($$$$$) { |
488
|
0
|
|
|
0
|
|
|
my ( $result, $msgType, $groupTag, $iField, $fields ) = @_; |
489
|
0
|
|
|
|
|
|
my $i = $iField; |
490
|
|
|
|
|
|
|
|
491
|
0
|
|
|
|
|
|
while ( $i < scalar(@$fields) ) { |
492
|
0
|
|
|
|
|
|
my $field = $fields->[$i]; |
493
|
0
|
|
|
|
|
|
my ( $k, $v ) = ( $field =~ /^([^=]+)=(.*)$/ ); |
494
|
|
|
|
|
|
|
|
495
|
0
|
0
|
|
|
|
|
if ( defined $$result->{$k} ) { |
496
|
0
|
0
|
|
|
|
|
return $i if defined $groupTag; |
497
|
0
|
|
|
|
|
|
warn("Field $k is already in hash!"); |
498
|
|
|
|
|
|
|
} |
499
|
0
|
0
|
|
|
|
|
if ( defined $groupTag ) { |
500
|
0
|
0
|
|
|
|
|
return $i if !isFieldInGroup( $msgType, $groupTag, $k ); |
501
|
|
|
|
|
|
|
} |
502
|
|
|
|
|
|
|
# Store both using Tag and FieldName. |
503
|
0
|
|
|
|
|
|
$$result->{$k} = $v; |
504
|
0
|
|
|
|
|
|
my $fieldName = getFieldName($k); |
505
|
0
|
0
|
|
|
|
|
if ( defined $fieldName ) { |
506
|
0
|
|
|
|
|
|
$$result->{$fieldName} = $v; |
507
|
|
|
|
|
|
|
} else { |
508
|
0
|
|
|
|
|
|
warn("Haven't found field $k in dictionary"); |
509
|
|
|
|
|
|
|
} |
510
|
|
|
|
|
|
|
|
511
|
0
|
0
|
|
|
|
|
if ( $fieldName eq 'MsgType' ) { |
|
|
0
|
|
|
|
|
|
512
|
0
|
|
|
|
|
|
$msgType = $v; |
513
|
|
|
|
|
|
|
} |
514
|
|
|
|
|
|
|
elsif ( isGroup($k) ) { |
515
|
0
|
|
|
|
|
|
my @elems; |
516
|
0
|
|
|
|
|
|
$i++; |
517
|
0
|
|
|
|
|
|
for ( 1 .. $v ) { |
518
|
0
|
|
|
|
|
|
my $localResult; |
519
|
0
|
|
|
|
|
|
$i = _parseFixArray( \$localResult, $msgType, $k, $i, $fields ); |
520
|
0
|
|
|
|
|
|
push( @elems, $localResult ); |
521
|
|
|
|
|
|
|
} |
522
|
|
|
|
|
|
|
# Store both using Tag and FieldName. |
523
|
0
|
|
|
|
|
|
$$result->{$k} = \@elems; |
524
|
0
|
|
|
|
|
|
$$result->{$fieldName} = \@elems; |
525
|
0
|
|
|
|
|
|
$i--; |
526
|
|
|
|
|
|
|
} |
527
|
0
|
|
|
|
|
|
$i++; |
528
|
|
|
|
|
|
|
} |
529
|
|
|
|
|
|
|
} |
530
|
|
|
|
|
|
|
|
531
|
|
|
|
|
|
|
sub randomString { |
532
|
0
|
|
|
0
|
0
|
|
my @chars = ("A".."Z", "a".."z"); |
533
|
0
|
|
|
|
|
|
my $string; |
534
|
0
|
|
|
|
|
|
$string .= $chars[rand @chars] for 1..6; |
535
|
0
|
|
|
|
|
|
return $string; |
536
|
|
|
|
|
|
|
} |
537
|
|
|
|
|
|
|
|
538
|
|
|
|
|
|
|
sub readableFix { |
539
|
0
|
|
|
0
|
0
|
|
my $fixMsg = shift; |
540
|
0
|
|
|
|
|
|
$fixMsg =~ s/\x01/\|/g; |
541
|
0
|
|
|
|
|
|
return $fixMsg; |
542
|
|
|
|
|
|
|
} |
543
|
|
|
|
|
|
|
|
544
|
|
|
|
|
|
|
sub quit { |
545
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
546
|
|
|
|
|
|
|
|
547
|
0
|
|
|
|
|
|
$self->close; |
548
|
|
|
|
|
|
|
} |
549
|
|
|
|
|
|
|
|
550
|
|
|
|
|
|
|
sub getMilliseconds { |
551
|
0
|
|
|
0
|
0
|
|
my $time = gettimeofday; |
552
|
0
|
|
|
|
|
|
return sprintf("%03d",int(($time-int($time))*1000)); |
553
|
|
|
|
|
|
|
} |
554
|
|
|
|
|
|
|
1; # End of FIX::Lite |
555
|
|
|
|
|
|
|
__END__ |