2022-11-04 22:51:07 +00:00
|
|
|
# State machine for the charger
|
|
|
|
#
|
|
|
|
#
|
|
|
|
|
|
|
|
#------------------------------------------------------------
|
|
|
|
|
|
|
|
import pyPlcTcpSocket
|
|
|
|
import time # for time.sleep()
|
2022-11-08 11:01:54 +00:00
|
|
|
from helpers import prettyHexMessage
|
2022-11-07 12:38:06 +00:00
|
|
|
from exiConnector import * # for EXI data handling/converting
|
2022-11-04 22:51:07 +00:00
|
|
|
|
|
|
|
stateWaitForSupportedApplicationProtocolRequest = 0
|
|
|
|
stateWaitForSessionSetupRequest = 1
|
|
|
|
stateWaitForServiceDiscoveryRequest = 2
|
2022-11-08 21:53:18 +00:00
|
|
|
stateWaitForServicePaymentSelectionRequest = 3
|
2022-11-09 18:11:18 +00:00
|
|
|
stateWaitForFlexibleRequest = 4
|
2022-11-08 21:53:18 +00:00
|
|
|
stateWaitForChargeParameterDiscoveryRequest = 5
|
2022-11-04 22:51:07 +00:00
|
|
|
stateWaitForCableCheckRequest = 6
|
|
|
|
stateWaitForPreChargeRequest = 7
|
|
|
|
stateWaitForPowerDeliveryRequest = 8
|
|
|
|
|
|
|
|
class fsmEvse():
|
2022-11-22 20:34:27 +00:00
|
|
|
def addToTrace(self, s):
|
2022-11-22 20:48:05 +00:00
|
|
|
self.callbackAddToTrace("[EVSE] " + s)
|
2022-12-19 17:09:39 +00:00
|
|
|
|
|
|
|
def publishStatus(self, s):
|
|
|
|
self.callbackShowStatus(s, "evseState")
|
2022-11-22 20:34:27 +00:00
|
|
|
|
2022-11-04 22:51:07 +00:00
|
|
|
def enterState(self, n):
|
2022-11-22 20:34:27 +00:00
|
|
|
self.addToTrace("from " + str(self.state) + " entering " + str(n))
|
2022-11-04 22:51:07 +00:00
|
|
|
self.state = n
|
|
|
|
self.cyclesInState = 0
|
|
|
|
|
|
|
|
def isTooLong(self):
|
|
|
|
# The timeout handling function.
|
2022-11-08 11:01:54 +00:00
|
|
|
return (self.cyclesInState > 50)
|
2022-11-04 22:51:07 +00:00
|
|
|
|
|
|
|
|
|
|
|
def stateFunctionWaitForSupportedApplicationProtocolRequest(self):
|
|
|
|
if (len(self.rxData)>0):
|
2022-11-22 20:34:27 +00:00
|
|
|
self.addToTrace("In state WaitForSupportedApplicationProtocolRequest, received " + prettyHexMessage(self.rxData))
|
2022-11-07 12:38:06 +00:00
|
|
|
exidata = removeV2GTPHeader(self.rxData)
|
2022-11-04 22:51:07 +00:00
|
|
|
self.rxData = []
|
2022-11-08 12:15:36 +00:00
|
|
|
strConverterResult = exiDecode(exidata, "DH") # Decode Handshake-request
|
2022-11-22 20:34:27 +00:00
|
|
|
self.addToTrace(strConverterResult)
|
2022-11-07 12:38:06 +00:00
|
|
|
if (strConverterResult.find("ProtocolNamespace=urn:din")>0):
|
|
|
|
# todo: of course we should care for schemaID and prio also here
|
2022-11-22 20:34:27 +00:00
|
|
|
self.addToTrace("Detected DIN")
|
2022-11-08 11:01:54 +00:00
|
|
|
# Eh for encode handshake, SupportedApplicationProtocolResponse
|
|
|
|
msg = addV2GTPHeader(exiEncode("Eh"))
|
2022-11-22 20:34:27 +00:00
|
|
|
self.addToTrace("responding " + prettyHexMessage(msg))
|
2022-11-07 12:38:06 +00:00
|
|
|
self.Tcp.transmit(msg)
|
2022-12-19 17:09:39 +00:00
|
|
|
self.publishStatus("Schema negotiated")
|
2022-11-08 21:53:18 +00:00
|
|
|
self.enterState(stateWaitForSessionSetupRequest)
|
2022-11-04 22:51:07 +00:00
|
|
|
|
|
|
|
def stateFunctionWaitForSessionSetupRequest(self):
|
|
|
|
if (len(self.rxData)>0):
|
2022-11-22 20:34:27 +00:00
|
|
|
self.addToTrace("In state stateFunctionWaitForSessionSetupRequest, received " + prettyHexMessage(self.rxData))
|
2022-11-08 11:01:54 +00:00
|
|
|
exidata = removeV2GTPHeader(self.rxData)
|
2022-11-04 22:51:07 +00:00
|
|
|
self.rxData = []
|
2022-11-08 21:53:18 +00:00
|
|
|
strConverterResult = exiDecode(exidata, "DD")
|
2022-11-22 20:34:27 +00:00
|
|
|
self.addToTrace(strConverterResult)
|
2022-11-08 21:53:18 +00:00
|
|
|
if (strConverterResult.find("SessionSetupReq")>0):
|
2022-11-08 11:01:54 +00:00
|
|
|
# todo: check the request content, and fill response parameters
|
|
|
|
msg = addV2GTPHeader(exiEncode("EDa")) # EDa for Encode, Din, SessionSetupResponse
|
2022-11-22 20:34:27 +00:00
|
|
|
self.addToTrace("responding " + prettyHexMessage(msg))
|
2022-12-19 17:09:39 +00:00
|
|
|
self.Tcp.transmit(msg)
|
|
|
|
self.publishStatus("Session established")
|
2022-11-08 21:53:18 +00:00
|
|
|
self.enterState(stateWaitForServiceDiscoveryRequest)
|
2022-11-04 22:51:07 +00:00
|
|
|
if (self.isTooLong()):
|
|
|
|
self.enterState(0)
|
|
|
|
|
|
|
|
def stateFunctionWaitForServiceDiscoveryRequest(self):
|
|
|
|
if (len(self.rxData)>0):
|
2022-11-22 20:34:27 +00:00
|
|
|
self.addToTrace("In state WaitForServiceDiscoveryRequest, received " + prettyHexMessage(self.rxData))
|
2022-11-08 21:53:18 +00:00
|
|
|
exidata = removeV2GTPHeader(self.rxData)
|
2022-11-04 22:51:07 +00:00
|
|
|
self.rxData = []
|
2022-11-08 21:53:18 +00:00
|
|
|
strConverterResult = exiDecode(exidata, "DD")
|
2022-11-22 20:34:27 +00:00
|
|
|
self.addToTrace(strConverterResult)
|
2022-11-08 21:53:18 +00:00
|
|
|
if (strConverterResult.find("ServiceDiscoveryReq")>0):
|
|
|
|
# todo: check the request content, and fill response parameters
|
|
|
|
msg = addV2GTPHeader(exiEncode("EDb")) # EDb for Encode, Din, ServiceDiscoveryResponse
|
2022-11-22 20:34:27 +00:00
|
|
|
self.addToTrace("responding " + prettyHexMessage(msg))
|
2022-12-19 17:09:39 +00:00
|
|
|
self.Tcp.transmit(msg)
|
|
|
|
self.publishStatus("Services discovered")
|
2022-11-08 21:53:18 +00:00
|
|
|
self.enterState(stateWaitForServicePaymentSelectionRequest)
|
2022-11-04 22:51:07 +00:00
|
|
|
if (self.isTooLong()):
|
|
|
|
self.enterState(0)
|
|
|
|
|
2022-11-08 21:53:18 +00:00
|
|
|
def stateFunctionWaitForServicePaymentSelectionRequest(self):
|
2022-11-04 22:51:07 +00:00
|
|
|
if (len(self.rxData)>0):
|
2022-11-22 20:34:27 +00:00
|
|
|
self.addToTrace("In state WaitForServicePaymentSelectionRequest, received " + prettyHexMessage(self.rxData))
|
2022-11-08 21:53:18 +00:00
|
|
|
exidata = removeV2GTPHeader(self.rxData)
|
2022-11-04 22:51:07 +00:00
|
|
|
self.rxData = []
|
2022-11-08 21:53:18 +00:00
|
|
|
strConverterResult = exiDecode(exidata, "DD")
|
2022-11-22 20:34:27 +00:00
|
|
|
self.addToTrace(strConverterResult)
|
2022-11-08 21:53:18 +00:00
|
|
|
if (strConverterResult.find("ServicePaymentSelectionReq")>0):
|
|
|
|
# todo: check the request content, and fill response parameters
|
|
|
|
msg = addV2GTPHeader(exiEncode("EDc")) # EDc for Encode, Din, ServicePaymentSelectionResponse
|
2022-11-22 20:34:27 +00:00
|
|
|
self.addToTrace("responding " + prettyHexMessage(msg))
|
2022-12-19 17:09:39 +00:00
|
|
|
self.Tcp.transmit(msg)
|
|
|
|
self.publishStatus("ServicePayment selected")
|
2022-11-09 18:11:18 +00:00
|
|
|
self.enterState(stateWaitForFlexibleRequest) # todo: not clear, what is specified. The Ioniq sends PowerDeliveryReq as next.
|
2022-11-04 22:51:07 +00:00
|
|
|
if (self.isTooLong()):
|
|
|
|
self.enterState(0)
|
|
|
|
|
2022-11-09 18:11:18 +00:00
|
|
|
def stateFunctionWaitForFlexibleRequest(self):
|
2022-11-04 22:51:07 +00:00
|
|
|
if (len(self.rxData)>0):
|
2022-11-22 20:34:27 +00:00
|
|
|
self.addToTrace("In state WaitForFlexibleRequest, received " + prettyHexMessage(self.rxData))
|
2022-11-08 21:53:18 +00:00
|
|
|
exidata = removeV2GTPHeader(self.rxData)
|
2022-11-04 22:51:07 +00:00
|
|
|
self.rxData = []
|
2022-11-08 21:53:18 +00:00
|
|
|
strConverterResult = exiDecode(exidata, "DD")
|
2022-11-22 20:34:27 +00:00
|
|
|
self.addToTrace(strConverterResult)
|
2022-11-09 18:11:18 +00:00
|
|
|
if (strConverterResult.find("PowerDeliveryReq")>0):
|
|
|
|
# todo: check the request content, and fill response parameters
|
|
|
|
msg = addV2GTPHeader(exiEncode("EDh")) # EDh for Encode, Din, PowerDeliveryResponse
|
2022-11-22 20:34:27 +00:00
|
|
|
self.addToTrace("responding " + prettyHexMessage(msg))
|
2022-12-19 17:09:39 +00:00
|
|
|
self.publishStatus("PowerDelivery")
|
2022-11-09 18:11:18 +00:00
|
|
|
self.Tcp.transmit(msg)
|
|
|
|
self.enterState(stateWaitForFlexibleRequest) # todo: not clear, what is specified in DIN
|
2022-11-08 21:53:18 +00:00
|
|
|
if (strConverterResult.find("ChargeParameterDiscoveryReq")>0):
|
|
|
|
# todo: check the request content, and fill response parameters
|
|
|
|
msg = addV2GTPHeader(exiEncode("EDe")) # EDe for Encode, Din, ChargeParameterDiscoveryResponse
|
2022-11-22 20:34:27 +00:00
|
|
|
self.addToTrace("responding " + prettyHexMessage(msg))
|
2022-12-19 17:09:39 +00:00
|
|
|
self.publishStatus("ChargeParamDiscovery")
|
2022-11-09 18:11:18 +00:00
|
|
|
self.Tcp.transmit(msg)
|
|
|
|
self.enterState(stateWaitForFlexibleRequest) # todo: not clear, what is specified in DIN
|
2022-11-08 21:53:18 +00:00
|
|
|
if (strConverterResult.find("CableCheckReq")>0):
|
|
|
|
# todo: check the request content, and fill response parameters
|
|
|
|
msg = addV2GTPHeader(exiEncode("EDf")) # EDf for Encode, Din, CableCheckResponse
|
2022-11-22 20:34:27 +00:00
|
|
|
self.addToTrace("responding " + prettyHexMessage(msg))
|
2022-12-19 17:09:39 +00:00
|
|
|
self.publishStatus("CableCheck")
|
2022-11-09 18:11:18 +00:00
|
|
|
self.Tcp.transmit(msg)
|
|
|
|
self.enterState(stateWaitForFlexibleRequest) # todo: not clear, what is specified in DIN
|
|
|
|
if (strConverterResult.find("PreChargeReq")>0):
|
|
|
|
# todo: check the request content, and fill response parameters
|
2022-11-16 18:33:37 +00:00
|
|
|
strPresentVoltage = "345"
|
|
|
|
msg = addV2GTPHeader(exiEncode("EDg_"+strPresentVoltage)) # EDg for Encode, Din, PreChargeResponse
|
2022-11-22 20:34:27 +00:00
|
|
|
self.addToTrace("responding " + prettyHexMessage(msg))
|
2022-12-19 17:09:39 +00:00
|
|
|
self.publishStatus("PreCharging")
|
2022-11-09 18:11:18 +00:00
|
|
|
self.Tcp.transmit(msg)
|
2022-11-11 11:29:13 +00:00
|
|
|
self.enterState(stateWaitForFlexibleRequest) # todo: not clear, what is specified in DIN
|
|
|
|
if (strConverterResult.find("ContractAuthenticationReq")>0):
|
|
|
|
# todo: check the request content, and fill response parameters
|
|
|
|
msg = addV2GTPHeader(exiEncode("EDl")) # EDl for Encode, Din, ContractAuthenticationResponse
|
2022-11-22 20:34:27 +00:00
|
|
|
self.addToTrace("responding " + prettyHexMessage(msg))
|
2022-12-19 17:09:39 +00:00
|
|
|
self.publishStatus("ContractAuthentication")
|
2022-11-11 11:29:13 +00:00
|
|
|
self.Tcp.transmit(msg)
|
|
|
|
self.enterState(stateWaitForFlexibleRequest) # todo: not clear, what is specified in DIN
|
2022-12-08 23:22:18 +00:00
|
|
|
if (strConverterResult.find("CurrentDemandReq")>0):
|
|
|
|
# todo: check the request content, and fill response parameters
|
|
|
|
msg = addV2GTPHeader(exiEncode("EDi")) # EDi for Encode, Din, CurrentDemandRes
|
|
|
|
self.addToTrace("responding " + prettyHexMessage(msg))
|
2022-12-19 17:09:39 +00:00
|
|
|
self.publishStatus("CurrentDemand")
|
2022-12-08 23:22:18 +00:00
|
|
|
self.Tcp.transmit(msg)
|
|
|
|
self.enterState(stateWaitForFlexibleRequest) # todo: not clear, what is specified in DIN
|
|
|
|
if (strConverterResult.find("WeldingDetectionReq")>0):
|
|
|
|
# todo: check the request content, and fill response parameters
|
|
|
|
msg = addV2GTPHeader(exiEncode("EDj")) # EDj for Encode, Din, WeldingDetectionRes
|
|
|
|
self.addToTrace("responding " + prettyHexMessage(msg))
|
2022-12-19 17:09:39 +00:00
|
|
|
self.publishStatus("WeldingDetection")
|
2022-12-08 23:22:18 +00:00
|
|
|
self.Tcp.transmit(msg)
|
|
|
|
self.enterState(stateWaitForFlexibleRequest) # todo: not clear, what is specified in DIN
|
|
|
|
if (strConverterResult.find("SessionStopReq")>0):
|
|
|
|
# todo: check the request content, and fill response parameters
|
|
|
|
msg = addV2GTPHeader(exiEncode("EDk")) # EDk for Encode, Din, SessionStopRes
|
|
|
|
self.addToTrace("responding " + prettyHexMessage(msg))
|
2022-12-19 17:09:39 +00:00
|
|
|
self.publishStatus("SessionStop")
|
2022-12-08 23:22:18 +00:00
|
|
|
self.Tcp.transmit(msg)
|
|
|
|
self.enterState(stateWaitForFlexibleRequest) # todo: not clear, what is specified in DIN
|
2022-11-11 11:29:13 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
2022-11-09 18:11:18 +00:00
|
|
|
if (self.isTooLong()):
|
|
|
|
self.enterState(0)
|
|
|
|
|
|
|
|
def stateFunctionWaitForChargeParameterDiscoveryRequest(self):
|
|
|
|
if (self.isTooLong()):
|
|
|
|
self.enterState(0)
|
|
|
|
|
|
|
|
def stateFunctionWaitForCableCheckRequest(self):
|
2022-11-04 22:51:07 +00:00
|
|
|
if (self.isTooLong()):
|
|
|
|
self.enterState(0)
|
|
|
|
|
|
|
|
def stateFunctionWaitForPreChargeRequest(self):
|
|
|
|
if (self.isTooLong()):
|
|
|
|
self.enterState(0)
|
|
|
|
|
|
|
|
def stateFunctionWaitForPowerDeliveryRequest(self):
|
|
|
|
if (len(self.rxData)>0):
|
2022-11-22 20:34:27 +00:00
|
|
|
self.addToTrace("In state WaitForPowerDeliveryRequest, received " + prettyHexMessage(self.rxData))
|
|
|
|
self.addToTrace("Todo: Reaction in state WaitForPowerDeliveryRequest is not implemented yet.")
|
2022-11-04 22:51:07 +00:00
|
|
|
self.rxData = []
|
|
|
|
self.enterState(0)
|
|
|
|
if (self.isTooLong()):
|
|
|
|
self.enterState(0)
|
|
|
|
|
|
|
|
|
|
|
|
stateFunctions = {
|
|
|
|
stateWaitForSupportedApplicationProtocolRequest: stateFunctionWaitForSupportedApplicationProtocolRequest,
|
|
|
|
stateWaitForSessionSetupRequest: stateFunctionWaitForSessionSetupRequest,
|
|
|
|
stateWaitForServiceDiscoveryRequest: stateFunctionWaitForServiceDiscoveryRequest,
|
2022-11-08 21:53:18 +00:00
|
|
|
stateWaitForServicePaymentSelectionRequest: stateFunctionWaitForServicePaymentSelectionRequest,
|
2022-11-09 18:11:18 +00:00
|
|
|
stateWaitForFlexibleRequest: stateFunctionWaitForFlexibleRequest,
|
2022-11-08 21:53:18 +00:00
|
|
|
stateWaitForChargeParameterDiscoveryRequest: stateFunctionWaitForChargeParameterDiscoveryRequest,
|
2022-11-04 22:51:07 +00:00
|
|
|
stateWaitForCableCheckRequest: stateFunctionWaitForCableCheckRequest,
|
|
|
|
stateWaitForPreChargeRequest: stateFunctionWaitForPreChargeRequest,
|
|
|
|
stateWaitForPowerDeliveryRequest: stateFunctionWaitForPowerDeliveryRequest,
|
|
|
|
}
|
|
|
|
|
2022-11-07 08:21:25 +00:00
|
|
|
def reInit(self):
|
2022-11-22 20:34:27 +00:00
|
|
|
self.addToTrace("re-initializing fsmEvse")
|
2022-11-07 08:21:25 +00:00
|
|
|
self.state = 0
|
|
|
|
self.cyclesInState = 0
|
|
|
|
self.rxData = []
|
|
|
|
|
2022-12-19 17:09:39 +00:00
|
|
|
def __init__(self, addressManager, callbackAddToTrace, hardwareInterface, callbackShowStatus):
|
2022-11-22 20:34:27 +00:00
|
|
|
self.callbackAddToTrace = callbackAddToTrace
|
2022-12-19 17:09:39 +00:00
|
|
|
self.callbackShowStatus = callbackShowStatus
|
|
|
|
#todo self.addressManager = addressManager
|
|
|
|
#todo self.hardwareInterface = hardwareInterface
|
2022-11-22 20:34:27 +00:00
|
|
|
self.addToTrace("initializing fsmEvse")
|
2022-11-29 07:40:34 +00:00
|
|
|
self.faultInjectionDelayUntilSocketOpen_s = 0
|
|
|
|
if (self.faultInjectionDelayUntilSocketOpen_s>0):
|
|
|
|
self.addToTrace("Fault injection: waiting " + str(self.faultInjectionDelayUntilSocketOpen_s) + " s until opening the TCP socket.")
|
|
|
|
time.sleep(self.faultInjectionDelayUntilSocketOpen_s)
|
2022-11-22 20:48:05 +00:00
|
|
|
self.Tcp = pyPlcTcpSocket.pyPlcTcpServerSocket(self.callbackAddToTrace)
|
2022-11-04 22:51:07 +00:00
|
|
|
self.state = 0
|
|
|
|
self.cyclesInState = 0
|
|
|
|
self.rxData = []
|
|
|
|
|
|
|
|
def mainfunction(self):
|
|
|
|
self.Tcp.mainfunction() # call the lower-level worker
|
|
|
|
if (self.Tcp.isRxDataAvailable()):
|
|
|
|
self.rxData = self.Tcp.getRxData()
|
2022-11-22 20:34:27 +00:00
|
|
|
#self.addToTrace("received " + str(self.rxData))
|
2022-11-04 22:51:07 +00:00
|
|
|
# run the state machine:
|
|
|
|
self.cyclesInState += 1 # for timeout handling, count how long we are in a state
|
|
|
|
self.stateFunctions[self.state](self)
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
print("Testing the evse state machine")
|
|
|
|
evse = fsmEvse()
|
|
|
|
print("Press Ctrl-Break for aborting")
|
|
|
|
while (True):
|
|
|
|
time.sleep(0.1)
|
|
|
|
evse.mainfunction()
|
|
|
|
|
|
|
|
|