From a86e3d0984d51ff8a1bf27d0001e44372fe57b3e Mon Sep 17 00:00:00 2001 From: Chi Huu Huynh <73843190+Chi-EEE@users.noreply.github.com> Date: Sun, 8 Oct 2023 00:42:17 +0100 Subject: [PATCH] Converted python RPLidar module to C++ --- .gitignore | 3 + .vscode/c_cpp_properties.json | 2 +- Car-Application.code-workspace | 69 ++- backend/.vscode/compile_commands.json | 16 +- .../packages/r/rplidar_sdk/port/xmake.lua | 35 -- .../packages/r/rplidar_sdk/xmake.lua | 41 -- .../repository/scripts/build_artifacts.lua | 71 --- backend/repository/scripts/list.lua | 14 - backend/repository/scripts/monkey.lua | 170 ------- backend/repository/scripts/new.lua | 272 ---------- backend/repository/scripts/packages.lua | 106 ---- backend/repository/scripts/sync.lua | 14 - backend/repository/scripts/test.lua | 248 --------- backend/src/ExpressPacket.hpp | 51 ++ backend/src/RPLidar.cpp | 474 ++++++++++++++++++ backend/src/RPLidar.h | 147 ++++++ backend/src/main.cpp | 29 +- backend/xmake.lua | 23 +- 18 files changed, 791 insertions(+), 994 deletions(-) delete mode 100644 backend/repository/packages/r/rplidar_sdk/port/xmake.lua delete mode 100644 backend/repository/packages/r/rplidar_sdk/xmake.lua delete mode 100644 backend/repository/scripts/build_artifacts.lua delete mode 100644 backend/repository/scripts/list.lua delete mode 100644 backend/repository/scripts/monkey.lua delete mode 100644 backend/repository/scripts/new.lua delete mode 100644 backend/repository/scripts/packages.lua delete mode 100644 backend/repository/scripts/sync.lua delete mode 100644 backend/repository/scripts/test.lua create mode 100644 backend/src/ExpressPacket.hpp create mode 100644 backend/src/RPLidar.cpp create mode 100644 backend/src/RPLidar.h diff --git a/.gitignore b/.gitignore index 45854b0a..42c559bf 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,8 @@ docs/* .xmake/ build/ +compile_commands.json + # MacOS Cache .DS_Store @@ -41,3 +43,4 @@ build/ *.exe *.out *.app +backend/.vscode/compile_commands.json diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json index d33e83e7..5bed24ff 100644 --- a/.vscode/c_cpp_properties.json +++ b/.vscode/c_cpp_properties.json @@ -11,7 +11,7 @@ "cppStandard": "gnu++17", "intelliSenseMode": "linux-gcc-x64", "configurationProvider": "ms-vscode.makefile-tools", - "compileCommands": ".vscode/compile_commands.json" + "compileCommands": "backend/compile_commands.json" } ], "version": 4 diff --git a/Car-Application.code-workspace b/Car-Application.code-workspace index 3900a65d..a50761a3 100644 --- a/Car-Application.code-workspace +++ b/Car-Application.code-workspace @@ -5,6 +5,73 @@ } ], "settings": { - "xmake.workingDirectory": "${workspaceRoot}/backend" + "xmake.workingDirectory": "${workspaceRoot}/backend", + "files.associations": { + "iostream": "cpp", + "vector": "cpp", + "xstring": "cpp", + "string": "cpp", + "atomic": "cpp", + "bit": "cpp", + "cctype": "cpp", + "charconv": "cpp", + "chrono": "cpp", + "clocale": "cpp", + "cmath": "cpp", + "compare": "cpp", + "concepts": "cpp", + "cstddef": "cpp", + "cstdint": "cpp", + "cstdio": "cpp", + "cstdlib": "cpp", + "cstring": "cpp", + "ctime": "cpp", + "cwchar": "cpp", + "exception": "cpp", + "format": "cpp", + "forward_list": "cpp", + "initializer_list": "cpp", + "iomanip": "cpp", + "ios": "cpp", + "iosfwd": "cpp", + "istream": "cpp", + "iterator": "cpp", + "limits": "cpp", + "locale": "cpp", + "map": "cpp", + "memory": "cpp", + "mutex": "cpp", + "new": "cpp", + "optional": "cpp", + "ostream": "cpp", + "ratio": "cpp", + "sstream": "cpp", + "stdexcept": "cpp", + "stop_token": "cpp", + "streambuf": "cpp", + "system_error": "cpp", + "thread": "cpp", + "tuple": "cpp", + "type_traits": "cpp", + "typeinfo": "cpp", + "utility": "cpp", + "xfacet": "cpp", + "xiosbase": "cpp", + "xlocale": "cpp", + "xlocbuf": "cpp", + "xlocinfo": "cpp", + "xlocmes": "cpp", + "xlocmon": "cpp", + "xlocnum": "cpp", + "xloctime": "cpp", + "xmemory": "cpp", + "xtr1common": "cpp", + "xtree": "cpp", + "xutility": "cpp", + "functional": "cpp", + "list": "cpp", + "unordered_map": "cpp", + "xhash": "cpp" + } } } \ No newline at end of file diff --git a/backend/.vscode/compile_commands.json b/backend/.vscode/compile_commands.json index 6a7e1fd4..38e529e9 100644 --- a/backend/.vscode/compile_commands.json +++ b/backend/.vscode/compile_commands.json @@ -1,6 +1,16 @@ [ { - "directory": "/home/pass_is_1/Car-Application/backend", - "arguments": ["/usr/bin/gcc", "-c", "-m64", "-g", "-O0", "-std=c++17", "-DCARES_STATICLIB", "-I", "/home/pass_is_1/.xmake/packages/d/drogon/v1.8.2/2cd05160177c49af96ab3f7d3f5506b8/include", "-I", "/home/pass_is_1/.xmake/packages/t/trantor/v1.5.14/8f08ce4c70de4747884cb10e87eef3b7/include", "-I", "/home/pass_is_1/.xmake/packages/c/c-ares/1.19.0/de08b8aebbb8402582b5466949d39ab8/include", "-I", "/home/pass_is_1/.xmake/packages/j/jsoncpp/1.9.5/6a59121558c24c01a73b20670010be3a/include", "-I", "/home/pass_is_1/.xmake/packages/b/brotli/v1.1.0/e3f40d36e48b41ef850d98dfb7dfc2cd/include", "-I", "/home/pass_is_1/.xmake/packages/z/zlib/v1.3/45ea3fb9069b468faf0ef6782964c796/include", "-I", "/home/pass_is_1/.xmake/packages/l/libuuid/1.0.3/ebb1e73a94a74620a8f85987fbf3009c/include", "-I", "/home/pass_is_1/.xmake/packages/o/openssl/1.1.1-t/25a06d63c5c94540b3812b3f969192cb/include", "-I", "/home/pass_is_1/.xmake/packages/r/rplidar_sdk/v2.0.0/6a84f7ec22434b108ac49f46c65ba829/include", "-o", "build/.objs/backend/linux/x86_64/debug/src/main.cpp.o", "src/main.cpp"], - "file": "src/main.cpp" + "directory": "c:\\Users\\admin\\Documents\\GitHub\\Car-Application\\backend", + "arguments": ["C:\\Program Files\\Microsoft Visual Studio\\2022\\Community\\VC\\Tools\\MSVC\\14.37.32822\\bin\\HostX64\\x64\\cl.exe", "/c", "/nologo", "/O2", "/fp:fast", "/std:c++17", "/D_WIN32", "/EHsc", "/IC:\\Users\\admin\\AppData\\Local\\.xmake\\packages\\s\\serial\\2022.3.9\\cefe600d4e804a218f4988a80900ae73\\include", "/IC:\\Users\\admin\\AppData\\Local\\.xmake\\packages\\t\\tl_expected\\v1.1.0\\06aea2638fda4a108c6e31ecc75bfc88\\include", "/DNDEBUG", "/Fobuild\\.objs\\backend\\windows\\x64\\release\\src\\main.cpp.obj", "src\\main.cpp"], + "file": "src\\main.cpp" +}, +{ + "directory": "c:\\Users\\admin\\Documents\\GitHub\\Car-Application\\backend", + "arguments": ["C:\\Program Files\\Microsoft Visual Studio\\2022\\Community\\VC\\Tools\\MSVC\\14.37.32822\\bin\\HostX64\\x64\\cl.exe", "/c", "/nologo", "/O2", "/fp:fast", "/std:c++17", "/D_WIN32", "/EHsc", "/IC:\\Users\\admin\\AppData\\Local\\.xmake\\packages\\s\\serial\\2022.3.9\\cefe600d4e804a218f4988a80900ae73\\include", "/IC:\\Users\\admin\\AppData\\Local\\.xmake\\packages\\t\\tl_expected\\v1.1.0\\06aea2638fda4a108c6e31ecc75bfc88\\include", "/DNDEBUG", "/Fobuild\\.objs\\backend\\windows\\x64\\release\\src\\RPLidar.cpp.obj", "src\\RPLidar.cpp"], + "file": "src\\RPLidar.cpp" +}, +{ + "directory": "c:\\Users\\admin\\Documents\\GitHub\\Car-Application\\backend", + "arguments": ["C:\\Program Files\\Microsoft Visual Studio\\2022\\Community\\VC\\Tools\\MSVC\\14.37.32822\\bin\\HostX64\\x64\\cl.exe", "/c", "/nologo", "/O2", "/fp:fast", "/std:c++17", "/D_WIN32", "/EHsc", "/IC:\\Users\\admin\\AppData\\Local\\.xmake\\packages\\s\\serial\\2022.3.9\\cefe600d4e804a218f4988a80900ae73\\include", "/IC:\\Users\\admin\\AppData\\Local\\.xmake\\packages\\t\\tl_expected\\v1.1.0\\06aea2638fda4a108c6e31ecc75bfc88\\include", "/DNDEBUG", "/Fobuild\\.objs\\backend\\windows\\x64\\release\\src\\Test.cpp.obj", "src\\Test.cpp"], + "file": "src\\Test.cpp" }] diff --git a/backend/repository/packages/r/rplidar_sdk/port/xmake.lua b/backend/repository/packages/r/rplidar_sdk/port/xmake.lua deleted file mode 100644 index 8e1e09e5..00000000 --- a/backend/repository/packages/r/rplidar_sdk/port/xmake.lua +++ /dev/null @@ -1,35 +0,0 @@ -add_rules("mode.debug", "mode.release") - -target("rplidar_sdk") - set_kind("$(kind)") - set_languages("c++11") - - add_files( - "src/*.cpp", - "src/hal/*.cpp" - ) - add_headerfiles( - "include/*.h", - "src/(hal/*.h)" - ) - add_includedirs("include", {public = true}) - add_includedirs("src", {public = true}) - - if is_plat("windows", "mingw") then - if is_arch("x64") then - add_defines("WIN64") - elseif is_arch("x86") then - add_defines("WIN32") - end - add_defines("_WIN32") - add_files("src/arch/win32/*.cpp") - add_headerfiles("src/win32/*.h") - elseif is_plat("linux") then - add_defines("__GNUC__") - add_files("src/arch/linux/*.cpp") - add_headerfiles("src/linux/*.h") - elseif is_plat("macosx") then - add_defines("_MACOS") - add_files("src/arch/macOS/*.cpp") - add_headerfiles("src/macOS/*.h") - end diff --git a/backend/repository/packages/r/rplidar_sdk/xmake.lua b/backend/repository/packages/r/rplidar_sdk/xmake.lua deleted file mode 100644 index 775fe110..00000000 --- a/backend/repository/packages/r/rplidar_sdk/xmake.lua +++ /dev/null @@ -1,41 +0,0 @@ -package("rplidar_sdk") - set_homepage("https://github.com/Slamtec/rplidar_sdk") - set_description("Open source SDK for Slamtec RPLIDAR series products") - set_license("BSD-2-Clause") - - set_urls("https://github.com/Slamtec/rplidar_sdk.git") - - add_versions("v2.0.0", "b9ab2c8e55c6457bc704a9269fbc4468efca156a") - - on_load(function (package) - if is_plat("windows", "mingw") then - package:add("defines", "__cplusplus") - package:add("defines", "_WIN32") - if is_arch("x64") then - package:add("defines", "WIN64") - elseif is_arch("x86") then - package:add("defines", "WIN32") - end - elseif is_plat("linux") then - package:add("defines", "__GNUC__") - elseif is_plat("macosx") then - package:add("defines", "_MACOS") - end - end) - - on_install(function (package) - os.cd("sdk") - os.cp(path.join(package:scriptdir(), "port", "xmake.lua"), "xmake.lua") - local configs = {} - import("package.tools.xmake").install(package, configs) - end) - - on_test(function (package) - assert(package:check_cxxsnippets({test = [[ - #include - using namespace sl; - void test() { - ILidarDriver * drv = *createLidarDriver(); - } - ]]}, {configs = {languages = "c++11"}})) - end) diff --git a/backend/repository/scripts/build_artifacts.lua b/backend/repository/scripts/build_artifacts.lua deleted file mode 100644 index 3ad48b2d..00000000 --- a/backend/repository/scripts/build_artifacts.lua +++ /dev/null @@ -1,71 +0,0 @@ -import("core.package.package") -import("core.base.semver") -import("packages") - --- load package -function _load_package(packagename, packagedir, packagefile) - local funcinfo = debug.getinfo(package.load_from_repository) - if funcinfo and funcinfo.nparams == 3 then -- >= 2.7.8 - return package.load_from_repository(packagename, packagedir, {packagefile = packagefile}) - else - -- deprecated - return package.load_from_repository(packagename, nil, packagedir, packagefile) - end -end - -function build_artifacts(name, versions) - local buildinfo = {name = name, versions = versions} - print(buildinfo) - os.tryrm("build-artifacts") - os.exec("git clone git@github.com:xmake-mirror/build-artifacts.git -b build") - local oldir = os.cd("build-artifacts") - local trycount = 0 - while trycount < 2 do - local ok = try - { - function () - io.save("build.txt", buildinfo) - os.exec("git add -A") - os.exec("git commit -a -m \"autobuild %s by xmake-repo/ci\"", name) - os.exec("git push origin build") - return true - end, - catch - { - function () - os.exec("git reset --hard HEAD^") - os.exec("git pull origin build") - end - } - } - if ok then - break - end - trycount = trycount + 1 - end - assert(trycount < 2) - os.cd(oldir) -end - -function main() - local files = os.iorun("git diff --name-only HEAD^") - for _, file in ipairs(files:split('\n'), string.trim) do - if file:find("packages", 1, true) and path.filename(file) == "xmake.lua" then - assert(file == file:lower(), "%s must be lower case!", file) - local packagedir = path.directory(file) - local packagename = path.filename(packagedir) - if #path.filename(path.directory(packagedir)) == 1 then - local instance = _load_package(packagename, packagedir, file) - if instance and packages.is_supported(instance, "windows") - and (instance.is_headeronly and not instance:is_headeronly()) then - local versions = instance:versions() - if versions and #versions > 0 then - table.sort(versions, function (a, b) return semver.compare(a, b) > 0 end) - local version_latest = versions[1] - build_artifacts(instance:name(), table.wrap(version_latest)) - end - end - end - end - end -end diff --git a/backend/repository/scripts/list.lua b/backend/repository/scripts/list.lua deleted file mode 100644 index d0450462..00000000 --- a/backend/repository/scripts/list.lua +++ /dev/null @@ -1,14 +0,0 @@ -import("packages") - -function main(...) - for plat, pkgs in pairs(packages()) do - cprint("${magenta}%s${clear}:", plat) - for _, pkg in ipairs(pkgs) do - if pkg.generic then - cprint(" ${yellow}->${clear} %s", pkg.name) - else - cprint(" ${yellow}->${clear} %s (%s)", pkg.name, table.concat(pkg.archs, ", ")) - end - end - end -end diff --git a/backend/repository/scripts/monkey.lua b/backend/repository/scripts/monkey.lua deleted file mode 100644 index b71ffd45..00000000 --- a/backend/repository/scripts/monkey.lua +++ /dev/null @@ -1,170 +0,0 @@ --- imports -import("core.base.option") -import("core.platform.platform") -import("packages", {alias = "get_packages"}) - --- the options -local options = -{ - {'v', "verbose", "k", nil, "Enable verbose information." } -, {'D', "diagnosis", "k", nil, "Enable diagnosis information." } -, {nil, "shallow", "k", nil, "Only install the root packages." } -, {'k', "kind", "kv", nil, "Enable static/shared library." } -, {'p', "plat", "kv", nil, "Set the given platform." } -, {'a', "arch", "kv", nil, "Set the given architecture." } -, {'m', "mode", "kv", nil, "Set the given mode." } -, {nil, "cflags", "kv", nil, "Set the cflags." } -, {nil, "cxxflags", "kv", nil, "Set the cxxflags." } -, {nil, "ldflags", "kv", nil, "Set the ldflags." } -, {nil, "ndk", "kv", nil, "Set the android NDK directory." } -, {nil, "sdk", "kv", nil, "Set the SDK directory of cross toolchain." } -, {nil, "vs_sdkver", "kv", nil, "Set the Windows SDK version." } -, {nil, "vs_runtime", "kv", nil, "Set the VS Runtime library." } -, {nil, "mingw", "kv", nil, "Set the MingW directory." } -, {nil, "toolchain", "kv", nil, "Set the toolchain name." } -, {nil, "packages", "vs", nil, "The package list." } -} - - --- require packages -function _require_packages(argv, packages) - local config_argv = {"f", "-c"} - if argv.verbose then - table.insert(config_argv, "-v") - end - if argv.diagnosis then - table.insert(config_argv, "-D") - end - if argv.plat then - table.insert(config_argv, "--plat=" .. argv.plat) - end - if argv.arch then - table.insert(config_argv, "--arch=" .. argv.arch) - end - if argv.mode then - table.insert(config_argv, "--mode=" .. argv.mode) - end - if argv.ndk then - table.insert(config_argv, "--ndk=" .. argv.ndk) - end - if argv.sdk then - table.insert(config_argv, "--sdk=" .. argv.sdk) - end - if argv.vs_sdkver then - table.insert(config_argv, "--vs_sdkver=" .. argv.vs_sdkver) - end - if argv.vs_runtime then - table.insert(config_argv, "--vs_runtime=" .. argv.vs_runtime) - end - if argv.mingw then - table.insert(config_argv, "--mingw=" .. argv.mingw) - end - if argv.toolchain then - table.insert(config_argv, "--toolchain=" .. argv.toolchain) - end - if argv.cflags then - table.insert(config_argv, "--cflags=" .. argv.cflags) - end - if argv.cxxflags then - table.insert(config_argv, "--cxxflags=" .. argv.cxxflags) - end - if argv.ldflags then - table.insert(config_argv, "--ldflags=" .. argv.ldflags) - end - os.vexecv("xmake", config_argv) - local require_argv = {"require", "-f", "-y"} - if argv.verbose then - table.insert(require_argv, "-v") - end - if argv.diagnosis then - table.insert(require_argv, "-D") - end - if argv.shallow then - table.insert(require_argv, "--shallow") - end - if argv.mode == "debug" and argv.kind == "shared" then - table.insert(require_argv, "--extra={debug=true,configs={shared=true}}") - elseif argv.mode == "debug" then - table.insert(require_argv, "--extra={debug=true}") - elseif argv.kind == "shared" then - table.insert(require_argv, "--extra={configs={shared=true}}") - end - table.join2(require_argv, packages) - os.vexecv("xmake", require_argv) -end - --- the given package is supported? -function _package_is_supported(argv, packagename) - local packages = get_packages() - if packages then - local plat = argv.plat or os.subhost() - local packages_plat = packages[plat] - for _, package in ipairs(packages_plat) do - if package and packagename:split("%s+")[1] == package.name then - local arch = argv.arch or platform.archs(plat)[1] or os.arch() - for _, package_arch in ipairs(package.archs) do - if arch == package_arch then - return true - end - end - end - end - end -end - --- the main entry -function main(...) - - -- parse arguments - local argv = option.parse({...}, options, "Test all the given or changed packages.") - - -- get packages - math.randomseed(os.time()) - local packages = argv.packages or {} - if #packages == 0 then - local files = os.files(path.join(os.scriptdir(), "..", "packages", "*", "*", "xmake.lua")) - local limit = is_host("bsd") and 1 or 10 - while #packages < limit do - local file = files[math.random(#files)] - if file:find("packages", 1, true) and path.filename(file) == "xmake.lua" then - assert(file == file:lower(), "%s must be lower case!", file) - local package = path.filename(path.directory(file)) - table.insert(packages, package) - end - end - end - if #packages == 0 then - table.insert(packages, "tbox dev") - end - - -- remove unsupported packages - for idx, package in irpairs(packages) do - assert(package == package:lower(), "package(%s) must be lower case!", package) - if not _package_is_supported(argv, package) then - table.remove(packages, idx) - end - end - if #packages == 0 then - print("no testable packages on %s!", argv.plat or os.subhost()) - return - end - - -- prepare test project - local repodir = os.curdir() - local workdir = path.join(os.tmpdir(), "xmake-repo") - print(packages) - os.setenv("XMAKE_STATS", "false") - os.tryrm(workdir) - os.mkdir(workdir) - os.cd(workdir) - os.exec("xmake create test") - os.cd("test") - print(os.curdir()) - os.exec("xmake repo --add local-repo %s", repodir) - os.exec("xmake repo -l") - - -- require packages - for _, package in ipairs(packages) do - _require_packages(argv, package) - end -end diff --git a/backend/repository/scripts/new.lua b/backend/repository/scripts/new.lua deleted file mode 100644 index fcda51a0..00000000 --- a/backend/repository/scripts/new.lua +++ /dev/null @@ -1,272 +0,0 @@ -import("core.base.option") -import("core.base.semver") -import("core.base.json") -import("core.base.hashset") -import("lib.detect.find_tool") -import("lib.detect.find_file") -import("net.http") -import("devel.git") -import("utils.archive") - -local options = { - {nil, "repo", "v", nil, "Set repository name.", - "e.g. ", - " - github:xmake-io/xmake", - " - gitlab:xmake-io/xmake"} -} - --- function to get Gitlab data -function get_gitlab_data(reponame) - local glab = assert(find_tool("glab"), "glab not found!") - local host = os.iorunv(glab.program, {"config", "get", "host"}):trim() - local graphql_query = 'query={ project(fullPath: "' .. reponame .. '") { description webUrl sshUrlToRepo name } }' - local repoinfo = os.iorunv(glab.program, {"api", "graphql", "-f", graphql_query}) - - local data = {} - if repoinfo then - repoinfo = json.decode(repoinfo) - if repoinfo.data and repoinfo.data.project then - -- extract required data and restructure it - local project_data = repoinfo.data.project - data = { - description = project_data.description, - homepageUrl = project_data.webUrl, - licenseInfo = "MIT", -- NOTE: Find a way to get the project license in gitlab - url = project_data.webUrl, - sshUrl = project_data.sshUrlToRepo, - name = project_data.name, - } - repoinfo.data.project = data - end - end - return {host = host, data = data} -end - -local function get_github_data(reponame) - local gh = assert(find_tool("gh"), "gh not found!") - local host = "github.com" - local data = os.iorunv(gh.program, { - "repo", - "view", - reponame, - "--json", - "description,homepageUrl,licenseInfo,url,sshUrl,name,latestRelease", - }) - if data then - data = json.decode(data) - end - return {data = data, host = host} -end - -function generate_package(reponame, get_data) - local repo_data = get_data(reponame) - local data = repo_data.data - local host = repo_data.host - - -- generate package header - local packagename = assert(data.name, "package name not found!"):lower() - local packagefile = path.join("packages", string.sub(packagename, 1, 1), packagename, "xmake.lua") - local file = io.open(packagefile, "w") - - -- define package and homepage - file:print('package("%s")', packagename) - local homepage = data.homepageUrl and data.homepageUrl ~= "" and data.homepageUrl or data.url - if homepage then - file:print(' set_homepage("%s")', homepage) - end - - local description = data.description or ("The " .. packagename .. " package") - file:print(' set_description("%s")', description) - - -- define license if available - if type(data.licenseInfo) == "table" and data.licenseInfo.key then - local licenses = { - ["apache-2.0"] = "Apache-2.0", - ["lgpl-2.0"] = "LGPL-2.0", - ["lgpl-2.1"] = "LGPL-2.1", - zlib = "zlib", - mit = "MIT", - } - local license = licenses[data.licenseInfo.key] - if license then - file:print(' set_license("%s")', license) - end - end - file:print("") - - -- define package URLs and versions - local repodir - local has_xmake, has_cmake, has_meson, has_bazel, has_autoconf, need_autogen - local latest_release = data.latestRelease - - if type(latest_release) == "table" then - local url = string.format("https://%s/%s/archive/refs/tags/%s.tar.gz", host, reponame, latest_release.tagName) - local giturl = string.format("https://%s/%s.git", host, reponame) - local tmpfile = os.tmpfile({ramdisk = false}) .. ".tar.gz" - repodir = tmpfile .. ".dir" - - file:write(' add_urls("https://' .. host .. '/' .. reponame .. '/archive/refs/tags/$(version).tar.gz",\n') - file:print(' "%s")\n', giturl) - - print("downloading %s", url) - http.download(url, tmpfile) - - file:print(' add_versions("%s", "%s")', latest_release.tagName, hash.sha256(tmpfile)) - archive.extract(tmpfile, repodir) - os.rm(tmpfile) - else - local giturl = string.format("https://%s/%s.git", host, reponame) - repodir = os.tmpfile({ ramdisk = false }) - - file:print(' add_urls("%s")', giturl) - - print("downloading %s", giturl) - git.clone(giturl, { outputdir = repodir, depth = 1 }) - - local commit = git.lastcommit({ repodir = repodir }) - local version = try { - function() - return os.iorunv("git", { - "log", - "-1", - "--date=format:%Y.%m.%d", - "--format=%ad", - }, { curdir = repodir }) - end - } - if version then - file:print(' add_versions("%s", "%s")', version:trim(), commit) - end - end - - local build_systems = { - ["xmake.lua"] = { - deps = {}, - priority = 1, - install = function(configs, package) - return [=[ - io.writefile("xmake.lua", [[ - add_rules("mode.release", "mode.debug") - target("%s") - set_kind("$(kind)") - add_files("src/*.c") - add_headerfiles("src/(*.h)") - ]]) - if package:config("shared") then - configs.kind = "shared" - end - import("package.tools.xmake").install(package, configs)]=] - end, - }, - ["CMakeLists.txt"] = { - deps = {"cmake"}, - priority = 2, - install = function(configs, package) - return [[ - table.insert(configs, "-DCMAKE_BUILD_TYPE=" .. (package:is_debug() and "Debug" or "Release")) - table.insert(configs, "-DBUILD_SHARED_LIBS=" .. (package:config("shared") and "ON" or "OFF")) - import("package.tools.cmake").install(package, configs)]] - end, - }, - ["configure,configure.ac,autogen.sh"] = { - deps = {"autoconf", "automake", "libtool"}, - priority = 3, - install = function(configs, package) - return [[ - table.insert(configs, "--enable-shared=" .. (package:config("shared") and "yes" or "no")) - if package:is_debug() then - table.insert(configs, "--enable-debug") - end - import("package.tools.autoconf").install(package, configs)]] - end, - }, - ["meson.build"] = { - deps = {"meson", "ninja"}, - priority = 4, - install = function(configs, package) - return [[ - table.insert(configs, "-Ddefault_library=" .. (package:config("shared") and "shared" or "static")) - import("package.tools.meson").install(package, configs)]] - end, - }, - ["BUILD,BUILD.bazel"] = { - deps = {"bazel"}, - priority = 5, - install = function(configs, package) - return [[ - import("package.tools.bazel").install(package, configs)]] - end, - } - } - - -- detect build system - local build_system_detected = {} - if repodir then - local files = os.files(path.join(repodir, "*")) or {} - table.join2(files, os.files(path.join(repodir, "*", "*"))) - for _, file in ipairs(files) do - local filename = path.filename(file) - for k, v in pairs(build_systems) do - local filenames = hashset.from(k:split(",")) - if filenames:has(filename) then - table.insert(build_system_detected, v) - end - end - end - os.rm(repodir) - end - local build_system - if #build_system_detected > 0 then - table.sort(build_system_detected, function (a, b) return a.priority < b.priority end) - build_system = build_system_detected[1] - end - if not build_system then - build_system = build_systems["xmake.lua"] - end - - -- add dependencies - if build_system then - file:print('') - local deps = table.wrap(build_system.deps) - if deps and #deps > 0 then - file:print(' add_deps("' .. table.concat(deps, '", "') .. '")') - end - end - - -- generate install scripts - file:print('') - file:print(' on_install(function (package)') - file:print(' local configs = {}') - if build_system then - file:print(build_system.install(configs, package)) - end - file:print(' end)') - - -- generate test scripts - file:print('') - file:print(' on_test(function (package)') - file:print(' assert(package:has_cfuncs("foo", {includes = "foo.h"}))') - file:print(' end)') - file:close() - - io.cat(packagefile) - cprint("${bright}%s generated!", packagefile) -end - -function main(...) - local opt = option.parse(table.pack(...), options, "New a package.", "", "Usage: xmake l scripts/new.lua [options]") - local repo = assert(opt.repo, "repository name must be set!") - local reponame = repo:sub(8) - - if repo:startswith("github:") then - generate_package(reponame, get_github_data) - return - end - - if repo:startswith("gitlab:") then - generate_package(reponame, get_gitlab_data) - return - end - - raise("unsupported repository source. only 'github' and 'gitlab' are supported.") -end diff --git a/backend/repository/scripts/packages.lua b/backend/repository/scripts/packages.lua deleted file mode 100644 index a021da61..00000000 --- a/backend/repository/scripts/packages.lua +++ /dev/null @@ -1,106 +0,0 @@ --- imports -import("core.package.package") -import("core.platform.platform") - --- is supported platform and architecture? -function is_supported(instance, plat, arch, opt) - - -- ignore template package - if instance.is_template and instance:is_template() then - return false - end - - -- get script - local script = instance:get("install") - local result = nil - if type(script) == "function" then - result = script - elseif type(script) == "table" then - - -- get plat and arch - local plat = plat or "" - local arch = arch or "" - - -- match pattern - -- - -- `@linux` - -- `@linux|x86_64` - -- `@macosx,linux` - -- `android@macosx,linux` - -- `android|armeabi-v7a@macosx,linux` - -- `android|armeabi-v7a@macosx,linux|x86_64` - -- `android|armeabi-v7a@linux|x86_64` - -- - for _pattern, _script in pairs(script) do - local hosts = {} - local hosts_spec = false - _pattern = _pattern:gsub("@(.+)", function (v) - for _, host in ipairs(v:split(',')) do - hosts[host] = true - hosts_spec = true - end - return "" - end) - if _pattern:trim() == "" and opt and opt.onlyhost then - _pattern = os.subhost() - end - if not _pattern:startswith("__") and (not hosts_spec or hosts[os.subhost() .. '|' .. os.subarch()] or hosts[os.subhost()]) - and (_pattern:trim() == "" or (plat .. '|' .. arch):find('^' .. _pattern .. '$') or plat:find('^' .. _pattern .. '$')) then - result = _script - break - end - end - - -- get generic script - result = result or script["__generic__"] - end - return result -end - --- load package -function _load_package(packagename, packagedir, packagefile) - local funcinfo = debug.getinfo(package.load_from_repository) - if funcinfo and funcinfo.nparams == 3 then -- >= 2.7.8 - return package.load_from_repository(packagename, packagedir, {packagefile = packagefile}) - else - -- deprecated - return package.load_from_repository(packagename, nil, packagedir, packagefile) - end -end - --- the main entry -function main(opt) - local packages = {} - for _, packagedir in ipairs(os.dirs(path.join("packages", "*", "*"))) do - local packagename = path.filename(packagedir) - local packagefile = path.join(packagedir, "xmake.lua") - local instance = _load_package(packagename, packagedir, packagefile) - local basename = instance:get("base") - if instance and basename then - local basedir = path.join("packages", basename:sub(1, 1):lower(), basename:lower()) - local basefile = path.join(basedir, "xmake.lua") - instance._BASE = _load_package(basename, basedir, basefile) - end - if instance then - for _, plat in ipairs({"windows", "linux", "macosx", "iphoneos", "android", "mingw", "msys", "bsd", "wasm", "cross"}) do - local archs = platform.archs(plat) - if archs then - local package_archs = {} - for _, arch in ipairs(archs) do - if is_supported(instance, plat, arch, opt) then - table.insert(package_archs, arch) - end - end - if #package_archs > 0 then - packages[plat] = packages[plat] or {} - table.insert(packages[plat], {name = instance:name(), instance = instance, archs = package_archs, generic = #package_archs == #archs}) - end - end - end - end - end - for _, packages_plat in pairs(packages) do - table.sort(packages_plat, function(a, b) return a.name < b.name end) - end - return packages -end diff --git a/backend/repository/scripts/sync.lua b/backend/repository/scripts/sync.lua deleted file mode 100644 index 67741516..00000000 --- a/backend/repository/scripts/sync.lua +++ /dev/null @@ -1,14 +0,0 @@ -import("core.base.option") - -function main() - print("update xmake-repo ..") - os.exec("git clone git@github.com:xmake-io/xmake-repo.git -b dev --recurse-submodules") - os.cd("xmake-repo") - os.exec("git push git@gitlab.com:tboox/xmake-repo.git dev") - os.exec("git push git@gitee.com:tboox/xmake-repo.git dev") - os.exec("git checkout master") - os.exec("git merge dev") - os.exec("git push git@github.com:xmake-io/xmake-repo.git master") - os.exec("git push git@gitlab.com:tboox/xmake-repo.git master") - os.exec("git push git@gitee.com:tboox/xmake-repo.git master") -end diff --git a/backend/repository/scripts/test.lua b/backend/repository/scripts/test.lua deleted file mode 100644 index 1ab18c98..00000000 --- a/backend/repository/scripts/test.lua +++ /dev/null @@ -1,248 +0,0 @@ --- imports -import("core.base.option") -import("core.platform.platform") -import("packages", {alias = "get_packages"}) - --- the options -local options = -{ - {'v', "verbose", "k", nil, "Enable verbose information." } -, {'D', "diagnosis", "k", nil, "Enable diagnosis information." } -, {nil, "shallow", "k", nil, "Only install the root packages." } -, {'k', "kind", "kv", nil, "Enable static/shared library." } -, {'p', "plat", "kv", nil, "Set the given platform." } -, {'a', "arch", "kv", nil, "Set the given architecture." } -, {'m', "mode", "kv", nil, "Set the given mode." } -, {'j', "jobs", "kv", nil, "Set the build jobs." } -, {'f', "configs", "kv", nil, "Set the configs." } -, {'d', "debugdir", "kv", nil, "Set the debug source directory." } -, {nil, "fetch", "k", nil, "Fetch package only." } -, {nil, "precompiled", "k", nil, "Attemp to install the precompiled package." } -, {nil, "remote", "k", nil, "Test package on the remote server." } -, {nil, "linkjobs", "kv", nil, "Set the link jobs." } -, {nil, "cflags", "kv", nil, "Set the cflags." } -, {nil, "cxxflags", "kv", nil, "Set the cxxflags." } -, {nil, "ldflags", "kv", nil, "Set the ldflags." } -, {nil, "ndk", "kv", nil, "Set the Android NDK directory." } -, {nil, "ndk_sdkver", "kv", nil, "Set the Android NDK platform sdk version." } -, {nil, "sdk", "kv", nil, "Set the SDK directory of cross toolchain." } -, {nil, "vs", "kv", nil, "Set the VS Compiler version." } -, {nil, "vs_sdkver", "kv", nil, "Set the Windows SDK version." } -, {nil, "vs_toolset", "kv", nil, "Set the Windows Toolset version." } -, {nil, "vs_runtime", "kv", nil, "Set the VS Runtime library." } -, {nil, "xcode_sdkver", "kv", nil, "The SDK Version for Xcode" } -, {nil, "target_minver", "kv", nil, "The Target Minimal Version" } -, {nil, "appledev", "kv", nil, "The Apple Device Type" } -, {nil, "mingw", "kv", nil, "Set the MingW directory." } -, {nil, "toolchain", "kv", nil, "Set the toolchain name." } -, {nil, "packages", "vs", nil, "The package list." } -} - - --- require packages -function _require_packages(argv, packages) - local config_argv = {"f", "-c"} - if argv.verbose then - table.insert(config_argv, "-v") - end - if argv.diagnosis then - table.insert(config_argv, "-D") - end - if argv.plat then - table.insert(config_argv, "--plat=" .. argv.plat) - end - if argv.arch then - table.insert(config_argv, "--arch=" .. argv.arch) - end - if argv.mode then - table.insert(config_argv, "--mode=" .. argv.mode) - end - if argv.ndk then - table.insert(config_argv, "--ndk=" .. argv.ndk) - end - if argv.sdk then - table.insert(config_argv, "--sdk=" .. argv.sdk) - end - if argv.ndk_sdkver then - table.insert(config_argv, "--ndk_sdkver=" .. argv.ndk_sdkver) - end - if argv.vs then - table.insert(config_argv, "--vs=" .. argv.vs) - end - if argv.vs_sdkver then - table.insert(config_argv, "--vs_sdkver=" .. argv.vs_sdkver) - end - if argv.vs_toolset then - table.insert(config_argv, "--vs_toolset=" .. argv.vs_toolset) - end - if argv.vs_runtime then - table.insert(config_argv, "--vs_runtime=" .. argv.vs_runtime) - end - if argv.xcode_sdkver then - table.insert(config_argv, "--xcode_sdkver=" .. argv.xcode_sdkver) - end - if argv.target_minver then - table.insert(config_argv, "--target_minver=" .. argv.target_minver) - end - if argv.appledev then - table.insert(config_argv, "--appledev=" .. argv.appledev) - end - if argv.mingw then - table.insert(config_argv, "--mingw=" .. argv.mingw) - end - if argv.toolchain then - table.insert(config_argv, "--toolchain=" .. argv.toolchain) - end - if argv.cflags then - table.insert(config_argv, "--cflags=" .. argv.cflags) - end - if argv.cxxflags then - table.insert(config_argv, "--cxxflags=" .. argv.cxxflags) - end - if argv.ldflags then - table.insert(config_argv, "--ldflags=" .. argv.ldflags) - end - os.vexecv("xmake", config_argv) - local require_argv = {"require", "-f", "-y"} - if not argv.precompiled then - table.insert(require_argv, "--build") - end - if argv.verbose then - table.insert(require_argv, "-v") - end - if argv.diagnosis then - table.insert(require_argv, "-D") - end - local is_debug = false - if argv.debugdir then - is_debug = true - table.insert(require_argv, "--debugdir=" .. argv.debugdir) - end - if argv.shallow or is_debug then - table.insert(require_argv, "--shallow") - end - if argv.jobs then - table.insert(require_argv, "--jobs=" .. argv.jobs) - end - if argv.linkjobs then - table.insert(require_argv, "--linkjobs=" .. argv.linkjobs) - end - if argv.fetch then - table.insert(require_argv, "--fetch") - end - local extra = {} - if argv.mode == "debug" then - extra.debug = true - end - -- Some packages set shared=true as default, so we need to force set - -- shared=false to test static build. - extra.configs = extra.configs or {} - extra.configs.shared = argv.kind == "shared" - local configs = argv.configs - if configs then - extra.system = false - extra.configs = extra.configs or {} - local extra_configs, errors = ("{" .. configs .. "}"):deserialize() - if extra_configs then - table.join2(extra.configs, extra_configs) - else - raise(errors) - end - end - local extra_str = string.serialize(extra, {indent = false, strip = true}) - table.insert(require_argv, "--extra=" .. extra_str) - table.join2(require_argv, packages) - os.vexecv("xmake", require_argv) -end - --- the given package is supported? -function _package_is_supported(argv, packagename) - local packages = get_packages() - if packages then - local plat = argv.plat or os.subhost() - local packages_plat = packages[plat] - for _, package in ipairs(packages_plat) do - if package and packagename:split("%s+")[1] == package.name then - local arch = argv.arch - if not arch and plat ~= os.subhost() then - arch = table.wrap(platform.archs(plat))[1] - end - if not arch then - arch = os.subarch() - end - for _, package_arch in ipairs(package.archs) do - if arch == package_arch then - return true - end - end - end - end - end -end - --- the main entry -function main(...) - - -- parse arguments - local argv = option.parse({...}, options, "Test all the given or changed packages.") - - -- get packages - local packages = argv.packages or {} - if #packages == 0 then - local files = os.iorun("git diff --name-only HEAD^") - for _, file in ipairs(files:split('\n'), string.trim) do - if file:startswith("packages") then - assert(file == file:lower(), "%s must be lower case!", file) - local package = file:match("packages/%w/(%S+)/") - table.insert(packages, package) - end - end - end - if #packages == 0 then - table.insert(packages, "tbox dev") - end - - -- remove unsupported packages - for idx, package in irpairs(packages) do - assert(package == package:lower(), "package(%s) must be lower case!", package) - if not _package_is_supported(argv, package) then - table.remove(packages, idx) - end - end - if #packages == 0 then - print("no testable packages on %s!", argv.plat or os.subhost()) - return - end - - -- prepare test project - local repodir = os.curdir() - local workdir = path.join(os.tmpdir(), "xmake-repo") - print(packages) - os.setenv("XMAKE_STATS", "false") - if not os.isfile(path.join(workdir, "test", "xmake.lua")) then - os.tryrm(workdir) - os.mkdir(workdir) - os.cd(workdir) - os.exec("xmake create test") - else - os.cd(workdir) - end - os.cd("test") - print(os.curdir()) - -- do action for remote? - os.exec("xmake service --disconnect") - if argv.remote then - os.tryrm("xmake-repo") - os.cp(path.join(repodir, "packages"), "xmake-repo/packages") - os.exec("xmake service --connect") - repodir = "xmake-repo" - end - os.exec("xmake repo --add local-repo %s", repodir) - os.exec("xmake repo -l") - - -- require packages - _require_packages(argv, packages) - --[[for _, package in ipairs(packages) do - _require_packages(argv, package) - end]] -end diff --git a/backend/src/ExpressPacket.hpp b/backend/src/ExpressPacket.hpp new file mode 100644 index 00000000..5a25bf61 --- /dev/null +++ b/backend/src/ExpressPacket.hpp @@ -0,0 +1,51 @@ +#ifndef EXPRESSPACKET_H +#define EXPRESSPACKET_H + +#pragma once + +#include +#include +#include + +class ExpressPacket { +public: + static const uint8_t sync1 = 0xa; + static const uint8_t sync2 = 0x5; + + ExpressPacket(std::vector data) { + if ((data[0] >> 4) != sync1 || (data[1] >> 4) != sync2) { + throw std::invalid_argument("try to parse corrupted data"); + } + + uint8_t checksum = 0; + for (size_t i = 2; i < data.size(); i++) { + checksum ^= data[i]; + } + + if (checksum != ((data[0] & 0x0F) + ((data[1] & 0x0F) << 4))) { + throw std::invalid_argument("Invalid checksum"); + } + + new_scan = (data[2] >> 7) & 0x01; + start_angle = static_cast((data[1] & 0x0F) << 8 | data[2]) / 64.0f; + + for (size_t i = 4; i < data.size(); i += 5) { + distance.push_back(((data[i + 1] >> 2) & 0x3F) | ((data[i] & 0x3F) << 6)); + angle.push_back((((data[i + 3] & 0x0F) + ((data[i + 1] & 0x01) << 4)) / 8.0f) * getSign(data[i + 1])); + distance.push_back(((data[i + 2] >> 2) & 0x3F) | ((data[i + 1] & 0x3F) << 6)); + angle.push_back((((data[i + 3] >> 4) & 0x0F) + ((data[i + 2] & 0x01) << 4)) / 8.0f * getSign(data[i + 2])); + } + } + + static int getSign(uint8_t value) { + return (value & 0x02) ? -1 : 1; + } + +public: + std::vector distance; + std::vector angle; + bool new_scan; + float start_angle; +}; + +#endif \ No newline at end of file diff --git a/backend/src/RPLidar.cpp b/backend/src/RPLidar.cpp new file mode 100644 index 00000000..ca23984d --- /dev/null +++ b/backend/src/RPLidar.cpp @@ -0,0 +1,474 @@ +#include "RPLidar.h" + +RPLidar::RPLidar(const std::string &port, uint32_t baudrate) +{ + this->port = port; + this->baudrate = baudrate; + try + { + this->_serial = std::make_unique(port, baudrate, serial::Timeout(1000U)); + std::cout << "Opened serial: " << _serial->isOpen() << '\n'; + } + catch (std::exception& e) + { + std::cout << "Unable to open serial: " << e.what() << '\n'; + } +} + +RPLidar::~RPLidar() +{ +} + +void RPLidar::disconnect() +{ + if (!this->_serial->isOpen()) + { + return; + } + this->_serial->close(); +} + +void RPLidar::_set_pwm(int pwm) +{ + std::string payload; + payload.push_back(static_cast(pwm & 0xFF)); + payload.push_back(static_cast((pwm >> 8) & 0xFF)); + this->_send_payload_cmd(SET_PWM_BYTE, payload); +} + +void RPLidar::set_motor_speed(int pwm) +{ + if (0 <= pwm && pwm <= MAX_MOTOR_PWM) + { + std::abort(); + } + this->_motor_speed = pwm; + if (this->motor_running) + this->_set_pwm(this->_motor_speed); +} + +void RPLidar::start_motor() +{ + // For A1 + this->_serial->setDTR(false); + + // For A2 + this->_set_pwm(this->_motor_speed); + this->motor_running = true; +} + +void RPLidar::stop_motor() +{ + // For A2 + this->_set_pwm(0); + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + + // For A1 + this->_serial->setDTR(true); + this->motor_running = false; +} + +void RPLidar::_send_payload_cmd(uint8_t cmd, const std::string &payload) +{ + // Calculate the size + uint8_t size = static_cast(payload.size()); + + // Construct the request string + std::string req; + req += static_cast(SYNC_BYTE); + req += static_cast(cmd); + req += static_cast(size); + req += payload; + + // Calculate the checksum + uint8_t checksum = 0; + for (const char &c : req) + { + checksum ^= static_cast(c); + } + + req += static_cast(checksum); + + this->_serial->write(req); +} + +void RPLidar::_send_cmd(uint8_t cmd) +{ + std::string req; + req += static_cast(SYNC_BYTE); + req += static_cast(cmd); + + this->_serial->write(req); +} + +std::tuple RPLidar::_read_descriptor() +{ + // Read descriptor packet + std::vector descriptor(DESCRIPTOR_LEN); + this->_serial->read(descriptor.data(), DESCRIPTOR_LEN); + + if (descriptor.size() != DESCRIPTOR_LEN) + { + throw std::runtime_error("Descriptor length mismatch"); + } + else if (descriptor[0] != SYNC_BYTE || descriptor[1] != SYNC_BYTE2) + { + throw std::runtime_error("Incorrect descriptor starting bytes"); + } + + bool isSingle = descriptor[5] == 0; + return std::make_tuple(descriptor[2], isSingle, descriptor[6]); +} + +std::vector RPLidar::_read_response(int dsize) +{ + std::vector data; + data.reserve(dsize); + + while (this->_serial->available() < dsize) + { + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + } + + // Read the specified number of bytes + for (int i = 0; i < dsize; ++i) + { + uint8_t byte; + _serial->read(&byte, 1); + data.push_back(byte); + } + + return data; +} + +std::string convertToHexString(uint8_t value) +{ + // Convert a uint8_t to a hexadecimal string + std::stringstream stream; + stream << std::hex << std::uppercase << std::setw(2) << std::setfill('0') << static_cast(value); + return stream.str(); +} + +DeviceInfo RPLidar::get_info() +{ + // Check if there's data in the buffer + if (this->_serial->available() > 0) + { + throw std::runtime_error("Data in buffer, you can't have info! Run flush() to empty the buffer."); + } + + this->_send_cmd(GET_INFO_BYTE); + + uint8_t dsize; + bool isSingle; + uint8_t dtype; + std::tie(dsize, isSingle, dtype) = this->_read_descriptor(); + + // Check response properties + if (dsize != INFO_LEN) + { + throw std::runtime_error("Wrong get_info reply length"); + } + if (!isSingle) + { + throw std::runtime_error("Not a single response mode"); + } + if (dtype != INFO_TYPE) + { + throw std::runtime_error("Wrong response data type"); + } + + // Read the response + std::vector raw = this->_read_response(dsize); + + // Convert serial number to a hex string + std::string serialNumber; + for (size_t i = 4; i < raw.size(); ++i) + { + serialNumber += convertToHexString(raw[i]); + } + + // Construct the device info struct + DeviceInfo info; + info.model = raw[0]; + info.firmware.first = raw[2]; + info.firmware.second = raw[1]; + info.hardware = raw[3]; + info.serialNumber = serialNumber; + + return info; +} + +HealthInfo RPLidar::get_health() +{ + // Check if there's data in the buffer + if (this->_serial->available() > 0) + { + throw std::runtime_error("Data in buffer, you can't get health info! Run cleanInput() to empty the buffer."); + } + + this->_send_cmd(GET_HEALTH_BYTE); + + // Read the descriptor + uint8_t dsize; + bool isSingle; + uint8_t dtype; + std::tie(dsize, isSingle, dtype) = this->_read_descriptor(); + + // Check response properties + if (dsize != HEALTH_LEN) + { + throw std::runtime_error("Wrong get_health reply length"); + } + if (!isSingle) + { + throw std::runtime_error("Not a single response mode"); + } + if (dtype != HEALTH_TYPE) + { + throw std::runtime_error("Wrong response data type"); + } + + // Read the response + std::vector raw = this->_read_response(dsize); + + // Extract status and error code + std::string status = HEALTH_STATUSES[raw[0]]; + int errorCode = (static_cast(raw[1]) << 8) + static_cast(raw[2]); + + return {status, errorCode}; +} + +void RPLidar::clean_input() +{ + if (this->scanning.currently_scanning) + { + throw std::runtime_error("Cleaning not allowed during scanning process active!"); + } + this->_serial->flush(); + this->express_trame = 32; + this->express_data = nullptr; +} + +void RPLidar::stop() +{ + this->_send_cmd(STOP_BYTE); + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + this->scanning.currently_scanning = false; + this->clean_input(); +} + +void RPLidar::start(ScanType scanType = NORMAL) +{ + if (this->scanning.currently_scanning) + { + throw std::runtime_error("Scanning already running!"); + } + + HealthInfo healthInfo = this->get_health(); + std::string status = healthInfo.status; + int errorCode = healthInfo.errorCode; + + if (status == "Error") + { + this->reset(); + healthInfo = this->get_health(); + status = healthInfo.status; + errorCode = healthInfo.errorCode; + if (status == "Error") + { + throw std::runtime_error("RPLidar hardware failure. Error code: " + std::to_string(errorCode)); + } + } + else if (status == "Warning") + { + std::cout << "Warning sensor status detected! Error code: " << errorCode << std::endl; + } + + uint8_t cmd = SCAN_TYPE[scanType]["byte"]; + + if (scanType == EXPRESS) + { + // Adjust this part according to your payload format + std::string payload = "\x00\x00\x00\x00\x00"; + this->_send_payload_cmd(cmd, payload); + } + else + { + this->_send_cmd(cmd); + } + + uint8_t dsize; + bool isSingle; + uint8_t dtype; + std::tie(dsize, isSingle, dtype) = this->_read_descriptor(); + + if (dsize != SCAN_TYPE[scanType]["size"]) + { + throw std::runtime_error("Wrong get_info reply length"); + } + if (isSingle) + { + throw std::runtime_error("Not a multiple response mode"); + } + if (dtype != SCAN_TYPE[scanType]["response"]) + { + throw std::runtime_error("Wrong response data type"); + } + + scanning = {true, dsize, scanType}; +} + +void RPLidar::reset() +{ + this->_send_cmd(RESET_BYTE); + std::this_thread::sleep_for(std::chrono::milliseconds(2000)); + this->clean_input(); +} + +Measure _process_scan(const std::vector &raw) +{ + Measure measurementData; + + bool newScan = static_cast(raw[0] & 0b1); + bool inversedNewScan = static_cast((raw[0] >> 1) & 0b1); + int quality = static_cast(raw[0] >> 2); + + if (newScan == inversedNewScan) + { + throw std::runtime_error("New scan flags mismatch"); + } + + int checkBit = static_cast(raw[1] & 0b1); + if (checkBit != 1) + { + throw std::runtime_error("Check bit not equal to 1"); + } + + int anglePart1 = static_cast(raw[1] >> 1); + int anglePart2 = static_cast(raw[2]) << 7; + float angle = (anglePart1 + anglePart2) / 64.0; + + int distancePart1 = static_cast(raw[3]); + int distancePart2 = static_cast(raw[4]) << 8; + float distance = (distancePart1 + distancePart2) / 4.0; + + measurementData.newScan = newScan; + measurementData.quality = quality; + measurementData.angle = angle; + measurementData.distance = distance; + + return measurementData; +} + +Measure _process_express_scan(std::shared_ptr data, float newAngle, int trame) +{ + Measure measurementData; + + bool newScan = (newAngle < data->start_angle) && (trame == 1); + + float angle = std::fmod((data->start_angle + ((newAngle - data->start_angle) / 32 * trame - data->angle[trame - 1])), 360); + float distance = data->distance[trame - 1]; + + measurementData.newScan = newScan; + measurementData.quality = 0; // Replace this with your actual quality value + measurementData.angle = angle; + measurementData.distance = distance; + + return measurementData; +} + +std::function RPLidar::iter_measures(ScanType scanType = NORMAL, int maxBufMeas = 3000) +{ +std::cout << "asdsda\n"; + this->start_motor(); +std::cout << "sss\n"; + + if (!this->scanning.currently_scanning) + { + this->start(scanType); + } + +std::cout << "ok\n"; + // Define a lambda function to generate measures + auto generator = [this, scanType, maxBufMeas]() -> Measure + { + while (true) + { + int dsize = scanning.dsize; + + if (maxBufMeas) + { + int dataInBuf = this->_serial->available(); + if (dataInBuf > maxBufMeas) + { + std::cout << "Too many bytes in the input buffer: " << dataInBuf << "/" << maxBufMeas + << ". Cleaning buffer..." << std::endl; + this->stop(); + this->start(scanning.type); + } + } + + if (scanType == NORMAL) + { + std::vector raw = this->_read_response(dsize); + Measure measure = _process_scan(raw); + return measure; + } + else if (scanType == EXPRESS) + { + if (this->express_trame == 32) + { + this->express_trame = 0; + + if (this->express_data == nullptr) + { + this->express_data = std::make_shared(ExpressPacket(this->_read_response(dsize))); + } + + this->express_old_data = this->express_data; + this->express_data = std::make_shared(ExpressPacket(this->_read_response(dsize))); + } + this->express_trame++; + Measure measure = _process_express_scan(this->express_old_data, this->express_data->start_angle, this->express_trame); + return measure; + } + } + }; +std::cout << "yes\n"; + + return generator; +} + +std::function()> RPLidar::iter_scans(ScanType scanType = NORMAL, int maxBufMeas = 3000, int minLen = 5) +{ + std::vector scanList; + std::cout << "start generator\n"; + std::function measureIterator = this->iter_measures(scanType, maxBufMeas); + std::cout << "startd generator\n"; + + // Define a lambda function to generate scans + auto scanGenerator = [this, scanType, maxBufMeas, minLen, measureIterator]() -> std::vector + { + std::vector scanList; + for (;;) + { + Measure measure = measureIterator(); + bool newScan = measure.newScan; + if (newScan) + { + if (scanList.size() > minLen) + { + return scanList; + } + scanList.clear(); + } + if (measure.distance > 0) + { + scanList.push_back(measure); + } + } + }; + std::cout << "return generator\n"; + return scanGenerator; +} diff --git a/backend/src/RPLidar.h b/backend/src/RPLidar.h new file mode 100644 index 00000000..6b17c9f0 --- /dev/null +++ b/backend/src/RPLidar.h @@ -0,0 +1,147 @@ +#ifndef RPLIDAR_H +#define RPLIDAR_H + +#pragma once + +#include +#include + +#include + +#include +#include +#include + +// std::this_thread::sleep_for(std::chrono::milliseconds(1)); +#include +#include + +// convertToHexString +#include +#include + +// iter_measures +#include + +#include "ExpressPacket.hpp" + +#define SYNC_BYTE 0xA5 +#define SYNC_BYTE2 0x5A + +#define GET_INFO_BYTE 0x50 +#define GET_HEALTH_BYTE 0x52 + +#define STOP_BYTE 0x25 +#define RESET_BYTE 0x40 + +enum ScanType +{ + NORMAL, + FORCE, + EXPRESS +}; + +struct ScanInfo +{ + int currently_scanning; + int dsize; + ScanType type; +}; + +static std::map> SCAN_TYPE = { + {ScanType::NORMAL, {{"byte", 0x20}, {"response", 129}, {"size", 5}}}, + {ScanType::FORCE, {{"byte", 0x21}, {"response", 129}, {"size", 5}}}, + {ScanType::EXPRESS, {{"byte", 0x82}, {"response", 130}, {"size", 84}}}}; + +#define DESCRIPTOR_LEN 7 +#define INFO_LEN 20 +#define HEALTH_LEN 3 + +#define INFO_TYPE 4 +#define HEALTH_TYPE 6 + +#define MAX_MOTOR_PWM 1023 +#define DEFAULT_MOTOR_PWM 660 +#define SET_PWM_BYTE 0xF0 + +static std::map HEALTH_STATUSES = { + {0, "Good"}, + {1, "Warning"}, + {2, "Error"}}; + +struct DeviceInfo +{ + uint8_t model; + std::pair firmware; + uint8_t hardware; + std::string serialNumber; +}; + +struct HealthInfo +{ + std::string status; + int errorCode; +}; + +struct Measure +{ + bool newScan; + int quality; + double angle; + double distance; +}; + +class RPLidar +{ +public: + RPLidar(const std::string &port, uint32_t baudrate = 115200U); + + ~RPLidar(); + + void disconnect(); + + void _set_pwm(int pwm); + + void set_motor_speed(int pwm); + + void start_motor(); + + void stop_motor(); + + void _send_payload_cmd(uint8_t cmd, const std::string &payload); + + void _send_cmd(uint8_t cmd); + + std::tuple _read_descriptor(); + + std::vector _read_response(int dsize); + + DeviceInfo get_info(); + + HealthInfo get_health(); + + void clean_input(); + + void stop(); + + void start(ScanType scanType); + + void reset(); + + std::function iter_measures(ScanType scanType, int maxBufMeas); + + std::function()> iter_scans(ScanType scanType, int maxBufMeas, int minLen); + +private: + std::unique_ptr _serial = nullptr; + std::string port; + uint32_t baudrate; + int _motor_speed = DEFAULT_MOTOR_PWM; + bool motor_running = false; + ScanInfo scanning = {false, 0, ScanType::NORMAL}; + int express_trame = 32; + std::shared_ptr express_data = nullptr; + std::shared_ptr express_old_data = nullptr; +}; + +#endif \ No newline at end of file diff --git a/backend/src/main.cpp b/backend/src/main.cpp index c9877439..fb12dbd1 100644 --- a/backend/src/main.cpp +++ b/backend/src/main.cpp @@ -1,16 +1,25 @@ #include -#include -#include - -using namespace drogon; +#include "RPLidar.h" int main() { - app().setLogPath("./") - .setLogLevel(trantor::Logger::kWarn) - .addListener("0.0.0.0", 9999) - .setThreadNum(16) - .enableRunAsDaemon() - .run(); + auto lidar = RPLidar("COM3"); + // std::function()> scanGenerator = lidar.iter_scans(ScanType::NORMAL, 3000, 5); + // for (int i = 0; i < 10; i++) + // { + // std::vector scan = scanGenerator(); + // std::cout << "Got " << scan.size() << " Measures!"; + // for (const Measure &measure : scan) + // { + // // Access individual measurements in the scan + // bool newScan = measure.newScan; + // int quality = measure.quality; + // float angle = measure.angle; + // float distance = measure.distance; + // } + // } + lidar.stop(); + lidar.stop_motor(); + lidar.disconnect(); } \ No newline at end of file diff --git a/backend/xmake.lua b/backend/xmake.lua index 8320c87c..f9182d68 100644 --- a/backend/xmake.lua +++ b/backend/xmake.lua @@ -5,14 +5,16 @@ add_repositories("repository repository") set_languages("cxx17") --- C++ Backend API for Svelte App -add_requires("drogon") - -- For the Lidar Scanner -add_requires("rplidar_sdk") +add_requires("serial") + +add_requires("spdlog") --- For the SunFounder Car if is_plat("linux", "macosx") then + -- C++ Backend API for Svelte App + add_requires("drogon") + + -- For the SunFounder Car add_requires("pca9685") end @@ -21,16 +23,21 @@ add_requires("tl_expected") target("backend") set_kind("binary") - add_packages("drogon") - - add_packages("rplidar_sdk") + add_packages("serial") + add_packages("spdlog") if is_plat("linux", "macosx") then + add_packages("drogon") add_packages("pca9685") end add_packages("tl_expected") add_files("src/*.cpp") + add_headerfiles("src/*.h", "src/*.hpp") + + if is_plat("windows") then + add_defines("_WIN32") + end -- -- If you want to known more usage about xmake, please see https://xmake.io