package services import ( "fmt" "lostcavewireplanner/internal/db" "lostcavewireplanner/internal/models" ) type Segment struct { DeviceID int64 DeviceName string ModelName string PortID int64 PortName string Side string } type TraceResult struct { FarSegments []Segment ConnectionID int64 ConnectionType string Color string Label1 string Label2 string ClickedPortID int64 ClickedPortName string ClickedDeviceID int64 ClickedDeviceName string ClickedModelName string ClickedSide string NearSegments []Segment HasConnection bool } func TraceConnection(store *db.Store, clickedPortID int64) (*TraceResult, error) { clickedPort, err := store.PortGetByID(clickedPortID) if err != nil { return nil, err } if clickedPort == nil { return nil, fmt.Errorf("port not found") } clickedDevice, err := store.DeviceGetByID(clickedPort.DeviceID) if err != nil { return nil, err } result := &TraceResult{ ClickedPortID: clickedPort.ID, ClickedPortName: clickedPort.Name, ClickedDeviceID: clickedDevice.ID, ClickedDeviceName: clickedDevice.Name, ClickedSide: clickedPort.Side, } if clickedDevice.Model != nil { result.ClickedModelName = clickedDevice.Model.Name } conn, err := store.ConnectionGetByPortID(clickedPortID) if err != nil { return nil, err } if conn == nil { return result, nil } result.HasConnection = true result.ConnectionID = conn.ID result.Color = conn.Color if conn.Label1 != nil { result.Label1 = *conn.Label1 } if conn.Label2 != nil { result.Label2 = *conn.Label2 } if conn.ConnectionType != nil { result.ConnectionType = conn.ConnectionType.Name } var remotePort *models.DevicePort if conn.PortID1 != nil && *conn.PortID1 == clickedPortID { if conn.PortID2 != nil { remotePort, _ = store.PortGetByID(*conn.PortID2) } } else if conn.PortID2 != nil && *conn.PortID2 == clickedPortID { if conn.PortID1 != nil { remotePort, _ = store.PortGetByID(*conn.PortID1) } } if remotePort != nil { result.FarSegments = traceAway(store, remotePort.ID, conn.ID) } result.NearSegments = traceThrough(store, clickedPortID, conn.ID) return result, nil } func traceAway(store *db.Store, portID int64, excludeConnID int64) []Segment { var segments []Segment visitedPorts := map[int64]bool{} currentPortID := portID for { if visitedPorts[currentPortID] { break } visitedPorts[currentPortID] = true port, err := store.PortGetByID(currentPortID) if err != nil || port == nil { break } device, err := store.DeviceGetByID(port.DeviceID) if err != nil || device == nil { break } modelName := "" if device.Model != nil { modelName = device.Model.Name } segments = append(segments, Segment{ DeviceID: device.ID, DeviceName: device.Name, ModelName: modelName, PortID: port.ID, PortName: port.Name, Side: port.Side, }) isPatch := device.Model != nil && (device.Model.IsPatchPanel || device.Model.IsWallSocket) if !isPatch { break } pairedSide := "back" if port.Side == "back" { pairedSide = "front" } pairedPort, err := store.PortGetPaired(port.DeviceID, port.Name, pairedSide) if err != nil || pairedPort == nil { break } if visitedPorts[pairedPort.ID] { break } nextConn, err := store.ConnectionGetByPortIDExcluding(pairedPort.ID, excludeConnID) if err != nil || nextConn == nil { break } var nextPortID int64 if nextConn.PortID1 != nil && *nextConn.PortID1 != pairedPort.ID { nextPortID = *nextConn.PortID1 } else if nextConn.PortID2 != nil && *nextConn.PortID2 != pairedPort.ID { nextPortID = *nextConn.PortID2 } else { break } excludeConnID = nextConn.ID currentPortID = nextPortID } return segments } func traceThrough(store *db.Store, portID int64, excludeConnID int64) []Segment { var segments []Segment port, err := store.PortGetByID(portID) if err != nil || port == nil { return segments } device, err := store.DeviceGetByID(port.DeviceID) if err != nil || device == nil { return segments } isPatch := device.Model != nil && (device.Model.IsPatchPanel || device.Model.IsWallSocket) if !isPatch { return segments } pairedSide := "back" if port.Side == "back" { pairedSide = "front" } pairedPort, err := store.PortGetPaired(port.DeviceID, port.Name, pairedSide) if err != nil || pairedPort == nil { return segments } nextConn, err := store.ConnectionGetByPortIDExcluding(pairedPort.ID, excludeConnID) if err != nil || nextConn == nil { return segments } var nextPortID int64 if nextConn.PortID1 != nil && *nextConn.PortID1 != pairedPort.ID { nextPortID = *nextConn.PortID1 } else if nextConn.PortID2 != nil && *nextConn.PortID2 != pairedPort.ID { nextPortID = *nextConn.PortID2 } else { return segments } return traceAway(store, nextPortID, nextConn.ID) }