|
# Can_Uds — CAN 上 UDS 诊æ–å议(ISO 14229 / ISO 15765-2)
在 `ALib.CAN.ICanPort` 之上实现 UDS 诊æ–åè®®æ ˆï¼Œåˆ†ä¸‰å±‚ï¼š
```
UdsClient / UdsServer 应用层:UDS æœåŠ¡ï¼ˆISO 14229-1)
↓
UdsRequest / UdsResponse 报文å°è£…ã€NRCã€ä¼šè¯ä¸Šä¸‹æ–‡
↓
IsoTpChannel ä¼ è¾“å±‚ï¼šISO-TP(ISO 15765-2)多帧拆包/æµæŽ§
↓
ICanPort (ZLG / CX / PCAN) æ•°æ®é“¾è·¯å±‚:CAN 帧收å‘
```
## 文件
| 文件 | 内容 |
|------|------|
| `IsoTp.cs` | ISO-TP ä¼ è¾“å±‚ï¼šå¯»å€æ¨¡å¼ã€PCI 帧类型ã€SF/FF/CF/FC æ”¶å‘ã€å¤šå¸§é‡ç»„ã€æµæŽ§ä¸Žå®šæ—¶å‚æ•° |
| `UdsDefinition.cs` | æœåŠ¡ SIDã€NRCã€ä¼šè¯ã€å¤ä½ã€DTC 状æ€ã€ä¾‹ç¨‹ã€å®‰å…¨ç‰çº§ã€DIDã€å¸¸é‡ |
| `UdsMessage.cs` | `UdsRequest` / `UdsResponse` å°è£…ã€`UdsSession` 会è¯ä¸Šä¸‹æ–‡ã€`UdsException` |
| `UdsClient.cs` | Tester ä¾§å®¢æˆ·ç«¯ï¼šåŒæ¥è¯·æ±‚å“应ã€å¸¸ç”¨æœåŠ¡æ–¹æ³•ã€S3 ä¿æ´»ã€å®‰å…¨è§£é” |
| `UdsServer.cs` | ECU ä¾§æœåŠ¡ç«¯ï¼šæ³¨å†ŒæœåС处ç†å™¨å¹¶è‡ªåŠ¨å›žå“应,用于仿真与è”è°ƒ |
## 快速上手(客户端)
```csharp
using ALib.CAN;
using ALib.Can_Uds;
var port = ...; // 已打开的 ICanPort
port.Open();
// 在端å£ä¸Šåˆ›å»º UDS 客户端(æ¯ç«¯å£å•例,自动å¤ç”¨ ISO-TP 通é“)
var uds = port.GetOrAdd(UdsClient.KeyItems, () => UdsClient.CreateByEcu(port, 0));
uds.Log = XTrace.Log;
// 读 VIN(DID 0xF190)
uds.DiagnosticSessionControl(UdsSession_e.Extended);
var vin = uds.ReadDataByIdentifier(0xF190);
Console.WriteLine(vin.ToStr());
// è¿›å…¥ç¼–ç¨‹ä¼šè¯ + 安全解é”(需æä¾›ç§å→密钥算法)
uds.DiagnosticSessionControl(UdsSession_e.Programming);
uds.SecurityAccess(UdsSecurityLevel_e.RequestSeedLevel1, seed => KeyAlgorithm(seed));
// è¯»å– DTC
var dtcs = uds.ReadDtcByStatusMask(0xFF);
foreach (var d in dtcs) Console.WriteLine(d);
// 用完释放
uds.Close();
```
## 常用æœåŠ¡æ–¹æ³•ï¼ˆUdsClient)
| æœåŠ¡ | 方法 |
|------|------|
| 0x10 ä¼šè¯æŽ§åˆ¶ | `DiagnosticSessionControl(UdsSession_e)` |
| 0x11 å¤ä½ | `ECUReset(UdsReset_e)` |
| 0x14 清除 DTC | `ClearDiagnosticInformation()` |
| 0x19 读 DTC | `ReadDtcByStatusMask()` / `ReadDtcCount()` / `ReadDTCInformation()` |
| 0x22 è¯»æ•°æ® | `ReadDataByIdentifier(did)` |
| 0x23 è¯»å†…å˜ | `ReadMemoryByAddress(addr, len)` |
| 0x27 安全访问 | `SecurityAccess(level, keyAlgo)` / `SecurityAccessRequestSeed` / `SecurityAccessSendKey` |
| 0x28 通信控制 | `CommunicationControl(ctrl, type)` |
| 0x2E å†™æ•°æ® | `WriteDataByIdentifier(did, data)` |
| 0x2F IO 控制 | `InputOutputControlByIdentifier(did, ctrl, data)` |
| 0x31 例程控制 | `StartRoutine/StopRoutine/RequestRoutineResults` |
| 0x34/35/36/37 刷写 | `RequestDownload` / `Download` / `TransferData` / `RequestTransferExit` |
| 0x3D å†™å†…å˜ | `WriteMemoryByAddress(addr, data)` |
| 0x3E ä¿æ´» | `TesterPresent()` |
- `Request()` 返回 `UdsResponse`,超时返回 `null`,å¦å®šå“应通过 `IsNegative` / `Nrc` 判æ–。
- `RequestThrow()` 在å¦å®šå“应时抛出 `UdsException`ï¼ˆå« NRC ä¸Žä¸æ–‡æè¿°ï¼‰ã€‚
- è‡ªåŠ¨å¤„ç† `NRC 0x78`(å“应挂起):延长到 `P2*` ç»§ç»ç‰å¾…。
- `StartKeepAlive()` 在éžé»˜è®¤ä¼šè¯æ—¶è‡ªåŠ¨å‘¨æœŸå‘é€ `3E 80` ä¿æ´»ã€‚
## æœåŠ¡ç«¯ï¼ˆä»¿çœŸ ECU)
```csharp
var srv = port.GetOrAdd(UdsServer.KeyItems, () => new UdsServer(port, 0x7E0, 0x7E8));
srv.Register(new UdsSessionControlHandler());
srv.Register(new UdsTesterPresentHandler());
srv.Register(new UdsEcuResetHandler { OnReset = r => Console.WriteLine($"å¤ä½ {r}") });
// 自定义æœåŠ¡ï¼šè¿”å›ž null 表示抑制å“应;抛 UdsException 回å¦å®šå“应
srv.Register(UdsService_e.ReadDataByIdentifier, (server, req, session) =>
{
var did = (UInt16)((req[1] << 8) | req[2]);
if (did != 0xF190) throw new UdsException(UdsService_e.ReadDataByIdentifier, UdsNrc_e.RequestOutOfRange);
var body = new List<byte> { 0xF1, 0x90 }; // å“应体自动补 SID+0x40
body.AddRange(Encoding.ASCII.GetBytes("VIN1234567890ABCD"));
return body.ToArray();
});
```
## ISO-TP 说明
```csharp
var tp = new IsoTpChannel(port, 0x7E0, 0x7E8) { Log = XTrace.Log };
tp.Addressing = IsoTpAddressing_e.Normal; // 或 Extended 扩展寻å€
tp.Timing.N_Bs = 1000; // ç‰å¾…æµæŽ§è¶…æ—¶
tp.Timing.N_Cr = 1000; // 连ç»å¸§æŽ¥æ”¶è¶…æ—¶
tp.MessageReceived += (s, m) => Console.WriteLine(m);
tp.Send(new byte[] { 0x22, 0xF1, 0x90 }); // 自动å•帧/多帧
```
- æ”¯æŒæ™®é€šå¯»å€ï¼ˆNormal)与扩展寻å€ï¼ˆExtended);混åˆå¯»å€æœªå®žçŽ°ã€‚
- å‘é€ä¾§ï¼šè‡ªåŠ¨å‘ FF → ç‰ FC → 按 BS/STmin å‘ CF,支æŒå¤šè½®æµæŽ§çª—å£ä¸Ž `FS=Wait`。
- 接收侧:自动é‡ç»„ FF/CF å¹¶å›žé€ FC,åºå·ä¸è¿žç»/超时丢包。
- ç»å…¸ CAN å¤šå¸§ä¸Šé™ 4095 å—节。
## 线程模型(é‡è¦ï¼‰
- `IsoTpChannel.Process` ç”± CAN æŽ¥æ”¶çº¿ç¨‹å›žè°ƒï¼Œè´Ÿè´£æ”¶åŒ…ä¸ŽçŠ¶æ€æœºæŽ¨è¿›ã€‚
- `UdsClient` çš„æ‰€æœ‰è¯Šæ–æ–¹æ³•ã€`IsoTpChannel.Send` 都会**阻塞调用线程**直到完æˆ/超时。
- **ç¦æ¢åœ¨æŽ¥æ”¶çº¿ç¨‹æˆ– `MessageReceived` äº‹ä»¶å†…è°ƒç”¨è¯Šæ–æ–¹æ³•或å‘é€**,å¦åˆ™æ»é”。
|