1111
1212class Result (object ):
1313 """A command result from the controller."""
14+ props = []
1415 def __init__ (self , data ):
1516 self .data = data
1617 self .decode (data )
@@ -23,9 +24,12 @@ def to_float(self, two_bytes):
2324 """Convert a list of two bytes into a floating point value."""
2425 # convert two bytes to a float value
2526 return ((two_bytes [1 ] << 8 ) | two_bytes [0 ]) / 100.0
27+ def __str__ (self ):
28+ return "%s{%s}" % (self .__class__ .__name__ , ", " .join (map (lambda a : "%s: %s" % (a , getattr (self , a )), self .props )))
2629
2730class QueryResult (Result ):
2831 """The result of a query command."""
32+ props = ['batt_voltage' , 'pv_voltage' , 'load_amps' , 'batt_overdischarge_voltage' , 'batt_full_voltage' , 'load_on' , 'load_overload' , 'load_short' , 'batt_overload' , 'batt_overdischarge' , 'batt_full' , 'batt_charging' , 'batt_temp' , 'charge_current' ]
2933 def decode (self , data ):
3034 """Decodes the query result, storing results as fields"""
3135 if len (data ) < 23 :
@@ -49,7 +53,7 @@ def decode(self, data):
4953
5054class Command (object ):
5155 """A command sent to the controller"""
52- def __init__ (self , code , data = [] ):
56+ def __init__ (self , code , data = bytearray () ):
5357 self .code = code
5458 self .data = data
5559 def decode_result (self , data ):
@@ -74,8 +78,8 @@ def __init__(self, state):
7478
7579class TracerSerial (object ):
7680 """A serial interface to the Tracer"""
77- sync_header = [0xEB , 0x90 ] * 3
78- comm_init = [0xAA , 0x55 ] * 3 + sync_header
81+ sync_header = bytearray ( [0xEB , 0x90 ] * 3 )
82+ comm_init = bytearray ( [0xAA , 0x55 ] * 3 ) + sync_header
7983
8084 def __init__ (self , tracer , port ):
8185 """Create a new Tracer interface on the given serial port
@@ -87,23 +91,45 @@ def __init__(self, tracer, port):
8791
8892 def to_bytes (self , command ):
8993 """Converts the command into the bytes that should be sent"""
90- cmd_data = self .tracer .get_command_bytes (command ) + [ 0x00 , 0x00 , 0x7F ]
94+ cmd_data = self .tracer .get_command_bytes (command ) + bytearray ( b' \x00 \x00 \x7F ' )
9195 crc_data = self .tracer .add_crc (cmd_data )
9296 to_send = self .comm_init + crc_data
9397
9498 return to_send
9599
96100 def from_bytes (self , data ):
97101 """Given bytes from the serial port, returns the appropriate command result"""
98- if list ( data [0 :6 ]) != self .sync_header :
102+ if data [0 :6 ] != self .sync_header :
99103 raise Exception ("Invalid sync header" )
100104 if len (data ) != data [8 ] + 12 :
101- raise Exception ("Invalid length" )
102- print list (data [6 :])
105+ raise Exception ("Invalid length. Expecting %d, got %d" % (data [8 ] + 12 , len (data )))
103106 if not self .tracer .verify_crc (data [6 :]):
104- print "invalid crc"
107+ print "invalid crc"
105108 #raise Exception("Invalid CRC")
106- return self .tracer .get_command (data [6 :])
109+ return self .tracer .get_result (data [6 :])
110+
111+ def send_command (self , command ):
112+ to_send = self .to_bytes (command )
113+ if len (to_send ) != self .port .write (to_send ):
114+ raise IOError ("Error sending command: did not send all bytes" )
115+
116+ def receive_result (self ):
117+ buff = bytearray ()
118+ read_idx = 0
119+
120+ b = self .port .read (1 )
121+ to_read = 200
122+
123+ while b >= 0 and read_idx < (to_read + 12 ):
124+ buff += b
125+ if read_idx < len (self .sync_header ) and b [0 ] != self .sync_header [read_idx ]:
126+ raise IOError ("Error receiving result: invalid sync header" )
127+ # the location of the read length
128+ elif read_idx == 8 :
129+ to_read = b [0 ]
130+ read_idx += 1
131+ b = self .port .read (1 )
132+ return self .from_bytes (buff )
107133
108134class Tracer (object ):
109135 """An implementation of the Tracer MT-5 communication protocol"""
@@ -116,17 +142,17 @@ def __init__(self, controller_id):
116142 def get_command_bytes (self , command ):
117143 """Given a command, gets its byte representation
118144
119- This excludes the CRC and trailer."""
120- data = []
145+ This excludes the header, CRC, and trailer."""
146+ data = bytearray ()
121147 data .append (self .controller_id )
122148 data .append (command .code )
123149 data .append (len (command .data ))
124150 data += command .data
125151
126152 return data
127153
128- def get_command (self , data ):
129- if data [1 ] == 0xA0 :
154+ def get_result (self , data ):
155+ if data [1 ] == QueryCommand (). code :
130156 return QueryResult (data [3 :])
131157
132158 def verify_crc (self , data ):
@@ -137,7 +163,6 @@ def verify_crc(self, data):
137163
138164 def add_crc (self , data ):
139165 """Returns a copy of the data with the CRC added"""
140- data = list (data )
141166 if len (data ) < 6 :
142167 raise Exception ("data are too short" )
143168 # the input CRC bytes must be zeroed
0 commit comments