api.py 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637
  1. import zmq
  2. from collections import namedtuple
  3. from . import serial
  4. from . import exc
  5. Property = namedtuple("Property", [
  6. "name",
  7. "type",
  8. "subtype",
  9. "defaults",
  10. "ui_name",
  11. "desc",
  12. "is_null_allowed",
  13. "array_len",
  14. "min_val",
  15. "max_val",
  16. "enum_items"
  17. ])
  18. class Command:
  19. HELLO = 0
  20. ADD_NODE = 1
  21. REMOVE_NODE = 9
  22. RENAME_NODE = 23
  23. SCAN_DANGLING = 24
  24. LOOKUP_NODE_ID = 12
  25. ADD_PROPERTY = 11
  26. LINK_NODE = 2
  27. UNLINK_NODE = 8
  28. GET_INFO = 19
  29. GET_CHILDREN = 4
  30. GET_PARENTS = 5
  31. GET_PROPERTIES = 3
  32. GET_PROPERTY_VALUE = 6
  33. SET_PROPERTY_VALUE = 7
  34. GET_SIGNALS = 14
  35. REGISTER_SLOT = 15
  36. UNREGISTER_SLOT = 16
  37. LOOKUP_SLOT_ID = 17
  38. GET_SLOTS = 18
  39. GET_METHODS = 20
  40. GET_METHOD = 21
  41. CALL_METHOD = 22
  42. class SceneNodeType:
  43. NULL = 0
  44. ROOT = 1
  45. WINDOW = 2
  46. WINDOW_INPUT = 6
  47. KEYBOARD = 7
  48. MOUSE = 8
  49. RENDER_LAYER = 3
  50. RENDER_OBJECT = 4
  51. RENDER_MESH = 5
  52. RENDER_TEXT = 9
  53. RENDER_TEXTURE = 13
  54. FONTS = 10
  55. FONT = 11
  56. LINE_POSITION = 12
  57. class PropertyType:
  58. NULL = 0
  59. BOOL = 1
  60. UINT32 = 2
  61. FLOAT32 = 3
  62. STR = 4
  63. ENUM = 5
  64. BUFFER = 6
  65. SCENE_NODE_ID = 7
  66. @staticmethod
  67. def to_str(prop_type):
  68. match prop_type:
  69. case PropertyType.NULL:
  70. return "null"
  71. case PropertyType.BOOL:
  72. return "bool"
  73. case PropertyType.UINT32:
  74. return "uint32"
  75. case PropertyType.FLOAT32:
  76. return "float32"
  77. case PropertyType.STR:
  78. return "str"
  79. case PropertyType.ENUM:
  80. return "enum"
  81. case PropertyType.BUFFER:
  82. return "buffer"
  83. case PropertyType.SCENE_NODE_ID:
  84. return "scene_node_id"
  85. class PropertySubType:
  86. NULL = 0
  87. COLOR = 1
  88. PIXEL = 2
  89. RESOURCE_ID = 3
  90. @staticmethod
  91. def to_str(prop_type):
  92. match prop_type:
  93. case PropertySubType.NULL:
  94. return "null"
  95. case PropertySubType.Color:
  96. return "color"
  97. case PropertySubType.PIXEL:
  98. return "pixel"
  99. case PropertySubType.RESOURCE_ID:
  100. return "resource_id"
  101. class PropertyStatus:
  102. OK = 0
  103. UNSET = 1
  104. NULL = 2
  105. class ErrorCode:
  106. INVALID_SCENE_PATH = 1
  107. NODE_NOT_FOUND = 2
  108. CHILD_NODE_NOT_FOUND = 3
  109. PARENT_NODE_NOT_FOUND = 4
  110. PROPERTY_ALREADY_EXISTS = 5
  111. PROPERTY_NOT_FOUND = 6
  112. PROPERTY_WRONG_TYPE = 7
  113. PROPERTY_WRONG_LEN = 8
  114. PROPERTY_WRONG_INDEX = 9
  115. PROPERTY_OUT_OF_RANGE = 10
  116. PROPERTY_NULL_NOT_ALLOWED = 11
  117. PROPERTY_IS_BOUNDED = 12
  118. PROPERTY_WRONG_ENUM_ITEM = 13
  119. SIGNAL_ALREADY_EXISTS = 14
  120. SIGNAL_NOT_FOUND = 15
  121. SLOT_NOT_FOUND = 16
  122. METHOD_ALREADY_EXISTS = 17
  123. METHOD_NOT_FOUND = 18
  124. NODES_ARE_LINKED = 19
  125. NODES_NOT_LINKED = 20
  126. NODE_HAS_PARENTS = 21
  127. NODE_HAS_CHILDREN = 22
  128. NODE_PARENT_NAME_CONFLICT = 23
  129. NODE_CHILD_NAME_CONFLICT = 24
  130. NODE_SIBLING_NAME_CONFLICT = 25
  131. FILE_NOT_FOUND = 26
  132. @staticmethod
  133. def to_str(errc):
  134. match errc:
  135. case ErrorCode.INVALID_SCENE_PATH:
  136. return "invalid_scene_path"
  137. case ErrorCode.NODE_NOT_FOUND:
  138. return "node_not_found"
  139. case ErrorCode.CHILD_NODE_NOT_FOUND:
  140. return "child_node_not_found"
  141. case ErrorCode.PARENT_NODE_NOT_FOUND:
  142. return "parent_node_not_found"
  143. case ErrorCode.PROPERTY_ALREADY_EXISTS:
  144. return "property_already_exists"
  145. case ErrorCode.PROPERTY_NOT_FOUND:
  146. return "property_not_found"
  147. case ErrorCode.PROPERTY_WRONG_TYPE:
  148. return "property_wrong_type"
  149. case ErrorCode.PROPERTY_WRONG_LEN:
  150. return "property_wrong_len"
  151. case ErrorCode.PROPERTY_WRONG_INDEX:
  152. return "property_wrong_index"
  153. case ErrorCode.PROPERTY_OUT_OF_RANGE:
  154. return "property_out_of_range"
  155. case ErrorCode.PROPERTY_NULL_NOT_ALLOWED:
  156. return "property_null_not_allowed"
  157. case ErrorCode.PROPERTY_IS_BOUNDED:
  158. return "property_is_bounded"
  159. case ErrorCode.PROPERTY_WRONG_ENUM_ITEM:
  160. return "property_wrong_enum_item"
  161. case ErrorCode.SIGNAL_ALREADY_EXISTS:
  162. return "signal_already_exists"
  163. case ErrorCode.SIGNAL_NOT_FOUND:
  164. return "signal_not_found"
  165. case ErrorCode.SLOT_NOT_FOUND:
  166. return "slot_not_found"
  167. case ErrorCode.METHOD_ALREADY_EXISTS:
  168. return "method_already_exists"
  169. case ErrorCode.METHOD_NOT_FOUND:
  170. return "method_not_found"
  171. case ErrorCode.NODES_ARE_LINKED:
  172. return "nodes_are_linked"
  173. case ErrorCode.NODES_NOT_LINKED:
  174. return "nodes_not_linked"
  175. case ErrorCode.NODE_HAS_PARENTS:
  176. return "node_has_parents"
  177. case ErrorCode.NODE_HAS_CHILDREN:
  178. return "node_has_children"
  179. case ErrorCode.NODE_PARENT_NAME_CONFLICT:
  180. return "node_parent_name_conflict"
  181. case ErrorCode.NODE_CHILD_NAME_CONFLICT:
  182. return "node_child_name_conflict"
  183. case ErrorCode.NODE_SIBLING_NAME_CONFLICT:
  184. return "node_sibling_name_conflict"
  185. case ErrorCode.FILE_NOT_FOUND:
  186. return "file_not_found"
  187. def vertex(x, y, r, g, b, a, u, v):
  188. buf = bytearray()
  189. serial.write_f32(buf, x)
  190. serial.write_f32(buf, y)
  191. serial.write_f32(buf, r)
  192. serial.write_f32(buf, g)
  193. serial.write_f32(buf, b)
  194. serial.write_f32(buf, a)
  195. serial.write_f32(buf, u)
  196. serial.write_f32(buf, v)
  197. return buf
  198. def face(idx1, idx2, idx3):
  199. buf = bytearray()
  200. serial.write_u32(buf, idx1)
  201. serial.write_u32(buf, idx2)
  202. serial.write_u32(buf, idx3)
  203. return buf
  204. class Api:
  205. def __init__(self, addr="[::1]", port=9484):
  206. context = zmq.Context()
  207. self.socket = context.socket(zmq.REQ)
  208. self.socket.setsockopt(zmq.IPV6, True)
  209. self.socket.connect(f"tcp://{addr}:{port}")
  210. def _make_request(self, cmd, payload):
  211. req_cmd = bytearray()
  212. serial.write_u8(req_cmd, cmd)
  213. self.socket.send_multipart([req_cmd, payload])
  214. errc, reply = self.socket.recv_multipart()
  215. errc = int.from_bytes(errc, "little")
  216. cursor = serial.Cursor(reply)
  217. match errc:
  218. case 1:
  219. raise exc.InvalidScenePath
  220. case 2:
  221. raise exc.NodeNotFound
  222. case 3:
  223. raise exc.ChildNodeNotFound
  224. case 4:
  225. raise exc.ParentNodeNotFound
  226. case 5:
  227. raise exc.PropertyAlreadyExists
  228. case 6:
  229. raise exc.PropertyNotFound
  230. case 7:
  231. raise exc.PropertyWrongType
  232. case 8:
  233. raise exc.PropertyWrongLen
  234. case 9:
  235. raise exc.PropertyWrongIndex
  236. case 10:
  237. raise exc.PropertyOutOfRange
  238. case 11:
  239. raise exc.PropertyNullNotAllowed
  240. case 12:
  241. raise exc.PropertyIsBounded
  242. case 13:
  243. raise exc.PropertyWrongEnumItem
  244. case 14:
  245. raise exc.SignalAlreadyExists
  246. case 15:
  247. raise exc.SignalNotFound
  248. case 16:
  249. raise exc.SlotNotFound
  250. case 17:
  251. raise exc.MethodAlreadyExists
  252. case 18:
  253. raise exc.MethodNotFound
  254. case 19:
  255. raise exc.NodesAreLinked
  256. case 20:
  257. raise exc.NodesNotLinked
  258. case 21:
  259. raise exc.NodeHasParents
  260. case 22:
  261. raise exc.NodeHasChildren
  262. case 23:
  263. raise exc.NodeParentNameConflict
  264. case 24:
  265. raise exc.NodeChildNameConflict
  266. case 25:
  267. raise exc.NodeSiblingNameConflict
  268. case 26:
  269. raise exc.FileNotFound
  270. return cursor
  271. def hello(self):
  272. response = self._make_request(Command.HELLO, bytearray())
  273. return serial.decode_str(response)
  274. def get_info(self, node_id):
  275. req = bytearray()
  276. serial.write_u32(req, node_id)
  277. cur = self._make_request(Command.GET_INFO, req)
  278. name = serial.decode_str(cur)
  279. type = serial.read_u8(cur)
  280. return (name, type)
  281. def get_children(self, node_id):
  282. req = bytearray()
  283. serial.write_u32(req, node_id)
  284. cur = self._make_request(Command.GET_CHILDREN, req)
  285. children_len = serial.decode_varint(cur)
  286. children = []
  287. for _ in range(children_len):
  288. child_name = serial.decode_str(cur)
  289. child_id = serial.read_u32(cur)
  290. child_type = serial.read_u8(cur)
  291. children.append((child_name, child_id, child_type))
  292. return children
  293. def get_parents(self, node_id):
  294. req = bytearray()
  295. serial.write_u32(req, node_id)
  296. cur = self._make_request(Command.GET_PARENTS, req)
  297. parents_len = serial.decode_varint(cur)
  298. parents = []
  299. for _ in range(parents_len):
  300. parent_name = serial.decode_str(cur)
  301. parent_id = serial.read_u32(cur)
  302. parent_type = serial.read_u8(cur)
  303. parents.append((parent_name, parent_id, parent_type))
  304. return parents
  305. def get_properties(self, node_id):
  306. req = bytearray()
  307. serial.write_u32(req, node_id)
  308. cur = self._make_request(Command.GET_PROPERTIES, req)
  309. props_len = serial.decode_varint(cur)
  310. props = []
  311. prop_read_fn = lambda cur: Api.read_prop_val(cur, prop_type)
  312. enum_read_fn = lambda cur: serial.decode_arr(cur, serial.decode_str)
  313. for _ in range(props_len):
  314. prop_name = serial.decode_str(cur)
  315. # We need prop_type below
  316. prop_type = serial.read_u8(cur)
  317. prop = Property(
  318. prop_name,
  319. prop_type,
  320. # subtype
  321. serial.read_u8(cur),
  322. # defaults
  323. serial.decode_arr(cur, prop_read_fn),
  324. # ui_name
  325. serial.decode_str(cur),
  326. # desc
  327. serial.decode_str(cur),
  328. # is_null_allowed
  329. bool(serial.read_u8(cur)),
  330. # array_len
  331. serial.read_u32(cur),
  332. # min_val
  333. serial.decode_opt(cur, prop_read_fn),
  334. # max_val
  335. serial.decode_opt(cur, prop_read_fn),
  336. # enum_items
  337. serial.decode_opt(cur, enum_read_fn)
  338. )
  339. props.append(prop)
  340. return props
  341. @staticmethod
  342. def read_prop_val(cur, prop_type):
  343. match prop_type:
  344. case PropertyType.NULL:
  345. return None
  346. case PropertyType.BOOL:
  347. return bool(serial.read_u8(cur))
  348. case PropertyType.UINT32:
  349. return serial.read_u32(cur)
  350. case PropertyType.FLOAT32:
  351. return serial.read_f32(cur)
  352. case PropertyType.STR:
  353. return serial.decode_str(cur)
  354. case PropertyType.ENUM:
  355. return serial.decode_str(cur)
  356. case PropertyType.BUFFER:
  357. pass
  358. case PropertyType.SCENE_NODE_ID:
  359. return serial.read_u32(cur)
  360. case _:
  361. raise Exception("unknown property type returned")
  362. def get_property_value(self, node_id, prop_name):
  363. req = bytearray()
  364. serial.write_u32(req, node_id)
  365. serial.encode_str(req, prop_name)
  366. cur = self._make_request(Command.GET_PROPERTY_VALUE, req)
  367. prop_type = serial.read_u8(cur)
  368. def prop_read_fn(cur):
  369. prop_status = serial.read_u8(cur)
  370. match prop_status:
  371. case PropertyStatus.NULL:
  372. return None
  373. case PropertyStatus.UNSET | PropertyStatus.OK:
  374. return Api.read_prop_val(cur, prop_type)
  375. vals = serial.decode_arr(cur, prop_read_fn)
  376. return vals
  377. def add_node(self, node_name, node_type):
  378. req = bytearray()
  379. serial.encode_str(req, node_name)
  380. serial.write_u8(req, int(node_type))
  381. cur = self._make_request(Command.ADD_NODE, req)
  382. node_id = serial.read_u32(cur)
  383. return node_id
  384. def remove_node(self, node_id):
  385. req = bytearray()
  386. serial.write_u32(req, node_id)
  387. self._make_request(Command.REMOVE_NODE, req)
  388. def rename_node(self, node_id, node_name):
  389. req = bytearray()
  390. serial.write_u32(req, node_id)
  391. serial.encode_str(req, node_name)
  392. self._make_request(Command.RENAME_NODE, req)
  393. def scan_dangling(self):
  394. cur = self._make_request(Command.SCAN_DANGLING, bytearray())
  395. dangling_len = serial.decode_varint(cur)
  396. dangling = []
  397. for _ in range(dangling_len):
  398. node_id = serial.read_u32(cur)
  399. dangling.append(node_id)
  400. return dangling
  401. def lookup_node_id(self, node_path):
  402. req = bytearray()
  403. serial.encode_str(req, node_path)
  404. try:
  405. cur = self._make_request(Command.LOOKUP_NODE_ID, req)
  406. except exc.RequestNodeNotFound:
  407. return None
  408. return serial.read_u32(cur)
  409. def add_property(self, node_id, prop):
  410. req = bytearray()
  411. serial.write_u32(req, node_id)
  412. serial.encode_str(req, prop.name)
  413. serial.write_u8(req, int(prop.type))
  414. serial.write_u8(req, int(prop.subtype))
  415. serial.write_u32(req, int(prop.array_len))
  416. if prop.defaults is None:
  417. serial.write_u8(req, 0)
  418. else:
  419. serial.write_u8(req, 1)
  420. defaults_len = len(prop.defaults)
  421. serial.encode_varint(req, defaults_len)
  422. for default in prop.defaults:
  423. match prop.type:
  424. case PropertyType.UINT32:
  425. serial.write_u32(req, default)
  426. case PropertyType.FLOAT32:
  427. serial.write_f32(req, default)
  428. case _:
  429. raise exc.PropertyWrongType
  430. serial.encode_str(req, prop.ui_name)
  431. serial.encode_str(req, prop.desc)
  432. serial.write_u8(req, int(prop.is_null_allowed))
  433. if prop.min_val is None:
  434. serial.write_u8(req, 0)
  435. else:
  436. serial.write_u8(req, 1)
  437. match prop.type:
  438. case PropertyType.UINT32:
  439. serial.write_u32(req, prop.min_val)
  440. case PropertyType.FLOAT32:
  441. serial.write_f32(req, prop.min_val)
  442. case _:
  443. raise exc.PropertyWrongType
  444. if prop.max_val is None:
  445. serial.write_u8(req, 0)
  446. else:
  447. serial.write_u8(req, 1)
  448. match prop.type:
  449. case PropertyType.UINT32:
  450. serial.write_u32(req, prop.max_val)
  451. case PropertyType.FLOAT32:
  452. serial.write_f32(req, prop.max_val)
  453. case _:
  454. raise exc.PropertyWrongType
  455. serial.encode_varint(req, len(prop.enum_items))
  456. for enum_item in prop.enum_items:
  457. if prop.type != PropertyType.ENUM:
  458. raise exc.PropertyWrongType
  459. serial.encode_str(req, enum_item)
  460. self._make_request(Command.ADD_PROPERTY, req)
  461. def link_node(self, child_id, parent_id):
  462. req = bytearray()
  463. serial.write_u32(req, child_id)
  464. serial.write_u32(req, parent_id)
  465. self._make_request(Command.LINK_NODE, req)
  466. def unlink_node(self, child_id, parent_id):
  467. req = bytearray()
  468. serial.write_u32(req, child_id)
  469. serial.write_u32(req, parent_id)
  470. self._make_request(Command.UNLINK_NODE, req)
  471. def set_property_bool(self, node_id, prop_name, val):
  472. req = bytearray()
  473. serial.write_u32(req, node_id)
  474. serial.encode_str(req, prop_name)
  475. serial.write_u8(req, int(val))
  476. self._make_request(Command.SET_PROPERTY_VALUE, req)
  477. def set_property_u32(self, node_id, prop_name, i, val):
  478. req = bytearray()
  479. serial.write_u32(req, node_id)
  480. serial.encode_str(req, prop_name)
  481. serial.write_u32(req, i)
  482. serial.write_u32(req, val)
  483. self._make_request(Command.SET_PROPERTY_VALUE, req)
  484. def set_property_f32(self, node_id, prop_name, i, val):
  485. req = bytearray()
  486. serial.write_u32(req, node_id)
  487. serial.encode_str(req, prop_name)
  488. serial.write_u32(req, i)
  489. serial.write_f32(req, val)
  490. self._make_request(Command.SET_PROPERTY_VALUE, req)
  491. def set_property_buffer(self, node_id, prop_name, i, buf):
  492. req = bytearray()
  493. serial.write_u32(req, node_id)
  494. serial.encode_str(req, prop_name)
  495. serial.write_u32(req, i)
  496. serial.encode_buf(req, buf)
  497. self._make_request(Command.SET_PROPERTY_VALUE, req)
  498. def set_property_str(self, node_id, prop_name, i, val):
  499. req = bytearray()
  500. serial.write_u32(req, node_id)
  501. serial.encode_str(req, prop_name)
  502. serial.write_u32(req, i)
  503. serial.encode_str(req, val)
  504. self._make_request(Command.SET_PROPERTY_VALUE, req)
  505. def get_signals(self, node_id):
  506. req = bytearray()
  507. serial.write_u32(req, node_id)
  508. cur = self._make_request(Command.GET_SIGNALS, req)
  509. sigs_len = serial.decode_varint(cur)
  510. sigs = []
  511. for _ in range(sigs_len):
  512. sigs.append(serial.decode_str(cur))
  513. return sigs
  514. def register_slot(self, node_id, sig_name, slot_name, user_data):
  515. req = bytearray()
  516. serial.write_u32(req, node_id)
  517. serial.encode_str(req, sig_name)
  518. serial.encode_str(req, slot_name)
  519. serial.encode_varint(req, len(user_data))
  520. req += user_data
  521. cur = self._make_request(Command.REGISTER_SLOT, req)
  522. slot_id = serial.read_u32(cur)
  523. return slot_id
  524. def unregister_slot(self, node_id, sig_name, slot_id):
  525. req = bytearray()
  526. serial.write_u32(req, node_id)
  527. serial.encode_str(req, sig_name)
  528. serial.write_u32(req, slot_id)
  529. self._make_request(Command.UNREGISTER_SLOT, req)
  530. def lookup_slot_id(self, node_id, sig_name, slot_name):
  531. req = bytearray()
  532. serial.write_u32(req, node_id)
  533. serial.encode_str(req, sig_name)
  534. serial.encode_str(req, slot_name)
  535. try:
  536. cur = self._make_request(Command.LOOKUP_SLOT_ID, req)
  537. except exc.RequestSlotNotFound:
  538. return None
  539. return serial.read_u32(cur)
  540. def get_slots(self, node_id, sig_name):
  541. req = bytearray()
  542. serial.write_u32(req, node_id)
  543. serial.encode_str(req, sig_name)
  544. cur = self._make_request(Command.GET_SLOTS, req)
  545. slots_len = serial.decode_varint(cur)
  546. slots = []
  547. for _ in range(slots_len):
  548. slot_name = serial.decode_str(cur)
  549. slot_id = serial.read_u32(cur)
  550. slots.append((slot_id, slot_name))
  551. return slots
  552. def get_methods(self, node_id):
  553. req = bytearray()
  554. serial.write_u32(req, node_id)
  555. cur = self._make_request(Command.GET_METHODS, req)
  556. methods_len = serial.decode_varint(cur)
  557. methods = []
  558. for _ in range(methods_len):
  559. method_name = serial.decode_str(cur)
  560. methods.append(method_name)
  561. return methods
  562. def get_method(self, node_id, method_name):
  563. req = bytearray()
  564. serial.write_u32(req, node_id)
  565. serial.encode_str(req, method_name)
  566. cur = self._make_request(Command.GET_METHOD, req)
  567. args_len = serial.decode_varint(cur)
  568. args = []
  569. for _ in range(args_len):
  570. arg_name = serial.decode_str(cur)
  571. arg_type = serial.read_u8(cur)
  572. args.append((arg_name, arg_type))
  573. results_len = serial.decode_varint(cur)
  574. results = []
  575. for _ in range(results_len):
  576. result_name = serial.decode_str(cur)
  577. result_type = serial.read_u8(cur)
  578. results.append((result_name, result_type))
  579. return (args, results)
  580. def call_method(self, node_id, method_name, arg_data):
  581. req = bytearray()
  582. serial.write_u32(req, node_id)
  583. serial.encode_str(req, method_name)
  584. serial.encode_buf(req, arg_data)
  585. cur = self._make_request(Command.CALL_METHOD, req)
  586. errc = serial.read_u8(cur)
  587. result = serial.decode_buf(cur)
  588. return (errc, result)