diff --git a/ControllByPs4/commands/backend/__pycache__/backend.cpython-39.pyc b/ControllByPs4/commands/backend/__pycache__/backend.cpython-39.pyc index 9099fe9..2bab237 100644 Binary files a/ControllByPs4/commands/backend/__pycache__/backend.cpython-39.pyc and b/ControllByPs4/commands/backend/__pycache__/backend.cpython-39.pyc differ diff --git a/ControllByPs4/commands/backend/backend.py b/ControllByPs4/commands/backend/backend.py index 32916a7..2bf16f8 100644 --- a/ControllByPs4/commands/backend/backend.py +++ b/ControllByPs4/commands/backend/backend.py @@ -9,17 +9,21 @@ import adsk.fusion import adsk.cam import traceback import json +from ...lib import hid CMD_NAME = 'View Controller backend' app = adsk.core.Application.get() ui = app.userInterface + +myThread = None stopFlag = None + cameraViewEvent = 'CameraViewEventId' updateConstantsEvent = "UpdateConstantsEventId" customEvents = [] -local_handlers = [] +l_handlers = [] X_ROTATION = 0 Y_ROTATION = 0 @@ -44,6 +48,7 @@ class UpdateConstantsEventHandler(adsk.core.CustomEventHandler): X_PAN = float(eventArgs["X_pan"]) Y_PAN = float(eventArgs["Y_pan"]) Z_PAN = float(eventArgs["Z_pan"]) + myThread.start() # The event handler that responds to the custom event being fired. @@ -93,9 +98,9 @@ class CameraViewEventHandler(adsk.core.CustomEventHandler): eye.transformBy(rotate_matrix) up.transformBy(rotate_matrix) - X_pan = float(eventArgs.get('X_pan', 0)) * X_pan - Y_pan = float(eventArgs.get('Y_pan', 0)) * Y_pan - Z_pan = float(eventArgs.get('Z_pan', 0)) * Z_pan + X_pan = float(eventArgs.get('X_pan', 0)) * X_PAN + Y_pan = float(eventArgs.get('Y_pan', 0)) * Y_PAN + Z_pan = float(eventArgs.get('Z_pan', 0)) * Z_PAN if any([X_pan, Z_pan]): move_vector = adsk.core.Vector3D.create(X_pan, 0, Z_pan) move_vector.transformBy(rotate_matrix) @@ -105,7 +110,7 @@ class CameraViewEventHandler(adsk.core.CustomEventHandler): if Y_pan: zoom = zoom + Y_pan * 10 - camera.isSmoothTransition = True + camera.isSmoothTransition = False camera.eye = eye camera.upVector = up camera.target = target @@ -128,13 +133,30 @@ class ControllerThread(threading.Thread): self.stopped = event def run(self): + futil.log(f'{CMD_NAME} ControllerThread started') + gamepad = hid.Device(vid=1356, pid=1476) + gamepad.nonblocking = True # Every five seconds fire a custom event, passing a random number. - while not self.stopped.wait(2): - futil.log(f'{CMD_NAME} Thread start sending data') - args = {'X_rotation': 0.0, 'Y_rotation': 0.0, 'Z_rotation': 0.0, - 'X_pan': 0.0, 'Y_pan': 0.0, 'Z_pan': 0.0} + while not self.stopped.wait(0.01): + # futil.log(f'{CMD_NAME} Thread start sending data') + report = list(gamepad.read(128)) + args = { + 'X_rotation': 0.0, + 'Y_rotation': self.convert(report[4], 10), + 'Z_rotation': self.convert(report[3], 10), + 'X_pan': self.convert(report[1], 10), + 'Y_pan': self.convert(report[2], 10), + 'Z_pan': 0.0 + } app.fireCustomEvent(cameraViewEvent, json.dumps(args)) + def convert(self, input, dedzone) -> float: + normal: float = input - 127 + if abs(normal) < dedzone: + return 0.0 + normal = normal / 128 + return normal + def start(): @@ -144,26 +166,25 @@ def start(): customEvents.append(app.registerCustomEvent(cameraViewEvent)) onCameraViewEvent = CameraViewEventHandler() customEvents[0].add(onCameraViewEvent) - local_handlers.append(onCameraViewEvent) + l_handlers.append(onCameraViewEvent) futil.log(f'{CMD_NAME} CameraViewEvent register') customEvents.append(app.registerCustomEvent(updateConstantsEvent)) onUpadateConstantEvent = UpdateConstantsEventHandler() customEvents[1].add(onUpadateConstantEvent) - local_handlers.append(onUpadateConstantEvent) + l_handlers.append(onUpadateConstantEvent) futil.log(f'{CMD_NAME} UpdateConstantsEvent register') # Create a new thread for the other processing. - global stopFlag + global stopFlag, myThread stopFlag = threading.Event() myThread = ControllerThread(stopFlag) # myThread.start() - futil.log(f'{CMD_NAME} ControllerThread started') def stop(): try: - for event, handler in zip(customEvents, local_handlers): + for event, handler in zip(customEvents, l_handlers): event.remove(handler) # if local_handlers.count: # customEvent.remove(local_handlers[0]) diff --git a/ControllByPs4/lib/hid/__init__.py b/ControllByPs4/lib/hid/__init__.py new file mode 100644 index 0000000..dc558f5 --- /dev/null +++ b/ControllByPs4/lib/hid/__init__.py @@ -0,0 +1,235 @@ +import os +import ctypes +import atexit + + +__all__ = ['HIDException', 'DeviceInfo', 'Device', 'enumerate'] + +dir = os.path.dirname(__file__).replace("/", "\\") + +hidapi = None +library_paths = ( + 'libhidapi-hidraw.so', + 'libhidapi-hidraw.so.0', + 'libhidapi-libusb.so', + 'libhidapi-libusb.so.0', + 'libhidapi-iohidmanager.so', + 'libhidapi-iohidmanager.so.0', + 'libhidapi.dylib', + 'hidapi.dll', + 'libhidapi-0.dll' +) + +for lib in library_paths: + try: + hidapi = ctypes.cdll.LoadLibrary(os.path.join(dir, lib)) + break + except OSError: + pass +else: + error = "Unable to load any of the following libraries:{}"\ + .format(' '.join(library_paths)) + raise ImportError(error) + + +hidapi.hid_init() +atexit.register(hidapi.hid_exit) + + +class HIDException(Exception): + pass + + +class DeviceInfo(ctypes.Structure): + def as_dict(self): + ret = {} + for name, type in self._fields_: + if name == 'next': + continue + ret[name] = getattr(self, name, None) + + return ret + + +DeviceInfo._fields_ = [ + ('path', ctypes.c_char_p), + ('vendor_id', ctypes.c_ushort), + ('product_id', ctypes.c_ushort), + ('serial_number', ctypes.c_wchar_p), + ('release_number', ctypes.c_ushort), + ('manufacturer_string', ctypes.c_wchar_p), + ('product_string', ctypes.c_wchar_p), + ('usage_page', ctypes.c_ushort), + ('usage', ctypes.c_ushort), + ('interface_number', ctypes.c_int), + ('next', ctypes.POINTER(DeviceInfo)), +] + +hidapi.hid_init.argtypes = [] +hidapi.hid_init.restype = ctypes.c_int +hidapi.hid_exit.argtypes = [] +hidapi.hid_exit.restype = ctypes.c_int +hidapi.hid_enumerate.argtypes = [ctypes.c_ushort, ctypes.c_ushort] +hidapi.hid_enumerate.restype = ctypes.POINTER(DeviceInfo) +hidapi.hid_free_enumeration.argtypes = [ctypes.POINTER(DeviceInfo)] +hidapi.hid_free_enumeration.restype = None +hidapi.hid_open.argtypes = [ctypes.c_ushort, ctypes.c_ushort, ctypes.c_wchar_p] +hidapi.hid_open.restype = ctypes.c_void_p +hidapi.hid_open_path.argtypes = [ctypes.c_char_p] +hidapi.hid_open_path.restype = ctypes.c_void_p +hidapi.hid_write.argtypes = [ctypes.c_void_p, ctypes.c_char_p, ctypes.c_size_t] +hidapi.hid_write.restype = ctypes.c_int +hidapi.hid_read_timeout.argtypes = [ + ctypes.c_void_p, ctypes.c_char_p, ctypes.c_size_t, ctypes.c_int] +hidapi.hid_read_timeout.restype = ctypes.c_int +hidapi.hid_read.argtypes = [ctypes.c_void_p, ctypes.c_char_p, ctypes.c_size_t] +hidapi.hid_read.restype = ctypes.c_int +hidapi.hid_get_input_report.argtypes = [ + ctypes.c_void_p, ctypes.c_char_p, ctypes.c_size_t] +hidapi.hid_get_input_report.restype = ctypes.c_int +hidapi.hid_set_nonblocking.argtypes = [ctypes.c_void_p, ctypes.c_int] +hidapi.hid_set_nonblocking.restype = ctypes.c_int +hidapi.hid_send_feature_report.argtypes = [ + ctypes.c_void_p, ctypes.c_char_p, ctypes.c_int] +hidapi.hid_send_feature_report.restype = ctypes.c_int +hidapi.hid_get_feature_report.argtypes = [ + ctypes.c_void_p, ctypes.c_char_p, ctypes.c_size_t] +hidapi.hid_get_feature_report.restype = ctypes.c_int +hidapi.hid_close.argtypes = [ctypes.c_void_p] +hidapi.hid_close.restype = None +hidapi.hid_get_manufacturer_string.argtypes = [ + ctypes.c_void_p, ctypes.c_wchar_p, ctypes.c_size_t] +hidapi.hid_get_manufacturer_string.restype = ctypes.c_int +hidapi.hid_get_product_string.argtypes = [ + ctypes.c_void_p, ctypes.c_wchar_p, ctypes.c_size_t] +hidapi.hid_get_product_string.restype = ctypes.c_int +hidapi.hid_get_serial_number_string.argtypes = [ + ctypes.c_void_p, ctypes.c_wchar_p, ctypes.c_size_t] +hidapi.hid_get_serial_number_string.restype = ctypes.c_int +hidapi.hid_get_indexed_string.argtypes = [ + ctypes.c_void_p, ctypes.c_int, ctypes.c_wchar_p, ctypes.c_size_t] +hidapi.hid_get_indexed_string.restype = ctypes.c_int +hidapi.hid_error.argtypes = [ctypes.c_void_p] +hidapi.hid_error.restype = ctypes.c_wchar_p + + +def enumerate(vid=0, pid=0): + ret = [] + info = hidapi.hid_enumerate(vid, pid) + c = info + + while c: + ret.append(c.contents.as_dict()) + c = c.contents.next + + hidapi.hid_free_enumeration(info) + + return ret + + +class Device(object): + def __init__(self, vid=None, pid=None, serial=None, path=None): + if path: + self.__dev = hidapi.hid_open_path(path) + elif serial: + serial = ctypes.create_unicode_buffer(serial) + self.__dev = hidapi.hid_open(vid, pid, serial) + elif vid and pid: + self.__dev = hidapi.hid_open(vid, pid, None) + else: + raise ValueError('specify vid/pid or path') + + if not self.__dev: + raise HIDException('unable to open device') + + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_value, exc_traceback): + self.close() + + def __hidcall(self, function, *args, **kwargs): + if not self.__dev: + raise HIDException('device closed') + + ret = function(*args, **kwargs) + + if ret == -1: + err = hidapi.hid_error(self.__dev) + raise HIDException(err) + return ret + + def __readstring(self, function, max_length=255): + buf = ctypes.create_unicode_buffer(max_length) + self.__hidcall(function, self.__dev, buf, max_length) + return buf.value + + def write(self, data): + return self.__hidcall(hidapi.hid_write, self.__dev, data, len(data)) + + def read(self, size, timeout=None): + data = ctypes.create_string_buffer(size) + + if timeout is None: + size = self.__hidcall(hidapi.hid_read, self.__dev, data, size) + else: + size = self.__hidcall( + hidapi.hid_read_timeout, self.__dev, data, size, timeout) + + return data.raw[:size] + + def get_input_report(self, report_id, size): + data = ctypes.create_string_buffer(size) + + # Pass the id of the report to be read. + data[0] = bytearray((report_id,)) + + size = self.__hidcall( + hidapi.hid_get_input_report, self.__dev, data, size) + return data.raw[:size] + + def send_feature_report(self, data): + return self.__hidcall(hidapi.hid_send_feature_report, + self.__dev, data, len(data)) + + def get_feature_report(self, report_id, size): + data = ctypes.create_string_buffer(size) + + # Pass the id of the report to be read. + data[0] = bytearray((report_id,)) + + size = self.__hidcall( + hidapi.hid_get_feature_report, self.__dev, data, size) + return data.raw[:size] + + def close(self): + if self.__dev: + hidapi.hid_close(self.__dev) + self.__dev = None + + @property + def nonblocking(self): + return getattr(self, '_nonblocking', 0) + + @nonblocking.setter + def nonblocking(self, value): + self.__hidcall(hidapi.hid_set_nonblocking, self.__dev, value) + setattr(self, '_nonblocking', value) + + @property + def manufacturer(self): + return self.__readstring(hidapi.hid_get_manufacturer_string) + + @property + def product(self): + return self.__readstring(hidapi.hid_get_product_string) + + @property + def serial(self): + return self.__readstring(hidapi.hid_get_serial_number_string) + + def get_indexed_string(self, index, max_length=255): + buf = ctypes.create_unicode_buffer(max_length) + self.__hidcall(hidapi.hid_get_indexed_string, + self.__dev, index, buf, max_length) + return buf.value diff --git a/ControllByPs4/lib/hid/__pycache__/__init__.cpython-39.pyc b/ControllByPs4/lib/hid/__pycache__/__init__.cpython-39.pyc new file mode 100644 index 0000000..a9d7a61 Binary files /dev/null and b/ControllByPs4/lib/hid/__pycache__/__init__.cpython-39.pyc differ diff --git a/ControllByPs4/lib/hid/hidapi.dll b/ControllByPs4/lib/hid/hidapi.dll new file mode 100644 index 0000000..8c78d78 Binary files /dev/null and b/ControllByPs4/lib/hid/hidapi.dll differ diff --git a/ControllByPs4/lib/hid/hidapi.lib b/ControllByPs4/lib/hid/hidapi.lib new file mode 100644 index 0000000..a028cee Binary files /dev/null and b/ControllByPs4/lib/hid/hidapi.lib differ diff --git a/ControllByPs4/lib/hid/hidapi.pdb b/ControllByPs4/lib/hid/hidapi.pdb new file mode 100644 index 0000000..4d00c01 Binary files /dev/null and b/ControllByPs4/lib/hid/hidapi.pdb differ