Skip to content

Commit

Permalink
Feat/media (#15)
Browse files Browse the repository at this point in the history
* [WIP] [feat] 多媒体数据上传

* [WIP] [feat] 多媒体数据

* [WIP] [feat] msg 9205/1205

* [WIP] [feat] 分包消息接收

* [WIP] [feat] 分包消息接收

* [WIP] [feat] 分包消息接收

* [WIP] [feat] 分包消息接收

* [WIP] [feat] 分包消息接收
  • Loading branch information
fakeyanss authored May 29, 2023
1 parent 16964d1 commit 7723c51
Show file tree
Hide file tree
Showing 22 changed files with 535 additions and 87 deletions.
6 changes: 5 additions & 1 deletion .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,11 @@
"type": "go",
"request": "launch",
"mode": "debug",
"program": "test/client/main.go"
"program": "test/client/main.go",
// "args": [
// "-c",
// "test/client/configs/default.yaml"
// ]
},
{
"name": "Launch Package",
Expand Down
25 changes: 22 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@

## 项目特点

### 兼容 2019/2013/2011 版本差异
### 兼容 JT808 2019/2013/2011 版本差异

定义版本类型分为 `Version2019 / Version2013 / Version2011`

Expand All @@ -29,6 +29,8 @@
| 终端 ID 编码长度 | 7 字节 | 7 字节 | 30 字节 |
| 从业资格证编码长度 | 40 字节 | 20 字节 | 20 字节 |

### 支持 JT1078 协议的音视频传输控制

### 支持常见消息列表 (WIP)

| 终端侧 | 平台侧 |
Expand Down Expand Up @@ -297,7 +299,10 @@ todo

<img src="https://ghproxy.com/https://raw.githubusercontent.com/fakeYanss/imgplace/master/2023/2023-02-18_20230218211153.png" alt="device manage" width="300">

<!--
在早些时候,查询多媒体资源列表会通过 JT808 协议的信令交互。后来在实际应用中发现,视频和音频的传输会长时间占用连接通道,这期间其他的操作啥也干不了,只能等着音视频数据传输完。这样不太好,所以推出了 JT1078 协议,此后在 JT808 协议交互中最多进行图片资源的传输,而音视频的传输则通过 JT1078 中的信令消息。

详细来说,就是在 JT1078 中特别指定了 0x0800/0x0801/0x8802/0x0802/0x8803 这 5 条信令消息中多媒体字段只应包含图片类型。

```plantuml
@startuml
Expand Down Expand Up @@ -351,11 +356,25 @@ group 平台下发
c -> s: 通用应答 0x0001
deactivate c
deactivate s
s -> c: 平台查询终端音视频资源列表 0x9205
activate s
activate c
c -> s: 终端上传音视频资源列表 0x1205
deactivate c
deactivate s
s -> c: 平台查询终端音视频资源列表 0x9205
activate s
activate c
c -> s: 终端上传音视频资源列表 0x1205
deactivate c
deactivate s
end
@enduml
```
-->

## 编译和运行

Expand Down
33 changes: 0 additions & 33 deletions internal/codec/hex/hex.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,17 +93,6 @@ func WriteByte(pkt []byte, num uint8) []byte {
return append(pkt, num)
}

func any2uint8(a any) uint8 {
if b, ok := a.(float64); ok {
return uint8(b)
}
return a.(uint8)
}

func WriteByteAny(pkt []byte, num any) []byte {
return WriteByte(pkt, any2uint8(num))
}

// 对应JT808类型WORD
func ReadWord(pkt []byte, idx *int) uint16 {
ans := binary.BigEndian.Uint16(pkt[*idx : *idx+2])
Expand All @@ -118,17 +107,6 @@ func WriteWord(pkt []byte, num uint16) []byte {
return append(pkt, numPkt...)
}

func any2uint16(a any) uint16 {
if b, ok := a.(float64); ok {
return uint16(b)
}
return a.(uint16)
}

func WriteWordAny(pkt []byte, num any) []byte {
return WriteWord(pkt, any2uint16(num))
}

// 对应JT808类型DWORD
func ReadDoubleWord(pkt []byte, idx *int) uint32 {
ans := binary.BigEndian.Uint32(pkt[*idx : *idx+4])
Expand All @@ -143,17 +121,6 @@ func WriteDoubleWord(pkt []byte, num uint32) []byte {
return append(pkt, numPkt...)
}

func any2uint32(a any) uint32 {
if b, ok := a.(float64); ok {
return uint32(b)
}
return a.(uint32)
}

func WriteDoubleWordAny(pkt []byte, num any) []byte {
return WriteDoubleWord(pkt, any2uint32(num))
}

// 对应JT808类型BYTE[n]
func ReadBytes(pkt []byte, idx *int, n int) []byte {
ans := pkt[*idx : *idx+n]
Expand Down
4 changes: 2 additions & 2 deletions internal/config/asset.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

58 changes: 58 additions & 0 deletions internal/protocol/model/device_media.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package model

import (
"time"

"github.com/fakeyanss/jt808-server-go/internal/codec/hex"
)

type DeviceMediaQuery struct {
LogicChannelID uint8 `json:"logicChannelId"` // 逻辑通道号
StartTime *time.Time `json:"startTime"` // 开始时间
EndTime *time.Time `json:"endTime"` // 结束时间
AlarmSign uint32 `json:"alarmSign"` // 报警标志位。bit0-bit31为0x0200的报警标志位,
AlarmSignExt uint32 `json:"alarmSignExt"` // 报警标志位。bit32-bit63?,全0表示无报警类型条件
MediaType uint8 `json:"mediaType"` // 音视频类型。0:音视频;1:音频;2:视频;3:视频或音视频
StreamType uint8 `json:"streamType"` // 码流类型。0:所有码流;1:主码流;2:子码流
StorageType uint8 `json:"storageType"` // 存储器类型。0:所有存储器;1:主存储器;2:灾备存储器
}

func (q *DeviceMediaQuery) Decode(pkt []byte, idx *int) {
q.LogicChannelID = hex.ReadByte(pkt, idx)
q.StartTime = hex.ReadTime(pkt, idx)
q.EndTime = hex.ReadTime(pkt, idx)
q.AlarmSign = hex.ReadDoubleWord(pkt, idx)
q.AlarmSignExt = hex.ReadDoubleWord(pkt, idx)
q.MediaType = hex.ReadByte(pkt, idx)
q.StreamType = hex.ReadByte(pkt, idx)
q.StorageType = hex.ReadByte(pkt, idx)
}

func (q *DeviceMediaQuery) Encode() (pkt []byte) {
pkt = hex.WriteByte(pkt, q.LogicChannelID)
pkt = hex.WriteTime(pkt, *q.StartTime)
pkt = hex.WriteTime(pkt, *q.EndTime)
pkt = hex.WriteDoubleWord(pkt, q.AlarmSign)
pkt = hex.WriteDoubleWord(pkt, q.AlarmSignExt)
pkt = hex.WriteByte(pkt, q.MediaType)
pkt = hex.WriteByte(pkt, q.StreamType)
pkt = hex.WriteByte(pkt, q.StorageType)
return pkt
}

type DeviceMedia struct {
DeviceMediaQuery
Size uint32 // 文件大小,单位Byte
}

func (m *DeviceMedia) Decode(pkt []byte) {
idx := 0
m.DeviceMediaQuery.Decode(pkt, &idx)
m.Size = hex.ReadDoubleWord(pkt, &idx)
}

func (m *DeviceMedia) Encode() (pkt []byte) {
pkt = m.DeviceMediaQuery.Encode()
pkt = hex.WriteDoubleWord(pkt, m.Size)
return pkt
}
94 changes: 75 additions & 19 deletions internal/protocol/model/device_params.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package model

import (
"fmt"
"sort"

"github.com/pkg/errors"
Expand All @@ -10,8 +11,9 @@ import (
)

var (
ErrDecodeDeviceParams = errors.New("Fail to decode device params")
ErrEncodeDeviceParams = errors.New("Fail to encode device params")
ErrDecodeDeviceParams = errors.New("Fail to decode device params")
ErrEncodeDeviceParams = errors.New("Fail to encode device params")
ErrParamIDNotSupportted = errors.New("Param id is not supportted")
)

type DeviceParams struct {
Expand Down Expand Up @@ -81,9 +83,11 @@ type ParamData struct {
func (p *ParamData) Decode(pkt []byte, idx *int) error {
p.ParamID = hex.ReadDoubleWord(pkt, idx)
p.ParamLen = hex.ReadByte(pkt, idx)
if fn, ok := argTable[p.ParamID]; ok {
p.ParamValue = fn.decode(pkt, idx, int(p.ParamLen))
fn, ok := argTable[p.ParamID]
if !ok {
log.Warn().Str("ParamID", fmt.Sprintf("0x%04x", p.ParamID)).Err(ErrParamIDNotSupportted).Msg("skip it")
}
p.ParamValue = fn.decode(pkt, idx, int(p.ParamLen))
return nil
}

Expand All @@ -95,7 +99,8 @@ func (p *ParamData) Encode() (pkt []byte, err error) {
pkt = hex.WriteBytes(pkt, value)
return pkt, nil
}
return nil, ErrEncodeDeviceParams
log.Warn().Str("ParamID", fmt.Sprintf("0x%04x", p.ParamID)).Err(ErrParamIDNotSupportted).Msg("skip it")
return nil, ErrParamIDNotSupportted
}

type paramFn struct {
Expand All @@ -114,22 +119,57 @@ type paramFn struct {
//
// 所以需要再encode时,将其按照json默认类型推断,再进行强转

func any2uint8(a any) uint8 {
if b, ok := a.(float64); ok {
return uint8(b)
}
return a.(uint8)
}

func writeByteAny(pkt []byte, num any) []byte {
return hex.WriteByte(pkt, any2uint8(num))
}

func any2uint16(a any) uint16 {
if b, ok := a.(float64); ok {
return uint16(b)
}
return a.(uint16)
}

func writeWordAny(pkt []byte, num any) []byte {
return hex.WriteWord(pkt, any2uint16(num))
}

func any2uint32(a any) uint32 {
if b, ok := a.(float64); ok {
return uint32(b)
}
return a.(uint32)
}

func writeDoubleWordAny(pkt []byte, num any) []byte {
return hex.WriteDoubleWord(pkt, any2uint32(num))
}

var (
decodeByte = func(b []byte, idx *int, paramLen int) any { return hex.ReadByte(b, idx) }
encodeByte = func(a any) (pkt []byte) { return hex.WriteByteAny(pkt, a) }
encodeByte = func(a any) (pkt []byte) { return writeByteAny(pkt, a) }
decodeWord = func(b []byte, idx *int, paramLen int) any { return hex.ReadWord(b, idx) }
encodeWord = func(a any) (pkt []byte) { return hex.WriteWordAny(pkt, a) }
encodeWord = func(a any) (pkt []byte) { return writeWordAny(pkt, a) }
decodeDoubleWord = func(b []byte, idx *int, paramLen int) any { return hex.ReadDoubleWord(b, idx) }
encodeDoubleWord = func(a any) (pkt []byte) { return hex.WriteDoubleWordAny(pkt, a) }
decodeBCD = func(b []byte, idx *int, paramLen int) any { return hex.ReadBCD(b, idx, paramLen) }
encodeBCD = func(a any) (pkt []byte) { return hex.WriteBCD(pkt, a.(string)) }
encodeDoubleWord = func(a any) (pkt []byte) { return writeDoubleWordAny(pkt, a) }
decodeBytes = func(b []byte, idx *int, paramLen int) any { return hex.ReadBCD(b, idx, paramLen) } // transform bytes to string
encodeBytes = func(a any) (pkt []byte) { return hex.WriteBCD(pkt, a.(string)) } // transform string to bytes
decodeString = func(b []byte, idx *int, paramLen int) any { return hex.ReadString(b, idx, paramLen) }
encodeString = func(a any) (pkt []byte) { return hex.WriteString(pkt, a.(string)) }
decodeGBK = func(b []byte, idx *int, paramLen int) any { return hex.ReadGBK(b, idx, paramLen) }
encodeGBK = func(a any) (pkt []byte) { return hex.WriteGBK(pkt, a.(string)) }
)

var argTable = map[uint32]*paramFn{
// JT808 param

// 终端心跳发送间隔,单位为秒(s)
0x0001: {decode: decodeDoubleWord, encode: encodeDoubleWord},
// TCP消息应答超时时间,单位为秒(s)
Expand Down Expand Up @@ -160,9 +200,9 @@ var argTable = map[uint32]*paramFn{
0x0016: {decode: decodeGBK, encode: encodeGBK},
// 备份服务器地址,IP或域名(2019版以冒号分割主机和端口,多个服务器使用分号分隔)
0x0017: {decode: decodeGBK, encode: encodeGBK},
// (JTT2013)服务器TCP端口
// (JT808 2013)服务器TCP端口
0x0018: {decode: decodeDoubleWord, encode: encodeDoubleWord},
// (JTT2013)服务器UDP端口
// (JT808 2013)服务器UDP端口
0x0019: {decode: decodeDoubleWord, encode: encodeDoubleWord},
// 道路运输证IC卡认证主服务器IP地址或域名
0x001A: {decode: decodeGBK, encode: encodeGBK},
Expand All @@ -178,13 +218,13 @@ var argTable = map[uint32]*paramFn{
0x0021: {decode: decodeDoubleWord, encode: encodeDoubleWord},
// 驾驶员未登录汇报时间间隔,单位为秒(s),>0
0x0022: {decode: decodeDoubleWord, encode: encodeDoubleWord},
// (JTT2019)从服务器APN.该值为空时,终端应使用主服务器相同配置
// (JT808 2019)从服务器APN.该值为空时,终端应使用主服务器相同配置
0x0023: {decode: decodeGBK, encode: encodeGBK},
// (JTT2019)从服务器无线通信拨号用户名.该值为空时,终端应使用主服务器相同配置
// (JT808 2019)从服务器无线通信拨号用户名.该值为空时,终端应使用主服务器相同配置
0x0024: {decode: decodeGBK, encode: encodeGBK},
// (JTT2019)从服务器无线通信拨号密码.该值为空时,终端应使用主服务器相同配置
// (JT808 2019)从服务器无线通信拨号密码.该值为空时,终端应使用主服务器相同配置
0x0025: {decode: decodeGBK, encode: encodeGBK},
// (JTT2019)从服务器备份地址、IP或域名.主服务器IP地址或域名,端口同主服务器
// (JT808 2019)从服务器备份地址、IP或域名.主服务器IP地址或域名,端口同主服务器
0x0026: {decode: decodeGBK, encode: encodeGBK},
// 休眠时汇报时间间隔,单位为秒(s),>0
0x0027: {decode: decodeDoubleWord, encode: encodeDoubleWord},
Expand All @@ -204,12 +244,12 @@ var argTable = map[uint32]*paramFn{
0x0030: {decode: decodeDoubleWord, encode: encodeDoubleWord},
// 电子围栏半径,单位为米
0x0031: {decode: decodeWord, encode: encodeWord},
// (JTT2019)违规行驶时段范围,精确到分。
// (JT808 2019)违规行驶时段范围,精确到分。
// byte1:违规行驶开始时间的小时部分;
// byte2:违规行驶开始的分钟部分;
// byte3:违规行驶结束时间的小时部分;
// byte4:违规行驶结束时间的分钟部分。
0x0032: {decode: decodeBCD, encode: encodeBCD},
0x0032: {decode: decodeBytes, encode: encodeBytes},
// 监控平台电话号码
0x0040: {decode: decodeGBK, encode: encodeGBK},
// 复位电话号码,可采用此电话号码拨打终端电话让终端复位
Expand Down Expand Up @@ -282,7 +322,7 @@ var argTable = map[uint32]*paramFn{
0x0082: {decode: decodeWord, encode: encodeWord},
// 公安交通管理部门颁发的机动车号牌
0x0083: {decode: decodeGBK, encode: encodeGBK},
// 车牌颜色,按照JT/T415-2006的5.4.12
// 车牌颜色,按照JT415-2006的5.4.12
0x0084: {decode: decodeByte, encode: encodeByte},
// GNSS定位模式,定义如下:
// bit0,0:禁用GPS定位,1:启用 GPS 定位;
Expand Down Expand Up @@ -337,4 +377,20 @@ var argTable = map[uint32]*paramFn{
// bit29 表示数据采集方式,0:原始数据,1:采集区间的计算值;
// bit28-bit0 表示 CAN 总线 ID。
0x0110: {decode: decodeString, encode: encodeString},

// JT1078 param
// 音视频参数设置
0x0075: {decode: decodeBytes, encode: encodeBytes},
// 音视频通道列表设置
0x0076: {decode: decodeBytes, encode: encodeBytes},
// 单独通道视频参数设置
0x0077: {decode: decodeBytes, encode: encodeBytes},
// 特殊报警录像参数设置
0x0079: {decode: decodeBytes, encode: encodeBytes},
// 视频相关报警屏蔽字
0x007A: {decode: decodeBytes, encode: encodeBytes},
// 图像分析报警参数设置
0x007B: {decode: decodeBytes, encode: encodeBytes},
// 终端休眠唤醒模式设置
0x007C: {decode: decodeBytes, encode: encodeBytes},
}
Loading

0 comments on commit 7723c51

Please sign in to comment.