Logfile Decoder für die Gen3 Zero

koka_S
Beiträge: 305
Registriert: Di 14. Jun 2022, 03:23
Roller: Soco ts
PLZ: 3
Kontaktdaten:

Re: Logfile Decoder für die Gen3 Zero

Beitrag 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.
2026-03-14 07_24_13-BMS_MBB Log Viewer.jpg
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>
koka_S
Beiträge: 305
Registriert: Di 14. Jun 2022, 03:23
Roller: Soco ts
PLZ: 3
Kontaktdaten:

Re: Logfile Decoder für die Gen3 Zero

Beitrag 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.
stevelectric
Beiträge: 5
Registriert: Mi 3. Sep 2025, 09:46
Roller: Zero DSR/X 2025
PLZ: 81379
Kontaktdaten:

Re: Logfile Decoder für die Gen3 Zero

Beitrag von stevelectric »

Ja, funktioniert bei meiner DSRX, deutlich übersichtlicher als die reine Textdatei :D
Screenshot 2026-03-15 205900.png
Jetzt bitte noch einen Viewer für das MBB log mit Anzeige der Ereignisse auf einer Karte oder so :mrgreen:
Benutzeravatar
xshunin
Beiträge: 295
Registriert: Di 13. Dez 2022, 22:56
Roller: Zero SR/S (2023)
PLZ: 31535
Wohnort: Niedersachsen
Tätigkeit: IT-Administrator
Kontaktdaten:

Re: Logfile Decoder für die Gen3 Zero

Beitrag 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?
Benutzeravatar
MEroller
Moderator
Beiträge: 20042
Registriert: Mo 1. Nov 2010, 22:37
Roller: Zero S 11kW ZF10.5/erider Thunder (R.I.P)
PLZ: 7
Tätigkeit: Entwickler (Traktionsbatterie)
Kontaktdaten:

Re: Logfile Decoder für die Gen3 Zero

Beitrag 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.
Zero S 11kWZF10.5
e-rider Thunder 5000: Ruht in Frieden
koka_S
Beiträge: 305
Registriert: Di 14. Jun 2022, 03:23
Roller: Soco ts
PLZ: 3
Kontaktdaten:

Re: Logfile Decoder für die Gen3 Zero

Beitrag 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>
Benutzeravatar
xshunin
Beiträge: 295
Registriert: Di 13. Dez 2022, 22:56
Roller: Zero SR/S (2023)
PLZ: 31535
Wohnort: Niedersachsen
Tätigkeit: IT-Administrator
Kontaktdaten:

Re: Logfile Decoder für die Gen3 Zero

Beitrag 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.
ernstg
Beiträge: 88
Registriert: Fr 18. Aug 2023, 08:38
Roller: Zero SR/F
PLZ: 2464
Land: A
Kontaktdaten:

Re: Logfile Decoder für die Gen3 Zero

Beitrag 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/
Benutzeravatar
el-hp
Beiträge: 29
Registriert: Do 10. Apr 2025, 19:45
PLZ: 47918
Kontaktdaten:

Re: Logfile Decoder für die Gen3 Zero

Beitrag 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.
2025er DSR X
ernstg
Beiträge: 88
Registriert: Fr 18. Aug 2023, 08:38
Roller: Zero SR/F
PLZ: 2464
Land: A
Kontaktdaten:

Re: Logfile Decoder für die Gen3 Zero

Beitrag 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).
Antworten

Zurück zu „Zero“

Wer ist online?

Mitglieder in diesem Forum: MiSt und 4 Gäste