diff --git a/CMakeLists.txt b/CMakeLists.txt index 894a850..6af8bb1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -20,7 +20,11 @@ find_library(VIZIONSDK_LIBRARY ) # Create executable -add_executable(vizionStreamer main.cpp) +add_executable(vizionStreamer + main.cpp + SocketServer.cpp + CameraController.cpp +) # Link VizionSDK library target_link_libraries(vizionStreamer PRIVATE ${VIZIONSDK_LIBRARY}) diff --git a/CameraController.cpp b/CameraController.cpp new file mode 100644 index 0000000..190a73e --- /dev/null +++ b/CameraController.cpp @@ -0,0 +1,295 @@ +#include "CameraController.h" +#include +#include + +CameraController::CameraController(std::shared_ptr camera) + : camera_(camera), streaming_(false) {} + +std::string CameraController::processCommand(const std::string& jsonCommand) { + std::lock_guard lock(mutex_); + + // Simple JSON parsing (basic implementation) + // Format: {"command":"name","params":{...}} + + size_t cmdPos = jsonCommand.find("\"command\""); + if (cmdPos == std::string::npos) { + return createErrorResponse("Missing command field"); + } + + size_t colonPos = jsonCommand.find(":", cmdPos); + size_t quoteStart = jsonCommand.find("\"", colonPos); + size_t quoteEnd = jsonCommand.find("\"", quoteStart + 1); + + if (quoteStart == std::string::npos || quoteEnd == std::string::npos) { + return createErrorResponse("Invalid command format"); + } + + std::string command = jsonCommand.substr(quoteStart + 1, quoteEnd - quoteStart - 1); + + // Helper lambda to extract parameter value + auto getParam = [&jsonCommand](const std::string& paramName) -> std::string { + size_t pos = jsonCommand.find("\"" + paramName + "\""); + if (pos == std::string::npos) return ""; + + size_t colonPos = jsonCommand.find(":", pos); + size_t valueStart = jsonCommand.find_first_not_of(" \t\n\r", colonPos + 1); + + if (jsonCommand[valueStart] == '\"') { + size_t valueEnd = jsonCommand.find("\"", valueStart + 1); + return jsonCommand.substr(valueStart + 1, valueEnd - valueStart - 1); + } else { + size_t valueEnd = jsonCommand.find_first_of(",}", valueStart); + return jsonCommand.substr(valueStart, valueEnd - valueStart); + } + }; + + // Route commands + if (command == "set_format") { + return handleSetFormat(getParam("width"), getParam("height"), + getParam("framerate"), getParam("format")); + } else if (command == "get_formats") { + return handleGetFormats(); + } else if (command == "set_exposure") { + return handleSetExposure(getParam("mode"), getParam("value")); + } else if (command == "set_whitebalance") { + return handleSetWhiteBalance(getParam("mode"), getParam("temperature")); + } else if (command == "set_brightness") { + return handleSetBrightness(getParam("value")); + } else if (command == "set_contrast") { + return handleSetContrast(getParam("value")); + } else if (command == "set_saturation") { + return handleSetSaturation(getParam("value")); + } else if (command == "set_sharpness") { + return handleSetSharpness(getParam("value")); + } else if (command == "set_gamma") { + return handleSetGamma(getParam("value")); + } else if (command == "set_gain") { + return handleSetGain(getParam("value")); + } else if (command == "get_status") { + return handleGetStatus(); + } else if (command == "start_stream") { + return handleStartStream(); + } else if (command == "stop_stream") { + return handleStopStream(); + } else { + return createErrorResponse("Unknown command: " + command); + } +} + +std::string CameraController::handleSetFormat(const std::string& width, const std::string& height, + const std::string& framerate, const std::string& format) { + if (streaming_) { + return createErrorResponse("Cannot change format while streaming"); + } + + try { + VxFormat fmt; + fmt.width = std::stoi(width); + fmt.height = std::stoi(height); + fmt.framerate = std::stoi(framerate); + fmt.format = stringToFormat(format); + fmt.mediatypeIdx = 0; + + if (VxSetFormat(camera_, fmt) != 0) { + return createErrorResponse("Failed to set format"); + } + + return createSuccessResponse("Format set successfully"); + } catch (const std::exception& e) { + return createErrorResponse(std::string("Invalid parameters: ") + e.what()); + } +} + +std::string CameraController::handleGetFormats() { + std::vector fmtList; + if (VxGetFormatList(camera_, fmtList) != 0) { + return createErrorResponse("Failed to get format list"); + } + + std::ostringstream oss; + oss << "{\"status\":\"success\",\"formats\":["; + for (size_t i = 0; i < fmtList.size(); i++) { + if (i > 0) oss << ","; + oss << "{\"width\":" << fmtList[i].width + << ",\"height\":" << fmtList[i].height + << ",\"framerate\":" << fmtList[i].framerate + << ",\"format\":\"" << formatToString(fmtList[i].format) << "\"}"; + } + oss << "]}"; + return oss.str(); +} + +std::string CameraController::handleSetExposure(const std::string& mode, const std::string& value) { + try { + int flag = (mode == "auto") ? 1 : 0; + long expValue = value.empty() ? 0 : std::stol(value); + + if (VxSetUVCImageProcessing(camera_, VX_UVC_IMAGE_PROPERTIES::UVC_IMAGE_EXPOSURE, + expValue, flag) != 0) { + return createErrorResponse("Failed to set exposure"); + } + + return createSuccessResponse("Exposure set successfully"); + } catch (const std::exception& e) { + return createErrorResponse(std::string("Invalid parameters: ") + e.what()); + } +} + +std::string CameraController::handleSetWhiteBalance(const std::string& mode, const std::string& temperature) { + try { + int flag = (mode == "auto") ? 1 : 0; + long tempValue = temperature.empty() ? 0 : std::stol(temperature); + + if (VxSetUVCImageProcessing(camera_, VX_UVC_IMAGE_PROPERTIES::UVC_IMAGE_WHITEBALANCE, + tempValue, flag) != 0) { + return createErrorResponse("Failed to set white balance"); + } + + return createSuccessResponse("White balance set successfully"); + } catch (const std::exception& e) { + return createErrorResponse(std::string("Invalid parameters: ") + e.what()); + } +} + +std::string CameraController::handleSetBrightness(const std::string& value) { + try { + long val = std::stol(value); + if (VxSetUVCImageProcessing(camera_, VX_UVC_IMAGE_PROPERTIES::UVC_IMAGE_BRIGHTNESS, + val, 0) != 0) { + return createErrorResponse("Failed to set brightness"); + } + return createSuccessResponse("Brightness set successfully"); + } catch (const std::exception& e) { + return createErrorResponse(std::string("Invalid parameters: ") + e.what()); + } +} + +std::string CameraController::handleSetContrast(const std::string& value) { + try { + long val = std::stol(value); + if (VxSetUVCImageProcessing(camera_, VX_UVC_IMAGE_PROPERTIES::UVC_IMAGE_CONTRAST, + val, 0) != 0) { + return createErrorResponse("Failed to set contrast"); + } + return createSuccessResponse("Contrast set successfully"); + } catch (const std::exception& e) { + return createErrorResponse(std::string("Invalid parameters: ") + e.what()); + } +} + +std::string CameraController::handleSetSaturation(const std::string& value) { + try { + long val = std::stol(value); + if (VxSetUVCImageProcessing(camera_, VX_UVC_IMAGE_PROPERTIES::UVC_IMAGE_SATURATION, + val, 0) != 0) { + return createErrorResponse("Failed to set saturation"); + } + return createSuccessResponse("Saturation set successfully"); + } catch (const std::exception& e) { + return createErrorResponse(std::string("Invalid parameters: ") + e.what()); + } +} + +std::string CameraController::handleSetSharpness(const std::string& value) { + try { + long val = std::stol(value); + if (VxSetUVCImageProcessing(camera_, VX_UVC_IMAGE_PROPERTIES::UVC_IMAGE_SHARPNESS, + val, 0) != 0) { + return createErrorResponse("Failed to set sharpness"); + } + return createSuccessResponse("Sharpness set successfully"); + } catch (const std::exception& e) { + return createErrorResponse(std::string("Invalid parameters: ") + e.what()); + } +} + +std::string CameraController::handleSetGamma(const std::string& value) { + try { + long val = std::stol(value); + if (VxSetUVCImageProcessing(camera_, VX_UVC_IMAGE_PROPERTIES::UVC_IMAGE_GAMMA, + val, 0) != 0) { + return createErrorResponse("Failed to set gamma"); + } + return createSuccessResponse("Gamma set successfully"); + } catch (const std::exception& e) { + return createErrorResponse(std::string("Invalid parameters: ") + e.what()); + } +} + +std::string CameraController::handleSetGain(const std::string& value) { + try { + long val = std::stol(value); + if (VxSetUVCImageProcessing(camera_, VX_UVC_IMAGE_PROPERTIES::UVC_IMAGE_GAIN, + val, 0) != 0) { + return createErrorResponse("Failed to set gain"); + } + return createSuccessResponse("Gain set successfully"); + } catch (const std::exception& e) { + return createErrorResponse(std::string("Invalid parameters: ") + e.what()); + } +} + +std::string CameraController::handleGetStatus() { + std::ostringstream oss; + oss << "{\"status\":\"success\",\"streaming\":" << (streaming_ ? "true" : "false") << "}"; + return oss.str(); +} + +std::string CameraController::handleStartStream() { + if (streaming_) { + return createErrorResponse("Already streaming"); + } + + if (VxStartStreaming(camera_) != 0) { + return createErrorResponse("Failed to start streaming"); + } + + streaming_ = true; + return createSuccessResponse("Streaming started"); +} + +std::string CameraController::handleStopStream() { + if (!streaming_) { + return createErrorResponse("Not streaming"); + } + + if (VxStopStreaming(camera_) != 0) { + return createErrorResponse("Failed to stop streaming"); + } + + streaming_ = false; + return createSuccessResponse("Streaming stopped"); +} + +VX_IMAGE_FORMAT CameraController::stringToFormat(const std::string& format) { + if (format == "YUY2") return VX_IMAGE_FORMAT::YUY2; + if (format == "UYVY") return VX_IMAGE_FORMAT::UYVY; + if (format == "NV12") return VX_IMAGE_FORMAT::NV12; + if (format == "MJPG") return VX_IMAGE_FORMAT::MJPG; + if (format == "BGR") return VX_IMAGE_FORMAT::BGR; + if (format == "RGB") return VX_IMAGE_FORMAT::RGB; + return VX_IMAGE_FORMAT::NONE; +} + +std::string CameraController::formatToString(VX_IMAGE_FORMAT format) { + switch (format) { + case VX_IMAGE_FORMAT::YUY2: return "YUY2"; + case VX_IMAGE_FORMAT::UYVY: return "UYVY"; + case VX_IMAGE_FORMAT::NV12: return "NV12"; + case VX_IMAGE_FORMAT::MJPG: return "MJPG"; + case VX_IMAGE_FORMAT::BGR: return "BGR"; + case VX_IMAGE_FORMAT::RGB: return "RGB"; + default: return "NONE"; + } +} + +std::string CameraController::createErrorResponse(const std::string& error) { + return "{\"status\":\"error\",\"message\":\"" + error + "\"}"; +} + +std::string CameraController::createSuccessResponse(const std::string& message) { + if (message.empty()) { + return "{\"status\":\"success\"}"; + } + return "{\"status\":\"success\",\"message\":\"" + message + "\"}"; +} diff --git a/CameraController.h b/CameraController.h new file mode 100644 index 0000000..c48acf4 --- /dev/null +++ b/CameraController.h @@ -0,0 +1,41 @@ +#pragma once + +#include +#include +#include +#include + +class CameraController { +public: + explicit CameraController(std::shared_ptr camera); + + // Process JSON command and return JSON response + std::string processCommand(const std::string& jsonCommand); + +private: + // Command handlers + std::string handleSetFormat(const std::string& width, const std::string& height, + const std::string& framerate, const std::string& format); + std::string handleGetFormats(); + std::string handleSetExposure(const std::string& mode, const std::string& value); + std::string handleSetWhiteBalance(const std::string& mode, const std::string& temperature); + std::string handleSetBrightness(const std::string& value); + std::string handleSetContrast(const std::string& value); + std::string handleSetSaturation(const std::string& value); + std::string handleSetSharpness(const std::string& value); + std::string handleSetGamma(const std::string& value); + std::string handleSetGain(const std::string& value); + std::string handleGetStatus(); + std::string handleStartStream(); + std::string handleStopStream(); + + // Helper functions + VX_IMAGE_FORMAT stringToFormat(const std::string& format); + std::string formatToString(VX_IMAGE_FORMAT format); + std::string createErrorResponse(const std::string& error); + std::string createSuccessResponse(const std::string& message = ""); + + std::shared_ptr camera_; + std::mutex mutex_; + bool streaming_; +}; diff --git a/SOCKET_API.md b/SOCKET_API.md new file mode 100644 index 0000000..ceb77c0 --- /dev/null +++ b/SOCKET_API.md @@ -0,0 +1,514 @@ +# VizionStreamer Socket Control API + +VizionStreamer can be controlled via a Unix Domain Socket interface. This allows external applications to configure camera parameters and stream settings at runtime. + +## Socket Connection + +- **Socket Path**: `/tmp/vizion_control.sock` +- **Protocol**: Unix Domain Socket (SOCK_STREAM) +- **Message Format**: JSON + +## Command Format + +All commands follow this JSON structure: + +```json +{ + "command": "command_name", + "params": { + "param1": "value1", + "param2": "value2" + } +} +``` + +## Response Format + +All responses follow this JSON structure: + +**Success Response:** +```json +{ + "status": "success", + "message": "Optional success message" +} +``` + +**Error Response:** +```json +{ + "status": "error", + "message": "Error description" +} +``` + +## Available Commands + +### 1. Get Available Formats + +Retrieve all supported video formats. + +**Command:** +```json +{ + "command": "get_formats" +} +``` + +**Response:** +```json +{ + "status": "success", + "formats": [ + { + "width": 1920, + "height": 1080, + "framerate": 30, + "format": "YUY2" + }, + { + "width": 1280, + "height": 720, + "framerate": 60, + "format": "MJPG" + } + ] +} +``` + +**Supported Formats:** YUY2, UYVY, NV12, MJPG, BGR, RGB + +--- + +### 2. Set Video Format + +Change the video format (resolution, framerate, pixel format). + +**Note:** Cannot be changed while streaming is active. + +**Command:** +```json +{ + "command": "set_format", + "params": { + "width": "1920", + "height": "1080", + "framerate": "30", + "format": "YUY2" + } +} +``` + +**Response:** +```json +{ + "status": "success", + "message": "Format set successfully" +} +``` + +--- + +### 3. Start Streaming + +Start video streaming from the camera. + +**Command:** +```json +{ + "command": "start_stream" +} +``` + +**Response:** +```json +{ + "status": "success", + "message": "Streaming started" +} +``` + +--- + +### 4. Stop Streaming + +Stop video streaming. + +**Command:** +```json +{ + "command": "stop_stream" +} +``` + +**Response:** +```json +{ + "status": "success", + "message": "Streaming stopped" +} +``` + +--- + +### 5. Get Status + +Get current streaming status. + +**Command:** +```json +{ + "command": "get_status" +} +``` + +**Response:** +```json +{ + "status": "success", + "streaming": true +} +``` + +--- + +### 6. Set Exposure + +Configure camera exposure settings. + +**Command:** +```json +{ + "command": "set_exposure", + "params": { + "mode": "manual", + "value": "100" + } +} +``` + +**Parameters:** +- `mode`: "auto" or "manual" +- `value`: Exposure value (only used in manual mode) + +**Response:** +```json +{ + "status": "success", + "message": "Exposure set successfully" +} +``` + +--- + +### 7. Set White Balance + +Configure white balance settings. + +**Command:** +```json +{ + "command": "set_whitebalance", + "params": { + "mode": "auto", + "temperature": "4500" + } +} +``` + +**Parameters:** +- `mode`: "auto" or "manual" +- `temperature`: Color temperature in Kelvin (only used in manual mode) + +**Response:** +```json +{ + "status": "success", + "message": "White balance set successfully" +} +``` + +--- + +### 8. Set Brightness + +Adjust camera brightness. + +**Command:** +```json +{ + "command": "set_brightness", + "params": { + "value": "50" + } +} +``` + +**Response:** +```json +{ + "status": "success", + "message": "Brightness set successfully" +} +``` + +--- + +### 9. Set Contrast + +Adjust camera contrast. + +**Command:** +```json +{ + "command": "set_contrast", + "params": { + "value": "32" + } +} +``` + +**Response:** +```json +{ + "status": "success", + "message": "Contrast set successfully" +} +``` + +--- + +### 10. Set Saturation + +Adjust color saturation. + +**Command:** +```json +{ + "command": "set_saturation", + "params": { + "value": "64" + } +} +``` + +**Response:** +```json +{ + "status": "success", + "message": "Saturation set successfully" +} +``` + +--- + +### 11. Set Sharpness + +Adjust image sharpness. + +**Command:** +```json +{ + "command": "set_sharpness", + "params": { + "value": "3" + } +} +``` + +**Response:** +```json +{ + "status": "success", + "message": "Sharpness set successfully" +} +``` + +--- + +### 12. Set Gamma + +Adjust gamma correction. + +**Command:** +```json +{ + "command": "set_gamma", + "params": { + "value": "100" + } +} +``` + +**Response:** +```json +{ + "status": "success", + "message": "Gamma set successfully" +} +``` + +--- + +### 13. Set Gain + +Adjust camera gain. + +**Command:** +```json +{ + "command": "set_gain", + "params": { + "value": "0" + } +} +``` + +**Response:** +```json +{ + "status": "success", + "message": "Gain set successfully" +} +``` + +--- + +## Usage Examples + +### Using `socat` + +```bash +# Get available formats +echo '{"command":"get_formats"}' | socat - UNIX-CONNECT:/tmp/vizion_control.sock + +# Set video format +echo '{"command":"set_format","params":{"width":"1920","height":"1080","framerate":"30","format":"YUY2"}}' | socat - UNIX-CONNECT:/tmp/vizion_control.sock + +# Start streaming +echo '{"command":"start_stream"}' | socat - UNIX-CONNECT:/tmp/vizion_control.sock + +# Set exposure to auto +echo '{"command":"set_exposure","params":{"mode":"auto"}}' | socat - UNIX-CONNECT:/tmp/vizion_control.sock + +# Set brightness +echo '{"command":"set_brightness","params":{"value":"50"}}' | socat - UNIX-CONNECT:/tmp/vizion_control.sock + +# Get status +echo '{"command":"get_status"}' | socat - UNIX-CONNECT:/tmp/vizion_control.sock + +# Stop streaming +echo '{"command":"stop_stream"}' | socat - UNIX-CONNECT:/tmp/vizion_control.sock +``` + +### Using `nc` (netcat with Unix socket support) + +```bash +echo '{"command":"get_formats"}' | nc -U /tmp/vizion_control.sock +``` + +### Using Python + +```python +import socket +import json + +def send_command(command, params=None): + sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) + sock.connect('/tmp/vizion_control.sock') + + cmd = {"command": command} + if params: + cmd["params"] = params + + sock.send(json.dumps(cmd).encode()) + response = sock.recv(4096).decode() + sock.close() + + return json.loads(response) + +# Examples +print(send_command("get_formats")) +print(send_command("set_format", { + "width": "1920", + "height": "1080", + "framerate": "30", + "format": "YUY2" +})) +print(send_command("set_exposure", {"mode": "auto"})) +print(send_command("start_stream")) +``` + +### Using C++ + +```cpp +#include +#include +#include +#include +#include + +std::string sendCommand(const std::string& command) { + int sock = socket(AF_UNIX, SOCK_STREAM, 0); + + struct sockaddr_un addr; + memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + strcpy(addr.sun_path, "/tmp/vizion_control.sock"); + + connect(sock, (struct sockaddr*)&addr, sizeof(addr)); + send(sock, command.c_str(), command.length(), 0); + + char buffer[4096]; + int bytesRead = recv(sock, buffer, sizeof(buffer) - 1, 0); + buffer[bytesRead] = '\0'; + + close(sock); + return std::string(buffer); +} + +// Example usage +int main() { + std::cout << sendCommand(R"({"command":"get_formats"})") << std::endl; + std::cout << sendCommand(R"({"command":"set_brightness","params":{"value":"50"}})") << std::endl; + return 0; +} +``` + +## Parameter Value Ranges + +The valid ranges for camera parameters depend on the specific camera model. You can query the camera capabilities through the VizionSDK API or experimentally determine valid ranges. + +**Typical ranges (camera-dependent):** +- Brightness: 0-255 +- Contrast: 0-255 +- Saturation: 0-255 +- Sharpness: 0-255 +- Gamma: 72-500 +- Gain: 0-100 +- Exposure: 1-10000 (in auto mode, value is ignored) +- White Balance Temperature: 2800-6500 Kelvin + +## Error Handling + +Always check the `status` field in the response: + +```python +response = send_command("set_format", {...}) +if response["status"] == "error": + print(f"Command failed: {response['message']}") +else: + print("Command successful") +``` + +## Thread Safety + +The socket server handles one client connection at a time. Commands are processed sequentially with mutex protection to ensure thread safety with the camera operations. + +## Notes + +- The socket file is automatically created when VizionStreamer starts +- The socket file is removed when VizionStreamer exits cleanly +- Format changes require streaming to be stopped first +- Some parameters may not be supported on all camera models +- Invalid parameter values will return an error response diff --git a/SocketServer.cpp b/SocketServer.cpp new file mode 100644 index 0000000..0a93727 --- /dev/null +++ b/SocketServer.cpp @@ -0,0 +1,111 @@ +#include "SocketServer.h" +#include +#include +#include +#include +#include + +SocketServer::SocketServer(const std::string& socketPath) + : socketPath_(socketPath), serverFd_(-1), running_(false) {} + +SocketServer::~SocketServer() { + stop(); +} + +bool SocketServer::start(CommandCallback callback) { + if (running_) { + return false; + } + + commandCallback_ = callback; + + // Remove existing socket file if it exists + unlink(socketPath_.c_str()); + + // Create Unix domain socket + serverFd_ = socket(AF_UNIX, SOCK_STREAM, 0); + if (serverFd_ < 0) { + std::cerr << "Failed to create socket" << std::endl; + return false; + } + + // Bind socket + struct sockaddr_un addr; + memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + strncpy(addr.sun_path, socketPath_.c_str(), sizeof(addr.sun_path) - 1); + + if (bind(serverFd_, (struct sockaddr*)&addr, sizeof(addr)) < 0) { + std::cerr << "Failed to bind socket: " << strerror(errno) << std::endl; + close(serverFd_); + return false; + } + + // Listen for connections + if (listen(serverFd_, 5) < 0) { + std::cerr << "Failed to listen on socket" << std::endl; + close(serverFd_); + unlink(socketPath_.c_str()); + return false; + } + + running_ = true; + serverThread_ = std::make_unique(&SocketServer::serverLoop, this); + + std::cout << "Socket server started on " << socketPath_ << std::endl; + return true; +} + +void SocketServer::stop() { + if (!running_) { + return; + } + + running_ = false; + + // Close server socket to unblock accept() + if (serverFd_ >= 0) { + shutdown(serverFd_, SHUT_RDWR); + close(serverFd_); + serverFd_ = -1; + } + + // Wait for server thread to finish + if (serverThread_ && serverThread_->joinable()) { + serverThread_->join(); + } + + unlink(socketPath_.c_str()); + std::cout << "Socket server stopped" << std::endl; +} + +void SocketServer::serverLoop() { + while (running_) { + int clientFd = accept(serverFd_, nullptr, nullptr); + if (clientFd < 0) { + if (running_) { + std::cerr << "Accept failed: " << strerror(errno) << std::endl; + } + continue; + } + + handleClient(clientFd); + close(clientFd); + } +} + +void SocketServer::handleClient(int clientFd) { + char buffer[4096]; + ssize_t bytesRead = recv(clientFd, buffer, sizeof(buffer) - 1, 0); + + if (bytesRead > 0) { + buffer[bytesRead] = '\0'; + std::string command(buffer); + + // Call the command callback + std::string response = commandCallback_(command); + + // Send response back to client + send(clientFd, response.c_str(), response.length(), 0); + } +} diff --git a/SocketServer.h b/SocketServer.h new file mode 100644 index 0000000..e114d89 --- /dev/null +++ b/SocketServer.h @@ -0,0 +1,29 @@ +#pragma once + +#include +#include +#include +#include +#include + +class SocketServer { +public: + using CommandCallback = std::function; + + explicit SocketServer(const std::string& socketPath); + ~SocketServer(); + + bool start(CommandCallback callback); + void stop(); + bool isRunning() const { return running_; } + +private: + void serverLoop(); + void handleClient(int clientFd); + + std::string socketPath_; + int serverFd_; + std::atomic running_; + std::unique_ptr serverThread_; + CommandCallback commandCallback_; +}; diff --git a/main.cpp b/main.cpp index ada045f..7d7aa84 100644 --- a/main.cpp +++ b/main.cpp @@ -1,9 +1,22 @@ #include #include +#include +#include #include +#include "SocketServer.h" +#include "CameraController.h" + +std::atomic g_running(true); + +void signalHandler(int signal) { + std::cout << "\nReceived signal " << signal << ", shutting down..." << std::endl; + g_running = false; +} -// TIP To Run code, press or click the icon in the gutter. int main() { + // Setup signal handlers for clean shutdown + signal(SIGINT, signalHandler); + signal(SIGTERM, signalHandler); // List available cameras std::vector devList; @@ -26,7 +39,7 @@ int main() { return -1; } - // Get and set format + // Get and set default format std::vector fmtList; if (VxGetFormatList(cam, fmtList) != 0) { std::cout << "Failed to get format list" << std::endl; @@ -40,31 +53,38 @@ int main() { return -1; } - // Start streaming - if (VxStartStreaming(cam) != 0) { - std::cout << "Failed to start streaming" << std::endl; + std::cout << "Initial format: " << fmtList[0].width << "x" << fmtList[0].height + << " @ " << fmtList[0].framerate << " fps" << std::endl; + + // Create camera controller + auto controller = std::make_shared(cam); + + // Start Unix domain socket server + const std::string socketPath = "/tmp/vizion_control.sock"; + SocketServer server(socketPath); + + if (!server.start([controller](const std::string& cmd) { + return controller->processCommand(cmd); + })) { + std::cout << "Failed to start socket server" << std::endl; VxClose(cam); return -1; } - // Capture 5 frames - std::shared_ptr buffer(new uint8_t[fmtList[0].width * fmtList[0].height * 3]); - int dataSize; + std::cout << "\nVizion Streamer is running." << std::endl; + std::cout << "Control socket: " << socketPath << std::endl; + std::cout << "Press Ctrl+C to exit.\n" << std::endl; - for (int i = 0; i < 5; i++) { - VX_CAPTURE_RESULT result = VxGetImage(cam, buffer.get(), &dataSize, 1000); - if (result == VX_CAPTURE_RESULT::VX_SUCCESS) { - std::cout << "Successfully captured frame " << i + 1 << " of size: " << dataSize << " bytes" << std::endl; - } else { - std::cout << "Failed to capture frame " << i + 1 << std::endl; - break; - } + // Main loop - keep running until signaled to stop + while (g_running) { + std::this_thread::sleep_for(std::chrono::milliseconds(100)); } // Cleanup - VxStopStreaming(cam); + std::cout << "Shutting down..." << std::endl; + server.stop(); VxClose(cam); + std::cout << "Shutdown complete." << std::endl; return 0; - // TIP See CLion help at jetbrains.com/help/clion/. Also, you can try interactive lessons for CLion by selecting 'Help | Learn IDE Features' from the main menu. } \ No newline at end of file