OCPP

OCPP 驱动参考:内置的中央系统、充电桩证书、充电接口点位,以及 Meter、Status、Transaction 的命名规则。

以 Markdown 查看

OCPP 驱动让 Ganter Lab 成为那些说 Open Charge Point Protocol 的电动汽车充电桩的中央系统。和 LoRaWAN 一样,方向是反过来的:不是工作站去连充电桩,而是充电桩通过一条 WebSocket 连到 Ganter Lab、自行注册,并把它的遥测数据推送进来。

内置的中央系统在第一台已启用的 OCPP 设备被载入正在跑的连接器时启动,在最后一台被移除或禁用时停下:一台充电桩都没配置时,端口上根本没有人在听。加一台就把它带回来,不必重启应用。你配置的每一台设备代表一台充电桩,由它连接时所用的那个 id 来标识。

准备充电桩

是充电桩主动连接本工作站,而它出厂时并不知道工作站在哪里。每台 OCPP 设备的页面上都有一张“准备充电桩”卡片,按本工作站写好要填入充电桩 OCPP 设置的内容:

要在充电桩上设置的 卡片给出的
中央系统地址 wss://<地址>:19521/<充电桩 id>,先是以本工作站名称写成的地址(内置 OPC UA 服务器配置中的“网络名称”,为空时是计算机名),然后是本工作站在网络上的每个 IPv4 地址各一条,每条带一个复制按钮。会核对服务器名称的充电桩需要按名称写的地址;不核对的充电桩接受其中任何一个,这时用与它同一网络的那个地址即可。
充电桩标识 设备的“标识符”;它为空时,就是工作站给设备起的名字。很多充电桩会自己把标识加到地址后面:对这类充电桩,地址只填到端口为止,标识填在它自己的栏位里。
安全 使用客户端证书的 TLS,即 OCPP 所说的安全配置文件 3。没有客户端证书的配置文件 1 和 2 不被接受。充电桩的证书在通用名称或 DNS 备用名称中写有它的标识(见证书)。
中央系统的根证书 本工作站根证书 GanterLabConnector-<station>.pem 的完整路径,充电桩安装它来信任中央系统;“打开文件夹”按钮会在 Windows 文件资源管理器中显示这个文件。按钮在工作站自己的窗口里起作用;在其他设备的浏览器里它保持灰色并说明原因。

工作站打开以来充电桩还没有连上过时,卡片是展开的;连上之后它会收起,但留在页面上,供下次设置充电桩时使用。证书没有任何 Windows 中已安装的根证书担保的充电桩,首次连接时会被拒绝,并在现场设备访问中等待。

文件在登记了充电桩、中央系统第一次启动时写出,在此之前卡片会说明这一点。本工作站所有充电桩用的是同一个文件,工作站的名称或地址变了,它也不变(见工作站的根证书)。

凡是拿到过旧版本文件的充电桩,都需要装上这个文件一次。 这个版本第一次启动时,如果工作站上的充电桩可能持有旧文件,卡片上会出现一条通知“本工作站的文件已更换”,只在那一次运行中显示。通知只是提醒,不是操作说明本身:对原来就有充电桩的工作站,不论卡片是否显示这条通知,都要在每台充电桩上把新文件装一次。会核对服务器名称的充电桩从此就会接受本工作站。

中央系统与端口

项目 取值
协议版本 OCPP 1.6、2.0、2.0.1 和 2.1(WebSocket 子协议协商;JSON)
监听 所有接口,TCP 端口 19521,只走 TLS
充电桩地址 wss://<station-address>:19521/<charge-point-id>
身份 WebSocket 路径的最后一段就是充电桩的 id

监听永远是 TLS;除了本地回环地址(它的存在只是为了进程内测试)之外,任何地方都拒绝明文 HTTP 的监听。充电桩用它的 BootNotification 自行注册(应答里给的心跳间隔是 60 秒),而每一张出示来做授权的卡都被接受:Ganter Lab 观察充电过程,它不管资费,也不管门禁。

证书

TLS 握手的服务器一侧出示的是由本工作站自己的根证书签发的证书(见工作站的根证书)。每一台充电桩都必须出示一份客户端证书:

  1. 它必须以两种方式之一得到信任:在这台 Windows 机器上构成一条直到其中已安装的根证书的有效证书链,没有任何 TLS 策略错误;或者你已经在这个驱动页面的现场设备访问中批准了这份证书本身(见下文)。无论哪种方式,它都必须在有效期之内。检查不通过就拒绝连接,诊断日志记下是哪一项检查没过。
  2. 它的主体通用名或者一个 DNS 主体备用名必须点名它连接时所用的那个充电桩 id。一份点名了另一台充电桩、而不是连接路径里那一台的证书会被拒绝,即使它已获批准。

没有客户端证书的握手,在任何数据流动之前就被丢掉。

工作站的根证书

项目 内容
根证书 每个配置档案只创建一次,在中央系统或 LoRaWAN 网络服务器第一次启动时。它的主题是 Ganter Lab Connector (<station>),其中 <station> 是当天的网络名称。它永不过期,也不会自行更换:改计算机名、改网络名称或改工作站的地址,它都不变。
文件 GanterLabConnector-<station>.pem,只含公开部分,位于数据文件夹的 ua\pki\station 下。充电桩和 LoRaWAN 网关用的是同一个文件,网关把它安装为 tc.trust。
中央系统出示的证书 由这个根证书签发。它写明工作站的网络名称(内置 OPC UA 服务器配置中的“网络名称”,为空时是计算机名)、localhost 和工作站的每个 IPv4 地址,所以会核对服务器名称的充电桩(安全配置文件 3)也接受它。网络名称或某个地址变了,只有这份证书会自动重新签发,充电桩上无需做任何事。
OPC UA 服务器的证书 此处不使用。内置的 OPC UA 服务器保留它自己的证书,供 OPC UA 客户端使用。

已保存的根证书如果再也打不开(例如是另一个 Windows 账户写入的),会被一个新的根证书取代。这时每台充电桩都需要新文件一次,卡片会说明这一点。

现场设备访问

如果一台充电桩的证书是自签名的,或者由制造商自己的证书颁发机构签发,因而没有任何 Windows 中已安装的根证书为它担保,也不需要把那个根证书安装到这台电脑上。它的第一次连接会被拒绝,它的证书在 OCPP 驱动页面末尾的现场设备访问里等待你的决定,这和 OPC UA 驱动页面上的是同一张卡片。

分组 列出的内容 你能做的事
等待批准 每一份被拒绝的证书,列在它连接时所用的充电桩 id 之下:它的 SHA-256 指纹、最近一次尝试的时间和尝试次数,以及它的详细信息(主体、颁发者、域名、有效期)。 批准从充电桩的下一次连接起接纳这份证书。阻止继续拒绝它,且不再把它列到这里。丢弃所有待处理的证书清空列表,不批准也不阻止任何东西。
已批准的现场设备 你批准过或导入过的证书,并注明是谁、在什么时候做的决定。 撤回批准关闭充电桩的连接,并把它的证书放回列表。导入证书在充电桩第一次连接之前就批准它的公开证书。
被阻止的现场设备 你阻止过的证书。被阻止的证书即使 Windows 信任它也会被拒绝。 解除阻止把证书放回列表,但不批准它。

批准的是一份一份的证书,从来不是签发它的证书颁发机构,所以同一制造商的第二台充电桩要等待它自己的批准。批准之前,先把指纹和充电桩报出的指纹核对一遍。批准不会改变这份证书是哪一台充电桩:它仍然只以它点名的那个充电桩 id 被接纳,而且只在有效期之内。Windows 已经信任的证书照旧被接纳,从不出现在这里;没有任何东西会被自动批准。

这些决定保存在工作站数据文件夹的 ua\pki\ocpp 下,重启后依然保留。只有可以配置 Connector 的角色才会看到这些命令,而且它们只在工作站自己的窗口里起作用:另一台设备上的浏览器只能读这张卡片,不能修改;也没有任何代理工具能批准证书。列表最多保存 100 份等待批准的证书;列表满了以后,新的证书仍会被拒绝,但不会列出,卡片会说明这一点。

连接字段

字段 它是什么 格式 默认值
标识符 这台设备所代表的那个充电桩 id:也就是充电桩放在它连接 URL 末尾、并写在它证书里的那个 id。 自由文本,比较时忽略大小写。留空就退回到规整过的设备名称。 空

没有主机、端口或凭据字段:连接的细节握在充电桩手里,本工作站只需要知道这台设备指的是哪一台充电桩。也没有轮询间隔字段。OCPP 是推送式的,充电桩按它自己的节奏发 MeterValues 和 StatusNotification 消息,而点位以固定的一秒周期从最近收到的数据里刷新。

因此,一台还没连上来的充电桩,或者一个还没报告过的充电接口,是没有读数,而不是读数失败。它的点位保持空,也不会有失败写进诊断日志:充电桩还没发来的点位,正是协议在正常工作。

充电桩的健康状态

充电桩有了点位才发过来,所以光凭点位没有动静,分不清一台还没报告的充电桩和一台已经不在的充电桩。它的健康状态,也就是它在树里那一行上的圆点和驱动页面上的健康状态一列,改为跟随它与本工作站中央系统的连接:

情况 健康状态
已配置,自工作站打开以来一直没连上来 离线
已连上,还什么都没发 在线
已连上,正在发送 在线;它发来的点位有一部分读不出来时为部分在线
掉线了:停了、断电了或者和网络断开了 离线,直到它重新连上
中央系统没在监听 离线
已禁用 已禁用

充电桩连着的时候,一个还没报告过的充电接口不会拉低它的状态。充电桩的改动保存之后,或者 Connector 启动之后,最多十秒内它读作尚未可知:重新放入设备可能会让中央系统重启,充电桩要过一会儿才能重新连上。

点位

OCPP 的点位读的是这台充电桩的一个充电接口(OCPP 意义上的 connector:一个插枪位,从 1 开始编号)。在 OCPP 2.0、2.0.1 和 2.1 里,这个编号是 EVSE 的编号,因为那里的充电接口在每个 EVSE 内部又从 1 开始编号。存在三种点位,靠点位名称的后缀区分开,而这个后缀是要校验的:

点位名以什么结尾 读的是什么 合适的类型
Meter 这个充电接口最近报告的那次采样电表值(OCPP MeterValues;在 OCPP 2.x 里,还包括充电过程中 TransactionEvent 带来的值)。单位是充电桩发来的那个,通常是 Wh。服务器保留最近 10 次采样,点位读的是最新那一次。从没报告过的充电接口读作一个空值,不是零。 Float 或 Double
Status 最近一次 StatusNotification 里那个充电接口的状态文字(例如 Available、Faulted)。在 OCPP 2.x 里,有多个充电接口的 EVSE 读作其中最能说明情况的那个:Faulted 优先于 Occupied,Occupied 优先于 Reserved,Reserved 优先于 Available,Available 优先于 Unavailable。还没报告过状态的 EVSE 读作一个空值。 String
Transaction 这个充电接口上正在进行的事务的编号,也就是 RemoteStopTransaction 和 GetTransactionStatus 两个命令要的那个编号:OCPP 1.6 里是事务开始时本工作站给它的编号,OCPP 2.x 里是充电站自己的标识,照它发来的原样读出。事务开始时填上,结束时清空,所以没有事务在进行时读到的是空文本。充电桩连上之后、第一次有事务开始或结束之前,它读作空值,因为可能早已有事务在进行。 String

这几种后缀都没有的名字会被校验拒绝:请把点位命名成 Connector1Meter、Connector1Status 和 Connector1Transaction 这样。

字段 作用 取值 默认值
Connector 这个点位读的编号:OCPP 1.6 里是充电接口的编号,OCPP 2.x 里是 EVSE 的编号。 1 或更大 1
数据类型 这个值对外暴露成的那个 OPC UA 类型。 Int32、Float、Double、String Float

OCPP 的点位是只读的:读写字段不提供可写的选项,一次写入会被拒绝而不是被假装成功。给充电桩下命令是它自己的一套命令,下面就讲,不是往点位里写一个值。

命令

充电桩不只是被读,也会被下命令。每一台 OCPP 设备都在本工作站自己的 OPC UA 地址空间里,把下面这些命令作为设备上的方法带着,所以连接到本工作站的 OPC UA 客户端调用该方法,就能下发一条命令。

每一条命令都以充电桩 id 作为它的第一个参数,并把充电桩自己的回答作为结果交回来。

命令 其余参数 作用
ChangeAvailability ConnectorId、Type(Operative 或 Inoperative) 把一个充电接口投入或退出运行。
ChangeConfiguration Key、Value 在充电桩上设一个配置键(OCPP 1.6)。
ClearCache 无 清掉充电桩的授权缓存。
GetConfiguration Key 读一个配置键(OCPP 1.6)。
RemoteStartTransaction IdTag 为一个令牌启动一次充电会话。
RemoteStopTransaction TransactionId 停掉一次正在进行的会话。
GetTransactionStatus TransactionId 问一次事务是不是还在进行(OCPP 2.x)。
Reset 无 重启充电桩。
UnlockConnector ConnectorId 从一个充电接口上把电缆解锁放开。
SetChargingProfile ConnectorId、Limit、NumberOfPhases 设一个充电功率上限,单位是瓦。ConnectorId 为 0 时限制整台充电桩;其他编号为该充电接口所充的会话设默认上限。
SetVariables ComponentName、VariableName、AttributeType、AttributeValue 设某个组件的某一个变量(OCPP 2.x)。
GetVariables ComponentName、VariableName、AttributeType 读某个组件的某一个变量(OCPP 2.x)。

AttributeType 是 Actual、Target、MinSet、MaxSet 之一。

TransactionId 从哪里来。 RemoteStopTransaction 和 GetTransactionStatus 作用于一次事务,用它的编号来指明。这个编号从会话所在充电接口(OCPP 2.x 里是 EVSE)的 Transaction 点位读出来,比如 Connector1Transaction,读到什么就原样传什么。那里是空文本,就说明这个充电接口上没有事务在进行。

一份命令表,适用于每个版本。 不论充电桩讲的是哪个版本,这些命令都沿用上面的名字,工作站按该版本规定的格式发送每一条。在 OCPP 2.0、2.0.1 和 2.1 里,充电接口归在 EVSE 之下,作为 ConnectorId 传入的编号指的是 EVSE。

命令 在 OCPP 1.6 上 在 OCPP 2.0、2.0.1 和 2.1 上
ChangeAvailability 作用于给定的充电接口;0 表示整台充电桩。 作用于给定的 EVSE;0 表示整个充电站。
RemoteStartTransaction 发送 IdTag,由充电桩选择充电接口。 以 RequestStartTransaction 发出,IdTag 作为中心令牌,并带上自己的启动编号;由充电站选择 EVSE。
RemoteStopTransaction TransactionId 是会话开始时本工作站给这次会话的编号。 以 RequestStopTransaction 发出;TransactionId 是充电站自己的标识,原样以文本传过去。
Reset 硬重启。 立即重启。
UnlockConnector 解锁给定的充电接口。 解锁给定 EVSE 的第一个充电接口。
SetChargingProfile 0 设充电桩的最大配置文件;其他充电接口得到一个默认配置文件。 0 设充电站的最大配置文件;其他 EVSE 得到一个默认配置文件。
ChangeConfiguration、GetConfiguration 同上表。 不属于这些版本:充电站会回一个错误,命令失败。
GetTransactionStatus、SetVariables、GetVariables 不属于这个版本:充电桩会回一个错误,命令失败。 同上表。

等那个回答。 工作站把命令发出去,这条命令并不算完。工作站会把这次调用一直挂着,直到充电桩回答的正是它发出去的那一条为止,靠随命令一起走的关联标识符来配对,然后才报告成功并把答复交回来。有三件事会以别的方式把它结束掉,每一种都作为失败连同原因一起报告:

  • 充电桩没有连上,或者它的连接没有打开。什么都不会被发出去。
  • 充电桩答的是一个 OCPP 错误,例如一条它没有实现的命令。错误码就是原因。
  • 过了 30 秒还完全没有回答。没有人回答的命令是一次失败,绝不是成功。

为一台某个设备并不代表的、或者没有连上的充电桩下发的命令,会当场被拒绝,而不是排队等着。

设备发现

OCPP 驱动上的发现设备列出此刻连在中央系统上的那些充电桩,而读取一个候选项的通道,会为这台充电桩到目前为止报告过的每一个充电接口各提议一个 Meter 点位、一个 Status 点位和一个 Transaction 点位。

第一台充电桩永远是手工配置的。 中央系统没在听之前什么都连不上,而它只有在这里存在一台 OCPP 设备之后才听,所以在一台充电桩都没配置的安装环境里做一次扫描,什么都找不到,而且它会这么说出来。请用充电桩将要连接时所用的那个 id 把第一台设备添加进来;此后,一台已经连上过的充电桩就能被发现了。一般的设备发现流程见 Connector 页面。

这个 id 在证书里、在连接路径里、以及和设备比对时都忽略大小写:一台以 cp-a 连上来、证书里写着 CP-A 的充电桩,和一台标识符写作 CP-A 的设备是同一台充电桩。

已经有的充电桩可以在自己的页面上重新读它的充电接口:“点位”卡片标题栏里的发现点位列出它连接期间报告过的每个充电接口的 Meter、Status 和 Transaction 点位中设备还没有的那些(见发现一台设备的点位)。