diff --git a/.gitignore b/.gitignore
index 883d63fd..d3ee04ac 100644
--- a/.gitignore
+++ b/.gitignore
@@ -46,4 +46,6 @@ libexample.json
test
*.log
-*.xml
\ No newline at end of file
+*.xml
+
+.xmake
\ No newline at end of file
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
new file mode 100644
index 00000000..0e379f7c
--- /dev/null
+++ b/.pre-commit-config.yaml
@@ -0,0 +1,7 @@
+fail_fast: false
+repos:
+ - repo: https://github.com/pocc/pre-commit-hooks
+ rev: master
+ hooks:
+ - id: clang-format
+ - id: cppcheck
\ No newline at end of file
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 6a57e6dd..f1305852 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -6,7 +6,7 @@
# Author: Max Qian
# License: GPL3
-cmake_minimum_required(VERSION 3.13)
+cmake_minimum_required(VERSION 3.20)
project(Lithium)
enable_language(C CXX)
@@ -87,6 +87,7 @@ endif(APPLE)
# Lithium includes
include_directories(${CMAKE_SOURCE_DIR}/libs/)
+include_directories(${CMAKE_SOURCE_DIR}/driverlibs/)
include_directories(${lithium_src_dir})
include_directories(${lithium_module_dir})
include_directories(${lithium_src_dir})
@@ -94,6 +95,18 @@ include_directories(${CMAKE_SOURCE_DIR}/libs/oatpp)
include_directories(${CMAKE_SOURCE_DIR}/libs/oatpp-swagger)
include_directories(${CMAKE_SOURCE_DIR}/libs/oatpp-websocket)
+find_package(OpenSSL REQUIRED)
+if(OpenSSL_FOUND)
+ message("-- Found OpenSSL ${OPENSSL_VERSION}: ${OPENSSL_LIBRARIES}")
+else()
+ message("-- OpenSSL Not Found")
+endif()
+
+find_package(CFITSIO REQUIRED)
+find_package(ZLIB REQUIRED)
+find_package(SQLite3 REQUIRED)
+find_package(fmt REQUIRED)
+
#################################################################################
#
# Subdirectories of Lithium
@@ -116,6 +129,10 @@ add_subdirectory(modules)
add_subdirectory(driver)
+add_subdirectory(${lithium_src_dir}/carbon)
+add_subdirectory(${lithium_src_dir}/config)
+add_subdirectory(${lithium_src_dir}/server)
+
#################################################################################
#
# General defines for compatibility across different platforms
@@ -179,41 +196,19 @@ set(component_module
${lithium_component_dir}/addons.cpp
${lithium_component_dir}/compiler.cpp
- ${lithium_component_dir}/finder.cpp
${lithium_component_dir}/loader.cpp
${lithium_component_dir}/manager.cpp
${lithium_component_dir}/sandbox.cpp
+ ${lithium_component_dir}/sort.cpp
)
set(config_module
${lithium_src_dir}/config/configor.cpp
- ${lithium_src_dir}/config/HubsConfig.cpp
)
-if (ENABLE_DEBUG_FLAG)
-set(debug_module
- ${lithium_src_dir}/debug/terminal.cpp
-)
-else()
set(debug_module
${lithium_src_dir}/debug/terminal.cpp
)
-endif()
-
-if (ENABLE_ASYNC_FLAG)
-set(server_module
- ${lithium_src_dir}/websocket/Hub.cpp
- ${lithium_src_dir}/websocket/Connection.cpp
- ${lithium_src_dir}/websocket/Registry.cpp
- ${lithium_src_dir}/websocket/Session.cpp
-)
-else()
-set(server_module
- ${lithium_src_dir}/websocket/WsServer.cpp
- ${lithium_src_dir}/websocket/WsHub.cpp
- ${lithium_src_dir}/websocket/WsInstance.cpp
-)
-endif()
set(script_module
${lithium_src_dir}/script/manager.cpp
@@ -221,6 +216,8 @@ set(script_module
${lithium_src_dir}/script/custom/config.cpp
${lithium_src_dir}/script/sheller.cpp
+
+ ${lithium_src_dir}/script/carbon.cpp
)
set(task_module
@@ -235,36 +232,25 @@ set(task_module
)
set(Lithium_module
- ${lithium_src_dir}/AppComponent.hpp
- ${lithium_src_dir}/ErrorHandler.cpp
${lithium_src_dir}/LithiumApp.cpp
)
-find_package(OpenSSL REQUIRED)
-if(OpenSSL_FOUND)
- message("-- Found OpenSSL ${OPENSSL_VERSION}: ${OPENSSL_LIBRARIES}")
-else()
- message("-- OpenSSL Not Found")
-endif()
-
-find_package(CFITSIO REQUIRED)
-find_package(ZLIB REQUIRED)
-find_package(SQLite3 REQUIRED)
-find_package(fmt REQUIRED)
-
#################################################################################
# Main
-add_library(lithium_server-library STATIC ${component_module} ${config_module} ${debug_module} ${module_module} ${device_module} ${task_module} ${server_module} ${script_module} ${Lithium_module})
-add_executable(lithium_server ${lithium_src_dir}/App.cpp)
+add_library(lithium_server-library STATIC ${component_module} ${config_module} ${debug_module} ${module_module} ${device_module} ${task_module} ${script_module} ${Lithium_module})
+target_link_libraries(lithium_server-library loguru)
+add_executable(lithium_server ${lithium_src_dir}/app.cpp)
target_link_directories(lithium_server PUBLIC ${CMAKE_BINARY_DIR}/libs)
target_link_libraries(lithium_server lithium_server-library)
+target_link_libraries(lithium_server lithium_webserver)
target_link_libraries(lithium_server oatpp-websocket oatpp-swagger oatpp-openssl oatpp-zlib oatpp)
target_link_libraries(lithium_server loguru)
target_link_libraries(lithium_server libzippp)
target_link_libraries(lithium_server atomstatic)
-target_link_libraries(lithium_server-library fmt::fmt)
+target_link_libraries(lithium_server carbon)
+target_link_libraries(lithium_server fmt::fmt)
target_compile_definitions(lithium_server PRIVATE LOGURU_DEBUG_LOGGING)
@@ -293,43 +279,3 @@ set_target_properties(
PROPERTIES
OUTPUT_NAME lithium_server
)
-
-#################################################################################
-# Hello Element Astro Launcher
-
-add_library(heal-library STATIC
- ${lithium_src_dir}/auth/AuthHandler.cpp
- ${lithium_src_dir}/auth/AuthHandler.hpp
- ${lithium_src_dir}/auth/JWT.cpp
- ${lithium_src_dir}/auth/JWT.hpp
- ${lithium_src_dir}/controller/StaticController.hpp
- ${lithium_src_dir}/controller/StoryController.hpp
- ${lithium_src_dir}/controller/AuthController.hpp
- ${lithium_src_dir}/database/model/StoryModel.hpp
- ${lithium_src_dir}/database/model/UserModel.hpp
- ${lithium_src_dir}/database/StoryDb.hpp
- ${lithium_src_dir}/database/UserDb.hpp
- ${lithium_src_dir}/data/AuthDto.hpp
- ${lithium_src_dir}/data/PageDto.hpp
- ${lithium_src_dir}/data/SignInDto.hpp
- ${lithium_src_dir}/data/SignUpDto.hpp
- ${lithium_src_dir}/data/StatusDto.hpp
- ${lithium_src_dir}/data/StoryDto.hpp
- ${lithium_src_dir}/interceptor/AuthInterceptor.cpp
- ${lithium_src_dir}/interceptor/AuthInterceptor.hpp
- ${lithium_src_dir}/service/AuthService.cpp
- ${lithium_src_dir}/service/AuthService.hpp
- ${lithium_src_dir}/service/StoryService.cpp
- ${lithium_src_dir}/service/StoryService.hpp
- ${lithium_src_dir}/LauncherComponent.hpp
- ${lithium_src_dir}/components/DatabaseComponent.hpp
- ${lithium_src_dir}/components/SwaggerComponent.hpp
- ${lithium_src_dir}/ErrorHandler.cpp
- ${lithium_src_dir}/ErrorHandler.hpp
-)
-
-target_include_directories(heal-library PUBLIC ${lithium_src_dir})
-
-add_executable(heal ${lithium_src_dir}/Launcher.cpp)
-target_link_libraries(heal heal-library)
-target_link_libraries(heal oatpp-websocket oatpp-swagger oatpp-openssl oatpp-zlib oatpp-sqlite oatpp)
diff --git a/README.md b/README.md
index fdd391fb..90e4a31b 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-## Lithium
+# Lithium
@@ -72,6 +72,10 @@ pacman -S mingw-w64-x86_64-libzip
pacman -S mingw-w64-x86_64-zlib
pacman -S mingw-w64-x86_64-fmt
pacman -S mingw-w64-x86_64-libnova
+pacman -S mingw-w64-x86_64-gsl
+
+# for test
+pacman -S mingw-w64-x86_64-gtest
```
#### On Ubuntu or other similar Linux platforms (No INDI needed)
@@ -79,7 +83,7 @@ pacman -S mingw-w64-x86_64-libnova
```shell
sudo apt-get update && sudo apt-get upgrade -y
sudo apt install gcc g++ cmake
-sudo apt install libcfitsio-dev zlib1g-dev libssl-dev libzip-dev libnova-dev libfmt-dev libudev-dev libuv1-dev
+sudo apt install libcfitsio-dev zlib1g-dev libssl-dev libzip-dev libnova-dev libfmt-dev libudev-dev
```
Alternatively, you can directly run the provided script according to your platform:
@@ -96,10 +100,18 @@ Unfortunately, the newest GCC and CMake are not available on Github Codespace, s
```shell
sudo add-apt-repository ppa:ubuntu-toolchain-r/test -y
sudo apt-get update
-sudo apt-get install gcc-13 # GCC13 is the best choice
+sudo apt-get install gcc-13 g++-13 # GCC13 is the best choice, clang is alse OK
wget https://cmake.org/files/v3.28/cmake-3.28.0-rc5.tar.gz
tar -zxvf cmake-3.28.0-rc5.tar.gz
+./bootstrap && make && sudo make install
+
+#install newest clang-format
+wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add -
+sudo nano /etc/apt/sources.list
+#deb http://apt.llvm.org/focal/ llvm-toolchain-focal-17 main
+#deb-src http://apt.llvm.org/focal/ llvm-toolchain-focal-17 main
+sudo apt install -y clang-format-17
```
Build the code:
@@ -116,7 +128,7 @@ Everything is very simple. The entire process is straightforward.
Here is a poem adapted from a quote :
-```
+```txt
Learning requires not mere imagination,
Nor can it be attained through mediocre dedication.
It is through diligent accumulation,
@@ -196,6 +208,11 @@ pacman -S mingw-w64-x86_64-fmt
pacman -S mingw-w64-x86_64-libnova
# 如果想用make构建
pacman -S make # 注意添加对应的目录,否则会当场爆炸
+
+pacman -S mingw-w64-x86_64-gsl
+
+# 测试用
+pacman -S mingw-w64-x86_64-gtest
```
#### Ubuntu/Debian/Other Linux
diff --git a/STYLE_OF_CODE.md b/STYLE_OF_CODE.md
index 538210ad..cd527f25 100644
--- a/STYLE_OF_CODE.md
+++ b/STYLE_OF_CODE.md
@@ -1,4 +1,6 @@
-# C++ Naming Conventions
+# Style of Code
+
+## C++ Naming Conventions
## Variable Naming
@@ -109,7 +111,7 @@
Good naming conventions improve code readability and maintainability, making it easier for others to understand and use your code.
-# C++命名规则
+## C++命名规则
## 变量命名
diff --git a/launcher/CMakeLists.txt b/driver/camera/atom-asi/_component.cpp
similarity index 100%
rename from launcher/CMakeLists.txt
rename to driver/camera/atom-asi/_component.cpp
diff --git a/driver/camera/atom-asi/_component.hpp b/driver/camera/atom-asi/_component.hpp
new file mode 100644
index 00000000..e69de29b
diff --git a/driver/camera/atom-asi/camera.cpp b/driver/camera/atom-asi/camera.cpp
new file mode 100644
index 00000000..3903f999
--- /dev/null
+++ b/driver/camera/atom-asi/camera.cpp
@@ -0,0 +1,474 @@
+/*
+ * camera.cpp
+ *
+ * Copyright (C) 2023-2024 Max Qian
+ */
+
+/*************************************************
+
+Date: 2023-3-29
+
+Description: ASICamera Simulator and Basic Definition
+
+**************************************************/
+
+#include "camera.hpp"
+
+#include "atom/driver/macro.hpp"
+#include "atom/log/loguru.hpp"
+
+#include
+#include
+
+#define ASI_CAMERA_CONNECTION_CHECK \
+ if (is_connected.load()) { \
+ LOG_F(WARNING, "Camera already connected"); \
+ return true; \
+ }
+
+#define ASI_CAMERA_CONNECT_CHECK \
+ if (!is_connected.load()) { \
+ LOG_F(ERROR, "Camera not connected"); \
+ return false; \
+ }
+
+#define ASI_CAMERA_EXPOSURE_CHECK \
+ if (is_exposing.load()) { \
+ LOG_F(ERROR, "Camera is exposing"); \
+ return false; \
+ }
+
+#define ASI_CAMERA_VIDEO_CHECK \
+ if (is_videoing.load()) { \
+ LOG_F(ERROR, "Camera is videoing"); \
+ return false; \
+ }
+
+using ImgBufferPtr = std::unique_ptr;
+
+ASICamera::ASICamera(const std::string &name) : AtomCamera(name) {}
+
+ASICamera::~ASICamera() {}
+
+bool ASICamera::initialize() {
+ AtomCamera::initialize();
+
+ registerVariable("CAMERA_COUNT", 0, "the number of connected cameras");
+ return true;
+}
+
+bool ASICamera::destroy() {
+ AtomCamera::destroy();
+ return true;
+}
+
+bool ASICamera::connect(const json ¶ms) {
+ ASI_CAMERA_CONNECTION_CHECK;
+ if (!params.contains("name")) {
+ LOG_F(ERROR, "No camera name provided");
+ return false;
+ }
+ auto camera_name = params["name"].get();
+
+ auto camera_count = ASIGetNumOfConnectedCameras();
+ if (camera_count <= 0) {
+ LOG_F(ERROR,
+ "ASI camera not found, please check the power supply or make "
+ "sure the camera is connected.");
+ return false;
+ }
+ for (int i = 0; i < camera_count; i++) {
+ /*获取相机信息*/
+ if ((errCode = ASIGetCameraProperty(&ASICameraInfo, i)) !=
+ ASI_SUCCESS) {
+ LOG_F(ERROR,
+ "Unable to get {} configuration information,the error "
+ "code is {},please check program permissions.\n",
+ ASICameraInfo.Name, errCode);
+ return false;
+ }
+ if (ASICameraInfo.Name == camera_name) {
+ LOG_F(INFO, "Find camera {}", ASICameraInfo.Name);
+ // Max: The member variable is faster than component variable
+ setVariable("DEVICE_ID", ASICameraInfo.CameraID);
+ setVariable("DEVICE_NAME", ASICameraInfo.Name);
+ m_camera_id = ASICameraInfo.CameraID;
+ m_camera_name = ASICameraInfo.Name;
+ /*打开相机*/
+ if ((errCode = ASIOpenCamera(ASICameraInfo.CameraID)) !=
+ ASI_SUCCESS) {
+ LOG_F(ERROR, "Unable to turn on the {}, error code: {}.",
+ ASICameraInfo.Name, errCode);
+ return false;
+ }
+ /*初始化相机*/
+ if ((errCode = ASIInitCamera(ASICameraInfo.CameraID)) !=
+ ASI_SUCCESS) {
+ LOG_F(ERROR,
+ "Unable to initialize connection to "
+ "camera,the error code is {}.",
+ errCode);
+ return false;
+ }
+ setVariable("DEVICE_CONNECTED", true);
+ is_connected.store(true);
+ LOG_F(INFO, "Camera connected successfully\n");
+ return true;
+ } else {
+ LOG_F(ERROR, "This is not a designated camera");
+ }
+ }
+ LOG_F(ERROR, "No camera found");
+ return false;
+}
+
+bool ASICamera::disconnect(const json ¶ms) { /*在关闭相机之前停止所有任务*/
+ ASI_CAMERA_CONNECT_CHECK;
+ if (!params.empty()) {
+ LOG_F(ERROR, "No parameters are allowed");
+ return false;
+ }
+
+ if (is_videoing.load()) {
+ if ((errCode = ASIStopVideoCapture(m_camera_id)) !=
+ ASI_SUCCESS) // 停止视频拍摄
+ {
+ LOG_F(ERROR,
+ "Unable to stop video capture,error code is {},please try "
+ "again.",
+ errCode);
+ return false;
+ }
+ is_videoing.store(false);
+ setVariable("CCD_VIDEO_STATUS", false);
+ LOG_F(INFO, "Stop video capture");
+ }
+ if (is_exposing.load()) {
+ if ((errCode = ASIStopExposure(m_camera_id)) !=
+ ASI_SUCCESS) // 停止曝光
+ {
+ LOG_F(ERROR,
+ "Unable to stop exposure,error code is {}, please try again.",
+ errCode);
+ return false;
+ }
+ is_exposing.store(false);
+ setVariable("CCD_EXPOSURE_STATUS", false);
+ LOG_F(INFO, "Stop exposure");
+ }
+ /*关闭相机*/
+ if ((errCode = ASICloseCamera(m_camera_id)) != ASI_SUCCESS) // 关闭相机
+ {
+ LOG_F(ERROR, "Unable to turn off the camera,error code: {}", errCode);
+ return false;
+ }
+ setVariable("DEVICE_CONNECTED", false);
+ is_connected.store(false);
+ LOG_F(INFO, "Disconnect from camera");
+ return true;
+}
+
+bool ASICamera::reconnect(const json ¶ms) {
+ ASI_CAMERA_CONNECT_CHECK;
+ int timeout = 0;
+ if (params.contains("timeout")) {
+ timeout = params["timeout"].get();
+ }
+
+ if (!disconnect({})) {
+ LOG_F(ERROR, "Unable to disconnect from camera");
+ return false;
+ }
+ if (!connect({})) {
+ LOG_F(ERROR, "Unable to connect to camera");
+ return false;
+ }
+ LOG_F(INFO, "Reconnect to camera: {}",
+ getVariable("DEVICE_NAME"));
+ return true;
+}
+
+bool ASICamera::isConnected() { return is_connected.load(); }
+
+bool ASICamera::startExposure(const double &duration) {
+ ASI_CAMERA_CONNECT_CHECK;
+ if (is_exposing.load()) {
+ LOG_F(ERROR, "Exposure is already in progress");
+ return false;
+ }
+
+ const long blink_duration = duration * 1000000;
+ LOG_F(INFO, "Blinking {} time(us) before exposure", blink_duration);
+ if ((errCode = ASISetControlValue(m_camera_id, ASI_EXPOSURE, blink_duration,
+ ASI_FALSE)) != ASI_SUCCESS) {
+ LOG_F(ERROR, "Failed to set blink exposure to {}us, error {}",
+ blink_duration, errCode);
+ return false;
+ }
+ if ((errCode = ASIStartExposure(m_camera_id, ASI_FALSE)) != ASI_SUCCESS) {
+ LOG_F(ERROR, "Failed to start blink exposure, error code: {}", errCode);
+ return false;
+ }
+ is_exposing.store(true);
+ setVariable("CCD_EXPOSURE_STATUS", true);
+ // Max: A timer is needed here
+ do {
+ usleep(10000);
+ errCode = ASIGetExpStatus(m_camera_id, &expStatus);
+ } while (errCode == ASI_SUCCESS && expStatus == ASI_EXP_WORKING);
+ if (errCode != ASI_SUCCESS) {
+ LOG_F(ERROR, "Blink exposure failed, error {}, error code: {}", errCode,
+ expStatus);
+ return false;
+ }
+ is_exposing.store(false);
+ setVariable("CCD_EXPOSURE_STATUS", false);
+ LOG_F(INFO, "Blink exposure completed");
+ return true;
+}
+
+bool ASICamera::abortExposure() {
+ ASI_CAMERA_CONNECT_CHECK;
+ if (!is_exposing.load()) {
+ LOG_F(ERROR, "No exposure is in progress");
+ return false;
+ }
+ if ((errCode = ASIStopExposure(m_camera_id)) != ASI_SUCCESS) {
+ LOG_F(ERROR, "Unable to stop camera exposure, error code: {}", errCode);
+ return false;
+ }
+ setVariable("CCD_EXPOSURE_STATUS", false);
+ is_exposing.store(false);
+ LOG_F(INFO, "Abort exposure");
+ return true;
+}
+
+bool ASICamera::getExposureStatus() {
+ ASI_CAMERA_CONNECT_CHECK;
+ if ((errCode = ASIGetExpStatus(m_camera_id, &expStatus)) != ASI_SUCCESS) {
+ LOG_F(INFO, "Camera is busy, status code: {}", errCode);
+ setVariable("CCD_EXPOSURE_STATUS", true);
+ is_exposing.store(true);
+ return true;
+ }
+ LOG_F(INFO, "Camera is idle");
+ return false;
+}
+
+bool ASICamera::getExposureResult() {
+ ASI_CAMERA_CONNECT_CHECK;
+ ASI_CAMERA_EXPOSURE_CHECK;
+
+ GET_INT_VARIABLE(width);
+ GET_INT_VARIABLE(height);
+
+ long imgSize = width * height;
+ //* (1 + (ASICAMERA->ImageType == ASI_IMG_RAW16));
+
+ // 使用智能指针管理图像缓冲区内存
+ ImgBufferPtr imgBuf(new unsigned char[imgSize]);
+
+ /*曝光后获取图像信息*/
+ int errCode = ASIGetDataAfterExp(m_camera_id, imgBuf.get(), imgSize);
+ if (errCode != ASI_SUCCESS) {
+ // 获取图像失败
+ LOG_F(ERROR, "Unable to get image from camera, error code: {}",
+ errCode);
+ return;
+ }
+
+ // 图像下载完成
+ LOG_F(INFO, "Download from camera completely.");
+
+ GET_STR_VARIABLE(upload_mode);
+ if (upload_mode == "LOCAL") [[likely]] {
+ // Max: image filename generation logic is needed
+ std::string FitsName = "test.fits";
+ LOG_F(INFO, "Upload mode is LOCAL, save image to {}", FitsName);
+ /*将图像写入本地文件*/
+ // auto res = getComponent("LITHIUM_IMAGE")
+ // ->runFunc("SaveImage", {{"filename", FitsName},
+ // {"data", imgBuf},
+ //{ "size", imgSize }
+ //});
+ // if (res.contains("error")) {
+ // LOG_F(ERROR, "Unable to save image to {}, error: {}", FitsName,
+ // res["error"].get());
+ // return false;
+ //}
+ } else if (upload_mode == "CLIENT") [[unlikely]] {
+ } else if (upload_mode == "None") [[unlikely]] {
+ LOG_F(INFO, "Upload mode is NONE, skip upload");
+ } else {
+ LOG_F(ERROR, "Invalid upload mode: {}", upload_mode);
+ return false;
+ }
+ return true;
+}
+
+bool ASICamera::saveExposureResult() { return true; }
+
+bool ASICamera::startVideo() { return true; }
+
+bool ASICamera::stopVideo() { return true; }
+
+bool ASICamera::getVideoStatus() { return true; }
+
+bool ASICamera::getVideoResult() { return true; }
+
+bool ASICamera::saveVideoResult() { return true; }
+
+bool ASICamera::startCooling() { return true; }
+
+bool ASICamera::stopCooling() { return true; }
+
+bool ASICamera::getCoolingStatus() { return true; }
+
+bool ASICamera::isCoolingAvailable() { return true; }
+
+bool ASICamera::getTemperature() { return true; }
+
+bool ASICamera::getCoolingPower() { return true; }
+
+bool ASICamera::setTemperature(const double &temperature) {
+ ASI_CAMERA_CONNECT_CHECK;
+ ASI_CAMERA_EXPOSURE_CHECK;
+ ASI_CAMERA_VIDEO_CHECK;
+ if (!is_cooling_available) {
+ LOG_F(ERROR, "Cooling is not available");
+ return false;
+ }
+ /*转化温度参数*/
+ long TargetTemp;
+ if (temperature > 0.5)
+ TargetTemp = static_cast(temperature + 0.49);
+ else if (temperature < 0.5)
+ TargetTemp = static_cast(temperature - 0.49);
+ else
+ TargetTemp = 0;
+ /*设置相机温度*/
+ if ((errCode = ASISetControlValue(m_camera_id, ASI_TEMPERATURE, TargetTemp,
+ ASI_FALSE)) != ASI_SUCCESS) {
+ LOG_F(ERROR, "Unable to set camera temperature, error code: {}",
+ errCode);
+ return false;
+ }
+ setVariable("CCD_TEMPERATURE_VALUE", TargetTemp);
+ LOG_F(INFO, "Set camera cooling temperature to {}", TargetTemp);
+ return true;
+}
+
+bool ASICamera::setCoolingPower(const double &power) { return true; }
+
+bool ASICamera::getGain() {
+ ASI_CAMERA_CONNECT_CHECK;
+ long gain;
+ if ((errCode = ASIGetControlValue(m_camera_id, ASI_GAIN, &gain, NULL)) !=
+ ASI_SUCCESS) {
+ LOG_F(ERROR, "Unable to get camera gain, error code: {}", errCode);
+ return false;
+ }
+ setVariable("CCD_GAIN", static_cast(gain));
+ m_gain.store(static_cast(gain));
+ LOG_F(INFO, "Get camera gain: {}", gain);
+ return true;
+}
+
+bool ASICamera::setGain(const int &gain) {
+ ASI_CAMERA_CONNECT_CHECK;
+ ASI_CAMERA_EXPOSURE_CHECK;
+ ASI_CAMERA_VIDEO_CHECK;
+ if ((errCode = ASISetControlValue(m_camera_id, ASI_GAIN, gain,
+ ASI_FALSE)) != ASI_SUCCESS) {
+ LOG_F(ERROR, "Unable to set camera gain,error code: {}", errCode);
+ return false;
+ }
+ setVariable("CCD_GAIN", gain);
+ m_gain.store(gain);
+ LOG_F(INFO, "Set camera gain to {}", gain);
+ return true;
+}
+
+bool ASICamera::isGainAvailable() {
+ LOG_F(INFO, "Gain is available for {}", m_camera_name);
+ return true;
+}
+
+bool ASICamera::getOffset() {
+ ASI_CAMERA_CONNECT_CHECK;
+ long offset;
+ if ((errCode = ASIGetControlValue(m_camera_id, ASI_BRIGHTNESS, &offset,
+ NULL)) != ASI_SUCCESS) {
+ LOG_F(ERROR, "Unable to get camera offset, error code: {}", errCode);
+ return false;
+ }
+ setVariable("CCD_OFFSET", static_cast(offset));
+ m_offset.store(static_cast(offset));
+ LOG_F(INFO, "Get camera offset: {}", offset);
+ return true;
+}
+
+bool ASICamera::setOffset(const int &offset) {
+ ASI_CAMERA_CONNECT_CHECK;
+ ASI_CAMERA_EXPOSURE_CHECK;
+ ASI_CAMERA_VIDEO_CHECK;
+ if ((errCode = ASISetControlValue(m_camera_id, ASI_BRIGHTNESS, offset,
+ ASI_FALSE)) != ASI_SUCCESS) {
+ LOG_F(ERROR, "Unable to set camera offset, error code: {}", errCode);
+ return false;
+ }
+ setVariable("CCD_OFFSET", offset);
+ m_offset.store(offset);
+ LOG_F(INFO, "Set camera offset to {}", offset);
+ return true;
+}
+
+bool ASICamera::isOffsetAvailable() {
+ LOG_F(INFO, "Offset is available for {}", m_camera_name);
+ return true;
+}
+
+bool ASICamera::getISO() {
+ LOG_F(ERROR, "ISO is not available for {}", m_camera_name);
+ return false;
+}
+
+bool ASICamera::setISO(const int &iso) {
+ LOG_F(ERROR, "ISO is not available for {}", m_camera_name);
+ return false;
+}
+
+bool ASICamera::isISOAvailable() {
+ LOG_F(INFO, "ISO is not available for {}", m_camera_name);
+ return false;
+}
+
+bool ASICamera::getFrame() { return true; }
+
+bool ASICamera::setFrame(const int &x, const int &y, const int &w,
+ const int &h) {
+ return true;
+}
+
+bool ASICamera::isFrameSettingAvailable() { return true; }
+
+bool ASICamera::getBinning() { return true; }
+
+bool ASICamera::setBinning(const int &hor, const int &ver) { return true; }
+
+bool ASICamera::getFrameType() { return true; }
+
+bool ASICamera::setFrameType(FrameType type) { return true; }
+
+bool ASICamera::getUploadMode() { return true; }
+
+bool ASICamera::setUploadMode(UploadMode mode) { return true; }
+
+bool ASICamera::refreshCameraInfo() {
+ if ((errCode = ASIGetCameraProperty(&ASICameraInfo, i)) != ASI_SUCCESS) {
+ LOG_F(ERROR, "Unable to get camera information, error code: {}",
+ errCode);
+ return false;
+ }
+ return true;
+}
\ No newline at end of file
diff --git a/driver/camera/atom-asi/camera.hpp b/driver/camera/atom-asi/camera.hpp
new file mode 100644
index 00000000..8c7fe6f2
--- /dev/null
+++ b/driver/camera/atom-asi/camera.hpp
@@ -0,0 +1,120 @@
+#ifndef ATOM_ASI_COMPONENT_HPP
+#define ATOM_ASI_COMPONENT_HPP
+
+#include "atom/driver/camera.hpp"
+
+#include "driverlibs/libasi/ASICamera2.h"
+
+#include
+
+class ASICamera : public AtomCamera {
+public:
+ explicit ASICamera(const std::string &name);
+ ~ASICamera();
+
+ bool initialize() final;
+ bool destroy() final;
+
+ bool connect(const json ¶ms) final;
+
+ bool disconnect(const json ¶ms) final;
+
+ bool reconnect(const json ¶ms) final;
+
+ bool isConnected() final;
+
+ bool startExposure(const double &duration) final;
+
+ bool abortExposure() final;
+
+ bool getExposureStatus() final;
+
+ bool getExposureResult() final;
+
+ bool saveExposureResult() final;
+
+ bool startVideo() final;
+
+ bool stopVideo() final;
+
+ bool getVideoStatus() final;
+
+ bool getVideoResult() final;
+
+ bool saveVideoResult() final;
+
+ bool startCooling() final;
+
+ bool stopCooling() final;
+
+ bool getCoolingStatus() final;
+
+ bool isCoolingAvailable() final;
+
+ bool getTemperature() final;
+
+ bool getCoolingPower() final;
+
+ bool setTemperature(const double &temperature) final;
+
+ bool setCoolingPower(const double &power) final;
+
+ bool getGain() final;
+
+ bool setGain(const int &gain) final;
+
+ bool isGainAvailable() final;
+
+ bool getOffset() final;
+
+ bool setOffset(const int &offset) final;
+
+ bool isOffsetAvailable() final;
+
+ bool getISO() final;
+
+ bool setISO(const int &iso) final;
+
+ bool isISOAvailable() final;
+
+ bool getFrame() final;
+
+ bool setFrame(const int &x, const int &y, const int &w, const int &h) final;
+
+ bool isFrameSettingAvailable() final;
+
+ bool getBinning() final;
+
+ bool setBinning(const int &hor, const int &ver) final;
+
+ bool getFrameType() final;
+
+ bool setFrameType(FrameType type) final;
+
+ bool getUploadMode() final;
+
+ bool setUploadMode(UploadMode mode) final;
+
+private:
+ bool refreshCameraInfo();
+
+ /*ASI相机参数*/
+ ASI_CAMERA_INFO ASICameraInfo;
+ ASI_ERROR_CODE errCode;
+ ASI_EXPOSURE_STATUS expStatus;
+
+ int m_camera_id;
+ std::string m_camera_name;
+
+ std::atomic_bool is_connected;
+ std::atomic_bool is_exposing;
+ std::atomic_bool is_videoing;
+ std::atomic_bool is_cooling;
+
+ bool is_cooling_available;
+
+ std::atomic m_gain;
+ std::atomic m_offset;
+};
+
+#endif
\ No newline at end of file
diff --git a/driver/camera/atom-asi/package.json b/driver/camera/atom-asi/package.json
new file mode 100644
index 00000000..cbd72db2
--- /dev/null
+++ b/driver/camera/atom-asi/package.json
@@ -0,0 +1,35 @@
+{
+ "name": "Atom-Camera-ASI",
+ "version": "1.0.0",
+ "type": "shared",
+ "description": "Atom driver for ASI Camera",
+ "license": "LGPL-3.0-or-later",
+ "author": "Max Qian",
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/ElementAstro/Atom-ASI"
+ },
+ "bugs": {
+ "url": "https://github.com/ElementAstro/Atom-ASI/issues"
+ },
+ "homepage": "https://github.com/ElementAstro/Atom-ASI",
+ "keywords": [
+ "asi",
+ "camera",
+ "filter wheel"
+ ],
+ "scripts": {
+ "dev": "./atom-asi --standalone",
+ "build": "cmake --build-type=Release -- -j 4",
+ "lint": "clang-format -i src/*.cpp src/*.h"
+ },
+ "dependencies": {
+ "asi-sdk" : "^1.34"
+ },
+ "main": {
+ "ASICamera": {
+ "func": "getInstance",
+ "type" : "shared"
+ }
+ }
+}
\ No newline at end of file
diff --git a/driver/camera/atom-touptek/_component.cpp b/driver/camera/atom-touptek/_component.cpp
new file mode 100644
index 00000000..e69de29b
diff --git a/driver/camera/atom-touptek/_component.hpp b/driver/camera/atom-touptek/_component.hpp
new file mode 100644
index 00000000..e69de29b
diff --git a/driver/camera/atom-touptek/camera.cpp b/driver/camera/atom-touptek/camera.cpp
new file mode 100644
index 00000000..79814e10
--- /dev/null
+++ b/driver/camera/atom-touptek/camera.cpp
@@ -0,0 +1,111 @@
+/*
+ * camera.cpp
+ *
+ * Copyright (C) 2023-2024 Max Qian
+ */
+
+/*************************************************
+
+Date: 2023-3-29
+
+Description: Touptek Camera
+
+**************************************************/
+
+#include "camera.hpp"
+
+#include "atom/log/loguru.hpp"
+
+#include
+
+ToupCamera::ToupCamera(const std::string &name)
+ : ToupCamera(name) {}
+
+ToupCamera::~ToupCamera() {}
+
+bool ToupCamera::initialize() { return true; }
+
+bool ToupCamera::destroy() { return true; }
+
+bool ToupCamera::connect(const json ¶ms) { return true; }
+
+bool ToupCamera::disconnect(const json ¶ms) { return true; }
+
+bool ToupCamera::reconnect(const json ¶ms) { return true; }
+
+bool ToupCamera::isConnected() { return true; }
+
+bool ToupCamera::startExposure(const double &duration) { return true; }
+
+bool ToupCamera::abortExposure() { return true; }
+
+bool ToupCamera::getExposureStatus() { return true; }
+
+bool ToupCamera::getExposureResult() { return true; }
+
+bool ToupCamera::saveExposureResult() { return true; }
+
+bool ToupCamera::startVideo() { return true; }
+
+bool ToupCamera::stopVideo() { return true; }
+
+bool ToupCamera::getVideoStatus() { return true; }
+
+bool ToupCamera::getVideoResult() { return true; }
+
+bool ToupCamera::saveVideoResult() { return true; }
+
+bool ToupCamera::startCooling() { return true; }
+
+bool ToupCamera::stopCooling() { return true; }
+
+bool ToupCamera::getCoolingStatus() { return true; }
+
+bool ToupCamera::isCoolingAvailable() { return true; }
+
+bool ToupCamera::getTemperature() { return true; }
+
+bool ToupCamera::getCoolingPower() { return true; }
+
+bool ToupCamera::setTemperature(const double &temperature) { return true; }
+
+bool ToupCamera::setCoolingPower(const double &power) { return true; }
+
+bool ToupCamera::getGain() { return true; }
+
+bool ToupCamera::setGain(const int &gain) { return true; }
+
+bool ToupCamera::isGainAvailable() { return true; }
+
+bool ToupCamera::getOffset() { return true; }
+
+bool ToupCamera::setOffset(const int &offset) { return true; }
+
+bool ToupCamera::isOffsetAvailable() { return true; }
+
+bool ToupCamera::getISO() { return true; }
+
+bool ToupCamera::setISO(const int &iso) { return true; }
+
+bool ToupCamera::isISOAvailable() { return true; }
+
+bool ToupCamera::getFrame() { return true; }
+
+bool ToupCamera::setFrame(const int &x, const int &y, const int &w,
+ const int &h) {
+ return true;
+}
+
+bool ToupCamera::isFrameSettingAvailable() { return true; }
+
+bool ToupCamera::getBinning() { return true; }
+
+bool ToupCamera::setBinning(const int &hor, const int &ver) { return true; }
+
+bool ToupCamera::getFrameType() { return true; }
+
+bool ToupCamera::setFrameType(FrameType type) { return true; }
+
+bool ToupCamera::getUploadMode() { return true; }
+
+bool ToupCamera::setUploadMode(UploadMode mode) { return true; }
\ No newline at end of file
diff --git a/driver/camera/atom-touptek/camera.hpp b/driver/camera/atom-touptek/camera.hpp
new file mode 100644
index 00000000..ee8a7aa5
--- /dev/null
+++ b/driver/camera/atom-touptek/camera.hpp
@@ -0,0 +1,101 @@
+#ifndef ATOM_TOUPTEK_COMPONENT_HPP
+#define ATOM_TOUPTEK_COMPONENT_HPP
+
+#include "atom/driver/camera.hpp"
+
+#include "libtouptek/toupcam.h"
+
+#include
+
+class ToupCamera : public AtomCamera {
+public:
+ explicit ToupCamera(const std::string &name);
+ ~ToupCamera();
+
+ bool initialize() final;
+ bool destroy() final;
+
+ bool connect(const json ¶ms) final;
+
+ bool disconnect(const json ¶ms) final;
+
+ bool reconnect(const json ¶ms) final;
+
+ bool isConnected() final;
+
+ bool startExposure(const double &duration) final;
+
+ bool abortExposure() final;
+
+ bool getExposureStatus() final;
+
+ bool getExposureResult() final;
+
+ bool saveExposureResult() final;
+
+ bool startVideo() final;
+
+ bool stopVideo() final;
+
+ bool getVideoStatus() final;
+
+ bool getVideoResult() final;
+
+ bool saveVideoResult() final;
+
+ bool startCooling() final;
+
+ bool stopCooling() final;
+
+ bool getCoolingStatus() final;
+
+ bool isCoolingAvailable() final;
+
+ bool getTemperature() final;
+
+ bool getCoolingPower() final;
+
+ bool setTemperature(const double &temperature) final;
+
+ bool setCoolingPower(const double &power) final;
+
+ bool getGain() final;
+
+ bool setGain(const int &gain) final;
+
+ bool isGainAvailable() final;
+
+ bool getOffset() final;
+
+ bool setOffset(const int &offset) final;
+
+ bool isOffsetAvailable() final;
+
+ bool getISO() final;
+
+ bool setISO(const int &iso) final;
+
+ bool isISOAvailable() final;
+
+ bool getFrame() final;
+
+ bool setFrame(const int &x, const int &y, const int &w, const int &h) final;
+
+ bool isFrameSettingAvailable() final;
+
+ bool getBinning() final;
+
+ bool setBinning(const int &hor, const int &ver) final;
+
+ bool getFrameType() final;
+
+ bool setFrameType(FrameType type) final;
+
+ bool getUploadMode() final;
+
+ bool setUploadMode(UploadMode mode) final;
+
+private:
+};
+
+#endif
\ No newline at end of file
diff --git a/driver/camera/atom-touptek/detail/libtoupbase.cpp b/driver/camera/atom-touptek/detail/libtoupbase.cpp
new file mode 100644
index 00000000..b4928da2
--- /dev/null
+++ b/driver/camera/atom-touptek/detail/libtoupbase.cpp
@@ -0,0 +1,46 @@
+/*
+ * libtoupbase.cpp
+ *
+ * Copyright (C) 2023-2024 Max Qian
+ */
+
+/*************************************************
+
+Date: 20234-3-1
+
+Description: ToupBase Library
+
+**************************************************/
+
+#include "libtoupbase.hpp"
+#include
+
+std::string errorCodes(HRESULT rc) {
+ static std::unordered_map errCodes = {
+ {0x00000000, "Success"},
+ {0x00000001, "Yet another success"},
+ {0x8000ffff, "Catastrophic failure"},
+ {0x80004001, "Not supported or not implemented"},
+ {0x80070005, "Permission denied"},
+ {0x8007000e, "Out of memory"},
+ {0x80070057, "One or more arguments are not valid"},
+ {0x80004003, "Pointer that is not valid"},
+ {0x80004005, "Generic failure"},
+ {0x8001010e, "Call function in the wrong thread"},
+ {0x8007001f, "Device not functioning"},
+ {0x800700aa, "The requested resource is in use"},
+ {0x8000000a,
+ "The data necessary to complete this operation is not yet available"},
+ {0x8001011f,
+ "This operation returned because the timeout period expired"}};
+
+ const std::unordered_map::iterator it =
+ errCodes.find(rc);
+ if (it != errCodes.end())
+ return it->second;
+ else {
+ char str[256];
+ snprintf(str, sizeof(str), "Unknown error: 0x%08x", rc);
+ return std::string(str);
+ }
+}
\ No newline at end of file
diff --git a/driver/camera/atom-touptek/detail/libtoupbase.hpp b/driver/camera/atom-touptek/detail/libtoupbase.hpp
new file mode 100644
index 00000000..ad4e4923
--- /dev/null
+++ b/driver/camera/atom-touptek/detail/libtoupbase.hpp
@@ -0,0 +1,91 @@
+/*
+ * libtoupbase.cpp
+ *
+ * Copyright (C) 2023-2024 Max Qian
+ */
+
+/*************************************************
+
+Date: 20234-3-1
+
+Description: ToupBase Library
+
+**************************************************/
+
+#pragma once
+
+#include
+
+#ifdef BUILD_TOUPCAM
+#include
+#define FP(x) Toupcam_##x
+#define CP(x) TOUPCAM_##x
+#define XP(x) Toupcam##x
+#define THAND HToupcam
+#define DNAME "ToupTek"
+#elif BUILD_ALTAIRCAM
+#include
+#define FP(x) Altaircam_##x
+#define CP(x) ALTAIRCAM_##x
+#define XP(x) Altaircam##x
+#define THAND HAltaircam
+#define DNAME "Altair"
+#elif BUILD_BRESSERCAM
+#include
+#define FP(x) Bressercam_##x
+#define CP(x) BRESSERCAM_##x
+#define XP(x) Bressercam##x
+#define THAND HBressercam
+#define DNAME "Bresser"
+#elif BUILD_MALLINCAM
+#include
+#define FP(x) Mallincam_##x
+#define CP(x) MALLINCAM_##x
+#define XP(x) Mallincam##x
+#define THAND HMallincam
+#define DNAME "MALLINCAM"
+#elif BUILD_NNCAM
+#include
+#define FP(x) Nncam_##x
+#define CP(x) NNCAM_##x
+#define XP(x) Nncam##x
+#define THAND HNncam
+#define DNAME "Nn"
+#elif BUILD_OGMACAM
+#include
+#define FP(x) Ogmacam_##x
+#define CP(x) OGMACAM_##x
+#define XP(x) Ogmacam##x
+#define THAND HOgmacam
+#define DNAME "OGMAVision"
+#elif BUILD_OMEGONPROCAM
+#include
+#define FP(x) Omegonprocam_##x
+#define CP(x) OMEGONPROCAM_##x
+#define XP(x) Omegonprocam##x
+#define THAND HOmegonprocam
+#define DNAME "Astroshop"
+#elif BUILD_STARSHOOTG
+#include
+#define FP(x) Starshootg_##x
+#define CP(x) STARSHOOTG_##x
+#define XP(x) Starshootg##x
+#define THAND HStarshootg
+#define DNAME "Orion"
+#elif BUILD_TSCAM
+#include
+#define FP(x) Tscam_##x
+#define CP(x) TSCAM_##x
+#define XP(x) Tscam##x
+#define THAND HTscam
+#define DNAME "Teleskop"
+#elif BUILD_MEADECAM
+#include
+#define FP(x) Toupcam_##x
+#define CP(x) TOUPCAM_##x
+#define XP(x) Toupcam##x
+#define THAND HToupcam
+#define DNAME "Meade"
+#endif
+
+extern std::string errorCodes(HRESULT rc);
diff --git a/driver/camera/atom-touptek/package.json b/driver/camera/atom-touptek/package.json
new file mode 100644
index 00000000..b54365c1
--- /dev/null
+++ b/driver/camera/atom-touptek/package.json
@@ -0,0 +1,35 @@
+{
+ "name": "Atom-Camera-Touptek",
+ "version": "1.0.0",
+ "type": "shared",
+ "description": "Atom driver for Touptek Camera",
+ "license": "LGPL-3.0-or-later",
+ "author": "Max Qian",
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/ElementAstro/Atom-Touptek"
+ },
+ "bugs": {
+ "url": "https://github.com/ElementAstro/Atom-Touptek/issues"
+ },
+ "homepage": "https://github.com/ElementAstro/Atom-Touptek",
+ "keywords": [
+ "asi",
+ "camera",
+ "filter wheel"
+ ],
+ "scripts": {
+ "dev": "./atom-touptek --standalone",
+ "build": "cmake --build-type=Release -- -j 4",
+ "lint": "clang-format -i src/*.cpp src/*.h"
+ },
+ "dependencies": {
+ "asi-sdk" : "^1.34"
+ },
+ "main": {
+ "TouptekCamera": {
+ "func": "getInstance",
+ "type" : "shared"
+ }
+ }
+}
\ No newline at end of file
diff --git a/driver/client/atom-hydrogen/hydrogencamera.cpp b/driver/client/atom-hydrogen/hydrogencamera.cpp
index 897c2426..00f5be27 100644
--- a/driver/client/atom-hydrogen/hydrogencamera.cpp
+++ b/driver/client/atom-hydrogen/hydrogencamera.cpp
@@ -26,6 +26,16 @@ HydrogenCamera::HydrogenCamera(const std::string &name) : Camera(name) {
Atom::Utils::StringSwitch>();
m_text_switch = std::make_unique<
Atom::Utils::StringSwitch>();
+
+ registerFunc("connect", &HydrogenCamera::connect, this);
+ registerFunc("disconnect", &HydrogenCamera::disconnect, this);
+ registerFunc("reconnect", &HydrogenCamera::reconnect, this);
+ registerFunc("isConnected", &HydrogenCamera::isConnected, this);
+ registerFunc("startExposure", &HydrogenCamera::startExposure, this);
+ registerFunc("abortExposure", &HydrogenCamera::abortExposure, this);
+ registerFunc("getExposureStatus", &HydrogenCamera::getExposureStatus, this);
+ registerFunc("getExposureResult", &HydrogenCamera::getExposureResult, this);
+
}
HydrogenCamera::~HydrogenCamera() {}
diff --git a/driver/filterwheel/atom-touptek/_component.cpp b/driver/filterwheel/atom-touptek/_component.cpp
new file mode 100644
index 00000000..e69de29b
diff --git a/driver/filterwheel/atom-touptek/_component.hpp b/driver/filterwheel/atom-touptek/_component.hpp
new file mode 100644
index 00000000..e69de29b
diff --git a/example/atom/algorithm/base.cpp b/example/atom/algorithm/base.cpp
new file mode 100644
index 00000000..e19e659d
--- /dev/null
+++ b/example/atom/algorithm/base.cpp
@@ -0,0 +1,44 @@
+#include "atom/algorithm/base.hpp"
+
+#include
+
+using namespace Atom::Algorithm;
+
+int main() {
+ std::string input = "Hello, world!";
+ auto encoded = base32Encode(reinterpret_cast(input.data()),
+ input.size());
+ std::cout << "Encoded: " << encoded << std::endl;
+
+ try {
+ auto decoded = base32Decode(encoded);
+ std::cout << "Decoded: " << decoded << std::endl;
+ } catch (const std::invalid_argument& e) {
+ std::cout << "Error: " << e.what() << std::endl;
+ }
+
+ std::string input = "Hello, world!";
+ auto encoded = base128Encode(
+ reinterpret_cast(input.data()), input.size());
+ std::cout << "Encoded: " << encoded << std::endl;
+
+ try {
+ auto decoded = base128Decode(encoded);
+ std::cout << "Decoded: " << decoded << std::endl;
+ } catch (const std::invalid_argument& e) {
+ std::cout << "Error: " << e.what() << std::endl;
+ }
+
+ uint8_t key = 0x42;
+ auto encrypted = xorEncrypt(input, key);
+ std::cout << "Encrypted: "
+ << base128Encode(
+ reinterpret_cast(encrypted.data()),
+ encrypted.size())
+ << std::endl;
+
+ auto decrypted = xorDecrypt(encrypted, key);
+ std::cout << "Decrypted: " << decrypted << std::endl;
+
+ return 0;
+}
\ No newline at end of file
diff --git a/example/atom/async/queue.cpp b/example/atom/async/queue.cpp
new file mode 100644
index 00000000..7750871b
--- /dev/null
+++ b/example/atom/async/queue.cpp
@@ -0,0 +1,118 @@
+#include
+#include
+#include
+
+using namespace Atom::Async;
+
+void producer(ThreadSafeQueue& queue) {
+ for (int i = 0; i < 10; ++i) {
+ queue.put(i);
+ std::cout << "Produced: " << i << std::endl;
+ std::this_thread::sleep_for(std::chrono::milliseconds(500));
+ }
+}
+
+void consumer(ThreadSafeQueue& queue) {
+ while (true) {
+ auto item = queue.take();
+ if (!item) {
+ break;
+ }
+ std::cout << "Consumed: " << *item << std::endl;
+ }
+}
+
+int main() {
+ ThreadSafeQueue queue;
+
+ std::thread t1(producer, std::ref(queue));
+ std::thread t2(consumer, std::ref(queue));
+
+ t1.join();
+ queue.destroy();
+ t2.join();
+
+ // 使用 emplace
+ ThreadSafeQueue strQueue;
+ strQueue.emplace("Hello");
+ strQueue.emplace("World");
+ std::cout << "Front: " << *strQueue.front() << std::endl;
+ std::cout << "Back: " << *strQueue.back() << std::endl;
+
+ // 使用 waitFor
+ ThreadSafeQueue intQueue;
+ std::thread t3([&intQueue]() {
+ std::this_thread::sleep_for(std::chrono::seconds(1));
+ intQueue.put(42);
+ });
+ auto item =
+ intQueue.waitFor([](const std::queue& q) { return !q.empty(); });
+ std::cout << "Waited for: " << *item << std::endl;
+ t3.join();
+
+ // 使用 extractIf
+ /*
+ ThreadSafeQueue extractQueue;
+ for (int i = 0; i < 10; ++i) {
+ extractQueue.put(i);
+ }
+ auto evenNumbers = extractQueue.extractIf([](int i) { return i % 2 == 0; });
+ std::cout << "Extracted even numbers: ";
+ for (auto num : evenNumbers) {
+ std::cout << num << " ";
+ }
+ std::cout << std::endl;
+ */
+
+
+ // 使用 sort
+ ThreadSafeQueue sortQueue;
+ sortQueue.put(3);
+ sortQueue.put(1);
+ sortQueue.put(4);
+ sortQueue.put(1);
+ sortQueue.put(5);
+ sortQueue.sort(std::greater());
+ std::cout << "Sorted queue: ";
+ while (!sortQueue.empty()) {
+ std::cout << *sortQueue.take() << " ";
+ }
+ std::cout << std::endl;
+
+ // 使用 transform
+ ThreadSafeQueue transformQueue;
+ for (int i = 1; i <= 5; ++i) {
+ transformQueue.put(i);
+ }
+ /*auto squaredQueue =
+ transformQueue.transform([](int i) { return i * i; });
+ std::cout << "Squared queue: ";
+ while (!squaredQueue.empty()) {
+ std::cout << *squaredQueue.take() << " ";
+ }
+ std::cout << std::endl;*/
+
+ // 使用 groupBy
+ /*
+ ThreadSafeQueue groupByQueue;
+ groupByQueue.put("apple");
+ groupByQueue.put("banana");
+ groupByQueue.put("cherry");
+ groupByQueue.put("date");
+ groupByQueue.put("elderberry");
+ auto groupedQueues = groupByQueue.groupBy(
+ [](const std::string& s) -> std::string {
+ return std::to_string(s[0]);
+ });
+ std::cout << "Grouped queues: " << std::endl;
+ for (auto& queue : groupedQueues) {
+ std::cout << "Group: ";
+ while (!queue.empty()) {
+ std::cout << *queue.take() << " ";
+ }
+ std::cout << std::endl;
+ }
+ */
+
+ return 0;
+}
\ No newline at end of file
diff --git a/example/atom/experiment/bind_first.cpp b/example/atom/experiment/bind_first.cpp
new file mode 100644
index 00000000..135d2cd4
--- /dev/null
+++ b/example/atom/experiment/bind_first.cpp
@@ -0,0 +1,52 @@
+#include "atom/experiment/bind_first.hpp"
+
+#include
+#include
+
+int add(int a, int b) { return a + b; }
+
+struct Adder {
+ int operator()(int a, int b) const { return a + b; }
+};
+
+struct Person {
+ std::string name;
+
+ void greet(const std::string &message) const {
+ std::cout << "Hello, " << name << "! " << message << std::endl;
+ }
+};
+
+int main() {
+ // 绑定普通函数
+ auto add5 = bind_first(add, 5);
+ std::cout << add5(3) << std::endl; // 输出: 8
+
+ // 绑定函数对象
+ // Adder adder;
+ // auto adder10 = bind_first(adder, 10);
+ // std::cout << adder10(7) << std::endl; // 输出: 17
+
+ // 绑定成员函数
+ Person person{"Alice"};
+ auto greet_alice = bind_first(&Person::greet, person);
+ greet_alice("How are you?"); // 输出: Hello, Alice! How are you?
+
+ // 绑定 std::function
+ std::function fn = [](int a, int b) { return a * b; };
+ auto multiply3 = bind_first(fn, 3);
+ std::cout << multiply3(5) << std::endl; // 输出: 15
+
+ // 绑定成员函数对象
+ // auto greet_bob = bind_first(std::cref(person), "Bob", &Person::greet);
+ // greet_bob("Nice to meet you!"); // 输出: Hello, Bob! Nice to meet you!
+
+ // 使用 is_invocable_v 和 is_nothrow_invocable_v
+ static_assert(is_invocable_v);
+ // static_assert(is_nothrow_invocable_v);
+ static_assert(is_invocable_v);
+ static_assert(is_invocable_v);
+ // static_assert(is_invocable_v);
+
+ return 0;
+}
\ No newline at end of file
diff --git a/example/atom/experiment/callable.cpp b/example/atom/experiment/callable.cpp
new file mode 100644
index 00000000..aadf1d8b
--- /dev/null
+++ b/example/atom/experiment/callable.cpp
@@ -0,0 +1,64 @@
+#include "atom/experiment/callable.hpp"
+
+#include
+#include
+
+struct Person {
+ Person(std::string name, int age) : m_name(std::move(name)), m_age(age) {}
+
+ void greet(const std::string &message) const {
+ std::cout << "Hello, " << m_name << "! " << message << std::endl;
+ }
+
+ std::string m_name;
+ int m_age;
+};
+
+int add(int a, int b) { return a + b; }
+
+int main() {
+ // 使用 Constructor 构造对象
+ Constructor person_constructor;
+ auto person = person_constructor("Alice", 30);
+ std::cout << "Name: " << person->m_name << ", Age: " << person->m_age
+ << std::endl;
+
+ // 使用 Const_Caller 调用常成员函数
+ Const_Caller greet_caller(
+ &Person::greet);
+ greet_caller(*person, "How are you?");
+
+ // 使用 Fun_Caller 调用普通函数
+ Fun_Caller add_caller(add);
+ int sum = add_caller(3, 5);
+ std::cout << "Sum: " << sum << std::endl;
+
+ // 使用 Caller 调用非常成员函数
+ struct Square {
+ int operator()(int x) { return x * x; }
+ };
+ Square square;
+ Caller square_caller(&Square::operator());
+ int result = square_caller(square, 4);
+ std::cout << "Square: " << result << std::endl;
+
+ // 使用 Arity 获取函数参数个数
+ // static_assert(Arity::value == 3);
+
+ // 使用 Function_Signature 获取函数签名
+ static_assert(
+ std::is_same_v::Return_Type,
+ int>);
+ static_assert(
+ std::is_same_v::Signature,
+ int (*)(double, char)>);
+
+ // 使用 Callable_Traits 获取可调用对象的特征
+ auto lambda = [](int x, int y) { return x + y; };
+ static_assert(
+ std::is_same_v::Return_Type, int>);
+ static_assert(std::is_same_v::Signature,
+ int (*)(int, int)>);
+
+ return 0;
+}
\ No newline at end of file
diff --git a/example/atom/experiment/static_vector.cpp b/example/atom/experiment/static_vector.cpp
new file mode 100644
index 00000000..88fa77df
--- /dev/null
+++ b/example/atom/experiment/static_vector.cpp
@@ -0,0 +1,87 @@
+#include "atom/experiment/static_vector.hpp"
+
+#include
+#include
+
+int main() {
+ // 创建一个容量为5的StaticVector,存储int类型
+ StaticVector vec;
+
+ // 使用push_back添加元素
+ vec.push_back(1);
+ vec.push_back(2);
+ vec.push_back(3);
+
+ // 使用emplace_back添加元素
+ vec.emplace_back(4);
+ vec.emplace_back(5);
+
+ // 使用下标操作符访问元素
+ std::cout << "vec[0]: " << vec[0] << std::endl;
+ std::cout << "vec[1]: " << vec[1] << std::endl;
+
+ // 使用at函数访问元素
+ std::cout << "vec.at(2): " << vec.at(2) << std::endl;
+ try {
+ std::cout << "vec.at(5): " << vec.at(5) << std::endl;
+ } catch (const std::out_of_range& e) {
+ std::cout << "Out of range: " << e.what() << std::endl;
+ }
+
+ // 使用front和back函数
+ std::cout << "vec.front(): " << vec.front() << std::endl;
+ std::cout << "vec.back(): " << vec.back() << std::endl;
+
+ // 使用size和capacity函数
+ std::cout << "vec.size(): " << vec.size() << std::endl;
+ std::cout << "vec.capacity(): " << vec.capacity() << std::endl;
+
+ // 使用范围for循环遍历元素
+ std::cout << "Elements in vec: ";
+ for (int x : vec) {
+ std::cout << x << " ";
+ }
+ std::cout << std::endl;
+
+ // 使用初始化列表构造函数
+ StaticVector str_vec{"Hello", "World", "!"};
+
+ // 使用比较操作符
+ StaticVector vec1{1, 2, 3};
+ StaticVector vec2{1, 2, 3};
+ StaticVector vec3{1, 2, 4};
+
+ std::cout << "vec1 == vec2: " << (vec1 == vec2) << std::endl;
+ std::cout << "vec1 == vec3: " << (vec1 == vec3) << std::endl;
+ // std::cout << "vec1 <=> vec2: " << (vec1 <=> vec2) << std::endl;
+ // std::cout << "vec1 <=> vec3: " << (vec1 <=> vec3) << std::endl;
+
+ // 使用swap函数
+ StaticVector vec4{1, 2, 3};
+ StaticVector vec5{4, 5, 6};
+ std::cout << "Before swap: ";
+ std::cout << "vec4: ";
+ for (int x : vec4) {
+ std::cout << x << " ";
+ }
+ std::cout << "vec5: ";
+ for (int x : vec5) {
+ std::cout << x << " ";
+ }
+ std::cout << std::endl;
+
+ swap(vec4, vec5);
+
+ std::cout << "After swap: ";
+ std::cout << "vec4: ";
+ for (int x : vec4) {
+ std::cout << x << " ";
+ }
+ std::cout << "vec5: ";
+ for (int x : vec5) {
+ std::cout << x << " ";
+ }
+ std::cout << std::endl;
+
+ return 0;
+}
\ No newline at end of file
diff --git a/example/benchmark.hpp b/example/benchmark.hpp
new file mode 100644
index 00000000..3e8dd87d
--- /dev/null
+++ b/example/benchmark.hpp
@@ -0,0 +1,88 @@
+#ifndef LITHIUM_BENCHMARK_HPP
+#define LITHIUM_BENCHMARK_HPP
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+class Benchmark {
+public:
+ using Clock = std::chrono::high_resolution_clock;
+ using TimePoint = Clock::time_point;
+ using Duration = Clock::duration;
+
+ Benchmark(const std::string& name) : name_(name) {}
+
+ template
+ void Run(Func&& func, int iterations) {
+ std::vector durations(iterations);
+
+ std::transform(std::execution::par, durations.begin(), durations.end(),
+ durations.begin(), [&func](const Duration&) {
+ TimePoint start = Clock::now();
+ func();
+ return Clock::now() - start;
+ });
+
+ Duration totalDuration = std::accumulate(
+ durations.begin(), durations.end(), Duration::zero());
+ double averageDuration = static_cast(totalDuration.count()) /
+ iterations / 1000.0; // in microseconds
+
+ double variance =
+ std::transform_reduce(
+ std::execution::par, durations.begin(), durations.end(), 0.0,
+ std::plus<>(),
+ [&averageDuration](const Duration& d) {
+ double durationInMicroseconds =
+ static_cast(d.count()) / 1000.0;
+ return std::pow(durationInMicroseconds - averageDuration,
+ 2);
+ }) /
+ iterations;
+
+ double standardDeviation = std::sqrt(variance);
+
+ results_.push_back({name_, totalDuration, averageDuration,
+ standardDeviation, iterations});
+ }
+
+ static void PrintResults() {
+ std::cout << "Benchmark Results:\n";
+ for (const auto& result : results_) {
+ std::cout << std::setw(20) << std::left << result.name << ": "
+ << std::chrono::duration_cast(
+ result.totalDuration)
+ .count()
+ << " us (avg: " << std::setprecision(4)
+ << result.averageDuration
+ << " us, std dev: " << result.standardDeviation << " us, "
+ << result.iterations << " iterations)\n";
+ }
+ }
+
+private:
+ struct Result {
+ std::string name;
+ Duration totalDuration;
+ double averageDuration;
+ double standardDeviation;
+ int iterations;
+ };
+
+ static inline std::vector results_;
+ std::string name_;
+};
+
+#define BENCHMARK(name, func, iterations) Benchmark(name).Run(func, iterations)
+
+std::vector Benchmark::results_;
+
+#endif
diff --git a/example/component_test/package.json b/example/component_test/package.json
index 86abadb4..840e9317 100644
--- a/example/component_test/package.json
+++ b/example/component_test/package.json
@@ -1,55 +1,36 @@
{
- "name": "example",
- "version": "0.0.0",
+ "name": "atom-config",
+ "version": "1.0.0",
"type": "shared",
- "description": "An example project",
+ "description": "Atom driver for Touptek Camera",
"license": "LGPL-3.0-or-later",
"author": "Max Qian",
"repository": {
"type": "git",
- "url": "https://github.com/maxqian/cobalt-example.git"
+ "url": "https://github.com/ElementAstro/Atom-Touptek"
},
"bugs": {
- "url": "https://github.com/maxqian/cobalt-example/issues"
+ "url": "https://github.com/ElementAstro/Atom-Touptek/issues"
},
- "homepage": "https://github.com/maxqian/cobalt-example",
+ "homepage": "https://github.com/ElementAstro/Atom-Touptek",
"keywords": [
- "cobalt",
- "example"
+ "asi",
+ "camera",
+ "filter wheel"
],
"scripts": {
- "dev": "vite",
- "build": "vite build",
- "lint": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0",
- "preview": "vite preview"
+ "build": "cmake --build-type=Release -- -j 4",
+ "foramt": "clang-format -i src/*.cpp src/*.h",
+ "lint": "clang-tidy src/*.cpp src/*.h",
+ "test": "echo \"Error: no test specified\" && exit 1"
},
"dependencies": {
- "axios": "^1.6.2",
- "bootstrap": "^5.3.2",
- "chart.js": "^4.4.1",
- "easy-peasy": "^6.0.4",
- "formik": "^2.4.5",
- "isomorphic-ws": "^5.0.0",
- "less": "^4.2.0",
- "localforage": "^1.10.0",
- "lodash": "^4.17.21",
- "lodash-es": "^4.17.21",
- "match-sorter": "^6.3.1",
- "react": "^18.2.0",
- "react-bootstrap": "^2.9.1",
- "react-bootstrap-icons": "^1.10.3",
- "react-dom": "^18.2.0",
- "react-router-bootstrap": "^0.26.2",
- "react-router-dom": "^6.21.1",
- "redux": "^4.2.1",
- "sort-by": "^0.0.2",
- "vite-plugin-svgr": "^4.2.0",
- "yup": "^1.3.3"
+ "asi-sdk": "^1.34"
},
- "main": {
- "example1": {
- "func": "getExample1",
- "type" : "shared"
+ "modules": {
+ "main": {
+ "func": "getInstance",
+ "check": true
}
}
}
\ No newline at end of file
diff --git a/launcher/#launcher.glade# b/launcher/#launcher.glade#
deleted file mode 100644
index ef2bca54..00000000
--- a/launcher/#launcher.glade#
+++ /dev/null
@@ -1,437 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
- False
- 440
- 250
- assets\atom.png
-
-
- True
- False
- vertical
- top
-
-
-
- False
- True
- 0
-
-
-
-
- True
- True
- left
- True
- True
-
-
- True
- False
- vertical
-
-
-
-
-
-
-
-
-
-
-
-
-
- True
- False
- Home
- center
- True
-
-
- Home
- True
- True
- True
-
-
-
-
- True
- False
- vertical
-
-
-
-
-
-
-
-
-
-
-
- 1
-
-
-
-
- True
- False
- Installation
-
-
- 1
- False
-
-
-
-
- True
- False
- vertical
-
-
-
-
-
-
-
-
-
-
-
- 2
-
-
-
-
- True
- False
- Settings
-
-
- 2
- False
-
-
-
-
- True
- True
- 1
-
-
-
-
- True
- True
-
-
- False
- True
- 2
-
-
-
-
-
-
- False
- About
- True
- help-about
- dialog
- Hello ElementAstro Launcher
- 1.0.0
- https://github.com/ElementAstro
- Official Github Repository
- Max Qian
- Max Qian
- Max Qian
- Max Qian
- assets\atom.png
- gpl-3-0
-
-
- False
- vertical
- 2
-
-
- False
- end
-
-
- False
- False
- 0
-
-
-
-
-
-
-
-
-
- False
- Lithium Script Editor
- True
- True
- edit-find-replace
- dialog
- lithium
- Lithium Script Editor
-
-
- False
- vertical
- 2
-
-
- False
- end
-
-
- False
- False
- 0
-
-
-
-
-
-
diff --git a/launcher/assets/atom.png b/launcher/assets/atom.png
deleted file mode 100644
index 6d32705b..00000000
Binary files a/launcher/assets/atom.png and /dev/null differ
diff --git a/launcher/launcher.cpp b/launcher/launcher.cpp
deleted file mode 100644
index a8ea8e84..00000000
--- a/launcher/launcher.cpp
+++ /dev/null
@@ -1,552 +0,0 @@
-/*
- * launcher.cpp
- *
- * Copyright (C) 2023-2024 Max Qian
- */
-
-/*************************************************
-
-Date: 2023-3-29
-
-Description: Lithium Server Launcher
-
-**************************************************/
-
-#include "launcher.hpp"
-
-#include
-#include
-#include
-#include
-
-#include "atom/system/crash.hpp"
-#include "atom/log/loguru.hpp"
-
-ServerLauncher::ServerLauncher(const std::string &config_file_path, const std::string &DLOG_File_path)
- : _config_file_path(config_file_path), _DLOG_File_path(DLOG_File_path)
-{
- try
- {
- // 加载配置文件
- load_config();
- }
- catch (const std::exception &e)
- {
- DLOG_F(ERROR, "Failed to initialize ServerLauncher: {}", e.what());
- throw;
- }
-}
-
-void ServerLauncher::run()
-{
- try
- {
- // 检查服务器所需的资源文件是否存在
- if (!check_resources())
- {
- DLOG_F(INFO, "Some resource files are missing, downloading now...");
- download_resources();
- }
-
- check_dependencies();
-
- check_config_file(_config_file_path);
-
- // 启动服务器
- start_server();
-
- // 读取服务器输出
- read_server_output();
-
- // 发送停止命令给服务器,并等待服务器退出
- stop_server();
-
- // 等待服务器退出
- wait_for_server_to_exit();
-
- DLOG_F(INFO, "Server stopped.");
- }
- catch (const std::exception &e)
- {
- LOG_F(ERROR, "Error occurred in ServerLauncher::run(): {}", e.what());
- throw;
- }
-}
-
-void ServerLauncher::stop()
-{
- // 设置请求停止服务器的标志
- _stop_requested = true;
-
- // 唤醒服务器条件变量,以便服务器检测到停止请求
- _server_cv.notify_all();
-
- DLOG_F(INFO, "Stop command sent to server.");
-}
-
-bool ServerLauncher::is_running() const
-{
- return _server_running;
-}
-
-void ServerLauncher::load_config()
-{
- std::ifstream config_file(_config_file_path);
- if (!config_file)
- {
- LOG_F(ERROR, "Failed to open config file: {}", _config_file_path);
- throw std::runtime_error("Failed to open config file: " + _config_file_path);
- }
- try
- {
- config_file >> _config;
- DLOG_F(INFO, "Config file loaded successfully.");
- }
- catch (const std::exception &e)
- {
- LOG_F(ERROR, "Error occurred when reading config file: {}", e.what());
- throw;
- }
-}
-
-bool ServerLauncher::check_resources()
-{
- const auto &resources = _config["resources"];
-
- for (const auto &res_file : resources)
- {
- const std::string filename = res_file;
-
- if (!fs::exists(filename))
- {
- LOG_F(ERROR, "Resource file '{}' is missing.", filename);
- return false;
- }
-
- // 计算 SHA256 值
- std::string sha256_val;
- if (!calculate_sha256(filename, sha256_val))
- {
- LOG_F(ERROR, "Failed to calculate SHA256 value of '" << filename << "'." << std::endl;
- return false;
- }
-
- const std::string expected_sha256 = res_file["sha256"];
- if (sha256_val != expected_sha256)
- {
- LOG_F(ERROR, "SHA256 check failed for '" << filename << "'." << std::endl);
- return false;
- }
- }
-
- DLOG_F(INFO, "All resource files are found.");
- return true;
-}
-
-void ServerLauncher::download_resources()
-{
- DLOG_F(INFO, "Downloading missing resources...");
-
- // 创建线程池
- ThreadPool pool(std::thread::hardware_concurrency());
-
- // 创建任务列表
- std::vector> tasks;
-
- for (const auto &res_file : _config["resources"])
- {
- // 发送 HTTP GET 请求下载文件
- const std::string url = _config["resource_server"].get() + "/" + res_file.get();
-
- // 添加下载任务到线程池
- tasks.emplace_back(pool.enqueue([url, res_file, this]
- {
- try {
- httplib::Client client(_config["resource_server"]);
- auto res = client.Get(url.c_str());
-
- if (!res) {
- LOG_F(ERROR, "Failed to download resource: {}", res_file.get());
- return false;
- }
-
- // 将下载的数据写入文件
- std::ofstream outfile(res_file);
- outfile.write(res->body.c_str(), res->body.size());
-
- DLOG_F(INFO,"Resource file '{}' downloaded.", res_file.get());
- return true;
- }
- catch (const std::exception &e) {
- LOG_F(ERROR, "Error occurred when downloading resource '{}: {}" ,res_file.get(), << e.what());
- return false;
- } }));
- }
-
- // 等待所有任务完成
- for (auto &&task : tasks)
- {
- task.wait();
- }
-
- // 检查是否有任务失败
- for (auto &&task : tasks)
- {
- if (!task.get())
- {
- LOG_F(ERROR, "Failed to download some resources.");
- }
- }
-
- DLOG_F(INFO, "Downloading finished.");
-}
-
-bool check_process(const std::string &name)
-{
-#ifdef _WIN32
- std::string command = "tasklist /FI \"IMAGENAME eq " + name + "\"";
-
- // 创建匿名管道
- SECURITY_ATTRIBUTES saAttr;
- HANDLE hReadPipe, hWritePipe;
- saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
- saAttr.bInheritHandle = TRUE;
- saAttr.lpSecurityDescriptor = NULL;
-
- if (!CreatePipe(&hReadPipe, &hWritePipe, &saAttr, 0))
- {
- return false;
- }
-
- // 设置命令行输出重定向到管道
- STARTUPINFOA si;
- PROCESS_INFORMATION pi;
- ZeroMemory(&si, sizeof(STARTUPINFOA));
- ZeroMemory(&pi, sizeof(PROCESS_INFORMATION));
- si.cb = sizeof(STARTUPINFOA);
- si.hStdError = hWritePipe;
- si.hStdOutput = hWritePipe;
- si.dwFlags |= STARTF_USESTDHANDLES;
-
- // 启动命令行进程
- if (!CreateProcessA(NULL, const_cast(command.c_str()), NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi))
- {
- CloseHandle(hReadPipe);
- CloseHandle(hWritePipe);
- return false;
- }
-
- // 关闭写入端,避免读取阻塞
- CloseHandle(hWritePipe);
-
- // 读取命令行的输出结果
- const int BUFFER_SIZE = 4096;
- char buffer[BUFFER_SIZE];
- DWORD bytesRead = 0;
- std::string output;
-
- while (ReadFile(hReadPipe, buffer, BUFFER_SIZE - 1, &bytesRead, NULL))
- {
- if (bytesRead == 0)
- {
- break;
- }
-
- buffer[bytesRead] = '\0';
- output += buffer;
- }
-
- // 关闭管道和进程句柄
- CloseHandle(hReadPipe);
- CloseHandle(pi.hProcess);
- CloseHandle(pi.hThread);
-
- // 检查输出结果中是否包含进程名
- return (output.find(name) != std::string::npos);
-#else
- std::string command = "ps aux | grep -v grep | grep -q '" + name + "'";
- return (system(command.c_str()) == 0);
-#endif
-}
-
-bool ServerLauncher::check_dependencies()
-{
- const std::vector dependencies = {"redis-server", "mysqld"};
-
- for (const auto &dependency : dependencies)
- {
- if (!check_process(dependency))
- {
- DLOG_F(INFO, "Dependency process '{}' is not running.", dependency);
- return false;
- }
- }
- DLOG_F(INFO, "All dependencies are ready.");
- return true;
-}
-
-bool ServerLauncher::check_config_file(const std::string &config_file)
-{
- if (!std::filesystem::exists(config_file))
- {
- LOG_F(ERROR, "Config file not found: {}", config_file);
- return false;
- }
-
- try
- {
- std::ifstream ifs(config_file);
- json config = json::parse(ifs);
-
- // 检查 "port" 配置项是否存在
- if (config.find("port") == config.end())
- {
- LOG_F(ERROR, "Config item 'port' not found in config file.");
- return false;
- }
-
- // 检查 "port" 配置项是否合法
- int port = config["port"].get();
- if (port < 0 || port > 65535)
- {
- LOG_F(ERROR, "Invalid 'port' configuration value: {}", port);
- return false;
- }
- }
- catch (const std::exception &e)
- {
- LOG_F(ERROR, "Failed to parse config file: {}", e.what());
- return false;
- }
-
- return true;
-}
-
-bool ServerLauncher::check_modules(const std::string &modules_dir, const json &module_list)
-{
- if (!std::filesystem::exists(modules_dir))
- {
- DLOG_F(INFO, "Modules directory not found: {}", modules_dir);
- if (!std::filesystem::create_directory(modules_dir))
- {
- LOG_F(ERROR, "Failed to create modules directory: {}", modules_dir);
- return false;
- }
- }
-
- bool all_found = true;
- for (const auto &module : module_list)
- {
- std::string module_path = modules_dir + "/" + module.get();
- if (!std::filesystem::exists(module_path))
- {
- DLOG_F(ERROR, "Required module not found: {}", module_path);
- all_found = false;
- }
- }
-
- return all_found;
-}
-
-void ServerLauncher::start_server()
-{
- DLOG_F(INFO, "Starting server...");
-
- // 执行启动服务器的命令
- const std::string cmd = _config["server_command"];
-
- _server_process = std::shared_ptr(_popen(cmd.c_str(), "r"), [](FILE *f)
- { if (f) { _pclose(f); } });
-
- if (!_server_process)
- {
- DLOG_F(ERROR, "Failed to execute server command: {}", cmd);
- throw std::runtime_error("Failed to execute server command: " + cmd);
- }
- else
- {
- DLOG_F(INFO, "Server process started with command: {}", cmd);
- std::cout << "Server process started with command: " << cmd << std::endl;
- }
-
- // 创建一个线程来等待服务器启动
- _server_thread = std::jthread([&]
- {
- std::unique_lock lock(_server_mutex);
-
- while (!_stop_requested) {
- // 在条件变量上等待,直到服务器启动
- _server_cv.wait(lock, [&] { return _server_running || _stop_requested; });
- }
-
- // 如果请求停止服务器,则发送停止命令给服务器
- if (_stop_requested) {
- fprintf(_server_process.get(), "%c", _config["stop_command"]);
- fflush(_server_process.get());
- DLOG_F(INFO,"Stop command sent to server process.");
- } });
-
- DLOG_F(INFO, "Server started.");
-}
-
-void ServerLauncher::stop_server()
-{
- DLOG_F(INFO, "Stopping server...");
-
- // 发送停止命令给服务器
- fprintf(_server_process.get(), "%c", _config["stop_command"]);
- fflush(_server_process.get());
-
- DLOG_F(INFO, "Stop command sent to server process.");
-}
-
-void ServerLauncher::wait_for_server_to_exit()
-{
- // 等待服务器退出并获取返回值
- int status = -1;
- _server_thread.join();
- _server_process.reset();
- _server_running = false;
-}
-
-void ServerLauncher::read_server_output()
-{
- // 定义正则表达式模板,匹配错误信息
- std::regex error_regex("ERROR: \\[(\\S+)\\] (.*)");
-
- // 创建一个线程来读取服务器输出
- std::thread read_thread([&]
- {
- char buffer[1024];
- while (fgets(buffer, sizeof(buffer), _server_process.get())) {
- std::cout << buffer;
-
- // 判断输出中是否包含错误信息
- std::string line(buffer);
- std::smatch match;
-
- if (std::regex_search(line, match, error_regex)) {
- // 匹配成功,提取错误类型和错误消息
- std::string error_type = match[1].str();
- std::string error_message = match[2].str();
-
- // 根据错误类型处理错误
- if (error_type == "CRITICAL") {
- // 生成冲突日志
- //Lithium::CrashReport::saveCrashLog(error_message);
- }
- else if (error_type == "WARNING") {
- }
-
- // 读取结束,设置服务器运行标志为 false
- _server_running = false;
-
- // 唤醒等待服务器退出的条件变量
- _server_cv.notify_all();
- return;
- }
- }
-
- // 读取结束,设置服务器运行标志为 false
- _server_running = false;
-
- // 唤醒等待服务器退出的条件变量
- _server_cv.notify_all(); });
-
- // 启动成功,设置服务器运行标志为 true
- _server_running = true;
-
- // 让分离线程自行运行,不阻塞 run() 函数
- read_thread.detach();
-}
-
-bool ServerLauncher::calculate_sha256(const std::string &filename, std::string &sha256_val)
-{
- // 打开文件
- std::ifstream file(filename, std::ios::binary);
- if (!file)
- {
- return false;
- }
-
- // 计算 SHA256 值
- EVP_MD_CTX *mdctx = EVP_MD_CTX_new();
- EVP_DigestInit_ex(mdctx, EVP_sha256(), NULL);
-
- char buffer[1024];
- while (file.read(buffer, sizeof(buffer)))
- {
- EVP_DigestUpdate(mdctx, buffer, sizeof(buffer));
- }
-
- if (file.gcount() > 0)
- {
- EVP_DigestUpdate(mdctx, buffer, file.gcount());
- }
-
- unsigned char hash[EVP_MAX_MD_SIZE];
- unsigned int hash_len = 0;
- EVP_DigestFinal_ex(mdctx, hash, &hash_len);
- EVP_MD_CTX_free(mdctx);
-
- // 转换为十六进制字符串
- sha256_val.clear();
- for (unsigned int i = 0; i < hash_len; ++i)
- {
- char hex_str[3];
- sprintf(hex_str, "%02x", hash[i]);
- sha256_val += hex_str;
- }
-
- return true;
-}
-
-void setupLogFile()
-{
- std::filesystem::path logsFolder = std::filesystem::current_path() / "logs";
- if (!std::filesystem::exists(logsFolder))
- {
- std::filesystem::create_directory(logsFolder);
- }
- auto now = std::chrono::system_clock::now();
- auto now_time_t = std::chrono::system_clock::to_time_t(now);
- std::tm *local_time = std::localtime(&now_time_t);
- char filename[100];
- std::strftime(filename, sizeof(filename), "%Y%m%d_%H%M%S.log", local_time);
- std::filesystem::path logFilePath = logsFolder / filename;
- loguru::add_file(logFilePath.string().c_str(), loguru::Append, loguru::Verbosity_MAX);
-
- loguru::set_fatal_handler([](const loguru::Message &message)
- { Lithium::CrashReport::saveCrashLog(std::string(message.prefix) + message.message); });
-}
-
-int main(int argc, char *argv[])
-{
- std::vector args(argv + 1, argv + argc);
-
- if (args.size() < 2)
- {
- LOG_F(INFO, "Error: Missing arguments.");
- LOG_F(INFO, "Usage: launcher ");
- return 1;
- }
-
- try
- {
- ServerLauncher launcher(args[0], args[1]);
- launcher.run();
-
- if (launcher.is_running())
- {
- launcher.stop();
- }
- }
- catch (const std::exception &e)
- {
- // 输出错误信息并返回
- LOG_F(ERROR, "Error: {}", e.what());
- return 1;
- }
-
- return 0;
-}
\ No newline at end of file
diff --git a/launcher/launcher.glade b/launcher/launcher.glade
deleted file mode 100644
index ffd0f7f7..00000000
--- a/launcher/launcher.glade
+++ /dev/null
@@ -1,181 +0,0 @@
-
-
-
-
-
-
-
-
-
-
- False
- True
-
-
- True
- False
- 介绍页
-
-
- progress
- False
-
-
-
-
- True
- False
- 内容页
-
-
- False
-
-
-
-
- True
- False
- 确认页
-
-
- confirm
- False
-
-
-
-
- False
- end
- 6
- 6
- 6
- 6
- 6
- 6
- 6
-
-
- intro
- False
-
-
-
-
- False
- Hello ElementAstro Launcher
- True
- assets\atom.png
-
-
- True
- False
- 0
- none
-
-
- True
- False
- 12
-
-
-
-
-
-
-
- True
- False
- __glade_unnamed_14
-
-
-
-
-
-
- False
- About
- True
- help-about
- dialog
- Hello ElementAstro Launcher
- 1.0.0
- https://github.com/ElementAstro
- Official Github Repository
- Max Qian
- Max Qian
- Max Qian
- Max Qian
- assets\atom.png
- gpl-3-0
-
-
- False
- vertical
- 2
-
-
- False
- end
-
-
- False
- False
- 0
-
-
-
-
-
-
-
-
-
- False
- Lithium Script Editor
- True
- True
- edit-find-replace
- dialog
- lithium
- Lithium Script Editor
-
-
- False
- vertical
- 2
-
-
- False
- end
-
-
- False
- False
- 0
-
-
-
-
-
-
diff --git a/launcher/launcher.glade~ b/launcher/launcher.glade~
deleted file mode 100644
index 9fbbf465..00000000
--- a/launcher/launcher.glade~
+++ /dev/null
@@ -1,167 +0,0 @@
-
-
-
-
-
-
-
-
-
-
- False
- True
-
-
- True
- False
- 介绍页
-
-
- progress
- False
-
-
-
-
- True
- False
- 内容页
-
-
- False
-
-
-
-
- True
- False
- 确认页
-
-
- confirm
- False
-
-
-
-
- False
- end
- 6
- 6
- 6
- 6
- 6
- 6
- 6
-
-
- intro
- False
-
-
-
-
- False
- Hello ElementAstro Launcher
- True
- assets\atom.png
-
-
-
-
-
- False
- About
- True
- help-about
- dialog
- Hello ElementAstro Launcher
- 1.0.0
- https://github.com/ElementAstro
- Official Github Repository
- Max Qian
- Max Qian
- Max Qian
- Max Qian
- assets\atom.png
- gpl-3-0
-
-
- False
- vertical
- 2
-
-
- False
- end
-
-
- False
- False
- 0
-
-
-
-
-
-
-
-
-
- False
- Lithium Script Editor
- True
- True
- edit-find-replace
- dialog
- lithium
- Lithium Script Editor
-
-
- False
- vertical
- 2
-
-
- False
- end
-
-
- False
- False
- 0
-
-
-
-
-
-
diff --git a/launcher/launcher.hpp b/launcher/launcher.hpp
deleted file mode 100644
index 18a1989d..00000000
--- a/launcher/launcher.hpp
+++ /dev/null
@@ -1,292 +0,0 @@
-/*
- * launcher.hpp
- *
- * Copyright (C) 2023-2024 Max Qian
- */
-
-/*************************************************
-
-Date: 2023-3-29
-
-Description: Lithium Server Launcher
-
-**************************************************/
-
-#pragma once
-
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include "atom/web/httplib.h"
-#include "atom/type/json.hpp"
-
-using json = nlohmann::json;
-namespace fs = std::filesystem;
-
-/**
- * @brief 一个服务器启动器类,用于管理和启动服务器,包括资源下载、依赖检查、配置文件解析等操作。
- */
-class ServerLauncher
-{
-public:
- /**
- * @brief 构造函数,初始化配置文件路径和日志文件路径,但不进行任何初始化操作。
- * @param config_file_path 服务器配置文件路径
- * @param DLOG_File_path 服务器日志文件路径
- */
- ServerLauncher(const std::string &config_file_path, const std::string &DLOG_File_path);
-
- /**
- * @brief 启动服务器并开始监听连接请求。
- *
- * 该函数首先检查运行所需的资源是否已下载和依赖项是否已安装,然后加载配置文件,
- * 启动服务器进程并开始监听连接请求。启动过程中,服务器输出将定向至日志文件中。
- *
- * 如果启动失败,则会发送警告邮件,并打印相关错误并退出程序。
- *
- * 在 start_server() 函数内部,会创建一个新的线程来启动服务器进程,并等待子进程结束。
- */
- void run();
-
- /**
- * @brief 停止服务器进程和启动器。
- *
- * 停止服务器进程前会发送停止信号以确保服务器进程优雅地退出。
- */
- void stop();
-
- /**
- * @brief 检查服务器是否正在运行。
- * @return 如果服务器正在运行,则返回 true,否则返回 false。
- */
- bool is_running() const;
-
- /**
- * @brief 重定向服务器的标准输出和标准错误输出到日志文件中。
- *
- * 该函数在服务器启动后应当被调用,以便将服务器输出记录到日志文件中。
- *
- * @param DLOG_File_path 日志文件路径。
- */
- void redirect_stdout_stderr(const std::string &DLOG_File_path);
-
- /**
- * @brief 计算指定文件的 SHA-256 哈希值。
- * @param filename 要计算哈希值的文件名。
- * @param sha256_val 输出参数,保存计算出的哈希值。
- * @return 如果文件存在并成功计算出哈希值,则返回 true,否则返回 false。
- */
- bool calculate_sha256(const std::string &filename, std::string &sha256_val);
-
-private:
- /**
- * @brief 加载配置文件,并检查其中的参数是否合法。
- *
- * 该函数在 ServerLauncher::run() 函数中被调用,用于加载服务器的配置文件,
- * 并检查其中的参数是否设置正确。如果参数检查失败,则会打印相关错误信息并退出程序。
- */
- void load_config();
-
- /**
- * @brief 检查启动器所需的资源是否已下载。
- *
- * 该函数在 ServerLauncher::run() 函数中被调用,用于检查启动器所需的资源是否已下载。
- * 如果没有下载,则会提示用户开始下载,或自动下载并安装缺失的资源。
- *
- * @return 如果所有必要的资源均已下载,则返回 true,否则返回 false。
- */
- bool check_resources();
-
- /**
- * @brief 下载启动器所需的资源。
- *
- * 该函数在 ServerLauncher::check_resources() 函数中被调用,用于下载启动器所需的资源,
- * 包括服务器程序、配置文件、数据文件等。下载完成后,将自动解压和安装这些资源。
- */
- void download_resources();
-
- /**
- * @brief 检查启动器所需的依赖项是否已安装。
- *
- * 该函数在 ServerLauncher::run() 函数中被调用,用于检查启动器所需的依赖项是否已安装。
- * 如果某些依赖项未安装,则会提示用户开始安装,或自动下载并安装这些依赖项。
- *
- * @return 如果所有必要的依赖项均已安装,则返回 true,否则返回 false。
- */
- bool check_dependencies();
-
- /**
- * @brief 检查服务器配置文件是否合法。
- * @param config_file 支持 JSON 或 YAML 格式的配置文件路径。
- * @return 如果配置文件有效,则返回 true,否则返回 false。
- */
- bool check_config_file(const std::string &config_file);
-
- /**
- * @brief 检查已安装的模组是否符合要求。
- *
- * 该函数在 ServerLauncher::run() 函数中被调用,用于检查已安装的模组是否符合要求,
- * 如果存在不支持或过时的模组,则会提示用户更新或删除这些模组。
- *
- * @param modules_dir 模组所在的目录路径。
- * @param module_list 从配置文件中读取的模组列表。
- * @return 如果所有必要的模组均已安装并且都是最新版,则返回 true,否则返回 false。
- */
- bool check_modules(const std::string &modules_dir, const json &module_list);
-
- /**
- * @brief 启动服务器进程并等待其结束。
- *
- * 该函数在 ServerLauncher::run() 函数中被调用,用于启动服务器进程并等待其结束。
- * 在启动服务器进程前,将检查服务器程序是否存在以及配置文件是否设置正确。
- * 若服务器程序不存在或配置文件设置错误,则会输出错误信息并退出程序。
- *
- * 在函数内部,会创建一个新的线程来启动服务器进程,并等待子进程结束。如果服务器进程
- * 提前结束,则会发送警告邮件给管理员,并打印相关错误信息并退出程序。
- */
- void start_server();
-
- /**
- * @brief 发送停止信号给服务器进程,并等待其优雅地退出。
- *
- * 该函数在 ServerLauncher::stop() 函数中被调用,用于发送停止信号给服务器进程,
- * 并等待其在一段时间内优雅地退出。如果服务器未能在规定时间内退出,则会发送
- * kill 信号以强制结束服务器进程。
- */
- void stop_server();
-
- /**
- * @brief 等待服务器进程结束。
- *
- * 该函数在 ServerLauncher::stop_server() 和 ServerLauncher::start_server()
- * 函数中都可能被调用,用于等待服务器进程在子线程中结束。
- *
- * 通过使用条件变量和互斥锁来控制等待流程,避免了死锁和忙等情况的出现。
- */
- void wait_for_server_to_exit();
-
- /**
- * @brief 读取服务器输出并将其记录到日志文件中。
- *
- * 该函数在 ServerLauncher::start_server() 中被调用,用于读取服务器进程的标准输出
- * 并将其记录到日志文件中。该函数在一个独立的线程中运行,以避免阻塞其他操作。
- */
- void read_server_output();
-
- std::string _config_file_path; ///< 配置文件路径
- std::string _DLOG_File_path; ///< 日志文件路径
- json _config; ///< 服务器配置文件
- std::atomic_bool _stop_requested = false; ///< 是否请求停止服务器的标志
- std::atomic_bool _server_running = false; ///< 是否正在运行服务器的标志
- std::jthread _server_thread; ///< 执行服务器进程的线程对象
- std::mutex _server_mutex; ///< 控制等待服务器进程结束的互斥锁
- std::condition_variable _server_cv; ///< 控制等待服务器进程结束的条件变量
- std::shared_ptr _server_process; ///< 指向服务器进程标准输出流的指针
-};
-
-/**
- * @brief 线程池
- */
-class ThreadPool {
-public:
- /**
- * @brief 构造函数,初始化线程池大小和停止标志位。
- *
- * 构造函数会创建 n_threads 个线程,并等待来自任务队列的任务分配。
- *
- * @param n_threads 线程池大小
- */
- ThreadPool(std::size_t n_threads)
- : stop(false)
- {
- for (std::size_t i = 0; i < n_threads; ++i)
- {
- threads.emplace_back([this]
- {
- for (;;) {
- std::function task;
- {
- std::unique_lock lock(queue_mutex);
- condition.wait(lock, [this] { return stop || !tasks.empty(); });
- if (stop && tasks.empty()) {
- return;
- }
- task = std::move(tasks.front());
- tasks.pop();
- }
- task();
- } });
- }
- }
-
- /**
- * @brief 析构函数,销毁所有线程并退出。
- *
- * 析构函数会向任务队列中插入空任务,并等待所有线程完成该任务并退出。
- */
- ~ThreadPool()
- {
- {
- std::unique_lock lock(queue_mutex);
- stop = true;
- }
- condition.notify_all();
- for (std::thread &thread : threads)
- {
- thread.join();
- }
- }
-
- /**
- * @brief 将指定任务添加到任务队列中,并返回该任务的 future 对象。
- *
- * 该函数用于将函数 f 和其参数 args 添加到任务队列中等待执行,并返回一
- * 个 std::future 对象,以便查询任务完成情况。当任务队列已满或线程池被
- * 停止时,将会抛出 std::runtime_error 异常。
- *
- * @tparam F 函数类型
- * @tparam Args 参数类型
- * @param f 要执行的函数对象
- * @param args 函数参数
- * @return 返回一个 std::future 对象,用于查询任务完成情况
- */
- template
- auto enqueue(F &&f, Args &&...args) -> std::future::type>
- {
- using return_type = typename std::result_of::type;
-
- auto task = std::make_shared>(
- std::bind(std::forward(f), std::forward(args)...));
-
- std::future res = task->get_future();
- {
- std::unique_lock lock(queue_mutex);
-
- if (stop)
- {
- throw std::runtime_error("enqueue on stopped ThreadPool");
- }
-
- tasks.emplace([task]
- { (*task)(); });
- }
- condition.notify_one();
- return res;
- }
-
-private:
- std::vector threads; ///< 线程池中的线程列表
- std::queue> tasks; ///< 任务队列
-
- std::mutex queue_mutex; ///< 任务队列的互斥锁
- std::condition_variable condition; ///< 任务队列的条件变量
- bool stop; ///< 停止标志位
-};
diff --git a/launcher/launcher.json b/launcher/launcher.json
deleted file mode 100644
index de0b14a6..00000000
--- a/launcher/launcher.json
+++ /dev/null
@@ -1,10 +0,0 @@
-{
- "server_command": "./server",
- "stop_command": "q\n",
- "resources": [
- "data.json",
- "config.ini"
- ],
- "resource_server": "http://example.com"
- }
-
\ No newline at end of file
diff --git a/locale/lithium.pot b/locale/lithium.pot
deleted file mode 100644
index 6876ffa8..00000000
--- a/locale/lithium.pot
+++ /dev/null
@@ -1,23 +0,0 @@
-# SOME DESCRIPTIVE TITLE.
-# Copyright (C) YEAR Max Qian
-# This file is distributed under the same license as the package.
-# FIRST AUTHOR , YEAR.
-#
-#, fuzzy
-msgid ""
-msgstr ""
-"Project-Id-Version: \n"
-"Report-Msgid-Bugs-To: astro_air@126.com\n"
-"POT-Creation-Date: 2024-02-06 17:50+0800\n"
-"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"Last-Translator: FULL NAME \n"
-"Language-Team: LANGUAGE \n"
-"Language: \n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=CHARSET\n"
-"Content-Transfer-Encoding: 8bit\n"
-
-#: E:/msys64/home/Lithium/src/LithiumApp.cpp:551
-#, c++-format
-msgid "Dispatched command {} with result: {}"
-msgstr ""
diff --git a/locale/po/en_US.UTF-8/lithium.po b/locale/po/en_US.UTF-8/lithium.po
deleted file mode 100644
index 629b9775..00000000
--- a/locale/po/en_US.UTF-8/lithium.po
+++ /dev/null
@@ -1,4 +0,0 @@
-#: E:/msys64/home/Lithium/src/LithiumApp.cpp:551
-#, c++-format
-msgid "Dispatched command {} with result: {}"
-msgstr ""
diff --git a/locale/po/es/lithium.po b/locale/po/es/lithium.po
deleted file mode 100644
index 94c1bb2c..00000000
--- a/locale/po/es/lithium.po
+++ /dev/null
@@ -1,46 +0,0 @@
-#: E:/msys64/home/Qrm/OpenAPT/src/App.cpp:232
-msgid "port the server running on"
-msgstr ""
-
-#: E:/msys64/home/Qrm/OpenAPT/src/App.cpp:233
-msgid "host the server running on"
-msgstr ""
-
-#: E:/msys64/home/Qrm/OpenAPT/src/App.cpp:234
-msgid "path to the config file"
-msgstr ""
-
-#: E:/msys64/home/Qrm/OpenAPT/src/App.cpp:235
-msgid "path to the modules directory"
-msgstr ""
-
-#: E:/msys64/home/Qrm/OpenAPT/src/App.cpp:236
-msgid "web panel"
-msgstr ""
-
-#: E:/msys64/home/Qrm/OpenAPT/src/App.cpp:237
-msgid "path to log file"
-msgstr ""
-
-#: E:/msys64/home/Qrm/OpenAPT/src/App.cpp:239
-msgid "Lithium Command Line Interface:"
-msgstr ""
-
-#: E:/msys64/home/Qrm/OpenAPT/src/App.cpp:240
-msgid "End."
-msgstr ""
-
-#: E:/msys64/home/Qrm/OpenAPT/src/App.cpp:248
-#, c-format
-msgid "Failed to parser command line : %s"
-msgstr ""
-
-#: E:/msys64/home/Qrm/OpenAPT/src/App.cpp:269
-#, c-format
-msgid "Command line server port : %d"
-msgstr ""
-
-#: E:/msys64/home/Qrm/OpenAPT/src/App.cpp:275
-#, c-format
-msgid "Set server port to %d"
-msgstr ""
diff --git a/locale/po/fr_FR/lithium.po b/locale/po/fr_FR/lithium.po
deleted file mode 100644
index 94c1bb2c..00000000
--- a/locale/po/fr_FR/lithium.po
+++ /dev/null
@@ -1,46 +0,0 @@
-#: E:/msys64/home/Qrm/OpenAPT/src/App.cpp:232
-msgid "port the server running on"
-msgstr ""
-
-#: E:/msys64/home/Qrm/OpenAPT/src/App.cpp:233
-msgid "host the server running on"
-msgstr ""
-
-#: E:/msys64/home/Qrm/OpenAPT/src/App.cpp:234
-msgid "path to the config file"
-msgstr ""
-
-#: E:/msys64/home/Qrm/OpenAPT/src/App.cpp:235
-msgid "path to the modules directory"
-msgstr ""
-
-#: E:/msys64/home/Qrm/OpenAPT/src/App.cpp:236
-msgid "web panel"
-msgstr ""
-
-#: E:/msys64/home/Qrm/OpenAPT/src/App.cpp:237
-msgid "path to log file"
-msgstr ""
-
-#: E:/msys64/home/Qrm/OpenAPT/src/App.cpp:239
-msgid "Lithium Command Line Interface:"
-msgstr ""
-
-#: E:/msys64/home/Qrm/OpenAPT/src/App.cpp:240
-msgid "End."
-msgstr ""
-
-#: E:/msys64/home/Qrm/OpenAPT/src/App.cpp:248
-#, c-format
-msgid "Failed to parser command line : %s"
-msgstr ""
-
-#: E:/msys64/home/Qrm/OpenAPT/src/App.cpp:269
-#, c-format
-msgid "Command line server port : %d"
-msgstr ""
-
-#: E:/msys64/home/Qrm/OpenAPT/src/App.cpp:275
-#, c-format
-msgid "Set server port to %d"
-msgstr ""
diff --git a/locale/po/zh-CN.UTF-8/lithium.po b/locale/po/zh-CN.UTF-8/lithium.po
deleted file mode 100644
index bdf1643a..00000000
--- a/locale/po/zh-CN.UTF-8/lithium.po
+++ /dev/null
@@ -1,258 +0,0 @@
-# Language zh-CN translations for Lithium package.
-# Copyright (C) 2023 Max Qian
-# This file is distributed under the same license as the Lithium package.
-# , 2023.
-#
-msgid ""
-msgstr ""
-"Project-Id-Version: Lithium \n"
-"Report-Msgid-Bugs-To: astro_air@126.com\n"
-"POT-Creation-Date: 2023-10-07 12:34+0000\n"
-"PO-Revision-Date: 2023-10-07 12:36+0000\n"
-"Last-Translator: \n"
-"Language-Team: Language zh-CN\n"
-"Language: zh-CN\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=ASCII\n"
-"Content-Transfer-Encoding: 8bit\n"
-
-#: /workspaces/Lithium/src/modules/thread/thread.cpp:63
-msgid "Failed to destroy ThreadManager: {}"
-msgstr ""
-
-#: /workspaces/Lithium/src/modules/thread/thread.cpp:82
-msgid "Thread manager has stopped, cannot add new thread"
-msgstr ""
-
-#: /workspaces/Lithium/src/modules/thread/thread.cpp:100
-#: /workspaces/Lithium/src/modules/thread/thread.cpp:122
-msgid "Unhandled exception in thread: {}"
-msgstr ""
-
-#: /workspaces/Lithium/src/modules/thread/thread.cpp:128
-msgid "Added thread: {}"
-msgstr ""
-
-#: /workspaces/Lithium/src/modules/thread/thread.cpp:133
-msgid "Failed to add thread {}: {}"
-msgstr ""
-
-#: /workspaces/Lithium/src/modules/thread/thread.cpp:149
-msgid "All threads joined"
-msgstr ""
-
-#: /workspaces/Lithium/src/modules/thread/thread.cpp:153
-msgid "Failed to join all threads: {}"
-msgstr ""
-
-#: /workspaces/Lithium/src/modules/thread/thread.cpp:163
-#: /workspaces/Lithium/src/modules/thread/thread.cpp:180
-#: /workspaces/Lithium/src/modules/thread/thread.cpp:194
-#: /workspaces/Lithium/src/modules/thread/thread.cpp:205
-msgid "Thread {} not found"
-msgstr ""
-
-#: /workspaces/Lithium/src/modules/thread/thread.cpp:172
-msgid "Thread {} joined"
-msgstr ""
-
-#: /workspaces/Lithium/src/modules/thread/thread.cpp:184
-msgid "Failed to join thread {}: {}"
-msgstr ""
-
-#: /workspaces/Lithium/src/modules/thread/thread.cpp:210
-msgid "Failed to check if thread {} is running: {}"
-msgstr ""
-
-#: /workspaces/Lithium/src/modules/system/process.cpp:75
-msgid "Failed to create PowerShell process"
-msgstr ""
-
-#: /workspaces/Lithium/src/modules/system/process.cpp:85
-msgid "Running command: {}"
-msgstr ""
-
-#: /workspaces/Lithium/src/modules/system/process.cpp:98
-#: /workspaces/Lithium/src/modules/system/process.cpp:122
-#: /workspaces/Lithium/src/modules/system/process.cpp:143
-msgid "Failed to create process"
-msgstr ""
-
-#: /workspaces/Lithium/src/modules/system/process.cpp:108
-#: /workspaces/Lithium/src/modules/system/process.cpp:153
-msgid "Process created: {} (PID: {})"
-msgstr ""
-
-#: /workspaces/Lithium/src/modules/system/process.cpp:132
-msgid "Running script: {}"
-msgstr ""
-
-#: /workspaces/Lithium/src/modules/system/process.cpp:170
-#: /workspaces/Lithium/src/modules/system/process.cpp:182
-msgid "Process terminated: {} (PID: {})"
-msgstr ""
-
-#: /workspaces/Lithium/src/modules/system/process.cpp:174
-msgid "Failed to terminate process"
-msgstr ""
-
-#: /workspaces/Lithium/src/modules/system/process.cpp:190
-#: /workspaces/Lithium/src/modules/system/process.cpp:246
-msgid "Process not found"
-msgstr ""
-
-#: /workspaces/Lithium/src/modules/system/process.cpp:205
-msgid "Process not found by name: {}"
-msgstr ""
-
-#: /workspaces/Lithium/src/modules/system/process.cpp:212
-msgid "Currently running processes:"
-msgstr ""
-
-#: /workspaces/Lithium/src/modules/system/process.cpp:216
-msgid "{} (PID: {})"
-msgstr ""
-
-#: /workspaces/Lithium/src/modules/system/process.cpp:261
-msgid "Process completed: {} (PID: {})"
-msgstr ""
-
-#: /workspaces/Lithium/src/modules/system/process.cpp:265
-msgid "Failed to wait for process completion"
-msgstr ""
-
-#: /workspaces/Lithium/src/modules/system/process.cpp:271
-#, c-format
-msgid "Process completed: %s (PID: %d)"
-msgstr ""
-
-#: /workspaces/Lithium/src/modules/system/process.cpp:276
-msgid "All processes completed."
-msgstr ""
-
-#: /workspaces/Lithium/src/modules/system/process.cpp:287
-msgid "Failed to create process snapshot"
-msgstr ""
-
-#: /workspaces/Lithium/src/modules/system/process.cpp:328
-msgid "Failed to open /proc directory"
-msgstr ""
-
-#: /workspaces/Lithium/src/modules/system/process.cpp:357
-msgid "Failed to get process path"
-msgstr ""
-
-#: /workspaces/Lithium/src/modules/system/process.cpp:378
-msgid "Failed to get process info length"
-msgstr ""
-
-#: /workspaces/Lithium/src/modules/system/process.cpp:385
-msgid "Failed to allocate memory"
-msgstr ""
-
-#: /workspaces/Lithium/src/modules/system/process.cpp:391
-msgid "Failed to get process info"
-msgstr ""
-
-#: /workspaces/Lithium/src/modules/system/pid.cpp:94
-msgid "CreateToolhelp32Snapshot failed."
-msgstr ""
-
-#: /workspaces/Lithium/src/modules/system/pid.cpp:113
-msgid "Watching process with PID: {}"
-msgstr ""
-
-#: /workspaces/Lithium/src/modules/system/pid.cpp:124
-msgid "Process exited with code: {}"
-msgstr ""
-
-#: /workspaces/Lithium/src/modules/system/pid.cpp:133
-msgid "GetExitCodeProcess failed."
-msgstr ""
-
-#: /workspaces/Lithium/src/modules/system/pid.cpp:143
-msgid "OpenProcess failed."
-msgstr ""
-
-#: /workspaces/Lithium/src/modules/system/pid.cpp:170
-msgid "Process exited with status: {}"
-msgstr ""
-
-#: /workspaces/Lithium/src/modules/system/pid.cpp:179
-msgid "Process terminated by signal: {}"
-msgstr ""
-
-#: /workspaces/Lithium/src/App.cpp:223
-msgid "port the server running on"
-msgstr ""
-
-#: /workspaces/Lithium/src/App.cpp:224
-msgid "host the server running on"
-msgstr ""
-
-#: /workspaces/Lithium/src/App.cpp:225
-msgid "path to the config file"
-msgstr ""
-
-#: /workspaces/Lithium/src/App.cpp:226
-msgid "path to the modules directory"
-msgstr ""
-
-#: /workspaces/Lithium/src/App.cpp:227
-msgid "web panel"
-msgstr ""
-
-#: /workspaces/Lithium/src/App.cpp:228
-msgid "path to log file"
-msgstr ""
-
-#: /workspaces/Lithium/src/App.cpp:230
-msgid "Lithium Command Line Interface:"
-msgstr ""
-
-#: /workspaces/Lithium/src/App.cpp:231
-msgid "End."
-msgstr ""
-
-#: /workspaces/Lithium/src/App.cpp:239
-#, c-format
-msgid "Failed to parser command line : %s"
-msgstr ""
-
-#: /workspaces/Lithium/src/App.cpp:260
-#, c-format
-msgid "Command line server port : %d"
-msgstr ""
-
-#: /workspaces/Lithium/src/App.cpp:266
-#, c-format
-msgid "Set server port to %d"
-msgstr ""
-
-#: /workspaces/Lithium/src/LithiumApp.cpp:67
-msgid "Failed to load Lithium App , error : {}"
-msgstr ""
-
-#: /workspaces/Lithium/src/LithiumApp.cpp:79
-msgid "Get config value: {}"
-msgstr ""
-
-#: /workspaces/Lithium/src/LithiumApp.cpp:85
-msgid "Set {} to {}"
-msgstr ""
-
-#: /workspaces/Lithium/src/LithiumApp.cpp:272
-msgid "Failed to run chai command : {}"
-msgstr ""
-
-#: /workspaces/Lithium/src/LithiumApp.cpp:290
-msgid "Failed to run chai multi command {}"
-msgstr ""
-
-#: /workspaces/Lithium/src/LithiumApp.cpp:303
-msgid "Failed to load chaiscript file {}"
-msgstr ""
-
-#: /workspaces/Lithium/src/LithiumApp.cpp:316
-msgid "Failed to run chai script {}"
-msgstr ""
diff --git a/locale/po/zh-CN/lithium.po b/locale/po/zh-CN/lithium.po
deleted file mode 100644
index c863404b..00000000
--- a/locale/po/zh-CN/lithium.po
+++ /dev/null
@@ -1,258 +0,0 @@
-# Language zh-CN translations for Lithium package.
-# Copyright (C) 2023 Max Qian
-# This file is distributed under the same license as the Lithium package.
-# , 2023.
-#
-msgid ""
-msgstr ""
-"Project-Id-Version: Lithium \n"
-"Report-Msgid-Bugs-To: astro_air@126.com\n"
-"POT-Creation-Date: 2023-10-07 12:34+0000\n"
-"PO-Revision-Date: 2023-10-07 12:37+0000\n"
-"Last-Translator: \n"
-"Language-Team: Language zh-CN\n"
-"Language: zh-CN\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=ASCII\n"
-"Content-Transfer-Encoding: 8bit\n"
-
-#: /workspaces/Lithium/src/modules/thread/thread.cpp:63
-msgid "Failed to destroy ThreadManager: {}"
-msgstr ""
-
-#: /workspaces/Lithium/src/modules/thread/thread.cpp:82
-msgid "Thread manager has stopped, cannot add new thread"
-msgstr ""
-
-#: /workspaces/Lithium/src/modules/thread/thread.cpp:100
-#: /workspaces/Lithium/src/modules/thread/thread.cpp:122
-msgid "Unhandled exception in thread: {}"
-msgstr ""
-
-#: /workspaces/Lithium/src/modules/thread/thread.cpp:128
-msgid "Added thread: {}"
-msgstr ""
-
-#: /workspaces/Lithium/src/modules/thread/thread.cpp:133
-msgid "Failed to add thread {}: {}"
-msgstr ""
-
-#: /workspaces/Lithium/src/modules/thread/thread.cpp:149
-msgid "All threads joined"
-msgstr ""
-
-#: /workspaces/Lithium/src/modules/thread/thread.cpp:153
-msgid "Failed to join all threads: {}"
-msgstr ""
-
-#: /workspaces/Lithium/src/modules/thread/thread.cpp:163
-#: /workspaces/Lithium/src/modules/thread/thread.cpp:180
-#: /workspaces/Lithium/src/modules/thread/thread.cpp:194
-#: /workspaces/Lithium/src/modules/thread/thread.cpp:205
-msgid "Thread {} not found"
-msgstr ""
-
-#: /workspaces/Lithium/src/modules/thread/thread.cpp:172
-msgid "Thread {} joined"
-msgstr ""
-
-#: /workspaces/Lithium/src/modules/thread/thread.cpp:184
-msgid "Failed to join thread {}: {}"
-msgstr ""
-
-#: /workspaces/Lithium/src/modules/thread/thread.cpp:210
-msgid "Failed to check if thread {} is running: {}"
-msgstr ""
-
-#: /workspaces/Lithium/src/modules/system/process.cpp:75
-msgid "Failed to create PowerShell process"
-msgstr ""
-
-#: /workspaces/Lithium/src/modules/system/process.cpp:85
-msgid "Running command: {}"
-msgstr ""
-
-#: /workspaces/Lithium/src/modules/system/process.cpp:98
-#: /workspaces/Lithium/src/modules/system/process.cpp:122
-#: /workspaces/Lithium/src/modules/system/process.cpp:143
-msgid "Failed to create process"
-msgstr ""
-
-#: /workspaces/Lithium/src/modules/system/process.cpp:108
-#: /workspaces/Lithium/src/modules/system/process.cpp:153
-msgid "Process created: {} (PID: {})"
-msgstr ""
-
-#: /workspaces/Lithium/src/modules/system/process.cpp:132
-msgid "Running script: {}"
-msgstr ""
-
-#: /workspaces/Lithium/src/modules/system/process.cpp:170
-#: /workspaces/Lithium/src/modules/system/process.cpp:182
-msgid "Process terminated: {} (PID: {})"
-msgstr ""
-
-#: /workspaces/Lithium/src/modules/system/process.cpp:174
-msgid "Failed to terminate process"
-msgstr ""
-
-#: /workspaces/Lithium/src/modules/system/process.cpp:190
-#: /workspaces/Lithium/src/modules/system/process.cpp:246
-msgid "Process not found"
-msgstr ""
-
-#: /workspaces/Lithium/src/modules/system/process.cpp:205
-msgid "Process not found by name: {}"
-msgstr ""
-
-#: /workspaces/Lithium/src/modules/system/process.cpp:212
-msgid "Currently running processes:"
-msgstr ""
-
-#: /workspaces/Lithium/src/modules/system/process.cpp:216
-msgid "{} (PID: {})"
-msgstr ""
-
-#: /workspaces/Lithium/src/modules/system/process.cpp:261
-msgid "Process completed: {} (PID: {})"
-msgstr ""
-
-#: /workspaces/Lithium/src/modules/system/process.cpp:265
-msgid "Failed to wait for process completion"
-msgstr ""
-
-#: /workspaces/Lithium/src/modules/system/process.cpp:271
-#, c-format
-msgid "Process completed: %s (PID: %d)"
-msgstr ""
-
-#: /workspaces/Lithium/src/modules/system/process.cpp:276
-msgid "All processes completed."
-msgstr ""
-
-#: /workspaces/Lithium/src/modules/system/process.cpp:287
-msgid "Failed to create process snapshot"
-msgstr ""
-
-#: /workspaces/Lithium/src/modules/system/process.cpp:328
-msgid "Failed to open /proc directory"
-msgstr ""
-
-#: /workspaces/Lithium/src/modules/system/process.cpp:357
-msgid "Failed to get process path"
-msgstr ""
-
-#: /workspaces/Lithium/src/modules/system/process.cpp:378
-msgid "Failed to get process info length"
-msgstr ""
-
-#: /workspaces/Lithium/src/modules/system/process.cpp:385
-msgid "Failed to allocate memory"
-msgstr ""
-
-#: /workspaces/Lithium/src/modules/system/process.cpp:391
-msgid "Failed to get process info"
-msgstr ""
-
-#: /workspaces/Lithium/src/modules/system/pid.cpp:94
-msgid "CreateToolhelp32Snapshot failed."
-msgstr ""
-
-#: /workspaces/Lithium/src/modules/system/pid.cpp:113
-msgid "Watching process with PID: {}"
-msgstr ""
-
-#: /workspaces/Lithium/src/modules/system/pid.cpp:124
-msgid "Process exited with code: {}"
-msgstr ""
-
-#: /workspaces/Lithium/src/modules/system/pid.cpp:133
-msgid "GetExitCodeProcess failed."
-msgstr ""
-
-#: /workspaces/Lithium/src/modules/system/pid.cpp:143
-msgid "OpenProcess failed."
-msgstr ""
-
-#: /workspaces/Lithium/src/modules/system/pid.cpp:170
-msgid "Process exited with status: {}"
-msgstr ""
-
-#: /workspaces/Lithium/src/modules/system/pid.cpp:179
-msgid "Process terminated by signal: {}"
-msgstr ""
-
-#: /workspaces/Lithium/src/App.cpp:223
-msgid "port the server running on"
-msgstr ""
-
-#: /workspaces/Lithium/src/App.cpp:224
-msgid "host the server running on"
-msgstr ""
-
-#: /workspaces/Lithium/src/App.cpp:225
-msgid "path to the config file"
-msgstr ""
-
-#: /workspaces/Lithium/src/App.cpp:226
-msgid "path to the modules directory"
-msgstr ""
-
-#: /workspaces/Lithium/src/App.cpp:227
-msgid "web panel"
-msgstr ""
-
-#: /workspaces/Lithium/src/App.cpp:228
-msgid "path to log file"
-msgstr ""
-
-#: /workspaces/Lithium/src/App.cpp:230
-msgid "Lithium Command Line Interface:"
-msgstr ""
-
-#: /workspaces/Lithium/src/App.cpp:231
-msgid "End."
-msgstr ""
-
-#: /workspaces/Lithium/src/App.cpp:239
-#, c-format
-msgid "Failed to parser command line : %s"
-msgstr ""
-
-#: /workspaces/Lithium/src/App.cpp:260
-#, c-format
-msgid "Command line server port : %d"
-msgstr ""
-
-#: /workspaces/Lithium/src/App.cpp:266
-#, c-format
-msgid "Set server port to %d"
-msgstr ""
-
-#: /workspaces/Lithium/src/LithiumApp.cpp:67
-msgid "Failed to load Lithium App , error : {}"
-msgstr ""
-
-#: /workspaces/Lithium/src/LithiumApp.cpp:79
-msgid "Get config value: {}"
-msgstr ""
-
-#: /workspaces/Lithium/src/LithiumApp.cpp:85
-msgid "Set {} to {}"
-msgstr ""
-
-#: /workspaces/Lithium/src/LithiumApp.cpp:272
-msgid "Failed to run chai command : {}"
-msgstr ""
-
-#: /workspaces/Lithium/src/LithiumApp.cpp:290
-msgid "Failed to run chai multi command {}"
-msgstr ""
-
-#: /workspaces/Lithium/src/LithiumApp.cpp:303
-msgid "Failed to load chaiscript file {}"
-msgstr ""
-
-#: /workspaces/Lithium/src/LithiumApp.cpp:316
-msgid "Failed to run chai script {}"
-msgstr ""
diff --git a/meson.build b/meson.build
new file mode 100644
index 00000000..d7e5043a
--- /dev/null
+++ b/meson.build
@@ -0,0 +1,80 @@
+project('Lithium', 'cpp')
+
+lithium_src_dir = 'src'
+lithium_module_dir = join_paths(lithium_src_dir, 'atom')
+lithium_client_dir = join_paths(lithium_src_dir, 'client')
+lithium_component_dir = join_paths(lithium_src_dir, 'addon')
+lithium_task_dir = join_paths(lithium_src_dir, 'task')
+
+libs_dir = 'libs'
+
+lithium_deps = [
+ dependency('oatpp-websocket'),
+ dependency('oatpp-swagger'),
+ dependency('oatpp-openssl'),
+ dependency('oatpp-zlib'),
+ dependency('loguru'),
+ dependency('libzippp'),
+ dependency('fmt'),
+ dependency('cfitsio'),
+ dependency('openssl'),
+ dependency('sqlite3'),
+ dependency('cpp_httplib'),
+ dependency('backward'),
+ dependency('tinyxml2'),
+ dependency('pocketpy'),
+]
+
+cpp = meson.get_compiler('cpp')
+
+lithium_server_src = files([
+ join_paths(lithium_src_dir, 'app.cpp'),
+])
+
+lithium_server_library_src = files([
+ join_paths(lithium_src_dir, 'device/server/ascom.cpp'),
+ join_paths(lithium_src_dir, 'device/server/hydrogen.cpp'),
+ join_paths(lithium_src_dir, 'device/server/hydrogen_driver.cpp'),
+ join_paths(lithium_src_dir, 'device/server/connector.cpp'),
+ join_paths(lithium_src_dir, 'device/manager.cpp'),
+ join_paths(lithium_src_dir, 'device/utils/utils.cpp'),
+ join_paths(lithium_component_dir, 'addons.cpp'),
+ join_paths(lithium_component_dir, 'compiler.cpp'),
+ join_paths(lithium_component_dir, 'finder.cpp'),
+ join_paths(lithium_component_dir, 'loader.cpp'),
+ join_paths(lithium_component_dir, 'manager.cpp'),
+ join_paths(lithium_component_dir, 'sandbox.cpp'),
+ join_paths(lithium_component_dir, 'sort.cpp'),
+ join_paths(lithium_src_dir, 'config/configor.cpp'),
+ join_paths(lithium_src_dir, 'debug/terminal.cpp'),
+ join_paths(lithium_task_dir, 'manager.cpp'),
+ join_paths(lithium_task_dir, 'generator.cpp'),
+ join_paths(lithium_task_dir, 'container.cpp'),
+ join_paths(lithium_task_dir, 'tick.cpp'),
+ join_paths(lithium_task_dir, 'loader.cpp'),
+ join_paths(lithium_task_dir, 'list.cpp'),
+ join_paths(lithium_task_dir, 'pool.cpp'),
+ join_paths(lithium_src_dir, 'LithiumApp.cpp'),
+])
+
+lithium_server_library = static_library(
+ 'lithium_server-library',
+ lithium_server_library_src,
+ dependencies: lithium_deps,
+ include_directories: include_directories('.', 'libs', 'libs/oatpp', 'libs/oatpp-swagger', 'libs/oatpp-websocket'),
+)
+
+lithium_server = executable(
+ 'lithium_server',
+ lithium_server_src,
+ dependencies: lithium_deps,
+ include_directories: include_directories('.', 'libs', 'libs/oatpp', 'libs/oatpp-swagger', 'libs/oatpp-websocket'),
+ link_with: lithium_server_library,
+ link_args: cpp.get_supported_link_arguments(),
+)
+
+configure_file(
+ input: 'config.h.in',
+ output: 'config.h',
+)
+
diff --git a/scripts/build_ci.sh b/scripts/build_ci.sh
index ab1314e6..d092c611 100644
--- a/scripts/build_ci.sh
+++ b/scripts/build_ci.sh
@@ -1,3 +1,68 @@
-sudo apt-get update && sudo apt-get upgrade -y
-sudo apt install gcc g++ cmake
-sudo apt install libcfitsio-dev zlib1g-dev libssl-dev libzip-dev libnova-dev libfmt-dev
\ No newline at end of file
+#!/bin/bash
+
+set -e
+
+echo "Checking system environment..."
+
+# Check if the script is run with sudo
+if [[ $EUID -ne 0 ]]; then
+ echo "This script must be run with sudo."
+ exit 1
+fi
+
+# Check if the system is Debian-based
+if ! command -v apt-get &> /dev/null; then
+ echo "This script only supports Debian-based systems."
+ exit 1
+fi
+
+# Check if gcc and g++ are installed
+if ! command -v gcc &> /dev/null || ! command -v g++ &> /dev/null; then
+ echo "gcc and g++ are not installed. Installing..."
+ apt-get install -y gcc g++
+else
+ echo "gcc and g++ are already installed."
+fi
+
+# Check if g++ version is at least 10
+gpp_version=$(g++ --version | grep -oP '(?<=g\+\+ )[0-9]+')
+if [ "$gpp_version" -lt "10" ]; then
+ sudo add-apt-repository ppa:ubuntu-toolchain-r/test -y
+ sudo apt-get install gcc-13 g++-13 -y
+fi
+
+# Check if CMake is installed
+if ! command -v cmake &> /dev/null; then
+ echo "CMake is not installed. Installing..."
+ apt-get install -y cmake
+else
+ echo "CMake is already installed."
+fi
+
+# Check if CMake version is at least 3.20
+cmake_version=$(cmake --version | grep -oP '(?<=version )([0-9]+\.[0-9]+)')
+if [ "$(printf "%s\n" "3.20" "$cmake_version" | sort -V | head -n1)" != "3.20" ]; then
+ wget https://cmake.org/files/v3.28/cmake-3.28.0-rc5.tar.gz
+ tar -zxvf cmake-3.28.0-rc5.tar.gz
+ cd cmake-3.28.0-rc5
+ ./bootstrap && make && sudo make install
+ cd ..
+fi
+
+echo "Updating system packages..."
+apt-get update
+apt-get upgrade -y
+
+echo "Installing dependencies..."
+apt-get install -y libcfitsio-dev zlib1g-dev libssl-dev libzip-dev libnova-dev libfmt-dev
+
+echo "Checking installed dependencies..."
+for lib in cfitsio zlib ssl zip nova fmt; do
+ if ! ldconfig -p | grep -q "lib$lib"; then
+ echo "lib$lib is not properly installed. Please check the installation manually."
+ else
+ echo "lib$lib is properly installed."
+ fi
+done
+
+echo "Build environment setup completed."
diff --git a/scripts/build_win.sh b/scripts/build_win.sh
index 13bd7725..d910a358 100644
--- a/scripts/build_win.sh
+++ b/scripts/build_win.sh
@@ -1,10 +1,75 @@
+#!/bin/bash
+
+set -e
+
+echo "Checking system environment..."
+
+# Check if pacman is available
+if ! command -v pacman &> /dev/null; then
+ echo "This script requires an MSYS2 environment with pacman."
+ exit 1
+fi
+
+echo "Updating MSYS2 mirror list..."
sed -i "s#https\?://mirror.msys2.org/#https://mirrors.tuna.tsinghua.edu.cn/msys2/#g" /etc/pacman.d/mirrorlist*
-pacman -Syu
-pacman -S mingw-w64-x86_64-toolchain
-pacman -S mingw-w64-x86_64-dlfcn
-pacman -S mingw-w64-x86_64-cfitsio
-pacman -S mingw-w64-x86_64-cmake
-pacman -S mingw-w64-x86_64-libzip
-pacman -S mingw-w64-x86_64-zlib
-pacman -S mingw-w64-x86_64-fmt
-pacman -S mingw-w64-x86_64-libnova
\ No newline at end of file
+
+echo "Updating system packages..."
+pacman -Syu --noconfirm
+
+echo "Installing mingw-w64-x86_64-toolchain..."
+if ! pacman -Q mingw-w64-x86_64-toolchain &> /dev/null; then
+ pacman -S --noconfirm mingw-w64-x86_64-toolchain
+else
+ echo "mingw-w64-x86_64-toolchain is already installed."
+fi
+
+echo "Installing mingw-w64-x86_64-dlfcn..."
+if ! pacman -Q mingw-w64-x86_64-dlfcn &> /dev/null; then
+ pacman -S --noconfirm mingw-w64-x86_64-dlfcn
+else
+ echo "mingw-w64-x86_64-dlfcn is already installed."
+fi
+
+echo "Installing mingw-w64-x86_64-cfitsio..."
+if ! pacman -Q mingw-w64-x86_64-cfitsio &> /dev/null; then
+ pacman -S --noconfirm mingw-w64-x86_64-cfitsio
+else
+ echo "mingw-w64-x86_64-cfitsio is already installed."
+fi
+
+echo "Installing mingw-w64-x86_64-cmake..."
+if ! pacman -Q mingw-w64-x86_64-cmake &> /dev/null; then
+ pacman -S --noconfirm mingw-w64-x86_64-cmake
+else
+ echo "mingw-w64-x86_64-cmake is already installed."
+fi
+
+echo "Installing mingw-w64-x86_64-libzip..."
+if ! pacman -Q mingw-w64-x86_64-libzip &> /dev/null; then
+ pacman -S --noconfirm mingw-w64-x86_64-libzip
+else
+ echo "mingw-w64-x86_64-libzip is already installed."
+fi
+
+echo "Installing mingw-w64-x86_64-zlib..."
+if ! pacman -Q mingw-w64-x86_64-zlib &> /dev/null; then
+ pacman -S --noconfirm mingw-w64-x86_64-zlib
+else
+ echo "mingw-w64-x86_64-zlib is already installed."
+fi
+
+echo "Installing mingw-w64-x86_64-fmt..."
+if ! pacman -Q mingw-w64-x86_64-fmt &> /dev/null; then
+ pacman -S --noconfirm mingw-w64-x86_64-fmt
+else
+ echo "mingw-w64-x86_64-fmt is already installed."
+fi
+
+echo "Installing mingw-w64-x86_64-libnova..."
+if ! pacman -Q mingw-w64-x86_64-libnova &> /dev/null; then
+ pacman -S --noconfirm mingw-w64-x86_64-libnova
+else
+ echo "mingw-w64-x86_64-libnova is already installed."
+fi
+
+echo "Environment setup completed."
\ No newline at end of file
diff --git a/scripts/install_msys2.ps1 b/scripts/install_msys2.ps1
new file mode 100644
index 00000000..34577ad4
--- /dev/null
+++ b/scripts/install_msys2.ps1
@@ -0,0 +1,29 @@
+# 设置MSYS2的下载URL和安装路径
+$msys2_url = "https://github.com/msys2/msys2-installer/releases/download/2022-06-03/msys2-x86_64-20220603.exe"
+$msys2_installer = "msys2-x86_64-20220603.exe"
+$install_dir = "C:\msys64"
+
+# 下载MSYS2安装程序
+Write-Output "Downloading MSYS2 installer..."
+Invoke-WebRequest -Uri $msys2_url -OutFile $msys2_installer
+
+# 安装MSYS2
+Write-Output "Installing MSYS2..."
+Start-Process -FilePath .\$msys2_installer -ArgumentList "/S /D=$install_dir" -Wait
+
+# 配置pacman
+Write-Output "Configuring pacman..."
+$pacman_conf = "C:\msys64\etc\pacman.conf"
+Add-Content -Path $pacman_conf -Value 'Server = https://mirrors.tuna.tsinghua.edu.cn/msys2/mingw/x86_64/'
+Add-Content -Path $pacman_conf -Value 'Server = https://mirrors.tuna.tsinghua.edu.cn/msys2/mingw/i686/'
+Add-Content -Path $pacman_conf -Value 'Server = https://mirrors.tuna.tsinghua.edu.cn/msys2/msys/$arch/'
+
+# 更新系统包
+Write-Output "Updating system packages..."
+C:\msys64\usr\bin\bash.exe -lc "pacman -Syu --noconfirm"
+
+# 安装必要的开发工具和库
+Write-Output "Installing development tools and libraries..."
+C:\msys64\usr\bin\bash.exe -lc "pacman -S --noconfirm mingw-w64-x86_64-toolchain mingw-w64-x86_64-dlfcn mingw-w64-x86_64-cfitsio mingw-w64-x86_64-cmake mingw-w64-x86_64-libzip mingw-w64-x86_64-zlib mingw-w64-x86_64-fmt mingw-w64-x86_64-libnova"
+
+Write-Output "MSYS2 installation and setup completed."
\ No newline at end of file
diff --git a/scripts/nginx.sh b/scripts/nginx.sh
index 20a7b377..83781c1d 100644
--- a/scripts/nginx.sh
+++ b/scripts/nginx.sh
@@ -1,66 +1,86 @@
#!/bin/bash
-# 定义颜色
+# Define colors
GREEN='\033[0;32m'
RED='\033[0;31m'
NC='\033[0m'
-# 定义 Nginx 路径
+# Define Nginx paths
NGINX_PATH="/etc/nginx"
NGINX_CONF="$NGINX_PATH/nginx.conf"
NGINX_BINARY="/usr/sbin/nginx"
-# 函数: 启动 Nginx
+# Function: Install Nginx if not installed
+install_nginx() {
+ if ! command -v nginx &>/dev/null; then
+ echo "Installing Nginx..."
+ # Add installation commands according to the package manager used (apt, yum, etc.)
+ # Example for apt:
+ sudo apt-get update
+ sudo apt-get install nginx -y
+ fi
+}
+
+# Function: Start Nginx
start_nginx() {
if [ -f "$NGINX_BINARY" ]; then
$NGINX_BINARY
- echo -e "${GREEN}Nginx 已启动${NC}"
+ echo -e "${GREEN}Nginx has been started${NC}"
else
- echo -e "${RED}无法找到 Nginx 二进制文件${NC}"
+ echo -e "${RED}Nginx binary not found${NC}"
fi
}
-# 函数: 停止 Nginx
+# Function: Stop Nginx
stop_nginx() {
if [ -f "$NGINX_BINARY" ]; then
$NGINX_BINARY -s stop
- echo -e "${GREEN}Nginx 已停止${NC}"
+ echo -e "${GREEN}Nginx has been stopped${NC}"
else
- echo -e "${RED}无法找到 Nginx 二进制文件${NC}"
+ echo -e "${RED}Nginx binary not found${NC}"
fi
}
-# 函数: 重新加载 Nginx 配置
+# Function: Reload Nginx configuration
reload_nginx() {
if [ -f "$NGINX_BINARY" ]; then
$NGINX_BINARY -s reload
- echo -e "${GREEN}Nginx 配置已重新加载${NC}"
+ echo -e "${GREEN}Nginx configuration has been reloaded${NC}"
else
- echo -e "${RED}无法找到 Nginx 二进制文件${NC}"
+ echo -e "${RED}Nginx binary not found${NC}"
fi
}
-# 函数: 检查 Nginx 配置文件语法
+# Function: Check Nginx configuration syntax
check_config() {
if [ -f "$NGINX_CONF" ]; then
$NGINX_BINARY -t -c "$NGINX_CONF"
else
- echo -e "${RED}无法找到 Nginx 配置文件${NC}"
+ echo -e "${RED}Nginx configuration file not found${NC}"
fi
}
-# 函数: 显示帮助信息
+# Function: Show help message
show_help() {
echo "Usage: $0 [start|stop|reload|check|help]"
- echo " start 启动 Nginx"
- echo " stop 停止 Nginx"
- echo " reload 重新加载 Nginx 配置"
- echo " check 检查 Nginx 配置文件语法"
- echo " help 显示帮助信息"
+ echo " start Start Nginx"
+ echo " stop Stop Nginx"
+ echo " reload Reload Nginx configuration"
+ echo " check Check Nginx configuration syntax"
+ echo " help Show help message"
}
-# 主函数
+# Main function
main() {
+ if [ "$1" == "help" ]; then
+ show_help
+ exit 0
+ fi
+
+ # Check if Nginx is installed
+ install_nginx
+
+ # Execute the command
case "$1" in
start)
start_nginx
@@ -74,15 +94,13 @@ main() {
check)
check_config
;;
- help)
- show_help
- ;;
*)
- echo -e "${RED}无效的命令${NC}"
+ echo -e "${RED}Invalid command${NC}"
show_help
+ exit 1
;;
esac
}
-# 执行主函数
-main "$1"
\ No newline at end of file
+# Execute main function with the provided argument
+main "$1"
diff --git a/scripts/start-hotspot.sh b/scripts/start-hotspot.sh
index 0995a745..bbb50ddb 100644
--- a/scripts/start-hotspot.sh
+++ b/scripts/start-hotspot.sh
@@ -3,35 +3,82 @@
# Set default values
SSID="LithiumServer"
PASSWORD="lithiumserver"
+PACKAGE_MANAGER=""
+
+# Function to detect package manager
+detect_package_manager() {
+ if command -v apt-get &>/dev/null; then
+ PACKAGE_MANAGER="apt"
+ elif command -v yum &>/dev/null; then
+ PACKAGE_MANAGER="yum"
+ elif command -v dnf &>/dev/null; then
+ PACKAGE_MANAGER="dnf"
+ else
+ echo "Unsupported package manager. Please install 'hostapd' and 'dnsmasq' manually."
+ exit 1
+ fi
+}
+
+# Function to install packages
+install_packages() {
+ case $PACKAGE_MANAGER in
+ apt)
+ sudo apt-get update
+ sudo apt-get install hostapd dnsmasq -y
+ ;;
+ yum|dnf)
+ sudo $PACKAGE_MANAGER install hostapd dnsmasq -y
+ ;;
+ *)
+ echo "Unsupported package manager."
+ exit 1
+ ;;
+ esac
+}
+
+# Function to start services
+start_services() {
+ sudo systemctl start dnsmasq
+ sudo systemctl start hostapd
+}
+
+# Function to check if services are running
+check_services() {
+ if ! systemctl is-active --quiet dnsmasq || ! systemctl is-active --quiet hostapd; then
+ echo "Failed to start the hotspot."
+ exit 1
+ fi
+}
+
+# Main script
# Process arguments
-while [[ $# -gt 0 ]]
-do
-key="$1"
+while [[ $# -gt 0 ]]; do
+ key="$1"
-case $key in
- -s|--ssid)
- SSID="$2"
- shift # past argument
- shift # past value
- ;;
- -p|--password)
- PASSWORD="$2"
- shift # past argument
- shift # past value
- ;;
- *) # unknown option
- echo "Unknown option: $1"
- exit 1
- ;;
-esac
+ case $key in
+ -s|--ssid)
+ SSID="$2"
+ shift 2
+ ;;
+ -p|--password)
+ PASSWORD="$2"
+ shift 2
+ ;;
+ *)
+ echo "Unknown option: $1"
+ exit 1
+ ;;
+ esac
done
+# Detect package manager
+detect_package_manager
+
# Check if hostapd and dnsmasq are installed
if ! dpkg -s hostapd dnsmasq > /dev/null 2>&1; then
echo "Installing hostapd and dnsmasq..."
- sudo apt-get update
- sudo apt-get install hostapd dnsmasq -y
+ install_packages
fi
# Configure dnsmasq
@@ -54,12 +101,9 @@ wpa_pairwise=TKIP
rsn_pairwise=CCMP" | sudo tee /etc/hostapd/hostapd.conf > /dev/null
# Start dnsmasq and hostapd
-sudo systemctl start dnsmasq
-sudo systemctl start hostapd
+start_services
# Check if the hotspot is started
-ip addr show wlan0 | grep inet | awk '{print $2}' | cut -d/ -f1 | if grep -qE '^(192\.168\.4\.[0-9]{1,3})$'; then
- echo "Hotspot $SSID has been started with password $PASSWORD"
-else
- echo "Failed to start the hotspot"
-fi
+check_services
+
+echo "Hotspot $SSID has been started with password $PASSWORD"
diff --git a/src/App.cpp b/src/App.cpp
index 2bb8c4ed..3125a486 100644
--- a/src/App.cpp
+++ b/src/App.cpp
@@ -1,49 +1,20 @@
/*
- * App.cpp
+ * app.cpp
*
* Copyright (C) 2023-2024 Max Qian
*/
/*************************************************
-Date: 2023-7-13
+Date: 2024-14
-Description: Main
+Description: Main Entry
**************************************************/
-#ifdef ENABLE_WEB_SERVER
-// This is for debug only, please remove it in production
-// Oatpp server is still experimental, it may be improved in the future
-#include "AppComponent.hpp"
+#include "lithiumapp.hpp"
-#ifdef ENABLE_ASYNC
-#include "controller/AsyncConfigController.hpp"
-#include "controller/AsyncDeviceController.hpp"
-#include "controller/AsyncIOController.hpp"
-#include "controller/AsyncProcessController.hpp"
-#include "controller/AsyncStaticController.hpp"
-#include "controller/AsyncSystemController.hpp"
-#include "controller/AsyncUploadController.hpp"
-// #include "controller/AsyncWebSocketController.hpp"
-#include "controller/AsyncClientController.hpp"
-#include "oatpp-swagger/AsyncController.hpp"
-#else
-#include "oatpp-swagger/Controller.hpp"
-#endif
-
-#include "oatpp/network/Server.hpp"
-
-#define ADD_CONTROLLER(controller, docEndpoints, router, logMessage) \
- auto controller##_ptr = controller::createShared(); \
- docEndpoints.append(controller##_ptr->getEndpoints()); \
- router->addController(controller##_ptr); \
- DLOG_F(INFO, logMessage " loaded");
-
-#endif
-#include
-
-#include "LithiumApp.hpp"
+#include "preload.hpp"
#include "atom/log/loguru.hpp"
#include "atom/server/global_ptr.hpp"
@@ -57,6 +28,8 @@ Description: Main
using namespace Lithium::Terminal;
#endif
+#include "server/App.hpp"
+
#include
#include
#include
@@ -66,97 +39,7 @@ using namespace Lithium::Terminal;
#include
#endif
-void BusLoggerFunction(void *user_data, const loguru::Message &message) {
- Lithium::MyApp->sendJsonMessage("log", {{"message", message.message},
- {"level", message.verbosity},
- {"file", message.filename},
- {"line", message.line},
- {"timestamp", message.preamble}});
-}
-
-#ifdef ENABLE_WEB_SERVER
-void runServer() {
- DLOG_F(INFO, "Loading App component ...");
-#if ENABLE_IPV6
- AppComponent components(
- Lithium::MyApp->GetConfig("config/server").value("host", "::"),
- Lithium::MyApp->GetConfig("config/server")
- .value("port", 8000)); // Create scope Environment components
-#else
- DLOG_F(INFO, "Server host:", Lithium::MyApp->GetConfig({"key", "config/server"})
- .value("host", "0.0.0.0"));
- DLOG_F(INFO, "Server port:", Lithium::MyApp->GetConfig({"key", "config/server"})
- .value("port", 8000));
- AppComponent components(
- Lithium::MyApp->GetConfig({"key", "config/server"})
- .value("host", "0.0.0.0"),
- Lithium::MyApp->GetConfig({"key", "config/server"})
- .value("port", 8000)); // Create scope Environment components
-#endif
- DLOG_F(INFO, "App component loaded");
-
- /* Get router component */
- OATPP_COMPONENT(std::shared_ptr, router);
- oatpp::web::server::api::Endpoints docEndpoints;
- /* Add routes & documents */
-
- ADD_CONTROLLER(ConfigController, docEndpoints, router,
- "AsyncConfigController");
-
- ADD_CONTROLLER(StaticController, docEndpoints, router,
- "AsyncStaticController");
-
- ADD_CONTROLLER(SystemController, docEndpoints, router,
- "AsyncSystemController");
-
- // ADD_CONTROLLER(WebSocketController, docEndpoints, router,
- // "AsyncWebSocketController");
-
- ADD_CONTROLLER(IOController, docEndpoints, router, "AsyncIOController");
-
- ADD_CONTROLLER(ProcessController, docEndpoints, router,
- "AsyncProcessController");
-
- ADD_CONTROLLER(ClientController, docEndpoints, router,
- "AsyncClientController");
-
- DLOG_F(INFO, "Starting to load API doc controller");
-#if ENABLE_ASYNC
- router->addController(
- oatpp::swagger::AsyncController::createShared(docEndpoints));
-#else
- router->addController(
- oatpp::swagger::Controller::createShared(docEndpoints));
-#endif
- DLOG_F(INFO, "API doc controller loaded");
-
- /* Load websocket route */
- // router->addController(WebSocketController::createShared());
-
- /* Get connection handler component */
- OATPP_COMPONENT(std::shared_ptr,
- connectionHandler, "http");
-
- /* Get connection provider component */
- OATPP_COMPONENT(std::shared_ptr,
- connectionProvider);
-
- DLOG_F(INFO, "Loaded server components ... Prepare for starting ...");
- /* create server */
- oatpp::network::Server server(connectionProvider, connectionHandler);
-
- DLOG_F(INFO, "Server running on port {}...",
- connectionProvider->getProperty("port").toString()->c_str());
-
- /* This is a block function that will be called when the server is started
- */
- server.run();
-}
-#endif
-
-
-// TODO: add network logger, not implemented yet
-// struct MyNetworkLogger {};
+#include "argparse/argparse.hpp"
/**
* @brief setup log file
@@ -176,16 +59,9 @@ void setupLogFile() {
loguru::add_file(logFilePath.string().c_str(), loguru::Append,
loguru::Verbosity_MAX);
- // MyNetworkLogger network_logger;
- // TODO loguru::add_callback("network_logger", BusLoggerFunction,
- // &network_logger, loguru::Verbosity_INFO);
-
loguru::set_fatal_handler([](const loguru::Message &message) {
Atom::System::saveCrashLog(std::string(message.prefix) +
message.message);
-#if ENABLE_WEB_SERVER
- oatpp::base::Environment::destroy();
-#endif
});
}
@@ -196,6 +72,8 @@ void setupLogFile() {
* @return 0 on success
*/
int main(int argc, char *argv[]) {
+ // NOTE: gettext is not supported yet, it will cause compilation error on
+ // Mingw64
/* Add gettext */
#if ENABLE_GETTEXT
bindtextdomain("lithium", "locale");
@@ -203,15 +81,16 @@ int main(int argc, char *argv[]) {
setlocale(LC_ALL, "");
textdomain("lithium");
#endif
+ // Set log file
+ setupLogFile();
// Init loguru log system
loguru::init(argc, argv);
- // Set log file
- setupLogFile();
/* Parse arguments */
argparse::ArgumentParser program("Lithium Server");
+ // NOTE: The command arguments' priority is higher than the config file
program.add_argument("-P", "--port")
.help("port the server running on")
.default_value(8000);
@@ -223,10 +102,13 @@ int main(int argc, char *argv[]) {
.default_value("config.json");
program.add_argument("-M", "--module-path")
.help("path to the modules directory")
- .default_value("modules");
+ .default_value("./modules");
program.add_argument("-W", "--web-panel")
.help("web panel")
.default_value(true);
+ program.add_argument("-D", "--debug")
+ .help("debug mode")
+ .default_value(false);
program.add_argument("-L", "--log-file").help("path to log file");
program.add_description("Lithium Command Line Interface:");
@@ -235,32 +117,33 @@ int main(int argc, char *argv[]) {
program.parse_args(argc, argv);
Lithium::InitLithiumApp(argc, argv);
- // Run oatpp server
+ // Create shared instance
Lithium::MyApp = Lithium::LithiumApp::createShared();
-
- auto cmd_port = program.get("--port");
- if (cmd_port != 8000) {
- DLOG_F(INFO, "Command line server port : {}", cmd_port);
-
- auto port =
- Lithium::MyApp->GetConfig("config/server").value("port", 8000);
- if (port != cmd_port) {
- Lithium::MyApp->SetConfig(
- {{"key", "config/server/port"}, {"value", cmd_port}});
- DLOG_F(INFO, "Set server port to {}", cmd_port);
- }
- }
+ // Parse arguments
try {
auto cmd_host = program.get("--host");
+ auto cmd_port = program.get("--port");
auto cmd_config_path = program.get("--config");
auto cmd_module_path = program.get("--module-path");
auto cmd_web_panel = program.get("--web-panel");
+ auto cmd_debug = program.get("--debug");
if (!cmd_host.empty()) {
Lithium::MyApp->SetConfig(
{{"key", "config/server/host"}, {"value", cmd_host}});
DLOG_F(INFO, "Set server host to {}", cmd_host);
}
+ if (cmd_port != 8000) {
+ DLOG_F(INFO, "Command line server port : {}", cmd_port);
+
+ auto port = Lithium::MyApp->GetConfig("config/server")
+ .value("port", 8000);
+ if (port != cmd_port) {
+ Lithium::MyApp->SetConfig(
+ {{"key", "config/server/port"}, {"value", cmd_port}});
+ DLOG_F(INFO, "Set server port to {}", cmd_port);
+ }
+ }
if (!cmd_config_path.empty()) {
Lithium::MyApp->SetConfig({{"key", "config/server/configpath"},
{"value", cmd_config_path}});
@@ -279,49 +162,30 @@ int main(int argc, char *argv[]) {
DLOG_F(INFO, "Disable web panel");
}
}
+
+ if (cmd_debug) {
+ if (!Lithium::MyApp->GetConfig("config/server/debug").get()) {
+ Lithium::MyApp->SetConfig(
+ {{"key", "config/server/debug"}, {"value", true}});
+ }
+ } else {
+ Lithium::MyApp->SetConfig(
+ {{"key", "config/server/debug"}, {"value", false}});
+ DLOG_F(INFO, "Disable debug mode");
+ }
} catch (const std::bad_any_cast &e) {
LOG_F(ERROR, "Invalid args format! Error: {}", e.what());
+ Atom::System::saveCrashLog(e.what());
+ return 1;
}
-#if ENABLE_TERMINAL
- Lithium::MyApp->SetConfig(
- {{"key", "config/terminal/enabled"}, {"value", true}});
-
- CommandManager manager;
-
- // 注册指令函数
- manager.registerCommand("ls", lsCommand);
- manager.registerCommand("pwd", pwdCommand);
- manager.registerCommand("mkdir", mkdirCommand);
- manager.registerCommand("cp", cpCommand);
- manager.registerCommand("system", systemCommand);
-
- clearTerminal();
-
- // 打印终端头部信息
- printHeader();
-
- while (true)
- {
- // 获取终端输入
- std::string input = getTerminalInput(manager);
-
- // 运行指令函数
- std::string result = manager.runCommand(input, "");
-
- // 在终端上显示执行结果
- std::cout << result << std::endl;
+ // In debug mode run the terminal first and will not run the server
+ if (Lithium::MyApp->GetConfig("config/server/debug").get()) {
+ ConsoleTerminal terminal;
+ terminal.run();
+ } else {
+ runServer();
}
-#endif
-
-#if ENABLE_WEB_SERVER
- oatpp::base::Environment::init();
- // Run the main server
- runServer();
- // Clean up all
- oatpp::base::Environment::destroy();
-#endif
-
return 0;
}
\ No newline at end of file
diff --git a/src/Launcher.cpp b/src/Launcher.cpp
deleted file mode 100644
index d2a75bb0..00000000
--- a/src/Launcher.cpp
+++ /dev/null
@@ -1,69 +0,0 @@
-
-#include "LauncherComponent.hpp"
-
-#include "controller/AuthController.hpp"
-#include "controller/StaticController.hpp"
-#include "controller/StoryController.hpp"
-
-
-#include "oatpp-swagger/Controller.hpp"
-
-#include "oatpp/network/Server.hpp"
-
-#include
-
-void run() {
- AppComponent components; // Create scope Environment components
-
- /* create ApiControllers and add endpoints to router */
-
- auto router = components.httpRouter.getObject();
-
- oatpp::web::server::api::Endpoints docEndpoints;
-
- docEndpoints.append(
- router->addController(AuthController::createShared())->getEndpoints());
- docEndpoints.append(
- router->addController(StoryController::createShared())->getEndpoints());
-
- router->addController(
- oatpp::swagger::Controller::createShared(docEndpoints));
- router->addController(StaticController::createShared());
-
- /* create server */
-
- oatpp::network::Server server(
- components.serverConnectionProvider.getObject(),
- components.serverConnectionHandler.getObject());
-
- OATPP_LOGD("Server", "Running on port %s...",
- components.serverConnectionProvider.getObject()
- ->getProperty("port")
- .toString()
- ->c_str());
-
- server.run();
-}
-
-/**
- * main
- */
-int main(int argc, const char *argv[]) {
- oatpp::base::Environment::init();
-
- run();
-
- /* Print how much objects were created during app running, and what have
- * left-probably leaked */
- /* Disable object counting for release builds using '-D
- * OATPP_DISABLE_ENV_OBJECT_COUNTERS' flag for better performance */
- std::cout << "\nEnvironment:\n";
- std::cout << "objectsCount = "
- << oatpp::base::Environment::getObjectsCount() << "\n";
- std::cout << "objectsCreated = "
- << oatpp::base::Environment::getObjectsCreated() << "\n\n";
-
- oatpp::base::Environment::destroy();
-
- return 0;
-}
diff --git a/src/LauncherComponent.hpp b/src/LauncherComponent.hpp
deleted file mode 100644
index 21572946..00000000
--- a/src/LauncherComponent.hpp
+++ /dev/null
@@ -1,109 +0,0 @@
-
-#ifndef AppComponent_hpp
-#define AppComponent_hpp
-
-#include "components/DatabaseComponent.hpp"
-#include "components/SwaggerComponent.hpp"
-
-
-#include "ErrorHandler.hpp"
-
-#include "interceptor/AuthInterceptor.hpp"
-#include "oatpp/web/server/interceptor/AllowCorsGlobal.hpp"
-
-
-#include "oatpp/network/tcp/server/ConnectionProvider.hpp"
-#include "oatpp/web/server/HttpConnectionHandler.hpp"
-#include "oatpp/web/server/HttpRouter.hpp"
-
-
-#include "oatpp/parser/json/mapping/ObjectMapper.hpp"
-
-#include "oatpp/core/macro/component.hpp"
-
-/**
- * Class which creates and holds Application components and registers
- * components in oatpp::base::Environment Order of components initialization is
- * from top to bottom
- */
-class AppComponent {
-public:
- /**
- * Swagger component
- */
- SwaggerComponent swaggerComponent;
-
- /**
- * Database component
- */
- DatabaseComponent databaseComponent;
-
- OATPP_CREATE_COMPONENT(std::shared_ptr, jwt)
- ([] { return std::make_shared("", ""); }());
-
- /**
- * Create ObjectMapper component to serialize/deserialize DTOs in
- * Contoller's API
- */
- OATPP_CREATE_COMPONENT(std::shared_ptr,
- apiObjectMapper)
- ([] {
- auto objectMapper =
- oatpp::parser::json::mapping::ObjectMapper::createShared();
- objectMapper->getDeserializer()->getConfig()->allowUnknownFields =
- false;
- return objectMapper;
- }());
-
- /**
- * Create ConnectionProvider component which listens on the port
- */
- OATPP_CREATE_COMPONENT(
- std::shared_ptr,
- serverConnectionProvider)
- ([] {
- return oatpp::network::tcp::server::ConnectionProvider::createShared(
- {"0.0.0.0", 8000, oatpp::network::Address::IP_4});
- }());
-
- /**
- * Create Router component
- */
- OATPP_CREATE_COMPONENT(std::shared_ptr,
- httpRouter)
- ([] { return oatpp::web::server::HttpRouter::createShared(); }());
-
- /**
- * Create ConnectionHandler component which uses Router component to route
- * requests
- */
- OATPP_CREATE_COMPONENT(std::shared_ptr,
- serverConnectionHandler)
- ([] {
- OATPP_COMPONENT(std::shared_ptr, jwt); // get JWT component
- OATPP_COMPONENT(std::shared_ptr,
- router); // get Router component
- OATPP_COMPONENT(std::shared_ptr,
- objectMapper); // get ObjectMapper component
-
- auto connectionHandler =
- oatpp::web::server::HttpConnectionHandler::createShared(router);
-
- connectionHandler->setErrorHandler(
- std::make_shared(objectMapper));
-
- connectionHandler->addRequestInterceptor(
- std::make_shared<
- oatpp::web::server::interceptor::AllowOptionsGlobal>());
- connectionHandler->addRequestInterceptor(
- std::make_shared(jwt));
-
- connectionHandler->addResponseInterceptor(
- std::make_shared<
- oatpp::web::server::interceptor::AllowCorsGlobal>());
-
- return connectionHandler;
- }());
-};
-
-#endif /* AppComponent_hpp */
diff --git a/src/LithiumApp.cpp b/src/LithiumApp.cpp
index 42bcdc02..4206c490 100644
--- a/src/LithiumApp.cpp
+++ b/src/LithiumApp.cpp
@@ -12,7 +12,7 @@ Description: Lithium App Enter
**************************************************/
-#include "LithiumApp.hpp"
+#include "lithiumapp.hpp"
#include "config.h"
@@ -31,9 +31,9 @@ Description: Lithium App Enter
#include "atom/error/error_stack.hpp"
#include "atom/log/loguru.hpp"
+#include "atom/server/global_ptr.hpp"
#include "atom/system/process.hpp"
#include "atom/utils/time.hpp"
-#include "atom/server/global_ptr.hpp"
#include "utils/marco.hpp"
#include "magic_enum/magic_enum.hpp"
@@ -84,7 +84,6 @@ namespace Lithium {
std::shared_ptr MyApp = nullptr;
LithiumApp::LithiumApp() {
- DLOG_SCOPE_FUNCTION(INFO);
DLOG_F(INFO, "LithiumApp Constructor");
try {
// Specialized Managers and Threads
@@ -112,10 +111,10 @@ LithiumApp::LithiumApp() {
// message
// to the right type to process.
// All of the messages are based on the Message class.
- DLOG_SCOPE_F(INFO, "Start Message Processing Thread");
+ DLOG_F(INFO, "Start Message Processing Thread");
m_MessageBus.lock()->StartProcessingThread();
- DLOG_SCOPE_F(INFO, "Register LithiumApp Member Functions");
+ DLOG_F(INFO, "Register LithiumApp Member Functions");
LiRegisterMemberFunc("GetConfig", &LithiumApp::GetConfig);
LiRegisterMemberFunc("SetConfig", &LithiumApp::SetConfig);
@@ -125,7 +124,7 @@ LithiumApp::LithiumApp() {
LOG_F(ERROR, "Failed to load Lithium App , error : {}", e.what());
throw std::runtime_error("Failed to load Lithium App");
}
- DLOG_SCOPE_F(INFO, "Lithium App Initialized");
+ DLOG_F(INFO, "Lithium App Initialized");
}
LithiumApp::~LithiumApp() {
@@ -159,8 +158,8 @@ void InitLithiumApp(int argc, char **argv) {
// ScriptManager::createShared(GetPtr("MessageBus")));
AddPtr("lithium.device",
DeviceManager::createShared(
- GetPtr("lithium.bus"),
- GetPtr("lithium.config")));
+ GetPtr("lithium.bus").value(),
+ GetPtr("lithium.config").value()));
AddPtr("lithium.device.hydrogen", HydrogenManager::createShared());
AddPtr("lithium.error.stack", std::make_shared());
@@ -178,7 +177,7 @@ void InitLithiumApp(int argc, char **argv) {
AddPtr("lithium.utils.env", Atom::Utils::Env::createShared(argc, argv));
- // TODO: Addons path need to be configurable
+ // TODO: Addons path need to be configurable
AddPtr("lithium.addon.loader", ModuleLoader::createShared("./modules"));
AddPtr("lithium.addon.addon", AddonManager::createShared());
AddPtr("lithium.addon.manager", ComponentManager::createShared());
@@ -249,9 +248,9 @@ json LithiumApp::GetConfig(const json ¶ms) {
CHECK_PARAM("key");
std::string key_path = params["key"].get();
json res;
- if (json value = m_ConfigManager.lock()->getValue(key_path);
- !value.is_null()) {
- return createSuccessResponse(__func__, value);
+ if (auto value = m_ConfigManager.lock()->getValue(key_path);
+ value.has_value()) {
+ return createSuccessResponse(__func__, value.value());
}
return createErrorResponse(__func__, json(),
std::format("Key {} not found", key_path));
diff --git a/src/LithiumApp.hpp b/src/LithiumApp.hpp
index c4801614..bd97ea3e 100644
--- a/src/LithiumApp.hpp
+++ b/src/LithiumApp.hpp
@@ -1,5 +1,5 @@
/*
- * LithiumApp.hpp
+ * lithiumapp.hpp
*
* Copyright (C) 2023-2024 Max Qian
*/
diff --git a/src/addon/compiler.cpp b/src/addon/compiler.cpp
index 336ee903..6840addc 100644
--- a/src/addon/compiler.cpp
+++ b/src/addon/compiler.cpp
@@ -14,403 +14,160 @@ Description: Compiler
#include "compiler.hpp"
-#include
-#include
+#include "utils/constant.hpp"
+
#include
#include
-#include
+#include
#include "atom/log/loguru.hpp"
#include "atom/type/json.hpp"
using json = nlohmann::json;
namespace fs = std::filesystem;
-#ifdef _WIN32
-#include
-#define COMPILER "cl.exe"
-#define CMD_PREFIX ""
-#define CMD_SUFFIX ".dll"
-#elif __APPLE__
-#include
-#define COMPILER "clang++"
-#define CMD_PREFIX "lib"
-#define CMD_SUFFIX ".dylib"
-#else
-#include
-#define COMPILER "g++"
-#define CMD_PREFIX "lib"
-#define CMD_SUFFIX ".so"
-#endif
-
namespace Lithium {
-bool Compiler::CompileToSharedLibraryAllinOne(const std::string &code,
- const std::string &moduleName,
- const std::string &functionName) {
- DLOG_F(INFO, "Compiling module {}::{}...", moduleName, functionName);
+bool Compiler::compileToSharedLibrary(std::string_view code,
+ std::string_view moduleName,
+ std::string_view functionName,
+ std::string_view optionsFile) {
+ LOG_F(INFO, "Compiling module {}::{}...", moduleName, functionName);
- // 参数校验
if (code.empty() || moduleName.empty() || functionName.empty()) {
LOG_F(ERROR, "Invalid parameters.");
return false;
}
- // Check if the module is already compiled and cached
- auto cachedResult = cache_.find(moduleName + "::" + functionName);
- if (cachedResult != cache_.end()) {
- DLOG_F(WARNING,
- "Module {}::{} is already compiled, returning cached result.",
- moduleName, functionName);
- return true;
- }
-
- // Create output directory if it does not exist
- const std::string outputDir = "atom/global/";
- if (!fs::exists(outputDir)) {
- DLOG_F(WARNING, "Output directory does not exist, creating it: {}",
- outputDir);
- try {
- fs::create_directories(outputDir);
- } catch (const std::exception &e) {
- LOG_F(ERROR, "Failed to create output directory: {}", e.what());
- return false;
- }
- }
-
- // Read compile options from JSON file
- std::string compileOptions = "-shared -fPIC -x c++ ";
- std::ifstream compileOptionFile("compile_options.json");
- if (compileOptionFile.is_open()) {
- json compileOptionsJson;
- try {
- compileOptionFile >> compileOptionsJson;
- if (compileOptionsJson.contains("optimization_level") &&
- compileOptionsJson.contains("cplus_version") &&
- compileOptionsJson.contains("warnings")) {
- compileOptions =
- compileOptionsJson["optimization_level"]
- .get() +
- " " +
- compileOptionsJson["cplus_version"].get() +
- " " + compileOptionsJson["warnings"].get() +
- " ";
- } else {
- LOG_F(ERROR, "Invalid format in compile_options.json.");
- return false;
- }
- } catch (const std::exception &e) {
- LOG_F(ERROR, "Error reading compile_options.json: {}", e.what());
- return false;
- }
- }
-
- // Specify output file path
- std::string output = outputDir + moduleName + CMD_SUFFIX;
-
- // Syntax and semantic checking
- std::stringstream syntaxCheckCmd;
- syntaxCheckCmd << COMPILER << " -fsyntax-only -x c++ -";
- std::string syntaxCheckOutput;
- if (RunShellCommand(syntaxCheckCmd.str(), code, syntaxCheckOutput) != 0) {
- LOG_F(ERROR, "Syntax error in C++ code: {}", syntaxCheckOutput);
- return false;
- }
-
- // Compile code
- std::string compilationOutput;
- std::string cmd =
- std::string(COMPILER) + " " + compileOptions + " - " + " -o " + output;
- DLOG_F(INFO, "{}", cmd);
-
- int exitCode = RunShellCommand(cmd, code, compilationOutput);
- if (exitCode != 0) {
- LOG_F(ERROR, "Failed to compile C++ code: {}", compilationOutput);
- return false;
- }
-
- // Cache compiled module
- cache_[moduleName + "::" + functionName] = output;
-
- /*
- // Load the compiled module
- if(m_App.GetModuleLoader()->LoadModule(output, moduleName)) {
- LOG_S(INFO) << "Module " << moduleName << "::" << functionName << "
- compiled successfully."; return true; } else { LOG_F(ERROR, "Failed to load
- the compiled module: {}", output); return false;
- }
- */
- return false;
-}
-
-// 编译为共享库
-bool Compiler::CompileToSharedLibrary(const std::string &code,
- const std::string &moduleName,
- const std::string &functionName,
- const std::string &optionsFile) {
- LOG_F(INFO, "Compiling module {}::{}...", moduleName, functionName);
-
- // 参数校验
- if (!CheckParameters(code, moduleName, functionName)) {
- return false;
- }
-
- // 检查是否已经编译并缓存
- if (IsModuleCached(moduleName, functionName, cache_)) {
+ // 检查模块是否已编译并缓存
+ auto cachedModule =
+ cache_.find(fmt::format("{}::{}", moduleName, functionName));
+ if (cachedModule != cache_.end()) {
+ LOG_F(WARNING,
+ "Module {}::{} is already compiled, using cached result.",
+ moduleName, functionName);
return true;
}
// 创建输出目录
- const std::string outputDir = "atom/global/";
- if (!CreateOutputDirectory(outputDir)) {
+ const fs::path outputDir = "atom/global";
+ createOutputDirectory(outputDir);
+
+ const auto availableCompilers = findAvailableCompilers();
+ if (availableCompilers.empty()) {
+ LOG_F(ERROR, "No available compilers found.");
return false;
}
+ LOG_F(INFO, "Available compilers: {}", fmt::join(availableCompilers, ", "));
// 读取编译选项
- std::string compileOptions = ReadCompileOptions(optionsFile);
- if (compileOptions.empty()) {
- return false;
- }
+ std::ifstream optionsStream(optionsFile.data());
+ const auto compileOptions = [&optionsStream] {
+ if (!optionsStream) {
+ LOG_F(
+ WARNING,
+ "Failed to open compile options file, using default options.");
+ return std::string{"-O2 -std=c++20 -Wall -shared -fPIC"};
+ }
+
+ try {
+ json optionsJson;
+ optionsStream >> optionsJson;
+ return fmt::format(
+ "{} {} {}",
+ optionsJson["optimization_level"].get(),
+ optionsJson["cplus_version"].get(),
+ optionsJson["warnings"].get());
+ } catch (const std::exception& e) {
+ LOG_F(ERROR, "Failed to parse compile options file: {}", e.what());
+ return std::string{"-O2 -std=c++20 -Wall -shared -fPIC"};
+ }
+ }();
- // 检查语法
- if (!SyntaxCheck(code, COMPILER)) {
+ // 语法检查
+ if (!syntaxCheck(code, constants::COMPILER)) {
return false;
}
- // 指定输出文件路径
- std::string output = outputDir + moduleName + CMD_SUFFIX;
-
// 编译代码
- if (!CompileCode(code, COMPILER, compileOptions, output)) {
+ const auto outputPath =
+ outputDir / fmt::format("{}{}{}", constants::LIB_EXTENSION, moduleName,
+ constants::LIB_EXTENSION);
+ if (!compileCode(code, constants::COMPILER, compileOptions, outputPath)) {
return false;
}
- // 缓存已编译的模块
- CacheCompiledModule(moduleName, functionName, output, cache_);
-
- return true;
-}
-
-// 检查参数是否有效
-bool Compiler::CheckParameters(const std::string &code,
- const std::string &moduleName,
- const std::string &functionName) {
- if (code.empty() || moduleName.empty() || functionName.empty()) {
- LOG_F(ERROR, "Invalid parameters.");
- return false;
- }
+ // 缓存编译结果
+ cache_[fmt::format("{}::{}", moduleName, functionName)] = outputPath;
return true;
}
-// 检查模块是否已经编译并缓存
-bool Compiler::IsModuleCached(
- const std::string &moduleName, const std::string &functionName,
- std::unordered_map &cache_) {
- std::string key = moduleName + "::" + functionName;
- auto cachedResult = cache_.find(key);
- if (cachedResult != cache_.end()) {
- LOG_F(WARNING, "Module {}::{} is already compiled.", moduleName,
- functionName);
- return true;
- }
- return false;
-}
-
-// 创建输出目录
-bool Compiler::CreateOutputDirectory(const std::string &outputDir) {
+void Compiler::createOutputDirectory(const fs::path& outputDir) {
if (!fs::exists(outputDir)) {
- LOG_F(WARNING, "Output directory does not exist.");
- try {
- fs::create_directories(outputDir);
- } catch (const std::exception &e) {
- LOG_F(ERROR, "Failed to create output directory. {}", e.what());
- return false;
- }
- }
- return true;
-}
-
-// 从 JSON 文件中读取编译选项
-std::string Compiler::ReadCompileOptions(const std::string &optionsFile) {
- std::ifstream compileOptionFile(optionsFile);
- if (compileOptionFile.is_open()) {
- json compileOptionsJson;
- try {
- compileOptionFile >> compileOptionsJson;
- if (compileOptionsJson.contains("optimization_level") &&
- compileOptionsJson.contains("cplus_version") &&
- compileOptionsJson.contains("warnings")) {
- std::string compileOptions =
- compileOptionsJson["optimization_level"]
- .get() +
- " " +
- compileOptionsJson["cplus_version"].get() +
- " " + compileOptionsJson["warnings"].get();
- return compileOptions;
- } else {
- LOG_F(ERROR, "Invalid format in compile_options.json.");
- return "";
- }
- } catch (const std::exception &e) {
- LOG_F(ERROR, "Error reading compile_options.json: {}", e.what());
- return "";
- }
+ LOG_F(WARNING, "Output directory {} does not exist, creating it.",
+ outputDir.string());
+ fs::create_directories(outputDir);
}
- return "";
}
-// 语法检查
-bool Compiler::SyntaxCheck(const std::string &code,
- const std::string &compiler) {
- std::stringstream syntaxCheckCmd;
- syntaxCheckCmd << compiler << " -fsyntax-only -x c++ -";
- std::string syntaxCheckOutput;
- if (RunShellCommand(syntaxCheckCmd.str(), code, syntaxCheckOutput) != 0) {
- LOG_F(ERROR, "Syntax error in C++ code: {}", syntaxCheckOutput);
+bool Compiler::syntaxCheck(std::string_view code, std::string_view compiler) {
+ const auto command = fmt::format("{} -fsyntax-only -xc++ -", compiler);
+ std::string output;
+ const auto exitCode = runCommand(command, code, output);
+ if (exitCode != 0) {
+ LOG_F(ERROR, "Syntax check failed:\n{}", output);
return false;
}
return true;
}
-// 编译代码
-bool Compiler::CompileCode(const std::string &code, const std::string &compiler,
- const std::string &compileOptions,
- const std::string &output) {
- std::string cmd = compiler + " " + compileOptions + " - " + " -o " + output;
- DLOG_F(INFO, "{}", cmd);
-
+bool Compiler::compileCode(std::string_view code, std::string_view compiler,
+ std::string_view compileOptions,
+ const fs::path& output) {
+ const auto command = fmt::format("{} {} -xc++ - -o {}", compiler,
+ compileOptions, output.string());
std::string compilationOutput;
- int exitCode = RunShellCommand(cmd, code, compilationOutput);
+ const auto exitCode = runCommand(command, code, compilationOutput);
if (exitCode != 0) {
- LOG_F(ERROR, "Failed to compile C++ code: {}", compilationOutput);
+ LOG_F(ERROR, "Compilation failed:\n{}", compilationOutput);
return false;
}
return true;
}
-// 缓存已编译的模块
-void Compiler::CacheCompiledModule(
- const std::string &moduleName, const std::string &functionName,
- const std::string &output,
- std::unordered_map &cache_) {
- std::string key = moduleName + "::" + functionName;
- cache_[key] = output;
-}
-
-bool Compiler::CopyFile_(const std::string &source,
- const std::string &destination) {
- try {
- std::ifstream src(source, std::ios::binary);
- if (!src) {
- LOG_F(ERROR, "Failed to open file for copy: {}", source);
- return false;
- }
-
- std::ofstream dst(destination, std::ios::binary);
- if (!dst) {
- LOG_F(ERROR, "Failed to create file for copy: {}", destination);
- return false;
- }
+int Compiler::runCommand(std::string_view command, std::string_view input,
+ std::string& output) {
+ std::array buffer;
+ output.clear();
- dst << src.rdbuf();
-
- if (!dst.good()) {
- LOG_F(ERROR, "Error occurred while writing to destination file: {}",
- destination);
- return false;
- }
-
- return true;
- } catch (const std::exception &e) {
- LOG_F(ERROR, "Exception occurred during file copy: {}", e.what());
- return false;
+ auto pipe = popen(command.data(), "r+");
+ if (!pipe) {
+ LOG_F(ERROR, "Failed to run command: {}", command);
+ return -1;
}
-}
-int Compiler::RunShellCommand(const std::string &command,
- const std::string &input, std::string &output) {
- int exitCode = -1;
-#ifdef _WIN32
- HANDLE hStdoutRead = NULL;
+ fwrite(input.data(), sizeof(char), input.size(), pipe);
+ fclose(pipe);
- STARTUPINFO si = {sizeof(si)};
- PROCESS_INFORMATION pi;
- HANDLE hStdinRead = NULL, hStdoutWrite = NULL;
- SECURITY_ATTRIBUTES sa;
- sa.nLength = sizeof(sa);
- sa.lpSecurityDescriptor = NULL;
- sa.bInheritHandle = TRUE;
- if (!CreatePipe(&hStdinRead, &hStdoutWrite, &sa, 0)) {
- LOG_F(ERROR, "Failed to create input pipe for shell command: {}",
- command);
- return exitCode;
- }
- if (!SetHandleInformation(hStdoutWrite, HANDLE_FLAG_INHERIT, 0)) {
- LOG_F(ERROR,
- "Failed to set input handle information for shell command: {}",
- command);
- CloseHandle(hStdinRead);
- return exitCode;
+ pipe = popen(command.data(), "r");
+ while (fgets(buffer.data(), buffer.size(), pipe) != nullptr) {
+ output += buffer.data();
}
- if (!CreatePipe(&hStdoutRead, &hStdoutWrite, &sa, 0)) {
- LOG_F(ERROR, "Failed to create output pipe for shell command: {}",
- command);
- CloseHandle(hStdinRead);
- CloseHandle(hStdoutWrite);
- return exitCode;
- }
-
- si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
- si.hStdInput = hStdinRead;
- si.hStdOutput = hStdoutWrite;
- si.hStdError = GetStdHandle(STD_ERROR_HANDLE);
-
- std::vector commandBuffer(command.begin(), command.end());
- commandBuffer.push_back('\0');
+ return pclose(pipe);
+}
- if (!CreateProcess(NULL, &commandBuffer[0], NULL, NULL, TRUE, 0, NULL, NULL,
- &si, &pi)) {
- LOG_F(ERROR, "Failed to launch shell command: {}", command);
- CloseHandle(hStdinRead);
- CloseHandle(hStdoutWrite);
- CloseHandle(hStdoutRead);
- return exitCode;
- }
- CloseHandle(hStdinRead);
- CloseHandle(hStdoutWrite);
+std::vector Compiler::findAvailableCompilers() {
+ std::vector availableCompilers;
- // Read the command output
- char buffer[4096];
- DWORD bytesRead;
- while (ReadFile(hStdoutRead, buffer, sizeof(buffer), &bytesRead, NULL)) {
- if (bytesRead > 0) {
- output.append(buffer, bytesRead);
- } else {
- break;
+ for (const auto& path : constants::COMPILER_PATHS) {
+ for (const auto& compiler : constants::COMMON_COMPILERS) {
+ std::filesystem::path compilerPath =
+ std::filesystem::path(path) / compiler;
+ if (std::filesystem::exists(compilerPath)) {
+ availableCompilers.push_back(compilerPath.string());
+ }
}
}
- // Wait for the command to finish
- WaitForSingleObject(pi.hProcess, INFINITE);
- GetExitCodeProcess(pi.hProcess, (LPDWORD)&exitCode);
-
- // Clean up
- CloseHandle(pi.hProcess);
- CloseHandle(pi.hThread);
- CloseHandle(hStdoutRead);
-#else
- FILE *pipe = popen(command.c_str(), "w");
- if (!pipe) {
- LOG_F(ERROR, "Failed to popen shell command: {}", command);
- return exitCode;
- }
-
- fwrite(input.c_str(), 1, input.size(), pipe);
- fclose(pipe);
-
- exitCode = WEXITSTATUS(pclose(pipe));
-#endif
-
- return exitCode;
+ return availableCompilers;
}
-
} // namespace Lithium
diff --git a/src/addon/compiler.hpp b/src/addon/compiler.hpp
index 5829c4b4..4a9b63b8 100644
--- a/src/addon/compiler.hpp
+++ b/src/addon/compiler.hpp
@@ -12,140 +12,77 @@ Description: Compiler
**************************************************/
-#pragma once
+#ifndef LITHIUM_ADDON_COMPILER_HPP
+#define LITHIUM_ADDON_COMPILER_HPP
+#include
#include
-
-#if ENABLE_FASTHASH
-#include "emhash/hash_table8.hpp"
-#else
+#include
#include
-#endif
+#include
namespace Lithium {
class Compiler {
public:
/**
- * \brief 编译 C++ 代码为共享库,并加载到内存中
- *
- * \param code 要编译的代码
- * \param moduleName 模块名
- * \param functionName 入口函数名
- * \return 编译是否成功
- */
- bool CompileToSharedLibrary(
- const std::string &code, const std::string &moduleName,
- const std::string &functionName,
- const std::string &optionsFile = "compile_options.json");
-
- /**
- * \brief 编译 C++ 代码为共享库,并加载到内存中
- *
- * \param code 要编译的代码
- * \param moduleName 模块名
- * \param functionName 入口函数名
- * \return 编译是否成功
+ * 编译 C++ 代码为共享库,并加载到内存中
+ * @param code 要编译的代码
+ * @param moduleName 模块名
+ * @param functionName 入口函数名
+ * @param optionsFile 编译选项文件路径,默认为 "compile_options.json"
+ * @return 编译是否成功
*/
- bool CompileToSharedLibraryAllinOne(const std::string &code,
- const std::string &moduleName,
- const std::string &functionName);
+ [[nodiscard]] bool compileToSharedLibrary(
+ std::string_view code, std::string_view moduleName,
+ std::string_view functionName,
+ std::string_view optionsFile = "compile_options.json");
private:
/**
- * \brief 检查参数
- * \param code 要编译的代码
- * \param moduleName 模块名
- * \param functionName 入口函数名
- * \return 检查是否成功
- */
- bool CheckParameters(const std::string &code, const std::string &moduleName,
- const std::string &functionName);
-
- /**
- * \brief 判断模块是否已缓存
- * \param moduleName 模块名
- * \param functionName 入口函数名
- * \return 模块是否已缓存
- */
- bool IsModuleCached(const std::string &moduleName,
- const std::string &functionName,
- std::unordered_map &cache_);
-
- /**
- * \brief 创建输出目录
- * \param outputDir 输出目录路径
- * \return 创建是否成功
- */
- bool CreateOutputDirectory(const std::string &outputDir);
-
- /**
- * \brief 读取编译选项
- * \param optionsFile 编译选项文件路径
- * \return 编译选项
- */
- std::string ReadCompileOptions(const std::string &optionsFile);
-
- /**
- * \brief 语法检查
- * \param code 要编译的代码
- * \param compiler 编译器路径
- * \return 语法检查是否成功
+ * 创建输出目录
+ * @param outputDir 输出目录路径
*/
- bool SyntaxCheck(const std::string &code, const std::string &compiler);
+ void createOutputDirectory(const std::filesystem::path& outputDir);
/**
- * \brief 编译代码
- * \param code 要编译的代码
- * \param compiler 编译器路径
- * \param compileOptions 编译选项
- * \param output 编译输出路径
- * \return 编译是否成功
+ * 语法检查
+ * @param code 要编译的代码
+ * @param compiler 编译器路径
+ * @return 语法检查是否成功
*/
- bool CompileCode(const std::string &code, const std::string &compiler,
- const std::string &compileOptions,
- const std::string &output);
+ [[nodiscard]] bool syntaxCheck(std::string_view code,
+ std::string_view compiler);
/**
- * \brief 缓存编译好的模块
- * \param moduleName 模块名
- * \param functionName 入口函数名
- * \param output 编译输出路径
- * \return 缓存是否成功
+ * 编译代码
+ * @param code 要编译的代码
+ * @param compiler 编译器路径
+ * @param compileOptions 编译选项
+ * @param output 编译输出路径
+ * @return 编译是否成功
*/
- void CacheCompiledModule(
- const std::string &moduleName, const std::string &functionName,
- const std::string &output,
- std::unordered_map &cache_);
+ [[nodiscard]] bool compileCode(std::string_view code,
+ std::string_view compiler,
+ std::string_view compileOptions,
+ const std::filesystem::path& output);
- // ----------------------------------------------------
- // File and Directory
- // ----------------------------------------------------
/**
- * \brief 复制文件
- *
- * \param source 源文件路径
- * \param destination 目标文件路径
- * \return 是否复制成功
+ * 查找可用的编译器
+ * @return 可用的编译器列表
*/
- bool CopyFile_(const std::string &source, const std::string &destination);
+ [[nodiscard]] std::vector findAvailableCompilers();
/**
- * \brief 运行外部 shell
- * 命令,并将标准输入输出流转发到命令的标准输入输出流中
- *
- * \param command 要运行的命令
- * \param inputStream 标准输入流
- * \param outputStream 标准输出流
- * \return 命令运行的返回值
+ * 运行外部 shell 命令,并将标准输入输出流转发到命令的标准输入输出流中
+ * @param command 要运行的命令
+ * @param input 标准输入
+ * @param output 标准输出
+ * @return 命令运行的返回值
*/
- int RunShellCommand(const std::string &command, const std::string &input,
- std::string &output);
+ int runCommand(std::string_view command, std::string_view input,
+ std::string& output);
-#if ENABLE_FASTHASH
- emhash8::HashMap cache_;
-#else
- std::unordered_map cache_;
-#endif
+ std::unordered_map cache_;
};
-
} // namespace Lithium
+#endif
diff --git a/src/addon/finder.cpp b/src/addon/finder.cpp
deleted file mode 100644
index e60a1a6e..00000000
--- a/src/addon/finder.cpp
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * finder.cpp
- *
- * Copyright (C) 2023-2024 Max Qian
- */
-
-/*************************************************
-
-Date: 2024-1-4
-
-Description: Addons finder
-
-**************************************************/
-
-#include "finder.hpp"
-
-#include "atom/log/loguru.hpp"
-
-namespace Lithium {
-DirContainer::DirContainer(const std::filesystem::path &path) : m_path(path) {}
-
-const std::filesystem::path &DirContainer::getPath() const { return m_path; }
-
-const std::vector &DirContainer::getSubdirs() const {
- return m_subdirs;
-}
-
-const std::vector &DirContainer::getFiles() const {
- return m_files;
-}
-
-void DirContainer::addSubdir(const DirContainer &subdir) {
- m_subdirs.push_back(subdir);
-}
-
-void DirContainer::addFile(const std::filesystem::path &file) {
- m_files.push_back(file);
-}
-
-AddonFinder::FilterFunction AddonFinder::m_filterFunc = nullptr;
-
-AddonFinder::AddonFinder(const std::filesystem::path &path,
- const FilterFunction &filterFunc)
- : m_path(path), m_dirContainer(m_path) {}
-
-bool AddonFinder::traverseDir(const std::filesystem::path &path) {
- if (std::filesystem::exists(m_path) &&
- std::filesystem::is_directory(m_path)) {
- traverseDir(m_path, m_dirContainer);
- } else {
- LOG_F(ERROR, "Invalid path: {}", m_path.string());
- return false;
- }
- return true;
-}
-
-std::vector AddonFinder::getAvailableDirs() const {
- std::vector matchingSubdirs;
-
- // Recursive function to find matching subdirectories
- std::function findMatchingSubdirs =
- [&](const DirContainer &dir) {
- for (const auto &subdir : dir.getSubdirs()) {
- if (!m_filterFunc && m_filterFunc(subdir.getPath())) {
- matchingSubdirs.push_back(
- subdir.getPath().filename().string());
- }
- findMatchingSubdirs(subdir);
- }
- };
-
- findMatchingSubdirs(m_dirContainer);
-
- return matchingSubdirs;
-}
-
-bool AddonFinder::hasFile(const std::filesystem::path &path,
- const std::string &filename) {
- for (const auto &entry : std::filesystem::directory_iterator(path)) {
- if (entry.is_directory()) {
- if (hasFile(entry.path(), filename)) {
- return true;
- }
- } else {
- if (entry.path().filename() == filename) {
- return true;
- }
- }
- }
- return false;
-}
-
-void AddonFinder::traverseDir(const std::filesystem::path &path,
- DirContainer &container) {
- for (const auto &entry : std::filesystem::directory_iterator(path)) {
- if (entry.is_directory()) {
- DirContainer subdir(entry.path());
- traverseDir(entry.path(), subdir);
- if (!subdir.getFiles().empty()) {
- container.addSubdir(subdir);
- }
- } else {
- if (!m_filterFunc || m_filterFunc(entry.path())) {
- container.addFile(entry.path());
- }
- }
- }
-}
-} // namespace Lithium
diff --git a/src/addon/finder.hpp b/src/addon/finder.hpp
deleted file mode 100644
index fb7c5507..00000000
--- a/src/addon/finder.hpp
+++ /dev/null
@@ -1,186 +0,0 @@
-/*
- * finder.hpp
- *
- * Copyright (C) 2023-2024 Max Qian
- */
-
-/*************************************************
-
-Date: 2024-1-4
-
-Description: Component finder (the core of the plugin system)
-
-**************************************************/
-
-#pragma once
-
-#include
-#include
-#include
-
-namespace Lithium {
-/**
- * @brief The DirContainer class represents a container for directory contents.
- */
-class DirContainer {
-public:
- /**
- * @brief Constructs a DirContainer object with the specified path.
- * @param path The path of the directory represented by this container.
- */
- explicit DirContainer(const std::filesystem::path &path);
-
- /**
- * @brief Gets the path of the directory represented by this container.
- * @return The path of the directory represented by this container.
- */
- const std::filesystem::path &getPath() const;
-
- /**
- * @brief Gets the subdirectories of the directory represented by this
- * container.
- * @return The subdirectories of the directory represented by this
- * container.
- */
- const std::vector &getSubdirs() const;
-
- /**
- * @brief Gets the files of the directory represented by this container.
- * @return The files of the directory represented by this container.
- */
- const std::vector &getFiles() const;
-
- /**
- * @brief Adds a subdirectory to the directory represented by this
- * container.
- * @param subdir The subdirectory to add.
- */
- void addSubdir(const DirContainer &subdir);
-
- /**
- * @brief Adds a file to the directory represented by this container.
- * @param file The file to add.
- */
- void addFile(const std::filesystem::path &file);
-
-private:
- std::filesystem::path m_path; /**< The path of the directory. */
- std::vector
- m_subdirs; /**< Subdirectories within the directory. */
- std::vector
- m_files; /**< Files within the directory. */
-};
-
-/**
- * @brief The AddonFinder class is responsible for finding components within a
- * given directory.
- */
-class AddonFinder {
-public:
- /**
- * @brief FilterFunction represents a function type used for filtering
- * paths.
- */
- using FilterFunction = std::function;
-
- /**
- * @brief Constructs a AddonFinder object with the specified path and filter
- * function.
- * @param path The path to the directory to search for components.
- * @param filterFunc The function used to filter paths within the directory
- * (optional).
- */
- explicit AddonFinder(const std::filesystem::path &path,
- const FilterFunction &filterFunc = {});
-
- /**
- * @brief Traverses the directory structure and populates the DirContainer
- * object.
- * @param path The path of the directory to traverse.
- * @return True if the traversal was successful, false otherwise.
- */
- bool traverseDir(const std::filesystem::path &path);
-
- /**
- * @brief Gets the names of the subdirectories that match the filter
- * function.
- * @return The names of the subdirectories that match the filter function.
- */
- std::vector getAvailableDirs() const;
-
- /**
- * @brief Checks if a file with the specified name exists within the given
- * path.
- * @param path The path to the directory to search for the file.
- * @param filename The name of the file to search for.
- * @return True if the file exists, false otherwise.
- */
- static bool hasFile(const std::filesystem::path &path,
- const std::string &filename);
-
-private:
- /**
- * @brief Recursively traverses the directory structure and populates the
- * DirContainer object.
- * @param path The path of the directory to traverse.
- * @param container The DirContainer object to populate with directory
- * contents.
- */
- static void traverseDir(const std::filesystem::path &path,
- DirContainer &container);
-
-private:
- std::filesystem::path m_path; /**< The path of the directory. */
- DirContainer m_dirContainer; /**< The DirContainer object representing the
- directory. */
- static FilterFunction
- m_filterFunc; /**< The filter function for path filtering. */
-};
-} // namespace Lithium
-
-/*
-// Example filter function: exclude files with ".txt" extension
-bool filterFunc(const std::filesystem::path &path)
-{
- return path.extension() != ".txt";
-}
-
-// Example check function: check if "example.txt" file exists in subdir
-bool checkFunc(const std::filesystem::path &path)
-{
- return AddonFinder::hasFile(path, "example.txt");
-}
-
-int main(int argc, char *argv[])
-{
- if (argc != 2)
- {
- std::cerr << "Usage: " << argv[0] << " " << std::endl;
- return -1;
- }
-
- std::filesystem::path path(argv[1]);
- if (!std::filesystem::exists(path) || !std::filesystem::is_directory(path))
- {
- std::cerr << "Invalid path: " << argv[1] << std::endl;
- return -1;
- }
-
- AddonFinder finder(path, filterFunc);
- finder.print();
-
- // Check if "example.txt" file exists in subdirs
- AddonFinder exampleFinder(path, checkFunc);
- if (exampleFinder.hasFile(path, "package.json"))
- {
- std::cout << "Found 'example.txt' in subdirs." << std::endl;
- }
- else
- {
- std::cout << "Did not find 'example.txt' in subdirs." << std::endl;
- }
-
- return 0;
-}
-
-*/
diff --git a/src/addon/loader.cpp b/src/addon/loader.cpp
index e3ecf8c8..a58bdfde 100644
--- a/src/addon/loader.cpp
+++ b/src/addon/loader.cpp
@@ -69,7 +69,8 @@ std::shared_ptr