Skip to content

Commit

Permalink
feat(api): Add physical USB Port information to the hardware controll…
Browse files Browse the repository at this point in the history
…er (#7359)
  • Loading branch information
Laura-Danielle authored Mar 11, 2021
1 parent cd83650 commit 7d9734b
Show file tree
Hide file tree
Showing 27 changed files with 1,067 additions and 29 deletions.
7 changes: 7 additions & 0 deletions api/src/opentrons/algorithms/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@

"""
Algorithms Module.
This module should only contain generic implementations for
common computer science algorithms that can be used in different applications.
"""
55 changes: 55 additions & 0 deletions api/src/opentrons/algorithms/dfs.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
"""
Depth first search.
Search a generic graph down to its leaf
nodes first before back-tracking up the tree.
"""

from typing import List, Set, Generic

from .graph import Graph
from .types import VertexLike, VertexName


class DFS(Generic[VertexName]):
"""
Depth first search class.
This class will build a graph object and then
perform a depth first search on the graph.
"""

def __init__(self, graph: List[VertexLike]) -> None:
"""
DFS Initializer.
:param graph: A list of nodes you wish to add to
the graph.
"""
self._graph = Graph.build(graph)

@property
def graph(self) -> Graph:
"""
DFS property: graph.
:returns: the graph object in which
dfs is being performed on.
"""
return self._graph

def dfs(self) -> Set[VertexName]:
"""
Depth first search.
:returns: the set of visited vertices
in depth first search order.
"""
visited_vertices: Set[VertexName] = set()
for node in self.graph.graph:
if node not in visited_vertices:
visited_vertices.add(node.name)
for neighbor in node.neighbors:
if neighbor not in visited_vertices:
visited_vertices.add(neighbor)
return visited_vertices
196 changes: 196 additions & 0 deletions api/src/opentrons/algorithms/graph.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@

"""
Graph Builder.
Generic graph builder classes.
"""
from __future__ import annotations

from typing import List, Dict, Callable, Sequence, Generic

from .types import VertexLike, VertexName


class Vertex(Generic[VertexName, VertexLike]):
"""
Vertex class.
A class to hold information about each vertex
of a graph.
"""

def __init__(
self, vertex: VertexLike,
neighbors: List[VertexName]) -> None:
"""
Vertex class initializer.
:param vertex: A node dataclass
:param neighbors: A list of node names who
are neighbors to the node dataclass
"""
self._vertex = vertex
self._neighbors = neighbors

@property
def name(self) -> VertexName:
"""
Vertex class property: name.
:returns: Name of the vertex
"""
return self._vertex.name

@property
def vertex(self) -> VertexLike:
"""
Vertex class property: vertex.
:returns: The node dataclass
"""
return self._vertex

@property
def neighbors(self) -> List[VertexName]:
"""
Vertex class property: neighbors.
:returns: The list of node names who are
neighbors with the vertex.
"""
return self._neighbors

def add_neighbor(self, vertex_name: VertexName) -> None:
"""
Add a neighbor.
:param vertex_name: The name of the neighbor
"""
self._neighbors.append(vertex_name)
self._neighbors.sort()

def remove_neighbor(self, vertex_name: VertexName) -> None:
"""
Remove a neighbor.
:param vertex_name: The name of the neighbor
"""
if vertex_name in self._neighbors:
self._neighbors.remove(vertex_name)


def default_sort(vertex: Vertex) -> VertexName:
"""
Sort function default for a graph.
By default, a graph's nodes will be searched
by the name of the node. Generally, the name
should either be a string or an integer.
"""
return vertex.name


class Graph(Generic[VertexName, VertexLike]):
"""
Graph class.
A class to handle functions moving through the
graph.
"""

# Note, the type of sort_by is actually
# Callable[[Vertex], VertexName] however there
# is an issue when using generics for functions
# passed into sort. See
# https://github.com/python/typing/issues/760
def __init__(
self, sorted_graph: List[Vertex],
lookup_table: Dict[VertexName, Vertex],
sort_by: Callable[[Vertex], str]) -> None:
"""
Graph class initializer.
:param sorted_graph: The initial graph, sorted
and converted to vertex objects.
:param lookup_table: A lookup table keyed by vertex
name and with a value of the vertex object
:param sort_by: The callable function used to sort
the graph nodes in priority order.
"""
self._sort_by = sort_by
self._sorted_graph = sorted_graph
self._lookup_table = lookup_table

@classmethod
def build(cls, graph: List[VertexLike],
sort_by: Callable = default_sort) -> Graph:
"""
Graph class builder.
:param graph: A list of nodes to add to the graph.
:param sort_by: The function used to sort the graph
in priority order.
:returns: A graph class
"""
sorted_graph = []
lookup_table = {}
for vertex in graph:
vertex_obj = cls.build_vertex(vertex)
lookup_table[vertex.name] = vertex_obj
sorted_graph.append(vertex_obj)
sorted_graph.sort(key=sort_by)
return cls(sorted_graph, lookup_table, sort_by)

@property
def graph(self) -> Sequence[Vertex]:
"""
Graph class property: graph.
:returns: A list of sorted vertex objects
"""
return self._sorted_graph

@staticmethod
def build_vertex(vertex: VertexLike) -> Vertex:
"""
Build a vertex.
Use this to sort the neighbors and then build
a vertex using a node dataclass.
:param vertex: A node dataclass
:returns: vertex object
"""
vertex.sub_names.sort()
return Vertex(vertex, vertex.sub_names)

def add_vertex(self, vertex: VertexLike) -> None:
"""
Add a vertex.
:param vertex: A node dataclass
"""
new_vertex = self.build_vertex(vertex)
if new_vertex not in self._lookup_table.values():
self._lookup_table[new_vertex.name] = new_vertex
self._sorted_graph.append(new_vertex)
self._sorted_graph.sort(key=self._sort_by)

def remove_vertex(self, vertex: VertexLike) -> None:
"""
Remove a vertex.
:param vertex: A node dataclass
"""
if vertex.name in self._lookup_table.keys():
vertex_to_remove = self._lookup_table[vertex.name]
del self._lookup_table[vertex.name]
self._sorted_graph.remove(vertex_to_remove)

def get_vertex(self, vertex_name: VertexName) -> Vertex:
"""
Get a vertex.
:param vertex_name: The name of the vertex
:returns: The vertex object
"""
return self._lookup_table[vertex_name]
40 changes: 40 additions & 0 deletions api/src/opentrons/algorithms/types.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
"""
Algorithm types.
Any type definitions required for the algorithms
module should be put in this file.
"""

from typing import TypeVar, Generic, List
from dataclasses import dataclass


VertexName = TypeVar('VertexName')


@dataclass(frozen=True)
class GenericNode(Generic[VertexName]):
"""
Generic graph node dataclass.
A dataclass to hold information about a
graph node. Information should not be
mutated about the node.
"""

name: VertexName
sub_names: List[str]

def __hash__(self) -> int:
"""
Hash function.
To have a unique set of nodes, they must
all have a unique hash. Lists are not
hashable which is why we need to unpack
the list here.
"""
return hash((self.name, *self.sub_names))


VertexLike = TypeVar('VertexLike', bound=GenericNode)
40 changes: 40 additions & 0 deletions api/src/opentrons/drivers/rpi_drivers/interfaces.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
from typing import List, Set
from typing_extensions import Protocol

from .types import USBPort


class USBDriverInterface(Protocol):

@staticmethod
def read_bus() -> List[str]:
...

@staticmethod
def convert_port_path(full_port_path: str) -> USBPort:
...

@property
def usb_dev(self) -> List[USBPort]:
...

@usb_dev.setter
def usb_dev(self, ports: List[USBPort]) -> None:
...

@property
def sorted_ports(self) -> Set:
...

@sorted_ports.setter
def sorted_ports(self, sorted: Set) -> None:
...

def read_usb_bus(self) -> List[USBPort]:
...

def find_port(self, device_path: str) -> USBPort:
...

def sort_ports(self) -> None:
...
Loading

0 comments on commit 7d9734b

Please sign in to comment.