Added 2016-2017 rover code, and a ROB assignment

This commit is contained in:
2017-11-29 12:46:14 -08:00
parent 4566d98b5f
commit cb3ce5dafc
1414 changed files with 5874 additions and 0 deletions

View File

@@ -0,0 +1,156 @@
"""
This file contains the live logs page sub-class
"""
#####################################
# Imports
#####################################
# Python native imports
from PyQt5 import QtCore, QtWidgets, QtGui
import logging
from inputs import devices, GamePad
import time
#####################################
# Global Variables
#####################################
GAME_CONTROLLER_NAME = "FrSky FrSky Taranis Joystick"
CONTROLLER_DATA_UPDATE_FREQUENCY = 50 # Times per second
#####################################
# Controller Class Definition
#####################################
class FreeSkyController(QtCore.QThread):
# ########## Signals ##########
controller_connection_aquired = QtCore.pyqtSignal(bool)
controller_update_ready_signal = QtCore.pyqtSignal()
def __init__(self, main_window):
super(FreeSkyController, self).__init__()
# ########## Reference to top level window ##########
self.main_window = main_window # type: QtWidgets.QMainWindow
# ########## Get the settings instance ##########
self.settings = QtCore.QSettings()
# ########## Get the Pick And Plate instance of the logger ##########
self.logger = logging.getLogger("RoverBaseStation")
# ########## Thread Flags ##########
self.run_thread_flag = True
self.setup_controller_flag = True
self.data_acquisition_and_broadcast_flag = True
self.controller_aquired = False
# ########## Class Variables ##########
self.gamepad = None # type: GamePad
self.controller_states = {
"left_stick_x_axis": 0,
"left_stick_y_axis": 0,
"right_stick_x_axis": 0,
"right_stick_y_axis": 0,
"sa_state": 0,
"sb_state": 0,
"sc_state": 0,
"sd_state": 0,
"se_state": 0,
"sf_state": 0,
"sg_state": 0,
"sh_state": 0,
"ls_axis": 0,
"rs_axis": 0,
"s1_axis": 0,
"s2_axis": 0
}
self.raw_mapping_to_class_mapping = {
"ABS_RX": "left_stick_x_axis",
"ABS_X": "left_stick_y_axis",
"ABS_Z": "right_stick_x_axis",
"ABS_Y": "right_stick_y_axis",
"BTN_SOUTH": "sa_state",
"BTN_EAST": "sb_state",
"BTN_C": "sc_state",
"BTN_NORTH": "sd_state",
"BTN_WEST": "se_state",
"BTN_Z": "sf_state",
"BTN_TL": "sg_state",
"BTN_TR": "sh_state",
"ABS_RY": "ls_axis",
"ABS_RUDDER": "rs_axis",
"ABS_RZ": "s1_axis",
"ABS_THROTTLE" : "s2_axis"
}
self.last_time = time.time()
def run(self):
self.logger.debug("FreeSky Thread Starting...")
while self.run_thread_flag:
if self.setup_controller_flag:
self.controller_aquired = self.__setup_controller()
self.setup_controller_flag = False
if self.data_acquisition_and_broadcast_flag:
self.__get_controller_data()
self.__broadcast_if_ready()
self.logger.debug("FreeSky Thread Stopping...")
# noinspection PyUnresolvedReferences
def connect_signals_to_slots__slot(self):
self.main_window.kill_threads_signal.connect(self.on_kill_threads__slot)
def __setup_controller(self):
for device in devices.gamepads:
if device.name == GAME_CONTROLLER_NAME:
self.gamepad = device
self.controller_connection_aquired.emit(True)
return True
self.logger.info("FrySky Failed to Connect")
self.controller_connection_aquired.emit(False)
return False
def __get_controller_data(self):
if self.controller_aquired:
events = self.gamepad.read()
# self.logger.debug(events[0].manager)
for event in events:
if event.code in self.raw_mapping_to_class_mapping:
self.controller_states[self.raw_mapping_to_class_mapping[event.code]] = event.state
# self.logger.debug(event.state)
# if event.code not in self.raw_mapping_to_class_mapping and event.code != "SYN_REPORT":
# self.logger.debug(str(event.code) + " : " + str(event.state))
#
# if event.code == "ABS_RUDDER":
# self.logger.debug(str(event.code) + " : " + str(event.state))
# self.logger.debug("Frksy: " + str(self.controller_states))
def __broadcast_if_ready(self):
current_time = time.time()
if (current_time - self.last_time) > (1/CONTROLLER_DATA_UPDATE_FREQUENCY):
self.controller_update_ready_signal.emit()
self.last_time = current_time
# self.logger.debug(self.controller_states)
def on_kill_threads__slot(self):
self.terminate() # DON'T normally do this, but fine in this instance
self.run_thread_flag = False

View File

@@ -0,0 +1,130 @@
"""
This file contains the logging core sub-class
"""
#####################################
# Imports
#####################################
# Python native imports
from PyQt5 import QtCore
from os import makedirs, rename, walk, unlink
from os.path import exists, getmtime
import logging
from datetime import datetime
#####################################
# Global Variables
#####################################
MAX_NUM_LOG_FILES = 30
#####################################
# Logger Definition
#####################################
class Logger(QtCore.QObject):
def __init__(self, console_output=True):
super(Logger, self).__init__()
# ########## Local class variables ##########
self.console_output = console_output
# ########## Get the settings instance ##########
self.settings = QtCore.QSettings()
# # ########## Get the Pick And Plate instance of the logger ##########
self.logger = logging.getLogger("RoverBaseStation")
# ########## Set variables with useful paths ##########
self.appdata_base_directory = self.settings.value("appdata_directory", type=str)
self.log_directory = self.appdata_base_directory + "/logs"
self.log_file_path = self.log_directory + "/log.txt"
# ########## Cleanup old log files ##########
self.__cleanup_log_files()
# ########## Set up logger with desired settings ##########
self.__setup_logger()
# ########## Place divider in log file to see new program launch ##########
self.__add_startup_log_buffer_text()
def __setup_logger(self):
# Get the appdata directory and make the log path if it doesn't exist
if not exists(self.log_directory):
makedirs(self.log_directory)
# Set the debugging level
self.logger.setLevel(logging.DEBUG)
# Make a formatter with the log line format wanted
formatter = logging.Formatter(fmt='%(levelname)s : %(asctime)s : %(message)s', datefmt='%m/%d/%y %H:%M:%S')
# Set up a file handler so everything can be saved and attach it to the logger
file_handler = logging.FileHandler(filename=self.log_file_path)
file_handler.setFormatter(formatter)
file_handler.setLevel(logging.DEBUG)
self.logger.addHandler(file_handler)
# Enable console output if requested
if self.console_output:
console_handler = logging.StreamHandler()
console_handler.setFormatter(formatter)
console_handler.setLevel(logging.DEBUG)
self.logger.addHandler(console_handler)
def __cleanup_log_files(self):
# This copies the existing log.txt file to an old version with a datetime stamp
# It then checks if there are too many log files, and if so, deletes the oldest
if exists(self.log_directory):
# Get the number of log files
num_log_files = self.__get_num_files_in_directory(self.log_directory)
# Check that we actually have log files
if num_log_files > 0:
date_time = datetime.now().strftime("%Y%m%d-%H%M%S")
# If we do, move the current logfile to a backup in the format old_log_datetime
if exists(self.log_file_path):
rename(self.log_file_path, self.log_directory + "/old_log_" + date_time + ".txt")
# If we have more than the max log files delete the oldest one
if num_log_files >= MAX_NUM_LOG_FILES:
unlink(self.__get_name_of_oldest_file(self.log_directory))
def __add_startup_log_buffer_text(self):
# Prints a header saying when the program started
self.logger.info("########## Application Starting ##########")
@staticmethod
def __get_name_of_oldest_file(input_path):
oldest_file_path = None
previous_oldest_time = 0
# Walk the directory passed in to get all folders and files
for dir_path, dir_names, file_names in walk(input_path):
# Go through all of the filenames found
for file in file_names:
# Recreate the full path and get the modified time of the file
current_path = dir_path + "/" + file
time = getmtime(current_path)
# Default case for if the variables above have not been initially set
if previous_oldest_time == 0:
previous_oldest_time = time
oldest_file_path = current_path
# Saves the oldest time and file path of the current file if it's older (lower timestamp) than the
# last file saved in the variables
if time < previous_oldest_time:
previous_oldest_time = time
oldest_file_path = current_path
# Returns the path to the oldest file after checking all the files
return oldest_file_path
@staticmethod
def __get_num_files_in_directory(input_path):
# Walk the directory passed in to get all the files
for _, _, file_names in walk(input_path):
# Return the number of files found in the directory
return len(file_names)

View File

@@ -0,0 +1,247 @@
"""
This file contains the live logs page sub-class
"""
#####################################
# Imports
#####################################
# Python native imports
from PyQt5 import QtCore, QtWidgets, QtGui
from Resources.Libraries.goompy import GooMPy
from PIL.ImageQt import ImageQt
from PIL import ImageDraw
import logging
#####################################
# Global Variables
#####################################
INITIAL_LATITUDE = 38.4064262
INITIAL_LONGITUDE = -110.794115
INITIAL_ZOOM = 18 # 15,16, and 18 18 seems best
MAPTYPE = 'satellite'
POSSIBLE_ZOOM_LEVELS = [18]
WIDTH = 2 * 640
HEIGHT = 640
HAB_X_OFFSET = 82
HAB_Y_OFFSET = -9
#####################################
# Live Logs Class Definition
#####################################
class MapProcessor(QtCore.QThread):
update_map_label_signal = QtCore.pyqtSignal()
def __init__(self, main_window):
super(MapProcessor, self).__init__()
# ########## Reference to top level window ##########
self.main_window = main_window # type: QtWidgets.QMainWindow
# ########## Get the settings instance ##########
self.settings = QtCore.QSettings()
# ########## Get the Pick And Plate instance of the logger ##########
self.logger = logging.getLogger("RoverBaseStation")
# ########## Thread Flags ##########
self.run_thread_flag = True
self.setup_goompy_flag = True
# ########## References to GUI Elements ##########
self.map_label = self.main_window.map_label # type: QtWidgets.QLabel
# ########## Class Variables ##########
self.goompy = None
self.pillow = None
self.pixmap = QtGui.QPixmap("Resources/Images/waiting.png")
self.gps_valid = None
self.lat = None
self.lon = None
self.alt = None # {'gps_pos_valid': 1, 'altitude': 21052, 'longitude': -668740150, 'latitude': 229797144}
self.gps_track_valid = None
self.gps_speed = None
self.gps_heading = None
self.current_zoom_index = POSSIBLE_ZOOM_LEVELS.index(INITIAL_ZOOM)
self.last_zoom_level_index = self.current_zoom_index
self.num_zoom_levels = len(POSSIBLE_ZOOM_LEVELS)
# ########## Gui Event Variables ##########
self.x_drag = 0
self.y_drag = 0
def run(self):
self.logger.debug("Map Processor Thread Starting...")
self.update_map_label_signal.emit()
while self.run_thread_flag:
if self.setup_goompy_flag:
self.__setup_goompy()
self.setup_goompy_flag = False
else:
self.__get_goompy_pillow_image()
#self.__add_map_point_from_gps(38.406404, -110.783481)
self.__show_map_plus_overlays()
self.msleep(1)
self.logger.debug("Map Processor Thread Stopping...")
def __setup_goompy(self):
self.goompy = GooMPy(WIDTH, HEIGHT, INITIAL_LATITUDE, INITIAL_LONGITUDE, INITIAL_ZOOM, MAPTYPE, 1500)
self.goompy.move(HAB_X_OFFSET, HAB_Y_OFFSET)
def __get_goompy_pillow_image(self):
if self.last_zoom_level_index != self.current_zoom_index:
self.logger.debug("Changed to zoom level: " + str(POSSIBLE_ZOOM_LEVELS[self.current_zoom_index]))
self.goompy.useZoom(POSSIBLE_ZOOM_LEVELS[self.current_zoom_index])
self.last_zoom_level_index = self.current_zoom_index
self.pillow = self.goompy.getImage().convert("RGBA")
def __show_map_plus_overlays(self):
self.__draw_circle_at_point(self.pillow, self.goompy.width // 2, self.goompy.height // 2, 3)
qim = ImageQt(self.pillow)
self.pixmap = QtGui.QPixmap.fromImage(qim)
self.update_map_label_signal.emit()
def __add_map_point_from_gps(self, lat, lon):
self.logger.debug("Beginning")
if self.goompy:
viewport_lat_nw, viewport_lon_nw = self.goompy.northwest
viewport_lat_se, viewport_lon_se = self.goompy.southeast
viewport_lat_diff = viewport_lat_nw - viewport_lat_se
viewport_lon_diff = viewport_lon_se - viewport_lon_nw
viewport_upper_y = self.goompy.uppery
viewport_left_x = self.goompy.leftx
viewport_width = self.goompy.width
viewport_height = self.goompy.height
bigimage_width = self.goompy.bigimage.size[0]
bigimage_height = self.goompy.bigimage.size[1]
pixel_per_lat = bigimage_height / viewport_lat_diff
pixel_per_lon = bigimage_width / viewport_lon_diff
#
# viewport_left_percentage = viewport_left_x / bigimage_width
# viewport_right_percentage = (viewport_left_x + viewport_width) / bigimage_width
# viewport_top_percentage = viewport_upper_y / bigimage_height
# viewport_bottom_percentage = (viewport_upper_y + viewport_height) / bigimage_height
#
new_lat_gps_range_percentage = (viewport_lat_nw - lat)
new_lon_gps_range_percentage = (lon - viewport_lon_nw)
point_x = new_lon_gps_range_percentage * pixel_per_lon
point_y = new_lat_gps_range_percentage * pixel_per_lat
self.__draw_circle_at_point(self.goompy.bigimage, int(point_x), int(point_y), 3)
#
# self.logger.debug("vlp: " + str(viewport_left_percentage) + " nlatp: " + str(new_lon_gps_range_percentage) + " vrp: " + str(viewport_right_percentage))
# self.logger.debug("vtp: " + str(viewport_top_percentage) + " nlonp: " + str(new_lat_gps_range_percentage) + " vbp: " + str(viewport_bottom_percentage))
#
# x = new_lon_gps_range_percentage * bigimage_width
# y = new_lat_gps_range_percentage * bigimage_height
#
# self.__draw_circle_at_point(self.goompy.bigimage, x, y, 3)
#
# if viewport_left_percentage < new_lon_gps_range_percentage < viewport_right_percentage and \
# viewport_top_percentage < new_lat_gps_range_percentage < viewport_bottom_percentage:
# self.logger.debug("valid point")
# else:
# self.logger.debug("invalid point")
self.logger.debug("Made it here")
def __draw_circle_at_point(self, image, x, y, r):
draw = ImageDraw.Draw(image)
draw.ellipse((x - r, y - r, x + r, y + r), fill=(255, 0, 0, 255))
def __on_map_label_update_requested__slot(self):
self.map_label.setPixmap(self.pixmap)
def on_gps_coordinates_updated__slot(self, sdict):
self.gps_valid = sdict["gps_pos_valid"]
self.lat = (sdict["latitude"] * 10 ** -5) / 60
self.lon = (sdict["longitude"] * 10 ** -5) / 60
self.alt = (sdict["altitude"] / 10)
if self.gps_valid:
pass
#self.logger.debug("LAT: " + str(self.lat) + " | LON: " + str(self.lon) + " | ALT: " + str(self.alt))
else:
pass
#self.logger.debug("Waiting for GPS lock")
def on_gps_track_updated__slot(self, sdict):
self.gps_track_valid = sdict["gps_track_valid"]
self.gps_speed = sdict["gps_speed"]
self.gps_heading = sdict["gps_heading"]
if self.gps_track_valid:
pass
# self.logger.debug("SPEED: " + str(self.gps_speed) + " | HEADING: " + str(self.gps_heading))
else:
pass
def __mouse_released_on_map_event(self, _):
self.x_drag = -1
self.y_drag = -1
def __mouse_scrolled_on_map(self, event):
if self.goompy:
if event.angleDelta().y() > 0:
self.current_zoom_index += 1
else:
self.current_zoom_index -= 1
self.current_zoom_index = self.clamp(self.current_zoom_index, 0, self.num_zoom_levels - 1)
def __mouse_moved_on_map_label(self, event):
if self.x_drag != -1 and self.y_drag != -1 and self.goompy:
buttons = event.buttons()
if buttons == QtCore.Qt.LeftButton:
x_pos = event.pos().x()
y_pos = event.pos().y()
dx = x_pos - self.x_drag
dy = y_pos - self.y_drag
if dy < 300 and dx < 300:
# self.logger.debug(str(dx) + " : " + str(dy))
self.goompy.move(-dx, -dy)
self.x_drag = x_pos
self.y_drag = y_pos
else:
self.x_drag = event.pos().x()
self.y_drag = event.pos().y()
else:
self.x_drag = event.pos().x()
self.y_drag = event.pos().y()
# noinspection PyUnresolvedReferences
def connect_signals_to_slots__slot(self):
self.main_window.kill_threads_signal.connect(self.on_kill_threads__slot)
self.update_map_label_signal.connect(self.__on_map_label_update_requested__slot)
self.main_window.miniboard_class.data_gps_position.connect(self.on_gps_coordinates_updated__slot)
self.main_window.miniboard_class.data_gps_track.connect(self.on_gps_track_updated__slot)
self.map_label.mouseReleaseEvent = self.__mouse_released_on_map_event
self.map_label.mouseMoveEvent = self.__mouse_moved_on_map_label
self.map_label.wheelEvent = self.__mouse_scrolled_on_map
def on_kill_threads__slot(self):
self.run_thread_flag = False
@staticmethod
def clamp(n, minn, maxn):
return max(min(maxn, n), minn)

View File

@@ -0,0 +1,305 @@
#!/usr/bin/env python3
"""
This file contains the rover communication code.
"""
import sys
import os
import serial
from PyQt5 import QtCore, QtWidgets, QtGui
import logging
import struct
import signal
import time
from serial.tools.list_ports import comports
from io import StringIO, BytesIO
import enum
# Import docparse
docparsepath = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) + "/common/"
sys.path.append(docparsepath)
import docparse
with open(docparsepath + "/SPECIFICATION.md") as p:
RoverCmdTable = docparse.extract_table(p.read())
RoverCmdDict = {cmd["code"]: cmd for cmd in RoverCmdTable}
for k in RoverCmdDict:
expected_size = 5
for a in RoverCmdDict[k]["argument"]:
if a[0] == "*":
expected_size += 256
else:
expected_size += int(a[0][1:]) // 8
RoverCmdDict[k]["expected_size"] = expected_size
# Create read/write functions
def create_funcs(module_vars, cmd_table):
"""Create functions of the form
read_<command_name>(signal)
write_<command_name>(signal, *args)
such as
read_pause(signal)
write_pause(signal, pause_state)
in the current module."""
log = logging.getLogger("Miniboard Command Functions")
def build_read_func(cmd):
"""Return a function that takes a signal (connected to MiniboardIO's send())
and emits a string of packet data."""
def f(signal):
packet_data = [cmd["code"] | 0x80]
signal.emit(packet_data)
return f
def build_write_func(cmd):
"""Return a function that takes a signal (connected to MiniboardIO's __send())
and emits a string of packet data."""
fmtcodes = {"u8": "<B", "i8": "<b", "u16": "<H", "i16": "<h", "u32": "<I", "i32": "<i", "i64": "<Q",
"i64": "<q"}
def f(signal, *args):
if len(args) != len(cmd["argument"]):
log.error("Error calling write func for command %s: expected %d arguments, got %d" % (
cmd["name"], len(args), len(cmd["arguments"])))
return
packet_data = [cmd["code"]]
for a, v in zip(cmd["argument"], args):
if a[0] == "*":
packet_data += list(v)
else:
packet_data += list(struct.pack(fmtcodes[a[0]], v))
signal.emit(packet_data)
return f
for c in cmd_table:
if "r" in c["rw"]: module_vars["read_" + docparse.cannon_name(c["name"])] = build_read_func(c)
if "w" in c["rw"]: module_vars["write_" + docparse.cannon_name(c["name"])] = build_write_func(c)
create_funcs(vars(), RoverCmdTable)
def make_signals():
"""Return a string that when eval'd in MiniboardIO
creates signals for received data from the rover.
Each signal has the name data_<canonical command name>,
such as data_battery_voltage.
Each signal emits a dictionary containing string keys of the
command's argument names."""
s = ""
for c in RoverCmdTable:
signame = "data_" + docparse.cannon_name(c["name"])
s += "%s = QtCore.pyqtSignal([dict])\n" % signame
signame = "ack_" + docparse.cannon_name(c["name"])
s += "%s = QtCore.pyqtSignal()\n" % signame
return s
signal_eval_str = make_signals()
class MiniboardIO(QtCore.QThread):
"""Handles reading and writing from the miniboard."""
path = "/dev/ttyUSB0"
# path = "COM21"
baud = 115200
on_kill_threads__slot = QtCore.pyqtSignal()
message_received_signal = QtCore.pyqtSignal()
exec(signal_eval_str)
def __init__(self, main_window):
super().__init__()
self.main_window = main_window
self.logger = logging.getLogger("RoverBaseStation")
os.system("stty -F %s -hupcl" % self.path)
com_name = comports()[0].device
if "COM" in com_name:
com_name = "COM21"
self.logger.debug("Connecting to " + com_name)
# self.tty = serial.Serial(port=com_name,
# baudrate=self.baud,
# parity=serial.PARITY_NONE,
# stopbits=serial.STOPBITS_ONE,
# bytesize=serial.EIGHTBITS)
self.run_thread_flag = True
self.queue = []
def calc_crc(self, body):
remainder = 0xFFFF
for i in range(0, len(body)):
remainder ^= body[i] << 8
remainder &= 0xFFFF
for bit in reversed(range(1, 9)):
if remainder & 0x8000:
remainder = (remainder << 1) ^ 0x1021
remainder &= 0xFFFF
else:
remainder <<= 1
remainder &= 0xFFFF
return remainder
def append(self, body_list):
"""Add a command to the queue."""
self.queue.append(body_list)
def __send(self, body_list):
"""Given a packet body list, turn it into a packet and send it to the rover."""
packet_contents = body_list
plen = len(packet_contents) + 2
crc = self.calc_crc(packet_contents)
packet_contents = [0x01] + [plen] + [crc & 0xFF] + [crc >> 8] + packet_contents
self.tty.write(packet_contents)
self.msleep((4 + len(body_list)) * .001)
def run(self):
"""Read from the serial port, recognize the command, and emit a signal."""
return
self.logger.debug("MiniboardIO Thread Starting...")
self.reply = []
fmtcodes = {"u8": "<B", "i8": "<b", "u16": "<H", "i16": "<h", "u32": "<I", "i32": "<i", "i64": "<Q",
"i64": "<q"}
waiting_for_command_reply = False
start_time = time.time()
while self.run_thread_flag:
if len(self.queue) > 0 and not waiting_for_command_reply:
body = self.queue[0]
self.__send(body)
self.queue = self.queue[1:]
if len(body) < 1:
expected_size = 1000000
else:
if body[0] & 0x80: # read
expected_size = RoverCmdDict[body[0] & 0x7F]["expected_size"]
else:
expected_size = 5
start_time = time.time()
waiting_for_command_reply = True
if waiting_for_command_reply:
if self.tty.inWaiting() >= expected_size:
waiting_for_command_reply = False
self.reply = list(self.tty.read(size=expected_size))
# n = 2
# while n > 0 and len(self.reply) < expected_size:
# self.reply += list(self.tty.read(size=1))
# n -= 1
while len(self.reply) > 0:
while len(self.reply) > 0:
if self.reply[0] != 0x01:
self.reply = self.reply[1:]
else:
break
if len(self.reply) > 0: # Got start byte
if len(self.reply) >= 5: # Got minimum complete packet
if len(self.reply) >= (self.reply[1] + 2): # Got enough bytes for this packet
if self.calc_crc(self.reply[4:]) == struct.unpack("<H", bytes(self.reply[2:4]))[0]: # CRC OK
if self.reply[4] & 0x80:
# self.logger.debug("read")
code = self.reply[4] & 0x7F
cmd = RoverCmdDict[code]
adict = {}
b = 5
vs = []
for a, i in zip(cmd["argument"], range(0, len(cmd["argument"]))):
if a[0] != "*":
s = struct.calcsize(fmtcodes[a[0]])
value = struct.unpack(fmtcodes[a[0]], bytes(self.reply[b:b + s]))[0]
adict[a[1]] = value
vs.append(value)
b += s
else:
s = vs[i - 1]
value = self.reply[b:b + s]
adict[a[1]] = value
b += s
getattr(self, "data_" + docparse.cannon_name(cmd["name"])).emit(adict)
# self.logger.debug(docparse.cannon_name + ": " + adict.__str__())
else:
code = self.reply[4] & 0x7F
cmd = RoverCmdDict[code]
getattr(self, "ack_" + docparse.cannon_name(cmd["name"])).emit()
self.reply = self.reply[(self.reply[1] + 2):]
self.message_received_signal.emit()
#self.logger.debug("MiniIO Loop: " + str(time.time() - start_time))
if time.time() - start_time > 0.3:
self.logger.error("MINIIO: Lost ACK : " + str(time.time() - start_time))
waiting_for_command_reply = False
self.queue = []
self.reply = []
self.tty.flushOutput()
self.tty.flushInput()
self.msleep(1)
self.logger.debug("MiniboardIO Thread Stopping...")
def clear_buffers_and_queues(self):
self.reply = ""
self.queue = []
self.tty.flushInput()
self.tty.flushOutput()
def connect_signals_to_slots__slot(self):
self.main_window.kill_threads_signal.connect(self.on_kill_threads__slot)
def on_kill_threads__slot(self):
self.run_thread_flag = False
class DemoThread(QtCore.QThread):
on_kill_threads__slot = QtCore.pyqtSignal()
send_packet = QtCore.pyqtSignal(list)
def __init__(self, main_window):
super().__init__()
self.main_window = main_window
self.connect_signals_to_slots()
def run(self):
while True:
write_swerve_drive_state(self.send_packet, 3)
read_swerve_drive_state(self.send_packet)
read_callsign(self.send_packet)
self.msleep(1000)
def connect_signals_to_slots(self):
self.main_window.kill_threads_signal.connect(self.on_kill_threads__slot)
self.send_packet.connect(self.main_window.m.append)
self.main_window.m.data_swerve_drive_state.connect(self.handle_swerve_data)
def handle_swerve_data(self, sdict):
pass
# self.logger.debug(sdict)
class DemoWindow(QtWidgets.QMainWindow):
kill_threads_signal = QtCore.pyqtSignal()
def __init__(self):
super().__init__()
self.m = MiniboardIO(self)
self.d = DemoThread(self)
self.m.start()
self.d.start()
if __name__ == "__main__":
signal.signal(signal.SIGINT, signal.SIG_DFL) # This allows the keyboard interrupt kill to work properly
application = QtWidgets.QApplication(sys.argv) # Create the base qt gui application
app_window = DemoWindow() # Make a window in this application
app_window.setWindowTitle("Demo Demo") # Sets the window title
app_window.show() # Show the window in the application
application.exec_() # Execute launching of the application

View File

@@ -0,0 +1,507 @@
"""
This file contains the live logs page sub-class
"""
#####################################
# Imports
#####################################
# Python native imports
from PyQt5 import QtCore, QtWidgets
import logging
from Framework.MiniBoardIOCore import write_drive_motor_power, read_drive_motor_power, write_pause, \
write_arm_motors, write_swerve_drive_state, write_joystick
import time
#####################################
# Global Variables
#####################################
JOY_MIN = -127
JOY_MAX = 127
DRIVE_MIN = -127
DRIVE_MAX = 127
ARMS_MIN = -127
ARMS_MAX = 127
DEAD_BAND_FRSKY = 20
DEAD_BAND_XBOX = 1500
DRIVE_TIMEOUT = 0.5 # Seconds
ARM_TIMEOUT = 0.25 # Seconds
PAN_TILT_TIMEOUT = 0.25 # Seconds
DRIVE_SWERVE_TIMEOUT = 5 # Seconds
#####################################
# Notes
#####################################
# ##### Xbox State Names
# "left_stick_x_axis"
# "left_stick_y_axis"
# "left_stick_center_pressed"
#
# "right_stick_x_axis"
# "right_stick_y_axis"
# "right_stick_center_pressed"
#
# "left_trigger_z_axis"
# "left_bumper_pressed"
#
# "right_trigger_z_axis"
# "right_bumper_pressed"
#
# "dpad_x"
# "dpad_y"
#
# "select_pressed"
# "start_pressed"
# "home_pressed"
#
# "a_pressed"
# "b_pressed"
# "x_pressed"
# "y_pressed"
# ##### Frsky State Names
# "left_stick_x_axis"
# "left_stick_y_axis"
#
# "right_stick_x_axis"
# "right_stick_y_axis"
#
# "sc_state"
# "sg_state"
# "se_state"
# "sa_state"
#
# "ls_axis"
# "rs_axis"
#
# "s1_axis"
#####################################
# Controller Class Definition
#####################################
class MotionProcessor(QtCore.QThread):
send_miniboard_control_packet = QtCore.pyqtSignal(list)
def __init__(self, main_window):
super(MotionProcessor, self).__init__()
# ########## Reference to top level window ##########
self.main_window = main_window # type: QtWidgets.QMainWindow
# ########## Get the settings instance ##########
self.settings = QtCore.QSettings()
# ########## Get the Pick And Plate instance of the logger ##########
self.logger = logging.getLogger("RoverBaseStation")
# ########## Thread Flags ##########
self.run_thread_flag = True
self.wait_for_drive_response = False
self.wait_for_pan_tilt_response = False
self.wait_for_arm_response = False
self.wait_for_swerve_state_response = False
self.wait_for_passthrough_response = False
# ########## Class Variables ##########
self.xbox_states = self.main_window.xbox_controller_class.controller_states
self.frsky_states = self.main_window.frsky_controller_class.controller_states
self.last_pause_state = 0
self.last_swerve_state = 0
self.last_drive_state = 0
self.last_left_drive_value = 0
self.last_right_drive_value = 0
self.pan_position = -1
self.tilt_position = -1
self.frsky_locked = False
self.xbox_locked = False
def run(self):
self.logger.debug("Motion Processor Thread Starting...")
while self.run_thread_flag:
if self.xbox_states and self.frsky_states:
start_time = time.time()
self.__send_controller_passthrough()
if not self.frsky_states["sa_state"]: # 0 is drive mode
if not self.frsky_states["se_state"]: # 0 is drive mode
# TODO: Change to send faster
pass
else: # 1 is auto mode
# TODO: Change to send slower
pass
# self.logger.debug("Control time: " + str(time.time() - start_time))
self.msleep(1)
self.logger.debug("Motion Processor Thread Stopping...")
# noinspection PyUnresolvedReferences
def connect_signals_to_slots__slot(self):
self.main_window.xbox_controller_class.controller_update_ready_signal.connect(self.on_xbox_states_updated__slot)
self.main_window.frsky_controller_class.controller_update_ready_signal.connect(
self.on_frsky_states_updated__slot)
self.send_miniboard_control_packet.connect(self.main_window.miniboard_class.append)
self.main_window.miniboard_class.data_drive_motor_power.connect(
self.on_drive_motor_power_response_received__slot)
self.main_window.miniboard_class.ack_drive_motor_power.connect(self.on_drive_response_received__slot)
# self.main_window.miniboard_class.data_pan_tilt.connect(self.on_pan_tilt_primary_position_response__slot)
# self.main_window.miniboard_class.data_pan_tilt_secondary.connect(
# self.on_pan_tilt_secondary_position_response__slot)
# self.main_window.miniboard_class.ack_pan_tilt.connect(self.on_primary_pan_tilt_write_acknowledged__slot)
self.main_window.miniboard_class.ack_arm_motors.connect(self.on_arm_motors_write_acknowledged__slot)
self.main_window.miniboard_class.ack_swerve_drive_state.connect(self.on_swerve_state_response_received__slot)
self.main_window.miniboard_class.ack_joystick.connect(self.on_passthrough_response_received__slot)
self.main_window.kill_threads_signal.connect(self.on_kill_threads__slot)
def __send_controller_passthrough(self):
# ##### Frsky #####
fr_left_horiz = int(self.clamp(self.frsky_states["left_stick_x_axis"], JOY_MIN, JOY_MAX))
fr_left_vert = int(self.clamp(self.frsky_states["left_stick_y_axis"], JOY_MIN, JOY_MAX))
fr_right_horiz = int(self.clamp(self.frsky_states["right_stick_x_axis"], JOY_MIN, JOY_MAX))
fr_right_vert = int(self.clamp(self.frsky_states["right_stick_y_axis"], JOY_MIN, JOY_MAX))
fr_left_pot = int(self.clamp(self.frsky_states["s1_axis"], JOY_MIN, JOY_MAX))
fr_right_pot = int(self.clamp(self.frsky_states["s2_axis"], JOY_MIN, JOY_MAX))
fr_left_side_pot = int(self.clamp(self.frsky_states["ls_axis"], JOY_MIN, JOY_MAX))
fr_right_side_pot = int(self.clamp(self.frsky_states["rs_axis"], JOY_MIN, JOY_MAX))
fr_button_a = self.frsky_states["sa_state"]
fr_button_b = self.frsky_states["sb_state"]
fr_button_c = self.frsky_states["sc_state"]
fr_button_d = self.frsky_states["sd_state"]
fr_button_e = self.frsky_states["se_state"]
fr_button_f = self.frsky_states["sf_state"]
fr_button_g = self.frsky_states["sg_state"]
fr_button_h = self.frsky_states["sh_state"]
frsky_buttons_byte = 0
frsky_buttons_byte |= (fr_button_a << 0)
frsky_buttons_byte |= (fr_button_b << 1)
frsky_buttons_byte |= (fr_button_c << 2)
frsky_buttons_byte |= (fr_button_d << 3)
frsky_buttons_byte |= (fr_button_e << 4)
frsky_buttons_byte |= (fr_button_f << 5)
frsky_buttons_byte |= (fr_button_g << 6)
frsky_buttons_byte |= (fr_button_h << 7)
# ##### XBOX #####
xb_left_horiz = int(self.clamp(self.xbox_states["left_stick_x_axis"] // 256, JOY_MIN, JOY_MAX))
xb_left_vert = int(self.clamp(self.xbox_states["left_stick_y_axis"] // 256, JOY_MIN, JOY_MAX))
xb_right_horiz = int(self.clamp(self.xbox_states["right_stick_x_axis"] // 256, JOY_MIN, JOY_MAX))
xb_right_vert = int(self.clamp(self.xbox_states["right_stick_y_axis"] // 256, JOY_MIN, JOY_MAX))
xb_left_trig = int(self.clamp(self.xbox_states["left_trigger_z_axis"] - 127, JOY_MIN, JOY_MAX))
xb_right_trig = int(self.clamp(self.xbox_states["right_trigger_z_axis"] - 127, JOY_MIN, JOY_MAX))
xb_button_a = self.xbox_states["a_pressed"]
xb_button_b = self.xbox_states["b_pressed"]
xb_button_x = self.xbox_states["x_pressed"]
xb_button_y = self.xbox_states["y_pressed"]
xb_button_lb = self.xbox_states["left_bumper_pressed"]
xb_button_rb = self.xbox_states["right_bumper_pressed"]
xb_button_lsc = self.xbox_states["left_stick_center_pressed"]
xb_button_rsc = self.xbox_states["right_stick_center_pressed"]
xb_button_sel = self.xbox_states["select_pressed"]
xb_button_strt = self.xbox_states["start_pressed"]
xb_button_home = self.xbox_states["home_pressed"]
xb_button_dph = self.xbox_states["dpad_x"]
xb_button_dpv = self.xbox_states["dpad_y"]
xbox_buttons_low_byte = 0
xbox_buttons_high_byte = 0
xbox_buttons_low_byte |= (xb_button_a << 0)
xbox_buttons_low_byte |= (xb_button_b << 1)
xbox_buttons_low_byte |= (xb_button_x << 2)
xbox_buttons_low_byte |= (xb_button_y << 3)
xbox_buttons_low_byte |= (xb_button_lb << 4)
xbox_buttons_low_byte |= (xb_button_rb << 5)
xbox_buttons_low_byte |= (xb_button_lsc << 6)
xbox_buttons_low_byte |= (xb_button_rsc << 7)
xbox_buttons_high_byte |= (xb_button_sel << 0)
xbox_buttons_high_byte |= (xb_button_strt << 1)
xbox_buttons_high_byte |= (xb_button_home << 2)
dpad_l_state = 0
dpad_r_state = 0
dpad_u_state = 0
dpad_d_state = 0
if xb_button_dph == -1:
dpad_l_state = 1
elif xb_button_dph == 1:
dpad_r_state = 1
if xb_button_dpv == -1:
dpad_u_state = 1
elif xb_button_dpv == 1:
dpad_d_state = 1
xbox_buttons_high_byte |= (dpad_l_state << 3)
xbox_buttons_high_byte |= (dpad_r_state << 4)
xbox_buttons_high_byte |= (dpad_u_state << 5)
xbox_buttons_high_byte |= (dpad_d_state << 6)
xbox_buttons_high_byte |= (1 << 7)
# self.logger.debug('{0:08b}'.format(xbox_buttons_high_byte) + " : " + '{0:08b}'.format(xbox_buttons_low_byte))
# current_array = [fr_left_horiz, fr_left_vert, fr_right_horiz, fr_right_vert, fr_left_pot, fr_right_pot, fr_left_side_pot, fr_right_side_pot, frsky_buttons_byte, xb_left_horiz, xb_left_vert, xb_right_horiz, xb_right_vert, xb_left_trig, xb_right_trig, xbox_buttons_high_byte, xbox_buttons_low_byte]
# desired_array = [str(number) for number in current_array]
# joined = " : ".join(desired_array)
# self.logger.debug(joined)
self.wait_for_passthrough_response = True
write_joystick(self.send_miniboard_control_packet, fr_left_horiz, fr_left_vert, fr_right_horiz, fr_right_vert, fr_left_pot, fr_right_pot, fr_left_side_pot, fr_right_side_pot, frsky_buttons_byte, xb_left_horiz, xb_left_vert, xb_right_horiz, xb_right_vert, xb_left_trig, xb_right_trig, xbox_buttons_high_byte, xbox_buttons_low_byte)
# ##### Standard timeout block #####
start_time = time.time()
time_elapsed = 0.0
while self.wait_for_passthrough_response and time_elapsed < DRIVE_TIMEOUT: # I'm being explicit here
time_elapsed = time.time() - start_time
self.msleep(1)
if time_elapsed > DRIVE_TIMEOUT:
self.logger.debug("MOTION: TOO SLOW: " + str(time_elapsed))
# ##### End standard timeout block #####
def __set_pause_on_state_change(self):
current_state = self.frsky_states['sf_state']
if current_state != self.last_pause_state:
write_pause(self.send_miniboard_control_packet, current_state)
self.last_pause_state = current_state
def __all_stop_on_arm_control(self):
current_state = self.frsky_states["sa_state"]
if (current_state == 1) and (self.last_drive_state == 0):
self.wait_for_drive_response = True
write_drive_motor_power(self.send_miniboard_control_packet, 0, 0, 0, 0,
0, 0)
# ##### Standard timeout block #####
start_time = time.time()
time_elapsed = 0
while self.wait_for_drive_response and time_elapsed < DRIVE_TIMEOUT: # I'm being explicit here
time_elapsed = time.time() - start_time
self.msleep(1)
# ##### End standard timeout block #####
while self.frsky_states["left_stick_y_axis"] > DEAD_BAND_FRSKY and self.frsky_states["right_stick_y_axis"] > DEAD_BAND_FRSKY:
self.msleep(1)
self.last_drive_state = current_state
elif (current_state == 0) and (self.last_drive_state == 1):
self.last_drive_state = 0
def __set_swerve_state_on_state_change(self):
current_state = self.frsky_states['sg_state']
self.logger.debug(str(current_state) + str(self.last_swerve_state))
if current_state != self.last_swerve_state:
self.logger.debug("State changed")
# #######################################
# ##### Then send the state change #####
# #######################################
if current_state: # 1 indicates turn to swerve, command 2
command = 2
else: # 0 indicates straight, command 1
command = 1
self.wait_for_swerve_state_response = True
write_swerve_drive_state(self.send_miniboard_control_packet, command)
# ##### Standard timeout block #####
start_time = time.time()
time_elapsed = 0
while self.wait_for_swerve_state_response and time_elapsed < DRIVE_SWERVE_TIMEOUT: # I'm being explicit here
time_elapsed = time.time() - start_time
self.msleep(1)
# ##### End standard timeout block #####
self.msleep(DRIVE_SWERVE_TIMEOUT)
self.last_swerve_state = current_state
def __arm_control_manual(self):
OFFSET = 127
max_speed_scale_raw = self.frsky_states["s1_axis"] + OFFSET
base_speed_raw = self.frsky_states["left_stick_x_axis"]
bicep_speed_raw = self.frsky_states["left_stick_y_axis"]
forearm_speed_raw = self.frsky_states["right_stick_y_axis"]
pitch_speed_raw = self.frsky_states["rs_axis"]
wrist_rotation_speed_raw = self.frsky_states["right_stick_x_axis"]
scale_percentage = max_speed_scale_raw / 255
base_speed_scaled = base_speed_raw * scale_percentage
bicep_speed_scaled = bicep_speed_raw * scale_percentage
forearm_speed_scaled = forearm_speed_raw * scale_percentage
pitch_speed_scaled = pitch_speed_raw * scale_percentage
wrist_rotation_speed_scaled = wrist_rotation_speed_raw * scale_percentage
base_speed_scaled = int(self.clamp(base_speed_scaled, ARMS_MIN, ARMS_MAX))
bicep_speed_scaled = int(self.clamp(bicep_speed_scaled, ARMS_MIN, ARMS_MAX))
forearm_speed_scaled = int(self.clamp(forearm_speed_scaled, ARMS_MIN, ARMS_MAX))
pitch_speed_scaled = int(self.clamp(pitch_speed_scaled, ARMS_MIN, ARMS_MAX))
wrist_rotation_speed_scaled = int(self.clamp(wrist_rotation_speed_scaled, ARMS_MIN, ARMS_MAX))
self.wait_for_arm_response = True
write_arm_motors(self.send_miniboard_control_packet,
base_speed_scaled,
bicep_speed_scaled,
forearm_speed_scaled,
pitch_speed_scaled,
wrist_rotation_speed_scaled)
# ##### Standard timeout block #####
start_time = time.time()
time_elapsed = 0
while self.wait_for_arm_response and time_elapsed < ARM_TIMEOUT: # I'm being explicit here
time_elapsed = time.time() - start_time
self.msleep(1)
# ##### End standard timeout block #####
# write_arm_motors(self.send_miniboard_control_packet, )
def __drive_manual(self):
OFFSET = 127
max_speed_scale_raw = self.frsky_states["s1_axis"] + OFFSET # Range (min to max) 0 to 255
left_stick_y_axis_raw = self.frsky_states["left_stick_y_axis"] # Range (min to max) -127 to 127
right_stick_y_axis_raw = self.frsky_states["right_stick_y_axis"] # Range (min to max) -127 to 127
scale_percentage = max_speed_scale_raw / 255
left_scaled = left_stick_y_axis_raw * scale_percentage
right_scaled = right_stick_y_axis_raw * scale_percentage
left_scaled = int(self.clamp(left_scaled, DRIVE_MIN, DRIVE_MAX))
right_scaled = int(self.clamp(right_scaled, DRIVE_MIN, DRIVE_MAX))
# if left_scaled != self.last_left_drive_value or right_scaled != self.last_right_drive_value:
self.wait_for_drive_response = True
write_drive_motor_power(self.send_miniboard_control_packet, left_scaled, left_scaled, left_scaled, right_scaled,
right_scaled, right_scaled)
self.logger.debug(str(left_scaled) + " : " + str(right_scaled))
# ##### Standard timeout block #####
start_time = time.time()
time_elapsed = 0
while self.wait_for_drive_response and time_elapsed < DRIVE_TIMEOUT: # I'm being explicit here
time_elapsed = time.time() - start_time
self.msleep(1)
# ##### End standard timeout block #####
# self.last_left_drive_value = left_scaled
# self.last_right_drive_value = right_scaled
# read_drive_motor_power(self.send_miniboard_control_packet)
def __drive_auto(self):
self.msleep(1)
def __pan_tilt_manual(self):
# X axis (min to max) -32768 to 32768
# Y axis (min to max) 32768 to 32768
controller_pan_raw = self.xbox_states["right_stick_x_axis"]
controller_tilt_raw = self.xbox_states["right_stick_y_axis"]
if abs(controller_pan_raw) < DEAD_BAND_XBOX:
controller_pan_raw = 0
if abs(controller_tilt_raw) < DEAD_BAND_XBOX:
controller_tilt_raw = 0
controller_pan = -controller_pan_raw/2560
controller_tilt = -controller_tilt_raw/2560
new_pan = self.clamp(int(self.pan_position+controller_pan), 0, 65535)
new_tilt = self.clamp(int(self.tilt_position+controller_tilt), 0, 65535)
self.wait_for_pan_tilt_response = True
write_pan_tilt(self.send_miniboard_control_packet, new_pan, new_tilt)
# ##### Standard timeout block #####
start_time = time.time()
time_elapsed = 0
while self.wait_for_pan_tilt_response and time_elapsed < PAN_TILT_TIMEOUT: # I'm being explicit here
time_elapsed = time.time() - start_time
self.msleep(1)
# ##### End standard timeout block #####
self.pan_position = new_pan
self.tilt_position = new_tilt
def on_xbox_states_updated__slot(self):
return
def on_frsky_states_updated__slot(self):
return
def on_primary_pan_tilt_write_acknowledged__slot(self):
self.wait_for_pan_tilt_response = False
def on_secondary_pan_tilt_write_acknowledged__slot(self):
self.wait_for_pan_tilt_response = False #TODO: make secondary
def on_pan_tilt_primary_position_response__slot(self, sdict):
try:
self.pan_position = sdict["pan"]
self.tilt_position = sdict["tilt"]
except:
pass
def on_pan_tilt_secondary_position_response__slot(self, sdict):
pass
#self.logger.debug(sdict)
def on_drive_motor_power_response_received__slot(self, sdict):
pass
#self.logger.debug(sdict)
def on_passthrough_response_received__slot(self):
self.wait_for_passthrough_response = False
def on_drive_response_received__slot(self):
self.wait_for_drive_response = False
def on_arm_motors_write_acknowledged__slot(self):
self.wait_for_arm_response = False
def on_swerve_state_response_received__slot(self):
self.wait_for_swerve_state_response = False
def on_kill_threads__slot(self):
self.run_thread_flag = False
@staticmethod
def clamp(n, minn, maxn):
return max(min(maxn, n), minn)

View File

@@ -0,0 +1,56 @@
"""
This file calls read voltage, and read drive motor power
"""
#####################################
# Imports
#####################################
# Python native imports
from PyQt5 import QtCore, QtWidgets
import logging
from Framework.MiniBoardIOCore import read_drive_motor_power, read_battery_voltage, read_arm_motors, read_gps_position, read_gps_track
import time
class ReadUpdater(QtCore.QThread):
send_miniboard_control_packet = QtCore.pyqtSignal(list)
def __init__(self, main_window):
super(ReadUpdater, self).__init__()
# ########## Reference to top level window ##########
self.main_window = main_window # type: QtWidgets.QMainWindow
# ########## Get the settings instance ##########
self.settings = QtCore.QSettings()
# ########## Get the Pick And Plate instance of the logger ##########
self.logger = logging.getLogger("RoverBaseStation")
# ########## Some Flags ##########
self.run_thread_flag = True
# ########## Class variables ##########
self.data_last_seen = time.time()
def run(self):
self.logger.debug("Read Updater Thread Starting...")
while self.run_thread_flag:
read_drive_motor_power(self.send_miniboard_control_packet)
#read_battery_voltage(self.send_miniboard_control_packet)
# read_arm_motors(self.send_miniboard_control_packet)
#read_gps_position(self.send_miniboard_control_packet)
#read_gps_track(self.send_miniboard_control_packet)
self.msleep(3000)
self.logger.debug("Read Updater Thread Stopping...")
def connect_signals_to_slots__slot(self):
self.send_miniboard_control_packet.connect(self.main_window.miniboard_class.append)
self.main_window.kill_threads_signal.connect(self.on_kill_threads__slot)
def on_kill_threads__slot(self):
self.run_thread_flag = False

View File

@@ -0,0 +1,56 @@
"""
This file contains the Settings sub-class as part of the Framework Class
This class handles initialization of system settings and handling defaults when no settings are found
"""
#####################################
# Imports
#####################################
# Python native imports
from PyQt5 import QtCore, QtWidgets
import os
# Custom imports
#####################################
# Global Variables
#####################################
#####################################
# PickAndPlateLogger Definition
#####################################
class Settings(QtCore.QObject):
def __init__(self, main_window):
super(Settings, self).__init__()
# ########## Reference to highest level window ##########
self.main_window = main_window # type: QtWidgets.QMainWindow
# ########## Set up settings for program ##########
self.__setup_settings()
# ########## Create Instance of settings ##########
self.settings = QtCore.QSettings()
# ########## Create Instance of settings ##########
self.__set_hardcoded_settings()
@staticmethod
def __setup_settings():
# noinspection PyCallByClass,PyTypeChecker,PyArgumentList
QtCore.QCoreApplication.setOrganizationName("OSURC")
# noinspection PyCallByClass,PyTypeChecker,PyArgumentList
QtCore.QCoreApplication.setOrganizationDomain("http://osurobotics.club")
# noinspection PyCallByClass,PyTypeChecker,PyArgumentList
QtCore.QCoreApplication.setApplicationName("RoverBaseStation")
def __set_hardcoded_settings(self):
# Set the temporary directory used to store files while processing them
try:
app_data_dir = os.environ["HOME"]
except:
app_data_dir = os.environ["APPDATA"]
folder_name = "basestation"
full_path = app_data_dir + "/" + folder_name + "/settings"
self.settings.setValue("appdata_directory", full_path)

View File

@@ -0,0 +1,158 @@
"""
This file contains the live logs page sub-class
"""
#####################################
# Imports
#####################################
# Python native imports
from PyQt5 import QtCore, QtWidgets, QtGui
import logging
from inputs import devices, GamePad
import time
#####################################
# Global Variables
#####################################
GAME_CONTROLLER_NAME = "Afterglow Gamepad for Xbox 360"
CONTROLLER_DATA_UPDATE_FREQUENCY = 50 # Times per second
#####################################
# Controller Class Definition
#####################################
class XBOXController(QtCore.QThread):
# ########## Signals ##########
controller_connection_aquired = QtCore.pyqtSignal(bool)
controller_update_ready_signal = QtCore.pyqtSignal()
def __init__(self, main_window):
super(XBOXController, self).__init__()
# ########## Reference to top level window ##########
self.main_window = main_window # type: QtWidgets.QMainWindow
# ########## Get the settings instance ##########
self.settings = QtCore.QSettings()
# ########## Get the Pick And Plate instance of the logger ##########
self.logger = logging.getLogger("RoverBaseStation")
# ########## Thread Flags ##########
self.run_thread_flag = True
self.setup_controller_flag = True
self.data_acquisition_and_broadcast_flag = True
self.controller_aquired = False
# ########## Class Variables ##########
self.gamepad = None # type: GamePad
self.controller_states = {
"left_stick_x_axis": 0,
"left_stick_y_axis": 0,
"left_stick_center_pressed": 0,
"right_stick_x_axis": 0,
"right_stick_y_axis": 0,
"right_stick_center_pressed": 0,
"left_trigger_z_axis": 0,
"left_bumper_pressed": 0,
"right_trigger_z_axis": 0,
"right_bumper_pressed": 0,
"dpad_x": 0,
"dpad_y": 0,
"select_pressed": 0,
"start_pressed": 0,
"home_pressed": 0,
"a_pressed": 0,
"b_pressed": 0,
"x_pressed": 0,
"y_pressed": 0
}
self.raw_mapping_to_class_mapping = {
"ABS_X": "left_stick_x_axis",
"ABS_Y": "left_stick_y_axis",
"BTN_THUMBL": "left_stick_center_pressed",
"ABS_RX": "right_stick_x_axis",
"ABS_RY": "right_stick_y_axis",
"BTN_THUMBR": "right_stick_center_pressed",
"ABS_Z": "left_trigger_z_axis",
"BTN_TL": "left_bumper_pressed",
"ABS_RZ": "right_trigger_z_axis",
"BTN_TR": "right_bumper_pressed",
"ABS_HAT0X": "dpad_x",
"ABS_HAT0Y": "dpad_y",
"BTN_SELECT": "select_pressed",
"BTN_START": "start_pressed",
"BTN_MODE": "home_pressed",
"BTN_SOUTH": "a_pressed",
"BTN_EAST": "b_pressed",
"BTN_NORTH": "x_pressed",
"BTN_WEST": "y_pressed"
}
self.last_time = time.time()
def run(self):
self.logger.debug("Xbox Thread Starting...")
while self.run_thread_flag:
if self.setup_controller_flag:
self.controller_aquired = self.__setup_controller()
self.setup_controller_flag = False
if self.data_acquisition_and_broadcast_flag:
self.__get_controller_data()
self.__broadcast_if_ready()
self.logger.debug("Xbox Thread Stopping...")
# noinspection PyUnresolvedReferences
def connect_signals_to_slots__slot(self):
self.main_window.kill_threads_signal.connect(self.on_kill_threads__slot)
def __setup_controller(self):
for device in devices.gamepads:
if device.name == GAME_CONTROLLER_NAME:
self.gamepad = device
self.controller_connection_aquired.emit(True)
return True
self.logger.info("XBOX Controller Failed to Connect")
self.controller_connection_aquired.emit(False)
return False
def __get_controller_data(self):
if (self.controller_aquired):
events = self.gamepad.read()
for event in events:
if event.code in self.raw_mapping_to_class_mapping:
self.controller_states[self.raw_mapping_to_class_mapping[event.code]] = event.state
# self.logger.debug("XBOX: " + str(self.controller_states))
def __broadcast_if_ready(self):
current_time = time.time()
if (current_time - self.last_time) > (1/CONTROLLER_DATA_UPDATE_FREQUENCY):
self.controller_update_ready_signal.emit()
self.last_time = current_time
def on_kill_threads__slot(self):
self.terminate() # DON'T normally do this!!!!!
self.run_thread_flag = False