Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
118 changes: 34 additions & 84 deletions pyparrot/commandsandsensors/DroneCommandParser.py
Original file line number Diff line number Diff line change
@@ -1,109 +1,59 @@
import untangle
import os
import xml.etree.ElementTree as et
from os.path import join
from typing import Tuple


class DroneCommandParser:
def __init__(self):
# store the commandsandsensors as they are called so you don't have to parse each time
self.command_tuple_cache = dict()

# parse the command files from XML (so we don't have to store ids and can use names
# for readability and portability!)

# grab module path per http://www.karoltomala.com/blog/?p=622
path = os.path.abspath(__file__)
dir_path = os.path.dirname(path)

self.common_commands = untangle.parse(join(dir_path, 'common.xml'))
self.minidrone_commands = untangle.parse(join(dir_path, 'minidrone.xml'))
self.ardrone3_commands = untangle.parse(join(dir_path, 'ardrone3.xml'))


def get_command_tuple(self, project, myclass, cmd):
self.xmls = {}

def _load_xml(self, project: str) -> et.ElementTree:
if project not in self.xmls:
# grab module path per http://www.karoltomala.com/blog/?p=622
path = os.path.abspath(__file__)
dir_path = os.path.dirname(path)
self.xmls[project] = et.parse(join(dir_path, f"{project}.xml"))
return self.xmls[project]

def get_command_tuple(
self, project: str, myclass: str, cmd: str
) -> Tuple[int, int, int]:
"""
Parses the command XML for the specified class name and command name

:param myclass: class name (renamed to myclass to avoid reserved name) in the xml file
:param cmd: command to execute (from XML file)
:return:
"""
# only search if it isn't already in the cache
if (myclass, cmd) in self.command_tuple_cache:
return self.command_tuple_cache[(myclass, cmd)]

# pick the right command file to draw from
if (project == "ardrone3"):
my_file = self.ardrone3_commands
elif (project == "minidrone"):
my_file = self.minidrone_commands
else:
my_file = self.common_commands

# run the search first in minidrone xml and then hit common if that failed
project_id = int(my_file.project['id'])

for child in my_file.project.myclass:
if child['name'] == myclass:
class_id = int(child['id'])
#print child['name']

for subchild in child.cmd:
#print subchild
if subchild['name'] == cmd:
#print subchild['name']
cmd_id = int(subchild['id'])

# cache the result
self.command_tuple_cache[(myclass, cmd)] = (project_id, class_id, cmd_id)
return (project_id, class_id, cmd_id)



def get_command_tuple_with_enum(self, project, myclass, cmd, enum_name):
xml = self._load_xml(project)
project_id = xml.getroot().get("id")
class_id = xml.find(f"myclass[@name='{myclass}']").get("id")
cmd_id = xml.find(f"myclass[@name='{myclass}']/cmd[@name='{cmd}']").get("id")
return (int(project_id), int(class_id), int(cmd_id))

def get_command_tuple_with_enum(
self, project: str, myclass: str, cmd: str, enum_name: str
) -> Tuple[Tuple[int, int, int], int]:
"""
Parses the command XML for the specified class name and command name and checks for enum_name

:param myclass: class name (renamed to myclass to avoid reserved name) in the xml file
:param cmd: command to execute (from XML file)
:return:
"""
# only search if it isn't already in the cache
if (myclass, cmd, enum_name) in self.command_tuple_cache:
#print("using the cache")
#print(self.command_tuple_cache[(myclass, cmd, enum_name)])
return self.command_tuple_cache[(myclass, cmd, enum_name)]

# pick the right command file to draw from
if (project == "ardrone3"):
my_file = self.ardrone3_commands
elif (project == "minidrone"):
my_file = self.minidrone_commands
else:
my_file = self.common_commands

# run the search first in minidrone xml and then hit common if that failed
project_id = int(my_file.project['id'])

for child in my_file.project.myclass:
if child['name'] == myclass:
class_id = int(child['id'])
#print child['name']

for subchild in child.cmd:
#print subchild
if subchild['name'] == cmd:
#print subchild['name']
cmd_id = int(subchild['id'])

for arg_child in subchild.arg:
if arg_child['type'] == "enum":
for e_idx, echild in enumerate(arg_child.enum):
if echild['name'] == enum_name:
enum_id = e_idx

# cache the result
self.command_tuple_cache[(myclass, cmd, enum_name)] = ((project_id, class_id, cmd_id), enum_id)

#print ((project_id, class_id, cmd_id), enum_id)
return ((project_id, class_id, cmd_id), enum_id)

xml = self._load_xml(project)
(project_id, class_id, cmd_id) = self.get_command_tuple(project, myclass, cmd)
arg_enum_node = xml.find(
f"myclass[@name='{myclass}']/cmd[@name='{cmd}']/arg[@type='enum']"
)
enum_id = list(arg_enum_node).index(
arg_enum_node.find(f"enum[@name='{enum_name}']")
)
return ((project_id, class_id, cmd_id), enum_id)
Empty file.
36 changes: 36 additions & 0 deletions pyparrot/commandsandsensors/tests/test_DroneCommandParser.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import unittest
from pyparrot.commandsandsensors import DroneCommandParser


class TestDroneCommandParser(unittest.TestCase):
def test_get_command_tuple(self):
cmd_parser = DroneCommandParser.DroneCommandParser()
actual = cmd_parser.get_command_tuple("minidrone", "Piloting", "PCMD")
expected = (2, 0, 2)
self.assertEqual(expected, actual)

def test_get_command_tuple_with_enum(self):
cmd_parser = DroneCommandParser.DroneCommandParser()
actual = cmd_parser.get_command_tuple_with_enum(
"minidrone", "UsbAccessory", "ClawControl", "OPEN"
)
expected = ((2, 16, 1), 0)
self.assertEqual(expected, actual)

def test_minidrone(self):
cmd_parser = DroneCommandParser.DroneCommandParser()
actual = cmd_parser._load_xml("minidrone").getroot().get("id")
expected = 2
self.assertEqual(expected, int(actual))

def test_ardrone3(self):
cmd_parser = DroneCommandParser.DroneCommandParser()
actual = cmd_parser._load_xml("ardrone3").getroot().get("id")
expected = 1
self.assertEqual(expected, int(actual))

def test_common(self):
cmd_parser = DroneCommandParser.DroneCommandParser()
actual = cmd_parser._load_xml("common").getroot().get("id")
expected = 0
self.assertEqual(expected, int(actual))