Skip to content

Commit cadde47

Browse files
committed
[DeviceManager] improvements & [FW] Updated for IMX296 support
1 parent d92ed41 commit cadde47

File tree

2 files changed

+113
-70
lines changed

2 files changed

+113
-70
lines changed

depthai-core

utilities/device_manager.py

Lines changed: 112 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
from typing import Dict
1111
import platform
1212
import os
13+
import numpy
1314

1415
if USE_OPENCV:
1516
# import cv2
@@ -20,10 +21,14 @@
2021

2122
SCRIPT_DIR = os.path.dirname(os.path.realpath(__file__))
2223

24+
PLATFORM_ICON_PATH = None
2325
if platform.system() == 'Windows':
24-
sg.set_global_icon(f'{SCRIPT_DIR}/assets/icon.ico')
26+
PLATFORM_ICON_PATH = f'{SCRIPT_DIR}/assets/icon.ico'
2527
else:
26-
sg.set_global_icon(f'{SCRIPT_DIR}/assets/icon.png')
28+
PLATFORM_ICON_PATH = f'{SCRIPT_DIR}/assets/icon.png'
29+
30+
# Apply icon globally
31+
sg.set_global_icon(PLATFORM_ICON_PATH)
2732

2833
CONF_TEXT_POE = ['ipTypeText', 'ipText', 'maskText', 'gatewayText', 'dnsText', 'dnsAltText', 'networkTimeoutText', 'macText']
2934
CONF_INPUT_POE = ['staticBut', 'dynamicBut', 'ip', 'mask', 'gateway', 'dns', 'dnsAlt', 'networkTimeout', 'mac']
@@ -38,27 +43,35 @@ def PrintException():
3843
filename = f.f_code.co_filename
3944
print('Exception in {}, line {}; {}'.format(filename, lineno, exc_obj))
4045

41-
def check_ip(s: str, req = True):
46+
def Popup(msg, window):
47+
main_window_location = window.CurrentLocation()
48+
main_window_size = window.size
49+
# Calculate the centered location for the new window
50+
centered_location = (main_window_location[0] + (main_window_size[0] - 50) // 2,
51+
main_window_location[1] + (main_window_size[1] - 50) // 2)
52+
return sg.Popup(msg, location=centered_location)
53+
54+
def check_ip(window, s: str, req = True):
4255
if s == "":
4356
return not req
4457
spl = s.split(".")
4558
if len(spl) != 4:
46-
sg.Popup("Wrong IP format.\nValue should be similar to 255.255.255.255")
59+
Popup("Wrong IP format.\nValue should be similar to 255.255.255.255", window=window)
4760
return False
4861
for num in spl:
4962
if 255 < int(num):
50-
sg.Popup("Wrong IP format.\nValues can not be above 255!")
63+
Popup("Wrong IP format.\nValues can not be above 255!", window=window)
5164
return False
5265
return True
5366

54-
def check_mac(s):
67+
def check_mac(window, s):
5568
if s.count(":") != 5:
56-
sg.Popup("Wrong MAC format.\nValue should be similar to FF:FF:FF:FF:FF:FF")
69+
Popup("Wrong MAC format.\nValue should be similar to FF:FF:FF:FF:FF:FF", window=window)
5770
return False
5871
for i in s.split(":"):
5972
for j in i:
6073
if j > "F" or (j < "A" and not j.isdigit()) or len(i) != 2:
61-
sg.Popup("Wrong MAC format.\nValue should be similar to FF:FF:FF:FF:FF:FF")
74+
Popup("Wrong MAC format.\nValue should be similar to FF:FF:FF:FF:FF:FF", window=window)
6275
return False
6376
return True
6477

@@ -113,7 +126,7 @@ def wait(self):
113126

114127

115128
class SelectIP:
116-
def __init__(self):
129+
def __init__(self, window):
117130
self.ok = False
118131
layout = [
119132
[sg.Text("Specify the custom IP of the OAK PoE\ncamera you want to connect to")],
@@ -124,15 +137,24 @@ def __init__(self):
124137
[sg.Submit(), sg.Cancel()],
125138
]
126139
self.window = sg.Window("Specify IP", layout, size=(300,110), modal=True, finalize=True)
140+
141+
main_window_location = window.CurrentLocation()
142+
main_window_size = window.size
143+
new_window_size = self.window.size
144+
# Calculate the centered location for the new window
145+
centered_location = (main_window_location[0] + (main_window_size[0] - new_window_size[0]) // 2,
146+
main_window_location[1] + (main_window_size[1] - new_window_size[1]) // 2)
147+
self.window.move(*centered_location)
148+
127149
def wait(self):
128150
event, values = self.window.Read()
129151
self.window.close()
130-
if str(event) == "Cancel" or values is None or not check_ip(values["ip"]):
152+
if str(event) == "Cancel" or values is None or not check_ip(self.window, values["ip"]):
131153
return False, ""
132154
return True, values["ip"]
133155

134156
class SearchDevice:
135-
def __init__(self):
157+
def __init__(self, window):
136158
self.infos = []
137159
layout = [
138160
[sg.Text("Select an OAK camera you would like to connect to.", font=('Arial', 10, 'bold'))],
@@ -156,12 +178,22 @@ def __init__(self):
156178
[sg.Button('Search', size=(15, 2), font=('Arial', 10, 'bold'))],
157179
]
158180
self.window = sg.Window("Select Device", layout, size=(550,375), modal=True, finalize=True)
181+
182+
main_window_location = window.CurrentLocation()
183+
main_window_size = window.size
184+
new_window_size = self.window.size
185+
# Calculate the centered location for the new window
186+
centered_location = (main_window_location[0] + (main_window_size[0] - new_window_size[0]) // 2,
187+
main_window_location[1] + (main_window_size[1] - new_window_size[1]) // 2)
188+
self.window.move(*centered_location)
189+
159190
self.search_devices()
160191

161192
def search_devices(self):
162193
self.infos = dai.XLinkConnection.getAllConnectedDevices()
163194
if not self.infos:
164-
sg.Popup("No devices found.")
195+
pass
196+
# sg.Popup("No devices found.")
165197
else:
166198
rows = []
167199
for info in self.infos:
@@ -257,43 +289,43 @@ def factoryReset(device: dai.DeviceInfo, type: dai.DeviceBootloader.Type):
257289

258290
def connectAndStartStreaming(dev):
259291

260-
# OpenCV
261-
if USE_OPENCV:
292+
with dai.Device(dev) as d:
262293
# Create pipeline
263294
pipeline = dai.Pipeline()
295+
# OpenCV
296+
if USE_OPENCV:
297+
camRgb = pipeline.create(dai.node.ColorCamera)
298+
camRgb.setIspScale(1,3)
299+
videnc = pipeline.create(dai.node.VideoEncoder)
300+
videnc.setDefaultProfilePreset(camRgb.getFps(), videnc.Properties.Profile.MJPEG)
301+
xout = pipeline.create(dai.node.XLinkOut)
302+
xout.setStreamName("mjpeg")
303+
camRgb.video.link(videnc.input)
304+
videnc.bitstream.link(xout.input)
264305

265-
camRgb = pipeline.create(dai.node.ColorCamera)
266-
camRgb.setIspScale(1,3)
267-
videnc = pipeline.create(dai.node.VideoEncoder)
268-
videnc.setDefaultProfilePreset(camRgb.getFps(), videnc.Properties.Profile.MJPEG)
269-
xout = pipeline.create(dai.node.XLinkOut)
270-
xout.setStreamName("mjpeg")
271-
camRgb.video.link(videnc.input)
272-
videnc.bitstream.link(xout.input)
273-
274-
with dai.Device(pipeline, dev) as d:
275306
while not d.isClosed():
276307
mjpeg = d.getOutputQueue('mjpeg').get()
277308
frame = cv2.imdecode(mjpeg.getData(), cv2.IMREAD_UNCHANGED)
278309
cv2.imshow('Color Camera', frame)
279310
if cv2.waitKey(1) == ord('q'):
280311
cv2.destroyWindow('Color Camera')
281312
break
282-
else:
283-
# Create pipeline (no opencv)
284-
pipeline = dai.Pipeline()
285-
camRgb = pipeline.create(dai.node.ColorCamera)
286-
camRgb.setIspScale(1,3)
287-
camRgb.setPreviewSize(camRgb.getIspSize())
288-
camRgb.setColorOrder(camRgb.Properties.ColorOrder.RGB)
289-
290-
xout = pipeline.create(dai.node.XLinkOut)
291-
xout.input.setQueueSize(2)
292-
xout.input.setBlocking(False)
293-
xout.setStreamName("color")
294-
camRgb.preview.link(xout.input)
295-
296-
with dai.Device(pipeline, dev) as d:
313+
else:
314+
camRgb = pipeline.create(dai.node.ColorCamera)
315+
camRgb.setIspScale(1,3)
316+
firstSensor = d.getConnectedCameraFeatures()[0]
317+
camRgb.setPreviewSize(firstSensor.width // 3, firstSensor.height // 3)
318+
camRgb.setColorOrder(camRgb.Properties.ColorOrder.RGB)
319+
320+
xout = pipeline.create(dai.node.XLinkOut)
321+
xout.input.setQueueSize(2)
322+
xout.input.setBlocking(False)
323+
xout.setStreamName("color")
324+
camRgb.preview.link(xout.input)
325+
326+
# Start pipeline
327+
d.startPipeline(pipeline)
328+
297329
frame = d.getOutputQueue('color', 2, False).get()
298330
width, height = frame.getWidth(), frame.getHeight()
299331

@@ -540,7 +572,7 @@ class DeviceManager:
540572

541573
def __init__(self) -> None:
542574
self.window = sg.Window(title="Device Manager",
543-
icon="assets/icon.png",
575+
icon=PLATFORM_ICON_PATH,
544576
layout=layout,
545577
size=(645, 380),
546578
finalize=True # So we can do First search for devices
@@ -556,7 +588,7 @@ def isPoE(self) -> bool:
556588
return self.bl.getType() == dai.DeviceBootloader.Type.NETWORK
557589
except Exception as ex:
558590
PrintException()
559-
sg.Popup(f'{ex}')
591+
Popup(f'{ex}', self.window)
560592

561593
def isUsb(self) -> bool:
562594
return not self.isPoE()
@@ -577,7 +609,7 @@ def run(self) -> None:
577609
device = self.device
578610
if deviceStateTxt(device.state) == "BOOTED":
579611
# device is already booted somewhere else
580-
sg.Popup("Device is already booted somewhere else!")
612+
Popup("Device is already booted somewhere else!", self.window)
581613
else:
582614
self.resetGui()
583615
self.bl = connectToDevice(device)
@@ -588,7 +620,7 @@ def run(self) -> None:
588620
self.window.Element('progress').update("No device selected.")
589621
elif event == "Search":
590622
self.getDevices() # Re-search devices for dropdown
591-
selDev = SearchDevice()
623+
selDev = SearchDevice(window=self.window)
592624
di = selDev.wait()
593625
if di is not None:
594626
self.resetGui()
@@ -601,7 +633,7 @@ def run(self) -> None:
601633
self.getConfigs()
602634
self.unlockConfig()
603635
elif event == "Specify IP":
604-
select = SelectIP()
636+
select = SelectIP(window=self.window)
605637
ok, ip = select.wait()
606638
if ok:
607639
self.resetGui()
@@ -655,14 +687,14 @@ def run(self) -> None:
655687
print("Factory reset cancelled.")
656688

657689
elif event == "Flash configuration":
658-
self.flashConfig()
659-
self.getConfigs()
660-
self.resetGui()
661-
if self.isUsb():
662-
self.unlockConfig()
663-
else:
664-
self.devices.clear()
665-
self.window.Element('devices').update("Search for devices", values=[])
690+
if self.flashConfig() is not None:
691+
self.getConfigs()
692+
self.resetGui()
693+
if self.isUsb():
694+
self.unlockConfig()
695+
else:
696+
self.devices.clear()
697+
self.window.Element('devices').update("Search for devices", values=[])
666698
elif event == "Clear configuration":
667699
self.clearConfig()
668700
self.getConfigs()
@@ -677,17 +709,17 @@ def run(self) -> None:
677709
confJson = self.bl.readConfigData()
678710
sg.popup_scrolled(confJson, title='Configuration')
679711
except Exception as ex:
680-
sg.popup(f'No existing config to view ({ex})')
712+
Popup(f'No existing config to view ({ex})', self.window)
681713

682714
elif event == "Flash application":
683715
file = sg.popup_get_file("Select .dap file", file_types=(('DepthAI Application Package', '*.dap'), ('All Files', '*.* *')))
684716
flashFromFile(file, self.bl)
685717
elif event == "Remove application":
686718
try:
687719
self.bl.flashClear()
688-
sg.popup(f'Successfully removed application')
720+
Popup(f'Successfully removed application', self.window)
689721
except Exception as ex:
690-
sg.popup(f"Couldn't remove application ({ex})")
722+
sg.popup(f"Couldn't remove application ({ex})", self.window)
691723

692724
elif event.startswith("_unique_configBtn"):
693725
self.window['-COL1-'].update(visible=False)
@@ -713,7 +745,7 @@ def run(self) -> None:
713745

714746
elif event == "recoveryMode":
715747
if recoveryMode(self.bl):
716-
sg.Popup(f'Device successfully put into USB recovery mode.')
748+
Popup(f'Device successfully put into USB recovery mode.', self.window)
717749
# Device will reboot, close previous and reset GUI
718750
self.closeDevice()
719751
self.resetGui()
@@ -872,6 +904,12 @@ def resetGui(self):
872904
self.window.Element('commit').update("-version-")
873905
self.window.Element('devState').update("-state-")
874906

907+
# Move back to 'About' page
908+
self.window['-COL1-'].update(visible=True)
909+
self.window['-COL2-'].update(visible=False)
910+
self.window['-COL3-'].update(visible=False)
911+
self.window['-COL4-'].update(visible=False)
912+
875913
def closeDevice(self):
876914
if self.bl is not None:
877915
self.bl.close()
@@ -885,7 +923,7 @@ def getDevices(self):
885923
deviceInfos = dai.XLinkConnection.getAllConnectedDevices()
886924
if not deviceInfos:
887925
self.window.Element('devices').update("No devices")
888-
sg.Popup("No devices found.")
926+
# sg.Popup("No devices found.")
889927
else:
890928
for deviceInfo in deviceInfos:
891929
deviceTxt = deviceInfo.getMxId()
@@ -896,7 +934,7 @@ def getDevices(self):
896934
self.window.Element('devices').update("Select device", values=listedDevices)
897935
except Exception as ex:
898936
PrintException()
899-
sg.Popup(f'{ex}')
937+
Popup(f'{ex}', window=self.window)
900938

901939
def flashConfig(self):
902940
values = self.values
@@ -912,22 +950,22 @@ def flashConfig(self):
912950
try:
913951
if self.isPoE:
914952
if self.values['staticBut']:
915-
if check_ip(values['ip']) and check_ip(values['mask']) and check_ip(values['gateway'], req=False):
953+
if check_ip(self.window, values['ip']) and check_ip(self.window, values['mask']) and check_ip(self.window, values['gateway'], req=False):
916954
conf.setStaticIPv4(values['ip'], values['mask'], values['gateway'])
917955
else:
918956
raise Exception('IP or Mask missing using static IP configuration')
919957
else:
920-
if check_ip(values['ip'], req=False) and check_ip(values['mask'], req=False) and check_ip(values['gateway'], req=False):
958+
if check_ip(self.window, values['ip'], req=False) and check_ip(self.window, values['mask'], req=False) and check_ip(self.window, values['gateway'], req=False):
921959
conf.setDynamicIPv4(values['ip'], values['mask'], values['gateway'])
922960

923961
conf.setDnsIPv4(values['dns'], values['dnsAlt'])
924962
if values['networkTimeout'] != "":
925963
if int(values['networkTimeout']) >= 0:
926964
conf.setNetworkTimeout(timedelta(seconds=int(values['networkTimeout']) / 1000))
927965
else:
928-
sg.Popup("Values can not be negative!")
966+
Popup("Values can not be negative!", window=self.window)
929967
if values['mac'] != "":
930-
if check_mac(values['mac']):
968+
if check_mac(self.window, values['mac']):
931969
conf.setMacAddress(values['mac'])
932970
else:
933971
conf.setMacAddress('00:00:00:00:00:00')
@@ -936,29 +974,34 @@ def flashConfig(self):
936974
if int(values['usbTimeout']) >= 0:
937975
conf.setUsbTimeout(timedelta(seconds=int(values['usbTimeout']) / 1000))
938976
else:
939-
sg.Popup("Values can not be negative!")
977+
Popup("Values can not be negative!", window=self.window)
940978
if values['usbSpeed'] != "":
941979
conf.setUsbMaxSpeed(getattr(dai.UsbSpeed, values['usbSpeed']))
942980

943981
success, error = self.bl.flashConfig(conf)
944982
if not success:
945-
sg.Popup(f"Flashing failed: {error}")
983+
Popup(f"Flashing failed: {error}", window=self.window)
984+
return False
946985
else:
947-
sg.Popup("Flashing successful.")
986+
Popup("Flashing successful.", window=self.window)
987+
return True
988+
948989
except Exception as ex:
949990
PrintException()
950-
sg.Popup(f'{ex}')
991+
Popup(f'{ex}', window=self.window)
992+
993+
return None
951994

952995
def clearConfig(self):
953996
try:
954997
success, error = self.bl.flashConfigClear()
955998
if not success:
956-
sg.Popup(f"Clearing configuration failed: {error}")
999+
Popup(f"Clearing configuration failed: {error}", window=self.window)
9571000
else:
958-
sg.Popup("Successfully cleared configuration.")
1001+
Popup("Successfully cleared configuration.", window=self.window)
9591002
except Exception as ex:
9601003
PrintException()
961-
sg.Popup(f'{ex}')
1004+
Popup(f'{ex}', window=self.window)
9621005

9631006

9641007
app = DeviceManager()

0 commit comments

Comments
 (0)