async def request_heartbeats(self): """Request heartbeats on a regular basis.""" await self._ready.wait() while not self.transport.is_closing(): await asyncio.sleep(self.HEARTBEAT_DELAY_MS / 1000, loop=self.event_loop) hm.send(self.transport, hm.make_heartbeat_request())
def identify_smart_sensors(serial_conns): """ Given a list of serial port connections, figure out which contain smart sensors. Returns: A map of serial port names to UIDs. """ def recv_subscription_response(conn, uid_queue, stop_event): """ Place received subscription response UIDs from CONN into UID_QUEUE, stopping when STOP_EVENT is set. """ try: for packet in hm.blocking_read_generator(conn, stop_event): msg_type = packet.get_message_id() if msg_type == hm.MESSAGE_TYPES["SubscriptionResponse"]: _, _, uid = hm.parse_subscription_response(packet) uid_queue.put(uid) except serial.SerialException: pass device_map = {} candidates = [] for conn in serial_conns: old_timeout = conn.write_timeout conn.write_timeout = IDENTIFY_TIMEOUT try: hm.send(conn, hm.make_ping()) except serial.SerialTimeoutException: continue finally: conn.write_timeout = old_timeout maybe_device = namedtuple("MaybeDevice", ["serial_conn", "queue", "event", "thread"]) maybe_device.queue = queue.Queue() maybe_device.event = threading.Event() maybe_device.serial_conn = conn maybe_device.thread = threading.Thread( target=recv_subscription_response, args=(conn, maybe_device.queue, maybe_device.event)) candidates.append(maybe_device) for cand in candidates: cand.thread.start() for cand in candidates: try: uid = cand.queue.get(block=True, timeout=IDENTIFY_TIMEOUT) device_map[cand.serial_conn.name] = uid # Shut device up hm.send(cand.serial_conn, hm.make_subscription_request(uid, [], 0)) except queue.Empty: pass for cand in candidates: cand.event.set() cand.thread.join() return device_map
def handleOutput(self, n): """Processes the next n packets, or the number of packets, whichever is smaller, in the sendBuffer queue. sendBuffer should have tuples of (serial, packet) """ for _ in range(n): if self.sendBuffer.empty(): break serial, packet = self.sendBuffer.get() hm.send(serial, packet)
def _process_sub_request(self, msg): """Respond to a subscription request with an appropriate response.""" self.update_time = time.time() dev_id = hm.uid_to_device_id(self.uid) self.verbose_log("Subscription request received") params, delay = struct.unpack("<HH", msg.get_payload()) subscribed_params = hm.decode_params(dev_id, params) hm.send( self.transport, hm.make_subscription_response(dev_id, subscribed_params, delay, self.uid)) self.delay = delay self.subscribed_params.update(set(subscribed_params))
def _process_device_read(self, msg): self.verbose_log("Device read received") device_id = hm.uid_to_device_id(self.uid) # Send a device data with the requested param and value tuples params, = struct.unpack("<H", msg.get_payload()) read_params = hm.decode_params(device_id, params) read_data = [] for param in read_params: if not (hm.readable(device_id, param) and param in self.param_values): raise ValueError( "Tried to read unreadable parameter {}".format(param)) read_data.append((param, self.param_values[param])) hm.send(self.transport, hm.make_device_data(device_id, read_data))
def test_read_device_data(self): """ Check that DeviceData packets are received and decoded. """ start_time = time.time() hibike_message.send( self.dummy_device, hibike_message.make_subscription_request(self.dummy_dev_id, self.dummy_params, 40)) for packet in hibike_message.blocking_read_generator( self.dummy_device): if packet.get_message_id( ) == hibike_message.MESSAGE_TYPES["DeviceData"]: return self.assertLessEqual( time.time() - start_time, self.STOP_TIMEOUT, "did not read DeviceData packet before timeout")
def test_stop_event(self): """ Make sure that triggering the stop event prevents additional reads """ hibike_message.send( self.dummy_device, hibike_message.make_subscription_request(self.dummy_dev_id, self.dummy_params, 40)) start_time = time.time() stop_event = threading.Event() for _ in hibike_message.blocking_read_generator( self.dummy_device, stop_event): stop_event.set() self.assertLessEqual( time.time() - start_time, self.STOP_TIMEOUT, "blocking_read_generator didn't stop when stop_event triggered" )
def _process_device_write(self, msg): # Write to requested parameters # and return the values of the parameters written to using a device data self.verbose_log("Device write received") device_id = hm.uid_to_device_id(self.uid) write_params_and_values = hm.decode_device_write(msg, device_id) for (param, value) in write_params_and_values: if not (hm.writable(device_id, param) and param in self.param_values): raise ValueError( "Tried to write read-only parameter: {}".format(param)) self.param_values[param] = value updated_params = [] for (param, value) in write_params_and_values: if hm.readable(device_id, param): updated_params.append((param, value)) hm.send(self.transport, hm.make_device_data(device_id, updated_params))
async def send_subscribed_params(self): """Send values of subscribed parameters at a regular interval.""" await self._ready.wait() device_id = hm.uid_to_device_id(self.uid) while not self.transport.is_closing(): await asyncio.sleep(0.005, loop=self.event_loop) if self.update_time != 0 and self.delay != 0: if time.time() - self.update_time >= self.delay * 0.001: # If the time equal to the delay has elapsed since the previous device data, # send a device data with the device id # and the device's subscribed params and values data = [] for param in self.subscribed_params: data.append((param, self.param_values[param])) hm.send(self.transport, hm.make_device_data(device_id, data)) self.update_time = time.time() self.verbose_log("Regular data update sent from {}", hm.uid_to_device_name(self.uid))
def device_write_thread(ser, queue): while True: instruction, args = queue.get() if instruction == "ping": hm.send(ser, hm.make_ping()) elif instruction == "subscribe": uid, delay, params = args hm.send( ser, hm.make_subscription_request(hm.uid_to_device_id(uid), params, delay)) elif instruction == "read": uid, params = args hm.send(ser, hm.make_device_read(hm.uid_to_device_id(uid), params)) elif instruction == "write": uid, params_and_values = args hm.send( ser, hm.make_device_write(hm.uid_to_device_id(uid), params_and_values))
async def register_sensor(self, event_loop, devices, pending): """ Try to get our UID from the sensor and register it with `hibike_process`. """ await self._ready.wait() hm.send(self.transport, hm.make_ping()) await asyncio.sleep(IDENTIFY_TIMEOUT, loop=event_loop) if self.uid is None: self.quit() else: hm.send(self.transport, hm.make_ping()) hm.send( self.transport, hm.make_subscription_request(hm.uid_to_device_id(self.uid), [], 0)) devices[self.uid] = self pending.remove(self.transport.serial.name)
subscribed_params = [] params_and_values = [("pot0", 6.7), ("pot1", 5.5), ("pot2", 34.1), ("pot3", 0.15)] if device == "YogiBear": subscribed_params = [] params_and_values = [("duty", 20), ("forward", False)] while (True): if (updateTime != 0 and delay != 0): if ((time.time() - updateTime) >= (delay * 0.001)): #If the time equal to the delay has elapsed since the previous device data, send a device data with the device id and the device's subscribed params and values data = [ data_tuple for data_tuple in params_and_values if data_tuple[0] in subscribed_params ] hm.send(conn, hm.make_device_data(device_id, data)) updateTime = time.time() print("Regular data update sent from %s" % device) msg = hm.read(conn) if not msg: time.sleep(.005) continue if msg.getmessageID() in [hm.messageTypes["SubscriptionRequest"]]: #Update the delay, subscription time, and params, then send a subscription response print("Subscription request recieved") params, delay = struct.unpack("<HH", msg.getPayload()) subscribed_params = hm.decode_params(device_id, params) hm.send( conn,
from hibike import Hibike import hibike_message as hm import time import pdb import binascii h = Hibike() hm.send(h.serialToUID.values()[0][1], hm.make_sub_request(0)) # devices = h.getUIDs() # pdb.set_trace() # while 1: # val = input("enter servo value: ") # if val == -1: # break # c.writeValue(device.uid, "servo0", int(val)) # time.sleep(0.2) # print(c.getData(device.uid, "servo0"))
def __repr__(self): res = "" res += hm.uid_to_device_name(self.uid) res += ":\n" res += " %d" % self.uid res += " %s\n" % self.serial_port.port res += " %s\n" % self.data return res SERIALS = [serial.Serial(port, 115200) for port in PORTS] DEVICES = [] for s in SERIALS: hm.send(s, hm.make_ping()) while SERIALS: remaining = [] for s in SERIALS: reading = hm.blocking_read(s) if reading: params, delay, device_type, year, id = struct.unpack( "<HHHBQ", reading.get_payload()) uid = (device_type << 72) | (year << 64) | id DEVICES.append(HibikeDevice(s, params, delay, uid)) else: remaining.append(s) SERIALS = remaining print("devices:", DEVICES)
def encode_packet(packet): """ Turn a HibikeMessage into an encoded packet. """ fake_port = FakeSerialPort() hibike_message.send(fake_port, packet) return fake_port.drain()
def main(): """ Create virtual devices and send test data on them. """ parser = argparse.ArgumentParser() parser.add_argument('-d', '--device', required=True, help='device type') parser.add_argument('-p', '--port', required=True, help='serial port') parser.add_argument( '-v', '--verbose', help='print messages when sending and receiving packets', action="store_true") args = parser.parse_args() def verbose_log(fmt_string, *fmt_args): """ Log a message using a formatting string if verbosity is enabled. """ if args.verbose: print(fmt_string.format(*fmt_args)) device = args.device port = args.port verbose_log("Device {} on port {}", device, port) conn = serial.Serial(port, 115200) for device_num in hm.DEVICES: if hm.DEVICES[device_num]["name"] == device: device_id = device_num break else: raise RuntimeError("Invalid Device Name!!!") year = 1 randomness = random.randint(0, 0xFFFFFFFFFFFFFFFF) delay = 0 update_time = 0 uid = (device_id << 72) | (year << 64) | randomness # Here, the parameters and values to be sent in device datas # are set for each device type, the list of subscribed parameters is set to empty, if device == "LimitSwitch": subscribed_params = [] params_and_values = [("switch0", True), ("switch1", True), ("switch2", False), ("switch3", False)] if device == "ServoControl": subscribed_params = [] params_and_values = [("servo0", 2), ("enable0", True), ("servo1", 0), ("enable1", True), ("servo2", 5), ("enable2", True), ("servo3", 3), ("enable3", False)] if device == "Potentiometer": subscribed_params = [] params_and_values = [("pot0", 6.7), ("pot1", 5.5), ("pot2", 34.1), ("pot3", 0.15)] if device == "YogiBear": subscribed_params = [] params_and_values = [("enable", True), ("command_state", 1), ("duty_cycle", 1.0), ("pid_pos_setpoint", 2.0), ("pid_pos_kp", 3.0), ("pid_pos_ki", 4.0), ("pid_pos_kd", 5.0), ("pid_vel_setpoint", 6.0), ("pid_vel_kp", 7.0), ("pid_vel_ki", 8.0), ("pid_vel_kd", 9.0), ("current_thresh", 10.0), ("enc_pos", 11.0), ("enc_vel", 12.0), ("motor_current", 13.0), ("deadband", 0.5)] if device == "RFID": subscribed_params = [] params_and_values = [("id", 0), ("tag_detect", 0)] if device == "BatteryBuzzer": subscribed_params = [] params_and_values = [("is_unsafe", False), ("calibrated", True), ("v_cell1", 11.0), ("v_cell2", 11.0), ("v_cell3", 11.0), ("v_batt", 11.0), ("dv_cell2", 11.0), ("dv_cell3", 11.0)] if device == "LineFollower": subscribed_params = [] params_and_values = [("left", 1.0), ("center", 1.0), ("right", 1.0)] while True: if update_time != 0 and delay != 0: if time.time() - update_time >= delay * 0.001: # If the time equal to the delay has elapsed since the previous device data, # send a device data with the device id # and the device's subscribed params and values data = [] for data_tuple in params_and_values: if data_tuple[0] in subscribed_params and hm.readable( device_id, data_tuple[0]): data.append(data_tuple) hm.send(conn, hm.make_device_data(device_id, data)) update_time = time.time() verbose_log("Regular data update sent from {}", device) msg = hm.read(conn) if not msg: time.sleep(.005) continue if msg.get_message_id() in [hm.MESSAGE_TYPES["SubscriptionRequest"]]: # Update the delay, subscription time, # and params, then send a subscription response verbose_log("Subscription request received") params, delay = struct.unpack("<HH", msg.get_payload()) subscribed_params = hm.decode_params(device_id, params) hm.send( conn, hm.make_subscription_response(device_id, subscribed_params, delay, uid)) update_time = time.time() if msg.get_message_id() in [hm.MESSAGE_TYPES["Ping"]]: # Send a subscription response verbose_log("Ping received") hm.send( conn, hm.make_subscription_response(device_id, subscribed_params, delay, uid)) if msg.get_message_id() in [hm.MESSAGE_TYPES["DeviceRead"]]: # Send a device data with the requested param and value tuples verbose_log("Device read received") params = struct.unpack("<H", msg.get_payload()) read_params = hm.decode_params(device_id, params) read_data = [] for data_tuple in params_and_values: if data_tuple[0] in read_params: if not hm.readable(device_id, data_tuple[0]): # Raise a syntax error if one of the values to be read is not readable raise SyntaxError( "Attempted to read an unreadable value") read_data.append(data_tuple) hm.send(conn, hm.make_device_data(device_id, read_data)) if msg.get_message_id() in [hm.MESSAGE_TYPES["DeviceWrite"]]: # Write to requested parameters # and return the values of the parameters written to using a device data verbose_log("Device write received") write_params_and_values = hm.decode_device_write(msg, device_id) write_params = [ param_val[0] for param_val in write_params_and_values ] value_types = [ hm.PARAM_MAP[device_id][name][1] for name in write_params ] write_tuples = [] # pylint: disable=consider-using-enumerate for index in range(len(write_params)): write_tuples.append( (write_params[index], write_params_and_values[index][1])) for new_tuple in write_tuples: if not hm.writable(device_id, new_tuple[0]): # Raise a syntax error if the value # that the message attempted to write to is not writable raise SyntaxError( "Attempted to write to an unwritable value") params_and_values[hm.PARAM_MAP[device_id][new_tuple[0]] [0]] = new_tuple # Send the written data, make sure you only send data for readable parameters index = 0 while index < len(write_params): if hm.readable(device_id, write_tuples[index][0]): index += 1 else: del write_tuples[index] hm.send(conn, hm.make_device_data(device_id, write_tuples))
def device_write_thread(ser, instr_queue): """ Send packets to SER based on instructions from INSTR_QUEUE. """ try: while True: instruction, args = instr_queue.get() if instruction == "ping": hm.send(ser, hm.make_ping()) elif instruction == "subscribe": uid, delay, params = args hm.send( ser, hm.make_subscription_request(hm.uid_to_device_id(uid), params, delay)) elif instruction == "read": uid, params = args hm.send(ser, hm.make_device_read(hm.uid_to_device_id(uid), params)) elif instruction == "write": uid, params_and_values = args hm.send( ser, hm.make_device_write(hm.uid_to_device_id(uid), params_and_values)) elif instruction == "disable": hm.send(ser, hm.make_disable()) elif instruction == "heartResp": uid = args[0] hm.send(ser, hm.make_heartbeat_response()) except serial.SerialException: # Device has disconnected pass
def _process_ping(self, msg): """Respond to a ping packet.""" self.verbose_log("Ping received") dev_id = hm.uid_to_device_id(self.uid) hm.send(self.transport, hm.make_subscription_response(dev_id, [], 0, self.uid))
async def send_messages(self): """ Send messages in the queue to the sensor. """ await self._ready.wait() while not self.transport.is_closing(): instruction, args = await self.write_queue.get() if instruction == "ping": hm.send(self.transport, hm.make_ping()) elif instruction == "subscribe": uid, delay, params = args hm.send( self.transport, hm.make_subscription_request(hm.uid_to_device_id(uid), params, delay)) elif instruction == "read": uid, params = args hm.send(self.transport, hm.make_device_read(hm.uid_to_device_id(uid), params)) elif instruction == "write": uid, params_and_values = args hm.send( self.transport, hm.make_device_write(hm.uid_to_device_id(uid), params_and_values)) elif instruction == "disable": hm.send(self.transport, hm.make_disable()) elif instruction == "heartResp": uid = args[0] hm.send(self.transport, hm.make_heartbeat_response(self.read_queue.qsize()))