Merge pull request #706 from clusterio/feature/stat-exporter-surfaces

Feature/stat exporter surfaces
This commit is contained in:
Daniel authored and GitHub committed 2024-12-09 15:24:19 -08:00
commit 6f5547b524
3 files changed
+174 -51

No files matched your search

+104 -21
View File
@@ -6,14 +6,26 @@ const { Gauge } = lib;
type IpcStats = {
game_tick: number,
player_count: number,
game_flow_statistics: {
pollution_statistics: IpcFlowType,
},
force_flow_statistics: {
[key:string]: {
[key:string]: IpcFlowType,
surface_statistics: {
[key: string]: {
game_flow_statistics: {
pollution_statistics: IpcFlowType,
},
force_flow_statistics: {
[key:string]: {
[key:string]: IpcFlowType,
}
},
}
},
platforms: {
[key: string]: {
force: string,
surface: string,
speed: number,
weight: number,
}
}
}
type IpcFlowType = {
@@ -34,29 +46,59 @@ const instanceGameTicksTotal = new Gauge(
const instanceForceFlowStatistics = new Gauge(
"clusterio_statistics_exporter_instance_force_flow_statistics",
"Items/fluids/enemies/buildings produced/built/killed by a force",
{ labels: ["instance_id", "force", "statistic", "direction", "name"] },
{ labels: ["instance_id", "surface", "force", "statistic", "direction", "name"] },
);
const instanceGameFlowStatistics = new Gauge(
"clusterio_statistics_exporter_instance_game_flow_statistics",
"Pollution produced/consumed in the game",
{ labels: ["instance_id", "statistic", "direction", "name"] },
{ labels: ["instance_id", "surface", "statistic", "direction", "name"] },
);
const instancePlatformMapping = new Gauge(
"clusterio_statistics_exporter_platform_mapping",
"Mapping of platform name to force, surface and instance",
{ labels: ["instance_id", "platform_name", "force", "surface"] }
);
const instancePlatformSpeed = new Gauge(
"clusterio_statistics_exporter_platform_speed",
"Current speed of the platform",
{ labels: ["instance_id", "force", "surface"] }
);
const instancePlatformWeight = new Gauge(
"clusterio_statistics_exporter_platform_weight",
"Current weight/mass of the platform",
{ labels: ["instance_id", "force", "surface"] }
);
function setForceFlowStatistic(
instanceId: number,
surfaceName: string,
forceName: string,
statisticName: string,
direction: string,
item: string,
value: number,
) {
instanceForceFlowStatistics.labels(String(instanceId), forceName, statisticName, direction, item).set(value);
instanceForceFlowStatistics.labels(
String(instanceId),
surfaceName,
forceName,
statisticName,
direction,
item
).set(value);
// For item and fluid statistics it's useful to compare the input flow with the
// output flow, to simplify the comparison ensure both directions have a value.
if (["item_production_statistics", "fluid_production_statistic"].includes(statisticName)) {
let reverseDirection = direction === "input" ? "output" : "input";
instanceForceFlowStatistics.labels(String(instanceId), forceName, statisticName, reverseDirection, item);
instanceForceFlowStatistics.labels(
String(instanceId),
surfaceName,
forceName,
statisticName,
reverseDirection,
item
).set(value);
}
}
@@ -84,21 +126,59 @@ export class InstancePlugin extends BaseInstancePlugin {
instanceGameTicksTotal.labels(String(instanceId)).set(stats.game_tick);
instancePlayerCount.labels(String(instanceId)).set(stats.player_count);
for (let [forceName, flowStatistics] of Object.entries(stats.force_flow_statistics)) {
for (let [statisticName, statistic] of Object.entries(flowStatistics)) {
for (let [direction, counts] of Object.entries(statistic)) {
for (let [item, value] of Object.entries(counts)) {
setForceFlowStatistic(instanceId, forceName, statisticName, direction, item, value);
// Remove existing platform mappings and stats for this instance
instancePlatformMapping.removeAll({ instance_id: String(instanceId) });
instancePlatformSpeed.removeAll({ instance_id: String(instanceId) });
instancePlatformWeight.removeAll({ instance_id: String(instanceId) });
// Set new platform mappings and stats
for (let [platformName, platform] of Object.entries(stats.platforms)) {
instancePlatformMapping.labels(
String(instanceId),
platformName,
platform.force,
platform.surface
).set(1);
instancePlatformSpeed.labels(
String(instanceId),
platform.force,
platform.surface
).set(platform.speed);
instancePlatformWeight.labels(
String(instanceId),
platform.force,
platform.surface
).set(platform.weight);
}
for (let [surfaceName, surfaceStats] of Object.entries(stats.surface_statistics)) {
for (let [forceName, flowStatistics] of Object.entries(surfaceStats.force_flow_statistics)) {
for (let [statisticName, statistic] of Object.entries(flowStatistics)) {
for (let [direction, counts] of Object.entries(statistic)) {
// eslint-disable-next-line max-depth
for (let [item, value] of Object.entries(counts)) {
setForceFlowStatistic(
instanceId,
surfaceName,
forceName,
statisticName,
direction,
item,
value
);
}
}
}
}
}
for (let [direction, counts] of Object.entries(stats.game_flow_statistics.pollution_statistics)) {
for (let [item, value] of Object.entries(counts)) {
instanceGameFlowStatistics.labels(
String(instanceId), "pollution_statistics", direction, item
).set(value);
for (let [direction, counts] of Object.entries(surfaceStats.game_flow_statistics.pollution_statistics)) {
for (let [item, value] of Object.entries(counts)) {
instanceGameFlowStatistics.labels(
String(instanceId), surfaceName, "pollution_statistics", direction, item
).set(value);
}
}
}
}
@@ -121,3 +201,6 @@ export const _instancePlayerCount = instancePlayerCount;
export const _instanceGameTicksTotal = instanceGameTicksTotal;
export const _instanceForceFlowStatistics = instanceForceFlowStatistics;
export const _instanceGameFlowStatistics = instanceGameFlowStatistics;
export const _instancePlatformMapping = instancePlatformMapping;
export const _instancePlatformSpeed = instancePlatformSpeed;
export const _instancePlatformWeight = instancePlatformWeight;
+38 -19
View File
@@ -11,32 +11,51 @@ local statistics = {
statistics_exporter = {}
function statistics_exporter.export()
--- @diagnostic disable-next-line undefined-field
local pollution = v2_stats and game.get_pollution_statistics(1) or game.pollution_statistics
local stats = {
game_tick = game.tick,
player_count = #game.connected_players,
game_flow_statistics = {
pollution_statistics = {
--- TODO support multi surface pollution statistics
input = pollution.input_counts,
output = pollution.output_counts,
},
},
force_flow_statistics = {}
surface_statistics = {},
platforms = {}
}
for _, force in pairs(game.forces) do
local flow_statistics = {}
for _, statName in pairs(statistics) do
local stat = v2_stats and force["get_" .. statName](1) or force[statName]
flow_statistics[statName] = {
input = stat.input_counts,
output = stat.output_counts,
}
for _, surface in pairs(game.surfaces) do
local surface_stats = {
game_flow_statistics = {
pollution_statistics = {
input = (v2_stats and game.get_pollution_statistics(surface.index) or surface.pollution_statistics).input_counts,
output = (v2_stats and game.get_pollution_statistics(surface.index) or surface.pollution_statistics).output_counts,
},
},
force_flow_statistics = {}
}
for _, force in pairs(game.forces) do
local flow_statistics = {}
for _, statName in pairs(statistics) do
local stat = v2_stats and force["get_" .. statName](surface.index) or force[statName]
flow_statistics[statName] = {
input = stat.input_counts,
output = stat.output_counts,
}
end
surface_stats.force_flow_statistics[force.name] = flow_statistics
end
stats.force_flow_statistics[force.name] = flow_statistics
stats.surface_statistics[surface.name] = surface_stats
end
if v2_stats then
for _, force in pairs(game.forces) do
for _, platform in pairs(force.platforms) do
stats.platforms[platform.name] = {
force = force.name,
surface = platform.surface.name,
speed = platform.speed,
weight = platform.weight,
}
end
end
end
rcon.print(compat.table_to_json(stats))
end
+32 -11
View File
@@ -30,18 +30,30 @@ describe("statistics_exporter plugin", function() {
JSON.stringify({
game_tick: 100,
player_count: 3,
force_flow_statistics: {
player: {
item_production_statistics: {
input: { "inserter": 10 },
output: { "iron-plate": 24 },
surface_statistics: {
"nauvis": {
game_flow_statistics: {
pollution_statistics: {
input: { "boiler": 2000 },
output: { "tree-proxy": 560 },
},
},
force_flow_statistics: {
player: {
item_production_statistics: {
input: { "inserter": 10 },
output: { "iron-plate": 24 },
},
},
},
},
},
game_flow_statistics: {
pollution_statistics: {
input: { "boiler": 2000 },
output: { "tree-proxy": 560 },
platforms: {
"platform-1": {
force: "player",
surface: "nauvis",
speed: 1,
weight: 2,
},
},
})
@@ -52,11 +64,20 @@ describe("statistics_exporter plugin", function() {
assert.equal(instance._instancePlayerCount.labels("7357").get(), 3);
assert.equal(instance._instanceGameTicksTotal.labels("7357").get(), 100);
assert.equal(instance._instanceForceFlowStatistics.labels(
"7357", "player", "item_production_statistics", "input", "inserter").get(),
"7357", "nauvis", "player", "item_production_statistics", "input", "inserter").get(),
10);
assert.equal(instance._instanceGameFlowStatistics.labels(
"7357", "pollution_statistics", "input", "boiler").get(),
"7357", "nauvis", "pollution_statistics", "input", "boiler").get(),
2000);
assert.equal(instance._instancePlatformMapping.labels(
"7357", "platform-1", "player", "nauvis").get(),
1);
assert.equal(instance._instancePlatformSpeed.labels(
"7357", "player", "nauvis").get(),
1);
assert.equal(instance._instancePlatformWeight.labels(
"7357", "player", "nauvis").get(),
2);
});
it("should pass on JSON parse errors", async function() {
let string = "An error occured\n";