22# Copyright (C) 2006,2007 Douglas P Lau
33# Copyright (C) 2009 Marcus Wanner, Paulo Vieira, rhn
44# Copyright (C) 2010,2011,2012 Marcus Wanner
5+ # Copyright (C) 2023 Nicolas Schodet
56#
67# This program is free software: you can redistribute it and/or modify
78# it under the terms of the GNU General Public License as published by
@@ -30,51 +31,58 @@ def __init__(self, version, product_id, sensor_type):
3031 self .sensor_type = sensor_type
3132
3233 def clarifybinary (self , instr , label ):
33- outstr = ''
34- outstr += ( label + ' : `' + instr + ' `\n ' )
34+ outstr = ""
35+ outstr += label + " : `" + instr + " `\n "
3536 for char in instr :
36- outstr += ( hex (ord (char ))+ ', ' )
37- outstr += ( ' \n ' )
37+ outstr += hex (ord (char )) + ", "
38+ outstr += " \n "
3839 return outstr
3940
4041 def __str__ (self ):
41- outstr = ''
42- outstr += ( self .clarifybinary (str (self .version ), ' Version' ) )
43- outstr += ( self .clarifybinary (str (self .product_id ), ' Product ID' ) )
44- outstr += ( self .clarifybinary (str (self .sensor_type ), ' Type' ) )
42+ outstr = ""
43+ outstr += self .clarifybinary (str (self .version ), " Version" )
44+ outstr += self .clarifybinary (str (self .product_id ), " Product ID" )
45+ outstr += self .clarifybinary (str (self .sensor_type ), " Type" )
4546 return outstr
4647
48+
4749class BaseDigitalSensor (nxt .sensor .Sensor ):
48- """Object for digital sensors. I2C_ADDRESS is the dictionary storing name
49- to i2c address mappings. It should be updated in every subclass. When
50- subclassing this class, make sure to call add_compatible_sensor to add
51- compatible sensor data.
50+ """Object for digital sensors.
51+
52+ :param bool check_compatible: Check sensor class match the connected sensor.
53+
54+ If `check_compatible` is ``True``, queries the sensor for its name and print
55+ a warning if the wrong sensor class is used.
56+
57+ `I2C_ADDRESS` is the dictionary storing name to I2C address mappings. It should be
58+ updated in every subclass. When subclassing this class, make sure to call
59+ :func:`add_compatible_sensor` to add compatible sensor data.
5260 """
61+
5362 I2C_DEV = 0x02
54- I2C_ADDRESS = {'version' : (0x00 , '8s' ),
55- 'product_id' : (0x08 , '8s' ),
56- 'sensor_type' : (0x10 , '8s' ),
57- # 'factory_zero': (0x11, 1), # is this really correct?
58- 'factory_scale_factor' : (0x12 , 'B' ),
59- 'factory_scale_divisor' : (0x13 , 'B' ),
63+ I2C_ADDRESS = {
64+ "version" : (0x00 , "8s" ),
65+ "product_id" : (0x08 , "8s" ),
66+ "sensor_type" : (0x10 , "8s" ),
67+ # "factory_zero": (0x11, 1), # is this really correct?
68+ "factory_scale_factor" : (0x12 , "B" ),
69+ "factory_scale_divisor" : (0x13 , "B" ),
6070 }
6171
6272 def __init__ (self , brick , port , check_compatible = True ):
63- """Creates a BaseDigitalSensor. If check_compatible is True, queries
64- the sensor for its name, and if a wrong sensor class was used, prints
65- a warning.
66- """
6773 super ().__init__ (brick , port )
6874 self .set_input_mode (nxt .sensor .Type .LOW_SPEED_9V , nxt .sensor .Mode .RAW )
6975 self .last_poll = time .time ()
7076 self .poll_delay = 0.01
7177 time .sleep (0.1 ) # Give I2C time to initialize
72- #Don't do type checking if this class has no compatible sensors listed.
73- try : self .compatible_sensors
74- except AttributeError : check_compatible = False
78+ # Don't do type checking if this class has no compatible sensors listed.
79+ try :
80+ self .compatible_sensors
81+ except AttributeError :
82+ check_compatible = False
7583 if check_compatible :
7684 sensor = self .get_sensor_info ()
77- if not sensor in self .compatible_sensors :
85+ if sensor not in self .compatible_sensors :
7886 logger .warning (
7987 "wrong sensor class chosen for sensor %s on port %s" ,
8088 sensor .product_id ,
@@ -91,57 +99,66 @@ def __init__(self, brick, port, check_compatible=True):
9199 )
92100
93101 def _ls_get_status (self , size ):
94- for n in range (30 ): # https://code.google.com/p/nxt-python/issues/detail?id=35
102+ for n in range (30 ): # https://code.google.com/p/nxt-python/issues/detail?id=35
95103 try :
96104 b = self ._brick .ls_get_status (self ._port )
97105 if b >= size :
98106 return b
99107 except I2CPendingError :
100108 pass
101- raise I2CError (' ls_get_status timeout' )
109+ raise I2CError (" ls_get_status timeout" )
102110
103111 def _i2c_command (self , address , value , format ):
104- """Writes an i2c value to the given address. value must be a string. value is
105- a tuple of values corresponding to the given format.
112+ """Write one or several values to an I2C register.
113+
114+ :param int address: I2C register address.
115+ :param tuple value: Tuple of values to write.
116+ :param str format: Format string using :mod:`struct` syntax.
106117 """
107118 value = struct .pack (format , * value )
108119 msg = bytes ((self .I2C_DEV , address )) + value
109120 now = time .time ()
110- if self .last_poll + self .poll_delay > now :
121+ if self .last_poll + self .poll_delay > now :
111122 diff = now - self .last_poll
112123 time .sleep (self .poll_delay - diff )
113124 self .last_poll = time .time ()
114125 self ._brick .ls_write (self ._port , msg , 0 )
115126
116127 def _i2c_query (self , address , format ):
117- """Reads an i2c value from given address, and returns a value unpacked
118- according to the given format. Format is the same as in the struct
119- module. See http://docs.python.org/library/struct.html#format-strings
128+ """Read one or several values from an I2C register.
129+
130+ :param int address: I2C register address.
131+ :param str format: Format string using :mod:`struct` syntax.
132+ :return: Read values in a tuple.
133+ :rtype: tuple
120134 """
121135 size = struct .calcsize (format )
122136 msg = bytes ((self .I2C_DEV , address ))
123137 now = time .time ()
124- if self .last_poll + self .poll_delay > now :
138+ if self .last_poll + self .poll_delay > now :
125139 diff = now - self .last_poll
126140 time .sleep (self .poll_delay - diff )
127141 self .last_poll = time .time ()
128142 self ._brick .ls_write (self ._port , msg , size )
129143 try :
130144 self ._ls_get_status (size )
131145 finally :
132- #we should clear the buffer no matter what happens
146+ # we should clear the buffer no matter what happens
133147 data = self ._brick .ls_read (self ._port )
134148 if len (data ) < size :
135- raise I2CError (' Read failure: Not enough bytes' )
149+ raise I2CError (" Read failure: Not enough bytes" )
136150 data = struct .unpack (format , data [- size :])
137151 return data
138152
139153 def read_value (self , name ):
140- """Reads a value from the sensor. Name must be a string found in
141- self.I2C_ADDRESS dictionary. Entries in self.I2C_ADDRESS are in the
142- name: (address, format) form, with format as in the struct module.
143- Be careful on unpacking single variables - struct module puts them in
144- tuples containing only one element.
154+ """Read one or several values from the sensor.
155+
156+ :param str name: Name of the values to read.
157+ :return: Read values in a tuple.
158+ :rtype: tuple
159+
160+ The `name` parameter is an index inside `I2C_ADDRESS` dictionary, which gives
161+ the corresponding I2C register address and format string.
145162 """
146163 address , fmt = self .I2C_ADDRESS [name ]
147164 for n in range (3 ):
@@ -152,62 +169,78 @@ def read_value(self, name):
152169 raise I2CError ("read_value timeout" )
153170
154171 def write_value (self , name , value ):
155- """Writes value to the sensor. Name must be a string found in
156- self.I2C_ADDRESS dictionary. Entries in self.I2C_ADDRESS are in the
157- name: (address, format) form, with format as in the struct module.
158- value is a tuple of values corresponding to the format from
159- self.I2C_ADDRESS dictionary.
172+ """Write one or several values to the sensor.
173+
174+ :param str name: Name of the values to write.
175+ :param tuple value: Tuple of values to write.
176+
177+ The `name` parameter is an index inside `I2C_ADDRESS` dictionary, which gives
178+ the corresponding I2C register address and format string.
160179 """
161180 address , fmt = self .I2C_ADDRESS [name ]
162181 self ._i2c_command (address , value , fmt )
163182
164183 def get_sensor_info (self ):
165- version = self .read_value ('version' )[0 ].decode ('windows-1252' ).split ('\0 ' )[0 ]
166- product_id = self .read_value ('product_id' )[0 ].decode ('windows-1252' ).split ('\0 ' )[0 ]
167- sensor_type = self .read_value ('sensor_type' )[0 ].decode ('windows-1252' ).split ('\0 ' )[0 ]
184+ version = self .read_value ("version" )[0 ].decode ("windows-1252" ).split ("\0 " )[0 ]
185+ product_id = (
186+ self .read_value ("product_id" )[0 ].decode ("windows-1252" ).split ("\0 " )[0 ]
187+ )
188+ sensor_type = (
189+ self .read_value ("sensor_type" )[0 ].decode ("windows-1252" ).split ("\0 " )[0 ]
190+ )
168191 return SensorInfo (version , product_id , sensor_type )
169192
170193 @classmethod
171194 def add_compatible_sensor (cls , version , product_id , sensor_type ):
172- """Adds an entry in the compatibility table for the sensor. If version
173- is None, then it's the default class for this model. If product_id is
174- None, then this is the default class for this vendor.
195+ """Adds an entry in the compatibility table for the sensor.
196+
197+ :param version: Sensor version, or ``None`` for default class.
198+ :type version: str or None
199+ :param product_id: Product identifier, or ``None`` for default class.
200+ :type product_id: str or None
201+ :param str sensor_type: Sensor type
175202 """
176203 try :
177204 cls .compatible_sensors
178205 except AttributeError :
179206 cls .compatible_sensors = []
180207 finally :
181- cls .compatible_sensors .append (SCompatibility (version , product_id ,
182- sensor_type ))
183- add_mapping (cls , version , product_id , sensor_type )
208+ cls .compatible_sensors .append (
209+ _SCompatibility (version , product_id , sensor_type )
210+ )
211+ _add_mapping (cls , version , product_id , sensor_type )
184212
185213
186- class SCompatibility (SensorInfo ):
187- """An object that helps manage the sensor mappings"""
214+ class _SCompatibility (SensorInfo ):
215+ """An object that helps manage the sensor mappings."""
216+
188217 def __eq__ (self , other ):
189218 if self .product_id is None :
190219 return self .product_id == other .product_id
191220 elif self .version is None :
192- return (self .product_id == other .product_id and
193- self .sensor_type == other .sensor_type )
221+ return (
222+ self .product_id == other .product_id
223+ and self .sensor_type == other .sensor_type
224+ )
194225 else :
195- return (self .version == other .version and
196- self .product_id == other .product_id and
197- self .sensor_type == other .sensor_type )
226+ return (
227+ self .version == other .version
228+ and self .product_id == other .product_id
229+ and self .sensor_type == other .sensor_type
230+ )
231+
198232
199233sensor_mappings = {}
200234
201235
202- def add_mapping (cls , version , product_id , sensor_type ):
203- "None means any other value"
236+ def _add_mapping (cls , version , product_id , sensor_type ):
204237 if product_id not in sensor_mappings :
205238 sensor_mappings [product_id ] = {}
206239 models = sensor_mappings [product_id ]
207240
208241 if sensor_type is None :
209242 if sensor_type in models :
210- raise ValueError (' Already registered!' )
243+ raise ValueError (" Already registered!" )
211244 models [sensor_type ] = cls
212245 return
213246
@@ -216,7 +249,7 @@ def add_mapping(cls, version, product_id, sensor_type):
216249 versions = models [sensor_type ]
217250
218251 if version in versions :
219- raise ValueError (' Already registered!' )
252+ raise ValueError (" Already registered!" )
220253 else :
221254 versions [version ] = cls
222255
@@ -226,14 +259,22 @@ class SearchError(Exception):
226259
227260
228261def find_class (info ):
229- """Returns an appropriate class for the given SensorInfo"""
262+ """Returns an appropriate class.
263+
264+ :param SensorInfo info: Information read from the sensor.
265+ :return: Class corresponding to sensor.
266+ :rtype: BaseDigitalSensor
267+ :raises SearchError: When no class found.
268+ """
230269 dic = sensor_mappings
231- for val , msg in zip ((info .product_id , info .sensor_type , info .version ),
232- ('Vendor' , 'Model' , 'Version' )):
270+ for val , msg in zip (
271+ (info .product_id , info .sensor_type , info .version ),
272+ ("Vendor" , "Model" , "Version" ),
273+ ):
233274 if val in dic :
234275 dic = dic [val ]
235276 elif None in dic :
236277 dic = dic [None ]
237278 else :
238- raise SearchError (msg + ' not found' )
279+ raise SearchError (msg + " not found" )
239280 return dic [info .sensor_type ][None ]
0 commit comments