1.3 亲历高复用研发模式

机缘巧合下,我加入了一家跨国公司并参与某关键工控产品的研发,然后我惊奇的发现其研发模式完全迥异于我以前的工作模式。这次经历不仅让我大开眼界,也开始倒逼自己认真思考这种工作模式的优缺点。为何要这样做?这样做的优点是什么?又是如何一步步迭代到目前这个样子的呢?

这种研发模式的核心是努力提升工作(包含代码,设计文档甚至培训讲义等,但本书以代码复用为切入点)的复用率,本章我会和大家分享一些曾经让自己很震惊的特征。

在我所在的公司中,大家习惯将这种研发模式称之为“平台化”模式。不过平台化这个词估计早就被用烂了,担心大家要望词乱生义,我喜欢称之为高复用研发模式,但也不可避免的会大量使用到平台化这个词语,希望大家不要误解。

1.3.1 夸张的代码复用率

我刚加入该项目组时,是由一位做规约开发的工程师带我熟悉整个研发流程。借助这个机会,我有幸阅读了他实现的一个规约程序,虽然仅仅是电力系统最常见的IEC104远动规约,但让我在内心中重新定义了“代码复用”这个概念。

我们平时做产品,代码复用是经常的事情。我的产品中需要某个功能,而恰好你的产品中已经实现过了,找你领导商量一下,然后将代码拷过来改一改,OK,搞定。这可能就是我们认为的代码复用。

与我们不一样,该规约项目组复用时不是直接将代码拷贝给其他项目组,而仅仅将编译后的中间文件和头文件导入代码库,其他项目组用配置软件直接导出复用就好。

大家看出关键差异了吗,其关键就在于我们需要改一改,而这儿不需要。因为不需要将一份代码维护成多个版本,这才是一种真正的复用。注意,从今而后,本书中一旦提到复用,都是指这种一个字符都不用修改的复用策略。

做过产品的工程师都知道,即使同样是104规约,很多产品线都存在着多多少少的差异,各个市场也会提出一些特殊的需求。这种情况下,是如何做到代码完全复用的呢。我阅读并分析代码后,发现关键需要做到如下几方面的工作:

  1. 需要抽象提炼各类数据模型api接口。
    规约程序需要访问很多具体的数据模型,如遥测、遥信等,这些数据模型在各类产品中概念大致相似,但或多或少的会存在细节上的差异,且会随着需求而持续变化。为了规约程序不受影响,就需要细致的提炼数据模型api接口。

  2. 构建动态执行架构,兼容前后台,实时OS或linux等多种运行环境。
    工控产品应用领域差异很大,因成本或需求要求不同,有些是前后台裸系统,有些则会使用复杂的强实时OS,或者使用linux系统。大家都知道前后台系统是简单的函数调用,而OS中是一个个的死循环任务函数,不想分别实现一套104规约,就需要构建抽象层,兼容各种运行环境。

  3. 构建规约数据参数配置表,需要维护软件或配置软件的支撑。
    各类装置虽然都使用104规约机制传输,但传输的具体数据却存在很多差异。因此,想让规约代码可复用,代码具体实现中就不能涉及任何特定的数据,用IT术语来说,规约代码只能针对数据接口进行编程。为了应对这种情况,需要额外构建各装置的数据接口配置表。如果规约传输的数据允许用户现场设置,该部分功能一般会内嵌在维护软件中,如果仅允许在厂内修改或合作伙伴二次开发,经常会内嵌在配置软件中(配置软件和维护软件会在后续框架设计中详述)。

  4. 可扩展程序架构,以支持各种二次开发环境。
    104规约虽然是国际标准,但在具体实施的过程中,大家总是会忍不住去扩展或修改它,因此会诞生各种区域版本(我实现过特殊的中东版本)、工程版本、特殊需求版本等。如何应对这种需求呢?最好的策略是分层,将104规约最基本的功能作为可复用模块进行构建,并提供二次修改的机制。最常见的二次扩展机制有参数文件、脚本和二次开发环境等(这些机制各有优缺点,在后续框架设计中都会提及)。

  5. 依据需求持续优化迭代。
    任何设计初期都可能存在不足,有些地方会考虑不周,而有些扩展选项又从未用过等。在实际工作中,我们经常是保持程序架构稳定,但会持续的优化细节。打磨的时间久了,我们就会惊奇的发现针对某一行业的程序改动频率会越来越小,此时,可复用的104规约模块就构建完毕了。

至此,我们终于完成了可复用104规约模块的构建。不知大家有没有意识到,其中提及的api接口、动态执行框架、配置软件、维护软件、脚本系统、二次开发环境等都不是104规约范围内的概念,为了完成104规约的复用,需要诸多模块配合,甚至平台级的支撑,而这,也正是可复用模块不容易做到的底层原因。

困难是有的,但如果你能闭眼想一想,假如我们的产品中,甚至我们部门负责的几条产品线,更甚至我们整个公司,所有的软件模块都是以类似这样的模式构建的,会是怎样的一种情景呢。下图是我当初参与具体研发工作时导出的可复用模块接口配置表(留意滚动条,最终会通过配置软件读取生成接口文件并下载到设备中),大家能感受到我当初的那种震撼心理吗?
在这里插入图片描述
基于大量可复用的软件模块,研发新产品就会简单很多了,其主要流程如下图所示:
在这里插入图片描述
1) 通过配置软件构建该设备的数据模型。
2) 从function database导入可复用软件模块,并通过配置软件构建工程。
3) 通过需求说明书增加新功能模块。
4) 提炼必要的软件模块到FB库中。
5) 内外品质保证(内外品质的概念会在后续质量一章中详述)。

基于这套研发模型,公司会要求非创新性的改进型产品,代码复用率要达到70%以上。实际上随着时间的积累,大多数产品都是特定领域的小改动,代码复用率经常高达90%左右。

一边是上班单纯的工作,下班大家经常聚在一起喝茶烤肉聊天从不谈工作;另一边是追求加班是一种奉献精神,大家整日忙的头昏脑涨,做着重复的工作,聚餐时还要经常在领导面前高谈阔论以表明自己是多么的全身心投入;最终可悲的是数以几十倍记的工作效率差距。

1.3.2 如何做出高端产品

在工控领域长期工作过的工程师,自然而然会接触到很多高端产品,其中很多都是名不见经传的厂家做出来的,但人家的产品就是用着可靠不给你惹事、长期性能稳定、精度高……,如我们用到的一种弹簧夹头,如一种要求数十年不漏气的密封圈,如在地铁非常潮湿环境下用的电气控制设备,如可长期稳定测量的高精度电能表……。

我以前接触的不敢说都是低层次产品,但绝对是每个大城市都有那么几家能做的产品。因此,大家只好拼价格,拉关系,堆售后。有时大家也会眼馋那些高端产品的利润率,拆开研究研究好似也没什么,也会集中人力物力进行尝试,但不知为啥,做出来的产品总感觉会差那么一点点。幸好,我加入的这个项目组,给了自己一次近距离观察思考的机会。

记得一次同带领我的规约工程师交流,我惊奇的发现他对规约的理解模式和我完全不一样。在我自己的传统观点中,规约不就是约定双方帧结构和排列顺序吗,如下表,是我设计的维护规约中的读遥测功能帧。
在这里插入图片描述
基于该表,构建规约程序仅需要按位置填充信息即可,甚至为了程序编写方便,我都在规约文本中标记了从0开始计数的序号。我顺着自己这种思维模式,在阅读理解国际标准规约的时候,总有一种不舒服的感觉。下图是国际标准IEC60870-5-104规约中读取SOE信息的帧结构。按照这个表去写规约程序时,总需要查来查去,很苦恼。
在这里插入图片描述
在带领我的规约工程师眼中,他了解一个规约的顺序依次是:适用范围,物理层,链路管理机制,传输的数据模型,基于数据模型的帧结构,而上图中描述的恰好是基于数据模型的帧组织结构图。基于这样的思维模式,规约程序会自然而然的显现出面向对象的特征,首先构建各数据模型的结构,然后基于这些数据模型在构建帧结构,程序结构也自然变得非常清晰。

作为对比,以前我们修改一个数据模型结构时,总是需要小心翼翼的重新排列规约结构,然后在调整规约两端程序,而现在,仅需要修改该数据模型对应类程序这一处。而且,基于这样的思维模式,也更容易理解电力行业新涌现出来的各种规约,如IEC61850规约,其层次结构更复杂。

理解了他的思维模式,我突然有一种豁然开朗的感觉。大家的计算机能力差不多,但这个同事为何能在规约领域比我高出一个层次呢。从表面上看,因为我要负责整个产品的研发工作,会涉及到多个领域,而他仅需要思考规约即可,但如果深层次思考,好似并没有这么简单。

该同事虽然仅负责规约开发,但有两个额外要求,第一要负责全部门几十款产品的规约开发,第二需要构建高度可复用的规约模块,其工作量并不小。以该同事的话说,他一开始也是负责具体产品的,仅仅是后来发现规约模块几乎每个产品都有,然后在慢慢的复用过程中,他的精力才逐渐的全部转移到规约这一个点上了。因此,复用是专业化的前提,而专业化是复用的衍生品。

同规约类似,我在项目组里发现了有专门研究脚本的,专门负责维护软件的,专门搞算法的……。记得有个搞了一辈子算法的老头特可爱,头发都白了,退休后又被公司返聘,他特别喜欢给我们讲课,讲解地铁机车启动时的大电流和故障时的大电流有何区别。我不是专业人员,基本听不懂,顶多听个热闹,但也会被老人那种专业的职业素养所折服。
在这里插入图片描述
此时,大家能理解我们为何做不出高端产品了吗。高端产品不是靠领导反复强调就能做到的,也不是靠短期堆砌人力或高端元器件就能做到的,更不可能弄几个所谓的炫酷点就能吸引用户买单。高端产品不仅需要在诸多细分领域的长期精打细磨,更需要创造一个适合工程师在细分领域长期打磨的支撑环境。

1.3.3 我心目中的架构师

不管是为了提升产品研发效率,还是尝试做高端产品,努力提升代码复用率都是基础前提。我在前文中提及,为了构建可复用的104规约模块,需要一系列支撑模块才能做到。正因为如此,我们在工作中虽然容易意识到需要努力提升代码复用率,但经常会因举步艰难而最终放弃。如何破解这个困局呢?

在我刚加入该项目组时,有一个大概五六十岁的中年人,经常拿着一张又一张的彩色图纸,给我讲解产品整体结构。后来,我才了解到,这个人就是整个项目的中枢:架构师。在一次,他的很多工作和思维模式重塑了我。

我们自己做产品,因为工期紧,人手少,文档欠缺是家常便饭,顶多在项目检查时被迫快速构建一些,然后就扔到垃圾桶里面去了。经常震撼于高端产品厚厚的一本说明书,我内心经常猜测,他们的研发类文档是否会更加的铺天盖地呢,但却被现实狠狠的打了脸。

以该架构师的观点,架构设计不是用来炫酷的,而是为了让整个项目组达成共识。为了达成共识,反复的交流,对新员工的充分培训都是必须的,太多的文字反而会影响共识达成,一图顶千言,更便于大家坐在一起交流。因此,我看到的架构设计就是几十张彩色的图纸,而且虽然我仅从事很小的一个工作分支,架构师也会给我介绍整个产品的设计理念。

下图是我当初花了很大代价理解的一张图,初期一看感觉好乱,但后来理清楚整个产品中各模块之间的关联关系,即使实际仅做着很小的一部分工作,但也会有全局思维,不会有那种云深不知处的迷茫感。整个图形太大,截取部分,大家不用管内容,意会即可。

在这里插入图片描述
在整个架构设计中,有一个设计理念我个人非常的欣赏,深刻揭示了可复用软件模块和产品之间的关系,如下图所示:

在这里插入图片描述
该架构设计图的演化步骤:
1) 在做一款新产品的时候,产品是主导,项目经理工作职责可能需要跨越市场、研发、生产等多个环节。
2) 如果在持续的构建产品过程中,发现某个软件模块在三个或三个以上的产品中重复使用,就应该考虑重构该模块,让其可复用,或者称之为平台化。
3) 随着平台中积累的可复用模块增加,在构建新产品的时候,就应该尽量的充分复用。当然,这也会推动平台层的可复用模块迭代提升。
4) 做产品需要考虑各个市场的差异,且应该尽量将这些差异部分纳入工程开发范畴,以减少维护工作量。
5) 综合起来,构成平台、产品、工程三层研发体系,设计各软件模块时,首先需要考虑其处在哪一层,然后采取相应的策略。

将可复用的软件模块统一纳入平台层,这也正是这种研发模式被称之为“平台化”研发模式的原因。

后来,我同这个架构师熟络起来后,慢慢的越发开始佩服他知识面全,专业素养高,工作态度严谨认真。更关键的是,他的年龄虽然已经五十多岁了,远超国内40岁的程序员大限了,不仅是公司最关键的角色之一,收入也颇丰,压根没有被辞退的可能。

不知大家有没有发现很重要的一点,平台化不是那个牛人直接构建出来的,而是一点点长出来的,更重要的是伴随着平台化的成长,我们自然也能成长成架构师。从那一刻开始,我内心暗暗发誓,成为架构师是我人生的奋斗目标之一,至少可以不用担心40岁后被淘汰沦落街头。

1.3.4 可持续优化的质量管理体系

鉴于自己曾经专业灭火队员的悲惨经历,在这次难得的研发经历中,我会自然的特别留意他们是如何提升产品质量的。很遗憾的是,实际情况同我的预料情况反差很大。在平时工作中,大家好像都没有刻意的提及产品质量问题,也不像我以前的部门,领导整天强调要仔细些、多测试。总有一种感觉,大家仅仅是按部就班的做事,然后出来的自然就是高质量的产品,好似所有的质量工作已经被下放到了平时工作细节中。

在内部培训中,他们特别强调一个价值流的概念,这个观点我特别欣赏。价值流,通俗的讲,就是每一步的工作都是具备单独价值的,是应该以产品的形态提交到下一环节的。从最初的一行行代码到最终产品,就是一个典型的价值流传递过程,下图是我参与的其中一段:
在这里插入图片描述
这段价值流传递过程,主要分为两部分,第一部分对应从单个人写代码到部门内的代码审核,第二部分对应部门内集成测试和测试组的功能测试(有时又称系统测试)。

1 代码审核机制

代码自查主要是针对个人的,其中一条很重要的原则就是你将代码提交出去让其他人帮你审核时,你至少应该进行了三遍代码自查。换句话说,自审后的代码才是你能力边界范围内做出来的产品。

刚开始我个人在执行这条策略时还有蛮强的抵触情绪的,都忙的一塌糊涂,哪有时间检查三遍。但后来习惯了之后,我发自内心的喜欢上了。一开始有抵触情绪,是因为会感觉这样很啰嗦,工作很慢,后来才发现这种慢节奏才是真正的高效率。我发现自己虽然写了二十多年的程序了,但写程序过程中依然会陷入在细节中,存在诸多纰漏,真是不审不知道,一审吓一跳。

在多年的持续训练后,我基本养成了自审三遍的好习惯,第一遍侧重审核代码细节,第二遍会站在全局思考整个代码流程是否合理恰当,第三遍会跳出当前代码段,思考其同其他模块的关系。当你会发现写了三天代码,编译竟然没有一个语法错误的时候,或者数年后别人问起你这段代码,你依然非常清晰时,内心会有一种怦然欣喜的感觉,快与慢竟然能如此有机的统一在一起。

我以前总埋怨团队不好带,随便离职一个人,就会坍塌一块代码,搞得大家很狼狈。这次研发经历,让我发现,有一条简单有效的策略可以破解这一难题,那就是代码审核机制。在我眼中,代码审核机制,不仅可以将很多个个人有效的扭结为一个高效的团队,而且是新人培养、老人提升、白盒测试、知识库积累等诸多工作的有效手段。

当然,现实世界中,读别人的代码比自己重写一遍都痛苦,如何让代码审核机制有效且低摩擦的执行下去,成为这项策略的关键。后续,我会有专门的一章节和大家交流这个问题,也和大家分享我们团队或拿来或总结出来的诸多行之有效的各种策略。

2. 集成测试

每一个模块都尽善尽美,并不等于组装出来的产品就是优秀产品,而集成测试就是将散落的模块打磨成一款产品。在我以往的经历中,我带的研发团队好不容易将产品做出来了,大家都试一试,应该是好用的,感觉就大功告成,可以交给测试团队了。但这次研发经历,有一条策略,颠覆了我的原有观点:集成测试是研发组份内之事,只有这样,才提交的是可测试的产品。

我以前很少做集成测试工作,为了增加体验,有一次一款样机研发完毕后,我耐着性子认真且完整的做了一次集成测试。那次测试前前后后差不多花了一个半月时间,我惊奇的发现自己已经交付的产品,每个工作日竟然还能发现的多达三处bug或改进点。以往我们研发组做的产品交付后工程人员或用户总会反馈一堆不舒服的地方,经过这次集成测试,类似反馈竟然大幅度减少了。至此,我内心才真正认可了集成测试是研发组份内之事。

很遗憾的是,现实世界中,研发人员好像都不太乐意做测试工作,工作即使安排下去,也会出现很多偷懒的情况,很多测试用例大家会想当然的认为不应该有问题而直接忽略。如何克服呢?在这次经历中我看到了一个有趣的小技巧。该跨国公司员工做测试时,首先将测试用例全部打印出来,测试人员每测试一项后,在记录处盖上个人的戳记(一种电子化设备,包含名字和时间,同时也是个人权限签名标签),最后在扫描电子化归档。现在全球都在推行无纸化办公,为何还会这样逆潮流的行为呢。交流后,我才意识到人们在签名时,会因仪式感而多了一点点慎重情绪,纯电子化的东西大家反而会因虚无感而轻视。一个小小的技巧,既尊重了人性,又增加了测试效能,我很佩服这样简单但有效的管理艺术。

为了做好集成测试,重点在测试用例。过多和繁琐的测试用例会降低执行的可行性,最后极容易让集成测试过程形同虚设,如何保证覆盖必要的关键环节,且有效的降低测试工作量,是一门艺术,没有标准答案,是反复权衡和迭代中的产物。同时,集成测试用例又是一个闭环节点,现场反馈的各种问题,都会逼迫我们重新审视测试用例。

3. 功能测试

既然测试人员将所有的测试都做完了,要测试组干嘛呢?这是我一开始接触集成测试时,脑袋中自然而然冒出来的问题。后来反复观察后发现,这两类测试是不重复的,他们的侧重点不同。

集成测试更多的是站在研发人员角度进行测试的,会更加侧重接口,一些特定情况下的特定处理等。而功能测试时,测试人员更多的是站在用户角度对产品进行测试的。如要求拿到的产品必须是包装好的,会检查各种资料是否齐全,会模拟手拿箱子不小心跌落在地后会发生什么状况。会模拟各种真实安装过程,如人工接线时螺丝刀不合适,是否会造成永久伤害等。

在公司内部,大家称集成测试为对内测试,功能测试为对外测试,我仅是为了大家理解方便,将其换成了软件工程中的常用名词。但仅内外这两个名字,就能形象的指出他们之间的区别。

很多具体的测试工作可以深化,如重复的测试工作可以搞各种自动化测试等,以提升工作效率。集成测试不容易外包,但功能测试可以外包,这样可以缓解研发进度和人员紧张之间的压力。我就曾经带着一个团队承接了一个功能测试外包项目,因成绩突出而获得中外双方的表彰。

4. 全面质量管理体系

代码自查、代码审核、集成测试、功能测试,一系列的价值流模型,或者说带闭环的产品传递模型,至此,大家是否能够理解,为何我们仅仅是按部就班的工作,就能做出来相对还不错的产品。但即使如此,只要是人做的东西,就总会出现各种各样意想不到的问题。面对这类问题该如何办呢?

在平时工作交流时,架构师经常给我们讲一个丰田纺纱机的故事(在那之前我一直以为丰田一开始就是造汽车起价的),其中最重要的特性是丰田纺纱机内部包含一个检测模块,一旦检测到问题后会自动停机。这样的好处是,每台纺纱机不需要一个人时时刻刻盯着,而是一个人可以管理多台机器,如果发现机器停下来,调整一下,就可以从新运行了。

将检测模块内嵌到产品内部,即使出了问题,也有策略去应对,这就是破解长期质量问题的关键策略。
在这里插入图片描述
当然,想将检测模块内嵌到产品中并不是一件容易的事情,需要在一开始架构设计的时候就给予充分的考虑,如装置发生异常后,有足够的异常记录信息用于分析问题;如在系统联调时,有足够的边界信息缩小问题范围;如某些行为同预想不一致时,有策略可以分析程序流……。记得一次我们为了分析广州地铁运行异常现象,采用专用设备以高频率的模式抓了真正一周的运行数据,1T大小的快速硬盘被塞满了好几块。

质量问题总给人一种虚无缥缈的感觉,我以前除了对下属员工叫嚷要仔细编码多多测试外几乎毫无办法,现在,接触到这种春风润物细无声的质量提升策略,内心还是有蛮多感触的。后来,顺着这条路,我才开始慢慢的接触到全面质量管理体系(俗称TQM),深入了解戴明14条,才好似给自己打开了另一扇窗户。

1.3.5 有序的生产研发管理体系

在我的职场生涯中,除了刚入职那几年,感觉剩余大部分时间都是被身不由己的赶着往前走。刚开始是被各种事推着走,经常是周一刚刚制定好计划,一个现场电话打来,原有计划就完全泡汤了。后来是忙于部门间交流,经常会被各种流程所累,折腾来折腾去,年终总结的时候,发现这一年又白过了,空悲切。

一个企业随着业务增加,各类工作就会显得非常杂乱,此时大家特容易出现疲于奔命的状况。为了解决这个问题,常采取的策略是将人员分成多个部门,如销售、研发、测试、生产、工程服务、技术支持等,但紧随其后的各部门之间的玻璃墙又会将人折腾的疲惫不堪。

人多了必须分部门,但部门多了交流就会困难,这种现象好像无解,别人有没有好的策略呢?

我所在的项目组因为人多,也分为多个部门,仅我接触到的就有需求管理、研发、平台、测试、生产、品质等诸多部门,但在具体做产品的时候,又感觉不像我们传统意义上的部门。如果有一个项目时,会从各部门挑选人员组成临时项目组,由项目负责人统一管理调度,项目结束后人员在回归原部门。

我当初参与的一个项目组,其中竟然包含一个生产工人。该产品在样机和小批量供货阶段,都是他一个人在负责生产,经常是推着一个小推车,一道道的工序打磨。后来这个产品结项后,这个人会自动回归生产部门,如何构建有效的生产指导文件,如何增加流水线以满足批量生产计划,如何同其他产品复用生产线等等诸多细节,都是生产部门份内的工作,不需要项目经理操心。

生产部门如此,其他部门也大都类似,如对外测试部门,会随着需求分析、产品架构设计等环节,自然而然的构建有效的测试案例,同时为了一些自动化测试,也会要求产品中增加特定的支撑功能。

这种工作模式,对个人成长非常有帮助,每个人都会获得两方面的成长,不仅在各部门接受专业化的训练,同时在项目组又可以更好的理解市场和产品,培养全局观。如带我的那个规约工程师,他经常会同时参与多个产品设计和研发。这种经历不仅让他理解规约应用在多个产品中的细微差异,而且也能以更专业的方式构建高复用的规约模块。

而且,这种工作模式,会大幅度减少各部门之间的推诿扯皮,甚至各部门之间根本不需要一些鸡同鸭讲的指导文档,实际上,研发人员压根写不出有效的生产指导文档的。

至此,我看到了一种有序的工作模式,平时看似散沙,但一旦有具体的项目任务,又能快速的聚集起来,成功的破解了我最初面对的各种困局。

1.3.6 看到就是一种力量

可能是因为自己曾经的“痛苦”经历,又或是自己一直在琢磨如何更有效的工作,短短一年左右的联合研发经历,让我在本职工作之外,狠狠的开阔了一次视野。

记得那些日子,因为常请教很多本质工作之外的事情,搞的两个翻译经常会很不耐烦,经常需要小心的陪着不是。虽然因为合资的原因,对方没法阻扰我们参与其中,但很多时候都会防着一手。对方喜欢拉着我们参观各部门、樱花树下喝小酒,但一旦涉及具体工作,就会谨慎得多。如我一直惊奇他们仅使用12位AD芯片,但精度竟然超过了我们使用16位AD芯片的产品,可惜最后别说弄明白了,甚至连相关部门的工程师都没见到一枚。

即使如此,我感觉这次联合研发的经历,也让我受益匪浅。自己一直在工控产品领域摸爬滚打,因为工作内容类似,很多东东都仅仅是一层窗户纸,看到别人怎么做,我很容易能想清楚为何要这样做。

别人能,我为什么不能?除了技术方面,这次经历给我最大的帮助就是信心的增强,而“看到”真真切切给的给我了力量。

带着您遨游一圈,您感受到那种力量了吗?

——————————————

我是小马儿,一个渴望良知与灵魂的嵌入式软件工程师,欢迎您的陪伴与同行,如感兴趣可加个人微信nzn_xiaomaer,需标注“异维”二字。

发布了12 篇原创文章 · 获赞 16 · 访问量 1479

猜你喜欢

转载自blog.csdn.net/zhangmalong/article/details/103417351
1.3