-
I have a server computer that contains a third party package for interacting with devices connected to that computer. I have a client computer on the same network that sends requests to the server computer. The purpose of these requests is to run various functions from the third party package. The diagram below shows a basic example of this where I'm requesting a serial number from the third party package. My example for accomplishing something like this with pyzmq is shown in the code given below. The Should I be sending a dictionary to the server to determine which functions to execute or does ZeroMQ have a feature that would be better suited to implement something like this? I'm new to ZeroMQ and pyzmq and I have read about topics with pub-sub patterns and multipart socket requests. But I don't see how to use any of these features for the request-reply pattern I'm using here. Any guidance on this would be greatly appreciated. # client.py
import zmq
from typing import cast
class Client:
def __init__(self):
context = zmq.Context()
socket = context.socket(zmq.REQ)
socket.connect("tcp://localhost:5555")
self.socket = socket
def get_serial_number(self) -> str:
d = {"call": "serial"}
self.socket.send_json(d)
sn = self.socket.recv_string()
return sn
def calc_motor_temp(self, speed: float) -> float:
d = {"call": "motor", "speed": speed}
self.socket.send_json(d)
motor_data = cast(dict[str, float], self.socket.recv_json())
return motor_data["temp"]
def main():
"""Run client."""
client = Client()
serial_num = client.get_serial_number()
motor_temp = client.calc_motor_temp(800)
print(f"Serial number: {serial_num}")
print(f"Motor temperature: {motor_temp}")
if __name__ == "__main__":
main() # server.py
import zmq
import package
from typing import Any
def main():
"""Run server."""
context = zmq.Context()
socket = context.socket(zmq.REP)
socket.bind("tcp://*:5555")
print("Server is running...")
while True:
# Wait for requests from client
request: Any = socket.recv_json()
print(f"Received request: {request}")
call = request["call"]
if call == "serial":
serial_num = package.serial_number()
socket.send_string(serial_num)
if call == "motor":
speed = request["speed"]
temp = package.motor_temperature(speed)
socket.send_json({"temp": temp})
if __name__ == "__main__":
main() # package.py
# This is just a module of functions but in production this would be a third party Python package
def serial_number() -> str:
"""Get the serial number."""
return "4234asdf1e"
def motor_temperature(speed: float) -> float:
"""Calculate motor temperature based on speed."""
t = 1.5 * speed / 32.1
return t |
Beta Was this translation helpful? Give feedback.
Replies: 2 comments 3 replies
-
I was able to use # client.py
import zmq
import struct
class Client:
def __init__(self):
context = zmq.Context()
socket = context.socket(zmq.REQ)
socket.connect("tcp://localhost:5555")
self.socket = socket
def get_serial_number(self) -> str:
self.socket.send_multipart([b"serial"])
sn = self.socket.recv_string()
return sn
def calc_motor_temp(self, speed: float) -> float:
speed_bytes = struct.pack("f", speed)
self.socket.send_multipart([b"motor", speed_bytes])
temp_bytes = self.socket.recv()
temp = struct.unpack("f", temp_bytes)[0]
return temp
def main():
"""Run client."""
client = Client()
serial_num = client.get_serial_number()
motor_temp = client.calc_motor_temp(800)
print(f"Serial number: {serial_num}")
print(f"Motor temperature: {motor_temp}")
if __name__ == "__main__":
main() # server.py
import zmq
import struct
import package
def main():
"""Run server."""
context = zmq.Context()
socket = context.socket(zmq.REP)
socket.bind("tcp://localhost:5555")
print("Server is running")
while True:
# Wait for requests from client
message = socket.recv_multipart()
print(f"Received message: {message}")
topic = message[0]
if topic == b"serial":
serial_num = package.serial_number()
socket.send_string(serial_num)
if topic == b"motor":
speed_bytes = message[1]
speed = struct.unpack("f", speed_bytes)[0]
temp = package.motor_temperature(speed)
temp_bytes = struct.pack("f", temp)
socket.send(temp_bytes)
if __name__ == "__main__":
main() |
Beta Was this translation helpful? Give feedback.
-
I would generally recommend defining a "Message" as something in your application, and then defining serialization/deserialization so that you have two functions: def send_message(socket: zmq.Socket, message: Message): ...
def recv_message(socket: zmq.Socket) -> Message: ... Then you can try different approaches to serialization and it won't show up anywhere in your code except in these two functions. Since a zmq message is one or more Frames (bytestrings), you might have a reason to split your message into multiple blobs (e.g. if they contain large blobs of data to avoid unnecessary copies, or to include message signatures, etc.). msgspec is a great serialization library which will get you validation and performance, but Python dicts via JSON work just fine, too, for simple cases (this is what Jupyter uses, for example). There are some docs on the topic. |
Beta Was this translation helpful? Give feedback.
Based on your suggestions, here is my client and server code: