mirror of
https://github.com/Monibuca/plugin-gb28181.git
synced 2025-12-24 13:27:57 +08:00
feat:预置位操作接口和列表查询
This commit is contained in:
21
README.md
21
README.md
@@ -139,3 +139,24 @@ http 200 表示成功,404流不存在
|
||||
| id | 是 | 设备ID |
|
||||
| expires | 是 | 订阅周期(秒) |
|
||||
| interval | 是 | 订阅间隔(秒) |
|
||||
|
||||
### 预置位列表查询
|
||||
|
||||
`/gb28181/api/preset/list`
|
||||
|
||||
| 参数名 | 必传 | 含义 |
|
||||
| -------- | ---- | -------------- |
|
||||
| id | 是 | 设备ID |
|
||||
| channel | 是 | 通道编号 |
|
||||
|
||||
|
||||
### 预置位操作
|
||||
|
||||
`/gb28181/api/preset/control`
|
||||
|
||||
| 参数名 | 必传 | 含义 |
|
||||
| -------- | ---- |---------------------|
|
||||
| id | 是 | 设备ID |
|
||||
| channel | 是 | 通道编号 |
|
||||
| cmd | 是 | 操作指令 0=新增,1=删除,2=调用 |
|
||||
| point | 是 | 预置点位1-255 |
|
||||
|
||||
41
channel.go
41
channel.go
@@ -31,6 +31,7 @@ func (p *PullStream) CreateRequest(method sip.RequestMethod) (req sip.Request) {
|
||||
req = p.channel.CreateRequst(method)
|
||||
from, _ := res.From()
|
||||
to, _ := res.To()
|
||||
|
||||
callId, _ := res.CallID()
|
||||
req.ReplaceHeaders(from.Name(), []sip.Header{from})
|
||||
req.ReplaceHeaders(to.Name(), []sip.Header{to})
|
||||
@@ -120,6 +121,11 @@ type Channel struct {
|
||||
ChannelInfo
|
||||
}
|
||||
|
||||
type PresetInfo struct {
|
||||
PresetID int `json:"-" yaml:"-"` //
|
||||
PresetName string `json:"-" yaml:"-"` //
|
||||
}
|
||||
|
||||
func (c *Channel) MarshalJSON() ([]byte, error) {
|
||||
m := map[string]any{
|
||||
"DeviceID": c.DeviceID,
|
||||
@@ -264,6 +270,41 @@ func (channel *Channel) QueryRecord(startTime, endTime string) ([]*Record, error
|
||||
return r.list, r.err
|
||||
}
|
||||
|
||||
func (channel *Channel) QueryPresetList() (sip.Response, error) {
|
||||
d := channel.Device
|
||||
request := d.CreateRequest(sip.MESSAGE)
|
||||
contentType := sip.ContentType("Application/MANSCDP+xml")
|
||||
request.AppendHeader(&contentType)
|
||||
|
||||
body := BuildPresetListXML(100, channel.DeviceID)
|
||||
request.SetBody(body, true)
|
||||
|
||||
resp, err := d.SipRequestForResponse(request)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("query error: %s", err)
|
||||
}
|
||||
if resp.StatusCode() != http.StatusOK {
|
||||
return nil, fmt.Errorf("query error, status=%d", resp.StatusCode())
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func (channel *Channel) PresetControl(ptzCode int, point byte) int {
|
||||
cmd := byte(PresetSet)
|
||||
switch ptzCode {
|
||||
case PresetAddPoint:
|
||||
cmd = PresetSet
|
||||
case PresetDelPoint:
|
||||
cmd = PresetDel
|
||||
case PresetCallPoint:
|
||||
cmd = PresetCall
|
||||
default:
|
||||
|
||||
}
|
||||
PTZCmd := Pack(cmd, point)
|
||||
return channel.Control(PTZCmd)
|
||||
}
|
||||
|
||||
func (channel *Channel) Control(PTZCmd string) int {
|
||||
d := channel.Device
|
||||
request := d.CreateRequest(sip.MESSAGE)
|
||||
|
||||
@@ -274,6 +274,8 @@ func (c *GB28181Config) OnMessage(req sip.Request, tx sip.ServerTransaction) {
|
||||
body = BuildAlarmResponseXML(d.ID)
|
||||
case "Broadcast":
|
||||
GB28181Plugin.Info("broadcast message", zap.String("body", req.Body()))
|
||||
case "PresetQuery":
|
||||
GB28181Plugin.Info("PresetQuery message", zap.String("body", req.Body()))
|
||||
default:
|
||||
d.Warn("Not supported CmdType", zap.String("CmdType", temp.CmdType), zap.String("body", req.Body()))
|
||||
response := sip.NewResponseFromRequest("", req, http.StatusBadRequest, "", "")
|
||||
|
||||
25
manscdp.go
25
manscdp.go
@@ -1,12 +1,23 @@
|
||||
package gb28181
|
||||
|
||||
import (
|
||||
"encoding/xml"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
// 获取预置位列表
|
||||
PresentListXML = `
|
||||
<?xml version="1.0"?>
|
||||
<Query>
|
||||
<CmdType>PresetQuery</CmdType>
|
||||
<SN>%d</SN>
|
||||
<DeviceID>%s</DeviceID>
|
||||
</Query>
|
||||
`
|
||||
|
||||
// CatalogXML 获取设备列表xml样式
|
||||
CatalogXML = `<?xml version="1.0"?><Query>
|
||||
<CmdType>Catalog</CmdType>
|
||||
@@ -65,6 +76,10 @@ func BuildCatalogXML(sn int, id string) string {
|
||||
return fmt.Sprintf(CatalogXML, sn, id)
|
||||
}
|
||||
|
||||
func BuildPresetListXML(sn int, id string) string {
|
||||
return fmt.Sprintf(PresentListXML, sn, id)
|
||||
}
|
||||
|
||||
// BuildRecordInfoXML 获取录像文件列表指令
|
||||
func BuildRecordInfoXML(sn int, id string, start, end int64) string {
|
||||
return fmt.Sprintf(RecordInfoXML, sn, id, intTotime(start).Format("2006-01-02T15:04:05"), intTotime(end).Format("2006-01-02T15:04:05"))
|
||||
@@ -90,3 +105,13 @@ var (
|
||||
func BuildAlarmResponseXML(id string) string {
|
||||
return fmt.Sprintf(AlarmResponseXML, id)
|
||||
}
|
||||
|
||||
func XmlEncode(v interface{}) (string, error) {
|
||||
xmlData, err := xml.MarshalIndent(v, "", " ")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
xml := string(xmlData)
|
||||
xml = `<?xml version="1.0" ?>` + "\n" + xml + "\n"
|
||||
return xml, err
|
||||
}
|
||||
|
||||
62
ptz.go
62
ptz.go
@@ -1,6 +1,10 @@
|
||||
package gb28181
|
||||
|
||||
import "fmt"
|
||||
import (
|
||||
"encoding/hex"
|
||||
"encoding/xml"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
var (
|
||||
name2code = map[string]uint8{
|
||||
@@ -18,6 +22,35 @@ var (
|
||||
}
|
||||
)
|
||||
|
||||
type PresetCmd byte
|
||||
|
||||
const (
|
||||
PresetAddPoint = 0
|
||||
PresetDelPoint = 1
|
||||
PresetCallPoint = 2
|
||||
)
|
||||
|
||||
const DeviceControl = "DeviceControl"
|
||||
const PTZFirstByte = 0xA5
|
||||
const (
|
||||
PresetSet = 0x81
|
||||
PresetCall = 0x82
|
||||
PresetDel = 0x83
|
||||
)
|
||||
|
||||
type MessagePtz struct {
|
||||
XMLName xml.Name `xml:"Control"`
|
||||
CmdType string `xml:"CmdType"`
|
||||
SN int `xml:"SN"`
|
||||
DeviceID string `xml:"DeviceID"`
|
||||
PTZCmd string `xml:"PTZCmd"`
|
||||
}
|
||||
|
||||
type Preset struct {
|
||||
CMD byte
|
||||
Point byte
|
||||
}
|
||||
|
||||
func toPtzStrByCmdName(cmdName string, horizontalSpeed, verticalSpeed, zoomSpeed uint8) (string, error) {
|
||||
c, err := toPtzCode(cmdName)
|
||||
if err != nil {
|
||||
@@ -45,3 +78,30 @@ func toPtzCode(cmd string) (uint8, error) {
|
||||
return 0, fmt.Errorf("invalid ptz cmd %q", cmd)
|
||||
}
|
||||
}
|
||||
|
||||
func getVerificationCode(ptz []byte) {
|
||||
sum := uint8(0)
|
||||
for i := 0; i < len(ptz)-1; i++ {
|
||||
sum += ptz[i]
|
||||
}
|
||||
ptz[len(ptz)-1] = sum
|
||||
}
|
||||
|
||||
func getAssembleCode() uint8 {
|
||||
return (PTZFirstByte>>4 + PTZFirstByte&0xF + 0) % 16
|
||||
}
|
||||
|
||||
func Pack(cmd, point byte) string {
|
||||
buf := make([]byte, 8)
|
||||
buf[0] = PTZFirstByte
|
||||
buf[1] = getAssembleCode()
|
||||
buf[2] = 0
|
||||
|
||||
buf[3] = cmd
|
||||
|
||||
buf[4] = 0
|
||||
buf[5] = point
|
||||
buf[6] = 0
|
||||
getVerificationCode(buf)
|
||||
return hex.EncodeToString(buf)
|
||||
}
|
||||
|
||||
41
restful.go
41
restful.go
@@ -230,6 +230,47 @@ func (c *GB28181Config) API_position(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
}
|
||||
|
||||
func (c *GB28181Config) API_preset_list(w http.ResponseWriter, r *http.Request) {
|
||||
query := r.URL.Query()
|
||||
//设备id
|
||||
id := query.Get("id")
|
||||
//获取通道
|
||||
channel := query.Get("channel")
|
||||
if c := FindChannel(id, channel); c != nil {
|
||||
res, err := c.QueryPresetList()
|
||||
if err == nil {
|
||||
util.ReturnValue(res, w, r)
|
||||
} else {
|
||||
util.ReturnError(util.APIErrorInternal, err.Error(), w, r)
|
||||
}
|
||||
} else {
|
||||
util.ReturnError(util.APIErrorNotFound, fmt.Sprintf("device %q channel %q not found", id, channel), w, r)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (c *GB28181Config) API_preset_control(w http.ResponseWriter, r *http.Request) {
|
||||
//CORS(w, r)
|
||||
query := r.URL.Query()
|
||||
//设备id
|
||||
id := query.Get("id")
|
||||
//获取通道
|
||||
channel := query.Get("channel")
|
||||
//获取cmd
|
||||
ptzCmd := query.Get("cmd")
|
||||
//获取点
|
||||
point := query.Get("point")
|
||||
|
||||
if c := FindChannel(id, channel); c != nil {
|
||||
_ptzCmd, _ := strconv.ParseInt(ptzCmd, 10, 16)
|
||||
_point, _ := strconv.ParseInt(point, 10, 8)
|
||||
code := c.PresetControl(int(_ptzCmd), byte(_point))
|
||||
util.ReturnError(code, "device received", w, r)
|
||||
} else {
|
||||
util.ReturnError(util.APIErrorNotFound, fmt.Sprintf("device %q channel %q not found", id, channel), w, r)
|
||||
}
|
||||
}
|
||||
|
||||
type DevicePosition struct {
|
||||
ID string
|
||||
GpsTime time.Time //gps时间
|
||||
|
||||
Reference in New Issue
Block a user