# 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](https://docs.python.org/3/library/venv.html). ## Prerequisites 1. Copy the code [below](#full-python-code) to `./demo.py` 2. Create an account on `https://stream.dromt.it` 3. Download your client certificate from the menu in the top-right corner and place it under `./data/cert_client.pfx` 4. Create a drone and copy its identifier in `demo.py`, setting the `DRONE_ID` variable 5. Download the drone certificate from the dashboard and place it under `./data/cert_drone.pfx` ## Install Dependencies and Generate GRPC Python files ```bash # 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 ```python 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 = "" # 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") ```