def METHOD_NAME(self) -> Iterator[ThreadManager]:
    """Context manager that starts emulated OT-2 hardware environment. A
    hardware controller is returned."""
    modules = self._config.modules
    # Entry point for the emulator app process
    def _run_app():
        async def _async_entry():
            await asyncio.gather(
                run_smoothie.run(self._config),
                run_app.run(self._config, modules=[m.value for m in modules]),
            )
        asyncio.run(_async_entry())
    proc = Process(target=_run_app)
    proc.daemon = True
    proc.start()
    # Entry point for process that waits for emulation to be ready.
    async def _wait_ready() -> None:
        c = await ModuleStatusClient.connect(
            host="localhost", port=self._config.module_server.port
        )
        await wait_emulators(client=c, modules=modules, timeout=5)
        c.close()
    def _run_wait_ready():
        asyncio.run(_wait_ready())
    ready_proc = Process(target=_run_wait_ready)
    ready_proc.daemon = True
    ready_proc.start()
    ready_proc.join()
    # Hardware controller
    conf = build_config({})
    emulator = ThreadManager(
        API.build_hardware_controller,
        conf,
        GCodeEngine.URI_TEMPLATE % self._config.smoothie.port,
    )
    # Wait for modules to be present
    while len(emulator.attached_modules) != len(modules):
        time.sleep(0.1)
    yield emulator
    # Finished. Stop the emulator
    proc.kill()
    proc.join()