38 from __future__
import division
40 from collections
import deque
50 from sensor_msgs.msg
import BatteryState
51 from diagnostic_msgs.msg
import DiagnosticStatus, DiagnosticArray, KeyValue
55 rv = float(raw_val.rstrip(
'mAh').strip()) / 1000.0
57 rv = float(raw_val.rstrip(
'Ah').strip())
58 elif 'mWh' in raw_val:
59 rv = float(raw_val.rstrip(
'mWh').strip()) / 1000.0
61 rv = float(raw_val.rstrip(
'Wh').strip())
63 raise Exception(
'Value %s did not have supported units. (mAh,Ah,mWh,Wh)' % raw_val)
68 rv = float(raw_val.rstrip(
'mV').strip()) / 1000.0
70 rv = float(raw_val.rstrip(
'V').strip())
72 raise Exception(
'Value %s did not have "V" or "mV"' % raw_val)
77 rv = float(raw_val.rstrip(
'mA').strip()) / 1000.0
79 rv = float(raw_val.rstrip(
'A').strip())
81 rv = float(raw_val.rstrip(
'mW').strip()) / 1000.0
83 rv = float(raw_val.rstrip(
'W').strip())
85 raise Exception(
'Value %s did not have supported units. (mAh,Ah,mWh,Wh)' % raw_val)
89 f = open(filename,
'r') 92 data = data.replace('\t',
' ')
96 if not os.access(filename, os.F_OK):
98 f = open(filename,
'r') 107 except exceptions.ValueError:
111 if _battery_acpi_path.startswith(
'/proc'):
113 if os.access(_battery_acpi_path, os.F_OK):
114 o =
slerp(_battery_acpi_path+
'/info')
116 raise NameError(_battery_acpi_path +
' does not exist')
118 batt_info = yaml.load(o)
119 design_capacity =
_strip_Ah(batt_info.get(
'design capacity',
'0 mAh'))
120 last_full_capacity =
_strip_Ah(batt_info.get(
'last full capacity',
'0 mAh'))
123 if os.path.exists(_battery_acpi_path +
'/energy_full_design'):
124 design_capacity =
_read_number(_battery_acpi_path +
'/energy_full_design')/10e5
125 elif os.path.exists(_battery_acpi_path +
'/charge_full_design'):
126 design_capacity =
_read_number(_battery_acpi_path +
'/charge_full_design')/10e5
128 raise NameError(_battery_acpi_path +
'/charge_full_design || ' + _battery_acpi_path +
'/energy_full_design does not exist')
130 if os.path.exists(_battery_acpi_path +
'/energy_full'):
131 last_full_capacity =
_read_number(_battery_acpi_path +
'/energy_full')/10e5
132 elif os.path.exists(_battery_acpi_path +
'/charge_full'):
133 last_full_capacity =
_read_number(_battery_acpi_path +
'/charge_full')/10e5
135 raise NameError(_battery_acpi_path +
'/charge_full || ' + _battery_acpi_path +
'/energy_full does not exist')
136 return (design_capacity, last_full_capacity)
138 state_to_val = {
'charged': BatteryState.POWER_SUPPLY_STATUS_FULL,
139 'full': BatteryState.POWER_SUPPLY_STATUS_FULL,
140 'charging': BatteryState.POWER_SUPPLY_STATUS_CHARGING,
141 'discharging': BatteryState.POWER_SUPPLY_STATUS_DISCHARGING,
142 'unknown': BatteryState.POWER_SUPPLY_STATUS_UNKNOWN, }
144 val_to_state = {BatteryState.POWER_SUPPLY_STATUS_FULL:
'charged',
145 BatteryState.POWER_SUPPLY_STATUS_CHARGING:
'charging',
146 BatteryState.POWER_SUPPLY_STATUS_DISCHARGING:
'discharging',
147 BatteryState.POWER_SUPPLY_STATUS_UNKNOWN:
'unknown', }
149 diag_level_to_msg = { DiagnosticStatus.OK:
'OK',
150 DiagnosticStatus.WARN:
'Warning',
151 DiagnosticStatus.ERROR:
'Error' }
159 if _battery_acpi_path.startswith(
'/proc'):
161 if os.access(_battery_acpi_path, os.F_OK):
162 o =
slerp(_battery_acpi_path+
'/state')
164 raise Exception(_battery_acpi_path+
' does not exist')
166 batt_info = yaml.load(o)
168 state = batt_info.get(
'charging state',
'discharging')
169 rv.power_supply_status = state_to_val.get(state, 0)
170 rv.current =
_strip_A(batt_info.get(
'present rate',
'-1 mA'))
171 if rv.power_supply_status == BatteryState.POWER_SUPPLY_STATUS_DISCHARGING:
172 rv.current = math.copysign(rv.current, -1)
174 rv.charge =
_strip_Ah(batt_info.get(
'remaining capacity',
'-1 mAh'))
175 rv.voltage =
_strip_V(batt_info.get(
'present voltage',
'-1 mV'))
176 rv.present = batt_info.get(
'present',
False)
178 rv.header.stamp = rospy.get_rostime()
182 state =
_read_string(_battery_acpi_path+
'/status',
'discharging').lower().rstrip()
183 rv.power_supply_status = state_to_val.get(state, 0)
185 if os.path.exists(_battery_acpi_path +
'/power_now'):
187 rv.current =
_read_number(_battery_acpi_path +
'/power_now')/10e5 / \
192 rv.current =
_read_number(_battery_acpi_path +
'/current_now')/10e5
194 if rv.power_supply_status == BatteryState.POWER_SUPPLY_STATUS_DISCHARGING:
195 rv.current = math.copysign(rv.current, -1)
197 if os.path.exists(_battery_acpi_path +
'/energy_now'):
198 rv.charge =
_read_number(_battery_acpi_path +
'/energy_now')/10e5
200 rv.charge =
_read_number(_battery_acpi_path +
'/charge_now')/10e5
202 rv.voltage =
_read_number(_battery_acpi_path +
'/voltage_now')/10e5
203 rv.present =
_read_number(_battery_acpi_path +
'/present') == 1
205 rv.header.stamp = rospy.get_rostime()
209 rv = DiagnosticStatus()
210 rv.level = DiagnosticStatus.OK
212 rv.name =
'Laptop Battery' 213 rv.hardware_id = socket.gethostname()
215 if not laptop_msg.present:
216 rv.level = DiagnosticStatus.ERROR
217 rv.message =
'Laptop battery missing' 219 rv.values.append(KeyValue(
'Voltage (V)', str(laptop_msg.voltage)))
220 rv.values.append(KeyValue(
'Current (A)', str(laptop_msg.current)))
221 rv.values.append(KeyValue(
'Charge (Ah)', str(laptop_msg.charge)))
222 rv.values.append(KeyValue(
'Capacity (Ah)', str(laptop_msg.capacity)))
223 rv.values.append(KeyValue(
'Design Capacity (Ah)', str(laptop_msg.design_capacity)))
224 rv.values.append(KeyValue(
'Percentage (%)', str(laptop_msg.percentage)))
225 rv.values.append(KeyValue(
'Charging state', val_to_state[laptop_msg.power_supply_status]))
237 self.
_power_pub = rospy.Publisher(
'laptop_charge', BatteryState, latch=
True, queue_size=5)
238 self.
_diag_pub = rospy.Publisher(
'/diagnostics', DiagnosticArray, queue_size=5)
248 self._batt_info_thread.daemon =
True 249 self._batt_info_thread.start()
254 self._batt_state_thread.daemon =
True 255 self._batt_state_thread.start()
259 while not rospy.is_shutdown():
266 except NameError
as e:
267 rospy.logwarn(
'Battery : unable to check laptop battery info [%s]' % e)
268 rospy.signal_shutdown(
'Battery : unable to check laptop battery info [%s]' % e)
273 while not rospy.is_shutdown():
280 rospy.logwarn(
'Battery : unable to check laptop battery state [%s]' % e)
281 rospy.signal_shutdown(
'Battery : unable to check laptop battery state [%s]' % e)
287 diag = DiagnosticArray()
288 diag.header.stamp = rospy.get_rostime()
297 self._msg.design_capacity = 0.0
298 self._msg.capacity = 0.0
300 if info_update_ok
and state_update_ok
and self._msg.capacity != 0:
301 self._msg.percentage = int(self._msg.charge / self._msg.capacity * 100.0)
304 if not info_update_ok
or not state_update_ok:
305 diag_stat.level = DiagnosticStatus.ERROR
306 diag_stat.message =
'Laptop battery data stale' 308 diag.status.append(diag_stat)
310 self._diag_pub.publish(diag)
311 self._power_pub.publish(self.
_msg)
313 if __name__ ==
'__main__':
314 rospy.init_node(
'laptop_battery')
319 while not rospy.is_shutdown():
322 except KeyboardInterrupt:
326 traceback.print_exc()
def _laptop_charge_to_diag(laptop_msg)
def _read_string(filename, default="")
def _check_battery_info(_battery_acpi_path)
def _check_battery_state(_battery_acpi_path)
def _check_batt_state(self)
def _read_number(filename, default=0)
def _check_batt_info(self)