Example Drone-Client Communication with Python
This demo shows a simple Drone-Client interaction via the Dromt GRPC server. We tested this demo on Ubuntu 20.04 with Python 3.8.10. We recommend creating a clean Python environment.
Prerequisites
Copy the code below to
./demo.py
Create an account on
https://stream.dromt.it
Download your client certificate from the menu in the top-right corner and place it under
./data/cert_client.pfx
Create a drone and copy its identifier in
demo.py
, setting theDRONE_ID
variableDownload the drone certificate from the dashboard and place it under
./data/cert_drone.pfx
Install Dependencies and Generate GRPC Python files
# Install dependencies
pip install cryptography==39.0.1 grpcio==1.51.1 protobuf==4.21.12 grpcio-tools==1.51.1
# Get .proto files
PROTO_URL=https://docs.dromt.it/static/dromt_proto.zip
wget $PROTO_URL -O proto.zip && unzip proto.zip && rm -rf proto.zip
# Generate Python files
python -m grpc_tools.protoc -Igrpc/protocol \
--python_out=./grpc \
--grpc_python_out=./grpc \
common.proto \
client.proto \
drone.proto
Run
# On the first shell, run the drone application
python demo.py drone
# On the second shell, run the client application
python demo.py client
# Enjoy!
Full Python Code
import asyncio
import grpc
import sys
from cryptography.hazmat.primitives.serialization.pkcs12 import load_key_and_certificates
from cryptography.hazmat.primitives import serialization
sys.path.append("grpc") # load GRPC files
import common_pb2
MAX_MESSAGE_SIZE = 10_000_000 # Set a maximum size for GRPC messages, in bytes
DRONE_ID = "<your_drone_id>" # get your ID from the web dashboard
def get_credentials(cert_file):
with open(cert_file, "rb") as f:
data = f.read()
key, cert, _ = load_key_and_certificates(data, None)
# encode key in PEM format
key_bytes = key.private_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PrivateFormat.TraditionalOpenSSL,
encryption_algorithm=serialization.NoEncryption()
)
# encode certificate in PEM format
cert_bytes = cert.public_bytes(
encoding=serialization.Encoding.PEM
)
return key_bytes, cert_bytes
# Function that generates ClientData objects to be sent to the GRPC server
async def client_stream(session):
while True:
await asyncio.sleep(1)
data = common_pb2.ClientData(
session=session,
command=common_pb2.Command(
id=1
)
)
yield data
# Function that generates DroneData objects to be sent to the GRPC server
async def drone_stream(session):
while True:
await asyncio.sleep(1)
data = common_pb2.DroneData(
session=session,
log=common_pb2.Log(
message="Hello!"
)
)
yield data
async def main(mode, key_file, session, stream_func):
# Get certificate and private key from PKCS12 ".pfx" file
# Both should be represented as byte arrays serialized in PEM format.
key, cert = get_credentials(key_file)
# Set credentials
cred = grpc.ssl_channel_credentials(
certificate_chain=cert, # drone's certificate
private_key=key # drone's private key
)
# Create GRPC channel
async with grpc.aio.secure_channel(
f"{mode}.grpc.dromt.it:443",
cred,
options=[
('grpc.max_send_message_length', MAX_MESSAGE_SIZE),
('grpc.max_receive_message_length', MAX_MESSAGE_SIZE),
]
) as channel:
try:
# create stub
stub = pb2_grpc.StreamerStub(channel)
# register session, receiving a new session object with the session ID.
session = await stub.Register(session)
# now we are connected
print("Connected to GRPC server.")
# opening stream sending ClientData objects.
async for data in stub.Stream(stream_func(session)):
# handle DroneData object received from the GRPC server
print(f"Received {data.WhichOneof('data')}")
except Exception as e:
# An exception occurred
print(f"Exception: {e}")
if len(sys.argv) != 2:
print(f"usage: {sys.argv[0]} {{client,drone}}")
sys.exit(-1)
mode = sys.argv[1]
if mode == "client":
import client_pb2_grpc as pb2_grpc
key_file = "data/cert_client.pfx"
stream_func = client_stream
session = common_pb2.ClientSession(droneId=DRONE_ID)
elif mode == "drone":
import drone_pb2_grpc as pb2_grpc
key_file = "data/cert_drone.pfx"
stream_func = drone_stream
session = common_pb2.DroneSession()
else:
print("Invalid mode: choose between 'client' and 'drone'")
sys.exit(-1)
try:
asyncio.run(main(mode, key_file, session, stream_func))
except KeyboardInterrupt:
print("Closing")