From 391d4976f1cfff482d4aa72eddace9deb697b47c Mon Sep 17 00:00:00 2001 From: Matthias Emde Date: Sun, 16 Nov 2025 21:00:19 +0100 Subject: [PATCH 1/7] feat: add inventory collectors This commit adds collectors for the four inventory endpoints: /getCloudInv /getCrateInv /getStorageInv /getWorldInv --- Companion/exporter/exporter.go | 27 +++++- .../exporter/inventory_collector_cloud.go | 31 +++++++ .../exporter/inventory_collector_crate.go | 68 +++++++++++++++ .../exporter/inventory_collector_storage.go | 68 +++++++++++++++ .../exporter/inventory_collector_world.go | 31 +++++++ Companion/exporter/inventory_detail.go | 13 +++ Companion/exporter/inventory_metrics.go | 83 +++++++++++++++++++ 7 files changed, 320 insertions(+), 1 deletion(-) create mode 100644 Companion/exporter/inventory_collector_cloud.go create mode 100644 Companion/exporter/inventory_collector_crate.go create mode 100644 Companion/exporter/inventory_collector_storage.go create mode 100644 Companion/exporter/inventory_collector_world.go create mode 100644 Companion/exporter/inventory_detail.go create mode 100644 Companion/exporter/inventory_metrics.go diff --git a/Companion/exporter/exporter.go b/Companion/exporter/exporter.go index df3131b..2bdd2e5 100644 --- a/Companion/exporter/exporter.go +++ b/Companion/exporter/exporter.go @@ -43,7 +43,32 @@ func NewPrometheusExporter(frmApiHosts []string) *PrometheusExporter { portalCollector := NewPortalCollector("/getPortal") hypertubeCollector := NewHypertubeCollector("/getHyperEntrance") frackingCollector := NewFrackingCollector("/getFrackingActivator") - collectorRunners = append(collectorRunners, NewCollectorRunner(ctx, frmApiHost, productionCollector, powerCollector, buildingCollector, vehicleCollector, trainCollector, droneCollector, vehicleStationCollector, trainStationCollector, resourceSinkCollector, pumpCollector, extractorCollector, portalCollector, hypertubeCollector, frackingCollector)) + cloudInventoryCollector := NewCloudInventoryCollector("/getCloudInv") + worldInventoryCollector := NewWorldInventoryCollector("/getWorldInv") + storageInventoryCollector := NewStorageInventoryCollector("/getStorageInv") + crateInventoryCollector := NewCrateInventoryCollector("/getCrateInv") + collectorRunners = append(collectorRunners, NewCollectorRunner( + ctx, + frmApiHost, + productionCollector, + powerCollector, + buildingCollector, + vehicleCollector, + trainCollector, + droneCollector, + vehicleStationCollector, + trainStationCollector, + resourceSinkCollector, + pumpCollector, + extractorCollector, + portalCollector, + hypertubeCollector, + frackingCollector, + cloudInventoryCollector, + storageInventoryCollector, + crateInventoryCollector, + worldInventoryCollector, + )) } return &PrometheusExporter{ diff --git a/Companion/exporter/inventory_collector_cloud.go b/Companion/exporter/inventory_collector_cloud.go new file mode 100644 index 0000000..2049aad --- /dev/null +++ b/Companion/exporter/inventory_collector_cloud.go @@ -0,0 +1,31 @@ +package exporter + +import ( + "log" +) + +type CloudInventoryCollector struct { + endpoint string +} + +func NewCloudInventoryCollector(endpoint string) *CloudInventoryCollector { + return &CloudInventoryCollector{ + endpoint: endpoint, + } +} + +func (c *CloudInventoryCollector) Collect(frmAddress string, sessionName string) { + items := []InventoryItem{} + err := retrieveData(frmAddress, c.endpoint, &items) + if err != nil { + log.Printf("error reading inventory statistics from FRM: %s\n", err) + return + } + + for _, item := range items { + CloudInventory.WithLabelValues(item.Name, frmAddress, sessionName).Set(float64(item.Amount)) + CloudInventoryMax.WithLabelValues(item.Name, frmAddress, sessionName).Set(float64(item.MaxAmount)) + } +} + +func (c *CloudInventoryCollector) DropCache() {} diff --git a/Companion/exporter/inventory_collector_crate.go b/Companion/exporter/inventory_collector_crate.go new file mode 100644 index 0000000..464051c --- /dev/null +++ b/Companion/exporter/inventory_collector_crate.go @@ -0,0 +1,68 @@ +package exporter + +import ( + "log" + "strconv" + + "github.com/prometheus/client_golang/prometheus" +) + +type CrateInventoryCollector struct { + endpoint string + metricsDropper *MetricsDropper +} + +func NewCrateInventoryCollector(endpoint string) *CrateInventoryCollector { + return &CrateInventoryCollector{ + endpoint: endpoint, + metricsDropper: NewMetricsDropper( + StorageInventory, + StorageInventoryMax, + ), + } +} + +func (c *CrateInventoryCollector) Collect(frmAddress string, sessionName string) { + details := []ContainerDetail{} + err := retrieveData(frmAddress, c.endpoint, &details) + if err != nil { + c.metricsDropper.DropStaleMetricLabels() + log.Printf("error reading inventory statistics from FRM: %s\n", err) + return + } + + for _, detail := range details { + c.metricsDropper.CacheFreshMetricLabel(prometheus.Labels{ + "url": frmAddress, + "session_name": sessionName, + "container_name": detail.Name, + "x": strconv.FormatFloat(detail.Location.X, 'f', -1, 64), + "y": strconv.FormatFloat(detail.Location.Y, 'f', -1, 64), + "z": strconv.FormatFloat(detail.Location.Z, 'f', -1, 64), + }) + for _, item := range detail.Inventory { + CrateInventory.WithLabelValues( + item.Name, + detail.Name, + strconv.FormatFloat(detail.Location.X, 'f', -1, 64), + strconv.FormatFloat(detail.Location.Y, 'f', -1, 64), + strconv.FormatFloat(detail.Location.Z, 'f', -1, 64), + frmAddress, + sessionName, + ).Set(float64(item.Amount)) + + CrateInventoryMax.WithLabelValues( + item.Name, + detail.Name, + strconv.FormatFloat(detail.Location.X, 'f', -1, 64), + strconv.FormatFloat(detail.Location.Y, 'f', -1, 64), + strconv.FormatFloat(detail.Location.Z, 'f', -1, 64), + frmAddress, + sessionName, + ).Set(float64(item.MaxAmount)) + } + } + c.metricsDropper.DropStaleMetricLabels() +} + +func (c *CrateInventoryCollector) DropCache() {} diff --git a/Companion/exporter/inventory_collector_storage.go b/Companion/exporter/inventory_collector_storage.go new file mode 100644 index 0000000..5ff3b68 --- /dev/null +++ b/Companion/exporter/inventory_collector_storage.go @@ -0,0 +1,68 @@ +package exporter + +import ( + "log" + "strconv" + + "github.com/prometheus/client_golang/prometheus" +) + +type StorageInventoryCollector struct { + endpoint string + metricsDropper *MetricsDropper +} + +func NewStorageInventoryCollector(endpoint string) *StorageInventoryCollector { + return &StorageInventoryCollector{ + endpoint: endpoint, + metricsDropper: NewMetricsDropper( + StorageInventory, + StorageInventoryMax, + ), + } +} + +func (c *StorageInventoryCollector) Collect(frmAddress string, sessionName string) { + details := []ContainerDetail{} + err := retrieveData(frmAddress, c.endpoint, &details) + if err != nil { + c.metricsDropper.DropStaleMetricLabels() + log.Printf("error reading inventory statistics from FRM: %s\n", err) + return + } + + for _, detail := range details { + c.metricsDropper.CacheFreshMetricLabel(prometheus.Labels{ + "url": frmAddress, + "session_name": sessionName, + "container_name": detail.Name, + "x": strconv.FormatFloat(detail.Location.X, 'f', -1, 64), + "y": strconv.FormatFloat(detail.Location.Y, 'f', -1, 64), + "z": strconv.FormatFloat(detail.Location.Z, 'f', -1, 64), + }) + for _, item := range detail.Inventory { + StorageInventory.WithLabelValues( + item.Name, + detail.Name, + strconv.FormatFloat(detail.Location.X, 'f', -1, 64), + strconv.FormatFloat(detail.Location.Y, 'f', -1, 64), + strconv.FormatFloat(detail.Location.Z, 'f', -1, 64), + frmAddress, + sessionName, + ).Set(float64(item.Amount)) + + StorageInventoryMax.WithLabelValues( + item.Name, + detail.Name, + strconv.FormatFloat(detail.Location.X, 'f', -1, 64), + strconv.FormatFloat(detail.Location.Y, 'f', -1, 64), + strconv.FormatFloat(detail.Location.Z, 'f', -1, 64), + frmAddress, + sessionName, + ).Set(float64(item.MaxAmount)) + } + } + c.metricsDropper.DropStaleMetricLabels() +} + +func (c *StorageInventoryCollector) DropCache() {} diff --git a/Companion/exporter/inventory_collector_world.go b/Companion/exporter/inventory_collector_world.go new file mode 100644 index 0000000..cb89344 --- /dev/null +++ b/Companion/exporter/inventory_collector_world.go @@ -0,0 +1,31 @@ +package exporter + +import ( + "log" +) + +type WorldInventoryCollector struct { + endpoint string +} + +func NewWorldInventoryCollector(endpoint string) *WorldInventoryCollector { + return &WorldInventoryCollector{ + endpoint: endpoint, + } +} + +func (c *WorldInventoryCollector) Collect(frmAddress string, sessionName string) { + items := []InventoryItem{} + err := retrieveData(frmAddress, c.endpoint, &items) + if err != nil { + log.Printf("error reading inventory statistics from FRM: %s\n", err) + return + } + + for _, item := range items { + WorldInventory.WithLabelValues(item.Name, frmAddress, sessionName).Set(float64(item.Amount)) + WorldInventoryMax.WithLabelValues(item.Name, frmAddress, sessionName).Set(float64(item.MaxAmount)) + } +} + +func (c *WorldInventoryCollector) DropCache() {} diff --git a/Companion/exporter/inventory_detail.go b/Companion/exporter/inventory_detail.go new file mode 100644 index 0000000..a934788 --- /dev/null +++ b/Companion/exporter/inventory_detail.go @@ -0,0 +1,13 @@ +package exporter + +type InventoryItem struct { + Name string `json:"Name"` + Amount int `json:"Amount"` + MaxAmount int `json:"MaxAmount"` +} + +type ContainerDetail struct { + Name string `json:"Name"` + Location Location `json:"location"` + Inventory []InventoryItem `json:"Inventory"` +} diff --git a/Companion/exporter/inventory_metrics.go b/Companion/exporter/inventory_metrics.go new file mode 100644 index 0000000..72c53a0 --- /dev/null +++ b/Companion/exporter/inventory_metrics.go @@ -0,0 +1,83 @@ +package exporter + +import ( + "github.com/prometheus/client_golang/prometheus" +) + +var ( + // Cloud Inventory (Dimensional Depot) + CloudInventory = RegisterNewGaugeVec(prometheus.GaugeOpts{ + Name: "cloud_inventory", + Help: "Items stored in the dimensional depot", + }, []string{ + "item_name", + }) + + CloudInventoryMax = RegisterNewGaugeVec(prometheus.GaugeOpts{ + Name: "cloud_inventory_max", + Help: "Stack size for items in the dimensional depot", + }, []string{ + "item_name", + }) + + // World Inventory + WorldInventory = RegisterNewGaugeVec(prometheus.GaugeOpts{ + Name: "world_inventory", + Help: "Inventory of the world regardless of location (All buildings whom purpose is to provide storage)", + }, []string{ + "item_name", + }) + + WorldInventoryMax = RegisterNewGaugeVec(prometheus.GaugeOpts{ + Name: "world_inventory_max", + Help: "Stack size for items in the world invetory", + }, []string{ + "item_name", + }) + + // Storage Container Inventory + StorageInventory = RegisterNewGaugeVec(prometheus.GaugeOpts{ + Name: "storage_inventory", + Help: "Items stored inside storage containers", + }, []string{ + "item_name", + "container_name", + "x", + "y", + "z", + }) + + StorageInventoryMax = RegisterNewGaugeVec(prometheus.GaugeOpts{ + Name: "storage_inventory_max", + Help: "Stack size for items stored in storage containers", + }, []string{ + "item_name", + "container_name", + "x", + "y", + "z", + }) + + // Crate Inventory (Dismantle and Death Crates) + CrateInventory = RegisterNewGaugeVec(prometheus.GaugeOpts{ + Name: "crate_inventory", + Help: "Items stored inside crates", + }, []string{ + "item_name", + "container_name", + "x", + "y", + "z", + }) + + CrateInventoryMax = RegisterNewGaugeVec(prometheus.GaugeOpts{ + Name: "crate_inventory_max", + Help: "Stack size for items stored in crates", + }, []string{ + "item_name", + "container_name", + "x", + "y", + "z", + }) +) From 189677010e75d464fc6c8b50204bdf2787183cac Mon Sep 17 00:00:00 2001 From: Matthias Emde Date: Fri, 21 Nov 2025 22:49:16 +0100 Subject: [PATCH 2/7] feat: add inventory metrics for machines --- Companion/exporter/factory_build_detail.go | 26 ++++++----- .../exporter/factory_building_collector.go | 37 +++++++++++++++ .../exporter/factory_building_metrics.go | 45 +++++++++++++++++++ Companion/exporter/inventory_detail.go | 6 +-- 4 files changed, 99 insertions(+), 15 deletions(-) diff --git a/Companion/exporter/factory_build_detail.go b/Companion/exporter/factory_build_detail.go index 1d167ea..b3ca23b 100644 --- a/Companion/exporter/factory_build_detail.go +++ b/Companion/exporter/factory_build_detail.go @@ -1,18 +1,20 @@ package exporter type BuildingDetail struct { - Building string `json:"Name"` - Location Location `json:"location"` - Recipe string `json:"Recipe"` - Production []Production `json:"production"` - Ingredients []Ingredient `json:"ingredients"` - ManuSpeed float64 `json:"ManuSpeed"` - IsConfigured bool `json:"IsConfigured"` - IsProducing bool `json:"IsProducing"` - IsPaused bool `json:"IsPaused"` - CircuitGroupId int `json:"CircuitGroupID"` - PowerInfo PowerInfo `json:"PowerInfo"` - Somersloops float64 `json:"Somersloops"` + Building string `json:"Name"` + Location Location `json:"location"` + Recipe string `json:"Recipe"` + Production []Production `json:"production"` + Ingredients []Ingredient `json:"ingredients"` + ManuSpeed float64 `json:"ManuSpeed"` + IsConfigured bool `json:"IsConfigured"` + IsProducing bool `json:"IsProducing"` + IsPaused bool `json:"IsPaused"` + CircuitGroupId int `json:"CircuitGroupID"` + PowerInfo PowerInfo `json:"PowerInfo"` + Somersloops float64 `json:"Somersloops"` + InputInventory []InventoryItem `json:"InputInventory"` + OutputInventory []InventoryItem `json:"OutputInventory"` } type Production struct { diff --git a/Companion/exporter/factory_building_collector.go b/Companion/exporter/factory_building_collector.go index 1222dc9..9a171b0 100644 --- a/Companion/exporter/factory_building_collector.go +++ b/Companion/exporter/factory_building_collector.go @@ -57,6 +57,43 @@ func (c *FactoryBuildingCollector) Collect(frmAddress string, sessionName string strconv.FormatFloat(building.Location.Z, 'f', -1, 64), frmAddress, sessionName, ).Set(prod.ProdPercent) + + for _, item := range building.InputInventory { + MachineInputInventory.WithLabelValues( + item.Name, + building.Building, + strconv.FormatFloat(building.Location.X, 'f', -1, 64), + strconv.FormatFloat(building.Location.Y, 'f', -1, 64), + strconv.FormatFloat(building.Location.Z, 'f', -1, 64), + frmAddress, sessionName, + ).Set(float64(item.Amount)) + MachineInputInventoryMax.WithLabelValues( + item.Name, + building.Building, + strconv.FormatFloat(building.Location.X, 'f', -1, 64), + strconv.FormatFloat(building.Location.Y, 'f', -1, 64), + strconv.FormatFloat(building.Location.Z, 'f', -1, 64), + frmAddress, sessionName, + ).Set(float64(item.MaxAmount)) + } + + for _, item := range building.OutputInventory { + MachineOutputInventory.WithLabelValues( + item.Name, + building.Building, + strconv.FormatFloat(building.Location.X, 'f', -1, 64), + strconv.FormatFloat(building.Location.Y, 'f', -1, 64), + strconv.FormatFloat(building.Location.Z, 'f', -1, 64), + frmAddress, sessionName, + ).Set(float64(item.Amount)) + MachineOutputInventoryMax.WithLabelValues( + item.Name, + building.Building, + strconv.FormatFloat(building.Location.X, 'f', -1, 64), + strconv.FormatFloat(building.Location.Y, 'f', -1, 64), + strconv.FormatFloat(building.Location.Z, 'f', -1, 64), + frmAddress, sessionName, + ).Set(float64(item.MaxAmount)) } val, ok := powerInfo[building.PowerInfo.CircuitGroupId] diff --git a/Companion/exporter/factory_building_metrics.go b/Companion/exporter/factory_building_metrics.go index d8a9d59..638bd29 100644 --- a/Companion/exporter/factory_building_metrics.go +++ b/Companion/exporter/factory_building_metrics.go @@ -26,6 +26,51 @@ var ( "y", "z", }) + + MachineInputInventory = RegisterNewGaugeVec(prometheus.GaugeOpts{ + Name: "machine_input_inventory", + Help: "How much of an item a building has stored in its input", + }, []string{ + "item_name", + "machine_name", + "x", + "y", + "z", + }) + + MachineInputInventoryMax = RegisterNewGaugeVec(prometheus.GaugeOpts{ + Name: "machine_input_inventory_max", + Help: "How much of an item a building can store in its input", + }, []string{ + "item_name", + "machine_name", + "x", + "y", + "z", + }) + + MachineOutputInventory = RegisterNewGaugeVec(prometheus.GaugeOpts{ + Name: "machine_output_inventory", + Help: "How much of an item a building has stored in its output", + }, []string{ + "item_name", + "machine_name", + "x", + "y", + "z", + }) + + MachineOutputInventoryMax = RegisterNewGaugeVec(prometheus.GaugeOpts{ + Name: "machine_output_inventory_max", + Help: "How much of an item a building can store in its output", + }, []string{ + "item_name", + "machine_name", + "x", + "y", + "z", + }) + FactoryPower = RegisterNewGaugeVec(prometheus.GaugeOpts{ Name: "factory_power", Help: "Power draw from factory machines in MW. Does not include extractors.", diff --git a/Companion/exporter/inventory_detail.go b/Companion/exporter/inventory_detail.go index a934788..3745c36 100644 --- a/Companion/exporter/inventory_detail.go +++ b/Companion/exporter/inventory_detail.go @@ -1,9 +1,9 @@ package exporter type InventoryItem struct { - Name string `json:"Name"` - Amount int `json:"Amount"` - MaxAmount int `json:"MaxAmount"` + Name string `json:"Name"` + Amount float64 `json:"Amount"` + MaxAmount float64 `json:"MaxAmount"` } type ContainerDetail struct { From 92e47ac95da6fb7425873cc0207e595041f0a6e9 Mon Sep 17 00:00:00 2001 From: Matthias Emde Date: Fri, 21 Nov 2025 22:49:59 +0100 Subject: [PATCH 3/7] feat: add max production metric for machines --- Companion/exporter/factory_building_collector.go | 10 ++++++++++ Companion/exporter/factory_building_metrics.go | 11 +++++++++++ 2 files changed, 21 insertions(+) diff --git a/Companion/exporter/factory_building_collector.go b/Companion/exporter/factory_building_collector.go index 9a171b0..886ce5f 100644 --- a/Companion/exporter/factory_building_collector.go +++ b/Companion/exporter/factory_building_collector.go @@ -58,6 +58,16 @@ func (c *FactoryBuildingCollector) Collect(frmAddress string, sessionName string frmAddress, sessionName, ).Set(prod.ProdPercent) + MachineItemsProducedMax.WithLabelValues( + prod.Name, + building.Building, + strconv.FormatFloat(building.Location.X, 'f', -1, 64), + strconv.FormatFloat(building.Location.Y, 'f', -1, 64), + strconv.FormatFloat(building.Location.Z, 'f', -1, 64), + frmAddress, sessionName, + ).Set(prod.MaxProd) + } + for _, item := range building.InputInventory { MachineInputInventory.WithLabelValues( item.Name, diff --git a/Companion/exporter/factory_building_metrics.go b/Companion/exporter/factory_building_metrics.go index 638bd29..41ac7c8 100644 --- a/Companion/exporter/factory_building_metrics.go +++ b/Companion/exporter/factory_building_metrics.go @@ -27,6 +27,17 @@ var ( "z", }) + MachineItemsProducedMax = RegisterNewGaugeVec(prometheus.GaugeOpts{ + Name: "machine_items_produced_max", + Help: "The maximum of a certain item which the machine can produce", + }, []string{ + "item_name", + "machine_name", + "x", + "y", + "z", + }) + MachineInputInventory = RegisterNewGaugeVec(prometheus.GaugeOpts{ Name: "machine_input_inventory", Help: "How much of an item a building has stored in its input", From b7f124945e398d17d3c9d754a4909e5aa72d39c7 Mon Sep 17 00:00:00 2001 From: Matthias Emde Date: Sun, 23 Nov 2025 17:15:01 +0100 Subject: [PATCH 4/7] ci: add tests for machine metrics --- .../factory_building_collector_test.go | 179 ++++++++++++++++++ 1 file changed, 179 insertions(+) diff --git a/Companion/exporter/factory_building_collector_test.go b/Companion/exporter/factory_building_collector_test.go index 88d910f..41f9a39 100644 --- a/Companion/exporter/factory_building_collector_test.go +++ b/Companion/exporter/factory_building_collector_test.go @@ -61,6 +61,30 @@ var _ = Describe("FactoryBuildingCollector", func() { PowerConsumed: 23, MaxPowerConsumed: 4, }, + InputInventory: []exporter.InventoryItem{ + { + Name: "Iron Ore", + Amount: 64, + MaxAmount: 100, + }, + { + Name: "Second input", + Amount: 32, + MaxAmount: 1000, + }, + }, + OutputInventory: []exporter.InventoryItem{ + { + Name: "Iron Ingot", + Amount: 33, + MaxAmount: 200, + }, + { + Name: "Second output", + Amount: 44, + MaxAmount: 2000, + }, + }, }, }) }) @@ -112,6 +136,161 @@ var _ = Describe("FactoryBuildingCollector", func() { }) }) + Describe("Machine item max production metrics", func() { + It("records a metric with labels for the produced item name, machine type, and x, y, z coordinates", func() { + collector.Collect(url, sessionName) + metric, err := getMetric(exporter.MachineItemsProducedMax, "Iron Ingot", "Smelter", "100", "200", "-300", url, sessionName) + Expect(err).ToNot(HaveOccurred()) + Expect(metric).ToNot(BeNil()) + }) + + It("records the current max production as the metric value", func() { + collector.Collect(url, sessionName) + + val, err := gaugeValue(exporter.MachineItemsProducedMax, "Iron Ingot", "Smelter", "100", "200", "-300", url, sessionName) + Expect(err).ToNot(HaveOccurred()) + Expect(val).To(Equal(float64(10))) + }) + + Describe("when a machine has multiple outputs", func() { + It("records a metric per item", func() { + collector.Collect(url, sessionName) + + ironIngots, err := gaugeValue(exporter.MachineItemsProducedMax, "Iron Ingot", "Smelter", "100", "200", "-300", url, sessionName) + Expect(err).ToNot(HaveOccurred()) + Expect(ironIngots).To(Equal(float64(10.0))) + + ironNothing, err := gaugeValue(exporter.MachineItemsProducedMax, "Iron Nothing", "Smelter", "100", "200", "-300", url, sessionName) + Expect(err).ToNot(HaveOccurred()) + Expect(ironNothing).To(Equal(float64(4000.0))) + }) + }) + }) + + Describe("Machine input inventory metrics", func() { + It("records a metric with labels for the stored item name, machine type, and x, y, z coordinates", func() { + collector.Collect(url, sessionName) + metric, err := getMetric(exporter.MachineInputInventory, "Iron Ore", "Smelter", "100", "200", "-300", url, sessionName) + Expect(err).ToNot(HaveOccurred()) + Expect(metric).ToNot(BeNil()) + }) + + It("records the current input invetory as the metric value", func() { + collector.Collect(url, sessionName) + + val, err := gaugeValue(exporter.MachineInputInventory, "Iron Ore", "Smelter", "100", "200", "-300", url, sessionName) + Expect(err).ToNot(HaveOccurred()) + Expect(val).To(Equal(float64(64.0))) + }) + + Describe("when a machine has multiple inputs", func() { + It("records a metric per item", func() { + collector.Collect(url, sessionName) + + ironIngots, err := gaugeValue(exporter.MachineInputInventory, "Iron Ore", "Smelter", "100", "200", "-300", url, sessionName) + Expect(err).ToNot(HaveOccurred()) + Expect(ironIngots).To(Equal(float64(64.0))) + + ironNothing, err := gaugeValue(exporter.MachineInputInventory, "Second input", "Smelter", "100", "200", "-300", url, sessionName) + Expect(err).ToNot(HaveOccurred()) + Expect(ironNothing).To(Equal(float64(32.0))) + }) + }) + }) + + Describe("Machine input inventory max metrics", func() { + It("records a metric with labels for the stored item name, machine type, and x, y, z coordinates", func() { + collector.Collect(url, sessionName) + metric, err := getMetric(exporter.MachineInputInventoryMax, "Iron Ore", "Smelter", "100", "200", "-300", url, sessionName) + Expect(err).ToNot(HaveOccurred()) + Expect(metric).ToNot(BeNil()) + }) + + It("records the current input invetory max as the metric value", func() { + collector.Collect(url, sessionName) + + val, err := gaugeValue(exporter.MachineInputInventoryMax, "Iron Ore", "Smelter", "100", "200", "-300", url, sessionName) + Expect(err).ToNot(HaveOccurred()) + Expect(val).To(Equal(float64(100.0))) + }) + + Describe("when a machine has multiple inputs", func() { + It("records a metric per item", func() { + collector.Collect(url, sessionName) + + ironIngots, err := gaugeValue(exporter.MachineInputInventoryMax, "Iron Ore", "Smelter", "100", "200", "-300", url, sessionName) + Expect(err).ToNot(HaveOccurred()) + Expect(ironIngots).To(Equal(float64(100.0))) + + ironNothing, err := gaugeValue(exporter.MachineInputInventoryMax, "Second input", "Smelter", "100", "200", "-300", url, sessionName) + Expect(err).ToNot(HaveOccurred()) + Expect(ironNothing).To(Equal(float64(1000.0))) + }) + }) + }) + + Describe("Machine input inventory metrics", func() { + It("records a metric with labels for the stored item name, machine type, and x, y, z coordinates", func() { + collector.Collect(url, sessionName) + metric, err := getMetric(exporter.MachineInputInventory, "Iron Ingot", "Smelter", "100", "200", "-300", url, sessionName) + Expect(err).ToNot(HaveOccurred()) + Expect(metric).ToNot(BeNil()) + }) + + It("records the current output invetory as the metric value", func() { + collector.Collect(url, sessionName) + + val, err := gaugeValue(exporter.MachineOutputInventory, "Iron Ingot", "Smelter", "100", "200", "-300", url, sessionName) + Expect(err).ToNot(HaveOccurred()) + Expect(val).To(Equal(float64(33.0))) + }) + + Describe("when a machine has multiple outputs", func() { + It("records a metric per item", func() { + collector.Collect(url, sessionName) + + ironIngots, err := gaugeValue(exporter.MachineOutputInventory, "Iron Ingot", "Smelter", "100", "200", "-300", url, sessionName) + Expect(err).ToNot(HaveOccurred()) + Expect(ironIngots).To(Equal(float64(33.0))) + + ironNothing, err := gaugeValue(exporter.MachineOutputInventory, "Second output", "Smelter", "100", "200", "-300", url, sessionName) + Expect(err).ToNot(HaveOccurred()) + Expect(ironNothing).To(Equal(float64(44.0))) + }) + }) + }) + + Describe("Machine output inventory max metrics", func() { + It("records a metric with labels for the stored item name, machine type, and x, y, z coordinates", func() { + collector.Collect(url, sessionName) + metric, err := getMetric(exporter.MachineOutputInventoryMax, "Iron Ingot", "Smelter", "100", "200", "-300", url, sessionName) + Expect(err).ToNot(HaveOccurred()) + Expect(metric).ToNot(BeNil()) + }) + + It("records the current output invetory max as the metric value", func() { + collector.Collect(url, sessionName) + + val, err := gaugeValue(exporter.MachineOutputInventoryMax, "Iron Ingot", "Smelter", "100", "200", "-300", url, sessionName) + Expect(err).ToNot(HaveOccurred()) + Expect(val).To(Equal(float64(200.0))) + }) + + Describe("when a machine has multiple outputs", func() { + It("records a metric per item", func() { + collector.Collect(url, sessionName) + + ironIngots, err := gaugeValue(exporter.MachineOutputInventoryMax, "Iron Ingot", "Smelter", "100", "200", "-300", url, sessionName) + Expect(err).ToNot(HaveOccurred()) + Expect(ironIngots).To(Equal(float64(200.0))) + + ironNothing, err := gaugeValue(exporter.MachineOutputInventoryMax, "Second output", "Smelter", "100", "200", "-300", url, sessionName) + Expect(err).ToNot(HaveOccurred()) + Expect(ironNothing).To(Equal(float64(2000.0))) + }) + }) + }) + Describe("Machine item production efficiency metrics", func() { It("records a metric with labels for the produced item name, machine type, and x, y, z coordinates", func() { collector.Collect(url, sessionName) From ea5c54f04361cba61f0559f653e1f1b115509b72 Mon Sep 17 00:00:00 2001 From: Matthias Emde Date: Sun, 23 Nov 2025 17:27:38 +0100 Subject: [PATCH 5/7] ci: add tests for inventory metrics --- Companion/exporter/frm_server_fake_test.go | 24 ++ .../exporter/inventory_collector_test.go | 400 ++++++++++++++++++ 2 files changed, 424 insertions(+) create mode 100644 Companion/exporter/inventory_collector_test.go diff --git a/Companion/exporter/frm_server_fake_test.go b/Companion/exporter/frm_server_fake_test.go index 3090175..9cda75a 100644 --- a/Companion/exporter/frm_server_fake_test.go +++ b/Companion/exporter/frm_server_fake_test.go @@ -27,6 +27,10 @@ type FRMServerFake struct { portalData []exporter.PortalDetails hypertubeData []exporter.HypertubeDetails frackingData []exporter.FrackingDetails + cloudInventoryData []exporter.InventoryItem + worldInventoryData []exporter.InventoryItem + storageContainerData []exporter.ContainerDetail + crateData []exporter.ContainerDetail } func NewFRMServerFake() *FRMServerFake { @@ -54,6 +58,10 @@ func NewFRMServerFake() *FRMServerFake { mux.Handle("/getPortal", http.HandlerFunc(getStatsHandler(&fake.portalData))) mux.Handle("/getHyperEntrance", http.HandlerFunc(getStatsHandler(&fake.hypertubeData))) mux.Handle("/getFrackingActivator", http.HandlerFunc(getStatsHandler(&fake.frackingData))) + mux.Handle("/getCloudInventory", http.HandlerFunc(getStatsHandler(&fake.cloudInventoryData))) + mux.Handle("/getWorldInventory", http.HandlerFunc(getStatsHandler(&fake.worldInventoryData))) + mux.Handle("/getStorage", http.HandlerFunc(getStatsHandler(&fake.storageContainerData))) + mux.Handle("/getCrates", http.HandlerFunc(getStatsHandler(&fake.crateData))) return fake } @@ -140,6 +148,22 @@ func (e *FRMServerFake) ReturnsSessionInfoData(data exporter.SessionInfo) { e.sessionInfoData = data } +func (e *FRMServerFake) ReturnsCloudInventoryData(data []exporter.InventoryItem) { + e.cloudInventoryData = data +} + +func (e *FRMServerFake) ReturnsWorldInventoryData(data []exporter.InventoryItem) { + e.worldInventoryData = data +} + +func (e *FRMServerFake) ReturnsStorageContainerData(data []exporter.ContainerDetail) { + e.storageContainerData = data +} + +func (e *FRMServerFake) ReturnsCrateData(data []exporter.ContainerDetail) { + e.crateData = data +} + func getStatsHandler(data any) func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) { jsonBytes, err := json.Marshal(data) diff --git a/Companion/exporter/inventory_collector_test.go b/Companion/exporter/inventory_collector_test.go new file mode 100644 index 0000000..e668639 --- /dev/null +++ b/Companion/exporter/inventory_collector_test.go @@ -0,0 +1,400 @@ +package exporter_test + +import ( + "github.com/AP-Hunt/FicsitRemoteMonitoringCompanion/Companion/exporter" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +var _ = Describe("InventoryCollectors", func() { + var url string + var sessionName = "default" + + BeforeEach(func() { + FRMServer.Reset() + url = FRMServer.server.URL + }) + + Describe("CloudInventoryCollector", func() { + var collector *exporter.CloudInventoryCollector + + BeforeEach(func() { + collector = exporter.NewCloudInventoryCollector("/getCloudInventory") + }) + + AfterEach(func() { + collector = nil + }) + + Describe("Cloud inventory metrics", func() { + BeforeEach(func() { + FRMServer.ReturnsCloudInventoryData([]exporter.InventoryItem{ + { + Name: "Iron Ingot", + Amount: 500, + MaxAmount: 1000, + }, + { + Name: "Copper Ingot", + Amount: 250, + MaxAmount: 500, + }, + }) + }) + + It("records metrics with labels for item name", func() { + collector.Collect(url, sessionName) + metric, err := getMetric(exporter.CloudInventory, "Iron Ingot", url, sessionName) + Expect(err).ToNot(HaveOccurred()) + Expect(metric).ToNot(BeNil()) + }) + + It("records the current amount as the metric value", func() { + collector.Collect(url, sessionName) + + val, err := gaugeValue(exporter.CloudInventory, "Iron Ingot", url, sessionName) + Expect(err).ToNot(HaveOccurred()) + Expect(val).To(Equal(500.0)) + }) + + It("records the max amount metric", func() { + collector.Collect(url, sessionName) + + val, err := gaugeValue(exporter.CloudInventoryMax, "Iron Ingot", url, sessionName) + Expect(err).ToNot(HaveOccurred()) + Expect(val).To(Equal(1000.0)) + }) + + Describe("when there are multiple items", func() { + It("records a metric per item", func() { + collector.Collect(url, sessionName) + + ironVal, err := gaugeValue(exporter.CloudInventory, "Iron Ingot", url, sessionName) + Expect(err).ToNot(HaveOccurred()) + Expect(ironVal).To(Equal(500.0)) + + copperVal, err := gaugeValue(exporter.CloudInventory, "Copper Ingot", url, sessionName) + Expect(err).ToNot(HaveOccurred()) + Expect(copperVal).To(Equal(250.0)) + }) + }) + }) + }) + + Describe("WorldInventoryCollector", func() { + var collector *exporter.WorldInventoryCollector + + BeforeEach(func() { + collector = exporter.NewWorldInventoryCollector("/getWorldInventory") + }) + + AfterEach(func() { + collector = nil + }) + + Describe("World inventory metrics", func() { + BeforeEach(func() { + FRMServer.ReturnsWorldInventoryData([]exporter.InventoryItem{ + { + Name: "Concrete", + Amount: 10000, + MaxAmount: 50000, + }, + { + Name: "Steel Beam", + Amount: 2500, + MaxAmount: 10000, + }, + }) + }) + + It("records metrics with labels for item name", func() { + collector.Collect(url, sessionName) + metric, err := getMetric(exporter.WorldInventory, "Concrete", url, sessionName) + Expect(err).ToNot(HaveOccurred()) + Expect(metric).ToNot(BeNil()) + }) + + It("records the current amount as the metric value", func() { + collector.Collect(url, sessionName) + + val, err := gaugeValue(exporter.WorldInventory, "Concrete", url, sessionName) + Expect(err).ToNot(HaveOccurred()) + Expect(val).To(Equal(10000.0)) + }) + + It("records the max amount metric", func() { + collector.Collect(url, sessionName) + + val, err := gaugeValue(exporter.WorldInventoryMax, "Concrete", url, sessionName) + Expect(err).ToNot(HaveOccurred()) + Expect(val).To(Equal(50000.0)) + }) + + Describe("when there are multiple items", func() { + It("records a metric per item", func() { + collector.Collect(url, sessionName) + + concreteVal, err := gaugeValue(exporter.WorldInventory, "Concrete", url, sessionName) + Expect(err).ToNot(HaveOccurred()) + Expect(concreteVal).To(Equal(10000.0)) + + steelVal, err := gaugeValue(exporter.WorldInventory, "Steel Beam", url, sessionName) + Expect(err).ToNot(HaveOccurred()) + Expect(steelVal).To(Equal(2500.0)) + }) + }) + }) + }) + + Describe("StorageInventoryCollector", func() { + var collector *exporter.StorageInventoryCollector + + BeforeEach(func() { + collector = exporter.NewStorageInventoryCollector("/getStorage") + }) + + AfterEach(func() { + collector = nil + }) + + Describe("Storage inventory metrics", func() { + BeforeEach(func() { + FRMServer.ReturnsStorageContainerData([]exporter.ContainerDetail{ + { + Name: "Storage Container", + Location: exporter.Location{ + X: 150.0, + Y: 250.0, + Z: -350.0, + }, + Inventory: []exporter.InventoryItem{ + { + Name: "Iron Ore", + Amount: 1200, + MaxAmount: 2400, + }, + { + Name: "Copper Ore", + Amount: 600, + MaxAmount: 2400, + }, + }, + }, + }) + }) + + It("records metrics with labels for item name, container name, and coordinates", func() { + collector.Collect(url, sessionName) + metric, err := getMetric(exporter.StorageInventory, "Iron Ore", "Storage Container", "150", "250", "-350", url, sessionName) + Expect(err).ToNot(HaveOccurred()) + Expect(metric).ToNot(BeNil()) + }) + + It("records the current amount as the metric value", func() { + collector.Collect(url, sessionName) + + val, err := gaugeValue(exporter.StorageInventory, "Iron Ore", "Storage Container", "150", "250", "-350", url, sessionName) + Expect(err).ToNot(HaveOccurred()) + Expect(val).To(Equal(1200.0)) + }) + + It("records the max amount metric", func() { + collector.Collect(url, sessionName) + + val, err := gaugeValue(exporter.StorageInventoryMax, "Iron Ore", "Storage Container", "150", "250", "-350", url, sessionName) + Expect(err).ToNot(HaveOccurred()) + Expect(val).To(Equal(2400.0)) + }) + + Describe("when a container has multiple items", func() { + It("records a metric per item", func() { + collector.Collect(url, sessionName) + + ironVal, err := gaugeValue(exporter.StorageInventory, "Iron Ore", "Storage Container", "150", "250", "-350", url, sessionName) + Expect(err).ToNot(HaveOccurred()) + Expect(ironVal).To(Equal(1200.0)) + + copperVal, err := gaugeValue(exporter.StorageInventory, "Copper Ore", "Storage Container", "150", "250", "-350", url, sessionName) + Expect(err).ToNot(HaveOccurred()) + Expect(copperVal).To(Equal(600.0)) + }) + }) + + Describe("when there are multiple containers", func() { + BeforeEach(func() { + FRMServer.ReturnsStorageContainerData([]exporter.ContainerDetail{ + { + Name: "Storage Container", + Location: exporter.Location{ + X: 100.0, + Y: 200.0, + Z: -300.0, + }, + Inventory: []exporter.InventoryItem{ + { + Name: "Iron Plate", + Amount: 400, + MaxAmount: 800, + }, + }, + }, + { + Name: "Storage Container", + Location: exporter.Location{ + X: 150.0, + Y: 250.0, + Z: -350.0, + }, + Inventory: []exporter.InventoryItem{ + { + Name: "Iron Ore", + Amount: 1200, + MaxAmount: 2400, + }, + }, + }, + }) + }) + + It("records a metric per container with distinct coordinates", func() { + collector.Collect(url, sessionName) + + val1, err := gaugeValue(exporter.StorageInventory, "Iron Plate", "Storage Container", "100", "200", "-300", url, sessionName) + Expect(err).ToNot(HaveOccurred()) + Expect(val1).To(Equal(400.0)) + + val2, err := gaugeValue(exporter.StorageInventory, "Iron Ore", "Storage Container", "150", "250", "-350", url, sessionName) + Expect(err).ToNot(HaveOccurred()) + Expect(val2).To(Equal(1200.0)) + }) + }) + }) + }) + + Describe("CrateInventoryCollector", func() { + var collector *exporter.CrateInventoryCollector + + BeforeEach(func() { + collector = exporter.NewCrateInventoryCollector("/getCrates") + }) + + AfterEach(func() { + collector = nil + }) + + Describe("Crate inventory metrics", func() { + BeforeEach(func() { + FRMServer.ReturnsCrateData([]exporter.ContainerDetail{ + { + Name: "Death Crate", + Location: exporter.Location{ + X: 75.0, + Y: 125.0, + Z: -175.0, + }, + Inventory: []exporter.InventoryItem{ + { + Name: "Rifle Ammo", + Amount: 50, + MaxAmount: 100, + }, + { + Name: "Health Inhaler", + Amount: 5, + MaxAmount: 10, + }, + }, + }, + }) + }) + + It("records metrics with labels for item name, container name, and coordinates", func() { + collector.Collect(url, sessionName) + metric, err := getMetric(exporter.CrateInventory, "Rifle Ammo", "Death Crate", "75", "125", "-175", url, sessionName) + Expect(err).ToNot(HaveOccurred()) + Expect(metric).ToNot(BeNil()) + }) + + It("records the current amount as the metric value", func() { + collector.Collect(url, sessionName) + + val, err := gaugeValue(exporter.CrateInventory, "Rifle Ammo", "Death Crate", "75", "125", "-175", url, sessionName) + Expect(err).ToNot(HaveOccurred()) + Expect(val).To(Equal(50.0)) + }) + + It("records the max amount metric", func() { + collector.Collect(url, sessionName) + + val, err := gaugeValue(exporter.CrateInventoryMax, "Rifle Ammo", "Death Crate", "75", "125", "-175", url, sessionName) + Expect(err).ToNot(HaveOccurred()) + Expect(val).To(Equal(100.0)) + }) + + Describe("when a crate has multiple items", func() { + It("records a metric per item", func() { + collector.Collect(url, sessionName) + + ammoVal, err := gaugeValue(exporter.CrateInventory, "Rifle Ammo", "Death Crate", "75", "125", "-175", url, sessionName) + Expect(err).ToNot(HaveOccurred()) + Expect(ammoVal).To(Equal(50.0)) + + inhalerVal, err := gaugeValue(exporter.CrateInventory, "Health Inhaler", "Death Crate", "75", "125", "-175", url, sessionName) + Expect(err).ToNot(HaveOccurred()) + Expect(inhalerVal).To(Equal(5.0)) + }) + }) + + Describe("when there are multiple crates", func() { + BeforeEach(func() { + FRMServer.ReturnsCrateData([]exporter.ContainerDetail{ + { + Name: "Death Crate", + Location: exporter.Location{ + X: 75.0, + Y: 125.0, + Z: -175.0, + }, + Inventory: []exporter.InventoryItem{ + { + Name: "Rifle Ammo", + Amount: 50, + MaxAmount: 100, + }, + }, + }, + { + Name: "Dismantle Crate", + Location: exporter.Location{ + X: 200.0, + Y: 300.0, + Z: -400.0, + }, + Inventory: []exporter.InventoryItem{ + { + Name: "Modular Frame", + Amount: 10, + MaxAmount: 20, + }, + }, + }, + }) + }) + + It("records a metric per crate with distinct coordinates", func() { + collector.Collect(url, sessionName) + + val1, err := gaugeValue(exporter.CrateInventory, "Rifle Ammo", "Death Crate", "75", "125", "-175", url, sessionName) + Expect(err).ToNot(HaveOccurred()) + Expect(val1).To(Equal(50.0)) + + val2, err := gaugeValue(exporter.CrateInventory, "Modular Frame", "Dismantle Crate", "200", "300", "-400", url, sessionName) + Expect(err).ToNot(HaveOccurred()) + Expect(val2).To(Equal(10.0)) + }) + }) + }) + }) +}) From ec075c3fba9b8f26a2f9ef70514db9ddbc1ddefd Mon Sep 17 00:00:00 2001 From: Matthias Emde Date: Sun, 23 Nov 2025 18:49:55 +0100 Subject: [PATCH 6/7] docs: add new machine and inventory metrics to README.md --- README.md | 65 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/README.md b/README.md index 18b25d1..61ed935 100644 --- a/README.md +++ b/README.md @@ -95,6 +95,31 @@ The [Prometheus metrics server](https://prometheus.io/) allows you to [explore t The efficiency with which a building is producing an item item_name, machine_name, x, y, z, url, session_name + + machine_items_produced_max + Maximum production rate of an item a building can produce per minute + item_name, machine_name, x, y, z, url, session_name + + + machine_input_inventory + How much of an item a building has stored in its input + item_name, machine_name, x, y, z, url, session_name + + + machine_input_inventory_max + How much of an item a building can store in its input + item_name, machine_name, x, y, z, url, session_name + + + machine_output_inventory + How much of an item a building has stored in its output + item_name, machine_name, x, y, z, url, session_name + + + machine_output_inventory_max + How much of an item a building can store in its output + item_name, machine_name, x, y, z, url, session_name + factory_power Power draw from factory machines in MW. Does not include extractors. @@ -325,6 +350,46 @@ The [Prometheus metrics server](https://prometheus.io/) allows you to [explore t Vehicle station max power use in MW circuit_id, url, session_name + + cloud_inventory + Items stored in the dimensional depot + item_name, url, session_name + + + cloud_inventory_max + Stack size for items in the dimensional depot + item_name, url, session_name + + + world_inventory + Inventory of the world regardless of location (All buildings whom purpose is to provide storage) + item_name, url, session_name + + + world_inventory_max + Stack size for items in the world invetory + item_name, url, session_name + + + storage_inventory + Items stored inside storage containers + item_name, container_name, x, y, z, url, session_name + + + storage_inventory_max + Stack size for items stored in storage containers + item_name, container_name, x, y, z, url, session_name + + + crate_inventory + Items stored inside crates + item_name, container_name, x, y, z, url, session_name + + + crate_inventory_max + Stack size for items stored in crates + item_name, container_name, x, y, z, url, session_name + From 3299fffab0d3eb9ceace6b58782933bef1c39795 Mon Sep 17 00:00:00 2001 From: Matthias Emde Date: Tue, 25 Nov 2025 23:01:15 +0100 Subject: [PATCH 7/7] fix: use id for metric dropping instead of coords --- Companion/exporter/factory_build_detail.go | 1 + Companion/exporter/factory_building_collector.go | 6 +----- Companion/exporter/inventory_collector_crate.go | 9 +++------ Companion/exporter/inventory_collector_storage.go | 9 +++------ Companion/exporter/inventory_detail.go | 1 + 5 files changed, 9 insertions(+), 17 deletions(-) diff --git a/Companion/exporter/factory_build_detail.go b/Companion/exporter/factory_build_detail.go index b3ca23b..f582309 100644 --- a/Companion/exporter/factory_build_detail.go +++ b/Companion/exporter/factory_build_detail.go @@ -1,6 +1,7 @@ package exporter type BuildingDetail struct { + Id string `json:"ID"` Building string `json:"Name"` Location Location `json:"location"` Recipe string `json:"Recipe"` diff --git a/Companion/exporter/factory_building_collector.go b/Companion/exporter/factory_building_collector.go index 886ce5f..6cfabba 100644 --- a/Companion/exporter/factory_building_collector.go +++ b/Companion/exporter/factory_building_collector.go @@ -34,11 +34,7 @@ func (c *FactoryBuildingCollector) Collect(frmAddress string, sessionName string powerInfo := map[float64]float64{} maxPowerInfo := map[float64]float64{} for _, building := range details { - c.metricsDropper.CacheFreshMetricLabel(prometheus.Labels{"url": frmAddress, "session_name": sessionName, "machine_name": building.Building, - "x": strconv.FormatFloat(building.Location.X, 'f', -1, 64), - "y": strconv.FormatFloat(building.Location.Y, 'f', -1, 64), - "z": strconv.FormatFloat(building.Location.Z, 'f', -1, 64), - }) + c.metricsDropper.CacheFreshMetricLabel(prometheus.Labels{"url": frmAddress, "session_name": sessionName, "id": building.Id}) for _, prod := range building.Production { MachineItemsProducedPerMin.WithLabelValues( prod.Name, diff --git a/Companion/exporter/inventory_collector_crate.go b/Companion/exporter/inventory_collector_crate.go index 464051c..2bdb547 100644 --- a/Companion/exporter/inventory_collector_crate.go +++ b/Companion/exporter/inventory_collector_crate.go @@ -33,12 +33,9 @@ func (c *CrateInventoryCollector) Collect(frmAddress string, sessionName string) for _, detail := range details { c.metricsDropper.CacheFreshMetricLabel(prometheus.Labels{ - "url": frmAddress, - "session_name": sessionName, - "container_name": detail.Name, - "x": strconv.FormatFloat(detail.Location.X, 'f', -1, 64), - "y": strconv.FormatFloat(detail.Location.Y, 'f', -1, 64), - "z": strconv.FormatFloat(detail.Location.Z, 'f', -1, 64), + "url": frmAddress, + "session_name": sessionName, + "id": detail.Id, }) for _, item := range detail.Inventory { CrateInventory.WithLabelValues( diff --git a/Companion/exporter/inventory_collector_storage.go b/Companion/exporter/inventory_collector_storage.go index 5ff3b68..6db732f 100644 --- a/Companion/exporter/inventory_collector_storage.go +++ b/Companion/exporter/inventory_collector_storage.go @@ -33,12 +33,9 @@ func (c *StorageInventoryCollector) Collect(frmAddress string, sessionName strin for _, detail := range details { c.metricsDropper.CacheFreshMetricLabel(prometheus.Labels{ - "url": frmAddress, - "session_name": sessionName, - "container_name": detail.Name, - "x": strconv.FormatFloat(detail.Location.X, 'f', -1, 64), - "y": strconv.FormatFloat(detail.Location.Y, 'f', -1, 64), - "z": strconv.FormatFloat(detail.Location.Z, 'f', -1, 64), + "url": frmAddress, + "session_name": sessionName, + "id": detail.Id, }) for _, item := range detail.Inventory { StorageInventory.WithLabelValues( diff --git a/Companion/exporter/inventory_detail.go b/Companion/exporter/inventory_detail.go index 3745c36..c42ef4c 100644 --- a/Companion/exporter/inventory_detail.go +++ b/Companion/exporter/inventory_detail.go @@ -7,6 +7,7 @@ type InventoryItem struct { } type ContainerDetail struct { + Id string `json:"ID"` Name string `json:"Name"` Location Location `json:"location"` Inventory []InventoryItem `json:"Inventory"`