robustness improved

This commit is contained in:
uhi22 2023-03-18 19:14:23 +01:00
parent aab380706f
commit 0edea0bc25
5 changed files with 65 additions and 9 deletions

View file

@ -28,6 +28,8 @@ class fsmEvse():
def enterState(self, n): def enterState(self, n):
self.addToTrace("from " + str(self.state) + " entering " + str(n)) self.addToTrace("from " + str(self.state) + " entering " + str(n))
if (self.state!=0) and (n==0):
self.publishStatus("Waiting f AppHandShake")
self.state = n self.state = n
self.cyclesInState = 0 self.cyclesInState = 0
@ -55,7 +57,7 @@ class fsmEvse():
def stateFunctionWaitForSessionSetupRequest(self): def stateFunctionWaitForSessionSetupRequest(self):
if (len(self.rxData)>0): if (len(self.rxData)>0):
self.addToTrace("In state stateFunctionWaitForSessionSetupRequest, received " + prettyHexMessage(self.rxData)) self.addToTrace("In state WaitForSessionSetupRequest, received " + prettyHexMessage(self.rxData))
exidata = removeV2GTPHeader(self.rxData) exidata = removeV2GTPHeader(self.rxData)
self.rxData = [] self.rxData = []
strConverterResult = exiDecode(exidata, "DD") strConverterResult = exiDecode(exidata, "DD")
@ -213,6 +215,21 @@ class fsmEvse():
self.state = 0 self.state = 0
self.cyclesInState = 0 self.cyclesInState = 0
self.rxData = [] self.rxData = []
self.Tcp.resetTheConnection()
def socketStateNotification(self, notification):
if (notification==0):
# The TCP informs us, that the connection is broken.
# Let's restart the state machine.
self.publishStatus("TCP conn broken")
self.addToTrace("re-initializing fsmEvse due to broken connection")
self.reInit()
if (notification==1):
# The TCP informs us, that it is listening, means waiting for incoming connection.
self.publishStatus("Listening TCP")
if (notification==2):
# The TCP informs us, that a connection is established.
self.publishStatus("TCP connected")
def __init__(self, addressManager, callbackAddToTrace, hardwareInterface, callbackShowStatus): def __init__(self, addressManager, callbackAddToTrace, hardwareInterface, callbackShowStatus):
self.callbackAddToTrace = callbackAddToTrace self.callbackAddToTrace = callbackAddToTrace
@ -224,7 +241,7 @@ class fsmEvse():
if (self.faultInjectionDelayUntilSocketOpen_s>0): if (self.faultInjectionDelayUntilSocketOpen_s>0):
self.addToTrace("Fault injection: waiting " + str(self.faultInjectionDelayUntilSocketOpen_s) + " s until opening the TCP socket.") self.addToTrace("Fault injection: waiting " + str(self.faultInjectionDelayUntilSocketOpen_s) + " s until opening the TCP socket.")
time.sleep(self.faultInjectionDelayUntilSocketOpen_s) time.sleep(self.faultInjectionDelayUntilSocketOpen_s)
self.Tcp = pyPlcTcpSocket.pyPlcTcpServerSocket(self.callbackAddToTrace) self.Tcp = pyPlcTcpSocket.pyPlcTcpServerSocket(self.callbackAddToTrace, self.socketStateNotification)
self.state = 0 self.state = 0
self.cyclesInState = 0 self.cyclesInState = 0
self.rxData = [] self.rxData = []

View file

@ -93,6 +93,9 @@ lblUInlet.pack()
lblMode = tk.Label(root, text="(mode)") lblMode = tk.Label(root, text="(mode)")
lblMode.pack() lblMode.pack()
if (myMode != C_PEV_MODE):
lblUInlet['text']= ""
nKeystrokes=0 nKeystrokes=0
# Bind the keyboard handler to all relevant elements: # Bind the keyboard handler to all relevant elements:
root.bind('<Key>', storekeyname) root.bind('<Key>', storekeyname)

View file

@ -630,6 +630,7 @@ class pyPlcHomeplug():
self.showStatus(prettyMac(self.pevMac), "pevmac") self.showStatus(prettyMac(self.pevMac), "pevmac")
# If we want to emulate an EVSE, we want to answer. # If we want to emulate an EVSE, we want to answer.
if (self.iAmEvse==1): if (self.iAmEvse==1):
self.showStatus("SLAC started", "evseState")
self.composeSlacParamCnf() self.composeSlacParamCnf()
self.addToTrace("[EVSE] transmitting CM_SLAC_PARAM.CNF") self.addToTrace("[EVSE] transmitting CM_SLAC_PARAM.CNF")
self.sniffer.sendpacket(bytes(self.mytransmitbuffer)) self.sniffer.sendpacket(bytes(self.mytransmitbuffer))
@ -648,6 +649,7 @@ class pyPlcHomeplug():
# to answer with a ATTEN_CHAR.IND, which normally contains the attenuation for 10 sounds, 58 groups. # to answer with a ATTEN_CHAR.IND, which normally contains the attenuation for 10 sounds, 58 groups.
self.addToTrace("received MNBC_SOUND.IND") self.addToTrace("received MNBC_SOUND.IND")
if (self.iAmEvse==1): if (self.iAmEvse==1):
self.showStatus("SLAC 2", "evseState")
countdown = self.myreceivebuffer[38] countdown = self.myreceivebuffer[38]
if (countdown == 0): if (countdown == 0):
self.composeAttenCharInd() self.composeAttenCharInd()
@ -678,6 +680,7 @@ class pyPlcHomeplug():
# If we are EVSE, we send the response. # If we are EVSE, we send the response.
self.addToTrace("received SLAC_MATCH.REQ") self.addToTrace("received SLAC_MATCH.REQ")
if (self.iAmEvse==1): if (self.iAmEvse==1):
self.showStatus("SLAC match", "evseState")
self.composeSlacMatchCnf() self.composeSlacMatchCnf()
self.addToTrace("[EVSE] transmitting SLAC_MATCH.CNF") self.addToTrace("[EVSE] transmitting SLAC_MATCH.CNF")
self.sniffer.sendpacket(bytes(self.mytransmitbuffer)) self.sniffer.sendpacket(bytes(self.mytransmitbuffer))
@ -1069,7 +1072,7 @@ class pyPlcHomeplug():
self.evseMac = [0x55, 0x56, 0x57, 0xAA, 0xAA, 0xAA ] # a default evse MAC. Will be overwritten later. self.evseMac = [0x55, 0x56, 0x57, 0xAA, 0xAA, 0xAA ] # a default evse MAC. Will be overwritten later.
self.myMAC = self.addressManager.getLocalMacAddress() self.myMAC = self.addressManager.getLocalMacAddress()
self.runningCounter=0 self.runningCounter=0
self.ipv6 = pyPlcIpv6.ipv6handler(self.transmit, self.addressManager) self.ipv6 = pyPlcIpv6.ipv6handler(self.transmit, self.addressManager, self.callbackShowStatus)
self.ipv6.ownMac = self.myMAC self.ipv6.ownMac = self.myMAC
self.udplog = udplog.udplog(self.transmit, self.addressManager) self.udplog = udplog.udplog(self.transmit, self.addressManager)
self.udplog.log("Test message to verify the syslog. pyPlcHomeplug.py is in the init function.") self.udplog.log("Test message to verify the syslog. pyPlcHomeplug.py is in the init function.")

View file

@ -144,8 +144,8 @@ class ipv6handler():
if ((self.destinationport == 15118) or (self.sourceport == 15118)): # port for the SECC if ((self.destinationport == 15118) or (self.sourceport == 15118)): # port for the SECC
if ((self.udpPayload[0]==0x01) and (self.udpPayload[1]==0xFE)): # protocol version 1 and inverted if ((self.udpPayload[0]==0x01) and (self.udpPayload[1]==0xFE)): # protocol version 1 and inverted
# it is a V2GTP message # it is a V2GTP message
if (self.iAmEvse): if (self.iAmEvse) and (self.destinationport == 15118):
# if we are the charger, lets save the cars IP for later use. # if we are the charger, and it is a message from car to charger, lets save the cars IP for later use.
self.EvccIp = self.sourceIp self.EvccIp = self.sourceIp
self.addressManager.setPevIp(self.EvccIp) self.addressManager.setPevIp(self.EvccIp)
showAsHex(self.udpPayload, "V2GTP ") showAsHex(self.udpPayload, "V2GTP ")
@ -170,6 +170,7 @@ class ipv6handler():
# This was a valid SDP request. Let's respond, if we are the charger. # This was a valid SDP request. Let's respond, if we are the charger.
if (self.iAmEvse==1): if (self.iAmEvse==1):
print("Ok, this was a valid SDP request. We are the SECC. Sending SDP response.") print("Ok, this was a valid SDP request. We are the SECC. Sending SDP response.")
self.callbackShowStatus("SDP 2", "evseState")
self.sendSdpResponse() self.sendSdpResponse()
else: else:
print("v2gptPayloadLen on SDP request is " + str(v2gptPayloadLen) + " not supported") print("v2gptPayloadLen on SDP request is " + str(v2gptPayloadLen) + " not supported")
@ -353,11 +354,12 @@ class ipv6handler():
if (self.nextheader == 0x06): # it is an TCP frame if (self.nextheader == 0x06): # it is an TCP frame
self.evaluateTcpPacket() self.evaluateTcpPacket()
def __init__(self, transmitCallback, addressManager): def __init__(self, transmitCallback, addressManager, callbackShowStatus):
self.enterEvseMode() self.enterEvseMode()
#self.enterListenMode() #self.enterListenMode()
self.transmit = transmitCallback self.transmit = transmitCallback
self.addressManager = addressManager self.addressManager = addressManager
self.callbackShowStatus = callbackShowStatus
#self.exiLogFile = open('SnifferExiLog.txt', 'w') #self.exiLogFile = open('SnifferExiLog.txt', 'w')
# 16 bytes, a default IPv6 address for the charging station # 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 ] # self.SeccIp = [ 0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0x06, 0xaa, 0xaa, 0xff, 0xfe, 0, 0xaa, 0xaa ]

View file

@ -144,8 +144,9 @@ class pyPlcTcpClientSocket():
return d return d
class pyPlcTcpServerSocket(): class pyPlcTcpServerSocket():
def __init__(self, callbackAddToTrace): def __init__(self, callbackAddToTrace, callbackStateNotification):
self.callbackAddToTrace = callbackAddToTrace self.callbackAddToTrace = callbackAddToTrace
self.callbackStateNotification = callbackStateNotification
# 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'
@ -162,6 +163,29 @@ 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)
self.callbackStateNotification(1) # inform the higher level state machines, that TCP is listening
self.addToTrace("pyPlcTcpSocket listening on port " + str(self.tcpPort))
hostname=socket.gethostname()
IPAddr=socket.gethostbyname(hostname)
addressInfo = socket.getaddrinfo(hostname, None, socket.AF_INET6)
#print("Your Computer Name is:"+hostname)
self.addToTrace("The socket is linked the following IP addresses:")
for i in range(0, len(addressInfo)):
#fe80::4c46:fea5:b6c9:25a9
IPv6Addr = addressInfo[i][4][0]
self.addToTrace(IPv6Addr)
self.read_list = [self.ourSocket]
self.rxData = []
def resetTheConnection(self):
# in case of a broken connection, here we try to start it again
self.addToTrace("Trying to reset the TCP socket")
# Todo: how to "reset" the socket?
self.ourSocket = socket.socket(socket.AF_INET6, socket.SOCK_STREAM, 0)
self.ourSocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
self.ourSocket.bind((self.ipAdress, self.tcpPort))
self.ourSocket.listen(1)
self.callbackStateNotification(1) # inform the higher level state machines, that TCP is listening
self.addToTrace("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)
@ -194,10 +218,12 @@ class pyPlcTcpServerSocket():
return -1 return -1
# Simplification: We will send to the FIRST open connection, even we would have more connections open. This is # Simplification: We will send to the FIRST open connection, even we would have more connections open. This is
# ok, because in our use case we have exactly one client. # ok, because in our use case we have exactly one client.
# Improvement: Instead of using the first (oldest(?)) connection, lets use the last. This helps for the case, that one
# connection has crashed and the charger makes a new connection.
totalsent = 0 totalsent = 0
MSGLEN = len(txMessage) MSGLEN = len(txMessage)
while totalsent < MSGLEN: while totalsent < MSGLEN:
sent = self.read_list[1].send(txMessage[totalsent:]) sent = self.read_list[numberOfSockets-1].send(txMessage[totalsent:])
if sent == 0: if sent == 0:
self.addToTrace("socket connection broken") self.addToTrace("socket connection broken")
return -1 return -1
@ -217,6 +243,7 @@ class pyPlcTcpServerSocket():
# 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)
self.addToTrace("Connection from " + str(address)) self.addToTrace("Connection from " + str(address))
self.callbackStateNotification(2) # inform the higher level state machines, that the connection is established.
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:
@ -232,7 +259,11 @@ class pyPlcTcpServerSocket():
else: else:
self.addToTrace("connection closed") self.addToTrace("connection closed")
s.close() s.close()
self.callbackStateNotification(0) # inform the higher level state machines, that the connection is gone.
try:
self.read_list.remove(s) self.read_list.remove(s)
except:
pass