您没有等待的物联网。开发和测试(第2部分)

继续文章“您没有等待的物联网”的第一部分“开发和测试(第1部分)”不久就到来了。这次,我将告诉您项目的体系结构是什么,以及在开始测试解决方案时我们踩了什么样的耙子。

免责声明:没有一个垃圾桶受到重创。



项目架构


我们有一个典型的微服务项目。“较低级别”的微服务层从设备和传感器接收数据,并将其存储在Kafka中,之后,面向业务的微服务可以与从Kafka接收的数据一起使用,以显示设备的当前状态,构建分析并微调其模型。



IoT项目中的Kafka变得非常酷。与RabbitMQ等其他系统相比,Kafka具有以下优势:

  • 使用流:可以处理来自传感器的原始数据以获得流。通过流,您可以灵活地配置要在其中进行过滤的内容,并且流操作非常容易(创建新的数据流)
  • : Kafka , , , . - , - , . , Kafka , . , , , .

Backend


首先,让我们看一下我们熟悉的后端,该应用程序的整个业务层都基于相同的Java和Spring堆栈构建。为了在真实环境中测试微服务应用程序,我们使用了test-containers库。它使您可以轻松地在Docker中部署外部绑定(Kafka,PostgreSQL,MongoDB等)。

首先,我们在Docker中引发所需的容器,启动应用程序,并且在实际实例上,我们已经在运行测试数据。

关于我们到底如何做到这一点,我在Heisenbug 2019 Piter的报告“微服务大战:JUnit第5集-TestContainers反击”中作了详细说明:


让我们看一下它的外观的一个小例子。下层服务从设备获取数据,并将其扔给Kafka。业务部门的热图服务从kafka获取数据并构建热图。



让我们使用“热图”服务(通过Kafka)测试接收数据

@KafkaTestContainer
@SpringBootTest
class KafkaIntegrationTest {

    @Autowired
    private KafkaTemplate kafkaTemplate;

    @Test
    void sendTest() {
        kafkaTemplate.send(TOPIC_NAME, MEASSAGE_BODY)

        //     
        //       
        //  
    }
}

我们正在编写常规的SpringBoot集成测试,但是它在基于注释的测试环境配置样式方面有所不同。@KafkaTestContainer需要注释才能提高Kafka。要使用它,您需要连接库:

spring-test-kafka

当应用程序启动时,Spring启动,而Kafka容器在Docker中启动。然后,在测试本身中,我们kafkaTemplate将其使用,将其注入测试用例中,然后将数据发送到Kafka,以测试处理来自该主题的新数据的逻辑。

所有这一切都发生在普通的Kafka实例上,没有嵌入式选项,只有在生产中旋转的版本。



热图服务使用MongoDB作为存储,对MongoDB的测试看起来类似:

@MongoDbDataTest
class SensorDataRecordServiceTest {

    @Autowired
    private SensorDataRecordRepository repository;

    @Test
    @MongoDataSet(value ="sensor_data.json")
    void findSingle() {
        var log = repository.findAllByDeviceId("001");
        assertThat(log).hasSize(1);
        ...
    }
}

@MongoDbDataTest与Kafka类似, Annotation 在Docker中启动MongoDB。启动应用程序后,我们可以使用存储库与MongoDB一起使用。

要在测试中使用此功能,您所需要做的就是连接库:

spring-test-mongo

顺便说一下,那里还有许多其他用途,例如,您可以在执行测试之前通过注释将数据库加载到数据库中,@MongoDataSet如上例所示,或者使用注释,@ExpectedMongoDataSet验证在数据库中完成测试用例后是否已经出现了我们期望的确切数据集。

我将在6月15日至18日在线举行Heisenbug 2020 Piter上告诉您有关使用测试数据的更多信息


测试物联网特定的事物


如果业务部分是典型的后端,那么处理来自设备的数据将包含很多与硬件相关的因素。

您有一台设备,需要与它配对。为此,您将需要文档。当您有一块铁并停靠在上面时,这很好。但是,一切都以不同的方式开始:只有文档,而且设备还在路上。我们拍摄了一个小应用程序,该应用程序在理论上应该是可行的,但是一旦真正的设备问世,我们的期望就会变成现实。

我们认为输入将是二进制格式,并且设备开始向我们抛出一些XML文件。如此艰难的形式诞生了物联网项目的第一条规则:

永远不要相信文档!

原则上,从设备接收的数据或多或少是清晰的:Time-这是时间戳,DevEUI-设备的标识符,LrrLAT以及LrrLON-坐标。



但这是什么payload_hex?我们看到8位数字,里面有什么?它是到垃圾箱的距离,传感器的电压,信号电平,倾斜角度,温度还是所有这些?在某个时候,我们认为这些设备的中国制造商知道某种存档的风水,并且能够将所有可能的内容打包成8位数字。但是,如果您从上面看,您会发现时间写在规则行中,并且包含的​​位多3倍,也就是说,字节显然没有人保存。结果,事实证明,特别是在此固件中,仅关闭了设备中一半的传感器,您需要等待新的固件。

他们在等待的时候,我们在办公室里做了一个测试台,实际上是一个普通的纸板箱。我们将设备固定在其盖子上,并将所有办公室物品扔进盒子。我们还需要测试承运人的汽车的副本,并且该项目的一名开发商的机器扮演了该角色的角色。

现在,我们在地图上看到了纸板箱的位置,我们知道了开发商的去向(破坏者:上班回家,在星期五晚上没有人取消酒吧)。



但是,带有测试平台的系统并没有持续很长时间,因为与实际容器有很大的差异。例如,如果我们谈论加速度计,那么我们将盒子从一侧转向另一侧,并接收来自传感器的读数,一切似乎都正常。但是实际上有一些限制。



在设备的第一个版本中,不是以绝对值来测量角度,而是以相对值来测量角度。当盒子倾斜超过固件中固定的增量时,传感器开始无法正常工作,甚至无法固定转弯。



当然,所有这些错误在此过程中都已得到纠正,但是在开始时,盒子和容器之间的差异带来了很多问题。然后,我们从各个方面对储罐进行了钻孔,同时决定了如何将传感器放置在容器中,以便在用运输工具的汽车提起储罐时,我们可以准确地记录下垃圾已被卸载。

除了倾斜角度的问题外,起初我们没有考虑容器中真正的垃圾是什么。如果我们将聚苯乙烯和枕头扔进那个盒子,那么实际上人们会将所有东西都放在容器中,甚至是水泥和沙子。结果,一旦传感器显示容器是空的,尽管实际上容器已满。事实证明,在维修过程中有人扔出了一种凉爽的吸音材料,从而减弱了传感器发出的信号。

在这一点上,我们决定与办公室所在商务中心的房东达成协议,以便在其垃圾容器上安装传感器。我们将场地布置在办公室的前面,从那时起,项目开发人员的生活和日常生活发生了巨大变化。通常,在工作日开始时,您想喝咖啡,阅读新闻,在这里,您会发现整卷磁带都满是垃圾:



在测试温度传感器时(例如在加速度计中),现实提出了新的方案。温度的阈值很难选择,因此我们可以及时知道传感器已打开,并且不会对它说再见。例如,在夏天,容器在阳光下变得非常热,而将阈值温度设置得太低会导致传感器不断发出通知。并且,如果该设备确实燃烧了,并且有人开始扑灭它,那么您需要准备将盛满水的水箱放到顶部,然后有人将其掉下,然后在地板上扑灭。在这种情况下,传感器显然将无法幸存。


在某个时候,一个新的固件出现了(您还记得我们在等待它吗?)。我们将其滚动到传感器上,与传感器的通信协议再次中断。再见XML,并且使二进制格式再次生效。我现在希望现在它对应于文档,但是……不!
因此,第二条规则:阅读第一条规则。那是-永远不要相信文档。
该怎么办?例如,进行逆向工程:我们坐在控制台上,收集数据,扭曲传感器,在其前面放一些东西,尝试识别模式。因此,您可以隔离距离,容器的状态和校验和。但是,其中一些数据难以解释,因为我们的中国设备制造商显然喜欢自行车。为了将浮点数打包为二进制格式以解释倾斜角度,他们决定占用两个字节并除以35。



在整个故事中,与设备一起使用的服务的底层与顶层隔离对我们大有帮助,所有数据都是通过kafka注入的,这些合同已经过协议并得到保证。

这对开发有很大帮助,因为如果下层崩溃了,那么我们就会静静地看到业务服务,因为合同是严格固定在其中的。因此,目前开发物联网项目的第二条规则是隔离服务和使用合同。
该报告仍然更加有趣:模拟,负载测试,总体而言,我建议您查看此报告。

在第三部分中,我将讨论仿真模型,敬请期待!

All Articles