RT-Thread中的GithubActions

RT-Thread中一共有五个Github Action(rt-thread/.github/workflow)

分别是:

  • RT-Thread BSP build check(.github/workflows/action.yml)

  • ToolsCI(.github/workflows/action_tools.yml)

  • AutoTestCI(.github/workflows/action_utest.yml)

  • Check File Format and License(.github/workflows/file_check.yml)

  • Static code analysis(.github/workflows/static_code_analysis.yml)

下面分别讲解这五个Github Action。


RT-Thread BSP build check

总的来说,这个Action会通过matrix尝试编译多个BSP,并记录编译成功和失败的信息。执行脚本中的每个BSP编译步骤都在日志中创建了一个分组,以便在编译成功或失败时可以更好地显示和记录相关信息。


matrix列表中每个元素有三个属性,分别是


  • RTT_BSP:这组BSP的名字,后续输出日志时会用到

  • RTT_TOOL_CHAIN:编译这组BSP使用的工具链

  • SUB_RTT_BSP:各个BSP的目录


RT-Thread BSP build check的第一步:

  • 首先先安装gcc和menuconfig依赖的包

  • 调用tools目录下的menuconfig的touch_env


  • 这个函数主要是创建一系列后续会使用到的文件夹,并且拉取远程的packages

  • 以及修改Kconfig

  • 设置一些RT-Thread自己的环境遍历,供后续使用

 1name: Install Tools
2shell: bash
3run: |
4  sudo apt-get update
5  sudo apt-get -qq install gcc-multilib libncurses5 libncurses5-dev libncursesw5-dev scons
6  sudo python -m pip install --upgrade pip -qq
7  pip install requests -qq
8  git config --global http.postBuffer 524288000
9  python -c "import tools.menuconfig; tools.menuconfig.touch_env()"
10  echo "RTT_ROOT=${{ github.workspace }}" >> $GITHUB_ENV
11  echo "RTT_CC=gcc" >> $GITHUB_ENV

RT-Thread BSP build check的第二步:

  • 会根据matrix.legs.RTT_TOOL_CHAIN判断需要安装什么工具链


RT-Thread BSP build check的第三步:

第三步是这次BSP编译测试的核心


  • 首先会遍历所有的SUB_RTT_BSP

  • 根据scons命令执行的成功与否(||前一个命令执行失败、&&前一个命令执行成功)来判断执行成功还是失败的逻辑

  • 输出的时候会在GitHub Actions日志中创建一个分组,用于显示BSP编译的信息

  • 计算总共花费的时间,输出BSP编译成功或者失败的信息,输出至$GITHUB_STEP_SUMMARY

 1name: Bsp Scons Compile
2if: ${{ success() }}
3shell: bash
4env:
5  RTT_BSP: ${{ matrix.legs.RTT_BSP }}
6  RTT_TOOL_CHAIN: ${{ matrix.legs.RTT_TOOL_CHAIN }}
7  SRTT_BSP: ${{ join(matrix.legs.SUB_RTT_BSP, ',') }}
8run: |
9  source ~/.env/env.sh
10  failed=0
11  count=0
12  for bsp in $(echo $SRTT_BSP | tr ',' '\n'); do
13    count=$((count+1))
14    echo "::group::Compiling BSP: ==$count=== $bsp ===="
15    echo bsp/$bsp
16    pushd bsp/$bsp && pkgs --update && popd
17    scons -C bsp/$bsp -j$(nproc) --debug=time | tee output.log || \
18    { total_time=$(grep "Total command execution time" output.log | awk '{print $5}'); \
19      failed=$((failed+1)) ; echo "::endgroup::" ; echo "::error::build $bsp failed" ; \
20      echo "- ❌ build $bsp failed in $total_time seconds " >> $GITHUB_STEP_SUMMARY ; } && \
21    { total_time=$(grep "Total command execution time" output.log | awk '{print $5}'); \
22      echo "- ✅ build $bsp success in $total_time seconds " >> $GITHUB_STEP_SUMMARY ; echo "::endgroup::" ; }
23  done
24  exit $failed

ToolsCI

总的来说,ToolsCI这个Action比较简单,会去尝试Build一个BSP、生成其它工程文件等等,用来测试使用。

关于scons一些参数的使用可以参考:SCons (rt-thread.org)

 1name: ToolsCI
2# Controls when the action will run. Triggers the workflow on push or pull request
3# events but only for the master branch
4on:
5  # Runs at 16:00 UTC (BeiJing 00:00) on the 1st of every month
6  schedule:
7    - cron:  '0 16 1 * *'
8  push:
9    branches:
10      - master
11    paths-ignore:
12      - documentation/**
13      - '**/
README.md'
14      - '**/README_zh.md'
15      - '**/*.c'
16      - '**/*.h'
17      - '**/*.cpp'
18  pull_request:
19    branches:
20      - master
21    paths-ignore:
22      - documentation/**
23      - '**/
README.md'
24      - '**/README_zh.md'
25      - '**/*.c'
26      - '**/*.h'
27      - '**/*.cpp'
28permissions:
29  contents: read # to fetch code (actions/checkout)
30jobs:
31  test:
32    runs-on: ubuntu-latest
33    name: Tools
34    strategy:
35      fail-fast: false
36    env:
37      TEST_BSP_ROOT: bsp/stm32/stm32f407-atk-explorer
38    steps:
39    - uses: actions/checkout@v3
40    - name: Install Tools
41      shell: bash
42      run: |
43        sudo apt-get update
44        sudo apt-get -yqq install scons
45    - name: Install Arm ToolChains
46      if: ${{ success() }}
47      shell: bash
48      run: |
49        wget -q https://github.com/RT-Thread/toolchains-ci/releases/download/v1.3/gcc-arm-none-eabi-10-2020-q4-major-x86_64-linux.tar.bz2
50        sudo tar xjf gcc-arm-none-eabi-10-2020-q4-major-x86_64-linux.tar.bz2 -C /opt
51        /opt/gcc-arm-none-eabi-10-2020-q4-major/bin/arm-none-eabi-gcc --version
52        echo "RTT_EXEC_PATH=/opt/gcc-arm-none-eabi-10-2020-q4-major/bin" >> $GITHUB_ENV
53    - name: Build Tools
54      run: |
55        scons --pyconfig-silent -C $TEST_BSP_ROOT
56        scons -j$(nproc) -C $TEST_BSP_ROOT
57    - name: Project generate Tools
58      if: ${{ success() }}
59      run: |
60        echo "Test to generate eclipse project"
61        scons --target=eclipse -s -C $TEST_BSP_ROOT
62        echo "Test to generate cmake project"
63        scons --target=cmake -s -C $TEST_BSP_ROOT
64        echo "Test to generate makefile project"
65        scons --target=makefile -s -C $TEST_BSP_ROOT
66    - name: Project dist Tools
67      if: ${{ success() }}
68      run: |
69        echo "Test to dist project"
70        scons --dist -C $TEST_BSP_ROOT
71        scons --dist-ide -C $TEST_BSP_ROOT
72        ls $TEST_BSP_ROOT
73        ls $TEST_BSP_ROOT/dist
74        scons -C $TEST_BSP_ROOT/dist/project
75        scons -C $TEST_BSP_ROOT/rt-studio-project

AutoTestCI

总的来说,这个Action的主要目标是根据matrix中的不同参数组合,安装必要的工具链、构建和测试代码。具体的步骤包括检出代码、安装所需工具、设置环境变量、构建代码,然后在qemu中运行测试并输出日志。根据参数的不同,这个工作流可以自动处理多个平台和架构的测试。


matrix列表中每个元素有五个属性,分别是


  • UTEST:这组BSP的名字,后续输出日志时会用到

  • RTT_BSP:测试使用的BSP

  • QEMU_ARCH:QEMU使用的平台架构

  • QEMU_MACHINE:选择QEMU的板级支持包

  • CONFIG_FILE:RT-Thread条件编译使用的CONFIG

  • SD_FILE:使用的sd.bin

  • RUN:是否启动


AutoTestCI的第一步:

安装必要的工具:scons、qemu、git


AutoTestCI的第二步:

根据matrix的属性选择安装相应的编译工具链

比如要测试arm架构和rtsmart/arm时,我们就选择安装arm-linux-musleabi_for_x86_64-pc-linux-g工具链

1name: Install Arm Musl ToolChains
2if: ${{ matrix.legs.QEMU_ARCH == 'arm' && matrix.legs.UTEST == 'rtsmart/arm' && success() }}
3shell: bash
4run: |
5  wget -q https://github.com/RT-Thread/toolchains-ci/releases/download/v1.7/arm-linux-musleabi_for_x86_64-pc-linux-g
6  sudo tar xjf arm-linux-musleabi_for_x86_64-pc-linux-gnu_stable.tar.bz2 -C /opt
7  /opt/arm-linux-musleabi_for_x86_64-pc-linux-gnu/bin/arm-linux-musleabi-gcc --version
8  echo "RTT_EXEC_PATH=/opt/arm-linux-musleabi_for_x86_64-pc-linux-gnu/bin" >> $GITHUB_ENV
9  echo "RTT_CC_PREFIX=arm-linux-musleabi-" >> $GITHUB_ENV

AutoTestCI的第三步:

第三步主要完成$TEST_BSP_ROOT下BSP的编译,以供后续在qemu上使用

1name: Build BSP
2run: |
3  echo CONFIG_RT_USING_UTESTCASES=y >> $TEST_BSP_ROOT/.config
4  cat examples/utest/configs/$TEST_CONFIG_FILE >> $TEST_BSP_ROOT/.config
5  scons --pyconfig-silent -C $TEST_BSP_ROOT
6  scons -j$(nproc) --strict -C $TEST_BSP_ROOT

AutoTestCI的第四步:

第四步也是最重要的一步,开始测试相关用例

 1name: Start run Test
2if: ${{matrix.legs.RUN == 'yes' && success() }}
3run: |
4  git clone https://github.com/armink/UtestRunner.git
5  pushd $TEST_BSP_ROOT
6  dd if=/dev/zero of=sd.bin bs=1024 count=65536
7  popd
8  pushd UtestRunner
9  if [ $TEST_SD_FILE != "None" ]; then
10    python3 qemu_runner.py --system $TEST_QEMU_ARCH --machine $TEST_QEMU_MACHINE --elf ../$TEST_BSP_ROOT/rtthread
11  else
12    python3 qemu_runner.py --system $TEST_QEMU_ARCH --machine $TEST_QEMU_MACHINE --elf ../$TEST_BSP_ROOT/rtthread
13  fi
14  cat rtt_console.log
15  popd

  • 拉取RT-Thread自动化测试机器人

  • 制作sd.bin

  • 使用需要测试的BSP启动qemu

  • 最后输出相关日志


Check File Format and License

这个Action主要是用来检查文件的格式化和版权信息的,主要工作都由tools/ci/file_check.py完成,在此之前主要先检出当前仓库的代码和安装Python脚本依赖的包。

 1name: Check File Format and License
2on: [pull_request]
3jobs:
4  scancode_job:
5    runs-on: ubuntu-latest
6    name: Scan code format and license
7    steps:
8      - uses: actions/checkout@v3
9      - name: Set up Python
10        uses: actions/setup-python@v3
11        with:
12          python-version: 3.8
13      - name: Check Format and License
14        shell: bash
15        run: |
16          pip install click chardet PyYaml
17          python tools/ci/file_check.py check 'https://github.com/RT-Thread/rt-thread' 'master'

file_check.py

我们可以先忽略使用的click命令行库,或者也可以从命名和使用方式猜测出它们的功能。


因为这个文件比较简单,所以我们可以猜测函数的入口就是check()。主函数里的逻辑是十分简单的,可以看到通过checkout.get_new_file()获得了一个文件列表,然后传递给了FormatCheckLicenseCheck,它们又分别调用了自身的check函数,最后根据它们返回值判断是否检查出错误。

 1def check(check_license, repo, branch):
2    """
3    check files license and format.
4    """
5    init_logger()
6    # get modified files list
7    checkout 
= CheckOut(repo, branch)
8    file_list = checkout.get_new_file()
9    if file_list is None:
10        logging.error("checkout files fail")
11        sys.exit(1)
12    # check modified files format
13    format_check = FormatCheck(file_list)
14    format_check_result = format_check.check()
15    license_check_result = True
16    if check_license:
17        license_check = LicenseCheck(file_list)
18        license_check_result = license_check.check()
19    if not format_check_result or not license_check_result:
20        logging.error("file format check or license check fail.")
21        sys.exit(1)
22    logging.info("check success.")
23    sys.exit(0)

checkout.get_new_file()获得的文件列表是需要检查的文件列表,而FormatCheckLicenseCheck执行各自的检查逻辑。


首先是get_new_file,具体的逻辑也比较简单:


1. 通过git命令获得新增和修改的文件列表

2. 然后遍历这个文件列表

3. 遍历这个文件列表中的文件路径的每一层目录,看是否存在.ignore_format.yml文件

4. 然后根据.ignore_format.yml的属性来判断当前文件是否需要被检查

FormatCheck主要完成的工作是:


1. 搜索所有.c和.h文件

2. 然后检查行首、行尾以及tab

LicenseCheck的逻辑也比较简单,主要就是判断当前的Copyright的年份是否正确。

 1if 'Copyright' in file[1and 'SPDX-License-Identifier: Apache-2.0' in file[3]:
2    try:
3        license_year = re.search(r'2006-\d{4}', file[1]).group()
4        true_year = '2006-{}'.format(current_year)
5        if license_year != true_year:
6            logging.warning("[{0}]: license year: {} is not true: {}, please update.".fo                                                                   
7        else:
8            logging.info("[{0}]: license check success.".format(file_path))
9    except Exception as e:
10        logging.error(e)
11else:
12    logging.error("[{0}]: license check fail.".format(file_path))
13    check_result = False

Static code analysis

这个Action和Check File Format and License是很类似的,主要流程都是相同的。

最重要的就是利用cppcheck完成静态代码检查的功能:

1. 从文件列表中再一次过滤出C/C++相关文件
2. 然后使用cppcheck逐个检查文件列表,并且捕获标准错误流
 1class CPPCheck:
2    def __init__(self, file_list):
3        self.file_list = file_list
4    def check(self):
5        file_list_filtered = [file for file in self.file_list if file.endswith(('.c''.cpp''.cc''.cxx'))]
6        logging.info("Start to static code analysis.")
7        check_result = True
8        for file in file_list_filtered:
9            result = subprocess.run(['cppcheck''--enable=warning''performance''portability''--inline-suppr''--error-exitcode=1''--force', file], stdout = subprocess.PIPE, stderr = subprocess.PIPE)
10            logging.info(result.stdout.decode())
11            logging.info(result.stderr.decode())
12            if result.stderr:
13                check_result = False
14        return check_result
15@click.group()
16@click.pass_context
17def cli(ctx):
18    pass
19@cli.command()
20def check():
21    """
22    static code analysis(cppcheck).
23    "
""
24    format_ignore.init_logger()
25    # get modified files list
26    checkout = format_ignore.CheckOut()
27    file_list = checkout.get_new_file()
28    if file_list is None:
29        logging.error("checkout files fail")
30        sys.exit(1)
31    # use cppcheck
32    cpp_check = CPPCheck(file_list)
33    cpp_check_result = cpp_check.check()
34    if not cpp_check_result:
35        logging.error("static code analysis(cppcheck) fail.")
36        sys.exit(1)
37    logging.info("check success.")
38    sys.exit(0)
39if __name__ == '__main__':
40    cli()


原文:https://club.rt-thread.org/ask/article/102cc126ad4dbfd8.html

———————End———————


RT-Thread线下入门培训

6月 - 郑州、杭州、深圳


1.免费 2.动手实验+理论 3.主办方免费提供开发板 4.自行携带电脑,及插线板用于笔记本电脑充电 5.参与者需要有C语言、单片机(ARM Cortex-M核)基础,请提前安装好RT-Thread Studio 开发环境



立即扫码报名



报名链接

https://jinshuju.net/f/UYxS2k

巡回城市:青岛、北京、西安、成都、武汉、郑州、杭州、深圳、上海、南京


你可以添加微信:rtthread2020 为好友,注明:公司+姓名,拉进RT-Thread官方微信交流群!



👊点击阅读原文,进入RT-Thread 官网


RTThread物联网操作系统 帮助您了解RT-Thread相关的资讯.
评论
  • 在高速数据传输的现代世界中,光模块扮演着至关重要的角色。它们如同信息高速公路上的“快递员”,负责将电信号转换为光信号进行远距离传输,再将光信号转换回电信号供设备使用。然而,在这看似简单的光-电转换过程中,有一个不起眼却不可或缺的元件在默默工作——它就是晶振,或称石英晶体振荡器。晶振:电子设备的“心跳”晶振的核心功能是产生稳定的时钟信号。想象一下,如果一场音乐会没有统一的节拍,演奏会变得混乱不堪。同样,在电子设备中,晶振提供的高精度时钟信号就像乐队的指挥,确保所有部件同步工作。在光模块中,这个“指
    TKD泰晶科技 2026-01-04 15:34 1810浏览
  • 1. 概述与核心目标本测评聚焦于 Raspberry Pi 最新推出的双核 Cortex-M33 微控制器 RP2350,探索其在业余无线电卫星通信场景下的可行性。通过集成 Semtech SX1255 射频收发器,构建一套兼具低成本、低功耗、高灵活性的通信节点。核心目标:验证 RP2350 + SX1255 组合在 435–438 MHz 业余卫星频段 的通信能力;评估其作为 LEO 纳卫星(CubeSat)载荷 的功耗、算力与可靠性;打造面向青少年与无线电爱好者的 开源科普套件,支持从基础
    牧童颜罗马 2025-12-28 12:13 2139浏览
  • 分享一个最新的便携微孔雾化装置的原理图,主要功能为驱动微孔雾化,兼具外围的升压和检水。适合:可以适用于医用微网雾化器、桌面加湿器、补水仪或者香氛仪的雾化驱动电路参考。特点:整体硬件成本低,具备自动扫频追频以及自动检水功能,按键和指示灯设计方便简洁主要逻辑功能需求雾化片:108KHz,16mm直径,微孔超声波雾化片雾化驱动:自动扫频追频,雾化量另行标定,名义雾化速率:4mg/s按键1: 长按开机,再次长按关机;关机状态下进入低功耗以节省电池电量;按键1在开机状态下循环,高-中-低档位控制雾化速率档
    努力精进的硬件 2025-12-30 09:53 1196浏览
  • 在智能家居的网络架构中,Wi-Fi、蓝牙、Zigbee与Thread等通信协议是实现设备无线互联与协同控制的底层逻辑。然而,这些协议在标准体系、寻址方式与网络管理机制上却彼此独立,缺乏统一的互操作框架,在进行跨协议组网时需要依赖中心网关作为“翻译桥梁”,这不仅抬高了全屋智能的部署成本,还增加了系统的网络复杂度与不稳定性。在此背景下,行业迫切地需要一种能跨协议、跨生态与跨品牌通信的统一标准来破局,从而在根本上解决智能家居场景中设备难互联、生态难融合与通信不稳定等问题,将智能家居从“平台主导”阶段全
    华普微HOPERF 2026-01-07 11:08 1332浏览
  •        面包板社区选品绝对优品!首先感谢面包板及工作人员在这之前策划了很多可以赚取E币的活动(发技术帖、分享项目经验、回答问题、参会、11-12月每天坚持签到领E币),每一个都几乎与了,所以获利不少,这次活动力度之大更是前所未有,买东西返现!!!再次感谢,比心!!!来看看我买了哪些好宝贝:                     
    甜椒的尾巴 2025-12-31 09:37 2544浏览
  • 2026年了,过去的一年工作和家庭事情都比较多,来面包板比较少。2026年事情好了很多,可以多来面包板分享自己的工作。 在新的一年里。工作方面,继续努力,自己的公司把业务都办理完成,能顺利营业就可以,主要也是为了自己工作方便。主业还是多参加比赛。具体的目标:(1)公司工商注册等都完成。(2)参加教学比赛2次。(3)完成论文3篇,一个项目论文,一个会议论文,这个是确定的,必须完成,自己在写一个论文。(4)参加一次技能比赛,视觉的比赛。 有时候运气也很重要,不是自己的也不去强求了
    curton 2026-01-05 09:59 77702浏览
  • 文:郭楚妤编辑:cc孙聪颖在硬核的美国科技展上,看到软萌的国宝大熊猫,是不是有一种反差感?这次,长虹将这份独特的“AI科技+国宝熊猫文化”带到了美国拉斯维加斯的CES展上,通过熊猫主题AI家电,以及全品类AI产品,在全球舞台上讲述着属于“东方智慧”的故事。于是,在此次CES展会上,你可以看到AI科技被赋予了文化的温度和脉络。打开长虹AI TV,智能体伙伴“熊猫小白”上线,化身全天候全场景的陪伴者。打开电视里的“熊猫乐园”,能够实时看到熊猫啃竹子、玩耍的治愈画面,仿佛把四川的竹林生态带到了身边。长
    华尔街科技眼 2026-01-08 15:22 1334浏览
  • 文:候煜、陈昊编辑:cc孙聪颖多年前,中国入世谈判首席代表龙永图在接受《华尔街科技眼》创始人采访时曾抛出一个鲜明观点:家电行业是国内“最皮实”的行业。在入世初期各领域普遍需要政策保护,唯独家电行业未受特殊庇护,却走出了相对稳健的发展路径。无论是全球市场知名度,还是行业整体营收规模,家电行业长期保持着稳固态势。而且,相较于始终站在风口、聚焦聚光灯下的互联网行业,家电行业始终略显低调。“稳”意味着行业变数少,也意味着从业者可以有一定的路径依赖。曝光度低意味着相对较少的关注和解读,于职业经理人而言,反
    华尔街科技眼 2025-12-30 12:56 2295浏览
  •   今天,做清理,想把闲置有点毛病了的一个老收录机看看还能不能用?  拿起电源线,呵呵,这线早就变硬了,拿到插头,准备插到电源插座上,哈哈,不声不响,插头断掉啦!  拿到了工作台,准备行动,拆解、检查、判断可否修复?  这可是一台够老的机器啦!1985年7月3日买的一台三洋牌手提式立体声收录机,那时是很时兴时髦的产品,发票和说明书都在。  前后左右上下都看了看,   SANYO STEREO RADIO CASSETTE RECORDER MODEL NO. M9805F  SANYO ELE
    自做自受 2026-01-06 21:15 1682浏览
  • ESP32S3小智开发板烧录指南ESP32S3小智开发板烧录核心需做好硬件接线、烧录模式操作与软件配置,步骤如下: 1. 硬件接线:用USB转TTL模块连接,3.3V接板载3.3V(严禁5V)、GND共地,模块TX接开发板RX(GPIO44)、RX接TX(GPIO43),确保接线无松动。 2. 进入烧录模式:按住板载BOOT键不松,快速按EN复位键,先松EN再松BOOT,此时开发板进入烧录模式。  3. 软件配置:IDF环境下先执行`idf.py set-
    丙丁先生 2026-01-10 12:33 69321浏览
  • 2026年1月6日,美国CES展会上有一则重磅消息,芬兰初创公司Donut Lab正式推出全球首款可量产全固态电池,该电池不仅实现5分钟满电、10万次循环寿命的性能飞跃,更已完成OEM量产适配,搭载该电池的电动摩托车将于今年第一季度交付用户。这一突破标志着长期停留在实验室阶段的全固态电池技术正式迈入商业化落地阶段,有望彻底解决传统锂电池续航短、充电慢、安全隐患三大核心痛点,为新能源产业带来颠覆性变革。这款全固态电池的性能参数堪称“碾压级”超越传统锂电池。据官方披露,其能量密度达到400Wh/kg
    面包超人Tech 2026-01-09 09:23 1490浏览
  •         本月17日南京地区发生了一次导航严重偏离的事件,一时互联网、金融、军迷等人群议论纷纷。19号,“南京卫星协会”微信公众号给出了一篇分析文章,官媒和社媒纷纷转载,视为结案。但是不知道为什么,今天我去找了一下,没找到这篇文章。                        根据公开资料,我把北斗、GPS和G
    电子知识打边炉 2025-12-28 22:55 981浏览
  • 在全球变暖、环境污染与生物多样性下降的生态危机下,发展科技的目的已不仅仅在于为各行各业提质增效,还在于促进人与自然的和谐发展。LoRaWAN作为一种面向大规模部署应用的低功耗IoT通信技术,正凭借着低功耗、远距离、大规模连接和低部署成本等特性,成为连接人类社会与自然生态的重要“沟通桥梁”。这座“沟通桥梁”不仅能将自然环境中原本难以察觉、零散分布的生态变化持续转化为可采集与分析的数据形态,使森林、河流、湿地与野生生物“开口说话”,还能帮助管理者在广袤、偏远、环境恶劣的区域中长期、稳定地获取生态数据
    华普微HOPERF 2026-01-13 15:58 327浏览
  • 坚 守2025年于风雨飘摇中逝去。多年来,我如老牛耕垦,在这片希望的田野上持续耕耘。尽管每年收成不一、亦因人而异,但“老骥伏枥,志在千里”的古训,始终激励着我前行。过去一年,职场中虽遇波澜,然心境渐趋平和。恰如苏轼《观潮》诗中所喻:庐山烟雨浙江潮,未至千般恨不消。到得还来别无事,庐山烟雨浙江潮。历事后方知,潮起潮落不过常态,唯有坚守本心、专注所为,方能穿透迷雾、踏实前行。一、专利布局与维护全年围绕核心技术及新产品,累计申报发明专利3项、实用新型专利1项,其中2项发明专利已进入实质审查阶段。系统
    广州铁金刚 2026-01-09 10:40 1490浏览
我要评论
0
点击右上角,分享到朋友圈 我知道啦
请使用浏览器分享功能 我知道啦