前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >The Things Network LoRaWAN Stack V3 学习笔记 3.1.2.2 下行 MAC 命令处理流程

The Things Network LoRaWAN Stack V3 学习笔记 3.1.2.2 下行 MAC 命令处理流程

作者头像
twowinter
发布2020-04-17 12:18:56
3430
发布2020-04-17 12:18:56
举报
文章被收录于专栏:twowintertwowinter

前言

MAC 命令下行,目前和数据下行一样,主要也是在 generateDownlink() 进行处理。

小能手这段时间在学习 The Things Network LoRaWAN Stack V3,从使用和代码等角度对该 Stack 进行了分析,详细可点此查看

1 代码流程梳理

1.1 MAC 命令准备

代码语言:javascript
复制
	dev.MACState.PendingRequests = dev.MACState.PendingRequests[:0]

	maxDownLen, maxUpLen, ok, err := enqueueLinkADRReq(ctx, dev, maxDownLen, maxUpLen, ns.FrequencyPlans)
	if err != nil {
		return nil, err
	}
	fPending := !ok
	for _, f := range []func(context.Context, *ttnpb.EndDevice, uint16, uint16) (uint16, uint16, bool){
		// LoRaWAN 1.0+
		enqueueNewChannelReq,
		enqueueDutyCycleReq,
		enqueueRxParamSetupReq,
		func(ctx context.Context, dev *ttnpb.EndDevice, maxDownLen uint16, maxUpLen uint16) (uint16, uint16, bool) {
			return enqueueDevStatusReq(ctx, dev, maxDownLen, maxUpLen, ns.defaultMACSettings)
		},
		enqueueRxTimingSetupReq,
		enqueuePingSlotChannelReq,
		enqueueBeaconFreqReq,

		// LoRaWAN 1.0.2+
		enqueueTxParamSetupReq,
		enqueueDLChannelReq,

		// LoRaWAN 1.1+
		enqueueADRParamSetupReq,
		enqueueForceRejoinReq,
		enqueueRejoinParamSetupReq,
	} {
		var ok bool
		maxDownLen, maxUpLen, ok = f(ctx, dev, maxDownLen, maxUpLen)
		fPending = fPending || !ok
	}
	cmds = append(cmds, dev.MACState.PendingRequests...)

在各类 MAC 命令的子模块中,都会调用 enqueueMACCommand() 来进行 MAC 命令的处理。这里得到的 cmds 传入 enqueueMACCommand() 函数中,再把 MAC 命令传递给 dev.MACState.PendingRequests。

代码语言:javascript
复制
func enqueueMACCommand(cid ttnpb.MACCommandIdentifier, maxDownLen, maxUpLen uint16, f func(nDown, nUp uint16) ([]*ttnpb.MACCommand, uint16, bool), cmds ...*ttnpb.MACCommand) ([]*ttnpb.MACCommand, uint16, uint16, bool) {
	desc := lorawan.DefaultMACCommands[cid]
	maxDown := maxDownLen / (1 + desc.DownlinkLength)
	maxUp := maxUpLen / (1 + desc.UplinkLength)
	enq, nUp, ok := f(maxDown, maxUp)
	if len(enq) > int(maxDown) || nUp > maxUp {
		panic("invalid amount of MAC commands enqueued")
	}
	return append(cmds, enq...), maxDownLen - uint16(len(enq))*desc.DownlinkLength, maxUpLen - nUp*desc.UplinkLength, ok
}

这里有个格式转化,初看的时候被绕蒙圈了,以 LinkADRReq 为例,做了个梳理。

函数

类型

包含内容

cmds

*ttnpb.MACCommand

CID + Payload

pld.MACCommand()

*MACCommand

CID + Payload

cmd.GetLinkADRReq()

*MACCommand_LinkADRReq

Payload

pld

*MACCommand_LinkADRReq

Payload

MACCommand() 是从 payload 获得完整 MAC 命令,GetPayload() 或者具体的 GetLinkADRReq() 是从 MAC 命令获得具体的 Payload。

代码语言:javascript
复制
// MACCommand returns the LinkADRReq MAC command as a *MACCommand.
func (pld *MACCommand_LinkADRReq) MACCommand() *MACCommand {
	return &MACCommand{
		CID: CID_LINK_ADR,
		Payload: &MACCommand_LinkADRReq_{
			LinkADRReq: pld,
		},
	}
}
代码语言:javascript
复制
func (m *MACCommand) GetLinkADRReq() *MACCommand_LinkADRReq {
	if x, ok := m.GetPayload().(*MACCommand_LinkADRReq_); ok {
		return x.LinkADRReq
	}
	return nil
}
代码语言:javascript
复制
type MACCommand struct {
	CID MACCommandIdentifier `protobuf:"varint,1,opt,name=cid,proto3,enum=ttn.lorawan.v3.MACCommandIdentifier" json:"cid,omitempty"`
	Payload              isMACCommand_Payload `protobuf_oneof:"payload"`
	XXX_NoUnkeyedLiteral struct{}             `json:"-"`
	XXX_sizecache        int32                `json:"-"`
}

1.2 MAC 命令组帧并下发

前头准备好的各类 MAC 命令,即 dev.MACState.PendingRequests,在这一步会被打散传入 cmds,进行组帧下发。

代码语言:javascript
复制
	cmds = append(cmds, dev.MACState.PendingRequests...)
	...
	cmdBuf := make([]byte, 0, maxDownLen)
	for _, cmd := range cmds {
		logger := logger.WithField("cid", cmd.CID)
		logger.Debug("Add MAC command to buffer")
		var err error
		cmdBuf, err = spec.AppendDownlink(phy, cmdBuf, *cmd)
		if err != nil {
			return nil, errEncodeMAC.WithCause(err)
		}
		if mType == ttnpb.MType_UNCONFIRMED_DOWN && spec[cmd.CID].ExpectAnswer && dev.MACState.DeviceClass == ttnpb.CLASS_C {
			logger.Debug("Use confirmed downlink to get immediate answer")
			mType = ttnpb.MType_CONFIRMED_DOWN
		}
	}

cmds 通过 spec.AppendDownlink() 传入,接下来的几步都在 mac.go 中进行处理。

代码语言:javascript
复制
	mType := ttnpb.MType_UNCONFIRMED_DOWN
	cmdBuf := make([]byte, 0, maxDownLen)
	for _, cmd := range cmds {
		logger := logger.WithField("cid", cmd.CID)
		logger.Debug("Add MAC command to buffer")
		var err error
		cmdBuf, err = spec.AppendDownlink(phy, cmdBuf, *cmd)
		if err != nil {
			return nil, errEncodeMAC.WithCause(err)
		}
		if mType == ttnpb.MType_UNCONFIRMED_DOWN && spec[cmd.CID].ExpectAnswer && dev.MACState.DeviceClass == ttnpb.CLASS_C {
			logger.Debug("Use confirmed downlink to get immediate answer")
			mType = ttnpb.MType_CONFIRMED_DOWN
		}
	}

一层层传递下去,AppendDownlink -> desc.AppendDownlink -> 各个MAC命令所对应的 MACCommandSpec

代码语言:javascript
复制
// AppendDownlink encodes downlink MAC command cmd, appends it to b and returns any errors encountered.
func (spec MACCommandSpec) AppendDownlink(phy band.Band, b []byte, cmd ttnpb.MACCommand) ([]byte, error) {
	return spec.append(phy, b, false, cmd)
}

func (spec MACCommandSpec) append(phy band.Band, b []byte, isUplink bool, cmd ttnpb.MACCommand) ([]byte, error) {
	desc, ok := spec[cmd.CID]
	if !ok || desc == nil {
		return nil, errUnknownMACCommand.WithAttributes("cid", fmt.Sprintf("0x%X", int32(cmd.CID)))
	}
	b = append(b, byte(cmd.CID))

	var appender func(phy band.Band, b []byte, cmd ttnpb.MACCommand) ([]byte, error)
	if isUplink {
		appender = desc.AppendUplink
		if appender == nil {
			return nil, errMACCommandUplink.WithAttributes("cid", fmt.Sprintf("0x%X", int32(cmd.CID)))
		}
	} else {
		appender = desc.AppendDownlink
		if appender == nil {
			return nil, errMACCommandDownlink.WithAttributes("cid", fmt.Sprintf("0x%X", int32(cmd.CID)))
		}
	}

	b, err := appender(phy, b, cmd)
	if err != nil {
		return nil, errEncodingMACCommand.WithAttributes("cid", fmt.Sprintf("0x%X", int32(cmd.CID))).WithCause(err)
	}
	return b, nil
}

最后在 MACCommandSpec 里头完成 LoRaWAN 协议组帧,这个结构体相当重要,大部分逻辑都在这里头处理。

代码语言:javascript
复制
// DefaultMACCommands contains all the default MAC commands.
var DefaultMACCommands = MACCommandSpec{
	ttnpb.CID_xxx: &MACCommandDescriptor{
		AppendUplink: 
		UnmarshalUplink:
		AppendDownlink: 
		UnmarshalDownlink:
	},
	...

2 代码流程图

generateDownlink

各个MAC命令生成缓存

缓存传递至dev.MACState.PendingRequests

spec.AppendDownlink

enqueueMACCommand

desc.AppendDownlink

在各个MAC命令对应的MACCommandSpec完成协议组帧

END


iotisan 博客专家

发布了251 篇原创文章 · 获赞 253 · 访问量 84万+

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 1 代码流程梳理
    • 1.1 MAC 命令准备
      • 1.2 MAC 命令组帧并下发
      • 2 代码流程图
      • END
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档