Support the older GigE cameras (Manta) on rietschen
Rietschen has three Manta G-1236 on one GbE port, whose firmware predates the names the program uses. A feature missing by its SFNC name is now looked up by the older one (ExposureTimeAbs, AcquisitionFrameRateAbs, ExposureAutoTarget, ExposureAutoAdjustTol), so test camera, the telemetry and the keep rule's reads work. export_camera_profile.py picks a gige recipe for such a camera: frame trigger off, BayerRG8 or Mono8, 60 MB/s, auto-exposure target 50 %. At the cameras' old target of 25 every waypoint ran into frame_timeout_ms. The camera probe now lists every camera. With several, the hardware.ini kit writes CHOOSE_ONE, install-tower asks which is the colour one, and the profile export is given that id - the first one the SDK lists was the NIR camera. install-tower names the unit that holds the motor port instead of assuming start_gimbal.service. Tested on rietschen's NIR camera with the motor mocked, beside the running old program: test camera PASS, a snapshot as a 3008x4112 grey JXL, a scan keeping a picture every 5 s. Tests 542/542 (mock build). The colour camera is not yet tested; the old program has it open. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
ed72f97fe5
commit
8850337250
|
|
@ -714,6 +714,46 @@ feature vocabularies differ, so copying another tower's XML does not work:
|
|||
| `IntensityAutoPrecedence` | `Minimizeexposuretime` / `Minimizenoise` | **`MinimizeBlur` / `MinimizeNoise`** |
|
||||
| Sensor | 12 MP | 16 MP, 5328x3040 |
|
||||
|
||||
### Older GigE cameras (Manta) `[partial]`
|
||||
|
||||
Rietschen has three Allied Vision Manta G-1236 on one GbE port (`enp1s0`,
|
||||
MTU 8000): RGB (G-1236C), NIR and ACR (both G-1236B). Their firmware
|
||||
(00.01.54) predates the names the program and the Alvium use:
|
||||
|
||||
| Program / Alvium | Manta |
|
||||
|---|---|
|
||||
| `ExposureTime` | `ExposureTimeAbs` |
|
||||
| `AcquisitionFrameRate` (+ `…Enable`) | `AcquisitionFrameRateAbs`, free-running at `AcquisitionFrameRateLimit` |
|
||||
| `IntensityControllerTarget` / `…Tolerance` | `ExposureAutoTarget` / `ExposureAutoAdjustTol` |
|
||||
| `DeviceLinkThroughputLimit` | `StreamBytesPerSecond` (max 124 MB/s, shared by every camera on the port) |
|
||||
| `BayerRG12p`, chunk data, `DeviceReset` | none |
|
||||
|
||||
The program looks a missing feature up under its older name, so `test camera`
|
||||
and the telemetry work. `export_camera_profile.py` picks its `gige` recipe by
|
||||
itself when a camera has `ExposureTimeAbs` and no `ExposureTime`: frame trigger
|
||||
off (the legacy cameras booted with `TriggerMode = On`, `Line1`), `BayerRG8` on
|
||||
the colour camera and `Mono8` on a mono one, 60 MB/s each, and an auto-exposure
|
||||
target of 50 %. That last one matters: the keep rule waits for the frame mean
|
||||
to reach 50 %, and at the cameras' old 25 every waypoint ran into
|
||||
`frame_timeout_ms`.
|
||||
|
||||
Give `--camera` the id: with several cameras the first one the SDK lists is
|
||||
not the colour one, and the order changes from run to run. `install.sh
|
||||
--probe` lists them all, `install.sh --hardware-ini` writes `CHOOSE_ONE`
|
||||
without `--camera-id`, and `install-tower` asks (`camera_id`).
|
||||
|
||||
What is still missing: a Manta has no `DeviceReset`, so the program cannot
|
||||
reset a stuck camera (it says so and goes on); and without chunk data the keep
|
||||
rule cannot tell a dark scene at the exposure limit, so at night it keeps the
|
||||
newest frame after `frame_timeout_ms`.
|
||||
|
||||
`[verified]` on rietschen's NIR camera, 2026-10-20, with the motor mocked and
|
||||
the old program running beside it: `test camera` PASS (5 frames 4112x3008
|
||||
Mono8 in 1.7 s, dark 100 % <= 20, bright mean 255), `snapshot` writes a
|
||||
3008x4112 grey JXL (`rotation_deg = 270`, as the old program rotated), and a
|
||||
scan keeps a picture every 5 s without a timeout. The colour camera is not yet
|
||||
tested: the old program has it open.
|
||||
|
||||
### Binning is not optional, and the frame-rate ceiling is not what it looks like
|
||||
|
||||
Unbinned RGB8 on the 16 MP sensor is a **48.6 MB frame** - over the ~20 MB
|
||||
|
|
|
|||
|
|
@ -100,6 +100,9 @@ probe_mcu() {
|
|||
# therefore taken only from a block whose transport layer is the USB or GigE
|
||||
# TL. A tower configured with DEV_Cam1 would "find its camera" and capture
|
||||
# synthetic images forever.
|
||||
#
|
||||
# One id per line, like probe_mcu: rietschen has three GigE cameras on one port,
|
||||
# and the first one listed is not the colour one, so the caller has to choose.
|
||||
probe_camera_id() {
|
||||
local tool
|
||||
for tool in /opt/VimbaX*/bin/ListCameras_VmbC /opt/VimbaX*/bin/ListCameras; do
|
||||
|
|
@ -113,7 +116,7 @@ probe_camera_id() {
|
|||
done
|
||||
"$tool" 2>/dev/null | awk '
|
||||
/Camera ID/ { id = $NF }
|
||||
/Transport Layer Path/ { if (id != "" && $NF !~ /Simulator/) { print id; exit } }
|
||||
/Transport Layer Path/ { if (id != "" && $NF !~ /Simulator/) print id; id = "" }
|
||||
'
|
||||
return
|
||||
done
|
||||
|
|
@ -134,7 +137,8 @@ probe_camera_lpm_path() {
|
|||
|
||||
probe_report() {
|
||||
local board mcu cam lpm
|
||||
board="$(probe_board)"; mcu="$(probe_mcu)"; cam="$(probe_camera_id)"; lpm="$(probe_camera_lpm_path)"
|
||||
board="$(probe_board)"; mcu="$(probe_mcu)"; lpm="$(probe_camera_lpm_path)"
|
||||
cam="$(probe_camera_id | paste -sd ' ')"
|
||||
echo "board : $board"
|
||||
echo "os : $(. /etc/os-release && echo "$PRETTY_NAME") ($(uname -r))"
|
||||
echo "motor MCU : ${mcu:-NOT FOUND}"
|
||||
|
|
@ -270,7 +274,7 @@ do_vimba() {
|
|||
|
||||
local id; id="$(probe_camera_id)"
|
||||
if [[ -n $id ]]; then
|
||||
say "camera found: $id"
|
||||
say "camera found: ${id//$'\n'/, }"
|
||||
else
|
||||
warn "no camera reported by ListCameras yet"
|
||||
warn "the SDK asks for a reboot before the transport layers are usable"
|
||||
|
|
@ -374,6 +378,11 @@ emit_hardware_ini() {
|
|||
warn "$mcu"
|
||||
mcu="CHOOSE_ONE"
|
||||
fi
|
||||
if [[ $(wc -l <<< "$cam") -gt 1 ]]; then
|
||||
warn "several cameras found; pick one with --camera-id:"
|
||||
warn "$cam"
|
||||
cam="CHOOSE_ONE"
|
||||
fi
|
||||
|
||||
cat <<EOF
|
||||
; hardware.ini - what this machine IS. Generated by hardware/$KIT_BOARD/install.sh
|
||||
|
|
|
|||
|
|
@ -120,6 +120,83 @@ RECIPES = {
|
|||
],
|
||||
}
|
||||
|
||||
# The older GigE cameras (Manta G-1236 on rietschen, firmware 00.01.54) name
|
||||
# things differently - ExposureTimeAbs, AcquisitionFrameRateAbs, ExposureAutoTarget
|
||||
# - and have no IntensityController, no DeviceLinkThroughputLimit and no 12-bit
|
||||
# packed Bayer the program reads. Measured on rietschen's NIR camera, 2026-10-20.
|
||||
# The link is shared: the camera free-runs as fast as StreamBytesPerSecond lets
|
||||
# it (41 MB/s gave 3.2 fps at 12.4 MB a frame), and the cameras of one tower sit
|
||||
# on one GbE port (~124 MB/s), so each gets half of it - room for RGB and NIR.
|
||||
# PixelFormat takes the first the camera offers: raw Bayer on the colour camera,
|
||||
# de-Bayered on the tower and a third of RGB8 on the wire; Mono8 on a mono one.
|
||||
# The packet size is left alone: it has to fit the port's MTU, which is the
|
||||
# tower's network setup, not the camera's.
|
||||
_GIGE_COMMON = [
|
||||
("TriggerSelector", "FrameStart"),
|
||||
("TriggerMode", "Off"),
|
||||
("AcquisitionMode", "Continuous"),
|
||||
("BinningVertical", 1),
|
||||
("BinningHorizontal", 1),
|
||||
("OffsetX", 0),
|
||||
("OffsetY", 0),
|
||||
("Width", 100000),
|
||||
("Height", 100000),
|
||||
("PixelFormat", ("BayerRG8", "Mono8")),
|
||||
("StreamBytesPerSecond", 60000000),
|
||||
("ChunkModeActive", False),
|
||||
# The keep rule waits for the frame mean to reach the camera's target. The
|
||||
# host starts on its default band of 50 % - rietschen's cameras were set to
|
||||
# 25, and every waypoint then ran into frame_timeout_ms (7.5 s, 2026-10-20).
|
||||
("ExposureAutoTarget", 50),
|
||||
("ExposureAuto", "Continuous"),
|
||||
("GainAuto", "Continuous"),
|
||||
]
|
||||
|
||||
GIGE_RECIPES = {
|
||||
"standard": _GIGE_COMMON + [
|
||||
("ExposureAutoMax", 20000),
|
||||
("ExposureAutoMin", 200),
|
||||
("GainAutoMax", 24.0),
|
||||
],
|
||||
"night": _GIGE_COMMON + [
|
||||
("ExposureAutoMax", 500000),
|
||||
("ExposureAutoMin", 1000),
|
||||
("GainAutoMax", 48.0), # clamped to the camera's 40
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
def has_feature(cam, name):
|
||||
try:
|
||||
cam.get_feature_by_name(name)
|
||||
return True
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
|
||||
def recipe_family(cam):
|
||||
"""'gige' for a camera that only knows the older names, else 'alvium'."""
|
||||
if not has_feature(cam, "ExposureTime") and has_feature(cam, "ExposureTimeAbs"):
|
||||
return "gige"
|
||||
return "alvium"
|
||||
|
||||
|
||||
def resolve_value(feat, value):
|
||||
"""The value to write to this feature; None if a choice list has no match.
|
||||
|
||||
A tuple is a list of choices for an enum: the first one the camera offers.
|
||||
An integer feature takes no float, even a whole one (ExposureAutoMin is Int
|
||||
on the Manta and Float on the Alvium)."""
|
||||
if isinstance(value, tuple):
|
||||
try:
|
||||
offered = {str(e) for e in feat.get_available_entries()}
|
||||
except Exception:
|
||||
offered = set()
|
||||
return next((v for v in value if v in offered), None)
|
||||
if isinstance(feat, vmbpy.IntFeature) and isinstance(value, float):
|
||||
return int(round(value))
|
||||
return value
|
||||
|
||||
|
||||
def apply_feature(cam, name, value, dry_run):
|
||||
try:
|
||||
|
|
@ -131,6 +208,10 @@ def apply_feature(cam, name, value, dry_run):
|
|||
except Exception:
|
||||
before = None
|
||||
|
||||
value = resolve_value(feat, value)
|
||||
if value is None:
|
||||
return ("refused", name, before, "none of the choices is offered")
|
||||
|
||||
# Clamp a numeric value the camera cannot reach, rather than letting the
|
||||
# write fail and leaving whatever was there before. Without this, exporting
|
||||
# 'standard' right after 'night' wrote a standard profile running at the
|
||||
|
|
@ -168,6 +249,8 @@ def main():
|
|||
"fire_gimbal_control", "camera_profiles", "rgb"),
|
||||
help="directory to write <profile>.xml into")
|
||||
ap.add_argument("--camera", default=None, help="camera id (default: the first real one)")
|
||||
ap.add_argument("--family", default="auto", choices=("auto", "alvium", "gige"),
|
||||
help="which recipe set (default: from the features the camera has)")
|
||||
ap.add_argument("--dry-run", action="store_true")
|
||||
args = ap.parse_args()
|
||||
|
||||
|
|
@ -191,10 +274,12 @@ def main():
|
|||
cam = cams[0]
|
||||
|
||||
with cam:
|
||||
family = recipe_family(cam) if args.family == "auto" else args.family
|
||||
recipe = (GIGE_RECIPES if family == "gige" else RECIPES)[args.profile]
|
||||
print(f"camera : {cam.get_id()} {cam.get_model()}")
|
||||
print(f"profile : {args.profile}{' (dry run)' if args.dry_run else ''}")
|
||||
print(f"profile : {args.profile} ({family} recipe){' (dry run)' if args.dry_run else ''}")
|
||||
refused = []
|
||||
for name, value in RECIPES[args.profile]:
|
||||
for name, value in recipe:
|
||||
state, n, before, after = apply_feature(cam, name, value, args.dry_run)
|
||||
mark = {"set": " ", "would-set": " ?", "missing": " !", "refused": " X",
|
||||
"clamped": " ~", "would-clamp": " ~"}[state]
|
||||
|
|
@ -205,13 +290,15 @@ def main():
|
|||
refused.append(n)
|
||||
|
||||
# What the settings actually cost per frame - the number that decides
|
||||
# whether this profile can be delivered over USB at all.
|
||||
# whether this profile can be delivered over the link at all. A
|
||||
# free-running GigE camera reports its ceiling as the rate limit.
|
||||
try:
|
||||
w = cam.get_feature_by_name("Width").get()
|
||||
h = cam.get_feature_by_name("Height").get()
|
||||
fmt = cam.get_feature_by_name("PixelFormat").get()
|
||||
bpp = {"RGB8": 3, "BGR8": 3, "BayerRG12p": 1.5, "BayerRG12": 2}.get(str(fmt), 1)
|
||||
rate = cam.get_feature_by_name("AcquisitionFrameRate").get()
|
||||
rate_name = "AcquisitionFrameRateLimit" if family == "gige" else "AcquisitionFrameRate"
|
||||
rate = cam.get_feature_by_name(rate_name).get()
|
||||
mb = w * h * bpp / 1e6
|
||||
print(f"\nresult : {w}x{h} {fmt} {mb:.1f} MB/frame "
|
||||
f"{rate:.2f} fps = {mb * rate:.0f} MB/s")
|
||||
|
|
|
|||
|
|
@ -334,22 +334,35 @@ set -euo pipefail
|
|||
|
||||
S_MCU="$(probe_mcu)"
|
||||
S_MCU_N=$(grep -c . <<< "$S_MCU" || true)
|
||||
# Another program on the motor MCU's port - a legacy tower's old program, started
|
||||
# by start_gimbal.service - would fight this one for it, and the flash would
|
||||
# fail half-way. Our own program is fine: it is stopped before anything needs
|
||||
# the port.
|
||||
# Another program on the motor MCU's port - a legacy tower's old program - would
|
||||
# fight this one for it, and the flash would fail half-way. Our own program is
|
||||
# fine: it is stopped before anything needs the port. The old program is started
|
||||
# differently from tower to tower: start_gimbal.service from
|
||||
# ~/scripts/startup_gimbal.sh, which also runs the picture transfer, on
|
||||
# staeffelsberg; gimbal_control.service on its own on rietschen.
|
||||
if (( S_MCU_N == 1 )) && [[ -e $S_MCU ]]; then
|
||||
for pid in $(fuser "$(readlink -f "$S_MCU")" 2>/dev/null); do
|
||||
args="$(ps -o args= -p "$pid" 2>/dev/null)"
|
||||
[[ $args == */fire_gimbal_control* ]] && continue
|
||||
die "the motor MCU's port is held by another program: pid $pid ($args).
|
||||
Stop it for good before installing. On a legacy tower it is started by
|
||||
start_gimbal.service from ~/scripts/startup_gimbal.sh, which also runs the picture
|
||||
transfer: comment out the old program's line there, keep the transfer, then
|
||||
unit="$(ps -o unit= -p "$pid" 2>/dev/null | tr -d ' ')"
|
||||
if [[ $unit == start_gimbal.service ]]; then
|
||||
how="It is started by start_gimbal.service from ~/scripts/startup_gimbal.sh, which
|
||||
also runs the picture transfer: comment out the old program's line there, keep the
|
||||
transfer, then
|
||||
sudo systemctl restart start_gimbal"
|
||||
elif [[ $unit == *.service ]]; then
|
||||
how="It is started by $unit:
|
||||
sudo systemctl disable --now $unit"
|
||||
else
|
||||
how="Find what starts it (systemctl status $pid) and stop that."
|
||||
fi
|
||||
die "the motor MCU's port is held by another program: pid $pid ($args).
|
||||
Stop it for good before installing. $how"
|
||||
done
|
||||
fi
|
||||
S_CAM="$(probe_camera_id)"
|
||||
S_CAM_N=$(grep -c . <<< "$S_CAM" || true)
|
||||
S_CAM_SHOWN="$(paste -sd ' ' <<< "$S_CAM")"
|
||||
S_LPM="$(probe_camera_lpm_path)"
|
||||
S_USBFS="$(cat /sys/module/usbcore/parameters/usbfs_memory_mb 2>/dev/null || echo '?')"
|
||||
S_PIO="$(pio_path || true)"
|
||||
|
|
@ -377,7 +390,7 @@ board : $S_BOARD (kit hardware/$S_BOARD, firmware env $KIT_FW_ENV)
|
|||
software / fw : $S_SW_REV / $S_FW_REV
|
||||
motor MCU : ${S_MCU:-NOT FOUND} [$S_MODE]
|
||||
running firmware : ${S_FW_NOW:-unknown}
|
||||
camera : ${S_CAM:-not found}${S_LPM:+ (LPM $(cat "$S_LPM") at $S_LPM)}
|
||||
camera : ${S_CAM_SHOWN:-not found}${S_LPM:+ (LPM $(cat "$S_LPM") at $S_LPM)}
|
||||
usbfs_memory_mb : $S_USBFS
|
||||
hardware.ini : $([[ -r $HW ]] && echo "present, $(hw_todos | grep -c . || true) TODO(s)" || echo absent)
|
||||
config.ini : $([[ -r $CFG ]] && echo "$CFG, tower_name $(ini_get "$CFG" General tower_name 2>/dev/null || echo '?'), [Startup] home $(ini_get "$CFG" Startup home 2>/dev/null || echo unset), mode $(ini_get "$CFG" Startup mode 2>/dev/null || echo unset)" || echo absent)
|
||||
|
|
@ -604,7 +617,11 @@ if [[ ! -r $HW ]]; then
|
|||
ask serial "No motor MCU found - its /dev/serial/by-id path"
|
||||
KIT_ARGS+=(--serial "$REPLY_VAL")
|
||||
fi
|
||||
if [[ $CAMS_A != none && -z $S_CAM ]]; then
|
||||
if [[ $CAMS_A != none ]] && (( S_CAM_N > 1 )); then
|
||||
echo "Several cameras:"; nl -w2 -s') ' <<< "$S_CAM"
|
||||
ask camera_id "Which is the colour camera (its DEV_ id)"
|
||||
KIT_ARGS+=(--camera-id "$REPLY_VAL")
|
||||
elif [[ $CAMS_A != none && -z $S_CAM ]]; then
|
||||
ask camera_id "Camera DEV_ id (none found; the SDK may need a reboot first)"
|
||||
KIT_ARGS+=(--camera-id "$REPLY_VAL")
|
||||
fi
|
||||
|
|
@ -1092,9 +1109,13 @@ if [[ ${DO[profiles]} == change ]]; then
|
|||
"$HOME/.venvs/vmbpy/bin/pip" install -q /opt/VimbaX/api/python/vmbpy-*.whl; }; }; then
|
||||
PROFDIR="$(prof_dir)" # the config may have moved since the plan was made
|
||||
mkdir -p "$PROFDIR"
|
||||
# The camera hardware.ini names, not the first one the SDK lists: on a
|
||||
# tower with several (rietschen: three GigE) that is another camera.
|
||||
cam_id="$(ini_get "$HW" Camera.rgb id 2>/dev/null || true)"
|
||||
cam_arg=(); [[ $cam_id == DEV_* ]] && cam_arg=(--camera "$cam_id")
|
||||
for prof in "${PROF_MISSING[@]}"; do
|
||||
( cd "$REPO" && GENICAM_GENTL64_PATH=/opt/VimbaX/cti "$HOME/.venvs/vmbpy/bin/python" \
|
||||
scripts/export_camera_profile.py --profile "$prof" --out "$PROFDIR" ) ||
|
||||
scripts/export_camera_profile.py --profile "$prof" --out "$PROFDIR" "${cam_arg[@]}" ) ||
|
||||
warn "could not export the $prof profile; export it later (docs/tower-installation.md section 8)"
|
||||
done
|
||||
done_step profiles
|
||||
|
|
|
|||
|
|
@ -188,12 +188,37 @@ private:
|
|||
bool has_ = false;
|
||||
};
|
||||
|
||||
// The older GigE cameras (Manta G-1236 on rietschen, firmware 00.01.54) predate
|
||||
// the SFNC names the program uses and the Alvium answers to. Where a camera has
|
||||
// no feature by the program's name, it is looked up by the older one. The
|
||||
// IntensityController pair is the Alvium's auto-exposure target and tolerance;
|
||||
// the Manta's ExposureAuto* pair means the same, in percent too.
|
||||
const char* legacyFeatureName(const std::string& name) {
|
||||
static const std::pair<const char*, const char*> kAliases[] = {
|
||||
{"ExposureTime", "ExposureTimeAbs"},
|
||||
{"AcquisitionFrameRate", "AcquisitionFrameRateAbs"},
|
||||
{"IntensityControllerTarget", "ExposureAutoTarget"},
|
||||
{"IntensityControllerTolerance", "ExposureAutoAdjustTol"},
|
||||
};
|
||||
for (const auto& [sfnc, legacy] : kAliases)
|
||||
if (name == sfnc) return legacy;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// A feature by the program's name, or by its older name on a camera that has only
|
||||
// that one.
|
||||
bool findFeature(const CameraPtr& cam, const std::string& name, FeaturePtr& f) {
|
||||
if (SP_ACCESS(cam)->GetFeatureByName(name.c_str(), f) == VmbErrorSuccess) return true;
|
||||
const char* legacy = legacyFeatureName(name);
|
||||
return legacy && SP_ACCESS(cam)->GetFeatureByName(legacy, f) == VmbErrorSuccess;
|
||||
}
|
||||
|
||||
// Read a numeric/string feature, returning `fallback`/"" if absent (tolerant, for
|
||||
// telemetry). Numeric takes an integer feature too: some are Int on one model and
|
||||
// Float on another (IntensityControllerTolerance is Int on the Alvium 1800 U-1240c).
|
||||
double getFloat(const CameraPtr& cam, const char* name, double fallback = 0.0) {
|
||||
FeaturePtr f;
|
||||
if (SP_ACCESS(cam)->GetFeatureByName(name, f) != VmbErrorSuccess) return fallback;
|
||||
if (!findFeature(cam, name, f)) return fallback;
|
||||
double v = fallback;
|
||||
if (f->GetValue(v) == VmbErrorSuccess) return v;
|
||||
VmbInt64_t i = 0;
|
||||
|
|
@ -203,7 +228,7 @@ double getFloat(const CameraPtr& cam, const char* name, double fallback = 0.0) {
|
|||
std::string getString(const CameraPtr& cam, const char* name) {
|
||||
FeaturePtr f;
|
||||
std::string v;
|
||||
if (SP_ACCESS(cam)->GetFeatureByName(name, f) == VmbErrorSuccess && f->GetValue(v) == VmbErrorSuccess)
|
||||
if (findFeature(cam, name, f) && f->GetValue(v) == VmbErrorSuccess)
|
||||
return v;
|
||||
return {};
|
||||
}
|
||||
|
|
@ -649,8 +674,7 @@ bool VimbaCameraSource::setFeature(int camera_index, const std::string& name,
|
|||
if (camera_index < 0 || idx >= impl_->cameras.size()) return false;
|
||||
FeaturePtr f;
|
||||
VmbFeatureDataType type = VmbFeatureDataUnknown;
|
||||
if (SP_ACCESS(impl_->cameras[idx])->GetFeatureByName(name.c_str(), f) != VmbErrorSuccess ||
|
||||
f->GetDataType(type) != VmbErrorSuccess)
|
||||
if (!findFeature(impl_->cameras[idx], name, f) || f->GetDataType(type) != VmbErrorSuccess)
|
||||
return false;
|
||||
// The value before the first change, so restoreProfile() can put it back
|
||||
// live. Only the first: a second change must not record the test's own value.
|
||||
|
|
@ -741,8 +765,7 @@ bool VimbaCameraSource::readFeature(int camera_index, const std::string& name, d
|
|||
if (camera_index < 0 || idx >= impl_->cameras.size()) return false;
|
||||
FeaturePtr f;
|
||||
VmbFeatureDataType type = VmbFeatureDataUnknown;
|
||||
if (SP_ACCESS(impl_->cameras[idx])->GetFeatureByName(name.c_str(), f) != VmbErrorSuccess ||
|
||||
f->GetDataType(type) != VmbErrorSuccess)
|
||||
if (!findFeature(impl_->cameras[idx], name, f) || f->GetDataType(type) != VmbErrorSuccess)
|
||||
return false;
|
||||
if (type == VmbFeatureDataFloat) return f->GetValue(value) == VmbErrorSuccess;
|
||||
if (type == VmbFeatureDataInt) {
|
||||
|
|
@ -760,8 +783,7 @@ bool VimbaCameraSource::featureRange(int camera_index, const std::string& name,
|
|||
if (camera_index < 0 || idx >= impl_->cameras.size()) return false;
|
||||
FeaturePtr f;
|
||||
VmbFeatureDataType type = VmbFeatureDataUnknown;
|
||||
if (SP_ACCESS(impl_->cameras[idx])->GetFeatureByName(name.c_str(), f) != VmbErrorSuccess ||
|
||||
f->GetDataType(type) != VmbErrorSuccess)
|
||||
if (!findFeature(impl_->cameras[idx], name, f) || f->GetDataType(type) != VmbErrorSuccess)
|
||||
return false;
|
||||
if (type == VmbFeatureDataFloat) return f->GetRange(min, max) == VmbErrorSuccess;
|
||||
if (type == VmbFeatureDataInt) {
|
||||
|
|
@ -788,7 +810,7 @@ bool VimbaCameraSource::restoreProfile(int camera_index) {
|
|||
for (auto it = undo.rbegin(); ok && it != undo.rend(); ++it) {
|
||||
FeaturePtr f;
|
||||
VmbFeatureDataType type = VmbFeatureDataUnknown;
|
||||
if (SP_ACCESS(impl_->cameras[idx])->GetFeatureByName(it->first.c_str(), f) != VmbErrorSuccess ||
|
||||
if (!findFeature(impl_->cameras[idx], it->first, f) ||
|
||||
f->GetDataType(type) != VmbErrorSuccess) { ok = false; break; }
|
||||
VmbErrorType rc = VmbErrorWrongType;
|
||||
try {
|
||||
|
|
|
|||
Loading…
Reference in New Issue