modbus驱采映射服务功能调整重构
1,添加驱动采集获取和写入接口 2,创建服务接口调整接收驱采字节数组 mqtt客户端功能代码调整,未完
This commit is contained in:
parent
4dd10ccddb
commit
0b20dcdf7a
136
main.go
136
main.go
|
@ -6,23 +6,22 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/eclipse/paho.golang/autopaho"
|
|
||||||
"github.com/eclipse/paho.golang/paho"
|
|
||||||
"google.golang.org/protobuf/proto"
|
|
||||||
"joylink.club/iot/config"
|
"joylink.club/iot/config"
|
||||||
"joylink.club/iot/mqtt"
|
"joylink.club/iot/mqtt"
|
||||||
mproto "joylink.club/iot/mqtt/proto"
|
mproto "joylink.club/iot/mqtt/proto"
|
||||||
|
"joylink.club/iot/service"
|
||||||
|
"joylink.club/iot/service/proto"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
slog.SetDefault(slog.New(slog.NewJSONHandler(os.Stderr, &slog.HandlerOptions{
|
// slog.SetDefault(slog.New(slog.NewJSONHandler(os.Stderr, &slog.HandlerOptions{
|
||||||
Level: slog.LevelDebug,
|
// Level: slog.LevelDebug,
|
||||||
AddSource: false,
|
// AddSource: false,
|
||||||
})))
|
// })))
|
||||||
config.LoadConfig()
|
config.LoadConfig()
|
||||||
mqttcfg := config.Cfg.Mqtt
|
mqttcfg := config.Cfg.Mqtt
|
||||||
mqtt.BuildTopics(mqttcfg.Topic.App, mqttcfg.ClientId)
|
cmc := &mqtt.IotMqttConfig{
|
||||||
cmc := &mqtt.ClientManageConfig{
|
AppId: mqttcfg.Topic.App,
|
||||||
BrokerUrl: mqttcfg.Address,
|
BrokerUrl: mqttcfg.Address,
|
||||||
ClientId: mqttcfg.ClientId,
|
ClientId: mqttcfg.ClientId,
|
||||||
Username: mqttcfg.Username,
|
Username: mqttcfg.Username,
|
||||||
|
@ -30,14 +29,6 @@ func main() {
|
||||||
KeepAlive: mqttcfg.KeepAlive,
|
KeepAlive: mqttcfg.KeepAlive,
|
||||||
ConnectRetryDelay: mqttcfg.ConnectRetryDelay,
|
ConnectRetryDelay: mqttcfg.ConnectRetryDelay,
|
||||||
ConnectTimeout: mqttcfg.ConnectTimeout,
|
ConnectTimeout: mqttcfg.ConnectTimeout,
|
||||||
OnConnectionUp: func(*autopaho.ConnectionManager, *paho.Connack) {
|
|
||||||
slog.Info("MQTT连接成功")
|
|
||||||
// err := mqtt.SubIotServiceState(mqtt.GetIotServiceStateTopic())
|
|
||||||
// if err != nil {
|
|
||||||
// slog.Error("订阅IotServiceState失败", "error", err)
|
|
||||||
// os.Exit(1)
|
|
||||||
// }
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
err := mqtt.Start(cmc)
|
err := mqtt.Start(cmc)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -46,24 +37,6 @@ func main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
time.Sleep(time.Second * 3)
|
time.Sleep(time.Second * 3)
|
||||||
err = mqtt.SubIotServiceState(mqtt.GetIotServiceStateTopic())
|
|
||||||
if err != nil {
|
|
||||||
slog.Error("订阅IotServiceState失败", "error", err)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
i := 0
|
|
||||||
mqtt.RegisterHandler(mqtt.GetIotServiceStateTopic(), func(m *paho.Publish) {
|
|
||||||
iss := &mproto.IotServiceState{}
|
|
||||||
err := proto.Unmarshal(m.Payload, iss)
|
|
||||||
if err != nil {
|
|
||||||
slog.Error("SubIotServiceState proto.Unmarshal异常", "error", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
slog.Debug("收到IotServiceState发布消息", "state", iss)
|
|
||||||
i++
|
|
||||||
fmt.Printf("%v次处理IotServiceState: %v\n", i, iss)
|
|
||||||
})
|
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
for {
|
for {
|
||||||
|
@ -75,51 +48,54 @@ func main() {
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
mds, err := service.NewModbusQcService(&proto.ModbusConfig{
|
||||||
|
Url: "tcp://127.0.0.1:502",
|
||||||
|
UnitId: 1,
|
||||||
|
Timeout: 500,
|
||||||
|
Interval: 1000,
|
||||||
|
Mapping: []*proto.ModbusDcMapping{
|
||||||
|
{
|
||||||
|
// Function: proto.Modbus_ReadHoldingRegister,
|
||||||
|
Function: proto.Modbus_ReadCoil,
|
||||||
|
Addr: 0,
|
||||||
|
Quantity: 16,
|
||||||
|
Type: proto.DataType_CollectTable,
|
||||||
|
Start: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Function: proto.Modbus_WriteCoils,
|
||||||
|
Addr: 16,
|
||||||
|
Quantity: 16,
|
||||||
|
Type: proto.DataType_DriveTable,
|
||||||
|
WriteStrategy: proto.Modbus_OnUpdate,
|
||||||
|
Start: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}, make([]byte, 2), make([]byte, 2))
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
i := 0
|
||||||
|
for {
|
||||||
|
c := mds.GetCjBytes()
|
||||||
|
fmt.Printf("采集数据: %v\n", c)
|
||||||
|
i++
|
||||||
|
if i%3 == 0 {
|
||||||
|
idx := i % 8
|
||||||
|
err := mds.WriteQdBytes([]byte{byte(1 << idx), byte(3 << idx)})
|
||||||
|
if err != nil {
|
||||||
|
slog.Error("设置驱动数据失败", "error", err)
|
||||||
|
} else {
|
||||||
|
fmt.Printf("设置驱动数据成功: %v\n", mds.GetQdBytes())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
time.Sleep(time.Second)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
time.Sleep(time.Minute)
|
time.Sleep(time.Minute)
|
||||||
// dc := model.NewDC(make([]byte, 2), make([]byte, 2))
|
|
||||||
// mds, err := service.NewModbusQcService(&proto.ModbusConfig{
|
|
||||||
// Url: "tcp://127.0.0.1:502",
|
|
||||||
// UnitId: 2,
|
|
||||||
// Timeout: 500,
|
|
||||||
// Interval: 1000,
|
|
||||||
// Mapping: []*proto.ModbusDcMapping{
|
|
||||||
// {
|
|
||||||
// // Function: proto.Modbus_ReadHoldingRegister,
|
|
||||||
// Function: proto.Modbus_ReadCoil,
|
|
||||||
// Addr: 0,
|
|
||||||
// Quantity: 16,
|
|
||||||
// Type: proto.DataType_CollectTable,
|
|
||||||
// Start: 0,
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// Function: proto.Modbus_WriteCoils,
|
|
||||||
// Addr: 16,
|
|
||||||
// Quantity: 16,
|
|
||||||
// Type: proto.DataType_DriveTable,
|
|
||||||
// WriteStrategy: proto.Modbus_OnUpdate,
|
|
||||||
// Start: 0,
|
|
||||||
// },
|
|
||||||
// },
|
|
||||||
// }, dc)
|
|
||||||
// if err != nil {
|
|
||||||
// panic(err)
|
|
||||||
// }
|
|
||||||
|
|
||||||
// go func() {
|
mds.Stop()
|
||||||
// i := 0
|
|
||||||
// for {
|
|
||||||
// c := dc.GetCollect()
|
|
||||||
// fmt.Printf("采集数据: %v\n", c)
|
|
||||||
// i++
|
|
||||||
// if i%3 == 0 {
|
|
||||||
// idx := i % 8
|
|
||||||
// dc.UpdateDriveByBytes(0, []byte{byte(1 << idx)})
|
|
||||||
// fmt.Printf("设置驱动数据: %v\n", dc.GetDrive())
|
|
||||||
// }
|
|
||||||
// time.Sleep(time.Second)
|
|
||||||
// }
|
|
||||||
// }()
|
|
||||||
|
|
||||||
// time.Sleep(time.Minute * 2)
|
|
||||||
// mds.Stop()
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,23 +0,0 @@
|
||||||
package modbus
|
|
||||||
|
|
||||||
// 功能码
|
|
||||||
type FunctionCode int
|
|
||||||
|
|
||||||
const (
|
|
||||||
// 读线圈
|
|
||||||
FCReadCoil FunctionCode = 0x01
|
|
||||||
// 读离散输入
|
|
||||||
FCReadDiscreteInput FunctionCode = 0x02
|
|
||||||
// 读多个寄存器
|
|
||||||
FCReadHoldingRegister FunctionCode = 0x03
|
|
||||||
// 读输入寄存器
|
|
||||||
FCReadInputRegister FunctionCode = 0x04
|
|
||||||
// 写单个线圈
|
|
||||||
FCWriteSingleCoil FunctionCode = 0x05
|
|
||||||
// 写单个寄存器
|
|
||||||
FCWriteSingleRegister FunctionCode = 0x06
|
|
||||||
// 写多个线圈
|
|
||||||
FCWriteMultipleCoil FunctionCode = 0x0F
|
|
||||||
// 写多个寄存器
|
|
||||||
FCWriteMultipleRegister FunctionCode = 0x10
|
|
||||||
)
|
|
|
@ -1,28 +0,0 @@
|
||||||
package mqtt
|
|
||||||
|
|
||||||
import "joylink.club/iot/mqtt/proto"
|
|
||||||
|
|
||||||
type IotServiceStateHandler func(state *proto.IotServiceState)
|
|
||||||
|
|
||||||
type IotService interface {
|
|
||||||
PubIotServiceState(state *proto.IotServiceState)
|
|
||||||
PubIotQdData(qd *proto.IotQd)
|
|
||||||
SubIotQd()
|
|
||||||
RegIotQd(h func(qd *proto.IotQd))
|
|
||||||
PubIotCjData(cj *proto.IotCj)
|
|
||||||
SubIotCj()
|
|
||||||
RegIotCj(h func(cj *proto.IotCj))
|
|
||||||
SubIotReq(cmd *proto.IotServiceReq)
|
|
||||||
}
|
|
||||||
|
|
||||||
type Service interface {
|
|
||||||
SubIotServiceState()
|
|
||||||
RegIotServiceState(h func(state *proto.IotServiceState))
|
|
||||||
PubIotQdData(qd *proto.IotQd)
|
|
||||||
SubIotQd()
|
|
||||||
RegIotQd(h func(qd *proto.IotQd))
|
|
||||||
PubIotCjData(cj *proto.IotCj)
|
|
||||||
SubIotCj()
|
|
||||||
RegIotCj(h func(cj *proto.IotCj))
|
|
||||||
ReqIotService(cmd *proto.IotServiceReq)
|
|
||||||
}
|
|
196
mqtt/client.go
196
mqtt/client.go
|
@ -2,22 +2,30 @@ package mqtt
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"fmt"
|
||||||
"log/slog"
|
"log/slog"
|
||||||
|
|
||||||
"github.com/eclipse/paho.golang/autopaho"
|
"github.com/eclipse/paho.golang/autopaho"
|
||||||
"github.com/eclipse/paho.golang/paho"
|
"github.com/eclipse/paho.golang/paho"
|
||||||
"google.golang.org/protobuf/proto"
|
"google.golang.org/protobuf/proto"
|
||||||
|
"google.golang.org/protobuf/reflect/protoreflect"
|
||||||
mproto "joylink.club/iot/mqtt/proto"
|
mproto "joylink.club/iot/mqtt/proto"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Manager struct {
|
var iotcli *IotClient
|
||||||
cc *autopaho.ClientConfig
|
|
||||||
cm *autopaho.ConnectionManager
|
type IotClient struct {
|
||||||
|
cmc *IotMqttConfig
|
||||||
|
cc *autopaho.ClientConfig
|
||||||
|
cm *autopaho.ConnectionManager
|
||||||
}
|
}
|
||||||
|
|
||||||
var manager *Manager
|
// 初始化并启动MQTT客户端服务
|
||||||
|
func Start(cmc *IotMqttConfig) error {
|
||||||
func Start(cmc *ClientManageConfig) error {
|
if err := checkConfig(cmc); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
BuildTopics(cmc.AppId, cmc.ClientId)
|
||||||
cc, err := cmc.tryInto()
|
cc, err := cmc.tryInto()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -26,62 +34,146 @@ func Start(cmc *ClientManageConfig) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
manager = &Manager{
|
iotcli = &IotClient{
|
||||||
cc: cc,
|
cmc: cmc,
|
||||||
cm: cm,
|
cc: cc,
|
||||||
|
cm: cm,
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func Publish(ctx context.Context, publish *paho.Publish) (*paho.PublishResponse, error) {
|
func checkConfig(cmc *IotMqttConfig) error {
|
||||||
return manager.cm.Publish(ctx, publish)
|
if cmc.AppId == "" {
|
||||||
}
|
return fmt.Errorf("应用编号不能为空")
|
||||||
|
|
||||||
func PubIotServiceState(s *mproto.IotServiceState) error {
|
|
||||||
return manager.PubIotServiceState(s)
|
|
||||||
}
|
|
||||||
|
|
||||||
func SubIotServiceState(topic string) error {
|
|
||||||
return manager.SubIotServiceState(topic)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *Manager) PubIotServiceState(s *mproto.IotServiceState) error {
|
|
||||||
if s == nil {
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
slog.Debug("PubIotServiceState", "topic", GetIotServiceStateTopic(), "state", s)
|
if cmc.ClientId == "" {
|
||||||
b, err := proto.Marshal(s)
|
return fmt.Errorf("客户端编号不能为空")
|
||||||
|
}
|
||||||
|
if cmc.BrokerUrl == "" {
|
||||||
|
return fmt.Errorf("MQTT代理服务地址不能为空")
|
||||||
|
}
|
||||||
|
if cmc.Username == "" {
|
||||||
|
return fmt.Errorf("MQTT用户名不能为空")
|
||||||
|
}
|
||||||
|
if cmc.Password == "" {
|
||||||
|
return fmt.Errorf("MQTT密码不能为空")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 发布IOT服务状态
|
||||||
|
func PubIotServiceState(s *mproto.IotServiceState) error {
|
||||||
|
return pub(GetIotServiceStateTopic(), s)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 发布IOT采集数据
|
||||||
|
func PubIotCjData(cj *mproto.IotCj) error {
|
||||||
|
return pub(GetCjTopic(), cj)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 发布IOT驱动数据
|
||||||
|
func PubIotQdData(qd *mproto.IotQd) error {
|
||||||
|
return pub(GetCjTopic(), qd)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 注册IOT采集数据处理
|
||||||
|
func RegIotCjHandler(h func(cj *mproto.IotCj)) {
|
||||||
|
iotcli.cc.Router.RegisterHandler(GetCmdTopic(), func(p *paho.Publish) {
|
||||||
|
cmd := &mproto.IotCj{}
|
||||||
|
err := proto.Unmarshal(p.Payload, cmd)
|
||||||
|
if err != nil {
|
||||||
|
slog.Error("采集数据proto.Unmarshal异常", "error", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
h(cmd)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 注册IOT驱动数据处理
|
||||||
|
func RegIotQdHandler(h func(qd *mproto.IotQd)) {
|
||||||
|
iotcli.cc.Router.RegisterHandler(GetCmdTopic(), func(p *paho.Publish) {
|
||||||
|
cmd := &mproto.IotQd{}
|
||||||
|
err := proto.Unmarshal(p.Payload, cmd)
|
||||||
|
if err != nil {
|
||||||
|
slog.Error("驱动数据proto.Unmarshal异常", "error", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
h(cmd)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 注册IOT日志查询请求处理
|
||||||
|
func RegIotLogReqHandler(h func(cmd *mproto.IotServiceLogReq)) {
|
||||||
|
iotcli.cc.Router.RegisterHandler(GetCmdTopic(), func(p *paho.Publish) {
|
||||||
|
cmd := &mproto.IotServiceLogReq{}
|
||||||
|
err := proto.Unmarshal(p.Payload, cmd)
|
||||||
|
if err != nil {
|
||||||
|
slog.Error("RegIotReqHandler proto.Unmarshal异常", "error", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
h(cmd)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 注销IOT处理
|
||||||
|
func UnregHandler(topic string) {
|
||||||
|
iotcli.cc.Router.UnregisterHandler(topic)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 注销所有IOT处理
|
||||||
|
func UnregAllHandler() {
|
||||||
|
iotcli.cc.Router = paho.NewStandardRouter()
|
||||||
|
}
|
||||||
|
|
||||||
|
func subIotQc() {
|
||||||
|
slog.Info("订阅Iot驱采")
|
||||||
|
sub(GetCjTopic()) // 订阅采集
|
||||||
|
sub(GetQdTopic()) // 订阅驱动
|
||||||
|
}
|
||||||
|
|
||||||
|
// 发起订阅
|
||||||
|
func sub(topic string) {
|
||||||
|
slog.Info("发起订阅", "topic", topic)
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
defer cancel()
|
||||||
|
_, err := iotcli.cm.Subscribe(ctx, &paho.Subscribe{
|
||||||
|
Subscriptions: []paho.SubscribeOptions{
|
||||||
|
{
|
||||||
|
Topic: topic,
|
||||||
|
QoS: 0,
|
||||||
|
NoLocal: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
slog.Error("订阅失败", "topic", topic, "error", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 发布数据
|
||||||
|
func pub(topic string, data protoreflect.ProtoMessage) error {
|
||||||
|
if data == nil {
|
||||||
|
return fmt.Errorf("发布数据引用为nil")
|
||||||
|
}
|
||||||
|
b, err := proto.Marshal(data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
_, err = m.cm.Publish(context.Background(), &paho.Publish{
|
switch topic {
|
||||||
Topic: GetIotServiceStateTopic(),
|
case GetIotServiceStateTopic():
|
||||||
|
slog.Debug("发布Iot服务状态", "topic", topic, "data", data)
|
||||||
|
case GetCjTopic():
|
||||||
|
slog.Debug("发布采集数据", "topic", topic, "data", data)
|
||||||
|
case GetQdTopic():
|
||||||
|
slog.Debug("发布驱动数据", "topic", topic, "data", data)
|
||||||
|
default:
|
||||||
|
slog.Error("未知发布主题", "topic", topic, "data", data)
|
||||||
|
return fmt.Errorf("未知发布主题: topic=%s", topic)
|
||||||
|
}
|
||||||
|
_, err = iotcli.cm.Publish(context.Background(), &paho.Publish{
|
||||||
|
Topic: topic,
|
||||||
QoS: 0,
|
QoS: 0,
|
||||||
Payload: b,
|
Payload: b,
|
||||||
})
|
})
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func RegisterHandler(topic string, h func(m *paho.Publish)) {
|
|
||||||
manager.cc.Router.RegisterHandler(topic, h)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *Manager) RegisterHandler(topic string, h func(m *paho.Publish)) {
|
|
||||||
m.cc.Router.RegisterHandler(topic, h)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *Manager) SubIotServiceState(topic string) error {
|
|
||||||
slog.Debug("订阅IotServiceState", "topic", topic)
|
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
|
||||||
defer cancel()
|
|
||||||
_, err := m.cm.Subscribe(ctx, &paho.Subscribe{
|
|
||||||
Subscriptions: []paho.SubscribeOptions{
|
|
||||||
{
|
|
||||||
Topic: topic,
|
|
||||||
QoS: 0,
|
|
||||||
// NoLocal: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
|
@ -10,7 +10,8 @@ import (
|
||||||
"github.com/eclipse/paho.golang/paho"
|
"github.com/eclipse/paho.golang/paho"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ClientManageConfig struct {
|
type IotMqttConfig struct {
|
||||||
|
AppId string // 所属应用编号
|
||||||
BrokerUrl string // Broker地址
|
BrokerUrl string // Broker地址
|
||||||
ClientId string // 客户端ID
|
ClientId string // 客户端ID
|
||||||
Username string // 用户名
|
Username string // 用户名
|
||||||
|
@ -18,10 +19,9 @@ type ClientManageConfig struct {
|
||||||
KeepAlive uint16 // 保活时间间隔,单位s,默认为60
|
KeepAlive uint16 // 保活时间间隔,单位s,默认为60
|
||||||
ConnectRetryDelay uint16 // 连接重试延时,单位s,默认为3
|
ConnectRetryDelay uint16 // 连接重试延时,单位s,默认为3
|
||||||
ConnectTimeout uint16 // 连接操作超时,单位s,默认为3
|
ConnectTimeout uint16 // 连接操作超时,单位s,默认为3
|
||||||
OnConnectionUp func(*autopaho.ConnectionManager, *paho.Connack)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *ClientManageConfig) tryInto() (*autopaho.ClientConfig, error) {
|
func (c *IotMqttConfig) tryInto() (*autopaho.ClientConfig, error) {
|
||||||
addr, err := url.Parse(c.BrokerUrl)
|
addr, err := url.Parse(c.BrokerUrl)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("Mqtt.Address格式错误, %s: %w", c.BrokerUrl, err)
|
return nil, fmt.Errorf("Mqtt.Address格式错误, %s: %w", c.BrokerUrl, err)
|
||||||
|
@ -42,7 +42,10 @@ func (c *ClientManageConfig) tryInto() (*autopaho.ClientConfig, error) {
|
||||||
KeepAlive: c.KeepAlive,
|
KeepAlive: c.KeepAlive,
|
||||||
ConnectRetryDelay: time.Duration(c.ConnectRetryDelay) * time.Second,
|
ConnectRetryDelay: time.Duration(c.ConnectRetryDelay) * time.Second,
|
||||||
ConnectTimeout: time.Duration(c.ConnectTimeout) * time.Second,
|
ConnectTimeout: time.Duration(c.ConnectTimeout) * time.Second,
|
||||||
OnConnectionUp: c.OnConnectionUp,
|
OnConnectionUp: func(*autopaho.ConnectionManager, *paho.Connack) {
|
||||||
|
slog.Info("MQTT连接成功")
|
||||||
|
subIotQc()
|
||||||
|
},
|
||||||
OnConnectError: func(err error) {
|
OnConnectError: func(err error) {
|
||||||
slog.Error("MQTT连接失败", "error", err)
|
slog.Error("MQTT连接失败", "error", err)
|
||||||
},
|
},
|
||||||
|
|
|
@ -66,49 +66,6 @@ func (ServiceState) EnumDescriptor() ([]byte, []int) {
|
||||||
return file_mqtt_mqtt_proto_rawDescGZIP(), []int{0}
|
return file_mqtt_mqtt_proto_rawDescGZIP(), []int{0}
|
||||||
}
|
}
|
||||||
|
|
||||||
type ServiceRequest int32
|
|
||||||
|
|
||||||
const (
|
|
||||||
ServiceRequest_Logs ServiceRequest = 0 // 日志
|
|
||||||
)
|
|
||||||
|
|
||||||
// Enum value maps for ServiceRequest.
|
|
||||||
var (
|
|
||||||
ServiceRequest_name = map[int32]string{
|
|
||||||
0: "Logs",
|
|
||||||
}
|
|
||||||
ServiceRequest_value = map[string]int32{
|
|
||||||
"Logs": 0,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
func (x ServiceRequest) Enum() *ServiceRequest {
|
|
||||||
p := new(ServiceRequest)
|
|
||||||
*p = x
|
|
||||||
return p
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x ServiceRequest) String() string {
|
|
||||||
return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ServiceRequest) Descriptor() protoreflect.EnumDescriptor {
|
|
||||||
return file_mqtt_mqtt_proto_enumTypes[1].Descriptor()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ServiceRequest) Type() protoreflect.EnumType {
|
|
||||||
return &file_mqtt_mqtt_proto_enumTypes[1]
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x ServiceRequest) Number() protoreflect.EnumNumber {
|
|
||||||
return protoreflect.EnumNumber(x)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deprecated: Use ServiceRequest.Descriptor instead.
|
|
||||||
func (ServiceRequest) EnumDescriptor() ([]byte, []int) {
|
|
||||||
return file_mqtt_mqtt_proto_rawDescGZIP(), []int{1}
|
|
||||||
}
|
|
||||||
|
|
||||||
// IOT服务状态
|
// IOT服务状态
|
||||||
type IotServiceState struct {
|
type IotServiceState struct {
|
||||||
state protoimpl.MessageState
|
state protoimpl.MessageState
|
||||||
|
@ -165,18 +122,18 @@ func (x *IotServiceState) GetState() ServiceState {
|
||||||
return ServiceState_Normal
|
return ServiceState_Normal
|
||||||
}
|
}
|
||||||
|
|
||||||
// IOT服务请求
|
// IOT服务获取日志请求
|
||||||
type IotServiceReq struct {
|
type IotServiceLogReq struct {
|
||||||
state protoimpl.MessageState
|
state protoimpl.MessageState
|
||||||
sizeCache protoimpl.SizeCache
|
sizeCache protoimpl.SizeCache
|
||||||
unknownFields protoimpl.UnknownFields
|
unknownFields protoimpl.UnknownFields
|
||||||
|
|
||||||
Code string `protobuf:"bytes,1,opt,name=code,proto3" json:"code,omitempty"` // 服务编号
|
Code string `protobuf:"bytes,1,opt,name=code,proto3" json:"code,omitempty"` // 服务编号
|
||||||
Req ServiceRequest `protobuf:"varint,2,opt,name=req,proto3,enum=mqtt_api.ServiceRequest" json:"req,omitempty"` // 服务请求
|
Count int32 `protobuf:"varint,3,opt,name=count,proto3" json:"count,omitempty"` // 日志条数
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *IotServiceReq) Reset() {
|
func (x *IotServiceLogReq) Reset() {
|
||||||
*x = IotServiceReq{}
|
*x = IotServiceLogReq{}
|
||||||
if protoimpl.UnsafeEnabled {
|
if protoimpl.UnsafeEnabled {
|
||||||
mi := &file_mqtt_mqtt_proto_msgTypes[1]
|
mi := &file_mqtt_mqtt_proto_msgTypes[1]
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
@ -184,13 +141,13 @@ func (x *IotServiceReq) Reset() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *IotServiceReq) String() string {
|
func (x *IotServiceLogReq) String() string {
|
||||||
return protoimpl.X.MessageStringOf(x)
|
return protoimpl.X.MessageStringOf(x)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (*IotServiceReq) ProtoMessage() {}
|
func (*IotServiceLogReq) ProtoMessage() {}
|
||||||
|
|
||||||
func (x *IotServiceReq) ProtoReflect() protoreflect.Message {
|
func (x *IotServiceLogReq) ProtoReflect() protoreflect.Message {
|
||||||
mi := &file_mqtt_mqtt_proto_msgTypes[1]
|
mi := &file_mqtt_mqtt_proto_msgTypes[1]
|
||||||
if protoimpl.UnsafeEnabled && x != nil {
|
if protoimpl.UnsafeEnabled && x != nil {
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
@ -202,36 +159,37 @@ func (x *IotServiceReq) ProtoReflect() protoreflect.Message {
|
||||||
return mi.MessageOf(x)
|
return mi.MessageOf(x)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Deprecated: Use IotServiceReq.ProtoReflect.Descriptor instead.
|
// Deprecated: Use IotServiceLogReq.ProtoReflect.Descriptor instead.
|
||||||
func (*IotServiceReq) Descriptor() ([]byte, []int) {
|
func (*IotServiceLogReq) Descriptor() ([]byte, []int) {
|
||||||
return file_mqtt_mqtt_proto_rawDescGZIP(), []int{1}
|
return file_mqtt_mqtt_proto_rawDescGZIP(), []int{1}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *IotServiceReq) GetCode() string {
|
func (x *IotServiceLogReq) GetCode() string {
|
||||||
if x != nil {
|
if x != nil {
|
||||||
return x.Code
|
return x.Code
|
||||||
}
|
}
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *IotServiceReq) GetReq() ServiceRequest {
|
func (x *IotServiceLogReq) GetCount() int32 {
|
||||||
if x != nil {
|
if x != nil {
|
||||||
return x.Req
|
return x.Count
|
||||||
}
|
}
|
||||||
return ServiceRequest_Logs
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
// IOT服务响应
|
// IOT服务日志响应
|
||||||
type IotServiceResp struct {
|
type IotServiceLogResp struct {
|
||||||
state protoimpl.MessageState
|
state protoimpl.MessageState
|
||||||
sizeCache protoimpl.SizeCache
|
sizeCache protoimpl.SizeCache
|
||||||
unknownFields protoimpl.UnknownFields
|
unknownFields protoimpl.UnknownFields
|
||||||
|
|
||||||
Code string `protobuf:"bytes,1,opt,name=code,proto3" json:"code,omitempty"` // 服务编号
|
Code string `protobuf:"bytes,1,opt,name=code,proto3" json:"code,omitempty"` // 服务编号
|
||||||
|
Logs []string `protobuf:"bytes,4,rep,name=logs,proto3" json:"logs,omitempty"` // 日志
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *IotServiceResp) Reset() {
|
func (x *IotServiceLogResp) Reset() {
|
||||||
*x = IotServiceResp{}
|
*x = IotServiceLogResp{}
|
||||||
if protoimpl.UnsafeEnabled {
|
if protoimpl.UnsafeEnabled {
|
||||||
mi := &file_mqtt_mqtt_proto_msgTypes[2]
|
mi := &file_mqtt_mqtt_proto_msgTypes[2]
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
@ -239,13 +197,13 @@ func (x *IotServiceResp) Reset() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *IotServiceResp) String() string {
|
func (x *IotServiceLogResp) String() string {
|
||||||
return protoimpl.X.MessageStringOf(x)
|
return protoimpl.X.MessageStringOf(x)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (*IotServiceResp) ProtoMessage() {}
|
func (*IotServiceLogResp) ProtoMessage() {}
|
||||||
|
|
||||||
func (x *IotServiceResp) ProtoReflect() protoreflect.Message {
|
func (x *IotServiceLogResp) ProtoReflect() protoreflect.Message {
|
||||||
mi := &file_mqtt_mqtt_proto_msgTypes[2]
|
mi := &file_mqtt_mqtt_proto_msgTypes[2]
|
||||||
if protoimpl.UnsafeEnabled && x != nil {
|
if protoimpl.UnsafeEnabled && x != nil {
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
@ -257,18 +215,25 @@ func (x *IotServiceResp) ProtoReflect() protoreflect.Message {
|
||||||
return mi.MessageOf(x)
|
return mi.MessageOf(x)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Deprecated: Use IotServiceResp.ProtoReflect.Descriptor instead.
|
// Deprecated: Use IotServiceLogResp.ProtoReflect.Descriptor instead.
|
||||||
func (*IotServiceResp) Descriptor() ([]byte, []int) {
|
func (*IotServiceLogResp) Descriptor() ([]byte, []int) {
|
||||||
return file_mqtt_mqtt_proto_rawDescGZIP(), []int{2}
|
return file_mqtt_mqtt_proto_rawDescGZIP(), []int{2}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *IotServiceResp) GetCode() string {
|
func (x *IotServiceLogResp) GetCode() string {
|
||||||
if x != nil {
|
if x != nil {
|
||||||
return x.Code
|
return x.Code
|
||||||
}
|
}
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (x *IotServiceLogResp) GetLogs() []string {
|
||||||
|
if x != nil {
|
||||||
|
return x.Logs
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// IOT驱动数据
|
// IOT驱动数据
|
||||||
type IotQd struct {
|
type IotQd struct {
|
||||||
state protoimpl.MessageState
|
state protoimpl.MessageState
|
||||||
|
@ -391,26 +356,24 @@ var file_mqtt_mqtt_proto_rawDesc = []byte{
|
||||||
0x64, 0x65, 0x12, 0x2c, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28,
|
0x64, 0x65, 0x12, 0x2c, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28,
|
||||||
0x0e, 0x32, 0x16, 0x2e, 0x6d, 0x71, 0x74, 0x74, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x53, 0x65, 0x72,
|
0x0e, 0x32, 0x16, 0x2e, 0x6d, 0x71, 0x74, 0x74, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x53, 0x65, 0x72,
|
||||||
0x76, 0x69, 0x63, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65,
|
0x76, 0x69, 0x63, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65,
|
||||||
0x22, 0x4f, 0x0a, 0x0d, 0x49, 0x6f, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x52, 0x65,
|
0x22, 0x3c, 0x0a, 0x10, 0x49, 0x6f, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4c, 0x6f,
|
||||||
0x71, 0x12, 0x12, 0x0a, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,
|
0x67, 0x52, 0x65, 0x71, 0x12, 0x12, 0x0a, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01,
|
||||||
0x04, 0x63, 0x6f, 0x64, 0x65, 0x12, 0x2a, 0x0a, 0x03, 0x72, 0x65, 0x71, 0x18, 0x02, 0x20, 0x01,
|
0x28, 0x09, 0x52, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x6f, 0x75, 0x6e,
|
||||||
0x28, 0x0e, 0x32, 0x18, 0x2e, 0x6d, 0x71, 0x74, 0x74, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x53, 0x65,
|
0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x3b,
|
||||||
0x72, 0x76, 0x69, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x03, 0x72, 0x65,
|
0x0a, 0x11, 0x49, 0x6f, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4c, 0x6f, 0x67, 0x52,
|
||||||
0x71, 0x22, 0x24, 0x0a, 0x0e, 0x49, 0x6f, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x52,
|
|
||||||
0x65, 0x73, 0x70, 0x12, 0x12, 0x0a, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28,
|
0x65, 0x73, 0x70, 0x12, 0x12, 0x0a, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28,
|
||||||
0x09, 0x52, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x22, 0x2f, 0x0a, 0x05, 0x49, 0x6f, 0x74, 0x51, 0x64,
|
0x09, 0x52, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6c, 0x6f, 0x67, 0x73, 0x18,
|
||||||
0x12, 0x12, 0x0a, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04,
|
0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x04, 0x6c, 0x6f, 0x67, 0x73, 0x22, 0x2f, 0x0a, 0x05, 0x49,
|
||||||
0x63, 0x6f, 0x64, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x02, 0x20, 0x01,
|
0x6f, 0x74, 0x51, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01,
|
||||||
0x28, 0x0c, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x22, 0x2f, 0x0a, 0x05, 0x49, 0x6f, 0x74, 0x43,
|
0x28, 0x09, 0x52, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61,
|
||||||
0x6a, 0x12, 0x12, 0x0a, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,
|
0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x22, 0x2f, 0x0a, 0x05,
|
||||||
0x04, 0x63, 0x6f, 0x64, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x02, 0x20,
|
0x49, 0x6f, 0x74, 0x43, 0x6a, 0x12, 0x12, 0x0a, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20,
|
||||||
0x01, 0x28, 0x0c, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x2a, 0x27, 0x0a, 0x0c, 0x53, 0x65, 0x72,
|
0x01, 0x28, 0x09, 0x52, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, 0x74,
|
||||||
0x76, 0x69, 0x63, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x0a, 0x0a, 0x06, 0x4e, 0x6f, 0x72,
|
0x61, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x2a, 0x27, 0x0a,
|
||||||
0x6d, 0x61, 0x6c, 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x4f, 0x66, 0x66, 0x6c, 0x69, 0x6e, 0x65,
|
0x0c, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x0a, 0x0a,
|
||||||
0x10, 0x01, 0x2a, 0x1a, 0x0a, 0x0e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x52, 0x65, 0x71,
|
0x06, 0x4e, 0x6f, 0x72, 0x6d, 0x61, 0x6c, 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x4f, 0x66, 0x66,
|
||||||
0x75, 0x65, 0x73, 0x74, 0x12, 0x08, 0x0a, 0x04, 0x4c, 0x6f, 0x67, 0x73, 0x10, 0x00, 0x42, 0x0e,
|
0x6c, 0x69, 0x6e, 0x65, 0x10, 0x01, 0x42, 0x0e, 0x5a, 0x0c, 0x2e, 0x2f, 0x6d, 0x71, 0x74, 0x74,
|
||||||
0x5a, 0x0c, 0x2e, 0x2f, 0x6d, 0x71, 0x74, 0x74, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06,
|
0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||||
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -425,25 +388,23 @@ func file_mqtt_mqtt_proto_rawDescGZIP() []byte {
|
||||||
return file_mqtt_mqtt_proto_rawDescData
|
return file_mqtt_mqtt_proto_rawDescData
|
||||||
}
|
}
|
||||||
|
|
||||||
var file_mqtt_mqtt_proto_enumTypes = make([]protoimpl.EnumInfo, 2)
|
var file_mqtt_mqtt_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
|
||||||
var file_mqtt_mqtt_proto_msgTypes = make([]protoimpl.MessageInfo, 5)
|
var file_mqtt_mqtt_proto_msgTypes = make([]protoimpl.MessageInfo, 5)
|
||||||
var file_mqtt_mqtt_proto_goTypes = []interface{}{
|
var file_mqtt_mqtt_proto_goTypes = []interface{}{
|
||||||
(ServiceState)(0), // 0: mqtt_api.ServiceState
|
(ServiceState)(0), // 0: mqtt_api.ServiceState
|
||||||
(ServiceRequest)(0), // 1: mqtt_api.ServiceRequest
|
(*IotServiceState)(nil), // 1: mqtt_api.IotServiceState
|
||||||
(*IotServiceState)(nil), // 2: mqtt_api.IotServiceState
|
(*IotServiceLogReq)(nil), // 2: mqtt_api.IotServiceLogReq
|
||||||
(*IotServiceReq)(nil), // 3: mqtt_api.IotServiceReq
|
(*IotServiceLogResp)(nil), // 3: mqtt_api.IotServiceLogResp
|
||||||
(*IotServiceResp)(nil), // 4: mqtt_api.IotServiceResp
|
(*IotQd)(nil), // 4: mqtt_api.IotQd
|
||||||
(*IotQd)(nil), // 5: mqtt_api.IotQd
|
(*IotCj)(nil), // 5: mqtt_api.IotCj
|
||||||
(*IotCj)(nil), // 6: mqtt_api.IotCj
|
|
||||||
}
|
}
|
||||||
var file_mqtt_mqtt_proto_depIdxs = []int32{
|
var file_mqtt_mqtt_proto_depIdxs = []int32{
|
||||||
0, // 0: mqtt_api.IotServiceState.state:type_name -> mqtt_api.ServiceState
|
0, // 0: mqtt_api.IotServiceState.state:type_name -> mqtt_api.ServiceState
|
||||||
1, // 1: mqtt_api.IotServiceReq.req:type_name -> mqtt_api.ServiceRequest
|
1, // [1:1] is the sub-list for method output_type
|
||||||
2, // [2:2] is the sub-list for method output_type
|
1, // [1:1] is the sub-list for method input_type
|
||||||
2, // [2:2] is the sub-list for method input_type
|
1, // [1:1] is the sub-list for extension type_name
|
||||||
2, // [2:2] is the sub-list for extension type_name
|
1, // [1:1] is the sub-list for extension extendee
|
||||||
2, // [2:2] is the sub-list for extension extendee
|
0, // [0:1] is the sub-list for field type_name
|
||||||
0, // [0:2] is the sub-list for field type_name
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() { file_mqtt_mqtt_proto_init() }
|
func init() { file_mqtt_mqtt_proto_init() }
|
||||||
|
@ -465,7 +426,7 @@ func file_mqtt_mqtt_proto_init() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
file_mqtt_mqtt_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
|
file_mqtt_mqtt_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
|
||||||
switch v := v.(*IotServiceReq); i {
|
switch v := v.(*IotServiceLogReq); i {
|
||||||
case 0:
|
case 0:
|
||||||
return &v.state
|
return &v.state
|
||||||
case 1:
|
case 1:
|
||||||
|
@ -477,7 +438,7 @@ func file_mqtt_mqtt_proto_init() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
file_mqtt_mqtt_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
|
file_mqtt_mqtt_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
|
||||||
switch v := v.(*IotServiceResp); i {
|
switch v := v.(*IotServiceLogResp); i {
|
||||||
case 0:
|
case 0:
|
||||||
return &v.state
|
return &v.state
|
||||||
case 1:
|
case 1:
|
||||||
|
@ -518,7 +479,7 @@ func file_mqtt_mqtt_proto_init() {
|
||||||
File: protoimpl.DescBuilder{
|
File: protoimpl.DescBuilder{
|
||||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||||
RawDescriptor: file_mqtt_mqtt_proto_rawDesc,
|
RawDescriptor: file_mqtt_mqtt_proto_rawDesc,
|
||||||
NumEnums: 2,
|
NumEnums: 1,
|
||||||
NumMessages: 5,
|
NumMessages: 5,
|
||||||
NumExtensions: 0,
|
NumExtensions: 0,
|
||||||
NumServices: 0,
|
NumServices: 0,
|
||||||
|
|
|
@ -4,7 +4,7 @@ import "fmt"
|
||||||
|
|
||||||
const (
|
const (
|
||||||
Topic_IotServiceState string = "/%s/%s/iotss"
|
Topic_IotServiceState string = "/%s/%s/iotss"
|
||||||
Topic_IotCmd string = "/%s/%s/iotcmd"
|
Topic_IotLog string = "/%s/%s/iotlog"
|
||||||
Topic_IotQd string = "/%s/%s/iotqd"
|
Topic_IotQd string = "/%s/%s/iotqd"
|
||||||
Topic_IotCj string = "/%s/%s/iotcj"
|
Topic_IotCj string = "/%s/%s/iotcj"
|
||||||
)
|
)
|
||||||
|
@ -13,7 +13,7 @@ var topicMap = make(map[string]string, 4)
|
||||||
|
|
||||||
func BuildTopics(sysCode, iotCode string) {
|
func BuildTopics(sysCode, iotCode string) {
|
||||||
topicMap[Topic_IotServiceState] = fmt.Sprintf(Topic_IotServiceState, sysCode, iotCode)
|
topicMap[Topic_IotServiceState] = fmt.Sprintf(Topic_IotServiceState, sysCode, iotCode)
|
||||||
topicMap[Topic_IotCmd] = fmt.Sprintf(Topic_IotCmd, sysCode, iotCode)
|
topicMap[Topic_IotLog] = fmt.Sprintf(Topic_IotLog, sysCode, iotCode)
|
||||||
topicMap[Topic_IotQd] = fmt.Sprintf(Topic_IotQd, sysCode, iotCode)
|
topicMap[Topic_IotQd] = fmt.Sprintf(Topic_IotQd, sysCode, iotCode)
|
||||||
topicMap[Topic_IotCj] = fmt.Sprintf(Topic_IotCj, sysCode, iotCode)
|
topicMap[Topic_IotCj] = fmt.Sprintf(Topic_IotCj, sysCode, iotCode)
|
||||||
}
|
}
|
||||||
|
@ -23,7 +23,7 @@ func GetIotServiceStateTopic() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetCmdTopic() string {
|
func GetCmdTopic() string {
|
||||||
return topicMap[Topic_IotCmd]
|
return topicMap[Topic_IotLog]
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetQdTopic() string {
|
func GetQdTopic() string {
|
||||||
|
|
|
@ -9,34 +9,22 @@ enum ServiceState {
|
||||||
Offline = 1; // 离线
|
Offline = 1; // 离线
|
||||||
}
|
}
|
||||||
|
|
||||||
enum ServiceRequest {
|
|
||||||
Logs = 0; // 日志
|
|
||||||
}
|
|
||||||
|
|
||||||
// IOT服务状态
|
// IOT服务状态
|
||||||
message IotServiceState {
|
message IotServiceState {
|
||||||
string code = 1; // 服务编号
|
string code = 1; // 服务编号
|
||||||
ServiceState state = 2; // 服务状态
|
ServiceState state = 2; // 服务状态
|
||||||
}
|
}
|
||||||
|
|
||||||
// IOT服务请求
|
// IOT服务获取日志请求
|
||||||
message IotServiceReq {
|
message IotServiceLogReq {
|
||||||
string code = 1; // 服务编号
|
string code = 1; // 服务编号
|
||||||
ServiceRequest req = 2; // 服务请求
|
int32 count = 3; // 日志条数
|
||||||
}
|
}
|
||||||
|
|
||||||
// IOT服务响应
|
// IOT服务日志响应
|
||||||
message IotServiceResp {
|
message IotServiceLogResp {
|
||||||
string code = 1; // 服务编号
|
string code = 1; // 服务编号
|
||||||
int32 err = 2; // 错误码
|
repeated string logs = 4; // 日志
|
||||||
string errmsg = 3; // 错误信息
|
|
||||||
oneof data {
|
|
||||||
IotServiceLog logs = 4; // 日志
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
message IotServiceLog {
|
|
||||||
repeated string logs = 1; // 日志
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// IOT驱动数据
|
// IOT驱动数据
|
||||||
|
|
|
@ -1,6 +1,20 @@
|
||||||
package service
|
package service
|
||||||
|
|
||||||
// IOT物联网应用
|
// IOT驱采映射服务
|
||||||
type IotService interface {
|
type IotQcMappingService interface {
|
||||||
|
// 停止
|
||||||
Stop() error
|
Stop() error
|
||||||
|
|
||||||
|
// 获取驱动字节列表
|
||||||
|
GetQdBytes() []byte
|
||||||
|
// 获取驱动位列表
|
||||||
|
GetQdBits() []bool
|
||||||
|
// 获取采集字节列表
|
||||||
|
GetCjBytes() []byte
|
||||||
|
// 获取采集位列表
|
||||||
|
GetCjBits() []bool
|
||||||
|
// 写驱动字节列表
|
||||||
|
WriteQdBytes(bytes []byte) error
|
||||||
|
// 写采集位列表
|
||||||
|
WriteCjBytes(bytes []byte) error
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,287 +0,0 @@
|
||||||
package service
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"log/slog"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"joylink.club/iot/modbus"
|
|
||||||
"joylink.club/iot/service/model"
|
|
||||||
sproto "joylink.club/iot/service/proto"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Modbus驱采服务
|
|
||||||
type modbusQcService struct {
|
|
||||||
config *sproto.ModbusConfig
|
|
||||||
cli modbus.MasterClient
|
|
||||||
qc model.QC
|
|
||||||
cancel context.CancelFunc
|
|
||||||
done chan struct{} // 服务协程退出信号
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewModbusQcService(config *sproto.ModbusConfig, dc model.QC) (IotService, error) {
|
|
||||||
if err := checkConfig(config); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
_, ok := modbus.GetClient(config.Url)
|
|
||||||
if ok {
|
|
||||||
return nil, fmt.Errorf("modbus客户端已存在,url=%s", config.Url)
|
|
||||||
}
|
|
||||||
cli, err := modbus.NewClient(&modbus.ClientConfig{
|
|
||||||
Url: config.Url,
|
|
||||||
Timeout: config.Timeout,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.Join(err, fmt.Errorf("modbus客户端创建失败,url=%s", config.Url))
|
|
||||||
}
|
|
||||||
cli.SetUnitId(uint8(config.UnitId))
|
|
||||||
cli.SetEndianness(convertEndianness(config.Endianness))
|
|
||||||
cli.Start()
|
|
||||||
s := &modbusQcService{
|
|
||||||
config: config,
|
|
||||||
cli: cli,
|
|
||||||
qc: dc,
|
|
||||||
done: make(chan struct{}),
|
|
||||||
}
|
|
||||||
s.initOnUpdateTask()
|
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
|
||||||
go s.run(ctx)
|
|
||||||
s.cancel = cancel
|
|
||||||
return s, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *modbusQcService) initOnUpdateTask() {
|
|
||||||
mapping := m.config.Mapping
|
|
||||||
for _, mdm := range mapping {
|
|
||||||
if mdm.WriteStrategy == sproto.Modbus_OnUpdate && isWriteFunction(mdm.Function) {
|
|
||||||
et := model.DCE_Drive_Update
|
|
||||||
if mdm.Type == sproto.DataType_CollectTable {
|
|
||||||
et = model.DCE_Collect_Update
|
|
||||||
}
|
|
||||||
m.qc.On(et, func(d model.QC) {
|
|
||||||
if !m.cli.IsConnected() {
|
|
||||||
slog.Warn("Modbus驱动采集服务数据更新写入失败,modbus客户端未连接", "url", m.config.Url, "Function", mdm.Function)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
switch mdm.Function {
|
|
||||||
case sproto.Modbus_WriteCoil, sproto.Modbus_WriteCoils, sproto.Modbus_RWCoils:
|
|
||||||
err := m.cli.WriteCoils(uint16(mdm.Addr), m.GetDcBits(mdm))
|
|
||||||
if err != nil {
|
|
||||||
slog.Error("Modbus驱动采集服务写入线圈失败", "url", m.config.Url, "error", err, "Function", mdm.Function)
|
|
||||||
} else {
|
|
||||||
slog.Info("Modbus驱动采集服务写入线圈成功", "url", m.config.Url, "Function", mdm.Function)
|
|
||||||
}
|
|
||||||
case sproto.Modbus_WriteRegister, sproto.Modbus_WriteRegisters, sproto.Modbus_RWRegisters:
|
|
||||||
err := m.cli.WriteRegisterBytes(uint16(mdm.Addr), m.GetDcBytes(mdm))
|
|
||||||
if err != nil {
|
|
||||||
slog.Error("Modbus驱动采集服务写入寄存器失败", "url", m.config.Url, "error", err, "Function", mdm.Function)
|
|
||||||
} else {
|
|
||||||
slog.Info("Modbus驱动采集服务写入寄存器成功", "url", m.config.Url, "Function", mdm.Function)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func isWriteFunction(modbus_Function sproto.Modbus_Function) bool {
|
|
||||||
return modbus_Function == sproto.Modbus_WriteCoil ||
|
|
||||||
modbus_Function == sproto.Modbus_WriteCoils ||
|
|
||||||
modbus_Function == sproto.Modbus_WriteRegister ||
|
|
||||||
modbus_Function == sproto.Modbus_WriteRegisters ||
|
|
||||||
modbus_Function == sproto.Modbus_RWCoils ||
|
|
||||||
modbus_Function == sproto.Modbus_RWRegisters
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *modbusQcService) run(ctx context.Context) {
|
|
||||||
defer close(m.done)
|
|
||||||
mainLoop:
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case <-ctx.Done():
|
|
||||||
slog.Debug("Modbus驱采映射循环取消,关闭modbus客户端", "url", m.config.Url)
|
|
||||||
modbus.DeleteClient(m.config.Url)
|
|
||||||
break mainLoop
|
|
||||||
default:
|
|
||||||
}
|
|
||||||
m.mappingTaskExecute()
|
|
||||||
time.Sleep(time.Millisecond * time.Duration(m.config.Interval))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *modbusQcService) mappingTaskExecute() {
|
|
||||||
if m.cli.IsConnected() {
|
|
||||||
for _, mdm := range m.config.Mapping {
|
|
||||||
switch mdm.Function {
|
|
||||||
case sproto.Modbus_ReadCoil, sproto.Modbus_RWCoils:
|
|
||||||
data, err := m.cli.ReadCoil(uint16(mdm.Addr), uint16(mdm.Quantity))
|
|
||||||
if err != nil {
|
|
||||||
slog.Error("Modbus驱动采集服务读取线圈失败", "url", m.config.Url, "error", err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
err = m.updateDcByBits(mdm, data)
|
|
||||||
if err != nil {
|
|
||||||
slog.Error("Modbus驱动采集服务更新驱采数据失败", "url", m.config.Url, "error", err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
case sproto.Modbus_ReadDiscreteInput:
|
|
||||||
data, err := m.cli.ReadDiscreteInput(uint16(mdm.Addr), uint16(mdm.Quantity))
|
|
||||||
if err != nil {
|
|
||||||
slog.Error("Modbus驱动采集服务读取离散输入失败", "url", m.config.Url, "error", err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
err = m.updateDcByBits(mdm, data)
|
|
||||||
if err != nil {
|
|
||||||
slog.Error("Modbus驱动采集服务更新驱采数据失败", "url", m.config.Url, "error", err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
case sproto.Modbus_ReadHoldingRegister, sproto.Modbus_RWRegisters:
|
|
||||||
data, err := m.cli.ReadHoldingRegisterBytes(uint16(mdm.Addr), uint16(mdm.Quantity*2))
|
|
||||||
if err != nil {
|
|
||||||
slog.Error("Modbus驱动采集服务读取保持寄存器失败", "url", m.config.Url, "error", err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
err = m.updateDcByBytes(mdm, data)
|
|
||||||
if err != nil {
|
|
||||||
slog.Error("Modbus驱动采集服务更新驱采数据失败", "url", m.config.Url, "error", err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
case sproto.Modbus_ReadInputRegister:
|
|
||||||
data, err := m.cli.ReadInputRegisterBytes(uint16(mdm.Addr), uint16(mdm.Quantity*2))
|
|
||||||
if err != nil {
|
|
||||||
slog.Error("Modbus驱动采集服务读取输入寄存器失败", "url", m.config.Url, "error", err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
err = m.updateDcByBytes(mdm, data)
|
|
||||||
if err != nil {
|
|
||||||
slog.Error("Modbus驱动采集服务更新驱采数据失败", "url", m.config.Url, "error", err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
case sproto.Modbus_WriteCoil:
|
|
||||||
if mdm.WriteStrategy == sproto.Modbus_OnScheduled { // 定时写
|
|
||||||
bits := m.GetDcBits(mdm)
|
|
||||||
err := m.cli.WriteCoil(uint16(mdm.Addr), bits[0])
|
|
||||||
if err != nil {
|
|
||||||
slog.Error("Modbus驱动采集服务写单线圈失败", "url", m.config.Url, "error", err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case sproto.Modbus_WriteCoils:
|
|
||||||
if mdm.WriteStrategy == sproto.Modbus_OnScheduled { // 定时写
|
|
||||||
bits := m.GetDcBits(mdm)
|
|
||||||
err := m.cli.WriteCoils(uint16(mdm.Addr), bits)
|
|
||||||
if err != nil {
|
|
||||||
slog.Error("Modbus驱动采集服务写多线圈失败", "url", m.config.Url, "error", err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case sproto.Modbus_WriteRegister:
|
|
||||||
if mdm.WriteStrategy == sproto.Modbus_OnScheduled { // 定时写
|
|
||||||
data := m.GetDcBytes(mdm)
|
|
||||||
err := m.cli.WriteRegisterBytes(uint16(mdm.Addr), data)
|
|
||||||
if err != nil {
|
|
||||||
slog.Error("Modbus驱动采集服务写单寄存器失败", "url", m.config.Url, "error", err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case sproto.Modbus_WriteRegisters:
|
|
||||||
if mdm.WriteStrategy == sproto.Modbus_OnScheduled { // 定时写
|
|
||||||
data := m.GetDcBytes(mdm)
|
|
||||||
err := m.cli.WriteRegisterBytes(uint16(mdm.Addr), data)
|
|
||||||
if err != nil {
|
|
||||||
slog.Error("Modbus驱动采集服务写多寄存器失败", "url", m.config.Url, "error", err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
slog.Error("Modbus驱动采集服务映射任务执行失败,Modbus未连接", "url", m.config.Url)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *modbusQcService) GetDcBits(mdm *sproto.ModbusDcMapping) []bool {
|
|
||||||
switch mdm.Type {
|
|
||||||
case sproto.DataType_CollectTable: // 采集数据
|
|
||||||
return m.qc.GetCollectBits(mdm.Start, mdm.Quantity)
|
|
||||||
case sproto.DataType_DriveTable: // 驱动数据
|
|
||||||
return m.qc.GetDriveBits(mdm.Start, mdm.Quantity)
|
|
||||||
default:
|
|
||||||
panic("未知数据类型")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *modbusQcService) GetDcBytes(mdm *sproto.ModbusDcMapping) []byte {
|
|
||||||
switch mdm.Type {
|
|
||||||
case sproto.DataType_CollectTable: // 采集数据
|
|
||||||
return m.qc.GetCollectBytes(mdm.Start, mdm.Quantity*2)
|
|
||||||
case sproto.DataType_DriveTable: // 驱动数据
|
|
||||||
return m.qc.GetDriveBytes(mdm.Start, mdm.Quantity*2)
|
|
||||||
default:
|
|
||||||
panic("未知数据类型")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *modbusQcService) updateDcByBits(mdm *sproto.ModbusDcMapping, bits []bool) error {
|
|
||||||
switch mdm.Type {
|
|
||||||
case sproto.DataType_CollectTable: // 采集数据
|
|
||||||
return m.qc.UpdateCollectByBits(mdm.Start, bits)
|
|
||||||
case sproto.DataType_DriveTable: // 驱动数据
|
|
||||||
return m.qc.UpdateDriveByBits(mdm.Start, bits)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *modbusQcService) updateDcByBytes(mdm *sproto.ModbusDcMapping, bytes []byte) error {
|
|
||||||
switch mdm.Type {
|
|
||||||
case sproto.DataType_CollectTable: // 采集数据
|
|
||||||
return m.qc.UpdateCollectByBytes(mdm.Start, bytes)
|
|
||||||
case sproto.DataType_DriveTable: // 驱动数据
|
|
||||||
return m.qc.UpdateDriveByBytes(mdm.Start, bytes)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func checkConfig(config *sproto.ModbusConfig) error {
|
|
||||||
if config.Url == "" {
|
|
||||||
return fmt.Errorf("Modbus配置未设置url")
|
|
||||||
}
|
|
||||||
if !strings.HasPrefix(config.Url, "tcp") {
|
|
||||||
return fmt.Errorf("Modbus配置url必须以tcp开头")
|
|
||||||
}
|
|
||||||
if config.UnitId == 0 {
|
|
||||||
return fmt.Errorf("Modbus配置未设置unitId")
|
|
||||||
}
|
|
||||||
if config.Interval == 0 {
|
|
||||||
return fmt.Errorf("Modbus配置未设置interval")
|
|
||||||
}
|
|
||||||
if len(config.Mapping) == 0 {
|
|
||||||
return fmt.Errorf("Modbus配置无映射配置")
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *modbusQcService) Start() error {
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *modbusQcService) Stop() error {
|
|
||||||
m.cancel()
|
|
||||||
<-m.done
|
|
||||||
slog.Info("Modbus驱采映射服务线程退出", "url", m.config.Url)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func convertEndianness(endianness sproto.Modbus_Endianness) modbus.Endianness {
|
|
||||||
switch endianness {
|
|
||||||
case sproto.Modbus_BigEndian:
|
|
||||||
return modbus.BigEndian
|
|
||||||
case sproto.Modbus_LittleEndian:
|
|
||||||
return modbus.LittleEndian
|
|
||||||
}
|
|
||||||
return modbus.BigEndian
|
|
||||||
}
|
|
|
@ -0,0 +1,380 @@
|
||||||
|
package service
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"joylink.club/iot/protocol/modbus"
|
||||||
|
"joylink.club/iot/service/model"
|
||||||
|
sproto "joylink.club/iot/service/proto"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Modbus驱采服务
|
||||||
|
type modbusQcService struct {
|
||||||
|
config *sproto.ModbusConfig
|
||||||
|
cli modbus.MasterClient
|
||||||
|
qc model.QC
|
||||||
|
cancel context.CancelFunc
|
||||||
|
done chan struct{} // 服务协程退出信号
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *modbusQcService) Stop() error {
|
||||||
|
m.cancel()
|
||||||
|
<-m.done
|
||||||
|
slog.Info("Modbus驱采映射服务线程退出", "url", m.config.Url)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetCjBits implements IotQcMappingService.
|
||||||
|
func (s *modbusQcService) GetCjBits() []bool {
|
||||||
|
return s.qc.GetCjBits()
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetCjBytes implements IotQcMappingService.
|
||||||
|
func (s *modbusQcService) GetCjBytes() []byte {
|
||||||
|
return s.qc.GetCjBytes()
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetQdBits implements IotQcMappingService.
|
||||||
|
func (s *modbusQcService) GetQdBits() []bool {
|
||||||
|
return s.qc.GetQdBits()
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetQdBytes implements IotQcMappingService.
|
||||||
|
func (s *modbusQcService) GetQdBytes() []byte {
|
||||||
|
return s.qc.GetQdBytes()
|
||||||
|
}
|
||||||
|
|
||||||
|
// WriteCjBytes implements IotQcMappingService.
|
||||||
|
func (s *modbusQcService) WriteCjBytes(bytes []byte) error {
|
||||||
|
if len(bytes) != len(s.qc.GetCjBytes()) {
|
||||||
|
return fmt.Errorf("写入采集字节长度有误,应为%d,实为%d", len(s.qc.GetCjBytes()), len(bytes))
|
||||||
|
}
|
||||||
|
err := s.onWrite(sproto.DataType_CollectTable, bytes)
|
||||||
|
if err == nil {
|
||||||
|
s.qc.SetCjBytes(bytes)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// WriteQdBytes implements IotQcMappingService.
|
||||||
|
func (s *modbusQcService) WriteQdBytes(bytes []byte) error {
|
||||||
|
if len(bytes) != len(s.qc.GetQdBytes()) {
|
||||||
|
return fmt.Errorf("写入驱动字节长度有误,应为%d,实为%d", len(s.qc.GetQdBytes()), len(bytes))
|
||||||
|
}
|
||||||
|
err := s.onWrite(sproto.DataType_DriveTable, bytes)
|
||||||
|
if err == nil {
|
||||||
|
s.qc.SetQdBytes(bytes)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewModbusQcService(config *sproto.ModbusConfig, qd []byte, cj []byte) (IotQcMappingService, error) {
|
||||||
|
if err := checkConfig(config); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
_, ok := modbus.GetClient(config.Url)
|
||||||
|
if ok {
|
||||||
|
return nil, fmt.Errorf("modbus客户端已存在,url=%s", config.Url)
|
||||||
|
}
|
||||||
|
cli, err := modbus.NewClient(&modbus.ClientConfig{
|
||||||
|
Url: config.Url,
|
||||||
|
Timeout: config.Timeout,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Join(err, fmt.Errorf("modbus客户端创建失败,url=%s", config.Url))
|
||||||
|
}
|
||||||
|
cli.SetUnitId(uint8(config.UnitId))
|
||||||
|
cli.SetEndianness(convertEndianness(config.Endianness))
|
||||||
|
cli.Start()
|
||||||
|
s := &modbusQcService{
|
||||||
|
config: config,
|
||||||
|
cli: cli,
|
||||||
|
qc: model.NewDC(qd, cj),
|
||||||
|
done: make(chan struct{}),
|
||||||
|
}
|
||||||
|
// s.initOnUpdateTask()
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
go s.run(ctx)
|
||||||
|
s.cancel = cancel
|
||||||
|
return s, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *modbusQcService) onWrite(dt sproto.DataType, bytes []byte) error {
|
||||||
|
mapping := m.config.Mapping
|
||||||
|
for _, mdm := range mapping {
|
||||||
|
if isWriteFunction(mdm.Function) {
|
||||||
|
if mdm.Type == dt {
|
||||||
|
if !m.cli.IsConnected() {
|
||||||
|
slog.Warn("Modbus驱动采集服务数据写入失败,modbus客户端未连接", "url", m.config.Url, "unitid", m.config.UnitId, "Function", mdm.Function)
|
||||||
|
return errors.New("数据写入失败,modbus客户端未连接")
|
||||||
|
}
|
||||||
|
switch mdm.Function {
|
||||||
|
case sproto.Modbus_WriteCoil, sproto.Modbus_WriteCoils, sproto.Modbus_RWCoils:
|
||||||
|
data := getQcBits(bytes, mdm)
|
||||||
|
err := m.cli.WriteCoils(uint16(mdm.Addr), data)
|
||||||
|
if err != nil {
|
||||||
|
slog.Error("Modbus驱动采集服务写入线圈失败", "url", m.config.Url, "unitid", m.config.UnitId, "error", err, "Function", mdm.Function)
|
||||||
|
return err
|
||||||
|
} else {
|
||||||
|
slog.Info("Modbus驱动采集服务写入线圈成功", "url", m.config.Url, "unitid", m.config.UnitId, "Function", mdm.Function, "data", data, "mapping", mdm)
|
||||||
|
}
|
||||||
|
case sproto.Modbus_WriteRegister, sproto.Modbus_WriteRegisters, sproto.Modbus_RWRegisters:
|
||||||
|
data := getQcBytes(bytes, mdm)
|
||||||
|
err := m.cli.WriteRegisterBytes(uint16(mdm.Addr), data)
|
||||||
|
if err != nil {
|
||||||
|
slog.Error("Modbus驱动采集服务写入寄存器失败", "url", m.config.Url, "unitid", m.config.UnitId, "error", err, "Function", mdm.Function)
|
||||||
|
return err
|
||||||
|
} else {
|
||||||
|
slog.Info("Modbus驱动采集服务写入寄存器成功", "url", m.config.Url, "unitid", m.config.UnitId, "Function", mdm.Function, "data", data, "mapping", mdm)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// func (m *modbusQcService) initOnUpdateTask() {
|
||||||
|
// mapping := m.config.Mapping
|
||||||
|
// for _, mdm := range mapping {
|
||||||
|
// if mdm.WriteStrategy == sproto.Modbus_OnUpdate && isWriteFunction(mdm.Function) {
|
||||||
|
// et := model.DCE_Drive_Update
|
||||||
|
// if mdm.Type == sproto.DataType_CollectTable {
|
||||||
|
// et = model.DCE_Collect_Update
|
||||||
|
// }
|
||||||
|
// m.qc.On(et, func(d model.QC) {
|
||||||
|
// if !m.cli.IsConnected() {
|
||||||
|
// slog.Warn("Modbus驱动采集服务数据更新写入失败,modbus客户端未连接", "url", m.config.Url, "Function", mdm.Function)
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
// switch mdm.Function {
|
||||||
|
// case sproto.Modbus_WriteCoil, sproto.Modbus_WriteCoils, sproto.Modbus_RWCoils:
|
||||||
|
// err := m.cli.WriteCoils(uint16(mdm.Addr), m.GetDcBits(mdm))
|
||||||
|
// if err != nil {
|
||||||
|
// slog.Error("Modbus驱动采集服务写入线圈失败", "url", m.config.Url, "error", err, "Function", mdm.Function)
|
||||||
|
// } else {
|
||||||
|
// slog.Info("Modbus驱动采集服务写入线圈成功", "url", m.config.Url, "Function", mdm.Function)
|
||||||
|
// }
|
||||||
|
// case sproto.Modbus_WriteRegister, sproto.Modbus_WriteRegisters, sproto.Modbus_RWRegisters:
|
||||||
|
// err := m.cli.WriteRegisterBytes(uint16(mdm.Addr), m.GetDcBytes(mdm))
|
||||||
|
// if err != nil {
|
||||||
|
// slog.Error("Modbus驱动采集服务写入寄存器失败", "url", m.config.Url, "error", err, "Function", mdm.Function)
|
||||||
|
// } else {
|
||||||
|
// slog.Info("Modbus驱动采集服务写入寄存器成功", "url", m.config.Url, "Function", mdm.Function)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// })
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
func isWriteFunction(modbus_Function sproto.Modbus_Function) bool {
|
||||||
|
return modbus_Function == sproto.Modbus_WriteCoil ||
|
||||||
|
modbus_Function == sproto.Modbus_WriteCoils ||
|
||||||
|
modbus_Function == sproto.Modbus_WriteRegister ||
|
||||||
|
modbus_Function == sproto.Modbus_WriteRegisters ||
|
||||||
|
modbus_Function == sproto.Modbus_RWCoils ||
|
||||||
|
modbus_Function == sproto.Modbus_RWRegisters
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *modbusQcService) run(ctx context.Context) {
|
||||||
|
defer close(m.done)
|
||||||
|
mainLoop:
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
slog.Debug("Modbus驱采映射循环取消,关闭modbus客户端", "url", m.config.Url)
|
||||||
|
modbus.DeleteClient(m.config.Url)
|
||||||
|
break mainLoop
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
m.mappingTaskExecute()
|
||||||
|
time.Sleep(time.Millisecond * time.Duration(m.config.Interval))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *modbusQcService) mappingTaskExecute() {
|
||||||
|
if m.cli.IsConnected() {
|
||||||
|
for _, mdm := range m.config.Mapping {
|
||||||
|
switch mdm.Function {
|
||||||
|
case sproto.Modbus_ReadCoil, sproto.Modbus_RWCoils:
|
||||||
|
data, err := m.cli.ReadCoil(uint16(mdm.Addr), uint16(mdm.Quantity))
|
||||||
|
if err != nil {
|
||||||
|
slog.Error("Modbus驱动采集服务读取线圈失败", "url", m.config.Url, "unitid", m.config.UnitId, "error", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
err = m.updateDcByBits(mdm, data)
|
||||||
|
if err != nil {
|
||||||
|
slog.Error("Modbus驱动采集服务更新驱采数据失败", "url", m.config.Url, "unitid", m.config.UnitId, "error", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
case sproto.Modbus_ReadDiscreteInput:
|
||||||
|
data, err := m.cli.ReadDiscreteInput(uint16(mdm.Addr), uint16(mdm.Quantity))
|
||||||
|
if err != nil {
|
||||||
|
slog.Error("Modbus驱动采集服务读取离散输入失败", "url", m.config.Url, "unitid", m.config.UnitId, "error", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
err = m.updateDcByBits(mdm, data)
|
||||||
|
if err != nil {
|
||||||
|
slog.Error("Modbus驱动采集服务更新驱采数据失败", "url", m.config.Url, "unitid", m.config.UnitId, "error", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
case sproto.Modbus_ReadHoldingRegister, sproto.Modbus_RWRegisters:
|
||||||
|
data, err := m.cli.ReadHoldingRegisterBytes(uint16(mdm.Addr), uint16(mdm.Quantity*2))
|
||||||
|
if err != nil {
|
||||||
|
slog.Error("Modbus驱动采集服务读取保持寄存器失败", "url", m.config.Url, "unitid", m.config.UnitId, "error", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
err = m.updateDcByBytes(mdm, data)
|
||||||
|
if err != nil {
|
||||||
|
slog.Error("Modbus驱动采集服务更新驱采数据失败", "url", m.config.Url, "unitid", m.config.UnitId, "error", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
case sproto.Modbus_ReadInputRegister:
|
||||||
|
data, err := m.cli.ReadInputRegisterBytes(uint16(mdm.Addr), uint16(mdm.Quantity*2))
|
||||||
|
if err != nil {
|
||||||
|
slog.Error("Modbus驱动采集服务读取输入寄存器失败", "url", m.config.Url, "unitid", m.config.UnitId, "error", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
err = m.updateDcByBytes(mdm, data)
|
||||||
|
if err != nil {
|
||||||
|
slog.Error("Modbus驱动采集服务更新驱采数据失败", "url", m.config.Url, "unitid", m.config.UnitId, "error", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// case sproto.Modbus_WriteCoil:
|
||||||
|
// if mdm.WriteStrategy == sproto.Modbus_OnScheduled { // 定时写
|
||||||
|
// bits := m.GetDcBits(mdm)
|
||||||
|
// err := m.cli.WriteCoil(uint16(mdm.Addr), bits[0])
|
||||||
|
// if err != nil {
|
||||||
|
// slog.Error("Modbus驱动采集服务写单线圈失败", "url", m.config.Url, "unitid", m.config.UnitId, "error", err)
|
||||||
|
// continue
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// case sproto.Modbus_WriteCoils:
|
||||||
|
// if mdm.WriteStrategy == sproto.Modbus_OnScheduled { // 定时写
|
||||||
|
// bits := m.GetDcBits(mdm)
|
||||||
|
// err := m.cli.WriteCoils(uint16(mdm.Addr), bits)
|
||||||
|
// if err != nil {
|
||||||
|
// slog.Error("Modbus驱动采集服务写多线圈失败", "url", m.config.Url, "unitid", m.config.UnitId, "error", err)
|
||||||
|
// continue
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// case sproto.Modbus_WriteRegister:
|
||||||
|
// if mdm.WriteStrategy == sproto.Modbus_OnScheduled { // 定时写
|
||||||
|
// data := m.GetDcBytes(mdm)
|
||||||
|
// err := m.cli.WriteRegisterBytes(uint16(mdm.Addr), data)
|
||||||
|
// if err != nil {
|
||||||
|
// slog.Error("Modbus驱动采集服务写单寄存器失败", "url", m.config.Url, "unitid", m.config.UnitId, "error", err)
|
||||||
|
// continue
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// case sproto.Modbus_WriteRegisters:
|
||||||
|
// if mdm.WriteStrategy == sproto.Modbus_OnScheduled { // 定时写
|
||||||
|
// data := m.GetDcBytes(mdm)
|
||||||
|
// err := m.cli.WriteRegisterBytes(uint16(mdm.Addr), data)
|
||||||
|
// if err != nil {
|
||||||
|
// slog.Error("Modbus驱动采集服务写多寄存器失败", "url", m.config.Url, "unitid", m.config.UnitId, "error", err)
|
||||||
|
// continue
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
slog.Error("Modbus驱动采集服务映射任务执行失败,Modbus未连接", "url", m.config.Url, "unitid", m.config.UnitId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getQcBits(bytes []byte, mdm *sproto.ModbusDcMapping) []bool {
|
||||||
|
bits := model.DecodeBools(bytes)
|
||||||
|
start := mdm.Start
|
||||||
|
quantity := mdm.Quantity
|
||||||
|
if start+quantity > uint32(len(bits)) {
|
||||||
|
panic(fmt.Errorf("getQcBits超出范围"))
|
||||||
|
}
|
||||||
|
return bits[start : start+quantity]
|
||||||
|
}
|
||||||
|
|
||||||
|
func getQcBytes(bytes []byte, mdm *sproto.ModbusDcMapping) []byte {
|
||||||
|
start := mdm.Start
|
||||||
|
quantity := mdm.Quantity * 2
|
||||||
|
if start+quantity > uint32(len(bytes)) {
|
||||||
|
panic(fmt.Errorf("getQcBytes超出范围"))
|
||||||
|
}
|
||||||
|
return bytes[start : start+quantity]
|
||||||
|
}
|
||||||
|
|
||||||
|
// func (m *modbusQcService) GetDcBits(mdm *sproto.ModbusDcMapping) []bool {
|
||||||
|
// switch mdm.Type {
|
||||||
|
// case sproto.DataType_CollectTable: // 采集数据
|
||||||
|
// return m.qc.GetCjBitsOf(mdm.Start, mdm.Quantity)
|
||||||
|
// case sproto.DataType_DriveTable: // 驱动数据
|
||||||
|
// return m.qc.GetQdBitsOf(mdm.Start, mdm.Quantity)
|
||||||
|
// default:
|
||||||
|
// panic("未知数据类型")
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// func (m *modbusQcService) GetDcBytes(mdm *sproto.ModbusDcMapping) []byte {
|
||||||
|
// switch mdm.Type {
|
||||||
|
// case sproto.DataType_CollectTable: // 采集数据
|
||||||
|
// return m.qc.GetCjBytesOf(mdm.Start, mdm.Quantity*2)
|
||||||
|
// case sproto.DataType_DriveTable: // 驱动数据
|
||||||
|
// return m.qc.GetQdBytesOf(mdm.Start, mdm.Quantity*2)
|
||||||
|
// default:
|
||||||
|
// panic("未知数据类型")
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
func (m *modbusQcService) updateDcByBits(mdm *sproto.ModbusDcMapping, bits []bool) error {
|
||||||
|
switch mdm.Type {
|
||||||
|
case sproto.DataType_CollectTable: // 采集数据
|
||||||
|
return m.qc.UpdateCjByBits(mdm.Start, bits)
|
||||||
|
case sproto.DataType_DriveTable: // 驱动数据
|
||||||
|
return m.qc.UpdateQdByBits(mdm.Start, bits)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *modbusQcService) updateDcByBytes(mdm *sproto.ModbusDcMapping, bytes []byte) error {
|
||||||
|
switch mdm.Type {
|
||||||
|
case sproto.DataType_CollectTable: // 采集数据
|
||||||
|
return m.qc.UpdateCjByBytes(mdm.Start, bytes)
|
||||||
|
case sproto.DataType_DriveTable: // 驱动数据
|
||||||
|
return m.qc.UpdateQdByBytes(mdm.Start, bytes)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkConfig(config *sproto.ModbusConfig) error {
|
||||||
|
if config.Url == "" {
|
||||||
|
return fmt.Errorf("Modbus配置未设置url")
|
||||||
|
}
|
||||||
|
if !strings.HasPrefix(config.Url, "tcp") {
|
||||||
|
return fmt.Errorf("Modbus配置url必须以tcp开头")
|
||||||
|
}
|
||||||
|
if config.UnitId == 0 {
|
||||||
|
return fmt.Errorf("Modbus配置未设置unitId")
|
||||||
|
}
|
||||||
|
if config.Interval == 0 {
|
||||||
|
return fmt.Errorf("Modbus配置未设置interval")
|
||||||
|
}
|
||||||
|
if len(config.Mapping) == 0 {
|
||||||
|
return fmt.Errorf("Modbus配置无映射配置")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func convertEndianness(endianness sproto.Modbus_Endianness) modbus.Endianness {
|
||||||
|
switch endianness {
|
||||||
|
case sproto.Modbus_BigEndian:
|
||||||
|
return modbus.BigEndian
|
||||||
|
case sproto.Modbus_LittleEndian:
|
||||||
|
return modbus.LittleEndian
|
||||||
|
}
|
||||||
|
return modbus.BigEndian
|
||||||
|
}
|
|
@ -17,121 +17,147 @@ const (
|
||||||
// 驱动采集数据
|
// 驱动采集数据
|
||||||
type QC interface {
|
type QC interface {
|
||||||
// 更新驱动数据,位数组
|
// 更新驱动数据,位数组
|
||||||
UpdateDriveByBits(start uint32, bits []bool) error
|
UpdateQdByBits(start uint32, bits []bool) error
|
||||||
// 更新驱动数据,字节数组
|
// 更新驱动数据,字节数组
|
||||||
UpdateDriveByBytes(start uint32, values []byte) error
|
UpdateQdByBytes(start uint32, values []byte) error
|
||||||
// 更新采集数据,位数组
|
// 更新采集数据,位数组
|
||||||
UpdateCollectByBits(start uint32, bits []bool) error
|
UpdateCjByBits(start uint32, bits []bool) error
|
||||||
// 更新采集数据,字节数组
|
// 更新采集数据,字节数组
|
||||||
UpdateCollectByBytes(start uint32, values []byte) error
|
UpdateCjByBytes(start uint32, values []byte) error
|
||||||
|
// 设置驱动数据
|
||||||
|
SetQdBytes(bytes []byte)
|
||||||
// 获取驱动数据,字节数组
|
// 获取驱动数据,字节数组
|
||||||
GetDrive() []byte
|
GetQdBytes() []byte
|
||||||
|
// 获取驱动数据,位数组
|
||||||
|
GetQdBits() []bool
|
||||||
// 获取指定驱动数据,位
|
// 获取指定驱动数据,位
|
||||||
GetDriveBit(start uint32) bool
|
GetQdBitOf(start uint32) bool
|
||||||
// 获取指定驱动数据,位数组
|
// 获取指定驱动数据,位数组
|
||||||
GetDriveBits(start uint32, quantity uint32) []bool
|
GetQdBitsOf(start uint32, quantity uint32) []bool
|
||||||
// 获取指定驱动数据,字节
|
// 获取指定驱动数据,字节
|
||||||
GetDriveByte(start uint32) byte
|
GetQdByteOf(start uint32) byte
|
||||||
// 获取指定驱动数据,字节数组
|
// 获取指定驱动数据,字节数组
|
||||||
GetDriveBytes(start uint32, quantity uint32) []byte
|
GetQdBytesOf(start uint32, quantity uint32) []byte
|
||||||
|
// 设置采集数据
|
||||||
|
SetCjBytes(bytes []byte)
|
||||||
// 获取采集数据,字节数组
|
// 获取采集数据,字节数组
|
||||||
GetCollect() []byte
|
GetCjBytes() []byte
|
||||||
|
// 获取采集数据,位数组
|
||||||
|
GetCjBits() []bool
|
||||||
// 获取指定采集数据,位
|
// 获取指定采集数据,位
|
||||||
GetCollectBit(start uint32) bool
|
GetCjBitOf(start uint32) bool
|
||||||
// 获取指定采集数据,位数组
|
// 获取指定采集数据,位数组
|
||||||
GetCollectBits(start uint32, quantity uint32) []bool
|
GetCjBitsOf(start uint32, quantity uint32) []bool
|
||||||
// 获取指定采集数据,字节
|
// 获取指定采集数据,字节
|
||||||
GetCollectByte(start uint32) byte
|
GetCjByteOf(start uint32) byte
|
||||||
// 获取指定采集数据,字节数组
|
// 获取指定采集数据,字节数组
|
||||||
GetCollectBytes(start uint32, quantity uint32) []byte
|
GetCjBytesOf(start uint32, quantity uint32) []byte
|
||||||
// 发布事件
|
// // 发布事件
|
||||||
Emit(event DCEvent)
|
// Emit(event DCEvent)
|
||||||
// 订阅事件
|
// // 订阅事件
|
||||||
On(event DCEvent, callback func(dc QC))
|
// On(event DCEvent, callback func(dc QC))
|
||||||
// 取消订阅
|
// // 取消订阅
|
||||||
Off(event DCEvent, callback func(dc QC))
|
// Off(event DCEvent, callback func(dc QC))
|
||||||
}
|
}
|
||||||
|
|
||||||
type qc struct {
|
type qc struct {
|
||||||
// 驱动数据
|
// 驱动数据
|
||||||
drive []byte
|
qd []byte
|
||||||
driveBits []bool
|
qdBits []bool
|
||||||
// 采集数据
|
// 采集数据
|
||||||
collect []byte
|
cj []byte
|
||||||
collectBits []bool
|
cjBits []bool
|
||||||
// 事件
|
// // 事件
|
||||||
subscribes map[DCEvent][]func(dc QC)
|
// subscribes map[DCEvent][]func(dc QC)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetCollect implements DC.
|
func (d *qc) SetCjBytes(bytes []byte) {
|
||||||
func (d *qc) GetCollect() []byte {
|
d.cj = bytes
|
||||||
return d.collect
|
d.cjBits = DecodeBools(bytes)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *qc) GetCollectBit(start uint32) bool {
|
// GetCjBytes implements DC.
|
||||||
if start >= uint32(len(d.collectBits)) {
|
func (d *qc) GetCjBytes() []byte {
|
||||||
|
return d.cj
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *qc) GetCjBits() []bool {
|
||||||
|
return d.cjBits
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *qc) GetCjBitOf(start uint32) bool {
|
||||||
|
if start >= uint32(len(d.cjBits)) {
|
||||||
panic(fmt.Errorf("GetCollectBit超出范围"))
|
panic(fmt.Errorf("GetCollectBit超出范围"))
|
||||||
}
|
}
|
||||||
return d.collectBits[start]
|
return d.cjBits[start]
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *qc) GetCollectBits(start uint32, quantity uint32) []bool {
|
func (d *qc) GetCjBitsOf(start uint32, quantity uint32) []bool {
|
||||||
if start+quantity > uint32(len(d.collectBits)) {
|
if start+quantity > uint32(len(d.cjBits)) {
|
||||||
panic(fmt.Errorf("GetCollectBits超出范围"))
|
panic(fmt.Errorf("GetCollectBits超出范围"))
|
||||||
}
|
}
|
||||||
return d.collectBits[start : start+quantity]
|
return d.cjBits[start : start+quantity]
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *qc) GetCollectByte(start uint32) byte {
|
func (d *qc) GetCjByteOf(start uint32) byte {
|
||||||
if start >= uint32(len(d.collect)) {
|
if start >= uint32(len(d.cj)) {
|
||||||
panic(fmt.Errorf("GetCollectByte超出范围"))
|
panic(fmt.Errorf("GetCollectByte超出范围"))
|
||||||
}
|
}
|
||||||
return d.collect[start]
|
return d.cj[start]
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *qc) GetCollectBytes(start uint32, quantity uint32) []byte {
|
func (d *qc) GetCjBytesOf(start uint32, quantity uint32) []byte {
|
||||||
if start+quantity > uint32(len(d.collect)) {
|
if start+quantity > uint32(len(d.cj)) {
|
||||||
panic(fmt.Errorf("GetCollectBytes超出范围"))
|
panic(fmt.Errorf("GetCollectBytes超出范围"))
|
||||||
}
|
}
|
||||||
return d.collect[start : start+quantity]
|
return d.cj[start : start+quantity]
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetDrive implements DC.
|
func (d *qc) SetQdBytes(bytes []byte) {
|
||||||
func (d *qc) GetDrive() []byte {
|
d.qd = bytes
|
||||||
return d.drive
|
d.qdBits = DecodeBools(bytes)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *qc) GetDriveBit(start uint32) bool {
|
// GetQdBytes implements DC.
|
||||||
if start >= uint32(len(d.driveBits)) {
|
func (d *qc) GetQdBytes() []byte {
|
||||||
|
return d.qd
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *qc) GetQdBits() []bool {
|
||||||
|
return d.qdBits
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *qc) GetQdBitOf(start uint32) bool {
|
||||||
|
if start >= uint32(len(d.qdBits)) {
|
||||||
panic(fmt.Errorf("GetDriveBit超出范围"))
|
panic(fmt.Errorf("GetDriveBit超出范围"))
|
||||||
}
|
}
|
||||||
return d.driveBits[start]
|
return d.qdBits[start]
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *qc) GetDriveBits(start uint32, quantity uint32) []bool {
|
func (d *qc) GetQdBitsOf(start uint32, quantity uint32) []bool {
|
||||||
if start+quantity > uint32(len(d.driveBits)) {
|
if start+quantity > uint32(len(d.qdBits)) {
|
||||||
panic(fmt.Errorf("GetDriveBits超出范围"))
|
panic(fmt.Errorf("GetDriveBits超出范围"))
|
||||||
}
|
}
|
||||||
return d.driveBits[start : start+quantity]
|
return d.qdBits[start : start+quantity]
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *qc) GetDriveByte(start uint32) byte {
|
func (d *qc) GetQdByteOf(start uint32) byte {
|
||||||
if start >= uint32(len(d.drive)) {
|
if start >= uint32(len(d.qd)) {
|
||||||
panic(fmt.Errorf("GetDriveByte超出范围"))
|
panic(fmt.Errorf("GetDriveByte超出范围"))
|
||||||
}
|
}
|
||||||
return d.drive[start]
|
return d.qd[start]
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *qc) GetDriveBytes(start uint32, quantity uint32) []byte {
|
func (d *qc) GetQdBytesOf(start uint32, quantity uint32) []byte {
|
||||||
if start+quantity > uint32(len(d.drive)) {
|
if start+quantity > uint32(len(d.qd)) {
|
||||||
panic(fmt.Errorf("GetDriveBytes超出范围"))
|
panic(fmt.Errorf("GetDriveBytes超出范围"))
|
||||||
}
|
}
|
||||||
return d.drive[start : start+quantity]
|
return d.qd[start : start+quantity]
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateCollectByBits implements DC.
|
// UpdateCjByBits implements DC.
|
||||||
func (d *qc) UpdateCollectByBits(start uint32, bits []bool) error {
|
func (d *qc) UpdateCjByBits(start uint32, bits []bool) error {
|
||||||
total := len(d.collectBits)
|
total := len(d.cjBits)
|
||||||
if start >= uint32(total) {
|
if start >= uint32(total) {
|
||||||
return fmt.Errorf("UpdateCollectByBits参数start超出范围")
|
return fmt.Errorf("UpdateCollectByBits参数start超出范围")
|
||||||
}
|
}
|
||||||
|
@ -140,17 +166,17 @@ func (d *qc) UpdateCollectByBits(start uint32, bits []bool) error {
|
||||||
return fmt.Errorf("UpdateCollectByBits参数start+len(bits)超出范围")
|
return fmt.Errorf("UpdateCollectByBits参数start+len(bits)超出范围")
|
||||||
}
|
}
|
||||||
for i := start; i < end; i++ {
|
for i := start; i < end; i++ {
|
||||||
d.collectBits[i] = bits[i-start]
|
d.cjBits[i] = bits[i-start]
|
||||||
}
|
}
|
||||||
d.collect = encodeBytes(d.collectBits)
|
d.cj = EncodeBytes(d.cjBits)
|
||||||
slog.Debug("UpdateCollectByBits成功", "collect", fmt.Sprintf("%v", d.collect), "collectBits", d.collectBits)
|
slog.Debug("UpdateCollectByBits成功", "collect", fmt.Sprintf("%v", d.cj), "collectBits", d.cjBits)
|
||||||
d.Emit(DCE_Collect_Update)
|
// d.Emit(DCE_Collect_Update)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateCollectByBytes implements DC.
|
// UpdateCjByBytes implements DC.
|
||||||
func (d *qc) UpdateCollectByBytes(start uint32, values []byte) error {
|
func (d *qc) UpdateCjByBytes(start uint32, values []byte) error {
|
||||||
total := len(d.collect)
|
total := len(d.cj)
|
||||||
if start >= uint32(total) {
|
if start >= uint32(total) {
|
||||||
return fmt.Errorf("UpdateCollectByBytes参数start超出范围")
|
return fmt.Errorf("UpdateCollectByBytes参数start超出范围")
|
||||||
}
|
}
|
||||||
|
@ -158,16 +184,16 @@ func (d *qc) UpdateCollectByBytes(start uint32, values []byte) error {
|
||||||
if end > uint32(total) {
|
if end > uint32(total) {
|
||||||
return fmt.Errorf("UpdateCollectByBytes参数start+len(values)超出范围")
|
return fmt.Errorf("UpdateCollectByBytes参数start+len(values)超出范围")
|
||||||
}
|
}
|
||||||
copy(d.collect[start:end], values)
|
copy(d.cj[start:end], values)
|
||||||
d.collectBits = decodeBools(d.collect)
|
d.cjBits = DecodeBools(d.cj)
|
||||||
slog.Debug("UpdateCollectByBytes成功", "collect", fmt.Sprintf("%v", d.collect), "collectBits", d.collectBits)
|
slog.Debug("UpdateCollectByBytes成功", "collect", fmt.Sprintf("%v", d.cj), "collectBits", d.cjBits)
|
||||||
d.Emit(DCE_Collect_Update)
|
// d.Emit(DCE_Collect_Update)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateDriveByBits implements DC.
|
// UpdateQdByBits implements DC.
|
||||||
func (d *qc) UpdateDriveByBits(start uint32, bits []bool) error {
|
func (d *qc) UpdateQdByBits(start uint32, bits []bool) error {
|
||||||
total := len(d.driveBits)
|
total := len(d.qdBits)
|
||||||
if start >= uint32(total) {
|
if start >= uint32(total) {
|
||||||
return fmt.Errorf("UpdateDriveByBits参数start超出范围")
|
return fmt.Errorf("UpdateDriveByBits参数start超出范围")
|
||||||
}
|
}
|
||||||
|
@ -176,17 +202,17 @@ func (d *qc) UpdateDriveByBits(start uint32, bits []bool) error {
|
||||||
return fmt.Errorf("UpdateDriveByBits参数start+len(bits)超出范围")
|
return fmt.Errorf("UpdateDriveByBits参数start+len(bits)超出范围")
|
||||||
}
|
}
|
||||||
for i := start; i < end; i++ {
|
for i := start; i < end; i++ {
|
||||||
d.driveBits[i] = bits[i-start]
|
d.qdBits[i] = bits[i-start]
|
||||||
}
|
}
|
||||||
d.drive = encodeBytes(d.driveBits)
|
d.qd = EncodeBytes(d.qdBits)
|
||||||
slog.Debug("UpdateDriveByBits成功", "drive", fmt.Sprintf("%v", d.drive), "driveBits", d.driveBits)
|
slog.Debug("UpdateDriveByBits成功", "drive", fmt.Sprintf("%v", d.qd), "driveBits", d.qdBits)
|
||||||
d.Emit(DCE_Drive_Update)
|
// d.Emit(DCE_Drive_Update)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateDriveByBytes implements DC.
|
// UpdateQdByBytes implements DC.
|
||||||
func (d *qc) UpdateDriveByBytes(start uint32, values []byte) error {
|
func (d *qc) UpdateQdByBytes(start uint32, values []byte) error {
|
||||||
total := len(d.drive)
|
total := len(d.qd)
|
||||||
if start >= uint32(total) {
|
if start >= uint32(total) {
|
||||||
return fmt.Errorf("UpdateDriveByBytes参数start超出范围")
|
return fmt.Errorf("UpdateDriveByBytes参数start超出范围")
|
||||||
}
|
}
|
||||||
|
@ -194,42 +220,42 @@ func (d *qc) UpdateDriveByBytes(start uint32, values []byte) error {
|
||||||
if end > uint32(total) {
|
if end > uint32(total) {
|
||||||
return fmt.Errorf("UpdateDriveByBytes参数start+len(values)超出范围")
|
return fmt.Errorf("UpdateDriveByBytes参数start+len(values)超出范围")
|
||||||
}
|
}
|
||||||
copy(d.drive[start:end], values)
|
copy(d.qd[start:end], values)
|
||||||
d.driveBits = decodeBools(d.drive)
|
d.qdBits = DecodeBools(d.qd)
|
||||||
slog.Debug("UpdateDriveByBytes成功", "drive", fmt.Sprintf("%v", d.drive), "driveBits", d.driveBits)
|
slog.Debug("UpdateDriveByBytes成功", "drive", fmt.Sprintf("%v", d.qd), "driveBits", d.qdBits)
|
||||||
d.Emit(DCE_Drive_Update)
|
// d.Emit(DCE_Drive_Update)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Emit implements DC.
|
// // Emit implements DC.
|
||||||
func (d *qc) Emit(event DCEvent) {
|
// func (d *qc) Emit(event DCEvent) {
|
||||||
listeners := d.subscribes[event]
|
// listeners := d.subscribes[event]
|
||||||
for _, v := range listeners {
|
// for _, v := range listeners {
|
||||||
v(d)
|
// v(d)
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
// On implements DC.
|
// // On implements DC.
|
||||||
func (d *qc) On(event DCEvent, callback func(d QC)) {
|
// func (d *qc) On(event DCEvent, callback func(d QC)) {
|
||||||
d.subscribes[event] = append(d.subscribes[event], callback)
|
// d.subscribes[event] = append(d.subscribes[event], callback)
|
||||||
}
|
// }
|
||||||
|
|
||||||
// Off implements DC.
|
// // Off implements DC.
|
||||||
func (d *qc) Off(event DCEvent, callback func(d QC)) {
|
// func (d *qc) Off(event DCEvent, callback func(d QC)) {
|
||||||
panic("unimplemented")
|
// panic("unimplemented")
|
||||||
}
|
// }
|
||||||
|
|
||||||
func NewDC(d []byte, c []byte) QC {
|
func NewDC(qd []byte, cj []byte) QC {
|
||||||
return &qc{
|
return &qc{
|
||||||
drive: d,
|
qd: qd,
|
||||||
driveBits: decodeBools(d),
|
qdBits: DecodeBools(qd),
|
||||||
collect: c,
|
cj: cj,
|
||||||
collectBits: decodeBools(c),
|
cjBits: DecodeBools(cj),
|
||||||
subscribes: make(map[DCEvent][]func(d QC)),
|
// subscribes: make(map[DCEvent][]func(d QC)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func decodeBools(bytes []byte) (out []bool) {
|
func DecodeBools(bytes []byte) (out []bool) {
|
||||||
len := len(bytes) * 8
|
len := len(bytes) * 8
|
||||||
var i int
|
var i int
|
||||||
for i = 0; i < len; i++ {
|
for i = 0; i < len; i++ {
|
||||||
|
@ -238,7 +264,7 @@ func decodeBools(bytes []byte) (out []bool) {
|
||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
func encodeBytes(bits []bool) (out []byte) {
|
func EncodeBytes(bits []bool) (out []byte) {
|
||||||
len := len(bits)
|
len := len(bits)
|
||||||
if len%8 != 0 {
|
if len%8 != 0 {
|
||||||
panic("encodeBytes参数bits长度错误")
|
panic("encodeBytes参数bits长度错误")
|
||||||
|
|
|
@ -7,7 +7,7 @@ import (
|
||||||
func TestEncodeBytes(t *testing.T) {
|
func TestEncodeBytes(t *testing.T) {
|
||||||
want := byte(0b11011000)
|
want := byte(0b11011000)
|
||||||
bits := []bool{false, false, false, true, true, false, true, true}
|
bits := []bool{false, false, false, true, true, false, true, true}
|
||||||
bs := encodeBytes(bits)
|
bs := EncodeBytes(bits)
|
||||||
if bs[0] != want {
|
if bs[0] != want {
|
||||||
t.Errorf("encodeBytes(%v) = %v, want %v", bits, bs, want)
|
t.Errorf("encodeBytes(%v) = %v, want %v", bits, bs, want)
|
||||||
}
|
}
|
||||||
|
@ -16,7 +16,7 @@ func TestEncodeBytes(t *testing.T) {
|
||||||
func TestDecodeBools(t *testing.T) {
|
func TestDecodeBools(t *testing.T) {
|
||||||
want := []bool{false, false, false, true, true, false, true, true}
|
want := []bool{false, false, false, true, true, false, true, true}
|
||||||
bs := []byte{0xD8}
|
bs := []byte{0xD8}
|
||||||
bits := decodeBools(bs)
|
bits := DecodeBools(bs)
|
||||||
if !sliceEquals(bits, want) {
|
if !sliceEquals(bits, want) {
|
||||||
t.Errorf("decodeBits(%v) = %v, want %v", bs, bits, want)
|
t.Errorf("decodeBits(%v) = %v, want %v", bs, bits, want)
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
package model
|
||||||
|
|
||||||
|
// 服务状态
|
||||||
|
type ServiceState struct {
|
||||||
|
State int32
|
||||||
|
}
|
Loading…
Reference in New Issue