diff --git a/CHANGELOG.md b/CHANGELOG.md index e9c7993ebf..ae76c08121 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ For details about compatibility between different releases, see the **Commitment ### Added - New `ListBands` RPC on the `Configuration` service. +- Support for loading end device template from Device Repository when importing devices using a CSV file. ### Changed diff --git a/api/api.md b/api/api.md index 21c6d5e851..6f343fa966 100644 --- a/api/api.md +++ b/api/api.md @@ -3352,6 +3352,7 @@ Configuration options for static ADR. | ----- | ---- | ----- | ----------- | | `format_id` | [`string`](#string) | | ID of the format. | | `data` | [`bytes`](#bytes) | | Data to convert. | +| `end_device_version_ids` | [`EndDeviceVersionIdentifiers`](#ttn.lorawan.v3.EndDeviceVersionIdentifiers) | | End device profile identifiers. | #### Field Rules diff --git a/api/api.swagger.json b/api/api.swagger.json index 2fa9d0bebf..f779d44457 100644 --- a/api/api.swagger.json +++ b/api/api.swagger.json @@ -20344,6 +20344,10 @@ "type": "string", "format": "byte", "description": "Data to convert." + }, + "end_device_version_ids": { + "$ref": "#/definitions/v3EndDeviceVersionIdentifiers", + "description": "End device profile identifiers." } } }, diff --git a/api/end_device.proto b/api/end_device.proto index 8b4cae972d..7b36bb106c 100644 --- a/api/end_device.proto +++ b/api/end_device.proto @@ -900,4 +900,7 @@ message ConvertEndDeviceTemplateRequest { string format_id = 1 [(validate.rules).string = {pattern: "^[a-z0-9](?:[-]?[a-z0-9]){2,}$", max_len: 36}]; // Data to convert. bytes data = 2; + + // End device profile identifiers. + EndDeviceVersionIdentifiers end_device_version_ids = 3; } diff --git a/cmd/ttn-lw-cli/commands/end_device_templates.go b/cmd/ttn-lw-cli/commands/end_device_templates.go index a8d8c45d79..05b6b57e97 100644 --- a/cmd/ttn-lw-cli/commands/end_device_templates.go +++ b/cmd/ttn-lw-cli/commands/end_device_templates.go @@ -39,10 +39,22 @@ func templateFormatIDFlags() *pflag.FlagSet { } var ( - errNoTemplateFormatID = errors.DefineInvalidArgument("no_template_format_id", "no template format ID set") - errEndDeviceMappingNotFound = errors.DefineNotFound("mapped_end_device_not_found", "end device mapping not found") - errNoEndDeviceTemplateJoinEUI = errors.DefineInvalidArgument("no_end_device_template_join_eui", "no end device template JoinEUI set") - errNoEndDeviceTemplateStartDevEUI = errors.DefineInvalidArgument("no_end_device_template_start_dev_eui", "no end device template start DevEUI set") + errNoTemplateFormatID = errors.DefineInvalidArgument( + "no_template_format_id", + "no template format ID set", + ) + errEndDeviceMappingNotFound = errors.DefineNotFound( + "mapped_end_device_not_found", + "end device mapping not found", + ) + errNoEndDeviceTemplateJoinEUI = errors.DefineInvalidArgument( + "no_end_device_template_join_eui", + "no end device template JoinEUI set", + ) + errNoEndDeviceTemplateStartDevEUI = errors.DefineInvalidArgument( + "no_end_device_template_start_dev_eui", + "no end device template start DevEUI set", + ) ) func getTemplateFormatID(flagSet *pflag.FlagSet, args []string) string { @@ -297,6 +309,7 @@ This command takes end device templates from stdin.`, Short: "Convert data to an end device template (EXPERIMENTAL)", PersistentPreRunE: preRun(), RunE: func(cmd *cobra.Command, args []string) error { + _ = optionalAuth() formatID := getTemplateFormatID(cmd.Flags(), args) if formatID == "" { return errNoTemplateFormatID.New() @@ -310,10 +323,16 @@ This command takes end device templates from stdin.`, if err != nil { return err } - stream, err := ttnpb.NewEndDeviceTemplateConverterClient(dtc).Convert(ctx, &ttnpb.ConvertEndDeviceTemplateRequest{ - FormatId: formatID, - Data: data, - }) + + req := &ttnpb.ConvertEndDeviceTemplateRequest{ + FormatId: formatID, + Data: data, + EndDeviceVersionIds: &ttnpb.EndDeviceVersionIdentifiers{}, + } + if _, err = req.EndDeviceVersionIds.SetFromFlags(cmd.Flags(), "end-device-version-ids"); err != nil { + return err + } + stream, err := ttnpb.NewEndDeviceTemplateConverterClient(dtc).Convert(ctx, req) if err != nil { return err } @@ -430,7 +449,10 @@ command to assign EUIs to map to end device templates.`, var res ttnpb.EndDeviceTemplate res.EndDevice.SetFields(inputEntry.EndDevice, inputEntry.FieldMask.GetPaths()...) res.EndDevice.SetFields(mappedEntry.EndDevice, mappedEntry.FieldMask.GetPaths()...) - res.FieldMask = ttnpb.FieldMask(ttnpb.BottomLevelFields(append(inputEntry.FieldMask.GetPaths(), mappedEntry.FieldMask.GetPaths()...))...) + res.FieldMask = ttnpb.FieldMask( + ttnpb.BottomLevelFields( + append(inputEntry.FieldMask.GetPaths(), mappedEntry.FieldMask.GetPaths()...), + )...) if err := io.Write(os.Stdout, config.OutputFormat, &res); err != nil { return err @@ -455,6 +477,11 @@ func init() { endDeviceTemplatesCommand.AddCommand(endDeviceTemplatesListFormats) endDeviceTemplatesFromDataCommand.Flags().AddFlagSet(templateFormatIDFlags()) endDeviceTemplatesFromDataCommand.Flags().AddFlagSet(dataFlags("", "")) + ttnpb.AddSetFlagsForEndDeviceVersionIdentifiers( + endDeviceTemplatesFromDataCommand.Flags(), + "end-device-version-ids", + false, + ) endDeviceTemplatesCommand.AddCommand(endDeviceTemplatesFromDataCommand) endDeviceTemplatesMapCommand.Flags().AddFlagSet(dataFlags("input", "input file")) endDeviceTemplatesMapCommand.Flags().AddFlagSet(dataFlags("mapping", "mapping file")) diff --git a/pkg/devicerepository/mock/mock.go b/pkg/devicerepository/mock/mock.go new file mode 100644 index 0000000000..03a6bcbd82 --- /dev/null +++ b/pkg/devicerepository/mock/mock.go @@ -0,0 +1,114 @@ +// Copyright © 2022 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 mockdr contains the mock of a Device Repository Server. +package mockdr + +import ( + "context" + + "go.thethings.network/lorawan-stack/v3/pkg/ttnpb" + "google.golang.org/grpc" +) + +// New returns a mock of the Device Repository client. +func New() *MockDR { + return &MockDR{} +} + +// MockDR contains the response that it will provide when a client communicates with the server address. +type MockDR struct { + Err error + ListEndDeviceBrandsResp *ttnpb.ListEndDeviceBrandsResponse + EndDeviceBrand *ttnpb.EndDeviceBrand + ListEndDeviceModelsResp *ttnpb.ListEndDeviceModelsResponse + EndDeviceModel *ttnpb.EndDeviceModel + EndDeviceTemplate *ttnpb.EndDeviceTemplate + MessagePayloadDecoder *ttnpb.MessagePayloadDecoder + MessagePayloadEncoder *ttnpb.MessagePayloadEncoder +} + +/// Methods of DR + +// ListBrands mock method. +func (mdr *MockDR) ListBrands( + context.Context, + *ttnpb.ListEndDeviceBrandsRequest, + ...grpc.CallOption, +) (*ttnpb.ListEndDeviceBrandsResponse, error) { + return mdr.ListEndDeviceBrandsResp, mdr.Err +} + +// GetBrand mock method. +func (mdr *MockDR) GetBrand( + context.Context, + *ttnpb.GetEndDeviceBrandRequest, + ...grpc.CallOption, +) (*ttnpb.EndDeviceBrand, error) { + return mdr.EndDeviceBrand, mdr.Err +} + +// ListModels mock method. +func (mdr *MockDR) ListModels( + context.Context, + *ttnpb.ListEndDeviceModelsRequest, + ...grpc.CallOption, +) (*ttnpb.ListEndDeviceModelsResponse, error) { + return mdr.ListEndDeviceModelsResp, mdr.Err +} + +// GetModel mock method. +func (mdr *MockDR) GetModel( + context.Context, + *ttnpb.GetEndDeviceModelRequest, + ...grpc.CallOption, +) (*ttnpb.EndDeviceModel, error) { + return mdr.EndDeviceModel, mdr.Err +} + +// GetTemplate mock method. +func (mdr *MockDR) GetTemplate( + context.Context, + *ttnpb.GetTemplateRequest, + ...grpc.CallOption, +) (*ttnpb.EndDeviceTemplate, error) { + return mdr.EndDeviceTemplate, mdr.Err +} + +// GetUplinkDecoder mock method. +func (mdr *MockDR) GetUplinkDecoder( + context.Context, + *ttnpb.GetPayloadFormatterRequest, + ...grpc.CallOption, +) (*ttnpb.MessagePayloadDecoder, error) { + return mdr.MessagePayloadDecoder, mdr.Err +} + +// GetDownlinkDecoder mock method. +func (mdr *MockDR) GetDownlinkDecoder( + context.Context, + *ttnpb.GetPayloadFormatterRequest, + ...grpc.CallOption, +) (*ttnpb.MessagePayloadDecoder, error) { + return mdr.MessagePayloadDecoder, mdr.Err +} + +// GetDownlinkEncoder mock method. +func (mdr *MockDR) GetDownlinkEncoder( + context.Context, + *ttnpb.GetPayloadFormatterRequest, + ...grpc.CallOption, +) (*ttnpb.MessagePayloadEncoder, error) { + return mdr.MessagePayloadEncoder, mdr.Err +} diff --git a/pkg/devicetemplateconverter/devicetemplateconverter.go b/pkg/devicetemplateconverter/devicetemplateconverter.go index 788bcb0c17..8c199a1cc8 100644 --- a/pkg/devicetemplateconverter/devicetemplateconverter.go +++ b/pkg/devicetemplateconverter/devicetemplateconverter.go @@ -50,7 +50,6 @@ func New(c *component.Component, conf *Config) (*DeviceTemplateConverter, error) devicetemplates.TTSJSON, devicetemplates.TTSCSV, ) - converters := make(map[string]devicetemplates.Converter, len(conf.Enabled)) for _, id := range conf.Enabled { converter := devicetemplates.GetConverter(id) @@ -77,7 +76,7 @@ func (dtc *DeviceTemplateConverter) Context() context.Context { } // Roles returns the roles that the Device Template Converter fulfills. -func (dtc *DeviceTemplateConverter) Roles() []ttnpb.ClusterRole { +func (*DeviceTemplateConverter) Roles() []ttnpb.ClusterRole { return []ttnpb.ClusterRole{ttnpb.ClusterRole_DEVICE_TEMPLATE_CONVERTER} } @@ -88,5 +87,5 @@ func (dtc *DeviceTemplateConverter) RegisterServices(s *grpc.Server) { // RegisterHandlers registers gRPC handlers. func (dtc *DeviceTemplateConverter) RegisterHandlers(s *runtime.ServeMux, conn *grpc.ClientConn) { - ttnpb.RegisterEndDeviceTemplateConverterHandler(dtc.Context(), s, conn) + ttnpb.RegisterEndDeviceTemplateConverterHandler(dtc.Context(), s, conn) //nolint:errcheck } diff --git a/pkg/devicetemplateconverter/devicetemplateconverter_test.go b/pkg/devicetemplateconverter/devicetemplateconverter_test.go index 22873d1e9a..695760cedd 100644 --- a/pkg/devicetemplateconverter/devicetemplateconverter_test.go +++ b/pkg/devicetemplateconverter/devicetemplateconverter_test.go @@ -26,6 +26,7 @@ import ( ) func TestDeviceTemplateConverter(t *testing.T) { + t.Parallel() ctx := log.NewContext(test.Context(), test.GetLogger(t)) conf := &component.Config{} diff --git a/pkg/devicetemplateconverter/grpc.go b/pkg/devicetemplateconverter/grpc.go index de8bdcbcd0..9ae19caf79 100644 --- a/pkg/devicetemplateconverter/grpc.go +++ b/pkg/devicetemplateconverter/grpc.go @@ -19,6 +19,8 @@ import ( "context" pbtypes "github.com/gogo/protobuf/types" + "go.thethings.network/lorawan-stack/v3/pkg/devicetemplateconverter/profilefetcher" + "go.thethings.network/lorawan-stack/v3/pkg/devicetemplates" "go.thethings.network/lorawan-stack/v3/pkg/errorcontext" "go.thethings.network/lorawan-stack/v3/pkg/ttnpb" ) @@ -28,7 +30,10 @@ type endDeviceTemplateConverterServer struct { } // ListFormats implements ttnpb.DeviceTemplateServiceServer. -func (s *endDeviceTemplateConverterServer) ListFormats(ctx context.Context, _ *pbtypes.Empty) (*ttnpb.EndDeviceTemplateFormats, error) { +func (s *endDeviceTemplateConverterServer) ListFormats( + context.Context, + *pbtypes.Empty, +) (*ttnpb.EndDeviceTemplateFormats, error) { formats := make(map[string]*ttnpb.EndDeviceTemplateFormat, len(s.DTC.converters)) for id, converter := range s.DTC.converters { formats[id] = converter.Format() @@ -39,25 +44,31 @@ func (s *endDeviceTemplateConverterServer) ListFormats(ctx context.Context, _ *p } // Convert implements ttnpb.DeviceTemplateServiceServer. -func (s *endDeviceTemplateConverterServer) Convert(req *ttnpb.ConvertEndDeviceTemplateRequest, res ttnpb.EndDeviceTemplateConverter_ConvertServer) error { +func (s *endDeviceTemplateConverterServer) Convert( + req *ttnpb.ConvertEndDeviceTemplateRequest, + res ttnpb.EndDeviceTemplateConverter_ConvertServer, +) error { converter, ok := s.DTC.converters[req.FormatId] if !ok { return errNotFound.WithAttributes("id", req.FormatId) } ctx, cancel := errorcontext.New(res.Context()) + ctx = devicetemplates.NewContextWithProfileIDs(ctx, req.GetEndDeviceVersionIds()) + ctx = profilefetcher.NewContextWithFetcher(ctx, profilefetcher.NewTemplateFetcher(s.DTC.Component)) ch := make(chan *ttnpb.EndDeviceTemplate) go func() { if err := converter.Convert(ctx, bytes.NewReader(req.Data), ch); err != nil { cancel(err) } }() + for { select { case <-ctx.Done(): return ctx.Err() case tmpl, ok := <-ch: if !ok { - return nil + return ctx.Err() } if err := res.Send(tmpl); err != nil { return err diff --git a/pkg/devicetemplateconverter/grpc_test.go b/pkg/devicetemplateconverter/grpc_test.go index ee6af7ae23..4445689083 100644 --- a/pkg/devicetemplateconverter/grpc_test.go +++ b/pkg/devicetemplateconverter/grpc_test.go @@ -17,6 +17,7 @@ package devicetemplateconverter_test import ( "bufio" "context" + "errors" "fmt" "io" "testing" @@ -33,6 +34,7 @@ import ( ) func TestConvertEndDeviceTemplate(t *testing.T) { + t.Parallel() a := assertions.New(t) ctx := log.NewContext(test.Context(), test.GetLogger(t)) @@ -94,7 +96,7 @@ func TestConvertEndDeviceTemplate(t *testing.T) { tmpls := make([]*ttnpb.EndDeviceTemplate, 0, 2) for { tmpl, err := stream.Recv() - if err == io.EOF { + if errors.Is(err, io.EOF) { break } tmpls = append(tmpls, tmpl) diff --git a/pkg/devicetemplateconverter/profilefetcher/profile_fetcher.go b/pkg/devicetemplateconverter/profilefetcher/profile_fetcher.go new file mode 100644 index 0000000000..ed99fff93f --- /dev/null +++ b/pkg/devicetemplateconverter/profilefetcher/profile_fetcher.go @@ -0,0 +1,90 @@ +// Copyright © 2022 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 profilefetcher contains definitions of end-device's profile fetchers based on the VersionIDs or VendorIDs. +package profilefetcher + +import ( + "context" + + "go.thethings.network/lorawan-stack/v3/pkg/cluster" + "go.thethings.network/lorawan-stack/v3/pkg/log" + "go.thethings.network/lorawan-stack/v3/pkg/rpcmetadata" + "go.thethings.network/lorawan-stack/v3/pkg/ttnpb" + "google.golang.org/grpc" +) + +// EndDeviceProfileFetcher specifies the necessary methods to ascertain if the end device contains the necessary +// information to make the profile template request and for performing the request itself. +type EndDeviceProfileFetcher interface { + ShouldFetchProfile(*ttnpb.EndDevice) bool + FetchProfile(context.Context, *ttnpb.EndDevice) (*ttnpb.EndDeviceTemplate, error) +} + +// TemplateFetcher implements the steps required to obtain a end-device template from the Device Repository. +type TemplateFetcher interface { + GetTemplate(ctx context.Context, in *ttnpb.GetTemplateRequest) (*ttnpb.EndDeviceTemplate, error) +} + +// Component abstracts the underlying *component.Component. +type Component interface { + GetPeerConn(ctx context.Context, role ttnpb.ClusterRole, ids cluster.EntityIdentifiers) (*grpc.ClientConn, error) + AllowInsecureForCredentials() bool +} + +type templateFetcher struct { + c Component +} + +// NewTemplateFetcher returns an end-device template fetcher with predefined call options. +func NewTemplateFetcher(c Component) TemplateFetcher { + return &templateFetcher{ + c: c, + } +} + +// GetTemplate makes a request to the Device Repository server with its predefined call options. +func (tf *templateFetcher) GetTemplate( + ctx context.Context, + in *ttnpb.GetTemplateRequest, +) (*ttnpb.EndDeviceTemplate, error) { + conn, err := tf.c.GetPeerConn(ctx, ttnpb.ClusterRole_DEVICE_REPOSITORY, nil) + if err != nil { + log.FromContext(ctx).WithError(err).Warn("Failed to get Device Repository peer") + return nil, err + } + + opt, err := rpcmetadata.WithForwardedAuth(ctx, tf.c.AllowInsecureForCredentials()) + if err != nil { + return nil, err + } + return ttnpb.NewDeviceRepositoryClient(conn).GetTemplate(ctx, in, opt) +} + +type templateFetcherContextKeyType struct{} + +var templateFetcherContextKey templateFetcherContextKeyType + +// NewContextWithFetcher returns a context with the TemplateFetcher. +func NewContextWithFetcher(ctx context.Context, tf TemplateFetcher) context.Context { + return context.WithValue(ctx, templateFetcherContextKey, tf) +} + +// fetcherFromContext returns a TemplateFetcher from the context provided. Will return false if not present. +func fetcherFromContext(ctx context.Context) (TemplateFetcher, bool) { + if fetcher, ok := ctx.Value(templateFetcherContextKey).(TemplateFetcher); ok { + return fetcher, true + } + return nil, false +} diff --git a/pkg/devicetemplateconverter/profilefetcher/profile_fetcher_test.go b/pkg/devicetemplateconverter/profilefetcher/profile_fetcher_test.go new file mode 100644 index 0000000000..3331d273ba --- /dev/null +++ b/pkg/devicetemplateconverter/profilefetcher/profile_fetcher_test.go @@ -0,0 +1,42 @@ +// Copyright © 2022 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 profilefetcher_test + +import ( + "context" + + mockdr "go.thethings.network/lorawan-stack/v3/pkg/devicerepository/mock" + . "go.thethings.network/lorawan-stack/v3/pkg/devicetemplateconverter/profilefetcher" + "go.thethings.network/lorawan-stack/v3/pkg/ttnpb" +) + +type mockTemplateFetcher struct { + mockDR *mockdr.MockDR +} + +// GetTemplate makes a request to the Device Repository server with its predefined call options. +func (tf *mockTemplateFetcher) GetTemplate( + ctx context.Context, + in *ttnpb.GetTemplateRequest, +) (*ttnpb.EndDeviceTemplate, error) { + return tf.mockDR.GetTemplate(ctx, in) +} + +// MockTemplateFetcher returns an end-device template fetcher that directly points to the DR mock. +func MockTemplateFetcher(mockDR *mockdr.MockDR) TemplateFetcher { + return &mockTemplateFetcher{ + mockDR: mockDR, + } +} diff --git a/pkg/devicetemplateconverter/profilefetcher/vendor_profile_fetcher.go b/pkg/devicetemplateconverter/profilefetcher/vendor_profile_fetcher.go new file mode 100644 index 0000000000..fd175f7d29 --- /dev/null +++ b/pkg/devicetemplateconverter/profilefetcher/vendor_profile_fetcher.go @@ -0,0 +1,51 @@ +// Copyright © 2022 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 profilefetcher + +import ( + "context" + + "go.thethings.network/lorawan-stack/v3/pkg/ttnpb" +) + +// vendorIDProfileFetcher handles the validation and fetching of end-device profile in the Device Repository. +type vendorIDProfileFetcher struct{} + +// NewFetcherByVendorIDs returns a end-device's profile fetcher that builds its request with vendorIDs. +func NewFetcherByVendorIDs() EndDeviceProfileFetcher { + return &vendorIDProfileFetcher{} +} + +// ShouldFetchProfile dictactes if the end-device has the necessary fields to fetch its profile. +func (*vendorIDProfileFetcher) ShouldFetchProfile(device *ttnpb.EndDevice) bool { + return device.GetVersionIds().GetVendorId() != 0 && device.GetVersionIds().GetVendorProfileId() != 0 +} + +// FetchProfile provides the end-device profile. +func (*vendorIDProfileFetcher) FetchProfile( + ctx context.Context, + device *ttnpb.EndDevice, +) (*ttnpb.EndDeviceTemplate, error) { + profileIdentifiers := &ttnpb.GetTemplateRequest_EndDeviceProfileIdentifiers{ + VendorId: device.GetVersionIds().GetVendorId(), + VendorProfileId: device.GetVersionIds().GetVendorProfileId(), + } + + fetcher, ok := fetcherFromContext(ctx) + if !ok { + return nil, nil + } + return fetcher.GetTemplate(ctx, &ttnpb.GetTemplateRequest{EndDeviceProfileIds: profileIdentifiers}) +} diff --git a/pkg/devicetemplateconverter/profilefetcher/vendor_profile_fetcher_test.go b/pkg/devicetemplateconverter/profilefetcher/vendor_profile_fetcher_test.go new file mode 100644 index 0000000000..a72cde8ae3 --- /dev/null +++ b/pkg/devicetemplateconverter/profilefetcher/vendor_profile_fetcher_test.go @@ -0,0 +1,153 @@ +// Copyright © 2022 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 profilefetcher_test + +import ( + "context" + "testing" + + "github.com/smartystreets/assertions" + mockdr "go.thethings.network/lorawan-stack/v3/pkg/devicerepository/mock" + . "go.thethings.network/lorawan-stack/v3/pkg/devicetemplateconverter/profilefetcher" + "go.thethings.network/lorawan-stack/v3/pkg/errors" + "go.thethings.network/lorawan-stack/v3/pkg/log" + "go.thethings.network/lorawan-stack/v3/pkg/ttnpb" + "go.thethings.network/lorawan-stack/v3/pkg/util/test" + "go.thethings.network/lorawan-stack/v3/pkg/util/test/assertions/should" +) + +func Test_VendorIDProfileFetcher_ShouldFetchProfile(t *testing.T) { + t.Parallel() + tests := []struct { + name string + device *ttnpb.EndDevice + want bool + }{ + { + name: "Invalid", + device: &ttnpb.EndDevice{}, + want: false, + }, + { + name: "Valid", + device: &ttnpb.EndDevice{ + VersionIds: &ttnpb.EndDeviceVersionIdentifiers{ + VendorId: 10, + VendorProfileId: 10, + }, + }, + want: true, + }, + } + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + a := assertions.New(t) + fetcher := NewFetcherByVendorIDs() + a.So(fetcher.ShouldFetchProfile(tt.device), should.Equal, tt.want) + }) + } +} + +func Test_VendorIDProfileFetcher_FetchProfile(t *testing.T) { + t.Parallel() + ctx := log.NewContext(test.Context(), test.GetLogger(t)) + ctx, cancelCtx := context.WithCancel(ctx) + t.Cleanup(func() { + cancelCtx() + }) + + errNoVendorID := errors.DefineInvalidArgument("no-vendor-id", "no vendor id") + + tests := []struct { + name string + endDevice *ttnpb.EndDevice + populateMock func(*mockdr.MockDR) + validateResp func(*assertions.Assertion, *ttnpb.EndDeviceTemplate) + validateErr func(error) bool + }{ + { + name: "fail/no vendor id", + endDevice: &ttnpb.EndDevice{ + VersionIds: &ttnpb.EndDeviceVersionIdentifiers{}, + }, + populateMock: func(md *mockdr.MockDR) { + md.Err = errNoVendorID.New() + }, + validateErr: func(err error) bool { + return errors.IsInvalidArgument(err) + }, + }, + { + name: "valid", + endDevice: &ttnpb.EndDevice{ + VersionIds: &ttnpb.EndDeviceVersionIdentifiers{ + VendorId: 1, + VendorProfileId: 1, + }, + }, + populateMock: func(md *mockdr.MockDR) { + md.EndDeviceTemplate = &ttnpb.EndDeviceTemplate{ + EndDevice: &ttnpb.EndDevice{ + VersionIds: &ttnpb.EndDeviceVersionIdentifiers{ + VendorId: 1, + VendorProfileId: 1, + }, + }, + FieldMask: ttnpb.FieldMask( + "version_ids.vendor_id", + "version_ids.vendor_profile_id", + ), + } + }, + validateResp: func(a *assertions.Assertion, tmpl *ttnpb.EndDeviceTemplate) { + // validates if there is a mock path and all the mocked Identifiers. + a.So(ttnpb.RequireFields(tmpl.GetFieldMask().GetPaths(), + "version_ids.vendor_id", + "version_ids.vendor_profile_id", + ), should.BeNil) + a.So( + tmpl.GetEndDevice().GetVersionIds().GetVendorId() == 1 && + tmpl.GetEndDevice().GetVersionIds().GetVendorProfileId() == 1, + should.BeTrue, + ) + }, + }, + } + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + a := assertions.New(t) + drMock := mockdr.New() + tt.populateMock(drMock) + ctx = NewContextWithFetcher(ctx, MockTemplateFetcher(drMock)) + + pf := NewFetcherByVendorIDs() + tmpl, err := pf.FetchProfile(ctx, tt.endDevice) + if tt.validateErr == nil { + a.So(err, should.BeNil) + } else { + a.So(tt.validateErr(err), should.BeTrue) + } + if tt.validateResp == nil { + a.So(tmpl, should.BeNil) + } else { + tt.validateResp(a, tmpl) + } + }) + } +} diff --git a/pkg/devicetemplateconverter/profilefetcher/version_profile_fetcher.go b/pkg/devicetemplateconverter/profilefetcher/version_profile_fetcher.go new file mode 100644 index 0000000000..62756a49a6 --- /dev/null +++ b/pkg/devicetemplateconverter/profilefetcher/version_profile_fetcher.go @@ -0,0 +1,56 @@ +// Copyright © 2022 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 profilefetcher + +import ( + "context" + + "go.thethings.network/lorawan-stack/v3/pkg/ttnpb" +) + +// versionIDProfileFetcher handles the validation and fetching of end device profile in the Device Repository. +type versionIDProfileFetcher struct{} + +// NewFetcherByVersionIDs returns a end-device's profile fetcher that builds its request with vendorIDs. +func NewFetcherByVersionIDs() EndDeviceProfileFetcher { + return &versionIDProfileFetcher{} +} + +// ShouldFetchProfile dictactes if the end device has the necessary fields to fetch its profile. +func (*versionIDProfileFetcher) ShouldFetchProfile(device *ttnpb.EndDevice) bool { + return device.GetVersionIds().GetBrandId() != "" && + device.GetVersionIds().GetModelId() != "" && + device.GetVersionIds().GetFirmwareVersion() != "" && + device.GetVersionIds().GetBandId() != "" +} + +// FetchProfile provides the end device profile. +func (pf *versionIDProfileFetcher) FetchProfile( + ctx context.Context, + device *ttnpb.EndDevice, +) (*ttnpb.EndDeviceTemplate, error) { + versionIDs := &ttnpb.EndDeviceVersionIdentifiers{ + BrandId: device.GetVersionIds().GetBrandId(), + ModelId: device.GetVersionIds().GetModelId(), + HardwareVersion: device.GetVersionIds().GetHardwareVersion(), + FirmwareVersion: device.GetVersionIds().GetFirmwareVersion(), + BandId: device.GetVersionIds().GetBandId(), + } + fetcher, ok := fetcherFromContext(ctx) + if !ok { + return nil, nil + } + return fetcher.GetTemplate(ctx, &ttnpb.GetTemplateRequest{VersionIds: versionIDs}) +} diff --git a/pkg/devicetemplateconverter/profilefetcher/version_profile_fetcher_test.go b/pkg/devicetemplateconverter/profilefetcher/version_profile_fetcher_test.go new file mode 100644 index 0000000000..9dc26850da --- /dev/null +++ b/pkg/devicetemplateconverter/profilefetcher/version_profile_fetcher_test.go @@ -0,0 +1,167 @@ +// Copyright © 2022 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 profilefetcher_test + +import ( + "context" + "testing" + + "github.com/smartystreets/assertions" + mockdr "go.thethings.network/lorawan-stack/v3/pkg/devicerepository/mock" + . "go.thethings.network/lorawan-stack/v3/pkg/devicetemplateconverter/profilefetcher" + "go.thethings.network/lorawan-stack/v3/pkg/errors" + "go.thethings.network/lorawan-stack/v3/pkg/log" + "go.thethings.network/lorawan-stack/v3/pkg/ttnpb" + "go.thethings.network/lorawan-stack/v3/pkg/util/test" + "go.thethings.network/lorawan-stack/v3/pkg/util/test/assertions/should" +) + +func Test_VersionIDProfileFetcher_ShouldFetchProfile(t *testing.T) { + t.Parallel() + tests := []struct { + name string + device *ttnpb.EndDevice + want bool + }{ + { + name: "Invalid", + device: &ttnpb.EndDevice{}, + want: false, + }, + { + name: "Valid", + device: &ttnpb.EndDevice{ + VersionIds: &ttnpb.EndDeviceVersionIdentifiers{ + BandId: "EU_863_870", + BrandId: "the-things-industries", + FirmwareVersion: "1.0", + HardwareVersion: "1.1", + ModelId: "generic-node-sensor-edition", + }, + }, + want: true, + }, + } + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + a := assertions.New(t) + fetcher := NewFetcherByVersionIDs() + a.So(fetcher.ShouldFetchProfile(tt.device), should.Equal, tt.want) + }) + } +} + +func Test_VersionIDProfileFetcher_FetchProfile(t *testing.T) { + t.Parallel() + ctx := log.NewContext(test.Context(), test.GetLogger(t)) + ctx, cancelCtx := context.WithCancel(ctx) + t.Cleanup(func() { + cancelCtx() + }) + + errNoVendorID := errors.DefineInvalidArgument("no-vendor-id", "no vendor id") + + tests := []struct { + name string + endDevice *ttnpb.EndDevice + populateMock func(*mockdr.MockDR) + validateResp func(*assertions.Assertion, *ttnpb.EndDeviceTemplate) + validateErr func(error) bool + }{ + { + name: "fail/no version id", + endDevice: &ttnpb.EndDevice{ + VersionIds: &ttnpb.EndDeviceVersionIdentifiers{}, + }, + populateMock: func(md *mockdr.MockDR) { + md.Err = errNoVendorID.New() + }, + validateErr: func(err error) bool { + return errors.IsInvalidArgument(err) + }, + }, + { + name: "valid", + endDevice: &ttnpb.EndDevice{ + VersionIds: &ttnpb.EndDeviceVersionIdentifiers{ + VendorId: 1, + VendorProfileId: 1, + }, + }, + populateMock: func(md *mockdr.MockDR) { + md.EndDeviceTemplate = &ttnpb.EndDeviceTemplate{ + EndDevice: &ttnpb.EndDevice{ + VersionIds: &ttnpb.EndDeviceVersionIdentifiers{ + BrandId: "the-things-industries", + ModelId: "generic-node-sensor-edition", + HardwareVersion: "1.1", + FirmwareVersion: "1.0", + BandId: "EU_863_870", + }, + }, + FieldMask: ttnpb.FieldMask( + "version_ids.brand_id", + "version_ids.model_id", + "version_ids.hardware_version", + "version_ids.firmware_version", + "version_ids.band_id", + ), + } + }, + validateResp: func(a *assertions.Assertion, tmpl *ttnpb.EndDeviceTemplate) { + // validates if there is a mock path and all the mocked Identifiers. + a.So(ttnpb.RequireFields(tmpl.GetFieldMask().GetPaths(), + "version_ids.brand_id", + "version_ids.model_id", + "version_ids.hardware_version", + "version_ids.firmware_version", + "version_ids.band_id", + ), should.BeNil) + a.So( + tmpl.GetEndDevice().GetVersionIds().GetBrandId() == "the-things-industries" && + tmpl.GetEndDevice().GetVersionIds().GetModelId() == "generic-node-sensor-edition" && + tmpl.GetEndDevice().GetVersionIds().GetHardwareVersion() == "1.1" && + tmpl.GetEndDevice().GetVersionIds().GetFirmwareVersion() == "1.0" && + tmpl.GetEndDevice().GetVersionIds().GetBandId() == "EU_863_870", + should.BeTrue, + ) + }, + }, + } + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + a := assertions.New(t) + drMock := mockdr.New() + tt.populateMock(drMock) + pf := NewFetcherByVersionIDs() + ctx = NewContextWithFetcher(ctx, MockTemplateFetcher(drMock)) + tmpl, err := pf.FetchProfile(ctx, tt.endDevice) + if tt.validateErr == nil { + a.So(err, should.BeNil) + } else { + a.So(tt.validateErr(err), should.BeTrue) + } + if tt.validateResp == nil { + a.So(tmpl, should.BeNil) + } else { + tt.validateResp(a, tmpl) + } + }) + } +} diff --git a/pkg/devicetemplates/testdata/with_version_id.csv b/pkg/devicetemplates/testdata/with_version_id.csv new file mode 100644 index 0000000000..e923627908 --- /dev/null +++ b/pkg/devicetemplates/testdata/with_version_id.csv @@ -0,0 +1,2 @@ +id;dev_eui;join_eui;name;frequency_plan_id;lorawan_version;brand_id;model_id;hardware_version;firmware_version;band_id;vendor_id +test-vendor;1111111111111111;1111111111111111;Device with vendorID;EU_863_870_TTN;MAC_V1_0_4;the-things-industries;generic-node-sensor-edition;1.1;1.0;EU_863_870;1AC diff --git a/pkg/devicetemplates/testdata/without_version_id.csv b/pkg/devicetemplates/testdata/without_version_id.csv new file mode 100644 index 0000000000..d4bc4bfd6f --- /dev/null +++ b/pkg/devicetemplates/testdata/without_version_id.csv @@ -0,0 +1,2 @@ +id;dev_eui;join_eui;name;frequency_plan_id;lorawan_version +test-vendor;1111111111111111;1111111111111111;Device with vendorID;EU_863_870_TTN;MAC_V1_0_4 diff --git a/pkg/devicetemplates/ttscsv.go b/pkg/devicetemplates/ttscsv.go index ef03a5cf98..c2b4a695db 100644 --- a/pkg/devicetemplates/ttscsv.go +++ b/pkg/devicetemplates/ttscsv.go @@ -27,6 +27,7 @@ import ( "time" "github.com/oklog/ulid/v2" + "go.thethings.network/lorawan-stack/v3/pkg/devicetemplateconverter/profilefetcher" "go.thethings.network/lorawan-stack/v3/pkg/errors" "go.thethings.network/lorawan-stack/v3/pkg/ttnpb" "go.thethings.network/lorawan-stack/v3/pkg/types" @@ -38,7 +39,9 @@ const TTSCSV = "the-things-stack-csv" var errGenerateSessionKeyID = errors.DefineUnavailable("generate_session_key_id", "generate session key ID") -type ttsCSV struct{} +type ttsCSV struct { + profileFetchers []profilefetcher.EndDeviceProfileFetcher +} // Format implements the devicetemplates.Converter interface. func (*ttsCSV) Format() *ttnpb.EndDeviceTemplateFormat { @@ -74,7 +77,7 @@ func convertCSVErr(err error) error { type csvFieldSetterFunc func(dst *ttnpb.EndDevice, field string) (paths []string, err error) -var csvFieldSetters = map[string]csvFieldSetterFunc{ +var csvFieldSetters = map[string]csvFieldSetterFunc{ //nolint:gocyclo "id": func(dst *ttnpb.EndDevice, val string) ([]string, error) { dst.Ids.DeviceId = val return []string{"ids.device_id"}, nil @@ -166,6 +169,28 @@ var csvFieldSetters = map[string]csvFieldSetterFunc{ dst.VersionIds.BandId = val return []string{"version_ids.band_id"}, nil }, + "vendor_id": func(dst *ttnpb.EndDevice, val string) ([]string, error) { + if dst.VersionIds == nil { + dst.VersionIds = &ttnpb.EndDeviceVersionIdentifiers{} + } + s, err := strconv.ParseUint(val, 16, 32) + if err != nil { + return nil, err + } + dst.VersionIds.VendorId = uint32(s) + return []string{"version_ids.vendor_id"}, nil + }, + "vendor_profile_id": func(dst *ttnpb.EndDevice, val string) ([]string, error) { + if dst.VersionIds == nil { + dst.VersionIds = &ttnpb.EndDeviceVersionIdentifiers{} + } + s, err := strconv.ParseUint(val, 16, 32) + if err != nil { + return nil, err + } + dst.VersionIds.VendorProfileId = uint32(s) + return []string{"version_ids.vendor_profile_id"}, nil + }, "app_key": func(dst *ttnpb.EndDevice, val string) ([]string, error) { var key types.AES128Key if err := key.UnmarshalText([]byte(val)); err != nil { @@ -331,7 +356,7 @@ func determineComma(head []byte) (rune, bool) { } // Convert implements the devicetemplates.Converter interface. -func (*ttsCSV) Convert(ctx context.Context, r io.Reader, ch chan<- *ttnpb.EndDeviceTemplate) error { +func (t *ttsCSV) Convert(ctx context.Context, r io.Reader, ch chan<- *ttnpb.EndDeviceTemplate) error { //nolint:gocyclo defer close(ch) r, err := charset.NewReader(r, "text/csv") @@ -383,10 +408,15 @@ func (*ttsCSV) Convert(ctx context.Context, r io.Reader, ch chan<- *ttnpb.EndDev return convertCSVErr(err) } + // Fallback value for end device profile fetching. + versionIDs := ProfileIDsFromContext(ctx) + + paths := make([]string, 0, len(record)) dev := &ttnpb.EndDevice{ - Ids: &ttnpb.EndDeviceIdentifiers{}, + Ids: &ttnpb.EndDeviceIdentifiers{}, + VersionIds: versionIDs, } - paths := make([]string, 0, len(record)) + for i, val := range record { fieldSetter, ok := fieldSetters[i] if !ok { @@ -410,6 +440,28 @@ func (*ttsCSV) Convert(ctx context.Context, r io.Reader, ch chan<- *ttnpb.EndDev EndDevice: dev, FieldMask: ttnpb.FieldMask(paths...), } + + for _, fetcher := range t.profileFetchers { + if !fetcher.ShouldFetchProfile(dev) { + continue + } + profileTmpl, err := fetcher.FetchProfile(ctx, dev) + if err != nil { + return err + } + if profileTmpl == nil { + continue + } + + // Overwrites profile values with the ones provided by the user. + if err := profileTmpl.EndDevice.SetFields(tmpl.EndDevice, tmpl.FieldMask.Paths...); err != nil { + return err + } + profileTmpl.FieldMask.Paths = ttnpb.AddFields(profileTmpl.FieldMask.Paths, tmpl.FieldMask.Paths...) + + tmpl = profileTmpl + } + select { case <-ctx.Done(): return ctx.Err() @@ -419,6 +471,29 @@ func (*ttsCSV) Convert(ctx context.Context, r io.Reader, ch chan<- *ttnpb.EndDev } } +type profileIdentifiersKeyType struct{} + +var profileIDsKey = &profileIdentifiersKeyType{} + +// NewContextWithProfileIDs returns a context with an EndDeviceVersionIdentifiers injected into it. +func NewContextWithProfileIDs(ctx context.Context, ids *ttnpb.EndDeviceVersionIdentifiers) context.Context { + return context.WithValue(ctx, profileIDsKey, ids) +} + +// ProfileIDsFromContext fetches an EndDeviceVersionIdentifiers from within the given context, nil if does not exist. +func ProfileIDsFromContext(ctx context.Context) *ttnpb.EndDeviceVersionIdentifiers { + ids, ok := ctx.Value(profileIDsKey).(*ttnpb.EndDeviceVersionIdentifiers) + if !ok { + return nil + } + return ids +} + func init() { - RegisterConverter(TTSCSV, &ttsCSV{}) + RegisterConverter(TTSCSV, &ttsCSV{ + profileFetchers: []profilefetcher.EndDeviceProfileFetcher{ + profilefetcher.NewFetcherByVendorIDs(), + profilefetcher.NewFetcherByVersionIDs(), + }, + }) } diff --git a/pkg/devicetemplates/ttscsv_test.go b/pkg/devicetemplates/ttscsv_test.go index c3bb728bf7..f5785ae192 100644 --- a/pkg/devicetemplates/ttscsv_test.go +++ b/pkg/devicetemplates/ttscsv_test.go @@ -16,6 +16,7 @@ package devicetemplates_test import ( "bytes" + "context" _ "embed" "io" "sync" @@ -23,7 +24,10 @@ import ( "time" "github.com/smartystreets/assertions" + mockdr "go.thethings.network/lorawan-stack/v3/pkg/devicerepository/mock" + "go.thethings.network/lorawan-stack/v3/pkg/devicetemplateconverter/profilefetcher" . "go.thethings.network/lorawan-stack/v3/pkg/devicetemplates" + "go.thethings.network/lorawan-stack/v3/pkg/errors" "go.thethings.network/lorawan-stack/v3/pkg/ttnpb" "go.thethings.network/lorawan-stack/v3/pkg/types" "go.thethings.network/lorawan-stack/v3/pkg/util/test" @@ -43,27 +47,30 @@ var ( csvAppEUI string //go:embed testdata/comma_separated.csv csvCommaSeparated string + //go:embed testdata/with_version_id.csv + csvVendorID string + //go:embed testdata/without_version_id.csv + csvNoVendorID string ) +var mockErr = errors.DefineInternal("mock_internal_error", "An error is expected") + func TestTTSCSVConverter(t *testing.T) { t.Parallel() - tts := GetConverter("the-things-stack-csv") - a := assertions.New(t) - if !a.So(tts, should.NotBeNil) { - t.FailNow() - } - for _, tc := range []struct { name string reader io.Reader + converter Converter + fillContext func(context.Context) context.Context validateError func(a *assertions.Assertion, err error) validateResult func(a *assertions.Assertion, templates []*ttnpb.EndDeviceTemplate, count int) nExpect int }{ { - name: "AllColumns", - reader: bytes.NewBufferString(csvAllColumns), + name: "AllColumns", + reader: bytes.NewBufferString(csvAllColumns), + converter: GetConverter("the-things-stack-csv"), validateError: func(a *assertions.Assertion, err error) { a.So(err, should.BeNil) }, @@ -102,8 +109,9 @@ func TestTTSCSVConverter(t *testing.T) { nExpect: 3, }, { - name: "ExtraColumns", - reader: bytes.NewBufferString(csvExtraColumns), + name: "ExtraColumns", + reader: bytes.NewBufferString(csvExtraColumns), + converter: GetConverter("the-things-stack-csv"), validateError: func(a *assertions.Assertion, err error) { a.So(err, should.BeNil) }, @@ -119,24 +127,27 @@ func TestTTSCSVConverter(t *testing.T) { nExpect: 1, }, { - name: "EmptyString", - reader: bytes.NewBufferString(""), + name: "EmptyString", + reader: bytes.NewBufferString(""), + converter: GetConverter("the-things-stack-csv"), validateError: func(a *assertions.Assertion, err error) { a.So(err, should.NotBeNil) }, nExpect: 0, }, { - name: "InvalidDevEUI", - reader: bytes.NewBufferString(csvInvalidDevEUI), + name: "InvalidDevEUI", + reader: bytes.NewBufferString(csvInvalidDevEUI), + converter: GetConverter("the-things-stack-csv"), validateError: func(a *assertions.Assertion, err error) { a.So(err, should.NotBeNil) }, nExpect: 0, }, { - name: "GenerateDeviceID", - reader: bytes.NewBufferString(csvGenerateDeviceID), + name: "GenerateDeviceID", + reader: bytes.NewBufferString(csvGenerateDeviceID), + converter: GetConverter("the-things-stack-csv"), validateError: func(a *assertions.Assertion, err error) { a.So(err, should.BeNil) }, @@ -150,8 +161,9 @@ func TestTTSCSVConverter(t *testing.T) { nExpect: 1, }, { - name: "AppEUI", - reader: bytes.NewBufferString(csvAppEUI), + name: "AppEUI", + reader: bytes.NewBufferString(csvAppEUI), + converter: GetConverter("the-things-stack-csv"), validateError: func(a *assertions.Assertion, err error) { a.So(err, should.BeNil) }, @@ -173,8 +185,9 @@ func TestTTSCSVConverter(t *testing.T) { nExpect: 1, }, { - name: "CommaSeparated", - reader: bytes.NewBufferString(csvCommaSeparated), + name: "CommaSeparated", + reader: bytes.NewBufferString(csvCommaSeparated), + converter: GetConverter("the-things-stack-csv"), validateError: func(a *assertions.Assertion, err error) { a.So(err, should.BeNil) }, @@ -193,12 +206,151 @@ func TestTTSCSVConverter(t *testing.T) { }, nExpect: 3, }, + { + name: "VendorID/No valid fields", + reader: bytes.NewBufferString(csvNoVendorID), + converter: GetConverter("the-things-stack-csv"), + fillContext: func(ctx context.Context) context.Context { + return profilefetcher.NewContextWithFetcher(ctx, &mockTemplateFetcher{}) + }, + validateError: func(a *assertions.Assertion, err error) { + a.So(err, should.BeNil) + }, + validateResult: func(a *assertions.Assertion, templates []*ttnpb.EndDeviceTemplate, count int) { + if !a.So(len(templates), should.Equal, count) { + t.FailNow() + } + dev := templates[0] + a.So( + dev.EndDevice.Ids.JoinEui, + should.Resemble, + types.EUI64{0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11}.Bytes(), + ) + a.So(ttnpb.RequireFields(dev.FieldMask.Paths, + "frequency_plan_id", + "ids.dev_eui", + "ids.device_id", + "ids.join_eui", + ), should.BeNil) + }, + nExpect: 1, + }, + { + name: "VendorID/Error fetching", + reader: bytes.NewBufferString(csvVendorID), + converter: GetConverter("the-things-stack-csv"), + fillContext: func(ctx context.Context) context.Context { + return profilefetcher.NewContextWithFetcher(ctx, &mockTemplateFetcher{ + Err: mockErr.New(), + }) + }, + validateError: func(a *assertions.Assertion, err error) { + a.So(errors.IsInternal(err), should.BeTrue) + }, + nExpect: 1, + }, + { + name: "VendorID/Valid", + reader: bytes.NewBufferString(csvVendorID), + converter: GetConverter("the-things-stack-csv"), + fillContext: func(ctx context.Context) context.Context { + return profilefetcher.NewContextWithFetcher(ctx, &mockTemplateFetcher{ + MockDR: func() *mockdr.MockDR { + dr := mockdr.New() + dr.EndDeviceTemplate = mockProfileTemplate + return dr + }(), + }) + }, + validateError: func(a *assertions.Assertion, err error) { + a.So(err, should.BeNil) + }, + validateResult: func(a *assertions.Assertion, templates []*ttnpb.EndDeviceTemplate, count int) { + if !a.So(len(templates), should.Equal, count) { + t.FailNow() + } + dev := templates[0] + a.So( + dev.EndDevice.Ids.JoinEui, + should.Resemble, + types.EUI64{0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11}.Bytes(), + ) + a.So(ttnpb.RequireFields(dev.FieldMask.Paths, + "frequency_plan_id", + "ids.dev_eui", + "ids.device_id", + "ids.join_eui", + "lorawan_version", + "lorawan_phy_version", + "supports_class_c", + ), should.BeNil) + + // validating if end device profile information was applied + a.So(dev.EndDevice.LorawanPhyVersion, should.Equal, ttnpb.PHYVersion_PHY_V1_0_2_REV_A) + a.So(dev.EndDevice.SupportsClassC, should.BeTrue) + // LoRaWAN Version was fetched from profile as MAC_V1_0_2 but provided by csv as MAC_V1_0_4. + a.So(dev.EndDevice.LorawanVersion, should.Equal, ttnpb.MACVersion_MAC_V1_0_4) + }, + nExpect: 1, + }, + { + name: "VendorID/Valid fallback value", + reader: bytes.NewBufferString(csvNoVendorID), + converter: GetConverter("the-things-stack-csv"), + fillContext: func(ctx context.Context) context.Context { + ctx = profilefetcher.NewContextWithFetcher(ctx, &mockTemplateFetcher{ + MockDR: func() *mockdr.MockDR { + dr := mockdr.New() + dr.EndDeviceTemplate = mockProfileTemplate + return dr + }(), + }) + return NewContextWithProfileIDs(ctx, &ttnpb.EndDeviceVersionIdentifiers{ + BandId: "EU_863_870", + BrandId: "the-things-industries", + FirmwareVersion: "1.0", + HardwareVersion: "1.1", + ModelId: "generic-node-sensor-edition", + }) + }, + validateError: func(a *assertions.Assertion, err error) { + a.So(err, should.BeNil) + }, + validateResult: func(a *assertions.Assertion, templates []*ttnpb.EndDeviceTemplate, count int) { + if !a.So(len(templates), should.Equal, count) { + t.FailNow() + } + dev := templates[0] + a.So( + dev.EndDevice.Ids.JoinEui, + should.Resemble, + types.EUI64{0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11}.Bytes(), + ) + a.So(ttnpb.RequireFields(dev.FieldMask.Paths, + "ids.dev_eui", + "ids.join_eui", + "lorawan_version", + "lorawan_phy_version", + "supports_class_c", + ), should.BeNil) + + // validating if end device profile information was applied + a.So(dev.EndDevice.LorawanPhyVersion, should.Equal, ttnpb.PHYVersion_PHY_V1_0_2_REV_A) + a.So(dev.EndDevice.SupportsClassC, should.BeTrue) + // LoRaWAN Version was fetched from profile as MAC_V1_0_2 but provided by csv as MAC_V1_0_4. + a.So(dev.EndDevice.LorawanVersion, should.Equal, ttnpb.MACVersion_MAC_V1_0_4) + }, + nExpect: 1, + }, } { tc := tc t.Run(tc.name, func(t *testing.T) { t.Parallel() - ctx := test.Context() + a, ctx := test.New(t) + if tc.fillContext != nil { + ctx = tc.fillContext(ctx) + } ch := make(chan *ttnpb.EndDeviceTemplate) wg := sync.WaitGroup{} @@ -206,7 +358,7 @@ func TestTTSCSVConverter(t *testing.T) { var err error templates := []*ttnpb.EndDeviceTemplate{} go func() { - err = tts.Convert(ctx, tc.reader, ch) + err = tc.converter.Convert(ctx, tc.reader, ch) wg.Done() }() go func() { @@ -231,7 +383,6 @@ func TestTTSCSVConverter(t *testing.T) { t.FailNow() } - a := assertions.New(t) tc.validateError(a, err) if tc.validateResult != nil { tc.validateResult(a, templates, tc.nExpect) @@ -239,3 +390,32 @@ func TestTTSCSVConverter(t *testing.T) { }) } } + +// mockProfileTemplate is used in the ValidID tests. +var mockProfileTemplate = &ttnpb.EndDeviceTemplate{ + EndDevice: &ttnpb.EndDevice{ + LorawanVersion: ttnpb.MACVersion_MAC_V1_0_2, + LorawanPhyVersion: ttnpb.PHYVersion_PHY_V1_0_2_REV_A, + SupportsClassC: true, + }, + FieldMask: ttnpb.FieldMask("lorawan_version", "lorawan_phy_version", "supports_class_c"), +} + +type mockTemplateFetcher struct { + MockDR *mockdr.MockDR + Err error +} + +// GetTemplate makes a request to the Device Repository server with its predefined call options. +func (tf *mockTemplateFetcher) GetTemplate( + ctx context.Context, + in *ttnpb.GetTemplateRequest, +) (*ttnpb.EndDeviceTemplate, error) { + if tf.Err != nil { + return nil, tf.Err + } + if tf.MockDR == nil { + return nil, nil + } + return tf.MockDR.GetTemplate(ctx, in) +} diff --git a/pkg/ttnpb/end_device.pb.go b/pkg/ttnpb/end_device.pb.go index 8fecb13f24..7b8aec4a87 100644 --- a/pkg/ttnpb/end_device.pb.go +++ b/pkg/ttnpb/end_device.pb.go @@ -3475,10 +3475,12 @@ type ConvertEndDeviceTemplateRequest struct { // ID of the format. FormatId string `protobuf:"bytes,1,opt,name=format_id,json=formatId,proto3" json:"format_id,omitempty"` // Data to convert. - Data []byte `protobuf:"bytes,2,opt,name=data,proto3" json:"data,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` + Data []byte `protobuf:"bytes,2,opt,name=data,proto3" json:"data,omitempty"` + // End device profile identifiers. + EndDeviceVersionIds *EndDeviceVersionIdentifiers `protobuf:"bytes,3,opt,name=end_device_version_ids,json=endDeviceVersionIds,proto3" json:"end_device_version_ids,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } func (m *ConvertEndDeviceTemplateRequest) Reset() { *m = ConvertEndDeviceTemplateRequest{} } @@ -3519,6 +3521,13 @@ func (m *ConvertEndDeviceTemplateRequest) GetData() []byte { return nil } +func (m *ConvertEndDeviceTemplateRequest) GetEndDeviceVersionIds() *EndDeviceVersionIdentifiers { + if m != nil { + return m.EndDeviceVersionIds + } + return nil +} + func init() { proto.RegisterEnum("ttn.lorawan.v3.PowerState", PowerState_name, PowerState_value) golang_proto.RegisterEnum("ttn.lorawan.v3.PowerState", PowerState_name, PowerState_value) @@ -3620,379 +3629,380 @@ func init() { } var fileDescriptor_a656ee0551c94a80 = []byte{ - // 5972 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xec, 0x5c, 0x4f, 0x6c, 0x1b, 0xd9, - 0x79, 0xd7, 0x50, 0x94, 0x48, 0x7e, 0xa4, 0xc8, 0xd1, 0x93, 0x64, 0x8d, 0x28, 0xff, 0x91, 0x69, + // 5994 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xec, 0x7c, 0x5f, 0x6c, 0x1b, 0xd9, + 0x75, 0xb7, 0x86, 0xa2, 0x44, 0xf2, 0x90, 0x22, 0x47, 0x57, 0xff, 0x46, 0x94, 0xff, 0xc8, 0x5c, 0xad, 0x97, 0x76, 0x2c, 0x4a, 0xa6, 0xd6, 0x9b, 0xac, 0x37, 0x89, 0x97, 0x94, 0xe4, 0x58, 0xb2, - 0xe5, 0x95, 0xc7, 0xb2, 0xbd, 0xc9, 0xae, 0x77, 0x32, 0x9a, 0x79, 0xa2, 0x66, 0x45, 0xce, 0x30, - 0x33, 0x43, 0x99, 0xca, 0x66, 0x81, 0x20, 0x6d, 0x51, 0x34, 0x40, 0x7a, 0x30, 0x8a, 0x06, 0x4d, - 0x2f, 0x41, 0x0f, 0x45, 0x61, 0x14, 0x45, 0x0f, 0x3d, 0xf4, 0x10, 0xa0, 0xbd, 0x04, 0x48, 0x11, - 0xb4, 0x4d, 0x7a, 0xec, 0xa9, 0x40, 0x0f, 0x2d, 0x02, 0xb4, 0x87, 0xbd, 0x55, 0x87, 0xb6, 0x78, - 0x6f, 0xde, 0x9b, 0x19, 0x92, 0x43, 0x89, 0x8a, 0x77, 0x91, 0x5d, 0x20, 0x97, 0xdd, 0xd1, 0x7b, - 0xdf, 0xf7, 0x9b, 0x37, 0xef, 0x7d, 0xdf, 0xf7, 0xbe, 0x7f, 0x34, 0x14, 0xea, 0x96, 0xad, 0x3e, - 0x53, 0xcd, 0x05, 0xc7, 0x55, 0xb5, 0xfd, 0x45, 0xb5, 0x69, 0x2c, 0x62, 0x53, 0x57, 0x74, 0x7c, - 0x60, 0x68, 0xb8, 0xd4, 0xb4, 0x2d, 0xd7, 0x42, 0x59, 0xd7, 0x35, 0x4b, 0x8c, 0xae, 0x74, 0xb0, - 0x9c, 0xaf, 0xd4, 0x0c, 0x77, 0xaf, 0xb5, 0x53, 0xd2, 0xac, 0xc6, 0x22, 0x36, 0x0f, 0xac, 0xc3, - 0xa6, 0x6d, 0xb5, 0x0f, 0x17, 0x29, 0xb1, 0xb6, 0x50, 0xc3, 0xe6, 0xc2, 0x81, 0x5a, 0x37, 0x74, - 0xd5, 0xc5, 0x8b, 0x3d, 0x0f, 0x1e, 0x64, 0x7e, 0x21, 0x04, 0x51, 0xb3, 0x6a, 0x96, 0xc7, 0xbc, - 0xd3, 0xda, 0xa5, 0x7f, 0xd1, 0x3f, 0xe8, 0x13, 0x23, 0x5f, 0x0d, 0x91, 0x6f, 0xef, 0xe1, 0xed, - 0x3d, 0xc3, 0xac, 0x39, 0xeb, 0xa6, 0xde, 0x72, 0x5c, 0xdb, 0xc0, 0x4e, 0xf8, 0xd5, 0x35, 0x6b, - 0x61, 0xb7, 0xae, 0xd6, 0x9c, 0x45, 0xd5, 0x34, 0x2d, 0x57, 0x75, 0x0d, 0xcb, 0x74, 0x18, 0xca, - 0xca, 0xa9, 0x50, 0x3e, 0x70, 0x2c, 0x33, 0x02, 0xe4, 0x7c, 0xcd, 0xb2, 0x6a, 0x75, 0x1c, 0x2c, - 0x58, 0x6f, 0xd9, 0x94, 0x80, 0xcd, 0xcf, 0x75, 0xcf, 0xef, 0x1a, 0xb8, 0xae, 0x2b, 0x0d, 0xd5, - 0xd9, 0x67, 0x14, 0x67, 0xbb, 0x29, 0x1c, 0xd7, 0x6e, 0x69, 0x2e, 0x9b, 0xbd, 0xd0, 0x3d, 0xeb, - 0x1a, 0x0d, 0xec, 0xb8, 0x6a, 0xa3, 0xd9, 0x6f, 0x01, 0xcf, 0x6c, 0xb5, 0xd9, 0xc4, 0x36, 0x5f, - 0xe0, 0xb9, 0xa8, 0x13, 0x6d, 0x35, 0xf8, 0xf4, 0xa5, 0xde, 0x69, 0x43, 0xc7, 0xa6, 0x6b, 0xec, - 0x1a, 0x01, 0xc6, 0xd9, 0x5e, 0xa2, 0x7d, 0x7c, 0xc8, 0x67, 0x2f, 0xf4, 0xce, 0x72, 0xe9, 0x60, - 0x7b, 0xd0, 0x4b, 0xd0, 0xc0, 0x8e, 0xa3, 0xd6, 0xb0, 0x73, 0x1c, 0x85, 0xab, 0xea, 0xaa, 0xab, - 0xf6, 0x7f, 0x49, 0xd3, 0xd0, 0xdc, 0x96, 0xcd, 0x45, 0xa8, 0x14, 0x3a, 0x2e, 0xab, 0x89, 0x4d, - 0xb5, 0x69, 0x1c, 0x94, 0x17, 0xad, 0x26, 0x3d, 0xac, 0xde, 0x83, 0x2b, 0xfc, 0x60, 0x14, 0x12, - 0x0f, 0xb1, 0xe3, 0x18, 0x96, 0x89, 0xfe, 0x2b, 0x06, 0x49, 0x1d, 0x1f, 0x28, 0xaa, 0xae, 0xdb, - 0x52, 0x6c, 0x4e, 0x28, 0x66, 0xaa, 0xbf, 0x8c, 0x3d, 0xaf, 0xcc, 0x6c, 0x40, 0xa1, 0xfc, 0xfa, - 0xd2, 0x52, 0xa5, 0xba, 0xb2, 0x5a, 0xf8, 0x51, 0x4c, 0x48, 0xfc, 0x59, 0x6c, 0x94, 0xc8, 0x86, - 0x59, 0x3b, 0xaa, 0x8e, 0x7e, 0x3b, 0xbe, 0x17, 0x6f, 0x0a, 0xbf, 0x7a, 0x31, 0xf3, 0x3d, 0x01, - 0x6e, 0xd5, 0xac, 0x92, 0xbb, 0x87, 0x5d, 0x2a, 0x41, 0x25, 0x13, 0xbb, 0xcf, 0x2c, 0x7b, 0x7f, - 0xb1, 0x73, 0xc5, 0x07, 0xcb, 0x8b, 0xcd, 0xfd, 0xda, 0xa2, 0x7b, 0xd8, 0xc4, 0x4e, 0x69, 0x53, - 0xb5, 0x9d, 0x3d, 0xb5, 0x7e, 0x67, 0xed, 0x9d, 0xea, 0xa1, 0x8b, 0x1d, 0x74, 0x6a, 0x80, 0x47, - 0x66, 0xc3, 0x83, 0x78, 0x8d, 0x02, 0x7c, 0xfc, 0x62, 0xe6, 0xc7, 0x42, 0xfe, 0xfe, 0x60, 0x28, - 0x5a, 0x43, 0x5f, 0x74, 0x5d, 0x73, 0xa1, 0xfe, 0x6c, 0x41, 0xab, 0x1b, 0x8b, 0x5a, 0xcb, 0x71, - 0xad, 0x06, 0x55, 0x92, 0xd2, 0x7d, 0xfc, 0xcc, 0x03, 0xbc, 0x5d, 0x57, 0x6b, 0x85, 0x97, 0xc7, - 0xfb, 0x1a, 0x76, 0xd7, 0xda, 0xaa, 0xe6, 0x52, 0x4c, 0x39, 0xa1, 0xe3, 0x83, 0x8a, 0xae, 0xdb, - 0xe8, 0x0d, 0x88, 0x13, 0xf9, 0x91, 0x86, 0xe7, 0x84, 0x62, 0xba, 0x3c, 0x5b, 0xea, 0x34, 0x28, - 0x25, 0x76, 0x2c, 0x77, 0xf1, 0xa1, 0x53, 0x4d, 0x1e, 0x55, 0x47, 0xbe, 0x2f, 0xc4, 0x44, 0x41, - 0xa6, 0x2c, 0xe8, 0x22, 0x8c, 0xd5, 0x55, 0xc7, 0x55, 0x76, 0x15, 0xcd, 0x74, 0x95, 0x56, 0x53, - 0x8a, 0xcf, 0x09, 0xc5, 0x31, 0x19, 0xc8, 0xe0, 0xed, 0x15, 0xd3, 0x7d, 0xd4, 0x44, 0x45, 0x18, - 0xa7, 0x24, 0x26, 0x23, 0xd2, 0xad, 0x67, 0xa6, 0x34, 0x42, 0xc9, 0x28, 0xef, 0x7d, 0x42, 0xb7, - 0x6a, 0x3d, 0x33, 0x7d, 0x4a, 0x35, 0x4c, 0x39, 0x1a, 0x50, 0x56, 0x7c, 0xca, 0x12, 0x4c, 0x52, - 0x4a, 0xcd, 0x32, 0x77, 0xc3, 0xc4, 0x09, 0x4a, 0x2c, 0x92, 0xb9, 0x15, 0xcb, 0xdc, 0xf5, 0xe9, - 0xdf, 0x00, 0x70, 0x5c, 0xd5, 0x76, 0xb1, 0xae, 0xa8, 0xae, 0x94, 0xa4, 0xdf, 0x99, 0x2f, 0x79, - 0xaa, 0x5a, 0xe2, 0xaa, 0x5a, 0xda, 0xe6, 0xba, 0x2c, 0xa7, 0x18, 0x75, 0xc5, 0x45, 0x18, 0xce, - 0x7e, 0xab, 0x85, 0x5b, 0x84, 0xb3, 0xd9, 0xac, 0x1b, 0x1a, 0x95, 0x5a, 0xfa, 0xb6, 0xba, 0x61, - 0xee, 0x3b, 0x52, 0x6a, 0x6e, 0xb8, 0x98, 0x2e, 0x5f, 0xea, 0xde, 0xb4, 0x4a, 0x40, 0xbc, 0xca, - 0x68, 0xe5, 0xbc, 0x07, 0x14, 0x31, 0xe5, 0xdc, 0x4c, 0x7e, 0xfc, 0x62, 0x26, 0x9e, 0x14, 0x44, - 0x61, 0x83, 0xfc, 0x37, 0x56, 0x58, 0x86, 0x54, 0xd5, 0xb2, 0xea, 0x8f, 0xd5, 0x7a, 0x0b, 0xa3, - 0x49, 0x18, 0x39, 0x20, 0x0f, 0x92, 0x30, 0x27, 0x14, 0x93, 0xb2, 0xf7, 0xc7, 0x4d, 0xf1, 0x57, - 0x2f, 0x66, 0x62, 0x92, 0xf0, 0xf1, 0x8b, 0x99, 0x51, 0xc2, 0x28, 0x09, 0x85, 0x9f, 0xe6, 0x60, - 0x6c, 0xb3, 0xb2, 0xb2, 0xa5, 0xda, 0x6a, 0x03, 0xbb, 0xd8, 0x76, 0xd0, 0x0c, 0x24, 0x1b, 0x6a, - 0x5b, 0xc1, 0x86, 0xdd, 0xa4, 0xcc, 0x31, 0x39, 0xd1, 0x50, 0xdb, 0x6b, 0x86, 0xdd, 0x44, 0x8f, - 0x61, 0x42, 0xd5, 0x6d, 0x85, 0x28, 0xb5, 0x62, 0xab, 0x2e, 0x56, 0x0c, 0x53, 0xc7, 0x6d, 0x7a, - 0x80, 0xd9, 0xf2, 0xb9, 0xee, 0xef, 0x59, 0x55, 0x5d, 0x55, 0x56, 0x5d, 0xbc, 0x4e, 0x88, 0xa8, - 0x18, 0x7c, 0x8f, 0x8a, 0x81, 0xa8, 0xea, 0x76, 0xc7, 0x1c, 0x7a, 0x0d, 0x10, 0xc1, 0x75, 0xdb, - 0x4a, 0xd3, 0x7a, 0x86, 0x6d, 0x06, 0x4b, 0x0f, 0xbc, 0x9a, 0x38, 0xaa, 0xc6, 0xaf, 0xc6, 0xa4, - 0x9c, 0x9c, 0x53, 0x75, 0x7b, 0xbb, 0xbd, 0x45, 0x08, 0x3c, 0xae, 0x2b, 0x90, 0x21, 0x5c, 0xe6, - 0x8e, 0xe2, 0xda, 0xaa, 0xe9, 0x78, 0xc7, 0x1e, 0xd0, 0x83, 0xaa, 0xdb, 0xf7, 0x77, 0xb6, 0xc9, - 0x14, 0xba, 0x0c, 0x63, 0x84, 0x54, 0xd5, 0xf6, 0x95, 0xba, 0xd1, 0x30, 0x5c, 0xef, 0xd4, 0xab, - 0x31, 0x49, 0x90, 0xd3, 0xaa, 0x6e, 0x57, 0xb4, 0xfd, 0x7b, 0x64, 0x38, 0x4c, 0xa7, 0xe3, 0xba, - 0x7a, 0x48, 0xcf, 0xbd, 0x83, 0x6e, 0x95, 0x0c, 0xa3, 0xaf, 0x42, 0xca, 0x6e, 0x5f, 0x67, 0x34, - 0x29, 0xfa, 0xf9, 0xd3, 0xdd, 0x9f, 0x2f, 0xb7, 0x29, 0x6d, 0xe8, 0xc3, 0x93, 0x76, 0xfb, 0xba, - 0xc7, 0xff, 0x75, 0x98, 0xa4, 0xfc, 0xfe, 0x46, 0x5a, 0xbb, 0xbb, 0x0e, 0x76, 0x25, 0xa0, 0x50, - 0xe7, 0xfb, 0xed, 0xe4, 0xdb, 0x94, 0x2a, 0x84, 0x38, 0x4e, 0x10, 0x3b, 0x26, 0xc9, 0x19, 0xd9, - 0xed, 0x72, 0xcf, 0x19, 0xa5, 0x4f, 0x79, 0x46, 0x76, 0xbb, 0xdc, 0x79, 0x46, 0x25, 0x18, 0x23, - 0xb8, 0xbb, 0x36, 0xfe, 0x56, 0x0b, 0x9b, 0xda, 0xa1, 0x94, 0x99, 0x13, 0x8a, 0xf1, 0x6a, 0xea, - 0xa8, 0x3a, 0x5a, 0x8e, 0x17, 0x7f, 0xfc, 0x83, 0x51, 0x39, 0x63, 0xb7, 0xcb, 0xb7, 0xf9, 0x34, - 0x7a, 0x08, 0x59, 0x22, 0x46, 0x7a, 0xcb, 0x3d, 0x54, 0xb4, 0x43, 0xad, 0x8e, 0xa5, 0x31, 0xba, - 0x84, 0x5e, 0xb1, 0xaf, 0xd5, 0x6c, 0x5c, 0x53, 0x5d, 0xac, 0xaf, 0xb6, 0xdc, 0xc3, 0x15, 0x42, - 0x1a, 0x5a, 0x48, 0xa6, 0xa1, 0xb6, 0xfd, 0x71, 0xa4, 0xc3, 0xb4, 0x8d, 0x3f, 0xb0, 0x0c, 0x53, - 0x21, 0x97, 0xa8, 0xd2, 0xc4, 0xb6, 0x61, 0xe9, 0x86, 0x66, 0xb8, 0x87, 0x52, 0x96, 0xa2, 0x17, - 0x7a, 0x4e, 0x81, 0x92, 0x13, 0x35, 0x5d, 0x6b, 0x37, 0x2d, 0x13, 0x9b, 0xe1, 0xed, 0x9b, 0xb2, - 0xfd, 0xd9, 0xad, 0x00, 0x0a, 0xd5, 0x40, 0x62, 0x6f, 0xd1, 0xac, 0x96, 0xe9, 0x76, 0xbc, 0x26, - 0x17, 0xfd, 0x11, 0xde, 0x6b, 0x56, 0x08, 0x79, 0xc4, 0x7b, 0xce, 0xd8, 0xc1, 0x74, 0xf8, 0x45, - 0x6f, 0xc2, 0x44, 0xd3, 0x30, 0x6b, 0x8a, 0x53, 0xb7, 0xdc, 0xd0, 0xce, 0x8a, 0x74, 0x67, 0xd3, - 0x47, 0xd5, 0x64, 0x79, 0x54, 0x1a, 0xa2, 0x7b, 0x3b, 0x4e, 0xe8, 0x1e, 0xd6, 0x2d, 0x37, 0xd8, - 0xe0, 0x77, 0x61, 0x26, 0x60, 0xee, 0x3e, 0xee, 0xf1, 0x41, 0x8e, 0x9b, 0x88, 0xf5, 0x14, 0x07, - 0xee, 0x3c, 0xed, 0xd7, 0x41, 0xdc, 0xc1, 0xaa, 0x66, 0x99, 0xa1, 0x65, 0xa1, 0xde, 0x65, 0xe5, - 0x3c, 0xa2, 0x60, 0x51, 0x77, 0x21, 0xa9, 0xed, 0xa9, 0xa6, 0x89, 0xeb, 0x8e, 0x34, 0x41, 0xcd, - 0xdc, 0x2b, 0xdd, 0x6b, 0xe8, 0xb0, 0x36, 0xa5, 0x15, 0x8f, 0x9a, 0x6e, 0xd6, 0x73, 0x21, 0x96, - 0x14, 0x64, 0x1f, 0x00, 0xad, 0xc1, 0x78, 0xab, 0x49, 0x6c, 0x9d, 0xa2, 0x3f, 0xc3, 0xf5, 0x3a, - 0x3d, 0x73, 0x69, 0x92, 0x5a, 0xe2, 0x99, 0x6e, 0x54, 0xdf, 0xf2, 0xc9, 0x39, 0x8f, 0x67, 0x95, - 0xb0, 0x90, 0x93, 0x45, 0xeb, 0x30, 0xc1, 0x6d, 0x6f, 0x18, 0x68, 0xea, 0x24, 0xa0, 0x71, 0xce, - 0x15, 0x40, 0x3d, 0x85, 0x33, 0x1d, 0x76, 0x44, 0xc1, 0xec, 0xb0, 0xa5, 0x33, 0x14, 0xad, 0xd8, - 0x23, 0xdc, 0xab, 0x32, 0x37, 0x2e, 0x5c, 0x2e, 0x3c, 0xf0, 0x89, 0x90, 0xd9, 0xe1, 0x33, 0x61, - 0x78, 0x6a, 0x5a, 0x02, 0xf8, 0xe9, 0xe3, 0xe0, 0xa9, 0x4d, 0x89, 0x84, 0xef, 0x98, 0x41, 0x35, - 0xb8, 0xd0, 0x57, 0x62, 0x14, 0xef, 0xb6, 0x90, 0xe8, 0x7b, 0x0a, 0xc7, 0xca, 0x8d, 0xf7, 0x86, - 0x7c, 0xa4, 0xe0, 0xd0, 0xb9, 0xfc, 0xbf, 0xc6, 0x20, 0xc1, 0x0e, 0x96, 0x48, 0x12, 0x3b, 0xc4, - 0x40, 0x92, 0x84, 0x08, 0x49, 0xf2, 0x88, 0x02, 0x49, 0xfa, 0x12, 0x20, 0xff, 0xd4, 0x02, 0xce, - 0x58, 0xb7, 0xd1, 0xf1, 0x0f, 0x29, 0xe0, 0x7c, 0x0c, 0x13, 0x0d, 0xc3, 0xec, 0x51, 0x89, 0xe1, - 0x53, 0x5a, 0xc0, 0x86, 0x61, 0x76, 0xea, 0x04, 0xc1, 0x25, 0x16, 0xed, 0x25, 0x6f, 0x3f, 0x62, - 0xd0, 0x3a, 0x70, 0x2f, 0xc1, 0x18, 0x36, 0xd5, 0x9d, 0x3a, 0x56, 0xbc, 0x3d, 0xa0, 0x17, 0x5f, - 0x52, 0xce, 0x78, 0x83, 0x8f, 0xe8, 0x58, 0x70, 0xd9, 0x77, 0x5c, 0xfb, 0x31, 0x71, 0x78, 0x23, - 0x9e, 0x1c, 0x16, 0xe3, 0x85, 0xa3, 0x11, 0x10, 0xd7, 0x4c, 0x7d, 0x95, 0x86, 0x79, 0x8f, 0xb1, - 0x4d, 0xbd, 0xe2, 0xaf, 0xc1, 0xb0, 0xa1, 0x3b, 0x74, 0xbb, 0xd3, 0xe5, 0x2f, 0x74, 0xaf, 0xb0, - 0x9b, 0x7c, 0x3d, 0x88, 0x1a, 0x42, 0x4e, 0x1b, 0x41, 0x40, 0x9b, 0x90, 0x63, 0x8c, 0xca, 0x81, - 0x47, 0x4c, 0x4f, 0x22, 0x5b, 0xce, 0x47, 0x68, 0x37, 0x83, 0x0b, 0x7d, 0x73, 0x96, 0x11, 0xf0, - 0x75, 0x6d, 0xc3, 0x04, 0x87, 0x6b, 0xee, 0x1d, 0xfa, 0x90, 0xc3, 0xd1, 0x90, 0x5b, 0x77, 0xbe, - 0xde, 0x0b, 0x39, 0xce, 0x08, 0xb6, 0xf6, 0x0e, 0x39, 0xea, 0x32, 0x8c, 0xfb, 0x82, 0xa2, 0x34, - 0xeb, 0xaa, 0xa9, 0x18, 0x3a, 0x3d, 0x9d, 0x14, 0x75, 0x0a, 0xec, 0x98, 0xf4, 0x96, 0x9c, 0xf3, - 0x29, 0xb6, 0xea, 0xaa, 0xb9, 0xae, 0xa3, 0x39, 0x18, 0x6d, 0xee, 0x59, 0xae, 0xe5, 0x48, 0x23, - 0x73, 0xc3, 0xc5, 0x14, 0xb7, 0x43, 0x22, 0xc8, 0x6c, 0x1c, 0x15, 0x41, 0x74, 0x5a, 0xcd, 0xa6, - 0x65, 0xbb, 0x8e, 0xa2, 0xd5, 0x55, 0xc7, 0x51, 0x76, 0xa8, 0xab, 0x91, 0x94, 0xb3, 0x7c, 0x7c, - 0x85, 0x0c, 0x57, 0x23, 0x28, 0x35, 0xea, 0x68, 0x74, 0x53, 0xae, 0xa0, 0x4d, 0x98, 0xd4, 0xf1, - 0xae, 0xda, 0xaa, 0xbb, 0x4a, 0x43, 0xd5, 0x14, 0x07, 0xbb, 0x2e, 0xf1, 0xd0, 0x99, 0x9b, 0x39, - 0x1b, 0xb1, 0xa9, 0x0f, 0x19, 0x89, 0x8c, 0x18, 0xe3, 0xa6, 0xaa, 0xf1, 0x31, 0x22, 0x41, 0x44, - 0xe2, 0x03, 0x35, 0x21, 0x2e, 0x49, 0x5c, 0xce, 0x34, 0x8c, 0x90, 0x69, 0x26, 0x44, 0x6a, 0x3b, - 0x44, 0x04, 0x8c, 0x48, 0x6d, 0x77, 0x10, 0xf9, 0x9f, 0x40, 0xae, 0x2c, 0xea, 0x37, 0x24, 0xe5, - 0x0c, 0x1f, 0xdc, 0xb0, 0x0c, 0x13, 0x5d, 0x03, 0x64, 0x63, 0x07, 0x33, 0x12, 0xc5, 0xb4, 0x4c, - 0x0d, 0x3b, 0xd4, 0x1f, 0x48, 0xca, 0xa2, 0x37, 0x43, 0xe8, 0xee, 0xd3, 0x71, 0xa4, 0x02, 0x5f, - 0xb2, 0xb2, 0x6b, 0xd9, 0x0d, 0xd5, 0x25, 0x76, 0x9f, 0x3a, 0x03, 0x11, 0x06, 0x6d, 0xd3, 0x8b, - 0x2a, 0xb7, 0xd4, 0xc3, 0xba, 0xa5, 0xea, 0xb7, 0x7d, 0xfa, 0x90, 0x40, 0x8e, 0x33, 0xb4, 0x60, - 0xb2, 0x70, 0x94, 0x80, 0x74, 0x65, 0x55, 0xf6, 0xf7, 0xe3, 0x2d, 0x18, 0x75, 0x48, 0xac, 0xa8, - 0x31, 0xd1, 0xbf, 0x1c, 0x61, 0x37, 0x39, 0x71, 0xe9, 0x21, 0xa5, 0xdc, 0xb4, 0x74, 0x7c, 0x67, - 0x48, 0x66, 0x7c, 0x68, 0x05, 0x12, 0xfa, 0xa1, 0xa9, 0x36, 0x0c, 0x8d, 0x0a, 0x7a, 0xba, 0xfc, - 0xea, 0x71, 0x10, 0xab, 0x1e, 0x29, 0xc3, 0xe0, 0x9c, 0xe8, 0x36, 0x24, 0x75, 0xc3, 0x21, 0x4a, - 0xac, 0xb3, 0x40, 0xa9, 0x78, 0x2c, 0x0a, 0xa3, 0x65, 0x30, 0x3e, 0x6f, 0xfe, 0x27, 0x02, 0x40, - 0xb0, 0x4a, 0xf4, 0x36, 0xe4, 0xba, 0x6d, 0x90, 0x70, 0x3a, 0x1b, 0x34, 0xa6, 0x77, 0x18, 0xa0, - 0x05, 0xc8, 0x76, 0xb9, 0xde, 0xb1, 0x4e, 0x57, 0x3a, 0xe3, 0x86, 0xfd, 0xee, 0x79, 0x48, 0xfa, - 0x3e, 0xf7, 0x30, 0x25, 0x24, 0xf6, 0xf8, 0x6a, 0x5c, 0xca, 0x15, 0x05, 0x39, 0x61, 0x7a, 0x2e, - 0x77, 0x60, 0xa6, 0xf2, 0x7f, 0x1d, 0x87, 0x74, 0x68, 0x87, 0xd0, 0x32, 0x8c, 0x36, 0x54, 0xbb, - 0x66, 0x98, 0xec, 0x74, 0x66, 0x7b, 0xa2, 0xaa, 0xdb, 0x75, 0x4b, 0x65, 0x17, 0x19, 0x23, 0x45, - 0x0f, 0xa2, 0x8d, 0x7a, 0x6c, 0xe0, 0xfb, 0xaa, 0xd7, 0x9e, 0x3f, 0x88, 0xb6, 0xe7, 0xc3, 0xa7, - 0x80, 0xec, 0x36, 0xe5, 0x32, 0x20, 0xb2, 0xca, 0xae, 0xdd, 0x8c, 0x53, 0xc4, 0xb3, 0x3d, 0x9f, - 0xf9, 0x68, 0xdd, 0x74, 0x97, 0xcb, 0x14, 0x2b, 0x14, 0xe6, 0x34, 0x0c, 0xb3, 0x23, 0xcc, 0x21, - 0x98, 0x6a, 0x3b, 0x2a, 0x38, 0x3a, 0x05, 0xa6, 0xda, 0xee, 0xc0, 0x5c, 0x07, 0x62, 0x1b, 0x3a, - 0x43, 0xa7, 0x93, 0xd0, 0xd8, 0x21, 0x0f, 0x17, 0x05, 0x19, 0x1a, 0x86, 0xc9, 0x43, 0x2b, 0x02, - 0xa5, 0xb6, 0x03, 0xa8, 0xc4, 0x69, 0xa1, 0xd4, 0xf6, 0xfd, 0x1e, 0x91, 0x91, 0x20, 0x13, 0xd6, - 0x86, 0xa8, 0xdb, 0xaf, 0x3a, 0x0a, 0xf1, 0x86, 0xa5, 0xe3, 0xc2, 0xbf, 0x4c, 0x41, 0x3a, 0x64, - 0x20, 0x51, 0x05, 0x72, 0xcc, 0x4c, 0x53, 0x3f, 0xcf, 0x6a, 0xb9, 0x4c, 0xce, 0x66, 0x7a, 0xd6, - 0xb4, 0xca, 0x32, 0x7d, 0xf2, 0x18, 0xe5, 0xa8, 0x6e, 0x7b, 0xf4, 0xe8, 0x09, 0x4c, 0x05, 0x8e, - 0x52, 0xd8, 0xfb, 0xf7, 0xc4, 0xad, 0xc7, 0xfb, 0xdf, 0x62, 0xae, 0x90, 0xe7, 0xdb, 0x33, 0x0f, - 0xac, 0xd9, 0x31, 0xe8, 0x39, 0xfc, 0xef, 0x1d, 0xe7, 0xb3, 0x0f, 0x2e, 0x78, 0x7d, 0x9c, 0xf6, - 0xfb, 0xd1, 0xe1, 0x84, 0x27, 0x7e, 0x3d, 0x41, 0xa5, 0x6f, 0xf4, 0x99, 0xb7, 0xdb, 0x1b, 0x61, - 0xac, 0x47, 0x04, 0x01, 0x33, 0x03, 0x81, 0xf5, 0xc4, 0x05, 0xfe, 0xa1, 0x68, 0xfe, 0xa1, 0x8c, - 0x0c, 0x76, 0x28, 0x2b, 0xfc, 0x50, 0xde, 0x08, 0xc7, 0xdc, 0x5c, 0x60, 0xa3, 0x63, 0x6e, 0x6f, - 0x11, 0x41, 0xb8, 0xbd, 0xdd, 0x27, 0xdc, 0x4e, 0x44, 0x1f, 0x67, 0x67, 0x44, 0xcd, 0xb6, 0xa7, - 0x37, 0xd2, 0x7e, 0x10, 0x1d, 0x69, 0x27, 0x07, 0xb7, 0x1f, 0x3d, 0x41, 0xf6, 0x4a, 0x77, 0x90, - 0x9d, 0x1a, 0x68, 0xbb, 0x3b, 0x23, 0xef, 0xdb, 0x90, 0xdf, 0x55, 0x35, 0xd7, 0xb2, 0x0f, 0x95, - 0x26, 0xbd, 0x8d, 0x7d, 0x3c, 0x03, 0x3b, 0x12, 0xcc, 0x0d, 0x17, 0xe3, 0xbe, 0x9b, 0xf3, 0x4d, - 0x59, 0x62, 0xb4, 0x5b, 0x94, 0xf4, 0x76, 0x40, 0x89, 0xee, 0xf7, 0x44, 0xf0, 0xe9, 0x3e, 0x97, - 0x58, 0x6f, 0x04, 0xcf, 0xd6, 0xd5, 0x11, 0xbc, 0xdf, 0x85, 0x29, 0xdf, 0xb7, 0x58, 0x2e, 0x2b, - 0x3b, 0x06, 0xcb, 0x01, 0x52, 0xcf, 0xe1, 0xf8, 0x48, 0x8c, 0xf3, 0x2d, 0x97, 0xab, 0x06, 0x4d, - 0x12, 0xa2, 0xd7, 0x21, 0xd1, 0x72, 0xb0, 0xa2, 0xea, 0x36, 0x73, 0x25, 0xfa, 0xb3, 0xd3, 0x38, - 0x77, 0xb4, 0xe5, 0xe0, 0x8a, 0x6e, 0xa3, 0xaf, 0x02, 0x90, 0x10, 0x8b, 0x5d, 0x40, 0xd9, 0x13, - 0x2f, 0x20, 0xca, 0x9c, 0x52, 0x75, 0x7b, 0xd3, 0xbb, 0x87, 0xde, 0x84, 0x0c, 0xf3, 0x7d, 0xbc, - 0xb5, 0xe7, 0x4e, 0x5a, 0x3b, 0x78, 0xe4, 0x74, 0xd1, 0x0f, 0x60, 0x9a, 0xf8, 0x17, 0x2d, 0xa7, - 0x37, 0x7d, 0x21, 0x9e, 0xa4, 0x0d, 0x53, 0x1e, 0x67, 0x77, 0xae, 0xe2, 0x31, 0x48, 0x0c, 0xb2, - 0x37, 0x57, 0x31, 0x7e, 0xb2, 0x29, 0x96, 0xcf, 0x78, 0xdc, 0x3d, 0xa9, 0x89, 0x3b, 0x30, 0xae, - 0x63, 0xc7, 0xb0, 0xb1, 0xae, 0x04, 0x5a, 0x87, 0x06, 0xd0, 0xba, 0x1c, 0x63, 0x93, 0xb9, 0xf2, - 0x69, 0x70, 0xb6, 0x03, 0xa9, 0x5b, 0x09, 0x27, 0x06, 0x57, 0x42, 0x29, 0x84, 0xdd, 0xa9, 0x8b, - 0xdf, 0x84, 0xd9, 0xe0, 0x25, 0xbd, 0x3a, 0x39, 0x39, 0xb0, 0x4e, 0x4e, 0xfb, 0xaf, 0x28, 0x77, - 0x5f, 0xed, 0x53, 0xe1, 0x37, 0x04, 0x2a, 0x3a, 0x35, 0x90, 0x8a, 0x4e, 0x04, 0xb8, 0x81, 0xa6, - 0x3e, 0x85, 0x33, 0x1c, 0xb3, 0x4b, 0xd3, 0xce, 0x9c, 0x52, 0xd3, 0x38, 0xfc, 0x66, 0x58, 0xe1, - 0xea, 0x70, 0x9e, 0xc3, 0xf7, 0xc9, 0x5a, 0x4c, 0x9f, 0x32, 0x6b, 0x91, 0x67, 0x78, 0x95, 0x88, - 0xe4, 0x45, 0xc4, 0xdb, 0xba, 0x92, 0x18, 0xd2, 0x29, 0x93, 0x18, 0x9d, 0x6f, 0xeb, 0xcc, 0x65, - 0xec, 0xc3, 0x45, 0xfe, 0xb6, 0xfe, 0x37, 0xea, 0xec, 0xc0, 0xc7, 0xce, 0x45, 0x74, 0x2b, 0xf2, - 0x62, 0x7d, 0x1a, 0x48, 0x57, 0xd4, 0x05, 0x7b, 0x76, 0x20, 0x09, 0x90, 0xba, 0x5e, 0x11, 0x88, - 0xc1, 0x3b, 0xc0, 0xe7, 0x94, 0x9e, 0xfb, 0xf6, 0xdc, 0x40, 0xd8, 0x5c, 0x8c, 0xaa, 0x5d, 0xd7, - 0xee, 0x3a, 0x88, 0x61, 0x01, 0xa3, 0x39, 0xfd, 0xf3, 0x14, 0xf1, 0x42, 0xcf, 0xa6, 0xd0, 0x54, - 0xc0, 0xda, 0xba, 0xbc, 0xe5, 0x41, 0x66, 0x03, 0x89, 0xa2, 0xb9, 0xff, 0x27, 0x30, 0xcb, 0xdd, - 0x2a, 0xcd, 0xaf, 0x65, 0x28, 0x86, 0xe9, 0x62, 0xfb, 0x40, 0xad, 0x4b, 0x17, 0x4e, 0xb2, 0x5f, - 0xd3, 0x9e, 0x8b, 0xb5, 0xc2, 0xeb, 0x17, 0xeb, 0x8c, 0x33, 0x3a, 0xcb, 0x37, 0xf7, 0x49, 0x65, - 0xf9, 0x2e, 0xfe, 0x1a, 0x59, 0xbe, 0x05, 0x18, 0x26, 0xf7, 0x4a, 0x21, 0x3a, 0x18, 0x0f, 0x85, - 0x6c, 0x32, 0xa1, 0x43, 0x77, 0x00, 0x39, 0xda, 0x1e, 0xd6, 0x5b, 0x75, 0x1c, 0x2a, 0xf2, 0x5c, - 0x3a, 0xf9, 0x52, 0x63, 0x4c, 0x11, 0x15, 0x9d, 0xc2, 0x1f, 0x17, 0x21, 0x49, 0x9c, 0x5a, 0x57, - 0x75, 0x31, 0x7a, 0x0c, 0x48, 0x6b, 0xd9, 0x36, 0x26, 0xe6, 0xdd, 0xcf, 0x9c, 0x32, 0xa7, 0xf6, - 0xdc, 0xb1, 0xe9, 0xd5, 0x70, 0xd8, 0xcc, 0x20, 0x42, 0x95, 0x9e, 0xc7, 0x24, 0x32, 0x67, 0x62, - 0x1d, 0xe0, 0xc6, 0x4e, 0x89, 0xcb, 0xc5, 0x3a, 0xc0, 0xad, 0x42, 0xc6, 0x6b, 0x37, 0xf0, 0xb2, - 0x20, 0x2c, 0xaf, 0x33, 0xd5, 0x8d, 0xe8, 0x65, 0x4d, 0x82, 0xa8, 0x34, 0xed, 0x31, 0xd1, 0xe1, - 0xa8, 0x8c, 0x53, 0xfc, 0x25, 0x32, 0x4e, 0x4f, 0x20, 0xef, 0x57, 0xff, 0x0c, 0xbb, 0x81, 0xf5, - 0x40, 0x84, 0x55, 0xee, 0x8a, 0x1e, 0x57, 0xdd, 0x9b, 0xe6, 0xf5, 0x41, 0xca, 0xcc, 0x4f, 0xac, - 0xe2, 0xa2, 0x1b, 0x20, 0x51, 0x60, 0x1d, 0x1f, 0x28, 0xec, 0x22, 0xf6, 0x0b, 0x9b, 0x5e, 0x1d, - 0x72, 0x82, 0xcc, 0xaf, 0xe2, 0x83, 0x87, 0x74, 0x96, 0x55, 0x38, 0xfb, 0x46, 0x18, 0x89, 0x97, - 0x8c, 0x30, 0x30, 0x9c, 0x6d, 0x62, 0x53, 0x27, 0xd8, 0x51, 0xc5, 0x47, 0xe6, 0x9d, 0x0e, 0x56, - 0x7b, 0x64, 0x40, 0x11, 0x73, 0x68, 0x0d, 0x44, 0x56, 0xe2, 0xb4, 0xb1, 0xd3, 0xb4, 0x4c, 0x07, - 0xf3, 0xb2, 0x66, 0xd4, 0xf9, 0xac, 0x58, 0x8d, 0x86, 0x6a, 0xea, 0x72, 0xce, 0xe3, 0x91, 0x39, - 0x0b, 0x81, 0xe1, 0xab, 0xa5, 0x36, 0xcb, 0x71, 0x3d, 0x07, 0xf5, 0x04, 0x18, 0xc6, 0x23, 0x33, - 0x16, 0xf4, 0x00, 0x10, 0x5b, 0x0d, 0x4d, 0x48, 0xa9, 0x9a, 0x86, 0x9b, 0x2e, 0xf3, 0x56, 0x2f, - 0x45, 0x25, 0xd3, 0x88, 0x5a, 0x95, 0x36, 0x2c, 0xc3, 0xac, 0x50, 0x52, 0x99, 0x7d, 0x4c, 0x30, - 0x82, 0x1e, 0xc3, 0x24, 0x5f, 0x19, 0xc5, 0x64, 0xcb, 0x63, 0xbe, 0xea, 0xfc, 0xb1, 0xa0, 0x6c, - 0x5d, 0x32, 0x62, 0x08, 0xa1, 0x31, 0xb4, 0x44, 0x42, 0x11, 0xe5, 0x99, 0x61, 0xea, 0xd6, 0x33, - 0x47, 0x51, 0x0f, 0x54, 0xa3, 0x4e, 0xc2, 0x5c, 0xea, 0xc4, 0x26, 0x65, 0x64, 0xb7, 0x9f, 0x78, - 0x53, 0x15, 0x3e, 0x83, 0x36, 0x21, 0x6b, 0x63, 0x0d, 0x53, 0x91, 0xf2, 0x4c, 0x4b, 0x96, 0xee, - 0xd0, 0xe5, 0xbe, 0x6b, 0xf0, 0x52, 0xc6, 0x2c, 0x95, 0x26, 0x8f, 0x79, 0xdc, 0xde, 0xa0, 0x83, - 0x1e, 0x82, 0xc8, 0xe0, 0x02, 0x5b, 0x95, 0xa3, 0x80, 0xc5, 0xbe, 0x80, 0xfc, 0xd8, 0x39, 0x64, - 0xce, 0x43, 0xf0, 0x0d, 0x17, 0xda, 0x85, 0x82, 0x57, 0xb0, 0xf7, 0x3a, 0x0c, 0x14, 0xc3, 0x34, - 0x5c, 0x83, 0xb8, 0x29, 0x1d, 0x6a, 0x26, 0x9e, 0xa8, 0x66, 0xe7, 0x69, 0x75, 0xdf, 0x03, 0x59, - 0xe7, 0x18, 0x21, 0x6d, 0xb3, 0xe1, 0xbc, 0x8d, 0x3f, 0xc0, 0x9a, 0xcb, 0x9c, 0x8c, 0xae, 0x0b, - 0x1f, 0x3b, 0xd2, 0xf8, 0xdc, 0xf0, 0xc9, 0x99, 0xb0, 0xdc, 0x51, 0x35, 0xf3, 0x5c, 0x48, 0x89, - 0xb9, 0x02, 0x33, 0x17, 0x79, 0x8e, 0x5a, 0xe9, 0x2a, 0x4d, 0x63, 0x07, 0x6d, 0xc1, 0xb9, 0x8e, - 0x77, 0x76, 0x26, 0x62, 0xb0, 0x23, 0xa1, 0xb9, 0xe1, 0xe2, 0x58, 0x35, 0x7b, 0x54, 0x4d, 0x3f, - 0x17, 0x92, 0x62, 0xae, 0xe0, 0xe5, 0x5c, 0x66, 0x42, 0x90, 0xe1, 0xdc, 0x0b, 0x76, 0x50, 0x05, - 0x26, 0x7d, 0xc4, 0x70, 0x68, 0x36, 0x41, 0x43, 0x33, 0x06, 0x54, 0xe0, 0x15, 0x8e, 0x09, 0x4e, - 0x1b, 0x8e, 0xcd, 0x56, 0x41, 0xf4, 0xcc, 0x4e, 0x68, 0x7b, 0x27, 0x4f, 0xdc, 0xde, 0x2c, 0x35, - 0x45, 0xc1, 0x76, 0x5a, 0xe0, 0xaf, 0x32, 0xb4, 0x95, 0xb6, 0x6a, 0xd6, 0xb0, 0x23, 0x4d, 0x51, - 0xa1, 0x78, 0xad, 0xaf, 0x50, 0xc8, 0x8c, 0x93, 0xef, 0x97, 0x4c, 0xd9, 0xd6, 0x4c, 0xd7, 0x3e, - 0xa4, 0x05, 0xcf, 0x88, 0x49, 0xdf, 0x5a, 0x92, 0x7d, 0xd4, 0xf6, 0xc8, 0x58, 0x60, 0x2d, 0xcf, - 0x04, 0xd6, 0xb2, 0xa2, 0xdb, 0x2b, 0x74, 0x96, 0x59, 0x4b, 0x83, 0x1c, 0x3b, 0x95, 0xd9, 0x86, - 0xaa, 0x29, 0x9a, 0x67, 0x06, 0x94, 0x50, 0x97, 0x93, 0x34, 0x4d, 0x8f, 0x7d, 0xbe, 0xbf, 0xd1, - 0x08, 0x8a, 0x1b, 0xf2, 0xac, 0x87, 0xb5, 0xa9, 0x6a, 0x3d, 0x73, 0x4e, 0xfe, 0x77, 0x62, 0x90, - 0x0e, 0xeb, 0xeb, 0x03, 0xf0, 0x1d, 0x84, 0x20, 0x4d, 0x3f, 0xca, 0x76, 0xba, 0x5b, 0xc8, 0xee, - 0x71, 0xc7, 0x20, 0x74, 0x3f, 0x8a, 0x9c, 0xdd, 0x4f, 0x50, 0x7d, 0x19, 0x92, 0x76, 0x9b, 0x45, - 0x54, 0x89, 0x41, 0x7b, 0x07, 0x12, 0xb6, 0x37, 0x84, 0x16, 0x21, 0xa1, 0xed, 0x2a, 0x75, 0xc3, - 0xe1, 0x4d, 0x29, 0x67, 0x7a, 0xee, 0xd5, 0xdb, 0xf7, 0x0c, 0xc7, 0x95, 0x47, 0xb5, 0x5d, 0xf2, - 0xff, 0xee, 0x36, 0x91, 0x70, 0xd5, 0x68, 0x23, 0x9e, 0x8c, 0x8b, 0x23, 0x1b, 0xf1, 0xe4, 0x88, - 0x38, 0xba, 0x11, 0x4f, 0xa6, 0x44, 0xd8, 0x88, 0x27, 0x41, 0x4c, 0xe7, 0x7f, 0x99, 0x00, 0x08, - 0x19, 0xc3, 0x4b, 0x90, 0x68, 0x7a, 0x19, 0x79, 0xea, 0x75, 0x64, 0x68, 0x02, 0xef, 0xdb, 0x71, - 0x71, 0x5c, 0xba, 0x28, 0xf3, 0x19, 0x74, 0x07, 0x12, 0xdc, 0x48, 0xc6, 0x06, 0x37, 0x92, 0xa1, - 0x9d, 0xe2, 0xec, 0x2f, 0xd3, 0x5c, 0x74, 0x03, 0x72, 0x9a, 0x65, 0xdb, 0xb8, 0xee, 0x5d, 0x7b, - 0x86, 0xee, 0x48, 0x71, 0x5a, 0xd7, 0xc9, 0x1c, 0x55, 0x53, 0xcf, 0x85, 0xd1, 0x42, 0xdc, 0x8e, - 0x49, 0xba, 0x9c, 0x0d, 0x11, 0xad, 0xeb, 0x4e, 0x67, 0xfb, 0xd8, 0xc8, 0x6f, 0xdb, 0xc7, 0x3e, - 0xed, 0xf6, 0xb1, 0xff, 0x88, 0xc1, 0xa8, 0x89, 0x5d, 0xc5, 0xd0, 0xa9, 0x2e, 0x65, 0xaa, 0xff, - 0x1c, 0x7b, 0x5e, 0x99, 0xde, 0x48, 0x16, 0x96, 0x96, 0x96, 0x96, 0xae, 0x2f, 0x47, 0xed, 0xf5, - 0xf0, 0x67, 0x60, 0xaf, 0x97, 0x3f, 0xe9, 0xbd, 0x5e, 0xfe, 0xf4, 0xf6, 0x7a, 0xc4, 0xc4, 0xee, - 0xba, 0x1e, 0xca, 0xa9, 0xff, 0x4f, 0x02, 0xc6, 0x3a, 0x3c, 0x03, 0xf4, 0x66, 0xa0, 0xd6, 0x9e, - 0xc6, 0x4e, 0xf7, 0x29, 0xc7, 0x85, 0x95, 0x94, 0xab, 0xfb, 0x13, 0x48, 0xfa, 0xf6, 0xd0, 0xcb, - 0x30, 0x5f, 0x1f, 0xcc, 0x21, 0x29, 0x6d, 0xb7, 0x23, 0xcc, 0xa4, 0x0f, 0x86, 0x64, 0x48, 0xdb, - 0x6d, 0x85, 0x37, 0x8f, 0xd2, 0xb2, 0xec, 0xe0, 0xd8, 0x72, 0x7b, 0x93, 0x31, 0xca, 0x60, 0xfb, - 0xcf, 0xe8, 0x4d, 0x48, 0x13, 0xa3, 0x6f, 0x1c, 0x78, 0xdd, 0x7c, 0xa3, 0x27, 0xde, 0x94, 0xc0, - 0xc9, 0xa9, 0x8b, 0xdf, 0x63, 0x53, 0x12, 0x03, 0xd8, 0x94, 0x9b, 0x30, 0xc9, 0xa3, 0x20, 0xaf, - 0x15, 0x82, 0x25, 0x25, 0x52, 0xb4, 0x64, 0x46, 0xbe, 0xfc, 0xea, 0xb0, 0xf4, 0x7f, 0x82, 0x8c, - 0x58, 0xd8, 0xe3, 0x11, 0x51, 0x1f, 0x21, 0xff, 0x10, 0x20, 0xd8, 0x25, 0x74, 0x0b, 0x52, 0xfe, - 0xed, 0xcc, 0xc2, 0x3e, 0xa9, 0x9f, 0x83, 0x13, 0xde, 0x52, 0x5e, 0xe5, 0xa3, 0x26, 0x1f, 0xf2, - 0xff, 0x36, 0x0c, 0x10, 0xec, 0x0f, 0xda, 0x84, 0x74, 0x4d, 0x75, 0xf1, 0x33, 0xf5, 0x50, 0x09, - 0x9a, 0x04, 0x7a, 0x72, 0x25, 0x5f, 0xf3, 0x48, 0xa2, 0x7b, 0x03, 0xa0, 0xc6, 0x67, 0x1d, 0x74, - 0x11, 0x32, 0xfc, 0x3b, 0x6d, 0xc7, 0x31, 0xe8, 0x67, 0xc6, 0xe4, 0x34, 0x1b, 0x93, 0x1d, 0xc7, - 0x40, 0x22, 0x0c, 0x3b, 0xa6, 0x4d, 0xfd, 0xf2, 0x98, 0x4c, 0x1e, 0xd1, 0x07, 0x20, 0xf9, 0xb7, - 0x6b, 0x53, 0x75, 0xf7, 0x48, 0x7c, 0xe6, 0xb8, 0xb6, 0x6a, 0x98, 0x2e, 0x6b, 0xe8, 0xea, 0xf1, - 0x72, 0xb9, 0xfb, 0xb2, 0xa5, 0xba, 0x7b, 0x2b, 0x3e, 0x75, 0xb8, 0xd9, 0x4a, 0x8f, 0xa4, 0x20, - 0x0b, 0x64, 0x79, 0x06, 0xd7, 0xda, 0xc7, 0x26, 0xcd, 0xdc, 0x66, 0xe4, 0xb4, 0x37, 0xb6, 0x4d, - 0x86, 0xd0, 0x3e, 0x8c, 0x35, 0x55, 0x6d, 0x1f, 0xbb, 0xca, 0x8e, 0x6d, 0xed, 0x63, 0x9b, 0x25, - 0x3c, 0x6f, 0x9f, 0x5a, 0xf8, 0x4a, 0x5b, 0x14, 0xa6, 0x4a, 0x51, 0x7c, 0x89, 0xcc, 0x34, 0x43, - 0xa3, 0xf9, 0xb3, 0x30, 0x19, 0x45, 0x45, 0x6f, 0xe9, 0x34, 0x3d, 0xb2, 0x94, 0x77, 0x1b, 0x6f, - 0xc4, 0x93, 0x19, 0x71, 0x6c, 0x23, 0x9e, 0x1c, 0x13, 0xb3, 0x1b, 0xf1, 0xa4, 0x28, 0x8e, 0x6f, - 0xc4, 0x93, 0xe3, 0x22, 0xda, 0x88, 0x27, 0x27, 0xc4, 0xc9, 0x8d, 0x78, 0x72, 0x52, 0x9c, 0xda, - 0x88, 0x27, 0x35, 0x51, 0xf7, 0x6f, 0x78, 0xef, 0x6e, 0x4f, 0x72, 0x84, 0xfc, 0x0f, 0xe3, 0x90, - 0xeb, 0x72, 0xe2, 0xd1, 0x46, 0xb7, 0xf6, 0x2f, 0x0d, 0xea, 0xff, 0x73, 0xb3, 0x10, 0x18, 0x83, - 0x08, 0x15, 0x19, 0x3d, 0x59, 0x45, 0xf2, 0xff, 0x1d, 0x83, 0x04, 0x5f, 0xce, 0x36, 0x8c, 0x34, - 0x94, 0x3d, 0xdd, 0x66, 0x82, 0x78, 0xe3, 0xb4, 0x8b, 0x29, 0x6d, 0xde, 0x59, 0x95, 0xc3, 0xfe, - 0x40, 0xe3, 0x8e, 0x6e, 0xa3, 0xf7, 0x20, 0x4d, 0x5c, 0x46, 0xfe, 0xa1, 0x9e, 0x47, 0xf1, 0xe6, - 0xe9, 0xb1, 0x2b, 0x2b, 0xac, 0x25, 0x41, 0x86, 0x86, 0xaa, 0xb1, 0xe7, 0xfc, 0x2a, 0xc4, 0xc9, - 0x5b, 0xd1, 0xeb, 0x30, 0xda, 0x50, 0xc8, 0x5d, 0xc1, 0x0a, 0xf1, 0x3d, 0xa9, 0x8e, 0xcd, 0xed, - 0xc3, 0x66, 0xb8, 0xab, 0x71, 0xa4, 0x41, 0x06, 0x3c, 0x57, 0x2c, 0xff, 0x14, 0x20, 0xc0, 0x47, - 0x17, 0x60, 0x74, 0x57, 0x69, 0x5a, 0xb6, 0xcb, 0x8a, 0xf0, 0x81, 0xa1, 0x18, 0xd9, 0xdd, 0xb2, - 0x6c, 0x17, 0x9d, 0x05, 0xd8, 0x6d, 0xd5, 0xeb, 0xac, 0xfe, 0xe0, 0x75, 0x45, 0x27, 0xc9, 0x08, - 0x71, 0x96, 0xbb, 0x24, 0xc0, 0xf3, 0xee, 0x3c, 0x7f, 0x2f, 0x2e, 0x26, 0x43, 0xb3, 0xc4, 0xdf, - 0x4b, 0x88, 0xc9, 0xfc, 0x3f, 0x08, 0x30, 0xd6, 0xe1, 0xa4, 0xf7, 0x6b, 0x9f, 0x12, 0x3e, 0xa5, - 0xf6, 0xa9, 0xd8, 0x4b, 0xb6, 0x4f, 0x85, 0x6e, 0x38, 0x1d, 0xb2, 0x5d, 0xf1, 0xc6, 0x1d, 0x18, - 0x65, 0xd1, 0x8c, 0x70, 0x42, 0xcc, 0xdc, 0xc1, 0x18, 0xea, 0x46, 0x64, 0xfc, 0xa1, 0xb7, 0xd8, - 0x30, 0x7b, 0x4c, 0xe8, 0x43, 0x8c, 0xdc, 0x3e, 0x66, 0x2d, 0x6e, 0x32, 0x79, 0x44, 0x5f, 0xe1, - 0xad, 0xd8, 0x7d, 0x3a, 0x49, 0xa2, 0xd7, 0xe0, 0xf0, 0x9e, 0xed, 0xd8, 0x97, 0x42, 0x55, 0xef, - 0xc2, 0xcf, 0x05, 0x98, 0xf5, 0x1b, 0xb7, 0x2a, 0x2d, 0x77, 0x8f, 0xd8, 0x65, 0x2f, 0x39, 0xb3, - 0x62, 0xe9, 0x18, 0x2d, 0x85, 0xfb, 0xbe, 0x53, 0xd5, 0xfc, 0x51, 0x75, 0xda, 0x9e, 0x2a, 0x4f, - 0xbc, 0xff, 0xae, 0xba, 0xf0, 0xed, 0xca, 0xc2, 0x37, 0x96, 0x16, 0xde, 0x78, 0xfa, 0xe1, 0xf5, - 0x6b, 0xcb, 0xe5, 0x8f, 0xe6, 0x19, 0x3e, 0x7a, 0x03, 0x80, 0xfe, 0x96, 0x47, 0xd9, 0xb5, 0xad, - 0x06, 0x5b, 0xe3, 0xb1, 0x8d, 0xee, 0x94, 0xfa, 0xb6, 0x6d, 0x35, 0xd0, 0x0d, 0x48, 0x7a, 0xac, - 0xae, 0xc5, 0x54, 0xeb, 0x38, 0xc6, 0x04, 0xa5, 0xdd, 0xb6, 0x42, 0x5f, 0xf3, 0xa7, 0x73, 0x90, - 0xf2, 0xbf, 0x06, 0xdd, 0x09, 0xb7, 0xab, 0xcd, 0xf7, 0x6d, 0x57, 0x0b, 0xdf, 0x45, 0x22, 0xd7, - 0x77, 0x0a, 0x39, 0x54, 0x64, 0xfd, 0x6a, 0x2b, 0x00, 0x9a, 0x8d, 0x55, 0xd6, 0xbc, 0x7f, 0xe2, - 0x37, 0x55, 0xbd, 0x15, 0x0d, 0x89, 0x43, 0x72, 0x8a, 0xf1, 0x55, 0x5c, 0x02, 0xd2, 0x6a, 0xea, - 0x1c, 0x64, 0xf8, 0x34, 0x20, 0x8c, 0xaf, 0xe2, 0xa2, 0x59, 0x88, 0x9b, 0x6a, 0x03, 0x77, 0xf6, - 0xa1, 0x95, 0x65, 0x3a, 0x88, 0xae, 0x42, 0x5a, 0xc7, 0x8e, 0x66, 0x1b, 0xf4, 0x37, 0x2e, 0x54, - 0x97, 0xbd, 0x0e, 0x34, 0x7b, 0x58, 0xfa, 0x45, 0x4e, 0x0e, 0x4f, 0xa2, 0xef, 0x0a, 0x00, 0xaa, - 0xeb, 0xda, 0xc6, 0x4e, 0xcb, 0xc5, 0x9e, 0x79, 0x4d, 0x97, 0xaf, 0xf4, 0xdd, 0xa4, 0x52, 0xc5, - 0xa7, 0xa5, 0x72, 0x59, 0xbd, 0x71, 0x54, 0x2d, 0xff, 0x48, 0x58, 0x14, 0xa1, 0x30, 0x6f, 0x17, - 0xa4, 0xf9, 0xf2, 0x79, 0x2a, 0x12, 0x44, 0x1e, 0x8a, 0xb7, 0x6e, 0xbe, 0xbb, 0xf0, 0xf4, 0x16, - 0xff, 0xf3, 0xca, 0x87, 0xe5, 0x6b, 0x1f, 0xcd, 0x5f, 0x25, 0x6b, 0xf8, 0x99, 0x20, 0x87, 0xde, - 0x89, 0xee, 0x41, 0x9a, 0xe5, 0x62, 0x99, 0x13, 0x74, 0xda, 0xb6, 0x42, 0x19, 0x0e, 0xf8, 0x18, - 0x09, 0xd5, 0x90, 0x83, 0x6d, 0xea, 0x20, 0x35, 0x6d, 0x6b, 0xd7, 0xa8, 0x63, 0x12, 0x0e, 0x24, - 0x3b, 0xfb, 0xf5, 0x44, 0x46, 0xb2, 0xe5, 0x51, 0xac, 0xeb, 0xe8, 0xa7, 0x02, 0x9c, 0xe1, 0x69, - 0x26, 0x32, 0x89, 0x6d, 0x1a, 0xb5, 0x61, 0xc7, 0xa1, 0x2e, 0x47, 0xaa, 0xfa, 0x87, 0xc2, 0x51, - 0xf5, 0xfb, 0x82, 0xfd, 0xfb, 0x42, 0xf9, 0x77, 0x85, 0xf7, 0x8b, 0xb7, 0x6e, 0x92, 0xcf, 0x0b, - 0x84, 0xff, 0x3b, 0xa1, 0xe7, 0xe0, 0xf1, 0xbd, 0x85, 0xa7, 0x57, 0x43, 0x13, 0x57, 0xde, 0x2b, - 0x5d, 0xb9, 0x4a, 0xf8, 0x2a, 0x0b, 0xdf, 0x60, 0xbb, 0xf2, 0x9d, 0xd0, 0x73, 0xf0, 0x48, 0xf9, - 0x82, 0x89, 0x2b, 0xc5, 0x5b, 0x37, 0x6f, 0xbe, 0xcb, 0x74, 0xec, 0xc6, 0x47, 0x57, 0x6e, 0xcd, - 0x7f, 0xe7, 0xfd, 0x79, 0x79, 0x92, 0x2d, 0xf7, 0x21, 0x5d, 0x6d, 0xc5, 0x5b, 0x2c, 0xaa, 0x80, - 0xd4, 0xf5, 0x19, 0xfb, 0x78, 0x5f, 0xa9, 0xab, 0x3b, 0xb8, 0x2e, 0x2d, 0x86, 0x05, 0xe1, 0xbb, - 0xa2, 0x3c, 0xd5, 0x81, 0x70, 0x17, 0xef, 0xdf, 0x23, 0x64, 0xe8, 0x1f, 0x05, 0xc8, 0x87, 0x93, - 0xbc, 0x5d, 0xdb, 0x01, 0x9f, 0xcd, 0xed, 0x90, 0x42, 0x4b, 0xee, 0xdc, 0x92, 0x75, 0x38, 0x1b, - 0xf1, 0x39, 0xc1, 0xb6, 0x2c, 0x75, 0x6d, 0xcb, 0x4c, 0x0f, 0x92, 0xbf, 0x35, 0x6f, 0xc2, 0x54, - 0x04, 0x94, 0xa1, 0x4b, 0xd7, 0xc3, 0xf2, 0xa5, 0xcb, 0x13, 0x3d, 0x10, 0xeb, 0x3a, 0xfa, 0x3b, - 0x01, 0x26, 0x68, 0xd2, 0xb7, 0x6b, 0x43, 0xd3, 0x9f, 0xcd, 0x0d, 0x1d, 0x27, 0x6b, 0xed, 0xdc, - 0x49, 0x17, 0x52, 0x75, 0xcb, 0xfb, 0x2a, 0x47, 0xca, 0x44, 0x67, 0x77, 0x03, 0x53, 0x71, 0x8f, - 0x93, 0x7a, 0x96, 0xe2, 0xda, 0x51, 0xf5, 0xca, 0x8f, 0x84, 0xcb, 0x83, 0xd9, 0x09, 0x39, 0x78, - 0x11, 0xba, 0x0e, 0x09, 0xf6, 0x8b, 0x3e, 0xa9, 0x1c, 0x1d, 0x4f, 0x6e, 0x79, 0xd3, 0x32, 0xa7, - 0x8b, 0x6c, 0xae, 0x1d, 0x1b, 0xb8, 0xb9, 0x36, 0xdb, 0xa7, 0xb9, 0xb6, 0xa7, 0x74, 0x94, 0xfb, - 0xe4, 0x9b, 0x95, 0xc5, 0x4f, 0xa1, 0x59, 0x79, 0xfc, 0x84, 0x66, 0xe5, 0x9e, 0x3e, 0x5f, 0x34, - 0x48, 0x9f, 0xef, 0xc4, 0x20, 0x7d, 0xbe, 0x93, 0x03, 0xf7, 0xf9, 0x4e, 0xf5, 0xe9, 0xf3, 0xbd, - 0x01, 0x29, 0xdb, 0xb2, 0x5c, 0x85, 0xa6, 0xee, 0xce, 0x44, 0x47, 0xa9, 0xb2, 0x65, 0xb9, 0x77, - 0xf1, 0xa1, 0x23, 0x27, 0x6d, 0xf6, 0x14, 0x4e, 0x05, 0x4d, 0xff, 0x36, 0x15, 0xf4, 0x29, 0xa6, - 0x82, 0xd0, 0x57, 0x21, 0xd3, 0xd1, 0x6c, 0x2e, 0x9d, 0xdc, 0x6c, 0x4e, 0x82, 0x27, 0x3f, 0x0d, - 0x71, 0x03, 0x52, 0x94, 0x9f, 0xf8, 0xaa, 0xac, 0x0f, 0x50, 0xea, 0xe7, 0xcb, 0xca, 0x49, 0xc2, - 0x49, 0xab, 0xd7, 0xab, 0x30, 0xce, 0x2b, 0x69, 0x01, 0xfb, 0xb5, 0x13, 0xd8, 0x79, 0x89, 0x6f, - 0x93, 0xa3, 0x5c, 0x87, 0x84, 0xe3, 0xe5, 0x7d, 0xa5, 0x7c, 0xb4, 0x6d, 0x61, 0x69, 0x61, 0x99, - 0xd3, 0xa1, 0xb7, 0x80, 0xa3, 0x28, 0x9c, 0x75, 0xf6, 0x78, 0xd6, 0x2c, 0xa3, 0xe7, 0xbf, 0x2a, - 0x9e, 0x87, 0xac, 0x5f, 0xdc, 0xa5, 0xd2, 0x4f, 0x5b, 0x3d, 0xc6, 0xe4, 0x0c, 0x2b, 0xe9, 0x52, - 0xc9, 0x47, 0x97, 0x21, 0xd7, 0x72, 0xb0, 0x1e, 0x50, 0x39, 0xd2, 0xb9, 0xb9, 0xe1, 0xe2, 0x98, - 0x3c, 0x46, 0x86, 0x39, 0x99, 0x43, 0xe8, 0x28, 0x5a, 0xa0, 0x4c, 0xb4, 0x17, 0x83, 0xfd, 0x52, - 0xd5, 0xd7, 0x24, 0xf4, 0x0a, 0xa3, 0xb3, 0x3f, 0x60, 0x3d, 0x5d, 0x4b, 0xb4, 0xbb, 0x82, 0xbd, - 0x56, 0xfe, 0x80, 0xf6, 0x6a, 0x2d, 0xf5, 0x92, 0x5d, 0xa7, 0x5d, 0x13, 0x9d, 0x64, 0xd7, 0xd1, - 0x3b, 0x30, 0xdb, 0x5d, 0xa0, 0x0e, 0xa7, 0xc2, 0x2e, 0x0e, 0x56, 0xfa, 0xf6, 0xeb, 0xd7, 0x72, - 0x90, 0x17, 0xbb, 0x0b, 0x69, 0xaf, 0x10, 0xe6, 0x1d, 0x69, 0xa1, 0x8f, 0x41, 0x24, 0x24, 0xf4, - 0x0c, 0xab, 0x59, 0x6e, 0x10, 0x3f, 0x7e, 0x31, 0x13, 0x13, 0x87, 0x64, 0x68, 0xfa, 0x73, 0xe8, - 0x5d, 0x40, 0x3b, 0xb4, 0x9b, 0xff, 0x50, 0x69, 0x62, 0x5b, 0xc3, 0xa6, 0xab, 0xd6, 0x30, 0x6b, - 0xa2, 0x38, 0xb6, 0x3f, 0x2f, 0x77, 0x54, 0xcd, 0x00, 0x9c, 0x1b, 0x1a, 0xfa, 0xee, 0xad, 0x85, - 0xa1, 0xa1, 0xa1, 0x21, 0x79, 0x9c, 0xe1, 0x6c, 0xf9, 0x30, 0xe8, 0x55, 0xc8, 0xf9, 0x69, 0x26, - 0xd6, 0xf9, 0x37, 0x3f, 0x27, 0x14, 0x47, 0xe4, 0x2c, 0x1f, 0x66, 0xdd, 0x7d, 0x27, 0xfd, 0x72, - 0xb7, 0xf8, 0x89, 0xfc, 0x72, 0x17, 0xdd, 0x01, 0x08, 0xfd, 0x14, 0xe2, 0xca, 0xe9, 0x7e, 0x0a, - 0x21, 0x87, 0x78, 0xd1, 0x03, 0xc8, 0x36, 0x6d, 0xeb, 0xc0, 0x20, 0xe2, 0xea, 0x39, 0x38, 0x57, - 0xe9, 0x1d, 0x72, 0xf5, 0xa8, 0xfa, 0xaa, 0xfd, 0x8a, 0x34, 0x5f, 0xbe, 0x78, 0xfc, 0xf5, 0x4d, - 0xfc, 0x87, 0xb1, 0x10, 0xc2, 0xba, 0x4e, 0xf5, 0x95, 0x0f, 0x10, 0xdd, 0xa1, 0x59, 0xd8, 0x2f, - 0x30, 0xc5, 0xe9, 0x3e, 0x88, 0x87, 0xf4, 0x5f, 0x3a, 0x90, 0xc5, 0x30, 0x07, 0x09, 0x63, 0xd1, - 0x59, 0x48, 0x35, 0x5a, 0x75, 0x12, 0x9a, 0x3a, 0xae, 0xb4, 0x40, 0xaf, 0x8c, 0x60, 0x00, 0xd5, - 0x60, 0x46, 0xab, 0xab, 0x46, 0x43, 0x51, 0x3b, 0x22, 0x58, 0x45, 0xb3, 0x74, 0x2c, 0x95, 0x4e, - 0x88, 0x2b, 0x7a, 0xa3, 0x5e, 0xda, 0x5c, 0x64, 0x34, 0x22, 0xc2, 0xe1, 0x12, 0x4c, 0x38, 0xfb, - 0x46, 0x93, 0x27, 0x80, 0x14, 0xcd, 0x3e, 0x6c, 0xba, 0x96, 0xb4, 0x4c, 0x17, 0x34, 0x4e, 0xa6, - 0xd8, 0xfe, 0xae, 0xd0, 0x09, 0xf4, 0x2e, 0x9c, 0x8d, 0xa0, 0x57, 0xac, 0x03, 0x6c, 0xdb, 0x86, - 0x8e, 0xa5, 0xd7, 0xfa, 0xa8, 0x4b, 0xd0, 0xd6, 0x33, 0xd3, 0x03, 0xfa, 0x36, 0x63, 0x46, 0x5f, - 0x81, 0x8c, 0xaa, 0xb9, 0xc6, 0x01, 0x0f, 0x29, 0x6f, 0x9c, 0xa8, 0x7b, 0x69, 0x9f, 0xbe, 0xe2, - 0xa2, 0x2f, 0x03, 0xd5, 0x6c, 0xc5, 0xc1, 0xd8, 0x24, 0xec, 0xaf, 0x9f, 0x9c, 0xc5, 0x26, 0xf4, - 0x0f, 0x31, 0x36, 0x2b, 0x6e, 0xfe, 0x2b, 0x90, 0xeb, 0x0a, 0x09, 0xc3, 0xa9, 0x8a, 0x94, 0x97, - 0xaa, 0x98, 0x0c, 0xa7, 0x2a, 0x52, 0xa1, 0x0c, 0x44, 0xfe, 0x31, 0x64, 0x3b, 0xdd, 0xc4, 0x08, - 0xee, 0x52, 0x67, 0xa2, 0xa3, 0xc7, 0xba, 0x73, 0x80, 0xc8, 0xcc, 0xc6, 0x46, 0x3c, 0xf9, 0x8a, - 0x78, 0x79, 0x23, 0x9e, 0xbc, 0x2c, 0xbe, 0xba, 0x11, 0x4f, 0xbe, 0x2a, 0x16, 0x0b, 0x77, 0x00, - 0xfc, 0x43, 0x77, 0xd0, 0x4d, 0x48, 0x07, 0xff, 0x90, 0x09, 0x4f, 0xe8, 0xcc, 0xf4, 0x95, 0x12, - 0x19, 0xb0, 0xcf, 0x5b, 0xf8, 0x58, 0x80, 0xb1, 0x55, 0xaf, 0xf6, 0xb4, 0x65, 0xe3, 0x5d, 0xa3, - 0x8d, 0x7e, 0x26, 0x84, 0x2a, 0x7e, 0x5e, 0x51, 0xf3, 0x2f, 0x85, 0xcf, 0x53, 0xc5, 0x2f, 0x28, - 0xa6, 0x9d, 0x81, 0xd1, 0x3a, 0x36, 0x6b, 0xee, 0x9e, 0x97, 0x31, 0x94, 0xd9, 0x5f, 0x85, 0xf7, - 0xe0, 0xcc, 0x0a, 0x4d, 0x66, 0x04, 0x7b, 0xc2, 0x0a, 0xac, 0x55, 0x80, 0x60, 0x2b, 0xfd, 0x5f, - 0x47, 0xf4, 0xdb, 0xc9, 0x50, 0x52, 0x35, 0xe5, 0xef, 0x69, 0xe1, 0x87, 0x02, 0x9c, 0x79, 0x44, - 0xd3, 0x1c, 0x9f, 0x06, 0x3c, 0x7a, 0x03, 0x20, 0xf8, 0x77, 0x56, 0xfa, 0x66, 0x70, 0x6e, 0x13, - 0x92, 0x4d, 0xd5, 0xd9, 0x97, 0x53, 0xbb, 0xfc, 0xb1, 0xf0, 0x17, 0x31, 0xb8, 0x54, 0x55, 0x5d, - 0x6d, 0xaf, 0x6b, 0x79, 0xf7, 0x98, 0x32, 0xf0, 0x65, 0x62, 0x48, 0x78, 0x79, 0x1a, 0x2e, 0x4c, - 0x77, 0x7b, 0x9a, 0xf5, 0x4e, 0x46, 0x29, 0xf5, 0x4c, 0x78, 0xf4, 0x32, 0xc7, 0xce, 0xff, 0x89, - 0x00, 0xd3, 0x7d, 0x88, 0xd0, 0x5b, 0xa7, 0xcf, 0x78, 0x75, 0xfd, 0x32, 0xb3, 0xdb, 0x28, 0xc4, - 0x4e, 0x63, 0x14, 0x0a, 0x7f, 0x2e, 0xc0, 0x04, 0xf1, 0x15, 0xbb, 0x4f, 0x70, 0x1b, 0xb2, 0xc1, - 0x09, 0x2a, 0xbf, 0xfe, 0x12, 0x33, 0x38, 0x98, 0x77, 0x5e, 0xe6, 0x4c, 0x7f, 0x3e, 0x0c, 0xaf, - 0x84, 0x17, 0x1a, 0x7a, 0xdd, 0x6d, 0xcb, 0x5e, 0x7b, 0xb4, 0xee, 0xf0, 0xa5, 0xff, 0x42, 0x80, - 0x24, 0xf5, 0xb0, 0x70, 0xcb, 0x60, 0x8a, 0xfd, 0x37, 0xc2, 0xf3, 0xca, 0xc5, 0x0d, 0x54, 0xf8, - 0xe2, 0x52, 0x75, 0x79, 0xf5, 0xc6, 0x17, 0xd7, 0x56, 0x97, 0xfa, 0x2b, 0x78, 0xf2, 0x33, 0xa0, - 0xe0, 0x5f, 0x62, 0x0a, 0x4e, 0x3e, 0x63, 0xad, 0x65, 0xa0, 0x7f, 0x12, 0x80, 0x28, 0x3b, 0xfd, - 0xa2, 0xd8, 0xe7, 0xf9, 0x8b, 0x46, 0x75, 0x7c, 0xb0, 0xd6, 0x32, 0x0a, 0x47, 0x31, 0x98, 0xba, - 0x67, 0x38, 0xc1, 0x71, 0xfa, 0xa7, 0xf7, 0x36, 0xe4, 0xc2, 0x9e, 0x57, 0x20, 0x79, 0x97, 0x8f, - 0xf1, 0xb9, 0xc2, 0x19, 0xc6, 0xac, 0x1a, 0x1e, 0x7f, 0x19, 0x99, 0x43, 0x3f, 0x16, 0x60, 0xc4, - 0xb2, 0x75, 0x6c, 0xd3, 0xdc, 0x6f, 0xaa, 0xfa, 0x07, 0xc2, 0x51, 0xf5, 0xf7, 0x04, 0xfb, 0x7b, - 0x82, 0x3c, 0x24, 0xa7, 0x7c, 0xd5, 0x90, 0x61, 0x21, 0x78, 0xf6, 0x25, 0x4f, 0x4e, 0x2d, 0xf8, - 0x8f, 0xfc, 0xec, 0xe4, 0xe4, 0x02, 0x7f, 0xa2, 0xe9, 0x5f, 0x79, 0x64, 0x81, 0xfe, 0x2f, 0x9c, - 0xe6, 0x95, 0x33, 0x0b, 0xe1, 0xbf, 0x42, 0x59, 0x6c, 0x39, 0xbd, 0x10, 0xfa, 0xc3, 0x5b, 0x18, - 0x3a, 0x0f, 0x23, 0xde, 0xbf, 0x67, 0x12, 0x0f, 0xd7, 0x8a, 0xfe, 0x33, 0x21, 0x7b, 0xc3, 0x08, - 0x41, 0xbc, 0x49, 0xfc, 0x68, 0xaf, 0x4a, 0x44, 0x9f, 0x0b, 0x7f, 0x24, 0xc0, 0xc4, 0xc3, 0x08, - 0x9d, 0xff, 0x0d, 0x5b, 0xed, 0xbf, 0x12, 0x60, 0x56, 0xc6, 0x0e, 0x76, 0x2b, 0xa6, 0xfe, 0xb9, - 0x30, 0x49, 0x3f, 0x11, 0x60, 0xdc, 0x7f, 0xd7, 0x36, 0x6e, 0x34, 0xeb, 0xc4, 0xa2, 0xff, 0x66, - 0x77, 0x11, 0x15, 0x21, 0xdd, 0x50, 0x9b, 0xb4, 0xb5, 0x98, 0x38, 0x67, 0xc3, 0x9d, 0xd9, 0x4e, - 0x60, 0x73, 0x77, 0xf1, 0x61, 0xe1, 0x6f, 0xc3, 0xd7, 0x12, 0x5f, 0xbe, 0x17, 0x65, 0xf8, 0x45, - 0x0b, 0xa1, 0x93, 0x3d, 0xb2, 0x68, 0x11, 0x0b, 0x27, 0x65, 0x7f, 0x26, 0x74, 0x16, 0x2d, 0xb6, - 0x21, 0x47, 0x13, 0xfb, 0xb8, 0xed, 0x62, 0xd3, 0xa1, 0xd9, 0xc8, 0x61, 0x5a, 0x17, 0xfe, 0xc2, - 0x51, 0xb5, 0xf8, 0x5c, 0x78, 0x45, 0xd4, 0x25, 0xa1, 0x70, 0xc1, 0x3e, 0x57, 0x9e, 0x7d, 0xbf, - 0x78, 0xeb, 0xe6, 0x7b, 0x25, 0x1e, 0x9c, 0x7c, 0x78, 0xfd, 0xda, 0xf5, 0xd7, 0x3f, 0xba, 0xf2, - 0xe1, 0xf5, 0x6b, 0xe5, 0x8f, 0xe6, 0xe5, 0x2c, 0xc1, 0x58, 0xf3, 0x21, 0x0a, 0xff, 0x2b, 0x80, - 0xd4, 0x67, 0xe9, 0x0e, 0xfa, 0x08, 0x12, 0x5e, 0x7c, 0xc4, 0x6f, 0xf5, 0x1b, 0x7d, 0x77, 0xbf, - 0x8b, 0xb5, 0xc4, 0xfe, 0xff, 0xeb, 0x64, 0x41, 0xf9, 0x3b, 0xf3, 0x1a, 0x64, 0xc2, 0x30, 0x11, - 0x5e, 0xf2, 0x49, 0xe5, 0xc0, 0x3e, 0xcb, 0x0b, 0x39, 0xcd, 0x85, 0x0f, 0xe1, 0xc2, 0x8a, 0x65, - 0x1e, 0x60, 0xdb, 0xed, 0x21, 0xe6, 0xea, 0xb2, 0x0a, 0x29, 0x6f, 0x49, 0x24, 0x26, 0xf4, 0xce, - 0xf1, 0xd5, 0xa3, 0xea, 0x60, 0x1f, 0x93, 0xf4, 0x38, 0xd7, 0x75, 0x62, 0x3f, 0x68, 0xf8, 0x47, - 0x6f, 0x1d, 0x99, 0x3e, 0x5f, 0x7d, 0x04, 0x10, 0xc4, 0xf4, 0x68, 0x1c, 0xc6, 0xb6, 0xde, 0x7e, - 0xb2, 0x26, 0x2b, 0x8f, 0xee, 0xdf, 0xbd, 0xff, 0xf6, 0x93, 0xfb, 0xe2, 0x50, 0x30, 0x54, 0xad, - 0x6c, 0x6f, 0xaf, 0xc9, 0x5f, 0x17, 0x05, 0x84, 0x20, 0xeb, 0x0d, 0xad, 0xbd, 0xb3, 0xbd, 0x26, - 0xdf, 0xaf, 0xdc, 0x13, 0x63, 0xf9, 0xb1, 0x5f, 0xbd, 0x98, 0x49, 0x49, 0xc2, 0xd5, 0x11, 0x3a, - 0x53, 0xbd, 0xf1, 0xf7, 0xff, 0x7e, 0x5e, 0xf8, 0xc6, 0xe2, 0x29, 0xae, 0x18, 0xd7, 0x6c, 0xee, - 0xec, 0x8c, 0x52, 0x7d, 0x58, 0xfe, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x6f, 0x97, 0x5e, 0xb6, - 0xf7, 0x50, 0x00, 0x00, + 0xe5, 0x95, 0xc7, 0xb2, 0xbd, 0xc9, 0xae, 0x77, 0x32, 0x9a, 0xb9, 0xa2, 0x66, 0x45, 0xce, 0x30, + 0x33, 0x43, 0x99, 0xca, 0x66, 0x81, 0x20, 0xdf, 0xf7, 0xe1, 0xc3, 0x17, 0x20, 0xdf, 0x83, 0x51, + 0x34, 0x68, 0xfa, 0x12, 0xf4, 0xa1, 0x28, 0x8c, 0xa2, 0xe8, 0x43, 0x1f, 0xfa, 0x10, 0xa0, 0x7d, + 0x09, 0x90, 0x22, 0x68, 0x9b, 0xf4, 0xb1, 0x40, 0x81, 0x02, 0x7d, 0x68, 0x11, 0xa0, 0x7d, 0xd8, + 0xb7, 0xea, 0xa1, 0x2d, 0xee, 0x9d, 0x7b, 0x67, 0x86, 0xe4, 0x50, 0xa2, 0xe2, 0x5d, 0x64, 0x03, + 0xe4, 0x65, 0x77, 0x74, 0xef, 0x39, 0xbf, 0xb9, 0x73, 0xef, 0x39, 0xe7, 0x9e, 0x7f, 0x34, 0x14, + 0xea, 0x96, 0xad, 0x3e, 0x53, 0xcd, 0x45, 0xc7, 0x55, 0xb5, 0x83, 0x25, 0xb5, 0x69, 0x2c, 0x61, + 0x53, 0x57, 0x74, 0x7c, 0x68, 0x68, 0xb8, 0xd4, 0xb4, 0x2d, 0xd7, 0x42, 0x59, 0xd7, 0x35, 0x4b, + 0x8c, 0xae, 0x74, 0xb8, 0x92, 0xaf, 0xd4, 0x0c, 0x77, 0xbf, 0xb5, 0x5b, 0xd2, 0xac, 0xc6, 0x12, + 0x36, 0x0f, 0xad, 0xa3, 0xa6, 0x6d, 0xb5, 0x8f, 0x96, 0x28, 0xb1, 0xb6, 0x58, 0xc3, 0xe6, 0xe2, + 0xa1, 0x5a, 0x37, 0x74, 0xd5, 0xc5, 0x4b, 0x3d, 0x0f, 0x1e, 0x64, 0x7e, 0x31, 0x04, 0x51, 0xb3, + 0x6a, 0x96, 0xc7, 0xbc, 0xdb, 0xda, 0xa3, 0x7f, 0xd1, 0x3f, 0xe8, 0x13, 0x23, 0x5f, 0x0b, 0x91, + 0xef, 0xec, 0xe3, 0x9d, 0x7d, 0xc3, 0xac, 0x39, 0x1b, 0xa6, 0xde, 0x72, 0x5c, 0xdb, 0xc0, 0x4e, + 0xf8, 0xd5, 0x35, 0x6b, 0x71, 0xaf, 0xae, 0xd6, 0x9c, 0x25, 0xd5, 0x34, 0x2d, 0x57, 0x75, 0x0d, + 0xcb, 0x74, 0x18, 0xca, 0xea, 0x99, 0x50, 0x3e, 0x74, 0x2c, 0x33, 0x02, 0xe4, 0x42, 0xcd, 0xb2, + 0x6a, 0x75, 0x1c, 0x2c, 0x58, 0x6f, 0xd9, 0x94, 0x80, 0xcd, 0xcf, 0x77, 0xcf, 0xef, 0x19, 0xb8, + 0xae, 0x2b, 0x0d, 0xd5, 0x39, 0x60, 0x14, 0xe7, 0xba, 0x29, 0x1c, 0xd7, 0x6e, 0x69, 0x2e, 0x9b, + 0xbd, 0xd8, 0x3d, 0xeb, 0x1a, 0x0d, 0xec, 0xb8, 0x6a, 0xa3, 0xd9, 0x6f, 0x01, 0xcf, 0x6c, 0xb5, + 0xd9, 0xc4, 0x36, 0x5f, 0xe0, 0xf9, 0xa8, 0x13, 0x6d, 0x35, 0xf8, 0xf4, 0x2b, 0xbd, 0xd3, 0x86, + 0x8e, 0x4d, 0xd7, 0xd8, 0x33, 0x02, 0x8c, 0x73, 0xbd, 0x44, 0x07, 0xf8, 0x88, 0xcf, 0x5e, 0xec, + 0x9d, 0xe5, 0xd2, 0xc1, 0xf6, 0xa0, 0x97, 0xa0, 0x81, 0x1d, 0x47, 0xad, 0x61, 0xe7, 0x24, 0x0a, + 0x57, 0xd5, 0x55, 0x57, 0xed, 0xff, 0x92, 0xa6, 0xa1, 0xb9, 0x2d, 0x9b, 0x8b, 0x50, 0x29, 0x74, + 0x5c, 0x56, 0x13, 0x9b, 0x6a, 0xd3, 0x38, 0x2c, 0x2f, 0x59, 0x4d, 0x7a, 0x58, 0xbd, 0x07, 0x57, + 0xf8, 0xc1, 0x28, 0x24, 0x1e, 0x62, 0xc7, 0x31, 0x2c, 0x13, 0xfd, 0x7b, 0x0c, 0x92, 0x3a, 0x3e, + 0x54, 0x54, 0x5d, 0xb7, 0xa5, 0xd8, 0xbc, 0x50, 0xcc, 0x54, 0x7f, 0x19, 0x7b, 0x5e, 0x99, 0xdd, + 0x84, 0x42, 0xf9, 0x8d, 0xe5, 0xe5, 0x4a, 0x75, 0x75, 0xad, 0xf0, 0xa3, 0x98, 0x90, 0xf8, 0xa3, + 0xd8, 0x28, 0x91, 0x0d, 0xb3, 0x76, 0x5c, 0x1d, 0xfd, 0x76, 0x7c, 0x3f, 0xde, 0x14, 0x7e, 0xf5, + 0x62, 0xf6, 0x7b, 0x02, 0xdc, 0xaa, 0x59, 0x25, 0x77, 0x1f, 0xbb, 0x54, 0x82, 0x4a, 0x26, 0x76, + 0x9f, 0x59, 0xf6, 0xc1, 0x52, 0xe7, 0x8a, 0x0f, 0x57, 0x96, 0x9a, 0x07, 0xb5, 0x25, 0xf7, 0xa8, + 0x89, 0x9d, 0xd2, 0x96, 0x6a, 0x3b, 0xfb, 0x6a, 0xfd, 0xce, 0xfa, 0xbb, 0xd5, 0x23, 0x17, 0x3b, + 0xe8, 0xcc, 0x00, 0x8f, 0xcc, 0x86, 0x07, 0xf1, 0x3a, 0x05, 0xf8, 0xe4, 0xc5, 0xec, 0x8f, 0x85, + 0xfc, 0xfd, 0xc1, 0x50, 0xb4, 0x86, 0xbe, 0xe4, 0xba, 0xe6, 0x62, 0xfd, 0xd9, 0xa2, 0x56, 0x37, + 0x96, 0xb4, 0x96, 0xe3, 0x5a, 0x0d, 0xaa, 0x24, 0xa5, 0xfb, 0xf8, 0x99, 0x07, 0x78, 0xbb, 0xae, + 0xd6, 0x0a, 0x2f, 0x8f, 0xf7, 0x35, 0xec, 0xae, 0xb7, 0x55, 0xcd, 0xa5, 0x98, 0x72, 0x42, 0xc7, + 0x87, 0x15, 0x5d, 0xb7, 0xd1, 0x9b, 0x10, 0x27, 0xf2, 0x23, 0x0d, 0xcf, 0x0b, 0xc5, 0x74, 0x79, + 0xae, 0xd4, 0x69, 0x50, 0x4a, 0xec, 0x58, 0xee, 0xe2, 0x23, 0xa7, 0x9a, 0x3c, 0xae, 0x8e, 0x7c, + 0x5f, 0x88, 0x89, 0x82, 0x4c, 0x59, 0xd0, 0x25, 0x18, 0xab, 0xab, 0x8e, 0xab, 0xec, 0x29, 0x9a, + 0xe9, 0x2a, 0xad, 0xa6, 0x14, 0x9f, 0x17, 0x8a, 0x63, 0x32, 0x90, 0xc1, 0xdb, 0xab, 0xa6, 0xfb, + 0xa8, 0x89, 0x8a, 0x30, 0x4e, 0x49, 0x4c, 0x46, 0xa4, 0x5b, 0xcf, 0x4c, 0x69, 0x84, 0x92, 0x51, + 0xde, 0xfb, 0x84, 0x6e, 0xcd, 0x7a, 0x66, 0xfa, 0x94, 0x6a, 0x98, 0x72, 0x34, 0xa0, 0xac, 0xf8, + 0x94, 0x25, 0x98, 0xa4, 0x94, 0x9a, 0x65, 0xee, 0x85, 0x89, 0x13, 0x94, 0x58, 0x24, 0x73, 0xab, + 0x96, 0xb9, 0xe7, 0xd3, 0xbf, 0x09, 0xe0, 0xb8, 0xaa, 0xed, 0x62, 0x5d, 0x51, 0x5d, 0x29, 0x49, + 0xbf, 0x33, 0x5f, 0xf2, 0x54, 0xb5, 0xc4, 0x55, 0xb5, 0xb4, 0xc3, 0x75, 0x59, 0x4e, 0x31, 0xea, + 0x8a, 0x8b, 0x30, 0x9c, 0xfb, 0x56, 0x0b, 0xb7, 0x08, 0x67, 0xb3, 0x59, 0x37, 0x34, 0x2a, 0xb5, + 0xf4, 0x6d, 0x75, 0xc3, 0x3c, 0x70, 0xa4, 0xd4, 0xfc, 0x70, 0x31, 0x5d, 0x7e, 0xa5, 0x7b, 0xd3, + 0x2a, 0x01, 0xf1, 0x1a, 0xa3, 0x95, 0xf3, 0x1e, 0x50, 0xc4, 0x94, 0x73, 0x33, 0xf9, 0xc9, 0x8b, + 0xd9, 0x78, 0x52, 0x10, 0x85, 0x4d, 0xf2, 0xdf, 0x58, 0x61, 0x05, 0x52, 0x55, 0xcb, 0xaa, 0x3f, + 0x56, 0xeb, 0x2d, 0x8c, 0x26, 0x61, 0xe4, 0x90, 0x3c, 0x48, 0xc2, 0xbc, 0x50, 0x4c, 0xca, 0xde, + 0x1f, 0x37, 0xc5, 0x5f, 0xbd, 0x98, 0x8d, 0x49, 0xc2, 0x27, 0x2f, 0x66, 0x47, 0x09, 0xa3, 0x24, + 0x14, 0x7e, 0x9a, 0x83, 0xb1, 0xad, 0xca, 0xea, 0xb6, 0x6a, 0xab, 0x0d, 0xec, 0x62, 0xdb, 0x41, + 0xb3, 0x90, 0x6c, 0xa8, 0x6d, 0x05, 0x1b, 0x76, 0x93, 0x32, 0xc7, 0xe4, 0x44, 0x43, 0x6d, 0xaf, + 0x1b, 0x76, 0x13, 0x3d, 0x86, 0x09, 0x55, 0xb7, 0x15, 0xa2, 0xd4, 0x8a, 0xad, 0xba, 0x58, 0x31, + 0x4c, 0x1d, 0xb7, 0xe9, 0x01, 0x66, 0xcb, 0xe7, 0xbb, 0xbf, 0x67, 0x4d, 0x75, 0x55, 0x59, 0x75, + 0xf1, 0x06, 0x21, 0xa2, 0x62, 0xf0, 0x3d, 0x2a, 0x06, 0xa2, 0xaa, 0xdb, 0x1d, 0x73, 0xe8, 0x75, + 0x40, 0x04, 0xd7, 0x6d, 0x2b, 0x4d, 0xeb, 0x19, 0xb6, 0x19, 0x2c, 0x3d, 0xf0, 0x6a, 0xe2, 0xb8, + 0x1a, 0xbf, 0x1a, 0x93, 0x72, 0x72, 0x4e, 0xd5, 0xed, 0x9d, 0xf6, 0x36, 0x21, 0xf0, 0xb8, 0xae, + 0x40, 0x86, 0x70, 0x99, 0xbb, 0x8a, 0x6b, 0xab, 0xa6, 0xe3, 0x1d, 0x7b, 0x40, 0x0f, 0xaa, 0x6e, + 0xdf, 0xdf, 0xdd, 0x21, 0x53, 0xe8, 0x32, 0x8c, 0x11, 0x52, 0x55, 0x3b, 0x50, 0xea, 0x46, 0xc3, + 0x70, 0xbd, 0x53, 0xaf, 0xc6, 0x24, 0x41, 0x4e, 0xab, 0xba, 0x5d, 0xd1, 0x0e, 0xee, 0x91, 0xe1, + 0x30, 0x9d, 0x8e, 0xeb, 0xea, 0x11, 0x3d, 0xf7, 0x0e, 0xba, 0x35, 0x32, 0x8c, 0xbe, 0x0a, 0x29, + 0xbb, 0x7d, 0x9d, 0xd1, 0xa4, 0xe8, 0xe7, 0xcf, 0x74, 0x7f, 0xbe, 0xdc, 0xa6, 0xb4, 0xa1, 0x0f, + 0x4f, 0xda, 0xed, 0xeb, 0x1e, 0xff, 0xd7, 0x61, 0x92, 0xf2, 0xfb, 0x1b, 0x69, 0xed, 0xed, 0x39, + 0xd8, 0x95, 0x80, 0x42, 0x5d, 0xe8, 0xb7, 0x93, 0xef, 0x50, 0xaa, 0x10, 0xe2, 0x38, 0x41, 0xec, + 0x98, 0x24, 0x67, 0x64, 0xb7, 0xcb, 0x3d, 0x67, 0x94, 0x3e, 0xe3, 0x19, 0xd9, 0xed, 0x72, 0xe7, + 0x19, 0x95, 0x60, 0x8c, 0xe0, 0xee, 0xd9, 0xf8, 0x5b, 0x2d, 0x6c, 0x6a, 0x47, 0x52, 0x66, 0x5e, + 0x28, 0xc6, 0xab, 0xa9, 0xe3, 0xea, 0x68, 0x39, 0x5e, 0xfc, 0xf1, 0x0f, 0x46, 0xe5, 0x8c, 0xdd, + 0x2e, 0xdf, 0xe6, 0xd3, 0xe8, 0x21, 0x64, 0x89, 0x18, 0xe9, 0x2d, 0xf7, 0x48, 0xd1, 0x8e, 0xb4, + 0x3a, 0x96, 0xc6, 0xe8, 0x12, 0x7a, 0xc5, 0xbe, 0x56, 0xb3, 0x71, 0x4d, 0x75, 0xb1, 0xbe, 0xd6, + 0x72, 0x8f, 0x56, 0x09, 0x69, 0x68, 0x21, 0x99, 0x86, 0xda, 0xf6, 0xc7, 0x91, 0x0e, 0x33, 0x36, + 0xfe, 0xd0, 0x32, 0x4c, 0x85, 0x5c, 0xa2, 0x4a, 0x13, 0xdb, 0x86, 0xa5, 0x1b, 0x9a, 0xe1, 0x1e, + 0x49, 0x59, 0x8a, 0x5e, 0xe8, 0x39, 0x05, 0x4a, 0x4e, 0xd4, 0x74, 0xbd, 0xdd, 0xb4, 0x4c, 0x6c, + 0x86, 0xb7, 0x6f, 0xca, 0xf6, 0x67, 0xb7, 0x03, 0x28, 0x54, 0x03, 0x89, 0xbd, 0x45, 0xb3, 0x5a, + 0xa6, 0xdb, 0xf1, 0x9a, 0x5c, 0xf4, 0x47, 0x78, 0xaf, 0x59, 0x25, 0xe4, 0x11, 0xef, 0x99, 0xb6, + 0x83, 0xe9, 0xf0, 0x8b, 0xde, 0x82, 0x89, 0xa6, 0x61, 0xd6, 0x14, 0xa7, 0x6e, 0xb9, 0xa1, 0x9d, + 0x15, 0xe9, 0xce, 0xa6, 0x8f, 0xab, 0xc9, 0xf2, 0xa8, 0x34, 0x44, 0xf7, 0x76, 0x9c, 0xd0, 0x3d, + 0xac, 0x5b, 0x6e, 0xb0, 0xc1, 0xef, 0xc1, 0x6c, 0xc0, 0xdc, 0x7d, 0xdc, 0xe3, 0x83, 0x1c, 0x37, + 0x11, 0xeb, 0x29, 0x0e, 0xdc, 0x79, 0xda, 0x6f, 0x80, 0xb8, 0x8b, 0x55, 0xcd, 0x32, 0x43, 0xcb, + 0x42, 0xbd, 0xcb, 0xca, 0x79, 0x44, 0xc1, 0xa2, 0xee, 0x42, 0x52, 0xdb, 0x57, 0x4d, 0x13, 0xd7, + 0x1d, 0x69, 0x82, 0x9a, 0xb9, 0x57, 0xbb, 0xd7, 0xd0, 0x61, 0x6d, 0x4a, 0xab, 0x1e, 0x35, 0xdd, + 0xac, 0xe7, 0x42, 0x2c, 0x29, 0xc8, 0x3e, 0x00, 0x5a, 0x87, 0xf1, 0x56, 0x93, 0xd8, 0x3a, 0x45, + 0x7f, 0x86, 0xeb, 0x75, 0x7a, 0xe6, 0xd2, 0x24, 0xb5, 0xc4, 0xb3, 0xdd, 0xa8, 0xbe, 0xe5, 0x93, + 0x73, 0x1e, 0xcf, 0x1a, 0x61, 0x21, 0x27, 0x8b, 0x36, 0x60, 0x82, 0xdb, 0xde, 0x30, 0xd0, 0xd4, + 0x69, 0x40, 0xe3, 0x9c, 0x2b, 0x80, 0x7a, 0x0a, 0xd3, 0x1d, 0x76, 0x44, 0xc1, 0xec, 0xb0, 0xa5, + 0x69, 0x8a, 0x56, 0xec, 0x11, 0xee, 0x35, 0x99, 0x1b, 0x17, 0x2e, 0x17, 0x1e, 0xf8, 0x44, 0xc8, + 0xec, 0xf0, 0x99, 0x30, 0x3c, 0x35, 0x2d, 0x01, 0xfc, 0xcc, 0x49, 0xf0, 0xd4, 0xa6, 0x44, 0xc2, + 0x77, 0xcc, 0xa0, 0x1a, 0x5c, 0xec, 0x2b, 0x31, 0x8a, 0x77, 0x5b, 0x48, 0xf4, 0x3d, 0x85, 0x13, + 0xe5, 0xc6, 0x7b, 0x43, 0x3e, 0x52, 0x70, 0xe8, 0x5c, 0xfe, 0x1f, 0x63, 0x90, 0x60, 0x07, 0x4b, + 0x24, 0x89, 0x1d, 0x62, 0x20, 0x49, 0x42, 0x84, 0x24, 0x79, 0x44, 0x81, 0x24, 0x7d, 0x09, 0x90, + 0x7f, 0x6a, 0x01, 0x67, 0xac, 0xdb, 0xe8, 0xf8, 0x87, 0x14, 0x70, 0x3e, 0x86, 0x89, 0x86, 0x61, + 0xf6, 0xa8, 0xc4, 0xf0, 0x19, 0x2d, 0x60, 0xc3, 0x30, 0x3b, 0x75, 0x82, 0xe0, 0x12, 0x8b, 0xf6, + 0x92, 0xb7, 0x1f, 0x31, 0x68, 0x1d, 0xb8, 0xaf, 0xc0, 0x18, 0x36, 0xd5, 0xdd, 0x3a, 0x56, 0xbc, + 0x3d, 0xa0, 0x17, 0x5f, 0x52, 0xce, 0x78, 0x83, 0x8f, 0xe8, 0x58, 0x70, 0xd9, 0x77, 0x5c, 0xfb, + 0x31, 0x71, 0x78, 0x33, 0x9e, 0x1c, 0x16, 0xe3, 0x85, 0xe3, 0x11, 0x10, 0xd7, 0x4d, 0x7d, 0x8d, + 0x86, 0x79, 0x8f, 0xb1, 0x4d, 0xbd, 0xe2, 0xaf, 0xc1, 0xb0, 0xa1, 0x3b, 0x74, 0xbb, 0xd3, 0xe5, + 0x2f, 0x74, 0xaf, 0xb0, 0x9b, 0x7c, 0x23, 0x88, 0x1a, 0x42, 0x4e, 0x1b, 0x41, 0x40, 0x5b, 0x90, + 0x63, 0x8c, 0xca, 0xa1, 0x47, 0x4c, 0x4f, 0x22, 0x5b, 0xce, 0x47, 0x68, 0x37, 0x83, 0x0b, 0x7d, + 0x73, 0x96, 0x11, 0xf0, 0x75, 0xed, 0xc0, 0x04, 0x87, 0x6b, 0xee, 0x1f, 0xf9, 0x90, 0xc3, 0xd1, + 0x90, 0xdb, 0x77, 0xbe, 0xde, 0x0b, 0x39, 0xce, 0x08, 0xb6, 0xf7, 0x8f, 0x38, 0xea, 0x0a, 0x8c, + 0xfb, 0x82, 0xa2, 0x34, 0xeb, 0xaa, 0xa9, 0x18, 0x3a, 0x3d, 0x9d, 0x14, 0x75, 0x0a, 0xec, 0x98, + 0xf4, 0xb6, 0x9c, 0xf3, 0x29, 0xb6, 0xeb, 0xaa, 0xb9, 0xa1, 0xa3, 0x79, 0x18, 0x6d, 0xee, 0x5b, + 0xae, 0xe5, 0x48, 0x23, 0xf3, 0xc3, 0xc5, 0x14, 0xb7, 0x43, 0x22, 0xc8, 0x6c, 0x1c, 0x15, 0x41, + 0x74, 0x5a, 0xcd, 0xa6, 0x65, 0xbb, 0x8e, 0xa2, 0xd5, 0x55, 0xc7, 0x51, 0x76, 0xa9, 0xab, 0x91, + 0x94, 0xb3, 0x7c, 0x7c, 0x95, 0x0c, 0x57, 0x23, 0x28, 0x35, 0xea, 0x68, 0x74, 0x53, 0xae, 0xa2, + 0x2d, 0x98, 0xd4, 0xf1, 0x9e, 0xda, 0xaa, 0xbb, 0x4a, 0x43, 0xd5, 0x14, 0x07, 0xbb, 0x2e, 0xf1, + 0xd0, 0x99, 0x9b, 0x39, 0x17, 0xb1, 0xa9, 0x0f, 0x19, 0x89, 0x8c, 0x18, 0xe3, 0x96, 0xaa, 0xf1, + 0x31, 0x22, 0x41, 0x44, 0xe2, 0x03, 0x35, 0x21, 0x2e, 0x49, 0x5c, 0xce, 0x34, 0x8c, 0x90, 0x69, + 0x26, 0x44, 0x6a, 0x3b, 0x44, 0x04, 0x8c, 0x48, 0x6d, 0x77, 0x10, 0xf9, 0x9f, 0x40, 0xae, 0x2c, + 0xea, 0x37, 0x24, 0xe5, 0x0c, 0x1f, 0xdc, 0xb4, 0x0c, 0x13, 0x5d, 0x03, 0x64, 0x63, 0x07, 0x33, + 0x12, 0xc5, 0xb4, 0x4c, 0x0d, 0x3b, 0xd4, 0x1f, 0x48, 0xca, 0xa2, 0x37, 0x43, 0xe8, 0xee, 0xd3, + 0x71, 0xa4, 0x02, 0x5f, 0xb2, 0xb2, 0x67, 0xd9, 0x0d, 0xd5, 0x25, 0x76, 0x9f, 0x3a, 0x03, 0x11, + 0x06, 0x6d, 0xcb, 0x8b, 0x2a, 0xb7, 0xd5, 0xa3, 0xba, 0xa5, 0xea, 0xb7, 0x7d, 0xfa, 0x90, 0x40, + 0x8e, 0x33, 0xb4, 0x60, 0xb2, 0x70, 0x9c, 0x80, 0x74, 0x65, 0x4d, 0xf6, 0xf7, 0xe3, 0x6d, 0x18, + 0x75, 0x48, 0xac, 0xa8, 0x31, 0xd1, 0xbf, 0x1c, 0x61, 0x37, 0x39, 0x71, 0xe9, 0x21, 0xa5, 0xdc, + 0xb2, 0x74, 0x7c, 0x67, 0x48, 0x66, 0x7c, 0x68, 0x15, 0x12, 0xfa, 0x91, 0xa9, 0x36, 0x0c, 0x8d, + 0x0a, 0x7a, 0xba, 0xfc, 0xda, 0x49, 0x10, 0x6b, 0x1e, 0x29, 0xc3, 0xe0, 0x9c, 0xe8, 0x36, 0x24, + 0x75, 0xc3, 0x21, 0x4a, 0xac, 0xb3, 0x40, 0xa9, 0x78, 0x22, 0x0a, 0xa3, 0x65, 0x30, 0x3e, 0x6f, + 0xfe, 0x27, 0x02, 0x40, 0xb0, 0x4a, 0xf4, 0x0e, 0xe4, 0xba, 0x6d, 0x90, 0x70, 0x36, 0x1b, 0x34, + 0xa6, 0x77, 0x18, 0xa0, 0x45, 0xc8, 0x76, 0xb9, 0xde, 0xb1, 0x4e, 0x57, 0x3a, 0xe3, 0x86, 0xfd, + 0xee, 0x05, 0x48, 0xfa, 0x3e, 0xf7, 0x30, 0x25, 0x24, 0xf6, 0xf8, 0x6a, 0x5c, 0xca, 0x15, 0x05, + 0x39, 0x61, 0x7a, 0x2e, 0x77, 0x60, 0xa6, 0xf2, 0x7f, 0x1e, 0x87, 0x74, 0x68, 0x87, 0xd0, 0x0a, + 0x8c, 0x36, 0x54, 0xbb, 0x66, 0x98, 0xec, 0x74, 0xe6, 0x7a, 0xa2, 0xaa, 0xdb, 0x75, 0x4b, 0x65, + 0x17, 0x19, 0x23, 0x45, 0x0f, 0xa2, 0x8d, 0x7a, 0x6c, 0xe0, 0xfb, 0xaa, 0xd7, 0x9e, 0x3f, 0x88, + 0xb6, 0xe7, 0xc3, 0x67, 0x80, 0xec, 0x36, 0xe5, 0x32, 0x20, 0xb2, 0xca, 0xae, 0xdd, 0x8c, 0x53, + 0xc4, 0x73, 0x3d, 0x9f, 0xf9, 0x68, 0xc3, 0x74, 0x57, 0xca, 0x14, 0x2b, 0x14, 0xe6, 0x34, 0x0c, + 0xb3, 0x23, 0xcc, 0x21, 0x98, 0x6a, 0x3b, 0x2a, 0x38, 0x3a, 0x03, 0xa6, 0xda, 0xee, 0xc0, 0xdc, + 0x00, 0x62, 0x1b, 0x3a, 0x43, 0xa7, 0xd3, 0xd0, 0xd8, 0x21, 0x0f, 0x17, 0x05, 0x19, 0x1a, 0x86, + 0xc9, 0x43, 0x2b, 0x02, 0xa5, 0xb6, 0x03, 0xa8, 0xc4, 0x59, 0xa1, 0xd4, 0xf6, 0xfd, 0x1e, 0x91, + 0x91, 0x20, 0x13, 0xd6, 0x86, 0xa8, 0xdb, 0xaf, 0x3a, 0x0a, 0xf1, 0x86, 0xa5, 0xe3, 0xc2, 0x3f, + 0x4c, 0x41, 0x3a, 0x64, 0x20, 0x51, 0x05, 0x72, 0xcc, 0x4c, 0x53, 0x3f, 0xcf, 0x6a, 0xb9, 0x4c, + 0xce, 0x66, 0x7b, 0xd6, 0xb4, 0xc6, 0x32, 0x7d, 0xf2, 0x18, 0xe5, 0xa8, 0xee, 0x78, 0xf4, 0xe8, + 0x09, 0x4c, 0x05, 0x8e, 0x52, 0xd8, 0xfb, 0xf7, 0xc4, 0xad, 0xc7, 0xfb, 0xdf, 0x66, 0xae, 0x90, + 0xe7, 0xdb, 0x33, 0x0f, 0xac, 0xd9, 0x31, 0xe8, 0x39, 0xfc, 0xef, 0x9f, 0xe4, 0xb3, 0x0f, 0x2e, + 0x78, 0x7d, 0x9c, 0xf6, 0xfb, 0xd1, 0xe1, 0x84, 0x27, 0x7e, 0x3d, 0x41, 0xa5, 0x6f, 0xf4, 0x99, + 0xb7, 0xdb, 0x1b, 0x61, 0x6c, 0x44, 0x04, 0x01, 0xb3, 0x03, 0x81, 0xf5, 0xc4, 0x05, 0xfe, 0xa1, + 0x68, 0xfe, 0xa1, 0x8c, 0x0c, 0x76, 0x28, 0xab, 0xfc, 0x50, 0xde, 0x0c, 0xc7, 0xdc, 0x5c, 0x60, + 0xa3, 0x63, 0x6e, 0x6f, 0x11, 0x41, 0xb8, 0xbd, 0xd3, 0x27, 0xdc, 0x4e, 0x44, 0x1f, 0x67, 0x67, + 0x44, 0xcd, 0xb6, 0xa7, 0x37, 0xd2, 0x7e, 0x10, 0x1d, 0x69, 0x27, 0x07, 0xb7, 0x1f, 0x3d, 0x41, + 0xf6, 0x6a, 0x77, 0x90, 0x9d, 0x1a, 0x68, 0xbb, 0x3b, 0x23, 0xef, 0xdb, 0x90, 0xdf, 0x53, 0x35, + 0xd7, 0xb2, 0x8f, 0x94, 0x26, 0xbd, 0x8d, 0x7d, 0x3c, 0x03, 0x3b, 0x12, 0xcc, 0x0f, 0x17, 0xe3, + 0xbe, 0x9b, 0xf3, 0x4d, 0x59, 0x62, 0xb4, 0xdb, 0x94, 0xf4, 0x76, 0x40, 0x89, 0xee, 0xf7, 0x44, + 0xf0, 0xe9, 0x3e, 0x97, 0x58, 0x6f, 0x04, 0xcf, 0xd6, 0xd5, 0x11, 0xbc, 0xdf, 0x85, 0x29, 0xdf, + 0xb7, 0x58, 0x29, 0x2b, 0xbb, 0x06, 0xcb, 0x01, 0x52, 0xcf, 0xe1, 0xe4, 0x48, 0x8c, 0xf3, 0xad, + 0x94, 0xab, 0x06, 0x4d, 0x12, 0xa2, 0x37, 0x20, 0xd1, 0x72, 0xb0, 0xa2, 0xea, 0x36, 0x73, 0x25, + 0xfa, 0xb3, 0xd3, 0x38, 0x77, 0xb4, 0xe5, 0xe0, 0x8a, 0x6e, 0xa3, 0xaf, 0x02, 0x90, 0x10, 0x8b, + 0x5d, 0x40, 0xd9, 0x53, 0x2f, 0x20, 0xca, 0x9c, 0x52, 0x75, 0x7b, 0xcb, 0xbb, 0x87, 0xde, 0x82, + 0x0c, 0xf3, 0x7d, 0xbc, 0xb5, 0xe7, 0x4e, 0x5b, 0x3b, 0x78, 0xe4, 0x74, 0xd1, 0x0f, 0x60, 0x86, + 0xf8, 0x17, 0x2d, 0xa7, 0x37, 0x7d, 0x21, 0x9e, 0xa6, 0x0d, 0x53, 0x1e, 0x67, 0x77, 0xae, 0xe2, + 0x31, 0x48, 0x0c, 0xb2, 0x37, 0x57, 0x31, 0x7e, 0xba, 0x29, 0x96, 0xa7, 0x3d, 0xee, 0x9e, 0xd4, + 0xc4, 0x1d, 0x18, 0xd7, 0xb1, 0x63, 0xd8, 0x58, 0x57, 0x02, 0xad, 0x43, 0x03, 0x68, 0x5d, 0x8e, + 0xb1, 0xc9, 0x5c, 0xf9, 0x34, 0x38, 0xd7, 0x81, 0xd4, 0xad, 0x84, 0x13, 0x83, 0x2b, 0xa1, 0x14, + 0xc2, 0xee, 0xd4, 0xc5, 0x6f, 0xc2, 0x5c, 0xf0, 0x92, 0x5e, 0x9d, 0x9c, 0x1c, 0x58, 0x27, 0x67, + 0xfc, 0x57, 0x94, 0xbb, 0xaf, 0xf6, 0xa9, 0xf0, 0x1b, 0x02, 0x15, 0x9d, 0x1a, 0x48, 0x45, 0x27, + 0x02, 0xdc, 0x40, 0x53, 0x9f, 0xc2, 0x34, 0xc7, 0xec, 0xd2, 0xb4, 0xe9, 0x33, 0x6a, 0x1a, 0x87, + 0xdf, 0x0a, 0x2b, 0x5c, 0x1d, 0x2e, 0x70, 0xf8, 0x3e, 0x59, 0x8b, 0x99, 0x33, 0x66, 0x2d, 0xf2, + 0x0c, 0xaf, 0x12, 0x91, 0xbc, 0x88, 0x78, 0x5b, 0x57, 0x12, 0x43, 0x3a, 0x63, 0x12, 0xa3, 0xf3, + 0x6d, 0x9d, 0xb9, 0x8c, 0x03, 0xb8, 0xc4, 0xdf, 0xd6, 0xff, 0x46, 0x9d, 0x1b, 0xf8, 0xd8, 0xb9, + 0x88, 0x6e, 0x47, 0x5e, 0xac, 0x4f, 0x03, 0xe9, 0x8a, 0xba, 0x60, 0xcf, 0x0d, 0x24, 0x01, 0x52, + 0xd7, 0x2b, 0x02, 0x31, 0x78, 0x17, 0xf8, 0x9c, 0xd2, 0x73, 0xdf, 0x9e, 0x1f, 0x08, 0x9b, 0x8b, + 0x51, 0xb5, 0xeb, 0xda, 0xdd, 0x00, 0x31, 0x2c, 0x60, 0x34, 0xa7, 0x7f, 0x81, 0x22, 0x5e, 0xec, + 0xd9, 0x14, 0x9a, 0x0a, 0x58, 0xdf, 0x90, 0xb7, 0x3d, 0xc8, 0x6c, 0x20, 0x51, 0x34, 0xf7, 0xff, + 0x04, 0xe6, 0xb8, 0x5b, 0xa5, 0xf9, 0xb5, 0x0c, 0xc5, 0x30, 0x5d, 0x6c, 0x1f, 0xaa, 0x75, 0xe9, + 0xe2, 0x69, 0xf6, 0x6b, 0xc6, 0x73, 0xb1, 0x56, 0x79, 0xfd, 0x62, 0x83, 0x71, 0x46, 0x67, 0xf9, + 0xe6, 0x3f, 0xad, 0x2c, 0xdf, 0xa5, 0x5f, 0x23, 0xcb, 0xb7, 0x08, 0xc3, 0xe4, 0x5e, 0x29, 0x44, + 0x07, 0xe3, 0xa1, 0x90, 0x4d, 0x26, 0x74, 0xe8, 0x0e, 0x20, 0x47, 0xdb, 0xc7, 0x7a, 0xab, 0x8e, + 0x43, 0x45, 0x9e, 0x57, 0x4e, 0xbf, 0xd4, 0x18, 0x53, 0x44, 0x45, 0xa7, 0xf0, 0xfb, 0x45, 0x48, + 0x12, 0xa7, 0xd6, 0x55, 0x5d, 0x8c, 0x1e, 0x03, 0xd2, 0x5a, 0xb6, 0x8d, 0x89, 0x79, 0xf7, 0x33, + 0xa7, 0xcc, 0xa9, 0x3d, 0x7f, 0x62, 0x7a, 0x35, 0x1c, 0x36, 0x33, 0x88, 0x50, 0xa5, 0xe7, 0x31, + 0x89, 0xcc, 0x99, 0x58, 0x07, 0xb8, 0xb1, 0x33, 0xe2, 0x72, 0xb1, 0x0e, 0x70, 0xab, 0x90, 0xf1, + 0xda, 0x0d, 0xbc, 0x2c, 0x08, 0xcb, 0xeb, 0x4c, 0x75, 0x23, 0x7a, 0x59, 0x93, 0x20, 0x2a, 0x4d, + 0x7b, 0x4c, 0x74, 0x38, 0x2a, 0xe3, 0x14, 0x7f, 0x89, 0x8c, 0xd3, 0x13, 0xc8, 0xfb, 0xd5, 0x3f, + 0xc3, 0x6e, 0x60, 0x3d, 0x10, 0x61, 0x95, 0xbb, 0xa2, 0x27, 0x55, 0xf7, 0x66, 0x78, 0x7d, 0x90, + 0x32, 0xf3, 0x13, 0xab, 0xb8, 0xe8, 0x06, 0x48, 0x14, 0x58, 0xc7, 0x87, 0x0a, 0xbb, 0x88, 0xfd, + 0xc2, 0xa6, 0x57, 0x87, 0x9c, 0x20, 0xf3, 0x6b, 0xf8, 0xf0, 0x21, 0x9d, 0x65, 0x15, 0xce, 0xbe, + 0x11, 0x46, 0xe2, 0x25, 0x23, 0x0c, 0x0c, 0xe7, 0x9a, 0xd8, 0xd4, 0x09, 0x76, 0x54, 0xf1, 0x91, + 0x79, 0xa7, 0x83, 0xd5, 0x1e, 0x19, 0x50, 0xc4, 0x1c, 0x5a, 0x07, 0x91, 0x95, 0x38, 0x6d, 0xec, + 0x34, 0x2d, 0xd3, 0xc1, 0xbc, 0xac, 0x19, 0x75, 0x3e, 0xab, 0x56, 0xa3, 0xa1, 0x9a, 0xba, 0x9c, + 0xf3, 0x78, 0x64, 0xce, 0x42, 0x60, 0xf8, 0x6a, 0xa9, 0xcd, 0x72, 0x5c, 0xcf, 0x41, 0x3d, 0x05, + 0x86, 0xf1, 0xc8, 0x8c, 0x05, 0x3d, 0x00, 0xc4, 0x56, 0x43, 0x13, 0x52, 0xaa, 0xa6, 0xe1, 0xa6, + 0xcb, 0xbc, 0xd5, 0x57, 0xa2, 0x92, 0x69, 0x44, 0xad, 0x4a, 0x9b, 0x96, 0x61, 0x56, 0x28, 0xa9, + 0xcc, 0x3e, 0x26, 0x18, 0x41, 0x8f, 0x61, 0x92, 0xaf, 0x8c, 0x62, 0xb2, 0xe5, 0x31, 0x5f, 0x75, + 0xe1, 0x44, 0x50, 0xb6, 0x2e, 0x19, 0x31, 0x84, 0xd0, 0x18, 0x5a, 0x26, 0xa1, 0x88, 0xf2, 0xcc, + 0x30, 0x75, 0xeb, 0x99, 0xa3, 0xa8, 0x87, 0xaa, 0x51, 0x27, 0x61, 0x2e, 0x75, 0x62, 0x93, 0x32, + 0xb2, 0xdb, 0x4f, 0xbc, 0xa9, 0x0a, 0x9f, 0x41, 0x5b, 0x90, 0xb5, 0xb1, 0x86, 0xa9, 0x48, 0x79, + 0xa6, 0x25, 0x4b, 0x77, 0xe8, 0x72, 0xdf, 0x35, 0x78, 0x29, 0x63, 0x96, 0x4a, 0x93, 0xc7, 0x3c, + 0x6e, 0x6f, 0xd0, 0x41, 0x0f, 0x41, 0x64, 0x70, 0x81, 0xad, 0xca, 0x51, 0xc0, 0x62, 0x5f, 0x40, + 0x7e, 0xec, 0x1c, 0x32, 0xe7, 0x21, 0xf8, 0x86, 0x0b, 0xed, 0x41, 0xc1, 0x2b, 0xd8, 0x7b, 0x1d, + 0x06, 0x8a, 0x61, 0x1a, 0xae, 0x41, 0xdc, 0x94, 0x0e, 0x35, 0x13, 0x4f, 0x55, 0xb3, 0x0b, 0xb4, + 0xba, 0xef, 0x81, 0x6c, 0x70, 0x8c, 0x90, 0xb6, 0xd9, 0x70, 0xc1, 0xc6, 0x1f, 0x62, 0xcd, 0x65, + 0x4e, 0x46, 0xd7, 0x85, 0x8f, 0x1d, 0x69, 0x7c, 0x7e, 0xf8, 0xf4, 0x4c, 0x58, 0xee, 0xb8, 0x9a, + 0x79, 0x2e, 0xa4, 0xc4, 0x5c, 0x81, 0x99, 0x8b, 0x3c, 0x47, 0xad, 0x74, 0x95, 0xa6, 0xb1, 0x83, + 0xb6, 0xe1, 0x7c, 0xc7, 0x3b, 0x3b, 0x13, 0x31, 0xd8, 0x91, 0xd0, 0xfc, 0x70, 0x71, 0xac, 0x9a, + 0x3d, 0xae, 0xa6, 0x9f, 0x0b, 0x49, 0x31, 0x57, 0xf0, 0x72, 0x2e, 0xb3, 0x21, 0xc8, 0x70, 0xee, + 0x05, 0x3b, 0xa8, 0x02, 0x93, 0x3e, 0x62, 0x38, 0x34, 0x9b, 0xa0, 0xa1, 0x19, 0x03, 0x2a, 0xf0, + 0x0a, 0xc7, 0x04, 0xa7, 0x0d, 0xc7, 0x66, 0x6b, 0x20, 0x7a, 0x66, 0x27, 0xb4, 0xbd, 0x93, 0xa7, + 0x6e, 0x6f, 0x96, 0x9a, 0xa2, 0x60, 0x3b, 0x2d, 0xf0, 0x57, 0x19, 0xda, 0x4a, 0x5b, 0x35, 0x6b, + 0xd8, 0x91, 0xa6, 0xa8, 0x50, 0xbc, 0xde, 0x57, 0x28, 0x64, 0xc6, 0xc9, 0xf7, 0x4b, 0xa6, 0x6c, + 0xeb, 0xa6, 0x6b, 0x1f, 0xd1, 0x82, 0x67, 0xc4, 0xa4, 0x6f, 0x2d, 0xc9, 0x3e, 0x6a, 0xfb, 0x64, + 0x2c, 0xb0, 0x96, 0xd3, 0x81, 0xb5, 0xac, 0xe8, 0xf6, 0x2a, 0x9d, 0x65, 0xd6, 0xd2, 0x20, 0xc7, + 0x4e, 0x65, 0xb6, 0xa1, 0x6a, 0x8a, 0xe6, 0x99, 0x01, 0x25, 0xd4, 0xe5, 0x24, 0xcd, 0xd0, 0x63, + 0x5f, 0xe8, 0x6f, 0x34, 0x82, 0xe2, 0x86, 0x3c, 0xe7, 0x61, 0x6d, 0xa9, 0x5a, 0xcf, 0x9c, 0x93, + 0xff, 0x5f, 0x31, 0x48, 0x87, 0xf5, 0xf5, 0x01, 0xf8, 0x0e, 0x42, 0x90, 0xa6, 0x1f, 0x65, 0x3b, + 0xdd, 0x2d, 0x64, 0xf7, 0xb8, 0x63, 0x10, 0xba, 0x1f, 0x45, 0xce, 0xee, 0x27, 0xa8, 0xbe, 0x0c, + 0x49, 0xbb, 0xcd, 0x22, 0xaa, 0xc4, 0xa0, 0xbd, 0x03, 0x09, 0xdb, 0x1b, 0x42, 0x4b, 0x90, 0xd0, + 0xf6, 0x94, 0xba, 0xe1, 0xf0, 0xa6, 0x94, 0xe9, 0x9e, 0x7b, 0xf5, 0xf6, 0x3d, 0xc3, 0x71, 0xe5, + 0x51, 0x6d, 0x8f, 0xfc, 0xbf, 0xbb, 0x4d, 0x24, 0x5c, 0x35, 0xda, 0x8c, 0x27, 0xe3, 0xe2, 0xc8, + 0x66, 0x3c, 0x39, 0x22, 0x8e, 0x6e, 0xc6, 0x93, 0x29, 0x11, 0x36, 0xe3, 0x49, 0x10, 0xd3, 0xf9, + 0x5f, 0x26, 0x00, 0x42, 0xc6, 0xf0, 0x15, 0x48, 0x34, 0xbd, 0x8c, 0x3c, 0xf5, 0x3a, 0x32, 0x34, + 0x81, 0xf7, 0xed, 0xb8, 0x38, 0x2e, 0x5d, 0x92, 0xf9, 0x0c, 0xba, 0x03, 0x09, 0x6e, 0x24, 0x63, + 0x83, 0x1b, 0xc9, 0xd0, 0x4e, 0x71, 0xf6, 0x97, 0x69, 0x2e, 0xba, 0x01, 0x39, 0xcd, 0xb2, 0x6d, + 0x5c, 0xf7, 0xae, 0x3d, 0x43, 0x77, 0xa4, 0x38, 0xad, 0xeb, 0x64, 0x8e, 0xab, 0xa9, 0xe7, 0xc2, + 0x68, 0x21, 0x6e, 0xc7, 0x24, 0x5d, 0xce, 0x86, 0x88, 0x36, 0x74, 0xa7, 0xb3, 0x7d, 0x6c, 0xe4, + 0x77, 0xed, 0x63, 0x9f, 0x75, 0xfb, 0xd8, 0xbf, 0xc6, 0x60, 0xd4, 0xc4, 0xae, 0x62, 0xe8, 0x54, + 0x97, 0x32, 0xd5, 0xbf, 0x8f, 0x3d, 0xaf, 0xcc, 0x6c, 0x26, 0x0b, 0xcb, 0xcb, 0xcb, 0xcb, 0xd7, + 0x57, 0xa2, 0xf6, 0x7a, 0xf8, 0x73, 0xb0, 0xd7, 0x2b, 0x9f, 0xf6, 0x5e, 0xaf, 0x7c, 0x76, 0x7b, + 0x3d, 0x62, 0x62, 0x77, 0x43, 0x0f, 0xe5, 0xd4, 0xff, 0x33, 0x01, 0x63, 0x1d, 0x9e, 0x01, 0x7a, + 0x2b, 0x50, 0x6b, 0x4f, 0x63, 0x67, 0xfa, 0x94, 0xe3, 0xc2, 0x4a, 0xca, 0xd5, 0xfd, 0x09, 0x24, + 0x7d, 0x7b, 0xe8, 0x65, 0x98, 0xaf, 0x0f, 0xe6, 0x90, 0x94, 0x76, 0xda, 0x11, 0x66, 0xd2, 0x07, + 0x43, 0x32, 0xa4, 0xed, 0xb6, 0xc2, 0x9b, 0x47, 0x69, 0x59, 0x76, 0x70, 0x6c, 0xb9, 0xbd, 0xc5, + 0x18, 0x65, 0xb0, 0xfd, 0x67, 0xf4, 0x16, 0xa4, 0x89, 0xd1, 0x37, 0x0e, 0xbd, 0x6e, 0xbe, 0xd1, + 0x53, 0x6f, 0x4a, 0xe0, 0xe4, 0xd4, 0xc5, 0xef, 0xb1, 0x29, 0x89, 0x01, 0x6c, 0xca, 0x4d, 0x98, + 0xe4, 0x51, 0x90, 0xd7, 0x0a, 0xc1, 0x92, 0x12, 0x29, 0x5a, 0x32, 0x23, 0x5f, 0x7e, 0x75, 0x58, + 0xfa, 0x6f, 0x41, 0x46, 0x2c, 0xec, 0xf1, 0x88, 0xa8, 0x8f, 0x90, 0x7f, 0x08, 0x10, 0xec, 0x12, + 0xba, 0x05, 0x29, 0xff, 0x76, 0x66, 0x61, 0x9f, 0xd4, 0xcf, 0xc1, 0x09, 0x6f, 0x29, 0xaf, 0xf2, + 0x51, 0x93, 0x0f, 0xf9, 0x7f, 0x1e, 0x06, 0x08, 0xf6, 0x07, 0x6d, 0x41, 0xba, 0xa6, 0xba, 0xf8, + 0x99, 0x7a, 0xa4, 0x04, 0x4d, 0x02, 0x3d, 0xb9, 0x92, 0xaf, 0x79, 0x24, 0xd1, 0xbd, 0x01, 0x50, + 0xe3, 0xb3, 0x0e, 0xba, 0x04, 0x19, 0xfe, 0x9d, 0xb6, 0xe3, 0x18, 0xf4, 0x33, 0x63, 0x72, 0x9a, + 0x8d, 0xc9, 0x8e, 0x63, 0x20, 0x11, 0x86, 0x1d, 0xd3, 0xa6, 0x7e, 0x79, 0x4c, 0x26, 0x8f, 0xe8, + 0x43, 0x90, 0xfc, 0xdb, 0xb5, 0xa9, 0xba, 0xfb, 0x24, 0x3e, 0x73, 0x5c, 0x5b, 0x35, 0x4c, 0x97, + 0x35, 0x74, 0xf5, 0x78, 0xb9, 0xdc, 0x7d, 0xd9, 0x56, 0xdd, 0xfd, 0x55, 0x9f, 0x3a, 0xdc, 0x6c, + 0xa5, 0x47, 0x52, 0x90, 0x05, 0xb2, 0x3c, 0x83, 0x6b, 0x1d, 0x60, 0x93, 0x66, 0x6e, 0x33, 0x72, + 0xda, 0x1b, 0xdb, 0x21, 0x43, 0xe8, 0x00, 0xc6, 0x9a, 0xaa, 0x76, 0x80, 0x5d, 0x65, 0xd7, 0xb6, + 0x0e, 0xb0, 0xcd, 0x12, 0x9e, 0xb7, 0xcf, 0x2c, 0x7c, 0xa5, 0x6d, 0x0a, 0x53, 0xa5, 0x28, 0xbe, + 0x44, 0x66, 0x9a, 0xa1, 0xd1, 0xfc, 0x39, 0x98, 0x8c, 0xa2, 0xa2, 0xb7, 0x74, 0x9a, 0x1e, 0x59, + 0xca, 0xbb, 0x8d, 0x37, 0xe3, 0xc9, 0x8c, 0x38, 0xb6, 0x19, 0x4f, 0x8e, 0x89, 0xd9, 0xcd, 0x78, + 0x52, 0x14, 0xc7, 0x37, 0xe3, 0xc9, 0x71, 0x11, 0x6d, 0xc6, 0x93, 0x13, 0xe2, 0xe4, 0x66, 0x3c, + 0x39, 0x29, 0x4e, 0x6d, 0xc6, 0x93, 0x9a, 0xa8, 0xfb, 0x37, 0xbc, 0x77, 0xb7, 0x27, 0x39, 0x42, + 0xfe, 0x87, 0x71, 0xc8, 0x75, 0x39, 0xf1, 0x68, 0xb3, 0x5b, 0xfb, 0x97, 0x07, 0xf5, 0xff, 0xb9, + 0x59, 0x08, 0x8c, 0x41, 0x84, 0x8a, 0x8c, 0x9e, 0xae, 0x22, 0xf9, 0xff, 0x88, 0x41, 0x82, 0x2f, + 0x67, 0x07, 0x46, 0x1a, 0xca, 0xbe, 0x6e, 0x33, 0x41, 0xbc, 0x71, 0xd6, 0xc5, 0x94, 0xb6, 0xee, + 0xac, 0xc9, 0x61, 0x7f, 0xa0, 0x71, 0x47, 0xb7, 0xd1, 0xfb, 0x90, 0x26, 0x2e, 0x23, 0xff, 0x50, + 0xcf, 0xa3, 0x78, 0xeb, 0xec, 0xd8, 0x95, 0x55, 0xd6, 0x92, 0x20, 0x43, 0x43, 0xd5, 0xd8, 0x73, + 0x7e, 0x0d, 0xe2, 0xe4, 0xad, 0xe8, 0x0d, 0x18, 0x6d, 0x28, 0xe4, 0xae, 0x60, 0x85, 0xf8, 0x9e, + 0x54, 0xc7, 0xd6, 0xce, 0x51, 0x33, 0xdc, 0xd5, 0x38, 0xd2, 0x20, 0x03, 0x9e, 0x2b, 0x96, 0x7f, + 0x0a, 0x10, 0xe0, 0xa3, 0x8b, 0x30, 0xba, 0xa7, 0x34, 0x2d, 0xdb, 0x65, 0x45, 0xf8, 0xc0, 0x50, + 0x8c, 0xec, 0x6d, 0x5b, 0xb6, 0x8b, 0xce, 0x01, 0xec, 0xb5, 0xea, 0x75, 0x56, 0x7f, 0xf0, 0xba, + 0xa2, 0x93, 0x64, 0x84, 0x38, 0xcb, 0x5d, 0x12, 0xe0, 0x79, 0x77, 0x9e, 0xbf, 0x17, 0x17, 0x93, + 0xa1, 0x59, 0xe2, 0xef, 0x25, 0xc4, 0x64, 0xfe, 0x6f, 0x04, 0x18, 0xeb, 0x70, 0xd2, 0xfb, 0xb5, + 0x4f, 0x09, 0x9f, 0x51, 0xfb, 0x54, 0xec, 0x25, 0xdb, 0xa7, 0x42, 0x37, 0x9c, 0x0e, 0xd9, 0xae, + 0x78, 0xe3, 0x0e, 0x8c, 0xb2, 0x68, 0x46, 0x38, 0x25, 0x66, 0xee, 0x60, 0x0c, 0x75, 0x23, 0x32, + 0xfe, 0xd0, 0x5b, 0x6c, 0x98, 0x3b, 0x21, 0xf4, 0x21, 0x46, 0xee, 0x00, 0xb3, 0x16, 0x37, 0x99, + 0x3c, 0xa2, 0xaf, 0xf0, 0x56, 0xec, 0x3e, 0x9d, 0x24, 0xd1, 0x6b, 0x70, 0x78, 0xcf, 0x76, 0xec, + 0x4b, 0xa1, 0xaa, 0x77, 0xe1, 0xe7, 0x02, 0xcc, 0xf9, 0x8d, 0x5b, 0x95, 0x96, 0xbb, 0x4f, 0xec, + 0xb2, 0x97, 0x9c, 0x59, 0xb5, 0x74, 0x8c, 0x96, 0xc3, 0x7d, 0xdf, 0xa9, 0x6a, 0xfe, 0xb8, 0x3a, + 0x63, 0x4f, 0x95, 0x27, 0x3e, 0x78, 0x4f, 0x5d, 0xfc, 0x76, 0x65, 0xf1, 0x1b, 0xcb, 0x8b, 0x6f, + 0x3e, 0xfd, 0xe8, 0xfa, 0xb5, 0x95, 0xf2, 0xc7, 0x0b, 0x0c, 0x1f, 0xbd, 0x09, 0x40, 0x7f, 0xcb, + 0xa3, 0xec, 0xd9, 0x56, 0x83, 0xad, 0xf1, 0xc4, 0x46, 0x77, 0x4a, 0x7d, 0xdb, 0xb6, 0x1a, 0xe8, + 0x06, 0x24, 0x3d, 0x56, 0xd7, 0x62, 0xaa, 0x75, 0x12, 0x63, 0x82, 0xd2, 0xee, 0x58, 0xa1, 0xaf, + 0xf9, 0xc3, 0x79, 0x48, 0xf9, 0x5f, 0x83, 0xee, 0x84, 0xdb, 0xd5, 0x16, 0xfa, 0xb6, 0xab, 0x85, + 0xef, 0x22, 0x91, 0xeb, 0x3b, 0x85, 0x1c, 0x2a, 0xb2, 0x7e, 0xb5, 0x55, 0x00, 0xcd, 0xc6, 0x2a, + 0x6b, 0xde, 0x3f, 0xf5, 0x9b, 0xaa, 0xde, 0x8a, 0x86, 0xc4, 0x21, 0x39, 0xc5, 0xf8, 0x2a, 0x2e, + 0x01, 0x69, 0x35, 0x75, 0x0e, 0x32, 0x7c, 0x16, 0x10, 0xc6, 0x57, 0x71, 0xd1, 0x1c, 0xc4, 0x4d, + 0xb5, 0x81, 0x3b, 0xfb, 0xd0, 0xca, 0x32, 0x1d, 0x44, 0x57, 0x21, 0xad, 0x63, 0x47, 0xb3, 0x0d, + 0xfa, 0x1b, 0x17, 0xaa, 0xcb, 0x5e, 0x07, 0x9a, 0x3d, 0x2c, 0xfd, 0x22, 0x27, 0x87, 0x27, 0xd1, + 0x77, 0x05, 0x00, 0xd5, 0x75, 0x6d, 0x63, 0xb7, 0xe5, 0x62, 0xcf, 0xbc, 0xa6, 0xcb, 0x57, 0xfa, + 0x6e, 0x52, 0xa9, 0xe2, 0xd3, 0x52, 0xb9, 0xac, 0xde, 0x38, 0xae, 0x96, 0x7f, 0x24, 0x2c, 0x89, + 0x50, 0x58, 0xb0, 0x0b, 0xd2, 0x42, 0xf9, 0x02, 0x15, 0x09, 0x22, 0x0f, 0xc5, 0x5b, 0x37, 0xdf, + 0x5b, 0x7c, 0x7a, 0x8b, 0xff, 0x79, 0xe5, 0xa3, 0xf2, 0xb5, 0x8f, 0x17, 0xae, 0x92, 0x35, 0xfc, + 0x4c, 0x90, 0x43, 0xef, 0x44, 0xf7, 0x20, 0xcd, 0x72, 0xb1, 0xcc, 0x09, 0x3a, 0x6b, 0x5b, 0xa1, + 0x0c, 0x87, 0x7c, 0x8c, 0x84, 0x6a, 0xc8, 0xc1, 0x36, 0x75, 0x90, 0x9a, 0xb6, 0xb5, 0x67, 0xd4, + 0x31, 0x09, 0x07, 0x92, 0x9d, 0xfd, 0x7a, 0x22, 0x23, 0xd9, 0xf6, 0x28, 0x36, 0x74, 0xf4, 0x53, + 0x01, 0xa6, 0x79, 0x9a, 0x89, 0x4c, 0x62, 0x9b, 0x46, 0x6d, 0xd8, 0x71, 0xa8, 0xcb, 0x91, 0xaa, + 0xfe, 0x7f, 0xe1, 0xb8, 0xfa, 0x7d, 0xc1, 0xfe, 0xbf, 0x42, 0xf9, 0x7f, 0x0b, 0x1f, 0x14, 0x6f, + 0xdd, 0x24, 0x9f, 0x17, 0x08, 0xff, 0x77, 0x42, 0xcf, 0xc1, 0xe3, 0xfb, 0x8b, 0x4f, 0xaf, 0x86, + 0x26, 0xae, 0xbc, 0x5f, 0xba, 0x72, 0x95, 0xf0, 0x55, 0x16, 0xbf, 0xc1, 0x76, 0xe5, 0x3b, 0xa1, + 0xe7, 0xe0, 0x91, 0xf2, 0x05, 0x13, 0x57, 0x8a, 0xb7, 0x6e, 0xde, 0x7c, 0x8f, 0xe9, 0xd8, 0x8d, + 0x8f, 0xaf, 0xdc, 0x5a, 0xf8, 0xce, 0x07, 0x0b, 0xf2, 0x24, 0x5b, 0xee, 0x43, 0xba, 0xda, 0x8a, + 0xb7, 0x58, 0x54, 0x01, 0xa9, 0xeb, 0x33, 0x0e, 0xf0, 0x81, 0x52, 0x57, 0x77, 0x71, 0x5d, 0x5a, + 0x0a, 0x0b, 0xc2, 0x77, 0x45, 0x79, 0xaa, 0x03, 0xe1, 0x2e, 0x3e, 0xb8, 0x47, 0xc8, 0xd0, 0xdf, + 0x0a, 0x90, 0x0f, 0x27, 0x79, 0xbb, 0xb6, 0x03, 0x3e, 0x9f, 0xdb, 0x21, 0x85, 0x96, 0xdc, 0xb9, + 0x25, 0x1b, 0x70, 0x2e, 0xe2, 0x73, 0x82, 0x6d, 0x59, 0xee, 0xda, 0x96, 0xd9, 0x1e, 0x24, 0x7f, + 0x6b, 0xde, 0x82, 0xa9, 0x08, 0x28, 0x43, 0x97, 0xae, 0x87, 0xe5, 0x4b, 0x97, 0x27, 0x7a, 0x20, + 0x36, 0x74, 0xf4, 0x57, 0x02, 0x4c, 0xd0, 0xa4, 0x6f, 0xd7, 0x86, 0xa6, 0x3f, 0x9f, 0x1b, 0x3a, + 0x4e, 0xd6, 0xda, 0xb9, 0x93, 0x2e, 0xa4, 0xea, 0x96, 0xf7, 0x55, 0x8e, 0x94, 0x89, 0xce, 0xee, + 0x06, 0xa6, 0xe2, 0x1e, 0x27, 0xf5, 0x2c, 0xc5, 0xb5, 0xe3, 0xea, 0x95, 0x1f, 0x09, 0x97, 0x07, + 0xb3, 0x13, 0x72, 0xf0, 0x22, 0x74, 0x1d, 0x12, 0xec, 0x17, 0x7d, 0x52, 0x39, 0x3a, 0x9e, 0xdc, + 0xf6, 0xa6, 0x65, 0x4e, 0x17, 0xd9, 0x5c, 0x3b, 0x36, 0x70, 0x73, 0x6d, 0xb6, 0x4f, 0x73, 0x6d, + 0x4f, 0xe9, 0x28, 0xf7, 0xe9, 0x37, 0x2b, 0x8b, 0x9f, 0x41, 0xb3, 0xf2, 0xf8, 0x29, 0xcd, 0xca, + 0x3d, 0x7d, 0xbe, 0x68, 0x90, 0x3e, 0xdf, 0x89, 0x41, 0xfa, 0x7c, 0x27, 0x07, 0xee, 0xf3, 0x9d, + 0xea, 0xd3, 0xe7, 0x7b, 0x03, 0x52, 0xb6, 0x65, 0xb9, 0x0a, 0x4d, 0xdd, 0x4d, 0x47, 0x47, 0xa9, + 0xb2, 0x65, 0xb9, 0x77, 0xf1, 0x91, 0x23, 0x27, 0x6d, 0xf6, 0x14, 0x4e, 0x05, 0xcd, 0xfc, 0x2e, + 0x15, 0xf4, 0x19, 0xa6, 0x82, 0xd0, 0x57, 0x21, 0xd3, 0xd1, 0x6c, 0x2e, 0x9d, 0xde, 0x6c, 0x4e, + 0x82, 0x27, 0x3f, 0x0d, 0x71, 0x03, 0x52, 0x94, 0x9f, 0xf8, 0xaa, 0xac, 0x0f, 0x50, 0xea, 0xe7, + 0xcb, 0xca, 0x49, 0xc2, 0x49, 0xab, 0xd7, 0x6b, 0x30, 0xce, 0x2b, 0x69, 0x01, 0xfb, 0xb5, 0x53, + 0xd8, 0x79, 0x89, 0x6f, 0x8b, 0xa3, 0x5c, 0x87, 0x84, 0xe3, 0xe5, 0x7d, 0xa5, 0x7c, 0xb4, 0x6d, + 0x61, 0x69, 0x61, 0x99, 0xd3, 0xa1, 0xb7, 0x81, 0xa3, 0x28, 0x9c, 0x75, 0xee, 0x64, 0xd6, 0x2c, + 0xa3, 0xe7, 0xbf, 0x2a, 0x5e, 0x80, 0xac, 0x5f, 0xdc, 0xa5, 0xd2, 0x4f, 0x5b, 0x3d, 0xc6, 0xe4, + 0x0c, 0x2b, 0xe9, 0x52, 0xc9, 0x47, 0x97, 0x21, 0xd7, 0x72, 0xb0, 0x1e, 0x50, 0x39, 0xd2, 0xf9, + 0xf9, 0xe1, 0xe2, 0x98, 0x3c, 0x46, 0x86, 0x39, 0x99, 0x43, 0xe8, 0x28, 0x5a, 0xa0, 0x4c, 0xb4, + 0x17, 0x83, 0xfd, 0x52, 0xd5, 0xd7, 0x24, 0xf4, 0x2a, 0xa3, 0xb3, 0x3f, 0x64, 0x3d, 0x5d, 0xcb, + 0xb4, 0xbb, 0x82, 0xbd, 0x56, 0xfe, 0x90, 0xf6, 0x6a, 0x2d, 0xf7, 0x92, 0x5d, 0xa7, 0x5d, 0x13, + 0x9d, 0x64, 0xd7, 0xd1, 0xbb, 0x30, 0xd7, 0x5d, 0xa0, 0x0e, 0xa7, 0xc2, 0x2e, 0x0d, 0x56, 0xfa, + 0xf6, 0xeb, 0xd7, 0x72, 0x90, 0x17, 0xbb, 0x0b, 0x69, 0xaf, 0x10, 0xe6, 0x1d, 0x69, 0xa1, 0x8f, + 0x41, 0x24, 0x24, 0xf4, 0x0c, 0xab, 0x59, 0x6e, 0x10, 0x3f, 0x79, 0x31, 0x1b, 0x13, 0x87, 0x64, + 0x68, 0xfa, 0x73, 0xe8, 0x3d, 0x40, 0xbb, 0xb4, 0x9b, 0xff, 0x48, 0x69, 0x62, 0x5b, 0xc3, 0xa6, + 0xab, 0xd6, 0x30, 0x6b, 0xa2, 0x38, 0xb1, 0x3f, 0x2f, 0x77, 0x5c, 0xcd, 0x00, 0x9c, 0x1f, 0x1a, + 0xfa, 0xee, 0xad, 0xc5, 0xa1, 0xa1, 0xa1, 0x21, 0x79, 0x9c, 0xe1, 0x6c, 0xfb, 0x30, 0xe8, 0x35, + 0xc8, 0xf9, 0x69, 0x26, 0xd6, 0xf9, 0xb7, 0x30, 0x2f, 0x14, 0x47, 0xe4, 0x2c, 0x1f, 0x66, 0xdd, + 0x7d, 0xa7, 0xfd, 0x72, 0xb7, 0xf8, 0xa9, 0xfc, 0x72, 0x17, 0xdd, 0x01, 0x08, 0xfd, 0x14, 0xe2, + 0xca, 0xd9, 0x7e, 0x0a, 0x21, 0x87, 0x78, 0xd1, 0x03, 0xc8, 0x36, 0x6d, 0xeb, 0xd0, 0x20, 0xe2, + 0xea, 0x39, 0x38, 0x57, 0xe9, 0x1d, 0x72, 0xf5, 0xb8, 0xfa, 0x9a, 0xfd, 0xaa, 0xb4, 0x50, 0xbe, + 0x74, 0xf2, 0xf5, 0x4d, 0xfc, 0x87, 0xb1, 0x10, 0xc2, 0x86, 0x4e, 0xf5, 0x95, 0x0f, 0x10, 0xdd, + 0xa1, 0x59, 0xd8, 0x2f, 0x30, 0xc5, 0xe9, 0x3e, 0x88, 0x87, 0xf4, 0x5f, 0x3a, 0x90, 0xc5, 0x30, + 0x07, 0x09, 0x63, 0xd1, 0x39, 0x48, 0x35, 0x5a, 0x75, 0x12, 0x9a, 0x3a, 0xae, 0xb4, 0x48, 0xaf, + 0x8c, 0x60, 0x00, 0xd5, 0x60, 0x56, 0xab, 0xab, 0x46, 0x43, 0x51, 0x3b, 0x22, 0x58, 0x45, 0xb3, + 0x74, 0x2c, 0x95, 0x4e, 0x89, 0x2b, 0x7a, 0xa3, 0x5e, 0xda, 0x5c, 0x64, 0x34, 0x22, 0xc2, 0xe1, + 0x12, 0x4c, 0x38, 0x07, 0x46, 0x93, 0x27, 0x80, 0x14, 0xcd, 0x3e, 0x6a, 0xba, 0x96, 0xb4, 0x42, + 0x17, 0x34, 0x4e, 0xa6, 0xd8, 0xfe, 0xae, 0xd2, 0x09, 0xf4, 0x1e, 0x9c, 0x8b, 0xa0, 0x57, 0xac, + 0x43, 0x6c, 0xdb, 0x86, 0x8e, 0xa5, 0xd7, 0xfb, 0xa8, 0x4b, 0xd0, 0xd6, 0x33, 0xdb, 0x03, 0xfa, + 0x0e, 0x63, 0x46, 0x5f, 0x81, 0x8c, 0xaa, 0xb9, 0xc6, 0x21, 0x0f, 0x29, 0x6f, 0x9c, 0xaa, 0x7b, + 0x69, 0x9f, 0xbe, 0xe2, 0xa2, 0x2f, 0x03, 0xd5, 0x6c, 0xc5, 0xc1, 0xd8, 0x24, 0xec, 0x6f, 0x9c, + 0x9e, 0xc5, 0x26, 0xf4, 0x0f, 0x31, 0x36, 0x2b, 0x6e, 0xfe, 0x2b, 0x90, 0xeb, 0x0a, 0x09, 0xc3, + 0xa9, 0x8a, 0x94, 0x97, 0xaa, 0x98, 0x0c, 0xa7, 0x2a, 0x52, 0xa1, 0x0c, 0x44, 0xfe, 0x31, 0x64, + 0x3b, 0xdd, 0xc4, 0x08, 0xee, 0x52, 0x67, 0xa2, 0xa3, 0xc7, 0xba, 0x73, 0x80, 0xc8, 0xcc, 0xc6, + 0x66, 0x3c, 0xf9, 0xaa, 0x78, 0x79, 0x33, 0x9e, 0xbc, 0x2c, 0xbe, 0xb6, 0x19, 0x4f, 0xbe, 0x26, + 0x16, 0x0b, 0x77, 0x00, 0xfc, 0x43, 0x77, 0xd0, 0x4d, 0x48, 0x07, 0xff, 0x90, 0x09, 0x4f, 0xe8, + 0xcc, 0xf6, 0x95, 0x12, 0x19, 0xb0, 0xcf, 0x5b, 0xf8, 0x44, 0x80, 0xb1, 0x35, 0xaf, 0xf6, 0xb4, + 0x6d, 0xe3, 0x3d, 0xa3, 0x8d, 0x7e, 0x26, 0x84, 0x2a, 0x7e, 0x5e, 0x51, 0xf3, 0x4f, 0x85, 0xdf, + 0xa6, 0x8a, 0x5f, 0x50, 0x4c, 0x9b, 0x86, 0xd1, 0x3a, 0x36, 0x6b, 0xee, 0xbe, 0x97, 0x31, 0x94, + 0xd9, 0x5f, 0x85, 0xf7, 0x61, 0x7a, 0x95, 0x26, 0x33, 0x82, 0x3d, 0x61, 0x05, 0xd6, 0x2a, 0x40, + 0xb0, 0x95, 0xfe, 0xaf, 0x23, 0xfa, 0xed, 0x64, 0x28, 0xa9, 0x9a, 0xf2, 0xf7, 0xb4, 0xf0, 0x43, + 0x01, 0xa6, 0x1f, 0xd1, 0x34, 0xc7, 0x67, 0x01, 0x8f, 0xde, 0x04, 0x08, 0xfe, 0x9d, 0x95, 0xbe, + 0x19, 0x9c, 0xdb, 0x84, 0x64, 0x4b, 0x75, 0x0e, 0xe4, 0xd4, 0x1e, 0x7f, 0x2c, 0xfc, 0x49, 0x0c, + 0x5e, 0xa9, 0xaa, 0xae, 0xb6, 0xdf, 0xb5, 0xbc, 0x7b, 0x4c, 0x19, 0xf8, 0x32, 0x31, 0x24, 0xbc, + 0x3c, 0x0d, 0x17, 0xa6, 0xbb, 0x3d, 0xcd, 0x7a, 0xa7, 0xa3, 0x94, 0x7a, 0x26, 0x3c, 0x7a, 0x99, + 0x63, 0xe7, 0xff, 0x40, 0x80, 0x99, 0x3e, 0x44, 0xe8, 0xed, 0xb3, 0x67, 0xbc, 0xba, 0x7e, 0x99, + 0xd9, 0x6d, 0x14, 0x62, 0x67, 0x31, 0x0a, 0x85, 0x3f, 0x16, 0x60, 0x82, 0xf8, 0x8a, 0xdd, 0x27, + 0xb8, 0x03, 0xd9, 0xe0, 0x04, 0x95, 0x5f, 0x7f, 0x89, 0x19, 0x1c, 0xcc, 0x3b, 0x2f, 0x73, 0xa6, + 0x3f, 0x1f, 0x86, 0x57, 0xc3, 0x0b, 0x0d, 0xbd, 0xee, 0xb6, 0x65, 0xaf, 0x3f, 0xda, 0x70, 0xf8, + 0xd2, 0x7f, 0x21, 0x40, 0x92, 0x7a, 0x58, 0xb8, 0x65, 0x30, 0xc5, 0xfe, 0x0b, 0xe1, 0x79, 0xe5, + 0xd2, 0x26, 0x2a, 0x7c, 0x71, 0xb9, 0xba, 0xb2, 0x76, 0xe3, 0x8b, 0xeb, 0x6b, 0xcb, 0xfd, 0x15, + 0x3c, 0xf9, 0x39, 0x50, 0xf0, 0x2f, 0x31, 0x05, 0x27, 0x9f, 0xb1, 0xde, 0x32, 0xd0, 0xdf, 0x09, + 0x40, 0x94, 0x9d, 0x7e, 0x51, 0xec, 0xb7, 0xf9, 0x8b, 0x46, 0x75, 0x7c, 0xb8, 0xde, 0x32, 0x0a, + 0xc7, 0x31, 0x98, 0xba, 0x67, 0x38, 0xc1, 0x71, 0xfa, 0xa7, 0xf7, 0x0e, 0xe4, 0xc2, 0x9e, 0x57, + 0x20, 0x79, 0x97, 0x4f, 0xf0, 0xb9, 0xc2, 0x19, 0xc6, 0xac, 0x1a, 0x1e, 0x7f, 0x19, 0x99, 0x43, + 0x3f, 0x16, 0x60, 0xc4, 0xb2, 0x75, 0x6c, 0xd3, 0xdc, 0x6f, 0xaa, 0xfa, 0xff, 0x84, 0xe3, 0xea, + 0xff, 0x11, 0xec, 0xef, 0x09, 0xf2, 0x90, 0x9c, 0xf2, 0x55, 0x43, 0x86, 0xc5, 0xe0, 0xd9, 0x97, + 0x3c, 0x39, 0xb5, 0xe8, 0x3f, 0xf2, 0xb3, 0x93, 0x93, 0x8b, 0xfc, 0x89, 0xa6, 0x7f, 0xe5, 0x91, + 0x45, 0xfa, 0xbf, 0x70, 0x9a, 0x57, 0xce, 0x2c, 0x86, 0xff, 0x0a, 0x65, 0xb1, 0xe5, 0xf4, 0x62, + 0xe8, 0x0f, 0x6f, 0x61, 0xe8, 0x02, 0x8c, 0x78, 0xff, 0x9e, 0x49, 0x3c, 0x5c, 0x2b, 0xfa, 0xb7, + 0x84, 0xec, 0x0d, 0x23, 0x04, 0xf1, 0x26, 0xf1, 0xa3, 0xbd, 0x2a, 0x11, 0x7d, 0x2e, 0xfc, 0x9e, + 0x00, 0x13, 0x0f, 0x23, 0x74, 0xfe, 0x37, 0x6c, 0xb5, 0xff, 0x4c, 0x80, 0x39, 0x19, 0x3b, 0xd8, + 0xad, 0x98, 0xfa, 0x6f, 0x85, 0x49, 0xfa, 0x89, 0x00, 0xe3, 0xfe, 0xbb, 0x76, 0x70, 0xa3, 0x59, + 0x27, 0x16, 0xfd, 0x37, 0xbb, 0x8b, 0xa8, 0x08, 0xe9, 0x86, 0xda, 0xa4, 0xad, 0xc5, 0xc4, 0x39, + 0x1b, 0xee, 0xcc, 0x76, 0x02, 0x9b, 0xbb, 0x8b, 0x8f, 0x0a, 0x7f, 0x19, 0xbe, 0x96, 0xf8, 0xf2, + 0xbd, 0x28, 0xc3, 0x2f, 0x5a, 0x08, 0x9d, 0xec, 0x91, 0x45, 0x8b, 0x58, 0x38, 0x29, 0xfb, 0x33, + 0xa1, 0xb3, 0x68, 0xb1, 0x03, 0x39, 0x9a, 0xd8, 0xc7, 0x6d, 0x17, 0x9b, 0x0e, 0xcd, 0x46, 0x0e, + 0xd3, 0xba, 0xf0, 0x17, 0x8e, 0xab, 0xc5, 0xe7, 0xc2, 0xab, 0xa2, 0x2e, 0x09, 0x85, 0x8b, 0xf6, + 0xf9, 0xf2, 0xdc, 0x07, 0xc5, 0x5b, 0x37, 0xdf, 0x2f, 0xf1, 0xe0, 0xe4, 0xa3, 0xeb, 0xd7, 0xae, + 0xbf, 0xf1, 0xf1, 0x95, 0x8f, 0xae, 0x5f, 0x2b, 0x7f, 0xbc, 0x20, 0x67, 0x09, 0xc6, 0xba, 0x0f, + 0x51, 0xf8, 0x2f, 0x01, 0xa4, 0x3e, 0x4b, 0x77, 0xd0, 0xc7, 0x90, 0xf0, 0xe2, 0x23, 0x7e, 0xab, + 0xdf, 0xe8, 0xbb, 0xfb, 0x5d, 0xac, 0x25, 0xf6, 0xff, 0x5f, 0x27, 0x0b, 0xca, 0xdf, 0x99, 0xd7, + 0x20, 0x13, 0x86, 0x89, 0xf0, 0x92, 0x4f, 0x2b, 0x07, 0xf6, 0x59, 0x5e, 0xc8, 0x69, 0x2e, 0xfc, + 0x93, 0x00, 0x17, 0x57, 0x2d, 0xf3, 0x10, 0xdb, 0x6e, 0x0f, 0x35, 0xd7, 0x97, 0x35, 0x48, 0x79, + 0x6b, 0x22, 0x41, 0xa1, 0x77, 0x90, 0xaf, 0x1d, 0x57, 0x07, 0xfb, 0x9a, 0xa4, 0xc7, 0xb9, 0xa1, + 0x13, 0x03, 0x42, 0xe3, 0x3f, 0x7a, 0xed, 0xc8, 0xf4, 0x19, 0x7d, 0x13, 0xa6, 0x43, 0x9a, 0x18, + 0xae, 0x08, 0x0d, 0x9f, 0xbd, 0x22, 0x34, 0x81, 0x7b, 0x26, 0x9d, 0xab, 0x8f, 0x00, 0x82, 0xb4, + 0x01, 0x1a, 0x87, 0xb1, 0xed, 0x77, 0x9e, 0xac, 0xcb, 0xca, 0xa3, 0xfb, 0x77, 0xef, 0xbf, 0xf3, + 0xe4, 0xbe, 0x38, 0x14, 0x0c, 0x55, 0x2b, 0x3b, 0x3b, 0xeb, 0xf2, 0xd7, 0x45, 0x01, 0x21, 0xc8, + 0x7a, 0x43, 0xeb, 0xef, 0xee, 0xac, 0xcb, 0xf7, 0x2b, 0xf7, 0xc4, 0x58, 0x7e, 0xec, 0x57, 0x2f, + 0x66, 0x53, 0x92, 0x70, 0x75, 0x84, 0xce, 0x54, 0x6f, 0xfc, 0xf5, 0xbf, 0x5c, 0x10, 0xbe, 0xb1, + 0x74, 0x86, 0x5b, 0xcc, 0x35, 0x9b, 0xbb, 0xbb, 0xa3, 0x54, 0xe5, 0x56, 0xfe, 0x27, 0x00, 0x00, + 0xff, 0xff, 0x99, 0x9e, 0xfc, 0x1d, 0x5a, 0x51, 0x00, 0x00, } diff --git a/pkg/ttnpb/end_device.pb.paths.fm.go b/pkg/ttnpb/end_device.pb.paths.fm.go index 175563f2da..ef4591e410 100644 --- a/pkg/ttnpb/end_device.pb.paths.fm.go +++ b/pkg/ttnpb/end_device.pb.paths.fm.go @@ -3082,11 +3082,21 @@ var EndDeviceTemplateFormatsFieldPathsTopLevel = []string{ } var ConvertEndDeviceTemplateRequestFieldPathsNested = []string{ "data", + "end_device_version_ids", + "end_device_version_ids.band_id", + "end_device_version_ids.brand_id", + "end_device_version_ids.firmware_version", + "end_device_version_ids.hardware_version", + "end_device_version_ids.model_id", + "end_device_version_ids.serial_number", + "end_device_version_ids.vendor_id", + "end_device_version_ids.vendor_profile_id", "format_id", } var ConvertEndDeviceTemplateRequestFieldPathsTopLevel = []string{ "data", + "end_device_version_ids", "format_id", } var MACParameters_ChannelFieldPathsNested = []string{ diff --git a/pkg/ttnpb/end_device.pb.setters.fm.go b/pkg/ttnpb/end_device.pb.setters.fm.go index a3a9b40baf..d5ff7e0138 100644 --- a/pkg/ttnpb/end_device.pb.setters.fm.go +++ b/pkg/ttnpb/end_device.pb.setters.fm.go @@ -3065,6 +3065,31 @@ func (dst *ConvertEndDeviceTemplateRequest) SetFields(src *ConvertEndDeviceTempl } else { dst.Data = nil } + case "end_device_version_ids": + if len(subs) > 0 { + var newDst, newSrc *EndDeviceVersionIdentifiers + if (src == nil || src.EndDeviceVersionIds == nil) && dst.EndDeviceVersionIds == nil { + continue + } + if src != nil { + newSrc = src.EndDeviceVersionIds + } + if dst.EndDeviceVersionIds != nil { + newDst = dst.EndDeviceVersionIds + } else { + newDst = &EndDeviceVersionIdentifiers{} + dst.EndDeviceVersionIds = newDst + } + if err := newDst.SetFields(newSrc, subs...); err != nil { + return err + } + } else { + if src != nil { + dst.EndDeviceVersionIds = src.EndDeviceVersionIds + } else { + dst.EndDeviceVersionIds = nil + } + } default: return fmt.Errorf("invalid field: '%s'", name) diff --git a/pkg/ttnpb/end_device.pb.validate.go b/pkg/ttnpb/end_device.pb.validate.go index 2c70aa9035..87c2ee2fca 100644 --- a/pkg/ttnpb/end_device.pb.validate.go +++ b/pkg/ttnpb/end_device.pb.validate.go @@ -3902,6 +3902,18 @@ func (m *ConvertEndDeviceTemplateRequest) ValidateFields(paths ...string) error case "data": // no validation rules for Data + case "end_device_version_ids": + + if v, ok := interface{}(m.GetEndDeviceVersionIds()).(interface{ ValidateFields(...string) error }); ok { + if err := v.ValidateFields(subs...); err != nil { + return ConvertEndDeviceTemplateRequestValidationError{ + field: "end_device_version_ids", + reason: "embedded message failed validation", + cause: err, + } + } + } + default: return ConvertEndDeviceTemplateRequestValidationError{ field: name, diff --git a/pkg/ttnpb/field_mask_validation.go b/pkg/ttnpb/field_mask_validation.go index 2a3c562eda..f6d15f29f1 100644 --- a/pkg/ttnpb/field_mask_validation.go +++ b/pkg/ttnpb/field_mask_validation.go @@ -522,6 +522,8 @@ var RPCFieldMaskPaths = map[string]RPCFieldMaskPathValue{ "version_ids.firmware_version", "version_ids.hardware_version", "version_ids.model_id", + "version_ids.vendor_id", + "version_ids.vendor_profile_id", }, Set: true, }, @@ -911,6 +913,8 @@ var RPCFieldMaskPaths = map[string]RPCFieldMaskPathValue{ "supports_class_c", "supports_join", "version_ids", + "version_ids.vendor_id", + "version_ids.vendor_profile_id", "version_ids.band_id", "version_ids.brand_id", "version_ids.firmware_version", diff --git a/sdk/js/generated/api-definition.json b/sdk/js/generated/api-definition.json index 5050a77298..577a75bc8a 100644 --- a/sdk/js/generated/api-definition.json +++ b/sdk/js/generated/api-definition.json @@ -624,7 +624,9 @@ "version_ids.brand_id", "version_ids.firmware_version", "version_ids.hardware_version", - "version_ids.model_id" + "version_ids.model_id", + "version_ids.vendor_id", + "version_ids.vendor_profile_id" ] }, "Delete": { @@ -4354,6 +4356,8 @@ "supports_class_c", "supports_join", "version_ids", + "version_ids.vendor_id", + "version_ids.vendor_profile_id", "version_ids.band_id", "version_ids.brand_id", "version_ids.firmware_version", diff --git a/sdk/js/generated/api.json b/sdk/js/generated/api.json index 7a8e1183e0..6106f9ad26 100644 --- a/sdk/js/generated/api.json +++ b/sdk/js/generated/api.json @@ -13949,6 +13949,18 @@ "isoneof": false, "oneofdecl": "", "defaultValue": "" + }, + { + "name": "end_device_version_ids", + "description": "End device profile identifiers.", + "label": "", + "type": "EndDeviceVersionIdentifiers", + "longType": "EndDeviceVersionIdentifiers", + "fullType": "ttn.lorawan.v3.EndDeviceVersionIdentifiers", + "ismap": false, + "isoneof": false, + "oneofdecl": "", + "defaultValue": "" } ] }, diff --git a/sdk/js/generated/device-entity-map.json b/sdk/js/generated/device-entity-map.json index 419afedb72..d19f0cb925 100644 --- a/sdk/js/generated/device-entity-map.json +++ b/sdk/js/generated/device-entity-map.json @@ -241,6 +241,20 @@ "as", "ns" ] + ], + "vendor_id": [ + "as", + [ + "as", + "ns" + ] + ], + "vendor_profile_id": [ + "as", + [ + "as", + "ns" + ] ] }, "picture": [