第5讲:建立自己的C函数库,js调用自己写的C/C++函数,并包含依赖C/C++第三方静态库。

news2024/10/10 2:20:55

在javascript中,Array有很多内置的功能,比如Array.map,Array.filter,Array.find等等,能用内置的功能就用内置的功能,最好不要自己实现一套,因为底层调用的可能压根就不是js语言本身,底层的实现可能由C/C++实现的。如果我们要做的一些功能,需要高性能密集计算,但是JavaScript内置函数无法满足我们要求的时候,这时候我们就要自己用C/C++编写一个程序,然后封装成wasm文件给JavaScript调用了,此时wasm还包含了.a文件这样的第三方库。

我们这里有个需求,就是在地球上有两艘船,船A和船B在某个经纬度位置触发,以某个航向、速度行驶,求它们间最小距离是多少,达到最小距离的时候,经过时间是多少秒?
首先这个功能用C/C++来编写,并且还要用到开源第三方库。
下图的红圈注释里面有几个参数,分别表示经度、纬度、速度、航向,当然getCPA最后一个参数6.5表示6.5分钟的时间长度。表示计算6.5分钟以内,两船最小距离是多少,并且到达最小距离时,经过时间是多少。
在这里插入图片描述

打开Visual Studio 2022,新建一个cmake工程,项目名称为GeoCompute。这仅仅是一个测试项目,如果测试通过,没有问题了,就把该代码交给emcc或者em++去编译。
CMakeLists.txt文件内容如下:
在这里,我采用vcpkg来安装GeographicLib库
可以执行如下命令安装。

vcpkg install GeographicLib:x64-windows
# CMakeList.txt: GeoCompute 的 CMake 项目,在此处包括源代码并定义
# 项目特定的逻辑。
#
cmake_minimum_required (VERSION 3.8)
set(VCPKG_ROOT "D:/CppPkg/WinVcpkg/vcpkg" CACHE PATH "")
set(CMAKE_TOOLCHAIN_FILE "${VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake")
# Enable Hot Reload for MSVC compilers if supported.
if (POLICY CMP0141)
  cmake_policy(SET CMP0141 NEW)
  set(CMAKE_MSVC_DEBUG_INFORMATION_FORMAT "$<IF:$<AND:$<C_COMPILER_ID:MSVC>,$<CXX_COMPILER_ID:MSVC>>,$<$<CONFIG:Debug,RelWithDebInfo>:EditAndContinue>,$<$<CONFIG:Debug,RelWithDebInfo>:ProgramDatabase>>")
endif()

project ("GeoCompute")

# 将源代码添加到此项目的可执行文件。
add_executable (GeoCompute "GeoCompute.cpp"  )


find_package (GeographicLib CONFIG REQUIRED)
target_link_libraries (GeoCompute PRIVATE ${GeographicLib_LIBRARIES})

if (CMAKE_VERSION VERSION_GREATER 3.12)
  set_property(TARGET GeoCompute PROPERTY CXX_STANDARD 20)
endif()

# TODO: 如有需要,请添加测试并安装目标。

然后编写一个GeoCompute.cpp文件

#include <iostream>
#include <GeographicLib/Geodesic.hpp>
#include <GeographicLib/Constants.hpp>
#include <cmath>
#include <vector>

const double EARTH_RADIUS = 6377830.0;  // 地球的平均半径,单位为千米
const double M_PI = 3.14159265359;

struct LatLon {
    double first;
    double second;
};

double deg2rad(double deg) {
    return deg * M_PI / 180.0;
}

double haversine_distance(double lat1, double lon1, double lat2, double lon2) {
    double dlat = deg2rad(lat2 - lat1);
    double dlon = deg2rad(lon2 - lon1);

    double a = std::sin(dlat / 2) * std::sin(dlat / 2) +
        std::cos(deg2rad(lat1)) * std::cos(deg2rad(lat2)) *
        std::sin(dlon / 2) * std::sin(dlon / 2);

    double c = 2 * std::atan2(std::sqrt(a), std::sqrt(1 - a));

    return EARTH_RADIUS * c;
}

LatLon new_position_with_geolib(double lat, double lon, double speed, double cog, double T) {
    const GeographicLib::Geodesic& geod = GeographicLib::Geodesic::WGS84();

    double s12 = speed * T;
    double lat2, lon2;

    // Direct method gives the destination point given start point, initial azimuth, and distance
    geod.Direct(lat, lon, cog, s12, lat2, lon2);
    return { lat2, lon2 };
}

double new_distance(double T, double latA, double lonA, double speedA, double cogA, double latB, double lonB, double speedB, double cogB) {
    auto resA = new_position_with_geolib(latA, lonA, speedA, cogA, T);
    auto resB = new_position_with_geolib(latB, lonB, speedB, cogB, T);
    return haversine_distance(resA.first, resA.second, resB.first, resB.second);
}

LatLon getCPA(double latA, double lonA, double speedA, double cogA, double latB, double lonB, double speedB, double cogB, double tcpa) {
    double RES_TCPA = INFINITY;
    double RES_DCPA = INFINITY;
    double prev_dist = INFINITY;
    double cur_dist = INFINITY;
    std::vector<int> status;
    int t_lim = tcpa * 60;
    int step = 1;
    if (t_lim > 600) {
        step = int(double(t_lim) / 300.0);
    }
    for (int t = 0;t < t_lim; t += step) {
        prev_dist = new_distance(t, latA, lonA, speedA, cogA, latB, lonB, speedB, cogB);
        cur_dist = new_distance(t + step, latA, lonA, speedA, cogA, latB, lonB, speedB, cogB);
        if (prev_dist < RES_DCPA) {
            RES_DCPA = prev_dist;
        }
        if (cur_dist - prev_dist <= 0) {
            if (status.size() == 0) {
                status.emplace_back(-1);
            }
        }
        else {
            if (status.size() == 0) {
                status.emplace_back(1);
                break;
            }
            else {
                if (status[0] == -1) {
                    status.emplace_back(1);
                }
            }
        }
        if (status.size() == 2 && status[0] == -1 && status[1] == 1) {
            RES_TCPA = t;
            break;
        }
    }
    return { RES_TCPA, RES_DCPA };
}

//1.  一开始距离就变大
// 2. 从0时刻到指定tcpa一直减小
// 3. 从0时刻到指定tcpa,先减小后增大
int main() {
    double lat = 40, lon = 100, speed = 10, cog = 45, T = 3600;
    auto result = new_position_with_geolib(lat, lon, speed, cog, T);
    std::cout << "New Latitude: " << result.first << ", New Longitude: " << result.second << std::endl;
    /*
    latA, lonA, speedA, cogA = 21.3058, 109.1014, 15.12, 187.13
    latB, lonB, speedB, cogB = 21.288205, 109.118725, 3.909777, 254.42
    */
    int i = 0;
    while (true) {
        // 先减小后增大
        auto res_ = getCPA(21.3058, 109.1014, 15.12, 187.13, 21.288205, 109.118725, 3.909777, 254.42, 6.5);
        //auto res_ = getCPA(22.3058, 108.1014, 15.12, 187.13, 21.288205, 109.118725, 3.909777, 254.42, 6.0);
        //auto res_ = getCPA(0.0, 0.0, 15.12, 225.0, 0.0000001, 0.0000001, 3.909777, 45.0, 6.0);
        std::cout << res_.first << " --- " << res_.second << std::endl;
        i++;
        printf("%d\n", i);
    }

    return 0;
}

好了,如果代码测试完成了。现在我们创建一个cmake工程,项目名为EmscriptenTest,这是用来生成wasm文件和js文件来给JavaScript调用的。

由于JavaScript运行在浏览器,不能直接支持windows的lib静态库,所以想办法得到.a库。
首先从github拉取geographiclib库的源码

 git clone https://github.com/geographiclib/geographiclib.git

然后进入到根目录:

cd geographiclib

最后用emcmake和emmake命令编译代码(前提是要安装emsdk:官网有教程说明:https://emscripten.org/docs/getting_started/downloads.html)

emcmake cmake .
emmake make

编译完成之后:
![(https://img-blog.csdnimg.cn/direct/3e76d9aba3834e01b2894e163ee5fcc8.png)

转到目录geographiclib/src
找到libGeographicLib.a文件,然后把该文件拷贝到EmscriptenTest项目的lib目录下(如果没有lib目录则自己新建)

然后是CMakeLists.txt文件:

# CMakeList.txt: EmscriptenTest 的 CMake 项目,在此处包括源代码并定义
# 项目特定的逻辑。
#
cmake_minimum_required (VERSION 3.8)
set(CMAKE_TOOLCHAIN_FILE "D:/CppPkg/emsdk/upstream/emscripten/cmake/Modules/Platform/Emscripten.cmake")
# 手动设置GeographicLib的路径
set(GEOGRAPHICLIB_INCLUDE_DIR "/path/to/vcpkg/installed/x64-windows/include")
#set(GEOGRAPHICLIB_LIB_DIR "/path/to/vcpkg/installed/x64-windows/lib")
set(GEOGRAPHICLIB_LIB_DIR "填写你的EmscriptenTest/lib目录的绝对路径")

# Enable Hot Reload for MSVC compilers if supported.
if (POLICY CMP0141)
  cmake_policy(SET CMP0141 NEW)
  set(CMAKE_MSVC_DEBUG_INFORMATION_FORMAT "$<IF:$<AND:$<C_COMPILER_ID:MSVC>,$<CXX_COMPILER_ID:MSVC>>,$<$<CONFIG:Debug,RelWithDebInfo>:EditAndContinue>,$<$<CONFIG:Debug,RelWithDebInfo>:ProgramDatabase>>")
endif()

project ("EmscriptenTest")

# 将源代码添加到此项目的可执行文件。
add_executable (EmscriptenTest "GeoCompute.cpp")

include_directories(D:/CppPkg/WinVcpkg/vcpkg/installed/x64-windows/include )
target_link_libraries(EmscriptenTest ${GEOGRAPHICLIB_LIB_DIR}/libGeographicLib.a)

add_library(GeoCompute STATIC GeoCompute.cpp)
set_target_properties(GeoCompute PROPERTIES SUFFIX ".wasm")
set_target_properties(GeoCompute PROPERTIES LINK_FLAGS "--bind -s WASM=1 -s MODULARIZE=1 -s EXPORT_NAME='GeoComputeModule' -s EXPORTED_FUNCTIONS='[\"getCPA\"]'")


if (CMAKE_VERSION VERSION_GREATER 3.12)
  set_property(TARGET EmscriptenTest PROPERTY CXX_STANDARD 20)
endif()

# TODO: 如有需要,请添加测试并安装目标。


这个EmscriptenTest.cpp没什么用,里面写个main函数,直接return 0;就完事儿了。
在项目根目录下新建GeoCompute.cpp文件
GeoCompute.cpp文件的内容:
可能会提示报错,但是如果点击重新生成,生成成功的话,是没事儿的。
在这个文件中,我修改了getCPA函数的返回类型为double* , 因为直接返回TDCPA结构体,JavaScript是无法识别的,一定要返回一个指针。

#include <iostream>
#include <GeographicLib/Geodesic.hpp>
#include <GeographicLib/Constants.hpp>
#include <cmath>
#include <vector>
#include <emscripten/emscripten.h>
const double EARTH_RADIUS = 6377830.0;  // 地球的平均半径,单位为千米
const double _M_PI = 3.14159265359;

struct LatLon {
    double lat;
    double lon;
};

struct TDCPA {
    double res_tcpa;
    double res_dcpa;
};

extern "C" {
    EMSCRIPTEN_KEEPALIVE
    double deg2rad(double deg) {
        return deg * _M_PI / 180.0;
    }
    EMSCRIPTEN_KEEPALIVE
    double haversine_distance(double lat1, double lon1, double lat2, double lon2) {
        double dlat = deg2rad(lat2 - lat1);
        double dlon = deg2rad(lon2 - lon1);

        double a = std::sin(dlat / 2) * std::sin(dlat / 2) +
            std::cos(deg2rad(lat1)) * std::cos(deg2rad(lat2)) *
            std::sin(dlon / 2) * std::sin(dlon / 2);

        double c = 2 * std::atan2(std::sqrt(a), std::sqrt(1 - a));

        return EARTH_RADIUS * c;
    }
    EMSCRIPTEN_KEEPALIVE
    LatLon new_position_with_geolib(double lat, double lon, double speed, double cog, double T) {
        const GeographicLib::Geodesic& geod = GeographicLib::Geodesic::WGS84();

        double s12 = speed * T;
        double lat2, lon2;

        // Direct method gives the destination point given start point, initial azimuth, and distance
        geod.Direct(lat, lon, cog, s12, lat2, lon2);
        return { lat2, lon2 };
    }
    EMSCRIPTEN_KEEPALIVE
    double new_distance(double T, double latA, double lonA, double speedA, double cogA, double latB, double lonB, double speedB, double cogB) {
        auto resA = new_position_with_geolib(latA, lonA, speedA, cogA, T);
        auto resB = new_position_with_geolib(latB, lonB, speedB, cogB, T);
        return haversine_distance(resA.lat, resA.lon, resB.lat, resB.lon);
    }

    EMSCRIPTEN_KEEPALIVE
    double* getCPA(double latA, double lonA, double speedA, double cogA, double latB, double lonB, double speedB, double cogB, double tcpa) {
        double RES_TCPA = INFINITY;
        double RES_DCPA = INFINITY;
        double prev_dist = INFINITY;
        double cur_dist = INFINITY;
        double* tdcpaPtr = new double[2];
        std::vector<int> status;
        int t_lim = tcpa * 60;
        int step = 1;
        if (t_lim > 600) {
            step = int(double(t_lim) / 300.0);
        }
        for (int t = 0; t < t_lim; t += step) {
            prev_dist = new_distance(t, latA, lonA, speedA, cogA, latB, lonB, speedB, cogB);
            cur_dist = new_distance(t + step, latA, lonA, speedA, cogA, latB, lonB, speedB, cogB);
            if (prev_dist < RES_DCPA) {
                RES_DCPA = prev_dist;
            }
            if (cur_dist - prev_dist <= 0) {
                if (status.size() == 0) {
                    status.emplace_back(-1);
                }
            }
            else {
                if (status.size() == 0) {
                    status.emplace_back(1);
                    break;
                }
                else {
                    if (status[0] == -1) {
                        status.emplace_back(1);
                    }
                }
            }
            if (status.size() == 2 && status[0] == -1 && status[1] == 1) {
                RES_TCPA = t;
                break;
            }
        }
        tdcpaPtr[0] = RES_TCPA;
        tdcpaPtr[1] = RES_DCPA;
        return tdcpaPtr;
    }

}

好了,现在就测试全部重新生成。如果没有报错,则通过测试。通过测试之后。证明可以大胆使用em++命令直接编译GeoCompute.cpp为wasm文件了。

打开cmd进入到EmscriptenTest工程根目录。

em++ GeoCompute.cpp -O1 -o libGeoCompute.js -s WASM=1 -s MODULARIZE=1 -s EXPORT_NAME="GeoComputeModule" -s "EXPORTED_FUNCTIONS=['_deg2rad', '_haversine_distance', '_new_position_with_geolib', '_new_distance', '_getCPA']" -s "EXPORTED_RUNTIME_METHODS=['ccall', 'cwrap']" -I D:/CppPkg/WinVcpkg/vcpkg/installed/x64-windows/include -L D:/HighPerformanceProjects/CppProjects/EmscriptenTest/lib -lGeographicLib

我这里导出了所有的函数,函数前面要加上下划线。但是在cmakelists.txt中不需要加下划线。
执行完成命令之后,会生成libGeoCompute.js和libGeoCompute.wasm文件。

在这里插入图片描述

现在可以使用npm创建一个原生的JavaScript项目。
这是工程目录结构:
在这里插入图片描述
最重要是main.js的写法:

const fs = require('fs');
const path = require('path');

// 加载 Emscripten 生成的模块
const Module = require('./libGeoCompute.js');

async function main() {
  try {
    const instance = await Module({
      locateFile: (filename) => path.join(__dirname, filename),
    });

    console.log('Module instance:', instance);
    console.log(typeof instance.cwrap)
    // await new Promise((resolve) => {
    //   console.log('Waiting for runtime initialization...');
    //   instance.onRuntimeInitialized = () => {
    //     console.log('Runtime initialized.');
    //     resolve();
    //   };
    // });

    // 使用 cwrap 包装 getCPA 函数
    const getCPA = instance.cwrap('getCPA', 'number', [
      'number', 'number', 'number', 'number', 'number', 'number', 'number', 'number', 'number'
    ]);
    console.log(typeof getCPA)
    // 调用导出的 getCPA 函数
    const resultPtr = getCPA(21.3058, 109.1014, 15.12, 187.13, 21.288205, 109.118725, 3.909777, 254.42, 6.5);
    console.log(typeof resultPtr)
    // 解析返回值(假设返回指向结构体的指针)
    const res_tcpa = instance.HEAPF64[resultPtr >> 3]; // 读取double指针的第0个元素
    const res_dcpa = instance.HEAPF64[(resultPtr >> 3) + 1]; // 读取地址偏移量+1

    console.log('TCPA:', res_tcpa);
    console.log('DCPA:', res_dcpa);

    console.log('Continuing after runtime initialization.');

    // 继续下面的逻辑...
  } catch (error) {
    console.error('Error:', error);
  }
}

main();

index.html的内容:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Blank Window</title>
</head>
<body>
  <!-- 这里可以添加窗体的内容 -->
</body>
</html>

package.json的内容:

{
  "name": "nodedevtest",
  "version": "1.0.0",
  "description": "A minimal Electron application",
  "main": "main.js",
  "scripts": {
    "start": "node main.js"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "axios": "^1.6.8",
    "electron": "^30.0.1",
    "pixi.js": "^8.1.0",
    "request": "^2.88.2"
  }
}

得出的结果:
在这里插入图片描述

得出的结果是154和1519.5687501879786
表示经过154秒以后,两船达到最小距离,并且最小距离为1519米多。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1863079.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

windows 安装 Kubernetes(k8s)

windows 安装 docker 详情见&#xff1a; https://blog.csdn.net/sinat_32502451/article/details/133026301 minikube Minikube 是一种轻量级的Kubernetes 实现&#xff0c;可在本地计算机上创建VM 并部署仅包含一个节点的简单集群。 下载地址&#xff1a;https://github.…

SSM 美食食谱分享推荐系统【附源码】

SSM 美食食谱分享推荐系统 本科毕业论文&#xff08;设计&#xff09;学术诚信声明 本人郑重声明&#xff1a;所呈交的毕业论文&#xff08;设计&#xff09;&#xff0c;是本人在导师的指导下&#xff0c;独立进行 研究工作所取得的成果。除文中已经注明引用的内容外&#xf…

嵌入式应用开发ADC学习笔记(立创STMF4开发板)

目录 #ADC #ADC学习相关涉及概念介绍 #ADC相关概念介绍 #软件实现 #常用结构体配置介绍 #常用函数介绍 #软件编写&#xff0c;完成效果。 #ADC #ADC学习相关涉及概念介绍 数字信号(Digital Signal)&#xff1a;数字信号用最为常见的二进制数来表示的信号&#x…

k8s部署mongodb副本集

1.什么mongodb&#xff1f; MongoDB 是一个基于分布式文件存储的数据库&#xff0c;由 C 语言编写&#xff0c;旨在为 WEB 应用提供可扩展的高性能数据存储解决方案。MongoDB 是一个介于关系数据库和非关系数据库之间的产品&#xff0c;是非关系数据库当中功能最丰富&#xff…

excel实现下拉筛选(超简单)

excel实现下拉筛选 引言1、需求&#xff1a;预警状态下的列 实现下拉筛选2、实现2.1、数据验证2.2、下拉筛选内容2.3、去掉预警状态单元格的下拉筛选 引言 通常&#xff0c;我们会单独新建一张sheet表 专门存每个列的下拉内容。下面我将专门建立一张名为代码表的sheet表来存放…

欧盟指控苹果应用商店规则非法压制竞争,面临巨额罚款风险

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

[Redis]事务

Redis事务 Redis 事务提供了一种将多个命令请求打包的功能。然后&#xff0c;再按顺序执行打包的所有命令&#xff0c;并且不会被中途打断。 但是&#xff0c;事务中的每条命令都会与 Redis 服务器进行网络交互&#xff0c;比较浪费资源 所以&#xff0c;日常开发中不建议使…

LeetCode题练习与总结:随机链表的复制--138

一、题目描述 给你一个长度为 n 的链表&#xff0c;每个节点包含一个额外增加的随机指针 random &#xff0c;该指针可以指向链表中的任何节点或空节点。 构造这个链表的 深拷贝。 深拷贝应该正好由 n 个 全新 节点组成&#xff0c;其中每个新节点的值都设为其对应的原节点的…

成功解决ES高亮内容引起的字段显示不一致问题

在处理搜索引擎&#xff08;如Elasticsearch&#xff09;结果时&#xff0c;常见需求之一是对用户搜索的关键词进行高亮显示&#xff0c;这有助于用户快速识别搜索结果为何与其查询相关。但在实际应用中&#xff0c;如果处理不当&#xff0c;直接使用高亮片段可能会导致原始数据…

冰淇淋PDF编辑器,轻量,无需安装,打开即用

​IceCream PDF Editor (冰淇淋PDF编辑器) 是一款简单实用的PDF文件编辑工具。功能包括&#xff1a;编辑文本、注释添加、页面管理、PDF文件保护等&#xff1b;操作简单&#xff0c;功能强大&#xff0c;使用户能够轻松编辑和修改PDF文件。 软件链接&#xff1a;轻量&#xff…

代码随想录算法训练营第四十五天| 198.打家劫舍,213.打家劫舍II ,337.打家劫舍III

198. 打家劫舍 - 力扣&#xff08;LeetCode&#xff09; class Solution {public int rob(int[] nums) {int[] dp new int[nums.length];if(nums.length 1){return nums[0];}dp[0] nums[0];dp[1] Math.max(nums[0],nums[1]);for(int i2;i<nums.length;i){dp[i] Math.ma…

【服务器】磁盘满载--docker 的日志文件太大造成满载

一.背景 早上过来测试反馈服务器都宕机了,访问不了。一看服务器磁盘都已经满了。所以开始清磁盘数据。 二.解决 主要查看下面目录情况: /home/libe/docker /containers /volumes /overlay21.查看磁盘情况 df -h/ du -a|sort -rn|…

国密SSL证书提升网络安全

随着数字化时代的到来&#xff0c;网络安全已经成为全球关注的焦点。在这种背景下&#xff0c;SSL证书作为保护数据传输安全的重要工具&#xff0c;其重要性日益凸显。 数字证书产品有以下几种类别&#xff1a; 单域名SSL证书&#xff1a;为单一网站提供安全保护。 多域名SS…

ICMAN液位检测——WS003B管道检测模组

ICMAN液位检测之WS003B管道检测模组 体积小&#xff0c;成本低&#xff0c; 液位检测精度高&#xff0c; 有水输出低电平无水高电平&#xff0c; 适用于饮水机、咖啡机、扫地机器人、洗地机等&#xff0c; 有需要朋友快联系我吧&#xff01; AWE展会不容错过的ICMAN检测模组…

sheng的学习笔记-AI-高斯混合模型(GMM)

AI目录&#xff1a;sheng的学习笔记-AI目录-CSDN博客 sheng的学习笔记-AI-聚类(Clustering)-CSDN博客需要学习前置知识&#xff1a; 聚类&#xff0c;可参考 sheng的学习笔记-AI-聚类(Clustering)-CSDN博客 EM算法&#xff0c;可参考 sheng的学习笔记-AI-EM算法-CSDN博客 贝…

python编写的多个FastApi接口如何批量运行

fastapi编写接口并批量运行 为什么要写这fastapi接口以及拿它做什么呢&#xff1f; fastapi可以快速构建你自己的api&#xff0c;前端后端联调时&#xff0c;后端接口还有做好&#xff0c;那么这个fastapi可以快速生成mock一些数据。 结合uvicorn这个python库使用起来很方便 为…

【Python实战因果推断】1_因果效应异质性1

目录 From ATE to CATE Why Prediction Is Not the Answer CATE and ITE 本文将介绍应用于行业的因果推理中最有趣的发展&#xff1a;效应异质性。在此之前&#xff0c;你们了解的是一种治疗方法的一般影响。现在&#xff0c;你将专注于发现它如何对不同的人产生不同的影响。…

【漏洞复现】用友 UFIDA saveDoc.ajax 任意文件上传漏洞

免责声明&#xff1a; 本文内容旨在提供有关特定漏洞或安全漏洞的信息&#xff0c;以帮助用户更好地了解可能存在的风险。公布此类信息的目的在于促进网络安全意识和技术进步&#xff0c;并非出于任何恶意目的。阅读者应该明白&#xff0c;在利用本文提到的漏洞信息或进行相关测…

web前端大作业-乡村扶贫、乡村振兴

文章目录 代码分析页面截图代码连接 代码分析 代码结构 主页index.html <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedge"><meta…

基于RabbitMQ原理的自定义消息队列实现

文章目录 1. 什么是消息队列2. 需求分析2.1. 核心概念12.2. 核心概念22.3. 核心API2.4. 交换机类型2.5. 持久化2.6. 网络通信2.7. 总结 3. 创建核心类3.1. Exchange3.2. MSGQueue3.3. Binding3.4. Message3.5. 数据库操作3.5.1. 建表操作3.5.2. 交换机操作3.5.3. 队列操作3.5.4…