328 lines
6.7 KiB
Go
328 lines
6.7 KiB
Go
// backend\milestone_devices.go
|
|
|
|
package main
|
|
|
|
import (
|
|
"context"
|
|
"strings"
|
|
"unicode"
|
|
|
|
"github.com/jackc/pgx/v5"
|
|
)
|
|
|
|
func syncMilestoneHardwareDevices(
|
|
ctx context.Context,
|
|
tx pgx.Tx,
|
|
hardwareDevices []milestoneHardwareDevice,
|
|
) (int, error) {
|
|
syncedCount := 0
|
|
|
|
for _, hardwareDevice := range hardwareDevices {
|
|
hardwareDevice.HardwareID = strings.TrimSpace(hardwareDevice.HardwareID)
|
|
hardwareDevice.RecordingServerID = strings.TrimSpace(hardwareDevice.RecordingServerID)
|
|
hardwareDevice.DisplayName = strings.TrimSpace(hardwareDevice.DisplayName)
|
|
hardwareDevice.DeviceName = strings.TrimSpace(hardwareDevice.DeviceName)
|
|
hardwareDevice.Manufacturer = strings.TrimSpace(hardwareDevice.Manufacturer)
|
|
hardwareDevice.DeviceModelName = strings.TrimSpace(hardwareDevice.DeviceModelName)
|
|
hardwareDevice.SerialNumber = strings.TrimSpace(hardwareDevice.SerialNumber)
|
|
hardwareDevice.MacAddress = strings.TrimSpace(hardwareDevice.MacAddress)
|
|
hardwareDevice.IPAddress = strings.TrimSpace(hardwareDevice.IPAddress)
|
|
hardwareDevice.Port = strings.TrimSpace(hardwareDevice.Port)
|
|
hardwareDevice.FirmwareVersion = strings.TrimSpace(hardwareDevice.FirmwareVersion)
|
|
|
|
if hardwareDevice.HardwareID == "" {
|
|
continue
|
|
}
|
|
|
|
if hardwareDevice.Manufacturer != "" {
|
|
if err := ensureOptionalDeviceLookupValue(
|
|
ctx,
|
|
tx,
|
|
"device_manufacturers",
|
|
hardwareDevice.Manufacturer,
|
|
); err != nil {
|
|
return syncedCount, err
|
|
}
|
|
}
|
|
|
|
synced, err := createOrUpdateMilestoneHardwareDevice(ctx, tx, hardwareDevice)
|
|
if err != nil {
|
|
return syncedCount, err
|
|
}
|
|
|
|
if synced {
|
|
syncedCount++
|
|
}
|
|
}
|
|
|
|
return syncedCount, nil
|
|
}
|
|
|
|
func createOrUpdateMilestoneHardwareDevice(
|
|
ctx context.Context,
|
|
tx pgx.Tx,
|
|
hardwareDevice milestoneHardwareDevice,
|
|
) (bool, error) {
|
|
inventoryNumber, err := buildMilestoneInventoryNumber(ctx, tx, hardwareDevice)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
|
|
commandTag, err := tx.Exec(
|
|
ctx,
|
|
`
|
|
INSERT INTO devices (
|
|
inventory_number,
|
|
manufacturer,
|
|
model,
|
|
serial_number,
|
|
mac_address,
|
|
ip_address,
|
|
milestone_port,
|
|
location,
|
|
loan_status,
|
|
is_bao_device,
|
|
comment,
|
|
firmware_version,
|
|
milestone_recording_server_id,
|
|
milestone_hardware_id,
|
|
milestone_display_name,
|
|
milestone_enabled,
|
|
milestone_synced_at
|
|
)
|
|
VALUES (
|
|
$1,
|
|
$2,
|
|
$3,
|
|
$4,
|
|
$5,
|
|
$6,
|
|
$7,
|
|
'',
|
|
'Verfügbar',
|
|
false,
|
|
'',
|
|
$8,
|
|
$9,
|
|
$10,
|
|
$11,
|
|
$12,
|
|
now()
|
|
)
|
|
ON CONFLICT (milestone_hardware_id)
|
|
WHERE milestone_hardware_id <> ''
|
|
DO UPDATE SET
|
|
inventory_number = EXCLUDED.inventory_number,
|
|
manufacturer = EXCLUDED.manufacturer,
|
|
model = EXCLUDED.model,
|
|
serial_number = EXCLUDED.serial_number,
|
|
mac_address = EXCLUDED.mac_address,
|
|
ip_address = EXCLUDED.ip_address,
|
|
milestone_port = EXCLUDED.milestone_port,
|
|
firmware_version = EXCLUDED.firmware_version,
|
|
milestone_recording_server_id = EXCLUDED.milestone_recording_server_id,
|
|
milestone_display_name = EXCLUDED.milestone_display_name,
|
|
milestone_enabled = EXCLUDED.milestone_enabled,
|
|
milestone_synced_at = now(),
|
|
updated_at = now()
|
|
`,
|
|
inventoryNumber,
|
|
hardwareDevice.Manufacturer,
|
|
hardwareDevice.DeviceModelName,
|
|
hardwareDevice.SerialNumber,
|
|
hardwareDevice.MacAddress,
|
|
hardwareDevice.IPAddress,
|
|
hardwareDevice.Port,
|
|
hardwareDevice.FirmwareVersion,
|
|
hardwareDevice.RecordingServerID,
|
|
hardwareDevice.HardwareID,
|
|
hardwareDevice.DisplayName,
|
|
hardwareDevice.Enabled,
|
|
)
|
|
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
|
|
return commandTag.RowsAffected() > 0, nil
|
|
}
|
|
|
|
func normalizeMilestoneManufacturer(value string, model string) string {
|
|
value = strings.TrimSpace(value)
|
|
if value == "" {
|
|
return ""
|
|
}
|
|
|
|
model = strings.TrimSpace(model)
|
|
normalizedValue := strings.ToLower(value)
|
|
|
|
if strings.EqualFold(value, model) ||
|
|
strings.HasSuffix(normalizedValue, " device") ||
|
|
strings.Contains(normalizedValue, " camera") ||
|
|
strings.Contains(normalizedValue, " encoder") ||
|
|
strings.Contains(normalizedValue, " video") {
|
|
if manufacturer := extractMilestoneManufacturerFromName(value); manufacturer != "" {
|
|
return manufacturer
|
|
}
|
|
}
|
|
|
|
return value
|
|
}
|
|
|
|
func extractMilestoneManufacturerFromName(value string) string {
|
|
value = strings.TrimSpace(value)
|
|
if value == "" {
|
|
return ""
|
|
}
|
|
|
|
parts := strings.FieldsFunc(value, func(r rune) bool {
|
|
return unicode.IsSpace(r) ||
|
|
r == '_' ||
|
|
r == '-' ||
|
|
r == '/' ||
|
|
r == '\\' ||
|
|
r == '(' ||
|
|
r == ')' ||
|
|
r == ','
|
|
})
|
|
|
|
ignoredWords := map[string]bool{
|
|
"device": true,
|
|
"camera": true,
|
|
"video": true,
|
|
"encoder": true,
|
|
"ip": true,
|
|
"network": true,
|
|
"hardware": true,
|
|
}
|
|
|
|
for _, part := range parts {
|
|
part = strings.TrimSpace(part)
|
|
|
|
if part == "" {
|
|
continue
|
|
}
|
|
|
|
if isOnlyDigits(part) {
|
|
continue
|
|
}
|
|
|
|
if ignoredWords[strings.ToLower(part)] {
|
|
continue
|
|
}
|
|
|
|
return part
|
|
}
|
|
|
|
return ""
|
|
}
|
|
|
|
func buildMilestoneInventoryNumber(
|
|
ctx context.Context,
|
|
tx pgx.Tx,
|
|
hardwareDevice milestoneHardwareDevice,
|
|
) (string, error) {
|
|
inventoryNumber := extractInventoryNumberFromMilestoneDeviceName(
|
|
hardwareDevice.DeviceName,
|
|
)
|
|
|
|
if inventoryNumber == "" {
|
|
inventoryNumber = extractInventoryNumberFromMilestoneDeviceName(
|
|
hardwareDevice.DisplayName,
|
|
)
|
|
}
|
|
|
|
if inventoryNumber == "" {
|
|
inventoryNumber = buildGeneratedMilestoneInventoryNumber(
|
|
hardwareDevice.HardwareID,
|
|
)
|
|
}
|
|
|
|
var exists bool
|
|
|
|
err := tx.QueryRow(
|
|
ctx,
|
|
`
|
|
SELECT EXISTS(
|
|
SELECT 1
|
|
FROM devices
|
|
WHERE lower(inventory_number) = lower($1)
|
|
AND milestone_hardware_id <> $2
|
|
)
|
|
`,
|
|
inventoryNumber,
|
|
hardwareDevice.HardwareID,
|
|
).Scan(&exists)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
if !exists {
|
|
return inventoryNumber, nil
|
|
}
|
|
|
|
suffix := strings.ToUpper(strings.ReplaceAll(strings.TrimSpace(hardwareDevice.HardwareID), "-", ""))
|
|
if len(suffix) > 8 {
|
|
suffix = suffix[:8]
|
|
}
|
|
|
|
if suffix == "" {
|
|
suffix = "MS"
|
|
}
|
|
|
|
return inventoryNumber + "-" + suffix, nil
|
|
}
|
|
|
|
func extractInventoryNumberFromMilestoneDeviceName(value string) string {
|
|
value = strings.TrimSpace(value)
|
|
if value == "" {
|
|
return ""
|
|
}
|
|
|
|
parts := strings.FieldsFunc(value, func(r rune) bool {
|
|
return r == '_' || r == '-' || unicode.IsSpace(r)
|
|
})
|
|
|
|
for _, part := range parts {
|
|
part = strings.TrimSpace(part)
|
|
|
|
if part == "" {
|
|
continue
|
|
}
|
|
|
|
if isOnlyDigits(part) {
|
|
return part
|
|
}
|
|
}
|
|
|
|
return ""
|
|
}
|
|
|
|
func isOnlyDigits(value string) bool {
|
|
if value == "" {
|
|
return false
|
|
}
|
|
|
|
for _, r := range value {
|
|
if r < '0' || r > '9' {
|
|
return false
|
|
}
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
func buildGeneratedMilestoneInventoryNumber(hardwareID string) string {
|
|
value := strings.ToUpper(strings.ReplaceAll(strings.TrimSpace(hardwareID), "-", ""))
|
|
|
|
if len(value) > 12 {
|
|
value = value[:12]
|
|
}
|
|
|
|
if value == "" {
|
|
value = "UNKNOWN"
|
|
}
|
|
|
|
return "MS-" + value
|
|
}
|