-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathindex.html
848 lines (700 loc) · 217 KB
/
index.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
<!DOCTYPE html>
<html lang="zh-cn">
<head>
<meta name="generator" content="Hugo 0.118.2">
<link rel="apple-touch-icon" sizes="180x180" href="/images/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="32x32" href="/images/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="/images/favicon-16x16.png">
<link rel="manifest" href="/images/site.webmanifest">
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="hbb's blog">
<title>Home | hbb's blog</title>
<link rel="stylesheet" href="/css/style.css" />
<link rel="stylesheet" href="/css/fonts.css" />
<link rel="stylesheet" href="/css/theme-override.css">
<header>
<nav>
<ul>
<li class="pull-left current">
<a href="/">~/hbb's blog</a>
</li>
<li class="pull-left ">
<a href="/tags/">~/tags</a>
</li>
<li class="pull-right">
<a href="/index.xml">~/rss</a>
</li>
<li class="pull-right">
<a href="/about">~/about me</a>
</li>
</ul>
</nav>
</header>
</head>
<body>
<ul>
<form id="search" class="pull-right"
action='/search/' method="get">
<label hidden for="search-input">Search site</label>
<input type="text" id="search-input" name="query"
placeholder="Type here to search">
<input type="submit" value="search">
</form>
</ul>
<br/>
<div class="content-wrapper">
<!--
<img
id="foxy"
src="/images/grayf0x.jpg"
alt="A foxy face">
-->
<h2 id="欢迎来到我的博客">欢迎来到我的博客</h2>
<img src="/images/partywizard.gif">
<hr>
<h2 id="posts">Posts:</h2>
<ul>
<li>
<span class="date">2024/12/29</span>
<a href="/post/modbus_rtu%E4%BB%8E%E7%AB%99%E4%BB%BF%E7%9C%9F/">modbus_rtu从站仿真</a>
</li>
<li>
<span class="date">2024/12/28</span>
<a href="/post/wsl2%E8%AE%BE%E7%BD%AE%E7%BD%91%E7%BB%9C%E6%A1%A5%E6%8E%A5%E5%88%B0%E7%89%A9%E7%90%86%E7%BD%91%E5%8D%A1/">wsl2设置网络桥接到物理网卡</a>
</li>
<li>
<span class="date">2024/10/26</span>
<a href="/post/raspberry-pi5-%E5%BC%80%E5%8F%91%E7%AC%94%E8%AE%B0/">raspberry-pi5-开发笔记</a>
</li>
<li>
<span class="date">2024/10/20</span>
<a href="/post/windows_wsl2_nvidia-docker2%E9%95%9C%E5%83%8F%E8%AE%B0%E5%BD%95/">windows_wsl2_nvidia-docker2镜像记录</a>
</li>
<li>
<span class="date">2024/10/06</span>
<a href="/post/%E4%BD%BF%E7%94%A8x11%E8%BD%AC%E5%8F%91%E5%9C%A8macos%E4%B8%8A%E4%BB%8Edocker%E5%AE%B9%E5%99%A8%E4%B8%AD%E5%90%AF%E5%8A%A8gui%E7%A8%8B%E5%BA%8F/">使用x11转发在macos上从docker容器中启动gui程序</a>
</li>
<li>
<span class="date">2024/07/27</span>
<a href="/post/radxa-rock5c-%E5%BC%80%E5%8F%91%E7%AC%94%E8%AE%B0/">radxa-rock5c-开发笔记</a>
</li>
<li>
<span class="date">2024/07/07</span>
<a href="/post/amd-zynq-freertos-%E5%BC%80%E5%8F%91%E7%AC%94%E8%AE%B0/">amd-zynq-freeRTOS-开发笔记</a>
</li>
<li>
<span class="date">2024/06/16</span>
<a href="/post/vim-ctag-%E7%AC%A6%E5%8F%B7%E6%9F%A5%E6%89%BE/">vim-ctag-符号查找</a>
</li>
<li>
<span class="date">2024/05/01</span>
<a href="/post/nvidia-jetson%E5%BC%80%E5%8F%91%E7%AC%94%E8%AE%B0/">nvidia-jetson开发笔记</a>
</li>
<li>
<span class="date">2024/04/04</span>
<a href="/post/cpp%E8%B7%A8%E5%B9%B3%E5%8F%B0%E5%85%B1%E4%BA%AB%E5%86%85%E5%AD%98%E8%BF%9B%E7%A8%8B%E9%80%9A%E8%AE%AF/">cpp跨平台共享内存进程通讯</a>
</li>
<li>
<span class="date">2024/03/24</span>
<a href="/post/boost-asio-udp-%E5%90%8C%E6%AD%A5%E8%B6%85%E6%97%B6%E9%99%B7%E9%98%B1/">boost-asio-udp-同步超时陷阱</a>
</li>
<li>
<span class="date">2024/03/23</span>
<a href="/post/forgejo-drone-ci-cd/">forgejo-drone-ci-cd</a>
</li>
<li>
<span class="date">2024/02/07</span>
<a href="/post/ubuntu%E5%AE%89%E8%A3%85nvidia%E6%98%BE%E5%8D%A1%E9%A9%B1%E5%8A%A8/">ubuntu安装nvidia显卡驱动</a>
</li>
<li>
<span class="date">2024/02/07</span>
<a href="/post/ubuntu%E5%AE%89%E8%A3%85%E5%A4%9A%E4%B8%AAgcc%E7%89%88%E6%9C%AC/">ubuntu安装多个gcc版本</a>
</li>
<li>
<span class="date">2024/01/31</span>
<a href="/post/cmake_vcpkg_meson%E4%BA%A4%E5%8F%89%E7%BC%96%E8%AF%91arm64_raspberry_pi%E7%A8%8B%E5%BA%8F/">cmake_vcpkg_meson交叉编译arm64_raspberry_pi程序</a>
</li>
<li>
<span class="date">2024/01/13</span>
<a href="/post/ubuntu16.04%E8%AE%BE%E7%BD%AEcore-dump/">ubuntu16.04设置core dump</a>
</li>
<li>
<span class="date">2023/12/21</span>
<a href="/post/%E5%8A%A8%E6%80%81%E5%8A%A0%E8%BD%BD%E5%8A%A8%E6%80%81%E5%BA%93%E5%8F%8A%E5%BA%93%E5%86%85c%E9%A3%8E%E6%A0%BC%E6%96%B9%E6%B3%95/">动态加载动态库,及库内c风格方法</a>
</li>
<li>
<span class="date">2023/12/09</span>
<a href="/post/qt5-cmake-qml%E5%A4%9A%E8%AF%AD%E8%A8%80%E5%AE%9E%E8%B7%B5/">qt5-cmake-qml多语言实践</a>
</li>
<li>
<span class="date">2023/11/30</span>
<a href="/post/ubuntu16.04%E7%BC%96%E8%AF%91qt5.15%E7%9A%84qt-creator/">ubuntu16.04编译qt5.15的qt creator</a>
</li>
<li>
<span class="date">2023/11/13</span>
<a href="/post/vcpkg%E8%B5%84%E4%BA%A7%E7%BC%93%E5%AD%98%E4%B8%8E%E4%BA%8C%E8%BF%9B%E5%88%B6%E7%BC%93%E5%AD%98/">vcpkg资产缓存与二进制缓存</a>
</li>
<li>
<span class="date">2023/10/06</span>
<a href="/post/%E5%88%9B%E5%BB%BAvcpkg%E7%A7%81%E6%9C%89%E4%BB%93%E5%BA%93/">创建vcpkg私有仓库</a>
</li>
<li>
<span class="date">2023/09/27</span>
<a href="/post/%E4%B8%BAdrone%E5%88%B6%E4%BD%9Cqt%E7%BC%96%E8%AF%91%E6%89%93%E5%8C%85%E7%9A%84ubuntu%E7%8E%AF%E5%A2%83%E7%9A%84docker%E9%95%9C%E5%83%8F/">为drone制作qt编译打包的ubuntu环境的docker镜像</a>
</li>
<li>
<span class="date">2023/09/17</span>
<a href="/post/cpp%E8%B7%A8%E5%B9%B3%E5%8F%B0%E9%A1%B9%E7%9B%AE%E5%9C%A8windows%E4%B8%8A%E7%9A%84%E6%96%87%E6%9C%AC%E7%BC%96%E7%A0%81%E9%97%AE%E9%A2%98/">cpp跨平台项目在windows上的文本编码问题</a>
</li>
</ul>
</div>
<footer>
<script>
(function() {
function center_el(tagName) {
var tags = document.getElementsByTagName(tagName), i, tag;
for (i = 0; i < tags.length; i++) {
tag = tags[i];
var parent = tag.parentElement;
if (parent.childNodes.length === 1) {
if (parent.nodeName === 'A') {
parent = parent.parentElement;
if (parent.childNodes.length != 1) continue;
}
if (parent.nodeName === 'P') parent.style.textAlign = 'center';
}
}
}
var tagNames = ['img', 'embed', 'object'];
for (var i = 0; i < tagNames.length; i++) {
center_el(tagNames[i]);
}
})();
function createCopyButton(highlightDiv) {
const button = document.createElement("button");
button.className = "copy-code-button";
button.type = "button";
button.innerText = "Copy";
button.addEventListener("click", () => copyCodeToClipboard(button, highlightDiv));
highlightDiv.insertBefore(button, highlightDiv.firstChild);
const wrapper = document.createElement("div");
wrapper.className = "highlight-wrapper";
highlightDiv.parentNode.insertBefore(wrapper, highlightDiv);
wrapper.appendChild(highlightDiv);
}
document.querySelectorAll(".highlight").forEach((highlightDiv) => createCopyButton(highlightDiv));
async function copyCodeToClipboard(button, highlightDiv) {
const codeToCopy = highlightDiv.querySelector(":last-child > .chroma > code").innerText;
try {
var result = await navigator.permissions.query({ name: "clipboard-write" });
if (result.state == "granted" || result.state == "prompt") {
await navigator.clipboard.writeText(codeToCopy);
} else {
copyCodeBlockExecCommand(codeToCopy, highlightDiv);
}
} catch (_) {
copyCodeBlockExecCommand(codeToCopy, highlightDiv);
} finally {
button.blur();
button.innerText = "Copied!";
setTimeout(function () {
button.innerText = "Copy";
}, 2000); }
}
function copyCodeBlockExecCommand(codeToCopy, highlightDiv) {
const textArea = document.createElement("textArea");
textArea.contentEditable = "true";
textArea.readOnly = "false";
textArea.className = "copyable-text-area";
textArea.value = codeToCopy;
highlightDiv.insertBefore(textArea, highlightDiv.firstChild);
const range = document.createRange();
range.selectNodeContents(textArea);
const sel = window.getSelection();
sel.removeAllRanges();
sel.addRange(range);
textArea.setSelectionRange(0, 999999);
document.execCommand("copy");
highlightDiv.removeChild(textArea);
}
</script>
<hr/>
蚓无爪牙之利,筋骨之强,上食埃土,下饮黄泉,用心一也
</footer>
</body>
</html>
<script>
window.store = {
"\/": {
"title": "Home",
"tags": [],
"content": " 欢迎来到我的博客 Posts: ",
"url": "\/"
},
"\/post\/modbus_rtu%E4%BB%8E%E7%AB%99%E4%BB%BF%E7%9C%9F\/": {
"title": "modbus_rtu从站仿真",
"tags": ["ModbusRTU","Simulation",],
"content": "前言 上周,开发了一个可视化配置的modbus模块,用于和电气部门的西门子plc的modbus RTU slave通讯.\n实现主要使用qt的qmodbusslave相关的类, 细节不再展开\n由于电气工程师们也是第一次使用modbus RTU模块, 对配置不熟悉,所以我需要自己仿真通讯,验证程序逻辑\n主要遇到的问题是, modbus RTU是基于串口的, 如果是modbus tcp直接通过已有的网络仿真 modbus slave就可以,但电脑本身没有串口,要怎么仿真 modbus slave运行,以验证自己实现的modbus master逻辑呢.\n通过一周下来的摸索,我找到3种方法\n方法1 qt5中,modbus RTU的从站对应的c++类是 QModbusRtuSerialMaster , modbus Tcp从站应对的c++类则是QModbusTcpClient. 它们都继承自QModbusClient.\n这两个除了连接的接口不同,上层封装的接口没有任何区别,包括读写,状态,错误接口\n所以可以先用QModbusTcpClient类 + 一个仿真的modbus tcp master, 就可以验证了\n可以直接从qt 5安装的exmaple中找到 modbus slave代码,编译了它你就拥有一个仿真用的modbus 从站\n配置好环境编译后直接运行以验证\n这个方法的缺点是串口连接的逻辑没有办法验证\n有一个坑是 QModbusRtuSerialMaster连接的方法connectDevice成功是同步返回的,不像 QModbusTcpClient连接后立即返回,\n如果用事件循环进一步封装需要注意,否则会导致事件循环永退出在开始之前,调用modbus 连接的线程一直挂起\n我就在这个问题浪费一下午,痛定思痛才摸索出第2种和第3种方式,完全的仿真 modbus RTU的通讯\n方法2 在windows上使用虚拟串口(这个可以自选上网搜索,大多数是破解收费的),创建一对Tx Rx交叉的串口对, 例如 COM1和 COM2\n然后使用qt example的modbus slave 运行在COM1\n你的modbus master程序使用com2 ,连接上之后就可以完整的验证modbus RTU的整个通讯过程\n方式3 此方式与 方法2雷同\n区别是直接使用两个物理串口 交叉连接 TX RX\n例如我使用raspberry pi5 ,先把qt example的modbus slave 交叉编译部署到raspberry pi中\n然后配置raspberry pi ,打开 0 和 2的串口, 配置方法可以参考我之前写的raspberry pi 5 开发笔记\n在开发板上用杜邦线 交叉连接两个串口的TX RX后,\n把qt modbus slave运行在 ttyAMA0 ,\n把自己的modbus master程序也部署到开发板上, 运行程序,通过ttyAMA2 连接到 qt modbus slave\n就可以开始测试了\n",
"url": "\/post\/modbus_rtu%E4%BB%8E%E7%AB%99%E4%BB%BF%E7%9C%9F\/"
},
"\/tags\/modbusrtu\/": {
"title": "ModbusRTU",
"tags": [],
"content": "",
"url": "\/tags\/modbusrtu\/"
},
"\/post\/": {
"title": "Posts",
"tags": [],
"content": "",
"url": "\/post\/"
},
"\/tags\/simulation\/": {
"title": "Simulation",
"tags": [],
"content": "",
"url": "\/tags\/simulation\/"
},
"\/tags\/": {
"title": "Tags",
"tags": [],
"content": "",
"url": "\/tags\/"
},
"\/tags\/windows\/": {
"title": "Windows",
"tags": [],
"content": "",
"url": "\/tags\/windows\/"
},
"\/tags\/wsl2\/": {
"title": "WSL2",
"tags": [],
"content": "",
"url": "\/tags\/wsl2\/"
},
"\/post\/wsl2%E8%AE%BE%E7%BD%AE%E7%BD%91%E7%BB%9C%E6%A1%A5%E6%8E%A5%E5%88%B0%E7%89%A9%E7%90%86%E7%BD%91%E5%8D%A1\/": {
"title": "wsl2设置网络桥接到物理网卡",
"tags": ["wsl2","windows",],
"content": "0. 前言 前几个月已经探索了通过x11 转发, 在windows与mac os上通过ubuntu docker 镜像,运行具有gpu加速的ubuntu gui程序\n但是在windows上有更直接的选择, 那就是通过wsl2 运行ubutnu的linux 子系统, 这种方式,可以通过在docker desktop中设置与具体的wsl2 发行版集成, 让windows和wsl2 发行版使用同一个docker socket,共享一个docker engine\ndocker 也更建议把docker与wsl2一起使用, 此方案应有更多的性能红利和社区支持红利\n由于我将在windows上通过wsl2 ubuntu运行的程序会使用udp广播来发现局域网中的设备,所以不能使用wsl默认的nat网络,否则udp广播刚不能传达到局域网的设备端. 所以我探索了设置wsl 网络桥接物理网卡的方式,并记录在此,留痕\n此方法需要\n使用hyper-v 创建虚拟网络 配置wsl 使用 虚拟网络 限制\n终于目前只在win11上使用 win10上还没测试,所以不保证在windows上可以成功 如果将来在windows 10测试验证, 将会更新些文档\n1.windows家族版安装hyper-v 通常安装hyper-v的方式是在程序中打开windows功能,选中hyper-v,确定安装,不过如果你是windows 家庭版,那么在打开windows功能的界面中不会有hyper-v的选项,需要用以下方式安装\n创建 hyper-v.bat\n1 2 3 4 5 6 pushd \u0026#34;%~dp0\u0026#34; dir /b %SystemRoot%\\servicing\\Packages\\*Hyper-V*.mum \u0026gt;hyper-v.txt for /f %%i in (\u0026#39;findstr /i . hyper-v.txt 2^\u0026gt;nul\u0026#39;) do dism /online /norestart /add-package:\u0026#34;%SystemRoot%\\servicing\\Packages\\%%i\u0026#34; del hyper-v.txt Dism /online /enable-feature /featurename:Microsoft-Hyper-V -All /LimitAccess /ALL pause 使用命令提示符或powershell运行这个脚本即可进行安装\n2. 创建hyper-v 虚拟交换机网络 然后运行hyper-v后,在右边点创建虚拟交换机,选择外部网络,并设置wsl需要使用的物理网卡, 将名称修改为自己想定义的名称,我这里命名为 wsl-net\n在powershell 使用Get-NetAdapter,或ncpa.cpl命令可以查看创建的虚拟机网络名称为 vEthernet(wsl-net), 为了方便配置,我们重命名它为wsl-net (直接在ncpa.cpl打开的网络适配器窗口右键重命名即可)\n1 2 3 4 PS C:\\WINDOWS\\system32\u0026gt; Get-NetAdapter Name InterfaceDescription ... vEthernet (wsl-net) Hyper-V Virtual Ethernet Adapter #3 ... 修改完还是在powershell中命令Get-NetAdapter验证一下\n1 2 3 4 PS C:\\WINDOWS\\system32\u0026gt; Get-NetAdapter Name InterfaceDescription ... wsl-net Hyper-V Virtual Ethernet Adapter #3 ... 3. 配置wsl配置文件,使用虚拟交换机网络 在配置之前,进入wsl中查看网络为\n1 2 3 4 5 6 7 8 9 10 11 12 13 DESKTOP-E31FB3M:/mnt/host/c/Users/kokbi# ip a 1: lo: \u0026lt;LOOPBACK,UP,LOWER_UP\u0026gt; mtu 65536 qdisc noqueue state UNKNOWN qlen 1000 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.0.1/8 scope host lo valid_lft forever preferred_lft forever inet6 ::1/128 scope host valid_lft forever preferred_lft forever 2: eth0: \u0026lt;BROADCAST,MULTICAST,UP,LOWER_UP\u0026gt; mtu 1500 qdisc mq state UP qlen 1000 link/ether 00:15:5d:b5:ac:4f brd ff:ff:ff:ff:ff:ff inet 172.28.219.98/20 brd 172.28.223.255 scope global eth0 valid_lft forever preferred_lft forever inet6 fe80::215:5dff:feb5:ac4f/64 scope link valid_lft forever preferred_lft forever 3.1 win11 在用户目录中修改.wslconfig (如果没有则创建)\n1 2 3 4 5 [WSL2] networkingMode=bridged vmSwitch=external dhcp=false localhostForwarding=True 3.2 win10 参考\nwin10不支持通过配置文件直接配置虚拟交换机网络,所以只能通过修改默认的wsl网络来达到桥接的目的\n在超级管理员权限的powershell中执行\n1 Set-VMSwitch -name \u0026#34;WSL\u0026#34; -NetAdapterName \u0026#34;WLAN\u0026#34; -AllowManagementOS $true WSL为默认的wsl网络,\nWLAN 为你想要桥接的网络\n在wsl 中执行刷新ip地址和dhcp获取ip地址\n1 2 ip addr flush dev eth0 dhclient eth0 如果需要网上冲浪,还需要配置一下dns\n1 sudo echo \u0026#34;nameserver 8.8.8.8\u0026#34; \u0026gt; /etc/resolv.conf 如果想切换回NAT网络 ,设置在具有超级管理员权限的powershell中执行\n1 Set-VMSwitch WSL -SwitchType Internal 更进一步设置到wsl里的/etc/wsl.conf,在wsl启动时将自动执行脚本\n1 2 3 4 5 [boot] systemd=true command = \u0026#34;/mnt/c/WINDOWS/System32/WindowsPowerShell/v1.0/powershell.exe -Command \u0026#39;Set-VMSwitch -name \u0026#34;WSL\u0026#34; -NetAdapterName \u0026#34;WLAN\u0026#34; -AllowManagementOS $true\u0026#39;; ip addr flush dev eth0; dhclient eth0\u0026#34; [network] generateResolvConf = false 等验证, 如果修改了默认的wsl网络,在wsl 关掉,重启电脑后会造成的影响需要观察,\n从上方github的链接中看他们在wsl开启后才修改为桥接,并在wsl shutdown后,马上就把网络修改回NAT模式了.\n",
"url": "\/post\/wsl2%E8%AE%BE%E7%BD%AE%E7%BD%91%E7%BB%9C%E6%A1%A5%E6%8E%A5%E5%88%B0%E7%89%A9%E7%90%86%E7%BD%91%E5%8D%A1\/"
},
"\/tags\/blutooth\/": {
"title": "BLUTOOTH",
"tags": [],
"content": "",
"url": "\/tags\/blutooth\/"
},
"\/tags\/c\u002b\u002b\/": {
"title": "C\u002b\u002b",
"tags": [],
"content": "",
"url": "\/tags\/c\u002b\u002b\/"
},
"\/tags\/gpio\/": {
"title": "GPIO",
"tags": [],
"content": "",
"url": "\/tags\/gpio\/"
},
"\/tags\/i2c\/": {
"title": "I2C",
"tags": [],
"content": "",
"url": "\/tags\/i2c\/"
},
"\/tags\/nvme\/": {
"title": "NVME",
"tags": [],
"content": "",
"url": "\/tags\/nvme\/"
},
"\/post\/raspberry-pi5-%E5%BC%80%E5%8F%91%E7%AC%94%E8%AE%B0\/": {
"title": "raspberry-pi5-开发笔记",
"tags": ["C\u002b\u002b","RaspberryPI 5","I2C","GPIO","NVME","BLUTOOTH",],
"content": " 2024/07/27 添加nvme,bluetoothcli 相关内容 2024/10/26 添加使用非pd电源遇到的Usb电流问题 2024/12/14更新i2c 写入eeprom , 定制启动动画和开始菜单图标 0. 前车之鉴 0.1 glibc 兼容性问题 刚开始的时候,pi5的板子用了官方的镜像,即 Debian 12 bookworm. 但是交叉编译的镜像使用了Ubuntu 23.10 mantic, 交叉编译了Qt5 和程序后,放到板子上运行时,提示glibc版本不兼容, 是因为 bookworm和ubuntu 23.10的 libc 和libcxx版本不同导致,ubuntu23.10 2.38, bookworm 2.36,后面板子和交叉编译的docker镜像统一之后就ok了\n0.2 Qt程序显示不正常,半透明的图形不显示,窗口有黑边 虽然Qt正常编译过了,但是部署到板子上后,程序显示不正常,后经排查,是由于 Qt配置 EGL on X11失败,需要在编译配置/src/qt5/qtbase/mkspecs/devices/linux-rasp-pi4-v3d-g++/qmake.confr中添加编译选项 -fpermissive\n1 QMAKE_CXXFLAGS = $$QMAKE_CFLAGS -fpermissive 否则qt 源码中的configure工具 生成的EGL on X11 的单元测试会执行失败,导致配置失败\n0.3 检测不到I2C 初遇0.1中所述问题时,我把板子和docker环境的系统都 刷成ubuntu 23.10,之后板子一直检测不到i2c,叫大师帮忙检测硬件接线问题,无果,用示波器测量波形,正常。其他硬件没有测试,但应该也是有问题的,是因为官方虽然给了ubuntu23.10的镜像,但却没有预装硬件相关的工具,例如i2cdetect, 于是我用apt安装了一个,于是不能正常工作 ,如果克隆内核源码,单独编译工具应该也可以行得通,但我解决的方法简单粗暴,把板子和docker的系统都换为bookworm了,bookworm的工具是齐全的。\n0.4 GPIO C++编码后,不能正常初始化,提示系统不是树莓派 由于 PI5 做了很大的变更,所以GPIO相关的系统工具虽然内置了,但并不能适用 于pi5,pigpio在 /usr/include和/usr/lib中头文件库可以正常编译,链接,但编好的程序会提示初始化失败,解决的方法是使用开始的源 libgpiod,不要使用pigpio\nhttps://github.com/joan2937/pigpio/issues/589\n1. I2C 树莓波5支持3路i2c通讯,默认只打开 一组,即pin3,pin5,通过修改/boot/fireware/config.txt可以打开其他两路,后面会简单介绍一下,目前我的项目只用了一路,所以使用默认配置 即可。\n也可以参考下图, 标注4的是只有raspberry pi 4才有的功能, 没有标注4的,表示raspberry pi 5也可以使用\n2.1配置 1 2 sudo raspi-config 找到i2c的选项,切换为打开 也可以直接修改配置 文件\n/boot/fireware/config.txt\n1 2 3 4 5 6 7 8 #省略 dtparam=i2c_arm=on#默认 dtparam=spi=on #默认 #配置i2c 0 为管脚x和y ,xy需要查看官方文档确定支持i2c的管脚号再配置 #dtoverlay=i2c0,pins_x_y #省略 2.2 bookworm配套i2c工具 编码之前,可以使用这些工具验证一下 设备是否正常\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 # 枚举i2c 总线 dv@rsap5:/opt/dv_app $ i2cdetect -l i2c-11 i2c 107d508200.i2c I2C adapter i2c-12 i2c 107d508280.i2c I2C adapter #由于没有开光机,所以i2c-1没有显示出来 # 查看i2c-1 上的设备 dv@rsap5:/opt/dv_app $ i2cdetect -y 1 0 1 2 3 4 5 6 7 8 9 a b c d e f 00: -- -- -- -- -- -- -- -- 10: -- -- -- -- -- -- -- -- -- -- -- 1b -- -- -- -- 20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 70: -- -- -- -- -- -- -- -- #如果光机有上电的放在,应该是这样,1b表示光机的设备总线地址 # 查看设备0x1b中寄存器的值 i2cdump -y 1 0x1b # 获取设备0x1b上寄存器0x2ff的值 i2cget -y 1 0x1b 0x2f # 设置i2c上0x1b设备0x2f寄存器的值为0xff i2cset -y 1 0x1b 0x2f 0xff 2.3 编码 如果使用bookworm, 这些头文件 应该就在系统目录\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 #include \u0026lt;i2c/smbus.h\u0026gt; #include \u0026lt;linux/i2c-dev.h\u0026gt; #include \u0026lt;sys/ioctl.h\u0026gt; #include \u0026lt;fcntl.h\u0026gt; #include \u0026lt;unistd.h\u0026gt; void rasp_I2c_helper::private_read() { int file; int adapter_nr = 1; char filename[20]; snprintf(filename, 19, \u0026#34;/dev/i2c-%d\u0026#34;, adapter_nr); file = open(filename, O_RDWR); if (file \u0026lt; 0) { exit(1); } int addr = 0x40; if (ioctl(file, I2C_SLAVE, addr) \u0026lt; 0) { exit(1); } __u8 reg = 0x10; __s32 res; res = i2c_smbus_read_word_data(file, reg); if (res \u0026lt; 0) { /* ERROR HANDLING: i2c transaction failed */ return; } else { /* res contains the read word */ } } void rasp_I2c_helper::private_write(l) { prism::qt::core::QEventLoopGuard raii(el) ; //光机命令 std::vector\u0026lt;uint8_t\u0026gt; writeMode = {0x05,0x01}; // solid color std::vector\u0026lt;uint8_t\u0026gt; writeRgbLedDisable = {0x52,0x00}; std::vector\u0026lt;uint8_t\u0026gt; writeRgbLedEnable = {0x52,0x07}; int file; int adapter_nr = 1; char filename[20]; snprintf(filename, 19, \u0026#34;/dev/i2c-%d\u0026#34;, adapter_nr); file = open(filename, O_RDWR); if (file \u0026lt; 0) { ZLOGE(tag_) \u0026lt;\u0026lt; \u0026#34;i2c open error, dev: \u0026#34; \u0026lt;\u0026lt; filename; return; } ZLOGI(tag_) \u0026lt;\u0026lt; \u0026#34;i2c open success, dev: \u0026#34; \u0026lt;\u0026lt; filename; int addr = 0x1b; if (ioctl(file, I2C_SLAVE, addr) \u0026lt; 0) { ZLOGE(tag_) \u0026lt;\u0026lt; \u0026#34;i2c ioctl error, addr: 0x\u0026#34; \u0026lt;\u0026lt; std::hex \u0026lt;\u0026lt; addr; return; } ZLOGI(tag_) \u0026lt;\u0026lt; \u0026#34;i2c ioctl success, addr: 0x\u0026#34; \u0026lt;\u0026lt; std::hex \u0026lt;\u0026lt; addr; //writeMode { if (write(file, writeMode.data(), writeMode.size()) != static_cast\u0026lt;int\u0026gt;(writeMode.size())) { ZLOGE(tag_) \u0026lt;\u0026lt; \u0026#34;i2c write error:\u0026#34; \u0026lt;\u0026lt; \u0026#34;writeMode\u0026#34;; return; } ZLOGI(tag_) \u0026lt;\u0026lt; \u0026#34;i2c write success:\u0026#34; \u0026lt;\u0026lt; \u0026#34;writeMode\u0026#34;; } //writeRgbLedEnable { if (write(file, writeRgbLedEnable.data(), writeRgbLedEnable.size()) != static_cast\u0026lt;int\u0026gt;(writeRgbLedEnable.size())) { ZLOGE(tag_) \u0026lt;\u0026lt; \u0026#34;i2c write error:\u0026#34; \u0026lt;\u0026lt; \u0026#34;writeRgbLedEnable\u0026#34;; return; } ZLOGI(tag_) \u0026lt;\u0026lt; \u0026#34;i2c write success:\u0026#34; \u0026lt;\u0026lt; \u0026#34;writeRgbLedEnable\u0026#34;; } std::this_thread::sleep_for(std::chrono::milliseconds(3000)); //writeRgbLedDisable { if (write(file, writeRgbLedDisable.data(), writeRgbLedDisable.size()) != static_cast\u0026lt;int\u0026gt;(writeRgbLedDisable.size())) { ZLOGE(tag_) \u0026lt;\u0026lt; \u0026#34;i2c write error:\u0026#34; \u0026lt;\u0026lt; \u0026#34;writeRgbLedDisable\u0026#34;; return; } ZLOGI(tag_) \u0026lt;\u0026lt; \u0026#34;i2c write success:\u0026#34; \u0026lt;\u0026lt; \u0026#34;writeRgbLedDisable\u0026#34;; } std::this_thread::sleep_for(std::chrono::milliseconds(150)); close(file); } 2. GPIO 2.1 bookworm配套GPIO工具 查看pin脚位置\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 #查看管脚位置, dv@rsap5:/opt/dv_app $ pinout Description : Raspberry Pi 5B rev 1.0 Revision : d04170 SoC : BCM2712 RAM : 8GB Storage : MicroSD USB ports : 4 (of which 2 USB3) Ethernet ports : 1 (1000Mbps max. speed) Wi-fi : True Bluetooth : True Camera ports (CSI) : 2 Display ports (DSI): 2 ,--------------------------------. | oooooooooooooooooooo J8 : +==== | 1ooooooooooooooooooo : |USB2 | Wi Pi Model 5B V1.0 fan +==== | Fi +---+ +---+ | | |RAM| |RP1| +==== ||p +---+ +---+ |USB3 ||c ------- +==== ||i SoC |c|c J14 | ( ------- J7|s|s 12 +====== | J2 bat uart 1|i|i oo | Net | pwr\\..|hd|...|hd|o|1|0 +====== `-| |-1o|m0|---|m1|--------------\u0026#39; J8: 3V3 (1) (2) 5V GPIO2 (3) (4) 5V GPIO3 (5) (6) GND GPIO4 (7) (8) GPIO14 GND (9) (10) GPIO15 GPIO17 (11) (12) GPIO18 GPIO27 (13) (14) GND GPIO22 (15) (16) GPIO23 3V3 (17) (18) GPIO24 GPIO10 (19) (20) GND GPIO9 (21) (22) GPIO25 GPIO11 (23) (24) GPIO8 GND (25) (26) GPIO7 GPIO0 (27) (28) GPIO1 GPIO5 (29) (30) GND GPIO6 (31) (32) GPIO12 GPIO13 (33) (34) GND GPIO19 (35) (36) GPIO16 GPIO26 (37) (38) GPIO20 GND (39) (40) GPIO21 J2: RUN (1) GND (2) J7: COMPOSITE (1) GND (2) J14: TR01 TAP (1) (2) TR00 TAP TR03 TAP (3) (4) TR02 TAP For further information, please refer to https://pinout.xyz/ 查看Pin脚功能,状态 例如配置了了串口后,这里会显示哪些pin脚为uart tx 或 rx\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 dv@rsap5:/opt/dv_app $ pinctrl 0: ip pu | hi // ID_SDA/GPIO0 = input 1: ip pu | hi // ID_SCL/GPIO1 = input 2: no pu | -- // GPIO2 = none 3: no pu | -- // GPIO3 = none 4: no pu | -- // GPIO4 = none 5: no pu | -- // GPIO5 = none 6: ip pu | hi // GPIO6 = input 7: ip pu | hi // GPIO7 = input 8: op dh pu | hi // GPIO8 = output 9: op dl pd | lo // GPIO9 = output 10: no pd | -- // GPIO10 = none 11: no pd | -- // GPIO11 = none 12: no pd | -- // GPIO12 = none 13: no pd | -- // GPIO13 = none 14: no pd | -- // GPIO14 = none 15: no pd | -- // GPIO15 = none 16: no pd | -- // GPIO16 = none 17: no pd | -- // GPIO17 = none 18: no pd | -- // GPIO18 = none 19: no pd | -- // GPIO19 = none 20: no pd | -- // GPIO20 = none 21: no pd | -- // GPIO21 = none 22: no pd | -- // GPIO22 = none 23: no pd | -- // GPIO23 = none 24: no pd | -- // GPIO24 = none 25: no pd | -- // GPIO25 = none 26: no pd | -- // GPIO26 = none 27: no pd | -- // GPIO27 = none 28: ip pd | lo // PCIE_RP1_WAKE/GPIO28 = input 29: no pu | hi // FAN_TACH/GPIO29 = none 30: no pu | -- // HOST_SDA/GPIO30 = none 31: no pu | -- // HOST_SCL/GPIO31 = none 32: op dh pd | hi // ETH_RST_N/GPIO32 = output 33: no pd | lo // GPIO33 = none 34: op dl pd | lo // CD0_IO0_MICCLK/GPIO34 = output 35: no pd | lo // CD0_IO0_MICDAT0/GPIO35 = none 36: no pd | lo // RP1_PCIE_CLKREQ_N/GPIO36 = none 37: no pd | lo // GPIO37 = none 38: ip pd | hi // CD0_SDA/GPIO38 = input 39: ip pd | hi // CD0_SCL/GPIO39 = input 40: ip pd | hi // CD1_SDA/GPIO40 = input 41: ip pd | hi // CD1_SCL/GPIO41 = input 42: a2 pd | hi // USB_VBUS_EN/GPIO42 = VBUS_EN1 43: a2 pu | hi // USB_OC_N/GPIO43 = VBUS_OC1 44: op dh pd | hi // RP1_STAT_LED/GPIO44 = output 45: a0 pd | hi // FAN_PWM/GPIO45 = PWM1_CHAN3 46: op dl pd | lo // CD1_IO0_MICCLK/GPIO46 = output 47: no pd | lo // 2712_WAKE/GPIO47 = none 48: no pd | lo // CD1_IO1_MICDAT1/GPIO48 = none 49: op dh pd | hi // EN_MAX_USB_CUR/GPIO49 = output 50: no pd | -- // GPIO50 = none 51: no pd | -- // GPIO51 = none 52: no pu | -- // GPIO52 = none 53: no pu | hi // GPIO53 = none 100: ip pd | lo // GPIO0 = input 101: op dh pu | hi // 2712_BOOT_CS_N/GPIO1 = output 102: a6 pn | hi // 2712_BOOT_MISO/GPIO2 = VC_SPI0_MISO 103: a5 pn | hi // 2712_BOOT_MOSI/GPIO3 = VC_SPI0_MOSI 104: a6 pn | lo // 2712_BOOT_SCLK/GPIO4 = VC_SPI0_SCLK 105: ip pd | lo // GPIO5 = input 106: ip pd | lo // GPIO6 = input 107: ip pd | lo // GPIO7 = input 108: ip pd | lo // GPIO8 = input 109: ip pd | lo // GPIO9 = input 110: ip pd | lo // GPIO10 = input 111: ip pd | lo // GPIO11 = input 112: ip pd | lo // GPIO12 = input 113: ip pd | lo // GPIO13 = input 114: a1 pd | lo // PCIE_SDA/GPIO14 = SPI_S_MOSI_OR_BSC_S_SDA 115: a1 pd | lo // PCIE_SCL/GPIO15 = SPI_S_SCK_OR_BSC_S_SCL 116: ip pd | lo // GPIO16 = input 117: ip pd | lo // GPIO17 = input 118: ip pd | lo // GPIO18 = input 119: ip pd | lo // GPIO19 = input 120: ip pu | hi // PWR_GPIO/GPIO20 = input 121: ip pd | lo // 2712_G21_FS/GPIO21 = input 122: ip pd | lo // GPIO22 = input 123: ip pd | lo // GPIO23 = input 124: a3 pn | lo // BT_RTS/GPIO24 = UART_RTS_0 125: a4 pu | lo // BT_CTS/GPIO25 = UART_CTS_0 126: a4 pn | hi // BT_TXD/GPIO26 = UART_TXD_0 127: a4 pu | hi // BT_RXD/GPIO27 = UART_RXD_0 128: op dh pd | hi // WL_ON/GPIO28 = output 129: op dh pd | hi // BT_ON/GPIO29 = output 130: a4 pn | lo // WIFI_SDIO_CLK/GPIO30 = SD2_CLK 131: a4 pu | hi // WIFI_SDIO_CMD/GPIO31 = SD2_CMD 132: a4 pd | hi // WIFI_SDIO_D0/GPIO32 = SD2_DAT0 133: a3 pu | hi // WIFI_SDIO_D1/GPIO33 = SD2_DAT1 134: a4 pn | hi // WIFI_SDIO_D2/GPIO34 = SD2_DAT2 135: a3 pn | hi // WIFI_SDIO_D3/GPIO35 = SD2_DAT3 200: ip pd | hi // RP1_SDA/AON_GPIO0 = input 201: ip pd | hi // RP1_SCL/AON_GPIO1 = input 202: op dh pd | hi // RP1_RUN/AON_GPIO2 = output 203: op dh pd | hi // SD_IOVDD_SEL/AON_GPIO3 = output 204: op dh pd | hi // SD_PWR_ON/AON_GPIO4 = output 205: a6 pu | lo // SD_CDET_N/AON_GPIO5 = SD_CARD_PRES_G 206: ip pu | hi // SD_FLG_N/AON_GPIO6 = input 207: ip pd | lo // AON_GPIO7 = input 208: ip pd | lo // 2712_WAKE/AON_GPIO8 = input 209: op dh pd | hi // 2712_STAT_LED/AON_GPIO9 = output 210: ip pd | lo // AON_GPIO10 = input 211: ip pd | lo // AON_GPIO11 = input 212: ip pd | lo // PMIC_INT/AON_GPIO12 = input 213: a3 pd | hi // UART_TX_FS/AON_GPIO13 = VC_TXD0 214: a3 pd | lo // UART_RX_FS/AON_GPIO14 = VC_RXD0 215: ip pd | lo // AON_GPIO15 = input 216: ip pu | hi // AON_GPIO16 = input 232: a1 -- | hi // HDMI0_SCL/AON_SGPIO0 = HDMI_TX0_BSC_SCL 233: a1 -- | hi // HDMI0_SDA/AON_SGPIO1 = HDMI_TX0_BSC_SDA 234: a1 -- | hi // HDMI1_SCL/AON_SGPIO2 = HDMI_TX1_BSC_SCL 235: a1 -- | hi // HDMI1_SDA/AON_SGPIO3 = HDMI_TX1_BSC_SDA 236: a2 -- | hi // PMIC_SCL/AON_SGPIO4 = BSC_M2_SCL 237: a2 -- | hi // PMIC_SDA/AON_SGPIO5 = BSC_M2_SDA gpiod 常用指令\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 #libgpiod radme中有使用方法,raspberry pi 5 内置了它https://github.com/brgl/libgpiod gpiodetect #查看可用的gpio芯片 gpioinfo 4 #查看第4块芯片管脚 gpioget 4 29 #配置第4块芯片第29脚为input,并打印其电平状态 gpioset 4 29=0 #置第4块芯片第29脚为output,并打印其电平状态 #下面这种方法是不推荐的方法,sysctl的方式已经被部分系统禁用了,包括pi5 #pi4可能还能用 #/sys/class/gpio/export(只读) 中写入导出的管脚号 # echo 26 \u0026gt;\u0026gt; cat /sys/kernel/debug/gpio #写入成功后会多一个文件 #/sys/class/gpio/gpio26/value #这个文件 的值就是管脚的状态 #可以通过 cat /sys/kernel/debug/gpio 打印所有管脚信息状态, 2.2 编译libgpiod gpiod是一个c语言的库,不过有多种语言的绑定,包括c++\n两者都是LGPL, 2.1中介绍的好些工具则是GPL, 可以自己编译,也可以直接使用bookworm内置的\n我们项目使用c++绑定,c++写的代码相对c来说会比较面向对象\n1 2 3 mkdir -p ~/source/repos cd ~/source/repos git clone https://github.com/brgl/libgpiod.git 配置\n1 ./autogen.sh --enable-tools=yes --prefix=/usr 配置可能会失败,会提示需要安装几个库, 安装好就可以正常配置了\n然后进入bindings/cxx 编译,安装\n1 2 3 cd bindings/cxx sudo make sudo make install 安装后需要手动把cxx绑定的头文件复制进去/usr/include里,不知道是不是姿势不对,只复制了c的头文件\n然后把sysroot同步到交叉编译的docker镜像中就可以编码了\n2.3 编码 github repo中也有example,我就直接复制我工程里的了\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 #include \u0026lt;/sysroot/usr/include/gpiod.hpp\u0026gt; void rasp_gpio_helper::async_watch_line_value() { /* * Assume a button connecting the pin to ground, so pull it up and * provide some debounce. */ auto request = ::gpiod::chip(chip_path) .prepare_request() .set_consumer(\u0026#34;async-watch-line-value\u0026#34;) .add_line_settings( il_ofsets, ::gpiod::line_settings() .set_direction( ::gpiod::line::direction::INPUT) .set_edge_detection( ::gpiod::line::edge::BOTH) .set_bias(::gpiod::line::bias::PULL_UP) .set_debounce_period( std::chrono::milliseconds(10))) .do_request(); gpiod::edge_event_buffer event_buffer = gpiod::edge_event_buffer(1); struct pollfd poll_fd; poll_fd.fd = request.fd(); poll_fd.events = POLLIN; while (m_poll_flag) { int ret = poll(\u0026amp;poll_fd,1,1); if(ret != -1) { if(poll_fd.revents \u0026amp; POLLPRI) { request.read_edge_events(event_buffer); for (const gpiod::edge_event\u0026amp; event : event_buffer) { ::std::cout \u0026lt;\u0026lt; \u0026#34;offset: \u0026#34; \u0026lt;\u0026lt; event.line_offset() \u0026lt;\u0026lt; \u0026#34; type: \u0026#34; \u0026lt;\u0026lt; ::std::setw(7) \u0026lt;\u0026lt; ::std::left \u0026lt;\u0026lt; edge_event_type_str(event) \u0026lt;\u0026lt; \u0026#34; event #\u0026#34; \u0026lt;\u0026lt; event.line_seqno() \u0026lt;\u0026lt; ::std::endl; } } } else ZLOGE(tag_) \u0026lt;\u0026lt; \u0026#34;async watch line value poll error\u0026#34;; } } void rasp_gpio_helper::async_write_line_value() { auto request = ::gpiod::chip(chip_path) .prepare_request() .set_consumer(\u0026#34;toggle-multiple-line-values\u0026#34;) .add_line_settings( ol_ofsets, ::gpiod::line_settings().set_direction( ::gpiod::line::direction::OUTPUT)) .set_output_values(o_values) .do_request(); struct pollfd poll_fd; poll_fd.fd = request.fd(); poll_fd.events = POLLOUT; while(m_poll_flag) { int ret = poll(\u0026amp;poll_fd,1,1); if(ret != -1) { request.set_values(ol_ofsets, o_values); } else ZLOGE(tag_) \u0026lt;\u0026lt; \u0026#34;async watch line value poll error\u0026#34;; } } 2.4 配置权限 1 2 3 cat \u0026lt;\u0026lt; EOF \u0026gt; /etc/udev/rules.d/99-gpio-rules SUBSYSTEM==\u0026#34;gpio\u0026#34;, KERNEL==\u0026#34;gpiochip4\u0026#34;, OWNER=\u0026#34;dv\u0026#34;, GROUP=\u0026#34;root\u0026#34;, MODE=\u0026#34;0660\u0026#34; EOF 3 . PWM调制脉冲 3.1 Installation On the Raspberry Pi, add dtoverlay=pwm-2chan to /boot/config.txt. This defaults to GPIO_18 as the pin for PWM0 and GPIO_19 as the pin for PWM1. Alternatively, you can change GPIO_18 to GPIO_12 and GPIO_19 to GPIO_13 using dtoverlay=pwm-2chan,pin=12,func=4,pin2=13,func2=4. On the Pi 5, use channels 0 and 1 to control GPIO_12 and GPIO13, respectively; use channels 2 and 3 to control GPIO_18 and GPIO_19, respectively On all other models, use channels 0 and 1 to control GPIO-18 and GPIO_19, respectively Reboot your Raspberry Pi. You can check everything is working on running lsmod | grep pwm and looking for pwm_bcm2835 Install this library: sudo pip3 install rpi-hardware-pwm 3.2 Examples For Rpi 1,2,3,4, use chip=0; For Rpi 5, use chip=2\n1 2 3 4 5 6 7 8 9 from rpi_hardware_pwm import HardwarePWM pwm = HardwarePWM(pwm_channel=0, hz=60, chip=0) pwm.start(100) # full duty cycle pwm.change_duty_cycle(50) pwm.change_frequency(25_000) pwm.stop() History The original code is from jdimpson/syspwm, We\u0026rsquo;ve updated it to Python3 and made it look like the RPi.GPIO library\u0026rsquo;s API (but more Pythonic than that.), and we use it in Pioreactor bioreactor system.\n4 . NVME启动 4.1 方式一:rasp-config 使用rasp-bian系统的话,有一个名为rasp-config的工具,可以进入类似bios的界面,选择一些cli工具配置,其中有一个boot order, 进入后可以选择nvme优先于emmc,sd或usb\n1 sudo rasp-config 配置时可能会失败, 提示说少一个eeprom相关的apt包没有安装最新的版本, 按提示安装一下就可以\n4.2 方式一, 修改config.txt 4.2.1 pcie接口接单个设备 在/boot/fireware/config.txt 末尾添加\n1 2 3 4 5 6 7 8 # Add to bottom of /boot/firmware/config.txt dtparam=pciex1 # Note: You could also just add the following (it is an alias to the above line) # dtparam=nvme # Optionally, you can control the PCIe lane speed using this parameter dtparam=pciex1_gen=2 #单个pcie设备,这里可以使用gen3, gen2可达450mb/s,gen3可达 900mb/s 42.2 pcie 接口通过switcher 接两个pcie设备 我们的项目中,除了外接一块nvme m2 ssd之外,还有一个pcie接口的 ai推理硬件模块\n这时候如果3.2.1 pcie中的配置方法, 引导程序会不知道要从哪个位置启动系统,所以/boot/fireware/config.txt需要再添加\n1 2 3 # /boot/fireware/config.txt dtparam=pciex1_no_l0s=on 然后设置EEPROM\n1 2 3 4 sudo ≈rpi-eeprom-config --edit # 这将以nano 打开配置文件编辑 #如果想使用vim进行编辑 #sudo EDITOR=vim rpi-eeprom-config --edit 把原来的配置\n1 2 3 4 [all] BOOT_UART=1 POWER_OFF_ON_HALT=0 BOOT_ORDER=0xf461 修改为\n1 2 3 4 5 # Change the BOOT_ORDER line to the following: BOOT_ORDER=0xf416 # Add the following line if using a non-HAT+ adapter: PCIE_PROBE=1 使用官方刷系统的工具把系统刷进nvme后, 接好之后,启动后可以使用下面的命令查看系统是否挂载到nvme 原来系统挂载在emmc sd卡上\n1 2 3 4 5 6 7 8 9 dv@rsap5:/boot/firmware $ df -h Filesystem Size Used Avail Use% Mounted on udev 3.8G 0 3.8G 0% /dev tmpfs 805M 6.2M 799M 1% /run /dev/mmcblk0p2 29G 11G 18G 38% / tmpfs 4.0G 496K 4.0G 1% /dev/shm tmpfs 5.0M 48K 5.0M 1% /run/lock /dev/mmcblk0p1 510M 74M 437M 15% /boot/firmware tmpfs 805M 144K 805M 1% /run/user/1000 正确修改后,上面的mmcblk应该变为nvme\n5 使用非PD电源 5.1 遇到的问题 raspberry pi 5 官方的电源是带pd协议的,会协商电源参数\n我们在我们以raspberry pi 5 为核心板扩展了底板,电源由我们自己提供,遇到两个问题\nusb hub外设供电不足,有概率的导致崩溃 接通电源后进入raspbin系统后右上角有气泡提示低电压报警, 如果是在系统中直接使用reboot则不会提示低电压报警 5.2 解决供电问题 在论坛上查找资料后, 据说原因是没有pd协商,所以raspberry 会认为接入的电源是3A的,并且只会给4个usb 总共600mA的电流\n如果想使用5A的非PD电源,按下面的步骤添加配置后, usb外设可以有1.6A的电流\n编辑config.txt加入一行\nusb_max_current_enable=1 执行下面这条命令配置 eeprom 配置板上的参数文件\n1 sudo EDITOR=vim rpi-eeprom-config --edit 添加一行\n1 PSU_MAX_CURRENT=5000 这两个配置后可以使用非pd的5v电源,不过仍然会提示低电压报警\n在config.txt添加忽略无视警告的配置,可以去掉低电压的报警\n1 avoid_warnings=1 但是我们还是有一个其他的电源报警去不掉,首次上电开机后,将一直停留在右上角,需要人为的点击才消除掉\n只需要在任务栏右键设置,把电源和电池的两个图标移除掉就行了\n5.3 其他 ps:\nraspberry pi的各个电压电流指标可以通过vcgencmd pmic_read_adc获取\n1 vcgencmd pmic_read_adc [name] #name是可选的,不填列出所有 论坛上一些低电压相关的帖\nhttps://forums.raspberrypi.com/viewtopic.php?t=373841\n6. UART 6.1 查看uart管脚 1 2 3 4 5 6 7 8 9 10 11 12 13 14 #查看 dtoverlay -a |grep uart dtoverlay -h uart1 #这里显示的gpio号不要当真,具体从pinctrl 命令看 #临时应用 sudo dtoverlay uart0,\u0026lt;param\u0026gt; #持久化,写到/boot/fireware/config.txt dtoverlay=uart0 dtoverlay=uart2 #uart0管脚8,10 #uart2管脚7,29 #与rock5c的uart2-m0和uart4-m2 管脚号一样 配置 - Raspberry Pi 文档\n6.2 测试uart读写的Python脚本 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 import serial import argparse import threading import time from datetime import datetime def send_data(ser): while True: current_time = datetime.now().strftime(\u0026#39;%Y-%m-%d %H:%M:%S\u0026#39;) ser.write(current_time.encode()) print(\u0026#34;Sent: {}\u0026#34;.format(current_time)) time.sleep(5) # 每5秒发送一次 def receive_data(ser): while True: if ser.in_waiting \u0026gt; 0: received_data = ser.readline().decode().strip() print(\u0026#34;Received: {}\u0026#34;.format(received_data)) def main(): parser = argparse.ArgumentParser(description=\u0026#34;Serial port listener and responder\u0026#34;) parser.add_argument(\u0026#39;--port\u0026#39;, type=str, required=True, help=\u0026#39;Serial port to use, e.g., /dev/ttyUSB0 or COM3\u0026#39;) parser.add_argument(\u0026#39;--baudrate\u0026#39;, type=int, required=True, help=\u0026#39;Baud rate for the serial communication\u0026#39;) args = parser.parse_args() try: with serial.Serial(args.port, args.baudrate, timeout=1) as ser: print(\u0026#34;Listening on {} at {} baud.\u0026#34;.format(args.port,args.baudrate)) # 创建发送和接收线程 send_thread = threading.Thread(target=send_data, args=(ser,)) receive_thread = threading.Thread(target=receive_data, args=(ser,)) # 启动线程 send_thread.start() receive_thread.start() # 确保主线程不会提前结束 send_thread.join() receive_thread.join() except serial.SerialException as e: print(\u0026#34;Error: {}\u0026#34;.format(e)) if __name__ == \u0026#34;__main__\u0026#34;: main() 用法\n1 2 3 4 python -m venv test cd text source bin/active (test) dv@rsap5:~/test $ sudo python ./tst_uart.py --port /dev/ttyAMA0 --baudrate 115200 7. I2C EEPROM 以下方法适用于kernal版本为6.6.20的版本\n根据这个帖可知道, 在linux kernal版本6.6.60之前,没有打补丁之前,在设备树源文件中, gpio 0 1管脚的名称为\nrp1_i2c0_0_1 之后打了补丁取了一个别名为i2c0_pins\n所以从别的设备上复制了设备树源文件过来使用,一直没有初始化成功,没有at24驱动,\n创建at24.dts 设备树源文件写入:\n/dts-v1/;\r/plugin/;\r/ {\rfragment@0 {\rtarget = \u0026lt;\u0026amp;i2c0\u0026gt;; // Reference to the I2C controller\r__overlay__ {\rpinctrl-names = \u0026#34;default\u0026#34;;\rpinctrl-0 = \u0026lt;\u0026amp;rp1_i2c0_0_1\u0026gt;; //kernal 6.6.y可用 \u0026amp;i2c0_pins\rclock-frequency = \u0026lt;400000\u0026gt;;\rstatus = \u0026#34;okay\u0026#34;;\r#address-cells = \u0026lt;1\u0026gt;; // Define addressing scheme\r#size-cells = \u0026lt;0\u0026gt;; // No size information for I2C\rat24@50 {\rcompatible = \u0026#34;atmel,24c64\u0026#34;, \u0026#34;at24\u0026#34;; // EEPROM compatibility\rreg = \u0026lt;0x50\u0026gt;; // I2C address of the EEPROM\rpagesize = \u0026lt;32\u0026gt;; // EEPROM page size in bytes\rsize = \u0026lt;8192\u0026gt;; // EEPROM size in bytes\raddress-width = \u0026lt;16\u0026gt;; // Address width in bits\r};\r};\r};\r}; 然后编译设备树,并把编译结果复制到/boot/firmware/overlays\n1 2 dtc -@ -I dts -O dtb -o at24.dtbo at24.dts sudo cp at24.dtbo /boot/firmware/overlays/ 在/boot/firmware/confit.txt中需要打开\ndtparam=i2c_arm=on #i2c pin27 28, gpio 0 1 dtparam=i2c_vc=on #\rdtoverlay=at24 #at24驱动 重启一下\n1 sudo reboot 在/etc/modules 文件中加入一行at24\n1 echo \u0026#34;at24\u0026#34; | sudo tee -a /etc/modules 测试读写eeprom\n1 2 3 test: echo \u0026#39;Hello World\u0026#39; | sudo tee /sys/class/i2c-dev/i2c-0/device/0-0050/eeprom sudo cat /sys/class/i2c-dev/i2c-0/device/0-0050/eeprom 8. 定制开机动画,开始菜单icon 替换这些位置的png图标\n1 2 3 4 5 6 7 8 9 10 #开始菜单图标 scp ./usr/share/icons/PiXflat/16x16/places/start-here.png root@10.2.1.203:/usr/share/icons/PiXflat/16x16/places/start-here.png scp ./usr/share/icons/PiXflat/24x24/places/start-here.png root@10.2.1.203:/usr/share/icons/PiXflat/24x24/places/start-here.png scp ./usr/share/icons/PiXflat/48x48/places/start-here.png root@10.2.1.203:/usr/share/icons/PiXflat/48x48/places/start-here.png scp ./usr/share/icons/PiXflat/32x32/places/start-here.png root@10.2.1.203:/usr/share/icons/PiXflat/32x32/places/start-here.png scp ./usr/share/icons/Adwaita/22x22/places/start-here.png #开机 scp ./usr/share/plymouth/themes/pix/splash.png root@10.2.1.203:/usr/share/plymouth/themes/pix/splash.png 然后update-initramfs -u固化一下,否则boot阶段还没有挂载上 rootfs, 没办法加载替换的图片.\n100. 其他 100.1 隐藏任务栏 ~/.config/wf-panel-pi.ini 1 2 autohide=true autohide_duration=500 100.2 raspbian蓝牙设备配对 raspberry pi 5 的raspbian系统好像没有蓝牙配的gui程序,不过有一个cli工具,大致用法,\n开启cli 工具进入交互交互式\n开启agent +\n确认设备可以被扫描到\n扫描设备\n停止扫描\n配对\n退出cli\n1 2 3 4 5 6 7 8 9 10 11 12 #树莓派蓝牙 bluetoolthctl agent on scan on scan off pair \u0026lt;device\u0026#39;s mac\u0026gt; quit ",
"url": "\/post\/raspberry-pi5-%E5%BC%80%E5%8F%91%E7%AC%94%E8%AE%B0\/"
},
"\/tags\/raspberrypi-5\/": {
"title": "RaspberryPI 5",
"tags": [],
"content": "",
"url": "\/tags\/raspberrypi-5\/"
},
"\/tags\/docker\/": {
"title": "Docker",
"tags": [],
"content": "",
"url": "\/tags\/docker\/"
},
"\/tags\/gui\/": {
"title": "GUI",
"tags": [],
"content": "",
"url": "\/tags\/gui\/"
},
"\/tags\/nvidia-docker2\/": {
"title": "Nvidia-docker2",
"tags": [],
"content": "",
"url": "\/tags\/nvidia-docker2\/"
},
"\/post\/windows_wsl2_nvidia-docker2%E9%95%9C%E5%83%8F%E8%AE%B0%E5%BD%95\/": {
"title": "windows_wsl2_nvidia-docker2镜像记录",
"tags": ["X11","Windows","Docker","WSL2","Nvidia-docker2","GUI",],
"content": "0. 写在前面 上上周我尝试了使用mac os 通过x11转发,显示从ubuntu docker中启动的gui程序,并使用ubuntu docker中的输入法的实践\n上周我尝试了在windows上在其基础上加入了nvidia-docker2的步骤,也成功了,不过没时间记录,于是今日写此文档记录\n不管是mac os还是windows上所做的这些实验,都是为了在docker中运行我的开源的yolo8辅助训练软件label-image\n1. 前置条件 1.1 wsl 2 由于windows上的ubuntu docker container运行在wsl2可以支持得更好,也是nvidia-docker2的充分必要条件,所以如果你安装的wsl发行版本不是wsl版本的话,需要先安装一个wsl2的docker-desktop\n安装成功后可以命令行查看\n1 2 3 PS C:\\WINDOWS\\system32\u0026gt; wsl --list -v NAME STATE VERSION * docker-desktop Running 2 wsl2的安装方法,随便搜索一下就有很多,略\n需要在docker-desktop中启用wsl2, 如果设置成功的放在, setting-\u0026gt;resouces中显示如下图\n1.2 nvidia-docker2安装 https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/latest/install-guide.html\n参照些文,如此如此,这般这般,就好了,这里不再展开,没安装成功的再努力百度一下\n相比linux下的步骤简单, 安装完后在docker-desktop中重启一下docker就可以生效\n如果是在linux下, 需要\nsudo systemctl reload-daemon \u0026amp;\u0026amp; sudo systemctl restart docker by the way ,\nlinux的docker的daemon.json在/etc/docker/daemon.json\nwidnows的docker的daemon.json在~/.config/docker/daemon.json\n设置代理会解决很多镜像拉不下来的问题\n1 2 3 4 5 6 7 8 9 10 11 12 { \u0026#34;registry-mirrors\u0026#34;: [ \u0026#34;https://dockerhub.icu\u0026#34;, \u0026#34;https://docker.chenby.cn\u0026#34;, \u0026#34;https://docker.1panel.live\u0026#34;, \u0026#34;https://docker.awsl9527.cn\u0026#34;, \u0026#34;https://docker.anyhub.us.kg\u0026#34;, \u0026#34;https://dhub.kubesre.xyz\u0026#34; ], //...保存原来的.. } 验证安装的话,先拉一个ubuntu镜像(直接用hello-world镜像不行)\n1 docker pull ubuntu20.04 查看镜像\n1 2 3 PS C:\\WINDOWS\\system32\u0026gt; docker images REPOSITORY TAG IMAGE ID CREATED SIZE ubuntu 20.04 9522b5ff29b8 3 weeks ago 72.8MB 然后以这个hello-world镜像运行一个容器,在里面调用nvidia-smi 查看显示信息\n1 docker run --rm ubuntu20.04 --runtime=nivdia --gpus all -it nvidia-smi 如果你机器上有多个gpu,但只想某一些gpu或某一个gpu, 可以查看文档,有多种方式可以约束\n但是如果docker run加了--privileged 参数, 不管你怎么约束,所有gpu都将在docker container内可见.(linux上是如此,windows没有实验过)\n1.3 安装windows上的x11 server 用于接收来自docker container中的gui程序交互消息\n我安装的是这个 x11 server\n安装后需要设置一下 禁用 访问策略或设置相应的规律,让自己的docker中的gui可以转发过来\n我是直接禁用了\n2.构建ubuntu20.04运行时docker 和在使用x11转发在macos上从docker容器中启动gui程序一样,首先构建一个ubuntu20.04的环境,测试显示gui程序和使用容器中的中文输入法\n基本和mac os 那篇记录中的脚本基本一致,只是做了几个改动\n基础镜像改为nvidia-cuda镜像 替换了apt源改为清华大学的apt source 修改docker container workdir为/opt/label-image ,这个目录将会是我部署label-image的目录 2.1 构建docker image 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 FROM nvidia/cuda:12.6.1-cudnn-devel-ubuntu20.04 #FROM --platform=linux/amd64 nvidia/cuda:12.6.1-cudnn-devel-ubuntu20.04 ENV DEBIAN_FRONTEND=noninteractive COPY .gitconfig /root/.gitconfig COPY .vimrc /root/.vimrc RUN cat \u0026lt;\u0026lt;EOF \u0026gt; /etc/apt/sources.list # 默认注释了源码镜像以提高 apt update 速度,如有需要可自行取消注释 deb https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ focal main restricted universe multiverse # deb-src https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ focal main restricted universe multiverse deb https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ focal-updates main restricted universe multiverse # deb-src https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ focal-updates main restricted universe multiverse deb https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ focal-backports main restricted universe multiverse # deb-src https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ focal-backports main restricted universe multiverse # 以下安全更新软件源包含了官方源与镜像站配置,如有需要可自行修改注释切换 deb http://security.ubuntu.com/ubuntu/ focal-security main restricted universe multiverse # deb-src http://security.ubuntu.com/ubuntu/ focal-security main restricted universe multiverse # 预发布软件源,不建议启用 # deb https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ focal-proposed main restricted universe multiverse # # deb-src https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ focal-proposed main restricted universe multiverse EOF RUN \\ apt update \u0026amp;\u0026amp; \\ apt install -y \\ build-essential \\ software-properties-common \u0026amp;\u0026amp;\\ apt update \u0026amp;\u0026amp; \\ apt install -y locales language-pack-zh-hans language-pack-gnome-zh-hans fonts-wqy-zenhei fonts-wqy-microhei ibus-rime librime-data-wubi \u0026amp;\u0026amp; sed -i.bak \u0026#39;s/# zh_CN.UTF-8/zh_CN.UTF-8/g\u0026#39; /etc/locale.gen \u0026amp;\u0026amp; locale-gen \u0026amp;\u0026amp; update-locale LANG=zh_CN.UTF-8 \u0026amp;\u0026amp; ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \u0026amp;\u0026amp; \\ apt install -y \\ autoconf \\ automake \\ bison \\ build-essential \\ cmake \\ gcc \\ g++ \\ git \\ lsb-release \\ make \\ ninja-build \\ python3 \\ rsync \\ tar \\ unzip \\ vim \\ wget \\ curl \\ cowsay \u0026amp;\u0026amp; \\ apt install -y openssh-server \u0026amp;\u0026amp; echo \u0026#39;root:aaa\u0026#39; | chpasswd \u0026amp;\u0026amp; sed -i.bak \u0026#39;s/#PermitRootLogin prohibit-password/PermitRootLogin yes/g\u0026#39; /etc/ssh/sshd_config \u0026amp;\u0026amp; service ssh restart \u0026amp;\u0026amp; \\ apt clean \u0026amp;\u0026amp; \\ mkdir -p /opt/label-image RUN apt install -y gnome \u0026amp;\u0026amp; apt clean COPY .config /root/.config WORKDIR /opt/label-image ENTRYPOINT [\u0026#34;/bin/bash\u0026#34;,\u0026#34;-c\u0026#34; ,\u0026#34;service ssh start \u0026amp;\u0026amp; tail -f /dev/null\u0026#34;] 2.2 启动docker container 我原本想要在windows上使用git bash 直接用 这个脚本启动容器\n不过基于这几个原因重写了一个.bat批处理脚本来启动windows上的容器\ngit bash仿真环境不能执行 docker run命令,会出现问题 windows上的x11服务器是基于 tcp 的,不需要像linux那样挂载 /tmp/.x11-unix display环境变量不像linux那样设置:n,而是host.docker.internal:0,Host在container中的域名:0 runDocker.bat\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 @echo off set display=host.docker.internal:0 set runcmd=docker run ^ --name hbb ^ --privileged ^ -p 2246:22 ^ --gpus all ^ --runtime=nvidia ^ -e DISPLAY=%display% ^ -e LANG=zh_CN.UTF-8 ^ -e XMODIFIERS=\u0026#34;@im=ibus\u0026#34; ^ -e QT_IM_MODULE=ibus ^ -e GTK_IM_MODULE=ibus ^ --entrypoint /bin/bash ^ -e GID=%NUMBER_OF_PROCESSORS% ^ -e UID=%USERNAME% ^ -it ubuntu:20.04 -c \u0026#34;ibus-daemon -xrd \u0026amp;\u0026amp; gedit\u0026#34; %runcmd% 执行这个脚本后, 启动输入法,打开 文本编辑器gedit, 测试输入法没有问题后, 可以开始准备编译程序和部署程序了\n3. label-image 编译部署 3.1 安装qt 开发环境 由于已经支持x11转发了,所以下载 qt 官方的在线安装器就能安装qt 5.15,不是arm64,所以不需要交叉编译qt 源码\n所以\u0026hellip; 略\n唯一需要注意的一点是, 访问host的使用 host.docker.internal\n3.2 编译 略\n3.3 测试label-image docker中使用cuda加速训练 虽然软件写得很烂,但还是成功在windows 上使用cuda加速训练了yolov8,\n不过我的windows电脑的GPU是 1650, 最终还是以out of memory失败告终,但证实这个方案是可行的\n",
"url": "\/post\/windows_wsl2_nvidia-docker2%E9%95%9C%E5%83%8F%E8%AE%B0%E5%BD%95\/"
},
"\/tags\/x11\/": {
"title": "X11",
"tags": [],
"content": "",
"url": "\/tags\/x11\/"
},
"\/tags\/macos\/": {
"title": "MacOS",
"tags": [],
"content": "",
"url": "\/tags\/macos\/"
},
"\/post\/%E4%BD%BF%E7%94%A8x11%E8%BD%AC%E5%8F%91%E5%9C%A8macos%E4%B8%8A%E4%BB%8Edocker%E5%AE%B9%E5%99%A8%E4%B8%AD%E5%90%AF%E5%8A%A8gui%E7%A8%8B%E5%BA%8F\/": {
"title": "使用x11转发在macos上从docker容器中启动gui程序",
"tags": ["X11","MacOS","Docker","GUI",],
"content": "0.写在前面 之所以要研究这个,主要是我写的一个Yolo目标检测AI 模型的辅助训练软件label-image不支持Windows平台, 如果需要在Windows上使用, 需要在Windows上启动ubuntu的docker ,再从中启动 ubuntu版本的label-image 通过转发x11消息的方式显示,在Nvidia-docker2的加持下, 直接从docker中访问windows上的显卡,使用cuda加速模型训练\n虽然目前MacOS的MPS不管是性能还是配套,都没有Nvidia CUDA做得好,不支持从docker中硬件加速,但我还是觉得有必要研究一下,毕竟将来也许也就支持了.\n1.安装 XQuartz XQuartz\n为了让mac os可以作为X11服务器,接收来自linux的x11消息\n安装XQuartz ,安装完后,配置为允许从网络客户端接收消息\n设置允许的x11客户端\n1 xhost + #表示不过滤任务客户端ip 2. 创建一个ubuntu docker镜像,安装中文输入法 dockfile\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 FROM --platform=linux/amd64 ubuntu:20.04 #FROM ubuntu:20.04 ENV DEBIAN_FRONTEND=noninteractive COPY .gitconfig /root/.gitconfig COPY .vimrc /root/.vimrc RUN \\ apt update \u0026amp;\u0026amp; \\ apt install -y \\ build-essential \\ software-properties-common \u0026amp;\u0026amp;\\ apt update \u0026amp;\u0026amp; \\ apt install -y locales language-pack-zh-hans language-pack-gnome-zh-hans fonts-wqy-zenhei fonts-wqy-microhei ibus-rime librime-data-wubi \u0026amp;\u0026amp; sed -i.bak \u0026#39;s/# zh_CN.UTF-8/zh_CN.UTF-8/g\u0026#39; /etc/locale.gen \u0026amp;\u0026amp; locale-gen \u0026amp;\u0026amp; update-locale LANG=zh_CN.UTF-8 \u0026amp;\u0026amp; ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \u0026amp;\u0026amp; \\ apt install -y \\ autoconf \\ gnome \\ automake \\ bison \\ build-essential \\ cmake \\ gcc \\ g++ \\ git \\ lsb-release \\ make \\ ninja-build \\ python3 \\ rsync \\ tar \\ unzip \\ vim \\ wget \\ curl \\ cowsay \u0026amp;\u0026amp; \\ apt install -y openssh-server \u0026amp;\u0026amp; echo \u0026#39;root:aaa\u0026#39; | chpasswd \u0026amp;\u0026amp; sed -i.bak \u0026#39;s/#PermitRootLogin prohibit-password/PermitRootLogin yes/g\u0026#39; /etc/ssh/sshd_config \u0026amp;\u0026amp; service ssh restart \u0026amp;\u0026amp; \\ apt clean \u0026amp;\u0026amp; \\ mkdir -p /opt/label-image #COPY .config /root/.config WORKDIR /opt/label-image ENTRYPOINT [\u0026#34;/bin/bash\u0026#34;,\u0026#34;-c\u0026#34; ,\u0026#34;service ssh start \u0026amp;\u0026amp; tail -f /dev/null\u0026#34;] 第14行,设置时区,本地化,安装字体,输入法等\n第36行设置open ssh 服务,支持以root用户远程登录\n第40行,复制输入法相关的配置,.config 文件夹是在配置好输入法后复制出来的文件夹,可选\nbuild docker的指令\n1 docker build . -f dockfile -t repos:version 3.启动docker container 创建一个 脚本来启动docker 容器 runDocker.sh\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 #!/bin/bash display=${HOSTNAME}:0 runcmd=\u0026#34;docker run \\ --name hbb --privileged \\ --device /dev/mem \\ -p 2246:22 \\ -e DISPLAY=$display \\ -e LANG=zh_CN.UTF-8 \\ -e XMODIFIERS=\\\u0026#34;@im=ibus\\\u0026#34; \\ -e QT_IM_MODULE=\\\u0026#34;ibus\\\u0026#34; \\ -e GTK_IM_MODULE=\\\u0026#34;ibus\\\u0026#34; \\ -e GID=`id -g` \\ -e UID=`id -u` \\ -v /tmp/.X11-unix/:/tmp/.X11-unix \\ --entrypoint /bin/bash \\ --rm -it label-image:u2004 -c \u0026#39;ibus-daemon -xrd \u0026amp;\u0026amp; gedit\u0026#39;\u0026#34; eval $runcmd 与输入法相关的,除了几个环境变量外,还需要挂载/tmp/.X11-unix 以转发x11消息\n脚本入口点为/bin/bash, 参数为 -c ibus-daemon -xrd \u0026amp;\u0026amp; gedit 表示先启动输入法的守护进程,再打开gedit文本编辑器\n然后脚本权限后 运行脚本\n1 2 sudo chmod +x runDocker.sh ./runDocker.sh 这时会看到启动了一个dockr,并以交互的方式打开了容器中的gedit\n如果在步骤2中没有第40行,这时是无法使用输入法的,\n可以重新开一个终端进入docker中\n1 docker exec -it hbb bash #hbb是步骤3脚本中--name 参数指定的 1 ibus-setup #弹出Ibus输入法配置窗口 设置好输入法后,把 ~/.config/dconf 和 ~/.config/ibus 复制出来, 补充到步骤2的第40行处,\n重新执行一下docker build,这时会很快速的基于上一次缓存的结果再生成一个带有输入法配置的docker image\n4.附录 4.1设置ibus默认输入法的方法 我没有找到从命令行配置输入法的方法, (否则就能直接在run脚本中配置)\n所以我的方式是,先启动命令行,在里面配置好输入法,再把相关的配置以文件的方式保存,\n再做一个docker image把配置文件覆盖进去,或挂载去去\nubuntu与ibus配置相关的配置在 ~/.config/dconf\nibus-rime的配置文件在 ~/.config/.config/ibus\n在步骤3中启动的docker中,配置ibus-rime为输入法\n1 ibus-setup 这时会转发x11到mac os的x11 server上, 弹出配置窗口\n添加rime输入法, 最好也设置一下自己喜欢的切换输入法的快捷键,不要和mac os冲突了.\n然后修改~/.config/ibus/rime 里的配置, 这个因人而异,我就此略过\n都做好修改后,把~/.config/dconf和~/.config/ibus 文件备份出来,如果对命令行复制不熟悉,可以使用fileZilla之类的支持sftp或scp的图形化工具, 在步骤2中已经把host的2246u端口转发到docker container中了,\n再做一个docker, 在步骤2中前几行,把备份的文件覆盖进去新的镜像中,即可\n4.2 x11 消息转发显示的 GUI程序+输入法 效果 ",
"url": "\/post\/%E4%BD%BF%E7%94%A8x11%E8%BD%AC%E5%8F%91%E5%9C%A8macos%E4%B8%8A%E4%BB%8Edocker%E5%AE%B9%E5%99%A8%E4%B8%AD%E5%90%AF%E5%8A%A8gui%E7%A8%8B%E5%BA%8F\/"
},
"\/tags\/radxa\/": {
"title": "Radxa",
"tags": [],
"content": "",
"url": "\/tags\/radxa\/"
},
"\/post\/radxa-rock5c-%E5%BC%80%E5%8F%91%E7%AC%94%E8%AE%B0\/": {
"title": "radxa-rock5c-开发笔记",
"tags": ["Radxa","Rock5C","NVME","UART",],
"content": "1. 从NVME 启动系统 从官方的文档上看rock5c, 你会有一种看山跑死马的感觉, 因为它在一本正经的胡说八道\n怀疑它只是把rock 5b或5a的文档复制过来改一一改\n按他们文档上刷nvme的文档,你将永远不可能成功, 因为rock 5c 从emmc或nvme启动,只能二选一\n因为pin脚不够用.但是他们文档上没有指出这点, 截止2024-07-27\n如果不想买额外的模块,又想从nvme启动系统,唯的一选项是 把系统刷到 sd中,\n这里sd卡中有三个分区\nbootloader 硬件配置分区 操作系统分区 然后把sd卡的 第二个分区和第三个分区格式化后,这样当sd卡中的bootloader找不到系统,会尝试从pcie启动\n前提是系统已经识别到nvme设备\n1 $lsblk #查看是否有nvme设备 格式化可以把sd加载到linux,使用fdisk\n1 2 3 4 5 6 $lsblk #查看是否正常识别sd卡设备 $fdisk /dev/sdc #进入交互式cli d 2 #删除第二分区 d 3 #删除第三分区 w #保存 q #退出 然后把官网下载的镜像烧录到m2, 接入rock5c\n启动后应该可以看到系统挂载在nvme\n1 df -h 可以买容量比较小的sd卡, 10元以内的, 烧录系统失败也没关系,因第一个分区应该还是成功的,失败了应该不影响使用sd卡作为bootload\n这样比较经济实惠\n2. 开机不自动WIFI 关闭 钱包服务\nkwalletmanager中禁用掉\n把wireless网络删除,重新创建 无线连接\n设置固定ip地址 为wifi生成一个随机的mac (radxa估计没有买mac地址,所以没有mac地址导致dhcp服务器不会缓存,每次重新开机都是新的ip,真是满满的惊喜)\n需要在/config/config.txt中设置\n1 connect_wi-fi YOUR_WIFI_SSID YOUR_WIFI_PASSWORD 如果 是刚刚完系统,可能会有一个before.txt, 也在里面加\n3 . ssh服务不自动启动 1 sudo systemctl enable ssh 4. UART sudo rsetp 中选择overlays 选择manager 后,进入选择想要的uart,再选择ok, 就会配置对应的uart 然后再选择一下rebuild overlays\n如果想确认uart对应的pin脚是哪个, 可以看官方的文档中的硬件目录中的一个表格\n也可以在 rsetup中 overlays目录选择一个 view indo的选项, 选择想查看的功能后,Ok,会逐个显示其pin脚信息\n强烈推荐使用 ttys2 ttyS4 ttyS6 (也就是配置界面中前3个uart)\n理由: ttys2, ttys4 和 raspberry pi 5 的pin脚是兼容的, ttyS6则可以配置为调试用的串口\n1 2 3 4 5 6 7 8 sudo rsetup #树莓派5 #uart0管脚8,10 #uart2管脚7,29 #与rock5c的uart2-m0和uart4-m2 管脚号一样 #uart6-m1主要用作调试用 #所以开这三个串口 ok后, 到同上级菜单选择重新编译,然后重启\n重启后会多出三个串口ttyS2 ttyS4 ttyS6\n2,4和树莓派串口的40pin 管脚号可以兼容,6不兼容, 所以把6作为调试用的串口,顺便把波特率改为和树莓派兼容的115200\n默认的1500000 太非主流了\n修改完执行\n1 sudo u-boot-update Rock5/hardware/5b/gpio - Radxa Wiki\noverlays/arch/arm64/boot/dts/rockchip/overlays/rk3588-fiq-debugger-uart2m2.dts at main · radxa/overlays (github.com)\nRock5/guide/hardware-configuration - Radxa Wiki\nRsetup | Radxa Docs\n硬件接口说明 | Radxa Docs\n4.1 测试uart的脚本 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 # 测试脚本 tst.py import serial import argparse import threading import time from datetime import datetime def send_data(ser): while True: current_time = datetime.now().strftime(\u0026#39;%Y-%m-%d %H:%M:%S\u0026#39;) ser.write(current_time.encode()) print(\u0026#34;Sent: {}\u0026#34;.format(current_time)) time.sleep(5) # 每5秒发送一次 def receive_data(ser): while True: if ser.in_waiting \u0026gt; 0: received_data = ser.readline().decode().strip() print(\u0026#34;Received: {}\u0026#34;.format(received_data)) def main(): parser = argparse.ArgumentParser(description=\u0026#34;Serial port listener and responder\u0026#34;) parser.add_argument(\u0026#39;--port\u0026#39;, type=str, required=True, help=\u0026#39;Serial port to use, e.g., /dev/ttyUSB0 or COM3\u0026#39;) parser.add_argument(\u0026#39;--baudrate\u0026#39;, type=int, required=True, help=\u0026#39;Baud rate for the serial communication\u0026#39;) args = parser.parse_args() try: with serial.Serial(args.port, args.baudrate, timeout=1) as ser: print(\u0026#34;Listening on {} at {} baud.\u0026#34;.format(args.port,args.baudrate)) # 创建发送和接收线程 send_thread = threading.Thread(target=send_data, args=(ser,)) receive_thread = threading.Thread(target=receive_data, args=(ser,)) # 启动线程 send_thread.start() receive_thread.start() # 确保主线程不会提前结束 send_thread.join() receive_thread.join() except serial.SerialException as e: print(\u0026#34;Error: {}\u0026#34;.format(e)) if __name__ == \u0026#34;__main__\u0026#34;: main() 1 2 3 4 python -m venv test # 创建一个名为test的python虚拟环境 cd test source bin/active # 激活python虚拟环境 python -m pip install pyserial #安装 py的串口库, 测试脚本会使用这个库 测试脚本用法:\n如果当前用户没有在dialout用户组先添加进去,否则将没有使用/dev/tts 的权限\n1 2 3 4 $sudo usermod dialout $USER #需要重启登录一下才生效 #可以用groups 来确认是不是有在这个权限组 $goups ,在激活python虚拟环境后,\n1 sudo chmod +x ./tst.py --port /dev/ttyS2 --baudrate 115200 5 . GPIO 可以参考我的博客中raspberry pi 5的gpio部分,都是使用 gpiod,\n唯一需要注意的是, raspberry pi 5 40 pin脚上的gpio都是来自gpio chip 4\nrock 5c 则是把多个chip 上的pin脚都引到40 pins上面了\n还有一个转换规则,\nrock 5c 中每个chip 的32pin被分为abcd4组 每组8个Pin\nA 0-7 B 8-15 C 16-23 D 24-31 例如 radxa系统中的GPIO3_B2 对应 gpiod中的 gpio chip1 11 line\n",
"url": "\/post\/radxa-rock5c-%E5%BC%80%E5%8F%91%E7%AC%94%E8%AE%B0\/"
},
"\/tags\/rock5c\/": {
"title": "Rock5C",
"tags": [],
"content": "",
"url": "\/tags\/rock5c\/"
},
"\/tags\/uart\/": {
"title": "UART",
"tags": [],
"content": "",
"url": "\/tags\/uart\/"
},
"\/tags\/amd\/": {
"title": "AMD",
"tags": [],
"content": "",
"url": "\/tags\/amd\/"
},
"\/post\/amd-zynq-freertos-%E5%BC%80%E5%8F%91%E7%AC%94%E8%AE%B0\/": {
"title": "amd-zynq-freeRTOS-开发笔记",
"tags": ["C","AMD","Zynq","FreeRTOS",],
"content": "0.序 Zynq®-7000 系列基于 Xilinx SoC 架构。这些产品在单个器件中集成了功能丰富的基于 ARM® Cortex™-A9 的双核或单核处理系统 (PS) 和 28 nm Xilinx 可编程逻辑 (PL)。 ARM Cortex-A9 CPU 是 PS 的核心,还包括片上存储器、外部存储器接口和丰富的外设连接接口。\n本文档记录的是 zynq 7020开发过程中的一些经验\n7020上PS端有两个ARM 核 ,还有一个FPGA用的PL\n我们的FPGA工程师使用Vivado完成PL端的工程配置,编码后,导出.xsa 文件,\n我和我组内的小伙伴使用Vitis ,将.xsa生成为 PS端的工程\n使用xilinx BSP中集成的SDK和FreeRTOS,进行裸机开发\n由我同事完成网络相机核心代码\n我作为摸鱼罪犯,被发配到这个项目做一些比较有手就行的任务\n裸机意味着很多工具都不能用了,比如ssh, shell,文件系统, 还有c++的libc\n所以我在这个项目中主要用c进行下面这几项开发\n文件系统移植,验证 (bsp中有现成的) FreeRTOS串口命令行CLI工具移植 (用来平替Linux的ssh + shell 格式化文件系统 上传文件 打印文件内容 打包程序,制作引导,烧录固化 1.FreeRTOS串口CLI 移植 zynq 7020有两个串口,默认会把启动信息写到第0个, 第1个串口则预留着,\n我们的硬件工程师只把串口0 暴露到设备外壳上,所以我们要做的是 zynq 默认串口通道上 加上CLI命令行交互功能以及传输文件功能,\n1.1 zynq相关源码获取 签出FreeRTOS源码,切换到vitis 内置的FreeRTOS对应的版本,对于我来说是10.4.x ,我们需要下列这些\nCLI插件整个目录\n串口CLI实现\n几个示例命令实现\nZynq串口头文件 和 Zynq的串口实现 (官方提供了一个基于队列的串口,性能比较差,但够用,如果想更快,需要自己用DMA实现\n1.2 修改源码 1.2.1 解决编译错误 把 1.1中2,3,4, 文件放放 1 目录中, 把目录放到你的Vitis工程中,设置为Include path\n添加一个源码中用到的的宏\n1 #define configCOMMAND_INT_MAX_OUTPUT_SIZE 2096 用于定义cli 输出buff的大小,如果不定义会编译报错\n官网示例是加在FreeRTOSConfig.h里, 不过vitis的freeRTOS代码会在你修改bsp配置时从安装目录复制覆盖项目中的,所以建议把这个宏加到自己工程里\n1.2.2 串口号修改 然后确认一下自己想要用于命令行交互的串口端口是0或1,如果是1的话,不需要修改serial.c (在步骤1.1.4中获取)\n如果是0的话,需要修改源码中的宏,\n修改内容参照下面的diff 图片, 左边是串口0端口,右边是串口1端口, 有2个宏需要修改\n1.2.3 自定义cli 命令 在 Sample-CLI-Commands.c中有vRegisterSampleCLICommands方法,用于注册命令\n在UARTCommandConsole.c中 收到用户按钮下换行后,会自动分割命令,校验参数个数,调用注册的命令结构体中的函数指针,传递参数\n1.2.4 在main()中启动命令行cli并注册命令 70 int main( void )\r1 {\r2 /* Start the tasks that implements the command console on the UART, as\r3 described above. */\r4 vUARTCommandConsoleStart( mainUART_COMMAND_CONSOLE_STACK_SIZE, mainUART_COMMAND_CONSOLE_TASK_PRIORITY );\r5\r6 /* Register the standard CLI commands. */\r7 vRegisterSampleCLICommands();\r8 vUARTCommandConsoleStart 用于启动一个较高优先级的freeRTOS的task,\nvRegisterSampleCLICommands用于注册 1.2.3中写的那些命令回调,详细逻辑查看UARTCommandConsole.c中的实现\n1.2.5 关于上传文件 在CLI/UARTCommandConsole.c,可以看到, 注册的命令回调会被调用1次或多次,直到返回 1\n利用好这个特点,可以写一个循环接收串口数据的命令\n2.烧录固化 2.1 制作固件 之前我们在PS跑linux时,Zynq 的启动顺序为\nbootROM (固化在硬件中,不可修改) fsbl (第一启动阶段) uboot linux 现在我们在PS跑裸机程序,不再需要uboot\nbootROM fsbl (用vivado导出的.xsa生成的) .bit 和 .elf 所以在vitis中创建boot.bin时这样选择\nboot.bin的三个分区,\n第一个是fsbl.elf, partition type为bootloader\n第二个是PL的 .bit , partition type为datafile\n第三个是PS的.elf , partition type为datafile\n2.2 开启硬件服务 用 jtag电缆连接 zynq 和 pc\n打开vivado, 在主界面下面的命令行输入\n1 hw_server 这时会在3121端口打开一个硬件server, 其他电脑或本机可以通过这个服务在线下载或烧录程序\n2.3 烧录 创建一个批处理脚本 flash.bat\n1 2 3 4 set cmdpath=D:\\Xilinx\\SDK\\2018.3\\bin\\ set program_flash=%cmdpath%program_flash %program_flash% -f .\\BOOT.BIN -offset 0 -flash_type qspi_single -fsbl .\\fsbl.elf -cable type xilinx_tcf url TCP:127.0.0.1:3121 把BOOT.bin和 fsbl.elf 和这个脚本放在一起,\n执行\n1 ./flash.bat 此时应该看到程序正在烧录, 如果有错误或烧录完成,都会在命令窗口体现\n2.4 注意 2021的vitis, vivado不支持 zynq 7020的烧录(没有qsip-single选项),可能是因为硬件太旧软件太新\n所以 步骤2.3中,我才使用调用 vivado 2018版本的sdk进行烧录\n3. 附录:问题记录 vitis 2021固化 zynq 7020失败\n新的 ide 的qsip flash模式不支持 经典款 zynq 7020芯片相应的qsip flash模式 (qsip-single),\n用2021项目生成的 fsbl+ bit+ elf 生成 boot.bin\n再用vitis 2018 + vtis 2018 hello wold的 .fsbl 文件 烧录 boot.bin可以成功烧录+ 启动\n程序下载完成,但没有启动,弹窗提示 mmu section translation fault\n固化的程序的MMU中的地址和在线 jtag 写入的系统MMU sections信息不匹配,重新固化即可\nxilffs文件系统创新文件时提示打开失败\n确保已经格式化了文件系统后\n文件名称限制在8个字符内就没问题\nxilffs文件系统打开文件后,第二次次调用f_seek写入文件失败\n可能是因为xilffs是一个很简陋的文件系统,所以创建文件后只支持写一次,关闭\n这个不太确定,暂时开一个大buffer用来接收串口字节流,一次性写入文件\n程序编译时不会自动生成boot.bin\n在 project_system项目右键 , create boot image,\n依次填入.fsbl .bit .elf后, 生成一次boot.bin,之后每次编译都会生成\n生成的boot.bin不能烧录固化成功,但用可以在线写入jtag, 在线调试\n检查.bit文件是不是和用来生成fsbl项目的 FPGA导出的 .xsa是对应的,如果不是,可能会出现固化失败,但jtag可以在线写入的情况\n",
"url": "\/post\/amd-zynq-freertos-%E5%BC%80%E5%8F%91%E7%AC%94%E8%AE%B0\/"
},
"\/tags\/c\/": {
"title": "C",
"tags": [],
"content": "",
"url": "\/tags\/c\/"
},
"\/tags\/freertos\/": {
"title": "FreeRTOS",
"tags": [],
"content": "",
"url": "\/tags\/freertos\/"
},
"\/tags\/zynq\/": {
"title": "Zynq",
"tags": [],
"content": "",
"url": "\/tags\/zynq\/"
},
"\/tags\/ctags\/": {
"title": "CTags",
"tags": [],
"content": "",
"url": "\/tags\/ctags\/"
},
"\/tags\/vim\/": {
"title": "VIM",
"tags": [],
"content": "",
"url": "\/tags\/vim\/"
},
"\/post\/vim-ctag-%E7%AC%A6%E5%8F%B7%E6%9F%A5%E6%89%BE\/": {
"title": "vim-ctag-符号查找",
"tags": ["C\u002b\u002b","C","VIM","CTags",],
"content": "0x00 为什么使用CTag 长久以来, 我一直在ide中使用VIM插件,除此之外在Bash,Zsh也使用vim作为基本的文本编辑器, 所有的Git操作也都都交给了VIM+ Shell,在文本编辑方面,我使用统一的一套快捷键方案\n我使用过的IDE:visual studio , visual studio code , qt creator\n他们都有很好的VIM插件,并且集成了很好的符号查找,所以作为实用主义者,我一直觉得没有使用CTag+VIM的必要\n直到上周疯狂摸鱼后,被抓去写 C \u0026amp; Embedded, 我接触到了 Xilinx Vitis这款AMD旗下的IDE,这货竟然没有VIM键盘方案\n整一周在这条路上我走得像一个瘸子,痛定思痛,我认为是时候端出CTag这根阵年火腿了\n0x01 CTag安装+生成索引 0x0101 安装 由于编译服务器是Ubuntu,所以先只记录Ubuntu, 其他平台大差不差\n1 $ sudo apt-get update \u0026amp;\u0026amp; sudo apt-get install -y exuberant-ctags 0x0110 生成索引 写一个用于生成c/c++ tag的脚本 ctag.sh\n1 2 3 4 5 6 7 8 9 #!/bin/bash regex=\u0026#34;^.+.+$\u0026#34; if [[ ! $1 =~ $regex ]]; then echo \u0026#34;需要输入生成tag的目录的绝对路径,不能是‘.’\u0026#34; exit 1 fi cd $1 ctags -R --languages=c,c++ --exclude=node_modules --c++-kinds=+p --fields=+iaS --extra=+q . -R:递归处理当前目录及其子目录中的所有文件。\n--languages=c,c++:指定要处理的编程语言为 C 和 C++。\n--exclude=node_modules:排除 node_modules 目录,不处理该目录下的文件。\n--c++-kinds=+p:在 C++ 中包括函数原型(prototypes)标签。\n--fields=+iaS:为生成的标签添加额外的信息。\ni:包含继承信息(inheritance information)。 a:包含类成员的访问控制信息(access control information),如 public、protected 和 private。 S:包含标签的范围信息(scope information),即函数、类或结构体所在的范围。 --extra=+q:为标签添加额外的扩展信息。\nq:包含类的构造函数和析构函数。 使用脚本\n1 2 3 sudo chmod +x ctag.sh cd src ctag.sh $(pwd) 0x10 在VIM中使用CTag 索引 在.vimrc 中配置 tags的路径\n1 set tags=./.tags;$HOME #从当前目录向上递归搜索.tags目录,直到到$HOME目录 在 Vim 中打开任何源文件,并使用以下基本命令:\n键盘命令 行动 Ctrl + ] 或 或g] :ta[g] Ctrl+rw 使用标签文件中的信息跳转到光标下方的标签 :ts[elect] \u0026lt;tag_name\u0026gt; 使用标签文件中的信息列出匹配的标签。如果未给出,则使用标签堆栈中的最后一个标签名称\u0026lt;tag_name\u0026gt;``\u0026lt;tag_name\u0026gt; :pts[elect] \u0026lt;tag_name\u0026gt; 在不移动光标的情况下在“预览”窗口(水平拆分)中执行并显示新标签:tselect Ctrl + w } 或 :ptag Ctrl+rw 打开一个预览窗口,其中包含标记定义的位置。光标不会更改其位置,因此不会更新标签堆栈 Ctrl + wz 或 :pc 关闭命令创建的预览窗口Ctrl+w } Ctrl + w Ctrl + ] 在水平拆分中打开定义 :tn 跳转到下一个匹配标签(如果有多个匹配项) :tp 跳转到上一个匹配标签(如果有多个匹配项) Ctrl-t 在标签堆栈中跳回 :tags 显示标签堆栈的内容。活动条目标有\u0026gt;\u0026lt;br / :tag 正则 用正则搜索tag 也可以在shell中直接使用vim 打开 搜索的tag\n1 vim -t \u0026#39;/vimregex\u0026#39; 0x11 其他补充 需要注意,修改程序后需要重新生成ctag,\n0x1100 代码补全 生成ctags后也可以用地代码补全, ctrl+n, ctrl+p\n0x1101 头文件切换 光标定位于#include gf 可以跳到头文件, 前是在.vimrc中配置了path+=path2include\n也可以配置 在.vimrc 中配置path+=**\n这样vim中使用 :find 就会递归搜索,\n然后\n: find %:r.c 切换到当前header对应的source\n或\n: find %:r.h 切换到当前source对应的header\n",
"url": "\/post\/vim-ctag-%E7%AC%A6%E5%8F%B7%E6%9F%A5%E6%89%BE\/"
},
"\/tags\/jetson\/": {
"title": "Jetson",
"tags": [],
"content": "",
"url": "\/tags\/jetson\/"
},
"\/tags\/nvidia\/": {
"title": "Nvidia",
"tags": [],
"content": "",
"url": "\/tags\/nvidia\/"
},
"\/post\/nvidia-jetson%E5%BC%80%E5%8F%91%E7%AC%94%E8%AE%B0\/": {
"title": "nvidia-jetson开发笔记",
"tags": ["C\u002b\u002b","Nvidia","Jetson",],
"content": " 2024.05.01 更新 jetson orin nx 刷nvme\n一开始只针对jetson xavier写文档,后来公司又用了jetson orin, 所以就一起写一个文档里了, jetson orin可以刷最新的 jetson linux 和 jetpack, 不过为了只维护一个版本, 我决定不管是xavier还是orin 都使用 xavier能用的最新版本, 即 jetpack 5.1.3+ jeson linux 35.5.0\n1. 刷机 jetson xavier NX开发板官方推荐的方式是使用刷sd卡的方式, 也可以使用flash.sh脚本或l4t_initrd_flash.sh直接刷,这样可以省略制作镜像的步骤\n截止2024.3.26, jetson xavier NX已然停产,不过仍然可以使用 JetPack 5.1.3 开发包进行开发\n我使用我的ubuntu16.04 开发环境的虚拟机制作镜像及烧录脚本烧录,\n也可以使用官方的gui刷机工具sdk-manager(ps 名称有点忘记了,好像是这个),不过那需要ubuntu18或ubuntu20\n1.1 准备工作 在Jetson Linux | NVIDIA Developer下载这两个\nTitle Driver Package (BSP) Sample Root Filesystem 最好在linux中下载,因为windows文件系统不支持符号链接文件,我担心解压后会影响文件完整性\n下载后得到\n1 2 3 4 5 6 ~/Downloads/nvidia/Linux_for_Tegra$ ll total 2229716 drwxrwxr-x 3 deepvision deepvision 4096 3月 26 13:10 ./ drwxr-xr-x 11 deepvision deepvision 4096 3月 26 12:03 ../ -rwxrw-rw- 1 deepvision deepvision 759217542 3月 26 11:10 Jetson_Linux_R35.5.0_aarch64.tbz2* -rwxrw-rw- 1 deepvision deepvision 1523988703 3月 25 11:27 Tegra_Linux_Sample-Root-Filesystem_R35.5.0_aarch64.tbz2* 解压出Linux_for_Tegra目录\n1 ~/Downloads/nvidia$ tar xf Jetson_Linux_R35.5.0_aarch64.tbz2 解压根目录\n1 ~/Downloads/nvidia$ sudo tar xpf Tegra_Linux_Sample-Root-Filesystem_R35.5.0_aarch64.tbz2 -C Linux_for_Tegra/rootfs/ 组装\n1 ~/Downloads/nvidia/Linux_for_Tegra$ sudo ./apply_binaries.sh 为执行脚本做准备\n1 ~/Downloads/nvidia/Linux_for_Tegra$ sudo ./tools/l4t_flash_prerequisites.sh 执行过程可能会提示需要安装一些包,按照提示安装就行了\n1.2 jetson xavier 1.2.1 刷sd卡 制作刷机用的镜像包\n1 ~/Downloads/nvidia/Linux_for_Tegra/tools$ ./jetson-disk-image-creator.sh -o \u0026lt;blob_name\u0026gt; -b \u0026lt;board\u0026gt; \u0026lt;blob_name\u0026gt; 为输出的镜像名称\n\u0026lt;board\u0026gt; 则通过查表的方式获取 jetson-modules-and-configurations\n最终我需要执行的是\n1 ~/Downloads/nvidia/Linux_for_Tegra/tools$ sudo ./jetson-disk-image-creator.sh -o sd-blob.img -b jetson-xavier-nx-devkit -d SD 执行过程可能会提示需要安装一些包,按照提示安装就行了\n如python缺少yaml模块\n1 $python3 -m pip install pyyaml 1.2.2 刷eMMC和sd卡 如果之前用的系统是安装在NVMe m2上, 但切换回sd卡,用上面的方式可能会失败,大概是因为这种方式只刷系统到卡上, 不会刷QSPI-NOR/eMMC中的引导\n如果遇到这种情况,需要强制刷机,把板子电断后,短接板子的 GND和FC REC(9,10 管脚),再上电,进入recovery模式(这时可以取下短接线,实测不取也不影响),通过Usb线刷机,执行\n1 sudo ./flash.sh jetson-xavier-nx-devkit internal 注意,以上说的都是jetson-xavier-nx 其他板子有强制恢复按钮\n1.2.3 刷 nvme 理论上应该使用sdkmanage安装,但我的开发环境是ubuntu16,所以仍然使用脚本\n官网的资料很少,只是提到需要先刷emmc,才能从nvme启动,\n我试了好几次,最好的结果只是完成了1.3,再把nvme 通过systemctl把/挂载以m2 ssd上,\n但这仍然是从sd卡启动,只是提升了系统空间,对启动加速没有什么提升,并且sd卡需要一直插着才能用\n所幸github上有不需要sd卡的方案\n使用这种方式,可以在没有sd卡的方式下刷nvme,并且启动时也是直接从nvme启动\n把板子电断后,短接板子的 GND和FC REC(9,10 管脚),取下sd卡,只接nvme M2,连接Usb, 运行脚本 ,等到脚本提示,取下短接跳线,之后脚本就会一直自动执行,执行完后,板子会重启后,连接Host的ssh server, 需要输入host密码,\n1.3 jetson orin 1.3.1 刷 nvme 由于xavier 官方文档上关于无sd卡刷nvme的方法语焉不详,所以借助了github上的脚本, 但其实那个脚本也是使用了官方的刷新脚本,只是参数让人眼花缭乱,\njetson orin 官方文档上有明确的指示应该怎么把 jetson linux + jetpack 一起刷到nvme上的方法\n1 sudo ./flash.sh jetson-orin-nano-devkit-nvme internal 2. 无界面模式(可选) 2.1 电源模式 在我们的需求中, xavier nx只有在出厂前的调试模式下,才需要有界面,其他时间不需要\n所以我们需要在命令行中切换电源模式\n1 sudo nvpmodel -m 8 //20w 8核 因为需要sudo 所以我们可以把他放到2.2启动服务的启动脚本run.sh中,这样它就可以有root权限了\n通过uname -r查看kernel名称是否包含tegra来判断是否是nivida的板,如果是就设置nvpmodel\n1 2 3 4 5 6 7 ... shopt -s nocasematch #匹配时忽略大小写 string=\u0026#34;$(uname -r)\u0026#34; if [[ $string =~ .*tegra.* ]]; then nvpmodel -m 8 fi ... 验证vnpmodel是否设置成功\n1 2 3 dv@dv-desktop:~$ sudo nvpmodel -q NV Power Mode: MODE_15W_2CORE 0 2.2 服务安装/卸载脚本 install.sh\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 #!/bin/bash cat \u0026lt;\u0026lt; EOF \u0026gt; /etc/systemd/system/dv_app.service [Unit] Description=dv app After=network.target [Service] Type=simple ExecStart=/opt/dv_app/run.sh WorkingDirectory=/opt/dv_app/ Restart=always RestartSec=3 [Install] WantedBy=multi-user.target EOF systemctl enable dv_app systemctl start dv_app uninstall.sh\n1 2 3 4 5 #!/bin/bash systemctl stop dv_app systemctl disable dv_app rm /etc/systemd/system/dv_app.service systemctl daemon-reload 2.3 qt程序修改无界面 注意,记得把qt程序启动时的qpa 设置为和vnc或其他支持仅命令行环境的模式,\n1 export QT_QPA_PLATFORM=vnc 并把qt程序中启动窗口的代码去掉,只执行\n1 2 3 4 5 6 int main() { QApplication app; //不要启动窗口 app.exec(); } 如果代码结构没有设计好,估计需要有一些重构工作,好在我使用mvvm设计模式,不需要修改代码,可以在没有ui的情况下运行业务逻辑\n2.4 linux设置无界面 linux可以用 runlevel 来指定系统启动时进入哪一种模式,常见的是命令行3和图形界面5,\n不同的系统设计方法不一样,因为我给xavier nx安装了最新的jet pack,所以他的系统是ubuntu20\nubuntu20 已经不再使用编etc/inittab的方式设置runlevel,而是使用systemctl\n查看可用的选项\n1 2 3 dv@dv-desktop:~$ systemctl list-units --type=target|grep -E \u0026#39;multi-user.target|graphical.target\u0026#39; graphical.target loaded active active Graphical Interface multi-user.target loaded active active Multi-User System graphical.target 是图形模式\nmulti-user.target 是命令行模式\n1 2 3 4 5 6 7 dv@dv-desktop:~$ systemctl status graphical.target ● graphical.target - Graphical Interface Loaded: loaded (/lib/systemd/system/graphical.target; indirect; vendor preset: enabled) Active: active since Mon 2024-04-01 15:05:11 CST; 2h 3min ago Docs: man:systemd.special(7) 4月 01 15:05:11 dv-desktop systemd[1]: Reached target Graphical Interface. 设置为命令行模式\n1 2 3 4 dv@dv-desktop:~$ sudo systemctl set-default multi-user.target [sudo] password for dv: Removed /etc/systemd/system/default.target. Created symlink /etc/systemd/system/default.target → /lib/systemd/system/multi-user.target. 设置完重启一下\n1 reboot 3. cuda交叉编译设置 为了让jetson orin nx和jetson xavier nx使用一样的系统(维护成本比较少),\n最新的版本是 Jetson linux 36.2 + Jetpack 6.0 DP(dp开发预览版本)\n我们刷的是 Jetson linux 35.5.0 + Jetpack 5.1.3\n版本兼容遵向后兼容,向前兼容,和小版本兼容\njetpack 5.1.3 的cuda版本是11.4, 可以升级,不过我们算法用的是11.3,所以我决定暂时不升级\n下面是cuda 11.4的文档\nInstallation Guide Linux :: CUDA Toolkit Documentation (nvidia.com)\n3.1 安装cuda编译器nvcc CUDA Toolkit 11.4 Update 4 Downloads | NVIDIA Developer\nlocal方式下载速度比较快, network的方式不额外设置apt source镜像很慢\n1 2 3 4 5 6 7 wget https://developer.download.nvidia.com/compute/cuda/repos/wsl-ubuntu/x86_64/cuda-wsl-ubuntu.pin sudo mv cuda-wsl-ubuntu.pin /etc/apt/preferences.d/cuda-repository-pin-600 wget https://developer.download.nvidia.com/compute/cuda/11.4.4/local_installers/cuda-repo-wsl-ubuntu-11-4-local_11.4.4-1_amd64.deb sudo dpkg -i cuda-repo-wsl-ubuntu-11-4-local_11.4.4-1_amd64.deb sudo apt-key add /var/cuda-repo-wsl-ubuntu-11-4-local/7fa2af80.pub sudo apt-get update sudo apt-get -y install cuda-nvcc-11-4 3.2 cuda-cross-aarch64 从3.1的页面下载不到 cuda-cross-aarch64,只能下载到cuda sbsa aarch64,所以只能从这个位置下载\nIndex (nvidia.cn)\n下载需求要包后\n1 2 3 4 5 dpkg -i --force-all *.deb \u0026amp;\u0026amp; \\ apt-get update \u0026amp;\u0026amp; \\ apt-get install -y -f \u0026amp;\u0026amp; \\ apt-get install -y cuda-cross-aarch64 cuda-cross-aarch64-10-2 \u0026amp;\u0026amp; \\ rm -rf /var/lib/apt/lists/* 3.3 测试CUDA程序 编译 cuda samples\nNVIDIA/cuda-samples: Samples for CUDA Developers which demonstrates features in CUDA Toolkit (github.com)\ncmake+cuda, 可以用于验证cmake+cuda是否配置成功\nBuilding Cross-Platform CUDA Applications with CMake | NVIDIA Technical Blog\ncode-samples/posts/cmake at master · robertmaynard/code-samples (github.com)\n1 2 3 4 5 set(CUDA_TOOLKIT_ROOT_DIR /usr/local/cuda-11.4/) set(CUDA_NVCC_EXECUTABLE /usr/local/cuda-11.4/bin/nvcc) set(CMAKE_CUDA_COMPILER /usr/local/cuda-11.4/bin/nvcc) set(CMAKE_CUDA_ARCHITECTURES 87) #jetson orin nx devicequery set(CMAKE_CUDA_FLAGS \u0026#34;-ccbin ${CMAKE_CXX_COMPILER} -Xcompiler -fPIC\u0026#34; CACHE STRING \u0026#34;\u0026#34; FORCE) 4. 交叉编译 VTK 9.3 jetpack 5.3.1 工具链需要额外的设置\nhttps://forums.developer.nvidia.com/t/question-about-cross-compilation-link-errors/238711/10\n先编译本机的 compile tools\n1 2 cmake -GNinja -DVTK_BUILD_COMPILE_TOOLS_ONLY=ON .. cmake --build . --target install 再交叉编译\n编译选项 VTK_REQUIRE_LARGE_FILE_SUPPORT需要打开,否则cmake不了\nDVTK_MODULE_ENABLE_VTK_libproj:STRING= 也需要关掉,否则交叉编译会因为sqlite数据库失败,这个库用于地图信息视频编码相关方面的,我们用不到\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 cmake \\ -DCMAKE_TOOLCHAIN_FILE=/repos/vcpkg/toolchains/arm64-jetson-xavier-nx.cmake \\ -DQMLPLUGINDUMP_EXECUTABLE=/build/xavier-nx/bin/qmake \\ -GNinja \\ -DVTK_REQUIRE_LARGE_FILE_SUPPORT=ON \\ -DVTK_MODULE_ENABLE_VTK_RenderingQt:STRING=YES \\ -DVTK_MODULE_ENABLE_VTK_ViewsQt:STRING=YES \\ -DVTK_GROUP_ENABLE_Qt:STRING=YES \\ -DVTK_MODULE_ENABLE_VTK_GUISupportQt:STRING=YES \\ -DVTK_MODULE_ENABLE_VTK_GUISupportQtQuick:STRING=YES \\ -DVTK_MODULE_ENABLE_VTK_libproj:STRING=NO \\ -DVTK_USE_CUDA:BOOL=ON \\ -DOPENGL_glx_LIBRARY=/sysroot/usr/lib/aarch64-linux-gnu/libGLX.so \\ -DOPENGL_opengl_LIBRARY=/sysroot/usr/lib/aarch64-linux-gnu/libOpenGL.so \\ -DX11_X11_LIB=/sysroot/usr/lib/aarch64-linux-gnu/libX11.so \\ .. #-DVTK_OPENGL_USE_GLES=OFF \\ #-DVTK_OPENGL_HAS_EGL=ON \\ #-DVTK_USE_X=OFF \\ #-DOPENGL_gles3_LIBRARY=/sysroot/usr/lib/aarch64-linux-gnu/libGLESv2.so \\ #-DOPENGL_egl_LIBRARY=/sysroot/usr/lib/aarch64-linux-gnu/libEGL.so \\ 因为qmlplugindump 不支持交叉编译,但是vtk用它来导出插件\n所以先给DQMLPLUGINDUMP_EXECUTABLE随便设置一个路径, 然后编译\n编译时会在最后导出qml plugin时失败, 这时编辑生成的ninja文件,屏蔽掉 生成qml插件,直接强制编译安装\n但发现除了qmltypes文件没有,其他的文件都仍然存在, 去之前编译的x86_64的版本上复制qmltypes过来就ok了\n5. 交叉编译PCL 1.13 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 cmake \\ -GNinja \\ -DWITH_LIBUSB=OFF \\ -DBUILD_tools=OFF \\ -DBoost_CONTEXT_LIBRARY_RELEASE:STRING=/sysroot/usr/lib/aarch64-linux-gnu/libboost_context.so.1.71.0 \\ -DBoost_DATE_TIME_LIBRARY_RELEASE:STRING=/sysroot/usr/lib/aarch64-linux-gnu/libboost_date_time.so.1.71.0 \\ -DBoost_DIR:PATH=/sysroot/usr/lib/aarch64-linux-gnu/cmake/Boost-1.71.0 \\ -DBoost_FIBER_LIBRARY_RELEASE:STRING=/sysroot/usr/lib/aarch64-linux-gnu/libboost_fiber.so.1.71.0 \\ -DBoost_FILESYSTEM_LIBRARY_RELEASE:STRING=/sysroot/usr/lib/aarch64-linux-gnu/libboost_filesystem.so.1.71.0 \\ -DBoost_IOSTREAMS_LIBRARY_RELEASE:STRING=/sysroot/usr/lib/aarch64-linux-gnu/libboost_iostreams.so.1.71.0 \\ -DBoost_SERIALIZATION_LIBRARY_RELEASE:STRING=/sysroot/usr/lib/aarch64-linux-gnu/libboost_serialization.so.1.71.0 \\ -DBoost_SYSTEM_LIBRARY_RELEASE:STRING=/sysroot/usr/lib/aarch64-linux-gnu/libboost_system.so.1.71.0 \\ -DBoost_THREAD_LIBRARY_RELEASE:STRING=/sysroot/usr/lib/aarch64-linux-gnu/libboost_thread.so.1.71.0 \\ -DCMAKE_TOOLCHAIN_FILE=/repos/vcpkg/toolchains/arm64-jetson-xavier-nx.cmake \\ -DOPENGL_opengl_LIBRARY=/sysroot/usr/lib/aarch64-linux-gnu/libOpenGL.so \\ -DOPENGL_glx_LIBRARY=/sysroot/usr/lib/aarch64-linux-gnu/libGLX.so \\ -DX11_X11_LIB=/sysroot/usr/lib/aarch64-linux-gnu/libX11.so \\ -DFLANN_LIBRARY_TYPE=SHARED \\ -DFLANN_LIBRARY=/sysroot/usr/lib/aarch64-linux-gnu/libflann.so \\ .. 关掉一些不需要的东西 vtk9.3 + pcl1.13 ,足以让我们在qt quick中快乐的开发点云相关的功能了 有时间再另外写一篇 qt quick + vtk + pcl 的博客,这里不再展开\n3. I^2C 开发 从手册上看, 有两对默认的i2c, 把设备接上 pin3 SDA pin5 SCL GND后,可以使用i2cdetect工具查看,系统内置了\n1 2 3 4 5 6 7 # 查看所有i2c 总线 i2cdetect -l # 查看总线上的设备地址 i2cdetect -y -r \u0026#34;name\u0026#34; #或 i2cdetect -y -r n # /dev/i2c-n 经实测, xavier XN的 3 5管脚的i2c ,在/dev/i2c-8\nc++ 编码可以参考我之前写的raspberry pi 5的i2c 博客\nraspberry-pi5-i2c-gpio-qt-cpp编程/#23-编码 | hbb\u0026rsquo;s blog (nocanstillbb.github.io)\n4. 附录 4.1 cmake toolchain file 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 set(CMAKE_SYSTEM_NAME Linux) set(CMAKE_SYSTEM_PROCESSOR arm64) set(CMAKE_STAGING_PREFIX /repos/installed) set(CMAKE_INSTALL_PREFIX /repos/installed) set(rootfs /sysroot) set(CMAKE_SYSROOT ${rootfs}) set(target_arch /usr/bin/aarch64-linux-gnu) set(CMAKE_C_COMPILER ${target_arch}-gcc) set(CMAKE_CXX_COMPILER ${target_arch}-g++) set(CMAKE_AR ${target_arch}-ar CACHE FILEPATH \u0026#34;\u0026#34; FORCE) set(CMAKE_RANLIB ${target_arch}-ranlib) set(CMAKE_LINKER ${target_arch}-ld) set(CUDA_TOOLKIT_ROOT_DIR /usr/local/cuda-11.4/) set(CUDA_NVCC_EXECUTABLE /usr/local/cuda-11.4/bin/nvcc) set(CMAKE_CUDA_COMPILER /usr/local/cuda-11.4/bin/nvcc) set(CMAKE_CUDA_ARCHITECTURES 87) #jetson orin nx devicequery set(CMAKE_CUDA_FLAGS \u0026#34;-ccbin ${CMAKE_CXX_COMPILER} -Xcompiler -fPIC\u0026#34; CACHE STRING \u0026#34;\u0026#34; FORCE) set(ENV{QT_DIR} /build/xavier-nx/lib/cmake/Qt5) set(ENV{Qt5_DIR} /build/xavier-nx/lib/cmake/Qt5) set(ENV{Qt_DIR} /build/xavier-nx/lib/cmake/Qt5) set(QT_DIR /build/xavier-nx/lib/cmake/Qt5) set(Qt5_DIR /build/xavier-nx/lib/cmake/Qt5) set(Qt_DIR /build/xavier-nx/lib/cmake/Qt5) set(QT_QMAKE_EXECUTABLE /build/xavier-nx/bin/qmake) set(Qt5Core_DIR /build/xavier-nx/lib/cmake/Qt5Core) set(Qt5Gui_DIR /build/xavier-nx/lib/cmake/Qt5Gui) set(Qt5Widgets_DIR /build/xavier-nx/lib/cmake/Qt5Widgets) set(Qt5Quick_DIR /build/xavier-nx/lib/cmake/Qt5Quick) set(Qt5QuickControls2_DIR /build/xavier-nx/lib/cmake/Qt5QuickControls2) set(Qt5QmlModels_DIR /build/xavier-nx/lib/cmake/Qt5QmlModels) set(Qt5Qml_DIR /build/xavier-nx/lib/cmake/Qt5Qml) set(Qt5Network_DIR /build/xavier-nx/lib/cmake/Qt5Network) set(Qt5LinguistTools_DIR /build/xavier-nx/lib/cmake/Qt5LinguistTools) set(Qt5Sql_DIR /build/xavier-nx/lib/cmake/Qt5Sql) set(Qt5OpenGL_DIR /build/xavier-nx/lib/cmake/Qt5OpenGL) set(Boost_INCLUDE_DIR ${rootfs}/usr/include) set(Boost_LIBRARY_DIR ${rootfs}/usr/lib/aarch64-linux-gnu) set(VTKCompileTools_DIR /usr/local/lib/cmake/vtkcompiletools-9.3) set(Protobuf_DIR /repos/dv_app_solution/vcpkg_installed/x64-linux/share/protobuf) #set(VTK_DIR /repos/vtk9.3/lib/cmake/vtk-9.3) #set(PCL_DIR /repos/pcl1.13/share/pcl-1.13/) # #set(Genicam_DIR /repos/rc_genicam_api/genicam) #set(OpenCV_DIR ${rootfs}/usr/lib/aarch64-linux-gnu/cmake/opencv4 ) #set(zlog_Dir /repos/installed/share/zlog) #set(Eigen3_DIR ${rootfs}/usr/lib/cmake/eigen3) #set(Flann_DIR /repos/installed/share/flann/) set(CMAKE_FIND_ROOT_PATH /sysroot/usr /sysroot/usr/lib/aarch64-linux-gnu /repos/installed ) set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) 4.2 ~/.bashrc 1 export LD_PRELOAD=/usr/lib/aarch64-linux-gnu/libgomp.so.1:/usr/local/Qt/5.15.11/gcc_arm64/plugins/xcbglintegrations/libqxcb-glx-integration.so:$LD_PRELOAD 5.参考 Jetson Software 入门 |NVIDIA 开发人员\nJetson Linux | NVIDIA Developer\nJetson Xavier NX 开发者套件 - 入门 |NVIDIA 开发人员\nFlashing Support — NVIDIA Jetson Linux Developer Guide 1 documentation\n",
"url": "\/post\/nvidia-jetson%E5%BC%80%E5%8F%91%E7%AC%94%E8%AE%B0\/"
},
"\/post\/cpp%E8%B7%A8%E5%B9%B3%E5%8F%B0%E5%85%B1%E4%BA%AB%E5%86%85%E5%AD%98%E8%BF%9B%E7%A8%8B%E9%80%9A%E8%AE%AF\/": {
"title": "cpp跨平台共享内存进程通讯",
"tags": ["C\u002b\u002b","SharedMemory","IPC",],
"content": "1. Linux的两种共享内存机制,及选用 shmget 和 shm_open 是两种不同的共享内存机制,它们在使用方式和本质上有所不同。\n1.1 System V 共享内存 shmget 是 POSIX 标准中定义的 System V 共享内存的接口函数。 System V 共享内存是一种传统的 UNIX 共享内存机制,使用 shmget 函数创建的共享内存对象被映射到进程的地址空间中,进程可以直接访问共享内存区域,而无需通过文件系统。 System V 共享内存需要使用键值来唯一标识共享内存对象,进程可以使用该键值来获取或创建共享内存对象。 System V 共享内存需要使用额外的信号量进行同步和互斥操作,以避免竞争条件和数据损坏。 1.2 映射文件共享内存 shm_open 是 POSIX 标准中定义的另一种共享内存机制,属于 POSIX 共享内存对象。 POSIX 共享内存是一种基于文件的共享内存机制,使用 shm_open 函数创建的共享内存对象实际上对应着文件系统中的一个特殊文件。 与 System V 共享内存不同,POSIX 共享内存可以直接通过文件系统访问和管理,进程可以像操作普通文件一样来操作共享内存对象。 POSIX 共享内存不需要使用键值来标识共享内存对象,而是使用文件路径来唯一标识共享内存对象,这样更加简洁和直观。 POSIX 共享内存不需要使用额外的信号量进行同步和互斥操作,而是通过操作系统提供的文件锁机制来实现并发访问的同步。 由于要考虑跨平台封装,windows上共享内存映射也是文件系统相关,所以选择映射文件的这个套机制\n2. 查看共享内存分配情况 2.1 POSIX - System V 虽然我们选用了映射文件共享内存的方式,但是还是简单提一下system v共享内存机制查看已分配内存的方式\n1 2 3 4 5 6 7 8 9 huangbinbin@cs-bj:~$ ipcs -m ------ Shared Memory Segments -------- key shmid owner perms bytes nattch status 0x00000000 557056 chenyan 600 524288 2 dest 0x00000000 557057 chenyan 600 2097152 2 dest 0x00000000 491523 hanhaopeng 600 8388608 2 dest 0x00000000 1146884 huangbinbi 600 67108864 2 dest ... 2.2 POSIX - 映射文件共享内存 1 2 3 4 5 6 7 8 9 10 [huangbinbin@dv-srv01 ~]$ ll /dev/shm/ total 10924 -rwx------ 1 renyanan algr 65536 Mar 12 16:27 3289461crawl10r0 -rwx------ 1 renyanan algr 65536 Mar 12 16:27 3289461crawl10s0 -rwx------ 1 renyanan algr 65536 Mar 12 16:30 3289461crawl11r0 -rwx------ 1 renyanan algr 65536 Mar 12 16:30 3289461crawl11s0 -rwx------ 1 renyanan algr 65536 Mar 12 16:31 3289461crawl12r0 -rwx------ 1 renyanan algr 65536 Mar 12 16:31 3289461crawl12s0 -rwx------ 1 renyanan algr 65536 Mar 12 16:34 3289461crawl13r0 ... 2.3 Windows windows无法像linux那样列出所有系统中的共享内存文件,只能通用微软的运维工具process explorer查看section类型的句柄\n3. 封装代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 #include \u0026lt;iostream\u0026gt; #ifdef _WIN32 // Windows #include \u0026lt;windows.h\u0026gt; class SharedMemory { public: SharedMemory(const char* name, size_t size):size(size) { handle = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, size, name); if (handle == NULL) { std::cerr \u0026lt;\u0026lt; \u0026#34;Failed to create shared memory\u0026#34; \u0026lt;\u0026lt; std::endl; } } ~SharedMemory() { CloseHandle(handle); } void* map() { return MapViewOfFile(handle, FILE_MAP_ALL_ACCESS, 0, 0, 0); } void unmap(void* addr) { UnmapViewOfFile(addr); } private: HANDLE handle; }; #else // Linux and macOS #include \u0026lt;fcntl.h\u0026gt; #include \u0026lt;sys/mman.h\u0026gt; #include \u0026lt;sys/stat.h\u0026gt; #include \u0026lt;unistd.h\u0026gt; class SharedMemory { public: SharedMemory(const char* name, size_t size):size(size) { fd = shm_open(name, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR); if (fd == -1) { std::cerr \u0026lt;\u0026lt; \u0026#34;Failed to create shared memory\u0026#34; \u0026lt;\u0026lt; std::endl; } ftruncate(fd, size); } ~SharedMemory() { close(fd); // Unlink shared memory object // shm_unlink(name); // Uncomment this if you want to remove shared memory object after use } void* map() { return mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); } void unmap(void* addr) { munmap(addr, size); } private: int fd; size_t size; }; #endif 使用示例,\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 int main() { SharedMemory shm(\u0026#34;MySharedMemory\u0026#34;, 1024); void* addr = shm.map(); if (addr == MAP_FAILED) { std::cerr \u0026lt;\u0026lt; \u0026#34;Failed to map shared memory\u0026#34; \u0026lt;\u0026lt; std::endl; return 1; } // do stuff shm.unmap(addr); return 0; } 4. 注意事项 4.1 链接库 linux下需要链接rt库\n1 $ g++ -o main main.cpp -lrt 4.2 权限问题 linux上 ,执行sub_open的进程如果是root,那么要打开这个共享内存文件,也需要是以root启动的进程\n否则 map返回的addr可能会是 MAP_FAILED ,注意,map失败不是返回nullptr,是MAP_FAILED 也就是 (void*)-1\n参考 ChatGPT (openai.com)\nCreating Named Shared Memory - Win32 apps | Microsoft Learn\n",
"url": "\/post\/cpp%E8%B7%A8%E5%B9%B3%E5%8F%B0%E5%85%B1%E4%BA%AB%E5%86%85%E5%AD%98%E8%BF%9B%E7%A8%8B%E9%80%9A%E8%AE%AF\/"
},
"\/tags\/ipc\/": {
"title": "IPC",
"tags": [],
"content": "",
"url": "\/tags\/ipc\/"
},
"\/tags\/sharedmemory\/": {
"title": "SharedMemory",
"tags": [],
"content": "",
"url": "\/tags\/sharedmemory\/"
},
"\/tags\/boost-asio\/": {
"title": "Boost Asio",
"tags": [],
"content": "",
"url": "\/tags\/boost-asio\/"
},
"\/post\/boost-asio-udp-%E5%90%8C%E6%AD%A5%E8%B6%85%E6%97%B6%E9%99%B7%E9%98%B1\/": {
"title": "boost-asio-udp-同步超时陷阱",
"tags": ["C\u002b\u002b","Boost Asio",],
"content": "2024/03/24 阴, 约了许久没见面的妹妹吃了一个饭,她去找他同学玩了,我到咖啡店找点下周要开发的nvidia jetson xavier的资料, 由于板子没在身边,具体型号也不记得,所以有时间记录一下去年遇到的一个boost asio 同步通讯中,客户端设置超时的坑, 顺便记录一下boost udp 客户端 通讯的example, 同步,异步应该是大同小异, 我使用的boost 版本是1.83\n0. boost udp 同步超时陷阱 遇到这个问题时,我在开发一个gige 协议的客户端,用于windows和linux环境,使用gige相机。gige中通过gvcp和gvsp来对相机进行交互和取流,gvcp是udp协议,所以我用的是udp socket。开始时,我在windows上开发, 使用下面这段代码设置超时时间 ,它可以正常的工作 ,当超时发生时,返回超时的结果。\n代码中先创建了endpoint和udp socket,然后把它们绑定在一起,然后设置socket的超时时间 为5000毫秒\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 void foo() { try { boost::asio::io_service io_service; boost::asio::ip::udp::socket socket(io_service); socket.open(boost::asio::ip::udp::v4()); boost::asio::ip::udp::endpoint if_endpoint(boost::asio::ip::address::from_string(\u0026#34;0.0.0.0\u0026#34;), 0); socket.bind(if_endpoint); boost::asio::ip::udp::endpoint endpoint(boost::asio::ip::address::from_string(devIp), 3956); boost::array\u0026lt;unsigned char,16\u0026gt; hex_data = {/* ... */ }; socket.send_to(boost::asio::buffer(hex_data), endpoint); // set timeout 5000ms socket.set_option(boost::asio::detail::socket_option::integer\u0026lt;SOL_SOCKET, SO_RCVTIMEO\u0026gt;{ 5000 }); boost::array\u0026lt;char, 600\u0026gt; recv_buffer; boost::asio::ip::udp::endpoint remote_endpoint; boost::system::error_code error; size_t bytes_received = socket.receive_from(boost::asio::buffer(recv_buffer), remote_endpoint, 0, error); Q_UNUSED(bytes_received) if (!error) { // do stuff } } else { std::cerr \u0026lt;\u0026lt; \u0026#34;Error receiving message: \u0026#34; \u0026lt;\u0026lt; error.message() \u0026lt;\u0026lt; std::endl; } } catch (const boost::system::system_error\u0026amp; e) { // timeout if (e.code() == boost::asio::error::timed_out) { std::cerr \u0026lt;\u0026lt; \u0026#34;Timeout: No response received within 1 second.\u0026#34; \u0026lt;\u0026lt; std::endl; } else { std::cerr \u0026lt;\u0026lt; \u0026#34;Error receiving message: \u0026#34; \u0026lt;\u0026lt; e.what() \u0026lt;\u0026lt; std::endl; } } catch (const std::exception\u0026amp; e) { std::cerr \u0026lt;\u0026lt; \u0026#34;Exception: \u0026#34; \u0026lt;\u0026lt; e.what() \u0026lt;\u0026lt; std::endl; } return \u0026#34;\u0026#34;; } 但是当我把这段代码放到linux上运行时,当超时发生时,会导致receive_from永远不会返回。\n1. window设置超时有效的原因 window和 linux的receive_from在经过平台适配层后,分发到平台系统api,\nwindows的api在boost socket_ops.ipp中为sync_recvfrom1中,api为WSARecvFrom\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 signed_size_type recvfrom(socket_type s, buf* bufs, size_t count, int flags, void* addr, std::size_t* addrlen, boost::system::error_code\u0026amp; ec) { #if defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) // Receive some data. DWORD recv_buf_count = static_cast\u0026lt;DWORD\u0026gt;(count); DWORD bytes_transferred = 0; DWORD recv_flags = flags; int tmp_addrlen = (int)*addrlen; int result = ::WSARecvFrom(s, bufs, recv_buf_count, \u0026amp;bytes_transferred, //调用 window api \u0026amp;recv_flags, static_cast\u0026lt;socket_addr_type*\u0026gt;(addr), \u0026amp;tmp_addrlen, 0, 0); get_last_error(ec, true); *addrlen = (std::size_t)tmp_addrlen; if (ec.value() == ERROR_NETNAME_DELETED) ec = boost::asio::error::connection_reset; else if (ec.value() == ERROR_PORT_UNREACHABLE) ec = boost::asio::error::connection_refused; else if (ec.value() == WSAEMSGSIZE || ec.value() == ERROR_MORE_DATA) result = 0; if (result != 0) return socket_error_retval; boost::asio::error::clear(ec); return bytes_transferred; #else // defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) //...省略 #endif // defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) } 可以通过设置SO_RCVTIMEO 来使 WSARecvFrom ,recvfrom等阻塞式的读数据的函数超时返回\n1 2 3 4 5 6 7 8 9 10 11 12 13 int main() { int iSock = socket(AF_INET, SOCK_DGRAM, 0); char szBuf[1024] = {0}; struct timeval tv; tv.tv_sec = 1; tv.tv_usec = 0; setsockopt(iSock, SOL_SOCKET, SO_RCVTIMEO, \u0026amp;tv, sizeof(tv)); int iRet = recvfrom(iSock, szBuf, sizeof(szBuf) - 1, 0, NULL, NULL); printf(\u0026#34;iRet is [%d]\\n\u0026#34;, iRet); close(iSock); } 因为我们通过boost的方式设置了SO_RCVTIMEO,\n1 socket.set_option(boost::asio::detail::socket_option::integer\u0026lt;SOL_SOCKET, SO_RCVTIMEO\u0026gt;{ 5000 }); 2. linux设置超时无效的原因 而linux调用系统api的位置在 boost socket_ops.ipp文件中的poll_read中,外部传入的超时时间为常量-1,即无限等待\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 int poll_read(socket_type s, state_type state, int msec, boost::system::error_code\u0026amp; ec) { if (s == invalid_socket) { ec = boost::asio::error::bad_descriptor; return socket_error_retval; } #if defined(BOOST_ASIO_WINDOWS) //widnows逻辑 //略 #else // linux逻辑 pollfd fds; fds.fd = s; fds.events = POLLIN; fds.revents = 0; int timeout = (state \u0026amp; user_set_non_blocking) ? 0 : msec; int result = ::poll(\u0026amp;fds, 1, timeout); //timeout实参常数-1 get_last_error(ec, result \u0026lt; 0); #endif if (result == 0) if (state \u0026amp; user_set_non_blocking) ec = boost::asio::error::would_block; return result; } 3. 解法 一种即达到同步等等,windows和linux又不需要维护两个版本的方法,是使用异步接口写同步等待 同步等待不必非得用同步接口,asio 擅长的本身就是异步\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 void foo() { std::string result; boost::asio::io_service io_service; boost::asio::io_service::work work(io_service); std::thread thread([\u0026amp;io_service](){ io_service.run(); }); boost::asio::ip::udp::socket socket(io_service); socket.open(boost::asio::ip::udp::v4()); boost::asio::ip::udp::endpoint if_endpoint(boost::asio::ip::address::from_string(\u0026#34;0.0.0.0\u0026#34;), 0); socket.bind(if_endpoint); boost::asio::ip::udp::endpoint endpoint(boost::asio::ip::address::from_string(devIp), 3956); // send datas boost::array\u0026lt;unsigned char,16\u0026gt; hex_data = { /* ... */ }; // send std::future\u0026lt;size_t\u0026gt; bytes_send = socket.async_send_to(boost::asio::buffer(hex_data), endpoint,boost::asio::use_future); boost::array\u0026lt;char, 600\u0026gt; recv_buffer; boost::asio::ip::udp::endpoint remote_endpoint; bytes_send.get(); std::future\u0026lt;size_t\u0026gt; bytes_received = socket.async_receive_from(boost::asio::buffer(recv_buffer), remote_endpoint,boost::asio::use_future); std::future_status status = bytes_received.wait_for(std::chrono::milliseconds(5000)); if (std::future_status::timeout == status) { socket.cancel(); // timeout } else if (status == std::future_status::ready) { //do stuff } else { //other errors } } ",
"url": "\/post\/boost-asio-udp-%E5%90%8C%E6%AD%A5%E8%B6%85%E6%97%B6%E9%99%B7%E9%98%B1\/"
},
"\/tags\/drone\/": {
"title": "Drone",
"tags": [],
"content": "",
"url": "\/tags\/drone\/"
},
"\/tags\/forgejo\/": {
"title": "Forgejo",
"tags": [],
"content": "",
"url": "\/tags\/forgejo\/"
},
"\/post\/forgejo-drone-ci-cd\/": {
"title": "forgejo-drone-ci-cd",
"tags": ["Forgejo","Drone",],
"content": "相关服务 forgejo : 源码版本控制, 前身是gitea, 由于 gitea有闭源风险, 社区不再信任,fork出forgejo继续开发开源版本, ps gitea也是从其他项目继续过来开发的\ndrone : ci/cd\ndrone服务 通过 weebhook 监听git 仓库事件,分发任务给runner执行CI/CD\nCI/CD 需要执行什么, 取决于添加到drone配置文件,通常名为 .drone.yml\n服务相关的docker compose放在附录之中了\n上传已有git仓库到forgejo 先在forgejo上创建仓库\n创建时,需要关注几个仓库的属性\n拥有者, 设置组织,如SW组织, 可见性, 一般都设置私有仓库, 再添加协作者 默认分支,一般设置为master ,和gitlab保持一致 创建完后把drone 用户添加到git仓库协作者, ci的docker环境会设置drone的ssh代理,让drone有权限访问代码\n再本地的代码库中设置远程库直接推送\n1 2 git remote add forgejo ssh://git@git3.deepvision-tech.net:222/SW/ci_demo_0.git git push 激活git仓库的drone 激活之前,git项目不会创建webhook,\n需要登录drone 激活 drone : http://10.1.8.129\n不需要填写用户名密码,点登录后自动以forgejo登录信息登录drone\n点击右上角同步项目信息,同步完会看到刚才新建的项目\n点击项目进制配置界面,点击\n默认设置点击保存\n保存后forgejo上的项目会生成一个webhook\n可以这weebhook中设置需要关注的git事件类型和git 分支\n通常是只有关键分支才需要ci/cd, 开发分支不需要,\n开发分支 合到关键分支,需要提交合并请求或拉取申请,待审核后合并后自动触发 持续集成\nweebook还可以试触发, 在调试ci/cd时很方便\n.drone.yml example\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 kind: pipeline type: docker name: build # https://github.com/devth/drone-docs/blob/master/content/usage/cloning.md clone: disable: true # 禁止自动拉取代码 steps: - name: build-all image: dv_ci_qt5.15:0.1 environment: PKG_VERSION: \u0026#34;1.0.0\u0026#34; PKG_NAME: \u0026#34;dv_algorithm\u0026#34; #https://docs.drone.io/pipeline/environment/reference/ DRONE_BRANCH: $DRONE_BRANCH DRONE_BUILD_CREATED: $DRONE_BUILD_CREATED DRONE_BUILD_EVENT: $DRONE_BUILD_EVENT DRONE_BUILD_FINISHED: $DRONE_BUILD_FINISHED DRONE_COMMIT: $DRONE_COMMIT DRONE_COMMIT_AFTER: $DRONE_COMMIT_AFTER DRONE_COMMIT_AUTHOR: $DRONE_COMMIT_AUTHOR DRONE_COMMIT_AUTHOR_AVATAR: $DRONE_COMMIT_AUTHOR_AVATAR DRONE_COMMIT_AUTHOR_EMAIL: $DRONE_COMMIT_AUTHOR_EMAIL DRONE_COMMIT_AUTHOR_NAME: $DRONE_COMMIT_AUTHOR_NAME DRONE_COMMIT_BEFORE: $DRONE_COMMIT_BEFORE DRONE_COMMIT_BRANCH: $DRONE_COMMIT_BRANCH DRONE_COMMIT_LINK: $DRONE_COMMIT_LINK DRONE_COMMIT_MESSAGE: $DRONE_COMMIT_MESSAGE DRONE_COMMIT_REF: $DRONE_COMMIT_REF DRONE_COMMIT_SHA: $DRONE_COMMIT_SHA DRONE_STAGE_ARCH: $DRONE_STAGE_ARCH DRONE_STAGE_FINISHED: $DRONE_STAGE_FINISHED DRONE_STAGE_OS: $DRONE_STAGE_OS DRONE_STAGE_MACHINE: $DRONE_STAGE_MACHINE settings: detach: false entrypoint: [\u0026#34;\u0026#34;] commands: - cd /source/repos - bash -c \u0026#34;rm -rf dv_app_solution\u0026#34; - bash -c \u0026#34;git clone ssh://git@git1.deepvision-tech.net:222/SW/dv_app_solution.git\u0026#34; - cd /source/repos/dv_app_solution - bash -c \u0026#34;rsync -ah ../vcpkg_installed . \u0026gt;/dev/null 2\u0026gt;\u0026amp;1 \u0026#34; - bash -c \u0026#34;git submodule update --init --recursive\u0026#34; - git c -t origin/$DRONE_BRANCH | true - git frsm \u0026#34;git c -t origin/$DRONE_BRANCH | true\u0026#34; - bash -c \u0026#34;./clear.sh \u0026amp;\u0026amp; ./build.sh\u0026#34; # 129没有部署exec runner --- kind: pipeline type: exec name: upload clone: disable: true steps: - name: upload_to_ftp environment: DRONE_BRANCH: $DRONE_BRANCH commands: - cd /root/source/repos/dv_app_solution/bin/x86_64/ - /root/ftp_upload.sh \u0026#34;/ci/$DRONE_BRANCH\u0026#34; libdv_algorithm.do depends_on: - build 手动添加构建流水线任务 如果希望使图中的PKG_VERSION覆盖.drone.yml中的同名变量,可以使用go模板语法\n${参数:-默认值}\n如果没有paramete 就显示为默认值,\n1 2 environment: PKG_VERSION: ${PKG_VERSION:-1.0.0} #如果没有传递PKG_VERSION变量,默认值为1.0.0 server 默认的ui dashboard 比较简陋,如果需要定制ui,可以自己写app,相送的api 在官网都可以找到 可以使用http post的方式创建 构建任务\n挂载到docer容器上的目录 源码本来不应该挂载到docker中,但考虑到二进制体积较大的情况,挂了两个目录到容器上\n只在docker中有访问权限的\n/home/drone/source/repos : /repos\n所有人有访问权限的\n/storage/sw_ci/ : /sw_ci\n也可以写脚本上传到其他服务器\nssh-agent 容器中会自动设置ssh代理,\n可以使用drone用户的ssh id 进行ssh的认证,例如git clone,\n1 ssh-add -l #可以查看已经代理的密钥签名 附录:docker compose forjejo server 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 version: \u0026#34;3\u0026#34; networks: forgejo: external: false services: server: image: codeberg.org/forgejo/forgejo:1.20 container_name: forgejo environment: - USER_UID=1001 - USER_GID=1001 - FORGEJO__database__DB_TYPE=postgres - FORGEJO__database__HOST=db:5432 - FORGEJO__database__NAME=forgejo - FORGEJO__database__USER=forgejo - FORGEJO__database__PASSWD=123456 restart: always networks: - forgejo volumes: - type: volume source: nfs-forgejo-data target: /data volume: nocopy: true - /etc/timezone:/etc/timezone:ro - /etc/localtime:/etc/localtime:ro ports: - \u0026#34;80:3000\u0026#34; - \u0026#34;222:22\u0026#34; depends_on: - db db: image: postgres:14 restart: always environment: - POSTGRES_USER=forgejo - POSTGRES_PASSWORD=123456 - POSTGRES_DB=forgejo networks: - forgejo volumes: - type: volume source: nfs-forgejo-postgres target: /var/lib/postgresql/data volume: nocopy: true volumes: nfs-forgejo-data: driver_opts: type: \u0026#34;nfs\u0026#34; o: \u0026#34;addr=10.1.8.83,nolock,soft,rw\u0026#34; device: \u0026#34;:/volume1/repo-03/forgejo\u0026#34; nfs-forgejo-postgres: driver_opts: type: \u0026#34;nfs\u0026#34; o: \u0026#34;addr=10.1.8.83,nolock,soft,rw\u0026#34; device: \u0026#34;:/volume1/repo-03/postgres\u0026#34; drone server 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 version: \u0026#34;3\u0026#34; services: drone: image: drone/drone:2 container_name: drone environment: - DRONE_GITEA_SERVER=http://forgejo.net - DRONE_GITEA_CLIENT_ID=1660a088-d5d7-4aee-951e-cae874282f13 - DRONE_GITEA_CLIENT_SECRET=gto_a4uwqotic2oenrx6ez6b4triae7hpphirviqsdxyq5unyj5smfrq - DRONE_RPC_SECRET=78d59e651522607da0f7242e6965ce26 - DRONE_SERVER_HOST=10.1.8.129 - DRONE_SERVER_PROTO=http restart: always volumes: - ./drone:/data ports: - 80:80 DRONE_GITEA_CLIENT_ID 和DRONE_GITEA_CLIENT_ID用于 drone oauth2 登录, 需要在gitea或forgejo 设置面板设置中创建 oauth2 应用 后获取以填充\ndrone docker runner 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 version: \u0026#34;3\u0026#34; services: runner: image: drone/drone-runner-docker:1 container_name:runner environment: - DRONE_RPC_PROTO=http - DRONE_RPC_HOST=server_host - DRONE_RPC_SECRET=78d59e651522607da0f7242e6965ce26 - DRONE_RUNNER_CAPACITY=1 - DRONE_RUNNER_NAME=drone-runner - DRONE_RUNNER_VOLUMES=/home/drone/.ssh/known_hosts:/root/.ssh/known_hosts,/home/drone/source:/source,/storage/sw_ci:/sw_ci,/tmp/ssh-n0ryHZ9GmCn5/agent.2507215:/tmp/ssh-n0ryHZ9GmCn5/agent.2507215 - DRONE_RUNNER_ENVIRON=SSH_AUTH_SOCK:/tmp/ssh-n0ryHZ9GmCn5/agent.2507215 restart: always volumes: - /var/run/docker.sock:/var/run/docker.sock ports: - 3000:3000 ",
"url": "\/post\/forgejo-drone-ci-cd\/"
},
"\/tags\/gcc\/": {
"title": "GCC",
"tags": [],
"content": "",
"url": "\/tags\/gcc\/"
},
"\/tags\/ubuntu\/": {
"title": "Ubuntu",
"tags": [],
"content": "",
"url": "\/tags\/ubuntu\/"
},
"\/post\/ubuntu%E5%AE%89%E8%A3%85nvidia%E6%98%BE%E5%8D%A1%E9%A9%B1%E5%8A%A8\/": {
"title": "ubuntu安装nvidia显卡驱动",
"tags": ["Ubuntu","Nvidia",],
"content": "0. 大纲 查看显卡驱动 添加apt 仓库 自动检测安装(对于较新型号无效) 手动安装 1.查看显卡驱动 1 lscpi |grep -i vga 输出\n确认是NVIDIA 显卡后, 继续后面的步骤,如果不是,可以试试 3,不一定有效, 2和4就不需要执行了\n2. 添加apt 源 添加Ubuntu图形驱动仓库PPA。在终端输入以下命令:\n1 2 sudo add-apt-repository ppa:graphics-drivers/ppa sudo apt update 3. 自动检测安装(对较新型号无效) 1 sudo ubuntu-drivers autoinstall 如果没有自动安装,需要手动安装,执行第4步\n4. 手动安装 根据步骤1 查到的型号 1e04,到这里转换为具体的型号\n点击jump后\n查到NVIDIA 型号为 RTX 2080 Ti,再到英伟达官网下载页面下载\n下载后 用root 权限安装\n1 sudo NVIDIA-***.run --no-x-check --no-nouveau-check -s 安装完后重启一下\n1 reboot \u0026mdash;-end\u0026mdash;-\n",
"url": "\/post\/ubuntu%E5%AE%89%E8%A3%85nvidia%E6%98%BE%E5%8D%A1%E9%A9%B1%E5%8A%A8\/"
},
"\/post\/ubuntu%E5%AE%89%E8%A3%85%E5%A4%9A%E4%B8%AAgcc%E7%89%88%E6%9C%AC\/": {
"title": "ubuntu安装多个gcc版本",
"tags": ["Ubuntu","GCC",],
"content": "ubuntu 16.04 安装多个gcc版本 1.背景 由于我们软件开发时,通常在ubuntu16.04系统中运行,而ubuntu16.04 中系统自带的gcc版本为5.4.\nubuntu16.04 从2016年发布以来,就实验性的支持了c++17的部分功能,所以我们的工程中也部分的使用了c++17的功能。\n最近,在windows上使用msvc写了一部分代码,在与linux版本合并的时候,出现很多编译错误,才来了解gcc对c++17的支持情况\ngcc 5,部分的,实验性的支持了c++17的语法\ngcc 7, 在语法层面,完整的兼容了c++17标准\ngcc 9, c++17 ABI层面完整的兼容(意味着可以安全的交叉编译)\n信息来源\n考虑到ubuntu16已经于2021年结束标准支持,2026年将结束生命周期,以及使用更多的c++17的特性\n我决定在我的开发环境安装多个gcc版本,踩一下升级gcc坑,\n先设定一个小目标:安装可以在gcc 5 和gcc7之间来回切换的开发环境\n2.升级之前 备份虚拟机环境 ,基操\n3. 升级 我们主要通过build-essential 这个基础设施来 安装,管理 gcc , 所以首先安装它\n1 sudo apt install build-essential 下面三个镜像任选一个\n1 2 3 4 5 6 #清华 sudo sed -i \u0026#39;s/http:\\/\\/archive.ubuntu.com\\/ubuntu\\//http:\\/\\/mirrors.tuna.tsinghua.edu.cn\\/ubuntu\\//g\u0026#39; /etc/apt/sources.list #阿里云 sudo sed -i \u0026#39;s/http:\\/\\/archive.ubuntu.com\\/ubuntu\\//http:\\/\\/mirrors.aliyun.com\\/ubuntu\\//g\u0026#39; /etc/apt/sources.list #华为云 sudo sed -i \u0026#39;s/http:\\/\\/archive.ubuntu.com\\/ubuntu\\//https:\\/\\/mirrors.huaweicloud.com\\/ubuntu\\//g\u0026#39; /etc/apt/sources.list 安装gcc版本\n1 sudo apt -y install gcc-5 g++-5 gcc-7 g++-7 1 2 3 4 sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-5 5 sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-5 5 sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-7 7 sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-7 7 配置gcc版本\n1 /etc/apt$ sudo update-alternatives --config gcc 按2再按回车就会切换到gcc7\n1 2 3 4 5 6 7 8 9 10 deepvision@sw-008:/etc/apt$ sudo update-alternatives --config gcc There are 2 choices for the alternative gcc (providing /usr/bin/gcc). Selection Path Priority Status ------------------------------------------------------------ 0 /usr/bin/gcc-7 7 auto mode * 1 /usr/bin/gcc-5 5 manual mode 2 /usr/bin/gcc-7 7 manual mode Press \u0026lt;enter\u0026gt; to keep the current choice[*], or type selection number: 可以打印出版本来看是否切换成功\n1 2 gcc --version g++ --version end\n",
"url": "\/post\/ubuntu%E5%AE%89%E8%A3%85%E5%A4%9A%E4%B8%AAgcc%E7%89%88%E6%9C%AC\/"
},
"\/tags\/arm64\/": {
"title": "Arm64",
"tags": [],
"content": "",
"url": "\/tags\/arm64\/"
},
"\/tags\/cmake\/": {
"title": "CMake",
"tags": [],
"content": "",
"url": "\/tags\/cmake\/"
},
"\/post\/cmake_vcpkg_meson%E4%BA%A4%E5%8F%89%E7%BC%96%E8%AF%91arm64_raspberry_pi%E7%A8%8B%E5%BA%8F\/": {
"title": "cmake_vcpkg_meson交叉编译arm64_raspberry_pi程序",
"tags": ["C\u002b\u002b","VCPKG","CMake","Meson","RaspberryPI","Arm64","Cross-comple",],
"content": "0. 背景 大概是上个月左右,协助韩博把我写的这套网口相机框架运行到树莓派上,一直想找时间 记录一下,终于有时间 了。\n除了交叉编译我们自己的代码之外,还有一些依赖的3方的框架也需要编译为arm64的库,大概会按我下面列的这几点为大纲进行记录\nvcpkg用于安装大部分版本稳定的三方框架,少部分三方框架用vcpkg安装也不一定编译得了,因为官方只维护几种常用的triplets,如果是自定义太高的编译工作,将不得不使用社区版本的triplets,将使vcpkg遇到比平时更多的编译问题,当然如果有时间 +有能力的话,可以fix这些bug为vcpkg提交pr ,这部分工作通常由微软的vcpkg小组和社区开发者一同维护\n接下来我不会很详细的介绍某个具体的库是怎么编译的,甚至会跳过,主要介绍交叉编译的大致过程\n目录\n准备工作\narm64 gcc工具链下载\n复制树莓派 systemroot库目录,用于交叉编译时程序链接\n编译环境docker制作\n编译arm64 Qt5.15\ncmake交叉编译树莓派的工具链配置\nmeson交叉编译配置\nvcpkg配置\n绕过qt下载编译,使用本地已有的Qt cmake交叉编译配置 meson交叉编译配置 1. 准备工作 1.1 arm64 gcc 下载 略\n1.2 复制树莓派 systemroot库目录,用于交叉编译时程序链接 略 ,韩博做的镜像 host:rpi4-1中已经有做这个操作了,/sysroot 这个目录\n1.3 编译环境docker制作 SW/dv-qt-dev: Dockerfiles and scripts for QT development with containers. - dv-qt-dev (deepvision-tech.net)\n之前韩博写的这个dockerfile里,是从qt 安装包安装qt 的.run安装包+提取安装包脚本 安装qt 5.12.6,\n但qt 5.15没有.run安装包,所以需要自己编译\n所以如果你想编译的是5.12.6,可以直接使用这个docker file\n如果想用5.15,可以参考我在韩博的帮助下,创建的这个dockerfile,它没有包含qt的编译,我们以它做成的容器为环境,在容器中编译qt到我们挂载的卷上\nbuild_dev_env.docker\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 FROM --platform=linux/amd64 ubuntu:16.04 ENV DEBIAN_FRONTEND=noninteractive #RUN touch /etc/apt/sources.list \u0026amp;\u0026amp; \\ #echo \u0026#34;deb http://ftp.cn.debian.org/debian bookworm main contrib non-free non-free-firmware\u0026#34; \u0026gt;\u0026gt; /etc/apt/sources.list \u0026amp;\u0026amp; \\ #echo \u0026#34;deb http://ftp.cn.debian.org/debian-security/ bookworm-security main contrib non-free non-free-firmware\u0026#34; \u0026gt;\u0026gt; /etc/apt/sources.list \u0026amp;\u0026amp; \\ #echo \u0026#34;deb http://ftp.cn.debian.org/debian bookworm-updates main contrib non-free non-free-firmware\u0026#34; \u0026gt;\u0026gt; /etc/apt/sources.list \u0026amp;\u0026amp; \\ #echo \u0026#34;deb-src http://ftp.cn.debian.org/debian bookworm main contrib non-free non-free-firmware\u0026#34; \u0026gt;\u0026gt; /etc/apt/sources.list \u0026amp;\u0026amp; \\ #echo \u0026#34;deb-src http://ftp.cn.debian.org/debian-security/ bookworm-security main contrib non-free non-free-firmware\u0026#34; \u0026gt;\u0026gt; /etc/apt/sources.list \u0026amp;\u0026amp; \\ #echo \u0026#34;deb-src http://ftp.cn.debian.org/debian bookworm-updates main contrib non-free non-free-firmware\u0026#34; \u0026gt;\u0026gt; /etc/apt/sources.list \u0026amp;\u0026amp; \\ COPY set_aptsource.sh /etc/apt RUN chmod +x /etc/apt/set_aptsource.sh \u0026amp;\u0026amp; bash /etc/apt/set_aptsource.sh \u0026amp;\u0026amp;\\ apt update \u0026amp;\u0026amp; \\ apt install -y \\ autoconf \\ libtool \\ autoconf-archive \\ automake \\ bison \\ build-essential \\ cmake \\ cowsay \\ ethtool \\ figlet \\ flex \\ freeglut3-dev \\ g++-aarch64-linux-gnu \\ gawk \\ gcc-aarch64-linux-gnu \\ gcc \\ gdb-multiarch \\ gfortran \\ git \\ gperf \\ libatspi2.0-dev \\ libavcodec-dev \\ libavformat-dev \\ libcap-dev \\ libcap2 \\ libclang-dev \\ libdc1394-22-dev \\ libfontconfig1-dev \\ libfreetype6-dev \\ libgl1-mesa-dev \\ libglade2-0 \\ libglade2-dev \\ libglu1-mesa-dev \\ libgstreamer-plugins-base0.10-dev \\ libgtk-3-dev \\ libjasper-dev \\ libjpeg-dev \\ liblapack-dev \\ libncurses-dev \\ libpcap0.8 \\ libpng12-dev \\ libsndio-dev \\ libssh2-1 \\ libswscale-dev \\ libtiff-dev \\ libva-dev \\ libva-drm1 \\ libva-egl1 \\ libva-glx1 \\ libva1 \\ libx11-dev \\ libx11-xcb-dev \\ libxcb-glx0-dev \\ libxcb-icccm4-dev \\ libxcb-image0-dev \\ libxcb-keysyms1-dev \\ libxcb-randr0-dev \\ libxcb-render-util0-dev \\ libxcb-shape0-dev \\ libxcb-shm0-dev \\ libxcb-sync-dev \\ libxcb-sync0-dev \\ libxcb-util-dev \\ libxcb-xfixes0-dev \\ libxcb-xinerama0-dev \\ libxcb-xkb-dev \\ libxcb1-dev \\ libxext-dev \\ libxfixes-dev \\ libxi-dev \\ libxkbcommon-dev \\ libxkbcommon-x11-0 \\ libxkbcommon-x11-dev \\ libxrender-dev \\ libxslt1-dev \\ lsb-release \\ make \\ mesa-common-dev \\ ninja-build \\ openssl \\ pigz \\ pkg-config \\ python3-pip \\ python3 \\ rsync \\ subversion \\ tar \\ texinfo \\ unzip \\ vim \\ wget \\ x11vnc \\ xauth \\ zlib1g-dev \u0026amp;\u0026amp; \\ mkdir /build \u0026amp;\u0026amp; \\ mkdir /src \u0026amp;\u0026amp; \\ mkdir /repos \u0026amp;\u0026amp; \\ apt-get clean CMD [\u0026#34;/usr/bash\u0026#34;] #pip3 install openpyxl==2.6.4 \u0026amp;\u0026amp; \\ #pip3 install xlrd==1.2.0 \u0026amp;\u0026amp; \\ #libgli-mesa-dev \\ #libglul-mesa-dev \\ 上面dockerfile用到的替换apt源的脚本,用于把镜像的apt源替换为我们公司的apt缓存\nset_aptsource.sh\n1 2 3 4 5 6 7 8 9 10 11 12 #!/bin/bash OldSource=`grep ^deb /etc/apt/sources.list|awk \u0026#39;{print $2}\u0026#39;|sort |uniq |sed \u0026#39;/security.ubuntu.com/d\u0026#39;` NewSource=\u0026#34;-apt.deepvision-tech.net/repository/apt-\u0026#34; OsVer=`cat /etc/issue|awk -F\u0026#39;[ .]+\u0026#39; \u0026#39;{print $2}\u0026#39;` cp -f /etc/apt/sources.list /etc/apt/sources.list.bak echo \u0026#34;\u0026#34; echo -e \u0026#34;${YELOW}** Old apt sources backup to [/etc/apt/sources.list.bak] **${RES}\u0026#34; sed -i \u0026#34;s#${OldSource}#http://hz${NewSource}${OsVer}/#g\u0026#34; /etc/apt/sources.list echo -e \u0026#34;${YELOW}** new apt source replaced by [http://hz${NewSource}${OsVer}] **${RES}\u0026#34; echo -e \u0026#34;${YELOW}** Please run command [sudo apt update],Let config become effective **${RES}\u0026#34; 把它们俩放在同一目录,然后在进入目录,执行\n1 docker build build_dev_env.docker . -t dockerRepoName:tag 这样就能构建docker镜像了\n2. 编译arm64 Qt5.15.11 2.1 Qt5.15 源码获取 如果你对qt的版本历史熟悉的话,应该知道,从官方网站上能下载到的最后一个开源版本的安装包是是Qt 5.14,再往后,需要下载Qt的在线安装器,在线安装, 由于我们将在编译服务器的docker容器中进行编译,默认情况下是没有gui支持的 ,所以用图形化的安装会多做一些步骤,虽然韩博已经有写过相关的文档,也可以做到,但我们还是更倾向于使用源码编译Qt5.15,因为在编译服务器160核的加持下,编译一个Qt5只需要大概十分钟左右\n所以需要获取Qt5.15的源码,\n从vcpkg源码中可以获取到qt5的源码下载路径,(不直接使用vcpkg是因为qt编译选项比较多,直接编译比较灵活)\n在qt_download_submodule.cmake这个文件中这几行中,可以看到获取到伯克利大学提供的qt源码\n1 2 3 4 5 6 set(FULL_VERSION \u0026#34;${QT_MAJOR_MINOR_VER}.${QT_PATCH_VER}\u0026#34;) set(ARCHIVE_NAME \u0026#34;${NAME}-everywhere-opensource-src-${FULL_VERSION}.tar.xz\u0026#34;) set(URLS \u0026#34;https://download.qt.io/archive/qt/${QT_MAJOR_MINOR_VER}/${FULL_VERSION}/submodules/${ARCHIVE_NAME}\u0026#34; \u0026#34;https://mirrors.ocf.berkeley.edu/qt/archive/qt/${QT_MAJOR_MINOR_VER}/${FULL_VERSION}/submodules/${ARCHIVE_NAME}\u0026#34; ) 也可以从浏览器直接进入 OCF Mirrors (berkeley.edu)\n如果要完整的源码,下载single目录下的tar包\n如果只是要编译某个子模块,下载submodules里的具体模块的tar包\n2.2 配置编译选项 树莓上有两种gui模式,一种是系统只有一个窗口,需要qt编译选项egfls,一种传统桌面系统,需要qt对应的编译选项是x11或wayland模式\neglfs的特点是系统除了命令行窗口,这种情况下适合使用qt quick controls 2 + opengl es开发,因为qt quick controls 2本身就是混合渲染的,控件不会创建系统原生窗口,\n相对于eglfs, x11或wayland 通常每个控件都会创建单独的系统原生窗口, qt quick controls 1 和 qt widget都是这个模式,\n配置qt的树霉交叉编译选项时,主要这个需要注意\n-device\n-device-option\n-sysroot\n-platform\n-xplatform\n其中 -platfrom 和 -xplatform在交叉编译rasp-pi时好像不是必要的\n在源码目录,qt5/qtbase/mkspecs中可以查看这些可选项\n其他的都是模块选项,安装目录之类的,这里主要还是参考韩博之前写的文档的脚本\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 #!/bin/bash # vim: tabstop=4 shiftwidth=4 softtabstop=4 # -*- sh-basic-offset: 4 -*- set -exuo pipefail BUILD_TARGET=/build SRC=/src # Note: If changing the QT_BRANCH to a lower version, modify line 43 for the correct path QT_BRANCH=\u0026#34;5.15.11\u0026#34; DEBIAN_VERSION=$(lsb_release -cs) MAKE_CORES=64 /usr/games/cowsay -f tux \u0026#39;Building QT version $QT_BRANCH.\u0026#39; function patch_qt () { local QMAKE_FILE=\u0026#39;/src/qt5/qtbase/mkspecs/devices/linux-rasp-pi4-v3d-g++/qmake.conf\u0026#39; echo \u0026#34;\u0026#34; \u0026gt; \u0026#34;$QMAKE_FILE\u0026#34; echo \u0026#39;include(../common/linux_device_pre.conf)\u0026#39; \u0026gt;\u0026gt; \u0026#34;$QMAKE_FILE\u0026#34; echo \u0026#39;QMAKE_INCDIR_OPENGL_ES2 = $${QMAKE_INCDIR_EGL}\u0026#39; \u0026gt;\u0026gt; \u0026#34;$QMAKE_FILE\u0026#34; echo \u0026#39;QMAKE_LIBS_OPENGL_ES2 = $${VC_LINK_LINE} -lGLESv2\u0026#39; \u0026gt;\u0026gt; \u0026#34;$QMAKE_FILE\u0026#34; echo \u0026#39;QMAKE_LIBS_EGL = $${VC_LINK_LINE} -lEGL -lGLESv2\u0026#39; \u0026gt;\u0026gt; \u0026#34;$QMAKE_FILE\u0026#34; #echo \u0026#39;QMAKE_LIBDIR_BCM_HOST = $$VC_LIBRARY_PATH\u0026#39; \u0026gt;\u0026gt; \u0026#34;$QMAKE_FILE\u0026#34; #echo \u0026#39;QMAKE_INCDIR_BCM_HOST = $$VC_INCLUDE_PATH\u0026#39; \u0026gt;\u0026gt; \u0026#34;$QMAKE_FILE\u0026#34; #echo \u0026#39;QMAKE_LIBS_BCM_HOST = -lbcm_host\u0026#39; \u0026gt;\u0026gt; \u0026#34;$QMAKE_FILE\u0026#34; echo \u0026#39;QMAKE_CFLAGS = -march=armv8-a\u0026#39; \u0026gt;\u0026gt; \u0026#34;$QMAKE_FILE\u0026#34; echo \u0026#39;QMAKE_CXXFLAGS = $$QMAKE_CFLAGS\u0026#39; \u0026gt;\u0026gt; \u0026#34;$QMAKE_FILE\u0026#34; echo \u0026#39;QT_QPA_DEFAULT_PLATFORM = xcb\u0026#39; \u0026gt;\u0026gt; \u0026#34;$QMAKE_FILE\u0026#34; #echo \u0026#39;EGLFS_DEVICE_INTEGRATION= eglfs_kms\u0026#39; \u0026gt;\u0026gt; \u0026#34;$QMAKE_FILE\u0026#34; echo \u0026#39;load(qt_config)\u0026#39; \u0026gt;\u0026gt; \u0026#34;$QMAKE_FILE\u0026#34; } function fetch_qt5 () { pushd /src if [ ! -d \u0026#34;qt5\u0026#34; ]; then mkdir qt5 tar xf /src/qt-everywhere-opensource-src-\u0026#34;$QT_BRANCH\u0026#34;.tar.xz -C ./qt5 --strip-components=1 fi popd\t} function build_qt () { local SRC_DIR=\u0026#34;/src/$1\u0026#34; if [ ! -f \u0026#34;$BUILD_TARGET/qt5-$QT_BRANCH-$DEBIAN_VERSION-$1.tar.gz\u0026#34; ]; then /usr/games/cowsay -f tux \u0026#34;Building QT for $1\u0026#34; fetch_qt5 mkdir -p \u0026#34;$SRC_DIR\u0026#34; pushd \u0026#34;$SRC_DIR\u0026#34; patch_qt /src/qt5/configure \\ -device linux-rasp-pi4-v3d-g++ \\ -opengl es2 \\ -egl \\ -qpa xcb \\ -xcb \\ -eglfs \\ -opensource \\ -confirm-license \\ -device-option CROSS_COMPILE=aarch64-linux-gnu- \\ -extprefix \u0026#34;$SRC_DIR/qt5pi\u0026#34; \\ -pkg-config \\ -qt-pcre \\ -no-pch \\ -evdev \\ -system-freetype \\ -fontconfig \\ -glib \\ -make libs \\ -no-compile-examples \\ -no-cups \\ -no-gtk \\ -no-use-gold-linker \\ -nomake examples \\ -nomake tests \\ -prefix /usr/local/qt5pi \\ -release \\ -skip qtwebengine \\ -skip qtandroidextras \\ -skip qtgamepad \\ -skip qtlocation \\ -skip qtlottie \\ -skip qtmacextras \\ -skip qtpurchasing \\ -skip qtscxml \\ -skip qtsensors \\ -skip qtserialbus \\ -skip qtserialport \\ -skip qtspeech \\ -skip qttools \\ -skip qttranslations \\ -skip qtvirtualkeyboard \\ -skip qtwayland \\ -skip qtwebview \\ -skip qtwinextras \\ -skip wayland \\ -sysroot /sysroot \\ -no-feature-eglfs_brcm \\ -recheck \\ -platform linux-g++-64 \\ -L/sysroot/lib/aarch64-linux-gnu \\ -L/sysroot/usr/lib/aarch64-linux-gnu \\ -I/sysroot/usr/include/ \\ -L/sysroot/lib/mesa-diverted/aarch64-linux-gnu \\ -I/sysroot/usr/include/GLES2 \\ -I/sysroot/usr/include/GLES /usr/games/cowsay -f tux \u0026#34;Making Qt...\u0026#34; make -j\u0026#34;$MAKE_CORES\u0026#34; /usr/games/cowsay -f tux \u0026#34;Installing Qt...\u0026#34; make install pushd \u0026#34;$SRC_DIR\u0026#34; tar cfz \u0026#34;$BUILD_TARGET/qt5-$QT_BRANCH-$DEBIAN_VERSION-$1.tar.gz\u0026#34; qt5pi popd pushd \u0026#34;$BUILD_TARGET\u0026#34; sha256sum \u0026#34;qt5-$QT_BRANCH-$DEBIAN_VERSION-$1.tar.gz\u0026#34; \u0026gt; \u0026#34;qt5-$QT_BRANCH-$DEBIAN_VERSION-$1.tar.gz.sha256\u0026#34; popd else echo \u0026#34;QT Build already exist.\u0026#34;q fi } # Fix relative paths for Raspberry Pi Sysroot build_qt \u0026#34;pi5\u0026#34; 3. cmake交叉编译树莓派的工具链配置 toolchain-arm64-raspberrypie.cmake\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 #参考文档 https://cmake.org/cmake/help/latest/manual/cmake-toolchains.7.html#cross-compiling-for-linux set(CMAKE_SYSTEM_NAME Linux) set(CMAKE_SYSTEM_PROCESSOR arm64) #树莓派sysroot目录 set(CMAKE_SYSROOT /sysroot) #安装目录 set(CMAKE_STAGING_PREFIX /repos/dv_app_solution/vcpkg_installed/arm64-linux) #指定编译器 set(tools /usr) set(CMAKE_C_COMPILER ${tools}/bin/aarch64-linux-gnu-gcc) set(CMAKE_CXX_COMPILER ${tools}/bin/aarch64-linux-gnu-g++) #手动指定arm64的qt cmake模块 set(QT_DIR /src/pi5/qt5pi/lib/cmake/Qt5) set(Qt5_DIR /src/pi5/qt5pi/lib/cmake/Qt5) set(ENV{QT_DIR} /src/pi5/qt5pi/lib/cmake/Qt5) set(ENV{QT_DIR} /src/pi5/qt5pi/lib/cmake/Qt5) set(QT_QMAKE_EXECUTABLE /src/pi5/qt5pi/bin/qmake) set(Qt5Core_DIR /src/pi5/qt5pi/lib/cmake/Qt5Core) set(Qt5Gui_DIR /src/pi5/qt5pi/lib/cmake/Qt5Gui) set(Qt5Widgets_DIR /src/pi5/qt5pi/lib/cmake/Qt5Widgets) set(Qt5Quick_DIR /src/pi5/qt5pi/lib/cmake/Qt5Quick) set(Qt5QuickControls2_DIR /src/pi5/qt5pi/lib/cmake/Qt5QuickControls2) set(Qt5QmlModels_DIR /src/pi5/qt5pi/lib/cmake/Qt5QmlModels) set(Qt5Qml_DIR /src/pi5/qt5pi/lib/cmake/Qt5Qml) set(Qt5Network_DIR /src/pi5/qt5pi/lib/cmake/Qt5Network) set(Qt5LinguistTools_DIR /src/pi5/qt5pi/lib/cmake/Qt5LinguistTools) #因为是交叉编译,所以主机里有arm64的库也有amd64的库,下面这几行设置不让cmake自己在目录中搜索库, #而是通过手动设置库的路径,防止编译错误 set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) #cmake prefix set(CMAKE_PREFIX_PATH /src/pi5/qt5pi;/repos/dv_app_solution/vcpkg_installed/arm64-linux) 4.meson交叉编译配置 toolchain-arm64-raspberrypie-messon.cross\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 #参考文档1 https://mesonbuild.com/Cross-compilation.html #参考文档2 https://github.com/mesonbuild/meson/blob/master/docs/markdown/Reference-tables.md#cpu-families [binaries] c = \u0026#39;/usr/bin/aarch64-linux-gnu-gcc\u0026#39; cpp = \u0026#39;/usr/bin/aarch64-linux-gnu-g++\u0026#39; ar = \u0026#39;/usr/bin/aarch64-linux-gnu-ar\u0026#39; strip = \u0026#39;/usr/bin/aarch64-linux-gnu-strip\u0026#39; [properties] sys_root = \u0026#39;/sysroot\u0026#39; c_args = [\u0026#39;-march=armv8-a\u0026#39;, \u0026#39;-mtune=cortex-a53\u0026#39;, \u0026#39;-marm\u0026#39;] # cpp_args = [\u0026#39;-std=c++17\u0026#39;,\u0026#39;-march=armv8-a\u0026#39;, \u0026#39;-mtune=cortex-a53\u0026#39;, \u0026#39;-marm\u0026#39;] # #pkg_config_libdir = \u0026#39;/repos/dv_app_solution/vcpkg_installed/arm64-linux\u0026#39; #sizeof_int = 4 #sizeof_wchar_t = 4 #sizeof_void* = 4 # #alignment_char = 1 #alignment_void* = 4 #alignment_double = 4 # #has_function_printf = true #c_args = [\u0026#39;-march=armv8-a\u0026#39;, \u0026#39;-mtune=cortex-a53\u0026#39;, \u0026#39;-marm\u0026#39;,\u0026#39;-I/src/pi5/qt5pi/include\u0026#39;,\u0026#39;-I/repos/dv_app_solution/vcpkg_installed/arm64-linux/include\u0026#39;] # #cpp_args = [\u0026#39;-march=armv8-a\u0026#39;, \u0026#39;-mtune=cortex-a53\u0026#39;, \u0026#39;-marm\u0026#39;,\u0026#39;-I/src/pi5/qt5pi/include\u0026#39;,\u0026#39;-I/repos/dv_app_solution/vcpkg_installed/arm64-linux/include\u0026#39;] # #library_dirs = [\u0026#39;/src/pi5/qt5pi/lib\u0026#39;,\u0026#39;/repos/dv_app_solution/vcpkg_installed/arm64-linux/lib\u0026#39;] #/repos/dv_app_solution/vcpkg_installed/arm64-linux/include/glib-2.0/glib-object.h [build_machine] system = \u0026#39;linux\u0026#39; cpu_family = \u0026#39;x86_64\u0026#39; cpu = \u0026#39;x86_64\u0026#39; endian = \u0026#39;little\u0026#39; [host_machine] system = \u0026#39;linux\u0026#39; cpu_family = \u0026#39;x86_64\u0026#39; cpu = \u0026#39;x86_64\u0026#39; endian = \u0026#39;little\u0026#39; [target_machine] system = \u0026#39;linux\u0026#39; cpu_family = \u0026#39;aarch64\u0026#39; cpu = \u0026#39;aarch64\u0026#39; endian = \u0026#39;little\u0026#39; 5. vcpkg配置 5.1 使用本地的qt,绕过下载qt,编译qt的步骤 使用custom-ports\n例如,想用vcpkg安装vtk,但vtk中依赖了qt,这时可以复制一份vtk在vcpkg中的配置\n1 cp -rf path2vcpkg/ports/vtk/ ~/my-custom-vtk-port 然后把 ~/my-custom-vtk-port/vcpkg.json 中的dependency json 数组 结点中的qt相关的条目删除掉\n然后 vcpkg install的时候加上 --overlay-ports=~/my-custom-vtk-port\n或者直接修改vcpkg的源码,在原来的port/vcpkg.json中直接删除qt相关的条目\n安装时不用--overlay-ports也不会下载 编译 qt\n5.2 为vcpkg设置交叉编译工具链 使用custom-triplet\n复制一份vcpkg源码下的triplet\n1 cp -rf path2vcpkg/triplits ~/my-custom-triplets 然后修改~/my-custom-triplets/community/arm64-linux.cmake\n内容改为\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 set(VCPKG_TARGET_ARCHITECTURE arm64) set(VCPKG_CRT_LINKAGE dynamic) set(VCPKG_LIBRARY_LINKAGE static) set(VCPKG_CMAKE_SYSTEM_NAME Linux) set(VCPKG_BUILD_TYPE release) set(QT_DIR /src/pi5/qt5pi/lib/cmake) set(ENV{QT_DIR} /src/pi5/qt5pi/lib/cmake) set(Qt5_DIR /src/pi5/qt5pi/lib/cmake) set(ENV{Qt5_DIR} /src/pi5/qt5pi/lib/cmake) set(VCPKG_ENV_PASSTHROUGH QT_DIR Qt5_DIR) set(CMAKE_TOOLCHAIN_FILE /repos/vcpkg/toolchains/arm64-raspberrypie.cmake) set(ENV{CMAKE_TOOLCHAIN_FILE} /repos/vcpkg/toolchains/arm64-raspberrypie.cmake) set(VCPKG_MESON_CROSS_FILE /repos/vcpkg/toolchains/arm64-raspberrypie-meson.cross) 由于我们在5.1中已经 设置了vcpkg不下载编译qt5,所以我们在这个triplet文件中,设置了qt5的本地路径,然后\n然后设置CMAKE_TOOLCHAIN_FILE为步骤3中写的cmake 工具链\n设置VCPKG_MESON_CROSS_FILE为步骤4中的meson交叉编译配置文件\n然后在使用使用配置好的交叉编译的triplet进行安装\n1 vcpkg install --overlay-triplets=~/my-custom-triplets --triplet=arm64-linux --host-triplet=x64-linux 这样所安装的库就会按配置进行交叉编译了\n6 编译我们自己的cmake工程 1 2 3 4 5 6 7 cd dv_app_solution #进入我们的工程 mkdir build \u0026amp;\u0026amp; cd build cmake -DCMAKE_TOOLCHAIN_FILE=~/path/to/toolchain-arm64-raspberrypie.cmake \\ -DCMAKE_PREFIX_PATH=/path/to/pi5/qt5pi/lib/cmake;/repos/dv_app_solution/vcpkg_installed/arm64-linux \\ .. cmake --build . --target=install 其中/repos/dv_app_solution/vcpkg_installed/arm64-linux 为vcpkg 库安装的目录,写在步骤3中的toolchain-arm64-raspberrypie.cmake中\n注意\ndv_app_solution首次编译时,如果没有把所有子模块发布到vcpkg,通过vcpkg安装依赖项,则需要手动逐个编译子模块,\n否则会因为cmake 的find_package找不到依赖,导致编译失败 ,编译安装顺序为\nprism prism_container prism_qt_modular prism_qt_core prism_qt_ui dv_algorithm dv_tools dv_common dv_camera dv_app 后续会参考其他开源项目的常用做法,编写bash和powershell的初始化编译脚本\n",
"url": "\/post\/cmake_vcpkg_meson%E4%BA%A4%E5%8F%89%E7%BC%96%E8%AF%91arm64_raspberry_pi%E7%A8%8B%E5%BA%8F\/"
},
"\/tags\/cross-comple\/": {
"title": "Cross-comple",
"tags": [],
"content": "",
"url": "\/tags\/cross-comple\/"
},
"\/tags\/meson\/": {
"title": "Meson",
"tags": [],
"content": "",
"url": "\/tags\/meson\/"
},
"\/tags\/raspberrypi\/": {
"title": "RaspberryPI",
"tags": [],
"content": "",
"url": "\/tags\/raspberrypi\/"
},
"\/tags\/vcpkg\/": {
"title": "VCPKG",
"tags": [],
"content": "",
"url": "\/tags\/vcpkg\/"
},
"\/tags\/core\/": {
"title": "Core",
"tags": [],
"content": "",
"url": "\/tags\/core\/"
},
"\/tags\/dump\/": {
"title": "Dump",
"tags": [],
"content": "",
"url": "\/tags\/dump\/"
},
"\/post\/ubuntu16.04%E8%AE%BE%E7%BD%AEcore-dump\/": {
"title": "ubuntu16.04设置core dump",
"tags": ["Ubuntu","Core","Dump",],
"content": "0. 之前同事写过这个主题,包括我们软件给技术支持的pdf里也有这个说明,但都请得比较抽象,所以重新写一个详细版本\n1. 设置core dump资源限制 通过ulimit -a可以查看各类资源限制,其中-c类表示core\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 deepvision@ubuntu:~/source/repos/dv_app_solution/bin/x86_64(master)$ ulimit -a core file size (blocks, -c) 0 data seg size (kbytes, -d) unlimited scheduling priority (-e) 0 file size (blocks, -f) unlimited pending signals (-i) 31574 max locked memory (kbytes, -l) 64 max memory size (kbytes, -m) unlimited open files (-n) 1024 pipe size (512 bytes, -p) 8 POSIX message queues (bytes, -q) 819200 real-time priority (-r) 0 stack size (kbytes, -s) 8192 cpu time (seconds, -t) unlimited max user processes (-u) 31574 virtual memory (kbytes, -v) unlimited file locks (-x) unlimited 默认情况下,应该看到core file size 限制为0,ubuntu 16.04稳定版本发布后的默认设置\n将它设置为unlimited是生成core dump的必要不充分条件\n1.1 临时设置 在终端设置ulimit -c unlimited,这种方式,另启动一个终端就恢复系统默认设置了,而且会受到1.2中第二列配置值的影响,可能不会生效,所以如果只是写到 ~/bashrc或~/profile, 当程序不使用bash运行 ,可能不会生效\n1.2 持久化设置 用管理员权限打开/etc/security/limits.config\n并写入\n1 * hard core ulimited 其中hard可以用soft替代, 区别是hard 普通用户不可以通过ulimit 命令临时改变资源限制配额,soft相反\n修改后让它立即生效\n1 su - deepvision 切换一下当前用户为当前用户,会重载配置,再ulimit -a查看配置,应该看到core已经是unlimited\n2. 设置bug, crash后处理的管道 2.1 查看系统处理bug,崩溃的默认配置 可以在/proc/sys/kernal/core_pattern 文件中查看到设置的处理方式\n刚装好的ubuntu16.04应该是这样的\n1 2 root@681e5135949f:/# cat /proc/sys/kernel/core_pattern |/usr/lib/systemd/systemd-coredump %P %u %g %s %t %c %h %e 搞错了,这个是我在129编译服务器上的ubuntu 16.04 docker 容器中打印的,由于docker容器会使用host的内核,所以这个是129服务器的默认设置,即为centos 8\nubuntu16.04内核应该是\n1 2 deepvision@ubuntu:/# cat /proc/sys/kernel/core_pattern |/usr/share/apport/apport %p %s %c %d %P %E 可以看出,ubuntu16.04 默认使用apport 报告错误,而centos8通过coredump服务来处理bug,崩溃\n2.2. 修改ubuntu16.04 处理bug,crash为生成dump 先停止掉apport服务\n1 sudo service apport stop 使用sudo 编辑 /etc/sysctl.config, 使打印后有kernal.core_pattern 的配置\n1 2 3 4 5 6 7 8 9 10 deepvision@ubuntu:~/source/repos/dv_app_solution(master)$ cat /etc/sysctl.conf |head -n 10 省略 . . . kernel.core_pattern = core.%e_%p_%h_%t . . . 省略 %e 可执行文件名称\n%p 进程号\n%h 主机名称\n%t时间戳表示 , (unix epoch + 生成的ms数得到当前时间)\n可以在core.%e_%p_%h_%t前面加绝对路径,不加表示生成在可执行文件所在的目录\n然后更新一下配置\n1 sudo sysctl -p 这条命令应该会把配置刷新到/proc/sys/kernal 目录的配置文件中,使配置生效\n测试生成core dump\n在程序main函数中加入崩溃代码\n1 2 3 int zero = 0; int exception_num = 1/zero; qDebug() \u0026lt;\u0026lt; exception_num; 如果是release配置,不要省略第3行,因为没有使用的变量会被编译器优化掉,导致没有崩溃\n1 2 3 4 deepvision@ubuntu:~/source/repos/dv_app_solution/bin/x86_64(master)$ ./dv_app Floating point exception (core dumped) deepvision@ubuntu:~/source/repos/dv_app_solution/bin/x86_64(master)$ ll|grep core -rw------- 1 deepvision deepvision 2174976 1月 13 15:18 core.dv_app_107389_ubuntu_1705130336 生成dump成功\n",
"url": "\/post\/ubuntu16.04%E8%AE%BE%E7%BD%AEcore-dump\/"
},
"\/post\/%E5%8A%A8%E6%80%81%E5%8A%A0%E8%BD%BD%E5%8A%A8%E6%80%81%E5%BA%93%E5%8F%8A%E5%BA%93%E5%86%85c%E9%A3%8E%E6%A0%BC%E6%96%B9%E6%B3%95\/": {
"title": "动态加载动态库,及库内c风格方法",
"tags": ["C\u002b\u002b",],
"content": "1 背景 《新的网口相机软件工程》,设计之初就考虑到扩展性,对于不同产品,不同的项目来说,\n代码全都收敛到同一个代码库,同一个分支。不同的产品也只不过是不同的插件而已,\n可以通过配置文件中的项目类型,过滤不需要加载的插件\n这样做的优点是,\n可以像搭积木一样,从各个动态库中取出UI和逻辑, 拼凑成一个app 减少重复的代码,提高代码重用率 减少维护成本,bug的修复,只需要一次,不需要跨分支,跨产品的到处合fix分支和补丁 减少代码分支数量,所有使用框架的人围绕着master分支展开工作 方便进行重构,好的代码都是重构出来的,当所有代码都在事一个分支上时,重构时只要把旧的接口保留,并标记为过时,增加新的重构方法,慢慢迭代掉过时方法就行了,如果分支很多的话,重构往往阻力重重 2 遇到什么问题 算法库,按产品划分分支, 不同的产品,有不同的头文件\n这导致一个问题\n当软件与不同的算法产品搭配使用时,按以往的方式,添加头文件,链接算法库, 由于算法头文件不同,将导致软件也需要开和算法一样多的分支才能保证链接算法库不会因为找不到符号导致编译失败\n这样一来,软件也不得不每一个定制项目开一个分支去和算法适配了。\n3 解决方法 我曾向算法部沟通建议过,让他们给出一个合并版本的头文件, 不同的产品,按名称空间划分好,例如二维码算法项目有自己的接口,也有3d激光线扫的接口,但3d激光线扫的接口只放空实现, 这个建议完驳回了,因为算法认为这样会污染头文件\n所以我尝试了另一种动态库调用的方式, 通过系统api 动态加载动态库,并查找已知的符号,转换为函数指针, 调用函数指针。\n经测试, windows上和linux上都可以正常的调用,不过需要算法提供C风格的函数导出。\n4 示例代码 4.1 纯c风格导出 头文件 purecpplib.h\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 #ifndef PURECPPLIB_H #define PURECPPLIB_H #ifdef _WIN32 #define HBBEXPORT __declspec(dllexport) #else #define HBBEXPORT #endif #ifdef __cplusplus extern \u0026#34;C\u0026#34; { #endif HBBEXPORT int avg(int a, int b); #ifdef __cplusplus } #endif #endif // PURECPPLIB_H 使用__cplusplus这个宏加了一对\n1 2 3 extern \u0026#34;C\u0026#34; { } 把想导出的方法包住,当然windows下仍然要加导出的修饰符__declspec(dllexport)\nCPP purecpplib.cpp\n1 2 3 4 5 #include \u0026#34;purecpplib.h\u0026#34; int avg(int a, int b) { return (a + b) / 2; } 4.2 windows/linux 动态调用简单封装 头文件 danymic_lib_caller.h\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 #ifndef DANYMIC_LIB_CALLER_H #define DANYMIC_LIB_CALLER_H #include \u0026#34;prism_qt_modular_global.h\u0026#34; #ifdef _WIN32 #include \u0026lt;Windows.h\u0026gt; #endif #ifdef __linux #include \u0026lt;dlfcn.h\u0026gt; #endif #include \u0026lt;iostream\u0026gt; #include \u0026lt;filesystem\u0026gt; class PRISM_QT_MODULAR_EXPORT dynamic_lib_caller { public: dynamic_lib_caller(); //加载动态库 static void* loadLib(std::string path); //通过符号获取函数指针 static void* getFunctionAddr(void *lib, const char* funName); //卸载动态库 static void unloadLib(void *lib); }; #endif // DANYMIC_LIB_CALLER_H CPP danymic_lib_caller.cpp\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 #include \u0026#34;include/prism/qt/modular/dynamic_lib_caller.h\u0026#34; #include \u0026lt;sstream\u0026gt; dynamic_lib_caller::dynamic_lib_caller() { } #ifdef _WIN32 void *dynamic_lib_caller::loadLib(std::string path) { HMODULE lib = LoadLibrary(path.c_str()) ; return static_cast\u0026lt;void*\u0026gt;(lib); } void *dynamic_lib_caller::getFunctionAddr(void *lib, const char* funName) { HMODULE library = static_cast\u0026lt;HMODULE\u0026gt;(lib); // 获取函数地址 FARPROC functionAddr = GetProcAddress(library, funName); if (!functionAddr) { return nullptr; } return static_cast\u0026lt;void*\u0026gt;(functionAddr); } void dynamic_lib_caller::unloadLib(void *lib) { if(lib) { HMODULE library = static_cast\u0026lt;HMODULE\u0026gt;(lib); FreeLibrary(library); } } #endif #ifdef __linux void *dynamic_lib_caller::loadLib(std::string path) { void* library = dlopen(path.c_str(), RTLD_NOW); return library; } void *dynamic_lib_caller::getFunctionAddr(void *lib, const char* funName) { void* functionAddr = dlsym(lib, funName); // 获取函数地址 if (!functionAddr) { return nullptr; } return functionAddr; } void dynamic_lib_caller::unloadLib(void *lib) { if(lib) { dlclose(lib); } } #endif 这个cpp文件通过 _WIN32 和 __linux 两个编译器自带的宏,兼容了windows和linux下的 动态加载库,获取函数指针,卸载动态库的方法.\n4.3 使用示例 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 #include \u0026lt;prism/qt/modular/dynamic_lib_caller.h\u0026gt; #include \u0026lt;catch2/catch_all.hpp\u0026gt; #include \u0026lt;catch2/catch_all.hpp\u0026gt; #define CATCH_CONFIG_RUNNER TEST_CASE(\u0026#34;动态库调用\u0026#34;) { #ifdef _WIN32 void* lib =dynamic_lib_caller::loadLib(R\u0026#34;(...\\pureCppLib.dll)\u0026#34;); #else void* lib =dynamic_lib_caller::loadLib(R\u0026#34;(.../libpureCppLib.so.1.0.0)\u0026#34;); #endif if(lib) { //[1] 查找pureCppLib动态库中的avg函数地址 void* func = dynamic_lib_caller::getFunctionAddr(lib,\u0026#34;avg\u0026#34;); if(func) { double value = -1; typedef int (*avgFuc)(int, int); // [2] 转换为函数指针 avgFuc f = (avgFuc)(func); if(f) { value = f(6,15); // [3] 调用 dynamic_lib_caller::unloadLib(lib); } std::cout \u0026lt;\u0026lt; value; } } } int main(int argc, const char** argv) { #ifdef _MSC_VER SetConsoleOutputCP(CP_UTF8); SetConsoleCP(CP_UTF8); #endif int result = Catch::Session().run(argc, argv); return result; } 在windows和linux下测试,函数都正常输出10, (6和15)的平均值\n如此一下,只需要算法给出c风格的接口,我们就能在不链接的情况下调用了。\n甚至可以在有gcc的环境,使用字符串写一个c++的方法,用命令行调用gcc编译后动态的加载到程序里直接调用,达到类似脚本的目的\n",
"url": "\/post\/%E5%8A%A8%E6%80%81%E5%8A%A0%E8%BD%BD%E5%8A%A8%E6%80%81%E5%BA%93%E5%8F%8A%E5%BA%93%E5%86%85c%E9%A3%8E%E6%A0%BC%E6%96%B9%E6%B3%95\/"
},
"\/tags\/qml\/": {
"title": "Qml",
"tags": [],
"content": "",
"url": "\/tags\/qml\/"
},
"\/tags\/qt5\/": {
"title": "Qt5",
"tags": [],
"content": "",
"url": "\/tags\/qt5\/"
},
"\/post\/qt5-cmake-qml%E5%A4%9A%E8%AF%AD%E8%A8%80%E5%AE%9E%E8%B7%B5\/": {
"title": "qt5-cmake-qml多语言实践",
"tags": ["Qt5","CMake","Qml","Translator",],
"content": "1. 编码 1.1 CMake 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 # 添加引用Qt的名为LinguistTools的component find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Core LinguistTools) find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Gui Widgets Quick Qml QuickControls2 LinguistTools) #定义需要生成的.ts ,放到TS_FILES这个cmake变量中 set(TS_FILES src/translations/zh_CN.ts src/translations/en_US.ts src/translations/ja_JP.ts ) #扫描这dv_app,dv_camera, dv_common这三个目录,把需要翻译的字符串扫描进.ts文件中 qt5_create_translation(QM_FILES ${CMAKE_SOURCE_DIR}/dv_app ${CMAKE_SOURCE_DIR}/dv_camera ${CMAKE_SOURCE_DIR}/dv_common ${TS_FILES} ) #在生成 xxx可执行文件时,把执行扫描生成.ts ,再把.ts生成.qm翻译二进制文件 add_library(xxx SHARED ${TS_FILES} #这一行只是为了在qt creator中看到.ts文件 ${QM_FILES} ) #复制翻译文件.qm到 生成目录的translations文件夹 add_custom_command(TARGET xxx POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy ${QM_FILES} $\u0026lt;TARGET_FILE_DIR:dv_common\u0026gt;/translations ) 1.2. c++ 1.2.1 简单封装加载qm翻译文件的逻辑 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 bool cpp_utility::load_language_qm(QString filePath) { try { std::shared_ptr\u0026lt;QTranslator\u0026gt; translator = prism::Container::get()-\u0026gt;resolve_object\u0026lt;QTranslator\u0026gt;(); if(!translator) { if(!QCoreApplication::removeTranslator(translator.get())) { std::shared_ptr\u0026lt;QTranslator\u0026gt; empty ; prism::Container::get()-\u0026gt;register_instance(empty); } else throw std::string(\u0026#34;卸载多语言翻译器失败\u0026#34;); } if(!filePath.isEmpty()) { std::shared_ptr\u0026lt;QTranslator\u0026gt; tr = std::make_shared\u0026lt;QTranslator\u0026gt;(); if(tr-\u0026gt;load(filePath)) { if(QCoreApplication::installTranslator(tr.get())) prism::Container::get()-\u0026gt;register_instance(tr); else throw std::string(\u0026#34;安装多语言翻译器失败\u0026#34;); } else throw std::string(\u0026#34;加载多语言文件失败:\u0026#34;) + filePath.toStdString(); } else { qInfo()\u0026lt;\u0026lt;\u0026#34;正在卸载非中文的多语言翻译器\u0026#34;; } //通知qml engine,如果有的话 std::shared_ptr\u0026lt;QQmlApplicationEngine\u0026gt; engine = Container::get()-\u0026gt;resolve_object\u0026lt;QQmlApplicationEngine\u0026gt;(); if(engine) engine-\u0026gt;retranslate(); } catch(std::string \u0026amp; mes) { qCritical()\u0026lt;\u0026lt; QString::fromStdString(mes); return false; } return true; } 上面这个c++方法,即被放到ioc容器中,又被注册到qml engine,所以c++和qml中都可以直接调用,只需要传入一个qm的文件路径,就可以加载,并在加载完成后通知qml engine刷新ui 。\n通知的这段逻辑需要qt版本至少qt 5.10\n1.3 调用 程序加载时 用户选择切换语言时 3.怎么让项目中的字符串被扫描到 .ts文件中 在qml中\n1 2 3 Text{ text: qsTr(\u0026#34;惨惨字字,将被扫描到.ts文件中,任人翻译\u0026#34;) } 在c++中\n1 qDebug() \u0026lt;\u0026lt; QObject::tr(\u0026#34;卑微字字,将被扫描到.ts文件中,任人翻译\u0026#34;); 4. 怎么使用qt 小工具翻译 没有翻译完,软件也不会报错,只是会在切换语言时,不会变更相关的翻译\n例如有一个需要翻译的文字[\u0026ldquo;黄滨滨\u0026rdquo;]中文翻译为[\u0026ldquo;靓仔\u0026rdquo;],英语翻译为[\u0026ldquo;huangbinbin\u0026rdquo;]完了,但是日语没有翻译,\n这时中文情况下显示[\u0026ldquo;靓仔\u0026rdquo;],\n切换英文后应会显示[\u0026ldquo;huangbinbin\u0026rdquo;]\n切换日语后,由于 没有翻译,会导致仍然显示[\u0026ldquo;huangbinbin\u0026rdquo;],\n并不是像你直觉的那样,仍然显示[\u0026ldquo;黄滨滨\u0026rdquo;]\n*. 注意事项 *.1 首次生成 .ts,需要设置一下语言 第三行中, language需要正确设置为BCP 47规范中的\u0026lt;小写国家\u0026gt;_\u0026lt;大写地区\u0026gt;,之后生成的.qm文件,才能被加载到qt 程序中,否则会报错.\n常用的语言\n英语 en_US 中文 zh_CN 日语 ja_JP 更多参阅 Language Tags (BCP 47)\n",
"url": "\/post\/qt5-cmake-qml%E5%A4%9A%E8%AF%AD%E8%A8%80%E5%AE%9E%E8%B7%B5\/"
},
"\/tags\/translator\/": {
"title": "Translator",
"tags": [],
"content": "",
"url": "\/tags\/translator\/"
},
"\/tags\/qt-creator\/": {
"title": "Qt Creator",
"tags": [],
"content": "",
"url": "\/tags\/qt-creator\/"
},
"\/post\/ubuntu16.04%E7%BC%96%E8%AF%91qt5.15%E7%9A%84qt-creator\/": {
"title": "ubuntu16.04编译qt5.15的qt creator",
"tags": ["Qt5","Qt Creator",],
"content": "1 为什么需要自己编译 相机软件 使用最新版本的qt 开源版本 5.15.2,以及cmake,qml来组织工程,\n遇到几个问题\n1.1 Qt5.15.2预装的Qt Creator不支持在Ubuntu16.04环境运行 qt 5.15.2预装版本的qt creator是在ubuntu20下使用qt 6和gcc9编译的,ubuntu16.04 下不能使用,我曾尝试升级ubuntu16的libc,最终把ubuntu16 环境搞崩溃,重启之后很多系统软件都不能运行了,apt升级也升级不了,依赖树产生问题。除非你添加18或20的源才能正确的升级,所以不得不重做开发环境, 先用qt 5.12.6预装的qt creator 4.10.2进行开发\n1.2 Qt Creator 4.10.2的问题 加载Qt 5.15.2的qml 控制库,很多误报,不管是在cmake运行,还是IDE里的代码提示,都很多噪音\ncmake 子模块代码变更后, 检测不到变更,只能整个工程rebuild才能正常\ncmake功能支持比较粗糙, 设置cmake比较不方便\ncmake循环依赖问题(在新的版本上不是问题)\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 CMake Warning at CMakeLists.txt:176 (add_executable): Cannot generate a safe runtime search path for target tst_dv_camera because there is a cycle in the constraint graph: dir 0 is [/home/deepvision/source/repos/dv_app_solution/build-dv_camera-Desktop_Qt_5_15_2_GCC_64bit-Debug] dir 4 must precede it due to runtime library [libdv_zlog.so] dir 5 must precede it due to runtime library [libdv_zlog.so] dir 1 is [/home/deepvision/source/repos/dv_app_solution/vcpkg_installed/x64-linux-dynamic/debug/lib] dir 2 must precede it due to runtime library [librc_genicam_api.so.2.6] dir 4 must precede it due to runtime library [libdv_zlog.so] dir 5 must precede it due to runtime library [libdv_zlog.so] dir 2 is [/home/deepvision/source/repos/dv_app_solution/vcpkg_installed/x64-linux-dynamic/lib] dir 0 must precede it due to runtime library [libdv_camera.so] dir 1 must precede it due to runtime library [libminizip.so] dir 4 must precede it due to runtime library [libdv_zlog.so] dir 3 is [/home/deepvision/Qt/5.15.2/gcc_64/lib] dir 4 is [/home/deepvision/source/repos/zlog/lib/linux] dir 5 must precede it due to runtime library [libdv_zlog.so] dir 5 is [/home/deepvision/source/repos/dv_app_solution/vcpkg_installed/x64-linux-dynamic/share/zlog/../../lib] dir 0 must precede it due to runtime library [libdv_camera.so] dir 1 must precede it due to runtime library [libminizip.so] dir 4 must precede it due to runtime library [libdv_zlog.so] 1.3 尝试过的其他解决方法 我到github release下载了多个qt creator的版本,发现即使是qt creator 4.15版本,也是依赖了libc 2.29(奇怪的是最新版的qt creaotr只依赖了libc 2.28), 远超出ubuntu16 的2.23\n可以用这条指令查看libc的版本\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 deepvision@ubuntu:/$ strings /lib/x86_64-linux-gnu/libc.so.6 |grep GLIBC GLIBC_2.2.5 GLIBC_2.2.6 GLIBC_2.3 GLIBC_2.3.2 GLIBC_2.3.3 GLIBC_2.3.4 GLIBC_2.4 GLIBC_2.5 GLIBC_2.6 GLIBC_2.7 GLIBC_2.8 GLIBC_2.9 GLIBC_2.10 GLIBC_2.11 GLIBC_2.12 GLIBC_2.13 GLIBC_2.14 GLIBC_2.15 GLIBC_2.16 GLIBC_2.17 GLIBC_2.18 GLIBC_2.22 GLIBC_2.23 GLIBC_PRIVATE GNU C Library (Ubuntu GLIBC 2.23-0ubuntu11.3) stable release version 2.23, by Roland McGrath et al. 我也试过单独为qt creator编译高版本的libc,放在单独的目录,结果发现,ubunt16.04下,最多只能调用libc 2.27的版本保证不会崩溃, 一旦调用了2.28或2.29就会崩溃\nlibc 可以通用这个脚本编译,记录一下,如果将来有需要用到的话\n1 2 3 4 5 6 7 8 9 10 #!bash cd /home/deepvision/lib/ wget http://ftp.gnu.org/gnu/glibc/glibc-2.29.tar.gz tar zxvf glibc-2.29.tar.gz cd glibc-2.29 mkdir build cd build ../configure --prefix=/opt/glibc-2.29 make sudo make install 由于以上几个方法都没有成功,所以决定自己编译qt creator\n2 编译 由于我虚拟机空间不足,我后来卸载掉了qt 6,所以查看qt creator源码后,我使用qt 5编译的最后一个版本qt creator 8.\n参照readme 进行编译, 记录一下编译中遇到的问题和解决步骤\nqt creator 最新版本目前是qt creator 12,但其实8已经算是很新的版本了\n我查看2022年3月份时我拉取的qt creator代码,那时最新的开发分支才7 beta\n所以8也算是比较新的版本\n2.1 编译LLVM 2.1.1 正常流程 克隆代码\n1 git clone -b release_130-based --recursive https://code.qt.io/clang/llvm-project.git 创建目录\ncmake生成\n1 2 3 4 5 6 cmake \\ -D CMAKE_BUILD_TYPE=Release \\ -D LLVM_ENABLE_RTTI=ON \\ -D LLVM_ENABLE_PROJECTS=\u0026#34;clang;clang-tools-extra\u0026#34; \\ -D CMAKE_INSTALL_PREFIX= 自己指定安装目录 \\ ../llvm-project/llvm 构建\n1 cmake --build . --target install 2.2.2 cmake失败问题解决 2.2.2.1 libxml2 找不到 sudo apt install dev-libxml2 安装\ncmake生成中加入参数 -D CMAKE_PREFIX_PATH=\u0026quot;/usr/\u0026quot;\n1 2 3 4 5 6 7 cmake \\ -D CMAKE_BUILD_TYPE=Release \\ -D LLVM_ENABLE_RTTI=ON \\ -D LLVM_ENABLE_PROJECTS=\u0026#34;clang;clang-tools-extra\u0026#34; \\ -D CMAKE_INSTALL_PREFIX= \u0026#34;自己指定安装目录,后面qt creator执行cmake时会用到\u0026#34; \\ -D CMAKE_PREFIX_PATH=\u0026#34;/usr/\u0026#34; ../llvm-project/llvm 2.2.2.1 config.guess: 71: Syntax error: word unexpected (expecting \u0026ldquo;in\u0026rdquo;) 参照 llvm github issue\nconfig.guess Syntax error: · Issue #52740 · llvm/llvm-project (github.com)\n应该config.guess文件格式问题,准确说是换行符问题\n因为我在windows上拉了代码后,上传到公司ftp服务器上,再在linux下载代码导致的问题\n如果直接在linux拉代码应该不会有这个问题\n解决方法 也简单,issue中提到使用dos2unix 对这个配置文件格式化成unix的换行符\n1 2 sudo apt install dos2unix doc2unux path/to/config.guess 2.2.2.2 其他注意事项 llvm编译会占用大量磁盘空间和内存,提前准备好防止编译失败要重新编译\n2.2 编译qt creator 克隆源码后切换到你想要编译的分支\n例如我想用qt 5 编译v8.0.2 (最后一个qt5编译的qt creaotr版本)\n创建 build文件夹,进入文件夹生成cmake\n1 cmake -DCMAKE_BUILD_TYPE=Debug -G Ninja \u0026#34;-DCMAKE_PREFIX_PATH=/home/deepvision/Qt/5.15.2/gcc_64;../../llvm/install\u0026#34; ../qt-creator 执行编译\n1 cmake --build . 执行安装\n1 cmake --install . --prefix ../ubuntu16.04-qtcerator8.0.2 然后修改qt creator的启动脚本,在脚本后面导致 ld_library_path变量之前,把qt 5的路径加进去\n1 2 LD_LIBRARY_PATH=/home/deepvision/Qt/5.15.2/gcc_64/lib:$LD_LIBRARY_PATH} export LD_LIBRARY_PATH 就可以用qtcreator.sh启动程序了\n如果用在线安装器安装了qt 5.15会有 ~/.local/share/applications/org.qt-project.qtcreator.desktop配置,修改为刚才修改好的启动脚本就可以从菜单或任务栏打开编译好的qt creator了\n1 2 3 4 5 6 7 8 9 10 11 deepvision@ubuntu:~$ cat ~/.local/share/applications/org.qt-project.qtcreator.desktop [Desktop Entry] Type=Applicatio Exec=\u0026#34;/home/deepvision/source/repos/qtcreator/ubuntu16.04-qtcerator8.0.2/bin/qtcreator.sh\u0026#34; Name=Qt Creator GenericName=The IDE of choice for Qt development. Icon=QtProject-qtcreator StartupWMClass=qtcreator Terminal=false Categories=Development;IDE;Qt; MimeType=text/x-c++src;text/x-c++hdr;text/x-xsrc;application/x-designer;application/vnd.qt.qmakeprofile;application/vnd.qt.xml.resource;text/x-qml;text/x-qt.qml;text/x-qt.qbs; 3 解决的问题验证 左边是qt creator8 右边是qt creator 4.10\nqml 和cmake都不误报了\nqt creator8已上传到dv-rd\n",
"url": "\/post\/ubuntu16.04%E7%BC%96%E8%AF%91qt5.15%E7%9A%84qt-creator\/"
},
"\/tags\/asset\/": {
"title": "Asset",
"tags": [],
"content": "",
"url": "\/tags\/asset\/"
},
"\/tags\/binaries\/": {
"title": "Binaries",
"tags": [],
"content": "",
"url": "\/tags\/binaries\/"
},
"\/post\/vcpkg%E8%B5%84%E4%BA%A7%E7%BC%93%E5%AD%98%E4%B8%8E%E4%BA%8C%E8%BF%9B%E5%88%B6%E7%BC%93%E5%AD%98\/": {
"title": "vcpkg资产缓存与二进制缓存",
"tags": ["VCPKG","Asset","Binaries",],
"content": "0x01 背景 翻墙用了一段时间的vcpkg了,在项目中使用vcpkg安装管理了一众开源框架\nrange-v3 catch2 boost fmt opencv4 体验感很好,除了两个问题点\n需要翻墙才能安装依赖 就算其他同事也翻墙使用vcpkg,那上面的库他需要从克隆代码,下载二进制,编译,重新来一次,很耗时 于是围绕这两个问题挖掘msdn文档,用这个markdown记录探索过程\n0x02 坑 为了解决第1个问题,我把这几个库,以及vcpkg本身,从github上迁移到公司内网的forgejo服务器上,然后尝试修改vcpkg中,包的配置,试图让vcpkg改为从公司内网下载代码。像catch2, fmt, range-v3这几个库,结构比较简单,用这种方式确实成功了,工作量也不大,维护也简单,只需要修改vcpkg源码中这几个库的url,从github改为公司的forgejo路径就行了。但是当我在处理opencv4和boost的时候,我发现光是boost就有一百八十几个git submodule , 不过这还不足以让我推翻这个方法, 因为我觉得可以通过脚本+ forgejo 的api服务+ access token就能用脚本批量的创建库存,上传代码。 直到我尝试修改opencv的vcpkg中的配置,我发现这些超大规模的大型开源库,并不只是用git clone拉取编译所需要的依赖,有的是通过http下载tar包,zip包。并且有时候并不是opencv4这种顶层项目下载的,而是它的子模块,或子模块的模块下载的。 这样一来,即使把源码都迁移到内网的forgejo上了,仍然会有一些二进制的包,没有在内网之中。 所以这个方案行不通\n0x03 最终解 在msdn上把vcpkg相关的文档精读了一下,我发现vcpkg不管是下载的文件,还是编译的文件,都可以缓存 ,而且可以在网络位置上共享。我测试了一下,把两台电脑设置了同样的网络位置的缓存目录,在一台电脑上翻墙,编译boost1.83.0和opencv4.5.5,在另一台电脑上,确实不翻墙的情况下也可以用vcpkg安装boost 1.83.0以及opencv 4.5.5,不需要重新从github下载,。当然还是需要从网络位置下载的,缓存几百兆的二进制文件如果不是在杭州,下载也是需要时间的,但相对来说快了不少。\n下面说说怎么配置\n1.研发的nas目录 dv-rd中创建缓存目录 vcpkg_asset_cache用于缓存安装过程中下载二进制\nvcpkg_binary_cache 用于缓存编译时生成的二进制包\n2.配置环境变量,告诉vcpkg从这两目录读写缓存 windows:\nVCPKG_BINARY_SOURCES clear;files,Z:/sw/vcpkg_binary_cache/,readwrite\nX_VCPKG_ASSET_SOURCES clear;x-azurl,file://Z:/sw/vcpkg_asset_cache/,,readwrite\nlinux:由于同办法想windows一样把网络位置映射成为磁盘,所以用nfs挂载的方式\n1 mount -t nfs 10.2.1.11:/data/syncthing_data /xxxx/xxx 1 2 export X_VCPKG_ASSET_SOURCES=\u0026#34;clear;x-azurl,file:/nfs/vcpkg_cache/vcpkg_cache_linux/asset/,,readwrite\u0026#34; export VCPKG_BINARY_SOURCES=\u0026#34;clear;files,/nfs/vcpkg_cache/vcpkg_cache_linux/binary/,readwrite\u0026#34; 这里也可以配置网络位置只读,加一个本地路径读写,我电脑空间比较紧张,就只配置了远程的了。\n然后使用vcpkg安装的时候,就会自动从这里面找缓存了。如果没有的缓存,只需要在翻墙的电脑上用vcpkg安装一下,就会自动上传了。\n",
"url": "\/post\/vcpkg%E8%B5%84%E4%BA%A7%E7%BC%93%E5%AD%98%E4%B8%8E%E4%BA%8C%E8%BF%9B%E5%88%B6%E7%BC%93%E5%AD%98\/"
},
"\/post\/%E5%88%9B%E5%BB%BAvcpkg%E7%A7%81%E6%9C%89%E4%BB%93%E5%BA%93\/": {
"title": "创建vcpkg私有仓库",
"tags": ["CMake","VCPKG",],
"content": "0.前言 最近在做公司一个新的工程,对标海康的MVS, 基于GENICAM通用协议,目标是适配市场上众多的厂牌的基于GENICAM协议的相机。 由于预计到会引用很多开源项目,且会围绕相机扩展很多公司的业务功能。所以我重新设计了新软件总体的架构,由原来的qmake改为cmake,并使用插件式开发。这即方便于引用其他开源框架,也可以使公司的代码模块化,更容易维护以及复用。但在众多开源模块与公司的模块,以及公司的不同的项目之间要如何处理依赖关系?如何科学的减少重复的工作?满地都是cmake + git submodule,我抬头却看到了vcpkg私有仓库。\n1.随便聊聊 1.1 包管理器 python有pip, c#有nuget , java有maven, 基本上每一个语言都有一个亲妈背书的包管理器,可以递归解决项目依赖,但是c++没有。\n语言特性太复杂, 编译选项不一样,同样的代码生成的程序就可能不能一起工作 模块元编程只能以源码方式发布,而不是二进制 ABI兼容性问题导致需要根据cpu架构发布不同的二进制文件版本 基于上面的问题以及等等,导致c++的包管理不好实现,长久以来,c++开发者如果需要采用其他开源项目,通常需要手动把 其他c++项目的源码,静态链接库,动态链接库集成到自己的项目里,在集成的过程中,往往不是顺利的,可能需要处理依赖关系,环境问题,编译选项问题。\n1.2 Cmake cmake的出现简化了这个过程, 它是一款跨平台c++项目的管工具, 可以很方便的把cmake工程集成到另一个cmake工程中,然后指定生成器,生成各种类型的构建工具的项目,如ninja,visual studio 等,,并且它可以枚举项目的依赖项目,但是它没有版本控制的能力,所以通常与git联合使用 。 目前基本主流的IDE都集成了cmake和git,并且c++开源项目大多数也是以cmake的方式组织工程,因此使用cmake+git可以方便的集成开源项目。这也是我想用cmake的原因之一,除此之外,只要项目没有相互依赖,不管所处何目录结点,cmake都支持并行构建(qmkae子目录构建完才会构建父目录),可以更好的利用多核计算机的性能。所以cmake已经足够好了,为什么还需要vcpkg?\n 想像一下,你有一个巨大的项目,依赖关系比较复杂,如果说顶层项目是第0层,某个第1层的项目A依赖了开源库D,另一个第1层的项目B的第2层项目BA也依赖了项目D, 你如果不修改优化工程结构,你的项目目录里将存在两份D的源码,并且如果修改了其中一份D的代码,需要把它提交给他的父项目们,直到第0层,然后再到另一份D代码处,拉代码后,再一层一层的提交到第0层。这样别人拉取你的第0层代码时,才能简单的git submodule update --init --recursive,克隆下来。而项目中可能会出现多个D项目这样的存在,这意味着即使使用git submodule,也需要做很多重复工作 。 而vcpkg可以解决这种问题,甚至允许控制每份D的代码版本不一样。\n1.3 vcpkg vcpkg是微软推出的c++ 源码级别的跨平台包管理器。虽然vc开头,但不仅限于visual c++, 它基本上=git + cmake,但扩展了包管理器应该有的功能,可以方便的解决项目依赖问题,也可以方便切换依赖项目的ABI,Cpu架构,目标平台。 vcpkg有经典模式和清单模式两种模式。\n经典模式\n允许安装D到系的某个目录,然后所有层级的项目都引用这个 D目录里的cmake module.\n清单模式\n允许在每个层级cmakelist.txt旁边创建一个vcpkg.json配置文件,把需要的包配置在里面,然后执行 vcpkg install --recursive 递归安装依赖包。\n可以看出 清单模式可以解决我在1.2第二段落中描述的问题。但拿来就用还是有一些局限性。以下两种情况\n需要针对性修改某个项目中某个vcpkg安装的开源库依赖,编译选项或bugfix,但其不希望他项目被影响 希望把公司的项目需要集成到vcpkg,享受包管理器带来的便利,又不想开源 何以解忧,唯有搭建自己的vcpkg 仓库,这也是本文档想讲的内容(终于酝酿好氛围)\n 除此这外,vcpkg相对于公司现在的项目管理,还有一个优点,就是减少二进制文件的提交,理论上只提交源码就行,install的时候,vcpkg会根据配置 使用git签出正确分支的源码,构建为本地的二进制文件\n2. 搭建公司内私有vcpkg仓库 目标是把开源的项目,如open cv之类的 以及公司的软件模块发布到私有仓库上。理论上,开源的库如果没有额外的修改,直接用vcpkg现有的库就行了,但因为我们在墙内,如果想提升vcpkg安装的速度, 我们最好还是把我们项目中用到的开源库迁移到公司内部git平台上,设置为镜像仓库,定时从github同步。 因此本小节,主要拆分为以下三个步骤\n搭建vcpkg 私有registry 把项目中使用到的开源项目同步到公司git 平台上 把公司的git 平台上的repos登记到私有的vcpkg registry 2.1 搭建vcpkg 私有仓库 参考\n创建一个空的git repository\nmkdir -p ~/source/repos/prism_registry \u0026amp;\u0026amp; cd ~/source/repos/prism_registry git init . 创建一个*** baseline.json*** 文件,放在根目录 versions目录\n1 2 mkdir versions \u0026amp;\u0026amp; cd versions vim baseline.json 写入 下面的json内容\n1 2 3 { \u0026#34;default\u0026#34;: {} } 2.2 添加库到vcpkg私有仓库 这里主要演示添加github库 和非github库两种方式\n虽然我们应该主要使用第二种方式\n2.2.1 添加github仓库示例 不管想添加到vcpkg仓库的项目是不是来自github,都需要执行这两步\n添加port 添加version 2.2.1.1 添加port 创建ports目录,在这里面编辑你想放到你vcpkg私有仓库的信息\n例如我想把我的prism 库放到私有库,就在ports目录中再创建一个prism的文件夹\nprism是一个只有头文件,且只依赖c++标准库的 c++库\n1 mkdir -p ~/source/repos/prism_registry/ports/prism \u0026amp;\u0026amp; cd ~/source/repos/prism_registry/ports/prism 然后在prism目录中创建文件 vcpkg.json ,把prism初始版本的信息写进去,因为我这个库没有依赖,所以没有写依赖项,如果有,在json里加\u0026quot;dependencies \u0026ldquo;:[]就行\n1 2 3 4 5 6 { \u0026#34;name\u0026#34;: \u0026#34;prism\u0026#34;, \u0026#34;version\u0026#34;: \u0026#34;1.0.0\u0026#34;, \u0026#34;description\u0026#34;: \u0026#34;a static reflect library of c++\u0026#34;, \u0026#34;homepage\u0026#34;: \u0026#34;https://github.com/nocanstillbb/prism\u0026#34; } 同时把vcpkg.json也提交到prism存储库根目录\n然后再在 ~/source/repos/prism_registry/ports/prism 中创建一个空的protfile.cmake\n做到这一步,你的文件目录应该是这样的\n../ prism_registry/ | .git/ | ports/ | | prism/ | | | protfile.cmake | | | vcpkg.json | versions/ | | baseline.json\n测试vcpkg能不能正常识别这个json文件\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 $ cd ~/source/repos/prism_registry/prism_registry $ vcpkg install prism --overlay-ports=ports/prism Computing installation plan... The following packages will be built and installed: prism:x64-windows -\u0026gt; 1.0.0 -- D:\\source\\repos\\prism_registry/ports/prism Detecting compiler hash for triplet x64-windows... Restored 0 package(s) from C:\\Users\\kokbi\\AppData\\Local\\vcpkg\\archives in 155 us. Use --debug to see more details. Installing 1/1 prism:x64-windows... Building prism:x64-windows... -- Installing port from location: D:\\source\\repos\\prism_registry/ports/prism CMake Error at scripts/ports.cmake:113 (message): Port is missing portfile: D:/source/repos/prism_registry/ports/prism/portfile.cmake error: building prism:x64-windows failed with: BUILD_FAILED Elapsed time to handle prism:x64-windows: 27 ms Please ensure you\u0026#39;re using the latest port files with `git pull` and `vcpkg update`. Then check for known issues at: https://github.com/microsoft/vcpkg/issues?q=is%3Aissue+is%3Aopen+in%3Atitle+prism You can submit a new issue at: https://github.com/microsoft/vcpkg/issues/new?title=[prism]+Build+error\u0026amp;body=Copy+issue+body+from+D%3A%2Fsource%2Frepos%2Fvcpkg%2Finstalled%2Fvcpkg%2Fissue_body.md 根据打印出来的信息,可以知道vcpkg安装时正确定位到port/prism/vcpkg.json了,只是protfile.cmake中是空的才导致安装失败了\n接下来添加protfile.cmake内容\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 vcpkg_from_github( OUT_SOURCE_PATH SOURCE_PATH REPO nocanstillbb/prism REF e4cb55e02bfad354ba86d70f5555fe12d8f70d10 SHA512 a9eebc68ed045ccac650950b5ba8b1f172fde8244856c4b5e0ac38977711f5e3bbfc305ec831c47787e4994d7faff4ef01bf525e589b140dff8a66b10febb6e6 HEAD_REF master ) #https://learn.microsoft.com/en-us/vcpkg/examples/packaging-github-repos vcpkg_cmake_configure( SOURCE_PATH \u0026#34;${SOURCE_PATH}\u0026#34;) vcpkg_cmake_install() #vcpkg_cmake_config_fixup(CONFIG_PATH lib/cmake/${PORT}) #vcpkg_cmake_config_fixup() vcpkg_fixup_pkgconfig() vcpkg_copy_pdbs() file(REMOVE_RECURSE \u0026#34;${CURRENT_PACKAGES_DIR}/debug\u0026#34;) file( INSTALL \u0026#34;${SOURCE_PATH}/LICENSE\u0026#34; DESTINATION \u0026#34;${CURRENT_PACKAGES_DIR}/share/${PORT}\u0026#34; RENAME copyright) file( INSTALL \u0026#34;${SOURCE_PATH}/usage\u0026#34; DESTINATION \u0026#34;${CURRENT_PACKAGES_DIR}/share/${PORT}\u0026#34;) 其中的shar512是下载的源码归档包的sha512校验和的值,可以自己下载下来校验,填写进去,也可以先写一个0,安装时会安装失败,但会把真正的值打印出来。\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 $ vcpkg install prism --overlay-ports=ports/prism Computing installation plan... The following packages will be built and installed: prism:x64-windows -\u0026gt; 1.0.0 -- D:\\source\\repos\\prism_registry/ports/prism Detecting compiler hash for triplet x64-windows... -- Using HTTP(S)_PROXY in environment variables. Restored 0 package(s) from C:\\Users\\kokbi\\AppData\\Local\\vcpkg\\archives in 171 us. Use --debug to see more details. Installing 1/1 prism:x64-windows... Building prism:x64-windows... -- Installing port from location: D:\\source\\repos\\prism_registry/ports/prism -- Downloading https://github.com/nocanstillbb/prism/archive/59fc49ca76338364c6351475a4ad5d80578f625.tar.gz -\u0026gt; nocanstillbb-prism-59fc49ca76338364c6351475a4ad5d80578f625.tar.gz... [DEBUG] To include the environment variables in debug output, pass --debug-env [DEBUG] Trying to load bundleconfig from D:\\source\\repos\\vcpkg\\vcpkg-bundle.json [DEBUG] Failed to open: D:\\source\\repos\\vcpkg\\vcpkg-bundle.json [DEBUG] Bundle config: readonly=false, usegitregistry=false, embeddedsha=nullopt, deployment=Git, vsversion=nullopt [DEBUG] Metrics enabled. [DEBUG] Feature flag \u0026#39;binarycaching\u0026#39; unset [DEBUG] Feature flag \u0026#39;compilertracking\u0026#39; unset [DEBUG] Feature flag \u0026#39;registries\u0026#39; unset [DEBUG] Feature flag \u0026#39;versions\u0026#39; unset Downloading https://github.com/nocanstillbb/prism/archive/59fc49ca76338364c6351475a4ad5d80578f625.tar.gz [DEBUG] Trying to hash D:\\source\\repos\\vcpkg\\downloads\\nocanstillbb-prism-59fc49ca76338364c6351475a4ad5d80578f625.tar.gz.18080.part [DEBUG] D:\\source\\repos\\vcpkg\\downloads\\nocanstillbb-prism-59fc49ca76338364c6351475a4ad5d80578f625.tar.gz.18080.part has hash f03d4c918e5bda46cc1aa129edd192a74274b9c07f6634dedec47b986aad6e0e8689b019620c57507e07fea36adaf9dfbff9b41fcc166da5f0748a3a9d491529 error: Failed to download from mirror set error: File does not have the expected hash: url: https://github.com/nocanstillbb/prism/archive/59fc49ca76338364c6351475a4ad5d80578f625.tar.gz File: D:\\source\\repos\\vcpkg\\downloads\\nocanstillbb-prism-59fc49ca76338364c6351475a4ad5d80578f625.tar.gz.18080.part Expected hash: 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 Actual hash: f03d4c918e5bda46cc1aa129edd192a74274b9c07f6634dedec47b986aad6e0e8689b019620c57507e07fea36adaf9dfbff9b41fcc166da5f0748a3a9d491529 把Actual hash复制下来写到portfile.cmake 重新测试安装\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 $ vcpkg install prism --overlay-ports=ports/prism Computing installation plan... The following packages will be built and installed: prism:x64-windows -\u0026gt; 1.0.0 -- D:\\source\\repos\\prism_registry\\ports/prism Detecting compiler hash for triplet x64-windows... -- Using HTTP(S)_PROXY in environment variables. Restored 1 package(s) from C:\\Users\\kokbi\\AppData\\Local\\vcpkg\\archives in 44 ms. Use --debug to see more details. Installing 1/1 prism:x64-windows... Elapsed time to handle prism:x64-windows: 30.2 ms Total install time: 30.3 ms The package prism usage: find_package(prism::prism) target_include_directories(main INTERFACE ${PRISM_INCLUDE_DIRS}) more infomation: https://github.com/nocanstillbb/prism 好的项目,安装后不会提示警告和错误,如果这里有提示警告的话,就会提示一个错误,提示你如果要让库被收录到vcpkg的优选目录,要更正portfile.cmake消除这些错误和警告。\n正常这里还应该在安装之前执行一下项目的单元测试,这个先不管,不是必须的。接下来测试一下刚才安装的库能不能正常使用,先用vcpkg list查看信息\n1 2 PS D:\\Downloads\u0026gt; vcpkg list |findstr prism prism:x64-windows 1.0.0 a static reflect library of c++ 然后我把我项目中prism源码目录移除后,用ide查看引用时,已经正确定位到vcpkg的安装目录了\n编译也没有问题,说明成功了。\n2.2.1.2 添加versions 接下来添加version相关的信息\n在prism_registry/versions 中创建以你的存储库名称头一个字母+ “-”开关的目录, 添加的库是prism,所以创建的目录为p-\n1 2 3 mkdir -p ~/source/repos/prism_registry/versions/p- cd ~/source/repos/prism_registry/versions/p- vim prism.json 创建prism.json文件写入\n1 2 3 4 5 6 7 8 { \u0026#34;versions\u0026#34;: [ { \u0026#34;version\u0026#34;: \u0026#34;1.0.0\u0026#34;, \u0026#34;git-tree\u0026#34;: \u0026#34;\u0026#34; } ] } git-tree 的值如此获取\n1 2 git add ports/prism git commit -m \u0026#34;[prism] new port\u0026#34; 单独提交prism的ports目录, 提交内容以[portName]开头,如果你想要合并到vcpkg master的话,这个是规则之一\n然后 先不着急提交\n1 git rev-parse HEAD:prots/prism 获取git分支HEAD上的git 目录的分支号,写入/versions/p-/prism.json中,刚才放空的git-tree\n然后编辑 versions/baseline.json\n1 2 3 4 5 6 7 8 { \u0026#34;default\u0026#34;: { \u0026#34;prism\u0026#34;: { \u0026#34;baseline\u0026#34;: \u0026#34;1.0.0\u0026#34;, \u0026#34;port-version\u0026#34;: 0 } } } 然后把prism_registry 提交推送到公司forgejo上\n2.2.2 添加非github的库示例 上面的示例是vcpkg团队博客上的示例,所以用 vcpkg_from_github导入的,而我们私有的代码库是放在公司forgejo服务器上的,所以应该探索一种通用的方法,我查看vcpkg源码里的Ports,看到有的库使用的是vcpkg_from_git\n1 2 3 4 5 6 7 vcpkg_from_git( OUT_SOURCE_PATH LSS_SOURCE_PATH URL https://chromium.googlesource.com/linux-syscall-support REF 7bde79cc274d06451bf65ae82c012a5d3e476b5a ) file(RENAME \u0026#34;${LSS_SOURCE_PATH}\u0026#34; \u0026#34;${SOURCE_PATH}/src/third_party/lss\u0026#34;) 和带git补丁的写法\n1 2 3 4 5 6 7 vcpkg_from_git( OUT_SOURCE_PATH SOURCE_PATH URL https://sourceware.org/git/elfutils REF ca4a785fc3061c7d136b198e9ffe0b14cf90c2be #elfutils-0.186 PATCHES configure.ac.patch ) 除此之外没有什么区别, 反而少了sha512 检验的步骤,更简单了\n2.2.3 用vcpkg的简化添加version步骤 添加完port 并提交到git后,可以使用这个方式简单的更新version和baseline.json\n如果是在微软vcpkg 叉出来的分支上,添加了使用git添加了ports/prism后,就可以使用x-add-version --all来生成 version/p-/prism.json 以及 version/baseline.json中的prism的条目\n如果不是vcpkg叉出来的分支,而是公司的私有库的话,参照教程:使用 Git 将包发布到私有 vcpkg 注册表 |Microsoft学习 加点参数也可以使用这个工具\n1 vcpkg --x-builtin-ports-root=./ports --x-builtin-registry-versions-dir=./versions x-add-version --all --verbose 了解这个工具,如果需要ci集成的话,就会方便很多\n2.3 在项目中使用私有的vcpkg仓库 首先我在公司的forgejo上开了一个空的存储库,把 vcpkg私有仓库上传上去\n然后在测试项目的根目录创建vcpkg-configuration.json\n基于github的vcpkg ,扩展我们私有的库, 这种是比较合理的,如果需要对github上的库做定制化修改,只需要把库迁移到内网就行\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 { \u0026#34;default-registry\u0026#34;: { \u0026#34;kind\u0026#34;: \u0026#34;git\u0026#34;, \u0026#34;repository\u0026#34;: \u0026#34;https://github.com/microsoft/vcpkg\u0026#34;, \u0026#34;baseline\u0026#34;: \u0026#34;0da9fe986d07603da71f35ef8dd55512154a64bd\u0026#34; }, \u0026#34;registries\u0026#34;: [ { \u0026#34;kind\u0026#34;: \u0026#34;git\u0026#34;, \u0026#34;repository\u0026#34;: \u0026#34;http://10.1.8.8/AL/dv-vcpkg\u0026#34;, \u0026#34;baseline\u0026#34;: \u0026#34;36f0728c26661f67fca9b583bd99084e9491be93\u0026#34;, \u0026#34;packages\u0026#34;: [ \u0026#34;prism\u0026#34; ] } ] } baseline是vcpkg仓库的提交,一般建立一个项目时,初始化的时候更新一下就可以了,后继修改个别的库,可以通过指定库版本,或重写库版本的方式,使项目大部分的引用的库都是稳定的。\n关于vcpkg版本控制 可以参考这个文档Versioning reference | Microsoft Learn\n然后创建一个vcpkg.json\n1 2 3 4 5 6 7 { \u0026#34;name\u0026#34;: \u0026#34;prismqt-core\u0026#34;, \u0026#34;version\u0026#34;: \u0026#34;0\u0026#34;, \u0026#34;dependencies\u0026#34;: [ \u0026#34;prism\u0026#34; ] } 这里的prism没有指定任何版本,所以 会从我们私有库的基本版本中获取默认的版本\n在测试项目根目录建立好这两个json后, 执行vcpkg install\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 $ vcpkg.exe install Detecting compiler hash for triplet x64-windows... -- Using %HTTP(S)_PROXY% in environment variables. The following packages will be rebuilt: * catch2:x64-windows -\u0026gt; 3.4.0 -- C:\\Users\\user\\AppData\\Local\\vcpkg\\registries\\git-trees\\5796c1c0513a7b49f135e8acdd1976f53e9944ea * vcpkg-cmake:x64-windows -\u0026gt; 2023-05-04 -- C:\\Users\\user\\AppData\\Local\\vcpkg\\registries\\git-trees\\88a7058fc7fa73a9c4c99cfcae9d79e2abf87a5a * vcpkg-cmake-config:x64-windows -\u0026gt; 2022-02-06#1 -- C:\\Users\\user\\AppData\\Local\\vcpkg\\registries\\git-trees\\8d54cc4f487d51b655abec5f9c9c3f86ca83311f The following packages will be built and installed: prism:x64-windows -\u0026gt; 1.0.0 -- C:\\Users\\user\\AppData\\Local\\vcpkg\\registries\\git-trees\\668c45ba147cbef160d9c228712f5775cff242b2 Additional packages (*) will be modified to complete this operation. Restored 3 package(s) from C:\\Users\\user\\AppData\\Local\\vcpkg\\archives in 1.3 s. Use --debug to see more details. Removing 1/7 catch2:x64-windows Elapsed time to handle catch2:x64-windows: 217 ms Removing 2/7 vcpkg-cmake-config:x64-windows Elapsed time to handle vcpkg-cmake-config:x64-windows: 98 ms Removing 3/7 vcpkg-cmake:x64-windows Elapsed time to handle vcpkg-cmake:x64-windows: 113 ms Installing 4/7 vcpkg-cmake:x64-windows... Elapsed time to handle vcpkg-cmake:x64-windows: 16 ms Installing 5/7 vcpkg-cmake-config:x64-windows... Elapsed time to handle vcpkg-cmake-config:x64-windows: 16.5 ms Installing 6/7 catch2:x64-windows... Elapsed time to handle catch2:x64-windows: 58.2 ms Installing 7/7 prism:x64-windows... Building prism:x64-windows... -- Installing port from location: C:\\Users\\user\\AppData\\Local\\vcpkg\\registries\\git-trees\\668c45ba147cbef160d9c228712f5775cff242b2 -- Using cached D:/Users/user/Documents/source/repos/vcpkg/downloads/prism-62156a21f843168993c8e641f22efb380b8e7e62.tar.gz -- Cleaning sources at D:/Users/user/Documents/source/repos/vcpkg/buildtrees/prism/src/380b8e7e62-68fffb19a4.clean. Use --editable to skip cleaning for the packages you specify. -- Extracting source D:/Users/user/Documents/source/repos/vcpkg/downloads/prism-62156a21f843168993c8e641f22efb380b8e7e62.tar.gz -- Using source at D:/Users/user/Documents/source/repos/vcpkg/buildtrees/prism/src/380b8e7e62-68fffb19a4.clean -- Found external ninja(\u0026#39;1.10.2\u0026#39;). -- Configuring x64-windows -- Building x64-windows-dbg -- Building x64-windows-rel -- Using cached mingw-w64-i686-pkgconf-1~1.8.0-2-any.pkg.tar.zst. -- Using cached msys2-msys2-runtime-3.4.6-1-x86_64.pkg.tar.zst. -- Using msys root at D:/Users/user/Documents/source/repos/vcpkg/downloads/tools/msys2/6f3fa1a12ef85a6f -- Installing: D:/Users/user/Documents/source/repos/vcpkg/packages/prism_x64-windows/share/prism/copyright -- Installing: D:/Users/user/Documents/source/repos/vcpkg/packages/prism_x64-windows/share/prism/usage -- Performing post-build validation Stored binaries in 1 destinations in 58.6 ms. Elapsed time to handle prism:x64-windows: 21 s Total install time: 21 s The package prism usage: find_package(prism::prism) target_include_directories(main INTERFACE ${PRISM_INCLUDE_DIRS}) more infomation: https://github.com/nocanstillbb/prism 依赖全部安装完毕,我们只需要把所有依赖列到顶层项目上,就可以用vcpkg安装所有依赖,更新所有依赖,也可以利用vcpkg的版本控制功能来管理依赖, 这样git分支只需要保留一个master分支就可以了,所有依赖的版本可以用根目录的vcpkg.json来控制和查看\n",
"url": "\/post\/%E5%88%9B%E5%BB%BAvcpkg%E7%A7%81%E6%9C%89%E4%BB%93%E5%BA%93\/"
},
"\/tags\/qt\/": {
"title": "Qt",
"tags": [],
"content": "",
"url": "\/tags\/qt\/"
},
"\/post\/%E4%B8%BAdrone%E5%88%B6%E4%BD%9Cqt%E7%BC%96%E8%AF%91%E6%89%93%E5%8C%85%E7%9A%84ubuntu%E7%8E%AF%E5%A2%83%E7%9A%84docker%E9%95%9C%E5%83%8F\/": {
"title": "为drone制作qt编译打包的ubuntu环境的docker镜像",
"tags": ["C\u002b\u002b","CI\/CD","Ubuntu","Qt","Docker",],
"content": "0.前言 之前我已经写了 Drone+Forgejo+SonarQube 持续集成code review的文档,\n但在之前的文档中,我们并没有自己动手创建docker,都是用别人做好的镜像。\n现在我们需要把开发环境打到docker镜像中,并且把我们的代码库拉到镜像中,做一个基线 ,\n每次Forgejo上触发一个webhook时,我们上Drone基于这个基线,执行指令递归拉取所有git submodule,执行打包脚本。\n1 三种思路 摆在我们面前有三种选择\n编写dockerfile ,从dockerfile生成镜像\n直接运行容器,在容器中安装好所有依赖后,把容器实例转换为镜像\n用现有的系统直接打成docker镜像\n前者的好处是,文件系统分层情况良好,当你有很多镜像时,他们可能可以复用某一些文件层,节约空间和网络流量\n不好的地方就是步骤比较繁琐。后者的好处就是操作简单,但基础镜像的分层信息会丢失。\n因为我们除了Drone构建打包用docker,基本上没其他事了,ubuntu16裸机的这个分层,复用率不高,另外我们只在ci cd的这台电脑上加载这个镜像,没有什么docker集群,所以也不用担心网络流量的问题。所以综合考虑,我们可以考虑使用第二种方式或第三种方式, 由于我硬盘剩余空间比较小,故采用第二种方式\n1 安装docker 由于闫工给我的机器是ubuntu,所以我用apt安装 docker,略\n2.制作开发环境docker 2.1 docker镜像 拉取,启动 拉一个下载量最多的\n1 root@drone-ci-8-20:~# docker pull homebrew/ubuntu16.04 这时查看电脑上的镜像可以看到已经拉取的\n1 2 3 root@drone-ci-8-20:~# docker images REPOSITORY TAG IMAGE ID CREATED SIZE homebrew/ubuntu16.04 latest 6347afdf9148 6 months ago 1.27GB 实例化容器\n1 2 root@drone-ci-8-20:~# docker run -d homebrew/ubuntu16.04 tail -f /dev/null ed1c0b7568119b788b940e21fb1b2d50d63c3e9fe74a48a1dabd0d02987b3a5c tail -f /dev/null 会使这个容器一启动就 一直保持运行,这样我们才有机会进去安装环境\n1 2 3 root@drone-ci-8-20:~# docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES ed1c0b756811 homebrew/ubuntu16.04 \u0026#34;tail -f /dev/null\u0026#34; 25 seconds ago Up 24 seconds sharp_shtern 查看一下容器,已经有一个状态 为up的容器,说明运行成功了。\n接下来使用 exec -it 以交互的形式执行容器的bash ,\n1 2 root@drone-ci-8-20:~# docker exec -it ed1c /bin/bash linuxbrew@149b1d533469:~$ 看一下系统内核\n1 2 3 linuxbrew@ed1c0b756811:~$ uname -a Linux ed1c0b756811 5.15.0-84-generic #93-Ubuntu SMP Tue Sep 5 17:16:10 UTC 2023 x86_64 x86_64 x86_64 GNU/Linux linuxbrew@ed1c0b756811:~$ 内核显示为5.15,而实际上ubuntu内核最新的是4.15.142, 两者显示不一样是因为docker container使用的是host的内核\n不过我们只是编译软件,应该没有太多问题,先不管内核版本\n接下来的操作,就和ubuntu裸机安装环境没太多区别了,甚至更简单,因为我们只是要编译软件,不需要安装运行环境,比如驱动\n2.2 安装基础依赖 接下来更新一下我们公司的apt源 ,下载一下闫工之前写的脚本\n1 2 3 4 5 6 sudo -i apt update apt install wget cd ~ wget http://file.deepvision-tech.net/scripts/apt.sh chmod +x apt.sh 因为docker中的网络环境和主机中不一样,所以我们需要修改一下脚本 ,手动注释掉 杭州或北京的脚本 逻辑\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 #!/bin/bash # v1.1 # 2023.3.21 # yanqianling@deepvision-tech.com OldSource=`grep ^deb /etc/apt/sources.list|awk \u0026#39;{print $2}\u0026#39;|sort |uniq |sed \u0026#39;/security.ubuntu.com/d\u0026#39;` NewSource=\u0026#34;-apt.deepvision-tech.net/repository/apt-\u0026#34; OsVer=`cat /etc/issue|awk -F\u0026#39;[ .]+\u0026#39; \u0026#39;{print $2}\u0026#39;` IP=`ip a | awk \u0026#39;-F[ .]+\u0026#39; \u0026#39;/255/{print $3\u0026#34;.\u0026#34;$4\u0026#34;.\u0026#34;$5}\u0026#39;|egrep \u0026#39;10.[1,2,8]{1}.\u0026#39;` YELOW=\u0026#39;\\E[1;33m\u0026#39; RED=\u0026#39;\\E[1;31m\u0026#39; RES=\u0026#39;\\E[0m\u0026#39; #case $IP in # \u0026#34;10.2.1\u0026#34;) # cp -f /etc/apt/sources.list /etc/apt/sources.list.bak # echo \u0026#34;\u0026#34; # echo -e \u0026#34;${YELOW} Old apt sources backup to [/etc/apt/sources.list.bak] ${RES}\u0026#34; # sed -i \u0026#34;s#${OldSource}#http://bj${NewSource}${OsVer}/#g\u0026#34; /etc/apt/sources.list # echo -e \u0026#34;${YELOW} new apt source replaced by [http://bj${NewSource}${OsVer}] ${RES}\u0026#34; # echo -e \u0026#34;${YELOW} Please run command [sudo apt update],Let config become effective ${RES}\u0026#34; #;; # \u0026#34;10.1.1\u0026#34;|\u0026#34;10.1.8\u0026#34;) cp -f /etc/apt/sources.list /etc/apt/sources.list.bak echo \u0026#34;\u0026#34; echo -e \u0026#34;${YELOW} Old apt sources backup to [/etc/apt/sources.list.bak] ${RES}\u0026#34; sed -i \u0026#34;s#${OldSource}#http://hz${NewSource}${OsVer}/#g\u0026#34; /etc/apt/sources.list echo -e \u0026#34;${YELOW} new apt source replaced by [http://hz${NewSource}${OsVer}] ${RES}\u0026#34; echo -e \u0026#34;${YELOW} Please run command [sudo apt update],Let config become effective ${RES}\u0026#34; # ;; # *) # echo -e \u0026#34;${YELOW} ${IP} ${RES}\u0026#34; # echo -e \u0026#34;${YELOW}YOU IP Error ${RES}\u0026#34; #esac ~ ~ ~ 例如我操作的这个机器是在杭州的,我就把脚本中的case 语句注释掉,只留下杭州这一段逻辑\n然后执行它\n1 ./apt.sh 替换完源后 ,更新一下\n1 apt update 然后开始安装编译环境\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 sudo apt install -y liblapack-dev sudo apt install -y libjpeg-dev libpng12-dev libtiff-dev libjasper-dev libdc1394-22-dev sudo apt install -y libva1 libva-dev libva-drm1 libva-egl1 libva-glx1 sudo apt install -y libswscale-dev libavcodec-dev libavformat-dev sudo apt install -y libssh2-1 zlib1g-dev sudo apt install -y libgl1-mesa-dev libglu1-mesa-dev freeglut3-dev libsndio-dev sudo apt install -y python3-pip pip3 install openpyxl==2.6.4 pip3 install xlrd==1.2.0 sudo apt install -y libcap-dev libx11-dev libxext-dev libgtk-3-dev libglade2-0 libglade2-dev libpcap0.8 libcap2 ethtool sudo apt install -y libsndio-dev sudo apt install -y libxcb-keysyms1-dev libxcb-image0-dev libxcb-shm0-dev libxcb-icccm4-dev libxcb-sync0-dev libxcb-xfixes0-dev libxcb-shape0-dev libxcb-randr0-dev libxcb-render-util0-dev libfontconfig1-dev libfreetype6-dev libx11-dev libxext-dev libxfixes-dev libxi-dev libxrender-dev libxcb1-dev libx11-xcb-dev libxcb-glx0-dev x11vnc xauth build-essential mesa-common-dev libglu1-mesa-dev libxkbcommon-dev libxcb-xkb-dev libxslt1-dev libgstreamer-plugins-base0.10-dev libxkbcommon-x11-0 sudo apt-get install build-essential libgli-mesa-dev libglul-mesa-dev freeglut3-dev sudo apt install -y libxcb-keysyms1-dev libxcb-image0-dev libxcb-shm0-dev libxcb-icccm4-dev libxcb-sync0-dev libxcb-xfixes0-dev libxcb-shape0-dev libxcb-randr0-dev libxcb-render-util0-dev libfontconfig1-dev libfreetype6-dev libx11-dev libxext-dev libxfixes-dev libxi-dev libxrender-dev libxcb1-dev libx11-xcb-dev libxcb-glx0-dev x11vnc xauth build-essential mesa-common-dev libglu1-mesa-dev libxkbcommon-dev libxcb-xkb-dev libxslt1-dev libgstreamer-plugins-base0.10-dev libxkbcommon-x11-0 2.3 安装Qt 2.3.1 方式一,编译源码安装 安装qt 5.12.6\n传统的方式是 在有gui的环境下运行.run的安装包, 但docker 里的ubuntu默认是没有图形界面的。\n所以直接下载官网的源码编译,安装\n1 2 3 4 5 6 7 8 9 10 11 12 13 wget https://download.qt.io/archive/qt/5.12/5.12.6/single/qt-everywhere-src-5.12.6.tar.xz tar -zxvf qt-everywhere-src-5.12.6.tar.xz cd qt-everywhere-src-5.12.6 ./configure make -j8 make install # 如果出现问题make confclean 清除配置信息 重新编译 安装完后会有 /local/Qt-5.12.6/这个路径, 把qmake复制到/usr/bin ,然后键入 qmake -v 查看是否编译安装正常\n1 2 3 linuxbrew@ed1c0b756811:/usr$ qmake -v QMake version 3.1 Using Qt version 5.12.6 in /usr/local/Qt-5.12.6/lib 因为我们只是用docker来构建,不需要启动图形界面,所以这种可以减少不必要的依赖安装,但编译时长较长,如果不想编译源码的话,可以使用另一种方案,docker宿主机上安装桌面环境\n2.3.2 方式二,docker挂载宿主机图形相关卷启动 1 2 sudo apt install ubuntu-desktop xhost local:root 使用vnc远程到宿主机后,然后参照这个设置,把docker挂载上显示相关的卷,\n1 docker run -v /tmp/.X11-unix:/tmp/.X11-unix -e DISPLAY=$DISPLAY -v /dev/shm:/dev/shm --device /dev/dri 就可以在docker容器中打开 run安装包安装了, 如果还打不开run安装包,可以尝试指定 不依赖硬件的平台\n1 qt-opensource-linux-x64-5.12.6.run -platform minimal 或\n1 qt-opensource-linux-x64-5.12.6.run -platform offscreen p latform 的可选项\ndirectfbegl,directfb,eglfs,linuxfb,minimal,minimalegl,offscreen,wayland-egl,wayland,xcb。\n以及这个, 使用脚本,静默安装 installation - Silent install Qt run installer on ubuntu server - Stack Overflow\n2.4 克隆源码构建 全安装后, 克隆一下源码\n1 2 3 4 mkdir -p ~/source/repos cd ~/source/repos/ git clone http://10.1.8.8/AL/dv-detector.git git submodule update --init --recursive 然后就可以运行打包脚本构建了\n1 ./pkg.sh 1.1.1.1 tst_build 2.5 把现有docker container 转换为docker image 删除掉不需要的文件,尽可能减少容器占用的空间,然后再打镜像\n1 2 3 4 5 root@drone-ci-8-20:~# docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES ed1c0b756811 homebrew/ubuntu16.04 \u0026#34;tail -f /dev/null\u0026#34; 25 seconds ago Up 24 seconds sharp_shtern docker commit ed1c0b756811 dv_detector:master1.1.1.1 因为我们目前没有搭建docker 私有仓库,所以一般通过导入导出 docker 镜像到文件系统传输\n导出docker 镜像到文本系统\n1 docker save -o myimage_exported.tar dv_detector:master1.1.1.1 从文件系统导入\n1 docker load -i myimage_exported.tar 3 配置drone,执行docker runner Overview | Drone\n官网之述备矣\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 kind: pipeline type: docker name: build clone: disable: true steps: - name: pkg image: dv_detector:master1.1.1.1 environment: DRONE_BRANCH: $DRONE_BRANCH PKG_VERSION: \u0026#34;1.1.1.2\u0026#34; PKG_NAME: \u0026#34;测试打包\u0026#34; settings: detach: false entrypoint: [\u0026#34;\u0026#34;] commands: - cd ~/source/repos/dv-detector - git reset --hard - git clean -xdf - git pull origin master - git submodule update --recursive - bash -c \u0026#34;./pkg.sh $PKG_VERSION $PKG_NAME\u0026#39;\u0026#39;_$(git rev-parse --short=6 HEAD)\u0026#34; 最终打出的包放在容器中没后续动作,因为本文只做技术验证\n如果需要,只需要在comands里加额外的处理就可以了\nend\n100. 附录:Docker 常用命令 100.1 容器生命周期管理: 创建一个容器:docker run \u0026lt;image_name\u0026gt; 启动容器:docker start \u0026lt;container_name\u0026gt; 停止容器:docker stop \u0026lt;container_name\u0026gt; 重启容器:docker restart \u0026lt;container_name\u0026gt; 删除容器:docker rm \u0026lt;container_name\u0026gt; 查看运行中的容器:docker ps 查看所有容器(包括停止的):docker ps -a 进入容器内部命令行:docker exec -it \u0026lt;container_name\u0026gt; /bin/bash 100.2 镜像操作: 搜索镜像:docker search \u0026lt;image_name\u0026gt; 下载镜像:docker pull \u0026lt;image_name\u0026gt; 列出本地镜像:docker images 删除本地镜像:docker rmi \u0026lt;image_name\u0026gt; 100.3容器日志和信息: 查看容器日志:docker logs \u0026lt;container_name\u0026gt; 查看容器详细信息:docker inspect \u0026lt;container_name\u0026gt; 100.4容器文件传输: 从容器复制文件到本地:docker cp \u0026lt;container_name\u0026gt;:\u0026lt;src_path\u0026gt; \u0026lt;dest_path\u0026gt; 从本地复制文件到容器:docker cp \u0026lt;src_path\u0026gt; \u0026lt;container_name\u0026gt;:\u0026lt;dest_path\u0026gt; 100.5构建镜像: 基于 Dockerfile 构建镜像:docker build -t \u0026lt;image_name\u0026gt;:\u0026lt;tag\u0026gt; \u0026lt;path_to_Dockerfile\u0026gt; 100.6Docker Compose: 使用 Docker Compose 启动多个容器:docker-compose up 停止 Docker Compose 启动的容器:docker-compose down 100.7网络操作: 列出 Docker 网络:docker network ls 创建 Docker 网络:docker network create \u0026lt;network_name\u0026gt; 100.8清理: 清理无用容器:docker container prune 清理无用镜像:docker image prune 清理无用卷(数据卷):docker volume prune ",
"url": "\/post\/%E4%B8%BAdrone%E5%88%B6%E4%BD%9Cqt%E7%BC%96%E8%AF%91%E6%89%93%E5%8C%85%E7%9A%84ubuntu%E7%8E%AF%E5%A2%83%E7%9A%84docker%E9%95%9C%E5%83%8F\/"
},
"\/post\/cpp%E8%B7%A8%E5%B9%B3%E5%8F%B0%E9%A1%B9%E7%9B%AE%E5%9C%A8windows%E4%B8%8A%E7%9A%84%E6%96%87%E6%9C%AC%E7%BC%96%E7%A0%81%E9%97%AE%E9%A2%98\/": {
"title": "cpp跨平台项目在windows上的文本编码问题",
"tags": ["C\u002b\u002b","Windows",],
"content": "1.背景 linux系统和mac os默认使用utf8编码,windows系统上通常是跟随系统设置,如果系统选择为中文地区的话,默认为GBK编码。\nwindows 10 1703开始,支持把windows编码设置为utf8 。\nIDE上一般也是默认是这个配置,但是IDE可以选择编码源码保存为什么格式。\n2.跨平台项目遇到的问题 由于linux和windows共用一个代码库,所以源码都使用同一编码格式,在我的情况,我把源码保存为utf8,并在windows上把ide配置为 utf8 不带BOM, FL 格式\nBOM,windows字符前加上额外字节的信息,用来让windows认识字符串属于哪种编码\nFL , 换行符windows上是CRFL, Mac os是CR ,Linux是FL\n同时windows上把编译器设置为输出utf8 格式,\nqmake使用 CONFIG += utf8_source 配置,它会根据编码器自动加上utf8的编码选项\n至此为止,世界如此美好, 一切显示正常,甚至还能用emoji 表情字符\n直到打开文件发现打不开, 调用系统命令行无故执行错误 ,控制台显示乱码\n3.解决方法 3.1 使用windows api转换utf8为gbk 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 #include \u0026lt;sstream\u0026gt; #include \u0026lt;locale\u0026gt; #include \u0026lt;codecvt\u0026gt; inline std::string utf8_to_gbk(const std::string\u0026amp; str) { std::wstring_convert\u0026lt;std::codecvt_utf8\u0026lt;wchar_t\u0026gt;\u0026gt; conv; std::wstring tmp_wstr = conv.from_bytes(str); //GBK locale name in windows const char* GBK_LOCALE_NAME = \u0026#34;.936\u0026#34;; std::wstring_convert\u0026lt;std::codecvt_byname\u0026lt;wchar_t, char, mbstate_t\u0026gt;\u0026gt; convert(new std::codecvt_byname\u0026lt;wchar_t, char, mbstate_t\u0026gt;(GBK_LOCALE_NAME)); return convert.to_bytes(tmp_wstr); } inline std::string gbk_to_utf8(const std::string\u0026amp; str) { //GBK locale name in windows const char* GBK_LOCALE_NAME = \u0026#34;.936\u0026#34;; std::wstring_convert\u0026lt;std::codecvt_byname\u0026lt;wchar_t, char, mbstate_t\u0026gt;\u0026gt; convert(new std::codecvt_byname\u0026lt;wchar_t, char, mbstate_t\u0026gt;(GBK_LOCALE_NAME)); std::wstring tmp_wstr = convert.from_bytes(str); std::wstring_convert\u0026lt;std::codecvt_utf8\u0026lt;wchar_t\u0026gt;\u0026gt; cv2; return cv2.to_bytes(tmp_wstr); } 每当需要和系统交互时,先把utf8转换为gbk ,例如 打开文件,调用系统命令行\n如果调用系统管道,返回了字符串,分别调用子进程,重定向了字符回显,也需要返回了字符串是gbk,要在程序里转换为utf8\n这种方式比较麻烦,但也比较通用,只要是字符串就能转\n3.2 使用 c++17 filesystem 解决文件路径 filesystem在gcc 5.4仍然属于实验阶段的功能,所以在gcc 5.4中,它的名称空间和头文件位置多了一级experimental\n1 2 3 4 5 6 7 //gcc 5.4 #include \u0026lt;experimental/filesystem\u0026gt; int main() { std::experimental::filesystem::path ..... } 如果是gcc 7 则需要去掉experimental这级 名称\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 #include \u0026lt;filesystem\u0026gt; void testFilePath(std::string filename) { std::ifstream file(filename); if(file.good()) std::cout \u0026lt;\u0026lt; filename \u0026lt;\u0026lt; \u0026#34; 存在\u0026#34;\u0026lt;\u0026lt; std::endl; else std::cout \u0026lt;\u0026lt; filename \u0026lt;\u0026lt; \u0026#34; 不存在\u0026#34;\u0026lt;\u0026lt; std::endl; } int main() { //全是ascii testFilePath(\u0026#34;d:/privatekey.pem\u0026#34;); testFilePath(\u0026#34;d:/数据判定.csv\u0026#34;); std::filesystem::path path = std::filesystem::u8path(\u0026#34;d:/数据判定.csv\u0026#34;); std::ifstream file(path); if(file.good()) std::cout \u0026lt;\u0026lt; path \u0026lt;\u0026lt; \u0026#34; 存在\u0026#34;\u0026lt;\u0026lt; std::endl; else std::cout \u0026lt;\u0026lt; path \u0026lt;\u0026lt; \u0026#34; 不存在\u0026#34;\u0026lt;\u0026lt; std::endl; } 上面的代码如果使用utf8编译选项,且源码保存为utf8,且在代码页设置为utf8的控制台上运行,你将看到输出\n1 2 3 d:/privatekey.pem 存在 d:/数据判定.csv 不存在 \u0026#34;d:/�����ж�.csv\u0026#34; 存在 这种方式可以十分方便的兼容多个平台,但std::filesystem::path只有c++17才有,并且,不能直接和std::string转换,\n但是上面的示例代码使用std::cout 输出是正常的,只是被加上了引号, 说明可以使用流的方式写入std::string里,只是可能会被加上引号。\n3.3 使用windows api解决 gbk系统环境控制台显示程序输出乱码 这种情况,可以使用3.1中的那种文件转换utf8编码,但是如果不小心把gbk转为gbk, 程序就会崩溃, 打印子进程回显时踩了一次坑\n可以使用windows设置程序输出到控制台的代码页\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 #ifdef _MSC_VER #define _AMD64_ #include \u0026lt;ConsoleApi2.h\u0026gt; #include \u0026lt;winnls.h\u0026gt; #endif int main(int argc, const char** argv) { #ifdef _MSC_VER SetConsoleOutputCP(CP_UTF8); SetConsoleCP(CP_UTF8); #endif } ",
"url": "\/post\/cpp%E8%B7%A8%E5%B9%B3%E5%8F%B0%E9%A1%B9%E7%9B%AE%E5%9C%A8windows%E4%B8%8A%E7%9A%84%E6%96%87%E6%9C%AC%E7%BC%96%E7%A0%81%E9%97%AE%E9%A2%98\/"
},
"\/about\/": {
"title": "About me",
"tags": [],
"content": " c++开源项目组织 cpp-prism 的作者\n组织中有以下几个代码仓库:\nprism 是一个静态反射框架,仅依赖于stl,支持扩展,内置一个简单的基于反射的json库,目前正在扩展sql orm\nprism_container 依赖注入框架\nprism_qt_modular 模块化相关,包括一个跨平台的动态加载动态库及c风格函数指针的封装\nprism_qt_cofe qt反射扩展核心库,主要对qt list model,tree model进行泛型封装,支持qml双向绑定到普通c++类\nprism_qt_ui qml反射扩展相关,封装了一些额外的桌面控制,如openGL 视频渲染器(目前只支持mono8,rgb)\nprism_qt_all git 顶层项目,通过git submodule管理所有子模块,方便签出各模块兼容的版本\nvcpkg 计划中的vcpkg private registry,prism所有模块都支持自动生成vcpkg包数据,简单的发布到私有vcpkg仓库,享受vcpkg的资产缓存和二进制缓存,版本控制等好处\n工作经历 14.09-18.03 泉州 电气工程师 18.03-21.03 广州 windows桌面开发 21.03-22.11 北京 windows桌面开发 22.11 \u0026mdash;\u0026ndash; 北京 windows/linux桌面开发 技能点 C# XAML WPF Wix C++ Python QML Qt Quick CMAKE VCPKG Linux Windows Docker ",
"url": "\/about\/"
},
"\/categories\/": {
"title": "Categories",
"tags": [],
"content": "",
"url": "\/categories\/"
},
"\/tags\/ci\/cd\/": {
"title": "CI\/CD",
"tags": [],
"content": "",
"url": "\/tags\/ci\/cd\/"
},
"\/search\/": {
"title": "Search Results:",
"tags": [],
"content": "",
"url": "\/search\/"
},
}
</script>
<script src="/js/lunr.min.js"></script>
<script src="/js/search.js"></script>