logging to PevExiLog and interpretation afterwards

This commit is contained in:
uhi22 2022-12-09 12:06:10 +01:00
parent 8e16179372
commit 82f347c788
8 changed files with 92 additions and 39 deletions

3
.gitignore vendored
View file

@ -1,3 +1,4 @@
/__pycache__/
/doc/*.jar
*.bat
*.bat
PevExiLog*.txt

View file

@ -230,7 +230,7 @@ def testDecoder(strHex, pre="DH", comment=""):
print("---***!!!FAIL!!!***---")
nFail+=1
def testReadExiFromFile():
def testReadExiFromSnifferFile():
file1 = open('results\\tmp.txt', 'r')
Lines = file1.readlines()
for myLine in Lines:
@ -242,6 +242,24 @@ def testReadExiFromFile():
#print(s)
testDecoder(s, "DD", "")
def testReadExiFromExiLogFile():
file1 = open('PevExiLog.txt', 'r')
fileOut = open('PevExiLog_decoded.txt', 'w')
# example: "ED 809a02004080c1014181c210b8"
Lines = file1.readlines()
for myLine in Lines:
if (myLine[1:3]=="D "): # it is DIN
posOfSpace=2
s = myLine[posOfSpace+1:] # The part after the " " contains the EXI hex data.
s = s.replace(" ", "") # Remove blanks
s = s.replace("\n", "") # Remove line feeds
#print(s)
decoded=exiDecode(s, "DD")
print(decoded)
print(myLine.replace("\n", "") + " means:", file=fileOut)
print(decoded, file=fileOut)
fileOut.close()
def testTimeConsumption():
strHex = "809a001150400000c80006400000"
pre = "DD"
@ -268,8 +286,8 @@ if __name__ == "__main__":
if (False):
testTimeConsumption()
exit()
if (False):
testReadExiFromFile()
if (True):
testReadExiFromExiLogFile()
exit()
if (False):

View file

@ -6,7 +6,7 @@
import pyPlcTcpSocket
import time # for time.sleep()
from helpers import prettyHexMessage
from helpers import prettyHexMessage, compactHexMessage
from exiConnector import * # for EXI data handling/converting
import json
@ -38,6 +38,18 @@ class fsmPev():
def addToTrace(self, s):
self.callbackAddToTrace("[PEV] " + s)
def exiDecode(self, exidata, schema):
s = compactHexMessage(exidata)
self.exiLogFile.write(schema + " " + s +"\n") # write the EXI data to the exiLogFile
return exiDecode(exidata, schema) # call the decoder
def exiEncode(self, input):
schema = input[0:2]
exidata = exiEncode(input) # call the encoder
s = exidata # it is already a hex string
self.exiLogFile.write(schema + " " + s +"\n") # write the EXI data to the exiLogFile
return exidata
def enterState(self, n):
print("from " + str(self.state) + " entering " + str(n))
self.state = n
@ -86,12 +98,12 @@ class fsmPev():
self.addToTrace("In state WaitForSupportedApplicationProtocolResponse, received " + prettyHexMessage(self.rxData))
exidata = removeV2GTPHeader(self.rxData)
self.rxData = []
strConverterResult = exiDecode(exidata, "Dh") # Decode Handshake-response
strConverterResult = self.exiDecode(exidata, "Dh") # Decode Handshake-response
self.addToTrace(strConverterResult)
if (strConverterResult.find("supportedAppProtocolRes")>0):
# todo: check the request content, and fill response parameters
self.addToTrace("Will send SessionSetupReq")
msg = addV2GTPHeader(exiEncode("EDA")) # EDA for Encode, Din, SessionSetupReq
msg = addV2GTPHeader(self.exiEncode("EDA")) # EDA for Encode, Din, SessionSetupReq
self.addToTrace("responding " + prettyHexMessage(msg))
self.Tcp.transmit(msg)
self.enterState(stateWaitForSessionSetupResponse)
@ -103,7 +115,7 @@ class fsmPev():
self.addToTrace("In state WaitForSessionSetupResponse, received " + prettyHexMessage(self.rxData))
exidata = removeV2GTPHeader(self.rxData)
self.rxData = []
strConverterResult = exiDecode(exidata, "DD") # Decode DIN
strConverterResult = self.exiDecode(exidata, "DD") # Decode DIN
self.addToTrace(strConverterResult)
if (strConverterResult.find("SessionSetupRes")>0):
# todo: check the request content, and fill response parameters
@ -115,7 +127,7 @@ class fsmPev():
except:
self.addToTrace("ERROR: Could not decode the sessionID")
self.addToTrace("Will send ServiceDiscoveryReq")
msg = addV2GTPHeader(exiEncode("EDB_"+self.sessionId)) # EDB for Encode, Din, ServiceDiscoveryRequest
msg = addV2GTPHeader(self.exiEncode("EDB_"+self.sessionId)) # EDB for Encode, Din, ServiceDiscoveryRequest
self.addToTrace("responding " + prettyHexMessage(msg))
self.Tcp.transmit(msg)
self.enterState(stateWaitForServiceDiscoveryResponse)
@ -127,12 +139,12 @@ class fsmPev():
self.addToTrace("In state WaitForServiceDiscoveryResponse, received " + prettyHexMessage(self.rxData))
exidata = removeV2GTPHeader(self.rxData)
self.rxData = []
strConverterResult = exiDecode(exidata, "DD") # Decode DIN
strConverterResult = self.exiDecode(exidata, "DD") # Decode DIN
self.addToTrace(strConverterResult)
if (strConverterResult.find("ServiceDiscoveryRes")>0):
# todo: check the request content, and fill response parameters
self.addToTrace("Will send ServicePaymentSelectionReq")
msg = addV2GTPHeader(exiEncode("EDC_"+self.sessionId)) # EDC for Encode, Din, ServicePaymentSelection
msg = addV2GTPHeader(self.exiEncode("EDC_"+self.sessionId)) # EDC for Encode, Din, ServicePaymentSelection
self.addToTrace("responding " + prettyHexMessage(msg))
self.Tcp.transmit(msg)
self.enterState(stateWaitForServicePaymentSelectionResponse)
@ -144,12 +156,12 @@ class fsmPev():
self.addToTrace("In state WaitForServicePaymentSelectionResponse, received " + prettyHexMessage(self.rxData))
exidata = removeV2GTPHeader(self.rxData)
self.rxData = []
strConverterResult = exiDecode(exidata, "DD") # Decode DIN
strConverterResult = self.exiDecode(exidata, "DD") # Decode DIN
self.addToTrace(strConverterResult)
if (strConverterResult.find("ServicePaymentSelectionRes")>0):
# todo: check the request content, and fill response parameters
self.addToTrace("Will send ContractAuthenticationReq")
msg = addV2GTPHeader(exiEncode("EDL_"+self.sessionId)) # EDL for Encode, Din, ContractAuthenticationReq.
msg = addV2GTPHeader(self.exiEncode("EDL_"+self.sessionId)) # EDL for Encode, Din, ContractAuthenticationReq.
self.addToTrace("responding " + prettyHexMessage(msg))
self.Tcp.transmit(msg)
self.numberOfContractAuthenticationReq = 1 # This is the first request.
@ -164,7 +176,7 @@ class fsmPev():
self.addToTrace("In state WaitForContractAuthentication, received " + prettyHexMessage(self.rxData))
exidata = removeV2GTPHeader(self.rxData)
self.rxData = []
strConverterResult = exiDecode(exidata, "DD") # Decode DIN
strConverterResult = self.exiDecode(exidata, "DD") # Decode DIN
self.addToTrace(strConverterResult)
if (strConverterResult.find("ContractAuthenticationRes")>0):
# In normal case, we can have two results here: either the Authentication is needed (the user
@ -172,7 +184,7 @@ class fsmPev():
# Or, the authorization is finished. This is shown by EVSEProcessing=Finished.
if (strConverterResult.find('"EVSEProcessing": "Finished"')>0):
self.addToTrace("It is Finished. Will send ChargeParameterDiscoveryReq")
msg = addV2GTPHeader(exiEncode("EDE_"+self.sessionId)) # EDE for Encode, Din, ChargeParameterDiscovery.
msg = addV2GTPHeader(self.exiEncode("EDE_"+self.sessionId)) # EDE for Encode, Din, ChargeParameterDiscovery.
self.addToTrace("responding " + prettyHexMessage(msg))
self.Tcp.transmit(msg)
self.numberOfChargeParameterDiscoveryReq = 1 # first message
@ -186,7 +198,7 @@ class fsmPev():
# Try again.
self.numberOfContractAuthenticationReq += 1 # count the number of tries.
self.addToTrace("Not (yet) finished. Will again send ContractAuthenticationReq #" + str(self.numberOfContractAuthenticationReq))
msg = addV2GTPHeader(exiEncode("EDL_"+self.sessionId)) # EDL for Encode, Din, ContractAuthenticationReq.
msg = addV2GTPHeader(self.exiEncode("EDL_"+self.sessionId)) # EDL for Encode, Din, ContractAuthenticationReq.
self.addToTrace("responding " + prettyHexMessage(msg))
self.Tcp.transmit(msg)
# We just stay in the same state, until the timeout elapses.
@ -202,7 +214,7 @@ class fsmPev():
self.addToTrace("In state WaitForChargeParameterDiscoveryResponse, received " + prettyHexMessage(self.rxData))
exidata = removeV2GTPHeader(self.rxData)
self.rxData = []
strConverterResult = exiDecode(exidata, "DD") # Decode DIN
strConverterResult = self.exiDecode(exidata, "DD") # Decode DIN
self.addToTrace(strConverterResult)
if (strConverterResult.find("ChargeParameterDiscoveryRes")>0):
# We can have two cases here:
@ -212,7 +224,7 @@ class fsmPev():
self.addToTrace("It is Finished. Will change to state C and send CableCheckReq.")
# pull the CP line to state C here:
self.hardwareInterface.setStateC()
msg = addV2GTPHeader(exiEncode("EDF_"+self.sessionId)) # EDF for Encode, Din, CableCheck
msg = addV2GTPHeader(self.exiEncode("EDF_"+self.sessionId)) # EDF for Encode, Din, CableCheck
self.addToTrace("responding " + prettyHexMessage(msg))
self.Tcp.transmit(msg)
self.numberOfCableCheckReq = 1 # This is the first request.
@ -226,7 +238,7 @@ class fsmPev():
# Try again.
self.numberOfChargeParameterDiscoveryReq += 1 # count the number of tries.
self.addToTrace("Not (yet) finished. Will again send ChargeParameterDiscoveryReq #" + str(self.numberOfChargeParameterDiscoveryReq))
msg = addV2GTPHeader(exiEncode("EDE_"+self.sessionId)) # EDE for Encode, Din, ChargeParameterDiscovery.
msg = addV2GTPHeader(self.exiEncode("EDE_"+self.sessionId)) # EDE for Encode, Din, ChargeParameterDiscovery.
self.addToTrace("responding " + prettyHexMessage(msg))
self.Tcp.transmit(msg)
# we stay in the same state
@ -241,7 +253,7 @@ class fsmPev():
self.addToTrace("In state WaitForCableCheckResponse, received " + prettyHexMessage(self.rxData))
exidata = removeV2GTPHeader(self.rxData)
self.rxData = []
strConverterResult = exiDecode(exidata, "DD") # Decode DIN
strConverterResult = self.exiDecode(exidata, "DD") # Decode DIN
self.addToTrace(strConverterResult)
if (strConverterResult.find("CableCheckRes")>0):
try:
@ -258,7 +270,7 @@ class fsmPev():
if ((strEVSEProcessing=="Finished") and (strResponseCode=="OK")):
self.addToTrace("The EVSE says that the CableCheck is finished and ok.")
self.addToTrace("Will send PreChargeReq")
msg = addV2GTPHeader(exiEncode("EDG_"+self.sessionId)) # EDG for Encode, Din, PreCharge
msg = addV2GTPHeader(self.exiEncode("EDG_"+self.sessionId)) # EDG for Encode, Din, PreCharge
self.addToTrace("responding " + prettyHexMessage(msg))
self.Tcp.transmit(msg)
self.enterState(stateWaitForPreChargeResponse)
@ -270,7 +282,7 @@ class fsmPev():
# cable check not yet finished or finished with bad result -> try again
self.numberOfCableCheckReq += 1
self.addToTrace("Will again send CableCheckReq")
msg = addV2GTPHeader(exiEncode("EDF_"+self.sessionId)) # EDF for Encode, Din, CableCheck
msg = addV2GTPHeader(self.exiEncode("EDF_"+self.sessionId)) # EDF for Encode, Din, CableCheck
self.addToTrace("responding " + prettyHexMessage(msg))
self.Tcp.transmit(msg)
# stay in the same state
@ -287,7 +299,7 @@ class fsmPev():
self.addToTrace("In state WaitForPreChargeResponse, received " + prettyHexMessage(self.rxData))
exidata = removeV2GTPHeader(self.rxData)
self.rxData = []
strConverterResult = exiDecode(exidata, "DD") # Decode DIN
strConverterResult = self.exiDecode(exidata, "DD") # Decode DIN
self.addToTrace(strConverterResult)
if (strConverterResult.find("PreChargeRes")>0):
# todo: check the request content, and fill response parameters
@ -295,7 +307,7 @@ class fsmPev():
if (abs(self.hardwareInterface.getInletVoltage()-self.hardwareInterface.getAccuVoltage()) < PARAM_U_DELTA_MAX_FOR_END_OF_PRECHARGE):
self.addToTrace("Difference between accu voltage and inlet voltage is small. Sending PowerDeliveryReq.")
self.hardwareInterface.setPowerRelayOn()
msg = addV2GTPHeader(exiEncode("EDH_"+self.sessionId+"_"+"1")) # EDH for Encode, Din, PowerDeliveryReq, ON
msg = addV2GTPHeader(self.exiEncode("EDH_"+self.sessionId+"_"+"1")) # EDH for Encode, Din, PowerDeliveryReq, ON
self.wasPowerDeliveryRequestedOn=True
self.addToTrace("responding " + prettyHexMessage(msg))
self.Tcp.transmit(msg)
@ -303,7 +315,7 @@ class fsmPev():
else:
self.addToTrace("Difference too big. Continuing PreCharge.")
#self.addToTrace("As Demo, we stay in PreCharge forever.")
msg = addV2GTPHeader(exiEncode("EDG_"+self.sessionId)) # EDG for Encode, Din, PreCharge
msg = addV2GTPHeader(self.exiEncode("EDG_"+self.sessionId)) # EDG for Encode, Din, PreCharge
self.addToTrace("responding " + prettyHexMessage(msg))
self.Tcp.transmit(msg)
self.DelayCycles=15 # wait with the next evaluation approx half a second
@ -315,12 +327,12 @@ class fsmPev():
self.addToTrace("In state WaitForPowerDeliveryRes, received " + prettyHexMessage(self.rxData))
exidata = removeV2GTPHeader(self.rxData)
self.rxData = []
strConverterResult = exiDecode(exidata, "DD") # Decode DIN
strConverterResult = self.exiDecode(exidata, "DD") # Decode DIN
self.addToTrace(strConverterResult)
if (strConverterResult.find("PowerDeliveryRes")>0):
if (self.wasPowerDeliveryRequestedOn):
self.addToTrace("Starting the charging loop with CurrentDemandReq")
msg = addV2GTPHeader(exiEncode("EDI_"+self.sessionId)) # EDI for Encode, Din, CurrentDemandReq
msg = addV2GTPHeader(self.exiEncode("EDI_"+self.sessionId)) # EDI for Encode, Din, CurrentDemandReq
self.addToTrace("responding " + prettyHexMessage(msg))
self.Tcp.transmit(msg)
self.enterState(stateWaitForCurrentDemandResponse)
@ -328,7 +340,7 @@ class fsmPev():
# We requested "OFF". So we turn-off the Relay and continue with the Welding detection.
self.addToTrace("Turning off the relay and starting the WeldingDetection")
self.hardwareInterface.setPowerRelayOff()
msg = addV2GTPHeader(exiEncode("EDJ_"+self.sessionId)) # EDI for Encode, Din, WeldingDetectionReq
msg = addV2GTPHeader(self.exiEncode("EDJ_"+self.sessionId)) # EDI for Encode, Din, WeldingDetectionReq
self.addToTrace("responding " + prettyHexMessage(msg))
self.Tcp.transmit(msg)
self.enterState(stateWaitForWeldingDetectionResponse)
@ -340,20 +352,20 @@ class fsmPev():
self.addToTrace("In state WaitForCurrentDemandRes, received " + prettyHexMessage(self.rxData))
exidata = removeV2GTPHeader(self.rxData)
self.rxData = []
strConverterResult = exiDecode(exidata, "DD") # Decode DIN
strConverterResult = self.exiDecode(exidata, "DD") # Decode DIN
self.addToTrace(strConverterResult)
if (strConverterResult.find("CurrentDemandRes")>0):
# as long as the accu is not full and no stop-demand from the user, we continue charging
if (self.hardwareInterface.getIsAccuFull()):
self.addToTrace("Accu is full. Sending PowerDeliveryReq Stop.")
msg = addV2GTPHeader(exiEncode("EDH_"+self.sessionId+"_"+"0")) # EDH for Encode, Din, PowerDeliveryReq, OFF
msg = addV2GTPHeader(self.exiEncode("EDH_"+self.sessionId+"_"+"0")) # EDH for Encode, Din, PowerDeliveryReq, OFF
self.wasPowerDeliveryRequestedOn=False
self.addToTrace("responding " + prettyHexMessage(msg))
self.Tcp.transmit(msg)
self.enterState(stateWaitForPowerDeliveryResponse)
else:
# continue charging loop
msg = addV2GTPHeader(exiEncode("EDI_"+self.sessionId)) # EDI for Encode, Din, CurrentDemandReq
msg = addV2GTPHeader(self.exiEncode("EDI_"+self.sessionId)) # EDI for Encode, Din, CurrentDemandReq
self.addToTrace("responding " + prettyHexMessage(msg))
self.Tcp.transmit(msg)
self.enterState(stateWaitForCurrentDemandResponse)
@ -366,11 +378,11 @@ class fsmPev():
self.addToTrace("In state WaitForWeldingDetectionResponse, received " + prettyHexMessage(self.rxData))
exidata = removeV2GTPHeader(self.rxData)
self.rxData = []
strConverterResult = exiDecode(exidata, "DD") # Decode DIN
strConverterResult = self.exiDecode(exidata, "DD") # Decode DIN
self.addToTrace(strConverterResult)
if (strConverterResult.find("WeldingDetectionRes")>0):
self.addToTrace("Sending SessionStopReq")
msg = addV2GTPHeader(exiEncode("EDK_"+self.sessionId)) # EDI for Encode, Din, SessionStopReq
msg = addV2GTPHeader(self.exiEncode("EDK_"+self.sessionId)) # EDI for Encode, Din, SessionStopReq
self.addToTrace("responding " + prettyHexMessage(msg))
self.Tcp.transmit(msg)
self.enterState(stateWaitForSessionStopResponse)
@ -382,7 +394,7 @@ class fsmPev():
self.addToTrace("In state WaitForSessionStopRes, received " + prettyHexMessage(self.rxData))
exidata = removeV2GTPHeader(self.rxData)
self.rxData = []
strConverterResult = exiDecode(exidata, "DD") # Decode DIN
strConverterResult = self.exiDecode(exidata, "DD") # Decode DIN
self.addToTrace(strConverterResult)
if (strConverterResult.find("SessionStopRes")>0):
# req -508
@ -439,6 +451,8 @@ class fsmPev():
def __init__(self, addressManager, callbackAddToTrace, hardwareInterface):
self.callbackAddToTrace = callbackAddToTrace
self.addToTrace("initializing fsmPev")
self.exiLogFile = open('PevExiLog.txt', 'a')
self.exiLogFile.write("init\n")
self.Tcp = pyPlcTcpSocket.pyPlcTcpClientSocket(self.callbackAddToTrace)
self.addressManager = addressManager
self.hardwareInterface = hardwareInterface
@ -449,6 +463,10 @@ class fsmPev():
self.rxData = []
# we do NOT call the reInit, because we want to wait with the connection until external trigger comes
def __del__(self):
self.exiLogFile.write("closing\n")
self.exiLogFile.close()
def mainfunction(self):
#self.Tcp.mainfunction() # call the lower-level worker
if (self.Tcp.isRxDataAvailable()):

View file

@ -18,6 +18,13 @@ def prettyHexMessage(mybytearray, description=""):
strHex = strHex + twoCharHex(mybytearray[i]) + " "
return description + "(" + str(packetlength) + "bytes) = " + strHex
def compactHexMessage(mybytearray):
packetlength = len(mybytearray)
strHex = ""
for i in range(0, packetlength):
strHex = strHex + twoCharHex(mybytearray[i])
return strHex
def prettyMac(macByteArray):
s=""
length = len(macByteArray)

View file

@ -86,5 +86,6 @@ while lastKey!="x":
# print(str(nMainloops) + " " + str(nKeystrokes)) # show something in the console window
root.update()
worker.mainfunction()
del(worker)
#---------------------------------------------------------------

View file

@ -751,7 +751,7 @@ class pyPlcHomeplug():
self.pevSequenceCyclesInState+=1
if (self.pevSequenceState==STATE_INITIAL): # Initial state.
# In real life we would check whether we see 5% PWM on the pilot line. We skip this check.
self.isSimulationMode = 0
self.isSimulationMode = self.isForcedSimulationMode # from command line, we can force the simulation mode
self.isSDPDone = 0
self.numberOfFoundModems = 0
self.localModemFound = 0
@ -1034,7 +1034,8 @@ class pyPlcHomeplug():
self.pevSequenceState = 0
self.pevSequenceCyclesInState = 0
self.numberOfSoftwareVersionResponses = 0
self.isSimulationMode = isSimulationMode # simulation without homeplug modem
self.numberOfFoundModems = 0
self.isForcedSimulationMode = isSimulationMode # simulation without homeplug modem
#self.sniffer = pcap.pcap(name=None, promisc=True, immediate=True, timeout_ms=50)
# eth3 means: Third entry from back, in the list of interfaces, which is provided by pcap.findalldevs.
# Improvement necessary: select the interface based on the name.

View file

@ -307,7 +307,9 @@ class ipv6handler():
# 0x8001 EXI encoded V2G message
if (v2gptPayloadType == 0x8001):
self.ExiPacket = self.v2gframe[8:] # the exi payload, without the 8 bytes V2GTP header
print("[SNIFFER] EXI from " + str(self.tcpsourceport) + " to " + str(self.tcpdestinationport) + " " + prettyHexMessage(self.ExiPacket))
s = "[SNIFFER] EXI from " + str(self.tcpsourceport) + " to " + str(self.tcpdestinationport) + " " + prettyHexMessage(self.ExiPacket)
print(s)
# print(s, file=self.exiLogFile)
# Todo: further process the EXI packet. E.g. write it into file for offline analysis.
# And send it to decoder.
@ -316,7 +318,7 @@ class ipv6handler():
# just want to listen to the conversation of two others, and extract what we hear.
self.tcpsourceport = self.myreceivebuffer[54] * 256 + self.myreceivebuffer[55]
self.tcpdestinationport = self.myreceivebuffer[56] * 256 + self.myreceivebuffer[57]
if ((self.tcpsourceport == 15118) or (self.tcpdestinationport == 15118)):
if (True): # we do not check port, because the TCP port is variable. (self.tcpsourceport == 15118) or (self.tcpdestinationport == 15118))
if (len(self.myreceivebuffer)>=74+9): # 74 is the TCP without any data. A V2GTP has 8 bytes header, plus at least 1 payload byte.
startOfV2gtp = 74 # the index of the first V2GTP byte in the ethernet buffer
if ((self.myreceivebuffer[startOfV2gtp] == 0x01) and (self.myreceivebuffer[startOfV2gtp+1] == 0xFE)):
@ -356,6 +358,7 @@ class ipv6handler():
#self.enterListenMode()
self.transmit = transmitCallback
self.addressManager = addressManager
#self.exiLogFile = open('SnifferExiLog.txt', 'w')
# 16 bytes, a default IPv6 address for the charging station
# self.SeccIp = [ 0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0x06, 0xaa, 0xaa, 0xff, 0xfe, 0, 0xaa, 0xaa ]
# fe80::e0ad:99ac:52eb:85d3 is the Win10 laptop

View file

@ -42,7 +42,11 @@ class pyPlcWorker():
self.evse = fsmEvse.fsmEvse(self.workerAddToTrace)
if (self.mode == C_PEV_MODE):
self.pev = fsmPev.fsmPev(self.addressManager, self.workerAddToTrace, self.hardwareInterface)
def __del__(self):
if (self.mode == C_PEV_MODE):
print("worker: deleting pev")
del(self.pev)
def workerAddToTrace(self, s):
# The central logging function. All logging messages from the different parts of the project
# shall come here.