diff --git a/README.md b/README.md new file mode 100644 index 0000000..eecc690 --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +# i7lib + +i7 library for scripts \ No newline at end of file diff --git a/src/client.py b/src/client.py new file mode 100644 index 0000000..d353b33 --- /dev/null +++ b/src/client.py @@ -0,0 +1,88 @@ +import os +import time +import math +import requests + +try: + from dotenv import load_dotenv + load_dotenv() +except ImportError: + pass + +SIDECAR_URL = os.getenv("SIDECAR_URL", "http://localhost:80") + +class WriteBatch: + def __init__(self): + self.payload = [] + + def add(self, signal_id, value, name=""): + self.payload.append({ + "id": int(signal_id), + "value": float(value), + "name": str(name) + }) + +class Plant: + def __init__(self): + self.state = {} + self.instants = {} + self.signals = [] + + def _parse(self, data): + self.signals = data + self.state = {} + self.instants = {} + for item in data: + val = item.get("value", math.nan) + instant = item.get("instant", 0) + self.state[item["id"]] = val + self.instants[item["id"]] = instant + if item.get("name"): + self.state[item["name"]] = val + self.instants[item["name"]] = instant + + def read_all(self): + try: + res = requests.get(f"{SIDECAR_URL}/api/read/", timeout=2) + res.raise_for_status() + self._parse(res.json().get("data", [])) + return self.state + except Exception as e: + print(f"Error reading state: {e}") + return {} + + def read_sync(self, interval_ms, offset_ms): + try: + timeout_sec = (interval_ms / 1000.0) + 2.0 + res = requests.get(f"{SIDECAR_URL}/api/read/{interval_ms}/{offset_ms}/", timeout=timeout_sec) + res.raise_for_status() + self._parse(res.json().get("data", [])) + return self.state + except Exception as e: + print(f"Error reading state: {e}") + return {} + + def new_write_batch(self): + return WriteBatch() + + def send(self, batch): + if not batch.payload: + return + try: + res = requests.post(f"{SIDECAR_URL}/api/write/", json=batch.payload, timeout=2) + res.raise_for_status() + except Exception as e: + print(f"Error sending batch: {e}") + +def run(logic_func, interval_ms=1000, offset_ms=200): + print(f"Connected to: {SIDECAR_URL} (Sync: {interval_ms}ms, Offset: {offset_ms}ms)") + plant = Plant() + while True: + try: + if not plant.read_sync(interval_ms, offset_ms): + time.sleep(1.0) + continue + logic_func(plant) + except Exception as e: + print(f"Logic Error: {e}") + time.sleep(1.0) diff --git a/src/dbutils.py b/src/dbutils.py new file mode 100644 index 0000000..ac024b4 --- /dev/null +++ b/src/dbutils.py @@ -0,0 +1,79 @@ +import os +import requests + +try: + from dotenv import load_dotenv + load_dotenv() +except ImportError: + pass + +DB_URL = os.getenv("DB_CONTROLLER_URL", "http://127.0.0.1:13213") + +class Database: + def __init__(self): + self.base_url = f"{DB_URL}/api" + + def get_row(self, table, row_id): + """GET /api/db/:table/:id/""" + try: + res = requests.get(f"{self.base_url}/db/{table}/{row_id}/") + res.raise_for_status() + return res.json().get("data") + except Exception as e: + print(f"DB Read Error: {e}") + return None + + def insert(self, table, data): + """POST /api/db/:table/""" + try: + res = requests.post(f"{self.base_url}/db/{table}/", json=data) + res.raise_for_status() + return res.json().get("data") # Returns {'id': lastID} + except Exception as e: + print(f"DB Insert Error: {e}") + return None + + def query(self, table, filters=None, order_by=None, limit=100): + """POST /api/db-query/:table/""" + payload = { + "filters": filters or [], + "orderBy": order_by or [], + "limit": limit + } + try: + res = requests.post(f"{self.base_url}/db-query/{table}/", json=payload) + res.raise_for_status() + return res.json().get("data") + except Exception as e: + print(f"DB Query Error: {e}") + return [] + + def update(self, table, row_id, data): + """PUT /api/db/:table/:id/""" + try: + res = requests.put(f"{self.base_url}/db/{table}/{row_id}/", json=data) + res.raise_for_status() + return True + except Exception as e: + print(f"DB Update Error: {e}") + return False + + def list_tables(self): + """GET /api/db-meta/tables""" + try: + res = requests.get(f"{self.base_url}/db-meta/tables") + res.raise_for_status() + return res.json().get("data") + except Exception as e: + print(f"DB List Tables Error: {e}") + return [] + + def list_columns(self, table): + """GET /api/db-meta/tables/:table/columns""" + try: + res = requests.get(f"{self.base_url}/db-meta/tables/{table}/columns") + res.raise_for_status() + return res.json().get("data") + except Exception as e: + print(f"DB List Columns Error: {e}") + return [] diff --git a/src/timeutils.py b/src/timeutils.py new file mode 100644 index 0000000..59c8259 --- /dev/null +++ b/src/timeutils.py @@ -0,0 +1,14 @@ +import time + +def now(): + """Returns current time in Unix Milliseconds (standard JS/App format)""" + return int(time.time() * 1000) + +def now_sec(): + """Returns current time in Unix Milliseconds, but floored to the second""" + return (int(time.time()) * 1000) + +# If you need to convert a JS-style milli back to a Python datetime for local logging +def to_datetime(unix_milli): + from datetime import datetime + return datetime.fromtimestamp(unix_milli / 1000.0)