我的 DeLorean 运行 Perl

人们看到一辆 DeLorean 在街上行驶,惊讶过后,他们下一个惊讶之处是它有一个用 Perl 编写的仪表盘电脑。
521 位读者喜欢这篇文章。

我现在的标志性爱好项目是我汽车的电脑化仪表盘,碰巧这辆车是一辆 DeLorean。但是,每当我向别人展示它时,我通常需要给他们一段时间来惊叹这辆车,然后他们才会注意到仪表盘里有一个电脑屏幕。当我开始描述软件时,也会遇到类似的问题;程序员们一听到实时 OpenGL 渲染的仪表数据都是用 Perl 编写的,立刻就会纠结于“为什么是 Perl???”。因此,任何关于我项目的讨论通常都始于 DeLorean 的历史,或者关于 Perl 与其他更常用工具的优缺点的讨论。

The author and the Delorean

作者和 DeLorean。 照片由 Michael Conrad 提供。

我在 2010 年开始了该项目,最初的想法是在仪表盘中集成一台电脑,充当个人助理,但它很快就变成了一个用软件渲染来替换原装仪表盘的项目。根据我想要的处理水平(我的梦想很大)和我想要的屏幕尺寸,我放弃了人们通常会使用的高端微控制器,而是选择了一台完整的 Linux PC 和桌面显示器,以及一个低端微控制器来读取汽车的模拟测量值。当时我在工作中做 OpenGL 和 C++,所以这是我的第一个软件选择。我可以写多篇文章关于硬件选择,但我想专注于本文的软件。(您可以在我的网站上找到更多关于该故事的信息,nrdvana.net。)

Demo mode

演示模式。 照片由 Michael Conrad 提供。

经过几年的努力,C++ 显然不适合我的大型个人项目。虽然 C++ 产生出色的性能和低资源消耗,但我最大的资源短缺是时间和“精神状态”。有时我会离开这个项目整整一个月,当我终于有一天空闲时间来处理它时,我却在试图回忆起我上次停在哪里。最糟糕的是,我通常无法在一个会话中完成重构我的设计,所以当我几周后回到它时,我并没有捕捉到设计更改破坏代码的所有地方。此外,虽然 C++ 在捕捉错误方面通常比 C 更好,但我仍然会遇到偶尔的内存损坏,这可能会耗费数小时的调试时间。而且,编写诊断实时、多线程应用程序所需的日志记录和调试例程也需要大量的开发开销。

与此同时,我的日常工作已经转向了 Perl。 我没有主动寻求 Perl;它只是随着紧急项目推到了我的面前。 然而,在几个月内,我就被它的可能性所吸引,现在它是我最喜欢的语言。

进入 Perl

在 2014 年,我冒险用 Perl 重写了仪表盘软件。经过多年使用 C++ 的艰难跋涉,我能够在几个月内获得一个工作原型(至少是软件的原型),并在 2015 年转向完成硬件和微控制器。

我的 Perl 小成功故事主要关于敏捷性。 我并不是一个真正的流行语爱好者,也不是那种阅读方法论书籍的人,但“敏捷”现在对我来说确实意味着什么。 我觉得 Perl 达到了一个神奇的甜蜜点,它提供了足够的结构来构建一个正确、高性能的程序,同时又足够小巧和通用,可以轻松地将事物连接在一起,甚至提供了足够的语法功能,可以用简洁但可读的代码来表达复杂的操作。(如果您不熟悉 Perl 的功能,请参阅我的配套文章“从系统程序员的角度看 Perl”,其中详细阐述了 Perl 如何适用于系统工作。)

Dash view

仪表盘视图。 照片由 Michael Conrad 提供。

主要且持续的好处是能够进行临时更改。 因为我没有太多时间来规划我的对象的完整需求,所以能够在毫无戒心的对象上添加一些额外的属性,或者根据需要在 Java 或 C++ 中需要笨拙的反射代码的标准快速对对象列表进行排序,这对生产力是一个很大的提升。 如果我确定喜欢这个更改,我会回去用正确声明的属性和接口重写它。 我发现我可以在不到一个小时的时间内编写一个新的图形小部件,包括动画。

工具链

我的项目的 C++ 版本真正的问题之一是保持所有二进制级别的代码同步。 各种组件(渲染、消息总线、逻辑核心、微控制器固件、控制工具、调试工具)都在共享二进制数据结构,并且在 makefile 中保持依赖关系是一件令人头疼的事情。 我个人对 automake 系列工具感到不满,因此每当我需要做一些奇怪的事情(比如使用 avr-gcc 编译微控制器代码)时,我都会冒着沮丧的风险,并绕道进入一个新的宏伟计划,为 autotools 创建一个替代品(当然这是一件我不需要浪费时间的事情)。

在我更改为 Perl 期间,我将微控制器转换为显示为 Linux 串行设备,并将协议更改为短文本符号字符串。(这些消息实际上比我之前使用的二进制数据包结构更小。)这让我可以用简单的 socat/dev/ttyS0 上进行调试。 它还简化了与微控制器通信的守护进程。 C++ 版本是用两个线程编写的,因为我使用的是 libusb,它最简单的操作模式有一个阻塞读取方法。 Perl 版本只是打开到字符设备的流并读取文本行。

我对主机端通信做了类似的更改,并让守护进程生成 JSON 行而不是二进制数据包。 因为使用 AnyEvent 这样的库在 Perl 中实现这一点非常容易,所以我完全放弃了“消息总线”的想法,而只是让每个程序创建自己的 Unix 套接字,其他程序可以根据需要连接到该套接字。 调试单线程要容易得多,而且也没有太多的调试工作要做,因为 AnyEvent 为我做了大部分工作。

由于所有内容都作为 JSON 传递,因此不再需要担心任何消息结构。 我的任何 Perl 程序都不再需要 make 过程,因此项目中唯一仍然有 makefile 的部分是微控制器固件,而且它足够简单,我只是手工编写了它。

性能

直接用 Perl 处理底层数学运算可能会很慢,但使用 Perl 来提高性能的最佳方法是将 C 库粘合在一起。 Perl 有一个名为 XS 的扩展系统,可以帮助您将 C 代码绑定到 Perl 函数,但更好的是,有一个 CPAN 存储库模块名为 Inline,它允许您将 C 或 C++(以及其他语言)直接粘贴到 Perl 模块中,并且它会在模块首次加载时编译。(但是,是的,我在为汽车构建固件镜像之前预先编译了它们。)

感谢 Inline,我可以根据需要将代码从 Perl 移到 C,而无需处理库版本。 我能够将我的一些 C++ 类直接带到新 Perl 版本的仪表盘中。 我还能够包装 FreeType for OpenGL (<FTGL) 库的 C++ 对象,这是一个我不想重新发明的重要部分。

使用 C++ 实现时,系统的 CPU 使用率约为 15%。 使用 Perl 时,约为 40%。 几乎所有这些都是渲染代码,因此如果我需要,我可以随时将其中的更多代码推回到 C++ 中。 但是,我也可以升级计算机,并且 40% 甚至不是问题,因为我保持了完整的每秒 60 帧(并且我运行的是 6.4 瓦的处理器)。

更广阔的视野

与其他语言相比,Perl 的 CPAN 公共包存储库特别大、文档完善、经过测试且稳定。 当然,这取决于各个作者(并且有很多反例),但我对普遍存在的测试覆盖率和有用的文档文化印象深刻。 安装和使用新的 Perl 模块也非常容易。 我不仅避免了 C/C++ 的工具链工作,而且还获得了 Perl 作者的优势,他们已经克服了冲突的线程模型或事件循环或日志记录系统,为我提供了插件体验。

由于所有内容都是用 Perl 编写的,我可以从 CPAN 上获取任何我喜欢的东西。 例如,我可以让汽车向我发送电子邮件或短信,托管一个 Web 应用程序以通过手机控制功能,编写燃油里程的 Excel 文件等等。 我还没有开始研究这些功能,但感觉不错的是,障碍已经消失了。

回馈

在做 C++ 的十年里,我从未发布过一个供公众消费的库。 这很大程度上是由于 autotools 的极其笨拙,而且即使没有正确地打包它以进行分发,仅仅创建一个系统安装的 C++ 库也是一件非常痛苦的事情。

Perl 使模块编写、测试和文档编制变得非常容易。 它是如此简单,以至于我为了自己的利益为我的 Math-InterpolationCompiler 编写了测试用例和文档,然后将它们发布在 CPAN 上,因为“为什么不呢?”。 我还成为了 X11-Xlib 的维护者并大大扩展了其 API,然后编写了 X11-GLX,以便我最终可以将所有 OpenGL 设置代码按正确的顺序排列。(这也是我尝试将仪表渲染器变成一个合成窗口管理器的一部分,但结果证明这比我预期的要困难得多。)目前,我正在努力将我的地图/导航数据库也变成一个 CPAN 模块。

但为什么不呢...

你可能会问“为什么不用 X 语言”,这里的 X 通常是“Python”。好吧,首先,我比 Python 更了解 Perl。我使用了大量 Perl 的高级特性,所以学习 Python 将会是另一个漫长的学习曲线。而且我比较喜欢 Perl 的工具链,特别是像 proveperldoc 这样的工具。我猜想用 Python 也能实现所有这些功能,但我没有令人信服的理由切换。至于其他任何语言 X……没有任何其他语言能像 Perl 或 Python 那样提供如此丰富的软件包,所以我不太倾向于尝试它们。我可以混合使用语言,因为我的项目由多个进程组成,但所有东西都使用同一种语言意味着我可以更容易地在程序之间共享代码。

“为什么不用 Android?”是另一个常见问题。的确,平板电脑比整个 PC 更容易嵌入,并且可以访问地图应用程序。显而易见的第一个问题是,我会回到 Java,并失去我最珍视的敏捷性。其次,我不清楚是否有任何方法可以将不同应用程序的图形合并在一起(例如使用 Google Maps 作为仪表盘中的纹理),尽管可能存在。第三,我一直在开发一个将视频源直接连接到图形作为纹理的功能。我不知道有任何平板电脑可以以足够低的延迟实时从外部源捕获视频,更不用说直接捕获到图形纹理缓冲区中了。Linux 桌面软件在这种深度处理方面更加开放,所以我可能会继续使用它。

总的来说,我很高兴我已经完成了足够的进度,可以驾驶我的 DeLorean 了。

标签
User profile image.
IntelliTree Solutions 的软件工程师。 Michael 擅长 Web 应用程序、嵌入式系统和性能调优。

3 条评论

无论什么方法有效,我们都无权评判?
88 英里/小时,那是当然! ;)

我很好奇您是否考虑过 Wayland。它比 X 更容易使用,并且他们添加了一些专门用于车辆设备的功能。

“更容易使用”有点主观 ;-) 我很清楚 Wayland 是未来的发展方向,特别是在深入了解 xlib 的内部结构的恐怖之后。 对我来说,GLX “更容易使用”,因为我在 2008 年已经编写了显示设置代码,并且我只是用一个 perl 模块包装了它。 为了切换到 Wayland,我首先需要将我的笔记本电脑切换到 Wayland 以进行开发,然后学习所有的 EGL API 并将它们封装到一个 perl 模块中,然后更改嵌入式图像中的一堆软件包。 我也没有找到像 X11 的 Xephyr 这样的等效 Wayland 测试工具; 我正在将仪表盘变成一个窗口管理器,如果我需要一台带有显示器的专用机器来测试它,那就太尴尬了。

回复 Trey Dempsey (未验证)

Creative Commons License本作品采用 Creative Commons Attribution-Share Alike 4.0 International License 授权。
© . All rights reserved.