Un-overengineered a bit. Made the descriptor smaller.
[cerebrum.git] / pylibcerebrum / pylibcerebrum.py
blob113ca4388b143047170ea6362cf24b4f6ed3e1ee
2 import serial
3 import json
4 import struct
5 import pylzma
7 class Ganglion:
8 # NOTE: the device config is *not* the stuff from the config "dev" section but
9 #read from the device. It can also be found in that [devicename].config.json
10 #file created by the code generator
11 def __init__(self, device=None, baudrate=115200, config=None, ser=None):
12 #FIXME HACK to et the initialization go smooth despite the __*__ special functions and "config" not yet being set
13 self._config = None
14 if ser is None:
15 self._ser = serial.Serial(device, baudrate)
16 self._opened_ser = self._ser
17 self._config = self.read_config()
18 else:
19 self._config = config
20 self._ser = ser
22 def __del__(self):
23 self.close()
25 def close(self):
26 try:
27 self._opened_ser.close()
28 except AttributeError:
29 pass
31 def read_config(self):
32 self._ser.write(b'\\#\x00\x00\x00\x00')
33 (clen,) = struct.unpack(">H", self._ser.read(2))
34 cbytes = self._ser.read(clen)
35 self._ser.read(2) #read and ignore the not-yet-crc
36 return json.JSONDecoder().decode(str(pylzma.decompress(cbytes), "UTF-8"))
38 @property
39 def members(self):
40 if "members" in self._config:
41 return list(self._config["members"].keys())
42 return []
44 @property
45 def properties(self):
46 if "properties" in self._config:
47 return list(self._config["properties"].keys())
48 return []
50 @property
51 def functions(self):
52 if "functions" in self._config:
53 return list(self._config["functions"].keys())
54 return []
56 def callfunc(self, fid, argsfmt, args, retfmt):
57 cmd = b'\\#' + struct.pack(">HH", fid, struct.calcsize(argsfmt)) + struct.pack(argsfmt, *args) + b'\x00\x00'
58 self._ser.write(cmd)
59 (clen,) = struct.unpack(">H", self._ser.read(2))
60 cbytes = self._ser.read(clen)
61 self._ser.read(2) #read and ignore the not-yet-crc
62 if clen is not struct.calcsize(retfmt):
63 #FIXME error handling
64 print("Length mismatch: {} != {}".format(clen, struct.calcsize(retfmt)))
65 return None
66 return struct.unpack(retfmt, cbytes)
68 def __dir__(self):
69 return self.members + self.properties + self.functions + list(self.__dict__.keys())
71 def __setattr__(self, name, value):
72 if name is not "_config":
73 if self._config and "properties" in self._config and name in self._config["properties"]:
74 var = self._config["properties"][name]
75 return self.callfunc(var["id"]+1, var["fmt"], value, "")
76 self.__dict__[name] = value
78 def __getattr__(self, name):
79 if not self._config:
80 raise AttributeError(name)
82 if "members" in self._config and name in self._config["members"]:
83 return Ganglion(config=self._config["members"][name], ser=self._ser)
85 if "properties" in self._config and name in self._config["properties"]:
86 var = self._config["properties"][name]
87 return self.callfunc(var["id"], "", None, var["fmt"])
89 if "functions" in self._config and name in self._config["functions"]:
90 fun = self._config["functions"][name]
91 def proxy_method(*args):
92 return self.callfunc(fun["id"], fun.get("args", ""), args, fun.get("returns", ""))
93 return proxy_method