<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>YOLOのBLOG</title>
  
  
  <link href="https://blog.felicx.eu.org/atom.xml" rel="self"/>
  
  <link href="https://blog.felicx.eu.org/"/>
  <updated>2026-04-11T14:35:00.000Z</updated>
  <id>https://blog.felicx.eu.org/</id>
  
  <author>
    <name>felicx</name>
    
  </author>
  
  <generator uri="https://hexo.io/">Hexo</generator>
  
  <entry>
    <title>人生如钓鱼，得失且随缘</title>
    <link href="https://blog.felicx.eu.org/4147028692.html"/>
    <id>https://blog.felicx.eu.org/4147028692.html</id>
    <published>2026-04-11T14:25:55.000Z</published>
    <updated>2026-04-11T14:35:00.000Z</updated>
    
    <content type="html"><![CDATA[<p>今天朋友硬拉我去钓鱼。说实话，我这种坐不住的人，对“一动不动等鱼上钩”这事一直提不起兴趣。但架不住他再三保证“钓场环境特别好，就当去郊游”，我还是去了。</p><p>结果，真香了。当然，这是后话。</p><hr><p>从下午三点坐到六点，我的鱼漂像钉在水面上一样，纹丝不动。朋友那边倒是断断续续上了几条小鲫鱼，我这边连个试探的动静都没有。</p><p>说实话，心态有点崩。</p><p>正当我准备收竿走人，跟朋友说“算了算了，我跟鱼没缘分”的时候，鱼漂突然猛地一沉。我下意识扬竿——手里传来一股挣扎的力道，不大，但很真实。</p><p>一条黄骨鱼，巴掌长，估摸着二三两的样子。</p><p>金黄色的身子，三根尖刺竖得笔直，在夕阳下泛着光。我小心翼翼地去摘钩，被它背上的刺扎了一下，疼得龇牙咧嘴。</p><p>朋友在旁边笑：“第一次被鱼欺负，记着点。”</p><p>但我顾不上疼，盯着桶里那条小小的黄骨鱼，莫名有种成就感。二三两，对老钓友来说可能连“鱼获”都算不上，但对我来说，那是整个下午的等待换来的答案。</p><hr><p>就在我收竿的时候，钓位隔壁的一个老哥突然站了起来，鱼竿弯成一张满弓。</p><p>“大的！”他低声喊了一句，声音里压着兴奋。</p><p>所有人都看了过去。鱼线在水里切出一道道水纹，鱼在水下拼命往外窜，老哥稳稳控着竿，一会儿放线，一会儿收线，跟那条鱼较着劲。</p><p>遛了得有五六分钟，鱼终于被拉到近岸，翻了个身——好大一条鲤鱼，目测七八斤往上，浑身鳞片金灿灿的。</p><p>朋友递过抄网，老哥弯腰去捞。就在抄网快碰到鱼的瞬间，那鱼猛地一个甩尾，啪的一声，子线断了。</p><p>鱼消失在浑浊的水里，像从没出现过一样。</p><p>整个钓场安静了两秒。老哥直起身，看着水面，愣了好一会儿。然后他从兜里摸出烟，点上，深深吸了一口，一句话没说。</p><p>那支烟燃完，他开始默默收竿，动作很慢，像是在消化什么。</p><hr><p>开车回去的路上，我一直在想这件事。</p><p>我花了三个多小时，只钓到一条二三两的黄骨鱼，但高兴得不行。隔壁老哥遛了那么久，眼看大鱼到手，最后却一场空，连个影都没留下。</p><p><strong>同样是一条鱼，有的人得到了小的，却满心欢喜；有的人差点得到大的，反而满腹遗憾。</strong></p><p>但换个角度想——</p><p>我得到的那条黄骨鱼，真的是因为我技术好吗？不是，多半是运气，是那条鱼恰好在那个时间点路过了我的钩子。而老哥失去的那条大鱼，是因为他操作失误吗？也不全是，野生的大家伙，力气大、经验足，断线跑鱼在钓鱼这件事里太常见了。</p><p><strong>钓鱼这件事，本质上充满了不确定性。你无法决定鱼什么时候来，来的是大鱼还是小鱼，来了之后会不会跑掉。你能做的，就是把竿抛出去，保持耐心，然后接受结果。</strong></p><p>这不就是人生吗？</p><p>我们总以为，努力了就一定有回报，准备好了就一定能抓住机会。但现实是，很多东西并不完全由我们掌控。有时候你拼尽全力，结果还是差那么一点；有时候你根本没抱希望，好运却自己找上门来。</p><p>那个老哥抽完烟默默收竿的样子，让我印象特别深。没有摔竿子，没有骂骂咧咧，就是沉默地接受了一切。</p><p>我想，这大概就是老钓友和新手的区别吧。</p><p>新手为一两二的鱼欢呼，老手放生七八斤的鱼也面不改色。不是不爱，是知道得失本就是常态。<strong>能享受等待的过程，也能接受空手而归的结果，这才是钓鱼真正的门槛。</strong></p><hr><p>到家后，我把那条黄骨鱼放进了公司边上的小河里。</p><p>朋友问：“好不容易钓的，怎么不放？”</p><p>我说：“它陪我完成了人生第一次，就够了。”</p><p>其实我也说不清为什么，就是觉得，有些东西，经历过比拥有更重要。</p><p>就像那个老哥，他虽然没有把鱼带回家，但那五六分钟里，鱼竿传来的每一阵颤动、鱼线切水的每一声呼啸，都是真实发生过的。大鱼带走了他的鱼钩，但带不走那个傍晚的惊心动魄。</p><p>鱼没上岸，可故事上岸了。</p><p>第一次钓鱼，我只带走了一个故事，和一碗黄骨鱼豆腐汤的想象。但我觉得，这已经是最好的结局了。</p>]]></content>
    
    
    <summary type="html">人生如钓鱼，得失皆是常态，能享受等待，也能接受空手，才算真正挥过竿。</summary>
    
    
    
    <category term="随笔" scheme="https://blog.felicx.eu.org/categories/%E9%9A%8F%E7%AC%94/"/>
    
    
    <category term="钓鱼" scheme="https://blog.felicx.eu.org/tags/%E9%92%93%E9%B1%BC/"/>
    
    <category term="人生" scheme="https://blog.felicx.eu.org/tags/%E4%BA%BA%E7%94%9F/"/>
    
  </entry>
  
  <entry>
    <title>Windows下Git与SSH完整配置指南</title>
    <link href="https://blog.felicx.eu.org/640658915.html"/>
    <id>https://blog.felicx.eu.org/640658915.html</id>
    <published>2026-02-02T14:17:29.000Z</published>
    <updated>2026-02-02T14:36:00.000Z</updated>
    
    <content type="html"><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>明明安装了Git，却在克隆仓库时接连遭遇“命令无法识别”和“权限被拒绝”？本文将带你系统解决 Windows环境下Git与SSH配置的完整难题。</p><h2 id="问题"><a href="#问题" class="headerlink" title="问题"></a>问题</h2><p>在Windows Terminal中尝试克隆GitHub仓库时，我遇到了两个经典问题：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 第一阶段：Git命令不存在</span></span><br><span class="line">git : 无法将“git”项识别为 cmdlet、函数、脚本文件或可运行程序的名称...</span><br><span class="line"></span><br><span class="line"><span class="comment"># 第二阶段：SSH密钥认证失败</span></span><br><span class="line">git@github.com: Permission denied (publickey).</span><br><span class="line">fatal: Could not <span class="built_in">read</span> from remote repository.</span><br></pre></td></tr></table></figure><p>这两个问题分别对应着不同的配置层面，需要系统性地解决。下面是我解决问题的完整流程。</p><h2 id="解决"><a href="#解决" class="headerlink" title="解决"></a>解决</h2><h3 id="解决Git命令识别问题"><a href="#解决Git命令识别问题" class="headerlink" title="解决Git命令识别问题"></a>解决Git命令识别问题</h3><h4 id="1-检查Git安装状态"><a href="#1-检查Git安装状态" class="headerlink" title="1. 检查Git安装状态"></a>1. 检查Git安装状态</h4><p>首先需要确认Git是否真的已经安装。在PowerShell中运行：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git --version</span><br></pre></td></tr></table></figure><p>如果返回版本号（如 <code>git version 2.41.0</code>），说明Git已正确安装且PATH配置正常。如果提示“不是内部或外部命令”，则需要重新安装。</p><h4 id="2-正确安装Git-for-Windows"><a href="#2-正确安装Git-for-Windows" class="headerlink" title="2. 正确安装Git for Windows"></a>2. 正确安装Git for Windows</h4><p>访问 <strong><a href="https://git-scm.com/">Git官方网站</a></strong> 下载安装程序。在安装过程中，<strong>最关键的一步</strong>出现在配置PATH环境变量时：</p><p><strong>务必选择</strong>：<em>“Use Git from the command line and also from 3rd-party software”</em></p><p>这个选项会让安装程序自动将Git的可执行文件路径添加到系统的PATH环境变量中，这是解决命令识别问题的关键。</p><h4 id="3-手动配置PATH（如已安装但未识别）"><a href="#3-手动配置PATH（如已安装但未识别）" class="headerlink" title="3. 手动配置PATH（如已安装但未识别）"></a>3. 手动配置PATH（如已安装但未识别）</h4><p>如果Git已安装但命令仍不可用，需要手动添加Git路径到系统PATH：</p><ol><li><code>Win + S</code>搜索“环境变量”，选择“编辑系统环境变量”</li><li>点击“环境变量”按钮</li><li>在“系统变量”部分找到并选中<code>Path</code>，点击“编辑”</li><li>点击“新建”，添加Git的<code>bin</code>目录路径，通常为：</li></ol><ul><li><code>C:\Program Files\Git\bin</code></li><li><code>C:\Program Files\Git\cmd</code>（某些版本）</li></ul><p><strong>重要提示</strong>：修改PATH后，必须<strong>关闭所有终端窗口并重新打开</strong>，更改才会生效。</p><h3 id="配置SSH密钥认证"><a href="#配置SSH密钥认证" class="headerlink" title="配置SSH密钥认证"></a>配置SSH密钥认证</h3><p>Git命令可用后，使用SSH协议克隆仓库时出现了第二个问题：公钥认证失败。这是因为本地没有配置SSH密钥对，或者公钥未添加到GitHub账户。</p><h4 id="1-检查现有SSH密钥"><a href="#1-检查现有SSH密钥" class="headerlink" title="1. 检查现有SSH密钥"></a>1. 检查现有SSH密钥</h4><p>在PowerShell中查看是否已有SSH密钥：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 查看.ssh目录内容</span></span><br><span class="line"><span class="built_in">dir</span> <span class="variable">$env</span>:USERPROFILE\.ssh</span><br><span class="line"></span><br><span class="line"><span class="comment"># 正常情况应看到类似以下文件：</span></span><br><span class="line"><span class="comment"># id_ed25519      # 私钥（保密！）</span></span><br><span class="line"><span class="comment"># id_ed25519.pub  # 公钥（可分享）</span></span><br><span class="line"><span class="comment"># known_hosts     # 已知主机记录</span></span><br></pre></td></tr></table></figure><p>如果只有<code>known_hosts</code>文件（记录已连接过的主机信息），说明还没有生成个人密钥对。</p><h4 id="2-生成新的SSH密钥对"><a href="#2-生成新的SSH密钥对" class="headerlink" title="2. 生成新的SSH密钥对"></a>2. 生成新的SSH密钥对</h4><p>使用Ed25519算法生成新的密钥对（比传统的RSA更安全且高效）：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">ssh-keygen -t ed25519 -C <span class="string">&quot;your_email@example.com&quot;</span></span><br></pre></td></tr></table></figure><p>按提示操作：</p><ul><li><strong>保存路径</strong>：直接回车使用默认位置 (<code>C:\Users\你的用户名\.ssh\id_ed25519</code>)</li><li><strong>设置密码</strong>：可为空（直接回车），或设置增强安全性</li></ul><p>成功后会看到密钥的“随机艺术图案”，这是密钥的可视化表示。</p><h4 id="3-管理SSH代理服务"><a href="#3-管理SSH代理服务" class="headerlink" title="3. 管理SSH代理服务"></a>3. 管理SSH代理服务</h4><p>SSH代理可以帮助管理密钥，避免每次操作都需输入密码。</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 方案A：如果只需临时使用，启动代理而不修改服务</span></span><br><span class="line">Start-Service ssh-agent -ErrorAction SilentlyContinue</span><br><span class="line"></span><br><span class="line"><span class="comment"># 方案B：需要永久配置时，以管理员身份运行</span></span><br><span class="line"><span class="comment"># 1. 右键点击Windows Terminal/PowerShell，选择“以管理员身份运行”</span></span><br><span class="line"><span class="comment"># 2. 执行以下命令：</span></span><br><span class="line">Get-Service ssh-agent | Set-Service -StartupType Manual -PassThru | Start-Service</span><br><span class="line"><span class="comment"># 3. 关闭管理员终端，返回普通终端继续操作</span></span><br></pre></td></tr></table></figure><h4 id="4-将私钥添加到SSH代理"><a href="#4-将私钥添加到SSH代理" class="headerlink" title="4. 将私钥添加到SSH代理"></a>4. 将私钥添加到SSH代理</h4><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 添加生成的Ed25519密钥到代理</span></span><br><span class="line">ssh-add <span class="variable">$env</span>:USERPROFILE\.ssh\id_ed25519</span><br><span class="line"></span><br><span class="line"><span class="comment"># 如果添加成功，将看到“Identity added”确认信息</span></span><br></pre></td></tr></table></figure><h4 id="5-复制公钥到剪贴板"><a href="#5-复制公钥到剪贴板" class="headerlink" title="5. 复制公钥到剪贴板"></a>5. 复制公钥到剪贴板</h4><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 查看公钥内容（以手动复制）</span></span><br><span class="line"><span class="built_in">type</span> <span class="variable">$env</span>:USERPROFILE\.ssh\id_ed25519.pub</span><br></pre></td></tr></table></figure><p>公钥内容看起来像这样（单行）：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJL3p2jZh7T2p3FQ5Qp7m2d6JKLp0E2UqJtB your_email@example.com</span><br></pre></td></tr></table></figure><h4 id="6-将公钥添加到GitHub账户"><a href="#6-将公钥添加到GitHub账户" class="headerlink" title="6. 将公钥添加到GitHub账户"></a>6. 将公钥添加到GitHub账户</h4><ol><li>登录GitHub，点击右上角头像 → <strong>Settings</strong></li><li>左侧边栏选择 <strong>SSH and GPG keys</strong></li><li>点击 <strong>New SSH key</strong></li><li><strong>Title</strong>：起一个识别名称（如“Windows Desktop 2024”）</li><li><strong>Key</strong>：粘贴刚才复制的公钥内容（完整一行）</li><li>点击 <strong>Add SSH key</strong> 完成添加</li></ol><h3 id="测试与验证"><a href="#测试与验证" class="headerlink" title="测试与验证"></a>测试与验证</h3><h4 id="1-测试SSH连接"><a href="#1-测试SSH连接" class="headerlink" title="1. 测试SSH连接"></a>1. 测试SSH连接</h4><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">ssh -T git@github.com</span><br></pre></td></tr></table></figure><p>首次连接会看到主机验证提示：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">The authenticity of host &#x27;github.com (20.205.243.166)&#x27; can&#x27;t be established.</span><br><span class="line">ED25519 key fingerprint is SHA256:+DiY3wvvV6TuJJhbpZisF/zLDA0zPMSvHdkr4UvCOqU</span><br><span class="line">Are you sure you want to continue connecting (yes/no/[fingerprint])?</span><br></pre></td></tr></table></figure><p>输入 <strong><code>yes</code></strong> 继续，成功后显示：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Hi 你的用户名! You&#x27;ve successfully authenticated, but GitHub does not provide shell access.</span><br></pre></td></tr></table></figure><h4 id="2-最终克隆测试"><a href="#2-最终克隆测试" class="headerlink" title="2. 最终克隆测试"></a>2. 最终克隆测试</h4><p>现在可以成功克隆最初失败的仓库了：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git <span class="built_in">clone</span> git@github.com:FelicxFoster/MyHexo.git</span><br></pre></td></tr></table></figure><h3 id="问题排查与进阶技巧"><a href="#问题排查与进阶技巧" class="headerlink" title="问题排查与进阶技巧"></a>问题排查与进阶技巧</h3><ol><li>如果仍然失败：详细调试模式</li></ol><p>使用<code>-v</code>（verbose）参数查看SSH连接详细信息：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">ssh -T -v git@github.com</span><br></pre></td></tr></table></figure><p>这会显示详细的连接过程，帮助定位问题所在。</p><ol start="2"><li>Windows权限问题修复</li></ol><p>如果遇到私钥权限问题，可以修复文件权限：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">icacls <span class="variable">$env</span>:USERPROFILE\.ssh\id_ed25519 /inheritance:r /grant:r <span class="string">&quot;<span class="variable">$env</span>:USERNAME:R&quot;</span></span><br></pre></td></tr></table></figure><ol start="3"><li>如果你有多个Git服务账户（如GitHub、GitLab等），可以创建<code>~/.ssh/config</code>文件管理不同主机的密钥：</li></ol><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># ~/.ssh/config</span></span><br><span class="line">Host github.com</span><br><span class="line">    HostName github.com</span><br><span class="line">    User git</span><br><span class="line">    IdentityFile ~/.ssh/id_ed25519_github</span><br><span class="line">    IdentitiesOnly <span class="built_in">yes</span></span><br><span class="line"></span><br><span class="line">Host gitlab.com</span><br><span class="line">    HostName gitlab.com</span><br><span class="line">    User git</span><br><span class="line">    IdentityFile ~/.ssh/id_ed25519_gitlab</span><br><span class="line">    IdentitiesOnly <span class="built_in">yes</span></span><br></pre></td></tr></table></figure><h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><ol><li><strong>Git命令识别</strong>：确保Git正确安装且<code>bin</code>目录已添加到系统PATH</li><li><strong>SSH密钥生成</strong>：使用<code>ssh-keygen -t ed25519</code>生成密钥对</li><li><strong>代理服务管理</strong>：启动<code>ssh-agent</code>服务并添加私钥</li><li><strong>公钥添加</strong>：将<code>.pub</code>文件内容完整添加到GitHub的SSH keys设置</li><li><strong>连接测试</strong>：使用<code>ssh -T git@github.com</code>验证配置成功</li></ol><p>遵循这个系统性的配置流程，你将彻底解决Windows下Git与SSH的配置问题，享受流畅的代码克隆与管理体验。</p>]]></content>
    
    
    <summary type="html">从头配置git和ssh</summary>
    
    
    
    <category term="开发运维" scheme="https://blog.felicx.eu.org/categories/%E5%BC%80%E5%8F%91%E8%BF%90%E7%BB%B4/"/>
    
    
    <category term="git" scheme="https://blog.felicx.eu.org/tags/git/"/>
    
  </entry>
  
  <entry>
    <title>争议VLA：小鹏、理想向左 华为向右（转）</title>
    <link href="https://blog.felicx.eu.org/2384731467.html"/>
    <id>https://blog.felicx.eu.org/2384731467.html</id>
    <published>2026-02-01T14:35:06.000Z</published>
    <updated>2026-02-02T14:10:00.000Z</updated>
    
    <content type="html"><![CDATA[<p>2025年12月22日，长城汽车发布魏牌蓝山智驾进阶版，完成了VLA上车元年的收官。</p><p>这一年，理想、小鹏、奇瑞和长城都已经推出了自己的VLA量产车型，小米、零跑等公司也有相关布局。</p><p>VLA显然成为了2025年的汽车圈热词。它得益于深度集成进神经网络的“视觉-语言-动作”模块，可以像GPT一样对驾驶行为进行深度思考，通过“直行变为红灯-红灯需要停车-控制刹车”的逻辑推理，和人类一样凭借认知去开车。</p><p><img src="/assets/post/20260201_RJ9QZFHE.webp" alt="图片"></p><p>10月底，一张手机拍摄的PPT照片突然在网络疯传，显示FSD V14已加入思维链</p><p>事实上，特斯拉经历完表现平平的FSD V13，也选择放弃坚持两年的端到端路线，FSD V14版本已改为类似VLA的架构。</p><p>这也是针对FSD V13的黑箱困境——即使参数量相比V12提高3倍，仍然无法杜绝闯红灯、逆行等低级问题。</p><p>特斯拉FSD引入VLA之后，小鹏汽车董事长兼CEO何小鹏近期也是亲赴北美体验，随后公开喊话FSD V14和特斯拉Robotaxi已经没有区别，L2和L4可用同一套系统实现。</p><p>理想汽车智能驾驶研发高级副总裁郎咸朋也发文表示，VLA就是自动驾驶最好的模型方案。</p><p>但即使如此，VLA的反对声音依旧不少。</p><p>“华为不会走向VLA的路径，我们认为这样看似取巧，其实并不是走向真正自动驾驶的路径。华为更看重WA，也就是World Action，中间省掉language这个环节。”华为智能汽车解决方案BU CEO靳玉志说道。</p><p>这指向的正是VLA的最大弊端，依靠语言模型进行推理，就需要视觉到语言、语言到动作的两次翻译，而翻译就会导致误差，反应也更慢。何小鹏也曾坦言：“一段1200多字的文字描述，也无法精准地‘翻译’一个十几秒视频。”</p><p>因此，有人认为“世界模型“才是未来。可以将这理解成是一种极致的端到端，既能用输入信息直接映射输出结果，又完全理解真实世界的运行规律，瞬间响应且完全可靠。</p><p>只是做出这样一个模型，需要给它灌输和世界有关的全部知识（参数量），再用难以计数的算力支持思考。因此，世界模型目前还只能部署在云端，难以上车。</p><p>“我认为想要做百辆无人车以上，世界模型最关键，对其他公司可能做VLA模型卖车最关键，大家选择不同的路线是因为目标不同。”小马智行CTO楼天城表示。</p><p><strong>NO.1</strong></p><p><strong>[</strong> <strong><strong>端到端很颠覆，但并不完美</strong></strong><strong>]</strong></p><p>2023年8月26日，马斯克在个人社交账号开启了一场45分钟的直播：乘坐一辆老款特斯拉Model S游览旧金山，自己只做安全员，交给仅有2000多行代码的FSD Bate V12系统驾驶。</p><p><img src="/assets/post/20260201_tCD8FvpZ.webp" alt="图片"></p><p>FSD Bate V12误闯红灯，马斯克随后接管</p><p>从他全程举起的手机视角可以看到，这辆Model S顺利通过无保护左转、斑马线、施工区域等复杂场景，遇到行人还可以等待让行，几乎可以应对全部路况，只有一次红灯没有停车而被接管。</p><p>这种能力在当年还很少见，只是大家更关心另外一个问题：为什么2000多行代码就能做出这样的效果？之前那么多年都在干什么？</p><p>“V12系统从头到尾都是通过AI实现。我们没有编程，没有让程序员写任何一行代码来识别道路、行人等。全部交给了神经网络。”马斯克在直播时回应。</p><p>他所指的编程代码正是人工规则，这也是提升早期智驾系统能力的原始途径。</p><p>当时采用的分治算法，就相当于一个“人工智障”机器人，只有执行能力，无法自主思考。</p><p>所以，想让它认识一个物体，知道红灯应该停车，都需要像对待“智障”一样，通过人工规则一条一条的教会它。教会的越多，能力也就越强。直到2024年，依然有人采用这种路径。</p><p>根据何小鹏2024年AI Day上的说法，如果要做到无限接近人类驾驶员的水平，需要大约10亿条规则，而当时稳定的系统只有10万条左右，相当于只完成了万分之一。</p><p>也就说明，想通过人力穷尽所有场景，写完所有规则，着实有些异想天开，所以特斯拉转向了端到端。</p><p><img src="/assets/post/20260201_GEcwrqZp.webp" alt="图片"></p><p><em>chib__p</em> s,<em>singh</em> p. <em>recent__advancements</em> in <em>end-to-end__autonomous__driving</em> using deep learning:a survey</p><p>比起分治算法，端到端就聪明多了，能像小孩子一样拥有模仿学习的能力。因此，不再需要依靠人工规则死记硬背，给它观看大量人类的驾驶视频，就能自发将环境和驾驶行为联系起来，找出“这种场景下，应该这么做”的规律。</p><p>端到端的架构也非常简化。尤其是特斯拉的一段式，从传感器图像输入到控制指令输出，中间只经过深度学习模型，取消了全部人工规则，代码量骤减99%。</p><p>尽管这是一个更有前景的方案，但同样存在弊端：只能看到观测到输入和输出两组数据，中间工作就像在一个不透明的黑色箱体里进行，完全不可见，出现错误也就难以追溯到是哪个环节导致的。</p><p>这也是困扰端到端的一个主要问题，传统方案每一步运算清晰可见，出现问题可以直接优化具体模块。而端到端出现问题，因为不清楚出错的地方，就只能不断投喂正确应对这一场景的优质视频片段，祈祷它能照着学习。效率低不说，效果还不可控。</p><p>所以，如果想解决这个问题，是不是可以想办法将它的决策过程翻译出来？基于这种思路，最先出现的是端到端+VLM。</p><p><img src="/assets/post/20260201_wdnO9mvo.webp" alt="图片"></p><p>理想汽车端到端+VLM</p><p>VLM即视觉-语言模型，相当于给端到端套了一个思维链“外挂”：同步接收传感器、导航等输入数据，利用语言模型（类似GPT）的推理能力，生成类似“路口变为红灯，应该减速停车”的场景描述和处理意见，给端到端的行为做出备注，极端场景或许还能参考它的靠谱决策。</p><p>这种方式解决了端到端缺少显式表达的问题，但VLM本质上还是一个独立运行的模型，跟不上端到端的节奏（端到端做完5次决策，VLM可能只来得及生成1个文本）。</p><p>因为运算的慢，VLM的决策虽然最终也会传给端到端，但非复杂场景都会直接忽略，仅作为棘手问题的兜底，联合训练和优化也没预想中那么好做。</p><p>如果参照“感知-判断-决策-控制”智能驾驶传统框架，VLM其实只能覆盖前三个阶段，缺乏对控制过程的理解。要是问题出在控制上，VLM就会因为看不懂控制信号这种“外语”，无法分析原因，进行优化。</p><p>因此，一个看起来更加合理的架构就出现了：再加上生成控制信号的能力，就可以打通最后一环，通过自动化的数据闭环，实现低成本、高效率的自我迭代了。</p><p>这就是VLA视觉-语言-动作模型。</p><p><strong>NO.2</strong></p><p><strong>[</strong> <strong><strong>VLA一边拆黑箱，一边加重担</strong></strong><strong>]</strong></p><p>如果用一句话描述：VLA既有全程可求导的端到端神经网络形式，又有大语言模型的推理能力。</p><p><img src="/assets/post/20260201_6E6dOSyP.webp" alt="图片"></p><p>元戎启行对比VLA与端到端+VLM</p><p>能力上，它可以看做VLM的进化，补全了动作特征的对齐。动作解码器（A）的引入，使语言模型不必再局限于生成类似“向右变道”这种难以对应为轨迹的文本，而是可以换成一种更加简洁高效，代表方向盘转角、刹车幅度等具体动作的特性向量，消除了自然语言和控制信号间的语义鸿沟。</p><p>这也意味着从感知到控制的每一个步骤，都是采用了可微分的数学计算，没有了之前的抽象语义理解过程，做到了推理过程的全程可求导。在遇到不如预期的驾驶行为时，就能从控制信号反向追溯错误源头，进行优化。</p><p>架构上，它又与VLM有着本质的不同。视觉感知、语言理解和动作生成都不再是外挂模块，而是深度集成进统一的神经网络，因此语言模型也可以顺理成章发挥更大作用：原本只是解释端到端的行为，现在已经集成进模型中，不如直接让它来主导决策。</p><p>这样的提升无疑是巨大的，语言模型擅长推理，可以像老司机一样用脑思考决策。而传统端到端只能不断拟合视频片段中的行为，寻找表面规律，缺乏底层的智能逻辑。</p><p>2025年，VLA已经出现了爆发式增长，理想、小鹏、奇瑞和长城等都已经走在这条道路上。就连最早将端到端落地到车的特斯拉，也引入了类似的架构——通过全景分割结果、3D占用结果、3D高斯渲染结果、语言信息等中间输出结果，对最终轨迹进行推理。</p><p>既然得到了广泛认同，那VLA能否成为智能驾驶的最终解呢？也不尽然，它仍然存在短板。</p><p><img src="/assets/post/20260201_f6yatSQ7.webp" alt="图片"></p><p>OpenVLA模型_架构_</p><p>VLA依赖于中间的大语言模型，而语言模型只能理解和输出token（可以是自然语言，也可以是一串符号），所以处理每一帧画面，就需要一次将“全部输入数据转化为token进行推理，再将token转化为控制信号”的完整过程。</p><p>然而，不管是传输数据、token转化，还是推理过程，都会带来巨大的算力消耗与带宽吞吐量压力，这恰恰是追求毫秒级响应的智驾系统最不想看到的。</p><p>以理想为例，曾经布置在单颗Orin-X芯片上的VLM只能以3Hz左右运行，MindVLA虽然换上了新一代Thor-U，并通过MoE架构、Sparse Attention等稀疏化设计降低推理负担，但也只提升到10Hz左右，和传统端到端的运行频率差距仍然明显。</p><p>实际上，这已经是VLA摒弃自然语言，转而采用一种信息含量更高、能隐式表达的抽象token，从而显著降低算力开销和延迟后的结果了。只是，这种token虽然具有更高的保真度，但仍无法避免原始信息的损失。</p><p>何小鹏曾举例量化这一过程的难度：“VLA模型中间涉及两次语言转换，这会带来大量信息损耗。比如，一段1200多字的文字描述，也无法精准地‘翻译’一个十几秒视频。”</p><p>一些热门AI视频创作者也有类似的抱怨，用语言还原自己想象中的画面非常困难。即使是仅有几秒的特效视频，也要用写满一整页Word的文字去约束生成效果，而这第一次得到的往往还不是想要的画面，还要再调整很多回。</p><p><strong>NO.3</strong></p><p><strong>[ VLA会是最终路线吗？****]</strong></p><p>尽管目前还没有达成未来路线的共识，但已经出现了很多新的尝试。</p><p>第一个方向是保持VLA路线，但要破解现有问题。</p><p>理想汽车针对VLA算力消耗大、运行频率不足的痛点，已经提出了降低模型精度，将运行频率提高到20Hz的想法。</p><p>这种优化逻辑可以简单理解为：目前主流的FP8&#x2F;INT8精度，每个计算节点都需要占用1字节资源。优化为FP4精度后，仅需0.5字节即可完成同等运算，相当于在相同硬件资源与时间成本下完成翻倍任务，间接让TOPS（每秒能执行的基本操作次数）提升1倍。</p><p>这一想法的出现，也是因为英伟达首次在智驾芯片的架构层兼容了FP4精度。如果实现，可由目前INT8和FP8混合推理精度下的700TOPS稠密算力，提升至近3倍的2000TOPS（需要配合稀疏优化）等效稀疏算力。</p><p>只是，目前业界还缺乏能被认可的FP4标准格式，贸然降低精度容易造成模型性能崩溃，还是一个漫长的技术攻坚。</p><p>第二个方向是针对现有架构的问题，直接在架构层做出调整。</p><p><img src="/assets/post/20260201_V1IXcPso.webp" alt="图片"></p><p>小鹏准备在下季度发布的VLA 2.0方案，将架构从V-L-A改成了V&#x2F;L-A。</p><p>这样带来的好处是，VLA 2.0不再依靠语言模型进行推理，输入和输出数据都不用再进行额外的token转化。而是和传统端到端一样，输入传感器数据，直接就能映射出控制信号，不仅显著减低延迟，还能减少两次翻译中的信息损耗。</p><p>华为WA车端世界行为模型则走了另外一条路，VLA是通过大语言模型解决端到端的黑箱问题，而WA则是在端到端的映射过程中，引入多模态输入和MoE多专家能力，通过 “信息补全” 和 “逻辑约束” 降低不确定性。</p><p>多模态输入很好理解，核心是通过综合多种类型信息，增加对周围环境的了解渠道。这样在决策时，就可以用多源数据进行交叉验证，缓解黑箱问题。</p><p><strong>另外，端到****端黑箱问题也集中于超出训练的极端场景，而华为在ADS4.0上增加了更多的激光雷达、4D毫米波雷达等传感器，实际也有助于在这类陌生环境中捕捉到关键特征。通过多模态输入，也能减小模型的推理盲区。</strong></p><p>MoE则是一种分布式的架构设计，它将传统的单一大模型，拆分成一个门控网络和多个专家模型。工作时可以单独调用对应的专家模型，无需激活所有参数。这些将会带来两个直观好处。</p><p><img src="/assets/post/20260201_JUwoJIrP.webp" alt="图片"></p><p><strong>DeepSeekMoE架构</strong></p><p>第一是，不同于传统单一大模型倾向于平均行为，难以兼顾所有情况，每个专家模型都可以独立训练，专精于某一类特定场景或技能。这样在处理一些复杂问题时，就能调用最适配的专家，摆脱单一模型的 “通用妥协”，从而<strong>提高长尾场景的适配能力与决策精度</strong>。</p><p>第二是，MoE的<strong>稀疏激活机制，使总参数量限制也得到了缓解。原本的单一大模型要严格受到车端芯片算力限制，而MoE只需要保证被调用的部分专家能流畅运行即可。这意味着，被调用专家的参数量之和，就可以逼近传统单一大模型的总参数量，通过扩容使性能增强，同样可提高决策的准确性。</strong></p><p>华为智能汽车解决方案BU CEO靳玉志认为，VLA是一种取巧方案，不是真正走向自动驾驶的路径。华为更看重WA世界行为模型，用多模态输入数据直接生成控制信号，省掉所有的语言转译环节。</p><p><strong>NO.4</strong></p><p><strong>[</strong> <strong><strong>写在最后</strong></strong><strong>]</strong></p><p>除了现有的流派，宇树科技创始人、CEO、CTO王兴兴还提出过另一个方法：生成一段动作视频，然后让机器人去模仿执行。他认为这一路线可能比VLA发展更快，收敛概率更大。</p><p>这正是通过世界模型实现自动驾驶的概念：根据输入画面，运用物理规律过滤冗余信息，推理出未来发生的众多可能，然后寻找出最优路径，生成这段未来场景的长视频，再让车辆照做。</p><p>只是这有个必要前提，想让生成的视频可靠，放心交给车辆执行，就要模型拥有真正理解物理世界的能力，也意味着需要异常夸张的参数量和算力支持。</p><p>以小鹏为例，其部署在云端的世界模型参数量已经达到720亿，需要3万卡算力集群，而车端VLA即使通过三颗图灵AI芯片拥有2250TOPS算力，也只放了几十亿参数量。</p><p><img src="/assets/post/20260201_Ow4GfiD2.webp" alt="图片"></p><p>商汤绝影强化学习路线</p><p>因此，世界模型现阶段主要还是用于车端模型的仿真训练。比如，主动生成现实中难以采集到的极端场景进行强化学习，提高系统的泛化能力；设立一个奖励函数，让系统决策在安全、效率和舒适等因素之间找到最优轨迹。</p><p>这种方式也确实取得了比较理想的结果，通过世界模型解决困扰已久的长尾场景收敛问题，要比依靠现实场景的数据闭环高效得多。而布局世界模型也已成为了目前自动驾驶界最为统一的行为，无论车端采用的是哪种方案。</p><p>实际上，这也反映出了另一个层面的问题，大家对于新技术从来不是抗拒的，只是出于市场竞争的需求进行标签化。而在背后，不同路线的框架已经有了很多相同的部分，就像很多人都在用云端世界模型、MoE架构，小鹏VLA 2.0也能和华为WA一样直接映射结果。</p><p>不同的路线，似乎不是看起来那样水火不相融。</p>]]></content>
    
    
    <summary type="html">通俗易懂介绍VLA</summary>
    
    
    
    <category term="自动驾驶" scheme="https://blog.felicx.eu.org/categories/%E8%87%AA%E5%8A%A8%E9%A9%BE%E9%A9%B6/"/>
    
    
    <category term="vla" scheme="https://blog.felicx.eu.org/tags/vla/"/>
    
  </entry>
  
  <entry>
    <title>Win10 蓝牙服务错误1075解决方法</title>
    <link href="https://blog.felicx.eu.org/1667248217.html"/>
    <id>https://blog.felicx.eu.org/1667248217.html</id>
    <published>2025-10-12T14:43:19.000Z</published>
    <updated>2025-10-12T14:55:19.000Z</updated>
    
    <content type="html"><![CDATA[<blockquote><p>彻底解决BluetoothUserService缺失问题解决，无需重装系统</p></blockquote><h2 id="问题背景"><a href="#问题背景" class="headerlink" title="问题背景"></a>问题背景</h2><p>近期，许多Windows 10用户反映蓝牙功能突然失效，在服务管理中找不到”蓝牙支持服务”，尝试启动BluetoothUserService时出现”错误1075”的提示。这一问题在联想笔记本用户中尤为常见，但其他品牌的电脑也可能遇到类似情况。</p><h2 id="问题确认"><a href="#问题确认" class="headerlink" title="问题确认"></a>问题确认</h2><p>在开始修复前，请先确认你是否遇到了相同的问题：</p><ol><li>右键点击”开始”按钮，选择”计算机管理”</li><li>进入”服务和应用程序” → “服务”</li><li>在服务列表中查找”蓝牙支持服务”和”蓝牙音频网关服务”</li><li>如果发现”蓝牙支持服务”缺失，或者BluetoothUserService无法启动并报错1075，那么你遇到的就是本文要解决的问题</li></ol><h2 id="解决方案"><a href="#解决方案" class="headerlink" title="解决方案"></a>解决方案</h2><p>经过多次尝试和失败后，我们发现联想官方提供的一款系统服务修复工具能够完美解决此问题。</p><h3 id="修复步骤"><a href="#修复步骤" class="headerlink" title="修复步骤"></a>修复步骤</h3><p><strong>步骤1：下载修复工具</strong></p><ul><li>访问联想官方修复工具下载地址：<a href="http://l.lenovo.cn/yGfuCzxl">http://l.lenovo.cn/yGfuCzxl</a></li><li>将程序下载到桌面，方便后续操作</li></ul><p><strong>步骤2：运行修复工具</strong></p><ul><li>双击运行下载的修复程序</li><li>等待工具加载完成</li><li>点击”开始运行”按钮</li></ul><p><strong>步骤3：选择修复项目</strong></p><ul><li>在工具列表中勾选序号13和14（注意：一次只能选择一个）</li><li>点击”修复”按钮</li><li>完成第一个项目修复后，重新打开工具，修复第二个项目</li></ul><p><strong>步骤4：重启计算机</strong></p><ul><li>两个服务修复完成后，必须重启电脑</li><li>重启是确保修复生效的关键步骤，切勿跳过</li></ul><h3 id="验证修复结果"><a href="#验证修复结果" class="headerlink" title="验证修复结果"></a>验证修复结果</h3><p>重启后，再次打开服务管理界面，检查以下内容：</p><ul><li>BluetoothUserService_xxxx服务是否正常运行</li><li>“蓝牙支持服务”是否已恢复</li><li>蓝牙功能是否可以正常使用</li></ul><h2 id="注意事项"><a href="#注意事项" class="headerlink" title="注意事项"></a>注意事项</h2><ol><li>该工具需要联网使用，请确保网络连接正常</li><li>修复过程中请勿关闭工具或中断操作</li><li>虽然该工具由联想开发，但其他品牌电脑用户也可尝试使用</li><li>如果问题依旧，建议检查系统更新和蓝牙驱动程序</li></ol><h2 id="结语"><a href="#结语" class="headerlink" title="结语"></a>结语</h2><p>这个由联想高级工程师开发的修复工具，成功解决了困扰笔者长达一年的蓝牙服务问题。相比重装系统这种”核弹级”解决方案，这种针对性的修复更加高效便捷。希望这篇文章能帮助到同样受此问题困扰的用户，让你们少走弯路，快速恢复蓝牙功能。</p><p>如果你也通过这个方法成功解决了问题，欢迎在评论区分享你的经历，让更多人受益！</p><hr><p><em>本文方法仅供参考，如涉及系统重要修改，建议备份重要数据后再进行操作。</em></p>]]></content>
    
    
    <summary type="html">解决Win10找不到蓝牙服务问题</summary>
    
    
    
    <category term="开发运维" scheme="https://blog.felicx.eu.org/categories/%E5%BC%80%E5%8F%91%E8%BF%90%E7%BB%B4/"/>
    
    
    <category term="win" scheme="https://blog.felicx.eu.org/tags/win/"/>
    
  </entry>
  
  <entry>
    <title>使用 Playwright 爬虫 Boss直聘</title>
    <link href="https://blog.felicx.eu.org/1805880227.html"/>
    <id>https://blog.felicx.eu.org/1805880227.html</id>
    <published>2025-09-07T08:11:48.000Z</published>
    <updated>2025-09-07T08:25:11.000Z</updated>
    
    <content type="html"><![CDATA[<h2 id="前言：为什么要学习爬虫"><a href="#前言：为什么要学习爬虫" class="headerlink" title="前言：为什么要学习爬虫"></a>前言：为什么要学习爬虫</h2><p>在当今数据驱动的时代，获取网络上的招聘信息对于求职者分析市场趋势、企业了解竞争对手薪资水平都非常有价值。本教程将带你从零开始，使用Playwright构建一个能够爬取Boss直聘岗位详情的爬虫程序。</p><p>即使你是编程新手，只要跟着本教程一步步操作，也能成功爬取到所需数据！</p><h2 id="一、环境准备与安装"><a href="#一、环境准备与安装" class="headerlink" title="一、环境准备与安装"></a>一、环境准备与安装</h2><h3 id="1-1-安装Python"><a href="#1-1-安装Python" class="headerlink" title="1.1 安装Python"></a>1.1 安装Python</h3><p>首先确保你的电脑上安装了Python（3.7或更高版本）。可以从<a href="https://www.python.org/downloads/">Python官网</a>下载安装。</p><p>安装完成后，打开终端（Mac&#x2F;Linux）或命令提示符（Windows），输入以下命令检查是否安装成功：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">python --version</span><br><span class="line"><span class="comment"># 或</span></span><br><span class="line">python3 --version</span><br></pre></td></tr></table></figure><h3 id="1-2-安装必要库"><a href="#1-2-安装必要库" class="headerlink" title="1.2 安装必要库"></a>1.2 安装必要库</h3><p>我们需要安装几个Python库来支持我们的爬虫：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">pip install playwright yaml pyyaml</span><br></pre></td></tr></table></figure><h3 id="1-3-安装Playwright浏览器"><a href="#1-3-安装Playwright浏览器" class="headerlink" title="1.3 安装Playwright浏览器"></a>1.3 安装Playwright浏览器</h3><p>Playwright需要安装浏览器内核才能工作：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">playwright install chromium</span><br></pre></td></tr></table></figure><p>这个过程可能会花费一些时间，因为它需要下载Chromium浏览器。</p><h2 id="二、爬虫工作原理"><a href="#二、爬虫工作原理" class="headerlink" title="二、爬虫工作原理"></a>二、爬虫工作原理</h2><h3 id="2-1-导入必要的库"><a href="#2-1-导入必要的库" class="headerlink" title="2.1 导入必要的库"></a>2.1 导入必要的库</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> asyncio</span><br><span class="line"><span class="keyword">import</span> json</span><br><span class="line"><span class="keyword">import</span> logging</span><br><span class="line"><span class="keyword">import</span> os</span><br><span class="line"><span class="keyword">import</span> re</span><br><span class="line"><span class="keyword">import</span> yaml</span><br><span class="line"><span class="keyword">import</span> time</span><br><span class="line"><span class="keyword">from</span> datetime <span class="keyword">import</span> datetime</span><br><span class="line"><span class="keyword">from</span> typing <span class="keyword">import</span> <span class="type">List</span>, <span class="type">Dict</span>, <span class="type">Any</span>, <span class="type">Optional</span></span><br><span class="line"><span class="keyword">from</span> pathlib <span class="keyword">import</span> Path</span><br><span class="line"></span><br><span class="line"><span class="keyword">from</span> playwright.async_api <span class="keyword">import</span> async_playwright, Browser, Page, BrowserContext</span><br></pre></td></tr></table></figure><p>这些库各自的作用：</p><ul><li><code>asyncio</code>: 用于异步编程，让爬虫可以高效处理多个任务</li><li><code>json</code>和<code>yaml</code>: 用于处理配置文件和保存数据</li><li><code>logging</code>: 用于记录程序运行情况，方便调试</li><li><code>playwright</code>: 核心库，用于控制浏览器</li></ul><h3 id="2-2-配置日志系统"><a href="#2-2-配置日志系统" class="headerlink" title="2.2 配置日志系统"></a>2.2 配置日志系统</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">logging.basicConfig(</span><br><span class="line">    level=logging.INFO,</span><br><span class="line">    <span class="built_in">format</span>=<span class="string">&#x27;%(asctime)s - %(name)s - %(levelname)s - %(message)s&#x27;</span></span><br><span class="line">)</span><br><span class="line">logger = logging.getLogger(<span class="string">&quot;boss_job_detail_crawler&quot;</span>)</span><br></pre></td></tr></table></figure><p>日志可以帮助我们了解程序运行状态，当出现问题时可以快速定位。</p><h3 id="2-3-创建爬虫类"><a href="#2-3-创建爬虫类" class="headerlink" title="2.3 创建爬虫类"></a>2.3 创建爬虫类</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">BossJobDetailCrawler</span>:</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, headless: <span class="built_in">bool</span> = <span class="literal">False</span></span>):</span><br><span class="line">        self.headless = headless  <span class="comment"># 是否无头模式（不显示浏览器界面）</span></span><br><span class="line">        self.browser = <span class="literal">None</span>      <span class="comment"># 浏览器对象</span></span><br><span class="line">        self.context = <span class="literal">None</span>      <span class="comment"># 浏览器上下文</span></span><br><span class="line">        self.page = <span class="literal">None</span>         <span class="comment"># 页面对象</span></span><br><span class="line">        self.playwright = <span class="literal">None</span>   <span class="comment"># Playwright实例</span></span><br></pre></td></tr></table></figure><h3 id="2-4-启动浏览器"><a href="#2-4-启动浏览器" class="headerlink" title="2.4 启动浏览器"></a>2.4 启动浏览器</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">async</span> <span class="keyword">def</span> <span class="title function_">start</span>(<span class="params">self</span>):</span><br><span class="line">    logger.info(<span class="string">&quot;启动浏览器...&quot;</span>)</span><br><span class="line">    self.playwright = <span class="keyword">await</span> async_playwright().start()</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 创建用户数据目录，用于保存登录状态</span></span><br><span class="line">    user_data_dir = os.path.expanduser(<span class="string">&#x27;~/boss_crawler_data&#x27;</span>)</span><br><span class="line">    os.makedirs(user_data_dir, exist_ok=<span class="literal">True</span>)</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 启动浏览器</span></span><br><span class="line">    self.browser = <span class="keyword">await</span> self.playwright.chromium.launch_persistent_context(</span><br><span class="line">        user_data_dir=user_data_dir,</span><br><span class="line">        headless=self.headless,</span><br><span class="line">        channel=<span class="string">&quot;chrome&quot;</span>,  <span class="comment"># 使用系统安装的Chrome</span></span><br><span class="line">        args=[</span><br><span class="line">            <span class="string">&#x27;--disable-blink-features=AutomationControlled&#x27;</span>,  <span class="comment"># 避免被检测为自动化工具</span></span><br><span class="line">            <span class="string">&#x27;--disable-web-security&#x27;</span>,</span><br><span class="line">            <span class="string">&#x27;--start-maximized&#x27;</span></span><br><span class="line">        ],</span><br><span class="line">        viewport=&#123;<span class="string">&#x27;width&#x27;</span>: <span class="number">1280</span>, <span class="string">&#x27;height&#x27;</span>: <span class="number">800</span>&#125;,</span><br><span class="line">        user_agent=<span class="string">&quot;Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36&quot;</span></span><br><span class="line">    )</span><br></pre></td></tr></table></figure><p>这里有几个关键点：</p><ol><li><code>launch_persistent_context</code>使用持久化上下文，可以保存cookies和登录状态</li><li><code>headless=False</code>表示显示浏览器界面，方便调试</li><li>设置用户代理(User-Agent)让请求看起来像来自真实浏览器</li></ol><h3 id="2-5-处理登录状态"><a href="#2-5-处理登录状态" class="headerlink" title="2.5 处理登录状态"></a>2.5 处理登录状态</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">async</span> <span class="keyword">def</span> <span class="title function_">ensure_logged_in</span>(<span class="params">self</span>):</span><br><span class="line">    logger.info(<span class="string">&quot;检查登录状态...&quot;</span>)</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 访问Boss直聘首页</span></span><br><span class="line">    <span class="keyword">await</span> self.page.goto(<span class="string">&quot;https://www.zhipin.com&quot;</span>, wait_until=<span class="string">&quot;domcontentloaded&quot;</span>)</span><br><span class="line">    <span class="keyword">await</span> asyncio.sleep(<span class="number">3</span>)</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 检查是否已登录的多个指标</span></span><br><span class="line">    login_indicators = [</span><br><span class="line">        <span class="string">&#x27;a[href*=&quot;/web/geek/chat&quot;]&#x27;</span>,  <span class="comment"># 聊天入口</span></span><br><span class="line">        <span class="string">&#x27;.nav-figure img&#x27;</span>,            <span class="comment"># 用户头像</span></span><br><span class="line">        <span class="string">&#x27;a[ka=&quot;header-username&quot;]&#x27;</span>,    <span class="comment"># 用户名链接</span></span><br><span class="line">    ]</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 尝试找到登录指标</span></span><br><span class="line">    <span class="keyword">for</span> indicator <span class="keyword">in</span> login_indicators:</span><br><span class="line">        <span class="keyword">try</span>:</span><br><span class="line">            element = <span class="keyword">await</span> self.page.query_selector(indicator)</span><br><span class="line">            <span class="keyword">if</span> element:</span><br><span class="line">                logger.info(<span class="string">&quot;检测到已登录状态&quot;</span>)</span><br><span class="line">                <span class="keyword">return</span> <span class="literal">True</span></span><br><span class="line">        <span class="keyword">except</span>:</span><br><span class="line">            <span class="keyword">continue</span></span><br><span class="line"></span><br><span class="line">    <span class="comment"># 如果未登录，提示用户手动登录</span></span><br><span class="line">    logger.info(<span class="string">&quot;未检测到登录状态，请手动登录...&quot;</span>)</span><br><span class="line">    logger.info(<span class="string">&quot;请在浏览器中完成登录，然后按Enter继续&quot;</span>)</span><br><span class="line">    <span class="built_in">input</span>(<span class="string">&quot;按Enter继续...&quot;</span>)</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 等待用户登录</span></span><br><span class="line">    <span class="keyword">for</span> _ <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">60</span>):  <span class="comment"># 最多等待5分钟</span></span><br><span class="line">        <span class="keyword">await</span> asyncio.sleep(<span class="number">5</span>)</span><br><span class="line">        <span class="keyword">for</span> indicator <span class="keyword">in</span> login_indicators:</span><br><span class="line">            <span class="keyword">try</span>:</span><br><span class="line">                element = <span class="keyword">await</span> self.page.query_selector(indicator)</span><br><span class="line">                <span class="keyword">if</span> element:</span><br><span class="line">                    logger.info(<span class="string">&quot;登录成功!&quot;</span>)</span><br><span class="line">                    <span class="keyword">return</span> <span class="literal">True</span></span><br><span class="line">            <span class="keyword">except</span>:</span><br><span class="line">                <span class="keyword">continue</span></span><br><span class="line">        logger.info(<span class="string">&quot;等待登录中...&quot;</span>)</span><br><span class="line"></span><br><span class="line">    logger.error(<span class="string">&quot;登录超时&quot;</span>)</span><br><span class="line">    <span class="keyword">return</span> <span class="literal">False</span></span><br></pre></td></tr></table></figure><p>这个函数确保我们已经登录Boss直聘，因为未登录状态下无法查看岗位详情。</p><h3 id="2-6-提取岗位详情"><a href="#2-6-提取岗位详情" class="headerlink" title="2.6 提取岗位详情"></a>2.6 提取岗位详情</h3><p>这是最核心的部分，我们需要从网页中提取所需信息：</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">async</span> <span class="keyword">def</span> <span class="title function_">extract_job_details</span>(<span class="params">self, job_url: <span class="built_in">str</span></span>) -&gt; <span class="type">Dict</span>[<span class="built_in">str</span>, <span class="type">Any</span>]:</span><br><span class="line">    logger.info(<span class="string">f&quot;开始提取岗位详情: <span class="subst">&#123;job_url&#125;</span>&quot;</span>)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">try</span>:</span><br><span class="line">        <span class="comment"># 导航到岗位详情页</span></span><br><span class="line">        <span class="keyword">await</span> self.page.goto(job_url, wait_until=<span class="string">&quot;domcontentloaded&quot;</span>)</span><br><span class="line">        <span class="keyword">await</span> asyncio.sleep(<span class="number">3</span>)  <span class="comment"># 等待页面加载</span></span><br><span class="line"></span><br><span class="line">        <span class="comment"># 提取岗位信息</span></span><br><span class="line">        job_details = &#123;</span><br><span class="line">            <span class="string">&#x27;url&#x27;</span>: job_url,</span><br><span class="line">            <span class="string">&#x27;title&#x27;</span>: <span class="keyword">await</span> self._extract_text(<span class="string">&#x27;.name h1&#x27;</span>),</span><br><span class="line">            <span class="string">&#x27;salary&#x27;</span>: <span class="keyword">await</span> self._extract_text(<span class="string">&#x27;.salary&#x27;</span>),</span><br><span class="line">            <span class="string">&#x27;city&#x27;</span>: <span class="keyword">await</span> self._extract_text(<span class="string">&#x27;.text-city&#x27;</span>),</span><br><span class="line">            <span class="string">&#x27;experience&#x27;</span>: <span class="keyword">await</span> self._extract_text(<span class="string">&#x27;.text-experience&#x27;</span>),</span><br><span class="line">            <span class="string">&#x27;education&#x27;</span>: <span class="keyword">await</span> self._extract_text(<span class="string">&#x27;.text-degree&#x27;</span>),</span><br><span class="line">            <span class="string">&#x27;company&#x27;</span>: <span class="keyword">await</span> self._extract_text(<span class="string">&#x27;.company-info .name&#x27;</span>),</span><br><span class="line">            <span class="string">&#x27;company_type&#x27;</span>: <span class="keyword">await</span> self._extract_text(<span class="string">&#x27;.company-info .type&#x27;</span>),</span><br><span class="line">            <span class="string">&#x27;company_size&#x27;</span>: <span class="keyword">await</span> self._extract_text(<span class="string">&#x27;.company-info .size&#x27;</span>),</span><br><span class="line">            <span class="string">&#x27;job_description&#x27;</span>: <span class="keyword">await</span> self._extract_text(<span class="string">&#x27;.job-sec-text&#x27;</span>),</span><br><span class="line">            <span class="string">&#x27;extracted_at&#x27;</span>: datetime.now().isoformat()</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment"># 提取标签信息</span></span><br><span class="line">        tags = <span class="keyword">await</span> self._extract_tags()</span><br><span class="line">        <span class="keyword">if</span> tags:</span><br><span class="line">            job_details[<span class="string">&#x27;tags&#x27;</span>] = tags</span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> job_details</span><br><span class="line"></span><br><span class="line">    <span class="keyword">except</span> Exception <span class="keyword">as</span> e:</span><br><span class="line">        logger.error(<span class="string">f&quot;提取岗位详情失败: <span class="subst">&#123;e&#125;</span>&quot;</span>)</span><br><span class="line">        <span class="comment"># 保存错误截图便于调试</span></span><br><span class="line">        screenshot_path = <span class="string">f&quot;error_screenshot_<span class="subst">&#123;<span class="built_in">int</span>(time.time())&#125;</span>.png&quot;</span></span><br><span class="line">        <span class="keyword">await</span> self.page.screenshot(path=screenshot_path)</span><br><span class="line">        logger.info(<span class="string">f&quot;已保存错误截图: <span class="subst">&#123;screenshot_path&#125;</span>&quot;</span>)</span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> &#123;</span><br><span class="line">            <span class="string">&#x27;url&#x27;</span>: job_url,</span><br><span class="line">            <span class="string">&#x27;error&#x27;</span>: <span class="built_in">str</span>(e),</span><br><span class="line">            <span class="string">&#x27;extracted_at&#x27;</span>: datetime.now().isoformat()</span><br><span class="line">        &#125;</span><br></pre></td></tr></table></figure><h3 id="2-7-辅助提取方法"><a href="#2-7-辅助提取方法" class="headerlink" title="2.7 辅助提取方法"></a>2.7 辅助提取方法</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">async</span> <span class="keyword">def</span> <span class="title function_">_extract_text</span>(<span class="params">self, selector: <span class="built_in">str</span></span>) -&gt; <span class="built_in">str</span>:</span><br><span class="line">    <span class="string">&quot;&quot;&quot;提取文本内容&quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">try</span>:</span><br><span class="line">        element = <span class="keyword">await</span> self.page.query_selector(selector)</span><br><span class="line">        <span class="keyword">if</span> element:</span><br><span class="line">            text = <span class="keyword">await</span> element.text_content()</span><br><span class="line">            <span class="keyword">return</span> text.strip() <span class="keyword">if</span> text <span class="keyword">else</span> <span class="string">&quot;&quot;</span></span><br><span class="line">    <span class="keyword">except</span>:</span><br><span class="line">        <span class="keyword">pass</span></span><br><span class="line">    <span class="keyword">return</span> <span class="string">&quot;&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">async</span> <span class="keyword">def</span> <span class="title function_">_extract_tags</span>(<span class="params">self</span>) -&gt; <span class="type">List</span>[<span class="built_in">str</span>]:</span><br><span class="line">    <span class="string">&quot;&quot;&quot;提取标签信息&quot;&quot;&quot;</span></span><br><span class="line">    tags = []</span><br><span class="line">    <span class="keyword">try</span>:</span><br><span class="line">        tag_elements = <span class="keyword">await</span> self.page.query_selector_all(<span class="string">&#x27;.job-tags span&#x27;</span>)</span><br><span class="line">        <span class="keyword">for</span> tag_element <span class="keyword">in</span> tag_elements:</span><br><span class="line">            tag_text = <span class="keyword">await</span> tag_element.text_content()</span><br><span class="line">            <span class="keyword">if</span> tag_text <span class="keyword">and</span> tag_text.strip():</span><br><span class="line">                tags.append(tag_text.strip())</span><br><span class="line">    <span class="keyword">except</span>:</span><br><span class="line">        <span class="keyword">pass</span></span><br><span class="line">    <span class="keyword">return</span> tags</span><br></pre></td></tr></table></figure><p>这两个辅助方法使用CSS选择器来定位和提取页面上的特定元素。</p><h2 id="三、运行你的第一个爬虫"><a href="#三、运行你的第一个爬虫" class="headerlink" title="三、运行你的第一个爬虫"></a>三、运行你的第一个爬虫</h2><p>具体代码见 <a href="https://github.com/FelicxFoster/SpiderHub/boss_crawl">boss_crawl</a>。</p><h3 id="3-1-创建配置文件"><a href="#3-1-创建配置文件" class="headerlink" title="3.1 创建配置文件"></a>3.1 创建配置文件</h3><p>在代码同一目录下创建<code>job_links.yaml</code>文件：</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">job_links:</span></span><br><span class="line">  <span class="bullet">-</span> <span class="string">https://www.zhipin.com/job_detail/第一个岗位的ID.html</span></span><br><span class="line">  <span class="bullet">-</span> <span class="string">https://www.zhipin.com/job_detail/第二个岗位的ID.html</span></span><br><span class="line">  <span class="bullet">-</span> <span class="string">https://www.zhipin.com/job_detail/第三个岗位的ID.html</span></span><br><span class="line"></span><br><span class="line"><span class="attr">output:</span></span><br><span class="line">  <span class="attr">dir:</span> <span class="string">./job_details</span>  <span class="comment"># 输出目录</span></span><br><span class="line">  <span class="attr">format:</span> <span class="string">json</span>        <span class="comment"># 输出格式，可以是json或csv</span></span><br></pre></td></tr></table></figure><p>将上面的URL替换为你想爬取的实际岗位链接。</p><h3 id="3-2-运行爬虫"><a href="#3-2-运行爬虫" class="headerlink" title="3.2 运行爬虫"></a>3.2 运行爬虫</h3><p>保存代码为<code>boss_crawl.py</code>，然后在终端中运行：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">python boss_crawl.py</span><br></pre></td></tr></table></figure><p>第一次运行时会弹出浏览器窗口，你需要手动登录Boss直聘。登录成功后，按终端中的Enter键，爬虫就会开始工作。</p><h3 id="3-3-查看结果"><a href="#3-3-查看结果" class="headerlink" title="3.3 查看结果"></a>3.3 查看结果</h3><p>爬虫完成后，会在<code>job_details</code>目录下生成一个包含时间戳的JSON或CSV文件，里面包含所有爬取到的岗位信息。</p><h2 id="四、常见问题与解决方案"><a href="#四、常见问题与解决方案" class="headerlink" title="四、常见问题与解决方案"></a>四、常见问题与解决方案</h2><h3 id="4-1-爬虫被检测到怎么办？"><a href="#4-1-爬虫被检测到怎么办？" class="headerlink" title="4.1 爬虫被检测到怎么办？"></a>4.1 爬虫被检测到怎么办？</h3><ul><li>调整<code>user-agent</code>，模拟真实浏览器</li><li>增加随机延迟，模拟人类操作</li><li>使用代理IP轮换请求</li></ul><h3 id="4-2-页面结构变化怎么办？"><a href="#4-2-页面结构变化怎么办？" class="headerlink" title="4.2 页面结构变化怎么办？"></a>4.2 页面结构变化怎么办？</h3><p>如果Boss直聘更新了页面结构，你需要更新代码中的CSS选择器。可以使用浏览器开发者工具检查元素，找到新的选择器。</p><h3 id="4-3-如何提高爬取效率？"><a href="#4-3-如何提高爬取效率？" class="headerlink" title="4.3 如何提高爬取效率？"></a>4.3 如何提高爬取效率？</h3><ul><li>使用异步并发处理多个页面</li><li>合理设置请求延迟，既不太快触发反爬，也不太慢影响效率</li></ul><h2 id="五、注意事项"><a href="#五、注意事项" class="headerlink" title="五、注意事项"></a>五、注意事项</h2><ol><li><strong>遵守Robots协议</strong>：检查Boss直聘的robots.txt文件，尊重网站的爬虫规则</li><li><strong>限制爬取频率</strong>：不要过于频繁地请求，以免给网站服务器造成压力</li><li><strong>合理使用数据</strong>：爬取的数据仅用于个人学习和研究，不要用于商业用途或侵犯他人隐私</li><li><strong>注明数据来源</strong>：如果公开使用这些数据，请注明来自Boss直聘</li></ol><h2 id="结语"><a href="#结语" class="headerlink" title="结语"></a>结语</h2><p>通过本教程，你学会了如何使用Playwright构建一个完整的爬虫程序，从环境搭建到实际运行，从页面导航到数据提取。这个技能不仅适用于Boss直聘，稍作修改也可以用于其他网站。</p><p>爬虫技术是一把双刃剑，希望你能合理使用它，在遵守法律和道德的前提下，获取对你有价值的数据。</p><p>如果你在实践过程中遇到任何问题，欢迎在评论区留言，我会尽力解答！</p>]]></content>
    
    
    <summary type="html">手把手教你用Playwright爬取Boss直聘岗位详情数据</summary>
    
    
    
    <category term="开发运维" scheme="https://blog.felicx.eu.org/categories/%E5%BC%80%E5%8F%91%E8%BF%90%E7%BB%B4/"/>
    
    
    <category term="python" scheme="https://blog.felicx.eu.org/tags/python/"/>
    
  </entry>
  
  <entry>
    <title>多版本 Python 环境管理指南</title>
    <link href="https://blog.felicx.eu.org/3316642405.html"/>
    <id>https://blog.felicx.eu.org/3316642405.html</id>
    <published>2025-08-16T09:12:04.000Z</published>
    <updated>2025-08-16T14:31:04.000Z</updated>
    
    <content type="html"><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>在 Python 开发中，不同项目常需不同 Python 版本和依赖库。本文将详解 Windows&#x2F;Linux 双平台下的环境隔离方案，彻底解决版本冲突问题！</p><hr><h2 id="核心工具选择"><a href="#核心工具选择" class="headerlink" title="核心工具选择"></a>核心工具选择</h2><table><thead><tr><th><strong>功能</strong></th><th><strong>Windows 方案</strong></th><th><strong>Linux 方案</strong></th><th><strong>跨平台方案</strong></th></tr></thead><tbody><tr><td>版本管理</td><td>pyenv-win</td><td>pyenv</td><td>conda</td></tr><tr><td>虚拟环境</td><td>venv</td><td>virtualenv</td><td>pipenv</td></tr><tr><td>图形界面</td><td>Anaconda Navigator</td><td>Anaconda Navigator</td><td>PyCharm</td></tr></tbody></table><hr><h2 id="Windows-方案"><a href="#Windows-方案" class="headerlink" title="Windows 方案"></a>Windows 方案</h2><h3 id="pyenv-win-venv"><a href="#pyenv-win-venv" class="headerlink" title="pyenv-win + venv"></a>pyenv-win + venv</h3><figure class="highlight powershell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 1. 安装pyenv-win</span></span><br><span class="line"><span class="built_in">iwr</span> <span class="literal">-useb</span> https://raw.githubusercontent.com/pyenv<span class="literal">-win</span>/pyenv<span class="literal">-win</span>/master/pyenv<span class="literal">-win</span>/<span class="built_in">install-pyenv</span><span class="literal">-win</span>.ps1 | <span class="built_in">iex</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 2. 配置环境变量（永久生效）</span></span><br><span class="line">[<span class="type">System.Environment</span>]::SetEnvironmentVariable(<span class="string">&#x27;PYENV&#x27;</span>, <span class="string">&quot;<span class="variable">$env:USERPROFILE</span>\.pyenv\pyenv-win\&quot;</span>, <span class="string">&quot;User&quot;</span>)</span><br><span class="line">[<span class="type">System.Environment</span>]::SetEnvironmentVariable(<span class="string">&#x27;PATH&#x27;</span>, <span class="string">&quot;<span class="variable">$env:USERPROFILE</span>\.pyenv\pyenv-win\bin;<span class="variable">$env:USERPROFILE</span>\.pyenv\pyenv-win\shims;<span class="variable">$env:PATH</span>&quot;</span>, <span class="string">&quot;User&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 3. 重启终端后验证</span></span><br><span class="line">pyenv <span class="literal">--version</span></span><br></pre></td></tr></table></figure><h3 id="环境创建"><a href="#环境创建" class="headerlink" title="环境创建"></a>环境创建</h3><figure class="highlight powershell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 1. 安装 Python 版本（使用 pyenv-win）</span></span><br><span class="line">pyenv install <span class="number">3.8</span>.<span class="number">10</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 2. 创建虚拟环境</span></span><br><span class="line">python <span class="literal">-m</span> venv C:\path\to\myenv</span><br><span class="line"></span><br><span class="line"><span class="comment"># 3. 激活环境</span></span><br><span class="line">C:\path\to\myenv\Scripts\activate  <span class="comment"># PowerShell</span></span><br><span class="line"><span class="comment"># 或 C:\path\to\myenv\Scripts\activate.bat # CMD</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 4. 验证</span></span><br><span class="line">(myenv) python <span class="literal">--version</span></span><br></pre></td></tr></table></figure><h3 id="环境卸载"><a href="#环境卸载" class="headerlink" title="环境卸载"></a>环境卸载</h3><figure class="highlight powershell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 1. 停用环境</span></span><br><span class="line">deactivate</span><br><span class="line"></span><br><span class="line"><span class="comment"># 2. 删除虚拟环境（直接删除文件夹）</span></span><br><span class="line"><span class="built_in">Remove-Item</span> <span class="literal">-Recurse</span> <span class="literal">-Force</span> C:\path\to\myenv  <span class="comment"># PowerShell</span></span><br><span class="line"><span class="comment"># 或 rmdir /s /q C:\path\to\myenv # CMD</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 3. 卸载 Python 版本（pyenv-win）</span></span><br><span class="line">pyenv uninstall <span class="number">3.8</span>.<span class="number">10</span></span><br></pre></td></tr></table></figure><h2 id="Linux-方案"><a href="#Linux-方案" class="headerlink" title="Linux 方案"></a>Linux 方案</h2><h3 id="pyenv-virtualenv"><a href="#pyenv-virtualenv" class="headerlink" title="pyenv + virtualenv"></a>pyenv + virtualenv</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 1. 安装依赖</span></span><br><span class="line">sudo apt install -y make build-essential libssl-dev zlib1g-dev</span><br><span class="line"></span><br><span class="line"><span class="comment"># 2. 安装pyenv</span></span><br><span class="line">curl https://pyenv.run | bash</span><br><span class="line"></span><br><span class="line"><span class="comment"># 3. 配置环境变量(~/.bashrc)</span></span><br><span class="line"><span class="built_in">echo</span> <span class="string">&#x27;export PATH=&quot;$HOME/.pyenv/bin:$PATH&quot;&#x27;</span> &gt;&gt; ~/.bashrc</span><br><span class="line"><span class="built_in">echo</span> <span class="string">&#x27;eval &quot;$(pyenv init --path)&quot;&#x27;</span> &gt;&gt; ~/.bashrc</span><br><span class="line"><span class="built_in">echo</span> <span class="string">&#x27;eval &quot;$(pyenv virtualenv-init -)&quot;&#x27;</span> &gt;&gt; ~/.bashrc</span><br><span class="line"><span class="built_in">source</span> ~/.bashrc</span><br></pre></td></tr></table></figure><h3 id="环境创建-1"><a href="#环境创建-1" class="headerlink" title="环境创建"></a>环境创建</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 1. 安装 Python 版本（pyenv）</span></span><br><span class="line">pyenv install 3.8.10</span><br><span class="line"></span><br><span class="line"><span class="comment"># 2. 创建虚拟环境</span></span><br><span class="line">pyenv virtualenv 3.8.10 myenv</span><br><span class="line"></span><br><span class="line"><span class="comment"># 3. 激活环境</span></span><br><span class="line">pyenv activate myenv</span><br><span class="line"></span><br><span class="line"><span class="comment"># 4. 验证</span></span><br><span class="line">(myenv) <span class="built_in">which</span> python</span><br></pre></td></tr></table></figure><h3 id="环境卸载-1"><a href="#环境卸载-1" class="headerlink" title="环境卸载"></a>环境卸载</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 1. 停用环境</span></span><br><span class="line">pyenv deactivate</span><br><span class="line"></span><br><span class="line"><span class="comment"># 2. 卸载虚拟环境</span></span><br><span class="line">pyenv uninstall myenv</span><br><span class="line"></span><br><span class="line"><span class="comment"># 3. 卸载 Python 版本</span></span><br><span class="line">pyenv uninstall 3.8.10</span><br><span class="line"></span><br><span class="line"><span class="comment"># 4. 手动清理残留</span></span><br><span class="line"><span class="built_in">rm</span> -rf ~/.pyenv/versions/3.8.10</span><br></pre></td></tr></table></figure><hr><h2 id="跨平台最佳选择"><a href="#跨平台最佳选择" class="headerlink" title="跨平台最佳选择"></a>跨平台最佳选择</h2><h3 id="统一使用-conda"><a href="#统一使用-conda" class="headerlink" title="统一使用 conda"></a>统一使用 conda</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Windows/Linux 通用命令</span></span><br><span class="line">conda create -n project_env python=3.9</span><br><span class="line">conda activate project_env</span><br><span class="line">conda deactivate</span><br><span class="line">conda remove -n py313 --all</span><br></pre></td></tr></table></figure><h3 id="使用-python-version-文件"><a href="#使用-python-version-文件" class="headerlink" title="使用 .python-version 文件"></a>使用 .python-version 文件</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 项目目录中创建文件</span></span><br><span class="line"><span class="built_in">echo</span> <span class="string">&quot;3.8.12&quot;</span> &gt; .python-version</span><br><span class="line"></span><br><span class="line"><span class="comment"># 这样 pyenv 会自动切换版本，无需手动激活（双平台支持）</span></span><br></pre></td></tr></table></figure><h3 id="环境迁移方案"><a href="#环境迁移方案" class="headerlink" title="环境迁移方案"></a>环境迁移方案</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 生成通用 requirements.txt</span></span><br><span class="line">pip freeze &gt; requirements.txt</span><br><span class="line"></span><br><span class="line"><span class="comment"># 跨平台安装</span></span><br><span class="line">pip install -r requirements.txt</span><br></pre></td></tr></table></figure>]]></content>
    
    
    <summary type="html">Windows/Linux 创建 Python 环境</summary>
    
    
    
    <category term="开发运维" scheme="https://blog.felicx.eu.org/categories/%E5%BC%80%E5%8F%91%E8%BF%90%E7%BB%B4/"/>
    
    
    <category term="python" scheme="https://blog.felicx.eu.org/tags/python/"/>
    
  </entry>
  
  <entry>
    <title>exe 反编译源码</title>
    <link href="https://blog.felicx.eu.org/1844880606.html"/>
    <id>https://blog.felicx.eu.org/1844880606.html</id>
    <published>2025-08-10T13:02:36.000Z</published>
    <updated>2025-08-10T13:10:36.000Z</updated>
    
    <content type="html"><![CDATA[<h2 id="引言"><a href="#引言" class="headerlink" title="引言"></a>引言</h2><p>当遇到丢失源码的Python打包exe程序时，我们可能需要逆向分析其逻辑。之前写过《<a href="https://blog.felicx.eu.org/1915330816.html">Pyinstaller 反编译</a>》，最近发现有些工具比较老了，反编译不成功。<br>所以重新整理了下，整个过程无需依赖反编译工具，借助AI即可完成（感谢强大的AI）。</p><hr><h3 id="使用pyinstxtractor解包exe"><a href="#使用pyinstxtractor解包exe" class="headerlink" title="使用pyinstxtractor解包exe"></a>使用pyinstxtractor解包exe</h3><ol><li><p><strong>安装工具</strong>：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">pip install pyinstxtractor</span><br></pre></td></tr></table></figure></li><li><p><strong>解包exe文件</strong>：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">python -m pyinstxtractor myapp.exe</span><br></pre></td></tr></table></figure><p>执行后生成<code>myapp.exe_extracted</code>文件夹，其中包含：</p><ul><li><code>.pyc</code>文件（编译后的字节码）</li><li>依赖库和资源文件</li></ul><blockquote><p>》注意：最好是用跟exe同样的python版本，不然有可能反编译不成功<br>》不知道是什么版本，就随便用一个版本先解包，生成的文件夹里有版本的dll文件<br>》可以创建python虚拟环境来操作，参考《<a href="https://blog.felicx.eu.org/3316642405.html">多版本Python环境管理指南</a>》</p></blockquote></li><li><p><strong>关键文件定位</strong>：<br>在解包目录中寻找<code>PYZ-00.pyz_extracted</code>文件夹，主程序的pyc文件通常位于此处（如<code>myapp.pyc</code>）</p></li></ol><hr><h3 id="pyc文件反编译"><a href="#pyc文件反编译" class="headerlink" title="pyc文件反编译"></a>pyc文件反编译</h3><p>使用以下脚本读取pyc并反编译为字节码：</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> dis</span><br><span class="line"><span class="keyword">import</span> marshal</span><br><span class="line"><span class="keyword">import</span> sys</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">decompile_pyc</span>(<span class="params">pyc_path</span>):</span><br><span class="line">    <span class="keyword">with</span> <span class="built_in">open</span>(pyc_path, <span class="string">&#x27;rb&#x27;</span>) <span class="keyword">as</span> f:</span><br><span class="line">        <span class="comment"># 跳过pyc文件头（不同Python版本头长度不同）</span></span><br><span class="line">        f.seek(<span class="number">16</span>)  <span class="comment"># Python 3.7+ 使用16字节头</span></span><br><span class="line">        <span class="keyword">try</span>:</span><br><span class="line">            code = marshal.load(f)</span><br><span class="line">        <span class="keyword">except</span>:</span><br><span class="line">            <span class="built_in">print</span>(<span class="string">&quot;⚠️ 文件头可能不匹配，尝试12字节偏移...&quot;</span>)</span><br><span class="line">            f.seek(<span class="number">12</span>)</span><br><span class="line">            code = marshal.load(f)</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 将字节码输出到文件</span></span><br><span class="line">    bytecode_path = pyc_path.replace(<span class="string">&#x27;.pyc&#x27;</span>, <span class="string">&#x27;_bytecode.txt&#x27;</span>)</span><br><span class="line">    <span class="keyword">with</span> <span class="built_in">open</span>(bytecode_path, <span class="string">&#x27;w&#x27;</span>) <span class="keyword">as</span> out:</span><br><span class="line">        sys.stdout = out</span><br><span class="line">        dis.dis(code)</span><br><span class="line">        sys.stdout = sys.__stdout__</span><br><span class="line"></span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f&quot;✅ 字节码已保存至: <span class="subst">&#123;bytecode_path&#125;</span>&quot;</span>)</span><br><span class="line">    <span class="keyword">return</span> bytecode_path</span><br><span class="line"></span><br><span class="line"><span class="comment"># 使用示例</span></span><br><span class="line">bytecode_file = decompile_pyc(<span class="string">&quot;myapp.pyc&quot;</span>)</span><br></pre></td></tr></table></figure><p><strong>关键说明</strong>：</p><ol><li>文件头偏移量根据Python版本调整：<ul><li>Python 3.7+：16字节</li><li>Python 3.3-3.6：12字节</li><li>更旧版本：8字节</li></ul></li><li>若遇到<code>ValueError</code>，可尝试不同偏移量</li></ol><hr><h3 id="通过AI还原源码（DeepSeek示例）"><a href="#通过AI还原源码（DeepSeek示例）" class="headerlink" title="通过AI还原源码（DeepSeek示例）"></a>通过AI还原源码（DeepSeek示例）</h3><ol><li><strong>打开 <a href="https://www.deepseek.com/">DeepSeek AI</a></strong></li><li><strong>输入提示词</strong>：<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">请将以下Python字节码还原为可读的Python源码：</span><br><span class="line">（粘贴bytecode.txt中的全部内容）</span><br><span class="line"></span><br><span class="line">注意：</span><br><span class="line">1. 还原函数和变量名称</span><br><span class="line">2. 保持原始逻辑结构</span><br><span class="line">3. 添加必要的注释</span><br></pre></td></tr></table></figure></li></ol>]]></content>
    
    
    <summary type="html">从Python打包的exe到源码的反编译流程</summary>
    
    
    
    <category term="开发运维" scheme="https://blog.felicx.eu.org/categories/%E5%BC%80%E5%8F%91%E8%BF%90%E7%BB%B4/"/>
    
    
    <category term="python" scheme="https://blog.felicx.eu.org/tags/python/"/>
    
  </entry>
  
  <entry>
    <title>CAN 报文故障注入</title>
    <link href="https://blog.felicx.eu.org/1973638084.html"/>
    <id>https://blog.felicx.eu.org/1973638084.html</id>
    <published>2025-07-27T01:33:15.000Z</published>
    <updated>2025-07-27T02:56:11.000Z</updated>
    
    <content type="html"><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>在汽车电子系统开发中，网关作为不同CAN网络间的桥梁至关重要。而故障注入测试则是验证ECU通信鲁棒性和安全性的核心手段。通过CANoe软件结合CAPL编程，我们能够高效构建虚拟网关并实现精准的报文故障注入。本文将深入解析从基础搭建到高级故障注入的完整技术方案。</p><hr><h2 id="故障注入原理"><a href="#故障注入原理" class="headerlink" title="故障注入原理"></a>故障注入原理</h2><p>汽车网关核心功能是协调不同网络间的通信（如CAN、CAN FD、LIN）。传统物理网关开发周期长且修改困难，而CANoe的虚拟网关方案通过软件模拟实现了灵活配置：</p><ul><li><strong>故障注入目的</strong>：模拟通信异常（如信号篡改、DoS攻击、E2E校验破坏），验证ECU的容错机制</li><li><strong>核心原理</strong>：将CANoe作为“中间人”，截获并修改流经网关的报文。例如：剪断ECU间物理线路，分别接入CANoe的Channel 1和Channel 2，形成逻辑隔离</li></ul><blockquote><p><strong>应用场景</strong>：控制器功能安全测试（ISO 26262）、网络安全渗透测试、总线负载压力验证</p></blockquote><hr><h2 id="CANoe网关搭建"><a href="#CANoe网关搭建" class="headerlink" title="CANoe网关搭建"></a>CANoe网关搭建</h2><h3 id="硬件连接与工程配置"><a href="#硬件连接与工程配置" class="headerlink" title="硬件连接与工程配置"></a>硬件连接与工程配置</h3><ul><li><p><strong>硬件拓扑</strong>：</p><pre class="mermaid">  graph LR  ECU1[ECU1] -->|原CAN线剪断| CANoe_CH1  ECU2[ECU2] -->|原CAN线剪断| CANoe_CH2  CANoe_CH1 -- 转发逻辑 --> CANoe_CH2</pre><p>使用DB9接口或OBD转接头连接物理CAN线到CANoe硬件通道</p></li><li><p><strong>工程配置</strong>：</p><ol><li>新建工程并添加两个CAN通道（如CAN1、CAN2）</li><li>加载DBC文件至对应通道，确保信号定义一致</li><li>创建Gateway节点，关联双通道（Buses设置为CAN1+CAN2）</li></ol></li></ul><h3 id="基础透传CAPL脚本"><a href="#基础透传CAPL脚本" class="headerlink" title="基础透传CAPL脚本"></a>基础透传CAPL脚本</h3><p>实现报文双向转发是故障注入的基础：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 基础透传示例 */</span></span><br><span class="line">on message CAN1.* &#123;</span><br><span class="line">  <span class="keyword">if</span>(<span class="keyword">this</span>.DIR == RX) &#123;  <span class="comment">// 仅处理接收帧</span></span><br><span class="line">    message CAN2.* msg = <span class="keyword">this</span>;</span><br><span class="line">    <span class="built_in">output</span>(msg);       <span class="comment">// 转发至CAN2</span></span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">on message CAN2.* &#123;</span><br><span class="line">  <span class="keyword">if</span>(<span class="keyword">this</span>.DIR == RX) &#123;</span><br><span class="line">    message CAN1.* msg = <span class="keyword">this</span>;</span><br><span class="line">    <span class="built_in">output</span>(msg);       <span class="comment">// 转发至CAN1</span></span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>此脚本确保非目标报文无损穿透网关</p><hr><h2 id="故障注入类型"><a href="#故障注入类型" class="headerlink" title="故障注入类型"></a>故障注入类型</h2><h3 id="信号篡改攻击"><a href="#信号篡改攻击" class="headerlink" title="信号篡改攻击"></a>信号篡改攻击</h3><p>针对特定ID报文中的信号值修改：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">on message CAN1<span class="number">.0</span>x506 &#123;  <span class="comment">// 截获ID=0x506的报文</span></span><br><span class="line">  <span class="keyword">if</span>(<span class="keyword">this</span>.DIR == RX) &#123;</span><br><span class="line">    message CAN2<span class="number">.0</span>x506 msg = <span class="keyword">this</span>;</span><br><span class="line">    msg.SignalA = <span class="number">0x31</span>;    <span class="comment">// 篡改信号值</span></span><br><span class="line">    <span class="comment">// 保留其他信号原始值</span></span><br><span class="line">    <span class="built_in">output</span>(msg);          <span class="comment">// 转发篡改后报文</span></span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><blockquote><p><strong>应用场景</strong>：模拟传感器数据异常（如车速信号突变）</p></blockquote><h3 id="报文结构破坏攻击"><a href="#报文结构破坏攻击" class="headerlink" title="报文结构破坏攻击"></a>报文结构破坏攻击</h3><p>通过CAPL函数动态改变报文属性：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 修改报文长度(DLC)</span></span><br><span class="line"><span class="built_in">TestSetMsgDLC</span>(MessageID_0x200, <span class="number">4</span>);  <span class="comment">// 强制DLC=4</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 修改发送周期</span></span><br><span class="line"><span class="built_in">TestSetMsgCycleTime</span>(MessageID_0x301, <span class="number">300</span>); <span class="comment">// 周期改为300ms</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 禁止报文发送</span></span><br><span class="line"><span class="built_in">TestDisableMsg</span>(MessageID_0x400);    <span class="comment">// 模拟报文丢失</span></span><br></pre></td></tr></table></figure><h3 id="高级E2E校验攻击"><a href="#高级E2E校验攻击" class="headerlink" title="高级E2E校验攻击"></a>高级E2E校验攻击</h3><p>在篡改信号后重新计算CRC以绕过接收方校验：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">on message CAN1<span class="number">.0</span>x600 &#123;</span><br><span class="line">  message CAN2<span class="number">.0</span>x600 msg = <span class="keyword">this</span>;</span><br><span class="line">  msg.SafetyCriticalSignal = <span class="number">999</span>; <span class="comment">// 篡改关键信号</span></span><br><span class="line"></span><br><span class="line">  <span class="comment">// 基于篡改后值重新计算CRC</span></span><br><span class="line">  dword newCRC = <span class="built_in">Calculate_E2ECRC</span>(msg);</span><br><span class="line">  msg.Counter = (msg.Counter + <span class="number">1</span>) % <span class="number">16</span>;  <span class="comment">// 更新计数器</span></span><br><span class="line">  msg.CRC = newCRC;                     <span class="comment">// 更新校验域</span></span><br><span class="line"></span><br><span class="line">  <span class="built_in">output</span>(msg);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><blockquote><p><strong>关键点</strong>：需在DBC中定义E2E协议规则（如AUTOSAR E2E Profile）</p></blockquote><h3 id="定时攻击场景"><a href="#定时攻击场景" class="headerlink" title="定时攻击场景"></a>定时攻击场景</h3><p>控制故障注入的精确时间窗口：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line">variables &#123;</span><br><span class="line">  msTimer faultTimer;</span><br><span class="line">  <span class="type">int</span> injectActive = <span class="number">0</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">on timer faultTimer &#123;</span><br><span class="line">  injectActive = <span class="number">0</span>;  <span class="comment">// 结束注入</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">on key <span class="string">&#x27;a&#x27;</span> &#123;  <span class="comment">// 按键触发注入</span></span><br><span class="line">  injectActive = <span class="number">1</span>;</span><br><span class="line">  <span class="built_in">setTimer</span>(faultTimer, <span class="number">5000</span>); <span class="comment">// 5秒后自动停止</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">on message CAN1.* &#123;</span><br><span class="line">  <span class="keyword">if</span>(injectActive &amp;&amp; <span class="keyword">this</span>.id == <span class="number">0x123</span>) &#123;</span><br><span class="line">    <span class="comment">// 执行篡改逻辑</span></span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><hr><h2 id="测试验证"><a href="#测试验证" class="headerlink" title="测试验证"></a>测试验证</h2><h3 id="测试环境搭建"><a href="#测试环境搭建" class="headerlink" title="测试环境搭建"></a>测试环境搭建</h3><ul><li>仿真模式：使用CANoe <strong>Interactive Generator</strong>模拟节点报文</li><li>监控工具：Trace窗口实时比对原始报文与转发报文</li><li>自动化测试：结合Test Module实现批量用例执行</li></ul><h3 id="故障注入效果验证"><a href="#故障注入效果验证" class="headerlink" title="故障注入效果验证"></a>故障注入效果验证</h3><table><thead><tr><th><strong>注入类型</strong></th><th><strong>预期效果</strong></th><th><strong>验证方法</strong></th></tr></thead><tbody><tr><td>信号篡改</td><td>接收ECU行为异常（如误报警）</td><td>监控ECU状态机跳转</td></tr><tr><td>报文超时</td><td>接收端触发Timeout DTC</td><td>诊断仪读取故障码</td></tr><tr><td>E2E校验错误</td><td>ECU拒收报文并记录CRC错误</td><td>跟踪接收计数器变化</td></tr><tr><td>DLC异常</td><td>接收ECU丢弃报文或进入安全状态</td><td>总线负载率分析</td></tr></tbody></table><h3 id="安全防护验证"><a href="#安全防护验证" class="headerlink" title="安全防护验证"></a>安全防护验证</h3><p>通过报警网关检测注入攻击：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 网关内嵌攻击检测逻辑</span></span><br><span class="line">on message * &#123;</span><br><span class="line">  <span class="keyword">if</span>(<span class="keyword">this</span>.DLC &gt; <span class="number">8</span>) &#123;  <span class="comment">// 检测DLC超限</span></span><br><span class="line">    <span class="built_in">write</span>(<span class="string">&quot;DoS攻击警报！ID:%x&quot;</span>, <span class="keyword">this</span>.id);</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">if</span>(<span class="built_in">checkSpoofing</span>(<span class="keyword">this</span>)) &#123;  <span class="comment">// 假扮攻击检测</span></span><br><span class="line">    <span class="built_in">write</span>(<span class="string">&quot;假扮ECU攻击！&quot;</span>);</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><hr><h2 id="工程实践技巧"><a href="#工程实践技巧" class="headerlink" title="工程实践技巧"></a>工程实践技巧</h2><h3 id="路由表管理"><a href="#路由表管理" class="headerlink" title="路由表管理"></a>路由表管理</h3><p>使用<code>Database::Lookup</code>动态获取报文属性，避免硬编码：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">message * msg = &#123;<span class="built_in">DBLookup</span>(<span class="string">&quot;LightStatus&quot;</span>)&#125;;</span><br><span class="line">msg.id = <span class="built_in">getSignal</span>(<span class="string">&quot;TargetID&quot;</span>);  <span class="comment">// 从面板动态获取ID</span></span><br></pre></td></tr></table></figure><h3 id="故障注入面板设计"><a href="#故障注入面板设计" class="headerlink" title="故障注入面板设计"></a>故障注入面板设计</h3><ul><li>通道选择下拉菜单（CAN1→CAN2 &#x2F; CAN2→CAN1）</li><li>报文ID列表（支持多选）</li><li>信号编辑器（支持表达式如<code>rand()</code>）</li><li>E2E校验使能开关</li></ul><h3 id="自动化测试集成"><a href="#自动化测试集成" class="headerlink" title="自动化测试集成"></a>自动化测试集成</h3><p>结合.vTESTstudio实现全自动故障注入测试：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">TEST CASE <span class="string">&quot;Fault Injection&quot;</span> &#123;</span><br><span class="line"><span class="built_in">SET_FAULT_PARAM</span>(ID=<span class="number">0x123</span>, Signal=<span class="string">&quot;Torque&quot;</span>, Value=<span class="number">2000</span>);</span><br><span class="line"><span class="built_in">START_INJECTION</span>();</span><br><span class="line">WAIT <span class="number">5</span> s;</span><br><span class="line"><span class="built_in">CHECK_DTC</span>(DTC_123_1);  <span class="comment">// 验证预期故障码</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><hr><h2 id="常见问题"><a href="#常见问题" class="headerlink" title="常见问题"></a>常见问题</h2><h3 id="实时性瓶颈"><a href="#实时性瓶颈" class="headerlink" title="实时性瓶颈"></a>实时性瓶颈</h3><p><strong>问题</strong>：高负载总线转发延迟导致丢帧<br><strong>方案</strong>：</p><ul><li>优化CAPL脚本（避免<code>while</code>循环）</li><li>启用<code>SetTimerCyclic()</code>替代<code>on message</code>事件</li></ul><h3 id="多通道同步"><a href="#多通道同步" class="headerlink" title="多通道同步"></a>多通道同步</h3><p><strong>问题</strong>：跨通道信号依赖导致逻辑冲突<br><strong>方案</strong>：</p><ul><li>使用<code>putValue()</code>同步环境变量</li><li>全局状态机管理转发逻辑</li></ul><h3 id="真实ECU兼容性"><a href="#真实ECU兼容性" class="headerlink" title="真实ECU兼容性"></a>真实ECU兼容性</h3><p><strong>问题</strong>：仿真网关与物理ECU协议栈差异<br><strong>方案</strong>：</p><ul><li>添加协议层适配（如J1939 TP转发）</li><li>休眠唤醒协调（集成OSEK NM）</li></ul><hr><h2 id="结语"><a href="#结语" class="headerlink" title="结语"></a>结语</h2><p>通过CANoe构建虚拟网关实现故障注入，工程师能在早期验证系统安全边界与容错机制。随着智能网联汽车复杂度提升，此类技术将成为E&#x2F;E架构开发的标准手段。建议进一步探索：</p><ul><li><strong>AI驱动的自适应故障注入</strong>（基于模型预测异常）</li><li><strong>混合型攻击场景</strong>（如DoS+重放组合攻击）</li><li><strong>与信息安全联动</strong>（基于ISO&#x2F;SAE 21434的渗透测试）</li></ul><blockquote><p><strong>参考</strong>：</p><ul><li><a href="https://vector.com/capl">CAPL API参考手册</a></li></ul></blockquote>]]></content>
    
    
    <summary type="html">CANoe制作网关实现CAN报文故障</summary>
    
    
    
    <category term="自动驾驶" scheme="https://blog.felicx.eu.org/categories/%E8%87%AA%E5%8A%A8%E9%A9%BE%E9%A9%B6/"/>
    
    
    <category term="can" scheme="https://blog.felicx.eu.org/tags/can/"/>
    
  </entry>
  
  <entry>
    <title>BitTorrent 技术解析</title>
    <link href="https://blog.felicx.eu.org/2919340134.html"/>
    <id>https://blog.felicx.eu.org/2919340134.html</id>
    <published>2025-07-20T07:31:20.000Z</published>
    <updated>2025-07-26T09:06:00.000Z</updated>
    
    <content type="html"><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>经常在网上飙车的老司机应该都知道BT下载，但是有时候拿到了种子却下载不动，会不会很抓狂，是不是还觉得是自己网不行，那作为一个合格的老司机，我们需要探究一下下载不动的原因是什么，BT 的运作方式是怎样的，如果你也有这样的疑惑，那么，系好安全带，我们一起来了解一下什么是 BT。</p><h2 id="从-P2P-说起"><a href="#从-P2P-说起" class="headerlink" title="从 P2P 说起"></a>从 P2P 说起</h2><p>P2P 有着很广泛的应用，比如P2P金融（雾），区块链，BT下载等。它的关键字是去中心化，依靠用户群（Peers）来互相传输数据，符合这种特征的都可以称之为P2P。</p><p>BitTorrent（简称BT）由 Bram Cohen 于2001年提出，其核心思想是通过去中心化的用户协作实现大文件高效分发。与传统HTTP依赖单一服务器不同，BT将文件传输负担分散到参与下载的所有用户节点（Peer）上，实现“下载即上传，人人为我，我为人人”的共享模式。这一设计显著降低了服务器带宽压力，并在用户规模增大时形成正反馈效应——参与者越多，整体下载速度越快。</p><h2 id="种子文件"><a href="#种子文件" class="headerlink" title="种子文件"></a>种子文件</h2><p>大家肯定有在互联网上下载各种资源的经历，比如电影电视剧，我们在网上一搜，就会搜到一些不知名的小网站，网站上通常会提供一个叫做「种子」的东西，我们使用时只需要把种子下载到电脑上，通常是一个后缀为<code>.torrent</code>的文件， 然后用迅雷或者其他的下载工具下载。</p><p>种子文件是采用 Bencode 编码的元数据文件，Bencode 编码规范如下</p><table><thead><tr><th>数据类型</th><th>编码规则</th><th>示例</th></tr></thead><tbody><tr><td>字符串</td><td>长度:内容</td><td>“hello” → “5:hello”</td></tr><tr><td>整数</td><td>i数字e</td><td>123 → “i123e”</td></tr><tr><td>列表</td><td>l元素e</td><td>[1,”a”] → “li1e1:ae”</td></tr><tr><td>字典</td><td>d排序键值对e</td><td>{“b”:2,”a”:1} → “d1:ai1e1:bi2ee”</td></tr></tbody></table><p>种子文件核心字段包括</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">&#123;</span><br><span class="line">  <span class="string">&quot;announce&quot;</span>: <span class="string">&quot;https://tracker.example.com&quot;</span>,  <span class="comment"># Tracker主地址</span></span><br><span class="line">  <span class="string">&quot;announce-list&quot;</span>: [[<span class="string">&quot;备用Tracker&quot;</span>]],         <span class="comment"># 备用Tracker</span></span><br><span class="line">  <span class="string">&quot;info&quot;</span>: &#123;</span><br><span class="line">    <span class="string">&quot;name&quot;</span>: <span class="string">&quot;文件名&quot;</span>,</span><br><span class="line">    <span class="string">&quot;piece length&quot;</span>: <span class="number">262144</span>,                  <span class="comment"># 分块大小(通常256KB)</span></span><br><span class="line">    <span class="string">&quot;pieces&quot;</span>: [<span class="string">&quot;sha1哈希值列表&quot;</span>],             <span class="comment"># 分块校验码</span></span><br><span class="line">    <span class="string">&quot;length&quot;</span>: 文件总字节数,</span><br><span class="line">    <span class="string">&quot;files&quot;</span>: [&#123;<span class="string">&quot;path&quot;</span>:[<span class="string">&quot;目录结构&quot;</span>],<span class="string">&quot;length&quot;</span>:大小&#125;] <span class="comment"># 多文件结构</span></span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>一个种子文件中的 info.pieces 字段包含了每个小块的哈希值，而每个块的大小都是 256KB。这样就相当于把下载任务分解了，分解成了下载若干个大小为 256KB 的小块的任务。采用并发的方式去依次请求每个块的数据，然后计算好数据的位置放进最终结果中返回，然后把内存中的数据写入硬盘。</p><h2 id="BitTorrent-工作全流程"><a href="#BitTorrent-工作全流程" class="headerlink" title="BitTorrent 工作全流程"></a>BitTorrent 工作全流程</h2><p>简单讲下BT下载的整个流程</p><ol><li>种子发布者制作种子，且向 Tracker 服务器表明，大家要下载这个种子就来找我。（Tracker 的地址就是种子文件中 announce 字段中的 url）；</li><li>种子发布者把做好的种子分享到互联网；</li><li>下载者在互联网上获取到种子文件；</li><li>下载者本地的 BT 客户端解析种子文件，拿到 Tarcker 地址，向 Tarcker 发起请求（HTTP或UDP），获取其他 Peer 的地址；</li><li>Tracker接收到请求后，去自己的存储里找拥有这个种子中的文件的 peers 的 IP:port，返回给下载者，并且把当前下载者的 IP:Port 加入服务器的存储；</li><li>下载者与其他 Peer 建立连接，由于一个文件被分成了若干个文件块，所以下载者可以和多个 Peer 下载不同的块，下载完成后，校验块的哈希值，保存在本地。(这也是下载种子的人越多，下载速度越快的原因)；</li><li>整个文件下载完成时，校验整个文件哈希值，不出意外，下载成功；</li><li>BT客户端不要关闭，自己作为 Peer 服务 BT 网络中的其他人；</li></ol><h3 id="Tracker-的作用"><a href="#Tracker-的作用" class="headerlink" title="Tracker 的作用"></a>Tracker 的作用</h3><p>种子文件中的 announce 字段中包含了一个 url ，这个 url 也就是 tracker 服务器。</p><p>服务器的作用是作为 peers 沟通的桥梁而存在，当下载者要下载某一个资源的时候，就会去向服务器询问，服务器查询之后如果发现自己保存了这个资源的其他节点，就把这些节点的地址返回，然后客户端知道这些 IP:Port 后，就去与其他 Peer 建立连接。</p><p>Tracker 不存储任何具体资源的文件信息，只存储文件的哈希值，来帮助 Peers 来建立连接。</p><h3 id="发布者做种"><a href="#发布者做种" class="headerlink" title="发布者做种"></a>发布者做种</h3><p>种子只有先被制作发布，才能使用。一般来说，使用 BT 下载软件进行做种的时候，下载软件会内置几个 tracker 服务器，当然也可以自己找一些 tarcker 的地址添加进去，BT客户端会向这些 tracker 发起请求。Tracker 服务器就会记录下来上传者的 IP:Port ，以便于传输给后续下载者进行下载。</p><p>那如果下载者要下载的时候，所有拥有这些文件的人都不在线怎么办。那就真没办法了，这种种子也叫「死种」，因为没人上传，这也就是网上很多种子下载不动的原因。</p><h3 id="下载者下载"><a href="#下载者下载" class="headerlink" title="下载者下载"></a>下载者下载</h3><p>下载者与 Trakcer 的通信方式有 UDP 和 HTTP 两种协议，具体使用哪一种，看种子里面包含的信息是 udp 还是 http。下面我们以 HTTP 的方式来进行探究：</p><p>下载者向 Tracker 发起一个 GET 请求，请求的格式包含的关键字段：</p><table><thead><tr><th>Key</th><th>含义</th></tr></thead><tbody><tr><td>info_hash</td><td>文件的哈希值，是一个资源的唯一标志</td></tr><tr><td>peer_id</td><td>由本地客户端自己生成，一个随机20字节的字符串，向网络中的其他Peers标记自己的身份</td></tr><tr><td>Port</td><td>本地客户端的监听接口，用于接收其他Peer发来的消息</td></tr></tbody></table><p>Tracker 接收到 GET 请求后，Tracker服务器就会反连（NatCheck）下载者的IP地址和端口。然后服务器返回现在正在下载这个文件的所有公网用户的IP地址和端口列表，返回给BT客户端。最后如果该用户是公网用户，Tracker服务器会把用户提交的IP地址和端口保存下来，这样其他人就可以找到该用户。</p><p>BT客户端得到这些其他用户IP后，就可以直接连接到这些IP和端口下载资料了。BT客户端会到所有的用户去寻找自己要下载的东西。BT客户端每找到一个用户就建立一个 Socket来下载 ，所以下载的人越多，速度就越快。</p><h3 id="Peer通信协议"><a href="#Peer通信协议" class="headerlink" title="Peer通信协议"></a>Peer通信协议</h3><p>节点间通过 TCP（默认端口6881-6889）或 UTP（基于UDP的拥塞控制协议）传输数据，消息结构如下：</p><table><thead><tr><th>消息类型</th><th>消息ID</th><th>字段说明</th></tr></thead><tbody><tr><td>握手（Handshake）</td><td>-</td><td>协议名（19字节）、InfoHash、Peer ID</td></tr><tr><td>Bitfield</td><td>5</td><td>比特位图（标识已拥有的分块）</td></tr><tr><td>Request</td><td>6</td><td>请求的分块索引、偏移量、长度</td></tr><tr><td>Piece</td><td>7</td><td>分块索引、偏移量、实际数据</td></tr><tr><td>Have</td><td>4</td><td>声明新获得的分块索引</td></tr><tr><td>Choke&#x2F;Unchoke</td><td>0&#x2F;1</td><td>阻塞&#x2F;解阻塞对方下载权限（流量控制）</td></tr></tbody></table><h3 id="校验哈希值"><a href="#校验哈希值" class="headerlink" title="校验哈希值"></a>校验哈希值</h3><p>当下载者收到数据提供者发送过来的二进制数据后，会计算一下这个二进制数据的哈希值，然后和自己的 torrent 文件中的对应块的哈希值进行比较，如果哈希值一样，就代表这块块有效，就保存下来，否则视为无效，会丢弃这个块重新下载。</p><h2 id="BitTorrent-的延伸"><a href="#BitTorrent-的延伸" class="headerlink" title="BitTorrent 的延伸"></a>BitTorrent 的延伸</h2><p>BT下载严重依赖于 Tracker 服务器，那是不是意味着 Tracker 崩了整个 BT 网络就崩了，当然 Tracker 可能不止有一个，多个 Tracker 服务器可以增加 P2P 网络的容错性，那有有没有一种比较优雅的解决方案，来更好的解决这个问题呢？</p><p>为了摆脱对 Tracker 服务器的依赖，彻底去中心化，这时候，DHT 出现了，DHT全称分布式哈希表(Distributed Hash Table)，是一种分布式存储方法。在不需要服务器的情况下，每个节点负责一个小范围的路由，并负责存储一小部分数据，从而实现整个DHT网络的寻址和存储，相当于所有人一起构成了一个庞大的分布式存储数据库。</p><p>在 DHT 网络中每个节点拥有两个角色：</p><ul><li>作为 BT 下载的节点，来进行上传和下载资源；</li><li>作为DHT网络中的一员，作为一个小型 Tracker ，保存一部分其他 Peer 的地址信息；</li></ul><p>DHT 的本质是把所有人都变成一个小型 Tracker，每个人都拿着一份动态更新的地址和文件信息。当需要进行下载的时候，先根据自己本地存的路由表找其他节点，其他节点再去找他们保存的其他节点，直到找到拥有文件的人。一传十十传百、千、万，最后通过N个人的中转，找到应该连上的人。</p><h3 id="磁力链接"><a href="#磁力链接" class="headerlink" title="磁力链接"></a>磁力链接</h3><p>目前最流行的下载方式是磁力链接（Magnet URI scheme）就是基于 DHT 网络的，格式为<code>magnet:?xt=urn:btih:&lt;InfoHash&gt;</code>。</p><p>其中 urn 为统一资源名称，btih 是 BitTorrent Info Hash 的缩写，是 BitTorrent 使用的 Hash 函数。除了 btih 还可以是其他类型的 Hash 函数，但不如 btih 用的多。这一串长度为 40 的字符串正是文件内容的 Hash，BT下载工具就根据这个 Hash 在DHT网络中定位下载文件。</p>]]></content>
    
    
    <summary type="html">BitTorrent核心技术浅析</summary>
    
    
    
    <category term="折腾系列" scheme="https://blog.felicx.eu.org/categories/%E6%8A%98%E8%85%BE%E7%B3%BB%E5%88%97/"/>
    
    
    <category term="bt" scheme="https://blog.felicx.eu.org/tags/bt/"/>
    
  </entry>
  
  <entry>
    <title>VirtualBox 中 Ubuntu 虚拟机扩容指南</title>
    <link href="https://blog.felicx.eu.org/3761641296.html"/>
    <id>https://blog.felicx.eu.org/3761641296.html</id>
    <published>2025-06-22T08:27:51.000Z</published>
    <updated>2025-06-22T09:00:51.000Z</updated>
    
    <content type="html"><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>当 Ubuntu 虚拟机的磁盘空间不足时，只需两步：扩展 VDI 磁盘文件，再调整分区和文件系统。本教程提供两种 VDI 扩容方案及详细操作步骤，助你快速解决空间不足问题。</p><h2 id="一、VDI-扩容（两种方案）"><a href="#一、VDI-扩容（两种方案）" class="headerlink" title="一、VDI 扩容（两种方案）"></a>一、VDI 扩容（两种方案）</h2><h3 id="方案1：通过克隆创建新-VDI（兼容性强）"><a href="#方案1：通过克隆创建新-VDI（兼容性强）" class="headerlink" title="方案1：通过克隆创建新 VDI（兼容性强）"></a>方案1：通过克隆创建新 VDI（兼容性强）</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 进入VirtualBox安装目录（Windows）</span></span><br><span class="line"><span class="built_in">cd</span> <span class="string">&#x27;C:\Program Files\Oracle\VirtualBox&#x27;</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 创建新VDI（目标大小为 200GB）</span></span><br><span class="line">.\VBoxManage.exe createhd -filename <span class="string">&quot;D:\NewUbuntu\new_ubuntu.vdi&quot;</span> -size 204800 -format VDI -variant Standard</span><br><span class="line"></span><br><span class="line"><span class="comment"># 克隆原VDI到新VDI（前面为原来的VDI，后面为扩大size的VDI）</span></span><br><span class="line">.\VBoxManage.exe clonehd <span class="string">&#x27;D:\原路径\ubuntu.vdi&#x27;</span> <span class="string">&#x27;D:\NewUbuntu\new_ubuntu.vdi&#x27;</span> --existing</span><br><span class="line"></span><br><span class="line"><span class="comment"># 扩容新VDI（调整至目标大小）</span></span><br><span class="line">.\VBoxManage.exe modifyhd <span class="string">&#x27;D:\NewUbuntu\new_ubuntu.vdi&#x27;</span> --resize 204800</span><br></pre></td></tr></table></figure><p><img src="/assets/post/20250622_I1xc1zIS.webp" alt="virtualbox"><br>操作后步骤：</p><ol><li>在 VirtualBox 中卸载原 VDI，挂载新 VDI（设置→存储→移除旧盘→添加新盘）</li><li>启动 Ubuntu 验证数据完整性</li></ol><h3 id="方案2：直接扩容原-VDI（更快捷）"><a href="#方案2：直接扩容原-VDI（更快捷）" class="headerlink" title="方案2：直接扩容原 VDI（更快捷）"></a>方案2：直接扩容原 VDI（更快捷）</h3><p>仅适用于动态分配的 VDI：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 关闭虚拟机后执行（Windows）</span></span><br><span class="line">.\VBoxManage.exe modifyhd <span class="string">&quot;D:\原路径\ubuntu.vdi&quot;</span> --resize 204800</span><br></pre></td></tr></table></figure><blockquote><p>✅ 说明：动态分配的 VDI 支持直接扩容；固定分配需用方案1克隆转换</p></blockquote><h2 id="二、Ubuntu-分区调整（GParted-操作）"><a href="#二、Ubuntu-分区调整（GParted-操作）" class="headerlink" title="二、Ubuntu 分区调整（GParted 操作）"></a>二、Ubuntu 分区调整（GParted 操作）</h2><h3 id="步骤1：安装-GParted"><a href="#步骤1：安装-GParted" class="headerlink" title="步骤1：安装 GParted"></a>步骤1：安装 GParted</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">sudo apt update</span><br><span class="line">sudo apt install gparted -y</span><br></pre></td></tr></table></figure><h3 id="步骤2：调整分区（重点！）"><a href="#步骤2：调整分区（重点！）" class="headerlink" title="步骤2：调整分区（重点！）"></a>步骤2：调整分区（重点！）</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo gparted  <span class="comment"># 启动图形工具</span></span><br></pre></td></tr></table></figure><ol><li>定位分区：<br><img src="/assets/post/20250622_AFEbZjgt.webp" alt="virtualbox"></li></ol><ul><li>&#x2F;dev&#x2F;sda1：系统主分区（需扩展），是我们原来的大小</li><li>linux-swap：交换分区（通常为 sda2&#x2F;sda5）</li><li>unallocated：扩容后未分配的空间</li></ul><ol start="2"><li>删除交换分区：</li></ol><ul><li>右键 &#x2F;dev&#x2F;sda5 分区 → Swapoff<br><img src="/assets/post/20250622_JE01JH3Q.webp" alt="virtualbox"></li><li>删除 &#x2F;dev&#x2F;sda2<br><img src="/assets/post/20250622_hK0kbWBX.webp" alt="virtualbox"></li></ul><ol start="3"><li>扩展主分区：</li></ol><ul><li>右键 &#x2F;dev&#x2F;sda1 → Resize&#x2F;Move</li><li>拖动滑块将 unallocated 空间合并到 sda1</li><li>关键设置：<ul><li>Align to 选择 MiB（解决无法拖动问题）</li><li>保留约 1GB 空间给新交换分区（非强制）<br><img src="/assets/post/20250622_lcUucBZX.webp" alt="virtualbox"><br><img src="/assets/post/20250622_vFITEThi.webp" alt="virtualbox"><blockquote><p>New size就是新的磁盘大小，Free space就是剩下的给交换空间的，这里给了1g(可能点不了resize，需要点一下Align to选MiB)</p></blockquote></li></ul></li></ul><ol start="4"><li>重建交换分区：</li></ol><ul><li>右键剩余 unallocated → New → 分区类型选 Extended Partition<br><img src="/assets/post/20250622_61ZvKLg6.webp" alt="virtualbox"></li><li>在新扩展分区上创建 linux-swap（文件系统选 linux-swap）<br><img src="/assets/post/20250622_ePQLr1un.webp" alt="virtualbox"></li></ul><ol start="5"><li>应用变更：</li></ol><ul><li>点击✅ → Apply（等待操作完成）<br><img src="/assets/post/20250622_RNIvcVPV.webp" alt="virtualbox"></li></ul><h3 id="步骤3：验证扩容"><a href="#步骤3：验证扩容" class="headerlink" title="步骤3：验证扩容"></a>步骤3：验证扩容</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">df</span> -h      <span class="comment"># 查看根分区容量是否增加</span></span><br><span class="line">free -h    <span class="comment"># 检查交换分区是否生效</span></span><br></pre></td></tr></table></figure><h2 id="三、注意事项及常见问题"><a href="#三、注意事项及常见问题" class="headerlink" title="三、注意事项及常见问题"></a>三、注意事项及常见问题</h2><ul><li>必备操作：</li></ul><ol><li>所有 VDI 操作前关闭虚拟机</li><li>扩容前备份重要数据（防操作失误）</li><li>Windows下需以管理员身份运行命令提示符</li></ol><ul><li><p>常见错误：</p></li><li><p>UUID 冲突：克隆 VDI 后需在 VirtualBox 中卸载原盘（防冲突）</p></li><li><p>分区无法调整：检查是否关闭交换分区（Swapoff）</p></li><li><p>文件系统未扩展：扩容分区后需执行 sudo resize2fs &#x2F;dev&#x2F;sda1（ext4分区）</p></li><li><p>替代方案：<br>若GParted操作复杂，可使用终端扩展分区：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">sudo parted /dev/sda</span><br><span class="line">resizepart 1   <span class="comment"># 1为主分区号</span></span><br><span class="line">Yes</span><br><span class="line">200GB          <span class="comment"># 输入目标大小</span></span><br><span class="line">quit</span><br><span class="line">sudo resize2fs /dev/sda1  <span class="comment"># 扩展文件系统</span></span><br></pre></td></tr></table></figure></li></ul><h2 id="四、结论"><a href="#四、结论" class="headerlink" title="四、结论"></a>四、结论</h2><p>通过 VDI 扩容 + 分区调整，可高效解决 Ubuntu 虚拟机磁盘不足问题：</p><ol><li>VDI扩容优先选直接扩容（方案2），动态分配磁盘首选</li><li>固定分配磁盘必须克隆转换（方案1）</li><li>分区调整推荐 GParted 图形化操作，注意删除交换分区后再扩展主分区</li></ol><blockquote><p>📌 终极建议：创建虚拟机时选择 动态分配 并预留充足空间（如 200GB），避免频繁扩容！</p></blockquote>]]></content>
    
    
    <summary type="html">VirtualBox 安装了 VDI 类型的 Ubuntu，给磁盘空间扩容</summary>
    
    
    
    <category term="开发运维" scheme="https://blog.felicx.eu.org/categories/%E5%BC%80%E5%8F%91%E8%BF%90%E7%BB%B4/"/>
    
    
    <category term="linux" scheme="https://blog.felicx.eu.org/tags/linux/"/>
    
  </entry>
  
  <entry>
    <title>原本只想擦亮星星，却用一生唤醒太阳</title>
    <link href="https://blog.felicx.eu.org/963147591.html"/>
    <id>https://blog.felicx.eu.org/963147591.html</id>
    <published>2025-05-11T06:10:17.000Z</published>
    <updated>2025-05-11T07:09:20.000Z</updated>
    
    <content type="html"><![CDATA[<div class="tag-plugin tabs" align="center"id="tab_57"><div class="nav-tabs"><div class="tab active"><a href="#tab_57-1">个人笔记</a></div><div class="tab"><a href="#tab_57-2">语句摘录</a></div></div><div class="tab-content"><div class="tab-pane active" id="tab_57-1"><h4 id="故事的核心：从“小爱”到“大爱”的升华"><a href="#故事的核心：从“小爱”到“大爱”的升华" class="headerlink" title="故事的核心：从“小爱”到“大爱”的升华"></a>故事的核心：从“小爱”到“大爱”的升华</h4><p>《烧火工》是刘慈欣笔下罕见的温情童话，表面上讲述少年萨沙为拯救爱人冰儿踏上冒险之旅的故事，内核却是一场关于人性、责任与宇宙秩序的哲学思辨。</p><p>萨沙的初心是纯粹的个人情感——为了治愈冰儿的绝症，他甘愿与世界尽头的烧火工签订契约，承诺接替其点燃太阳的使命。然而，当冰儿康复后，萨沙并未选择回归俗世，而是留在极东岛成为新一任烧火工。</p><p>这一选择的转折点，恰恰揭示了故事的深层隐喻：爱不仅是私欲的满足，更是一种对更广阔生命的责任感。</p><p>起初，他的行动由“小爱”驱动，带有强烈的功利性；但当他目睹烧火工日复一日的孤独劳作，甚至目睹鲸鱼因人类的“工具理性”而牺牲时（烧火工以鲸骨制笛诱捕鲸鱼，用其脂肪炼油点燃太阳），他逐渐意识到生命的关联性。</p><p>这种关联性超越了个人情感，指向一种宇宙尺度的责任——正如烧火工所言：“如果我们不烧火，黑夜就不会结束。”</p><p>萨沙的选择，本质上是对“存在意义”的重新锚定：从“为一个人活”到“为千万人活”，从“被爱驱使”到“以爱为燃料”。</p><h4 id="烧火工的象征：孤独与使命的悖论"><a href="#烧火工的象征：孤独与使命的悖论" class="headerlink" title="烧火工的象征：孤独与使命的悖论"></a>烧火工的象征：孤独与使命的悖论</h4><p>烧火工的形象充满矛盾：他是人类光明的守护者，却常年与黑暗为伴；他掌握着宇宙的奥秘（擦亮星星、点燃太阳），却过着最原始的生活（挖煤、捕鲸）。</p><p>这种反差暗含对现代文明的反思：真正的奉献往往需要退守到文明的边缘，甚至以牺牲个体幸福为代价。</p><p>烧火工好比古希腊神话中的普罗米修斯，但两者的差异更耐人寻味。</p><p>普罗米修斯盗火是对神权的反抗，充满英雄主义的悲壮；而烧火工的劳作却是静默的、循环的，如同西西弗斯推石上山。这种“无英雄的史诗”更贴近普通人的生存状态——我们或许不必惊天动地，但需在琐碎中坚守本心，默默维护系统运转。</p><h4 id="科幻设定中的诗意与残酷"><a href="#科幻设定中的诗意与残酷" class="headerlink" title="科幻设定中的诗意与残酷"></a>科幻设定中的诗意与残酷</h4><p>刘慈欣将硬核科幻元素融入童话框架，形成独特的张力：</p><ul><li>浪漫想象：月亮是柔软的乳白色球体，星星碰撞发出风铃般声响，登天需用鲸牙制成火箭；</li><li>冰冷现实：鲸鱼因人类的生存需求被工具化（骨为笛、脂为油、皮为屋），太阳的点燃伴随着逃亡般的惊险。</li></ul><p>这种矛盾恰是人类的生存缩影。我们仰望星空时渴望诗意，却不得不面对资源争夺的残酷。</p><p>如果按书中所写，要拯救千万人需牺牲一个物种，这种选择是否正当？刘慈欣未给出答案，却通过萨沙的沉默暗示了现实的复杂性。</p></div><div class="tab-pane" id="tab_57-2"><blockquote><p>“地上的每一个人，在天上都有一颗属于他的星星。如果那颗星星出了毛病，星光照不到那人身上，那人就病了；如果星光长时间暗下去，那人就得了绝症。”<br>“一般人的死法，他们的星星化成流星，大部分在落地前就烧光了，有些剩下的部分落到地上，也不过是一块平淡无奇的石头。”</p></blockquote><p>这两句将人的生命与星辰关联，赋予疾病与死亡以浪漫的象征。星辰的明暗成为生命的晴雨表，而流星的消逝则暗喻凡人生命的平凡与脆弱。这种设定既呼应了“天人感应”的古典哲学，又隐含对现代人存在意义的追问：当个体的光芒被遮蔽时，是否有人愿意为其拭去尘埃？</p><hr><blockquote><p>“月面摸上去细腻光滑，像冰儿的肌肤，这让萨沙心里一动。”<br>“星星不时碰到航行中的月牙，发出悠扬清脆的叮铃声，像夏日微风中的风铃。”</p></blockquote><p>刘慈欣颠覆了月亮坚硬冰冷的常规想象，赋予其柔软与温度。月面的触感与冰儿的肌肤相联，暗示爱情是萨沙冒险的动力；而星星碰撞月牙的“叮铃声”，则将登天之旅化作一场听觉与视觉交织的童话漫游，充满童真与梦幻。</p><hr><blockquote><p>“每天凌晨如果我们不烧火，黑夜就不会结束。”<br>“太阳漆黑一片，像夜的精华……点燃后腾起一片蓝色的火焰，世界从黑夜中复苏。”</p></blockquote><p>太阳的熄灭与点燃象征着生命循环与责任传承。烧火工的工作被赋予神性，却又充满凡人的艰辛——必须“按时烧火，不能早也不能晚”。这种对时间精准性的苛求，暗喻维系世界运转的平凡英雄们不可言说的孤独与坚持。</p><hr><blockquote><p>“鲸鱼因笛声的诱惑冲上海岸，窒息而亡。人们取鲸骨制笛、炼油，用其脂肪点燃太阳。”<br>“大鲸盲目地以为有另一只鲸在等它，满怀希望奔向死亡。”</p></blockquote><p>鲸的悲剧揭示了人类生存的伦理困境：为拯救千万人而牺牲一个物种是否正当？鲸被笛声（爱的谎言）诱捕，与萨沙为爱赴险形成镜像，暗示“爱”既是救赎的起点，也可能成为暴力的借口。这一设定引发对工具理性与生态伦理的反思。</p><hr><blockquote><p>“你懂得爱。”老烧火工对萨沙说，“这和信用无关。”<br>“萨沙抚弄阳光，最欣慰的是这阳光也有冰儿一份。”</p></blockquote><p>萨沙的“爱”从拯救冰儿的私欲升华为守护世界的大爱。老烧火工看透本质：真正的爱不是契约，而是甘愿将个体情感融入更广阔的使命。阳光中的“冰儿一份”将个人幸福与众生光明融为一体，完成从“小我”到“大我”的蜕变。</p><hr><blockquote><p>“火箭爆炸时溅出的火星，像在月亮上放了一个焰火。”<br>“海鸥向日出的地方飞去，太阳越升越高，将云雾烧成朝霞。”</p></blockquote><p>这些场景以极简的笔触勾勒出宏大与渺小的对比。焰火的璀璨转瞬即逝，暗喻登天壮举的偶然与脆弱；海鸥追逐朝阳则象征生命对光明的本能向往，赋予科幻叙事以古典诗画的意境。</p></div></div></div>]]></content>
    
    
    <summary type="html">《烧火工》-- 刘慈欣</summary>
    
    
    
    <category term="读书笔记" scheme="https://blog.felicx.eu.org/categories/%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B0/"/>
    
    
    <category term="科幻" scheme="https://blog.felicx.eu.org/tags/%E7%A7%91%E5%B9%BB/"/>
    
  </entry>
  
  <entry>
    <title>CANoe 常用操作指南</title>
    <link href="https://blog.felicx.eu.org/108916185.html"/>
    <id>https://blog.felicx.eu.org/108916185.html</id>
    <published>2025-04-06T09:24:17.000Z</published>
    <updated>2025-04-13T09:46:40.000Z</updated>
    
    <content type="html"><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>CANoe 是德国 Vector 公司为汽车总线的开发而设计的一款总线开发环境，全称叫 CAN open environment。CANoe 集合了网络监控、数据获取&#x2F;记录、节点仿真、诊断、自动测试等功能。</p><hr><h2 id="一、新建工程与基础配置"><a href="#一、新建工程与基础配置" class="headerlink" title="一、新建工程与基础配置"></a>一、新建工程与基础配置</h2><h3 id="1-1-新建工程"><a href="#1-1-新建工程" class="headerlink" title="1.1 新建工程"></a>1.1 新建工程</h3><ol><li>启动 CANoe 软件，执行 <strong>File → New → Available Templates</strong></li><li>根据需求选择模板类型：<ul><li><strong>CAN 500k 1ch</strong>：单通道虚拟 CAN，默认波特率 500kbps</li><li><strong>CAN 500k 2ch</strong>：双通道虚拟 CAN，支持后续硬件扩展</li></ul></li><li>保存工程为 <code>.cfg</code> 格式文件<br><img src="/assets/post/20250413_DSbNv6tS.webp" alt="新建工程模板"></li></ol><hr><h3 id="1-2-通道配置与硬件映射"><a href="#1-2-通道配置与硬件映射" class="headerlink" title="1.2 通道配置与硬件映射"></a>1.2 通道配置与硬件映射</h3><ol><li><p><strong>设置通道数目</strong>：进入 <strong>Hardware -&gt; Channel Usage</strong>，配置网络通道数目，需确保不超过 CANoe 硬件最大通道数。<br>  <img src="/assets/post/20250413_q1D9Bg3h.webp" alt="通道数目"></p></li><li><p><strong>设置波特率</strong>：在 <strong>Hardware -&gt; Network Hardware -&gt; Setup</strong> 中设置 CAN 波特率。<br>  <img src="/assets/post/20250413_rIOmuKcW.webp" alt="波特率"></p></li><li><p><strong>设置滤波</strong>：在 <strong>Acceptance Filter</strong> 中配置标准帧&#x2F;扩展帧的滤波规则（ID 掩码、范围）。<br>  <img src="/assets/post/20250413_DOfr9L1J.webp" alt="滤波"></p></li><li><p><strong>硬件通道映射</strong>：进入 <strong>Application -&gt; Channel Mapping</strong>，将软件通道与物理硬件通道绑定（如将 CAN 1 映射到硬件 Channel 1）。<br>  <img src="/assets/post/20250413_oEJF4yKn.webp" alt="通道映射"></p></li></ol><hr><h3 id="1-3-DBC-文件导入"><a href="#1-3-DBC-文件导入" class="headerlink" title="1.3 DBC 文件导入"></a>1.3 DBC 文件导入</h3><p>两种导入方式：</p><ol><li><p><strong>直接添加</strong>：进入 <strong>NetWorks -&gt; Database</strong>，右键选择 <strong>Add</strong>，导入 DBC 文件。<br><img src="/assets/post/20250413_N6jp9tKY.webp" alt="导入DBC_1"></p></li><li><p><strong>向导导入</strong>：使用 <strong>Import Wizard</strong> 自动解析 DBC 文件中的网络节点，并添加到仿真节点中。<br><img src="/assets/post/20250413_j5VfhLLk.webp" alt="导入DBC_2"><br><img src="/assets/post/20250413_RFNRhqEX.webp" alt="导入DBC_3"></p></li></ol><hr><h2 id="二、常用操作与功能"><a href="#二、常用操作与功能" class="headerlink" title="二、常用操作与功能"></a>二、常用操作与功能</h2><h3 id="2-1-使用-Trace-工具监控报文"><a href="#2-1-使用-Trace-工具监控报文" class="headerlink" title="2.1 使用 Trace 工具监控报文"></a>2.1 使用 Trace 工具监控报文</h3><ol><li>点击 <strong>Analysis -&gt; Trace</strong> 打开 Trace 界面。</li><li>运行工程（点击闪电图标），选择正确的 CAN 通道（参考 <strong>Simulation Setup -&gt; Channels</strong>）。</li><li><strong>过滤功能</strong>：<ul><li>点击 ID 列的过滤图标，按信号 ID 过滤报文；</li><li>点击标志 3 处的图标切换报文显示模式（逐条显示或仅显示最后一次的此 ID 报文）；</li><li>标志 1 处的图标清空当前 Trace 界面的报文显示；</li><li>标志 2 处的图标停止&#x2F;更新 Trace 界面的报文显示。<br><img src="/assets/post/20250413_L3CXtvGu.webp" alt="Trace界面"></li></ul></li></ol><hr><h3 id="2-2-使用-Logging-保存日志"><a href="#2-2-使用-Logging-保存日志" class="headerlink" title="2.2 使用 Logging 保存日志"></a>2.2 使用 Logging 保存日志</h3><ol><li>进入 <strong>Analysis -&gt; Measurement Setup</strong>，右键 <strong>Logging</strong>，选择 <strong>Logging File Configuration</strong> 设置保存路径。<br><img src="/assets/post/20250413_XZWaKfUp.webp" alt="Logging配置"></li><li>双击暂停箭头图标启用日志记录，运行工程后所有报文交互将被保存（<code>.blf</code> 格式）。<br><img src="/assets/post/20250413_CbexzhBG.webp" alt="Logging开启"></li></ol><hr><h3 id="2-3-创建-IG-发送报文"><a href="#2-3-创建-IG-发送报文" class="headerlink" title="2.3 创建 IG 发送报文"></a>2.3 创建 IG 发送报文</h3><ol><li><p><strong>添加 IG 模块</strong>：在 <strong>Simulation Setup</strong> 界面，右键 <strong>Interactive Generators -&gt; Insert Interactive Generators Block CAN</strong>。<br><img src="/assets/post/20250413_YLB2Dih2.webp" alt="IG发送报文"></p></li><li><p><strong>自定义报文</strong>：双击 IG 模块，右键空白处选择 <strong>Add CAN Frame</strong>，填写 ID 和数据内容，支持单次或周期发送。<br><img src="/assets/post/20250413_s7nQFm1Y.webp" alt="IG发送报文"></p></li><li><p><strong>从 DBC 导入报文</strong>：需先导入 DBC 文件（参考 [1.3 DBC 文件导入]），右键选择 <strong>Add Frame From Database</strong> 添加预定义报文。<br><img src="/assets/post/20250413_516232Qg.webp" alt="IG发送报文"></p></li><li><p><strong>报文发送</strong>：完成 DBC 报文导入后，在 IG 界面对导入的报文进行发送操作。<br><img src="/assets/post/20250413_HvDOJTJG.webp" alt="IG发送报文"></p></li></ol><hr><h3 id="2-4-使用-Graphics-分析信号波形"><a href="#2-4-使用-Graphics-分析信号波形" class="headerlink" title="2.4 使用 Graphics 分析信号波形"></a>2.4 使用 Graphics 分析信号波形</h3><ol><li>打开 <strong>Analysis -&gt; Graphics</strong>，右键选择 <strong>Add Signals</strong>。</li><li><strong>手动定义信号</strong>：在 <strong>Graphics</strong> 界面下右键，选择 <strong>User Defined Signals</strong>，填写报文 ID、信号起始位及长度。<br>  <img src="/assets/post/20250413_s6JjwipM.webp" alt="信号波形"></li><li><strong>从 DBC 导入信号</strong>：在 <strong>Signals</strong> 或 <strong>Frames</strong> 中选择要查看的信号。<br>  <img src="/assets/post/20250413_rvWmSMV0.webp" alt="信号波形"></li><li><strong>多信号显示模式</strong><ul><li><strong>独立显示</strong>：点击图标选择 <strong>Show Signals in Separate Diagrams</strong>。<br><img src="/assets/post/20250413_zPsJpG43.webp" alt="信号波形"></li><li><strong>合并显示</strong>：选择 <strong>Show All Y-Axis</strong>。<br><img src="/assets/post/20250413_oqJKR6Ib.webp" alt="信号波形"></li></ul></li></ol><hr><h3 id="2-5-加载-CDD-诊断文件"><a href="#2-5-加载-CDD-诊断文件" class="headerlink" title="2.5 加载 CDD 诊断文件"></a>2.5 加载 CDD 诊断文件</h3><ol><li>点击 <strong>Diagnostics -&gt; Diagnostic ISO TP</strong>。<br><img src="/assets/post/20250413_7GfTgVh7.webp" alt="CDD加载"></li><li>右击 <strong>CAN</strong>，选择 <strong>Add Diagnostic Description</strong>，导入 CDD 文件。<br><img src="/assets/post/20250413_dX8qukBa.webp" alt="CDD加载"></li><li>通过 <strong>Diagnostic Console</strong> 发送预定义诊断报文。<br><img src="/assets/post/20250413_dnwFORuI.webp" alt="CDD加载"></li></ol><hr><h3 id="2-6-过滤报文接收"><a href="#2-6-过滤报文接收" class="headerlink" title="2.6 过滤报文接收"></a>2.6 过滤报文接收</h3><ol><li><p><strong>硬件级过滤</strong>：进入 <strong>Hardware -&gt; Network Hardware</strong>，配置接收 ID 范围。<br>  <img src="/assets/post/20250413_iv97ukWx.webp" alt="报文过滤"></p></li><li><p><strong>软件级过滤</strong></p><ul><li>在 <strong>Measurement Setup</strong> 中右击图标，选择 <strong>Insert Event Filter</strong>（显示为 CFB 图标）。<br><img src="/assets/post/20250413_BZWMPx5S.webp" alt="报文过滤"></li><li>双击 CFB 图标配置过滤规则。<br><img src="/assets/post/20250413_IUs1NViq.webp" alt="报文过滤"></li></ul></li><li><p><strong>功能模块扩展</strong>：在 <strong>Measurement Setup</strong> 界面右击图标，选择其他功能模块（如 CAPL 程序）。<br>  <img src="/assets/post/20250413_SRez732x.webp" alt="报文过滤"></p></li></ol><hr><h2 id="三、高级功能与统计"><a href="#三、高级功能与统计" class="headerlink" title="三、高级功能与统计"></a>三、高级功能与统计</h2><ol><li><p><strong>总线统计</strong><br>通过 <strong>Analysis -&gt; Statistics</strong> 查看总线负载、报文频率等。<br><img src="/assets/post/20250413_gWlTo4pQ.webp" alt="统计功能"></p></li><li><p><strong>变量监控</strong><br>在 <strong>Analysis -&gt; Data</strong> 中实时监控信号值并保存为 MDF 文件。<br><img src="/assets/post/20250413_dsDtq54H.webp" alt="统计功能"></p></li></ol><hr><h2 id="四、注意事项"><a href="#四、注意事项" class="headerlink" title="四、注意事项"></a>四、注意事项</h2><ol><li>修改硬件配置（如通道映射、波特率）需在工程停止状态下操作。</li><li>导入 DBC&#x2F;CDD 文件时，需确保文件与当前网络协议匹配。</li><li>使用 IG 发送报文时，若未收到响应，需检查通道映射和硬件连接。</li></ol>]]></content>
    
    
    <summary type="html">记录了 CANoe 工程配置还有一些常用操作，方便学习</summary>
    
    
    
    <category term="自动驾驶" scheme="https://blog.felicx.eu.org/categories/%E8%87%AA%E5%8A%A8%E9%A9%BE%E9%A9%B6/"/>
    
    
    <category term="can" scheme="https://blog.felicx.eu.org/tags/can/"/>
    
    <category term="canoe" scheme="https://blog.felicx.eu.org/tags/canoe/"/>
    
  </entry>
  
  <entry>
    <title>北京一日游</title>
    <link href="https://blog.felicx.eu.org/440314130.html"/>
    <id>https://blog.felicx.eu.org/440314130.html</id>
    <published>2025-03-23T02:22:00.000Z</published>
    <updated>2025-03-23T09:30:00.000Z</updated>
    
    <content type="html"><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>最近出差天津，想着难得来一趟，不去北京就亏了。但时间比较极限，晚上9.的飞机，所以只有不到一天的时间逛北京又是特种兵暴走的一天。</p><h2 id="凌晨六点的天津"><a href="#凌晨六点的天津" class="headerlink" title="凌晨六点的天津"></a>凌晨六点的天津</h2><p>被闹钟惊醒时，窗外还飘着零星小雨。昨晚下过雪，地面还是湿的，天气微冷。天津的滴滴给我一种感觉是会提前到，在指定地点等你，跟广州的不太一样。</p><p>到了高铁站，天津南站小得出乎意料，只有一道匝机，就是进高铁的那道，进站不需要过匝机。七点的高铁上，窗外的金光铺满脸颊，一时间疲惫全消。<br><img src="/assets/post/20250323_NRmqLPuW.webp" alt="img"></p><h2 id="京城初印象"><a href="#京城初印象" class="headerlink" title="京城初印象"></a>京城初印象</h2><p>到了北京南站后，第一件事是寄存行李，我不想拖着行李箱到处逛。虽说之前在小红书看了怎么前往高铁站寄存点的路线，实际到了还是迷迷糊糊的。</p><p>北京南站是真大呀，跟广州南有的一拼了。索性不自己找路线了，直接去问检票的小姐姐。小姐姐还特地问我等会回来是坐地铁还是打车，坐地铁就直接寄存在东停车场处。抬头找东停车场的指示标志，一直往前走穿过行李案件的通道，就来到地下停车场，然后左拐走到尽头就能看到行李寄存点了。</p><p>就是有点小贵，收费员还拿着卷尺量尺寸，我的刚刚超一点，25块一天，高铁站物价果然名不虚传（摊手.jpg）。</p><h2 id="胡同里的早餐"><a href="#胡同里的早餐" class="headerlink" title="胡同里的早餐"></a>胡同里的早餐</h2><p>小红书看到的，很多人推荐“增盛魁小吃”。直接就坐地铁前往了。顺便一提，羊城通可以坐北京地铁。</p><p>到了小吃店，人确实多。店面小，里面已经坐满了，我只好排队外带。现炸的糖油饼确实酥脆，就是甜得有点齁嗓子。牛肉饼分量实在，边走边吃时，油脂渗过纸袋沾了满手。</p><p>因为店面没法坐，我就边拎着吃边往天安门广场走，沿着王府井西街慢慢走。穿过一片青砖灰瓦的胡同，晾衣绳上冻硬的秋裤和墙根堆着的蜂窝煤。老妈看了我的照片直接吐槽，为啥北京的房子比老家的还要旧。<br><img src="/assets/post/20250323_6sB8H5bc.webp" alt="img"></p><h2 id="天安门的仪式感"><a href="#天安门的仪式感" class="headerlink" title="天安门的仪式感"></a>天安门的仪式感</h2><p>慢悠悠走了半小时，总算来到金水桥前往故宫方向（我是直接导航天安门西地铁B出口），差不多就跟着人群走就行了。从看见城墙开始就有安检口，一路检查了三遍，人是真的多，排队差不多花了半个小时。还好我这是淡季，不敢想象旺季得排到啥时候。</p><p>天安门对面就是长安街，远远的可以看到人民纪念碑。刚出安检口，就能看到天安门和伟人的画像。不得不说，北京的天真蓝啊。<br><img src="/assets/post/20250323_aH5EAR27.webp" alt="img"></p><h2 id="紫禁城十二时辰"><a href="#紫禁城十二时辰" class="headerlink" title="紫禁城十二时辰"></a>紫禁城十二时辰</h2><p>跟着人群走，走过外金水桥，穿过城门，就能远远看到端门了。挺好奇的，过城门时，那些大爷大妈都在摸城门的鎏金门钉，是有啥讲究吗。我不懂，但还是跟着摸了摸。<br><img src="/assets/post/20250323_YlxzuNhj.webp" alt="img"></p><p>穿过端门，就是很大的空地，前面是午门检票口，左边是天安门城楼的检票口，因为没预约城楼，所以上不去。还没进故宫，给我的感觉就是大，在这里踢足球得累的够喘。</p><p>安检口的人更多，排了不止半小时总算进来了。有一半都是叔叔阿姨辈的，果然老一辈对故宫就是有一种说不清的向往。<br><img src="/assets/post/20250323_YdCVvo5X.webp" alt="img"></p><p>穿过午门，映入眼帘的就是内金水桥。心中的崇敬悠然而生（有一半可能是风吹的发抖），原来古代百官每天都是走这条路去上朝的。</p><p>太和殿前乌泱泱全是旅行团，导游的小旗子在空中乱舞。挤到前排才看清金銮殿的真容——比电视剧里小很多，龙椅上的金漆有些斑驳，突然觉得皇帝上班也挺憋屈。<br><img src="/assets/post/20250323_MB4Uvlax.webp" alt="img"></p><p>来到慈宁宫，说好的甄嬛养老院，结果全是佛像雕塑，跟进了庙会似的。边上就是网红打卡墙。后来回想，确实只有那一片墙是最鲜红的，不知道是不是经常刷漆焕新。</p><p>西六宫的巷道特别窄，旅游团挤得水泄不通。东六宫的延禧宫，西洋烂尾楼让人眼前一亮，钢筋骨架像未完工的现代艺术装置。</p><p>走到御花园时腿已不是自己的。这里好多连理枝，有幸抓拍了个小朋友。<br><img src="/assets/post/20250323_PEaCF9n.webp" alt="img"></p><p>从神武门出来时，护城河水波零零。看夕阳给故宫屋顶镀金，突然明白为什么说”一座紫禁城，半部中国史”。<br><img src="/assets/post/20250323_lrDwDSEa.webp" alt="img"></p><h2 id="归去来兮"><a href="#归去来兮" class="headerlink" title="归去来兮"></a>归去来兮</h2><p>逛完了故宫，本来想着去天坛走走。下了地铁才记起天坛要购票，而且手机只剩10%电量，遂作废直奔高铁站。在南站又兜了一会才找到行李寄存处，候车时肚子开始咕咕叫，想起早上的牛肉包子还没吃完。已经顾不上什么形象了，填饱肚子最重要。</p><p>高铁直接到天津站，然后地铁直达津海国际机场。顺便提一嘴，津海机场最好是上二楼机场进口打车，取行李的是一楼，车进不来，都是拉客的在那里忽悠。广州的白云机场也是，取了行李上二楼机场进口更容易打到车。</p><p>飞机冲入云层时，舷窗外星河低垂。闭眼尽是宫墙日影、糖油饼香、脊兽凝望，忽然懂得梁思成说的：”这些建筑绝不是某一时某一人的产物，而是整个中华民族的集体记忆。”二十四小时的特种兵行程，竟与六百年时光打了个照面。</p>]]></content>
    
    
    <summary type="html">天津-北京特种兵一日游</summary>
    
    
    
    <category term="随笔" scheme="https://blog.felicx.eu.org/categories/%E9%9A%8F%E7%AC%94/"/>
    
    
    <category term="旅游" scheme="https://blog.felicx.eu.org/tags/%E6%97%85%E6%B8%B8/"/>
    
  </entry>
  
  <entry>
    <title>使用 GitHub Actions 实现多项目自动化工作流</title>
    <link href="https://blog.felicx.eu.org/339201242.html"/>
    <id>https://blog.felicx.eu.org/339201242.html</id>
    <published>2025-03-02T07:06:53.000Z</published>
    <updated>2025-03-02T07:30:53.000Z</updated>
    
    <content type="html"><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>在开源项目协作中，自动化工作流（CI&#x2F;CD）是提升效率的关键。GitHub Actions 作为 GitHub 官方提供的自动化工具，可以轻松实现定时任务、事件触发、多环境部署等功能。但在实际应用中，<strong>多项目混合仓库</strong>（monorepo）的自动化常会遇到路径配置、权限管理等问题。本文将以一个 ETF 指数追踪项目为例，详细讲解如何正确配置 GitHub Actions 实现以下功能：</p><p>✅ <strong>定时爬取数据</strong><br>✅ <strong>自动生成可视化图表</strong><br>✅ <strong>跨子目录文件操作</strong><br>✅ <strong>错误通知与提交回传</strong></p><h2 id="项目结构分析"><a href="#项目结构分析" class="headerlink" title="项目结构分析"></a>项目结构分析</h2><p>假设仓库目录结构如下：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">your-repo/</span><br><span class="line">├── .github/</span><br><span class="line">│   └── workflows/</span><br><span class="line">│       └── etf-index-tracker.yml  <span class="comment"># Action 配置文件</span></span><br><span class="line">└── etf-index-tracker/  <span class="comment"># 子项目目录</span></span><br><span class="line">    ├── main.py         <span class="comment"># 数据处理脚本</span></span><br><span class="line">    ├── requirements.txt</span><br><span class="line">    ├── data/           <span class="comment"># 存储 CSV 数据</span></span><br><span class="line">    └── html/           <span class="comment"># 生成的图表文件</span></span><br></pre></td></tr></table></figure><p><strong>核心需求</strong>：<br>当 <code>etf-index-tracker</code> 子目录代码更新时，或每日定时任务触发时：</p><ol><li>运行 Python 脚本生成数据文件（<code>data/*.csv</code>）</li><li>生成可视化 HTML 报告（<code>html/*.html</code>）</li><li>自动将结果提交回仓库</li></ol><h2 id="GitHub-Action-配置详解"><a href="#GitHub-Action-配置详解" class="headerlink" title="GitHub Action 配置详解"></a>GitHub Action 配置详解</h2><h3 id="完整工作流文件"><a href="#完整工作流文件" class="headerlink" title="完整工作流文件"></a>完整工作流文件</h3><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">name:</span> <span class="string">ETF</span> <span class="string">Index</span> <span class="string">Tracker</span></span><br><span class="line"><span class="attr">on:</span></span><br><span class="line">  <span class="attr">push:</span></span><br><span class="line">    <span class="attr">paths:</span></span><br><span class="line">      <span class="bullet">-</span> <span class="string">&#x27;etf-index-tracker/**&#x27;</span>  <span class="comment"># 仅监控子目录变更</span></span><br><span class="line">      <span class="bullet">-</span> <span class="string">&#x27;!html/**&#x27;</span>              <span class="comment"># 忽略生成文件的触发</span></span><br><span class="line">  <span class="attr">schedule:</span></span><br><span class="line">    <span class="bullet">-</span> <span class="attr">cron:</span> <span class="string">&#x27;0 10 * * *&#x27;</span>  <span class="comment"># UTC 10:00（北京时间 18:00）</span></span><br><span class="line">  <span class="attr">workflow_dispatch:</span>       <span class="comment"># 手动触发选项</span></span><br><span class="line"></span><br><span class="line"><span class="attr">jobs:</span></span><br><span class="line">  <span class="attr">run-script:</span></span><br><span class="line">    <span class="attr">runs-on:</span> <span class="string">ubuntu-latest</span></span><br><span class="line">    <span class="attr">permissions:</span></span><br><span class="line">      <span class="attr">contents:</span> <span class="string">write</span>  <span class="comment"># 关键：允许写入仓库</span></span><br><span class="line"></span><br><span class="line">    <span class="attr">steps:</span></span><br><span class="line">      <span class="bullet">-</span> <span class="attr">name:</span> <span class="string">Checkout</span></span><br><span class="line">        <span class="attr">uses:</span> <span class="string">actions/checkout@v4</span></span><br><span class="line">        <span class="attr">with:</span></span><br><span class="line">          <span class="attr">fetch-depth:</span> <span class="number">0</span>  <span class="comment"># 获取完整提交历史</span></span><br><span class="line"></span><br><span class="line">      <span class="bullet">-</span> <span class="attr">name:</span> <span class="string">Setup</span> <span class="string">Python</span></span><br><span class="line">        <span class="attr">uses:</span> <span class="string">actions/setup-python@v5</span></span><br><span class="line">        <span class="attr">with:</span></span><br><span class="line">          <span class="attr">python-version:</span> <span class="string">&#x27;3.12&#x27;</span></span><br><span class="line"></span><br><span class="line">      <span class="bullet">-</span> <span class="attr">name:</span> <span class="string">Install</span> <span class="string">Dependencies</span></span><br><span class="line">        <span class="attr">run:</span> <span class="string">pip</span> <span class="string">install</span> <span class="string">-r</span> <span class="string">requirements.txt</span></span><br><span class="line">        <span class="attr">working-directory:</span> <span class="string">./etf-index-tracker</span>  <span class="comment"># 指定子目录</span></span><br><span class="line"></span><br><span class="line">      <span class="bullet">-</span> <span class="attr">name:</span> <span class="string">Generate</span> <span class="string">Reports</span></span><br><span class="line">        <span class="attr">run:</span> <span class="string">python</span> <span class="string">main.py</span></span><br><span class="line">        <span class="attr">working-directory:</span> <span class="string">./etf-index-tracker</span></span><br><span class="line"></span><br><span class="line">      <span class="bullet">-</span> <span class="attr">name:</span> <span class="string">Commit</span> <span class="string">Results</span></span><br><span class="line">        <span class="attr">env:</span></span><br><span class="line">          <span class="attr">GITHUB_TOKEN:</span> <span class="string">$&#123;&#123;</span> <span class="string">secrets.GITHUB_TOKEN</span> <span class="string">&#125;&#125;</span></span><br><span class="line">        <span class="attr">run:</span> <span class="string">|</span></span><br><span class="line"><span class="string">          cd ./etf-index-tracker  # 进入子项目目录</span></span><br><span class="line"><span class="string"></span></span><br><span class="line">          <span class="comment"># 配置 Git 身份</span></span><br><span class="line">          <span class="string">git</span> <span class="string">config</span> <span class="string">--global</span> <span class="string">user.name</span> <span class="string">&quot;github-actions&quot;</span></span><br><span class="line">          <span class="string">git</span> <span class="string">config</span> <span class="string">--global</span> <span class="string">user.email</span> <span class="string">&quot;41898282+github-actions[bot]@users.noreply.github.com&quot;</span></span><br><span class="line"></span><br><span class="line">          <span class="comment"># 提交变更</span></span><br><span class="line">          <span class="string">git</span> <span class="string">add</span> <span class="string">data/</span> <span class="string">html/</span></span><br><span class="line">          <span class="string">git</span> <span class="string">commit</span> <span class="string">-m</span> <span class="string">&quot;Auto-update: $(date &#x27;+%Y-%m-%d %H:%M&#x27;) [skip ci]&quot;</span> <span class="string">||</span> <span class="string">echo</span> <span class="string">&quot;No changes&quot;</span></span><br><span class="line">          <span class="string">git</span> <span class="string">push</span> <span class="string">origin</span> <span class="string">HEAD:main</span></span><br></pre></td></tr></table></figure><h2 id="关键技术点解析"><a href="#关键技术点解析" class="headerlink" title="关键技术点解析"></a>关键技术点解析</h2><h3 id="路径隔离与工作目录"><a href="#路径隔离与工作目录" class="headerlink" title="路径隔离与工作目录"></a>路径隔离与工作目录</h3><ul><li><p><strong><code>working-directory</code></strong> 参数：<br>指定命令执行的子目录，避免路径混乱。</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="bullet">-</span> <span class="attr">name:</span> <span class="string">Install</span> <span class="string">Dependencies</span></span><br><span class="line">  <span class="attr">run:</span> <span class="string">pip</span> <span class="string">install</span> <span class="string">-r</span> <span class="string">requirements.txt</span></span><br><span class="line">  <span class="attr">working-directory:</span> <span class="string">./etf-index-tracker</span>  <span class="comment"># 作用范围仅限此步骤</span></span><br></pre></td></tr></table></figure></li><li><p><strong>Python 脚本中的路径处理</strong>：<br>使用 <code>os.path.dirname(__file__)</code> 获取脚本所在目录。</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 在 main.py 中定义路径</span></span><br><span class="line"><span class="keyword">import</span> os</span><br><span class="line">BASE_DIR = os.path.dirname(__file__)</span><br><span class="line">HTML_DIR = os.path.join(BASE_DIR, <span class="string">&quot;html&quot;</span>)</span><br></pre></td></tr></table></figure></li></ul><h3 id="防循环触发机制"><a href="#防循环触发机制" class="headerlink" title="防循环触发机制"></a>防循环触发机制</h3><ul><li><p><strong>忽略生成文件的推送</strong>：</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">on:</span></span><br><span class="line">  <span class="attr">push:</span></span><br><span class="line">    <span class="attr">paths:</span></span><br><span class="line">      <span class="bullet">-</span> <span class="string">&#x27;etf-index-tracker/**&#x27;</span></span><br><span class="line">      <span class="bullet">-</span> <span class="string">&#x27;!html/**&#x27;</span>  <span class="comment"># 排除 html 目录的变更</span></span><br></pre></td></tr></table></figure></li><li><p><strong><code>[skip ci]</code> 提交标记</strong>：<br>避免自动提交触发新的工作流。</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git commit -m <span class="string">&quot;Update [skip ci]&quot;</span></span><br></pre></td></tr></table></figure></li></ul><h3 id="权限管理"><a href="#权限管理" class="headerlink" title="权限管理"></a>权限管理</h3><ul><li><p><strong>工作流级别权限</strong>：</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">jobs:</span></span><br><span class="line">  <span class="attr">run-script:</span></span><br><span class="line">    <span class="attr">permissions:</span></span><br><span class="line">      <span class="attr">contents:</span> <span class="string">write</span>  <span class="comment"># 允许写入仓库</span></span><br></pre></td></tr></table></figure></li><li><p><strong>仓库设置</strong>：<br>在仓库的 <code>Settings &gt; Actions &gt; General</code> 中：<br>✅ 勾选 “Read and write permissions”<br>✅ 允许 “Workflow permissions”</p></li></ul><h3 id="Git-设置"><a href="#Git-设置" class="headerlink" title="Git 设置"></a>Git 设置</h3><ul><li><p><strong>Commit and Push 配置不需要修改</strong>：<br>✅ 用户名：github-actions 是 GitHub 官方推荐的机器用户名称<br>✅ 邮箱：41898282+github-actions[bot]… 是 GitHub 自动生成的 no-reply 邮箱（格式固定）<br>✅ 作用：这些配置会使得提交记录显示为 github-actions &lt;41898282+github-actions[bot]@users.noreply.github.com&gt;</p></li><li><p><strong>什么情况下需要修改</strong></p><table><thead><tr><th>场景</th><th>建议配置</th><th>示例</th></tr></thead><tbody><tr><td>希望提交显示为你的账户</td><td>使用你的 GitHub 账户邮箱（需配置为 Secret）</td><td><code>git config --global user.email &quot;$&#123;&#123; secrets.USER_EMAIL &#125;&#125;&quot;</code></td></tr><tr><td>组织项目需要规范提交者</td><td>使用组织专属邮箱</td><td><a href="mailto:&#116;&#101;&#x61;&#x6d;&#x40;&#111;&#114;&#103;&#46;&#x63;&#111;&#109;">&#116;&#101;&#x61;&#x6d;&#x40;&#111;&#114;&#103;&#46;&#x63;&#111;&#109;</a></td></tr><tr><td>需要绕过邮件通知</td><td>保持默认（no-reply 邮箱不会触发通知）</td><td>无需修改</td></tr></tbody></table></li><li><p><strong>推荐保持默认的原因</strong></p><ul><li>权限安全：当前配置使用 GITHUB_TOKEN 已具备提交权限，无需暴露个人邮箱</li><li>防止循环触发：[skip ci] 标记 + 默认邮箱能有效避免工作流重复触发</li><li>官方认证标识：github-actions[bot] 邮箱会在提交记录中显示 GitHub 官方机器人图标</li></ul></li></ul><h2 id="常见问题与解决方案"><a href="#常见问题与解决方案" class="headerlink" title="常见问题与解决方案"></a>常见问题与解决方案</h2><h3 id="错误：fatal-pathspec-html-did-not-match-any-files"><a href="#错误：fatal-pathspec-html-did-not-match-any-files" class="headerlink" title="错误：fatal: pathspec &#39;html/*&#39; did not match any files"></a>错误：<code>fatal: pathspec &#39;html/*&#39; did not match any files</code></h3><ul><li><strong>原因</strong>：文件生成路径错误，或目录为空</li><li><strong>修复</strong>：<figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="bullet">-</span> <span class="attr">name:</span> <span class="string">Commit</span> <span class="string">Results</span></span><br><span class="line">  <span class="attr">run:</span> <span class="string">|</span></span><br><span class="line"><span class="string">    # 强制创建目录（防首次运行失败）</span></span><br><span class="line"><span class="string">    mkdir -p ./etf-index-tracker/html</span></span><br><span class="line"><span class="string"></span></span><br><span class="line">    <span class="comment"># 改用目录路径而非通配符</span></span><br><span class="line">    <span class="string">git</span> <span class="string">add</span> <span class="string">html/</span> <span class="string">data/</span></span><br></pre></td></tr></table></figure></li></ul><h3 id="错误：Permission-denied-to-GitHub-Actions"><a href="#错误：Permission-denied-to-GitHub-Actions" class="headerlink" title="错误：Permission denied to GitHub Actions"></a>错误：<code>Permission denied to GitHub Actions</code></h3><ul><li><strong>原因</strong>：缺少 <code>GITHUB_TOKEN</code> 或权限不足</li><li><strong>修复</strong>：<figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="bullet">-</span> <span class="attr">name:</span> <span class="string">Commit</span> <span class="string">Results</span></span><br><span class="line">  <span class="attr">env:</span></span><br><span class="line">    <span class="attr">GITHUB_TOKEN:</span> <span class="string">$&#123;&#123;</span> <span class="string">secrets.GITHUB_TOKEN</span> <span class="string">&#125;&#125;</span>  <span class="comment"># 必须注入 token</span></span><br></pre></td></tr></table></figure></li></ul><h3 id="调试技巧"><a href="#调试技巧" class="headerlink" title="调试技巧"></a>调试技巧</h3><ul><li><strong>查看生成文件</strong>：<figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="bullet">-</span> <span class="attr">name:</span> <span class="string">Debug</span></span><br><span class="line">  <span class="attr">run:</span> <span class="string">ls</span> <span class="string">-l</span> <span class="string">./etf-index-tracker/html/</span></span><br></pre></td></tr></table></figure></li><li><strong>本地模拟运行</strong>：<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">act -j run-script -s GITHUB_TOKEN=your_token</span><br></pre></td></tr></table></figure></li></ul><hr><h2 id="扩展应用：多项目管理"><a href="#扩展应用：多项目管理" class="headerlink" title="扩展应用：多项目管理"></a>扩展应用：多项目管理</h2><p>若仓库包含多个独立项目（如 <code>project-a/</code>, <code>project-b/</code>），可通过以下方式扩展：</p><h3 id="独立工作流文件"><a href="#独立工作流文件" class="headerlink" title="独立工作流文件"></a>独立工作流文件</h3><p>为每个项目创建单独的 <code>.yml</code> 文件：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">.github/workflows/</span><br><span class="line">├── project-a.yml</span><br><span class="line">└── project-b.yml</span><br></pre></td></tr></table></figure><h3 id="矩阵策略（Matrix）"><a href="#矩阵策略（Matrix）" class="headerlink" title="矩阵策略（Matrix）"></a>矩阵策略（Matrix）</h3><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">jobs:</span></span><br><span class="line">  <span class="attr">run-projects:</span></span><br><span class="line">    <span class="attr">strategy:</span></span><br><span class="line">      <span class="attr">matrix:</span></span><br><span class="line">        <span class="attr">project:</span> [<span class="string">etf-tracker</span>, <span class="string">stock-analyser</span>]</span><br><span class="line">    <span class="attr">steps:</span></span><br><span class="line">      <span class="bullet">-</span> <span class="attr">name:</span> <span class="string">Run</span> <span class="string">Script</span></span><br><span class="line">        <span class="attr">run:</span> <span class="string">python</span> <span class="string">main.py</span></span><br><span class="line">        <span class="attr">working-directory:</span> <span class="string">./$&#123;&#123;</span> <span class="string">matrix.project</span> <span class="string">&#125;&#125;</span></span><br></pre></td></tr></table></figure><h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>通过合理配置 GitHub Actions，您可以实现：<br>🕒 <strong>定时任务</strong>：每日自动数据更新<br>🔀 <strong>事件触发</strong>：代码推送立即执行<br>📁 <strong>路径隔离</strong>：多项目互不干扰<br>🔒 <strong>安全提交</strong>：自动回传结果至仓库</p><p>本文的完整代码示例可在 <a href="https://github.com/FelicxFoster/automagically">GitHub 仓库</a> 获取。如果你在配置过程中遇到其他问题，欢迎在评论区讨论！</p><hr><p><strong>延伸阅读</strong>：</p><ul><li><a href="https://docs.github.com/actions">GitHub Actions 官方文档</a></li><li><a href="https://docs.bokeh.org/en/latest/docs/user_guide/quickstart.html">Bokeh 可视化教程</a></li><li><a href="https://monorepo.tools/">Monorepo 最佳实践</a></li></ul>]]></content>
    
    
    <summary type="html">Github Actions 自动化指南</summary>
    
    
    
    <category term="开发运维" scheme="https://blog.felicx.eu.org/categories/%E5%BC%80%E5%8F%91%E8%BF%90%E7%BB%B4/"/>
    
    
    <category term="git" scheme="https://blog.felicx.eu.org/tags/git/"/>
    
  </entry>
  
  <entry>
    <title>解决 Git 连接 GitHub 超时问题</title>
    <link href="https://blog.felicx.eu.org/1049638684.html"/>
    <id>https://blog.felicx.eu.org/1049638684.html</id>
    <published>2025-02-09T08:11:04.000Z</published>
    <updated>2025-02-09T08:20:04.000Z</updated>
    
    <content type="html"><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>在使用 Git 进行代码拉取或推送时，可能会遇到 <code>Failed to connect to github.com port 443: Timed out</code> 的错误提示。这个问题通常与网络连接或代理设置有关。本文将详细介绍几种常见的解决方法，帮助你快速解决这一问题。</p><h2 id="问题描述"><a href="#问题描述" class="headerlink" title="问题描述"></a>问题描述</h2><p>在 Windows 10 系统中，使用 Git 拉取代码时，突然出现以下错误提示：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Failed to connect to github.com port 443: Timed out</span><br></pre></td></tr></table></figure><p>尽管通过 <code>ping</code> 命令检测 <code>github.com</code> 可以正常连接，且 GitHub 网站也能正常访问，但 Git 操作仍然失败。经过查阅资料，发现该问题可能与代理设置有关。</p><h2 id="解决方法"><a href="#解决方法" class="headerlink" title="解决方法"></a>解决方法</h2><h3 id="方法一：通过-Git-Config-设置代理"><a href="#方法一：通过-Git-Config-设置代理" class="headerlink" title="方法一：通过 Git Config 设置代理"></a>方法一：通过 Git Config 设置代理</h3><ol><li><p><strong>进入网络设置</strong><br>打开 Windows 的设置 -&gt; 网络和 Internet -&gt; 代理，找到 <code>使用设置脚本</code> 选项，将其打开，并复制脚本地址到浏览器中下载。</p></li><li><p><strong>查看代理文件</strong><br>下载的 PAC 代理文件中，开头通常会有一个 <code>proxy</code> 字段，记录下该字段的值。</p></li><li><p><strong>设置 Git 代理</strong><br>使用以下命令为 Git 设置代理：</p><ul><li>全局设置代理：<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git config --global http.proxy xxxxx</span><br></pre></td></tr></table></figure></li><li>为某个特定项目设置代理：<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git config --<span class="built_in">local</span> http.proxy xxxxx</span><br></pre></td></tr></table></figure>设置完成后，再次尝试拉取代码。</li></ul></li></ol><h3 id="方法二：修改-Hosts-文件（推荐）"><a href="#方法二：修改-Hosts-文件（推荐）" class="headerlink" title="方法二：修改 Hosts 文件（推荐）"></a>方法二：修改 Hosts 文件（推荐）</h3><ol><li><p><strong>查询 GitHub 的 IP 地址</strong><br>访问 <a href="https://www.ipaddress.com/">IPAddress.com</a>，输入 <code>github.com</code> 查询其对应的 IP 地址。</p></li><li><p><strong>修改 Hosts 文件</strong><br>打开 <code>C:\Windows\System32\drivers\etc\hosts</code> 文件，添加以下内容：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">140.82.112.4 github.com</span><br></pre></td></tr></table></figure></li><li><p><strong>刷新 DNS 缓存</strong><br>在命令提示符中执行以下命令，刷新 DNS 缓存：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">ipconfig /flushdns</span><br></pre></td></tr></table></figure></li></ol><h3 id="方法三：设置-Git-代理端口（推荐）"><a href="#方法三：设置-Git-代理端口（推荐）" class="headerlink" title="方法三：设置 Git 代理端口（推荐）"></a>方法三：设置 Git 代理端口（推荐）</h3><ol><li><p><strong>查看网络代理端口</strong><br>打开网络代理设置页面（Windows 的设置 -&gt; 网络和 Internet -&gt; 代理 -&gt; 手动设置代理），查看当前使用的代理端口号。</p></li><li><p><strong>设置 Git 代理端口</strong><br>使用以下命令设置 Git 的代理端口，确保与网络代理端口一致：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">git config --global http.proxy 127.0.0.1:7890</span><br><span class="line">git config --global https.proxy 127.0.0.1:7890</span><br><span class="line">ipconfig /flushdns</span><br></pre></td></tr></table></figure></li></ol><h3 id="方法四：清除-Git-代理设置"><a href="#方法四：清除-Git-代理设置" class="headerlink" title="方法四：清除 Git 代理设置"></a>方法四：清除 Git 代理设置</h3><p>如果你关闭了代理工具（如梯子），但在拉取代码时仍然报错：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Failed to connect to 127.0.0.1 port 7890 after 2034 ms: Couldn<span class="string">&#x27;t connect to server</span></span><br></pre></td></tr></table></figure><ol><li><p><strong>检查代理设置</strong><br>使用以下命令查看当前 Git 的代理设置：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git config --global http.proxy</span><br></pre></td></tr></table></figure></li><li><p><strong>清除代理设置</strong><br>如果存在代理设置，使用以下命令清除：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git config --global --<span class="built_in">unset</span> http.proxy</span><br></pre></td></tr></table></figure></li></ol><h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p><code>Failed to connect to github.com port 443: Timed out</code> 错误通常与网络代理设置有关。通过以上几种方法，你可以根据实际情况选择合适的解决方案。推荐优先尝试修改 Hosts 文件或设置 Git 代理端口，这两种方法在大多数情况下都能有效解决问题。</p>]]></content>
    
    
    <summary type="html">Failed to connect to github.com port 443 Timed out</summary>
    
    
    
    <category term="开发运维" scheme="https://blog.felicx.eu.org/categories/%E5%BC%80%E5%8F%91%E8%BF%90%E7%BB%B4/"/>
    
    
    <category term="git" scheme="https://blog.felicx.eu.org/tags/git/"/>
    
  </entry>
  
  <entry>
    <title>2025年度OKR</title>
    <link href="https://blog.felicx.eu.org/1985364334.html"/>
    <id>https://blog.felicx.eu.org/1985364334.html</id>
    <published>2025-01-04T01:00:00.000Z</published>
    <updated>2025-01-04T01:50:00.000Z</updated>
    
    <content type="html"><![CDATA[<div class="hbe hbe-container" id="hexo-blog-encrypt" data-wpm="Oh, this is an invalid password. Check and try again, please." data-whm="OOPS, these decrypted content may changed, but you can still have a look.">  <script id="hbeData" type="hbeData" data-hmacdigest="73bfca46d159ba0922e94ee3aa53be503efdbd530feab8780ee9eb09fb5634f0">c8f9345acf8a8db129dc4f70a76800da18d542646271b3d2d46081d8c283c54eb6c12ea6cb7c8cd49becfe8ef4c789366af60f6703fe84ef6629e4efaacb2261d2eeb17465754c54fd3a232bb2f9e820735f23f6573a3592ec6b35f1bf9caf4144c52e861c55c72ba05e381ba1586dccdabe4ec155efa9f95a07ba06482dea20050ae396cc497fa93a957425c5ea53ee69c861fa0c597d859158625e882fc54338adf275fafc1e3d8a39de05ad1d88f5e92c0d485d36b5df7cb486d30ab6132f72b1e1664da11ae5cd2954a9a9ce9c242f2beb4d868ccbe6087d5eb454199adb11245a55a73b956259fbbc14844d0a56b0e150f13a022a1b19cfd0f112f8e35fffcbbf05b3d2aeb47d2fa0156cc51cdfe7f6358aa82b7e5daa7169819749ce83c6370feb196edec9dc90eb38c28e9efa9c36482b9abc42e4f716d906e779bbc9bdaafc2f4913245bb2dce0412bfdb91da7fef44c6606ee81a23207c672bc928757db6ecb3bdcda0d8337eca363ffd332ddb2dd314f3a89a6b5bd7808dbb14c1e869558e7e847b8414a0991ebea919808c35c20fdf4c62dd69727386c69b0beed79a53cf2c6cb658e2593fc8216659546a8804e0360f7acaac68600c648ad5bf9370d5fb83918b6fe2d2e437ce07719eba27d09199c624097d327b2b8c133d846d810e1326bca04232f08b36c86237ef07988bc796859e0da7c04f0f9138d9c180cdea56acd81fcb2f3469c970f9226321eb4243bcc10a71064b34cc6ba01b0f393fb5115d71a8913faf7172ef9f4243356fa429418602fb05ddb335a34156b2f7950999408db72950431c3f1666fd2916abb0617c9b202c18e9c8d525386cc611a9cbb56faeb8afc575d88676aa835aad40d069887c18b9720e5b17396087349fbf84d534cb1dad456567f30e0f34e2b1ec50e3faf654e3121aac18c36d785fb5993cab3a1cdc4ef98845f8b25adcbaf4d270ab58ed0c1d7d238ac488b13d5416c06929344167fc142fd9873bf8d4ec057944989ae0fd392a5323083207ffe90c33e1ed9c111c9dd6a74dc4469908d22727d180393b5ffae4d581e798186a096dac84333f82e603a15b0e272f46ce0f47ac6decd34dd75af0fc6b5d7d433dc9f5f4325d037613104e406ccde7198bab9b5d301a4e83142769827aa3c9db19abfb676cf0596e50563ac7399507c5e52383a766c51be9706737182c0d197bf2e543a86063d51a4d8fbdb889808f5d82f11fa97c0bec92bdd864e319c91130d878d6ca443e43c444ed1cce3c30cf16f97771e9b4fa93f07c75a5cf370394a6a019b168487f3d75c765b05e2f9c9b92cc727d00eae2583aed34f7a92c066f8f62162bf8fc8897f6aab004f44a878de287fa1caea7e943ce968aa4a5fb94f02221c3d48e73c98bb79d0c7c3a77510459d7c506683c10aa1c0ef9b75f9bfe5e1edbc6caf34ec8f04e7f8ac3165d0f29fdd2277dab13ee97e656ed7764c1f380f3898ee86790726b20a29be4426c48a86149a8b51f0af4069835367ee4bd00612f2ab964780d0221bbe52fd14acab28c5d781f6b2f87d8af2ddf951a7cb9d026e47dbc3f2361fd9abe2e29000e784de59afb69e9722b7cc124acc160f6bc74f1d4e58cf9fae6bbe22d05cd75e1b240443139d892464145e29ab46ba26f0aec12ea00fd2a9a872a24087c4ef406d746d50679ec08c8a02f9ae3e89c311c51f7f7db8eb979fc7957d4b5c119add45be53ba8e00549477648bb1a26f7e03765503b5fddd3fe1ec9a9eb5cfbe62bf920f932390dd305dcd1e0a91008891d0fe1ff9f133b1eac0279eece580ab2df6ca1117acbd81410ac23bf6e8f30ed043058eb8fc371de2cce994f2d575016a4720903428167c2dde7ab7f78b0f0dd1f0ac7e2b301ca8d92e6a4d1a172e32ac1074a1fa97bf92c13660207ccc40c0c234cbda33cc308a8daecc002e34b0559580be9415e3249a8414f230d1a479f25d087219fa3e5de8472b3f3572b956194c703c14c4621e56f74dc71e2814d57693ca240b79cf7d93e40e6f0b911df498b8ac0cbdadd9f50c984d83ee1b47441253358c6baaddcd905f22847c2832246cffa0cfa32ba5b49443dfd151653fb3f4191cb66fdcc36fb0c94325b3fdadbbdd351fa72fdd79ea631180f7f6f845ce2aa8169011738b1e121cd7ba55f66ce07b8a23a0c1d4ad0d2791d5408881b7d5f5689f0086b73f56e352f31e57791e816c65d508e6ecadb5690f38a9820f3b905aaea3bd1d2c1235055c97801cbf3eed858030c5e368c9726d5f58738df5e71227e9f50e8c84917613342f2c728c69215e6e3bb9d5599522f7c06b24115a6bd98b3eeff931709794809ead1a6560a4f8e4956d76e0cf8937eb8b57872fa6546f3f7d0857acae6b3ab23c11dc117ae483c84535d966ba706fa2428997e5692641e98343830f933b91c4ffde47bdf2bf2770f7ab6a4110a0f2d965afc20deb0ffc660df4ff022c3044175711c686289068b54c36fd7d44f393cc89a7a9db67905c753f645cb797b1cd430d49a46a5c902b94e63c87b0b9003e0d04ecc702f17786401a85fc42ffb8f75df365280f71558d541bdc48a5fdab68c68c9f2df62d7bf62d37dbf78a44cf668ab6960173229d8ebf74d9b461c6529809166180873461d4124e764205134dc87aca7e09447c85356e2499c0c73ce385461035fed2837c6078847cec0172813a9d13bff90c696f767c9cdb3f4e0443830e7bce5245745e7a38e9d7b476e8e1e1c8fe058053f17c86becb7a2a792bf8dc7d7dabf43686049d7f2cba257f176cdf540bf19ecaeb1ede187692a6ce18efa896424736095d02b13e41cdab2f0e8ce0c4143c1c8e85cfb1132ad1c18f207a34874eb7dcd57f3ac40407b2af33af3e6d7523448936ae5eb5e5dd02e56b41f905bf4e81f770172fdce9a5a5d19999e14746bf052ff2c2335251410d173fe1c091e002cdf0a773217fe90b133870987480e1c419d8708fa44a4a82e301d09e2a57c978d27e8221483065aaeb87eb040ae779c669335b7c6dea2b43fa4f06348bdca35ba8bf08894fce806e61848bb2001a69374136aed2904b0f657546c9fe7d221b6c75c0e523bc411a98d11993d4f72805846d6ccdbd1ba6e0a8e31cb052fd2a21d0c90bdbdb1b833c897bfce26112203498a737f4f0d950eefeaa7c47fdbddbe0cf2d63abfe8fab35d2c9781900706543131c9a268346315292f5f36a1f8e24d915264512138f7952ac9d763ba514da28940e2d07609036e8a14eeec7b64ae149784e5b1f3481df88234625a74486f87c9c091a747b8f5e9b89e68fece4f3df9b55e2938adfa70d8d3b9791c4b6126749db9eb27cd87456f2da55417a2174684e4f9c6f0755dd75eb8acc402f36eb7017c5a3da1bc67230981e29e4df308c32020f131b068a0bc72a7cb45f571d54d7bd63247529bea076ba0873973b5d191bdc8a396e8976d00fa596c071f639ae6ec2a20a8978b8839bc4cf3d57501f881f8c533af911dd8dc96ae187295936d507d03643f2740de0cf3cafb2c2f10c788627e742c2f9856659f8b50b8aa8ae248305645a5e68ad042b20a7b704322688e99b86d17c3f887511749fb5bb5d55924cdd85e46b72845e5d9d25fd8fb2c0458583c9c9c0238358ab444f76537b60368cf846b5f1d7398acc6251292ab0bd73ff338bb586318be4f98d80699212a9ed8852a882f1678888918b23ef5a947ab8ae8a935510ede1d68c82e0b330c98f4192ff8d14a4e6b409692f1904d38cd83a2a6c8d2b46f3bc7c852d88258302e94c5cb7f9b2d39ba2511e48e7ece2e2171a782d6705b9495723b5e5017b24bda4097e795d807189a721352a10c25fe1c3b74a5986bdd824e06c8237ec351f4d82ad4ec87618866c73a3604ccf22ce124ae6e7a44df2f44bc528a548599e12ae818ff41ba9fb74230979393fe8a810edffb788381d21912f70e66432f26f86e12579fd90838a6f44ada8e8e3865babbf7797aaa6253d39de88dc61934ac918bc4126827f79b06bddfbde3d69e32d4436e59ca2ff87c357953dd41e66535f8eb7728597161a369e52b68e6505bd5f4ac4d6fe35095dfff566439c4ec41bc445d05757e033d70894acb2558a8ebdc24054b5fda34a405e74de133f171c7ab52e8553fc3395190eacc19d4cabc8f2bac5d2a25465d0a907a96254ee99ed14f3448865f9581eec1bb3b45ad91b0422838c04026ee1bfc4d0523e9241a76739bb439b303a36384672538228e070e0f043992bcfaa19e5deb105697da0baa2757ea66f092e0cdc1508d5c141c31981592e6ac97f7f63c3e67d5cddff66aeaded4ea781ca57738b2ca303328c30ac6908c73e20add7733473d8269409cf9f94b6dee2d796100068f848e553f1af2e5aea2f137223b4ea77f1e61ead2d0c6c0c36b6b0ae2da236592e368b6b808c7c05f0f6c45b61b957499409634fceef2c108475c339c2e478336e0e861da49491b0123936f68f3903a0dac4ee8fc983b98d0e3e3739aa827c0ef063e09dd6d1f5c6bd2717c1b2e12e5d9f351ddd53b4b6f4f9494c2ab56276279cbf45d6e1748f30f57d9a9cb3ba3db844c278ab129f5c4cc57b667df870d8bcb8e4204f451835730e13566e999e38d38f58b12e66469e0b4e959a761d65ae43214d935659dd95e934769e97dfe330dbfb11ab1449976e93f85f28cebc92de38b35f53af03f289e0cf13088ba4a40a3954c008b417a3227f256bb9c898def5da46bf1cfe8d612eb2703cb7a06b730f9e17594aa7f9c3bf28fecb47f17d32071d54481f8d2739ee2fc400f96ff25d071fd3b8d75a1a5858e835bc715fb3911534b4ba850d916195f87b1719d3d5c56001ecda4af7c2574e94cf76efe41f0eb6ebb44f3fd65db099a41c277401eb9f46d6a0359fa3824ecc5ce2a7a5ba359422d8c998791bb912f1d4e9ea64a2f5d5476f66c7ace0f3983a36afc99d387b9919e6cc52685750f336da364f02e5f3c421012b049f686a4d992a67afd6af7b8e5701d84d9df78268801594fe4bd2d48714ead099644200edc1beff363971fd4942bb574be8b7f1d5d013b2642ccfadbb3389cd899461b6529d9c1eb54460c60ce9904dd635bb862b4351f122250f78c34f661dc0f319c0b2ec4c25fc74c3a6c3f8163db8639ed8d519bebb0f645c7461eb16e2f8affc2b24beba7d0c587fe164335cafb4e967bba78d23238c3db5bad26312caa3a649bd11b5b49c129105f5ec21e49d42cd100a4c673060c30326b41220606da3a7fac30f85a48bbb44faf79735bfb20a4ecc7ce6f2b9639b3469c9f670014db85dd2c6a0d5e8613c705097c6e96b1d5267c131de43aee46517fb4137c9a24c93ee39ea4349262307bbe527b6a3d50d00799d056a45ec9eb2af108fa28e8b6b600f4983be628e6e947da7f64dcf3b8f6cbb6f2547a5f523fa5610ddaf643f1af6c258fd765a3c347e88ebd5fef00cb1a15ddacf5114ed1592dfa79bc6d56f2dea52651c8dab76784b351bfa17441c69af7b194537af02b61bcae0eedd599e6710dd0ad7c03d6145f902d961619823642701879f1b73d0c66465522aaf23ce4b6bc7f494f108855dd8997bc51b888cfa203b4e98bd67171769982921f6b8f0725d8457483d8538c5418277c8f119f0ef1c89060a57748a4160849ecc21fda4e83d4410f39c672858bccdc2348ac15abd5e035d4c830a9c03edac5d0e5382526bd7a9ffcb89a88a9c211e21388e5d20eb069f6100f4141d4947313cd80aa54805e825e2494b43f5f24497166b8f96d36b64238838d877937ed0ea58c435ab956fd5e72c0b3e34bed650bb174e3168e8bb8d458241b0906f6f4aa0f4e29d6d7aee8c10ee5a38ade3e582dca532d060917cfe91928e574f7bf8287d763a83e025c1253c2186837d236fbd0297a2637016546a38217966b66c305d2fe85098e02e7c0e0a22306368e4402e73da75d868c27f43cac533ebb821d95a91630e27ae925c7913b24561dd5a824e774e650d01f3539a4cf24daf26e075a49fcb3e0635fd351d5e90f832755ac270ca55ed2742e8b028d98a303268124539a82a42fc1c747bbdde15e41d23651be7da2b2da0cb85b3f065b20e19fc347b99fffbc27c81b4e53677c7b7bf78bff0a0aa4b61545cfc43ba544c6bb1f23effde68abd6571efedae5340c4558afd1c794c77e8616134bfbf0b2d12554be3bf5bb4943ad849b7339c1edbde80fb04329d540a056ed6261b7768c861791a2d372d576998e84d0936116f884697f77577b958c6b35755e8e8b9da8f192699ff8950844063f347146868851905bc6d37a8567c8f796b88f1807df3a6bd2fa142d81931a3f89a4b7ae67e7e7d83f94cb7ab5789a4c62355c0124d444f9d39ca060ba54a4dd66ec928b11236e869d85f1c0f7d2658bad401cb3baf9339e455ef04181a4a34080bdf0cb29a013fca96e26d55594b27d420bfebf6db1f8485952d20cfceafdf10f89700674f78fae82b04415fdb60a14f0460c38b95234194f8010dcc776efe933e2f1a7c8217f26b3691af350b4fe9ec406f7f35a606bad9e05f74e4c934f92f47ecc5fc5953994bd6a4b0f8d706f40dbbcbaf3825833ff290d72ba4d7e21e6379b86f4bd2fb50603ae194dabf658faf3a716ba4cfb8b9386c12eb7fc31cb724bee3444efc3f52dd00fd1f718f5a669f72ba3e1af60e43aa0bfe750551b01396a5d5b2621d26549e6a2928351f85163e86a13e27992b526567c0b77ebb90621bbf6de2925909d7e7e58b86d8fa4e2f5b899bf3f4796bdbef72987938994cec3e898f49015ff7695789893ca1fb440f13bc84bc77b3b3a271d1c1d6cb24c918eaed9638445b8ffc9c3a137e20a0b40080ecd8fd948c5872f7cb70ff0ad7bf25d3ec37afef664f1498cef96059b72eed8e687a833801a0c3b10e87e9b133365497c53cef50382c1c08b005c0a89b2acf1e07eceb506e6d88a520909ffd4081cf4ae148614dada7f700a000d8d456016bfafbb3b863a940cffa836d8949851522004e9142233fffccb2f734bf369466b4d754e195da273535a9d48180d6b5e294d5a727f1eee590efd7a2ddc409109b6e69065da2014f7d8f239c7da4dadf4bc72612db7ed97555fa96974f86d79410f64990823e7d0de2648ff3de3c1a7fb629f3db31ee82841cdbc5d479d9a691047ad94daca9a9a68ddcc5b0bf9d8dbb2fa85f5c2d1cd7eaabf48c1e37fb79eda489ac07ad89891a9b107d5ff27e236202116a283b633a3cb4e96a6653603e019c51ea81c6ae937b1dc663be1823740aa3aaaed4b2e01b24062a01418a734b45d27bb606c3aa98ea6b5bceca8d2d1cbfb02cee8b51eda1db90b8706a3e3c1e90812a3b23e5d2ee3afe954be4be33255a30636ec27e5065c06fa5d589ed4c1bda63d879b1d9c2c9ce9eddc3cd3c106d437f2d8d37116866df99efa058b8f6e7e9df31f20756cf547bca5a338ae5cd838c6773e43ab2165800a0da94fe4b5a603d9ad134a703c92c1f509622b88681a886e1d2e93862ca108b73336e157e41a3fe201e41853dc7f9a4cc1a0be0ed84584e1c3db6127512707a84565a98384b36b171e98ab7f67619dd89d2ec84af08938afa3432b33bbd2ef4bc6e7a3cc708996a32a5f65450e2fed9557fc18f8ebf42a65bca456b1474dd2ac0a82a618ce80ef85d73306bf733999c0fb378bf6fa3c21a067ed07d53c426e710aa5e9e7172155f6be00c6d10d92d243376182f1f7c3cce12e686e58d70fa4d980fbd7e2d053c13b67fc77476ea0ecd5f0c1f711eda16315f2f7112ea74a60c5d792ea959c1aeeb39826e311bc9ad4e1dc215d875910b32517afa8361028f603c86643f7f920c4d75300d5e494a1484a2199811607aa2d2659105855da4e54902343ba19743fd73d364d36730aed69729a1277315ba67f4f9d3ac1ad484abbddda8e84c4dda92e8db5c5f072d3e6b6a40299ff3694c701476ddb5bfacd51391a0f5bf4bf9bc26519200da68f2d8bbc2f82bb02fbc338157faba2689f0fec513ece915c66ec16cfe56ad8defa67ea9710c1749b949351b03d8cc9ef3e2ca7f52825616697dc911edbbdeafbf71e2f61f80d61b7eb2f94e972b4814be23b788ddcbf1617bb4f78857391afa0c2f1f19c4a951ae92c44f256aabf95768bea787e1979c7c75d0e9d26c0402aab86a99c1ea66f427b0dda5b7475c856cf6874897736cf25c92b0769443a3907f58c17159a59f3e8b24b10aa1d1d84afd5f5c81e6285815dce044e997c570b375e753dfdc59ed29a110babb98eb391b4a5790ce19aa631bfe23d0fd391d43410eea82cc67819be08914a477868623004fa0b28ddf4ecd9e9c6e1da2922e3474cb0e4388ef836c3557d640f28cb8e531a2b58614ef9b6ad45054571f82c2a866a714545ac4d0aed6882d31691a357c8f0d12b101117e8f66d7230b9baa9ff5ef722777343d44bfd64d3d08bb5bfdd4f6d494ff5967901831c29021263b84c00077fd95c68c27e23c6f9ab050421754bcbc429bf72cb2b56f8ad17e787c7980afac1c10da328014533b304f5b77dee1829809a5e446c6d2c502b52de72dc39406248618b72a2021d5363f5f1f2fedc669a082eb44124f1041d08f879aba6ce9964ad6b917ca1260369ca6710a3c8f2c92466c0033daeac1b5de13e6e89b2719477ec32eda892e3bfe831d5c4d017e1c87ed85359d396f8348c9c648b47cc6e022ea9d137ed232d98c0f380eca0e290d409571537d63467fac2f5658443d7a13973eec6aeb918b801379970866094153131dadc03d1375b7597136ade9aeaf7f56dc4c4d2a0cff8392040a7f87ad36aaac7c1c9c2a4359fd9036e0c7155b5d338af22fb5f161bbf15f762fa60243328137aa3ff188886158bf30484c829870c16643a47c0594dcba2ee2ee3fb59a6b6d100f21e28ad0537c22c58e9082a5f7631f8e19e38374eeb9bc3e5a1ab9ac6dce53c6dd19c64698d4a842934f6f1ef4b534a4f65196f52362bdfeaa9313e6e5c80c50b6573f28dc5fef0db6da17d8ff1621c3dc3029e5e316c574784e58e496a3240ae5c1af258eaf05812ecd3aefe207bbbd2d838d82e5858dbaac040ea38c4976b440ac049b006ad4327bee6ec0d7f0d601cb38e8dbd78a3c11a2674eb036e0ef29a750d9f2b67238ce823afc4cdea55fe6a69e5c0fe2514dd8efc60724d095e78f79c023a9fbd90aebb722e91f8587d22b84ddb87ccd6854dc60ccf139d4240bc78ce4e9954e99868effec66b9888db9c467e1fbc54bb94671576ba5ff0f58a4ef112ff7697a79ba43c581bf66e9258f17859561ad0b51dc4d155e43e395a027f1f3141cb143e441074ba1351535bf467ef685c41ebc0f95cb3ddf31c7e8dcca29aac0f4fbfa68440205c272922df829a3994a9a5449d37aa698ac708785249f9101e194f762d8bae213c7f36dec3cd84cf29a0d663f76eb3dbbb5a3462dfef8ef006de88c46f16b54f0ba487c7a1d33ffe39ffa389e0032cfbc173b12f6711a1e10d7b263757129da82c49274789735a10ce6a74847e27429fb1d40f85bb86b52ddc744f82d707a38740ff197d7bb7d5ee4f69a276ed03f2aab1c098b8a3d26ff170796cd70a9a82548a199330cbac23a460b563cfae149fe9b0290354604385701e06f4caa2146746087617890ffda0a066950190a0de703aec808da062444f0cd8cb63285007b6e12b5c1d67e7a899b79dbe27b51ba97df9769f77d9b0ae7040c787c9af1505cf9fdc3f04a1a6b12c0293e47e268ecb5078e522ce7f4488497a5bf8f267a7a1ee164611d6803738337083413cc3665ba334a3750741856853cb2bf81eddc4f531ea588772fe92683c31580ce6330277b8e5191880a6f8111823fd72a8eb6eb0752bf7d68b440c5dc49327bf170c9d570c585957169ec1256e893d35405cee1415bd0d018ec510cde248cef9e32a994dfd2d41285ee25622afef29509af08f286efc78e07b1eec56f29985a857e8ad92135113b6d4dff89a19e91fbc055e37e861f8f94063ece7affa1d82550a8e791ab01dd806f0af87e2f8c34171c87903ec90e314f71a9d98562b529785843eed9ce237c652280742b06e0e9753b478230a3dd729ecf8ec596e8503cce4f689ef45cfcab7b9a9c49e219e65ac408ee92c8052159857d02b733d35ff5a094075e9cf82b74045354b367841231dde62740e8cf8a166b5c9964e88f1cca19b53f241a48898183beff12fb67b545afacd30289f57d41cc8c1d7c1f54851b7f5329ab6eb99f7ddc409ca9f5f84f8fddaf7d596e943369ff1b1898acfa7fc2b9a22ec9927aa3bb3efe0f23b48bd0198d4c5a1b5882efbcb030caa76250bd34385f1109ba22ea867338fe8775aeb233c7b9045e78da2e08e6716b374083b8602cc59172fe543c8627e43934adeb4c339456756e3db5346fcc50cb8f1fb20243cf16f1e9e905edb672d745dd1d33985aa76806cd9be3459c034f3b9a585eb684c6faba8be2df5c6fb71949854ed2002f320f05d913ee33e994062ea0ec630c8e0d12d67be1a1de469e027bb6d6f637ab860db58cc370137eae806d08e2d919dce9804be474c6596aba1223d710369e3e633d045ccc7903e7a487fe012783239f41397d7250c3ab8f059c578a113867b718ab65e79eed73eb6b716a0ae22a152a1399607932fa3c7e71bd2305dcda376ad4cc02acd6db1dd169dd3dae5d0a5c5adff5af8b1741918fc29dfcc6d54c2dc32caacf42a954ebe54c359fa6ab5a12b74b6fba9617709b2a47b47fee6d9c1c42db66e3066143124e09bc723514f3572d3fcb0c54c2eeaa40ff68213ee8700299b8d40576c7cac3ded27e8017761b705179f130180fbaeddacaef005d5e1cc5dff5c3bb8dc89a81887f2939ef8c6a45ae2c4d839dd4980aa7fbea6b750a366b301b495420d2d4014cfe75c5ae705a6591fc48371daf618f410e721629421eaf451b5d97f90a9455e9cf3bd351b9580e7ff3987c4d2211da622e58f48e30fc8e8dbe4ceb0009632e49e5f98a5bd5b1dd33cf8433f2b766c7e0796fdb7926a07952a7f0e95f044230552d1a00f025270f6fef6f801fab452284a50a1d61d338f8ca6e2953913c716fcfaecdc6a961ea751e3a8d1ab4dd64664eddd223c1cc9ec18f1df43d5b3ad6ac5a8cb53bb15e195f164af42d19329177e187d7303a0bba8637a4b3e9f06c89283878ee147e489d90118f0f1494f99262eae20832b2f872edb709fa0691f11bd44ca1a0e83c4cedb7c462b25cb6878b51d6298f9f572892775d7fd5759a85e9729274f2308f36b02c886d8e38b3363cd00f896d9dabb8b574b80b082426dda9e4c1e311f8109013b5d6b8c35c93260bd36ff30546cb95ab904880115d771ed6084c4bebe9cf115960d92a2cdeec4ef1ad593ff75915029bf5dd02bd5cb9bc01d0e393c3dc1091edf328dcafa8fdf4cdb502c11a14190a11314ba6672dbb17b32e1d076be839cbb8b9dda386ca9827e54360befc635a2794c543e064ba7e5c8f64ef7cbb2ddc79e0bcbe6b7f497aa32cc4b2d7cbcd6993eca230f127cf3312ab3939875f4bfab7660427e20ec8a592fe02a53b5dae4a0a55713ec80c54eaabcac708cf0185b945f973d07be58a82873012a9ed7db7884ab1cf22bf4a621373d0eb564e8d3db9aad04346fb5edc889833d77e72a04f126046ea2b611561c75e54caf4afba0a6d7b0693850931de307474077c7682100743642602577ca036793c7a7982a51a4a039d9e39a680df00756540694b1ec376272ff1f419ce8ae606a9a0003d1cb88f7cd0cda1d952e6100b0878dc9313b7906f13cbe2834859060f65a2f78cbf9ee83f6db4d737c71a016c9d87c93571dee7b3385b0a150476d645a06e8831643734ba15d5bd1911e2b3674d9c6fdf876d748a846466d192346a16175a75bbe85df23114c9a683a4a3ed0fda432e09aae4e971c46e3054a4686d7f70025e98d92863509ccea54f7da1fde618f764daaa8e33837799e499bee8ba9fb2d0a1af1a104a93706a72c4b971bc38335e5a061c2deeab6a62fd08e66d945a9c59d7e6e3a0e592e63cdb47db1b3c9bc6700aa43757e5d0d8bc9166a92b7e7c7a1dafd21321abe7d5a3d390526d3950610d52ee8084c66032d2978854d4b2679d4b0adadcfeb17dae613c50cb0b4ba689b3220c86a0881ca2590501b62a600db5280de0c827e5a53adcf61319024a48d41f906ac4fcd1cba63c458bfda9f21d67e1d234b7662753bba09ec5c93b55c6433fc4f0491f2ed242ef11739dc539cac77d8cd17cfb26bfd403fcff497f7f9299d75325458e5842b8aedf36f34be0d5e2dde8a25b3648ceec419889f90e50734f0b49cdffd3d3f6be000249a8405f848c957800242b8108eb8b046ae56d6cc6cbc5cf4500aadcb4b8605ac788d5340f6c18141346d5356148aab385d57e517c08968c49c34d8cc532a37d43e5bfd9d929f3bac5530a6afc7d0db057f3f36b605381925a6f8f7fd9c1bf71ff5b5f642cc4a503d6a26ae5e35ad51e1b60261f94ce036c1c6aced5c0af0b5650f401ed2c799226d99dbc8a7ea20862d9e84ef03c143e10ca0af0d04cff6b8b545ddffff36573a220cb7f686b6426839793986def1fc57feb14d4aad81b499a8e3236b895238425de7067915a86ab87b34f62a7558e0861e313662b5a8e65f4a05323ea43de57008be8a32462a565de113d22da1b4a9a267f54fb0b4cac9cc4708b036da22aa8f8977b6b45b6e36e7e5ba9c05e29d258db2c86dc37e054b553c630395b49845b7c44da4aae47fd5022810002a603563c6c7351dee2c1345248d49e6691744a01a6207ba1b11b442bce624504034b2079e983761938fd29fba63a730984e633a473b76f97ff94696a9df0166d3f766b42d6ac6376448a003e0d92122fa58518580eca8f79d1bf5bd2341e7ddb2f01f23fe81a270c42788b9fd1eaa60dedbc1247ea71dd6cfaa86749297babaccbb31fcfabbba5442c0532535e9c6a04871e843194b583446cecd17200d007dd932393f9826cd22ce16509a2c84a567e6139b9f23f43aa66ebeec4b25f497406b7d8b92a230c20311ea549386c18e5d316a21e0e6a0f7c763e4501e8e31ffd13bdd0e8430ac5a92783640aa868b21c1811749a6503a566ba3c36be1d9e0788edee9e8c5b1c1d2ee8215f59390d45f7e9f375a039d535cefc4f97fb0724d6b8163313dbefa70c35cee342f3cde7f15d154efe9166741c66de7f7f1844c0083d5aff7f703e06ca16edc55b5bd0dc0229bf82a9a563a241d8a1f576c52de1cec1b3562e9a513e11282a77b28a691625054ccee129e0098f5bed44f071f6e0e08baa084f2290b48c72053af632072c4bc4dae9d802abb7fcafe3e141dd00fb85fd45ab180bf561c99e35a52f3ea91a105792fa57effd31083a6caecd13a01c4d7696c37dbd39a6b9e744b0b4820447442328d3b5f4b51745b0d00a74d1eab8ae3397dc41acdc6541f1a70d07dd3b819095420064db58fd761ea3e31e562a0542c55e713d30b5c8985cb8c9b6d0c9c4e2e58127ec03204379fbf629f79e21b8538e5d976274b8ccf022f876be5b331be3d44f58f2c589797d809a95a902dad2ca302d4bf11135e5ee2939e87bff1beae575942be4ea6516b509ab4779d72814bbee62bfeafd439b78ff043c68943dd939e9d3d406ada3d16b1e8c53536b8eab01502e2500aeaf078cf9b49f9508db16107d50e55dfd9ce819cada42cdea44fad1f4c8736ad8459d95294fdda07a6c39599a9258957f0371e613364d4df75a635afc5c56607b113cd956e25f92e162c73edc0b6fea4a7c03079792ace49a091a24650598c0fba056ce2be4f750fe3184a84e1ce6340f27830eaf58feefe3f79b6f541ffb36a0e4d6b734c93baf207ea7434221770f0cb8520e5093df4a929fad31e84c3243a0a919c5a9c480500eac72a0426ab81dc13a2efe29cce94b0cdf7ae301bd4530acf4b4deec23fb1d9d5d3d1e133f0c4ce061a36df33634cf74be92c18874c51d886d491471065f256f0a9fdbe9d79754b5714025eaa47cf51dc0255a643bbdbacc372b17fdb6319611f9281912a6c2c2faa9ab9867072963b104e808554712132f1b776621af3b6ba4461ff4a70d4bb238eb0dfcaa521ffc3b7c7795efededf08137b27dc498680dab34bfccf32c1cec0df3159073a4e0e14f71ec7e9f984d5a80bf6f453c237c1a78c5474b81bafe8f4a8c3e55866e52877b110b0a9599a10a31c19eeb68b3781fe04aebeabb804095546314955d4ddd08bdff51b79fd5f8ce0bd6630bbb11a78c9a01420aa9ae31a06055df9fd760819bf1df106204baeb13d3e72a6b944703ebd4c1fb3a5e7742dda83c2bb065982ed0d8829470bb64ce9d7399ea84ffaf44de37c989e61e888c3c0e002c240e3029b57b4eed5ae4b16792d467aef65fd837df2f7019b9596847cbbde26ea78c78c6db769ad015dcb137b66130f818606c4409af9dfb1a67f0e67940125522c7d753f891b57f771894c6cb252562e31f1d0b90eaef22912d5f75dc1f3c4b98277cb8fb3a89753764d896cbd824a0225aea9921a9dd39086e5f2f8a81c433756402c6ed59e45703e230c58617ac6ff642701b253ac488efb79b91e02d4e568add99ee51cbb54c8f618b9582da2a46a991eb2c7e4eb7aea6ffcbd8ce7e55f75cffe01359d8308ff876f50d7adfa12a0f12ad2b90944180e71dc31de350c0bc8f7c1fe5255ec35fe6f69f2e90fbcdd1f0f27795302c7869c8a6db1c03a1ad0763a5cc43f578f7e3db878d14647be2227a5ceeebdb47fc62a96bfb574b59b936b9a6f665df6de4255b2eb15cd61564133e698d745c4d0588b44a1d6fcdaaa605603b27f99b5f1abe25163b2bd167f379118b7c3ac139020fbd116f9ed70632429e080cf90d9db41c06bbc68c1c99192d3d3c1029773e3180a8206bb1556cbf79ca88e683b638c112f6a88405e7ccc2a0eedca5d084bfb6f4bd59cfe66d6fec17eb4a53baa92e0238b551faa6404870a1eca9f93dd9f1c54ad0c4d96710ed1aa8abfc4b9ca1f9e82a728a5ed11c37cbd6d10a5ed4d6b7dfb8a906999acc4adcd38e17b3acdc59c30f7c58ebd2190ad9268cce3867961539d6c3dcf8478270800cfd618a3d45fadb27b8b025f35256c4c0a10f794a591e905f9f37886756420ab4a353d038398cbac02a29ec7b71f909daca29a20f14de2820944b2d93946abf8bf03e531b3250d3f48c5334782fbf880464996d7367a4fc1d0052892c02bddb5c7a867f2c09ad96bab4fe90511a9f50fcb59a332c3ad0d11b79e66532a88dafb3becfd7cc1e4a5c7895615fec66b910590fd2126267964b2e53b8d656c3b348bb0c3e60e6e1cc488ec0b2bfd5d6caf43e1264e859a48830c58b8794d2e4a87026e9b86a87fa743787ff7da56231b88cac9adb0bc42b0aeffb73939b69f8f47b3d7ca1ac5f4b3c941c952c18fd6e31a0bdbcc9583a97f8aba018ad3cfb21e36c26649ba6442143008a91e21bd4c838e1d3f325f06cd60586f1c81bb5f1f8307c55fc915abe21b6b18d875bd6a1a2eef3e4d65c2333448cbf4bfcad4d6028d390abd3ced55cb78d75af9e2bbae399c321a30578c1d255b3adb6f1b796cb4dd2e52a7495b650ef164bcf853838e850961c92f7245a6a67b88e992058285cb79c4bf0cb684d00d5685abb85291d1f10121ba82f8af8bf3a2a70e4d31f946ccf049ffe57b00cc34f734f8ae9cf7172741208dda477f07db3b64e5683457965949363064ea99b7e97c9a29b56480e9c9ea6195e833de176fa79c257eb9483093016b0bf19f8025614b80ea6ccb62f9fb8c74f7cd0bf01bd4d5e9bde9ab9ffdd30b0af958945398b31b523f61d2cfce119108e5f6be830e3ea41288020920d2125b5091d258a3becf634316fa7efb7cc505a17b051eeebb783e9f26d62c888ce2dac5ba5af2e7c09bad88ed8686b8a44123d024603556057a32489e7bc92c7f64c8526d2acdde09fe04db0c5617a0a1679bbe325965b52cad349477b552979c729875a0cf42dce40327edd4d73d90f72d5c5748c38c9b4696f558b7af9c9b584d78509003bde269d5095c7d995ed693d848ddd68a697c311c77986bebcdc66f94ef9d7f5d54a5ecb6ab6ef7f7b30205e546c956fad485c8315b6b1a980f9bda0782b0ee80b39525332e68713ad986f6e4c8b6c6f517169609adb22a5b492cad8b92be747f3f7ef50b0b544a5219e57c074694c765187376ccf9655dd3b5506d9010276b1053987a1de5fbb33a0e30ad3beacb6b78c6d6dbf491a68f79afb5cf9e75b75679cc9b4ce3843cf1cd4a196036552b4926177e46ff31b83bc96b6724274fca20a6e670ea7d6e0761a235aecc0c6e7adb6423131b683400856d915f7a42bf8ddc102efb7de77947755e9b56d531b2d08e233a66e2a6ed52904259b1226d195b7e6397cdf38d229a9815e86f986c39cb03e32d54cb6fa272dae5d1c3d3830e57a7dcfac14e9c0c76ec62a6a5f52f2c1f5033d13adb936c1d58fe1d35ee19047c76a1d7e8c6b6875e8f081459641f4778d9d2e63225c1f1a6451e15b82f6ec529eb3e16606a5e3ab6952aca2b2b9f2bd29ef323bd81fc5b2bc42a6e15ee4ea2a37caea19161cea09cff89eaabb9c4de67f10983f8040fd1ed6afbef8216e8df9130080494fcdf63a915ef201b00ac52362f923c4c50b20f3ed77351fff79e01be8823436bb8fd1076eca0314f6cd43e4cdf0b7223cc1afc2128773c46b551b099be54938f391e9eda468fc4558d85ff890a4388ac227bf04efa35ec8d4857c3ec1c9fd8655067db6990912615bcce0954adc03bebddcf141e5d34ce344624fb14f4c7a1768ae68efe6290b5c103afcd9dda401d3c73a4081f3e7a73dedbd8f4aa216a9c605a0369607b870b6f8fe5c9c6d850206724a8a0f638828afec29c2e02f8113a556c4ab79c3979f67d42b674dd78f4266177dc1cd45149550bb20654ce44caa9fff1ffe8f3fd8f3c22beab5ce2f1b6c5fabdab3cbd0ff9409464fcaf158ed2cf9426becad30abf9607611bf72dfd1665f34758fbf1d274726abb8875a4e9bab563c55f45cd7a3a476276259e60fed4a597536c29787400fdc6e9b4d764091e00a5b58fc0b5eff1b5be86713e087e1f482b29b13b2664f5a12a5c5d785be38999720a2075e95401f1c43d22778027045bd6f26f8cd13d6e8dd71186aa0e3d039b1bdb889b8634bdbaea40ff7439115a9909e41efe42eda9932e3325f6ffda38e4382dda218e43f12f68792fa5c31e689fa94863276d0ab336c3b8228f696c4dfef31938306064073b1c2ee407472641288a3e9e944c2db599abbfd6c74670da20b92d4f722e42e2de8dbae5e109321051c292fdd61ed9bb354a1981e2cd058b77f481aeeddfefa912cf0765606495c47caa6f0860d885f114e52a3adf45bfc45d5d2bb5dff89130ed4824c129bcbfa04d27bffa59b28c80a7abb555da079c3341f67113c44fe31432099fea9fda251af51df91f71df7394ea6c7681ff655b8f22adb87f55d70bd73268f1502df3724853d048050dd62ee23f1e663c08181388b1fb266ca8c99fa1533594ea48e8438e140592803087eda03e1ab43adbcee5a32a5e80fc4ff416bd480404a18b6ed15c820b5cc587fa634c0c85c202aba7c572560e9a5c853e2da6313c54af75f426df720160efaf55ea84990174c58c6cf4a71e1846daca5ae7c7db0e6febad9886fcf4cb286a00601e66719f29479ad8372a2c0d562a0519afb375b5d4df843f8698f4be3a0a4bba865861a8b6297bc9f2b5d1d2cca9d57037e455b140393e1b416d2c14d51607964411bb1cc4ede4e7224a3e9f5f09c8d491e05443695de665c4628b486410c2511be3560360acdb622b3ae2af5cc228d66f52a341f5327a01ae15eb6f812d2f374c65ff1495211f472c33c0c20b6ec7ebcf90759821c9748ac97da5dbfc886f4688bed1ae849893800bce20393a94ee285f7a5ad7c8983798469dc949f458673a89a61d9715595bef6a28cd9caa11633cfbf6d2a911fe621044670bf8f2e7302f76e4f1ca57e8179a0bcd06c34c8c1b8b89d8115c6803c60c9b57fbbbd1ed2a25a2ecce42323010dc2e92c7e5de30bb570e3b164cb677b7bbe9102afa3176b40389ed1685cb43edb0ca148bd90ee7d5fa8a6cdd6ba40a0a83942be4f5e8d9db8082759bc8db5472a78838da618af3a79929fe71751e61c53642e0ec0a580f56b6fceb1183ff11018d56501470dfb350ae1b1f80b30845801de02b57c60d5211145dcdfacd37a67eaedaf3483763d6ca2969aab49712dd0ccc63eac23b216298e61e13815f7c8315a69547a2ab32477daaa31f9a062b0877c257a525e174ba9aeb24f17dddf50086f0899b09394365603117667588f8a57d45b3667a00afb623abd1389b50ed43bb56a5db789a4d55d3bf6088a6194bbc87863e2205471600b062e6ad8a50de4213aa21e232cfd580cb5498fbd031cd154f129a458225aceab23f7181e77e3251c9f50bd033f974b8066f11367ae95d98192baefe111cc05c2418e23b0df5ddcf72cb52e6ea2b26401030c9bed0a1dca4fc6cc990c64b3a4434b3ea3897fa3723107ea009048e426e0d1f8b20ffc09a4a09d8e97d7695b290b33bf87d2749643f6cc45355684847023e18f5f9217bf6d4bf390d88606d861b0ddf8b24b7a4d03752bc23b229527008fea97c9bdd133382aea449b681b2aee20d23d93b8c70ddca6d20fe6937e51d307adc66bce226a984e4260bac588eb22dbfbc4f00f6f1c20fa9273fdab6959217bbfbb16c843cb3e9d929f78277c0a17bfe9b3a8cfa70d0fcd2758485312cc5308859c761d326c141b20df4debddd8c5167b12849fe07ecb05e28fd9b6e2d152085630b42b98604d41151b0702953aaeddf2454f1d980ce3cf62b27c544a9ebce68c6bcbc3ab6fcd43d9efa73ce991b6cd36ece02c32c9ad6ccfd44b0d439390c07964543dc3462f50e67f2b6d170956c25714357d5f193fba954e0b1ee45ce255cfcd6675a3c3478dab2ebc99e88dffdafdae83edcf64429b18611b98492a22e143bb0ce40558facaf0fe21eab46373509d855793f40231c4a00a546c39a2c80543c2703e3178c3860d7dd34fdfcc5d51d858cf678fbdf05ba4ad94bb553d0c46c268857bfb70b4a8343f4f163b09d70b1a995d70f6088b8f88f53190e11105be7a217fb0f1ca4eabada7f47c8a91119c80367ad7118b73602c70ac5a75df20d158cf80135c406895184d35807e71dec5056fdbde5503f8365d1e873099f3082d757616c33a0620a7bfd974c79dcf482f85d190c5480468c6df3ca25911636fede7c12f7c275ec7317fd9f8fdc814c89f4b716dd388e66d659715812cad15bd395d2d250824a5a6b693a29d602f3827ff1f0372b17a2f4d2fd32ae9c3857e649e4902d281f5c1d090397cbbdabbe950769c4aeed7a007871094c463802adcba16e12cc586ba1dce48f36e5f03899917d260f8f3c77844b2b379b96ccb113cf11119a11ca90ca3fdf5aaa169b76a59dbf925feeb2efeb8d02e46cc13e7b52df288df60e55badb5f8f3766b1120cebb08671254cbfd3c2026bd89b65895674add3ae1e9532d8bc287356382d477d670f8987acac1ef81749449271a538b456484c95a3305de1533521835fddd163ff344e9f98f7494295a5c86d1767cf8f1e6176198846751f23e7c289557e7d2007d27a484b3a1632bbfac2f8bbe1ab46912faa90e07ce895146d9b81ec3ab7cc0c00d366d6dc57e30c99072cec832ffd219a6e5930ef483335a1771e49bcda23a927e161c33dec9b0d4457a20acff68f6a588e7e558de4cae766fcc11c1a13c59d1cec892e47fb959db9da95096b9d0311327fe7378b377449b48895b4089c2cd87333c471fc5e217358ce26b50ecc7b8b05b535018439d6dd2e5ba22fe55e0c2b9e6072c0b99e3d8b81ac9a712ac9147411b3aadd1c7d2fc5618fced5f2a7097683ec8ee0316f583f07a20599912ab06585f0409ac9df6e606a203955afd80d01c53d6c31e12a605e1f84d28280b76aa8c30cf3b4f5203a2776f4a5419de2158830469e319768f91fb33910a45323df9024f32809065164d909641b6e1b7104e689b57f71b63a7d661135df95dd37ed85be3cd987cef7eb82b85edd15cd4d1e551a70bd614caced79b0601eb5de78d8f1c3cd859a347e8ac6e652497f638988cf4ad2ff793113013b18355b4e8aefd339025d494708fb05b8e1ee0f82223f676eed7406707120603787de0362faae32e5c175eb02266f22025cd6f6c509d4cd984578101ba23c8f5398b34591be114aa71cac759091ffd2f51ab35b336632aee0405b921a87e8095c71a3b136c3ae871fec97f4fd95cfa15a30129ee4aead6969d12fc4a230250c0dec484215307c393c2922f509eb9bc754785a560752e23fc38c18620b034bebd43ca0cb976c890c7574061a3e1cf0d5729bd9d969464a7e1deedb4b33c5154d55cea06b91f42fdb1a3e151c8d64d29865c7a04e08e1299dc4829be58699a6d451c4a6102012469e693501e5f256097843e8014fcef693831c6e5ea3adbe968da9bf155286fd089b6ada134d668a52b546624b7c10f3baa09963fd215fcd81c6b4df1bfff8de50bb58f1c7a19248729f518d1a754b03560c36edf18310f8d746eec8a932bc2173a1350ecc9c3b1421232a22792e53e1deb0c5fbd9b03e10f8002b860fb01eec8869570d67ec55d2ff2fe4ee96979d0393582f228078073538b0ba70b13567f0197f8b90715a196cb7edbcf8013c957e5d3d74b17137363a2b3efc2917a67b557db25d3611f4176ce0203c5779ac73acec2f61a9f7846562ce1d4e1f550fc5a3adc1512b77fccb02565300df3ded4d431f1e5e1a830ddc99e028651b4f1fabd1ad6e9e129dcf0fdc6f33177896aff30445f09e1531ead68625d962b4b561f712bfcd228df45b6380e69151f0d66edb41bd8714e5d346c2fd0eb99a6ca57f63e63359255ec644e20045c40bc44a62215da9f23e3845861a3e979726e714e63fe1e54aad3b17a6ad6b8add14a2ef6046f66729d9416534bd2ad9823d60384c2cb0f231da6d0878ead8abfb5d9fbc03a77a811313dbd9ebe82b5a2f5fef29c7b331afcff454294c9fba757be5e252ef5bf4e6056e708eb92c550d5dbdd9ee02d88944c2161f5b94ad520101968567d7752f29c3605dd1e78cb8c99d4e367643b5fd316899ac81b1f49f3bb7511dd2f2116b8efeef8e6df4131928afbfb4cd66a4d92c0f176e90916cd92a4c3d687ca3b99e8846d80efa6fdea84dad8c685d2e1c2c2e3ac2eaf22cf12974e3241bb0fd0a5b7445b2d141a796647d4483006f02e26845db6f8487b6ca17d839d163248395437c438f2e1a47a875c92bda7b13656e9d547d0429ee95a72e077b207e85fb4595041a07adb4e4bd768e19a403972d6887b6716b815bd89ca1814411536e7443eca4891d7a5eb4a9229814e6ddc93477ecb8d0fa2c4f44516d337e565e32073a1f1104658c92d3d88f35ded3253f2d440f8a96575478e6cb356a2a65226cda4364ab0290967802894a5cf03b908c3080c54be5ac68e073c02f83d08b5c06c8d8526e77b37d5d06e0afccaf0ff508ab19fe4e11a37d0961dca83ffb142da36a769027b01a26510c992b00b2515f187ab39480bc597be11f9108b11a886b7bc2b5b2974b34711acc3875eb1f6a619262fa782fd26ca32e067b71495114122067867eccda73607d432320af8a2f0249eaa5166009d74ee32774892de478bb95922ad4d0f4f9bab2b17b2ed7abd06c98fd1df938717f65ccc3f4daadfb0ef42ce1af81acbd9756d0186b0c772ce303f84ab25b2ea276d680b5731a6a29b402af05ca86daa79bb1dbb22d2b9200a1f99abe27cd15b0f02607d396723b68bdc988a55ba13f3dc39c379cf1d3fc5708dc8e2a8396ed6724163f39d3282d18702c6ee09cb619511033ae379a91c9ba46e480081b799a289216be81c20cddff686190dd6558ab51de823d88e56f3f4c44196dedc3308131ac46255a5ed7a98d98d5dabb8e09c364be33eede368caf260b3338443c363b25ac6e23151b67470e4b448c3fb4478bcb31cbe8acdf3ba90b9a2b8994dc32362272cf526dc6667b76af739fc084e9a3b7164bfe296a7e48f617c549caef8668eddfb90e1b80c40f1a8f001e93d47dec487d28f03a3154b0233bb290d45633c2d017e51832f576b9b3981dc2fc7be71fbde31e47d7539e9f85d1bd1f7cf30c0c181b5357feee16f7a96e6c9e329693f6f023b5c3ca6b3faa75b2dac140874fa6e390f99f3d66c230afd6526fc99db68db8b7fd91e89a3cb5bfa704b22177548faf124a1e3fc4d3fec64bd949ead53bdc1cf050fdf137ce0ae379e3cee955a7b4918f48ff7556a1ca43a22e9cf5481df3b42c4b54f8e89dd7ecb62782a7a62a4e562141e1d56e85e8ffa53d2191522509755c0531832a465e830cf5875998aae1a57c38d2ec4f7ad0a719df15afa8251acd12acd3a866a7dc83d3d3464e26f888d23a4c474bc1f6da39d33645209f1ca3623009f3bb657200d4a6c1b0266a140dfceff8115cf1e78f080ef422e60494272142a36b1191ad529de27c6c1d35619eb447d6d9bf19c92324f65e21658861053b1c1ba9a3f81c26b1cb7bea4096ae53856a7edb68b77a030a9f1b0b5f950a1f4591dead29a89272017f1349427dd70a0b62edecec9bba10cde854f743a33ce0ce9b0eab48f0b87b5a96065f3a1055d0603599e8c6cdd65ad23b29a7871f83cbf69cfccc70c6bfd2c13dee4d854e434f80368924a8b25f997475c26199c5494cb6ee48b9648ab9694699ca949992ee8f48c6752e78ada0d8e14c0164102036f06a28acac64990724ef5522b66186c60d597212578a8917af94f0db00c658e23124b800457fcceedb15ada7e734941396afea15217609ee0147c9b01b0a7e0de17eb73082d4349c3a5ca46250030c54fdd2c427135f093a1c6a7722e5b0152ee94d3cc0cc87dd0440d159fd09366bd507a01a32a94eee0a34f6da187fa1349452f01bdc10cb62a475e38af002742056e3702b8c5402689301b3cdee8f7b8476c88056180ae0ae01beb33bac43c84257b61577d6852e3a5de2a7c44a7aade3cbbf12f87a53a2c044b074362ff6eb84332f55de437c361f151e09de94f46a40961fd61f0be9c5573933931508f61bcd20da1b30dee3a9e5eef78ed515f9532efa4b615d3292a564b0ffe5bc50b24bad609582f4d27889d94f07c8393e80c054af41d1148a9d50e4a239d5f5625070c1cd37a83b5851bc73f9b25eddfdabe0b67d638505a7e14222bdfc10e665248855d4588b57c0bff07ca0466fb0af6df431cd819b12cc27fd000051ae2e1e155716fa99734a3888b253732be6e1890ed9f54b7cb5663b8a398686bdbbb7b786c63360e772963b6863d7bde46994c35e1f8f809233f3cd66f7291016cee71ee738eb1958df6ef19a82d16789a233358bbf409fbdf9ea75c8797ecb43812f5dce4d6ffea5dcf32ace515537aa40a81db02b129adb4299ac2ea1e07d6ca1385c1069f751b0a11680c102b354b2812d208b28f56f22ca42ac1d429d1795f9c403b13cde63cc7af1ac5cc67f2a3c4d03bd9172c122358be2d7f56100a584674b9e38cfcd04384050e741c4cc547ed5ce72d23fe883e9a2fe5e82d9392de378404172b8dd648311465c64b4152a0695c97c6a3af11a018318b451742d702df9675f084a78f5b55c16b0a3ec5f3893e0886432d9c539eba60a0c226de8849d4a9dae8a240bc9cde0081f8f64d0c2e60c065ea2b6541595bdea49f1cf97ea7b909ffc73e4884b4ebec2a05aa786e2419494ee3a8d75da80e3ec1a5d3f035fea2efba46aeb9298749d3457c3521c6bd88fbc8c6daf2f7623038467e0f6c8658a1a34d30796d64a088d2ef2baa1307725e5d00fd3ae7deb18ecaeddcef4e9c9ffe77cff629de80ec9b09729ba565cfe0b06d6ff76fefd8f3fea7027fafb83bc0f9b596477826717b0330c1ecf0f4f33fd8220455e39db25091e9a32a80a0739c4d397180dd69efe80c9daafdd43f434e95b139cbef10afb025bdbf56950fde43c34070731f58ffa91e73ef94278ac4ca54f6384aa62e04b7e1e130a3255597750d28b65000b74d63a7835d073ff2e8c1d7221a0df8cdb531a12b3eee7cd1bb41598e7284f5c2bdb7a01c546d2e2cb3f3aa4519f2c080d6695cca98f3d00047f39d35c2db7fbdb5b1ccd2c57bb3b098beb4f1fcbe903a3e9fac1e62acfe45d787f4d8b6c7d816ea5d9e8dbae7f31d6a2007da2224bd315d7469d66021f60a0eab512c8915ca7bf814e5eb7dd1e74180f68662413ed7e0b4a01959384f6257ddf362b28cd1852d102167a900e75ee0801cf9b8fca34c2d31f47a4810c0996915455c2bc8d3a65f853e4c010dddd385d9e9b917fe29a7e158e99fb0a525657961fcd421d8cd6472a7a8b63fb45b51f2183266cfc677a4a6e4e4d0b2b14ffeb491881812e28e75eab63668bc1bb7fc0f0d5aede182f9862369a04f7783c099aa4349f98679fc7f24f3d3b452052803418b0260478067252b8b99048d8de0c06a2acfe8408c60ac1e7740ed2bfc9202b0b40643e83ffa1db9b78681c58c80995ca4dd1240cf8c608b0cd27147c4f9ce8ced4b6169acc419521caf8c9619665a0238fefca5b9f9f786cdf7baa94a3ce394d39e644a6598f2f19346b6e977f21a648f050e516ccd4ab9f1fda0a6336dd65be7b1c5791cc0d378ffb31f6ae76e2fa5ce2edea66d2aee9f0e221357e4b99d8cd054c767de4ba397099a9efef3777b544602b369f43286b80cbacede530fb0079dcb3bb5c2c4aeb6b3cbcf9108b2fbc46645967f6ff470b586ac657946a4fb98ee8998fff6f31a515357ca3338cccb4604ddebe4398de1c28fdcde7504329426ae46f7157a107de52a45c93d006f4938396087542887f163521d432c1ca0d18008573abb1a35dad661be1187090d541bd73a5de1d48210c93a898526bdf6041bdd1aee509fa9538039070d8bdcde64a90832021ddcadf27b192b799a8525ee7cd7701d5aa7be3f9a5a59fe9a8e29dbaf90ed83e98b7af0f6729e7c0c9adb0c2681970370daab595d793d19a6d0a18f7d02a8c976220b7b4bb8f60d51c4fea491a034ba735a2c3dd858652e44e0555a7d6631af4b91f1375392dca46fcf0370db224a45d16dd187e70fa3cbf087930f84f0568a68fd5c1aaec5986f74b19667f7c24027c2bf7070ba383ba1e1ba3f41528c6d8ddb26173b466e036d713633618c212041f462254e379118fd454d34af669b2742e6b6f3447af9367154767ce3572f8c2ca0f75adafd3b8665f730e0f7ff318d7defc6c40af9ac1b3297036ba5207c472ac8a09bc87844ae15e19b92bbf551469ccef6112b0df6af4b55abf768d11bb1ab044307221d6c28385450a616aac1e4bf621348bf3a17398e4d18adb6ac485d19d1df05bfc23a19210f2e3b2a39c2980757f36f20b0b60b6301078bd7b78fdccc752f1e0afce7cbd33e4528b4dbd642558479d6ad6c49cf88c0ca47d8cd04b6051d1d245bc684b4d70d6592bf94b02d83317acd1256e3eff16641d9b2978ef48badbb97574bf4d980ffff1bbe2dd1dd2d65d35f874e3b469f87d2ea6a57c76dd7ca5f73e3664487db3a95c821c3b8869e55dff3d23636f5cdc3590816e8259a9cfc4ea18cf0b078594e95b3cdfae45a23b6c4f7bfefea5e1353ceec4ca8cdcb4374356645fd8e7dcc7d482c69b04ad69bd10dd5736769b99d360d0f7a614e1f626ea2bb3bc8e8e374de5aa53c7f0e89a0ca8e978b8dd29f4360a43e35989f5bdfda104feec86f1c23b6560f5b034f1189cca41f43edd18f8e1bf57a499bc7cee0cd1bf4df0368d6b4a22e815a822cd928bf6b4eeabbc82795bb7541a2b8019725fe734fcfd7f0d276ccbf1d30b8e330af5b021cf3cd56714b3b66a30933c682799c7c3672dac4c6a2fea35023cf86f10ef690ad2a1e29a79ceb43a509d027e6b80fe0c714f4d26eb0abb83b848937cbfa9b9b1d4617484dd522a55efc9f6cdeac57aa7d009e78f6fcdac173b3697a73d5e0f9d913fee27aa85e1c674b00d85a9b9b574932c64fcc61363549ce218d13954df4a6b06aa7980c1cf751e40b3fe0bf4327e403eeed7307d3031665ecc6f74d44f7f545ebfc908079a7b7102cf79983e315372d52a830db034b7b662c7a2a1b6e588e554852f7f7de196c1b1c4efbb3cbad824cb68dcc6829fef9e8f74119892df871866c0c18d16526dbc90eb643410501d1b69ecdc3771e0500ea4a5ca84bb8d549cf8c28a3c9916cf215253ab2294157880f97fc7abcefe7e0b40be22efcee69f380a0468fda425e06bc15d01c7d31dc7b8ac5ad53143679b9049ddd20187be3037af09b1fa5f25f9b89bd4a01980c6c4ef192baf223a28b79c5ce6ad2db8c0fa982511d30d483b53321f372a1bd6a7da2ba7db57490b9f8e01c4749ca0497478d6b3595fb7043e6c6921a0711cf6c64f778f61828eb0e9a9ea5a8d349bc62b76cfd4ba9399fa0bee914a7e7478725061f4aab9b391e486c9d7b6c78be433ab77464d017076cf3a1ec12e433e2b69b1b280bf0e60c136164124259ff8ebad3519c0ccb7a66753dd172e25fffff0856b561818ea2c72e942f9f23ff03cd4630a5a6c7b98f3d4809d39aa78d7c5e8dfa0c2f232d2613856042368b95e80296145b3295ec15cdcbec70fd9370cd13577596859ea719a8984cc6cec943bb782dd754ca4063447557122c62db27b616dc0ae64b92fa67a81844450fd6c5cc9d71017b4b825969cf78595ecb3b55580b150a47f5d7cd10fcfff33e89f0c9c74c6870c39421f0a3c77c29cfbcd5c98241b1d3157cf415d9bf2687104ef33159aac36560cd8d16b0c01168bf849a3e7ec4965a92d7036fd4e4e9441de82cfee63875ab1af719e7910bbced05115c36bca506fe1c6365fe68efecac703e7f086cc1609b021db306cba9915eae121267efd54c7728631e133773d314d9e39093aef998679419b1ed326848f86227c7deffcc040f7d459fe9cef2e28bf2bcb5383f5b71ab48df4f64a4d611d51f3087ce0c8f87d18eed614af6eeabafbea7b0f9db2723b7b3ca6302a410addd3ddc29d746e497148d35b24239a2cfa571db5e6115a32a6f37a9502b654eea237d9bbac4fdb6c3db149a86ea1317174e69ceccbb1b7b861716aaf6b9d2768924958c41f3da050e52d7dbc68de6ed50b5b6fc1ef9d4826cde6d8aa8bda8dca18ac887c665b5012de1e9ce47d1ff2f6229eb36c977194c97288ce4ef1f10b0e321d7281135cef443a0d7a5995c2b022862e6e60bc2d37b38e114af751e1988c2de4226abeedaa5986faa0ea5d471abcf1c648a60c5f586236202b5c524c0fd0bc5da52c522eec98befd52e76a9d62d3f100d65330e347f37952f27f9f2f3a17eb29516c756d6a1158ecfca3774d55d700b2b07b65ea89913391b020ee6e3473e597af5297b356f8447b44743ee37158b13f94ac9df6bb88e2b5d2e1c851005f09853292b44b825127fc1028707861014b596077868fb9e49a50080ae9f24a32e8ebff71d2287125e6ddf3b26bb9d5625fdf19a0437afa3cd3ef240951387d5d1347faa7552273a9d97f498677561997fd320ecc4d23dd738dd4e6abab4d4326d4e1c8cae579291f3ac3e753b66ab5981d39f14da9180ac28ffdfa1e19c123fdd2c27a0d087f54bd3090d57c8aed8558aece193f0c894ed79b93613241fa685391e87222bdfce7a323f92d3a1bc9b24f3b654b4b821386583645c9db3aa26d02fd135419ad45cc0539ac017c03235592024358b4f95bb9833a69617652a2a759e71c65c2c28e361d8acaf69105b852bed52911ee276e409124ccda5112d523578f30b5bbf77de371d02448b70492d01730ee926dd26d7aefaf08e0812c1cd07a50ab7e3aa516df215f22f61c8c3b92a0e88daaee02cedd4c738c6a40aa042152214d100de0b79b0d497d154fdcfac77a233730a425b0cc32eae0c0559944434a59ee1871cd53649e59da5e8db71357f86e7ba2161fc2c666f676bfed35bf6dbd5935d0faa54c514ee0cbc76dda6d45df58f7484b75bd1cea2ceaee6de78a6fac9ea07f2a71d153b2a117bc84043fea300db99dbd64419c296d368a3ec7b1aac01fce6dec99f22322ba6c641dee5298702658ac9c3e40b406e8baa19029c6a01cb2f15b3ad12c6694461b7a72c0fb70f60b241e3a2e60fa294633a86841f3de047dc1040bebbdb59a2d290ab3d1107f00a0d365e68c638eeda307b12820a420e6e48b31f391d9f50f27bebac8d7c5b53953e5cac4bb24242f901ae395bcced606b00a2c9c60730f51cac22050cd6a3b55b1ecec6a0608a8f1f3af4e76f002147889b77a321f294b9a1e1f42f87f1db23844a232be33a5411a59b6e81c5f8bb8949079de17cbf6694029ae57dc16ab14cdd391bcd70d9c48460dfba425bf4a7dbe4802273065bf3bc1b737b1fcb41d268e92d3bc827e8886992d03ca0cf9f6c20ea2d09506fb663a5650e96d98e5cacf49de3143798b0bac87f43c372fc03e9820df7964aa13e43a08cf22517283bb44d3cf8c515de2c5ed12b32b13cbf33618389e5045128df00277ec416e398c4ecee47993aa46f08f3dc0a7b2922172aadc8594f00f70e5646babd92996d3b3791ccba40fd3b4ca741324d886463832b11c3fcdf8b1751f18ac0ce7b7458e22cc204ee7afa743054be6d6801819f4a2c83ed6a14c4fb48c78bf2c3ad017cfe9ddc484c80d2c752be335333dc74e6236caab5836c07dfa3d13b06eeb340488ad0a45a42f49b89c40e6d5a12dc42b68abcba1e008d4d1b76e98e44b4b62eaaf70c4638fe3550de08887bcb247ef2870a7d6ab88c48b37a25d699c58d744472d39ab315e59b795202b5d0fe42642383fdbff4ea0b46255c48476472249f0eeb8d2346e7bb9a46254024cb1ae5d68e02ce15c753e9a592a5cfd826f2af8e73fee5d23a2f49e93cc523191278c9e3e8791f26229794270f91d4b9bcaa10a5ce025823c7743f79ecce1deadeac86cdaf76d66523b24f1d92dcd27afb0d009280da592f33323f0547760283aa7e02dd9dc1188434bc7943678c6e7f12e415955c673a644e38d6dcd5f8828b273f860a427d589234b6af2f5d1e8328befeeb4fdd1c315276b97726daf0627bc48388acd054cc621525ff0b8b49e13942b04dffc1fbd274b4fbac24ba9ddfc61a0932f33527fe522f512caa0377cd0b77637d91da71788473ee32a3cb4cb4aaf2e35a479fbd33637c27f9f8306a4b4785f332949fe189e7bbcac1685d7074010fe5439081e0bf1912142b99967ad1e07a26344b816fb9750c0febf40a61aabbbea2ba57d552332cfdea829104fc1197677561aa692269f9d41967599c496721ba802e7e50e16d730e173f5d32a7c3000eb65ac0d3bda887ef385388f928159bc2fdd9c2da75cf53074e4a642955dbaa817873b16e1bc3a0d1605fbc0b09286a1374e51a624d85922936d6f43a007a09e1502f1334bb6141ae755bfdd47cbd583f4eba95f1912e2f5dc8fec334ad6c8f42580ccdc65a5bcbd4bc4c6841a008d92327a96e8aa296050c772f6194cb96e822eb79d2e838d7de3a24f8cf4cbc9d68692eeafe795b18dc2dad548e242e3fa630b5dd7eb9c1eba55642a89fcc748f945cee226370ff68b4d3aa3013117d027ccd486054b94b0cf449dd503222a7c8317fa043ebdedc87c42a7f28fef284d2acf09548107bd03034818617f3692ec783da807df1f28d3ea1223fb9d8b3e0add2340fe973e6155efd61acb32879a5513221b96ad543d483bfe0466cc79c8b00f35f4dbb5095c66f907f8a7ce44de76a8bfa2bd305549dc7c8272693d5acf1c754be2b00bbb20a7549801daca5fde7c28a073dd80e324eab72964ef41c4b59dccb2db851394ef9eb87336c03288316d22c5d3d39a25754c027b2546417e0b2979a5969bc93b6f200f39a8c2019cc4dec4ced09e142637386cdc07b582f5b5ba691ae4537327503d7be927f1cbd41a6a3c6b7d25d35b19f71b136ae4aa3e77deb481a68a53b50df34a0b3c4705c085ff69be54e90c5fad281c7a4236e6b23af9d5199fdb170bbd77bf80eb38124f1ef7112ec0fe5c87ba48eeb6585f976e35ab4b8c1968958241f5df71e7343a5270fe21e500b3b825a393078105bd973a369e350113f51c8213c69dcbc03a6e033fcdb9b55c66aec8439433c5cf325c1669e734ed47515e5380c741fcd7eb9fcc758e3f093c0ee19f353c631d0659319c0cea2a2a250e16c81ab33441ea980000613bbe955844815e0babdad31a23d0cc0811ea1f9a27c10329abe9123af2e446c96b8931eeb6810ff8680fc809615f4c2761223ba1c22cf28ee5bf5b6bb8a86ac164515935706b95246c6035251f63c86a9cac7dc30f1e76f243897127bd9b68112285f9e45cd5c147fa6c9012d036c423fa8ab0cc9fca760ce434099d47f452641e08b5a5aa11ba635c2ca414a147e201ff2c1ff28320e60219bec779dc6154c201b74c8b911417a2dd6e8be266464ba157861c7d254c53f72d27211a2774c2a3f982be7b0f2f56416b22a3ed9457356b08bc7ddc74871afbc963cdeb5e814cb53ed8ccc4778b1a585fa7b5b927b4451d124b8da5f88514c3191b6843140d53b768f194ebc507daadbeca1007483e0695a8c5bd404cf835667741c1cd6e4f3666a0c365e28de7039464032e4e4c99143caf2549ccca1a991f216ea1d14474598ffee3a6945d55e7984b2909bbaecb5375c9cf25616460260320cd6e96b0030df898228f9047e4fb8c3117ee1d27e6c5caafa2fe55a60a04a1becda89f989f0204bfa7779bf537d519a15e632bd2d8c764c639453c98940ccee2c0b19df2184618abec228800e965eeaa8fcd76d8846720172d50e63b7b0172ad3a9d94901d9a4e841d7775eeb22c8f1afa67d86c10a7faf22bab348d97c506583adf56d1bec23921c5288873d36d2624566e77afd99524d6f8b374fb7e30a4d1ed59d9430ef76390d302f0cfe2d3e7b89a5d68d6b8e6fab56623f9c7f0ec13bda586213e614b776d79c83571bbbe1019b8aa0079512cf25dc79ac830cc25482cca2efcad76b48d3fd13e2bc8015b44a7d9aad3f0e09d678c2bb5a8e553d21f18496b08157335a70da7c8856fbf8a4240730ed97a40747674b5b67de03938c588fed2fc06eb716cbd07f048627561f82158f413f0f75a6d39c627bd13af46bcbe84f6473528b17a4ff84c808dd9ac5a71809ed172c1fc2adee1c16d3aa1ee4740f127baab22ad6cfa73c699adf5a061f75c5268729405d4e7adca8650c3699f1f63280ae5af45544fb938bdb85b500ade5ce887dfcff179dea6ee8008fa17dc5f56d377666bc12b13077a276d5b2bf28f4f91fdbba7d8a9fa98c4b8dc68ac1495172f77d31230e9e44ceeaf0809e71136683ba7771a7fa435dc98764483bd019efeb37f91fcd7ac7cd563c26df5847f83b8ee54e671048e9a72a5d7b729f53a87381ef04cc4d556a25efcebb182c209c25fe1316c0a1c87624726c4b9c3ed86a3f072326d435ede2794fb9a0f16d4d37a3d927dedfea79477fcd9c5d8cbe2491aef9d790ebff4876f0ee5f834895260fd05e5c6db08ffbe3e9753ef87933b2758e9629712ca26c94325af16ded69891bf8e6406fb146f7caefd4147bf738450aac0e37c02122e101ade06f0901ba02501415d4d95762a9f7b2a234d6e89af35dac358160eadd39a20059a9be797253d564609e2bcaed2c9d5d5cafd93bdd87a165db7f70a3dd9e9fac08ae17cf46cb62d42ae4adc468bdedb365d6cf4582e61d65564a30f44ec781c7c077ce2ffde653789f379ac6d6e37e559501a97cb0a404be12e0499f6c5967671068dbaa41dce4fce7dbe8c1e37080ab442648be6a66f13fda705d9c2ec635784528a3c1cff28fa4278e6a05aef5b489392ac16eb4368b76cd4fd29711925d5f547699a865fed4d353c10960d2ae97c33de9c349c213f8950644475d1d45b5e63f7bc6f71b93a4fb7396897e20786b30f8b0abd46971127cffb846a9acc3c43b6bf4eed0ec1abebffba951d15bd40edc7b820871e5b19c5495a1a13b4eef662c7d8facee08b59f01e7e79a4d2b1efc45500013026a55242e0b37ebc02fa96c13021f8708fb589b0e21545086e9dd4b6edd659856c6077eeccb21421e4d4d9ea3492e98e43458099fc1b7ede427467071a45d931b657c02302f064a67b5a8598bac793809164e6bdaf845f96b9b3ebee621d9f709d6a5f74c01cdc458e23b3a34fa87039dee9b68a7f9435e723209dda3f2ff8069248145b7b6e09eaed1ce304340612238a53c22916c12b3a4956b900d503dc176bafd1b44dc0c7793d80c42d03e165566b9f2b129a27eb8a3a434808053cd00c0ed5e9f677b29026373d97e192202d95d0a336f6fb89c0432e94f1f155a7a51f9db98a162a520453a6e79585a745005f63c2640fc418405a8618d3c567009e9051861d50f044c95800befeef4d113f7a0cf42d06ac88c42b04f8a0ea4ea8d93a7520feac4b3be8f8f30af002737795fdaf908906785f1095f9153cdfc215f0661c0c78f52ee4649505f1ee85ed1cbfb710d9c148bf9635c08e4c1e48e3323630e254591c7ce7df0c07e38fa8a72a00fe0ce4332d63f8f227e69cefe1b002663b2216fa9f8255e580a1f206b9aa9ed7a2c7db3bbdebd39c1ca23a7cb96412d57a5ab8932a40b897ce7c2f56d2c4a2aa5b73af754df6112a14760254e563af5a19882eb309ea97e8f32159f61ad470f4903f859402fbecf3c341d44fa564b428c6988e7c1e48f7caf6f279c283beabd97f9b6ac12107c0fe63830ebf76d09a8b4937d952d8b5aa6c8b949792381ee4756b5a429ba67b91bafa44689dce7a9f552d131b32e439cfdebbdaedff77c03c45a050e561d1b9aff39e04d73648bbd423815ba118f9c5e6284262b46fbeede41378d559568c5dc496cd594cefa6a2d15933d40c2d077a7e7ddf69885ff602d1eef8f47d8a01bf87a5b8207f6ffe8e949390f93a1ee074195892be5830898eac03545b256639743c20c1d6c26e5a686d4a0ae0a2222a8ff600ddbb8560974b7296853b765ec8b57b1bfb1b43dd6b83a355c37b2802ee9dc7b0e224f53346e3dfa8c727540818da2900525e6e24959806bda332bc649970cac66d75d7c1d53a516388a85f2688104cb3769f5e6fdb3cbc2569a5b959a5b83cacf92099d31db4452e25adbe2fd7bb27ce7dea3bc56d286c834045bf5c0136e42e323c2af77f5b4d84442458c57607ffa771bb2a7a8f4c78b144aaee1d7046298f456e84be070a94a855dacce9f9ac04646ee1428597ba2bef8a9e8c69d4f4734f68f05896f382e282be5130c77ed53b629c07d7169db815f9a814827f27dd1bc93215e0adbb259d0e7833abd880dc676f7d0953f825efda6e540c34cb37230f2ccbac38de0ae8c2b59d7829d73e3cfc09785f97a7ce4f6e1ee67f5110c451c0aabe81eeb5ce657b680d0496f9473d69e1e571cce8740e4d2d96f63eb5fa51aa88c95f813266c83b929b3ad29153bbe17e3fd7b89274965dbedb68a0cf883b8d8da95d15a9e4f5acd3d4741d9825e2619ad2c0601eb998fbf5ccd3f1f60b14713fd556d2bd2db2cfb061c0528955c11d8181f1e7cc865c8d28777987321cca6b084ba51cf58ff0e6b486a11b47b52efb605578fb935edf0cda77ee34d055200d1adf484cfc7f08384e096316050331f90fc4dc7f1f67fde31b5e5923b2e3ba0bba5b642bb48dbb385a1b068dd70a2712a8caef531821fbb5cd6da552bce4ac85a3b79076e7c078182fe43c154801ec4effcdc6e580c230fd72357b8ab36bba77fd0c81816b91f3d02b97c6b44393ecf817dc63dd40516cae27f3b9ce62ce123779740139bdba8831d5ee38a2b871eb16b00a3bb4d2b42bcea5920a7ebcdf9559e5de9fd9c8db1a6a8cda5342f63dd17c76201b92a3bce37ea5655def4562122bc7ab54216a23e2234f6f2efcbd09df2a327eb15d29ea3f7d3f411007760cf0035ffae700643b7d1b967004ea7ff6a034fc97977defff591f793654d68010bd23a554edf2a07db367d3c8282e56ee40b33ded02bccdc348f1d11398cc7d795d574af246afc49a7bc0156733b29f4d0d1d33cab7b57f5fa838d65151a3f9711e3cc3b485d9d221b186fb6e29262c305c502e9d362860ebb0b180aa3c6c7554b653e67db927e4bfaeb3ebc0510870b59c4d26aaa72cd2e93ba2183afaa330e4f198aab9351605ed7b494cd6c0634417bfe3250582b5344da5cb0d6316623dd3f28c8738417c302a6341794b8a4fe3f3cf1cdd92b8b4392791733ceac63e3a28e74d5712d99502968ca0528482d287137489ea020efe26a3636726a4d97cd6f6ab4714297085a59b35cb461ae9da63483b704f6370f4cc96072f3807d3092e5738d83d424d4fb60ffb6c3a679298c2f3bce3f34c6a4990a8d5209a185ae24584c8235ff32581e640b704c0609fd402a5c8c037bd8924305c930099debac14702290697210dd0e5e5c2b945e1b9161de5d90bc7433bd79878937760c204dcb95f561db25bf136b25ccecc103b087144493d7d7619118acd4cfad7d0e5f8814c643c0bcc7cea5b2dc93e824440dac10ea815180236f9b6fff13153427dde8ca9b4ac2b532df6731cf049d3ba6f1373711fe14f70a216b3ae1e2d4a64f18a7b5a3c2210ef441eed42992bc2d614b0db43612747b365c15192f3c6c846a1a98428081cf20629313794d112e54a3eb960c6b32098967320e81813243c7c5759d0cc4a6b761197734af7f5fad568770f89abedb9584eefb699198d4849f57ebfa4e2ec3f8525791379a8508c2ff9ee672e7da0d31a33016832526c054a4cc39c74c2a4e85f838865e9cd90f68f15e7ba8003db32ac9038759aea95275d271c7987fae61fa40413a5b9e38ac058a64a896841066293441a4cf429b5ee0317df87f8363410ddbb19b6cb2cb0df099e80712149a8b890471c545ebbdc97a4c0f29d0c61b4a42cc474a24425de2d959a973f45da898878aa859048ba1c179a6b1812ff8efd1801467c22db8078a7403a96ee8ba0b336e7f70f2219beb5d461303d538ef1d0336afe83b5c62dd30d831b0b189148a25bbe6a780c4c9f914c4acbd228b09a69469f368bb303074199e0ec925c366c6fb81f557f4cf9bd1a63de6ed6fd9810c401b1d7cc7cd940f51ae55422a6e1d52ee9fdb25f64e22dfa18d6bb0862c4abee6f4d4af8daddb6b17864cb76419764a5b4d33a023a47dd794be4d6028889906d27ea0686a1f27564cd5d23c1cb857a1e44391f24c90e35f8539a7c28c54903ec0cd6fb2185778b4f00c531a191633ee7b55a97210ddf8b7e4cd9f0393edd331185b86ce3f5953d2eceda3ec117ded6250966efa318023ce95f1e96a810f95d672211f1aa477538c17bb45865f7ad1d3000b95f90e406bbd9d2f209d4e05b9a75f7e5c07c9c52a087070d4c956f8d3017b9830ab5e9affadbeef21129b69420f0271fb777e48fe3c69484b119842cc87cbf400180c8d29b6d31721caecb334e18996f5717bb8c4950692899caa1354641bfad25f80954b3a1e3d108bb431b32cbf51eda9833e558cf682e9281b5132a788a02db65183f461a083ed3c997ffafb1217b5ac572d901d5b9f574d8c538f499f2e9c3ed559aa5ddade10dfb819c8f354a68ad54e279249300e72fef28aa89a211c82522bbc19f82b3b25b569054f27a5cfcac205332e60e89e5692b2d55289cc00b91fe27a27b012863c7ed26b1df28d9e70915709958239c8b53b3657004ba35c33a13dbc34648ea88d289e926ef33d6699aef7da22efe8e5ced43e519f470262cfd6f594dbb8db2304f6b169f7ef84784b2dd42f16bf1d60c8aceaebc67693f94da983bb77b8b7f7ea9f4cb1a1586c08001545e22639480036194e9b6f7f219a377142c8103d2138ca355c1ebb00de710bb422536263857c5f8d031b9c0127d55a1ee38fb6d1324b21b3bcec631fbbbcef192be256d8753cccc487a44490e69c6a2b7e2b919d06405f02c0d8c09a579ae1bb8e47062522fb3f39f34dc43a36b3dfc6aa64ccdca7d0da3d3f59195e43dff16a6d78046809c6bc2e69b35c7e77cf51d3fcb858800ccd21b6286da94ed63caab0cb1ce8d4cac59e8204397d156c612ca4ce582b2b52bcdee168a444320ce83813875b0bae02044a2495ff832f27d633adeabe8b555afc7b3c9ab7fca41e7e703e58b669064ff4868786e9dcc318ea325968c8acee6df577e60dc2e89bfb8289894275e6d83b0621de84fbc7bd57a37712214ab6e6a1b75d0357da0e781df973ffabf6de99fe4a533d8775bef03c263f6b4f42e6d2c4940d927dbd9607033976c750ff0c79fcfb56cfba7084029d5d10c5bba4a996ac8549a893f3d6d2e4cbbd9c50a08d9071a72c7dca1a13c1a9f09ccf2c6bd79d9434a118ef472c8928d8dee07c5fd669e1e660c90d48e04f3ccd9f2a69d28de56dc9c2112bdb0f360818ece45bf707689de7085e5fb54193eb4b45df2f89e48ad035ece6c331145cbadc01c9faed0d29f2357d1301b3cabcd2b1417f25e7f8fbcc563a1cd183fd74efd78ecb89d2d36ac20e8d60ccf7ca9b48f730ba1731472ec2a12cf9a78c1d61d339157456bfd7da3bad474f1839fb0b5a502b91f73af204fc2821972acf23f9ba7771497270934ecd489fcc6a879ad29781def7e684181a1472b1f91156b3b768173bb46b22c132f2fc0c85483ed1682556850a173d1ae447311b45ba4f7c0569666aab122e6c332e124d26e6771efb518e6053acada38619b0d7938c246ca4b8be3c0d4dee6c596bb2d0615d98253344d59e9a087019aa778e2a12a06375fa29c2a01de94ef697c1ad99f80a295d4354266de0978533b526ea2d10c8a1f0c1fad7e9155b7ce0408fda327de5c34d74ff306c3e1eed69dd601dc663ce63043a54cf1d0155a43f7099b87910fce1f98648b0adc44fbfc679ad519ce6db86f406bfac26e5381d5760258185f7507e707dd4a6a24635ddcea24fc8a3feb30f1dc9cdcdedbb87ed24460eaf218edbf1033867f642157dd50d6e8e0e81c45e2fcd84fe56872d2bc3a6bdc39a60360ff3e2fe39e3a205f3fe4e6bc4440b133dc56a9a393d65d57e4b6ef2137ced86d1c71876dc517d83f32cef9ea121b8ae9b12f009be8a618135412a370799b20bfbe73b987c0f360ed8eb382f589c854bc8365185450c9150c10350f60268c9ae241deb330bc3e502c854b4ac8313696061148d6e2243ca1129b8e544b0f7ffd6d45f41ce01de091f453ca1c997613becaf93701241814ae58fbae5aaebecc621708f546ce471c1a06322a4a044285cf5279d3a9aa81b19e8e2b5faf56f804e517be066653af772828578d536fe34b48b8d907e8219c062d0f7e06b0f128a47ee39c7797fcb3aad86b4e0d278fa2754495e483486c3e9678ba2367b1a5682da9580813e26d0cba4033a972ce505668d181a57a5177837628a8e97febbe68531984f4e5eb4395b0bd18cde3817fe9bff4c5311546afab97c193f854f7305da5c2256bcc2da2d053b38c66b425fc917ecab678321a2a508f1368658de57bf0c3c9b591ef211b77c0a49f72ce8c448080306a09a860be7e9f4139fb024098d7e93549f76002f71b4d534dab6c6fe1586b72e7a39b3d639960deb18645d1a812cf2acf3dce55092e124ac459bba083dba8ed0f2a203b38654418ad64aa6cf5339adee16dffb2aac362d97c3b53d1d6a8a8a6a2b3d2e437a5df331845ef42a7fc9d57618992a5f0cee3cfc9b6397924a52395f42d049b57c522fb4925a9b9d847d21eb446d9558d8355f39a597eb2430848b468c74ff890edbab60cc0a832f70882e7eb500eb8aa3309f90cb53daa5379940ce193c4ffe7ab5ac616b45d99108578bd87e29123663f78fa18ef188f04b4cf3312d2b12172079adbf85da3d7f2c83cb2a8b7160f7ed50617a2cc217fc4820f0054cf03b59bcfd9e161ffd62e0f4a06596f8f2c786add34f4b4ded1bac05b6717e8a2af6c5fbe627873d4a8237e8782b109b30245ed2ef8b609711e19884282793c6d776fec1ce57fc02c4349df6ff962f0b4ffd6c5de7b0e7792fc5816d6dc8f135d0a74f7849b97355e62ac9f5496a56d21210c2fcfdc561edaec69f258681c0037ec78683bb36444d95e1116a8253bcd263854c0b07d3ab0ed29dc368d5431d1dbb772ef37785d633ccd9197c4262fd56d6c8d22d31132f438365c1d937fecdb764c5eac818e9b31acafe6ab72716e92c3354f2467d47eaf6ae02f585262d120b8f4017517f67056c9e3a94696880d5b52529fa0899214b918c9db13b6202b66135babf9c9df464faa11d0f7fbd268418ff767a984e4938c6a87ee611f28ee152a774160f8706f33e89ddbd962f21a5752a3c35d874c80efad8df4f51bd75cf143460e1936aadec3fb47f7eaf95635ca8ce39869aa3fbe1be74bb80ece7d8431b66d7482c39a666bb3ac34799f574c9fc7e14ccfb0fe61a4d4df9cdcf4c41ca80455de7d0c5581962b83f48e6d6504b05c04dc2b067bd16a16feebfc126375cef332cc46c7184389bf8caf7ed5aa16af86b3a553955612af64c76e491714877fd085a667b458660088af83bd2087f03b933c58333717502112f3c9bc7ffa4d8e6f024eeb6991c14337f17443429061748837a7848fe3c1f9c5bb3baaa3e69903123fc0751e9dd945d336354bf575c4faa53cdbc69f8e5dccd8f8ba24988410aa431271ac4cdf9b4eb0c60912f823a51aa26c53598b5a54857b8c68bf877826427a57aebd487071d6ae3fa0a8b860860e810e214e6194f66f85a3c3d291503dc1bb4d8390ee0e56a7a561c963dbf309c8d64b309c972daa7be4e6665c9b066e4b9bed1920c4240b05ae847da1e99eb69c26ac731cce60f15cb386acbfdeac5fec01e82e122ceb10b260d83ce7e3bb7aa9fde69107b89a27462a0dc1c105e970c0298ad2294e8af8597a81512ac481eacb36c1fac6d0d1acc87d853ef7f63519df293404da93f78e05e96719f8c149d9de6ce34f8e6a2624039f37f909da46a9cc5b6cf0a5608dc197570222109756c03aac5b8a6514bf0e02cd4fcae9576c4e3b9e84ce950b9f7b18f71fdb3e28eb99f75953edcfd84d491639a79f2d06cf1eba8b4180a81a417de483c8721ee9aa514a1136e968a26c2e41e6bf46707bcd767d4f40afa3b108d13e7a5b104493fbaedecf3d5e87ff0af73c2948b1916b515a7384f5fc22adab9f26227b0e927077044bde0dd83acae1406da07b529c94026dd383e3a460ef82c13dfc853cff51ad6f4c2fe8d65c766eaf6aecb65b3645293b85bf099de54c32ad4118e3bf9366618f24084384e268db0bb7b95d92f5f38f32c4af6b67c56ab4d5dffe8842caa1c4ce74cf615f31997e738e358d1f9500a91c988fc6799d6dddca5464fe3bde027f10a01a2b4e919b17779fdf45fa001f59476f280c1c119753e0047fa1f7188c116c5947b8edfb3a6d11cc9723a1962115daf5958886def2bc71b5db3148541788a71f4637a4b95c22cba7c1b472c419e4607453c08f7b760f85b63a8ce7af98a8702f3a44ee4f53caef1453ddb6b7aaf3549f92558de188eaca4752c6f9b03aa2ec1f466dd12897cbb28fa27d252cd305fce0561f6d5dced5408f53ea7ff28f721f72a278268985bb0da82902c945e4a8c338492197db8a1026481018e26853bc4b5e7e48acd90f6292e2c002138341cd031d9ea3741832d936a3d7c95aff134f2a7c92c8e27409b8ffd76623b88655f346b0af72e363e0c5d277b43344ba2deacd13482cb274aead2573fb6ea098271b4978922f0d76ff20ffd5d2732609926edf50e4b548da5d7564806def7086454d9a2dad8e37661084a8f32af9fc6014d63b15eebd23a4102cac395931a7cd9e67cd4cabd0314c9c23ad1ed6947bbdb608069b5e57f3bce1487f11dc64a2312ce257aef5917e740b381ef5a42c17b30f7f52509218c8ecee2b1f8aac5e86601ee040d0ee9c646d47a5ae431763dff2044e6c598a2f0a83e16905ce8b992c6333a44af9d3b8619dce47a6ece0717ab41ca8ce4786d467c3ad395f9589ec00a7fc3eaaf20e75966bb4d8c784ca9b5e2d1c81438896d4b217483dc9a107728d2e9f767139447c687c025c69d49cd0233daa6d1f87bbcc4142b399bbb15cb9300608dc8637dd6ee09f71b4c6ba43fa02446305c22d1b787a092c3e96e2e41527446d323657f6c6ce309094312c63daa6acff9ab4f0260dfd82fcdc8d428945e3ab675d46c847752536e46dcce0f3149e780111b1419b59b9260c7c26bf31ea9d93923a9f0156eee09c7ec7c33674ec6255fb5dd41c458cef4cf58e50f3037b380b916d2ddf56b3a819e364df93ad415ecd0720c0730d574b7a39f0c831b52c497478e851d82ac071e39157a7ca19d01a2d6ee85386350df71cb5ba4c606dc37247ab0cde00742662fce7ddcf31fa08ed6f4cf1f984467d3c613e841a693c4b97556528d0219707aa185b3772a77c4cf423de0d2056709060cb4f8dc94dd7ca1f57989e4525f9b3bc6ca119fc944881f061d9c360cd9e146c327f60e01415088ac98f4b40b1ddd8ae7b21cc35dfe9d872fa28f5de78d8f166dbdc285d0e5b96599e976702f063b4479cd01b2d16fffab05b6e9ff792429da9d8ba2a54704b481494a495d2eb378fe1521c8056ff473e356801a9add8e794beb95e8762063866c2aba1edf24209a45d6476ac8f93dd822ed604256a720275addc6dbd2c14c7c5143f94f1d613d7dfbc54ad0deb744c03fed84f8079e7a6cb1755d6e6137ef7975f642a4af7ee249c60d79f1572402d296496aa656aca2d5cc73b30be3692ff20bc8569ffa88b57dad578ee2e97ff0909270c6c53011dd20cc8260f6dcc8e77b85ed43ac61e41239c59482d503feebaff5e7aab4531233b1f88bd44145fda1ac7fca3bccbf416c8403dd91abe790936ea3c2cdcaa89b746c73c8eb6de450d2caf09a92f54858e8ebbb101792eb6804d3c504253406209b5a61c4eb55174f80ba97418f2a43d79cf01bbec5511ff359e294d5c68f96b213840af975aea8ea6c77a99319af39fa51536f3666d64737f6a28457bfc6ed0662b402db9c52ce13489290d57a17962be4d9662386f5ee8cd4127f84d90533bf98453412c2b994beb598981e56e74f56eda9fee13e93aed83a5652e9862d83afa9681aeae168bb329526f40f7c3e5082fd40cd6251211bcfc3a41c9fc9f1631bc8d8fdbfdb2afb20b7c0e66b49a052a9076e58fbf3271b9b84591eec0b0aa82d22271593b38ccc2782d345fd6f2aec903d5f9c7600709447a596caebee3feb8b40988f73970b3bec2acb03ffea5bf9acdacc06bbed01f41f20941ab8f7e1461230fc7a087dfad15847f04acbbc32e7512c3bcb1fc096a3eb949492736a0be802a4b916f45cf82607d63e970944d1f834da75bc69a847b6daa4ef552b59fd27987b99e585787b472c86c87fcd3a5cea0dc5367a9aecf9c1a246cd8df82a92fec977c927cd55a143f46ce4cf0a7a5f233ba516fe61ca23ca79959878efb6f731bb459cd847516176b5c92a2f2f9716ac9e329b3c16b94f9f8d266d56440a18a101bcca30656aa1625471a2bd29631541a3af3692c944b69bbe61a0a177f1b970bb9f55436b626e772ef595dc99873387e6258d48713a84896e224fd9a414c37bd48dd257664c73caf37d9d674f67cd4fc0f9959962b20e7fcd44a91f9e52597d673c93abc24d794f654a2ad300032c4788bfd77b12da583afdaf00abd740a6c1de93a5860b2446b790da4a1fe594146ca95b359ff4b6ea16570dff13361219fc07148dbbaecd83edfaac4fc7b8578d126e50391a1d5c19242101662a6436da53f84cd6cc74891ba803967a3bb12ee82d7c5e9ba072201c876baf5c4220d1c923927b1334c608883e8790c6de10eeea3b0d135c6aa0a6658fa2652a1f8a47151572bf6f79d1ba885d0cee25f00d2285888c097ecb2f75864d3a2acbc67bc79ea80729e27c68e5fbd25f4105c484a1a3f437fefe06b7908bf6a5c87117021aabf11b2c4aeded6d71ae9f917185da3141309f4e138f0c8740cb3ed7fdcd8c3365b8b6ce92ee2ff28bdd2b8922103a55c5216856f86cfd954aef4b6cc4b4a03ffca45e46aa8d3ff9400ff4c109e5b1eb720f704ee28f5fdc8b6cc619cfd96e6bb0013bac37a829c872e70c97979fa9a0c2af5e44c9cb5762cc8376dfda18d754636d908e2efd48071d8c9b3bdc5cb64479f7e138de044ea4212a1afe30e2d1311eaad25206e85a7833e0be8b9798ca080606375ee819eb640c526e7c9ec44b371fa9ca4e597f446a224f63bbc1239fda8e7353d2fab27d7b261d7e537bd8d0f6644650b9e95a64c06e80314b22618d21140f048bc55768a29d40e1442c967e443ebbbae30429f9d8c00dffe5ef3587f6a4dccd3a4d789fc5092ad22bab24eb94cce31dc2d46b82fed4993b855dee6147a29cb3e0efe65176ba721f9b816bf9e12a7d9e0c0a2a583f942f5a210d6f20787bb490bc07ed503ea12aa4daa3637a647aa40a0d9fa9c91b3fe207610d027d9a38c9283534c4533f72f8a3586f74c58101bfd09168d89a5915b047b2bfd5ba452ae06c4ec99124d1ef22a7de01bc494dd60cbb3568b86d2492c4c217738d00acc8fd0adc39e4094a69e7dd288df19c742467c68240a63225e5b1a47e6b82e6b0ec99432373c9fb98b53267e09c906650e683afdc56e6dae80cc47a96b2ec09defb9cff1c38e55a85205d543ee95796ea278095be03054287b1c9e40bc7e48f07a7b1fa6f021623554254fc53a10a385bb2168c07465297dbd5cc7f6c07944633f8980434e5605e920845788f38ef29fdc85e590aff6b2f4be3a93efc67f0c67224bcd2c32717ec03a5ca12a49d83297eedbe3d78c3ab9f39de8e2cfd9fc1d4eadc1c15e0d84b2a421f3f88b2edffee8f870cdcbe48bfb12d950990500f1f007f600be2cc5008de8f4ea6b484ace7d3857ba027f68aa5d4373b3c84aa1633a54a089eee62d74d19291eeaa238905580e5975accf0a8ac1efc2f418655097c46829ad7fbbd9b2a4cc8302cf648d133e75e394eb7440426c8eb54bdf5bd5fe67e5c2f1d0150f6e352f715ddd16482b54293c7dc4ae096675f67af07524731b58c13b8bdad7dda296a545c8e860aa554b4b79b77f65a0d11da4d6cfb4632ee99dc2cccbd211054252b01765dba0b64c2cf0c130256d5070f04451343d355c7e0ab08717e5eec0b107153ffafba53cc0fc3547437491b1c2705dbc64e283ca68593f9b3bb2efedf5c078b8c0b242928b98aa49b91da32a87614e63118be9266a89d502d9e8329c7672794718854893e83d959f123a3fe06b48ca6b5c7bf066b5fff4e716447c51134c2f0fed9c16ba34bf7cb3e5ab1243646da3a1305a512c897a6e97a3a1926a6cbcaae71113b39aa8eb12f5b1f6c5ba789a87e4532dcc74b220713eeb3e0097d86891d33cdc31e8a270535201f98817509f64cc648e97a1679517d6e464cace2f0f1b6759ace5e49c745d8cc866912e9bf13b981eb6aa2824ddc2c76caa448f87d9e90d6f9449c610c1d7c9f0e19207514de4e39c2dba295d81e57e180fcb0ded78caacd8f27d424ec1e434ab7a02f2732880599e16d8efc1b0a9a944998172c2d7c45a6950993fc1f45bc496571acaf480c69f94212a86fee1499d71380486eac8c700f50e63ba93d122cdc1d1e50450ee477d96c5078cbe52e702d926fa7f1c7a16b4bb3079272cbbb0965f5e0f6deb3fe257cf2a56462950447397d620d9c5cc0d3b2e53caa9a9eee54aca744c6d019f3962de5930dd31c99f53c3d6885e1323afec6674ccfd2094ddfe8a8f2f79909763558d86df8ea87bebddaf82362b5cf84373f1063fa4979748764c3f492e93f8593d2f3c6666f87ee73d5c03af18c20bf01b0eb6f711af1e483c7306a870c57b2c11f48d4e779ac35bfa6d54121d20e2fdede7b48e95279eef561967e7df91b1e54b31c656b714e817a9cfb78aa11591b49a9c6e61fdbfb8242d7af2c51425080503dc2995d53041dc385be0c7361bc2980dc90d6d8c70416bf499c575e370e980d156628d5d115ad0efc91dcd4340391e3206f30c73367f8983121750d382e4c84c397bc9cd69e7f0b5933eb90d8e347699d4778f474c00af72afc968098e7e46cb477abd26346fb79578b4a0f71c6154258f33d1007dae98694a1c4d25d91c8ebf1ac6a26eea4539782c356ed3a4caa31e5746ac46fdcb758dc1c0d010fd775852be0d0d41d88c694a3fd0806e28c50b0a5f0d794c857f25be7c0e7bcf8ae1bfecbd854e99e7590e99f15ea63e8f0c147033affde35e3d8fb1d11db9274c754f02e603e0a15fede48fe86c2780c25208ab8391780f0328f1775daa6991bb3c2051b8004cc0654d72d54e829fa6c5f66412c51566d0863dd14669c5a21a6f181ea7c056f6c63deb8666743d971eb898e49f4841e4ce3fda54a18ada4650db0699395cafa4a29e712de817b3d8b3172d7b932df50c8db9e769731c04d60f02a11cf078085b5083a1539eb20f840227f595eb11e5f69e95ea04803a5f42dbf91a43c1d31cdaac4e4fc88a8e98dac0e7c6bd4734b3757f055d1e391053a6a9ad66fdb3a263d6d7286a97455328e48c1d32581b784a406017be568cce5001e7956e119fbc5e6b634befa4d8aa4b8333be65acd43373e7cea6efc2930ae1a2827044b3643d3744f6f2f04adcd93bd0123a235c2f94cfd694c6a59098574f711b7bf3e45acc28a035d7d96b1f3f74030ac81f16163d8239976430e468d10f33b6987e1f8cf9b8b2d2fb056c5ac6de367af68490c082cf45756e55b1b45a1ca7ca1295676e7133cb0a3a9c588cf21aca53aab62795f65e3f1e6cbde43db134728da8cf54c408dd1c40ceaf6e0b6895b9635c9c3068d5b29d06c46b85ae8a5f39a7a9d4084fb14ba8cc2ab524130ce8f2b1f3217d5a91a16f2b31584caf62ae597e9885d88447a560c31e5a41982b0b7471622fb60c4392ff3ee6f04caba3a7d443beedf2c0a392597ef6d93fd619760b1ae970e4c05723dd6654606e99fdcd609db9df04c1ec9bbc7212ef2f3407ece4b959fa38f98461c7ada1185594ff6d73e569c00ed018f89435e6b67556c7c6f46cfb4d4429a8890a38e14d6bc885a43f48fabd87b89dc2493d2ed05c8f65bfee62a9466c397a93c1a4794b1fec40887627acf7db78a8b0b0a69b05638400ecbc2f16ac8a7a89a38060c346c50edba8640069f39cd3528a03a89ab156e04679f252a249e1d9a9d4c06e0c5e7c05631e2eca5ff87823bfabbe7fe52144b6604cf736a900910bdd98720a7099129d72b1da3e60001f3c649e48af8df812b71f91a6be4cc89c88abd93c0e1622ac52047dc3a3d96a95e51b74fff10d8a2a8ecc38b73b92c7c56586296ed32ce8ba518fb0ecb00668ffb51fea58903d23449e742b38a876a951a937943f78534d280518633d2f741697f36c188f1be5c13c415faf00861d0610c9735cd577893c36fb460e4082ec29eb97572ea1c66994886ea5d8acc3a2a6dbdc1d5a65a9a4083b659847e848360a587f0543d469ff7224fbe2c9d7c15972b0ee9af96df885ba5a0141cd8b83a4eb58f950e9278a65039ea7b57f180cdfb6d44c767401adab4ec7e69552abb1ccb6a1210decbc3b33ad9541011943af3120b431bdd4f49009e2ab9a1c0f20c78d488e697cae176baa5f0c59038b5e7493a389c12c96b853ac6bcfe573548f9281aa6930c204debfe0fa79934aa5b1e5d55ccb89028915a5c16ee23f99122dbed996e0d5f91fba26c3f9c86c1e903ca08a6e22b0ec53f5101ba928bf6ab2bf6050c6224836ff2cabe4a31156e3add35dfab4dea074f1154043180c3089cb5e44cb648373c0a879ba527741a2fa0cf203ace00dcf9d4212f2893bd654b777490f5ee3fd97aa449a1872e74eba9540e3346d5a400580cdd1a5dec9a95b46eaea7306467ec5c4dbee8e038a64255903f25b2ffffc93562b88bf367edef4c5b7c2c4bcbdddace29020bb16578b49ce08d10e7eebdec169b9230688de74823c7535183dd5223ca9f07bee94a1ee70a8bbc59079afe7a39e2afd45c4bd2a9dd01ebfb63293cc88d1889806330414e01c1ef63078ea5314b0600e9d6a98a6ff7f6cf729430544eb253bfa54f1bcd79f282d181ccea84611697cb84208cd6d1b52fb1ae971865b7b8c48501af561892c61e7e54bb0818cce9b9b3a3462f3457958b6547ddcf4b0c0d20b9fc811203abf4d251ebec044e190e6f13929ed45393b8a45134830ae51c9cf039b0174523903802dfbe66e49c9670a57be6b576698b3295ddbdaae3b72db1f8ac941ceb923ed088992ded12fe1ea7b30403bf787353fb9161b6d7d8e54360ee888910ec95f33edd4c377eccad06b69af923288ca5c7d79b3c45bd69fc4dd5eedb85363fe7da226d29ad788b96691b76e67110d3476dac8550f8e0ba89854b9b20811432b32e4389b360e0d42eefb00c4f02eea257ba07ed9c88cf160f422377644064b0d36dca00db3c554dc71d5c4cb033f61b56332d412106a51891649bc7949378d0f8c891fff05fe9d82499a7d7cc57cc4bdd145b00b6ca18cecd99a63a08e3bcd8a6c18b4a991ab0763b7ed1d6d15d2dab91c773f386c91faf4fe1cd9eb9a53750bc68acfd27c7dd4f6616f846a8a742d11f63d00cd334109d3cb1081ca84b60af40f7a7407b2845caa39a2c2195249af96cfb45e64c18e3ace52668476c2f55b27e54c30a8b9642dade11fb5b96a44d9aa30c48608c9c4e09bbf9a830b98aed2eb60c0af43d434d5e0e399509c7e59777eb738023e0f7ac86ee9d5ab360e5d85e629a4fa8c2cf137cb4fb0fd71480d8a3a12060880c63c392df7b769d7647bdc9be83d3ecb4558fa32215412282e8aeb1dca2d994d2c7d03a2ed495552932487e7997035aac6118cb7532d704aecf9d768737b436d3f11649cf6e81c79731518c2bd328e11aa2b4d9fda0784c17013145e331eee6c6297f42a4e0da4f0d00ec2ddfb2d7a967158d59504a62eaa93a8d98684099ca2bd6c4043df1e4d01228065cccffac2c0d1180a93e152a9616a7dc703696fd68ea57334844da35bd08dcc56941ca7370fd0665ece6aa2670edee82a7c7fea21ac4f9164aa3b636fe85dc3dcf9eaf27fa14f5b13ce879623e2541dc6294b25d8da3383a9534524ed83fd07c208e1669005239dc596083822cb6f6b94a78accb49ae0163bc9c4ac9b0a036ae375f1d6b141fde9296be99bb3b30f528e366d45ac4239d9fcaf0f201d077d719defbac362b69b920279ed8972e2f67d49aa2cbcb7ebe57c1948b8ad0651baa35aee870a9d7f134ab47ba8a77e90201eccf3303db5fa95c760bd0cb4792e4b1d764f2501fb9a9bdc642f4846ac90a122c40c8c8be03d9b75893e6bc9121abeeaad993f01911ce3ec8b864a3cafc88296bfe13a4a7043914946f3e19fb6985c7dc69d0115ce137334a052ac5a162c15dbc242b6574d5ceb5dbe759dbfe22e2cae3d492ea9c5b6e1716023d94a7064219a0f4a1c8552c4083394a63c9facf19423066aa3fdb30bc900cd0ef5b87285397c8c54c93300a4b5223eab08cdc7cef4eb3bb74c62e70f8a069b802269bc7bcc0e9dad927902c93a6353663fb7b0469953e9433cbfa07895535496884d738da47fbcade82b67b267c6edac5bceb58b2bdecfa356e4047ccdd93a74165671531bafc6154d0346de3c4d75b13f6e816bffc5807abaab7d6d1b63129c333fd9bf7fc55681d0c68ff83d0886f59f1d04ae189a35e57df3f11197f715449b23238a4c6ca1bff27e04455b6455d0720bf872b069d7b97e5ce890d4addf9d25d7c9464f95cf26caaf4d36e9ea2abeafb09212a1f57ac1dfb02a74e32a9cf90ad0235def24793725f1b04665f7ff83ba530f1f386d0acdcf67bb8b7d73d761e202f59f0463c5e42f25dbc3f6026c00f1039f95f9094744b71037280849cb9b8db1a506a7fa1786aac272f7088ba50daefe26aadc2ca598b65d79e0bc5923fdf13af0d7dd389f30e49505178153a658dbd3089e36bd8635f2c15ffecf7638acedaea439085217c15cc1c4263b980634478e8ac797ca47a7a6c7f835bb32c2028c892e1eec7f20837fd957408d49e18d26b0cd9e44ad2c37a70ef41f950960b7f51d45b832ed02a1b0da023a9edf7fa5d10d270787d28cdde0a29cb4e4dfccb789d81c61d8a23baa4439214bea14d51c6ebcea1bb2c0f0d5755217fa9caf9db2b20ee3ed670007dfe9a4f760ae6e956dbc96c4d65c812d7ce6f74f30ff41382948e35cdb469c845512e524bb421bbc7b071cd2312d01e4b6f8684f5b284efdf81973b9c85670cb65d831bb033deb35ca3668238ce2f37e0329e70b6a327d6110fe694eb70c25ba416cca18f2f7e6accb9ee406b4c92c0e1f7da26f4076a0692b6975155996ec543edf007bc2371d3e80d00d054228ef6249db09ace86318058e7b285a28ed0c2dc15012fccf6357b5aea161c6249417c9b32a1cd1b85a5917194dd1712ab0599dd18dc21474f94ede6aaa001856b7ad80be05b556e08accbd558899c87b000ab9cda7d6abe36e89b69429fe2748f3e06d0e784180b9f80d73763a98c483517d433be536a47af46bee7f6176fd6a7865a370ee3f873d6d89a9d33ca8ca31506f850baeac1d5940439df9b45d3c9bc89b47e22e8688b8e85d329151ad80abe5e7b76a24ebbfc80a5aa55d063af41d55fa3339c4188f7dc65b3348e8718d0cc1c3d61c95ef471cc80c7a56efd61c61f12d7fdcafb1f84a671e3db04444b82aae9b5aa9cf1e74ca589aab532a504e9e5a8365fd895dc3c15745007a2073d143937c9195570dfc5d64f023e4fadd12442e8f1c11e1c5d3a833da959b5b26efe3c3ce84a8e0214089cf81d1021a725565fd9cff49634b488f1eced1299f956dda0fd0b41b19c7e4a10377bbe02c0b330ccce15f37b88f49d2d7e26837f3be8f4a8a1a977a138146aaa30a8b5931601c28e80b8f45df7dcd3038910f7b4f8f049d88d90a706afe3022c7091e0d32f0dea9bfaf522a7b7e04da331908cc8d02bdc82e08a8d0e6b827be8e4dcd2442797d029c708c0f5d5cfb127fb97d1f11e87c2f535d2a258ea84672e06e6cfa4fc4e84fcb173779324d9d13d858e2cd058665d694dc405c58bce51986714cc8d6aeddf9312ba33c982411754abdaccdde2aa904e69606cbe9d33e2f32b12ab981d613326bd829b7d48bf0d10761294372dbd0c3f364f41449abccfd2065b3beaf690ff5e19dc3ed9882620621272a526572661afcb867a26ada843ffb1e33476c7d55c04e14173058b7ad64033d9790742c33d28400a6581e405a3b5cbcb94357ff6b6cd08749b9c37183e86d416c83a4b92f653dd8b886fa89d515b946ba8412beed4b3805eec7adcd230682a35584475571df2cdf459cd227c84beed98d6481d4db0dc1f98bc417b5fd165923679110d309d5b5c9c8a69036f19d90becc83219c9750859cc604e0c4d3dba5af93628d34684bcd26d628b46d85991ad73a9722df2857e29e956132d33f8a3c9ee960a578eef4f39c1cd6901fc999adcb2649683b5053349e51acfdc2dc6f2fa91895aad7a89deeae7c962b8062e8e110fbfe59bd93d184dde58bf1a0021d07ae2c24b85583b934003b27ab2fd6f2589f6320af663bfbf99b6781891470f6cd0ac1b27627c00b6813de214057eb3255051967322323e1055f4e6cef04a145fca6b4bb0150f9bb481fc3dc9ae1f727d2a2314996456ee2da0d487c2969876a7fde66fcbffe1909600446ac77cf0e27dfdab1d6cf5a20ff0188d01fa74c5ffbb19ef3bb1198cf111390eade0bbca6dbab62c157ad226251845996ffb7a641dcf0ab44a1240cee1ce21eb371c27544de92c970b8d5c641c1488a7140a9e2ea9bfd0f7eeea7f7ac8cc5d03ddb86ab08541588377e238fbe1586a3c820fa0b962c438b4650c043f7fccf1d4371cfaa7593cbf422e0fc8405ae09a97602d1891abb207e4d43ed468af79341df2f0c015814160bbd715ba1b903af2b8622c4cce560d08ee552c42612a9ad8d922967c8f37a6894647d25cf0e95e5297f1795a56f306e3c075e1bfc3fef512b8eba6bfb739a94d6965551fda52ffae4ce1545a78848ec5b2f5b1512027f2e2907d169535a71c378446e85c1ec0e6d89b127920f02a506f8c8164932df81866027b48d9b479b22348c9eb2c5c14fe0a44c31a2beee926d4aa55e6526ff410f624a19730865fbe0a85d2be7b5e8aad0756f34898a43090cad84b08b826f30107072b99ac0fb4bc0157e645315d66bcd01e230c2125db3a32d34ed9b8224a4ad04f0a9716dba42bb94484aa63095efd5be2e2538ff64b54895b25584ac636651779535eed756a0b7b730a16b732146a4cd487aae5f0882435d70f86772d4d8b6e18afa22c545856920d366fdec84ffb203a65d2bd37a95ac81b27b81996a21ca1f4f5a1f5f42f7a4abd6dcaf727e69251214d711473382cee0b1e820584b155edbb9287729037f130a69026934503d76aba91798e0bdd50bbf2bbd0dc6fcd5becaed19e7d776308d1c8680cb586ff769e1125691858ea876468e98fbe90e7ba6397ca179f4bfbfc09e5e23250b795a21d17d4318b6de2cbe8c2fbaca99bac47ec99adbaf3d629c9f53129a32a7c07260d95458833f0c3b13ec3bc179b2618e33eee44fc7405434f81773148e5c87df7f83e80516ec9d282210ab5f83f5a1f99bbbb14e2ccc73e73b267c72929cdfd990da94ae6daacae154ebe95a7be1cdbcacc3527488b47f6e0396e5371eb2d21a009c304b34788d66ef70d87378bdb00f66349a9ba8aef3718e40f7e62477c695a3f5424af12e8b1e15df1f276df403f3395f09d3aed0840c010c636382dfb479a7c05eb5a489d6dda6120b867cfaac1e26553d7c3bfc2b9dfe5878af89a7f6088221cbf8c7444efc1954e338a156ded8d672b5b0b952e838bdbf4f827c87431cfd0cb0aef7ee9e0142cf828676e86c92d477011e8fca0323ebe3f2dda8f25fb95a3fec3c8f2be062a9e1b133fd97fd648d22b2986e15a7c628f30dd923c9564c0fdcb17f009b86626e3f8901cb8a51e225e4d5921f3d12f958e48418df3da10534919ce53eabc932e6f76c3fbebedd8dd1c8a1cba28278b7dc5420b2357b7fd0b8d4d2caddf5697e7a842b4e2f3811129d030e2322ffd203f7c9eea4b92df96ce48982de866760da9ea06fffaddec9d74b079d04fad9bcadfa09ed3b</script>  <div class="hbe hbe-content">    <div class="hbe hbe-input hbe-input-default">      <input class="hbe hbe-input-field hbe-input-field-default" type="password" id="hbePass">      <label class="hbe hbe-input-label hbe-input-label-default" for="hbePass">        <span class="hbe hbe-input-label-content hbe-input-label-content-default">Hey, password is required here.</span>      </label>    </div>  </div></div><script data-pjax src="/lib/hbe.js"></script><link href="/css/hbe.style.css" rel="stylesheet" type="text/css">]]></content>
    
    
    <summary type="html">Here&#39;s something encrypted, password is required to continue reading.</summary>
    
    
    
    <category term="随笔" scheme="https://blog.felicx.eu.org/categories/%E9%9A%8F%E7%AC%94/"/>
    
    
    <category term="年度总结" scheme="https://blog.felicx.eu.org/tags/%E5%B9%B4%E5%BA%A6%E6%80%BB%E7%BB%93/"/>
    
  </entry>
  
  <entry>
    <title>2024年度总结</title>
    <link href="https://blog.felicx.eu.org/3268380856.html"/>
    <id>https://blog.felicx.eu.org/3268380856.html</id>
    <published>2024-12-30T12:00:00.000Z</published>
    <updated>2025-01-11T08:37:58.000Z</updated>
    
    <content type="html"><![CDATA[<div class="hbe hbe-container" id="hexo-blog-encrypt" data-wpm="Oh, this is an invalid password. Check and try again, please." data-whm="OOPS, these decrypted content may changed, but you can still have a look.">  <script id="hbeData" type="hbeData" data-hmacdigest="12b2ac00ada45562183accab28de2823cd059a975f676c3c7f98e3b518a06859">c8f9345acf8a8db129dc4f70a76800da97666e865e48b375463d4f6051c53443be7235fac1de6da59c75d6a8caec235f37e4605222c7ad93c59f67d9fba704c84f9acba59137e1c27bd9d31dcbe65570d4e5d96e21b0f9e368e45b04b7d004a7a49da4325f5bdab5b5cf006f69592da865753029ccc3cc4adc2c4f31ffe85a53fb223fcea56eda89e3b442f2b36c143127c0a2afbfa5518a87e804f2f54ada301995b8aa11ab989c066336f13fefc9ea4ba597a8b79dca913dd5d8264a6addbfdf42ea8ce982aa0a97f555b75859456a7f66a9eab0bba731c3a83699444c1a7227ab003b26d15bd287755276ae1d23d5ea238edbcc5c9cdc5eb5856cf118de85c0d6ef62d86e7ef4c908fb6f42becf6c8dc5613a511491a470268ba185316e816905c9dec75c4235c656d3877488ceec21cc9fda04bfac42347559d877b774e67fc937c81e523909875229b87ad3cfc7dbb08b62baa55d96a07289f0800f0fdd16036297fa0a2c9aa778e6b3d1444627fb44474d607ba5616917ef06b9d976a90f756022d7f9dd503880c7168b3e3225ddd5ef7f2aeaf88c06c70cc052d53817eb6c2a78ef7c0d55b3b1db9ba9c54c40ec019b9565ad0548d136224d46f6e51441ffca13c3cea5313f87b4a78c49cb6e535d18e14bfe58740e18dbd5d8602340d1f5255ec8098b388a79f13fdbdab87ebe94cd808a72d831100da55154db8c4005fc04cff7522985146598d7587c9addadf56adf21c7e9b4d0e7cb4a099d39bb13ed18e01a0394b972dccecd62dbb1188124fc43c36c5613d78ac9466edda1662a8983cc8cc4aae3d163bd2537d2a958052468c161d009117fc3b75fd8482a681f8f3eb2aaf89c3caaefcd5e60680b9b730eefbdd30a158965d4195969f5c4675b996f719de7dc55c3ca962e03cb0317f9bb71246bd43b9a0cf9a9b0a6d2f812d2901707f93a8445409792e0cc39ef11a56198e2900b3f7f0dbdc504f16a54603a13383225e6d0252d861ab56eb3c859355815456744b632c744170dfb467993f99f1247bd69bd42aee0fe0944bacef139aeb4f6df13ab1011268a1a2df954d10e729b3f33bb5313d819ff8300b8e5cf508eafaae5153fe644f12ae93348fdacd1363ab53758933925f3d6ffd51175f860cb9a7a367a855f7d4aa99e9950ce9f9b0a148fa191d0c622e1464998e2ab936a17d5409609cce34a93bf058b4ccf729c80dc91f1625fd829672ca27cfba9c40b9281cc24fe7272f629825dd7043d2124e902f0ea32a02a364da88b80b6bef1edc65c3d9c6e146c8fa05c694fac3144a3edcbb9a61406da2457779f7eb5d8b1126eaff058b928591da50d24b5e79a14439a74b71dab6a6dc905198fc9e5746f6b4ad7cf670e6fec5ad03469ab7828cb6a9ad4b704e47601f3e78432d440f62bf61bc7673e92c0b6d3c7bf2340345a91ed0d7bfc39be650ad1e05cf99702c3760aeb1dc8334c721e916b300944c12f6c974f2eeeea0dec10ab2d11bb28666b42df0776c11bda50f755ea8aa6aa99210b8f8b91bead1087bf1a54b1e91fd2a21e1411546f9e8d846379b82b972ac2f80a6fa1fb0d9f55d67eb20003b983b544774b240d993610951660aa4796250937196596524bd62bd6817d2a807e09133e316b608978ad56b9691135ffdcbe6845300be1714102c098b6b6d9592367f42cc50e2e45f2e6e892f38606343d535e8bd99d342bdbbf1473c7b5d63ed3fb9a51f0923e703be2aa73450ca2e3c72adcc24d6f4601b827e0ff80a7d7140780a7ed385ee0d6c1fa906cdfb155f2be4be59314129b5c2d9c90df8df927ed489efd011bd4536ab7fd9b2c4d515e3049fea20fae9880914ef2da90c4da11ffd40b824aafa8959b81a8f95d14e148a3692424677579694c948244c346a7b0a27f65e4ff50770b5c54e9e222490c0d0e2601354665ecac0c7696992319c98dd7bc70d483eb5ba94a8a35f1817795c095c1e9fce7ea881ba4f194947c9aacfcc25463970eb610bedb512e58eb82fccbc9a88a3383a623a2bc1368fca5803a683647db3cc9b569808afde2197a97b7eaf601ab090451461b60b58341afeec38edb53c52627314ab8ed086932f495f09e12c413830593b8e099d273b44c12a76682548b861de3f33ca5a3e899d4b186802dcfb402304775f153a591f1ece322cade317336ada0940f8a065fb1ec84f52cf99a8cb57114a005e8b1dae2f1b3b40bb014cd5e769c166954ddf7eabff3fdb9145939aef1451c978d5c04170398f43c3753feb3bf224d3a967251aceeaf717b7d76ee201dc2757cc032d2b0ff6b4435061a767884a373ba15e5afc1361b60d1177a2982d9e884614f7eef3e8fa3275e3956f727a616037aa95abbf2c7e445050e4a108529deeae0ccbb8ecd5dcb680162d0e41da74f9eefc15464f70a60fbb48495bb2f9ea948b157a9c98d52cdf6dc61bf10f51c52d0d366a40153c55d05d3e8df5845729e5583e2f9046a98b96c0514b88402081ed9ffd368add6d48cfd3f394308d04e2a55a2fb582133e0c066dd30c6ff492348126418ff30d9953dbbe44e6bf8429b101ab9b250f99921a607e2756abc15e13bde50d453e477670f46e02184f37b359411fb28bf9a64828682c3fe560592da28fccadfa8275b59adb6b1dca8522463bd83f6d9726ee028bad8b25506471d7410df09054a911a8690785058e8cb519d2125fdab66df04f9d7dc8723688c10ca090ed289f9c07272bb5efc376430eb9939cd3997b9eadffec8b9abd057e9afb66134662d30eca2c8ea80f426bbaa88b3808f7cf1da67506b511b62026d2662316133ca6894a7e6d95b265ea95116b91a101e64282e96b85deed7c3d8c5a7ecdda9084f731cfc981a9591a2992acd779c413ffa3433f9fae429c2c2aab7fc4a4649227e7f675d9a74725200fee7abb6a15501521f59188dbfbdc3cacb957a148182cf01bbf7ce8082b4ebd2f8f4b5abc888f659d5b385da506210834b89e760f560e97bf5cefe2fe955995a5a65e38449cc9e685e1619df1bb2df24ed670f874bdfb84124792bede96abb0ea985da32159ceb52044fb23e1fb580d2cdba8ca6c59febc4fa3f5d359cf33f529d011496805431e37803c5b40e36aea58963894a95c8ce0df84ac850d0f8f89889cdf0d29f5151634ea3eac24d5b75e8964a6748d8eb6c055313e48d693420582f81c70d0222e7844f8638378e3f8852ac6148aa88b7379371d65f128172f1aa8a5bca64f0806114556ef906fb39329eda8e1ef3d9ea53e187b212780c6e27e5284ac8eb93a6834093d51da388ccaa9010518da6e1741bd56b126e5b2e7236a9a86f8874ab529fd048fc04153fad6f4fad6bd506849efb3fe4faf930e51b1225dc4be79b818eac00171d97b7474cd255ab0affe51f3f42a9a0e1a5181a487578fb840ddb4f9be61c7fee43596798f252198bc3f15d828768b117e46f4438a718fcc5202627a86104810b30d0b80eeb07dc41f6b2ffaf870b0611a4c745a264357dcdb165fedc444442ccda749812e05cf70b731c245789478ef9e55b8ddf36d9a99eb2fe1fcf4bd4fe39803372bbb3f4d5be4427b3678986f6e44599f31de25800e6c29ef5b1e8fad0ef1518e6f85900340b97f6ebc68a7ff0a0d53df7059c79fb1d57db1e0a34d73b994c9446b683536c892c49ba45832629f46b6a8a21cf6c2cb90373646854c7bd31dff9656ef9b0260153207cdc857d1a41165615e6bf13732ff6632b6109f921f177ce1c80a7974c2204d1e579241c2eaf0c7e263e4f8ca967f7b46e90d05cf706774ba870a7cf8679a99c22d0e6b9693d632bae9d9cf096d6097149d2699ae8d6975d6b44dd599cca032af6f3efc093f2a81cda215126407a3f9ddb453ab2cc62cbd6c5b2b987e6a59e81a779369bfd6a8ba4784cac619b4bf38152c1dc52a5f82efdcc7be25af159f2c42621e33159710518f0475c84a60e91f38c1b87f4642c9d57618f346dbae267ff4d6a3dcadf13480aa85f4626f2675ffd1016bc0358aba86dafb860c05c77a2ca709ab39233d6a4d0d697a204e30798976450c2d6c34e24c0b1d305c4325fee9171e9b2807b6cf563a4ca218de9aad01021b88cdac185125df0dce8b513f76cd026c355a1950773b4e62aee64e558f14122b31e9216000fc01d57039f88632afc99f6953af11d5f1dc9da4b431d1bae332108f50aa715d2f46bcce40d6fa955d3d0d9659840848920def8ee32226c821747da575fffd6e66b6f826772ed349f3be464ee0cd16c803f942fc865e20b61465f55522fb5e63ec6c498c669dff88a0a312b49d1b0ab3f31850517003a3533e9c9a0cd5d6f8b995d2023288998a5ae211b4a746a80d846f9519cbae9d1c4e742af89eef67d01ddbf0dcb1e8eeef8c24e258fc546a07aed8510ccf2e6045613e26d2cc20c3bb7bc62c77208c184a097e6ef1afe985bdc86891b1ef8e47d3f336745de3ee42ce83ac9b4edbd26e10025a1d6c2d4046054ee48139afef0cf8e2284b6fe84e8897b9b193f230b7f6d1d0545623de5a827cee59043b0cc45cfa4f6d04fd3cd544295e2e0d8758034ab686869779234742adac1b931bd76a9b95e09b8165199f0f0e365e1da96faff9b24bf8a5deac1f4f234d35c42f296acd3e7a4b3254247a3f3da8bbb3612816a70540e8fa8f92f28849e758e47b6c2d3faba0af88ffa38fdfd2b03e0f97307a80fd67de75038dc646f504ee1da800e4bcc89dc1a67df61a8289cfc47baac704af4c98638be9ec717126ee11eb84c9259da51a305e3894bd990f9c29f09d610d7685a344cb83993c83e77e1eb9385ea45a1a6707d8ae0edccec4e019f8ec233fc2e768f6d734a4f584dcf1d8f7af60a1f91edc920fc9b087217c1cbae169d8509df5af4acf184d7407f736feafbc5213e38c66c57742c563a3622e9867617c21ff2611390fc7d9b0f930cb6a6a3ae9cfe7e5f31b8b4bba327bf57a2b12701744c4182238efcee4275dbb90f12ee103d83b45e39308a6c63c5f48c069bf32179b5c3851a1d42f752111db739b3b5fdcae558b523306dee6db59e599752b3d706ac87b21953b55349a8ea3bd38b8007331273027c7a76228b11e1bc729591d28ea8ab1f51086af143b3081a74d1d0259541f22757617d37d28229a9b8222d9e276dcb30b2d6d00bc34c0e0f009bb3baa66c66a13766076aa79815567d57048b30e59db6fb6cf1e52543a55720762c4cd97df5141317d4dedd5ea78c60284472851f986efd2ba3b5f80e263e95d81c8bd7d9f0b3fb3981a2a9f72d67442b185df87a97d2fbdbf3d123766dc238b742d0362ef9f60f51b0e5ba1190c2fdc2bb6e794af271d1f88ea6f6fbf1e35d988b67615e08686022e9f0f8904d41943ba312d7097ab4e10797bd340ce10a63e3bd3b1969f8c1e104055f6f476f46f24565222472c01b5f7914a4ec3197db4c9cba3c30e1dc0696257866333076d2a150602db9204f5ce87a28ac3a5891f087d3e053e9599c6f6e7b62fca58caf475d56fc518bb35ec52dbbac2d78190e49ab776bffad3bc7d79465db81b1df90f4d638f44aba2ca3c7f2490a2e3fb1a6218f8d8c37edc1f5cc671fdee0e93771f96bf398f8930b80ec7fe2b743e0bd232ed9ac6c95dfe39600ea3de700fc9784767e7aa73badf5c2eb78afbb43c0cedd7e184fd3f0795991e9295f033f05a61d4e53f484c02bb41bb2499cd642a88864ee9c1016177b4b763659dfc4b864ace92a55b327eb81f3064c3945e1392f0addeb9e0410b9dc85fd664f70887bbf1f3296f884b5023480654148a309ec988c21de6c6df348f5f1d407158191598262b7ce9329f14e19d6a97b4c59038dc9e8ad2915c4c744d87d831a2137ba697703c2589dcde6f26cec00bf3e2bbc7333d5ce391f50f5719c29b708d803e4cf14d3a2d05da2047c4af7f1f35dd3bd40e007f3d0fdb9d71add09a3a7e37878a2119a2072d1ffd3af92a15b49419164ab8303619429739d34292ef341d4c41bedd6fa450230180892adac365b8d43633960ac31b0c60b25f84264a101341b8eaa2c023ce3c1b719e6d830b9c20a370883f65a26de5dbe8bce9193c4fae46ce248e61ecc75eb6c55681befbf05d04de5d335ba99d8a0f577adfe7af9316594c9a86baf7d73f0a413bd303054b1cd7822efade6c3ef094e3b106f20fbc42ccb3fabb6d4c9cecb724ee322fa7d11d0d6dbb5a56dd2dcb137e52b03bbfe43ad7277614ab44df9bae3e02ec2a5b0af9aa21785acb86b0e4637536a5a8b09ead7d08f166e4067273fe9f61f1ed2c1b611e91cae05b1b9b595578c543749f43d5f74cf15892d172391dfe6addc0e64b42d8a48bdf9644e0e8f3a86333d7284951e3376d71d7ddf55745795acd7dfd9e3b9012f4914bda779636e135964e2d5d10cce9da53726cc9364a67ab0b225bc1ff67d853ffa9f6354794e892286cceab5e64fbddf5c680ad6db6821d2374a82832429253a02b3e6e568d731f822df46065d1471c1789ee170a43ae49ee6aa22a0a3c374e71357b8e33c53872190052f960a33f7c57877b5b2823a768b6a7dc1e9ab6a4f0362f8e4e5bd076ff39b1fa553a196d32b658fb1f8b7eb1800593300991971cc53acbe62e4ca69f345a59d44cde6beb1991d92cccc754ef8fef267fed5efe17b0ac8357679ce82bbcfb1446061e2d317a6740ab25d4cbfab52c6fe1a96657941a17c52c7b78e244fa25423c90409de3fcbdfaff3c5327242a5bcb4861ff7658b36fc954f3c2409edfaf46612f79580a3ae0289097c304735537266a43080cc621ce89fb704a4042e3ca2d587fe7b3a7ccc729f1e89779022656d4e4de7ccbf834af7bc41b1d4dec82728f9aeea36c202de35ce221d3678bad9f40940e32d15a82cdf0065383f2043bfa83665717bf9c6cdf4f224a34920578a953e0987e75e19e90abf8ca9dadb104901bc336e5e8d448ac873231575847869a755104b83f286f63d16c16e8e5c106d6ff3865904e72d246a0d6225b8a8980b899e107646750e10741c2a884d884f4adfe8653fcc7556c662a9b4ca8be9e94556a6f6713c91fed818a134a4c9e9bf6a2ec815199850232e4dc2529e9b5bcd035dfdd287a64d5faa07c5674c6e4b3ac8bbc9427d69fc8274f7d87b81c830abc10c9f48b893e65c9a08470b7722d7032f661288739a6afef627c7730a52847e04dfd89f28476e708fe82641299df4264c09f068992af1e05e6f0045b23b180a996951cecc1ccb28afebf424fc3032dfd9a23298c014ecbdf467f4250ed83d5dd19f733576ac1c6d034a2b28d08c7713d890dbde83c211bb4af8768dd55f6f3e09462b615d2dfc2b5a9dd3fc62f859b5100370062c4d5f0d10fd857a43d9c99aeb1c474ec059630c844b71b138de9d0fffa72df878ad745c16c70270b0c77c6574abffc2efde62fc23100741b4c9c8677078f9a015c4b3b6a34aa54f8c39ca8f69d5db9757f677eb2ecbaba26c4cd1db0e9f18d32a310a2e50ffee9314b34ce0e790f474654148addf0df6c5a4006d6479fc2fb1bbd02d21907babe2048cce978befc578de40a50a3168d17729a65563de5d1b8bb35677d7342e72aefa2e2c66124a9fc03c3f4743ed3264dd2d2710b7f4e69772f4ddf3c6dbff1bc9df9821e885ffc6713e9e7ef5946f9588654b4c98b699cbd2f258b0ae5e436e46548bf2434322ad40783f5f23424585cd6090fad582b8faf5b65be73b1053b7f5917b334af345be5de5ad1b113e895e285cbb858f9f9cec2c97d286afa30524dc9165b60a79f22df9370a4281af57ffb13fe16a3ad9fe549eabc0113200dbe2e4d3a1030129caae6eb3aa0a593b5e6a57da5904f256512fbc79f6d760b230b1e855ab9c2e3c02a3230752f7486f3fda7e0599f31e3d8b74e13c9a1321f98773c5c059b738d6465ec59ecfc1be791b9c0c6e2ba606243295d7b47e7639b43e5bf69fc326373762a4729d5317638bebad6c384bd3ddb09540a4a767ef9813fe0e481847b760c6f1d037e1faa9f014eae157b486a19b849518e0fa84d98553bd5391af8d8a64b2b4a43cd267039069a70946489a6d037b3895d197ccb7968a3e7ad584f5305c7ebf8229e3fa3b4cbb1ef7b3adf081174e180146a24fa19b8574cb326d6af67cd7503a266229be869274f1e6f62a19d87d3d1e0cd705becedabd9908b1b4b541dd8fd2d7367ea4fa43a69649e32459c7ceb6f3df7b0fa41b0beb38b282450a433a16f64dc8ba165b04a11e1814bab1a33c6003d65c5e153da2aaea995a27bd2e88433a2b423abc4cc6913471e5b27ccf6b62a80aeee2df3a58049cf52975ba2c7de8c6a5dacba011a38db9c357c8c375a1f1cfa60697cc85d7b018e39a4d313f472353e9fa3587358e044ef1284464e0ec97ef8eb610dda44c51214e9fe7e73812c10696a4b70c2800b36c9d83ab48a86e56d02d1dafc413bdb424e991e314dae8fd87f946cd7590014491a1b3ce3ac0d8d3d4b84ef2fbfda397799842fd9c4fd7fca404ca11e88d965ba111d10d3d6c3abca240f613ef5e712a52e25a15ad0de6647a12f4d0371642e2810e98c0a8d51914f686cf87869e04d3434286de1d4c2e807f5e40b66673eda24e7f0a0bdb84836f0cf9825e24c052c21ce5bec0722bd3930aa7773000baabe4f5aa2840a5be4ef6580f14011b833d17f2947242dc7b254f9bbf0e1db36882bbb4091433011da43e831afca8281c1d35492ede7d399e05f040a59d74273e69f3ac92728d5ddd5a8bf48bbdfb83ca3f8377b9a6a733164be4a6fd9ddea09efb0ecdcad7b4bba524dc03d3948e7410a757ccf45a4257a70462a8cb5fb05ba4c160d49a28fc5b7f857daf3ac04afa9bf4c00a4dfa0a64a5eebadf42232025a32606c157abb8b5724fcb17839c54b49913314f72f38b65174f5898c8fc95d9c731153d1ef779f2f26d180becf62e509ec04351e8471f11bbdb6639918d92bc0ccb8b0626c079b8e5b1c5df55aa65727c318f800e0821618e4aea95b352a2df9cfbb8a8f3165b75c0a20547662317de05ac7f060fffdd3e502af0a51234b7098655e9973c7437821786017959944cf81c8c4cacd8474198db744b1ffc5aef5de64cf34f0e7a2c37836b0a63aed7a9d42f35f633206a5946e86bedf267173351d1f1594531a47c21b506a8e18a5219c162dea850331f11067197d712bc84645171163e8ac3a9fd45c502a19cba6b7cbd54764965f99d82f93fc8a891ec264b166f4718508a67c1fd3ab96d3a9b8cb63c878aa4a88b8d3031b59a05baec3d9baebd2adc34f303d1d69dec97d08e813a8c31e34026b9d2ceff4e28bc4470e201fb4fd88965cba4cacccdc7d7f0dc5a949cb27769f1b96475a572d8e353fe74c35e527cc8c22f98c0ea3e8a186f87ad53cb9d97b14ebd2269ad6a7a8161af267c26dcef1ed744d2c8d89ff581fb9d63bc6e7b0621de539ecf58579113bf77f508c8ccbf54dba299e540827fbe42dfa4bd4789712463deca1bf46daeef2afccb8f5def8dc454725ffcdcff14d4b7413b9c2d2a19c6ae00e385f72fbe26a320a2170838a79269d42dce3a9c91a2f5d985d4b16210827c757c7e5fdd2484228b19785252d13715871434a93813e3f0db45a8cc7af648feb8d0b1ddebf72d4360754bbc49db3e692294d9812a7e04bd18057564cb2d83332c4fd18aa39681fa4304f0164b4ab1d7ce50b2b36c90ddfcefbc50a096950a8c4ac45ae97bebedd737d9a47a560a01710f5d4949f03a9669ffa73c735a4e93baf4acba2d8c73d94ebcdb925ad337643c1ec967a2ade9158ba68bd180aa2027ffb48da7736e2ef1e9f680b05e3bf9f79d2028cd4aa22f5cf85cddba993a764fd1f608b65233fcae58dfd9fe8075636a1ddf1d589e0446994d74debfc252649184d6c950a8211f0c710c62209d2609ecddaecfcb897d3228d4512d281960a324aa364ca986c14e552cfc8f8666527c63ac01a8c8a0b6eeb65cf77ac9102d3190e1db8d7ed73fa3c146a86ebc79f90301da6743a4179b014c0b249040cc1f6000d328ad57390583718920fd3e085c812358474af1ff0bfa6460f4ff6da77bf9f2bb4cde5ad7da547aadae3705d137140df56da0c38822f2286f8ccbd7fcf4284974131fb844dd8de7838ef356fdb97a841a83f31884305a910be3f4cc53e6d54c96b0365c7f375a72b9b78ce8b559a8454294eb0f63cb87f2bd1db430f8a4e7a917fd16ea0bb16c6e9e46dd4e6712ff7ccd2b177160eba37bba6331d7d25bec189489400894a437ec8b456bb1848177926562bb1a442c734a0c6a43aba07b28b1ca35f1781b7708451ffb1f12f38d629893341ab95de520d22a1f91bea020d345ec272717f3f6362b41f3293c507eba261252ec91a3f5f35a600e121f94e4f09f15ed4c3900202365bd72319deab9c0e9e41bb0d8d06e51800339381665ecc10e30ea570f7784e78a3b05b1963a2aa54b51b8b71ba96214a0d99d31ace619f56a0bae92ab6fc69cd46eb20ba2e9b31af5e40ffd03f4dd058a21f2e0f7cb2066f02fbdc429403a0eea1ae5dc08f451ba7127e59a9bd1234db9791b8ffa330f92e66051056b75d0357dac06e9ae5af334423ebfd930352370aad505a5fbecb0b73e601ea23ce7eb7d88dc4b6e27cfc9c7cd19a15445c574c2beb488880110f84f7ff314d917d0be889d3098a42d0a54c3bf8103080c387ce1e38f3f0abb09dffb626861fda0f89d19b8439e29acd5b99010715a3cf6375ac48ce56a610f7f850242d414aec30096c7c521b3955f343f611b503a05796c80fb1335cb780357c6e885b556bc0329db8fa92ff7a4187d75e07786727bc0c2b6928409402ae6b81117b95384c831554614e39524eb5b509919f64ce27246d49ebafcfa7f2abe4c0b8fc2e3c76a5d6c6ae162788cc39a517d42b247245de79cf421b3d74e43b624b847f107ffb3bb291622bc1108bc47a240e0cb62544900d8db3726e14296c2ffebb7b064826e8970b0652b1095276598ff7220c0a6ac91ecd08115756c443b3c0c1c400334e2c4997abefd4b1489e2c4b5ac0c3694e9a8fbb5178df2c7d39876b4ecd5ce423269afc37d2687b2b04089c8a4882bade1c0789a94d1dc47baf3759d59195f7ebdc25df31ef4920d12a5376a7ab031e8efe3b715d8488b1e867d5d4ad39c453eff1c19fb4249cafd0307024ed981db40646372f6a7631288cba3edc5c56ecb57b168ac41d765d73a1d7732d817dada55afd86eed8e3e7fab14d74272577510abc8a3134ab7b7d1781352354b709a2452c834ffd1e4970a17301b33f261e25dfd3e84a811bd1be695554803ae118224e5b88f7298c68929079645dbd6970e34b6679e2a707c30c6217ade1fcc4ed5b584ae2013af918f612ec910b81f78534d9d01e38c307ef9e600ff62a75158e87fa3a25890dc8c72a06610c22b0aa3e2eba269ddde7a03d7238b6dc253582ad7b253d1e3a9fac9a3081a5436ccb5b32e4c84222903577c96fc3c708f8182657014fedf11db088dd91196fcdc1cdfcd368800e1c6b2eaf9eff59d11fe3932aa49941edf7dfa3807fe91575676cd57ff78d175a380f075583355baff821ab3c193ca0993dd91733bd7e17e9e508ab9412abfde4e86dcb297f4019bf647bbbcf3432d6eb4041289ae660dbafe4d7f351fffeedba79877f90451e5f26939a744faedaea85e5d5eebd3da43e685eaedd1da527a0a893f68e2bbdcb8e27bacb7c006811b7461d43b2a92b5fbd9cb216ba11c77f7e33aed6440a44d351da9ebc7316e74c6e3bcf604f9318ec6e59da91e333d65471f7fb376dc461589accf617d2ff61b2e62f2edc7ff93dd5a31a99bf16d2ed5901f12dcd000d50af28f9630fb0b85a7b910519dfa70fc731775e302d69c9a704295d56d5c4decdecd42c846fed894fbca147f7431192639569c3b44b7145c2096018bbf0943c00c8719fb92f82ab44a13d3f4122ed99400e63491dffc2ffb19d94cb10fe38458ae14f24f50059971e807ad4c92a8ae7a3879716693348356dfafe3c90ad6a5701fa1422a0b8f3929b34e9b2b1ca6ab20b2d8b7b987b551a01f8d2e10a43d51f9755b8e7f163c15991c1f1dc25cba44ee2347e1853e608f82d8476b572d17f5b362f7d54e6df66753d5e7813d1d834986ece90ef588639753f31ff02c9a5120ee4cd38683338fe6f95a9a81b21192e7cf9d80b64bca9e875072b1a4400a04ab70697b7aec2453668a46efb5d079740cb688233edb955318edc4fcbc7d4c2e57da88e9d51f179206407412bfe4ae1372f8d3250c7173c9ee556987027f9ef776ca19e592de8d3aa5dd4100e9b606f8909e7831121b82e32ad9ad13d9cc77ef3381617107f8bd62b24c856f217580d96b6fec01c064a90cd51bc4486fbdb27baf0d6220d86e8f889f520876d18bb42bf0abefeba827b5f578cf764df8b33083ca4c83014cac350b59d2e834f3eccfb972850b5d842c72d77541d1dc5b408a45d88f4e3f518cd1e4d6a68966f435f4041ab93368e87aa521094e00bae6cd99bd9bc2a35c76652889423d479d2d0c56a0fbc14af53feb4e7edcb2e8d1f6ad4e0d2e0985c8cc762187a753423a30808fd64915332c7af2136b57a6a4e359afbd2d497bebfba6de7441876352c0540f8232bb0d1eabe9b16c6694459518b2d8bfa79e4cf3c9da6f97ed89bb18a0b7aef71d7cec313343576121813fe57135265dbca9beaf33e428d2e3474154e9df733eeff1f38dccad6d3cd203df160ac7181c759f4e561c1487958a36fdc41a212dc56c909dbf1e0064402e7cf13660667e663f9ab30810e082ef423c8f5d50c0451295ba865dbb6be44d49dcf2e82c3f882024b40ae3eecbbbdea3b431f15af1fb3b15f7aed19ee80886b23439a608c835314cb5499d3279be2beff2b26aa7bfcb10dd152510d83f2c7ee4a810cc156f7d1ca8f65fba251d64874781c05236a44132ff4f28d1fb096bbb16a79fadf02a717a20a2f7e0ff1e6c1119803fcf02467ffa0c408e1fd25fb994bf8acd24f681592502ba023e383aa4c568abb0e0855b84cfe432dd6acde4f984a55489820d7d57101ebae05a732366cc799b986414e7143d46a61bc8892680b629f092d0ba0c69d608eaac8555e5730ec6b4e5471a4cc7cc1762943fff007f498c3ab21d2747f353c1435ce2af7fe5f4ed56dd91a126447f2d3ddd3267aea8dc2cbe4fdc8abe646c54dc7b474152ca43211e3117558102f4deb79f92fcdfb284989091aa3cf93907f24f02a86e27d8a6224edbd2f9d824e013506b721a4f1bb54e3f53fc8959d26e69587b40cc7992c5a238ad8b241dcd702c2fa7ffbdcab5c91bf1da8ec24c4149ce36dc779726b94d8500fe26d15836474a1b4020e9462b71eb9f50ece03eb8261a631e5a7169176d5ed7670a6d0c5f71230900a46e78918c80fd1f4fc8d3cfbe8c39e70a4d849f48e8270d00f65c4c6232d6ee18e84ebd161cf9b293f5a1a955537f72d287c1cc63a0eee9a58cd85b9d4393be3dbe85979e2428dfd9f92fefce601fc9d27ad15845ba952d0dbd63932d15e54b6ff9c16c2dd5686ead3517f5346d423bbab422d597740eb3e0ee86a66df261be6923faedbe0ce889922d961ce8d7458a3de45a71f12191b6902a8f20df7e6e4c4e442f82fc02ee9225e4dfb3951250da46fd6f701a4c317957f373498f6c2d652fb7f29e2bd73c3079a8f58088388af3ffcaf02bcfda4897e31bef055c13ca8a3de67b51a6a10bb5d923ca97bdd1a99f876d5a0557cc86126c65aa53d11cb137ffb9bc64a1923c6c6b9d10019c14f07abe466095a7035e7e7a3f0eced8f3911a9713b7c92aeb77f205c4eddf62bad73e1c1a69ce99d775af85e47eea3ca7305711b983f0d2e8c7d14039eb5281b7591afaaba9cdc7418be431a9f43156312f65eb0541cd436aa3684f104ddd4db7228c88a172c2c41405d446f83c7db1bb5b098516de9b3fd8d5caba6169b4c4cfe5ca080a32fb27d976961c3aa6b2e4865427907a22e133b5851e8c930de99e6d5834f6dd1c2376ff3e87f45d3c747220710c99a9619240a2021a42b732829c7b1468820a994f9451de4482fa966b717806817ea69a9ae7a4712ff257410a544342012123af132ec6a063cb8916ab7a315d1ee0951f512ff1cc7dc969b3f0bbc29c6591af57f49834e7211ac42217a6c5818b286e8fb25abff3a7b1c0d5d4b93a2cf0269d22127fd501868ca0f466d74ee9aeff4b9b02981b594ffe0581f682691045ba2a27f1b25319be69b397e518938a93d16ba3c4ce8963e1cecd0c2cfad93f5f9ddb013002f947c3afd0479dd07878b33643d9e4afb91de71880bf8e9311f6d92835ee61faf91a39e597a6c8c99213f913bd857b165767572286484c154bc5891f897569e8e1caf6f5256b956ef39295efcf8c66f80b7e1a93e0e20a31a49ab239ff833674ba22a21d9bc7b501df1d5c2d06b33da1278d2a95aab38a12e7a43a35711974ef82fdaffecbeda3c1ee4ec2c38c7f59671ed6680bb307e259e55be76125b8408e48258baa5399dd82a50d26e778a4e941c2110ba7d70378a5818bcbd6e4d99ce551ed871319edd552e74718aac4cbcbb10aea84b123c7e5b59edfdb10ccdbdf0adbf652397141c0bf616b188c751781e00079153572a4e78ea09f2d7d6f9d32c626465eb32830b38b70a45b560e5bfee36d91baa0a9bc1198735b9d05832686335461dd491263bf98585eb35d4dee3ee8ef2ccc1d8e4c76accdd1d3d4fd5ebfffa5fb52352c3c1dfe00f5a0bd6d3745226f80ec2cb1e365257ce99932d23bc8d665b099f55c106b648167c36b5dbc745a6f5fe5fdf53938e3c46f4648019a1bcafd5414628b1e4daa01b206a6762690403ad9e420d9dfc82b6e36553a1ebe5a228cb83157fefed82eef21e946322fc0434b542e7aaefa91674b5a39263cef8235e8f4ae4f2a27ddc89417268c4f3f1bc04e92b1df27a94bae5800a0d2ab62cbc5da9001b07cac84873e723a7f0ab35207cb18f863f7d015a52f381be8fa673c21e32dbc76ecc0d0c3b667c3043e84c23e39d09d0993fa21e80f578a7014d9bed0abe5b64c49cd40ad945ee811c7efad91fc671b811c6cf4fa7a529988dc667de176115e7689a0bb50975be2430baa1eac203aab60e14bbd2482ad29e2bb31dc73b357398c68ab978d5e37b87281acbaa4e65766655fa46645f9ea25aaca6f6c3eca3410ff7d7ee27843855cd300456b9e9c53efc9e964a887f562ea75dbdcda3df39d9f90bc89b75131d1e86ecadec6c4d73d25f718d5ea62c2668ce842c0b6593d4b7f6e05910a1a9a50419647d61daad754a531186434230e181f96068970617e700a4990355bec3313fe453c2a4b19623042dc384a1880ec0e9f9d057668ad82c01e74246ae704562eef7a5ef31310b948bdf1682c3a820a3dca6e89c15f4a7e1434d612d1965420bd459788bc8bfb7e2946a39bb0f536be94598d0dcd7a54a5a58b6fc52a32e81d87086c91d1b9f7ae6a0d893078e7aa026628d675b86bb68ee4eb48b7f354028f37657c4e8cf35f80dac9508de6b479879a9403f991fce0ef441febfff9ad81235e742763620cf92484441482013dd47f1f47f4fb93f344a344e342d35d1bce6296054c8c0ddef6587d8fdfc0a56b5606769aafcbace001847981d8d13e398cc33089bd406a76708ef299f9861245e77caa0876a153031e09b9d8824df875ccb6c12b832eb5090ec7c5eed2e70b36f8615eb397f3f00406ae7766d2fc4780ee083afab4c505db755a8da8438e66c2c96affeba62a02cf3b67a7280b4c5604167b666ebb330100efa17ac27245260d268e06d6f0e99b21d721525365cdee0784487fbe9377ee5750d45e712e4454f190b2e63eb67deb433edcc565fb15738d8b2b94301ff1651e2e8f0e95847505d963710f738e37c967405465d6be27680c12c6d3b8a80c60c5af90679591176bee375a17da58da372d9dfd4543c3f1c2979e08f3e3533e12ae9980b633686aa21bcebdda47514160946c84f3e1354a513ea283fda2883dfad39825bfe300e5966fc05dc19e585690ecdcf44f440fdf677438bb7f8835ed2c76df5ac4dcfe1e1466a4395989e0db152daa014b844d05cc2f771ec89bdd961f9478c015e67c92e74336a53b8e0b8b43a309d92d730181bcd27d0c27d8c824b64abb53e1b9c9f0a7cbb12a38046f79c79dc81d185277de97e5749e1e748ca46a77c3a4906d69884e34cd9b3339599f614abc9542e579b4d447c1a66027449d45a9d9c8186da167723594f7f980286e2a4e9df28f9b20605747ee538e62fb7e897883c60dc0510b79f8d1844a77a6c250c45fa2c122999fa9b9bc2503241584208c967ac403d10b18817a7a0f5f4bd62d6d61e9cbc2c196383fded3b9ca5852c0ad8a5dc0b31ead7c2a190a5f88f5a2a35c22b63b76c1c0f24d63a4ae593d49d5d43e75994f0f3fab990f03073224fed65f685789db462cdbf3e4f3c7b075d0fb0d352378f4665624dd4c08db0e6b023dcd79f1f3efd8d154e4724eee7b336213392a57240d3474cffa3d16d478ae325c7a5cca08f86f4c8b3cecd901fd64f529e1036274571e5261bc47bd37a4f135054d762cf1d788e824acd319d3a329ea49bc1d07f477be2f2a277da73670f92a82b5316fc55d3d1d8add9f102f942aa6108bdbaaf814a2b4bccd18ba38d3f5ffba74c3dcf148bbccebee4ccbea40d8203aba4952ff32d06c0c6189aa78db48a285b0b53b25cfb5ef3e0a5d0dc69345b87baf7ebef9bfd841174481620bd59ddb658f31a44ef2773e50ff13a9485b07c33b19706c4e07646612dc4df22c4575f1ee534baac96b2172d06d31250c00931d2c6d14adc5459da42ebf029fed4f8b179abd7bb96c3074e4f1c3edd73f5a8cb846893f031336d2d94bd58fda725092c064000e66e260949cfef23cdc1becc5f4157dcdbdfa3880db475499eb3140597b53f7606157228db18d3d571d297afcd7a5f338255336e0a35a7ba75bd06b0887ba0cd81390b23263aba081e681c6ca652c5edeecaf8e971c3daf722de93977e5258eae40a6163061f87991fa6fbb13fd75363af6763741ae674e000f7d482b4225df879954c3aa7ea81c41ea3655db409dd6415cd722c4f8fbf10ef528df52b901e28bf2cc6931d328f25ab79a2a965fc9738164d000bf3b17e8f21709e095d29cb533ed526687ed31a0463b37619c82ae8a9eb2392ba58d6948ccbfb7d0f00c415cdfec4330bc0b6cd2de502a7d6f004e6f7c2c1e4c52ffd0d4fa3b5788d9011087050ed087e9f713adde909c8a343c82b330672ca925674c3d095854a8a349b69027556f65619256dad190e6ef551524ee68f93de81b5f4cbe6106dd0d2807ae92ed4f677df284689f74db0c37d8183af89819d8d8e30fc0b2e54e8612b636f0eea8c2a14700381c7c015e3349ad9a5057dce2382909aed9445c2e9d0d99110376bbf57f134450cc844142f7548f8e871a23fa024e1663f85cad17830c5ebe8b887b92a815bb3055d1ce55b96de5d4de96f6ada83b0c4b004a7b715440d08a0c7ef25463a499d3a8c317d39cbd9b5ef0af60e1e6178921c227f8c14de61cdb903a27bde2c36c74a683f16dd8328f775f5d34554bbf6e60e205d7ebc0895324021159ecf8a0e305c721b445ab36900942aab2422f79dcf0d6435b71e80e0142ad64d1b60a7d3aa2afa3eb1a5636ac81df475ac02764e06f26dd28002c5cbef750ddfe3fd36c15b9e0cf04b411aad8bdd48f7d5261a3dd90459773f28394c761124cc2ec0beabf2d9fff022b1c81e631dcea2626471e71492df211f26c4dd01cbe45df3da53b33c481b73ae1baf14d61d91a3abd04912fccd7845c505d07a6bbc29b28fb55f6bacf5ae01b85d2f9570ab05710bdc6115e8f83119ad6657d80e8505f7f2ba17af242123a53b4842dea2b10c02f6378da8974640c9e66f2130d3646a51d6c2746456f17a4acf78f4067768a79c927f4e907c357cfa07d81503456c83b21fdd68f2c0454f8879b93e0e90b958671d666937317db03efac3034c43206ff7ea71adf7b6b3bd1f34a48fe076b81ed7964773c96f7d4cb175016c41946d897cba2dfcc1b52782621578babd83f95ceb814ab0252de8a732073022999c5c224f5e1fa043972ec9891740f50aa6eb454615299b388f589552e3d48f827d14ec6c592b7ee675edf794a78373d7820216c8983393f99e497db81d1aaf4decb96554bc35653753530f58ffdc9bb15899687c55ecb80a54571ba2e5cb87eb5ffadc57c164af9bd36bc1e6eb10be0394a7983cf6b45fcf7f1b4b32b96927f5e8c6419a750cee540b70f60889fbd688c3d430d85171c210f2a0bc653441124c564476e2fa926fa45ec179dc24b1a624a088adee06a3319d0c0458e4d5be2cb5d241964a9dc24a509abcd1073e154e576754ebb1c6e834f4d97cd5fd31d9464a2bda81321bae77f88a2870ecc9e156180db24a0c5ccf915d341872314bbae2edff84c67de42b074089611483fc3ed50382d29e52c14eb6aab74ce691b2fdff94d732494d50457647e4c539c9d18cead67ce2537c56c442ceea61793a1d8f8f2ff6d7860761d325500927b838b5fa33957ae363ff59fa7487d8b015c742bf68f61aa77adcdbe1eadbeb71434e8093a76fc45f3bd0a4662aba11c822e43ebd2e7cbc1010cabdda365fc90ce4a00d246b9d8784e3c9794c6e9a8d555cc7c38d8adb3536609e308af70c50aca9fbf2b0111708722442dc6bfd977a2dc0971117a424e9013a42a478cb04831c772b5e2592b716321dd5e7b05074a05e92bdef6fcd074cfbb078cc3ea46ab835be8f2a4d5c9d87bfa4ff4129318f947af607b2496d1555e250e97097c80185386818bf74dd793949ad2bce37b3e76eed1abad45f6afff65eb4cd7ceed33e4e5c35c268f6178246d508622cc4656bb5a5b69804b14d9c587e22c71f7f21a069836034708b65f4e8d10cacde13a5674746e4eb493f7d9970a933a9c155f709af9256f3093772fd155dad3d55d581480d85935201da97092d68838a4216bc424c164b5083bb78ad2bd94b84b49cf0e1893dfdf59f8fa16cdc8fddc80b5580c54b20a56fdeaff03ee9d472b894dcdc3587abbd9b58d115a6d31fb81431f2e955cd801cdba4be00cabdc41fcfd706c25410ace95ae7945b990c5b17ce280841d71cf6fe792ae69893a8c2a0863f114d807661f61b20ef559ab767733fe6a0439505746d2bc28c5ef35da26a8042d1b9566400988108e3c01d3974cb4332409e52d12794154b42337b6e5457b9f9dcac1364ab47425467df3435a63d3007e330609d3add6077c7adc4d05ab0a0c3b7c1f74adf1e183a25ceff0f3e77b799571571a914febc0557da8327399e0533f8a6e18e596d2a44e57565c2eee69d9a75ffa4dd375f240c75dfc4bf1f0d41f0d1b3d04883a9cf48f022dfc00a80b7e2f86fbeea6a8a744a0035aeafd895351f84131d9444a471917668f5bcd4a5134c4cbee961d1db3aaa8629955396a7fe958bb1523587230044f24b96b94046809c222812d81799be9c38c7b3e2d38c8e6b856531850ecc4001e7db59f9ea7e206d58e676e6df42b0bc1416ba190ba2a1ff2f5c2592705fa647fb8db28925a031a992329fb29bebf412440eb1d969d1a7c3ec2fb02f46db2b9094d9c8c47a3625f041e9725aa7b784e7afa44293a9b8142d15b33c72d64ffc45b90bfef7a26175312e5d1523d0183f73377f3255378fbc21d2f851d6cf96d778cb38e65f2e0205b00eea8a969de364e54ea52686f1a4dac8db3d1aa617161dd80dad2819314aed9e8f31b689723f779b566ca884e533495bb54e65edcd917cf69c2cfe4c3d58c5e816b05ce2963e7de828934421d5898a9745fe3e4dc950a19dd70d3d614fc1d0d03c2e5531d60b8f3bbab8b740445845e773afb4e23e746d1bb9e862ee10984e354d815c67a6a0928769f3edc2eeda920ba5b9c88f415541aba016e22c3ad85b587baf6c6ea5553d570419000ea393f05d6b40ad78be7370e900430545195670ea916b952495ccdbba1abe9e54d3519f7c866a62f44dddf797bfbea21345ddfaf972e5ad25efaa14cce9a4e04f298e7688f4d0b5213efee32a1047e17bbe23d48c2631ca0403647653b76670ea38c28712fdaa087034c5d92194f9132a40b5fbe2c06a9a92017b396891af5d2ac4cfdf64dfb7200a4c037b49a9e65dc26083eabbe566d123c8999611511315ea85705407aebe76241a568197aab3492861e377e34af1043dc036c5ee50c9c7cc2b4ae2831d514d066c44d55984888f1af620052b486bd5ae1d3b632666e526b65560b141ddd32023957d9aa3668bdc86f9e4c7fe8862375185438f125518fa801e0d742299ccfcad1c7efd297b62f57f68df1c8ebd0f92253e003927068aaacd835a6bc56d8b504e8f4045ea19af0175072128db5568e3cfbb4947de7292219e4429569118a72870dbea6e3e070e80ac601c6731f6f749727eb1b73215e6e839cad761661ee22592ad07c0b2f6d882bfee83e3b8420e933c7cf271df576fe1e2b79b319bd6ecca517242d1be9fe5ca3ce8e6f8573862568799d098962954ab8353ff7c8fd5f2d36caa618872d3fdc2ddd66222b7ef665740fdeef4eb20d622d0e4efc73123fcf77a6da82ce07ef5d691bcd286ff3de4544860fd82149df1fcc97ccb344177a763ccdf13e5dcf88dfb1a62d6f421609ceec934540ee723a18153f125f2c85215c1a950f506aa740038358ed5dfe3caa78bc8619140a5452ba28310b2e2b64fad7291b6d27a3079eb6f43014638d93bb2f0cf0658deadade7fe41656c78ee70a649024b6c6b63cd50ad9127200424958b8e1a1f25a440eb991d1503d9a22012c0f2bc6a15b74047576f43edd5783758e48815a1cbd65052c01fe06ebc4c03ef0b086a695e7b2fc574d3fba0bcc9073a34efb4a83dbf025e889861851cb0eba5600975e2058aaf14a183bb5f82a1fb5d6ac8936340a0e64554e7f1b191a5c45a87ebdb029bcc160c6fce48d03b2a6831e97e1c990f948605e85fda31345006da5c8aea0b139c98b1c6213a6a77445970cceffcc2d9023db009f2cc37f21db68366c8b90267b4dc26893e3229fc36ac3d7c494a6009d89ddf8ee82c26b09f49b769bb6a7ff3807b6bdd3a0a07dbcc0035f6e12bf7259477a2ab2eabfe311a1632cc920c08a027e20fcf276b50c3d12d2eefd80ecea96bdf4854836896cca835c773e3b7486a1ef24d368a68862f708735e6ba19e6d655f7e826c1d869269c2d267b72785467a17a5fd2cc04cdb9c95fc5a044bd8fbcac1ed88121bb00f9e227d9fd45658cb76590e1a5ccc0f853f0a92a615d8648576eab226ac9fbea5c9f52a29f33f9baecc9d335d2ba4199780a4f273d4831f9525eea94912e17417069f7de4fc48d654728e0b1fe65f1a97471eb22d5b0cc416aa3f1bcdc244bf1dd21b9866fa6a531e927c09b8b75b15c100f33244b6cd2dd8b9d2b2f630e9313dd7cc0e634aaaa4336f82ed7e4b92178702d06cf6cf5eb2168f3ef5602691e2419cd21ac2504b0f6ee2ac9e97b1a7a934ab38953cfbdbdf1b85af36d1770ef17ad363bb20343502b377cbf747f9fa8428dabce718f7e9a4e1fb412b3d1b79c636f8fd5a54afcc129569ea58c7461382d4650f6f097157b6f4d69d4f85da491c41b4f140cb24534a708b99c6a45d51119c3ccbfa73eace012327340c14fdde92d58c249d76a73be6c034f820e887351ee9a8e2ac96f35710b36ce1ecd5842332ee321084536937cfae70643c6d4c1ad71e68a1ed10b27bb637eefaaaf45350a6e8411639c91ffa658ca3d5246c00bc3ee070885b3ad2da5c65cf752a7c284d0ec34648e44d733b091434a7b487a8b9d833c62551d21e627e900d5428ab2e7d262040146cdea1cc560af4296ca5eb7e6d381e42056f4fc37c8ede0e76c8e45387f053c7b84a090f9d3fae330350e8b3151ee6f522bf2732df207e3681bb80d257ee4deffe281e477b1c63f9fd9fc1c386699547e9e4221955a99aab7f77bd21a23f8bd9d308dd6f95664ca5618821cf0add96ad562c378a0ede7a16dacf5b94eff17677a0e29cc9d177b6d8164c47148d85a4d1a550cebcb0251189a38e44ebe3a7ae0728482dd66342a50f982dc7ae2d3ab801b0a93d7349badb1c26d469d53c546e075352be6281d9fd09e1f2f5af672c8c9b1879b39dc2f10b8597c8d3f8de26aaf8c767774f67db46247a1130c05141c4ba77ce8e0ffc2cfcda91b8b77c1c3e044eba4c86d440b82873a8bb2bc426dea313f93ebe2527cca63a4fe2b0c646e22ca3c459e2548baa3965adc983dfee653375fadf51b1ee51240c518c77bbecfa6bace5bf8360e18745987a537cafe4af4564bc7fb113d6d3fdecf51370d0ecd92fe7a78af838826567fbc36e26cecc55eeab2de942042a97f685d629f627f2d7f7228649e03884f092fb810dad6af5664f857d57bea9d1fe16a1f4f8aa172a7536486caf06e295bd2e345bd7082131046d5a2e1faf5326e3e7ac4af889778b0b25a0dd00b561d867c140b1bef8b8406986b5bd46bc462d7aaaf4ca55f592862476ceb871199d11b9caceb65d22560ed9de7c5800cf5c6565e8b2f2019ddb4f300f93746646edc15366b03b2fd977be3a0e6731e8c818f1f918bb7568c71cdc6a7a6a8c171643b2c3f652f0037baad1b5fad09aeefd1bd3ee5200027b002a1662a06addeea5fbb517e313db3dee5f38f96214d2877691b811a92a533da97fba80d6e222eb9d629b8b178c9b454d5d1643cb9b9164555b28cab483cbea05d9c2b628a2fe677c208a626c9c8eddd14bd9a8090738116493e3862f1ab1de18d91d88348da146742ebd02a8c06ad8a0b646f5007685daca10be22259f809eb8910bb6fe744750f5df311b99b4bea316d59310c77aba8633b975e1bc657bf86af077eaba32282c0550cd709a62489b8287c74162b466d67c233ea1399dc915fe5b8884914467b7b22083815e930da31e8214a229a2b49630094c2b8e20d92509cf50288fa25d5e7781e85268ba76f60f0979b8b8de80642b31a1a6161338a030ed5ab2958d12a1cc340a405e782df6b537f7019659dcd18f5a5020cc720fde2013e2e51e1329ac1f774f825c4bedf32c34cc683f3ccf73d1b88e2062e666a7d081150b47ad2832454818352058427f344ecc93eb457603ea5b50f0fc6989a2284a51aed07680cd3e1b040b544cea6b12fbe4af7a32f83912366f8ab6c2b643dd4838026f471781f9883ff99eaa57c9968a5bfb9e6dfbd80099915bfacb3e75ec98473c63f83669953a3c20067b3295c64669d755f2ccd07eaae2d6ee9e6d818582f190441da572442e10f7c7d02b631ded529b8de3a4432cfb4ad9883a5443c22e6ecd4f671fd6e666762bb7bab8e138887c96edc57b7e0320417cbdf7da9357f1d4ea9e35bcad101b860d6c32461232922d43a4b02b7a858c3e4f9b0127e0bfa4fae33e4ca38f242d8bb40982cb8c2b90fbb97cf53d43d60e0fbab8d65fbcd44367555e76a8403ac4d40833355828e5b8dc2ec59cda82065c511c805bc764a51a2e642da3fe62ec13cccf15fa721d0db55cdee59cbd1d2643b4bb2d6d129328da8b2fb46a66c98ce3a93f3473861f18198b0eb2e1d96825a7d1eae4100db859332f52739dc8ab9bb08d98a066c696f3a1bf54084420cd921afed07619df56ddf282f1737b167688b8b9a789ccf3f1d770d2a4784a0ed04124a5d40c80bad252871ac90f8413a598b905dfd6a3aa6079e86adbfe0c9b5000bdf4f4ca7e4426f5000c25f33493d7e14dd939f60fdb0b304e04033410bf5c1beac99c317535f37cf03f84b835c8e5acd50751bf973ac357b87fc99809e86740fe4169b9f78bc68d516e7d6ad3dcb4ccf742c69b07a899c255b356f9b617d8d5daf300fbf4d3f05763e5ebab024393e0f673d7dcdaba73f21cd1aa620c9855fe88b403cd4be8f93da98be8b6dc0ecf25b6e7fd88cdf5da4dc0a43b86004f5b5d852d3b36fbea5744e0821586315607b1ddd4f883c2041eb057f2d4199499153e640c310d8300a9a7e59ea8991e4fb9aafde00c1546cabb199d56c2b5baa87056b845fef8950feb9b98f6a53d57a83972b05d5c3e38025228d910b1700d033d5868b913f13509a47b2d93b9ffa5045d364cf2c35dc1f28c35b8c0ac6d8c0781d187fd83047fa8959783bf98dbc0a79ac39c47200c181f71d2118f05a28ead0a3c893d41f02115fb57c7b6b8a7c3ebd5ccdfb5ef16ffc119783d9b922be40aaefeed649a5ac1563e9b92cfc073d7155abf8d8f0ed64c2083121c34a08d6cab44504afe0341aa582bddcd1a17e6004f7d009a3cd0cc3592cc5a226dc04c84b1e844896508c573757e9ea98ff669546ef92ecec42cf92d6ca78d76620e60a8c457e3a6c0d05033b37a0d9ad4ce04f79914d0fcb5e60092d7637f606a6dd3802938ca6f4c9af50ea4bb0d9979c5d979b54c28939b0ebce26243b66e99515f1eeddd78199ceeb55c05305e213f318046e800c6e8aa1491b2ed73e73c61d6dc05185d360cf8813141766b46b732b66f20c4ba868e00d5766b010f8ef95eaa2690eb27af2ab0f2b1bce5f5982a6bb330033e0aa825c71cd12780001e5ad2da303bd2785a6a843f244afd35ee3c2f7ce1223f72e495e9f978a22b812786cb70013f1bc7704bf982a88c6d792049f886fb7d01162adcef06bce10f80c629b9c5b14b3bf4929dc46381c86bd434595afb92377b2b8bf664d0809bd1e464571f8f739e4d468e195798605e3ed732aa0489543ad6896058bc6e9e697b7721f496d3e50fe7350eb9c2c3e76f071d98574f9df9b672e787dd968b8c3ba7ffe5c009fa722d94c88ef3e45f7be995fd8559d35b3964e1888f071602d39829dca37c6aa22dbe6a6a52b860a3548379c4cbbb1eb5959d12ccc76a8864c0604425fedf0651517df69ad1ae898e93b242282220d74d77856f7e1e23ab340d24f01b1cc4e7b59b1f93a661dccf44ef6ff914968e1e9d3ba88476f3e0770aa8e08f73a42114341e4e8f5bbd86574d9d89b227b9d56d02dce3519957e3332cac0e27952a8b0f7fccb939cff5c678a1778ae8e137810d525b8f30f0d7d1586cf3a31a179ac49bdbf73e2a20eeef81953cf2099b7743a959c11539bdda53da98262cbfb0fbadada63485f17a88f7136213489e192b69e0a29fe0646b45af9ad3f86f97179e24cdb84246b3e9d5cdc0ec4556eae7db7a45c7114b968db5f3208adc52c7179627b78268e20e5583cc7c16c3d5b985cf16728dfce4d44fe688eb122db95ad001dc1e1f35c0110e409c21c5cf99f2bd5eb448e7b7995eb15ebdc91d42c1048cdd2762a6ce2cd38de42b3d95641c93039b2fb0495d3a9b3401f83b3360c1adfd5d5086f83371742cdd93c85e6d4f51ad585f369e55384d9c27e03eef51a5e7d8b7b5bb0a9ff87b489a4a59607cb64cc866cf67045d51d7ed039a7857995528ba3a153cd15873d8b544df559c260480f51b51d8b18320b8b6d52219b71f5ca675a8927ccfe62af4945c4bb7a2b58f26d634fc2c717f95e993ed59e32b649525af6da368da6e4684daf8f19b6e1ac5fa954a0018ce936dd85701d47a44e3ad92a8ba0cb0de959e990435a7e7944c5e9efb2582eb0ea51b2633a66cbb2c116977263a5c4d64012a0658e185655179d187b082e51bd9c2868910d3afb1f85620aa501d603fa75603a9ab74927c4e39badc66f88706853c4b5b9110eb4348a049cc15493cc76396993555b5a6ff769e45d5d736b733cce848da22dd118daf8e70d3d7b23259dbfd7b941384eb89cfa5bcab329af481e88c3ab049e8955778b9c8253edeb1c29d8efd9e8fbeeb0df8d750a8cc3171faea5a4b8fd756185c0bd2d7341a01e01d86def3748789a50ca15d275cbd7c9b35eca6de87b6fb2284c8417000b6ec599fbd429d7c53c8f0215f847b9746fa6f6712d7b50224f535f59f3596bb37a53eaf7804db26339d8cc4e238dd4b3d29963e002e78ffc0d9adfc9231933b6f676f6cfc0d114c7cc9368be461b96373b27d49292a69f51bf47f7fc7f0053b2be919914b6a2b41acbb636432e61e92aebaa5f3a3e33a7d86e8e614d9e71b7c10195e5e18bf734e93ad833d3e4a3253495c110bdc3cb7da5ff2fbff7371a70648cd8c79647187a59d4e99c330846266c98a02971ed8e85f7a27327960d271cb441f89fb3f295c11daa4c4716c465ba0f71547e8eb316ded2cd004dd6184c09fec5229c83a30f64b6281d783536fa964355c253154f825740003117d9d5786264c95e7783d405a3a4d66dcfcc0bc952a182d1a743f68366fdfd85cf28bd2e70547e886d824176443b4cbf301ed9ba8505f4915f5db46b3e792a7b3fc2c87867adae61a3cba772a63ba5169a7f406dd6c7b908693d1b000fe91e8eb58d1c07a04fe8613db954796aaa9bdc47462c9efcd3abd38456c94f0b5038dd802900f50252ebca743648673a0c0b1d5a3ea00a1e6a28634012bc8f56b9da65520582c829a440b6baf67080dd3cdac071be967929de527c7f04482095226f5964389a5831319a8643601ca0aefde08faee8bb5ebfbcd72e643489d56e338bbeb4f6855808407b9a228b3b73981ea9e34bf333ef09fe3daccc916f287d606389c9a18a55c0e43831488c776ae3dbe1d2bbc73fdb9794ade476c7531d9cb3ac1b1178fbbe0aa30bce2740a16a5df10d93aaeeee8e14eefdcdf1280e62a58520355b4a3c5ed1ac8545712a8b17ba47109818237a26148eefbda456e72d18a363bbf50357767e5171ad97b22ec13bdfefb78a65ca2961e82ebc1aa674aa237c14f183a14115984a1caa439a6202206096d6503860e4974d61e57915ebc514f530cc072002e54e42d443b6301185954e17e1aef41a19ceabe3a095b9d4d83ea788e8ecb0988aa363c1214ec8989e7238d2d27a071d265397751709cc35cbd7bdd5ac285c5c38fe1f1cbb9cf7dc1488f1ecab66706263b9ec40d220f0ffae46f6e34b748c802863599526cae43bcce2e32bc0f76f10187982380e7ac966f1639292c7f64e478aeaff3916e163b2cf4c617f1d47d2cc0419fb19859d63a1f077a49ad5171a6ab2633991fe00e16121869f8bf27c7ec44637913262fab8032c27d73ae6702846d0374f4a3d6df3a4a1ab99cc42a00ba15398aa8e86e197286a6c9129f9b01ea236d606346b0c091de549a54e90b1dc9471df5a671bc80e53fe8c81d5dc52221e37dd775a330d01c39229f23b0e6cd56a0f66dea74948bc6ae99d2dfec096e7acad7cac44c2d2c3f3f8563cc43c841127afd7db65a605522212055519ccfe6e7c5da834c1e61a7f0de7996db9ab2e574eda1ad3075d9f6f5814566cf746fad224029baf75d225802e6d268e7b3</script>  <div class="hbe hbe-content">    <div class="hbe hbe-input hbe-input-default">      <input class="hbe hbe-input-field hbe-input-field-default" type="password" id="hbePass">      <label class="hbe hbe-input-label hbe-input-label-default" for="hbePass">        <span class="hbe hbe-input-label-content hbe-input-label-content-default">Hey, password is required here.</span>      </label>    </div>  </div></div><script data-pjax src="/lib/hbe.js"></script><link href="/css/hbe.style.css" rel="stylesheet" type="text/css">]]></content>
    
    
    <summary type="html">Here&#39;s something encrypted, password is required to continue reading.</summary>
    
    
    
    <category term="随笔" scheme="https://blog.felicx.eu.org/categories/%E9%9A%8F%E7%AC%94/"/>
    
    
    <category term="年度总结" scheme="https://blog.felicx.eu.org/tags/%E5%B9%B4%E5%BA%A6%E6%80%BB%E7%BB%93/"/>
    
  </entry>
  
  <entry>
    <title>我的听歌之路</title>
    <link href="https://blog.felicx.eu.org/3315959774.html"/>
    <id>https://blog.felicx.eu.org/3315959774.html</id>
    <published>2024-12-14T08:32:42.000Z</published>
    <updated>2024-12-14T10:01:00.000Z</updated>
    
    <content type="html"><![CDATA[<p>今天闲来无事，翻了下网易云创建的歌单，发现很多歌单现在都不怎么听了，但是在当时的年纪，却每天都在循环。</p><p>小学时听音乐的途径只有收音机，每天除了听鬼怪故事，就是听歌，喜欢听《包青天》、《我的中国心》、《酒干倘卖无》、《冬天里的一把火》，反反复复的听。反倒是对大街小巷传唱度很高的《老鼠爱大米》、《最炫民族风》这些流行歌曲嗤之以鼻。现在想起来，多半是受父亲熏陶，因为这些老歌属于他那个年代的流行歌曲，而他每天都在家里哼，久而久之便习惯上了老歌。</p><p>到了初中，开始接触到 MP3，也开始接触到了更多的流行歌曲。像《春天里》、《我的好兄弟》这类讲兄弟情的，当时很是喜欢。初中是叛逆期，就喜欢称兄道弟，倒也不出奇。也是在初中开始接触到了 Beyond，《海阔天空》是我听的第一首粤语歌，从那时候开始，就开始喜欢上这支乐队，无论是家驹的嗓音，还是他们的歌词，总能激起心中的澎湃，特别是后面听了《Amani》，全场大合唱的时候，很有感触。</p><p>到了高中，刚好碰上《爸爸去哪儿》开播，那时还特地把手机铃声换成了主题曲，当时用的是老人机，没法下载歌曲，还是用收音机录了好多次才当了铃声。这个阶段，开始慢慢意识到自己和别人的差距，也没有时间去说情情爱爱，所以对《客官不可以》、《多余的解释》这些都不感兴趣，尤其反感 TFBOYS 的歌。高中更多的是听朴树的《平凡之路》和南征北战的《我的天空》，给我难熬的学习生涯一抹阳光，让我还有动力继续往前走。值得一提的是，直到毕业，我都不喜欢凤凰传奇的歌，觉得太土了。</p><p>到了大学，开始喜欢民谣，还是那种伤感的民谣。那个时间段，网易的歌单里都是《成都》、《城南花已开》、《Let Her Go》、《Five Hundred Miles》这一类的；慢慢的开始听《觅香》、《Wait For You》这些，情窦初开了嘛；到了大三，接触到了机器人，每天都是泡在实验室，开始喜欢上了小众的歌曲，像《阿衣莫》、《上头歌》、《月光光》，说不上为什么喜欢，但听这些歌能让我平静，写代码这些能更有思路。当然，这个阶段我更像是是杂食系，听粤语，听日语，听英语，只要是喜欢的都收藏。期间买了把口琴，有事没事就在宿舍里吹，所以我的歌单里又多了很多流行歌曲的口琴版。大三时韩综《Super Band》开始播出，每周更新都没落下，十分惊艳乐手们的创作水准，专门列了个歌单来收藏他们的歌。当然，这个时候 Beyond 更是我的内心寄托，感觉和 Beyond 有种奇怪的缘分，高中时年级级歌就是《光辉岁月》改编的，到了大学，校歌《大学问》也是《光辉岁月》改编的。</p><p>毕业后，开始工作。恰好这时《乐队的夏天》开播。每天都是在公司下好新出的一集，回家慢慢看。当然不是所有摇滚都喜欢，像重塑的歌我就无感，我更喜欢新裤子、后海大鲨鱼这些，像羽果的《怒马》、丢火车的《火车日记》、康士坦的《美好的事可不可以发生在我身上》也比较喜欢。仔细想想，我应该是向往这种自由的气息，毕竟一周除了工作没剩多少个人时间。工作第二年，我开始跳槽，也是这个时候接触到了毛不易的歌，很慢很舒服，尤其喜欢《无名的人》，仿佛写的就是自己，比《平凡之路》还写实。对凤凰传奇的改观也是工作后，再也不觉得他们的歌土了，而且又开始喜欢起听小时候的华语乐坛金曲，实在不喜欢现在的流行歌曲，完全没有听下去的欲望，感觉华语乐坛倒退了好多。</p><p>写了这么多，听歌品味的变化，我觉得是我听音乐的动机和方式不同了。小时候只有收音机和父亲的哼唱，所以喜欢老歌；初高中接触到了流行歌曲和粤语歌，所以听歌风格开始变化；到了大学，要静下心写代码，所以听小众歌曲，要练口琴，所以听口琴版；毕业工作了，更加向往自由，所以听乐队，越发觉得孤独，所以听老歌。</p>]]></content>
    
    
    <summary type="html">年龄越来越大，听的歌也在慢慢变化</summary>
    
    
    
    <category term="随笔" scheme="https://blog.felicx.eu.org/categories/%E9%9A%8F%E7%AC%94/"/>
    
    
    <category term="音乐" scheme="https://blog.felicx.eu.org/tags/%E9%9F%B3%E4%B9%90/"/>
    
  </entry>
  
  <entry>
    <title>Typecho 博客搭建</title>
    <link href="https://blog.felicx.eu.org/9399837.html"/>
    <id>https://blog.felicx.eu.org/9399837.html</id>
    <published>2024-10-20T04:28:34.000Z</published>
    <updated>2024-10-20T08:10:34.000Z</updated>
    
    <content type="html"><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>国庆前就搭好博客了，放假回来到现在，总算有时间整理下博客了。主要是前段时间看到个朋友圈主题的博客，挺适合我这种随手记的。</p><h2 id="数据库创建"><a href="#数据库创建" class="headerlink" title="数据库创建"></a>数据库创建</h2><p>之前写过一篇『<a href="https://blog.felicx.eu.org/1521393913.html">国内外一些免费的云数据库</a>』，挑来挑去选了 TiDB（<del>主要是使用 Supbase 死活无法连接 Typecho</del>）。</p><ol><li>创建数据库集群；<br><img src="/assets/post/20241020_BrQp44wY.webp" alt="img"></li><li>创建项目所需数据库；<br>CREATE DATABASE tidbcloud_WeChat_Moments_icefox; （可自定义为想起的数据库名称）<br><img src="/assets/post/20241020_VBC3Vv0w.webp" alt="img"></li><li>在 Overview 界面，点击右上角的 Connect 获得数据库连接参数；<br><img src="/assets/post/20241020_TCnyiBt4.webp" alt="img"></li><li>Endpoint Type 选择 Public，数据库选择为刚刚创建的数据库，保存好生成的参数；<br><img src="/assets/post/20241020_2sY3AU2R.webp" alt="img"></li><li><a href="https://letsencrypt.org/certs/isrgrootx1.pem">下载 CA 证书</a>，TiDB 的 Public 连接方式强制要求 TLS 连接，故还需配置其 CA 证书；</li></ol><h2 id="Typecho-程序"><a href="#Typecho-程序" class="headerlink" title="Typecho 程序"></a>Typecho 程序</h2><ol><li>打开<a href="https://github.com/typecho/typecho"> Github 链接</a>下载 Typecho 程序；</li><li>根目录新建 vercel.json，输入以下<figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="punctuation">&#123;</span></span><br><span class="line">  <span class="attr">&quot;functions&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">    <span class="attr">&quot;api/index.php&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">      <span class="attr">&quot;runtime&quot;</span><span class="punctuation">:</span> <span class="string">&quot;vercel-php@0.6.0&quot;</span></span><br><span class="line">    <span class="punctuation">&#125;</span></span><br><span class="line">  <span class="punctuation">&#125;</span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">&quot;routes&quot;</span><span class="punctuation">:</span> <span class="punctuation">[</span></span><br><span class="line">    <span class="punctuation">&#123;</span> <span class="attr">&quot;src&quot;</span><span class="punctuation">:</span> <span class="string">&quot;/(.*)&quot;</span><span class="punctuation">,</span> <span class="attr">&quot;dest&quot;</span><span class="punctuation">:</span> <span class="string">&quot;/api/index.php&quot;</span> <span class="punctuation">&#125;</span></span><br><span class="line">  <span class="punctuation">]</span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">&quot;regions&quot;</span><span class="punctuation">:</span> <span class="punctuation">[</span><span class="string">&quot;hkg1&quot;</span><span class="punctuation">]</span></span><br><span class="line"><span class="punctuation">&#125;</span></span><br></pre></td></tr></table></figure></li><li>根目录新建 &#x2F;api&#x2F;index.php，输入以下<figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?php</span></span><br><span class="line"><span class="variable">$file</span>= <span class="keyword">__DIR__</span> . <span class="string">&#x27;/..&#x27;</span>.<span class="variable">$_SERVER</span>[<span class="string">&quot;PHP_SELF&quot;</span>];</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span>(<span class="title function_ invoke__">file_exists</span>(<span class="variable">$file</span>))</span><br><span class="line">&#123;</span><br><span class="line">   <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">else</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">require_once</span> <span class="keyword">__DIR__</span> . <span class="string">&#x27;/../index.php&#x27;</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">#echo $_SERVER[&quot;PHP_SELF&quot;];</span></span><br></pre></td></tr></table></figure></li><li>将刚才下载的 CA 证书 isrgrootx1.pem 放在根目录；</li><li>选个主题，我挑的是 <a href="https://github.com/xiaopanglian/icefox">icefox</a>，放入 \usr\themes 目录下</li><li>将代码提交到 Github，等下要用 Vercel 引入；</li></ol><h2 id="Vercel-部署"><a href="#Vercel-部署" class="headerlink" title="Vercel 部署"></a>Vercel 部署</h2><p>Vercel 部署没什么好说的，注意要配置环境变量</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">TYPECHO_HOST=<span class="string">&quot;&quot;</span> <span class="comment"># 数据库地址</span></span><br><span class="line">TYPECHO_DATABASE=<span class="string">&quot;tidbcloud_WeChat_Moments_icefox&quot;</span> <span class="comment"># 数据库名称</span></span><br><span class="line">TYPECHO_USERNAME=<span class="string">&quot;&quot;</span> <span class="comment"># 数据库用户名</span></span><br><span class="line">TYPECHO_PASSWORD=<span class="string">&quot;&quot;</span> <span class="comment"># 数据库密码</span></span><br><span class="line">TYPECHO_PREFIX=<span class="string">&quot;typecho_&quot;</span> <span class="comment"># 前缀</span></span><br><span class="line">TYPECHO_ADAPTER_NAME=<span class="string">&quot;Pdo_Mysql&quot;</span> <span class="comment"># 数据库适配器</span></span><br><span class="line">TYPECHO_CHARSET=<span class="string">&quot;utf8mb4&quot;</span> <span class="comment"># 数据库编码</span></span><br><span class="line">TYPECHO_ENGINE=<span class="string">&quot;MyISAM&quot;</span> <span class="comment"># 数据库引擎</span></span><br><span class="line">TYPECHO_PORT=<span class="string">&quot;4000&quot;</span> <span class="comment"># 数据库端口</span></span><br><span class="line">TYPECHO_SSL_CA=<span class="string">&quot;isrgrootx1.pem&quot;</span> <span class="comment"># 数据库SSL证书位置</span></span><br></pre></td></tr></table></figure><p><img src="/assets/post/20241020_idZ9ZWGA.webp" alt="img"></p><h2 id="Typecho-安装"><a href="#Typecho-安装" class="headerlink" title="Typecho 安装"></a>Typecho 安装</h2><p>输入 Vercel 分配的网址+&#x2F;install.php，进入安装页面，跟着步骤走就行了。<br><img src="/assets/post/20241020_4x3p7Ntp.webp" alt="img"></p>]]></content>
    
    
    <summary type="html">Typecho + Vercel + TiDB，把白嫖用到极致</summary>
    
    
    
    <category term="折腾系列" scheme="https://blog.felicx.eu.org/categories/%E6%8A%98%E8%85%BE%E7%B3%BB%E5%88%97/"/>
    
    
    <category term="typecho" scheme="https://blog.felicx.eu.org/tags/typecho/"/>
    
  </entry>
  
  <entry>
    <title>技术岗位如何写简历（转）</title>
    <link href="https://blog.felicx.eu.org/1589846811.html"/>
    <id>https://blog.felicx.eu.org/1589846811.html</id>
    <published>2024-09-23T15:15:21.000Z</published>
    <updated>2024-09-23T15:30:21.000Z</updated>
    
    <content type="html"><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>最近在搜东西，无意中来到一位大佬的<a href="https://www.paincker.com/">博客</a>，看到了这篇<a href="https://www.paincker.com/how-to-write-a-resume/">技术岗位如何写简历</a>，这里做个转载，方便日后反复学习。</p><h2 id="明确目标"><a href="#明确目标" class="headerlink" title="明确目标"></a>明确目标</h2><p><strong>求职是一个双向选择的过程</strong>。投简历面试的过程应该尽可能<strong>充分且实事求是</strong>的展示个人能力和特点，让 HR 和面试官在最短的时间里了解你，判断是否适合岗位需要。</p><ul><li><p>能力问题。简历应该尽力展示自己的能力，但是如果目前能力还不够，抱着侥幸心理刻意夸大个人实力、隐瞒欺骗方式通过面试的做法不可取。即使通过面试了，如果个人能力差太多，工作业绩很不好，一样会被淘汰。</p></li><li><p>时机与合适问题。如果对自己的认知不够清晰，不了解自己喜欢和擅长什么，强行拿到并不适合自己的岗位，和自己擅长或者感兴趣的事情差异太大，每天上班都很难受，也没意思。反过来看，面试失败并不一定是因为能力不够，可能是因为目前招人的意愿不强，或者只是因为个人能力和岗位需求不匹配。</p></li></ul><p>双向选择的过程通常都可以拿谈恋爱来类比。</p><ul><li>为了追到对自己并不感兴趣的异性，一直努力迎合对方，从一开始身份就是不公平的，即使追到了也容易会被嫌弃。学习PUA技术欺骗女生的做法也不可取。</li><li>强行改变自己甚至迷失自我的做法也是不好的。被一个女生拒绝并不一定是不够优秀，可能是因为对方目前没心思谈恋爱，或者是因为不合适。例如某个女生就喜欢家庭条件一般能吃苦的人，而你偏偏是个富二代 (●ﾟωﾟ●)</li></ul><h2 id="换位思考"><a href="#换位思考" class="headerlink" title="换位思考"></a>换位思考</h2><p>多进行换位思考，站在面试官的角度想问题。很多有工作经验的人包括我自己也做过面试官，对面试官的处境有所了解。在短短几个小时的时间里了解一个候选人其实很困难，有时候面试完了也拿不定主意，不确定候选人到底合适不合适。面试官挖掘候选人的能力很重要，候选人自我展示的能力也很重要。</p><p>应聘岗位的关注点：</p><ul><li>大公司、负责成熟产品的团队，分工更加明确，招聘实际干活的开发人员时，通常比较注重基础知识，关注技术深度，希望求职者在某些领域有较为深入的研究（当然不可能所有领域都很深入），一方面这种技能可以解决团队遇到的该领域的疑难问题，另一方面也体现了候选人的钻研能力。</li><li>高职级的岗位，或者是小团队，可能会关心员工有没有创业精神、产品意识、技术广度、领导能力等。</li><li>开发人员的通用能力都会比较被重视，例如智商、逻辑思维、学习能力、解决问题能力、团队精神、沟通能力等。</li></ul><h2 id="简历内容组成"><a href="#简历内容组成" class="headerlink" title="简历内容组成"></a>简历内容组成</h2><p>关于简历具体的形式（应该写多长，写哪些内容），不同的人有不同的看法，这里主要说一下我的看法。</p><p>根据文章 <a href="https://novoresume.com/career-blog/cv-vs-resume-what-is-the-difference">CV vs Resume - What are the Differences &amp; Definitions [+ Examples]</a> 的介绍，Resume 和 CV 是两个不同的概念，Resume 更加精简，通常只有一页，用于各行各业的求职；而 CV 更加详细，用于学术相关的招生和招聘。</p><p>有很多“江湖传言”说简历只能写一页，理由是 HR 每天要看很多简历，太长了没时间看，这里的简历指的正是 Resume。但是对于开发岗位，最好要用类似 CV 的东西，从技术专业角度对项目做一些更详细的介绍。</p><p>借鉴 Resume 和 CV 的概念，针对社招开发岗位，简历可以分为几部分：</p><p>一、简历 (Resume)，保持精简，最好是一页纸。简历用于<strong>HR筛选</strong>，以及让面试官快速了解你的<strong>基本情况</strong>。</p><ul><li>基本信息：姓名、年龄、联系方式（电话、邮箱、微信），必要时加上居住地、求职意向。</li><li>教育经历：时间、城市、学校、专业、学历，必要时加上英语成绩。</li><li>工作和项目经验：因为要精简，并且通常会有重复内容，这两者可以压缩到一起。<ul><li>每段工作经历，基本信息可包括时间、城市、公司、团队、职位。</li><li>可以采用<strong>总 - 分结构</strong>描述。每段工作先用一句话做简单总结（例如公司、团队、项目规模、个人成长等），然后分几点介绍工作内容和成果。每一点又可以先用一句话概括，再详细介绍。</li><li>关键项目的介绍，说清楚自己<strong>扮演的角色</strong>，是独立完成、项目负责人还是参与者，也可以注明大致的贡献占比。使用技术关键字（例如开发语言、工具），但不用写技术细节。成果要有说服力，多使用<strong>数据展示</strong>（例如开源项目 GitHub Star 2k+，性能提升 30% 等）。重点关键词可加粗，还可以插入相关网址链接（当然电子简历才好用链接，纸质简历就不太方便了）。</li><li>如果项目多，又有博客，可以写“更多项目详见个人博客”并插入链接。</li><li>做到让 HR 这样的非技术人员也能大致看到你的实力，并能根据技术关键字判断你的技能和岗位需求是否匹配。</li></ul></li><li>自我评价：可以列举自己的优点，一定要有<strong>实际案例支撑</strong>（可直接在括号中简要说明），否则就成了没有说服力的空话了。</li><li>个人技能：最擅长的东西应该在项目经验中已经介绍过了，这里列举你用过的所有编程语言、框架等技术关键字，主要作用是<strong>体现技术广度</strong>。使用“入门”、“了解”、“熟练”等描述，<strong>慎用精通</strong>，因为容易翻车。如果写了精通，面试官碰巧有了解这方面，可能会问一些比较难的问题，被问倒了就会让人怀疑你是否诚实了。也可以使用图形例如五角星来描述熟练度。</li></ul><p>二、履历 (CV) 或附录，是简历的补充，内容相对详细，长度可以有多页。</p><ul><li>内容可以包括重点项目经验的技术实现细节和关键点等。可以借鉴 STAR 原则说明，即 Situation（情景）、Task（任务）、Action（行动）和Result（结果），但也要根据实际进行调整，不能生搬硬套。</li><li>面试官如果对你简历中的某个项目感兴趣可以具体看 CV；同时在面试聊项目时也起到提纲的效果，如果担心一时想不起来，可以对照 CV 介绍。</li><li>形式上，建议附在简历末尾。也可以考虑写到个人博客中，在简历里贴链接。</li></ul><p>三、其他。</p><ul><li>个人博客：如果有还不错的个人博客，可以贴到简历里。个人博客如果写的好，<strong>远比简历的参考价值要大</strong>。</li><li>GitHub：如果 GitHub 比较活跃，有个人项目，可以贴到简历里。有一定技术含量、Star 较多的开源项目，通常是个<strong>很大的加分项</strong>。</li><li>Demo：对于客户端 App、网站之类的项目，必要时可以准备好项目、Demo、截图、网址等，面试官可以自行查看，或者现场给面试官演示，帮助进行说明。</li></ul><h2 id="项目经验的整理"><a href="#项目经验的整理" class="headerlink" title="项目经验的整理"></a>项目经验的整理</h2><p>有一定工作经验的人通常会有较多项目经验。为了保持简历的简洁，需要挑选最重点的内容来写。写项目经验时可以按照这样的方式进行：</p><ul><li>使用思维导图，将自己过去所有感觉还可以的项目都列出来，想到什么都写上。</li><li>对每个项目的成果如何展示、能反映什么样的能力特点做思考。例如复杂的业务，说明自己业务能力较好；技术项目，说明自己有一定的技术深度；某些小工具项目，说明自己注重工具的使用；个人开源项目，说明自己热爱技术等。</li><li>挑选几个最有代表性的、比较容易说明和展示的项目，做相对详细的分析，根据项目的重要性，介绍的篇幅也需要有控制。</li><li>最后将选定的项目和介绍压缩到简历上。</li><li>其他没有被选中但是也还不错的项目，可以通过博客或附录的形式展示。</li></ul><h2 id="内容与排版"><a href="#内容与排版" class="headerlink" title="内容与排版"></a>内容与排版</h2><ul><li>整体结构要清晰，避免逻辑混乱和内容冗余，和写文章类似。例如工作经验和项目经验，常会有很多重复内容，可以合并到一起。</li><li>文字表述要简单明了。如果觉得内容较少，不要刻意凑字数，简历字数和个人经验能力没有直接联系。</li><li>排版要整洁，版面要匀称。例如前面很拥挤，但是后面却有大块空白，就会很不匀称。字数较少可以适当加大字体、增加行间距、增加页面距等。字数太多，首先考虑压缩文字，实在没法压缩再从排版上考虑。</li><li>尽量避免错别字和标点符号问题。这会让人觉得你做事情不认真。</li></ul><h2 id="简历编辑工具"><a href="#简历编辑工具" class="headerlink" title="简历编辑工具"></a>简历编辑工具</h2><p>简历有多种编辑工具：</p><ul><li>Word。不太建议使用，想让简历稍微精致一点，Word 不太好实现。</li><li>Markdown。我使用的是这种方式，自己修改了 CSS 调整格式。</li><li>LaTeX。LaTeX 的排版效果很好，但是环境配置相对复杂，LaTeX 语法也需要一点时间掌握。我尝试用了大佬的 <a href="https://github.com/billryan/resume">LaTeX简历模板</a>，但是编译后的效果不对，可能是兼容性问题，一时不好解决，就放弃了。</li><li>PhotoShop 等设计工具，可以制作出画面更加精美的简历。比较耗时并且考验审美能力，对于技术开发岗位没太大必要，更适合设计师类岗位。</li><li>在线简历模板。例如 <a href="https://www.wondercv.com/">超级简历</a> 。</li></ul>]]></content>
    
    
    <summary type="html">转自一位博主的写简历大法</summary>
    
    
    
    <category term="随笔" scheme="https://blog.felicx.eu.org/categories/%E9%9A%8F%E7%AC%94/"/>
    
    
    <category term="职场" scheme="https://blog.felicx.eu.org/tags/%E8%81%8C%E5%9C%BA/"/>
    
  </entry>
  
</feed>
