Lorawan-stack: 支持来自一种格式的多个结果消息

创建于 2019-08-13  ·  12评论  ·  资料来源: TheThingsNetwork/lorawan-stack

概括

一个ttnpb.ApplicationUp应该能够产生多个前端消息,而不是一个。

我们为什么需要这个?

目前,一个ttnpb.ApplicationUp仅被编组为一个字节切片,从而生成一个前端消息(一个 HTTP 调用、一个 MQTT 消息等)。 但是,如果上行链路的有效载荷包含多个测量值(想想连接到一个设备的多个传感器),那么允许某些格式为每个测量值创建多个消息会很有帮助。

什么已经存在? 你现在看到了什么?

https://github.com/TheThingsNetwork/lorawan-stack/blob/9112dd7bcd3f1f03190055542908b427b27acd1c/pkg/applicationserver/io/formatters/formatter.go#L23
该格式当前仅返回一条要发送到前端的消息。

有什么不见了? 你要看什么?

结果应从[]byte更改[][]byte

环境

93ea01b4b5af2672da5883b570be825a54b8afc1

您建议如何实施?

更改Format接口。 之后,每个前端都应该更改为发送所有消息,而不仅仅是一个:

你能自己做这个并提交拉取请求吗?

是的,但我会将其留给社区。

application server needtriage

所有12条评论

@adriansmares我目前正在处理这个问题。 在 formatter.go 文件中将 *ttnpb.ApplicationUp 函数从 []byte 更改为 [][]byte 后,我也在 json.go 和 protobuf.go 文件中更改了它。 但是现在,它给出了以下错误:
image
请提供一些建议。 谢谢你。

@mihirr93由于您现在正在修改调用站点以返回一个结果片段(每个结果都是一个字节片段),因此您应该用[][]byte{ slice-containing-result }包装仅返回一个结果的位置。

你好@adriansmares先生。 我对我们的方法有点困惑。 我们试图将前端消息(原始有效负载)分割成多条消息。 但在这种情况下,消息将被切片的点取决于我们未知的编码(Cayenne 或任何自定义编码)。 所以,要做到这一点,我们需要使用解码的有效载荷。 一旦用户在 TTN 的负载格式选项卡中输入解码器,应用程序实际上会以 JSON 格式显示解码值(如图所示)。 我们不应该直接拆分这个解码的有效负载并使用 http 进一步推送它吗?
image
你能解释一下这种混乱吗?
并且,我们是否只是修改 pkg 文件而 .proto 文件会自动更新?
非常感谢您的支持和时间。

_请记住,此存储库包含我们 LoRaWAN 堆栈的 V3 版本,提供的图片来自 TTN,它运行的是V2版本。_

请记住,此问题仅涉及支持从一条消息生成多个有效负载,您可能感兴趣的新格式问题是https://github.com/TheThingsNetwork/lorawan-stack/issues/1158。 请检查此问题是否确实是您要查找的问题。

尽管如此,在这种情况下,我建议您引入一种基于JSON格式的新格式,该格式基于在上行链路中找到的解码信息生成多个有效负载。
您可能希望专门处理ApplicationUp_UplinkMessage上行链路消息,因为它包含您感兴趣的字段作为DecodedPayload字段的一部分。

您不必为此用例修改 proto 文件。

我仍在尝试了解堆栈的内部工作和代码。 目前,我只关注这个问题,而不是#1158。 正如您建议修改ApplicationUp_Uplinkmessage ,我正在尝试解决这个问题。 目前我有以下疑问:
1)struct ApplicationUplink和func ApplicationUp_UplinkMessage定义在ttnpb目录下的messages.pb.go文件中。 然而,文件的标题提到“请勿编辑”。 我应该忽略它吗? 或者有没有其他方法可以修改它?
2)你能更详细地描述一下“ introduce a new format, based on the JSON one ”吗? 我认为我们只需要修改现有格式而不是创建新格式。
3)您还提到了只返回一个结果的结果包装。 那究竟会做什么? 它是否会将多条消息重新组合为一条消息并仅返回一个结果? 我们是否只需要对上行链路消息执行此操作?

[感谢@adriansmares的持续指导。 虽然,我拿的是golang教程,这是我第一次接触GO语言,请多多包涵】

  • 您不需要编辑该文件,也不需要编辑任何*.pb.*文件。 这些是在您运行mage proto:all时自动从*.proto文件生成的。 尽管如此,就本问题而言,无需进行此类修改。
  • 请在另一个问题中跟进。
  • 包装建议允许您保持现有代码完整无缺(仍然返回一个要从一个上行链路消息发布的有效负载)。

让我试着更好地解释整个管道,也许这会阐明它是如何工作的

  1. 一个设备发送1条上行链路。
  2. 1 上行链路由网关接收并发送到网关服务器,网关服务器将其转发给网络服务器,然后网络服务器将其转发给应用服务器。 到达 AS 的消息是*ttnpb.ApplicationUplink
  3. AS 使用这 1 个上行链路并尝试使用设备负载格式化程序将其二进制负载解码为解码字段(将FRMPayload字段中的字节转换为DecodedPayload的解码结构)。
  4. 然后将其封装在*ttnpb.ApplicationUp并将其发送到前端(MQTT、webhooks、PubSub、应用程序包)。
  5. 前端收到这 1 *ttnpb.ApplicationUp并根据他们的设置(格式)选择要使用的格式化程序(格式化程序接口在pkg/applicationserver/io/formatters )。
  6. 前端用 1 *ttnph.ApplicationUp调用formatter.FromUp并接收一个字节切片 ( []byte ),代表 1 条消息的正文。

现在,关于这个问题,我希望可以看到只有第 6 步需要更改:

  • 更改接口和实现的返回类型,以便FromUp可以返回多个有效负载(字节切片的切片)。
  • 现有的ProtobufJSON格式不应改变它们的行为,因此应继续仅返回 1 个有效负载(这就是需要包装的原因)。
  • 更改这 2 个(接口和格式化程序)以返回[][]byte您将开始看到错误(因为大多数调用FromUp只希望收到一个有效负载 - 您必须更新这些调用站点并让它们遍历有效负载列表并发送它们中的每一个。

只有在这个根本不会改变行为的问题得到修复之后,您才能继续实现自己的格式化程序,这将返回多个有效负载的切片,每个测量一个(但请记住,这种新格式超出了这个问题的范围!)。

非常感谢@adriansmares提供如此详细的解释和指南。 我想我现在已经设法解决了这个问题。 我还用./mage go:test js:test jsSDK:test对其进行了测试,它没有给出任何错误。 代码中所做的主要更改突出显示如下:
格式化程序

type Formatter interface {
    FromUp(*ttnpb.ApplicationUp) ([][]byte, error)
    ToDownlinks([]byte) (*ttnpb.ApplicationDownlinks, error)
    ToDownlinkQueueRequest([]byte) (*ttnpb.DownlinkQueueRequest, error)
}

json.go

func (json) FromUp(msg *ttnpb.ApplicationUp) ([][]byte, error) {
    m, e := jsonpb.TTN().Marshal(msg)
    return [][]byte{m}, e
}

protobuf.go
和上面一样

mqtt.go

buf, err := c.format.FromUp(up.ApplicationUp)
                if err != nil {
                    logger.WithError(err).Warn("Failed to marshal upstream message")
                    continue
                }
                logger.Debug("Publish upstream message")
                for _, v := range buf {
                    c.session.Publish(&packet.PublishPacket{
                        TopicName:  topic.Join(topicParts),
                        TopicParts: topicParts,
                        QoS:        qosUpstream,
                        Message:    v,
                    })
                }

发布订阅
和上面一样

webhooks.go

unc (w *webhooks) newRequest(ctx context.Context, msg *ttnpb.ApplicationUp, hook *ttnpb.ApplicationWebhook) ([]*http.Request, error) {
    var cfg *ttnpb.ApplicationWebhook_Message
    switch msg.Up.(type) {
    case *ttnpb.ApplicationUp_UplinkMessage:
        cfg = hook.UplinkMessage
    case *ttnpb.ApplicationUp_JoinAccept:
        cfg = hook.JoinAccept
    case *ttnpb.ApplicationUp_DownlinkAck:
        cfg = hook.DownlinkAck
    case *ttnpb.ApplicationUp_DownlinkNack:
        cfg = hook.DownlinkNack
    case *ttnpb.ApplicationUp_DownlinkSent:
        cfg = hook.DownlinkSent
    case *ttnpb.ApplicationUp_DownlinkFailed:
        cfg = hook.DownlinkFailed
    case *ttnpb.ApplicationUp_DownlinkQueued:
        cfg = hook.DownlinkQueued
    case *ttnpb.ApplicationUp_LocationSolved:
        cfg = hook.LocationSolved
    }
    if cfg == nil {
        return nil, nil
    }
    url, err := url.Parse(hook.BaseURL)
    if err != nil {
        return nil, err
    }
    url.Path = path.Join(url.Path, cfg.Path)
    expandVariables(url, msg)
    if err != nil {
        return nil, err
    }
    format, ok := formats[hook.Format]
    if !ok {
        return nil, errFormatNotFound.WithAttributes("format", hook.Format)
    }
    buf, err := format.FromUp(msg)
    if err != nil {
        return nil, err
    }
    var requests []*http.Request
    for i, v := range buf {
        req, err := http.NewRequest(http.MethodPost, url.String(), bytes.NewReader(v))
        requests[i] = req
        if err != nil {
            return nil, err
        }
        for key, value := range hook.Headers {
            req.Header.Set(key, value)
        }
        if hook.DownlinkAPIKey != "" {
            req.Header.Set(downlinkKeyHeader, hook.DownlinkAPIKey)
            req.Header.Set(downlinkPushHeader, w.createDownlinkURL(ctx, hook.ApplicationWebhookIdentifiers, msg.EndDeviceIdentifiers, "push"))
            req.Header.Set(downlinkReplaceHeader, w.createDownlinkURL(ctx, hook.ApplicationWebhookIdentifiers, msg.EndDeviceIdentifiers, "replace"))
        }
        req.Header.Set("Content-Type", format.ContentType)
        req.Header.Set("User-Agent", userAgent)
    }
    return requests, nil
}

请提供您的反馈和意见。 任务完成后,我将根据代码样式指南进行更正(Var 命名、缩进等)。 如果这看起来正确,请您进一步指导我解决问题 #1158 。 再一次谢谢你。

@mihirr93看起来不错! 请提交您的更改并为此问题提交拉取请求。 合并后,我们可以继续使用#1158 中提到的新格式化程序。

@adriansmares当然,我会这样做。 该拉取请求应该基于上述环境还是最新版本的堆栈?

我建议您根据最新的master调整您的更改,以避免以后发生冲突。

这个问题已经有一段时间没有活动了,所以让我们把它移回分类,看看是否还有这个需求,或者我们应该放弃它。

这不再需要,因为它的扩展性很差。

此页面是否有帮助?
0 / 5 - 0 等级

相关问题

htdvisser picture htdvisser  ·  9评论

htdvisser picture htdvisser  ·  4评论

thinkOfaNumber picture thinkOfaNumber  ·  4评论

adamsondelacruz picture adamsondelacruz  ·  7评论

kschiffer picture kschiffer  ·  6评论