func HandleScheduleNextQueueItem(ctx context.Context, db sqlx.Ext, ds storage.DeviceSession, mode storage.DeviceMode) error {
	nqctx := dataContext{
		ctx:             ctx,
		DB:              db,
		DBInTransaction: true,  
		DeviceMode:      mode,
		DeviceSession:   ds,
	}
	for _, t := range scheduleNextQueueItemTasks {
		if err := t(&nqctx); err != nil {
			if err == ErrAbort {
				return nil
			}
			return err
		}
	}
	return nil
}
HandleScheduleNextQueueItem handles scheduling the next device-queue item.
package data
import (
	"bytes"
	"context"
	"encoding/binary"
	"fmt"
	"time"
	"github.com/gofrs/uuid"
	"github.com/golang/protobuf/ptypes"
	"github.com/jmoiron/sqlx"
	"github.com/pkg/errors"
	log "github.com/sirupsen/logrus"
	"github.com/brocaar/chirpstack-api/go/v3/as"
	"github.com/brocaar/chirpstack-api/go/v3/gw"
	adrr "github.com/brocaar/chirpstack-network-server/v3/adr"
	"github.com/brocaar/chirpstack-network-server/v3/internal/adr"
	"github.com/brocaar/chirpstack-network-server/v3/internal/backend/applicationserver"
	"github.com/brocaar/chirpstack-network-server/v3/internal/backend/gateway"
	"github.com/brocaar/chirpstack-network-server/v3/internal/band"
	"github.com/brocaar/chirpstack-network-server/v3/internal/channels"
	"github.com/brocaar/chirpstack-network-server/v3/internal/config"
	dwngateway "github.com/brocaar/chirpstack-network-server/v3/internal/downlink/gateway"
	"github.com/brocaar/chirpstack-network-server/v3/internal/gps"
	"github.com/brocaar/chirpstack-network-server/v3/internal/helpers"
	"github.com/brocaar/chirpstack-network-server/v3/internal/logging"
	"github.com/brocaar/chirpstack-network-server/v3/internal/maccommand"
	"github.com/brocaar/chirpstack-network-server/v3/internal/models"
	"github.com/brocaar/chirpstack-network-server/v3/internal/roaming"
	"github.com/brocaar/chirpstack-network-server/v3/internal/storage"
	"github.com/brocaar/lorawan"
	"github.com/brocaar/lorawan/backend"
	loraband "github.com/brocaar/lorawan/band"
	"github.com/brocaar/lorawan/sensitivity"
)
const (
	defaultCodeRate        = "4/5"
	deviceDownlinkLockKey  = "lora:ns:device:%s:down:lock"
	gatewayDownlinkLockKey = "lora:ns:gw:%s:down:lock"
)
type incompatibleCIDMapping struct {
	CID              lorawan.CID
	IncompatibleCIDs []lorawan.CID
}
var incompatibleMACCommands = []incompatibleCIDMapping{
	{CID: lorawan.NewChannelReq, IncompatibleCIDs: []lorawan.CID{lorawan.LinkADRReq}},
	{CID: lorawan.LinkADRReq, IncompatibleCIDs: []lorawan.CID{lorawan.NewChannelReq}},
}
var (
	rejoinRequestEnabled   bool
	rejoinRequestMaxCountN int
	rejoinRequestMaxTimeN  int
	classBPingSlotDR        int
	classBPingSlotFrequency uint32
	rxWindow              int
	rx2PreferOnRX1DRLt    int
	rx2PreferOnLinkBudget bool
	rx2Frequency uint32
	rx2DR        int
	rx1DROffset int
	rx1Delay    int
	downlinkTXPower int
	disableMACCommands bool
	disableADR bool
	classCDeviceDownlinkLockDuration  time.Duration
	classCGatewayDownlinkLockDuration time.Duration
	uplinkDwellTime400ms   bool
	downlinkDwellTime400ms bool
	uplinkMaxEIRPIndex     uint8
	maxMACCommandErrorCount int
	gatewayPreferMinMargin float64
)
var setMACCommandsSet = setMACCommands(
	requestCustomChannelReconfiguration,
	requestChannelMaskReconfiguration,
	requestADRChange,
	requestDevStatus,
	requestRejoinParamSetup,
	setPingSlotParameters,
	setRXParameters,
	setTXParameters,
	getMACCommandsFromQueue,
)
var responseTasks = []func(*dataContext) error{
	getDeviceProfile,
	getServiceProfile,
	setDeviceGatewayRXInfo,
	selectDownlinkGateway,
	setDataTXInfo,
	setToken,
	getNextDeviceQueueItem,
	setMACCommandsSet,
	stopOnNothingToSend,
	setPHYPayloads,
	isRoaming(false,
		sendDownlinkFrame,
	),
	isRoaming(true,
		sendDownlinkFramePassiveRoaming,
	),
	saveDeviceSession,
	saveDownlinkFrame,
}
var scheduleNextQueueItemTasks = []func(*dataContext) error{
	getDeviceProfile,
	getServiceProfile,
	forClass(storage.DeviceModeC,
		getDownlinkDeviceLock,
	),
	setDeviceGatewayRXInfo,
	selectDownlinkGateway,
	forClass(storage.DeviceModeC,
		getDownlinkGatewayLock,
		setImmediately,
		setTXInfoForRX2,
	),
	forClass(storage.DeviceModeB,
		setTXInfoForClassB,
	),
	forClass(storage.DeviceModeA,
		returnInvalidDeviceClassError,
	),
	setToken,
	getNextDeviceQueueItem,
	stopOnNothingToSend,
	setPHYPayloads,
	sendDownlinkFrame,
	saveDeviceSession,
	saveDownlinkFrame,
	setDeviceQueueItemRetryAfter,
}
func Setup(conf config.Config) error {
	nsConf := conf.NetworkServer.NetworkSettings
	rejoinRequestEnabled = nsConf.RejoinRequest.Enabled
	rejoinRequestMaxCountN = nsConf.RejoinRequest.MaxCountN
	rejoinRequestMaxTimeN = nsConf.RejoinRequest.MaxTimeN
	classBPingSlotDR = nsConf.ClassB.PingSlotDR
	classBPingSlotFrequency = nsConf.ClassB.PingSlotFrequency
	rx2Frequency = uint32(nsConf.RX2Frequency)
	rx2DR = nsConf.RX2DR
	rx1DROffset = nsConf.RX1DROffset
	rx1Delay = nsConf.RX1Delay
	rxWindow = nsConf.RXWindow
	rx2PreferOnRX1DRLt = nsConf.RX2PreferOnRX1DRLt
	rx2PreferOnLinkBudget = nsConf.RX2PreferOnLinkBudget
	downlinkTXPower = nsConf.DownlinkTXPower
	disableMACCommands = nsConf.DisableMACCommands
	disableADR = nsConf.DisableADR
	classCDeviceDownlinkLockDuration = conf.NetworkServer.Scheduler.ClassC.DeviceDownlinkLockDuration
	classCGatewayDownlinkLockDuration = conf.NetworkServer.Scheduler.ClassC.GatewayDownlinkLockDuration
	uplinkDwellTime400ms = conf.NetworkServer.Band.UplinkDwellTime400ms
	downlinkDwellTime400ms = conf.NetworkServer.Band.DownlinkDwellTime400ms
	maxEIRP := conf.NetworkServer.Band.UplinkMaxEIRP
	if maxEIRP == -1 {
		maxEIRP = band.Band().GetDefaultMaxUplinkEIRP()
	}
	uplinkMaxEIRPIndex = lorawan.GetTXParamSetupEIRPIndex(maxEIRP)
	maxMACCommandErrorCount = conf.NetworkServer.NetworkSettings.MaxMACCommandErrorCount
	gatewayPreferMinMargin = conf.NetworkServer.NetworkSettings.GatewayPreferMinMargin
	return nil
}
type dataContext struct {
	ctx context.Context
	DB              sqlx.Ext
	DBInTransaction bool
	DeviceMode storage.DeviceMode
	ServiceProfile storage.ServiceProfile
	DeviceProfile storage.DeviceProfile
	DeviceSession storage.DeviceSession
	DeviceGatewayRXInfo []storage.DeviceGatewayRXInfo
	MustSend bool
	ACK bool
	RXPacket *models.RXPacket
	Immediately bool
	MACCommands []storage.MACCommandBlock
	DeviceQueueItem *storage.DeviceQueueItem
	MoreDeviceQueueItems bool
	DownlinkFrame gw.DownlinkFrame
	DownlinkFrameItems []downlinkFrameItem
	DownlinkGateway storage.DeviceGatewayRXInfo
}
type downlinkFrameItem struct {
	DownlinkFrameItem gw.DownlinkFrameItem
	RemainingPayloadSize int
}
func forClass(mode storage.DeviceMode, tasks ...func(*dataContext) error) func(*dataContext) error {
	return func(ctx *dataContext) error {
		if mode != ctx.DeviceMode {
			return nil
		}
		for _, f := range tasks {
			if err := f(ctx); err != nil {
				return err
			}
		}
		return nil
	}
}
func isRoaming(r bool, tasks ...func(*dataContext) error) func(*dataContext) error {
	return func(ctx *dataContext) error {
		if r == (ctx.RXPacket.RoamingMetaData != nil) {
			for _, f := range tasks {
				if err := f(ctx); err != nil {
					return err
				}
			}
		}
		return nil
	}
}
func HandleResponse(ctx context.Context, rxPacket models.RXPacket, sp storage.ServiceProfile, ds storage.DeviceSession, adr, mustSend, ack bool, macCommands []storage.MACCommandBlock) error {
	rctx := dataContext{
		ctx:             ctx,
		DB:              storage.DB(),
		DBInTransaction: false,
		ServiceProfile:  sp,
		DeviceSession:   ds,
		ACK:             ack,
		MustSend:        mustSend,
		RXPacket:        &rxPacket,
		MACCommands:     macCommands,
	}
	for _, t := range responseTasks {
		if err := t(&rctx); err != nil {
			if err == ErrAbort {
				return nil
			}
			return err
		}
	}
	return nil
}