<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Suzunan</title><description>npofsi&apos;s Blog</description><link>https://npofsi-blog.vercel.app/</link><language>zh_CN</language><item><title>对含有 Simscape 的 Simulink 模型中进行程序控制</title><link>https://npofsi-blog.vercel.app/posts/programmatically-simulink-with-simscape/</link><guid isPermaLink="true">https://npofsi-blog.vercel.app/posts/programmatically-simulink-with-simscape/</guid><description>使用 Matlab 程序对 Simulink 模型进行操作</description><pubDate>Sun, 15 Oct 2023 10:15:28 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;本文基于 Matlab 2023b，Simulink 10.4，Simscape 5.3 编写。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;背景&lt;/h2&gt;
&lt;p&gt;最近学校的一门课程，大作业的任务是通过线路牵引供电模型计算铁路区间上正在行驶的列车所处的位置，其中用到了 Simscape，Matlab 本身对 Simulink 的支持很到位，文档也给的很全，但是对于 Simscape 的部分还是踩了许多坑，这里记录一下，这部分主要集中在文章的后半段；模型本身其实比较简单，所以以下的举例也不是很复杂，主要针对已有模型。&lt;/p&gt;
&lt;h2&gt;使用 Matlab 读取 Simulink 模型&lt;/h2&gt;
&lt;p&gt;Simulink 中的功能大多都可以通过 Matlab 程序实现，可以查看 &lt;a href=&quot;https://ww2.mathworks.cn/help/simulink/ug/approach-modeling-programmatically.html&quot;&gt;Simulink - Programmatic Modeling Basics&lt;/a&gt; ，这里总结一下，对已有模型，在 .m 语言中有以下几个函数可以对模型进行一些文件操作：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;% 打开模型
% 同时会打开 Simulink 的窗口，对比加载模型，同步性会更好
% 也不容易遇到文件锁的问题，这个问题在使用这行代码打开后可以通过关闭 Simulink 窗口解决
open_system(&apos;model_name&apos;);

% 加载模型
% 只会加载模型，不会打开 Simulink 窗口
% 但如果程序中间异常退出，没有进行到 close_system，会导致模型文件被锁定，无法再次打开，可能需要重启 Matlab
load_system(&apos;model_name&apos;);

% 保存模型
% 保存模型的当前状态，包括参数设置等
% 可以设置 (&apos;OverwriteIfChangedOnDisk&apos;,true) 来响应文件系统变更
save_system(&apos;model_name&apos;);

% 关闭模型
close_system(&apos;model_name&apos;);
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;使用 Matlab 编辑 Simulink 模型&lt;/h2&gt;
&lt;p&gt;首先，在 Simulink 内部，模型的不同组件使用的是像 Linux 文件系统一样的路径来进行标识，例如：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;% 这里的 SubsystemName 和 BlockName 都是模块的名字（如下图模块的名字就是 AT1）
block_name = &apos;model_name/BlockName&apos;
block_name1 = &apos;model_name/SubsystemName/BlockName&apos;
block_name2 = &apos;model_name/SubsystemName/SubsystemName/BlockName&apos;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;simulink_block.png&quot; alt=&quot;一个 Simulink 模块&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;关于 Simulink 模块&lt;/h3&gt;
&lt;p&gt;可以使用以下几个命令读取或者设置模块的参数：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;% 获取模块参数
get_param(block_name,&apos;parameter_name&apos;)
% 例如：获取模块句柄 (Handle)
% 通过句柄可以对模块进行操作
get_param(&apos;model_name/BlockName&apos;,&apos;Handle&apos;)

% 设置模块参数
set_param(block_name,&apos;parameter_name&apos;,&apos;value&apos;)
% 例如：设置模块位置
set_param(&apos;model_name/BlockName&apos;,&apos;Position&apos;,[100,100,200,200])
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Matlab 中提供了两个函数可以对模块进行增删：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;% 添加模块
add_block(&apos;block_type&apos;,&apos;model_name/BlockName&apos;)
% 这个函数也可以用于模块的复制
add_block(&apos;model_name/Subsystem1/BlockName&apos;,&apos;model_name/Subsystem2/BlockName&apos;)
% 例如：添加一个 Gain 模块
add_block(&apos;simulink/Commonly Used Blocks/Gain&apos;,&apos;model_name/BlockName&apos;)

% 删除模块
delete_block(&apos;model_name/SubsystemName/BlockName&apos;)
% 例如：删除一个 Gain 模块
delete_block(&apos;model_name/BlockName&apos;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Matlab 没有提供模块的移动（剪切）的函数&lt;/strong&gt;，这个可以通过以下方法实现（踩的坑之一）：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;% 复制模块
add_block(&apos;model_name/Subsystem1/BlockName&apos;,&apos;model_name/Subsystem2/BlockName&apos;)
% 删除原模块
delete_block(&apos;model_name/Subsystem1/BlockName&apos;)
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;关于模块的连接&lt;/h3&gt;
&lt;p&gt;对于 Simscape 模块，建议使用模块的端口句柄来建立连接线，例如：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;% 获取端口句柄(实际上是一个包含端口ID的列表)
Block_PortHandle = get_param(&apos;model_name/BlockName&apos;,&apos;PortHandles&apos;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;注意&lt;/strong&gt; 大部分 Simscape 电器模块的端口是双向的，所以 &lt;strong&gt;不&lt;/strong&gt; 需要指定端口的方向（即不能使用句柄中的&lt;code&gt;Inport&lt;/code&gt;和&lt;code&gt;Outport&lt;/code&gt;，而是使用&lt;code&gt;LConn&lt;/code&gt;和&lt;code&gt;RConn&lt;/code&gt;，这两个属性并不区分方向，只是指示端口在模块的左侧还是右侧），这和 Simulink 中的一般模块不同，但 Simscape 模块之间的连接线在程序索引时依然 &lt;strong&gt;区分&lt;/strong&gt; 方向，在这一小节结尾会讨论这件事&lt;/p&gt;
&lt;p&gt;模块的连接可以通过以下几个函数实现：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;% 添加连接线
add_line(&apos;model_name/Subsystem&apos;,&apos;BlockName1/Port&apos;,&apos;BlockName2/Port&apos;)
% 使用句柄
add_line(&apos;model_name/Subsystem&apos;,Block1_PortHandle.PortName,Block2_PortHandle.PortName)
% 例如：连接两个模块
add_line(&apos;model_name/Subsystem&apos;,&apos;Block1/LConn1&apos;,&apos;Block2/RConn4&apos;)
add_line(&apos;model_name/Subsystem&apos;,Block1_PortHandle.LConn(1),Block2_PortHandle.RConn(4))
% 如果句柄中端口数量一样，可以进行批量连接
add_line(&apos;model_name/Subsystem&apos;,Block1_PortHandle.LConn,Block2_PortHandle.RConn)

% 删除连接线
delete_line(&apos;model_name/Subsystem&apos;,&apos;BlockName1/PortName&apos;,&apos;BlockName2/PortName&apos;)
% 例如：删除两个模块的连接
delete_line(&apos;model_name/Subsystem&apos;,&apos;Block1/LConn3&apos;,&apos;Block/RConn5&apos;)
% 同理，可以使用句柄
delete_line(&apos;model_name/Subsystem&apos;,Block1_PortHandle.LConn(3),Block2_PortHandle.RConn(5))
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这里也遇到了两个问题，首先，删除连接线时如果线路不存在，会报错，所以如果可能遇到重复删除的问题，建议套一个 &lt;code&gt;try catch&lt;/code&gt;，例如：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;try
    delete_line(&apos;model_name/Subsystem&apos;,Block1_PortHandle.RConn(4),Block2_PortHandle.LConn(5))
catch
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;其次，如果连接线的两端模块不在同一个子系统中，会报错，可能需要移动模块，实现参考上一小节。&lt;/p&gt;
&lt;p&gt;最后，关于 Simscape 模块间的连接线，虽然 Simscape 的电气端口不区分方向，但是其连接线仍像 Simulink 中的连接线一样区分方向，所以在进行删除操作时可能会遇到 &lt;strong&gt;明明连接线存在，但是删除时报错的情况&lt;/strong&gt; ，这大概率就是删除时端口的顺序反过来了，所以建议对每个连接线进行两次删除，因为一定会有一次冗余，所以这样操作一定要套上 &lt;code&gt;try catch&lt;/code&gt; ，例如：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;try
    delete_line(&apos;model_name/Subsystem&apos;,Block1_PortHandle.RConn(4),Block2_PortHandle.LConn(5))
catch
end
try
    delete_line(&apos;model_name/Subsystem&apos;,Block2_PortHandle.LConn(5),Block1_PortHandle.RConn(4))
catch
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;以上这种情况更有可能发生在已有的手动建立的模型中，手动建立的连接线非常可能起始于不同的模块，但在程序建立连接线时，如果保证在函数中输入的端口顺序都一致的话就不会出现这种问题。&lt;/p&gt;
&lt;h2&gt;运行 Simulink 仿真&lt;/h2&gt;
&lt;p&gt;运行模型就比较简单了，只需要调用 &lt;code&gt;sim&lt;/code&gt; 函数：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;% 直接运行仿真，关于 sim 函数的更多参数请查看文档
% https://ww2.mathworks.cn/help/simulink/slref/sim.html
out = sim(&apos;model_name&apos;);

% 或使用 set_param 方法调用 Simulink 的运行命令
set_param(&apos;model_name&apos;,&apos;SimulationCommand&apos;,&apos;start&apos;);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;refs:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://ww2.mathworks.cn/help/simulink/ug/approach-modeling-programmatically.html&quot;&gt;Simulink - Programmatic Modeling Basics&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>使用 ssh 访问 Github （从创建密钥到 clone 仓库）</title><link>https://npofsi-blog.vercel.app/posts/access-github-use-ssh/</link><guid isPermaLink="true">https://npofsi-blog.vercel.app/posts/access-github-use-ssh/</guid><pubDate>Mon, 22 May 2023 02:12:28 GMT</pubDate><content:encoded>&lt;p&gt;最近在打编译器设计赛，帮队友配置 git 的时候发现配置 ssh 的部分每次要看好几篇文章，现在用这一篇文章就可以在一台新的 Linux 系统上快速使用 ssh 访问 Github 进行开发。&lt;/p&gt;
&lt;h2&gt;创建 ssh 密钥&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;$ ssh-keygen -t ed25519 -C &quot;your_email@example.com&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果不支持 ed25519，可以使用：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ ssh-keygen -t rsa -b 4096 -C &quot;your_email@example.com&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;中间会需要选择密钥的创建位置等：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;gt; Enter a file in which to save the key (/c/Users/YOU/.ssh/id_ALGORITHM):[Press enter]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;密钥默认生成在 &lt;code&gt;~/.ssh/id_ALGORITHM&lt;/code&gt;,&lt;/p&gt;
&lt;p&gt;&lt;code&gt;ALGORITHM&lt;/code&gt; 即上面创建密钥时使用的算法&lt;/p&gt;
&lt;p&gt;&lt;code&gt;~/.ssh/id_ALGORITHM.pub&lt;/code&gt; 为公钥文件;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;~/.ssh/id_ALGORITHM&lt;/code&gt; 为私钥文件;&lt;/p&gt;
&lt;h2&gt;添加 ssh 公钥到 GitHub&lt;/h2&gt;
&lt;p&gt;执行&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ cat ~/.ssh/id_ALGORITHM.pub
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;复制输出的公钥&lt;/p&gt;
&lt;p&gt;在 &lt;a href=&quot;https://github.com/settings/keys&quot;&gt;Github User Setting -&amp;gt; SSH and GPG keys&lt;/a&gt; 页面添加 ssh 公钥。&lt;/p&gt;
&lt;h2&gt;启用 ssh agent&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;$ eval &quot;$(ssh-agent -s)&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后将我们的密钥交给 ssh-agent 管理&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ ssh-add ~/.ssh/id_ALGORITHM
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;在 &lt;code&gt;~/.ssh/&lt;/code&gt; 目录下新建一个 config 文件&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ touch ~/.ssh/config
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;编辑加入以下内容：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;HOST github.com
 User git
 ForwardAgent yes
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;em&gt;PS:&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;如果直接通过 ssh 访问 github 有问题，可以通过下面的配置使用 443 端口进行访问：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Host gitee.com
	HostName ssh.gitee.com
	Port 443
	User git
	ForwardAgent yes
Host github.com
	HostName ssh.github.com
	Port 443
	User git
	ForwardAgent yes
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;测试&lt;/h2&gt;
&lt;p&gt;现在已经配置好了 ssh 可以用以下命令进行测试：&lt;/p&gt;
&lt;p&gt;使用这个命令检查密钥是否被加入 ssh-agent&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ ssh-add -L
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;使用这个命令检查是否已经可以使用 ssh 访问 Github:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ ssh -T git@github.com
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果以上命令返回了 success ，那么已经可以使用 ssh 访问 Github了！&lt;/p&gt;
&lt;h2&gt;Clone 仓库&lt;/h2&gt;
&lt;p&gt;接着我们就能用下面的命令 clone 仓库了。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ git clone &amp;lt;url&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;refs：&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://docs.github.com/zh/authentication/connecting-to-github-with-ssh/generating-a-new-ssh-key-and-adding-it-to-the-ssh-agent&quot;&gt;生成新的 SSH 密钥并将其添加到 ssh-agent&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://docs.github.com/zh/authentication/connecting-to-github-with-ssh/using-ssh-agent-forwarding&quot;&gt;使用 SSH 代理转发&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>不使用 root 账户登陆服务器</title><link>https://npofsi-blog.vercel.app/posts/how-to-setup-a-server--without-root-login/</link><guid isPermaLink="true">https://npofsi-blog.vercel.app/posts/how-to-setup-a-server--without-root-login/</guid><pubDate>Sat, 25 Jun 2022 01:26:02 GMT</pubDate><content:encoded>&lt;p&gt;之前服务器的时候遇到疑似 root 被爆破的情况，所幸最后查明是服务商流量计费出错了，而且使用 root 登陆服务器权限过大太过危险，容易发生 &lt;code&gt;rm -rf&lt;/code&gt; 这种事故，所以这次建服的时候就考虑了一下这些事情，而且操作起来并不是很麻烦。&lt;/p&gt;
&lt;h4&gt;基本信息&lt;/h4&gt;
&lt;p&gt;一般云服务提供商会提供 root 账户的密码，这里用 &lt;code&gt;[password]&lt;/code&gt; 表示
&amp;lt;!-- more --&amp;gt;&lt;/p&gt;
&lt;h4&gt;新建用户&lt;/h4&gt;
&lt;p&gt;首先使用 root 账户登录服务器&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;PS&amp;gt; ssh root@[server]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;添加一个新的账户&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;root&amp;gt; useradd [username] -m
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;赋予该账户 sudo 权限&lt;/h4&gt;
&lt;p&gt;编辑配置文件&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;root&amp;gt; visudo
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;在 root 账户权限旁加入一行&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[username] ALL=(ALL:ALL) ALL
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;编辑SSH配置文件&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;root&amp;gt; vim /etc/ssh/sshd_config
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;在文件中添加新的一行&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;AllowUsers [username1] [username2]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;并修改 root 是否可以登录 ssh&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;PermitRootLogin no
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;!!! Warning 请确保以上编辑工作无误后继续&lt;/p&gt;
&lt;p&gt;保存文件后，运行&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;root&amp;gt; service sshd restart
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;现在该服务器就不可以使用 root 账户登录 ssh 了，需要使用新的账户来登陆服务器。&lt;/p&gt;
&lt;h4&gt;使用SSHKey登录&lt;/h4&gt;
&lt;p&gt;使用 ssh-copy-id 将 sshkey 公钥导入服务器，公钥储存在 ~/.ssh/authorized_keys&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;PS&amp;gt; ssh-copy-id [username]@[server]
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>CMake 学习小结</title><link>https://npofsi-blog.vercel.app/posts/cmake-learning-01/</link><guid isPermaLink="true">https://npofsi-blog.vercel.app/posts/cmake-learning-01/</guid><pubDate>Sun, 12 Jul 2020 09:47:28 GMT</pubDate><content:encoded>&lt;p&gt;高考完这段时间点子大概比较多？既然学了两年的信息学奥赛写了两年 C++ ，却根本不会用 C++ 去做开发，这次正好想做的一个东西对性能要求比较高，就考虑用 C++ 来做，又因为原来一直用的 Java 导致我比较关注多平台的兼容，就想趁机学习一下 CMake 这类通用构建工具。这篇文章会列举一下我学到的 CMake 常用的命令，做一个小结。&lt;/p&gt;
&lt;p&gt;如果想要学习 CMake ，非常推荐 &lt;a href=&quot;https://cmake.org/cmake/help/latest/guide/tutorial/index.html&quot;&gt;CMake 官方提供的教程&lt;/a&gt; ，跟下来的话 CMake 基本就可以掌握了。&lt;/p&gt;
&lt;p&gt;&amp;lt;!-- more --&amp;gt;&lt;/p&gt;
&lt;h5&gt;基本信息&lt;/h5&gt;
&lt;p&gt;这些就是些必填的东西&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;cmake_minimum_required (VERSION 3.8)

project (&amp;lt;PROJECT_NAME&amp;gt;)

&lt;/code&gt;&lt;/pre&gt;
&lt;h5&gt;子目录&lt;/h5&gt;
&lt;p&gt;用来标记该项目要使用到（存在 CMakeLists.txt）的子目录，&amp;lt;del&amp;gt;致使 CMakeLists.txt 比 源代码 还多的原罪啊&amp;lt;/del&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;add_subdirectory(&amp;lt;PATH&amp;gt;)
&lt;/code&gt;&lt;/pre&gt;
&lt;h5&gt;编译可执行文件&lt;/h5&gt;
&lt;p&gt;将标记的源代码文件编译成可执行文件&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;add_executable(&amp;lt;EXEC_FILE_NAME&amp;gt; &amp;lt;SOURCE_CODE_FILE。..&amp;gt;)
&lt;/code&gt;&lt;/pre&gt;
&lt;h5&gt;编译库文件&lt;/h5&gt;
&lt;p&gt;将标记的源代码文件编译成为链接库文件&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;add_library(&amp;lt;LIB_FILE_NAME&amp;gt; &amp;lt;SOURCE_CODE_FILE。..&amp;gt;)
&lt;/code&gt;&lt;/pre&gt;
&lt;h5&gt;安装&lt;/h5&gt;
&lt;p&gt;关于 install 的用法极多，具体还是查阅手册比较好 在我项目里用来引入 Duktape 库 &lt;code&gt;install(TARGETS duktape DESTINATION lib)&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;install(...)
&lt;/code&gt;&lt;/pre&gt;
&lt;h5&gt;预编译库的引入&lt;/h5&gt;
&lt;p&gt;一个 &lt;code&gt;EXEC_FILE_NAME&lt;/code&gt; 或 &lt;code&gt;EXEC_FILE_NAME&lt;/code&gt; 可作为 &lt;code&gt;TARGET_NAME&lt;/code&gt; ，本质上是将该预编译库链接在 obj 文件中，&lt;strong&gt;这也就是说需要先存在一个 &lt;code&gt;EXEC_FILE_NAME&lt;/code&gt; 或 &lt;code&gt;EXEC_FILE_NAME&lt;/code&gt;，请保证调用此命令前调用过相应的 &lt;code&gt;add_executable&lt;/code&gt; 或 &lt;code&gt;add_library&lt;/code&gt;，否则会报 &lt;code&gt; Cannot specify include directories when use target target_include_directories&lt;/code&gt; 错误&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;target_link_libraries(&amp;lt;TARGET_NAME&amp;gt; &amp;lt;PUBLIC|SHARED|PRIVATE&amp;gt; &amp;lt;LIB_NAME&amp;gt;)
&lt;/code&gt;&lt;/pre&gt;
&lt;h5&gt;头文件目录的引入&lt;/h5&gt;
&lt;p&gt;经常要用的，我的项目中是用了很多 stb 项目的头文件 &lt;code&gt;target_include_directories(${PROJECT_NAME} PUBLIC stb)&lt;/code&gt;，&lt;strong&gt;&lt;code&gt;TARGET_NAME&lt;/code&gt; 的要求同上&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;target_include_directories(&amp;lt;TARGET_NAME&amp;gt; &amp;lt;PUBLIC|SHARED|PRIVATE&amp;gt; &amp;lt;DIR_NAME&amp;gt;)
&lt;/code&gt;&lt;/pre&gt;
&lt;h5&gt;获取文件夹下所有源文件&lt;/h5&gt;
&lt;p&gt;将根目录下所有文件文件名转储到一个变量里，在添加可执行文件时的例子： &lt;code&gt;add_executable (${PROJECT_NAME} ${&amp;lt;VARIABLE_NAME&amp;gt;})&quot;&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;aux_source_directory(. &amp;lt;VARIABLE_NAME&amp;gt;)
&lt;/code&gt;&lt;/pre&gt;
&lt;h5&gt;生成 Config 文件&lt;/h5&gt;
&lt;p&gt;在编译时生成 config 文件以便在不同要求，不同操作系统下编译或输出文件&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;configure_file(Config.h.in Config.h)
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>Android ContentProvider 使用小计</title><link>https://npofsi-blog.vercel.app/posts/android-contentresolver-more/</link><guid isPermaLink="true">https://npofsi-blog.vercel.app/posts/android-contentresolver-more/</guid><pubDate>Sat, 14 Mar 2020 11:34:50 GMT</pubDate><content:encoded>&lt;p&gt;Android 应用在选取文件时推荐使用 &lt;code&gt;Intent.ACTION_GET_CONTENT&lt;/code&gt; 意图，而不是使用应用自己创建的文件选择器并获取读取文件权限。&lt;/p&gt;
&lt;p&gt;因为从 Android Marshmallow 开始 Android 权限管理开始变得更加整顿严谨，几个版本以来 Android 解决了权限管理松散的问题。&lt;/p&gt;
&lt;p&gt;其实 &lt;code&gt;ContentProvider&lt;/code&gt; 并不是什么新事物，在 Android 中一直存在着，但随着权限的明确划分，权限的索取要更加的明确，否则会极大的影响用户体验，这时使用系统给予的机制会更加符合操作逻辑，也可以减少工作量。&lt;/p&gt;
&lt;p&gt;&amp;lt;!-- more --&amp;gt;&lt;/p&gt;
&lt;p&gt;下面是一个选取文件的简单使用场景&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;//需要指定一个 int 来定位返回值
final int REQUEST_GET_CONTENT=1

//调用系统文件选择器
public void getContent() {
	Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
	intent.setType(&quot;*/*&quot;);
	intent.addCategory(Intent.CATEGORY_OPENABLE);
	startActivityForResult(intent, REQUEST_GET_CONTENT);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;//接受 URI
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
	super.onActivityResult(requestCode, resultCode, data);
	if (resultCode == RESULT_OK) {
		switch (requestCode) {
			case REQUEST_GET_CONTENT:
				Uri uri=data.getData();
				break;
		}
	}
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;之后要读取这个文件的内容只需要调用 &lt;code&gt;getContentResolver().openInputStream(uri)&lt;/code&gt; 就可以了&lt;/p&gt;
&lt;p&gt;但是！&lt;/p&gt;
&lt;p&gt;这里有个问题&lt;/p&gt;
&lt;p&gt;这样得不到文件的名字&lt;/p&gt;
&lt;p&gt;直到我在 Android 文档里翻了又翻翻了又翻翻了又翻————官方还是很良心的，只是忘了用一下搜索功能。。。。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public String getContentName(Uri uri) {
	String result = null;
	if (uri.getScheme().equals(&quot;content&quot;)) {
		Cursor cursor = this.getContentResolver().query(uri, null, null, null, null);
		try {
			if (cursor != null &amp;amp;&amp;amp; cursor.moveToFirst()) {
				result = cursor.getString(cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME));
			}
		} finally {
			cursor.close();
		}
	}
	if (result == null) {
		result = uri.getPath();
		int cut = result.lastIndexOf(&apos;/&apos;);
		if (cut != -1) {
			result = result.substring(cut + 1);
		}
	}
	return result;
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;文档里直接把这个函数给出来了，就直接拿来放心用就好了&lt;/p&gt;
&lt;p&gt;后来搜索相关的东西的时候在 stackoverflow 也发现了这个话题，回答是一样的。&lt;/p&gt;
&lt;p&gt;这些基本就是选取文件完全方法了，虽然很水，不过写出来就不容易忘了吧&lt;/p&gt;
&lt;p&gt;以上&lt;/p&gt;
</content:encoded></item><item><title>距高考那 一百天</title><link>https://npofsi-blog.vercel.app/posts/gaokao-in-100-days-2019/</link><guid isPermaLink="true">https://npofsi-blog.vercel.app/posts/gaokao-in-100-days-2019/</guid><pubDate>Wed, 26 Feb 2020 03:31:56 GMT</pubDate><content:encoded>&lt;p&gt;距离高考还有一百天了，有点不舍，两年半来的生活好像箭一般的飞过，只不过是三个月，就算对不起什么，也不能让高中的时光过得没有任何意义。&lt;s&gt;一个月前还在推 gal 的人不要说话&lt;/s&gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;!-- more --&amp;gt;&lt;/p&gt;
&lt;p&gt;不论是为了百日之后，还是为了四年之后，我想应该去尝试一下了。也曾有的时候会迷茫，失去目标，但当我每每落下笔，每每拿起一本书，我就能想起，不论是自由，还是责任，只要至少其一存在，就应该去努力。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;正是因为认真，所以才无法隐藏或逃避内心的悔恨和惋惜。只有放手去做，才能抹平心中的悔恨和惋惜，就算再失败，再失败，也只能去做。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;也得克服一下自己的&lt;strong&gt;拖延症&lt;/strong&gt;了，确实是经常把小事安排在前面导致后面工作量大的事一拖再拖。据说先做工作量大的工作就不容易拖延，&lt;s&gt;那怕不是连小事也做不完了（误）&lt;/s&gt;，希望这能起作用。&lt;/p&gt;
&lt;p&gt;顺便这里也列一下高考完想做的事吧&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;去哪里游览一次，非常想去荷兰看看，那里也有我想考研去的学校w&lt;/li&gt;
&lt;li&gt;学习一下 Verilog 等硬件描述语言&lt;/li&gt;
&lt;li&gt;把几个开了的 Android 应用完结掉，试试开发 PC 端的软件&lt;/li&gt;
&lt;li&gt;本来兴趣就在物理的，毕业以后要把《力学概论》读完，顺便看一下电气相关的书&lt;/li&gt;
&lt;li&gt;2020 年有好多的番，电影，音乐 ，想买鹿乃的专辑&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这些天里要下下辛苦呢，&lt;strong&gt;否则时间就这样过去也是一种浪费啊(๑•﹏•)。&lt;/strong&gt;&lt;/p&gt;
</content:encoded></item><item><title>《千恋 * 万花》</title><link>https://npofsi-blog.vercel.app/posts/galgame-senrenbanka-review/</link><guid isPermaLink="true">https://npofsi-blog.vercel.app/posts/galgame-senrenbanka-review/</guid><pubDate>Wed, 29 Jan 2020 13:22:07 GMT</pubDate><content:encoded>&lt;p&gt;前一阵子逛 &lt;a href=&quot;https://bbs.zdfx.net/&quot;&gt;终点论坛&lt;/a&gt; 的时候发现了这部 &lt;a href=&quot;https://www.yuzusoft.com/&quot;&gt;柚子社&lt;/a&gt; 的 GAL, 因为原先有人推荐，所以就下下来玩了。丛雨线确实是十分出色 &lt;s&gt;丛雨天下第一&lt;/s&gt; ，而且famishin为柚子社这一作的配乐也十分的棒。整部游戏用了半个星期的时间全通完了，最感人的先觉得是芳乃和丛雨。一开始因为初通不能推姊妹线，所以一开始推到了 BE，有点尴尬。总体上这部游戏还是很出色的，也难怪丛雨大人那么受欢迎了（&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;http://www.yuzu-soft.com/products/senren/index.html&quot;&gt;《千恋 * 万花》官网&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;PS：问我为什么去下硬盘版不去下 steam 版？？&lt;s&gt;我找不到 NFSW 补丁啊&lt;/s&gt;&lt;/p&gt;
</content:encoded></item><item><title>NOIp 2018 复赛总结</title><link>https://npofsi-blog.vercel.app/posts/noip-tg-2018-summary/</link><guid isPermaLink="true">https://npofsi-blog.vercel.app/posts/noip-tg-2018-summary/</guid><pubDate>Thu, 15 Nov 2018 11:49:18 GMT</pubDate><content:encoded>&lt;p&gt;在这个星期日，经历了长达两天的 NOIp 复试，是时候做个总结了( ；∀；) （ tcl ）&amp;lt;del&amp;gt;( 好像 www.noi.cn 出了点问题）&amp;lt;/del&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;!-- more --&amp;gt;&lt;/p&gt;
&lt;p&gt;只有第一天前两道题能确切的拿到分啊 / 第二天的题完全没有希望：：=&lt;/p&gt;
&lt;p&gt;而且考完 NOIp 就期中考试真的没关系？&lt;/p&gt;
&lt;p&gt;总感觉自己好弱，（ dalao 们求罩了）&lt;/p&gt;
&lt;p&gt;现在还是正经点说一下我做出来的部分qwq&lt;/p&gt;
&lt;h5&gt;DAY1 T1 修路&lt;/h5&gt;
&lt;p&gt;我的思路和 luogu 的题解有些不同。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;我定义了区间数组 &lt;code&gt; int arr[100002];&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;然后用这个数组储存了整条路（区间）。&lt;/li&gt;
&lt;li&gt;写一个函数 &lt;code&gt;check()&lt;/code&gt; 判断 &lt;code&gt;arr[1]~arr[n]&lt;/code&gt; 是否全为零（已填平）。&lt;/li&gt;
&lt;li&gt;写一个函数 &lt;code&gt;fill()&lt;/code&gt; 从 &lt;code&gt;arr[1]&lt;/code&gt; 开始向后寻找连续的不为零的区段，并找出最浅的地方，并把每个地方的深度修改为 &lt;code&gt;当前深度 - 最浅深度&lt;/code&gt; （把该地向上填） 并使答案增加最浅的深度（一天填一层所以 “天数 = 深度” ）&lt;/li&gt;
&lt;li&gt;因为保证初始区间全不为零，所以使用 do-while ：&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;do{
  fill();
}while(check());
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;并没有彻底模拟每一天填的状况。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;这个算法的算法复杂度在 O(n^2) 到 O(n).
具体取决于输入数据，如果是金字塔形的就会被逼到 O(n^2) 如果是崎岖不平的而差别不太大的数据 复杂度就接近 O(n)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h5&gt;DAY1 T2 &lt;a href=&quot;https://www.luogu.com.cn/problem/P5020&quot;&gt;货币系统&lt;/a&gt;&lt;/h5&gt;
&lt;p&gt;一开始自己感觉没有思路，不过回想了一下学过的知识发现这道题就是一个很常规的筛法，如果用 dfs 的话第二个样例就吃不消了， &lt;s&gt;尝试写了一下 dp 还是太菜就放弃了&lt;/s&gt; ，最后用 dfs 只拿了60分。&lt;/p&gt;
&lt;h5&gt;DAY1 T3 &lt;a href=&quot;https://www.luogu.org/problem/P5021&quot;&gt;赛道修建&lt;/a&gt;&lt;/h5&gt;
&lt;p&gt;这道题有点思路，想到了二分，但是不会写啊，时间也很紧，然后就丢了（哭）。&lt;/p&gt;
&lt;h5&gt;DAY2 T1 旅行&lt;/h5&gt;
&lt;p&gt;这道题看了一会儿就知道要干什么了，匆匆忙忙开始写。结果写完一后只过了一个样例，另一个样例完全不符，十分头疼。半个小时也没想出来是为什么。&lt;/p&gt;
&lt;p&gt;考完后来去看别人题解，原来是要枚举边，而我枚举的是点。&lt;/p&gt;
&lt;p&gt;估计是出题人好心，测试数据中有两个是枚举点也能过得，最后拿了 20 分。&lt;/p&gt;
&lt;h5&gt;DAY2 T2 填数游戏&lt;/h5&gt;
&lt;p&gt;这道题一开始拿来还是很自信的，毕竟是规律题，做不出来还可以猜，结果我琢磨了半个小时也没琢磨明白，去猜的过程中手算也算不了几个数据，只能发现两条边上的结果是  2&amp;lt;sup&amp;gt;n&amp;lt;/sup&amp;gt; ，就只写了这么多，最后拿了 15 分虽然有些不甘心，但是总比扔掉要强。&lt;/p&gt;
&lt;h5&gt;DAY2 T3 保卫王国&lt;/h5&gt;
&lt;p&gt;这道题看完题我觉得是要用 dp 就写，接着发现是动态 dp 就瞬间放弃，我 dp 本来就不熟练，加点东西就更废了，时间也不够了，这道题也只好扔掉了。&lt;/p&gt;
</content:encoded></item><item><title>C++ 将数字转换成字符串</title><link>https://npofsi-blog.vercel.app/posts/cpp-int-to-string/</link><guid isPermaLink="true">https://npofsi-blog.vercel.app/posts/cpp-int-to-string/</guid><pubDate>Sun, 09 Sep 2018 07:23:21 GMT</pubDate><content:encoded>&lt;p&gt;&lt;s&gt;今天碰到一个需要高效的将数字转换成字符串并连接起来的题，本来想用 &lt;code&gt;+&apos;0&apos;&lt;/code&gt; 的办法 但这样效率实在不高，而我想知道有没有更简便的办法同时做到这两个工作，我就想 cpp 的流是不是能为我所用。果然，我找到了 &lt;code&gt;&amp;lt;sstream&amp;gt;&lt;/code&gt; 这个头，其中的 &lt;code&gt;stringstream&lt;/code&gt; 正合我意。于是就有了以下的转换操作：&lt;/s&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;stringstream sst ;
for(...) sst&amp;lt;&amp;lt;i ;
string str = sst.str() ;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;意识到实际上 &lt;code&gt;sscanf(...)&lt;/code&gt; 就行了&lt;/p&gt;
</content:encoded></item><item><title>博客转移Github并使用Ci集成</title><link>https://npofsi-blog.vercel.app/posts/move-blog-to-github/</link><guid isPermaLink="true">https://npofsi-blog.vercel.app/posts/move-blog-to-github/</guid><pubDate>Mon, 30 Jul 2018 01:47:21 GMT</pubDate><content:encoded>&lt;p&gt;用了一上午的时间用Travis-CI和Github实现了博客持续集成。&lt;/p&gt;
&lt;p&gt;实现方法见: &lt;s&gt;&lt;a href=&quot;https://blog.nfz.moe/archives/hexo-auto-deploy-with-travis-ci.html&quot;&gt;nfz的博客&lt;/a&gt;&lt;/s&gt;(已失效)&lt;/p&gt;
</content:encoded></item><item><title>查询 MAC 地址</title><link>https://npofsi-blog.vercel.app/posts/research-mac-address/</link><guid isPermaLink="true">https://npofsi-blog.vercel.app/posts/research-mac-address/</guid><description>使用 OUI 查询 MAC 地址</description><pubDate>Thu, 17 May 2018 04:57:28 GMT</pubDate><content:encoded>&lt;p&gt;MAC 是 IEEE 规定的国际网卡标识号码，只有获得 MAC 地址字段的厂家才可以生产网卡。
这个字段是公开的（即号码的前六位）储存在一个 oui.txt 文件里， IEEE 内部是用 PHP 解析的，这个文件标注了字段所属公司的名称，地址等信息.如果需要可以访问：
&lt;a href=&quot;http://standards-oui.ieee.org/oui/oui.txt&quot;&gt;oui.txt&lt;/a&gt;&lt;/p&gt;
</content:encoded></item></channel></rss>