@emdzej/ncsx-coder
v0.8.0
Published
Top-level coding orchestrator: chassis + FA + FSW/PSW edits → per-SG netto buffers ready for CODIERDATEN_SCHREIBEN.
Readme
@emdzej/ncsx-coder
End-to-end coding orchestrator. Takes a chassis bundle, a user FA, and a list of
FSW/PSW edits, and produces a CodingPlan[] — one entry per SG that needs coding, each
with the encoded netto buffer ready to ship to apiJob(sgbd, 'SG_CODIEREN', hex(netto),
'').
This is the single entry point higher-level tools (CLI, web app, automation) reach for when they want to translate "user said: change KEYCARDREADER to eingebaut" into "bytes go on the wire here".
Spec: ../../docs/coding-flow.md.
What it does
FA string ──→ faToAsw ──→ AswSet ──→ selectEcus ──→ in-scope SGs
│
▼
per-SG edits ──→ load CABD ──→ encodeField → netto bytes
│
▼
CodingPlan { sgbd, jobName, netto, … }faToAsw(fa, { chassis })— convert the FA string into the ASW bit set.selectEcus(chassis, asw)— pick in-scope SGs by walkingSGAUSWAHL_*.- For each selected SG, group the edits that target it:
- if
edit.sgNameis set, only that SG; - otherwise, any SG whose CABD has a matching FSW id.
- if
- For each (SG, edits) pair, lazily load the CABD
.Cxxfile, find thePARZUWEISUNG_FSWrow for each FSW id, build aCabdRule, encode the PSW value into the SG's netto buffer. - Return one
CodingPlanper SG with the final netto byte buffer.
Install
pnpm add @emdzej/ncsx-coder
# or:
"@emdzej/ncsx-coder": "workspace:*"Quick start
import { loadChassis } from '@emdzej/ncsx-chassis';
import { nodeChassisSource } from '@emdzej/ncsx-chassis/node';
import { planCoding } from '@emdzej/ncsx-coder';
const chassis = await loadChassis(nodeChassisSource('…/DATEN'), 'E46');
const plans = await planCoding({
chassis,
fa: '0205 0502 0524',
edits: [
// Numeric FSW + PSW for now — name→id resolution is the next planned addition.
{ sgName: 'KMB', fsw: 0x025F, psw: 0x01 }, // KEYCARDREADER = eingebaut
],
});
for (const plan of plans) {
console.log(plan.sgName, plan.sgbd, plan.jobName, plan.netto.length, 'bytes');
// Hand plan.netto to apiJob(plan.sgbd, plan.jobName, hexEncode(plan.netto), '')
}API
planCoding(options: PlanCodingOptions): Promise<CodingPlan[]>
interface PlanCodingOptions {
chassis: Chassis; // from @emdzej/ncsx-chassis
fa: string; // user-supplied FA string
edits: CodingEdit[];
jobName?: string; // default 'SG_CODIEREN'
initialNetto?: Map<string, Uint8Array>; // per-SG starting buffers (from CODIERDATEN_LESEN)
codingIndex?: Map<string, number>; // per-SG CI override for CABD .Cxx selection
onWarning?: (msg: string) => void;
}
interface CodingEdit {
fsw: number; // u16 FSW id from PARZUWEISUNG_FSW
psw: number; // raw PSW value bytes interpreted as a number (currently)
sgName?: string; // pin to one SG; otherwise applies to all SGs that declare this FSW
index?: number; // if multiple FSW rows match, pin by INDEX
blocknr?: number; // if multiple match, pin by BLOCKNR
}
interface CodingPlan {
sgName: string;
sgbd: string; // EDIABAS SGBD file name (e.g. 'KMBI_E60')
cabd: string; // CABD module name (e.g. 'A_KMBI_E60')
jobName: string; // e.g. 'SG_CODIEREN'
netto: Uint8Array; // bytes to flash to this SG
applied: AppliedEdit[];
skipped: { edit: CodingEdit; reason: string }[];
source: SelectionSource; // which SGAUSWAHL_* block surfaced this SG
}Initial netto buffer
By default, each SG's plan starts from a zero-filled buffer sized to fit every
PARZUWEISUNG_FSW row's WORTADR+BYTEADR. If you want to flash a delta on top of the
ECU's current coding, pass initialNetto.get(sgName) for each SG — typically the bytes
you just read with CODIERDATEN_LESEN.
What it doesn't do (yet)
- Read ECU first. Production NCSEXPER calls
CODIERDATEN_LESENfor each SG to fetch the current coding, then splices edits on top. The orchestrator currently expects you to pre-fetch and pass viainitialNetto. - FSW/PSW name resolution.
fswmust be the numeric u16 id;pswmust be the raw value.chassis.swtFsw/chassis.swtPswgive you the lookups; a thin name-resolver helper is the next planned addition (seeSTATUS.mdresume entry points). - Wire transfer. The plan is data; sending it to the ECU is the caller's job —
typically
@emdzej/ediabasxcallingapiJob(plan.sgbd, plan.jobName, hex(plan.netto), '').
Related
@emdzej/ncsx-chassis— supplies the chassis bundle this consumes.@emdzej/ncsx-fa-asw+@emdzej/ncsx-ecu-select— used internally to pick SGs.@emdzej/ncsx-cabd— per-edit encoding.@emdzej/ncsx-trace— for the same edits as TRC/MAN files.- Spec:
../../docs/coding-flow.md.
