Skip to content

Commit

Permalink
Merge pull request #31 from iq-motion-control/refactor-pulsing-module
Browse files Browse the repository at this point in the history
Updated Vertiq2306 with new pulsing firmware. Added additional files …
  • Loading branch information
Vertiq-Ben committed May 31, 2023
2 parents 1ea5bf1 + 121f64e commit cb2f997
Show file tree
Hide file tree
Showing 9 changed files with 287 additions and 20 deletions.
2 changes: 2 additions & 0 deletions iqmotion/__init__.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
from iqmotion.iq_devices.base_iq_module import BaseIqModule
from iqmotion.iq_devices.custom_iq_module import CustomIqModule

# Soon to be deprecated: but the original way to access motor firmware API
from iqmotion.iq_devices.speed_module import SpeedModule
from iqmotion.iq_devices.servo_module import ServoModule
from iqmotion.iq_devices.step_dir_module import StepDirModule
from iqmotion.iq_devices.pulsing_module import PulsingModule

# Make Motor Modules front facing visible
from iqmotion.iq_devices.vertiq8108_module import Vertiq8108
Expand Down
81 changes: 81 additions & 0 deletions iqmotion/iq_devices/custom_iq_module.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import os

from iqmotion.iq_devices.iq_module import IqModule
from iqmotion.communication.communicator import Communicator


# There are 3 options when it comes to adding extra clients
#
# -Quick and dirty-
# 1. clients_path {str: directory path}:
# Points to a directory containing client entry jsons
# Note: this option will add every client entry in the folder
#
# -More Work but Flexible-
# 2. extra_clients {list: [dir path, dir path, ...]}:
# Contains a list of paths to each cleint entry you want to include
# Note: you need to pass in an absolute paths
#
# -Most Work but cleanest-
# 3. custom_module_location {str: directory path}:
# Commonly passed in as a cwd path, this can contain a module file
# in addition to extra client files.
# Note: You need to have a folder named extra_client_files hosting your
# client files
# Example of 3rd Option
# {
# "clients": [
# "brushless_drive",
# "propeller_motor_control",
# "anticogging",
# "buzzer_control",
# "esc_propeller_input_parser",
# "hobby_input",
# "persistent_memory",
# "power_monitor",
# "serial_interface",
# "servo_input_parser",
# "step_direction_input",
# "system_control",
# "temperature_estimator",
# "temperature_monitor_uc"
# ],
# "extra_clients": [
# "extra_client"
# ]
# }


class CustomIqModule(IqModule):
_MODULE_FILE_NAME = ""

def __init__(
self,
custom_module_location: str,
com: Communicator,
module_idn=0,
clients_path: str=None,
extra_clients: list=None,
):
module_file_path = os.path.join(
os.path.dirname(custom_module_location),
("module_files/" + self._MODULE_FILE_NAME),
)

super().__init__(com,
module_idn,
clients_path=clients_path,
extra_clients=extra_clients,
module_file_path=module_file_path)

# This will pull clients from the Module JSON
# --Refer to example Below--
if "extra_clients" in self._module_file_dict:
for extra_client_name in self._module_file_dict["extra_clients"]:
extra_client_file_path = os.path.join(
os.path.dirname(custom_module_location),
(f"extra_client_files/{extra_client_name}.json"),
)
self.add_client(extra_client_file_path)


19 changes: 19 additions & 0 deletions iqmotion/iq_devices/module_files/pulsing.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"clients": [
"brushless_drive",
"buzzer_control",
"coil_temperature_estimator",
"esc_propeller_input_parser",
"iquart_flight_controller_interface",
"persistent_memory",
"power_monitor",
"power_safety",
"propeller_motor_control",
"pulsing_rectangular_input_parser",
"serial_interface",
"system_control",
"temperature_estimator",
"temperature_monitor_uc",
"voltage_superposition"
]
}
25 changes: 25 additions & 0 deletions iqmotion/iq_devices/pulsing_module.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
from iqmotion.communication.communicator import Communicator
from iqmotion.iq_devices.custom_iq_module import CustomIqModule


class PulsingModule(CustomIqModule):
""" Creates PulsingModule object
Arguments:
com {Communicator} -- The communicator object to interface with the IqModule
Keyword Arguments:
module_idn {int} -- The idn of the module (default: {0})
extra_clients {list} -- list of file paths to extra clients you want to load in the module (default: {None})
"""

_DEFAULT_CONTROL_CLIENT = "propeller_motor_control"
_DEFAULT_VELOCITY_CLIENT_ENTRY = "ctrl_velocity"
_DEFAULT_VOLTS_CLIENT_ENTRY = "ctrl_volts"

_MODULE_FILE_NAME = "pulsing.json"

def __init__(
self, com: Communicator, module_idn=0, extra_clients=None,
):
super().__init__(__file__, com, module_idn, extra_clients)
47 changes: 28 additions & 19 deletions iqmotion/iq_devices/vertiq2306_module.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,30 +2,33 @@
from iqmotion.communication.communicator import Communicator
from iqmotion.custom_errors import IqModuleError

# This is a IQ Module Wrapper for the vertiq 2306
# This Wrapper takes in firmware argument and creates an veritq2036 API Object
class Vertiq2306(IqModule):
""" Creates vertiq2306 object with default speed firmware
Arguments:
com {Communicator} -- The communicator object to interface with the IqModule
firmware {str} -- Which firmware is the motor using (default: {"speed"})
Keyword Arguments:
module_idn {int} -- The idn of the module (default: {0})
extra_clients {list} -- list of file paths to extra clients you want to load in the module (default: {None})
"""

class Vertiq2306(IqModule):
def __init__(
self,
com: Communicator,
com: Communicator,
module_idn: int = 0,
firmware: str = "speed", # Default Firmware
clients_path: str = None
firmware: str = "speed", # Default Firmware
clients_path: str = None,
):
"""Creates Vertiq2306 object with default speed firmware.
:param com: The communicator object used to interface with the IqModule
:type com: Communicator
:param module_idn: The idn of the module (default: 0)
:type module_idn: int
:param firmware: The firmware type used to determine which clients are available for the module to use
Options include:
* speed
* stepdir
* servo
* pulsing
:type firmware: str
:param clients_path: Optional parameter to specify a directory containing extra clients for the module to use (default: {None})
:type clients_path: str
"""

# Point to the correct JSON File depending on the firmware
#
if firmware.lower() == "speed":
self._DEFAULT_CONTROL_CLIENT = "propeller_motor_control"
self._DEFAULT_VELOCITY_CLIENT_ENTRY = "ctrl_velocity"
Expand All @@ -40,16 +43,22 @@ def __init__(

self._MODULE_FILE_NAME = "step_direction.json"

elif firmware.lower() == "servo":
elif firmware.lower() == "servo":
self._DEFAULT_CONTROL_CLIENT = "multi_turn_angle_control"
self._DEFAULT_VELOCITY_CLIENT_ENTRY = "ctrl_velocity"
self._DEFAULT_VOLTS_CLIENT_ENTRY = "ctrl_volts"

self._MODULE_FILE_NAME = "servo.json"

elif firmware.lower() == "pulsing":
self._DEFAULT_CONTROL_CLIENT = "propeller_motor_control"
self._DEFAULT_VELOCITY_CLIENT_ENTRY = "ctrl_velocity"
self._DEFAULT_VOLTS_CLIENT_ENTRY = "ctrl_volts"

self._MODULE_FILE_NAME = "pulsing.json"

else:
raise IqModuleError("'" + str(firmware) + "' firmware is not supported")

# Pass the Super
super().__init__(com, module_idn, clients_path)
super().__init__(com, module_idn, clients_path)
21 changes: 21 additions & 0 deletions iqmotion/tests/module_files/new_module.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"clients": [
"brushless_drive",
"propeller_motor_control",
"anticogging",
"buzzer_control",
"esc_propeller_input_parser",
"hobby_input",
"persistent_memory",
"power_monitor",
"serial_interface",
"servo_input_parser",
"step_direction_input",
"system_control",
"temperature_estimator",
"temperature_monitor_uc"
],
"extra_clients": [
"extra_client"
]
}
18 changes: 18 additions & 0 deletions iqmotion/tests/module_files/non_extra_module.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"clients": [
"brushless_drive",
"propeller_motor_control",
"anticogging",
"buzzer_control",
"esc_propeller_input_parser",
"hobby_input",
"persistent_memory",
"power_monitor",
"serial_interface",
"servo_input_parser",
"step_direction_input",
"system_control",
"temperature_estimator",
"temperature_monitor_uc"
]
}
89 changes: 89 additions & 0 deletions iqmotion/tests/test_custom_iq_module.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import pytest
import os

from iqmotion.iq_devices.custom_iq_module import CustomIqModule

from iqmotion.tests.helpers import MockCommunicator


class NewModule(CustomIqModule):
_DEFAULT_CONTROL_CLIENT = "CONTROL"
_DEFAULT_VELOCITY_CLIENT_ENTRY = "VEL"
_DEFAULT_VOLTS_CLIENT_ENTRY = "VOLTS"
_MODULE_FILE_NAME = "new_module.json"

def __init__(
self, com, module_idn=0, extra_clients=None,
):
super().__init__(__file__, com, module_idn, extra_clients=extra_clients)

class NonExtraModule(CustomIqModule):
_DEFAULT_CONTROL_CLIENT = "CONTROL"
_DEFAULT_VELOCITY_CLIENT_ENTRY = "VEL"
_DEFAULT_VOLTS_CLIENT_ENTRY = "VOLTS"
_MODULE_FILE_NAME = "non_extra_module.json"

def __init__(
self, com, module_idn=0, extra_clients=None,
):
super().__init__(__file__, com, module_idn, extra_clients=extra_clients)


@pytest.fixture
def mock_communicator():
mock_class = MockCommunicator()
return mock_class


# pylint: disable=redefined-outer-name
def test_custom_iq_module(mock_communicator):
expected_clients = set(
[
"brushless_drive",
"propeller_motor_control",
"anticogging",
"buzzer_control",
"esc_propeller_input_parser",
"hobby_input",
"persistent_memory",
"power_monitor",
"serial_interface",
"servo_input_parser",
"step_direction_input",
"system_control",
"temperature_estimator",
"temperature_monitor_uc",
"extra_client",
],
)
iq_module = NewModule(mock_communicator, 0)
client_dict = set(iq_module._client_dict.keys())
assert client_dict == expected_clients

def test_extra_clients_iq_module(mock_communicator):
expected_clients = set(
[
"brushless_drive",
"propeller_motor_control",
"anticogging",
"buzzer_control",
"esc_propeller_input_parser",
"hobby_input",
"persistent_memory",
"power_monitor",
"serial_interface",
"servo_input_parser",
"step_direction_input",
"system_control",
"temperature_estimator",
"temperature_monitor_uc",
"extra_client",
],
)

extra_client = os.path.join(os.path.dirname(__file__), ("extra_client_files/extra_client.json"))
print(extra_client)

iq_module = NonExtraModule(mock_communicator, 0, extra_clients=[extra_client])
client_dict = set(iq_module._client_dict.keys())
assert client_dict == expected_clients
5 changes: 4 additions & 1 deletion iqmotion/tests/test_vertiq2306.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ def test_stepdir_module(self, mock_communicator):
def test_servo_module(self, mock_communicator):
Vertiq2306(mock_communicator, firmware="servo")

def test_pulsing_module(self, mock_communicator):
Vertiq2306(mock_communicator, firmware="pulsing")

def test_false_module(self, mock_communicator):
with pytest.raises(IqModuleError):
Vertiq2306(mock_communicator, firmware="pulse")
Vertiq2306(mock_communicator, firmware="abcxyz")

0 comments on commit cb2f997

Please sign in to comment.