Skip to content

Commit 4fb4908

Browse files
committed
feat: Performance-Logging — PERF-Zeilen in log.txt, Perf-Export via Dashboard
- Lua: appendPerfLine() schreibt via print() in die FS25-log.txt (getTime()-Timing pro Funktion: collect, fields, vehicles, vehicleMeta, commands, weather, soil_<layer>; idle-Sampling alle 5 s auf leeren Frames) - Lua: perfLogEnabled via config.xml <performanceLog enabled="false"/> - Server: /api/perf-export — liest PERF-Zeilen aus log.txt, sammelt Systeminfo (OS, CPU, Kerne, RAM, GPU) und Mod-Konfiguration, schreibt perf_export.txt in das modSettings-Verzeichnis - Server: Server-Version (Build-Tag) statt Go-Compiler-Version im Export - Dashboard: Diagnose-Sektion in Mod-Konfiguration mit Toggle + Exportieren-Button (Settings → Mod-Konfiguration → Diagnose)
1 parent 7c66a75 commit 4fb4908

3 files changed

Lines changed: 263 additions & 6 deletions

File tree

FS25_FarmMonitor/FarmMonitor.lua

Lines changed: 58 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -304,6 +304,7 @@ function FarmMonitor:loadConfig()
304304
commandInterval = 1,
305305
soilResolution = 128,
306306
soilRowsPerTick = 2,
307+
perfLogEnabled = false,
307308
}
308309

309310
local cfg = {}
@@ -322,6 +323,8 @@ function FarmMonitor:loadConfig()
322323
cfg.commandInterval = getInt("exportIntervals.commands#seconds", defaults.commandInterval)
323324
cfg.soilResolution = getInt("soilMap.resolution#value", defaults.soilResolution)
324325
cfg.soilRowsPerTick = getInt("soilMap.rowsPerTick#value", defaults.soilRowsPerTick)
326+
local perfVal = getXMLBool(xmlId, "farmMonitor.performanceLog#enabled")
327+
cfg.perfLogEnabled = perfVal == true
325328
delete(xmlId)
326329
end)
327330
if not ok then
@@ -348,6 +351,8 @@ function FarmMonitor:loadConfig()
348351
f:write(' <resolution value="' .. defaults.soilResolution .. '"/>\n')
349352
f:write(' <rowsPerTick value="' .. defaults.soilRowsPerTick .. '"/>\n')
350353
f:write(' </soilMap>\n')
354+
f:write(' <!-- Performance logging: writes perf.csv to modSettings folder -->\n')
355+
f:write(' <performanceLog enabled="false"/>\n')
351356
f:write('</farmMonitor>\n')
352357
f:close()
353358
print("[FarmMonitor] Created default config.xml at: " .. path)
@@ -362,6 +367,8 @@ function FarmMonitor:loadConfig()
362367
FarmMonitor.commandInterval = cfg.commandInterval * 1000
363368
FarmMonitor.soilResolution = cfg.soilResolution
364369
FarmMonitor.soilRowsPerTick = cfg.soilRowsPerTick
370+
FarmMonitor.perfLogEnabled = cfg.perfLogEnabled
371+
FarmMonitor.perfIdleTimer = 0
365372

366373
print("[FarmMonitor] Config loaded:")
367374
print(string.format("[FarmMonitor] Haupt-Export : %d s", cfg.mainInterval))
@@ -371,6 +378,13 @@ function FarmMonitor:loadConfig()
371378
print(string.format("[FarmMonitor] IPC-Commands : %d s", cfg.commandInterval))
372379
print(string.format("[FarmMonitor] Soil-Aufloesung : %d px", cfg.soilResolution))
373380
print(string.format("[FarmMonitor] Soil-RowsPerTick: %d", cfg.soilRowsPerTick))
381+
print(string.format("[FarmMonitor] Perf-Logging : %s", cfg.perfLogEnabled and "an" or "aus"))
382+
end
383+
384+
local function appendPerfLine(fn, modMs, frameDt)
385+
local ts = getDate("%Y-%m-%dT%H:%M:%S")
386+
local dtStr = frameDt ~= nil and string.format(" dt=%.1fms", frameDt) or ""
387+
print(string.format("[FarmMonitor] PERF %s %s: %.1fms%s", ts, fn, modMs, dtStr))
374388
end
375389

376390
function FarmMonitor:deleteMap()
@@ -493,40 +507,62 @@ function FarmMonitor:update(dt)
493507
FarmMonitor:exportAutoDriveMarkers()
494508
end
495509

510+
local perfOn = FarmMonitor.perfLogEnabled
511+
local heavyFrame = false
512+
local t0
513+
496514
FarmMonitor.timer = FarmMonitor.timer + dt
497515
if FarmMonitor.timer >= FarmMonitor.updateInterval then
498516
FarmMonitor.timer = 0
517+
t0 = perfOn and getTime() or nil
499518
FarmMonitor:collectAndSave()
519+
if perfOn then appendPerfLine("collect", getTime() - t0, dt) end
520+
heavyFrame = true
500521
end
501522

502523
FarmMonitor.fieldTimer = FarmMonitor.fieldTimer + dt
503524
if FarmMonitor.fieldTimer >= FarmMonitor.fieldInterval then
504525
FarmMonitor.fieldTimer = 0
526+
t0 = perfOn and getTime() or nil
505527
FarmMonitor:collectAndSaveFields()
528+
if perfOn then appendPerfLine("fields", getTime() - t0, dt) end
529+
heavyFrame = true
506530
end
507531

508532
FarmMonitor.vehicleTimer = FarmMonitor.vehicleTimer + dt
509533
if FarmMonitor.vehicleTimer >= FarmMonitor.vehicleInterval then
510534
FarmMonitor.vehicleTimer = 0
535+
t0 = perfOn and getTime() or nil
511536
FarmMonitor:collectAndSaveVehicles()
537+
if perfOn then appendPerfLine("vehicles", getTime() - t0, dt) end
538+
heavyFrame = true
512539
end
513540

514541
FarmMonitor.vehicleMetaTimer = FarmMonitor.vehicleMetaTimer + dt
515542
if FarmMonitor.vehicleMetaTimer >= FarmMonitor.vehicleMetaInterval then
516543
FarmMonitor.vehicleMetaTimer = 0
544+
t0 = perfOn and getTime() or nil
517545
FarmMonitor:collectAndSaveVehicleMeta()
546+
if perfOn then appendPerfLine("vehicleMeta", getTime() - t0, dt) end
547+
heavyFrame = true
518548
end
519549

520550
FarmMonitor.commandTimer = FarmMonitor.commandTimer + dt
521551
if FarmMonitor.commandTimer >= FarmMonitor.commandInterval then
522552
FarmMonitor.commandTimer = 0
553+
t0 = perfOn and getTime() or nil
523554
FarmMonitor:processCommands()
555+
if perfOn then appendPerfLine("commands", getTime() - t0, dt) end
556+
heavyFrame = true
524557
end
525558

526559
FarmMonitor.weatherTimer = FarmMonitor.weatherTimer + dt
527560
if FarmMonitor.weatherTimer >= FarmMonitor.weatherInterval then
528561
FarmMonitor.weatherTimer = 0
562+
t0 = perfOn and getTime() or nil
529563
FarmMonitor:collectAndSaveWeather()
564+
if perfOn then appendPerfLine("weather", getTime() - t0, dt) end
565+
heavyFrame = true
530566
end
531567

532568
-- Incremental soil layer export: runs every tick, samples a few rows at a time
@@ -536,6 +572,16 @@ function FarmMonitor:update(dt)
536572
if FarmMonitor.soilState ~= nil then
537573
FarmMonitor:stepSoilExport()
538574
end
575+
576+
-- Idle baseline: sample frame dt every 5 s on frames with no heavy mod work
577+
if perfOn and not heavyFrame then
578+
FarmMonitor.perfIdleTimer = FarmMonitor.perfIdleTimer + dt
579+
if FarmMonitor.perfIdleTimer >= 5000 then
580+
FarmMonitor.perfIdleTimer = 0
581+
appendPerfLine("idle", 0, dt)
582+
end
583+
end
584+
539585
end
540586

541587
function FarmMonitor:draw() end
@@ -3015,9 +3061,10 @@ function FarmMonitor:initSoilState()
30153061
cellSize = terrainSize / res,
30163062
half = terrainSize / 2,
30173063
rowsPerTick = FarmMonitor.soilRowsPerTick or 2,
3018-
layerIdx = 1, -- C: current layer in round-robin
3019-
currentRow = 0, -- A: next row to sample
3020-
values = {}, -- accumulator for current layer
3064+
layerIdx = 1, -- C: current layer in round-robin
3065+
currentRow = 0, -- A: next row to sample
3066+
values = {}, -- accumulator for current layer
3067+
layerStartTime = getTime(), -- for perf logging
30213068
}
30223069
end
30233070

@@ -3083,10 +3130,15 @@ function FarmMonitor:stepSoilExport()
30833130
print("[FarmMonitor] ERROR writing layer_" .. name .. ": " .. tostring(writeErr))
30843131
end
30853132

3133+
if FarmMonitor.perfLogEnabled then
3134+
appendPerfLine("soil_" .. name, getTime() - st.layerStartTime, nil)
3135+
end
3136+
30863137
-- Advance to next layer (C: round-robin)
3087-
st.layerIdx = (st.layerIdx % #st.layers) + 1
3088-
st.currentRow = 0
3089-
st.values = {}
3138+
st.layerIdx = (st.layerIdx % #st.layers) + 1
3139+
st.currentRow = 0
3140+
st.values = {}
3141+
st.layerStartTime = getTime()
30903142
end
30913143
end
30923144

Server/dashboard.html

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5266,6 +5266,24 @@ <h2>Aktive Warnungen</h2>
52665266
${cfgRow('Auflösung', 'Pixelgröße des Rasters. Empfohlen: 128.<br>Werte: 32 / 64 / 128 / 256 / 512.', 'mcfg-soil-res', mcs.resolution ?? 128, 32, 512, 'px')}
52675267
${cfgRow('Zeilen pro Tick', 'Zeilen pro Spiel-Tick.<br>Höher = schnellere Aktualisierung, mehr Last. Empfohlen: 2.', 'mcfg-soil-rows', mcs.rowsPerTick ?? 2, 1, 16, 'Zeilen')}
52685268
</div>
5269+
<div style="font-size:12px;color:var(--muted);font-weight:700;text-transform:uppercase;letter-spacing:.06em;margin-top:4px">Diagnose</div>
5270+
<div class="placeable-item">
5271+
<div class="placeable-item-info">
5272+
<div class="placeable-item-name">Performance-Logging</div>
5273+
<div class="placeable-item-uid">Schreibt PERF-Zeilen in die log.txt. Nur für Diagnose aktivieren.</div>
5274+
</div>
5275+
<label class="toggle">
5276+
<input type="checkbox" id="mcfg-perflog" ${modConfigCache?.perfLog ? 'checked' : ''}>
5277+
<span class="toggle-slider"></span>
5278+
</label>
5279+
</div>
5280+
<div class="placeable-item">
5281+
<div class="placeable-item-info">
5282+
<div class="placeable-item-name">Perf-Export erstellen</div>
5283+
<div class="placeable-item-uid" id="perf-export-status">Systeminfo + Konfiguration + Messungen → perf_export.txt</div>
5284+
</div>
5285+
<button onclick="perfExport()" class="edit-mode-btn active" style="padding:6px 14px;white-space:nowrap">Exportieren</button>
5286+
</div>
52695287
<div style="display:flex;align-items:center;justify-content:flex-end;margin-top:4px">
52705288
<button onclick="saveModConfig()" class="edit-mode-btn active" style="padding:7px 18px">Speichern</button>
52715289
</div>
@@ -5292,6 +5310,19 @@ <h2>Aktive Warnungen</h2>
52925310
} catch(e) { console.warn('mod-config fetch failed', e); }
52935311
}
52945312

5313+
async function perfExport() {
5314+
const status = document.getElementById('perf-export-status');
5315+
if (status) status.textContent = 'Exportiere …';
5316+
try {
5317+
const res = await fetch('/api/perf-export', { method: 'POST' });
5318+
if (!res.ok) throw new Error(res.statusText);
5319+
const data = await res.json();
5320+
if (status) status.textContent = '✓ Exportiert: ' + data.path;
5321+
} catch(e) {
5322+
if (status) status.textContent = '✗ Fehler: ' + e.message;
5323+
}
5324+
}
5325+
52955326
async function saveModConfig() {
52965327
const get = id => parseInt(document.getElementById(id)?.value ?? '0', 10);
52975328
const cfg = {
@@ -5306,6 +5337,7 @@ <h2>Aktive Warnungen</h2>
53065337
resolution: get('mcfg-soil-res'),
53075338
rowsPerTick: get('mcfg-soil-rows'),
53085339
},
5340+
perfLog: document.getElementById('mcfg-perflog')?.checked ?? false,
53095341
};
53105342
try {
53115343
const res = await fetch('/api/mod-config', {

0 commit comments

Comments
 (0)