-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
5 changed files
with
299 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
package mirror | ||
|
||
import ( | ||
"github.com/ibuilding-x/driver-box/driverbox" | ||
"github.com/ibuilding-x/driver-box/driverbox/plugin" | ||
"github.com/ibuilding-x/driver-box/driverbox/plugin/callback" | ||
"github.com/ibuilding-x/driver-box/internal/plugins/mirror" | ||
"sync" | ||
) | ||
|
||
var driverInstance *Export | ||
var once = &sync.Once{} | ||
|
||
type Export struct { | ||
ready bool | ||
plugin *mirror.Plugin | ||
} | ||
|
||
func (export *Export) Init() error { | ||
//注册镜像插件 | ||
export.plugin = new(mirror.Plugin) | ||
driverbox.RegisterPlugin("mirror", export.plugin) | ||
export.ready = true | ||
return nil | ||
} | ||
func NewExport() *Export { | ||
once.Do(func() { | ||
driverInstance = &Export{} | ||
}) | ||
return driverInstance | ||
} | ||
|
||
// 点位变化触发场景联动 | ||
func (export *Export) ExportTo(deviceData plugin.DeviceData) { | ||
callback.OnReceiveHandler(export.plugin.VirtualConnector, deviceData) | ||
} | ||
|
||
// 继承Export OnEvent接口 | ||
func (export *Export) OnEvent(eventCode string, key string, eventValue interface{}) error { | ||
return nil | ||
} | ||
|
||
func (export *Export) IsReady() bool { | ||
return export.ready | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
package mirror | ||
|
||
import ( | ||
"errors" | ||
"github.com/ibuilding-x/driver-box/driverbox/plugin" | ||
) | ||
|
||
// Encode 编码数据 | ||
func (c *connector) Encode(deviceId string, mode plugin.EncodeMode, values ...plugin.PointData) (res interface{}, err error) { | ||
//本次操作的点位可能涉及到多个通讯设备,要先进行归类 | ||
group := make(map[string]EncodeModel) | ||
for _, point := range values { | ||
//匹配镜像设备 | ||
mirrorDevice, ok := c.mirrors[deviceId] | ||
if !ok { | ||
return nil, errors.New("mirror device not found") | ||
} | ||
//匹配原始设备 | ||
rawDevice, ok := mirrorDevice[point.PointName] | ||
if !ok { | ||
return nil, errors.New("mirror pointName not found") | ||
} | ||
if _, ok := group[rawDevice.deviceId]; !ok { | ||
group[rawDevice.deviceId] = EncodeModel{ | ||
deviceId: rawDevice.deviceId, | ||
points: make([]plugin.PointData, 0), | ||
mode: mode, | ||
} | ||
} | ||
model := group[rawDevice.deviceId] | ||
model.points = append(model.points, point) | ||
} | ||
//group转数组 | ||
models := make([]EncodeModel, 0) | ||
for _, model := range group { | ||
models = append(models, model) | ||
} | ||
return models, err | ||
} | ||
|
||
// Decode 数据来源于ExportTo | ||
func (c *connector) Decode(raw interface{}) (res []plugin.DeviceData, err error) { | ||
//真实通讯设备的数据 | ||
rawDeviceData := raw.(plugin.DeviceData) | ||
//镜像设备数据不支持二次镜像,否则配置失误时存在死循环风险 | ||
if _, ok := c.mirrors[rawDeviceData.ID]; ok { | ||
return []plugin.DeviceData{}, err | ||
} | ||
pointMapping, ok := c.rawMapping[rawDeviceData.ID] | ||
//当前通讯设备不存在镜像设备 | ||
if !ok { | ||
return []plugin.DeviceData{}, err | ||
} | ||
//镜像设备分组 | ||
group := make(map[string]plugin.DeviceData) | ||
for _, point := range rawDeviceData.Values { | ||
mirrors, ok := pointMapping[point.PointName] | ||
if !ok { | ||
continue | ||
} | ||
for _, mirror := range mirrors { | ||
//镜像设备分组以存在,填充点位 | ||
if mirrorData, ok := group[mirror.ID]; ok { | ||
mirrorData.Values = append(mirrorData.Values, point) | ||
continue | ||
} | ||
//创建镜像设备数据 | ||
group[mirror.ID] = plugin.DeviceData{ | ||
ID: mirror.ID, | ||
Values: []plugin.PointData{ | ||
point, | ||
}, | ||
} | ||
} | ||
} | ||
for _, data := range group { | ||
res = append(res, data) | ||
} | ||
return res, err | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
package mirror | ||
|
||
import ( | ||
"errors" | ||
"github.com/ibuilding-x/driver-box/driverbox/plugin" | ||
"github.com/ibuilding-x/driver-box/internal/core" | ||
) | ||
|
||
type connector struct { | ||
plugin *Plugin | ||
//镜像设备与真实设备的映射关系,镜像设备ID:{镜像点位:原始设备点位} | ||
//镜像设备的点位只会指向唯一的原始设备点位 | ||
mirrors map[string]map[string]Device | ||
//真实设备点位与镜像设备的映射关系, rawDeviceId:{rawPointName:{mirrorDevice:[mirrorPoint]}} | ||
//原始设备点位可能指向多个镜像设备和多个点位 | ||
//------------原始设备ID 原始点位 镜像点位------------ | ||
rawMapping map[string]map[string][]plugin.DeviceData | ||
} | ||
|
||
// Release 虚拟链接,无需释放 | ||
func (c *connector) Release() (err error) { | ||
return | ||
} | ||
|
||
// ProtocolAdapter 协议适配器 | ||
func (p *connector) ProtocolAdapter() plugin.ProtocolAdapter { | ||
return p | ||
} | ||
|
||
// Send 发送请求 | ||
func (c *connector) Send(raw interface{}) (err error) { | ||
var e error | ||
models := raw.([]EncodeModel) | ||
for _, encodeModel := range models { | ||
switch encodeModel.mode { | ||
case plugin.WriteMode: | ||
e = core.SendBatchWrite(encodeModel.deviceId, encodeModel.points) | ||
case plugin.ReadMode: | ||
e = core.SendBatchRead(encodeModel.deviceId, encodeModel.points) | ||
default: | ||
return errors.New("unknown mode") | ||
} | ||
if e != nil { | ||
err = e | ||
} | ||
} | ||
return err | ||
|
||
} | ||
|
||
func getMirrorKey(deviceId, pointName string) string { | ||
return deviceId + "_" + pointName | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
package mirror | ||
|
||
import "github.com/ibuilding-x/driver-box/driverbox/plugin" | ||
|
||
type EncodeModel struct { | ||
deviceId string | ||
points []plugin.PointData | ||
mode plugin.EncodeMode | ||
} | ||
|
||
// Device 原始设备 | ||
type Device struct { | ||
deviceId string | ||
pointName string | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,106 @@ | ||
package mirror | ||
|
||
import ( | ||
"errors" | ||
"github.com/ibuilding-x/driver-box/driverbox/config" | ||
"github.com/ibuilding-x/driver-box/driverbox/helper" | ||
"github.com/ibuilding-x/driver-box/driverbox/plugin" | ||
lua "github.com/yuin/gopher-lua" | ||
"go.uber.org/zap" | ||
) | ||
|
||
type Plugin struct { | ||
logger *zap.Logger // 日志记录器 | ||
config config.Config // 核心配置 | ||
ls *lua.LState // lua 虚拟机 | ||
VirtualConnector plugin.Connector //虚拟链接 | ||
} | ||
|
||
func (p *Plugin) Initialize(logger *zap.Logger, c config.Config, ls *lua.LState) (err error) { | ||
p.logger = logger | ||
p.config = c | ||
p.ls = ls | ||
|
||
mirrors := make(map[string]map[string]Device) | ||
rawDeviceMappings := make(map[string]map[string][]plugin.DeviceData) | ||
//生成镜像设备映射关系 | ||
for _, model := range p.config.DeviceModels { | ||
deviceCount := len(model.Devices) | ||
if deviceCount == 0 { | ||
continue | ||
} | ||
if deviceCount > 1 { | ||
return errors.New("mirror only support one device") | ||
} | ||
device := model.Devices[0] | ||
if _, ok := mirrors[device.ID]; ok { | ||
return errors.New("mirror device id must be unique") | ||
} | ||
|
||
mirrors[device.ID] = make(map[string]Device) | ||
for _, point := range model.DevicePoints { | ||
pointModel := point.ToPoint() | ||
if pointModel.Extends["rawDevice"] == nil || pointModel.Extends["rawPoint"] == nil { | ||
return errors.New("mirror point must have rawDevice and rawPoint") | ||
} | ||
//原始设备 | ||
rawDevice := pointModel.Extends["rawDevice"].(string) | ||
//原始设备点位 | ||
rawPoint := pointModel.Extends["rawPoint"].(string) | ||
//创建镜像设备与原始设备的映射关系 | ||
mirrors[device.ID][pointModel.Name] = Device{ | ||
deviceId: rawDevice, | ||
pointName: rawPoint, | ||
} | ||
|
||
//真实设备点位与镜像设备的映射关系 | ||
if _, ok := rawDeviceMappings[rawDevice]; !ok { | ||
//初始化设备映射 | ||
rawDeviceMappings[rawDevice] = make(map[string][]plugin.DeviceData) | ||
} | ||
rawPointMapping := rawDeviceMappings[rawDevice] | ||
if _, ok := rawPointMapping[rawPoint]; !ok { | ||
//初始化点位映射 | ||
rawPointMapping[rawPoint] = make([]plugin.DeviceData, 0) | ||
} | ||
ok := false | ||
for _, deviceData := range rawPointMapping[rawPoint] { | ||
if deviceData.ID != device.ID { | ||
continue | ||
} | ||
deviceData.Values = append(deviceData.Values, plugin.PointData{ | ||
PointName: pointModel.Name, | ||
}) | ||
ok = true | ||
break | ||
} | ||
if !ok { | ||
rawPointMapping[rawPoint] = append(rawPointMapping[rawPoint], plugin.DeviceData{ | ||
ID: device.ID, | ||
Values: []plugin.PointData{ | ||
{ | ||
PointName: pointModel.Name, | ||
}, | ||
}, | ||
}) | ||
} | ||
} | ||
} | ||
p.VirtualConnector = &connector{ | ||
plugin: p, | ||
mirrors: mirrors, | ||
rawMapping: rawDeviceMappings, | ||
} | ||
return nil | ||
} | ||
|
||
func (p *Plugin) Connector(deviceSn, pointName string) (connector plugin.Connector, err error) { | ||
return p.VirtualConnector, nil | ||
} | ||
|
||
func (p *Plugin) Destroy() error { | ||
if p.ls != nil { | ||
helper.Close(p.ls) | ||
} | ||
return nil | ||
} |