protocol.go 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225
  1. package reporter
  2. import (
  3. "crypto/md5"
  4. "encoding/hex"
  5. "encoding/json"
  6. "fmt"
  7. "io"
  8. "net/url"
  9. "os"
  10. "hnyfkj.com.cn/rtu/bxs-sy/baseapp"
  11. )
  12. type rpcRequest struct {
  13. JSONRPC string `json:"jsonrpc"` // 版本号, 固定: "2.0"
  14. Method string `json:"method"` // 调用方法, 执行函数名
  15. Params json.RawMessage `json:"params,omitempty"` // 请求参数, 可选可为空
  16. ID int `json:"id"` // 请求ID, 用于标识请求
  17. }
  18. type rpcError struct {
  19. Code int `json:"code"` // 错误码
  20. Message string `json:"message"` // 错误信息
  21. }
  22. type rpcResponse struct {
  23. JSONRPC string `json:"jsonrpc"` // 版本号, 固定: "2.0"
  24. Result json.RawMessage `json:"result,omitempty"` // 响应结果, 可选可为空
  25. Error *rpcError `json:"error,omitempty"` // 错误信息, 可选可为空
  26. ID int `json:"id"` // 应答ID, 响应匹配请求
  27. }
  28. func makeLoginReq(imei, iccid, rssi, appVersion, inheritDUI string) ([]byte, error) {
  29. type params struct {
  30. DeviceType string `json:"deviceType"`
  31. IMEI string `json:"imei"`
  32. ICCID string `json:"iccid"`
  33. RSSI string `json:"rssi"`
  34. AppVersion string `json:"appVersion"`
  35. InheritID string `json:"inheritDUI,omitempty"` // 继承的DUI, 可选可为空
  36. }
  37. paramsVar := params{
  38. DeviceType: "33", // 便携式色诱产品类型为: 33
  39. IMEI: imei,
  40. ICCID: iccid,
  41. RSSI: rssi,
  42. AppVersion: appVersion,
  43. InheritID: inheritDUI,
  44. }
  45. paramsBytes, err := json.Marshal(paramsVar)
  46. if err != nil {
  47. return nil, err
  48. }
  49. req := rpcRequest{
  50. JSONRPC: "2.0",
  51. Method: "login",
  52. Params: paramsBytes,
  53. ID: 1,
  54. }
  55. return json.Marshal(req)
  56. }
  57. func parseLoginResp(data []byte) (string, string, error) {
  58. var resp rpcResponse
  59. if err := json.Unmarshal(data, &resp); err != nil {
  60. return "", "", err
  61. }
  62. if resp.Error != nil {
  63. return "", "", fmt.Errorf("服务器返回错误: %d, %s", resp.Error.Code, resp.Error.Message)
  64. }
  65. var result struct {
  66. IMEI string `json:"imei"`
  67. DUI string `json:"DUI"`
  68. }
  69. if err := json.Unmarshal(resp.Result, &result); err != nil {
  70. return "", "", err
  71. }
  72. if len(result.DUI) != 14 || !isDecimal(result.DUI) {
  73. return "", "", fmt.Errorf("无效DUI: %q", result.DUI)
  74. }
  75. return result.IMEI, result.DUI, nil
  76. }
  77. func makeLogoutMsg(imei string) ([]byte, error) {
  78. type params struct {
  79. IMEI string `json:"imei"`
  80. }
  81. paramsVar := params{
  82. IMEI: imei,
  83. }
  84. paramsBytes, err := json.Marshal(paramsVar)
  85. if err != nil {
  86. return nil, err
  87. }
  88. req := rpcRequest{
  89. JSONRPC: "2.0",
  90. Method: "logout",
  91. Params: paramsBytes,
  92. ID: 1,
  93. }
  94. return json.Marshal(req)
  95. }
  96. func makeCustomError(err error) *rpcError {
  97. if err == nil {
  98. return nil
  99. }
  100. return &rpcError{-32000, fmt.Sprintf("Custom error: %v", err)}
  101. }
  102. type mcuParams struct {
  103. Version string `json:"version"` // 版本号 x.x.x.x, 定义: [major(8bit)].[minor(8bit)].[patch(8bit)].[build(8bit)]
  104. CtrlMode string `json:"ctrlMode"` // 定时控制模式(0=光控, 1=时控)
  105. LightDuration string `json:"lightDuration"` // 光控定时时长(小时, 1-10, 仅光控模式有效)
  106. StartHour string `json:"startHour"` // 时控开始时间(小时, 0-23, 仅时控模式有效)
  107. EndHour string `json:"endHour"` // 时控结束时间(小时, 0-23, 仅时控模式有效)
  108. PhotoIntervalMinutes string `json:"takePhotoIntervalMinutes"` // 拍照间隔(分钟)
  109. }
  110. func isDecimal(s string) bool {
  111. for _, ch := range s {
  112. if ch < '0' || ch > '9' {
  113. return false
  114. }
  115. }
  116. return s != "" // 不允许空字符串
  117. }
  118. func parseMCUParams(data []byte) (mcuParams, error) {
  119. var req rpcRequest
  120. if err := json.Unmarshal(data, &req); err != nil {
  121. return mcuParams{}, err
  122. }
  123. if len(req.Params) == 0 {
  124. return mcuParams{}, fmt.Errorf("missing params")
  125. }
  126. var params mcuParams
  127. if err := json.Unmarshal(req.Params, &params); err != nil {
  128. return mcuParams{}, err
  129. }
  130. var verCode baseapp.VersionCode
  131. err := verCode.FromString(params.Version)
  132. if err != nil || !isDecimal(params.CtrlMode) || !isDecimal(params.LightDuration) ||
  133. !isDecimal(params.StartHour) || !isDecimal(params.EndHour) || !isDecimal(params.PhotoIntervalMinutes) {
  134. return mcuParams{}, fmt.Errorf("invalid params")
  135. }
  136. return params, nil
  137. }
  138. func parseUpgradeAppResp(data []byte) (serverAddr, loginUser, loginPass, remoteFile, md5value string, err error) {
  139. var req rpcRequest
  140. if err := json.Unmarshal(data, &req); err != nil {
  141. return "", "", "", "", "", err
  142. }
  143. if len(req.Params) == 0 {
  144. return "", "", "", "", "", fmt.Errorf("missing params")
  145. }
  146. var params struct {
  147. URL string `json:"url"` // 升级包的URL地址
  148. MD5 string `json:"md5"` // 升级包的MD5校验值, 32个十六进制数字组成, 16字节
  149. }
  150. if err := json.Unmarshal(req.Params, &params); err != nil {
  151. return "", "", "", "", "", err
  152. }
  153. u, err := url.Parse(params.URL)
  154. if err != nil {
  155. return "", "", "", "", "", fmt.Errorf("invalid URL: %s", params.URL)
  156. }
  157. if u.Scheme != "ftp" {
  158. return "", "", "", "", "", fmt.Errorf("unsupported scheme: %s", u.Scheme)
  159. }
  160. host := u.Hostname()
  161. port := u.Port()
  162. if port == "" {
  163. port = "21"
  164. }
  165. serverAddr = fmt.Sprintf("%s:%s", host, port)
  166. loginUser = u.User.Username()
  167. loginPass, _ = u.User.Password()
  168. remoteFile = u.Path
  169. if remoteFile == "" {
  170. return "", "", "", "", "", fmt.Errorf("missing remote file path")
  171. }
  172. md5value = params.MD5
  173. return serverAddr, loginUser, loginPass, remoteFile, md5value, nil
  174. }
  175. func fileMD5(path string) (string, error) {
  176. f, err := os.Open(path)
  177. if err != nil {
  178. return "", err
  179. }
  180. defer f.Close()
  181. h := md5.New()
  182. if _, err := io.Copy(h, f); err != nil {
  183. return "", err
  184. }
  185. return hex.EncodeToString(h.Sum(nil)), nil
  186. }