add mqtt broker and hummingbird
parent
d3778129fc
commit
7769b1da62
|
@ -1,7 +1,4 @@
|
|||
.idea
|
||||
vendor
|
||||
logs
|
||||
edge-db-data
|
||||
hummingbird
|
||||
mqtt-broker
|
||||
go.sum
|
||||
|
|
|
@ -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"]
|
|
@ -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())
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
// +build !windows
|
||||
|
||||
package mqttd
|
||||
|
||||
var (
|
||||
DefaultConfigDir = "./res/"
|
||||
)
|
||||
|
||||
func GetDefaultConfigDir() (string, error) {
|
||||
return DefaultConfigDir, nil
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
packages:
|
||||
- admin
|
||||
# - federation
|
||||
- aplugin
|
||||
# for external plugin, use full import path
|
||||
# - gitlab.com/tedge/edgex/internal/thummingbird/mqttbroker/plugin/prometheus
|
|
@ -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-----
|
|
@ -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 client’s 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 subscription’s 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
|
||||
|
|
@ -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 client’s 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 subscription’s 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
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
# This is a sample plain password file for the auth plugin.
|
||||
- username: root
|
||||
password: root
|
|
@ -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-----
|
|
@ -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-----
|
|
@ -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-----
|
|
@ -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
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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
|
||||
|
||||
}
|
|
@ -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",
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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(¶ms.runtime, "runtime", "", "", "")
|
||||
f.StringVarP(¶ms.net, "net", "", runMode, "")
|
||||
f.StringArrayVarP(¶ms.env, "env", "e", []string{}, "")
|
||||
f.StringArrayVarP(¶ms.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)
|
||||
}
|
|
@ -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,
|
||||
}
|
||||
}
|
|
@ -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()
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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,
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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),
|
||||
}
|
||||
}
|
|
@ -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]`
|
||||
}
|
|
@ -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)
|
||||
//}
|
|
@ -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)
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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 ""
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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,
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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()
|
||||
}
|
|
@ -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
|
||||
|
||||
}
|
|
@ -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,
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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()
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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)
|
||||
//}
|
|
@ -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)
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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
|
|
@ -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
Loading…
Reference in New Issue