一个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
接口。 之后,每个前端都应该更改为发送所有消息,而不仅仅是一个:
[]http.Request
而不是http.Request
),并且format.FromUp
应该用于创建这些请求。topic.Send
的每个结果执行i.format.FromUp
。[][]byte
,因为默认的 JSON 格式只返回一条消息。是的,但我会将其留给社区。
@adriansmares我目前正在处理这个问题。 在 formatter.go 文件中将 *ttnpb.ApplicationUp 函数从 []byte 更改为 [][]byte 后,我也在 json.go 和 protobuf.go 文件中更改了它。 但是现在,它给出了以下错误:
请提供一些建议。 谢谢你。
@mihirr93由于您现在正在修改调用站点以返回一个结果片段(每个结果都是一个字节片段),因此您应该用[][]byte{ slice-containing-result }
包装仅返回一个结果的位置。
你好@adriansmares先生。 我对我们的方法有点困惑。 我们试图将前端消息(原始有效负载)分割成多条消息。 但在这种情况下,消息将被切片的点取决于我们未知的编码(Cayenne 或任何自定义编码)。 所以,要做到这一点,我们需要使用解码的有效载荷。 一旦用户在 TTN 的负载格式选项卡中输入解码器,应用程序实际上会以 JSON 格式显示解码值(如图所示)。 我们不应该直接拆分这个解码的有效负载并使用 http 进一步推送它吗?
你能解释一下这种混乱吗?
并且,我们是否只是修改 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
文件生成的。 尽管如此,就本问题而言,无需进行此类修改。让我试着更好地解释整个管道,也许这会阐明它是如何工作的
*ttnpb.ApplicationUplink
。FRMPayload
字段中的字节转换为DecodedPayload
的解码结构)。*ttnpb.ApplicationUp
并将其发送到前端(MQTT、webhooks、PubSub、应用程序包)。*ttnpb.ApplicationUp
并根据他们的设置(格式)选择要使用的格式化程序(格式化程序接口在pkg/applicationserver/io/formatters
)。*ttnph.ApplicationUp
调用formatter.FromUp
并接收一个字节切片 ( []byte
),代表 1 条消息的正文。现在,关于这个问题,我希望可以看到只有第 6 步需要更改:
FromUp
可以返回多个有效负载(字节切片的切片)。Protobuf
和JSON
格式不应改变它们的行为,因此应继续仅返回 1 个有效负载(这就是需要包装的原因)。[][]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
调整您的更改,以避免以后发生冲突。
这个问题已经有一段时间没有活动了,所以让我们把它移回分类,看看是否还有这个需求,或者我们应该放弃它。
这不再需要,因为它的扩展性很差。