299 lines
8.8 KiB
Go
299 lines
8.8 KiB
Go
package entity
|
|
|
|
import (
|
|
"database/sql/driver"
|
|
"encoding/json"
|
|
"fmt"
|
|
"sort"
|
|
"time"
|
|
|
|
"github.com/google/uuid"
|
|
)
|
|
|
|
type PortStatus string
|
|
|
|
const (
|
|
PortStatusOn PortStatus = "on"
|
|
PortStatusOff PortStatus = "off"
|
|
PortStatusDyingGasp PortStatus = "dyingGasp"
|
|
PortStatusLOS PortStatus = "los"
|
|
)
|
|
|
|
type StringSlice []string
|
|
|
|
func (s StringSlice) Value() (driver.Value, error) {
|
|
if len(s) == 0 {
|
|
return "[]", nil
|
|
}
|
|
return json.Marshal(s)
|
|
}
|
|
|
|
func (s *StringSlice) Scan(value interface{}) error {
|
|
if value == nil {
|
|
*s = StringSlice{}
|
|
return nil
|
|
}
|
|
|
|
var bytes []byte
|
|
switch v := value.(type) {
|
|
case []byte: // []byte and []uint8 are the same type in Go
|
|
bytes = v
|
|
case string:
|
|
bytes = []byte(v)
|
|
default:
|
|
return fmt.Errorf("cannot scan %T into StringSlice", value)
|
|
}
|
|
|
|
// Handle empty string case
|
|
if len(bytes) == 0 {
|
|
*s = StringSlice{}
|
|
return nil
|
|
}
|
|
|
|
return json.Unmarshal(bytes, s)
|
|
}
|
|
|
|
type PortAssignment struct {
|
|
PortNumber int `json:"port_number"`
|
|
CustomerName *string `json:"customer_name"` // Nullable for empty ports
|
|
Status PortStatus `json:"status"` // Add status field
|
|
Bandwidth *string `json:"bandwidth"` // Add bandwidth field (nullable)
|
|
}
|
|
|
|
type PortAssignmentResponse struct {
|
|
PortNumber int `json:"port_number"`
|
|
CustomerName *string `json:"customer_name"`
|
|
IsOccupied bool `json:"is_occupied"`
|
|
Status PortStatus `json:"status"` // Add status field
|
|
Bandwidth *string `json:"bandwidth"` // Add bandwidth field
|
|
}
|
|
|
|
type PortAssignments []PortAssignment
|
|
|
|
func (p PortAssignments) Value() (driver.Value, error) {
|
|
if len(p) == 0 {
|
|
return "[]", nil
|
|
}
|
|
return json.Marshal(p)
|
|
}
|
|
|
|
func (p *PortAssignments) Scan(value interface{}) error {
|
|
if value == nil {
|
|
*p = PortAssignments{}
|
|
return nil
|
|
}
|
|
|
|
var bytes []byte
|
|
switch v := value.(type) {
|
|
case []byte:
|
|
bytes = v
|
|
case string:
|
|
bytes = []byte(v)
|
|
default:
|
|
return fmt.Errorf("cannot scan %T into PortAssignments", value)
|
|
}
|
|
|
|
if len(bytes) == 0 {
|
|
*p = PortAssignments{}
|
|
return nil
|
|
}
|
|
|
|
return json.Unmarshal(bytes, p)
|
|
}
|
|
|
|
type DevicePort struct {
|
|
ID uuid.UUID `json:"id" gorm:"type:uuid;primaryKey"`
|
|
DeviceID uuid.UUID `json:"device_id" gorm:"type:uuid;not null"`
|
|
PortUsed int `json:"port_used"`
|
|
PortAvailable int `json:"port_available"`
|
|
CustomerCount int `json:"customer_count"`
|
|
CustomerNames StringSlice `json:"customer_names" gorm:"type:jsonb"`
|
|
PortAssignments PortAssignments `json:"port_assignments" gorm:"type:jsonb"`
|
|
CreatedAt time.Time `json:"created_at"`
|
|
UpdatedAt time.Time `json:"updated_at"`
|
|
|
|
// Relationships
|
|
Device Device `json:"device" gorm:"foreignKey:DeviceID"`
|
|
}
|
|
|
|
// Helper method to get customer names with port numbers
|
|
func (dp *DevicePort) GetCustomerNamesWithPorts() []string {
|
|
if dp.Device.DeviceType == "OTB" {
|
|
return []string{} // OTB devices do not have port assignments
|
|
}
|
|
var result []string
|
|
for _, assignment := range dp.PortAssignments {
|
|
if assignment.CustomerName != nil && *assignment.CustomerName != "" {
|
|
result = append(result, fmt.Sprintf("%d: %s", assignment.PortNumber, *assignment.CustomerName))
|
|
}
|
|
}
|
|
// If no port assignments, fall back to customer names
|
|
if len(result) == 0 {
|
|
for i, name := range dp.CustomerNames {
|
|
if name != "" {
|
|
result = append(result, fmt.Sprintf("%d: %s", i+1, name))
|
|
}
|
|
}
|
|
}
|
|
return result
|
|
}
|
|
|
|
// Helper method to get only customer names
|
|
func (dp *DevicePort) GetCustomerNames() []string {
|
|
if dp.Device.DeviceType == "OTB" {
|
|
return []string{} // OTB devices do not have port assignments
|
|
}
|
|
var result []string
|
|
for _, assignment := range dp.PortAssignments {
|
|
if assignment.CustomerName != nil && *assignment.CustomerName != "" {
|
|
result = append(result, *assignment.CustomerName)
|
|
}
|
|
}
|
|
// If no port assignments, fall back to customer names
|
|
if len(result) == 0 {
|
|
for _, name := range dp.CustomerNames {
|
|
if name != "" {
|
|
result = append(result, name)
|
|
}
|
|
}
|
|
}
|
|
return result
|
|
}
|
|
|
|
func (dp *DevicePort) GetPortAssignmentsWithDetails() []PortAssignmentResponse {
|
|
// If port_used is 0, return empty array
|
|
|
|
if dp.Device.DeviceType == "OTB" {
|
|
return []PortAssignmentResponse{}
|
|
}
|
|
|
|
if dp.PortUsed == 0 {
|
|
return []PortAssignmentResponse{}
|
|
}
|
|
|
|
var result []PortAssignmentResponse
|
|
|
|
// Step 1: Create a map of all port assignments
|
|
portMap := make(map[int]*PortAssignment)
|
|
maxPort := 0
|
|
|
|
if len(dp.PortAssignments) > 0 {
|
|
for _, assignment := range dp.PortAssignments {
|
|
portMap[assignment.PortNumber] = &assignment
|
|
if assignment.PortNumber > maxPort {
|
|
maxPort = assignment.PortNumber
|
|
}
|
|
}
|
|
} else {
|
|
// Fallback: use CustomerNames array if PortAssignments is empty
|
|
for i, name := range dp.CustomerNames {
|
|
portNum := i + 1
|
|
if name != "" || portNum <= dp.PortUsed {
|
|
var customerName *string
|
|
if name != "" {
|
|
customerName = &name
|
|
}
|
|
assignment := PortAssignment{
|
|
PortNumber: portNum,
|
|
CustomerName: customerName,
|
|
Status: PortStatusOn, // Default status
|
|
Bandwidth: nil, // Default bandwidth
|
|
}
|
|
portMap[portNum] = &assignment
|
|
if portNum > maxPort {
|
|
maxPort = portNum
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Ensure maxPort is at least equal to port_used
|
|
if maxPort < dp.PortUsed {
|
|
maxPort = dp.PortUsed
|
|
}
|
|
|
|
// Step 2: Determine which ports should be occupied
|
|
portsWithCustomers := make([]int, 0)
|
|
for portNum, assignment := range portMap {
|
|
if assignment.CustomerName != nil && *assignment.CustomerName != "" {
|
|
portsWithCustomers = append(portsWithCustomers, portNum)
|
|
}
|
|
}
|
|
|
|
// Sort to get them in order
|
|
sort.Ints(portsWithCustomers)
|
|
|
|
// Step 3: Determine occupied ports
|
|
occupiedPorts := make(map[int]bool)
|
|
|
|
// All ports with customers are occupied
|
|
for _, portNum := range portsWithCustomers {
|
|
occupiedPorts[portNum] = true
|
|
}
|
|
|
|
// Fill remaining slots to reach port_used
|
|
occupiedCount := len(portsWithCustomers)
|
|
if occupiedCount < dp.PortUsed {
|
|
// Need to occupy more ports (ports without customer names but still occupied)
|
|
for i := 1; i <= maxPort && occupiedCount < dp.PortUsed; i++ {
|
|
if !occupiedPorts[i] {
|
|
occupiedPorts[i] = true
|
|
occupiedCount++
|
|
}
|
|
}
|
|
}
|
|
|
|
// Step 4: Create the result for ALL ports up to maxPort
|
|
for i := 1; i <= maxPort; i++ {
|
|
var customerName *string
|
|
var status PortStatus = PortStatusOff // Default status for unoccupied ports
|
|
var bandwidth *string
|
|
|
|
if assignment := portMap[i]; assignment != nil {
|
|
customerName = assignment.CustomerName
|
|
status = assignment.Status
|
|
bandwidth = assignment.Bandwidth
|
|
} else if occupiedPorts[i] {
|
|
// Port is occupied but no assignment data - use defaults
|
|
status = PortStatusOn
|
|
}
|
|
|
|
result = append(result, PortAssignmentResponse{
|
|
PortNumber: i,
|
|
CustomerName: customerName,
|
|
IsOccupied: occupiedPorts[i],
|
|
Status: status,
|
|
Bandwidth: bandwidth,
|
|
})
|
|
}
|
|
|
|
return result
|
|
}
|
|
func (dp *DevicePort) GetCustomerNamesOnly() []string {
|
|
if dp.Device.DeviceType == "OTB" {
|
|
return []string{} // OTB devices do not have port assignments
|
|
}
|
|
var result []string
|
|
|
|
// If we have PortAssignments data, use it
|
|
if len(dp.PortAssignments) > 0 {
|
|
for _, assignment := range dp.PortAssignments {
|
|
if assignment.CustomerName != nil && *assignment.CustomerName != "" {
|
|
result = append(result, *assignment.CustomerName)
|
|
}
|
|
}
|
|
} else {
|
|
// Fallback: use CustomerNames directly
|
|
for _, name := range dp.CustomerNames {
|
|
if name != "" {
|
|
result = append(result, name)
|
|
}
|
|
}
|
|
}
|
|
|
|
return result
|
|
}
|
|
|
|
func (DevicePort) TableName() string {
|
|
return "device_ports"
|
|
} |