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 +24 -0
- data/README.md +75 -0
- data/lib/s41c.rb +1 -0
- data/lib/s41c/client.rb +97 -0
- data/lib/s41c/server.rb +90 -52
- data/lib/s41c/version.rb +1 -1
- data/s41c-0.0.1.gem +0 -0
- metadata +4 -2
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
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
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
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(
|
64
|
+
def connect_to_1c(args)
|
92
65
|
@conn.ole_free unless @conn.nil?
|
93
|
-
options =
|
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(
|
81
|
+
def create(args)
|
109
82
|
return "Error: not connected" unless @conn
|
110
|
-
obj_name =
|
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(
|
92
|
+
def eval_expr(args)
|
120
93
|
return "Error: not connected" unless @conn
|
121
|
-
expr =
|
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(
|
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(*
|
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
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.
|
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-
|
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
|