p2ruby 0.1.4 → 0.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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