-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #12 from CLeARoboticsLab/general_connection
Add general TCP connection
- Loading branch information
Showing
9 changed files
with
250 additions
and
113 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
using RosSockets | ||
import JSON | ||
|
||
function test_time_server() | ||
ip = "192.168.1.135" # ip address of the host of the ROS node | ||
port = 42423 # port to connect on | ||
|
||
# open a connection to a TCP server | ||
connection = open_connection(ip, port) | ||
|
||
# create command strings that are in the form of JSONs. These strings should | ||
# be formatted as required by the receiving ROS node. Note that some nodes | ||
# may require an end of line character, such as "\n", to terminate the command. | ||
start_cmd = JSON.json(Dict("action" => "start_experiment")) * "\n" | ||
stop_cmd = JSON.json(Dict("action" => "stop_experiment")) * "\n" | ||
get_time_cmd = JSON.json(Dict("action" => "get_time_elapsed")) * "\n" | ||
|
||
# send a command to the TCP server | ||
send(connection, start_cmd) | ||
|
||
# send commands and wait to receive responses from the server | ||
for _ in 1:10 | ||
payload = send_receive(connection, get_time_cmd) | ||
data = JSON.parse(String(payload)) | ||
elapsed_time = data["elapsed_time"] | ||
println("Elapsed time: $(elapsed_time)") | ||
sleep(0.5) | ||
end | ||
|
||
# send another command to the TCP server | ||
send(connection, stop_cmd) | ||
sleep(1.0) | ||
|
||
# Close the connection to the TCP server. This should always be called when | ||
# complete with tasks to ensure graceful shutdown. | ||
close_connection(connection) | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
complete_control_sequence = zeros(500) | ||
|
||
ip = "192.168.88.128" # ip address of the host of the ROS node | ||
port = 42421 # port to connect on | ||
|
||
max_window_duration = 5.0 # maximum length of control sequence (sec) | ||
update_time = 2.0 # duration between each sending of the control sequence (sec) | ||
timestep = 0.1 # duration of each timestep (sec) | ||
|
||
max_window_length = Integer(round(max_window_duration/timestep)) | ||
sleep_length = Integer(round(update_time/timestep)) | ||
complete_control_sequence_length = size(complete_control_sequence)[1] | ||
|
||
robot_connection = open_robot_connection(ip, port) | ||
|
||
idx = 1 | ||
while idx < complete_control_sequence_length | ||
window_length = min(max_window_length, complete_control_sequence_length - idx) | ||
control_sequence = complete_control_sequence[idx:idx+window_length] | ||
send_control_commands(robot_connection, control_sequence) | ||
idx += sleep_length | ||
sleep(update_time) | ||
end | ||
|
||
close_robot_connection(robot_connection) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,117 @@ | ||
struct Connection | ||
task::Task | ||
command_channel::Channel{Any} | ||
data_channel::Channel{Any} | ||
|
||
function Connection(ip::String, port::Integer) | ||
command_channel = Channel(1) | ||
data_channel = Channel(1) | ||
@info "Connecting to server at $(ip):$(port) ..." | ||
socket = Sockets.connect(ip, port) | ||
task = errormonitor(Threads.@spawn connection_task(socket, | ||
command_channel, data_channel)) | ||
connection = new(task, command_channel, data_channel) | ||
return connection | ||
end | ||
end | ||
|
||
struct TimeoutError <: Exception end | ||
|
||
""" | ||
open_connection(ip::String, port::Integer) | ||
Open a connection to a TCP server and return the `Connection`. | ||
The `ip` must be a string formatted as `"123.123.123.123"` | ||
""" | ||
function open_connection(ip::String, port::Integer) | ||
return Connection(ip, port) | ||
end | ||
|
||
function connection_task(socket, command_channel, data_channel) | ||
@info "Connection task spawned" | ||
while true | ||
command, msg = take!(command_channel) | ||
if command === :close | ||
@info "Closing connection ..." | ||
break | ||
elseif command === :send | ||
write(socket, msg) | ||
elseif command === :send_receive | ||
write(socket, msg) | ||
data = readline(socket) | ||
put!(data_channel, data) | ||
end | ||
end | ||
close(socket) | ||
@info "Connection task completed" | ||
end | ||
|
||
""" | ||
send(connection::Connection, msg::String) | ||
Send a message. | ||
# Arguments | ||
- `connection`: the `Connection` obtained from `open_connection`. | ||
- `msg`: the message to be sent. Note: some TCP servers may be require the | ||
message be formatted a certain way (such as JSON), and may also require an | ||
end of line character, such as `\\n`, to terminate the message. | ||
""" | ||
function send(connection::Connection, msg::String) | ||
put!(connection.command_channel, (:send, msg)) | ||
end | ||
|
||
""" | ||
send_receive(connection::Connection, msg::String, timeout::Real = 10.0) | ||
Sends a message and waits for a response, which is then returned. This function | ||
blocks execution while waiting, up to the timeout duration provided. If the | ||
timeout duration elapses without the arrival of data, throws a TimeoutError | ||
exception. Note: the payload which is returned will be in a raw format. To | ||
convert to a string, use `String(payload)`. This string may further converted to | ||
other formats such as JSON. | ||
# Arguments | ||
- `connection`: the `Connection` obtained from `open_connection`. | ||
- `msg`: the message to be sent. Note: some TCP servers may be require the | ||
message be formatted a certain way (such as JSON), and may also require an | ||
end of line character, such as `\\n`, to terminate the message. | ||
- `timeout`: maximum time in seconds to wait for data. | ||
""" | ||
function send_receive(connection::Connection, msg::String, timeout::Real = 10.0) | ||
t = Timer(_ -> timeout_callback(connection), timeout) | ||
payload = nothing | ||
try | ||
put!(connection.command_channel, (:send_receive, msg)) | ||
payload = take!(connection.data_channel) | ||
catch e | ||
if typeof(e) != InvalidStateException | ||
close_connection(connection) | ||
rethrow(e) | ||
end | ||
finally | ||
close(t) | ||
end | ||
return payload | ||
end | ||
|
||
function timeout_callback(connection::Connection) | ||
close_connection(connection) | ||
@error "Server timed out waiting for data." | ||
throw(TimeoutError()) | ||
end | ||
|
||
""" | ||
close_connection(connection::Connection) | ||
Close the connection to the TCP server. | ||
""" | ||
function close_connection(connection::Connection) | ||
@info "Stopping server ..." | ||
put!(connection.command_channel, (:close, "")) | ||
wait(connection.task) | ||
close(connection.data_channel) | ||
close(connection.command_channel) | ||
@info "Server stopped" | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
5a17709
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@JuliaRegistrator register
5a17709
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Registration pull request created: JuliaRegistries/General/85962
After the above pull request is merged, it is recommended that a tag is created on this repository for the registered package version.
This will be done automatically if the Julia TagBot GitHub Action is installed, or can be done manually through the github interface, or via:
Also, note the warning: This looks like a new registration that registers version 0.4.0.
Ideally, you should register an initial release with 0.0.1, 0.1.0 or 1.0.0 version numbers
This can be safely ignored. However, if you want to fix this you can do so. Call register() again after making the fix. This will update the Pull request.