One message vocabulary¶
Both roles are built on one set of message definitions. This page says what that demands
of uds_protocol, what it demands of the application, and why implementing both roles
together is the only way to find out whether the set is complete.
Rationale: two definition sets for one protocol is two places for the wire format to live, and
they diverge silently — a tester built against one and an ECU built against the other
disagree only on the bench. Client and server therefore are built over the same
Four paths are exercised, and each message definition needs all four:
One definition set, four paths. Each diagonal pair is an inverse of the other, which is what makes the set testable.¶ This is why both roles are in the first pass. A server alone exercises request decoding and response encoding; a client alone exercises the other two. Building one role first proves half the definition set and leaves the other half unverified until the second role arrives — by which point the first role’s API has already been shaped around what was convenient for it. The four services of the first pass are chosen so that all four paths are exercised on each. The two roles also run in different build environments — the server in embedded
firmware, the client in host tooling — which What it demands of |
Architecture Element: The same definitions build for the embedded server and the host client UDSSVC_ARCH_0027
|
Rationale: a vehicle programme implements diagnostics on the ECU and interacts with that ECU from a
host tool, and the two agreeing is the whole requirement. If the shared types cannot
compile into both environments, the sharing is nominal and the host tool ends up with
its own transcription of the wire format — the exact divergence The two roles are not merely two APIs; they are two build environments. The server role
is compiled into embedded firmware for a bare-metal target, without Three consequences, each a constraint on something outside this crate:
What this element does not forbid is asynchrony. |
A data record’s length is not carried on the wire. The application’s identifier vocabulary is therefore the only thing that knows how long each record is, and it must supply that knowledge to the client half of this crate. Clause 11.2.1 states that the format and definition of the dataRecord shall be vehicle
manufacturer or system supplier specific, and the positive response message definition
in 11.2.3.1 lists The two roles need this differently, and the difference is precise:
The vocabulary supplies this as fn split_record<'a>(self, buf: &'a [u8])
-> Result<(&'a [u8], &'a [u8]), RecordError>;
A length is deliberately not the hook, and the reason is the codec’s own.
fn split_record<'a>(self, buf: &'a [u8]) -> Result<(&'a [u8], &'a [u8]), RecordError> {
match self {
Self::VehicleSpeed => split_via::<u16>(buf),
Self::Vin => split_via::<Vin>(buf),
}
}
Three things follow from taking the codec’s shape rather than inventing one.
Note what this crate must not do. It must not maintain its own table of identifier lengths, and it must not infer a record’s extent by assuming the records fill the message — with more than one identifier requested that inference is unsound, and with one requested it is right by accident. |