Merge branch 'camera'

# Conflicts:
#	config/machine_settings/st-ten-13.ini
#	src/ui/test_leak/test_leak.ui
This commit is contained in:
sttentest 2025-08-29 10:37:22 +02:00
commit 03eadbe7d7
37 changed files with 433 additions and 37 deletions

View File

@ -11,7 +11,7 @@ ST-TEN-9: st-ten-9
st-ten-10: st-ten-10
st-ten-11: st-ten-11
st-ten-12: st-ten-12
st-ten-13: st-ten-13
st-ten-13: ST-TEN-13
st-ten-14: st-ten-14
st-ten-15: st-ten-15
test-linux: test-linux

View File

@ -16,7 +16,7 @@ vision: present
screwdriver: absent
digital_io: present
barcode_recipe_selection: present
fixture_id: present
fixture_id: absent
discard_box: absent
# enforce_piece_removal: yes

View File

@ -9,9 +9,10 @@ uvc_camera: absent
label_printer: present
neo_pixels: absent
remote_api: absent
hikrobot_sc: present
tecna_t3: present
vision_saver: absent
vision: absent
vision_saver: present
vision: present
screwdriver: absent
digital_io: present
barcode_recipe_selection: present
@ -20,14 +21,12 @@ discard_box: absent
#enforce_piece_removal: yes
[tecna_t3]
connection_type:ethernet
ip_address:10.10.10.5
port: 502
model: t3p
port: COM4
model: t3l
[label_printer]
platform: linux
printer: ZTC-ZD421-203dpi-ZPL
platform: windows
printer: zd421
risoluzione:203
@ -36,7 +35,7 @@ risoluzione:203
id: USB-5862,BID#0
[fixture_rfid]
port: ttyUSB1 #correct way to declare usb in linux
port: COM5
[recipe]
recipe_name_field: codice_ricetta

View File

@ -0,0 +1,10 @@
item {
id: 1
name: 'ok'
color: '0x55AA55'
}
item {
id: 2
name: 'ko'
color: '0xff0000'
}

View File

@ -0,0 +1,10 @@
item {
id: 1
name: 'ok'
color: '0x55AA55'
}
item {
id: 2
name: 'ko'
color: '0xff0000'
}

View File

@ -0,0 +1,10 @@
item {
id: 1
name: 'ok'
color: '0x55AA55'
}
item {
id: 2
name: 'ko'
color: '0xff0000'
}

View File

@ -0,0 +1,10 @@
item {
id: 1
name: 'ok'
color: '0x55AA55'
}
item {
id: 2
name: 'ko'
color: '0xff0000'
}

View File

@ -0,0 +1,10 @@
item {
id: 1
name: 'ok'
color: '0x55AA55'
}
item {
id: 2
name: 'ko'
color: '0xff0000'
}

View File

@ -0,0 +1,10 @@
item {
id: 1
name: 'ok'
color: '0x55AA55'
}
item {
id: 2
name: 'ko'
color: '0xff0000'
}

View File

@ -0,0 +1,10 @@
item {
id: 1
name: 'ok'
color: '0x55AA55'
}
item {
id: 2
name: 'ko'
color: '0xff0000'
}

View File

@ -0,0 +1,10 @@
item {
id: 1
name: 'ok'
color: '0x55AA55'
}
item {
id: 2
name: 'ko'
color: '0xff0000'
}

View File

@ -0,0 +1,10 @@
item {
id: 1
name: 'ok'
color: '0x55AA55'
}
item {
id: 2
name: 'ko'
color: '0xff0000'
}

View File

@ -0,0 +1,20 @@
# O-RING PRESENCE DETECTOR
# FOR FERRARI 000952054
[general]
name: POLYTHEC
instruction: CONTROLLARE ASSENZA PUNTO VERDE
neural_network: none
type: smart_camera
camera_type: hikrobot_sc
num_cameras: 1
rotations=0
solution_name=300146
[markers]
[zones]
p1: 620,630 600,600 ok
[labels]

Binary file not shown.

View File

@ -0,0 +1,20 @@
# O-RING PRESENCE DETECTOR
# FOR FERRARI 000952054
[general]
name: Test assenza
instruction: CONTROLLARE ASSENZA PUNTO VERDE
neural_network: none
type: smart_camera
camera_type: hikrobot_sc
num_cameras: 1
rotations=0
solution_name=670051071
[markers]
[zones]
p1: 620,630 600,600 ok
[labels]

Binary file not shown.

View File

@ -0,0 +1,20 @@
# O-RING PRESENCE DETECTOR
# FOR FERRARI 000952054
[general]
name: Test assenza
instruction: CONTROLLARE ASSENZA PUNTO VERDE
neural_network: none
type: smart_camera
camera_type: hikrobot_sc
num_cameras: 1
rotations=0
solution_name=670051072
[markers]
[zones]
p1: 620,630 600,600 ok
[labels]

Binary file not shown.

View File

@ -0,0 +1,20 @@
# O-RING PRESENCE DETECTOR
# FOR FERRARI 000952054
[general]
name: POLYTHEC
instruction: CONTROLLARE PRESENZA PUNTO VERDE
neural_network: none
type: smart_camera
camera_type: hikrobot_sc
num_cameras: 1
rotations=0
solution_name=123123
[markers]
[zones]
p1: 620,630 600,600 ok
[labels]

View File

@ -0,0 +1,20 @@
# O-RING PRESENCE DETECTOR
# FOR FERRARI 000952054
[general]
name: Test assenza
instruction: CONTROLLARE ASSENZA PUNTO VERDE
neural_network: none
type: smart_camera
camera_type: hikrobot_sc
num_cameras: 1
rotations=0
solution_name=670054129
[markers]
[zones]
p1: 620,630 600,600 ok
[labels]

Binary file not shown.

View File

@ -0,0 +1,20 @@
# O-RING PRESENCE DETECTOR
# FOR FERRARI 000952054
[general]
name: Test assenza
instruction: CONTROLLARE ASSENZA PUNTO VERDE
neural_network: none
type: smart_camera
camera_type: hikrobot_sc
num_cameras: 1
rotations=0
solution_name=670054130
[markers]
[zones]
p1: 620,630 600,600 ok
[labels]

Binary file not shown.

View File

@ -0,0 +1,20 @@
# O-RING PRESENCE DETECTOR
# FOR FERRARI 000952054
[general]
name: Test presenza
instruction: CONTROLLARE PRESENZA PUNTO VERDE
neural_network: none
type: smart_camera
camera_type: hikrobot_sc
num_cameras: 1
rotations=0
solution_name=670054812
[markers]
[zones]
p1: 620,630 600,600 ok
[labels]

Binary file not shown.

View File

@ -0,0 +1,20 @@
# O-RING PRESENCE DETECTOR
# FOR FERRARI 000952054
[general]
name: test Assemza
instruction: CONTROLLARE PRESENZA PUNTO VERDE
neural_network: none
type: smart_camera
camera_type: hikrobot_sc
num_cameras: 1
rotations=0
solution_name=test_absent
[markers]
[zones]
p1: 620,630 600,600 ok
[labels]

Binary file not shown.

View File

@ -0,0 +1,20 @@
# O-RING PRESENCE DETECTOR
# FOR FERRARI 000952054
[general]
name: Test presenza
instruction: CONTROLLARE PRESENZA PUNTO VERDE
neural_network: none
type: smart_camera
camera_type: hikrobot_sc
num_cameras: 1
rotations=0
solution_name=test_presence
[markers]
[zones]
p1: 620,630 600,600 ok
[labels]

Binary file not shown.

View File

@ -1,4 +1,4 @@
echo on
SET mypath=%~dp0
cd %mypath%
.\venv\Scripts\activate.bat && python -O "./src/main.py"
.\venv\Scripts\activate.bat && python -O "./src/main.py" --vision

View File

@ -221,6 +221,9 @@ mv_lib.MV_VS_StartRun.restype = c_int
mv_lib.MV_VS_SetCommandValue.argtypes = [c_void_p, c_char_p]
mv_lib.MV_VS_SetCommandValue.restype = c_int
mv_lib.MV_VS_SetStringValue.argtypes = [c_void_p, c_char_p, c_char_p]
mv_lib.MV_VS_SetStringValue.restype = c_int
def MV_VS_GetFrame(handle, name=""):

View File

@ -17,19 +17,17 @@ class HikrobotSmartCamera(Component):
def __init__(self, config=None, name=None, period=0.2, lazy=True, paused=False, threaded=True):
super().__init__(config=config, name=name, period=period, lazy=lazy, paused=paused, threaded=threaded)
self.simulate = "--sim-camera" in sys.argv
self.num_cameras=1
self.num_cameras = 1
self.connected = False
self.ok_memory = True
self.ok_frames = []
self.ok_results =[]
self.ok_results = []
def config_changed(self):
self.connected = False
global MV_VS_OK
device_info_list = MV_VS_DEVICE_INFO_LIST()
nRet = mv_lib.MV_VS_EnumDevices(ctypes.byref(device_info_list))
enum_ok = False
for n in range(3):
if nRet != MV_VS_OK:
@ -37,7 +35,6 @@ class HikrobotSmartCamera(Component):
return -2
else:
print("Enum devices OK")
if device_info_list.nDeviceNum > 0:
print_device_info(device_info_list)
if device_info_list.nDeviceNum >= self.num_cameras:
@ -75,6 +72,13 @@ class HikrobotSmartCamera(Component):
if not login_ok:
return -2
time.sleep(0.2)
if hasattr(self.config, "vision_config"):
solution_name = self.config.vision_config.get("solution_name", None)
else:
solution_name = None
self.switch_scheme(solution_name,cam_idx)
nRet = mv_lib.MV_VS_SetBoolValue(pHandle, b"CommandImageMode", 0)
if nRet != MV_VS_OK:
self.log.error(f"CAM{cam_idx} CommandImageMode failed! [{nRet&0xFFFFFFFF:#x}]")
@ -90,6 +94,84 @@ class HikrobotSmartCamera(Component):
self.log.error(f"CAM{cam_idx} Start run failed! [{nRet & 0xFFFFFFFF:#x}]")
self.connected = True
def switch_scheme(self,solution_name,cam_idx):
# --- Start of Added Code: Switch Scheme ---
# As per SDK PDF Chapter 5.11, this section switches the camera's active scheme.
# It reads a comma-separated list of scheme names from the config file.
pHandle= self.cam_list[cam_idx]["handle"]
try:
if solution_name is not None:
scheme_name = solution_name
self.log.info(f"CAM{cam_idx} Attempting to switch to scheme: {scheme_name}")
scheme_name_b = bytes(scheme_name,"utf-8")
# Step 1: Set the scheme name to be switched using MV_VS_SetStringValue.
# The node name is "SolutionName".
nRet = mv_lib.MV_VS_SetStringValue(pHandle, b"SrcOperateSolutionName", c_char_p(scheme_name_b))
if nRet != MV_VS_OK:
self.log.error(f"CAM{cam_idx} Set SolutionName failed! nRet [{nRet & 0xFFFFFFFF:#x}]")
# Clean up resources before returning
mv_lib.MV_VS_Logout(pHandle)
mv_lib.MV_VS_DestroyHandle(pHandle)
return -2
# Step 2: Call the scheme switch command using MV_VS_SetCommandValue.
# The command is "CommandSolutionSwitch".
nRet = mv_lib.MV_VS_SetCommandValue(pHandle, b"CommandProjectLoad")
if nRet != MV_VS_OK:
self.log.error(f"CAM{cam_idx} CommandSolutionSwitch failed! nRet [{nRet & 0xFFFFFFFF:#x}]")
# Clean up resources before returning
mv_lib.MV_VS_Logout(pHandle)
mv_lib.MV_VS_DestroyHandle(pHandle)
return -2
self.log.info(f"CAM{cam_idx} Switched to scheme '{scheme_name}' successfully.")
# Step 3: Wait for the scheme switch to complete.
# As per SDK PDF Chapter 3.3.2, we poll "SolutionSwitchStatus".
# A value of 0 indicates the switch is complete.
self.log.info(f"CAM{cam_idx} Waiting for scheme switch to complete...")
switch_timeout = 30 # 30-second timeout for the switch
start_time = time.time()
while True:
if time.time() - start_time > switch_timeout:
self.log.error(f"CAM{cam_idx} Timed out waiting for scheme switch.")
mv_lib.MV_VS_Logout(pHandle)
mv_lib.MV_VS_DestroyHandle(pHandle)
return -2
# Create a variable to store the status
status = ctypes.c_uint8()
nRet = mv_lib.MV_VS_GetEnumValue(pHandle, b"ScPushState", byref(status))
if nRet != MV_VS_OK:
self.log.error(f"CAM{cam_idx} Get SolutionSwitchStatus failed! nRet [{nRet & 0xFFFFFFFF:#x}]")
mv_lib.MV_VS_Logout(pHandle)
mv_lib.MV_VS_DestroyHandle(pHandle)
return -2
if status.value == 0:
self.log.info(f"CAM{cam_idx} Scheme switch completed successfully.")
break
else:
self.log.info(f"CAM{cam_idx} Scheme switch not yet complete. Waiting...")
time.sleep(1)
else:
self.log.warning(
f"CAM{cam_idx} No scheme name defined in config for this camera index. Using default scheme.")
except KeyError:
self.log.warning("'schemes' key not found in vision_config. Using camera's default/current scheme.")
except Exception as e:
self.log.error(f"CAM{cam_idx} An unexpected error occurred during scheme switching: {e}")
# Clean up resources before returning
mv_lib.MV_VS_Logout(pHandle)
mv_lib.MV_VS_DestroyHandle(pHandle)
return -2
# --- End of Added Code ---
@Component.reconfig_on_error
def _get(self):
if not self.connected:
@ -152,6 +234,9 @@ class HikrobotSmartCamera(Component):
if len(self.cam_list) == self.num_cameras:
# Dummy acquisition for each camera to avoid reading past images
for cam_idx in range(self.num_cameras):
solution_name = self.config.vision_config.get("solution_name", None)
self.log.info(f"Setting Solution {solution_name} to camera {cam_idx}")
self.switch_scheme(solution_name,cam_idx)
cam = self.cam_list[cam_idx]
nRet = mv_lib.MV_VS_SetCommandValue(cam["handle"], b"AcquisitionStart")
frame_res = MV_VS_GetFrame(cam["handle"])

View File

@ -62,6 +62,7 @@ class Vision(Component):
status_signal = pyqtSignal(object)
loading_model_signal = pyqtSignal(object)
recipe_changed_signal = pyqtSignal(str) # Signal to notify when a recipe is changed
def __init__(self, config=None, name=None, period=None, lazy=True, paused=False, threaded=True):
super().__init__(config=config, name=name, period=period, lazy=lazy, paused=paused, threaded=threaded)
@ -108,7 +109,8 @@ class Vision(Component):
self.recipes_dir = Path(self.config[self.name].get("recipes_dir", "./config/vision/recipes"))
self.set_recipe(vision_recipe)
self.vision_recipe=vision_recipe
self.config.vision_config=self.vision_config
# Store vision_config in the config dictionary instead of as an attribute
self.config.vision_config = self.vision_config
self.recipe_watcher = QFileSystemWatcher([])
self.recipe_watcher.fileChanged.connect(self._set_recipe)
# LOAD MODEL
@ -175,12 +177,19 @@ class Vision(Component):
read = config.read(self.recipe_path)
if len(read) != 1 or self.recipe_path not in read:
raise AssertionError("Recipe could not be read.")
os.path.splitext(os.path.basename(read[0]))[0]
recipe_name = os.path.splitext(os.path.basename(read[0]))[0]
self.vision_config = config._sections.get("general", None)
self.markers = self.parse_markers(config._sections.get("markers", None))
self.zones = self.parse_zones(config._sections.get("zones", None))
self.labels = self.parse_labels(config._sections.get("labels", None))
self.recipe_watcher.addPath(str(self.recipe_path))
# Emit a signal that can be connected to the HikrobotSmartCamera component
self.log.info(f"Recipe loaded: {recipe_name}")
# Emit the recipe_changed_signal with the recipe name
# This signal can be connected to the HikrobotSmartCamera component in main.py
self.recipe_changed_signal.emit(recipe_name)
except Exception:
self.log.exception(traceback.format_exc())
self.log.exception(f"Error reading {self.recipe_path!r}:")

View File

@ -179,6 +179,8 @@ try:
if "vision" in self.components and "hikrobot_sc" in self.components:
self.components["vision"].set_sources({"hikrobot_sc": self.components["hikrobot_sc"].out})
# connect tecna to screwdriver
if "screwdriver" in self.components and "tecna_t3" in self.components:
self.components["tecna_t3"].set_requestors({"screwdriver": self.components["screwdriver"].request})

View File

@ -45,24 +45,7 @@
</widget>
</item>
<item row="0" column="1">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QComboBox" name="label_template_cb"/>
</item>
<item>
<widget class="QLabel" name="template_label_l">
<property name="font">
<font>
<pointsize>10</pointsize>
<italic>true</italic>
</font>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
</layout>
<widget class="QComboBox" name="label_template_cb"/>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_30">

View File

@ -317,6 +317,13 @@ class Test(Widget):
self.user_l.setText("ADMIN")
self.user_l.setStyleSheet("QLabel { color: red; }")
#Refresh Recipe_Selection UI to show admin buttons
if "select_recipe" in self.cycle_available_steps and self.cycle_available_steps["select_recipe"].widget:
recipe_selection = self.cycle_available_steps["select_recipe"].widget
if hasattr(recipe_selection, "refresh"):
recipe_selection.refresh()
self.log.info("Recipe Selection UI refreshed to show admin buttons")
self.log.info(f"Temporary admin privileges enabled for user: {session.username}")
return True
@ -343,6 +350,13 @@ class Test(Widget):
else:
self.user_l.setStyleSheet("")
# Refresh Recipe_Selection UI to hide admin buttons
if "select_recipe" in self.cycle_available_steps and self.cycle_available_steps["select_recipe"].widget:
recipe_selection = self.cycle_available_steps["select_recipe"].widget
if hasattr(recipe_selection, "refresh"):
recipe_selection.refresh()
self.log.info("Recipe Selection UI refreshed to hide admin buttons")
self.log.info(f"Temporary admin privileges disabled for user: {session.username}")
return True

View File

@ -2277,6 +2277,37 @@ border: 1px solid black;
<property name="styleSheet">
<string notr="true">background-color: rgb(255, 255, 255);
border: 1px solid black;
</string>
</property>
<property name="text">
<string>-</string>
</property>
</widget>
</item>
<item row="9" column="4" alignment="Qt::AlignRight">
<widget class="QLabel" name="valore_PID">
<property name="font">
<font>
<pointsize>16</pointsize>
<bold>false</bold>
</font>
</property>
<property name="text">
<string>Valore PID</string>
</property>
</widget>
</item>
<item row="9" column="7">
<widget class="QLabel" name="valore_PID_l">
<property name="font">
<font>
<pointsize>16</pointsize>
<bold>false</bold>
</font>
</property>
<property name="styleSheet">
<string notr="true">background-color: rgb(255, 255, 255);
border: 1px solid black;
</string>
</property>
<property name="text">