lis 0.2.3 → 0.4.3
Sign up to get free protection for your applications and to get access to all the features.
- data/.travis.yml +6 -0
- data/Gemfile +1 -1
- data/README.markdown +46 -1
- data/Rakefile +1 -3
- data/bin/lis +8 -0
- data/features/lis.feature +1 -5
- data/features/lis_cli.feature +6 -0
- data/features/step_definitions/lis_cli_steps.rb +6 -0
- data/features/step_definitions/lis_steps.rb +6 -11
- data/features/support/env.rb +7 -0
- data/lib/lis.rb +5 -4
- data/lib/lis/cli.rb +47 -0
- data/lib/lis/data.rb +45 -0
- data/lib/lis/http_interface.rb +74 -0
- data/lib/lis/interface_server.rb +9 -5
- data/lib/lis/messages/result.rb +4 -0
- data/lib/lis/transfer/application_protocol.rb +12 -7
- data/lib/lis/transfer/astm_e1394.rb +7 -3
- data/lib/lis/transfer/logging.rb +27 -0
- data/lib/lis/version.rb +1 -1
- data/lis.gemspec +10 -6
- data/test/helper.rb +4 -1
- data/test/test_http_interface.rb +61 -0
- data/test/test_messages.rb +1 -1
- metadata +186 -129
- data/bin/lis2http +0 -6
- data/lib/lis/commands/application.rb +0 -57
- data/lib/lis/worklist_manager_interface.rb +0 -69
- data/test/lib/mock_server.rb +0 -50
data/.travis.yml
ADDED
data/Gemfile
CHANGED
@@ -1,2 +1,2 @@
|
|
1
|
-
source
|
1
|
+
source "https://rubygems.org"
|
2
2
|
gemspec
|
data/README.markdown
CHANGED
@@ -13,7 +13,52 @@ available][spec])
|
|
13
13
|
[spec]: http://www.google.com/search?q=dpc+lis+immulite+2000+filetype:pdf
|
14
14
|
|
15
15
|
|
16
|
+
## Usage
|
17
|
+
|
18
|
+
* run the LIS server:
|
19
|
+
|
20
|
+
```
|
21
|
+
$ gem install lis
|
22
|
+
$ lis help
|
23
|
+
$ lis -l /dev/ttyUSB0 -e http://worklist.example/lis server
|
24
|
+
```
|
25
|
+
|
26
|
+
* now, whenever order requests arrive from the LIS hardware, lis2http will forward them to the specified HTTP endpoint:
|
27
|
+
|
28
|
+
```
|
29
|
+
GET http://worklist.example/lis/{DEVICE_NAME}-{SPECIMEN_ID}
|
30
|
+
```
|
31
|
+
|
32
|
+
* this should return basic patient information as well as test IDs for all pending requests:
|
33
|
+
|
34
|
+
```
|
35
|
+
---
|
36
|
+
id: '1234'
|
37
|
+
patient:
|
38
|
+
id: 98
|
39
|
+
last_name: Sierra
|
40
|
+
first_name: Rudolph
|
41
|
+
types:
|
42
|
+
- TSTID
|
43
|
+
- TSH
|
44
|
+
- FT3
|
45
|
+
- FT4
|
46
|
+
```
|
47
|
+
|
48
|
+
* results are posted to the same URI as soon as they are received:
|
49
|
+
|
50
|
+
```
|
51
|
+
POST http://worklist.example/lis/{DEVICE_NAME}-{SPECIMEN_ID}/{TEST_NAME}
|
52
|
+
|
53
|
+
---
|
54
|
+
flags: N
|
55
|
+
result_timestamp: '1993-10-11T09:12:33+00:00'
|
56
|
+
status: F
|
57
|
+
test_name: TSTID
|
58
|
+
unit: mIU/mL
|
59
|
+
value: '8.2'
|
60
|
+
```
|
16
61
|
|
17
62
|
## Copyright
|
18
63
|
|
19
|
-
Copyright (c) 2010-
|
64
|
+
Copyright (c) 2010-2013 Levin Alexander. See LICENSE for details.
|
data/Rakefile
CHANGED
data/bin/lis
ADDED
data/features/lis.feature
CHANGED
@@ -1,8 +1,4 @@
|
|
1
1
|
Feature:
|
2
|
-
In order to diagnose patients correctly
|
3
|
-
As a doctor
|
4
|
-
I want to be able to receive test results via LIS
|
5
|
-
|
6
2
|
Scenario: LIS asking for pending requests
|
7
3
|
Given LIS Interface listening for messages
|
8
4
|
And the following requests are pending for DPC:
|
@@ -17,7 +13,7 @@ Feature:
|
|
17
13
|
Then LIS should have sent test orders to client:
|
18
14
|
"""
|
19
15
|
1H|\^&|||LIS||||8N1|DPC||P|1|
|
20
|
-
2P|1
|
16
|
+
2P|1|98||98|Müller^Rudolph||||||||
|
21
17
|
3O|1|123ABC||^^^TSH
|
22
18
|
4O|1|123ABC||^^^FT3
|
23
19
|
5O|1|123ABC||^^^FT4
|
@@ -1,16 +1,14 @@
|
|
1
1
|
# encoding: UTF-8
|
2
2
|
|
3
|
-
|
4
|
-
require 'yaml'
|
3
|
+
|
5
4
|
|
6
5
|
|
7
6
|
Given /^LIS Interface listening for messages$/ do
|
8
7
|
@client, @r, @w = PacketIO::Test::MockServer.build
|
9
8
|
@io = PacketIO::IOListener.new(@r, @w)
|
10
|
-
@server = LIS::InterfaceServer.create(@io, "http://localhost/lis
|
9
|
+
@server = LIS::InterfaceServer.create(@io, "http://localhost/lis")
|
11
10
|
|
12
|
-
stub_request(:post, /http:\/\/localhost\/lis
|
13
|
-
with(:body => /.*/, :headers => {'Accept'=>'*/*', 'Content-Type'=>'application/x-www-form-urlencoded'}).
|
11
|
+
stub_request(:post, /http:\/\/localhost\/lis\/.*?\/.*/).
|
14
12
|
to_return(:status => 200, :body => "", :headers => {})
|
15
13
|
|
16
14
|
@t = Thread.new do
|
@@ -36,7 +34,7 @@ end
|
|
36
34
|
Then /^should have posted results:$/ do |table|
|
37
35
|
table.hashes.each do |row|
|
38
36
|
expected_body = ["test_name", "value", "unit", "status", "flags", "result_timestamp"].inject({}) { |h,k| h[k] = row[k]; h }
|
39
|
-
assert_requested(:post, "http://localhost/lis
|
37
|
+
assert_requested(:post, "http://localhost/lis/#{row["id"]}/#{row["test_name"]}", :times => 1, :body => hash_including(expected_body))
|
40
38
|
end
|
41
39
|
end
|
42
40
|
|
@@ -53,16 +51,13 @@ Given /^the following requests are pending for (\w+):$/ do |device_name, table|
|
|
53
51
|
table.hashes.each do |patient|
|
54
52
|
body = { "patient" => { "last_name" => patient["last_name"],
|
55
53
|
"first_name" => patient["first_name"],
|
56
|
-
"
|
54
|
+
"number" => patient["patient_id"]},
|
57
55
|
"id" => patient["id"],
|
58
56
|
"types" => patient["test_names"].strip.split(/\s+/) }
|
59
57
|
|
60
|
-
stub_request(:get, "http://localhost/lis
|
61
|
-
with(:headers => {'Accept'=>'*/*'}).
|
58
|
+
stub_request(:get, "http://localhost/lis/#{device_name}-#{patient["id"]}").
|
62
59
|
to_return(:status => 200, :body => body.to_yaml, :headers => {})
|
63
|
-
|
64
60
|
end
|
65
|
-
|
66
61
|
end
|
67
62
|
|
68
63
|
|
data/features/support/env.rb
CHANGED
@@ -1,9 +1,16 @@
|
|
1
1
|
$LOAD_PATH.unshift(File.dirname(__FILE__) + '/../../lib')
|
2
|
+
|
2
3
|
require 'lis'
|
3
4
|
|
5
|
+
require 'mocha'
|
6
|
+
require 'yaml'
|
7
|
+
require 'aruba/cucumber'
|
8
|
+
|
4
9
|
require 'packet_io/test/mock_server'
|
5
10
|
require 'test/unit/assertions'
|
6
11
|
require 'webmock/cucumber'
|
7
12
|
|
13
|
+
WebMock.disable_net_connect!
|
14
|
+
|
8
15
|
World(Test::Unit::Assertions)
|
9
16
|
|
data/lib/lis.rb
CHANGED
@@ -1,15 +1,16 @@
|
|
1
|
-
$:.unshift File.dirname(__FILE__)
|
2
|
-
|
3
1
|
require 'packet_io'
|
2
|
+
require 'yaml'
|
4
3
|
|
5
4
|
require 'lis/version'
|
6
|
-
|
7
5
|
require 'lis/messages.rb'
|
6
|
+
require 'lis/data'
|
7
|
+
|
8
8
|
Dir[File.join(File.dirname(__FILE__), 'lis/messages/**/*.rb')].each { |f| require f }
|
9
9
|
|
10
10
|
require 'lis/transfer/astm_e1394.rb'
|
11
11
|
require 'lis/transfer/application_protocol.rb'
|
12
|
+
require 'lis/transfer/logging'
|
13
|
+
require 'lis/http_interface.rb'
|
12
14
|
|
13
|
-
require 'lis/worklist_manager_interface.rb'
|
14
15
|
require 'lis/interface_server.rb'
|
15
16
|
|
data/lib/lis/cli.rb
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
module LIS
|
2
|
+
module CLI
|
3
|
+
extend GLI::App
|
4
|
+
program_desc "LIS interface to Siemens Immulite 2000XPi or other similar analyzers"
|
5
|
+
version LIS::VERSION
|
6
|
+
|
7
|
+
flag ["config","c"], :default_value => File.join(ENV['HOME'],'.lis'),
|
8
|
+
:desc => "stores configuration and authentication data",
|
9
|
+
:arg_name => "FILE"
|
10
|
+
|
11
|
+
flag ["listen", "l"], :arg_name => "PORT", :desc => "which port to listen on", :default_value => "/dev/ttyUSB0"
|
12
|
+
flag ["endpoint", "e"], :arg_name => "URI", :desc => "HTTP endpoint", :default_value => "http://localhost/api/lis"
|
13
|
+
switch ["verbose", "v"], :desc => "Increase verbosity"
|
14
|
+
|
15
|
+
pre do |global_options,command,options,args|
|
16
|
+
$VERBOSE = global_options[:verbose]
|
17
|
+
true
|
18
|
+
end
|
19
|
+
|
20
|
+
desc "run the LIS server"
|
21
|
+
command "server" do |c|
|
22
|
+
c.action do |global_options,options,args|
|
23
|
+
port = global_options[:listen]
|
24
|
+
warn "configuring #{port}"
|
25
|
+
`stty -echo raw ospeed 9600 ispeed 9600 < #{port}`
|
26
|
+
warn "listening on: #{port}"
|
27
|
+
port = File.open(port, "w+")
|
28
|
+
|
29
|
+
LIS::InterfaceServer.listen(port, global_options[:endpoint]).run!
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
desc "load a list of test requests"
|
34
|
+
arg_name "DEVICE_NAME REQUEST_ID"
|
35
|
+
command "requests" do |c|
|
36
|
+
c.action do |global_options,options,args|
|
37
|
+
|
38
|
+
req = LIS::HTTPInterface.new(global_options[:endpoint]).load_requests(*args)
|
39
|
+
puts req.types
|
40
|
+
end
|
41
|
+
|
42
|
+
|
43
|
+
end
|
44
|
+
|
45
|
+
|
46
|
+
end
|
47
|
+
end
|
data/lib/lis/data.rb
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
module LIS
|
2
|
+
module Data
|
3
|
+
class Request
|
4
|
+
def initialize(data={})
|
5
|
+
@data = data
|
6
|
+
end
|
7
|
+
|
8
|
+
def patient_id
|
9
|
+
@data["patient"]["number"]
|
10
|
+
end
|
11
|
+
def patient_last_name
|
12
|
+
@data["patient"]["last_name"]
|
13
|
+
end
|
14
|
+
def patient_first_name
|
15
|
+
@data["patient"]["first_name"]
|
16
|
+
end
|
17
|
+
def id
|
18
|
+
@data["id"]
|
19
|
+
end
|
20
|
+
|
21
|
+
def types
|
22
|
+
@data["types"]
|
23
|
+
end
|
24
|
+
|
25
|
+
|
26
|
+
def each_type
|
27
|
+
@data["types"].each do |t|
|
28
|
+
yield id, t
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def to_hash
|
33
|
+
@data
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.from_yaml(text, barcode)
|
37
|
+
data = YAML.load(text)
|
38
|
+
data["id"] = barcode
|
39
|
+
|
40
|
+
new(data)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
@@ -0,0 +1,74 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
require 'rest-client'
|
4
|
+
|
5
|
+
class LIS::HTTPInterface
|
6
|
+
def initialize(endpoint)
|
7
|
+
@endpoint = endpoint
|
8
|
+
end
|
9
|
+
|
10
|
+
# expects all pending requests for the given device and barcode
|
11
|
+
#
|
12
|
+
# { "id" => "1234",
|
13
|
+
# "patient" => { "id" => 98,
|
14
|
+
# "last_name" => "Sierra",
|
15
|
+
# "first_name" => "Rudolph" },
|
16
|
+
# "types" => [ "TSH", "FT3", "FT4" ] }
|
17
|
+
#
|
18
|
+
def load_requests(device_name, barcode)
|
19
|
+
begin
|
20
|
+
result = RestClient.get(uri(device_name, barcode))
|
21
|
+
data = LIS::Data::Request.from_yaml(result.body, barcode)
|
22
|
+
rescue Exception => e
|
23
|
+
puts e
|
24
|
+
puts e.backtrace
|
25
|
+
data = nil
|
26
|
+
end
|
27
|
+
|
28
|
+
warn "data: #{data.inspect}" if $VERBOSE
|
29
|
+
|
30
|
+
data
|
31
|
+
end
|
32
|
+
|
33
|
+
|
34
|
+
def set_request_status(device_name, data)
|
35
|
+
# uri = URI.join(@endpoint, "result_status/#{[device_name, data.id].join('-')}")
|
36
|
+
# Net::HTTP.post_form(uri, data.to_hash)
|
37
|
+
end
|
38
|
+
|
39
|
+
def send_result(device_name, order, result)
|
40
|
+
barcode = order.specimen_id
|
41
|
+
|
42
|
+
data = {
|
43
|
+
"test_name" => order.universal_test_id,
|
44
|
+
"value" => result.result_value,
|
45
|
+
"unit" => result.unit,
|
46
|
+
"status" => result.result_status,
|
47
|
+
"flags" => result.abnormal_flags,
|
48
|
+
"result_timestamp" => result.test_completed_at,
|
49
|
+
"raw" => result.raw_data
|
50
|
+
}
|
51
|
+
|
52
|
+
# FIXME: WTF: should not just catch everything
|
53
|
+
begin
|
54
|
+
res = RestClient.post(uri(device_name, barcode, order.universal_test_id), data)
|
55
|
+
rescue Exception => e
|
56
|
+
puts "EXCEPTION"
|
57
|
+
p e
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
|
62
|
+
private
|
63
|
+
|
64
|
+
def uri(device_name, barcode, test_name = nil)
|
65
|
+
id = [device_name, barcode].join("-")
|
66
|
+
|
67
|
+
s = [@endpoint, id, test_name].compact.join("/")
|
68
|
+
warn "uri: #{s}" if $VERBOSE
|
69
|
+
|
70
|
+
s
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
74
|
+
|
data/lib/lis/interface_server.rb
CHANGED
@@ -10,17 +10,21 @@ module LIS
|
|
10
10
|
end
|
11
11
|
|
12
12
|
|
13
|
-
def initialize(server, http_endpoint
|
13
|
+
def initialize(server, http_endpoint)
|
14
14
|
@server = server
|
15
|
-
protocol = protocol_stack.inject(server) { |i,klass| klass.new(i) }
|
16
|
-
interface = WorklistManagerInterface.new(http_endpoint)
|
17
15
|
|
16
|
+
protocol = LIS::Transfer::ApplicationProtocol.new(LIS::Transfer::Logging.new(LIS::Transfer::ASTM::E1394.new(server)))
|
17
|
+
interface = HTTPInterface.new(http_endpoint)
|
18
18
|
|
19
19
|
protocol.on_request do |device_name, barcode|
|
20
|
+
warn "loading requests" if $VERBOSE
|
20
21
|
interface.load_requests(device_name, barcode)
|
21
22
|
end
|
22
|
-
protocol.on_result do
|
23
|
-
interface.send_result(
|
23
|
+
protocol.on_result do |device_name, patient, order, result|
|
24
|
+
interface.send_result(device_name, order, result)
|
25
|
+
end
|
26
|
+
protocol.on_request_sent do |device_name, data|
|
27
|
+
interface.set_request_status(device_name, data)
|
24
28
|
end
|
25
29
|
end
|
26
30
|
|
data/lib/lis/messages/result.rb
CHANGED
@@ -12,6 +12,10 @@ module LIS::Transfer
|
|
12
12
|
@on_request_callback = block
|
13
13
|
end
|
14
14
|
|
15
|
+
def on_request_sent(&block)
|
16
|
+
@on_request_sent_callback = block
|
17
|
+
end
|
18
|
+
|
15
19
|
def received_header(message)
|
16
20
|
@patient_information_requests ||= {} # delete the list of patients
|
17
21
|
@device_name = message.sender_name
|
@@ -42,14 +46,16 @@ module LIS::Transfer
|
|
42
46
|
|
43
47
|
def send_pending_requests
|
44
48
|
sending_session(@patient_information_requests) do |patient_information|
|
45
|
-
patient_information.each do |sequence_nr,
|
49
|
+
patient_information.each do |sequence_nr, request_data|
|
46
50
|
write :message, LIS::Message::Patient.new(sequence_nr,
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
write :message, LIS::Message::Order.new(sequence_nr,
|
51
|
+
request_data.patient_id,
|
52
|
+
request_data.patient_last_name,
|
53
|
+
request_data.patient_first_name).to_message
|
54
|
+
request_data.each_type do |id, request|
|
55
|
+
write :message, LIS::Message::Order.new(sequence_nr, id, request).to_message
|
52
56
|
end
|
57
|
+
|
58
|
+
@on_request_sent_callback.call(@device_name, request_data) if @on_request_sent_callback
|
53
59
|
end
|
54
60
|
end
|
55
61
|
@patient_information_requests = {}
|
@@ -71,7 +77,6 @@ module LIS::Transfer
|
|
71
77
|
end
|
72
78
|
|
73
79
|
def receive(type, message = nil)
|
74
|
-
warn "[R] #{message}" if type == :message and $VERBOSE
|
75
80
|
case type
|
76
81
|
when :begin
|
77
82
|
@last_patient = nil
|
@@ -8,7 +8,7 @@ module LIS::Transfer
|
|
8
8
|
# splits a stream into lis packets and only lets packets through
|
9
9
|
# that are inside a session delimited by ENQ .. EOT
|
10
10
|
#
|
11
|
-
#
|
11
|
+
# checks the checksum and does acknowledgement of messages
|
12
12
|
#
|
13
13
|
# forwards the following events:
|
14
14
|
#
|
@@ -81,7 +81,7 @@ module LIS::Transfer
|
|
81
81
|
|
82
82
|
frame_number, data, checksum = match[1 .. 3]
|
83
83
|
|
84
|
-
expected_checksum = (frame_number + data)
|
84
|
+
expected_checksum = compute_checksum(frame_number + data)
|
85
85
|
actual_checksum = checksum.to_i(16)
|
86
86
|
|
87
87
|
raise "checksum mismatch: expected %03x got %03x" % [expected_checksum, actual_checksum] unless expected_checksum == actual_checksum
|
@@ -90,12 +90,16 @@ module LIS::Transfer
|
|
90
90
|
|
91
91
|
def self.wrap_message(string, frame_number)
|
92
92
|
frame_number = (frame_number % 8).to_s
|
93
|
-
checksum = (frame_number + string)
|
93
|
+
checksum = compute_checksum(frame_number + string)
|
94
94
|
checksum = checksum.to_s(16).upcase.rjust(2,"0")
|
95
95
|
|
96
96
|
"\002#{frame_number}#{string}\015\003#{checksum}\015\012"
|
97
97
|
end
|
98
98
|
|
99
|
+
def self.compute_checksum(str)
|
100
|
+
str.each_byte.inject(16) { |a,b| (a+b) % 0x100 }
|
101
|
+
end
|
102
|
+
|
99
103
|
|
100
104
|
def received_message(message)
|
101
105
|
return false unless @inside_transmission
|
@@ -0,0 +1,27 @@
|
|
1
|
+
|
2
|
+
module LIS::Transfer
|
3
|
+
class Logging < ::PacketIO::Base
|
4
|
+
def initialize(*args)
|
5
|
+
@verbose = $VERBOSE
|
6
|
+
super
|
7
|
+
end
|
8
|
+
|
9
|
+
def receive(type, message=nil)
|
10
|
+
output("<", type, message)
|
11
|
+
super
|
12
|
+
end
|
13
|
+
|
14
|
+
def write(type, message=nil)
|
15
|
+
output(">", type, message)
|
16
|
+
super
|
17
|
+
end
|
18
|
+
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def output(direction, type, message)
|
23
|
+
warn [direction, type.to_s, message].join(" ") if @verbose
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
data/lib/lis/version.rb
CHANGED
data/lis.gemspec
CHANGED
@@ -20,12 +20,16 @@ Gem::Specification.new do |s|
|
|
20
20
|
s.require_paths = ["lib"]
|
21
21
|
s.summary = %q{LIS interface to Siemens Immulite 2000XPi or other similar analyzers}
|
22
22
|
|
23
|
-
s.add_dependency "packet_io", "
|
23
|
+
s.add_dependency "packet_io", ">= 0.4.2"
|
24
|
+
s.add_dependency "rest-client"
|
25
|
+
s.add_dependency "rake"
|
26
|
+
s.add_dependency "gli", ">= 2.0.0.rc5"
|
24
27
|
|
25
|
-
s.add_development_dependency("shoulda", "
|
26
|
-
s.add_development_dependency("mocha", "
|
27
|
-
s.add_development_dependency("yard"
|
28
|
-
s.add_development_dependency("cucumber", "
|
29
|
-
s.add_development_dependency("webmock", "
|
28
|
+
s.add_development_dependency("shoulda", ">= 3.1.0")
|
29
|
+
s.add_development_dependency("mocha", ">= 0.12.0")
|
30
|
+
s.add_development_dependency("yard")
|
31
|
+
s.add_development_dependency("cucumber", ">= 1.2.0")
|
32
|
+
s.add_development_dependency("webmock", ">= 1.8.7")
|
33
|
+
s.add_development_dependency('aruba')
|
30
34
|
end
|
31
35
|
|
data/test/helper.rb
CHANGED
@@ -1,12 +1,15 @@
|
|
1
1
|
require 'rubygems'
|
2
2
|
require 'test/unit'
|
3
|
+
require 'webmock/test_unit'
|
3
4
|
require 'shoulda'
|
4
|
-
require 'mocha'
|
5
|
+
require 'mocha/setup'
|
5
6
|
|
6
7
|
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
7
8
|
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
8
9
|
require 'lis'
|
9
10
|
|
11
|
+
WebMock.disable_net_connect!
|
12
|
+
|
10
13
|
class Test::Unit::TestCase
|
11
14
|
end
|
12
15
|
|
@@ -0,0 +1,61 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
class TestHTTPInterface < Test::Unit::TestCase
|
4
|
+
|
5
|
+
def setup
|
6
|
+
@interface = LIS::HTTPInterface.new("http://localhost/lis")
|
7
|
+
@device_name = "LIS1"
|
8
|
+
end
|
9
|
+
|
10
|
+
|
11
|
+
context "posting a result" do
|
12
|
+
setup do
|
13
|
+
@order = LIS::Message::Order.new(13, 12345, "TSTID")
|
14
|
+
@string = "R|1|^^^LH|8.2|mIU/mL|.7\.7^400\400|N|N|F||test|19931011091233|19931011091233|DPCCIRRUS"
|
15
|
+
@result = LIS::Message::Result.from_string(@string)
|
16
|
+
end
|
17
|
+
|
18
|
+
should "should post correct format to the HTTP endpoint" do
|
19
|
+
result_stub = stub_request(:post, "http://localhost/lis/LIS1-12345/TSTID").
|
20
|
+
with(:body => { "flags"=>"N",
|
21
|
+
"result_timestamp"=>"1993-10-11T09:12:33+00:00",
|
22
|
+
"status"=>"F",
|
23
|
+
"test_name"=>"TSTID",
|
24
|
+
"unit"=>"mIU/mL",
|
25
|
+
"value"=>"8.2",
|
26
|
+
"raw"=>@string}).
|
27
|
+
to_return(:status => 200, :body => "", :headers => {})
|
28
|
+
|
29
|
+
@interface.send_result(@device_name, @order, @result)
|
30
|
+
assert_requested(result_stub)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
context "requesting test" do
|
35
|
+
setup do
|
36
|
+
@http_result = { "id" => "1234",
|
37
|
+
"patient" => { "number" => 98,
|
38
|
+
"last_name" => "Sierra",
|
39
|
+
"first_name" => "Rudolph" },
|
40
|
+
"types" => [ "TSH", "FT3", "FT4" ] }.to_yaml
|
41
|
+
end
|
42
|
+
|
43
|
+
|
44
|
+
should "return patient information and requests" do
|
45
|
+
stub_request( :get, "http://localhost/lis/LIS1-SOMETHING").to_return(:status => 301, :headers => { 'Location' => "http://localhost/lis/LIS1-1234" } )
|
46
|
+
|
47
|
+
result_stub = stub_request(:get, "http://localhost/lis/LIS1-1234")
|
48
|
+
result_stub.to_return( :status => 200, :body => @http_result, :headers => {})
|
49
|
+
data = @interface.load_requests(@device_name, "SOMETHING")
|
50
|
+
|
51
|
+
expected = { "id" => "SOMETHING",
|
52
|
+
"patient" => { "number" => 98,
|
53
|
+
"last_name" => "Sierra",
|
54
|
+
"first_name" => "Rudolph" },
|
55
|
+
"types" => [ "TSH", "FT3", "FT4" ] }
|
56
|
+
|
57
|
+
assert_equal expected, data.to_hash
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
data/test/test_messages.rb
CHANGED
metadata
CHANGED
@@ -1,141 +1,201 @@
|
|
1
|
-
--- !ruby/object:Gem::Specification
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
2
|
name: lis
|
3
|
-
version: !ruby/object:Gem::Version
|
4
|
-
|
5
|
-
prerelease:
|
6
|
-
segments:
|
7
|
-
- 0
|
8
|
-
- 2
|
9
|
-
- 3
|
10
|
-
version: 0.2.3
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.4.3
|
5
|
+
prerelease:
|
11
6
|
platform: ruby
|
12
|
-
authors:
|
7
|
+
authors:
|
13
8
|
- Levin Alexander
|
14
9
|
autorequire:
|
15
10
|
bindir: bin
|
16
11
|
cert_chain: []
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
dependencies:
|
21
|
-
- !ruby/object:Gem::Dependency
|
12
|
+
date: 2013-03-05 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
22
15
|
name: packet_io
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: 0.4.2
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: 0.4.2
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: rest-client
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
38
|
+
type: :runtime
|
23
39
|
prerelease: false
|
24
|
-
|
25
|
-
none: false
|
26
|
-
requirements:
|
27
|
-
- -
|
28
|
-
- !ruby/object:Gem::Version
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: rake
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ! '>='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
35
54
|
type: :runtime
|
36
|
-
version_requirements: *id001
|
37
|
-
- !ruby/object:Gem::Dependency
|
38
|
-
name: shoulda
|
39
55
|
prerelease: false
|
40
|
-
|
41
|
-
none: false
|
42
|
-
requirements:
|
43
|
-
- -
|
44
|
-
- !ruby/object:Gem::Version
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: gli
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ! '>='
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: 2.0.0.rc5
|
70
|
+
type: :runtime
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ! '>='
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: 2.0.0.rc5
|
78
|
+
- !ruby/object:Gem::Dependency
|
79
|
+
name: shoulda
|
80
|
+
requirement: !ruby/object:Gem::Requirement
|
81
|
+
none: false
|
82
|
+
requirements:
|
83
|
+
- - ! '>='
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: 3.1.0
|
51
86
|
type: :development
|
52
|
-
version_requirements: *id002
|
53
|
-
- !ruby/object:Gem::Dependency
|
54
|
-
name: mocha
|
55
87
|
prerelease: false
|
56
|
-
|
57
|
-
none: false
|
58
|
-
requirements:
|
59
|
-
- -
|
60
|
-
- !ruby/object:Gem::Version
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
88
|
+
version_requirements: !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
90
|
+
requirements:
|
91
|
+
- - ! '>='
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: 3.1.0
|
94
|
+
- !ruby/object:Gem::Dependency
|
95
|
+
name: mocha
|
96
|
+
requirement: !ruby/object:Gem::Requirement
|
97
|
+
none: false
|
98
|
+
requirements:
|
99
|
+
- - ! '>='
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
version: 0.12.0
|
67
102
|
type: :development
|
68
|
-
version_requirements: *id003
|
69
|
-
- !ruby/object:Gem::Dependency
|
70
|
-
name: yard
|
71
103
|
prerelease: false
|
72
|
-
|
73
|
-
none: false
|
74
|
-
requirements:
|
75
|
-
- -
|
76
|
-
- !ruby/object:Gem::Version
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
104
|
+
version_requirements: !ruby/object:Gem::Requirement
|
105
|
+
none: false
|
106
|
+
requirements:
|
107
|
+
- - ! '>='
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: 0.12.0
|
110
|
+
- !ruby/object:Gem::Dependency
|
111
|
+
name: yard
|
112
|
+
requirement: !ruby/object:Gem::Requirement
|
113
|
+
none: false
|
114
|
+
requirements:
|
115
|
+
- - ! '>='
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
83
118
|
type: :development
|
84
|
-
version_requirements: *id004
|
85
|
-
- !ruby/object:Gem::Dependency
|
86
|
-
name: cucumber
|
87
119
|
prerelease: false
|
88
|
-
|
89
|
-
none: false
|
90
|
-
requirements:
|
91
|
-
- -
|
92
|
-
- !ruby/object:Gem::Version
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
none: false
|
122
|
+
requirements:
|
123
|
+
- - ! '>='
|
124
|
+
- !ruby/object:Gem::Version
|
125
|
+
version: '0'
|
126
|
+
- !ruby/object:Gem::Dependency
|
127
|
+
name: cucumber
|
128
|
+
requirement: !ruby/object:Gem::Requirement
|
129
|
+
none: false
|
130
|
+
requirements:
|
131
|
+
- - ! '>='
|
132
|
+
- !ruby/object:Gem::Version
|
133
|
+
version: 1.2.0
|
99
134
|
type: :development
|
100
|
-
|
101
|
-
|
135
|
+
prerelease: false
|
136
|
+
version_requirements: !ruby/object:Gem::Requirement
|
137
|
+
none: false
|
138
|
+
requirements:
|
139
|
+
- - ! '>='
|
140
|
+
- !ruby/object:Gem::Version
|
141
|
+
version: 1.2.0
|
142
|
+
- !ruby/object:Gem::Dependency
|
102
143
|
name: webmock
|
144
|
+
requirement: !ruby/object:Gem::Requirement
|
145
|
+
none: false
|
146
|
+
requirements:
|
147
|
+
- - ! '>='
|
148
|
+
- !ruby/object:Gem::Version
|
149
|
+
version: 1.8.7
|
150
|
+
type: :development
|
103
151
|
prerelease: false
|
104
|
-
|
105
|
-
none: false
|
106
|
-
requirements:
|
107
|
-
- -
|
108
|
-
- !ruby/object:Gem::Version
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
152
|
+
version_requirements: !ruby/object:Gem::Requirement
|
153
|
+
none: false
|
154
|
+
requirements:
|
155
|
+
- - ! '>='
|
156
|
+
- !ruby/object:Gem::Version
|
157
|
+
version: 1.8.7
|
158
|
+
- !ruby/object:Gem::Dependency
|
159
|
+
name: aruba
|
160
|
+
requirement: !ruby/object:Gem::Requirement
|
161
|
+
none: false
|
162
|
+
requirements:
|
163
|
+
- - ! '>='
|
164
|
+
- !ruby/object:Gem::Version
|
165
|
+
version: '0'
|
115
166
|
type: :development
|
116
|
-
|
117
|
-
|
167
|
+
prerelease: false
|
168
|
+
version_requirements: !ruby/object:Gem::Requirement
|
169
|
+
none: false
|
170
|
+
requirements:
|
171
|
+
- - ! '>='
|
172
|
+
- !ruby/object:Gem::Version
|
173
|
+
version: '0'
|
174
|
+
description: ''
|
118
175
|
email: mail@levinalex.net
|
119
|
-
executables:
|
120
|
-
-
|
176
|
+
executables:
|
177
|
+
- lis
|
121
178
|
extensions: []
|
122
|
-
|
123
179
|
extra_rdoc_files: []
|
124
|
-
|
125
|
-
files:
|
180
|
+
files:
|
126
181
|
- .gitignore
|
182
|
+
- .travis.yml
|
127
183
|
- .yardopts
|
128
184
|
- Gemfile
|
129
185
|
- LICENSE
|
130
186
|
- README.markdown
|
131
187
|
- Rakefile
|
132
|
-
- bin/
|
188
|
+
- bin/lis
|
133
189
|
- features/communication basics.feature
|
134
190
|
- features/lis.feature
|
191
|
+
- features/lis_cli.feature
|
192
|
+
- features/step_definitions/lis_cli_steps.rb
|
135
193
|
- features/step_definitions/lis_steps.rb
|
136
194
|
- features/support/env.rb
|
137
195
|
- lib/lis.rb
|
138
|
-
- lib/lis/
|
196
|
+
- lib/lis/cli.rb
|
197
|
+
- lib/lis/data.rb
|
198
|
+
- lib/lis/http_interface.rb
|
139
199
|
- lib/lis/interface_server.rb
|
140
200
|
- lib/lis/messages.rb
|
141
201
|
- lib/lis/messages/comment.rb
|
@@ -147,63 +207,60 @@ files:
|
|
147
207
|
- lib/lis/messages/terminator.rb
|
148
208
|
- lib/lis/transfer/application_protocol.rb
|
149
209
|
- lib/lis/transfer/astm_e1394.rb
|
210
|
+
- lib/lis/transfer/logging.rb
|
150
211
|
- lib/lis/version.rb
|
151
|
-
- lib/lis/worklist_manager_interface.rb
|
152
212
|
- lis.gemspec
|
153
213
|
- test/helper.rb
|
154
|
-
- test/lib/mock_server.rb
|
155
214
|
- test/messages/test_header.rb
|
156
215
|
- test/messages/test_order.rb
|
157
216
|
- test/messages/test_patients.rb
|
158
217
|
- test/messages/test_terminator.rb
|
159
218
|
- test/test_application_protocol.rb
|
160
219
|
- test/test_astm_e1394.rb
|
220
|
+
- test/test_http_interface.rb
|
161
221
|
- test/test_messages.rb
|
162
|
-
has_rdoc: true
|
163
222
|
homepage: http://github.com/levinalex/lis
|
164
223
|
licenses: []
|
165
|
-
|
166
224
|
post_install_message:
|
167
|
-
rdoc_options:
|
225
|
+
rdoc_options:
|
168
226
|
- --charset=UTF-8
|
169
|
-
require_paths:
|
227
|
+
require_paths:
|
170
228
|
- lib
|
171
|
-
required_ruby_version: !ruby/object:Gem::Requirement
|
229
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
172
230
|
none: false
|
173
|
-
requirements:
|
174
|
-
- -
|
175
|
-
- !ruby/object:Gem::Version
|
176
|
-
|
177
|
-
segments:
|
231
|
+
requirements:
|
232
|
+
- - ! '>='
|
233
|
+
- !ruby/object:Gem::Version
|
234
|
+
version: '0'
|
235
|
+
segments:
|
178
236
|
- 0
|
179
|
-
|
180
|
-
required_rubygems_version: !ruby/object:Gem::Requirement
|
237
|
+
hash: 4116448878207311103
|
238
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
181
239
|
none: false
|
182
|
-
requirements:
|
183
|
-
- -
|
184
|
-
- !ruby/object:Gem::Version
|
185
|
-
|
186
|
-
segments:
|
187
|
-
- 0
|
188
|
-
version: "0"
|
240
|
+
requirements:
|
241
|
+
- - ! '>='
|
242
|
+
- !ruby/object:Gem::Version
|
243
|
+
version: '0'
|
189
244
|
requirements: []
|
190
|
-
|
191
245
|
rubyforge_project:
|
192
|
-
rubygems_version: 1.
|
246
|
+
rubygems_version: 1.8.25
|
193
247
|
signing_key:
|
194
248
|
specification_version: 3
|
195
249
|
summary: LIS interface to Siemens Immulite 2000XPi or other similar analyzers
|
196
|
-
test_files:
|
250
|
+
test_files:
|
197
251
|
- features/communication basics.feature
|
198
252
|
- features/lis.feature
|
253
|
+
- features/lis_cli.feature
|
254
|
+
- features/step_definitions/lis_cli_steps.rb
|
199
255
|
- features/step_definitions/lis_steps.rb
|
200
256
|
- features/support/env.rb
|
201
257
|
- test/helper.rb
|
202
|
-
- test/lib/mock_server.rb
|
203
258
|
- test/messages/test_header.rb
|
204
259
|
- test/messages/test_order.rb
|
205
260
|
- test/messages/test_patients.rb
|
206
261
|
- test/messages/test_terminator.rb
|
207
262
|
- test/test_application_protocol.rb
|
208
263
|
- test/test_astm_e1394.rb
|
264
|
+
- test/test_http_interface.rb
|
209
265
|
- test/test_messages.rb
|
266
|
+
has_rdoc:
|
data/bin/lis2http
DELETED
@@ -1,57 +0,0 @@
|
|
1
|
-
# encoding: UTF-8
|
2
|
-
|
3
|
-
require 'optparse'
|
4
|
-
|
5
|
-
module LIS
|
6
|
-
class Options < Hash
|
7
|
-
attr_reader :opts
|
8
|
-
|
9
|
-
def initialize(args)
|
10
|
-
super()
|
11
|
-
|
12
|
-
default_options = { :port => "/dev/ttyUSB0", :uri => "http://localhost/lis/" }
|
13
|
-
self.merge!(default_options)
|
14
|
-
|
15
|
-
@opts = OptionParser.new do |o|
|
16
|
-
appname = File.basename($0)
|
17
|
-
o.banner = "Usage: #{appname} [options]"
|
18
|
-
|
19
|
-
o.on('-l, --listen PORT', 'which port to listen on (default: "/dev/ttyUSB0")') do |port|
|
20
|
-
self[:port] = port
|
21
|
-
end
|
22
|
-
o.on('-e, --endpoint URI', 'HTTP endpoint (default: "http://localhost/lis/")') do |endpoint|
|
23
|
-
self[:uri] = endpoint
|
24
|
-
end
|
25
|
-
o.on("-v", "--[no-]verbose", "Run verbosely") do |v|
|
26
|
-
$VERBOSE = v
|
27
|
-
self[:verbose] = v
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
begin
|
32
|
-
@opts.parse!(args)
|
33
|
-
self[:project_name] = args.shift
|
34
|
-
rescue OptionParser::ParseError => e
|
35
|
-
self[:invalid_argument] = e.message
|
36
|
-
end
|
37
|
-
end
|
38
|
-
end
|
39
|
-
|
40
|
-
class Application
|
41
|
-
def initialize(opts)
|
42
|
-
@options = Options.new(opts)
|
43
|
-
|
44
|
-
if @options[:invalid_argument]
|
45
|
-
warn @options.opts
|
46
|
-
exit 1
|
47
|
-
end
|
48
|
-
end
|
49
|
-
|
50
|
-
def run!
|
51
|
-
warn "listening on: #{@options[:port]}"
|
52
|
-
port = File.open(@options[:port], "w+")
|
53
|
-
LIS::InterfaceServer.listen(port, @options[:uri]).run!
|
54
|
-
end
|
55
|
-
end
|
56
|
-
|
57
|
-
end
|
@@ -1,69 +0,0 @@
|
|
1
|
-
# encoding: UTF-8
|
2
|
-
|
3
|
-
require 'net/http'
|
4
|
-
require 'yaml'
|
5
|
-
|
6
|
-
class WorklistManagerInterface
|
7
|
-
def initialize(endpoint)
|
8
|
-
@endpoint = endpoint
|
9
|
-
end
|
10
|
-
|
11
|
-
# expects all pending requests for the given device and barcode
|
12
|
-
#
|
13
|
-
# { "id" => "1234",
|
14
|
-
# "patient" => { "number" => 98,
|
15
|
-
# "last_name" => "Sierra",
|
16
|
-
# "first_name" => "Rudolph" },
|
17
|
-
# "types" => [ "TSH", "FT3", "FT4" ] }
|
18
|
-
#
|
19
|
-
#
|
20
|
-
def load_requests(device_name, barcode)
|
21
|
-
begin
|
22
|
-
uri = URI.join(@endpoint,"find_requests/#{[device_name, barcode].join('-')}")
|
23
|
-
result = fetch_with_redirect(uri.to_s)
|
24
|
-
data = YAML.load(result.body)
|
25
|
-
data["id"] = barcode
|
26
|
-
rescue Exception => e
|
27
|
-
puts e
|
28
|
-
puts e.backtrace
|
29
|
-
data = nil
|
30
|
-
end
|
31
|
-
|
32
|
-
data
|
33
|
-
end
|
34
|
-
|
35
|
-
def send_result(device_name, patient, order, result)
|
36
|
-
barcode = order.specimen_id
|
37
|
-
data = {
|
38
|
-
"test_name" => order.universal_test_id,
|
39
|
-
"value" => result.result_value,
|
40
|
-
"unit" => result.unit,
|
41
|
-
"status" => result.result_status,
|
42
|
-
"flags" => result.abnormal_flags,
|
43
|
-
"result_timestamp" => result.test_completed_at
|
44
|
-
}
|
45
|
-
|
46
|
-
# FIXME: WTF: should not just catch everything
|
47
|
-
begin
|
48
|
-
res = Net::HTTP.post_form(URI.join(@endpoint, "result/#{[device_name, barcode].join('-')}"), data.to_hash)
|
49
|
-
rescue Exception => e
|
50
|
-
puts "EXCEPTION"
|
51
|
-
p e
|
52
|
-
end
|
53
|
-
end
|
54
|
-
|
55
|
-
|
56
|
-
private
|
57
|
-
|
58
|
-
def fetch_with_redirect(uri_str, limit = 10)
|
59
|
-
raise ArgumentError, 'too many HTTP redirects' if limit == 0
|
60
|
-
|
61
|
-
response = Net::HTTP.get_response(URI.parse(uri_str))
|
62
|
-
case response
|
63
|
-
when Net::HTTPSuccess then response
|
64
|
-
when Net::HTTPRedirection then fetch_with_redirect(response['location'], limit - 1)
|
65
|
-
else
|
66
|
-
response.error!
|
67
|
-
end
|
68
|
-
end
|
69
|
-
end
|
data/test/lib/mock_server.rb
DELETED
@@ -1,50 +0,0 @@
|
|
1
|
-
module Mock
|
2
|
-
end
|
3
|
-
|
4
|
-
class Mock::Server
|
5
|
-
def initialize(read, write)
|
6
|
-
@read, @write = read, write
|
7
|
-
@queue = Queue.new
|
8
|
-
@thread = Thread.new do
|
9
|
-
parse_commands
|
10
|
-
end
|
11
|
-
end
|
12
|
-
|
13
|
-
def write(string)
|
14
|
-
@queue.push [:write, string]
|
15
|
-
self
|
16
|
-
end
|
17
|
-
|
18
|
-
def read_all
|
19
|
-
@read.readpartial(4096)
|
20
|
-
end
|
21
|
-
|
22
|
-
def wait(seconds = 0.02)
|
23
|
-
@queue.push [:wait, seconds]
|
24
|
-
self
|
25
|
-
end
|
26
|
-
|
27
|
-
def eof
|
28
|
-
@queue.push [:close]
|
29
|
-
end
|
30
|
-
|
31
|
-
|
32
|
-
private
|
33
|
-
|
34
|
-
def parse_commands
|
35
|
-
loop do
|
36
|
-
action, data = @queue.pop
|
37
|
-
|
38
|
-
case action
|
39
|
-
when :close
|
40
|
-
@write.close
|
41
|
-
break
|
42
|
-
when :write
|
43
|
-
@write.write(data)
|
44
|
-
@write.flush
|
45
|
-
when :wait
|
46
|
-
sleep data
|
47
|
-
end
|
48
|
-
end
|
49
|
-
end
|
50
|
-
end
|