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")
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