From 219ff63bf896a4139b6683540b2ab764d7160d83 Mon Sep 17 00:00:00 2001
From: Anton Tetov <anton@tetov.se>
Date: Wed, 13 Oct 2021 08:59:43 +0200
Subject: [PATCH] test nonblocking

---
 abb_egm_client/egm_client.py   | 180 +++++++++++++++------------------
 examples/print_egm_feedback.py |  19 ++--
 2 files changed, 88 insertions(+), 111 deletions(-)

diff --git a/abb_egm_client/egm_client.py b/abb_egm_client/egm_client.py
index aba3597..d24c293 100644
--- a/abb_egm_client/egm_client.py
+++ b/abb_egm_client/egm_client.py
@@ -1,123 +1,107 @@
 import socket
-import select
-from errno import EINTR
+import logging
 
-try:
-    from egm_pb2 import EgmRobot
-except ModuleNotFoundError:
-    from .egm_pb2 import EgmRobot
+from abb_egm_client.atomic_counter import AtomicCounter
 
+from abb_egm_client.egm_pb2 import EgmRobot, EgmSensor, EgmHeader
 
-class EGMClient:
-    def __init__(self, port=6510):
-        self.socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
-        self.socket.bind("", port)
 
-    def receive_from_robot(self, timeout=0):
+log = logging.getLogger(__name__)
 
-        try:
-            result = select.select([self.socket], [], [self.socket], timeout=timeout)
-        except select.error as err:
-            if err.args[0] == EINTR:
-                return False, None
-            else:
-                raise
-
-        if len(res[0]) == 0 and len(res[2]) == 0:
-            return False, None
-        # try:
-        buf, addr = self.socket.recvfrom(65536)
-        # except:
-        #    self.egm_addr=None
-        #    return False, None
-
-        # self.egm_addr=addr
-
-        msg = egm_pb2.EgmRobot()
-        msg.ParseFromString(buf)
-
-        Robot_pos = None
-        joint_angles = None
-        rapid_running = False
-        motors_on = False
-        global Robot_Position_Flag
-
-        if robot_message.HasField("feedBack"):
-            if Robot_Position_Flag == False:
-                Robot_pos = robot_message.feedBack.cartesian.pos
-                Robot_Position_Flag = True
-            Joints = robot_message.feedBack.joints.joints
-            print(robot_message.feedBack.joints.joints)
-        if robot_message.HasField("rapidExecState"):
-            rapid_running = (
-                robot_message.rapidExecState.state
-                == robot_message.rapidExecState.RAPID_RUNNING
-            )
-        if robot_message.HasField("motorState"):
-            motors_on = (
-                robot_message.motorState.state == robot_message.motorState.MOTORS_ON
-            )
 
-        return True, Robot_pos, Joints
+class EGMClientException(Exception):
+    """Base class for other exceptions"""
+
+    pass
 
-    def send_to_robot_joints(self, joint_angles):
 
-        if not self.egm_addr:
-            return False
+class EGMClientNotInitializedException(EGMClientException):
+    """When user tries to access attributes set in self.recieve_from_robot()"""
 
-        self.send_sequence_number += 1
+    pass
 
-        sensorMessage = egm_pb2.EgmSensor()
 
-        header = sensorMessage.header
-        header.mtype = egm_pb2.EgmHeader.MessageType.Value("MSGTYPE_CORRECTION")
-        header.seqno = self.send_sequence_number
-        self.send_sequence_number += 1
+class EGMClient:
+    def __init__(self, port=6510):
+        self.socket = socket.socket(family=socket.AF_INET, type=socket.SOCK_DGRAM)
+        self.robot_controller_address = None
+        self.send_counter = AtomicCounter()
+
+        self.socket.bind(("", port))
+
+        self.socket.setblocking(False)
+
+    # def _ensure_socket_ready(self, timeout=1):
+    #     # try:
+    #     # except select.error as err:
+    #     #     if err.args[0] == EINTR:
+    #     #         return False
+    #     #     else:
+    #     #         raise
+    #     result = select.select([self.socket], [], [self.socket], timeout)
+
+    #     print(result)
+
+    #     # if len(result[0]) == 0 and len(result[2]) == 0:
+    #     #     raise Exception("error in ensure_socket_ready")
+
+    def _get_last_packet(self):
+        last_recieved = (None, None)
+
+        while True:
+            try:
+                data, addr = self.socket.recvfrom(1024)
+                if data:
+                    last_recieved = (data, addr)
+            except socket.error as err:
+                # EWOULDBLOCK: recvfrom would block since socket buffer is empty
+                if err.args[0] == socket.EWOULDBLOCK:
+                    return last_recieved  # return last data recieved
+                else:
+                    raise err  # if there is another error, raise exception
+
+    def receive_from_robot(self, timeout=0):
+
+        data, addr = self.socket.recvfrom(1024)
 
-        planned = sensorMessage.planned
+        if not self.robot_controller_address:
+            self.robot_controller_address = addr
 
-        if joint_angles is not None:
-            joint_angles2 = list(np.rad2deg(cartesian))
-            print(joint_angles2)
-            print(planned.joints.joints)
-        buf2 = sensorMessage.SerializeToString()
+        msg = EgmRobot()
+        msg.ParseFromString(data)
 
-        try:
-            self.socket.sendto(buf2, self.egm_addr)
-        except:
-            return False
+        return msg
 
-        return True
+    def send_msg(self, msg):
+        if not self.robot_controller_address:
+            raise EGMClientNotInitializedException(
+                "You need to start communication with controller."
+            )
 
-    def send_to_robot_cartesian(self, Position, Rob_pos):
+        self.socket.sendto(msg.SerializeToString(), self.robot_controller_address)
 
-        if not self.egm_addr:
-            return False
+    def _create_sensor_msg(self, type="MSGTYPE_CORRECTION"):
+        msg = EgmSensor()
+        msg.header.mtype = EgmHeader.MessageType.Value(type)
+        msg.header.seqno = self.send_counter.inc()
 
-        self.send_sequence_number += 1
+        return msg
 
-        sensorMessage = egm_pb2.EgmSensor()
+    def send_planned_configuration(self, configuration):
+        msg = self._create_sensor_msg_type_correction()
 
-        header = sensorMessage.header
-        header.mtype = egm_pb2.EgmHeader.MessageType.Value("MSGTYPE_CORRECTION")
-        header.seqno = self.send_sequence_number
-        self.send_sequence_number += 1
+        msg.planned.joints.joints.extend(configuration)
 
-        planned = sensorMessage.planned
+        self.send_msg(msg)
 
-        if Position is not None:
-            planned.cartesian.pos.x = Rob_pos.x + Position[2]
-            planned.cartesian.pos.y = Rob_pos.y + Position[0]
-            planned.cartesian.pos.z = Rob_pos.z + Position[1]
-            planned.cartesian.euler.x = -90
-            planned.cartesian.euler.y = 180
-            planned.cartesian.euler.z = 0
-            print(planned.cartesian.pos)
-        buf2 = sensorMessage.SerializeToString()
+    def send_planned_frame(self, x, y, z, rx, ry, rz):
+        msg = self._create_sensor_msg_type_correction()
 
-        try:
-            self.socket.sendto(buf2, self.egm_addr)
-        except:
-            return False
+        msg.planned.cartesian.pos.x = x
+        msg.planned.cartesian.pos.y = y
+        msg.planned.cartesian.pos.z = z
+        msg.planned.cartesian.euler.x = rx
+        msg.planned.cartesian.euler.y = ry
+        msg.planned.cartesian.euler.z = rz
 
-        return True
+        self.send_msg(msg)
diff --git a/examples/print_egm_feedback.py b/examples/print_egm_feedback.py
index 67e621a..1ba02cd 100644
--- a/examples/print_egm_feedback.py
+++ b/examples/print_egm_feedback.py
@@ -1,8 +1,5 @@
 #!/usr/bin/env python
 import logging
-import sys
-
-log = logging.getLogger()
 
 try:
     from abb_egm_client import EGMClient
@@ -11,32 +8,28 @@ except ImportError:
 
 UDP_PORT = 6510
 
+log = logging.getLogger("egm_client")
 
-def print_egm_feedback(rate: int):
+def print_egm_feedback() -> None:
     """Print EGM feedback.
 
     Parameters
     ----------
     rate
         Frequency of prints in hertz.
-
     """
 
     egm_client = EGMClient(port=UDP_PORT)
 
-    counter = 0
     while True:
         try:
-            ok, feedback = egm_client.receive_from_robot()
-            if ok and counter % rate:
-                print(f"Seq: {counter}\tMsg: {feedback}")
-            else:
-                log.error("False from EGMClient.recieve_from_robot")
+            msg = egm_client.receive_from_robot()
+            print(f"Header:\n{msg.header.seqno}")
+            # print(f"Feedback:\n{msg.feedBack}")
         except Exception as exc:
             log.error(f"Exception raised {exc}")
             log.info("Retrying")
-            counter = 0
 
 
 if __name__ == "__main__":
-    print_egm_feedback(sys.argv[0])
+    print_egm_feedback()
-- 
GitLab