add mqtt broker and hummingbird

master
winc-link 2023-08-29 15:36:46 +08:00
parent d3778129fc
commit 7769b1da62
362 changed files with 52541 additions and 3 deletions

3
.gitignore vendored
View File

@ -1,7 +1,4 @@
.idea
vendor
logs
edge-db-data
hummingbird
mqtt-broker
go.sum

View File

@ -0,0 +1,42 @@
# ----------------------------------------------------------------------------------
# Copyright 2018 Dell Technologies, Inc.
# Copyright 2018 Cavium
#
# 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.
#
# ----------------------------------------------------------------------------------
ARG BUILDER_BASE=golang:latest
FROM ${BUILDER_BASE} AS builder
WORKDIR /edge
# gitlab
COPY . .
RUN --mount=type=cache,target=/root/.cache/go-build make cmd/mqtt-broker/mqtt-broker
#Next image - Copy built Go binary into new workspace
FROM alpine:3.16
RUN --mount=type=cache,target=/var/cache/apk apk add --update --no-cache dumb-init
EXPOSE 58090
WORKDIR /
COPY --from=builder /edge/cmd/mqtt-broker/mqtt-broker /bin/
COPY --from=builder /edge/cmd/mqtt-broker/res/configuration.yml.dist /etc/emqtt-broker/res/configuration.yml
#RUN mkdir -p /logs/mqtt-broker
CMD ["/bin/sh", "-c", "/bin/mqtt-broker start -c=/etc/emqtt-broker/res/configuration.yml"]

View File

@ -0,0 +1,39 @@
/*******************************************************************************
* Copyright 2017 Dell Inc.
*
* 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 initcmd
import (
"fmt"
"github.com/spf13/cobra"
"github.com/winc-link/hummingbird/cmd/mqtt-broker/mqttd"
"github.com/winc-link/hummingbird/cmd/mqtt-broker/mqttd/command"
"os"
"path"
)
func must(err error) {
if err != nil {
fmt.Fprint(os.Stderr, err.Error())
os.Exit(1)
}
}
func Init(rootCmd *cobra.Command) {
configDir, err := mqttd.GetDefaultConfigDir()
must(err)
command.ConfigFile = path.Join(configDir, "configuration.yml")
rootCmd.PersistentFlags().StringVarP(&command.ConfigFile, "config", "c", command.ConfigFile, "The configuration file path")
rootCmd.AddCommand(command.NewStartCmd())
}

55
cmd/mqtt-broker/main.go Normal file
View File

@ -0,0 +1,55 @@
/*******************************************************************************
* Copyright 2017 Dell Inc.
*
* 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 main
import (
"fmt"
"github.com/spf13/cobra"
"github.com/winc-link/hummingbird/cmd/mqtt-broker/initcmd"
_ "github.com/winc-link/hummingbird/internal/hummingbird/mqttbroker/persistence"
_ "github.com/winc-link/hummingbird/internal/hummingbird/mqttbroker/topicalias/fifo"
_ "github.com/winc-link/hummingbird/internal/hummingbird/mqttbroker/plugin/admin"
_ "github.com/winc-link/hummingbird/internal/hummingbird/mqttbroker/plugin/aplugin"
_ "github.com/winc-link/hummingbird/internal/hummingbird/mqttbroker/plugin/auth"
_ "github.com/winc-link/hummingbird/internal/hummingbird/mqttbroker/plugin/federation"
"net/http"
"os"
)
var (
rootCmd = &cobra.Command{
Use: "mqttd",
Long: "This is a MQTT broker that fully implements MQTT V5.0 and V3.1.1 protocol",
Version: "",
}
enablePprof bool
pprofAddr = "127.0.0.1:60600"
)
func main() {
if enablePprof {
go func() {
http.ListenAndServe(pprofAddr, nil)
}()
}
initcmd.Init(rootCmd)
if err := rootCmd.Execute(); err != nil {
fmt.Fprint(os.Stderr, err.Error())
os.Exit(1)
}
}

View File

@ -0,0 +1,155 @@
package command
import (
"context"
"crypto/tls"
"fmt"
"net"
"net/http"
"os"
"os/signal"
"syscall"
"github.com/spf13/cobra"
"github.com/winc-link/hummingbird/internal/hummingbird/mqttbroker/config"
"github.com/winc-link/hummingbird/internal/hummingbird/mqttbroker/server"
"github.com/winc-link/hummingbird/internal/pkg/pidfile"
"go.uber.org/zap"
)
var (
ConfigFile string
logger *zap.Logger
)
func must(err error) {
if err != nil {
fmt.Fprint(os.Stderr, err)
os.Exit(1)
}
}
func installSignal(srv server.Server) {
// reload
reloadSignalCh := make(chan os.Signal, 1)
signal.Notify(reloadSignalCh, syscall.SIGHUP)
// stop
stopSignalCh := make(chan os.Signal, 1)
signal.Notify(stopSignalCh, os.Interrupt, syscall.SIGTERM)
for {
select {
case <-reloadSignalCh:
var c config.Config
var err error
c, err = config.ParseConfig(ConfigFile)
if err != nil {
logger.Error("reload error", zap.Error(err))
return
}
srv.ApplyConfig(c)
logger.Info("gmqtt reloaded")
case <-stopSignalCh:
err := srv.Stop(context.Background())
if err != nil {
logger.Error(err.Error())
//fmt.Fprint(os.Stderr, err.Error())
}
}
}
}
func GetListeners(c config.Config) (tcpListeners []net.Listener, websockets []*server.WsServer, err error) {
for _, v := range c.Listeners {
var ln net.Listener
if v.Websocket != nil {
ws := &server.WsServer{
Server: &http.Server{Addr: v.Address},
Path: v.Websocket.Path,
}
if v.TLSOptions != nil {
ws.KeyFile = v.Key
ws.CertFile = v.Cert
}
websockets = append(websockets, ws)
continue
}
if v.TLSOptions != nil {
var cert tls.Certificate
cert, err = tls.LoadX509KeyPair(v.Cert, v.Key)
if err != nil {
return
}
ln, err = tls.Listen("tcp", v.Address, &tls.Config{
Certificates: []tls.Certificate{cert},
})
} else {
ln, err = net.Listen("tcp", v.Address)
}
tcpListeners = append(tcpListeners, ln)
}
return
}
// NewStartCmd creates a *cobra.Command object for start command.
func NewStartCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "start",
Short: "Start gmqtt broker",
Run: func(cmd *cobra.Command, args []string) {
var err error
must(err)
c, err := config.ParseConfig(ConfigFile)
if os.IsNotExist(err) {
must(err)
} else {
must(err)
}
if c.PidFile != "" {
pid, err := pidfile.New(c.PidFile)
if err != nil {
must(fmt.Errorf("open pid file failed: %s", err))
}
defer pid.Remove()
}
level, l, err := c.GetLogger(c.Log)
must(err)
logger = l
//db := mqttbroker.NewDatabase(c)
//err = db.InitDBClient(l)
//must(err)
tcpListeners, websockets, err := GetListeners(c)
must(err)
s := server.New(
server.WithConfig(c),
server.WithTCPListener(tcpListeners...),
server.WithWebsocketServer(websockets...),
server.WithLogger(&server.DefaultLogger{
Level: level,
Logger: l,
}),
)
err = s.Init()
if err != nil {
fmt.Println(err)
os.Exit(1)
return
}
go installSignal(s)
err = s.Run()
if err != nil {
fmt.Fprint(os.Stderr, err.Error())
os.Exit(1)
return
}
},
}
return cmd
}

View File

@ -0,0 +1,11 @@
// +build !windows
package mqttd
var (
DefaultConfigDir = "./res/"
)
func GetDefaultConfigDir() (string, error) {
return DefaultConfigDir, nil
}

View File

@ -0,0 +1,85 @@
// +build ignore
package main
import (
"bytes"
"go/format"
"io"
"io/ioutil"
"log"
"strings"
"text/template"
"gopkg.in/yaml.v2"
)
var tmpl = `//go:generate sh -c "cd ../../ && go run plugin_generate.go"
// generated by plugin_generate.go; DO NOT EDIT
package mqttd
import (
{{- range $index, $element := .}}
_ "{{$element}}"
{{- end}}
)
`
const (
pluginFile = "./mqttd/plugins.go"
pluginCfg = "plugin_imports.yml"
importPath = "gitlab.com/tedge/edgex/internal/thummingbird/mqttbroker/plugin"
)
type ymlCfg struct {
Packages []string `yaml:"packages"`
}
func main() {
b, err := ioutil.ReadFile(pluginCfg)
if err != nil {
log.Fatalf("ReadFile error %s", err)
return
}
var cfg ymlCfg
err = yaml.Unmarshal(b, &cfg)
if err != nil {
log.Fatalf("Unmarshal error: %s", err)
return
}
t, err := template.New("plugin_gen").Parse(tmpl)
if err != nil {
log.Fatalf("Parse template error: %s", err)
return
}
for k, v := range cfg.Packages {
if !strings.Contains(v, "/") {
cfg.Packages[k] = importPath + "/" + v
}
}
if err != nil && err != io.EOF {
log.Fatalf("read error: %s", err)
return
}
buf := &bytes.Buffer{}
err = t.Execute(buf, cfg.Packages)
if err != nil {
log.Fatalf("excute template error: %s", err)
return
}
rs, err := format.Source(buf.Bytes())
if err != nil {
log.Fatalf("format error: %s", err)
return
}
err = ioutil.WriteFile(pluginFile, rs, 0666)
if err != nil {
log.Fatalf("writeFile error: %s", err)
return
}
return
}

View File

@ -0,0 +1,6 @@
packages:
- admin
# - federation
- aplugin
# for external plugin, use full import path
# - gitlab.com/tedge/edgex/internal/thummingbird/mqttbroker/plugin/prometheus

View File

@ -0,0 +1,22 @@
-----BEGIN CERTIFICATE-----
MIIDszCCApugAwIBAgIJAJXKBu6eNV6YMA0GCSqGSIb3DQEBCwUAMHAxCzAJBgNV
BAYTAkNOMQswCQYDVQQIDAJaSjELMAkGA1UEBwwCSFoxCzAJBgNVBAoMAlRZMQsw
CQYDVQQLDAJUWTEOMAwGA1UEAwwFdGVkZ2UxHTAbBgkqhkiG9w0BCQEWDnRlZGdl
QHR1eWEuY29tMB4XDTIyMDEyNDA2NTQ0M1oXDTMyMDEyMjA2NTQ0M1owcDELMAkG
A1UEBhMCQ04xCzAJBgNVBAgMAlpKMQswCQYDVQQHDAJIWjELMAkGA1UECgwCVFkx
CzAJBgNVBAsMAlRZMQ4wDAYDVQQDDAV0ZWRnZTEdMBsGCSqGSIb3DQEJARYOdGVk
Z2VAdHV5YS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC/IZfF
++ytZVIDbt5Ypz/55e0HTrq9jrpVOZAKSBbmSUryjpo8NfZoDp5QVZi4kSo1G0xV
Wf9C+5h13TFM2pDm9W9q4v8e3cB3Z+qK8nHn66xyQYnTihg8D9vyJHIQ2nirCVqW
HL2wYdakE0MojbVsQPWufYh84tWXyyUIo2W2ycoXmSfpWhb4LDEf4tcmDBNp2ydG
ef7MNbrS3t/h/iOzqjj7s+styiLyKjxE0oh1VfOOp8e9HPnh2EvaQwwTq91KRf+v
rl4DPZt93oMd9i28HuxBsWsE6eDRfYmF96ZoIXEh4ga9XWR8geuRCsTREQo7tqUX
gXFUXCe2Uo6R0uh9AgMBAAGjUDBOMB0GA1UdDgQWBBQayDeoKN44f/FV+Z6rv1vT
bsITvTAfBgNVHSMEGDAWgBQayDeoKN44f/FV+Z6rv1vTbsITvTAMBgNVHRMEBTAD
AQH/MA0GCSqGSIb3DQEBCwUAA4IBAQB0G/AM7zU2USZj3C32zzzM6LQNx465x1i2
XgSw9ECM7M3ct6x859L6vKXWUm6+OQO7jm9xRRyDQIrCSQT66MCei5C+nqzyPIZA
zL5cV7bRXA39KBwThyZZqWl4bttp98UZnEbX6yICVEcjsnaIA2D0vh1zZar4Ilyq
mBl4NA13HTIcQ0s4Efuhdf5RdPw3ha2cjf74aNNj2WijAY4rKEVF1Buw/PrvJ7WR
hrQlNrv214e3hbjV99oiII8OLDT0oJApUbSr6ktjF26bAu929b3QDADEK9QpBYE1
brg3KD51xgD+HGKd3PVLqr60y7OQKKMHd8TrQK/ibQVgFdbE6/AG
-----END CERTIFICATE-----

View File

@ -0,0 +1,159 @@
# Path to pid file.
# If not set, there will be no pid file.
# pid_file: /var/run/mqttd.pid
listeners:
# bind address
- address: ":58090" # 58090
# - address: ":21883" # 21883
# tls:
# cacert: "/etc/mqtt-broker/ca.crt"
# cert: "/etc/mqtt-broker/server.pem"
# key: "/etc/mqtt-broker/server.key"
#
# cacert: "cmd/mqtt-broker/res/ca.crt"
# cert: "cmd/mqtt-broker/res/server.pem"
# key: "cmd/mqtt-broker/res/server.key"
#
# - address: ":28883" # 28883
# # websocket setting
# websocket:
# path: "/"
api:
grpc:
# The gRPC server listen address. Supports unix socket and tcp socket.
- address: "tcp://127.0.0.1:57090" # 57090
http:
# The HTTP server listen address. This is a reverse-proxy server in front of gRPC server.
- address: "tcp://127.0.0.1:57091" # 57091
map: "tcp://127.0.0.1:57090" # The backend gRPC server endpoint,
mqtt:
# The maximum session expiry interval in seconds.
session_expiry: 2h
# The interval time for session expiry checker to check whether there are expired sessions.
session_expiry_check_timer: 20s
# The maximum lifetime of the message in seconds.
# If a message in the queue is not sent in message_expiry time, it will be dropped, which means it will not be sent to the subscriber.
message_expiry: 2h
# The lifetime of the "inflight" message in seconds.
# If a "inflight" message is not acknowledged by a client in inflight_expiry time, it will be removed when the message queue is full.
inflight_expiry: 30s
# The maximum packet size that the server is willing to accept from the client.
max_packet_size: 268435456
# The maximum number of QoS 1 and QoS 2 publications that the server is willing to process concurrently for the client.
server_receive_maximum: 100
# The maximum keep alive time in seconds allows by the server.
# If the client requests a keepalive time bigger than MaxKeepalive,the server will use MaxKeepAlive as the keepalive time.
# In this case, if the client version is v5, the server will set MaxKeepalive into CONNACK to inform the client.
# But if the client version is 3.x, the server has no way to inform the client that the keepalive time has been changed.
max_keepalive: 300
# The highest value that the server will accept as a Topic Alias sent by the client.
# No-op if the client version is MQTTv3.x .
topic_alias_maximum: 10
# Whether the server supports Subscription Identifiers.
# No-op if the client version is MQTTv3.x .
subscription_identifier_available: true
# Whether the server supports Wildcard Subscriptions.
wildcard_subscription_available: true
# Whether the server supports Shared Subscriptions.
shared_subscription_available: true
# The highest QOS level permitted for a Publish.
maximum_qos: 2
# Whether the server supports retained messages.
retain_available: true
# The maximum queue length of the outgoing messages.
# If the queue is full, some message will be dropped.
# The message dropping strategy is described in the document of the persistence/queue.Store interface.
max_queued_messages: 1000
# The limits of inflight message length of the outgoing messages.
# Inflight message is also stored in the message queue, so it must be less than or equal to max_queued_messages.
# Inflight message is the QoS 1 or QoS 2 message that has been sent out to a client but not been acknowledged yet.
max_inflight: 100
# Whether to store QoS 0 message for a offline session.
queue_qos0_messages: true
# The delivery mode. The possible value can be "overlap" or "onlyonce".
# It is possible for a clients subscriptions to overlap so that a published message might match multiple filters.
# When set to "overlap" , the server will deliver one message for each matching subscription and respecting the subscriptions QoS in each case.
# When set to "onlyonce", the server will deliver the message to the client respecting the maximum QoS of all the matching subscriptions.
delivery_mode: onlyonce
# Whether to allow a client to connect with empty client id.
allow_zero_length_clientid: true
persistence:
type: memory # memory | redis
# The redis configuration only take effect when type == redis.
redis:
# redis server address
addr: "127.0.0.1:56379"
# the maximum number of idle connections in the redis connection pool.
max_idle: 1000
# the maximum number of connections allocated by the redis connection pool at a given time.
# If zero, there is no limit on the number of connections in the pool.
max_active: 0
# the connection idle timeout, connection will be closed after remaining idle for this duration. If the value is zero, then idle connections are not closed.
idle_timeout: 240s
password: "qqwihyzjb8l2sx0c"
# the number of the redis database.
database: 0
# The topic alias manager setting. The topic alias feature is introduced by MQTT V5.
# This setting is used to control how the broker manage topic alias.
topic_alias_manager:
# Currently, only FIFO strategy is supported.
type: fifo
plugins:
aplugin:
# Password hash type. (plain | md5 | sha256 | bcrypt)
# Default to MD5.
hash: md5
# The file to store password. If it is a relative path, it locates in the same directory as the config file.
# (e.g: ./gmqtt_password => /etc/gmqtt/gmqtt_password.yml)
# Defaults to ./gmqtt_password.yml
# password_file:
federation:
# node_name is the unique identifier for the node in the federation. Defaults to hostname.
# node_name:
# fed_addr is the gRPC server listening address for the federation internal communication. Defaults to :8901
fed_addr: :8901
# advertise_fed_addr is used to change the federation gRPC server address that we advertise to other nodes in the cluster.
# Defaults to "fed_addr".However, in some cases, there may be a routable address that cannot be bound.
# If the port is missing, the default federation port (8901) will be used.
advertise_fed_addr: :8901
# gossip_addr is the address that the gossip will listen on, It is used for both UDP and TCP gossip. Defaults to :8902
gossip_addr: :8902
# advertise_gossip_addr is used to change the gossip server address that we advertise to other nodes in the cluster.
# Defaults to "GossipAddr" or the private IP address of the node if the IP in "GossipAddr" is 0.0.0.0.
# If the port is missing, the default gossip port (8902) will be used.
advertise_gossip_addr: :8902
# retry_join is the address of other nodes to join upon starting up.
# If port is missing, the default gossip port (8902) will be used.
#retry_join:
# - 127.0.0.1:8902
# rejoin_after_leave will be pass to "RejoinAfterLeave" in serf configuration.
# It controls our interaction with the snapshot file.
# When set to false (default), a leave causes a Serf to not rejoin the cluster until an explicit join is received.
# If this is set to true, we ignore the leave, and rejoin the cluster on start.
rejoin_after_leave: false
# snapshot_path will be pass to "SnapshotPath" in serf configuration.
# When Serf is started with a snapshot,it will attempt to join all the previously known nodes until one
# succeeds and will also avoid replaying old user events.
snapshot_path:
# plugin loading orders
plugin_order:
# Uncomment auth to enable authentication.
- aplugin
#- admin
#- federation
log:
level: debug # debug | info | warn | error
file_path: "./mqtt-broker/mqtt-broker.log"
# whether to dump MQTT packet in debug level
dump_packet: false

View File

@ -0,0 +1,159 @@
# Path to pid file.
# If not set, there will be no pid file.
# pid_file: /var/run/mqttd.pid
listeners:
# bind address
- address: ":58090" # 58090
# - address: ":21883" # 21883
# tls:
# cacert: "/etc/mqtt-broker/ca.crt"
# cert: "/etc/mqtt-broker/server.pem"
# key: "/etc/mqtt-broker/server.key"
#
# cacert: "cmd/mqtt-broker/res/ca.crt"
# cert: "cmd/mqtt-broker/res/server.pem"
# key: "cmd/mqtt-broker/res/server.key"
#
# - address: ":28883" # 28883
# # websocket setting
# websocket:
# path: "/"
api:
grpc:
# The gRPC server listen address. Supports unix socket and tcp socket.
- address: "tcp://127.0.0.1:57090" # 57090
http:
# The HTTP server listen address. This is a reverse-proxy server in front of gRPC server.
- address: "tcp://127.0.0.1:57091" # 57091
map: "tcp://127.0.0.1:57090" # The backend gRPC server endpoint,
mqtt:
# The maximum session expiry interval in seconds.
session_expiry: 2h
# The interval time for session expiry checker to check whether there are expired sessions.
session_expiry_check_timer: 20s
# The maximum lifetime of the message in seconds.
# If a message in the queue is not sent in message_expiry time, it will be dropped, which means it will not be sent to the subscriber.
message_expiry: 2h
# The lifetime of the "inflight" message in seconds.
# If a "inflight" message is not acknowledged by a client in inflight_expiry time, it will be removed when the message queue is full.
inflight_expiry: 30s
# The maximum packet size that the server is willing to accept from the client.
max_packet_size: 268435456
# The maximum number of QoS 1 and QoS 2 publications that the server is willing to process concurrently for the client.
server_receive_maximum: 100
# The maximum keep alive time in seconds allows by the server.
# If the client requests a keepalive time bigger than MaxKeepalive,the server will use MaxKeepAlive as the keepalive time.
# In this case, if the client version is v5, the server will set MaxKeepalive into CONNACK to inform the client.
# But if the client version is 3.x, the server has no way to inform the client that the keepalive time has been changed.
max_keepalive: 300
# The highest value that the server will accept as a Topic Alias sent by the client.
# No-op if the client version is MQTTv3.x .
topic_alias_maximum: 10
# Whether the server supports Subscription Identifiers.
# No-op if the client version is MQTTv3.x .
subscription_identifier_available: true
# Whether the server supports Wildcard Subscriptions.
wildcard_subscription_available: true
# Whether the server supports Shared Subscriptions.
shared_subscription_available: true
# The highest QOS level permitted for a Publish.
maximum_qos: 2
# Whether the server supports retained messages.
retain_available: true
# The maximum queue length of the outgoing messages.
# If the queue is full, some message will be dropped.
# The message dropping strategy is described in the document of the persistence/queue.Store interface.
max_queued_messages: 1000
# The limits of inflight message length of the outgoing messages.
# Inflight message is also stored in the message queue, so it must be less than or equal to max_queued_messages.
# Inflight message is the QoS 1 or QoS 2 message that has been sent out to a client but not been acknowledged yet.
max_inflight: 100
# Whether to store QoS 0 message for a offline session.
queue_qos0_messages: true
# The delivery mode. The possible value can be "overlap" or "onlyonce".
# It is possible for a clients subscriptions to overlap so that a published message might match multiple filters.
# When set to "overlap" , the server will deliver one message for each matching subscription and respecting the subscriptions QoS in each case.
# When set to "onlyonce", the server will deliver the message to the client respecting the maximum QoS of all the matching subscriptions.
delivery_mode: onlyonce
# Whether to allow a client to connect with empty client id.
allow_zero_length_clientid: true
persistence:
type: memory # memory | redis
# The redis configuration only take effect when type == redis.
redis:
# redis server address
addr: "127.0.0.1:56379"
# the maximum number of idle connections in the redis connection pool.
max_idle: 1000
# the maximum number of connections allocated by the redis connection pool at a given time.
# If zero, there is no limit on the number of connections in the pool.
max_active: 0
# the connection idle timeout, connection will be closed after remaining idle for this duration. If the value is zero, then idle connections are not closed.
idle_timeout: 240s
password: "qqwihyzjb8l2sx0c"
# the number of the redis database.
database: 0
# The topic alias manager setting. The topic alias feature is introduced by MQTT V5.
# This setting is used to control how the broker manage topic alias.
topic_alias_manager:
# Currently, only FIFO strategy is supported.
type: fifo
plugins:
aplugin:
# Password hash type. (plain | md5 | sha256 | bcrypt)
# Default to MD5.
hash: md5
# The file to store password. If it is a relative path, it locates in the same directory as the config file.
# (e.g: ./gmqtt_password => /etc/gmqtt/gmqtt_password.yml)
# Defaults to ./gmqtt_password.yml
# password_file:
federation:
# node_name is the unique identifier for the node in the federation. Defaults to hostname.
# node_name:
# fed_addr is the gRPC server listening address for the federation internal communication. Defaults to :8901
fed_addr: :8901
# advertise_fed_addr is used to change the federation gRPC server address that we advertise to other nodes in the cluster.
# Defaults to "fed_addr".However, in some cases, there may be a routable address that cannot be bound.
# If the port is missing, the default federation port (8901) will be used.
advertise_fed_addr: :8901
# gossip_addr is the address that the gossip will listen on, It is used for both UDP and TCP gossip. Defaults to :8902
gossip_addr: :8902
# advertise_gossip_addr is used to change the gossip server address that we advertise to other nodes in the cluster.
# Defaults to "GossipAddr" or the private IP address of the node if the IP in "GossipAddr" is 0.0.0.0.
# If the port is missing, the default gossip port (8902) will be used.
advertise_gossip_addr: :8902
# retry_join is the address of other nodes to join upon starting up.
# If port is missing, the default gossip port (8902) will be used.
#retry_join:
# - 127.0.0.1:8902
# rejoin_after_leave will be pass to "RejoinAfterLeave" in serf configuration.
# It controls our interaction with the snapshot file.
# When set to false (default), a leave causes a Serf to not rejoin the cluster until an explicit join is received.
# If this is set to true, we ignore the leave, and rejoin the cluster on start.
rejoin_after_leave: false
# snapshot_path will be pass to "SnapshotPath" in serf configuration.
# When Serf is started with a snapshot,it will attempt to join all the previously known nodes until one
# succeeds and will also avoid replaying old user events.
snapshot_path:
# plugin loading orders
plugin_order:
# Uncomment auth to enable authentication.
- aplugin
#- admin
#- federation
log:
level: debug # debug | info | warn | error
file_path: "/logs/mqtt-broker/mqtt-broker.log"
# whether to dump MQTT packet in debug level
dump_packet: false

View File

@ -0,0 +1,3 @@
# This is a sample plain password file for the auth plugin.
- username: root
password: root

View File

@ -0,0 +1,14 @@
-----BEGIN CERTIFICATE REQUEST-----
MIICFTCCAX4CAQAwcDELMAkGA1UEBhMCQ04xCzAJBgNVBAgMAlpKMQswCQYDVQQH
DAJIWjELMAkGA1UECgwCVFkxCzAJBgNVBAsMAlRZMQ4wDAYDVQQDDAV0ZWRnZTEd
MBsGCSqGSIb3DQEJARYOdGVkZ2VAdHV5YS5jb20wgZ8wDQYJKoZIhvcNAQEBBQAD
gY0AMIGJAoGBAMe1bzSZLfvqrBeBOgxAdaDqh8fWudqeb0wqC+ZSZg4uH+WEG4Hu
rMbt4B+b1U98ctpA/aOEgnZiV1z79w8Rm9ENvfCUOKJ8uJyVf2usAdR/HkudDOhU
KlnvXaCd5t99gi8pyBmYkaXf82ya7CN97f/Y35zNIcWTJhJmYmd3N6FRAgMBAAGg
ZTARBgkqhkiG9w0BCQIxBAwCdHkwFQYJKoZIhvcNAQkHMQgMBmJleW9uZDA5Bgkq
hkiG9w0BCQ4xLDAqMAkGA1UdEwQCMAAwCwYDVR0PBAQDAgXgMBAGA1UdEQQJMAeC
BXRlZGdlMA0GCSqGSIb3DQEBCwUAA4GBADSeemIkKCvrfOz+m0AxQN/9L8SEqdHB
l8YBaHSHgdxq6666ENPz5o2uPNnu6qYaBZUMOZ5223Sx2MJPNDAxemFnOw7YbnCV
jIPwI3O9KIFDZ+tmhEIVHSlqRFphYNIWAVVFBsdNkse1gLTLLBLKfbsCZeoD4Dz2
mc3JPZjeo4Mq
-----END CERTIFICATE REQUEST-----

View File

@ -0,0 +1,16 @@
-----BEGIN PRIVATE KEY-----
MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBAMe1bzSZLfvqrBeB
OgxAdaDqh8fWudqeb0wqC+ZSZg4uH+WEG4HurMbt4B+b1U98ctpA/aOEgnZiV1z7
9w8Rm9ENvfCUOKJ8uJyVf2usAdR/HkudDOhUKlnvXaCd5t99gi8pyBmYkaXf82ya
7CN97f/Y35zNIcWTJhJmYmd3N6FRAgMBAAECgYBVCLMGIWcMCdsm0vZlexja4KHZ
/Fr8dFONiaWxd0pPJWKddofD5l2ZAnZY3yCPjLzWo6+b7XMjdzIdvIdw2h2OwGpE
kPQST1lkN5VlPIG67jwmIyJVw1LBAqknmqRFLjJ8NcJRttNjYjEkpetMOq1rM3Di
90mY3lBLT2g5lZa0pQJBAOr0lEPC3WJMq1N04wzqM6h6y8FUKhGZDawl1HGne9bs
4IDzVEhiCT9VvN3eoX+bk6av1/uZOxHY78j81q8KzY8CQQDZmKpPOZFeBK8dIOt7
L4XB1NMVAkOy4UFZ1I9lpn9OVSQEPrnV0oyMnKIIMCzGy4nnFmrY/u4LKpuYSoVO
lvMfAkAHwhOzORf+SvHNS6rDnmgeRA++Tn0lH5yn9ofRSOp56lBvcZly2mnbwYT+
/n7uq8BwXJYRJLoimLsyM8cS+JRZAkAp40Glzqc1OiGbseKi7BsLnTSlLrJplQNH
j6urHcoUAj/UsV6E0utLhjuK5/s2qaf6XE5lR237qFAbmPzgjB5xAkAP7H0cfdzs
X9gCe4RqQgIuJzK6Y59GkVeWVT8lScL9FyWm2JmeGh907HgnTXt6t8Hhk23JxD3x
KNcIk5xzRaOO
-----END PRIVATE KEY-----

View File

@ -0,0 +1,19 @@
-----BEGIN CERTIFICATE-----
MIIDCzCCAfOgAwIBAgIJAMMZRyEj7GMLMA0GCSqGSIb3DQEBCwUAMHAxCzAJBgNV
BAYTAkNOMQswCQYDVQQIDAJaSjELMAkGA1UEBwwCSFoxCzAJBgNVBAoMAlRZMQsw
CQYDVQQLDAJUWTEOMAwGA1UEAwwFdGVkZ2UxHTAbBgkqhkiG9w0BCQEWDnRlZGdl
QHR1eWEuY29tMB4XDTIyMDEyNDA3MDAxNVoXDTMyMDEyMjA3MDAxNVowcDELMAkG
A1UEBhMCQ04xCzAJBgNVBAgMAlpKMQswCQYDVQQHDAJIWjELMAkGA1UECgwCVFkx
CzAJBgNVBAsMAlRZMQ4wDAYDVQQDDAV0ZWRnZTEdMBsGCSqGSIb3DQEJARYOdGVk
Z2VAdHV5YS5jb20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAMe1bzSZLfvq
rBeBOgxAdaDqh8fWudqeb0wqC+ZSZg4uH+WEG4HurMbt4B+b1U98ctpA/aOEgnZi
V1z79w8Rm9ENvfCUOKJ8uJyVf2usAdR/HkudDOhUKlnvXaCd5t99gi8pyBmYkaXf
82ya7CN97f/Y35zNIcWTJhJmYmd3N6FRAgMBAAGjLDAqMAkGA1UdEwQCMAAwCwYD
VR0PBAQDAgXgMBAGA1UdEQQJMAeCBXRlZGdlMA0GCSqGSIb3DQEBCwUAA4IBAQA5
htWsbfo7XedP2DBbVRXWFhEw7RPFfmyFMgzQq3aifnNB93xpDRwauXH5k6TEsiIO
OKjQit9aiSA28sTad6k6S09SwJokeQ9l14T3vVVMdDVJCw1Hq/mEhgoGgpYM+om0
t/gl7e4FHL0AH6vcAyO70Q4uVRGpnm6Ehp8MxW0f/uip6TLxSj3lTitkCytMSGMK
WhvTLy8gsD9sSkiZUL/jknVkSp5An3roayWZLZucPV0E2rINchRcMcrrY1UkeYu1
HB94dGg2U7R7Qj0eJBdxJN0uCY5n02pBXabJXRtwvOReHsW6Qoo50MhWEd2sazCA
HwMRWr6g8aAun7QJfySG
-----END CERTIFICATE-----

View File

@ -0,0 +1,960 @@
/*******************************************************************************
* Copyright 2017.
*
* 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 alertcentreapp
import (
"context"
"encoding/json"
"errors"
"fmt"
"github.com/winc-link/hummingbird/internal/dtos"
resourceContainer "github.com/winc-link/hummingbird/internal/hummingbird/core/container"
interfaces "github.com/winc-link/hummingbird/internal/hummingbird/core/interface"
"github.com/winc-link/hummingbird/internal/models"
"github.com/winc-link/hummingbird/internal/pkg/constants"
"github.com/winc-link/hummingbird/internal/pkg/container"
"github.com/winc-link/hummingbird/internal/pkg/di"
"github.com/winc-link/hummingbird/internal/pkg/errort"
"github.com/winc-link/hummingbird/internal/pkg/logger"
"github.com/winc-link/hummingbird/internal/pkg/utils"
dingding "github.com/winc-link/hummingbird/internal/tools/notify/dingding"
feishu "github.com/winc-link/hummingbird/internal/tools/notify/feishu"
yiqiweixin "github.com/winc-link/hummingbird/internal/tools/notify/qiyeweixin"
"github.com/winc-link/hummingbird/internal/tools/notify/webapi"
"gorm.io/gorm"
"strconv"
"strings"
"time"
)
type alertApp struct {
dic *di.Container
dbClient interfaces.DBClient
lc logger.LoggingClient
}
func NewAlertCentreApp(ctx context.Context, dic *di.Container) interfaces.AlertRuleApp {
lc := container.LoggingClientFrom(dic.Get)
dbClient := resourceContainer.DBClientFrom(dic.Get)
app := &alertApp{
dic: dic,
dbClient: dbClient,
lc: lc,
}
go app.monitor()
return app
}
func (p alertApp) AddAlertRule(ctx context.Context, req dtos.RuleAddRequest) (string, error) {
var insertAlertRule models.AlertRule
insertAlertRule.Id = utils.RandomNum()
insertAlertRule.Name = req.Name
insertAlertRule.AlertType = constants.DeviceAlertType
//insertAlertRule.Status = constants.RuleStop
insertAlertRule.AlertLevel = req.AlertLevel
insertAlertRule.Description = req.Description
resp, err := p.dbClient.AddAlertRule(insertAlertRule)
if err != nil {
return "", err
}
return resp.Id, nil
}
func (p alertApp) UpdateAlertField(ctx context.Context, req dtos.RuleFieldUpdate) error {
if req.Id == "" {
return errort.NewCommonEdgeX(errort.DefaultReqParamsError, "update req id is required", nil)
}
alertRule, err := p.dbClient.AlertRuleById(req.Id)
if err != nil {
return err
}
dtos.ReplaceRuleFields(&alertRule, req)
err = p.dbClient.GetDBInstance().Table(alertRule.TableName()).Select("*").Updates(alertRule).Error
if err != nil {
return err
}
return nil
}
func (p alertApp) UpdateAlertRule(ctx context.Context, req dtos.RuleUpdateRequest) error {
if req.Id == "" {
return errort.NewCommonEdgeX(errort.DefaultReqParamsError, "update req id is required", nil)
}
if len(req.SubRule) != 1 {
return errors.New("")
}
device, err := p.dbClient.DeviceById(req.SubRule[0].DeviceId)
if err != nil {
return err
}
product, err := p.dbClient.ProductById(device.ProductId)
if err != nil {
return err
}
alertRule, err := p.dbClient.AlertRuleById(req.Id)
if err != nil {
return err
}
if req.SubRule[0].ProductId != device.ProductId {
return errort.NewCommonEdgeX(errort.AlertRuleParamsError, "device product id not equal to req product id", nil)
}
if len(req.Notify) > 0 {
if err = checkNotifyParam(req.Notify); err != nil {
return err
}
}
var sql string
switch req.SubRule[0].Trigger {
case constants.DeviceDataTrigger:
var code string
if v, ok := req.SubRule[0].Option["code"]; ok {
code = v
} else {
return errort.NewCommonEdgeX(errort.AlertRuleParamsError, "update rule code is required", nil)
}
var find bool
var productProperty models.Properties
for _, property := range product.Properties {
if property.Code == code {
find = true
productProperty = property
break
}
}
if !find {
return errort.NewCommonEdgeX(errort.ProductPropertyCodeNotExist, "product property code exist", nil)
}
switch productProperty.TypeSpec.Type {
case constants.SpecsTypeInt, constants.SpecsTypeFloat:
if err = checkSpecsTypeIntOrFloatParam(req.SubRule[0]); err != nil {
return err
}
case constants.SpecsTypeText:
if err = checkSpecsTypeTextParam(req.SubRule[0]); err != nil {
return err
}
case constants.SpecsTypeBool:
if err = checkSpecsTypeBoolParam(req.SubRule[0]); err != nil {
return err
}
case constants.SpecsTypeEnum:
if err = checkSpecsTypeEnumParam(req.SubRule[0]); err != nil {
return err
}
default:
return errort.NewCommonEdgeX(errort.DefaultReqParamsError, "update rule code verify failed", nil)
}
sql = req.BuildEkuiperSql(device.Id, productProperty.TypeSpec.Type)
case constants.DeviceEventTrigger:
var code string
if v, ok := req.SubRule[0].Option["code"]; ok {
code = v
} else {
return errort.NewCommonEdgeX(errort.AlertRuleParamsError, "update rule code is required", nil)
}
var find bool
//var productProperty models.Properties
for _, event := range product.Events {
if event.Code == code {
find = true
//productProperty = property
break
}
}
if !find {
return errort.NewCommonEdgeX(errort.ProductPropertyCodeNotExist, "product event code exist", nil)
}
sqlTemp := `SELECT rule_id(),json_path_query(data, "$.eventTime") as report_time,deviceId FROM mqtt_stream where deviceId = "%s" and messageType = "EVENT_REPORT" and json_path_exists(data, "$.eventCode") = true and json_path_query(data, "$.eventCode") = "%s"`
sql = fmt.Sprintf(sqlTemp, device.Id, code)
case constants.DeviceStatusTrigger:
//{"code":"","device_id":"2499708","end_at":null,"start_at":null}
var status string
deviceStatus := req.SubRule[0].Option["status"]
if deviceStatus == "" {
err = errort.NewCommonEdgeX(errort.DefaultReqParamsError, "required status parameter missing", nil)
return err
}
if deviceStatus == "在线" {
status = constants.DeviceOnline
} else if deviceStatus == "离线" {
status = constants.DeviceOffline
} else {
err = errort.NewCommonEdgeX(errort.DefaultReqParamsError, "required status parameter missing", nil)
return err
}
sqlTemp := `SELECT rule_id(),json_path_query(data, "$.time") as report_time,deviceId FROM mqtt_stream where deviceId = "%s" and messageType = "DEVICE_STATUS" and json_path_exists(data, "$.status") = true and json_path_query(data, "$.status") = "%s"`
sql = fmt.Sprintf(sqlTemp, device.Id, status)
default:
return errort.NewCommonEdgeX(errort.DefaultReqParamsError, "update rule trigger is required", nil)
}
if sql == "" {
return errort.NewCommonEdgeX(errort.AlertRuleParamsError, "sql is null", nil)
}
ekuiperApp := resourceContainer.EkuiperAppFrom(p.dic.Get)
exist, err := ekuiperApp.RuleExist(ctx, alertRule.Id)
if err != nil {
return err
}
configapp := resourceContainer.ConfigurationFrom(p.dic.Get)
if !exist {
if err = ekuiperApp.CreateRule(ctx, dtos.GetRuleAlertEkuiperActions(configapp.Service.Url()), alertRule.Id, sql); err != nil {
return err
}
} else {
if err = ekuiperApp.UpdateRule(ctx, dtos.GetRuleAlertEkuiperActions(configapp.Service.Url()), alertRule.Id, sql); err != nil {
return err
}
}
dtos.ReplaceRuleModelFields(&alertRule, req)
//alertRule.Status = constants.RuleStop
alertRule.DeviceId = device.Id
err = p.dbClient.GetDBInstance().Table(alertRule.TableName()).Select("*").Updates(alertRule).Error
if err != nil {
return err
}
return nil
}
func checkNotifyParam(notify []dtos.Notify) error {
for _, d := range notify {
if !utils.InStringSlice(string(d.Name), constants.GetAlertWays()) {
return errort.NewCommonEdgeX(errort.DefaultReqParamsError, "notify name not in alertways", nil)
}
if d.StartEffectTime == "" {
return errort.NewCommonEdgeX(errort.DefaultReqParamsError, "startEffectTime is required", nil)
}
if d.EndEffectTime == "" {
return errort.NewCommonEdgeX(errort.DefaultReqParamsError, "endEffectTime is required", nil)
}
if !checkEffectTimeParam(d.StartEffectTime, d.EndEffectTime) {
return errort.NewCommonEdgeX(errort.EffectTimeParamsError, "The format of the effective time is"+
" incorrect. The end time should be greater than the start time.", nil)
}
}
return nil
}
func checkSpecsTypeBoolParam(req dtos.SubRule) error {
var decideCondition string
if v, ok := req.Option["decide_condition"]; ok {
decideCondition = v
} else {
return errort.NewCommonEdgeX(errort.DefaultReqParamsError, "update rule decide_condition is required", nil)
}
st := strings.Split(decideCondition, " ")
if len(st) != 2 {
return errort.NewCommonEdgeX(errort.DefaultReqParamsError, "update rule decide_condition verify failed", nil)
}
//if st[0] != "==" {
// return errort.NewCommonEdgeX(errort.DefaultReqParamsError, "update rule decide_condition verify failed", nil)
//}
if !(st[1] == "true" || st[1] == "false") {
return errort.NewCommonEdgeX(errort.DefaultReqParamsError, "update rule decide_condition verify failed", nil)
}
return nil
}
func checkSpecsTypeEnumParam(req dtos.SubRule) error {
var decideCondition string
if v, ok := req.Option["decide_condition"]; ok {
decideCondition = v
} else {
return errort.NewCommonEdgeX(errort.DefaultReqParamsError, "update rule decide_condition is required", nil)
}
st := strings.Split(decideCondition, " ")
if len(st) != 2 {
return errort.NewCommonEdgeX(errort.DefaultReqParamsError, "update rule decide_condition verify failed", nil)
}
//if st[0] != "==" {
// return errort.NewCommonEdgeX(errort.DefaultReqParamsError, "update rule decide_condition verify failed", nil)
//}
if st[0] == "" {
return errort.NewCommonEdgeX(errort.DefaultReqParamsError, "update rule decide_condition verify failed", nil)
}
return nil
}
func checkSpecsTypeTextParam(req dtos.SubRule) error {
var decideCondition string
if v, ok := req.Option["decide_condition"]; ok {
decideCondition = v
} else {
return errort.NewCommonEdgeX(errort.DefaultReqParamsError, "update rule decide_condition is required", nil)
}
st := strings.Split(decideCondition, " ")
if len(st) != 2 {
return errort.NewCommonEdgeX(errort.DefaultReqParamsError, "update rule decide_condition verify failed", nil)
}
//if st[0] != "==" {
// return errort.NewCommonEdgeX(errort.DefaultReqParamsError, "update rule decide_condition verify failed", nil)
//}
if st[1] == "" {
return errort.NewCommonEdgeX(errort.DefaultReqParamsError, "update rule decide_condition verify failed", nil)
}
return nil
}
func checkSpecsTypeIntOrFloatParam(req dtos.SubRule) error {
var valueType, decideCondition string
if v, ok := req.Option["value_type"]; ok {
valueType = v
} else {
return errort.NewCommonEdgeX(errort.DefaultReqParamsError, "update rule value_type is required", nil)
}
find := false
for _, s := range constants.ValueTypes {
if s == valueType {
find = true
break
}
}
if !find {
return errort.NewCommonEdgeX(errort.DefaultReqParamsError, "update rule value_type verify failed", nil)
}
if v, ok := req.Option["decide_condition"]; ok {
decideCondition = v
} else {
return errort.NewCommonEdgeX(errort.DefaultReqParamsError, "update rule decide_condition is required", nil)
}
st := strings.Split(decideCondition, " ")
if len(st) != 2 {
return errort.NewCommonEdgeX(errort.DefaultReqParamsError, "update rule decide_condition verify failed", nil)
}
find = false
for _, condition := range constants.DecideConditions {
if condition == st[0] {
find = true
break
}
}
if !find {
return errort.NewCommonEdgeX(errort.DefaultReqParamsError, "update rule decide_condition verify failed", nil)
}
return nil
}
func (p alertApp) AlertRuleById(ctx context.Context, id string) (dtos.RuleResponse, error) {
alertRule, err := p.dbClient.AlertRuleById(id)
var response dtos.RuleResponse
if err != nil {
return response, err
}
var ruleResponse dtos.RuleResponse
ruleResponse.Id = alertRule.Id
ruleResponse.Name = alertRule.Name
ruleResponse.AlertType = alertRule.AlertType
ruleResponse.AlertLevel = alertRule.AlertLevel
ruleResponse.Status = alertRule.Status
ruleResponse.Condition = alertRule.Condition
ruleResponse.SilenceTime = alertRule.SilenceTime
ruleResponse.Description = alertRule.Description
ruleResponse.Created = alertRule.Created
ruleResponse.Modified = alertRule.Modified
ruleResponse.Notify = alertRule.Notify
if len(ruleResponse.Notify) == 0 {
ruleResponse.Notify = make([]models.SubNotify, 0)
}
var ruleSubRules dtos.RuleSubRules
for _, rule := range alertRule.SubRule {
device, err := p.dbClient.DeviceById(alertRule.DeviceId)
if err != nil {
return response, err
}
product, err := p.dbClient.ProductById(device.ProductId)
if err != nil {
return response, err
}
code := rule.Option["code"]
var (
eventCodeName string
propertyCodeName string
)
for _, event := range product.Events {
if event.Code == code {
eventCodeName = event.Name
}
}
for _, property := range product.Properties {
if property.Code == code {
propertyCodeName = property.Name
}
}
var valueType string
switch rule.Option["value_type"] {
case "original":
valueType = "原始值"
case "avg":
valueType = "平均值"
case "max":
valueType = "最大值"
case "min":
valueType = "最小值"
case "sum":
valueType = "求和值"
}
var condition string
switch rule.Trigger {
case constants.DeviceDataTrigger:
if rule.Option["value_cycle"] == "" {
if valueType == "" {
valueType = "原始值"
}
condition = string(constants.DeviceDataTrigger) + ": 产品: " + product.Name + " | " +
"设备: " + device.Name + " | " +
"功能: " + propertyCodeName + " | " +
"触发条件: " + valueType + " " + rule.Option["decide_condition"]
} else {
condition = string(constants.DeviceDataTrigger) + ": 产品: " + product.Name + " | " +
"设备: " + device.Name + " | " +
"功能: " + propertyCodeName + " | " +
"触发条件: " + valueType + " " + fmt.Sprintf("(%s)", rule.Option["value_cycle"]) + " " + rule.Option["decide_condition"]
}
case constants.DeviceEventTrigger:
condition = string(constants.DeviceEventTrigger) + ": 产品: " + product.Name + " | " +
"设备: " + device.Name + " | " +
fmt.Sprintf("事件 = %s", eventCodeName)
case constants.DeviceStatusTrigger:
condition = string(constants.DeviceStatusTrigger) + ": 产品: " + product.Name + " | " +
"设备: " + device.Name + " | " +
fmt.Sprintf("设备状态 = %s", rule.Option["status"])
default:
condition = ""
}
ruleSubRules = append(ruleSubRules, dtos.RuleSubRule{
ProductId: rule.ProductId,
ProductName: product.Name,
DeviceId: rule.DeviceId,
DeviceName: device.Name,
Trigger: rule.Trigger,
Code: code,
Condition: condition,
Option: rule.Option,
})
}
ruleResponse.SubRule = ruleSubRules
return ruleResponse, nil
}
func (p alertApp) AlertRulesSearch(ctx context.Context, req dtos.AlertRuleSearchQueryRequest) ([]dtos.AlertRuleSearchQueryResponse, uint32, error) {
offset, limit := req.BaseSearchConditionQuery.GetPage()
resp, total, err := p.dbClient.AlertRuleSearch(offset, limit, req)
if err != nil {
return []dtos.AlertRuleSearchQueryResponse{}, 0, err
}
alertRules := make([]dtos.AlertRuleSearchQueryResponse, len(resp))
for i, p := range resp {
alertRules[i] = dtos.RuleSearchQueryResponseFromModel(p)
}
return alertRules, total, nil
}
func (p alertApp) AlertSearch(ctx context.Context, req dtos.AlertSearchQueryRequest) ([]dtos.AlertSearchQueryResponse, uint32, error) {
offset, limit := req.BaseSearchConditionQuery.GetPage()
resp, total, err := p.dbClient.AlertListSearch(offset, limit, req)
if err != nil {
return []dtos.AlertSearchQueryResponse{}, 0, err
}
return resp, total, nil
}
func (p alertApp) AlertPlate(ctx context.Context, beforeTime int64) ([]dtos.AlertPlateQueryResponse, error) {
data, err := p.dbClient.AlertPlate(beforeTime)
if err != nil {
return []dtos.AlertPlateQueryResponse{}, err
}
var dealData []dtos.AlertPlateQueryResponse
dealData = append(append(append(append(dealData, dtos.AlertPlateQueryResponse{
AlertLevel: constants.Urgent,
Count: p.getAlertDataCount(data, constants.Urgent),
}), dtos.AlertPlateQueryResponse{
AlertLevel: constants.Important,
Count: p.getAlertDataCount(data, constants.Important),
}), dtos.AlertPlateQueryResponse{
AlertLevel: constants.LessImportant,
Count: p.getAlertDataCount(data, constants.LessImportant),
}), dtos.AlertPlateQueryResponse{
AlertLevel: constants.Remind,
Count: p.getAlertDataCount(data, constants.Remind),
})
return dealData, nil
}
func (p alertApp) getAlertDataCount(data []dtos.AlertPlateQueryResponse, level constants.AlertLevel) int {
for _, datum := range data {
if datum.AlertLevel == level {
return datum.Count
}
}
return 0
}
func (p alertApp) AlertRulesDelete(ctx context.Context, id string) error {
_, err := p.dbClient.AlertRuleById(id)
if err != nil {
return err
}
ekuiperApp := resourceContainer.EkuiperAppFrom(p.dic.Get)
err = ekuiperApp.DeleteRule(ctx, id)
if err != nil {
return err
}
return p.dbClient.DeleteAlertRuleById(id)
}
func (p alertApp) AlertRulesRestart(ctx context.Context, id string) error {
alertRule, err := p.dbClient.AlertRuleById(id)
if err != nil {
return err
}
if err = p.checkAlertRuleParam(ctx, alertRule, "restart"); err != nil {
return err
}
if alertRule.EkuiperRule() {
ekuiperApp := resourceContainer.EkuiperAppFrom(p.dic.Get)
err = ekuiperApp.RestartRule(ctx, id)
if err != nil {
return err
}
}
return p.dbClient.AlertRuleStart(id)
}
func (p alertApp) AlertRulesStop(ctx context.Context, id string) error {
_, err := p.dbClient.AlertRuleById(id)
if err != nil {
return err
}
ekuiperApp := resourceContainer.EkuiperAppFrom(p.dic.Get)
err = ekuiperApp.StopRule(ctx, id)
if err != nil {
return err
}
return p.dbClient.AlertRuleStop(id)
}
func (p alertApp) AlertRulesStart(ctx context.Context, id string) error {
alertRule, err := p.dbClient.AlertRuleById(id)
if err != nil {
return err
}
if err = p.checkAlertRuleParam(ctx, alertRule, "start"); err != nil {
return err
}
ekuiperApp := resourceContainer.EkuiperAppFrom(p.dic.Get)
err = ekuiperApp.StartRule(ctx, id)
if err != nil {
return err
}
return p.dbClient.AlertRuleStart(id)
}
func (p alertApp) AlertIgnore(ctx context.Context, id string) error {
return p.dbClient.AlertIgnore(id)
}
func (p alertApp) TreatedIgnore(ctx context.Context, id, message string) error {
return p.dbClient.TreatedIgnore(id, message)
}
func (p alertApp) AlertRuleStatus(ctx context.Context, id string) (constants.RuleStatus, error) {
alertRule, err := p.dbClient.AlertRuleById(id)
if err != nil {
return "", err
}
return alertRule.Status, nil
}
func (p alertApp) checkAlertRuleParam(ctx context.Context, rule models.AlertRule, operate string) error {
if operate == "start" {
if rule.Status == constants.RuleStart {
return errort.NewCommonErr(errort.AlertRuleStatusStarting, fmt.Errorf("alertRule id(%s) is runing ,not allow start", rule.Id))
}
}
if rule.AlertType == "" || rule.AlertLevel == "" {
return errort.NewCommonErr(errort.AlertRuleParamsError, fmt.Errorf("alertRule id(%s) alertType or alertLevel is null", rule.Id))
}
if len(rule.SubRule) == 0 {
return errort.NewCommonErr(errort.AlertRuleParamsError, fmt.Errorf("alertRule id(%s) subrule is null", rule.Id))
}
for _, subRule := range rule.SubRule {
if subRule.Trigger == "" {
return errort.NewCommonErr(errort.AlertRuleParamsError, fmt.Errorf("alertRule id(%s) subrule trigger is null", rule.Id))
}
if subRule.ProductId == "" || subRule.DeviceId == "" {
return errort.NewCommonErr(errort.AlertRuleParamsError, fmt.Errorf("alertRule id(%s) device id or product id is null", rule.Id))
}
product, err := p.dbClient.ProductById(subRule.ProductId)
if err != nil {
return errort.NewCommonErr(errort.AlertRuleProductOrDeviceUpdate, fmt.Errorf("alertRule id(%s) device id or product id is null", rule.Id))
}
device, err := p.dbClient.DeviceById(subRule.DeviceId)
if err != nil {
return errort.NewCommonErr(errort.AlertRuleProductOrDeviceUpdate, fmt.Errorf("alertRule id(%s) product or device has been modified. Please edit the rule again", rule.Id))
}
if device.ProductId != product.Id {
return errort.NewCommonErr(errort.AlertRuleProductOrDeviceUpdate, fmt.Errorf("alertRule id(%s) product or device has been modified. Please edit the rule again", rule.Id))
}
code := subRule.Option["code"]
switch subRule.Trigger {
case constants.DeviceDataTrigger:
if code == "" {
return errort.NewCommonErr(errort.AlertRuleParamsError, fmt.Errorf("alertRule id(%s) code is null", rule.Id))
}
var find bool
var typeSpecType constants.SpecsType
for _, property := range product.Properties {
if property.Code == code {
find = true
typeSpecType = property.TypeSpec.Type
break
}
}
if !find {
return errort.NewCommonErr(errort.AlertRuleProductOrDeviceUpdate, fmt.Errorf("alertRule id(%s) product or device has been modified. Please edit the rule again", rule.Id))
}
if !typeSpecType.AllowSendInEkuiper() {
return errort.NewCommonErr(errort.AlertRuleParamsError, fmt.Errorf("alertRule id(%s) %s allowSendInEkuiper", rule.Id, typeSpecType))
}
valueType := subRule.Option["value_type"]
if valueType == "" {
return errort.NewCommonErr(errort.AlertRuleParamsError, fmt.Errorf("alertRule id(%s) valueType is null", rule.Id))
}
var valueTypeFind bool
for _, s := range constants.ValueTypes {
if s == valueType {
valueTypeFind = true
break
}
}
if !valueTypeFind {
return errort.NewCommonErr(errort.AlertRuleParamsError, fmt.Errorf("alertRule id(%s) valueTypeFind error", rule.Id))
}
valueCycle := subRule.Option["value_cycle"]
if valueType != constants.Original {
if valueCycle == "" {
return errort.NewCommonErr(errort.AlertRuleParamsError, fmt.Errorf("alertRule id(%s) valueCycle is null", rule.Id))
}
}
decideCondition := subRule.Option["decide_condition"]
if decideCondition == "" {
return errort.NewCommonErr(errort.AlertRuleParamsError, fmt.Errorf("alertRule id(%s) decideCondition is null", rule.Id))
}
case constants.DeviceEventTrigger:
if code == "" {
return errort.NewCommonErr(errort.AlertRuleParamsError, fmt.Errorf("alertRule id(%s) code is null", rule.Id))
}
var find bool
for _, event := range product.Events {
if event.Code == code {
find = true
break
}
}
if !find {
return errort.NewCommonErr(errort.AlertRuleProductOrDeviceUpdate, fmt.Errorf("alertRule id(%s) product or device has been modified. Please edit the rule again", rule.Id))
}
case constants.DeviceStatusTrigger:
}
}
return nil
}
func (p alertApp) AddAlert(ctx context.Context, req map[string]interface{}) error {
deviceId, ok := req["deviceId"]
if !ok {
return errort.NewCommonErr(errort.DefaultReqParamsError, errors.New(""))
}
ruleId, ok := req["rule_id"]
if !ok {
return errort.NewCommonErr(errort.DefaultReqParamsError, errors.New(""))
}
var (
coverDeviceId string
coverRuleId string
)
switch deviceId.(type) {
case string:
coverDeviceId = deviceId.(string)
case int:
coverDeviceId = strconv.Itoa(deviceId.(int))
case int64:
coverDeviceId = strconv.Itoa(int(deviceId.(int64)))
case float64:
coverDeviceId = fmt.Sprintf("%f", deviceId.(float64))
case float32:
coverDeviceId = fmt.Sprintf("%f", deviceId.(float64))
}
if coverDeviceId == "" {
return errort.NewCommonErr(errort.DefaultReqParamsError, errors.New(""))
}
switch ruleId.(type) {
case string:
coverRuleId = ruleId.(string)
case int:
coverRuleId = strconv.Itoa(ruleId.(int))
case int64:
coverRuleId = strconv.Itoa(int(ruleId.(int64)))
case float64:
coverRuleId = fmt.Sprintf("%f", ruleId.(float64))
case float32:
coverRuleId = fmt.Sprintf("%f", ruleId.(float64))
}
if coverRuleId == "" {
return errort.NewCommonErr(errort.DefaultReqParamsError, errors.New(""))
}
device, err := p.dbClient.DeviceById(coverDeviceId)
if err != nil {
return err
}
product, err := p.dbClient.ProductById(device.ProductId)
if err != nil {
return err
}
alertRule, err := p.dbClient.AlertRuleById(coverRuleId)
if err != nil {
return err
}
alertResult := make(map[string]interface{})
alertResult["device_id"] = device.Id
alertResult["code"] = alertRule.SubRule[0].Option["code"]
if req["window_start"] != nil && req["window_end"] != nil {
p.lc.Info("msg report1:", req["window_start"])
alertResult["start_at"] = req["window_start"]
alertResult["end_at"] = req["window_end"]
} else if req["report_time"] != nil {
reportTime := utils.InterfaceToString(req["report_time"])
if len(reportTime) > 3 {
sa, err := strconv.Atoi(reportTime[0:len(reportTime)-3] + "000")
if err == nil {
alertResult["start_at"] = sa
}
ea, err := strconv.Atoi(reportTime[0:len(reportTime)-3] + "999")
if err == nil {
alertResult["end_at"] = ea
}
}
}
if len(alertRule.SubRule) > 0 {
switch alertRule.SubRule[0].Trigger {
case constants.DeviceEventTrigger:
alertResult["trigger"] = string(constants.DeviceEventTrigger)
case constants.DeviceDataTrigger:
alertResult["trigger"] = string(constants.DeviceDataTrigger)
}
}
var alertList models.AlertList
alertList.AlertRuleId = alertRule.Id
alertList.AlertResult = alertResult
alertList.TriggerTime = time.Now().UnixMilli()
send := false
if alertRule.SilenceTime > 0 {
alertSend, err := p.dbClient.AlertListLastSend(alertRule.Id)
if err != nil {
if err == gorm.ErrRecordNotFound {
send = true
goto Jump
}
} else {
if alertSend.Created+alertRule.SilenceTime <= utils.MakeTimestamp() {
send = true
goto Jump
}
}
}
Jump:
if send == false {
alertList.IsSend = false
_, err = p.dbClient.AddAlertList(alertList)
if err != nil {
return err
}
return nil
}
alertList.IsSend = true
alertList.Status = constants.Untreated
_, err = p.dbClient.AddAlertList(alertList)
if err != nil {
return err
}
for _, notify := range alertRule.Notify {
switch notify.Name {
case constants.SMS:
if !checkEffectTime(notify.StartEffectTime, notify.EndEffectTime) {
continue
}
var phoneNumber string
if v, ok := notify.Option["phoneNumber"]; ok {
phoneNumber = v
}
if phoneNumber == "" {
p.lc.Debug("phoneNumber is null")
continue
}
//templateId templateParamSet 内容请用户自行补充。
var templateId string
var templateParamSet []string
smsApp := resourceContainer.SmsServiceAppFrom(p.dic.Get)
go smsApp.Send(templateId, templateParamSet, []string{phoneNumber})
case constants.PHONE:
case constants.QYweixin:
if !checkEffectTime(notify.StartEffectTime, notify.EndEffectTime) {
continue
}
weixinAlertClient := yiqiweixin.NewWeiXinClient(p.lc, p.dic)
//发送内容请用户自行完善
text := ""
go weixinAlertClient.Send(notify.Option["webhook"], text)
case constants.DingDing:
if !checkEffectTime(notify.StartEffectTime, notify.EndEffectTime) {
continue
}
weixinAlertClient := dingding.NewDingDingClient(p.lc, p.dic)
//发送内容请用户自行完善
text := ""
go weixinAlertClient.Send(notify.Option["webhook"], text)
case constants.FeiShu:
if !checkEffectTime(notify.StartEffectTime, notify.EndEffectTime) {
continue
}
feishuAlertClient := feishu.NewFeishuClient(p.lc, p.dic)
//发送内容请用户自行完善
text := ""
go feishuAlertClient.Send(notify.Option["webhook"], text)
case constants.WEBAPI:
if !checkEffectTime(notify.StartEffectTime, notify.EndEffectTime) {
continue
}
webApiClient := webapi.NewWebApiClient(p.lc, p.dic)
headermap := make([]map[string]string, 0)
if header, ok := notify.Option["header"]; ok {
err := json.Unmarshal([]byte(header), &headermap)
if err != nil {
return err
}
}
go webApiClient.Send(notify.Option["webhook"], headermap, alertRule, device, product, req)
}
}
return nil
}
func checkEffectTime(startTime, endTime string) bool {
timeTemplate := "2006-01-02 15:04:05"
startstamp, _ := time.ParseInLocation(timeTemplate, fmt.Sprintf("%d-%02d-%02d", time.Now().Year(), time.Now().Month(), time.Now().Day())+" "+startTime, time.Local)
endstamp, _ := time.ParseInLocation(timeTemplate, fmt.Sprintf("%d-%02d-%02d", time.Now().Year(), time.Now().Month(), time.Now().Day())+" "+endTime, time.Local)
if startstamp.Unix() < time.Now().Unix() && endstamp.Unix() > time.Now().Unix() {
//发送
return true
} else {
return false
}
}
func checkEffectTimeParam(startTime, endTime string) bool {
timeTemplate := "2006-01-02 15:04:05"
startstamp, _ := time.ParseInLocation(timeTemplate, fmt.Sprintf("%d-%02d-%02d", time.Now().Year(), time.Now().Month(), time.Now().Day())+" "+startTime, time.Local)
endstamp, _ := time.ParseInLocation(timeTemplate, fmt.Sprintf("%d-%02d-%02d", time.Now().Year(), time.Now().Month(), time.Now().Day())+" "+endTime, time.Local)
if endstamp.Unix()-startstamp.Unix() <= 0 {
return false
}
return true
}
func (p alertApp) CheckRuleByProductId(ctx context.Context, productId string) error {
var req dtos.AlertRuleSearchQueryRequest
req.Status = string(constants.RuleStart)
alertRules, _, err := p.AlertRulesSearch(ctx, req)
if err != nil {
return err
}
for _, rule := range alertRules {
for _, subRule := range rule.SubRule {
if subRule.ProductId == productId {
return errort.NewCommonEdgeX(errort.ProductAssociationAlertRule, "This product has been bound"+
" to alarm rules. Please stop reporting relevant alarm rules before proceeding with the operation.", nil)
}
}
}
return nil
}
func (p alertApp) CheckRuleByDeviceId(ctx context.Context, deviceId string) error {
var req dtos.AlertRuleSearchQueryRequest
req.Status = string(constants.RuleStart)
alertRules, _, err := p.AlertRulesSearch(ctx, req)
if err != nil {
return err
}
for _, rule := range alertRules {
for _, subRule := range rule.SubRule {
if subRule.DeviceId == deviceId {
return errort.NewCommonEdgeX(errort.DeviceAssociationAlertRule, "This device has been bound to alarm"+
" rules. Please stop reporting relevant alarm rules before proceeding with the operation", nil)
}
}
}
return nil
}

View File

@ -0,0 +1,61 @@
/*******************************************************************************
* Copyright 2017.
*
* 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 alertcentreapp
import (
"context"
"github.com/winc-link/hummingbird/internal/dtos"
resourceContainer "github.com/winc-link/hummingbird/internal/hummingbird/core/container"
"github.com/winc-link/hummingbird/internal/pkg/constants"
"time"
)
func (p alertApp) monitor() {
tickTime := time.Second * 5
timeTickerChan := time.Tick(tickTime)
for {
select {
case <-timeTickerChan:
p.checkRuleStatus()
}
}
}
func (p alertApp) checkRuleStatus() {
alerts, _, err := p.dbClient.AlertRuleSearch(0, -1, dtos.AlertRuleSearchQueryRequest{})
if err != nil {
p.lc.Errorf("get alerts err:", err)
}
ekuiperApp := resourceContainer.EkuiperAppFrom(p.dic.Get)
for _, alert := range alerts {
if len(alert.SubRule) == 0 {
continue
}
resp, err := ekuiperApp.GetRuleStats(context.Background(), alert.Id)
if err != nil {
continue
}
status, ok := resp["status"]
if ok {
if status != string(alert.Status) {
if status == string(constants.RuleStop) {
p.dbClient.AlertRuleStop(alert.Id)
} else if status == string(constants.RuleStart) {
p.dbClient.AlertRuleStart(alert.Id)
}
}
}
}
}

View File

@ -0,0 +1,122 @@
/*******************************************************************************
* Copyright 2017 Dell Inc.
* Copyright (c) 2019 Intel Corporation
*
* 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 categorytemplate
import (
"context"
"encoding/json"
"github.com/winc-link/hummingbird/internal/dtos"
"github.com/winc-link/hummingbird/internal/models"
"github.com/winc-link/hummingbird/internal/pkg/container"
"github.com/winc-link/hummingbird/internal/pkg/utils"
"time"
resourceContainer "github.com/winc-link/hummingbird/internal/hummingbird/core/container"
interfaces "github.com/winc-link/hummingbird/internal/hummingbird/core/interface"
"github.com/winc-link/hummingbird/internal/pkg/di"
"github.com/winc-link/hummingbird/internal/pkg/logger"
)
type categoryApp struct {
dic *di.Container
dbClient interfaces.DBClient
lc logger.LoggingClient
}
func NewCategoryTemplateApp(ctx context.Context, dic *di.Container) interfaces.CategoryApp {
lc := container.LoggingClientFrom(dic.Get)
dbClient := resourceContainer.DBClientFrom(dic.Get)
return &categoryApp{
dic: dic,
dbClient: dbClient,
lc: lc,
}
}
func (m *categoryApp) CategoryTemplateSearch(ctx context.Context, req dtos.CategoryTemplateRequest) ([]dtos.CategoryTemplateResponse, uint32, error) {
offset, limit := req.BaseSearchConditionQuery.GetPage()
categoryTemplates, total, err := m.dbClient.CategoryTemplateSearch(offset, limit, req)
if err != nil {
m.lc.Errorf("categoryTemplates Search err %v", err)
return []dtos.CategoryTemplateResponse{}, 0, err
}
libs := make([]dtos.CategoryTemplateResponse, len(categoryTemplates))
for i, categoryTemplate := range categoryTemplates {
libs[i] = dtos.CategoryTemplateResponseFromModel(categoryTemplate)
}
return libs, total, nil
}
func (m *categoryApp) Sync(ctx context.Context, versionName string) (int64, error) {
filePath := versionName + "/category_template.json"
cosApp := resourceContainer.CosAppNameFrom(m.dic.Get)
bs, err := cosApp.Get(filePath)
if err != nil {
m.lc.Errorf(err.Error())
return 0, err
}
var cosCategoryTemplateResp []dtos.CosCategoryTemplateResponse
err = json.Unmarshal(bs, &cosCategoryTemplateResp)
if err != nil {
m.lc.Errorf(err.Error())
return 0, err
}
baseQuery := dtos.BaseSearchConditionQuery{
IsAll: true,
}
dbreq := dtos.CategoryTemplateRequest{BaseSearchConditionQuery: baseQuery}
categoryTemplateResponse, _, err := m.CategoryTemplateSearch(ctx, dbreq)
if err != nil {
return 0, err
}
upsertCategoryTemplate := make([]models.CategoryTemplate, 0)
for _, cosCategoryTemplate := range cosCategoryTemplateResp {
var find bool
for _, localTemplateResponse := range categoryTemplateResponse {
if cosCategoryTemplate.CategoryKey == localTemplateResponse.CategoryKey {
upsertCategoryTemplate = append(upsertCategoryTemplate, models.CategoryTemplate{
Id: localTemplateResponse.Id,
CategoryName: cosCategoryTemplate.CategoryName,
CategoryKey: cosCategoryTemplate.CategoryKey,
Scene: cosCategoryTemplate.Scene,
})
find = true
break
}
}
if !find {
upsertCategoryTemplate = append(upsertCategoryTemplate, models.CategoryTemplate{
Timestamps: models.Timestamps{
Created: time.Now().Unix(),
},
Id: utils.GenUUID(),
CategoryName: cosCategoryTemplate.CategoryName,
CategoryKey: cosCategoryTemplate.CategoryKey,
Scene: cosCategoryTemplate.Scene,
})
}
}
rows, err := m.dbClient.BatchUpsertCategoryTemplate(upsertCategoryTemplate)
m.lc.Infof("upsert category template rows %+v", rows)
if err != nil {
return 0, err
}
return rows, nil
}

View File

@ -0,0 +1,371 @@
/*******************************************************************************
* Copyright 2017.
*
* 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 dataresource
import (
"context"
"database/sql"
"fmt"
mqtt "github.com/eclipse/paho.mqtt.golang"
"github.com/mitchellh/mapstructure"
"github.com/winc-link/hummingbird/internal/dtos"
resourceContainer "github.com/winc-link/hummingbird/internal/hummingbird/core/container"
interfaces "github.com/winc-link/hummingbird/internal/hummingbird/core/interface"
"github.com/winc-link/hummingbird/internal/models"
"github.com/winc-link/hummingbird/internal/pkg/constants"
"github.com/winc-link/hummingbird/internal/pkg/container"
"github.com/winc-link/hummingbird/internal/pkg/di"
"github.com/winc-link/hummingbird/internal/pkg/errort"
"github.com/winc-link/hummingbird/internal/pkg/logger"
"net/url"
"time"
_ "github.com/influxdata/influxdb1-client/v2"
client "github.com/influxdata/influxdb1-client/v2"
//_ "github.com/taosdata/driver-go/v2/taosSql"
)
type dataResourceApp struct {
dic *di.Container
dbClient interfaces.DBClient
lc logger.LoggingClient
}
func NewDataResourceApp(ctx context.Context, dic *di.Container) interfaces.DataResourceApp {
lc := container.LoggingClientFrom(dic.Get)
dbClient := resourceContainer.DBClientFrom(dic.Get)
app := &dataResourceApp{
dic: dic,
dbClient: dbClient,
lc: lc,
}
return app
}
func (p dataResourceApp) AddDataResource(ctx context.Context, req dtos.AddDataResourceReq) (string, error) {
var insertDataResource models.DataResource
insertDataResource.Name = req.Name
insertDataResource.Type = constants.DataResourceType(req.Type)
insertDataResource.Option = req.Option
insertDataResource.Option["sendSingle"] = true
id, err := p.dbClient.AddDataResource(insertDataResource)
if err != nil {
return "", err
}
return id, nil
}
func (p dataResourceApp) DataResourceById(ctx context.Context, id string) (models.DataResource, error) {
if id == "" {
return models.DataResource{}, errort.NewCommonEdgeX(errort.DefaultReqParamsError, "req id is required", nil)
}
dataResource, edgeXErr := p.dbClient.DataResourceById(id)
if edgeXErr != nil {
return models.DataResource{}, edgeXErr
}
return dataResource, nil
}
func (p dataResourceApp) UpdateDataResource(ctx context.Context, req dtos.UpdateDataResource) error {
if req.Id == "" {
return errort.NewCommonEdgeX(errort.DefaultReqParamsError, "update req id is required", nil)
}
dataResource, edgeXErr := p.dbClient.DataResourceById(req.Id)
if edgeXErr != nil {
return edgeXErr
}
ruleEngines, _, err := p.dbClient.RuleEngineSearch(0, -1, dtos.RuleEngineSearchQueryRequest{
Status: string(constants.RuleEngineStart),
})
if err != nil {
return err
}
for _, engine := range ruleEngines {
if engine.DataResourceId == req.Id {
return errort.NewCommonErr(errort.RuleEngineIsStartingNotAllowUpdate, fmt.Errorf("please stop this rule engine (%s) before editing it", req.Id))
}
}
dtos.ReplaceDataResourceModelFields(&dataResource, req)
edgeXErr = p.dbClient.UpdateDataResource(dataResource)
if edgeXErr != nil {
return edgeXErr
}
return nil
}
func (p dataResourceApp) DelDataResourceById(ctx context.Context, id string) error {
ruleEngines, _, err := p.dbClient.RuleEngineSearch(0, -1, dtos.RuleEngineSearchQueryRequest{
Status: string(constants.RuleEngineStart),
})
if err != nil {
return err
}
for _, engine := range ruleEngines {
if engine.DataResourceId == id {
return errort.NewCommonErr(errort.RuleEngineIsStartingNotAllowUpdate, fmt.Errorf("please stop this rule engine (%s) before editing it", id))
}
}
err = p.dbClient.DelDataResource(id)
if err != nil {
return err
}
return nil
}
func (p dataResourceApp) DataResourceSearch(ctx context.Context, req dtos.DataResourceSearchQueryRequest) ([]models.DataResource, uint32, error) {
offset, limit := req.BaseSearchConditionQuery.GetPage()
resp, total, err := p.dbClient.SearchDataResource(offset, limit, req)
if err != nil {
return []models.DataResource{}, 0, err
}
return resp, total, nil
}
func (p dataResourceApp) DataResourceType(ctx context.Context) []constants.DataResourceType {
return constants.DataResources
}
func (p dataResourceApp) DataResourceHealth(ctx context.Context, resourceId string) error {
dataResource, err := p.dbClient.DataResourceById(resourceId)
if err != nil {
return err
}
//return p.dbClient.UpdateDataResourceHealth(dataResource.Id, true)
switch dataResource.Type {
case constants.HttpResource:
err = p.checkHttpResourceHealth(dataResource)
case constants.MQTTResource:
err = p.checkMQTTResourceHealth(dataResource)
case constants.KafkaResource:
err = p.checkKafkaResourceHealth(dataResource)
case constants.InfluxDBResource:
err = p.checkInfluxDBResourceHealth(dataResource)
case constants.TDengineResource:
//err = p.checkTdengineResourceHealth(dataResource)
default:
return errort.NewCommonErr(errort.DefaultReqParamsError, fmt.Errorf("resource type not much"))
}
if err != nil {
return err
}
return p.dbClient.UpdateDataResourceHealth(dataResource.Id, true)
}
func (p dataResourceApp) checkHttpResourceHealth(resource models.DataResource) error {
urlAddr := resource.Option["url"].(string)
_, err := url.Parse(urlAddr)
if err != nil {
return err
}
return nil
}
func (p dataResourceApp) checkMQTTResourceHealth(resource models.DataResource) error {
var (
server, topic string
clientId, username, password string
//certificationPath,
//privateKeyPath, rootCaPath, insecureSkipVerify, retained, compression, connectionSelector string
)
server = resource.Option["server"].(string)
topic = resource.Option["topic"].(string)
clientId = resource.Option["clientId"].(string)
//protocolVersion = resource.Option["protocolVersion"]
//qos = resource.Option["qos"].(int)
username = resource.Option["username"].(string)
password = resource.Option["password"].(string)
//certificationPath = resource.Option["certificationPath"]
//privateKeyPath = resource.Option["privateKeyPath"]
//rootCaPath = resource.Option["rootCaPath"]
//insecureSkipVerify = resource.Option["insecureSkipVerify"]
//retained = resource.Option["retained"]
//compression = resource.Option["compression"]
//connectionSelector = resource.Option["connectionSelector"]
if server == "" || topic == "" || clientId == "" || username == "" || password == "" {
}
opts := mqtt.NewClientOptions()
opts.AddBroker(server)
opts.SetUsername(username)
opts.SetPassword(password)
opts.SetClientID(clientId)
client := mqtt.NewClient(opts)
token := client.Connect()
// 如果连接失败,则终止程序
if token.WaitTimeout(3*time.Second) && token.Error() != nil {
return token.Error()
}
defer client.Disconnect(250)
return nil
}
func (p dataResourceApp) checkKafkaResourceHealth(resource models.DataResource) error {
return nil
}
func (p dataResourceApp) checkInfluxDBResourceHealth(resource models.DataResource) error {
type influxSink struct {
addr string
username string
password string
measurement string
databaseName string
tagKey string
tagValue string
fields string
cli client.Client
fieldMap map[string]interface{}
hasTransform bool
}
var m influxSink
if i, ok := resource.Option["addr"]; ok {
if i, ok := i.(string); ok {
m.addr = i
}
}
if i, ok := resource.Option["username"]; ok {
if i, ok := i.(string); ok {
m.username = i
}
}
if i, ok := resource.Option["password"]; ok {
if i, ok := i.(string); ok {
m.password = i
}
}
if i, ok := resource.Option["measurement"]; ok {
if i, ok := i.(string); ok {
m.measurement = i
}
}
if i, ok := resource.Option["databasename"]; ok {
if i, ok := i.(string); ok {
m.databaseName = i
}
}
if i, ok := resource.Option["tagkey"]; ok {
if i, ok := i.(string); ok {
m.tagKey = i
}
}
if i, ok := resource.Option["tagvalue"]; ok {
if i, ok := i.(string); ok {
m.tagValue = i
}
}
if i, ok := resource.Option["fields"]; ok {
if i, ok := i.(string); ok {
m.fields = i
}
}
if i, ok := resource.Option["dataTemplate"]; ok {
if i, ok := i.(string); ok && i != "" {
m.hasTransform = true
}
}
_, err := client.NewHTTPClient(client.HTTPConfig{
Addr: m.addr,
Username: m.username,
Password: m.password,
})
if err != nil {
return err
}
return nil
}
func (p dataResourceApp) checkTdengineResourceHealth(resource models.DataResource) error {
type taosConfig struct {
ProvideTs bool `json:"provideTs"`
Port int `json:"port"`
Ip string `json:"ip"` // To be deprecated
Host string `json:"host"`
User string `json:"user"`
Password string `json:"password"`
Database string `json:"database"`
Table string `json:"table"`
TsFieldName string `json:"tsFieldName"`
Fields []string `json:"fields"`
STable string `json:"sTable"`
TagFields []string `json:"tagFields"`
DataTemplate string `json:"dataTemplate"`
TableDataField string `json:"tableDataField"`
}
cfg := &taosConfig{
User: "root",
Password: "taosdata",
}
err := MapToStruct(resource.Option, cfg)
if err != nil {
return fmt.Errorf("read properties %v fail with error: %v", resource.Option, err)
}
if cfg.Ip != "" {
fmt.Errorf("Deprecated: Tdengine sink ip property is deprecated, use host instead.")
if cfg.Host == "" {
cfg.Host = cfg.Ip
}
}
if cfg.Host == "" {
cfg.Host = "localhost"
}
if cfg.User == "" {
return fmt.Errorf("propert user is required.")
}
if cfg.Password == "" {
return fmt.Errorf("propert password is required.")
}
if cfg.Database == "" {
return fmt.Errorf("property database is required")
}
if cfg.Table == "" {
return fmt.Errorf("property table is required")
}
if cfg.TsFieldName == "" {
return fmt.Errorf("property TsFieldName is required")
}
if cfg.STable != "" && len(cfg.TagFields) == 0 {
return fmt.Errorf("property tagFields is required when sTable is set")
}
url := fmt.Sprintf(`%s:%s@tcp(%s:%d)/%s`, cfg.User, cfg.Password, cfg.Host, cfg.Port, cfg.Database)
//m.conf = cfg
_, err = sql.Open("taosSql", url)
if err != nil {
return err
}
return nil
}
func MapToStruct(input, output interface{}) error {
config := &mapstructure.DecoderConfig{
TagName: "json",
Result: output,
}
decoder, err := mapstructure.NewDecoder(config)
if err != nil {
return err
}
return decoder.Decode(input)
}

View File

@ -0,0 +1,118 @@
/*******************************************************************************
* Copyright 2017 Dell Inc.
*
* 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 deviceapp
import (
"context"
"github.com/docker/distribution/uuid"
"github.com/winc-link/hummingbird/internal/hummingbird/core/container"
"github.com/winc-link/hummingbird/internal/pkg/constants"
"github.com/winc-link/hummingbird/internal/pkg/errort"
"strconv"
"github.com/winc-link/edge-driver-proto/drivercommon"
"github.com/winc-link/edge-driver-proto/driverdevice"
)
func (p deviceApp) ConnectIotPlatform(ctx context.Context, request *driverdevice.ConnectIotPlatformRequest) *driverdevice.ConnectIotPlatformResponse {
response := new(driverdevice.ConnectIotPlatformResponse)
baseResponse := new(drivercommon.CommonResponse)
baseResponse.Success = false
deviceInfo, err := p.DeviceById(ctx, request.DeviceId)
if err != nil {
errWrapper := errort.NewCommonEdgeXWrapper(err)
baseResponse.Code = strconv.Itoa(int(errWrapper.Code()))
baseResponse.ErrorMessage = errWrapper.Message()
response.BaseResponse = baseResponse
return response
}
//把消息投体进入消息总线
messageApp := container.MessageItfFrom(p.dic.Get)
messageApp.DeviceStatusToMessageBus(ctx, deviceInfo.Id, constants.DeviceOnline)
err = p.dbClient.DeviceOnlineById(request.DeviceId)
if err != nil {
errWrapper := errort.NewCommonEdgeXWrapper(err)
baseResponse.Code = strconv.Itoa(int(errWrapper.Code()))
baseResponse.ErrorMessage = errWrapper.Message()
response.BaseResponse = baseResponse
return response
} else {
baseResponse.Success = true
baseResponse.RequestId = uuid.Generate().String()
response.Data = new(driverdevice.ConnectIotPlatformResponse_Data)
response.Data.Status = driverdevice.ConnectStatus_ONLINE
response.BaseResponse = baseResponse
return response
}
}
func (p deviceApp) DisConnectIotPlatform(ctx context.Context, request *driverdevice.DisconnectIotPlatformRequest) *driverdevice.DisconnectIotPlatformResponse {
deviceInfo, err := p.DeviceById(ctx, request.DeviceId)
response := new(driverdevice.DisconnectIotPlatformResponse)
baseResponse := new(drivercommon.CommonResponse)
baseResponse.Success = false
if err != nil {
errWrapper := errort.NewCommonEdgeXWrapper(err)
baseResponse.Code = strconv.Itoa(int(errWrapper.Code()))
baseResponse.ErrorMessage = errWrapper.Message()
response.BaseResponse = baseResponse
return response
}
messageApp := container.MessageItfFrom(p.dic.Get)
messageApp.DeviceStatusToMessageBus(ctx, deviceInfo.Id, constants.DeviceOffline)
err = p.dbClient.DeviceOfflineById(request.DeviceId)
if err != nil {
errWrapper := errort.NewCommonEdgeXWrapper(err)
baseResponse.Code = strconv.Itoa(int(errWrapper.Code()))
baseResponse.ErrorMessage = errWrapper.Message()
response.BaseResponse = baseResponse
return response
}
baseResponse.Success = true
baseResponse.RequestId = uuid.Generate().String()
response.Data = new(driverdevice.DisconnectIotPlatformResponse_Data)
response.Data.Status = driverdevice.ConnectStatus_OFFLINE
response.BaseResponse = baseResponse
return response
}
func (p deviceApp) GetDeviceConnectStatus(ctx context.Context, request *driverdevice.GetDeviceConnectStatusRequest) *driverdevice.GetDeviceConnectStatusResponse {
deviceInfo, err := p.DeviceById(ctx, request.DeviceId)
response := new(driverdevice.GetDeviceConnectStatusResponse)
baseResponse := new(drivercommon.CommonResponse)
baseResponse.Success = false
if err != nil {
errWrapper := errort.NewCommonEdgeXWrapper(err)
baseResponse.Code = strconv.Itoa(int(errWrapper.Code()))
baseResponse.ErrorMessage = errWrapper.Message()
response.BaseResponse = baseResponse
return response
}
baseResponse.Success = true
baseResponse.RequestId = uuid.Generate().String()
response.Data = new(driverdevice.GetDeviceConnectStatusResponse_Data)
if deviceInfo.Status == constants.DeviceStatusOnline {
response.Data.Status = driverdevice.ConnectStatus_ONLINE
} else {
response.Data.Status = driverdevice.ConnectStatus_OFFLINE
}
response.BaseResponse = baseResponse
return response
}

View File

@ -0,0 +1,270 @@
/*******************************************************************************
* Copyright 2017.
*
* 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 deviceapp
import (
"context"
"encoding/json"
"github.com/docker/distribution/uuid"
"github.com/winc-link/edge-driver-proto/thingmodel"
"github.com/winc-link/hummingbird/internal/dtos"
"github.com/winc-link/hummingbird/internal/hummingbird/core/container"
"github.com/winc-link/hummingbird/internal/pkg/constants"
"github.com/winc-link/hummingbird/internal/pkg/di"
"github.com/winc-link/hummingbird/internal/tools/rpcclient"
"time"
)
const (
ResultSuccess = "success"
ResultFail = "fail"
)
// ui控制台、定时任务、场景联动、云平台api 都可以调用
func (p *deviceApp) DeviceAction(jobAction dtos.JobAction) dtos.DeviceExecRes {
defer func() {
if err := recover(); err != nil {
p.lc.Error("CreateDeviceCallBack Panic:", err)
}
}()
device, err := p.dbClient.DeviceById(jobAction.DeviceId)
if err != nil {
return dtos.DeviceExecRes{
Result: false,
Message: "device not found",
}
}
deviceService, err := p.dbClient.DeviceServiceById(device.DriveInstanceId)
if err != nil {
return dtos.DeviceExecRes{
Result: false,
Message: "driver not found",
}
}
driverService := container.DriverServiceAppFrom(di.GContainer.Get)
status := driverService.GetState(deviceService.Id)
if status == constants.RunStatusStarted {
client, errX := rpcclient.NewDriverRpcClient(deviceService.BaseAddress, false, "", deviceService.Id, p.lc)
if errX != nil {
return dtos.DeviceExecRes{
Result: false,
Message: errX.Error(),
}
}
defer client.Close()
var rpcRequest thingmodel.ThingModelIssueMsg
rpcRequest.DeviceId = jobAction.DeviceId
rpcRequest.OperationType = thingmodel.OperationType_PROPERTY_SET
var data dtos.PropertySet
data.Version = "v1.0"
data.MsgId = uuid.Generate().String()
data.Time = time.Now().UnixMilli()
param := make(map[string]interface{})
param[jobAction.Code] = jobAction.Value
data.Params = param
rpcRequest.Data = data.ToString()
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
defer cancel()
_, err = client.ThingModelDownServiceClient.ThingModelMsgIssue(ctx, &rpcRequest)
if err == nil {
return dtos.DeviceExecRes{
Result: true,
Message: ResultSuccess,
}
} else {
return dtos.DeviceExecRes{
Result: false,
Message: err.Error(),
}
}
}
return dtos.DeviceExecRes{
Result: false,
Message: "driver status stop",
}
}
func (p *deviceApp) DeviceInvokeThingService(invokeDeviceServiceReq dtos.InvokeDeviceServiceReq) dtos.DeviceExecRes {
defer func() {
if err := recover(); err != nil {
p.lc.Error("CreateDeviceCallBack Panic:", err)
}
}()
device, err := p.dbClient.DeviceById(invokeDeviceServiceReq.DeviceId)
if err != nil {
return dtos.DeviceExecRes{
Result: false,
Message: "device not found",
}
}
product, err := p.dbClient.ProductById(device.ProductId)
if err != nil {
return dtos.DeviceExecRes{
Result: false,
Message: "product not found",
}
}
var find bool
var callType constants.CallType
for _, action := range product.Actions {
if action.Code == invokeDeviceServiceReq.Code {
find = true
callType = action.CallType
}
}
if !find {
return dtos.DeviceExecRes{
Result: false,
Message: "code not found",
}
}
deviceService, err := p.dbClient.DeviceServiceById(device.DriveInstanceId)
if err != nil {
return dtos.DeviceExecRes{
Result: false,
Message: "driver not found",
}
}
driverService := container.DriverServiceAppFrom(di.GContainer.Get)
status := driverService.GetState(deviceService.Id)
if status == constants.RunStatusStarted {
client, errX := rpcclient.NewDriverRpcClient(deviceService.BaseAddress, false, "", deviceService.Id, p.lc)
if errX != nil {
return dtos.DeviceExecRes{
Result: false,
Message: errX.Error(),
}
}
defer client.Close()
var rpcRequest thingmodel.ThingModelIssueMsg
rpcRequest.DeviceId = invokeDeviceServiceReq.DeviceId
rpcRequest.OperationType = thingmodel.OperationType_SERVICE_EXECUTE
var data dtos.InvokeDeviceService
data.Version = "v1.0"
data.MsgId = uuid.Generate().String()
data.Time = time.Now().UnixMilli()
data.Data.Code = invokeDeviceServiceReq.Code
data.Data.InputParams = invokeDeviceServiceReq.Items
rpcRequest.Data = data.ToString()
if callType == constants.CallTypeAsync {
//saveServiceInfo := genSaveServiceInfo(data.MsgId, data.Time, invokeDeviceServiceReq)
var saveServiceInfo dtos.ThingModelMessage
saveServiceInfo.OpType = int32(thingmodel.OperationType_SERVICE_EXECUTE)
saveServiceInfo.Cid = device.Id
var saveData dtos.SaveServiceIssueData
saveData.MsgId = data.MsgId
saveData.Code = invokeDeviceServiceReq.Code
saveData.Time = data.Time
saveData.InputParams = invokeDeviceServiceReq.Items
saveData.OutputParams = map[string]interface{}{
"result": true,
"message": "success",
}
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
defer cancel()
_, err = client.ThingModelDownServiceClient.ThingModelMsgIssue(ctx, &rpcRequest)
if err == nil {
persistItf := container.PersistItfFrom(p.dic.Get)
_ = persistItf.SaveDeviceThingModelData(saveServiceInfo)
return dtos.DeviceExecRes{
Result: true,
Message: ResultSuccess,
}
} else {
return dtos.DeviceExecRes{
Result: false,
Message: err.Error(),
}
}
} else if callType == constants.CallTypeSync {
var saveServiceInfo dtos.ThingModelMessage
saveServiceInfo.OpType = int32(thingmodel.OperationType_SERVICE_EXECUTE)
saveServiceInfo.Cid = device.Id
var saveData dtos.SaveServiceIssueData
saveData.MsgId = data.MsgId
saveData.Code = invokeDeviceServiceReq.Code
saveData.Time = data.Time
saveData.InputParams = invokeDeviceServiceReq.Items
persistItf := container.PersistItfFrom(p.dic.Get)
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
defer cancel()
_, err = client.ThingModelDownServiceClient.ThingModelMsgIssue(ctx, &rpcRequest)
if err != nil {
return dtos.DeviceExecRes{
Result: false,
Message: err.Error(),
}
}
messageStore := container.MessageStoreItfFrom(p.dic.Get)
ch := messageStore.GenAckChan(data.MsgId)
select {
case <-time.After(5 * time.Second):
ch.TryCloseChan()
saveData.OutputParams = map[string]interface{}{
"result": false,
"message": "wait response timeout",
}
s, _ := json.Marshal(saveData)
saveServiceInfo.Data = string(s)
_ = persistItf.SaveDeviceThingModelData(saveServiceInfo)
return dtos.DeviceExecRes{
Result: false,
Message: "wait response timeout",
}
case <-ctx.Done():
saveData.OutputParams = map[string]interface{}{
"result": false,
"message": "wait response timeout",
}
s, _ := json.Marshal(saveData)
saveServiceInfo.Data = string(s)
_ = persistItf.SaveDeviceThingModelData(saveServiceInfo)
return dtos.DeviceExecRes{
Result: false,
Message: "wait response timeout",
}
case resp := <-ch.DataChan:
if v, ok := resp.(map[string]interface{}); ok {
saveData.OutputParams = v
s, _ := json.Marshal(saveData)
saveServiceInfo.Data = string(s)
_ = persistItf.SaveDeviceThingModelData(saveServiceInfo)
message, _ := json.Marshal(v)
return dtos.DeviceExecRes{
Result: true,
Message: string(message),
}
}
}
}
}
return dtos.DeviceExecRes{
Result: false,
Message: "driver status stop",
}
}

View File

@ -0,0 +1,577 @@
/*******************************************************************************
* Copyright 2017 Dell Inc.
* Copyright (c) 2019 Intel Corporation
*
* 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 deviceapp
import (
"context"
"fmt"
"github.com/winc-link/hummingbird/internal/dtos"
resourceContainer "github.com/winc-link/hummingbird/internal/hummingbird/core/container"
interfaces "github.com/winc-link/hummingbird/internal/hummingbird/core/interface"
"github.com/winc-link/hummingbird/internal/models"
"github.com/winc-link/hummingbird/internal/pkg/constants"
"github.com/winc-link/hummingbird/internal/pkg/container"
"github.com/winc-link/hummingbird/internal/pkg/di"
"github.com/winc-link/hummingbird/internal/pkg/errort"
"github.com/winc-link/hummingbird/internal/pkg/logger"
"github.com/winc-link/hummingbird/internal/pkg/utils"
"time"
)
type deviceApp struct {
//*propertyTyApp
dic *di.Container
dbClient interfaces.DBClient
lc logger.LoggingClient
}
func NewDeviceApp(ctx context.Context, dic *di.Container) interfaces.DeviceItf {
lc := container.LoggingClientFrom(dic.Get)
dbClient := resourceContainer.DBClientFrom(dic.Get)
return &deviceApp{
dic: dic,
dbClient: dbClient,
lc: lc,
}
}
func (p deviceApp) DeviceById(ctx context.Context, id string) (dtos.DeviceInfoResponse, error) {
device, err := p.dbClient.DeviceById(id)
var response dtos.DeviceInfoResponse
if err != nil {
return response, err
}
var deviceServiceName string
deviceService, err := p.dbClient.DeviceServiceById(device.DriveInstanceId)
if err != nil {
deviceServiceName = deviceService.Name
}
response = dtos.DeviceInfoResponseFromModel(device, deviceServiceName)
return response, nil
}
func (p deviceApp) OpenApiDeviceById(ctx context.Context, id string) (dtos.OpenApiDeviceInfoResponse, error) {
device, err := p.dbClient.DeviceById(id)
var response dtos.OpenApiDeviceInfoResponse
if err != nil {
return response, err
}
response = dtos.OpenApiDeviceInfoResponseFromModel(device)
return response, nil
}
func (p deviceApp) OpenApiDeviceStatusById(ctx context.Context, id string) (dtos.OpenApiDeviceStatus, error) {
device, err := p.dbClient.DeviceById(id)
var response dtos.OpenApiDeviceStatus
if err != nil {
return response, err
}
response.Status = device.Status
return response, nil
}
func (p deviceApp) DeviceByCloudId(ctx context.Context, id string) (models.Device, error) {
return p.dbClient.DeviceByCloudId(id)
}
func (p deviceApp) DeviceModelById(ctx context.Context, id string) (models.Device, error) {
return p.dbClient.DeviceById(id)
}
func (p *deviceApp) DevicesSearch(ctx context.Context, req dtos.DeviceSearchQueryRequest) ([]dtos.DeviceSearchQueryResponse, uint32, error) {
offset, limit := req.BaseSearchConditionQuery.GetPage()
resp, total, err := p.dbClient.DevicesSearch(offset, limit, req)
if err != nil {
return []dtos.DeviceSearchQueryResponse{}, 0, err
}
devices := make([]dtos.DeviceSearchQueryResponse, len(resp))
for i, dev := range resp {
deviceService, _ := p.dbClient.DeviceServiceById(dev.DriveInstanceId)
devices[i] = dtos.DeviceResponseFromModel(dev, deviceService.Name)
}
return devices, total, nil
}
func (p *deviceApp) OpenApiDevicesSearch(ctx context.Context, req dtos.DeviceSearchQueryRequest) ([]dtos.OpenApiDeviceInfoResponse, uint32, error) {
offset, limit := req.BaseSearchConditionQuery.GetPage()
resp, total, err := p.dbClient.DevicesSearch(offset, limit, req)
if err != nil {
return []dtos.OpenApiDeviceInfoResponse{}, 0, err
}
devices := make([]dtos.OpenApiDeviceInfoResponse, len(resp))
for i, device := range resp {
devices[i] = dtos.OpenApiDeviceInfoResponseFromModel(device)
}
return devices, total, nil
}
func (p *deviceApp) DevicesModelSearch(ctx context.Context, req dtos.DeviceSearchQueryRequest) ([]models.Device, uint32, error) {
offset, limit := req.BaseSearchConditionQuery.GetPage()
return p.dbClient.DevicesSearch(offset, limit, req)
}
func (p *deviceApp) AddDevice(ctx context.Context, req dtos.DeviceAddRequest) (string, error) {
if req.DriverInstanceId != "" {
driverInstance, err := p.dbClient.DeviceServiceById(req.DriverInstanceId)
if err != nil {
return "", err
}
if driverInstance.Platform != "" && driverInstance.Platform != constants.IotPlatform_LocalIot {
return "", errort.NewCommonErr(errort.DeviceServiceMustLocalPlatform, fmt.Errorf("please sync product data"))
}
}
productInfo, err := p.dbClient.ProductById(req.ProductId)
if productInfo.Status == constants.ProductUnRelease {
return "", errort.NewCommonEdgeX(errort.ProductUnRelease, "The product has not been released yet. Please release the product before adding devices", nil)
}
deviceId := utils.RandomNum()
if err != nil {
return "", err
}
err = resourceContainer.DataDBClientFrom(p.dic.Get).CreateTable(ctx, constants.DB_PREFIX+productInfo.Id, deviceId)
if err != nil {
return "", err
}
var insertDevice models.Device
insertDevice.Id = deviceId
insertDevice.Name = req.Name
insertDevice.ProductId = req.ProductId
insertDevice.Platform = constants.IotPlatform_LocalIot
insertDevice.DriveInstanceId = req.DriverInstanceId
insertDevice.Status = constants.DeviceStatusOffline
insertDevice.Secret = utils.GenerateDeviceSecret(12)
insertDevice.Description = req.Description
id, err := p.dbClient.AddDevice(insertDevice)
if err != nil {
return "", err
}
return id, nil
}
func (p *deviceApp) BatchDeleteDevice(ctx context.Context, ids []string) error {
var searchReq dtos.DeviceSearchQueryRequest
searchReq.BaseSearchConditionQuery.Ids = dtos.ApiParamsArrayToString(ids)
devices, _, err := p.dbClient.DevicesSearch(0, -1, searchReq)
if err != nil {
return err
}
alertApp := resourceContainer.AlertRuleAppNameFrom(p.dic.Get)
for _, device := range devices {
edgeXErr := alertApp.CheckRuleByDeviceId(ctx, device.Id)
if edgeXErr != nil {
return edgeXErr
}
}
err = p.dbClient.BatchDeleteDevice(ids)
if err != nil {
return err
}
for _, device := range devices {
delDevice := device
go func() {
p.DeleteDeviceCallBack(delDevice)
}()
}
return nil
}
func (p *deviceApp) DeviceMqttAuthInfo(ctx context.Context, id string) (dtos.DeviceAuthInfoResponse, error) {
mqttAuth, err := p.dbClient.DeviceMqttAuthInfo(id)
var response dtos.DeviceAuthInfoResponse
if err != nil {
return response, err
}
response = dtos.DeviceAuthInfoResponseFromModel(mqttAuth)
return response, nil
}
func (p *deviceApp) AddMqttAuth(ctx context.Context, req dtos.AddMqttAuthInfoRequest) (string, error) {
var mqttAuth models.MqttAuth
mqttAuth.ClientId = req.ClientId
mqttAuth.UserName = req.UserName
mqttAuth.Password = req.Password
mqttAuth.ResourceId = req.ResourceId
mqttAuth.ResourceType = constants.ResourceType(req.ResourceType)
return p.dbClient.AddMqttAuthInfo(mqttAuth)
}
func (p *deviceApp) DeleteDeviceById(ctx context.Context, id string) error {
deviceInfo, err := p.dbClient.DeviceById(id)
if err != nil {
return err
}
alertApp := resourceContainer.AlertRuleAppNameFrom(p.dic.Get)
edgeXErr := alertApp.CheckRuleByDeviceId(ctx, id)
if edgeXErr != nil {
return edgeXErr
}
sceneApp := resourceContainer.SceneAppNameFrom(p.dic.Get)
edgeXErr = sceneApp.CheckSceneByDeviceId(ctx, id)
if edgeXErr != nil {
return edgeXErr
}
err = p.dbClient.DeleteDeviceById(id)
if err != nil {
return err
}
_ = resourceContainer.DataDBClientFrom(p.dic.Get).DropTable(ctx, id)
go func() {
p.DeleteDeviceCallBack(models.Device{
Id: deviceInfo.Id,
DriveInstanceId: deviceInfo.DriveInstanceId,
})
}()
return nil
}
func (p *deviceApp) DeviceUpdate(ctx context.Context, req dtos.DeviceUpdateRequest) error {
if req.Id == "" {
return errort.NewCommonEdgeX(errort.DefaultReqParamsError, "update req id is required", nil)
}
device, edgeXErr := p.dbClient.DeviceById(req.Id)
if edgeXErr != nil {
return edgeXErr
}
if device.Platform != constants.IotPlatform_LocalIot {
}
alertApp := resourceContainer.AlertRuleAppNameFrom(p.dic.Get)
edgeXErr = alertApp.CheckRuleByDeviceId(ctx, req.Id)
if edgeXErr != nil {
return edgeXErr
}
sceneApp := resourceContainer.SceneAppNameFrom(p.dic.Get)
edgeXErr = sceneApp.CheckSceneByDeviceId(ctx, req.Id)
if edgeXErr != nil {
return edgeXErr
}
dtos.ReplaceDeviceModelFields(&device, req)
edgeXErr = p.dbClient.UpdateDevice(device)
if edgeXErr != nil {
return edgeXErr
}
go func() {
p.UpdateDeviceCallBack(device)
}()
return nil
}
func (p *deviceApp) DevicesUnBindDriver(ctx context.Context, req dtos.DevicesUnBindDriver) error {
var searchReq dtos.DeviceSearchQueryRequest
searchReq.BaseSearchConditionQuery.Ids = dtos.ApiParamsArrayToString(req.DeviceIds)
var devices []models.Device
var err error
var total uint32
devices, total, err = p.dbClient.DevicesSearch(0, -1, searchReq)
if err != nil {
return err
}
if total == 0 {
return errort.NewCommonErr(errort.DeviceNotExist, fmt.Errorf("devices not found"))
}
err = p.dbClient.BatchUnBindDevice(req.DeviceIds)
if err != nil {
return err
}
for _, device := range devices {
callBackDevice := device
go func() {
p.DeleteDeviceCallBack(models.Device{
Id: callBackDevice.Id,
DriveInstanceId: callBackDevice.DriveInstanceId,
})
}()
}
return nil
}
//DevicesBindProductId
func (p *deviceApp) DevicesBindProductId(ctx context.Context, req dtos.DevicesBindProductId) error {
var searchReq dtos.DeviceSearchQueryRequest
searchReq.ProductId = req.ProductId
var devices []models.Device
var err error
var total uint32
devices, total, err = p.dbClient.DevicesSearch(0, -1, searchReq)
if err != nil {
return err
}
if total == 0 {
return errort.NewCommonErr(errort.DeviceNotExist, fmt.Errorf("devices not found"))
}
var deviceIds []string
for _, device := range devices {
deviceIds = append(deviceIds, device.Id)
}
err = p.dbClient.BatchBindDevice(deviceIds, req.DriverInstanceId)
if err != nil {
return err
}
return nil
}
func (p *deviceApp) DevicesBindDriver(ctx context.Context, req dtos.DevicesBindDriver) error {
var searchReq dtos.DeviceSearchQueryRequest
searchReq.BaseSearchConditionQuery.Ids = dtos.ApiParamsArrayToString(req.DeviceIds)
var devices []models.Device
var err error
var total uint32
devices, total, err = p.dbClient.DevicesSearch(0, -1, searchReq)
if err != nil {
return err
}
if total == 0 {
return errort.NewCommonErr(errort.DeviceNotExist, fmt.Errorf("devices not found"))
}
driverInstance, err := p.dbClient.DeviceServiceById(req.DriverInstanceId)
if err != nil {
return err
}
for _, device := range devices {
if device.DriveInstanceId != "" {
return errort.NewCommonErr(errort.DeviceNotUnbindDriver, fmt.Errorf("please unbind the device with the driver first"))
}
}
for _, device := range devices {
if driverInstance.Platform != device.Platform && driverInstance.Platform != "" {
return errort.NewCommonErr(errort.DeviceAndDriverPlatformNotIdentical, fmt.Errorf("the device platform is inconsistent with the drive platform"))
}
}
err = p.dbClient.BatchBindDevice(req.DeviceIds, req.DriverInstanceId)
if err != nil {
return err
}
for _, device := range devices {
device.DriveInstanceId = req.DriverInstanceId
callBackDevice := device
go func() {
p.CreateDeviceCallBack(callBackDevice)
}()
}
return nil
}
func (p *deviceApp) DeviceUpdateConnectStatus(id string, status constants.DeviceStatus) error {
device, edgeXErr := p.dbClient.DeviceById(id)
if edgeXErr != nil {
return edgeXErr
}
device.Status = status
if status == constants.DeviceStatusOnline {
device.LastOnlineTime = utils.MakeTimestamp()
}
edgeXErr = p.dbClient.UpdateDevice(device)
if edgeXErr != nil {
return edgeXErr
}
return nil
}
func setDeviceInfoSheet(file *dtos.ExportFile, req dtos.DeviceImportTemplateRequest) error {
file.Excel.SetSheetName("Sheet1", dtos.DevicesFilename)
file.Excel.SetCellStyle(dtos.DevicesFilename, "A1", "A1", file.GetCenterStyle())
file.Excel.MergeCell(dtos.DevicesFilename, "A1", "B1")
file.Excel.SetCellStr(dtos.DevicesFilename, "A1", "Device Base Info")
file.Excel.SetCellStr(dtos.DevicesFilename, "A2", "DeviceName")
file.Excel.SetCellStr(dtos.DevicesFilename, "B2", "Description")
return nil
}
func (p *deviceApp) DeviceImportTemplateDownload(ctx context.Context, req dtos.DeviceImportTemplateRequest) (*dtos.ExportFile, error) {
file, err := dtos.NewExportFile(dtos.DevicesFilename)
if err != nil {
return nil, err
}
if err := setDeviceInfoSheet(file, req); err != nil {
p.lc.Error(err.Error())
return nil, err
}
return file, nil
}
func (p *deviceApp) UploadValidated(ctx context.Context, file *dtos.ImportFile) error {
rows, err := file.Excel.Rows(dtos.DevicesFilename)
if err != nil {
return errort.NewCommonEdgeX(errort.DefaultReadExcelErrorCode, "read rows error", err)
}
idx := 0
for rows.Next() {
idx++
cols, err := rows.Columns()
if err != nil {
return errort.NewCommonEdgeX(errort.DefaultReadExcelErrorCode, "read cols error", err)
}
if idx == 1 {
continue
}
if idx == 2 {
if len(cols) != 2 {
return errort.NewCommonEdgeX(errort.DefaultReadExcelErrorCode, fmt.Sprintf("read cols error need len %d,but read len %d", 2, len(cols)), err)
}
continue
}
// 空行过滤
if len(cols) <= 0 {
continue
}
if cols[0] == "" {
return errort.NewCommonEdgeX(errort.DefaultReadExcelErrorParamsRequiredCode, fmt.Sprintf("read excel params required %+v", "deviceName"), nil)
}
}
return nil
}
func (p *deviceApp) DevicesImport(ctx context.Context, file *dtos.ImportFile, productId, driverInstanceId string) (int64, error) {
productService := resourceContainer.ProductAppNameFrom(p.dic.Get)
productInfo, err := productService.ProductById(ctx, productId)
if err != nil {
return 0, err
}
if productInfo.Status == string(constants.ProductUnRelease) {
return 0, errort.NewCommonEdgeX(errort.ProductUnRelease, "The product has not been released yet. Please release the product before adding devices", nil)
}
if driverInstanceId != "" {
driverService := resourceContainer.DriverServiceAppFrom(p.dic.Get)
driverInfo, err := driverService.Get(ctx, driverInstanceId)
if err != nil {
return 0, err
}
if driverInfo.Platform != "" && driverInfo.Platform != constants.IotPlatform_LocalIot {
return 0, errort.NewCommonEdgeX(errort.DeviceServiceMustLocalPlatform, "driver service must local platform", err)
}
}
rows, err := file.Excel.Rows(dtos.DevicesFilename)
if err != nil {
return 0, errort.NewCommonEdgeX(errort.DefaultReadExcelErrorCode, "read rows error", err)
}
devices := make([]models.Device, 0)
idx := 0
for rows.Next() {
idx++
deviceAddRequest := models.Device{
ProductId: productId,
DriveInstanceId: driverInstanceId,
}
cols, err := rows.Columns()
if err != nil {
return 0, errort.NewCommonEdgeX(errort.DefaultReadExcelErrorCode, "read cols error", err)
}
if idx == 1 {
continue
}
if idx == 2 {
if len(cols) != 2 {
return 0, errort.NewCommonEdgeX(errort.DefaultReadExcelErrorCode, fmt.Sprintf("read cols error need len %d,but read len %d", 2, len(cols)), err)
}
continue
}
// 空行过滤
if len(cols) <= 0 {
continue
}
deviceAddRequest.Id = utils.RandomNum()
deviceAddRequest.Name = cols[0]
if len(cols) >= 2 {
deviceAddRequest.Description = cols[1]
}
deviceAddRequest.Status = constants.DeviceStatusOffline
deviceAddRequest.Platform = constants.IotPlatform_LocalIot
deviceAddRequest.Created = utils.MakeTimestamp()
deviceAddRequest.Secret = utils.GenerateDeviceSecret(12)
if deviceAddRequest.Name == "" {
return 0, errort.NewCommonEdgeX(errort.DefaultReadExcelErrorParamsRequiredCode, fmt.Sprintf("read excel params required %+v", deviceAddRequest), nil)
}
devices = append(devices, deviceAddRequest)
}
for _, device := range devices {
err = resourceContainer.DataDBClientFrom(p.dic.Get).CreateTable(ctx, constants.DB_PREFIX+productInfo.Id, device.Id)
if err != nil {
return 0, err
}
}
total, err := p.dbClient.BatchUpsertDevice(devices)
if err != nil {
return 0, err
}
for _, device := range devices {
addDevice := device
go func() {
p.CreateDeviceCallBack(addDevice)
}()
}
return total, nil
}
func (p *deviceApp) DevicesReportMsgGather(ctx context.Context) error {
var count int
var err error
startTime, endTime := GetYesterdayStartTimeAndEndTime()
persistApp := resourceContainer.PersistItfFrom(p.dic.Get)
count, err = persistApp.SearchDeviceMsgCount(startTime, endTime)
if err != nil {
}
var msgGather models.MsgGather
msgGather.Count = count
msgGather.Date = time.Now().AddDate(0, 0, -1).Format("2006-01-02")
return p.dbClient.AddMsgGather(msgGather)
}
func GetYesterdayStartTimeAndEndTime() (int64, int64) {
NowTime := time.Now()
var startTime time.Time
if NowTime.Hour() == 0 && NowTime.Minute() == 0 && NowTime.Second() == 0 {
startTime = time.Unix(NowTime.Unix()-86399, 0) //当天的最后一秒
} else {
startTime = time.Unix(NowTime.Unix()-86400, 0)
}
currentYear := startTime.Year()
currentMonth := startTime.Month()
currentDay := startTime.Day()
yesterdayStartTime := time.Date(currentYear, currentMonth, currentDay, 0, 0, 0, 0, time.Local).UnixMilli()
yesterdayEndTime := time.Date(currentYear, currentMonth, currentDay, 23, 59, 59, 0, time.Local).UnixMilli()
return yesterdayStartTime, yesterdayEndTime
}

View File

@ -0,0 +1,113 @@
/*******************************************************************************
* Copyright 2017 Dell Inc.
* Copyright (c) 2019 Intel Corporation
*
* 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 deviceapp
import (
"context"
"github.com/winc-link/edge-driver-proto/devicecallback"
"github.com/winc-link/hummingbird/internal/hummingbird/core/container"
"github.com/winc-link/hummingbird/internal/models"
"github.com/winc-link/hummingbird/internal/pkg/constants"
"github.com/winc-link/hummingbird/internal/pkg/di"
"github.com/winc-link/hummingbird/internal/tools/rpcclient"
"time"
)
func (p *deviceApp) CreateDeviceCallBack(createDevice models.Device) {
defer func() {
if err := recover(); err != nil {
p.lc.Error("CreateDeviceCallBack Panic:", err)
}
}()
deviceService, err := p.dbClient.DeviceServiceById(createDevice.DriveInstanceId)
if err != nil {
return
}
driverService := container.DriverServiceAppFrom(di.GContainer.Get)
status := driverService.GetState(deviceService.Id)
if status == constants.RunStatusStarted {
client, errX := rpcclient.NewDriverRpcClient(deviceService.BaseAddress, false, "", deviceService.Id, p.lc)
if errX != nil {
return
}
defer client.Close()
var rpcRequest devicecallback.CreateDeviceCallbackRequest
rpcRequest.Data = createDevice.TransformToDriverDevice()
rpcRequest.HappenTime = uint64(time.Now().Unix())
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
defer cancel()
_, _ = client.DeviceCallBackServiceClient.CreateDeviceCallback(ctx, &rpcRequest)
}
}
func (p *deviceApp) UpdateDeviceCallBack(updateDevice models.Device) {
defer func() {
if err := recover(); err != nil {
p.lc.Error("UpdateDeviceCallBack Panic:", err)
}
}()
deviceService, err := p.dbClient.DeviceServiceById(updateDevice.DriveInstanceId)
if err != nil {
return
}
driverService := container.DriverServiceAppFrom(di.GContainer.Get)
status := driverService.GetState(deviceService.Id)
if status == constants.RunStatusStarted {
client, errX := rpcclient.NewDriverRpcClient(deviceService.BaseAddress, false, "", deviceService.Id, p.lc)
if errX != nil {
return
}
defer client.Close()
var rpcRequest devicecallback.UpdateDeviceCallbackRequest
rpcRequest.Data = updateDevice.TransformToDriverDevice()
rpcRequest.HappenTime = uint64(time.Now().Second())
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
defer cancel()
_, _ = client.DeviceCallBackServiceClient.UpdateDeviceCallback(ctx, &rpcRequest)
}
}
func (p *deviceApp) DeleteDeviceCallBack(deleteDevice models.Device) {
//查出哪些驱动和这个平台相关联,做推送通知。
defer func() {
if err := recover(); err != nil {
p.lc.Error("DeleteDeviceCallBack Panic:", err)
}
}()
deviceService, err := p.dbClient.DeviceServiceById(deleteDevice.DriveInstanceId)
if err != nil {
return
}
driverService := container.DriverServiceAppFrom(di.GContainer.Get)
status := driverService.GetState(deviceService.Id)
if status == constants.RunStatusStarted {
client, errX := rpcclient.NewDriverRpcClient(deviceService.BaseAddress, false, "", deviceService.Id, p.lc)
if errX != nil {
return
}
defer client.Close()
var rpcRequest devicecallback.DeleteDeviceCallbackRequest
rpcRequest.DeviceId = deleteDevice.Id
rpcRequest.HappenTime = uint64(time.Now().Second())
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
defer cancel()
_, _ = client.DeviceCallBackServiceClient.DeleteDeviceCallback(ctx, &rpcRequest)
}
}

View File

@ -0,0 +1,28 @@
/*******************************************************************************
* Copyright 2017 Dell Inc.
* Copyright (c) 2019 Intel Corporation
*
* 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 dmi
import (
"context"
"github.com/winc-link/hummingbird/internal/dtos"
"github.com/winc-link/hummingbird/internal/hummingbird/core/application/dmi/docker"
interfaces "github.com/winc-link/hummingbird/internal/hummingbird/core/interface"
"github.com/winc-link/hummingbird/internal/pkg/di"
"sync"
)
func New(dic *di.Container, ctx context.Context, wg *sync.WaitGroup, dcm dtos.DriverConfigManage) (interfaces.DMI, error) {
return docker.New(dic, ctx, wg, dcm)
}

View File

@ -0,0 +1,171 @@
package docker
import (
"context"
"fmt"
"github.com/docker/docker/errdefs"
"github.com/winc-link/hummingbird/internal/dtos"
resourceContainer "github.com/winc-link/hummingbird/internal/hummingbird/core/container"
interfaces "github.com/winc-link/hummingbird/internal/hummingbird/core/interface"
"github.com/winc-link/hummingbird/internal/pkg/constants"
"github.com/winc-link/hummingbird/internal/pkg/container"
"github.com/winc-link/hummingbird/internal/pkg/di"
"github.com/winc-link/hummingbird/internal/pkg/errort"
"github.com/winc-link/hummingbird/internal/pkg/logger"
"github.com/winc-link/hummingbird/internal/pkg/utils"
"io/ioutil"
"path"
"strings"
"sync"
)
var _ interfaces.DMI = new(dockerImpl)
type dockerImpl struct {
dic *di.Container
lc logger.LoggingClient
dm *DockerManager
dcm *dtos.DriverConfigManage
}
func (d *dockerImpl) GetDriverInstanceLogPath(serviceName string) string {
return d.dcm.GetHostLogFilePath(serviceName)
}
func New(dic *di.Container, ctx context.Context, wg *sync.WaitGroup, dcm dtos.DriverConfigManage) (*dockerImpl, error) {
dm, err := NewDockerManager(ctx, dic, &dcm)
if err != nil {
return nil, err
}
dil := &dockerImpl{
dic: dic,
lc: container.LoggingClientFrom(dic.Get),
dm: dm,
dcm: &dcm,
}
dil.setDcmRootDir()
return dil, nil
}
func (d *dockerImpl) StopAllInstance() {
dbClient := resourceContainer.DBClientFrom(d.dic.Get)
deviceService, _, err := dbClient.DeviceServicesSearch(0, -1, dtos.DeviceServiceSearchQueryRequest{})
if err != nil {
d.lc.Errorf("search service error :", err.Error())
}
for _, service := range deviceService {
d.lc.Info(fmt.Sprintf("stop docker instance[%s]", service.ContainerName))
err := d.StopInstance(dtos.DeviceService{ContainerName: service.ContainerName})
if err != nil {
d.lc.Error(fmt.Sprintf("stop docker instance[%s] error:", err.Error()))
}
}
}
// DownApp 下载应用
func (d *dockerImpl) DownApp(cfg dtos.DockerConfig, app dtos.DeviceLibrary, toVersion string) (string, error) {
authToken, err := d.getAuthToken(cfg.Address, cfg.Account, cfg.Password, cfg.SaltKey)
if err != nil {
return "", err
}
// 2. pull images
return d.getApp(authToken, d.getImageUrl(cfg.Address, app.DockerRepoName, toVersion))
}
// getAuthToken 获取 docker 认证 token
func (d *dockerImpl) getAuthToken(address, account, pass, salt string) (string, error) {
if account == "" || pass == "" {
return "", nil
}
// 处理docker密码
rawPassword, err := utils.DecryptAuthPassword(pass, salt)
if err != nil {
d.lc.Errorf("3.getAuthToken docker id:%s, account:%s, password err:%n", address, account, err)
return "", err
}
return d.dm.GetAuthToken(account, rawPassword, address), nil
}
// getApp 下载镜像
func (d *dockerImpl) getApp(token, imageUrl string) (string, error) {
dockerImageId, dockerErr := d.dm.PullDockerImage(imageUrl, token)
if dockerErr != nil {
code := errort.DefaultSystemError
err := fmt.Errorf("driver library download error")
if errdefs.IsUnauthorized(dockerErr) || errdefs.IsForbidden(dockerErr) ||
strings.Contains(dockerErr.Error(), "denied") ||
strings.Contains(dockerErr.Error(), "unauthorized") {
code = errort.DeviceLibraryDockerAuthInvalid
err = fmt.Errorf("docker auth invalid")
} else if errdefs.IsNotFound(dockerErr) || strings.Contains(dockerErr.Error(), "not found") {
code = errort.DeviceLibraryDockerImagesNotFound
err = fmt.Errorf("docker images not found")
} else if strings.Contains(dockerErr.Error(), "invalid reference format") {
code = errort.DeviceLibraryDockerImagesNotFound
err = fmt.Errorf("docker images not found, url invalid")
}
d.lc.Errorf("4.getApp imageUrl %s, PullDockerImage err:%v", imageUrl, dockerErr)
return "", errort.NewCommonErr(code, err)
}
return dockerImageId, nil
}
func (d *dockerImpl) getImageUrl(address, repoName, version string) string {
return path.Clean(address+"/"+repoName) + ":" + version
}
// StateApp 驱动软件下载情况
func (d *dockerImpl) StateApp(dockerImageId string) bool {
return d.dm.ExistImageById(dockerImageId)
}
// GetAllApp 获取所有镜像信息
func (d *dockerImpl) GetAllApp() []string {
return d.dm.GetAllImagesIds()
}
func (d *dockerImpl) setDcmRootDir() {
res, err := d.dm.GetContainerInspect(d.dcm.DockerSelfName)
if err != nil {
d.lc.Errorf("GetContainerInspect:%v", err)
return
}
for _, v := range res.Mounts {
if v.Destination == constants.DockerHummingbirdRootDir {
d.dcm.SetHostRootDir(v.Source)
break
}
}
networkName := res.HostConfig.NetworkMode.UserDefined()
d.dcm.SetNetworkName(networkName)
}
func (d *dockerImpl) genRunServiceConfig(name string, cfgContent string, instanceType constants.InstanceType) (string, error) {
var err error
var filePath string
if instanceType == constants.CloudInstance {
} else if instanceType == constants.DriverInstance {
filePath = d.dcm.GetRunConfigPath(name)
err = utils.CreateDirIfNotExist(constants.DockerHummingbirdRootDir + "/" + constants.DriverRunConfigDir)
if err != nil {
return "", err
}
}
err = ioutil.WriteFile(filePath, []byte(cfgContent), 0644)
if err != nil {
d.lc.Error("err:", err)
return "", err
}
if instanceType == constants.CloudInstance {
} else if instanceType == constants.DriverInstance {
return d.dcm.GetHostRunConfigPath(name), nil
}
return "", nil
}

View File

@ -0,0 +1,102 @@
/*******************************************************************************
* Copyright 2017 Dell Inc.
* Copyright (c) 2019 Intel Corporation
*
* 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 docker
import (
"github.com/winc-link/hummingbird/internal/dtos"
"github.com/winc-link/hummingbird/internal/pkg/constants"
"github.com/winc-link/hummingbird/internal/pkg/utils"
"net"
"time"
)
// RemoveApp 删除App文件
func (d *dockerImpl) RemoveApp(app dtos.DeviceLibrary) error {
return d.dm.ImageRemove(app.DockerImageId)
}
func (d *dockerImpl) GetSelfIp() string {
ip, err := d.dm.GetContainerIp(d.dcm.DockerSelfName)
if err != nil {
d.lc.Errorf("GetContainerIp err:%v", err)
}
return ip
}
// instance
func (d *dockerImpl) InstanceState(ins dtos.DeviceService) bool {
// 先判断docker 是否在运行中
stats, err := d.dm.GetContainerRunStatus(ins.ContainerName)
if err != nil {
d.lc.Errorf("GetContainerRunStatus err:%v", err)
return false
}
if stats != constants.ContainerRunStatusRunning {
return false
}
// 在通过ping 实例服务存在否
client, err := net.DialTimeout("tcp", ins.BaseAddress, 2*time.Second)
defer func() {
if client != nil {
_ = client.Close()
}
}()
if err != nil {
return false
}
return true
}
func (d *dockerImpl) StartInstance(ins dtos.DeviceService, cfg dtos.RunServiceCfg) (string, error) {
// 关闭自定义开关
if !ins.DockerParamsSwitch {
cfg.DockerParams = ""
}
filePath, err := d.genRunServiceConfig(ins.ContainerName, cfg.RunConfig, constants.DriverInstance)
if err != nil {
return "", err
}
ip, err := d.dm.ContainerStart(cfg.ImageRepo, ins.ContainerName, filePath, cfg.DockerMountDevices, cfg.DockerParams, constants.DriverInstance)
return ip, err
}
func (d *dockerImpl) StopInstance(ins dtos.DeviceService) error {
err := d.dm.ContainerStop(ins.ContainerName)
if err != nil {
return err
}
return nil
}
func (d *dockerImpl) DeleteInstance(ins dtos.DeviceService) error {
// 删除容器
err := d.dm.ContainerRemove(ins.ContainerName)
if err != nil {
return err
}
paths := []string{
d.dcm.GetRunConfigPath(ins.ContainerName),
d.dcm.GetMntDir(ins.ContainerName),
}
for _, v := range paths {
err = utils.RemoveFileOrDir(v)
if err != nil {
d.lc.Errorf("RemoveFileOrDir [%s] err %v", v, err)
}
}
return nil
}

View File

@ -0,0 +1,560 @@
package docker
import (
"context"
"encoding/base64"
"fmt"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/network"
"github.com/docker/go-connections/nat"
"github.com/winc-link/hummingbird/internal/pkg/utils"
"io"
"io/ioutil"
"regexp"
"strconv"
"strings"
"sync"
"time"
"github.com/docker/docker/client"
flag "github.com/spf13/pflag"
"github.com/winc-link/hummingbird/internal/dtos"
"github.com/winc-link/hummingbird/internal/pkg/constants"
"github.com/winc-link/hummingbird/internal/pkg/di"
"github.com/winc-link/hummingbird/internal/pkg/errort"
"github.com/winc-link/hummingbird/internal/pkg/logger"
dicContainer "github.com/winc-link/hummingbird/internal/pkg/container"
)
const (
containerInternalMountPath = "/mnt"
containerInternalConfigPath = "/etc/driver/res/configuration.toml"
)
type DockerManager struct {
// 镜像repoTags:imageinfo
ImageMap map[string]ImageInfo
// 容器name:ContainerInfo
ContainerMap map[string]ContainerInfo
cli *client.Client
ctx context.Context
timeout time.Duration
dic *di.Container
lc logger.LoggingClient
authToken string
mutex sync.RWMutex
dcm *dtos.DriverConfigManage
defaultRegistries []string
}
type CustomParams struct {
env []string
runtime string
net string
mnt []string
}
// 镜像信息
type ImageInfo struct {
Id string
RepoTags []string
}
// 容器信息
type ContainerInfo struct {
Id string
Name string
State string
}
// NewDockerManager 创建
func NewDockerManager(ctx context.Context, dic *di.Container, dcm *dtos.DriverConfigManage) (*DockerManager, error) {
cli, err := client.NewClientWithOpts(client.WithVersion(dcm.DockerApiVersion))
if err != nil {
return nil, err
}
lc := dicContainer.LoggingClientFrom(dic.Get)
dockerManager := &DockerManager{
cli: cli,
ImageMap: make(map[string]ImageInfo),
ContainerMap: make(map[string]ContainerInfo),
ctx: context.Background(),
timeout: time.Second * 10,
dic: dic,
lc: lc,
dcm: dcm,
}
dockerManager.setDefaultRegistry()
dockerManager.flushImageMap()
tickTime := time.Second * 10
timeTickerChanImage := time.Tick(tickTime)
go func() {
for {
select {
case <-ctx.Done():
lc.Info("close to flushImageMap")
return
case <-timeTickerChanImage:
dockerManager.flushImageMap()
}
}
}()
dockerManager.flushContainerMap()
timeTickerChanContainer := time.Tick(tickTime)
go func() {
for {
select {
case <-ctx.Done():
lc.Info("close to flushContainerMap")
return
case <-timeTickerChanContainer:
dockerManager.flushContainerMap()
}
}
}()
return dockerManager, nil
}
func (dm *DockerManager) setDefaultRegistry() {
info, err := dm.cli.Info(dm.ctx)
if err != nil {
dm.lc.Errorf("get docker info err:%v", err)
return
}
for _, v := range info.RegistryConfig.IndexConfigs {
dm.defaultRegistries = append(dm.defaultRegistries, v.Name)
}
}
// 刷新docker镜像数据至内存中
func (dm *DockerManager) flushImageMap() {
dm.mutex.Lock()
defer dm.mutex.Unlock()
images, err := dm.GetImageList()
if err == nil {
dm.ImageMap = make(map[string]ImageInfo)
for _, image := range images {
if len(image.RepoTags) > 0 {
dm.ImageMap[image.RepoTags[0]] = ImageInfo{
Id: image.ID,
RepoTags: image.RepoTags,
}
}
}
}
}
// 刷新docker容器数据至内存中
func (dm *DockerManager) flushContainerMap() {
dm.mutex.Lock()
defer dm.mutex.Unlock()
containers, err := dm.GetContainerList()
if err == nil {
dm.ContainerMap = make(map[string]ContainerInfo)
for _, c := range containers {
if len(c.Names) > 0 {
dm.ContainerMap[c.Names[0][1:]] = ContainerInfo{
Id: c.ID,
Name: c.Names[0][1:],
State: c.State,
}
}
}
}
}
// 启动容器,这里为强制重启 先删除容器在启动新容器 containerId 可为空
func (dm *DockerManager) ContainerStart(imageRepo string, containerName string, runConfigPath string, mountDevices []string, customParams string, instanceType constants.InstanceType) (ip string, err error) {
dm.flushImageMap()
dm.flushContainerMap()
if !dm.ExistImageById(imageRepo) {
return "", errort.NewCommonErr(errort.DeviceLibraryDockerImagesNotFound, fmt.Errorf("imageRepo is %s not exist", imageRepo))
}
// 1.停止容器,相同名字容器直接删除
_ = dm.ContainerStop(containerName)
_ = dm.ContainerRemove(containerName)
// 2.创建新容器,配置等
//exposedPorts, portMap := dm.makeExposedPorts(exposePorts)
//resourceDevices := dm.makeMountDevices(mountDevices)
binds := make([]string, 0)
binds = append(binds, "/etc/localtime:/etc/localtime:ro") // 挂载时区
var thisRunMode container.NetworkMode
if instanceType == constants.CloudInstance {
} else if instanceType == constants.DriverInstance {
binds = append(binds, runConfigPath+":"+containerInternalConfigPath) //挂载启动配置文件
binds = append(binds, dm.dcm.GetHostMntDir(containerName)+":"+containerInternalMountPath) //挂载日志
thisRunMode = container.NetworkMode(dm.dcm.NetWorkName)
}
dockerCustomParams, err := dm.ParseCustomParams(customParams)
if err != nil {
dm.lc.Errorf("dockerCustomParams err: %+v", err)
return "", err
}
binds = append(binds, dockerCustomParams.mnt...)
dm.lc.Infof("dockerCustomParams: %+v,%+v", dockerCustomParams, customParams)
dm.lc.Infof("binds: %+v", binds)
dm.lc.Infof("Image:%+v", dm.ImageMap[imageRepo])
dm.lc.Infof("thisRunMode:%+v", string(thisRunMode))
_, cErr := dm.cli.ContainerCreate(dm.ctx, &container.Config{
Image: imageRepo,
Env: dockerCustomParams.env,
}, &container.HostConfig{
Binds: binds,
NetworkMode: thisRunMode,
RestartPolicy: container.RestartPolicy{
MaximumRetryCount: 10,
},
Runtime: dockerCustomParams.runtime,
}, &network.NetworkingConfig{}, nil, containerName)
if cErr != nil {
return "", cErr
}
// 3.启动容器
if err = dm.cli.ContainerStart(dm.ctx, containerName, types.ContainerStartOptions{}); err != nil {
return "", errort.NewCommonEdgeX(errort.ContainerRunFail, "Start Container Fail", err)
}
// 启动后暂停1秒查看状态
time.Sleep(time.Second * 1)
dm.flushContainerMap()
// 4.查看容器信息并返回相应的数据
status, err := dm.GetContainerRunStatus(containerName)
dm.lc.Infof("status: %+v", status)
if err != nil {
return "", errort.NewCommonEdgeX(errort.DefaultSystemError, "GetContainerRunStatus Fail", err)
}
if status != constants.ContainerRunStatusRunning {
err = errort.NewCommonEdgeX(errort.ContainerRunFail, fmt.Sprintf("%s container status %s", containerName, status), nil)
}
if thisRunMode.IsHost() {
ip = constants.HostAddress
} else {
ip, err = dm.GetContainerIp(containerName)
}
return
}
// 端口导出组装
func (dm *DockerManager) makeExposedPorts(exposePorts []int) (nat.PortSet, nat.PortMap) {
portMap := make(nat.PortMap)
exposedPorts := make(nat.PortSet, 0)
var empty struct{}
for _, p := range exposePorts {
tmpPort, _ := nat.NewPort("tcp", strconv.Itoa(p))
portMap[tmpPort] = []nat.PortBinding{
{
HostIP: "",
HostPort: strconv.Itoa(p),
},
}
exposedPorts[tmpPort] = empty
}
return exposedPorts, portMap
}
// 挂载设备组装
func (dm *DockerManager) makeMountDevices(devices []string) container.Resources {
resourceDevices := make([]container.DeviceMapping, 0)
for _, v := range devices {
resourceDevices = append(resourceDevices, container.DeviceMapping{
PathOnHost: v,
PathInContainer: v,
CgroupPermissions: "rwm",
})
}
return container.Resources{Devices: resourceDevices}
}
func (dm *DockerManager) ContainerStop(containerIdOrName string) error {
defer dm.flushContainerMap()
if err := dm.cli.ContainerStop(dm.ctx, containerIdOrName, &dm.timeout); err != nil {
dm.lc.Infof("ContainerStop fail %v", err.Error())
killErr := dm.cli.ContainerKill(dm.ctx, containerIdOrName, "SIGKILL")
if killErr != nil {
dm.lc.Infof("ContainerKill fail %v", err.Error())
}
return nil
}
return nil
}
// 默认为容器强制删除
func (dm *DockerManager) ContainerRemove(containerIdOrName string) error {
dm.flushContainerMap()
if err := dm.cli.ContainerRemove(dm.ctx, containerIdOrName, types.ContainerRemoveOptions{Force: true}); err != nil {
dm.lc.Infof("ContainerRemove fail containerId: %s, err: %v", containerIdOrName, err.Error())
// 先不用抛出错误
return nil
}
dm.flushContainerMap()
return nil
}
func (dm *DockerManager) ImageRemove(imageId string) error {
if imageId == "" {
return nil
}
dm.lc.Infof("doing remove imageId %s", imageId)
// 错误只做日志,不做抛出,以免影响后续操作
dm.flushImageMap()
if _, ok := dm.ImageMap[imageId]; !ok {
dm.lc.Infof("remove imageId %s is not exist", imageId)
return nil
}
if _, err := dm.cli.ImageRemove(dm.ctx, imageId, types.ImageRemoveOptions{}); err != nil {
dm.lc.Infof("ImageRemove imageId %s fail %v", imageId, err.Error())
return nil
}
dm.flushImageMap()
return nil
}
func (dm *DockerManager) GetImageList() ([]types.ImageSummary, error) {
return dm.cli.ImageList(dm.ctx, types.ImageListOptions{
All: true,
})
}
func (dm *DockerManager) GetContainerList() (containers []types.Container, err error) {
return dm.cli.ContainerList(dm.ctx, types.ContainerListOptions{
All: true,
})
}
// 获取容器运行状态, 目前不做任何错误处理
func (dm *DockerManager) GetContainerRunStatus(containerName string) (status string, err error) {
if len(containerName) == 0 {
return constants.ContainerRunStatusExited, nil
}
dm.mutex.Lock()
defer dm.mutex.Unlock()
if _, ok := dm.ContainerMap[containerName]; !ok {
return constants.ContainerRunStatusExited, nil
}
return dm.ContainerMap[containerName].State, nil
}
func (dm *DockerManager) GetContainerIp(containerId string) (ip string, err error) {
ip = constants.HostAddress
res, err := dm.cli.ContainerInspect(dm.ctx, containerId)
if err != nil {
return
}
dm.lc.Infof("Container Networks %+v", res.NetworkSettings.Networks)
if _, ok := res.NetworkSettings.Networks["bridge"]; ok {
return res.NetworkSettings.Networks["bridge"].IPAddress, nil
}
//dm.lc.Infof("GetContainerIp fail networks %v", res.NetworkSettings.Networks)
return ip, err
}
// 获取镜像id是否存在
func (dm *DockerManager) ExistImageById(imageId string) bool {
if len(imageId) == 0 {
return false
}
if len(dm.checkGetImageId(imageId)) >= 0 {
return true
}
return false
}
// 获取容器id
func (dm *DockerManager) getContainerIdByName(containerName string) string {
if len(containerName) == 0 {
return ""
}
for _, v := range dm.ContainerMap {
if v.Name == "/"+containerName {
return v.Id
}
}
return ""
}
// 获取容器挂载设备的信息,返回挂载设备路径slice ["/dev/ttyUSB0","/dev/ttyUSB0"]
func (dm *DockerManager) GetContainerMountDevices(containerId string) []string {
resDevices := make([]string, 0)
if _, ok := dm.ContainerMap[containerId]; !ok {
dm.lc.Errorf("containerId is %s not exist", containerId)
return []string{}
}
res, err := dm.cli.ContainerInspect(dm.ctx, containerId)
if err != nil {
dm.lc.Errorf("containerInspect err %v", err)
return []string{}
}
for _, v := range res.HostConfig.Devices {
resDevices = append(resDevices, v.PathOnHost)
}
return resDevices
}
func (dm *DockerManager) PullDockerImage(imageUrl string, authToken string) (string, error) {
var resp io.ReadCloser
var err error
var dockerImageId string
// 每次pull都去重新刷新组装token
dm.lc.Debugf("authToken len %d", len(authToken))
resp, err = dm.cli.ImagePull(dm.ctx, imageUrl, types.ImagePullOptions{
RegistryAuth: authToken,
})
if err != nil {
dm.lc.Errorf("url: %s ImagePull err: %+v", imageUrl, err)
err = errort.NewCommonErr(errort.DeviceLibraryImageDownloadFail, err)
return dockerImageId, err
}
readResp, err := ioutil.ReadAll(resp)
if err != nil {
dm.lc.Errorf("url: %s ImagePull err: %+v", imageUrl, err)
return dockerImageId, err
}
dm.lc.Infof("readResp imageUrl %s, %+v", imageUrl, string(readResp))
re, err := regexp.Compile(`Digest: (\w+:\w+)`)
if err != nil {
dm.lc.Errorf("regexp Compile err %v", err)
return dockerImageId, err
}
strSubMatch := re.FindStringSubmatch(string(readResp))
if len(strSubMatch) < 2 {
dm.lc.Errorf("regexp not match imagesId")
return dockerImageId, errort.NewCommonEdgeX(errort.DeviceLibraryImageDownloadFail, "regexp not match imagesId", nil)
}
dockerImageId = dm.checkGetImageId(imageUrl)
if dockerImageId == "" {
return "", errort.NewCommonEdgeX(errort.DeviceLibraryImageDownloadFail, "docker images is null", nil)
}
dm.lc.Infof("images pull success imageId: %s", dockerImageId)
return dockerImageId, nil
}
func (dm *DockerManager) checkGetImageId(imageUrl string) string {
dm.flushImageMap()
for imageId, v := range dm.ImageMap {
repoTags := v.RepoTags
if utils.InStringSlice(imageUrl, repoTags) {
return imageId
}
// 补充默认docker url前缀 如默认dockerhub的nginx下载下来是image是 nginx:1.12.0 那么补充默认后变成 docker.io/nginx:1.12.0
for i, _ := range dm.defaultRegistries {
for j, _ := range repoTags {
repoTags[j] = dm.defaultRegistries[i] + "/" + repoTags[j]
}
if utils.InStringSlice(imageUrl, repoTags) {
return imageId
}
}
}
return ""
}
func (dm *DockerManager) ContainerIsExist(containerIdOrName string) bool {
_, err := dm.cli.ContainerInspect(dm.ctx, containerIdOrName)
if err != nil {
return false
}
return true
}
func (dm *DockerManager) ContainerRename(originNameOrId string, nowName string) bool {
err := dm.cli.ContainerRename(dm.ctx, originNameOrId, nowName)
if err != nil {
dm.lc.Errorf("ContainerRename %v", err)
return false
}
return true
}
// Deprecated: docker api 的flush接口返回的值无效
func (dm *DockerManager) FlushAuthToken(username string, password string, serverAddress string) {
dm.lc.Debugf("docker token from serverAddress %s, username %s", serverAddress, username)
// docker api登陆结果的IdentityToken为空这里的token为明文组装后base64的值 详见https://github.com/moby/moby/issues/38830
dm.authToken = base64.StdEncoding.EncodeToString([]byte(`{"username":"` + username + `", "password": "` + password + `", "serveraddress": "` + serverAddress + `"}`))
}
func (dm *DockerManager) GetAuthToken(username string, password string, serverAddress string) string {
token := base64.StdEncoding.EncodeToString([]byte(`{"username":"` + username + `", "password": "` + password + `", "serveraddress": "` + serverAddress + `"}`))
dm.lc.Debugf("docker token from serverAddress %s, username %s token %s", serverAddress, username, token)
return token
}
// 自定义docker启动参数解析
func (dm *DockerManager) ParseCustomParams(cmd string) (CustomParams, error) {
runMode := dm.dcm.DockerManageConfig.DockerRunMode
if !utils.InStringSlice(runMode, []string{
constants.NetworkModeHost, constants.NetworkModeBridge,
}) {
runMode = constants.NetworkModeHost
}
params := CustomParams{
runtime: "",
env: []string{},
net: runMode,
}
if cmd == "" {
return params, nil
}
cmd = strings.Replace(cmd, "\n", "", -1)
strArr := strings.Split(cmd, "\\")
args := make([]string, 0)
for _, v := range strArr {
args = append(args, strings.Split(v, " ")...)
}
f := flag.NewFlagSet("edge-flag", flag.ContinueOnError)
f.StringVarP(&params.runtime, "runtime", "", "", "")
f.StringVarP(&params.net, "net", "", runMode, "")
f.StringArrayVarP(&params.env, "env", "e", []string{}, "")
f.StringArrayVarP(&params.mnt, "mnt", "v", []string{}, "")
err := f.Parse(args)
if err != nil {
return CustomParams{}, errort.NewCommonErr(errort.DockerParamsParseErr, fmt.Errorf("parse docker params err:%v", err))
}
// 内部限制只支持host和bridge两种模式
if !utils.InStringSlice(params.net, []string{
constants.NetworkModeHost, constants.NetworkModeBridge,
}) {
params.net = runMode
}
return params, nil
}
// 自定义docker启动参数解析
func (dm *DockerManager) ParseCustomParamsIsRunBridge(cmd string) (bool, error) {
params, err := dm.ParseCustomParams(cmd)
if err != nil {
return false, err
}
return container.NetworkMode(params.net).IsBridge(), nil
}
func (dm *DockerManager) GetAllImagesIds() []string {
ids := make([]string, 0)
tmpMap := dm.ImageMap
for id, _ := range tmpMap {
ids = append(ids, id)
}
return ids
}
func (dm *DockerManager) GetContainerInspect(containerName string) (types.ContainerJSON, error) {
return dm.cli.ContainerInspect(dm.ctx, containerName)
}

View File

@ -0,0 +1,100 @@
/*******************************************************************************
* Copyright 2017.
*
* 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 docapp
import (
"context"
"encoding/json"
"github.com/winc-link/hummingbird/internal/dtos"
resourceContainer "github.com/winc-link/hummingbird/internal/hummingbird/core/container"
interfaces "github.com/winc-link/hummingbird/internal/hummingbird/core/interface"
"github.com/winc-link/hummingbird/internal/models"
"github.com/winc-link/hummingbird/internal/pkg/container"
"github.com/winc-link/hummingbird/internal/pkg/di"
"github.com/winc-link/hummingbird/internal/pkg/logger"
"github.com/winc-link/hummingbird/internal/pkg/utils"
)
type docApp struct {
dic *di.Container
dbClient interfaces.DBClient
lc logger.LoggingClient
}
func (m docApp) SyncDocs(ctx context.Context, versionName string) (int64, error) {
filePath := versionName + "/doc.json"
cosApp := resourceContainer.CosAppNameFrom(m.dic.Get)
bs, err := cosApp.Get(filePath)
if err != nil {
m.lc.Errorf(err.Error())
return 0, err
}
var cosDocTemplateResponse []dtos.CosDocTemplateResponse
err = json.Unmarshal(bs, &cosDocTemplateResponse)
if err != nil {
m.lc.Errorf(err.Error())
return 0, err
}
baseQuery := dtos.BaseSearchConditionQuery{
IsAll: true,
}
dbreq := dtos.DocsSearchQueryRequest{BaseSearchConditionQuery: baseQuery}
docs, _, err := m.dbClient.DocsSearch(0, -1, dbreq)
if err != nil {
return 0, err
}
upsertCosTemplate := make([]models.Doc, 0)
for _, cosDocsTemplate := range cosDocTemplateResponse {
var find bool
for _, localDocsResponse := range docs {
if cosDocsTemplate.Name == localDocsResponse.Name {
upsertCosTemplate = append(upsertCosTemplate, models.Doc{
Id: localDocsResponse.Id,
Name: cosDocsTemplate.Name,
Sort: cosDocsTemplate.Sort,
JumpLink: cosDocsTemplate.JumpLink,
})
find = true
break
}
}
if !find {
upsertCosTemplate = append(upsertCosTemplate, models.Doc{
Id: utils.RandomNum(),
Name: cosDocsTemplate.Name,
Sort: cosDocsTemplate.Sort,
JumpLink: cosDocsTemplate.JumpLink,
})
}
}
rows, err := m.dbClient.BatchUpsertDocsTemplate(upsertCosTemplate)
if err != nil {
return 0, err
}
return rows, nil
}
func NewDocsApp(ctx context.Context, dic *di.Container) interfaces.DocsApp {
lc := container.LoggingClientFrom(dic.Get)
dbClient := resourceContainer.DBClientFrom(dic.Get)
return &docApp{
dic: dic,
dbClient: dbClient,
lc: lc,
}
}

View File

@ -0,0 +1,118 @@
package driverapp
import (
"context"
"github.com/google/uuid"
"github.com/winc-link/hummingbird/internal/dtos"
"github.com/winc-link/hummingbird/internal/hummingbird/core/container"
"github.com/winc-link/hummingbird/internal/models"
"github.com/winc-link/hummingbird/internal/pkg/errort"
"github.com/winc-link/hummingbird/internal/pkg/utils"
//"gitlab.com/tedge/edgex/internal/pkg/utils"
//
//"github.com/google/uuid"
//"gitlab.com/tedge/edgex/internal/dtos"
//"gitlab.com/tedge/edgex/internal/models"
//"gitlab.com/tedge/edgex/internal/pkg/errort"
//resourceContainer "gitlab.com/tedge/edgex/internal/tedge/resource/container"
)
// 配置镜像/驱动的账号密码仓库地址, 账号密码可为空
func (app *driverLibApp) DownConfigAdd(ctx context.Context, req dtos.DockerConfigAddRequest) error {
dc := models.DockerConfig{
Id: req.Id,
Address: req.Address,
}
var err error
// 账号密码为空的情况
if req.Account == "" || req.Password == "" {
dc.Account = ""
dc.Password = ""
} else {
dc.Account = req.Account
dc.SaltKey = generateSaltKey()
dc.Password, err = utils.EncryptAuthPassword(req.Password, dc.SaltKey)
if err != nil {
return err
}
}
return app.DownConfigInternalAdd(dc)
}
func (app *driverLibApp) DownConfigInternalAdd(dc models.DockerConfig) error {
_, err := app.dbClient.DockerConfigAdd(dc)
if err != nil {
return err
}
return nil
}
func (app *driverLibApp) DownConfigUpdate(ctx context.Context, req dtos.DockerConfigUpdateRequest) error {
dbClient := container.DBClientFrom(app.dic.Get)
if req.Id == "" {
return errort.NewCommonEdgeX(errort.DefaultReqParamsError, "update req id is required", nil)
}
dc, edgeXErr := dbClient.DockerConfigById(req.Id)
if edgeXErr != nil {
return edgeXErr
}
dtos.ReplaceDockerConfigModelFieldsWithDTO(&dc, req)
if *req.Password != "" {
var err error
dc.SaltKey = generateSaltKey()
dc.Password, err = utils.EncryptAuthPassword(dc.Password, dc.SaltKey)
if err != nil {
return err
}
}
edgeXErr = dbClient.DockerConfigUpdate(dc)
if edgeXErr != nil {
return edgeXErr
}
return nil
}
func (app *driverLibApp) DownConfigSearch(ctx context.Context, req dtos.DockerConfigSearchQueryRequest) ([]models.DockerConfig, uint32, error) {
offset, limit := req.BaseSearchConditionQuery.GetPage()
dcs, total, err := app.dbClient.DockerConfigsSearch(offset, limit, req)
if err != nil {
return dcs, 0, err
}
return dcs, total, nil
}
func (app *driverLibApp) DownConfigDel(ctx context.Context, id string) error {
dc, edgeXErr := app.dbClient.DockerConfigById(id)
if edgeXErr != nil {
return edgeXErr
}
// 判断 此配置是否被 library使用
_, total, err := app.DeviceLibrariesSearch(ctx, dtos.DeviceLibrarySearchQueryRequest{
DockerConfigId: id,
})
if err != nil {
return edgeXErr
}
if total > 0 {
return errort.NewCommonEdgeX(errort.DockerConfigMustDeleteDeviceLibrary, "请先删除绑定此配置的驱动", nil)
}
err = app.dbClient.DockerConfigDelete(dc.Id)
if err != nil {
return err
}
return nil
}
// 生成password的salt key
func generateSaltKey() string {
return uuid.New().String()
}

View File

@ -0,0 +1,52 @@
/*******************************************************************************
* Copyright 2017 Dell Inc.
* Copyright (c) 2019 Intel Corporation
*
* 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 driverapp
import (
"context"
"github.com/winc-link/hummingbird/internal/hummingbird/core/container"
interfaces "github.com/winc-link/hummingbird/internal/hummingbird/core/interface"
pkgcontainer "github.com/winc-link/hummingbird/internal/pkg/container"
"github.com/winc-link/hummingbird/internal/pkg/di"
"github.com/winc-link/hummingbird/internal/pkg/logger"
)
type driverLibApp struct {
dic *di.Container
lc logger.LoggingClient
dbClient interfaces.DBClient
manager DeviceLibraryManager
market *driverMarket // Refactor: interface
}
func NewDriverApp(ctx context.Context, dic *di.Container) interfaces.DriverLibApp {
return newDriverLibApp(dic)
}
func newDriverLibApp(dic *di.Container) *driverLibApp {
app := &driverLibApp{
dic: dic,
lc: pkgcontainer.LoggingClientFrom(dic.Get),
dbClient: container.DBClientFrom(dic.Get),
}
app.manager = newDriverLibManager(dic, app)
app.market = newDriverMarket(dic, app)
return app
}
func (app *driverLibApp) getDriverServiceApp() interfaces.DriverServiceApp {
return container.DriverServiceAppFrom(app.dic.Get)
}

View File

@ -0,0 +1,38 @@
/*******************************************************************************
* Copyright 2017 Dell Inc.
* Copyright (c) 2019 Intel Corporation
*
* 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 driverapp
import (
"context"
"github.com/winc-link/hummingbird/internal/dtos"
)
func (app *driverLibApp) GetDriverClassify(ctx context.Context, req dtos.DriverClassifyQueryRequest) ([]dtos.DriverClassifyResponse, uint32, error) {
offset, limit := req.BaseSearchConditionQuery.GetPage()
dcs, total, err := app.dbClient.DriverClassifySearch(offset, limit, req)
res := make([]dtos.DriverClassifyResponse, len(dcs))
if err != nil {
return res, 0, err
}
for i, dc := range dcs {
res[i] = dtos.DriverClassifyResponse{
Id: dc.Id,
Name: dc.Name,
}
}
return res, total, nil
}

View File

@ -0,0 +1,212 @@
/*******************************************************************************
* Copyright 2017 Dell Inc.
* Copyright (c) 2019 Intel Corporation
*
* 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 driverapp
import (
"context"
"fmt"
"github.com/winc-link/hummingbird/internal/dtos"
"github.com/winc-link/hummingbird/internal/models"
"github.com/winc-link/hummingbird/internal/pkg/constants"
"github.com/winc-link/hummingbird/internal/pkg/errort"
)
func (app *driverLibApp) AddDriverLib(ctx context.Context, dl dtos.DeviceLibraryAddRequest) error {
dlm := app.addOrUpdateSupportVersionConfig(dtos.FromDeviceLibraryRpcToModel(&dl))
_, err := app.createDriverLib(dlm)
if err != nil {
return err
}
return nil
}
func (app *driverLibApp) createDriverLib(dl models.DeviceLibrary) (models.DeviceLibrary, error) {
return app.dbClient.AddDeviceLibrary(dl)
}
func (app *driverLibApp) addOrUpdateSupportVersionConfig(dl models.DeviceLibrary) models.DeviceLibrary {
for _, sv := range dl.SupportVersions {
if sv.Version == dl.Version {
//dl.SupportVersions[i].ConfigJson = dl.Config
return dl
}
}
if dl.Version == "" {
return dl
}
// add
version := models.SupportVersion{
Version: dl.Version,
}
dl.SupportVersions = []models.SupportVersion{version}
return dl
}
func (app *driverLibApp) DeviceLibrariesSearch(ctx context.Context, req dtos.DeviceLibrarySearchQueryRequest) ([]models.DeviceLibrary, uint32, error) {
offset, limit := req.BaseSearchConditionQuery.GetPage()
installingIds := app.manager.FilterState(constants.OperateStatusInstalling)
if req.DownloadStatus == constants.OperateStatusInstalling && len(installingIds) == 0 {
app.lc.Infof("deviceLibSearch install status is empty, req: %+v", req)
return []models.DeviceLibrary{}, 0, nil
}
req = app.prepareSearch(req, installingIds)
deviceLibraries, total, err := app.dbClient.DeviceLibrariesSearch(offset, limit, req)
if err != nil {
return deviceLibraries, 0, err
}
dlIds := make([]string, 0)
dlMapDsExist := make(map[string]bool)
for _, v := range deviceLibraries {
dlIds = append(dlIds, v.Id)
dlMapDsExist[v.Id] = false
}
dss, _, err := app.dbClient.DeviceServicesSearch(0, -1, dtos.DeviceServiceSearchQueryRequest{DeviceLibraryIds: dtos.ApiParamsArrayToString(dlIds)})
if err != nil {
return deviceLibraries, 0, err
}
for _, v := range dss {
dlMapDsExist[v.DeviceLibraryId] = true
}
// 设置驱动库状态
for i, dl := range deviceLibraries {
stats := app.manager.GetState(dl.Id)
if stats == constants.OperateStatusInstalling {
deviceLibraries[i].OperateStatus = constants.OperateStatusInstalling
} else if app.manager.ExistImage(dl.DockerImageId) && dlMapDsExist[dl.Id] {
deviceLibraries[i].OperateStatus = constants.OperateStatusInstalled
} else {
deviceLibraries[i].OperateStatus = constants.OperateStatusDefault
deviceLibraries[i].DockerImageId = ""
}
}
return deviceLibraries, total, nil
}
func (app *driverLibApp) prepareSearch(req dtos.DeviceLibrarySearchQueryRequest, installingIds []string) dtos.DeviceLibrarySearchQueryRequest {
// 处理驱动安装状态
existImages := app.manager.GetAllImages()
if req.DownloadStatus == constants.OperateStatusInstalling {
req.Ids = dtos.ApiParamsArrayToString(installingIds)
} else if req.DownloadStatus == constants.OperateStatusInstalled {
req.NoInIds = dtos.ApiParamsArrayToString(installingIds)
req.ImageIds = dtos.ApiParamsArrayToString(existImages)
} else if req.DownloadStatus == constants.OperateStatusUninstall || req.DownloadStatus == constants.OperateStatusDefault {
req.NoInIds = dtos.ApiParamsArrayToString(installingIds)
req.NoInImageIds = dtos.ApiParamsArrayToString(existImages)
}
return req
}
func (app *driverLibApp) DeleteDeviceLibraryById(ctx context.Context, id string) error {
dl, err := app.dbClient.DeviceLibraryById(id)
if err != nil {
return err
}
// 内置驱动市场不允许删除
if dl.IsInternal {
return errort.NewCommonErr(errort.DeviceLibraryNotAllowDelete, fmt.Errorf("internal library not allow delete"))
}
// 删除驱动前需要查看 驱动所属驱动实例是否存在
_, total, edgeXErr := app.getDriverServiceApp().Search(ctx, dtos.DeviceServiceSearchQueryRequest{DeviceLibraryId: id})
if edgeXErr != nil {
return edgeXErr
}
if total > 0 {
return errort.NewCommonErr(errort.DeviceLibraryMustDeleteDeviceService, fmt.Errorf("must delete service"))
}
// 删除驱动前需要查看 驱动所属的产品是否存在
//_, total, edgeXErr = app.dbClient.ProductsSearch(0, 1, dtos.ProductSearchQueryRequest{
// DeviceLibraryId: id,
//})
//if edgeXErr != nil {
// return edgeXErr
//}
//if total > 0 {
// return errort.NewCommonErr(errort.DeviceLibraryMustDeleteProduct, fmt.Errorf("must delete product"))
//}
app.manager.Remove(id)
return nil
}
func (app *driverLibApp) DriverLibById(dlId string) (models.DeviceLibrary, error) {
dl, err := app.dbClient.DeviceLibraryById(dlId)
if err != nil {
app.lc.Errorf("DriverLibById req DeviceLibraryById(%s) err %v", dlId, err)
return models.DeviceLibrary{}, err
}
return dl, nil
}
func (app *driverLibApp) DeviceLibraryById(ctx context.Context, id string) (models.DeviceLibrary, error) {
dl, err := app.DriverLibById(id)
if err != nil {
return models.DeviceLibrary{}, err
}
dl.OperateStatus = app.manager.GetState(id)
return dl, nil
}
// UpgradeDeviceLibrary 升级驱动库版本
func (app *driverLibApp) UpgradeDeviceLibrary(ctx context.Context, req dtos.DeviceLibraryUpgradeRequest) error {
if app.manager.GetState(req.Id) == constants.OperateStatusInstalling {
return errort.NewCommonErr(errort.DeviceLibraryUpgradeIng, fmt.Errorf("is upgrading, please wait"))
}
//检查驱动是否存在
_, edgeXErr := app.DriverLibById(req.Id)
if edgeXErr != nil {
return edgeXErr
}
if err := app.manager.Upgrade(req.Id, req.Version); err != nil {
return err
}
return nil
}
func (app *driverLibApp) UpdateDeviceLibrary(ctx context.Context, update dtos.UpdateDeviceLibrary) error {
dl, dbErr := app.dbClient.DeviceLibraryById(update.Id)
if dbErr != nil {
return dbErr
}
dtos.ReplaceDeviceLibraryModelFieldsWithDTO(&dl, update)
dl = app.addOrUpdateSupportVersionConfig(dl)
if err := app.dbClient.UpdateDeviceLibrary(dl); err != nil {
return err
}
app.lc.Infof("deviceLibrary(%v) update succ. ", dl.Id)
return nil
}

View File

@ -0,0 +1,230 @@
/*******************************************************************************
* Copyright 2017 Dell Inc.
* Copyright (c) 2019 Intel Corporation
*
* 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 driverapp
import (
"fmt"
"github.com/winc-link/hummingbird/internal/dtos"
"github.com/winc-link/hummingbird/internal/hummingbird/core/container"
resourceContainer "github.com/winc-link/hummingbird/internal/hummingbird/core/container"
interfaces "github.com/winc-link/hummingbird/internal/hummingbird/core/interface"
"github.com/winc-link/hummingbird/internal/models"
"github.com/winc-link/hummingbird/internal/pkg/constants"
pkgcontainer "github.com/winc-link/hummingbird/internal/pkg/container"
"github.com/winc-link/hummingbird/internal/pkg/di"
"github.com/winc-link/hummingbird/internal/pkg/errort"
"github.com/winc-link/hummingbird/internal/pkg/logger"
"sync"
)
type DeviceLibraryManager interface {
GetState(dlId string) string
SetState(dlId, state string)
FilterState(state string) []string
Upgrade(dlId, version string) error
Remove(dlId string) error
//
GetAllImages() []string // 获取所有安装镜像ID
ExistImage(dockerImageId string) bool
}
type deviceLibraryManager struct {
libs sync.Map
dic *di.Container
lc logger.LoggingClient
driverApp interfaces.DriverLibApp
appModel interfaces.DMI
}
func newDriverLibManager(dic *di.Container, app interfaces.DriverLibApp) *deviceLibraryManager {
return &deviceLibraryManager{
libs: sync.Map{},
dic: dic,
lc: pkgcontainer.LoggingClientFrom(dic.Get),
appModel: interfaces.DMIFrom(dic.Get),
driverApp: app,
}
}
func (m *deviceLibraryManager) GetState(dlId string) string {
state, ok := m.libs.Load(dlId)
if ok {
return state.(string)
}
m.libs.Store(dlId, constants.OperateStatusDefault)
return constants.OperateStatusDefault
}
func (m *deviceLibraryManager) SetState(dlId, state string) {
m.libs.Store(dlId, state)
}
func (m *deviceLibraryManager) FilterState(state string) []string {
var list []string
m.libs.Range(func(key, value interface{}) bool {
if value.(string) == state {
list = append(list, key.(string))
}
return true
})
return list
}
func (m *deviceLibraryManager) Remove(dlId string) error {
dbClient := container.DBClientFrom(m.dic.Get)
dl, err := dbClient.DeviceLibraryById(dlId)
if err != nil {
return err
}
// 删除自定义驱动
if err := dbClient.DeleteDeviceLibraryById(dlId); err != nil {
return err
}
m.asyncRemoveImage(dl)
return nil
}
func (m *deviceLibraryManager) GetAllImages() []string {
return m.appModel.GetAllApp()
}
func (m *deviceLibraryManager) ExistImage(dockerImageId string) bool {
return m.appModel.StateApp(dockerImageId)
}
// updateDL 下载新版本镜像,并更新驱动库版本信息
func (m *deviceLibraryManager) updateDL(dlId, updateVersion string) error {
// 获取驱动库信息 Refactor:
dl, dc, err := m.driverApp.GetDeviceLibraryAndMirrorConfig(dlId)
if err != nil {
m.SetState(dlId, constants.OperateStatusDefault)
return err
}
imageId, err := m.downloadVersion(dl, dc, updateVersion)
if err != nil {
return err
}
old := dl
// 添加驱动库版本
dl.DockerImageId = imageId
newVersionInfo, isExistVersion := getNewSupportVersion(dl.SupportVersions, dl.Version, updateVersion)
if !isExistVersion {
dl.SupportVersions = append(dl.SupportVersions, newVersionInfo)
}
dl = m.updateDLDefaultVersion(dl, newVersionInfo)
dbClient := resourceContainer.DBClientFrom(m.dic.Get)
if err := dbClient.UpdateDeviceLibrary(dl); err != nil {
m.lc.Errorf("updateDeviceLibrary %s fail %+v", dl.Id, err)
return err
}
m.cleanOldVersion(old, dl)
return nil
}
// cleanOldVersion 异步清理旧版本镜像
func (m *deviceLibraryManager) cleanOldVersion(oldDL, dl models.DeviceLibrary) {
if oldDL.DockerImageId == dl.DockerImageId {
return
}
m.asyncRemoveImage(oldDL)
}
func (m *deviceLibraryManager) asyncRemoveImage(dl models.DeviceLibrary) {
// 镜像删除
go m.appModel.RemoveApp(dtos.DeviceLibraryFromModel(dl))
}
func getNewSupportVersion(versions models.SupportVersions, curVersion, newVersion string) (models.SupportVersion, bool) {
newVersionInfo := models.SupportVersion{}
for _, v := range versions {
if v.Version == newVersion {
return v, true
}
// 将老版本的配置复制到新版本中
if v.Version == curVersion {
newVersionInfo = v
}
}
newVersionInfo.Version = newVersion
return newVersionInfo, false
}
func (m *deviceLibraryManager) updateDLDefaultVersion(dl models.DeviceLibrary, newVersion models.SupportVersion) models.DeviceLibrary {
// 2.驱动库配置更新为新版本的
//dl.Config = newVersion.ConfigJson
//if dl.Config == "" {
// dl.Config = dtos.GetLibrarySimpleBaseConfig()
//}
//dl.ConfigFile = newVersion.ConfigFile
dl.Version = newVersion.Version
return dl
}
func (m *deviceLibraryManager) downloadVersion(dl models.DeviceLibrary, dc models.DockerConfig, version string) (string, error) {
// 3.下载应用
imageId, err := m.appModel.DownApp(dtos.DockerConfigFromModel(dc), dtos.DeviceLibraryFromModel(dl), version)
if err != nil {
return "", err
}
return imageId, nil
}
func (m *deviceLibraryManager) Upgrade(dlId, updateVersion string) error {
if m.GetState(dlId) == constants.OperateStatusInstalling {
return errort.NewCommonErr(errort.DeviceLibraryUpgradeIng, fmt.Errorf("device library upgradeing"))
}
// 1. 设置为升级中
m.SetState(dlId, constants.OperateStatusInstalling)
m.lc.Infof("1.start updateDeviceLibrary %v to version %v", dlId, updateVersion)
// 下载新版本镜像,并更新驱动库版本信息
if err := m.updateDL(dlId, updateVersion); err != nil {
m.SetState(dlId, constants.OperateStatusDefault)
m.lc.Errorf("updateDeviceLibrary version fail", err)
return err
}
m.SetState(dlId, constants.OperateStatusInstalled)
//m.lc.Infof("2.updateDeviceLibrary %v version %v", dlId, updateVersion)
// 3. 升级驱动实例
if err := m.upgradeDeviceService(dlId); err != nil {
m.lc.Errorf("3.upgradeDeviceService error %v", err)
return err
}
return nil
}
func (m *deviceLibraryManager) upgradeDeviceService(dlId string) error {
dl, err := m.driverApp.DriverLibById(dlId)
if err != nil {
return err
}
return resourceContainer.DriverServiceAppFrom(m.dic.Get).Upgrade(dl)
}

View File

@ -0,0 +1,36 @@
/*******************************************************************************
* Copyright 2017 Dell Inc.
* Copyright (c) 2019 Intel Corporation
*
* 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 driverapp
import (
interfaces "github.com/winc-link/hummingbird/internal/hummingbird/core/interface"
"github.com/winc-link/hummingbird/internal/pkg/container"
"github.com/winc-link/hummingbird/internal/pkg/di"
"github.com/winc-link/hummingbird/internal/pkg/logger"
)
type driverMarket struct {
dic *di.Container
lc logger.LoggingClient
driverApp interfaces.DriverLibApp
}
func newDriverMarket(dic *di.Container, app interfaces.DriverLibApp) *driverMarket {
return &driverMarket{
dic: dic,
lc: container.LoggingClientFrom(dic.Get),
driverApp: app,
}
}

View File

@ -0,0 +1,21 @@
package driverapp
import "github.com/winc-link/hummingbird/internal/models"
func (app *driverLibApp) GetDeviceLibraryAndMirrorConfig(dlId string) (dl models.DeviceLibrary, dc models.DockerConfig, err error) {
// 1. 获取驱动库
dl, err = app.DriverLibById(dlId)
if err != nil {
app.lc.Errorf("1.DeviceLibraryOperate device library id:%s, err:%n", dlId, err)
return
}
// 2.获取docker仓库配置
dc, err = app.dbClient.DockerConfigById(dl.DockerConfigId)
if err != nil {
app.lc.Errorf("2.DeviceLibraryOperate docker hub, id:%s, DockerConfigId:%s, err:%v", dlId, dl.DockerConfigId, err)
return
}
return
}

View File

@ -0,0 +1,25 @@
package driverserviceapp
import (
"context"
interfaces "github.com/winc-link/hummingbird/internal/hummingbird/core/interface"
"github.com/winc-link/hummingbird/internal/pkg/container"
"github.com/winc-link/hummingbird/internal/pkg/di"
"github.com/winc-link/hummingbird/internal/pkg/logger"
)
type driverServiceApp struct {
dic *di.Container
lc logger.LoggingClient
*driverServiceAppM
}
func NewDriverServiceApp(ctx context.Context, dic *di.Container) interfaces.DriverServiceApp {
return &driverServiceApp{
dic: dic,
lc: container.LoggingClientFrom(dic.Get),
driverServiceAppM: newDriverServiceApp(ctx, dic),
}
}

View File

@ -0,0 +1,40 @@
package driverserviceapp
import (
//"gitlab.com/tedge/edgex/internal/models"
//"gitlab.com/tedge/edgex/internal/pkg/constants"
"github.com/winc-link/hummingbird/internal/models"
)
// 驱动运行的配置模版
func getDriverConfigTemplate(ds models.DeviceService) string {
return getDefaultDriverConfig()
}
func getDefaultDriverConfig() string {
return `[Logger]
FileName = "/mnt/logs/driver.log"
LogLevel = "INFO" # DEBUG INFO WARN ERROR
[Clients]
[Clients.Core]
Address = "hummingbird-core:57081"
UseTLS = false
CertFilePath = ""
[Service]
ID = ""
Name = ""
ProductList = []
GwId = ""
LocalKey = ""
Activated = false
[Service.Server]
Address = "0.0.0.0:49991"
UseTLS = false
CertFile = ""
KeyFile = ""
[CustomConfig]`
}

View File

@ -0,0 +1,830 @@
package driverserviceapp
import (
"bytes"
"context"
"fmt"
"github.com/BurntSushi/toml"
pkgerr "github.com/pkg/errors"
"github.com/winc-link/hummingbird/internal/dtos"
"github.com/winc-link/hummingbird/internal/hummingbird/core/container"
interfaces "github.com/winc-link/hummingbird/internal/hummingbird/core/interface"
"github.com/winc-link/hummingbird/internal/models"
"github.com/winc-link/hummingbird/internal/pkg/constants"
bootstrapContainer "github.com/winc-link/hummingbird/internal/pkg/container"
"github.com/winc-link/hummingbird/internal/pkg/di"
"github.com/winc-link/hummingbird/internal/pkg/errort"
"github.com/winc-link/hummingbird/internal/pkg/logger"
"github.com/winc-link/hummingbird/internal/pkg/utils"
"gorm.io/gorm"
"net"
"reflect"
"strconv"
"strings"
"sync"
)
// 驱动实例管理
func newDriverServiceApp(ctx context.Context, dic *di.Container) *driverServiceAppM {
dsManager := &driverServiceAppM{
state: sync.Map{},
dic: dic,
lc: bootstrapContainer.LoggingClientFrom(dic.Get),
dbClient: container.DBClientFrom(dic.Get),
ctx: ctx,
appModel: interfaces.DMIFrom(dic.Get),
dsMonitor: make(map[string]*DeviceServiceMonitor),
}
//dsManager.FlushStatsToAgent()
dsManager.initMonitor()
return dsManager
}
//
type driverServiceAppM struct {
state sync.Map
dic *di.Container
lc logger.LoggingClient
ctx context.Context // Bootstrap init 启动传入的, 用来处理done数据
// interfaces
dbClient interfaces.DBClient
appModel interfaces.DMI
dsMonitor map[string]*DeviceServiceMonitor
}
func (m *driverServiceAppM) getDriverApp() interfaces.DriverLibApp {
return container.DriverAppFrom(m.dic.Get)
}
func (m *driverServiceAppM) GetState(id string) int {
state, ok := m.state.Load(id)
if ok {
return state.(int)
}
m.state.Store(id, constants.RunStatusStopped)
return constants.RunStatusStopped
}
func (m *driverServiceAppM) SetState(id string, state int) {
m.state.Store(id, state)
}
func (m *driverServiceAppM) Start(id string) error {
var err error
defer func() {
if err != nil {
m.SetState(id, constants.RunStatusStopped)
}
}()
if m.InProgress(id) {
return fmt.Errorf("that id(%s) is staring or stopping, do not to start", id)
}
ds, err := m.Get(context.Background(), id)
if err != nil {
return err
}
dl, err := m.getDriverApp().DriverLibById(ds.DeviceLibraryId)
if err != nil {
return err
}
driverRunPort, err := utils.GetAvailablePort(ds.GetPort())
if err != nil {
return errort.NewCommonErr(errort.CreateConfigFileFail, fmt.Errorf("create cofig file faild %w", err))
}
// 获取自身服务运行的ip,并组装运行启动的配置
runConfig, err := m.buildServiceRunCfg(m.appModel.GetSelfIp(), driverRunPort, ds)
if err != nil {
return errort.NewCommonErr(errort.GetAvailablePortFail, fmt.Errorf("get available port fail"))
}
dtoDs := dtos.DeviceServiceFromModel(ds)
dtoRunCfg := dtos.RunServiceCfg{
ImageRepo: dl.DockerImageId,
RunConfig: runConfig,
DockerParams: ds.DockerParams,
DriverName: dl.Name,
}
m.SetState(id, constants.RunStatusStarting)
_, err = m.appModel.StartInstance(dtoDs, dtoRunCfg)
if err != nil {
return err
}
m.SetState(id, constants.RunStatusStarted)
//重新刷新数据
ds, err = m.Get(context.Background(), id)
if err != nil {
return err
}
oldBaseAddress := ds.BaseAddress
// 更新驱动服务数据
ds.BaseAddress = ds.ContainerName + ":" + strconv.Itoa(driverRunPort)
// 更新监控ds 如果不更新ping 定时检测会失效
if oldBaseAddress != ds.BaseAddress {
err = m.dbClient.UpdateDeviceService(ds)
if err != nil {
return err
}
if _, ok := m.dsMonitor[ds.Id]; ok {
m.dsMonitor[ds.Id].ds = dtos.DeviceServiceFromModel(ds)
}
}
return nil
}
func (m *driverServiceAppM) Stop(id string) error {
ds, err := m.Get(context.Background(), id)
if err != nil {
return err
}
m.SetState(id, constants.RunStatusStopping)
stopErr := m.appModel.StopInstance(dtos.DeviceServiceFromModel(ds))
if stopErr != nil {
m.SetState(id, constants.RunStatusStopped)
return errort.NewCommonErr(errort.ContainerStopFail, pkgerr.WithMessage(stopErr, "stop driverService fail"))
}
m.SetState(id, constants.RunStatusStopped)
return nil
}
func (m *driverServiceAppM) ReStart(id string) error {
err := m.Stop(id)
if err != nil {
return fmt.Errorf("dsId(%v), stop err:%v", id, err)
}
err = m.Start(id)
if err != nil {
return fmt.Errorf("dsId(%v), start err:%v", id, err)
}
return nil
}
//
func (m *driverServiceAppM) Add(ctx context.Context, ds models.DeviceService) error {
if ds.BaseAddress == "" {
address, err := net.ResolveTCPAddr("tcp", fmt.Sprintf("%s:0", "0.0.0.0"))
if err != nil {
return err
}
port, _ := utils.AvailablePort(address)
ds.BaseAddress = ds.ContainerName + ":" + strconv.Itoa(port)
}
// 处理专家模式配置
if ds.ExpertMode && len(ds.ExpertModeContent) > 0 {
tmpKv, err := dtos.FromYamlStrToMap(ds.ExpertModeContent)
if err != nil {
return errort.NewCommonErr(errort.DefaultReqParamsError, fmt.Errorf("parse expertModeContent err:%v", err))
}
ds.Config[constants.ConfigKeyDriver] = tmpKv
}
ds, err := m.dbClient.AddDeviceService(ds)
if err != nil {
return err
}
// 添加后台监控
if _, ok := m.dsMonitor[ds.Id]; ok {
m.dsMonitor[ds.Id].ds = dtos.DeviceServiceFromModel(ds)
} else {
m.dsMonitor[ds.Id] = NewDeviceServiceMonitor(m.ctx, dtos.DeviceServiceFromModel(ds), m.dic)
}
//go m.FlushStatsToAgent()
//go m.autoAddDevice(ds)
return nil
}
//
func (m *driverServiceAppM) Update(ctx context.Context, dto dtos.DeviceServiceUpdateRequest) error {
deviceService, edgeXErr := m.Get(ctx, dto.Id)
if edgeXErr != nil {
return edgeXErr
}
if m.GetState(dto.Id) == constants.RunStatusStarted {
return errort.NewCommonErr(errort.DeviceServiceMustStopService, fmt.Errorf("service(%v) is running not update", deviceService.Id))
}
dtos.ReplaceDeviceServiceModelFieldsWithDTO(&deviceService, dto)
edgeXErr = m.dbClient.UpdateDeviceService(deviceService)
if edgeXErr != nil {
return edgeXErr
}
return nil
}
//
// 升级实例: 如果不存在则创建数据、如果存在,但未运行,不做处理、若运行中则重启
func (m *driverServiceAppM) Upgrade(dl models.DeviceLibrary) error {
dss, _, err := m.Search(m.ctx, dtos.DeviceServiceSearchQueryRequest{DeviceLibraryId: dl.Id})
// 不存在则创建
if len(dss) <= 0 {
version := models.SupportVersion{}
for _, v := range dl.SupportVersions {
if v.Version == dl.Version {
version = v
break
}
}
err = m.Add(m.ctx, models.DeviceService{
//Id: dsCode,
Name: dl.Name,
DeviceLibraryId: dl.Id,
ExpertMode: version.ExpertMode,
ExpertModeContent: version.ExpertModeContent,
DockerParamsSwitch: version.DockerParamsSwitch,
DockerParams: version.DockerParams,
ContainerName: dl.ContainerName,
Config: make(map[string]interface{}),
})
if err != nil {
m.lc.Errorf("add device service err:%v", err)
return err
}
return nil
}
ds := dss[0]
// 存在则 判断是否更新
if m.GetState(ds.Id) != constants.RunStatusStarted {
return nil
}
// 重启
if err = m.Stop(ds.Id); err != nil {
m.lc.Errorf("stop deviceService(%s) err:%v", ds.Id, err)
return err
}
if err = m.Start(ds.Id); err != nil {
m.lc.Errorf("start deviceService(%s) err:%v", ds.Id, err)
return err
}
return nil
}
func (m *driverServiceAppM) Search(ctx context.Context, req dtos.DeviceServiceSearchQueryRequest) ([]models.DeviceService, uint32, error) {
offset, limit := req.BaseSearchConditionQuery.GetPage()
deviceServices, total, err := m.dbClient.DeviceServicesSearch(offset, limit, req)
if err != nil {
return deviceServices, 0, err
}
dlIds := make([]string, 0)
for i, _ := range deviceServices {
dlIds = append(dlIds, deviceServices[i].DeviceLibraryId)
}
dls, _, err := m.getDriverApp().DeviceLibrariesSearch(m.ctx, dtos.DeviceLibrarySearchQueryRequest{
BaseSearchConditionQuery: dtos.BaseSearchConditionQuery{Ids: dtos.ApiParamsArrayToString(dlIds)},
})
if err != nil {
return deviceServices, 0, err
}
dlIdMap := make(map[string]models.DeviceLibrary)
for i, _ := range dls {
dlIdMap[dls[i].Id] = dls[i]
}
for i, v := range deviceServices {
deviceServices[i].RunStatus = m.GetState(v.Id)
if _, ok := dlIdMap[v.DeviceLibraryId]; ok {
deviceServices[i].ImageExist = dlIdMap[v.DeviceLibraryId].OperateStatus == constants.OperateStatusInstalled
}
}
return deviceServices, total, nil
}
func (m *driverServiceAppM) Del(ctx context.Context, id string) error {
ds, edgeXErr := m.dbClient.DeviceServiceById(id)
if edgeXErr != nil {
return edgeXErr
}
// 删除驱动实例前需要查看 实例所属的设备是否存在
//_, total, edgeXErr := m.getDeviceApp().DevicesSearch(dtos.DeviceSearchQueryRequest{ServiceId: id})
//if edgeXErr != nil {
// return edgeXErr
//}
//if total > 0 {
// return errort.NewCommonErr(errort.DeviceServiceMustDeleteDevice, fmt.Errorf("must delete device"))
//}
// 检查容器是否在运行中
if m.GetState(id) != constants.RunStatusStopped {
return errort.NewCommonErr(errort.DeviceServiceMustStopService, fmt.Errorf("must stop service"))
}
m.dbClient.GetDBInstance().Transaction(func(tx *gorm.DB) error {
err := tx.Model(&models.DeviceService{}).Where("id =?", id).Delete(&models.DeviceService{}).Error
if err != nil {
return err
}
err = tx.Model(&models.Device{}).Where("drive_instance_id = ?", id).Updates(map[string]interface{}{"drive_instance_id": ""}).Error
if err != nil {
return err
}
return nil
})
// 删除容器、监控
err := m.appModel.DeleteInstance(dtos.DeviceServiceFromModel(ds))
if err != nil {
m.lc.Errorf("DeleteInstance err:%v", err)
}
// 刷新agent 服务信息
//go m.FlushStatsToAgent()
// 删除后台监控
delete(m.dsMonitor, id)
m.state.Delete(id)
return nil
}
//
func (m *driverServiceAppM) Get(ctx context.Context, id string) (models.DeviceService, error) {
if id == "" {
return models.DeviceService{}, errort.NewCommonErr(errort.DefaultReqParamsError, fmt.Errorf("id(%s) is empty", id))
}
deviceService, err := m.dbClient.DeviceServiceById(id)
if err != nil {
return deviceService, err
}
deviceService.RunStatus = m.GetState(id)
dl, _ := m.getDriverApp().DriverLibById(deviceService.DeviceLibraryId)
deviceService.ImageExist = dl.OperateStatus == constants.OperateStatusInstalled
return deviceService, nil
}
//
func (m *driverServiceAppM) InProgress(id string) bool {
state, ok := m.state.Load(id)
if !ok {
return false
}
if state.(int) == constants.RunStatusStarting || state.(int) == constants.RunStatusStopping {
return true
}
return false
}
//
//// 监控驱动运行状态
func (m *driverServiceAppM) initMonitor() {
dbClient := container.DBClientFrom(m.dic.Get)
lc := bootstrapContainer.LoggingClientFrom(m.dic.Get)
ds, _, err := dbClient.DeviceServicesSearch(0, -1, dtos.DeviceServiceSearchQueryRequest{})
if err != nil {
lc.Errorf("DeviceServicesSearch err %v", err)
return
}
for _, v := range ds {
m.dsMonitor[v.Id] = NewDeviceServiceMonitor(m.ctx, dtos.DeviceServiceFromModel(v), m.dic)
}
}
//
//func (m *driverServiceAppM) UpdateAdvanceConfig(ctx context.Context, req dtos.UpdateServiceLogLevelConfigRequest) error {
// ds, err := m.Get(ctx, req.Id)
// if err != nil {
// return err
// }
//
// // 更新配置
// ds.LogLevel = constants.LogLevel(req.LogLevel)
//
// if err = m.dbClient.UpdateDeviceService(ds); err != nil {
// return err
// }
//
// // 通知驱动
// if !ds.IsRunning() {
// m.lc.Infof("service %s is stop", ds.Id)
// return nil
// }
// if ds.IsDriver() {
// if err = application.DeviceServiceChangeLogLevelCallback(m.ctx, m.dic, ds, m.lc); err != nil {
// return err
// }
// } else {
// if err = application.AppServiceChangeLogLevelCallback(m.ctx, m.dic, ds, m.lc); err != nil {
// return err
// }
// }
// return nil
//}
//
func (m *driverServiceAppM) UpdateRunStatus(ctx context.Context, req dtos.UpdateDeviceServiceRunStatusRequest) error {
// 1.正在处理中,返回错误
if m.InProgress(req.Id) {
return errort.NewCommonErr(errort.DeviceServiceMustStopDoingService, fmt.Errorf("device service is processing"))
}
// 2.请求状态和本地状态一致,无需操作
if req.RunStatus == m.GetState(req.Id) {
m.lc.Infof("driverService state is %d", req.RunStatus)
return nil
}
_, err := m.Get(ctx, req.Id)
if err != nil {
return err
}
if req.RunStatus == constants.RunStatusStopped {
if err = m.Stop(req.Id); err != nil {
return err
}
} else if req.RunStatus == constants.RunStatusStarted {
if err = m.Start(req.Id); err != nil {
return err
}
}
return nil
}
// 将deviceService里的配置转换到配置文件中然后启动服务
func (m *driverServiceAppM) buildServiceRunCfg(serviceIp string, runPort int, ds models.DeviceService) (string, error) {
if ds.DriverType == constants.DriverLibTypeDefault {
return m.buildDriverCfg(serviceIp, runPort, ds)
} else if ds.DriverType == constants.DriverLibTypeAppService {
//return m.buildAppCfg(serviceIp, runPort, ds)
}
return "", nil
}
func (m *driverServiceAppM) buildDriverCfg(localDefaultIp string, runPort int, ds models.DeviceService) (string, error) {
configuration := &dtos.DriverConfig{}
sysConfig := container.ConfigurationFrom(m.dic.Get)
// 读取模版配置
if _, err := toml.Decode(getDriverConfigTemplate(ds), configuration); err != nil {
return "", err
}
// 修改与核心服务通信的ip
for k, v := range configuration.Clients {
if k == "Core" {
data := v
data.Address = strings.Replace(data.Address, "127.0.0.1", localDefaultIp, -1)
data.Address = strings.Split(data.Address, ":")[0] + ":" + strings.Split(sysConfig.RpcServer.Address, ":")[1]
configuration.Clients[k] = data
}
//else if k == "MQTTBroker" {
// driverMqttInfo, err := m.dbClient.DriverMqttAuthInfo(ds.Id)
// if err != nil {
// return "", err
// }
// data := v
// data.Address = strings.Replace(data.Address, "127.0.0.1", localDefaultIp, -1)
// data.Address = strings.Split(data.Address, ":")[0] + ":" + strings.Split(data.Address, ":")[1] + ":" + "21883"
// data.ClientId = driverMqttInfo.ClientId
// data.Username = driverMqttInfo.UserName
// data.Password = driverMqttInfo.Password
// configuration.Clients[k] = data
//}
}
configuration.Service.ID = ds.Id
configuration.Service.Name = ds.Name
// 驱动服务只开启rpc服务
configuration.Service.Server.Address = "0.0.0.0:" + strconv.Itoa(runPort)
if ds.ExpertMode && ds.ExpertModeContent != "" {
configuration.CustomParam = string(ds.ExpertModeContent)
}
// set log level
configuration.Logger.LogLevel = constants.LogMap[ds.LogLevel]
configuration.Logger.FileName = "/mnt/logs/driver.log"
var buff bytes.Buffer
e := toml.NewEncoder(&buff)
err := e.Encode(configuration)
if err != nil {
return "", err
}
return buff.String(), nil
}
//
////func (m *driverServiceAppM) buildAppCfg(localDefaultIp string, runPort int, ds models.DeviceService) (string, error) {
//// configuration := &dtos.AppServiceConfig{}
//// sysConfig := container.ConfigurationFrom(m.dic.Get)
////
//// // 读取模版配置
//// if _, err := toml.Decode(getDriverConfigTemplate(ds), configuration); err != nil {
//// return "", err
//// }
////
//// // 修改与核心服务通信的ip
//// p, err := strconv.Atoi(strings.Split(sysConfig.RpcServer.Address, ":")[1])
//// if err != nil {
//// return "", err
//// }
//// configuration.Tedge.Host = localDefaultIp
//// configuration.Tedge.Port = int32(p)
////
//// configuration.Server.ID = ds.Id
//// configuration.Server.Name = ds.Name
//// // 驱动服务只开启rpc服务
//// configuration.Server.Host = "0.0.0.0"
//// configuration.Server.Port = int32(runPort)
////
//// dl, err := m.getDriverApp().DriverLibById(ds.DeviceLibraryId)
//// if err != nil {
//// return "", err
//// }
//// dlc, err := dl.GetConfig()
//// if err != nil {
//// return "", err
//// }
////
//// // 如果有专家模式直接用专家模式的yaml转换为toml不会有小数点问题
//// if ds.ExpertMode && ds.ExpertModeContent != "" {
//// err := yaml.Unmarshal([]byte(ds.ExpertModeContent), &configuration.CustomConfig)
//// if err != nil {
//// return "", err
//// }
//// } else {
//// // driver 模块做映射, driver通过配置文件进行强制转换
//// if ds.Config != nil {
//// if driver, ok := ds.Config[constants.ConfigKeyDriver]; ok {
//// finalDriver := make(map[string]interface{})
//// if _, ok := driver.(map[string]interface{}); ok {
//// for i, v := range driver.(map[string]interface{}) {
//// v = convertCfgDriverKeyType(dlc, i, v)
//// finalDriver[i] = v
//// }
//// configuration.CustomConfig = finalDriver
//// }
//// }
//// }
//// }
////
//// // set log level
//// configuration.Log.LogLevel = constants.LogMap[ds.LogLevel]
////
//// var buff bytes.Buffer
//// e := toml.NewEncoder(&buff)
//// err = e.Encode(configuration)
//// if err != nil {
//// return "", err
//// }
////
//// return buff.String(), nil
////}
//
//// TODO 只针对docker版本
////func (m *driverServiceAppM) NotifyAddDevice(d models.Device) {
//// // 目前只支持modbus-rtu协议
//// protocolKey := constants.DriverModbusRtu
//// if _, ok := d.Protocols[protocolKey]["Address"]; !ok {
//// return
//// }
////
//// // 如果容器没有处于运行状态,不做任何处理
//// if m.GetState(d.ServiceId) != constants.RunStatusStarted {
//// return
//// }
////
//// // 重启docker驱动
//// err := m.ReStart(d.ServiceId)
//// if err != nil {
//// m.lc.Errorf("NotifyAddDevice restart serviceId(%s) err:%v", d.ServiceId, err)
//// }
////}
//
// 将配置数据强制转换为定义的类型,如果定义错误,则不转换
func convertCfgDriverKeyType(dlc models.DeviceLibraryConfig, key string, value interface{}) interface{} {
var ok bool
if _, ok = dlc.DeviceServer[constants.ConfigKeyDriver]; !ok {
return value
}
rt := reflect.TypeOf(value)
rv := reflect.ValueOf(value)
items := dlc.DeviceServer[constants.ConfigKeyDriver]
for _, v := range items {
if v.Name == key {
switch v.Type {
case constants.DriverConfigTypeInt:
if rt.Kind() == reflect.String {
tmpV, e := strconv.Atoi(rv.String())
if e != nil {
return value
}
return tmpV
}
if rt.Kind() == reflect.Float64 {
return int(rv.Float())
}
return value
case constants.DriverConfigTypeFloat:
if rt.Kind() == reflect.String {
tmpV, e := strconv.ParseInt(rv.String(), 10, 64)
if e != nil {
return value
}
return tmpV
}
if rt.Kind() == reflect.Int {
return float64(rv.Int())
}
return value
default:
// 其他类型目前先不做处理
return value
}
}
}
return value
}
//
//// 清理所有驱动资源:包括驱动镜像、
//func (m *driverServiceAppM) ClearAllContainer() {
// dss, err := m.AllService()
// if err != nil {
// m.lc.Errorf("get all service err:%v", err)
// return
// }
// for _, v := range dss {
// // 停止驱动
// _ = m.appModel.StopInstance(dtos.DeviceServiceFromModel(v))
// }
//
// // 删除驱动路径 /var/tedge/edgex-driver-data
// err = utils.RemoveFileOrDir(constants.DriverBaseDir)
// if err != nil {
// m.lc.Errorf("remove driverBaseDir(%s) err:%v", constants.DriverBaseDir, err)
// }
//}
//
//func (m *driverServiceAppM) AllService() ([]models.DeviceService, error) {
// dss, _, err := m.dbClient.DeviceServicesSearch(0, -1, dtos.DeviceServiceSearchQueryRequest{})
// return dss, err
//}
//
//func (m *driverServiceAppM) FlushStatsToAgent() {
// // 取出所有驱动,进行批量更新
// dss, _, edgeXErr := m.Search(context.Background(), dtos.DeviceServiceSearchQueryRequest{})
// if edgeXErr != nil {
// m.lc.Errorf("deviceServicesSearch err %v", edgeXErr)
// return
// }
//
// // 取出所有缓存
// client := pkgcontainer.AgentClientNameFrom(m.dic.Get)
// ctx := context.Background()
// stats, err := client.GetAllDriverMonitor(ctx)
// if err != nil {
// m.lc.Errorf("http request get all driver monitor err: %v", err)
// return
// }
// statsIdMap := make(map[string]models.ServiceStats)
// for _, v := range stats {
// // 只处理驱动的服务
// if v.ServiceType == models.ServiceTypeEnumDriver {
// statsIdMap[v.Id] = dtos.FromDTOServiceStatsToModel(v)
// }
// }
// deleteIds := make([]string, 0)
//
// // 对比获取需要删除的id
// for statsId, _ := range statsIdMap {
// deleteId := statsId
// for _, ds := range dss {
// if ds.Id == statsId {
// deleteId = ""
// }
// }
// if deleteId != "" {
// deleteIds = append(deleteIds, statsId)
// }
// }
//
// // 处理添加/更新
// for _, v := range dss {
// newStats := models.ServiceStats{}
// if _, ok := statsIdMap[v.Id]; ok {
// newStats = statsIdMap[v.Id]
// }
// newStats.Id = v.Id
// newStats.Name = v.Name
// newStats.LogPath = m.appModel.GetInstanceLogPath(dtos.DeviceServiceFromModel(v))
// newStats.ServiceType = models.ServiceTypeEnumDriver
// err = client.AddServiceMonitor(ctx, dtos.FromModelsServiceStatsToDTO(newStats))
// if err != nil {
// m.lc.Errorf("http request add service monitor err: %v", err)
// }
// }
// //处理删除
// for _, v := range deleteIds {
// err = client.DeleteServiceMonitor(ctx, statsIdMap[v].Id)
// if err != nil {
// m.lc.Errorf("http request delete service monitor err: %v", err)
// }
// }
//}
//
//// 异步调用,自动绑定设备、产品、驱动的关系
//func (m *driverServiceAppM) autoAddDevice(ds models.DeviceService) {
// // app模型的不作处理
// if ds.DriverType == constants.DriverLibTypeAppService {
// return
// }
// products, _, err := container.ProductItfFrom(m.dic.Get).ProductsSearch(dtos.ProductSearchQueryRequest{DeviceLibraryId: ds.DeviceLibraryId})
// if err != nil {
// m.lc.Errorf("search product err: %v", err)
// return
// }
// for _, p := range products {
// m.getProductApp().ProductSyncUpdateDeviceServiceId(p)
// }
//}
//
//// 挂载的设备, 目前只支持modbus-rtu这个配置的设备挂载
//func buildMountDevices(devices []models.Device) []string {
// mountDevices := make([]string, 0)
// for _, v := range devices {
// if address, ok := v.Protocols[constants.DriverModbusRtu]["Address"]; ok {
// mountDevices = append(mountDevices, address)
// }
// }
// return mountDevices
//}
//
//func AtopReportDriverConfigEdit(dic *di.Container, dl models.DeviceLibrary, ds models.DeviceService, lc logger.LoggingClient) {
// if !application.CanRequestAtop(dic) {
// return
// }
//
// if dl.IsInternal && dl.DriverType == constants.DriverLibTypeDefault {
// runConfig, _ := json.Marshal(ds.Config)
// err := application.AtopDataReport(constants.DataType_ConfigUpdate, dtos.AtopDataReportDriverConfigUpdate{
// DriverCode: ds.DeviceLibraryId,
// OpenDockerEnv: ds.DockerParamsSwitch,
// DockerEnv: ds.DockerParams,
// OpenExpertMode: ds.ExpertMode,
// ExpertMode: ds.ExpertModeContent,
// RunConfig: string(runConfig),
// })
// if err != nil {
// lc.Warnf("reportDriverConfigEdit err: %v", err)
// }
// }
// lc.Infof("atopReportDriverConfigEdit success dlId(%v)", dl.Id)
//}
//
//func AtopReportDriverRunOrStop(dic *di.Container, dl models.DeviceLibrary, status int, lc logger.LoggingClient) {
// if !application.CanRequestAtop(dic) {
// return
// }
//
// if dl.IsInternal && dl.DriverType == constants.DriverLibTypeDefault {
// // 不上报 停止中、启动中 这种中间状态,不好控制
// err := application.AtopDataReport(constants.DataType_DriverRunOrStop, dtos.AtopDataReportDriverRunOrStop{
// DriverCode: dl.Id,
// RunStatus: status,
// })
// if err != nil {
// lc.Errorf("reportDriverRunOrStop dlId(%v) err: %v", dl.Id, err)
// return
// }
// }
// lc.Infof("reportDriverRunOrStop success dlId(%v)", dl.Id)
//}
//
//func AtopReportDriverDelete(dic *di.Container, dl models.DeviceLibrary, lc logger.LoggingClient) {
// if !application.CanRequestAtop(dic) {
// return
// }
//
// if dl.IsInternal && dl.DriverType == constants.DriverLibTypeDefault {
// err := application.AtopDataReport(constants.DataType_DriverDelete, dtos.AtopDataReportDriverDelete{
// DriverCode: dl.Id,
// })
// if err != nil {
// lc.Errorf("reportDriverDelete dlId(%v) err: %v", dl.Id, err)
// return
// }
// }
// lc.Infof("reportDriverDelete success dlId(%v)", dl.Id)
//}

View File

@ -0,0 +1,105 @@
package driverserviceapp
import (
"context"
"github.com/winc-link/hummingbird/internal/dtos"
interfaces "github.com/winc-link/hummingbird/internal/hummingbird/core/interface"
"github.com/winc-link/hummingbird/internal/pkg/constants"
"github.com/winc-link/hummingbird/internal/pkg/di"
"github.com/winc-link/hummingbird/internal/pkg/logger"
"github.com/winc-link/hummingbird/internal/pkg/middleware"
"sync"
"time"
//"gitlab.com/tedge/edgex/internal/tedge/resource/interfaces"
//
//"gitlab.com/tedge/edgex/internal/dtos"
//"gitlab.com/tedge/edgex/internal/pkg/constants"
pkgContainer "github.com/winc-link/hummingbird/internal/pkg/container"
//"gitlab.com/tedge/edgex/internal/pkg/di"
//"gitlab.com/tedge/edgex/internal/pkg/logger"
//"gitlab.com/tedge/edgex/internal/pkg/middleware"
resourceContainer "github.com/winc-link/hummingbird/internal/hummingbird/core/container"
)
/**
rpc
*/
type DeviceServiceMonitor struct {
ds dtos.DeviceService
isRunning bool
ctx context.Context
dic *di.Container
lc logger.LoggingClient
exitChan chan struct{}
mutex sync.RWMutex
}
func NewDeviceServiceMonitor(ctx context.Context, ds dtos.DeviceService, dic *di.Container) *DeviceServiceMonitor {
dsm := &DeviceServiceMonitor{
ds: ds,
isRunning: false,
ctx: ctx,
dic: dic,
lc: pkgContainer.LoggingClientFrom(dic.Get),
exitChan: make(chan struct{}),
}
go dsm.monitor()
return dsm
}
func (dsm *DeviceServiceMonitor) monitor() {
// 监控间隔
tickTime := time.Second * 5
timeTickerChan := time.Tick(tickTime)
for {
select {
case <-dsm.ctx.Done():
dsm.lc.Infof("close to DeviceServiceMonitor dsId: %s", dsm.ds.Id)
return
case <-dsm.exitChan:
dsm.lc.Infof("close to DeviceServiceMonitor dsId: %s", dsm.ds.Id)
return
case <-timeTickerChan:
dsm.CheckServiceAvailable()
}
}
}
func (dsm *DeviceServiceMonitor) CheckServiceAvailable() {
dsm.mutex.Lock()
defer dsm.mutex.Unlock()
ctx := middleware.WithCorrelationId(context.Background())
dsApp := resourceContainer.DriverServiceAppFrom(dsm.dic.Get)
_, err := dsApp.Get(dsm.ctx, dsm.ds.Id)
if err != nil {
dsm.lc.Infof("monitor get driver instance err %+v", err.Error())
dsm.exitChan <- struct{}{}
return
}
isRunning := interfaces.DMIFrom(dsm.dic.Get).InstanceState(dsm.ds)
// 驱动运行状态更改
if dsm.isRunning != isRunning {
dsm.lc.Debugf("id [%s] before status [%v] current status [%v]: %v", dsm.ds.Id, dsm.isRunning, isRunning, middleware.FromContext(ctx))
// 状态更改上报
}
// 更新管理驱动实例状态
if !dsApp.InProgress(dsm.ds.Id) {
if isRunning {
dsApp.SetState(dsm.ds.Id, constants.RunStatusStarted)
} else {
dsApp.SetState(dsm.ds.Id, constants.RunStatusStopped)
}
}
if dsm.isRunning && !isRunning {
//OfflineDevicesByServiceId(ctx, dsm.dic, dsm.ds.Id)
}
dsm.isRunning = isRunning
}
func (dsm *DeviceServiceMonitor) Stop() {
close(dsm.exitChan)
}

View File

@ -0,0 +1,159 @@
/*******************************************************************************
* 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 homepageapp
import (
"context"
"github.com/winc-link/hummingbird/internal/dtos"
resourceContainer "github.com/winc-link/hummingbird/internal/hummingbird/core/container"
interfaces "github.com/winc-link/hummingbird/internal/hummingbird/core/interface"
"github.com/winc-link/hummingbird/internal/models"
"github.com/winc-link/hummingbird/internal/pkg/constants"
"github.com/winc-link/hummingbird/internal/pkg/container"
"github.com/winc-link/hummingbird/internal/pkg/di"
"github.com/winc-link/hummingbird/internal/pkg/logger"
"time"
)
const (
HummingbridDoc = "https://doc.hummingbird.winc-link.com/"
)
type homePageApp struct {
dic *di.Container
lc logger.LoggingClient
dbClient interfaces.DBClient
}
func NewHomePageApp(ctx context.Context, dic *di.Container) interfaces.HomePageItf {
dbClient := resourceContainer.DBClientFrom(dic.Get)
return &homePageApp{
dic: dic,
lc: container.LoggingClientFrom(dic.Get),
dbClient: dbClient,
}
}
func (h homePageApp) HomePageInfo(ctx context.Context, req dtos.HomePageRequest) (response dtos.HomePageResponse, err error) {
var responseResponse dtos.HomePageResponse
devices, deviceTotal, err := h.dbClient.DevicesSearch(0, -1, dtos.DeviceSearchQueryRequest{})
var selfDeviceTotal uint32
for _, device := range devices {
if device.Platform == constants.IotPlatform_LocalIot {
selfDeviceTotal++
}
}
responseResponse.PageInfo.Device.Total = deviceTotal
responseResponse.PageInfo.Device.Self = selfDeviceTotal
if deviceTotal-selfDeviceTotal < 0 {
responseResponse.PageInfo.Device.Other = 0
} else {
responseResponse.PageInfo.Device.Other = deviceTotal - selfDeviceTotal
}
products, productTotal, err := h.dbClient.ProductsSearch(0, -1, false, dtos.ProductSearchQueryRequest{})
var selfProductTotal uint32
for _, product := range products {
if product.Platform == constants.IotPlatform_LocalIot {
selfProductTotal++
}
}
responseResponse.PageInfo.Product.Total = productTotal
responseResponse.PageInfo.Product.Self = selfProductTotal
if productTotal-selfProductTotal < 0 {
responseResponse.PageInfo.Product.Other = 0
} else {
responseResponse.PageInfo.Product.Other = productTotal - selfProductTotal
}
responseResponse.PageInfo.CloudInstance.StopCount = responseResponse.PageInfo.CloudInstance.Count - responseResponse.PageInfo.CloudInstance.RunCount
if responseResponse.PageInfo.CloudInstance.StopCount < 0 {
responseResponse.PageInfo.CloudInstance.StopCount = 0
}
var searchQuickNavigationReq dtos.QuickNavigationSearchQueryRequest
searchQuickNavigationReq.OrderBy = "sort"
quickNavigations, _, _ := h.dbClient.QuickNavigationSearch(0, -1, searchQuickNavigationReq)
navigations := make([]dtos.QuickNavigation, 0)
for _, navigation := range quickNavigations {
navigations = append(navigations, dtos.QuickNavigation{
Id: navigation.Id,
Name: navigation.Name,
Icon: navigation.Icon,
//JumpLink: navigation.JumpLink,
})
}
responseResponse.QuickNavigation = navigations
var searchDocsReq dtos.DocsSearchQueryRequest
searchDocsReq.OrderBy = "sort"
dbDocs, _, _ := h.dbClient.DocsSearch(0, -1, searchDocsReq)
docs := make([]dtos.Doc, 0)
for _, doc := range dbDocs {
docs = append(docs, dtos.Doc{
Name: doc.Name,
JumpLink: doc.JumpLink,
})
}
responseResponse.Docs.More = HummingbridDoc
responseResponse.Docs.Doc = docs
alertRuleApp := resourceContainer.AlertRuleAppNameFrom(h.dic.Get)
alertResp, _ := alertRuleApp.AlertPlate(ctx, time.Now().AddDate(0, 0, -1).UnixMilli())
responseResponse.AlertPlate = alertResp
var alertTotal uint32
for _, alert := range responseResponse.AlertPlate {
alertTotal += uint32(alert.Count)
}
responseResponse.PageInfo.Alert.Total = alertTotal
//设备消息总数
var msgGatherReq dtos.MsgGatherSearchQueryRequest
msgGatherReq.Date = append(append(append(append(append(append(msgGatherReq.Date,
time.Now().AddDate(0, 0, -1).Format("2006-01-02")),
time.Now().AddDate(0, 0, -2).Format("2006-01-02")),
time.Now().AddDate(0, 0, -3).Format("2006-01-02")),
time.Now().AddDate(0, 0, -4).Format("2006-01-02")),
time.Now().AddDate(0, 0, -5).Format("2006-01-02")),
time.Now().AddDate(0, 0, -6).Format("2006-01-02"))
msgGather, _, err := h.dbClient.MsgGatherSearch(0, -1, msgGatherReq)
responseResponse.MsgGather = append(append(append(append(append(append(responseResponse.MsgGather, dtos.MsgGather{
Date: time.Now().AddDate(0, 0, -1).Format("2006-01-02"),
Count: getMsgGatherCountByDate(msgGather, time.Now().AddDate(0, 0, -1).Format("2006-01-02")),
}), dtos.MsgGather{
Date: time.Now().AddDate(0, 0, -2).Format("2006-01-02"),
Count: getMsgGatherCountByDate(msgGather, time.Now().AddDate(0, 0, -2).Format("2006-01-02")),
}), dtos.MsgGather{
Date: time.Now().AddDate(0, 0, -3).Format("2006-01-02"),
Count: getMsgGatherCountByDate(msgGather, time.Now().AddDate(0, 0, -3).Format("2006-01-02")),
}), dtos.MsgGather{
Date: time.Now().AddDate(0, 0, -4).Format("2006-01-02"),
Count: getMsgGatherCountByDate(msgGather, time.Now().AddDate(0, 0, -4).Format("2006-01-02")),
}), dtos.MsgGather{
Date: time.Now().AddDate(0, 0, -5).Format("2006-01-02"),
Count: getMsgGatherCountByDate(msgGather, time.Now().AddDate(0, 0, -5).Format("2006-01-02")),
}), dtos.MsgGather{
Date: time.Now().AddDate(0, 0, -6).Format("2006-01-02"),
Count: getMsgGatherCountByDate(msgGather, time.Now().AddDate(0, 0, -6).Format("2006-01-02")),
})
return responseResponse, nil
}
func getMsgGatherCountByDate(msgGather []models.MsgGather, data string) int {
for _, gather := range msgGather {
if gather.Date == data {
return gather.Count
}
}
return 0
}

View File

@ -0,0 +1,107 @@
/*******************************************************************************
* Copyright 2017.
*
* 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 languagesdkapp
import (
"context"
"encoding/json"
"github.com/winc-link/hummingbird/internal/dtos"
"github.com/winc-link/hummingbird/internal/hummingbird/core/container"
interfaces "github.com/winc-link/hummingbird/internal/hummingbird/core/interface"
"github.com/winc-link/hummingbird/internal/models"
pkgcontainer "github.com/winc-link/hummingbird/internal/pkg/container"
"github.com/winc-link/hummingbird/internal/pkg/di"
"github.com/winc-link/hummingbird/internal/pkg/logger"
)
type languageSDKApp struct {
dic *di.Container
lc logger.LoggingClient
dbClient interfaces.DBClient
}
func (m languageSDKApp) LanguageSDKSearch(ctx context.Context, req dtos.LanguageSDKSearchQueryRequest) ([]dtos.LanguageSDKSearchResponse, uint32, error) {
offset, limit := req.BaseSearchConditionQuery.GetPage()
req.BaseSearchConditionQuery.OrderBy = "sort:asc"
languages, total, err := m.dbClient.LanguageSearch(offset, limit, req)
if err != nil {
return nil, 0, err
}
libs := make([]dtos.LanguageSDKSearchResponse, len(languages))
for i, language := range languages {
libs[i] = dtos.LanguageSDKSearchResponse{
Name: language.Name,
Icon: language.Icon,
Addr: language.Addr,
Description: language.Description,
}
}
return libs, total, nil
}
func (m languageSDKApp) Sync(ctx context.Context, versionName string) error {
filePath := versionName + "/language_sdk.json"
cosApp := container.CosAppNameFrom(m.dic.Get)
bs, err := cosApp.Get(filePath)
if err != nil {
m.lc.Errorf(err.Error())
}
var cosLanguageSdkResp []dtos.LanguageSDK
err = json.Unmarshal(bs, &cosLanguageSdkResp)
if err != nil {
m.lc.Errorf(err.Error())
}
for _, sdk := range cosLanguageSdkResp {
if languageSdk, err := m.dbClient.LanguageSdkByName(sdk.Name); err != nil {
createModel := models.LanguageSdk{
Name: sdk.Name,
Icon: sdk.Icon,
Sort: sdk.Sort,
Addr: sdk.Addr,
Description: sdk.Description,
}
_, err := m.dbClient.AddLanguageSdk(createModel)
if err != nil {
return err
}
} else {
updateModel := models.LanguageSdk{
Id: languageSdk.Id,
Name: sdk.Name,
Icon: sdk.Icon,
Sort: sdk.Sort,
Addr: sdk.Addr,
Description: sdk.Description,
}
err = m.dbClient.UpdateLanguageSdk(updateModel)
if err != nil {
return err
}
}
}
return nil
}
func NewLanguageSDKApp(ctx context.Context, dic *di.Container) interfaces.LanguageSDKApp {
app := &languageSDKApp{
dic: dic,
lc: pkgcontainer.LoggingClientFrom(dic.Get),
dbClient: container.DBClientFrom(dic.Get),
}
return app
}

View File

@ -0,0 +1,25 @@
package application
import (
//"gitlab.com/tedge/edgex/internal/pkg/limit"
//"gitlab.com/tedge/edgex/internal/tedge/resource/config"
"github.com/winc-link/hummingbird/internal/hummingbird/core/config"
"github.com/winc-link/hummingbird/internal/pkg/limit"
)
type LimitMethodConf struct {
methods map[string]struct{}
}
//TODO: 接口限流功能需要重构
func NewLimitMethodConf(configuration config.ConfigurationStruct) limit.LimitMethodConf {
var conf = &LimitMethodConf{methods: make(map[string]struct{})}
for _, method := range configuration.Writable.LimitMethods {
conf.methods[method] = struct{}{}
}
return conf
}
func (lmc *LimitMethodConf) GetLimitMethods() map[string]struct{} {
return lmc.methods
}

View File

@ -0,0 +1,72 @@
/*******************************************************************************
* Copyright 2017.
*
* 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 messageapp
import (
"context"
mqtt "github.com/eclipse/paho.mqtt.golang"
"github.com/winc-link/hummingbird/internal/dtos"
"github.com/winc-link/hummingbird/internal/hummingbird/core/container"
"github.com/winc-link/hummingbird/internal/pkg/errort"
pkgMQTT "github.com/winc-link/hummingbird/internal/tools/mqttclient"
)
func (msp *MessageApp) connectMQTT() (mqttClient pkgMQTT.MQTTClient) {
lc := msp.lc
var req dtos.NewMQTTClient
var consumeCallback mqtt.MessageHandler
var err error
req, consumeCallback, err = msp.prepareMqttConnectParams()
if err != nil {
lc.Errorf("ConnectMQTT failed, err:%v", err)
return
}
connF := func(ctx context.Context) {
msp.lc.Info("ekuiper mqtt connect")
}
disConnF := func(ctx context.Context, msg dtos.CallbackMessage) {
msp.lc.Info("ekuiper mqtt disconnect")
}
mqttClient, err = pkgMQTT.NewMQTTClient(req, lc, consumeCallback, connF, disConnF)
if err != nil {
err = errort.NewCommonErr(errort.MqttConnFail, err)
lc.Errorf("ConnectMQTT failed, err:%v", err)
}
return mqttClient
}
func (tmq *MessageApp) prepareMqttConnectParams() (req dtos.NewMQTTClient, consumeCallback mqtt.MessageHandler, err error) {
config := container.ConfigurationFrom(tmq.dic.Get)
req = dtos.NewMQTTClient{
Broker: config.MessageQueue.URL(),
ClientId: config.MessageQueue.Optional["ClientId"],
Username: config.MessageQueue.Optional["Username"],
Password: config.MessageQueue.Optional["Password"],
}
consumeCallback = tmq.ekuiperMsgHandle
return
}
func (tmq *MessageApp) ekuiperMsgHandle(client mqtt.Client, message mqtt.Message) {
}
func (tmq *MessageApp) pushMsgToMessageBus(msg []byte) {
config := container.ConfigurationFrom(tmq.dic.Get)
tmq.ekuiperMqttClient.AsyncPublish(nil, config.MessageQueue.PublishTopicPrefix, msg, false)
}

View File

@ -0,0 +1,124 @@
/*******************************************************************************
* Copyright 2017 Dell Inc.
*
* 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 messageapp
import (
"context"
"encoding/json"
"github.com/kirinlabs/HttpRequest"
"github.com/winc-link/edge-driver-proto/drivercommon"
"github.com/winc-link/hummingbird/internal/dtos"
coreContainer "github.com/winc-link/hummingbird/internal/hummingbird/core/container"
interfaces "github.com/winc-link/hummingbird/internal/hummingbird/core/interface"
"github.com/winc-link/hummingbird/internal/pkg/container"
"github.com/winc-link/hummingbird/internal/pkg/di"
"github.com/winc-link/hummingbird/internal/pkg/errort"
"github.com/winc-link/hummingbird/internal/pkg/logger"
"strconv"
"strings"
"time"
pkgMQTT "github.com/winc-link/hummingbird/internal/tools/mqttclient"
)
type MessageApp struct {
dic *di.Container
lc logger.LoggingClient
dbClient interfaces.DBClient
ekuiperMqttClient pkgMQTT.MQTTClient
}
func NewMessageApp(dic *di.Container) *MessageApp {
lc := container.LoggingClientFrom(dic.Get)
dbClient := coreContainer.DBClientFrom(dic.Get)
msgApp := &MessageApp{
dic: dic,
dbClient: dbClient,
lc: lc,
}
mqttClient := msgApp.connectMQTT()
msgApp.ekuiperMqttClient = mqttClient
msgApp.initeKuiperStreams()
return msgApp
}
func (tmq *MessageApp) initeKuiperStreams() {
req := HttpRequest.NewRequest()
r := make(map[string]string)
r["sql"] = "CREATE STREAM mqtt_stream () WITH (DATASOURCE=\"eventbus/in\", FORMAT=\"JSON\",SHARED = \"true\")"
b, _ := json.Marshal(r)
resp, err := req.Post("http://ekuiper:9081/streams", b)
if err != nil {
tmq.lc.Errorf("init ekuiper stream failed error:%+v", err.Error())
return
}
if resp.StatusCode() == 201 {
body, err := resp.Body()
if err != nil {
tmq.lc.Errorf("init ekuiper stream failed error:%+v", err.Error())
return
}
if strings.Contains(string(body), "created") {
tmq.lc.Infof("init ekuiper stream success")
return
}
} else if resp.StatusCode() == 400 {
body, err := resp.Body()
tmq.lc.Infof("init ekuiper stream body", string(body))
if err != nil {
tmq.lc.Errorf("init ekuiper stream failed error:%+v", err.Error())
return
}
if strings.Contains(string(body), "already exists") {
tmq.lc.Infof("init ekuiper stream plug success")
return
}
} else {
tmq.lc.Errorf("init ekuiper stream failed resp code:%+v", resp.StatusCode())
}
}
func (tmq *MessageApp) DeviceStatusToMessageBus(ctx context.Context, deviceId, deviceStatus string) {
var messageBus dtos.MessageBus
messageBus.DeviceId = deviceId
messageBus.MessageType = "DEVICE_STATUS"
messageBus.Data = map[string]interface{}{
"status": deviceStatus,
"time": time.Now().UnixMilli(),
}
b, _ := json.Marshal(messageBus)
tmq.pushMsgToMessageBus(b)
}
func (tmq *MessageApp) ThingModelMsgReport(ctx context.Context, msg dtos.ThingModelMessage) (*drivercommon.CommonResponse, error) {
tmq.pushMsgToMessageBus(msg.TransformMessageBus())
persistItf := coreContainer.PersistItfFrom(tmq.dic.Get)
err := persistItf.SaveDeviceThingModelData(msg)
if err != nil {
tmq.lc.Error("saveDeviceThingModelData error:", err.Error())
}
response := new(drivercommon.CommonResponse)
if err != nil {
response.Success = false
response.Code = strconv.Itoa(errort.KindDatabaseError)
response.ErrorMessage = err.Error()
} else {
response.Code = "0"
response.Success = true
}
return response, nil
}

View File

@ -0,0 +1,45 @@
/*******************************************************************************
* Copyright 2017.
*
* 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 messagestore
import "sync"
type MsgAckChan struct {
Mu sync.Mutex
Id string
IsClosed bool
DataChan chan interface{}
}
func (mac *MsgAckChan) TryCloseChan() {
mac.Mu.Lock()
defer mac.Mu.Unlock()
if !mac.IsClosed {
close(mac.DataChan)
mac.IsClosed = true
}
}
func (mac *MsgAckChan) TrySendDataAndCloseChan(data interface{}) bool {
mac.Mu.Lock()
defer mac.Mu.Unlock()
if !mac.IsClosed {
mac.DataChan <- data
close(mac.DataChan)
mac.IsClosed = true
return true
}
return false
}

View File

@ -0,0 +1,68 @@
/*******************************************************************************
* Copyright 2017.
*
* 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 messagestore
import (
"context"
"github.com/winc-link/hummingbird/internal/pkg/container"
"github.com/winc-link/hummingbird/internal/pkg/di"
"github.com/winc-link/hummingbird/internal/pkg/logger"
"sync"
)
type MessageStores interface {
StoreMsgId(id string, ch string)
LoadMsgChan(id string) (interface{}, bool)
DeleteMsgId(id string)
GenAckChan(id string) *MsgAckChan
}
type (
MessageStore struct {
logger logger.LoggingClient
ctx context.Context
mutex sync.Mutex
wg *sync.WaitGroup
ackMap sync.Map
}
)
func NewMessageStore(dic *di.Container) *MessageStore {
lc := container.LoggingClientFrom(dic.Get)
return &MessageStore{
logger: lc,
}
}
func (wp *MessageStore) StoreMsgId(id string, ch string) {
wp.ackMap.Store(id, ch)
}
func (wp *MessageStore) DeleteMsgId(id string) {
wp.ackMap.Delete(id)
}
func (wp *MessageStore) LoadMsgChan(id string) (interface{}, bool) {
return wp.ackMap.Load(id)
}
func (wp *MessageStore) GenAckChan(id string) *MsgAckChan {
ack := &MsgAckChan{
Id: id,
DataChan: make(chan interface{}, 1),
}
wp.ackMap.Store(id, ack)
return ack
}

View File

@ -0,0 +1,118 @@
/*******************************************************************************
* Copyright 2017.
*
* 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 monitor
import (
"context"
"github.com/winc-link/hummingbird/internal/dtos"
"github.com/winc-link/hummingbird/internal/hummingbird/core/container"
"github.com/winc-link/hummingbird/internal/pkg/constants"
pkgcontainer "github.com/winc-link/hummingbird/internal/pkg/container"
"github.com/winc-link/hummingbird/internal/pkg/di"
"github.com/winc-link/hummingbird/internal/pkg/logger"
"sync"
"time"
)
type monitor struct {
dic *di.Container
lc logger.LoggingClient
serviceMonitorMap sync.Map
ctx context.Context
exitCh chan struct{}
systemMonitor *systemMonitor
}
func NewMonitor(ctx context.Context, dic *di.Container) *monitor {
lc := pkgcontainer.LoggingClientFrom(dic.Get)
m := monitor{
dic: dic,
lc: lc,
serviceMonitorMap: sync.Map{},
ctx: context.Background(),
exitCh: make(chan struct{}),
systemMonitor: NewSystemMonitor(dic, lc),
}
//go m.run()
return &m
}
func systemMetricsTypeToTime(t string) (time.Time, time.Time) {
switch t {
case constants.HourMetricsType:
end := time.Now()
start := time.Now().Add(-1 * time.Hour)
return start, end
case constants.HalfDayMetricsType:
end := time.Now()
start := time.Now().Add(-12 * time.Hour)
return start, end
case constants.DayMetricsType:
end := time.Now()
start := time.Now().Add(-24 * time.Hour)
return start, end
default:
end := time.Now()
start := time.Now().Add(-1 * time.Hour)
return start, end
}
}
func (m *monitor) GetSystemMetrics(ctx context.Context, query dtos.SystemMetricsQuery) (dtos.SystemMetricsResponse, error) {
dbClient := container.DBClientFrom(m.dic.Get)
start, end := systemMetricsTypeToTime(query.MetricsType)
metrics, err := dbClient.GetSystemMetrics(start.UnixMilli(), end.UnixMilli())
if err != nil {
return dtos.SystemMetricsResponse{}, err
}
resp := dtos.SystemMetricsResponse{
Metrics: make([]dtos.SystemStatResponse, 0),
}
step := 1
if query.MetricsType == constants.HalfDayMetricsType || query.MetricsType == constants.DayMetricsType {
step = 5
}
for i := 0; i < len(metrics); i = i + step {
metric := metrics[i]
item := dtos.SystemStatResponse{
Timestamp: metric.Timestamp,
CpuUsedPercent: metric.CpuUsedPercent,
MemoryTotal: metric.Memory.Total,
MemoryUsed: metric.Memory.Used,
MemoryUsedPercent: metric.Memory.UsedPercent,
DiskTotal: metric.Disk.Total,
DiskUsed: metric.Disk.Used,
DiskUsedPercent: metric.Disk.UsedPercent,
Openfiles: metric.Openfiles,
}
if iface, ok := metric.Network[query.Iface]; ok {
item.NetSentBytes = iface.BytesSentPre
item.NetRecvBytes = iface.BytesRecvPre
}
resp.Metrics = append(resp.Metrics, item)
}
resp.Total = len(resp.Metrics)
return resp, nil
}

View File

@ -0,0 +1,218 @@
/*******************************************************************************
* Copyright 2017.
*
* 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 monitor
import (
"context"
"fmt"
"github.com/shirou/gopsutil/v3/cpu"
"github.com/shirou/gopsutil/v3/disk"
"github.com/shirou/gopsutil/v3/host"
"github.com/shirou/gopsutil/v3/load"
"github.com/shirou/gopsutil/v3/mem"
"github.com/shirou/gopsutil/v3/net"
"github.com/shirou/gopsutil/v3/process"
"github.com/winc-link/hummingbird/internal/dtos"
"github.com/winc-link/hummingbird/internal/hummingbird/core/container"
interfaces "github.com/winc-link/hummingbird/internal/hummingbird/core/interface"
"github.com/winc-link/hummingbird/internal/pkg/di"
"github.com/winc-link/hummingbird/internal/pkg/logger"
"github.com/winc-link/hummingbird/internal/pkg/utils"
"strconv"
"time"
)
type systemMonitor struct {
ctx context.Context
dic *di.Container
dbClient interfaces.DBClient
lc logger.LoggingClient
exitCh chan struct{}
ethMap map[string]*dtos.SystemNetwork
//aa *AlertApplication
diskAlert int
cpuAlert int
}
func NewSystemMonitor(dic *di.Container, lc logger.LoggingClient) *systemMonitor {
dbClient := container.DBClientFrom(dic.Get)
ctx := context.Background()
m := systemMonitor{
ctx: ctx,
dic: dic,
dbClient: dbClient,
lc: lc,
ethMap: make(map[string]*dtos.SystemNetwork),
exitCh: make(chan struct{}),
//aa: NewAlertApplication(ctx, dic, lc),
diskAlert: 3, // 默认告警 3 次
cpuAlert: 3,
}
go m.run()
return &m
}
func (m *systemMonitor) run() {
tick := time.Tick(time.Minute) // 1 minute
tickClear := time.Tick(24 * time.Hour) // 每24小时删除一次数据
go func() {
for {
select {
case <-tick:
metrics := m.collect()
if err := m.dbClient.UpdateSystemMetrics(metrics); err != nil {
m.lc.Errorf("failed to UpdateSystemMetrics %v", err)
}
//m.reportSystemMetricsAlert(metrics)
case <-tickClear:
m.clearMetrics()
case <-m.exitCh:
return
}
}
}()
}
func (m *systemMonitor) clearMetrics() {
min := "0"
max := strconv.FormatInt(time.Now().Add(-24*time.Hour).UnixMilli(), 10)
m.lc.Infof("remove system metrics data from %v to %v", min, max)
if err := m.dbClient.RemoveRangeSystemMetrics(min, max); err != nil {
m.lc.Error("failed to clearMetrics", err)
}
}
func (m *systemMonitor) Close() {
close(m.exitCh)
}
func (m *systemMonitor) collect() dtos.SystemMetrics {
return dtos.SystemMetrics{
Timestamp: time.Now().UnixMilli(),
CpuUsedPercent: getCpu(),
CpuAvg: getCpuLoad(),
Memory: getMemory(),
Network: getNetwork(m.ethMap),
Disk: getDisk(),
Openfiles: getOpenfiles(),
}
}
func getMemory() dtos.SystemMemory {
v, _ := mem.VirtualMemory()
return dtos.SystemMemory{
Total: v.Total,
Used: v.Used,
UsedPercent: v.UsedPercent,
}
}
func getCpu() float64 {
// cpu的使用率
totalPercent, _ := cpu.Percent(0, false)
if len(totalPercent) <= 0 {
return 0
}
return totalPercent[0]
}
func getCpuLoad() float64 {
// cpu的使用率
avg, _ := load.Avg()
if avg == nil {
return 0
}
return avg.Load1
}
func getDisk() dtos.SystemDisk {
// 目录 / 的磁盘使用率
usage, _ := disk.Usage("/")
return dtos.SystemDisk{
Path: "/",
Total: usage.Total,
Used: usage.Used,
UsedPercent: usage.UsedPercent,
}
}
func getNetwork(ethMap map[string]*dtos.SystemNetwork) map[string]dtos.SystemNetwork {
stats := make(map[string]dtos.SystemNetwork)
info, _ := net.IOCounters(true)
for _, v := range info {
ethName := v.Name
if !utils.CheckNetIface(ethName) {
continue
}
if v.BytesSent <= 0 && v.BytesRecv <= 0 {
continue
}
_, ok := ethMap[ethName]
if !ok {
ethMap[ethName] = &dtos.SystemNetwork{}
}
ethItem := ethMap[ethName]
var (
byteRecvPre uint64
byteSentPre uint64
)
now := time.Now().Unix()
if ethItem.Last == 0 {
// 第一次采集,没有初始值,不计算
} else {
byteRecvPre = v.BytesRecv - ethItem.BytesRecv
byteSentPre = v.BytesSent - ethItem.BytesSent
}
item := dtos.SystemNetwork{
Name: ethName,
BytesSent: v.BytesSent,
BytesRecv: v.BytesRecv,
BytesRecvPre: byteRecvPre,
BytesSentPre: byteSentPre,
Last: now,
}
stats[ethName] = item
ethMap[ethName] = &item
}
return stats
}
func getOpenfiles() int {
// only linux
// https://github.com/shirou/gopsutil#process-class
processes, _ := process.Processes()
var openfiles int
for _, pid := range processes {
files, _ := pid.OpenFiles()
openfiles += len(files)
}
return openfiles
}
func getPlatform() {
// 查看平台信息
platform, family, version, _ := host.PlatformInformation()
fmt.Printf("platform = %v ,family = %v , version = %v \n", platform, family, version)
}

View File

@ -0,0 +1,802 @@
/*******************************************************************************
* Copyright 2017 Dell Inc.
*
* 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 persistence
import (
"context"
"encoding/json"
"errors"
"github.com/winc-link/edge-driver-proto/thingmodel"
"github.com/winc-link/hummingbird/internal/dtos"
"github.com/winc-link/hummingbird/internal/hummingbird/core/application/messagestore"
resourceContainer "github.com/winc-link/hummingbird/internal/hummingbird/core/container"
interfaces "github.com/winc-link/hummingbird/internal/hummingbird/core/interface"
"github.com/winc-link/hummingbird/internal/models"
"github.com/winc-link/hummingbird/internal/pkg/constants"
"github.com/winc-link/hummingbird/internal/pkg/container"
"github.com/winc-link/hummingbird/internal/pkg/di"
"github.com/winc-link/hummingbird/internal/pkg/logger"
"github.com/winc-link/hummingbird/internal/pkg/utils"
"strconv"
)
type persistApp struct {
dic *di.Container
lc logger.LoggingClient
dbClient interfaces.DBClient
dataDbClient interfaces.DataDBClient
}
func NewPersistApp(dic *di.Container) *persistApp {
lc := container.LoggingClientFrom(dic.Get)
dbClient := resourceContainer.DBClientFrom(dic.Get)
dataDbClient := resourceContainer.DataDBClientFrom(dic.Get)
pstApp := &persistApp{
lc: lc,
dic: dic,
dbClient: dbClient,
dataDbClient: dataDbClient,
}
return pstApp
}
func (pst *persistApp) SaveDeviceThingModelData(req dtos.ThingModelMessage) error {
switch pst.dataDbClient.GetDataDBType() {
case constants.LevelDB:
return pst.saveDeviceThingModelToLevelDB(req)
case constants.TDengine:
return pst.saveDeviceThingModelToTdengine(req)
default:
return nil
}
}
func (pst *persistApp) saveDeviceThingModelToLevelDB(req dtos.ThingModelMessage) error {
switch req.GetOpType() {
case thingmodel.OperationType_PROPERTY_REPORT:
propertyMsg, err := req.TransformMessageDataByProperty()
if err != nil {
return err
}
kvs := make(map[string]interface{})
for s, data := range propertyMsg.Data {
key := generatePropertyLeveldbKey(req.Cid, s, data.Time)
value, err := data.Marshal()
if err != nil {
continue
}
kvs[key] = value
}
//批量写。
err = pst.dataDbClient.Insert(context.Background(), "", kvs)
if err != nil {
return err
}
case thingmodel.OperationType_EVENT_REPORT:
eventMsg, err := req.TransformMessageDataByEvent()
if err != nil {
return err
}
kvs := make(map[string]interface{})
var key string
key = generateEventLeveldbKey(req.Cid, eventMsg.Data.EventCode, eventMsg.Data.EventTime)
value, _ := eventMsg.Data.Marshal()
kvs[key] = value
//批量写。
err = pst.dataDbClient.Insert(context.Background(), "", kvs)
if err != nil {
return err
}
case thingmodel.OperationType_SERVICE_EXECUTE:
serviceMsg, err := req.TransformMessageDataByService()
kvs := make(map[string]interface{})
var key string
key = generateActionLeveldbKey(req.Cid, serviceMsg.Code, serviceMsg.Time)
value, _ := serviceMsg.Marshal()
kvs[key] = value
err = pst.dataDbClient.Insert(context.Background(), "", kvs)
if err != nil {
return err
}
case thingmodel.OperationType_SERVICE_EXECUTE_RESPONSE:
serviceMsg, err := req.TransformMessageDataByServiceExec()
if err != nil {
return err
}
device, err := pst.dbClient.DeviceById(req.Cid)
if err != nil {
return err
}
product, err := pst.dbClient.ProductById(device.ProductId)
if err != nil {
return err
}
var find bool
var callType constants.CallType
for _, action := range product.Actions {
if action.Code == serviceMsg.Code {
find = true
callType = action.CallType
break
}
}
if !find {
return errors.New("")
}
if callType == constants.CallTypeSync {
messageStore := resourceContainer.MessageStoreItfFrom(pst.dic.Get)
ack, ok := messageStore.LoadMsgChan(serviceMsg.MsgId)
if !ok {
//可能是超时了。
return nil
}
if v, ok := ack.(*messagestore.MsgAckChan); ok {
v.TrySendDataAndCloseChan(serviceMsg.OutputParams)
messageStore.DeleteMsgId(serviceMsg.MsgId)
}
} else if callType == constants.CallTypeAsync {
kvs := make(map[string]interface{})
var key string
key = generateActionLeveldbKey(req.Cid, serviceMsg.Code, serviceMsg.Time)
value, _ := serviceMsg.Marshal()
kvs[key] = value
err = pst.dataDbClient.Insert(context.Background(), "", kvs)
if err != nil {
return err
}
}
case thingmodel.OperationType_DATA_BATCH_REPORT:
msg, err := req.TransformMessageDataByBatchReport()
if err != nil {
return err
}
t := msg.Time
kvs := make(map[string]interface{})
for code, property := range msg.Data.Properties {
var data dtos.ReportData
data.Value = property.Value
data.Time = t
key := generatePropertyLeveldbKey(req.Cid, code, t)
value, err := data.Marshal()
if err != nil {
continue
}
kvs[key] = value
}
for code, event := range msg.Data.Events {
var data dtos.EventData
data.OutputParams = event.OutputParams
data.EventTime = t
data.EventCode = code
key := generateEventLeveldbKey(req.Cid, code, t)
value, _ := data.Marshal()
kvs[key] = value
}
//批量写。
err = pst.dataDbClient.Insert(context.Background(), "", kvs)
if err != nil {
return err
}
return nil
}
return nil
}
func (pst *persistApp) saveDeviceThingModelToTdengine(req dtos.ThingModelMessage) error {
switch req.GetOpType() {
case thingmodel.OperationType_PROPERTY_REPORT:
propertyMsg, err := req.TransformMessageDataByProperty()
if err != nil {
return err
}
data := make(map[string]interface{})
for s, reportData := range propertyMsg.Data {
data[s] = reportData.Value
}
err = pst.dataDbClient.Insert(context.Background(), constants.DB_PREFIX+req.Cid, data)
if err != nil {
return err
}
case thingmodel.OperationType_EVENT_REPORT:
eventMsg, err := req.TransformMessageDataByEvent()
if err != nil {
return err
}
data := make(map[string]interface{})
data[eventMsg.Data.EventCode] = eventMsg.Data
err = pst.dataDbClient.Insert(context.Background(), constants.DB_PREFIX+req.Cid, data)
if err != nil {
return err
}
case thingmodel.OperationType_SERVICE_EXECUTE:
serviceMsg, err := req.TransformMessageDataByService()
if err != nil {
return err
}
v, _ := serviceMsg.Marshal()
data := make(map[string]interface{})
data[serviceMsg.Code] = string(v)
err = pst.dataDbClient.Insert(context.Background(), constants.DB_PREFIX+req.Cid, data)
if err != nil {
return err
}
case thingmodel.OperationType_DATA_BATCH_REPORT:
case thingmodel.OperationType_SERVICE_EXECUTE_RESPONSE:
serviceMsg, err := req.TransformMessageDataByServiceExec()
if err != nil {
return err
}
device, err := pst.dbClient.DeviceById(req.Cid)
if err != nil {
return err
}
product, err := pst.dbClient.ProductById(device.ProductId)
if err != nil {
return err
}
var find bool
var callType constants.CallType
for _, action := range product.Actions {
if action.Code == serviceMsg.Code {
find = true
callType = action.CallType
break
}
}
if !find {
return errors.New("")
}
if callType == constants.CallTypeSync {
messageStore := resourceContainer.MessageStoreItfFrom(pst.dic.Get)
ack, ok := messageStore.LoadMsgChan(serviceMsg.MsgId)
if !ok {
//可能是超时了。
return nil
}
if v, ok := ack.(*messagestore.MsgAckChan); ok {
v.TrySendDataAndCloseChan(serviceMsg.OutputParams)
messageStore.DeleteMsgId(serviceMsg.MsgId)
}
} else if callType == constants.CallTypeAsync {
v, _ := serviceMsg.Marshal()
data := make(map[string]interface{})
data[serviceMsg.Code] = string(v)
err = pst.dataDbClient.Insert(context.Background(), constants.DB_PREFIX+req.Cid, data)
if err != nil {
return err
}
}
}
return nil
}
func generatePropertyLeveldbKey(cid, code string, reportTime int64) string {
return cid + "-" + constants.Property + "-" + code + "-" + strconv.Itoa(int(reportTime))
}
func generateOncePropertyLeveldbKey(cid, code string) string {
return cid + "-" + constants.Property + "-" + code
}
func generateEventLeveldbKey(cid, code string, reportTime int64) string {
return cid + "-" + constants.Event + "-" + code + "-" + strconv.Itoa(int(reportTime))
}
func generateOnceEventLeveldbKey(cid, code string) string {
return cid + "-" + constants.Event + "-" + code
}
func generateActionLeveldbKey(cid, code string, reportTime int64) string {
return cid + "-" + constants.Action + "-" + code + "-" + strconv.Itoa(int(reportTime))
}
func generateOnceActionLeveldbKey(cid, code string) string {
return cid + "-" + constants.Action + "-" + code
}
func (pst *persistApp) searchDeviceThingModelPropertyDataFromLevelDB(req dtos.ThingModelPropertyDataRequest) (interface{}, error) {
deviceInfo, err := pst.dbClient.DeviceById(req.DeviceId)
if err != nil {
return nil, err
}
var productInfo models.Product
response := make([]dtos.ThingModelDataResponse, 0)
productInfo, err = pst.dbClient.ProductById(deviceInfo.ProductId)
if err != nil {
return nil, err
}
if req.Code == "" {
for _, property := range productInfo.Properties {
req.Code = property.Code
ksv, _, err := pst.dataDbClient.GetDeviceProperty(req, deviceInfo)
if err != nil {
pst.lc.Errorf("GetDeviceProperty error %+v", err)
continue
}
var reportData dtos.ReportData
if len(ksv) > 0 {
reportData = ksv[0]
}
var unit string
if property.TypeSpec.Type == constants.SpecsTypeInt || property.TypeSpec.Type == constants.SpecsTypeFloat {
var typeSpecIntOrFloat models.TypeSpecIntOrFloat
_ = json.Unmarshal([]byte(property.TypeSpec.Specs), &typeSpecIntOrFloat)
unit = typeSpecIntOrFloat.Unit
} else if property.TypeSpec.Type == constants.SpecsTypeEnum {
//enum 的单位需要特殊处理一下
enumTypeSpec := make(map[string]string)
_ = json.Unmarshal([]byte(property.TypeSpec.Specs), &enumTypeSpec)
for key, value := range enumTypeSpec {
s := utils.InterfaceToString(reportData.Value)
if key == s {
unit = value
}
}
}
response = append(response, dtos.ThingModelDataResponse{
ReportData: reportData,
Code: property.Code,
DataType: string(property.TypeSpec.Type),
Name: property.Name,
Unit: unit,
AccessMode: property.AccessMode,
})
}
}
return response, nil
}
func (pst *persistApp) searchDeviceThingModelHistoryPropertyDataFromTDengine(req dtos.ThingModelPropertyDataRequest) (interface{}, int, error) {
var count int
deviceInfo, err := pst.dbClient.DeviceById(req.DeviceId)
if err != nil {
return nil, count, err
}
var productInfo models.Product
productInfo, err = pst.dbClient.ProductById(deviceInfo.ProductId)
if err != nil {
return nil, count, err
}
var response []dtos.ReportData
for _, property := range productInfo.Properties {
if property.Code == req.Code {
req.Code = property.Code
response, count, err = pst.dataDbClient.GetDeviceProperty(req, deviceInfo)
if err != nil {
pst.lc.Errorf("GetDeviceProperty error %+v", err)
}
var typeSpecIntOrFloat models.TypeSpecIntOrFloat
if property.TypeSpec.Type == constants.SpecsTypeInt || property.TypeSpec.Type == constants.SpecsTypeFloat {
_ = json.Unmarshal([]byte(property.TypeSpec.Specs), &typeSpecIntOrFloat)
}
if typeSpecIntOrFloat.Unit == "" {
typeSpecIntOrFloat.Unit = "-"
}
break
}
}
return response, count, nil
}
func (pst *persistApp) searchDeviceThingModelPropertyDataFromTDengine(req dtos.ThingModelPropertyDataRequest) (interface{}, error) {
deviceInfo, err := pst.dbClient.DeviceById(req.DeviceId)
if err != nil {
return nil, err
}
var productInfo models.Product
response := make([]dtos.ThingModelDataResponse, 0)
productInfo, err = pst.dbClient.ProductById(deviceInfo.ProductId)
if err != nil {
return nil, err
}
if req.Code == "" {
for _, property := range productInfo.Properties {
req.Code = property.Code
ksv, _, err := pst.dataDbClient.GetDeviceProperty(req, deviceInfo)
if err != nil {
pst.lc.Errorf("GetDeviceProperty error %+v", err)
continue
}
var reportData dtos.ReportData
if len(ksv) > 0 {
reportData = ksv[0]
}
var unit string
if property.TypeSpec.Type == constants.SpecsTypeInt || property.TypeSpec.Type == constants.SpecsTypeFloat {
var typeSpecIntOrFloat models.TypeSpecIntOrFloat
_ = json.Unmarshal([]byte(property.TypeSpec.Specs), &typeSpecIntOrFloat)
unit = typeSpecIntOrFloat.Unit
} else if property.TypeSpec.Type == constants.SpecsTypeEnum {
//enum 的单位需要特殊处理一下
enumTypeSpec := make(map[string]string)
_ = json.Unmarshal([]byte(property.TypeSpec.Specs), &enumTypeSpec)
//pst.lc.Info("reportDataType enumTypeSpec", enumTypeSpec)
for key, value := range enumTypeSpec {
s := utils.InterfaceToString(reportData.Value)
if key == s {
unit = value
}
}
}
if unit == "" {
unit = "-"
}
response = append(response, dtos.ThingModelDataResponse{
ReportData: reportData,
Code: property.Code,
DataType: string(property.TypeSpec.Type),
Name: property.Name,
Unit: unit,
AccessMode: property.AccessMode,
})
}
}
return response, nil
}
func (pst *persistApp) SearchDeviceThingModelPropertyData(req dtos.ThingModelPropertyDataRequest) (interface{}, error) {
switch pst.dataDbClient.GetDataDBType() {
case constants.LevelDB:
return pst.searchDeviceThingModelPropertyDataFromLevelDB(req)
case constants.TDengine:
return pst.searchDeviceThingModelPropertyDataFromTDengine(req)
default:
return make([]interface{}, 0), nil
}
}
func (pst *persistApp) searchDeviceThingModelHistoryPropertyDataFromLevelDB(req dtos.ThingModelPropertyDataRequest) (interface{}, int, error) {
deviceInfo, err := pst.dbClient.DeviceById(req.DeviceId)
if err != nil {
return nil, 0, err
}
var count int
var productInfo models.Product
productInfo, err = pst.dbClient.ProductById(deviceInfo.ProductId)
if err != nil {
return nil, 0, err
}
var response []dtos.ReportData
for _, property := range productInfo.Properties {
if property.Code == req.Code {
response, count, err = pst.dataDbClient.GetDeviceProperty(req, deviceInfo)
if err != nil {
pst.lc.Errorf("GetHistoryDeviceProperty error %+v", err)
}
break
}
}
return response, count, nil
}
func (pst *persistApp) SearchDeviceThingModelHistoryPropertyData(req dtos.ThingModelPropertyDataRequest) (interface{}, int, error) {
switch pst.dataDbClient.GetDataDBType() {
case constants.LevelDB:
return pst.searchDeviceThingModelHistoryPropertyDataFromLevelDB(req)
case constants.TDengine:
return pst.searchDeviceThingModelHistoryPropertyDataFromTDengine(req)
}
response := make([]interface{}, 0)
return response, 0, nil
}
func (pst *persistApp) searchDeviceThingModelServiceDataFromLevelDB(req dtos.ThingModelServiceDataRequest) ([]dtos.ThingModelServiceDataResponse, int, error) {
var count int
deviceInfo, err := pst.dbClient.DeviceById(req.DeviceId)
if err != nil {
return nil, count, err
}
var productInfo models.Product
productInfo, err = pst.dbClient.ProductById(deviceInfo.ProductId)
if err != nil {
return nil, count, err
}
var response dtos.ThingModelServiceDataResponseArray
if req.Code == "" {
for _, action := range productInfo.Actions {
req.Code = action.Code
var ksv []dtos.SaveServiceIssueData
ksv, count, err = pst.dataDbClient.GetDeviceService(req, deviceInfo, productInfo)
if err != nil {
continue
}
for _, data := range ksv {
response = append(response, dtos.ThingModelServiceDataResponse{
ServiceName: action.Name,
Code: data.Code,
InputData: data.InputParams,
OutputData: data.OutputParams,
ReportTime: data.Time,
})
}
}
} else {
var ksv []dtos.SaveServiceIssueData
ksv, count, err = pst.dataDbClient.GetDeviceService(req, deviceInfo, productInfo)
if err != nil {
return nil, count, err
}
for _, data := range ksv {
name := getServiceName(productInfo.Actions, data.Code)
response = append(response, dtos.ThingModelServiceDataResponse{
ServiceName: name,
Code: data.Code,
InputData: data.InputParams,
OutputData: data.OutputParams,
ReportTime: data.Time,
})
}
}
return response, count, nil
}
func (pst *persistApp) searchDeviceThingModelServiceDataFromTDengine(req dtos.ThingModelServiceDataRequest) ([]dtos.ThingModelServiceDataResponse, int, error) {
var response dtos.ThingModelServiceDataResponseArray
var count int
deviceInfo, err := pst.dbClient.DeviceById(req.DeviceId)
if err != nil {
return response, count, err
}
var productInfo models.Product
productInfo, err = pst.dbClient.ProductById(deviceInfo.ProductId)
if err != nil {
return response, count, err
}
serviceData, count, err := pst.dataDbClient.GetDeviceService(req, deviceInfo, productInfo)
if err != nil {
return response, count, err
}
for _, data := range serviceData {
name := getServiceName(productInfo.Actions, data.Code)
response = append(response, dtos.ThingModelServiceDataResponse{
InputData: data.InputParams,
OutputData: data.OutputParams,
Code: data.Code,
ReportTime: data.Time,
ServiceName: name,
})
}
return response, count, nil
}
func (pst *persistApp) SearchDeviceThingModelServiceData(req dtos.ThingModelServiceDataRequest) ([]dtos.ThingModelServiceDataResponse, int, error) {
var response dtos.ThingModelServiceDataResponseArray
switch pst.dataDbClient.GetDataDBType() {
case constants.LevelDB:
return pst.searchDeviceThingModelServiceDataFromLevelDB(req)
case constants.TDengine:
return pst.searchDeviceThingModelServiceDataFromTDengine(req)
}
return response, 0, nil
}
func (pst *persistApp) searchDeviceThingModelEventDataFromLevelDB(req dtos.ThingModelEventDataRequest) (dtos.ThingModelEventDataResponseArray, int, error) {
var count int
deviceInfo, err := pst.dbClient.DeviceById(req.DeviceId)
if err != nil {
return nil, count, err
}
var response dtos.ThingModelEventDataResponseArray
var productInfo models.Product
productInfo, err = pst.dbClient.ProductById(deviceInfo.ProductId)
if err != nil {
return nil, count, err
}
var ksv []dtos.EventData
ksv, count, err = pst.dataDbClient.GetDeviceEvent(req, deviceInfo, productInfo)
if err != nil {
return nil, count, err
}
for _, data := range ksv {
var eventType string
var name string
eventType, name = getEventTypeAndName(productInfo.Events, data.EventCode)
response = append(response, dtos.ThingModelEventDataResponse{
EventCode: data.EventCode,
EventType: eventType,
OutputData: data.OutputParams,
ReportTime: data.EventTime,
Name: name,
})
}
return response, count, nil
}
func (pst *persistApp) searchDeviceThingModelEventDataFromTDengine(req dtos.ThingModelEventDataRequest) (dtos.ThingModelEventDataResponseArray, int, error) {
var response dtos.ThingModelEventDataResponseArray
var count int
deviceInfo, err := pst.dbClient.DeviceById(req.DeviceId)
if err != nil {
return response, count, err
}
var productInfo models.Product
productInfo, err = pst.dbClient.ProductById(deviceInfo.ProductId)
if err != nil {
return response, count, err
}
eventData, count, err := pst.dataDbClient.GetDeviceEvent(req, deviceInfo, productInfo)
if err != nil {
return response, count, err
}
for _, data := range eventData {
var eventType string
var name string
eventType, name = getEventTypeAndName(productInfo.Events, data.EventCode)
response = append(response, dtos.ThingModelEventDataResponse{
EventCode: data.EventCode,
EventType: eventType,
Name: name,
OutputData: data.OutputParams,
ReportTime: data.EventTime,
})
}
return response, count, nil
}
func (pst *persistApp) SearchDeviceThingModelEventData(req dtos.ThingModelEventDataRequest) ([]dtos.ThingModelEventDataResponse, int, error) {
var response dtos.ThingModelEventDataResponseArray
switch pst.dataDbClient.GetDataDBType() {
case constants.LevelDB:
return pst.searchDeviceThingModelEventDataFromLevelDB(req)
case constants.TDengine:
return pst.searchDeviceThingModelEventDataFromTDengine(req)
}
return response, 0, nil
}
func (pst *persistApp) searchDeviceMsgCountFromLevelDB(startTime, endTime int64) (int, error) {
var (
count int
err error
)
devices, _, err := pst.dbClient.DevicesSearch(0, -1, dtos.DeviceSearchQueryRequest{})
if err != nil {
return 0, err
}
for _, device := range devices {
product, err := pst.dbClient.ProductById(device.ProductId)
if err != nil {
pst.lc.Errorf("search product:", err)
}
for _, property := range product.Properties {
var req dtos.ThingModelPropertyDataRequest
req.DeviceId = device.Id
req.Code = property.Code
req.Range = append(req.Range, startTime, endTime)
propertyCount, err := pst.dataDbClient.GetDevicePropertyCount(req)
if err != nil {
return 0, err
}
count += propertyCount
}
for _, event := range product.Events {
var req dtos.ThingModelEventDataRequest
req.DeviceId = device.Id
req.EventCode = event.Code
req.Range = append(req.Range, startTime, endTime)
eventCount, err := pst.dataDbClient.GetDeviceEventCount(req)
if err != nil {
return 0, err
}
count += eventCount
}
}
return count, err
}
func (pst *persistApp) searchDeviceMsgCountFromTDengine(startTime, endTime int64) (int, error) {
var (
count int
err error
)
devices, _, err := pst.dbClient.DevicesSearch(0, -1, dtos.DeviceSearchQueryRequest{})
if err != nil {
return 0, err
}
for _, device := range devices {
msgCount, err := pst.dataDbClient.GetDeviceMsgCountByGiveTime(device.Id, startTime, endTime)
if err != nil {
return 0, err
}
count += msgCount
}
return 0, nil
}
// SearchDeviceMsgCount 统计设备的消息总数(属性、事件都算在内)
func (pst *persistApp) SearchDeviceMsgCount(startTime, endTime int64) (int, error) {
switch pst.dataDbClient.GetDataDBType() {
case constants.LevelDB:
return pst.searchDeviceMsgCountFromLevelDB(startTime, endTime)
case constants.TDengine:
return pst.searchDeviceMsgCountFromTDengine(startTime, endTime)
}
return 0, nil
}
func getEventTypeAndName(events []models.Events, code string) (string, string) {
for _, event := range events {
if event.Code == code {
return event.EventType, event.Name
}
}
return "", ""
}
func getServiceName(events []models.Actions, code string) string {
for _, event := range events {
if event.Code == code {
return event.Name
}
}
return ""
}

View File

@ -0,0 +1,309 @@
/*******************************************************************************
* Copyright 2017 Dell Inc.
* Copyright (c) 2019 Intel Corporation
*
* 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 productapp
import (
"context"
"errors"
"github.com/winc-link/hummingbird/internal/dtos"
resourceContainer "github.com/winc-link/hummingbird/internal/hummingbird/core/container"
interfaces "github.com/winc-link/hummingbird/internal/hummingbird/core/interface"
"github.com/winc-link/hummingbird/internal/models"
"github.com/winc-link/hummingbird/internal/pkg/constants"
"github.com/winc-link/hummingbird/internal/pkg/container"
"github.com/winc-link/hummingbird/internal/pkg/di"
"github.com/winc-link/hummingbird/internal/pkg/errort"
"github.com/winc-link/hummingbird/internal/pkg/logger"
"github.com/winc-link/hummingbird/internal/pkg/utils"
)
type productApp struct {
//*propertyTyApp
dic *di.Container
dbClient interfaces.DBClient
lc logger.LoggingClient
}
func NewProductApp(ctx context.Context, dic *di.Container) interfaces.ProductItf {
lc := container.LoggingClientFrom(dic.Get)
dbClient := resourceContainer.DBClientFrom(dic.Get)
return &productApp{
dic: dic,
dbClient: dbClient,
lc: lc,
}
}
func (p *productApp) ProductsSearch(ctx context.Context, req dtos.ProductSearchQueryRequest) ([]dtos.ProductSearchQueryResponse, uint32, error) {
offset, limit := req.BaseSearchConditionQuery.GetPage()
resp, total, err := p.dbClient.ProductsSearch(offset, limit, false, req)
if err != nil {
return []dtos.ProductSearchQueryResponse{}, 0, err
}
products := make([]dtos.ProductSearchQueryResponse, len(resp))
for i, p := range resp {
products[i] = dtos.ProductResponseFromModel(p)
}
return products, total, nil
}
func (p *productApp) ProductsModelSearch(ctx context.Context, req dtos.ProductSearchQueryRequest) ([]models.Product, uint32, error) {
offset, limit := req.BaseSearchConditionQuery.GetPage()
return p.dbClient.ProductsSearch(offset, limit, true, req)
}
func (p *productApp) ProductById(ctx context.Context, id string) (dtos.ProductSearchByIdResponse, error) {
resp, err := p.dbClient.ProductById(id)
if err != nil {
return dtos.ProductSearchByIdResponse{}, err
}
return dtos.ProductSearchByIdFromModel(resp), nil
}
func (p *productApp) OpenApiProductById(ctx context.Context, id string) (dtos.ProductSearchByIdOpenApiResponse, error) {
resp, err := p.dbClient.ProductById(id)
if err != nil {
return dtos.ProductSearchByIdOpenApiResponse{}, err
}
return dtos.ProductSearchByIdOpenApiFromModel(resp), nil
}
func (p *productApp) OpenApiProductSearch(ctx context.Context, req dtos.ProductSearchQueryRequest) ([]dtos.ProductSearchOpenApiResponse, uint32, error) {
offset, limit := req.BaseSearchConditionQuery.GetPage()
resp, total, err := p.dbClient.ProductsSearch(offset, limit, false, req)
if err != nil {
return []dtos.ProductSearchOpenApiResponse{}, 0, err
}
products := make([]dtos.ProductSearchOpenApiResponse, len(resp))
for i, product := range resp {
products[i] = dtos.ProductSearchOpenApiFromModel(product)
}
return products, total, nil
}
func (p *productApp) ProductModelById(ctx context.Context, id string) (models.Product, error) {
resp, err := p.dbClient.ProductById(id)
if err != nil {
return models.Product{}, err
}
return resp, nil
}
func (p *productApp) ProductDelete(ctx context.Context, id string) error {
productInfo, err := p.dbClient.ProductById(id)
if err != nil {
return err
}
_, total, err := p.dbClient.DevicesSearch(0, -1, dtos.DeviceSearchQueryRequest{ProductId: productInfo.Id})
if err != nil {
return err
}
if total > 0 {
return errort.NewCommonEdgeX(errort.ProductMustDeleteDevice, "该产品已绑定子设备,请优先删除子设备", err)
}
alertApp := resourceContainer.AlertRuleAppNameFrom(p.dic.Get)
err = alertApp.CheckRuleByProductId(ctx, id)
if err != nil {
return err
}
if err = p.dbClient.AssociationsDeleteProductObject(productInfo); err != nil {
return err
}
_ = resourceContainer.DataDBClientFrom(p.dic.Get).DropStable(ctx, productInfo.Id)
go func() {
p.DeleteProductCallBack(models.Product{
Id: productInfo.Id,
Platform: productInfo.Platform,
})
}()
return nil
}
func (p *productApp) AddProduct(ctx context.Context, req dtos.ProductAddRequest) (productId string, err error) {
// 标准品类
var properties []models.Properties
var events []models.Events
var actions []models.Actions
if req.CategoryTemplateId != "1" {
categoryTempInfo, err := p.dbClient.CategoryTemplateById(req.CategoryTemplateId)
if err != nil {
return "", err
}
thingModelTemplateInfo, err := p.dbClient.ThingModelTemplateByCategoryKey(categoryTempInfo.CategoryKey)
if err != nil {
return "", err
}
if thingModelTemplateInfo.ThingModelJSON != "" {
properties, events, actions = dtos.GetModelPropertyEventActionByThingModelTemplate(thingModelTemplateInfo.ThingModelJSON)
}
}
var insertProduct models.Product
insertProduct.Id = utils.RandomNum()
insertProduct.Name = req.Name
insertProduct.CloudProductId = utils.GenerateDeviceSecret(15)
insertProduct.Platform = constants.IotPlatform_LocalIot
insertProduct.Protocol = req.Protocol
insertProduct.NodeType = constants.ProductNodeType(req.NodeType)
insertProduct.NetType = constants.ProductNetType(req.NetType)
insertProduct.DataFormat = req.DataFormat
insertProduct.Factory = req.Factory
insertProduct.Description = req.Description
insertProduct.Key = req.Key
insertProduct.Status = constants.ProductUnRelease
insertProduct.Properties = properties
insertProduct.Events = events
insertProduct.Actions = actions
ps, err := p.dbClient.AddProduct(insertProduct)
if err != nil {
return "", err
}
go func() {
p.CreateProductCallBack(insertProduct)
}()
return ps.Id, nil
}
func (p *productApp) ProductRelease(ctx context.Context, productId string) error {
var err error
var productInfo models.Product
productInfo, err = p.dbClient.ProductById(productId)
if err != nil {
return err
}
if productInfo.Status == constants.ProductRelease {
return errors.New("")
}
err = resourceContainer.DataDBClientFrom(p.dic.Get).CreateStable(ctx, productInfo)
if err != nil {
return err
}
productInfo.Status = constants.ProductRelease
return p.dbClient.UpdateProduct(productInfo)
}
func (p *productApp) ProductUnRelease(ctx context.Context, productId string) error {
var err error
var productInfo models.Product
productInfo, err = p.dbClient.ProductById(productId)
if err != nil {
return err
}
if productInfo.Status == constants.ProductUnRelease {
return errors.New("")
}
productInfo.Status = constants.ProductUnRelease
return p.dbClient.UpdateProduct(productInfo)
}
func (p *productApp) OpenApiAddProduct(ctx context.Context, req dtos.OpenApiAddProductRequest) (productId string, err error) {
//var properties []models.Properties
//var events []models.Events
//var actions []models.Actions
//properties, events, actions = dtos.OpenApiGetModelPropertyEventActionByThingModelTemplate(req)
//if len(properties) == 0 {
// properties = make([]models.Properties, 0)
//}
//
//if len(events) == 0 {
// events = make([]models.Events, 0)
//}
//
//if len(actions) == 0 {
// actions = make([]models.Actions, 0)
//}
var insertProduct models.Product
insertProduct.Name = req.Name
insertProduct.CloudProductId = utils.GenerateDeviceSecret(15)
insertProduct.Platform = constants.IotPlatform_LocalIot
insertProduct.Protocol = req.Protocol
insertProduct.NodeType = constants.ProductNodeType(req.NodeType)
insertProduct.NetType = constants.ProductNetType(req.NetType)
insertProduct.DataFormat = req.DataFormat
insertProduct.Factory = req.Factory
insertProduct.Description = req.Description
//insertProduct.Properties = properties
//insertProduct.Events = events
//insertProduct.Actions = actions
ps, err := p.dbClient.AddProduct(insertProduct)
if err != nil {
return "", err
}
go func() {
p.CreateProductCallBack(insertProduct)
}()
return ps.Id, nil
}
func (p *productApp) OpenApiUpdateProduct(ctx context.Context, req dtos.OpenApiUpdateProductRequest) error {
product, err := p.dbClient.ProductById(req.Id)
if err != nil {
return err
}
if req.Name != nil {
product.Name = *req.Name
}
product.Platform = constants.IotPlatform_LocalIot
if req.Protocol != nil {
product.Protocol = *req.Protocol
}
if req.NetType != nil {
product.NodeType = constants.ProductNodeType(*req.NodeType)
}
if req.NetType != nil {
product.NetType = constants.ProductNetType(*req.NetType)
}
if req.DataFormat != nil {
product.DataFormat = *req.DataFormat
}
if req.Factory != nil {
product.Factory = *req.Factory
}
if req.Description != nil {
product.Description = *req.Description
}
err = p.dbClient.UpdateProduct(product)
if err != nil {
return err
}
go func() {
p.UpdateProductCallBack(product)
}()
return nil
}

View File

@ -0,0 +1,118 @@
/*******************************************************************************
* Copyright 2017 Dell Inc.
* Copyright (c) 2019 Intel Corporation
*
* 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 productapp
import (
"context"
"github.com/winc-link/edge-driver-proto/productcallback"
"github.com/winc-link/hummingbird/internal/dtos"
"github.com/winc-link/hummingbird/internal/models"
"github.com/winc-link/hummingbird/internal/pkg/constants"
"github.com/winc-link/hummingbird/internal/pkg/di"
"time"
//"github.com/winc-link/hummingbird/internal/hummingbird/core/application/driverapp"
"github.com/winc-link/hummingbird/internal/hummingbird/core/container"
"github.com/winc-link/hummingbird/internal/tools/rpcclient"
)
func (p *productApp) CreateProductCallBack(productInfo models.Product) {
deviceServices, total, err := p.dbClient.DeviceServicesSearch(0, -1, dtos.DeviceServiceSearchQueryRequest{})
if err != nil {
return
}
if total == 0 {
return
}
for _, service := range deviceServices {
if productInfo.Platform == service.Platform {
driverService := container.DriverServiceAppFrom(di.GContainer.Get)
status := driverService.GetState(service.Id)
if status == constants.RunStatusStarted {
client, errX := rpcclient.NewDriverRpcClient(service.BaseAddress, false, "", service.Id, p.lc)
if errX != nil {
return
}
defer client.Close()
var rpcRequest productcallback.CreateProductCallbackRequest
rpcRequest.Data = productInfo.TransformToDriverProduct()
rpcRequest.HappenTime = uint64(time.Now().Unix())
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
defer cancel()
_, _ = client.ProductCallBackServiceClient.CreateProductCallback(ctx, &rpcRequest)
}
}
}
}
func (p *productApp) UpdateProductCallBack(productInfo models.Product) {
//p.lc.Infof("UpdateProductCallBack Platform :%s name :%s id :%s", productInfo.Platform, productInfo.Name, productInfo.Id)
deviceServices, total, err := p.dbClient.DeviceServicesSearch(0, -1, dtos.DeviceServiceSearchQueryRequest{})
if err != nil {
return
}
if total == 0 {
return
}
for _, service := range deviceServices {
if productInfo.Platform == service.Platform {
driverService := container.DriverServiceAppFrom(di.GContainer.Get)
status := driverService.GetState(service.Id)
if status == constants.RunStatusStarted {
client, errX := rpcclient.NewDriverRpcClient(service.BaseAddress, false, "", service.Id, p.lc)
if errX != nil {
return
}
defer client.Close()
var rpcRequest productcallback.UpdateProductCallbackRequest
rpcRequest.Data = productInfo.TransformToDriverProduct()
rpcRequest.HappenTime = uint64(time.Now().Second())
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
defer cancel()
_, _ = client.ProductCallBackServiceClient.UpdateProductCallback(ctx, &rpcRequest)
}
}
}
}
func (p *productApp) DeleteProductCallBack(productInfo models.Product) {
//p.lc.Infof("DeleteProductCallBack Platform :%s name :%s id :%s", productInfo.Platform, productInfo.Name, productInfo.Id)
deviceServices, total, err := p.dbClient.DeviceServicesSearch(0, -1, dtos.DeviceServiceSearchQueryRequest{})
if total == 0 {
return
}
if err != nil {
return
}
for _, service := range deviceServices {
if productInfo.Platform == service.Platform {
driverService := container.DriverServiceAppFrom(di.GContainer.Get)
status := driverService.GetState(service.Id)
if status == constants.RunStatusStarted {
client, errX := rpcclient.NewDriverRpcClient(service.BaseAddress, false, "", service.Id, p.lc)
if errX != nil {
return
}
defer client.Close()
var rpcRequest productcallback.DeleteProductCallbackRequest
rpcRequest.ProductId = productInfo.Id
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
defer cancel()
_, _ = client.ProductCallBackServiceClient.DeleteProductCallback(ctx, &rpcRequest)
}
}
}
}

View File

@ -0,0 +1,113 @@
/*******************************************************************************
* Copyright 2017.
*
* 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 quicknavigationapp
import (
"context"
"encoding/json"
"github.com/winc-link/hummingbird/internal/dtos"
resourceContainer "github.com/winc-link/hummingbird/internal/hummingbird/core/container"
interfaces "github.com/winc-link/hummingbird/internal/hummingbird/core/interface"
"github.com/winc-link/hummingbird/internal/models"
"github.com/winc-link/hummingbird/internal/pkg/container"
"github.com/winc-link/hummingbird/internal/pkg/di"
"github.com/winc-link/hummingbird/internal/pkg/logger"
"github.com/winc-link/hummingbird/internal/pkg/utils"
)
type quickNavigationApp struct {
dic *di.Container
dbClient interfaces.DBClient
lc logger.LoggingClient
}
func (m quickNavigationApp) SyncQuickNavigation(ctx context.Context, versionName string) (int64, error) {
filePath := versionName + "/quick_navigation.json"
cosApp := resourceContainer.CosAppNameFrom(m.dic.Get)
bs, err := cosApp.Get(filePath)
if err != nil {
m.lc.Errorf(err.Error())
return 0, err
}
var cosQuickNavigationTemplateResponse []dtos.CosQuickNavigationTemplateResponse
err = json.Unmarshal(bs, &cosQuickNavigationTemplateResponse)
if err != nil {
m.lc.Errorf(err.Error())
return 0, err
}
baseQuery := dtos.BaseSearchConditionQuery{
IsAll: true,
}
dbreq := dtos.QuickNavigationSearchQueryRequest{BaseSearchConditionQuery: baseQuery}
quickNavigations, _, err := m.dbClient.QuickNavigationSearch(0, -1, dbreq)
if err != nil {
return 0, err
}
var cosQuickNavigationName []string
upsertQuickNavigationTemplate := make([]models.QuickNavigation, 0)
for _, cosQuickNavigationTemplate := range cosQuickNavigationTemplateResponse {
cosQuickNavigationName = append(cosQuickNavigationName, cosQuickNavigationTemplate.Name)
var find bool
for _, localQuickNavigation := range quickNavigations {
if cosQuickNavigationTemplate.Name == localQuickNavigation.Name {
upsertQuickNavigationTemplate = append(upsertQuickNavigationTemplate, models.QuickNavigation{
Id: localQuickNavigation.Id,
Name: cosQuickNavigationTemplate.Name,
Icon: cosQuickNavigationTemplate.Icon,
Sort: cosQuickNavigationTemplate.Sort,
JumpLink: cosQuickNavigationTemplate.JumpLink,
})
find = true
break
}
}
if !find {
upsertQuickNavigationTemplate = append(upsertQuickNavigationTemplate, models.QuickNavigation{
Id: utils.RandomNum(),
Name: cosQuickNavigationTemplate.Name,
Icon: cosQuickNavigationTemplate.Icon,
Sort: cosQuickNavigationTemplate.Sort,
JumpLink: cosQuickNavigationTemplate.JumpLink,
})
}
}
rows, err := m.dbClient.BatchUpsertQuickNavigationTemplate(upsertQuickNavigationTemplate)
if err != nil {
return 0, err
}
for _, navigation := range quickNavigations {
if !utils.InStringSlice(navigation.Name, cosQuickNavigationName) {
err = m.dbClient.DeleteQuickNavigation(navigation.Id)
if err != nil {
return 0, err
}
}
}
return rows, nil
}
func NewQuickNavigationApp(ctx context.Context, dic *di.Container) interfaces.QuickNavigation {
lc := container.LoggingClientFrom(dic.Get)
dbClient := resourceContainer.DBClientFrom(dic.Get)
return &quickNavigationApp{
dic: dic,
dbClient: dbClient,
lc: lc,
}
}

View File

@ -0,0 +1,59 @@
/*******************************************************************************
* Copyright 2017.
*
* 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 ruleengine
import (
"context"
"github.com/winc-link/hummingbird/internal/dtos"
resourceContainer "github.com/winc-link/hummingbird/internal/hummingbird/core/container"
"github.com/winc-link/hummingbird/internal/pkg/constants"
"time"
)
func (p ruleEngineApp) monitor() {
tickTime := time.Second * 5
timeTickerChan := time.Tick(tickTime)
for {
select {
case <-timeTickerChan:
p.checkRuleStatus()
}
}
}
func (p ruleEngineApp) checkRuleStatus() {
ruleEngines, _, err := p.dbClient.RuleEngineSearch(0, -1, dtos.RuleEngineSearchQueryRequest{})
if err != nil {
p.lc.Errorf("get engines err:", err)
}
ekuiperApp := resourceContainer.EkuiperAppFrom(p.dic.Get)
for _, ruleEngine := range ruleEngines {
resp, err := ekuiperApp.GetRuleStats(context.Background(), ruleEngine.Id)
if err != nil {
p.lc.Errorf("error:", err)
continue
}
status, ok := resp["status"]
if ok {
if status != string(ruleEngine.Status) {
if status == string(constants.RuleEngineStop) {
p.dbClient.RuleEngineStop(ruleEngine.Id)
} else if status == string(constants.RuleEngineStart) {
p.dbClient.RuleEngineStart(ruleEngine.Id)
}
}
}
}
}

View File

@ -0,0 +1,245 @@
/*******************************************************************************
* Copyright 2017.
*
* 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 ruleengine
import (
"context"
"fmt"
"github.com/winc-link/hummingbird/internal/dtos"
resourceContainer "github.com/winc-link/hummingbird/internal/hummingbird/core/container"
interfaces "github.com/winc-link/hummingbird/internal/hummingbird/core/interface"
"github.com/winc-link/hummingbird/internal/models"
"github.com/winc-link/hummingbird/internal/pkg/constants"
"github.com/winc-link/hummingbird/internal/pkg/container"
"github.com/winc-link/hummingbird/internal/pkg/di"
"github.com/winc-link/hummingbird/internal/pkg/errort"
"github.com/winc-link/hummingbird/internal/pkg/logger"
"github.com/winc-link/hummingbird/internal/pkg/utils"
)
type ruleEngineApp struct {
dic *di.Container
dbClient interfaces.DBClient
lc logger.LoggingClient
}
func (p ruleEngineApp) AddRuleEngine(ctx context.Context, req dtos.RuleEngineRequest) (string, error) {
dataResource, err := p.dbClient.DataResourceById(req.DataResourceId)
if err != nil {
return "", err
}
randomId := utils.RandomNum()
ekuiperApp := resourceContainer.EkuiperAppFrom(p.dic.Get)
sql := req.BuildEkuiperSql()
var actions []dtos.Actions
switch dataResource.Type {
case constants.HttpResource:
actions = append(actions, dtos.Actions{
Rest: dataResource.Option,
})
case constants.MQTTResource:
actions = append(actions, dtos.Actions{
MQTT: dataResource.Option,
})
case constants.KafkaResource:
actions = append(actions, dtos.Actions{
Kafka: dataResource.Option,
})
case constants.InfluxDBResource:
actions = append(actions, dtos.Actions{
Influx: dataResource.Option,
})
case constants.TDengineResource:
actions = append(actions, dtos.Actions{
Tdengine: dataResource.Option,
})
default:
return "", errort.NewCommonErr(errort.DefaultReqParamsError, fmt.Errorf("rule engine action not much"))
}
if err = ekuiperApp.CreateRule(ctx, actions, randomId, sql); err != nil {
return "", err
}
var insertRuleEngine models.RuleEngine
insertRuleEngine.Name = req.Name
insertRuleEngine.Id = randomId
insertRuleEngine.Description = req.Description
insertRuleEngine.Filter = models.Filter(req.Filter)
insertRuleEngine.DataResourceId = req.DataResourceId
insertRuleEngine.Status = constants.RuleEngineStop
id, err := p.dbClient.AddRuleEngine(insertRuleEngine)
if err != nil {
return "", err
}
return id, nil
}
func (p ruleEngineApp) UpdateRuleEngine(ctx context.Context, req dtos.RuleEngineUpdateRequest) error {
dataResource, err := p.dbClient.DataResourceById(*req.DataResourceId)
if err != nil {
return err
}
ruleEngine, err := p.dbClient.RuleEngineById(req.Id)
if err != nil {
return err
}
sql := req.BuildEkuiperSql()
var actions []dtos.Actions
switch dataResource.Type {
case constants.HttpResource:
actions = append(actions, dtos.Actions{
Rest: dataResource.Option,
})
case constants.MQTTResource:
actions = append(actions, dtos.Actions{
MQTT: dataResource.Option,
})
case constants.KafkaResource:
actions = append(actions, dtos.Actions{
Kafka: dataResource.Option,
})
case constants.InfluxDBResource:
actions = append(actions, dtos.Actions{
Influx: dataResource.Option,
})
case constants.TDengineResource:
actions = append(actions, dtos.Actions{
Tdengine: dataResource.Option,
})
default:
return errort.NewCommonErr(errort.DefaultReqParamsError, fmt.Errorf("rule engine action not much"))
}
ekuiperApp := resourceContainer.EkuiperAppFrom(p.dic.Get)
if err = ekuiperApp.UpdateRule(ctx, actions, req.Id, sql); err != nil {
return err
}
dtos.ReplaceRuleEngineModelFields(&ruleEngine, req)
err = p.dbClient.UpdateRuleEngine(ruleEngine)
if err != nil {
return err
}
return nil
}
func (p ruleEngineApp) UpdateRuleEngineField(ctx context.Context, req dtos.RuleEngineFieldUpdateRequest) error {
//TODO implement me
panic("implement me")
}
func (p ruleEngineApp) RuleEngineById(ctx context.Context, id string) (dtos.RuleEngineResponse, error) {
ruleEngine, err := p.dbClient.RuleEngineById(id)
var ruleEngineResponse dtos.RuleEngineResponse
if err != nil {
return ruleEngineResponse, err
}
ruleEngineResponse.Id = ruleEngine.Id
ruleEngineResponse.Name = ruleEngine.Name
ruleEngineResponse.Description = ruleEngine.Description
ruleEngineResponse.Created = ruleEngine.Created
ruleEngineResponse.Filter = dtos.Filter(ruleEngine.Filter)
ruleEngineResponse.DataResourceId = ruleEngine.DataResourceId
ruleEngineResponse.DataResource = dtos.DataResourceInfo{
Name: ruleEngine.DataResource.Name,
Type: string(ruleEngine.DataResource.Type),
Option: ruleEngine.DataResource.Option,
}
return ruleEngineResponse, nil
}
func (p ruleEngineApp) RuleEngineSearch(ctx context.Context, req dtos.RuleEngineSearchQueryRequest) ([]dtos.RuleEngineSearchQueryResponse, uint32, error) {
offset, limit := req.BaseSearchConditionQuery.GetPage()
resp, total, err := p.dbClient.RuleEngineSearch(offset, limit, req)
if err != nil {
return []dtos.RuleEngineSearchQueryResponse{}, 0, err
}
ruleEngines := make([]dtos.RuleEngineSearchQueryResponse, len(resp))
for i, p := range resp {
ruleEngines[i] = dtos.RuleEngineSearchQueryResponseFromModel(p)
}
return ruleEngines, total, nil
}
func (p ruleEngineApp) RuleEngineDelete(ctx context.Context, id string) error {
_, err := p.dbClient.RuleEngineById(id)
if err != nil {
return err
}
ekuiperApp := resourceContainer.EkuiperAppFrom(p.dic.Get)
err = ekuiperApp.DeleteRule(ctx, id)
if err != nil {
return err
}
return p.dbClient.DeleteRuleEngineById(id)
}
func (p ruleEngineApp) RuleEngineStop(ctx context.Context, id string) error {
_, err := p.dbClient.RuleEngineById(id)
if err != nil {
return err
}
//if alertRule.EkuiperRule() {
ekuiperApp := resourceContainer.EkuiperAppFrom(p.dic.Get)
err = ekuiperApp.StopRule(ctx, id)
if err != nil {
return err
}
return p.dbClient.RuleEngineStop(id)
}
func (p ruleEngineApp) RuleEngineStart(ctx context.Context, id string) error {
ruleEngine, err := p.dbClient.RuleEngineById(id)
if err != nil {
return err
}
dataResource, err := p.dbClient.DataResourceById(ruleEngine.DataResourceId)
if err != nil {
return err
}
if dataResource.Health != true {
return errort.NewCommonErr(errort.InvalidSource, fmt.Errorf("invalid resource configuration, please check the resource configuration resource id (%s)", dataResource.Id))
}
ekuiperApp := resourceContainer.EkuiperAppFrom(p.dic.Get)
err = ekuiperApp.StartRule(ctx, id)
if err != nil {
return err
}
//}
return p.dbClient.RuleEngineStart(id)
}
func (p ruleEngineApp) RuleEngineStatus(ctx context.Context, id string) (map[string]interface{}, error) {
response := make(map[string]interface{}, 0)
_, err := p.dbClient.RuleEngineById(id)
if err != nil {
return response, err
}
ekuiperApp := resourceContainer.EkuiperAppFrom(p.dic.Get)
return ekuiperApp.GetRuleStats(ctx, id)
}
func NewRuleEngineApp(ctx context.Context, dic *di.Container) interfaces.RuleEngineApp {
lc := container.LoggingClientFrom(dic.Get)
dbClient := resourceContainer.DBClientFrom(dic.Get)
app := &ruleEngineApp{
dic: dic,
dbClient: dbClient,
lc: lc,
}
go app.monitor()
return app
}

View File

@ -0,0 +1,65 @@
/*******************************************************************************
* Copyright 2017.
*
* 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 scene
import (
"context"
"github.com/winc-link/hummingbird/internal/dtos"
resourceContainer "github.com/winc-link/hummingbird/internal/hummingbird/core/container"
"github.com/winc-link/hummingbird/internal/pkg/constants"
"time"
)
func (p sceneApp) monitor() {
tickTime := time.Second * 9
timeTickerChan := time.Tick(tickTime)
for {
select {
case <-timeTickerChan:
p.checkSceneRuleStatus()
}
}
}
func (p sceneApp) checkSceneRuleStatus() {
scenes, _, err := p.dbClient.SceneSearch(0, -1, dtos.SceneSearchQueryRequest{})
if err != nil {
p.lc.Errorf("get engines err:", err)
}
ekuiperApp := resourceContainer.EkuiperAppFrom(p.dic.Get)
for _, scene := range scenes {
if len(scene.Conditions) != 1 {
continue
}
if scene.Conditions[0].ConditionType != "notify" {
continue
}
resp, err := ekuiperApp.GetRuleStats(context.Background(), scene.Id)
if err != nil {
p.lc.Errorf("error:", err)
continue
}
status, ok := resp["status"]
if ok {
if status != string(scene.Status) {
if status == string(constants.SceneStart) {
p.dbClient.SceneStart(scene.Id)
} else if status == string(constants.SceneStop) {
p.dbClient.SceneStop(scene.Id)
}
}
}
}
}

View File

@ -0,0 +1,628 @@
/*******************************************************************************
* Copyright 2017.
*
* 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 scene
import (
"context"
"errors"
"fmt"
"github.com/winc-link/hummingbird/internal/dtos"
resourceContainer "github.com/winc-link/hummingbird/internal/hummingbird/core/container"
interfaces "github.com/winc-link/hummingbird/internal/hummingbird/core/interface"
"github.com/winc-link/hummingbird/internal/models"
"github.com/winc-link/hummingbird/internal/pkg/constants"
"github.com/winc-link/hummingbird/internal/pkg/container"
"github.com/winc-link/hummingbird/internal/pkg/di"
"github.com/winc-link/hummingbird/internal/pkg/errort"
"github.com/winc-link/hummingbird/internal/pkg/logger"
"strconv"
"strings"
)
type sceneApp struct {
dic *di.Container
dbClient interfaces.DBClient
lc logger.LoggingClient
}
func NewSceneApp(ctx context.Context, dic *di.Container) interfaces.SceneApp {
lc := container.LoggingClientFrom(dic.Get)
dbClient := resourceContainer.DBClientFrom(dic.Get)
app := &sceneApp{
dic: dic,
dbClient: dbClient,
lc: lc,
}
go app.monitor()
return app
}
func (p sceneApp) AddScene(ctx context.Context, req dtos.SceneAddRequest) (string, error) {
var scene models.Scene
scene.Name = req.Name
scene.Description = req.Description
resp, err := p.dbClient.AddScene(scene)
if err != nil {
return "", err
}
return resp.Id, nil
}
func (p sceneApp) UpdateScene(ctx context.Context, req dtos.SceneUpdateRequest) error {
if req.Id == "" {
return errort.NewCommonEdgeX(errort.DefaultReqParamsError, "update req id is required", nil)
}
scene, edgeXErr := p.dbClient.SceneById(req.Id)
if edgeXErr != nil {
return edgeXErr
}
if len(req.Conditions) != 1 {
return errort.NewCommonEdgeX(errort.DefaultReqParamsError, "conditions len not eq 1", nil)
}
switch req.Conditions[0].ConditionType {
case "timer":
if scene.Status == constants.SceneStart {
return errort.NewCommonEdgeX(errort.SceneTimerIsStartingNotAllowUpdate, "Please stop this scheduled"+
" tasks before editing it.", nil)
}
case "notify":
if req.Conditions[0].Option == nil {
return errort.NewCommonEdgeX(errort.DefaultReqParamsError, "condition option is null", nil)
}
actions, sql, err := p.buildEkuiperSqlAndAction(req)
if err != nil {
return err
}
p.lc.Infof("sql:", sql)
ekuiperApp := resourceContainer.EkuiperAppFrom(p.dic.Get)
exist, err := ekuiperApp.RuleExist(ctx, scene.Id)
if err != nil {
return err
}
if exist {
err = ekuiperApp.UpdateRule(ctx, actions, scene.Id, sql)
if err != nil {
return err
}
} else {
err = ekuiperApp.CreateRule(ctx, actions, scene.Id, sql)
if err != nil {
return err
}
}
}
dtos.ReplaceSceneModelFields(&scene, req)
edgeXErr = p.dbClient.UpdateScene(scene)
if edgeXErr != nil {
return edgeXErr
}
return nil
}
func (p sceneApp) SceneById(ctx context.Context, sceneId string) (models.Scene, error) {
return p.dbClient.SceneById(sceneId)
}
func (p sceneApp) SceneStartById(ctx context.Context, sceneId string) error {
scene, err := p.dbClient.SceneById(sceneId)
if err != nil {
return err
}
if len(scene.Conditions) == 0 {
return errort.NewCommonErr(errort.SceneRuleParamsError, fmt.Errorf("scene id(%s) conditionType param errror", scene.Id))
}
switch scene.Conditions[0].ConditionType {
case "timer":
tmpJob, errJob := scene.ToRuntimeJob()
if errJob != nil {
return errort.NewCommonEdgeX(errort.DefaultSystemError, errJob.Error(), errJob)
}
p.lc.Infof("tmpJob: %v", tmpJob)
conJobApp := resourceContainer.ConJobAppNameFrom(p.dic.Get)
err = conJobApp.AddJobToRunQueue(tmpJob)
if err != nil {
return err
}
case "notify":
if scene.Conditions[0].Option == nil {
return errort.NewCommonEdgeX(errort.DefaultReqParamsError, "condition option is null", nil)
}
if err = p.checkAlertRuleParam(ctx, scene, "start"); err != nil {
return err
}
ekuiperApp := resourceContainer.EkuiperAppFrom(p.dic.Get)
err = ekuiperApp.StartRule(ctx, sceneId)
if err != nil {
return err
}
default:
return errort.NewCommonEdgeX(errort.DefaultReqParamsError, "condition Type value not much", nil)
}
return p.dbClient.SceneStart(sceneId)
}
func (p sceneApp) SceneStopById(ctx context.Context, sceneId string) error {
scene, err := p.dbClient.SceneById(sceneId)
if err != nil {
return err
}
switch scene.Conditions[0].ConditionType {
case "timer":
conJobApp := resourceContainer.ConJobAppNameFrom(p.dic.Get)
conJobApp.DeleteJob(scene.Id)
case "notify":
ekuiperApp := resourceContainer.EkuiperAppFrom(p.dic.Get)
err = ekuiperApp.StopRule(ctx, sceneId)
if err != nil {
return err
}
default:
return errort.NewCommonEdgeX(errort.DefaultReqParamsError, "condition Type value not much", nil)
}
return p.dbClient.SceneStop(sceneId)
}
func (p sceneApp) DelSceneById(ctx context.Context, sceneId string) error {
scene, err := p.dbClient.SceneById(sceneId)
if err != nil {
return err
}
if len(scene.Conditions) == 0 {
return p.dbClient.DeleteSceneById(sceneId)
//return errort.NewCommonEdgeX(errort.DefaultSystemError, "conditions param error", nil)
}
switch scene.Conditions[0].ConditionType {
case "timer":
conJobApp := resourceContainer.ConJobAppNameFrom(p.dic.Get)
conJobApp.DeleteJob(scene.Id)
case "notify":
ekuiperApp := resourceContainer.EkuiperAppFrom(p.dic.Get)
err = ekuiperApp.DeleteRule(ctx, sceneId)
if err != nil {
return err
}
default:
return errort.NewCommonEdgeX(errort.DefaultReqParamsError, "condition Type value not much", nil)
}
return p.dbClient.DeleteSceneById(sceneId)
}
func (p sceneApp) SceneSearch(ctx context.Context, req dtos.SceneSearchQueryRequest) ([]models.Scene, uint32, error) {
offset, limit := req.BaseSearchConditionQuery.GetPage()
resp, total, err := p.dbClient.SceneSearch(offset, limit, req)
if err != nil {
return []models.Scene{}, 0, err
}
return resp, total, nil
}
func (p sceneApp) SceneLogSearch(ctx context.Context, req dtos.SceneLogSearchQueryRequest) ([]models.SceneLog, uint32, error) {
offset, limit := req.BaseSearchConditionQuery.GetPage()
resp, total, err := p.dbClient.SceneLogSearch(offset, limit, req)
if err != nil {
return []models.SceneLog{}, 0, err
}
return resp, total, nil
}
func (p sceneApp) buildEkuiperSqlAndAction(req dtos.SceneUpdateRequest) (actions []dtos.Actions, sql string, err error) {
configapp := resourceContainer.ConfigurationFrom(p.dic.Get)
actions = dtos.GetRuleSceneEkuiperActions(configapp.Service.Url())
option := req.Conditions[0].Option
deviceId := option["device_id"]
deviceName := option["device_name"]
productId := option["product_id"]
productName := option["product_name"]
trigger := option["trigger"]
code := option["code"]
if deviceId == "" || deviceName == "" || productId == "" || productName == "" || code == "" || trigger == "" {
err = errort.NewCommonEdgeX(errort.DefaultReqParamsError, "required parameter missing", nil)
return
}
device, err := p.dbClient.DeviceById(deviceId)
if err != nil {
return
}
product, err := p.dbClient.ProductById(productId)
if err != nil {
return
}
if device.ProductId != product.Id {
err = errort.NewCommonEdgeX(errort.DefaultSystemError, "", nil)
return
}
switch trigger {
case string(constants.DeviceDataTrigger):
var codeFind bool
for _, property := range product.Properties {
if code == property.Code {
codeFind = true
if !property.TypeSpec.Type.AllowSendInEkuiper() {
err = errort.NewCommonEdgeX(errort.DefaultReqParamsError, "required parameter missing", nil)
}
var s int
switch option["value_cycle"] {
case "1分钟周期":
s = 60
case "5分钟周期":
s = 60 * 5
case "15分钟周期":
s = 60 * 15
case "30分钟周期":
s = 60 * 30
case "60分钟周期":
s = 60 * 60
default:
}
switch property.TypeSpec.Type {
case constants.SpecsTypeInt, constants.SpecsTypeFloat:
valueType := option["value_type"]
if valueType == "" {
err = errort.NewCommonEdgeX(errort.DefaultReqParamsError, "required value_type parameter missing", nil)
return
}
switch valueType {
case constants.Original: //原始值
decideCondition := option["decide_condition"]
if decideCondition == "" {
err = errort.NewCommonEdgeX(errort.DefaultReqParamsError, "required decide_condition parameter missing", nil)
return
}
originalTemp := `SELECT rule_id(),json_path_query(data, "$.%s.time") as report_time ,deviceId FROM mqtt_stream where deviceId = "%s" and messageType = "PROPERTY_REPORT" and json_path_exists(data, "$.%s") = true and json_path_query(data, "$.%s.value") %s`
sql = fmt.Sprintf(originalTemp, code, deviceId, code, code, decideCondition)
return
case constants.Max:
sqlTemp := `SELECT window_start(),window_end(),rule_id(),deviceId,max(json_path_query(data, "$.%s.value")) as max_%s FROM mqtt_stream where deviceId = "%s" and messageType = "PROPERTY_REPORT" and json_path_exists(data, "$.%s") = true GROUP BY %s HAVING max_%s %s`
valueCycle := s
if valueCycle == 0 {
err = errort.NewCommonEdgeX(errort.DefaultReqParamsError, "required value_cycle parameter missing", nil)
return
}
decideCondition := option["decide_condition"]
if decideCondition == "" {
err = errort.NewCommonEdgeX(errort.DefaultReqParamsError, "required decide_condition parameter missing", nil)
return
}
sql = fmt.Sprintf(sqlTemp, code, code, device.Id, code, fmt.Sprintf("TUMBLINGWINDOW(ss, %d)", valueCycle), code, decideCondition)
return
case constants.Min:
sqlTemp := `SELECT window_start(),window_end(),rule_id(),deviceId,min(json_path_query(data, "$.%s.value")) as min_%s FROM mqtt_stream where deviceId = "%s" and messageType = "PROPERTY_REPORT" and json_path_exists(data, "$.%s") = true GROUP BY %s HAVING min_%s %s`
valueCycle := s
if valueCycle == 0 {
err = errort.NewCommonEdgeX(errort.DefaultReqParamsError, "required value_cycle parameter missing", nil)
return
}
decideCondition := option["decide_condition"]
if decideCondition == "" {
err = errort.NewCommonEdgeX(errort.DefaultReqParamsError, "required decide_condition parameter missing", nil)
return
}
sql = fmt.Sprintf(sqlTemp, code, code, device.Id, code, fmt.Sprintf("TUMBLINGWINDOW(ss, %d)", valueCycle), code, decideCondition)
return
case constants.Sum:
sqlTemp := `SELECT window_start(),window_end(),rule_id(),deviceId,sum(json_path_query(data, "$.%s.value")) as sum_%s FROM mqtt_stream where deviceId = "%s" and messageType = "PROPERTY_REPORT" and json_path_exists(data, "$.%s") = true GROUP BY %s HAVING sum_%s %s`
valueCycle := s
if valueCycle == 0 {
err = errort.NewCommonEdgeX(errort.DefaultReqParamsError, "required value_cycle parameter missing", nil)
return
}
decideCondition := option["decide_condition"]
if decideCondition == "" {
err = errort.NewCommonEdgeX(errort.DefaultReqParamsError, "required decide_condition parameter missing", nil)
return
}
sql = fmt.Sprintf(sqlTemp, code, code, device.Id, code, fmt.Sprintf("TUMBLINGWINDOW(ss, %d)", valueCycle), code, decideCondition)
return
case constants.Avg:
sqlTemp := `SELECT window_start(),window_end(),rule_id(),deviceId,avg(json_path_query(data, "$.%s.value")) as avg_%s FROM mqtt_stream where deviceId = "%s" and messageType = "PROPERTY_REPORT" and json_path_exists(data, "$.%s") = true GROUP BY %s HAVING avg_%s %s`
valueCycle := s
if valueCycle == 0 {
err = errort.NewCommonEdgeX(errort.DefaultReqParamsError, "required value_cycle parameter missing", nil)
return
}
decideCondition := option["decide_condition"]
if decideCondition == "" {
err = errort.NewCommonEdgeX(errort.DefaultReqParamsError, "required decide_condition parameter missing", nil)
return
}
sql = fmt.Sprintf(sqlTemp, code, code, device.Id, code, fmt.Sprintf("TUMBLINGWINDOW(ss, %d)", valueCycle), code, decideCondition)
return
}
case constants.SpecsTypeText:
decideCondition := option["decide_condition"]
if decideCondition == "" {
err = errort.NewCommonEdgeX(errort.DefaultReqParamsError, "required decide_condition parameter missing", nil)
return
}
st := strings.Split(decideCondition, " ")
if len(st) != 2 {
return
}
sqlTemp := `SELECT rule_id(),json_path_query(data, "$.%s.time") as report_time,deviceId FROM mqtt_stream where deviceId = "%s" and messageType = "PROPERTY_REPORT" and json_path_exists(data, "$.%s") = true and json_path_query(data, "$.%s.value") = "%s"`
sql = fmt.Sprintf(sqlTemp, code, deviceId, code, code, st[1])
return
case constants.SpecsTypeBool:
decideCondition := option["decide_condition"]
if decideCondition == "" {
err = errort.NewCommonEdgeX(errort.DefaultReqParamsError, "required decide_condition parameter missing", nil)
return
}
st := strings.Split(decideCondition, " ")
if len(st) != 2 {
return
}
sqlTemp := `SELECT rule_id(),json_path_query(data, "$.%s.time") as report_time,deviceId FROM mqtt_stream where deviceId = "%s" and messageType = "PROPERTY_REPORT" and json_path_exists(data, "$.%s") = true and json_path_query(data, "$.%s.value") = "%s"`
if st[1] == "true" {
sql = fmt.Sprintf(sqlTemp, code, deviceId, code, code, "1")
} else if st[1] == "false" {
sql = fmt.Sprintf(sqlTemp, code, deviceId, code, code, "0")
}
return
case constants.SpecsTypeEnum:
decideCondition := option["decide_condition"]
if decideCondition == "" {
err = errort.NewCommonEdgeX(errort.DefaultReqParamsError, "required decide_condition parameter missing", nil)
return
}
st := strings.Split(decideCondition, " ")
if len(st) != 2 {
return
}
sqlTemp := `SELECT rule_id(),json_path_query(data, "$.%s.time") as report_time,deviceId FROM mqtt_stream where deviceId = "%s" and messageType = "PROPERTY_REPORT" and json_path_exists(data, "$.%s") = true and json_path_query(data, "$.%s.value") = "%s"`
sql = fmt.Sprintf(sqlTemp, code, deviceId, code, code, st[1])
return
}
}
}
if !codeFind {
err = errort.NewCommonEdgeX(errort.DefaultReqParamsError, "required code parameter missing", nil)
}
case string(constants.DeviceEventTrigger):
var codeFind bool
for _, event := range product.Events {
if code == event.Code {
codeFind = true
sqlTemp := `SELECT rule_id(),json_path_query(data, "$.eventTime") as report_time,deviceId FROM mqtt_stream where deviceId = "%s" and messageType = "EVENT_REPORT" and json_path_exists(data, "$.eventCode") = true and json_path_query(data, "$.eventCode") = "%s"`
sql = fmt.Sprintf(sqlTemp, device.Id, code)
return
}
}
if !codeFind {
err = errort.NewCommonEdgeX(errort.DefaultReqParamsError, "required code parameter missing", nil)
}
case string(constants.DeviceStatusTrigger):
var status string
deviceStatus := option["status"]
if deviceStatus == "" {
err = errort.NewCommonEdgeX(errort.DefaultReqParamsError, "required status parameter missing", nil)
return
}
if deviceStatus == "在线" {
status = constants.DeviceOnline
} else if deviceStatus == "离线" {
status = constants.DeviceOffline
} else {
err = errort.NewCommonEdgeX(errort.DefaultReqParamsError, "required status parameter missing", nil)
return
}
sqlTemp := `SELECT rule_id(),json_path_query(data, "$.time") as report_time,deviceId FROM mqtt_stream where deviceId = "%s" and messageType = "DEVICE_STATUS" and json_path_exists(data, "$.status") = true and json_path_query(data, "$.status") = "%s"`
sql = fmt.Sprintf(sqlTemp, device.Id, status)
return
default:
err = errort.NewCommonEdgeX(errort.DefaultReqParamsError, "required trigger parameter missing", nil)
return
}
return
}
func (p sceneApp) checkAlertRuleParam(ctx context.Context, scene models.Scene, operate string) error {
if operate == "start" {
if scene.Status == constants.SceneStart {
return errort.NewCommonErr(errort.AlertRuleStatusStarting, fmt.Errorf("scene id(%s) is runing ,not allow start", scene.Id))
}
}
var (
trigger string
)
if len(scene.Conditions) != 1 {
trigger = scene.Conditions[0].Option["trigger"]
switch scene.Conditions[0].ConditionType {
case "timer":
case "notify":
ekuiperApp := resourceContainer.EkuiperAppFrom(p.dic.Get)
exist, err := ekuiperApp.RuleExist(ctx, scene.Id)
if err != nil {
return err
}
if !exist {
}
trigger = scene.Conditions[0].Option["trigger"]
if trigger != string(constants.DeviceDataTrigger) || trigger != string(constants.DeviceEventTrigger) || trigger != string(constants.DeviceStatusTrigger) {
return errort.NewCommonErr(errort.SceneRuleParamsError, fmt.Errorf("scene id(%s) trigger param error", scene.Id))
}
option := scene.Conditions[0].Option
deviceId := option["device_id"]
deviceName := option["device_name"]
productId := option["product_id"]
productName := option["product_name"]
//trigger := option["trigger"]
code := option["code"]
if deviceId == "" || deviceName == "" || productId == "" || productName == "" || code == "" || trigger == "" {
return errort.NewCommonEdgeX(errort.SceneRuleParamsError, "required parameter missing", nil)
}
device, err := p.dbClient.DeviceById(deviceId)
if err != nil {
return errort.NewCommonErr(errort.SceneRuleParamsError, fmt.Errorf("scene id(%s) device not found", scene.Id))
}
product, err := p.dbClient.ProductById(productId)
if err != nil {
return errort.NewCommonErr(errort.SceneRuleParamsError, fmt.Errorf("scene id(%s) actions is null", scene.Id))
}
if device.ProductId != product.Id {
return errort.NewCommonErr(errort.SceneRuleParamsError, fmt.Errorf("scene id(%s) actions is null", scene.Id))
}
default:
return errort.NewCommonErr(errort.SceneRuleParamsError, fmt.Errorf("scene id(%s) conditionType param errror", scene.Id))
}
}
//-------------------------
if len(scene.Actions) == 0 {
return errort.NewCommonErr(errort.SceneRuleParamsError, fmt.Errorf("scene id(%s) actions is null", scene.Id))
}
for _, action := range scene.Actions {
//检查产品和设备是否存在
device, err := p.dbClient.DeviceById(action.DeviceID)
if err != nil {
return errort.NewCommonErr(errort.SceneRuleParamsError, fmt.Errorf("scene id(%s) device not found", scene.Id))
}
product, err := p.dbClient.ProductById(action.ProductID)
if err != nil {
return errort.NewCommonErr(errort.SceneRuleParamsError, fmt.Errorf("scene id(%s) product not found", scene.Id))
}
if device.ProductId != product.Id {
return errort.NewCommonErr(errort.SceneRuleParamsError, fmt.Errorf("scene id(%s) actions is null", scene.Id))
}
var find bool
if trigger == string(constants.DeviceDataTrigger) {
for _, property := range product.Properties {
if property.Code == action.Code {
find = true
break
}
}
}
if trigger == string(constants.DeviceEventTrigger) {
for _, event := range product.Events {
if event.Code == action.Code {
find = true
break
}
}
}
if !find {
return errort.NewCommonErr(errort.SceneRuleParamsError, fmt.Errorf("scene id(%s) code not found", scene.Id))
}
if action.Value == "" {
return errort.NewCommonErr(errort.SceneRuleParamsError, fmt.Errorf("scene id(%s) value is null", scene.Id))
}
}
return nil
}
func (p sceneApp) EkuiperNotify(ctx context.Context, req map[string]interface{}) error {
sceneId, ok := req["rule_id"]
if !ok {
return errort.NewCommonErr(errort.DefaultReqParamsError, errors.New(""))
}
var (
coverSceneId string
)
switch sceneId.(type) {
case string:
coverSceneId = sceneId.(string)
case int:
coverSceneId = strconv.Itoa(sceneId.(int))
case int64:
coverSceneId = strconv.Itoa(int(sceneId.(int64)))
case float64:
coverSceneId = fmt.Sprintf("%f", sceneId.(float64))
case float32:
coverSceneId = fmt.Sprintf("%f", sceneId.(float64))
}
if coverSceneId == "" {
return errort.NewCommonErr(errort.DefaultReqParamsError, errors.New(""))
}
scene, err := p.dbClient.SceneById(coverSceneId)
if err != nil {
return err
}
for _, action := range scene.Actions {
deviceApp := resourceContainer.DeviceItfFrom(p.dic.Get)
execRes := deviceApp.DeviceAction(dtos.JobAction{
ProductId: action.ProductID,
})
_, err := p.dbClient.AddSceneLog(models.SceneLog{
SceneId: scene.Id,
Name: scene.Name,
ExecRes: execRes.ToString(),
})
if err != nil {
p.lc.Errorf("add sceneLog err %v", err.Error())
}
}
return nil
}
func (p sceneApp) CheckSceneByDeviceId(ctx context.Context, deviceId string) error {
var req dtos.SceneSearchQueryRequest
req.Status = string(constants.SceneStart)
scenes, _, err := p.SceneSearch(ctx, req)
if err != nil {
return err
}
for _, scene := range scenes {
for _, condition := range scene.Conditions {
if condition.Option != nil && condition.Option["device_id"] == deviceId {
return errort.NewCommonEdgeX(errort.DeviceAssociationSceneRule, "This device has been bound to scene rules. Please stop reporting scene rules before proceeding with the operation", nil)
}
}
for _, action := range scene.Actions {
if action.DeviceID == deviceId {
return errort.NewCommonEdgeX(errort.DeviceAssociationSceneRule, "This device has been bound to scene rules. Please stop reporting scene rules before proceeding with the operation", nil)
}
}
}
return nil
}

View File

@ -0,0 +1,39 @@
//go:build !community
// +build !community
package application
import (
"context"
resourceContainer "github.com/winc-link/hummingbird/internal/hummingbird/core/container"
"github.com/winc-link/hummingbird/internal/pkg/di"
"github.com/winc-link/hummingbird/internal/pkg/logger"
"time"
//"gitlab.com/tedge/edgex/internal/dtos"
//"gitlab.com/tedge/edgex/internal/pkg/constants"
//"gitlab.com/tedge/edgex/internal/pkg/container"
//pkgContainer "github.com/winc-link/hummingbird/internal/pkg/container"
"github.com/winc-link/hummingbird/internal/pkg/crontab"
//"gitlab.com/tedge/edgex/internal/pkg/di"
//"gitlab.com/tedge/edgex/internal/pkg/errort"
//"gitlab.com/tedge/edgex/internal/pkg/logger"
//resourceContainer "gitlab.com/tedge/edgex/internal/tedge/resource/container"
//"gitlab.com/tedge/edgex/internal/tools/atopclient"
)
func InitSchedule(dic *di.Container, lc logger.LoggingClient) {
lc.Info("init schedule")
// 每天 1 点
crontab.Schedule.AddFunc("0 1 * * *", func() {
lc.Debugf("schedule statistic device msg conut: %v", time.Now().Format("2006-01-02 15:04:05"))
deviceItf := resourceContainer.DeviceItfFrom(dic.Get)
err := deviceItf.DevicesReportMsgGather(context.Background())
if err != nil {
lc.Error("schedule statistic device err:", err)
}
})
crontab.Start()
}

View File

@ -0,0 +1,633 @@
/*******************************************************************************
* Copyright 2017 Dell Inc.
* Copyright (c) 2019 Intel Corporation
*
* 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 thingmodelapp
import (
"context"
"encoding/json"
"fmt"
"github.com/winc-link/hummingbird/internal/dtos"
resourceContainer "github.com/winc-link/hummingbird/internal/hummingbird/core/container"
interfaces "github.com/winc-link/hummingbird/internal/hummingbird/core/interface"
"github.com/winc-link/hummingbird/internal/models"
"github.com/winc-link/hummingbird/internal/pkg/constants"
"github.com/winc-link/hummingbird/internal/pkg/container"
"github.com/winc-link/hummingbird/internal/pkg/di"
"github.com/winc-link/hummingbird/internal/pkg/errort"
"github.com/winc-link/hummingbird/internal/pkg/logger"
"github.com/winc-link/hummingbird/internal/pkg/utils"
"gorm.io/gorm"
"gorm.io/gorm/clause"
"strings"
"time"
)
const (
Property = "property"
Event = "event"
Action = "action"
)
type thingModelApp struct {
dic *di.Container
dbClient interfaces.DBClient
lc logger.LoggingClient
}
func (t thingModelApp) AddThingModel(ctx context.Context, req dtos.ThingModelAddOrUpdateReq) (string, error) {
product, err := t.dbClient.ProductById(req.ProductId)
if err != nil {
return "", err
}
if product.Status == constants.ProductRelease {
//产品已发布,不能修改物模型
return "", errort.NewCommonEdgeX(errort.ProductRelease, "Please cancel publishing the product first before proceeding with the operation", nil)
}
if err = validatorReq(req, product); err != nil {
return "", errort.NewCommonEdgeX(errort.DefaultReqParamsError, "param valida error", err)
}
switch req.ThingModelType {
case Property:
var property models.Properties
property.ProductId = req.ProductId
property.Name = req.Name
property.Code = req.Code
property.Description = req.Description
if req.Property != nil {
typeSpec, _ := json.Marshal(req.Property.TypeSpec)
property.AccessMode = req.Property.AccessModel
property.Require = req.Property.Require
property.TypeSpec.Type = req.Property.DataType
property.TypeSpec.Specs = string(typeSpec)
}
property.Tag = req.Tag
err = resourceContainer.DataDBClientFrom(t.dic.Get).AddDatabaseField(ctx, req.ProductId, req.Property.DataType, req.Code, req.Name)
if err != nil {
return "", err
}
ds, err := t.dbClient.AddThingModelProperty(property)
if err != nil {
return "", err
}
t.ProductUpdateCallback(req.ProductId)
return ds.Id, nil
case Event:
var event models.Events
event.ProductId = req.ProductId
event.Name = req.Name
event.Code = req.Code
event.Description = req.Description
if req.Event != nil {
var inputOutput []models.InputOutput
for _, outPutParam := range req.Event.OutPutParam {
typeSpec, _ := json.Marshal(outPutParam.TypeSpec)
inputOutput = append(inputOutput, models.InputOutput{
Code: outPutParam.Code,
Name: outPutParam.Name,
TypeSpec: models.TypeSpec{
Type: outPutParam.DataType,
Specs: string(typeSpec),
},
})
}
event.OutputParams = inputOutput
event.EventType = req.Event.EventType
}
event.Tag = req.Tag
err = resourceContainer.DataDBClientFrom(t.dic.Get).AddDatabaseField(ctx, req.ProductId, "", req.Code, req.Name)
if err != nil {
return "", err
}
ds, err := t.dbClient.AddThingModelEvent(event)
if err != nil {
return "", err
}
t.ProductUpdateCallback(req.ProductId)
return ds.Id, nil
case Action:
var action models.Actions
action.ProductId = req.ProductId
action.Name = req.Name
action.Code = req.Code
action.Description = req.Description
if req.Action != nil {
action.CallType = req.Action.CallType
var inputOutput []models.InputOutput
for _, inPutParam := range req.Action.InPutParam {
typeSpec, _ := json.Marshal(inPutParam.TypeSpec)
inputOutput = append(inputOutput, models.InputOutput{
Code: inPutParam.Code,
Name: inPutParam.Name,
TypeSpec: models.TypeSpec{
Type: inPutParam.DataType,
Specs: string(typeSpec),
},
})
}
action.InputParams = inputOutput
var outOutput []models.InputOutput
for _, outPutParam := range req.Action.OutPutParam {
typeSpec, _ := json.Marshal(outPutParam.TypeSpec)
outOutput = append(outOutput, models.InputOutput{
Code: outPutParam.Code,
Name: outPutParam.Name,
TypeSpec: models.TypeSpec{
Type: outPutParam.DataType,
Specs: string(typeSpec),
},
})
}
action.OutputParams = outOutput
}
action.Tag = req.Tag
err = resourceContainer.DataDBClientFrom(t.dic.Get).AddDatabaseField(ctx, req.ProductId, "", req.Code, req.Name)
if err != nil {
return "", err
}
ds, err := t.dbClient.AddThingModelAction(action)
if err != nil {
return "", err
}
t.ProductUpdateCallback(req.ProductId)
return ds.Id, nil
default:
return "", errort.NewCommonEdgeX(errort.DefaultReqParamsError, "param valida error", fmt.Errorf("req params error"))
}
}
func (t thingModelApp) UpdateThingModel(ctx context.Context, req dtos.ThingModelAddOrUpdateReq) error {
product, err := t.dbClient.ProductById(req.ProductId)
if err != nil {
return err
}
if product.Status == constants.ProductRelease {
//产品已发布,不能修改物模型
return errort.NewCommonEdgeX(errort.ProductRelease, "Please cancel publishing the product first before proceeding with the operation", nil)
}
if err := validatorReq(req, product); err != nil {
return errort.NewCommonEdgeX(errort.DefaultReqParamsError, "param valida error", err)
}
switch req.ThingModelType {
case Property:
var property models.Properties
property.Id = req.Id
property.ProductId = req.ProductId
property.Name = req.Name
property.Code = req.Code
property.Description = req.Description
if req.Property != nil {
typeSpec, _ := json.Marshal(req.Property.TypeSpec)
property.AccessMode = req.Property.AccessModel
property.Require = req.Property.Require
property.TypeSpec.Type = req.Property.DataType
property.TypeSpec.Specs = string(typeSpec)
}
property.Tag = req.Tag
err = resourceContainer.DataDBClientFrom(t.dic.Get).ModifyDatabaseField(ctx, req.ProductId, req.Property.DataType, req.Code, req.Name)
if err != nil {
return err
}
err = t.dbClient.UpdateThingModelProperty(property)
if err != nil {
return err
}
t.ProductUpdateCallback(req.ProductId)
return nil
case Event:
var event models.Events
event.Id = req.Id
event.ProductId = req.ProductId
event.Name = req.Name
event.Code = req.Code
event.Description = req.Description
if req.Event != nil {
var inputOutput []models.InputOutput
for _, outPutParam := range req.Event.OutPutParam {
typeSpec, _ := json.Marshal(outPutParam.TypeSpec)
inputOutput = append(inputOutput, models.InputOutput{
Code: outPutParam.Code,
Name: outPutParam.Name,
TypeSpec: models.TypeSpec{
Type: outPutParam.DataType,
Specs: string(typeSpec),
},
})
}
event.OutputParams = inputOutput
event.EventType = req.Event.EventType
}
event.Tag = req.Tag
err = resourceContainer.DataDBClientFrom(t.dic.Get).ModifyDatabaseField(ctx, req.ProductId, "", req.Code, req.Name)
if err != nil {
return err
}
err = t.dbClient.UpdateThingModelEvent(event)
if err != nil {
return err
}
t.ProductUpdateCallback(req.ProductId)
return nil
case Action:
var action models.Actions
action.Id = req.Id
action.ProductId = req.ProductId
action.Name = req.Name
action.Code = req.Code
action.Description = req.Description
if req.Action != nil {
action.CallType = req.Action.CallType
var inputOutput []models.InputOutput
for _, inPutParam := range req.Action.InPutParam {
typeSpec, _ := json.Marshal(inPutParam.TypeSpec)
inputOutput = append(inputOutput, models.InputOutput{
Code: inPutParam.Code,
Name: inPutParam.Name,
TypeSpec: models.TypeSpec{
Type: inPutParam.DataType,
Specs: string(typeSpec),
},
})
}
action.InputParams = inputOutput
var outOutput []models.InputOutput
for _, outPutParam := range req.Action.OutPutParam {
typeSpec, _ := json.Marshal(outPutParam.TypeSpec)
outOutput = append(outOutput, models.InputOutput{
Code: outPutParam.Code,
Name: outPutParam.Name,
TypeSpec: models.TypeSpec{
Type: outPutParam.DataType,
Specs: string(typeSpec),
},
})
}
action.OutputParams = outOutput
}
action.Tag = req.Tag
err = resourceContainer.DataDBClientFrom(t.dic.Get).ModifyDatabaseField(ctx, req.ProductId, "", req.Code, req.Name)
if err != nil {
return err
}
err = t.dbClient.UpdateThingModelAction(action)
if err != nil {
return err
}
t.ProductUpdateCallback(req.ProductId)
return nil
default:
return errort.NewCommonEdgeX(errort.DefaultReqParamsError, "param valida error", fmt.Errorf("req params error"))
}
}
func validatorReq(req dtos.ThingModelAddOrUpdateReq, product models.Product) error {
if req.ProductId == "" || req.Name == "" || req.Code == "" {
return errort.NewCommonEdgeX(errort.DefaultReqParamsError, "params error", nil)
}
switch req.ThingModelType {
case "property":
for _, property := range product.Properties {
if strings.ToLower(property.Code) == strings.ToLower(req.Code) {
return errort.NewCommonEdgeX(errort.ThingModelCodeExist, "code identifier already exists", nil)
}
}
case "action":
for _, action := range product.Actions {
if strings.ToLower(action.Code) == strings.ToLower(req.Code) {
return errort.NewCommonEdgeX(errort.ThingModelCodeExist, "code identifier already exists", nil)
}
}
case "event":
for _, event := range product.Events {
if strings.ToLower(event.Code) == strings.ToLower(req.Code) {
return errort.NewCommonEdgeX(errort.ThingModelCodeExist, "code identifier already exists", nil)
}
}
default:
return errort.NewCommonEdgeX(errort.DefaultReqParamsError, "params error", nil)
}
return nil
}
func NewThingModelApp(ctx context.Context, dic *di.Container) interfaces.ThingModelItf {
lc := container.LoggingClientFrom(dic.Get)
dbClient := resourceContainer.DBClientFrom(dic.Get)
return &thingModelApp{
dic: dic,
dbClient: dbClient,
lc: lc,
}
}
func (t thingModelApp) ThingModelDelete(ctx context.Context, id string, thingModelType string) error {
switch thingModelType {
case Property:
propertyInfo, err := t.dbClient.ThingModelPropertyById(id)
if err != nil {
return err
}
err = resourceContainer.DataDBClientFrom(t.dic.Get).DelDatabaseField(ctx, propertyInfo.ProductId, propertyInfo.Code)
if err != nil {
return err
}
err = t.dbClient.ThingModelDeleteProperty(id)
if err != nil {
return err
}
t.ProductUpdateCallback(propertyInfo.ProductId)
case Event:
eventInfo, err := t.dbClient.ThingModelEventById(id)
if err != nil {
return err
}
err = resourceContainer.DataDBClientFrom(t.dic.Get).DelDatabaseField(ctx, eventInfo.ProductId, eventInfo.Code)
if err != nil {
return err
}
err = t.dbClient.ThingModelDeleteEvent(id)
if err != nil {
return err
}
t.ProductUpdateCallback(eventInfo.ProductId)
case Action:
actionInfo, err := t.dbClient.ThingModelActionsById(id)
if err != nil {
return err
}
err = resourceContainer.DataDBClientFrom(t.dic.Get).DelDatabaseField(ctx, actionInfo.ProductId, actionInfo.Code)
if err != nil {
return err
}
err = t.dbClient.ThingModelDeleteAction(id)
if err != nil {
return err
}
t.ProductUpdateCallback(actionInfo.ProductId)
default:
return errort.NewCommonEdgeX(errort.DefaultReqParamsError, "param valida error", fmt.Errorf("req params error"))
}
return nil
}
func (t thingModelApp) ProductUpdateCallback(productId string) {
go func() {
productService := resourceContainer.ProductAppNameFrom(t.dic.Get)
product, err := productService.ProductModelById(context.Background(), productId)
if err != nil {
return
}
productService.UpdateProductCallBack(product)
}()
}
func (t thingModelApp) SystemThingModelSearch(ctx context.Context, req dtos.SystemThingModelSearchReq) (interface{}, error) {
return t.dbClient.SystemThingModelSearch(req.ThingModelType, req.ModelName)
}
func (t thingModelApp) OpenApiQueryThingModel(ctx context.Context, productId string) (dtos.OpenApiQueryThingModel, error) {
product, err := t.dbClient.ProductById(productId)
if err != nil {
return dtos.OpenApiQueryThingModel{}, err
}
var response dtos.OpenApiQueryThingModel
for _, property := range product.Properties {
response.Properties = append(response.Properties, dtos.OpenApiThingModelProperties{
Id: property.Id,
Name: property.Name,
Code: property.Code,
AccessMode: property.AccessMode,
Require: property.Require,
TypeSpec: property.TypeSpec,
Description: property.Description,
})
}
if len(response.Properties) == 0 {
response.Properties = make([]dtos.OpenApiThingModelProperties, 0)
}
for _, event := range product.Events {
response.Events = append(response.Events, dtos.OpenApiThingModelEvents{
Id: event.Id,
EventType: event.EventType,
Name: event.Name,
Code: event.Code,
Description: event.Description,
Require: event.Require,
OutputParams: event.OutputParams,
})
}
if len(response.Events) == 0 {
response.Events = make([]dtos.OpenApiThingModelEvents, 0)
}
for _, action := range product.Actions {
response.Services = append(response.Services, dtos.OpenApiThingModelServices{
Id: action.Id,
Name: action.Name,
Code: action.Code,
Description: action.Description,
Require: action.Require,
CallType: action.CallType,
InputParams: action.InputParams,
OutputParams: action.OutputParams,
})
}
if len(response.Services) == 0 {
response.Services = make([]dtos.OpenApiThingModelServices, 0)
}
return response, nil
}
func (t thingModelApp) OpenApiAddThingModel(ctx context.Context, req dtos.OpenApiThingModelAddOrUpdateReq) error {
_, err := t.dbClient.ProductById(req.ProductId)
if err != nil {
return err
}
var properties []models.Properties
var events []models.Events
var action []models.Actions
for _, property := range req.Properties {
propertyId := property.Id
if propertyId == "" {
propertyId = utils.RandomNum()
}
properties = append(properties, models.Properties{
Id: propertyId,
ProductId: req.ProductId,
Name: property.Name,
Code: property.Code,
AccessMode: property.AccessMode,
Require: property.Require,
TypeSpec: property.TypeSpec,
Description: property.Description,
Timestamps: models.Timestamps{
Created: time.Now().UnixMilli(),
},
})
}
for _, event := range req.Events {
eventId := event.Id
if eventId == "" {
eventId = utils.RandomNum()
}
events = append(events, models.Events{
Id: eventId,
ProductId: req.ProductId,
Name: event.Name,
EventType: event.EventType,
Code: event.Code,
Description: event.Description,
Require: event.Require,
OutputParams: event.OutputParams,
Timestamps: models.Timestamps{
Created: time.Now().UnixMilli(),
},
})
}
for _, service := range req.Services {
serviceId := service.Id
if serviceId == "" {
serviceId = utils.RandomNum()
}
action = append(action, models.Actions{
Id: serviceId,
ProductId: req.ProductId,
Name: service.Name,
Code: service.Code,
Description: service.Description,
Require: service.Require,
CallType: service.CallType,
InputParams: service.InputParams,
OutputParams: service.OutputParams,
Timestamps: models.Timestamps{
Created: time.Now().UnixMilli(),
},
})
}
//t.dbClient.ThingModelActionsById()
var shouldCallBack bool
updateFunc := func(source interface{}, db *gorm.DB) error {
tx := db.Session(&gorm.Session{FullSaveAssociations: true}).Clauses(clause.OnConflict{UpdateAll: true}).Save(source)
return tx.Error
//tx := db.Save(&source).Error
//return tx
}
db := t.dbClient.GetDBInstance()
//db.Begin()
if len(properties) > 0 {
shouldCallBack = true
err := updateFunc(properties, db)
if err != nil {
//db.Rollback()
return err
}
}
if len(events) > 0 {
shouldCallBack = true
err := updateFunc(events, db)
if err != nil {
//db.Rollback()
return err
}
}
if len(action) > 0 {
shouldCallBack = true
err := updateFunc(action, db)
if err != nil {
//db.Rollback()
return err
}
}
//db.Commit()
if shouldCallBack {
t.ProductUpdateCallback(req.ProductId)
}
return nil
}
func (t thingModelApp) OpenApiDeleteThingModel(ctx context.Context, req dtos.OpenApiThingModelDeleteReq) error {
product, err := t.dbClient.ProductById(req.ProductId)
if err != nil {
return err
}
var productPropertyIds []string
var productEventIds []string
var productService []string
for _, property := range product.Properties {
productPropertyIds = append(productPropertyIds, property.Id)
}
for _, event := range product.Events {
productEventIds = append(productEventIds, event.Id)
}
for _, action := range product.Actions {
productService = append(productService, action.Id)
}
for _, id := range req.PropertyIds {
if utils.InStringSlice(id, productPropertyIds) {
if err = t.dbClient.ThingModelDeleteProperty(id); err != nil {
return err
}
}
}
for _, id := range req.EventIds {
if utils.InStringSlice(id, productEventIds) {
if err = t.dbClient.ThingModelDeleteEvent(id); err != nil {
return err
}
}
}
for _, id := range req.ServiceIds {
if utils.InStringSlice(id, productService) {
if err = t.dbClient.ThingModelDeleteAction(id); err != nil {
return err
}
}
}
return nil
}

View File

@ -0,0 +1,175 @@
/*******************************************************************************
* Copyright 2017 Dell Inc.
* Copyright (c) 2019 Intel Corporation
*
* 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 thingmodeltemplate
import (
"context"
"encoding/json"
"github.com/winc-link/hummingbird/internal/dtos"
resourceContainer "github.com/winc-link/hummingbird/internal/hummingbird/core/container"
interfaces "github.com/winc-link/hummingbird/internal/hummingbird/core/interface"
"github.com/winc-link/hummingbird/internal/models"
"github.com/winc-link/hummingbird/internal/pkg/container"
"github.com/winc-link/hummingbird/internal/pkg/di"
"github.com/winc-link/hummingbird/internal/pkg/logger"
"github.com/winc-link/hummingbird/internal/pkg/utils"
"time"
)
type thingModelTemplate struct {
dic *di.Container
dbClient interfaces.DBClient
lc logger.LoggingClient
}
func (m thingModelTemplate) ThingModelTemplateSearch(ctx context.Context, req dtos.ThingModelTemplateRequest) ([]dtos.ThingModelTemplateResponse, uint32, error) {
offset, limit := req.BaseSearchConditionQuery.GetPage()
thingModelTemplates, total, err := m.dbClient.ThingModelTemplateSearch(offset, limit, req)
if err != nil {
m.lc.Errorf("thingModelTemplate Search err %v", err)
return []dtos.ThingModelTemplateResponse{}, 0, err
}
libs := make([]dtos.ThingModelTemplateResponse, len(thingModelTemplates))
for i, thingModelTemplate := range thingModelTemplates {
libs[i] = dtos.ThingModelTemplateResponseFromModel(thingModelTemplate)
}
return libs, total, nil
}
func (m thingModelTemplate) ThingModelTemplateByCategoryKey(ctx context.Context, categoryKey string) (dtos.ThingModelTemplateResponse, error) {
thingModelTemplate, err := m.dbClient.ThingModelTemplateByCategoryKey(categoryKey)
if err != nil {
m.lc.Errorf("thingModelTemplate Search err %v", err)
return dtos.ThingModelTemplateResponse{}, err
}
var libs dtos.ThingModelTemplateResponse
libs = dtos.ThingModelTemplateResponseFromModel(thingModelTemplate)
return libs, nil
}
func (m thingModelTemplate) Sync(ctx context.Context, versionName string) (int64, error) {
filePath := versionName + "/thing_model_template.json"
cosApp := resourceContainer.CosAppNameFrom(m.dic.Get)
bs, err := cosApp.Get(filePath)
if err != nil {
m.lc.Errorf(err.Error())
return 0, err
}
var cosThingModelTemplateResp []dtos.CosThingModelTemplateResponse
err = json.Unmarshal(bs, &cosThingModelTemplateResp)
if err != nil {
m.lc.Errorf(err.Error())
return 0, err
}
baseQuery := dtos.BaseSearchConditionQuery{
IsAll: true,
}
dbreq := dtos.ThingModelTemplateRequest{BaseSearchConditionQuery: baseQuery}
thingModelTemplateResponse, _, err := m.ThingModelTemplateSearch(ctx, dbreq)
if err != nil {
return 0, err
}
upsertThingModelTemplate := make([]models.ThingModelTemplate, 0)
for _, cosThingModelTemplate := range cosThingModelTemplateResp {
var find bool
for _, localThingModelResponse := range thingModelTemplateResponse {
if cosThingModelTemplate.CategoryKey == localThingModelResponse.CategoryKey {
upsertThingModelTemplate = append(upsertThingModelTemplate, models.ThingModelTemplate{
Id: localThingModelResponse.Id,
CategoryName: cosThingModelTemplate.CategoryName,
CategoryKey: cosThingModelTemplate.CategoryKey,
ThingModelJSON: cosThingModelTemplate.ThingModelJSON,
})
find = true
break
}
}
if !find {
upsertThingModelTemplate = append(upsertThingModelTemplate, models.ThingModelTemplate{
Timestamps: models.Timestamps{
Created: time.Now().Unix(),
},
Id: utils.GenUUID(),
CategoryName: cosThingModelTemplate.CategoryName,
CategoryKey: cosThingModelTemplate.CategoryKey,
ThingModelJSON: cosThingModelTemplate.ThingModelJSON,
})
}
}
rows, err := m.dbClient.BatchUpsertThingModelTemplate(upsertThingModelTemplate)
m.lc.Infof("upsert thingModelTemplate rows %+v", rows)
if err != nil {
return 0, err
}
var modelProperty []models.Properties
var modelEvent []models.Events
var modelAction []models.Actions
propertyTemp := map[string]struct{}{}
eventTemp := map[string]struct{}{}
actionTemp := map[string]struct{}{}
for _, cosThingModelTemplate := range cosThingModelTemplateResp {
p, e, a := dtos.GetModelPropertyEventActionByThingModelTemplate(cosThingModelTemplate.ThingModelJSON)
for _, properties := range p {
if _, ok := propertyTemp[properties.Code]; !ok {
properties.Id = utils.GenUUID()
properties.System = true
modelProperty = append(modelProperty, properties)
propertyTemp[properties.Code] = struct{}{}
}
}
for _, event := range e {
if _, ok := eventTemp[event.Code]; !ok {
event.Id = utils.GenUUID()
event.System = true
modelEvent = append(modelEvent, event)
eventTemp[event.Code] = struct{}{}
}
}
for _, action := range a {
if _, ok := actionTemp[action.Code]; !ok {
action.Id = utils.GenUUID()
action.System = true
modelAction = append(modelAction, action)
actionTemp[action.Code] = struct{}{}
}
}
}
m.dbClient.BatchDeleteSystemProperties()
m.dbClient.BatchDeleteSystemActions()
m.dbClient.BatchDeleteSystemEvents()
m.dbClient.BatchInsertSystemProperties(modelProperty)
m.dbClient.BatchInsertSystemActions(modelAction)
m.dbClient.BatchInsertSystemEvents(modelEvent)
return rows, nil
}
func NewThingModelTemplateApp(ctx context.Context, dic *di.Container) interfaces.ThingModelTemplateApp {
lc := container.LoggingClientFrom(dic.Get)
dbClient := resourceContainer.DBClientFrom(dic.Get)
return &thingModelTemplate{
dic: dic,
dbClient: dbClient,
lc: lc,
}
}

View File

@ -0,0 +1,320 @@
/*******************************************************************************
* Copyright 2017.
*
* 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 timerapp
import (
"context"
"github.com/winc-link/hummingbird/internal/dtos"
resourceContainer "github.com/winc-link/hummingbird/internal/hummingbird/core/container"
interfaces "github.com/winc-link/hummingbird/internal/hummingbird/core/interface"
"github.com/winc-link/hummingbird/internal/pkg/constants"
"github.com/winc-link/hummingbird/internal/pkg/container"
"github.com/winc-link/hummingbird/internal/pkg/di"
//"github.com/winc-link/hummingbird/internal/edge/sharp/module/timer/db"
//"github.com/winc-link/hummingbird/internal/pkg/timer/db"
"github.com/winc-link/hummingbird/internal/pkg/timer/jobrunner"
"github.com/winc-link/hummingbird/internal/pkg/timer/jobs"
"github.com/winc-link/hummingbird/internal/pkg/logger"
"sort"
"sync"
"time"
)
type EdgeTimer struct {
mutex sync.Mutex
logger logger.LoggingClient
db interfaces.DBClient
//db db.TimerDBClient
// job id map
jobMap map[string]struct{}
// 任务链表 有序
entries []*entry
// 停止信号
stop chan struct{}
// 添加任务channel
add chan *entry
// 更新任务
//update chan *jobs.UpdateJobStu
// 删除任务 uuid
rm chan string
// 启动标志
running bool
location *time.Location
f jobrunner.JobRunFunc
}
func NewCronTimer(ctx context.Context,
f jobrunner.JobRunFunc, dic *di.Container) *EdgeTimer {
dbClient := resourceContainer.DBClientFrom(dic.Get)
l := container.LoggingClientFrom(dic.Get)
et := &EdgeTimer{
logger: l,
db: dbClient,
rm: make(chan string),
add: make(chan *entry),
entries: nil,
jobMap: make(map[string]struct{}),
stop: make(chan struct{}),
running: false,
location: time.Local,
f: f,
}
// restore
et.restoreJobs()
go et.run()
return et
}
func (et *EdgeTimer) restoreJobs() {
scenes, _, _ := et.db.SceneSearch(0, -1, dtos.SceneSearchQueryRequest{})
if len(scenes) == 0 {
return
}
for _, scene := range scenes {
if len(scene.Conditions) > 0 && scene.Status == constants.SceneStart {
if scene.Conditions[0].ConditionType == "timer" {
job, err := scene.ToRuntimeJob()
if err != nil {
et.logger.Errorf("restore jobs runtime job err %v", err.Error())
continue
}
err = et.AddJobToRunQueue(job)
if err != nil {
et.logger.Errorf("restore jobs add job to queue err %v", err.Error())
}
}
}
}
return
}
func (et *EdgeTimer) Stop() {
et.mutex.Lock()
defer et.mutex.Unlock()
if et.running {
close(et.stop)
et.running = false
}
}
type (
// 任务
entry struct {
JobID string
Schedule *jobs.JobSchedule
Next time.Time
Prev time.Time
}
)
func (e entry) Valid() bool { return e.JobID != "" }
type byTime []*entry
func (s byTime) Len() int { return len(s) }
func (s byTime) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
func (s byTime) Less(i, j int) bool {
if s[i].Next.IsZero() {
return false
}
if s[j].Next.IsZero() {
return true
}
return s[i].Next.Before(s[j].Next)
}
func (et *EdgeTimer) now() time.Time {
return time.Now().In(et.location)
}
func (et *EdgeTimer) run() {
et.mutex.Lock()
if et.running {
et.mutex.Unlock()
return
}
et.running = true
et.mutex.Unlock()
et.logger.Info("edge timer started...")
now := et.now()
for _, entry := range et.entries {
if next, b := entry.Schedule.Next(now); !b {
entry.Next = next
}
}
var timer = time.NewTimer(100000 * time.Hour)
for {
// Determine the next entry to run.
sort.Sort(byTime(et.entries))
if len(et.entries) == 0 || et.entries[0].Next.IsZero() {
// If there are no entries yet, just sleep - it still handles new entries
// and stop requests.
timer.Reset(100000 * time.Hour)
} else {
et.logger.Debugf("next wake time: %+v with jobID: %s", et.entries[0].Next, et.entries[0].JobID)
timer.Reset(et.entries[0].Next.Sub(now))
}
select {
case now = <-timer.C:
timer.Stop()
now = now.In(et.location)
et.logger.Infof("wake now: %+v with jobID: %s", now, et.entries[0].JobID)
var (
//finished []int
//eIndex = len(et.entries) - 1
)
for i, e := range et.entries {
if e.Next.After(now) || e.Next.IsZero() {
break
}
// async call
go et.f(e.JobID, *et.entries[i].Schedule)
//times := e.Schedule.ScheduleAdd1()
if false {
//finished = append(finished, i)
} else {
e.Prev = e.Next
if next, b := e.Schedule.Next(now); !b {
e.Next = next
et.logger.Infof("run now: %+v, entry: jobId: %s, jobName: %s, next: %+v", now, e.JobID, e.Schedule.JobName, e.Next)
// update prev next and runtimes
//if err := et.db.UpdateRuntimeInfo(e.JobID, e.Prev.UnixMilli(), e.Next.UnixMilli(), times); err != nil {
// et.logger.Errorf("update job: %s runtime info error: %s, prev: %d, next: %d",
// e.JobID, err, e.Prev.Unix(), e.Next.Unix())
//}
}
//}
}
}
//if len(finished) > 0 {
// for i := range finished {
// et.entries[finished[i]], et.entries[eIndex] = et.entries[eIndex], et.entries[finished[i]]
// eIndex--
// }
// del := et.entries[eIndex+1:]
// ids := make([]string, 0, len(del))
// for i := range del {
// ids = append(ids, del[i].JobID)
// }
// et.logger.Infof("jobs ended, delete from db: %+v", ids)
// if err := et.db.DeleteJobs(ids); err != nil {
// et.logger.Errorf("jobs ended, delete from db failure: %+v", ids)
// }
// et.entries = et.entries[:eIndex+1]
//}
//et.logger.Infof("entries len: %d", len(et.entries))
case newEntry := <-et.add:
timer.Stop()
now = et.now()
if next, b := newEntry.Schedule.Next(now); !b {
newEntry.Next = next
et.entries = append(et.entries, newEntry)
et.logger.Infof("added job now: %+v, next: %+v", now, newEntry.Next)
}
et.logger.Infof("added job: %v, now: %+v, next: %+v", newEntry.JobID, now, newEntry.Next)
case entryID := <-et.rm:
timer.Stop()
now = et.now()
et.removeEntry(entryID)
case <-et.stop:
timer.Stop()
et.logger.Info("tedge timer stopped...")
return
}
}
}
func (et *EdgeTimer) schedule(schedule *jobs.JobSchedule) {
et.mutex.Lock()
defer et.mutex.Unlock()
entry := &entry{
JobID: schedule.GetJobId(),
Schedule: schedule,
}
if !et.running {
et.entries = append(et.entries, entry)
} else {
et.add <- entry
}
}
func (et *EdgeTimer) remove(id string) {
if et.running {
et.rm <- id
} else {
et.removeEntry(id)
}
}
func (et *EdgeTimer) removeEntry(id string) {
var b bool
et.mutex.Lock()
defer et.mutex.Unlock()
for i, e := range et.entries {
if e.JobID == id {
et.entries[i], et.entries[len(et.entries)-1] = et.entries[len(et.entries)-1], et.entries[i]
b = true
break
}
}
if b {
et.entries[len(et.entries)-1] = nil
et.entries = et.entries[:len(et.entries)-1]
delete(et.jobMap, id)
et.logger.Debugf("entry length: %d, deleted job id: %s", len(et.entries), id)
} else {
et.logger.Warnf("unknown jobs,id: %s", id)
}
}
func (et *EdgeTimer) DeleteJob(id string) {
et.remove(id)
}
func (et *EdgeTimer) AddJobToRunQueue(j *jobs.JobSchedule) error {
if _, ok := et.jobMap[j.JobID]; ok {
et.logger.Warnf("job is already in map: %s", j.JobID)
return nil
}
// check expire
//if exp, ok := j.TimeData.Expression.(jobs.CronExp); !ok {
// return errort.NewCommonErr(errort.DefaultSystemError, fmt.Errorf("cron job expression error"))
//} else {
// if _, err := jobs.ParseStandard(exp.CronTab); err != nil {
// return err
// }
//}
if _, err := jobs.ParseStandard(j.TimeData.Expression); err != nil {
return err
}
et.schedule(j)
et.mutex.Lock()
defer et.mutex.Unlock()
et.jobMap[j.JobID] = struct{}{}
return nil
}

View File

@ -0,0 +1,114 @@
/*******************************************************************************
* Copyright 2017 Dell Inc.
* Copyright (c) 2019 Intel Corporation
*
* 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 unittemplate
import (
"context"
"encoding/json"
"github.com/winc-link/hummingbird/internal/dtos"
resourceContainer "github.com/winc-link/hummingbird/internal/hummingbird/core/container"
interfaces "github.com/winc-link/hummingbird/internal/hummingbird/core/interface"
"github.com/winc-link/hummingbird/internal/models"
"github.com/winc-link/hummingbird/internal/pkg/container"
"github.com/winc-link/hummingbird/internal/pkg/di"
"github.com/winc-link/hummingbird/internal/pkg/logger"
"github.com/winc-link/hummingbird/internal/pkg/utils"
)
type unitApp struct {
dic *di.Container
dbClient interfaces.DBClient
lc logger.LoggingClient
}
func NewUnitTemplateApp(ctx context.Context, dic *di.Container) interfaces.UnitApp {
lc := container.LoggingClientFrom(dic.Get)
dbClient := resourceContainer.DBClientFrom(dic.Get)
return &unitApp{
dic: dic,
dbClient: dbClient,
lc: lc,
}
}
func (m *unitApp) UnitTemplateSearch(ctx context.Context, req dtos.UnitRequest) ([]dtos.UnitResponse, uint32, error) {
offset, limit := req.BaseSearchConditionQuery.GetPage()
unitTemplates, total, err := m.dbClient.UnitSearch(offset, limit, req)
if err != nil {
m.lc.Errorf("unit Templates Search err %v", err)
return []dtos.UnitResponse{}, 0, err
}
libs := make([]dtos.UnitResponse, len(unitTemplates))
for i, unitTemplate := range unitTemplates {
libs[i] = dtos.UnitTemplateResponseFromModel(unitTemplate)
}
return libs, total, nil
}
func (m *unitApp) Sync(ctx context.Context, versionName string) (int64, error) {
filePath := versionName + "/unit_template.json"
cosApp := resourceContainer.CosAppNameFrom(m.dic.Get)
bs, err := cosApp.Get(filePath)
if err != nil {
m.lc.Errorf(err.Error())
return 0, err
}
var cosUnitTemplateResp []dtos.CosUnitTemplateResponse
err = json.Unmarshal(bs, &cosUnitTemplateResp)
if err != nil {
m.lc.Errorf(err.Error())
return 0, err
}
baseQuery := dtos.BaseSearchConditionQuery{
IsAll: true,
}
dbreq := dtos.UnitRequest{BaseSearchConditionQuery: baseQuery}
unitTemplateResponse, _, err := m.UnitTemplateSearch(ctx, dbreq)
if err != nil {
return 0, err
}
upsertUnitTemplate := make([]models.Unit, 0)
for _, cosUnitTemplate := range cosUnitTemplateResp {
var find bool
for _, localTemplateResponse := range unitTemplateResponse {
if cosUnitTemplate.UnitName == localTemplateResponse.UnitName {
upsertUnitTemplate = append(upsertUnitTemplate, models.Unit{
Id: localTemplateResponse.Id,
UnitName: cosUnitTemplate.UnitName,
Symbol: cosUnitTemplate.Symbol,
})
find = true
break
}
}
if !find {
upsertUnitTemplate = append(upsertUnitTemplate, models.Unit{
Id: utils.GenUUID(),
UnitName: cosUnitTemplate.UnitName,
Symbol: cosUnitTemplate.Symbol,
})
}
}
rows, err := m.dbClient.BatchUpsertUnitTemplate(upsertUnitTemplate)
if err != nil {
return 0, err
}
return rows, nil
}

View File

@ -0,0 +1,257 @@
package userapp
import (
"context"
"github.com/dgrijalva/jwt-go"
"github.com/winc-link/hummingbird/internal/dtos"
"github.com/winc-link/hummingbird/internal/hummingbird/core/container"
interfaces "github.com/winc-link/hummingbird/internal/hummingbird/core/interface"
"github.com/winc-link/hummingbird/internal/models"
pkgcontainer "github.com/winc-link/hummingbird/internal/pkg/container"
"github.com/winc-link/hummingbird/internal/pkg/di"
"github.com/winc-link/hummingbird/internal/pkg/errort"
"github.com/winc-link/hummingbird/internal/pkg/logger"
"github.com/winc-link/hummingbird/internal/pkg/middleware"
"time"
//"gitlab.com/tedge/edgex/internal/pkg/container"
//resourceContainer "gitlab.com/tedge/edgex/internal/tedge/resource/container"
//
//"gitlab.com/tedge/edgex/internal/pkg/di"
//"gitlab.com/tedge/edgex/internal/pkg/errort"
//"gitlab.com/tedge/edgex/internal/pkg/logger"
//
jwt2 "github.com/winc-link/hummingbird/internal/tools/jwt"
//
//"github.com/dgrijalva/jwt-go"
//"gitlab.com/tedge/edgex/internal/dtos"
//"gitlab.com/tedge/edgex/internal/models"
//"gitlab.com/tedge/edgex/internal/pkg/middleware"
//"gitlab.com/tedge/edgex/internal/tedge/resource/interfaces"
"golang.org/x/crypto/bcrypt"
)
const (
DefaultUserName = "admin"
DefaultLang = "en"
)
var _ interfaces.UserItf = new(userApp)
type userApp struct {
dic *di.Container
dbClient interfaces.DBClient
lc logger.LoggingClient
}
func New(dic *di.Container) *userApp {
return &userApp{
dic: dic,
lc: pkgcontainer.LoggingClientFrom(dic.Get),
dbClient: container.DBClientFrom(dic.Get),
}
}
//UserLogin 用户登录
func (uapp *userApp) UserLogin(ctx context.Context, req dtos.LoginRequest) (res dtos.LoginResponse, err error) {
// 从数据库用户信息
user, edgeXErr := uapp.dbClient.GetUserByUserName(req.Username)
if edgeXErr != nil {
return res, errort.NewCommonEdgeX(errort.AppPasswordError, "", edgeXErr)
}
// 校验密码
cErr := bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(req.Password))
if cErr != nil {
err = errort.NewCommonErr(errort.AppPasswordError, cErr)
return
}
j := jwt2.NewJWT(jwt2.JwtSignKey)
claims := middleware.CustomClaims{
ID: 1,
Username: req.Username,
StandardClaims: jwt.StandardClaims{
NotBefore: time.Now().Unix() - 1000, // 签名生效时间
ExpiresAt: time.Now().Unix() + 60*60*24*3, // 过期时间 7天
Issuer: jwt2.JwtIssuer, // 签名的发行者
},
}
token, jwtErr := j.CreateToken(claims)
if jwtErr != nil {
err = jwtErr
return
}
lang := user.Lang
if lang == "" {
lang = DefaultLang
}
res = dtos.LoginResponse{
User: dtos.UserResponse{
Username: user.Username,
Lang: lang,
},
ExpiresAt: claims.StandardClaims.ExpiresAt * 1000,
Token: token,
}
return
}
//InitInfo 查询用户信息
func (uapp *userApp) InitInfo() (res dtos.InitInfoResponse, err error) {
// 从数据库用户信息
_, edgeXErr := uapp.dbClient.GetUserByUserName(DefaultUserName)
if edgeXErr != nil {
//if errort.NewCommonEdgeXWrapper(edgeXErr).Code() == {
if errort.Is(errort.DefaultResourcesNotFound, edgeXErr) {
res.IsInit = false
return
}
return
}
res.IsInit = true
return
}
// InitPassword 初始化密码
func (uapp *userApp) InitPassword(ctx context.Context, req dtos.InitPasswordRequest) error {
lc := uapp.lc
// 从数据库用户信息
_, edgeXErr := uapp.dbClient.GetUserByUserName(DefaultUserName)
if edgeXErr == nil {
return errort.NewCommonErr(errort.AppSystemInitialized, edgeXErr)
}
// 生成新密码并存储
newPasswordHash, err := bcrypt.GenerateFromPassword([]byte(req.NewPassword), bcrypt.DefaultCost)
if err != nil {
return err
}
newUser := models.User{
Username: DefaultUserName,
Password: string(newPasswordHash),
Lang: DefaultLang,
OpenAPIKey: jwt2.GenerateJwtSignKey(),
GatewayKey: jwt2.GenerateJwtSignKey(),
}
jwt2.SetOpenAPIKey(newUser.OpenAPIKey)
jwt2.SetJwtSignKey(newUser.GatewayKey)
//db操作存储
_, edgeXErr = uapp.dbClient.AddUser(newUser)
if edgeXErr != nil {
lc.Errorf("add user error %v", edgeXErr)
return edgeXErr
}
return nil
}
// UpdateUserPassword 修改密码
func (uapp *userApp) UpdateUserPassword(ctx context.Context, username string, req dtos.UpdatePasswordRequest) error {
// 从数据库用户信息
user, edgeXErr := uapp.dbClient.GetUserByUserName(username)
if edgeXErr != nil {
return edgeXErr
}
err := bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(req.CurrentPassword))
if err != nil {
return err
}
// 生成新密码并存储
newPasswordHash, gErr := bcrypt.GenerateFromPassword([]byte(req.NewPassword), bcrypt.DefaultCost)
if gErr != nil {
return err
}
user.Password = string(newPasswordHash)
//db操作存储
edgeXErr = uapp.dbClient.UpdateUser(user)
if edgeXErr != nil {
return edgeXErr
}
return nil
}
//OpenApiUserLogin openapi用户登录
func (uapp *userApp) OpenApiUserLogin(ctx context.Context, req dtos.LoginRequest) (res *dtos.TokenDetail, err error) {
// 从数据库用户信息
user, edgeXErr := uapp.dbClient.GetUserByUserName(req.Username)
if edgeXErr != nil {
return res, errort.NewCommonErr(errort.AppPasswordError, edgeXErr)
}
// 校验密码
cErr := bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(req.Password))
if cErr != nil {
err = errort.NewCommonErr(errort.AppPasswordError, cErr)
return
}
td, err := uapp.CreateTokenDetail(req.Username)
if err != nil {
return
}
return td, nil
}
// CreateTokenDetail 根据用户名生成 token
func (uapp *userApp) CreateTokenDetail(userName string) (*dtos.TokenDetail, error) {
td := &dtos.TokenDetail{
AccessId: "accessId",
RefreshId: "refreshId",
AtExpires: time.Now().Add(time.Minute * 120).Unix(), //两小时
RtExpires: time.Now().Add(time.Hour * 24 * 14).Unix(), //两星期
}
var (
userID uint = 1
err error
)
td.AccessToken, err = uapp.createToken(userID, userName, td.AtExpires, jwt2.OpenAPIKey)
if err != nil {
return nil, err
}
td.RefreshToken, err = uapp.createToken(userID, userName, td.RtExpires, jwt2.RefreshKey)
if err != nil {
return nil, err
}
return td, nil
}
// CreateToken 生成 token
func (uapp *userApp) createToken(useId uint, userName string, expire int64, signKey string) (string, error) {
j := jwt2.NewJWT(signKey)
claims := middleware.CustomClaims{
ID: useId,
Username: userName,
StandardClaims: jwt.StandardClaims{
NotBefore: time.Now().Unix(), // 签名生效时间
ExpiresAt: expire,
Issuer: jwt2.JwtIssuer, // 签名的发行者
},
}
token, jwtErr := j.CreateToken(claims)
if jwtErr != nil {
err := jwtErr
return "", err
}
return token, nil
}
func (uapp *userApp) InitJwtKey() {
user, err := uapp.dbClient.GetUserByUserName(DefaultUserName)
if err != nil {
return
}
if user.GatewayKey == "" {
user.GatewayKey = jwt2.GenerateJwtSignKey()
}
if user.OpenAPIKey == "" {
user.OpenAPIKey = jwt2.GenerateJwtSignKey()
}
if err = uapp.dbClient.UpdateUser(user); err != nil {
panic(err)
}
jwt2.SetOpenAPIKey(user.OpenAPIKey)
jwt2.SetJwtSignKey(user.GatewayKey)
}

View File

@ -0,0 +1,127 @@
//
// Copyright (C) 2020 IOTech Ltd
//
// SPDX-License-Identifier: Apache-2.0
package database
import (
"context"
"errors"
"github.com/winc-link/hummingbird/internal/dtos"
"github.com/winc-link/hummingbird/internal/hummingbird/core/config"
"github.com/winc-link/hummingbird/internal/hummingbird/core/container"
"github.com/winc-link/hummingbird/internal/hummingbird/core/infrastructure/mysql"
"github.com/winc-link/hummingbird/internal/hummingbird/core/infrastructure/sqlite"
"github.com/winc-link/hummingbird/internal/pkg/constants"
"github.com/winc-link/hummingbird/internal/tools/datadb/tdengine"
"github.com/winc-link/hummingbird/internal/pkg/di"
"github.com/winc-link/hummingbird/internal/pkg/logger"
"github.com/winc-link/hummingbird/internal/pkg/startup"
"github.com/winc-link/hummingbird/internal/tools/datadb/leveldb"
"sync"
interfaces "github.com/winc-link/hummingbird/internal/hummingbird/core/interface"
pkgContainer "github.com/winc-link/hummingbird/internal/pkg/container"
)
// Database contains references to dependencies required by the database bootstrap implementation.
type Database struct {
database *config.ConfigurationStruct
}
// NewDatabase is a factory method that returns an initialized Database receiver struct.
func NewDatabase(database *config.ConfigurationStruct) Database {
return Database{
database: database,
}
}
//Return the dbClient interfaces
func (d Database) newDBClient(
lc logger.LoggingClient) (interfaces.DBClient, error) {
databaseInfo := d.database.GetDatabaseInfo()["Primary"]
switch databaseInfo.Type {
case string(constants.MySQL):
return mysql.NewClient(dtos.Configuration{
Dsn: databaseInfo.Dsn,
}, lc)
case string(constants.SQLite):
return sqlite.NewClient(dtos.Configuration{
Username: databaseInfo.Username,
Password: databaseInfo.Password,
DataSource: databaseInfo.DataSource,
}, lc)
default:
panic(errors.New("database configuration error"))
}
}
func (d Database) newDataDBClient(
lc logger.LoggingClient) (interfaces.DataDBClient, error) {
dataDbInfo := d.database.GetDataDatabaseInfo()["Primary"]
switch dataDbInfo.Type {
case string(constants.LevelDB):
return leveldb.NewClient(dtos.Configuration{
DataSource: dataDbInfo.DataSource,
}, lc)
case string(constants.TDengine):
return tdengine.NewClient(dtos.Configuration{
Dsn: dataDbInfo.Dsn,
}, lc)
default:
panic(errors.New("database configuration error"))
}
}
// BootstrapHandler fulfills the BootstrapHandler contract and initializes the database.
func (d Database) BootstrapHandler(
ctx context.Context,
wg *sync.WaitGroup,
startupTimer startup.Timer,
dic *di.Container) bool {
lc := pkgContainer.LoggingClientFrom(dic.Get)
// initialize Metadata db.
dbClient, err := d.newDBClient(lc)
if err != nil {
panic(err)
}
dic.Update(di.ServiceConstructorMap{
container.DBClientInterfaceName: func(get di.Get) interface{} {
return dbClient
},
})
// initialize Data db.
dataDbClient, err := d.newDataDBClient(lc)
if err != nil {
panic(err)
}
dic.Update(di.ServiceConstructorMap{
container.DataDBClientInterfaceName: func(get di.Get) interface{} {
return dataDbClient
},
})
lc.Info("DatabaseInfo connected")
wg.Add(1)
go func() {
defer wg.Done()
select {
case <-ctx.Done():
interfaces.DMIFrom(di.GContainer.Get).StopAllInstance() //stop all instance
container.DBClientFrom(di.GContainer.Get).CloseSession()
container.DataDBClientFrom(di.GContainer.Get).CloseSession()
lc.Info("DatabaseInfo disconnected")
}
}()
return true
}

View File

@ -0,0 +1,204 @@
/*******************************************************************************
* Copyright 2023 Winc link Inc.
*
* 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 config
import (
"fmt"
"go.uber.org/atomic"
bootstrapConfig "github.com/winc-link/hummingbird/internal/pkg/config"
)
var (
DefaultEnv = "pro"
)
// Struct used to parse the JSON configuration file
type ConfigurationStruct struct {
Writable WritableInfo
MessageQueue MessageQueueInfo
Clients map[string]bootstrapConfig.ClientInfo
Databases map[string]map[string]bootstrapConfig.Database
Registry bootstrapConfig.RegistryInfo
Service bootstrapConfig.ServiceInfo
RpcServer bootstrapConfig.RPCServiceInfo
SecretStore bootstrapConfig.SecretStoreInfo
WebServer bootstrapConfig.ServiceInfo
DockerManage DockerManage
ApplicationSettings ApplicationSettings
Topics struct {
CommandTopic TopicInfo
}
}
type WritableInfo struct {
PersistData atomic.Bool `toml:"-"`
PersistPeriod atomic.Int32 `toml:"-"`
LogLevel string
LogPath string
InsecureSecrets bootstrapConfig.InsecureSecrets
DebugProfile bool
IsNewModel bool
LimitMethods []string
}
type TopicInfo struct {
Topic string
}
type DockerManage struct {
ContainerConfigPath string
HostRootDir string
DockerApiVersion string
Privileged bool
}
// MessageQueueInfo provides parameters related to connecting to a message bus
type MessageQueueInfo struct {
// Host is the hostname or IP address of the broker, if applicable.
Host string
// Port defines the port on which to access the message queue.
Port int
// Protocol indicates the protocol to use when accessing the message queue.
Protocol string
// Indicates the message queue platform being used.
Type string
// Indicates the topic the data is published/subscribed
SubscribeTopics []string
// Indicates the topic prefix the data is published to. Note that /<device-profile-name>/<device-name> will be
// added to this Publish Topic prefix as the complete publish topic
PublishTopicPrefix string
// Provides additional configuration properties which do not fit within the existing field.
// Typically the key is the name of the configuration property and the value is a string representation of the
// desired value for the configuration property.
Optional map[string]string
}
func (m MessageQueueInfo) Enable() bool {
return !(m.Host == "" || m.Port <= 0)
}
type ApplicationSettings struct {
DeviceIds string
ResDir string
DriverDir string
IsScreenControl bool
OTADir string
CloseAuthToken bool
GatewayEdition string // 网关版本标识
WebBuildPath string // 前端路径
TedgeNumber string
}
// URL constructs a URL from the protocol, host and port and returns that as a string.
func (m MessageQueueInfo) URL() string {
return fmt.Sprintf("%s://%s:%v", m.Protocol, m.Host, m.Port)
}
// UpdateFromRaw converts configuration received from the registry to a service-specific configuration struct which is
// then used to overwrite the service's existing configuration struct.
func (c *ConfigurationStruct) UpdateFromRaw(rawConfig interface{}) bool {
configuration, ok := rawConfig.(*ConfigurationStruct)
if ok {
// Check that information was successfully read from Registry
if configuration.Service.Port == 0 {
return false
}
*c = *configuration
}
return ok
}
// EmptyWritablePtr returns a pointer to a service-specific empty WritableInfo struct. It is used by the bootstrap to
// provide the appropriate structure to registry.C's WatchForChanges().
func (c *ConfigurationStruct) EmptyWritablePtr() interface{} {
return &WritableInfo{}
}
// UpdateWritableFromRaw converts configuration received from the registry to a service-specific WritableInfo struct
// which is then used to overwrite the service's existing configuration's WritableInfo struct.
func (c *ConfigurationStruct) UpdateWritableFromRaw(rawWritable interface{}) bool {
writable, ok := rawWritable.(*WritableInfo)
if ok {
c.Writable = *writable
}
return ok
}
// GetBootstrap returns the configuration elements required by the bootstrap. Currently, a copy of the configuration
// data is returned. This is intended to be temporary -- since ConfigurationStruct drives the configuration.toml's
// structure -- until we can make backwards-breaking configuration.toml changes (which would consolidate these fields
// into an bootstrapConfig.BootstrapConfiguration struct contained within ConfigurationStruct).
func (c *ConfigurationStruct) GetBootstrap() bootstrapConfig.BootstrapConfiguration {
// temporary until we can make backwards-breaking configuration.toml change
return bootstrapConfig.BootstrapConfiguration{
Clients: c.Clients,
Service: c.Service,
RpcServer: c.RpcServer,
Registry: c.Registry,
SecretStore: c.SecretStore,
}
}
// GetLogLevel returns the current ConfigurationStruct's log level.
func (c *ConfigurationStruct) GetLogLevel() string {
return c.Writable.LogLevel
}
func (c *ConfigurationStruct) GetLogPath() string {
return c.Writable.LogPath
}
// GetRegistryInfo returns the RegistryInfo from the ConfigurationStruct.
func (c *ConfigurationStruct) GetRegistryInfo() bootstrapConfig.RegistryInfo {
return c.Registry
}
// GetDatabaseInfo returns a database information map.
func (c *ConfigurationStruct) GetDatabaseInfo() map[string]bootstrapConfig.Database {
cfg := c.Databases["Metadata"]
return cfg
}
// GetDataDatabaseInfo returns a database information map for events & readings.
func (c *ConfigurationStruct) GetDataDatabaseInfo() map[string]bootstrapConfig.Database {
cfg := c.Databases["Data"]
return cfg
}
// GetDataDatabaseInfo returns a database information map for events & readings.
func (c *ConfigurationStruct) GetRedisInfo() map[string]bootstrapConfig.Database {
cfg := c.Databases["Redis"]
return cfg
}
// GetInsecureSecrets returns the service's InsecureSecrets.
func (c *ConfigurationStruct) GetInsecureSecrets() bootstrapConfig.InsecureSecrets {
return c.Writable.InsecureSecrets
}
// 判断是否为物模型
func (c *ConfigurationStruct) IsThingModel() bool {
return c.Writable.IsNewModel
}
func (c *ConfigurationStruct) GetPersistData() bool {
return c.Writable.PersistData.Load()
}
func (c *ConfigurationStruct) GetPersisPeriod() int32 {
return c.Writable.PersistPeriod.Load()
}

View File

@ -0,0 +1,12 @@
package container
import (
"github.com/winc-link/hummingbird/internal/pkg/di"
"github.com/winc-link/hummingbird/internal/tools/agentclient"
)
var AgentClientName = di.TypeInstanceToName((*agentclient.AgentClient)(nil))
func AgentClientNameFrom(get di.Get) agentclient.AgentClient {
return get(AgentClientName).(agentclient.AgentClient)
}

View File

@ -0,0 +1,36 @@
/*******************************************************************************
* Copyright 2017.
*
* 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 container
import (
interfaces "github.com/winc-link/hummingbird/internal/hummingbird/core/interface"
"github.com/winc-link/hummingbird/internal/pkg/di"
)
var (
AlertRuleAppName = di.TypeInstanceToName((*interfaces.AlertRuleApp)(nil))
)
func AlertRuleAppNameFrom(get di.Get) interfaces.AlertRuleApp {
return get(AlertRuleAppName).(interfaces.AlertRuleApp)
}
var (
RuleEngineAppName = di.TypeInstanceToName((*interfaces.RuleEngineApp)(nil))
)
func RuleEngineAppNameFrom(get di.Get) interfaces.RuleEngineApp {
return get(RuleEngineAppName).(interfaces.RuleEngineApp)
}

View File

@ -0,0 +1,60 @@
/*******************************************************************************
* Copyright 2017 Dell Inc.
* Copyright (c) 2019 Intel Corporation
*
* 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 container
import (
interfaces "github.com/winc-link/hummingbird/internal/hummingbird/core/interface"
"github.com/winc-link/hummingbird/internal/pkg/di"
)
var (
CategoryTemplateAppName = di.TypeInstanceToName((*interfaces.CategoryApp)(nil))
)
func CategoryTemplateAppFrom(get di.Get) interfaces.CategoryApp {
return get(CategoryTemplateAppName).(interfaces.CategoryApp)
}
var (
ThingModelTemplateAppName = di.TypeInstanceToName((*interfaces.ThingModelTemplateApp)(nil))
)
func ThingModelTemplateAppFrom(get di.Get) interfaces.ThingModelTemplateApp {
return get(ThingModelTemplateAppName).(interfaces.ThingModelTemplateApp)
}
var (
UnitTemplateAppName = di.TypeInstanceToName((*interfaces.UnitApp)(nil))
)
func UnitTemplateAppFrom(get di.Get) interfaces.UnitApp {
return get(UnitTemplateAppName).(interfaces.UnitApp)
}
var (
DocsAppName = di.TypeInstanceToName((*interfaces.DocsApp)(nil))
)
func DocsTemplateAppFrom(get di.Get) interfaces.DocsApp {
return get(DocsAppName).(interfaces.DocsApp)
}
var (
QuickNavigationAppName = di.TypeInstanceToName((*interfaces.QuickNavigation)(nil))
)
func QuickNavigationAppTemplateAppFrom(get di.Get) interfaces.QuickNavigation {
return get(QuickNavigationAppName).(interfaces.QuickNavigation)
}

View File

@ -0,0 +1,28 @@
/*******************************************************************************
* Copyright 2019 Dell Inc.
*
* 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 container
import (
"github.com/winc-link/hummingbird/internal/hummingbird/core/config"
"github.com/winc-link/hummingbird/internal/pkg/di"
)
// ConfigurationName contains the name of the resource's config.ConfigurationStruct implementation in the DIC.
var ConfigurationName = di.TypeInstanceToName((*config.ConfigurationStruct)(nil))
// ConfigurationFrom helper function queries the DIC and returns resource's config.ConfigurationStruct implementation.
func ConfigurationFrom(get di.Get) *config.ConfigurationStruct {
return get(ConfigurationName).(*config.ConfigurationStruct)
}

View File

@ -0,0 +1,28 @@
/*******************************************************************************
* Copyright 2017 Dell Inc.
* Copyright (c) 2019 Intel Corporation
*
* 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 container
import (
interfaces "github.com/winc-link/hummingbird/internal/hummingbird/core/interface"
"github.com/winc-link/hummingbird/internal/pkg/di"
)
var (
CosAppName = di.TypeInstanceToName((*interfaces.CosApp)(nil))
)
func CosAppNameFrom(get di.Get) interfaces.CosApp {
return get(CosAppName).(interfaces.CosApp)
}

View File

@ -0,0 +1,19 @@
//
// Copyright (C) 2020 IOTech Ltd
//
// SPDX-License-Identifier: Apache-2.0
package container
import (
interfaces "github.com/winc-link/hummingbird/internal/hummingbird/core/interface"
"github.com/winc-link/hummingbird/internal/pkg/di"
)
// DBClientInterfaceName contains the name of the interfaces.DBClient implementation in the DIC.
var DBClientInterfaceName = di.TypeInstanceToName((*interfaces.DBClient)(nil))
// DBClientFrom helper function queries the DIC and returns the interfaces.DBClient implementation.
func DBClientFrom(get di.Get) interfaces.DBClient {
return get(DBClientInterfaceName).(interfaces.DBClient)
}

View File

@ -0,0 +1,12 @@
package container
import (
interfaces "github.com/winc-link/hummingbird/internal/hummingbird/core/interface"
"github.com/winc-link/hummingbird/internal/pkg/di"
)
var DataDBClientInterfaceName = di.TypeInstanceToName((*interfaces.DataDBClient)(nil))
func DataDBClientFrom(get di.Get) interfaces.DataDBClient {
return get(DataDBClientInterfaceName).(interfaces.DataDBClient)
}

View File

@ -0,0 +1,26 @@
/*******************************************************************************
* Copyright 2017.
*
* 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 container
import (
interfaces "github.com/winc-link/hummingbird/internal/hummingbird/core/interface"
"github.com/winc-link/hummingbird/internal/pkg/di"
)
var DataResourceName = di.TypeInstanceToName((*interfaces.DataResourceApp)(nil))
func DataResourceFrom(get di.Get) interfaces.DataResourceApp {
return get(DataResourceName).(interfaces.DataResourceApp)
}

View File

@ -0,0 +1,14 @@
package container
import (
interfaces "github.com/winc-link/hummingbird/internal/hummingbird/core/interface"
"github.com/winc-link/hummingbird/internal/pkg/di"
)
// DeviceItfName
var DeviceItfName = di.TypeInstanceToName((*interfaces.DeviceItf)(nil))
// DeviceItfFrom
func DeviceItfFrom(get di.Get) interfaces.DeviceItf {
return get(DeviceItfName).(interfaces.DeviceItf)
}

View File

@ -0,0 +1,23 @@
package container
import (
interfaces "github.com/winc-link/hummingbird/internal/hummingbird/core/interface"
"github.com/winc-link/hummingbird/internal/pkg/di"
)
// DI
var (
DriverAppName = di.TypeInstanceToName((*interfaces.DriverLibApp)(nil))
)
func DriverAppFrom(get di.Get) interfaces.DriverLibApp {
return get(DriverAppName).(interfaces.DriverLibApp)
}
var (
DriverServiceAppName = di.TypeInstanceToName((*interfaces.DriverServiceApp)(nil))
)
func DriverServiceAppFrom(get di.Get) interfaces.DriverServiceApp {
return get(DriverServiceAppName).(interfaces.DriverServiceApp)
}

View File

@ -0,0 +1,29 @@
/*******************************************************************************
* Copyright 2017.
*
* 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 container
import (
"github.com/winc-link/hummingbird/internal/pkg/di"
"github.com/winc-link/hummingbird/internal/tools/ekuiperclient"
//"github.com/winc-link/hummingbird/internal/tools/ekuiperclient"
)
var (
EkuiperAppName = di.TypeInstanceToName((*ekuiperclient.EkuiperClient)(nil))
)
func EkuiperAppFrom(get di.Get) ekuiperclient.EkuiperClient {
return get(EkuiperAppName).(ekuiperclient.EkuiperClient)
}

View File

@ -0,0 +1,25 @@
/*******************************************************************************
* 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 container
import (
interfaces "github.com/winc-link/hummingbird/internal/hummingbird/core/interface"
"github.com/winc-link/hummingbird/internal/pkg/di"
)
var (
HomePageAppName = di.TypeInstanceToName((*interfaces.HomePageItf)(nil))
)
func HomePageAppNameFrom(get di.Get) interfaces.HomePageItf {
return get(HomePageAppName).(interfaces.HomePageItf)
}

View File

@ -0,0 +1,29 @@
/*******************************************************************************
* Copyright 2017 Dell Inc.
* Copyright (c) 2019 Intel Corporation
*
* 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 container
import (
//interfaces "github.com/winc-link/hummingbird/internal/hummingbird/core/interface"
"github.com/winc-link/hummingbird/internal/pkg/di"
"github.com/winc-link/hummingbird/internal/tools/hpcloudclient"
)
var (
HpcServiceAppName = di.TypeInstanceToName((*hpcloudclient.Hpcloud)(nil))
)
func HpcServiceAppFrom(get di.Get) hpcloudclient.Hpcloud {
return get(HpcServiceAppName).(hpcloudclient.Hpcloud)
}

View File

@ -0,0 +1,28 @@
/*******************************************************************************
* Copyright 2017.
*
* 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 container
import (
interfaces "github.com/winc-link/hummingbird/internal/hummingbird/core/interface"
"github.com/winc-link/hummingbird/internal/pkg/di"
)
var (
LanguageSDKAppName = di.TypeInstanceToName((*interfaces.LanguageSDKApp)(nil))
)
func LanguageAppNameFrom(get di.Get) interfaces.LanguageSDKApp {
return get(LanguageSDKAppName).(interfaces.LanguageSDKApp)
}

View File

@ -0,0 +1,18 @@
package container
import (
interfaces "github.com/winc-link/hummingbird/internal/hummingbird/core/interface"
"github.com/winc-link/hummingbird/internal/pkg/di"
)
var MessageItfName = di.TypeInstanceToName((*interfaces.MessageItf)(nil))
func MessageItfFrom(get di.Get) interfaces.MessageItf {
return get(MessageItfName).(interfaces.MessageItf)
}
var MessageStoreItfName = di.TypeInstanceToName((*interfaces.MessageStores)(nil))
func MessageStoreItfFrom(get di.Get) interfaces.MessageStores {
return get(MessageStoreItfName).(interfaces.MessageStores)
}

View File

@ -0,0 +1,28 @@
/*******************************************************************************
* Copyright 2017.
*
* 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 container
import (
interfaces "github.com/winc-link/hummingbird/internal/hummingbird/core/interface"
"github.com/winc-link/hummingbird/internal/pkg/di"
)
var (
MonitorAppName = di.TypeInstanceToName((*interfaces.MonitorItf)(nil))
)
func MonitorAppNameFrom(get di.Get) interfaces.MonitorItf {
return get(MonitorAppName).(interfaces.MonitorItf)
}

View File

@ -0,0 +1,28 @@
/*******************************************************************************
* Copyright 2017.
*
* 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 container
import (
"github.com/winc-link/hummingbird/internal/pkg/di"
"github.com/winc-link/hummingbird/internal/tools/notify/sms"
)
var (
SmsServiceAppName = di.TypeInstanceToName((*sms.SMSer)(nil))
)
func SmsServiceAppFrom(get di.Get) sms.SMSer {
return get(SmsServiceAppName).(sms.SMSer)
}

View File

@ -0,0 +1,25 @@
/*******************************************************************************
* Copyright 2017 Dell Inc.
*
* 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 container
import (
interfaces "github.com/winc-link/hummingbird/internal/hummingbird/core/interface"
"github.com/winc-link/hummingbird/internal/pkg/di"
)
var PersistItfName = di.TypeInstanceToName((*interfaces.PersistItf)(nil))
func PersistItfFrom(get di.Get) interfaces.PersistItf {
return get(PersistItfName).(interfaces.PersistItf)
}

View File

@ -0,0 +1,28 @@
/*******************************************************************************
* Copyright 2017 Dell Inc.
* Copyright (c) 2019 Intel Corporation
*
* 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 container
import (
interfaces "github.com/winc-link/hummingbird/internal/hummingbird/core/interface"
"github.com/winc-link/hummingbird/internal/pkg/di"
)
var (
ProductAppName = di.TypeInstanceToName((*interfaces.ProductItf)(nil))
)
func ProductAppNameFrom(get di.Get) interfaces.ProductItf {
return get(ProductAppName).(interfaces.ProductItf)
}

View File

@ -0,0 +1,36 @@
/*******************************************************************************
* Copyright 2017.
*
* 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 container
import (
interfaces "github.com/winc-link/hummingbird/internal/hummingbird/core/interface"
"github.com/winc-link/hummingbird/internal/pkg/di"
)
var (
SceneAppName = di.TypeInstanceToName((*interfaces.SceneApp)(nil))
)
func SceneAppNameFrom(get di.Get) interfaces.SceneApp {
return get(SceneAppName).(interfaces.SceneApp)
}
var (
ConJobAppName = di.TypeInstanceToName((*interfaces.ConJob)(nil))
)
func ConJobAppNameFrom(get di.Get) interfaces.ConJob {
return get(ConJobAppName).(interfaces.ConJob)
}

View File

@ -0,0 +1,16 @@
package container
import (
//"gitlab.com/tedge/edgex/internal/pkg/di"
//"gitlab.com/tedge/edgex/internal/tedge/resource/interfaces"
interfaces "github.com/winc-link/hummingbird/internal/hummingbird/core/interface"
"github.com/winc-link/hummingbird/internal/pkg/di"
)
// SystemItfName
var SystemItfName = di.TypeInstanceToName((*interfaces.SystemItf)(nil))
// SystemItfFrom
func SystemItfFrom(get di.Get) interfaces.SystemItf {
return get(SystemItfName).(interfaces.SystemItf)
}

View File

@ -0,0 +1,28 @@
/*******************************************************************************
* Copyright 2017 Dell Inc.
* Copyright (c) 2019 Intel Corporation
*
* 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 container
import (
interfaces "github.com/winc-link/hummingbird/internal/hummingbird/core/interface"
"github.com/winc-link/hummingbird/internal/pkg/di"
)
var (
ThingModelAppName = di.TypeInstanceToName((*interfaces.ThingModelItf)(nil))
)
func ThingModelAppNameFrom(get di.Get) interfaces.ThingModelItf {
return get(ThingModelAppName).(interfaces.ThingModelItf)
}

View File

@ -0,0 +1,14 @@
package container
import (
interfaces "github.com/winc-link/hummingbird/internal/hummingbird/core/interface"
"github.com/winc-link/hummingbird/internal/pkg/di"
)
// UserItfName
var UserItfName = di.TypeInstanceToName((*interfaces.UserItf)(nil))
// UserItfFrom
func UserItfFrom(get di.Get) interfaces.UserItf {
return get(UserItfName).(interfaces.UserItf)
}

View File

@ -0,0 +1,57 @@
/*******************************************************************************
* Copyright 2017.
*
* 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 gateway
import (
"github.com/gin-gonic/gin"
"github.com/winc-link/hummingbird/internal/dtos"
"github.com/winc-link/hummingbird/internal/pkg/errort"
"github.com/winc-link/hummingbird/internal/pkg/httphelper"
)
// @Tags 运维管理
// @Summary 获取系统性能
// @Produce json
// @Param request query dtos.SystemMetricsQuery true "参数"
// @Success 200 {object} dtos.SystemMetricsResponse
// @Router /api/v1/metrics/system [get]
//func (ctl *controller) GetSystemMetricsHandle(c *gin.Context) {
// ctl.ProxyAgentServer(c)
//}
func (c *controller) SystemMetricsHandler(ctx *gin.Context) {
var query = dtos.SystemMetricsQuery{}
if err := ctx.BindQuery(&query); err != nil {
httphelper.RenderFail(ctx, errort.NewCommonErr(errort.DefaultReqParamsError, err), ctx.Writer, c.lc)
return
}
metrics, err := c.getSystemMonitorApp().GetSystemMetrics(ctx, query)
if err != nil {
httphelper.RenderFail(ctx, err, ctx.Writer, c.lc)
return
}
httphelper.ResultSuccess(metrics, ctx.Writer, c.lc)
}
// @Tags 运维管理
// @Summary 操作服务重启
// @Produce json
// @Param request body dtos.Operation true "操作"
// @Success 200 {object} httphelper.CommonResponse
// @Router /api/v1/operation [post]
//func (ctl *controller) OperationServiceHandle(c *gin.Context) {
// ctl.ProxyAgentServer(c)
//}

View File

@ -0,0 +1,60 @@
/*******************************************************************************
* Copyright 2017 Dell Inc.
* Copyright (c) 2019 Intel Corporation
*
* 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 gateway
import (
"github.com/gin-gonic/gin"
"github.com/winc-link/hummingbird/internal/dtos"
"github.com/winc-link/hummingbird/internal/pkg/httphelper"
)
// @Tags 标准产品品类
// @Summary 标准产品品类列表
// @Produce json
// @Param request query dtos.CategoryTemplateRequest true "参数"
// @Success 200 {array} dtos.CategoryTemplateResponse
// @Router /api/v1/category-template [get]
//@Security ApiKeyAuth
func (ctl *controller) CategoryTemplateSearch(c *gin.Context) {
lc := ctl.lc
var req dtos.CategoryTemplateRequest
urlDecodeParam(&req, c.Request, lc)
dtos.CorrectionPageParam(&req.BaseSearchConditionQuery)
data, total, edgeXErr := ctl.getCategoryTemplateApp().CategoryTemplateSearch(c, req)
if edgeXErr != nil {
httphelper.RenderFail(c, edgeXErr, c.Writer, lc)
return
}
pageResult := httphelper.NewPageResult(data, total, req.Page, req.PageSize)
httphelper.ResultSuccess(pageResult, c.Writer, lc)
}
// @Tags 标准产品品类
// @Summary 同步标准产品品类
// @Produce json
// @Param request query dtos.CategoryTemplateRequest true "参数"
// @Router /api/v1/category-template/sync [post]
//@Security ApiKeyAuth
func (ctl *controller) CategoryTemplateSync(c *gin.Context) {
lc := ctl.lc
var req dtos.CategoryTemplateSyncRequest
urlDecodeParam(&req, c.Request, lc)
_, edgeXErr := ctl.getCategoryTemplateApp().Sync(c, "Ireland")
if edgeXErr != nil {
httphelper.RenderFail(c, edgeXErr, c.Writer, lc)
return
}
httphelper.ResultSuccess(nil, c.Writer, lc)
}

View File

@ -0,0 +1,39 @@
/*******************************************************************************
* Copyright 2017 Dell Inc.
*
* 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 gateway
import (
"github.com/gin-gonic/gin"
"github.com/winc-link/hummingbird/internal/pkg/httphelper"
)
// @Tags 我的云服务实例
// @Summary 云服务实例列表
// @Produce json
// @Param request query dtos.CloudInstanceSearchQueryRequest true "参数"
// @Success 200 {object} dtos.CloudInstanceSearchQueryRequest
// @Router /api/v1/cloud-instance [get]
//@Security ApiKeyAuth
func (ctl *controller) CloudInstanceSearch(c *gin.Context) {
lc := ctl.lc
//var req dtos.CloudInstanceSearchQueryRequest
//urlDecodeParam(&req, c.Request, lc)
//dtos.CorrectionPageParam(&req.BaseSearchConditionQuery)
//data, total := 1,0
data := make([]string, 0)
total := 0
pageResult := httphelper.NewPageResult(data, uint32(total), 1, 10)
httphelper.ResultSuccess(pageResult, c.Writer, lc)
}

View File

@ -0,0 +1,68 @@
package gateway
import (
"fmt"
"github.com/gin-gonic/gin"
"github.com/gorilla/schema"
"github.com/winc-link/hummingbird/internal/pkg/errort"
"github.com/winc-link/hummingbird/internal/pkg/httphelper"
"github.com/winc-link/hummingbird/internal/pkg/logger"
"net/http"
"net/http/httputil"
"net/url"
)
const (
UrlParamSceneId = "sceneId"
UrlParamActionId = "actionId"
UrlParamStrategyId = "strategyId"
UrlParamConditionId = "conditionId"
UrlParamJobId = "jobId"
UrlParamProductId = "productId"
UrlParamCategoryKey = "categoryKey"
UrlParamCloudInstanceId = "cloudInstanceId"
UrlParamDeviceId = "deviceId"
UrlParamFuncPointId = "funcPointId"
UrlParamDeviceLibraryId = "deviceLibraryId"
UrlParamDeviceServiceId = "deviceServiceId"
UrlParamDockerConfigId = "dockerConfigId"
UrlParamRuleId = "ruleId"
UrlDataResourceId = "dataResourceId"
RuleEngineId = "ruleEngineId"
)
var decoder *schema.Decoder
func init() {
decoder = schema.NewDecoder()
decoder.IgnoreUnknownKeys(true)
}
func urlDecodeParam(obj interface{}, r *http.Request, lc logger.LoggingClient) {
err := decoder.Decode(obj, r.URL.Query())
if err != nil {
lc.Errorf("url decoding err %v", err)
}
}
func (ctl *controller) ProxyAgentServer(c *gin.Context) {
proxy := ctl.cfg.Clients["Agent"]
ctl.lc.Infof("agentProxy: %v", proxy)
ctl.ServeHTTP(c, fmt.Sprintf("http://%v:%v", proxy.Host, proxy.Port))
}
//func (ctl *controller) ProxySharpServer(c *gin.Context) {
// proxy := ctl.cfg.Clients["Sharp"]
// ctl.lc.Infof("sharpProxy: %v", proxy)
// ctl.ServeHTTP(c, fmt.Sprintf("http://%v:%v", proxy.Host, proxy.Port))
//}
func (ctl *controller) ServeHTTP(c *gin.Context, URL string) {
parseRootUrl, err := url.Parse(URL)
if err != nil {
httphelper.RenderFail(c, errort.NewCommonErr(errort.DefaultReqParamsError, err), c.Writer, ctl.lc)
return
}
proxy := httputil.NewSingleHostReverseProxy(parseRootUrl)
proxy.ServeHTTP(c.Writer, c.Request)
}

View File

@ -0,0 +1,120 @@
/*******************************************************************************
* Copyright 2017 Dell Inc.
* Copyright (c) 2019 Intel Corporation
*
* 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 gateway
import (
"github.com/winc-link/hummingbird/internal/hummingbird/core/config"
"github.com/winc-link/hummingbird/internal/hummingbird/core/container"
interfaces "github.com/winc-link/hummingbird/internal/hummingbird/core/interface"
pkgcontainer "github.com/winc-link/hummingbird/internal/pkg/container"
"github.com/winc-link/hummingbird/internal/pkg/di"
"github.com/winc-link/hummingbird/internal/pkg/logger"
)
type controller struct {
dic *di.Container
lc logger.LoggingClient
cfg *config.ConfigurationStruct
}
func New(dic *di.Container) *controller {
lc := pkgcontainer.LoggingClientFrom(dic.Get)
cfg := container.ConfigurationFrom(dic.Get)
return &controller{
dic: dic,
lc: lc,
cfg: cfg,
}
}
func (ctl *controller) getDriverLibApp() interfaces.DriverLibApp {
return container.DriverAppFrom(ctl.dic.Get)
}
func (ctl *controller) getUserApp() interfaces.UserItf {
return container.UserItfFrom(ctl.dic.Get)
}
func (ctl *controller) getDriverServiceApp() interfaces.DriverServiceApp {
return container.DriverServiceAppFrom(ctl.dic.Get)
}
func (ctl *controller) getSystemMonitorApp() interfaces.MonitorItf {
return container.MonitorAppNameFrom(ctl.dic.Get)
}
func (ctl *controller) getLanguageApp() interfaces.LanguageSDKApp {
return container.LanguageAppNameFrom(ctl.dic.Get)
}
func (ctl *controller) getProductApp() interfaces.ProductItf {
return container.ProductAppNameFrom(ctl.dic.Get)
}
func (ctl *controller) getDeviceApp() interfaces.DeviceItf {
return container.DeviceItfFrom(ctl.dic.Get)
}
func (ctl *controller) getPersistApp() interfaces.PersistItf {
return container.PersistItfFrom(ctl.dic.Get)
}
func (ctl *controller) getCategoryTemplateApp() interfaces.CategoryApp {
return container.CategoryTemplateAppFrom(ctl.dic.Get)
}
func (ctl *controller) getThingModelTemplateApp() interfaces.ThingModelTemplateApp {
return container.ThingModelTemplateAppFrom(ctl.dic.Get)
}
func (ctl *controller) getThingModelApp() interfaces.ThingModelCtlItf {
return container.ThingModelAppNameFrom(ctl.dic.Get)
}
func (ctl *controller) getUnitModelApp() interfaces.UnitApp {
return container.UnitTemplateAppFrom(ctl.dic.Get)
}
func (ctl *controller) getAlertRuleApp() interfaces.AlertRuleApp {
return container.AlertRuleAppNameFrom(ctl.dic.Get)
}
func (ctl *controller) getRuleEngineApp() interfaces.RuleEngineApp {
return container.RuleEngineAppNameFrom(ctl.dic.Get)
}
func (ctl *controller) getHomePageApp() interfaces.HomePageItf {
return container.HomePageAppNameFrom(ctl.dic.Get)
}
func (ctl *controller) getSystemApp() interfaces.SystemItf {
return container.SystemItfFrom(ctl.dic.Get)
}
func (ctl *controller) getDocsApp() interfaces.DocsApp {
return container.DocsTemplateAppFrom(ctl.dic.Get)
}
func (ctl *controller) getQuickNavigationApp() interfaces.QuickNavigation {
return container.QuickNavigationAppTemplateAppFrom(ctl.dic.Get)
}
func (ctl *controller) getDataResourceApp() interfaces.DataResourceApp {
return container.DataResourceFrom(ctl.dic.Get)
}
func (ctl *controller) getSceneApp() interfaces.SceneApp {
return container.SceneAppNameFrom(ctl.dic.Get)
}

View File

@ -0,0 +1,139 @@
/*******************************************************************************
* Copyright 2017.
*
* 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 gateway
import (
"github.com/gin-gonic/gin"
"github.com/winc-link/hummingbird/internal/dtos"
"github.com/winc-link/hummingbird/internal/pkg/errort"
"github.com/winc-link/hummingbird/internal/pkg/httphelper"
)
// @Tags 资源管理
// @Summary 实例类型
// @Produce json
// @Param request query dtos.AddDataResourceReq true "参数"
// @Success 200 {object} httphelper.CommonResponse
// @Router /api/v1/typeresource [get]
func (ctl *controller) DataResourceType(c *gin.Context) {
lc := ctl.lc
types := ctl.getDataResourceApp().DataResourceType(c)
httphelper.ResultSuccess(types, c.Writer, lc)
}
// @Tags 资源管理
// @Summary 添加资源管理
// @Produce json
// @Param request query dtos.AddDataResourceReq true "参数"
// @Success 200 {object} httphelper.CommonResponse
// @Router /api/v1/dataresource [post]
func (ctl *controller) DataResourceAdd(c *gin.Context) {
lc := ctl.lc
var req dtos.AddDataResourceReq
if err := c.ShouldBind(&req); err != nil {
httphelper.RenderFail(c, errort.NewCommonErr(errort.DefaultReqParamsError, err), c.Writer, lc)
return
}
_, edgeXErr := ctl.getDataResourceApp().AddDataResource(c, req)
if edgeXErr != nil {
httphelper.RenderFail(c, edgeXErr, c.Writer, lc)
return
}
httphelper.ResultSuccess(nil, c.Writer, lc)
}
// @Tags 资源管理
// @Summary 添加资源管理
// @Produce json
// @Param request query dtos.AddDataResourceReq true "参数"
// @Success 200 {object} httphelper.CommonResponse
// @Router /api/v1/dataresource/:resourceId [get]
func (ctl *controller) DataResourceById(c *gin.Context) {
lc := ctl.lc
id := c.Param(UrlDataResourceId)
dataSource, edgeXErr := ctl.getDataResourceApp().DataResourceById(c, id)
if edgeXErr != nil {
httphelper.RenderFail(c, edgeXErr, c.Writer, lc)
return
}
httphelper.ResultSuccess(dataSource, c.Writer, lc)
}
// @Tags 资源管理
// @Summary 修改资源管理
// @Produce json
// @Param request query dtos.AddDataResourceReq true "参数"
// @Success 200 {object} httphelper.CommonResponse
// @Router /api/v1/dataresource [put]
func (ctl *controller) UpdateDataResource(c *gin.Context) {
lc := ctl.lc
var req dtos.UpdateDataResource
if err := c.ShouldBind(&req); err != nil {
httphelper.RenderFail(c, errort.NewCommonErr(errort.DefaultReqParamsError, err), c.Writer, lc)
return
}
edgeXErr := ctl.getDataResourceApp().UpdateDataResource(c, req)
if edgeXErr != nil {
httphelper.RenderFail(c, edgeXErr, c.Writer, lc)
return
}
httphelper.ResultSuccess(nil, c.Writer, lc)
}
// @Tags 资源管理
// @Summary 删除资源管理
// @Produce json
// @Success 200 {object} httphelper.CommonResponse
// @Router /api/v1/dataresource/:resourceId [delete]
func (ctl *controller) DataResourceDel(c *gin.Context) {
lc := ctl.lc
id := c.Param(UrlDataResourceId)
edgeXErr := ctl.getDataResourceApp().DelDataResourceById(c, id)
if edgeXErr != nil {
httphelper.RenderFail(c, edgeXErr, c.Writer, lc)
return
}
httphelper.ResultSuccess(nil, c.Writer, lc)
}
// @Tags 资源管理
// @Summary 资源管理查询
// @Produce json
// @Success 200 {object} httphelper.CommonResponse
// @Router /api/v1/dataresource [get]
func (ctl *controller) DataResourceSearch(c *gin.Context) {
lc := ctl.lc
var req dtos.DataResourceSearchQueryRequest
urlDecodeParam(&req, c.Request, lc)
dtos.CorrectionPageParam(&req.BaseSearchConditionQuery)
data, total, edgeXErr := ctl.getDataResourceApp().DataResourceSearch(c, req)
if edgeXErr != nil {
httphelper.RenderFail(c, edgeXErr, c.Writer, lc)
return
}
pageResult := httphelper.NewPageResult(data, total, req.Page, req.PageSize)
httphelper.ResultSuccess(pageResult, c.Writer, lc)
}
func (ctl *controller) DataResourceHealth(c *gin.Context) {
lc := ctl.lc
id := c.Param(UrlDataResourceId)
edgeXErr := ctl.getDataResourceApp().DataResourceHealth(c, id)
if edgeXErr != nil {
httphelper.RenderFail(c, edgeXErr, c.Writer, lc)
return
}
httphelper.ResultSuccess(nil, c.Writer, lc)
}

View File

@ -0,0 +1,407 @@
/*******************************************************************************
* Copyright 2017 Dell Inc.
* Copyright (c) 2019 Intel Corporation
*
* 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 gateway
import (
"github.com/gin-gonic/gin"
"github.com/winc-link/hummingbird/internal/dtos"
"github.com/winc-link/hummingbird/internal/pkg/constants"
"github.com/winc-link/hummingbird/internal/pkg/errort"
"github.com/winc-link/hummingbird/internal/pkg/httphelper"
)
// @Tags 设备管理
// @Summary 查询设备列表
// @Produce json
// @Param request query dtos.DeviceSearchQueryRequest true "参数"
// @Success 200 {array} []dtos.DeviceSearchQueryResponse
// @Router /api/v1/devices [get]
func (ctl *controller) DevicesSearch(c *gin.Context) {
lc := ctl.lc
var req dtos.DeviceSearchQueryRequest
urlDecodeParam(&req, c.Request, lc)
dtos.CorrectionPageParam(&req.BaseSearchConditionQuery)
data, total, edgeXErr := ctl.getDeviceApp().DevicesSearch(c, req)
if edgeXErr != nil {
httphelper.RenderFail(c, edgeXErr, c.Writer, lc)
return
}
pageResult := httphelper.NewPageResult(data, total, req.Page, req.PageSize)
httphelper.ResultSuccess(pageResult, c.Writer, lc)
}
// @Tags 设备管理
// @Summary 查询详情
// @Produce json
// @Param deviceId path string true "pid"
// @Success 200 {object} dtos.DeviceInfoResponse
// @Router /api/v1/device/:deviceId [get]
func (ctl *controller) DeviceById(c *gin.Context) {
lc := ctl.lc
id := c.Param(UrlParamDeviceId)
data, edgeXErr := ctl.getDeviceApp().DeviceById(c, id)
if edgeXErr != nil {
httphelper.RenderFail(c, edgeXErr, c.Writer, lc)
return
}
httphelper.ResultSuccess(data, c.Writer, lc)
}
// @Tags 设备管理
// @Summary 删除设备
// @Produce json
// @Param deviceId path string true "pid"
// @Success 200 {object} httphelper.CommonResponse
// @Router /api/v1/device/:deviceId [delete]
func (ctl *controller) DeviceDelete(c *gin.Context) {
lc := ctl.lc
id := c.Param(UrlParamDeviceId)
edgeXErr := ctl.getDeviceApp().DeleteDeviceById(c, id)
if edgeXErr != nil {
httphelper.RenderFail(c, edgeXErr, c.Writer, lc)
return
}
httphelper.ResultSuccess(nil, c.Writer, lc)
}
// @Tags 设备管理
// @Summary 批量删除设备
// @Produce json
// @Param deviceId path string true "pid"
// @Success 200 {object} httphelper.CommonResponse
// @Router /api/v1/devices [delete]
func (ctl *controller) DevicesDelete(c *gin.Context) {
lc := ctl.lc
var req dtos.DeviceBatchDelete
if err := c.ShouldBind(&req); err != nil {
httphelper.RenderFail(c, errort.NewCommonErr(errort.DefaultReqParamsError, err), c.Writer, lc)
return
}
if len(req.DeviceIds) == 0 {
httphelper.RenderFail(c, errort.NewCommonErr(errort.DefaultReqParamsError, nil), c.Writer, lc)
return
}
edgeXErr := ctl.getDeviceApp().BatchDeleteDevice(c, req.DeviceIds)
if edgeXErr != nil {
httphelper.RenderFail(c, edgeXErr, c.Writer, lc)
return
}
httphelper.ResultSuccess(nil, c.Writer, lc)
}
// @Tags 设备管理
// @Summary 添加设备
// @Produce json
// @Param request query dtos.DeviceAddRequest true "参数"
// @Success 200 {object} httphelper.CommonResponse
// @Router /api/v1/device [post]
func (ctl *controller) DeviceByAdd(c *gin.Context) {
lc := ctl.lc
var req dtos.DeviceAddRequest
if err := c.ShouldBind(&req); err != nil {
httphelper.RenderFail(c, errort.NewCommonErr(errort.DefaultReqParamsError, err), c.Writer, lc)
return
}
_, edgeXErr := ctl.getDeviceApp().AddDevice(c, req)
if edgeXErr != nil {
httphelper.RenderFail(c, edgeXErr, c.Writer, lc)
return
}
httphelper.ResultSuccess(nil, c.Writer, lc)
}
// @Tags 设备管理
// @Summary 查询mqtt连接详情
// @Produce json
// @Param deviceId path string true "pid"
// @Success 200 {object} dtos.DeviceAuthInfoResponse
// @Router /api/v1/device-mqtt/:deviceId [get]
func (ctl *controller) DeviceMqttInfoById(c *gin.Context) {
lc := ctl.lc
id := c.Param(UrlParamDeviceId)
data, edgeXErr := ctl.getDeviceApp().DeviceMqttAuthInfo(c, id)
if edgeXErr != nil {
httphelper.RenderFail(c, edgeXErr, c.Writer, lc)
return
}
httphelper.ResultSuccess(data, c.Writer, lc)
}
func (ctl *controller) AddMqttAuth(c *gin.Context) {
lc := ctl.lc
var req dtos.AddMqttAuthInfoRequest
if err := c.ShouldBind(&req); err != nil {
httphelper.RenderFail(c, errort.NewCommonErr(errort.DefaultReqParamsError, err), c.Writer, lc)
return
}
data, edgeXErr := ctl.getDeviceApp().AddMqttAuth(c, req)
if edgeXErr != nil {
httphelper.RenderFail(c, edgeXErr, c.Writer, lc)
return
}
httphelper.ResultSuccess(data, c.Writer, lc)
}
// @Tags 设备管理
// @Summary 设备导入模版下载
// @Produce json
// @Param req query dtos.DeviceImportTemplateRequest true "参数"
// @Success 200 {object} string
// @Router /api/v1/devices/import-template [get]
func (ctl *controller) DeviceImportTemplateDownload(c *gin.Context) {
lc := ctl.lc
var req dtos.DeviceImportTemplateRequest
if err := c.ShouldBind(&req); err != nil {
httphelper.RenderFail(c, errort.NewCommonErr(errort.DefaultReqParamsError, err), c.Writer, lc)
return
}
file, edgeXErr := ctl.getDeviceApp().DeviceImportTemplateDownload(c, req)
if edgeXErr != nil {
httphelper.RenderFail(c, edgeXErr, c.Writer, lc)
return
}
data, _ := file.Excel.WriteToBuffer()
httphelper.ResultExcelData(c, file.FileName, data)
}
// @Tags 设备管理
// @Summary 设备导入模版校验
// @Produce json
// @Success 200 {object} string
// @Router /api/v1/device/upload-validated [post]
func (ctl *controller) UploadValidated(c *gin.Context) {
lc := ctl.lc
files, _ := c.FormFile("file")
f, err := files.Open()
if err != nil {
err = errort.NewCommonErr(errort.DefaultUploadFileErrorCode, err)
httphelper.RenderFail(c, err, c.Writer, lc)
return
}
file, edgeXErr := dtos.NewImportFile(f)
if edgeXErr != nil {
httphelper.RenderFail(c, edgeXErr, c.Writer, lc)
return
}
edgeXErr = ctl.getDeviceApp().UploadValidated(c, file)
if edgeXErr != nil {
httphelper.RenderFail(c, edgeXErr, c.Writer, lc)
return
}
httphelper.ResultSuccess(nil, c.Writer, lc)
}
// @Tags 设备管理
// @Summary 设备导入
// @Produce json
// @Success 200 {object} httphelper.CommonResponse
// @Router /api/v1/devices/import [post]
func (ctl *controller) DevicesImport(c *gin.Context) {
lc := ctl.lc
//productId := c.Param(UrlParamProductId)
//cloudInstanceId := c.Param(UrlParamCloudInstanceId)
//var req dtos.ProductSearchQueryRequest
var req dtos.DevicesImport
urlDecodeParam(&req, c.Request, lc)
files, _ := c.FormFile("file")
f, err := files.Open()
if err != nil {
err = errort.NewCommonErr(errort.DefaultUploadFileErrorCode, err)
httphelper.RenderFail(c, err, c.Writer, lc)
return
}
file, edgeXErr := dtos.NewImportFile(f)
if edgeXErr != nil {
httphelper.RenderFail(c, edgeXErr, c.Writer, lc)
return
}
result, edgeXErr := ctl.getDeviceApp().DevicesImport(c, file, req.ProductId, req.DriverInstanceId)
if edgeXErr != nil {
httphelper.RenderFail(c, edgeXErr, c.Writer, lc)
return
}
httphelper.ResultSuccess(result, c.Writer, lc)
}
// @Tags 设备管理
// @Summary 更新设备
// @Produce json
// @Param deviceId path string true "pid"
// @Success 200 {object} httphelper.CommonResponse
// @Router /api/v1/device/:deviceId [put]
func (ctl *controller) DeviceUpdate(c *gin.Context) {
lc := ctl.lc
var req dtos.DeviceUpdateRequest
if err := c.ShouldBind(&req); err != nil {
httphelper.RenderFail(c, errort.NewCommonErr(errort.DefaultReqParamsError, err), c.Writer, lc)
return
}
err := ctl.getDeviceApp().DeviceUpdate(c, req)
if err != nil {
httphelper.RenderFail(c, err, c.Writer, lc)
return
}
httphelper.ResultSuccess(nil, c.Writer, lc)
}
// @Tags 设备管理
// @Summary 设备批量绑定驱动
// @Produce json
// @Param request query dtos.DevicesBindDriver true "参数"
// @Success 200 {object} httphelper.CommonResponse
// @Router /api/v1/devices/bind-driver [put]
func (ctl *controller) DevicesBindDriver(c *gin.Context) {
lc := ctl.lc
var req dtos.DevicesBindDriver
if err := c.ShouldBind(&req); err != nil {
httphelper.RenderFail(c, errort.NewCommonErr(errort.DefaultReqParamsError, err), c.Writer, lc)
return
}
err := ctl.getDeviceApp().DevicesBindDriver(c, req)
if err != nil {
httphelper.RenderFail(c, err, c.Writer, lc)
return
}
httphelper.ResultSuccess(nil, c.Writer, lc)
}
// @Tags 设备管理
// @Summary 设备批量与驱动解绑
// @Produce json
// @Param request query dtos.DevicesBindDriver true "参数"
// @Success 200 {object} httphelper.CommonResponse
// @Router /api/v1/unbind-driver [put]
func (ctl *controller) DevicesUnBindDriver(c *gin.Context) {
lc := ctl.lc
var req dtos.DevicesUnBindDriver
if err := c.ShouldBind(&req); err != nil {
httphelper.RenderFail(c, errort.NewCommonErr(errort.DefaultReqParamsError, err), c.Writer, lc)
return
}
err := ctl.getDeviceApp().DevicesUnBindDriver(c, req)
if err != nil {
httphelper.RenderFail(c, err, c.Writer, lc)
return
}
httphelper.ResultSuccess(nil, c.Writer, lc)
}
func (ctl *controller) DevicesBindByProductId(c *gin.Context) {
lc := ctl.lc
var req dtos.DevicesBindProductId
if err := c.ShouldBind(&req); err != nil {
httphelper.RenderFail(c, errort.NewCommonErr(errort.DefaultReqParamsError, err), c.Writer, lc)
return
}
err := ctl.getDeviceApp().DevicesBindProductId(c, req)
if err != nil {
httphelper.RenderFail(c, err, c.Writer, lc)
return
}
httphelper.ResultSuccess(nil, c.Writer, lc)
}
// @Tags 设备管理
// @Summary 查看设备属性数据
// @Produce json
// @Param request query dtos.ThingModelPropertyDataRequest true "参数"
// @Success 200 {array} []dtos.ThingModelDataResponse
// @Router /api/v1/device/:deviceId/thing-model/property [get]
func (ctl *controller) DeviceThingModelPropertyDataSearch(c *gin.Context) {
lc := ctl.lc
var req dtos.ThingModelPropertyDataRequest
urlDecodeParam(&req, c.Request, lc)
deviceId := c.Param(UrlParamDeviceId)
req.DeviceId = deviceId
data, edgeXErr := ctl.getPersistApp().SearchDeviceThingModelPropertyData(req)
if edgeXErr != nil {
httphelper.RenderFail(c, edgeXErr, c.Writer, lc)
return
}
httphelper.ResultSuccess(data, c.Writer, lc)
}
func (ctl *controller) DeviceThingModelHistoryPropertyDataSearch(c *gin.Context) {
lc := ctl.lc
var req dtos.ThingModelPropertyDataRequest
urlDecodeParam(&req, c.Request, lc)
dtos.CorrectionPageParam(&req.BaseSearchConditionQuery)
deviceId := c.Param(UrlParamDeviceId)
req.DeviceId = deviceId
data, total, edgeXErr := ctl.getPersistApp().SearchDeviceThingModelHistoryPropertyData(req)
if edgeXErr != nil {
httphelper.RenderFail(c, edgeXErr, c.Writer, lc)
return
}
pageResult := httphelper.NewPageResult(data, uint32(total), req.Page, req.PageSize)
httphelper.ResultSuccess(pageResult, c.Writer, lc)
}
// @Tags 设备管理
// @Summary 查看设备事件数据
// @Produce json
// @Param request query dtos.ThingModelPropertyDataRequest true "参数"
// @Success 200 {array} []dtos.ThingModelEventDataResponse
// @Router /api/v1/device/:deviceId/thing-model/event [get]
func (ctl *controller) DeviceThingModelEventDataSearch(c *gin.Context) {
lc := ctl.lc
var req dtos.ThingModelEventDataRequest
urlDecodeParam(&req, c.Request, lc)
deviceId := c.Param(UrlParamDeviceId)
dtos.CorrectionPageParam(&req.BaseSearchConditionQuery)
req.DeviceId = deviceId
data, total, edgeXErr := ctl.getPersistApp().SearchDeviceThingModelEventData(req)
if edgeXErr != nil {
httphelper.RenderFail(c, edgeXErr, c.Writer, lc)
return
}
pageResult := httphelper.NewPageResult(data, uint32(total), req.Page, req.PageSize)
httphelper.ResultSuccess(pageResult, c.Writer, lc)
}
// @Tags 设备管理
// @Summary 查看设备服务调用数据
// @Produce json
// @Param request query dtos.ThingModelServiceDataRequest true "参数"
// @Success 200 {array} []dtos.ThingModelServiceDataResponse
// @Router /api/v1/device/:deviceId/thing-model/service [get]
func (ctl *controller) DeviceThingModelServiceDataSearch(c *gin.Context) {
lc := ctl.lc
var req dtos.ThingModelServiceDataRequest
urlDecodeParam(&req, c.Request, lc)
deviceId := c.Param(UrlParamDeviceId)
dtos.CorrectionPageParam(&req.BaseSearchConditionQuery)
req.DeviceId = deviceId
data, total, edgeXErr := ctl.getPersistApp().SearchDeviceThingModelServiceData(req)
if edgeXErr != nil {
httphelper.RenderFail(c, edgeXErr, c.Writer, lc)
return
}
pageResult := httphelper.NewPageResult(data, uint32(total), req.Page, req.PageSize)
httphelper.ResultSuccess(pageResult, c.Writer, lc)
}
func (ctl *controller) DeviceStatusTemplate(c *gin.Context) {
lc := ctl.lc
var deviceStatus []constants.DeviceStatus
deviceStatus = append(append(append(deviceStatus),
constants.DeviceStatusOnline),
constants.DeviceStatusOffline)
httphelper.ResultSuccess(deviceStatus, c.Writer, lc)
}

View File

@ -0,0 +1,280 @@
/*******************************************************************************
* Copyright 2017.
*
* 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 gateway
import (
"github.com/gin-gonic/gin"
"github.com/winc-link/hummingbird/internal/dtos"
"github.com/winc-link/hummingbird/internal/pkg/errort"
"github.com/winc-link/hummingbird/internal/pkg/httphelper"
"time"
)
// @Tags 告警中心
// @Summary 添加告警规则
// @Produce json
// @Param request query dtos.RuleAddRequest true "参数"
// @Success 200 {object} httphelper.CommonResponse
// @Router /api/v1/alert-rule [post]
func (ctl *controller) AlertRuleAdd(c *gin.Context) {
lc := ctl.lc
var req dtos.RuleAddRequest
if err := c.ShouldBind(&req); err != nil {
httphelper.RenderFail(c, errort.NewCommonErr(errort.DefaultReqParamsError, err), c.Writer, lc)
return
}
_, edgeXErr := ctl.getAlertRuleApp().AddAlertRule(c, req)
if edgeXErr != nil {
httphelper.RenderFail(c, edgeXErr, c.Writer, lc)
return
}
httphelper.ResultSuccess(nil, c.Writer, lc)
}
// @Tags 告警中心
// @Summary 编辑告警规则
// @Produce json
// @Param request query dtos.RuleUpdateRequest true "参数"
// @Success 200 {object} httphelper.CommonResponse
// @Router /api/v1/alert-rule/:ruleId [put]
func (ctl *controller) AlertRuleUpdate(c *gin.Context) {
lc := ctl.lc
var req dtos.RuleUpdateRequest
if err := c.ShouldBind(&req); err != nil {
httphelper.RenderFail(c, errort.NewCommonErr(errort.DefaultReqParamsError, err), c.Writer, lc)
return
}
edgeXErr := ctl.getAlertRuleApp().UpdateAlertRule(c, req)
if edgeXErr != nil {
httphelper.RenderFail(c, edgeXErr, c.Writer, lc)
return
}
httphelper.ResultSuccess(nil, c.Writer, lc)
}
func (ctl *controller) AlertRuleUpdateField(c *gin.Context) {
lc := ctl.lc
var req dtos.RuleFieldUpdate
if err := c.ShouldBind(&req); err != nil {
httphelper.RenderFail(c, errort.NewCommonErr(errort.DefaultReqParamsError, err), c.Writer, lc)
return
}
edgeXErr := ctl.getAlertRuleApp().UpdateAlertField(c, req)
if edgeXErr != nil {
httphelper.RenderFail(c, edgeXErr, c.Writer, lc)
return
}
httphelper.ResultSuccess(nil, c.Writer, lc)
}
// @Tags 告警中心
// @Summary 告警规则详情
// @Produce json
// @Param ruleId path string true "ruleId"
// @Success 200 {object} httphelper.CommonResponse
// @Router /api/v1/alert-rule/:ruleId [get]
func (ctl *controller) AlertRuleById(c *gin.Context) {
lc := ctl.lc
id := c.Param(UrlParamRuleId)
data, edgeXErr := ctl.getAlertRuleApp().AlertRuleById(c, id)
if edgeXErr != nil {
httphelper.RenderFail(c, edgeXErr, c.Writer, lc)
return
}
httphelper.ResultSuccess(data, c.Writer, lc)
}
// @Tags 告警中心
// @Summary 告警规则列表
// @Produce json
// @Param request query dtos.AlertRuleSearchQueryRequest true "参数"
// @Success 200 {array} []dtos.AlertRuleSearchQueryResponse
// @Router /api/v1/alert-rule [get]
func (ctl *controller) AlertRuleSearch(c *gin.Context) {
lc := ctl.lc
var req dtos.AlertRuleSearchQueryRequest
urlDecodeParam(&req, c.Request, lc)
dtos.CorrectionPageParam(&req.BaseSearchConditionQuery)
data, total, edgeXErr := ctl.getAlertRuleApp().AlertRulesSearch(c, req)
if edgeXErr != nil {
httphelper.RenderFail(c, edgeXErr, c.Writer, lc)
return
}
pageResult := httphelper.NewPageResult(data, total, req.Page, req.PageSize)
httphelper.ResultSuccess(pageResult, c.Writer, lc)
}
// @Tags 告警中心
// @Summary 告警规则启动
// @Produce json
// @Param ruleId path string true "ruleId"
// @Success 200 {object} httphelper.CommonResponse
// @Router /api/v1/alert-rule/:ruleId/start [post]
func (ctl *controller) AlertRuleStart(c *gin.Context) {
lc := ctl.lc
id := c.Param(UrlParamRuleId)
edgeXErr := ctl.getAlertRuleApp().AlertRulesStart(c, id)
if edgeXErr != nil {
httphelper.RenderFail(c, edgeXErr, c.Writer, lc)
return
}
httphelper.ResultSuccess(nil, c.Writer, lc)
}
// @Tags 告警中心
// @Summary 告警规则停止
// @Produce json
// @Param ruleId path string true "ruleId"
// @Success 200 {object} httphelper.CommonResponse
// @Router /api/v1/alert-rule/:ruleId/stop [post]
func (ctl *controller) AlertRuleStop(c *gin.Context) {
lc := ctl.lc
id := c.Param(UrlParamRuleId)
edgeXErr := ctl.getAlertRuleApp().AlertRulesStop(c, id)
if edgeXErr != nil {
httphelper.RenderFail(c, edgeXErr, c.Writer, lc)
return
}
httphelper.ResultSuccess(nil, c.Writer, lc)
}
// @Tags 告警中心
// @Summary 告警规则重启
// @Produce json
// @Param ruleId path string true "ruleId"
// @Success 200 {object} httphelper.CommonResponse
// @Router /api/v1/alert-rule/:ruleId/restart [post]
func (ctl *controller) AlertRuleRestart(c *gin.Context) {
lc := ctl.lc
id := c.Param(UrlParamRuleId)
edgeXErr := ctl.getAlertRuleApp().AlertRulesRestart(c, id)
if edgeXErr != nil {
httphelper.RenderFail(c, edgeXErr, c.Writer, lc)
return
}
httphelper.ResultSuccess(nil, c.Writer, lc)
}
// @Tags 告警中心
// @Summary 告警规则删除
// @Produce json
// @Param ruleId path string true "ruleId"
// @Success 200 {object} httphelper.CommonResponse
// @Router /api/v1/alert-rule/:ruleId [delete]
func (ctl *controller) AlertRuleDelete(c *gin.Context) {
lc := ctl.lc
id := c.Param(UrlParamRuleId)
edgeXErr := ctl.getAlertRuleApp().AlertRulesDelete(c, id)
if edgeXErr != nil {
httphelper.RenderFail(c, edgeXErr, c.Writer, lc)
return
}
httphelper.ResultSuccess(nil, c.Writer, lc)
}
// @Tags 告警中心
// @Summary 告警列表
// @Produce json
// @Param request query dtos.AlertSearchQueryRequest true "参数"
// @Success 200 {array} []dtos.AlertSearchQueryResponse
// @Router /api/v1/alert-list [get]
func (ctl *controller) AlertSearch(c *gin.Context) {
lc := ctl.lc
var req dtos.AlertSearchQueryRequest
urlDecodeParam(&req, c.Request, lc)
dtos.CorrectionPageParam(&req.BaseSearchConditionQuery)
data, total, edgeXErr := ctl.getAlertRuleApp().AlertSearch(c, req)
if edgeXErr != nil {
httphelper.RenderFail(c, edgeXErr, c.Writer, lc)
return
}
pageResult := httphelper.NewPageResult(data, total, req.Page, req.PageSize)
httphelper.ResultSuccess(pageResult, c.Writer, lc)
}
// @Tags 告警中心
// @Summary 告警列表
// @Produce json
// @Param request query dtos.AlertSearchQueryRequest true "参数"
// @Success 200 {array} []dtos.AlertSearchQueryResponse
// @Router /api/v1/alert-plate [get]
func (ctl *controller) AlertPlate(c *gin.Context) {
lc := ctl.lc
currentTime := time.Now()
beforeTime := currentTime.AddDate(0, 0, -7).UnixMilli()
data, edgeXErr := ctl.getAlertRuleApp().AlertPlate(c, beforeTime)
if edgeXErr != nil {
httphelper.RenderFail(c, edgeXErr, c.Writer, lc)
return
}
httphelper.ResultSuccess(data, c.Writer, lc)
}
// @Tags 告警中心
// @Summary 忽略告警
// @Produce json
// @Router /api/v1/alert-ignore/:ruleId [put]
func (ctl *controller) AlertIgnore(c *gin.Context) {
lc := ctl.lc
id := c.Param(UrlParamRuleId)
edgeXErr := ctl.getAlertRuleApp().AlertIgnore(c, id)
if edgeXErr != nil {
httphelper.RenderFail(c, edgeXErr, c.Writer, lc)
return
}
httphelper.ResultSuccess(nil, c.Writer, lc)
}
// @Tags 告警中心
// @Summary 处理告警
// @Produce json
// @Router /api/v1/alert-treated [post]
func (ctl *controller) AlertTreated(c *gin.Context) {
lc := ctl.lc
var req dtos.AlertTreatedRequest
if err := c.ShouldBind(&req); err != nil {
httphelper.RenderFail(c, errort.NewCommonErr(errort.DefaultReqParamsError, err), c.Writer, lc)
return
}
edgeXErr := ctl.getAlertRuleApp().TreatedIgnore(c, req.Id, req.Message)
if edgeXErr != nil {
httphelper.RenderFail(c, edgeXErr, c.Writer, lc)
return
}
httphelper.ResultSuccess(nil, c.Writer, lc)
}
// @Tags 告警中心
// @Summary 告警列表
// @Produce json
// @Param request query dtos.AlertAddRequest true "参数"
// @Success 200 {object} httphelper.CommonResponse
// @Router /api/v1/alert [post]
func (ctl *controller) EkuiperAlert(c *gin.Context) {
lc := ctl.lc
req := make(map[string]interface{})
if err := c.ShouldBind(&req); err != nil {
httphelper.RenderFail(c, errort.NewCommonErr(errort.DefaultReqParamsError, err), c.Writer, lc)
return
}
lc.Info("req....", req)
edgeXErr := ctl.getAlertRuleApp().AddAlert(c, req)
if edgeXErr != nil {
httphelper.RenderFail(c, edgeXErr, c.Writer, lc)
return
}
httphelper.ResultSuccess(nil, c.Writer, lc)
}

View File

@ -0,0 +1,270 @@
package gateway
import (
"github.com/gin-gonic/gin"
"github.com/winc-link/hummingbird/internal/dtos"
"github.com/winc-link/hummingbird/internal/pkg/errort"
"github.com/winc-link/hummingbird/internal/pkg/httphelper"
)
// @Tags 驱动库管理
// @Summary 新增驱动库
// @Produce json
// @Param request body dtos.DeviceLibraryAddRequest true "参数"
// @Success 200 {object} httphelper.CommonResponse
// @Router /api/v1/device-libraries [post]
func (ctl *controller) DeviceLibraryAdd(c *gin.Context) {
lc := ctl.lc
var req dtos.DeviceLibraryAddRequest
if err := c.ShouldBind(&req); err != nil {
httphelper.RenderFail(c, errort.NewCommonErr(errort.DefaultReqParamsError, err), c.Writer, lc)
return
}
err := ctl.getDriverLibApp().AddDriverLib(c, req)
if err != nil {
httphelper.RenderFail(c, err, c.Writer, lc)
return
}
httphelper.ResultSuccess(nil, c.Writer, lc)
}
// @Tags 驱动库管理
// @Summary 查询驱动
// @Produce json
// @Param request query dtos.DeviceLibrarySearchQueryRequest true "参数"
// @Success 200 {object} httphelper.ResPageResult
// @Router /api/v1/device-libraries [get]
func (ctl *controller) DeviceLibrariesSearch(c *gin.Context) {
lc := ctl.lc
var req dtos.DeviceLibrarySearchQueryRequest
if err := c.ShouldBind(&req); err != nil {
httphelper.RenderFail(c, errort.NewCommonErr(errort.DefaultReqParamsError, err), c.Writer, lc)
return
}
dtos.CorrectionPageParam(&req.BaseSearchConditionQuery)
//req2 := dtos.FromDeviceLibrarySearchQueryRequestToRpc(req)
list, total, edgeXErr := ctl.getDriverLibApp().DeviceLibrariesSearch(c, req)
if edgeXErr != nil {
httphelper.RenderFail(c, edgeXErr, c.Writer, lc)
return
}
libs := make([]dtos.DeviceLibraryResponse, len(list))
for i, p := range list {
libs[i] = dtos.DeviceLibraryResponseFromModel(p)
}
pageResult := httphelper.NewPageResult(libs, total, req.Page, req.PageSize)
httphelper.ResultSuccess(pageResult, c.Writer, lc)
}
// @Tags 驱动库管理
// @Summary 删除驱动
// @Produce json
// @Param deviceLibraryId path string true "驱动ID"
// @Success 200 {object} httphelper.CommonResponse
// @Router /api/v1/device-libraries/:deviceLibraryId [delete]
func (ctl *controller) DeviceLibraryDelete(c *gin.Context) {
lc := ctl.lc
id := c.Param(UrlParamDeviceLibraryId)
err := ctl.getDriverLibApp().DeleteDeviceLibraryById(c, id)
if err != nil {
httphelper.RenderFail(c, err, c.Writer, lc)
return
}
httphelper.ResultSuccess(nil, c.Writer, lc)
}
// @Tags 驱动库管理
// @Summary 获取驱动定义配置信息
// @Produce json
// @Param request query dtos.DeviceLibraryConfigRequest true "参数"
// @Success 200 {object} httphelper.CommonResponse
// @Router /api/v1/device-libraries/config [get]
//func (ctl *controller) DeviceLibraryConfig(c *gin.Context) {
// lc := ctl.lc
// var req dtos.DeviceLibraryConfigRequest
// if err := c.ShouldBindQuery(&req); err != nil {
// httphelper.RenderFail(c, errort.NewCommonErr(errort.DefaultReqParamsError, err), c.Writer, lc)
// return
// }
// if req.DeviceLibraryId == nil && req.CloudProductId == nil && req.DeviceServiceId == nil && req.DeviceId == nil {
// err := fmt.Errorf("deviceLibraryConfig req is null")
// httphelper.RenderFail(c, errort.NewCommonErr(errort.DefaultReqParamsError, err), c.Writer, lc)
// return
// }
//
// dl, err := ctl.getDriverLibApp().GetDeviceLibraryConfig(c, req)
// //data, edgeXErr := gatewayapp.DeviceLibraryConfig(c, req)
// if err != nil {
// httphelper.RenderFail(c, err, c.Writer, lc)
// return
// }
// config, err := dl.GetConfigMap()
// if err != nil {
// httphelper.RenderFail(c, err, c.Writer, lc)
// return
// }
//
// httphelper.ResultSuccess(config, c.Writer, lc)
//}
// @Tags 驱动库管理
// @Summary 驱动库升级/下载
// @Produce json
// @Param deviceLibraryId path string true "驱动ID"
// @Param request query dtos.DeviceLibraryUpgradeRequest true "参数"
// @Success 200 {object} httphelper.CommonResponse
// @Router /api/v1/device-libraries/:deviceLibraryId/upgrade-download [put]
// Deprecated
//func (ctl *controller) DeviceLibraryUpgrade(c *gin.Context) {
// lc := ctl.lc
// var req dtos.DeviceLibraryUpgradeRequest
// req.Id = c.Param(UrlParamDeviceLibraryId)
// if err := c.ShouldBind(&req); err != nil {
// httphelper.RenderFail(c, errort.NewCommonErr(errort.DefaultReqParamsError, err), c.Writer, lc)
// return
// }
//
// err := ctl.getDriverLibApp().UpgradeDeviceLibrary(c, req, true)
// //edgeXErr := gatewayapp.DeviceLibraryUpgrade(c, req)
// if err != nil {
// httphelper.RenderFail(c, err, c.Writer, lc)
// return
// }
//
// httphelper.ResultSuccess(nil, c.Writer, lc)
//}
// @Tags 驱动库管理
// @Summary 上传驱动配置文件
// @Accept multipart/form-data
// @Produce json
// @Success 200 {object} dtos.DeviceLibraryUploadResponse
// @Router /api/v1/device-libraries/upload [post]
//func (ctl *controller) DeviceLibraryUpload(c *gin.Context) {
// lc := ctl.lc
// file, err := c.FormFile("fileName")
// if err != nil {
// httphelper.RenderFail(c, errort.NewCommonErr(errort.DefaultReqParamsError, err), c.Writer, lc)
// return
// }
// var req dtos.DeviceLibraryUploadRequest
// req.FileName = file.Filename
// if !utils.CheckFileValid(req.FileName) {
// err := fmt.Errorf("file name cannot contain special characters: %s", req.FileName)
// httphelper.RenderFail(c, errort.NewCommonErr(errort.DefaultFileNotSpecialSymbol, err), c.Writer, lc)
// return
// }
//
// if fileSuffix := path.Ext(req.FileName); fileSuffix != ".json" {
// err := fmt.Errorf("file type not json, filename: %s", req.FileName)
// httphelper.RenderFail(c, errort.NewCommonErr(errort.DefaultJsonParseError, err), c.Writer, lc)
// return
// }
//
// uploadType, err := strconv.Atoi(c.Request.PostForm["type"][0])
// if err != nil {
// httphelper.RenderFail(c, errort.NewCommonErr(errort.DefaultReqParamsError, err), c.Writer, lc)
// return
// }
//
// req.Type = uploadType
// if req.Type != constants.DeviceLibraryUploadTypeConfig {
// err := fmt.Errorf("req upload type %d not is %d", req.Type, constants.DeviceLibraryUploadTypeConfig)
// httphelper.RenderFail(c, errort.NewCommonErr(errort.DefaultReqParamsError, err), c.Writer, lc)
// return
// }
// f, err := file.Open()
// if err != nil {
// httphelper.RenderFail(c, errort.NewCommonErr(errort.DefaultReqParamsError, err), c.Writer, lc)
// return
// }
//
// // 将文件流读入请求中,并校验格式
// req.FileBytes, err = ioutil.ReadAll(f)
// if err != nil {
// httphelper.RenderFail(c, errort.NewCommonErr(errort.DefaultReqParamsError, err), c.Writer, lc)
// return
// }
// if !json.Valid(req.FileBytes) {
// err := fmt.Errorf("config file content must be json")
// httphelper.RenderFail(c, errort.NewCommonErr(errort.DefaultJsonParseError, err), c.Writer, lc)
// return
// }
//
// fileName, err := ctl.getDriverLibApp().UploadDeviceLibraryConfig(c, req)
// //resp, edgeXErr := gatewayapp.DeviceLibraryUpload(c, req)
// if err != nil {
// httphelper.RenderFail(c, err, c.Writer, lc)
// return
// }
// resp := dtos.DeviceLibraryUploadResponse{
// FileName: fileName,
// }
// httphelper.ResultSuccess(resp, c.Writer, lc)
//}
//@Tags 驱动库管理
//@Summary 驱动库更新
//@Produce json
//@Param deviceLibraryId path string true "驱动ID"
//@Param request query dtos.UpdateDeviceLibrary true "参数"
//@Success 200 {object} httphelper.CommonResponse
//@Router /api/v1/device-libraries/:deviceLibraryId [put]
func (ctl *controller) DeviceLibraryUpdate(c *gin.Context) {
lc := ctl.lc
var req dtos.UpdateDeviceLibrary
req.Id = c.Param(UrlParamDeviceLibraryId)
if err := c.ShouldBind(&req); err != nil {
httphelper.RenderFail(c, errort.NewCommonErr(errort.DefaultReqParamsError, err), c.Writer, lc)
return
}
err := ctl.getDriverLibApp().UpdateDeviceLibrary(c, req)
if err != nil {
httphelper.RenderFail(c, err, c.Writer, lc)
return
}
httphelper.ResultSuccess(nil, c.Writer, lc)
}
// @Tags 驱动库管理
// @Summary 驱动库配置下载
// @Produce application/octet-stream
// @Router /api/v1/device-libraries/config/download [get]
//func (ctl *controller) DeviceLibraryConfigDownload(c *gin.Context) {
// //dir, _ := os.Getwd()
// //if dir == "/" {
// // dir = ""
// //}
// //filePath := dir + "/template/driver_config_demo.json"
// //
// //fileName := path.Base(filePath)
// //c.Header("Content-Type", "application/octet-stream")
// //c.Header("Content-Disposition", "attachment; filename="+fileName)
// //c.File(filePath)
// cfg := ctl.getDriverLibApp().ConfigDemo()
// buff := bytes.NewBuffer([]byte(cfg))
// httphelper.ResultExcelData(c, "driver_config.json", buff)
//}
//@Tags 驱动库分类
//@Summary 驱动库分类
//@Produce json
// @Param request query dtos.DriverClassifyQueryRequest true "参数"
//@Success 200 {object} httphelper.CommonResponse
//@Router /api/v1/device_classify [get]
func (ctl *controller) DeviceClassify(c *gin.Context) {
lc := ctl.lc
var req dtos.DriverClassifyQueryRequest
urlDecodeParam(&req, c.Request, lc)
dtos.CorrectionPageParam(&req.BaseSearchConditionQuery)
list, total, edgeXErr := ctl.getDriverLibApp().GetDriverClassify(c, req)
if edgeXErr != nil {
httphelper.RenderFail(c, edgeXErr, c.Writer, lc)
return
}
pageResult := httphelper.NewPageResult(list, total, req.Page, req.PageSize)
httphelper.ResultSuccess(pageResult, c.Writer, lc)
}

View File

@ -0,0 +1,98 @@
/*******************************************************************************
* Copyright 2017 Dell Inc.
* Copyright (c) 2019 Intel Corporation
*
* 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 gateway
import (
"github.com/gin-gonic/gin"
"github.com/winc-link/hummingbird/internal/dtos"
"github.com/winc-link/hummingbird/internal/pkg/errort"
"github.com/winc-link/hummingbird/internal/pkg/httphelper"
)
// @Tags 驱动实例管理
// @Summary 查询驱动实例
// @Produce json
// @Param request query dtos.DeviceServiceSearchQueryRequest true "参数"
// @Success 200 {object} httphelper.ResPageResult
// @Router /api/v1/device-servers [get]
func (ctl *controller) DeviceServicesSearch(c *gin.Context) {
lc := ctl.lc
var req dtos.DeviceServiceSearchQueryRequest
urlDecodeParam(&req, c.Request, lc)
dtos.CorrectionPageParam(&req.BaseSearchConditionQuery)
if err := c.ShouldBind(&req); err != nil {
httphelper.RenderFail(c, errort.NewCommonErr(errort.DefaultReqParamsError, err), c.Writer, lc)
return
}
// TODO 驱动实例搜索是否需要查询驱动库
dss, total, err := ctl.getDriverServiceApp().Search(c, req)
if err != nil {
httphelper.RenderFail(c, err, c.Writer, lc)
return
}
data := make([]dtos.DeviceServiceResponse, len(dss))
for i, ds := range dss {
dl, err := ctl.getDriverLibApp().DriverLibById(ds.DeviceLibraryId)
if err != nil {
httphelper.RenderFail(c, err, c.Writer, lc)
return
}
data[i] = dtos.DeviceServiceResponseFromModel(ds, dl)
}
pageResult := httphelper.NewPageResult(data, total, req.Page, req.PageSize)
httphelper.ResultSuccess(pageResult, c.Writer, lc)
}
// @Tags 驱动实例管理
// @Summary 删除驱动实例废弃已经改为websockert形式)
// @Produce json
// @Param deviceServiceId path string true "驱动实例 ID"
// @Success 200 {object} httphelper.CommonResponse
// @Router /api/v1/device_server/:deviceServiceId [delete]
func (ctl *controller) DeviceServiceDelete(c *gin.Context) {
lc := ctl.lc
id := c.Param(UrlParamDeviceServiceId)
err := ctl.getDriverServiceApp().Del(c, id)
//edgeXErr := gatewayapp.DeviceServiceDelete(c, id)
if err != nil {
httphelper.RenderFail(c, err, c.Writer, lc)
return
}
httphelper.ResultSuccess(nil, c.Writer, lc)
}
// @Tags 驱动实例管理
// @Summary 编辑驱动实例
// @Produce json
// @Param request body dtos.DeviceServiceUpdateRequest true "参数"
// @Success 200 {object} httphelper.CommonResponse
// @Router /api/v1/device-server [put]
func (ctl *controller) DeviceServiceUpdate(c *gin.Context) {
lc := ctl.lc
var req dtos.DeviceServiceUpdateRequest
if err := c.ShouldBind(&req); err != nil {
httphelper.RenderFail(c, errort.NewCommonErr(errort.DefaultReqParamsError, err), c.Writer, lc)
return
}
err := ctl.getDriverServiceApp().Update(c, req)
if err != nil {
httphelper.RenderFail(c, err, c.Writer, lc)
return
}
httphelper.ResultSuccess(nil, c.Writer, lc)
}

View File

@ -0,0 +1,109 @@
/*******************************************************************************
* Copyright 2017 Dell Inc.
* Copyright (c) 2019 Intel Corporation
*
* 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 gateway
import (
"github.com/gin-gonic/gin"
"github.com/winc-link/hummingbird/internal/dtos"
"github.com/winc-link/hummingbird/internal/pkg/errort"
"github.com/winc-link/hummingbird/internal/pkg/httphelper"
)
// @Tags 镜像仓库管理
// @Summary 新增镜像
// @Produce json
// @Param request body dtos.DockerConfigAddRequest true "参数"
// @Success 200 {object} httphelper.CommonResponse
// @Router /api/v1/docker-configs [post]
func (ctl *controller) DockerConfigAdd(c *gin.Context) {
lc := ctl.lc
var req dtos.DockerConfigAddRequest
if err := c.ShouldBind(&req); err != nil {
httphelper.RenderFail(c, errort.NewCommonErr(errort.DefaultReqParamsError, err), c.Writer, lc)
return
}
edgeXErr := ctl.getDriverLibApp().DownConfigAdd(c, req)
if edgeXErr != nil {
httphelper.RenderFail(c, edgeXErr, c.Writer, lc)
return
}
httphelper.ResultSuccess(nil, c.Writer, lc)
}
// @Tags 镜像仓库管理
// @Summary 获取镜像列表
// @Produce json
// @Param request query dtos.DockerConfigSearchQueryRequest true "参数"
// @Success 200 {object} httphelper.ResPageResult
// @Router /api/v1/docker-configs [get]
func (ctl *controller) DockerConfigsSearch(c *gin.Context) {
lc := ctl.lc
var req dtos.DockerConfigSearchQueryRequest
urlDecodeParam(&req, c.Request, lc)
dtos.CorrectionPageParam(&req.BaseSearchConditionQuery)
list, total, edgeXErr := ctl.getDriverLibApp().DownConfigSearch(c, req)
if edgeXErr != nil {
httphelper.RenderFail(c, edgeXErr, c.Writer, lc)
return
}
dcs := make([]dtos.DockerConfigResponse, len(list))
for i, v := range list {
dcs[i] = dtos.DockerConfigResponseFromModel(v)
}
pageResult := httphelper.NewPageResult(dcs, total, req.Page, req.PageSize)
httphelper.ResultSuccess(pageResult, c.Writer, lc)
}
// @Tags 镜像仓库管理
// @Summary 修改仓库信息
// @Produce json
// @Param request body dtos.DockerConfigUpdateRequest true "参数"
// @Success 200 {object} httphelper.CommonResponse
// @Router /api/v1/docker-configs/:dockerConfigId [put]
func (ctl *controller) DockerConfigUpdate(c *gin.Context) {
lc := ctl.lc
var req dtos.DockerConfigUpdateRequest
req.Id = c.Param(UrlParamDockerConfigId)
if err := c.ShouldBind(&req); err != nil {
httphelper.RenderFail(c, errort.NewCommonErr(errort.DefaultReqParamsError, err), c.Writer, lc)
return
}
edgeXErr := ctl.getDriverLibApp().DownConfigUpdate(c, req)
if edgeXErr != nil {
httphelper.RenderFail(c, edgeXErr, c.Writer, lc)
return
}
httphelper.ResultSuccess(nil, c.Writer, lc)
}
// @Tags 镜像仓库管理
// @Summary 删除仓库信息
// @Produce json
// @Param dockerConfigId path string true "镜像ID"
// @Success 200 {object} httphelper.CommonResponse
// @Router /api/v1/docker-configs/:dockerConfigId [delete]
func (ctl *controller) DockerConfigDelete(c *gin.Context) {
lc := ctl.lc
id := c.Param(UrlParamDockerConfigId)
edgeXErr := ctl.getDriverLibApp().DownConfigDel(c, id)
if edgeXErr != nil {
httphelper.RenderFail(c, edgeXErr, c.Writer, lc)
return
}
httphelper.ResultSuccess(nil, c.Writer, lc)
}

View File

@ -0,0 +1,15 @@
/*******************************************************************************
* Copyright 2017.
*
* 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 gateway

View File

@ -0,0 +1,38 @@
/*******************************************************************************
* Copyright 2017.
*
* 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 gateway
import (
"github.com/gin-gonic/gin"
"github.com/winc-link/hummingbird/internal/pkg/errort"
"github.com/winc-link/hummingbird/internal/pkg/httphelper"
)
func (ctl *controller) EkuiperScene(c *gin.Context) {
lc := ctl.lc
req := make(map[string]interface{})
if err := c.ShouldBind(&req); err != nil {
httphelper.RenderFail(c, errort.NewCommonErr(errort.DefaultReqParamsError, err), c.Writer, lc)
return
}
lc.Info("scene req....", req)
edgeXErr := ctl.getSceneApp().EkuiperNotify(c, req)
if edgeXErr != nil {
httphelper.RenderFail(c, edgeXErr, c.Writer, lc)
return
}
httphelper.ResultSuccess(nil, c.Writer, lc)
}

Some files were not shown because too many files have changed in this diff Show More