ODF/app/services.py

433 lines
14 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""ODF 光纤配线单元管理系统 - 业务逻辑层。
使用内存假数据存储,模拟 ODF 配线单元的管理功能。
提供机架、配线单元、端口、跳接连接的 CRUD 操作。
"""
import uuid
from datetime import datetime
from typing import Optional
from app.models import Rack, Unit, Port, Connection, PortType, PortStatus
class OdfService:
"""ODF 光纤配线单元管理服务。
使用内存字典存储数据,提供对机架、配线单元、端口和跳接连接的管理能力。
此类为单例服务,所有数据在服务运行期间保存在内存中。
"""
def __init__(self) -> None:
"""初始化内存数据存储和初始假数据。"""
self._racks: dict[str, Rack] = {}
self._units: dict[str, Unit] = {}
self._ports: dict[str, Port] = {}
self._connections: dict[str, Connection] = {}
self._init_demo_data()
def _init_demo_data(self) -> None:
"""初始化演示用假数据,方便测试和展示。"""
# 创建 2 个机架
rack1 = Rack(
rack_id="rack-001",
rack_name="ODF-001",
location="A机房-1列-01排",
)
rack2 = Rack(
rack_id="rack-002",
rack_name="ODF-002",
location="A机房-1列-02排",
)
# 为机架1创建2个单元每个单元12口
unit1 = self._create_unit_internal("rack-001", 1, "上排单元A", "上排", 12, PortType.LC)
unit2 = self._create_unit_internal("rack-001", 2, "下排单元B", "下排", 12, PortType.SC)
# 为机架2创建1个单元24口
unit3 = self._create_unit_internal("rack-002", 1, "上排单元C", "上排", 24, PortType.LC)
rack1.units = [unit1, unit2]
rack2.units = [unit3]
self._racks[rack1.rack_id] = rack1
self._racks[rack2.rack_id] = rack2
# 创建一条示例跳接连接单元1的端口1 -> 单元2的端口1
unit1_ports = [p for p in self._ports.values() if p.unit_id == unit1.unit_id]
unit2_ports = [p for p in self._ports.values() if p.unit_id == unit2.unit_id]
if unit1_ports and unit2_ports:
pa = unit1_ports[0]
pb = unit2_ports[0]
pa.status = PortStatus.IN_USE
pb.status = PortStatus.IN_USE
conn = Connection(
connection_id=f"conn-{uuid.uuid4().hex[:8]}",
port_a_id=pa.port_id,
port_b_id=pb.port_id,
fiber_length=3.5,
create_time=datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
remark="示例跳接",
)
self._connections[conn.connection_id] = conn
def _create_unit_internal(
self,
rack_id: str,
unit_number: int,
unit_name: str,
position: str,
port_count: int,
port_type: PortType,
) -> Unit:
"""内部方法:创建一个配线单元及其端口。
Args:
rack_id: 所属机架 ID。
unit_number: 单元编号。
unit_name: 单元名称。
position: 位置描述。
port_count: 端口数量。
port_type: 端口类型。
Returns:
创建的 Unit 实例。
"""
unit_id = f"unit-{uuid.uuid4().hex[:8]}"
unit = Unit(
unit_id=unit_id,
rack_id=rack_id,
unit_number=unit_number,
unit_name=unit_name,
position=position,
ports=[],
)
ports: list[Port] = []
for i in range(1, port_count + 1):
port_id = f"port-{uuid.uuid4().hex[:8]}"
port = Port(
port_id=port_id,
unit_id=unit_id,
port_number=i,
port_type=port_type,
status=PortStatus.FREE,
label=f"{rack_id}-{unit_number}-{i:02d}",
)
self._ports[port_id] = port
ports.append(port)
unit.ports = ports
self._units[unit_id] = unit
return unit
# ==================== 机架管理 ====================
def list_racks(self) -> list[dict]:
"""获取所有机架的简要列表。
Returns:
包含机架信息和单元数量的字典列表。
"""
result = []
for rack in self._racks.values():
result.append({
"rack_id": rack.rack_id,
"rack_name": rack.rack_name,
"location": rack.location,
"unit_count": len(rack.units),
})
return result
def get_rack(self, rack_id: str) -> Optional[Rack]:
"""根据 ID 获取机架详情(包含单元和端口)。
Args:
rack_id: 机架 ID。
Returns:
机架对象,未找到时返回 None。
"""
return self._racks.get(rack_id)
def create_rack(self, rack_name: str, location: str) -> Rack:
"""创建一个新的空机架。
Args:
rack_name: 机架名称/编号。
location: 物理位置。
Returns:
新创建的机架对象。
"""
rack_id = f"rack-{uuid.uuid4().hex[:8]}"
rack = Rack(
rack_id=rack_id,
rack_name=rack_name,
location=location,
units=[],
)
self._racks[rack_id] = rack
return rack
def delete_rack(self, rack_id: str) -> bool:
"""删除指定机架(同时删除其下的单元和端口)。
Args:
rack_id: 机架 ID。
Returns:
删除成功返回 True机架不存在返回 False。
"""
if rack_id not in self._racks:
return False
rack = self._racks[rack_id]
for unit in rack.units:
for port in unit.ports:
self._ports.pop(port.port_id, None)
self._units.pop(unit.unit_id, None)
del self._racks[rack_id]
return True
# ==================== 配线单元管理 ====================
def create_unit(
self,
rack_id: str,
unit_number: int,
unit_name: str,
position: str,
port_count: int,
port_type: PortType,
) -> Optional[Unit]:
"""在指定机架下创建配线单元。
Args:
rack_id: 所属机架 ID。
unit_number: 单元编号。
unit_name: 单元名称。
position: 位置描述。
port_count: 端口数量。
port_type: 端口类型。
Returns:
创建的 Unit 对象,机架不存在时返回 None。
"""
if rack_id not in self._racks:
return None
unit = self._create_unit_internal(rack_id, unit_number, unit_name, position, port_count, port_type)
self._racks[rack_id].units.append(unit)
return unit
def delete_unit(self, unit_id: str) -> bool:
"""删除指定配线单元(同时删除其端口)。
Args:
unit_id: 单元 ID。
Returns:
删除成功返回 True单元不存在返回 False。
"""
if unit_id not in self._units:
return False
unit = self._units[unit_id]
for port in unit.ports:
self._ports.pop(port.port_id, None)
rack = self._racks.get(unit.rack_id)
if rack:
rack.units = [u for u in rack.units if u.unit_id != unit_id]
del self._units[unit_id]
return True
# ==================== 端口管理 ====================
def get_free_ports(self, rack_id: Optional[str] = None) -> list[dict]:
"""查询空闲端口列表。
Args:
rack_id: 可选的机架 ID指定时只查询该机架下的空闲端口。
Returns:
空闲端口信息列表,包含所属单元和机架信息。
"""
result = []
for port in self._ports.values():
if port.status != PortStatus.FREE:
continue
unit = self._units.get(port.unit_id)
if not unit:
continue
rack = self._racks.get(unit.rack_id)
if not rack:
continue
if rack_id and rack.rack_id != rack_id:
continue
result.append({
"port_id": port.port_id,
"port_number": port.port_number,
"port_type": port.port_type,
"label": port.label,
"unit_id": unit.unit_id,
"unit_number": unit.unit_number,
"rack_id": rack.rack_id,
"rack_name": rack.rack_name,
})
return result
def get_port_detail(self, port_id: str) -> Optional[dict]:
"""获取端口的详细信息(含所属单元和机架)。
Args:
port_id: 端口 ID。
Returns:
端口详细信息字典,端口不存在时返回 None。
"""
port = self._ports.get(port_id)
if not port:
return None
unit = self._units.get(port.unit_id)
if not unit:
return None
rack = self._racks.get(unit.rack_id)
if not rack:
return None
return {
"port_id": port.port_id,
"port_number": port.port_number,
"port_type": port.port_type,
"status": port.status,
"label": port.label,
"unit_id": unit.unit_id,
"unit_number": unit.unit_number,
"rack_id": rack.rack_id,
"rack_name": rack.rack_name,
}
# ==================== 跳接连接管理 ====================
def create_connection(
self,
port_a_id: str,
port_b_id: str,
fiber_length: float,
remark: str,
) -> Optional[Connection]:
"""创建端口之间的跳接连接。
两个端口必须都存在且状态为空闲FREE
Args:
port_a_id: A 端端口 ID。
port_b_id: B 端端口 ID。
fiber_length: 光纤长度(米)。
remark: 备注。
Returns:
创建的 Connection 对象,端口不存在或端口繁忙时返回 None。
"""
port_a = self._ports.get(port_a_id)
port_b = self._ports.get(port_b_id)
if not port_a or not port_b:
return None
if port_a.status != PortStatus.FREE or port_b.status != PortStatus.FREE:
return None
if port_a_id == port_b_id:
return None
connection_id = f"conn-{uuid.uuid4().hex[:8]}"
conn = Connection(
connection_id=connection_id,
port_a_id=port_a_id,
port_b_id=port_b_id,
fiber_length=fiber_length,
create_time=datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
remark=remark,
)
port_a.status = PortStatus.IN_USE
port_b.status = PortStatus.IN_USE
self._connections[connection_id] = conn
return conn
def list_connections(self) -> list[Connection]:
"""获取所有跳接连接列表。
Returns:
连接对象列表。
"""
return list(self._connections.values())
def delete_connection(self, connection_id: str) -> bool:
"""删除跳接连接(释放两个端口状态为空闲)。
Args:
connection_id: 连接 ID。
Returns:
删除成功返回 True连接不存在返回 False。
"""
conn = self._connections.get(connection_id)
if not conn:
return False
port_a = self._ports.get(conn.port_a_id)
port_b = self._ports.get(conn.port_b_id)
if port_a:
port_a.status = PortStatus.FREE
if port_b:
port_b.status = PortStatus.FREE
del self._connections[connection_id]
return True
def get_port_path(self, port_id: str) -> Optional[dict]:
"""查询指定端口的连接路径信息。
Args:
port_id: 端口 ID。
Returns:
包含端口信息和关联连接的字典,端口不存在时返回 None。
"""
port = self._ports.get(port_id)
if not port:
return None
unit = self._units.get(port.unit_id)
rack = self._racks.get(unit.rack_id) if unit else None
connected_conn = None
for conn in self._connections.values():
if conn.port_a_id == port_id or conn.port_b_id == port_id:
connected_conn = conn
break
other_port = None
if connected_conn:
other_port_id = (
connected_conn.port_b_id
if connected_conn.port_a_id == port_id
else connected_conn.port_a_id
)
other_port = self.get_port_detail(other_port_id)
return {
"port": {
"port_id": port.port_id,
"port_number": port.port_number,
"port_type": port.port_type,
"status": port.status,
"label": port.label,
"unit_id": unit.unit_id if unit else "",
"unit_number": unit.unit_number if unit else 0,
"rack_id": rack.rack_id if rack else "",
"rack_name": rack.rack_name if rack else "",
},
"connection": {
"connection_id": connected_conn.connection_id if connected_conn else None,
"fiber_length": connected_conn.fiber_length if connected_conn else 0,
"create_time": connected_conn.create_time if connected_conn else "",
"remark": connected_conn.remark if connected_conn else "",
} if connected_conn else None,
"connected_port": other_port,
}
# 全局单例服务实例
service = OdfService()