p2ruby 0.1.4 → 0.1.5

Sign up to get free protection for your applications and to get access to all the features.
data/HISTORY CHANGED
@@ -21,3 +21,7 @@
21
21
  == 0.1.4 / 2011-11-11
22
22
 
23
23
  * Fixed brittle specs
24
+
25
+ == 0.1.5 / 2011-11-11
26
+
27
+ * Sample client scripts added
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.1.4
1
+ 0.1.5
@@ -0,0 +1,72 @@
1
+ #!/usr/bin/env ruby
2
+ # encoding: Windows-1251
3
+ require_relative 'script_helper'
4
+ require 'clients/order_console'
5
+
6
+ # Global functions
7
+ #####################################
8
+ def send_message conn, server_address, message_factory
9
+ $log.puts "Sending sync message..."
10
+
11
+ msg = message_factory.message :name => "FutAddOrder",
12
+ :field => {
13
+ "P2_Category" => "FORTS_MSG",
14
+ "P2_Type" => 1,
15
+ "isin" => "RTS-3.12",
16
+ :price => "185500",
17
+ :amount => 1,
18
+ "client_code" => "001",
19
+ "type" => 1,
20
+ "dir" => 1}
21
+ msg.DestAddr = server_address
22
+
23
+ reply = msg.Send(conn, 1000)
24
+
25
+ $log.puts reply.parse_reply
26
+
27
+ $send = false
28
+ end
29
+
30
+ # Signal Handler (sends signals into main event loop via global variables)
31
+ ####################################
32
+ Signal.trap("INT") do |signo|
33
+ puts "Send sync message?"
34
+ if 'y' == STDIN.gets.chomp
35
+ $send = true
36
+ else
37
+ puts "Interrupted... (#{signo})"
38
+ $exit = true
39
+ end
40
+ end
41
+
42
+ # Main execution logics
43
+ #####################################
44
+ # void ThreadProc(void* name) : Not sure why second event loop is needed?
45
+
46
+ $log = ARGV.first == 'log' ? File.new("log/order_console.log", 'w') : STDOUT
47
+ $exit = false
48
+ $send = false
49
+
50
+ start_router do
51
+ conn = Clients::ConnectionEvents.new "RbOrderConsole"
52
+ ds_futinfo = Clients::DataStreamEvents.new conn, "FORTS_FUTINFO"
53
+ # ds_pos = DataStreamEvents.new conn, "FORTS_POS"
54
+ # ds_part = DataStreamEvents.new conn, "FORTS_PART"
55
+ # ds_futtrade = DataStreamEvents.new conn, "FORTS_FUTTRADE"
56
+ # ds_optinfo = DataStreamEvents.new conn, "FORTS_OPTINFO"
57
+ # ds_futaggr = DataStreamEvents.new conn, "FORTS_OPTAGGR"
58
+ # ds_futaggr20 = DataStreamEvents.new conn, "FORTS_FUTAGGR20"
59
+ # ds_optcommon = DataStreamEvents.new conn, "FORTS_OPTCOMMON"
60
+ # ds_futcommon = DataStreamEvents.new conn, "FORTS_FUTCOMMON"
61
+ ds_index = Clients::DataStreamEvents.new conn, "RTS_INDEX"
62
+
63
+ server_address = conn.ResolveService("FORTS_SRV")
64
+
65
+ puts "Press Ctrl-C to send message or interrupt program"
66
+ message_factory = P2::MessageFactory.new :ini => MESSAGE_INI
67
+
68
+ until $exit
69
+ send_message(conn, server_address, message_factory) if $send
70
+ conn.ProcessMessage2(1000)
71
+ end
72
+ end
@@ -0,0 +1,66 @@
1
+ #!/usr/bin/env ruby
2
+ require 'bundler'
3
+ Bundler.setup
4
+
5
+ require 'pathname'
6
+ require 'fileutils'
7
+
8
+ BASE_DIR = (Pathname.new(__FILE__).dirname + '..').realpath.to_s
9
+ #BASE_DIR = File.expand_path(File.join(File.dirname(File.expand_path(__FILE__)), '..'))
10
+ SOURCE_DIR = BASE_DIR + '/p2/'
11
+ TMP_DIR = BASE_DIR + '/tmp/'
12
+ TEST_DIR = BASE_DIR + '/tmp/p2/'
13
+ CONFIG_DIR = BASE_DIR + '/config/'
14
+ INI_DIR = BASE_DIR + '/config/ini/'
15
+ DATA_DIR = BASE_DIR + '/data/' # For saving incoming (replication) data
16
+ LIB_DIR = BASE_DIR + '/lib/'
17
+
18
+ $LOAD_PATH.unshift LIB_DIR unless $LOAD_PATH.include?(LIB_DIR)
19
+
20
+ CLIENT_INI = INI_DIR + 'P2ClientGate.ini'
21
+ MESSAGE_INI = INI_DIR + 'p2fortsgate_messages.ini'
22
+ TABLESET_INI = INI_DIR + 'rts_index.ini'
23
+ ROUTER_INI = INI_DIR + 'client_router.ini'
24
+ ROUTER_PATH = TEST_DIR + 'p2bin/P2MQRouter.exe'
25
+ ROUTER_TITLE = /P2MQRouter - /
26
+
27
+ def prepare_dirs
28
+ # First we need to prepare clean copy of P2 stand by copying P2 files to /tmp
29
+ FileUtils.rm_rf TMP_DIR
30
+ FileUtils.cp_r SOURCE_DIR, TEST_DIR
31
+
32
+ # Create temp dirs unless they aready exist
33
+ FileUtils.mkdir DATA_DIR unless File.exist? DATA_DIR
34
+ end
35
+
36
+ # Starts Router, yields to given block (if any)
37
+ def start_router opts ={}
38
+ # Make sure p2ruby gem WAS indeed required...
39
+ require 'p2ruby' unless defined? P2
40
+
41
+ # Find any working router if no opts given
42
+ router = opts.empty? ? P2::Router.find : nil
43
+
44
+ unless router # is already found
45
+ prepare_dirs
46
+ router = P2::Router.new :dir => opts[:dir] || TEST_DIR,
47
+ :path => opts[:path] || ROUTER_PATH,
48
+ :ini => opts[:ini] || ROUTER_INI,
49
+ :args => opts[:args], # usually, it's just /ini:,
50
+ :title => opts[:title] || ROUTER_TITLE,
51
+ :timeout => opts[:timeout] || 5
52
+
53
+ puts "Router started at #{ROUTER_PATH}, establishing uplink..."
54
+ sleep 0.7
55
+ end
56
+
57
+ if block_given?
58
+ yield router
59
+ else
60
+ router
61
+ end
62
+
63
+ rescue => e
64
+ puts "Caught in start_router: #{e}"
65
+ raise e
66
+ end
@@ -0,0 +1,46 @@
1
+ require_relative 'script_helper'
2
+ require 'p2ruby'
3
+
4
+ # This script replicates SimpleSend.js functionality
5
+ start_router do
6
+
7
+ # Creating Connection object
8
+ conn = P2::Connection.new(:ini => CLIENT_INI,
9
+ :app_name => "RbOrdSend", # ����� ���������� � ������������� ���.
10
+ :host => "127.0.0.1", # IP �����
11
+ :port => 4001) # � ���� ���������� �������
12
+
13
+ result = conn.Connect() # ������������� ���������� � ��������� ��������
14
+
15
+ puts "Connection result: #{result}..."
16
+
17
+ server_address = conn.ResolveService("FORTS_SRV") # ���� ����� ������� ������ ������
18
+
19
+ puts "FORTS_SRV server address: #{server_address}..."
20
+
21
+ # ������� � �������������� ������� ��������-���������
22
+ msgs = P2::MessageFactory.new :ini => MESSAGE_INI
23
+
24
+ puts "Msg Factory inited..."
25
+
26
+ # ������� � ��������� ���������
27
+ msg = msgs.message :name => "FutAddOrder",
28
+ :dest_addr => server_address,
29
+ :field => {
30
+ "P2_Category" => "FORTS_MSG",
31
+ "P2_Type" => 1,
32
+ "isin" => "RTS-3.12",
33
+ :price => "155500",
34
+ :amount => 1,
35
+ "client_code" => "001",
36
+ "type" => 1,
37
+ "dir" => 1}
38
+
39
+ msg.DestAddr = server_address
40
+
41
+ puts "Msg created, Sending it..."
42
+
43
+ msg = msg.Send(conn, 5000) # ��������, ���� ������ � ������� 5000 �����������
44
+
45
+ puts msg.parse_reply #'CP866' #'CP1251'
46
+ end
@@ -0,0 +1,8 @@
1
+ require_relative 'script_helper'
2
+ require 'clients/simple_client'
3
+
4
+ # The main entry point for the application.
5
+ start_router do
6
+ client = P2::SimpleClient.new
7
+ client.run
8
+ end
@@ -0,0 +1,4 @@
1
+ # encoding: CP1251
2
+ require_relative 'script_helper'
3
+
4
+ start_router
@@ -0,0 +1,120 @@
1
+ require 'pp'
2
+ require 'win/time'
3
+ require 'clients/exception_wrapper'
4
+
5
+ # TODO: Change Client into an Interface module, force concrete classes to include it?
6
+ # Base (non-configureble!)client class, all configuration in-code, subclass it for your specific needs
7
+ module Clients
8
+
9
+ # Base (non-configureble!)client class, all configuration in-code, subclass it for your specific needs
10
+ class Client
11
+ # File path constants
12
+ # TODO: Set paths in calling script, instead of hardcoding here?
13
+ LOG_PATH = LOG_DIR + 'basic_client.log'
14
+ REV_PATH = DATA_DIR + 'BasicRevisions.txt'
15
+ APP_NAME = 'Client'
16
+
17
+ include Mix::ExceptionWrapper
18
+
19
+ attr_accessor :name, :conn, :logger, :streams, :outputs, :stopped
20
+
21
+ # Uniform access to table handlers via @client[:instruments] syntax
22
+ def [] key
23
+ send key
24
+ end
25
+
26
+ def initialize opts = {}
27
+ @name = opts[:name] || APP_NAME
28
+ @logger = opts[:logger] || STDOUT # File.new(LOG_PATH, "w") # System.Text.Encoding.Unicode)
29
+ @stop = false
30
+
31
+ begin
32
+ # Create Connection object with P2MQRouter connectivity parameters
33
+ @conn = P2::Connection.new :ini => CLIENT_INI,
34
+ :host => "localhost",
35
+ :port => 4001,
36
+ :AppName => @name
37
+ # Client will handle Connection's events by default
38
+ @conn.events.handler = self
39
+
40
+ @streams = {}
41
+ @outputs = []
42
+ # Run setup for client subclasses
43
+ setup opts
44
+
45
+ # Adding streams stats to outputs:
46
+ @outputs += @streams.map { |id, stream| [id, stream.stats] }.flatten
47
+
48
+ rescue WIN32OLERuntimeError => e
49
+ puts e
50
+ if P2.p2_error(e) == P2::P2ERR_INI_FILE_NOT_FOUND #Marshal.GetHRForException(e)
51
+ puts "Can't find one or both of ini file: P2ClientGate.ini, orders_aggr.ini"
52
+ end
53
+ raise e
54
+ rescue Exception => e #(System.Exception e)
55
+ puts "Raising non-Win32Ole error in initialize: #{e}"
56
+ raise e
57
+ end
58
+ end
59
+
60
+ # Override and set up @streams and @outputs here
61
+ # (as well as other artifacts specific to your client)
62
+ def setup opts = {}
63
+ end
64
+
65
+ # Main event cycle
66
+ def run
67
+ until @stop
68
+ try do
69
+ # (Re)-connecting to Router
70
+ @conn.Connect()
71
+
72
+ # Processing messages in a loop
73
+ try { process_messages until @stop }
74
+
75
+ # Make sure streams are closed and disconnect before reconnecting
76
+ disconnect
77
+ end
78
+ end
79
+ finalize
80
+ end
81
+
82
+ # Keep alive streams and process messages once
83
+ def process_messages
84
+ # Check status for all streams, reopen as necessary
85
+ @streams.each { |_, stream| try { stream.keep_alive } }
86
+
87
+ # Actual processing of incoming messages happens in event callbacks
88
+ # O����������� ��������� ��������� � ����������� ��������� ������
89
+ @conn.ProcessMessage2(100)
90
+ end
91
+
92
+ # First close streams, then disconnect connection
93
+ def disconnect
94
+ @streams.each { |_, stream| try { stream.finalize } }
95
+ @conn.Disconnect()
96
+ end
97
+
98
+ # Client's cleanup actions
99
+ def finalize
100
+ # Make sure this finalizer runs only once
101
+ unless @stopping
102
+ @stop = true
103
+ @stopping = true
104
+ disconnect
105
+
106
+ @outputs.each { |out| pp out }
107
+ @stopped = true
108
+ end
109
+ end
110
+
111
+ # Handling Connection status change
112
+ def onConnectionStatusChanged(conn, new_status)
113
+ puts :info, "MQ connection state " + @conn.status_text(new_status)
114
+
115
+ if ((new_status & P2::CS_ROUTER_CONNECTED) != 0)
116
+ # ����� ����������� - ����������� ����� �������-����������� ?
117
+ end
118
+ end
119
+ end # class Client
120
+ end # module Clients
@@ -0,0 +1,20 @@
1
+ # This file contains EventedDataStream class and its helper modules/classes
2
+
3
+ module Mix
4
+
5
+ # Provides #try method that nicely wraps WIN32OLE exception handling in host classes
6
+ module ExceptionWrapper
7
+ # Exception handling wrapper for Win32OLE exceptions.
8
+ # Catch/log Win32OLE exceptions, pass on all others...
9
+ def try
10
+ yield
11
+ rescue WIN32OLERuntimeError => e
12
+ puts :error, "Ignoring caught Win32ole runtime error:", e
13
+ sleep 0.1 # Give other Threads a chance to execute
14
+ rescue Exception => e
15
+ self.finalize if respond_to? :finalize
16
+ puts :error, "Raising non-Win32ole error:", e
17
+ raise e
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,83 @@
1
+ #!/usr/bin/env ruby
2
+ # encoding: Windows-1251
3
+ require 'p2ruby'
4
+
5
+ # This script replicates P2AddOrderConsole.cpp functionality (but for async Send)
6
+
7
+ module Clients
8
+ # Event processing classes
9
+ ######################################
10
+ class ConnectionEvents < P2::Connection
11
+ def initialize app_name
12
+ # Create Connection object
13
+ super :ini => CLIENT_INI, :app_name => app_name,
14
+ :host => "127.0.0.1", :port => 4001
15
+ self.events.handler = self
16
+ self.Connect()
17
+ end
18
+
19
+ # Define Handler for IP2ConnectionEvent event interface
20
+ def onConnectionStatusChanged(conn, new_status)
21
+ $log.puts "EVENT ConnectionStatusChanged: #{conn} - #{status_text new_status}"
22
+ end
23
+ end # class ConnectionEvents
24
+
25
+ #####################################
26
+ class DataStreamEvents < P2::DataStream
27
+ def initialize conn, short_name
28
+ # Create DataStream object
29
+ super :stream_name => "#{short_name}_REPL", :type => P2::RT_COMBINED_DYNAMIC #,
30
+ # :DBConnString => "P2DBSqLiteD.dll;;Log\\#{short_name}_.db"
31
+ self.events.handler = self
32
+ self.Open(conn)
33
+ end
34
+
35
+ def wrap(rec)
36
+ P2::Record.new :ole => rec
37
+ end
38
+
39
+ # Define Handlers for IP2DataStreamEvents event interface
40
+ def onStreamStateChanged(stream, new_state)
41
+ $log.puts "StreamStateChanged #{stream.StreamName} - #{state_text(new_state)}"
42
+ end
43
+
44
+ def onStreamDataInserted stream, table_name, rec
45
+ return unless table_name == 'sys_messages' # Single out one table events
46
+ $log.puts "StreamDataInserted #{stream.StreamName} - #{table_name}: #{wrap(rec)}"
47
+ end
48
+
49
+ def onStreamDataUpdated(stream, table_name, id, rec)
50
+ $log.puts "StreamDataUpdated #{stream.StreamName} - #{table_name} - #{id}: #{wrap(rec)}"
51
+ end
52
+
53
+ def onStreamDataDeleted(stream, table_name, id, rec)
54
+ $log.puts "StreamDataDeleted #{stream.StreamName} - #{table_name} - #{id}: #{wrap(rec)}"
55
+ end
56
+
57
+ def onStreamDatumDeleted(stream, table_name, rev)
58
+ $log.puts "StreamDatumDeleted #{stream.StreamName} - #{table_name} - #{rev}"
59
+ end
60
+
61
+ def onStreamDBWillBeDeleted(stream)
62
+ $log.puts "StreamDBWillBeDeleted #{stream.StreamName} "
63
+ end
64
+
65
+ def onStreamLifeNumChanged(stream, life_num)
66
+ $log.puts "StreamLifeNumChanged #{stream.StreamName} - #{life_num} "
67
+ end
68
+
69
+ def onStreamDataBegin(stream)
70
+ $log.puts "StreamDataBegin #{stream.StreamName} "
71
+ end
72
+
73
+ def onStreamDataEnd(stream)
74
+ $log.puts "StreamDataEnd #{stream.StreamName} "
75
+ end
76
+ end # class DataStreamEvents
77
+
78
+ end # module Clients
79
+
80
+ #####################################
81
+ #class CAsyncMessageEvent : Not used, Async Send event interfaces not implemented :(
82
+ #####################################
83
+ #class CAsyncSendEvent2 : Not used, Async Send event interfaces not implemented :(
@@ -0,0 +1,264 @@
1
+ require 'pp'
2
+ require 'p2ruby'
3
+
4
+ # Naive all-it-one Client implementation that handles all events internally
5
+ # and uses original P2 as its namespace
6
+
7
+ # File path constants
8
+ LOG_PATH = 'log\simple_client.log'
9
+ AGGR_PATH = 'data\SaveAggrRev.txt'
10
+ DEAL_PATH = 'data\SaveDeal.txt'
11
+
12
+ # Replication Stream parameters
13
+ AGGR_INI = INI_DIR + 'orders_aggr.ini'
14
+ DEAL_INI = INI_DIR + 'fut_trades.ini'
15
+ AGGR_ID = 'FORTS_FUTAGGR20_REPL'
16
+ DEAL_ID = 'FORTS_FUTTRADE_REPL'
17
+
18
+ ## Global functions
19
+
20
+ # Normal log (STDOUT)
21
+ def log
22
+ STDOUT
23
+ # @log_file ||= File.new(LOG_PATH, "w") # System.Text.Encoding.Unicode)
24
+ end
25
+
26
+ # Error log (for unexpected events/exceptions)
27
+ def error_log
28
+ @error_log_file ||= File.new(LOG_PATH, "w") # System.Text.Encoding.Unicode)
29
+ end
30
+
31
+ # Extensions to P2Ruby library classes
32
+ module P2
33
+ # Reopening P2::DataStream class to hack in #keep_alive method.
34
+ class DataStream < Base
35
+ # (Re)-opens stale data stream, optionally resetting table revisions of its TableSet
36
+ def keep_alive(conn, revisions={})
37
+ if closed? || error?
38
+ self.Close() if error?
39
+ revisions.each { |table, rev| self.TableSet.Rev[table.to_s] = rev } if self.TableSet
40
+ self.Open(conn)
41
+ end
42
+ end
43
+ end
44
+ end
45
+
46
+ module P2 # P2SimpleGate2Client
47
+
48
+ # Event proxy that collects event statistics
49
+ class Stats
50
+ def initialize real_handler
51
+ @real_handler = real_handler
52
+ @stats = {}
53
+
54
+ # Mock all event processing methods of real event handler
55
+ @real_handler.methods.select { |m| m =~/^on/ }.each do |method|
56
+ self.define_singleton_method(method) do |stream, key, *args|
57
+ @stats[method] ||= Hash.new(0)
58
+ @stats[method][key] += 1
59
+ @real_handler.send method, stream, key, *args
60
+ end
61
+ end
62
+ end
63
+
64
+ def inspect
65
+ @stats
66
+ end
67
+
68
+ def to_s
69
+ @stats
70
+ end
71
+ end
72
+
73
+ # Simple all-it-one client that handles all events internally
74
+ class SimpleClient
75
+
76
+ attr_accessor :stats
77
+
78
+ def initialize
79
+ @stop = false
80
+
81
+ begin
82
+ # Create Connection object with P2MQRouter connectivity parameters
83
+ @conn = P2::Connection.new :ini => CLIENT_INI,
84
+ :host => "localhost",
85
+ :port => 4001,
86
+ :AppName => "p2ruby_baseless"
87
+
88
+ # Load previous table revisions of data streams
89
+ @current_rev = {"orders_aggr" => load_rev(AGGR_PATH),
90
+ "deal" => load_rev(DEAL_PATH)}
91
+
92
+ # Open files for writing received data (and tracking table revisions)
93
+ @aggr_file ||= File.new(AGGR_PATH, "w") # System.Text.Encoding.Unicode)
94
+ @deal_file ||= File.new(DEAL_PATH, "w") # System.Text.Encoding.Unicode)
95
+
96
+ # Initialize TableSets with scheme and revision data
97
+ deal_tables = P2::TableSet.new :ini => DEAL_INI,
98
+ :rev => {"deal" => @current_rev["deal"] + 1}
99
+ aggr_tables = P2::TableSet.new :ini => AGGR_INI,
100
+ :rev => {"orders_aggr" =>
101
+ @current_rev["orders_aggr"] + 1}
102
+
103
+ # Create "replication data stream" object for aggregated orders info
104
+ @aggr_stream = P2::DataStream.new :type => P2::RT_COMBINED_DYNAMIC,
105
+ :name => AGGR_ID,
106
+ :TableSet => aggr_tables
107
+
108
+ # Create "replication data stream" object for incoming trades/deals info
109
+ @deal_stream = P2::DataStream.new :type => P2::RT_COMBINED_DYNAMIC,
110
+ :name => DEAL_ID,
111
+ :TableSet => deal_tables
112
+ # @deal_stream.TableSet.InitFromIni2("forts_scheme.ini", "FutTrade")
113
+
114
+ # Create Stats objects that collect event statistics
115
+ @stats = {AGGR_ID => Stats.new(self),
116
+ DEAL_ID => Stats.new(self)}
117
+
118
+ # Register event handlers for Connection and Data Stream events
119
+ @conn.events.handler = self
120
+ @aggr_stream.events.handler = @stats[AGGR_ID] # self
121
+ @deal_stream.events.handler = @stats[DEAL_ID] # self
122
+
123
+ rescue WIN32OLERuntimeError => e
124
+ log.puts e
125
+ if P2.p2_error(e) == P2::P2ERR_INI_PATH_NOT_FOUND #Marshal.GetHRForException(e)
126
+ error_log.puts "Can't find one or both of ini file: P2ClientGate.ini, orders_aggr.ini"
127
+ end
128
+ raise e
129
+ rescue Exception => e #(System.Exception e)
130
+ log.puts "Raising non-Win32Ole error in initialize:", e
131
+ raise e
132
+ end
133
+ end
134
+
135
+ # Exception handling wrapper for Win32OLE exceptions.
136
+ # Catch/log Win32OLE exceptions, pass on all others...
137
+ def try
138
+ begin
139
+ yield
140
+ rescue WIN32OLERuntimeError => e #(System.Runtime.InteropServices.COMException e)
141
+ log.puts "Ignoring caught Win32Ole runtime error:", e
142
+ rescue Exception => e #(System.Exception e)
143
+ pp @stats
144
+ log.puts "Raising non-Win32Ole error:", e
145
+ raise e
146
+ end
147
+ end
148
+
149
+ # Main event cycle
150
+ def run
151
+ until @stop
152
+ try do
153
+ # (Re)-connecting to Router
154
+ @conn.Connect()
155
+ try do
156
+ until @stop
157
+ try do
158
+ @aggr_stream.keep_alive @conn, :quotes => @current_rev["orders_aggr"] + 1
159
+ @deal_stream.keep_alive @conn, :deal => @current_rev["deal"] + 1
160
+ end
161
+ # Actual processing of incoming messages happens in event callbacks
162
+ # O����������� ��������� ��������� � ����������� ��������� ������
163
+ @conn.ProcessMessage2(100)
164
+ end
165
+ end
166
+
167
+ try { @aggr_stream.Close() } unless @aggr_stream.closed?
168
+ try { @deal_stream.Close() } unless @deal_stream.closed?
169
+
170
+ @conn.Disconnect()
171
+ end
172
+ end
173
+ end
174
+
175
+ # Handling Connection status change
176
+ def onConnectionStatusChanged(conn, new_status)
177
+ state = "MQ connection state " + @conn.status_text(new_status)
178
+
179
+ if ((new_status & P2::CS_ROUTER_CONNECTED) != 0)
180
+ # ����� ����������� - ����������� ����� �������-�����������
181
+ end
182
+ log.puts(state)
183
+ end
184
+
185
+ # Handling replication Data Stream status change
186
+ def onStreamStateChanged(stream, new_state)
187
+ state = "Stream #{stream.StreamName} state: #{@deal_stream.state_text(new_state)}"
188
+ case new_state
189
+ when P2::DS_STATE_CLOSE, P2::DS_STATE_ERROR
190
+ # @opened = false
191
+ end
192
+ log.puts(state)
193
+ end
194
+
195
+ # Insert record
196
+ def onStreamDataInserted(stream, table_name, rec)
197
+ # Interrupt inside event hook bubbles up instead of being caught in main loop...
198
+ try do
199
+ log.puts "Stream #{stream.StreamName} inserts into #{table_name} "
200
+
201
+ if stream.StreamName == AGGR_ID
202
+ # This is FORTS_FUTAGGR20_REPL stream event
203
+ save_aggr(rec, table_name, stream)
204
+
205
+ elsif stream.StreamName == DEAL_ID && table_name == 'deal'
206
+ # This is FORTS_FUTTRADE_REPL stream event
207
+ # !!!! Saving only records from 'deal' table, not heartbeat or multileg_deal
208
+ save_data(rec, table_name, @deal_file, stream, "\n", '')
209
+ end
210
+ end
211
+ end
212
+
213
+ # Delete record
214
+ def onStreamDataDeleted(stream, table_name, id, rec)
215
+ log.puts "Stream #{stream.StreamName} deletes #{id} from #{table_name} "
216
+ if stream.StreamName == AGGR_ID
217
+ save_aggr(rec, table_name, stream)
218
+ else
219
+ error_log.puts 'Unexpected onStreamDataDeleted event'
220
+ end
221
+ end
222
+
223
+ # Stream LifeNum change
224
+ def onStreamLifeNumChanged(stream, life_num)
225
+ if (stream.StreamName == AGGR_ID)
226
+ @aggr_stream.TableSet.LifeNum = life_num
227
+ @aggr_stream.TableSet.SetLifeNumToIni(AGGR_INI)
228
+ end
229
+ if (stream.StreamName == DEAL_ID)
230
+ @deal_stream.TableSet.LifeNum = life_num
231
+ @deal_stream.TableSet.SetLifeNumToIni(DEAL_INI)
232
+ end
233
+ end
234
+
235
+ # Load latest revision from file with given path,
236
+ # return 0 if no file or saved revision available
237
+ def load_rev file_path
238
+ if File.exists? file_path
239
+ File.open(file_path) do |file|
240
+ line = file.readlines.select { |l| l =~ /^replRev/ }.last
241
+ rev = line ? line.split('=')[1] : nil
242
+ rev.chomp.to_i if rev
243
+ end
244
+ end || 0
245
+ end
246
+
247
+ # Save/log aggregate orders record
248
+ def save_aggr(rec, table_name, stream)
249
+ save_data(rec, table_name, log, stream)
250
+ @aggr_file.puts "replRev=#{@current_rev['orders_aggr']}"
251
+ @aggr_file.flush
252
+ end
253
+
254
+ # Save/log given record data
255
+ def save_data(rec, table_name, file, stream, divider = '; ', finalizer = nil)
256
+ @current_rev[table_name] = rec.GetValAsLongByIndex(1)
257
+
258
+ fields = stream.TableSet.FieldList(table_name) #"deal"]
259
+ file.puts fields.split(',').map { |f| "#{f}=#{rec.GetValAsString(f)}" }.join divider
260
+ file.puts(finalizer) if finalizer
261
+ file.flush
262
+ end
263
+ end
264
+ end
metadata CHANGED
@@ -2,7 +2,7 @@
2
2
  name: p2ruby
3
3
  version: !ruby/object:Gem::Version
4
4
  prerelease:
5
- version: 0.1.4
5
+ version: 0.1.5
6
6
  platform: ruby
7
7
  authors:
8
8
  - arvicco
@@ -39,6 +39,11 @@ description: Ruby bindings and wrapper classes for P2ClientGate
39
39
  email: arvitallian@gmail.com
40
40
  executables:
41
41
  - olegen.rb
42
+ - order_console
43
+ - script_helper.rb
44
+ - send_once
45
+ - simple_client
46
+ - start_router
42
47
  extensions: []
43
48
 
44
49
  extra_rdoc_files:
@@ -47,6 +52,15 @@ extra_rdoc_files:
47
52
  - README.rdoc
48
53
  files:
49
54
  - bin/olegen.rb
55
+ - bin/order_console
56
+ - bin/script_helper.rb
57
+ - bin/send_once
58
+ - bin/simple_client
59
+ - bin/start_router
60
+ - lib/clients/client.rb
61
+ - lib/clients/exception_wrapper.rb
62
+ - lib/clients/order_console.rb
63
+ - lib/clients/simple_client.rb
50
64
  - lib/extension.rb
51
65
  - lib/ole20110223-013209.rb
52
66
  - lib/p2ruby/application.rb