Skip to content
1 change: 1 addition & 0 deletions cpp/frameProcessor/src/FrameProcessorController.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ void FrameProcessorController::handleCtrlChannel()
try {
OdinData::IpcMessage ctrlMsg(ctrlMsgEncoded.c_str());
OdinData::IpcMessage replyMsg; // Instantiate default IpmMessage
replyMsg.set_msg_type(OdinData::IpcMessage::MsgTypeAck);
replyMsg.set_msg_val(ctrlMsg.get_msg_val());
msg_id = ctrlMsg.get_msg_id();
replyMsg.set_msg_id(msg_id);
Expand Down
76 changes: 51 additions & 25 deletions python/src/odin_data/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,20 +26,26 @@ class OdinDataClient(object):
This class implements the odin-data command-line client.
"""

def __init__(self):
def __init__(self, is_cli=False, ctrl_endpoint=None, log_level=logging.WARNING):
"""Initialise the odin-data client."""
self.is_cli = is_cli

prog_name = os.path.basename(sys.argv[0])

# Parse command line arguments
self.args = self._parse_arguments(prog_name)
if is_cli:
# Parse command line arguments
self.args = self._parse_arguments(prog_name)
log_level = logging.DEBUG
else:
self.args = None

# create logger
self.logger = logging.getLogger(prog_name)
self.logger.setLevel(logging.DEBUG)
self.logger.setLevel(log_level)

# create console handler and set level to debug
# create console handler
ch = logging.StreamHandler(sys.stdout)
ch.setLevel(logging.DEBUG)
ch.setLevel(log_level)

# create formatter
formatter = logging.Formatter('%(asctime)s %(levelname)s %(name)s - %(message)s')
Expand All @@ -50,12 +56,20 @@ def __init__(self):
# add ch to logger
self.logger.addHandler(ch)

# When not in CLI mode the ctrl_endpoint must be given
if is_cli:
self.ctrl_endpoint = self.args.ctrl_endpoint
else:
assert ctrl_endpoint, "The ctrl_endpoint must be given"
self.ctrl_endpoint = ctrl_endpoint

# Create the appropriate IPC channels
self.ctrl_channel = IpcChannel(IpcChannel.CHANNEL_TYPE_DEALER)
self.ctrl_channel.connect(self.args.ctrl_endpoint)
self.ctrl_channel.connect(self.ctrl_endpoint)

self._msg_id = 0
self._run = True
self._response = None

def _parse_arguments(self, prog_name=sys.argv[0]):
"""Parse arguments from the command line."""
Expand Down Expand Up @@ -104,7 +118,7 @@ def run(self):
if self.args.shutdown:
self.do_shutdown_cmd()

def do_config_cmd(self, config_file):
def do_config_cmd(self, config_file, timeout_ms=1000):
"""Send a configure command to odin-data with the specified JSON configuration file."""
try:
config_params = json.load(config_file)
Expand All @@ -117,50 +131,62 @@ def do_config_cmd(self, config_file):
"Sending configure command to the odin-data application with specified parameters"
)
self.ctrl_channel.send(config_msg.encode())
self.await_response()
self.await_response(timeout_ms)
return self._response

except JSONDecodeError as e:
self.logger.error("Failed to parse configuration file: {}".format(e))

def do_status_cmd(self):
def do_status_cmd(self, timeout_ms=1000):
"""Send a status command to odin-data."""
status_msg = IpcMessage('cmd', 'status', id=self._next_msg_id())
self.logger.info("Sending status request to the odin-data application")
id = self._next_msg_id()
status_msg = IpcMessage('cmd', 'status', id=id)
self.logger.info(f"Sending status request to the odin-data application {id}")
self.ctrl_channel.send(status_msg.encode())
self.await_response()
self.await_response(timeout_ms)
return self._response

def do_request_config_cmd(self):
def do_request_config_cmd(self, timeout_ms=1000):
"""Send a request configuration command to odin-data."""
status_msg = IpcMessage('cmd', 'request_configuration', id=self._next_msg_id())
self.logger.info("Sending configuration request to the odin-data application")
self.ctrl_channel.send(status_msg.encode())
self.await_response()
self.await_response(timeout_ms)
return self._response

def do_request_command_cmd(self):
def do_request_command_cmd(self, timeout_ms=1000):
"""Send a request commands command to odin-data"""
status_msg = IpcMessage('cmd', 'request_commands', id=self._next_msg_id())
self.logger.info("Sending command request to the odin-data application")
self.ctrl_channel.send(status_msg.encode())
self.await_response()
self.await_response(timeout_ms)
return self._response

def do_shutdown_cmd(self):
def do_shutdown_cmd(self, timeout_ms=1000):
"""Send a shutdown command to odin-data."""
shutdown_msg = IpcMessage('cmd', 'shutdown', id=self._next_msg_id())
self.logger.info("Sending shutdown command to the odin-data application")
self.ctrl_channel.send(shutdown_msg.encode())
self.await_response()
self.await_response(timeout_ms)
return self._response

def await_response(self, timeout_ms=1000):
def await_response(self, timeout_ms):
"""Await a response to a client command."""
pollevts = self.ctrl_channel.poll(1000)
pollevts = self.ctrl_channel.poll(timeout_ms)
if pollevts == IpcChannel.POLLIN:
reply = IpcMessage(from_str=self.ctrl_channel.recv())
self.logger.info("Got response: {}".format(reply))

self._response = IpcMessage(from_str=self.ctrl_channel.recv())
if self.is_cli:
self.logger.info("Got response: {}".format(self._response))
else:
# For non CLI return the dict of attributes from the response message
assert isinstance(self._response, IpcMessage), self._response
self._response = self._response.attrs
else:
self._response = None

def main():
"""Run the odin-data client."""
app = OdinDataClient()
app = OdinDataClient(is_cli=True)
app.run()


Expand Down
4 changes: 2 additions & 2 deletions python/src/odin_data/control/live_view_adapter.py
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,7 @@ def create_image_from_socket(self, msg):
dtype = 'float32'

# create a np array of the image data, of type specified in the frame header
img_data = np.fromstring(msg[1], dtype=np.dtype(dtype))
img_data = np.frombuffer(msg[1], dtype=np.dtype(dtype))

self.img_data = img_data.reshape([int(header["shape"][0]), int(header["shape"][1])])
self.header = header
Expand Down Expand Up @@ -289,7 +289,7 @@ def render_image(self, colormap=None, clip_min=None, clip_max=None):
# Most time consuming step, depending on image size and the type of image
img_encode = cv2.imencode(
'.png', img_colormapped, params=[cv2.IMWRITE_PNG_COMPRESSION, 0])[1]
return img_encode.tostring()
return img_encode.tobytes()

@staticmethod
def scale_array(src, tmin, tmax):
Expand Down
Loading
Loading