forked from zietzm/Helmholtz_Test_Bench
Refatoring: Moved .py files into src folder. Unified some file names
This commit is contained in:
@@ -0,0 +1,146 @@
|
||||
import globals as g
|
||||
from user_interface import ui_print
|
||||
import cage_func as cage_controls
|
||||
|
||||
from threading import Thread
|
||||
import socket
|
||||
import numpy as np
|
||||
|
||||
# --- Definition of TCP interface ---
|
||||
#
|
||||
# Clients should by default initialize a TCP connection to port 6677
|
||||
# The commands shown must be terminated with a single \n (newline) char
|
||||
# Commands may be split across multiple packets.
|
||||
# Before useful commands can be sent, declare_api_version must be called.
|
||||
#
|
||||
# A description of the TCP api (safety limits are always enforced):
|
||||
#
|
||||
# set_raw_field [X comp.] [Y comp.] [Z comp.]
|
||||
# Returns: 0 or 1 for success
|
||||
# Accepts decimal point formatted floats, with or without scientific notation. The float() cast must understand it.
|
||||
# The field units are Tesla
|
||||
# This causes an additional field of the given strength to be generated, without regard for the pre-existing
|
||||
# geomagnetic/external fields.
|
||||
#
|
||||
# set_compensated_field [X comp.] [Y comp.] [Z comp.]
|
||||
# Returns: 0 or 1 for success
|
||||
# Accepts decimal point formatted floats, with or without scientific notation. The float() cast must understand it.
|
||||
# The field units are Tesla
|
||||
# This causes a field of exactly the given magnitude to be generated by compensating external factors such as the
|
||||
# geomagnetic field.
|
||||
#
|
||||
# set_coil_currents [X comp.] [Y comp.] [Z comp.]
|
||||
# Returns: 0 or 1 for success
|
||||
# Accepts decimal point formatted floats, with or without scientific notation. The float() cast must understand it.
|
||||
# The field units are Ampere
|
||||
# This establishes the requested current in the individual coils.
|
||||
#
|
||||
# get_api_version
|
||||
# Returns: a string uniquely identifying each API version.
|
||||
# This function can be called before declare_api_version.
|
||||
# Please dont put
|
||||
#
|
||||
# declare_api_version [version]
|
||||
# Returns: 0 or 1 (terminated with newline)
|
||||
# Declare the api version the client application was programmed for. It must be compatible with the current
|
||||
# API version. This prevents unexpected behaviour by forcing programmers to specify which API they are expecting.
|
||||
# This function must be called before sending HW commands.
|
||||
|
||||
|
||||
SOCKET_INTERFACE_API_VERSION = "1"
|
||||
|
||||
|
||||
class ClientConnectionThread(Thread):
|
||||
def __init__(self, client_socket, address):
|
||||
Thread.__init__(self)
|
||||
self.client_socket = client_socket
|
||||
self.client_address = address
|
||||
|
||||
self.api_compat = False # Indicates whether the client has a compatible API version
|
||||
|
||||
def run(self):
|
||||
msg = ''
|
||||
while True:
|
||||
raw_msg = self.client_socket.recv(2048).decode()
|
||||
for char in raw_msg:
|
||||
if char == '\n':
|
||||
msg = msg.rstrip() # Some systems will try to send \r characters... looking at you windows O_O
|
||||
try:
|
||||
response = self.handle_msg(msg)
|
||||
except Exception as e:
|
||||
ui_print("An error occurred while processing a client message")
|
||||
ui_print("Msg: {}".format(msg))
|
||||
ui_print(e)
|
||||
response = "err"
|
||||
self.client_socket.sendall((response + '\n').encode('utf-8'))
|
||||
msg = ''
|
||||
else:
|
||||
msg += char
|
||||
|
||||
def handle_msg(self, message):
|
||||
""" Executes command logic and returns string response (for client). """
|
||||
tokens = message.split(" ")
|
||||
if tokens[0] == "get_api_version":
|
||||
return SOCKET_INTERFACE_API_VERSION
|
||||
elif tokens[0] == "declare_api_version":
|
||||
if tokens[1] == SOCKET_INTERFACE_API_VERSION:
|
||||
self.api_compat = True
|
||||
return "1"
|
||||
else:
|
||||
ui_print("Declared socket API version ({}) is incompatible with current version ({})!".format(tokens[1], SOCKET_INTERFACE_API_VERSION))
|
||||
return "0"
|
||||
else:
|
||||
# api_compat indicates we have checked the api version and are ready to accept commands
|
||||
if self.api_compat:
|
||||
if tokens[0] == "set_raw_field":
|
||||
x = float(tokens[1])
|
||||
y = float(tokens[2])
|
||||
z = float(tokens[3])
|
||||
field_vec = np.array([x, y, z], dtype=np.float32)
|
||||
# uncompensated
|
||||
cage_controls.set_field_simple(field_vec)
|
||||
return "1"
|
||||
elif tokens[0] == "set_compensated_field":
|
||||
x = float(tokens[1])
|
||||
y = float(tokens[2])
|
||||
z = float(tokens[3])
|
||||
field_vec = np.array([x, y, z], dtype=np.float32)
|
||||
# compensated
|
||||
cage_controls.set_field(field_vec)
|
||||
return "1"
|
||||
elif tokens[0] == "set_coil_currents":
|
||||
x = float(tokens[1])
|
||||
y = float(tokens[2])
|
||||
z = float(tokens[3])
|
||||
current_vec = np.array([x, y, z], dtype=np.float32)
|
||||
cage_controls.set_current_vec(current_vec)
|
||||
return "1"
|
||||
else:
|
||||
# The message given is unknown. The programmer probably did not intend for this, so display an error
|
||||
# even if is not inherently problematic.
|
||||
raise Exception("The command '{}' is unknown".format(tokens[0]))
|
||||
else:
|
||||
raise Exception("The command '{}' may not be called before 'declare_api_version'".format(tokens[0]))
|
||||
|
||||
|
||||
class SocketInterfaceThread(Thread):
|
||||
def __init__(self):
|
||||
Thread.__init__(self)
|
||||
self.server_socket = None
|
||||
|
||||
# Can throw exception, which should be passed on to the instantiator of this class
|
||||
self.configure_tcp_port()
|
||||
|
||||
def run(self):
|
||||
while True:
|
||||
(client_socket, address) = self.server_socket.accept()
|
||||
new_thread = ClientConnectionThread(client_socket, address)
|
||||
new_thread.start()
|
||||
ui_print("Accepted connection from {}".format(address))
|
||||
|
||||
def configure_tcp_port(self):
|
||||
# Creates and configures the listening port
|
||||
self.server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
self.server_socket.bind(('', g.SOCKET_PORT))
|
||||
self.server_socket.listen(5) # Limit to max. 5 simultaneous connections
|
||||
ui_print("Listening for TCP connections on port {}".format(g.SOCKET_PORT))
|
||||
Reference in New Issue
Block a user