diff --git a/CHANGELOG.md b/CHANGELOG.md
index c6c6635..f9796e4 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -35,6 +35,7 @@ The rules for this file:
- Replaced connect error alert with dialog (PR #85)
- Added refresh frequency config option for 3D view (PR #86)
- Added more client side widget input validations (PR #87)
+- Added configurable UI and kernel timeouts (PR #90)
### Fixed
diff --git a/mdadash/backend/kernel/manager.py b/mdadash/backend/kernel/manager.py
index 4c66784..4d1ab6d 100644
--- a/mdadash/backend/kernel/manager.py
+++ b/mdadash/backend/kernel/manager.py
@@ -279,7 +279,7 @@ async def send_message(self, msg_type: str, data: dict) -> None:
self.kc.shell_channel.send(data_msg)
async def send_message_await_response(
- self, msg_type: str, data: dict | None = None, timeout: int = 5
+ self, msg_type: str, data: dict | None = None, timeout: int | None = None
) -> dict | None:
"""Send message to kernel and wait for a response (async)
@@ -288,11 +288,12 @@ async def send_message_await_response(
msg_type: str
A message type string that the kernel has a handler registered for
- data: dict
+ data: dict | None
Dict that gets passed to the handler in the kernel (default: None)
- timeout: int
- Timeout in seconds (default: 5)
+ timeout: int | None
+ Timeout in seconds (default: None)
+ When None, the configured kernel timeout in dashboard configurtion is used.
Returns
-------
@@ -318,6 +319,8 @@ async def send_message_await_response(
future = asyncio.get_running_loop().create_future()
self._pending_futures[msg_id] = future
self.kc.shell_channel.send(data_msg)
+ if timeout is None:
+ timeout = self.sm.dashboard_config["kernel_timeout"]
try:
return await asyncio.wait_for(future, timeout=timeout)
except TimeoutError as e: # pragma: no cover
@@ -325,7 +328,7 @@ async def send_message_await_response(
finally:
self._pending_futures.pop(msg_id, None)
- async def execute_code(self, code: str, timeout: int = 5) -> str:
+ async def execute_code(self, code: str, timeout: int | None = None) -> str:
"""Execute code in the kernel
Parameters
@@ -333,8 +336,9 @@ async def execute_code(self, code: str, timeout: int = 5) -> str:
code: str
Code to execute in the kernel
- timeout: int
- Timeout in seconds (default: 5)
+ timeout: int | None
+ Timeout in seconds (default: None)
+ When None, the configured kernel timeout in dashboard configurtion is used.
Returns
-------
@@ -342,6 +346,8 @@ async def execute_code(self, code: str, timeout: int = 5) -> str:
A string representation of the output of the code executed
"""
+ if timeout is None:
+ timeout = self.sm.dashboard_config["kernel_timeout"]
response = await self.send_message_await_response(
"execute_code", {"code": code}, timeout
)
@@ -363,10 +369,12 @@ async def connect_to_simulations(self) -> dict:
"""
try:
+ # Use a larger, but configurable timeout to allow creation of Universes
+ # Eg: guess_bonds=True could take a while to complete
response = await self.send_message_await_response(
"connect_to_simulations",
self.sm.universe_configs,
- timeout=20,
+ timeout=(6 * self.sm.dashboard_config["kernel_timeout"]),
)
if response["status"] == "ok":
self.sm.running_state["connected"] = True
diff --git a/mdadash/backend/state/manager.py b/mdadash/backend/state/manager.py
index 6462ca9..0ed591b 100644
--- a/mdadash/backend/state/manager.py
+++ b/mdadash/backend/state/manager.py
@@ -134,7 +134,7 @@ def load(self):
"nojump": False,
"socket_bufsize": None,
"buffer_size": 10000000,
- "timeout": 5,
+ "timeout": 600,
"continue_after_disconnect": None,
"step": 1,
"total_steps": None,
@@ -168,8 +168,11 @@ def _upgrade(self):
"selection_error": "Please enter a selection phrase",
}
settings = self._state["settings"]
- if "view3d_frequency" not in settings["dashboard_config"]:
- settings["dashboard_config"]["view3d_frequency"] = 1
+ dashboard_config = settings["dashboard_config"]
+ if "view3d_frequency" not in dashboard_config:
+ dashboard_config["view3d_frequency"] = 1
+ if "kernel_timeout" not in dashboard_config:
+ dashboard_config["kernel_timeout"] = 5
@property
def state(self) -> dict:
diff --git a/mdadash/frontend/src/__tests__/views/SettingsView.spec.js b/mdadash/frontend/src/__tests__/views/SettingsView.spec.js
index 101f305..e937ffb 100644
--- a/mdadash/frontend/src/__tests__/views/SettingsView.spec.js
+++ b/mdadash/frontend/src/__tests__/views/SettingsView.spec.js
@@ -13,6 +13,8 @@ const settings = ref({
dashboard_config: {
show_session_info: true,
show_energies: true,
+ ui_request_timeout: 5,
+ kernel_timeout: 5,
n_jobs: 2,
view3d_frequency: 1,
},
@@ -75,14 +77,24 @@ describe('SettingsView.vue', () => {
await input.setValue(dataMap[name])
}
}
- // update n_jobs
+ // update ui_request_timeout
const inputs = form.findAllComponents({ name: 'VNumberInput' })
- const n_jobs = inputs[0]
+ const ui_request_timeout = inputs[0]
+ expect(ui_request_timeout).toBeDefined()
+ expect(ui_request_timeout.props('rules')[0](null)).toMatch(/^Cannot be empty/)
+ expect(ui_request_timeout.props('rules')[0](5)).toBe(true)
+ // update kernel_timeout
+ const kernel_timeout = inputs[1]
+ expect(kernel_timeout).toBeDefined()
+ expect(kernel_timeout.props('rules')[0](null)).toMatch(/^Cannot be empty/)
+ expect(kernel_timeout.props('rules')[0](5)).toBe(true)
+ // update n_jobs
+ const n_jobs = inputs[2]
expect(n_jobs).toBeDefined()
expect(n_jobs.props('rules')[0](null)).toMatch(/^Cannot be empty/)
expect(n_jobs.props('rules')[0](2)).toBe(true)
// update view3d_frequency
- const view3d_frequency = inputs[1]
+ const view3d_frequency = inputs[3]
expect(view3d_frequency).toBeDefined()
expect(view3d_frequency.props('rules')[0](null)).toMatch(/^Cannot be empty/)
expect(view3d_frequency.props('rules')[0](1)).toBe(true)
diff --git a/mdadash/frontend/src/views/SettingsView.vue b/mdadash/frontend/src/views/SettingsView.vue
index 66e16f2..030e877 100644
--- a/mdadash/frontend/src/views/SettingsView.vue
+++ b/mdadash/frontend/src/views/SettingsView.vue
@@ -45,13 +45,41 @@
+
+
+
+
@@ -142,7 +170,7 @@
:min="8192"
:max="262144"
:step="1024"
- hint="Size of the socket buffer in bytes. Default is to use the system default"
+ hint="Size of imdclient socket buffer in bytes [System default]"
persistent-hint
>
@@ -153,7 +181,7 @@
v-model="settings.universe_configs[0].buffer_size"
control-variant="hidden"
:min="1000000"
- hint="IMDFrameBuffer will be filled with as many IMDFrame fit in buffer_size bytes [10MB]"
+ hint="Size of imdclient frame buffer in bytes [1000000]"
persistent-hint
:rules="[(v) => !!v || 'Cannot be empty. Default value is 10000000']"
validate-on="eager"
@@ -165,12 +193,12 @@
variant="outlined"
v-model="settings.universe_configs[0].timeout"
control-variant="default"
- hint="Timeout for the socket in seconds [5]"
+ hint="Timeout for imdclient socket in seconds [600]"
persistent-hint
:min="0"
- :max="600"
+ :max="900"
:step="1"
- :rules="[(v) => !!v || 'Cannot be empty. Default value is 5']"
+ :rules="[(v) => !!v || 'Cannot be empty. Default value is 600']"
validate-on="eager"
>
@@ -183,7 +211,7 @@
{ title: 'False', value: false },
{ title: 'None', value: null },
]"
- hint="Continue simulation after disconnect"
+ hint="Continue simulation after disconnect [None]"
persistent-hint
>
@@ -194,7 +222,7 @@
v-model="settings.universe_configs[0].step"
:min="1"
control-variant="default"
- hint="frame(s) during iteration"
+ hint="Step n frame(s) during iteration [1]"
persistent-hint
:rules="[(v) => !!v || 'Cannot be empty. Default value is 1']"
validate-on="eager"