mirror of
https://github.com/uhi22/pyPLC.git
synced 2024-11-10 01:05:42 +00:00
Logging to UDP broadcast port 514
This commit is contained in:
parent
15111915ec
commit
43d96f333e
6 changed files with 213 additions and 91 deletions
58
fsmEvse.py
58
fsmEvse.py
|
@ -20,8 +20,11 @@ stateWaitForPreChargeRequest = 7
|
|||
stateWaitForPowerDeliveryRequest = 8
|
||||
|
||||
class fsmEvse():
|
||||
def addToTrace(self, s):
|
||||
self.callbackAddToTrace(s)
|
||||
|
||||
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.cyclesInState = 0
|
||||
|
||||
|
@ -32,31 +35,31 @@ class fsmEvse():
|
|||
|
||||
def stateFunctionWaitForSupportedApplicationProtocolRequest(self):
|
||||
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)
|
||||
self.rxData = []
|
||||
strConverterResult = exiDecode(exidata, "DH") # Decode Handshake-request
|
||||
print(strConverterResult)
|
||||
self.addToTrace(strConverterResult)
|
||||
if (strConverterResult.find("ProtocolNamespace=urn:din")>0):
|
||||
# todo: of course we should care for schemaID and prio also here
|
||||
print("Detected DIN")
|
||||
self.addToTrace("Detected DIN")
|
||||
# Eh for encode handshake, SupportedApplicationProtocolResponse
|
||||
msg = addV2GTPHeader(exiEncode("Eh"))
|
||||
print("responding " + prettyHexMessage(msg))
|
||||
self.addToTrace("responding " + prettyHexMessage(msg))
|
||||
self.Tcp.transmit(msg)
|
||||
self.enterState(stateWaitForSessionSetupRequest)
|
||||
|
||||
def stateFunctionWaitForSessionSetupRequest(self):
|
||||
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)
|
||||
self.rxData = []
|
||||
strConverterResult = exiDecode(exidata, "DD")
|
||||
print(strConverterResult)
|
||||
self.addToTrace(strConverterResult)
|
||||
if (strConverterResult.find("SessionSetupReq")>0):
|
||||
# todo: check the request content, and fill response parameters
|
||||
msg = addV2GTPHeader(exiEncode("EDa")) # EDa for Encode, Din, SessionSetupResponse
|
||||
print("responding " + prettyHexMessage(msg))
|
||||
self.addToTrace("responding " + prettyHexMessage(msg))
|
||||
self.Tcp.transmit(msg)
|
||||
self.enterState(stateWaitForServiceDiscoveryRequest)
|
||||
if (self.isTooLong()):
|
||||
|
@ -64,15 +67,15 @@ class fsmEvse():
|
|||
|
||||
def stateFunctionWaitForServiceDiscoveryRequest(self):
|
||||
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)
|
||||
self.rxData = []
|
||||
strConverterResult = exiDecode(exidata, "DD")
|
||||
print(strConverterResult)
|
||||
self.addToTrace(strConverterResult)
|
||||
if (strConverterResult.find("ServiceDiscoveryReq")>0):
|
||||
# todo: check the request content, and fill response parameters
|
||||
msg = addV2GTPHeader(exiEncode("EDb")) # EDb for Encode, Din, ServiceDiscoveryResponse
|
||||
print("responding " + prettyHexMessage(msg))
|
||||
self.addToTrace("responding " + prettyHexMessage(msg))
|
||||
self.Tcp.transmit(msg)
|
||||
self.enterState(stateWaitForServicePaymentSelectionRequest)
|
||||
if (self.isTooLong()):
|
||||
|
@ -80,15 +83,15 @@ class fsmEvse():
|
|||
|
||||
def stateFunctionWaitForServicePaymentSelectionRequest(self):
|
||||
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)
|
||||
self.rxData = []
|
||||
strConverterResult = exiDecode(exidata, "DD")
|
||||
print(strConverterResult)
|
||||
self.addToTrace(strConverterResult)
|
||||
if (strConverterResult.find("ServicePaymentSelectionReq")>0):
|
||||
# todo: check the request content, and fill response parameters
|
||||
msg = addV2GTPHeader(exiEncode("EDc")) # EDc for Encode, Din, ServicePaymentSelectionResponse
|
||||
print("responding " + prettyHexMessage(msg))
|
||||
self.addToTrace("responding " + prettyHexMessage(msg))
|
||||
self.Tcp.transmit(msg)
|
||||
self.enterState(stateWaitForFlexibleRequest) # todo: not clear, what is specified. The Ioniq sends PowerDeliveryReq as next.
|
||||
if (self.isTooLong()):
|
||||
|
@ -96,40 +99,40 @@ class fsmEvse():
|
|||
|
||||
def stateFunctionWaitForFlexibleRequest(self):
|
||||
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)
|
||||
self.rxData = []
|
||||
strConverterResult = exiDecode(exidata, "DD")
|
||||
print(strConverterResult)
|
||||
self.addToTrace(strConverterResult)
|
||||
if (strConverterResult.find("PowerDeliveryReq")>0):
|
||||
# todo: check the request content, and fill response parameters
|
||||
msg = addV2GTPHeader(exiEncode("EDh")) # EDh for Encode, Din, PowerDeliveryResponse
|
||||
print("responding " + prettyHexMessage(msg))
|
||||
self.addToTrace("responding " + prettyHexMessage(msg))
|
||||
self.Tcp.transmit(msg)
|
||||
self.enterState(stateWaitForFlexibleRequest) # todo: not clear, what is specified in DIN
|
||||
if (strConverterResult.find("ChargeParameterDiscoveryReq")>0):
|
||||
# todo: check the request content, and fill response parameters
|
||||
msg = addV2GTPHeader(exiEncode("EDe")) # EDe for Encode, Din, ChargeParameterDiscoveryResponse
|
||||
print("responding " + prettyHexMessage(msg))
|
||||
self.addToTrace("responding " + prettyHexMessage(msg))
|
||||
self.Tcp.transmit(msg)
|
||||
self.enterState(stateWaitForFlexibleRequest) # todo: not clear, what is specified in DIN
|
||||
if (strConverterResult.find("CableCheckReq")>0):
|
||||
# todo: check the request content, and fill response parameters
|
||||
msg = addV2GTPHeader(exiEncode("EDf")) # EDf for Encode, Din, CableCheckResponse
|
||||
print("responding " + prettyHexMessage(msg))
|
||||
self.addToTrace("responding " + prettyHexMessage(msg))
|
||||
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
|
||||
strPresentVoltage = "345"
|
||||
msg = addV2GTPHeader(exiEncode("EDg_"+strPresentVoltage)) # EDg for Encode, Din, PreChargeResponse
|
||||
print("responding " + prettyHexMessage(msg))
|
||||
self.addToTrace("responding " + prettyHexMessage(msg))
|
||||
self.Tcp.transmit(msg)
|
||||
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
|
||||
print("responding " + prettyHexMessage(msg))
|
||||
self.addToTrace("responding " + prettyHexMessage(msg))
|
||||
self.Tcp.transmit(msg)
|
||||
self.enterState(stateWaitForFlexibleRequest) # todo: not clear, what is specified in DIN
|
||||
|
||||
|
@ -152,8 +155,8 @@ class fsmEvse():
|
|||
|
||||
def stateFunctionWaitForPowerDeliveryRequest(self):
|
||||
if (len(self.rxData)>0):
|
||||
print("In state WaitForPowerDeliveryRequest, received " + prettyHexMessage(self.rxData))
|
||||
print("Todo: Reaction in state WaitForPowerDeliveryRequest is not implemented yet.")
|
||||
self.addToTrace("In state WaitForPowerDeliveryRequest, received " + prettyHexMessage(self.rxData))
|
||||
self.addToTrace("Todo: Reaction in state WaitForPowerDeliveryRequest is not implemented yet.")
|
||||
self.rxData = []
|
||||
self.enterState(0)
|
||||
if (self.isTooLong()):
|
||||
|
@ -173,13 +176,14 @@ class fsmEvse():
|
|||
}
|
||||
|
||||
def reInit(self):
|
||||
print("re-initializing fsmEvse")
|
||||
self.addToTrace("re-initializing fsmEvse")
|
||||
self.state = 0
|
||||
self.cyclesInState = 0
|
||||
self.rxData = []
|
||||
|
||||
def __init__(self):
|
||||
print("initializing fsmEvse")
|
||||
def __init__(self, callbackAddToTrace):
|
||||
self.callbackAddToTrace = callbackAddToTrace
|
||||
self.addToTrace("initializing fsmEvse")
|
||||
self.Tcp = pyPlcTcpSocket.pyPlcTcpServerSocket()
|
||||
self.state = 0
|
||||
self.cyclesInState = 0
|
||||
|
@ -189,7 +193,7 @@ class fsmEvse():
|
|||
self.Tcp.mainfunction() # call the lower-level worker
|
||||
if (self.Tcp.isRxDataAvailable()):
|
||||
self.rxData = self.Tcp.getRxData()
|
||||
#print("received " + str(self.rxData))
|
||||
#self.addToTrace("received " + str(self.rxData))
|
||||
# run the state machine:
|
||||
self.cyclesInState += 1 # for timeout handling, count how long we are in a state
|
||||
self.stateFunctions[self.state](self)
|
||||
|
|
74
fsmPev.py
74
fsmPev.py
|
@ -26,6 +26,9 @@ dinEVSEProcessingType_Finished = "0"
|
|||
dinEVSEProcessingType_Ongoing = "1"
|
||||
|
||||
class fsmPev():
|
||||
def addToTrace(self, s):
|
||||
self.callbackAddToTrace(s)
|
||||
|
||||
def enterState(self, n):
|
||||
print("from " + str(self.state) + " entering " + str(n))
|
||||
self.state = n
|
||||
|
@ -48,15 +51,15 @@ class fsmPev():
|
|||
|
||||
def stateFunctionWaitForSupportedApplicationProtocolResponse(self):
|
||||
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)
|
||||
self.rxData = []
|
||||
strConverterResult = exiDecode(exidata, "Dh") # Decode Handshake-response
|
||||
print(strConverterResult)
|
||||
self.addToTrace(strConverterResult)
|
||||
if (strConverterResult.find("supportedAppProtocolRes")>0):
|
||||
# todo: check the request content, and fill response parameters
|
||||
msg = addV2GTPHeader(exiEncode("EDA")) # EDA for Encode, Din, SessionSetupReq
|
||||
print("responding " + prettyHexMessage(msg))
|
||||
self.addToTrace("responding " + prettyHexMessage(msg))
|
||||
self.Tcp.transmit(msg)
|
||||
self.enterState(stateWaitForSessionSetupResponse)
|
||||
if (self.isTooLong()):
|
||||
|
@ -64,22 +67,22 @@ class fsmPev():
|
|||
|
||||
def stateFunctionWaitForSessionSetupResponse(self):
|
||||
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)
|
||||
self.rxData = []
|
||||
strConverterResult = exiDecode(exidata, "DD") # Decode DIN
|
||||
print(strConverterResult)
|
||||
self.addToTrace(strConverterResult)
|
||||
if (strConverterResult.find("SessionSetupRes")>0):
|
||||
# todo: check the request content, and fill response parameters
|
||||
try:
|
||||
y = json.loads(strConverterResult)
|
||||
strSessionId = y["header.SessionID"]
|
||||
print("[PEV] The Evse decided for SessionId " + strSessionId)
|
||||
self.addToTrace("[PEV] The Evse decided for SessionId " + strSessionId)
|
||||
self.sessionId = strSessionId
|
||||
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
|
||||
print("responding " + prettyHexMessage(msg))
|
||||
self.addToTrace("responding " + prettyHexMessage(msg))
|
||||
self.Tcp.transmit(msg)
|
||||
self.enterState(stateWaitForServiceDiscoveryResponse)
|
||||
if (self.isTooLong()):
|
||||
|
@ -87,15 +90,15 @@ class fsmPev():
|
|||
|
||||
def stateFunctionWaitForServiceDiscoveryResponse(self):
|
||||
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)
|
||||
self.rxData = []
|
||||
strConverterResult = exiDecode(exidata, "DD") # Decode DIN
|
||||
print(strConverterResult)
|
||||
self.addToTrace(strConverterResult)
|
||||
if (strConverterResult.find("ServiceDiscoveryRes")>0):
|
||||
# todo: check the request content, and fill response parameters
|
||||
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.enterState(stateWaitForServicePaymentSelectionResponse)
|
||||
if (self.isTooLong()):
|
||||
|
@ -103,15 +106,15 @@ class fsmPev():
|
|||
|
||||
def stateFunctionWaitForServicePaymentSelectionResponse(self):
|
||||
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)
|
||||
self.rxData = []
|
||||
strConverterResult = exiDecode(exidata, "DD") # Decode DIN
|
||||
print(strConverterResult)
|
||||
self.addToTrace(strConverterResult)
|
||||
if (strConverterResult.find("ServicePaymentSelectionRes")>0):
|
||||
# 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.
|
||||
print("responding " + prettyHexMessage(msg))
|
||||
self.addToTrace("responding " + prettyHexMessage(msg))
|
||||
self.Tcp.transmit(msg)
|
||||
self.enterState(stateWaitForChargeParameterDiscoveryResponse)
|
||||
if (self.isTooLong()):
|
||||
|
@ -119,15 +122,15 @@ class fsmPev():
|
|||
|
||||
def stateFunctionWaitForChargeParameterDiscoveryResponse(self):
|
||||
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)
|
||||
self.rxData = []
|
||||
strConverterResult = exiDecode(exidata, "DD") # Decode DIN
|
||||
print(strConverterResult)
|
||||
self.addToTrace(strConverterResult)
|
||||
if (strConverterResult.find("ChargeParameterDiscoveryRes")>0):
|
||||
# todo: check the request content, and fill response parameters
|
||||
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.enterState(stateWaitForCableCheckResponse)
|
||||
if (self.isTooLong()):
|
||||
|
@ -135,32 +138,32 @@ class fsmPev():
|
|||
|
||||
def stateFunctionWaitForCableCheckResponse(self):
|
||||
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)
|
||||
self.rxData = []
|
||||
strConverterResult = exiDecode(exidata, "DD") # Decode DIN
|
||||
print(strConverterResult)
|
||||
self.addToTrace(strConverterResult)
|
||||
if (strConverterResult.find("CableCheckRes")>0):
|
||||
try:
|
||||
y = json.loads(strConverterResult)
|
||||
strResponseCode = y["ResponseCode"]
|
||||
strEVSEProcessing = y["EVSEProcessing"]
|
||||
print("[PEV] The CableCheck result is " + strResponseCode + " " + strEVSEProcessing)
|
||||
self.addToTrace("[PEV] The CableCheck result is " + strResponseCode + " " + strEVSEProcessing)
|
||||
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
|
||||
# We have two cases here:
|
||||
# 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.
|
||||
if ((strEVSEProcessing==dinEVSEProcessingType_Finished) and (strResponseCode=="OK")):
|
||||
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.enterState(stateWaitForPreChargeResponse)
|
||||
else:
|
||||
# cable check not yet finished or finished with bad result -> try again
|
||||
msg = addV2GTPHeader(exiEncode("EDF_"+self.sessionId)) # EDF for Encode, Din, CableCheck
|
||||
print("responding " + prettyHexMessage(msg))
|
||||
self.addToTrace("responding " + prettyHexMessage(msg))
|
||||
self.Tcp.transmit(msg)
|
||||
|
||||
if (self.isTooLong()):
|
||||
|
@ -171,17 +174,17 @@ class fsmPev():
|
|||
self.DelayCycles-=1
|
||||
return
|
||||
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)
|
||||
self.rxData = []
|
||||
strConverterResult = exiDecode(exidata, "DD") # Decode DIN
|
||||
print(strConverterResult)
|
||||
self.addToTrace(strConverterResult)
|
||||
if (strConverterResult.find("PreChargeRes")>0):
|
||||
# todo: check the request content, and fill response parameters
|
||||
print("PreCharge aknowledge received.")
|
||||
print("As Demo, we stay in PreCharge forever.")
|
||||
self.addToTrace("PreCharge aknowledge received.")
|
||||
self.addToTrace("As Demo, we stay in PreCharge forever.")
|
||||
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.DelayCycles=15 # wait with the next evaluation approx half a second
|
||||
if (self.isTooLong()):
|
||||
|
@ -203,7 +206,7 @@ class fsmPev():
|
|||
}
|
||||
|
||||
def reInit(self):
|
||||
print("re-initializing fsmPev")
|
||||
self.addToTrace("re-initializing fsmPev")
|
||||
self.state = stateInitialized
|
||||
self.cyclesInState = 0
|
||||
self.rxData = []
|
||||
|
@ -212,13 +215,14 @@ class fsmPev():
|
|||
evseIp = self.addressManager.getSeccIp() # the EVSE IP address which was found out with SDP
|
||||
self.Tcp.connect(evseIp, 15118)
|
||||
if (not self.Tcp.isConnected):
|
||||
print("connection failed")
|
||||
self.addToTrace("connection failed")
|
||||
else:
|
||||
print("connected")
|
||||
self.addToTrace("connected")
|
||||
|
||||
def __init__(self, addressManager):
|
||||
print("initializing fsmPev")
|
||||
self.Tcp = pyPlcTcpSocket.pyPlcTcpClientSocket()
|
||||
def __init__(self, addressManager, callbackAddToTrace):
|
||||
self.callbackAddToTrace = callbackAddToTrace
|
||||
self.addToTrace("initializing fsmPev")
|
||||
self.Tcp = pyPlcTcpSocket.pyPlcTcpClientSocket(self.callbackAddToTrace)
|
||||
self.addressManager = addressManager
|
||||
self.state = stateNotYetInitialized
|
||||
self.sessionId = "DEAD55AADEAD55AA"
|
||||
|
@ -231,7 +235,7 @@ class fsmPev():
|
|||
#self.Tcp.mainfunction() # call the lower-level worker
|
||||
if (self.Tcp.isRxDataAvailable()):
|
||||
self.rxData = self.Tcp.getRxData()
|
||||
#print("received " + prettyHexMessage(self.rxData))
|
||||
#self.addToTrace("received " + prettyHexMessage(self.rxData))
|
||||
# run the state machine:
|
||||
self.cyclesInState += 1 # for timeout handling, count how long we are in a state
|
||||
self.stateFunctions[self.state](self)
|
||||
|
|
|
@ -35,6 +35,8 @@
|
|||
|
||||
import pcap
|
||||
import pyPlcIpv6
|
||||
import udplog
|
||||
import time
|
||||
from helpers import * # prettyMac etc
|
||||
from pyPlcModes import *
|
||||
|
||||
|
@ -918,8 +920,10 @@ class pyPlcHomeplug():
|
|||
self.pevSequenceDelayCycles=30
|
||||
self.enterState(STATE_WAITING_FOR_RESTART2)
|
||||
return
|
||||
# The EVSE modem is present.
|
||||
# The EVSE modem is present (or we are simulating)
|
||||
self.addToTrace("[PEVSLAC] EVSE is up, pairing successful.")
|
||||
if (self.isSimulationMode):
|
||||
self.addToTrace("[PEVSLAC] But this is only simulated.")
|
||||
self.nEvseModemMissingCounter=0
|
||||
# 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.
|
||||
|
@ -994,6 +998,9 @@ class pyPlcHomeplug():
|
|||
self.ipv6.enterListenMode()
|
||||
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):
|
||||
self.mytransmitbuffer = bytearray("Hallo das ist ein Test", 'UTF-8')
|
||||
self.nPacketsReceived = 0
|
||||
|
@ -1033,6 +1040,10 @@ class pyPlcHomeplug():
|
|||
self.runningCounter=0
|
||||
self.ipv6 = pyPlcIpv6.ipv6handler(self.transmit, self.addressManager)
|
||||
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):
|
||||
self.enterListenMode()
|
||||
if (mode == C_EVSE_MODE):
|
||||
|
|
|
@ -17,20 +17,24 @@ import os
|
|||
import subprocess
|
||||
|
||||
class pyPlcTcpClientSocket():
|
||||
def __init__(self):
|
||||
def __init__(self, callbackAddToTrace):
|
||||
self.callbackAddToTrace = callbackAddToTrace
|
||||
self.sock = socket.socket(socket.AF_INET6, socket.SOCK_STREAM)
|
||||
self.isConnected = False
|
||||
self.rxData = []
|
||||
|
||||
def addToTrace(self, s):
|
||||
self.callbackAddToTrace(s)
|
||||
|
||||
def connect(self, host, port):
|
||||
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
|
||||
# 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:
|
||||
print("step1")
|
||||
#print("step1")
|
||||
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
|
||||
# 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
|
||||
if (os.name != 'nt'):
|
||||
# We are at the Raspberry
|
||||
print(host[0:5].lower())
|
||||
#print(host[0:5].lower())
|
||||
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"
|
||||
socket_addr = socket.getaddrinfo(host,port,socket.AF_INET6,socket.SOCK_DGRAM,socket.SOL_UDP)[0][4]
|
||||
|
||||
print(socket_addr)
|
||||
print("step2b")
|
||||
#print(socket_addr)
|
||||
#print("step2b")
|
||||
# https://stackoverflow.com/questions/5358021/establishing-an-ipv6-connection-using-sockets-in-python
|
||||
# (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
|
||||
# of link-local ip-address.
|
||||
#self.sock.connect((host, port, 0, 0))
|
||||
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.isConnected = True
|
||||
except socket.error as e:
|
||||
print("connection failed", e)
|
||||
self.addToTrace("connection failed", e)
|
||||
self.isConnected = False
|
||||
|
||||
def transmit(self, msg):
|
||||
|
@ -73,7 +77,7 @@ class pyPlcTcpClientSocket():
|
|||
sent = self.sock.send(msg[totalsent:])
|
||||
if sent == 0:
|
||||
self.isConnected = False
|
||||
print("socket connection broken")
|
||||
self.addToTrace("socket connection broken")
|
||||
return -1
|
||||
totalsent = totalsent + sent
|
||||
except:
|
||||
|
@ -116,7 +120,8 @@ class pyPlcTcpClientSocket():
|
|||
return d
|
||||
|
||||
class pyPlcTcpServerSocket():
|
||||
def __init__(self):
|
||||
def __init__(self, callbackAddToTrace):
|
||||
self.callbackAddToTrace = callbackAddToTrace
|
||||
# Todo: find the link-local IPv6 address automatically.
|
||||
#self.ipAdress = 'fe80::e0ad:99ac:52eb:85d3'
|
||||
#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.bind((self.ipAdress, self.tcpPort))
|
||||
self.ourSocket.listen(1)
|
||||
print("pyPlcTcpSocket listening on port " + str(self.tcpPort))
|
||||
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)
|
||||
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)):
|
||||
#fe80::4c46:fea5:b6c9:25a9
|
||||
IPv6Addr = addressInfo[i][4][0]
|
||||
print(IPv6Addr)
|
||||
self.addToTrace(IPv6Addr)
|
||||
self.read_list = [self.ourSocket]
|
||||
self.rxData = []
|
||||
|
||||
def addToTrace(self, s):
|
||||
self.callbackAddToTrace(s)
|
||||
|
||||
def isRxDataAvailable(self):
|
||||
return (len(self.rxData)>0)
|
||||
|
||||
|
@ -167,7 +175,7 @@ class pyPlcTcpServerSocket():
|
|||
while totalsent < MSGLEN:
|
||||
sent = self.read_list[1].send(txMessage[totalsent:])
|
||||
if sent == 0:
|
||||
print("socket connection broken")
|
||||
self.addToTrace("socket connection broken")
|
||||
return -1
|
||||
totalsent = totalsent + sent
|
||||
return 0 # success
|
||||
|
@ -184,7 +192,7 @@ class pyPlcTcpServerSocket():
|
|||
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.
|
||||
self.read_list.append(client_socket)
|
||||
print("Connection from", address)
|
||||
self.addToTrace("Connection from", address)
|
||||
else:
|
||||
# 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:
|
||||
|
@ -198,7 +206,7 @@ class pyPlcTcpServerSocket():
|
|||
# print("received data:", data)
|
||||
self.rxData = data
|
||||
else:
|
||||
print("connection closed")
|
||||
self.addToTrace("connection closed")
|
||||
s.close()
|
||||
self.read_list.remove(s)
|
||||
|
||||
|
|
|
@ -23,28 +23,33 @@ class pyPlcWorker():
|
|||
self.callbackShowStatus = callbackShowStatus
|
||||
self.oldAvlnStatus = 0
|
||||
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):
|
||||
self.evse = fsmEvse.fsmEvse()
|
||||
self.evse = fsmEvse.fsmEvse(self.workerAddToTrace)
|
||||
if (self.mode == C_PEV_MODE):
|
||||
self.pev = fsmPev.fsmPev(self.addressManager)
|
||||
self.pev = fsmPev.fsmPev(self.addressManager, self.workerAddToTrace)
|
||||
|
||||
def addToTrace(self, s):
|
||||
self.callbackAddToTrace(s)
|
||||
def workerAddToTrace(self, 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 = ""):
|
||||
self.callbackShowStatus(s, selection)
|
||||
|
||||
def callbackReadyForTcp(self, status):
|
||||
if (status==1):
|
||||
print("[PLCWORKER] Network is established, ready for TCP.")
|
||||
self.workerAddToTrace("[PLCWORKER] Network is established, ready for TCP.")
|
||||
if (self.oldAvlnStatus==0):
|
||||
self.oldAvlnStatus = 1
|
||||
if (self.mode == C_PEV_MODE):
|
||||
self.pev.reInit()
|
||||
|
||||
else:
|
||||
print("[PLCWORKER] no network")
|
||||
self.workerAddToTrace("[PLCWORKER] no network")
|
||||
self.oldAvlnStatus = 0
|
||||
|
||||
def mainfunction(self):
|
||||
|
@ -67,7 +72,7 @@ class pyPlcWorker():
|
|||
self.hp.enterPevMode()
|
||||
if (not hasattr(self, 'pev')):
|
||||
print("creating pev")
|
||||
self.pev = fsmPev.fsmPev(self.addressManager)
|
||||
self.pev = fsmPev.fsmPev(self.addressManager, self.workerAddToTrace)
|
||||
self.pev.reInit()
|
||||
if (strAction == "E"):
|
||||
print("switching to EVSE mode")
|
||||
|
@ -78,7 +83,7 @@ class pyPlcWorker():
|
|||
self.hp.enterEvseMode()
|
||||
if (not hasattr(self, 'evse')):
|
||||
print("creating fsmEvse")
|
||||
self.evse = fsmEvse.fsmEvse()
|
||||
self.evse = fsmEvse.fsmEvse(self.workerAddToTrace)
|
||||
self.evse.reInit()
|
||||
if (strAction == "L"):
|
||||
print("switching to LISTEN mode")
|
||||
|
|
90
udplog.py
Executable file
90
udplog.py
Executable 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))
|
Loading…
Reference in a new issue