commit dd6b5e904192ce74d863a51f9a5faa853b184e35 Author: 阿恒 <2390904403@qq.com> Date: Tue Sep 2 17:37:02 2025 +0800 第一版 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..58162b2 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +cmd/ +*.pem \ No newline at end of file diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..84f76a4 --- /dev/null +++ b/go.mod @@ -0,0 +1,7 @@ +module gitee.com/LifetimeNine/singularity-rpc-client + +go 1.24.3 + +require golang.org/x/net v0.43.0 + +require golang.org/x/text v0.28.0 // indirect diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..07413a4 --- /dev/null +++ b/go.sum @@ -0,0 +1,4 @@ +golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE= +golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg= +golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng= +golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU= diff --git a/pkg/rpc/device.go b/pkg/rpc/device.go new file mode 100644 index 0000000..0a06a38 --- /dev/null +++ b/pkg/rpc/device.go @@ -0,0 +1,147 @@ +package rpc + +import ( + "time" +) + +// 设备详情 +type DeviceDetail struct { + Type uint8 `json:"type"` + ConnectType uint8 `json:"connect_type"` + Secret string `json:"secret"` + PId uint32 `json:"p_id"` + DataId uint32 `json:"data_id"` +} + +var device *Device + +// 获取设备相关RPC类 +func GetDevice() *Device { + if device == nil { + device = &Device{} + } + return device +} + +// 设备相关RPC +type Device struct{} + +// 获取设备详情 +func (d *Device) GetDetail(deviceId uint32) (*Result[DeviceDetail], error) { + body := map[string]any{ + "device_id": deviceId, + "field_list": []string{"type", "secret", "connect_type", "p_id", "data_id"}, + } + result := &Result[DeviceDetail]{} + if err := GetRequest().Send("device.get_detail", body, result); err != nil { + return nil, err + } + return result, nil +} + +// 重置设备在线状态 +func (d *Device) ResetOnlineStatus() (*Result[any], error) { + result := &Result[any]{} + if err := GetRequest().Send("device.reset_online_status", map[string]any{}, result); err != nil { + return nil, err + } + return result, nil +} + +// 更新设备在线状态 +func (d *Device) UpdateOnlineStatus(deviceId uint32, online bool, updateTime time.Time) (*Result[any], error) { + body := map[string]any{ + "device_id": deviceId, + "online": online, + "time": updateTime.Format(time.DateTime), + } + result := &Result[any]{} + if err := GetRequest().Send("device.update_online_status", body, result); err != nil { + return nil, err + } + return result, nil +} + +// 更新从设备在线状态 +func (d *Device) UpdateSlaveOnlineStatus(deviceId uint32, online bool, updateTime time.Time, parentId *uint32) (*Result[any], error) { + body := map[string]any{ + "device_id": deviceId, + "online": online, + "time": updateTime.Format(time.DateTime), + } + if parentId != nil { + body["parent_id"] = *parentId + } + result := &Result[any]{} + if err := GetRequest().Send("device.update_salve_online_status", body, result); err != nil { + return nil, err + } + return result, nil +} + +// 更新设备属性上报时间 +func (d *Device) UpdateReportTime(deviceId uint32, reportTime *time.Time) (*Result[any], error) { + body := map[string]any{ + "device_id": deviceId, + } + if reportTime != nil { + body["time"] = reportTime.Format(time.DateTime) + } + result := &Result[any]{} + if err := GetRequest().Send("device.update_report_time", body, result); err != nil { + return nil, err + } + return result, nil +} + +// 设置设备属性 +func (d *Device) SetAttribute(deviceId uint32, attributeSet map[string]any) (*Result[any], error) { + body := map[string]any{ + "device_id": deviceId, + "attribute": attributeSet, + } + result := &Result[any]{} + if err := GetRequest().Send("device.set_attribute", body, result); err != nil { + return nil, err + } + return result, nil +} + +// 获取设备属性 +func (d *Device) GetAttribute(deviceId uint32) (*Result[map[string]any], error) { + body := map[string]any{ + "device_id": deviceId, + } + result := &Result[map[string]any]{} + if err := GetRequest().Send("device.get_attribute", body, result); err != nil { + return nil, err + } + return result, nil +} + +// 更新设备版本信息 +func (d *Device) UpdateVersion(deviceId uint32, versionNumber uint) (*Result[any], error) { + body := map[string]any{ + "device_id": deviceId, + "version_number": versionNumber, + } + result := &Result[any]{} + if err := GetRequest().Send("device.update_version", body, result); err != nil { + return nil, err + } + return result, nil +} + +// 保存升级结果 +func (d *Device) SaveUpgradeResult(upgradeTaskId uint32, status uint8, finishTime time.Time) (*Result[any], error) { + body := map[string]any{ + "upgrade_task_id": upgradeTaskId, + "status": status, + "finish_time": finishTime.Format(time.DateTime), + } + result := &Result[any]{} + if err := GetRequest().Send("device.save_upgrade_result", body, result); err != nil { + return nil, err + } + return result, nil +} diff --git a/pkg/rpc/request.go b/pkg/rpc/request.go new file mode 100644 index 0000000..845bf88 --- /dev/null +++ b/pkg/rpc/request.go @@ -0,0 +1,102 @@ +package rpc + +import ( + "bytes" + "crypto/md5" + "crypto/tls" + "crypto/x509" + "encoding/hex" + "encoding/json" + "fmt" + "io" + "net/http" + "os" + "time" + + "golang.org/x/net/http2" +) + +var defaultRequest Request + +// 请求类 +type Request struct { + host string + port uint16 + rootCertPath string + client *http.Client +} + +// 发送请求 +func (r *Request) Send(methodName string, body any, responseBody any) error { + if r.client == nil { + return fmt.Errorf("not initialized") + } + bodyByte, err := json.Marshal(body) + if err != nil { + return fmt.Errorf("request body json marshal fail: %v", err) + } + request, err := http.NewRequest("POST", fmt.Sprintf("https://%s:%d/%s", r.host, r.port, methodName), bytes.NewBuffer(bodyByte)) + if err != nil { + return err + } + signByte := md5.Sum([]byte(fmt.Sprintf("%s\nsingularity\n%s", methodName, bodyByte))) + request.Header.Set("Content-Type", "application/json") + request.Header.Set("Authorization", fmt.Sprintf("Digest %s", hex.EncodeToString(signByte[:]))) + + response, err := r.client.Do(request) + if err != nil { + return err + } + defer response.Body.Close() + + responseBodyByte, err := io.ReadAll(response.Body) + if err != nil { + return err + } + + if err := json.Unmarshal(responseBodyByte, responseBody); err != nil { + return fmt.Errorf("response body json unmarshal fail: %v", err) + } + + return nil +} + +// 初始化请求类 +func InitiateRequest(host string, port uint16, rootCertPath string) error { + rootCert, err := os.ReadFile(rootCertPath) + if err != nil { + return fmt.Errorf("cert file read fail") + } + defaultRequest.host = host + defaultRequest.port = port + defaultRequest.rootCertPath = rootCertPath + + certPool := x509.NewCertPool() + certPool.AppendCertsFromPEM(rootCert) + + transport := &http2.Transport{ + AllowHTTP: true, + TLSClientConfig: &tls.Config{ + RootCAs: certPool, + ServerName: host, + NextProtos: []string{"h2"}, + }, + StrictMaxConcurrentStreams: true, + IdleConnTimeout: time.Minute, + PingTimeout: 10 * time.Second, + } + defaultRequest.client = &http.Client{ + Transport: transport, + Timeout: 5 * time.Second, + } + return nil +} + +// 获取请求类 +func GetRequest() *Request { + return &defaultRequest +} + +func init() { + defaultRequest = Request{} +} diff --git a/pkg/rpc/type.go b/pkg/rpc/type.go new file mode 100644 index 0000000..abd3a30 --- /dev/null +++ b/pkg/rpc/type.go @@ -0,0 +1,43 @@ +package rpc + +// 结果Code +type ResultCode uint16 + +const ( + // 响应Code 正常 + ResultCodeSuccess ResultCode = 0 + // 响应Code 失败 + ResultCodeError ResultCode = 10000 + // 响应Code 参数异常 + ResultCodeParamError ResultCode = 10001 + // 响应Code 操作失败 + ResultCodeActionFail ResultCode = 10002 + // 响应Code 数据不存在 + ResultCodeDataNotExist ResultCode = 10003 + // 响应Code 签名错误 + ResultCodeSignError ResultCode = 10105 + + // 服务通讯失败 + ResultCodeServiceCommunicationFail ResultCode = 11000 + // 数据解析失败 + ResultCodeDataParseFail ResultCode = 11001 + // 操作不存在 + ResultCodeOperationNotExist ResultCode = 11002 + // 设备不存在 + ResultCodeDeviceNotExist ResultCode = 11003 + // 设备信息不在线 + ResultCodeDeviceOffline ResultCode = 11004 + // 设备响应超时 + ResultCodeDeviceResponseTimeout ResultCode = 11005 + // 设备属性不可操作 + ResultCodeDeviceAttributeCannotOperation ResultCode = 11006 + // 设备属性不存在 + ResultCodeDeviceAttributeNotExits ResultCode = 11007 +) + +// 响应结果 +type Result[D any] struct { + Code ResultCode `json:"code"` + Message string `json:"message"` + Data D `json:"data"` +}