Skip to content

Commit

Permalink
bridge: implement thing description to support Web of Things (#463)
Browse files Browse the repository at this point in the history
* bridge: implement thing description to support Web of Things

---------

Co-authored-by: Daniel Adam <daniel.adam1922@protonmail.com>
  • Loading branch information
jkralik and Danielius1922 authored Apr 19, 2024
1 parent d7aeb36 commit a646d69
Show file tree
Hide file tree
Showing 28 changed files with 1,396 additions and 70 deletions.
4 changes: 2 additions & 2 deletions bridge/device/cloud/manager_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ func TestManagerDeviceBecomesUnauthorized(t *testing.T) {
})
deviceID := uuid.New().String()
tickInterval := time.Second
d1 := bridgeTest.NewBridgedDevice(t, s1, deviceID, true, false, device.WithCloudOptions(cloud.WithTickInterval(tickInterval)))
d1 := bridgeTest.NewBridgedDevice(t, s1, deviceID, true, false, true, device.WithCloudOptions(cloud.WithTickInterval(tickInterval)))
s1Shutdown := bridgeTest.RunBridgeService(s1)
t.Cleanup(func() {
_ = s1Shutdown()
Expand Down Expand Up @@ -167,7 +167,7 @@ func TestProvisioningOnDeviceRestart(t *testing.T) {
_ = s1.Shutdown()
})
deviceID := uuid.New().String()
d1 := bridgeTest.NewBridgedDevice(t, s1, deviceID, true, false)
d1 := bridgeTest.NewBridgedDevice(t, s1, deviceID, true, false, true)
s1Shutdown := bridgeTest.RunBridgeService(s1)
t.Cleanup(func() {
_ = s1Shutdown()
Expand Down
42 changes: 32 additions & 10 deletions bridge/device/device.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,15 @@ import (
"github.com/google/uuid"
"github.com/plgd-dev/device/v2/bridge/device/cloud"
"github.com/plgd-dev/device/v2/bridge/device/credential"
"github.com/plgd-dev/device/v2/bridge/device/thingDescription"
"github.com/plgd-dev/device/v2/bridge/net"
"github.com/plgd-dev/device/v2/bridge/resources"
cloudResource "github.com/plgd-dev/device/v2/bridge/resources/cloud"
resourcesDevice "github.com/plgd-dev/device/v2/bridge/resources/device"
"github.com/plgd-dev/device/v2/bridge/resources/discovery"
"github.com/plgd-dev/device/v2/bridge/resources/maintenance"
credentialResource "github.com/plgd-dev/device/v2/bridge/resources/secure/credential"
thingDescriptionResource "github.com/plgd-dev/device/v2/bridge/resources/thingDescription"
"github.com/plgd-dev/device/v2/pkg/eventloop"
pkgLog "github.com/plgd-dev/device/v2/pkg/log"
"github.com/plgd-dev/device/v2/schema"
Expand All @@ -47,9 +49,10 @@ import (
"github.com/plgd-dev/go-coap/v3/message/codes"
"github.com/plgd-dev/go-coap/v3/message/pool"
"github.com/plgd-dev/go-coap/v3/pkg/sync"
wotTD "github.com/web-of-things-open-source/thingdescription-go/thingDescription"
)

type Resource interface {
type Resource = interface {
Close()
ETag() []byte
GetHref() string
Expand All @@ -59,18 +62,20 @@ type Resource interface {
GetPolicyBitMask() schema.BitMask
SetObserveHandler(loop *eventloop.Loop, createSubscription resources.CreateSubscriptionFunc)
UpdateETag()
SupportsOperations() resources.SupportedOperation
}

type Device struct {
cfg Config
resources *sync.Map[string, Resource]
cloudManager *cloud.Manager
credentialManager *credential.Manager
onDeviceUpdated func(d *Device)
loop *eventloop.Loop
runLoop bool
done chan struct{}
stopped atomic.Bool
cfg Config
resources *sync.Map[string, Resource]
cloudManager *cloud.Manager
credentialManager *credential.Manager
thingDescriptionManager *thingDescription.Manager
onDeviceUpdated func(d *Device)
loop *eventloop.Loop
runLoop bool
done chan struct{}
stopped atomic.Bool
}

func NewLogger(id uuid.UUID, level pkgLog.Level) pkgLog.Logger {
Expand Down Expand Up @@ -170,6 +175,15 @@ func New(cfg Config, opts ...Option) (*Device, error) {
d.cloudManager = cm
d.AddResources(cloudResource.New(cloudSchema.ResourceURI, d.cloudManager))
}
if o.getThingDescription != nil {
td := thingDescription.New(d, o.loop)
tdRes := thingDescriptionResource.New(thingDescriptionResource.ResourceURI, func(ctx context.Context, endpoints schema.Endpoints) *wotTD.ThingDescription {
return o.getThingDescription(ctx, d, endpoints)
}, td.RegisterSubscription)
tdRes.SetObserveHandler(o.loop, tdRes.CreateSubscription)
d.AddResources(tdRes)
d.thingDescriptionManager = td
}

d.AddResources(resourcesDevice.New(plgdDevice.ResourceURI, d, o.getAdditionalProperties))
// oic/res is not discoverable
Expand Down Expand Up @@ -212,6 +226,11 @@ func (d *Device) GetCloudManager() *cloud.Manager {
return d.cloudManager
}

// GetThingDescriptionManager returns thing description manager of the device.
func (d *Device) GetThingDescriptionManager() *thingDescription.Manager {
return d.thingDescriptionManager
}

func (d *Device) Range(f func(resourceHref string, resource Resource) bool) {
d.resources.Range(f)
}
Expand Down Expand Up @@ -308,6 +327,9 @@ func (d *Device) Close() {
if d.credentialManager != nil {
d.credentialManager.Close()
}
if d.thingDescriptionManager != nil {
d.thingDescriptionManager.Close()
}
for _, resource := range d.resources.LoadAndDeleteAll() {
resource.Close()
}
Expand Down
15 changes: 14 additions & 1 deletion bridge/device/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,21 @@
package device

import (
"context"
"crypto/x509"

"github.com/plgd-dev/device/v2/bridge/device/cloud"
"github.com/plgd-dev/device/v2/bridge/resources/device"
"github.com/plgd-dev/device/v2/pkg/eventloop"
"github.com/plgd-dev/device/v2/pkg/log"
"github.com/plgd-dev/device/v2/schema"
wotTD "github.com/web-of-things-open-source/thingdescription-go/thingDescription"
)

type OnDeviceUpdated func(d *Device)
type (
OnDeviceUpdated func(d *Device)
GetThingDescription func(ctx context.Context, d *Device, endpoints schema.Endpoints) *wotTD.ThingDescription
)

type CAPoolGetter interface {
IsValid() bool
Expand All @@ -43,6 +49,7 @@ type OptionsCfg struct {
loop *eventloop.Loop
runLoop bool
cloudOptions []cloud.Option
getThingDescription GetThingDescription
}

type Option func(*OptionsCfg)
Expand Down Expand Up @@ -89,3 +96,9 @@ func WithCloudOptions(cloudOptions ...cloud.Option) Option {
o.cloudOptions = cloudOptions
}
}

func WithThingDescription(getThingDescription GetThingDescription) Option {
return func(o *OptionsCfg) {
o.getThingDescription = getThingDescription
}
}
88 changes: 88 additions & 0 deletions bridge/device/options_internal_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
/****************************************************************************
*
* Copyright (c) 2024 plgd.dev s.r.o.
*
* 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 device

import (
"context"
"crypto/tls"
"crypto/x509"
"testing"
"time"

"github.com/plgd-dev/device/v2/bridge/device/cloud"
"github.com/plgd-dev/device/v2/bridge/device/credential"
"github.com/plgd-dev/device/v2/pkg/eventloop"
"github.com/plgd-dev/device/v2/pkg/log"
"github.com/plgd-dev/device/v2/schema"
"github.com/stretchr/testify/require"
wotTD "github.com/web-of-things-open-source/thingdescription-go/thingDescription"
)

func TestOptions(t *testing.T) {
cfg := OptionsCfg{}

opts := []Option{}
onDeviceUpdated := func(*Device) {
// no-op
}
opts = append(opts, WithOnDeviceUpdated(onDeviceUpdated))

getAdditionalPropertiesForResponseFunc := func() map[string]interface{} {
return nil
}
opts = append(opts, WithGetAdditionalPropertiesForResponse(getAdditionalPropertiesForResponseFunc))

getCertificates := func(string) []tls.Certificate {
return nil
}
opts = append(opts, WithGetCertificates(getCertificates))

getCAPool := func() []*x509.Certificate {
return []*x509.Certificate{{}}
}
caPool := credential.MakeCAPool(nil, getCAPool)
opts = append(opts, WithCAPool(caPool))

logger := log.NewNilLogger()
opts = append(opts, WithLogger(logger))

loop := eventloop.New()
opts = append(opts, WithEventLoop(loop))

cloudOpt := cloud.WithTickInterval(time.Second)
opts = append(opts, WithCloudOptions(cloudOpt))

getThingDescription := func(context.Context, *Device, schema.Endpoints) *wotTD.ThingDescription {
return nil
}
opts = append(opts, WithThingDescription(getThingDescription))

for _, o := range opts {
o(&cfg)
}

require.NotNil(t, cfg.onDeviceUpdated)
require.NotNil(t, cfg.getAdditionalProperties)
require.NotNil(t, cfg.getCertificates)
require.NotNil(t, cfg.caPool)
require.Equal(t, logger, cfg.logger)
require.Equal(t, loop, cfg.loop)
require.Len(t, cfg.cloudOptions, 1)
require.NotNil(t, cfg.getThingDescription)
}
Loading

0 comments on commit a646d69

Please sign in to comment.