diff --git a/pkg/config/loader.go b/pkg/config/loader.go index d72c1d6..0e21631 100644 --- a/pkg/config/loader.go +++ b/pkg/config/loader.go @@ -11,6 +11,7 @@ import ( ) func GenerateConfig() error { + // 生成配置文件 p, _ := filepath.Abs("./config.toml") flag := os.O_RDWR @@ -35,6 +36,7 @@ func GenerateConfig() error { } func ReadClientConfig() (*ClientConfig, error) { + // 读取客户端配置文件,如果不存在则生成示例配置 viper.SetConfigName(constants.ConfigFileName) viper.SetConfigType(constants.ConfigType) for _, path := range constants.ConfigPaths { diff --git a/pkg/handler/message/message.go b/pkg/handler/message/message.go index b56512b..b047ae0 100644 --- a/pkg/handler/message/message.go +++ b/pkg/handler/message/message.go @@ -9,17 +9,28 @@ import ( "github.com/emiago/sipgo/sip" ) +// messageHandler 是一个处理SIP消息的函数类型 type messageHandler = func(client *sipgo.Client, clientConfig *config.ClientConfig, req *sip.Request, tx sip.ServerTransaction) +// handlers 是一个存储消息处理器的映射,键为命令类型,值为消息处理器函数 var handlers = make(map[string]messageHandler) +// SetupMessageHandler 设置SIP服务器的消息处理器,根据消息的命令类型调用相应的处理器 func SetupMessageHandler(srv *sipgo.Server, client *sipgo.Client, clientConfig *config.ClientConfig) { srv.OnMessage(func(req *sip.Request, tx sip.ServerTransaction) { body := req.Body() + if len(body) == 0 { + log.Log().Warn().Msgf("消息体为空") + _ = tx.Respond(sip.NewResponseFromRequest(req, sip.StatusOK, "OK", nil)) + return + } + message := new(manscdp.MessageReq) err := utils.XMLUnmarshal(body, message) if err != nil { log.Log().Error().Msgf("XML Unmarshal error: %v", err) + _ = tx.Respond(sip.NewResponseFromRequest(req, sip.StatusBadRequest, "Bad Request", nil)) + return } handlers, ok := handlers[message.CmdType] diff --git a/pkg/manscdp/catalog.go b/pkg/manscdp/catalog.go index 856e6d5..7acb7b0 100644 --- a/pkg/manscdp/catalog.go +++ b/pkg/manscdp/catalog.go @@ -2,6 +2,7 @@ package manscdp import "encoding/xml" +// CatalogReq 定义了查询目录的请求结构 type CatalogReq struct { XMLName xml.Name `xml:"Query"` CmdType string `xml:"CmdType"` @@ -9,6 +10,7 @@ type CatalogReq struct { DeviceID string `xml:"DeviceID"` } +// CatalogResp 定义了查询目录的响应结构 type CatalogResp struct { XMLName xml.Name `xml:"Response"` CmdType string `xml:"CmdType"` @@ -18,12 +20,14 @@ type CatalogResp struct { DeviceID string `xml:"DeviceID"` } +// CateLogDeviceList 定义了设备列表的结构 type CateLogDeviceList struct { XMLName xml.Name `xml:"DeviceList"` Num string `xml:"Num,attr"` Item []CateLogDevice `xml:"Item"` } +// CateLogDevice 定义了单个设备的详细信息结构 type CateLogDevice struct { XMLName xml.Name `xml:"Item"` Name string `xml:"Name"` diff --git a/pkg/manscdp/message.go b/pkg/manscdp/message.go index 5161c04..f52312d 100644 --- a/pkg/manscdp/message.go +++ b/pkg/manscdp/message.go @@ -2,6 +2,7 @@ package manscdp import "encoding/xml" +// MessageReq 定义了查询消息的请求结构体,使用XML格式进行序列化和反序列化 type MessageReq struct { XMLName xml.Name `xml:"Query"` CmdType string `xml:"CmdType"` diff --git a/pkg/services/zlmediakit/api.go b/pkg/services/zlmediakit/api.go index e5423ef..5e6c168 100644 --- a/pkg/services/zlmediakit/api.go +++ b/pkg/services/zlmediakit/api.go @@ -1,6 +1,8 @@ package zlmediakit import ( + "encoding/json" + "git.skcks.cn/Shikong/go-gb28181/pkg/services/zlmediakit/types" "github.com/go-resty/resty/v2" "time" ) @@ -22,3 +24,51 @@ func SetupZLMediaKitService(config *Config) { client: client, } } + +func GetZLMediaKitService() *ZLMediaKit { + return zLMediaKitService +} + +func (z *ZLMediaKit) GetApiList() (data *types.Data[[]string], err error) { + resp, err := z.client.R().Get("/index/api/getApiList") + if err != nil { + return nil, err + } + + data = new(types.Data[[]string]) + err = json.Unmarshal(resp.Body(), data) + if err != nil { + return nil, err + } + return data, nil +} + +func (z *ZLMediaKit) GetServerConfig() (data *types.ServerConfigResp, err error) { + resp, err := z.client.R().Get("/index/api/getServerConfig") + if err != nil { + return nil, err + } + + data = new(types.ServerConfigResp) + err = json.Unmarshal(resp.Body(), data) + if err != nil { + return nil, err + } + return data, nil +} + +func (z *ZLMediaKit) SetServerConfig(config *types.ServerConfig) (data *types.SetServerConfigResp, err error) { + resp, err := z.client.R(). + SetBody(config). + Post("/index/api/setServerConfig") + if err != nil { + return nil, err + } + + data = new(types.SetServerConfigResp) + err = json.Unmarshal(resp.Body(), data) + if err != nil { + return nil, err + } + return data, nil +} diff --git a/pkg/services/zlmediakit/api_test.go b/pkg/services/zlmediakit/api_test.go index 209800d..4d802c9 100644 --- a/pkg/services/zlmediakit/api_test.go +++ b/pkg/services/zlmediakit/api_test.go @@ -5,18 +5,22 @@ import ( "fmt" "git.skcks.cn/Shikong/go-gb28181/pkg/services/zlmediakit/types" "github.com/duke-git/lancet/v2/convertor" + "github.com/duke-git/lancet/v2/formatter" "github.com/duke-git/lancet/v2/netutil" "github.com/go-resty/resty/v2" + "strings" "testing" ) func TestZLMediaKit(t *testing.T) { + // 设置ZLMediaKit服务,传入配置信息 SetupZLMediaKitService(&Config{ Id: "amrWMKmbKqoBjRQ9", Url: "http://10.10.10.200:5081", Secret: "4155cca6-2f9f-11ee-85e6-8de4ce2e7333", }) + // 获取媒体列表并检查响应 resp, err := zLMediaKitService.client.R().Get("/index/api/getMediaList") if err != nil { t.Fatal(err) @@ -27,6 +31,67 @@ func TestZLMediaKit(t *testing.T) { for _, datum := range data.Data { t.Logf("%+v\n", datum) } + + // 获取ZLMediaKit服务实例 + service := GetZLMediaKitService() + + // 获取API列表并打印数据 + apiList, err := service.GetApiList() + printData(apiList, err, t) + + // 打印分隔线 + t.Log(strings.Repeat("=", 50)) + + // 获取服务器配置并打印数据 + getConfigResp, err := service.GetServerConfig() + printData(getConfigResp, err, t) + + // 深度克隆配置并修改SRT超时时间,然后设置服务器配置 + config := convertor.DeepClone(getConfigResp.Data[0]) + config.SrtTimeoutSec = "5" + serverConfigResp, err := service.SetServerConfig(&config) + printResp(serverConfigResp, err, t) + + // 再次获取服务器配置并打印数据,以验证修改是否成功 + getConfigResp, err = service.GetServerConfig() + printData(getConfigResp, err, t) + + // 打印分隔线 + t.Log(strings.Repeat("=", 50)) +} + +func printResp[T any](data *T, err error, t *testing.T) { + if err != nil { + t.Error(err) + } else { + pretty, _ := formatter.Pretty(data) + t.Log(pretty) + } +} + +func printData[T any](data *types.Data[T], err error, t *testing.T) { + if err != nil { + t.Error(err) + } else { + + switch x := interface{}(data.Data).(type) { + case string, int, int64, float32, float64, bool, nil, []byte: + t.Log(x) + + case []any: + for _, s := range x { + t.Logf("\t%+v\n", s) + } + case []string: + for _, s := range x { + t.Logf("\t%+v\n", s) + } + + default: + result, _ := formatter.Pretty(x) + t.Logf("%s\n", result) + } + } } func TestZLMediaKit_API(t *testing.T) { diff --git a/pkg/services/zlmediakit/types/base.go b/pkg/services/zlmediakit/types/base.go index 4886aca..97cdf66 100644 --- a/pkg/services/zlmediakit/types/base.go +++ b/pkg/services/zlmediakit/types/base.go @@ -1,5 +1,6 @@ package types +// Data 是一个泛型结构体,用于封装 API 响应的数据 type Data[T any] struct { Code int `json:"code"` Data T `json:"data"` diff --git a/pkg/services/zlmediakit/types/server_config.go b/pkg/services/zlmediakit/types/server_config.go index e401715..665bc52 100644 --- a/pkg/services/zlmediakit/types/server_config.go +++ b/pkg/services/zlmediakit/types/server_config.go @@ -4,6 +4,11 @@ type ServerConfigResp = Data[ServerConfigRespRaw] type ServerConfigRespRaw = []ServerConfig +type SetServerConfigResp struct { + Code int `json:"code"` + Changed int `json:"changed"` +} + type ServerConfig struct { ApiApiDebug string `json:"api.apiDebug"` ApiDefaultSnap string `json:"api.defaultSnap"` diff --git a/pkg/utils/xml.go b/pkg/utils/xml.go index 411b82e..1887d43 100644 --- a/pkg/utils/xml.go +++ b/pkg/utils/xml.go @@ -12,6 +12,7 @@ import ( "strings" ) +// XMLMarshalString 将给定的对象v序列化为XML字符串,并指定字符集charset func XMLMarshalString(v interface{}, charset string) (string, error) { marshal, err := XMLMarshal(v, charset) if err != nil { @@ -20,6 +21,7 @@ func XMLMarshalString(v interface{}, charset string) (string, error) { return string(marshal), nil } +// XMLMarshal 将给定的对象obj序列化为XML格式的字节数组,并指定字符集charset func XMLMarshal(obj interface{}, charset string) ([]byte, error) { marshal := &bytes.Buffer{} encoder := xml.NewEncoder(marshal) @@ -69,6 +71,7 @@ func XMLMarshal(obj interface{}, charset string) ([]byte, error) { return xmlBytes.Bytes(), nil } +// XMLUnmarshal 将XML格式的字节数组data反序列化为对象obj func XMLUnmarshal(data []byte, obj interface{}) error { decoder := xml.NewDecoder(bytes.NewReader(data)) decoder.CharsetReader = func(c string, input io.Reader) (io.Reader, error) {