s41c 0.0.1 → 0.0.2

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/LICENSE.BSD ADDED
@@ -0,0 +1,24 @@
1
+ Copyright (c) 2012 DansingBytes.ru
2
+ All rights reserved.
3
+
4
+ Author: redfield (up.redfield@gmail.com)
5
+
6
+ Redistribution and use in source and binary forms, with or without
7
+ modification, are permitted provided that the following conditions are met:
8
+
9
+ * Redistributions of source code must retain the above copyright
10
+ notice, this list of conditions and the following disclaimer.
11
+ * Redistributions in binary form must reproduce the above copyright
12
+ notice, this list of conditions and the following disclaimer in the
13
+ documentation and/or other materials provided with the distribution.
14
+
15
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
16
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18
+ ARE DISCLAIMED. IN NO EVENT SHALL DansingBytes BE LIABLE FOR ANY
19
+ DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21
+ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
22
+ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
data/README.md ADDED
@@ -0,0 +1,75 @@
1
+ S41C (Socket for 1C)
2
+ ======
3
+
4
+
5
+ TCP-socket-сервер и клиент для платформы "1С:Предприятие"
6
+
7
+ ### Системные требования
8
+
9
+ * Microsoft Windows семейства NT (32bit)
10
+ * 1С:Предприятие v7
11
+ * [Ruby](http://rubyinstaller.org/downloads/) 1.9.3
12
+
13
+ ### Установка
14
+
15
+ gem install s41c
16
+
17
+ ### Пример использования
18
+
19
+ Запустить сервер для 1C:Предприятия 7.7 (сетевая версия) на localhost:1421
20
+
21
+ require 's41c'
22
+
23
+ server = S41C::Server.new
24
+ server.start
25
+
26
+ Или чуть сложнее: запустить сервер для 1С:Предприятия (локальная версия) на
27
+ 127.0.0.1:2000, писать лог в c:\server.log и записать время остановки
28
+ сервера в файл
29
+
30
+ require 's41c'
31
+
32
+ server = S41C::Server.new('127.0.0.1', 2000, 'c:\server.log')
33
+ server.ole_object = 'V77L.Application'
34
+
35
+ server.at_exit do
36
+ File.open('c:\server_stoped_at.txt', 'w') { |f| f.puts Time.now }
37
+ end
38
+
39
+ server.start
40
+
41
+ После запуска с сервером можно общаться по telnet. Комманды, содержащие не-ASCII
42
+ символы, должны быть utf8-строками переведенными в бинарный формат. В руби это
43
+ можно сделать с помощью метода [String#force_encoding](http://ruby-doc.org/core-1.9.3/String.html#method-i-force_encoding):
44
+
45
+ "utf8-строка".force_encoding("BINARY")
46
+ => "utf8-\xD1\x81\xD1\x82\xD1\x80\xD0\xBE\xD0\xBA\xD0\xB0"
47
+
48
+ Отвечает сервер в том же формате, т.е. на стороне клиента ответ нужно
49
+ преобразовать в utf-строку:
50
+
51
+ "utf8-\xD1\x81\xD1\x82\xD1\x80\xD0\xBE\xD0\xBA\xD0\xB0".force_encoding("UTF-8")
52
+ => "utf8-строка"
53
+
54
+ Список команд:
55
+
56
+ * 'connect|параметры_подключения' - подключиться к базе, например:
57
+
58
+ 'connect|/d c:\1c\\'
59
+
60
+ * 'eval_expr|команда' - выполнить команду
61
+
62
+ 'eval_expr|ОсновнойЯзык()'
63
+
64
+ * 'create|Название.Объекта' - создать объект
65
+
66
+ 'create|Справочник.Товары'
67
+
68
+ * 'invoke|НазваниеМетода|параметры|метода' - выполнить процедуру/функцию для объекта
69
+
70
+ 'invoke|НайтиПоНаименованию|Шляпа с полями'
71
+
72
+ * 'disconnect' - отключиться от 1С
73
+ * 'shutdown' - остановить сервер
74
+
75
+
data/lib/s41c.rb CHANGED
@@ -1,3 +1,4 @@
1
+ require 'socket'
1
2
  require 's41c/version'
2
3
  require 's41c/utils'
3
4
  require 's41c/server'
data/lib/s41c/client.rb CHANGED
@@ -0,0 +1,97 @@
1
+ # encoding: utf-8
2
+
3
+ module S41C
4
+
5
+ class Client
6
+
7
+ def initialize(host='localhost', port=1421)
8
+ require 'net/telnet'
9
+
10
+ @host, @port = host, port
11
+ @prompt = /^\+OK/n
12
+ @errors = []
13
+
14
+ end # initialize
15
+
16
+ def login(username, password = nil)
17
+ @login = username.nil? || username.empty? ? nil : username
18
+ @password = password
19
+ end # login
20
+
21
+ def errors
22
+ @errors
23
+ end # errors
24
+
25
+ def connect(options)
26
+ cmd "connect|#{options}"
27
+ end # connect
28
+
29
+ def ping
30
+ cmd "ping"
31
+ end # ping
32
+
33
+ def eval_expr(expr)
34
+ cmd "eval_expr|#{expr}"
35
+ end # eval_expr
36
+
37
+ def create(obj_name)
38
+ cmd "create|#{obj_name}"
39
+ end # create
40
+
41
+ def invoke(method_name, *args)
42
+ cmd "invoke|#{method_name}|#{args.join('|')}"
43
+ end # invoke
44
+
45
+ def disconnect
46
+ cmd "disconnect"
47
+ end # disconnect
48
+
49
+ def shutdown
50
+ cmd "shutdown"
51
+ end # shutdown
52
+
53
+ private
54
+
55
+ def conn
56
+ return true if @client
57
+ begin
58
+
59
+ @client = Net::Telnet.new('Host' => @host, 'Port' => @port, "Prompt" => @prompt)
60
+
61
+ if @login
62
+ resp = @client.login(@login, @password)
63
+
64
+ unless resp["success"]
65
+ @errors << "Invalid login or password"
66
+ return false
67
+ end # unless
68
+ end # if
69
+
70
+ return true
71
+
72
+ rescue Errno::ECONNREFUSED => e
73
+ @errors << e.message
74
+ return false
75
+ end
76
+ end # conn
77
+
78
+ def cmd(str)
79
+ return @errors unless conn
80
+ parse @client.cmd(S41C::Utils.to_bin(str))
81
+ end # cmd
82
+
83
+ def parse(response)
84
+ resp = S41C::Utils.to_utf8(response)
85
+ res = []
86
+
87
+ resp.each_line do |line|
88
+ res << line.chomp unless line[@prompt]
89
+ end # each_line
90
+
91
+ res.count > 1 ? res : res.first
92
+ end # parse
93
+
94
+ end # Client
95
+
96
+ end # S41C
97
+
data/lib/s41c/server.rb CHANGED
@@ -2,17 +2,21 @@
2
2
 
3
3
  module S41C
4
4
 
5
- require 'win32ole'
6
- require 'socket'
7
-
8
5
  class Server
9
6
 
10
7
  def initialize(host='localhost', port=1421, log_file=nil)
8
+ require 'win32ole'
9
+
11
10
  @host, @port = host, port
12
11
  @logger = log_file ? ::STDOUT.reopen(log_file, 'a') : ::STDOUT
13
12
  @ole_object = 'V77.Application'
14
13
  end # initialize
15
14
 
15
+ def set_login(username, password)
16
+ @login = username
17
+ @password = password
18
+ end # login_info
19
+
16
20
  def ole_object=(name)
17
21
  @ole_object = name
18
22
  end # ole_object
@@ -37,60 +41,29 @@ module S41C
37
41
  log "*** Server has been started on #{@host}:#{@port}"
38
42
  log "*** Ctrl+C for stopping"
39
43
 
40
- loop do
41
-
42
- begin
43
- session = server.accept_nonblock
44
- rescue IO::WaitReadable, Errno::EINTR
45
- IO.select([server])
46
- retry
47
- end
48
-
49
- session.print "Welcome\r\n"
50
-
51
- loop {
52
- attrs = S41C::Utils.to_utf8(session.gets || '').chomp.split('|')
53
- cmd = attrs.shift
54
- case cmd
55
- when "connect"
56
- session.puts S41C::Utils.to_bin(connect_to_1c(attrs))
57
- session.puts "+OK"
58
- when "create"
59
- session.puts S41C::Utils.to_bin(create(attrs))
60
- session.puts "+OK"
61
- when "eval_expr"
62
- session.puts S41C::Utils.to_bin(eval_expr(attrs))
63
- session.puts "+OK"
64
- when "invoke"
65
- session.puts S41C::Utils.to_bin(invoke(attrs))
66
- session.puts "+OK"
67
- when "disconnect"
68
- session.puts "Goodbye"
69
- session.close
70
- break
71
- when "shutdown"
72
- session.puts "Server is going down now"
73
- session.close
74
- exit
75
- else
76
- session.puts "Bad command"
77
- session.puts "+OK"
78
- end
79
- }
80
-
44
+ begin
45
+ main_loop(server)
46
+ rescue Errno::ECONNABORTED
47
+ log "*** [#{Time.now}] Session aborted"
48
+ retry
81
49
  end
50
+
82
51
  end # start
83
52
 
84
53
  private
85
54
 
55
+ def read_response(session)
56
+ S41C::Utils.to_utf8(session.gets || '').chomp
57
+ end # read_response
58
+
86
59
  def log(msg)
87
60
  @logger.puts msg
88
61
  @logger.flush
89
62
  end # log
90
63
 
91
- def connect_to_1c(attrs)
64
+ def connect_to_1c(args)
92
65
  @conn.ole_free unless @conn.nil?
93
- options = attrs.shift || ''
66
+ options = args.shift || ''
94
67
  begin
95
68
  @conn = ::WIN32OLE.new('V77.Application')
96
69
  res = @conn.Initialize(
@@ -105,9 +78,9 @@ module S41C
105
78
  end
106
79
  end
107
80
 
108
- def create(attrs)
81
+ def create(args)
109
82
  return "Error: not connected" unless @conn
110
- obj_name = attrs.shift || ''
83
+ obj_name = args.shift || ''
111
84
  begin
112
85
  @obj = @conn.CreateObject(obj_name)
113
86
  "Created"
@@ -116,9 +89,9 @@ module S41C
116
89
  end
117
90
  end
118
91
 
119
- def eval_expr(attrs)
92
+ def eval_expr(args)
120
93
  return "Error: not connected" unless @conn
121
- expr = attrs.shift || ''
94
+ expr = args.shift || ''
122
95
  begin
123
96
  @conn.invoke("EvalExpr", expr).to_s
124
97
  rescue => e
@@ -126,15 +99,80 @@ module S41C
126
99
  end
127
100
  end
128
101
 
129
- def invoke(attrs)
102
+ def invoke(args)
130
103
  return "Error: working object not found. You must create it before" unless @conn
131
104
  begin
132
- @obj.invoke(*attrs).to_s.encode("UTF-8", "IBM866", :invalid => :replace, :replace => "?")
105
+ @obj.invoke(*args).to_s.encode("UTF-8", "IBM866", :invalid => :replace, :replace => "?")
133
106
  rescue => e
134
107
  "Error: #{e.message}"
135
108
  end
136
109
  end
137
110
 
111
+ def main_loop(server)
112
+ loop do
113
+
114
+ begin
115
+ session = server.accept_nonblock
116
+ rescue IO::WaitReadable, Errno::EINTR
117
+ IO.select([server])
118
+ retry
119
+ end
120
+
121
+ if @login
122
+ res = true
123
+ session.print "login"
124
+ res = res && (read_response(session) == @login)
125
+ if @password
126
+ session.print "password"
127
+ res = res && (read_response(session) == @password)
128
+ end
129
+
130
+ if res
131
+ session.puts("success")
132
+ session.puts("+OK")
133
+ else
134
+ session.puts "\n\rInvalid login or password"
135
+ session.close
136
+ next
137
+ end # if
138
+ end
139
+
140
+ loop {
141
+ args = S41C::Utils.to_utf8(session.gets || '').chomp.split('|')
142
+ cmd = args.shift
143
+ case cmd
144
+ when "connect"
145
+ session.puts S41C::Utils.to_bin(connect_to_1c(args))
146
+ session.puts "+OK"
147
+ when "create"
148
+ session.puts S41C::Utils.to_bin(create(args))
149
+ session.puts "+OK"
150
+ when "eval_expr"
151
+ session.puts S41C::Utils.to_bin(eval_expr(args))
152
+ session.puts "+OK"
153
+ when "invoke"
154
+ session.puts S41C::Utils.to_bin(invoke(args))
155
+ session.puts "+OK"
156
+ when "ping"
157
+ session.puts "pong"
158
+ session.puts "+OK"
159
+ when "disconnect"
160
+ session.puts "Goodbye"
161
+ session.close
162
+ break
163
+ when "shutdown"
164
+ session.puts "Server is going down now"
165
+ session.close
166
+ exit
167
+ else
168
+ session.puts "Bad command"
169
+ session.puts "+OK"
170
+ end
171
+ }
172
+
173
+ end
174
+ end # main_loop
175
+
138
176
  end # Server
139
177
 
140
178
  end # S41C
data/lib/s41c/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  module S41C
2
2
 
3
- VERSION = '0.0.1'
3
+ VERSION = '0.0.2'
4
4
 
5
5
  end # S41C
data/s41c-0.0.1.gem ADDED
Binary file
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: s41c
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-03-17 00:00:00.000000000 Z
12
+ date: 2012-03-19 00:00:00.000000000 Z
13
13
  dependencies: []
14
14
  description: TCP-socket сервер и клиент для платформы "1С:Предприятие"
15
15
  email:
@@ -18,8 +18,10 @@ executables: []
18
18
  extensions: []
19
19
  extra_rdoc_files: []
20
20
  files:
21
+ - LICENSE.BSD
21
22
  - s41c.gemspec
22
23
  - s41c-0.0.1.gem
24
+ - README.md
23
25
  - lib/s41c/version.rb
24
26
  - lib/s41c/utils.rb
25
27
  - lib/s41c/server.rb