diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json index 25ea3af..08bb42f 100644 --- a/.vscode/c_cpp_properties.json +++ b/.vscode/c_cpp_properties.json @@ -9,6 +9,8 @@ "/opt/aarch64-v01c01-linux-gnu-gcc/lib/ZLMediaKit/include", "/opt/aarch64-v01c01-linux-gnu-gcc/lib/LeakTracer/include", "/opt/aarch64-v01c01-linux-gnu-gcc/lib/opencv-4.11.0/include/opencv4", + "/opt/aarch64-v01c01-linux-gnu-gcc/lib/nng-1.10.1/include", + "build/_deps/ftxui-src/include", "build/_deps/kylin-src" ], "defines": [], diff --git a/CMakeLists.txt b/CMakeLists.txt index 18a8e54..7cb5eb5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -24,7 +24,8 @@ set(Boost_INCLUDE_DIR ${BOOST_ROOT}/include) set(Boost_USE_STATIC_LIBS ON) find_package(Boost REQUIRED COMPONENTS log serialization) -set(MbedTLS_DIR ${Libraries_ROOT}/mbedtls-3.6.2/lib/cmake/MbedTLS) +set(MbedTLS_DIR ${Libraries_ROOT}/mbedtls-3.6.3/lib/cmake/MbedTLS) +set(nng_DIR ${Libraries_ROOT}/nng-1.10.1/lib/cmake/nng) set(ZLMediaKit_ROOT /opt/aarch64-v01c01-linux-gnu-gcc/lib/ZLMediaKit) set(ZLMediaKit_INCLUDE_DIR ${ZLMediaKit_ROOT}/include) @@ -41,4 +42,5 @@ FetchContent_Declare(Kylin GIT_REPOSITORY ssh://git@gitea.amass.fun:2022/amass/Kylin.git ) set(ENABLE_ROUTER ON) +set(ENABLE_NNG ON) FetchContent_MakeAvailable(Kylin) \ No newline at end of file diff --git a/Main/CMakeLists.txt b/Main/CMakeLists.txt index 69ec62e..51c7cba 100644 --- a/Main/CMakeLists.txt +++ b/Main/CMakeLists.txt @@ -7,6 +7,7 @@ add_executable(PassengerStatistics main.cpp Application.h Application.cpp Camera.h Camera.cpp ImageUtilities.h ImageUtilities.cpp + NngServer.h NngServer.cpp RtspServer.h RtspServer.cpp Settings.h Settings.cpp VideoInput.h VideoInput.cpp @@ -38,6 +39,7 @@ target_link_directories(PassengerStatistics target_link_libraries(PassengerStatistics PRIVATE Kylin::Core PRIVATE Kylin::Router + PRIVATE Kylin::Nng PRIVATE LibDataChannel::LibDataChannel PRIVATE OpenSSL::SSL PRIVATE OpenSSL::Crypto diff --git a/Main/Camera.cpp b/Main/Camera.cpp index c7411c7..b025f9a 100644 --- a/Main/Camera.cpp +++ b/Main/Camera.cpp @@ -82,7 +82,7 @@ bool Camera::zoom(Zoom type) { autolens_param.Params.Param_Base.nSpeed = 10; int status = SensorSdk_AutoLens_SetParam(0, zoomType, &autolens_param); - std::this_thread::sleep_for(10ms); + std::this_thread::sleep_for(20ms); autolens_param.Params.Param_Base.nStop = 1; autolens_param.Params.Param_Base.nSpeed = 0; diff --git a/Main/NngServer.cpp b/Main/NngServer.cpp new file mode 100644 index 0000000..69c627e --- /dev/null +++ b/Main/NngServer.cpp @@ -0,0 +1,54 @@ +#include "NngServer.h" +#include "Camera.h" +#include "Core/Logger.h" +#include "Nng/SocketAisoWrapper.h" +#include +#include +#include +#include + +NngServer::NngServer(boost::asio::io_context &ioContex) { + m_socket = std::make_shared(ioContex, Nng::Reply); +} + +void NngServer::asyncRead() { + m_socket->asyncReceive([ptr{weak_from_this()}](const boost::system::error_code &error, const Nng::Buffer &buffer) { + if (error) { + LOG(error) << error.message(); + return; + } + if (ptr.expired()) return; + auto self = ptr.lock(); + std::error_code jsonError; + auto value = boost::json::parse(buffer.data(), jsonError); + if (jsonError) { + LOG(error) << jsonError.message(); + } else { + auto &request = value.as_object(); + auto command = request.at("command").as_string(); + LOG(info) << "command: " << command; + auto camera = Core::Singleton::instance(); + if (command == "ZeroCheck") { + + camera->zeroCheck(); + } else if (command == "Zoom") { + auto direction = request.at("direction").as_string(); + camera->zoom(direction == "In" ? Camera::Zoom::In : Camera::Zoom::Out); + } else if (command == "Focus") { + auto direction = request.at("direction").as_string(); + camera->focus(direction == "Far" ? Camera::Focus::Far : Camera::Focus::Near); + } + } + + LOG(info) << "nng received: " << buffer.data(); + self->m_socket->send((void *)"world", strlen("world") + 1); + self->asyncRead(); + }); +} + +void NngServer::start(uint16_t replyPort) { + std::ostringstream oss; + oss << "tcp://0.0.0.0:" << replyPort; + m_socket->listen(oss.str()); + asyncRead(); +} diff --git a/Main/NngServer.h b/Main/NngServer.h new file mode 100644 index 0000000..880d7d1 --- /dev/null +++ b/Main/NngServer.h @@ -0,0 +1,34 @@ +#ifndef __NNGSERVER_H__ +#define __NNGSERVER_H__ + +#include "Core/Singleton.h" + +namespace boost { +namespace asio { +class io_context; +} +} // namespace boost + +namespace Nng { + +namespace Asio { + +class Socket; +} +} // namespace Nng + +class NngServer : public std::enable_shared_from_this { + friend class Core::Singleton; + +public: + void start(uint16_t replyPort); + +protected: + NngServer(boost::asio::io_context &ioContex); + void asyncRead(); + +private: + std::shared_ptr m_socket; +}; + +#endif // __NNGSERVER_H__ \ No newline at end of file diff --git a/Main/Settings.cpp b/Main/Settings.cpp index 78d0d50..00e3dde 100644 --- a/Main/Settings.cpp +++ b/Main/Settings.cpp @@ -67,4 +67,8 @@ std::string Settings::sqlitePath() const { return m_sqlitePath; } +uint16_t Settings::nngReplyPort() const { + return m_nngReplyPort; +} + } // namespace Danki \ No newline at end of file diff --git a/Main/Settings.h b/Main/Settings.h index c83dde0..2d23264 100644 --- a/Main/Settings.h +++ b/Main/Settings.h @@ -16,6 +16,7 @@ public: uint16_t port() const; std::string documentRoot() const; std::string sqlitePath() const; + uint16_t nngReplyPort() const; private: uint32_t m_threads = 1; @@ -24,6 +25,8 @@ private: std::string m_documentRoot = "/data/sdcard/PassengerStatistics/web"; std::string m_sqlitePath = "database.sqlite"; + + uint16_t m_nngReplyPort = 8000; }; } // namespace Danki diff --git a/Main/WebRTC/Streamer.cpp b/Main/WebRTC/Streamer.cpp index 25cfda9..4427fe7 100644 --- a/Main/WebRTC/Streamer.cpp +++ b/Main/WebRTC/Streamer.cpp @@ -163,6 +163,7 @@ void Streamer::start(const std::string &signalServerAddress, uint16_t signalServ void Streamer::push(const uint8_t *data, uint32_t size) { using namespace std::chrono; + if (m_d->m_clients.empty()) return; boost::asio::post(m_d->strand, [this, frame = rtc::binary(reinterpret_cast(data), reinterpret_cast(data) + size)]() { for (auto &[id, client] : m_d->m_clients) { diff --git a/Main/main.cpp b/Main/main.cpp index 7f26ef5..b50f373 100644 --- a/Main/main.cpp +++ b/Main/main.cpp @@ -4,7 +4,9 @@ #include "Core/IoContext.h" #include "Core/Logger.h" #include "Core/Singleton.h" +#include "NngServer.h" #include "RtspServer.h" +#include "Settings.h" #include "VideoInput.h" #include "WebRTC/Streamer.h" #include "rw_mpp_api.h" @@ -27,12 +29,16 @@ int main(int argc, char const *argv[]) try { }); auto application = Singleton::construct(); + auto settings = Singleton::instance(); auto camera = Singleton::construct(); auto rtsp = std::make_shared(application->ioContext()); auto streamer = std::make_shared(application->ioContext()); streamer->start("127.0.0.1", 80); + auto nng = Singleton::construct(application->ioContext()); + nng->start(settings->nngReplyPort()); + auto video = std::make_shared(2592, 1536); video->setPacketHandler([&](const uint8_t *data, uint32_t size) { rtsp->push(data, size); diff --git a/Tools/CMakeLists.txt b/Tools/CMakeLists.txt index eeb3737..acb0fa7 100644 --- a/Tools/CMakeLists.txt +++ b/Tools/CMakeLists.txt @@ -1 +1,2 @@ -add_subdirectory(LeakTracer) \ No newline at end of file +add_subdirectory(LeakTracer) +add_subdirectory(Controller) \ No newline at end of file diff --git a/Tools/Controller/CMakeLists.txt b/Tools/Controller/CMakeLists.txt new file mode 100644 index 0000000..f4c05a8 --- /dev/null +++ b/Tools/Controller/CMakeLists.txt @@ -0,0 +1,23 @@ +find_package(Boost COMPONENTS json REQUIRED) + +add_executable(Controller + main.cpp + NngClient.h NngClient.cpp +) + +include(FetchContent) + +FetchContent_Declare(FTXUI + GIT_REPOSITORY https://github.com/ArthurSonzogni/FTXUI + GIT_TAG v6.1.8 +) +FetchContent_MakeAvailable(FTXUI) + +target_link_libraries(Controller + PRIVATE Kylin::Core + PRIVATE Kylin::Nng + PRIVATE ftxui::screen + PRIVATE ftxui::dom + PRIVATE ftxui::component + PRIVATE Boost::json +) \ No newline at end of file diff --git a/Tools/Controller/NngClient.cpp b/Tools/Controller/NngClient.cpp new file mode 100644 index 0000000..9437198 --- /dev/null +++ b/Tools/Controller/NngClient.cpp @@ -0,0 +1,54 @@ +#include "NngClient.h" +#include "Core/Logger.h" +#include "Nng/SocketAisoWrapper.h" +#include +#include +#include + +NngClient::NngClient(boost::asio::io_context &ioContex) { + m_socket = std::make_shared(ioContex, Nng::Request); +} + +void NngClient::asyncRead() { + m_socket->asyncReceive([ptr{weak_from_this()}](const boost::system::error_code &error, const Nng::Buffer &buffer) { + if (error) { + LOG(error) << error.message(); + return; + } + if (ptr.expired()) return; + auto self = ptr.lock(); + // LOG(info) << "nng received: " << buffer.data(); + self->asyncRead(); + }); +} + +void NngClient::start(const std::string &server, uint16_t port) { + std::ostringstream oss; + oss << "tcp://" << server << ":" << port; + std::error_code error; + m_socket->dial(oss.str(), error); + asyncRead(); +} + +void NngClient::requestZeroCheck() { + boost::json::object request; + request["command"] = "ZeroCheck"; + auto json = boost::json::serialize(request); + m_socket->send(json.data(), json.size() + 1); +} + +void NngClient::requestZoom(bool in) { + boost::json::object request; + request["command"] = "Zoom"; + request["direction"] = in ? "In" : "Out"; + auto json = boost::json::serialize(request); + m_socket->send(json.data(), json.size() + 1); +} + +void NngClient::requestFocus(bool far) { + boost::json::object request; + request["command"] = "Focus"; + request["direction"] = far ? "Far" : "Near"; + auto json = boost::json::serialize(request); + m_socket->send(json.data(), json.size() + 1); +} diff --git a/Tools/Controller/NngClient.h b/Tools/Controller/NngClient.h new file mode 100644 index 0000000..2f195e0 --- /dev/null +++ b/Tools/Controller/NngClient.h @@ -0,0 +1,38 @@ +#ifndef __NNGCLIENT_H__ +#define __NNGCLIENT_H__ + +#include "Core/Singleton.h" +#include + +namespace boost { +namespace asio { +class io_context; +} +} // namespace boost + +namespace Nng { + +namespace Asio { + +class Socket; +} +} // namespace Nng + +class NngClient : public std::enable_shared_from_this { + friend class Core::Singleton; + +public: + void start(const std::string &server, uint16_t port); + void requestZeroCheck(); + void requestZoom(bool in); + void requestFocus(bool far); + +protected: + NngClient(boost::asio::io_context &ioContex); + void asyncRead(); + +private: + std::shared_ptr m_socket; +}; + +#endif // __NNGCLIENT_H__ \ No newline at end of file diff --git a/Tools/Controller/main.cpp b/Tools/Controller/main.cpp new file mode 100644 index 0000000..386fbc8 --- /dev/null +++ b/Tools/Controller/main.cpp @@ -0,0 +1,60 @@ +#include "Core/IoContext.h" +#include "Core/Singleton.h" +#include "NngClient.h" +#include +#include + +int main() { + using namespace Core; + auto ioContext = Singleton::construct(std::thread::hardware_concurrency()); + auto nng = Singleton::construct(*ioContext->ioContext()); + nng->start("127.0.0.1", 8000); + + auto zeroCheckButton = + ftxui::Button("零点校正", [&]() { nng->requestZeroCheck(); }, ftxui::ButtonOption::Animated()); + + auto zoomButtons = ftxui::Container::Horizontal({ + ftxui::Button( + "+", [&]() { nng->requestZoom(false); }, ftxui::ButtonOption::Animated()), + ftxui::Button( + "-", [&]() { nng->requestZoom(true); }, ftxui::ButtonOption::Animated()), + }); + auto zoomItem = ftxui::Renderer(zoomButtons, [&]() { + return ftxui::hbox({ + ftxui::text("zoom:") | ftxui::vcenter, + zoomButtons->Render(), + }); + }); + + auto focusButtons = ftxui::Container::Horizontal({ + ftxui::Button( + "+", [&]() { nng->requestFocus(true); }, ftxui::ButtonOption::Animated()), + ftxui::Button( + "-", [&]() { nng->requestFocus(false); }, ftxui::ButtonOption::Animated()), + }); + auto focusItem = ftxui::Renderer(focusButtons, [&]() { + return ftxui::hbox({ + ftxui::text("focus:") | ftxui::vcenter, + focusButtons->Render(), + }); + }); + + auto controls = ftxui::Container::Vertical({ + zeroCheckButton, + zoomItem, + focusItem, + }); + + // Modify the way to render them on screen: + auto component = ftxui::Renderer(controls, [&] { + return ftxui::vbox({ + ftxui::text("Nng 控制端"), + controls->Render(), + }); + }); + + ioContext->run(false); + auto screen = ftxui::ScreenInteractive::FitComponent(); + screen.Loop(component); + return 0; +} \ No newline at end of file diff --git a/resources/build.sh b/resources/build.sh index aa94d46..1ff4bdf 100755 --- a/resources/build.sh +++ b/resources/build.sh @@ -96,6 +96,7 @@ function deploy() { echo "deploy to target, path: ${TARGET_PATH} ..." echo "put ${build_path}/Main/PassengerStatistics ${TARGET_PATH}" | sftp danki echo "put ${build_path}/Tools/LeakTracer/LeakTracer ${TARGET_PATH}" | sftp danki + echo "put ${build_path}/Tools/Controller/Controller ${TARGET_PATH}" | sftp danki ssh danki "sync" }