Add extracted tools: CitrineOS, OpenOCPP, ShapeShifter

- CitrineOS core extracted (CSMS OCPP 2.0.1)
- OpenOCPP extracted (firmware OCPP 1.6J/2.0.1)
- ShapeShifter library installed (pip install -e)
- ShapeShifter specification extracted
- EVerest extracted

TODO updated with progress
This commit is contained in:
Eric F
2026-06-08 00:38:27 -04:00
parent 468cfeaa50
commit d398a6ced2
7326 changed files with 1177561 additions and 7 deletions

View File

@@ -0,0 +1,31 @@
#
# AUTO GENERATED - MARKED REGIONS WILL BE KEPT
# template version 3
#
# module setup:
# - ${MODULE_NAME}: module name
ev_setup_cpp_module()
# ev@bcc62523-e22b-41d7-ba2f-825b493a3c97:v1
target_sources(${MODULE_NAME}
PRIVATE
main/fsm_controller.cpp
)
target_link_libraries(${MODULE_NAME}
PRIVATE
slac::io
slac::fsm::evse
)
# ev@bcc62523-e22b-41d7-ba2f-825b493a3c97:v1
target_sources(${MODULE_NAME}
PRIVATE
"main/slacImpl.cpp"
)
# ev@c55432ab-152c-45a9-9d2e-7281d50c69c3:v1
# insert other things like install cmds etc here
# ev@c55432ab-152c-45a9-9d2e-7281d50c69c3:v1

View File

@@ -0,0 +1,15 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2022 - 2022 Pionix GmbH and Contributors to EVerest
#include "EvseSlac.hpp"
namespace module {
void EvseSlac::init() {
invoke_init(*p_main);
}
void EvseSlac::ready() {
invoke_ready(*p_main);
}
} // namespace module

View File

@@ -0,0 +1,58 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright Pionix GmbH and Contributors to EVerest
#ifndef EVSE_SLAC_HPP
#define EVSE_SLAC_HPP
//
// AUTO GENERATED - MARKED REGIONS WILL BE KEPT
// template version 2
//
#include "ld-ev.hpp"
// headers for provided interface implementations
#include <generated/interfaces/slac/Implementation.hpp>
// ev@4bf81b14-a215-475c-a1d3-0a484ae48918:v1
// insert your custom include headers here
// ev@4bf81b14-a215-475c-a1d3-0a484ae48918:v1
namespace module {
struct Conf {};
class EvseSlac : public Everest::ModuleBase {
public:
EvseSlac() = delete;
EvseSlac(const ModuleInfo& info, std::unique_ptr<slacImplBase> p_main, Conf& config) :
ModuleBase(info), p_main(std::move(p_main)), config(config){};
const std::unique_ptr<slacImplBase> p_main;
const Conf& config;
// ev@1fce4c5e-0ab8-41bb-90f7-14277703d2ac:v1
// insert your public definitions here
// ev@1fce4c5e-0ab8-41bb-90f7-14277703d2ac:v1
protected:
// ev@4714b2ab-a24f-4b95-ab81-36439e1478de:v1
// insert your protected definitions here
// ev@4714b2ab-a24f-4b95-ab81-36439e1478de:v1
private:
friend class LdEverest;
void init();
void ready();
// ev@211cfdbe-f69a-4cd6-a4ec-f8aaa3d1b6c8:v1
// insert your private definitions here
// ev@211cfdbe-f69a-4cd6-a4ec-f8aaa3d1b6c8:v1
};
// ev@087e516b-124c-48df-94fb-109508c7cda9:v1
// insert other definitions here
// ev@087e516b-124c-48df-94fb-109508c7cda9:v1
} // namespace module
#endif // EVSE_SLAC_HPP

View File

@@ -0,0 +1,239 @@
<mxfile host="65bd71144e">
<diagram id="enTwcsW3kJKdFbbsoOIB" name="Page-1">
<mxGraphModel dx="1340" dy="861" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="850" pageHeight="1100" math="0" shadow="0">
<root>
<mxCell id="0"/>
<mxCell id="1" parent="0"/>
<mxCell id="26" style="edgeStyle=none;html=1;exitX=0.099;exitY=0.004;exitDx=0;exitDy=0;entryX=0.607;entryY=0.013;entryDx=0;entryDy=0;entryPerimeter=0;exitPerimeter=0;" edge="1" parent="1" source="21" target="7">
<mxGeometry relative="1" as="geometry">
<mxPoint x="290" y="280" as="targetPoint"/>
<Array as="points">
<mxPoint x="590" y="280"/>
<mxPoint x="193" y="280"/>
</Array>
</mxGeometry>
</mxCell>
<mxCell id="27" value="Reset" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" vertex="1" connectable="0" parent="26">
<mxGeometry x="-0.1157" y="-3" relative="1" as="geometry">
<mxPoint y="-7" as="offset"/>
</mxGeometry>
</mxCell>
<mxCell id="28" style="edgeStyle=none;html=1;entryX=0.669;entryY=-0.029;entryDx=0;entryDy=0;entryPerimeter=0;exitX=0.007;exitY=0.089;exitDx=0;exitDy=0;exitPerimeter=0;" edge="1" parent="1" source="21" target="11">
<mxGeometry relative="1" as="geometry">
<mxPoint x="450" y="319.9999999999999" as="targetPoint"/>
<Array as="points">
<mxPoint x="490" y="320"/>
<mxPoint x="400" y="320"/>
</Array>
</mxGeometry>
</mxCell>
<mxCell id="29" value="LeaveBCD" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" vertex="1" connectable="0" parent="28">
<mxGeometry x="-0.2156" y="-2" relative="1" as="geometry">
<mxPoint x="-13" y="-8" as="offset"/>
</mxGeometry>
</mxCell>
<mxCell id="50" style="edgeStyle=none;html=1;entryX=0;entryY=0.5;entryDx=0;entryDy=0;" edge="1" parent="1" source="21" target="49">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="52" style="edgeStyle=none;html=1;" edge="1" parent="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="800" y="339" as="targetPoint"/>
<mxPoint x="920" y="339" as="sourcePoint"/>
<Array as="points">
<mxPoint x="920" y="280"/>
<mxPoint x="800" y="280"/>
</Array>
</mxGeometry>
</mxCell>
<mxCell id="53" value="RetryMatching" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" vertex="1" connectable="0" parent="52">
<mxGeometry x="-0.1239" relative="1" as="geometry">
<mxPoint x="-19" y="-10" as="offset"/>
</mxGeometry>
</mxCell>
<mxCell id="21" value="Matching" style="swimlane;fontStyle=1;align=center;verticalAlign=middle;childLayout=stackLayout;horizontal=1;startSize=30;horizontalStack=0;resizeParent=0;resizeLast=1;container=0;fontColor=#000000;collapsible=0;rounded=1;arcSize=30;strokeColor=#ff0000;fillColor=#ffffc0;swimlaneFillColor=#ffffc0;dropTarget=0;" vertex="1" parent="1">
<mxGeometry x="520" y="340" width="600" height="680" as="geometry"/>
</mxCell>
<mxCell id="12" style="edgeStyle=none;html=1;exitX=0.5;exitY=0;exitDx=0;exitDy=0;entryX=0.25;entryY=0;entryDx=0;entryDy=0;" edge="1" parent="1" source="7" target="7">
<mxGeometry relative="1" as="geometry">
<mxPoint x="210.33333333333348" y="279.9999999999999" as="targetPoint"/>
<Array as="points">
<mxPoint x="180" y="320"/>
<mxPoint x="150" y="320"/>
</Array>
</mxGeometry>
</mxCell>
<mxCell id="13" value="Reset" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" vertex="1" connectable="0" parent="12">
<mxGeometry x="-0.1148" y="-1" relative="1" as="geometry">
<mxPoint x="-10" y="-9" as="offset"/>
</mxGeometry>
</mxCell>
<mxCell id="14" value="" style="edgeStyle=none;html=1;" edge="1" parent="1" source="7" target="11">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="15" value="ResetDone" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" vertex="1" connectable="0" parent="14">
<mxGeometry x="-0.3139" y="1" relative="1" as="geometry">
<mxPoint x="13" y="-9" as="offset"/>
</mxGeometry>
</mxCell>
<mxCell id="7" value="Reset" style="rounded=1;whiteSpace=wrap;html=1;arcSize=40;fontColor=#000000;fillColor=#ffffc0;strokeColor=#ff0000;" vertex="1" parent="1">
<mxGeometry x="120" y="360" width="120" height="40" as="geometry"/>
</mxCell>
<mxCell id="9" value="" style="ellipse;html=1;shape=startState;fillColor=#000000;strokeColor=#ff0000;" vertex="1" parent="1">
<mxGeometry x="70" y="365" width="30" height="30" as="geometry"/>
</mxCell>
<mxCell id="10" value="" style="edgeStyle=orthogonalEdgeStyle;html=1;verticalAlign=bottom;endArrow=open;endSize=8;strokeColor=#ff0000;entryX=0;entryY=0.5;entryDx=0;entryDy=0;exitX=0.867;exitY=0.5;exitDx=0;exitDy=0;exitPerimeter=0;" edge="1" source="9" parent="1" target="7">
<mxGeometry relative="1" as="geometry">
<mxPoint x="210" y="330" as="targetPoint"/>
<mxPoint x="110" y="390" as="sourcePoint"/>
<Array as="points"/>
</mxGeometry>
</mxCell>
<mxCell id="16" style="edgeStyle=none;html=1;entryX=0.75;entryY=0;entryDx=0;entryDy=0;exitX=0.25;exitY=0;exitDx=0;exitDy=0;" edge="1" parent="1" source="11" target="7">
<mxGeometry relative="1" as="geometry">
<Array as="points">
<mxPoint x="350" y="320"/>
<mxPoint x="210" y="320"/>
</Array>
</mxGeometry>
</mxCell>
<mxCell id="17" value="Reset" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" vertex="1" connectable="0" parent="16">
<mxGeometry x="0.0857" y="-1" relative="1" as="geometry">
<mxPoint x="9" y="-9" as="offset"/>
</mxGeometry>
</mxCell>
<mxCell id="19" value="" style="edgeStyle=none;html=1;entryX=0;entryY=0.25;entryDx=0;entryDy=0;" edge="1" parent="1" source="11" target="21">
<mxGeometry relative="1" as="geometry">
<mxPoint x="520" y="380" as="targetPoint"/>
</mxGeometry>
</mxCell>
<mxCell id="20" value="EnterBCD" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" vertex="1" connectable="0" parent="19">
<mxGeometry x="-0.2733" y="-2" relative="1" as="geometry">
<mxPoint x="11" y="-12" as="offset"/>
</mxGeometry>
</mxCell>
<mxCell id="11" value="Idle" style="rounded=1;whiteSpace=wrap;html=1;arcSize=40;fontColor=#000000;fillColor=#ffffc0;strokeColor=#ff0000;" vertex="1" parent="1">
<mxGeometry x="320" y="360" width="120" height="40" as="geometry"/>
</mxCell>
<mxCell id="31" value="" style="edgeStyle=none;html=1;" edge="1" parent="1" source="18" target="30">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="32" value="CM_START_ATTEN_CHAR_IND" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" vertex="1" connectable="0" parent="31">
<mxGeometry x="-0.2364" y="-1" relative="1" as="geometry">
<mxPoint x="1" y="7" as="offset"/>
</mxGeometry>
</mxCell>
<mxCell id="18" value="WaitForStartAttenChar" style="rounded=1;whiteSpace=wrap;html=1;arcSize=40;fontColor=#000000;fillColor=#ffffc0;strokeColor=#ff0000;" vertex="1" parent="1">
<mxGeometry x="640" y="480" width="140" height="40" as="geometry"/>
</mxCell>
<mxCell id="25" style="edgeStyle=none;html=1;entryX=0.5;entryY=0;entryDx=0;entryDy=0;exitX=0.88;exitY=0.507;exitDx=0;exitDy=0;exitPerimeter=0;" edge="1" parent="1" source="24" target="18">
<mxGeometry relative="1" as="geometry">
<Array as="points">
<mxPoint x="710" y="455"/>
</Array>
</mxGeometry>
</mxCell>
<mxCell id="51" value="CM_SLAC_PARM_REQ" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" vertex="1" connectable="0" parent="25">
<mxGeometry x="-0.2586" y="2" relative="1" as="geometry">
<mxPoint x="11" y="2" as="offset"/>
</mxGeometry>
</mxCell>
<mxCell id="24" value="" style="ellipse;html=1;shape=startState;fillColor=#000000;strokeColor=#ff0000;" vertex="1" parent="1">
<mxGeometry x="540" y="440" width="30" height="30" as="geometry"/>
</mxCell>
<mxCell id="54" style="edgeStyle=none;html=1;entryX=1;entryY=0.5;entryDx=0;entryDy=0;exitX=0.824;exitY=-0.025;exitDx=0;exitDy=0;exitPerimeter=0;" edge="1" parent="1" source="30" target="30">
<mxGeometry relative="1" as="geometry">
<mxPoint x="560" y="560" as="targetPoint"/>
<Array as="points">
<mxPoint x="755" y="570"/>
<mxPoint x="820" y="570"/>
<mxPoint x="820" y="610"/>
</Array>
</mxGeometry>
</mxCell>
<mxCell id="55" value="CM_MNBC_SOUND_IND" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" vertex="1" connectable="0" parent="54">
<mxGeometry x="0.4156" y="-2" relative="1" as="geometry">
<mxPoint x="52" y="-12" as="offset"/>
</mxGeometry>
</mxCell>
<mxCell id="62" style="edgeStyle=none;html=1;exitX=0;exitY=0.5;exitDx=0;exitDy=0;" edge="1" parent="1" source="30">
<mxGeometry relative="1" as="geometry">
<mxPoint x="670" y="670" as="targetPoint"/>
<Array as="points">
<mxPoint x="610" y="610"/>
<mxPoint x="610" y="670"/>
</Array>
</mxGeometry>
</mxCell>
<mxCell id="63" value="CM_ATTEN_PROFILE_IND" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" vertex="1" connectable="0" parent="62">
<mxGeometry x="-0.0889" relative="1" as="geometry">
<mxPoint y="2" as="offset"/>
</mxGeometry>
</mxCell>
<mxCell id="30" value="Sounding" style="rounded=1;whiteSpace=wrap;html=1;arcSize=40;fontColor=#000000;fillColor=#ffffc0;strokeColor=#ff0000;" vertex="1" parent="1">
<mxGeometry x="640" y="590" width="140" height="40" as="geometry"/>
</mxCell>
<mxCell id="33" value="NoSlacPerformed" style="rounded=1;whiteSpace=wrap;html=1;arcSize=40;fontColor=#000000;fillColor=#ffffc0;strokeColor=#ff0000;" vertex="1" parent="1">
<mxGeometry x="120" y="560" width="120" height="40" as="geometry"/>
</mxCell>
<mxCell id="34" value="MatchingFailed" style="rounded=1;whiteSpace=wrap;html=1;arcSize=40;fontColor=#000000;fillColor=#ffffc0;strokeColor=#ff0000;" vertex="1" parent="1">
<mxGeometry x="280" y="560" width="120" height="40" as="geometry"/>
</mxCell>
<mxCell id="39" value="" style="edgeStyle=none;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" edge="1" parent="1" source="35" target="38">
<mxGeometry relative="1" as="geometry">
<Array as="points"/>
</mxGeometry>
</mxCell>
<mxCell id="35" value="WaitForAttenCharRsp" style="rounded=1;whiteSpace=wrap;html=1;arcSize=40;fontColor=#000000;fillColor=#ffffc0;strokeColor=#ff0000;" vertex="1" parent="1">
<mxGeometry x="640" y="740" width="140" height="40" as="geometry"/>
</mxCell>
<mxCell id="41" value="" style="edgeStyle=none;html=1;" edge="1" parent="1" source="38" target="40">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="42" value="AttenCharRspReceived" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" vertex="1" connectable="0" parent="41">
<mxGeometry x="-0.4695" y="5" relative="1" as="geometry">
<mxPoint x="-5" y="12" as="offset"/>
</mxGeometry>
</mxCell>
<mxCell id="38" value="WaitForSlacMatch" style="rounded=1;whiteSpace=wrap;html=1;arcSize=40;fontColor=#000000;fillColor=#ffffc0;strokeColor=#ff0000;" vertex="1" parent="1">
<mxGeometry x="640" y="830" width="140" height="40" as="geometry"/>
</mxCell>
<mxCell id="40" value="ReceivedSlacMatch" style="rounded=1;whiteSpace=wrap;html=1;arcSize=40;fontColor=#000000;fillColor=#ffffc0;strokeColor=#ff0000;" vertex="1" parent="1">
<mxGeometry x="640" y="940" width="140" height="40" as="geometry"/>
</mxCell>
<mxCell id="49" value="Matched" style="rounded=1;whiteSpace=wrap;html=1;arcSize=40;fontColor=#000000;fillColor=#ffffc0;strokeColor=#ff0000;" vertex="1" parent="1">
<mxGeometry x="1320" y="400" width="140" height="40" as="geometry"/>
</mxCell>
<mxCell id="64" style="edgeStyle=none;html=1;entryX=0.5;entryY=0;entryDx=0;entryDy=0;fontSize=7;" edge="1" parent="1" source="59" target="35">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="67" value="yes" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];fontSize=12;" vertex="1" connectable="0" parent="64">
<mxGeometry x="-0.2082" y="-4" relative="1" as="geometry">
<mxPoint x="4" as="offset"/>
</mxGeometry>
</mxCell>
<mxCell id="65" style="edgeStyle=none;html=1;fontSize=7;exitX=1;exitY=0.5;exitDx=0;exitDy=0;" edge="1" parent="1" source="59">
<mxGeometry relative="1" as="geometry">
<mxPoint x="770" y="630" as="targetPoint"/>
<Array as="points">
<mxPoint x="770" y="670"/>
</Array>
</mxGeometry>
</mxCell>
<mxCell id="66" value="&lt;span style=&quot;font-size: 12px;&quot;&gt;no&lt;/span&gt;" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];fontSize=7;" vertex="1" connectable="0" parent="65">
<mxGeometry x="0.0778" y="-1" relative="1" as="geometry">
<mxPoint y="-7" as="offset"/>
</mxGeometry>
</mxCell>
<mxCell id="59" value="&lt;font style=&quot;font-size: 7px;&quot;&gt;all sounds received&lt;/font&gt;" style="rhombus;whiteSpace=wrap;html=1;fillColor=#ffffc0;strokeColor=#ff0000;" vertex="1" parent="1">
<mxGeometry x="670" y="650" width="80" height="40" as="geometry"/>
</mxCell>
<mxCell id="69" value="&lt;span style=&quot;font-size: 10px;&quot;&gt;- wait for&amp;nbsp;&lt;/span&gt;TT_SLAC_EVSE_INIT milliseconds&lt;span style=&quot;font-size: 10px;&quot;&gt;, until CM_SLAC_PARM_REQ is received&lt;br&gt;- if it gets received, spawn a new matching session with the substate WaitForStartAttenChar&lt;/span&gt;" style="text;html=1;strokeColor=none;fillColor=none;align=left;verticalAlign=top;whiteSpace=wrap;rounded=0;fontSize=10;" vertex="1" parent="1">
<mxGeometry x="540" y="380" width="430" height="40" as="geometry"/>
</mxCell>
<mxCell id="71" value="- wait for TT_MATCH_SEQUENCE milliseconds, until CM_START_ATTEN_CHAR_IND is received&lt;br&gt;- if so, go to the substate SOUNDING, if not, go to FAILED" style="text;html=1;strokeColor=none;fillColor=none;align=left;verticalAlign=top;whiteSpace=wrap;rounded=0;fontSize=10;" vertex="1" parent="1">
<mxGeometry x="790" y="480" width="300" height="40" as="geometry"/>
</mxCell>
</root>
</mxGraphModel>
</diagram>
</mxfile>

View File

@@ -0,0 +1,25 @@
.. _everest_modules_handwritten_EvseSlac:
.. ===================
.. EvseSlac
.. ===================
This is an implementation of the ISO 15118-3 EVSE side SLAC protocol.
The general flow of operation will be like this:
- start of operation begins with a control pilot transition from state
A, E or F to Bx, Cx or Dx. This is indicated by the EvseManager by calling
the commands enter_bcd/leave_bcd.
- Once started, it waits for a CM_SLAC_PARM_REQ from the EV side to start the SLAC session.
- In case of success, SLAC finishes with a CM_SLAC_MATCH_RES, which sends a new NMK to the EV side.
The EV then joins the logical network and the two modems are paired. A dlink_ready(true) is published
to signal to the EvseManager that the low level PLC link is ready for communication.
If not run as root user, this modules requires the capability CAP_NET_RAW.
Todo
====
- make use of the enable flag in the reset command or drop it, if not needed
- handle CM_VALIDATE.REQ message
- implement PLC chip resetting after unplug or failed SLAC sessions (especially for QCA chips)

View File

@@ -0,0 +1,79 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2023 - 2023 Pionix GmbH and Contributors to EVerest
#include "fsm_controller.hpp"
#include <everest/slac/fsm/evse/states/others.hpp>
FSMController::FSMController(slac::fsm::evse::Context& context) : ctx(context){};
void FSMController::signal_new_slac_message(slac::messages::HomeplugMessage& msg) {
if (running == false) {
return;
}
{
const std::lock_guard<std::mutex> feed_lck(feed_mtx);
ctx.slac_message_payload = msg;
fsm.handle_event(slac::fsm::evse::Event::SLAC_MESSAGE);
}
new_event = true;
new_event_cv.notify_all();
}
void FSMController::signal_reset() {
signal_simple_event(slac::fsm::evse::Event::RESET);
}
bool FSMController::signal_enter_bcd() {
return signal_simple_event(slac::fsm::evse::Event::ENTER_BCD);
}
bool FSMController::signal_leave_bcd() {
return signal_simple_event(slac::fsm::evse::Event::LEAVE_BCD);
}
bool FSMController::signal_simple_event(slac::fsm::evse::Event ev) {
const std::lock_guard<std::mutex> feed_lck(feed_mtx);
auto event_result = fsm.handle_event(ev);
new_event = true;
new_event_cv.notify_all();
return event_result == fsm::HandleEventResult::SUCCESS;
}
void FSMController::run() {
ctx.log_info("Starting the SLAC state machine");
fsm.reset<slac::fsm::evse::InitState>(ctx);
std::unique_lock<std::mutex> feed_lck(feed_mtx);
running = true;
while (true) {
auto feed_result = fsm.feed();
if (feed_result.transition()) {
// call immediately again
continue;
} else if (feed_result.internal_error() || feed_result.unhandled_event()) {
// FIXME (aw): would need to log here!
} else if (feed_result.has_value() == true) {
const auto timeout = *feed_result;
if (timeout == 0) {
// call feed directly again
continue;
}
new_event_cv.wait_for(feed_lck, std::chrono::milliseconds(timeout), [this] { return new_event; });
} else {
// nothing happened, no return value -> wait for new event
new_event_cv.wait(feed_lck, [this] { return new_event; });
}
if (new_event) {
// we got a new event, reset it and let run feed again
new_event = false;
}
}
}

View File

@@ -0,0 +1,33 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2023 - 2023 Pionix GmbH and Contributors to EVerest
#ifndef EVSE_SLAC_FSM_CONTROLLER_HPP
#define EVSE_SLAC_FSM_CONTROLLER_HPP
#include <everest/slac/fsm/evse/fsm.hpp>
#include <condition_variable>
#include <mutex>
class FSMController {
public:
explicit FSMController(slac::fsm::evse::Context& ctx);
void signal_new_slac_message(slac::messages::HomeplugMessage&);
void signal_reset();
bool signal_enter_bcd();
bool signal_leave_bcd();
void run();
private:
bool signal_simple_event(slac::fsm::evse::Event ev);
slac::fsm::evse::Context& ctx;
slac::fsm::evse::FSM fsm;
bool running{false};
std::mutex feed_mtx;
std::condition_variable new_event_cv;
bool new_event{false};
};
#endif // EVSE_SLAC_FSM_CONTROLLER_HPP

View File

@@ -0,0 +1,177 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2022 - 2026 Pionix GmbH and Contributors to EVerest
#include "slacImpl.hpp"
#include <chrono>
#include <future>
#include <everest/slac/io.hpp>
#include <fmt/core.h>
#include <slac/channel.hpp>
#include <thread>
#include "fsm_controller.hpp"
static std::promise<void> module_ready;
// FIXME (aw): this is ugly, but due to the design of the auto-generated module skeleton ..
static std::unique_ptr<FSMController> fsm_ctrl{nullptr};
namespace module {
namespace main {
static std::string mac_to_ascii(const std::string& mac_binary) {
if (mac_binary.size() < 6)
return "";
return fmt::format("{:02X}:{:02X}:{:02X}:{:02X}:{:02X}:{:02X}", mac_binary[0], mac_binary[1], mac_binary[2],
mac_binary[3], mac_binary[4], mac_binary[5]);
}
void slacImpl::init() {
// setup evse fsm thread
std::thread(&slacImpl::run, this).detach();
}
void slacImpl::ready() {
// let the waiting run thread go
module_ready.set_value();
}
void slacImpl::run() {
// wait until ready
module_ready.get_future().get();
if (config.startup_delay_ms > 0) {
EVLOG_info << "Delaying SLAC startup by " << config.startup_delay_ms << "ms";
std::this_thread::sleep_for(std::chrono::milliseconds(config.startup_delay_ms));
EVLOG_info << "Continuing with SLAC initialization";
}
// initialize slac i/o
SlacIO slac_io;
try {
slac_io.init(config.device);
} catch (const std::exception& e) {
EVLOG_error << fmt::format("Couldn't open device {} for SLAC communication. Reason: {}", config.device,
e.what());
raise_error(
error_factory->create_error("generic/CommunicationFault", "", "Could not open device " + config.device));
return;
}
// setup callbacks
slac::fsm::evse::ContextCallbacks callbacks;
callbacks.send_raw_slac = [&slac_io](slac::messages::HomeplugMessage& msg) { slac_io.send(msg); };
callbacks.signal_dlink_ready = [this](bool value) { publish_dlink_ready(value); };
callbacks.signal_state = [this](const std::string& value) {
try {
publish_state(types::slac::string_to_state(value));
} catch (const std::exception& e) {
EVLOG_error << fmt::format("Tried to publish unknown SLAC state '{}'. Error: {}", value, e.what());
}
};
callbacks.signal_error_routine_request = [this]() { publish_request_error_routine(nullptr); };
callbacks.log_debug = [](const std::string& text) { EVLOG_debug << text; };
callbacks.log_info = [](const std::string& text) { EVLOG_info << text; };
callbacks.log_warn = [](const std::string& text) { EVLOG_warning << text; };
callbacks.log_error = [](const std::string& text) { EVLOG_error << text; };
if (config.publish_mac_on_first_parm_req) {
callbacks.signal_ev_mac_address_parm_req = [this](const std::string& mac) { publish_ev_mac_address(mac); };
}
if (config.publish_mac_on_match_cnf) {
callbacks.signal_ev_mac_address_match_cnf = [this](const std::string& mac) { publish_ev_mac_address(mac); };
}
auto fsm_ctx = slac::fsm::evse::Context(callbacks);
fsm_ctx.slac_config.set_key_timeout_ms = config.set_key_timeout_ms;
fsm_ctx.slac_config.slac_init_timeout_ms = config.slac_init_timeout_ms;
fsm_ctx.slac_config.ac_mode_five_percent = config.ac_mode_five_percent;
fsm_ctx.slac_config.sounding_atten_adjustment = config.sounding_attenuation_adjustment;
fsm_ctx.slac_config.chip_reset.enabled = config.do_chip_reset;
fsm_ctx.slac_config.chip_reset.delay_ms = config.chip_reset_delay_ms;
fsm_ctx.slac_config.chip_reset.timeout_ms = config.chip_reset_timeout_ms;
fsm_ctx.slac_config.link_status.do_detect = config.link_status_detection;
fsm_ctx.slac_config.link_status.retry_ms = config.link_status_retry_ms;
fsm_ctx.slac_config.link_status.timeout_ms = config.link_status_timeout_ms;
fsm_ctx.slac_config.link_status.debug_simulate_failed_matching = config.debug_simulate_failed_matching;
fsm_ctx.slac_config.reset_instead_of_fail = config.reset_instead_of_fail;
fsm_ctx.slac_config.regenerate_key_on_reset = !config.hack_disable_regenerate_key_on_reset;
fsm_ctx.slac_config.generate_nmk();
memcpy(fsm_ctx.evse_mac, slac_io.get_mac_addr(), ETH_ALEN);
fsm_ctrl = std::make_unique<FSMController>(fsm_ctx);
// Qualcomm PLC chip emits VS_ATTENUATION_CHARACTERISTICS (vendor MMTYPE 0xA14E) as
// unsolicited broadcasts during sounding from a sibling MAC. FSM does not handle this
// MMTYPE and logs "Received non-expected SLAC message of type 0xA14E" per frame, which
// adds RX/log load. Drop it pre-FSM. Other MMTYPEs (incl. CM_SET_KEY.CNF, CM_ATTEN_PROFILE.IND)
// pass through unchanged.
slac_io.run([](slac::messages::HomeplugMessage& msg) {
if (msg.get_mmtype() == slac::defs::qualcomm::MMTYPE_VS_ATTENUATION_CHARACTERISTICS) {
return;
}
fsm_ctrl->signal_new_slac_message(msg);
});
fsm_ctrl->run();
}
void slacImpl::handle_reset(bool& enable) {
// FIXME (aw): the enable could be used for power saving etc, but it is not implemented yet
// CC: as power saving is not implemented, we actually don't need to reset at beginning of session (enable=true): At
// start of everest it is being reset once and then it is enough to reset at the end of each session. This saves
// some hundreds of msecs at the beginning of the charging session as we do not need to set up keys. Then
// EvseManager can switch on 5% PWM basically immediately as SLAC is already ready.
if (!enable) {
fsm_ctrl->signal_reset();
}
};
void slacImpl::handle_enter_bcd() {
fsm_ctrl->signal_enter_bcd();
};
void slacImpl::handle_leave_bcd() {
fsm_ctrl->signal_leave_bcd();
};
void slacImpl::handle_dlink_terminate() {
// With receiving a D-LINK_TERMINATE.request from HLE, the communication node
// shall leave the logical network within TP_match_leave. All parameters related
// to the current link shall be set to the default value and shall change to the status "Unmatched".
EVLOG_info << "D-LINK_TERMINATE.request received, leaving network.";
fsm_ctrl->signal_reset();
};
void slacImpl::handle_dlink_error() {
// The D-LINK_ERROR.request requests lower layers to terminate the data link and restart the matching
// process by a control pilot transition through state E (on EVSE side this should be state F though)
// CP signal is handled by EvseManager, so we just need to reset the SLAC state machine here.
// DLINK_ERROR will be send from HLC layers when they detect that the connection is dead.
EVLOG_warning << "D-LINK_ERROR.request received";
fsm_ctrl->signal_reset();
};
void slacImpl::handle_dlink_pause() {
// The D-LINK_PAUSE.request requests lower layers to enter a power saving mode. While being in this
// mode, the state will be kept to "Matched".
// So we don't need to do anything here as we do not support low power mode to power down the PLC modem.
// This is optional in ISO15118-3.
EVLOG_info << "D-LINK_PAUSE.request received. Staying in MATCHED, PLC chip stays powered on (low power mode "
"optional in -3)";
};
} // namespace main
} // namespace module

View File

@@ -0,0 +1,85 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright Pionix GmbH and Contributors to EVerest
#ifndef MAIN_SLAC_IMPL_HPP
#define MAIN_SLAC_IMPL_HPP
//
// AUTO GENERATED - MARKED REGIONS WILL BE KEPT
// template version 3
//
#include <generated/interfaces/slac/Implementation.hpp>
#include "../EvseSlac.hpp"
// ev@75ac1216-19eb-4182-a85c-820f1fc2c091:v1
// insert your custom include headers here
// ev@75ac1216-19eb-4182-a85c-820f1fc2c091:v1
namespace module {
namespace main {
struct Conf {
std::string device;
int number_of_sounds;
bool ac_mode_five_percent;
int set_key_timeout_ms;
int sounding_attenuation_adjustment;
bool publish_mac_on_match_cnf;
bool publish_mac_on_first_parm_req;
bool do_chip_reset;
int chip_reset_delay_ms;
int chip_reset_timeout_ms;
bool link_status_detection;
int link_status_retry_ms;
int link_status_timeout_ms;
bool debug_simulate_failed_matching;
bool reset_instead_of_fail;
int startup_delay_ms;
int slac_init_timeout_ms;
bool hack_disable_regenerate_key_on_reset;
};
class slacImpl : public slacImplBase {
public:
slacImpl() = delete;
slacImpl(Everest::ModuleAdapter* ev, const Everest::PtrContainer<EvseSlac>& mod, Conf& config) :
slacImplBase(ev, "main"), mod(mod), config(config){};
// ev@8ea32d28-373f-4c90-ae5e-b4fcc74e2a61:v1
// insert your public definitions here
// ev@8ea32d28-373f-4c90-ae5e-b4fcc74e2a61:v1
protected:
// command handler functions (virtual)
virtual void handle_reset(bool& enable) override;
virtual void handle_enter_bcd() override;
virtual void handle_leave_bcd() override;
virtual void handle_dlink_terminate() override;
virtual void handle_dlink_error() override;
virtual void handle_dlink_pause() override;
// ev@d2d1847a-7b88-41dd-ad07-92785f06f5c4:v1
// insert your protected definitions here
// ev@d2d1847a-7b88-41dd-ad07-92785f06f5c4:v1
private:
const Everest::PtrContainer<EvseSlac>& mod;
const Conf& config;
virtual void init() override;
virtual void ready() override;
// ev@3370e4dd-95f4-47a9-aaec-ea76f34a66c9:v1
void run();
// ev@3370e4dd-95f4-47a9-aaec-ea76f34a66c9:v1
};
// ev@3d7da0ad-02c2-493d-9920-0bbbd56b9876:v1
// insert other definitions here
// ev@3d7da0ad-02c2-493d-9920-0bbbd56b9876:v1
} // namespace main
} // namespace module
#endif // MAIN_SLAC_IMPL_HPP

View File

@@ -0,0 +1,103 @@
description: Implementation of SLAC data link negotiation according to ISO15118-3.
provides:
main:
interface: slac
description: SLAC interface implementation.
config:
device:
description: Ethernet device used for PLC.
type: string
default: eth1
number_of_sounds:
description: SLAC number of sounds.
type: integer
default: 10
ac_mode_five_percent:
description: >-
Use AC 5% mode according to ISO15118-3. This restarts SLAC sessions if they fail according to the standard.
The standard only allows the retries for AC, not for DC. However, it is strongly recommended to always
set this option to true, also for DC, otherwise some EVs in the field will fail to do SLAC frequently.
type: boolean
default: true
set_key_timeout_ms:
description: Timeout for CM_SET_KEY.REQ. Default works for QCA7000/QCA7005/CG5317.
type: integer
default: 1000
sounding_attenuation_adjustment:
description: Offset in dB that should be added to the calculated sounding attenuation
type: integer
default: 0
publish_mac_on_match_cnf:
description: >-
Publish the EV MAC address on the token_provider interface when matching is confirmed (CM_SLAC_MATCH.CNF).
This can be used for autocharge as an alternative to the EVCCID derived from HLC and published by EvseManager.
This can be used for AC autocharge on cars that do not support actual HLC on AC.
type: boolean
default: true
publish_mac_on_first_parm_req:
description: >-
Publish the EV MAC address when the first CM_SLAC_PARM.REQ.
This should not be used as it is quite error prone: The MAC address might be from another car via cross talk.
It is better to wait for the matching to be done.
type: boolean
default: false
do_chip_reset:
description: Perform a chip reset after setting NMK using the RS_DEV.REQ Vendor MME Extension (Only works on Qualcomm chips)
type: boolean
default: false
chip_reset_delay_ms:
description: Delay between SET_KEY.CNF and RS_DEV.REQ
type: integer
default: 100
chip_reset_timeout_ms:
description: Timeout for RS_DEV.REQ (waiting for RS_DEV.CNF)
type: integer
default: 500
link_status_detection:
description: After matching.cnf, wait for link to come up before sending out d_link_ready=connected using LINK_STATUS Vendor MME Extension (Works on Qualcomm and Lumissil chips)
type: boolean
default: false
link_status_retry_ms:
description: Delay between retries of LINK_STATUS requests after matching request
type: integer
default: 100
link_status_timeout_ms:
description: Timeout for Link to come up after matching request
type: integer
default: 10000
debug_simulate_failed_matching:
description: Only for debugging. Simulate failed matching by sending a wrong NMK to the EV.
type: boolean
default: false
reset_instead_of_fail:
description: >-
Go to reset state instead of failed state. This is against the ISO15118-3.
But some cars directly send a CM_SLAC_PARAM.req message when the SLAC process aborts.
To react to this message and restart the SLAC process, the EVSE go to the reset state here.
type: boolean
default: true
startup_delay_ms:
description: >-
Delay initialization of SLAC I/O by the given amount.
On some hardware platforms with multiple EvseSlac modules loaded this can be necessary to ensure that the initial query of the device information does not happen at the same time.
type: integer
default: 0
slac_init_timeout_ms:
description: >-
Timeout for CM_SLAC_PARM.REQ. Values between 10000 and 20000 can be set at your own risk, as they
violate ISO15118-3. Experience has shown that this should not cause any problems.
type: integer
minimum: 10000
maximum: 50000
default: 40000
hack_disable_regenerate_key_on_reset:
description: >-
Hack: Do not regenerate NMK on reset, this is not spec compliant and should only be set for debugging purposes.
type: boolean
default: false
metadata:
base_license: https://directory.fsf.org/wiki/License:BSD-3-Clause-Clear
license: https://opensource.org/licenses/Apache-2.0
authors:
- aw@pionix.de
- Cornelius Claussen (Pionix GmbH)