Add wrappers for the rest of APIs

This commit is contained in:
Aya Hassan 2026-03-29 23:55:40 +02:00
parent 1a7f9a9d00
commit eb7ab05e8d

View File

@ -6,7 +6,7 @@ from time import sleep
from cffi import FFI from cffi import FFI
from pathlib import Path from pathlib import Path
from result import Result, Ok, Err from result import Result, Ok, Err
import time
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
ffi = FFI() ffi = FFI()
@ -65,6 +65,19 @@ int logosdelivery_send(
void *userData, void *userData,
const char *messageJson const char *messageJson
); );
int logosdelivery_get_available_node_info_ids(
void *ctx,
FFICallBack callback,
void *userData
);
int logosdelivery_get_node_info(
void *ctx,
FFICallBack callback,
void *userData,
const char *nodeInfoId
);
""" """
) )
@ -82,7 +95,7 @@ def _new_cb_state():
} }
def _wait_cb(state, op_name: str, timeout_s: float = 20.0) -> Result[int, str]: def _wait_cb_raw(state, op_name: str, timeout_s: float = 20.0):
finished = state["done"].wait(timeout_s) finished = state["done"].wait(timeout_s)
if not finished: if not finished:
return Err(f"{op_name}: timeout waiting for callback after {timeout_s}s") return Err(f"{op_name}: timeout waiting for callback after {timeout_s}s")
@ -93,6 +106,15 @@ def _wait_cb(state, op_name: str, timeout_s: float = 20.0) -> Result[int, str]:
if ret is None: if ret is None:
return Err(f"{op_name}: callback fired but ret is None") return Err(f"{op_name}: callback fired but ret is None")
return Ok((ret, msg))
def _wait_cb_status(state, op_name: str, timeout_s: float = 20.0) -> Result[int, str]:
wait_result = _wait_cb_raw(state, op_name, timeout_s)
if wait_result.is_err():
return Err(wait_result.err())
ret, msg = wait_result.ok_value
if ret != 0: if ret != 0:
return Err(f"{op_name}: failed (ret={ret}) msg={msg!r}") return Err(f"{op_name}: failed (ret={ret}) msg={msg!r}")
@ -148,7 +170,7 @@ class NodeWrapper:
if ctx == ffi.NULL: if ctx == ffi.NULL:
return Err("create_node: ctx is NULL") return Err("create_node: ctx is NULL")
wait_result = _wait_cb(state, "create_node", timeout_s) wait_result = _wait_cb_status(state, "create_node", timeout_s)
if wait_result.is_err(): if wait_result.is_err():
return Err(wait_result.err()) return Err(wait_result.err())
@ -194,7 +216,7 @@ class NodeWrapper:
if rc != 0: if rc != 0:
return Err(f"start_node: immediate call failed (ret={rc})") return Err(f"start_node: immediate call failed (ret={rc})")
return _wait_cb(state, "start_node", timeout_s) return _wait_cb_status(state, "start_node", timeout_s)
def stop_node(self, *, timeout_s: float = 20.0) -> Result[int, str]: def stop_node(self, *, timeout_s: float = 20.0) -> Result[int, str]:
state = _new_cb_state() state = _new_cb_state()
@ -204,7 +226,7 @@ class NodeWrapper:
if rc != 0: if rc != 0:
return Err(f"stop_node: immediate call failed (ret={rc})") return Err(f"stop_node: immediate call failed (ret={rc})")
return _wait_cb(state, "stop_node", timeout_s) return _wait_cb_status(state, "stop_node", timeout_s)
def destroy(self, *, timeout_s: float = 20.0) -> Result[int, str]: def destroy(self, *, timeout_s: float = 20.0) -> Result[int, str]:
state = _new_cb_state() state = _new_cb_state()
@ -214,7 +236,7 @@ class NodeWrapper:
if rc != 0: if rc != 0:
return Err(f"destroy: immediate call failed (ret={rc})") return Err(f"destroy: immediate call failed (ret={rc})")
return _wait_cb(state, "destroy", timeout_s) return _wait_cb_status(state, "destroy", timeout_s)
def stop_and_destroy(self, *, timeout_s: float = 20.0) -> Result[int, str]: def stop_and_destroy(self, *, timeout_s: float = 20.0) -> Result[int, str]:
stop_result = self.stop_node(timeout_s=timeout_s) stop_result = self.stop_node(timeout_s=timeout_s)
@ -236,7 +258,7 @@ class NodeWrapper:
if rc != 0: if rc != 0:
return Err(f"subscribe_content_topic: immediate call failed (ret={rc})") return Err(f"subscribe_content_topic: immediate call failed (ret={rc})")
return _wait_cb(state, f"subscribe({content_topic})", timeout_s) return _wait_cb_status(state, f"subscribe({content_topic})", timeout_s)
def unsubscribe_content_topic(self, content_topic: str, *, timeout_s: float = 20.0) -> Result[int, str]: def unsubscribe_content_topic(self, content_topic: str, *, timeout_s: float = 20.0) -> Result[int, str]:
state = _new_cb_state() state = _new_cb_state()
@ -251,30 +273,108 @@ class NodeWrapper:
if rc != 0: if rc != 0:
return Err(f"unsubscribe_content_topic: immediate call failed (ret={rc})") return Err(f"unsubscribe_content_topic: immediate call failed (ret={rc})")
return _wait_cb(state, f"unsubscribe({content_topic})", timeout_s) return _wait_cb_status(state, f"unsubscribe({content_topic})", timeout_s)
def send_message(self, message: dict, *, timeout_s: float = 20.0) -> Result[int, str]: def send_message(self, message: dict, *, timeout_s: float = 20.0) -> Result[str, str]:
state = _new_cb_state() state = _new_cb_state()
cb = self._make_waiting_cb(state) cb = self._make_waiting_cb(state)
message_json = json.dumps(message, separators=(",", ":"), ensure_ascii=False) message_json = json.dumps(message, separators=(",", ":"), ensure_ascii=False)
request_id = lib.logosdelivery_send( rc = lib.logosdelivery_send(
self.ctx, self.ctx,
cb, cb,
ffi.NULL, ffi.NULL,
message_json.encode("utf-8"), message_json.encode("utf-8"),
) )
if request_id < 0: if rc < 0:
return Err(f"send_message: immediate call failed (ret={request_id})") return Err(f"send_message: immediate call failed (ret={rc})")
wait_result = _wait_cb(state, "send_message", timeout_s) wait_result = _wait_cb_raw(state, "send_message", timeout_s)
if wait_result.is_err(): if wait_result.is_err():
return Err(wait_result.err()) return Err(wait_result.err())
cb_ret, cb_msg = wait_result.ok_value
if cb_ret != 0:
return Err(f"send_message: failed (ret={cb_ret}) msg={cb_msg!r}")
request_id = cb_msg.decode("utf-8") if cb_msg else ""
return Ok(request_id) return Ok(request_id)
def get_available_node_info_ids(self, *, timeout_s: float = 20.0) -> Result[int, str]:
state = _new_cb_state()
cb = self._make_waiting_cb(state)
node_info_id = lib.logosdelivery_get_available_node_info_ids(
self.ctx,
cb,
ffi.NULL,
)
if node_info_id < 0:
return Err(f"get_available_node_info_ids: immediate call failed (ret={node_info_id})")
wait_result = _wait_cb_raw(state, "get_available_node_info_ids", timeout_s)
if wait_result.is_err():
return Err(wait_result.err())
return Ok(node_info_id)
def get_node_info(self, node_info_id: str, *, timeout_s: float = 20.0) -> Result[dict, str]:
state = _new_cb_state()
cb = self._make_waiting_cb(state)
rc = lib.logosdelivery_get_node_info(
self.ctx,
cb,
ffi.NULL,
node_info_id.encode("utf-8"),
)
if rc < 0:
return Err(f"get_node_info: immediate call failed (ret={rc})")
wait_result = _wait_cb_raw(state, "get_node_info", timeout_s)
if wait_result.is_err():
return Err(wait_result.err())
cb_ret, cb_msg = wait_result.ok_value
if cb_ret != 0:
return Err(f"get_node_info: failed (ret={cb_ret}) msg={cb_msg!r}")
if not cb_msg:
return Err("get_node_info: empty response")
try:
result = json.loads(cb_msg.decode("utf-8"))
except Exception as e:
return Err(f"get_node_info: failed to parse response: {e}")
return Ok(result)
def debug_get_node_info(self, node_info_id: str, *, timeout_s: float = 20.0) -> Result[tuple, str]:
state = _new_cb_state()
cb = self._make_waiting_cb(state)
rc = lib.logosdelivery_get_node_info(
self.ctx,
cb,
ffi.NULL,
node_info_id.encode("utf-8"),
)
print(f"[DEBUG] get_node_info immediate rc={rc}")
wait_result = _wait_cb_raw(state, "get_node_info", timeout_s)
if wait_result.is_err():
return Err(wait_result.err())
cb_ret, cb_msg = wait_result.ok_value
print(f"[DEBUG] get_node_info callback ret={cb_ret}, msg={cb_msg}")
return Ok((rc, cb_ret, cb_msg))
def main(): def main():
config = { config = {
"logLevel": "DEBUG", "logLevel": "DEBUG",
@ -310,11 +410,12 @@ def main():
node = node_result.ok_value node = node_result.ok_value
print(node.subscribe_content_topic(topic)) print(node.subscribe_content_topic(topic))
#print(node.send_message(message)) print(node.send_message(message))
#print(node.get_available_node_info_ids())
print(node.stop_node()) print(node.stop_node())
print(node.destroy()) print(node.destroy())
if __name__ == "__main__": if __name__ == "__main__":
main() main()