From 6231dc546896c6649ee4f54912f947981cdc9edd Mon Sep 17 00:00:00 2001 From: uhi22 Date: Thu, 4 May 2023 19:08:58 +0200 Subject: [PATCH] feature: prepared connector locking and safe-shutdown --- fsmPev.py | 109 ++++++++++++++++++++++++++++++++++--------- hardwareInterface.py | 11 +++++ readme.md | 4 ++ 3 files changed, 103 insertions(+), 21 deletions(-) diff --git a/fsmPev.py b/fsmPev.py index 4ac53d2..367d4c7 100644 --- a/fsmPev.py +++ b/fsmPev.py @@ -22,16 +22,21 @@ stateWaitForServiceDiscoveryResponse = 5 stateWaitForServicePaymentSelectionResponse = 6 stateWaitForContractAuthenticationResponse = 7 stateWaitForChargeParameterDiscoveryResponse = 8 -stateWaitForCableCheckResponse = 9 -stateWaitForPreChargeResponse = 10 -stateWaitForContactorsClosed = 11 -stateWaitForPowerDeliveryResponse = 12 -stateWaitForCurrentDemandResponse = 13 -stateWaitForWeldingDetectionResponse = 14 -stateWaitForSessionStopResponse = 15 -stateChargingFinished = 16 +stateWaitForConnectorLock = 9 +stateWaitForCableCheckResponse = 10 +stateWaitForPreChargeResponse = 11 +stateWaitForContactorsClosed = 12 +stateWaitForPowerDeliveryResponse = 13 +stateWaitForCurrentDemandResponse = 14 +stateWaitForWeldingDetectionResponse = 15 +stateWaitForSessionStopResponse = 16 +stateChargingFinished = 17 stateUnrecoverableError = 88 stateSequenceTimeout = 99 +stateSafeShutDownWaitForChargerShutdown = 111 +stateSafeShutDownWaitForContactorsOpen = 222 +stateEnd = 1000 + dinEVSEProcessingType_Finished = "0" @@ -79,6 +84,8 @@ class fsmPev(): s = "WaitForContractAuthenticationResponse" if (statenumber == stateWaitForChargeParameterDiscoveryResponse): s = "WaitForChargeParameterDiscoveryResponse" + if (statenumber == stateWaitForConnectorLock): + s = "WaitForConnectorLock" if (statenumber == stateWaitForCableCheckResponse): s = "WaitForCableCheckResponse" if (statenumber == stateWaitForPreChargeResponse): @@ -99,6 +106,12 @@ class fsmPev(): s = "UnrecoverableError" if (statenumber == stateSequenceTimeout): s = "SequenceTimeout" + if (statenumber == stateSafeShutDownWaitForChargerShutdown): + s = "SafeShutDownWaitForChargerShutdown" + if (statenumber == stateSafeShutDownWaitForContactorsOpen): + s = "SafeShutDownWaitForContactorsOpen" + if (statenumber == stateEnd): + s = "End" return s def sendChargeParameterDiscoveryReq(self): @@ -313,12 +326,12 @@ class fsmPev(): # (B) The charger finished to tell the charge parameters. if (strConverterResult.find('"EVSEProcessing": "Finished"')>0): self.publishStatus("ChargeParams discovered") - self.addToTrace("Checkpoint550: It is Finished. Will change to state C and send CableCheckReq.") + self.addToTrace("Checkpoint550: ChargeParams are discovered. Will change to state C.") # pull the CP line to state C here: self.hardwareInterface.setStateC() - self.sendCableCheckReq() - self.numberOfCableCheckReq = 1 # This is the first request. - self.enterState(stateWaitForCableCheckResponse) + self.addToTrace("Checkpoint555: Locking the connector.") + self.hardwareInterface.triggerConnectorLocking() + self.enterState(stateWaitForConnectorLock) else: # Not (yet) finished. if (self.numberOfChargeParameterDiscoveryReq>=20): # approx 20 seconds, should be sufficient for the charger to find its parameters... @@ -335,6 +348,15 @@ class fsmPev(): if (self.isTooLong()): self.enterState(stateSequenceTimeout) + def stateFunctionWaitForConnectorLock(self): + if (self.hardwareInterface.isConnectorLocked()): + self.addToTrace("Checkpoint560: Connector Lock confirmed. Will send CableCheckReq.") + self.sendCableCheckReq() + self.numberOfCableCheckReq = 1 # This is the first request. + self.enterState(stateWaitForCableCheckResponse) + if (self.isTooLong()): + self.enterState(stateSequenceTimeout) + def stateFunctionWaitForCableCheckResponse(self): if (self.cyclesInState<30): # The first second in the state just do nothing. return @@ -482,6 +504,10 @@ class fsmPev(): else: # We requested "OFF". So we turn-off the Relay and continue with the Welding detection. self.publishStatus("PwrDelvry OFF success") + self.addToTrace("Checkpoint806: PowerDelivery Off confirmed.") + self.addToTrace("Checkpoint810: Changing CP line to State B.") + # set the CP line to B + self.hardwareInterface.setStateB() self.addToTrace("Turning off the relay and starting the WeldingDetection") self.hardwareInterface.setPowerRelayOff() self.hardwareInterface.setRelay2Off() @@ -584,26 +610,63 @@ class fsmPev(): # Todo: close the TCP connection here. # Todo: Unlock the connector lock. self.publishStatus("Stopped normally") - self.hardwareInterface.setStateB() 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 - + # charging is finished. + # Finally unlock the connector + self.addToTrace("Charging successfully finished. Unlocking the connector") + self.hardwareInterface.triggerConnectorUnlocking() + self.enterState(stateEnd) + def stateFunctionSequenceTimeout(self): - # Here we end, if we run into a timeout in the state machine. This is an error case, and - # an end of the PEV state machine. The re-initialization is performed by the - # lower layers SLAC, SDP, together with the connection manager. Nothing to do here. + # Here we end, if we run into a timeout in the state machine. self.publishStatus("ERROR Timeout") + # Initiate the safe-shutdown-sequence. + self.addToTrace("Safe-shutdown-sequence: setting state B") + self.hardwareInterface.setStateB() # setting CP line to B disables in the charger the current flow. + self.DelayCycles = 66 # 66*30ms=2s for charger shutdown + self.enterState(stateSafeShutDownWaitForChargerShutdown) def stateFunctionUnrecoverableError(self): # Here we end, if the EVSE reported an error code, which terminates the charging session. - # This is an end of the PEV state machine. The re-init is performed by the lower layers. Nothing more to do here. self.publishStatus("ERROR reported") + # Initiate the safe-shutdown-sequence. + self.addToTrace("Safe-shutdown-sequence: setting state B") + self.hardwareInterface.setStateB() # setting CP line to B disables in the charger the current flow. + self.DelayCycles = 66 # 66*30ms=2s for charger shutdown + self.enterState(stateSafeShutDownWaitForChargerShutdown) + + def stateFunctionSafeShutDownWaitForChargerShutdown(self): + # wait state, to give the charger the time to stop the current. + if (self.DelayCycles>0): + self.DelayCycles-=1 + return + # Now the current flow is stopped by the charger. We can safely open the contactors: + self.addToTrace("Safe-shutdown-sequence: opening contactors") + self.hardwareInterface.setPowerRelayOff() + self.hardwareInterface.setRelay2Off() + self.DelayCycles = 33 # 33*30ms=1s for opening the contactors + self.enterState(stateSafeShutDownWaitForContactorsOpen) + + def stateFunctionSafeShutDownWaitForContactorsOpen(self): + # wait state, to give the contactors the time to open. + if (self.DelayCycles>0): + self.DelayCycles-=1 + return + # Finally, when we have no current and no voltage, unlock the connector + self.addToTrace("Safe-shutdown-sequence: unlocking the connector") + self.hardwareInterface.triggerConnectorUnlocking() + # This is the end of the safe-shutdown-sequence. + self.enterState(stateEnd) + + def stateFunctionEnd(self): + # Just stay here, until we get re-initialized after a new SLAC/SDP. + pass + stateFunctions = { stateNotYetInitialized: stateFunctionNotYetInitialized, @@ -615,6 +678,7 @@ class fsmPev(): stateWaitForServicePaymentSelectionResponse: stateFunctionWaitForServicePaymentSelectionResponse, stateWaitForContractAuthenticationResponse: stateFunctionWaitForContractAuthenticationResponse, stateWaitForChargeParameterDiscoveryResponse: stateFunctionWaitForChargeParameterDiscoveryResponse, + stateWaitForConnectorLock: stateFunctionWaitForConnectorLock, stateWaitForCableCheckResponse: stateFunctionWaitForCableCheckResponse, stateWaitForPreChargeResponse: stateFunctionWaitForPreChargeResponse, stateWaitForContactorsClosed: stateFunctionWaitForContactorsClosed, @@ -624,7 +688,10 @@ class fsmPev(): stateWaitForSessionStopResponse: stateFunctionWaitForSessionStopResponse, stateChargingFinished: stateFunctionChargingFinished, stateUnrecoverableError: stateFunctionUnrecoverableError, - stateSequenceTimeout: stateFunctionSequenceTimeout + stateSequenceTimeout: stateFunctionSequenceTimeout, + stateSafeShutDownWaitForChargerShutdown: stateFunctionSafeShutDownWaitForChargerShutdown, + stateSafeShutDownWaitForContactorsOpen: stateFunctionSafeShutDownWaitForContactorsOpen, + stateEnd: stateFunctionEnd } def stopCharging(self): diff --git a/hardwareInterface.py b/hardwareInterface.py index ecb4595..525f83b 100644 --- a/hardwareInterface.py +++ b/hardwareInterface.py @@ -91,6 +91,17 @@ class hardwareInterface(): def getPowerRelayConfirmation(self): return 1 # todo: self.contactor_confirmed + + def triggerConnectorLocking(self): + self.addToTrace("Locking the connector") + # todo control the lock motor into lock direction until the end (time based or current based stopping?) + + def triggerConnectorUnlocking(self): + self.addToTrace("Unocking the connector") + # todo control the lock motor into unlock direction until the end (time based or current based stopping?) + + def isConnectorLocked(self): + return 1 # todo: use the real connector lock feedback def getInletVoltage(self): # uncomment this line, to take the simulated inlet voltage instead of the really measured diff --git a/readme.md b/readme.md index 8524ebc..4ac0af0 100644 --- a/readme.md +++ b/readme.md @@ -311,6 +311,8 @@ DC or AC and which power pins are used. The car announces the maximum current li * Checkpoint545: The charger confirms with ChargeParameterResponse. The contains the limits from charger side, e.g. min and max voltage, min and max current. Now, the initialization phase of the charging session is finished. * Checkpoint550: The car changes to CP State to C or D, by applying an additional resistor between CP and ground. +* Checkpoint555: The car controls the connector lock motor into direction 'lock'. +* Checkpoint556: The car checks whether the connector lock is confirmed. * Checkpoint560: The car sends CableCheckRequest. This contains the information, whether the connector is locked. * Checkpoint561: The charger applies voltage to the cable and measures the isolation resistance. * Checkpoint565: The charger confirms with CableCheckResponse. @@ -333,6 +335,8 @@ is active (current limitation, voltage limitation, power limitation). * Checkpoint710: The CurrentDemandRequest/CurrentDemandResponse are repeated during the charging. * Checkpoint800: When the end of charging is decided (battery full or user wish), the car sends PowerDelivery(Stop)Request. * Checkpoint805: The charger confirms with PowerDeliveryResponse. +* Checkpoint806: The car receives the PowerDeliveryResponse. +* Checkpoint810: The car changes the CP line from StateC to StateB. (according to Figure 107) * Checkpoint850: The car sends WeldingDetectionRequest. * Checkpoint855: The charger confirms with WeldingDetectionResponse. * Checkpoint900: The car sends SessionStopRequest.