From 4e36d3ea34715e286309e7b1d01d7cf34a02bb54 Mon Sep 17 00:00:00 2001 From: LLLLimbo <31607003+LLLLimbo@users.noreply.github.com> Date: Tue, 4 Jul 2023 15:43:40 +0800 Subject: [PATCH] support 09 & 0a --- Readme.md | 5 ++-- doc/messages.md | 9 +++++++ doc/restapi.md | 55 ++++++++++++++++++++++++++++++++++++++ handler.go | 14 ++++++++++ main.go | 4 +++ protocol.go | 71 +++++++++++++++++++++++++++++++++++++++++++++++-- routes.go | 28 ++++++++++++++++++- 7 files changed, 180 insertions(+), 6 deletions(-) diff --git a/Readme.md b/Readme.md index 4bf7ab6..042887e 100644 --- a/Readme.md +++ b/Readme.md @@ -108,8 +108,8 @@ This is the current list of supported messages, you can find more detailed infor | 0x04 | 心跳包应答 | 运营平台->充电桩 | :white_check_mark: | | 0x05 | 计费模型验证请求 | 充电桩->运营平台 | :white_check_mark: | | 0x06 | 计费模型验证请求应答 | 运营平台->充电桩 | :white_check_mark: | -| 0x09 | 充电桩计费模型请求 | 充电桩->运营平台 | | -| 0x0A | 计费模型请求应答 | 运营平台->充电桩 | | +| 0x09 | 充电桩计费模型请求 | 充电桩->运营平台 | :white_check_mark: | +| 0x0A | 计费模型请求应答 | 运营平台->充电桩 | :white_check_mark: | | 0x12 | 读取实时监测数据 | 运营平台->充电桩 | | | 0x13 | 离线监测数据 | 充电桩->运营平台 | :white_check_mark: | | 0x15 | 充电握手 | 充电桩->运营平台 | | @@ -154,4 +154,3 @@ This is the current list of supported messages, you can find more detailed infor | 0xA3 | 远程并充启机命令回复 | 运营平台->充电桩 | | | 0xA4 | 运营平台远程控制并充启机 | 充电桩->运营平台 | | - diff --git a/doc/messages.md b/doc/messages.md index 4d4498e..18ce1dd 100644 --- a/doc/messages.md +++ b/doc/messages.md @@ -41,6 +41,15 @@ +### Billing model request (09) + +| Field | Type | Description | +| ------ | ------ | ----------- | +| header | Header | | +| id | string | device id | + + + ### Offline data report (13) | Field | Type | Description | diff --git a/doc/restapi.md b/doc/restapi.md index ee61f77..c7dd748 100644 --- a/doc/restapi.md +++ b/doc/restapi.md @@ -84,6 +84,61 @@ Response body: +### Billing model response(0a) + +Path: `/proxy/0a` + +Request body: + +| Field | Type | Description | +| ---------------- | ------ | ------------------------------------------------------------ | +| header | Header | | +| id | string | device id | +| billingModelCode | string | code of billing model | +| sharpUnitPrice | int | sharp unit price (X100000) | +| sharpServiceFee | int | sharp service fee (X100000) | +| peakUnitPrice | int | peak unit price (X100000) | +| peakServiceFee | int | peak service fee (X100000) | +| flatUnitPrice | int | flat unit price (X100000) | +| flatServiceFee | int | flat service fee (X100000) | +| valleyUnitPrice | int | valley unit price (X100000) | +| valleyServiceFee | int | valley service fee (X100000) | +| accrualRatio | int | see [pdf](云快充平台协议V1.6.pdf) for more information | +| rateList | []int | list of rate numbers per time period 0-sharp price 1-peak price 2-flat price 3-valley price | + + + +Example request: + +```json +{ + "header":{ + "encrypted": false, + "seq": 73 + }, + "billingModelCode": "01", + "sharpUnitPrice": 100000, + "sharpServiceFee": 100000, + "peakUnitPrice": 100000, + "peakServiceFee": 100000, + "flatUnitPrice": 100000, + "flatServiceFee": 100000, + "valleyUnitPrice": 100000, + "valleyServiceFee": 100000, + "rateList": [0, 0, 3, 2, 3, 0, 0, 0, 3, 0, 0, 1, 0, 3, 1, 1, 0, 3, 2, 3, 1, 2, 3, 1, 3, 2, 1, 0, 0, 0, 1, 0, 1, 2, 2, 0, 2, 0, 0, 0, 0, 1, 3, 2, 2, 2, 2, 2] +} +``` + + + + + +Response body: + +| Field | Type | Description | +| ------- | ------ | ------------- | +| message | string | error message | + ### Remote bootstrap(34) diff --git a/handler.go b/handler.go index 24af60f..3ab3b07 100644 --- a/handler.go +++ b/handler.go @@ -116,3 +116,17 @@ func SendSetBillingModelRequestMessage(req *SetBillingModelRequestMessage) error }).Debug("[58] SetBillingModelRequest message sent") return nil } + +func SendBillingModelResponseMessage(req *BillingModelResponseMessage) error { + c, err := GetClient(req.Id) + if err != nil { + return err + } + resp := PackBillingModelResponseMessage(req) + _, _ = c.Write(resp) + log.WithFields(log.Fields{ + "id": req.Id, + "request": BytesToHex(resp), + }).Debug("[0a] BillingModelResponse message sent") + return nil +} diff --git a/main.go b/main.go index 5bf9b62..372d555 100644 --- a/main.go +++ b/main.go @@ -89,6 +89,7 @@ func enableHttpServer(opt *Options) { r := gin.Default() r.POST("/proxy/02", VerificationResponseRouter) r.POST("/proxy/06", BillingModelVerificationResponseRouter) + r.POST("/proxy/0a", BillingModelResponseMessageRouter) r.POST("/proxy/34", RemoteBootstrapRequestRouter) r.POST("/proxy/36", RemoteShutdownRequestRouter) r.POST("/proxy/40", TransactionRecordConfirmedRouter) @@ -165,6 +166,9 @@ func drain(opt *Options, conn net.Conn) error { case BillingModelVerification: BillingModelVerificationRouter(opt, hex, header, conn) break + case BillingModelRequest: + BillingModelRequestMessageRouter(opt, hex, header, conn) + break case OfflineDataReport: OfflineDataReportMessageRouter(opt, hex, header) break diff --git a/protocol.go b/protocol.go index 229c441..e5d79d5 100644 --- a/protocol.go +++ b/protocol.go @@ -15,7 +15,7 @@ const ( Verification = byte(0x01) Heartbeat = byte(0x03) BillingModelVerification = byte(0x05) - BillingModelRequest = byte(0x08) + BillingModelRequest = byte(0x09) OfflineDataReport = byte(0x13) ChargingHandshake = byte(0x15) Configuration = byte(0x17) @@ -180,6 +180,73 @@ func PackBillingModelVerificationMessage(hex []string, header *Header) *BillingM return msg } +type BillingModelRequestMessage struct { + Header *Header `json:"header"` + Id string `json:"Id"` +} + +func PackBillingModelRequestMessage(hex []string, header *Header) *BillingModelRequestMessage { + //Id + id := "" + for _, v := range hex[6:13] { + id += v + } + + msg := &BillingModelRequestMessage{ + Header: header, + Id: id, + } + return msg +} + +type BillingModelResponseMessage struct { + Header *Header `json:"header"` + Id string `json:"id"` + BillingModelCode string `json:"billingModelCode"` + SharpUnitPrice int `json:"sharpUnitPrice"` + SharpServiceFee int `json:"sharpServiceFee"` + PeakUnitPrice int `json:"peakUnitPrice"` + PeakServiceFee int `json:"peakServiceFee"` + FlatUnitPrice int `json:"flatUnitPrice"` + FlatServiceFee int `json:"flatServiceFee"` + ValleyUnitPrice int `json:"valleyUnitPrice"` + ValleyServiceFee int `json:"valleyServiceFee"` + AccrualRatio int `json:"accrualRatio"` + RateList []int `json:"rateList"` +} + +func PackBillingModelResponseMessage(msg *BillingModelResponseMessage) []byte { + var resp bytes.Buffer + resp.Write([]byte{0x68, 0x5e}) + seqStr := fmt.Sprintf("%x", GenerateSeq()) + seq := ConvertIntSeqToReversedHexArr(seqStr) + resp.Write(HexToBytes(MakeHexStringFromHexArray(seq))) + if msg.Header.Encrypted { + resp.WriteByte(0x01) + } else { + resp.WriteByte(0x00) + } + resp.Write([]byte{0x0a}) + resp.Write(HexToBytes(msg.Id)) + resp.Write(HexToBytes(msg.BillingModelCode)) + resp.Write(IntToBytes(msg.SharpUnitPrice)) + resp.Write(IntToBytes(msg.SharpServiceFee)) + resp.Write(IntToBytes(msg.PeakUnitPrice)) + resp.Write(IntToBytes(msg.PeakServiceFee)) + resp.Write(IntToBytes(msg.FlatUnitPrice)) + resp.Write(IntToBytes(msg.FlatServiceFee)) + resp.Write(IntToBytes(msg.ValleyUnitPrice)) + resp.Write(IntToBytes(msg.ValleyServiceFee)) + resp.Write([]byte(strconv.Itoa(msg.AccrualRatio))) + + for _, v := range msg.RateList { + resp.Write(IntToBytes(v)) + } + + resp.Write(ModbusCRC(resp.Bytes()[2:])) + return resp.Bytes() +} + type BillingModelVerificationResponseMessage struct { Header *Header `json:"header"` Id string `json:"id"` @@ -812,7 +879,7 @@ func PackSetBillingModelRequestMessage(msg *SetBillingModelRequestMessage) []byt resp.Write([]byte(strconv.Itoa(msg.AccrualRatio))) for _, v := range msg.RateList { - resp.Write([]byte(strconv.Itoa(v))) + resp.Write(IntToBytes(v)) } resp.Write(ModbusCRC(resp.Bytes()[2:])) diff --git a/routes.go b/routes.go index aaa1179..e4eb913 100644 --- a/routes.go +++ b/routes.go @@ -88,7 +88,7 @@ func BillingModelVerificationRouter(opt *Options, hex []string, header *Header, log.WithFields(log.Fields{ "id": msg.Id, "billing_model_code": msg.BillingModelCode, - }).Debug("[05] BillingModelRequest message") + }).Debug("[05] BillingModelVerification message") //auto response if opt.AutoBillingModelVerify { @@ -113,6 +113,32 @@ func BillingModelVerificationRouter(opt *Options, hex []string, header *Header, } } +func BillingModelRequestMessageRouter(opt *Options, hex []string, header *Header, conn net.Conn) { + msg := PackBillingModelRequestMessage(hex, header) + log.WithFields(log.Fields{ + "id": msg.Id, + }).Debug("[09] BillingModelRequest message") + + //forward + if opt.MessageForwarder != nil { + //convert msg to json string bytes + b, _ := json.Marshal(msg) + _ = opt.MessageForwarder.Publish("09", b) + } +} + +func BillingModelResponseMessageRouter(c *gin.Context) { + var req BillingModelResponseMessage + if c.ShouldBind(&req) == nil { + err := SendBillingModelResponseMessage(&req) + if err != nil { + c.JSON(500, gin.H{"message": err}) + return + } + } + c.JSON(200, gin.H{"message": "done"}) +} + func BillingModelVerificationResponseRouter(c *gin.Context) { var req BillingModelVerificationResponseMessage if c.ShouldBind(&req) == nil {