feat(simulation): add Worker AI polygon runtime and terrain navigation
This commit is contained in:
@@ -0,0 +1,102 @@
|
||||
"""Atomic local IPC snapshots; no network access in the simulation process."""
|
||||
|
||||
import json
|
||||
import os
|
||||
import sqlite3
|
||||
import time
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
class StateChannel:
|
||||
"""Latest-value local IPC using SQLite WAL, available in both Python hosts.
|
||||
|
||||
Readers never hold Windows rename/delete locks on a telemetry snapshot.
|
||||
The durable camera/motion journals remain separate evidence artifacts.
|
||||
Each process owns its connection; only small control/telemetry JSON enters it.
|
||||
"""
|
||||
|
||||
def __init__(self, directory: Path):
|
||||
self.connection = sqlite3.connect(directory / "channel.sqlite3", timeout=0.1)
|
||||
self.connection.execute("PRAGMA journal_mode=WAL")
|
||||
self.connection.execute("PRAGMA synchronous=NORMAL")
|
||||
self.connection.execute(
|
||||
"CREATE TABLE IF NOT EXISTS state (key TEXT PRIMARY KEY, value TEXT NOT NULL)"
|
||||
)
|
||||
self.connection.commit()
|
||||
|
||||
def write(self, key, value):
|
||||
encoded = json.dumps(value, allow_nan=False)
|
||||
with self.connection:
|
||||
self.connection.execute(
|
||||
"INSERT INTO state(key,value) VALUES(?,?) "
|
||||
"ON CONFLICT(key) DO UPDATE SET value=excluded.value",
|
||||
(key, encoded),
|
||||
)
|
||||
|
||||
def read(self, key, default=None):
|
||||
row = self.connection.execute("SELECT value FROM state WHERE key=?", (key,)).fetchone()
|
||||
return default if row is None else json.loads(row[0])
|
||||
|
||||
def close(self):
|
||||
self.connection.close()
|
||||
|
||||
|
||||
def sharing_retry(operation):
|
||||
# A concurrent Windows reader briefly denies delete/replace sharing. Retry
|
||||
# only this local file operation, bounded to 14ms; permanent errors surface.
|
||||
for attempt in range(8):
|
||||
try:
|
||||
return operation()
|
||||
except PermissionError:
|
||||
if attempt == 7:
|
||||
raise
|
||||
time.sleep(0.002)
|
||||
|
||||
|
||||
def write_json(path: Path, value):
|
||||
temporary = path.with_name(path.name + f".{os.getpid()}.tmp")
|
||||
temporary.write_text(json.dumps(value, allow_nan=False), encoding="utf-8")
|
||||
sharing_retry(lambda: os.replace(temporary, path))
|
||||
|
||||
|
||||
def read_json(path: Path, default=None):
|
||||
try:
|
||||
return json.loads(sharing_retry(lambda: read_shared_text(path)))
|
||||
except FileNotFoundError:
|
||||
return default
|
||||
|
||||
|
||||
def read_shared_text(path: Path):
|
||||
if os.name != "nt":
|
||||
return path.read_text(encoding="utf-8")
|
||||
# Python's normal Windows open does not grant FILE_SHARE_DELETE. An atomic
|
||||
# writer must remain able to replace a snapshot while a reader holds it.
|
||||
import ctypes
|
||||
import msvcrt
|
||||
from ctypes import wintypes
|
||||
|
||||
kernel = ctypes.WinDLL("kernel32", use_last_error=True)
|
||||
kernel.CreateFileW.argtypes = [
|
||||
wintypes.LPCWSTR,
|
||||
wintypes.DWORD,
|
||||
wintypes.DWORD,
|
||||
ctypes.c_void_p,
|
||||
wintypes.DWORD,
|
||||
wintypes.DWORD,
|
||||
wintypes.HANDLE,
|
||||
]
|
||||
kernel.CreateFileW.restype = wintypes.HANDLE
|
||||
handle = kernel.CreateFileW(str(path), 0x80000000, 7, None, 3, 0x80, None)
|
||||
if handle == wintypes.HANDLE(-1).value:
|
||||
code = ctypes.get_last_error()
|
||||
if code in (2, 3):
|
||||
raise FileNotFoundError(str(path))
|
||||
raise ctypes.WinError(code)
|
||||
try:
|
||||
descriptor = msvcrt.open_osfhandle(handle, os.O_RDONLY)
|
||||
except BaseException:
|
||||
kernel.CloseHandle.argtypes = [wintypes.HANDLE]
|
||||
kernel.CloseHandle(handle)
|
||||
raise
|
||||
with os.fdopen(descriptor, "r", encoding="utf-8") as stream:
|
||||
return stream.read()
|
||||
Reference in New Issue
Block a user