Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
cb751f8
Merge pull request #105 from pycroscopy/main
AustinHouston Jun 5, 2026
ea1a067
Merge pull request #106 from pycroscopy/main
AustinHouston Jun 5, 2026
aabfa83
add mcp to startup configs
AustinHouston Jun 8, 2026
14ea9b1
docs
AustinHouston Jun 8, 2026
529959d
test
AustinHouston Jun 8, 2026
c648cec
refactor: MCP
AustinHouston Jun 8, 2026
06b0449
Merge branch 'dev-codex' of https://github.com/pycroscopy/asyncroscop…
AustinHouston Jun 8, 2026
3460718
add --quiet flad for mcp
AustinHouston Jun 9, 2026
1893bb8
configs for mcp
AustinHouston Jun 9, 2026
d048a78
NOTE: all servers must be running on one computer, as o fnow
AustinHouston Jun 9, 2026
13224cf
Feat: Gui utilizes instruments in configs and updated docs
whittlegears Jun 9, 2026
70963b9
Docs: reflected updates to framework and for run-server-gui.md
whittlegears Jun 10, 2026
b16e101
Merge branch 'pycroscopy:main' into main
whittlegears Jun 10, 2026
bc8e01b
Docs: added images
whittlegears Jun 10, 2026
b039e9b
Fix: images not rendering on docs
whittlegears Jun 10, 2026
631ccdf
Fix:links on main page
whittlegears Jun 10, 2026
dc8ce16
feat: user-ready stage 1 and add insrrucitons for putting code in Talos
Jun 12, 2026
44bd923
add: .gitattributes for consistent line endings across mac-windows fo…
Jun 14, 2026
da754e8
Feat(Claude-help: med)(Conscious-read: yes): add --debug flag to scri…
Jun 14, 2026
47410a3
docs: writting first paper
AustinHouston Jun 15, 2026
845a978
MCP: changes to structure, name, and startup
AustinHouston Jun 15, 2026
2a2ffc0
MCP in run servers
AustinHouston Jun 15, 2026
ed3f3eb
rename (configs to match name changes)
AustinHouston Jun 15, 2026
1182f94
Merge pull request #118 from pycroscopy/main
AustinHouston Jun 15, 2026
50d099b
rename(Microscope): compliment to Instrument PR reorganization
AustinHouston Jun 15, 2026
a875d94
Merge branch 'main' into Talos-cm
Jun 15, 2026
be8de3b
Merge pull request #120 from pycroscopy/boris_instrument
utkarshp1161 Jun 15, 2026
ef79941
Merge branch 'main' into Talos-cm
Jun 15, 2026
21b2710
Merge pull request #116 from pycroscopy/Talos-cm
AustinHouston Jun 15, 2026
d89ad7a
Simplify MCP server tool registration
AustinHouston Jun 16, 2026
d2e8b6e
Split MCP startup from server startup
AustinHouston Jun 16, 2026
5832736
refactor(gui): based on tkinter. gui now formats a yaml then calls ru…
AustinHouston Jun 16, 2026
803277b
refactor(gui): built on tkinter, seperated into server and mcp guis
AustinHouston Jun 16, 2026
5f5021b
local config for debugging
AustinHouston Jun 16, 2026
661f71e
chore
AustinHouston Jun 16, 2026
f38947a
test(mcp windows) path was weird
AustinHouston Jun 16, 2026
b27e8fa
chore (trim old file)
AustinHouston Jun 16, 2026
0a08c97
refactor(everything)
AustinHouston Jun 16, 2026
be74a6a
Merge branch 'dev-ACH' of https://github.com/pycroscopy/asyncroscopy …
AustinHouston Jun 17, 2026
a3eadc3
fix (merge relics)
AustinHouston Jun 17, 2026
3c63f8a
chore
AustinHouston Jun 17, 2026
fbcd102
chore
AustinHouston Jun 17, 2026
1512c6e
docs (removed notes)
AustinHouston Jun 17, 2026
fc7ff70
docs (renaming microscope)
AustinHouston Jun 17, 2026
6f91d51
keep jeol from main branch
AustinHouston Jun 17, 2026
8ef49f5
jeol
AustinHouston Jun 17, 2026
996786e
Merge pull request #114 from whittlegears/main
AustinHouston Jun 17, 2026
6c07501
refactor(GUI): guis back in pyqt6 and tango_dabase.db file is deleted…
AustinHouston Jun 17, 2026
d3217e2
feat(startup): old database file is handled in config
AustinHouston Jun 17, 2026
8d38f0a
feat(DT-preacquired data): hackathon prep
AustinHouston Jun 18, 2026
55558c1
compat(QT5): gui have qt5 translation layer for older windows versions
AustinHouston Jun 18, 2026
8ce0c9b
fix(QT5): wheels
AustinHouston Jun 18, 2026
74852e7
chore(notebooks): update to cleaner data server init now that we have…
AustinHouston Jun 18, 2026
01ec9e1
Merge upstream/main into dev-ACH
AustinHouston Jun 18, 2026
4aabebe
Merge pull request #122 from pycroscopy/dev-ACH
AustinHouston Jun 18, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file modified .DS_Store
Binary file not shown.
1 change: 1 addition & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
* text=auto
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -236,5 +236,8 @@ ClAUDE.md

tiled_data/
outputs/
output_tango_devices_logs/

.claude/

aws-use/
12 changes: 7 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
# Asyncroscopy:
- Enabling smart microscopy via asynchronous servers
# Asyncroscopy: Enabling smart microscopy via asynchronous servers

![Schematic of the functional project structure](architecture.png)
<div class="d-flex justify-content-between">
<img src="docs/images/architecturev1.png" width="33.5%" />
<img src="docs/images/architecturev2.png" width="57%" />
</div>

Note: `main` branch now contains the PyTango-based architecture. The previous Twisted-based implementation is preserved in the `twisted-legacy` branch for reference.
---
Expand All @@ -17,7 +19,7 @@ see: [Tutorial notebook](notebooks/1_Client_tutorial.ipynb)
```
.
├── src/
│ ├── Microscope.py # Main device — owns AutoScript connection and all acquisition commands
│ ├── ElectronMicroscope.py # Electron microscope base device and acquisition commands
│ ├── detectors/
│ │ ├── HAADF.py # HAADF detector settings device
│ │ ├── EELS.py # EELS detector settings device (stub)
Expand All @@ -30,7 +32,7 @@ see: [Tutorial notebook](notebooks/1_Client_tutorial.ipynb)
│ └── advanced_acquisition.py # Multi-detector acquisition helpers (stub)
├── tests/
│ ├── conftest.py # Shared pytest fixtures (DeviceTestContext proxies)
│ ├── test_microscope.py # Microscope device tests
│ ├── test_microscope.py # STEMMicroscope device tests
│ ├── test_acquisition.py # Acquisition tests
│ └── detectors/
│ └── test_HAADF.py # HAADF device tests
Expand Down
126 changes: 126 additions & 0 deletions asyncroscopy/JeolMicroscope.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
## started on 5th June 2026 --> Aimed for JEM-F200

# Import python internal libraries
import math
import time
from datetime import datetime
from pathlib import Path

# Import external libraries
import numpy as np
import tango
from tango import AttrWriteType, DevState
from tango.server import attribute, command, device_property

# Import Asyncroscopy relevant modules
from asyncroscopy.Microscope import Microscope
from asyncroscopy.software.DataWriter import DEFAULT_ACQUISITION_DIR, save_acquisition

# Import Manufacturer-specific libraries



# Define class for Microscope

class JeolMicroscope(Microscope):
"""
Manages the PyJEM connection and exposes acquisition commands.
Detector-specific settings (dwell time, resolution) are stored in
dedicated detector devices and read via DeviceProxy at acquisition time.
"""

# ------------------------------------------------------------------
# Device properties — configure in Tango DB per deployment
# ------------------------------------------------------------------
autoscript_host_ip = device_property(
dtype=str,
default_value="10.46.217.241",
doc="Hostname or IP of the AutoScript microscope server",
)
autoscript_host_port = device_property(
dtype=int,
default_value=9095,
doc="Hostname or IP of the AutoScript microscope server",
)
acquisition_save_directory = device_property(
dtype=str,
default_value=DEFAULT_ACQUISITION_DIR,
doc="Directory where AutoScript acquisitions are saved before the Tiled server serves them.",
)
acquisition_file_format = device_property(
dtype=str,
default_value="h5",
doc="Acquisition file format. HDF5 stores acquisition data and parsed metadata attributes.",
)
data_device_address = device_property(
dtype=str,
default_value="",
doc="Optional Tango device address for the DATA device, e.g. 'asyncroscopy/data/default'.",
)
# ------------------------------------------------------------------
# Attributes
# ------------------------------------------------------------------

# ------------------------------------------------------------------
# Initialisation
# ------------------------------------------------------------------

# ------------------------------------------------------------------
# Attribute read methods
# ------------------------------------------------------------------

# ------------------------------------------------------------------
# Commands pertaining to setting children attributes, e.g. stage position, scan parameters, EDS settings, etc. --> iuser accesses it in a jupyter notebook using the device proxy
# ------------------------------------------------------------------


# ------------------------------------------------------------------
# Internal acquisition helpers
# ------------------------------------------------------------------
def _persist(self, adorned, acquisition_type, detector, data_server, dataset_name="image"):
"""Save acquired images in the format requested by the SCAN device.
"""
scan = self._detector_proxies.get("scan")
fmt = scan.output_format if scan is not None else ".h5" # ".h5" default
if fmt == ".h5":
return save_acquisition(self, data_server, acquisition_type, detector, adorned, dataset_name=dataset_name)
if fmt != ".tiff":
raise ValueError(f"Unsupported output_format {fmt!r}; expected '.h5' or '.tiff'")

# .tiff → AutoScript native save, one file per detector sharing one stamp
images = list(adorned) if isinstance(adorned, (list, tuple)) else [adorned]
detectors = list(detector) if isinstance(detector, (list, tuple)) else [detector]
if len(images) != len(detectors):
raise ValueError(f"Got {len(images)} images for {len(detectors)} detector(s) {detectors}")

save_dir = data_server.save_path if data_server is not None else DEFAULT_ACQUISITION_DIR
directory = Path(save_dir).expanduser()
directory.mkdir(parents=True, exist_ok=True)
stamp = datetime.now().strftime("%Y%m%dT%H%M%S%f")
stem = f"{acquisition_type}_{stamp}"
# AutoScript returns images in the requested detector order (assumed; verify on hardware)
for img, det in zip(images, detectors):
path = directory / f"{stem}_{det}.tiff"
img.save(str(path))
if data_server is not None:
data_server.register_path(str(path))
return stem

def _acquire_scanned_image(
self,
imsize: int,
dwell_time: float,
detector_list: list[str] = ["haadf"],
scan_region: list[float] = [0.0, 0.0, 1.0, 1.0],
) -> str:
"""
Call AutoScript scanned image acquisition, save one HDF5 file, and return its DATA/Tiled key.
"""
detector_list = [d.upper() for d in detector_list]
#settings = StemAcquisitionSettings(dwell_time=dwell_time, detector_types=detector_list, size=imsize, region=Region(RegionCoordinateSystem.RELATIVE, Rectangle(*scan_region)))
#adorned = self._microscope.acquisition.acquire_stem_images_advanced(settings)
if not isinstance(adorned, list):
adorned = [adorned]
data_server = self._detector_proxies.get("data")
return self._persist(adorned, "stem_image", detector_list, data_server)

Loading
Loading