mirror of
https://github.com/mainflux/mainflux.git
synced 2025-04-28 13:48:49 +08:00
NOISSUE - Store successfull OPC-UA subscriptions (#999)
Signed-off-by: Manuel Imperiale <manuel.imperiale@gmail.com>
This commit is contained in:
parent
b3991b8497
commit
4ba1717582
@ -5,9 +5,7 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/csv"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
@ -20,6 +18,7 @@ import (
|
|||||||
"github.com/mainflux/mainflux/logger"
|
"github.com/mainflux/mainflux/logger"
|
||||||
"github.com/mainflux/mainflux/opcua"
|
"github.com/mainflux/mainflux/opcua"
|
||||||
"github.com/mainflux/mainflux/opcua/api"
|
"github.com/mainflux/mainflux/opcua/api"
|
||||||
|
"github.com/mainflux/mainflux/opcua/db"
|
||||||
"github.com/mainflux/mainflux/opcua/gopcua"
|
"github.com/mainflux/mainflux/opcua/gopcua"
|
||||||
pub "github.com/mainflux/mainflux/opcua/nats"
|
pub "github.com/mainflux/mainflux/opcua/nats"
|
||||||
"github.com/mainflux/mainflux/opcua/redis"
|
"github.com/mainflux/mainflux/opcua/redis"
|
||||||
@ -44,7 +43,6 @@ const (
|
|||||||
defRouteMapURL = "localhost:6379"
|
defRouteMapURL = "localhost:6379"
|
||||||
defRouteMapPass = ""
|
defRouteMapPass = ""
|
||||||
defRouteMapDB = "0"
|
defRouteMapDB = "0"
|
||||||
defNodesConfig = "/config/nodes.csv"
|
|
||||||
|
|
||||||
envHTTPPort = "MF_OPCUA_ADAPTER_HTTP_PORT"
|
envHTTPPort = "MF_OPCUA_ADAPTER_HTTP_PORT"
|
||||||
envLogLevel = "MF_OPCUA_ADAPTER_LOG_LEVEL"
|
envLogLevel = "MF_OPCUA_ADAPTER_LOG_LEVEL"
|
||||||
@ -60,13 +58,10 @@ const (
|
|||||||
envRouteMapURL = "MF_OPCUA_ADAPTER_ROUTE_MAP_URL"
|
envRouteMapURL = "MF_OPCUA_ADAPTER_ROUTE_MAP_URL"
|
||||||
envRouteMapPass = "MF_OPCUA_ADAPTER_ROUTE_MAP_PASS"
|
envRouteMapPass = "MF_OPCUA_ADAPTER_ROUTE_MAP_PASS"
|
||||||
envRouteMapDB = "MF_OPCUA_ADAPTER_ROUTE_MAP_DB"
|
envRouteMapDB = "MF_OPCUA_ADAPTER_ROUTE_MAP_DB"
|
||||||
envNodesConfig = "MF_OPCUA_ADAPTER_CONFIG_FILE"
|
|
||||||
|
|
||||||
thingsRMPrefix = "thing"
|
thingsRMPrefix = "thing"
|
||||||
channelsRMPrefix = "channel"
|
channelsRMPrefix = "channel"
|
||||||
connectionRMPrefix = "connection"
|
connectionRMPrefix = "connection"
|
||||||
|
|
||||||
columns = 2
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type config struct {
|
type config struct {
|
||||||
@ -81,7 +76,6 @@ type config struct {
|
|||||||
routeMapURL string
|
routeMapURL string
|
||||||
routeMapPass string
|
routeMapPass string
|
||||||
routeMapDB string
|
routeMapDB string
|
||||||
nodesConfig string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
@ -129,7 +123,7 @@ func main() {
|
|||||||
}, []string{"method"}),
|
}, []string{"method"}),
|
||||||
)
|
)
|
||||||
|
|
||||||
go subscribeToNodesFromFile(sub, cfg.nodesConfig, cfg.opcuaConfig, logger)
|
go subscribeToStoredSubs(sub, cfg.opcuaConfig, logger)
|
||||||
go subscribeToThingsES(svc, esConn, cfg.esConsumerName, logger)
|
go subscribeToThingsES(svc, esConn, cfg.esConsumerName, logger)
|
||||||
|
|
||||||
errs := make(chan error, 2)
|
errs := make(chan error, 2)
|
||||||
@ -165,7 +159,6 @@ func loadConfig() config {
|
|||||||
routeMapURL: mainflux.Env(envRouteMapURL, defRouteMapURL),
|
routeMapURL: mainflux.Env(envRouteMapURL, defRouteMapURL),
|
||||||
routeMapPass: mainflux.Env(envRouteMapPass, defRouteMapPass),
|
routeMapPass: mainflux.Env(envRouteMapPass, defRouteMapPass),
|
||||||
routeMapDB: mainflux.Env(envRouteMapDB, defRouteMapDB),
|
routeMapDB: mainflux.Env(envRouteMapDB, defRouteMapDB),
|
||||||
nodesConfig: mainflux.Env(envNodesConfig, defNodesConfig),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -194,45 +187,22 @@ func connectToRedis(redisURL, redisPass, redisDB string, logger logger.Logger) *
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func subscribeToNodesFromFile(sub opcua.Subscriber, nodes string, cfg opcua.Config, logger logger.Logger) {
|
func subscribeToStoredSubs(sub opcua.Subscriber, cfg opcua.Config, logger logger.Logger) {
|
||||||
if _, err := os.Stat(nodes); os.IsNotExist(err) {
|
// Get all stored subscriptions
|
||||||
logger.Warn(fmt.Sprintf("Config file not found: %s", err))
|
nodes, err := db.ReadAll()
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
file, err := os.OpenFile(nodes, os.O_RDONLY, os.ModePerm)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Warn(fmt.Sprintf("Failed to open config file: %s", err))
|
logger.Warn(fmt.Sprintf("Read stored subscriptions failed: %s", err))
|
||||||
return
|
|
||||||
}
|
|
||||||
defer file.Close()
|
|
||||||
|
|
||||||
reader := csv.NewReader(file)
|
|
||||||
for {
|
|
||||||
l, err := reader.Read()
|
|
||||||
if err == io.EOF {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
logger.Warn(fmt.Sprintf("Failed to read config file: %s", err))
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(l) < columns {
|
for _, n := range nodes {
|
||||||
logger.Warn("Empty or incomplete line found in file")
|
cfg.ServerURI = n.ServerURI
|
||||||
return
|
cfg.NodeID = n.NodeID
|
||||||
}
|
go func() {
|
||||||
|
|
||||||
cfg.ServerURI = l[0]
|
|
||||||
cfg.NodeID = l[1]
|
|
||||||
go subscribe(sub, cfg, logger)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func subscribe(sub opcua.Subscriber, cfg opcua.Config, logger logger.Logger) {
|
|
||||||
if err := sub.Subscribe(cfg); err != nil {
|
if err := sub.Subscribe(cfg); err != nil {
|
||||||
logger.Warn(fmt.Sprintf("Subscription failed: %s", err))
|
logger.Warn(fmt.Sprintf("Subscription failed: %s", err))
|
||||||
}
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func subscribeToOpcuaServer(gc opcua.Subscriber, cfg opcua.Config, logger logger.Logger) {
|
func subscribeToOpcuaServer(gc opcua.Subscriber, cfg opcua.Config, logger logger.Logger) {
|
||||||
|
@ -12,6 +12,9 @@ networks:
|
|||||||
docker_mainflux-base-net:
|
docker_mainflux-base-net:
|
||||||
external: true
|
external: true
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
mainflux-opcua-adapter-volume:
|
||||||
|
|
||||||
services:
|
services:
|
||||||
opcua-redis:
|
opcua-redis:
|
||||||
image: redis:5.0-alpine
|
image: redis:5.0-alpine
|
||||||
@ -47,4 +50,4 @@ services:
|
|||||||
networks:
|
networks:
|
||||||
- docker_mainflux-base-net
|
- docker_mainflux-base-net
|
||||||
volumes:
|
volumes:
|
||||||
- ./nodes.csv:/config/nodes.csv
|
- mainflux-opcua-adapter-volume:/store
|
||||||
|
@ -1 +0,0 @@
|
|||||||
opc.tcp://opcua.rocks:4840,ns=0;i=2256
|
|
|
75
opcua/db/subs.go
Normal file
75
opcua/db/subs.go
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
// Copyright (c) Mainflux
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package db
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/csv"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/mainflux/mainflux/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
const columns = 2
|
||||||
|
const path = "/store/nodes.csv"
|
||||||
|
|
||||||
|
var (
|
||||||
|
errNotFound = errors.New("file not found")
|
||||||
|
errWriteFile = errors.New("failed de write file")
|
||||||
|
errOpenFile = errors.New("failed to open file")
|
||||||
|
errReadFile = errors.New("failed to read file")
|
||||||
|
errEmptyLine = errors.New("empty or incomplete line found in file")
|
||||||
|
)
|
||||||
|
|
||||||
|
// Node represents an OPC-UA node
|
||||||
|
type Node struct {
|
||||||
|
ServerURI string
|
||||||
|
NodeID string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save stores a successfull subscription
|
||||||
|
func Save(serverURI, nodeID string) error {
|
||||||
|
file, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY, os.ModePerm)
|
||||||
|
if err != nil {
|
||||||
|
errors.Wrap(errWriteFile, err)
|
||||||
|
}
|
||||||
|
csvWriter := csv.NewWriter(file)
|
||||||
|
csvWriter.Write([]string{serverURI, nodeID})
|
||||||
|
csvWriter.Flush()
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadAll returns all stored subscriptions
|
||||||
|
func ReadAll() ([]Node, error) {
|
||||||
|
if _, err := os.Stat(path); os.IsNotExist(err) {
|
||||||
|
return nil, errors.Wrap(errNotFound, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
file, err := os.OpenFile(path, os.O_RDONLY, os.ModePerm)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(errOpenFile, err)
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
reader := csv.NewReader(file)
|
||||||
|
nodes := []Node{}
|
||||||
|
for {
|
||||||
|
l, err := reader.Read()
|
||||||
|
if err == io.EOF {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(errReadFile, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(l) < columns {
|
||||||
|
return nil, errEmptyLine
|
||||||
|
}
|
||||||
|
|
||||||
|
nodes = append(nodes, Node{l[0], l[1]})
|
||||||
|
}
|
||||||
|
|
||||||
|
return nodes, nil
|
||||||
|
}
|
@ -14,6 +14,7 @@ import (
|
|||||||
"github.com/mainflux/mainflux/errors"
|
"github.com/mainflux/mainflux/errors"
|
||||||
"github.com/mainflux/mainflux/logger"
|
"github.com/mainflux/mainflux/logger"
|
||||||
"github.com/mainflux/mainflux/opcua"
|
"github.com/mainflux/mainflux/opcua"
|
||||||
|
"github.com/mainflux/mainflux/opcua/db"
|
||||||
)
|
)
|
||||||
|
|
||||||
const protocol = "opcua"
|
const protocol = "opcua"
|
||||||
@ -124,7 +125,13 @@ func (c client) runHandler(sub *opcuaGopcua.Subscription, cfg opcua.Config) erro
|
|||||||
|
|
||||||
go sub.Run(c.ctx)
|
go sub.Run(c.ctx)
|
||||||
|
|
||||||
c.logger.Info(fmt.Sprintf("subscribe to server %s and node_id %s", cfg.ServerURI, cfg.NodeID))
|
// Store subscription details
|
||||||
|
if err := db.Save(cfg.ServerURI, cfg.NodeID); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
c.logger.Info(fmt.Sprintf("subscribed to server %s and node_id %s", cfg.ServerURI, cfg.NodeID))
|
||||||
|
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-c.ctx.Done():
|
case <-c.ctx.Done():
|
||||||
|
@ -120,7 +120,12 @@ func (as *adapterService) ConnectThing(mfxChanID, mfxThingID string) error {
|
|||||||
|
|
||||||
as.cfg.NodeID = nodeID
|
as.cfg.NodeID = nodeID
|
||||||
as.cfg.ServerURI = serverURI
|
as.cfg.ServerURI = serverURI
|
||||||
go as.subscribe(as.cfg)
|
|
||||||
|
go func() {
|
||||||
|
if err := as.subscriber.Subscribe(as.cfg); err != nil {
|
||||||
|
as.logger.Warn(fmt.Sprintf("subscription failed: %s", err))
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
c := fmt.Sprintf("%s:%s", mfxChanID, mfxThingID)
|
c := fmt.Sprintf("%s:%s", mfxChanID, mfxThingID)
|
||||||
return as.connectRM.Save(c, c)
|
return as.connectRM.Save(c, c)
|
||||||
@ -138,9 +143,3 @@ func (as *adapterService) DisconnectThing(mfxChanID, mfxThingID string) error {
|
|||||||
c := fmt.Sprintf("%s:%s", mfxChanID, mfxThingID)
|
c := fmt.Sprintf("%s:%s", mfxChanID, mfxThingID)
|
||||||
return as.connectRM.Remove(c)
|
return as.connectRM.Remove(c)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (as *adapterService) subscribe(cfg Config) {
|
|
||||||
if err := as.subscriber.Subscribe(cfg); err != nil {
|
|
||||||
as.logger.Warn(fmt.Sprintf("subscription failed: %s", err))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user