s41c 0.0.2 → 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -16,21 +16,29 @@ TCP-socket-сервер и клиент для платформы "1С:Пред
16
16
 
17
17
  ### Пример использования
18
18
 
19
- Запустить сервер для 1C:Предприятия 7.7 (сетевая версия) на localhost:1421
19
+ #### Сервер
20
+
21
+ Запустить сервер для 1C:Предприятия 7.7 (сетевая версия) на localhost:1421
22
+ и использовать базу "C:\Base\"
20
23
 
21
24
  require 's41c'
22
25
 
23
26
  server = S41C::Server.new
27
+ server.db('C:\Base\\')
24
28
  server.start
25
29
 
26
30
  Или чуть сложнее: запустить сервер для 1С:Предприятия (локальная версия) на
27
- 127.0.0.1:2000, писать лог в c:\server.log и записать время остановки
28
- сервера в файл
31
+ 127.0.0.1:2000, использовать базу "C:\Base\", при подключении требовать логин
32
+ "username" и пароль "password", писать лог в c:\server.log и записать время
33
+ остановки сервера в файл
29
34
 
30
35
  require 's41c'
31
36
 
32
37
  server = S41C::Server.new('127.0.0.1', 2000, 'c:\server.log')
33
- server.ole_object = 'V77L.Application'
38
+ server.ole_name = 'V77L.Application'
39
+ server.db('C:\Base\\')
40
+
41
+ server.login('username', 'password')
34
42
 
35
43
  server.at_exit do
36
44
  File.open('c:\server_stoped_at.txt', 'w') { |f| f.puts Time.now }
@@ -38,38 +46,56 @@ TCP-socket-сервер и клиент для платформы "1С:Пред
38
46
 
39
47
  server.start
40
48
 
41
- После запуска с сервером можно общаться по telnet. Комманды, содержащие не-ASCII
42
- символы, должны быть utf8-строками переведенными в бинарный формат. В руби это
43
- можно сделать с помощью метода [String#force_encoding](http://ruby-doc.org/core-1.9.3/String.html#method-i-force_encoding):
49
+ #### Клиент
50
+
51
+ При подключении можно указать хост и порт сервера (если не указаны, будут
52
+ использоваться localhost:1421), а также данные для авторизации. После
53
+ подключения на сервер передаётся руби-код в виде строки, который и выполняет все,
54
+ что нам нужно
55
+
56
+ require 's41c'
57
+
58
+ client = S41C::Client.new('127.0.0.1', 2000)
59
+ client.login('username', 'password')
60
+
61
+ response = client.eval %{
44
62
 
45
- "utf8-строка".force_encoding("BINARY")
46
- => "utf8-\xD1\x81\xD1\x82\xD1\x80\xD0\xBE\xD0\xBA\xD0\xB0"
63
+ spr = @ole.CreateObject('Справочник.Товары')
47
64
 
48
- Отвечает сервер в том же формате, т.е. на стороне клиента ответ нужно
49
- преобразовать в utf-строку:
65
+ if spr.invoke('НайтиПоНаименованию', 'Название товара') == 1.0
66
+ item = spr.invoke('ТекущийЭлемент')
67
+ item_id = get_1c_id(item)
68
+ expr = 'КакаяНибудьФункция(ЗначениеИзСтрокиВнутр(' + item_id + '))'
69
+ result = @ole.EvalExpr(expr)
70
+ end
50
71
 
51
- "utf8-\xD1\x81\xD1\x82\xD1\x80\xD0\xBE\xD0\xBA\xD0\xB0".force_encoding("UTF-8")
52
- => "utf8-строка"
72
+ to_utf8(result)
53
73
 
54
- Список команд:
74
+ }
55
75
 
56
- * 'connect|параметры_подключения' - подключиться к базе, например:
57
-
58
- 'connect|/d c:\1c\\'
76
+ puts response
77
+ client.disconnect
59
78
 
60
- * 'eval_expr|команда' - выполнить команду
61
-
62
- 'eval_expr|ОсновнойЯзык()'
79
+ Внутри кода доступен объект @ole, который и является инстансом 1С. У него есть
80
+ 3 метода
63
81
 
64
- * 'create|Название.Объекта' - создать объект
82
+ * CreateObject - создает объект агрегатного типа данных 1С:Предприятия и
83
+ возвращает ссылку на него.
65
84
 
66
- 'create|Справочник.Товары'
85
+ * EvalExpr - вычислияет выражение системы 1С:Предприятие и возвращает результат
86
+ вычисления
67
87
 
68
- * 'invoke|НазваниеМетода|параметры|метода' - выполнить процедуру/функцию для объекта
88
+ * ExecuteBatch - выполняет последовательность операторов системы
89
+ 1С:Предприятие и возвращает статус выполнения (true/false)
69
90
 
70
- 'invoke|НайтиПоНаименованию|Шляпа с полями'
91
+ Вызов функций и процедур созданных объектов осуществляется с помощью метода
92
+ 'invoke', которому в качестве первого аргумента передаётся название
93
+ функции/процедуры, а последующих - параметры.
71
94
 
72
- * 'disconnect' - отключиться от 1С
73
- * 'shutdown' - остановить сервер
95
+ Также достпен ряд вспомогательных функций
74
96
 
97
+ * to_bin - переводит utf-строку в бинарный формат
98
+ * to_utf8 - переводит бинарную или строку из 1С в utf-8
99
+ * get_1c_id - обертка над 1С-функцией 'ЗначениеВСтрокуВнутр', возращает
100
+ строковый идентификатор объекта.
75
101
 
data/lib/s41c/client.rb CHANGED
@@ -4,6 +4,8 @@ module S41C
4
4
 
5
5
  class Client
6
6
 
7
+ include S41C::Utils
8
+
7
9
  def initialize(host='localhost', port=1421)
8
10
  require 'net/telnet'
9
11
 
@@ -16,6 +18,8 @@ module S41C
16
18
  def login(username, password = nil)
17
19
  @login = username.nil? || username.empty? ? nil : username
18
20
  @password = password
21
+
22
+ self
19
23
  end # login
20
24
 
21
25
  def errors
@@ -23,25 +27,13 @@ module S41C
23
27
  end # errors
24
28
 
25
29
  def connect(options)
26
- cmd "connect|#{options}"
30
+ cmd "connect\0#{options}"
27
31
  end # connect
28
32
 
29
33
  def ping
30
34
  cmd "ping"
31
35
  end # ping
32
36
 
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
37
  def disconnect
46
38
  cmd "disconnect"
47
39
  end # disconnect
@@ -50,6 +42,10 @@ module S41C
50
42
  cmd "shutdown"
51
43
  end # shutdown
52
44
 
45
+ def eval(code)
46
+ cmd "eval\0\n#{code}\nend_of_code"
47
+ end
48
+
53
49
  private
54
50
 
55
51
  def conn
@@ -77,21 +73,14 @@ module S41C
77
73
 
78
74
  def cmd(str)
79
75
  return @errors unless conn
80
- parse @client.cmd(S41C::Utils.to_bin(str))
76
+ parse @client.cmd(to_bin(str))
81
77
  end # cmd
82
78
 
83
79
  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
80
+ resp = to_utf8(response)
81
+ resp.lines.first.chomp
92
82
  end # parse
93
83
 
94
84
  end # Client
95
85
 
96
86
  end # S41C
97
-
@@ -0,0 +1,32 @@
1
+ # encoding: utf-8
2
+
3
+ module S41C
4
+
5
+ class Sandbox
6
+
7
+ include S41C::Utils
8
+
9
+ def initialize(ole, code)
10
+ @ole = ole
11
+ code.untaint
12
+ @code = proc {
13
+ $SAFE = 3
14
+ instance_eval code, __FILE__, __LINE__
15
+ }
16
+ end
17
+
18
+ def eval_code
19
+ begin
20
+ @code.call
21
+ rescue WIN32OLERuntimeError => e
22
+ "Error: #{to_utf8(e.message)}"
23
+ rescue Exception => e
24
+ "Error: #{e.message}"
25
+ rescue => e
26
+ "Error: #{e.message} from #{__FILE__}:#{__LINE__}"
27
+ end
28
+ end
29
+
30
+ end
31
+
32
+ end # S41C
data/lib/s41c/server.rb CHANGED
@@ -4,27 +4,45 @@ module S41C
4
4
 
5
5
  class Server
6
6
 
7
+ include S41C::Utils
8
+
7
9
  def initialize(host='localhost', port=1421, log_file=nil)
10
+ require 'socket'
8
11
  require 'win32ole'
9
12
 
10
13
  @host, @port = host, port
11
14
  @logger = log_file ? ::STDOUT.reopen(log_file, 'a') : ::STDOUT
12
- @ole_object = 'V77.Application'
15
+ @ole_name = 'V77.Application'
16
+
13
17
  end # initialize
14
18
 
15
- def set_login(username, password)
19
+ def login(username, password)
16
20
  @login = username
17
21
  @password = password
18
- end # login_info
19
22
 
20
- def ole_object=(name)
21
- @ole_object = name
22
- end # ole_object
23
+ self
24
+ end # login
25
+
26
+ def db(database, user=nil, password=nil)
27
+ @conn_options = "/d #{database}"
28
+ @conn_options << " /n #{user}" if user
29
+ @conn_options << " /p #{password}" if password
30
+
31
+ self
32
+ end # db
33
+
34
+ def ole_name=(name)
35
+ @ole_name = name
36
+
37
+ self
38
+ end # ole_name=
23
39
 
24
40
  def at_exit(&block)
25
41
  ::Kernel::at_exit do
26
42
  yield
27
43
  end
44
+
45
+ self
28
46
  end # at_exit
29
47
 
30
48
  def start
@@ -36,6 +54,8 @@ module S41C
36
54
  }
37
55
  end
38
56
 
57
+ connect_to_1c
58
+
39
59
  server = TCPServer.open(@host, @port)
40
60
 
41
61
  log "*** Server has been started on #{@host}:#{@port}"
@@ -53,7 +73,7 @@ module S41C
53
73
  private
54
74
 
55
75
  def read_response(session)
56
- S41C::Utils.to_utf8(session.gets || '').chomp
76
+ to_utf8(session.gets || '').chomp
57
77
  end # read_response
58
78
 
59
79
  def log(msg)
@@ -61,52 +81,27 @@ module S41C
61
81
  @logger.flush
62
82
  end # log
63
83
 
64
- def connect_to_1c(args)
65
- @conn.ole_free unless @conn.nil?
66
- options = args.shift || ''
84
+ def connect_to_1c
67
85
  begin
68
- @conn = ::WIN32OLE.new('V77.Application')
69
- res = @conn.Initialize(
70
- @conn.RMTrade,
71
- options,
86
+ @ole = ::WIN32OLE.new(@ole_name)
87
+ @conn = @ole.Initialize(
88
+ @ole.RMTrade,
89
+ (@conn_options),
72
90
  ''
73
91
  )
74
- "Connected"
92
+ rescue WIN32OLERuntimeError => e
93
+ @conn = nil
94
+ log "*** Error: #{to_utf8(e.message)}"
75
95
  rescue => e
76
- @conn.ole_free
77
- "Error: #{e.message}"
96
+ log "*** Error: #{e.message} from #{__FILE__}:#{__LINE__}"
78
97
  end
79
98
  end
80
99
 
81
- def create(args)
100
+ def eval_code(code)
82
101
  return "Error: not connected" unless @conn
83
- obj_name = args.shift || ''
84
- begin
85
- @obj = @conn.CreateObject(obj_name)
86
- "Created"
87
- rescue => e
88
- "Error: #{e.message}"
89
- end
90
- end
91
102
 
92
- def eval_expr(args)
93
- return "Error: not connected" unless @conn
94
- expr = args.shift || ''
95
- begin
96
- @conn.invoke("EvalExpr", expr).to_s
97
- rescue => e
98
- "Error: #{e.message}"
99
- end
100
- end
101
-
102
- def invoke(args)
103
- return "Error: working object not found. You must create it before" unless @conn
104
- begin
105
- @obj.invoke(*args).to_s.encode("UTF-8", "IBM866", :invalid => :replace, :replace => "?")
106
- rescue => e
107
- "Error: #{e.message}"
108
- end
109
- end
103
+ S41C::Sandbox.new(@ole, code).eval_code
104
+ end # eval_code
110
105
 
111
106
  def main_loop(server)
112
107
  loop do
@@ -138,20 +133,15 @@ module S41C
138
133
  end
139
134
 
140
135
  loop {
141
- args = S41C::Utils.to_utf8(session.gets || '').chomp.split('|')
136
+ args = read_response(session).split("\0")
142
137
  cmd = args.shift
143
138
  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))
139
+ when "eval"
140
+ code = ""
141
+ while !(part = session.gets)['end_of_code']
142
+ code << to_utf8(part)
143
+ end
144
+ session.puts to_bin(eval_code(code))
155
145
  session.puts "+OK"
156
146
  when "ping"
157
147
  session.puts "pong"
@@ -165,7 +155,7 @@ module S41C
165
155
  session.close
166
156
  exit
167
157
  else
168
- session.puts "Bad command"
158
+ session.puts "Bad command: `#{cmd}`"
169
159
  session.puts "+OK"
170
160
  end
171
161
  }
data/lib/s41c/utils.rb CHANGED
@@ -1,15 +1,30 @@
1
+ # encoding: utf-8
2
+
1
3
  module S41C
2
4
 
3
5
  module Utils
4
6
 
5
- def Utils.to_bin(str)
6
- str.force_encoding("BINARY")
7
+ def to_bin(str)
8
+ str.to_s.force_encoding("BINARY")
7
9
  end
8
10
 
9
- def Utils.to_utf8(str)
10
- str.force_encoding("UTF-8")
11
+ def to_utf8(str)
12
+ return unless str.is_a?(String)
13
+ if str.encoding.to_s == "IBM866"
14
+ str.to_s.encode("UTF-8", "IBM866", :invalid => :replace, :replace => "?")
15
+ else
16
+ str.to_s.force_encoding("UTF-8")
17
+ end
11
18
  end
12
19
 
20
+ def get_1c_id(obj)
21
+ return false unless @ole
22
+
23
+ str_id = @ole.invoke('ЗначениеВСтрокуВнутр', obj)
24
+ escaped_str_id = "\"#{str_id.gsub('"', '""')}\""
25
+
26
+ end # get_1c_id
27
+
13
28
  end # Utils
14
29
 
15
30
  end # S41C
data/lib/s41c/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  module S41C
2
2
 
3
- VERSION = '0.0.2'
3
+ VERSION = '0.0.4'
4
4
 
5
5
  end # S41C
data/lib/s41c.rb CHANGED
@@ -1,5 +1,5 @@
1
- require 'socket'
2
1
  require 's41c/version'
3
2
  require 's41c/utils'
3
+ require 's41c/sandbox'
4
4
  require 's41c/server'
5
5
  require 's41c/client'
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.2
4
+ version: 0.0.4
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-19 00:00:00.000000000 Z
12
+ date: 2012-03-24 00:00:00.000000000 Z
13
13
  dependencies: []
14
14
  description: TCP-socket сервер и клиент для платформы "1С:Предприятие"
15
15
  email:
@@ -20,8 +20,8 @@ extra_rdoc_files: []
20
20
  files:
21
21
  - LICENSE.BSD
22
22
  - s41c.gemspec
23
- - s41c-0.0.1.gem
24
23
  - README.md
24
+ - lib/s41c/sandbox.rb
25
25
  - lib/s41c/version.rb
26
26
  - lib/s41c/utils.rb
27
27
  - lib/s41c/server.rb
data/s41c-0.0.1.gem DELETED
Binary file