Skip to content

Commit

Permalink
ns: Add RelayConfigurationService WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
adriansmares committed Nov 24, 2023
1 parent cff5de1 commit 2ecb67d
Show file tree
Hide file tree
Showing 3 changed files with 212 additions and 7 deletions.
4 changes: 2 additions & 2 deletions pkg/networkserver/grpc_deviceregistry.go
Original file line number Diff line number Diff line change
Expand Up @@ -3565,7 +3565,7 @@ func (ns *NetworkServer) Delete(ctx context.Context, req *ttnpb.EndDeviceIdentif
type nsEndDeviceBatchRegistry struct {
ttnpb.UnimplementedNsEndDeviceBatchRegistryServer

NS *NetworkServer
devices DeviceRegistry
}

// Delete implements ttipb.NsEndDeviceBatchRegistryServer.
Expand All @@ -3581,7 +3581,7 @@ func (srv *nsEndDeviceBatchRegistry) Delete(
); err != nil {
return nil, err
}
deleted, err := srv.NS.devices.BatchDelete(ctx, req.ApplicationIds, req.DeviceIds)
deleted, err := srv.devices.BatchDelete(ctx, req.ApplicationIds, req.DeviceIds)
if err != nil {
logRegistryRPCError(ctx, err, "Failed to delete device from registry")
return nil, err
Expand Down
201 changes: 201 additions & 0 deletions pkg/networkserver/grpc_relay.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,201 @@
// Copyright © 2023 The Things Network Foundation, The Things Industries B.V.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package networkserver

import (
"context"
"fmt"
"strings"

"go.thethings.network/lorawan-stack/v3/pkg/auth/rights"
"go.thethings.network/lorawan-stack/v3/pkg/band"
"go.thethings.network/lorawan-stack/v3/pkg/errors"
"go.thethings.network/lorawan-stack/v3/pkg/frequencyplans"
. "go.thethings.network/lorawan-stack/v3/pkg/networkserver/internal"
"go.thethings.network/lorawan-stack/v3/pkg/ttnpb"
)

var errRelayAlreadyExists = errors.DefineAlreadyExists("relay", "relay already exists")

func validateRelaySecondChannel(secondCh *ttnpb.RelaySecondChannel, phy *band.Band, path ...string) error {
if secondCh == nil {
return nil
}
if _, ok := phy.DataRates[secondCh.DataRateIndex]; !ok {
return newInvalidFieldValueError(strings.Join(append(path, "data_rate_index"), "."))
}
inSubBand := false
for _, sb := range phy.SubBands {
if sb.MinFrequency >= secondCh.Frequency && secondCh.Frequency <= sb.MaxFrequency {
inSubBand = true
break
}
}
if !inSubBand {
return newInvalidFieldValueError(strings.Join(append(path, "frequency"), "."))
}
return nil
}

func validateRelayConfigurationServed(served *ttnpb.RelayConfiguration_Served, phy *band.Band, path ...string) error {
if served == nil {
return nil
}
if err := validateRelaySecondChannel(served.SecondChannel, phy, append(path, "second_channel")...); err != nil {
return err
}
return nil
}

func validateRelayConfigurationServing(serving *ttnpb.RelayConfiguration_Serving, phy *band.Band, path ...string) error {
if serving == nil {
return nil
}
if err := validateRelaySecondChannel(serving.SecondChannel, phy, append(path, "second_channel")...); err != nil {
return err
}
return nil
}

func validateRelayConfiguration(conf *ttnpb.RelayConfiguration, phy *band.Band, path ...string) error {
if conf == nil {
return nil
}
switch mode := conf.Mode.(type) {
case *ttnpb.RelayConfiguration_Served_:
return validateRelayConfigurationServed(mode.Served, phy, append(path, "mode", "served")...)
case *ttnpb.RelayConfiguration_Serving_:
return validateRelayConfigurationServing(mode.Serving, phy, append(path, "mode", "serving")...)
default:
panic(fmt.Sprintf("unknown mode %T", mode))
}
}

type nsRelayConfigurationService struct {
ttnpb.UnimplementedNsRelayConfigurationServiceServer

devices DeviceRegistry
frequencyPlans func(context.Context) (*frequencyplans.Store, error)
}

func relayParametersFromConfiguration(conf *ttnpb.RelayConfiguration) *ttnpb.RelayParameters {
if conf == nil {
return nil
}
switch mode := conf.Mode.(type) {
case *ttnpb.RelayConfiguration_Served_:
served := &ttnpb.ServedRelayParameters{
Backoff: mode.Served.Backoff,
SecondChannel: mode.Served.SecondChannel,
ServingDeviceId: mode.Served.ServingDeviceId,
}
switch mode := mode.Served.Mode.(type) {
case *ttnpb.RelayConfiguration_Served_Always:
served.Mode = &ttnpb.ServedRelayParameters_Always{
Always: mode.Always,
}
case *ttnpb.RelayConfiguration_Served_Dynamic:
served.Mode = &ttnpb.ServedRelayParameters_Dynamic{
Dynamic: mode.Dynamic,
}
case *ttnpb.RelayConfiguration_Served_EndDeviceControlled:
served.Mode = &ttnpb.ServedRelayParameters_EndDeviceControlled{
EndDeviceControlled: mode.EndDeviceControlled,
}
default:
panic(fmt.Sprintf("unknown mode %T", mode))
}
return &ttnpb.RelayParameters{
Mode: &ttnpb.RelayParameters_Served{
Served: served,
},
}
case *ttnpb.RelayConfiguration_Serving_:
return &ttnpb.RelayParameters{
Mode: &ttnpb.RelayParameters_Serving{
Serving: &ttnpb.ServingRelayParameters{
SecondChannel: mode.Serving.SecondChannel,
DefaultChannelIndex: mode.Serving.DefaultChannelIndex,
CadPeriodicity: mode.Serving.CadPeriodicity,
Limits: mode.Serving.Limits,
},
},
}
default:
panic(fmt.Sprintf("unknown mode %T", mode))
}
}

// CreateRelay implements ttnpb.NsRelayConfigurationServiceServer.
func (s *nsRelayConfigurationService) CreateRelay(
ctx context.Context, req *ttnpb.CreateRelayRequest,
) (*ttnpb.CreateRelayResponse, error) {
if err := rights.RequireApplication(
ctx, req.EndDeviceIds.ApplicationIds, ttnpb.Right_RIGHT_APPLICATION_DEVICES_WRITE,
); err != nil {
return nil, err
}
fps, err := s.frequencyPlans(ctx)
if err != nil {
return nil, err
}
if _, _, err := s.devices.SetByID(
ctx,
req.EndDeviceIds.ApplicationIds,
req.EndDeviceIds.DeviceId,
[]string{
"frequency_plan_id",
"lorawan_phy_version",
"mac_settings.relay",
"mac_state.desired_parameters",
"pending_mac_state.desired_parameters",
},
func(ctx context.Context, dev *ttnpb.EndDevice) (*ttnpb.EndDevice, []string, error) {
if dev == nil {
return nil, nil, errDeviceNotFound.New()
}
if dev.MacSettings.GetRelay() != nil {
return nil, nil, errRelayAlreadyExists.New()
}
phy, err := DeviceBand(dev, fps)
if err != nil {
return nil, nil, err
}
if err := validateRelayConfiguration(req.Configuration, phy, "configuration"); err != nil {
return nil, nil, err
}
parameters := relayParametersFromConfiguration(req.Configuration)
dev.MacSettings = &ttnpb.MACSettings{Relay: parameters}
paths := []string{"mac_settings.relay"}
for path, desiredParameters := range map[string]*ttnpb.MACParameters{
"mac_state.desired_parameters.relay": dev.MacState.GetDesiredParameters(),
"pending_mac_state.desired_parameters.relay": dev.PendingMacState.GetDesiredParameters(),
} {
if desiredParameters == nil {
continue
}
desiredParameters.Relay = parameters
paths = ttnpb.AddFields(paths, path)
}
return dev, paths, nil
},
); err != nil {
logRegistryRPCError(ctx, err, "Failed to create relay")
return nil, err
}
return &ttnpb.CreateRelayResponse{
Configuration: req.Configuration,
}, nil
}
14 changes: 9 additions & 5 deletions pkg/networkserver/networkserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -157,8 +157,10 @@ type NetworkServer struct {
*component.Component
ctx context.Context

devices DeviceRegistry
batchDevices *nsEndDeviceBatchRegistry
devices DeviceRegistry

batchDevices ttnpb.NsEndDeviceBatchRegistryServer
relayConfiguration ttnpb.NsRelayConfigurationServiceServer

netID netIDFunc
nsID nsIDFunc
Expand Down Expand Up @@ -284,6 +286,8 @@ func New(c *component.Component, conf *Config, opts ...Option) (*NetworkServer,
deduplicationWindow: makeWindowDurationFunc(conf.DeduplicationWindow),
collectionWindow: makeWindowDurationFunc(conf.DeduplicationWindow + conf.CooldownWindow),
devices: wrapEndDeviceRegistryWithReplacedFields(conf.Devices, replacedEndDeviceFields...),
batchDevices: &nsEndDeviceBatchRegistry{devices: conf.Devices},
relayConfiguration: &nsRelayConfigurationService{devices: conf.Devices, frequencyPlans: c.FrequencyPlansStore},
downlinkTasks: conf.DownlinkTaskQueue.Queue,
downlinkPriorities: downlinkPriorities,
defaultMACSettings: defaultMACSettings,
Expand All @@ -301,9 +305,6 @@ func New(c *component.Component, conf *Config, opts ...Option) (*NetworkServer,
QueueSize: int(conf.ApplicationUplinkQueue.FastBufferSize),
MaxWorkers: int(conf.ApplicationUplinkQueue.FastNumConsumers),
})
ns.batchDevices = &nsEndDeviceBatchRegistry{
NS: ns,
}
ctx = ns.Context()

if len(opts) == 0 {
Expand All @@ -326,6 +327,7 @@ func New(c *component.Component, conf *Config, opts ...Option) (*NetworkServer,
"/ttn.lorawan.v3.NsEndDeviceRegistry",
"/ttn.lorawan.v3.NsEndDeviceBatchRegistry",
"/ttn.lorawan.v3.Ns",
"/ttn.lorawan.v3.RelayConfigurationService",
} {
c.GRPC.RegisterUnaryHook(filter, hook.name, hook.middleware)
}
Expand Down Expand Up @@ -395,13 +397,15 @@ func (ns *NetworkServer) RegisterServices(s *grpc.Server) {
ttnpb.RegisterNsEndDeviceRegistryServer(s, ns)
ttnpb.RegisterNsEndDeviceBatchRegistryServer(s, ns.batchDevices)
ttnpb.RegisterNsServer(s, ns)
ttnpb.RegisterNsRelayConfigurationServiceServer(s, ns.relayConfiguration)
}

// RegisterHandlers registers gRPC handlers.
func (ns *NetworkServer) RegisterHandlers(s *runtime.ServeMux, conn *grpc.ClientConn) {
ttnpb.RegisterNsEndDeviceRegistryHandler(ns.Context(), s, conn)
ttnpb.RegisterNsEndDeviceBatchRegistryHandler(ns.Context(), s, conn) // nolint:errcheck
ttnpb.RegisterNsHandler(ns.Context(), s, conn)
ttnpb.RegisterNsRelayConfigurationServiceHandler(ns.Context(), s, conn) // nolint:errcheck
}

// Roles returns the roles that the Network Server fulfills.
Expand Down

0 comments on commit 2ecb67d

Please sign in to comment.