fix(exporter): assign PSU slot positions by serial order, not encounter order
disambiguatePSUSlots previously renumbered colliding PSU slots in whatever order the devices happened to appear in after parsing/dedup, which isn't guaranteed stable across runs. Sort each colliding group by serial number ascending before assigning 0-based positions, so the same PSU always lands in the same slot on re-conversion regardless of incidental ordering upstream. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
a590242382
commit
6827c1fe20
@@ -2188,26 +2188,29 @@ func dedupePSUs(items []ReanimatorPSU) []ReanimatorPSU {
|
|||||||
// string from the source. Some collectors (e.g. BEE-SP) report every PSU
|
// string from the source. Some collectors (e.g. BEE-SP) report every PSU
|
||||||
// at slot "0". A shared slot value prevents Reanimator from telling the
|
// at slot "0". A shared slot value prevents Reanimator from telling the
|
||||||
// PSUs apart by installed position, so they land in "other components"
|
// PSUs apart by installed position, so they land in "other components"
|
||||||
// instead of being tracked as PSU installations. Reassigning stable 0-based
|
// instead of being tracked as PSU installations. Within each colliding
|
||||||
// positions by encounter order is the best available disambiguation without
|
// group, positions are assigned by ascending serial number rather than
|
||||||
// inventing a serial or other identity data we don't have.
|
// source encounter order, so the result is deterministic and reproducible
|
||||||
|
// across re-imports instead of depending on incidental parse ordering.
|
||||||
func disambiguatePSUSlots(items []ReanimatorPSU) []ReanimatorPSU {
|
func disambiguatePSUSlots(items []ReanimatorPSU) []ReanimatorPSU {
|
||||||
if len(items) < 2 {
|
if len(items) < 2 {
|
||||||
return items
|
return items
|
||||||
}
|
}
|
||||||
counts := make(map[string]int, len(items))
|
groups := make(map[string][]int, len(items))
|
||||||
for _, it := range items {
|
for i, it := range items {
|
||||||
counts[strings.ToLower(strings.TrimSpace(it.Slot))]++
|
key := strings.ToLower(strings.TrimSpace(it.Slot))
|
||||||
|
groups[key] = append(groups[key], i)
|
||||||
}
|
}
|
||||||
next := make(map[string]int, len(items))
|
for _, idxs := range groups {
|
||||||
for i := range items {
|
if len(idxs) < 2 {
|
||||||
key := strings.ToLower(strings.TrimSpace(items[i].Slot))
|
|
||||||
if counts[key] < 2 {
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
idx := next[key]
|
sort.Slice(idxs, func(a, b int) bool {
|
||||||
next[key]++
|
return strings.ToLower(items[idxs[a]].SerialNumber) < strings.ToLower(items[idxs[b]].SerialNumber)
|
||||||
items[i].Slot = strconv.Itoa(idx)
|
})
|
||||||
|
for pos, idx := range idxs {
|
||||||
|
items[idx].Slot = strconv.Itoa(pos)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return items
|
return items
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user