Logging to UDP broadcast port 514

This commit is contained in:
uhi22 2022-11-22 21:34:27 +01:00
parent 15111915ec
commit 43d96f333e
6 changed files with 213 additions and 91 deletions

View file

@ -20,8 +20,11 @@ stateWaitForPreChargeRequest = 7
stateWaitForPowerDeliveryRequest = 8 stateWaitForPowerDeliveryRequest = 8
class fsmEvse(): class fsmEvse():
def addToTrace(self, s):
self.callbackAddToTrace(s)
def enterState(self, n): def enterState(self, n):
print("from " + str(self.state) + " entering " + str(n)) self.addToTrace("from " + str(self.state) + " entering " + str(n))
self.state = n self.state = n
self.cyclesInState = 0 self.cyclesInState = 0
@ -32,31 +35,31 @@ class fsmEvse():
def stateFunctionWaitForSupportedApplicationProtocolRequest(self): def stateFunctionWaitForSupportedApplicationProtocolRequest(self):
if (len(self.rxData)>0): if (len(self.rxData)>0):
print("In state WaitForSupportedApplicationProtocolRequest, received " + prettyHexMessage(self.rxData)) self.addToTrace("In state WaitForSupportedApplicationProtocolRequest, received " + prettyHexMessage(self.rxData))
exidata = removeV2GTPHeader(self.rxData) exidata = removeV2GTPHeader(self.rxData)
self.rxData = [] self.rxData = []
strConverterResult = exiDecode(exidata, "DH") # Decode Handshake-request strConverterResult = exiDecode(exidata, "DH") # Decode Handshake-request
print(strConverterResult) self.addToTrace(strConverterResult)
if (strConverterResult.find("ProtocolNamespace=urn:din")>0): if (strConverterResult.find("ProtocolNamespace=urn:din")>0):
# todo: of course we should care for schemaID and prio also here # todo: of course we should care for schemaID and prio also here
print("Detected DIN") self.addToTrace("Detected DIN")
# Eh for encode handshake, SupportedApplicationProtocolResponse # Eh for encode handshake, SupportedApplicationProtocolResponse
msg = addV2GTPHeader(exiEncode("Eh")) msg = addV2GTPHeader(exiEncode("Eh"))
print("responding " + prettyHexMessage(msg)) self.addToTrace("responding " + prettyHexMessage(msg))
self.Tcp.transmit(msg) self.Tcp.transmit(msg)
self.enterState(stateWaitForSessionSetupRequest) self.enterState(stateWaitForSessionSetupRequest)
def stateFunctionWaitForSessionSetupRequest(self): def stateFunctionWaitForSessionSetupRequest(self):
if (len(self.rxData)>0): if (len(self.rxData)>0):
print("In state stateFunctionWaitForSessionSetupRequest, received " + prettyHexMessage(self.rxData)) self.addToTrace("In state stateFunctionWaitForSessionSetupRequest, received " + prettyHexMessage(self.rxData))
exidata = removeV2GTPHeader(self.rxData) exidata = removeV2GTPHeader(self.rxData)
self.rxData = [] self.rxData = []
strConverterResult = exiDecode(exidata, "DD") strConverterResult = exiDecode(exidata, "DD")
print(strConverterResult) self.addToTrace(strConverterResult)
if (strConverterResult.find("SessionSetupReq")>0): if (strConverterResult.find("SessionSetupReq")>0):
# todo: check the request content, and fill response parameters # todo: check the request content, and fill response parameters
msg = addV2GTPHeader(exiEncode("EDa")) # EDa for Encode, Din, SessionSetupResponse msg = addV2GTPHeader(exiEncode("EDa")) # EDa for Encode, Din, SessionSetupResponse
print("responding " + prettyHexMessage(msg)) self.addToTrace("responding " + prettyHexMessage(msg))
self.Tcp.transmit(msg) self.Tcp.transmit(msg)
self.enterState(stateWaitForServiceDiscoveryRequest) self.enterState(stateWaitForServiceDiscoveryRequest)
if (self.isTooLong()): if (self.isTooLong()):
@ -64,15 +67,15 @@ class fsmEvse():
def stateFunctionWaitForServiceDiscoveryRequest(self): def stateFunctionWaitForServiceDiscoveryRequest(self):
if (len(self.rxData)>0): if (len(self.rxData)>0):
print("In state WaitForServiceDiscoveryRequest, received " + prettyHexMessage(self.rxData)) self.addToTrace("In state WaitForServiceDiscoveryRequest, received " + prettyHexMessage(self.rxData))
exidata = removeV2GTPHeader(self.rxData) exidata = removeV2GTPHeader(self.rxData)
self.rxData = [] self.rxData = []
strConverterResult = exiDecode(exidata, "DD") strConverterResult = exiDecode(exidata, "DD")
print(strConverterResult) self.addToTrace(strConverterResult)
if (strConverterResult.find("ServiceDiscoveryReq")>0): if (strConverterResult.find("ServiceDiscoveryReq")>0):
# todo: check the request content, and fill response parameters # todo: check the request content, and fill response parameters
msg = addV2GTPHeader(exiEncode("EDb")) # EDb for Encode, Din, ServiceDiscoveryResponse msg = addV2GTPHeader(exiEncode("EDb")) # EDb for Encode, Din, ServiceDiscoveryResponse
print("responding " + prettyHexMessage(msg)) self.addToTrace("responding " + prettyHexMessage(msg))
self.Tcp.transmit(msg) self.Tcp.transmit(msg)
self.enterState(stateWaitForServicePaymentSelectionRequest) self.enterState(stateWaitForServicePaymentSelectionRequest)
if (self.isTooLong()): if (self.isTooLong()):
@ -80,15 +83,15 @@ class fsmEvse():
def stateFunctionWaitForServicePaymentSelectionRequest(self): def stateFunctionWaitForServicePaymentSelectionRequest(self):
if (len(self.rxData)>0): if (len(self.rxData)>0):
print("In state WaitForServicePaymentSelectionRequest, received " + prettyHexMessage(self.rxData)) self.addToTrace("In state WaitForServicePaymentSelectionRequest, received " + prettyHexMessage(self.rxData))
exidata = removeV2GTPHeader(self.rxData) exidata = removeV2GTPHeader(self.rxData)
self.rxData = [] self.rxData = []
strConverterResult = exiDecode(exidata, "DD") strConverterResult = exiDecode(exidata, "DD")
print(strConverterResult) self.addToTrace(strConverterResult)
if (strConverterResult.find("ServicePaymentSelectionReq")>0): if (strConverterResult.find("ServicePaymentSelectionReq")>0):
# todo: check the request content, and fill response parameters # todo: check the request content, and fill response parameters
msg = addV2GTPHeader(exiEncode("EDc")) # EDc for Encode, Din, ServicePaymentSelectionResponse msg = addV2GTPHeader(exiEncode("EDc")) # EDc for Encode, Din, ServicePaymentSelectionResponse
print("responding " + prettyHexMessage(msg)) self.addToTrace("responding " + prettyHexMessage(msg))
self.Tcp.transmit(msg) self.Tcp.transmit(msg)
self.enterState(stateWaitForFlexibleRequest) # todo: not clear, what is specified. The Ioniq sends PowerDeliveryReq as next. self.enterState(stateWaitForFlexibleRequest) # todo: not clear, what is specified. The Ioniq sends PowerDeliveryReq as next.
if (self.isTooLong()): if (self.isTooLong()):
@ -96,40 +99,40 @@ class fsmEvse():
def stateFunctionWaitForFlexibleRequest(self): def stateFunctionWaitForFlexibleRequest(self):
if (len(self.rxData)>0): if (len(self.rxData)>0):
print("In state WaitForFlexibleRequest, received " + prettyHexMessage(self.rxData)) self.addToTrace("In state WaitForFlexibleRequest, received " + prettyHexMessage(self.rxData))
exidata = removeV2GTPHeader(self.rxData) exidata = removeV2GTPHeader(self.rxData)
self.rxData = [] self.rxData = []
strConverterResult = exiDecode(exidata, "DD") strConverterResult = exiDecode(exidata, "DD")
print(strConverterResult) self.addToTrace(strConverterResult)
if (strConverterResult.find("PowerDeliveryReq")>0): if (strConverterResult.find("PowerDeliveryReq")>0):
# todo: check the request content, and fill response parameters # todo: check the request content, and fill response parameters
msg = addV2GTPHeader(exiEncode("EDh")) # EDh for Encode, Din, PowerDeliveryResponse msg = addV2GTPHeader(exiEncode("EDh")) # EDh for Encode, Din, PowerDeliveryResponse
print("responding " + prettyHexMessage(msg)) self.addToTrace("responding " + prettyHexMessage(msg))
self.Tcp.transmit(msg) self.Tcp.transmit(msg)
self.enterState(stateWaitForFlexibleRequest) # todo: not clear, what is specified in DIN self.enterState(stateWaitForFlexibleRequest) # todo: not clear, what is specified in DIN
if (strConverterResult.find("ChargeParameterDiscoveryReq")>0): if (strConverterResult.find("ChargeParameterDiscoveryReq")>0):
# todo: check the request content, and fill response parameters # todo: check the request content, and fill response parameters
msg = addV2GTPHeader(exiEncode("EDe")) # EDe for Encode, Din, ChargeParameterDiscoveryResponse msg = addV2GTPHeader(exiEncode("EDe")) # EDe for Encode, Din, ChargeParameterDiscoveryResponse
print("responding " + prettyHexMessage(msg)) self.addToTrace("responding " + prettyHexMessage(msg))
self.Tcp.transmit(msg) self.Tcp.transmit(msg)
self.enterState(stateWaitForFlexibleRequest) # todo: not clear, what is specified in DIN self.enterState(stateWaitForFlexibleRequest) # todo: not clear, what is specified in DIN
if (strConverterResult.find("CableCheckReq")>0): if (strConverterResult.find("CableCheckReq")>0):
# todo: check the request content, and fill response parameters # todo: check the request content, and fill response parameters
msg = addV2GTPHeader(exiEncode("EDf")) # EDf for Encode, Din, CableCheckResponse msg = addV2GTPHeader(exiEncode("EDf")) # EDf for Encode, Din, CableCheckResponse
print("responding " + prettyHexMessage(msg)) self.addToTrace("responding " + prettyHexMessage(msg))
self.Tcp.transmit(msg) self.Tcp.transmit(msg)
self.enterState(stateWaitForFlexibleRequest) # todo: not clear, what is specified in DIN self.enterState(stateWaitForFlexibleRequest) # todo: not clear, what is specified in DIN
if (strConverterResult.find("PreChargeReq")>0): if (strConverterResult.find("PreChargeReq")>0):
# todo: check the request content, and fill response parameters # todo: check the request content, and fill response parameters
strPresentVoltage = "345" strPresentVoltage = "345"
msg = addV2GTPHeader(exiEncode("EDg_"+strPresentVoltage)) # EDg for Encode, Din, PreChargeResponse msg = addV2GTPHeader(exiEncode("EDg_"+strPresentVoltage)) # EDg for Encode, Din, PreChargeResponse
print("responding " + prettyHexMessage(msg)) self.addToTrace("responding " + prettyHexMessage(msg))
self.Tcp.transmit(msg) self.Tcp.transmit(msg)
self.enterState(stateWaitForFlexibleRequest) # todo: not clear, what is specified in DIN self.enterState(stateWaitForFlexibleRequest) # todo: not clear, what is specified in DIN
if (strConverterResult.find("ContractAuthenticationReq")>0): if (strConverterResult.find("ContractAuthenticationReq")>0):
# todo: check the request content, and fill response parameters # todo: check the request content, and fill response parameters
msg = addV2GTPHeader(exiEncode("EDl")) # EDl for Encode, Din, ContractAuthenticationResponse msg = addV2GTPHeader(exiEncode("EDl")) # EDl for Encode, Din, ContractAuthenticationResponse
print("responding " + prettyHexMessage(msg)) self.addToTrace("responding " + prettyHexMessage(msg))
self.Tcp.transmit(msg) self.Tcp.transmit(msg)
self.enterState(stateWaitForFlexibleRequest) # todo: not clear, what is specified in DIN self.enterState(stateWaitForFlexibleRequest) # todo: not clear, what is specified in DIN
@ -152,8 +155,8 @@ class fsmEvse():
def stateFunctionWaitForPowerDeliveryRequest(self): def stateFunctionWaitForPowerDeliveryRequest(self):
if (len(self.rxData)>0): if (len(self.rxData)>0):
print("In state WaitForPowerDeliveryRequest, received " + prettyHexMessage(self.rxData)) self.addToTrace("In state WaitForPowerDeliveryRequest, received " + prettyHexMessage(self.rxData))
print("Todo: Reaction in state WaitForPowerDeliveryRequest is not implemented yet.") self.addToTrace("Todo: Reaction in state WaitForPowerDeliveryRequest is not implemented yet.")
self.rxData = [] self.rxData = []
self.enterState(0) self.enterState(0)
if (self.isTooLong()): if (self.isTooLong()):
@ -173,13 +176,14 @@ class fsmEvse():
} }
def reInit(self): def reInit(self):
print("re-initializing fsmEvse") self.addToTrace("re-initializing fsmEvse")
self.state = 0 self.state = 0
self.cyclesInState = 0 self.cyclesInState = 0
self.rxData = [] self.rxData = []
def __init__(self): def __init__(self, callbackAddToTrace):
print("initializing fsmEvse") self.callbackAddToTrace = callbackAddToTrace
self.addToTrace("initializing fsmEvse")
self.Tcp = pyPlcTcpSocket.pyPlcTcpServerSocket() self.Tcp = pyPlcTcpSocket.pyPlcTcpServerSocket()
self.state = 0 self.state = 0
self.cyclesInState = 0 self.cyclesInState = 0
@ -189,7 +193,7 @@ class fsmEvse():
self.Tcp.mainfunction() # call the lower-level worker self.Tcp.mainfunction() # call the lower-level worker
if (self.Tcp.isRxDataAvailable()): if (self.Tcp.isRxDataAvailable()):
self.rxData = self.Tcp.getRxData() self.rxData = self.Tcp.getRxData()
#print("received " + str(self.rxData)) #self.addToTrace("received " + str(self.rxData))
# run the state machine: # run the state machine:
self.cyclesInState += 1 # for timeout handling, count how long we are in a state self.cyclesInState += 1 # for timeout handling, count how long we are in a state
self.stateFunctions[self.state](self) self.stateFunctions[self.state](self)

View file

@ -26,6 +26,9 @@ dinEVSEProcessingType_Finished = "0"
dinEVSEProcessingType_Ongoing = "1" dinEVSEProcessingType_Ongoing = "1"
class fsmPev(): class fsmPev():
def addToTrace(self, s):
self.callbackAddToTrace(s)
def enterState(self, n): def enterState(self, n):
print("from " + str(self.state) + " entering " + str(n)) print("from " + str(self.state) + " entering " + str(n))
self.state = n self.state = n
@ -48,15 +51,15 @@ class fsmPev():
def stateFunctionWaitForSupportedApplicationProtocolResponse(self): def stateFunctionWaitForSupportedApplicationProtocolResponse(self):
if (len(self.rxData)>0): if (len(self.rxData)>0):
print("In state WaitForSupportedApplicationProtocolResponse, received " + prettyHexMessage(self.rxData)) self.addToTrace("In state WaitForSupportedApplicationProtocolResponse, received " + prettyHexMessage(self.rxData))
exidata = removeV2GTPHeader(self.rxData) exidata = removeV2GTPHeader(self.rxData)
self.rxData = [] self.rxData = []
strConverterResult = exiDecode(exidata, "Dh") # Decode Handshake-response strConverterResult = exiDecode(exidata, "Dh") # Decode Handshake-response
print(strConverterResult) self.addToTrace(strConverterResult)
if (strConverterResult.find("supportedAppProtocolRes")>0): if (strConverterResult.find("supportedAppProtocolRes")>0):
# todo: check the request content, and fill response parameters # todo: check the request content, and fill response parameters
msg = addV2GTPHeader(exiEncode("EDA")) # EDA for Encode, Din, SessionSetupReq msg = addV2GTPHeader(exiEncode("EDA")) # EDA for Encode, Din, SessionSetupReq
print("responding " + prettyHexMessage(msg)) self.addToTrace("responding " + prettyHexMessage(msg))
self.Tcp.transmit(msg) self.Tcp.transmit(msg)
self.enterState(stateWaitForSessionSetupResponse) self.enterState(stateWaitForSessionSetupResponse)
if (self.isTooLong()): if (self.isTooLong()):
@ -64,22 +67,22 @@ class fsmPev():
def stateFunctionWaitForSessionSetupResponse(self): def stateFunctionWaitForSessionSetupResponse(self):
if (len(self.rxData)>0): if (len(self.rxData)>0):
print("In state WaitForSessionSetupResponse, received " + prettyHexMessage(self.rxData)) self.addToTrace("In state WaitForSessionSetupResponse, received " + prettyHexMessage(self.rxData))
exidata = removeV2GTPHeader(self.rxData) exidata = removeV2GTPHeader(self.rxData)
self.rxData = [] self.rxData = []
strConverterResult = exiDecode(exidata, "DD") # Decode DIN strConverterResult = exiDecode(exidata, "DD") # Decode DIN
print(strConverterResult) self.addToTrace(strConverterResult)
if (strConverterResult.find("SessionSetupRes")>0): if (strConverterResult.find("SessionSetupRes")>0):
# todo: check the request content, and fill response parameters # todo: check the request content, and fill response parameters
try: try:
y = json.loads(strConverterResult) y = json.loads(strConverterResult)
strSessionId = y["header.SessionID"] strSessionId = y["header.SessionID"]
print("[PEV] The Evse decided for SessionId " + strSessionId) self.addToTrace("[PEV] The Evse decided for SessionId " + strSessionId)
self.sessionId = strSessionId self.sessionId = strSessionId
except: except:
print("ERROR: Could not decode the sessionID") self.addToTrace("ERROR: Could not decode the sessionID")
msg = addV2GTPHeader(exiEncode("EDB_"+self.sessionId)) # EDB for Encode, Din, ServiceDiscoveryRequest msg = addV2GTPHeader(exiEncode("EDB_"+self.sessionId)) # EDB for Encode, Din, ServiceDiscoveryRequest
print("responding " + prettyHexMessage(msg)) self.addToTrace("responding " + prettyHexMessage(msg))
self.Tcp.transmit(msg) self.Tcp.transmit(msg)
self.enterState(stateWaitForServiceDiscoveryResponse) self.enterState(stateWaitForServiceDiscoveryResponse)
if (self.isTooLong()): if (self.isTooLong()):
@ -87,15 +90,15 @@ class fsmPev():
def stateFunctionWaitForServiceDiscoveryResponse(self): def stateFunctionWaitForServiceDiscoveryResponse(self):
if (len(self.rxData)>0): if (len(self.rxData)>0):
print("In state WaitForServiceDiscoveryResponse, received " + prettyHexMessage(self.rxData)) self.addToTrace("In state WaitForServiceDiscoveryResponse, received " + prettyHexMessage(self.rxData))
exidata = removeV2GTPHeader(self.rxData) exidata = removeV2GTPHeader(self.rxData)
self.rxData = [] self.rxData = []
strConverterResult = exiDecode(exidata, "DD") # Decode DIN strConverterResult = exiDecode(exidata, "DD") # Decode DIN
print(strConverterResult) self.addToTrace(strConverterResult)
if (strConverterResult.find("ServiceDiscoveryRes")>0): if (strConverterResult.find("ServiceDiscoveryRes")>0):
# todo: check the request content, and fill response parameters # todo: check the request content, and fill response parameters
msg = addV2GTPHeader(exiEncode("EDC_"+self.sessionId)) # EDC for Encode, Din, ServicePaymentSelection msg = addV2GTPHeader(exiEncode("EDC_"+self.sessionId)) # EDC for Encode, Din, ServicePaymentSelection
print("responding " + prettyHexMessage(msg)) self.addToTrace("responding " + prettyHexMessage(msg))
self.Tcp.transmit(msg) self.Tcp.transmit(msg)
self.enterState(stateWaitForServicePaymentSelectionResponse) self.enterState(stateWaitForServicePaymentSelectionResponse)
if (self.isTooLong()): if (self.isTooLong()):
@ -103,15 +106,15 @@ class fsmPev():
def stateFunctionWaitForServicePaymentSelectionResponse(self): def stateFunctionWaitForServicePaymentSelectionResponse(self):
if (len(self.rxData)>0): if (len(self.rxData)>0):
print("In state WaitForServicePaymentSelectionResponse, received " + prettyHexMessage(self.rxData)) self.addToTrace("In state WaitForServicePaymentSelectionResponse, received " + prettyHexMessage(self.rxData))
exidata = removeV2GTPHeader(self.rxData) exidata = removeV2GTPHeader(self.rxData)
self.rxData = [] self.rxData = []
strConverterResult = exiDecode(exidata, "DD") # Decode DIN strConverterResult = exiDecode(exidata, "DD") # Decode DIN
print(strConverterResult) self.addToTrace(strConverterResult)
if (strConverterResult.find("ServicePaymentSelectionRes")>0): if (strConverterResult.find("ServicePaymentSelectionRes")>0):
# todo: check the request content, and fill response parameters # todo: check the request content, and fill response parameters
msg = addV2GTPHeader(exiEncode("EDE_"+self.sessionId)) # EDE for Encode, Din, ChargeParameterDiscovery. We ignore Authorization, not specified in DIN. msg = addV2GTPHeader(exiEncode("EDE_"+self.sessionId)) # EDE for Encode, Din, ChargeParameterDiscovery. We ignore Authorization, not specified in DIN.
print("responding " + prettyHexMessage(msg)) self.addToTrace("responding " + prettyHexMessage(msg))
self.Tcp.transmit(msg) self.Tcp.transmit(msg)
self.enterState(stateWaitForChargeParameterDiscoveryResponse) self.enterState(stateWaitForChargeParameterDiscoveryResponse)
if (self.isTooLong()): if (self.isTooLong()):
@ -119,15 +122,15 @@ class fsmPev():
def stateFunctionWaitForChargeParameterDiscoveryResponse(self): def stateFunctionWaitForChargeParameterDiscoveryResponse(self):
if (len(self.rxData)>0): if (len(self.rxData)>0):
print("In state WaitForChargeParameterDiscoveryResponse, received " + prettyHexMessage(self.rxData)) self.addToTrace("In state WaitForChargeParameterDiscoveryResponse, received " + prettyHexMessage(self.rxData))
exidata = removeV2GTPHeader(self.rxData) exidata = removeV2GTPHeader(self.rxData)
self.rxData = [] self.rxData = []
strConverterResult = exiDecode(exidata, "DD") # Decode DIN strConverterResult = exiDecode(exidata, "DD") # Decode DIN
print(strConverterResult) self.addToTrace(strConverterResult)
if (strConverterResult.find("ChargeParameterDiscoveryRes")>0): if (strConverterResult.find("ChargeParameterDiscoveryRes")>0):
# todo: check the request content, and fill response parameters # todo: check the request content, and fill response parameters
msg = addV2GTPHeader(exiEncode("EDF_"+self.sessionId)) # EDF for Encode, Din, CableCheck msg = addV2GTPHeader(exiEncode("EDF_"+self.sessionId)) # EDF for Encode, Din, CableCheck
print("responding " + prettyHexMessage(msg)) self.addToTrace("responding " + prettyHexMessage(msg))
self.Tcp.transmit(msg) self.Tcp.transmit(msg)
self.enterState(stateWaitForCableCheckResponse) self.enterState(stateWaitForCableCheckResponse)
if (self.isTooLong()): if (self.isTooLong()):
@ -135,32 +138,32 @@ class fsmPev():
def stateFunctionWaitForCableCheckResponse(self): def stateFunctionWaitForCableCheckResponse(self):
if (len(self.rxData)>0): if (len(self.rxData)>0):
print("In state WaitForCableCheckResponse, received " + prettyHexMessage(self.rxData)) self.addToTrace("In state WaitForCableCheckResponse, received " + prettyHexMessage(self.rxData))
exidata = removeV2GTPHeader(self.rxData) exidata = removeV2GTPHeader(self.rxData)
self.rxData = [] self.rxData = []
strConverterResult = exiDecode(exidata, "DD") # Decode DIN strConverterResult = exiDecode(exidata, "DD") # Decode DIN
print(strConverterResult) self.addToTrace(strConverterResult)
if (strConverterResult.find("CableCheckRes")>0): if (strConverterResult.find("CableCheckRes")>0):
try: try:
y = json.loads(strConverterResult) y = json.loads(strConverterResult)
strResponseCode = y["ResponseCode"] strResponseCode = y["ResponseCode"]
strEVSEProcessing = y["EVSEProcessing"] strEVSEProcessing = y["EVSEProcessing"]
print("[PEV] The CableCheck result is " + strResponseCode + " " + strEVSEProcessing) self.addToTrace("[PEV] The CableCheck result is " + strResponseCode + " " + strEVSEProcessing)
except: except:
print("ERROR: Could not decode the CableCheckRes") self.addToTrace("ERROR: Could not decode the CableCheckRes")
# todo: check the request content, and fill response parameters # todo: check the request content, and fill response parameters
# We have two cases here: # We have two cases here:
# 1) The charger says "cable check is finished and cable ok", by setting ResponseCode=OK and EVSEProcessing=Finished. # 1) The charger says "cable check is finished and cable ok", by setting ResponseCode=OK and EVSEProcessing=Finished.
# 2) Else: The charger says "need more time or cable not ok". In this case, we just run into timeout and start from the beginning. # 2) Else: The charger says "need more time or cable not ok". In this case, we just run into timeout and start from the beginning.
if ((strEVSEProcessing==dinEVSEProcessingType_Finished) and (strResponseCode=="OK")): if ((strEVSEProcessing==dinEVSEProcessingType_Finished) and (strResponseCode=="OK")):
msg = addV2GTPHeader(exiEncode("EDG_"+self.sessionId)) # EDG for Encode, Din, PreCharge msg = addV2GTPHeader(exiEncode("EDG_"+self.sessionId)) # EDG for Encode, Din, PreCharge
print("responding " + prettyHexMessage(msg)) self.addToTrace("responding " + prettyHexMessage(msg))
self.Tcp.transmit(msg) self.Tcp.transmit(msg)
self.enterState(stateWaitForPreChargeResponse) self.enterState(stateWaitForPreChargeResponse)
else: else:
# cable check not yet finished or finished with bad result -> try again # cable check not yet finished or finished with bad result -> try again
msg = addV2GTPHeader(exiEncode("EDF_"+self.sessionId)) # EDF for Encode, Din, CableCheck msg = addV2GTPHeader(exiEncode("EDF_"+self.sessionId)) # EDF for Encode, Din, CableCheck
print("responding " + prettyHexMessage(msg)) self.addToTrace("responding " + prettyHexMessage(msg))
self.Tcp.transmit(msg) self.Tcp.transmit(msg)
if (self.isTooLong()): if (self.isTooLong()):
@ -171,17 +174,17 @@ class fsmPev():
self.DelayCycles-=1 self.DelayCycles-=1
return return
if (len(self.rxData)>0): if (len(self.rxData)>0):
print("In state WaitForPreChargeResponse, received " + prettyHexMessage(self.rxData)) self.addToTrace("In state WaitForPreChargeResponse, received " + prettyHexMessage(self.rxData))
exidata = removeV2GTPHeader(self.rxData) exidata = removeV2GTPHeader(self.rxData)
self.rxData = [] self.rxData = []
strConverterResult = exiDecode(exidata, "DD") # Decode DIN strConverterResult = exiDecode(exidata, "DD") # Decode DIN
print(strConverterResult) self.addToTrace(strConverterResult)
if (strConverterResult.find("PreChargeRes")>0): if (strConverterResult.find("PreChargeRes")>0):
# todo: check the request content, and fill response parameters # todo: check the request content, and fill response parameters
print("PreCharge aknowledge received.") self.addToTrace("PreCharge aknowledge received.")
print("As Demo, we stay in PreCharge forever.") self.addToTrace("As Demo, we stay in PreCharge forever.")
msg = addV2GTPHeader(exiEncode("EDG_"+self.sessionId)) # EDG for Encode, Din, PreCharge msg = addV2GTPHeader(exiEncode("EDG_"+self.sessionId)) # EDG for Encode, Din, PreCharge
print("responding " + prettyHexMessage(msg)) self.addToTrace("responding " + prettyHexMessage(msg))
self.Tcp.transmit(msg) self.Tcp.transmit(msg)
self.DelayCycles=15 # wait with the next evaluation approx half a second self.DelayCycles=15 # wait with the next evaluation approx half a second
if (self.isTooLong()): if (self.isTooLong()):
@ -203,7 +206,7 @@ class fsmPev():
} }
def reInit(self): def reInit(self):
print("re-initializing fsmPev") self.addToTrace("re-initializing fsmPev")
self.state = stateInitialized self.state = stateInitialized
self.cyclesInState = 0 self.cyclesInState = 0
self.rxData = [] self.rxData = []
@ -212,13 +215,14 @@ class fsmPev():
evseIp = self.addressManager.getSeccIp() # the EVSE IP address which was found out with SDP evseIp = self.addressManager.getSeccIp() # the EVSE IP address which was found out with SDP
self.Tcp.connect(evseIp, 15118) self.Tcp.connect(evseIp, 15118)
if (not self.Tcp.isConnected): if (not self.Tcp.isConnected):
print("connection failed") self.addToTrace("connection failed")
else: else:
print("connected") self.addToTrace("connected")
def __init__(self, addressManager): def __init__(self, addressManager, callbackAddToTrace):
print("initializing fsmPev") self.callbackAddToTrace = callbackAddToTrace
self.Tcp = pyPlcTcpSocket.pyPlcTcpClientSocket() self.addToTrace("initializing fsmPev")
self.Tcp = pyPlcTcpSocket.pyPlcTcpClientSocket(self.callbackAddToTrace)
self.addressManager = addressManager self.addressManager = addressManager
self.state = stateNotYetInitialized self.state = stateNotYetInitialized
self.sessionId = "DEAD55AADEAD55AA" self.sessionId = "DEAD55AADEAD55AA"
@ -231,7 +235,7 @@ class fsmPev():
#self.Tcp.mainfunction() # call the lower-level worker #self.Tcp.mainfunction() # call the lower-level worker
if (self.Tcp.isRxDataAvailable()): if (self.Tcp.isRxDataAvailable()):
self.rxData = self.Tcp.getRxData() self.rxData = self.Tcp.getRxData()
#print("received " + prettyHexMessage(self.rxData)) #self.addToTrace("received " + prettyHexMessage(self.rxData))
# run the state machine: # run the state machine:
self.cyclesInState += 1 # for timeout handling, count how long we are in a state self.cyclesInState += 1 # for timeout handling, count how long we are in a state
self.stateFunctions[self.state](self) self.stateFunctions[self.state](self)

View file

@ -35,6 +35,8 @@
import pcap import pcap
import pyPlcIpv6 import pyPlcIpv6
import udplog
import time
from helpers import * # prettyMac etc from helpers import * # prettyMac etc
from pyPlcModes import * from pyPlcModes import *
@ -918,8 +920,10 @@ class pyPlcHomeplug():
self.pevSequenceDelayCycles=30 self.pevSequenceDelayCycles=30
self.enterState(STATE_WAITING_FOR_RESTART2) self.enterState(STATE_WAITING_FOR_RESTART2)
return return
# The EVSE modem is present. # The EVSE modem is present (or we are simulating)
self.addToTrace("[PEVSLAC] EVSE is up, pairing successful.") self.addToTrace("[PEVSLAC] EVSE is up, pairing successful.")
if (self.isSimulationMode):
self.addToTrace("[PEVSLAC] But this is only simulated.")
self.nEvseModemMissingCounter=0 self.nEvseModemMissingCounter=0
# The AVLN is established, we have at least two modems in the network. # The AVLN is established, we have at least two modems in the network.
# If we did not SDP up to now, let's do it. # If we did not SDP up to now, let's do it.
@ -992,7 +996,10 @@ class pyPlcHomeplug():
self.iAmEvse = 0 # not emulating a charging station self.iAmEvse = 0 # not emulating a charging station
self.iAmPev = 0 # not emulating a vehicle self.iAmPev = 0 # not emulating a vehicle
self.ipv6.enterListenMode() self.ipv6.enterListenMode()
self.showStatus("LISTEN mode", "mode") self.showStatus("LISTEN mode", "mode")
def printToUdp(self, s):
self.udplog.log(s)
def __init__(self, callbackAddToTrace=None, callbackShowStatus=None, mode=C_LISTEN_MODE, addrMan=None, callbackReadyForTcp=None, isSimulationMode=0): def __init__(self, callbackAddToTrace=None, callbackShowStatus=None, mode=C_LISTEN_MODE, addrMan=None, callbackReadyForTcp=None, isSimulationMode=0):
self.mytransmitbuffer = bytearray("Hallo das ist ein Test", 'UTF-8') self.mytransmitbuffer = bytearray("Hallo das ist ein Test", 'UTF-8')
@ -1033,6 +1040,10 @@ class pyPlcHomeplug():
self.runningCounter=0 self.runningCounter=0
self.ipv6 = pyPlcIpv6.ipv6handler(self.transmit, self.addressManager) self.ipv6 = pyPlcIpv6.ipv6handler(self.transmit, self.addressManager)
self.ipv6.ownMac = self.myMAC self.ipv6.ownMac = self.myMAC
self.udplog = udplog.udplog(self.transmit, self.addressManager)
for k in range(0, 10):
self.udplog.log("Test message number " + str(k))
time.sleep(0.1)
if (mode == C_LISTEN_MODE): if (mode == C_LISTEN_MODE):
self.enterListenMode() self.enterListenMode()
if (mode == C_EVSE_MODE): if (mode == C_EVSE_MODE):

View file

@ -17,20 +17,24 @@ import os
import subprocess import subprocess
class pyPlcTcpClientSocket(): class pyPlcTcpClientSocket():
def __init__(self): def __init__(self, callbackAddToTrace):
self.callbackAddToTrace = callbackAddToTrace
self.sock = socket.socket(socket.AF_INET6, socket.SOCK_STREAM) self.sock = socket.socket(socket.AF_INET6, socket.SOCK_STREAM)
self.isConnected = False self.isConnected = False
self.rxData = [] self.rxData = []
def addToTrace(self, s):
self.callbackAddToTrace(s)
def connect(self, host, port): def connect(self, host, port):
try: try:
print("connecting to " + str(host) + " port " + str(port) + "...") self.addToTrace("connecting to " + str(host) + " port " + str(port) + "...")
# for connecting, we are still in blocking-mode because # for connecting, we are still in blocking-mode because
# otherwise we run into error "[Errno 10035] A non-blocking socket operation could not be completed immediately" # otherwise we run into error "[Errno 10035] A non-blocking socket operation could not be completed immediately"
# We set a shorter timeout, so we do not block too long if the connection is not established: # We set a shorter timeout, so we do not block too long if the connection is not established:
print("step1") #print("step1")
self.sock.settimeout(0.5) self.sock.settimeout(0.5)
print("step2") #print("step2")
# https://stackoverflow.com/questions/71022092/python3-socket-program-udp-ipv6-bind-function-with-link-local-ip-address-gi # https://stackoverflow.com/questions/71022092/python3-socket-program-udp-ipv6-bind-function-with-link-local-ip-address-gi
# While on Windows10 just connecting to a remote link-local-address works, under # While on Windows10 just connecting to a remote link-local-address works, under
@ -41,25 +45,25 @@ class pyPlcTcpClientSocket():
# host = "fe80::c690:83f3:fbcb:980e%eth0" # ok with socket.getaddrinfo # host = "fe80::c690:83f3:fbcb:980e%eth0" # ok with socket.getaddrinfo
if (os.name != 'nt'): if (os.name != 'nt'):
# We are at the Raspberry # We are at the Raspberry
print(host[0:5].lower()) #print(host[0:5].lower())
if (host[0:5].lower()=="fe80:"): if (host[0:5].lower()=="fe80:"):
print("This is a link local address. We need to add %eth0 at the end.") #print("This is a link local address. We need to add %eth0 at the end.")
host = host + "%eth0" host = host + "%eth0"
socket_addr = socket.getaddrinfo(host,port,socket.AF_INET6,socket.SOCK_DGRAM,socket.SOL_UDP)[0][4] socket_addr = socket.getaddrinfo(host,port,socket.AF_INET6,socket.SOCK_DGRAM,socket.SOL_UDP)[0][4]
print(socket_addr) #print(socket_addr)
print("step2b") #print("step2b")
# https://stackoverflow.com/questions/5358021/establishing-an-ipv6-connection-using-sockets-in-python # https://stackoverflow.com/questions/5358021/establishing-an-ipv6-connection-using-sockets-in-python
# (address, port, flow info, scope id) 4-tuple for AF_INET6 # (address, port, flow info, scope id) 4-tuple for AF_INET6
# On Raspberry, the ScopeId is important. Just giving 0 leads to "invalid argument" in case # On Raspberry, the ScopeId is important. Just giving 0 leads to "invalid argument" in case
# of link-local ip-address. # of link-local ip-address.
#self.sock.connect((host, port, 0, 0)) #self.sock.connect((host, port, 0, 0))
self.sock.connect(socket_addr) self.sock.connect(socket_addr)
print("step3") #print("step3")
self.sock.setblocking(0) # make this socket non-blocking, so that the recv function will immediately return self.sock.setblocking(0) # make this socket non-blocking, so that the recv function will immediately return
self.isConnected = True self.isConnected = True
except socket.error as e: except socket.error as e:
print("connection failed", e) self.addToTrace("connection failed", e)
self.isConnected = False self.isConnected = False
def transmit(self, msg): def transmit(self, msg):
@ -73,7 +77,7 @@ class pyPlcTcpClientSocket():
sent = self.sock.send(msg[totalsent:]) sent = self.sock.send(msg[totalsent:])
if sent == 0: if sent == 0:
self.isConnected = False self.isConnected = False
print("socket connection broken") self.addToTrace("socket connection broken")
return -1 return -1
totalsent = totalsent + sent totalsent = totalsent + sent
except: except:
@ -116,7 +120,8 @@ class pyPlcTcpClientSocket():
return d return d
class pyPlcTcpServerSocket(): class pyPlcTcpServerSocket():
def __init__(self): def __init__(self, callbackAddToTrace):
self.callbackAddToTrace = callbackAddToTrace
# Todo: find the link-local IPv6 address automatically. # Todo: find the link-local IPv6 address automatically.
#self.ipAdress = 'fe80::e0ad:99ac:52eb:85d3' #self.ipAdress = 'fe80::e0ad:99ac:52eb:85d3'
#self.ipAdress = 'fe80::c690:83f3:fbcb:980e%15' #self.ipAdress = 'fe80::c690:83f3:fbcb:980e%15'
@ -133,19 +138,22 @@ class pyPlcTcpServerSocket():
self.ourSocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self.ourSocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
self.ourSocket.bind((self.ipAdress, self.tcpPort)) self.ourSocket.bind((self.ipAdress, self.tcpPort))
self.ourSocket.listen(1) self.ourSocket.listen(1)
print("pyPlcTcpSocket listening on port " + str(self.tcpPort)) self.addToTrace("pyPlcTcpSocket listening on port " + str(self.tcpPort))
hostname=socket.gethostname() hostname=socket.gethostname()
IPAddr=socket.gethostbyname(hostname) IPAddr=socket.gethostbyname(hostname)
addressInfo = socket.getaddrinfo(hostname, None, socket.AF_INET6) addressInfo = socket.getaddrinfo(hostname, None, socket.AF_INET6)
#print("Your Computer Name is:"+hostname) #print("Your Computer Name is:"+hostname)
print("The socket is linked the following IP addresses:") self.addToTrace("The socket is linked the following IP addresses:")
for i in range(0, len(addressInfo)): for i in range(0, len(addressInfo)):
#fe80::4c46:fea5:b6c9:25a9 #fe80::4c46:fea5:b6c9:25a9
IPv6Addr = addressInfo[i][4][0] IPv6Addr = addressInfo[i][4][0]
print(IPv6Addr) self.addToTrace(IPv6Addr)
self.read_list = [self.ourSocket] self.read_list = [self.ourSocket]
self.rxData = [] self.rxData = []
def addToTrace(self, s):
self.callbackAddToTrace(s)
def isRxDataAvailable(self): def isRxDataAvailable(self):
return (len(self.rxData)>0) return (len(self.rxData)>0)
@ -167,7 +175,7 @@ class pyPlcTcpServerSocket():
while totalsent < MSGLEN: while totalsent < MSGLEN:
sent = self.read_list[1].send(txMessage[totalsent:]) sent = self.read_list[1].send(txMessage[totalsent:])
if sent == 0: if sent == 0:
print("socket connection broken") self.addToTrace("socket connection broken")
return -1 return -1
totalsent = totalsent + sent totalsent = totalsent + sent
return 0 # success return 0 # success
@ -184,7 +192,7 @@ class pyPlcTcpServerSocket():
client_socket, address = self.ourSocket.accept() client_socket, address = self.ourSocket.accept()
# and we append this new socket to the list of sockets, which in the next loop will be handled by the select. # and we append this new socket to the list of sockets, which in the next loop will be handled by the select.
self.read_list.append(client_socket) self.read_list.append(client_socket)
print("Connection from", address) self.addToTrace("Connection from", address)
else: else:
# It is not the "listener socket", it is an above created "client socket" for talking with a client. # It is not the "listener socket", it is an above created "client socket" for talking with a client.
# Let's take the data from it: # Let's take the data from it:
@ -198,7 +206,7 @@ class pyPlcTcpServerSocket():
# print("received data:", data) # print("received data:", data)
self.rxData = data self.rxData = data
else: else:
print("connection closed") self.addToTrace("connection closed")
s.close() s.close()
self.read_list.remove(s) self.read_list.remove(s)

View file

@ -23,28 +23,33 @@ class pyPlcWorker():
self.callbackShowStatus = callbackShowStatus self.callbackShowStatus = callbackShowStatus
self.oldAvlnStatus = 0 self.oldAvlnStatus = 0
self.isSimulationMode = isSimulationMode self.isSimulationMode = isSimulationMode
self.hp = pyPlcHomeplug.pyPlcHomeplug(self.callbackAddToTrace, self.callbackShowStatus, self.mode, self.addressManager, self.callbackReadyForTcp, self.isSimulationMode) self.hp = pyPlcHomeplug.pyPlcHomeplug(self.workerAddToTrace, self.callbackShowStatus, self.mode, self.addressManager, self.callbackReadyForTcp, self.isSimulationMode)
self.hp.printToUdp("pyPlcWorker init")
if (self.mode == C_EVSE_MODE): if (self.mode == C_EVSE_MODE):
self.evse = fsmEvse.fsmEvse() self.evse = fsmEvse.fsmEvse(self.workerAddToTrace)
if (self.mode == C_PEV_MODE): if (self.mode == C_PEV_MODE):
self.pev = fsmPev.fsmPev(self.addressManager) self.pev = fsmPev.fsmPev(self.addressManager, self.workerAddToTrace)
def addToTrace(self, s): def workerAddToTrace(self, s):
self.callbackAddToTrace(s) # The central logging function. All logging messages from the different parts of the project
# shall come here.
#print("workerAddToTrace " + s)
self.callbackAddToTrace(s) # give the message to the upper level, eg for console log.
self.hp.printToUdp(s) # give the message to the udp for remote logging.
def showStatus(self, s, selection = ""): def showStatus(self, s, selection = ""):
self.callbackShowStatus(s, selection) self.callbackShowStatus(s, selection)
def callbackReadyForTcp(self, status): def callbackReadyForTcp(self, status):
if (status==1): if (status==1):
print("[PLCWORKER] Network is established, ready for TCP.") self.workerAddToTrace("[PLCWORKER] Network is established, ready for TCP.")
if (self.oldAvlnStatus==0): if (self.oldAvlnStatus==0):
self.oldAvlnStatus = 1 self.oldAvlnStatus = 1
if (self.mode == C_PEV_MODE): if (self.mode == C_PEV_MODE):
self.pev.reInit() self.pev.reInit()
else: else:
print("[PLCWORKER] no network") self.workerAddToTrace("[PLCWORKER] no network")
self.oldAvlnStatus = 0 self.oldAvlnStatus = 0
def mainfunction(self): def mainfunction(self):
@ -67,7 +72,7 @@ class pyPlcWorker():
self.hp.enterPevMode() self.hp.enterPevMode()
if (not hasattr(self, 'pev')): if (not hasattr(self, 'pev')):
print("creating pev") print("creating pev")
self.pev = fsmPev.fsmPev(self.addressManager) self.pev = fsmPev.fsmPev(self.addressManager, self.workerAddToTrace)
self.pev.reInit() self.pev.reInit()
if (strAction == "E"): if (strAction == "E"):
print("switching to EVSE mode") print("switching to EVSE mode")
@ -78,7 +83,7 @@ class pyPlcWorker():
self.hp.enterEvseMode() self.hp.enterEvseMode()
if (not hasattr(self, 'evse')): if (not hasattr(self, 'evse')):
print("creating fsmEvse") print("creating fsmEvse")
self.evse = fsmEvse.fsmEvse() self.evse = fsmEvse.fsmEvse(self.workerAddToTrace)
self.evse.reInit() self.evse.reInit()
if (strAction == "L"): if (strAction == "L"):
print("switching to LISTEN mode") print("switching to LISTEN mode")

90
udplog.py Executable file
View file

@ -0,0 +1,90 @@
# This module "prints" log messages to UDP broadcasts to port 514.
from helpers import prettyMac
class udplog():
def fillMac(self, macbytearray, position=6): # position 6 is the source MAC
for i in range(0, 6):
self.EthTxFrame[6+i] = macbytearray[i]
def log(self, s):
strLevel="<15>"
strMessage=s+"\0"
lenPayLoad = 4 + len(strMessage)
buffer=bytearray(lenPayLoad+28)
# syslog level (4 characters)
for i in range(0, len(strLevel)):
buffer[28+i] = ord(strLevel[i])
# the message, terminated by 00
for i in range(0, len(strMessage)):
buffer[32+i] = ord(strMessage[i])
buffer[0] = 0x45 # IP header len
buffer[1] = 0
iplen=len(buffer) # including IP header and all payload
buffer[2] = iplen >> 8
buffer[3] = (iplen & 0xff)
ipid = 0x8a9f
buffer[4] = ipid >> 8
buffer[5] = (ipid & 0xff)
fragoffset = 0
buffer[6] = fragoffset >> 8
buffer[7] = (fragoffset & 0xff)
buffer[8] = 0x80 # ttl
buffer[9] = 0x11 # proto
checksum = 0
buffer[10] = checksum >> 8
buffer[11] = (checksum & 0xff)
# source ip 4 bytes
buffer[12] = 192 # does this really matter?
buffer[13] = 168
buffer[14] = 2
buffer[15] = 222
# destination ip 4 bytes: broadcast
buffer[16] = 0xff
buffer[17] = 0xff
buffer[18] = 0xff
buffer[19] = 0xff
# source port
buffer[20] = 0xff
buffer[21] = 0x95
# destination port
buffer[22] = 0x02
buffer[23] = 0x02
udplen = lenPayLoad + 8 # payload plus 8 byte udp header
buffer[24] = udplen >> 8
buffer[25] = (udplen & 0xff)
udpchecksum = 0
buffer[26] = udpchecksum >> 8
buffer[27] = (udpchecksum & 0xff)
# packs the IP packet into an ethernet packet
self.EthTxFrame = bytearray(len(buffer) + 6 + 6 + 2) # Ethernet header needs 14 bytes:
# 6 bytes destination MAC
# 6 bytes source MAC
# 2 bytes EtherType
# fill the destination MAC with broadcast MAC
self.EthTxFrame[0] = 0xFF
self.EthTxFrame[1] = 0xFF
self.EthTxFrame[2] = 0xFF
self.EthTxFrame[3] = 0xFF
self.EthTxFrame[4] = 0xFF
self.EthTxFrame[5] = 0xFF
self.fillMac(self.ownMac) # bytes 6 to 11 are the source MAC
self.EthTxFrame[12] = 0x08 # 0800 is IPv4
self.EthTxFrame[13] = 0x00
for i in range(0, len(buffer)):
self.EthTxFrame[14+i] = buffer[i]
self.transmit(self.EthTxFrame)
def __init__(self, transmitCallback, addressManager):
self.transmit = transmitCallback
self.addressManager = addressManager
self.ownMac = self.addressManager.getLocalMacAddress()
print("udplog started with ownMac " + prettyMac(self.ownMac))