class SearchableTestAdapter: def __init__(self): self.id = None # type: Optional[str] self.start_timestamp = None # type: Optional[int] self.software_version = None # type: Optional[str] self.log_level = None # type: Optional[str] self.is_shutdown = False # type: bool self.fclient = FormantClient(ignore_unavailable=True) # listen for commands self.fclient.register_command_request_callback( self.issue_command, command_filter=COMMAND_SCRIPT_MAPPING.keys()) # listen for changes to Formant application configuration self.update_app_config() self.fclient.register_config_update_callback(self.update_app_config) # idly spin while the command request callback listens for commands while not self.is_shutdown: time.sleep(1.0) def __del__(self): self.is_shutdown = True def update_app_config(self): self.software_version = self.fclient.get_app_config( "software_version", None) # add defaults in place of None self.log_level = self.fclient.get_app_config("log_level", None) def reset_session(self): # Searchable session events via uuid naming scheme, e.g. "R473b9f9ada91" self.id = "R" + "".join(str(uuid.uuid4()).split("-")[0:2]) self.start_time = 1000 * int(time.time()) def run_script(self, path): if os.path.exists(path): subprocess.run("bash " + path, shell=True, check=True) def issue_command(self, request): if request.command == "start_recording": self.reset_session() elif (request.command == "stop_recording" and self.id is not None and self.start_time is not None): self.fclient.create_event( self.id + ": Recording session", timestamp=self.start_time, end_timestamp=1000 * int(time.time()), ) self.id = None self.start_time = None success = True try: self.run_script(COMMAND_SCRIPT_MAPPING.get(request.command)) except Exception: success = False self.fclient.send_command_response(request.id, success=success)
def ingest_with_ignore_exceptions(): fclient = FormantClient(ignore_throttled=True, enable_logging=False) print( "Sending datapoints at a rate higher than the default stream throttle rate." ) print("Ignoring all throttled exceptions.") for _ in range(50): time.sleep(0.1) fclient.post_numeric("example.numeric", _) print("Complete.\n")
def ingest_with_exception_handling(): fclient = FormantClient(enable_logging=False) print( "Sending datapoints at a rate higher than the default stream throttle rate." ) print("Catching all throttled exceptions.") count = 0 for _ in range(50): time.sleep(0.1) try: fclient.post_numeric("example.numeric", _) except Throttled: count += 1 print("Throttled " + str(count) + " datapoints", end="\r") print("\nComplete.\n")
class FormantExampleNode: def __init__(self): """ Integration with Formant agent """ rospy.init_node("formant_example_node") # Create ROS subscribers self._subscriptions = [ rospy.Subscriber( "/joint_states", JointState, self._joint_states_callback, queue_size=10, ), ] # Ignore throttled or agent unavailable exceptions self._formant_client = FormantClient( agent_url="localhost:5501", ignore_throttled=True, ignore_unavailable=True ) rospy.Timer(rospy.Duration(1), self._capture_state) rospy.spin() def _joint_states_callback(self, msg): """ Integration with sensor_msgs/JointState. The turtlebot has two joints 'wheel_right_joint' and 'wheel_left_joint'. This function parses joint states into Formant Numeric streams which are sent to the agent. """ joint_state_positive = {} joint_range = range(len(msg.name)) for i in joint_range: # for each joint post to the numeric stream "wheel_joint_position" # with a tag for the joint self._formant_client.post_numeric( "wheel_joint_position", msg.position[i], tags={"joint": msg.name[i]} ) # set the state for each joint in the dict joint_state_positive[msg.name[i]] = msg.position[i] > 0 # send the joint state self._formant_client.post_bitset( "wheel_joint_position_state_positive", joint_state_positive ) def _capture_state(self, event=None): """ Use a timed callback method for data not available through topic callbacks, or data that needs to be polled periodically. """ # send the system state on a text stream self._formant_client.post_text("system_state.mode", "RUNNING") # send a bitset of the system state self._formant_client.post_bitset( "system_state", {"RUNNING": True, "STOPPED": False, "ERROR": False, "UNKNOWN": False}, )
def __init__(self): print("INFO: Starting Formant JetBot Adapter") # Set global params self.max_speed = DEFAULT_MAX_SPEED self.min_speed = DEFAULT_MIN_SPEED self.speed_deadzone = DEFAULT_SPEED_DEADZONE self.speed_increment = DEFAULT_SPEED_INCREMENT self.angular_reduction = DEFAULT_ANGULAR_REDUCTION self.latitude = DEFAULT_LATITUDE self.longitude = DEFAULT_LONGITUDE self.gst_string = DEFAULT_GST_STRING self.start_speed = DEFAULT_START_SPEED self.speed = self.start_speed self.is_shutdown = False # Store frame rate information to publish self.camera_width = 0 self.camera_height = 0 self.camera_frame_timestamps = collections.deque([], maxlen=100) self.camera_frame_sizes = collections.deque([], maxlen=100) # Create clients self.robot = Robot() self.ina219 = INA219(addr=0x41) self.fclient = FormantClient(ignore_throttled=True, ignore_unavailable=True) self.update_from_app_config() self.publish_online_event() self.fclient.register_command_request_callback( self.handle_command_request) self.fclient.register_teleop_callback(self.handle_teleop, ["Joystick", "Buttons"]) print("INFO: Starting speed thread") threading.Thread(target=self.publish_speed, daemon=True).start() print("INFO: Starting motor states thread") threading.Thread(target=self.publish_motor_states, daemon=True).start() print("INFO: Starting location thread") threading.Thread(target=self.publish_location, daemon=True).start() print("INFO: Starting battery state thread") threading.Thread(target=self.publish_battery_state, daemon=True).start() print("INFO: Starting camera stats thread") threading.Thread(target=self.publish_camera_stats, daemon=True).start() # Start the camera feed self.publish_camera_feed()
def __init__(self): """ Integration with Formant agent """ rospy.init_node("formant_example_node") # Create ROS subscribers self._subscriptions = [ rospy.Subscriber( "/joint_states", JointState, self._joint_states_callback, queue_size=10, ), ] # Ignore throttled or agent unavailable exceptions self._formant_client = FormantClient( agent_url="localhost:5501", ignore_throttled=True, ignore_unavailable=True ) rospy.Timer(rospy.Duration(1), self._capture_state) rospy.spin()
def __init__(self): self.id = None # type: Optional[str] self.start_timestamp = None # type: Optional[int] self.software_version = None # type: Optional[str] self.log_level = None # type: Optional[str] self.is_shutdown = False # type: bool self.fclient = FormantClient(ignore_unavailable=True) # listen for commands self.fclient.register_command_request_callback( self.issue_command, command_filter=COMMAND_SCRIPT_MAPPING.keys()) # listen for changes to Formant application configuration self.update_app_config() self.fclient.register_config_update_callback(self.update_app_config) # idly spin while the command request callback listens for commands while not self.is_shutdown: time.sleep(1.0)
import cv2 from formant.sdk.agent.v1 import Client as FormantClient if __name__ == "__main__": fclient = FormantClient() image = cv2.imread("/path/to/example.jpg") encoded = cv2.imencode(".jpg", image)[1].tostring() fclient.post_image("example.image", encoded)
class FormantJetBotAdapter: def __init__(self): print("INFO: Starting Formant JetBot Adapter") # Set global params self.max_speed = DEFAULT_MAX_SPEED self.min_speed = DEFAULT_MIN_SPEED self.speed_deadzone = DEFAULT_SPEED_DEADZONE self.speed_increment = DEFAULT_SPEED_INCREMENT self.angular_reduction = DEFAULT_ANGULAR_REDUCTION self.latitude = DEFAULT_LATITUDE self.longitude = DEFAULT_LONGITUDE self.gst_string = DEFAULT_GST_STRING self.start_speed = DEFAULT_START_SPEED self.speed = self.start_speed self.is_shutdown = False # Store frame rate information to publish self.camera_width = 0 self.camera_height = 0 self.camera_frame_timestamps = collections.deque([], maxlen=100) self.camera_frame_sizes = collections.deque([], maxlen=100) # Create clients self.robot = Robot() self.ina219 = INA219(addr=0x41) self.fclient = FormantClient(ignore_throttled=True, ignore_unavailable=True) self.update_from_app_config() self.publish_online_event() self.fclient.register_command_request_callback( self.handle_command_request) self.fclient.register_teleop_callback(self.handle_teleop, ["Joystick", "Buttons"]) print("INFO: Starting speed thread") threading.Thread(target=self.publish_speed, daemon=True).start() print("INFO: Starting motor states thread") threading.Thread(target=self.publish_motor_states, daemon=True).start() print("INFO: Starting location thread") threading.Thread(target=self.publish_location, daemon=True).start() print("INFO: Starting battery state thread") threading.Thread(target=self.publish_battery_state, daemon=True).start() print("INFO: Starting camera stats thread") threading.Thread(target=self.publish_camera_stats, daemon=True).start() # Start the camera feed self.publish_camera_feed() def __del__(self): self.is_shutdown = True def publish_speed(self): while not self.is_shutdown: # self.fclient.post_numeric("speed", self.speed) self.fclient.post_numericset( "Speed", {"speed": (self.speed + self.speed_deadzone, "m/s")}, ) time.sleep(1.0) def publish_motor_states(self): while not self.is_shutdown: self.fclient.post_numeric("Motor Speed", self.robot.left_motor.value, {"value": "left"}) self.fclient.post_numeric("Motor Speed", self.robot.right_motor.value, {"value": "right"}) time.sleep(0.5) def publish_location(self): while not self.is_shutdown: self.fclient.post_geolocation("Location", self.latitude, self.longitude) time.sleep(10.0) def publish_battery_state(self): while not self.is_shutdown: bus_voltage = self.ina219.getBusVoltage_V() shunt_voltage = self.ina219.getShuntVoltage_mV() / 1000 current = self.ina219.getCurrent_mA() / 1000 charging = False if shunt_voltage > 0.01 and current > 0.01: charging = True # approximate battery charge percentage calibration now = bus_voltage - MIN_DISCHARGING_VOLTAGE full = MAX_DISCHARGING_VOLTAGE - MIN_DISCHARGING_VOLTAGE charge_percentage = now / full * 100 if charging: now = bus_voltage - MIN_CHARGING_VOLTAGE full = MAX_CHARGING_VOLTAGE - MIN_CHARGING_VOLTAGE charge_percentage = now / full * 100 if charge_percentage >= 100: charge_percentage = 100 self.fclient.post_battery("Battery State", charge_percentage, voltage=bus_voltage, current=current) self.fclient.post_bitset("Battery Charging", { "charging": charging, "discharging": not charging }) time.sleep(1.0) def publish_camera_stats(self): while not self.is_shutdown: try: timestamps = list(self.camera_frame_timestamps) sizes = list(self.camera_frame_sizes) except: print("ERROR: deque mutated while iterating") pass length = len(timestamps) if length > 2: size_mean = mean(sizes) size_stdev = stdev(sizes) jitter = self.calculate_jitter(timestamps) oldest = timestamps[0] newest = timestamps[-1] diff = newest - oldest if diff > 0: hz = length / diff self.fclient.post_numericset( "Camera Statistics", { "Rate": (hz, "Hz"), "Mean Size": (size_mean, "bytes"), "Std Dev": (size_stdev, "bytes"), "Mean Jitter": (jitter, "ms"), "Width": (self.camera_width, "pixels"), "Height": (self.camera_height, "pixels"), }, ) time.sleep(5.0) def publish_camera_feed(self): cap = cv2.VideoCapture(self.gst_string, cv2.CAP_GSTREAMER) if cap is None: print("ERROR: Could not read from video capture source.") sys.exit() while not self.is_shutdown: _, image = cap.read() try: encoded = cv2.imencode(".jpg", image)[1].tostring() self.fclient.post_image("Camera", encoded) # Track stats for publishing self.camera_frame_timestamps.append(time.time()) self.camera_frame_sizes.append(len(encoded) * 3 / 4) self.camera_width = image.shape[1] self.camera_height = image.shape[0] except: print("ERROR: encoding failed") def publish_online_event(self): try: commit_hash_file = ( "/home/jetbot/formant-jetbot-adapter/.git/refs/heads/main") with open(commit_hash_file) as f: commit_hash = f.read() except Exception: print( "ERROR: formant-jetbot-adapter repo must be installed at " "/home/jetbot/formant-jetbot-adapter to receive online event") self.fclient.create_event( "Formant JetBot adapter online", notify=False, tags={"hash": commit_hash.strip()}, ) def update_from_app_config(self): print("INFO: Updating configuration ...") self.max_speed = float( self.fclient.get_app_config("max_speed", DEFAULT_MAX_SPEED)) self.min_speed = float( self.fclient.get_app_config("min_speed", DEFAULT_MIN_SPEED)) self.speed_deadzone = float( self.fclient.get_app_config("speed_deadzone", DEFAULT_SPEED_DEADZONE)) self.speed_increment = float( self.fclient.get_app_config("speed_increment", DEFAULT_SPEED_INCREMENT)) self.angular_reduction = float( self.fclient.get_app_config("angular_reduction", DEFAULT_ANGULAR_REDUCTION)) self.latitude = float( self.fclient.get_app_config("latitude", DEFAULT_LATITUDE)) self.longitude = float( self.fclient.get_app_config("longitude", DEFAULT_LONGITUDE)) self.gst_string = self.fclient.get_app_config("gst_string", DEFAULT_GST_STRING) self.start_speed = float( self.fclient.get_app_config("start_speed", DEFAULT_START_SPEED)) def handle_command_request(self, request): if request.command == "jetbot.nudge_forward": self._handle_nudge_forward() self.fclient.send_command_response(request.id, success=True) elif request.command == "jetbot.nudge_backward": self._handle_nudge_backward() self.fclient.send_command_response(request.id, success=True) elif request.command == "jetbot.update_config": self.update_from_app_config() self.fclient.send_command_response(request.id, success=True) else: self.fclient.send_command_response(request.id, success=False) def handle_teleop(self, control_datapoint): if control_datapoint.stream == "Joystick": self.handle_joystick(control_datapoint) elif control_datapoint.stream == "Buttons": self.handle_buttons(control_datapoint) def handle_joystick(self, joystick): left_motor_value = 0.0 right_motor_value = 0.0 # Add contributions from the joysticks # TODO: turn this into a circle, not a square left_motor_value += (self.speed * joystick.twist.angular.z * self.angular_reduction) right_motor_value += (-self.speed * joystick.twist.angular.z * self.angular_reduction) left_motor_value += self.speed * joystick.twist.linear.x right_motor_value += self.speed * joystick.twist.linear.x # Improve the deadzone if left_motor_value > 0: left_motor_value += self.speed_deadzone elif left_motor_value < 0: left_motor_value -= self.speed_deadzone if right_motor_value > 0: right_motor_value += self.speed_deadzone elif right_motor_value < 0: right_motor_value -= self.speed_deadzone # Set the motor values self.robot.left_motor.value = left_motor_value self.robot.right_motor.value = right_motor_value def handle_buttons(self, _): if _.bitset.bits[0].key == "nudge forward": self._handle_nudge_forward() elif _.bitset.bits[0].key == "nudge backward": self._handle_nudge_backward() elif _.bitset.bits[0].key == "speed +": self._handle_increase_speed() elif _.bitset.bits[0].key == "speed -": self._handle_decrease_speed() def _handle_nudge_forward(self): self.fclient.post_text("Commands", "nudge forward") self.robot.forward(self.speed + self.speed_deadzone) time.sleep(0.5) self.robot.stop() def _handle_nudge_backward(self): self.fclient.post_text("Commands", "nudge backward") self.robot.backward(self.speed + self.speed_deadzone) time.sleep(0.5) self.robot.stop() def _handle_increase_speed(self): self.fclient.post_text("Commands", "increase speed") if self.speed + self.speed_increment <= self.max_speed: self.speed += self.speed_increment else: self.speed = self.max_speed def _handle_decrease_speed(self): self.fclient.post_text("Commands", "decrease speed") if self.speed - self.speed_increment >= self.min_speed: self.speed -= self.speed_increment else: self.speed = self.min_speed def calculate_jitter(self, timestamps): length = len(self.camera_frame_timestamps) oldest = self.camera_frame_timestamps[0] newest = self.camera_frame_timestamps[-1] step_value = (newest - oldest) / length # Make a list of the difference between the expected and actual step sizes jitters = [] for n in range(length - 1): if n > 0: jitter = abs((timestamps[n] - timestamps[n - 1]) - step_value) jitters.append(jitter) return mean(jitters)
import random import time import json from formant.sdk.agent.v1 import Client as FormantAgentClient def callback(message): print(json.loads(message)) if __name__ == "__main__": fclient = FormantAgentClient() fclient.register_custom_data_channel_message_callback(callback) def f(): d = { "t": time.time(), "X": [random.random() for _ in range(10)], } payload = json.dumps(d).encode("utf-8") # To send and receive on custom data channels, # any channels ("test-sdk" in this case) must be created by your custom interface. # For instance, # using the Data SDK in the toolkit (https://github.com/FormantIO/toolkit): # `await device.createCustomDataChannel("test-sdk");` fclient.send_on_custom_data_channel("test-sdk", payload)
import time import random from formant.sdk.agent.v1 import Client as FormantClient def handle_telemetry(datapoint): print("Received datapoint:", datapoint) if __name__ == "__main__": fclient = FormantClient(ignore_throttled=True, ignore_unavailable=True) # Handling data ... fclient.register_telemetry_listener_callback( handle_telemetry, stream_filter=["example.numeric", "example.numericset"]) # Post datapoints with varying types at varying intervals... while True: time.sleep(random.random() / 10.0) # wait 0.0 to 0.1 seconds between datapoints r = random.random() if r < 0.33: print("post numeric") fclient.post_numeric("example.numeric", r) elif r < 0.66: print("post text") fclient.post_text( "example.text",
def handle_destination(_): print(_.stream) print(_.timestamp) print(_.pose.translation.x) print(_.pose.rotation.w) def handle_buttons(_): print(_.stream) print(_.timestamp) print(_.bitset.bits) if __name__ == "__main__": fclient = FormantClient( ignore_throttled=True, ignore_unavailable=True, ) # Handling data ... fclient.register_teleop_callback( handle_teleop, ["Joystick", "Localization", "Buttons"] ) # Sending data ... while True: fclient.post_bitset( "Status", { "PTZ mode": True, "Walk mode": False, "Has lease": True,
from formant.sdk.agent.v1 import Client as FormantClient if __name__ == "__main__": fclient = FormantClient() # Ingest text datapoint fclient.post_text("example.text", "Send text example processed") # Ingest numeric datapoint fclient.post_numeric("example.numeric", 3.0) # Ingest numericset datapoint, 'percent' and '%' units adds # additional donut visualization fclient.post_numericset( "example.numericset2", { "frequency": (998, "Hz"), "usage": (30, "percent"), "warp factor": (6.0, None), }, ) # Ingest bitset datapoint fclient.post_bitset("example.bitset", { "standing": False, "walking": False, "sitting": True }) # Ingest geolocation datapoint fclient.post_geolocation("example.geolocation", 22.835,
import time from formant.sdk.agent.v1 import Client from formant.protos.agent.v1 import agent_pb2 fclient = Client(agent_url="unix:///tmp/agent.sock") time.sleep(0.01) sdp = input("> ") r = fclient.agent_stub.PostLanRtcOffer( agent_pb2.PostLanRtcOfferRequest(offer_sdp=sdp)) print("\n\n" + r.answer_sdp)
import time import random from formant.sdk.agent.v1 import Client as FormantClient if __name__ == "__main__": fclient = FormantClient() low_power_mode = fclient.get_app_config("low_power_mode", None) == "true" if low_power_mode: print("Entering low power mode.") cycle_period = 1.0 if low_power_mode else 0.25 while True: fclient.post_numeric("example.numeric", random.random()) time.sleep(cycle_period)
import time from formant.sdk.agent.v1 import Client as FormantAgentClient def f(heartbeat): print("Received heartbeat callback at", time.time()) print(heartbeat.is_disconnect) if __name__ == "__main__": fclient = FormantAgentClient(ignore_unavailable=True) fclient.register_teleop_heartbeat_callback(f) try: while True: time.sleep(0.1) except KeyboardInterrupt: exit()
import subprocess import time from formant.sdk.agent.v1 import Client as FormantClient SENSOR_ADDRESSES = ["192.168.1.28, 192.168.1.29"] MOTOR_ADDRESSES = [ "192.168.30.90", "192.168.30.91", "192.168.30.92", "192.168.30.93" ] fclient = FormantClient() def ping(address): # check if the address responds within 0.1 seconds shell_command = ["timeout", "0.1", "ping", "-c", "1", address] try: subprocess.check_output(shell_command) return True except (OSError, subprocess.CalledProcessError): return False def handle_command_request(request): if request.command == "sensor_check": addresses = SENSOR_ADDRESSES elif request.command == "motor_check": addresses = MOTOR_ADDRESSES else: return
import sys import cv2 from formant.sdk.agent.v1 import Client as FormantClient if __name__ == "__main__": fclient = FormantClient(ignore_throttled=True, ignore_unavailable=True) cap = cv2.VideoCapture( 0) # usb cam may be on a different index: try 1, 2 ... if cap is None: sys.exit() while True: _, image = cap.read() encoded = cv2.imencode(".jpg", image)[1].tostring() fclient.post_image("usb_cam", encoded)
from formant.sdk.agent.v1 import Client as FormantClient if __name__ == "__main__": fclient = FormantClient() fclient.create_event( "Synchronized transporter annular confinement beam to warp frequency 0.4e17 hz", notify=True, tags={"Region": "North"}, severity="warning", # one of "info", "warning", "error", "critical" ) print("Successfully created event.")