def test_current_plant():
    print("\n")
    """
    Test of initialization of ImageProcessor with a fake camera object
    """
    print("Making fake camera")
    cam = Camera(dummy=True, dummy_img=Path(__file__).parent /\
                 "data/imgs/plants/1/1-cc-0.jpg")
    img_proc = ImageProcessor(Path(__file__).parent / 'plant_model.pth')
    img_proc._assign_camera(cam)
    assert img_proc.currentPlant() == img_proc.neural_net.NO_PLANT
    assert img_proc.plantPresent() == False
    print("\n")
def test_add_plant_2():
    print("\n")
    cam = Camera(dummy=True, dummy_img=Path(__file__).parent /\
                 "data/imgs/plants/2/2-ze-0.jpg")
    img_proc = ImageProcessor(Path(__file__).parent / 'plant_model.pth')
    img_proc._assign_camera(cam)
    # Before it switches should be nothing present
    assert img_proc.currentPlant() == img_proc.neural_net.NO_PLANT
    assert img_proc.plantPresent() == False
    img_proc.resetDetected()
    assert img_proc.currentPlant() == 1
    assert img_proc.plantPresent() == True
    print(f"Plant {img_proc.currentPlant()} detected after update")
    print("\n")
def test_init_bad_camera():
    """
    Test of initialization of ImageProcessor with a bad camera object.
    Test fails if the _assign_camera() function fails to realize that
    the camera object is not good.
    """
    print("\n")
    img_proc = ImageProcessor(Path(__file__).parent / 'plant_model.pth')
    try:
        print("Trying to assign bad camera object to processor")
        img_proc._assign_camera(None)
        assert False
    except TypeError:
        print("Assignment failed successfuly")
        assert True
    print("\n")
def test_init_good_camera():
    """
    Test of initialization of ImageProcessor with a good camera object
    """
    print("\n")
    print("Trying to find camera")
    try:
        cam = Camera()
        assert (False)
    except:
        print("No physical camera found, using software camera")
        cam = Camera(dummy=True, dummy_img=Path(__file__).parent /\
                    "data/imgs/plants/1/1-cc-0.jpg")
    img_proc = ImageProcessor(Path(__file__).parent / 'plant_model.pth')
    img_proc._assign_camera(cam)
    assert isinstance(img_proc, ImageProcessor)
    assert isinstance(img_proc.camera, Camera)
    print("Camera successfully bound to image processor")
    print("\n")
def test_remove_plant_1():
    print("\n")
    cam = Camera(dummy=True, dummy_img=Path(__file__).parent /\
                 "data/imgs/plants/1/1-cc-132.jpg")
    img_proc = ImageProcessor(Path(__file__).parent / 'plant_model.pth')
    img_proc._assign_camera(cam)

    img_proc.resetDetected()
    assert img_proc.currentPlant() == 0
    assert img_proc.plantPresent() == True
    print(f"Plant detected {img_proc.plantPresent()} before removal")

    cam = Camera(dummy=True, dummy_img=Path(__file__).parent /\
                 "data/imgs/plants/5/5-no-10.jpg")
    img_proc._assign_camera(cam)
    img_proc.resetDetected()
    assert img_proc.currentPlant() == img_proc.neural_net.NO_PLANT
    assert img_proc.plantPresent() == False
    print(f"Plant detected {img_proc.plantPresent()} after update")
예제 #6
0
class ImageServer:
    """image server is responsible for sending data to both the servers
    plant management database as well as the video streaming service
    """
    WATER_COMMAND = '0001'
    LIGHT_COMMAND = '0010'
    PLANTID_COMMAND = '0100'
    UPDATE_COMMAND = '0111'
    ACK = '01100001'
    NACK = '01101110'
    IDEN_INTERVAL = 300
    RETRIES = 5
    ENCODING = [int(cv2.IMWRITE_JPEG_QUALITY),
                90]  # Quality affects stream rate

    def __init__(self,
                 send,
                 recv,
                 stream=1337,
                 ard_port="/dev/ttyACM0",
                 baud=9600):
        print("Binding Server Ports ... ")
        self.send_port = send
        self.recv_port = recv
        self.s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

        print("Binding video streaming service")
        self.stream_port = stream
        self.stream = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.stream.connect(("localhost", self.stream_port))
        self.connection = self.stream.makefile("wb")

        # Start listening
        my_address = ('192.168.43.84', recv)
        self.s.bind(my_address)

        self.server_address = ('192.168.43.84', send)

        print("Setting up image processor ... ")
        # Set up image processing
        #self.cam = Camera(True, Path("./data/imgs/plants/1/1-cc-0.jpg"))
        self.cam = Camera()
        self.identifier = ImageProcessor(Path("./plant_model.pth"))

        print("Binding camera ... ")
        assert self.identifier._assign_camera(self.cam)

        print("Connecting to arduino ... ")
        try:
            self.arduino = ArduinoPlant(ard_port, baud)
        except:
            print("Arduino not found")
            self.arduino = None

    def stream_frame(self) -> int:
        """Encodes and streams one frame to the client

        :return: The time it took to encode and stream the frame in s
        """
        start_time = time.time()
        frame = self.cam.requestData()
        res, frame = cv2.imencode(".jpg", frame, self.ENCODING)
        frame_data = pickle.dumps(frame, 0)
        size = len(
            frame_data)  # We need this to tell the client how far to read

        self.stream.sendall(struct.pack(">L", size) + frame_data)
        return time.time() - start_time

    def run_server(self):
        """
        The main server loop
        """
        print("Running Server")
        self.s.setblocking(0)
        last_checked_id = int(time.time())
        last_checked_conditions = int(time.time())
        last_cmd = int(time.time())
        while True:
            try:
                data, address = self.s.recvfrom(1024)
                if self.process_command(data.decode()):
                    print("Command Completed Successfully")
                else:
                    print("Command Failed Successfully")
            except BlockingIOError:
                pass

            if (int(time.time()) -
                    last_cmd) > self.IDEN_INTERVAL:  # Space cmds by 60 s
                if (int(time.time()) - last_checked_id) >= (
                    (time.time()) - last_checked_conditions):
                    print("Re-identifying plant")
                    last_checked_id = int(time.time())
                    is_new = self.re_identify()
                    if is_new:
                        self.update_plant_id()
                else:
                    print("Sending condition update to server")
                    self.update_server()
                    last_checked_conditions = int(time.time())

                last_cmd = int(time.time())

            self.stream_frame()

    def wait_ack(self):
        # Set blocking and wait for ack before continuing, bad
        # practice but python cant do futures
        print("Waiting for ack")
        self.s.setblocking(1)
        data = self.s.recv(1024)
        self.s.setblocking(0)
        if data.decode() == self.NACK:
            print(f"NACK found ...")
            return False
        elif data.decode() == self.ACK:
            print("Got ack ...")
            return True
        else:
            print(data)
            print("Unkown return while waiting for ack")
            return False

    def update_plant_id(self) -> bool:
        """
        When the image processor detects a change in the current plant, we need
        to update the server
        """
        # Encode and send plant string
        print("Plant changed, updating server")
        plant_id_str = str(bin(self.identifier.currentPlant() +
                               1))[2:].zfill(4)
        print(plant_id_str)
        print(self.PLANTID_COMMAND + plant_id_str)
        for i in range(self.RETRIES):
            self.s.sendto(
                (self.PLANTID_COMMAND + plant_id_str).encode("utf-8"),
                self.server_address)

            # Set blocking and wait for ack before continuing, bad
            # practice but python cant do futures
            if self.wait_ack():
                break
            print(f"Retrying {self.RETRIES-i}")
        else:
            return False
        print("Done updating plant id")
        return True

    def re_identify(self) -> bool:
        current_plant = self.identifier.currentPlant()
        self.identifier.resetDetected()
        return current_plant != self.identifier.currentPlant()

    def update_server(self) -> bool:
        """
        Called to update server with up to date data
        """
        try:
            conditions = self.arduino.updateData()
        except:
            print("Update server could not access arduino")
            return False
        print(conditions)
        if conditions == "error":
            print("Could not get updated data from arduino")
            return False
        else:
            for i in range(self.RETRIES):
                self.s.sendto(conditions.encode("utf-8"), self.server_address)
                if self.wait_ack():
                    break
                print(f"Retrying ... {self.RETRIES - i}")
            else:
                print("Max update retried exceeded ...")
                return False
        print("Done updating server")
        return True

    def process_command(self, data) -> bool:
        """
        Responsible for executing the commands recieved from the server
        """
        if data[:4] == self.WATER_COMMAND:
            print('Received water command.')
            watered = self.arduino.waterPlant(int(data[4:], 2))
            if watered:
                self.s.sendto(self.ACK.encode("utf-8"), self.server_address)
                print("Sent ACK")
                return True
            else:
                self.s.sendto(self.NACK.encode("utf-8"), self.server_address)
                print("Sent NACK")

        elif data[:4] == self.LIGHT_COMMAND:
            print('Received light command.')
            light_set = self.arduino.setLightLevel(int(data[4:], 2))
            if light_set:
                self.s.sendto(self.ACK.encode("utf-8"), self.server_address)
                print("Sent ACK")
                return True
            else:
                self.s.sendto(self.NACK.encode("utf-8"), self.server_address)
                print("Sent NACK")

        return False