lis 0.2.3 → 0.4.3
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/.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
|