{ "cells": [ { "cell_type": "markdown", "id": "intro", "metadata": {}, "source": [ "# Discovery and Launch\n", "\n", "This notebook demonstrates how to find running MITK Workbench instances and\n", "start new ones programmatically.\n", "\n", "You will learn how to:\n", "\n", "- Discover running Workbench instances with `mw.discover()`\n", "- Connect to a discovered instance\n", "- Launch a new Workbench with `mw.launch()`\n", "- Work with multiple Workbench instances\n", "- Shut down a launched Workbench\n", "\n", "**Prerequisites:**\n", "- For discovery: at least one running MITK Workbench with the REST API enabled\n", "- For launch: the `MITK_WORKBENCH` environment variable pointing to the\n", " Workbench executable, or pass the path explicitly" ] }, { "cell_type": "markdown", "id": "sec-discover", "metadata": {}, "source": [ "## 1. Discover running instances\n", "\n", "`mw.discover()` probes localhost ports 8080-8099 (by default) for running MITK\n", "Workbench REST servers. Probes run concurrently, so scanning 20 ports takes\n", "about as long as a single probe.\n", "\n", "Each found instance is returned as a ready-to-use `Workbench` handle." ] }, { "cell_type": "code", "execution_count": 1, "id": "discover", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Found 1 running Workbench instance(s):\n", "\n", " http://localhost:8080 — MITK Workbench REST API (MITK 2025.12.99-1b4706d3)\n" ] } ], "source": [ "import mitk_workbench_remote as mw\n", "\n", "instances = mw.discover()\n", "print(f\"Found {len(instances)} running Workbench instance(s):\\n\")\n", "\n", "for wb in instances:\n", " info = wb.info\n", " print(f\" {wb.url} — {info.name} (MITK {info.mitk_version})\")" ] }, { "cell_type": "markdown", "id": "sec-discover-connect", "metadata": {}, "source": [ "## 2. Use a discovered instance\n", "\n", "The returned `Workbench` handles work exactly like those from `mw.connect()`.\n", "Pick one and use it." ] }, { "cell_type": "code", "execution_count": 2, "id": "discover-connect", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Showed data in http://localhost:8080\n" ] } ], "source": [ "import numpy as np\n", "\n", "if instances:\n", " wb = instances[0]\n", " node = wb.show(\n", " np.random.default_rng(42).random((32, 32, 32), dtype=np.float32),\n", " name=\"Discovery Demo\",\n", " )\n", " print(f\"Showed data in {wb.url}\")\n", " node.remove()\n", "else:\n", " print(\"No instances found — start a Workbench and re-run this cell.\")" ] }, { "cell_type": "markdown", "id": "a3ba0736", "source": [ "## 3. Connecting to an auth-enabled instance\n", "\n", "A Workbench can be configured to require authentication: in the REST API\n", "preferences, set `requireAuth=true` and a non-empty `apiToken`. Every request\n", "then needs an `Authorization: Bearer ` header, which `connect()` adds\n", "when you pass `token`:\n", "\n", "```python\n", "secured = mw.connect(\"http://localhost:8080\", token=\"your-api-token\")\n", "print(secured.info.name)\n", "```\n", "\n", "`launch()` (section 5) does this automatically: it generates a random token and\n", "wires it into the returned handle. You only need to pass a token yourself when\n", "connecting to an already-running instance that was secured manually.\n", "\n", "**Caveat for `discover()`:** discovery probes `/health`, which is exempt from\n", "authentication, so auth-enabled instances are still *found*. But the returned\n", "handles carry no token, and discovery cannot tell whether an instance requires\n", "auth (the server does not advertise it). The first non-exempt call against a\n", "secured instance therefore raises `AuthenticationError`. If you know an instance\n", "is secured, reconnect with the token:\n", "\n", "```python\n", "secured = mw.connect(wb.url, token=\"your-api-token\")\n", "```" ], "metadata": {} }, { "cell_type": "markdown", "id": "sec-custom-range", "metadata": {}, "source": [ "## 4. Custom port range\n", "\n", "Pass a custom port range if your Workbench is on a non-default port." ] }, { "cell_type": "code", "execution_count": 3, "id": "custom-range", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Found 1 instance(s) in port range 8080-8199\n" ] } ], "source": [ "# Scan a wider range\n", "found = mw.discover(ports=range(8080, 8200), timeout=1.0)\n", "print(f\"Found {len(found)} instance(s) in port range 8080-8199\")" ] }, { "cell_type": "markdown", "id": "sec-launch", "metadata": {}, "source": [ "## 5. Launch a new Workbench\n", "\n", "`mw.launch()` starts a new MITK Workbench process with the REST API enabled.\n", "It waits for the server to become healthy, then returns a connected `Workbench`\n", "handle.\n", "\n", "The executable is resolved from:\n", "1. The `executable` argument (if given)\n", "2. The `MITK_WORKBENCH` environment variable\n", "\n", "A secure random API token is generated automatically. The port is auto-selected\n", "from the 8080-8099 range unless specified." ] }, { "cell_type": "code", "execution_count": null, "id": "launch", "metadata": { "pycharm": { "is_executing": true } }, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "pydev debugger: Unable to find real location for: \n" ] } ], "source": [ "# Uncomment and adjust the path for your system:\n", "wb_new = mw.launch(\"D:\\\\Dev\\\\MITK\\\\installers\\\\MITK-v2026.06-rc1-windows-x86_64\\\\MitkWorkbench.bat\", port=8085)\n", "\n", "# Or use the MITK_WORKBENCH environment variable:\n", "import mitk_workbench_remote as mw\n", "import os\n", "os.environ[\"MITK_WORKBENCH\"] = \"D:\\\\Dev\\\\MITK\\\\installers\\\\MITK-v2026.06-rc1-windows-x86_64\\\\MitkWorkbench.bat\"\n", "# wb_new = mw.launch(port=8085)\n", "\n", "# For this example, we attempt launch and handle the error gracefully\n", "try:\n", " wb_new = mw.launch(timeout=15)\n", " print(f\"Launched at {wb_new.url}\")\n", " print(f\" MITK version: {wb_new.info.mitk_version}\")\n", " print(f\" Managed process: {wb_new.is_launched_remotely}\")\n", "except (mw.errors.MitkError, FileNotFoundError) as e:\n", " wb_new = None\n", " print(f\"Could not launch: {e}\")\n", " print(\"Set MITK_WORKBENCH env var or pass the executable path.\")" ] }, { "cell_type": "markdown", "id": "sec-multi", "metadata": {}, "source": [ "## 6. Multi-Workbench workflow\n", "\n", "Each `Workbench` handle is independent — no global state is shared.\n", "You can show the same data in all running instances." ] }, { "cell_type": "code", "execution_count": null, "id": "multi", "metadata": {}, "outputs": [], "source": [ "all_instances = mw.discover()\n", "data = np.random.default_rng(7).random((32, 32, 32), dtype=np.float32)\n", "\n", "nodes = []\n", "for wb in all_instances:\n", " n = wb.show(data, name=\"Shared Data\")\n", " nodes.append((wb, n))\n", " print(f\"Showed in {wb.url}\")\n", "\n", "# Clean up\n", "for wb, n in nodes:\n", " n.remove()\n", "\n", "print(f\"\\nShowed data in {len(all_instances)} instance(s)\")" ] }, { "cell_type": "markdown", "id": "sec-shutdown", "metadata": {}, "source": [ "## 7. Shutdown\n", "\n", "`wb.shutdown()` terminates a Workbench that was started with `mw.launch()`.\n", "It also closes the underlying transport session.\n", "\n", "Only instances started by `launch()` can be shut down — calling `shutdown()`\n", "on a `connect()`-ed handle raises `MitkError`. Use `wb.is_launched_remotely`\n", "to check." ] }, { "cell_type": "code", "execution_count": null, "id": "shutdown", "metadata": {}, "outputs": [], "source": [ "if wb_new is not None:\n", " print(f\"Shutting down {wb_new.url} ...\")\n", " wb_new.shutdown()\n", " print(\"Done.\")\n", "else:\n", " print(\"No launched instance to shut down.\")" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "name": "python", "version": "3.10.0" } }, "nbformat": 4, "nbformat_minor": 5 }