Skip to content

Commit

Permalink
Add network configuration
Browse files Browse the repository at this point in the history
This commit adds a wrapper for NetworkConfiguration datamodel as well as
wrappers for new Camera class methods `network_configuration` and
`apply_network_configuration`.
  • Loading branch information
johningve committed Jun 5, 2024
1 parent a0608ac commit ab24745
Show file tree
Hide file tree
Showing 11 changed files with 385 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -714,6 +714,7 @@ def generate_all_datamodels(dest_dir: Path) -> None:
["datetime"],
),
(_zivid.CameraIntrinsics, "camera_intrinsics.py", []),
(_zivid.NetworkConfiguration, "network_configuration.py", []),
]:
_generate_datamodel_frontend(
internal_class=internal_class,
Expand Down
1 change: 1 addition & 0 deletions modules/_zivid/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@
CameraInfo,
infield_correction,
Matrix4x4,
NetworkConfiguration,
data_model,
PixelMapping,
projection,
Expand Down
1 change: 1 addition & 0 deletions modules/zivid/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,4 @@
from zivid.camera_info import CameraInfo
from zivid.camera_intrinsics import CameraIntrinsics
from zivid.matrix4x4 import Matrix4x4
from zivid.network_configuration import NetworkConfiguration
50 changes: 48 additions & 2 deletions modules/zivid/camera.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
"""Contains Camera class."""
import _zivid
from zivid.camera_info import _to_camera_info
from zivid.camera_state import _to_camera_state
from zivid.frame import Frame
from zivid.frame_2d import Frame2D
from zivid.network_configuration import NetworkConfiguration, _to_internal_network_configuration, \
_to_network_configuration
from zivid.settings import Settings, _to_internal_settings
from zivid.settings_2d import Settings2D, _to_internal_settings2d
from zivid.camera_info import _to_camera_info
from zivid.camera_state import _to_camera_state


class Camera:
Expand Down Expand Up @@ -90,6 +92,50 @@ def disconnect(self):
"""Disconnect from the camera and free all resources associated with it."""
self.__impl.disconnect()

@property
def network_configuration(self):
"""Get the network configuration of the camera.
Returns:
NetworkConfiguration instance
"""
return _to_network_configuration(self.__impl.network_configuration)

def apply_network_configuration(self, network_configuration):
"""
Applies the given network configuration to the camera.
Args:
network_configuration (NetworkConfiguration): The network configuration to apply to the camera.
This method blocks until the camera has finished applying the network configuration, or raises an exception if
the camera does not reappear on the network before a timeout occurs.
This method can be used even if the camera is inaccessible via TCP/IP, for example a camera that is on a
different subnet to the PC, or a camera with an IP conflict, as it uses UDP multicast to communicate with the
camera.
This method can also be used to configure cameras that require a firmware update, as long as the firmware
supports network configuration via UDP multicast. This has been supported on all firmware versions included
with SDK 2.10.0 or newer. This method will raise an exception if the camera firmware is too old to support
UDP multicast.
This method will raise an exception if the camera status (see CameraState.Status) is "busy", "connected",
"connecting" or "disconnecting". If the status is "connected", then you must first call disconnect() before
calling this method.
Raises:
TypeError: If the provided network_configuration is not an instance of NetworkConfiguration.
"""
if not isinstance(network_configuration, NetworkConfiguration):
raise TypeError(
"Unsupported type, expected: {expected_type}, got: {value_type}".format(
expected_type=NetworkConfiguration,
value_type=type(network_configuration),
)
)
self.__impl.apply_network_configuration(_to_internal_network_configuration(network_configuration))

def write_user_data(self, user_data):
"""Write user data to camera. The total number of writes supported depends on camera model and size of data.
Expand Down
2 changes: 2 additions & 0 deletions modules/zivid/camera_state.py
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,7 @@ def valid_values(cls):
return list(cls._valid_values.keys())

class Status:
applyingNetworkConfiguration = "applyingNetworkConfiguration"
available = "available"
busy = "busy"
connected = "connected"
Expand All @@ -302,6 +303,7 @@ class Status:
updatingFirmware = "updatingFirmware"

_valid_values = {
"applyingNetworkConfiguration": _zivid.CameraState.Status.applyingNetworkConfiguration,
"available": _zivid.CameraState.Status.available,
"busy": _zivid.CameraState.Status.busy,
"connected": _zivid.CameraState.Status.connected,
Expand Down
194 changes: 194 additions & 0 deletions modules/zivid/network_configuration.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
"""Auto generated, do not edit."""
# pylint: disable=too-many-lines,protected-access,too-few-public-methods,too-many-arguments,line-too-long,missing-function-docstring,missing-class-docstring,redefined-builtin,too-many-branches,too-many-boolean-expressions
import _zivid


class NetworkConfiguration:
class IPV4:
class Mode:
dhcp = "dhcp"
manual = "manual"

_valid_values = {
"dhcp": _zivid.NetworkConfiguration.IPV4.Mode.dhcp,
"manual": _zivid.NetworkConfiguration.IPV4.Mode.manual,
}

@classmethod
def valid_values(cls):
return list(cls._valid_values.keys())

def __init__(
self,
address=_zivid.NetworkConfiguration.IPV4.Address().value,
mode=_zivid.NetworkConfiguration.IPV4.Mode().value,
subnet_mask=_zivid.NetworkConfiguration.IPV4.SubnetMask().value,
):
if isinstance(address, (str,)):
self._address = _zivid.NetworkConfiguration.IPV4.Address(address)
else:
raise TypeError(
"Unsupported type, expected: (str,), got {value_type}".format(
value_type=type(address)
)
)

if isinstance(mode, _zivid.NetworkConfiguration.IPV4.Mode.enum):
self._mode = _zivid.NetworkConfiguration.IPV4.Mode(mode)
elif isinstance(mode, str):
self._mode = _zivid.NetworkConfiguration.IPV4.Mode(
self.Mode._valid_values[mode]
)
else:
raise TypeError(
"Unsupported type, expected: str, got {value_type}".format(
value_type=type(mode)
)
)

if isinstance(subnet_mask, (str,)):
self._subnet_mask = _zivid.NetworkConfiguration.IPV4.SubnetMask(
subnet_mask
)
else:
raise TypeError(
"Unsupported type, expected: (str,), got {value_type}".format(
value_type=type(subnet_mask)
)
)

@property
def address(self):
return self._address.value

@property
def mode(self):
if self._mode.value is None:
return None
for key, internal_value in self.Mode._valid_values.items():
if internal_value == self._mode.value:
return key
raise ValueError("Unsupported value {value}".format(value=self._mode))

@property
def subnet_mask(self):
return self._subnet_mask.value

@address.setter
def address(self, value):
if isinstance(value, (str,)):
self._address = _zivid.NetworkConfiguration.IPV4.Address(value)
else:
raise TypeError(
"Unsupported type, expected: str, got {value_type}".format(
value_type=type(value)
)
)

@mode.setter
def mode(self, value):
if isinstance(value, str):
self._mode = _zivid.NetworkConfiguration.IPV4.Mode(
self.Mode._valid_values[value]
)
elif isinstance(value, _zivid.NetworkConfiguration.IPV4.Mode.enum):
self._mode = _zivid.NetworkConfiguration.IPV4.Mode(value)
else:
raise TypeError(
"Unsupported type, expected: str, got {value_type}".format(
value_type=type(value)
)
)

@subnet_mask.setter
def subnet_mask(self, value):
if isinstance(value, (str,)):
self._subnet_mask = _zivid.NetworkConfiguration.IPV4.SubnetMask(value)
else:
raise TypeError(
"Unsupported type, expected: str, got {value_type}".format(
value_type=type(value)
)
)

def __eq__(self, other):
if (
self._address == other._address
and self._mode == other._mode
and self._subnet_mask == other._subnet_mask
):
return True
return False

def __str__(self):
return str(_to_internal_network_configuration_ipv4(self))

def __init__(
self,
ipv4=None,
):
if ipv4 is None:
ipv4 = self.IPV4()
if not isinstance(ipv4, self.IPV4):
raise TypeError("Unsupported type: {value}".format(value=type(ipv4)))
self._ipv4 = ipv4

@property
def ipv4(self):
return self._ipv4

@ipv4.setter
def ipv4(self, value):
if not isinstance(value, self.IPV4):
raise TypeError("Unsupported type {value}".format(value=type(value)))
self._ipv4 = value

@classmethod
def load(cls, file_name):
return _to_network_configuration(_zivid.NetworkConfiguration(str(file_name)))

def save(self, file_name):
_to_internal_network_configuration(self).save(str(file_name))

def __eq__(self, other):
if self._ipv4 == other._ipv4:
return True
return False

def __str__(self):
return str(_to_internal_network_configuration(self))


def _to_network_configuration_ipv4(internal_ipv4):
return NetworkConfiguration.IPV4(
address=internal_ipv4.address.value,
mode=internal_ipv4.mode.value,
subnet_mask=internal_ipv4.subnet_mask.value,
)


def _to_network_configuration(internal_network_configuration):
return NetworkConfiguration(
ipv4=_to_network_configuration_ipv4(internal_network_configuration.ipv4),
)


def _to_internal_network_configuration_ipv4(ipv4):
internal_ipv4 = _zivid.NetworkConfiguration.IPV4()

internal_ipv4.address = _zivid.NetworkConfiguration.IPV4.Address(ipv4.address)
internal_ipv4.mode = _zivid.NetworkConfiguration.IPV4.Mode(ipv4._mode.value)
internal_ipv4.subnet_mask = _zivid.NetworkConfiguration.IPV4.SubnetMask(
ipv4.subnet_mask
)

return internal_ipv4


def _to_internal_network_configuration(network_configuration):
internal_network_configuration = _zivid.NetworkConfiguration()

internal_network_configuration.ipv4 = _to_internal_network_configuration_ipv4(
network_configuration.ipv4
)
return internal_network_configuration
65 changes: 65 additions & 0 deletions samples/sample_network_configuration.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import zivid


def confirm(message):
while True:
input_value = input(f"{message} [Y/n] ")
if input_value.lower() in ["y", "yes"]:
return True
elif input_value.lower() in ["n", "no"]:
return False


def main():
try:
app = zivid.Application()
camera = app.cameras()[0]

original_config = camera.network_configuration

print(f"Current network configuration of camera {camera.info.serial_number}:")
print(original_config)
print()

mode = zivid.NetworkConfiguration.IPV4.Mode.manual
address = original_config.ipv4.address
subnet_mask = original_config.ipv4.subnet_mask

if confirm("Do you want to use DHCP?"):
mode = zivid.NetworkConfiguration.IPV4.Mode.dhcp
else:
input_address = input(f"Enter IPv4 Address [{original_config.ipv4.address}]: ")
address = input_address if input_address else original_config.ipv4.address
input_subnet_mask = input(f"Enter new Subnet mask [{original_config.ipv4.subnet_mask}]: ")
subnet_mask = input_subnet_mask if input_subnet_mask else original_config.ipv4.subnet_mask

new_config = zivid.NetworkConfiguration(
ipv4=zivid.NetworkConfiguration.IPV4(
mode=mode,
address=address,
subnet_mask=subnet_mask,
)
)

print()
print("New network configuration:")
print(new_config)
if not confirm(f"Do you want to apply the new network configuration to camera {camera.info.serial_number}?"):
return 0

print("Applying network configuration...")
camera.apply_network_configuration(new_config)

print(f"Updated network configuration of camera {camera.info.serial_number}:")
print(camera.network_configuration)
print()

print(f"Camera status is '{camera.state.status}'")
except Exception as ex:
print(f"Error: {str(ex)}")
return 1
return 0


if __name__ == "__main__":
main()
4 changes: 3 additions & 1 deletion src/ReleasableCamera.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ namespace ZividPython
.def_property_readonly("state", &ReleasableCamera::state)
.def_property_readonly("info", &ReleasableCamera::info)
.def("write_user_data", &ReleasableCamera::writeUserData)
.def_property_readonly("user_data", &ReleasableCamera::userData);
.def_property_readonly("user_data", &ReleasableCamera::userData)
.def_property_readonly("network_configuration", &ReleasableCamera::networkConfiguration)
.def("apply_network_configuration", &ReleasableCamera::applyNetworkConfiguration);
}
} // namespace ZividPython
1 change: 1 addition & 0 deletions src/Wrapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ ZIVID_PYTHON_MODULE // NOLINT
ZIVID_PYTHON_WRAP_DATA_MODEL(module, CameraInfo);
ZIVID_PYTHON_WRAP_DATA_MODEL(module, FrameInfo);
ZIVID_PYTHON_WRAP_DATA_MODEL(module, CameraIntrinsics);
ZIVID_PYTHON_WRAP_DATA_MODEL(module, NetworkConfiguration);

ZIVID_PYTHON_WRAP_CLASS_AS_SINGLETON(module, Application);
ZIVID_PYTHON_WRAP_CLASS_AS_RELEASABLE(module, Camera);
Expand Down
2 changes: 2 additions & 0 deletions src/include/ZividPython/ReleasableCamera.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ namespace ZividPython
ZIVID_PYTHON_FORWARD_0_ARGS(info)
ZIVID_PYTHON_FORWARD_1_ARGS(writeUserData, const std::vector<uint8_t> &, data)
ZIVID_PYTHON_FORWARD_0_ARGS(userData)
ZIVID_PYTHON_FORWARD_0_ARGS(networkConfiguration)
ZIVID_PYTHON_FORWARD_1_ARGS(applyNetworkConfiguration, const Zivid::NetworkConfiguration &, networkConfiguration)
};

void wrapClass(pybind11::class_<ReleasableCamera> pyClass);
Expand Down
Loading

0 comments on commit ab24745

Please sign in to comment.