Merge pull request #3 from ArendJanKramer/master

Added SoC callback feature, fixed socket re-opening error
This commit is contained in:
uhi22 2023-05-26 07:55:42 +02:00 committed by GitHub
commit 6c5c2b6af6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 140 additions and 57 deletions

View file

@ -66,8 +66,8 @@ class addressManager():
else: else:
lines = result.stdout.split("\n") lines = result.stdout.split("\n")
for line in lines: for line in lines:
if (line.find("inet6")>0): if (line.strip().find("inet6")>0):
k = line.find(" fe80::") # the beginning of the IPv6 k = line.strip().find("fe80::") # the beginning of the IPv6
if (k>0): if (k>0):
sIpWithText = line[k+1:] sIpWithText = line[k+1:]
x = sIpWithText.find(" ") # the space is the end of the IPv6 x = sIpWithText.find(" ") # the space is the end of the IPv6

View file

@ -94,3 +94,7 @@ testsuite_enable = No
# If this is activated, the pyPlc will send all logging messages also to the network interface, # If this is activated, the pyPlc will send all logging messages also to the network interface,
# in form of UDP Syslog messages. For details see in udplog.py. # in form of UDP Syslog messages. For details see in udplog.py.
udp_syslog_enable = Yes udp_syslog_enable = Yes
# REST callback for SoC states. Comment out to disable. Do not leave a trailing slash
soc_callback_enabled = False
soc_callback_endpoint = http://1.1.1.1

46
evseNoGui.py Normal file
View file

@ -0,0 +1,46 @@
# The non-GUI variant of the PEV side
import time
import pyPlcWorker
from configmodule import getConfigValue, getConfigValueBool
from pyPlcModes import *
import sys # for argv
import requests
startTime_ms = round(time.time()*1000)
def cbAddToTrace(s):
currentTime_ms = round(time.time()*1000)
dT_ms = currentTime_ms - startTime_ms
print("[" + str(dT_ms) + "ms] " + s)
def cbShowStatus(s, selection=""):
pass
soc_callback_enabled = getConfigValueBool("soc_callback_enabled")
soc_callback_url = getConfigValue("soc_callback_endpoint") if soc_callback_enabled else ""
def socStatusCallback(remaining_soc: int, full_soc: int = -1, bulk_soc: int = -1, origin: str = ""):
print(f"Received SoC status from {origin}.\n"
f" Remaining {remaining_soc}% \n"
f" Full at {full_soc}%\n"
f" Bulk at {bulk_soc}%")
if soc_callback_enabled:
requests.post(f"{soc_callback_url}/modem?remaining_soc={remaining_soc}&full_soc={full_soc}&bulk_soc={bulk_soc}")
myMode = C_EVSE_MODE
print("starting in EVSE_MODE")
print("press Ctrl-C to exit")
worker=pyPlcWorker.pyPlcWorker(cbAddToTrace, cbShowStatus, myMode, 0, socStatusCallback)
nMainloops=0
while (1):
time.sleep(.03) # 'do some calculation'
nMainloops+=1
worker.mainfunction()
#---------------------------------------------------------------

View file

@ -10,6 +10,7 @@ from helpers import prettyHexMessage, combineValueAndMultiplier
from mytestsuite import * from mytestsuite import *
from random import random from random import random
from exiConnector import * # for EXI data handling/converting from exiConnector import * # for EXI data handling/converting
import requests
stateWaitForSupportedApplicationProtocolRequest = 0 stateWaitForSupportedApplicationProtocolRequest = 0
stateWaitForSessionSetupRequest = 1 stateWaitForSessionSetupRequest = 1
@ -28,6 +29,10 @@ class fsmEvse():
def publishStatus(self, s): def publishStatus(self, s):
self.callbackShowStatus(s, "evseState") self.callbackShowStatus(s, "evseState")
def publishSoCs(self, remaining_soc: int, full_soc: int = -1, bulk_soc: int = -1, origin: str = ""):
if self.callbackSoCStatus is not None:
self.callbackSoCStatus(remaining_soc, full_soc, bulk_soc, origin)
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): if (self.state!=0) and (n==0):
@ -130,6 +135,10 @@ class fsmEvse():
self.addToTrace(strConverterResult) self.addToTrace(strConverterResult)
if (strConverterResult.find("PowerDeliveryReq")>0): if (strConverterResult.find("PowerDeliveryReq")>0):
# todo: check the request content, and fill response parameters # todo: check the request content, and fill response parameters
self.addToTrace("Received PowerDeliveryReq. Extracting SoC parameters")
info = json.loads(strConverterResult)
remaining_soc = int(info.get("EVRESSSOC", -1))
self.publishSoCs(remaining_soc, origin="PowerDeliveryReq")
msg = addV2GTPHeader(exiEncode("EDh")) # EDh for Encode, Din, PowerDeliveryResponse msg = addV2GTPHeader(exiEncode("EDh")) # EDh for Encode, Din, PowerDeliveryResponse
if (testsuite_faultinjection_is_triggered(TC_EVSE_ResponseCode_Failed_for_PowerDeliveryRes)): if (testsuite_faultinjection_is_triggered(TC_EVSE_ResponseCode_Failed_for_PowerDeliveryRes)):
# send a PowerDeliveryResponse with Responsecode Failed # send a PowerDeliveryResponse with Responsecode Failed
@ -139,6 +148,13 @@ class fsmEvse():
self.Tcp.transmit(msg) self.Tcp.transmit(msg)
self.enterState(stateWaitForFlexibleRequest) # todo: not clear, what is specified in DIN self.enterState(stateWaitForFlexibleRequest) # todo: not clear, what is specified in DIN
if (strConverterResult.find("ChargeParameterDiscoveryReq")>0): if (strConverterResult.find("ChargeParameterDiscoveryReq")>0):
self.addToTrace("Received ChargeParameterDiscoveryReq. Extracting SoC parameters via DC")
info = json.loads(strConverterResult)
remaining_soc = int(info.get("DC_EVStatus.EVRESSSOC", -1))
full_soc = int(info.get("FullSOC", -1))
bulk_soc = int(info.get("BulkSOC", -1))
self.publishSoCs(remaining_soc, full_soc, bulk_soc, origin="ChargeParameterDiscoveryReq")
# todo: check the request content, and fill response parameters # todo: check the request content, and fill response parameters
msg = addV2GTPHeader(exiEncode("EDe")) # EDe for Encode, Din, ChargeParameterDiscoveryResponse msg = addV2GTPHeader(exiEncode("EDe")) # EDe for Encode, Din, ChargeParameterDiscoveryResponse
if (testsuite_faultinjection_is_triggered(TC_EVSE_ResponseCode_ServiceSelectionInvalid_for_ChargeParameterDiscovery)): if (testsuite_faultinjection_is_triggered(TC_EVSE_ResponseCode_ServiceSelectionInvalid_for_ChargeParameterDiscovery)):
@ -151,6 +167,11 @@ class fsmEvse():
if (strConverterResult.find("CableCheckReq")>0): if (strConverterResult.find("CableCheckReq")>0):
# todo: check the request content, and fill response parameters # todo: check the request content, and fill response parameters
# todo: make a real cable check, and while it is ongoing, send "Ongoing". # todo: make a real cable check, and while it is ongoing, send "Ongoing".
self.addToTrace("Received CableCheckReq. Extracting SoC parameters via DC")
info = json.loads(strConverterResult)
remaining_soc = int(info.get("DC_EVStatus.EVRESSSOC", -1))
self.publishSoCs(remaining_soc, -1, -1, origin="CableCheckReq")
msg = addV2GTPHeader(exiEncode("EDf")) # EDf for Encode, Din, CableCheckResponse msg = addV2GTPHeader(exiEncode("EDf")) # EDf for Encode, Din, CableCheckResponse
if (testsuite_faultinjection_is_triggered(TC_EVSE_ResponseCode_Failed_for_CableCheckRes)): if (testsuite_faultinjection_is_triggered(TC_EVSE_ResponseCode_Failed_for_CableCheckRes)):
# send a CableCheckResponse with Responsecode Failed # send a CableCheckResponse with Responsecode Failed
@ -212,8 +233,14 @@ class fsmEvse():
strEVTargetVoltageMultiplier = y["EVTargetVoltage.Multiplier"] strEVTargetVoltageMultiplier = y["EVTargetVoltage.Multiplier"]
uTarget = combineValueAndMultiplier(strEVTargetVoltageValue, strEVTargetVoltageMultiplier) uTarget = combineValueAndMultiplier(strEVTargetVoltageValue, strEVTargetVoltageMultiplier)
self.addToTrace("EV wants EVTargetVoltage " + str(uTarget)) self.addToTrace("EV wants EVTargetVoltage " + str(uTarget))
strSoc = y["DC_EVStatus.EVRESSSOC"]
self.callbackShowStatus(strSoc, "soc") remaining_soc = int(y.get("DC_EVStatus.EVRESSSOC", -1))
full_soc = int(y.get("FullSOC", -1))
bulk_soc = int(y.get("BulkSOC", -1))
self.publishSoCs(remaining_soc, full_soc, bulk_soc, origin="CurrentDemandReq")
self.callbackShowStatus(str(remaining_soc), "soc")
except: except:
self.addToTrace("ERROR: Could not decode the CurrentDemandReq") self.addToTrace("ERROR: Could not decode the CurrentDemandReq")
self.simulatedPresentVoltage = uTarget + 3*random() # The charger provides the voltage which is demanded by the car. self.simulatedPresentVoltage = uTarget + 3*random() # The charger provides the voltage which is demanded by the car.
@ -311,9 +338,10 @@ class fsmEvse():
# The TCP informs us, that a connection is established. # The TCP informs us, that a connection is established.
self.publishStatus("TCP connected") self.publishStatus("TCP connected")
def __init__(self, addressManager, callbackAddToTrace, hardwareInterface, callbackShowStatus): def __init__(self, addressManager, callbackAddToTrace, hardwareInterface, callbackShowStatus, callbackSoCStatus = None):
self.callbackAddToTrace = callbackAddToTrace self.callbackAddToTrace = callbackAddToTrace
self.callbackShowStatus = callbackShowStatus self.callbackShowStatus = callbackShowStatus
self.callbackSoCStatus = callbackSoCStatus
#todo self.addressManager = addressManager #todo self.addressManager = addressManager
#todo self.hardwareInterface = hardwareInterface #todo self.hardwareInterface = hardwareInterface
self.addToTrace("initializing fsmEvse") self.addToTrace("initializing fsmEvse")

View file

@ -1020,7 +1020,7 @@ class pyPlcHomeplug():
def findEthernetAdaptor(self): def findEthernetAdaptor(self):
self.strInterfaceName="eth0" # default, if the real is not found self.strInterfaceName="eth1" # default, if the real is not found
#print("Interfaces:\n" + '\n'.join(pcap.findalldevs())) #print("Interfaces:\n" + '\n'.join(pcap.findalldevs()))
for i in range(0, 10): for i in range(0, 10):
strInterfaceName = pcap.ex_name("eth"+str(i)) strInterfaceName = pcap.ex_name("eth"+str(i))

View file

@ -52,7 +52,7 @@ class pyPlcTcpClientSocket():
#print(host[0:5].lower()) #print(host[0:5].lower())
if (host[0:5].lower()=="fe80:"): 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" host = host + "%eth1"
socket_addr = socket.getaddrinfo(host,port,socket.AF_INET6,socket.SOCK_DGRAM,socket.SOL_UDP)[0][4] socket_addr = socket.getaddrinfo(host,port,socket.AF_INET6,socket.SOCK_DGRAM,socket.SOL_UDP)[0][4]
#print(socket_addr) #print(socket_addr)
@ -182,6 +182,10 @@ class pyPlcTcpServerSocket():
# in case of a broken connection, here we try to start it again # in case of a broken connection, here we try to start it again
self.addToTrace("Trying to reset the TCP socket") self.addToTrace("Trying to reset the TCP socket")
# Todo: how to "reset" the socket? # Todo: how to "reset" the socket?
try:
self.ourSocket.close()
except:
pass
self.ourSocket = socket.socket(socket.AF_INET6, socket.SOCK_STREAM, 0) self.ourSocket = socket.socket(socket.AF_INET6, socket.SOCK_STREAM, 0)
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))
@ -297,7 +301,7 @@ def testClientSocket():
#c.connect('fe80::e0ad:99ac:52eb:85d3', 15118) #c.connect('fe80::e0ad:99ac:52eb:85d3', 15118)
#c.connect('fe80::e0ad:99ac:52eb:9999', 15118) #c.connect('fe80::e0ad:99ac:52eb:9999', 15118)
#c.connect('localhost', 15118) #c.connect('localhost', 15118)
c.connect('fe80::c690:83f3:fbcb:980e', 15118) c.connect('fe80::407d:89f9:f6c2:6ee0', 15118)
print("connected="+str(c.isConnected)) print("connected="+str(c.isConnected))
if (c.isConnected): if (c.isConnected):
print("sending something to the server") print("sending something to the server")

View file

@ -18,7 +18,7 @@ import connMgr
class pyPlcWorker(): class pyPlcWorker():
def __init__(self, callbackAddToTrace=None, callbackShowStatus=None, mode=C_EVSE_MODE, isSimulationMode=0): def __init__(self, callbackAddToTrace=None, callbackShowStatus=None, mode=C_EVSE_MODE, isSimulationMode=0, callbackSoC=None):
print("initializing pyPlcWorker") print("initializing pyPlcWorker")
self.nMainFunctionCalls=0 self.nMainFunctionCalls=0
self.mode = mode self.mode = mode
@ -26,6 +26,7 @@ class pyPlcWorker():
self.addressManager = addressManager.addressManager() self.addressManager = addressManager.addressManager()
self.callbackAddToTrace = callbackAddToTrace self.callbackAddToTrace = callbackAddToTrace
self.callbackShowStatus = callbackShowStatus self.callbackShowStatus = callbackShowStatus
self.callbackSoC = callbackSoC
self.oldAvlnStatus = 0 self.oldAvlnStatus = 0
self.isSimulationMode = isSimulationMode self.isSimulationMode = isSimulationMode
self.connMgr = connMgr.connMgr(self.workerAddToTrace, self.showStatus) self.connMgr = connMgr.connMgr(self.workerAddToTrace, self.showStatus)
@ -40,7 +41,7 @@ class pyPlcWorker():
strLabel = "(unknown version. 'git describe --tags' failed.)" strLabel = "(unknown version. 'git describe --tags' failed.)"
self.workerAddToTrace("[pyPlcWorker] Software version " + strLabel) self.workerAddToTrace("[pyPlcWorker] Software version " + strLabel)
if (self.mode == C_EVSE_MODE): if (self.mode == C_EVSE_MODE):
self.evse = fsmEvse.fsmEvse(self.addressManager, self.workerAddToTrace, self.hardwareInterface, self.showStatus) self.evse = fsmEvse.fsmEvse(self.addressManager, self.workerAddToTrace, self.hardwareInterface, self.showStatus, self.callbackSoC)
if (self.mode == C_PEV_MODE): if (self.mode == C_PEV_MODE):
self.pev = fsmPev.fsmPev(self.addressManager, self.connMgr, self.workerAddToTrace, self.hardwareInterface, self.showStatus) self.pev = fsmPev.fsmPev(self.addressManager, self.connMgr, self.workerAddToTrace, self.hardwareInterface, self.showStatus)
def __del__(self): def __del__(self):