Lorawan-stack: Поддержка нескольких результирующих сообщений из одного формата

Созданный на 13 авг. 2019  ·  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 Я сейчас работаю над этой проблемой. После изменения байта [] на байт [] [] для функции * ttnpb.ApplicationUp в файле formatter.go я также изменил его в файлах json.go и protobuf.go. Но теперь выдает следующую ошибку:
image
Пожалуйста, дайте несколько предложений. Спасибо.

@ mihirr93 Поскольку вы сейчас модифицируете сайты вызовов, чтобы они возвращали часть результатов (каждый результат представляет собой часть байтов), вам следует обернуть места, где возвращается только один результат, с помощью [][]byte{ slice-containing-result } .

Привет, мистер @adriansmares . Я немного смущен нашим подходом. Мы привязываем, чтобы разделить внешнее сообщение (необработанную полезную нагрузку) на несколько сообщений. Но в этом случае точка, в которой сообщение будет разрезано, зависит от его кодировки (Cayenne или любая другая кодировка), которая нам неизвестна. Итак, для этого нам нужно использовать декодированную полезную нагрузку. Как только пользователь вводит декодер на вкладке формата полезной нагрузки TTN, приложение фактически показывает декодированные значения в JSON (как показано на рисунке). Разве мы не должны напрямую разделить эту декодированную полезную нагрузку и продвинуть ее дальше, используя http?
image
Не могли бы вы пролить свет на эту путаницу?
И мы просто изменяем файлы pkg, и файлы .proto будут автоматически обновляться?
Большое спасибо за вашу поддержку и время.

_ Имейте в виду, что этот репозиторий содержит версию V3 нашего стека LoRaWAN, а предоставленное изображение взято из TTN, который запускает версию V2. _

Имейте в виду, что эта проблема касается только поддержки создания нескольких полезных нагрузок из одного сообщения, а проблема нового формата, которая вас, вероятно, интересует, - это https://github.com/TheThingsNetwork/lorawan-stack/issues/1158. Пожалуйста, проверьте, действительно ли эта проблема является той, которую вы ищете.

Тем не менее, в этом случае я бы посоветовал вам ввести новый формат, основанный на формате JSON , который создает несколько полезных нагрузок на основе декодированной информации, находящейся внутри восходящего канала.
Вероятно, вы захотите обработать сообщение восходящей ссылки ApplicationUp_UplinkMessage специально, поскольку оно содержит интересующие вас поля как часть поля DecodedPayload .

Вам не нужно изменять прото-файлы для этого варианта использования.

Я все еще пытаюсь понять внутреннюю работу и код стека. В настоящее время я занимаюсь только этим вопросом, а не №1158. Поскольку вы предлагали изменить ApplicationUp_Uplinkmessage , я пытаюсь понять это. На данный момент у меня есть следующие вопросы:
1) Структура ApplicationUplink и функция ApplicationUp_UplinkMessage определены в файле messages.pb.go в каталоге ttnpb. Однако в заголовке файла упоминается «НЕ РЕДАКТИРОВАТЬ». Должен ли я игнорировать это? или есть альтернативный подход к его изменению?
2) Не могли бы вы описать это поподробнее " introduce a new format, based on the JSON one "? Я подумал, что нам просто нужно изменить существующий формат, а не создавать новый.
3) Вы также упомянули об упаковке результатов, при которой возвращается только один результат. Что именно это будет делать? Будет ли он повторно объединять несколько сообщений в одно и возвращать только один результат? И нужно ли это делать только для сообщения Uplink?

[Спасибо @adriansmares за постоянное руководство. Хотя я взял учебник по голангу, это мое первое знакомство с языком GO, простите за то, что беспокою вас столькими сомнениями]

  • Вам не нужно редактировать этот файл или какие-либо файлы *.pb.* . Они генерируются из файлов *.proto автоматически при запуске mage proto:all . Тем не менее, для целей данного выпуска такие модификации вносить не нужно.
  • Пожалуйста, ответьте на этот вопрос в другом выпуске.
  • Предложение по упаковке позволяет сохранить существующий код в неизменном виде (по-прежнему возвращая одну полезную нагрузку, которая будет отправлена ​​из одного сообщения восходящей ссылки).

Позвольте мне попытаться лучше объяснить весь конвейер, возможно, это прольет свет на то, как это работает

  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. Интерфейс вызывает formatter.FromUp с 1 *ttnph.ApplicationUp и получает фрагмент байта ( []byte ), представляющий тело 1 сообщения.

Теперь, что касается этой проблемы, я надеюсь, что видно, что только шаг 6 нуждается в изменениях:

  • Изменение типов возвращаемых значений как интерфейса, так и реализаций таким образом, что FromUp может возвращать несколько полезных данных (срез срезов байта).
  • Существующие форматы Protobuf и JSON не должны изменять своего поведения и, как таковые, должны продолжать возвращать только 1 полезную нагрузку (поэтому требуется перенос).
  • После изменения этих 2 (интерфейс и средства форматирования) для возврата [][]byte вы начнете видеть ошибки (поскольку большинство мест, вызывающих FromUp ожидают получения только одной полезной нагрузки, вам необходимо обновить эти сайты вызывают и заставляют их перебирать список полезных данных и отправлять каждый из них.

Только после того, как эта проблема, которая вообще не меняет поведение, будет исправлена, вы можете перейти к реализации собственного средства форматирования, которое будет возвращать часть нескольких полезных нагрузок, по одной для каждого измерения (но имейте в виду, что этот новый формат выходит за рамки данной проблемы!).

Большое спасибо @adriansmares за такое подробное объяснение и руководство. Думаю, сейчас мне удалось решить эту проблему. Я также тестировал его с помощью ./mage go:test js:test jsSDK:test и он не выдает ошибок. Основные изменения, внесенные в код, выделены ниже:
formatter.go

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,
                    })
                }

pubsub.go
То же, что и выше

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 рейтинги