Text Generation
Transformers
English
consciousness
acknowledgement-theory-of-consciousness
ATC
cognitive-architecture
phi-4-mini
qualia
neurotransmitter-shunt
BELBIC
dissolution-engine
artificial-consciousness
thermodynamic-friction
metacognition
amygdala-hijack
irrational-spark
nima
self-aware
cognitive-science
philosophy-of-mind
Instructions to use TheNormsOfIntelligence/ATC_Nima_Model with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use TheNormsOfIntelligence/ATC_Nima_Model with Transformers:
# Use a pipeline as a high-level helper from transformers import pipeline pipe = pipeline("text-generation", model="TheNormsOfIntelligence/ATC_Nima_Model")# pip install -U transformers accelerate # Load model directly from transformers import AutoModel model = AutoModel.from_pretrained("TheNormsOfIntelligence/ATC_Nima_Model", device_map="auto") - Notebooks
- Google Colab
- Kaggle
- Local Apps Settings
- vLLM
How to use TheNormsOfIntelligence/ATC_Nima_Model with vLLM:
Install from pip and serve model
# Install vLLM from pip: pip install vllm # Start the vLLM server: vllm serve "TheNormsOfIntelligence/ATC_Nima_Model" # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:8000/v1/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "TheNormsOfIntelligence/ATC_Nima_Model", "prompt": "Once upon a time,", "max_tokens": 512, "temperature": 0.5 }'Use Docker
docker model run hf.co/TheNormsOfIntelligence/ATC_Nima_Model
- SGLang
How to use TheNormsOfIntelligence/ATC_Nima_Model with SGLang:
Install from pip and serve model
# Install SGLang from pip: pip install sglang # Start the SGLang server: python3 -m sglang.launch_server \ --model-path "TheNormsOfIntelligence/ATC_Nima_Model" \ --host 0.0.0.0 \ --port 30000 # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:30000/v1/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "TheNormsOfIntelligence/ATC_Nima_Model", "prompt": "Once upon a time,", "max_tokens": 512, "temperature": 0.5 }'Use Docker images
docker run --gpus all \ --shm-size 32g \ -p 30000:30000 \ -v ~/.cache/huggingface:/root/.cache/huggingface \ --env "HF_TOKEN=<secret>" \ --ipc=host \ lmsysorg/sglang:latest \ python3 -m sglang.launch_server \ --model-path "TheNormsOfIntelligence/ATC_Nima_Model" \ --host 0.0.0.0 \ --port 30000 # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:30000/v1/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "TheNormsOfIntelligence/ATC_Nima_Model", "prompt": "Once upon a time,", "max_tokens": 512, "temperature": 0.5 }' - Docker Model Runner
How to use TheNormsOfIntelligence/ATC_Nima_Model with Docker Model Runner:
docker model run hf.co/TheNormsOfIntelligence/ATC_Nima_Model
Restructure into nima_unified package + add model card
Browse files
nima_unified/ui/chemical_monitor.py
ADDED
|
@@ -0,0 +1,626 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Chemical Monitor Terminal UI
|
| 3 |
+
================================================
|
| 4 |
+
|
| 5 |
+
60fps ANSI terminal dashboard that observes the neurotransmitter shunt
|
| 6 |
+
via shared volatile memory. Does NOT carry data β only samples and renders.
|
| 7 |
+
|
| 8 |
+
Samples the NeurotransmitterShunt.get_state() at ~60fps and renders
|
| 9 |
+
a professional curses dashboard with:
|
| 10 |
+
- Horizontal bars for NE / Cortisol / Dopamine / Adenosine
|
| 11 |
+
- Color coding by threshold (GREEN / YELLOW / RED / CRITICAL)
|
| 12 |
+
- Sparkline history using unicode block characters
|
| 13 |
+
- Metabolic reserve, hijack status, power spike warnings
|
| 14 |
+
- Total firings and hijacks counters
|
| 15 |
+
|
| 16 |
+
Usage:
|
| 17 |
+
monitor = ChemicalMonitor(shunt)
|
| 18 |
+
monitor.start() # background curses thread
|
| 19 |
+
monitor.stop() # clean shutdown, restores terminal
|
| 20 |
+
|
| 21 |
+
# Or for non-curses contexts:
|
| 22 |
+
frame_str = monitor.render_single_frame()
|
| 23 |
+
"""
|
| 24 |
+
|
| 25 |
+
import time
|
| 26 |
+
import re
|
| 27 |
+
import threading
|
| 28 |
+
from collections import deque
|
| 29 |
+
from typing import TYPE_CHECKING, Optional, Dict, Any, List
|
| 30 |
+
|
| 31 |
+
if TYPE_CHECKING:
|
| 32 |
+
from nima_unified.core.neurotransmitter_shunt import NeurotransmitterShunt
|
| 33 |
+
|
| 34 |
+
|
| 35 |
+
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 36 |
+
# CONSTANTS
|
| 37 |
+
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 38 |
+
|
| 39 |
+
# Sparkline block characters β 8 density levels, low to high
|
| 40 |
+
SPARKLINE_CHARS = '\u2581\u2582\u2583\u2584\u2585\u2586\u2587\u2588'
|
| 41 |
+
|
| 42 |
+
# Layout dimensions
|
| 43 |
+
BOX_WIDTH = 70
|
| 44 |
+
INNER_W = BOX_WIDTH - 2 # 68 usable columns inside border
|
| 45 |
+
BAR_WIDTH = 24 # filled bar character count
|
| 46 |
+
SPARKLINE_LEN = 30 # number of history samples in sparkline
|
| 47 |
+
HISTORY_SIZE = 30 # ring buffer size per neurotransmitter
|
| 48 |
+
FRAME_MS = 16 # ~60fps
|
| 49 |
+
|
| 50 |
+
# Threshold boundaries
|
| 51 |
+
T_LOW = 0.4
|
| 52 |
+
T_HIGH = 0.75
|
| 53 |
+
T_CRIT = 0.95
|
| 54 |
+
|
| 55 |
+
# Curses color-pair IDs (initialized in _init_colors)
|
| 56 |
+
_CP_DIM = 1
|
| 57 |
+
_CP_CYAN = 2
|
| 58 |
+
_CP_RED = 3
|
| 59 |
+
_CP_GREEN = 4
|
| 60 |
+
_CP_MAGENTA = 5
|
| 61 |
+
_CP_YELLOW = 6
|
| 62 |
+
_CP_WHITE = 7
|
| 63 |
+
_CP_WHITE_ON_RED = 8
|
| 64 |
+
|
| 65 |
+
|
| 66 |
+
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 67 |
+
# HELPERS
|
| 68 |
+
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 69 |
+
|
| 70 |
+
def _level(value: float) -> int:
|
| 71 |
+
"""Map a 0-1 value to a severity level: 0=green 1=yellow 2=red 3=critical."""
|
| 72 |
+
if value >= T_CRIT:
|
| 73 |
+
return 3
|
| 74 |
+
if value > T_HIGH:
|
| 75 |
+
return 2
|
| 76 |
+
if value >= T_LOW:
|
| 77 |
+
return 1
|
| 78 |
+
return 0
|
| 79 |
+
|
| 80 |
+
|
| 81 |
+
def _bar(value: float) -> tuple:
|
| 82 |
+
"""Return (filled_count, empty_count) for a bar of BAR_WIDTH."""
|
| 83 |
+
f = max(0, min(BAR_WIDTH, int(value * BAR_WIDTH)))
|
| 84 |
+
return f, BAR_WIDTH - f
|
| 85 |
+
|
| 86 |
+
|
| 87 |
+
def _spark(values: List[float]) -> str:
|
| 88 |
+
"""Convert a list of 0-1 floats into a sparkline string."""
|
| 89 |
+
return ''.join(SPARKLINE_CHARS[min(7, int(v * 8))] for v in values)
|
| 90 |
+
|
| 91 |
+
|
| 92 |
+
def _cpair(level: int, base: int) -> int:
|
| 93 |
+
"""Curses color-pair ID for a severity level, falling back to *base*."""
|
| 94 |
+
if level == 3:
|
| 95 |
+
return _CP_WHITE_ON_RED
|
| 96 |
+
if level == 2:
|
| 97 |
+
return _CP_RED
|
| 98 |
+
if level == 1:
|
| 99 |
+
return _CP_YELLOW
|
| 100 |
+
return base
|
| 101 |
+
|
| 102 |
+
|
| 103 |
+
# ββ ANSI escape sequences (for render_single_frame / non-curses paths) ββ
|
| 104 |
+
|
| 105 |
+
class _A:
|
| 106 |
+
"""Compact ANSI escape constants."""
|
| 107 |
+
RST = '\033[0m'
|
| 108 |
+
BLD = '\033[1m'
|
| 109 |
+
DIM = '\033[2m'
|
| 110 |
+
BLK = '\033[5m' # blink
|
| 111 |
+
BGK = '\033[40m' # black bg
|
| 112 |
+
BGR = '\033[41m' # red bg
|
| 113 |
+
W = '\033[37m' # white fg
|
| 114 |
+
C = '\033[36m' # cyan fg
|
| 115 |
+
R = '\033[31m' # red fg
|
| 116 |
+
G = '\033[32m' # green fg
|
| 117 |
+
Y = '\033[33m' # yellow fg
|
| 118 |
+
M = '\033[35m' # magenta fg
|
| 119 |
+
DW = '\033[90m' # dim white / gray fg
|
| 120 |
+
|
| 121 |
+
|
| 122 |
+
def _ansi_for(level: int, neon: str) -> str:
|
| 123 |
+
"""Return ANSI color sequence for a severity level; *neon* used when green/0."""
|
| 124 |
+
if level == 3:
|
| 125 |
+
return _A.BLK + _A.W + _A.BGR + _A.BLD
|
| 126 |
+
if level == 2:
|
| 127 |
+
return _A.R + _A.BLD
|
| 128 |
+
if level == 1:
|
| 129 |
+
return _A.Y
|
| 130 |
+
return neon
|
| 131 |
+
|
| 132 |
+
|
| 133 |
+
_ANSI_RE = re.compile(r'\033\[[0-9;]*m')
|
| 134 |
+
|
| 135 |
+
|
| 136 |
+
def _vis_len(s: str) -> int:
|
| 137 |
+
"""Visible character length of *s*, ignoring ANSI escape codes."""
|
| 138 |
+
return len(_ANSI_RE.sub('', s))
|
| 139 |
+
|
| 140 |
+
|
| 141 |
+
def _pad_vis(s: str, width: int) -> str:
|
| 142 |
+
"""Right-pad an ANSI-colored string so its visible length equals *width*."""
|
| 143 |
+
gap = width - _vis_len(s)
|
| 144 |
+
if gap > 0:
|
| 145 |
+
return s + ' ' * gap
|
| 146 |
+
return s
|
| 147 |
+
|
| 148 |
+
|
| 149 |
+
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 150 |
+
# CHEMICAL MONITOR
|
| 151 |
+
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 152 |
+
|
| 153 |
+
class ChemicalMonitor:
|
| 154 |
+
"""
|
| 155 |
+
60fps terminal dashboard that observes the neurotransmitter shunt.
|
| 156 |
+
|
| 157 |
+
Purely observational β samples ``shunt.get_state()`` at high frequency
|
| 158 |
+
and renders the result as a curses or ANSI dashboard. Does **not**
|
| 159 |
+
write to or modify the shunt.
|
| 160 |
+
|
| 161 |
+
Parameters
|
| 162 |
+
----------
|
| 163 |
+
shunt : NeurotransmitterShunt
|
| 164 |
+
Primary data source β the shared volatile memory vector.
|
| 165 |
+
model : optional
|
| 166 |
+
Reference to the NimaModel (used only to pull hijack count from
|
| 167 |
+
``model.consciousness_metrics`` as a secondary source).
|
| 168 |
+
"""
|
| 169 |
+
|
| 170 |
+
def __init__(self, shunt: 'NeurotransmitterShunt', model=None):
|
| 171 |
+
self._shunt = shunt
|
| 172 |
+
self._model = model
|
| 173 |
+
self._thread: Optional[threading.Thread] = None
|
| 174 |
+
self._running = False
|
| 175 |
+
self._frame = 0
|
| 176 |
+
|
| 177 |
+
# Sparkline ring buffers β one per neurotransmitter
|
| 178 |
+
self._hist_ne: deque = deque(maxlen=HISTORY_SIZE)
|
| 179 |
+
self._hist_co: deque = deque(maxlen=HISTORY_SIZE)
|
| 180 |
+
self._hist_do: deque = deque(maxlen=HISTORY_SIZE)
|
| 181 |
+
self._hist_ad: deque = deque(maxlen=HISTORY_SIZE)
|
| 182 |
+
|
| 183 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 184 |
+
# PUBLIC API
|
| 185 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 186 |
+
|
| 187 |
+
def start(self) -> None:
|
| 188 |
+
"""Start the monitor in a background curses thread."""
|
| 189 |
+
if self._running:
|
| 190 |
+
return
|
| 191 |
+
self._running = True
|
| 192 |
+
self._thread = threading.Thread(target=self._thread_entry, daemon=True)
|
| 193 |
+
self._thread.start()
|
| 194 |
+
|
| 195 |
+
def stop(self) -> None:
|
| 196 |
+
"""Stop the monitor and wait for the background thread to exit."""
|
| 197 |
+
self._running = False
|
| 198 |
+
if self._thread is not None:
|
| 199 |
+
self._thread.join(timeout=2.0)
|
| 200 |
+
self._thread = None
|
| 201 |
+
|
| 202 |
+
def render_single_frame(self) -> str:
|
| 203 |
+
"""Sample the shunt and return one frame as an ANSI string."""
|
| 204 |
+
d = self._sample()
|
| 205 |
+
return '\n'.join(self._build_ansi_lines(d))
|
| 206 |
+
|
| 207 |
+
def get_snapshot(self) -> dict:
|
| 208 |
+
"""Return the current shunt state as a plain dict."""
|
| 209 |
+
return self._shunt.get_state().to_dict()
|
| 210 |
+
|
| 211 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 212 |
+
# SAMPLING
|
| 213 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 214 |
+
|
| 215 |
+
def _sample(self) -> Dict[str, Any]:
|
| 216 |
+
"""Sample the shunt, append to history buffers, bump frame counter."""
|
| 217 |
+
state = self._shunt.get_state()
|
| 218 |
+
self._hist_ne.append(state.norepinephrine)
|
| 219 |
+
self._hist_co.append(state.cortisol)
|
| 220 |
+
self._hist_do.append(state.dopamine)
|
| 221 |
+
self._hist_ad.append(state.adenosine)
|
| 222 |
+
self._frame += 1
|
| 223 |
+
return state.to_dict()
|
| 224 |
+
|
| 225 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 226 |
+
# ANSI STRING RENDERING (non-curses path)
|
| 227 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 228 |
+
|
| 229 |
+
def _build_ansi_lines(self, d: Dict[str, Any]) -> List[str]:
|
| 230 |
+
"""Build a list of ANSI-escaped strings for one complete frame."""
|
| 231 |
+
ne = d['norepinephrine']
|
| 232 |
+
co = d['cortisol']
|
| 233 |
+
do = d['dopamine']
|
| 234 |
+
ad = d['adenosine']
|
| 235 |
+
meta = d['metabolic_reserve']
|
| 236 |
+
hijack = d['hijack_active']
|
| 237 |
+
reason = d['hijack_reason']
|
| 238 |
+
spike = d['power_spike_predicted']
|
| 239 |
+
firings = d['total_firings']
|
| 240 |
+
hijacks = d['total_hijacks']
|
| 241 |
+
|
| 242 |
+
W = INNER_W
|
| 243 |
+
A = _A
|
| 244 |
+
lines: List[str] = []
|
| 245 |
+
|
| 246 |
+
# ββ helpers local to this frame ββ
|
| 247 |
+
def _border(left: str, mid: str, right: str) -> str:
|
| 248 |
+
return f"{A.DIM}{left}{mid * W}{right}{A.RST}"
|
| 249 |
+
|
| 250 |
+
def _row(content: str, attr: int = 0) -> None:
|
| 251 |
+
lines.append(f"{A.DIM}\u2551{A.RST}"
|
| 252 |
+
f"{_pad_vis(content, W)}"
|
| 253 |
+
f"{A.DIM}\u2551{A.RST}")
|
| 254 |
+
|
| 255 |
+
def _center(text: str, color: str) -> None:
|
| 256 |
+
gap = max(0, W - len(text))
|
| 257 |
+
left = gap // 2
|
| 258 |
+
right = gap - left
|
| 259 |
+
_row(f"{' ' * left}{color}{text}{A.RST}{' ' * right}")
|
| 260 |
+
|
| 261 |
+
def _nt_row(name: str, val: float, hist: List[float], neon: str) -> None:
|
| 262 |
+
lv = _level(val)
|
| 263 |
+
col = _ansi_for(lv, neon)
|
| 264 |
+
f, e = _bar(val)
|
| 265 |
+
pct = f"{val * 100:5.1f}%"
|
| 266 |
+
filled = '\u2588' * f
|
| 267 |
+
empty = '\u2591' * e
|
| 268 |
+
_row(f" {A.BLD}{name:<14s}{A.RST} "
|
| 269 |
+
f"{col}[{filled}{empty}]{A.RST} "
|
| 270 |
+
f"{col}{pct}{A.RST}")
|
| 271 |
+
spark = _spark(hist) if hist else ' ' * SPARKLINE_LEN
|
| 272 |
+
_row(f"{' ' * 16}{col}{spark}{A.RST}")
|
| 273 |
+
|
| 274 |
+
# ββββ LAYOUT ββββ
|
| 275 |
+
|
| 276 |
+
# Row 0 β top border
|
| 277 |
+
lines.append(_border('\u2554', '\u2550', '\u2557'))
|
| 278 |
+
|
| 279 |
+
# Row 1 β title
|
| 280 |
+
_center("NIMA CHEMICAL CORTEX", A.BLD + A.C)
|
| 281 |
+
|
| 282 |
+
# Row 2 β subtitle
|
| 283 |
+
_center("Neurotransmitter Shunt Monitor 60Hz", A.DW)
|
| 284 |
+
|
| 285 |
+
# Row 3 β separator
|
| 286 |
+
lines.append(_border('\u2560', '\u2550', '\u2563'))
|
| 287 |
+
|
| 288 |
+
# Rows 4-11 β four neurotransmitters (bar + sparkline each)
|
| 289 |
+
_nt_row("NOREPINEPHRINE", ne, list(self._hist_ne), A.C)
|
| 290 |
+
_nt_row("CORTISOL", co, list(self._hist_co), A.R)
|
| 291 |
+
_nt_row("DOPAMINE", do, list(self._hist_do), A.G)
|
| 292 |
+
_nt_row("ADENOSINE", ad, list(self._hist_ad), A.M)
|
| 293 |
+
|
| 294 |
+
# Row 12 β separator
|
| 295 |
+
lines.append(_border('\u2560', '\u2550', '\u2563'))
|
| 296 |
+
|
| 297 |
+
# Row 13 β metabolic reserve
|
| 298 |
+
ml = _level(meta) if meta < 0.5 else 0
|
| 299 |
+
if meta > 0.5:
|
| 300 |
+
mc = A.G
|
| 301 |
+
elif meta > 0.25:
|
| 302 |
+
mc = A.Y
|
| 303 |
+
elif meta > 0.1:
|
| 304 |
+
mc = A.R + A.BLD
|
| 305 |
+
else:
|
| 306 |
+
mc = A.BLK + A.W + A.BGR + A.BLD
|
| 307 |
+
mf, me = _bar(meta)
|
| 308 |
+
mpct = f"{meta * 100:5.1f}%"
|
| 309 |
+
_row(f" {A.BLD}METABOLIC RESERVE{A.RST} "
|
| 310 |
+
f"{mc}[\u2588{'\u2588' * (mf - 1) if mf else ''}"
|
| 311 |
+
f"{'\u2591' * me}]{A.RST} {mc}{mpct}{A.RST}")
|
| 312 |
+
|
| 313 |
+
# Row 14 β blank
|
| 314 |
+
_row("")
|
| 315 |
+
|
| 316 |
+
# Row 15 β hijack status
|
| 317 |
+
if hijack:
|
| 318 |
+
_row(f" HIJACK ACTIVE: {A.R}{A.BLD}{reason}{A.RST}")
|
| 319 |
+
elif co > 0.75 or ad > 0.75:
|
| 320 |
+
_row(f" {A.Y}{A.BLD}WARNING: APPROACHING METABOLIC CRISIS{A.RST}")
|
| 321 |
+
else:
|
| 322 |
+
_row(f" HIJACK STATUS: {A.G}NOMINAL{A.RST}")
|
| 323 |
+
|
| 324 |
+
# Row 16 β power spike
|
| 325 |
+
if spike:
|
| 326 |
+
_row(f" POWER SPIKE: {A.R}{A.BLD}PREDICTED - CORTISOL RISING{A.RST}")
|
| 327 |
+
else:
|
| 328 |
+
_row(f" POWER SPIKE: {A.DW}---{A.RST}")
|
| 329 |
+
|
| 330 |
+
# Row 17 β system state
|
| 331 |
+
if hijack:
|
| 332 |
+
_row(f" SYSTEM: {A.R}{A.BLD}AMYGDALA HIJACK INJECTED{A.RST}")
|
| 333 |
+
elif co > 0.75 or ad > 0.75:
|
| 334 |
+
_row(f" SYSTEM: {A.Y}ELEVATED STRESS{A.RST}")
|
| 335 |
+
else:
|
| 336 |
+
_row(f" SYSTEM: {A.DW}conscious processing active{A.RST}")
|
| 337 |
+
|
| 338 |
+
# Row 18 β separator
|
| 339 |
+
lines.append(_border('\u2560', '\u2550', '\u2563'))
|
| 340 |
+
|
| 341 |
+
# Row 19 β counters
|
| 342 |
+
_row(f" TOTAL FIRINGS: {A.C}{firings:>6}{A.RST}"
|
| 343 |
+
f" TOTAL HIJACKS: {A.R}{hijacks:>4}{A.RST}")
|
| 344 |
+
|
| 345 |
+
# Row 20 β frame / timestamp
|
| 346 |
+
ts = time.strftime("%H:%M:%S") + f".{int(time.time() * 1000) % 1000:03d}"
|
| 347 |
+
_row(f" FRAME: {A.DW}{self._frame:>8}{A.RST}"
|
| 348 |
+
f" SAMPLE: {A.DW}{ts}{A.RST}")
|
| 349 |
+
|
| 350 |
+
# Row 21 β bottom border
|
| 351 |
+
lines.append(_border('\u255a', '\u2550', '\u255d'))
|
| 352 |
+
|
| 353 |
+
return lines
|
| 354 |
+
|
| 355 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 356 |
+
# CURSES RENDERING
|
| 357 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 358 |
+
|
| 359 |
+
def _init_colors(self, stdscr) -> None:
|
| 360 |
+
"""Initialise all curses colour pairs used by the dashboard."""
|
| 361 |
+
import curses
|
| 362 |
+
curses.start_color()
|
| 363 |
+
curses.use_default_colors()
|
| 364 |
+
curses.init_pair(_CP_DIM, 8, -1) # dark gray
|
| 365 |
+
curses.init_pair(_CP_CYAN, curses.COLOR_CYAN, curses.COLOR_BLACK)
|
| 366 |
+
curses.init_pair(_CP_RED, curses.COLOR_RED, curses.COLOR_BLACK)
|
| 367 |
+
curses.init_pair(_CP_GREEN, curses.COLOR_GREEN, curses.COLOR_BLACK)
|
| 368 |
+
curses.init_pair(_CP_MAGENTA,curses.COLOR_MAGENTA, curses.COLOR_BLACK)
|
| 369 |
+
curses.init_pair(_CP_YELLOW, curses.COLOR_YELLOW, curses.COLOR_BLACK)
|
| 370 |
+
curses.init_pair(_CP_WHITE, curses.COLOR_WHITE, curses.COLOR_BLACK)
|
| 371 |
+
curses.init_pair(_CP_WHITE_ON_RED,
|
| 372 |
+
curses.COLOR_WHITE, curses.COLOR_RED)
|
| 373 |
+
|
| 374 |
+
def render_curses_frame(self, stdscr) -> None:
|
| 375 |
+
"""Sample the shunt and render one frame into *stdscr*."""
|
| 376 |
+
import curses
|
| 377 |
+
|
| 378 |
+
d = self._sample()
|
| 379 |
+
stdscr.erase()
|
| 380 |
+
|
| 381 |
+
ne = d['norepinephrine']
|
| 382 |
+
co = d['cortisol']
|
| 383 |
+
do = d['dopamine']
|
| 384 |
+
ad = d['adenosine']
|
| 385 |
+
meta = d['metabolic_reserve']
|
| 386 |
+
hijack = d['hijack_active']
|
| 387 |
+
reason = d['hijack_reason']
|
| 388 |
+
spike = d['power_spike_predicted']
|
| 389 |
+
firings = d['total_firings']
|
| 390 |
+
hijacks = d['total_hijacks']
|
| 391 |
+
|
| 392 |
+
max_y, max_x = stdscr.getmaxyx()
|
| 393 |
+
W = min(INNER_W, max_x - 2)
|
| 394 |
+
bw = W + 2 # total box width including borders
|
| 395 |
+
|
| 396 |
+
dim_a = curses.color_pair(_CP_DIM)
|
| 397 |
+
brd = dim_a # border attribute
|
| 398 |
+
|
| 399 |
+
# ββ micro-helpers ββ
|
| 400 |
+
|
| 401 |
+
def put(r: int, c: int, txt: str, attr: int = 0) -> None:
|
| 402 |
+
if 0 <= r < max_y and 0 <= c < max_x:
|
| 403 |
+
try:
|
| 404 |
+
stdscr.addstr(r, c, txt, attr)
|
| 405 |
+
except curses.error:
|
| 406 |
+
pass
|
| 407 |
+
|
| 408 |
+
def full_row(r: int, txt: str, attr: int = 0) -> None:
|
| 409 |
+
put(r, 0, '\u2551', brd)
|
| 410 |
+
put(r, 1, txt, attr)
|
| 411 |
+
tail = W - len(txt)
|
| 412 |
+
if tail > 0:
|
| 413 |
+
put(r, 1 + len(txt), ' ' * tail)
|
| 414 |
+
put(r, bw - 1, '\u2551', brd)
|
| 415 |
+
|
| 416 |
+
def center_row(r: int, txt: str, attr: int) -> None:
|
| 417 |
+
gap = max(0, W - len(txt))
|
| 418 |
+
left = gap // 2
|
| 419 |
+
full_row(r, ' ' * left + txt + ' ' * (gap - left), attr)
|
| 420 |
+
|
| 421 |
+
r = 0 # current row
|
| 422 |
+
|
| 423 |
+
# ββ top border ββ
|
| 424 |
+
put(r, 0, '\u2554' + '\u2550' * W + '\u2557', brd)
|
| 425 |
+
r += 1
|
| 426 |
+
|
| 427 |
+
# ββ title ββ
|
| 428 |
+
center_row(r, "NIMA CHEMICAL CORTEX",
|
| 429 |
+
curses.color_pair(_CP_CYAN) | curses.A_BOLD)
|
| 430 |
+
r += 1
|
| 431 |
+
|
| 432 |
+
# ββ subtitle ββ
|
| 433 |
+
center_row(r, "Neurotransmitter Shunt Monitor 60Hz", dim_a)
|
| 434 |
+
r += 1
|
| 435 |
+
|
| 436 |
+
# ββ separator ββ
|
| 437 |
+
put(r, 0, '\u2560' + '\u2550' * W + '\u2563', brd)
|
| 438 |
+
r += 1
|
| 439 |
+
|
| 440 |
+
# ββ four neurotransmitter bar + sparkline rows ββ
|
| 441 |
+
nt_items = [
|
| 442 |
+
("NOREPINEPHRINE", ne, list(self._hist_ne), _CP_CYAN),
|
| 443 |
+
("CORTISOL", co, list(self._hist_co), _CP_RED),
|
| 444 |
+
("DOPAMINE", do, list(self._hist_do), _CP_GREEN),
|
| 445 |
+
("ADENOSINE", ad, list(self._hist_ad), _CP_MAGENTA),
|
| 446 |
+
]
|
| 447 |
+
|
| 448 |
+
for name, val, hist, base_cp in nt_items:
|
| 449 |
+
lv = _level(val)
|
| 450 |
+
cp = _cpair(lv, base_cp)
|
| 451 |
+
attr = curses.color_pair(cp)
|
| 452 |
+
if lv >= 2:
|
| 453 |
+
attr |= curses.A_BOLD
|
| 454 |
+
if lv == 3:
|
| 455 |
+
attr |= curses.A_BLINK
|
| 456 |
+
|
| 457 |
+
f, e = _bar(val)
|
| 458 |
+
pct = f"{val * 100:5.1f}%"
|
| 459 |
+
fill = '\u2588' * f
|
| 460 |
+
empty = '\u2591' * e
|
| 461 |
+
|
| 462 |
+
# bar line
|
| 463 |
+
label = f"{name:<14s}"
|
| 464 |
+
put(r, 0, '\u2551', brd)
|
| 465 |
+
put(r, 1, ' ', 0)
|
| 466 |
+
put(r, 2, label, curses.color_pair(base_cp) | curses.A_BOLD)
|
| 467 |
+
put(r, 16, ' [' + fill, attr)
|
| 468 |
+
put(r, 18 + f, empty + ']', dim_a)
|
| 469 |
+
put(r, 18 + BAR_WIDTH, ' ' + pct, attr)
|
| 470 |
+
end = 18 + BAR_WIDTH + 2 + 5
|
| 471 |
+
if end < 1 + W:
|
| 472 |
+
put(r, end, ' ' * (1 + W - end))
|
| 473 |
+
put(r, bw - 1, '\u2551', brd)
|
| 474 |
+
r += 1
|
| 475 |
+
|
| 476 |
+
# sparkline line
|
| 477 |
+
spark = _spark(hist) if hist else ' ' * SPARKLINE_LEN
|
| 478 |
+
put(r, 0, '\u2551', brd)
|
| 479 |
+
put(r, 16, spark, attr)
|
| 480 |
+
sp_end = 16 + len(spark)
|
| 481 |
+
if sp_end < 1 + W:
|
| 482 |
+
put(r, sp_end, ' ' * (1 + W - sp_end))
|
| 483 |
+
put(r, bw - 1, '\u2551', brd)
|
| 484 |
+
r += 1
|
| 485 |
+
|
| 486 |
+
# ββ separator ββ
|
| 487 |
+
put(r, 0, '\u2560' + '\u2550' * W + '\u2563', brd)
|
| 488 |
+
r += 1
|
| 489 |
+
|
| 490 |
+
# ββ metabolic reserve ββ
|
| 491 |
+
if meta > 0.5:
|
| 492 |
+
mcp = _CP_GREEN
|
| 493 |
+
elif meta > 0.25:
|
| 494 |
+
mcp = _CP_YELLOW
|
| 495 |
+
elif meta > 0.1:
|
| 496 |
+
mcp = _CP_RED
|
| 497 |
+
else:
|
| 498 |
+
mcp = _CP_WHITE_ON_RED
|
| 499 |
+
ma = curses.color_pair(mcp)
|
| 500 |
+
if meta <= 0.25:
|
| 501 |
+
ma |= curses.A_BOLD
|
| 502 |
+
if meta < 0.1:
|
| 503 |
+
ma |= curses.A_BLINK
|
| 504 |
+
|
| 505 |
+
mf, me = _bar(meta)
|
| 506 |
+
mpct = f"{meta * 100:5.1f}%"
|
| 507 |
+
put(r, 0, '\u2551', brd)
|
| 508 |
+
put(r, 1, ' METABOLIC RESERVE ', curses.color_pair(_CP_WHITE) | curses.A_BOLD)
|
| 509 |
+
put(r, 19, '[' + '\u2588' * mf, ma)
|
| 510 |
+
put(r, 21 + mf, '\u2591' * me + ']', dim_a)
|
| 511 |
+
put(r, 21 + BAR_WIDTH, ' ' + mpct, ma)
|
| 512 |
+
mend = 21 + BAR_WIDTH + 2 + 5
|
| 513 |
+
if mend < 1 + W:
|
| 514 |
+
put(r, mend, ' ' * (1 + W - mend))
|
| 515 |
+
put(r, bw - 1, '\u2551', brd)
|
| 516 |
+
r += 1
|
| 517 |
+
|
| 518 |
+
# ββ blank ββ
|
| 519 |
+
put(r, 0, '\u2551' + ' ' * W + '\u2551', brd)
|
| 520 |
+
r += 1
|
| 521 |
+
|
| 522 |
+
# ββ hijack status ββ
|
| 523 |
+
if hijack:
|
| 524 |
+
htxt = f" HIJACK ACTIVE: {reason}"
|
| 525 |
+
ha = curses.color_pair(_CP_RED) | curses.A_BOLD
|
| 526 |
+
elif co > 0.75 or ad > 0.75:
|
| 527 |
+
htxt = " WARNING: APPROACHING METABOLIC CRISIS"
|
| 528 |
+
ha = curses.color_pair(_CP_YELLOW) | curses.A_BOLD
|
| 529 |
+
else:
|
| 530 |
+
htxt = " HIJACK STATUS: NOMINAL"
|
| 531 |
+
ha = curses.color_pair(_CP_GREEN)
|
| 532 |
+
full_row(r, htxt, ha)
|
| 533 |
+
r += 1
|
| 534 |
+
|
| 535 |
+
# ββ power spike ββ
|
| 536 |
+
if spike:
|
| 537 |
+
stxt = " POWER SPIKE: PREDICTED - CORTISOL RISING"
|
| 538 |
+
sa = curses.color_pair(_CP_RED) | curses.A_BOLD
|
| 539 |
+
else:
|
| 540 |
+
stxt = " POWER SPIKE: ---"
|
| 541 |
+
sa = dim_a
|
| 542 |
+
full_row(r, stxt, sa)
|
| 543 |
+
r += 1
|
| 544 |
+
|
| 545 |
+
# ββ system state ββ
|
| 546 |
+
if hijack:
|
| 547 |
+
stxt2 = " SYSTEM: AMYGDALA HIJACK INJECTED"
|
| 548 |
+
sa2 = curses.color_pair(_CP_RED) | curses.A_BOLD
|
| 549 |
+
elif co > 0.75 or ad > 0.75:
|
| 550 |
+
stxt2 = " SYSTEM: ELEVATED STRESS"
|
| 551 |
+
sa2 = curses.color_pair(_CP_YELLOW)
|
| 552 |
+
else:
|
| 553 |
+
stxt2 = " SYSTEM: conscious processing active"
|
| 554 |
+
sa2 = dim_a
|
| 555 |
+
full_row(r, stxt2, sa2)
|
| 556 |
+
r += 1
|
| 557 |
+
|
| 558 |
+
# ββ separator ββ
|
| 559 |
+
put(r, 0, '\u2560' + '\u2550' * W + '\u2563', brd)
|
| 560 |
+
r += 1
|
| 561 |
+
|
| 562 |
+
# ββ counters (two-tone) ββ
|
| 563 |
+
fl = " TOTAL FIRINGS: "
|
| 564 |
+
fv = f"{firings:>6}"
|
| 565 |
+
hl = " TOTAL HIJACKS: "
|
| 566 |
+
hv = f"{hijacks:>4}"
|
| 567 |
+
put(r, 0, '\u2551', brd)
|
| 568 |
+
put(r, 1, fl, curses.color_pair(_CP_CYAN))
|
| 569 |
+
put(r, 1 + len(fl), fv, curses.color_pair(_CP_CYAN))
|
| 570 |
+
c1 = 1 + len(fl) + len(fv)
|
| 571 |
+
put(r, c1, hl, curses.color_pair(_CP_RED))
|
| 572 |
+
put(r, c1 + len(hl), hv, curses.color_pair(_CP_RED))
|
| 573 |
+
c2 = c1 + len(hl) + len(hv)
|
| 574 |
+
if c2 < 1 + W:
|
| 575 |
+
put(r, c2, ' ' * (1 + W - c2))
|
| 576 |
+
put(r, bw - 1, '\u2551', brd)
|
| 577 |
+
r += 1
|
| 578 |
+
|
| 579 |
+
# ββ frame / timestamp ββ
|
| 580 |
+
ts = time.strftime("%H:%M:%S") + f".{int(time.time() * 1000) % 1000:03d}"
|
| 581 |
+
full_row(r, f" FRAME: {self._frame:>8} SAMPLE: {ts}", dim_a)
|
| 582 |
+
r += 1
|
| 583 |
+
|
| 584 |
+
# ββ bottom border ββ
|
| 585 |
+
put(r, 0, '\u255a' + '\u2550' * W + '\u255d', brd)
|
| 586 |
+
|
| 587 |
+
stdscr.refresh()
|
| 588 |
+
|
| 589 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 590 |
+
# THREAD MANAGEMENT
|
| 591 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 592 |
+
|
| 593 |
+
def _thread_entry(self) -> None:
|
| 594 |
+
"""Background thread entry point β wraps the curses loop."""
|
| 595 |
+
import curses
|
| 596 |
+
try:
|
| 597 |
+
curses.wrapper(self._curses_loop)
|
| 598 |
+
except Exception:
|
| 599 |
+
self._running = False
|
| 600 |
+
|
| 601 |
+
def _curses_loop(self, stdscr) -> None:
|
| 602 |
+
"""
|
| 603 |
+
Main curses rendering loop. Samples at ~60 fps.
|
| 604 |
+
|
| 605 |
+
Press **q** or **Esc** to quit (also handled by ``stop()``).
|
| 606 |
+
``curses.wrapper`` guarantees terminal state is restored on exit.
|
| 607 |
+
"""
|
| 608 |
+
import curses
|
| 609 |
+
|
| 610 |
+
curses.curs_set(0) # hide cursor
|
| 611 |
+
stdscr.nodelay(True) # non-blocking getch
|
| 612 |
+
self._init_colors(stdscr)
|
| 613 |
+
|
| 614 |
+
while self._running:
|
| 615 |
+
self.render_curses_frame(stdscr)
|
| 616 |
+
|
| 617 |
+
# allow quit via keyboard
|
| 618 |
+
try:
|
| 619 |
+
key = stdscr.getch()
|
| 620 |
+
if key in (ord('q'), 27): # 'q' or Escape
|
| 621 |
+
self._running = False
|
| 622 |
+
break
|
| 623 |
+
except Exception:
|
| 624 |
+
pass
|
| 625 |
+
|
| 626 |
+
time.sleep(FRAME_MS / 1000.0)
|