fix(links): reserve ports on add, accept port name and auto, add port_in_use code

This commit is contained in:
Mark Paronyan committed 2026-10-07 15:41:04 +03:00
1 parent 2fb7056cb9
commit a9e87df644
10 files changed
+286 -21

No files matched your search

+6 -1
View File
@@ -17,9 +17,14 @@
class ControllerError(Exception):
def __init__(self, message: str):
def __init__(self, message: str, code: str | None = None):
super().__init__()
self._message = message
self._code = code
@property
def code(self):
return self._code
def __repr__(self):
return self._message
+16 -8
View File
@@ -276,7 +276,7 @@ class Link:
if port is None:
raise ControllerNotFoundError(f"Port {adapter_number}/{port_number} for {node.name} not found")
if port.link is not None:
raise ControllerError("Port is already used")
raise ControllerError("Port is already used", code="port_in_use")
self._link_type = port.link_type
@@ -313,17 +313,29 @@ class Link:
{"node": node, "adapter_number": adapter_number, "port_number": port_number, "port": port, "label": label}
)
if not batch:
port.link = self
if len(self._nodes) == 2 and not batch:
await self.create()
try:
await self.create()
except BaseException:
self.release_ports()
raise
for n in self._nodes:
n["node"].add_link(self)
n["port"].link = self
self._created = True
self._project.emit_notification("link.created", self.asdict())
if dump:
self._project.dump()
def release_ports(self):
for n in self._nodes:
if n["port"].link == self:
n["port"].link = None
n["node"].links.discard(self)
async def update_nodes(self, nodes):
for node_data in nodes:
node = self._project.get_node(node_data["node_id"])
@@ -352,11 +364,7 @@ class Link:
"""
Delete the link
"""
for n in self._nodes:
# It could be different of self if we rollback an already existing link
if n["port"].link == self:
n["port"].link = None
n["node"].remove_link(self)
self.release_ports()
async def reset(self):
"""
+21
View File
@@ -1142,6 +1142,27 @@ class Node:
return port
return None
def get_port_by_name(self, name):
"""
Return the port with this name or None if the port is not found
"""
for port in self.ports:
if port.name == name:
return port
return None
def get_free_port(self, link_type=None):
"""
Return the lowest free port, optionally restricted to a link type,
or None if there is no such port
"""
free_ports = [
port for port in self.ports if port.link is None and (link_type is None or port.link_type == link_type)
]
if not free_ports:
return None
return min(free_ports, key=lambda port: (port.adapter_number, port.port_number))
def _list_ports(self):
"""
Generate the list of port display in the client
+4
View File
@@ -41,6 +41,10 @@ class Port:
def link(self, val):
self._link = val
@property
def name(self):
return self._name
@property
def adapter_number(self):
return self._adapter_number