tangram-system 0.2.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,83 @@
1
+ <template>
2
+ <div class="system-widget">
3
+ <div
4
+ class="clock"
5
+ @mouseover="state.hovered = true"
6
+ @mouseleave="state.hovered = false"
7
+ >
8
+ <span id="info_time">{{ state.hovered ? local_time : utc_time }}</span>
9
+ </div>
10
+ <span id="uptime">{{ state.uptime }}</span>
11
+ </div>
12
+ </template>
13
+
14
+ <script setup lang="ts">
15
+ import { reactive, computed, inject, onMounted, onUnmounted } from "vue";
16
+ import type { TangramApi, Disposable } from "@open-aviation/tangram-core/api";
17
+
18
+ const tangramApi = inject<TangramApi>("tangramApi");
19
+ if (!tangramApi) {
20
+ throw new Error("assert: tangram api not provided");
21
+ }
22
+
23
+ const state = reactive({
24
+ hovered: false,
25
+ uptime: "",
26
+ info_utc: new Date().getTime()
27
+ });
28
+
29
+ let subscription: Disposable | null = null;
30
+
31
+ onMounted(async () => {
32
+ try {
33
+ subscription = await tangramApi.realtime.subscribe(
34
+ "system:update-node",
35
+ (payload: { el: string; value: string | number }) => {
36
+ if (payload.el === "uptime") state.uptime = payload.value as string;
37
+ if (payload.el === "info_utc") state.info_utc = payload.value as number;
38
+ }
39
+ );
40
+ } catch (e) {
41
+ console.error("failed to subscribe to system:update-node", e);
42
+ }
43
+ });
44
+
45
+ onUnmounted(() => {
46
+ subscription?.dispose();
47
+ });
48
+
49
+ const utc_time = computed(() => {
50
+ const date = new Date(state.info_utc);
51
+ const hours = date.getUTCHours().toString().padStart(2, "0");
52
+ const minutes = date.getUTCMinutes().toString().padStart(2, "0");
53
+ const seconds = date.getUTCSeconds().toString().padStart(2, "0");
54
+ return `${hours}:${minutes}:${seconds} Z`;
55
+ });
56
+
57
+ const local_time = computed(() => {
58
+ const date = new Date(state.info_utc);
59
+ return date.toLocaleTimeString([], {
60
+ hour: "2-digit",
61
+ minute: "2-digit",
62
+ second: "2-digit",
63
+ hour12: false,
64
+ timeZoneName: "shortOffset"
65
+ });
66
+ });
67
+ </script>
68
+
69
+ <style scoped>
70
+ #uptime {
71
+ color: #79706e;
72
+ font-size: 9pt;
73
+ text-align: center;
74
+ }
75
+
76
+ .system-widget {
77
+ display: flex;
78
+ flex-direction: column;
79
+ align-items: center;
80
+ padding: 1px;
81
+ border-bottom: 1px solid #ddd;
82
+ }
83
+ </style>
@@ -0,0 +1,81 @@
1
+ import asyncio
2
+ import logging
3
+ from dataclasses import dataclass
4
+ from datetime import datetime, timedelta, timezone
5
+ from typing import Any, NoReturn
6
+
7
+ import orjson
8
+ import psutil
9
+ import redis.asyncio as redis
10
+ import tangram_core
11
+
12
+ log = logging.getLogger(__name__)
13
+
14
+
15
+ @dataclass
16
+ class SystemConfig(tangram_core.config.HasSidebarUiConfig):
17
+ topbar_order: int = 0
18
+
19
+
20
+ def transform_config(config_dict: dict[str, Any]) -> SystemConfig:
21
+ from pydantic import TypeAdapter
22
+
23
+ return TypeAdapter(SystemConfig).validate_python(config_dict)
24
+
25
+
26
+ def uptime(counter: int) -> dict[str, str]:
27
+ return {
28
+ "el": "uptime",
29
+ "value": f"{timedelta(seconds=counter)}",
30
+ }
31
+
32
+
33
+ def info_utc() -> dict[str, str | int]:
34
+ return {
35
+ "el": "info_utc",
36
+ "value": 1000 * int(datetime.now(timezone.utc).timestamp()),
37
+ }
38
+
39
+
40
+ def cpu_load() -> dict[str, str]:
41
+ try:
42
+ load1, _load5, _load15 = psutil.getloadavg()
43
+ cpu_count = psutil.cpu_count(logical=True) or 1
44
+ load_percent = (load1 / cpu_count) * 100
45
+ return {"el": "cpu_load", "value": f"{load_percent:.2f}%"}
46
+ except Exception:
47
+ return {"el": "cpu_load", "value": "Unavailable"}
48
+
49
+
50
+ def ram_usage() -> dict[str, str]:
51
+ try:
52
+ mem = psutil.virtual_memory()
53
+ return {"el": "ram_usage", "value": f"{mem.percent:.2f}%"}
54
+ except Exception:
55
+ return {"el": "ram_usage", "value": "Unavailable"}
56
+
57
+
58
+ async def server_events(redis_client: redis.Redis) -> NoReturn:
59
+ counter = 0
60
+ log.info("serving system events...")
61
+
62
+ while True:
63
+ await redis_client.publish(
64
+ "to:system:update-node", orjson.dumps(uptime(counter))
65
+ )
66
+ await redis_client.publish("to:system:update-node", orjson.dumps(info_utc()))
67
+ await redis_client.publish("to:system:update-node", orjson.dumps(cpu_load()))
68
+ await redis_client.publish("to:system:update-node", orjson.dumps(ram_usage()))
69
+ counter += 1
70
+
71
+ await asyncio.sleep(1)
72
+
73
+
74
+ plugin = tangram_core.Plugin(
75
+ frontend_path="dist-frontend", into_frontend_config_function=transform_config
76
+ )
77
+
78
+
79
+ @plugin.register_service()
80
+ async def run_system(backend_state: tangram_core.BackendState) -> None:
81
+ await server_events(backend_state.redis_client)
@@ -0,0 +1 @@
1
+ #uptime[data-v-6e31ee8b]{color:#79706e;font-size:9pt;text-align:center}.system-widget[data-v-6e31ee8b]{display:flex;flex-direction:column;align-items:center;padding:1px;border-bottom:1px solid #ddd}
@@ -0,0 +1,63 @@
1
+ import { defineComponent as l, inject as _, reactive as g, onMounted as f, onUnmounted as v, computed as u, createElementBlock as S, openBlock as h, createElementVNode as c, toDisplayString as d } from "vue";
2
+ const w = { class: "system-widget" }, y = { id: "info_time" }, b = { id: "uptime" }, T = /* @__PURE__ */ l({
3
+ __name: "SystemWidget",
4
+ setup(s) {
5
+ const o = _("tangramApi");
6
+ if (!o)
7
+ throw new Error("assert: tangram api not provided");
8
+ const e = g({
9
+ hovered: !1,
10
+ uptime: "",
11
+ info_utc: (/* @__PURE__ */ new Date()).getTime()
12
+ });
13
+ let i = null;
14
+ f(async () => {
15
+ try {
16
+ i = await o.realtime.subscribe(
17
+ "system:update-node",
18
+ (t) => {
19
+ t.el === "uptime" && (e.uptime = t.value), t.el === "info_utc" && (e.info_utc = t.value);
20
+ }
21
+ );
22
+ } catch (t) {
23
+ console.error("failed to subscribe to system:update-node", t);
24
+ }
25
+ }), v(() => {
26
+ i?.dispose();
27
+ });
28
+ const r = u(() => {
29
+ const t = new Date(e.info_utc), n = t.getUTCHours().toString().padStart(2, "0"), a = t.getUTCMinutes().toString().padStart(2, "0"), p = t.getUTCSeconds().toString().padStart(2, "0");
30
+ return `${n}:${a}:${p} Z`;
31
+ }), m = u(() => new Date(e.info_utc).toLocaleTimeString([], {
32
+ hour: "2-digit",
33
+ minute: "2-digit",
34
+ second: "2-digit",
35
+ hour12: !1,
36
+ timeZoneName: "shortOffset"
37
+ }));
38
+ return (t, n) => (h(), S("div", w, [
39
+ c("div", {
40
+ class: "clock",
41
+ onMouseover: n[0] || (n[0] = (a) => e.hovered = !0),
42
+ onMouseleave: n[1] || (n[1] = (a) => e.hovered = !1)
43
+ }, [
44
+ c("span", y, d(e.hovered ? m.value : r.value), 1)
45
+ ], 32),
46
+ c("span", b, d(e.uptime), 1)
47
+ ]));
48
+ }
49
+ }), $ = (s, o) => {
50
+ const e = s.__vccOpts || s;
51
+ for (const [i, r] of o)
52
+ e[i] = r;
53
+ return e;
54
+ }, k = /* @__PURE__ */ $(T, [["__scopeId", "data-v-6e31ee8b"]]);
55
+ function D(s, o) {
56
+ s.ui.registerWidget("system-widget", "TopBar", k, {
57
+ priority: o?.topbar_order
58
+ });
59
+ }
60
+ export {
61
+ D as install
62
+ };
63
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sources":["../src/tangram_system/SystemWidget.vue","../src/tangram_system/index.ts"],"sourcesContent":["<template>\n <div class=\"system-widget\">\n <div\n class=\"clock\"\n @mouseover=\"state.hovered = true\"\n @mouseleave=\"state.hovered = false\"\n >\n <span id=\"info_time\">{{ state.hovered ? local_time : utc_time }}</span>\n </div>\n <span id=\"uptime\">{{ state.uptime }}</span>\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { reactive, computed, inject, onMounted, onUnmounted } from \"vue\";\nimport type { TangramApi, Disposable } from \"@open-aviation/tangram-core/api\";\n\nconst tangramApi = inject<TangramApi>(\"tangramApi\");\nif (!tangramApi) {\n throw new Error(\"assert: tangram api not provided\");\n}\n\nconst state = reactive({\n hovered: false,\n uptime: \"\",\n info_utc: new Date().getTime()\n});\n\nlet subscription: Disposable | null = null;\n\nonMounted(async () => {\n try {\n subscription = await tangramApi.realtime.subscribe(\n \"system:update-node\",\n (payload: { el: string; value: string | number }) => {\n if (payload.el === \"uptime\") state.uptime = payload.value as string;\n if (payload.el === \"info_utc\") state.info_utc = payload.value as number;\n }\n );\n } catch (e) {\n console.error(\"failed to subscribe to system:update-node\", e);\n }\n});\n\nonUnmounted(() => {\n subscription?.dispose();\n});\n\nconst utc_time = computed(() => {\n const date = new Date(state.info_utc);\n const hours = date.getUTCHours().toString().padStart(2, \"0\");\n const minutes = date.getUTCMinutes().toString().padStart(2, \"0\");\n const seconds = date.getUTCSeconds().toString().padStart(2, \"0\");\n return `${hours}:${minutes}:${seconds} Z`;\n});\n\nconst local_time = computed(() => {\n const date = new Date(state.info_utc);\n return date.toLocaleTimeString([], {\n hour: \"2-digit\",\n minute: \"2-digit\",\n second: \"2-digit\",\n hour12: false,\n timeZoneName: \"shortOffset\"\n });\n});\n</script>\n\n<style scoped>\n#uptime {\n color: #79706e;\n font-size: 9pt;\n text-align: center;\n}\n\n.system-widget {\n display: flex;\n flex-direction: column;\n align-items: center;\n padding: 1px;\n border-bottom: 1px solid #ddd;\n}\n</style>\n","import type { TangramApi } from \"@open-aviation/tangram-core/api\";\nimport SystemWidget from \"./SystemWidget.vue\";\n\ninterface SystemConfig {\n topbar_order: number;\n}\n\nexport function install(api: TangramApi, config?: SystemConfig) {\n api.ui.registerWidget(\"system-widget\", \"TopBar\", SystemWidget, {\n priority: config?.topbar_order\n });\n}\n"],"names":["tangramApi","inject","state","reactive","subscription","onMounted","payload","e","onUnmounted","utc_time","computed","date","hours","minutes","seconds","local_time","_openBlock","_createElementBlock","_hoisted_1","_createElementVNode","_cache","$event","_hoisted_2","_toDisplayString","_hoisted_3","install","api","config","SystemWidget"],"mappings":";;;;AAiBA,UAAMA,IAAaC,EAAmB,YAAY;AAClD,QAAI,CAACD;AACH,YAAM,IAAI,MAAM,kCAAkC;AAGpD,UAAME,IAAQC,EAAS;AAAA,MACrB,SAAS;AAAA,MACT,QAAQ;AAAA,MACR,WAAU,oBAAI,KAAA,GAAO,QAAA;AAAA,IAAQ,CAC9B;AAED,QAAIC,IAAkC;AAEtC,IAAAC,EAAU,YAAY;AACpB,UAAI;AACF,QAAAD,IAAe,MAAMJ,EAAW,SAAS;AAAA,UACvC;AAAA,UACA,CAACM,MAAoD;AACnD,YAAIA,EAAQ,OAAO,aAAUJ,EAAM,SAASI,EAAQ,QAChDA,EAAQ,OAAO,eAAYJ,EAAM,WAAWI,EAAQ;AAAA,UAC1D;AAAA,QAAA;AAAA,MAEJ,SAASC,GAAG;AACV,gBAAQ,MAAM,6CAA6CA,CAAC;AAAA,MAC9D;AAAA,IACF,CAAC,GAEDC,EAAY,MAAM;AAChB,MAAAJ,GAAc,QAAA;AAAA,IAChB,CAAC;AAED,UAAMK,IAAWC,EAAS,MAAM;AAC9B,YAAMC,IAAO,IAAI,KAAKT,EAAM,QAAQ,GAC9BU,IAAQD,EAAK,YAAA,EAAc,WAAW,SAAS,GAAG,GAAG,GACrDE,IAAUF,EAAK,cAAA,EAAgB,WAAW,SAAS,GAAG,GAAG,GACzDG,IAAUH,EAAK,cAAA,EAAgB,WAAW,SAAS,GAAG,GAAG;AAC/D,aAAO,GAAGC,CAAK,IAAIC,CAAO,IAAIC,CAAO;AAAA,IACvC,CAAC,GAEKC,IAAaL,EAAS,MACb,IAAI,KAAKR,EAAM,QAAQ,EACxB,mBAAmB,IAAI;AAAA,MACjC,MAAM;AAAA,MACN,QAAQ;AAAA,MACR,QAAQ;AAAA,MACR,QAAQ;AAAA,MACR,cAAc;AAAA,IAAA,CACf,CACF;sBAhECc,EAAA,GAAAC,EASM,OATNC,GASM;AAAA,MARJC,EAMM,OAAA;AAAA,QALJ,OAAM;AAAA,QACL,aAASC,EAAA,CAAA,MAAAA,EAAA,CAAA,IAAA,CAAAC,MAAEnB,EAAM,UAAO;AAAA,QACxB,cAAUkB,EAAA,CAAA,MAAAA,EAAA,CAAA,IAAA,CAAAC,MAAEnB,EAAM,UAAO;AAAA,MAAA;QAE1BiB,EAAuE,QAAvEG,GAAuEC,EAA/CrB,EAAM,UAAUa,EAAA,QAAaN,EAAA,KAAQ,GAAA,CAAA;AAAA,MAAA;MAE/DU,EAA2C,QAA3CK,GAA2CD,EAAtBrB,EAAM,MAAM,GAAA,CAAA;AAAA,IAAA;;;;;;;;ACF9B,SAASuB,EAAQC,GAAiBC,GAAuB;AAC9D,EAAAD,EAAI,GAAG,eAAe,iBAAiB,UAAUE,GAAc;AAAA,IAC7D,UAAUD,GAAQ;AAAA,EAAA,CACnB;AACH;"}
@@ -0,0 +1,5 @@
1
+ {
2
+ "name": "@open-aviation/tangram-system",
3
+ "main": "index.js",
4
+ "style": "index.css"
5
+ }
@@ -0,0 +1,12 @@
1
+ import type { TangramApi } from "@open-aviation/tangram-core/api";
2
+ import SystemWidget from "./SystemWidget.vue";
3
+
4
+ interface SystemConfig {
5
+ topbar_order: number;
6
+ }
7
+
8
+ export function install(api: TangramApi, config?: SystemConfig) {
9
+ api.ui.registerWidget("system-widget", "TopBar", SystemWidget, {
10
+ priority: config?.topbar_order
11
+ });
12
+ }
@@ -0,0 +1,16 @@
1
+ {
2
+ "name": "@open-aviation/tangram-system",
3
+ "version": "0.2.1",
4
+ "private": true,
5
+ "type": "module",
6
+ "main": "src/tangram_system/index.ts",
7
+ "scripts": {
8
+ "build": "vite build"
9
+ },
10
+ "dependencies": {
11
+ "@open-aviation/tangram-core": "workspace:*"
12
+ },
13
+ "devDependencies": {
14
+ "vue": "^3.5.25"
15
+ }
16
+ }
@@ -0,0 +1,42 @@
1
+ Metadata-Version: 2.4
2
+ Name: tangram_system
3
+ Version: 0.2.1
4
+ Summary: System monitoring plugin for tangram
5
+ Author-email: Xavier Olive <git@xoolive.org>, Junzi Sun <git@junzis.com>
6
+ License-Expression: AGPL-3.0
7
+ Classifier: Development Status :: 4 - Beta
8
+ Classifier: Intended Audience :: Science/Research
9
+ Classifier: License :: OSI Approved :: GNU Affero General Public License v3
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: Programming Language :: Python :: 3.10
12
+ Classifier: Programming Language :: Python :: 3.11
13
+ Classifier: Programming Language :: Python :: 3.12
14
+ Classifier: Programming Language :: Python :: 3.13
15
+ Classifier: Programming Language :: Python :: 3.14
16
+ Classifier: Topic :: System :: Monitoring
17
+ Requires-Python: >=3.10
18
+ Requires-Dist: psutil
19
+ Requires-Dist: tangram-core>=0.2.0
20
+ Description-Content-Type: text/markdown
21
+
22
+ # tangram_system
23
+
24
+ The `tangram_system` plugin provides system monitoring capabilities for the tangram framework.
25
+
26
+ It includes a background service that broadcasts server metrics (CPU, RAM, Uptime) to connected frontend clients via the realtime channel.
27
+
28
+ ## About Tangram
29
+
30
+ `tangram_system` is a plugin for `tangram`, an open framework for modular, real-time air traffic management research.
31
+
32
+ - Documentation: <https://mode-s.org/tangram/>
33
+ - Repository: <https://github.com/open-aviation/tangram>
34
+
35
+ Installation:
36
+
37
+ ```sh
38
+ # cli via uv
39
+ uv tool install --with tangram-system tangram-core
40
+ # with pip
41
+ pip install tangram-core tangram-system
42
+ ```
@@ -0,0 +1,12 @@
1
+ tangram_system/SystemWidget.vue,sha256=A1eZIibTmmrCyPdmqtcJi60dArSg4qYw6Mn76Iw2rgE,2069
2
+ tangram_system/__init__.py,sha256=CJqn9VUAirPI8jOXCI1Lk3GR9FIgNhGF6if5yfsR3Wk,2245
3
+ tangram_system/index.ts,sha256=COqiA9ro0jHvdtigahKbZ75SoApJtbV24vaakeNVXKI,343
4
+ tangram_system/dist-frontend/index.css,sha256=Ck5orxty4qPRwI417S1V8NU7Xw606jMQZqdSf0bqgmk,199
5
+ tangram_system/dist-frontend/index.js,sha256=4d3jbhcLv9pwDophIF8UvD6Z8Si3wtTcV5HIh_sEKKk,2108
6
+ tangram_system/dist-frontend/index.js.map,sha256=8HSWZ_rCKEyo8HsqiPCqbGbCyAxvqDTTGjXOmp3R0nw,4529
7
+ tangram_system/dist-frontend/plugin.json,sha256=f9ITIfSjnW0OhcoQa6pp3HgxSLxDszLCLiZ6H2coz9A,91
8
+ tangram_system/package.json,sha256=zT1MUNUIYT1SqAtG8Kq70Gb9To8xT2kHIV4EwoB2MnM,316
9
+ tangram_system-0.2.1.dist-info/METADATA,sha256=m3IlhvIETh0ns9gBzD8VUWCc7AUjQh-tg6IDdJ0Jz1I,1478
10
+ tangram_system-0.2.1.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
11
+ tangram_system-0.2.1.dist-info/entry_points.txt,sha256=lC7Uu8HPLHLg2ULa0lh2SWTN2qt_LrPFUrcqVL4heZk,62
12
+ tangram_system-0.2.1.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.28.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,2 @@
1
+ [tangram_core.plugins]
2
+ tangram_system = tangram_system:plugin