学习了解充电桩协议OCPP——J规范

这一章拖了有点久......今天补上,估计这么久之前小姐姐的尝试应该没成功额,不过前段时间小伙伴内推了下硬件工厂的,面试电话说环境艰苦开荒的,我说我不怕哦,问了有没有电工证......没有,之后就没有之后了,emmm准备考个吧,感觉这个对于芯片也好汽车也好甚至就是日常生活都是有点用的......这个月官方报名满了,下个月看看......争取下个月可以考出,到时候再看看机会

这次是用了claudedcode帮忙翻译的,感觉也还不错哦,比页面直接丢文件要好用


OCPP 2.0.1 --- Part 4: OCPP-J 规范

OCPP 2.0.1 Edition 4 --- OCPP JSON/WebSocket Implementation Specification OCPP 2.0.1 第四版 --- OCPP JSON/WebSocket 实现规范


目录 (Table of Contents)

章节 标题 原文页码
--- 免责声明 (Disclaimer) ---
--- 版本历史 (Version History) ---
1 引言 (Introduction) 3--4
1.1 本文目的 (Purpose of this document) 3
1.2 目标读者 (Intended audience) 3
1.3 OCPP-S 与 OCPP-J (OCPP-S and OCPP-J) 3
1.4 约定 (Conventions) 3
1.5 定义与缩写 (Definitions & Abbreviations) 3
1.6 参考文献 (References) 4
2 优势与问题 (Benefits & Issues) 5
3 WebSocket 6--8
3.1 客户端请求 (Client request) 6--7
3.2 服务端响应 (Server response) 7
3.3 WebSocket 压缩 (WebSocket Compression) 8
4 RPC 框架 (RPC framework) 9--14
4.1 引言 (Introduction) 9--11
4.2 消息结构 (Message structures) 11--14
4.3 RPC 框架错误码 (RPC Framework Error Codes) 14
4.4 扩展回退机制 (Extension fallback mechanism) 14
5 连接 (Connection) 15--17
5.1 数据完整性 (Data integrity) 15
5.2 TLS 分片长度 (TLS fragment length) 15
5.3 WebSocket Ping 与 OCPP Heartbeat 15--17
5.4 重连 (Reconnecting) 17
5.5 网络节点层级 (Network node hierarchy) 17
6 OCPP 路由 (OCPP Routing) 18--19
6.1 本地控制器 (Local Controller) 18
6.2 连接 (Connections) 18
6.3 连接丢失 (Connection loss) 19
6.4 本地控制器发起的消息 19
6.5 本地控制器安全 (Local Controller Security) 19
7 签名消息 (Signed Messages) 21--22
7.1 签名消息格式 (Signed Message Format) 21
7.2 处理签名消息 (Handling Signed Messages) 21
7.3 允许的算法 (Allowed Algorithms) 21
7.4 密钥管理 (Key Management) 22
8 配置 (Configuration) 23
8.1 RetryBackOffRepeatTimes 23
8.2 RetryBackOffRandomRange 23
8.3 RetryBackOffWaitMinimum 23
8.4 WebSocketPingInterval 23
9 CustomData 扩展 (CustomData Extension) 25

免责声明 (Disclaimer)

原文

Copyright © 2010 -- 2025 Open Charge Alliance. All rights reserved.

This document is made available under the Creative Commons Attribution-NoDerivatives 4.0 International Public License (Legal Code - Attribution-NoDerivatives 4.0 International - Creative Commons).
译文

版权所有 © 2010 -- 2025 Open Charge Alliance。保留所有权利。

本文档依据 Creative Commons Attribution-NoDerivatives 4.0 International Public License (知识共享 署名-禁止演绎 4.0 国际公共许可证)提供 (Legal Code - Attribution-NoDerivatives 4.0 International - Creative Commons)。


版本历史 (Version History)

原文

Version Date Description
2.0.1 Edition 4 2025-12-03 OCPP 2.0.1 Edition 4. All errata from OCPP 2.0.1 Part 4 until and including Errata 2025-11 have been merged into this version of the specification.
2.0.1 Edition 3 2024-05-06 OCPP 2.0.1 Edition 3. All errata from OCPP 2.0.1 Part 4 until and including Errata 2024-04 have been merged into this version of the specification.
2.0.1 2020-03-31 Final version of OCPP 2.0.1
2.0 2018-04-11 OCPP 2.0 April 2018 --- Update for 2.0. Section added for OCPP Routing and Signed Messages
1.6 2015-10-08 Updated to 1.6 --- Asciidoc formatting, remove JSON schema for 1.5, some clarification, added 1.6 json schema

译文

版本 日期 说明
2.0.1 第4版 2025-12-03 OCPP 2.0.1 第4版。OCPP 2.0.1 Part 4 截至 Errata 2025-11 的所有勘误已合并到此版本中。
2.0.1 第3版 2024-05-06 OCPP 2.0.1 第3版。OCPP 2.0.1 Part 4 截至 Errata 2024-04 的所有勘误已合并到此版本中。
2.0.1 2020-03-31 OCPP 2.0.1 最终版本
2.0 2018-04-11 OCPP 2.0 2018年4月 --- 2.0 更新。新增 OCPP 路由和签名消息章节
1.6 2015-10-08 更新至 1.6 --- Asciidoc 格式化,移除 1.5 的 JSON schema,部分澄清,新增 1.6 JSON schema

1.1 --- 本文目的 (Purpose of this document)

原文页码 (Original Page): 3


原文 (Original)

The purpose of this document is to give the reader the information required to create a correct interoperable OCPP JSON implementation (OCPP-J). We will try to explain what is mandatory, what is considered good practice and what one should not do, based on our own experience. Undoubtedly misunderstandings or ambiguities will remain but by means of this document we aim to prevent them as much as possible.
译文 (Translation)

本文档的目的是向读者提供创建正确的、可互操作的 OCPP JSON 实现(OCPP-J)所需的信息。我们将基于自身经验,尝试解释哪些是强制性的、哪些被认为是良好实践、以及哪些是不应该做的。毫无疑问,误解或歧义仍会存在,但我们旨在通过本文档尽可能避免它们。

1.2 --- 目标读者 (Intended audience)

原文页码 (Original Page): 3


原文 (Original)

This document is intended for developers looking to understand and/or implement OCPP JSON in a correct and interoperable way. Rudimentary knowledge of implementing web services on a server or embedded device is assumed.
译文 (Translation)

本文档面向希望理解并且/或者以正确、可互操作的方式实现 OCPP JSON 的开发人员。假定读者具备在服务器或嵌入式设备上实现 Web 服务的基本知识。

1.3 --- OCPP-S 与 OCPP-J (OCPP-S and OCPP-J)

原文页码 (Original Page): 3


原文 (Original)

With the introduction of OCPP 1.6, there were two different flavors of OCPP; SOAP and JSON. To avoid confusion in communication on the type of implementation we recommend using the distinct suffixes -J and -S to indicate JSON or SOAP. In generic terms this would be OCPP-J for JSON and OCPP-S for SOAP. Version specific terminology would be OCPP1.6J or OCPP1.2S. If no suffix is specified for OCPP 1.2 or 1.5 then a SOAP implementation must be assumed. As of release 1.6 this can no longer be implicit and should always be made clear. If a system supports both the JSON and SOAP variant it is considered good practice to label this OCPP1.6JS instead of just OCPP1.6.

OCPP 2.0.1 only supports JSON, but it is preferable to keep using the -J designation. As a new transport mechanism might be introduced in a future version of OCPP. So it will be OCPP2.0.1J.
译文 (Translation)

从 OCPP 1.6 开始,OCPP 存在两种不同的形式:SOAP 和 JSON。为避免在沟通中对实现类型产生混淆,我们建议使用明确的后缀 -J 和 -S 来分别表示 JSON 或 SOAP。通用术语中,OCPP-J 表示 JSON,OCPP-S 表示 SOAP。版本特定术语如 OCPP1.6J 或 OCPP1.2S。如果 OCPP 1.2 或 1.5 未指定后缀,则必须假定为 SOAP 实现。自 1.6 版本起,不再允许隐式假定,应始终明确标注。如果系统同时支持 JSON 和 SOAP 变体,良好实践是标注为 OCPP1.6JS,而非仅 OCPP1.6。

OCPP 2.0.1 仅支持 JSON,但仍建议保留 -J 标识。因为未来版本的 OCPP 可能引入新的传输机制。因此正式名称为 OCPP2.0.1J。

1.4 --- 约定 (Conventions)

原文页码 (Original Page): 3


原文 (Original)

The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC2119.
译文 (Translation)

本文档中的关键词 "MUST"(必须)、"MUST NOT"(禁止)、"REQUIRED"(要求)、"SHALL"(应当)、"SHALL NOT"(不得)、"SHOULD"(建议)、"SHOULD NOT"(不建议)、"RECOMMENDED"(推荐)、"MAY"(可以)和 "OPTIONAL"(可选)的解释应按照 RFC2119 中的描述进行。
译者注 (Translator's Note)

以上关键词在 OCPP 规范中有严格的法律/技术含义,类似中文国标中的"应"、"宜"、"可"等措辞层级。译文将保留原文关键词的大小写形式,以便读者对照 RFC2119 理解其约束力等级。

1.5 --- 定义与缩写 (Definitions & Abbreviations)

原文页码 (Original Page): 3


原文 (Original)

Abbreviation Description
IANA Internet Assigned Numbers Authority (www.iana.org).
OCPP-J OCPP communication over WebSocket using JSON. Specific OCPP versions should be indicated with the J extension. OCPP2.0.1J means we are talking about a JSON/WebSocket implementation of 2.0.1.
OCPP-S OCPP communication over SOAP and HTTP(S). As of version 1.6 this should be explicitly mentioned. Older versions are assumed to be S unless clearly specified otherwise, e.g. OCPP1.5 is the same as OCPP1.5S
RPC Remote procedure call
WAMP WAMP is an open WebSocket subprotocol that provides messaging patterns to handle asynchronous data.

译文 (Translation)

缩写 说明
IANA 互联网号码分配机构 (Internet Assigned Numbers Authority, www.iana.org)。
OCPP-J 基于 WebSocket 使用 JSON 的 OCPP 通信。特定 OCPP 版本应带 J 后缀标明。OCPP2.0.1J 表示使用 JSON/WebSocket 的 2.0.1 实现。
OCPP-S 基于 SOAP 和 HTTP(S) 的 OCPP 通信。自 1.6 版本起应明确标注。更早版本默认假定为 S,除非另有明确说明,例如 OCPP1.5 等同于 OCPP1.5S。
RPC 远程过程调用 (Remote Procedure Call)
WAMP WAMP 是一种开放的 WebSocket 子协议,提供用于处理异步数据的消息传递模式。

1.6 --- 参考文献 (References)

原文页码 (Original Page): 4


原文 (Original)

Reference Description
ISO15118-2 Road vehicles -- Vehicle to grid communication interface -- Part 2: Technical protocol description and Open Systems Interconnection (OSI) layer requirements, Document Identifier: 69/216/CDV. [ISO 15118-2:2014 | IEC](https://webstore.iec.ch/publication/9273 "ISO 15118-2:2014
OCPP2.0-PART2 "OCPP 2.0.1: Part 2 - Specification". Download OCPP - Open Charge Alliance
RFC1951 "DEFLATE Compressed Data Format Specification version 1.3". https://www.ietf.org/rfc/rfc1951
RFC2119 "Key words for use in RFCs to Indicate Requirement Levels". S. Bradner. March 1997. http://www.ietf.org/rfc/rfc2119.txt
RFC2616 "Hypertext Transfer Protocol -- HTTP/1.1". RFC 2616 - Hypertext Transfer Protocol -- HTTP/1.1
RFC3629 "UTF-8, a transformation format of ISO 10646". RFC 3629 - UTF-8, a transformation format of ISO 10646
RFC3986 "Uniform Resource Identifier (URI): Generic Syntax". RFC 3986 - Uniform Resource Identifier (URI): Generic Syntax
RFC5246 "The Transport Layer Security (TLS) Protocol; Version 1.2". RFC 5246 - The Transport Layer Security (TLS) Protocol Version 1.2
RFC6455 "The WebSocket Protocol". RFC 6455 - The WebSocket Protocol
RFC7515 "JSON Web Signatures (JWS)". RFC 7515 - JSON Web Signature (JWS)
RFC7518 "JSON Web Algorithms (JWA)". RFC 7518 - JSON Web Algorithms (JWA)
RFC7617 "The 'Basic' HTTP Authentication Scheme". RFC 7617 - The 'Basic' HTTP Authentication Scheme
RFC7692 "Compression Extensions for WebSocket". RFC 7692 - Compression Extensions for WebSocket
RFC8259 "The JavaScript Object Notation (JSON) Data Interchange Format". T. Bray. December 2017. RFC 8259 - The JavaScript Object Notation (JSON) Data Interchange Format
RFC8265 Describes methods for handling Unicode strings representing usernames and passwords. RFC 8265 - Preparation, Enforcement, and Comparison of Internationalized Strings Representing Usernames and Passwords
WAMP The Web Application Messaging Protocol --- Web Application Messaging Protocol version 2 documentation

译文 (Translation)

参考文献 说明
ISO15118-2 道路车辆 --- 车辆到电网通信接口 --- 第2部分:技术协议描述与开放系统互连 (OSI) 层要求,文档标识符: 69/216/CDV。
OCPP2.0-PART2 "OCPP 2.0.1: 第2部分 - 规范"。Download OCPP - Open Charge Alliance
RFC1951 "DEFLATE 压缩数据格式规范 1.3 版"。
RFC2119 "用于 RFC 中指示需求等级的关键词"。S. Bradner。1997年3月。
RFC2616 "超文本传输协议 -- HTTP/1.1"。
RFC3629 "UTF-8,ISO 10646 的一种转换格式"。
RFC3986 "统一资源标识符 (URI): 通用语法"。
RFC5246 "传输层安全 (TLS) 协议;1.2 版"。
RFC6455 "WebSocket 协议"。
RFC7515 "JSON Web 签名 (JWS)"。
RFC7518 "JSON Web 算法 (JWA)"。
RFC7617 "'Basic' HTTP 认证方案"。
RFC7692 "WebSocket 压缩扩展"。
RFC8259 "JavaScript 对象表示法 (JSON) 数据交换格式"。T. Bray。2017年12月。
RFC8265 描述处理表示用户名和密码的 Unicode 字符串的方法。
WAMP WebSocket 应用消息传递协议。The Web Application Messaging Protocol --- Web Application Messaging Protocol version 2 documentation

2 --- 优势与问题 (Benefits & Issues)

原文页码 (Original Page): 5


原文 (Original)

The WebSocket protocol is defined in RFC6455. Working implementations of earlier draft WebSocket specifications exist, but OCPP-J implementations SHOULD use the protocol described in RFC6455.

Be aware that WebSocket defines its own message structure on top of TCP. Data sent over a WebSocket, on a TCP level, is wrapped in a WebSocket frame with a header. When using a framework this is completely transparent. When working for an embedded system however, WebSocket libraries may not be available and then one has to frame messages correctly according to RFC6455 him/herself.
译文 (Translation)

WebSocket 协议定义于 RFC6455。虽然存在基于早期 WebSocket 草案规范的工作实现,但 OCPP-J 实现 SHOULD(建议)使用 RFC6455 中描述的协议。

请注意,WebSocket 在 TCP 之上定义了自己的消息结构。在 TCP 层面,通过 WebSocket 发送的数据被包装在一个带头的 WebSocket 帧中。使用框架时这对开发者完全透明;但在嵌入式系统中,WebSocket 库可能不可用,此时开发者需要按 RFC6455 自行正确封装消息帧。

3.1 --- 客户端请求 (Client request)

原文页码 (Original Page): 6--7


原文 (Original)

For the connection between a Charging Station and a Charging Station Management System (CSMS) using OCPP-J, the CSMS acts as a WebSocket server and the Charging Station acts as a WebSocket client.

To set up a connection, the Charging Station initiates a WebSocket connection as described in RFC6455 section 4, "Opening Handshake".

OCPP-J imposes extra constraints on the URL and the WebSocket subprotocol, detailed in the following two sections 3.1.1 and 3.1.2.

The Client (Charging Station) SHALL keep this WebSocket connection open all the time.
译文 (Translation)

对于使用 OCPP-J 的充电桩 (Charging Station) 与充电站管理系统 (CSMS) 之间的连接,CSMS 充当 WebSocket 服务器,充电桩充当 WebSocket 客户端。

为建立连接,充电桩按照 RFC6455 第4节"握手开始"所述发起 WebSocket 连接。

OCPP-J 对 URL 和 WebSocket 子协议施加了额外约束,详见以下 3.1.1 和 3.1.2 两节。

客户端(充电桩)SHALL(应当)始终保持此 WebSocket 连接处于打开状态。


3.1.1 --- 连接 URL (The connection URL)

原文 (Original)

To initiate a WebSocket connection, the Charging Station needs a URL (RFC3986) to connect to. This URL is henceforth called the "connection URL". This connection URL is specific to a Charging Station. The Charging Station's connection URL contains the Charging Station identity so that the CSMS knows which Charging Station a WebSocket connection belongs to. However it is RECOMMENDED to let the CSMS NOT solely rely on the connection URL to identify a Charging Station, but to double-check the Charging Station's identity against their authentication credentials.

A CSMS supporting OCPP-J MUST provide at least one OCPP-J endpoint URL, from which the Charging Station SHOULD derive its connection URL. This OCPP-J endpoint URL can be any URL with a "ws" or "wss" scheme. How the Charging Station obtains an OCPP-J endpoint URL is outside of the scope of this document.

To derive its connection URL, the Charging Station modifies the OCPP-J endpoint URL by appending to the path first a '/' (U+002F SOLIDUS) and then a string uniquely identifying the Charging Station. This uniquely identifying string has to be percent-encoded / URL encoded as necessary as described in RFC3986.

Example 1: for a Charging Station with identity "CS001" connecting to a CSMS with OCPP-J endpoint URL "ws://csms.example.com/ocpp" this would give the following connection URL:

ws://csms.example.com/ocpp/CS001

Example 2: for a Charging Station with identity "RDAM|123" connecting to a CSMS with OCPP-J endpoint URL "wss://csms.example.com/ocppj" this would give the following URL:

wss://csms.example.com/ocppj/RDAM%7C123

The Charging Station identity datatype is identifierString (For definition see OCPP2.0.1-PART2). In addition, the colon ":" character SHALL NOT be used, because the unique identifier is also used for the basic authentication username. The colon ":" character is used to separate the basic authentication username and the password. The maximum length of the Charging Station identity is: 48 characters. (Note: Maximum length was chosen to ensure compatibility with EVSE ID from ISO15118-2.)
译文 (Translation)

为发起 WebSocket 连接,充电桩需要一个用于连接的 URL (RFC3986)。此 URL 以下称为"连接 URL" (connection URL)。连接 URL 对每个充电桩是唯一的,其中包含充电桩身份标识,以便 CSMS 知道 WebSocket 连接属于哪个充电桩。但 RECOMMENDED(推荐)CSMS 不要仅依赖连接 URL 来识别充电桩,而应结合认证凭据双重验证充电桩的身份。

支持 OCPP-J 的 CSMS MUST(必须)提供至少一个 OCPP-J 端点 URL,充电桩 SHOULD(建议)由此派生其连接 URL。OCPP-J 端点 URL 可以是任意带有 "ws" 或 "wss" scheme 的 URL。充电桩如何获取 OCPP-J 端点 URL 不在本文档范围内。

为派生连接 URL,充电桩在 OCPP-J 端点 URL 的路径后先追加一个 '/' (U+002F 斜线),再追加唯一标识充电桩的字符串。此唯一标识字符串必须按 RFC3986 进行百分号编码/URL 编码。

示例1: 身份标识为 "CS001" 的充电桩连接到 OCPP-J 端点 URL 为 "ws://csms.example.com/ocpp" 的 CSMS,连接 URL 为:

ws://csms.example.com/ocpp/CS001

示例2: 身份标识为 "RDAM|123" 的充电桩连接到 OCPP-J 端点 URL 为 "wss://csms.example.com/ocppj" 的 CSMS:

wss://csms.example.com/ocppj/RDAM%7C123

充电桩身份标识的数据类型为 identifierString(定义见 OCPP2.0.1-PART2)。此外,冒号 ":" 字符 SHALL NOT(不得)使用,因为该唯一标识同时用作 Basic 认证的用户名,而冒号用于分隔 Basic 认证的用户名和密码。充电桩身份标识的最大长度为 48 个字符。(注:选择此最大长度是为了确保与 ISO15118-2 中的 EVSE ID 兼容。)


3.1.2 --- OCPP 版本协商 (OCPP version negotiation)

原文 (Original)

The OCPP version(s) MUST be specified in the Sec-Websocket-Protocol field. The client lists the OCPP version(s) it supports in order of preference. They SHOULD be one or more of the following values:
译文 (Translation)

OCPP 版本 MUST(必须)在 Sec-Websocket-Protocol 字段中指定。客户端按偏好顺序列出其支持的 OCPP 版本。SHOULD(建议)为以下一个或多个值:
原文 (Original)

OCPP version WebSocket subprotocol name
1.2 ocpp1.2
1.5 ocpp1.5
1.6 ocpp1.6
2.0 ocpp2.0
2.0.1 ocpp2.0.1

译文 (Translation)

OCPP 版本 WebSocket 子协议名称
1.2 ocpp1.2
1.5 ocpp1.5
1.6 ocpp1.6
2.0 ocpp2.0
2.0.1 ocpp2.0.1

原文 (Original)

The ones for OCPP 1.2, 1.5, 1.6, 2.0 and 2.0.1 are official WebSocket subprotocol name values. They are registered as such with IANA.

Note that OCPP 1.2 and 1.5 are in the list. Since the JSON over WebSocket solution is independent of the actual message content the solution can be used for older OCPP versions as well. Please keep in mind that in these cases the implementation should preferably also maintain support for the SOAP based solution to be interoperable.

If the OCPP version is part of the OCPP-J endpoint URL it SHALL NOT determine the OCPP version to use, because the OCPP version is selected via the websocket protocol negotiation mechanism, as explained in section 3.3 Server response.
译文 (Translation)

OCPP 1.2、1.5、1.6、2.0 和 2.0.1 的值均为已在 IANA 注册的正式 WebSocket 子协议名称。

注意 OCPP 1.2 和 1.5 也在列表中。由于 JSON over WebSocket 方案独立于实际消息内容,该方案同样可用于旧版 OCPP。但请注意,在此情况下实现方为保持互操作性,最好也应维持对 SOAP 方案的支持。

即使 OCPP-J 端点 URL 中包含 OCPP 版本信息,SHALL NOT(不得)以此确定使用的 OCPP 版本,因为 OCPP 版本是通过 WebSocket 协议协商机制选定的,详见第 3.3 节服务端响应。


3.1.3 --- 握手请求示例 (Example of an opening HTTP request)

原文 (Original)

The following is an example of an opening HTTP request of an OCPP-J connection handshake:
译文 (Translation)

以下是 OCPP-J 连接握手中 HTTP 请求的示例:
原文 (Original)

python 复制代码
GET /webServices/ocpp/CS3211 HTTP/1.1
Host: some.server.com:33033
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==
Sec-WebSocket-Protocol: ocpp2.0.1, ocpp1.6
Sec-WebSocket-Version: 13

译文 + 注释 (Translation + Notes)

python 复制代码
# HTTP 请求行:使用 GET 方法,请求路径包含充电桩标识 CS3211
GET /webServices/ocpp/CS3211 HTTP/1.1
# 目标主机与端口
Host: some.server.com:33033
# 请求升级到 WebSocket 协议
Upgrade: websocket
Connection: Upgrade
# WebSocket 握手密钥(Base64 编码的随机16字节)
Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==
# 客户端支持的 OCPP 子协议列表,ocpp2.0.1 优先于 ocpp1.6
Sec-WebSocket-Protocol: ocpp2.0.1, ocpp1.6
# WebSocket 协议版本号
Sec-WebSocket-Version: 13

原文 (Original)

The bold parts are found as such in every WebSocket handshake request, the other parts are specific to this example.

In this example, the CSMS's OCPP-J endpoint URL is "ws://some.server.com:33033/webServices/ocpp". The Charging Station's unique identifier is "CS3211", so the path to request becomes "webServices/ocpp/CS3211".

With the Sec-WebSocket-Protocol header, the Charging Station indicates here that it can use OCPP2.0.1J and OCPP1.6J, with a preference for the former.

The other headers in this example are part of the HTTP and WebSocket protocols and are not relevant to those implementing OCPP-J on top of third-party WebSocket libraries. The roles of these headers are explained in RFC2616 and RFC6455.
译文 (Translation)

粗体部分在每次 WebSocket 握手请求中均相同,其余部分特定于此示例。

此示例中,CSMS 的 OCPP-J 端点 URL 是 "ws://some.server.com:33033/webServices/ocpp"。充电桩的唯一标识是 "CS3211",因此请求路径为 "webServices/ocpp/CS3211"。

通过 Sec-WebSocket-Protocol 头,充电桩表明其可以使用 OCPP2.0.1J 和 OCPP1.6J,优先选择前者。

示例中的其他头部属于 HTTP 和 WebSocket 协议的标准部分,对于基于第三方 WebSocket 库实现 OCPP-J 的开发者无需关注。这些头的作用参见 RFC2616RFC6455

3.2 --- 服务端响应 (Server response)

原文页码 (Original Page): 7


原文 (Original)

Upon receiving the Charging Station's handshake request, the CSMS has to finish the handshake with a response as described in RFC6455.

The following OCPP-J-specific conditions apply:

  • If the CSMS does not recognize the Charging Station identifier in the URL path, it SHOULD send an HTTP response with status 404 and abort the WebSocket connection as described in RFC6455.
  • If the CSMS does not agree to using one of the subprotocols offered by the client, it MUST complete the WebSocket handshake with a response without a Sec-WebSocket-Protocol header and then immediately close the WebSocket connection.

If the CSMS accepts the above example request and agrees to using OCPP 2.0.1J with the Charging Station, the CSMS's response will look as follows:
译文 (Translation)

收到充电桩的握手请求后,CSMS 必须按 RFC6455 所述以响应完成握手。

以下 OCPP-J 特定条件适用:

  • 如果 CSMS 不识别 URL 路径中的充电桩标识,SHOULD(建议)发送 HTTP 404 响应并按 RFC6455 中止 WebSocket 连接。
  • 如果 CSMS 不同意使用客户端提供的任一子协议,MUST(必须)以不含 Sec-WebSocket-Protocol 头的响应完成 WebSocket 握手,然后立即关闭 WebSocket 连接。

如果 CSMS 接受上述示例请求并同意使用 OCPP 2.0.1J,则响应如下:
原文 (Original)

python 复制代码
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
Sec-WebSocket-Protocol: ocpp2.0.1

译文 + 注释 (Translation + Notes)

python 复制代码
# HTTP 状态码 101 表示同意切换协议
HTTP/1.1 101 Switching Protocols
# 确认升级到 WebSocket 协议
Upgrade: websocket
Connection: Upgrade
# WebSocket 握手应答密钥(由客户端 Key + GUID 做 SHA-1 哈希后 Base64 编码)
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
# 服务端选定的子协议为 ocpp2.0.1
Sec-WebSocket-Protocol: ocpp2.0.1

原文 (Original)

The bold parts are found as such in every WebSocket handshake response, the other parts are specific to this example.

The role of the Sec-WebSocket-Accept header is explained in RFC6455.

The Sec-WebSocket-Protocol header indicates that the server will be using OCPP2.0.1J on this connection.

For a definition on how a server SHALL report a 'failure to process the message', see: CALLERROR.
译文 (Translation)

粗体部分在每次 WebSocket 握手响应中均相同,其余部分特定于此示例。

Sec-WebSocket-Accept 头的作用参见 RFC6455

Sec-WebSocket-Protocol 头表明服务端将在此连接上使用 OCPP2.0.1J。

关于服务端 SHALL(应当)如何报告"消息处理失败",参见 CALLERROR 的定义。

3.3 --- WebSocket 压缩 (WebSocket Compression)

原文页码 (Original Page): 8


原文 (Original)

OCPP 2.0.1 supports RFC 7692: Compression Extensions for WebSocket see: RFC7692.
译文 (Translation)

OCPP 2.0.1 支持 RFC 7692:WebSocket 压缩扩展,参见 RFC7692
原文 (Original)

Device WebSocket Compression Support
Charging Station Optional
CSMS Required
Local Controller Required

译文 (Translation)

设备 WebSocket 压缩支持要求
充电桩 (Charging Station) 可选 (Optional)
CSMS 必须 (Required)
本地控制器 (Local Controller) 必须 (Required)

原文 (Original)

The CSMS (and Local Controller) SHALL support RFC 7692, WebSocket compression, which is a relatively simple way to reduce mobile data usage. For a Charging Station this is not a hard requirement, as this might be more complex to implement on an embedded platform. It is RECOMMENDED to be implemented on a Charging Station, because it is an efficient solution to reduce mobile data usage.

RFC 7692 allows the Charging Station and the CSMS to do a negotiation during the connection setup. When both parties support the Compression Extension they will then use DEFLATE compression (RFC1951) when sending data over the line. When one of the parties doesn't support it, the JSON will be sent uncompressed (like in OCPP 1.6J).

When the Charging Station detects that compression is not used, it is RECOMMENDED not to close the connection, as turning off compression can be very useful during development, testing and debugging.

For more detailed information read the RFC 7692.
译文 (Translation)

CSMS(及本地控制器)SHALL(应当)支持 RFC 7692 WebSocket 压缩,这是减少移动数据使用量的相对简单的方法。对于充电桩这不是硬性要求,因为在嵌入式平台上实现可能更复杂。但 RECOMMENDED(推荐)充电桩也实现此功能,因为它是降低移动数据流量的高效方案。

RFC 7692 允许充电桩和 CSMS 在连接建立期间进行协商。当双方都支持压缩扩展时,将使用 DEFLATE 压缩 (RFC1951) 在线路上发送数据。当其中一方不支持时,JSON 将以未压缩方式发送(与 OCPP 1.6J 相同)。

当充电桩检测到未使用压缩时,RECOMMENDED(推荐)不要关闭连接,因为在开发、测试和调试期间关闭压缩非常有用。

更多详细信息请阅读 RFC 7692。

4.1 --- 引言 (Introduction)

原文页码 (Original Page): 9--11


原文 (Original)

A websocket is a full-duplex connection, simply put a pipe where data goes in and data can come out and without a clear relation between in and out. The WebSocket protocol by itself provides no way to relate messages as requests and responses. To encode these request/response relations we need a small protocol on top of WebSocket. This problem occurs in more use cases of WebSocket so there are existing schemes to solve it. The most widely-used is WAMP (see WAMP) but with the current version of that framework handling RPCs symmetrically is not WAMP compliant. Since the required framework is very simple we decided to define our own framework, inspired by WAMP, leaving out what we do not need and adding what we find missing.

Basically what we need is very simple: we need to send a message (CALL) and receive a reply (CALLRESULT) or an explanation why the message could not be handled properly (CALLERROR). For possible future compatibility we will keep the numbering of these message in sync with WAMP. Our actual OCPP message will be put into a wrapper that at least contains the type of message, a unique message ID and the payload, the OCPP message itself.
译文 (Translation)

WebSocket 是全双工连接,简单说就是一条数据进、数据出的管道,且进和出之间没有明确的对应关系。WebSocket 协议本身不提供将消息关联为请求和响应的机制。要编码这种请求/响应关系,我们需要在 WebSocket 之上构建一个小型协议。这个问题在 WebSocket 的众多用例中都会遇到,因此已有现成的解决方案。使用最广泛的是 WAMP(参见 WAMP),但当前版本的 WAMP 框架在对称处理 RPC 方面并不合规。由于所需框架非常简单,我们决定定义自己的框架,受 WAMP 启发,去掉不需要的部分,加入我们认为缺失的部分。

基本来说,我们需要的东西很简单:发送一条消息(CALL),收到一条应答(CALLRESULT)或者一个解释------为什么消息未能正确处理(CALLERROR)。为了未来可能的兼容性,我们将保持这些消息的编号与 WAMP 同步。真正的 OCPP 消息将被放入一个包装器中,该包装器至少包含消息类型、唯一消息 ID 和有效载荷------即 OCPP 消息本身。


4.1.1 --- 同步性 (Synchronicity)

原文 (Original)

A Charging Station or CSMS SHALL NOT send a CALL message to the other party unless the CALL message it sent before has been responded to or has timed out. This does not mean that the CSMS cannot send a message to another Charging Station, while waiting for a response of a first Charging Station, this rule is per OCPP-J connection. A CALL message has been responded to when a CALLERROR or CALLRESULT message has been received with the message ID of the CALL message.

A CALL message has timed out when:

  • it has not been responded to, and
  • an implementation-dependent timeout interval has elapsed since the message was sent.

Implementations are free to choose this timeout interval. It is RECOMMENDED that they take into account the kind of network used to communicate with the other party. Mobile networks typically have much longer worst-case round-trip times than fixed lines.

NOTE: The above requirements do not rule out that a Charging Station or CSMS will receive a CALL message from the other party while it is waiting for a CALLERROR or CALLRESULT. Such a situation is difficult to prevent because CALL messages from both sides can always cross each other.
译文 (Translation)

充电桩或 CSMS SHALL NOT(不得)在已发送的 CALL 消息被响应或超时之前向对方发送新的 CALL 消息。这并不意味着 CSMS 在等待第一个充电桩响应时不能向另一个充电桩发送消息------此规则针对每条 OCPP-J 连接。当收到包含 CALL 消息 messageId 的 CALLERROR 或 CALLRESULT 消息时,该 CALL 消息即为"已被响应"。

CALL 消息在以下情况下视为"已超时":

  • 尚未被响应,且
  • 自消息发送以来,一个取决于实现的超时间隔已过。

实现方可以自由选择此超时间隔。RECOMMENDED(推荐)考虑用于与对方通信的网络类型。移动网络的往返最坏时间通常比固定线路长得多。

注: 以上要求并不排除充电桩或 CSMS 在等待 CALLERROR 或 CALLRESULT 时收到来自对方的 CALL 消息。这种情况难以避免,因为双方发送的 CALL 消息始终可能交叉。


4.1.2 --- 消息有效性与字符编码 (Message validity and Character encoding)

原文 (Original)

The whole message consisting of wrapper and payload MUST be valid JSON encoded with the UTF-8 (see RFC3629) character encoding. Furthermore, the Charging Station and CSMS have the right to reject messages which are not conform the JSON schemas.

Note that all valid US-ASCII text is also valid UTF-8, so if a system sends only US-ASCII text, all messages it sends comply with the UTF-8 requirement. Non US-ASCII characters SHOULD only be used for sending natural-language text. An example of such natural-language text is the MessageType which contains the text of a DisplayMessage in OCPP 2.0.1.
译文 (Translation)

由包装器和有效载荷组成的完整消息 MUST(必须)是有效的 JSON,并使用 UTF-8(参见 RFC3629)字符编码。此外,充电桩和 CSMS 有权拒绝不符合 JSON Schema 的消息。

注意,所有有效的 US-ASCII 文本同时也是有效的 UTF-8,因此如果系统仅发送 US-ASCII 文本,其所有消息均符合 UTF-8 要求。非 US-ASCII 字符 SHOULD(建议)仅用于发送自然语言文本。此类自然语言文本的一个示例是 OCPP 2.0.1 中包含 DisplayMessage 文本的 MessageType。


4.1.3 --- 消息类型 (The message type)

原文 (Original)

To identify the type of message one of the following Message Type Numbers MUST be used.
译文 (Translation)

为标识消息类型,MUST(必须)使用以下消息类型编号之一:
原文 (Original)

MessageType Message Type Number Description
CALL 2 Request message, i.e. messages ending in "Request"
CALLRESULT 3 Response message, i.e. message ending in "Response"
CALLERROR 4 Error response to a request message

译文 (Translation)

消息类型 消息类型编号 说明
CALL 2 请求消息,即以 "Request" 结尾的消息
CALLRESULT 3 响应消息,即以 "Response" 结尾的消息
CALLERROR 4 对请求消息的错误响应

原文 (Original)

When a system receives a message with a Message Type Number not in this list, it SHALL ignore the message payload. Each message type may have additional required fields.
译文 (Translation)

当系统收到消息类型编号不在此列表中的消息时,SHALL(应当)忽略该消息的载荷。每种消息类型可能有额外的必填字段。


4.1.4 --- 消息 ID (Message ID)

原文 (Original)

The message ID serves to identify a request. A message ID for any CALL message MUST be different from all message IDs previously used by the same sender for any other CALL message on any WebSocket connection using the same unique Charging Station identifier. The message ID for a retried message (e.g. when no response was received within timeout) MAY be identical to the message ID of the original message.

A message ID for a CALLRESULT or CALLERROR message MUST be equal to that of the CALL message that the CALLRESULT or CALLERROR message is a response to.
译文 (Translation)

消息 ID 用于标识请求。任何 CALL 消息的 messageId MUST(必须)与同一发送方此前在使用相同充电桩唯一标识的任何 WebSocket 连接上发送的所有其他 CALL 消息的 messageId 不同。重试消息的 messageId(例如超时未收到响应时重发)MAY(可以)与原消息的 messageId 完全相同。

CALLRESULT 或 CALLERROR 消息的 messageId MUST(必须)等于其所响应的 CALL 消息的 messageId。
原文 (Original)

Name Datatype Restrictions
messageId string36 Unique message ID, maximum length of 36 characters, to allow for UUIDs/GUIDs

译文 (Translation)

名称 数据类型 约束
messageId string36 唯一消息 ID,最大长度 36 个字符,以兼容 UUID/GUID

4.1.5 --- JSON 载荷 (JSON Payload)

原文 (Original)

The Payload of a message is a JSON object containing the arguments relevant to the Action.

If there is no payload JSON allows for two different notations: null or an empty object {}. Although it seems trivial, we consider it good practice to only use the empty object statement. Null usually represents something undefined, which is not the same as empty, and also {} is shorter.

When a field is optional in the OCPP action (0..1 or 0..*) and is left empty for a specific request/response, JSON allows for a couple of different ways to put this in a JSON string. But because OCPP is designed for wireless links, OCPP only allows 1 option: Do not put the field in the payload (so null, {} or \[\] are not allowed for an empty field).

When a field has a cardinality of zero/one to many (0..* or 1..*) and it has been given one entity, then it will still remain a list, but of size 1.
译文 (Translation)

消息的载荷 (Payload) 是一个包含 Action 相关参数的 JSON 对象。

如果没有载荷内容,JSON 允许两种记法:null 或空对象 {}。虽然看似细微,我们将其视为良好实践,仅使用空对象记法。Null 通常表示"未定义"而非"空",且 {} 更短。

当 OCPP action 中某字段为可选(0..1 或 0..*)且在特定请求/响应中留空时,JSON 有多种方式表示。但 OCPP 是为无线链路设计的,因此只允许一种方式:不要将该字段放入载荷(即不允许用 null、{} 或 \[\] 表示空字段)。

当某字段的基数为一对多(0..* 或 1..*)且仅赋予一个实体时,它仍然保持为列表形式,只是列表大小为 1。


4.1.6 --- Action

原文 (Original)

The Action field in the CALL message MUST be the OCPP message name without the "Request" suffix.

For example: For a "BootNotificationRequest" the action field will be set to "BootNotification".

BTW: The CALLRESULT does not contain the action field. A client can match the Response (CALLRESULT) with the Request (CALL) via the MessageId field.
译文 (Translation)

CALL 消息中的 Action 字段 MUST(必须)为去掉 "Request" 后缀的 OCPP 消息名称。

例如:对于 "BootNotificationRequest",Action 字段设为 "BootNotification"。

顺带一提:CALLRESULT 不包含 Action 字段。客户端可通过 MessageId 字段将响应 (CALLRESULT) 与请求 (CALL) 进行匹配。


4.1.7 --- 消息有效性 (Message Validity)

原文 (Original)

A message is only valid when:

  • Action is a known Action.
  • The JSON payload is valid JSON
  • All the required fields for the Action are present
  • All data is of the correct data type.

When a message is not valid, the server SHALL respond with a CALLERROR.
译文 (Translation)

消息仅在满足以下条件时有效:

  • Action 是已知的 Action。
  • JSON 载荷是有效 JSON。
  • Action 的所有必填字段均已提供。
  • 所有数据均为正确的数据类型。

当消息无效时,服务端 SHALL(应当)以 CALLERROR 响应。

4.2 --- 消息结构 (Message structures for different message types)

原文页码 (Original Page): 11--14


注 (Note): 您可能注意到以下段落中缺少充电桩身份标识。身份标识在 WebSocket 连接握手期间交换,是连接的属性。每条消息均由该身份发送或发往该身份,因此无需在每条消息中重复。


4.2.1 --- CALL

原文 (Original)

A CALL always consists of 4 elements: The standard elements MessageTypeId and MessageId, a specific Action that is required on the other side and a payload, the arguments to the Action. The syntax of a CALL looks like this:

[<MessageTypeId>, "<MessageId>", "<Action>", {<Payload>}]
译文 (Translation)

CALL 始终由 4 个元素组成:标准元素 MessageTypeId 和 MessageId、一个要求对方执行的 Action、以及载荷(Action 的参数)。CALL 的语法如下:

[<MessageTypeId>, "<MessageId>", "<Action>", {<Payload>}]
原文 (Original)

Field Datatype Meaning
MessageTypeId integer This is a Message Type Number which is used to identify the type of the message.
MessageId string36 This is a unique identifier that will be used to match request and result.
Action string The name of the remote procedure or action. This field SHALL contain a case-sensitive string. The field SHALL contain the OCPP Message name without the "Request" suffix. For example: For a "BootNotificationRequest", this field shall be set to "BootNotification".
Payload JSON JSON Payload of the action, see: JSON Payload for more information.

译文 (Translation)

字段 数据类型 含义
MessageTypeId integer 消息类型编号,用于标识消息的类型。
MessageId string36 唯一标识符,用于将请求与结果进行匹配。
Action string 远程过程或操作的名称。此字段 SHALL(应当)包含区分大小写的字符串,内容为去掉 "Request" 后缀的 OCPP 消息名称。例如 "BootNotificationRequest" 对应 "BootNotification"。
Payload JSON Action 的 JSON 载荷,详见 JSON Payload 章节。

原文 (Original)

For example, a BootNotificationRequest could look like this:
译文 (Translation)

例如,BootNotificationRequest 可能如下所示:
原文 (Original)

python 复制代码
[2,
"19223201",
"BootNotification",
{
    "reason": "PowerUp",
    "chargingStation": {
    "model": "SingleSocketCharger",
    "vendorName": "VendorX"
    }
}
]

带注释版本 (Annotated)

python 复制代码
[
  2,                           // [MessageTypeId] 2 = CALL(请求消息)
  "19223201",                  // [MessageId] 唯一消息标识符,用于匹配响应
  "BootNotification",          // [Action] 操作名称(BootNotificationRequest 去掉 Request 后缀)
  {                            // [Payload] JSON 载荷------Action 的参数
    "reason": "PowerUp",       //   启动原因:上电启动
    "chargingStation": {       //   充电桩信息
      "model": "SingleSocketCharger", // 型号
      "vendorName": "VendorX"         // 厂商名称
    }
  }
]

4.2.2 --- CALLRESULT

原文 (Original)

If the call can be handled correctly the result will be a regular CALLRESULT. Error situations that are covered by the definition of the OCPP response definition are not considered errors in this context. They are regular results and as such will be treated as a normal CALLRESULT, even if the result is undesirable for the recipient.

A CALLRESULT always consists of 3 elements: The standard elements MessageTypeId and MessageId and a payload, containing the response to the Action in the original Call.

The syntax of a CALLRESULT looks like this:

[<MessageTypeId>, "<MessageId>", {<Payload>}]
译文 (Translation)

如果调用可以正确处理,结果将为常规的 CALLRESULT。那些被 OCPP 响应定义所涵盖的错误情况在此上下文中不被视为错误。它们是常规结果,因此将作为普通 CALLRESULT 处理,即使结果对接收方来说不尽如人意。

CALLRESULT 始终由 3 个元素组成:标准元素 MessageTypeId 和 MessageId,以及一个载荷,其中包含对原始 CALL 中 Action 的响应。

CALLRESULT 的语法如下:

[<MessageTypeId>, "<MessageId>", {<Payload>}]
原文 (Original)

Field Datatype Meaning
MessageTypeId integer This is a Message Type Number which is used to identify the type of the message.
MessageId string36 This MUST be the exact same ID that is in the call request so that the recipient can match request and result.
Payload JSON JSON Payload of the action, see: JSON Payload for more information.

译文 (Translation)

字段 数据类型 含义
MessageTypeId integer 消息类型编号,用于标识消息的类型。
MessageId string36 MUST(必须)与 CALL 请求中的 ID 完全一致,以便接收方匹配请求和结果。
Payload JSON Action 的 JSON 载荷,详见 JSON Payload 章节。

原文 (Original)

For example, a BootNotification response could look like this:
带注释版本 (Annotated)

python 复制代码
[
  3,                                // [MessageTypeId] 3 = CALLRESULT(响应消息)
  "19223201",                       // [MessageId] 与请求的 messageId 一致
  {                                 // [Payload] JSON 载荷------Action 的响应数据
    "currentTime": "2013-02-01T20:53:32.486Z", // 服务端当前时间(ISO 8601 UTC)
    "interval": 300,                //   心跳间隔(秒),充电桩应每 300 秒发送一次 Heartbeat
    "status": "Accepted"            //   注册状态:已接受
  }
]

4.2.3 --- CALLERROR

原文 (Original)

We only use CALLERROR in two situations:

  1. An error occurred during the transport of the message. This can be a network issue, an availability of service issue, etc.
  2. The call is received but the content of the call does not meet the requirements for a proper message. This could be missing mandatory fields, an existing call with the same unique identifier is being handled already, unique identifier too long, invalid JSON or OCPP syntax etc.

When a server needs to report a 'failure to process the message', the server SHALL use a Message Type: CallError (MessageTypeNumber = 4).

When a server receives a corrupt message, the CALLERROR SHALL NOT directly include syntactically invalid JSON (For example, without encoding it first). When also the MessageId cannot be read, the CALLERROR SHALL contain "-1" as MessageId.

When a message contains any invalid OCPP and/or it is not conform the JSON schema, the system is allowed to drop the message.
译文 (Translation)

CALLERROR 仅在两种情况下使用:

  1. 消息传输期间发生错误。可能是网络问题、服务可用性问题等。
  2. 调用已收到,但消息内容不符合规范要求。可能是缺少必填字段、已存在使用相同唯一标识的待处理调用、唯一标识过长、JSON 无效或 OCPP 语法错误等。

当服务端需要报告"消息处理失败"时,SHALL(应当)使用消息类型:CallError (MessageTypeNumber = 4)。

当收到损坏的消息时,CALLERROR SHALL NOT(不得)直接包含语法无效的 JSON(例如不先对其进行编码)。当 MessageId 也无法读取时,CALLERROR SHALL(应当)包含 "-1" 作为 MessageId。

当消息包含任何无效的 OCPP 内容且/或不符合 JSON schema 时,系统可以丢弃该消息。
原文 (Original)

A CALLERROR always consists of 5 elements: The standard elements MessageTypeId and MessageId, an errorCode string, an errorDescription string and an errorDetails object.

The syntax of a CALLERROR looks like this:

[<MessageTypeId>, "<MessageId>", "<errorCode>", "<errorDescription>", {<errorDetails>}]
译文 (Translation)

CALLERROR 始终由 5 个元素组成:标准元素 MessageTypeId 和 MessageId,一个 errorCode 字符串、一个 errorDescription 字符串和一个 errorDetails 对象。

CALLERROR 的语法如下:

[<MessageTypeId>, "<MessageId>", "<errorCode>", "<errorDescription>", {<errorDetails>}]
原文 (Original)

Field Datatype Meaning
MessageTypeId integer This is a Message Type Number which is used to identify the type of the message.
MessageId string36 This MUST be the exact same id that is in the call request so that the recipient can match request and result.
ErrorCode string This field MUST contain a string from the RPC Framework Error Codes table.
ErrorDescription string255 Should be filled in if possible, otherwise a clear empty string "".
ErrorDetails JSON This JSON object describes error details in an undefined way. If there are no error details you MUST fill in an empty object {}.

译文 (Translation)

字段 数据类型 含义
MessageTypeId integer 消息类型编号,用于标识消息类型。
MessageId string36 MUST(必须)与 CALL 请求中的 ID 完全一致,以便接收方匹配请求和结果。
ErrorCode string MUST(必须)包含 RPC 框架错误码表中的字符串。
ErrorDescription string255 应尽可能填写清晰描述,否则填写空字符串 ""。
ErrorDetails JSON 以非预定义方式描述错误详情的 JSON 对象。如无错误详情 MUST(必须)填入空对象 {}。

原文 (Original)

For example, a CALLERROR could look like this:
带注释版本 (Annotated)

python 复制代码
[
  4,                                    // [MessageTypeId] 4 = CALLERROR(错误响应)
  "162376037",                          // [MessageId] 对应原始请求的 messageId
  "NotSupported",                       // [ErrorCode] 错误码:不支持该操作
  "SetDisplayMessageRequest not supported", // [ErrorDescription] 错误描述
  {}                                    // [ErrorDetails] 无额外详情,填入空对象
]

4.3 --- RPC 框架错误码 (RPC Framework Error Codes)

原文页码 (Original Page): 14


原文 (Original)

The following table contains all the allowed error codes for the OCPP RPC Framework.
译文 (Translation)

下表包含 OCPP RPC 框架的所有允许的错误码。
原文 (Original)

ErrorCode Description
FormatViolation Payload for Action is syntactically incorrect
GenericError Any other error not covered by the more specific error codes in this table
InternalError An internal error occurred and the receiver was not able to process the requested Action successfully
MessageTypeNotSupported A message with an Message Type Number received that is not supported by this implementation.
NotImplemented Requested Action is not known by receiver
NotSupported Requested Action is recognized but not supported by the receiver
OccurrenceConstraintViolation Payload for Action is syntactically correct but at least one of the fields violates occurrence constraints
PropertyConstraintViolation Payload is syntactically correct but at least one field contains an invalid value
ProtocolError Payload for Action is not conform the PDU structure
RpcFrameworkError Content of the call is not a valid RPC Request, for example: MessageId could not be read.
SecurityError During the processing of Action a security issue occurred preventing receiver from completing the Action successfully
TypeConstraintViolation Payload for Action is syntactically correct but at least one of the fields violates data type constraints (e.g. "somestring": 12)

译文 (Translation)

错误码 说明
FormatViolation Action 的载荷语法不正确(格式违规)
GenericError 未被本表更具体的错误码涵盖的任何其他错误(通用错误)
InternalError 接收方发生内部错误,无法成功处理请求的 Action(内部错误)
MessageTypeNotSupported 收到的消息类型编号不被本实现支持(消息类型不支持)
NotImplemented 请求的 Action 不为接收方所知(未实现)
NotSupported 请求的 Action 被识别但接收方不支持(不支持)
OccurrenceConstraintViolation Action 载荷语法正确,但至少有一个字段违反了出现次数约束(出现次数违规)
PropertyConstraintViolation 载荷语法正确,但至少有一个字段包含无效值(属性约束违规)
ProtocolError Action 的载荷不符合 PDU 结构(协议错误)
RpcFrameworkError 调用内容不是有效的 RPC 请求,例如 MessageId 无法读取(RPC 框架错误)
SecurityError 处理 Action 期间出现安全问题,阻止接收方成功完成 Action(安全错误)
TypeConstraintViolation Action 载荷语法正确,但至少有一个字段违反数据类型约束,例如 "somestring": 12(类型约束违规)

译者注 (Translator's Note)

以上错误码是 OCPP RPC 框架层面的错误,与业务层(如 BootNotification 返回 Rejected)不同。实际开发中,FormatViolation、PropertyConstraintViolation 和 TypeConstraintViolation 是最常见的入参校验相关错误。

4.4 --- 扩展回退机制 (Extension fallback mechanism)

原文页码 (Original Page): 14


原文 (Original)

Future versions of OCPP might add extra Message Types (other than CALL, CALLRESULT and CALLERROR).

When an OCPP 2.0.1 implementation receives a message with an unknown message type, it SHALL respond with a CALLERROR with errorCode: MessageTypeNotSupported. This SHOULD notify the sending party about the not supported Message Type. The Sending Party SHALL then either terminate the connection, or fallback to the known: CALL, CALLRESULT and CALLERROR.
译文 (Translation)

OCPP 的未来版本可能会增加额外的消息类型(除 CALL、CALLRESULT 和 CALLERROR 之外)。

当 OCPP 2.0.1 实现收到未知消息类型的消息时,SHALL(应当)以 errorCode 为 MessageTypeNotSupported 的 CALLERROR 进行响应。此举 SHOULD(建议)通知发送方其不支持该消息类型。发送方 SHALL(应当)随后要么终止连接,要么回退到已知的消息类型:CALL、CALLRESULT 和 CALLERROR。

5.1 --- 数据完整性 (Data integrity)

原文页码 (Original Page): 15


原文 (Original)

For data integrity we rely on the underlying TCP/IP transport layer mechanisms.
译文 (Translation)

对于数据完整性,我们依赖底层的 TCP/IP 传输层机制来保障。

5.2 --- TLS 分片长度 (TLS fragment length)

原文页码 (Original Page): 15


原文 (Original)

TLS involves sending "Records" between peers. Records can be of type "Handshake", "Alert", "ChangeCipherSpec", "Heartbeat" or "Application". OCPP messages are sent in Application records. The payload contains a "fragment" of the application data. The record layer fragments information blocks into TLSPlaintext records carrying data in chunks of 2^14 bytes (16kB) or less.

TLS peers need to maintain an input and an output buffer to store an entire fragment of 16 kB. For a low resource device it is a large cost to allocate 32 kB for the TLS connection.
译文 (Translation)

TLS 涉及在对等方之间发送"记录" (Records)。记录类型可以是 "Handshake"(握手)、"Alert"(告警)、"ChangeCipherSpec"(更改密码规范)、"Heartbeat"(心跳)或 "Application"(应用)。OCPP 消息在 Application 记录中发送。其载荷包含应用数据的"片段" (fragment)。记录层将信息块分解为 TLSPlaintext 记录,以 2^14 字节 (16kB) 或更小的块承载数据。

TLS 对等方需要维护输入和输出缓冲区以存储完整的 16 kB 片段。对于资源有限的设备,为 TLS 连接分配 32 kB 内存成本很高。
原文 (Original)

flowchart

python 复制代码
graph LR
    subgraph EVSE
  A["OCPP"] --> B["TLS impl."]
  B --> C["out_buf (16kB)"]
  B --> D["in_buf (16kB)"]
        E["TCP"] <--> F["CSMS"]
    end
    subgraph CSMS
  G["TCP"] --> H["TLS impl."]
  H --> I["out_buf (16kB)"]
  H --> J["in_buf (16kB)"]
        K["OCPP"]
    end

Figure 1. Peers allocating standard 16 kB TLS buffers

译文 (Translation)

流程图

python 复制代码
graph LR
    subgraph EVSE["EVSE(充电桩侧)"]
  A["OCPP"] --> B["TLS 实现"]
  B --> C["输出缓冲区 out_buf (16kB)"]
  B --> D["输入缓冲区 in_buf (16kB)"]
        E["TCP"] <--> F["CSMS"]
    end
    subgraph CSMS["CSMS(服务端侧)"]
  G["TCP"] --> H["TLS 实现"]
  H --> I["输出缓冲区 out_buf (16kB)"]
  H --> J["输入缓冲区 in_buf (16kB)"]
        K["OCPP"]
    end

图1:对等方分配标准 16 kB TLS 缓冲区

原文 (Original)

A TLS extension is defined in TLS Extensions RFC6066 Section 4, that allows the client to ask for a different maximum fragment length than the default 16kB. A client can ask for a maximum fragment length of 0.5 kB, 1 kB, 2 kB or 4 kB. This TLS extension is, however, not widely supported and native managed cloud TLS termination services typically don't support this.

A resource-constrained Charging Station SHOULD try to negotiate a smaller TLS maximum fragment size, and if that is not accepted by the peer, then Charging Station MAY unilaterally decide to allocate less memory to its TLS output buffer. A TLS maximum fragment length of 2 kB is suggested based on data collection during certification tests, which shows that 99% of the messages fit in a 2 kB buffer.
译文 (Translation)

TLS 扩展 RFC6066 第4节定义了一个 TLS 扩展,允许客户端请求不同于默认 16kB 的最大片段长度。客户端可请求 0.5 kB、1 kB、2 kB 或 4 kB 的最大片段长度。然而,此 TLS 扩展并未被广泛支持,原生托管云 TLS 终止服务通常不支持。

资源受限的充电桩 SHOULD(建议)尝试协商较小的 TLS 最大片段大小;如果对方不接受,充电桩 MAY(可以)单方面决定为其 TLS 输出缓冲区分配更少内存。根据认证测试期间的数据收集,建议使用 2 kB 作为 TLS 最大片段长度,数据显示 99% 的消息均在 2 kB 缓冲区内。
原文 (Original)

flowchart

python 复制代码
graph LR
    subgraph EVSE
  A["OCPP"] --> B["TLS impl."]
  B --> C["out_buf (2kB)"]
  B --> D["in_buf (16kB)"]
        E["TCP"] <--> F["CSMS"]
    end
    subgraph CSMS
  G["TCP"] --> H["TLS impl."]
  H --> I["out_buf (16kB)"]
  H --> J["in_buf (16kB)"]
        K["OCPP"]
    end

Figure 2. Charging Station allocating a 2 kB TLS output buffer

译文 (Translation)

流程图

python 复制代码
graph LR
    subgraph EVSE["EVSE(充电桩侧)"]
  A["OCPP"] --> B["TLS 实现"]
  B --> C["输出缓冲区 out_buf (2kB) ← 仅2kB"]
  B --> D["输入缓冲区 in_buf (16kB)"]
        E["TCP"] <--> F["CSMS"]
    end
    subgraph CSMS["CSMS(服务端侧)"]
  G["TCP"] --> H["TLS 实现"]
  H --> I["输出缓冲区 out_buf (16kB)"]
  H --> J["输入缓冲区 in_buf (16kB)"]
        K["OCPP"]
    end

图2:充电桩分配 2 kB TLS 输出缓冲区

5.3 --- WebSocket Ping 与 OCPP Heartbeat 的关系 (WebSocket Ping in relation to OCPP Heartbeat)

原文页码 (Original Page): 15--17


原文 (Original)

The WebSocket specification defines Ping and Pong frames that are used to check if the remote endpoint is still responsive. In practice this mechanism is also used to prevent the network operator from quietly closing the underlying network connection after a certain period of inactivity. This websocket feature can be used as a substitute for most of the OCPP Heartbeat messages, but cannot replace all of its functionality. A Heartbeat message checks connectivity end-to-end, whereas a Websocket ping/pong only checks from point-to-point. This makes a difference in an extended network topology with a Local Controller between Charging Station and CSMS.

Another important aspect of the Heartbeat response is time synchronisation. The Ping and Pong frames cannot be used for this so at least one original Heartbeat message a day is recommended to ensure a correct clock setting on the Charging Station.
译文 (Translation)

WebSocket 规范定义了 Ping 和 Pong 帧,用于检查远端是否仍然响应。在实践中,此机制也用于防止网络运营商在一定时间的无活动后悄悄关闭底层网络连接。这一 WebSocket 特性可以替代大部分 OCPP Heartbeat 消息,但不能完全取代其所有功能。Heartbeat 消息检查的是端到端 (end-to-end) 的连通性,而 WebSocket ping/pong 只检查点对点 (point-to-point)。在充电桩与 CSMS 之间存在本地控制器 (Local Controller) 的扩展网络拓扑中,这一点存在差异。

Heartbeat 响应的另一个重要方面是时间同步。Ping 和 Pong 帧无法用于此目的,因此建议每天至少发送一条原生的 Heartbeat 消息,以确保充电桩的时钟设置正确。

5.4 --- 重连 (Reconnecting)

原文页码 (Original Page): 17


原文 (Original)

When the connection is lost, the Charging Station SHALL try to reconnect. When reconnecting, the Charging Station SHALL use an increasing back-off time with some randomization until it has successfully reconnected. This prevents an overload of the CSMS when all Charging Stations reconnect after a restart of the CSMS.

The first reconnection attempts SHALL be after a back-off time of: RetryBackOffWaitMinimum seconds, plus a random value with a maximum of RetryBackOffRandomRange seconds. After every failed reconnection attempt the Charging Station SHALL double the previous back-off time, with a maximum of RetryBackOffRepeatTimes, adding a new random value with a maximum of RetryBackOffRandomRange seconds to every reconnection attempt. After RetryBackOffRepeatTimes reconnection attempts, the Charging Station SHALL keep reconnecting with the last back-off time, not increasing it any further. After a successful connection the backoff wait timer SHALL be reset to RetryBackOffWaitMinimum seconds.

When reconnecting, a Charging Station SHOULD NOT send a BootNotification unless one or more of the elements in the BootNotification have changed since the last connection. For the previous SOAP based solutions this was considered good practice but when using WebSocket the server can already make the match between the identity and a communication channel at the moment the connection is established. There is no need for an additional message.
译文 (Translation)

当连接丢失时,充电桩 SHALL(应当)尝试重连。重连时,充电桩 SHALL(应当)使用递增的退避时间并加入随机化,直到成功重连。这可以防止在 CSMS 重启后所有充电桩同时重连导致的过载。

首次重连尝试 SHALL(应当)在以下退避时间后进行:RetryBackOffWaitMinimum 秒 + 一个最大为 RetryBackOffRandomRange 秒的随机值。每次失败的重连尝试之后,充电桩 SHALL(应当)将之前的退避时间加倍,最多增加 RetryBackOffRepeatTimes 次,每次重连尝试都添加一个新的随机值(最大为 RetryBackOffRandomRange 秒)。经过 RetryBackOffRepeatTimes 次重连尝试后,充电桩 SHALL(应当)以最后一次的退避时间继续重连,不再增加。成功连接后,退避等待计时器 SHALL(应当)重置为 RetryBackOffWaitMinimum 秒。

重连时,充电桩 SHOULD NOT(不建议)发送 BootNotification,除非自上次连接以来 BootNotification 中的一个或多个元素发生了变化。对于之前的 SOAP 方案,这被视为良好实践;但在使用 WebSocket 时,服务端在连接建立时即可完成身份标识与通信通道的匹配,无需额外的消息。

5.5 --- 网络节点层级 (Network node hierarchy)

原文页码 (Original Page): 17


原文 (Original)

The physical network topology is not influenced by a choice for JSON or SOAP. In case of JSON however the issues with Network Address Translation (NAT) have been resolved by letting the Charging Station open a TCP connection to the CSMS and keeping this connection open for communication initiated by the CSMS. It is therefore no longer necessary to have a smart device capable of interpreting and redirecting SOAP calls in between the CSMS and the Charging Station.
译文 (Translation)

物理网络拓扑不受选择 JSON 还是 SOAP 的影响。然而,在 JSON 方案下,网络地址转换 (NAT) 的问题已通过让充电桩主动向 CSMS 发起 TCP 连接并保持连接打开来支持 CSMS 发起的通信而得到解决。因此,不再需要在 CSMS 和充电桩之间部署能够解析和重定向 SOAP 调用的智能设备。

6.1 --- 本地控制器 (Local Controller)

原文页码 (Original Page): 18


原文 (Original)

For some topologies it is required to route OCPP-J messages. For example when implementing a Local Controller.

This section contains a solution for OCPP message routing that will work with any Charging Station and CSMS.

A Local controller is a device that sits between the CSMS and any number of Charging Stations, creating a local group. It is located near to the Charging Station (maybe even connected wired to the Charging Stations), so it does not have problem of losing the connection to the Charging Stations. This is practically useful for doing Local Smart Charging: load balancing between the Charging Stations on the same location. The Local Controller can see all the messages, ongoing transactions etc. It can send charging profiles to the Charging Station to influence the energy used by the Charging Stations, this way preventing the group to use more energy than available at the location at that time.

The Local Controller SHALL work so the Charging Station doesn't have to behave different when connected to the Local Controller, compared to a direct connection to a CSMS. A Local Controller SHALL work so that a Charging Station can work out of the box with the Local Controller, requiring only the parameters that are needed to connect to the Local Controller to be set. The Local Controller SHALL work so that the CSMS can not notice if the Charging Station is connecting to it directly, or via the Local Controller.
译文 (Translation)

某些拓扑结构中需要路由 OCPP-J 消息。例如实现本地控制器 (Local Controller) 时。

本节提供了可与任何充电桩和 CSMS 配合工作的 OCPP 消息路由方案。

本地控制器是位于 CSMS 和任意数量充电桩之间的设备,形成一个本地群组。由于部署在充电桩附近(甚至可能通过有线方式连接充电桩),因此不存在与充电桩断连的问题。这对实现"本地智能充电"非常实用:即在同一位置的多台充电桩之间进行负载均衡。本地控制器可以看到所有消息、进行中的交易等信息,并可以向充电桩发送充电曲线 (Charging Profile) 以影响其能量使用,从而防止群组用电超过该地点当时可用的总能量。

本地控制器 SHALL(应当)以如下方式工作:充电桩在连接到本地控制器时的行为不应与直接连接到 CSMS 时有任何不同;充电桩应能开箱即用地与本地控制器配合,仅需设置连接到本地控制器所需的参数;CSMS 无法察觉充电桩是直接连接还是通过本地控制器连接。
原文 (Original)

flowchart

python 复制代码
graph TD
  A["CSMS"] --> B["Local Controller CS00"]
  B --> C["Charging Station CS03"]
  B --> D["Charging Station CS02"]
  B --> E["Charging Station CS01"]

Figure 3. Local Controller Topology

译文 (Translation)

流程图

python 复制代码
graph TD
  A["CSMS(充电站管理系统)"] --> B["本地控制器 Local Controller CS00"]
  B --> C["充电桩 CS03"]
  B --> D["充电桩 CS02"]
  B --> E["充电桩 CS01"]

图3:本地控制器拓扑结构

6.2 --- 连接 (Connections)

原文页码 (Original Page): 18


原文 (Original)

For each Charging Station that connects to the Local Controller, two connections will be established:

  1. A WebSocket connection from the Charging Station to the Local Controller (configured in the Charging Station)
  2. A WebSocket connection from the Local Controller to the CSMS (configured in the Local Controller)

Both connections SHOULD use a similar connection URI with the same Charging Station identifier. To the CSMS, the connection from the Local Controller appears to be a regular Charging Station connection.

The Local Controller may open a separate WebSocket connection to the CSMS that allows the CSMS to address the Local Controller directly, which may be useful for changing settings or setting overall Charging Profiles.

When a Charging Station connects to the Local Controller, it SHALL connect to it like it would to a CSMS, using the same URI Path in the connection URL as it would use to connect to the CSMS. When the connection between Charging Station and the CSMS is successfully set up, the Local Controller SHALL set up a WebSocket connection to the CSMS with the same URI Path in the connection URL that was used by the Charging Station to setup the connection. The Local Controller SHALL open a WebSocket connection for every Charging Station that connects to it.
译文 (Translation)

对于每台连接到本地控制器的充电桩,将建立两条连接:

  1. 充电桩到本地控制器的 WebSocket 连接(在充电桩侧配置)
  2. 本地控制器到 CSMS 的 WebSocket 连接(在本地控制器侧配置)

两条连接 SHOULD(建议)使用相似的连接 URI 和相同的充电桩标识。对于 CSMS 而言,来自本地控制器的连接看起来就是普通的充电桩连接。

本地控制器可以额外打开一条独立的 WebSocket 连接到 CSMS,使 CSMS 能够直接寻址本地控制器,这对于更改设置或设定全局充电曲线很有用。

当充电桩连接到本地控制器时,SHALL(应当)像连接到 CSMS 一样,使用与连接 CSMS 时相同的 URI 路径。当充电桩到 CSMS 的连接成功建立后,本地控制器 SHALL(应当)使用与充电桩建连时相同的 URI 路径建立到 CSMS 的 WebSocket 连接。本地控制器 SHALL(应当)为每台连接到它的充电桩打开一条 WebSocket 连接。
原文 (Original)

flowchart

python 复制代码
graph TD
  A["CSMS"] -->|WebSocket ws://csms.example.com/ocpp/CS00| B["Local Controller CS00"]
  A -->|WebSocket ws://csms.example.com/ocpp/CS01| B
  A -->|WebSocket ws://csms.example.com/ocpp/CS02| B
  A -->|WebSocket ws://csms.example.com/ocpp/CS03| B
  B -->|WebSocket ws://controller.example.local/ocpp/CS03| C["Charging Station CS03"]
  B -->|WebSocket ws://controller.example.local/ocpp/CS02| D["Charging Station CS02"]
  B -->|WebSocket ws://controller.example.local/ocpp/CS01| E["Charging Station CS01"]
    style A fill:#f9f,stroke:#333
    style B fill:#ccf,stroke:#333
    style C fill:#cfc,stroke:#333
    style D fill:#cfc,stroke:#333
    style E fill:#cfc,stroke:#333

Figure 4. Local Controller WebSocket Connections

译文 (Translation)

流程图

python 复制代码
graph TD
  A["CSMS"] -->|"WebSocket ws://csms.example.com/ocpp/CS00"| B["本地控制器 CS00"]
  A -->|"WebSocket ws://csms.example.com/ocpp/CS01"| B
  A -->|"WebSocket ws://csms.example.com/ocpp/CS02"| B
  A -->|"WebSocket ws://csms.example.com/ocpp/CS03"| B
  B -->|"WebSocket ws://controller.example.local/ocpp/CS03"| C["充电桩 CS03"]
  B -->|"WebSocket ws://controller.example.local/ocpp/CS02"| D["充电桩 CS02"]
  B -->|"WebSocket ws://controller.example.local/ocpp/CS01"| E["充电桩 CS01"]
    style A fill:#f9f,stroke:#333
    style B fill:#ccf,stroke:#333
    style C fill:#cfc,stroke:#333
    style D fill:#cfc,stroke:#333
    style E fill:#cfc,stroke:#333

图4:本地控制器 WebSocket 连接 --- CSMS 侧每条连接对应一个充电桩标识,本地控制器侧使用本地域名

6.3 --- 连接丢失 (Connection loss)

原文页码 (Original Page): 19


原文 (Original)

Whenever one or more WebSocket connections between CSMS and the Local Controller are lost, the Local Controller SHALL close all corresponding WebSockets to the Charging Stations that are connected to it, unless the Local Controller is capable of responding to Charging Station requests, and forwards transaction-related requests to the CSMS once the connection is restored. This is needed to force the Charging Station to queue messages as it would have done if it would have been connected to the CSMS directly and would have lost the connection to that CSMS.

Whenever the connection between a Charging Station and the Local Controller is lost, the Local Controller SHALL close the WebSocket connection it has for the Charging Station to the CSMS. This is needed to let the CSMS know the Charging Station is offline and no CSMS initiated messages can be sent to it.
译文 (Translation)

当 CSMS 与本地控制器之间的一条或多条 WebSocket 连接丢失时,本地控制器 SHALL(应当)关闭其与所连充电桩之间所有对应的 WebSocket 连接。除非本地控制器有能力响应充电桩的请求,并在连接恢复后将交易相关请求转发给 CSMS。此举是为了强制充电桩将消息排队,就像它直接连接到 CSMS 并丢失了与该 CSMS 连接时会做的那样。

当充电桩与本地控制器之间的连接丢失时,本地控制器 SHALL(应当)关闭其为该充电桩建立的到 CSMS 的 WebSocket 连接。这是为了让 CSMS 知道该充电桩已离线,无法向其发送 CSMS 发起的消息。

6.4 --- 本地控制器发起的消息 (Local Controller initiated messages)

原文页码 (Original Page): 19


原文 (Original)

The Local Controller SHALL relay any Charging Station initiated message to the CSMS (and vice versa).

Since the Local Controller can also initiate its own messages to the Charging Station(s), a Local Controller SHALL take care of the following:

  1. If a Local Controller sends its own messages to a Charging Station, it SHALL guarantee that its messages have IDs that do not collide with IDs used by the CSMS, now and in the future. This can be done by either assigning a range of numbers to the Local Controller to use (and the CSMS to skip), or by using UUIDs/GUIDs.
  2. Replies to messages from the Charging Station to messages initiated by the Local Controller SHALL NOT be sent to the CSMS.
    译文 (Translation)

本地控制器 SHALL(应当)将充电桩发起的任何消息中继给 CSMS(反之亦然)。

由于本地控制器也可以向充电桩发起自己的消息,本地控制器 SHALL(应当)注意以下事项:

  1. 如果本地控制器向充电桩发送自己的消息,SHALL(应当)确保其消息 ID 不会与 CSMS 使用过的或将使用的 ID 冲突。可以通过以下方式实现:为本地控制器分配一个数字区间(CSMS 跳过该区间),或者使用 UUID/GUID。
  2. 对充电桩回复本地控制器发起消息的响应 SHALL NOT(不得)发送给 CSMS。

6.5 --- 本地控制器安全 (Local Controller Security)

原文页码 (Original Page): 19


原文 (Original)

For the local controller, the normal OCPP security mechanisms will be used, as described in OCPP2.0.1-PART2, part A. Security. All security profiles described there MAY be used when a Local Controller is deployed. The security section (part A) only describes the roles of the CSMS, and Charging Station. When a local controller is used, the security specification SHALL be interpreted as follows:

  • In the connection from the Charging Station to the Local Controller, the Charging Station SHALL act as the Charging Station, and the Local Controller SHALL act as the CSMS. When TLS is used, the Local Controller SHALL be the TLS server, and the Charging Station SHALL be the TLS client.
  • In the connection from the Local Controller to the CSMS, the Local Controller SHALL act as the Charging Station, and the CSMS SHALL act as the CSMS. When TLS is used, the CSMS SHALL be the TLS server, and the Local Controller SHALL be the TLS client.

When TLS with Client Side Certificates is used, the Local Controller SHALL have both a CSMS Certificate, and a Charging Station certificate (see OCPP2.0.1-PART2 Part A - Keys used in OCPP), as it can function in both roles. These certificates SHALL be unique to the Local Controller. The Local Controller SHALL NOT store the Charging Station certificates of the attached Charging Stations. It SHALL also NOT store the CSMS Certificate of the CSMS. These certificates SHALL be kept private on their respective owners. The Local Controller SHALL only use its own certificates for setting up the TLS connections.

It SHALL be possible to distinguish the Local Controller from the CSMS based on the URL in the CSMS Certificate. Because the Local Controller is placed in the field, there is a risk that its certificates get stolen from it, e.g. by an attack on the hardware. In that case, it SHALL only be possible to use the CSMS Certificate on the Local Controller to communicate with the attached Charging Stations, not with any other Charging Stations in the infrastructure.

The TLS connections terminate on the Local Controller. So, the Local Controller can both read and manipulate data sent between the CSMSs and Charging Stations. If the security of the Local Controller is compromised, it will affect all attached Charging Stations. It is therefore RECOMMENDED to take sufficient security measures to protect the Local Controller. It is also RECOMMENDED to sign critical commands or replies with the mechanism described in Signed Messages. In this way, it can be detected if the Local Controller tries to manipulate data.
译文 (Translation)

针对本地控制器,将使用 OCPP2.0.1-PART2 第A部分"安全"中描述的常规 OCPP 安全机制。部署本地控制器时 MAY(可以)使用其中描述的所有安全配置文件。安全章节(第A部分)仅描述了 CSMS 和充电桩的角色。使用本地控制器时,安全规范 SHALL(应当)按如下解释:

  • 在充电桩到本地控制器的连接中,充电桩 SHALL(应当)充当充电桩角色,本地控制器 SHALL(应当)充当 CSMS 角色。当使用 TLS 时,本地控制器 SHALL(应当)为 TLS 服务端,充电桩 SHALL(应当)为 TLS 客户端。
  • 在本地控制器到 CSMS 的连接中,本地控制器 SHALL(应当)充当充电桩角色,CSMS SHALL(应当)充当 CSMS 角色。当使用 TLS 时,CSMS SHALL(应当)为 TLS 服务端,本地控制器 SHALL(应当)为 TLS 客户端。

当使用带客户端证书的 TLS 时,本地控制器 SHALL(应当)同时拥有 CSMS 证书和充电桩证书(参见 OCPP2.0.1-PART2 第A部分 --- OCPP 中使用的密钥),因为它可以扮演两种角色。这些证书 SHALL(应当)是本地控制器独有的。本地控制器 SHALL NOT(不得)存储所连接充电桩的充电桩证书,也 SHALL NOT(不得)存储 CSMS 的 CSMS 证书。这些证书 SHALL(应当)由其各自所有者保密。本地控制器 SHALL(应当)仅使用自己的证书建立 TLS 连接。

SHALL(应当)能够根据 CSMS 证书中的 URL 将本地控制器与 CSMS 区分开来。由于本地控制器部署于现场,其证书存在被窃取的风险(例如通过硬件攻击)。在此情况下,SHALL(应当)仅能使用本地控制器上的 CSMS 证书与所连接的充电桩通信,而不能与基础设施中的任何其他充电桩通信。

TLS 连接终结于本地控制器。因此,本地控制器既能读取也能操控 CSMS 与充电桩之间发送的数据。如果本地控制器的安全性被攻破,将影响所有连接的充电桩。因此 RECOMMENDED(推荐)采取充分的安全措施保护本地控制器。同时 RECOMMENDED(推荐)使用"签名消息" (Signed Messages) 中描述的机制对关键命令或回复进行签名。这样,如果本地控制器试图操控数据,将能被检测到。

7.1 --- 签名消息格式 (Signed Message Format)

原文页码 (Original Page): 21


原文 (Original)

For Signed OCPP Messages, JWS is used. For more information see: RFC7515.

Suppose we have an OCPP call encoded in the OCPP-J message:

[<MessageTypeId>, "<MessageId>", {<Extension>}, "<Action>", {<Payload>}]

Then we define the equivalent signed message as follows.

[<MessageTypeId>, "<MessageId>", {<Extension>}, "<SignedAction>", {<SignedPayload>}]

The MessageTypeId and MessageId SHALL stay the same. The <SignedAction> field SHALL be the action name (<Action>) with the string "-Signed" appended. For instance, if <Action> is "BootNotification", then <SignedAction> is "BootNotification-Signed". The <SignedPayload> SHALL be the JWS encoding of the payload, computed according to the following settings:

  • The JWS Payload SHALL be the <Payload> from the original message.
  • The JWS Protected Header SHALL contain a field called "OCPPAction" containing the name (<Action>) of the OCPP action, and a field called "OCPPMessageTypedId" containing the message ID (<MessageTypeId>).
  • The JWS Protected Header SHOULD contain the x5t#S256 field to identify the key used for signing, as specified in Section 7.4.
  • The <SignedPayload> SHALL be encoded using the Flattened JWS JSON Serialization syntax.
    译文 (Translation)

OCPP 签名消息使用 JWS (JSON Web Signature)。详见 RFC7515

假设有一条编码为 OCPP-J 消息的 OCPP 调用:

[<MessageTypeId>, "<MessageId>", {<Extension>}, "<Action>", {<Payload>}]

则对应的签名消息定义如下:

[<MessageTypeId>, "<MessageId>", {<Extension>}, "<SignedAction>", {<SignedPayload>}]

MessageTypeId 和 MessageId SHALL(应当)保持不变。<SignedAction> 字段 SHALL(应当)为操作名称 <Action> 后追加字符串 "-Signed"。例如 <Action> 为 "BootNotification",则 <SignedAction> 为 "BootNotification-Signed"。<SignedPayload> SHALL(应当)为载荷的 JWS 编码,按以下设置计算:

  • JWS 载荷 SHALL(应当)为原始消息中的 <Payload>
  • JWS 受保护头部 SHALL(应当)包含名为 "OCPPAction" 的字段(值为 OCPP 操作名称 <Action>),以及名为 "OCPPMessageTypedId" 的字段(值为消息类型编号 <MessageTypeId>)。
  • JWS 受保护头部 SHOULD(建议)包含 x5t#S256 字段,用于标识签名所用密钥,详见第 7.4 节。
  • <SignedPayload> SHALL(应当)使用 Flattened JWS JSON Serialization 语法进行编码。

格式变换示意:

python 复制代码
原始消息:     [2, "msg001", "BootNotification",       {...payload...}]
                      ↓ 签名封装
签名消息:     [2, "msg001", "BootNotification-Signed", {...JWS payload...}]

7.2 --- 处理签名消息 (Handling Signed Messages)

原文页码 (Original Page): 21


原文 (Original)

When a Charging Station or CSMS receives a signed message, it SHALL extract the encapsulated normal message. It SHALL process it normally, following the OCPP 2.0.1 standard. It MAY perform additional actions that use the digital signature. This is optional, because a secure connection between the CSMS and the Charging Station is expected, hence a second process to validate a signature on message level is redundant. When a Charging Station receives a signed request, and it supports digital signing, it SHALL send a signed reply.
译文 (Translation)

当充电桩或 CSMS 收到签名消息时,SHALL(应当)提取其中封装的普通消息,并按照 OCPP 2.0.1 标准正常处理。MAY(可以)执行使用数字签名的额外操作。这是可选的,因为 CSMS 和充电桩之间预期已有安全连接(TLS),因此在消息层面再次验证签名是冗余的。当充电桩收到签名请求且其支持数字签名时,SHALL(应当)发送签名回复。
处理流程总结:

  1. 收到 <SignedAction> → 提取 <Payload>(验签可选)
  2. 正常处理提取出的 <Action>
  3. 若支持签名 → 响应也用签名消息返回

7.3 --- 允许的算法 (Allowed Algorithms)

原文页码 (Original Page): 21


原文 (Original)

The algorithms allowed for use with JSON Web Signatures are defined in the JSON Web Algorithms standard RFC7518. To limit the cryptographic algorithms that a Charging Station has to implement, for OCPP the same algorithms SHALL be used as for the TLS connection used to secure communications. This means that for generating the digital signatures, the Charging Station and CSMS SHALL use the following algorithms from the JSON Web Algorithms standard RFC7518, section 3.1:

  • ES256: ECDSA using P-256 and SHA-256
  • RS256: RSASSA-PKCS1-v1_5 using SHA-256
  • RS384: RSASSA-PKCS1-v1_5 using SHA-384

Note that RS256 and ES256 are the algorithms recommended by RFC7518.
译文 (Translation)

允许用于 JSON Web 签名的算法由 JSON Web 算法标准 RFC7518 定义。为限制充电桩需实现的密码算法数量,OCPP SHALL(应当)使用与保护通信的 TLS 连接相同的算法。这意味着在生成数字签名时,充电桩和 CSMS SHALL(应当)使用 RFC7518 第3.1节中的以下算法:

算法 说明 安全等级
ES256 ECDSA(椭圆曲线数字签名),使用 P-256 曲线 + SHA-256 128-bit
RS256 RSASSA-PKCS1-v1_5,使用 SHA-256 128-bit
RS384 RSASSA-PKCS1-v1_5,使用 SHA-384 192-bit

注意,RS256 和 ES256 是 RFC7518 推荐的算法。

7.4 --- 密钥管理 (Key Management)

原文页码 (Original Page): 22


原文 (Original)

This section does not prescribe specific keys to be used for digital signatures. The CSMS Certificate and Charging Station Certificate, used for setting up a secure TLS connection, MAY be used for signing. For many use cases, these will however not be the correct keys. For instance, if the use case is to provide non-repudiation of meter readings, the messages should be signed with a certificate stored in the calibrated measuring chip.

To be able to verify the digital signature, one needs to know which key was used to sign it. JSON Web Signatures supports several ways to store a key identifier with the signed message. As the certificates that can be used for signing are not specified, hash values will be used to identify them. Within the OCPP, the Charging Station and CSMS SHOULD include the field x5t#S256 in the JWS Protected Header to identify the certificate. Following the JSON Web Signatures standard RFC7515 the value of this field SHOULD be set to the SHA-256 hash of the DER encoding of the signing certificate. How stakeholders can look up the certificate based on the hash value is out of scope for this document.

NOTE: In the set up with a Local Controller, described in Local Controller, the TLS client key that would be signing messages to the CSMS will, in fact, not be the TLS client key that the TLS connection is using, since the key in the Local Controller is different from that in the Charging Station. Similarly, the TLS server key signing messages will be that of the CSMS, not that of the local controller. Therefore, implementation of the protocol MUST NOT regard this mismatch as invalidating the signatures; in fact, it is an expected and desired property to provide end-to-end authenticity.
译文 (Translation)

本节不规定数字签名所使用的具体密钥。用于建立安全 TLS 连接的 CSMS 证书和充电桩证书 MAY(可以)用于签名。但在许多使用场景中,这些可能不是正确的密钥。例如,若需要提供电表读数的不可否认性 (non-repudiation),则消息应使用存储在校准计量芯片中的证书进行签名。

为验证数字签名,需要知道使用了哪个密钥签名。JSON Web 签名支持多种在签名消息中存储密钥标识符的方式。由于可用于签名的证书未作规定,将使用哈希值来标识它们。在 OCPP 中,充电桩和 CSMS SHOULD(建议)在 JWS 受保护头部中包含 x5t#S256 字段用于标识证书。按照 JWS 标准 RFC7515,此字段的值 SHOULD(建议)设为签名证书 DER 编码的 SHA-256 哈希。利益相关方如何根据哈希值查找证书不在本文档范围内。

注: 在本地控制器设置中(见第6章),签名发送给 CSMS 消息的 TLS 客户端密钥实际上并非 TLS 连接所使用的 TLS 客户端密钥,因为本地控制器中的密钥与充电桩的密钥不同。类似地,签名消息的 TLS 服务端密钥是 CSMS 的,而非本地控制器的。因此,协议实现 MUST NOT(禁止)将此不匹配视为签名无效;事实上,这是提供端到端真实性的预期和所需的特性。

8.1 --- RetryBackOffRepeatTimes

原文页码 (Original Page): 23


原文 (Original)

Required yes
Component componentName: OCPPCommCtrlr
Variable variableName: RetryBackOffRepeatTimes
mutability: ReadWrite
dataType: integer
Description When the Charging Station is reconnecting, after a connection loss, it will use this variable for the amount of times it will double the previous back-off time. When the maximum number of increments is reached, the Charging Station keeps connecting with the same back-off time.

译文 (Translation)

属性
是否必须 是 (yes)
所属组件 componentName: OCPPCommCtrlr(OCPP 通信控制器)
变量定义 variableName: RetryBackOffRepeatTimes
mutability(可修改性): ReadWrite(可读写)
dataType(数据类型): integer(整数)
说明 充电桩在连接丢失后重连时,使用此变量控制退避时间翻倍的次数。当达到最大递增次数后,充电桩将以相同的退避时间继续连接。

译者注: 例如设为 5,则退避时间将翻倍 5 次(第1次: base、第2次: 2×base、...、第6次及以后: 32×base,不再增加)。

8.2 --- RetryBackOffRandomRange

原文页码 (Original Page): 23


原文 (Original)

Required yes
Component componentName: OCPPCommCtrlr
Variable variableName: RetryBackOffRandomRange
mutability: ReadWrite
unit: s (seconds)
dataType: integer
Description When the Charging Station is reconnecting, after a connection loss, it will use this variable as the maximum value for the random part of the back-off time. It will add a new random value to every increasing back-off time, including the first connection attempt (with this maximum), for the amount of times it will double the previous back-off time. When the maximum number of increments is reached, the Charging Station will keep connecting with the same back-off time.

译文 (Translation)

属性
是否必须 是 (yes)
所属组件 componentName: OCPPCommCtrlr
变量定义 variableName: RetryBackOffRandomRange
mutability: ReadWrite
unit(单位): s(秒)
dataType: integer
说明 充电桩重连时,使用此变量作为退避时间中随机部分的最大值。每次递增的退避时间(包括首次连接尝试)都会附加一个新的随机值(以此变量为上限),重复次数由 RetryBackOffRepeatTimes 决定。达到最大递增次数后,充电桩将以相同的退避时间继续连接。

重连时间计算公式:

python 复制代码
第n次重连等待时间 = min(RetryBackOffWaitMinimum × 2^n, 最终上限) + random(0, RetryBackOffRandomRange)
其中 n = 0, 1, 2, ..., RetryBackOffRepeatTimes

8.3 --- RetryBackOffWaitMinimum

原文页码 (Original Page): 23


原文 (Original)

Required yes
Component componentName: OCPPCommCtrlr
Variable variableName: RetryBackOffWaitMinimum
mutability: ReadWrite
unit: s (seconds)
dataType: integer
Description When the Charging Station is reconnecting, after a connection loss, it will use this variable as the minimum back-off time, the first time it tries to reconnect.

译文 (Translation)

属性
是否必须 是 (yes)
所属组件 componentName: OCPPCommCtrlr
变量定义 variableName: RetryBackOffWaitMinimum
mutability: ReadWrite
unit(单位): s(秒)
dataType: integer
说明 充电桩连接丢失后重连时,使用此变量作为首次重连尝试的最小退避时间。

译者注: 这是重连算法的基础等待时间(单位秒),后续每次失败后在此基础上翻倍并附加随机抖动。

8.4 --- WebSocketPingInterval

原文页码 (Original Page): 23


原文 (Original)

Required yes
Component componentName: OCPPCommCtrlr
Variable variableName: WebSocketPingInterval
mutability: ReadWrite
unit: s (seconds)
dataType: integer
Description A value of 0 disables client side websocket Ping / Pong. In this case there is either no ping / pong or the server initiates the ping and client responds with Pong. Positive values are interpreted as number of seconds between pings. Negative values are not allowed, SetConfiguration is then expected to return a Rejected result. It is recommended to configure WebSocketPingInterval smaller than: MessageAttemptsTransactionEvent × MessageAttemptIntervalTransactionEvent. This will limit the chance of the resend mechanism for transaction-related messages being triggered by connectivity issues.

译文 (Translation)

属性
是否必须 是 (yes)
所属组件 componentName: OCPPCommCtrlr
变量定义 variableName: WebSocketPingInterval
mutability: ReadWrite
unit(单位): s(秒)
dataType: integer
说明 值为 0 时禁用客户端侧 WebSocket Ping/Pong。此时要么无 ping/pong,要么由服务端发起 ping 而客户端以 Pong 响应。正数值 表示两次 ping 之间的秒数间隔。负数值 不允许,SetConfiguration 将返回 Rejected 结果。建议将 WebSocketPingInterval 配置为小于 MessageAttemptsTransactionEvent × MessageAttemptIntervalTransactionEvent,以降低交易相关消息的重发机制因网络连通性问题而被触发的概率。

配置建议:

python 复制代码
WebSocketPingInterval < MessageAttemptsTransactionEvent × MessageAttemptIntervalTransactionEvent

9 --- CustomData 扩展 (CustomData Extension)

原文页码 (Original Page): 25


原文 (Original)

In the JSON schema files all classes have the attribute additionalProperties set to false, such that a JSON parser will not accept any other properties in the message. In order to allow for some flexibility to create non-standard extensions for experimentation purposes, every JSON class has been extended with a "customData" property. This property is of type "CustomDataType", which has only one required property: "vendorId", which is used to identify the kind of customization. However, since it does not have additionalProperties set to false it can be freely extended with new properties.

In the same way as is defined for the DataTransfer message, the "vendorId" SHOULD be a value from the reversed DNS namespace, where the top tiers of the name, when reversed, corresponds to the publicly registered primary DNS name of the Vendor organization.
译文 (Translation)

在 JSON Schema 文件中,所有类的 additionalProperties 均设为 false,因此 JSON 解析器不会接受消息中的任何其他属性。为允许一定的灵活性,支持创建非标准扩展用于实验目的,每个 JSON 类都被扩展了一个 "customData" 属性。该属性的类型为 "CustomDataType",只有一个必填属性 "vendorId",用于标识自定义的类型。然而,由于 CustomDataType 本身未将 additionalProperties 设为 false,因此可以被自由扩展添加新属性。

与 DataTransfer 消息的定义方式相同,"vendorId" SHOULD(建议)使用反向 DNS 命名空间的值,即倒序后顶层域名对应于供应商组织的公开注册主 DNS 名称。


CustomDataType 定义与示例

原文 (Original)

The following example shows the "CustomDataType" definition and the (optional) "customData" property in the schema definition of HeartbeatRequest:
译文 (Translation)

以下示例展示了 HeartbeatRequest 的 Schema 定义中 "CustomDataType" 的定义以及(可选的)"customData" 属性:
原文 (Original)

python 复制代码
{
    "$schema": "http://json-schema.org/draft-06/schema#",
    "$id": "HeartbeatRequest",
    "definitions": {
    "CustomDataType": {
    "description": "This class does not get 'AdditionalProperties = false' in the schema
    generation, so it can be extended with arbitrary JSON properties to allow adding
    custom data.",
    "javaType": "CustomData",
    "type": "object",
    "properties": {
    "vendorId": {
    "type": "string",
    "maxLength": 255
    }
    },
    "required": [ "vendorId" ]
    }
},
"type": "object",
"additionalProperties": false,
"properties": {
    "customData": {
    "$ref": "#/definitions/CustomDataType"
    }
}
}

带注释版本 (Annotated)

python 复制代码
{
    "$schema": "http://json-schema.org/draft-06/schema#",  // JSON Schema 版本
    "$id": "HeartbeatRequest",                              // Schema 标识:心跳请求
    "definitions": {
        "CustomDataType": {                                 // 自定义数据类型定义
            "description": "此类在 schema 生成时不会设置 AdditionalProperties=false,因此可以扩展任意 JSON 属性以添加自定义数据。",
            "javaType": "CustomData",
            "type": "object",
            "properties": {
                "vendorId": {                               // 供应商 ID(必填)
                    "type": "string",
                    "maxLength": 255                        // 最大长度 255 字符
                }
            },
            "required": ["vendorId"]                        // vendorId 为必填
        }
    },
    "type": "object",
    "additionalProperties": false,                          // HeartbeatRequest 本身不允许额外属性
    "properties": {
        "customData": {                                     // 可选的自定义数据字段
            "$ref": "#/definitions/CustomDataType"          // 引用 CustomDataType
        }
    }
}

原文 (Original)

Whereas the standard HeartbeatRequest has an empty body, a customized version, that provides the value of the main meter and a count of all sessions to date, could look like this:
译文 (Translation)

标准 HeartbeatRequest 的 body 为空,而以下示例展示了一个自定义版本,提供了主电表读数和截至当前的会话计数:
带注释版本 (Annotated)

python 复制代码
{
    "customData": {
        "vendorId": "com.mycompany.customheartbeat",  // 供应商标识(反向 DNS 格式)
        "mainMeterValue": 12345,                       // 自定义字段:主电表读数
        "sessionsToDate": 342                          // 自定义字段:累计会话数
    }
}

原文 (Original)

A CSMS that has implemented this extension, identified by its "vendorId", will be able to process the data. Other CSMS implementations will simply ignore these custom properties.

A CSMS can request a report of the CustomizationCtrlr component to get a list of all customizations that are supported by the Charging Station.
译文 (Translation)

已实现此扩展(由其 "vendorId" 标识)的 CSMS 将能处理这些数据。其他 CSMS 实现将简单地忽略这些自定义属性。

CSMS 可以通过请求 CustomizationCtrlr 组件的报告来获取充电桩支持的所有自定义扩展列表。

相关推荐
H__Rick2 小时前
C51单片机学习-DAY3
单片机·学习·mongodb
yoothey3 小时前
异常学习笔记:为什么自定义异常后还要 throw?
笔记·学习
WangN24 小时前
【通识】宇树G1_29DOF速度跟踪训练—逐章学习手册
人工智能·python·学习·机器人·具身智能
lazy H5 小时前
Spring Boot 项目如何连接 Redis?新手入门配置和常见错误总结
ide·spring boot·redis·后端·学习·intellij-idea
雾沉川5 小时前
Flutter 入门开发环境完整搭建教程
学习·flutter
星夜夏空995 小时前
STM32单片机学习(37) —— PWR和BKP
stm32·单片机·学习
万岳科技5 小时前
教育培训系统开发流程详解:平台建设关键环节解析
数据库·后端·学习
fanged5 小时前
高通学习14--RB5(TODO)
学习