├── .gitignore ├── CMakeLists.txt ├── PelcoDProtocolParser ├── CMakeLists.txt └── src │ ├── CMakeLists.txt │ ├── PelcoDProtocolParser.cpp │ └── PelcoDProtocolParser.h ├── PelcoDProtocolParserTests ├── CMakeLists.txt └── PelcoDDemoApplication │ ├── CMakeLists.txt │ ├── PelcoDDemoApplication.json │ ├── SerialPort.cpp │ ├── SerialPort.h │ ├── TCPPort.cpp │ ├── TCPPort.h │ ├── json.hpp │ └── main.cpp ├── README.md └── _static ├── pelco-d_logo_with_borders.png └── user_interface.png /.gitignore: -------------------------------------------------------------------------------- 1 | # User-specific files 2 | *.suo 3 | *.user 4 | *.userosscache 5 | *.sln.docstates 6 | 7 | # User-specific files (MonoDevelop/Xamarin Studio) 8 | *.userprefs 9 | 10 | # Build results 11 | [Bb]uild/ 12 | .vs/ 13 | out/ -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.13) 2 | 3 | 4 | ############################################################################### 5 | ## PROJECT 6 | ############################################################################### 7 | project(Pelco_D_Protocol_Parser LANGUAGES CXX) 8 | 9 | 10 | ############################################################################### 11 | ## SETTINGS 12 | ## basic project settings before use 13 | ############################################################################### 14 | # If this project is used as a submodule, the variable should be overridden 15 | # to "OFF" in the top-level application (to disable forced cache rewriting) 16 | option(${PARENT}_SUBMODULE_CACHE_OVERWRITE "Enable forced cache rewriting" ON) 17 | if (${PARENT}_SUBMODULE_CACHE_OVERWRITE) 18 | SET(REWRITE_FORCE "FORCE") 19 | else() 20 | SET(REWRITE_FORCE "") 21 | endif() 22 | 23 | 24 | ############################################################################### 25 | ## CONFIGURATION 26 | ## project configuration 27 | ############################################################################### 28 | # API class 29 | SET(${PARENT}_PELCO_D_PROTOCOL_PARSER ON CACHE BOOL "" ${REWRITE_FORCE}) 30 | SET(${PARENT}_PELCO_D_PROTOCOL_PARSER_TESTS ON CACHE BOOL "" ${REWRITE_FORCE}) 31 | 32 | 33 | ## add projects 34 | if (${PARENT}_PELCO_D_PROTOCOL_PARSER) 35 | add_subdirectory(PelcoDProtocolParser) 36 | endif() 37 | 38 | if (${PARENT}_PELCO_D_PROTOCOL_PARSER_TESTS) 39 | add_subdirectory(PelcoDProtocolParserTests) 40 | endif() 41 | 42 | -------------------------------------------------------------------------------- /PelcoDProtocolParser/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.13) 2 | 3 | 4 | 5 | ############################################################################### 6 | ## PROJECT 7 | ## name and version 8 | ############################################################################### 9 | project(PelcoDProtocolParser LANGUAGES CXX) 10 | 11 | 12 | 13 | ############################################################################### 14 | ## INCLUDING SUBDIRECTORIES 15 | ## Adding subdirectories according to the configuration 16 | ############################################################################### 17 | #src 18 | add_subdirectory(src) -------------------------------------------------------------------------------- /PelcoDProtocolParser/src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.5) 2 | 3 | 4 | ## 5 | ## PROJECT 6 | ## name and version 7 | ## 8 | project(PelcoDProtocolParser LANGUAGES CXX) 9 | 10 | 11 | ## 12 | ## CONFIGURATION 13 | ## 14 | set(CMAKE_INCLUDE_CURRENT_DIR ON) 15 | set(CMAKE_CXX_STANDARD 11) 16 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 17 | 18 | # Enabling export of all symbols to create a dynamic library 19 | set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON) 20 | set(CMAKE_POSITION_INDEPENDENT_CODE ON) 21 | 22 | # Creating output directory architecture in accordance with GNU guidelines 23 | set(BINARY_DIR "${CMAKE_BINARY_DIR}") 24 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${BINARY_DIR}/bin") 25 | set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${BINARY_DIR}/lib") 26 | 27 | 28 | # create glob files for *.h, *.cpp 29 | file (GLOB H_FILES ${CMAKE_CURRENT_SOURCE_DIR}/*.h) 30 | file (GLOB CPP_FILES ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp) 31 | # concatenate the results (glob files) to variable 32 | set (SOURCES ${CPP_FILES} ${H_FILES}) 33 | 34 | 35 | ## 36 | ## TARGET 37 | ## create target and add include path 38 | ## 39 | add_library(${PROJECT_NAME} STATIC ${SOURCES}) 40 | set_target_properties(${PROJECT_NAME} PROPERTIES PREFIX "") 41 | 42 | target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) 43 | -------------------------------------------------------------------------------- /PelcoDProtocolParser/src/PelcoDProtocolParser.cpp: -------------------------------------------------------------------------------- 1 | #include "PelcoDProtocolParser.h" 2 | 3 | 4 | 5 | pelco::PelcoDProtocolParser::PelcoDProtocolParser() 6 | { 7 | 8 | } 9 | 10 | 11 | 12 | pelco::PelcoDProtocolParser::~PelcoDProtocolParser() 13 | { 14 | 15 | } 16 | 17 | 18 | 19 | void pelco::PelcoDProtocolParser::GetChecksum(uint8_t* packet) 20 | { 21 | uint32_t checksum = (uint32_t)packet[1] + (uint32_t)packet[2] + (uint32_t)packet[3] + (uint32_t)packet[4] + (uint32_t)packet[5]; 22 | checksum %= 0x100; 23 | 24 | packet[6] = (uint8_t)checksum; 25 | } 26 | 27 | 28 | bool pelco::PelcoDProtocolParser::GetCommand( 29 | uint8_t* packet, 30 | uint8_t address, 31 | pelco::PelcoDCommands command_ID, 32 | uint8_t data_1, 33 | uint8_t data_2) 34 | { 35 | // Check command. 36 | switch (command_ID) 37 | { 38 | case pelco::PelcoDCommands::CAMERA_ON: 39 | packet[0] = 0xFF; 40 | packet[1] = address; 41 | packet[2] = 0x0A; 42 | packet[3] = 0x00; 43 | packet[4] = 0x00; 44 | packet[5] = 0x00; 45 | GetChecksum(packet); 46 | return true; 47 | 48 | case pelco::PelcoDCommands::CAMERA_OFF: 49 | packet[0] = 0xFF; 50 | packet[1] = address; 51 | packet[2] = 0x02; 52 | packet[3] = 0x00; 53 | packet[4] = 0x00; 54 | packet[5] = 0x00; 55 | GetChecksum(packet); 56 | return true; 57 | 58 | case pelco::PelcoDCommands::IRIS_CLOSE: 59 | packet[0] = 0xFF; 60 | packet[1] = address; 61 | packet[2] = 0x08; 62 | packet[3] = 0x00; 63 | packet[4] = 0x00; 64 | packet[5] = 0x00; 65 | GetChecksum(packet); 66 | return true; 67 | 68 | case pelco::PelcoDCommands::IRIS_OPEN: 69 | packet[0] = 0xFF; 70 | packet[1] = address; 71 | packet[2] = 0x0A; 72 | packet[3] = 0x00; 73 | packet[4] = 0x00; 74 | packet[5] = 0x00; 75 | GetChecksum(packet); 76 | return true; 77 | 78 | case pelco::PelcoDCommands::FOCUS_NEAR: 79 | packet[0] = 0xFF; 80 | packet[1] = address; 81 | packet[2] = 0x09; 82 | packet[3] = 0x00; 83 | packet[4] = 0x00; 84 | packet[5] = 0x00; 85 | GetChecksum(packet); 86 | return true; 87 | 88 | case pelco::PelcoDCommands::FOCUS_FAR: 89 | packet[0] = 0xFF; 90 | packet[1] = address; 91 | packet[2] = 0x08; 92 | packet[3] = 0x80; 93 | packet[4] = 0x00; 94 | packet[5] = 0x00; 95 | GetChecksum(packet); 96 | return true; 97 | 98 | case pelco::PelcoDCommands::FOCUS_STOP: 99 | packet[0] = 0xFF; 100 | packet[1] = address; 101 | packet[2] = 0x08; 102 | packet[3] = 0x00; 103 | packet[4] = 0x00; 104 | packet[5] = 0x00; 105 | GetChecksum(packet); 106 | return true; 107 | 108 | case pelco::PelcoDCommands::ZOOM_WIDE: 109 | packet[0] = 0xFF; 110 | packet[1] = address; 111 | packet[2] = 0x08; 112 | packet[3] = 0x40; 113 | packet[4] = 0x00; 114 | packet[5] = 0x00; 115 | GetChecksum(packet); 116 | return true; 117 | 118 | case pelco::PelcoDCommands::ZOOM_TELE: 119 | packet[0] = 0xFF; 120 | packet[1] = address; 121 | packet[2] = 0x08; 122 | packet[3] = 0x20; 123 | packet[4] = 0x00; 124 | packet[5] = 0x00; 125 | GetChecksum(packet); 126 | return true; 127 | 128 | case pelco::PelcoDCommands::ZOOM_STOP: 129 | packet[0] = 0xFF; 130 | packet[1] = address; 131 | packet[2] = 0x08; 132 | packet[3] = 0x00; 133 | packet[4] = 0x00; 134 | packet[5] = 0x00; 135 | GetChecksum(packet); 136 | return true; 137 | 138 | case pelco::PelcoDCommands::DOWN: 139 | packet[0] = 0xFF; 140 | packet[1] = address; 141 | packet[2] = 0x08; 142 | packet[3] = 0x10; 143 | packet[4] = 0x00; 144 | packet[5] = data_2; 145 | GetChecksum(packet); 146 | return true; 147 | 148 | case pelco::PelcoDCommands::UP: 149 | packet[0] = 0xFF; 150 | packet[1] = address; 151 | packet[2] = 0x08; 152 | packet[3] = 0x08; 153 | packet[4] = 0x00; 154 | packet[5] = data_2; 155 | GetChecksum(packet); 156 | return true; 157 | 158 | case pelco::PelcoDCommands::LEFT: 159 | packet[0] = 0xFF; 160 | packet[1] = address; 161 | packet[2] = 0x08; 162 | packet[3] = 0x04; 163 | packet[4] = data_1; 164 | packet[5] = 0x00; 165 | GetChecksum(packet); 166 | return true; 167 | 168 | case pelco::PelcoDCommands::RIGHT: 169 | packet[0] = 0xFF; 170 | packet[1] = address; 171 | packet[2] = 0x08; 172 | packet[3] = 0x02; 173 | packet[4] = data_1; 174 | packet[5] = 0x00; 175 | GetChecksum(packet); 176 | return true; 177 | 178 | case pelco::PelcoDCommands::UP_RIGHT: 179 | packet[0] = 0xFF; 180 | packet[1] = address; 181 | packet[2] = 0x08; 182 | packet[3] = 0x0A; 183 | packet[4] = data_1; 184 | packet[5] = data_2; 185 | GetChecksum(packet); 186 | return true; 187 | 188 | case pelco::PelcoDCommands::UP_LEFT: 189 | packet[0] = 0xFF; 190 | packet[1] = address; 191 | packet[2] = 0x08; 192 | packet[3] = 0x0C; 193 | packet[4] = data_1; 194 | packet[5] = data_2; 195 | GetChecksum(packet); 196 | return true; 197 | 198 | case pelco::PelcoDCommands::DOWN_RIGHT: 199 | packet[0] = 0xFF; 200 | packet[1] = address; 201 | packet[2] = 0x08; 202 | packet[3] = 0x12; 203 | packet[4] = data_1; 204 | packet[5] = data_2; 205 | GetChecksum(packet); 206 | return true; 207 | 208 | case pelco::PelcoDCommands::DOWN_LEFT: 209 | packet[0] = 0xFF; 210 | packet[1] = address; 211 | packet[2] = 0x08; 212 | packet[3] = 0x14; 213 | packet[4] = data_1; 214 | packet[5] = data_2; 215 | GetChecksum(packet); 216 | return true; 217 | 218 | case pelco::PelcoDCommands::STOP: 219 | packet[0] = 0xFF; 220 | packet[1] = address; 221 | packet[2] = 0x08; 222 | packet[3] = 0x00; 223 | packet[4] = 0x00; 224 | packet[5] = 0x00; 225 | GetChecksum(packet); 226 | return true; 227 | 228 | case pelco::PelcoDCommands::SET_PRESET: 229 | packet[0] = 0xFF; 230 | packet[1] = address; 231 | packet[2] = 0x00; 232 | packet[3] = 0x03; 233 | packet[4] = 0x00; 234 | packet[5] = data_1; 235 | GetChecksum(packet); 236 | return true; 237 | 238 | case pelco::PelcoDCommands::CLEAR_PRESET: 239 | packet[0] = 0xFF; 240 | packet[1] = address; 241 | packet[2] = 0x00; 242 | packet[3] = 0x05; 243 | packet[4] = 0x00; 244 | packet[5] = data_1; 245 | GetChecksum(packet); 246 | return true; 247 | 248 | case pelco::PelcoDCommands::GO_TO_PRESET: 249 | packet[0] = 0xFF; 250 | packet[1] = address; 251 | packet[2] = 0x00; 252 | packet[3] = 0x07; 253 | packet[4] = 0x00; 254 | packet[5] = data_1; 255 | GetChecksum(packet); 256 | return true; 257 | 258 | case pelco::PelcoDCommands::FLIP_180DEG_ABOUT: 259 | packet[0] = 0xFF; 260 | packet[1] = address; 261 | packet[2] = 0x00; 262 | packet[3] = 0x07; 263 | packet[4] = 0x00; 264 | packet[5] = 0x21; 265 | GetChecksum(packet); 266 | return true; 267 | 268 | case pelco::PelcoDCommands::GO_TO_ZERO_PAN: 269 | packet[0] = 0xFF; 270 | packet[1] = address; 271 | packet[2] = 0x00; 272 | packet[3] = 0x07; 273 | packet[4] = 0x00; 274 | packet[5] = 0x22; 275 | GetChecksum(packet); 276 | return true; 277 | 278 | case pelco::PelcoDCommands::SET_AUXILIARY: 279 | packet[0] = 0xFF; 280 | packet[1] = address; 281 | packet[2] = 0x00; 282 | packet[3] = 0x09; 283 | packet[4] = 0x00; 284 | packet[5] = data_1; 285 | GetChecksum(packet); 286 | return true; 287 | 288 | case pelco::PelcoDCommands::CLEAR_AUXILIARY: 289 | packet[0] = 0xFF; 290 | packet[1] = address; 291 | packet[2] = 0x00; 292 | packet[3] = 0x0B; 293 | packet[4] = 0x00; 294 | packet[5] = data_1; 295 | GetChecksum(packet); 296 | return true; 297 | 298 | case pelco::PelcoDCommands::REMOTE_RESET: 299 | packet[0] = 0xFF; 300 | packet[1] = address; 301 | packet[2] = 0x00; 302 | packet[3] = 0x0F; 303 | packet[4] = 0x00; 304 | packet[5] = 0x00; 305 | GetChecksum(packet); 306 | return true; 307 | 308 | case pelco::PelcoDCommands::SET_ZONE_START: 309 | packet[0] = 0xFF; 310 | packet[1] = address; 311 | packet[2] = 0x00; 312 | packet[3] = 0x11; 313 | packet[4] = 0x00; 314 | packet[5] = data_1; 315 | GetChecksum(packet); 316 | return true; 317 | 318 | case pelco::PelcoDCommands::SET_ZONE_END: 319 | packet[0] = 0xFF; 320 | packet[1] = address; 321 | packet[2] = 0x00; 322 | packet[3] = 0x13; 323 | packet[4] = 0x00; 324 | packet[5] = data_1; 325 | GetChecksum(packet); 326 | return true; 327 | 328 | case pelco::PelcoDCommands::WRITE_CHAR_TO_SCREEN: 329 | packet[0] = 0xFF; 330 | packet[1] = address; 331 | packet[2] = 0x00; 332 | packet[3] = 0x15; 333 | packet[4] = data_1; 334 | packet[5] = data_2; 335 | GetChecksum(packet); 336 | return true; 337 | 338 | case pelco::PelcoDCommands::CLEAR_SCREEN: 339 | packet[0] = 0xFF; 340 | packet[1] = address; 341 | packet[2] = 0x00; 342 | packet[3] = 0x17; 343 | packet[4] = 0x00; 344 | packet[5] = 0x00; 345 | GetChecksum(packet); 346 | return true; 347 | 348 | case pelco::PelcoDCommands::ALARM_ACKNOWLEDGE: 349 | packet[0] = 0xFF; 350 | packet[1] = address; 351 | packet[2] = 0x00; 352 | packet[3] = 0x19; 353 | packet[4] = 0x00; 354 | packet[5] = data_1; 355 | GetChecksum(packet); 356 | return true; 357 | 358 | case pelco::PelcoDCommands::ZONE_SCAN_ON: 359 | packet[0] = 0xFF; 360 | packet[1] = address; 361 | packet[2] = 0x00; 362 | packet[3] = 0x1B; 363 | packet[4] = 0x00; 364 | packet[5] = 0x00; 365 | GetChecksum(packet); 366 | return true; 367 | 368 | case pelco::PelcoDCommands::ZONE_SCAN_OFF: 369 | packet[0] = 0xFF; 370 | packet[1] = address; 371 | packet[2] = 0x00; 372 | packet[3] = 0x1D; 373 | packet[4] = 0x00; 374 | packet[5] = 0x00; 375 | GetChecksum(packet); 376 | return true; 377 | 378 | case pelco::PelcoDCommands::SET_PATTERN_START: 379 | packet[0] = 0xFF; 380 | packet[1] = address; 381 | packet[2] = 0x00; 382 | packet[3] = 0x1F; 383 | packet[4] = 0x00; 384 | packet[5] = 0x00; 385 | GetChecksum(packet); 386 | return true; 387 | 388 | case pelco::PelcoDCommands::SET_PATTERN_STOP: 389 | packet[0] = 0xFF; 390 | packet[1] = address; 391 | packet[2] = 0x00; 392 | packet[3] = 0x21; 393 | packet[4] = 0x00; 394 | packet[5] = 0x00; 395 | GetChecksum(packet); 396 | return true; 397 | 398 | case pelco::PelcoDCommands::RUN_PATTERN: 399 | packet[0] = 0xFF; 400 | packet[1] = address; 401 | packet[2] = 0x00; 402 | packet[3] = 0x23; 403 | packet[4] = 0x00; 404 | packet[5] = 0x00; 405 | GetChecksum(packet); 406 | return true; 407 | 408 | case pelco::PelcoDCommands::SET_ZOOM_SPEED: 409 | packet[0] = 0xFF; 410 | packet[1] = address; 411 | packet[2] = 0x00; 412 | packet[3] = 0x25; 413 | packet[4] = 0x00; 414 | packet[5] = data_1; 415 | GetChecksum(packet); 416 | return true; 417 | 418 | case pelco::PelcoDCommands::SET_FOCUS_SPEED: 419 | packet[0] = 0xFF; 420 | packet[1] = address; 421 | packet[2] = 0x00; 422 | packet[3] = 0x27; 423 | packet[4] = 0x00; 424 | packet[5] = data_1; 425 | GetChecksum(packet); 426 | return true; 427 | 428 | case pelco::PelcoDCommands::RESET_CAMERA_TO_DEFAULT: 429 | packet[0] = 0xFF; 430 | packet[1] = address; 431 | packet[2] = 0x00; 432 | packet[3] = 0x29; 433 | packet[4] = 0x00; 434 | packet[5] = 0x00; 435 | GetChecksum(packet); 436 | return true; 437 | 438 | case pelco::PelcoDCommands::AUTO_FOCUS_AUTO_ON_OFF: 439 | packet[0] = 0xFF; 440 | packet[1] = address; 441 | packet[2] = 0x00; 442 | packet[3] = 0x2B; 443 | packet[4] = 0x00; 444 | packet[5] = data_1; 445 | GetChecksum(packet); 446 | return true; 447 | 448 | case pelco::PelcoDCommands::AUTO_IRIS_AUTO_ON_OFF: 449 | packet[0] = 0xFF; 450 | packet[1] = address; 451 | packet[2] = 0x00; 452 | packet[3] = 0x2D; 453 | packet[4] = 0x00; 454 | packet[5] = data_1; 455 | GetChecksum(packet); 456 | return true; 457 | 458 | case pelco::PelcoDCommands::AGC_AUTO_ON_OFF: 459 | packet[0] = 0xFF; 460 | packet[1] = address; 461 | packet[2] = 0x00; 462 | packet[3] = 0x2F; 463 | packet[4] = 0x00; 464 | packet[5] = data_1; 465 | GetChecksum(packet); 466 | return true; 467 | 468 | case pelco::PelcoDCommands::BACKLIGHT_COMPENSATION_ON_OFF: 469 | packet[0] = 0xFF; 470 | packet[1] = address; 471 | packet[2] = 0x00; 472 | packet[3] = 0x31; 473 | packet[4] = 0x00; 474 | packet[5] = data_1; 475 | GetChecksum(packet); 476 | return true; 477 | 478 | case pelco::PelcoDCommands::AUTO_WHITE_BALANCE_ON_OFF: 479 | packet[0] = 0xFF; 480 | packet[1] = address; 481 | packet[2] = 0x00; 482 | packet[3] = 0x33; 483 | packet[4] = 0x00; 484 | packet[5] = data_1; 485 | GetChecksum(packet); 486 | return true; 487 | 488 | case pelco::PelcoDCommands::ENABLE_DEVICE_PHASE_DELAY_MODE: 489 | packet[0] = 0xFF; 490 | packet[1] = address; 491 | packet[2] = 0x00; 492 | packet[3] = 0x35; 493 | packet[4] = 0x00; 494 | packet[5] = 0x00; 495 | GetChecksum(packet); 496 | return true; 497 | 498 | case pelco::PelcoDCommands::SET_SHUTTER_SPEED: 499 | packet[0] = 0xFF; 500 | packet[1] = address; 501 | packet[2] = 0x00; 502 | packet[3] = 0x37; 503 | packet[4] = 0x00; 504 | packet[5] = data_1; 505 | GetChecksum(packet); 506 | return true; 507 | 508 | case pelco::PelcoDCommands::ADJUST_LINE_LOCK_PHASE_DELAY: 509 | packet[0] = 0xFF; 510 | packet[1] = address; 511 | packet[2] = data_1; 512 | packet[3] = 0x39; 513 | packet[4] = 0x00; 514 | packet[5] = 0x00; 515 | GetChecksum(packet); 516 | return true; 517 | 518 | case pelco::PelcoDCommands::ADJUST_WHITE_BALANCE_R_B: 519 | packet[0] = 0xFF; 520 | packet[1] = address; 521 | packet[2] = data_1; 522 | packet[3] = 0x3B; 523 | packet[4] = 0x00; 524 | packet[5] = 0x00; 525 | GetChecksum(packet); 526 | return true; 527 | 528 | case pelco::PelcoDCommands::ADJUST_WHITE_BALANCE_M_G: 529 | packet[0] = 0xFF; 530 | packet[1] = address; 531 | packet[2] = data_1; 532 | packet[3] = 0x3D; 533 | packet[4] = 0x00; 534 | packet[5] = 0x00; 535 | GetChecksum(packet); 536 | return true; 537 | 538 | case pelco::PelcoDCommands::ADJUST_GAIN: 539 | packet[0] = 0xFF; 540 | packet[1] = address; 541 | packet[2] = data_1; 542 | packet[3] = 0x3F; 543 | packet[4] = 0x00; 544 | packet[5] = 0x00; 545 | GetChecksum(packet); 546 | return true; 547 | 548 | case pelco::PelcoDCommands::ADJUST_AUTO_IRIS_LEVEL: 549 | packet[0] = 0xFF; 550 | packet[1] = address; 551 | packet[2] = data_1; 552 | packet[3] = 0x41; 553 | packet[4] = 0x00; 554 | packet[5] = 0x00; 555 | GetChecksum(packet); 556 | return true; 557 | 558 | case pelco::PelcoDCommands::ADJUST_AUTO_IRIS_PEACK_VALUE: 559 | packet[0] = 0xFF; 560 | packet[1] = address; 561 | packet[2] = data_1; 562 | packet[3] = 0x43; 563 | packet[4] = 0x00; 564 | packet[5] = 0x00; 565 | GetChecksum(packet); 566 | return true; 567 | 568 | case pelco::PelcoDCommands::QUERY: 569 | packet[0] = 0xFF; 570 | packet[1] = address; 571 | packet[2] = 0x00; 572 | packet[3] = 0x45; 573 | packet[4] = 0x00; 574 | packet[5] = 0x00; 575 | GetChecksum(packet); 576 | return true; 577 | 578 | default: 579 | return false; 580 | } 581 | } 582 | -------------------------------------------------------------------------------- /PelcoDProtocolParser/src/PelcoDProtocolParser.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | 4 | namespace pelco 5 | { 6 | enum class PelcoDCommands 7 | { 8 | // Main commands. 9 | CAMERA_ON, 10 | CAMERA_OFF, 11 | IRIS_CLOSE, 12 | IRIS_OPEN, 13 | FOCUS_NEAR, 14 | FOCUS_FAR, 15 | FOCUS_STOP, 16 | ZOOM_WIDE, 17 | ZOOM_TELE, 18 | ZOOM_STOP, 19 | DOWN, 20 | UP, 21 | LEFT, 22 | RIGHT, 23 | UP_RIGHT, 24 | UP_LEFT, 25 | DOWN_RIGHT, 26 | DOWN_LEFT, 27 | STOP, 28 | 29 | // Extended commands. 30 | SET_PRESET, 31 | CLEAR_PRESET, 32 | GO_TO_PRESET, 33 | FLIP_180DEG_ABOUT, 34 | GO_TO_ZERO_PAN, 35 | SET_AUXILIARY, 36 | CLEAR_AUXILIARY, 37 | REMOTE_RESET, 38 | SET_ZONE_START, 39 | SET_ZONE_END, 40 | WRITE_CHAR_TO_SCREEN, 41 | CLEAR_SCREEN, 42 | ALARM_ACKNOWLEDGE, 43 | ZONE_SCAN_ON, 44 | ZONE_SCAN_OFF, 45 | SET_PATTERN_START, 46 | SET_PATTERN_STOP, 47 | RUN_PATTERN, 48 | SET_ZOOM_SPEED, 49 | SET_FOCUS_SPEED, 50 | RESET_CAMERA_TO_DEFAULT, 51 | AUTO_FOCUS_AUTO_ON_OFF, 52 | AUTO_IRIS_AUTO_ON_OFF, 53 | AGC_AUTO_ON_OFF, 54 | BACKLIGHT_COMPENSATION_ON_OFF, 55 | AUTO_WHITE_BALANCE_ON_OFF, 56 | ENABLE_DEVICE_PHASE_DELAY_MODE, 57 | SET_SHUTTER_SPEED, 58 | ADJUST_LINE_LOCK_PHASE_DELAY, 59 | ADJUST_WHITE_BALANCE_R_B, 60 | ADJUST_WHITE_BALANCE_M_G, 61 | ADJUST_GAIN, 62 | ADJUST_AUTO_IRIS_LEVEL, 63 | ADJUST_AUTO_IRIS_PEACK_VALUE, 64 | QUERY 65 | }; 66 | 67 | /** 68 | * @brief Pelco-D protocol parser class. 69 | */ 70 | class PelcoDProtocolParser 71 | { 72 | public: 73 | 74 | /** 75 | * @brief Class constructor. 76 | */ 77 | PelcoDProtocolParser(); 78 | 79 | /** 80 | * @brief Class destructor. 81 | */ 82 | ~PelcoDProtocolParser(); 83 | 84 | /** 85 | * @brief Method to encode Pelco-D command (Always 7 bytes). 86 | * 87 | * @param packet Pointer to packet buffer. Always 7 bytes. 88 | * @param address Camera address (usually 1). 89 | * @param command_ID ID of command. 90 | * @param data_1 First byte of command data (according to Pelco-D specification). 91 | * @param data_2 Second byte of command data (according to Pelco-D specification). 92 | * @return true If the command has been encoded. 93 | * @return false In case any errors. 94 | */ 95 | bool GetCommand( 96 | uint8_t* packet, 97 | uint8_t address, 98 | pelco::PelcoDCommands command_ID, 99 | uint8_t data_1 = 0, 100 | uint8_t data_2 = 0); 101 | 102 | private: 103 | 104 | void GetChecksum(uint8_t* packet); 105 | 106 | }; 107 | } 108 | -------------------------------------------------------------------------------- /PelcoDProtocolParserTests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.13) 2 | 3 | 4 | 5 | ############################################################################### 6 | ## 3RD-PARTY 7 | ## dependencies for the project 8 | ############################################################################### 9 | project(PelcoDProtocolParserTests LANGUAGES CXX) 10 | 11 | 12 | 13 | ############################################################################### 14 | ## SETTINGS 15 | ## basic 3rd-party settings before use 16 | ############################################################################### 17 | 18 | # Disable self-overwriting of parameters inside included subdirectories. 19 | SET(${PARENT}_SUBMODULE_CACHE_OVERWRITE OFF CACHE BOOL "" FORCE) 20 | 21 | 22 | 23 | ############################################################################### 24 | ## CONFIGURATION 25 | ## 3rd-party submodules configuration 26 | ############################################################################## 27 | SET(${PARENT}_PELCO_D_DEMO_APPLICATION ON CACHE BOOL "" FORCE) 28 | 29 | 30 | if (${PARENT}_PELCO_D_DEMO_APPLICATION) 31 | add_subdirectory(PelcoDDemoApplication) 32 | endif() 33 | -------------------------------------------------------------------------------- /PelcoDProtocolParserTests/PelcoDDemoApplication/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.13) 2 | 3 | 4 | 5 | ############################################################################### 6 | ## EXECUTABLE-PROJECT 7 | ## name and version 8 | ############################################################################### 9 | project(PelcoDDemoApplication LANGUAGES CXX) 10 | 11 | 12 | 13 | ############################################################################### 14 | ## SETTINGS 15 | ## basic project settings before use 16 | ############################################################################### 17 | set(CMAKE_INCLUDE_CURRENT_DIR ON) 18 | set(CMAKE_CXX_STANDARD 11) 19 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 20 | # creating output directory architecture in accordance with GNU guidelines 21 | set(BINARY_DIR "${CMAKE_BINARY_DIR}") 22 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${BINARY_DIR}/bin") 23 | set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${BINARY_DIR}/lib") 24 | 25 | 26 | 27 | ############################################################################### 28 | ## TARGET 29 | ## create target and add include path 30 | ############################################################################### 31 | # create glob files for *.h, *.cpp 32 | file (GLOB H_FILES ${CMAKE_CURRENT_SOURCE_DIR}/*.h) 33 | file (GLOB CPP_FILES ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp) 34 | file (GLOB HPP_FILES ${CMAKE_CURRENT_SOURCE_DIR}/*.hpp) 35 | # concatenate the results (glob files) to variable 36 | set (SOURCES ${CPP_FILES} ${H_FILES} ${HPP_FILES}) 37 | # create executable from src 38 | add_executable(${PROJECT_NAME} ${SOURCES}) 39 | 40 | 41 | 42 | ############################################################################### 43 | ## LINK LIBRARIES 44 | ## linking all dependencies 45 | ############################################################################### 46 | find_package(OpenCV) 47 | target_link_libraries(${PROJECT_NAME} ${OpenCV_LIBS}) 48 | 49 | target_link_libraries(${PROJECT_NAME} PelcoDProtocolParser) 50 | 51 | -------------------------------------------------------------------------------- /PelcoDProtocolParserTests/PelcoDDemoApplication/PelcoDDemoApplication.json: -------------------------------------------------------------------------------- 1 | { 2 | "Video_Source_Params": 3 | { 4 | "video_source_init_string":"test.mp4" 5 | }, 6 | 7 | "Communication_Params": 8 | { 9 | "camera_IP":"127.0.0.1", 10 | "TCP_port":-1, 11 | "serial_port_name":"none", 12 | "serial_port_baudrate":115200, 13 | "camera_address":1, 14 | "max_PTU_speed":63 15 | } 16 | } -------------------------------------------------------------------------------- /PelcoDProtocolParserTests/PelcoDDemoApplication/SerialPort.cpp: -------------------------------------------------------------------------------- 1 | #define _CRT_SECURE_NO_WARNINGS 2 | #include "SerialPort.h" 3 | #include 4 | 5 | 6 | clib::SerialPort::SerialPort() 7 | { 8 | // Init variabes by default values. 9 | initFlag = false; 10 | Cport = 0; 11 | } 12 | 13 | 14 | clib::SerialPort::~SerialPort() 15 | { 16 | // Close serial port. 17 | Close(); 18 | } 19 | 20 | 21 | void clib::SerialPort::Close() 22 | { 23 | 24 | #if defined(linux) || defined(__linux) || defined(__linux__)|| defined(__FreeBSD__) 25 | if (initFlag) { 26 | close(Cport); 27 | flock(Cport, LOCK_UN); 28 | }//if... 29 | initFlag = false; 30 | #elif defined(_WIN32) || defined(__WIN32__) || defined(WIN32) 31 | if (initFlag) CloseHandle(Cport); 32 | initFlag = false; 33 | #endif 34 | 35 | } 36 | 37 | 38 | int clib::SerialPort::ReadData(uint8_t *buf, uint32_t size) 39 | { 40 | if (!initFlag) 41 | return -1; 42 | 43 | #if defined(linux) || defined(__linux) || defined(__linux__)|| defined(__FreeBSD__) 44 | return read(Cport, buf, size); 45 | #elif defined(_WIN32) || defined(__WIN32__) || defined(WIN32) 46 | int n; 47 | if (ReadFile(Cport, buf, size, (LPDWORD)((void*)&n), NULL)) 48 | return n; 49 | #endif 50 | 51 | return -1; 52 | } 53 | 54 | 55 | int clib::SerialPort::SendData(uint8_t *buf, uint32_t size) 56 | { 57 | if (!initFlag) 58 | return -1; 59 | 60 | #if defined(linux) || defined(__linux) || defined(__linux__)|| defined(__FreeBSD__) 61 | return write(Cport, buf, size); 62 | #elif defined(_WIN32) || defined(__WIN32__) || defined(WIN32) 63 | int n; 64 | if (WriteFile(Cport, buf, size, (LPDWORD)((void*)&n), NULL)) 65 | return n; 66 | #endif 67 | 68 | return -1; 69 | } 70 | 71 | 72 | bool clib::SerialPort::Open( 73 | const char *comport_file, 74 | unsigned int baudrate, 75 | unsigned int timeout, 76 | const char *mode) { 77 | 78 | if (initFlag) 79 | Close(); 80 | 81 | #if defined(linux) || defined(__linux) || defined(__linux__)|| defined(__FreeBSD__) 82 | 83 | int baudr = 0; 84 | switch (baudrate) 85 | { 86 | case 50: baudr = B50; break; 87 | case 75: baudr = B75; break; 88 | case 110: baudr = B110; break; 89 | case 134: baudr = B134; break; 90 | case 150: baudr = B150; break; 91 | case 200: baudr = B200; break; 92 | case 300: baudr = B300; break; 93 | case 600: baudr = B600; break; 94 | case 1200: baudr = B1200; break; 95 | case 1800: baudr = B1800; break; 96 | case 2400: baudr = B2400; break; 97 | case 4800: baudr = B4800; break; 98 | case 9600: baudr = B9600; break; 99 | case 19200: baudr = B19200; break; 100 | case 38400: baudr = B38400; break; 101 | case 57600: baudr = B57600; break; 102 | case 115200: baudr = B115200; break; 103 | case 230400: baudr = B230400; break; 104 | case 460800: baudr = B460800; break; 105 | case 500000: baudr = B500000; break; 106 | case 576000: baudr = B576000; break; 107 | case 921600: baudr = B921600; break; 108 | case 1000000: baudr = B1000000; break; 109 | case 1152000: baudr = B1152000; break; 110 | case 1500000: baudr = B1500000; break; 111 | case 2000000: baudr = B2000000; break; 112 | case 2500000: baudr = B2500000; break; 113 | case 3000000: baudr = B3000000; break; 114 | case 3500000: baudr = B3500000; break; 115 | case 4000000: baudr = B4000000; break; 116 | default: return false; 117 | } 118 | 119 | if (strlen(mode) != 3) 120 | return false; 121 | 122 | int cbits = CS8; 123 | switch (mode[0]) { 124 | case '8': 125 | cbits = CS8; 126 | break; 127 | case '7': 128 | cbits = CS7; 129 | break; 130 | case '6': 131 | cbits = CS6; 132 | break; 133 | case '5': 134 | cbits = CS5; 135 | break; 136 | default: 137 | return false; 138 | } 139 | 140 | int cpar = 0; 141 | int ipar = IGNPAR; 142 | switch (mode[1]) { 143 | case 'N': case 'n': 144 | cpar = 0; 145 | ipar = IGNPAR; 146 | break; 147 | case 'E': case 'e': 148 | cpar = PARENB; 149 | ipar = INPCK; 150 | break; 151 | case 'O': case 'o': 152 | cpar = (PARENB | PARODD); 153 | ipar = INPCK; 154 | break; 155 | default: 156 | return false; 157 | } 158 | 159 | int bstop = 0; 160 | switch (mode[2]) { 161 | case '1': 162 | bstop = 0; 163 | break; 164 | case '2': 165 | bstop = CSTOPB; 166 | break; 167 | default: 168 | return false; 169 | } 170 | 171 | Cport = open(comport_file, O_RDWR | O_NOCTTY | O_NONBLOCK); 172 | if (Cport == -1) 173 | return false; 174 | 175 | if (flock(Cport, LOCK_EX | LOCK_NB) != 0) { 176 | Close(); 177 | return false; 178 | } 179 | 180 | struct termios port_settings; 181 | memset(&port_settings, 0, sizeof(port_settings)); 182 | port_settings.c_cflag = cbits | cpar | bstop | CLOCAL | CREAD; // control mode flags 183 | port_settings.c_iflag = ipar; // input mode flags 184 | port_settings.c_iflag &= ~(IXON | IXOFF | IXANY); // Turn off software based flow control (XON / XOFF). 185 | port_settings.c_iflag &= ~(ICANON | ECHO | ECHOE | ISIG); // NON Cannonical mode 186 | port_settings.c_oflag = 0; // output mode flags */ 187 | port_settings.c_lflag = 0; // local mode flags */ 188 | port_settings.c_cc[VMIN] = 0; 189 | port_settings.c_cc[VTIME] = 0; 190 | 191 | cfsetispeed(&port_settings, baudr); 192 | cfsetospeed(&port_settings, baudr); 193 | 194 | if (tcsetattr(Cport, TCSANOW, &port_settings) == -1) { 195 | Close(); 196 | return false; 197 | } 198 | 199 | initFlag = true; 200 | 201 | return true; 202 | 203 | #elif defined(_WIN32) || defined(__WIN32__) || defined(WIN32) 204 | 205 | char mode_str[128]; 206 | 207 | switch (baudrate) { 208 | case 110: strcpy(mode_str, "baud=110"); break; 209 | case 300: strcpy(mode_str, "baud=300"); break; 210 | case 600: strcpy(mode_str, "baud=600"); break; 211 | case 1200: strcpy(mode_str, "baud=1200"); break; 212 | case 2400: strcpy(mode_str, "baud=2400"); break; 213 | case 4800: strcpy(mode_str, "baud=4800"); break; 214 | case 9600: strcpy(mode_str, "baud=9600"); break; 215 | case 19200: strcpy(mode_str, "baud=19200"); break; 216 | case 38400: strcpy(mode_str, "baud=38400"); break; 217 | case 57600: strcpy(mode_str, "baud=57600"); break; 218 | case 115200: strcpy(mode_str, "baud=115200"); break; 219 | case 128000: strcpy(mode_str, "baud=128000"); break; 220 | case 256000: strcpy(mode_str, "baud=256000"); break; 221 | case 500000: strcpy(mode_str, "baud=500000"); break; 222 | case 1000000: strcpy(mode_str, "baud=1000000"); break; 223 | default: 224 | return false; 225 | } 226 | 227 | if (strlen(mode) != 3) 228 | return false; 229 | 230 | switch (mode[0]) { 231 | case '8': 232 | strcat(mode_str, " data=8"); 233 | break; 234 | case '7': 235 | strcat(mode_str, " data=7"); 236 | break; 237 | case '6': 238 | strcat(mode_str, " data=6"); 239 | break; 240 | case '5': 241 | strcat(mode_str, " data=5"); 242 | break; 243 | default: return false; 244 | } 245 | 246 | switch (mode[1]) { 247 | case 'N': case 'n': 248 | strcat(mode_str, " parity=n"); 249 | break; 250 | case 'E': case 'e': 251 | strcat(mode_str, " parity=e"); 252 | break; 253 | case 'O': case 'o': 254 | strcat(mode_str, " parity=o"); 255 | break; 256 | default: 257 | return false; 258 | } 259 | 260 | switch (mode[2]) { 261 | case '1': 262 | strcat(mode_str, " stop=1"); 263 | break; 264 | case '2': 265 | strcat(mode_str, " stop=2"); 266 | break; 267 | default: 268 | return false; 269 | } 270 | 271 | strcat(mode_str, " dtr=on rts=on"); 272 | 273 | Cport = CreateFileA(comport_file, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, NULL, NULL); 274 | if (Cport == INVALID_HANDLE_VALUE) 275 | return false; 276 | 277 | DCB port_settings; 278 | memset(&port_settings, 0, sizeof(port_settings)); 279 | port_settings.DCBlength = sizeof(port_settings); 280 | 281 | if (!BuildCommDCBA(mode_str, &port_settings)) { 282 | Close(); 283 | return false; 284 | } 285 | 286 | if (!SetCommState(Cport, &port_settings)) { 287 | Close(); 288 | return false; 289 | } 290 | 291 | COMMTIMEOUTS Cptimeouts; 292 | Cptimeouts.ReadIntervalTimeout = MAXDWORD; 293 | Cptimeouts.ReadTotalTimeoutMultiplier = 0; 294 | Cptimeouts.ReadTotalTimeoutConstant = timeout; 295 | Cptimeouts.WriteTotalTimeoutMultiplier = 0; 296 | Cptimeouts.WriteTotalTimeoutConstant = 0; 297 | 298 | if (!SetCommTimeouts(Cport, &Cptimeouts)) { 299 | Close(); 300 | return false; 301 | } 302 | 303 | initFlag = true; 304 | 305 | return true; 306 | 307 | #endif 308 | 309 | } 310 | 311 | 312 | bool clib::SerialPort::isOpen() 313 | { 314 | return initFlag; 315 | } 316 | -------------------------------------------------------------------------------- /PelcoDProtocolParserTests/PelcoDDemoApplication/SerialPort.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | /** 3 | * \file Header file 4 | */ 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #if defined(linux) || defined(__linux) || defined(__linux__) 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #else 22 | #include 23 | #endif 24 | 25 | namespace clib 26 | { 27 | 28 | /** 29 | * \brief Serial port class. 30 | */ 31 | class SerialPort { 32 | public: 33 | 34 | /** 35 | * @brief Class constructor. 36 | */ 37 | SerialPort(); 38 | 39 | /** 40 | * @brief Class destructor. 41 | */ 42 | ~SerialPort(); 43 | 44 | /** 45 | * @brief Method to open serial port. 46 | * @param comport_file Serial port name string. Format depends from OS. 47 | * @param baudrate Boudrate. 48 | * @param timeout Wait data timeout. 49 | * @param mode Mode. Always 3 simbols: 1 - Number of bits (8, 7, 6, 5), 2 - parity (N, E, O), 3 - number of stop bits (1 or 2). Example: "8N1". 50 | * @return TRUE in case success or FALSE in case any errors. 51 | */ 52 | bool Open( 53 | const char *comport_file, 54 | unsigned int baudrate, 55 | unsigned int timeout = 100, 56 | const char *mode = "8N1"); 57 | 58 | /** 59 | * @brief Method to read data from serial port. 60 | * @param buf pointer to data buffer to copy. 61 | * @param size size of data buffer. 62 | * @return Number of readed bytes or returns -1. 63 | */ 64 | int ReadData(uint8_t *buf, uint32_t size); 65 | 66 | /** 67 | * @brief Method to send data. 68 | * @param buf pointer to data to send. 69 | * @param size size of data to send. 70 | * @return Number of bytes sended or return -1 in case any errors. 71 | */ 72 | int SendData(uint8_t *buf, uint32_t size); 73 | 74 | /** 75 | * \brief Method to close serial port. 76 | */ 77 | void Close(); 78 | /** 79 | * \brief Method to check if serial port open. 80 | */ 81 | bool isOpen(); 82 | 83 | private: 84 | 85 | ///< Port initialization flag. 86 | bool initFlag; 87 | ///< Port handle. 88 | #if defined(linux) || defined(__linux) || defined(__linux__)|| defined(__FreeBSD__) 89 | int Cport; 90 | #elif defined(_WIN32) || defined(__WIN32__) || defined(WIN32) 91 | HANDLE Cport; 92 | #endif 93 | 94 | }; 95 | 96 | } -------------------------------------------------------------------------------- /PelcoDProtocolParserTests/PelcoDDemoApplication/TCPPort.cpp: -------------------------------------------------------------------------------- 1 | #define _WINSOCK_DEPRECATED_NO_WARNINGS 2 | #include "TCPPort.h" 3 | 4 | #define BACKLOG 5 //Maximum number of connection waiting to be accepted 5 | #define TIMEOUT_SEC 30 //Timeout parameter for select() - in seconds 6 | #define TIMEOUT_USEC 0 //Timeout parameter for select() - in micro seconds 7 | #define MAX_BUFFER_SIZE 1024//Incoming data buffer size (maximum bytes per incoming data) 8 | #define MAX_QUEUE_SIZE 100 //Incoming data buffer size (maximum bytes per incoming data) 9 | 10 | 11 | #if defined(linux) || defined(__linux) || defined(__linux__) 12 | 13 | #else 14 | #define NS_INADDRSZ 4 15 | #define NS_IN6ADDRSZ 16 16 | #define NS_INT16SZ 2 17 | 18 | static char *inet_ntop4(const u_char *src, char *dst, socklen_t size); 19 | static char *inet_ntop6(const u_char *src, char *dst, socklen_t size); 20 | 21 | /* char * 22 | * inet_ntop(af, src, dst, size) 23 | * convert a network format address to presentation format. 24 | * return: 25 | * pointer to presentation format address (`dst'), or NULL (see errno). 26 | * author: 27 | * Paul Vixie, 1996. 28 | */ 29 | char * 30 | inet_ntop(int af, const void *src, char *dst, socklen_t size) 31 | { 32 | switch (af) { 33 | case AF_INET: 34 | return (inet_ntop4( (unsigned char*)src, (char*)dst, size)); // **** 35 | case AF_INET6: 36 | return (char*)(inet_ntop6( (unsigned char*)src, (char*)dst, size)); // **** 37 | default: 38 | // return (NULL); // **** 39 | return 0 ; // **** 40 | } 41 | /* NOTREACHED */ 42 | } 43 | 44 | /* const char * 45 | * inet_ntop4(src, dst, size) 46 | * format an IPv4 address 47 | * return: 48 | * `dst' (as a const) 49 | * notes: 50 | * (1) uses no statics 51 | * (2) takes a u_char* not an in_addr as input 52 | * author: 53 | * Paul Vixie, 1996. 54 | */ 55 | static char * 56 | inet_ntop4(const u_char *src, char *dst, socklen_t size) 57 | { 58 | static const char fmt[128] = "%u.%u.%u.%u"; 59 | char tmp[sizeof "255.255.255.255"]; 60 | int l; 61 | 62 | // l = snprintf(tmp, sizeof(tmp), fmt, src[0], src[1], src[2], src[3]); // **** 63 | l = std::sprintf( tmp, fmt, src[0], src[1], src[2], src[3] ); // **** vc++ does not have snprintf 64 | if (l <= 0 || (socklen_t) l >= size) { 65 | return (NULL); 66 | } 67 | memcpy(dst, tmp, size); 68 | return (dst); 69 | } 70 | 71 | /* const char * 72 | * inet_ntop6(src, dst, size) 73 | * convert IPv6 binary address into presentation (printable) format 74 | * author: 75 | * Paul Vixie, 1996. 76 | */ 77 | static char * 78 | inet_ntop6(const u_char *src, char *dst, socklen_t size) 79 | { 80 | /* 81 | * Note that int32_t and int16_t need only be "at least" large enough 82 | * to contain a value of the specified size. On some systems, like 83 | * Crays, there is no such thing as an integer variable with 16 bits. 84 | * Keep this in mind if you think this function should have been coded 85 | * to use pointer overlays. All the world's not a VAX. 86 | */ 87 | char tmp[sizeof "ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255"], *tp; 88 | struct { int base, len; } best, cur; 89 | #define NS_IN6ADDRSZ 16 90 | #define NS_INT16SZ 2 91 | u_int words[NS_IN6ADDRSZ / NS_INT16SZ]; 92 | int i; 93 | 94 | /* 95 | * Preprocess: 96 | * Copy the input (bytewise) array into a wordwise array. 97 | * Find the longest run of 0x00's in src[] for :: shorthanding. 98 | */ 99 | memset(words, '\0', sizeof words); 100 | for (i = 0; i < NS_IN6ADDRSZ; i++) 101 | words[i / 2] |= (src[i] << ((1 - (i % 2)) << 3)); 102 | best.base = -1; 103 | best.len = 0; 104 | cur.base = -1; 105 | cur.len = 0; 106 | for (i = 0; i < (NS_IN6ADDRSZ / NS_INT16SZ); i++) { 107 | if (words[i] == 0) { 108 | if (cur.base == -1) 109 | cur.base = i, cur.len = 1; 110 | else 111 | cur.len++; 112 | } else { 113 | if (cur.base != -1) { 114 | if (best.base == -1 || cur.len > best.len) 115 | best = cur; 116 | cur.base = -1; 117 | } 118 | } 119 | } 120 | if (cur.base != -1) { 121 | if (best.base == -1 || cur.len > best.len) 122 | best = cur; 123 | } 124 | if (best.base != -1 && best.len < 2) 125 | best.base = -1; 126 | 127 | /* 128 | * Format the result. 129 | */ 130 | tp = tmp; 131 | for (i = 0; i < (NS_IN6ADDRSZ / NS_INT16SZ); i++) { 132 | /* Are we inside the best run of 0x00's? */ 133 | if (best.base != -1 && i >= best.base && 134 | i < (best.base + best.len)) { 135 | if (i == best.base) 136 | *tp++ = ':'; 137 | continue; 138 | } 139 | /* Are we following an initial run of 0x00s or any real hex? */ 140 | if (i != 0) 141 | *tp++ = ':'; 142 | /* Is this address an encapsulated IPv4? */ 143 | if (i == 6 && best.base == 0 && (best.len == 6 || 144 | (best.len == 7 && words[7] != 0x0001) || 145 | (best.len == 5 && words[5] == 0xffff))) { 146 | if (!inet_ntop4(src+12, tp, sizeof tmp - (tp - tmp))) 147 | return (NULL); 148 | tp += strlen(tp); 149 | break; 150 | } 151 | tp += std::sprintf(tp, "%x", words[i]); // **** 152 | } 153 | /* Was it a trailing run of 0x00's? */ 154 | if (best.base != -1 && (best.base + best.len) == 155 | (NS_IN6ADDRSZ / NS_INT16SZ)) 156 | *tp++ = ':'; 157 | *tp++ = '\0'; 158 | 159 | /* 160 | * Check for overflow, copy, and we're done. 161 | */ 162 | if ((socklen_t)(tp - tmp) > size) { 163 | return (NULL); 164 | } 165 | strcpy(dst, tmp); 166 | return (dst); 167 | } 168 | #endif 169 | 170 | clib::TCPPort::TCPPort() 171 | { 172 | // Init variabes by default 173 | _data = new std::queue(); 174 | _is_server = false; 175 | _timeout = 100; 176 | 177 | _port = 0; 178 | 179 | memset(&_addr, 0, sizeof(sockaddr_in)); 180 | _addr.sin_addr.s_addr = inet_addr("127.0.0.1"); 181 | } 182 | 183 | 184 | clib::TCPPort::~TCPPort() 185 | { 186 | // Close socket 187 | Close(); 188 | } 189 | 190 | 191 | bool clib::TCPPort::Open(uint16_t portNumber, uint16_t timeout, bool serverSocket) 192 | { 193 | // Init params in Windows OS. 194 | #if defined(linux) || defined(__linux) || defined(__linux__) 195 | #else 196 | WSADATA wsaData = { 0 }; 197 | int iResult = WSAStartup(MAKEWORD(2, 2), &wsaData); 198 | if (iResult != 0) 199 | return false; 200 | #endif 201 | 202 | _port = portNumber; 203 | _running = true; 204 | _is_server = serverSocket; 205 | _timeout = timeout; 206 | return start(); 207 | } 208 | 209 | 210 | 211 | void clib::TCPPort::SetIPAddr(const char* ip_addr) 212 | { 213 | _addr.sin_addr.s_addr = inet_addr(ip_addr); 214 | } 215 | 216 | 217 | void clib::TCPPort::SetIPAddr(sockaddr_in & addr) 218 | { 219 | _addr.sin_addr.s_addr = addr.sin_addr.s_addr; 220 | } 221 | 222 | 223 | int clib::TCPPort::ReadData(uint8_t *buf, uint32_t size, int& conn_id) 224 | { 225 | if (_is_server) 226 | { 227 | if (!_data->empty()){ 228 | incom_data packet = _data->front(); 229 | conn_id = packet.cliend_id; 230 | _data->pop(); 231 | memcpy(buf, packet.data, packet.data_size); 232 | delete[] packet.data; 233 | return packet.data_size; 234 | } 235 | else{ 236 | return -1; 237 | } 238 | }else 239 | { 240 | return socket_wrapper.receive_data(size, (char*)buf, conn_id); 241 | } 242 | } 243 | 244 | 245 | int clib::TCPPort::SendData(uint8_t * buf, uint32_t size, int conn_id) 246 | { 247 | if (_is_server) 248 | { 249 | return socket_wrapper.respond_data((char*)buf,size, conn_id); 250 | }else 251 | { 252 | return socket_wrapper.send_data(size, (char*)buf, conn_id); 253 | } 254 | return -1; 255 | } 256 | 257 | 258 | void clib::TCPPort::Close() 259 | { 260 | _running = false; 261 | if (_thread.joinable()) 262 | _thread.join(); 263 | } 264 | 265 | bool clib::TCPPort::start() 266 | { 267 | if (_is_server) 268 | { 269 | _running = true; 270 | _thread = std::thread(&TCPPort::run, this); 271 | return true; 272 | }else 273 | { 274 | return socket_wrapper.create_socket(_port, _addr, _timeout); 275 | } 276 | } 277 | 278 | void clib::TCPPort::stop() 279 | { 280 | _running = false; 281 | if (_thread.joinable()) 282 | _thread.join(); 283 | } 284 | 285 | void clib::TCPPort::run() 286 | { 287 | int listener_fd; //Listener socket 288 | int max_fd; //Hold the maximum file descriptor number from the active sockets 289 | 290 | //Wraps the sockets detailed implementation. See SocketWrapper for details. 291 | // 1. 292 | listener_fd = socket_wrapper.socket_bind(_port, _addr); 293 | 294 | // 2. 295 | socket_wrapper.start_listen(listener_fd, BACKLOG); 296 | std::cout << "Listening on port " << _port << "..." << std::endl; 297 | 298 | // 3. 299 | socket_wrapper.set_nonblock(listener_fd); 300 | 301 | // 4. 302 | socket_wrapper.create_sets(listener_fd); 303 | max_fd = listener_fd; 304 | 305 | // 5. 306 | while(_running){ 307 | 308 | //5.1 309 | socket_wrapper.start_select(max_fd, TIMEOUT_SEC, TIMEOUT_USEC); 310 | for (int i=0; i < max_fd+1; i++){ 311 | 312 | // 5.2 313 | if (i == listener_fd){ 314 | max_fd = socket_wrapper.check_new_connection(listener_fd, max_fd); 315 | } 316 | 317 | //5.3 318 | else { 319 | incom_data packet; 320 | packet.data = new char[MAX_BUFFER_SIZE]; 321 | int bytes = socket_wrapper.receive_data(MAX_BUFFER_SIZE, packet.data, i); 322 | if (bytes > 0) { 323 | packet.data_size = bytes; 324 | packet.cliend_id = i; 325 | //Add data to the internal data queue 326 | _data->push(packet); 327 | if (_data->size() > MAX_QUEUE_SIZE) 328 | { 329 | incom_data old =_data->front(); 330 | _data->pop(); 331 | delete[] old.data; 332 | } 333 | }else 334 | { 335 | delete[] packet.data; 336 | } 337 | } 338 | } 339 | } 340 | 341 | // 6. 342 | socket_wrapper.close_all(max_fd); 343 | } 344 | 345 | 346 | bool clib::TCPPort::isOpen() 347 | { 348 | return _running; 349 | } 350 | 351 | clib::TCPPort::SocketWrapper::SocketWrapper() 352 | { 353 | //Clearing master and read sets 354 | FD_ZERO(&master_fds); 355 | FD_ZERO(&read_fds); 356 | } 357 | 358 | clib::TCPPort::SocketWrapper::~SocketWrapper() 359 | { 360 | 361 | } 362 | 363 | int clib::TCPPort::SocketWrapper::create_socket(int port, sockaddr_in addr, uint16_t _timeout) 364 | { 365 | if ((client_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) 366 | { 367 | printf("\n Socket creation error \n"); 368 | return false; 369 | } 370 | 371 | int retVal = 0; 372 | #if defined(linux) || defined(__linux) || defined(__linux__) 373 | // Init timeouts 374 | timeval timeparams; 375 | timeparams.tv_sec = _timeout / 1000; 376 | timeparams.tv_usec = _timeout * 1000; // Timeout in microseconds for read data from socket. 377 | if (timeout != 0) { 378 | retVal = setsockopt(client_fd, SOL_SOCKET, SO_RCVTIMEO, (const char*)&timeparams, sizeof(timeval)); 379 | // Close socket in case error 380 | if (retVal < 0) { 381 | close(sock); 382 | #else 383 | // Init timeouts 384 | DWORD timeout = _timeout; // Timeout in milliseconds for read data from socket. 385 | if (timeout != 0) { 386 | retVal = setsockopt(client_fd, SOL_SOCKET, SO_RCVTIMEO, (const char*)&timeout, sizeof(timeout)); 387 | // Close socket in case error 388 | if (retVal < 0) { 389 | closesocket(client_fd); 390 | WSACleanup(); 391 | #endif 392 | return false; 393 | } 394 | } 395 | 396 | struct sockaddr_in serv_addr = {0}; 397 | serv_addr.sin_family = AF_INET; 398 | serv_addr.sin_port = htons(port); 399 | serv_addr.sin_addr = addr.sin_addr; 400 | 401 | // Convert IPv4 and IPv6 addresses from text to binary form 402 | // if(inet_pton(AF_INET, "127.0.0.1", &serv_addr.sin_addr)<=0) 403 | // { 404 | // printf("\nInvalid address/ Address not supported \n"); 405 | // return -1; 406 | // } 407 | 408 | if (connect(client_fd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) 409 | { 410 | printf("\nConnection Failed \n"); 411 | return false; 412 | } 413 | 414 | return true; 415 | } 416 | 417 | int clib::TCPPort::SocketWrapper::socket_bind(int port, sockaddr_in addr) 418 | { 419 | int listener_fd; //Listener socket descriptor 420 | struct addrinfo hints; //Holds wanted settings for the listener socket 421 | struct addrinfo *server_info_list; //A list of possible information to create socket 422 | 423 | //All the other fields in the addrinfo struct (hints) must contain 0 424 | memset(&hints, 0, sizeof hints); 425 | 426 | //Initialize connection information 427 | hints.ai_family = AF_UNSPEC; //Supports IPv4 and IPv6 428 | hints.ai_socktype = SOCK_STREAM; //Reliable Stream (TCP) 429 | hints.ai_flags = AI_PASSIVE; //Assign local host address to socket 430 | 431 | //Get address information 432 | int err; 433 | char *ip = inet_ntoa(addr.sin_addr); 434 | err = getaddrinfo(ip, std::to_string(port).c_str(), &hints, &server_info_list); 435 | if (err != 0){ 436 | std::cerr << "getaddrinfo: " << gai_strerror(err) << std::endl; 437 | #if defined(linux) || defined(__linux) || defined(__linux__) 438 | close(listener_fd); 439 | #else 440 | closesocket(listener_fd); 441 | WSACleanup(); 442 | #endif 443 | return false; 444 | } 445 | 446 | //Go over list and try to create socket and bind 447 | addrinfo* p; 448 | for(p = server_info_list;p != NULL; p = p->ai_next) { 449 | 450 | //Create the socket - system call that returns the file descriptor of the socket 451 | listener_fd = socket(p->ai_family, p->ai_socktype,p->ai_protocol); 452 | if (listener_fd == -1) { 453 | continue; //try next 454 | } 455 | 456 | //Make sure the port is not in use. Allows reuse of local address (and port) 457 | int retVal; 458 | #if defined(linux) || defined(__linux) || defined(__linux__) 459 | int trueflag = 1; 460 | retVal = setsockopt(listener_fd, SOL_SOCKET, SO_REUSEADDR, &trueflag, sizeof trueflag); 461 | if (retVal < 0) 462 | { 463 | close(listener_fd); 464 | return false; 465 | } 466 | #else 467 | const char trueflag = 1; 468 | retVal = setsockopt(listener_fd, SOL_SOCKET, SO_REUSEADDR, &trueflag, sizeof(trueflag)); 469 | // Close socket in case error 470 | if (retVal < 0) { 471 | perror("setsockopt"); 472 | closesocket(listener_fd); 473 | WSACleanup(); 474 | return false; 475 | } 476 | #endif 477 | //Bind socket to specific port (p->ai_addr holds the address and port information) 478 | if (bind(listener_fd, p->ai_addr, p->ai_addrlen) == -1) { 479 | #if defined(linux) || defined(__linux) || defined(__linux__) 480 | close(listener_fd); 481 | #else 482 | closesocket(listener_fd); 483 | #endif 484 | continue; //try next 485 | } 486 | 487 | break; //success 488 | } 489 | 490 | //No one from the list succeeded - failed to bind 491 | if (p == NULL) { 492 | std::cerr << "failed to bind" << std::endl; 493 | #if defined(linux) || defined(__linux) || defined(__linux__) 494 | close(listener_fd); 495 | #else 496 | closesocket(listener_fd); 497 | WSACleanup(); 498 | #endif 499 | return false; 500 | } 501 | 502 | //If we got here we successfully created a socket to listen on 503 | //Free list as we no longer need it 504 | freeaddrinfo(server_info_list); 505 | 506 | //return the listener socket descriptor 507 | return listener_fd; 508 | } 509 | 510 | void clib::TCPPort::SocketWrapper::start_listen(int listener_fd, int backlog) 511 | { 512 | //Listen on the given port for incoming connections (Maximum BACKLOG waiting connections in queue) 513 | if (listen(listener_fd, backlog) == -1){ 514 | perror("listen"); 515 | #if defined(linux) || defined(__linux) || defined(__linux__) 516 | close(listener_fd); 517 | #else 518 | closesocket(listener_fd); 519 | WSACleanup(); 520 | #endif 521 | } 522 | } 523 | 524 | void clib::TCPPort::SocketWrapper::set_nonblock(int socket) 525 | { 526 | #if defined(linux) || defined(__linux) || defined(__linux__) 527 | int flags; 528 | // save current flags 529 | flags = fcntl(socket, F_GETFL, 0); 530 | if (flags == -1) 531 | perror("fcntl"); 532 | // set socket to be non-blocking 533 | if (fcntl(socket, F_SETFL, flags | O_NONBLOCK) == -1) 534 | perror("fcntl"); 535 | #else 536 | u_long mode = 1; // 1 to enable non-blocking socket 537 | ioctlsocket(socket, FIONBIO, &mode); 538 | #endif 539 | 540 | } 541 | 542 | void clib::TCPPort::SocketWrapper::create_sets(int listener_fd) 543 | { 544 | //clear master and read sets 545 | FD_ZERO(&master_fds); 546 | FD_ZERO(&read_fds); 547 | // add the listener socket descriptor to the master set 548 | #if defined(linux) || defined(__linux) || defined(__linux__) 549 | FD_SET(listener_fd, &master_fds); 550 | #else 551 | FD_SET((SOCKET)listener_fd, &master_fds); 552 | #endif 553 | 554 | } 555 | 556 | void clib::TCPPort::SocketWrapper::start_select(int max_fd, int timeout_sec, int timeout_usec) 557 | { 558 | //Set timeout values (for waiting on select()) 559 | struct timeval timeout = {timeout_sec, timeout_usec}; 560 | //Copy all available (open) sockets to the read set 561 | read_fds = master_fds; 562 | 563 | //Select - modifies read_fds set to show which sockets are ready for reading 564 | // if none are ready, it will timeout after the given timeout values 565 | int sel = select(max_fd+1, &read_fds, NULL, NULL, &timeout); 566 | if (sel == -1) { 567 | perror("select"); 568 | } 569 | } 570 | 571 | int clib::TCPPort::SocketWrapper::check_new_connection(int listener_fd, int max_fd) 572 | { 573 | //Check if listener socket is in read set (has changed and has an incoming connection to accept) 574 | if (FD_ISSET(listener_fd,&read_fds)) 575 | { 576 | struct sockaddr_storage their_addr; 577 | socklen_t addr_size = sizeof their_addr; 578 | 579 | //Accept the incoming connection, save the socket descriptor (client_fd) 580 | client_fd = accept(listener_fd, (struct sockaddr *)&their_addr, &addr_size); 581 | if (client_fd == -1){ 582 | perror("accept"); 583 | } 584 | else{ //If connection accepted 585 | //Set this socket to be non-blocking 586 | set_nonblock(client_fd); 587 | //Add socket to the master set 588 | #if defined(linux) || defined(__linux) || defined(__linux__) 589 | FD_SET(client_fd, &master_fds); 590 | #else 591 | FD_SET((SOCKET)client_fd, &master_fds); 592 | #endif 593 | //Update max_fd 594 | if (client_fd > max_fd) 595 | max_fd = client_fd; 596 | //Print incoming connection 597 | if (their_addr.ss_family == AF_INET){ 598 | //IPv4 599 | char ip_as_string[INET_ADDRSTRLEN]; 600 | inet_ntop(their_addr.ss_family,&((struct sockaddr_in *)&their_addr)->sin_addr,ip_as_string, INET_ADDRSTRLEN); 601 | std::cout << "New connection from " << ip_as_string << " on socket " << client_fd << std::endl; 602 | } else if(their_addr.ss_family == AF_INET6){ 603 | //IPv6 604 | char ip_as_string[INET6_ADDRSTRLEN]; 605 | inet_ntop(their_addr.ss_family,&((struct sockaddr_in6 *)&their_addr)->sin6_addr,ip_as_string, INET6_ADDRSTRLEN); 606 | std::cout << "New connection from " << ip_as_string << " on socket " << client_fd << std::endl; 607 | } 608 | } 609 | } 610 | return max_fd; 611 | } 612 | 613 | int clib::TCPPort::SocketWrapper::receive_data(int buffer_size, char* data, int& client) 614 | { 615 | if (client >= 0) 616 | { 617 | //Check if socket is in read set (has data or has closed the connection) 618 | if (FD_ISSET(client, &read_fds)) 619 | { 620 | int bytes; 621 | 622 | //Receive data 623 | bytes = recv(client, data, buffer_size, 0); 624 | 625 | //Connection has been closed by client 626 | if (bytes <= 0) 627 | { 628 | if (bytes == -1) 629 | perror("recv"); 630 | //close socket 631 | #if defined(linux) || defined(__linux) || defined(__linux__) 632 | close(client); 633 | #else 634 | closesocket(client); 635 | #endif 636 | //remove from master set 637 | #if defined(linux) || defined(__linux) || defined(__linux__) 638 | FD_CLR(client_fd, &master_fds); 639 | #else 640 | FD_CLR((SOCKET)client, &master_fds); 641 | #endif 642 | return 0; 643 | } 644 | //Some data received - bytes > 0 645 | return bytes; 646 | } 647 | }else 648 | { 649 | int bytes; 650 | bytes = recv(client_fd, data, buffer_size, 0); 651 | client = client_fd; 652 | return bytes; 653 | } 654 | return -1; 655 | } 656 | 657 | int clib::TCPPort::SocketWrapper::send_data(int data_size, char *data, int client) 658 | { 659 | if (client < 0) 660 | return send(client_fd , data , data_size , 0 ); 661 | else 662 | return send(client , data , data_size , 0 ); 663 | } 664 | 665 | int clib::TCPPort::SocketWrapper::respond_data(char* data, int data_size, int conn_id) 666 | { 667 | //Send "data" to given client 668 | int ret = send(conn_id, data, data_size, 0); 669 | if (ret == -1) { 670 | perror("send"); 671 | } 672 | return ret; 673 | } 674 | 675 | void clib::TCPPort::SocketWrapper::close_all(int max_fd) 676 | { 677 | //Close all socket descriptors, this will terminate all connections 678 | for (int i=0; i < max_fd+1; i++){ 679 | //If socket is in the master set it means it is still open - so close it 680 | if (FD_ISSET(i, &master_fds)) 681 | #if defined(linux) || defined(__linux) || defined(__linux__) 682 | close(i); 683 | #else 684 | closesocket(i); 685 | #endif 686 | } 687 | 688 | #if defined(linux) || defined(__linux) || defined(__linux__) 689 | #else 690 | WSACleanup(); 691 | #endif 692 | } 693 | 694 | -------------------------------------------------------------------------------- /PelcoDProtocolParserTests/PelcoDDemoApplication/TCPPort.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #if defined(linux) || defined(__linux) || defined(__linux__) 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include "arpa/inet.h" 19 | #else 20 | #define WIN32_LEAN_AND_MEAN 21 | #include 22 | #include 23 | #include 24 | #include 25 | #pragma comment(lib, "Ws2_32.lib") 26 | #endif 27 | 28 | 29 | namespace clib 30 | { 31 | 32 | /** 33 | * @brief TCP socket class. 34 | */ 35 | class TCPPort 36 | { 37 | public: 38 | 39 | /** 40 | * @brief Class constructor. 41 | */ 42 | TCPPort(); 43 | 44 | /** 45 | * @brief Class destructor. 46 | */ 47 | ~TCPPort(); 48 | 49 | /** 50 | * @brief Open - Method to open TCP socket. 51 | * @param portNumber TCP port number for TCP socket. 52 | * @param timeout Wait data timeout in milliseconds. 53 | * @param isServer If set to TRUE - the server socket type will be opened, 54 | * otherwise - will be opened the client socket type. 55 | * @return TRUE in case success or FALSE in case any errors. 56 | */ 57 | bool Open(uint16_t portNumber, uint16_t timeout, bool isServer); 58 | 59 | /** 60 | * @brief SetIPAddr - Method to set host IP address for server socet 61 | * type (default 127.0.0.1 Host IP) and destination IP address for 62 | * client socket type (default 255.255.255.255 destination IP). 63 | * @param dstIP IP address string. 64 | */ 65 | void SetIPAddr(const char *ip_addr); 66 | 67 | /** 68 | * @brief SetIPAddr - Method to set host IP address for server socet 69 | * type (default 127.0.0.1 Host IP) and destination IP address for 70 | * client socket type (default 255.255.255.255 destination IP). 71 | * @param dstAddr IP address structure. 72 | */ 73 | void SetIPAddr(sockaddr_in &addr); 74 | 75 | /** 76 | * @brief Method to read data. 77 | * @param buf pointer to data buffer to copy data. 78 | * @param size size of data buffer. 79 | * @param conn_id Сonnection number from which the data was received 80 | * @return Number of read bytes or return -1 in case error. 81 | */ 82 | int ReadData(uint8_t *buf, uint32_t size, int& conn_id); 83 | 84 | /** 85 | * @brief Method to send data. 86 | * @param buf pointer to data to send. 87 | * @param size size of data to send. 88 | * @param conn_id Сonnection number where data needs to be sent. Only 89 | * for server socket. For the client type, conn_id should be set to -1. 90 | * @return Number of bytes sent or return -1 if UDP socket not open. 91 | */ 92 | int SendData(uint8_t *buf, uint32_t size, int conn_id = -1); 93 | 94 | /** 95 | * @brief Method to check if UDP socket open. 96 | * @return TRUE if socke open or FALSE. 97 | */ 98 | bool isOpen(); 99 | 100 | /** 101 | * \brief Methos to close UDP socket. 102 | */ 103 | void Close(); 104 | 105 | private: 106 | class SocketWrapper { 107 | public: 108 | /** 109 | * Constructor. 110 | * Initializes relevant fd_sets 111 | */ 112 | SocketWrapper(); 113 | /** 114 | * Destructor. Nothing to destruct. 115 | */ 116 | virtual ~SocketWrapper(); 117 | /** 118 | * This method get local address information, 119 | * creates a listener sockets and binds it to the given port. 120 | * Returns the listener socket file descriptor. 121 | */ 122 | int create_socket(int, sockaddr_in, uint16_t); 123 | /** 124 | * This method get local address information, 125 | * creates a listener sockets and binds it to the given port. 126 | * Returns the listener socket file descriptor. 127 | */ 128 | int socket_bind(int, sockaddr_in); 129 | /** 130 | * This method receives listener socket and backlog number. 131 | * It sets the socket to start listening for incoming connections 132 | * with maximum backlog number of incoming connections waiting to be accepted. 133 | */ 134 | void start_listen(int,int); 135 | /** 136 | * This method receives a socket and sets it to be non-blocking. 137 | */ 138 | void set_nonblock(int); 139 | /** 140 | * This method initializes the relevant sets and adds the given socket to the master set. 141 | */ 142 | void create_sets(int); 143 | /** 144 | * This method receives the maximum socket descriptor number (max_fd) and timeout values. 145 | * It checks if sockets descriptors (up to max_fd) are ready for read / write / accept connections, 146 | * and sets the read_fds set accordingly. 147 | * If none are ready it will timeout after the given timeout values. 148 | */ 149 | void start_select(int,int,int); 150 | /** 151 | * This method a listener socket and the maximum socket descriptor number (max_fd). 152 | * It checks if the socket is in the read_fds set (meaning that it has a incoming connection waiting to be accepted). 153 | * If it is, it accepts the incoming connection and adding its socket descriptor to the master set. 154 | * Updates and returns the new max_fd (if changed). 155 | */ 156 | int check_new_connection(int,int); 157 | /** 158 | * This method receives a socket, buffer size, and a reference to data string. 159 | * It checks if the socket is in the read_fds set (meaning it has incoming data to receive or it has ended the connection). 160 | * If the socket has ended the connection, it closes the file descriptor and delete it from the master set. 161 | * If the socket has incoming data, it reads it (up to buffer_size) and assign it to the given string. 162 | * It returns the number of bytes received or -1 if the socket is not in the read_fds set. 163 | */ 164 | int receive_data(int buffer_size, char* data, int& conn_id); 165 | /** 166 | * This method receives a socket, buffer size, and a reference to data string. 167 | * It checks if the socket is in the read_fds set (meaning it has incoming data to receive or it has ended the connection). 168 | * If the socket has ended the connection, it closes the file descriptor and delete it from the master set. 169 | * If the socket has incoming data, it reads it (up to buffer_size) and assign it to the given string. 170 | * It returns the number of bytes received or -1 if the socket is not in the read_fds set. 171 | */ 172 | int send_data(int data_size, char* data, int conn_id = -1); 173 | 174 | /** 175 | * This method send "OK" to the given socket. 176 | */ 177 | int respond_data(char* data, int data_size, int conn_id); 178 | /** 179 | * This method closes all active sockets. 180 | * The active sockets are the one that are in the master_fd set. 181 | */ 182 | void close_all(int); 183 | private: 184 | int client_fd; 185 | fd_set read_fds; //Socket descriptor set that holds the sockets that are ready for read 186 | fd_set master_fds; //Socket descriptor set that hold all the available (open) sockets 187 | }; 188 | 189 | SocketWrapper socket_wrapper; 190 | 191 | ///< TCP port 192 | uint16_t _port; 193 | struct sockaddr_in _addr; 194 | bool _is_server; 195 | uint16_t _timeout; 196 | std::thread _thread; //Internal thread, this is in order to start and stop the thread from different class methods 197 | struct incom_data 198 | { 199 | char* data; 200 | uint16_t data_size; 201 | uint16_t cliend_id; 202 | }; 203 | 204 | std::queue* _data; //Queue for saving incoming data 205 | // Flag for starting and terminating the main loop 206 | bool _running; 207 | 208 | 209 | /** 210 | * Starts the internal thread that executes the main routine (run()). 211 | */ 212 | bool start(); 213 | /** 214 | * Stops the main routine and the internal thread. 215 | */ 216 | void stop(); 217 | /** 218 | * This is the main routine of this class. 219 | * It accepts incoming connection and receives incoming data from these connections. 220 | * It is private because it is only executed in a different thread by start() method. 221 | */ 222 | void run(); 223 | 224 | 225 | }; 226 | 227 | } 228 | 229 | -------------------------------------------------------------------------------- /PelcoDProtocolParserTests/PelcoDDemoApplication/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include "json.hpp" 11 | #include "TCPPort.h" 12 | #include "SerialPort.h" 13 | #include 14 | #include 15 | 16 | using json = nlohmann::json; // Necсessary for JSON library. 17 | 18 | 19 | /* 20 | * GLOBAL CONSTANTS. 21 | */ 22 | std::string configuration_file_name = "PelcoDDemoApplication.json"; // Config file name. 23 | 24 | 25 | /* 26 | * GLOBAL VARIABLES. 27 | */ 28 | cv::VideoCapture video_source; // Video source. 29 | clib::SerialPort serial_port; // Serial port. 30 | clib::TCPPort tcp_port; // TCP port. 31 | int frame_width = 0; // Video frame width. 32 | int frame_height = 0; // Video frame height. 33 | int mouse_x = 0; // Horizontal mouse position. 34 | int mouse_y = 0; // Vertical mouse position. 35 | int camera_address = 1; // Pelco-D camera address. 36 | int max_PTU_speed = 63; // Maximum PTU speed (max 63 according Pelco-D). 37 | bool button_pushed = false; // Mouse button pushed flag. 38 | pelco::PelcoDProtocolParser pelco_protocol_parser; // Pelco-D protocol parser. 39 | 40 | 41 | /* 42 | * FUNCTIONS PROTOTYPES. 43 | */ 44 | 45 | /// Read and init params function prototype. 46 | bool Load_And_Init_Params(); 47 | 48 | /// Mouse callback prototype function. 49 | void Mouse_Call_Back_Function(int event, int x, int y, int flags, void* userdata); 50 | 51 | /// Keyboard processing functions. 52 | void Keyboard_Processing_Function(int key); 53 | 54 | /// Draw info function. 55 | void Draw_Info_Function(cv::Mat frame); 56 | 57 | 58 | // Entry point. 59 | int main(void) 60 | { 61 | // Load and init params. 62 | if (!Load_And_Init_Params()) 63 | { 64 | std::cout << "ERROR: Parameters not loaded. Exit..." << std::endl; 65 | std::this_thread::sleep_for(std::chrono::seconds(1)); 66 | return -1; 67 | } 68 | 69 | // Init variables. 70 | cv::Mat frame = cv::Mat(cv::Size(frame_width, frame_height), CV_8UC3); 71 | 72 | // Init windows. 73 | cv::namedWindow("PELCO-D DEMO APPLICATION", cv::WINDOW_AUTOSIZE); 74 | cv::moveWindow("PELCO-D DEMO APPLICATION", 5, 5); 75 | cv::setMouseCallback("PELCO-D DEMO APPLICATION", Mouse_Call_Back_Function, nullptr); 76 | 77 | // Main loop. 78 | while (true) 79 | { 80 | // Capture video frame. 81 | video_source >> frame; 82 | if (frame.empty()) 83 | { 84 | video_source.set(cv::CAP_PROP_POS_FRAMES, 1); 85 | continue; 86 | } 87 | 88 | // Control PTU according to mouse position. 89 | if (button_pushed) 90 | { 91 | // Calculate speed. 92 | int delta_pan = mouse_x - (frame_width / 2); 93 | int delta_tilt = mouse_y - (frame_height / 2); 94 | 95 | uint8_t pan_speed = (uint8_t)(((float)abs(delta_pan) / (float)(frame_width / 2)) * (float)max_PTU_speed); 96 | uint8_t tilt_speed = (uint8_t)(((float)abs(delta_tilt) / (float)(frame_height / 2)) * (float)max_PTU_speed); 97 | 98 | if (delta_pan < 0) 99 | { 100 | if (delta_tilt < 0) 101 | { 102 | uint8_t packet[7]; 103 | if (pelco_protocol_parser.GetCommand(packet, camera_address, pelco::PelcoDCommands::UP_LEFT, pan_speed, tilt_speed)) 104 | { 105 | tcp_port.SendData(packet, 7); 106 | serial_port.SendData(packet, 7); 107 | } 108 | } 109 | else if (delta_tilt > 0) 110 | { 111 | uint8_t packet[7]; 112 | if (pelco_protocol_parser.GetCommand(packet, camera_address, pelco::PelcoDCommands::DOWN_LEFT, pan_speed, tilt_speed)) 113 | { 114 | tcp_port.SendData(packet, 7); 115 | serial_port.SendData(packet, 7); 116 | } 117 | } 118 | else // delta_tilt == 0 119 | { 120 | uint8_t packet[7]; 121 | if (pelco_protocol_parser.GetCommand(packet, camera_address, pelco::PelcoDCommands::LEFT, pan_speed, 0)) 122 | { 123 | tcp_port.SendData(packet, 7); 124 | serial_port.SendData(packet, 7); 125 | } 126 | } 127 | } 128 | else if (delta_pan > 0) 129 | { 130 | if (delta_tilt < 0) 131 | { 132 | uint8_t packet[7]; 133 | if (pelco_protocol_parser.GetCommand(packet, camera_address, pelco::PelcoDCommands::UP_RIGHT, pan_speed, tilt_speed)) 134 | { 135 | tcp_port.SendData(packet, 7); 136 | serial_port.SendData(packet, 7); 137 | } 138 | } 139 | else if (delta_tilt > 0) 140 | { 141 | uint8_t packet[7]; 142 | if (pelco_protocol_parser.GetCommand(packet, camera_address, pelco::PelcoDCommands::DOWN_RIGHT, pan_speed, tilt_speed)) 143 | { 144 | tcp_port.SendData(packet, 7); 145 | serial_port.SendData(packet, 7); 146 | } 147 | } 148 | else // delta_tilt == 0 149 | { 150 | uint8_t packet[7]; 151 | if (pelco_protocol_parser.GetCommand(packet, camera_address, pelco::PelcoDCommands::RIGHT, pan_speed, 0)) 152 | { 153 | tcp_port.SendData(packet, 7); 154 | serial_port.SendData(packet, 7); 155 | } 156 | } 157 | } 158 | else // delta_pan == 0 159 | { 160 | if (delta_tilt < 0) 161 | { 162 | uint8_t packet[7]; 163 | if (pelco_protocol_parser.GetCommand(packet, camera_address, pelco::PelcoDCommands::UP, pan_speed, tilt_speed)) 164 | { 165 | tcp_port.SendData(packet, 7); 166 | serial_port.SendData(packet, 7); 167 | } 168 | } 169 | else if (delta_tilt > 0) 170 | { 171 | uint8_t packet[7]; 172 | if (pelco_protocol_parser.GetCommand(packet, camera_address, pelco::PelcoDCommands::DOWN, pan_speed, tilt_speed)) 173 | { 174 | tcp_port.SendData(packet, 7); 175 | serial_port.SendData(packet, 7); 176 | } 177 | } 178 | else // delta_tilt == 0 179 | { 180 | uint8_t packet[7]; 181 | if (pelco_protocol_parser.GetCommand(packet, camera_address, pelco::PelcoDCommands::STOP, 0, 0)) 182 | { 183 | tcp_port.SendData(packet, 7); 184 | serial_port.SendData(packet, 7); 185 | } 186 | } 187 | } 188 | } 189 | 190 | // Draw info on video. 191 | Draw_Info_Function(frame); 192 | 193 | // Show video. 194 | cv::imshow("PELCO-D DEMO APPLICATION", frame); 195 | 196 | // Keyboard processing function. 197 | Keyboard_Processing_Function(cv::waitKey(1)); 198 | 199 | } 200 | 201 | return 1; 202 | } 203 | 204 | 205 | bool Load_And_Init_Params() 206 | { 207 | // Init variables. 208 | json json_conf; 209 | 210 | // Open JSON file. 211 | std::ifstream configuration_file(configuration_file_name); 212 | if (!configuration_file.is_open()) 213 | { 214 | return false; 215 | } 216 | 217 | // Parse JSON file. 218 | try { 219 | json_conf = json::parse(configuration_file); 220 | } 221 | catch (json::parse_error& e) 222 | { 223 | // Close file. 224 | configuration_file.close(); 225 | return false; 226 | } 227 | 228 | // Close file. 229 | configuration_file.close(); 230 | 231 | // Read params. 232 | try 233 | { 234 | // Read video source params. 235 | json video_source_params = json_conf.at("Video_Source_Params"); 236 | std::string video_source_init_string = video_source_params.at("video_source_init_string").get(); 237 | 238 | // Init video source. 239 | if (video_source_init_string.length() < 4) 240 | { 241 | if (!video_source.open(std::stoi(video_source_init_string))) 242 | return false; 243 | } 244 | else 245 | { 246 | if (!video_source.open(video_source_init_string)) 247 | return false; 248 | } 249 | 250 | // Get video frame size. 251 | frame_width = (int)video_source.get(cv::CAP_PROP_FRAME_WIDTH); 252 | frame_height = (int)video_source.get(cv::CAP_PROP_FRAME_HEIGHT); 253 | 254 | // Read communication params. 255 | json communication_params = json_conf.at("Communication_Params"); 256 | std::string camera_IP = communication_params.at("camera_IP").get(); 257 | int TCP_port = communication_params.at("TCP_port").get(); 258 | std::string serial_port_name = communication_params.at("serial_port_name").get(); 259 | int serial_port_baudrate = communication_params.at("serial_port_baudrate").get(); 260 | camera_address = communication_params.at("camera_address").get(); 261 | max_PTU_speed = communication_params.at("max_PTU_speed").get(); 262 | 263 | // Init TCP port. 264 | tcp_port.SetIPAddr(camera_IP.c_str()); 265 | if (!tcp_port.Open(TCP_port, 100, false)) 266 | { 267 | std::cout << "PTU Pelco-D TCP port not open" << std::endl; 268 | } 269 | 270 | // Init serial port. 271 | if (!serial_port.Open(serial_port_name.c_str(), serial_port_baudrate, 100)) 272 | { 273 | std::cout << "PTU Pelco-D serial port port not open" << std::endl; 274 | } 275 | } 276 | catch(const std::exception& e) 277 | { 278 | std::cerr << e.what() << std::endl; 279 | return false; 280 | } 281 | 282 | return true; 283 | } 284 | 285 | 286 | 287 | void Mouse_Call_Back_Function(int event, int x, int y, int flags, void* userdata) 288 | { 289 | flags = 0; 290 | userdata = nullptr; 291 | 292 | // Update mouse position. 293 | mouse_x = x; 294 | mouse_y = y; 295 | 296 | // Check ivent. 297 | switch (event) 298 | { 299 | case cv::EVENT_LBUTTONDOWN: // Capture/Reset function 300 | button_pushed = true; 301 | break; 302 | case cv::EVENT_LBUTTONUP: // Capture/Reset function 303 | button_pushed = false; 304 | // Create stop command. 305 | { 306 | uint8_t packet[7]; 307 | if (pelco_protocol_parser.GetCommand(packet, camera_address, pelco::PelcoDCommands::STOP)) 308 | { 309 | tcp_port.SendData(packet, 7); 310 | } 311 | } 312 | break; 313 | case cv::EVENT_RBUTTONDOWN: break; 314 | case cv::EVENT_RBUTTONUP: break; 315 | case cv::EVENT_MBUTTONDOWN: break; 316 | case cv::EVENT_MOUSEMOVE: break; 317 | } 318 | } 319 | 320 | 321 | 322 | void Keyboard_Processing_Function(int key) 323 | { 324 | // Init variables. 325 | uint8_t packet[7]; 326 | 327 | switch (key) 328 | { 329 | // Exit. 330 | case 27: // "ESC" 331 | // Stop video recording. 332 | cv::destroyAllWindows(); 333 | exit(0); 334 | 335 | // STOP camera and PTU. 336 | case 32: // "SPACE" 337 | if (pelco_protocol_parser.GetCommand(packet, camera_address, pelco::PelcoDCommands::STOP)) 338 | { 339 | tcp_port.SendData(packet, 7); 340 | serial_port.SendData(packet, 7); 341 | } 342 | break; 343 | 344 | // Camera ON. 345 | case 49: // "1" 346 | if (pelco_protocol_parser.GetCommand(packet, camera_address, pelco::PelcoDCommands::CAMERA_ON)) 347 | { 348 | tcp_port.SendData(packet, 7); 349 | serial_port.SendData(packet, 7); 350 | } 351 | break; 352 | 353 | // Camera OFF. 354 | case 50: // "2" 355 | if (pelco_protocol_parser.GetCommand(packet, camera_address, pelco::PelcoDCommands::CAMERA_OFF)) 356 | { 357 | tcp_port.SendData(packet, 7); 358 | serial_port.SendData(packet, 7); 359 | } 360 | break; 361 | 362 | // Focus NEAR. 363 | case 51: // "3" 364 | if (pelco_protocol_parser.GetCommand(packet, camera_address, pelco::PelcoDCommands::FOCUS_NEAR)) 365 | { 366 | tcp_port.SendData(packet, 7); 367 | serial_port.SendData(packet, 7); 368 | } 369 | break; 370 | 371 | // Focus FAR. 372 | case 52: // "4" 373 | if (pelco_protocol_parser.GetCommand(packet, camera_address, pelco::PelcoDCommands::FOCUS_FAR)) 374 | { 375 | tcp_port.SendData(packet, 7); 376 | serial_port.SendData(packet, 7); 377 | } 378 | break; 379 | 380 | // Zoom WIDE. 381 | case 53: // "5" 382 | if (pelco_protocol_parser.GetCommand(packet, camera_address, pelco::PelcoDCommands::ZOOM_WIDE)) 383 | { 384 | tcp_port.SendData(packet, 7); 385 | serial_port.SendData(packet, 7); 386 | } 387 | break; 388 | 389 | // Zoom tele. 390 | case 54: // "6" 391 | if (pelco_protocol_parser.GetCommand(packet, camera_address, pelco::PelcoDCommands::ZOOM_TELE)) 392 | { 393 | tcp_port.SendData(packet, 7); 394 | serial_port.SendData(packet, 7); 395 | } 396 | break; 397 | 398 | // Move RIGHT. 399 | case 100: // "D" 400 | if (pelco_protocol_parser.GetCommand(packet, camera_address, pelco::PelcoDCommands::RIGHT, 0x10, 0)) 401 | { 402 | tcp_port.SendData(packet, 7); 403 | serial_port.SendData(packet, 7); 404 | } 405 | break; 406 | 407 | // Move LEFT. 408 | case 97: // "A" 409 | if (pelco_protocol_parser.GetCommand(packet, camera_address, pelco::PelcoDCommands::LEFT, 0x10, 0)) 410 | { 411 | tcp_port.SendData(packet, 7); 412 | serial_port.SendData(packet, 7); 413 | } 414 | break; 415 | 416 | // Move UP. 417 | case 119: // "W" 418 | if (pelco_protocol_parser.GetCommand(packet, camera_address, pelco::PelcoDCommands::UP, 0, 0x10)) 419 | { 420 | tcp_port.SendData(packet, 7); 421 | serial_port.SendData(packet, 7); 422 | } 423 | break; 424 | 425 | // Move DOWN. 426 | case 120: // "X" 427 | if (pelco_protocol_parser.GetCommand(packet, camera_address, pelco::PelcoDCommands::DOWN, 0, 0x10)) 428 | { 429 | tcp_port.SendData(packet, 7); 430 | serial_port.SendData(packet, 7); 431 | } 432 | break; 433 | 434 | // Move UP-LEFT. 435 | case 113: // "Q" 436 | if (pelco_protocol_parser.GetCommand(packet, camera_address, pelco::PelcoDCommands::UP_LEFT, 0x10, 0x10)) 437 | { 438 | tcp_port.SendData(packet, 7); 439 | serial_port.SendData(packet, 7); 440 | } 441 | break; 442 | 443 | // Mode UP-RIGHT. 444 | case 101: // "E" 445 | if (pelco_protocol_parser.GetCommand(packet, camera_address, pelco::PelcoDCommands::UP_RIGHT, 0x10, 0x10)) 446 | { 447 | tcp_port.SendData(packet, 7); 448 | serial_port.SendData(packet, 7); 449 | } 450 | break; 451 | 452 | // Move DOWN-LEFT. 453 | case 122: // "Z" 454 | if (pelco_protocol_parser.GetCommand(packet, camera_address, pelco::PelcoDCommands::DOWN_LEFT, 0x10, 0x10)) 455 | { 456 | tcp_port.SendData(packet, 7); 457 | serial_port.SendData(packet, 7); 458 | } 459 | break; 460 | 461 | // Mode DOWN-RIGHT. 462 | case 99: // "C" 463 | if (pelco_protocol_parser.GetCommand(packet, camera_address, pelco::PelcoDCommands::DOWN_RIGHT, 0x10, 0x10)) 464 | { 465 | tcp_port.SendData(packet, 7); 466 | serial_port.SendData(packet, 7); 467 | } 468 | break; 469 | } 470 | } 471 | 472 | 473 | 474 | void Draw_Info_Function(cv::Mat frame) 475 | { 476 | // Draw central lines. 477 | cv::line(frame, cv::Point(0, frame_height / 2), cv::Point(frame_width, frame_height / 2), cv::Scalar(0, 0, 0)); 478 | cv::line(frame, cv::Point(frame_width / 2, 0), cv::Point(frame_width / 2, frame_height), cv::Scalar(0, 0, 0)); 479 | 480 | // Draw mouse line. 481 | cv::line(frame, cv::Point(frame_width / 2, frame_height / 2), cv::Point(mouse_x, mouse_y), cv::Scalar(255, 255, 0)); 482 | 483 | // Draw buttons info. 484 | cv::putText(frame, "BUTTONS:", cv::Point(5, 15), cv::FONT_HERSHEY_SIMPLEX, 0.5, cv::Scalar(0, 0, 255)); 485 | cv::putText(frame, "SPACE - Stop PTU, Zoom, Focus", cv::Point(5, 40), cv::FONT_HERSHEY_SIMPLEX, 0.5, cv::Scalar(0, 0, 255)); 486 | cv::putText(frame, "1 - Camera ON", cv::Point(5, 65), cv::FONT_HERSHEY_SIMPLEX, 0.5, cv::Scalar(0, 0, 255)); 487 | cv::putText(frame, "2 - Camera OFF", cv::Point(5, 90), cv::FONT_HERSHEY_SIMPLEX, 0.5, cv::Scalar(0, 0, 255)); 488 | cv::putText(frame, "3 - Focus NEAR", cv::Point(5, 115), cv::FONT_HERSHEY_SIMPLEX, 0.5, cv::Scalar(0, 0, 255)); 489 | cv::putText(frame, "4 - Focus FAR", cv::Point(5, 140), cv::FONT_HERSHEY_SIMPLEX, 0.5, cv::Scalar(0, 0, 255)); 490 | cv::putText(frame, "5 - Zoom WIDE", cv::Point(5, 165), cv::FONT_HERSHEY_SIMPLEX, 0.5, cv::Scalar(0, 0, 255)); 491 | cv::putText(frame, "6 - Zoom TELE", cv::Point(5, 190), cv::FONT_HERSHEY_SIMPLEX, 0.5, cv::Scalar(0, 0, 255)); 492 | cv::putText(frame, "D - Move RIGHT (fixed speed)", cv::Point(5, 215), cv::FONT_HERSHEY_SIMPLEX, 0.5, cv::Scalar(0, 0, 255)); 493 | cv::putText(frame, "A - Move LEFT (fixed speed)", cv::Point(5, 240), cv::FONT_HERSHEY_SIMPLEX, 0.5, cv::Scalar(0, 0, 255)); 494 | cv::putText(frame, "W - Move UP (fixed speed)", cv::Point(5, 265), cv::FONT_HERSHEY_SIMPLEX, 0.5, cv::Scalar(0, 0, 255)); 495 | cv::putText(frame, "X - Move DOWN (fixed speed)", cv::Point(5, 290), cv::FONT_HERSHEY_SIMPLEX, 0.5, cv::Scalar(0, 0, 255)); 496 | cv::putText(frame, "Q - Move UP-LEFT (fixed speed)", cv::Point(5, 315), cv::FONT_HERSHEY_SIMPLEX, 0.5, cv::Scalar(0, 0, 255)); 497 | cv::putText(frame, "E - Move UP-RIGHT (fixed speed)", cv::Point(5, 340), cv::FONT_HERSHEY_SIMPLEX, 0.5, cv::Scalar(0, 0, 255)); 498 | cv::putText(frame, "Z - Move DOWN-LEFT (fixed speed)", cv::Point(5, 365), cv::FONT_HERSHEY_SIMPLEX, 0.5, cv::Scalar(0, 0, 255)); 499 | cv::putText(frame, "C - Move DOWN-RIGHT (fixed speed)", cv::Point(5, 390), cv::FONT_HERSHEY_SIMPLEX, 0.5, cv::Scalar(0, 0, 255)); 500 | } 501 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![PELCO_D](_static/pelco-d_logo_with_borders.png) 2 | *** 3 | ## OVERVIEW 4 | 5 | The **Pelco-D Protocol Parser library** is designed to help developers control cameras and pan-tilt platforms using the Pelco-D protocol. The library has a simple interface and only does message encoding. The library includes a single class **PelcoDProtocolParser**. The interface of the programming class is shown below: 6 | ```c++ 7 | /** 8 | * @brief Pelco-D protocol parser class. 9 | */ 10 | class PelcoDProtocolParser 11 | { 12 | public: 13 | /** 14 | * @brief Class constructor. 15 | */ 16 | PelcoDProtocolParser(); 17 | /** 18 | * @brief Class destructor. 19 | */ 20 | ~PelcoDProtocolParser(); 21 | /** 22 | * @brief Method to encode Pelco-D command (Always 7 bytes). 23 | * 24 | * @param packet Pointer to packet buffer. Always 7 bytes. 25 | * @param address Camera address (usually 1). 26 | * @param command_ID ID of command. 27 | * @param data_1 First byte of command data (according to Pelco-D specification). 28 | * @param data_2 Second byte of command data (according to Pelco-D specification). 29 | * @return true If the command has been encoded. 30 | * @return false In case any errors. 31 | */ 32 | bool GetCommand( 33 | uint8_t* packet, 34 | uint8_t address, 35 | pelco::PelcoDCommands command_ID, 36 | uint8_t data_1 = 0, 37 | uint8_t data_2 = 0); 38 | }; 39 | ``` 40 | The command IDs are described in the **PelcoDCommands** structure in the **PelcoDProtocolParser.h** file. The field names of the structure correspond to the Pelco-D protocol specification. The The declaration of the structure is as follows. 41 | ```c++ 42 | enum class PelcoDCommands 43 | { 44 | // Main commands. 45 | CAMERA_ON, 46 | CAMERA_OFF, 47 | IRIS_CLOSE, 48 | IRIS_OPEN, 49 | FOCUS_NEAR, 50 | FOCUS_FAR, 51 | FOCUS_STOP, 52 | ZOOM_WIDE, 53 | ZOOM_TELE, 54 | ZOOM_STOP, 55 | DOWN, 56 | UP, 57 | LEFT, 58 | RIGHT, 59 | UP_RIGHT, 60 | UP_LEFT, 61 | DOWN_RIGHT, 62 | DOWN_LEFT, 63 | STOP, 64 | // Extended commands. 65 | SET_PRESET, 66 | CLEAR_PRESET, 67 | GO_TO_PRESET, 68 | FLIP_180DEG_ABOUT, 69 | GO_TO_ZERO_PAN, 70 | SET_AUXILIARY, 71 | CLEAR_AUXILIARY, 72 | REMOTE_RESET, 73 | SET_ZONE_START, 74 | SET_ZONE_END, 75 | WRITE_CHAR_TO_SCREEN, 76 | CLEAR_SCREEN, 77 | ALARM_ACKNOWLEDGE, 78 | ZONE_SCAN_ON, 79 | ZONE_SCAN_OFF, 80 | SET_PATTERN_START, 81 | SET_PATTERN_STOP, 82 | RUN_PATTERN, 83 | SET_ZOOM_SPEED, 84 | SET_FOCUS_SPEED, 85 | RESET_CAMERA_TO_DEFAULT, 86 | AUTO_FOCUS_AUTO_ON_OFF, 87 | AUTO_IRIS_AUTO_ON_OFF, 88 | AGC_AUTO_ON_OFF, 89 | BACKLIGHT_COMPENSATION_ON_OFF, 90 | AUTO_WHITE_BALANCE_ON_OFF, 91 | ENABLE_DEVICE_PHASE_DELAY_MODE, 92 | SET_SHUTTER_SPEED, 93 | ADJUST_LINE_LOCK_PHASE_DELAY, 94 | ADJUST_WHITE_BALANCE_R_B, 95 | ADJUST_WHITE_BALANCE_M_G, 96 | ADJUST_GAIN, 97 | ADJUST_AUTO_IRIS_LEVEL, 98 | ADJUST_AUTO_IRIS_PEACK_VALUE, 99 | QUERY 100 | }; 101 | ``` 102 | *** 103 | ## EXAMPLE COMMAND ENCODING 104 | Below is an example of the platform stop command and an example of the zoom in command. 105 | ```c++ 106 | // Init variables. 107 | uint8_t packet[7]; 108 | uint8_t camera_address = 1; 109 | // STOP camera and PTU. 110 | if (pelco_protocol_parser.GetCommand(packet, camera_address, pelco::PelcoDCommands::STOP)) 111 | { 112 | // Send command. 113 | tcp_port.SendData(packet, 7); 114 | serial_port.SendData(packet, 7); 115 | } 116 | // Zoom tele. 117 | if (pelco_protocol_parser.GetCommand(packet, camera_address, pelco::PelcoDCommands::ZOOM_TELE)) 118 | { 119 | // Send command. 120 | tcp_port.SendData(packet, 7); 121 | serial_port.SendData(packet, 7); 122 | } 123 | ``` 124 | *** 125 | ## DEMO APPLICATION 126 | Folder **Pelco_D_ProtocolParser -> PelcoDProtocolParserTests** contains the demo application files (**PelcoDDemoApplication**). The application reads the **PelcoDemoApplication.json** parameter file after start-up, which contains video source parameters and connection parameters to the camera or pan-tilt platform. The application can control the camera or pan-tilt platform via a serial port or TCP connection. The contents of the configuration file are shown below. 127 | ```json 128 | { 129 | "Video_Source_Params": 130 | { 131 | "video_source_init_string":"0" 132 | }, 133 | 134 | "Communication_Params": 135 | { 136 | "camera_IP":"127.0.0.1", 137 | "TCP_port":5600, 138 | "serial_port_name":"/dev/tty0", 139 | "serial_port_baudrate":115200, 140 | "camera_address":1, 141 | "max_PTU_speed":63 142 | } 143 | } 144 | ``` 145 | The application can capture video from a video file (for example: **video_source_init_string: "test.mp4"**), can capture RTP video stream (for example: **video_source_init_string: "rtp://xxx.xxx.xxx.xxx:1234"**) or can capture video from WEB camera (for example: **video_source_init_string: "0"**). The **Communication_Params** section contains the parameters for the connection to the camera or pan-tilt platform. If you want to control via a TCP connection, you must set the correct TCP port (**TCP_Port** field) and device IP address (**Camera_IP** field) in the config file. If TCP connection is not required then set field **TCP_port** value to **-1**. To control via the serial port you need to set the serial port name (**serial_port_name** field) and baudrate (**serial_port_baudrate** field). The **camera_address** field defines the name of the camera or platform to be controlled via the Pelco-D protocol. The **max_PTU_speed** field defines the maximum pan-tilt platform speed (according to Pelco-D protocol the maximum speed can be not more then **63**). Once the demo application has been started and the parameters initialised, the software shows the video in a separate window. The application uses the **OpenCV** library to capture and display video. To compile the demo application you must specify path to OpenCV lib in you OS. The user interface of demo application is shown below. 146 | 147 | ![PELCO_D](_static/user_interface.png) 148 | 149 | The user interface consists of a video display window with superimposed information: a line from the centre of the video to the position of the mouse pointer and hints for the programme control buttons. The user can control the camera rotation using the mouse. To do this, left-click at any position in the video window. Depending on the position of the mouse pointer, a rotation command will be generated with the desired speed (at the edge of the video the speed will be maximum, and in the centre the speed will be minimum, according to **max_PTU_speed** field in config file). The user can also control the camera using the keyboard buttons: 150 | **1** - Camera ON 151 | **2** - Camera OFF 152 | **3** - Focus NEAR 153 | **4** - Focus FAR 154 | **5** - Zoom WIDE 155 | **6** - Zoom TELE 156 | **D** - Move RIGHT (fixed speed) 157 | **A** - Move LEFT (fixed speed) 158 | **W** - Move UP (fixed speed) 159 | **X** - Move DOWN (fixed speed) 160 | **Q** - Move UP-LEFT (fixed speed) 161 | **E** - Move UP-RIGHT (fixed speed) 162 | **Z** - Move DOWN-LEFT (fixed speed) 163 | **C** - Move DOWN-RIGHT (fixed speed) 164 | 165 | 166 | 167 | 168 | 169 | 170 | -------------------------------------------------------------------------------- /_static/pelco-d_logo_with_borders.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ConstantRobotics/Pelco_D_ProtocolParser/3e435b7c3fb5cff7465b17b0c9848f9bbe4b5289/_static/pelco-d_logo_with_borders.png -------------------------------------------------------------------------------- /_static/user_interface.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ConstantRobotics/Pelco_D_ProtocolParser/3e435b7c3fb5cff7465b17b0c9848f9bbe4b5289/_static/user_interface.png --------------------------------------------------------------------------------