Lorawan-stack: Suporta várias mensagens resultantes de um formato

Criado em 13 ago. 2019  ·  12Comentários  ·  Fonte: TheThingsNetwork/lorawan-stack

Resumo

Um ttnpb.ApplicationUp deve resultar em várias mensagens front-end, em vez de apenas uma.

Por que nós precisamos disso?

Atualmente, um ttnpb.ApplicationUp é empacotado em apenas uma fatia de byte que resulta em uma mensagem front-end (uma chamada HTTP, uma mensagem MQTT etc.). No entanto, se a carga útil do uplink contiver várias medições (pense em vários sensores conectados a um dispositivo), seria útil permitir que certos formatos criem várias mensagens para cada medição.

O que já está aí? O que você vê agora?

https://github.com/TheThingsNetwork/lorawan-stack/blob/9112dd7bcd3f1f03190055542908b427b27acd1c/pkg/applicationserver/io/formatters/formatter.go#L23
O formato atualmente retorna apenas uma mensagem a ser enviada aos front-ends.

O que está faltando? O que você quer ver?

O resultado deve ser alterado de []byte para [][]byte .

Ambiente

93ea01b4b5af2672da5883b570be825a54b8afc1

Como você pretende implementar isso?

Altere a interface Format . Depois disso, cada front-end deve ser alterado para enviar todas as mensagens em vez de apenas uma:

Você pode fazer isso sozinho e enviar uma solicitação pull?

Sim, mas vou deixar isso para a comunidade.

application server needtriage

Todos 12 comentários

@adriansmares Atualmente estou trabalhando nesta questão. Depois de mudar de [] byte para [] [] byte para a função * ttnpb.ApplicationUp no arquivo formatter.go, também mudei nos arquivos json.go e protobuf.go. Mas agora, dá o seguinte erro:
image
Por favor, dê algumas sugestões. Obrigada.

@ mihirr93 Como agora você está modificando os sites de chamada para retornar uma fatia dos resultados (cada resultado sendo uma fatia de bytes), você deve agrupar os locais onde apenas um resultado é retornado com [][]byte{ slice-containing-result } .

Olá Sr. @adriansmares . Estou um pouco confuso quanto à nossa abordagem. Estamos tentando dividir a mensagem front-end (carga útil bruta) em várias mensagens. Mas, neste caso, o ponto em que a mensagem será dividida depende de sua codificação (Cayenne ou qualquer codificação personalizada), que é desconhecida para nós. Portanto, para fazer isso, precisamos usar a carga decodificada. Depois que o usuário insere o decodificador na guia de formato de carga útil do TTN, o aplicativo realmente mostra os valores decodificados em JSON (como visto na imagem). Não deveríamos dividir diretamente essa carga decodificada e empurrá-la ainda mais usando http?
image
Você poderia, por favor, lançar alguma luz sobre essa confusão?
E, nós apenas modificamos os arquivos pkg e os arquivos .proto serão atualizados automaticamente?
Muito obrigado pelo seu amável apoio e tempo.

_Keep em mente que este repositório contém a versão V3 da nossa pilha LoRaWAN, ea imagem fornecida é de TTN, que executa o V2 version._

Lembre-se de que esse problema trata apenas do suporte para a produção de vários payloads de uma mensagem, e o novo problema de formato no qual você provavelmente está interessado é https://github.com/TheThingsNetwork/lorawan-stack/issues/1158. Verifique se esse problema é realmente o que você está procurando.

No entanto, sugiro, neste caso, que você introduza um novo formato, baseado no JSON , que produz várias cargas com base nas informações decodificadas encontradas dentro do uplink.
Você provavelmente desejará lidar com a mensagem ApplicationUp_UplinkMessage uplink especificamente, pois ela contém os campos nos quais você está interessado como parte do campo DecodedPayload .

Você não deveria ter que modificar os arquivos proto para este caso de uso.

Ainda estou tentando entender o funcionamento interno e o código da pilha. Atualmente, estou me concentrando apenas neste problema, e não no # 1158. Como você sugeriu modificar o ApplicationUp_Uplinkmessage , estou tentando descobrir isso. Tenho as seguintes perguntas neste momento:
1) A struct ApplicationUplink e a func ApplicationUp_UplinkMessage são definidas no arquivo messages.pb.go do diretório ttnpb. No entanto, o cabeçalho do arquivo menciona "NÃO EDITAR". Devo ignorar isso? ou existe alguma abordagem alternativa para modificá-lo?
2) Você pode descrever isso em mais detalhes " introduce a new format, based on the JSON one "? Achei que devíamos apenas modificar o formato existente em vez de criar um novo.
3) Você também mencionou o agrupamento de resultados em que apenas um resultado é retornado. O que exatamente isso vai fazer? Ele vai recombinar as mensagens múltiplas em uma e retornar apenas um resultado? E precisamos fazer isso apenas para mensagem Uplink?

[Obrigado @adriansmares por sua orientação contínua. Embora eu tenha feito o tutorial de golang, esta é minha primeira interação com a linguagem GO, perdão por incomodar com tantas dúvidas]

  • Você não precisa editar esse arquivo, nem quaisquer *.pb.* arquivos. Eles são gerados a partir dos arquivos *.proto automaticamente quando você executa mage proto:all . No entanto, para o propósito deste problema, nenhuma modificação precisa ser feita.
  • Acompanhe isso na outra edição.
  • A sugestão de empacotamento permite que você mantenha o código existente intacto (ainda retornando uma carga útil a ser postada de uma mensagem de uplink).

Deixe-me tentar explicar melhor todo o pipeline, talvez isso lance alguma luz sobre como isso funciona

  1. Um dispositivo envia 1 uplink.
  2. O 1 uplink é recebido pelo gateway e enviado ao Servidor Gateway, que o encaminha para o Servidor de Rede, que então o encaminha para o Servidor de Aplicativos. A mensagem que chega ao AS é do tipo *ttnpb.ApplicationUplink .
  3. O AS pega este 1 uplink e tenta decodificar sua carga binária nos campos decodificados (transforme os bytes no campo FRMPayload na estrutura decodificada de DecodedPayload ) usando o formatador de carga útil do dispositivo.
  4. Em seguida, ele o embrulha em *ttnpb.ApplicationUp e o envia para os front-ends (MQTT, webhooks, PubSub, pacotes de aplicativos).
  5. Os frontends recebem este 1 *ttnpb.ApplicationUp e com base em suas configurações (o formato) escolhem qual formatador usar (a interface do formatador é definida em pkg/applicationserver/io/formatters ).
  6. O frontend chama formatter.FromUp com o 1 *ttnph.ApplicationUp e recebe uma fatia do byte ( []byte ), representando o corpo de 1 mensagem.

Agora, com relação a esse problema, espero que seja visível que apenas a etapa 6 precisa de alterações:

  • Alterar os tipos de retorno da interface e das implementações de forma que FromUp possa retornar várias cargas úteis (uma fatia de fatias de byte).
  • Os formatos Protobuf e JSON não devem mudar seu comportamento e, como tal, devem continuar a retornar apenas 1 carga útil (é por isso que o envoltório é necessário).
  • Depois de alterar estes 2 (a interface e os formatadores) para retornar [][]byte você começará a ver erros (já que a maioria dos lugares que chamam FromUp esperam que apenas uma carga útil seja recebida - você deve atualizar esses sites chamam e fazem com que percorram a lista de cargas úteis e enviem cada um deles.

Somente depois que esse problema, que não altera o comportamento em nada, for corrigido, você pode prosseguir com a implementação de seu próprio formatador, que retornaria uma fatia de múltiplas cargas úteis, uma para cada medição (mas tenha em mente que este novo formato está fora do escopo deste problema!).

Muito obrigado @adriansmares por uma explicação e guia tão detalhados. Acho que consegui resolver esse problema agora. Eu também testei com ./mage go:test js:test jsSDK:test e não deu nenhum erro. As principais alterações feitas no código são destacadas abaixo:
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
O mesmo que acima

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
O mesmo que acima

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
}

Forneça seu feedback e comentários. Farei as correções (nomenclatura Var, recuo, etc) de acordo com as diretrizes de estilo de código, uma vez que a tarefa esteja completamente concluída. E se parecer correto, você poderia me orientar mais sobre o problema nº 1158. Agradeço novamente.

@ mihirr93 parece bom! Por favor, comprometa suas alterações e envie uma solicitação pull para este problema. Após mesclá-lo, podemos avançar para o novo formatador mencionado em # 1158.

@adriansmares Claro, vou fazer isso. Essa solicitação de pull deve ser baseada no ambiente mencionado ou na versão mais recente da pilha?

Eu recomendo que você rebase suas alterações sobre os master mais recentes para evitar conflitos no futuro.

Esse problema está inativo há um bom tempo, então vamos movê-lo de volta para a triagem para ver se ainda há demanda para isso ou se devemos simplesmente descartá-lo.

Isso não é mais necessário, pois tem uma escala insuficiente.

Esta página foi útil?
0 / 5 - 0 avaliações

Questões relacionadas

htdvisser picture htdvisser  ·  9Comentários

bafonins picture bafonins  ·  5Comentários

thinkOfaNumber picture thinkOfaNumber  ·  4Comentários

ecities picture ecities  ·  5Comentários

kschiffer picture kschiffer  ·  7Comentários