{ "cells": [ { "cell_type": "markdown", "id": "243e380f", "metadata": {}, "source": [ "## TEM3: event tutorial\n", "\n", "This tutorial covers the TEM3 event mechanism, including:\n", "\n", "1. Basics of EventManager and EventKind\n", "2. Event queue control (enable/disable, size, retrieval)\n", "3. Callback registration and deregistration\n", "4. Real-machine event monitoring (optional)\n", "\n", ":::{note}\n", "- Operations involving actual instrument control must follow your facility's operating rules.\n", "- The first part of this tutorial uses simulation (put_event) and can be explored safely.\n", ":::" ] }, { "cell_type": "code", "execution_count": 2, "id": "0745bf3d", "metadata": {}, "outputs": [], "source": [ "from datetime import datetime, timedelta\n", "import time\n", "\n", "from PyJEM import TEM3" ] }, { "cell_type": "markdown", "id": "4caeba30", "metadata": {}, "source": [ "### 1. Checking EventManager and Event Types\n", "\n", "Use `TEM3.EventManager()` to obtain the event management object.\n", "\n", "#### `queue_info()`\n", "\n", "`queue_info()` returns the queue state of all events as a dictionary. Each entry contains the following fields:\n", "\n", "| Field | Description |\n", "|---|---|\n", "| `datasize` | Size of data stored in the queue |\n", "| `maxsize` | Maximum number of events the queue can hold |\n", "| `enabled` | Whether the event is enabled |\n", "\n", "#### `TEM3.EventKind`\n", "\n", "Available event types are enumerated in `TEM3.EventKind`. Representative examples are shown below.\n", "\n", "| Event Name | Description |\n", "|---|---|\n", "| `ChgMag` | Magnification change |\n", "| `ChgFunctionMode` | Function mode change |\n", "| `ChangedTemStemMode` | TEM/STEM mode change |\n", "| `ChgStageX` / `ChgStageY` / `ChgStageZ` | Stage X/Y/Z position change |\n", "| `ChgStageTX` / `ChgStageTY` | Stage tilt change |\n", "| `ChgDefocus` | Defocus change |" ] }, { "cell_type": "code", "execution_count": 3, "id": "e665ca3a", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Event queue summary (first 8):\n", "ChangeSpectrumMode: size=0 maxsize=10 enabled=True\n", "ChangedAcdHeatEnd: size=0 maxsize=10 enabled=True\n", "ChangedAcdHeatStart: size=0 maxsize=10 enabled=True\n", "ChangedBakeOutEnd: size=0 maxsize=10 enabled=True\n", "ChangedBakeOutStart: size=0 maxsize=10 enabled=True\n", "ChangedGIFMode: size=0 maxsize=10 enabled=True\n", "ChangedHomePositionOfDeflector: size=0 maxsize=10 enabled=True\n", "ChangedRelativeValueOfDeflector: size=0 maxsize=10 enabled=True\n", "\n", "Sample event names:\n", "['ChangeSpectrumMode', 'ChangedAcdHeatEnd', 'ChangedAcdHeatStart', 'ChangedBakeOutEnd', 'ChangedBakeOutStart', 'ChangedGIFMode', 'ChangedHomePositionOfDeflector', 'ChangedRelativeValueOfDeflector', 'ChangedSaafLimitStatus', 'ChangedSaafMode']\n" ] } ], "source": [ "# 1) Create event manager and inspect available events\n", "em = TEM3.EventManager()\n", "\n", "print(\"Event queue summary (first 8):\")\n", "info = em.queue_info()\n", "for i, (name, meta) in enumerate(info.items()):\n", " if i >= 8:\n", " break\n", " print(f\"{name}: size={meta['datasize']} maxsize={meta['maxsize']} enabled={meta['enabled']}\")\n", "\n", "print(\"\\nSample event names:\")\n", "print([e.name for e in list(TEM3.EventKind)[:10]])" ] }, { "cell_type": "markdown", "id": "d2759470", "metadata": {}, "source": [ "### 2. Registering Callbacks\n", "\n", "Register, list, and deregister functions to be called when an event occurs.\n", "\n", "The following cell demonstrates the full cycle of registering, invoking, and deregistering a callback for the `ChgMag` event.\n", "\n", "| Operation | Method | Description |\n", "|---|---|---|\n", "| Register | `regist_method(event_name, func, key)` | Register a callback function with a key |\n", "| List | `method_list(event_name)` | Retrieve the list of registered callbacks |\n", "| Deregister | `unregist_method(event_name, key)` | Remove a callback by key |" ] }, { "cell_type": "code", "execution_count": 5, "id": "f68d1076", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "registered callbacks: 1\n", "[callback] ChgMag received: ({'from': 'tutorial', 'at': '2026-03-23T14:47:27.028220'},)\n", "callbacks after unregist: 0\n" ] } ], "source": [ "event_name = TEM3.EventKind.ChgMag.name\n", "\n", "def on_chg_mag(*args):\n", " print(\"[callback] ChgMag received:\", args)\n", "\n", "# Register callback\n", "em.regist_method(event_name, on_chg_mag, \"demo_chg_mag\")\n", "print(\"registered callbacks:\", len(em.method_list(event_name)))\n", "\n", "# Simulate callback dispatch for tutorial purpose\n", "for cb in em.method_list(event_name):\n", " cb({\"from\": \"tutorial\", \"at\": datetime.now().isoformat()})\n", "\n", "# Unregister callback\n", "em.unregist_method(event_name, \"demo_chg_mag\")\n", "print(\"callbacks after unregist:\", len(em.method_list(event_name)))" ] }, { "cell_type": "markdown", "id": "a2b43d71", "metadata": {}, "source": [ "### 3. Monitoring Real-Machine Events (Hardware Required)\n", "\n", "This section requires a live TEM3 connection. \n", "Once connected, changing magnification or other settings on the instrument will enqueue the corresponding events.\n", "\n", "The following cell connects to TEM3 and monitors multiple events for 20 seconds. \n", "Operating the instrument (magnification, stage, mode, etc.) will record the corresponding events in the queue.\n", "\n", "Events monitored:\n", "\n", "| Event Name | Triggering Operation |\n", "|---|---|\n", "| `ChgMag` | Magnification change |\n", "| `ChgFunctionMode` | Function mode change |\n", "| `ChangedTemStemMode` | TEM/STEM mode switch |\n", "| `ChgStageX` / `ChgStageY` / `ChgStageZ` | Stage position change |\n", "| `ChgStageTX` / `ChgStageTY` | Stage tilt change |\n", "| `ChgDefocus` | Defocus change |\n", "\n", "After monitoring ends, a summary of total event count, per-type counts, and a timeline of the first events is printed." ] }, { "cell_type": "code", "execution_count": 9, "id": "517925e4", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "TEM3 connected: True\n", "Monitoring 9 events for 20 seconds...\n", "Operate TEM now (change mag/stage/mode, etc.).\n", "[2026-04-03 15:37:08.120401] ChgMag: (2000000, 'X', 'x2.0M')\n", "[2026-04-03 15:40:59.074381] ChgFunctionMode: (2, 'SMMAG')\n", "[2026-04-03 15:37:09.459789] ChgMag: (2500000, 'X', 'x2.5M')\n", "[2026-04-03 15:41:00.419068] ChgFunctionMode: (2, 'SMMAG')\n", "[2026-04-03 15:37:13.635347] ChgMag: (2000000, 'X', 'x2.0M')\n", "[2026-04-03 15:41:01.972621] ChgFunctionMode: (2, 'SMMAG')\n", "[2026-04-03 15:37:15.454976] ChgMag: (1500000, 'X', 'x1.5M')\n", "[2026-04-03 15:41:03.532489] ChgFunctionMode: (2, 'SMMAG')\n", "[2026-04-03 15:40:51.466562] ChgMag: (20000, 'X', 'x20k')\n", "[2026-04-03 15:41:05.061735] ChgFunctionMode: (2, 'SMMAG')\n", "[2026-04-03 15:40:51.950044] ChgMag: (25000, 'X', 'x25k')\n", "[2026-04-03 15:41:06.621841] ChgFunctionMode: (2, 'SMMAG')\n", "[2026-04-03 15:40:53.273855] ChgMag: (30000, 'X', 'x30k')\n", "[2026-04-03 15:41:08.183915] ChgFunctionMode: (2, 'SMMAG')\n", "[2026-04-03 15:40:54.640979] ChgMag: (40000, 'X', 'x40k')\n", "[2026-04-03 15:41:09.785154] ChgFunctionMode: (2, 'SMMAG')\n", "[2026-04-03 15:40:56.187460] ChgMag: (50000, 'X', 'x50k')\n", "[2026-04-03 15:41:11.302722] ChgFunctionMode: (2, 'SMMAG')\n", "[2026-04-03 15:40:57.564335] ChgMag: (60000, 'X', 'x60k')\n", "[2026-04-03 15:41:12.625081] ChgFunctionMode: (2, 'SMMAG')\n", "[2026-04-03 15:40:59.074381] ChgMag: (80000, 'X', 'x80k')\n", "[2026-04-03 15:41:00.419068] ChgMag: (100000, 'X', 'x100k')\n", "[2026-04-03 15:41:01.972621] ChgMag: (120000, 'X', 'x120k')\n", "[2026-04-03 15:41:03.532489] ChgMag: (150000, 'X', 'x150k')\n", "[2026-04-03 15:41:05.061735] ChgMag: (200000, 'X', 'x200k')\n", "[2026-04-03 15:41:06.621841] ChgMag: (250000, 'X', 'x250k')\n", "[2026-04-03 15:41:08.183915] ChgMag: (300000, 'X', 'x300k')\n", "[2026-04-03 15:41:09.785154] ChgMag: (400000, 'X', 'x400k')\n", "[2026-04-03 15:41:11.302722] ChgMag: (500000, 'X', 'x500k')\n", "[2026-04-03 15:41:12.625081] ChgMag: (600000, 'X', 'x600k')\n", "[2026-04-03 15:41:21.429149] ChgMag: (20000, 'X', 'x20k')\n", "[2026-04-03 15:41:21.429149] ChgFunctionMode: (2, 'SMMAG')\n", "[2026-04-03 15:41:21.916072] ChgMag: (25000, 'X', 'x25k')\n", "[2026-04-03 15:41:21.916072] ChgFunctionMode: (2, 'SMMAG')\n", "[2026-04-03 15:41:23.242699] ChgMag: (30000, 'X', 'x30k')\n", "[2026-04-03 15:41:23.242699] ChgFunctionMode: (2, 'SMMAG')\n", "[2026-04-03 15:41:24.813252] ChgMag: (40000, 'X', 'x40k')\n", "[2026-04-03 15:41:24.813252] ChgFunctionMode: (2, 'SMMAG')\n", "[2026-04-03 15:41:26.195036] ChgMag: (50000, 'X', 'x50k')\n", "[2026-04-03 15:41:26.195036] ChgFunctionMode: (2, 'SMMAG')\n", "[2026-04-03 15:41:27.716587] ChgMag: (60000, 'X', 'x60k')\n", "[2026-04-03 15:41:27.716587] ChgFunctionMode: (2, 'SMMAG')\n", "[2026-04-03 15:41:29.101735] ChgMag: (80000, 'X', 'x80k')\n", "[2026-04-03 15:41:29.101735] ChgFunctionMode: (2, 'SMMAG')\n", "[2026-04-03 15:41:30.610721] ChgMag: (100000, 'X', 'x100k')\n", "[2026-04-03 15:41:30.610721] ChgFunctionMode: (2, 'SMMAG')\n", "[2026-04-03 15:41:32.157029] ChgMag: (120000, 'X', 'x120k')\n", "[2026-04-03 15:41:32.157029] ChgFunctionMode: (2, 'SMMAG')\n", "[2026-04-03 15:41:33.732630] ChgFunctionMode: (2, 'SMMAG')\n", "[2026-04-03 15:41:33.732630] ChgMag: (150000, 'X', 'x150k')\n", "[2026-04-03 15:41:35.304930] ChgMag: (200000, 'X', 'x200k')\n", "[2026-04-03 15:41:35.304930] ChgFunctionMode: (2, 'SMMAG')\n", "[2026-04-03 15:41:36.870486] ChgMag: (250000, 'X', 'x250k')\n", "[2026-04-03 15:41:36.870486] ChgFunctionMode: (2, 'SMMAG')\n", "[2026-04-03 15:41:38.446246] ChgMag: (300000, 'X', 'x300k')\n", "[2026-04-03 15:41:38.446246] ChgFunctionMode: (2, 'SMMAG')\n", "\n", "--- Summary ---\n", "total events: 56\n", "event counts:\n", " ChgMag: 33\n", " ChgFunctionMode: 23\n", "\n", "first 10 events (timeline):\n", " 2026-04-03 15:37:08.120401 | ChgMag | (2000000, 'X', 'x2.0M')\n", " 2026-04-03 15:40:59.074381 | ChgFunctionMode | (2, 'SMMAG')\n", " 2026-04-03 15:37:09.459789 | ChgMag | (2500000, 'X', 'x2.5M')\n", " 2026-04-03 15:41:00.419068 | ChgFunctionMode | (2, 'SMMAG')\n", " 2026-04-03 15:37:13.635347 | ChgMag | (2000000, 'X', 'x2.0M')\n", " 2026-04-03 15:41:01.972621 | ChgFunctionMode | (2, 'SMMAG')\n", " 2026-04-03 15:37:15.454976 | ChgMag | (1500000, 'X', 'x1.5M')\n", " 2026-04-03 15:41:03.532489 | ChgFunctionMode | (2, 'SMMAG')\n", " 2026-04-03 15:40:51.466562 | ChgMag | (20000, 'X', 'x20k')\n", " 2026-04-03 15:41:05.061735 | ChgFunctionMode | (2, 'SMMAG')\n" ] } ], "source": [ "# Real-machine event monitor: log what happened\n", "from collections import Counter\n", "\n", "connected = TEM3.connect()\n", "print(\"TEM3 connected:\", connected)\n", "\n", "if not connected:\n", " print(\"Skip monitoring because TEM3 is not connected.\")\n", "else:\n", " em = TEM3.EventManager()\n", "\n", " # Add or remove events to match your operation target\n", " watch_events = [\n", " TEM3.EventKind.ChgMag.name,\n", " TEM3.EventKind.ChgFunctionMode.name,\n", " TEM3.EventKind.ChangedTemStemMode.name,\n", " TEM3.EventKind.ChgStageX.name,\n", " TEM3.EventKind.ChgStageY.name,\n", " TEM3.EventKind.ChgStageZ.name,\n", " TEM3.EventKind.ChgStageTX.name,\n", " TEM3.EventKind.ChgStageTY.name,\n", " TEM3.EventKind.ChgDefocus.name,\n", " ]\n", "\n", " for ev_name in watch_events:\n", " em.set_enabled(ev_name, True)\n", " em.set_maxsize(ev_name, 100)\n", "\n", " monitor_seconds = 20\n", " print(f\"Monitoring {len(watch_events)} events for {monitor_seconds} seconds...\")\n", " print(\"Operate TEM now (change mag/stage/mode, etc.).\")\n", "\n", " end = datetime.now() + timedelta(seconds=monitor_seconds)\n", " event_log = []\n", "\n", " while datetime.now() < end:\n", " for ev_name in watch_events:\n", " ev = em.get_event(ev_name)\n", " if ev is not None:\n", " event_log.append(ev)\n", " print(f\"[{ev.time}] {ev.name}: {ev.data}\")\n", " time.sleep(0.1)\n", "\n", " print(\"\\n--- Summary ---\")\n", " print(\"total events:\", len(event_log))\n", "\n", " if event_log:\n", " counts = Counter(e.name for e in event_log)\n", " print(\"event counts:\")\n", " for name, count in counts.most_common():\n", " print(f\" {name}: {count}\")\n", "\n", " print(\"\\nfirst 10 events (timeline):\")\n", " for e in event_log[:10]:\n", " print(f\" {e.time} | {e.name} | {e.data}\")\n", " else:\n", " print(\"No events captured.\")" ] }, { "cell_type": "markdown", "id": "f4af9b0a", "metadata": {}, "source": [ "#### Callback Monitoring for `ChgMag` Events\n", "\n", "Register a callback function with `regist_method` and monitor the `ChgMag` (magnification change) event for 15 seconds. \n", "Unlike a polling loop, the callback is invoked immediately when an event occurs.\n", "\n", "| Step | Method | Description |\n", "|---|---|---|\n", "| Register callback | `regist_method(event_name, func, key)` | Register a function to be called on event |\n", "| Wait | `time.sleep(15)` | Wait 15 seconds while TEM events may occur |\n", "| Deregister callback | `unregist_method(event_name, key)` | Remove the callback after monitoring ends |" ] }, { "cell_type": "code", "execution_count": 8, "id": "7c6c2dab", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "TEM3 connected: True\n", "Monitoring for 15 seconds via callback...\n", "Try changing magnification on TEM during this period.\n", "[2026-04-03T15:40:51.466562] [ChgMag] (20000, 'X', 'x20k')\n", "[2026-04-03T15:40:51.950044] [ChgMag] (25000, 'X', 'x25k')\n", "[2026-04-03T15:40:53.273855] [ChgMag] (30000, 'X', 'x30k')\n", "[2026-04-03T15:40:54.640979] [ChgMag] (40000, 'X', 'x40k')\n", "[2026-04-03T15:40:56.187460] [ChgMag] (50000, 'X', 'x50k')\n", "[2026-04-03T15:40:57.564335] [ChgMag] (60000, 'X', 'x60k')\n", "[2026-04-03T15:40:59.074381] [ChgMag] (80000, 'X', 'x80k')\n", "[2026-04-03T15:41:00.419068] [ChgMag] (100000, 'X', 'x100k')\n", "captured count: 8\n" ] } ], "source": [ "connected = TEM3.connect()\n", "print(\"TEM3 connected:\", connected)\n", "\n", "if not connected:\n", " print(\"Skip monitoring because TEM3 is not connected.\")\n", "else:\n", " watch_event = TEM3.EventKind.ChgMag.name\n", " em.set_enabled(watch_event, True)\n", " em.set_maxsize(watch_event, 20)\n", "\n", " captured = []\n", "\n", " def on_chg_mag(*args):\n", " captured.append(args)\n", " print(f\"[{datetime.now().isoformat()}] [ChgMag]\", args)\n", "\n", " em.regist_method(watch_event, on_chg_mag, \"monitor_chg_mag\")\n", " print(\"Monitoring for 15 seconds via callback...\")\n", " print(\"Try changing magnification on TEM during this period.\")\n", "\n", " time.sleep(15)\n", "\n", " em.unregist_method(watch_event, \"monitor_chg_mag\")\n", " print(\"captured count:\", len(captured))" ] }, { "cell_type": "markdown", "id": "68113128", "metadata": {}, "source": [ "### Appendix: Manipulating the Event Queue (Offline)\n", "\n", "For offline testing, use `put_event` to inject simulated events and verify queue behavior." ] }, { "cell_type": "code", "execution_count": null, "id": "aa5d5a86", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "qsize after put: 3\n", "drain queue:\n", "\n", "\n", "\n" ] } ], "source": [ "event_name = TEM3.EventKind.ChgMag.name\n", "\n", "# Configure queue behavior\n", "em.set_enabled(event_name, True)\n", "em.set_maxsize(event_name, 3)\n", "\n", "# Put 5 events into queue (maxsize=3 -> old events are dropped)\n", "for i in range(5):\n", " em.put_event(event_name, {\"sim_index\": i, \"note\": \"simulated event\"})\n", "\n", "print(\"qsize after put:\", em.qsize(event_name))\n", "\n", "# Drain queue\n", "print(\"drain queue:\")\n", "while em.qsize(event_name) > 0:\n", " ev = em.get_event(event_name)\n", " print(ev)" ] } ], "metadata": { "kernelspec": { "display_name": "vjem310", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.10.12" } }, "nbformat": 4, "nbformat_minor": 5 }