干货十足,惊为佳作,五年“划水”经验总结(持续更新)
29.[B/S模式]快速构建发布
先安装nodejs,再用vscode打开前端vue文件夹,// install dependencies [npm install] 。
// develop [npm run dev] ## Build [npm run build]。
build之后会有一个dist文件夹,部署nginx,配置conf文件夹中内nginx.conf文件,然后start nginx。
28.脱离IDE工具编译,使用cmake+msbuild一键发布部署(入门级)
前提条件:
环境变量[path] -> cmake路径:D:\Program Files\CMake\bin
环境变量[path] -> msbuild路径:D:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\MSBuild\15.0\Bin\amd64
--------备注(一定要确认好本机上安装的是专业版还是企业版,否则会报错某个文件缺失)
具体步骤:
第一步:新建一个build目录
示例:E:\cmake_sys\gtest-cmake-example-master\build
第二步:win+r 打开命令行界面 输入
cd /d E:\cmake_sys\gtest-cmake-example-master\build
第三步:在命令行界面 输入
cmake -G "Visual Studio 15 2017 Win64" E:\cmake_sys\gtest-cmake-example-master
第四步:在命令行界面 输入
msbuild ALL_BUILD.vcxproj /t:Rebuild /p:configuration=RelWithDebInfo
第五步:写bat脚本,一键发布部署
@echo off setlocal enabledelayedexpansion echo currentBatDir:%~dp0 set vcxprojDir=%~dp0%build mkdir %vcxprojDir% cd /d %vcxprojDir% cmake -G "Visual Studio 15 2017 Win64" %~dp0 msbuild ALL_BUILD.vcxproj /t:Rebuild /p:configuration=RelWithDebInfo echo build ok
pause exit
27.使用QT开源接收机软件(Gqrx),搭配硬件RTL2832U RTL-SDR USB
软件无线电,这款软件主要就是频谱图+瀑布图展示,观察信号频率。对于模拟信号进行识别解调获取音频数据。
26.使用json_rpc_cxx框架
类似于rest_rpc,但是个人认为这个框架水平要高一些(在代码编写层次上)
核心代码如下:
1 JsonRpc2Server rpcServer; 2 3 // Bindings 4 WarehouseServer app; 5 rpcServer.Add("GetProduct", GetHandle(&WarehouseServer::GetProduct, app), {"id"}); 6 rpcServer.Add("AddProduct", GetHandle(&WarehouseServer::AddProduct, app), {"product"}); 7 rpcServer.Add("AllProducts", GetHandle(&WarehouseServer::AllProducts, app), {}); 8 9 // 本地跨进程调用 10 cout << "Running in-memory example" << "\n"; 11 InMemoryConnector inMemoryConnector(rpcServer); 12 doWarehouseStuff(inMemoryConnector); 13 14 // 网络跨进程调用(服务端) 15 cout << "Running http example" << "\n"; 16 CppHttpLibServerConnector httpServer(rpcServer, 10086); 17 cout << "Starting http server: " << std::boolalpha << httpServer.StartListening() << "\n"; 18 // 网络跨进程调用(客户端) 19 CppHttpLibClientConnector httpClient("localhost", 10086); 20 std::this_thread::sleep_for(0.5s); 21 doWarehouseStuff(httpClient);
统一抽象层,和普通函数类似的无差别调用
1 void doWarehouseStuff(IClientConnector &clientConnector) 2 { 3 JsonRpcClient client(clientConnector, version::v2); 4 WareHouseClient appClient(client); 5 Product p; 6 p.id = "0xff"; 7 p.price = 22.4; 8 p.name = "Product 1"; 9 p.cat = category::cash_carry; 10 // 抽象层。新增“产品”,已存在的对象会执行失败 11 cout << "Adding product: " << std::boolalpha << appClient.AddProduct(p) << "\n"; 12 13 // 抽象层。获取指定“产品”(存在的情况) 14 Product p2 = appClient.GetProduct("0xff"); 15 cout << "Found product: " << p2.name << "\n"; 16 try { 17 // 抽象层。获取指定“产品”(不存在的情况) 18 appClient.GetProduct("0xff2"); 19 } catch (JsonRpcException &e) { 20 cerr << "Error finding product: " << e.what() << "\n"; 21 } 22 23 // 抽象层。获取所有“产品” 24 auto all = appClient.AllProducts(); 25 for (const auto &p: all) { 26 cout << p.name << endl; 27 } 28 }
25.编译并使用Nulloy
使用带有波形进度条的控件,让声音可视化,不做盲目的“管理者”。(使用qmake)
24.编译并使用QGIS
源代码编译预计俩个半小时,根据电脑配置有所差异。准备编译环境需要半天,有一点费功夫。(使用cmake)(RelWithDeb:生成可调试的release发布版本)
23.使用rest_rpc框架
基本执行流程:服务端将内部实现的函数以字符串的形式注册在缓存中,等待客户端远程调用触发;而客户端写回调函数来响应服务端的执行结果。具体效果如下:
(业务场景是,客户端将一个字符串参数通过该框架传递给相应的功能函数(在服务端的两个函数分别为:echo和async_echo),通过回调函数收到结果后进行展示。)
客户端调用代码如下:
1 rpc_client client; 2 bool r = client.connect("127.0.0.1", 9000); 3 4 for (size_t i = 0; i < 5; i++) { 5 std::string test = "test" + std::to_string(i + 1); 6 // set timeout 100ms 7 client.async_call<100>( 8 "async_echo", 9 [](const asio::error_code &ec, string_view data) { 10 if (ec) { 11 std::cout << ec.value() << "async_echo timeout" << std::endl; 12 return; 13 } 14 15 auto str = as<std::string>(data); 16 std::cout << "echo [async_echo]" << str << '\n'; 17 }, 18 test); 19 20 std::string test1 = "test" + std::to_string(i + 2); 21 // zero means no timeout check, no param means using default timeout(5s) 22 client.async_call<0>( 23 "echo", 24 [](const asio::error_code &ec, string_view data) { 25 auto str = as<std::string>(data); 26 std::cout << "echo [echo]" << str << '\n'; 27 }, 28 test1); 29 } 30 31 client.run();
22.使用CMAKE编译开源库
第一步,找到CMakeLists.txt文件,注意所在的文件夹。新建一个dir-build
第二步,打开cmake软件,我这里用的3.18.0,配置好源码路径、生成文件路径
第三步,在cmake软件,配置好所需的编译器。软件自动转换,将会生成以下内容
第四步,在cmake软件,点击“generate”按钮,再点击“open project”按钮,工程将自动用VS打开
第五步,在VS点击构建,将会编译生成可执行文件
21.使用MQTT通信
前期需要第三方服务端,我选择了EMQ代理服务。主要使用浏览器打开 http://localhost:18083,能够看见连接、主题等等,方便测试
当前我推荐(依赖库)Qmqtt
win32:CONFIG(release, debug|release): LIBS += -L$$PWD/lib/ -lQt5Qmqtt else:win32:CONFIG(debug, debug|release): LIBS += -L$$PWD/lib/ -lQt5Qmqttd else:unix: LIBS += -L$$PWD/lib/ -lQt5Qmqtt INCLUDEPATH += $$PWD/include DEPENDPATH += $$PWD/include
代码示例:(连接信息仅需IP+端口即可)
QMQTT::Client *client = new QMQTT::Client(QHostAddress::LocalHost, 1883); client->setClientId("lzw"); client->setUsername("admin"); client->setPassword("public"); client->connectToHost();
注意一下Qos:
QOS0不可靠,因此适合大量数据的传输,因为很大量的数据,完全避免不丢包是很难的(网络环境、现实环境);
QOS1可靠,一般的场景够用,因为总能接到数据嘛。缺点就是 “可能造成” 一条数据,接收到了多次;
QOS2严格可靠,保证相同的消息只接收一条,耗性能。
20.使用Redis数据库
前期准备工作,需要下载解压Redis的Windows安装包。现在我们开始体验:
当前我推荐(依赖库)
INCLUDEPATH += Depends\include
LIBS += -L$$DESTDIR -llib_acl_cpp_d
代码示例:(连接信息仅需IP+端口即可)(这里加入键值对:test_key - test_value)
#include "acl_cpp/lib_acl.hpp"
int run() { // init socket module for windows acl::acl_cpp_init(); const char* redis_addr = "127.0.0.1:6379"; int conn_timeout = 10, rw_timeout = 10; // the redis client connection acl::redis_client conn(redis_addr, conn_timeout, rw_timeout); const char* key = "test_key"; // test redis STRING command // bind redis_string command with redis connection acl::redis_string cmd_string(&conn); test_redis_string(cmd_string, key); // test redis KEY command with the same redis connection acl::redis_key cmd_key(&conn); test_redis_key(cmd_key, key); return 0; }
19.了解一下memcpy_s,以便看懂他人代码
某个程序的稳定型,主要还是看代码逻辑,虽然有这些安全函数,但也不能过分依赖
char in_src[10]{0}; char in_no = -1 * -1; in_src[in_no]= -100; int in_desLen = 9; char *in_des = new char[in_desLen]; QByteArray in_checkValue = QByteArray::fromRawData(in_des, in_desLen); qDebug()<<in_checkValue.length(); // in_desLen>=in_srcLen时,copy成功,返回值0 // in_desLen<in_srcLen时,copy失败,整个数组赋值为0,返回值非0 auto in_rlt = memcpy_s(in_des, in_desLen, in_src, 10); qDebug()<<in_rlt; in_checkValue = QByteArray::fromRawData(in_des, in_desLen); qDebug()<<in_checkValue.length();
18.使用深拷贝,更改对象所属权
/*************************************************************** * @brief 获取深拷贝对象,跨线程使用 * @date 2022-01-23 ***************************************************************/ stuLog *TestHelper::GetCloneObj(stuLog *in_orgInfo) { stuLog *in_newInfo = new stuLog(); *in_newInfo = *in_orgInfo; return in_newInfo; }
注意:所定义类中不能含有指针
17.使用线程安全队列,控制界面绘图速度
//模拟标识 int g_num = 1; /*************************************************************** * @brief 模拟其他组件产生数据 * @date 2022-01-22 ***************************************************************/ void TestHelper::RunExecWrite() { while(1) { //if (g_num>50) break; stuLog in_info; in_info.id = g_num++; in_info.name = "hello www"; this->AddData(in_info); QThread::msleep(20); } } /*************************************************************** * @brief 实际应用,其他组件传来数据 * @date 2022-01-22 ***************************************************************/ void TestHelper::AddData(stuLog &in_data) { bool in_rlt = m_queue->push(in_data); if (!in_rlt) qDebug()<<QDateTime::currentDateTime().toString("mm:ss.zzz")<<"AddData:"<<in_rlt<<" "<<in_data.id; } /*************************************************************** * @brief 实际应用,在界面上消费(避免界面卡顿,用户体验差) * @date 2022-01-22 ***************************************************************/ void TestHelper::RunExecRead() { while(1) { QList<stuLog> in_list; if (m_queue->length()==0) { // 信号量阻塞,直到有新数据收到 stuLog in_info; m_queue->pop(in_info); // 有新数据来了 in_list.append(in_info); } for(qint32 in_idx=0; in_idx<m_queue->length(); ++in_idx) { stuLog in_info; bool in_rlt = m_queue->pop(in_info); if (in_rlt) in_list.append(in_info); } qDebug()<<QDateTime::currentDateTime().toString("mm:ss.zzz")<<"RunExecRead:"<<in_list.length(); //SubData(in_list); QThread::msleep(200); } }
16.kafka入门实践(默认需要安装java服务端)
客户端可以选择java,csharp,c++。支持跨平台,客户端上线数据回滚,毕竟是分布式持久化神器哈。
使用时,一般先创建一个主题,客户端(生产者)与客户端(消费者)通过主题进行通信。
它们之间的内部依赖关系如下。zkserver(java),kafka服务端(java),客户端(生产者),客户端(消费者)
可执行程序目录如下:
关键代码如下:
1 static void Main(string[] args) 2 { 3 do 4 { 5 Produce(GetKafkaBroker(), getTopicName()); 6 System.Threading.Thread.Sleep(3000); 7 } while (true); 8 }
1 static void Main(string[] args) 2 { 3 Consume(getKafkaBroker(), getTopicName()); 4 }
15.C++实现类成员模板函数(自定义结构体)
入口统一,模拟接收了很多报文,都将进行数据预处理,最后再将结果统一输出,出口统一
void TestTemplate::PrintSomething(const QString &in_recvMsg) { qDebug()<<in_recvMsg; TemplateA in_tplA; in_tplA.CarName = "LZW Car"; HandleData<TemplateA>(&in_tplA); TemplateB in_tplB; in_tplB.PhoneName = "LZW Phone"; HandleData<TemplateB>(&in_tplB); } template <typename T> void TestTemplate::HandleData(T *in_info) { QString in_rlt = "Nice"; // 方式一 if (std::is_same<T, TemplateA>::value) { TemplateA *in_tpA = (TemplateA*)(in_info); if (in_tpA) { qDebug()<<"CarName:"<<in_tpA->CarName; in_rlt.prepend(in_tpA->CarName); } } // 方式二 QString in_className = typeid(in_info).name(); if (in_className.contains("TemplateA")) { TemplateA *in_tpA = (TemplateA*)(in_info); if (in_tpA) { qDebug()<<"CarName:"<<in_tpA->CarName; in_rlt.prepend(in_tpA->CarName); } } else if (in_className.contains("TemplateB")) { auto in_tpB = (TemplateB*)(in_info); if (in_tpB) { qDebug()<<"PhoneName:"<<in_tpB->PhoneName; in_rlt.prepend(in_tpB->PhoneName); } } } template<typename T> T TestTemplate::GetObj(const T &in_info) { return in_info; }
14.C++(qt)实现类似于CSharp的lambda查找(自定义结构体)
主要避免了写for循环,至于查找速度上,也快一点
//1 struct person { QString name; int age; QString note; int cost; person() { } person(const QString &in_name, int in_age) { name = in_name; age = in_age; } //2 bool operator == (const person &in_item) { return name.compare(in_item.name) == 0; } }; /*************************************************************** * @brief 自定义结构体查找 * @date 2021-12-29 ***************************************************************/ void FindDefineStuList() { QList<person> in_list; in_list.append(person("lzw1", 18)); in_list.append(person("lzw2", 19)); in_list.append(person("lzw3", 20)); // 找这个人的年龄? person in_objAm; in_objAm.name = "lzw2"; QList<person>::iterator in_rlt = qFind(in_list.begin(), in_list.end(), in_objAm); if (in_rlt!=in_list.end()) { qDebug()<<in_objAm.name<<":"<<in_rlt[0].age; } }
13.使用zmq业务数据交互通信(请求-应答模式)与并行计算通信(任务-管道模式)
结合具体场景使用。
12.使用zmq轻巧业务应用通信(发布-订阅模式)
结合具体场景使用。大部分用于数据分发的场景
11.使用OSG-Earth玩转三维地图(离线加载)
主要效果如下,由于个人对c++宠爱多一点,所以就没玩 cesium.js,使用的传统的cs桌面软件形式
10.使用QMapcontrol体验轻量级的二维地图(离线加载)
由于直接依赖qt,所有用得简洁轻便,传统的cs桌面软件形式
9.快速输出C++结构体成员名与值,类型?
#include <iostream> #include <string> #include <typeinfo> //获取变量的字面名字 #define Name(X) #X //打印变量的字面名字与值 #define ShowName(X) {std::string xname=Name(X); \ std::cout<<xname.substr(2,xname.size()-1)<<":"<<X<<std::endl; \ } //打印变量的类型 function { int x=0; std::cout<<typeid(x).name()<<std::endl; }
8.机器学习是选择tensorflow还是tensorflow lite了?
个人觉得还是结合实际场景进行选择。
TensorFlow 是谷歌开源的人工智能库,有最完善的生态支持,是进行人工智能领域开发和科研的必备工具。主要还是桌面端,linux居多
而TensorFlow Lite 是一种用于设备端推断的开源深度学习框架,同样也是跨平台。看了一下现在的桌面QQ也在使用了,注意保护个人隐私了。
7.基于QT插件系统,自定义开发业务框架?
//! ------>当前框架系统已集成 **********内部组件通信、外部进程通信、写日志、数据库ORM********** ! <------//
//! ------>近期将集成二维地图插件<------//
6.GIS,WGS84(世界通用坐标系)与GCJ02(火星坐标系)?
WGS-84 到 GCJ-02 的转换(即 GPS 加偏)算法是一个普通青年轻易无法接触到的“公开”的秘密。
目前可用于WGS84的共用地图,谷歌卫星地图、ArcGIS卫星地图和必应卫星地图
目前可用于GCJ02的共用地图,高德地图(互联网应用在本朝必须使用该坐标系)
1、高德地图:
【境内】:GCJ-02 WGS-84——>GCJ-02(高德有接口提供,反过来没有) 【境外】:暂不支持
AMap 就是高德地图,是高德地图在纳斯达克上市用的名字,主要面向互联网企业或个人提供免费API服务
MapABC 是高德集团底下的图盟公司,主要面向大众型企业或政府机关,并提供付费的有偿服务
Amap和MapABC,数据和服务都是共享的,所以Mapabc用Amap的API是正常的
2、百度地图
【境内(包括港澳台)】:BD09 在GCJ-02坐标系基础上再次加密 支持WGS-84、GCJ-02转换成BD09,反向不支持,并且批量转换一次有条数限制 【境外】:WGS-84
3、google地图
【境内】:GCJ-02 数据来源于高德,两者互通 【境外】:WGS-84
4、微软Bing地图:BingMap
【全球统一】:WGS-84
5、腾讯地图:SoSo地图
【境内】:GCJ02
坐标系知识回顾如下:
WGS84
WGS84坐标系是为GPS全球定位系统使用而建立的坐标系统,通过遍布世界的卫星观测站观测到的坐标建立,其初次WGS84的精度为1-2m。
瓦片z=0,x=0,y=0,对应的坐标范围是经度[-180,180],纬度[-85.05,85.05],对应的瓦片尺寸是256像素*246像素。
经纬度坐标(lng,lat)转瓦片坐标(tileX,tileY)的公式如下:
tileX=int( (lng+180)/360*2^z)
tileY=(1- arsinh(tan(lat*π/180))/π) *2^(z-1)
其中z是zoom level,计算结果取整数。
其中反双曲正弦函数arsinh(x),等价于ln(x+(x^2+1)^0.5)。
瓦片坐标(tileX,tileY)转经纬度坐标(lng,lat)公式如下(计算结果为该瓦片对应的最小经度和最大纬度):
lng=tileX/2^z * 360-180
lat=arctan(sinh(π*(1-2*tileY/2^z)))*180/π
其中,z是zoom level,双曲函数sinh(x)=(e^x-e^(-x))*0.5。
经纬度坐标(lng,lat)转像素坐标(pixelX,pixelY)公式如下:
pixelX=(lng+180)/360*2^z*256%256
pixelY=(1-ln(tan(lat*π/180)+sec(lat*π/180))/(2*π))*2^z*256%256
公式中设定瓦片图块尺寸为256*256pixel,z是zoom level,web墨卡托平面坐标除以像素分辨率,再除以256取余就是像素坐标。
瓦片坐标(tileX,tileY)的像素坐标(pixelX,pixelY)转经纬度坐标(lng,lat)公式如下:
lng=(tileX+pixelX/256)/2^z*360-180
lat=arctan(sinh(π-2*π*(tileY+pixelY/256)/2^z))*180/π
其中,z是zoom level。
GCJ02
GCJ-02是由中国国家测绘局(G表示Guojia国家,C表示Cehui测绘,J表示Ju局)制订的地理信息系统的坐标系统。凡是公开发布的商业互联网地图,一定要在此加密算法的基础上进行发布,这样一来地图的坐标就与实地的坐标不相符了,于是大家把这种坐标戏称为"火星坐标"。
5.GIS,小数点度 转 度分秒?
//12.2436° --> 12°14'36″
QString convDecimaltoDMS(double in_dNum)
{
//返回小于或等于指定数字的最大整数
int in_dValue = (int)floor(in_dNum);
double x = (in_dNum-in_dValue)*60;
int in_mValue = (int)floor(x);
double in_sValue = (x-in_mValue)*60;
//返回结果,字符串格式化,不区分经纬,NE-SW
QString in_ret = QString("%1°%2′%3″").arg(in_dValue)
.arg(in_mValue, 2, 10, QChar('0'))
.arg((int)in_sValue, 2, 10, QChar('0'));
return in_ret;
}
4.根据业务需要,如何进行三表查询?
前提条件:股市数据表:tbStockMarket(主表),股票详情表:tbStockAttach,个人关注股票表:tbStockConcern
业务需要:查看最近个人关注的某只股票的股市价格走势,并带有股票信息
使用等值连接,SQL语句如下:
SELECT
A.ID, A.Name, A.Price, A.CurrentDate, B.Note, C.Grade
FROM tbStockMarket AS A
INNER JOIN tbStockAttach AS B ON (A.Code = B.Code)
INNER JOIN tbStockConcern AS C ON (C.Code = B.Code)
WHERE C.Code = '600039' ORDER BY A.CurrentDate
成功执行该语句,符合预期
SQL知识回顾如下:
left join(左联接) 返回包括左表中的所有记录和右表中联结字段相等的记录
right join(右联接) 返回包括右表中的所有记录和左表中联结字段相等的记录
inner join(等值连接) 只返回两个表中联结字段相等的行
3.QTreeWidget排序后,如何使每行序号不发生改变?
第一步:初始化首列为序号,并默认赋值
QTreeWidgetItem *in_viewItem = new QTreeWidgetItem;
in_viewItem->setData(0, 0, ++in_idx); //注释:in_idx是最初的默认排序
第二步:关联信号槽,排序后人工重置序号
connect(ui->tw_dataMarket->header(), &QHeaderView::sortIndicatorChanged,
this, [=]() {
for (int in_idx=0; in_idx<ui->tw_dataMarket->topLevelItemCount(); ++in_idx)
{
ui->tw_dataMarket->topLevelItem(in_idx)->setData(0,0,in_idx+1);
} });
效果如下:
2.win下面同一套代码,linux下编译成功后,竟然找不到同级目录的共享库?
在项目工程pro文件下,加上RPATH命令:
unix:!mac: QMAKE_LFLAGS += -Wl,--rpath=./
执行qmake,重新构建即可
第一步:现象重现,找出哪个倒霉蛋
第二步:走回理想状态,破镜重圆
1.使用虚拟机之前还能和宿主机通信,后来突然就不行了,怎么回事?
虚拟机指定的物理网卡和宿主机使用的物理网卡不一样,需要修改虚拟机的物理网卡后重新启动
第一步:关闭虚拟机操作系统
第二步:修改虚拟机的物理网卡,之后重新启动
第三步:打通任督二脉,状态回升
---------------->版本迭代中,最近更新日期:2023-04-29,五一快乐<----------------
---------------->版本迭代中,最近更新日期:2022-06-12,又是一年高考季<----------------
---------------->版本迭代中,最近更新日期:2022-11-12,双十一刚过<----------------
---------------->版本迭代中,最近更新日期:2023-03-04,离职待业<----------------
//! *****工作已稳定,目前就职于成都博宇利华。五年如一日,技术交流 -> qq邮箱 1358849798@qq.com,非诚勿扰***** !//