diff --git a/fsmEvse.py b/fsmEvse.py index e1b46cd..6a214cd 100644 --- a/fsmEvse.py +++ b/fsmEvse.py @@ -135,6 +135,24 @@ class fsmEvse(): self.addToTrace("responding " + prettyHexMessage(msg)) self.Tcp.transmit(msg) self.enterState(stateWaitForFlexibleRequest) # todo: not clear, what is specified in DIN + if (strConverterResult.find("CurrentDemandReq")>0): + # todo: check the request content, and fill response parameters + msg = addV2GTPHeader(exiEncode("EDi")) # EDi for Encode, Din, CurrentDemandRes + self.addToTrace("responding " + prettyHexMessage(msg)) + self.Tcp.transmit(msg) + self.enterState(stateWaitForFlexibleRequest) # todo: not clear, what is specified in DIN + if (strConverterResult.find("WeldingDetectionReq")>0): + # todo: check the request content, and fill response parameters + msg = addV2GTPHeader(exiEncode("EDj")) # EDj for Encode, Din, WeldingDetectionRes + self.addToTrace("responding " + prettyHexMessage(msg)) + self.Tcp.transmit(msg) + self.enterState(stateWaitForFlexibleRequest) # todo: not clear, what is specified in DIN + if (strConverterResult.find("SessionStopReq")>0): + # todo: check the request content, and fill response parameters + msg = addV2GTPHeader(exiEncode("EDk")) # EDk for Encode, Din, SessionStopRes + self.addToTrace("responding " + prettyHexMessage(msg)) + self.Tcp.transmit(msg) + self.enterState(stateWaitForFlexibleRequest) # todo: not clear, what is specified in DIN diff --git a/fsmPev.py b/fsmPev.py index 73b8041..0b720e6 100644 --- a/fsmPev.py +++ b/fsmPev.py @@ -10,6 +10,8 @@ from helpers import prettyHexMessage from exiConnector import * # for EXI data handling/converting import json +PARAM_U_DELTA_MAX_FOR_END_OF_PRECHARGE = 10 # volts between inlet and accu, to change from PreCharge to PowerDelivery + stateNotYetInitialized = 0 stateConnecting = 1 stateConnected = 2 @@ -22,6 +24,10 @@ stateWaitForChargeParameterDiscoveryResponse = 8 stateWaitForCableCheckResponse = 9 stateWaitForPreChargeResponse = 10 stateWaitForPowerDeliveryResponse = 11 +stateWaitForCurrentDemandResponse = 12 +stateWaitForWeldingDetectionResponse = 13 +stateWaitForSessionStopResponse = 14 +stateChargingFinished = 15 stateSequenceTimeout = 99 @@ -286,14 +292,111 @@ class fsmPev(): if (strConverterResult.find("PreChargeRes")>0): # todo: check the request content, and fill response parameters 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 - self.addToTrace("responding " + prettyHexMessage(msg)) - self.Tcp.transmit(msg) - self.DelayCycles=15 # wait with the next evaluation approx half a second + 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 + self.wasPowerDeliveryRequestedOn=True + self.addToTrace("responding " + prettyHexMessage(msg)) + self.Tcp.transmit(msg) + self.enterState(stateWaitForPowerDeliveryResponse) + 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 + self.addToTrace("responding " + prettyHexMessage(msg)) + self.Tcp.transmit(msg) + self.DelayCycles=15 # wait with the next evaluation approx half a second if (self.isTooLong()): self.enterState(stateSequenceTimeout) + def stateFunctionWaitForPowerDeliveryResponse(self): + if (len(self.rxData)>0): + self.addToTrace("In state WaitForPowerDeliveryRes, received " + prettyHexMessage(self.rxData)) + exidata = removeV2GTPHeader(self.rxData) + self.rxData = [] + strConverterResult = 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 + self.addToTrace("responding " + prettyHexMessage(msg)) + self.Tcp.transmit(msg) + self.enterState(stateWaitForCurrentDemandResponse) + else: + # 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 + self.addToTrace("responding " + prettyHexMessage(msg)) + self.Tcp.transmit(msg) + self.enterState(stateWaitForWeldingDetectionResponse) + if (self.isTooLong()): + self.enterState(stateSequenceTimeout) + + def stateFunctionWaitForCurrentDemandResponse(self): + if (len(self.rxData)>0): + self.addToTrace("In state WaitForCurrentDemandRes, received " + prettyHexMessage(self.rxData)) + exidata = removeV2GTPHeader(self.rxData) + self.rxData = [] + strConverterResult = 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 + 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 + self.addToTrace("responding " + prettyHexMessage(msg)) + self.Tcp.transmit(msg) + self.enterState(stateWaitForCurrentDemandResponse) + + if (self.isTooLong()): + self.enterState(stateSequenceTimeout) + + def stateFunctionWaitForWeldingDetectionResponse(self): + if (len(self.rxData)>0): + self.addToTrace("In state WaitForWeldingDetectionResponse, received " + prettyHexMessage(self.rxData)) + exidata = removeV2GTPHeader(self.rxData) + self.rxData = [] + strConverterResult = 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 + self.addToTrace("responding " + prettyHexMessage(msg)) + self.Tcp.transmit(msg) + self.enterState(stateWaitForSessionStopResponse) + if (self.isTooLong()): + self.enterState(stateSequenceTimeout) + + def stateFunctionWaitForSessionStopResponse(self): + if (len(self.rxData)>0): + self.addToTrace("In state WaitForSessionStopRes, received " + prettyHexMessage(self.rxData)) + exidata = removeV2GTPHeader(self.rxData) + self.rxData = [] + strConverterResult = exiDecode(exidata, "DD") # Decode DIN + self.addToTrace(strConverterResult) + if (strConverterResult.find("SessionStopRes")>0): + # req -508 + # Todo: close the TCP connection here. + # Todo: Unlock the connector lock. + self.addToTrace("Charging is finished") + self.enterState(stateChargingFinished) + if (self.isTooLong()): + self.enterState(stateSequenceTimeout) + + def stateFunctionChargingFinished(self): + # charging is finished. Nothing to do. Just stay here, until we get re-initialized after a new SLAC/SDP. + pass + def stateFunctionSequenceTimeout(self): # Here we end, if we run into a timeout in the state machine. This is an error case, and # we should re-initalize and try again to get a communication. @@ -314,13 +417,21 @@ class fsmPev(): stateWaitForChargeParameterDiscoveryResponse: stateFunctionWaitForChargeParameterDiscoveryResponse, stateWaitForCableCheckResponse: stateFunctionWaitForCableCheckResponse, stateWaitForPreChargeResponse: stateFunctionWaitForPreChargeResponse, + stateWaitForPowerDeliveryResponse: stateFunctionWaitForPowerDeliveryResponse, + stateWaitForCurrentDemandResponse: stateFunctionWaitForCurrentDemandResponse, + stateWaitForWeldingDetectionResponse: stateFunctionWaitForWeldingDetectionResponse, + stateWaitForSessionStopResponse: stateFunctionWaitForSessionStopResponse, + stateChargingFinished: stateFunctionChargingFinished, stateSequenceTimeout: stateFunctionSequenceTimeout } + + def reInit(self): self.addToTrace("re-initializing fsmPev") self.Tcp.disconnect() self.hardwareInterface.setStateB() + self.hardwareInterface.setPowerRelayOff() self.state = stateConnecting self.cyclesInState = 0 self.rxData = [] diff --git a/hardwareInterface.py b/hardwareInterface.py index 6bce233..c03ce70 100644 --- a/hardwareInterface.py +++ b/hardwareInterface.py @@ -36,16 +36,47 @@ class hardwareInterface(): def setStateB(self): self.addToTrace("Setting CP line into state B.") - self.outvalue = 0 + self.outvalue &= ~1 def setStateC(self): self.addToTrace("Setting CP line into state C.") - self.outvalue = 1 + self.outvalue |= 1 + + def setPowerRelayOn(self): + self.addToTrace("Switching PowerRelay ON.") + self.outvalue |= 2 + + def setPowerRelayOff(self): + self.addToTrace("Switching PowerRelay OFF.") + self.outvalue &= ~2 + + def getInletVoltage(self): + #todo: get real measured voltage from the inlet + self.inletVoltage = 230 + return self.inletVoltage + + def getAccuVoltage(self): + #todo: get real measured voltage from the accu + self.accuVoltage = 230 + return self.accuVoltage + + def getAccuMaxCurrent(self): + #todo: get max charging current from the BMS + self.accuMaxCurrent = 10 + return self.accuMaxCurrent + + def getIsAccuFull(self): + #todo: get "full" indication from the BMS + self.IsAccuFull = (self.simulatedSoc >= 98) + return self.IsAccuFull + def __init__(self, callbackAddToTrace=None): self.callbackAddToTrace = callbackAddToTrace self.loopcounter = 0 self.outvalue = 0 + self.simulatedSoc = 20.0 # percent + self.inletVoltage = 0.0 # volts self.findSerialPort() def close(self): @@ -53,6 +84,10 @@ class hardwareInterface(): self.ser.close() def mainfunction(self): + if (self.simulatedSoc<100): + if ((self.outvalue & 2)!=0): + # while the relay is closes, simulate increasing SOC + self.simulatedSoc = self.simulatedSoc + 0.2 self.loopcounter+=1 if (self.isInterfaceOk): if (self.loopcounter>15):