Seite 2 von 3
Re: Logfile Decoder für die Gen3 Zero
Verfasst: Sa 14. Mär 2026, 07:26
von koka_S
Ich dachte eher an so etwas. Eine HTML wo man den Log einliest. Versuche das gerade mit Chatgpt zu bauen, bzw. er baut das für mich. Leider klappt das nur so halb, da ich davon null plan habe.
Das ist das Script dazu, vielleicht hat hier jemand mehr Ahnung davon.
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<title>BMS/MBB Log Viewer</title>
<style>
body{
font-family:Arial;
background:#f2f2f2;
margin:20px;
}
table{
border-collapse:collapse;
width:100%;
background:white;
}
th,td{
border:1px solid #ccc;
padding:6px;
font-size:12px;
text-align:center;
}
th{
background:#444;
color:white;
}
.error{
background:#ffb3b3;
}
.warn{
background:#ffd27f;
}
td.text{
text-align:left;
}
canvas{
margin-top:20px;
background:white;
}
</style>
</head>
<body>
<h2>BMS/MBB Log Viewer</h2>
<input type="file" id="fileInput" accept=".txt">
<table id="logTable">
<thead>
<tr>
<th>#</th>
<th>Typ</th>
<th>Zeit</th>
<th>SOC</th>
<th>Cell Min</th>
<th>Cell Max</th>
<th>Pack Voltage</th>
<th>Info / Text</th>
</tr>
</thead>
<tbody></tbody>
</table>
<script>
const fileInput=document.getElementById("fileInput")
const tbody=document.querySelector("#logTable tbody")
fileInput.addEventListener("change",function(){
const file=this.files[0]
const reader=new FileReader()
reader.onload=function(e){
tbody.innerHTML=""
const lines=e.target.result.split(/\r?\n/)
let row=1
lines.forEach(line=>{
if(!line.trim()) return
const cols=line.split(";")
if(cols.length<6) return
let typ=cols[1]
let time=cols[2]
let info=cols.slice(6).join(";")
let soc=""
let cellMin=""
let cellMax=""
let pack=""
if(typ=="75" || typ=="76" || typ=="77"){
let data=line.split(";")
let nums=data.slice(-15)
cellMin=nums[1]
cellMax=nums[3]
soc=nums[4]
pack=nums[11]
}
let tr=document.createElement("tr")
if(/error|crc|could not/i.test(info)) tr.classList.add("error")
if(/fault|invalid/i.test(info)) tr.classList.add("warn")
tr.innerHTML=`
<td>${row++}</td>
<td>${typ}</td>
<td>${time}</td>
<td>${soc}</td>
<td>${cellMin}</td>
<td>${cellMax}</td>
<td>${pack}</td>
<td class="text">${info}</td>
`
tbody.appendChild(tr)
})
}
reader.readAsText(file)
})
</script>
</body>
</html>
Re: Logfile Decoder für die Gen3 Zero
Verfasst: Sa 14. Mär 2026, 10:32
von koka_S
Hier ist noch ein Skript das etwas besser funktioniert, aber dennoch nicht das gelbe vom Ei ist. Einfach in einen Text Editor einfügen und als HTML speichern.
Als Code formatiert (MEroller):
Code: Alles auswählen
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<title>BMS Log Viewer mit SOC/Voltage</title>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<style>
body {font-family: Arial, sans-serif; background: #fff; color: #000;}
table {border-collapse: collapse; width: 100%; margin-top: 10px;}
th, td {border: 1px solid #ccc; padding: 4px; text-align: left;}
th {background: #eee;}
tr:nth-child(even) {background: #f9f9f9;}
tr:nth-child(odd) {background: #fff;}
.event-error {background-color: #fdd;}
.event-warning {background-color: #ffe5b4;}
.event-info {background-color: #d9ecff;}
#chartContainer {width: 100%; height: 400px; margin-top: 20px;}
</style>
</head>
<body>
<h2>BMS Log Viewer Type 75/76/77/253</h2>
<input type="file" id="fileInput" accept=".csv,.txt">
<select id="typeFilter">
<option value="all">Alle Typen</option>
<option value="75">Type 75</option>
<option value="76">Type 76</option>
<option value="77">Type 77</option>
<option value="253">Type 253 (Events)</option>
</select>
<table>
<thead>
<tr>
<th>Counter</th>
<th>Type</th>
<th>Datum/Zeit</th>
<th>Cell MIN</th>
<th>Cell OCV Low</th>
<th>Cell MAX</th>
<th>SOC %</th>
<th>Current (mA)</th>
<th>Voltage</th>
<th>Report Mode</th>
<th>RUN</th>
<th>RUN+CHARGE</th>
<th>Event Message</th>
</tr>
</thead>
<tbody id="logBody"></tbody>
</table>
<div id="chartContainer">
<canvas id="socVoltageChart"></canvas>
</div>
<script>
const fileInput = document.getElementById("fileInput");
const tbody = document.getElementById("logBody");
const typeFilter = document.getElementById("typeFilter");
let chart; // Chart.js Object
fileInput.addEventListener("change", e => {
const file = e.target.files[0];
const reader = new FileReader();
reader.onload = () => parseCSV(reader.result);
reader.readAsText(file);
});
typeFilter.addEventListener("change", () => {
const rows = tbody.querySelectorAll("tr");
rows.forEach(r => {
const type = r.dataset.type;
r.style.display = (typeFilter.value === "all" || type === typeFilter.value) ? "" : "none";
});
});
function parseCSV(text) {
tbody.innerHTML = "";
const lines = text.split("\n").filter(l => l.trim());
const socData = [];
const voltageData = [];
const labels = [];
lines.forEach(line => {
const cols = line.split(";");
if (cols.length < 7) return;
const counter = cols[5] || "";
const type = cols[1] || "";
const datetime = cols[2] || "";
const dataCols = cols.slice(6).map(v => v.trim()).filter(v => v !== "");
let cmin="", covcl="", cmax="", soc="", current="", voltage="", report="", run="", runcharge="", eventMsg="", rowClass="";
if (["75","76","77"].includes(type)) {
cmin = dataCols[4] || "";
covcl = dataCols[5] || "";
cmax = dataCols[6] || "";
soc = dataCols[7] || "";
current = dataCols[8] ? (-1 * Number(dataCols[8])) : "";
voltage = dataCols[17] || "";
if (type === "75") run = dataCols[20] || "";
if (type === "76") run = dataCols[26] || "";
if (type === "77") {
run = dataCols[26] || "";
runcharge = dataCols[30] || "";
}
const reportModeNum = dataCols[16] || "";
report = reportModeNum === "1" ? "std" : reportModeNum === "3" ? "min" : reportModeNum === "7" ? "active" : reportModeNum;
// Für Diagramm nur wenn SOC & Voltage numerisch sind
if (!isNaN(Number(soc)) && !isNaN(Number(voltage))) {
labels.push(datetime);
socData.push(Number(soc));
voltageData.push(Number(voltage)/1000); // mV → V
}
}
if (type === "253") {
eventMsg = cols[6] || "";
if (/Fault set/i.test(eventMsg)) rowClass="event-error";
else if (/Quick Self Test Passed|Contactor|Latching/i.test(eventMsg)) rowClass="event-warning";
else rowClass="event-info";
}
const tr = document.createElement("tr");
tr.dataset.type = type;
if(rowClass) tr.classList.add(rowClass);
tr.innerHTML = `
<td>${counter}</td>
<td>${type}</td>
<td>${datetime}</td>
<td>${cmin}</td>
<td>${covcl}</td>
<td>${cmax}</td>
<td>${soc}</td>
<td>${current}</td>
<td>${voltage}</td>
<td>${report}</td>
<td>${run}</td>
<td>${runcharge || ""}</td>
<td>${eventMsg}</td>
`;
tbody.appendChild(tr);
});
renderChart(labels, socData, voltageData);
}
function renderChart(labels, socData, voltageData) {
const ctx = document.getElementById('socVoltageChart').getContext('2d');
if(chart) chart.destroy();
chart = new Chart(ctx, {
type: 'line',
data: {
labels: labels,
datasets: [
{
label: 'SOC %',
data: socData,
borderColor: 'blue',
backgroundColor: 'rgba(0,0,255,0.1)',
yAxisID: 'y1',
tension: 0.2,
},
{
label: 'Voltage V',
data: voltageData,
borderColor: 'red',
backgroundColor: 'rgba(255,0,0,0.1)',
yAxisID: 'y2',
tension: 0.2,
}
]
},
options: {
responsive: true,
interaction: {mode: 'index', intersect: false},
stacked: false,
plugins: {title: {display: true, text: 'SOC und Voltage über Zeit'}},
scales: {
y1: {
type: 'linear',
position: 'left',
title: {display:true, text:'SOC %'},
min: 0, max: 100
},
y2: {
type: 'linear',
position: 'right',
title: {display:true, text:'Voltage V'},
min: 0
},
x: {title: {display:true, text:'Zeit'}}
}
}
});
}
</script>
</body>
</html>
Für Optimierungen bin ich offen und sind willkommen.
Re: Logfile Decoder für die Gen3 Zero
Verfasst: So 15. Mär 2026, 21:01
von stevelectric
Ja, funktioniert bei meiner DSRX, deutlich übersichtlicher als die reine Textdatei
Jetzt bitte noch einen Viewer für das MBB log mit Anzeige der Ereignisse auf einer Karte oder so

Re: Logfile Decoder für die Gen3 Zero
Verfasst: Mo 16. Mär 2026, 08:32
von xshunin
Bitte doch keine ChatGPT Lösungen posten, die man nicht selbst versteht

Man trägt doch eine gewisse Verantwortung von Infos, die man postet. Oder sehen das die Mods hier anders?
Re: Logfile Decoder für die Gen3 Zero
Verfasst: Mo 16. Mär 2026, 08:58
von MEroller
Wenn sinnvolle Lösungen mit Hilfe von KI hier von Usern generiert, getestet, für gut befunden und geteilt werden spricht nichts dagegen. Aber zur Infobeschaffung ist die KI leider nicht zuverlässig.
Re: Logfile Decoder für die Gen3 Zero
Verfasst: Mo 16. Mär 2026, 09:09
von koka_S
Ich wünschte ich könnte das selbst schreiben, kann ich aber nicht. Ich sehe die KI als Werkzeug wie eine Säge oder einen Bohrer, ich "muss" nicht verstehen wie das funktioniert, aber ich kann es "versuchen" zu verstehen. Ich nutze es und wenn das Ergebnis, das ist was ich mir vorgestellt habe, dann bin ich zufrieden. Genau wie beim Sägen oder Bohren.
Hier ist eine etwas bessere Version davon was die KI gebaut hat.
Code: Alles auswählen
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<title>BMS Log Analyzer</title>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script src="https://cdn.jsdelivr.net/npm/chartjs-plugin-zoom@2.0.1/dist/chartjs-plugin-zoom.min.js"></script>
<style>
body{
font-family:Arial;
background:#fff;
color:#000;
}
table{
border-collapse:collapse;
width:100%;
margin-top:10px;
}
th,td{
border:1px solid #ccc;
padding:4px;
font-size:12px;
}
th{background:#eee;}
tr:nth-child(even){background:#f9f9f9;}
tr:nth-child(odd){background:#fff;}
/* EVENT FARBEN */
.event-error{
background:#ffcccc;
color:#b00000;
font-weight:bold;
}
.event-warning{
background:#fff2cc;
color:#9a5b00;
font-weight:bold;
}
.event-info{
background:#e6f2ff;
color:#004a99;
}
#chartWrapper{
width:100%;
overflow-x:auto;
margin-top:30px;
}
.chartBox{
height:420px;
margin-bottom:30px;
}
canvas{
height:420px;
}
</style>
</head>
<body>
<h2>BMS Log Analyzer</h2>
<input type="file" id="fileInput" accept=".csv,.txt">
<select id="typeFilter">
<option value="all">Alle Typen</option>
<option value="75">Type 75</option>
<option value="76">Type 76</option>
<option value="77">Type 77</option>
<option value="253">Type 253 Events</option>
</select>
<table>
<thead>
<tr>
<th>Counter</th>
<th>Type</th>
<th>Date</th>
<th>Cell MIN</th>
<th>Cell OCV</th>
<th>Cell MAX</th>
<th>SOC</th>
<th>Current</th>
<th>Voltage</th>
<th>Report</th>
<th>RUN</th>
<th>RUN+CHARGE</th>
<th>Event</th>
</tr>
</thead>
<tbody id="logBody"></tbody>
</table>
<div id="chartWrapper">
<div class="chartBox">
<canvas id="socVoltageChart"></canvas>
</div>
<div class="chartBox">
<canvas id="deltaChart"></canvas>
</div>
</div>
<script>
const fileInput=document.getElementById("fileInput");
const tbody=document.getElementById("logBody");
const typeFilter=document.getElementById("typeFilter");
let socChart;
let deltaChart;
fileInput.addEventListener("change",e=>{
const file=e.target.files[0];
const reader=new FileReader();
reader.onload=()=>parseCSV(reader.result);
reader.readAsText(file);
});
typeFilter.addEventListener("change",()=>{
const rows=tbody.querySelectorAll("tr");
rows.forEach(r=>{
const type=r.dataset.type;
r.style.display=
(typeFilter.value==="all"||type===typeFilter.value)?"":"none";
});
});
function parseCSV(text){
tbody.innerHTML="";
const lines=text.split("\n").filter(l=>l.trim());
const labels=[];
const socData=[];
const voltageData=[];
const delta=[];
const socAxis=[];
lines.forEach(line=>{
const cols=line.split(";");
if(cols.length<7) return;
const counter=cols[5]||"";
const type=cols[1]||"";
const datetime=cols[2]||"";
const dataCols=cols.slice(6).map(v=>v.trim()).filter(v=>v!="");
let cmin="",covcl="",cmax="",soc="",current="",voltage="",report="",run="",runcharge="",eventMsg="",rowClass="";
if(["75","76","77"].includes(type)){
cmin=dataCols[4]||"";
covcl=dataCols[5]||"";
cmax=dataCols[6]||"";
soc=dataCols[7]||"";
current=dataCols[8]?(-1*Number(dataCols[8])):"";
voltage=dataCols[17]||"";
if(type==="75") run=dataCols[20]||"";
if(type==="76") run=dataCols[26]||"";
if(type==="77"){
run=dataCols[26]||"";
runcharge=dataCols[30]||"";
}
const reportMode=dataCols[16]||"";
report=
reportMode==="1"?"std":
reportMode==="3"?"min":
reportMode==="7"?"active":
reportMode;
if(!isNaN(Number(soc))&&!isNaN(Number(voltage))){
labels.push(datetime);
socData.push(Number(soc));
voltageData.push(Number(voltage)/1000);
}
if(!isNaN(Number(cmin))&&!isNaN(Number(cmax))){
const min=Number(cmin)/1000;
const max=Number(cmax)/1000;
delta.push(max-min);
socAxis.push(Number(soc));
}
}
if(type==="253"){
eventMsg=cols[6]||"";
if(/Fault set/i.test(eventMsg)){
rowClass="event-error";
eventMsg="❌ "+eventMsg;
}
else if(/Contactor|Latching/i.test(eventMsg)){
rowClass="event-warning";
eventMsg="⚠️ "+eventMsg;
}
else{
rowClass="event-info";
eventMsg="ℹ️ "+eventMsg;
}
}
const tr=document.createElement("tr");
tr.dataset.type=type;
if(rowClass) tr.classList.add(rowClass);
tr.innerHTML=`
<td>${counter}</td>
<td>${type}</td>
<td>${datetime}</td>
<td>${cmin}</td>
<td>${covcl}</td>
<td>${cmax}</td>
<td>${soc}</td>
<td>${current}</td>
<td>${voltage}</td>
<td>${report}</td>
<td>${run}</td>
<td>${runcharge}</td>
<td>${eventMsg}</td>
`;
tbody.appendChild(tr);
});
renderSOCChart(labels,socData,voltageData);
renderDeltaChart(socAxis,delta);
}
function zoomOptions(){
return{
zoom:{
wheel:{enabled:true},
drag:{enabled:true},
mode:'xy'
},
pan:{
enabled:true,
mode:'xy'
}
}
}
function renderSOCChart(labels,soc,voltage){
const ctx=document.getElementById("socVoltageChart");
if(socChart) socChart.destroy();
socChart=new Chart(ctx,{
type:'line',
data:{
labels:labels,
datasets:[
{
label:"SOC %",
data:soc,
borderColor:"blue",
yAxisID:"y1",
tension:0.2
},
{
label:"Pack Voltage",
data:voltage,
borderColor:"red",
yAxisID:"y2",
tension:0.2
}
]
},
options:{
plugins:{
title:{display:true,text:"SOC und Pack Voltage über Zeit"},
zoom:zoomOptions()
},
interaction:{mode:'index',intersect:false},
scales:{
y1:{
position:'left',
min:0,
max:100,
title:{display:true,text:"SOC %"}
},
y2:{
position:'right',
title:{display:true,text:"Voltage V"}
}
}
}
});
}
function renderDeltaChart(soc,delta){
const ctx=document.getElementById("deltaChart");
if(deltaChart) deltaChart.destroy();
deltaChart=new Chart(ctx,{
type:'line',
data:{
datasets:[
{
label:"Cell Delta (Max-Min)",
data:soc.map((s,i)=>({x:s,y:delta[i]})),
borderColor:"purple",
tension:0.2
}
]
},
options:{
plugins:{
title:{display:true,text:"Cell Imbalance über SOC"},
zoom:zoomOptions()
},
scales:{
x:{
type:'linear',
title:{display:true,text:"SOC %"}
},
y:{
title:{display:true,text:"Delta V"}
}
}
}
});
}
</script>
</body>
</html>
Re: Logfile Decoder für die Gen3 Zero
Verfasst: Mo 16. Mär 2026, 10:52
von xshunin
Wenn du die Säge oder den Bohrer aber nicht korrekt bedienst, kannst du dir sehr schnell mal was absägen, was nicht abgesägt werden soll. Das war dann aber auch mein letzter Einwurf. Als ITLer bin ich von Natur aus maximal skeptisch der KI gegenüber.
Re: Logfile Decoder für die Gen3 Zero
Verfasst: Mo 16. Mär 2026, 15:34
von ernstg
Sehr cool, dass da mehrere Leute dran arbeiten.
Ich habe meinen decoder auch ein bisschen bearbeitet.
die Daten werden als Tabelle mit Filter- und Sortiermöglichkeit dargestellt.
Ernst
https://ernstgl.github.io/ZeroGen3LogParser/
Re: Logfile Decoder für die Gen3 Zero
Verfasst: Sa 21. Mär 2026, 11:52
von el-hp
Moin zusammen,
Dank an Ernst für die Erstellung des Decoders.
Schon interessant was man so alles liest.
Im MBB ist mir aufgefallen, das meine DSRX in den Winterschlaf schaltet. <HIB> in der Spalte R
Ist doch kein Winter mehr, oder ist das in Abhängigkeit der Aussentemperatur ?
In der Garage wird es Nachts manchmal noch recht frisch.
Allen eine coole Saison.
Möge der Saft uns niemals ausgehen.
Re: Logfile Decoder für die Gen3 Zero
Verfasst: Sa 21. Mär 2026, 13:41
von ernstg
HIB ist nicht der Winterschlaf. Das ist der Ruhezustand aus dem das Motorrad nachts (bzw. wenn es abgestellt ist) stündlich erwacht (und die 12V Batterie lädt).