accessi
Advanced tools
+1
-1
| Metadata-Version: 2.1 | ||
| Name: accessi | ||
| Version: 0.0.3 | ||
| Version: 0.0.4 | ||
| Summary: Library for Siemens Access-i MR Scanner Interface to integrate and control the MR Scanner. | ||
@@ -5,0 +5,0 @@ Author-email: Martin Reinok <m.reinok@student.utwente.nl> |
+1
-1
| [project] | ||
| name = "accessi" | ||
| version = "0.0.3" | ||
| version = "0.0.4" | ||
| authors = [ | ||
@@ -5,0 +5,0 @@ { name="Martin Reinok", email="m.reinok@student.utwente.nl" }, |
| Metadata-Version: 2.1 | ||
| Name: accessi | ||
| Version: 0.0.3 | ||
| Version: 0.0.4 | ||
| Summary: Library for Siemens Access-i MR Scanner Interface to integrate and control the MR Scanner. | ||
@@ -5,0 +5,0 @@ Author-email: Martin Reinok <m.reinok@student.utwente.nl> |
+98
-26
@@ -5,3 +5,2 @@ """ | ||
| """ | ||
| import asyncio | ||
| # TODO: TemplateSelection Service Not implemented. | ||
@@ -18,9 +17,9 @@ # TODO: Patient Service Not implemented. | ||
| from typing import Literal | ||
| import numpy as np | ||
| import websockets | ||
| import requests | ||
| import urllib3 | ||
| import math | ||
| import asyncio | ||
| import json | ||
| import ssl | ||
| import sys | ||
@@ -53,3 +52,7 @@ urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) | ||
| @staticmethod | ||
| def websocket_default_url(): | ||
| return f"wss://{config.ip_address}:{config.websocket_port}/SRC?sessionId={config.session_id}" | ||
| class Remote: | ||
@@ -88,3 +91,3 @@ """ | ||
| Before any of the services described in this document can be used, | ||
| a RC must register providing a valid authentication key (provided by your point of contact at Siemens Healthineers), | ||
| an RC must register providing a valid authentication key (provided by your point of contact at Siemens Healthineers), | ||
| which also encodes the scope of functionality that can be used by the RC. | ||
@@ -153,3 +156,3 @@ """ | ||
| has the mastership with respect to controlling the workflow. | ||
| As soon as a RC releases the control, the MR host is automatically in charge. | ||
| As soon as an RC releases the control, the MR host is automatically in charge. | ||
| The RC can only retrieve the mastership if a patient is registered and no sequence is running or open. | ||
@@ -423,3 +426,3 @@ """ | ||
| @staticmethod | ||
| def set_slice_position_dcs(index=0, x=None, y=None, z=None, allow_side_effects=True): | ||
| def set_slice_position_dcs(x=None, y=None, z=None, allow_side_effects=True, index=0): | ||
| """ | ||
@@ -435,3 +438,3 @@ Response example: | ||
| "allowSideEffects": allow_side_effects} | ||
| return send_request(url, data) | ||
| return send_request(url, data, "POST") | ||
@@ -452,6 +455,4 @@ @staticmethod | ||
| @staticmethod | ||
| def set_slice_orientation_dcs(index=0, allow_side_effects=True, | ||
| normal: tuple = (0, 0, 0), | ||
| phase: tuple = (0, 0, 0), | ||
| read: tuple = (0, 0, 0)): | ||
| def set_slice_orientation_dcs(normal: tuple = (0, 0, 0), phase: tuple = (0, 0, 0), read: tuple = (0, 0, 0), | ||
| allow_side_effects=True, index=0): | ||
| """ | ||
@@ -474,2 +475,36 @@ Response example: | ||
| @staticmethod | ||
| def set_slice_orientation_degrees(x_deg, y_deg, z_deg, allow_side_effects=True, index=0): | ||
| """ | ||
| Response example: | ||
| - "result":{"success":true,"reason":"ok","time":"20170608T143325.423"}, | ||
| - "normalSet":{"x":0,"y":0,"z":-1.0}, | ||
| - "phaseSet":{"x":0,"y":-1.0,"z":0}, | ||
| - "readSet":{"x":-1.0,"y":0,"z":0} | ||
| Don't really understand how the math works | ||
| """ | ||
| # Convert degrees to radians | ||
| x_rad = np.radians(x_deg) | ||
| y_rad = np.radians(y_deg) | ||
| z_rad = np.radians(z_deg) | ||
| # Calculate the rotation matrices around each axis | ||
| Rx = np.array([[1, 0, 0], [0, np.cos(x_rad), -np.sin(x_rad)], [0, np.sin(x_rad), np.cos(x_rad)]]) | ||
| Ry = np.array([[np.cos(y_rad), 0, np.sin(y_rad)], [0, 1, 0], [-np.sin(y_rad), 0, np.cos(y_rad)]]) | ||
| Rz = np.array([[np.cos(z_rad), -np.sin(z_rad), 0], [np.sin(z_rad), np.cos(z_rad), 0], [0, 0, 1]]) | ||
| # Combine the rotation matrices to get the overall rotation matrix | ||
| R = np.dot(Rz, np.dot(Ry, Rx)) | ||
| # Extract the orientation vectors (normal, phase, read) from the rotation matrix | ||
| normal = R[:, 2] # Third column | ||
| phase = R[:, 1] # Second column | ||
| read = R[:, 0] # First column | ||
| # Normalize the vectors | ||
| normal /= np.linalg.norm(normal) | ||
| phase /= np.linalg.norm(phase) | ||
| read /= np.linalg.norm(read) | ||
| return ParameterStandard.set_slice_orientation_dcs(normal, phase, read, allow_side_effects, index) | ||
| @staticmethod | ||
| def get_slice_thickness(): | ||
@@ -487,2 +522,14 @@ """ | ||
| @staticmethod | ||
| def get_field_of_view_read(): | ||
| """ | ||
| Unit:mm | ||
| Response example: | ||
| - "result":{"success":true,"reason":"ok","time":"20170608T143325.423"}, | ||
| - "value": 400.0 | ||
| """ | ||
| url = f"{config.base_url()}/parameter/standard/getFieldOfViewRead" | ||
| data = {"sessionId": config.session_id} | ||
| return send_request(url, data, "GET") | ||
| @staticmethod | ||
| def set_slice_thickness(value, allow_side_effects=True): | ||
@@ -499,3 +546,27 @@ """ | ||
| @staticmethod | ||
| def get_base_resolution(): | ||
| """ | ||
| Unit:mm | ||
| Response example: | ||
| - "result":{"success":true,"reason":"ok","time":"20170608T143325.423"}, | ||
| - "value":128 | ||
| """ | ||
| url = f"{config.base_url()}/parameter/standard/getBaseResolution" | ||
| data = {"sessionId": config.session_id} | ||
| return send_request(url, data, "GET") | ||
| @staticmethod | ||
| def set_base_resolution(value, allow_side_effects=True): | ||
| """ | ||
| Unit:mm | ||
| Response example: | ||
| - "result":{"success":true,"reason":"ok","time":"20170608T143325.423"}, | ||
| - "valueSet":192 | ||
| """ | ||
| url = f"{config.base_url()}/parameter/standard/setBaseResolution" | ||
| data = {"sessionId": config.session_id, "value": value, "allowSideEffects": allow_side_effects} | ||
| return send_request(url, data, "POST") | ||
| class Image: | ||
@@ -677,19 +748,20 @@ """ | ||
| async def connect_websocket(queue: asyncio.Queue, websocket_connected: asyncio.Event): | ||
| async def connect_websocket(): | ||
| """ | ||
| Can use any asyncio queue | ||
| Connects to the websocket server and returns the connected websocket object. | ||
| Returns websockets.legacy.client.Connect object | ||
| """ | ||
| url = f"wss://{config.ip_address}:{config.websocket_port}/SRC?sessionId={config.session_id}" | ||
| if not config.ssl_verify: | ||
| ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT) | ||
| ssl_context.check_hostname = False | ||
| ssl_context.verify_mode = ssl.CERT_NONE | ||
| else: | ||
| raise SystemExit("SSL verification no supported yet") | ||
| async with websockets.connect(url, ssl=ssl_context) as websocket: | ||
| websocket_connected.set() | ||
| while True: | ||
| message = await websocket.recv() | ||
| decoded_message = handle_websocket_message(message) | ||
| await queue.put(decoded_message) | ||
| try: | ||
| url = config.websocket_default_url() | ||
| if not config.ssl_verify: | ||
| ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT) | ||
| ssl_context.check_hostname = False | ||
| ssl_context.verify_mode = ssl.CERT_NONE | ||
| else: | ||
| raise SystemExit("SSL verification not supported yet") | ||
| return websockets.connect(url, ssl=ssl_context) | ||
| except (websockets.ConnectionClosed, asyncio.TimeoutError) as e: | ||
| raise ConnectionError("Connection failed: " + str(e)) from e | ||
| except Exception as e: | ||
| raise e | ||
@@ -696,0 +768,0 @@ |
+20
-31
@@ -175,38 +175,27 @@ """ | ||
| async def demo_callback_function(queue: asyncio.Queue): | ||
| while True: | ||
| image_data = await queue.get() | ||
| while not queue.empty(): | ||
| await queue.get() | ||
| if "imageStream" in image_data: | ||
| image_data = json.loads(json.dumps(image_data), object_hook=lambda d: SimpleNamespace(**d)) | ||
| print(f"Websocket callback image dimensions: " | ||
| f"{image_data[2].value.image.dimensions.columns}," | ||
| f"{image_data[2].value.image.dimensions.rows} ") | ||
| async def main(): | ||
| lifo_queue = LifoQueue() | ||
| # Run websocket | ||
| websocket_connected_event = asyncio.Event() | ||
| asyncio.create_task(Access.connect_websocket(lifo_queue, websocket_connected_event)) | ||
| await websocket_connected_event.wait() | ||
| async with await Access.connect_websocket() as websocket: | ||
| # Connect the image service to websocket | ||
| websocket = Access.Image.connect_to_default_web_socket() | ||
| print(f"connect_to_default_web_socket: {websocket}") | ||
| assert websocket.result.success is True | ||
| # Connect the image service to websocket | ||
| websocket_connection = Access.Image.connect_to_default_web_socket() | ||
| print(f"connect_to_default_web_socket: {websocket_connection}") | ||
| assert websocket_connection.result.success is True | ||
| # Start template | ||
| output = Access.TemplateExecution.start(template_id).result.success | ||
| print(f"start: {output}") | ||
| assert output is True | ||
| # Start template | ||
| output = Access.TemplateExecution.start(template_id).result.success | ||
| print(f"start: {output}") | ||
| assert output is True | ||
| # Start image processing thread | ||
| asyncio.create_task(demo_callback_function(lifo_queue)) | ||
| # Receive one image and quit | ||
| while True: | ||
| image_data = await websocket.recv() | ||
| if '"imageStream"' in image_data: | ||
| image_data = json.dumps(Access.handle_websocket_message(image_data)) | ||
| image_data = json.loads(image_data, object_hook=lambda d: SimpleNamespace(**d)) | ||
| print(f"Websocket callback image dimensions: " | ||
| f"{image_data[2].value.image.dimensions.columns}," | ||
| f"{image_data[2].value.image.dimensions.rows} ") | ||
| break | ||
| while True: | ||
| await asyncio.sleep(5) | ||
| """ | ||
@@ -224,3 +213,3 @@ Done, cleanup | ||
| print("Running websocket for 5 seconds") | ||
| print("Running websocket for 1 image") | ||
| asyncio.run(main()) |
Alert delta unavailable
Currently unable to show alert delta for PyPI packages.
41134
7%858
6.98%