Skip to content

Commit 0207867

Browse files
committed
feat: init implementation of test command
1 parent 346cc2f commit 0207867

12 files changed

+249
-34
lines changed

camera/autocamera.cpp

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -65,15 +65,32 @@ long long unsigned AutoCamera::intensityVariationSum()
6565
long long unsigned sum = 0;
6666
for (unsigned i = 0; i < diffs.size() - 1; ++i)
6767
sum += static_cast<long long unsigned>(abs(diffs[i] - diffs[i + 1]));
68-
68+
6969
closeCap();
7070
return sum;
7171
}
7272

73+
74+
/**
75+
* @brief Check if the emitter is working,
76+
* without asking for manual confirmation
77+
*
78+
* @return true if yes, false if not
79+
*/
80+
bool AutoCamera::isEmitterWorkingNoConfirm()
81+
{
82+
return intensityVariationSum() > refIntesityVarSum * MAGIC_REF_INTENSITY_VAR_COEF;
83+
}
84+
85+
/**
86+
* @brief Check if the emitter is working,
87+
* if so, ask for manual confirmation
88+
*
89+
* @return true if yes, false if not
90+
*/
7391
bool AutoCamera::isEmitterWorking()
7492
{
75-
bool isWorking = intensityVariationSum() > refIntesityVarSum * MAGIC_REF_INTENSITY_VAR_COEF;
76-
return isWorking && Camera::isEmitterWorking();
93+
return isEmitterWorkingNoConfirm() && Camera::isEmitterWorking();
7794
}
7895

7996
AutoCamera::AutoCamera(const string &device, unsigned captureTimeMs) : Camera(device), captureTimeMs(captureTimeMs), refIntesityVarSum(intensityVariationSum()) {}

camera/autocamera.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ class AutoCamera : public Camera
2525

2626
bool isEmitterWorking() override;
2727

28+
bool isEmitterWorkingNoConfirm();
29+
2830
AutoCamera &operator=(const AutoCamera &) = delete;
2931

3032
AutoCamera(const AutoCamera &) = delete;

camera/camera.cpp

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,28 @@ Camera::~Camera()
127127
closeCap();
128128
}
129129

130+
/**
131+
* @brief Show a video feedback until the user exit
132+
*/
133+
void Camera::play()
134+
{
135+
openCap();
136+
cv::Mat frame;
137+
int key = -1;
138+
139+
cout << "Press any key in the window to close" << endl;
140+
141+
while (key == -1)
142+
{
143+
cap->read(frame);
144+
cv::imshow("linux-enable-ir-emitter", frame);
145+
key = cv::waitKey(5);
146+
}
147+
148+
closeCap();
149+
cv::destroyAllWindows();
150+
}
151+
130152
/**
131153
* @brief Apply an instruction on the camera
132154
*
@@ -166,6 +188,8 @@ unique_ptr<cv::Mat> Camera::read1()
166188

167189
/**
168190
* @brief Check if the emitter is working
191+
* by showing a video feedback and
192+
* asking confirmation to the user
169193
*
170194
* @throw CameraException if unable to open the camera device
171195
*
@@ -190,7 +214,7 @@ bool Camera::isEmitterWorking()
190214

191215
closeCap();
192216
cv::destroyAllWindows();
193-
217+
194218
return key == OK_KEY;
195219
}
196220

camera/camera.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,8 @@ class Camera
5353

5454
Camera(Camera &&other) = delete;
5555

56+
void play();
57+
5658
bool apply(const CameraInstruction &instruction);
5759

5860
virtual bool isEmitterWorking();

camera/is-emitter-working.cpp

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
#include <memory>
2+
using namespace std;
3+
4+
#include "autocamera.hpp"
5+
#include "opencv.hpp"
6+
7+
constexpr unsigned EXIT_FD_ERROR = 126;
8+
9+
/**
10+
* Check if a ir emitter is working using the automatic detection algorithm
11+
*
12+
* usage: is-emitter-camera [device]
13+
* device path to the camera
14+
*
15+
* Exit code: 0 Success
16+
* 1 Error
17+
* 126 Unable to open the camera device
18+
*/
19+
int main(int, const char **argv)
20+
{
21+
AutoCamera camera(argv[1]);
22+
try
23+
{
24+
return static_cast<int>(camera.isEmitterWorkingNoConfirm());
25+
}
26+
catch (CameraException &e)
27+
{
28+
cerr << e.what() << endl;
29+
return EXIT_FD_ERROR;
30+
}
31+
}

camera/video-feedback.cpp

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
#include <memory>
2+
using namespace std;
3+
4+
#include "autocamera.hpp"
5+
#include "opencv.hpp"
6+
7+
constexpr unsigned EXIT_FD_ERROR = 126;
8+
9+
/**
10+
* Check if a ir emitter is working using the automatic detection algorithm
11+
*
12+
* usage: is-emitter-camera [device]
13+
* device path to the camera
14+
*
15+
* Exit code: 0 Success
16+
* 126 Unable to open the camera device
17+
*/
18+
int main(int, const char **argv)
19+
{
20+
Camera camera(argv[1]);
21+
try
22+
{
23+
camera.play();
24+
return EXIT_SUCCESS;
25+
}
26+
catch (CameraException &e)
27+
{
28+
cerr << e.what() << endl;
29+
return EXIT_FD_ERROR;
30+
}
31+
}

command/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@
22
from command.configure import configure
33
from command.delete import delete
44
from command.run import run
5+
from command.test import test

command/configure.py

Lines changed: 5 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
from globals import (
88
BIN_GENERATE_DRIVER_PATH,
99
SAVE_DRIVER_FOLDER_PATH,
10-
BIN_IS_GREY_CAMERA_PATH,
10+
find_grayscale_camera,
1111
ExitCode,
1212
)
1313

@@ -23,36 +23,15 @@ def configure(
2323
emitters (int): number of emitters on the device
2424
neg_answer_limit (int): number of negative answer before the pattern is skiped. Use -1 for unlimited
2525
"""
26-
logging.info("Stand in front of and close to the camera and make sure the room is well lit.")
26+
logging.info(
27+
"Stand in front of and close to the camera and make sure the room is well lit."
28+
)
2729
logging.info("Ensure to not use the camera during the execution.")
2830
logging.info("Warning to do not kill the process !")
2931

3032
if device is None:
31-
dev_devices = (
32-
subprocess.run(
33-
f"ls /dev/v4l/by-path/*",
34-
shell=True,
35-
capture_output=True,
36-
text=True,
37-
)
38-
.stdout.strip()
39-
.split()
40-
)
33+
device = find_grayscale_camera()
4134

42-
for dev in dev_devices:
43-
exit_code = subprocess.call(
44-
[BIN_IS_GREY_CAMERA_PATH, dev],
45-
stdout=subprocess.DEVNULL,
46-
stderr=subprocess.DEVNULL,
47-
)
48-
if exit_code == ExitCode.SUCCESS:
49-
device = dev
50-
break
51-
52-
if device is None:
53-
logging.critical("Impossible to find your infrared camera.")
54-
logging.info("Please specify your infrared camera path using the -d option.")
55-
exit(ExitCode.FAILURE)
5635
logging.info(f"Configuring the camera: {device}.")
5736
log_level = int(logging.getLogger().level == logging.DEBUG)
5837
exit_code = subprocess.call(

command/test.py

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
from typing import NoReturn
2+
3+
import subprocess
4+
import logging
5+
6+
from globals import (
7+
BIN_IS_GRAY_CAMERA_PATH,
8+
BIN_IS_EMITTER_WORKING_PATH,
9+
BIN_VIDEO_FEEDBACK_PATH,
10+
find_grayscale_camera,
11+
ExitCode,
12+
)
13+
14+
15+
def test(device: str | None) -> NoReturn:
16+
"""Test if the camera is in grayscale and if the emitter is working.
17+
Also display a video feedback, then exit.
18+
19+
Args:
20+
device (str | None): path to the camera, None for automatic detection
21+
"""
22+
if device is None:
23+
device = find_grayscale_camera()
24+
25+
exit_code = subprocess.call(
26+
[BIN_IS_GRAY_CAMERA_PATH, device],
27+
stdout=subprocess.DEVNULL,
28+
stderr=subprocess.DEVNULL,
29+
)
30+
if exit_code == ExitCode.FILE_DESCRIPTOR_ERROR:
31+
exit(exit_code)
32+
elif exit_code == ExitCode.SUCCESS:
33+
logging.info(
34+
"The camera is in gray scale. This is probably your infrared camera."
35+
)
36+
else:
37+
logging.error(
38+
"The camera is not in gray scale. This is probably your regular camera."
39+
)
40+
41+
exit_code = subprocess.call(
42+
[BIN_IS_EMITTER_WORKING_PATH, device],
43+
stdout=subprocess.DEVNULL,
44+
stderr=subprocess.DEVNULL,
45+
)
46+
if exit_code == ExitCode.FILE_DESCRIPTOR_ERROR:
47+
exit(exit_code)
48+
elif exit_code == ExitCode.SUCCESS:
49+
logging.info("The infrared emitter is working.")
50+
else:
51+
logging.error("The infrared emitter is not working.")
52+
53+
exit_code = subprocess.call(
54+
[BIN_VIDEO_FEEDBACK_PATH, device],
55+
stdout=subprocess.DEVNULL,
56+
stderr=subprocess.DEVNULL,
57+
)
58+
if exit_code == ExitCode.FILE_DESCRIPTOR_ERROR:
59+
exit(exit_code)
60+
61+
exit(ExitCode.SUCCESS)

globals.py

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
from __future__ import annotations
2-
from typing import TYPE_CHECKING, Type
2+
from typing import TYPE_CHECKING, Type, NoReturn
33

44
if TYPE_CHECKING:
55
from boot_service.systemd import Systemd
@@ -18,7 +18,9 @@
1818

1919
BIN_EXECUTE_DRIVER_PATH = "@BIN_EXECUTE_DRIVER_PATH@"
2020
BIN_GENERATE_DRIVER_PATH = "@BIN_GENERATE_DRIVER_PATH@"
21-
BIN_IS_GREY_CAMERA_PATH = "@BIN_IS_CAMERA_PATH@"
21+
BIN_IS_GRAY_CAMERA_PATH = "@BIN_IS_GRAY_CAMERA_PATH@"
22+
BIN_IS_EMITTER_WORKING_PATH = "@BIN_IS_EMITTER_WORKING_PATH@"
23+
BIN_VIDEO_FEEDBACK_PATH = "@BIN_VIDEO_FEEDBACK_PATH@"
2224
BIN_GENERATE_DRIVER_PATH = "@BIN_GENERATE_DRIVER_PATH@"
2325

2426
UDEV_RULE_PATH = "@UDEV_RULE_PATH@"
@@ -112,3 +114,35 @@ def get_kernels(device: str) -> str:
112114
shell=True,
113115
text=True,
114116
).strip()
117+
118+
119+
def find_grayscale_camera() -> str | NoReturn:
120+
"""Automatically determine the grayscale camera.
121+
Exit if no gray camera is found.
122+
123+
Returns:
124+
str: path to the grayscale camera
125+
"""
126+
devices = (
127+
subprocess.run(
128+
f"ls /dev/v4l/by-path/*",
129+
shell=True,
130+
capture_output=True,
131+
text=True,
132+
)
133+
.stdout.strip()
134+
.split()
135+
)
136+
137+
for device in devices:
138+
exit_code = subprocess.call(
139+
[BIN_IS_GRAY_CAMERA_PATH, device],
140+
stdout=subprocess.DEVNULL,
141+
stderr=subprocess.DEVNULL,
142+
)
143+
if exit_code == ExitCode.SUCCESS:
144+
return device
145+
146+
logging.critical("Impossible to find your infrared camera.")
147+
logging.info("Please specify your infrared camera path using the -d option.")
148+
exit(ExitCode.FAILURE)

linux-enable-ir-emitter.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
import logging
55
import subprocess
66

7-
from command import boot, configure, delete, run
7+
from command import boot, configure, delete, run, test
88
from globals import ExitCode, check_root
99

1010
if __name__ == "__main__":
@@ -28,7 +28,7 @@
2828
"-V",
2929
"--version",
3030
action="version",
31-
version="%(prog)s 5.0.2\nDevelopped by Maxime Dirksen - EmixamPP\nMIT License",
31+
version="%(prog)s @version@\nDevelopped by Maxime Dirksen - EmixamPP\nMIT License",
3232
help="show version information and exit",
3333
)
3434
parser.add_argument(
@@ -69,6 +69,10 @@
6969
default=40,
7070
type=int,
7171
)
72+
command_test = command_subparser.add_parser(
73+
"test",
74+
help="test a camera",
75+
)
7276
command_boot = command_subparser.add_parser(
7377
"boot",
7478
help="enable ir at boot",
@@ -114,6 +118,9 @@
114118
check_root()
115119
configure(device, args.manual, args.emitters, args.limit)
116120

121+
elif args.command == "test":
122+
test(device)
123+
117124
elif args.command == "boot":
118125
check_root()
119126
boot(args.boot_status)

0 commit comments

Comments
 (0)