Commit f0c9daeeb5c3777e531a3078f4e250e19c8595c4
1 parent
861b8b8a60
Exists in
master
Mainly cosmetic changes and add decode/encode to read/write procedure on socket
Showing 1 changed file with 28 additions and 38 deletions Inline Diff
eddsctrl_server/eddsctrlserver
#!/usr/bin/python2.7 | 1 | 1 | #!/usr/bin/python3 | |
# -*- coding: utf-8 -*- | 2 | 2 | # -*- coding: utf-8 -*- | |
3 | 3 | |||
"""package eddsctrl_server | 4 | 4 | """package eddsctrl_server | |
author Benoit Dubois | 5 | 5 | author Benoit Dubois | |
copyright FEMTO ENGINEERING | 6 | 6 | copyright FEMTO ENGINEERING | |
license GPL v3.0+ | 7 | 7 | license GPL v3.0+ | |
brief DDS controller, server part. | 8 | 8 | brief DDS controller, server part. | |
details This package provides interface to handle DDS package. | 9 | 9 | details This package provides interface to handle DDS package. | |
This package implements server part of a client/server architecture. | 10 | 10 | This package implements server part of a client/server architecture. | |
Code inspired from Python Module of the Week website (BSD licence): | 11 | 11 | Code inspired from Python Module of the Week website (BSD licence): | |
http://pymotw.com/2/select/ | 12 | 12 | http://pymotw.com/2/select/ | |
for the logic and from: | 13 | 13 | for the logic and from: | |
http://eli.thegreenplace.net/2011/08/02/length-prefix-framing-for-protocol-buffers/ | 14 | 14 | http://eli.thegreenplace.net/2011/08/02/length-prefix-framing-for-protocol-buffers/ | |
for the message using the length prefix technique. | 15 | 15 | for the message using the length prefix technique. | |
""" | 16 | 16 | """ | |
17 | 17 | |||
import logging | 18 | 18 | # Ctrl-c closes the application | |
CONSOLE_LOG_LEVEL = logging.DEBUG | 19 | 19 | import signal | |
FILE_LOG_LEVEL = logging.DEBUG | 20 | 20 | signal.signal(signal.SIGINT, signal.SIG_DFL) | |
21 | 21 | |||
import os | 22 | 22 | import os | |
try: | 23 | 23 | import logging | |
import configparser | 24 | 24 | import configparser | |
except ImportError: | 25 | 25 | from queue import Queue, Empty | |
import ConfigParser as configparser | 26 | |||
try: | 27 | |||
from queue import Queue, Empty | 28 | |||
except ImportError: | 29 | |||
from Queue import Queue, Empty | 30 | |||
from struct import pack, unpack | 31 | 26 | from struct import pack, unpack | |
from select import select | 32 | 27 | from select import select | |
from socket import socket, AF_INET, SOCK_STREAM | 33 | 28 | from socket import socket, AF_INET, SOCK_STREAM | |
34 | 29 | |||
from dds.ad9912dev import Ad9912Dev as DdsDevice # for actual use | 35 | 30 | from dds.ad9912dev import Ad9912Dev as DdsDevice # for actual use | |
#from dds.dds_emul import TestUsbDds as DdsDevice # for test without DDS | 36 | 31 | #from dds.dds_emul import TestUsbDds as DdsDevice # for test without DDS | |
37 | 32 | from eddsctrl_server.constants import SERVER_PORT, SERVERHOST, \ | ||
from eddsctrl_server.constants import APP_NAME, SERVER_PORT, SERVERHOST, \ | 38 | |||
DEFAULT_IFREQ, DEFAULT_OFREQ, DEFAULT_AMP, DEFAULT_PHY, \ | 39 | 33 | DEFAULT_IFREQ, DEFAULT_OFREQ, DEFAULT_AMP, DEFAULT_PHY, \ | |
CFG_SERVER_FILE, LOG_SERVER_FILE | 40 | 34 | CFG_SERVER_FILE, LOG_SERVER_FILE | |
41 | 35 | |||
36 | CONSOLE_LOG_LEVEL = logging.DEBUG | |||
37 | FILE_LOG_LEVEL = logging.DEBUG | |||
42 | 38 | |||
#============================================================================== | 43 | 39 | #============================================================================== | |
class EDdsCtrlServer(object): | 44 | 40 | class EDdsCtrlServer(object): | |
"""Class dedicated to interface socket client with DDS device. | 45 | 41 | """Class dedicated to interface socket client with DDS device. | |
Provide a basic interface to handle client queries: | 46 | 42 | Provide a basic interface to handle client queries: | |
Message is built using the length prefix technique: length is sent as a | 47 | 43 | Message is built using the length prefix technique: length is sent as a | |
packed 4-byte little-endian integer. | 48 | 44 | packed 4-byte little-endian integer. | |
Allow only 4 queries, changement of output frequency, input frequency, | 49 | 45 | Allow only 4 queries, changement of output frequency, input frequency, | |
phase or amplitude value. | 50 | 46 | phase or amplitude value. | |
Message structure is simple: | 51 | 47 | Message structure is simple: | |
- Length of message, | 52 | 48 | - Length of message, | |
- Exclamation mark | 53 | 49 | - Exclamation mark | |
- Character identifier, | 54 | 50 | - Character identifier, | |
- Exclamation mark | 55 | 51 | - Exclamation mark | |
- Value | 56 | 52 | - Value | |
- Exclamation mark | 57 | 53 | - Exclamation mark | |
There are 8 valid identifiers: | 58 | 54 | There are 8 valid identifiers: | |
- 'o[?]' for set/get output frequency | 59 | 55 | - 'o[?]' for set/get output frequency | |
- 'i[?]' for set/get input frequency | 60 | 56 | - 'i[?]' for set/get input frequency | |
- 'p[?]' for set/get phase | 61 | 57 | - 'p[?]' for set/get phase | |
- 'a[?]' for set/get amplitude | 62 | 58 | - 'a[?]' for set/get amplitude | |
""" | 63 | 59 | """ | |
64 | 60 | |||
def __init__(self, settings_file=None): | 65 | 61 | def __init__(self, settings_file=None): | |
"""Constructor: initialize the server. | 66 | 62 | """Constructor: initialize the server. | |
:param settings_file: file containing setting data value (str) | 67 | 63 | :param settings_file: file containing setting data value (str) | |
:returns: None | 68 | 64 | :returns: None | |
""" | 69 | 65 | """ | |
# Retrieve parameter values from 'ini' file | 70 | 66 | # Retrieve parameter values from 'ini' file | |
if settings_file is None: | 71 | 67 | if settings_file is None: | |
raise ValueError("Parameter settings_file missing.") | 72 | 68 | raise ValueError("Parameter settings_file missing.") | |
self._settingsf = settings_file | 73 | 69 | self._settingsf = settings_file | |
config = configparser.ConfigParser() | 74 | 70 | config = configparser.ConfigParser() | |
config.read(settings_file) | 75 | 71 | config.read(settings_file) | |
try: | 76 | 72 | try: | |
self._port = config.getint('dds_ctrl', 'server_port') | 77 | 73 | self._port = config.getint('dds_ctrl', 'server_port') | |
ifreq = config.getfloat('dds_ctrl', 'ifreq') | 78 | 74 | ifreq = config.getfloat('dds_ctrl', 'ifreq') | |
ofreq = config.getfloat('dds_ctrl', 'ofreq') | 79 | 75 | ofreq = config.getfloat('dds_ctrl', 'ofreq') | |
phase = config.getfloat('dds_ctrl', 'phase') | 80 | 76 | phase = config.getfloat('dds_ctrl', 'phase') | |
amp = config.getint('dds_ctrl', 'amp') | 81 | 77 | amp = config.getint('dds_ctrl', 'amp') | |
except KeyError as ex: | 82 | 78 | except KeyError as ex: | |
logging.critical("Correct or delete the configuration file.") | 83 | 79 | logging.critical("Correct or delete the configuration file.") | |
raise KeyError("Key %s not found in configuration file:"% str(ex)) | 84 | 80 | raise KeyError("Key %s not found in configuration file:"% str(ex)) | |
# Init devices | 85 | 81 | # Init devices | |
self._init_dds(ifreq, ofreq, phase, amp) | 86 | 82 | self._init_dds(ifreq, ofreq, phase, amp) | |
self._init_server() | 87 | 83 | self._init_server() | |
# Start threaded server | 88 | 84 | # Start threaded server | |
self._server_loop() | 89 | 85 | self._server_loop() | |
90 | 86 | |||
def _init_dds(self, ifreq, ofreq, phase, amp): | 91 | 87 | def _init_dds(self, ifreq, ofreq, phase, amp): | |
"""Create and configure a DDS object. | 92 | 88 | """Create and configure a DDS object. | |
:param ifreq: dds input frequency (float) | 93 | 89 | :param ifreq: dds input frequency (float) | |
:param ofreq: dds output frequency (float) | 94 | 90 | :param ofreq: dds output frequency (float) | |
:param phase: dds output phase (float) | 95 | 91 | :param phase: dds output phase (float) | |
:param amp: dds output amplitude (int) | 96 | 92 | :param amp: dds output amplitude (int) | |
:returns: None | 97 | 93 | :returns: None | |
""" | 98 | 94 | """ | |
self._dds = DdsDevice() | 99 | 95 | self._dds = DdsDevice() | |
try: | 100 | 96 | try: | |
self._dds.set_ifreq(ifreq) | 101 | 97 | self._dds.set_ifreq(ifreq) | |
self._dds.set_ofreq(ofreq) | 102 | 98 | self._dds.set_ofreq(ofreq) | |
self._dds.set_phy(phase) | 103 | 99 | self._dds.set_phy(phase) | |
self._dds.set_amp(amp) | 104 | 100 | self._dds.set_amp(amp) | |
self._dds.set_hstl_output_state(False) | 105 | 101 | self._dds.set_hstl_output_state(False) | |
self._dds.set_cmos_output_state(False) | 106 | 102 | self._dds.set_cmos_output_state(False) | |
except Exception as ex: | 107 | 103 | except Exception as ex: | |
raise Exception("Unexpected error during DDS initialisation: %s" \ | 108 | 104 | raise Exception("Unexpected error during DDS initialisation: %s" \ | |
% str(ex)) | 109 | 105 | % str(ex)) | |
logging.debug("DDS inititialization done") | 110 | 106 | logging.debug("DDS inititialization done") | |
111 | 107 | |||
def _init_server(self): | 112 | 108 | def _init_server(self): | |
"""Create and configure a basic server object. | 113 | 109 | """Create and configure a basic server object. | |
:returns: None | 114 | 110 | :returns: None | |
""" | 115 | 111 | """ | |
try: | 116 | 112 | try: | |
self.server = socket(AF_INET, SOCK_STREAM) | 117 | 113 | self.server = socket(AF_INET, SOCK_STREAM) | |
self.server.setblocking(0) | 118 | 114 | self.server.setblocking(0) | |
host = SERVERHOST # Get local machine name | 119 | 115 | host = SERVERHOST # Get local machine name | |
self.server.bind((host, self._port)) # Bind to the port | 120 | 116 | self.server.bind((host, self._port)) # Bind to the port | |
self.server.listen(3) # Now wait for client connection | 121 | 117 | self.server.listen(3) # Now wait for client connection | |
except IOError as ex: | 122 | 118 | except IOError as ex: | |
raise IOError("EDDSCTRLSERVER: %s, have you closed properly the " \ | 123 | 119 | raise IOError("EDDSCTRLSERVER: %s, have you closed properly the " \ | |
"server? " % str(ex)) | 124 | 120 | "server? " % str(ex)) | |
except Exception as ex: | 125 | 121 | except Exception as ex: | |
raise Exception("EDDSCTRLSERVER: unexpected error during server " \ | 126 | 122 | raise Exception("EDDSCTRLSERVER: unexpected error during server " \ | |
"initialisation: %s" % str(ex)) | 127 | 123 | "initialisation: %s" % str(ex)) | |
# Sockets from which we expect to read | 128 | 124 | # Sockets from which we expect to read | |
self.inputs = [self.server] | 129 | 125 | self.inputs = [self.server] | |
# Sockets to which we expect to write | 130 | 126 | # Sockets to which we expect to write | |
self.outputs = [] | 131 | 127 | self.outputs = [] | |
# Outgoing message queues (socket:Queue) | 132 | 128 | # Outgoing message queues (socket:Queue) | |
self.message_queues = {} | 133 | 129 | self.message_queues = {} | |
logging.debug("Server initialization done") | 134 | 130 | logging.debug("Server initialization done") | |
135 | 131 | |||
def _server_loop(self): | 136 | 132 | def _server_loop(self): | |
"""Main server loop: Handle connection, read and write on server socket. | 137 | 133 | """Main server loop: Handle connection, read and write on server socket. | |
:returns: None | 138 | 134 | :returns: None | |
""" | 139 | 135 | """ | |
while self.inputs: | 140 | 136 | while self.inputs: | |
# Await an event on a readable socket descriptor | 141 | 137 | # Await an event on a readable socket descriptor | |
readable, writable, exceptional = select(self.inputs, \ | 142 | 138 | readable, writable, exceptional = select(self.inputs, \ | |
self.outputs, \ | 143 | 139 | self.outputs, \ | |
self.inputs) | 144 | 140 | self.inputs) | |
# Handle inputs | 145 | 141 | # Handle inputs | |
self._handle_readable(readable) | 146 | 142 | self._handle_readable(readable) | |
# Handle outputs | 147 | 143 | # Handle outputs | |
self._handle_writable(writable) | 148 | 144 | self._handle_writable(writable) | |
# Handle exceptional condition | 149 | 145 | # Handle exceptional condition | |
self._handle_exceptional(exceptional) | 150 | 146 | self._handle_exceptional(exceptional) | |
151 | 147 | |||
def _handle_readable(self, socket_list): | 152 | 148 | def _handle_readable(self, socket_list): | |
"""Read message from client. | 153 | 149 | """Read message from client. | |
Message is built using the length prefix technique. See: | 154 | 150 | Message is built using the length prefix technique. See: | |
http://eli.thegreenplace.net/2011/08/02/length-prefix-framing-for-protocol-buffers/ | 155 | 151 | http://eli.thegreenplace.net/2011/08/02/length-prefix-framing-for-protocol-buffers/ | |
:param socket_list: List of socket usable to receive data | 156 | 152 | :param socket_list: List of socket usable to receive data | |
:returns: None | 157 | 153 | :returns: None | |
""" | 158 | 154 | """ | |
for sock in socket_list: | 159 | 155 | for sock in socket_list: | |
# Received a connect to the server (listening) socket | 160 | 156 | # Received a connect to the server (listening) socket | |
if sock is self.server: | 161 | 157 | if sock is self.server: | |
self._accept_connect() | 162 | 158 | self._accept_connect() | |
# Established connection | 163 | 159 | # Established connection | |
else: | 164 | 160 | else: | |
header_data = self._recv_n_bytes(sock, 4) | 165 | 161 | header_data = self._recv_n_bytes(sock, 4) | |
if header_data == None: | 166 | 162 | if header_data is None: | |
logging.error("Message seems received but no data to read") | 167 | 163 | logging.error("Message seems received but no data to read") | |
return | 168 | 164 | return | |
if len(header_data) == 4: | 169 | 165 | if len(header_data) == 4: | |
msg_len = unpack('<L', header_data)[0] | 170 | 166 | msg_len = unpack('<L', header_data)[0] | |
data = self._recv_n_bytes(sock, msg_len) | 171 | 167 | data = self._recv_n_bytes(sock, msg_len).decode('utf-8') | |
# Check message validity | 172 | 168 | # Check message validity | |
if data == None: | 173 | 169 | if data is None: | |
logging.error("Message received but no data to read") | 174 | 170 | logging.error("Message received but no data to read") | |
return | 175 | 171 | return | |
if len(data) != msg_len: | 176 | 172 | if len(data) != msg_len: | |
logging.error("Bad message length") | 177 | 173 | logging.error("Bad message length") | |
return | 178 | 174 | return | |
logging.debug("receive %s from %s", \ | 179 | 175 | logging.debug("receive %s from %s", \ | |
data, sock.getpeername()) | 180 | 176 | data, sock.getpeername()) | |
# Handle message | 181 | 177 | # Handle message | |
ret_data = self._input_msg_handler(data) | 182 | 178 | ret_data = self._input_msg_handler(data) | |
# Return data to client | 183 | 179 | # Return data to client | |
self.message_queues[sock].put(ret_data) | 184 | 180 | self.message_queues[sock].put(ret_data) | |
# Add output channel for response | 185 | 181 | # Add output channel for response | |
if sock not in self.outputs: | 186 | 182 | if sock not in self.outputs: | |
self.outputs.append(sock) | 187 | 183 | self.outputs.append(sock) | |
else: | 188 | 184 | else: | |
# Interpret empty result as closed connection | 189 | 185 | # Interpret empty result as closed connection | |
logging.info("Closing connection after reading no data") | 190 | 186 | logging.info("Closing connection after reading no data") | |
# Stop listening for input on the connection | 191 | 187 | # Stop listening for input on the connection | |
if sock in self.outputs: | 192 | 188 | if sock in self.outputs: | |
self.outputs.remove(sock) | 193 | 189 | self.outputs.remove(sock) | |
self.inputs.remove(sock) | 194 | 190 | self.inputs.remove(sock) | |
sock.close() | 195 | 191 | sock.close() | |
# Remove message queue | 196 | 192 | # Remove message queue | |
del self.message_queues[sock] | 197 | 193 | del self.message_queues[sock] | |
198 | 194 | |||
def _handle_writable(self, socket_list): | 199 | 195 | def _handle_writable(self, socket_list): | |
"""Send message to client. | 200 | 196 | """Send message to client. | |
:param socket_list: List of socket object usable to send data | 201 | 197 | :param socket_list: List of socket object usable to send data | |
:returns: None | 202 | 198 | :returns: None | |
""" | 203 | 199 | """ | |
for sock in socket_list: | 204 | 200 | for sock in socket_list: | |
try: | 205 | 201 | try: | |
next_msg = self.message_queues[sock].get_nowait() | 206 | 202 | next_msg = self.message_queues[sock].get_nowait() | |
except Empty: | 207 | 203 | except Empty: | |
self.outputs.remove(sock) | 208 | 204 | self.outputs.remove(sock) | |
else: | 209 | 205 | else: | |
self._send_msg(sock, next_msg) | 210 | 206 | self._send_msg(sock, next_msg) | |
logging.debug("send %s to %s", next_msg, sock.getpeername()) | 211 | 207 | logging.debug("send %s to %s", next_msg, sock.getpeername()) | |
212 | 208 | |||
def _handle_exceptional(self, socket_list): | 213 | 209 | def _handle_exceptional(self, socket_list): | |
"""Handle error with socket by closing it. | 214 | 210 | """Handle error with socket by closing it. | |
:param socket_list: List of socket object in exceptional condition | 215 | 211 | :param socket_list: List of socket object in exceptional condition | |
:returns: None | 216 | 212 | :returns: None | |
""" | 217 | 213 | """ | |
try: | 218 | 214 | try: | |
for sock in socket_list: | 219 | 215 | for sock in socket_list: | |
logging.warning("handling exceptional condition for %s", \ | 220 | 216 | logging.warning("handling exceptional condition for %s", \ | |
sock.getpeername()) | 221 | 217 | sock.getpeername()) | |
# Stop listening for input on the connection | 222 | 218 | # Stop listening for input on the connection | |
self.inputs.remove(sock) | 223 | 219 | self.inputs.remove(sock) | |
if sock in outputs: | 224 | 220 | if sock in self.outputs: | |
self.outputs.remove(sock) | 225 | 221 | self.outputs.remove(sock) | |
sock.close() | 226 | 222 | sock.close() | |
# Remove message queue | 227 | 223 | # Remove message queue | |
del self.message_queues[sock] | 228 | 224 | del self.message_queues[sock] | |
except Exception as ex: | 229 | 225 | except Exception as ex: | |
logging.error("Unexpected error: %s", ex) | 230 | 226 | logging.error("Unexpected error: %s", ex) | |
231 | 227 | |||
def _input_msg_handler(self, msg): | 232 | 228 | def _input_msg_handler(self, msg): | |
"""Handle message from clients. Message format is defined in | 233 | 229 | """Handle message from clients. Message format is defined in | |
:class:`eddsctrl.server.eddsctrlsserver.EDdsCtrlServer`. | 234 | 230 | :class:`eddsctrl.server.eddsctrlsserver.EDdsCtrlServer`. | |
Message contains a command to update DDS device state. | 235 | 231 | Message contains a command to update DDS device state. | |
:param msg: A formated string message (str) | 236 | 232 | :param msg: A formated string message (str) | |
:returns: Return actual value of parameter in DDS (float) | 237 | 233 | :returns: Return actual value of parameter in DDS (float) | |
""" | 238 | 234 | """ | |
split_msg = msg.split("!") | 239 | 235 | split_msg = msg.split("!") | |
index = split_msg[1].strip('\0') # Remove extra binary NULL characters | 240 | 236 | index = split_msg[1].strip('\0') # Remove extra binary NULL characters | |
value = split_msg[2] | 241 | 237 | value = split_msg[2] | |
config = configparser.ConfigParser() | 242 | 238 | config = configparser.ConfigParser() | |
config.read(self._settingsf) | 243 | 239 | config.read(self._settingsf) | |
if index == "o": | 244 | 240 | if index == "o": | |
retval = self._dds.set_ofreq(float(value)) | 245 | 241 | retval = self._dds.set_ofreq(float(value)) | |
config.set('dds_ctrl', 'ofreq', str(self._dds.get_ofreq())) | 246 | 242 | config.set('dds_ctrl', 'ofreq', str(self._dds.get_ofreq())) | |
elif index == "p": | 247 | 243 | elif index == "p": | |
retval = self._dds.set_phy(float(value)) | 248 | 244 | retval = self._dds.set_phy(float(value)) | |
config.set('dds_ctrl', 'phase', str(self._dds.get_phy())) | 249 | 245 | config.set('dds_ctrl', 'phase', str(self._dds.get_phy())) | |
elif index == "a": | 250 | 246 | elif index == "a": | |
retval = self._dds.set_amp(int(value)) | 251 | 247 | retval = self._dds.set_amp(int(value)) | |
config.set('dds_ctrl', 'amp', str(self._dds.get_amp())) | 252 | 248 | config.set('dds_ctrl', 'amp', str(self._dds.get_amp())) | |
elif index == "i": | 253 | 249 | elif index == "i": | |
retval = self._dds.set_ifreq(float(value)) | 254 | 250 | retval = self._dds.set_ifreq(float(value)) | |
config.set('dds_ctrl', 'ifreq', str(self._dds.get_ifreq())) | 255 | 251 | config.set('dds_ctrl', 'ifreq', str(self._dds.get_ifreq())) | |
elif index == "o?": | 256 | 252 | elif index == "o?": | |
retval = self._dds.get_ofreq() | 257 | 253 | retval = self._dds.get_ofreq() | |
elif index == "p?": | 258 | 254 | elif index == "p?": | |
retval = self._dds.get_phy() | 259 | 255 | retval = self._dds.get_phy() | |
elif index == "a?": | 260 | 256 | elif index == "a?": | |
retval = self._dds.get_amp() | 261 | 257 | retval = self._dds.get_amp() | |
elif index == "i?": | 262 | 258 | elif index == "i?": | |
retval = self._dds.get_ifreq() | 263 | 259 | retval = self._dds.get_ifreq() | |
else: # Bad identifier, message not valid | 264 | 260 | else: # Bad identifier, message not valid | |
logging.error("Bad identifier," \ | 265 | 261 | logging.error("Bad identifier, expected o[?], p[?], a[?] or i[?]: " | |
"expected o[?], p[?], a[?] or i[?]: ", \ | 266 | 262 | + index + " given") | |
index, " given") | 267 | |||
return | 268 | 263 | return | |
# Write modification to setting file | 269 | 264 | # Write modification to setting file | |
with open(self._settingsf, 'w') as fd: | 270 | 265 | with open(self._settingsf, 'w') as fd: | |
config.write(fd) | 271 | 266 | config.write(fd) | |
# Note that value actually writed in DDS (retval) can be a bit different | 272 | 267 | # Note that value actually writed in DDS (retval) can be a bit different | |
# than value sended to DDS (value). | 273 | 268 | # than value sended to DDS (value). | |
274 | ||||
275 | ||||
276 | ||||
msg = "!" + index + "!" + str(retval) + "!" | 277 | 269 | msg = "!" + index + "!" + str(retval) + "!" | |
return msg | 278 | 270 | return msg | |
279 | 271 | |||
@staticmethod | 280 | 272 | @staticmethod | |
def _send_msg(sock, msg): | 281 | 273 | def _send_msg(sock, msg): | |
"""Method for sending 'msg' to socket. | 282 | 274 | """Method for sending 'msg' to socket. | |
Message is built using the length prefix technique. See: | 283 | 275 | Message is built using the length prefix technique. See: | |
http://eli.thegreenplace.net/2011/08/02/length-prefix-framing-for-protocol-buffers/ | 284 | 276 | http://eli.thegreenplace.net/2011/08/02/length-prefix-framing-for-protocol-buffers/ | |
:param sock: a valid socket object | 285 | 277 | :param sock: a valid socket object | |
:param msg: message to be send (str) | 286 | 278 | :param msg: message to be send (str) | |
:returns: None | 287 | 279 | :returns: None | |
""" | 288 | 280 | """ | |
header = pack('<L', len(msg)) | 289 | 281 | header = pack('<L', len(msg)) | |
try: | 290 | 282 | try: | |
sock.sendall(header + msg) | 291 | 283 | sock.sendall(header + msg.encode('utf-8')) | |
except IOError as ex: | 292 | 284 | except IOError as ex: | |
logging.error("Error during sending: %s", ex) | 293 | 285 | logging.error("Error during sending: %s", ex) | |
294 | 286 | |||
@staticmethod | 295 | 287 | @staticmethod | |
def _recv_n_bytes(sock, n): | 296 | 288 | def _recv_n_bytes(sock, nb): | |
"""Convenience method for receiving exactly n bytes from socket | 297 | 289 | """Convenience method for receiving exactly n bytes from socket | |
(assuming it's open and connected). | 298 | 290 | (assuming it's open and connected). | |
:param sock: socket object which receives the n bytes | 299 | 291 | :param sock: socket object which receives the n bytes | |
:param n: number of bytes to be received (int) | 300 | 292 | :param nb: number of bytes to be received (int) | |
:returns: data received (str) | 301 | 293 | :returns: data received (str) | |
""" | 302 | 294 | """ | |
data = '' | 303 | 295 | data = '' | |
while len(data) < n: | 304 | 296 | while len(data) < nb: | |
try: | 305 | 297 | try: | |
chunk = sock.recv(n - len(data)) | 306 | 298 | chunk = sock.recv(nb - len(data)) | |
if chunk == '': | 307 | 299 | if chunk == '': | |
break | 308 | 300 | break | |
data += chunk | 309 | 301 | data += chunk | |
except IOError as ex: | 310 | 302 | except IOError as ex: | |
logging.error("Socket error in _recv_n_bytes: %s", ex) | 311 | 303 | logging.error("Socket error in _recv_n_bytes: %s", ex) | |
return | 312 | 304 | return | |
except Exception as ex: | 313 | 305 | except Exception as ex: | |
logging.error("Error in _recv_n_bytes: %s", ex) | 314 | 306 | logging.error("Error in _recv_n_bytes: %s", ex) | |
return | 315 | 307 | return | |
return data | 316 | 308 | return data | |
317 | 309 | |||
def _accept_connect(self): | 318 | 310 | def _accept_connect(self): | |
"""Server accept connection from client. | 319 | 311 | """Server accept connection from client. | |
:returns: a new socket object related to client connection (socket) | 320 | 312 | :returns: a new socket object related to client connection (socket) | |
""" | 321 | 313 | """ | |
# A "readable" server socket is ready to accept a connection | 322 | 314 | # A "readable" server socket is ready to accept a connection | |
connection, client_address = self.server.accept() | 323 | 315 | connection, client_address = self.server.accept() | |
logging.info("New connection from %s", client_address) | 324 | 316 | logging.info("New connection from %s", client_address) | |
connection.setblocking(0) | 325 | 317 | connection.setblocking(0) | |
self.inputs.append(connection) | 326 | 318 | self.inputs.append(connection) | |
self.outputs.append(connection) | 327 | 319 | self.outputs.append(connection) | |
# Give the connection a queue for data we want to send | 328 | 320 | # Give the connection a queue for data we want to send | |
self.message_queues[connection] = Queue() | 329 | 321 | self.message_queues[connection] = Queue() | |
return connection | 330 | 322 | return connection | |
331 | 323 | |||
332 | 324 | |||
#============================================================================== | 333 | 325 | #============================================================================== | |
def reset_settings(settings_file): | 334 | 326 | def reset_settings(settings_file): | |
"""Resets the "settings" file with default values. | 335 | 327 | """Resets the "settings" file with default values. | |
:param settings_file: file containing setting data value (str) | 336 | 328 | :param settings_file: file containing setting data value (str) | |
:returns: None | 337 | 329 | :returns: None | |
""" | 338 | 330 | """ | |
config = configparser.ConfigParser() | 339 | 331 | config = configparser.ConfigParser() | |
config.add_section('dds_ctrl') | 340 | 332 | config.add_section('dds_ctrl') | |
config.set('dds_ctrl', 'server_port', str(SERVER_PORT)) | 341 | 333 | config.set('dds_ctrl', 'server_port', str(SERVER_PORT)) | |
config.set('dds_ctrl', 'ofreq', str(DEFAULT_OFREQ)) | 342 | 334 | config.set('dds_ctrl', 'ofreq', str(DEFAULT_OFREQ)) | |
config.set('dds_ctrl', 'phase', str(DEFAULT_PHY)) | 343 | 335 | config.set('dds_ctrl', 'phase', str(DEFAULT_PHY)) | |
config.set('dds_ctrl', 'amp', str(DEFAULT_AMP)) | 344 | 336 | config.set('dds_ctrl', 'amp', str(DEFAULT_AMP)) | |
config.set('dds_ctrl', 'ifreq', str(DEFAULT_IFREQ)) | 345 | 337 | config.set('dds_ctrl', 'ifreq', str(DEFAULT_IFREQ)) | |
# Write modification to setting file | 346 | 338 | # Write modification to setting file | |
with open(settings_file, 'w') as fd: | 347 | 339 | with open(settings_file, 'w') as fd: | |
fd.truncate(0) # Reset file contents | 348 | 340 | fd.truncate(0) # Reset file contents | |
config.write(fd) | 349 | 341 | config.write(fd) | |
logging.info("Settings file reseted.") | 350 | 342 | logging.info("Settings file reseted.") | |
351 | 343 | |||
352 | 344 | |||
#============================================================================== | 353 | 345 | #============================================================================== | |
def configure_logging(): | 354 | 346 | def configure_logging(): | |
"""Configures logs. | 355 | 347 | """Configures logs. | |
""" | 356 | 348 | """ | |
date_fmt = "%d/%m/%Y %H:%M:%S" | 357 | 349 | date_fmt = "%d/%m/%Y %H:%M:%S" | |
log_format = "%(asctime)s %(levelname) -8s %(filename)s " + \ | 358 | 350 | log_format = "%(asctime)s %(levelname) -8s %(filename)s " + \ | |
" %(funcName)s (%(lineno)d): %(message)s" | 359 | 351 | " %(funcName)s (%(lineno)d): %(message)s" | |
logging.basicConfig(level=FILE_LOG_LEVEL, \ | 360 | 352 | logging.basicConfig(level=FILE_LOG_LEVEL, \ | |
datefmt=date_fmt, \ | 361 | 353 | datefmt=date_fmt, \ | |
format=log_format, \ | 362 | 354 | format=log_format, \ | |
filename=LOG_SERVER_FILE, \ | 363 | 355 | filename=LOG_SERVER_FILE, \ | |
filemode='w') | 364 | 356 | filemode='w') | |
console = logging.StreamHandler() | 365 | 357 | console = logging.StreamHandler() | |
# define a Handler which writes messages to the sys.stderr | 366 | 358 | # define a Handler which writes messages to the sys.stderr | |
console.setLevel(CONSOLE_LOG_LEVEL) | 367 | 359 | console.setLevel(CONSOLE_LOG_LEVEL) | |
# set a format which is simpler for console use | 368 | 360 | # set a format which is simpler for console use | |
console_format = '%(levelname) -8s %(filename)s (%(lineno)d): %(message)s' | 369 | 361 | console_format = '%(levelname) -8s %(filename)s (%(lineno)d): %(message)s' | |
formatter = logging.Formatter(console_format) | 370 | 362 | formatter = logging.Formatter(console_format) | |
# tell the handler to use this format | 371 | 363 | # tell the handler to use this format | |
console.setFormatter(formatter) | 372 | 364 | console.setFormatter(formatter) | |
# add the handler to the root logger | 373 | 365 | # add the handler to the root logger | |
logging.getLogger('').addHandler(console) | 374 | 366 | logging.getLogger('').addHandler(console) | |
375 | 367 | |||
376 | 368 | |||
#============================================================================== | 377 | 369 | #============================================================================== | |
def eddsctrlserver(settings_file): | 378 | 370 | def main(settings_file): | |
371 | """Main | |||
372 | """ | |||
configure_logging() | 379 | 373 | configure_logging() | |
if os.path.isfile(settings_file) is False: | 380 | 374 | if os.path.isfile(settings_file) is False: | |
logging.error("Settings file missing: create one with default values.") | 381 | 375 | logging.error("Settings file missing: create one with default values.") | |
reset_settings(settings_file) | 382 | 376 | reset_settings(settings_file) | |
EDdsCtrlServer(settings_file) | 383 | 377 | EDdsCtrlServer(settings_file) | |
384 | 378 | |||
385 | 379 | |||
#============================================================================== | 386 | 380 | #============================================================================== |