Skip to content
Merged
53 changes: 34 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,18 @@ On Mac, you should use brew:
* `export CFLAGS="-I $(brew --prefix graphviz)/include"`
* `export LDFLAGS="-L $(brew --prefix graphviz)/lib"`

On Windows, follow the instructions [here](https://pygraphviz.github.io/documentation/stable/install.html#windows). Short version:

* Download and install [Visual C/C++](https://visualstudio.microsoft.com/visual-cpp-build-tools/)
* Download and install [graphviz](https://gitlab.com/graphviz/graphviz/-/releases). Get the most recent Windows x64 CMake releases.
* Install pygraphviz in your environment:
```
python -m pip install --config-settings="--global-option=build_ext"
--config-settings="--global-option=-IC:\Program Files\Graphviz\include"
--config-settings="--global-option=-LC:\Program Files\Graphviz\lib"
pygraphviz
```

### Release

Install the latest release from pypi with: `pip install ezmsg-tools` (or `uv add ...` or `poetry add ...`).
Expand All @@ -38,37 +50,41 @@ Or install the latest development version:

This package includes some entrypoints with useful tools.

### ezmsg-performance-monitor
### ezmsg-signal-monitor

This tool operates on logfiles created by ezmsg. Logfiles will automatically be created when running a pipeline containing nodes decorated with `ezmsg.sigproc.util.profile.profile_subpub`,
and if the `EZMSG_LOGLEVEL` environment variable is set to DEBUG. The logfiles will be created in `~/.ezmsg/profile/ezprofiler.log` by default but this can be changed with the `EZMSG_PROFILE` environment variable.
The pipeline must be running on a graph service exposed on the network. For example, first, run the GraphService on an open port:

Most of the nodes provided by `ezmsg.sigproc` are already decorated to enable profiling, as is any custom nodes that inherit from `ezmsg.sigproc.base.GenAxisArray`.
You can decorate other nodes with `ezmsg.sigproc.util.profile.profile_subpub` to enable profiling.
`ezmsg --address 127.0.0.1:25978 start`

During a run with profiling enabled, the logfiles will be created in the specified location. You may wish to additionally create a graph file: (`uv run`) `EZMSG_LOGLEVEL=WARN ezmsg mermaid > ~/.ezmsg/profile/ezprofiler.mermaid`
Then run your usual pipeline but make sure it attaches to the graph address by passing `graph_address=("127.0.0.1", 25978)` as a kwarg to `ez.run`.

During or after a pipeline run with profiling enabled, you can run (`uv run `) `performance-monitor` to visualize the performance of the nodes in the pipeline.
While the pipeline is running, you can run the signal-monitor tool with (`uv run`) `ezmsg-signal-monitor --graph-addr 127.0.0.1:25978`.

> Unlike `signal-monitor`, this tool does not require the pipeline to attach to an existing graph service because it relies exclusively on the logfile.
This launches a window with graph visualized on the left. Click on a node's output box to get a live visualization on the right side of the screen plotting the data as it leaves that node. Use `a` to toggle auto-scaling. With auto-scaling off, use `-`, and `=` to zoom out and in, respectively. See the [phosphor docs](https://www.ezmsg.org/phosphor/) for the full list of keyboard shortcuts.

> This performance monitor is soon to be deprecated in favor of monitoring tools built-in to ezmsg.
> Currently only 2-D outputs are supported!

### ezmsg-signal-monitor
Don't forget to shutdown your graph service when you are done, e.g.: `ezmsg --address 127.0.0.1:25978 shutdown`

The pipeline must be running on a graph service exposed on the network. For example, first, run the GraphService on an open port:
### ezmsg-performance-monitor

`ezmsg --address 127.0.0.1:25978 start`
**DEPRECATED**

Then run your usual pipeline but make sure it attaches to the graph address by passing `graph_address=("127.0.0.1", 25978)` as a kwarg to `ez.run`.
> ezmsg will soon includes a built-in performance monitor that can be used instead of this tool.

While the pipeline is running, you can run the signal-monitor tool with (`uv run`) `signal-monitor --graph-addr 127.0.0.1:25978`.
This tool operates on logfiles created by ezmsg. Logfiles will automatically be created when running a pipeline containing nodes decorated with `ezmsg.sigproc.util.profile.profile_subpub`,
and if the `EZMSG_LOGLEVEL` environment variable is set to DEBUG. The logfiles will be created in `~/.ezmsg/profile/ezprofiler.log` by default but this can be changed with the `EZMSG_PROFILE` environment variable.

This launches a window with graph visualized on the left. Click on a node's output box to get a live visualization on the right side of the screen plotting the data as it leaves that node. Use `a` to toggle auto-scaling. With auto-scaling off, use `-`, and `=` to zoom out and in, respectively.
Most of the nodes provided by `ezmsg.sigproc` are already decorated to enable profiling, as is any custom nodes that inherit from `ezmsg.sigproc.base.GenAxisArray`.
You can decorate other nodes with `ezmsg.sigproc.util.profile.profile_subpub` to enable profiling.

> Currently only 2-D outputs are supported!
During a run with profiling enabled, the logfiles will be created in the specified location. You may wish to additionally create a graph file: (`uv run`) `EZMSG_LOGLEVEL=WARN ezmsg mermaid > ~/.ezmsg/profile/ezprofiler.mermaid`

Don't forget to shutdown your graph service when you are done, e.g.: `ezmsg --address 127.0.0.1:25978 shutdown`
During or after a pipeline run with profiling enabled, you can run (`uv run `) `ezmsg-performance-monitor` to visualize the performance of the nodes in the pipeline.

> Unlike `signal-monitor`, this tool does not require the pipeline to attach to an existing graph service because it relies exclusively on the logfile.

> This performance monitor is soon to be deprecated in favor of monitoring tools built-in to ezmsg.

## Developers

Expand All @@ -78,8 +94,7 @@ We use [`uv`](https://docs.astral.sh/uv/getting-started/installation/) for devel
2. Fork ezmsg-tools and clone your fork to your local computer.
3. Open a terminal and `cd` to the cloned folder.
4. Make sure `pygraphviz` [pre-requisites](#pre-requisites) are installed.
* On mac: `export CFLAGS="-I $(brew --prefix graphviz)/include"` and `export LDFLAGS="-L $(brew --prefix graphviz)/lib"`
5. `uv sync --all-extras` to create a .venv and install ezmsg-tools including dev and test dependencies.
5. `uv sync --all-extras` to create a .venv and install ezmsg-tools including dev and test dependencies.
6. (Optional) Install pre-commit hooks: `uv run pre-commit install`
7. After editing code and making commits, Run the test suite before making a PR: `uv run pytest`

Expand Down
19 changes: 12 additions & 7 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,26 +6,27 @@ authors = [
]
license = "MIT"
readme = "README.md"
requires-python = ">=3.10.15"
requires-python = ">=3.11"
dynamic = ["version"]
dependencies = [
"ezmsg>=3.6.2",
"numpy>=1.26.0",
"typer>=0.24.1",
]

[dependency-groups]
dev = [
"pre-commit>=4.0.0",
"scipy>=1.14.1",
"ezmsg-sigproc>=2.8.0",
"ezmsg-sigproc>=2.18.0",
"ezmsg-simbiophys>=1.4.1",
{include-group = "lint"},
{include-group = "test"},
]
lint = [
"ruff>=0.12.9",
]
test = [
"ezmsg-sigproc>=1.6.0",
"pytest>=8.3.3",
]
docs = [
Expand All @@ -48,14 +49,17 @@ perfmon = [
"ezmsg-baseproc>=1.1.0",
]
sigmon = [
"pygame>=2.6.1",
"PySide6>=6.7",
"pygraphviz>=1.14",
"typer>=0.15.1",
"phosphor>=0.2",
"pandas",
"ezmsg-qt",
]

[project.scripts]
ezmsg-performance-monitor = "ezmsg.tools.perfmon.main:main"
ezmsg-signal-monitor = "ezmsg.tools.sigmon.main:main"
ezmsg-performance-monitor = "ezmsg.tools.perfmon.cli:main"
ezmsg-signal-monitor = "ezmsg.tools.sigmon.cli:main"

[build-system]
requires = ["hatchling", "hatch-vcs"]
Expand Down Expand Up @@ -89,4 +93,5 @@ known-third-party = ["ezmsg"]

[tool.uv.sources]
# Uncomment to use development version of ezmsg from git
ezmsg = { git = "https://github.com/ezmsg-org/ezmsg.git", branch = "dev" }
#ezmsg = { git = "https://github.com/ezmsg-org/ezmsg.git", branch = "dev" }
ezmsg-qt = { git = "https://github.com/ezmsg-org/ezmsg-qt.git", branch = "dynamic_subscriber" }
74 changes: 74 additions & 0 deletions scripts_nbs/generator/eeg_generator_graph.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
"""Test graph for sigmon development.

Generates synthetic EEG (time-domain) and routes a copy through
Window + Spectrum to produce frequency-domain data. Both branches
terminate at a no-op Sink so the topics exist in the graph for
sigmon to subscribe to.

Usage:
uv run python scripts/eeg_generator_graph.py
"""

import ezmsg.core as ez
from ezmsg.sigproc.spectrum import Spectrum, SpectrumSettings
from ezmsg.sigproc.window import Window, WindowSettings
from ezmsg.simbiophys import EEGSynth, EEGSynthSettings
from ezmsg.util.messages.axisarray import AxisArray


class Sink(ez.Unit):
"""Consumes messages and does nothing."""

INPUT_SIGNAL = ez.InputStream(AxisArray)

@ez.subscriber(INPUT_SIGNAL)
async def on_message(self, msg: AxisArray) -> None:
pass


def main() -> None:
eeg = EEGSynth(
EEGSynthSettings(
fs=2000.0,
n_time=100,
n_ch=16,
alpha_freq=10.5,
)
)

win = Window(
WindowSettings(
axis="time",
window_dur=0.5,
window_shift=0.2,
)
)

spec = Spectrum(
SpectrumSettings(
axis="time",
)
)

time_sink = Sink()
freq_sink = Sink()

ez.run(
components={
"EEG": eeg,
"WIN": win,
"SPEC": spec,
"TIME_SINK": time_sink,
"FREQ_SINK": freq_sink,
},
connections=(
(eeg.OUTPUT_SIGNAL, time_sink.INPUT_SIGNAL),
(eeg.OUTPUT_SIGNAL, win.INPUT_SIGNAL),
(win.OUTPUT_SIGNAL, spec.INPUT_SIGNAL),
(spec.OUTPUT_SIGNAL, freq_sink.INPUT_SIGNAL),
),
)


if __name__ == "__main__":
main()
12 changes: 6 additions & 6 deletions src/ezmsg/tools/dag.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,14 +61,14 @@ async def dag_with_timeout():
# where 'port' might be a pub (out) stream or a sub (input) stream.

b_refresh_dag = False
_monitor_topics = {"VISBUFF/INPUT_SIGNAL", "SIGMON/INPUT"}
for k, v in graph_connections.items():
if "VISBUFF/INPUT_SIGNAL" in v:
b_refresh_dag = True
loop.run_until_complete(
ez.graphserver.GraphService(address=graph_address).disconnect(k, "VISBUFF/INPUT_SIGNAL")
)
for sub in v:
if any(mt in sub for mt in _monitor_topics):
b_refresh_dag = True
asyncio.run(ez.graphserver.GraphService(address=graph_address).disconnect(k, sub))
if b_refresh_dag:
dag = loop.run_until_complete(ez.graphserver.GraphService(address=graph_address).dag())
dag = asyncio.run(ez.graphserver.GraphService(address=graph_address).dag())
graph_connections = dag.graph.copy()

# Generate UUID node names
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -279,5 +279,9 @@ def update_hist(data):
return fig, f"Sum: {proc_sum:.2f} ms"


def main() -> None:
app.run(debug=True)


if __name__ == "__main__":
app.run(debug=True)
87 changes: 0 additions & 87 deletions src/ezmsg/tools/proc.py

This file was deleted.

Loading
Loading