I am new to OCPP protocol and I am building a Python OCPP server that can communicate with an EV charger using OCPP protocol. This server has the feature "Authenticate user via RFID". I have created 2 Python files which are Charge_Stattion.py:
# Charge_Stattion.py
import asyncio
import logging
import websockets
from ocpp.v201 import call
from ocpp.v201 import ChargePoint as cp
logging.basicConfig(level=logging.INFO)
class ChargePoint(cp):
async def authentication(self):
request = call.AuthorizePayload(
id_token={'id_token':'AA12345',
'type': 'ISO14443'})
response = await self.call(request)
print(response)
async def main():
async with websockets.connect(
'ws://localhost:9000/CP_1',
subprotocols=['ocpp2.0.1']
) as ws:
cp = ChargePoint('CP_1', ws)
await asyncio.gather(cp.start(), cp.authentication())
if __name__ == '__main__':
asyncio.run(main())
and Central_System.py:
#Central_System.py
import asyncio
import logging
import websockets
from datetime import datetime
from ocpp.routing import on
from ocpp.v201 import ChargePoint as cp
from ocpp.v201 import call_result
from ocpp.v201.enums import AuthorizationStatusType, Action
logging.basicConfig(level=logging.INFO)
class ChargePoint(cp):
#on('BootNotification')
async def on_boot_notification(self, charging_station, reason, **kwargs):
return call_result.BootNotificationPayload(
current_time=datetime.utcnow().isoformat(),
interval=10,
status='Accepted'
)
#on(Action.Authorize)
async def on_authorize(self, id_token):
return call_result.AuthorizePayload(id_token_info={"status": AuthorizationStatusType.accepted})
async def on_connect(websocket, path):
""" For every new charge point that connects, create a ChargePoint
instance and start listening for messages.
"""
try:
requested_protocols = websocket.request_headers[
'Sec-WebSocket-Protocol']
except KeyError:
logging.info("Client hasn't requested any Subprotocol. "
"Closing Connection")
if websocket.subprotocol:
logging.info("Protocols Matched: %s", websocket.subprotocol)
else:
# In the websockets lib if no subprotocols are supported by the
# client and the server, it proceeds without a subprotocol,
# so we have to manually close the connection.
logging.warning('Protocols Mismatched | Expected Subprotocols: %s,'
' but client supports %s | Closing connection',
websocket.available_subprotocols,
requested_protocols)
return await websocket.close()
charge_point_id = path.strip('/')
cp = ChargePoint(charge_point_id, websocket)
logging.info("abcxyz: %s", charge_point_id)
await cp.start()
async def main():
server = await websockets.serve(
on_connect,
'0.0.0.0',
9000,
subprotocols=['ocpp2.0.1']
)
logging.info("WebSocket Server Started")
await server.wait_closed()
if __name__ == '__main__':
asyncio.run(main())
Following the document here, I understand that the user must present an RFID card first, then the Charge Station will send an AuthorizeRequest containing idToken from this RFID card to Central System, then Central System will send and AuthorizeResponse to Charge Station. In the 2 python files above, I have implemented the process Charge Station sends andAuthorizeRequest to Central System and Central System sends back AuthorizeResponse to Charge Station. This picture demonstrates these processes
My questions are:
How can I implement the process EV driver present an RFID card to Charge Station. Should I create 2 other python files which represent EV driver and RFID card?
How can I know whether Center System accept this authentication and how to implement this ?
Any help will be appreciated.
This is a simple flow
EV owner registers himself as a EV client on some server where the server provides an unique id, like "unique-client-id" and stores this value as idTag on a database.
When this client go to charge to some charging station, he inputs that unique id to charging device which sends the id in the following form via websocket connection:
[3, "unique-id-representing-the-current-msg", "Authorize", {"idTag": "unique-client-id"}]
OCPP server receives that message, and looks for received idTag on the database, if it exists it will send back response like below:
[4, "unique-id-representing-the-current-msg", {"idTagInfo": {"status": "Accepted"}}]
I recommend using sanic framework since it has both websocket and http support by default.
Related
I am using micropython for esp32 to make BLE app using aioble library.i am using as per sample code + add from library but i am facing this problem and don't understand why. Is it because the library is having problems?. I have followed the instructions on github but the error still occurs, I can't handle it
import sys
sys.path.append("")
from micropython import const
import uasyncio as asyncio
import aioble
import bluetooth
SERVICE_UUID = bluetooth.UUID('00001800-0000-1000-8000-00805F9B34FB')
mtu_connect = 0
async def find_temp_sensor():
# Scan for 5 seconds, in active mode, with very low interval/window (to
# maximise detection rate).
async with aioble.scan(10000, interval_us=12000, window_us=10000, active=True) as scanner:
async for result in scanner:
# See if it matches our name and the environmental sensing service.
print(result, result.name(), result.rssi, result.services())
if result.name() == "70001697":
return result.device
return None
async def main():
device = await find_temp_sensor()
while not device:
print("Temperature sensor not found")
device = await find_temp_sensor()
return
print(device)
mtu_connect = 0
while mtu_connect < 3:
try:
print("Connecting to", device)
connection = await device.connect()
service = await connection.service(SERVICE_UUID)
print("service", service.uuid)
# Discover characteristics.
uuids = []
async for char in service.characteristics():
uuids.append(char.uuid)
print("found", sorted(uuids))
print("Connecting done!")
break
except asyncio.TimeoutError:
print("Timeout during connection")
mtu_connect = mtu_connect + 1
asyncio.run(main())
You need to specify the actual error message to get some help. Note that you need to put on your microcontroller the aioble library files (typically under lib/aioble) as aioble is not part of the standard micropython firmware.
You need to import aioble using mip. See the user instructions on installation with mpremote. It cannot be done through thonny.
We have modified and implemented the below code to accomodate remote start transaction and we are able to control the start and stop the charger quite well.
However, we are stuck in trying to implement the below:
Web Interface - Using reactJS
a) Has to show LIVE Status of the chargers
How do i make my web app listen to the CSMS system and get all messages for all chargers?
Do i need to add to the database Boot and Status Notifications as and when the CSMS receives a message?
Basically, my question is how do I achieve this to show realtime data.
Mobile Interface - Using Flutter
a) Has to Start and Stop a transaction (Successful)
b) Keep getting the meter values, time etc to show on the mobile device real time while charging.
On click of the "Start Charging" button on the mobile app, we make an API call to "localhost:8080/remoteStart", however, we just get a 200 response. How do get realtime update here to show the meter values etc.
Any help or guidance in the right direction will be very helpful. Thanks.
import asyncio
import websockets
from aiohttp import web
from functools import partial
from datetime import datetime
from ocpp.routing import on
from ocpp.v16 import ChargePoint as cp
from ocpp.v16.enums import Action, RegistrationStatus
from ocpp.v16 import call_result, call
class ChargePoint(cp):
#on(Action.BootNotification)
def on_boot_notitication(self, charge_point_vendor, charge_point_model, **kwargs):
return call_result.BootNotificationPayload(
current_time=datetime.utcnow().isoformat(),
interval=10,
status=RegistrationStatus.accepted,
)
async def change_configuration(self, key: str, value: str):
return await self.call(call.ChangeConfigurationPayload(key=key, value=value))
class CentralSystem:
def __init__(self):
self._chargers = {}
def register_charger(self, cp: ChargePoint) -> asyncio.Queue:
""" Register a new ChargePoint at the CSMS. The function returns a
queue. The CSMS will put a message on the queue if the CSMS wants to
close the connection.
"""
queue = asyncio.Queue(maxsize=1)
# Store a reference to the task so we can cancel it later if needed.
task = asyncio.create_task(self.start_charger(cp, queue))
self._chargers[cp] = task
return queue
async def start_charger(self, cp, queue):
""" Start listening for message of charger. """
try:
await cp.start()
except Exception as e:
print(f"Charger {cp.id} disconnected: {e}")
finally:
# Make sure to remove referenc to charger after it disconnected.
del self._chargers[cp]
# This will unblock the `on_connect()` handler and the connection
# will be destroyed.
await queue.put(True)
async def change_configuration(self, key: str, value: str):
for cp in self._chargers:
await cp.change_configuration(key, value)
def disconnect_charger(self, id: str):
for cp, task in self._chargers.items():
if cp.id == id:
task.cancel()
return
raise ValueError(f"Charger {id} not connected.")
async def change_config(request):
""" HTTP handler for changing configuration of all charge points. """
data = await request.json()
csms = request.app["csms"]
await csms.change_configuration(data["key"], data["value"])
return web.Response()
async def disconnect_charger(request):
""" HTTP handler for disconnecting a charger. """
data = await request.json()
csms = request.app["csms"]
try:
csms.disconnect_charger(data["id"])
except ValueError as e:
print(f"Failed to disconnect charger: {e}")
return web.Response(status=404)
return web.Response()
async def on_connect(websocket, path, csms):
""" For every new charge point that connects, create a ChargePoint instance
and start listening for messages.
The ChargePoint is registered at the CSMS.
"""
charge_point_id = path.strip("/")
cp = ChargePoint(charge_point_id, websocket)
print(f"Charger {cp.id} connected.")
# If this handler returns the connection will be destroyed. Therefore we need some
# synchronization mechanism that blocks until CSMS wants to close the connection.
# An `asyncio.Queue` is used for that.
queue = csms.register_charger(cp)
await queue.get()
async def create_websocket_server(csms: CentralSystem):
handler = partial(on_connect, csms=csms)
return await websockets.serve(handler, "0.0.0.0", 9000, subprotocols=["ocpp1.6"])
async def create_http_server(csms: CentralSystem):
app = web.Application()
app.add_routes([web.post("/", change_config)])
app.add_routes([web.post("/disconnect", disconnect_charger)])
# Put CSMS in app so it can be accessed from request handlers.
# https://docs.aiohttp.org/en/stable/faq.html#where-do-i-put-my-database-connection-so-handlers-can-access-it
app["csms"] = csms
# https://docs.aiohttp.org/en/stable/web_advanced.html#application-runners
runner = web.AppRunner(app)
await runner.setup()
site = web.TCPSite(runner, "localhost", 8080)
return site
async def main():
csms = CentralSystem()
websocket_server = await create_websocket_server(csms)
http_server = await create_http_server(csms)
await asyncio.wait([websocket_server.wait_closed(), http_server.start()])
if __name__ == "__main__":
asyncio.run(main())
According to the Azure ServiceBus docs here:
The ServiceBusReceiver class defines a high level interface for receiving messages from the Azure Service Bus Queue or Topic Subscription. The two primary channels for message receipt are receive() to make a single request for messages, and async for message in receiver: to continuously receive incoming messages in an ongoing fashion.
I have been attempting to use the async for message in receiver: advice to trigger a function everytime a message comes up, but I'm unsure how to do it right, as I have little experience working with async functions. Could someone familiar with async/service bus explain how the code should be formatted?
Edit: Let me provide some more context. I am creating a python flask service, and on start-up, I need it to start listening to messages on a topic/subscription_name. Whenever it receives a message, it will execute some code, then send a message back. So... how do I start an async listener on startup, and have it execute some code whenever it is triggered? It should also be able to process each message in a non-blocking way. So if two messages are received at once, both should be processed at the same time.
Note: I cannot use Azure Functions.
Assuming you are using topic-subscription, you can use below code:
#!/usr/bin/env python
# --------------------------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See License.txt in the project root for license information.
# --------------------------------------------------------------------------------------------
"""
Example to show receiving batch messages from a Service Bus Subscription under specific Topic asynchronously.
"""
# pylint: disable=C0111
import os
import asyncio
from azure.servicebus.aio import ServiceBusClient
CONNECTION_STR = os.environ['SERVICE_BUS_CONNECTION_STR']
TOPIC_NAME = os.environ["SERVICE_BUS_TOPIC_NAME"]
SUBSCRIPTION_NAME = os.environ["SERVICE_BUS_SUBSCRIPTION_NAME"]
async def main():
servicebus_client = ServiceBusClient.from_connection_string(conn_str=CONNECTION_STR)
async with servicebus_client:
receiver = servicebus_client.get_subscription_receiver(
topic_name=TOPIC_NAME,
subscription_name=SUBSCRIPTION_NAME
)
async with receiver:
received_msgs = await receiver.receive_messages(max_message_count=10, max_wait_time=5)
for msg in received_msgs:
print(str(msg))
await receiver.complete_message(msg)
loop = asyncio.get_event_loop()
loop.run_until_complete(main())
Complete tutorial: Send messages to an Azure Service Bus topic and receive messages from subscriptions to the topic (Python)
Further, you can explore these samples (both sync and async versions): Azure Service Bus client library for Python Samples
my goal is to implement a gRPC client that is unfamiliar of the proto file (only knows server/service port number) - the client find the service on run-time (reflection)
my client should be able to:
connect to the server
learn which server capabilities exists (including messages)
send rpc requests to the enable-reflection server
i already see an example in Go & Java and failed to find one in python that work for me.
i successfully used "evans" cli client with my server but i want to implement cli client on my own.
https://github.com/grpc/grpc/blob/master/doc/server_reflection_tutorial.md
i look on this example of how to implementing the server & i can use some help to better understand how to implement the client.
https://github.com/grpc/grpc/blob/master/examples/python/helloworld/greeter_server_with_reflection.py
my questions are:
how to find the current service from the client only from port number?
how to communicate with the server without importing the generated proto files?
server side:
from __future__ import print_function
import logging
import grpc
import helloworld_pb2
import helloworld_pb2_grpc
def run():
# NOTE(gRPC Python Team): .close() is possible on a channel and should be
# used in circumstances in which the with statement does not fit the needs
# of the code.
#
# For more channel options, please see https://grpc.io/grpc/core/group__grpc__arg__keys.html
with grpc.insecure_channel(target='localhost:50051',
options=[('grpc.lb_policy_name', 'pick_first'),
('grpc.enable_retries', 0),
('grpc.keepalive_timeout_ms', 10000)
]) as channel:
stub = helloworld_pb2_grpc.GreeterStub(channel)
# Timeout in seconds.
# Please refer gRPC Python documents for more detail. https://grpc.io/grpc/python/grpc.html
response = stub.SayHello(helloworld_pb2.HelloRequest(name='you'),
timeout=10)
print("Greeter client received: " + response.message)
if __name__ == '__main__':
logging.basicConfig()
run()
client side:
from __future__ import print_function
import logging
import grpc
import helloworld_pb2
import helloworld_pb2_grpc
def run():
# NOTE(gRPC Python Team): .close() is possible on a channel and should be
# used in circumstances in which the with statement does not fit the needs
# of the code.
with grpc.insecure_channel('localhost:50051') as channel:
stub = helloworld_pb2_grpc.GreeterStub(channel)
response = stub.SayHello(helloworld_pb2.HelloRequest(name='you'))
print("Greeter client received: " + response.message)
if __name__ == '__main__':
logging.basicConfig()
run()
I'm writing a program with websockets in python. I've got an example server and client code running and they work well if only one client is connected. If there are multiple clients, data from the server will go randomly to one of the clients.
I would like for:
Server to keep track of the various clients connected
Server to be able to direct messages to a specific client out of multiple(For eg. 5) clients
websockets is the library I'm using.
Python version 3.7.2
Server Code:
import asyncio
import websockets
uri='localhost'
async def response(websocket, path):
msg = input("What do you want to send : ")
print("message:",msg)
await websocket.send(msg)
start_server = websockets.serve(response, uri, 5000)
asyncio.get_event_loop().run_until_complete(start_server)
asyncio.get_event_loop().run_forever()
Client Code:
import asyncio
import websockets
uri="ws://localhost:5000"
async def message():
async with websockets.connect(uri) as socket:
print(await socket.recv())
while True:
asyncio.get_event_loop().run_until_complete(message())
If I create 2 files with the client code as client1.py and client2.py, and send message from the server side, I get the sent data going to either on of the clients.
I would like to:
Server keeps track of the various clients connected
Server is able to direct messages to a specific client out of multiple clients
As I am just starting out with websockets, all input is appreciated.
In this output given, I intended to send all my messages to client 1, yet they got split up between client 1 and 2
"websocket" targets the current connection and if you say "websocket.send(msg)" you're sending a message to the client that has just connected and websocket is an object that is reusable while the client is connected. You can assign the websocket as a variable then send a message some other time as long as the connection is still opened.
NOT RECOMMENED
Requiring user's input from the server is not a good idea because now you're awaiting the server until it receives user inputs. Nothing really happens to the server while it's waiting for user's input and this may crush your server.
RECOMMENED
A client has to tell the server which connection / client to send the message to. I would recommend using a JSON format when sending messages within client's and the server and then convert the String to a python-dict since websocket requires only strings.
Click here to check out my GitHub repository. A websockets server made only Python.
SERVER EXAMPLE
Example on how you can send a packet to a specific client
I'm not familiar with asyncio, so I will try to get to the point with functions/threads;
Usually, my server side listens to one connection and once it accepts it, I have a function 'handler' that is threaded to each connection that gets accepted.
part of my server and handler:
def handler(conn, addr):
global data1
while True:
data = conn.recv(2048)
data1 = json.loads(data.decode())
# treat it as you need
while True:
s.listen(1)
conn, addr = s.accept()
print('Conectado com', addr[0],':', str(addr[1]))
thr = threading.Thread(target = handler, args = (conn, addr)).start()
Now, for the control of the clients and such, I always use a dictionary. The key should be the username or any other particular info. The value of the key is the 'conn' from that user. This way you can get the connection of user 'x' by its specific key.
Something like:
import socket
import time
import datetime as dt
import base64
import os
import json
import threading
HOST = ''
PORT = 12999
global s
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind((HOST,PORT))
whoto = {}
def autenticacao():
global data1
global conn
global nomeuser
user1 = data1[1]
passwd = data1[2]
auth = c.execute('SELECT usuario FROM fullinfo WHERE usuario= ? AND password = ?', (user1,passwd)).fetchone()
if auth is not None:
word = 'autenticado'
conn.sendall(word.encode())
nomeuser = auth[0]
whoto[nomeuser] = conn
I'm sorry i'm leaving it unreproducible, but my point is to show the 'algorithm'. This dictionary is what I use to keep record of who is online, the 'adress' (conn) of each client to send messages to single clients and such.
On the example above, I add the user (key) and conn (value) once it's authenticated inside my server.
Hope this helps. Good luck!