├── .github
└── workflows
│ └── go.yml
├── .gitignore
├── 645.go
├── LICENSE
├── Makefile
├── api.go
├── client.go
├── client_option.go
├── conf
└── config.go
├── control.go
├── decode.go
├── docs
├── .nojekyll
├── README.md
├── _coverpage.md
└── index.html
├── example
├── bf
│ └── main.go
└── gaea
│ └── main.go
├── exception.go
├── go.mod
├── go.sum
├── log.go
├── prefix.go
├── protocol.go
├── protocol_test.go
├── read.go
├── readme.md
├── rtuClient.go
├── serial.go
└── value.go
/.github/workflows/go.yml:
--------------------------------------------------------------------------------
1 | name: Go
2 |
3 | on:
4 | push:
5 | branches: [ main ]
6 | pull_request:
7 | branches: [ main ]
8 |
9 | jobs:
10 |
11 | build:
12 | runs-on: ubuntu-latest
13 | steps:
14 | - uses: actions/checkout@v2
15 |
16 | - name: Set up Go
17 | uses: actions/setup-go@v2
18 | with:
19 | go-version: 1.16
20 |
21 | - name: Test
22 | run: go test -v
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ### JetBrains template
2 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
3 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
4 |
5 | # User-specific stuff
6 | .idea/**/workspace.xml
7 | .idea/**/tasks.xml
8 | .idea/**/usage.statistics.xml
9 | .idea/**/dictionaries
10 | .idea/**/shelf
11 |
12 | # Generated files
13 | .idea/**/contentModel.xml
14 |
15 | # Sensitive or high-churn files
16 | .idea/**/dataSources/
17 | .idea/**/dataSources.ids
18 | .idea/**/dataSources.local.xml
19 | .idea/**/sqlDataSources.xml
20 | .idea/**/dynamic.xml
21 | .idea/**/uiDesigner.xml
22 | .idea/**/dbnavigator.xml
23 |
24 | # Gradle
25 | .idea/**/gradle.xml
26 | .idea/**/libraries
27 |
28 | .idea
29 |
30 | # Gradle and Maven with auto-import
31 | # When using Gradle or Maven with auto-import, you should exclude module files,
32 | # since they will be recreated, and may cause churn. Uncomment if using
33 | # auto-import.
34 | # .idea/artifacts
35 | # .idea/compiler.xml
36 | # .idea/jarRepositories.xml
37 | # .idea/modules.xml
38 | # .idea/*.iml
39 | # .idea/modules
40 | # *.iml
41 | # *.ipr
42 |
43 | # CMake
44 | cmake-build-*/
45 |
46 | # Mongo Explorer plugin
47 | .idea/**/mongoSettings.xml
48 |
49 | # File-based project format
50 | *.iws
51 |
52 | # IntelliJ
53 | out/
54 |
55 | # mpeltonen/sbt-idea plugin
56 | .idea_modules/
57 |
58 | # JIRA plugin
59 | atlassian-ide-plugin.xml
60 |
61 | # Cursive Clojure plugin
62 | .idea/replstate.xml
63 |
64 | # Crashlytics plugin (for Android Studio and IntelliJ)
65 | com_crashlytics_export_strings.xml
66 | crashlytics.properties
67 | crashlytics-build.properties
68 | fabric.properties
69 |
70 | # Editor-based Rest Client
71 | .idea/httpRequests
72 |
73 | # Android studio 3.1+ serialized cache file
74 | .idea/caches/build_file_checksums.ser
75 |
76 | ### Go template
77 | # Binaries for programs and plugins
78 | *.exe
79 | *.exe~
80 | *.dll
81 | *.so
82 | *.dylib
83 |
84 | # Test binary, built with `go test -c`
85 | *.test
86 |
87 | # Output of the go coverage tool, specifically when used with LiteIDE
88 | *.out
89 |
90 | # Dependency directories (remove the comment below to include it)
91 | # vendor/
92 |
93 |
--------------------------------------------------------------------------------
/645.go:
--------------------------------------------------------------------------------
1 | package go645
2 |
3 | import (
4 | "time"
5 |
6 | "github.com/tarm/serial"
7 | )
8 |
9 | type ClientProvider interface {
10 | // Connect try to connect the remote server
11 | Connect() error
12 | // IsConnected returns a bool signifying whether
13 | // the client is connected or not.
14 | IsConnected() bool
15 | LogMode(enable bool)
16 | // Close disconnect the remote server
17 | Close() error
18 | setSerialConfig(config serial.Config)
19 | setPrefixHandler(handler PrefixHandler)
20 | // setTCPTimeout set tcp connect & read timeout
21 | setTCPTimeout(t time.Duration)
22 | setLogProvider(p LogProvider)
23 | SendAndRead(*Protocol) (aduResponse []byte, err error)
24 | SendRawFrameAndRead(aduRequest []byte) (aduResponse []byte, err error)
25 | SendRawFrame(aduRequest []byte) (err error)
26 | ReadRawFrame() (aduResponse []byte, err error)
27 | Send(*Protocol) (err error)
28 | }
29 |
30 | // LogProvider log message levels only Debug and Error
31 | type LogProvider interface {
32 | Errorf(format string, v ...interface{})
33 | Debugf(format string, v ...interface{})
34 | }
35 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 短狐
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | BUILD_TIME=`date +%FT%T%z`
2 | GITTAG=1.0
3 | OUTPUT=./out/zoom
4 |
5 | ARM=arm
6 | MAC= mac
7 | WIN =win
8 | OUTPUT=./g645
9 | all: ${ARM} ${MAC} ${WIN}
10 |
11 | ${ARM}:
12 | @echo "============ [${ARM}] 开始编译 =========="
13 | GOOS=linux GOARCH=arm go build -o ${OUTPUT}_${ARM} ./example/gaea/main.go
14 | @echo "============[${ARM}] 编译结束============"
15 | ${MAC}:
16 | @echo "============ [${MAC}] 开始编译 ============"
17 | GOOS=darwin GOARCH=amd64 go build -o ${OUTPUT}_${MAC}_amd64 ./main.go
18 | @echo "============ [${MAC}] 编译结束============"
19 | ${WIN}:
20 | @echo "============ [${WIN}] 开始编译 ============"
21 | GOOS=windows GOARCH=amd64 go build -o ${OUTPUT}_${WIN}_amd64 ./main.go
22 | @echo "============ [${WIN}] 编译结束 ============"
--------------------------------------------------------------------------------
/api.go:
--------------------------------------------------------------------------------
1 | package go645
2 |
3 | type Client interface {
4 | ClientProvider
5 | //Read 发送读请求
6 | Read(address Address, itemCode int32) (*ReadData, bool, error)
7 | //ReadWithBlock 读请求使能块
8 | ReadWithBlock(address Address, data ReadRequestData) (*Protocol, error)
9 | //Broadcast 开始广播
10 | Broadcast(p InformationElement, control Control) error
11 | }
12 |
--------------------------------------------------------------------------------
/client.go:
--------------------------------------------------------------------------------
1 | package go645
2 |
3 | import (
4 | "bytes"
5 | "sync"
6 | )
7 |
8 | var (
9 | // check implements Client interface.
10 | _ Client = (*client)(nil)
11 | )
12 |
13 | type client struct {
14 | ClientProvider
15 | mu sync.Mutex
16 | }
17 |
18 | func (c *client) Read(address Address, itemCode int32) (*ReadData, bool, error) {
19 | resp, err := c.ClientProvider.SendAndRead(ReadRequest(address, itemCode))
20 | if err != nil {
21 | return nil, false, err
22 | }
23 | decode, err := Decode(bytes.NewBuffer(resp))
24 | if err != nil {
25 | return nil, false, err
26 | }
27 | return decode.Data.(*ReadData), decode.Control.IsState(HasNext), err
28 | }
29 |
30 | //Broadcast 设备广播
31 | func (c *client) Broadcast(p InformationElement, control Control) error {
32 | var err error
33 | bf := bytes.NewBuffer(make([]byte, 0))
34 | err = p.Encode(bf)
35 | if err != nil {
36 | return err
37 | }
38 | return c.Send(NewProtocol(NewAddress(BroadcastAddress, LittleEndian), p, &control))
39 | }
40 |
41 | func (c *client) ReadWithBlock(address Address, data ReadRequestData) (*Protocol, error) {
42 | resp, err := c.ClientProvider.SendAndRead(ReadRequestWithBlock(address, data))
43 | if err != nil {
44 | return nil, err
45 | }
46 | return Decode(bytes.NewBuffer(resp))
47 | }
48 |
49 | // Option custom option
50 | type Option func(c *client)
51 |
52 | func NewClient(p ClientProvider, opts ...Option) Client {
53 | c := &client{ClientProvider: p}
54 | for _, opt := range opts {
55 | opt(c)
56 | }
57 | return c
58 | }
59 |
--------------------------------------------------------------------------------
/client_option.go:
--------------------------------------------------------------------------------
1 | package go645
2 |
3 | import (
4 | "time"
5 |
6 | "github.com/tarm/serial"
7 | )
8 |
9 | // ClientProviderOption client provider option for user.
10 | type ClientProviderOption func(ClientProvider)
11 |
12 | // WithLogProvider set logger provider.
13 | func WithLogProvider(provider LogProvider) ClientProviderOption {
14 | return func(p ClientProvider) {
15 | p.setLogProvider(provider)
16 | }
17 | }
18 |
19 | // WithEnableLogger enable log output when you has set logger.
20 | func WithEnableLogger() ClientProviderOption {
21 | return func(p ClientProvider) {
22 | p.LogMode(true)
23 | }
24 | }
25 |
26 | // WithSerialConfig set serial config, only valid on serial.
27 | func WithSerialConfig(config serial.Config) ClientProviderOption {
28 | return func(p ClientProvider) {
29 | p.setSerialConfig(config)
30 | }
31 | }
32 |
33 | func WithPrefixHandler(prefixHandler PrefixHandler) ClientProviderOption {
34 | return func(p ClientProvider) {
35 | p.setPrefixHandler(prefixHandler)
36 | }
37 | }
38 |
39 | // WithTCPTimeout set tcp Connect & Read timeout, only valid on TCP.
40 | func WithTCPTimeout(t time.Duration) ClientProviderOption {
41 | return func(p ClientProvider) {
42 | p.setTCPTimeout(t)
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/conf/config.go:
--------------------------------------------------------------------------------
1 | package conf
2 |
3 | import (
4 | "time"
5 |
6 | "github.com/tarm/serial"
7 | )
8 |
9 | type Config struct {
10 | Retry int
11 | Com []Com
12 | Mqtt Mqtt
13 | }
14 | type Com struct {
15 | Config SerialConfig
16 | Size int
17 | Type uint8
18 | Start int
19 | }
20 |
21 | type SerialConfig struct {
22 | Address string
23 | BaudRate int
24 | Timeout time.Duration
25 | Size byte
26 | Parity string
27 | StopBits byte
28 | }
29 | type Mqtt struct {
30 | Clinet string
31 | Broker string
32 | Username string
33 | Password string
34 | InfoTopic string
35 | AvgTopic string
36 |
37 | ReceiveTopic string
38 | TimeOut uint8
39 | SendTime uint8
40 | ControlTopic string
41 | ControlCallBackTopic string
42 | }
43 |
44 | func (s SerialConfig) NewSerialConfig() serial.Config {
45 | var p serial.Parity
46 | switch s.Parity {
47 | case "N":
48 | p = serial.ParityNone
49 | case "E":
50 | p = serial.ParityEven
51 | case "O":
52 | p = serial.ParityOdd
53 | }
54 |
55 | return serial.Config{
56 | Parity: p,
57 | Name: s.Address,
58 | Baud: s.BaudRate,
59 | ReadTimeout: s.Timeout,
60 | StopBits: serial.StopBits(s.StopBits),
61 | }
62 |
63 | }
64 |
--------------------------------------------------------------------------------
/control.go:
--------------------------------------------------------------------------------
1 | package go645
2 |
3 | import (
4 | "bytes"
5 | "encoding/binary"
6 | "errors"
7 | "fmt"
8 | )
9 |
10 | type ControlType byte
11 |
12 | //01010
13 | const (
14 | IsSlave ControlType = 1 << 7
15 | SlaveErr ControlType = 1 << 6
16 | HasNext ControlType = 1 << 5
17 | //Retain 保留
18 | Retain ControlType = 0b00000
19 | //Broadcast 广播校时
20 | Broadcast ControlType = 0b01000
21 | // ReadNext 读后续10001
22 | ReadNext ControlType = 0b10010
23 | //ReadAddress 读通讯地址
24 | ReadAddress ControlType = 0b10011
25 | //Write 写数据
26 | Write ControlType = 0b10100
27 | //WriteAddress 读通讯地址
28 | WriteAddress ControlType = 0b10101
29 | //ToChangeCommunicationRate 更改通讯速率
30 | ToChangeCommunicationRate ControlType = 0b10111
31 | Freeze ControlType = 0b10110
32 | //PassWord 修改密码
33 | PassWord ControlType = 0b11000
34 | ResetMaxDemand ControlType = 0b11001
35 | //ResetEM 电表清零
36 | ResetEM ControlType = 0b11010
37 | ResetEvent ControlType = 0b11011
38 | //Read 读
39 | Read ControlType = 0b10001
40 | )
41 |
42 | type Control struct {
43 | Data ControlType
44 | }
45 |
46 | func DecodeControl(buffer *bytes.Buffer) (*Control, error) {
47 | c := new(Control)
48 | if err := binary.Read(buffer, binary.LittleEndian, &c.Data); err != nil {
49 | return nil, err
50 | }
51 | return c, nil
52 | }
53 | func NewControl() *Control {
54 | return &Control{Data: 0}
55 | }
56 | func NewControlValue(data ControlType) *Control {
57 | return &Control{Data: data}
58 | }
59 |
60 | func (c *Control) SetState(state ControlType) {
61 | c.Data = c.Data | state
62 | }
63 |
64 | //SetStates 批量设置状态
65 | func (c *Control) SetStates(state ...ControlType) {
66 | for _, s := range state {
67 | c.Data = c.Data | s
68 | }
69 | }
70 | func (c *Control) IsState(state ControlType) bool {
71 | return (c.Data & state) == state
72 | }
73 |
74 | //IsStates 判断控制域
75 | func (c *Control) IsStates(state ...ControlType) bool {
76 | for _, s := range state {
77 | if !c.IsState(s) {
78 | return false
79 | }
80 | }
81 | return true
82 | }
83 |
84 | func (c *Control) Reset() {
85 | c.Data = 0
86 | }
87 | func (c *Control) getLen() uint16 {
88 | return 1
89 | }
90 |
91 | func (c *Control) Encode(buffer *bytes.Buffer) error {
92 | if err := binary.Write(buffer, binary.BigEndian, c.Data); err != nil {
93 | s := fmt.Sprintf("Control , %v", err)
94 | return errors.New(s)
95 | }
96 | return nil
97 | }
98 |
--------------------------------------------------------------------------------
/decode.go:
--------------------------------------------------------------------------------
1 | package go645
2 |
3 | import (
4 | "bytes"
5 | "encoding/binary"
6 | "errors"
7 | "log"
8 | )
9 |
10 | type Decoder func(buffer *bytes.Buffer) (*InformationElement, error)
11 |
12 | func Handler(control *Control, buffer *bytes.Buffer, size byte) (InformationElement, error) {
13 | //从站响应异常响应
14 | if control == nil {
15 | return nil, errors.New("未知错误")
16 | }
17 | if control.IsState(SlaveErr) {
18 | return nil, DecodeException(buffer)
19 | }
20 | //从站读正确响应
21 | if control.IsState(Read) {
22 | return DecodeRead(buffer, int(size)), nil
23 | }
24 | //佳和强制联机
25 | if control.Data == 0x8a {
26 | return DecodeNullData(buffer), nil
27 | }
28 | return nil, errors.New("未定义的数据类型")
29 | }
30 | func Decode(buffer *bytes.Buffer) (*Protocol, error) {
31 | var err error
32 | read := func(data interface{}) {
33 | if err != nil {
34 | return
35 | }
36 | err = binary.Read(buffer, binary.LittleEndian, data)
37 | }
38 | p := new(Protocol)
39 | read(&p.Start)
40 | p.Address, err = DecodeAddress(buffer, 6)
41 | read(&p.Start2)
42 | p.Control, err = DecodeControl(buffer)
43 | read(&p.DataLength)
44 | p.Data, err = Handler(p.Control, buffer, p.DataLength)
45 | read(&p.CS)
46 | read(&p.End)
47 | if err != nil {
48 | log.Print(err.Error())
49 | }
50 | return p, err
51 | }
52 | func DecodeAddress(buffer *bytes.Buffer, size int) (Address, error) {
53 | var a Address
54 |
55 | value := make([]byte, size)
56 | if err := binary.Read(buffer, binary.LittleEndian, &value); err != nil {
57 | return a, err
58 | }
59 | {
60 | a.value = value
61 | a.strValue = Bcd2Number(a.value)
62 | }
63 | return a, nil
64 | }
65 | func DecodeData(buffer *bytes.Buffer, size byte) (*ReadData, error) {
66 | var err error
67 | read := func(data interface{}) {
68 | if err != nil {
69 | return
70 | }
71 | err = binary.Read(buffer, binary.LittleEndian, data)
72 | }
73 | data := new(ReadData)
74 | var dataType []byte
75 | dataValue := make([]byte, size-4)
76 | read(&dataType)
77 | //for i, j := 0, len(dataType)-1; i < j; i, j = i+1, j-1 {
78 | // dataType[j], dataType[i] = dataType[i], dataType[j]
79 | //}
80 | for index, item := range dataType {
81 | dataType[index] = item - 0x33
82 | }
83 | read(&dataValue)
84 | for index, item := range dataValue {
85 | dataValue[index] = item - 0x33
86 | }
87 | for i, j := 0, len(dataValue)-1; i < j; i, j = i+1, j-1 {
88 | dataValue[i], dataValue[j] = dataValue[j], dataValue[i]
89 | }
90 |
91 | //瞬时功率及当前需量最高位表示方向,0正,1负,三相三线B相为0。取值范围:0.0000~79.9999。
92 | //表内温度 最高位0表示零上,1表示零下。取值范围:0.0~799.9
93 | //电流最高位表示方向,0正,1负,取值范围:0.000~799.999。功率因数最高位表示方向,0正,1负,取值范围
94 | data.bcdValue = Bcd2Number(dataValue)
95 | data.rawValue = dataValue
96 | data.dataType = dataType
97 | return data, nil
98 | }
99 | func DecoderData(buffer *bytes.Buffer, size int) (*bytes.Buffer, error) {
100 | var err error
101 | read := func(data interface{}) {
102 | if err != nil {
103 | return
104 | }
105 | err = binary.Read(buffer, binary.LittleEndian, data)
106 | }
107 | var value = make([]byte, size)
108 | read(value)
109 | for i, j := 0, len(value)-1; i <= j; i, j = i+1, j-1 {
110 | value[i], value[j] = value[j]-0x33, value[i]-0x33
111 | }
112 |
113 | return bytes.NewBuffer(value), nil
114 | }
115 | func DecodeRead(buffer *bytes.Buffer, size int) InformationElement {
116 | df, _ := DecoderData(buffer, size)
117 | var err error
118 | read := func(data interface{}) {
119 | if err != nil {
120 | return
121 | }
122 | err = binary.Read(df, binary.LittleEndian, data)
123 | }
124 | data := new(ReadData)
125 | var dataType = make([]byte, 4)
126 | dataValue := make([]byte, size-4)
127 | read(&dataValue)
128 | read(&dataType)
129 | for i, j := 0, len(dataType)-1; i < j; i, j = i+1, j-1 {
130 | dataType[j], dataType[i] = dataType[i], dataType[j]
131 | }
132 | //瞬时功率及当前需量最高位表示方向,0正,1负,三相三线B相为0。取值范围:0.0000~79.9999。
133 | //表内温度 最高位0表示零上,1表示零下。取值范围:0.0~799.9
134 | //电流最高位表示方向,0正,1负,取值范围:0.000~799.999。功率因数最高位表示方向,0正,1负,取值范围
135 | //判断最高位是否为0
136 | if dataType[3] == 0x02 && dataType[2] >= 0x3 && dataType[2] <= 0x6 {
137 | if IsStateUin8(dataValue[0], 7) {
138 | data.Negative = true
139 | dataValue[0] = dataValue[0] << 1 >> 1
140 | }
141 | }
142 | if dataType[3] == 02 && dataType[2] == 0x80 && dataType[0] >= 0x4 && dataType[0] <= 0x7 {
143 | if IsStateUin8(dataValue[0], 7) {
144 | data.Negative = true
145 | dataValue[0] = dataValue[0] << 1 >> 1
146 | }
147 | }
148 |
149 | data.bcdValue = Bcd2Number(dataValue)
150 | data.dataType = dataType
151 | return data
152 | }
153 | func DecodeException(buffer *bytes.Buffer) error {
154 | var data uint16
155 | err := binary.Read(buffer, binary.LittleEndian, &data)
156 | if err != nil {
157 | log.Print(err)
158 | return nil
159 | }
160 | return &Exception{data}
161 | }
162 | func DecodeNullData(*bytes.Buffer) InformationElement {
163 | return NullData{}
164 | }
165 | func IsStateUin8(des uint8, i int) bool {
166 | return (des & (0x01 << i)) != 0
167 | }
168 |
--------------------------------------------------------------------------------
/docs/.nojekyll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zcx1218029121/go645/af08785ca9dceaeb8d6d9b229c2295f49bc97aa3/docs/.nojekyll
--------------------------------------------------------------------------------
/docs/README.md:
--------------------------------------------------------------------------------
1 | # 快速开始
2 |
3 | ### 安装
4 |
5 | ```shell
6 | go get github.com/zcx1218029121/go645
7 | ```
8 |
9 | ### 创建串口客户端
10 |
11 | ```go
12 | p := go645.NewRTUClientProvider(
13 | go645.WithSerialConfig(serial.Config{
14 | Address: "COM2",
15 | BaudRate: b,
16 | DataBits: 8,
17 | StopBits: 1,
18 | Parity: "E",
19 | Timeout: time.Second * 30}))
20 | ```
21 |
22 | ### 创建串口客户端并打印日志
23 |
24 | ```go
25 | p := go645.NewRTUClientProvider(
26 | go645.WithSerialConfig(serial.Config{
27 | Address: "COM2",
28 | BaudRate: b,
29 | DataBits: 8,
30 | StopBits: 1,
31 | Parity: "E",
32 | Timeout: time.Second * 30}),
33 | go645.WithEnableLogger())
34 | ```
35 |
36 | ### 连接串口
37 |
38 | ```go
39 | err := c.Connect()
40 | if err != nil {
41 | panic(err)
42 | }
43 | ```
44 |
45 | ### 自定义引导词
46 |
47 | ```go
48 | var _ go645.PrefixHandler = (*Handler)(nil)
49 |
50 | type Handler struct {
51 | }
52 |
53 | func (h Handler) EncodePrefix(buffer *bytes.Buffer) error {
54 | // 写入引导词
55 | buffer.Write([]byte{0xfe, 0xfe, 0xfe, 0xfe})
56 | return nil
57 | }
58 | // 写入引导解码 一般来说 在这里的引导词都会被丢弃
59 | func (h Handler) DecodePrefix(reader io.Reader) ([]byte, error) {
60 | fe := make([]byte, 4)
61 | _, err := io.ReadAtLeast(reader, fe, 4)
62 | if err != nil {
63 | return nil, err
64 | }
65 | return fe, err
66 | }
67 | p := go645.NewRTUClientProvider(
68 | go645.WithSerialConfig(serial.Config{
69 | Address: "COM2",
70 | BaudRate: b,
71 | DataBits: 8,
72 | StopBits: 1,
73 | Parity: "E",
74 | Timeout: time.Second * 30}),
75 | go645.WithEnableLogger(),go645.WithPrefixHandler(&Handler{}))
76 | ```
77 | ### 发送读请求
78 | 1. hasNext 为是否有后续的Frame 标识
79 | 2. read 为读请求响应
80 | ```go
81 | read, hasNext, err := c.Read(go645.NewAddress("000703200136", go645.LittleEndian), 0x00_01_00_00)
82 | if err != nil{
83 | log.Printf("rec %s \n", read.GetValue())
84 | if hasNext{
85 | log.Printf("有后续Frame \n", read.GetValue())
86 | }
87 |
88 | }
89 |
90 | ```
91 |
92 | ### 发送广播请求
93 | > 标准的广播请求不会有后续响应如果为特殊响应请手动调用 frame, err := c.ReadRawFrame()
94 | #### 正常广播(无需返回值)
95 | ```go
96 | err:=c.Broadcast(go645.NullData{}, *go645.NewControlValue(0x0a))
97 | if err != nil{
98 | log.Printf("广播发送失败")
99 | }
100 | ```
101 | #### 特殊广播(需返回值)
102 | ```go
103 | err:=c.Broadcast(go645.NullData{}, *go645.NewControlValue(0x0a))
104 | if err != nil{
105 | log.Printf("广播发送失败")
106 | }
107 | frame, err := c.ReadRawFrame()
108 | if err != nil {
109 | log.Printf(err.Error())
110 | return
111 | }
112 | ```
113 |
--------------------------------------------------------------------------------
/docs/_coverpage.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 | # G645
4 |
5 | > 一个纯Go实现的645-2007 协议解析
6 |
7 | [GitHub](https://github.com/zcx1218029121/go645)
8 | [Get Started](#quick-start)
9 |
--------------------------------------------------------------------------------
/docs/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Document
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
19 |
20 |
21 |
28 |
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/example/bf/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "bytes"
5 | "flag"
6 | "github.com/goburrow/serial"
7 | "github.com/zcx1218029121/go645"
8 | "io"
9 | "log"
10 | "time"
11 | )
12 |
13 | var _ go645.PrefixHandler = (*Handler)(nil)
14 |
15 | type Handler struct {
16 | }
17 |
18 | func (h Handler) EncodePrefix(buffer *bytes.Buffer) error {
19 | buffer.Write([]byte{0xfe, 0xfe, 0xfe, 0xfe})
20 | return nil
21 | }
22 |
23 | func (h Handler) DecodePrefix(reader io.Reader) ([]byte, error) {
24 | fe := make([]byte, 4)
25 | _, err := io.ReadAtLeast(reader, fe, 4)
26 | if err != nil {
27 | return nil, err
28 | }
29 | return fe, err
30 | }
31 |
32 | //百富电表
33 | func main() {
34 | var b int
35 | var code int
36 | flag.IntVar(&b, "b", 2400, "波特率")
37 | flag.IntVar(&code, "c", 0x00_03_00_00, "波特率")
38 | flag.Parse()
39 | p := go645.NewRTUClientProvider(go645.WithSerialConfig(serial.Config{Address: "COM2", BaudRate: b, DataBits: 8, StopBits: 1, Parity: "E", Timeout: time.Second * 30}), go645.WithEnableLogger(), go645.WithPrefixHandler(&Handler{}))
40 | c := go645.NewClient(p)
41 | err := c.Connect()
42 | if err != nil {
43 | panic(err)
44 | }
45 | defer c.Close()
46 | for {
47 | time.Sleep(50 * time.Millisecond)
48 | read, _, err := c.Read(go645.NewAddress("000703200136", go645.LittleEndian), 0x00_01_00_00)
49 | if err == nil {
50 | log.Printf("rec %s", read.GetValue())
51 | }
52 |
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/example/gaea/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "bytes"
5 | "flag"
6 | "github.com/goburrow/serial"
7 | "github.com/zcx1218029121/go645"
8 | "log"
9 | "sync"
10 | "time"
11 | )
12 |
13 | var device sync.Map
14 | var mu sync.Mutex
15 |
16 | //特殊电表解析 同步
17 | func main() {
18 | var b int
19 | var code int
20 | //1200、2400、4800、9600
21 | flag.IntVar(&b, "b", 19200, "波特率")
22 | flag.IntVar(&code, "c", 0x02_04_00_00, "特征码")
23 | flag.Parse()
24 | p := go645.NewRTUClientProvider(
25 | go645.WithSerialConfig(serial.Config{
26 | Address: "/dev/ttyUSB3",
27 | BaudRate: b,
28 | DataBits: 8,
29 | StopBits: 1,
30 | Parity: "E",
31 | Timeout: time.Second * 30}),
32 | go645.WithEnableLogger())
33 | c := go645.NewClient(p)
34 | err := c.Connect()
35 | if err != nil {
36 | panic(err)
37 | }
38 | if err != nil {
39 | return
40 | }
41 | defer c.Close()
42 |
43 | forceOnline(c)
44 |
45 | go func() {
46 | time.Sleep(1 * time.Minute)
47 | forceOnline(c)
48 | }()
49 |
50 | for {
51 | //如果对扫描速度不是要求很高 需要重新扫描
52 | time.Sleep(50 * time.Millisecond)
53 |
54 | device.Range(func(key, value interface{}) bool {
55 | func() {
56 | mu.Lock()
57 | defer mu.Unlock()
58 | read, _, err := c.Read(go645.NewAddress(key.(string), go645.LittleEndian), int32(code))
59 | if err == nil {
60 | log.Printf("address %s :rec %f", key.(string), read.GetFloat64Value())
61 | }
62 | }()
63 | return false
64 | })
65 | }
66 |
67 | }
68 | func forceOnline(c go645.Client) {
69 | c.Broadcast(go645.NullData{}, *go645.NewControlValue(0x0a))
70 | mu.Lock()
71 | defer mu.Unlock()
72 | for {
73 |
74 | frame, err := c.ReadRawFrame()
75 | if err != nil {
76 | log.Printf(err.Error())
77 | return
78 | }
79 | if frame != nil && len(frame) > 10 {
80 | p, err := go645.Decode(bytes.NewBuffer(frame))
81 | if err != nil {
82 | log.Printf(err.Error())
83 | return
84 | }
85 | device.Store(p.Address.GetStrAddress(go645.LittleEndian), nil)
86 | }
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/exception.go:
--------------------------------------------------------------------------------
1 | package go645
2 |
3 | import (
4 | "bytes"
5 | "encoding/binary"
6 | "fmt"
7 | )
8 |
9 | //Exception 错误
10 | type Exception struct {
11 | Err uint16
12 | }
13 |
14 | func (e *Exception) Error() string {
15 |
16 | return fmt.Sprintf("645 err %d", e.Err)
17 | }
18 |
19 | func (e Exception) Encode(buffer *bytes.Buffer) error {
20 | return binary.Write(buffer, binary.LittleEndian, e.Err+0x33)
21 | }
22 |
23 | //GetLen 错误响应报文长度2
24 | func (e Exception) GetLen() byte {
25 | return 2
26 | }
27 |
--------------------------------------------------------------------------------
/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/zcx1218029121/go645
2 |
3 | go 1.16
4 |
5 | require (
6 | github.com/goburrow/serial v0.1.0
7 | github.com/jonboulle/clockwork v0.2.2 // indirect
8 | github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible
9 | github.com/lestrrat-go/strftime v1.0.5 // indirect
10 | go.uber.org/atomic v1.9.0
11 | go.uber.org/zap v1.20.0
12 | )
13 |
--------------------------------------------------------------------------------
/go.sum:
--------------------------------------------------------------------------------
1 | github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
2 | github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
3 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
4 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
5 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
6 | github.com/goburrow/serial v0.1.0 h1:v2T1SQa/dlUqQiYIT8+Cu7YolfqAi3K96UmhwYyuSrA=
7 | github.com/goburrow/serial v0.1.0/go.mod h1:sAiqG0nRVswsm1C97xsttiYCzSLBmUZ/VSlVLZJ8haA=
8 | github.com/jonboulle/clockwork v0.2.2 h1:UOGuzwb1PwsrDAObMuhUnj0p5ULPj8V/xJ7Kx9qUBdQ=
9 | github.com/jonboulle/clockwork v0.2.2/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8=
10 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
11 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
12 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
13 | github.com/lestrrat-go/envload v0.0.0-20180220234015-a3eb8ddeffcc h1:RKf14vYWi2ttpEmkA4aQ3j4u9dStX2t4M8UM6qqNsG8=
14 | github.com/lestrrat-go/envload v0.0.0-20180220234015-a3eb8ddeffcc/go.mod h1:kopuH9ugFRkIXf3YoqHKyrJ9YfUFsckUU9S7B+XP+is=
15 | github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible h1:Y6sqxHMyB1D2YSzWkLibYKgg+SwmyFU9dF2hn6MdTj4=
16 | github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible/go.mod h1:ZQnN8lSECaebrkQytbHj4xNgtg8CR7RYXnPok8e0EHA=
17 | github.com/lestrrat-go/strftime v1.0.5 h1:A7H3tT8DhTz8u65w+JRpiBxM4dINQhUXAZnhBa2xeOE=
18 | github.com/lestrrat-go/strftime v1.0.5/go.mod h1:E1nN3pCbtMSu1yjSVeyuRFVm/U0xoR76fd03sz+Qz4g=
19 | github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
20 | github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
21 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
22 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
23 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
24 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
25 | github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
26 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
27 | github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
28 | go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
29 | go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE=
30 | go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
31 | go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI=
32 | go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
33 | go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4=
34 | go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
35 | go.uber.org/zap v1.20.0 h1:N4oPlghZwYG55MlU6LXk/Zp00FVNE9X9wrYO8CEs4lc=
36 | go.uber.org/zap v1.20.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw=
37 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
38 | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
39 | golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
40 | golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
41 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
42 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
43 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
44 | golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
45 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
46 | golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
47 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
48 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
49 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
50 | golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
51 | golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
52 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
53 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
54 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
55 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
56 | golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
57 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
58 | golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
59 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
60 | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
61 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
62 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
63 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
64 | gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
65 | gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
66 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
67 | gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
68 | gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
69 |
--------------------------------------------------------------------------------
/log.go:
--------------------------------------------------------------------------------
1 | package go645
2 |
3 | import (
4 | "log"
5 | "os"
6 | "sync/atomic"
7 |
8 | "go.uber.org/zap"
9 | )
10 |
11 | // 内部调试实现.
12 | type logger struct {
13 | provider LogProvider
14 | // has log output enabled,
15 | // 1: enable
16 | // 0: disable
17 | has uint32
18 | }
19 |
20 | // newLogger new logger with prefix.
21 | func newLogger(prefix string) logger {
22 | return logger{
23 | provider: defaultLogger{log.New(os.Stdout, prefix, log.LstdFlags)},
24 | has: 0,
25 | }
26 | }
27 |
28 | // LogMode set enable or disable log output when you has set logger.
29 | func (sf *logger) LogMode(enable bool) {
30 | if enable {
31 | atomic.StoreUint32(&sf.has, 1)
32 | } else {
33 | atomic.StoreUint32(&sf.has, 0)
34 | }
35 | }
36 |
37 | // setLogProvider overwrite log provider.
38 | func (sf *logger) setLogProvider(p LogProvider) {
39 | if p != nil {
40 | sf.provider = p
41 | }
42 | }
43 |
44 | // Error Log ERROR level message.
45 | func (sf logger) Errorf(format string, v ...interface{}) {
46 | if atomic.LoadUint32(&sf.has) == 1 {
47 | sf.provider.Errorf(format, v...)
48 | }
49 | }
50 |
51 | // Debug Log DEBUG level message.
52 | func (sf logger) Debugf(format string, v ...interface{}) {
53 | if atomic.LoadUint32(&sf.has) == 1 {
54 | sf.provider.Debugf(format, v...)
55 | }
56 | }
57 |
58 | // default log.
59 | type defaultLogger struct {
60 | *log.Logger
61 | }
62 |
63 | // check implement LogProvider interface.
64 | var _ LogProvider = (*defaultLogger)(nil)
65 |
66 | // Error Log ERROR level message.
67 | func (sf defaultLogger) Errorf(format string, v ...interface{}) {
68 | sf.Printf("[E]: "+format, v...)
69 | }
70 |
71 | // Debug Log DEBUG level message.
72 | func (sf defaultLogger) Debugf(format string, v ...interface{}) {
73 | sf.Printf("[D]: "+format, v...)
74 | }
75 |
76 | // default log.
77 | type ZapLogger struct {
78 | *zap.Logger
79 | }
80 |
81 | // check implement LogProvider interface.
82 | var _ LogProvider = (*ZapLogger)(nil)
83 |
84 | // Error Log ERROR level message.
85 | func (sf ZapLogger) Errorf(format string, v ...interface{}) {
86 | sf.Logger.Sugar().Errorf("[E]: "+format, v...)
87 | }
88 |
89 | // Debug Log DEBUG level message.
90 | func (sf ZapLogger) Debugf(format string, v ...interface{}) {
91 | sf.Logger.Sugar().Debugf("[D]: "+format, v...)
92 | }
93 |
--------------------------------------------------------------------------------
/prefix.go:
--------------------------------------------------------------------------------
1 | package go645
2 |
3 | import (
4 | "bytes"
5 | "io"
6 | )
7 |
8 | var _ PrefixHandler = (*DefaultPrefix)(nil)
9 |
10 | type PrefixHandler interface {
11 | EncodePrefix(buffer *bytes.Buffer) error
12 | DecodePrefix(reader io.Reader) ([]byte, error)
13 | }
14 |
15 | type DefaultPrefix struct {
16 | }
17 |
18 | func (d DefaultPrefix) EncodePrefix(buffer *bytes.Buffer) error {
19 |
20 | return nil
21 | }
22 |
23 | func (d DefaultPrefix) DecodePrefix(reader io.Reader) ([]byte, error) {
24 | return nil, nil
25 | }
26 |
--------------------------------------------------------------------------------
/protocol.go:
--------------------------------------------------------------------------------
1 | package go645
2 |
3 | import (
4 | "bytes"
5 | "encoding/binary"
6 | "encoding/hex"
7 | "fmt"
8 | "strconv"
9 | "strings"
10 | "time"
11 | )
12 |
13 | const (
14 | LittleEndian Order = false
15 | BigEndian Order = true
16 | Start = 0x68
17 | End = 0x16
18 | HeadLen = 1 + 6 + 1
19 | BroadcastAddress = "999999999999"
20 | )
21 |
22 | var (
23 | _ InformationElement = (*Address)(nil)
24 |
25 | _ InformationElement = (*Protocol)(nil)
26 |
27 | _ InformationElement = (*ReadData)(nil)
28 |
29 | _ InformationElement = (*ReadRequestData)(nil)
30 |
31 | _ InformationElement = (*Exception)(nil)
32 | _ InformationElement = (*YearDateTimeS)(nil)
33 | _ InformationElement = (*DateTime)(nil)
34 | _ InformationElement = (*NullData)(nil)
35 | )
36 |
37 | type (
38 | //Order 地址大小端
39 | Order bool
40 | InformationElement interface {
41 | Encode(buffer *bytes.Buffer) error
42 | GetLen() byte
43 | }
44 | NullData struct {
45 | }
46 |
47 | YearDateTimeS struct {
48 | ss byte
49 | mm byte
50 | hh byte
51 | day byte
52 | month byte
53 | year byte
54 | }
55 |
56 | DateTime struct {
57 | mm byte
58 | hh byte
59 | day byte
60 | month byte
61 | }
62 |
63 | //Address 表计地址
64 | Address struct {
65 | value []byte
66 | strValue string
67 | }
68 |
69 | //Protocol 协议
70 | Protocol struct {
71 | //Start 645协议起始符号
72 | Start byte
73 | //设备地址 6个字节的BCD
74 | Address Address
75 | //Start 645协议起始符号 标志报文头结束
76 | Start2 byte
77 | //Control 控制域
78 | Control *Control
79 | //Control 数据长度
80 | DataLength byte
81 | //Control 数据抽象
82 | Data InformationElement
83 | //CS 校验和
84 | CS byte
85 | //End 0x16
86 | End byte
87 | }
88 | )
89 |
90 | func (n NullData) Encode(buffer *bytes.Buffer) error {
91 | return nil
92 | }
93 |
94 | func (n NullData) GetLen() byte {
95 | return 0
96 | }
97 |
98 | func (t DateTime) Encode(buffer *bytes.Buffer) error {
99 | var err error
100 | err = binary.Write(buffer, binary.BigEndian, t.mm)
101 | if err != nil {
102 | return err
103 | }
104 | err = binary.Write(buffer, binary.BigEndian, t.hh)
105 | if err != nil {
106 | return err
107 | }
108 | err = binary.Write(buffer, binary.BigEndian, t.day)
109 | if err != nil {
110 | return err
111 | }
112 | err = binary.Write(buffer, binary.BigEndian, t.month)
113 | if err != nil {
114 | return err
115 | }
116 | return err
117 | }
118 |
119 | func (t DateTime) GetLen() byte {
120 | panic("implement me")
121 | }
122 |
123 | func NewTimeS() *YearDateTimeS {
124 | t := time.Now()
125 | return &YearDateTimeS{ss: byte(t.Second()), mm: byte(t.Minute()), hh: byte(t.Hour()), day: byte(t.Day()), year: byte(t.Year()), month: byte(t.Month())}
126 | }
127 | func (t YearDateTimeS) Encode(buffer *bytes.Buffer) error {
128 | var err error
129 | err = binary.Write(buffer, binary.BigEndian, t.ss)
130 | if err != nil {
131 | return err
132 | }
133 | err = binary.Write(buffer, binary.BigEndian, t.mm)
134 | if err != nil {
135 | return err
136 | }
137 | err = binary.Write(buffer, binary.BigEndian, t.hh)
138 | if err != nil {
139 | return err
140 | }
141 | err = binary.Write(buffer, binary.BigEndian, t.day)
142 | if err != nil {
143 | return err
144 | }
145 | err = binary.Write(buffer, binary.BigEndian, t.month)
146 | if err != nil {
147 | return err
148 | }
149 | err = binary.Write(buffer, binary.BigEndian, t.year)
150 | if err != nil {
151 | return err
152 | }
153 | return err
154 |
155 | }
156 |
157 | func (t YearDateTimeS) GetLen() byte {
158 | return 6
159 | }
160 |
161 | // NewAddress ,构建设备地址
162 | // 参数:
163 | // address : 设备地址
164 | // order : 大小端表示
165 | // 返回值:
166 | // *Address 设备地址
167 | func NewAddress(address string, order Order) Address {
168 | value := Number2bcd(address)
169 | if !order {
170 | for i, j := 0, len(value)-1; i < j; i, j = i+1, j-1 {
171 | value[i], value[j] = value[j], value[i]
172 | }
173 | }
174 |
175 | return Address{value: value, strValue: address}
176 | }
177 |
178 | func NewReadData(dataType int32, value string) ReadData {
179 | return ReadData{dataType: Int2bytes(dataType), bcdValue: value}
180 | }
181 |
182 | func NewProtocol(address Address, data InformationElement, control *Control) *Protocol {
183 | return &Protocol{
184 | Start: Start,
185 | Start2: Start,
186 | End: End,
187 | Address: address,
188 | Control: control,
189 | DataLength: data.GetLen(),
190 | Data: data,
191 | }
192 | }
193 |
194 | // Encode ,协议解码
195 | // 参数:
196 | // buffer : 字节码缓冲
197 | // 返回值:
198 | // error 解码异常
199 | func (a Address) Encode(buffer *bytes.Buffer) error {
200 | return binary.Write(buffer, binary.BigEndian, a.value)
201 | }
202 |
203 | func (a Address) GetStrAddress(order Order) string {
204 | if !order {
205 | temp := make([]byte, len(a.value))
206 | for i, j := 0, len(a.value)-1; i < j; i, j = i+1, j-1 {
207 | temp[i], temp[j] = a.value[j], a.value[i]
208 | }
209 | return Bcd2Number(temp)
210 | }
211 | return a.strValue
212 | }
213 |
214 | func (a Address) GetLen() byte {
215 | return 6
216 | }
217 |
218 | //GetHex 返回16进制string
219 | func GetHex(protocol *Protocol) (string, error) {
220 | bf := bytes.NewBuffer(make([]byte, 0))
221 | if err := protocol.Encode(bf); err != nil {
222 | return "", err
223 | }
224 | return hex.EncodeToString(bf.Bytes()), nil
225 | }
226 |
227 | func (p Protocol) Encode(buffer *bytes.Buffer) error {
228 | //计算cs 需要重写开辟字节码缓冲区
229 | tmp := make([]byte, 0)
230 | bf := bytes.NewBuffer(tmp)
231 | var err error
232 | write := func(data interface{}) {
233 | if err != nil {
234 | return
235 | }
236 | err = binary.Write(bf, binary.LittleEndian, data)
237 | }
238 | write(&p.Start)
239 | err = p.Address.Encode(bf)
240 | write(&p.Start2)
241 | err = p.Control.Encode(bf)
242 | write(&p.DataLength)
243 | err = p.Data.Encode(bf)
244 | var cs = 0
245 | for _, b := range bf.Bytes() {
246 | cs += int(b)
247 | }
248 | p.CS = byte(cs)
249 | write(p.CS)
250 | write(p.End)
251 | err = binary.Write(buffer, binary.LittleEndian, bf.Bytes())
252 | return err
253 |
254 | }
255 |
256 | func (p Protocol) GetLen() byte {
257 | if p.DataLength != 0 {
258 | return p.DataLength
259 | }
260 | return HeadLen + 4 + p.Data.GetLen()
261 | }
262 |
263 | func Bcd2Number(bcd []byte) string {
264 | var number string
265 | for _, i := range bcd {
266 | number += fmt.Sprintf("%02X", i)
267 | }
268 | pos := strings.LastIndex(number, "F")
269 | if pos == 8 {
270 | return "0"
271 | }
272 | return number[pos+1:]
273 | }
274 |
275 | func Number2bcd(number string) []byte {
276 | var rNumber = number
277 | for i := 0; i < 8-len(number); i++ {
278 | rNumber = "f" + rNumber
279 | }
280 | bcd := Hex2Byte(rNumber)
281 | return bcd
282 | }
283 |
284 | func Hex2Byte(str string) []byte {
285 | slen := len(str)
286 | bHex := make([]byte, len(str)/2)
287 | ii := 0
288 | for i := 0; i < len(str); i = i + 2 {
289 | if slen != 1 {
290 | ss := string(str[i]) + string(str[i+1])
291 | bt, _ := strconv.ParseInt(ss, 16, 32)
292 | bHex[ii] = byte(bt)
293 | ii = ii + 1
294 | slen = slen - 2
295 | }
296 | }
297 | return bHex
298 | }
299 |
300 | func Int2bytes(data int32) []byte {
301 | var b3 = make([]byte, 4)
302 | b3[0] = uint8(data)
303 | b3[1] = uint8(data >> 8)
304 | b3[2] = uint8(data >> 16)
305 | b3[3] = uint8(data >> 24)
306 | return b3
307 | }
308 |
309 | func WriteWithOffSet(buffer *bytes.Buffer, data byte) error {
310 | return binary.Write(buffer, binary.LittleEndian, data+0x33)
311 | }
312 |
--------------------------------------------------------------------------------
/protocol_test.go:
--------------------------------------------------------------------------------
1 | package go645
2 |
3 | import (
4 | "bytes"
5 | "encoding/binary"
6 | "encoding/hex"
7 | "errors"
8 | "fmt"
9 | "testing"
10 | )
11 |
12 | //TestRead 测试上报请求
13 | func TestDecode(t *testing.T) {
14 | str := "681401003182216891083333343339333333f116"
15 | decodeString, err := hex.DecodeString(str)
16 | if err != nil {
17 | return
18 | }
19 | p, _ := Decode(bytes.NewBuffer(decodeString))
20 | if p.Address.strValue != "140100318221" {
21 | t.Errorf("地址解析错误")
22 | }
23 | if p.Address.GetLen() != 6 {
24 | t.Errorf("长度错误")
25 | }
26 | if !p.Control.IsState(IsSlave) || !p.Control.IsState(Read) {
27 | t.Errorf("状态解析错误")
28 | }
29 | fmt.Printf("%f \n", p.Data.(*ReadData).GetFloat64Value())
30 | if p.GetLen() != 0x08 {
31 | t.Errorf("长度错误")
32 | }
33 |
34 | }
35 |
36 | //TestRead 测试解析读请求
37 | func TestRead(t *testing.T) {
38 | str := "6881040007213a68910733353535333333ba16"
39 | data := make([]byte, 0)
40 | c := NewControl()
41 | c.SetState(Read)
42 | r := ReadRequest(NewAddress("610100000000", BigEndian), 0x00_01_00_00)
43 | bf := bytes.NewBuffer(data)
44 | _ = r.Encode(bf)
45 | decodeString, _ := hex.DecodeString(str)
46 | p2, _ := Decode(bytes.NewBuffer(decodeString))
47 | print(p2.Data.(*ReadData).GetValue() + "\n")
48 | print(p2.Data.(*ReadData).GetDataTypeStr() + "\n")
49 | fmt.Printf("%f", p2.Data.(*ReadData).GetFloat64Value())
50 | }
51 |
52 | //TestSend 测试发送
53 | func TestSend(t *testing.T) {
54 | str := "68610100000000681104333334331416"
55 | data := make([]byte, 0)
56 | c := NewControl()
57 | c.SetState(Read)
58 | r := ReadRequest(NewAddress("610100000000", BigEndian), 0x00_01_00_00)
59 | bf := bytes.NewBuffer(data)
60 | _ = r.Encode(bf)
61 | p, _ := Decode(bf)
62 | decodeString, _ := hex.DecodeString(str)
63 | p2, _ := Decode(bytes.NewBuffer(decodeString))
64 | print(p.Data.(*ReadData).GetValue())
65 | AssertEquest("地址错误", p2.Address.strValue, p.Address.strValue, t)
66 | AssertEquest("校验码错误", p.CS, p2.CS, t)
67 |
68 | }
69 | func TestLEnd(t *testing.T) {
70 | str := "68610100000000681104333334331416"
71 | data := make([]byte, 0)
72 | c := NewControl()
73 | c.SetState(Read)
74 | r := ReadRequest(NewAddress("610100000000", LittleEndian), 0x00_01_00_00)
75 | bf := bytes.NewBuffer(data)
76 | _ = r.Encode(bf)
77 | p, _ := Decode(bf)
78 | decodeString, _ := hex.DecodeString(str)
79 | p2, _ := Decode(bytes.NewBuffer(decodeString))
80 | AssertEquest("地址错误", p2.Address.GetStrAddress(LittleEndian), "000000000161", t)
81 | AssertEquest("地址错误", p2.Address.GetStrAddress(BigEndian), "610100000000", t)
82 | AssertEquest("校验码错误", p.CS, p2.CS, t)
83 | }
84 | func Assert(msg string, assert func() bool, t *testing.T) {
85 | if !assert() {
86 | t.Errorf(msg)
87 | }
88 | }
89 | func AssertEquest(msg string, exp interface{}, act interface{}, t *testing.T) {
90 | Assert(msg, func() bool { return exp == act }, t)
91 | }
92 |
93 | func AssertState(assert func() bool, t *testing.T) {
94 | Assert("状态解析错误", assert, t)
95 | }
96 | func TestControl_IsState(t *testing.T) {
97 | d := NewControlValue(0x0A)
98 | println(d.IsStates(IsSlave))
99 | println(d.IsStates(Broadcast))
100 | println(d.IsStates(Read))
101 |
102 | }
103 | func TestControl(t *testing.T) {
104 | c := &Control{}
105 | if c.getLen() != 1 {
106 | t.Errorf("长度错误")
107 | }
108 | c.SetState(SlaveErr)
109 | if !c.IsState(SlaveErr) {
110 | t.Errorf("设置错误")
111 | }
112 | c.Reset()
113 | if c.IsState(SlaveErr) {
114 | t.Errorf("复归错误")
115 | }
116 | c.SetStates(SlaveErr, IsSlave, HasNext, Retain, Broadcast, ReadNext, ReadAddress)
117 | if !c.IsStates(SlaveErr, IsSlave) {
118 | t.Errorf("设置错误")
119 | }
120 | }
121 | func (c *Control) TestErr(buffer *bytes.Buffer) error {
122 |
123 | var bf *bytes.Buffer
124 | r := ReadRequest(NewAddress("610100000000", LittleEndian), 0x00_01_00_00)
125 | _ = r.Encode(bf)
126 |
127 | if err := binary.Write(buffer, binary.BigEndian, c.Data); err != nil {
128 | s := fmt.Sprintf("Control , %v", err)
129 | return errors.New(s)
130 | }
131 | return nil
132 | }
133 | func TestReadResponse(t *testing.T) {
134 | rp := ReadResponse(NewAddress("610100000000", LittleEndian), 0x00_01_00_00, NewControl(), "200")
135 |
136 | if rp.Address.GetStrAddress(LittleEndian) != "610100000000" {
137 | t.Errorf("地址错误")
138 | }
139 | }
140 |
--------------------------------------------------------------------------------
/read.go:
--------------------------------------------------------------------------------
1 | package go645
2 |
3 | import (
4 | "bytes"
5 | "encoding/binary"
6 | "encoding/hex"
7 | "strconv"
8 | "time"
9 | )
10 |
11 | type (
12 | ReadDataWithTime struct {
13 | ReadData
14 | value float64
15 | time time.Time
16 | }
17 | //ReadData 数据域
18 | ReadData struct {
19 | //数据标识 4 个字节
20 | dataType []byte
21 | //bcd 码的数据
22 | bcdValue string
23 | rawValue []byte
24 | Negative bool
25 | }
26 | //WriteData 写数据
27 | WriteData struct {
28 | dataType []byte
29 | permissions byte
30 | passWord []byte
31 | optCode []byte
32 | }
33 | ReadRequestData struct {
34 | dataType []byte
35 | recordNum byte
36 | min byte
37 | hours byte
38 | day byte
39 | month byte
40 | year byte
41 | withTime bool
42 | }
43 | )
44 |
45 | func (d ReadData) GetDataType() []byte {
46 | return d.dataType
47 | }
48 | func (d ReadData) GetDataTypeStr() string {
49 | return hex.EncodeToString(d.dataType)
50 | }
51 | func (d *ReadData) GetFloat64ValueWithTime() *ReadDataWithTime {
52 | if d.dataType[3] == 0x01 {
53 | _, _ = strconv.Atoi(d.bcdValue[:6])
54 | }
55 | return nil
56 | }
57 | func (d *ReadData) GetIntValue() (int, error) {
58 | value, err := strconv.Atoi(d.bcdValue)
59 | if err != nil {
60 | return 0, err
61 | }
62 | return value,nil
63 | }
64 | func (d *ReadData) GetFloat64Value() float64 {
65 | var data float64
66 | if d.dataType[3] == 0x00 {
67 | value, _ := strconv.Atoi(d.bcdValue)
68 | data = float64(value) * 0.01
69 | }
70 | if d.dataType[3] == 0x02 && d.dataType[2] == 0x01 && d.dataType[0] == 0 {
71 | value, _ := strconv.Atoi(d.bcdValue)
72 | data = float64(value) / 10
73 | }
74 | if d.dataType[3] == 0x02 && d.dataType[2] == 0x02 && d.dataType[0] == 0 {
75 | value, _ := strconv.Atoi(d.bcdValue)
76 | data = float64(value) * 0.001
77 | }
78 | if d.dataType[3] == 0x02 && d.dataType[2] == 0x03 && d.dataType[0] == 0 {
79 | value, _ := strconv.Atoi(d.bcdValue)
80 | data = float64(value) * 0.0001
81 | }
82 | if d.dataType[3] == 0x02 && d.dataType[2] == 0x04 {
83 | value, _ := strconv.Atoi(d.bcdValue)
84 | data = float64(value) * 0.0001
85 |
86 | }
87 | if d.dataType[3] == 0x02 && d.dataType[2] == 0x05 && d.dataType[0] == 0 {
88 | value, _ := strconv.Atoi(d.bcdValue)
89 | data = float64(value) * 0.0001
90 | }
91 | if d.dataType[3] == 0x02 && d.dataType[2] == 0x06 && d.dataType[0] == 0 {
92 | value, _ := strconv.Atoi(d.bcdValue)
93 | data = float64(value) * 0.001
94 | }
95 | if d.dataType[3] == 0x02 && d.dataType[2] == 0x07 && d.dataType[0] == 0 {
96 | value, _ := strconv.Atoi(d.bcdValue)
97 | data = float64(value) * 0.01
98 | }
99 | if d.dataType[3] == 0x02 && d.dataType[2] == 0x08 && d.dataType[0] == 0 {
100 | value, _ := strconv.Atoi(d.bcdValue)
101 | data = float64(value) * 0.01
102 | }
103 | if d.dataType[3] == 0x02 && d.dataType[2] == 0x09 && d.dataType[0] == 0 {
104 | value, _ := strconv.Atoi(d.bcdValue)
105 | data = float64(value) * 0.01
106 | }
107 | if d.dataType[3] == 0x02 && d.dataType[2] == 0x0A && d.dataType[1] == 0x01 {
108 | value, _ := strconv.Atoi(d.bcdValue)
109 | data = float64(value) * 0.01
110 | }
111 | if d.dataType[3] == 0x02 && d.dataType[2] == 0x0A && d.dataType[1] == 0x02 {
112 | value, _ := strconv.Atoi(d.bcdValue)
113 | data = float64(value) * 0.01
114 | }
115 | if d.dataType[3] == 0x02 && d.dataType[2] == 0x0A && d.dataType[1] == 0x03 {
116 | value, _ := strconv.Atoi(d.bcdValue)
117 | data = float64(value) * 0.01
118 | }
119 | if d.dataType[3] == 0x02 && d.dataType[2] == 0x0B && d.dataType[1] == 0x01 {
120 | value, _ := strconv.Atoi(d.bcdValue)
121 | data = float64(value) * 0.01
122 | }
123 | if d.dataType[3] == 0x02 && d.dataType[2] == 0x0B && d.dataType[1] == 0x02 {
124 | value, _ := strconv.Atoi(d.bcdValue)
125 | data = float64(value) * 0.01
126 | }
127 | if d.dataType[3] == 0x02 && d.dataType[2] == 0x0B && d.dataType[1] == 0x03 {
128 | value, _ := strconv.Atoi(d.bcdValue)
129 | data = float64(value) * 0.01
130 | }
131 | if d.dataType[3] == 0x02 && d.dataType[2] == 0x80 && d.dataType[0] == 0x01 {
132 | value, _ := strconv.Atoi(d.bcdValue)
133 | data = float64(value) / 1000
134 | }
135 | if d.dataType[3] == 0x02 && d.dataType[2] == 0x80 && d.dataType[0] == 0x02 {
136 | value, _ := strconv.Atoi(d.bcdValue)
137 | data = float64(value) / 100
138 | }
139 | if d.dataType[3] == 0x02 && d.dataType[2] == 0x80 && d.dataType[0] == 0x03 {
140 | value, _ := strconv.Atoi(d.bcdValue)
141 | data = float64(value) / 10000
142 | }
143 | if d.dataType[3] == 0x02 && d.dataType[2] == 0x80 && d.dataType[0] == 0x04 {
144 | value, _ := strconv.Atoi(d.bcdValue)
145 | data = float64(value) / 10000
146 | }
147 | if d.dataType[3] == 0x02 && d.dataType[2] == 0x80 && d.dataType[0] == 0x05 {
148 | value, _ := strconv.Atoi(d.bcdValue)
149 | data = float64(value) / 10000
150 | }
151 | if d.dataType[3] == 0x02 && d.dataType[2] == 0x80 && d.dataType[0] == 0x06 {
152 | value, _ := strconv.Atoi(d.bcdValue)
153 | data = float64(value) / 10000
154 | }
155 | if d.dataType[3] == 0x02 && d.dataType[2] == 0x80 && d.dataType[0] == 0x07 {
156 | value, _ := strconv.Atoi(d.bcdValue)
157 | data = float64(value) / 10
158 | }
159 | if d.dataType[3] == 0x02 && d.dataType[2] == 0x80 && d.dataType[0] == 0x08 {
160 | value, _ := strconv.Atoi(d.bcdValue)
161 | data = float64(value) / 100
162 | }
163 | if d.dataType[3] == 0x02 && d.dataType[2] == 0x80 && d.dataType[0] == 0x09 {
164 | value, _ := strconv.Atoi(d.bcdValue)
165 | data = float64(value) / 100
166 | }
167 | if d.dataType[3] == 0x02 && d.dataType[2] == 0x80 && d.dataType[0] == 0x0A {
168 | value, _ := strconv.Atoi(d.bcdValue)
169 | data = float64(value)
170 | }
171 | if d.dataType[3] == 0x02 && d.dataType[2] == 0x80 && d.dataType[0] == 0x0B {
172 | value, _ := strconv.Atoi(d.bcdValue)
173 | data = float64(value) / 10000
174 | }
175 | if d.Negative {
176 | return data * -1
177 | }
178 | return data
179 | }
180 | func (d *ReadData) GetFloat64ValueUnsigned() float64 {
181 | for i, j := 0, len(d.dataType)-1; i < j; i, j = i+1, j-1 {
182 | d.dataType[j], d.dataType[i] = d.dataType[i], d.dataType[j]
183 | }
184 | if d.dataType[3] == 0x00 {
185 | value, _ := strconv.Atoi(d.bcdValue)
186 | return float64(value) * 0.01
187 | }
188 | if d.dataType[3] == 0x02 && d.dataType[2] == 0x03 && d.dataType[0] == 0 {
189 | value, _ := strconv.Atoi(d.bcdValue)
190 | return float64(value) * 0.0001
191 |
192 | }
193 | if d.dataType[3] == 0x02 && d.dataType[2] == 0x04 {
194 | value, _ := strconv.Atoi(d.bcdValue)
195 | data := float64(value) * 0.0001
196 | if data > 80 {
197 | return (data - 80) * -1
198 | } else {
199 | return data
200 | }
201 | }
202 | if d.dataType[3] == 0x02 && d.dataType[2] == 0x05 && d.dataType[0] == 0 {
203 | value, _ := strconv.Atoi(d.bcdValue)
204 | return float64(value) * 0.0001
205 | }
206 | if d.dataType[3] == 0x02 && d.dataType[2] == 0x06 && d.dataType[0] == 0 {
207 | value, _ := strconv.Atoi(d.bcdValue)
208 | return float64(value) * 0.001
209 | }
210 | if d.dataType[3] == 0x02 && d.dataType[2] == 0x05 && d.dataType[0] == 0 {
211 | value, _ := strconv.Atoi(d.bcdValue)
212 | return float64(value) * 0.0001
213 | }
214 | if d.dataType[3] == 0x02 && d.dataType[2] == 0x06 && d.dataType[0] == 0 {
215 | value, _ := strconv.Atoi(d.bcdValue)
216 | return float64(value) * 0.001
217 | }
218 | if d.dataType[3] == 0x02 && d.dataType[2] == 0x07 && d.dataType[0] == 0 {
219 | value, _ := strconv.Atoi(d.bcdValue)
220 | return float64(value) * 0.1
221 | }
222 | if d.dataType[3] == 0x02 && d.dataType[2] == 0x08 && d.dataType[0] == 0 {
223 | value, _ := strconv.Atoi(d.bcdValue)
224 | return float64(value) * 0.01
225 | }
226 | if d.dataType[3] == 0x02 && d.dataType[2] == 0x09 && d.dataType[0] == 0 {
227 | value, _ := strconv.Atoi(d.bcdValue)
228 | return float64(value) * 0.01
229 | }
230 | if d.dataType[3] == 0x02 && d.dataType[2] == 0x0A && d.dataType[1] == 0x01 {
231 | value, _ := strconv.Atoi(d.bcdValue)
232 | return float64(value) * 0.01
233 | }
234 | if d.dataType[3] == 0x02 && d.dataType[2] == 0x0A && d.dataType[1] == 0x02 {
235 | value, _ := strconv.Atoi(d.bcdValue)
236 | return float64(value) * 0.01
237 | }
238 | if d.dataType[3] == 0x02 && d.dataType[2] == 0x0A && d.dataType[1] == 0x03 {
239 | value, _ := strconv.Atoi(d.bcdValue)
240 | return float64(value) * 0.01
241 | }
242 | if d.dataType[3] == 0x02 && d.dataType[2] == 0x0B && d.dataType[1] == 0x01 {
243 | value, _ := strconv.Atoi(d.bcdValue)
244 | return float64(value) * 0.01
245 | }
246 | if d.dataType[3] == 0x02 && d.dataType[2] == 0x0B && d.dataType[1] == 0x02 {
247 | value, _ := strconv.Atoi(d.bcdValue)
248 | return float64(value) * 0.01
249 | }
250 | if d.dataType[3] == 0x02 && d.dataType[2] == 0x0B && d.dataType[1] == 0x03 {
251 | value, _ := strconv.Atoi(d.bcdValue)
252 | return float64(value) * 0.01
253 | }
254 | if d.dataType[3] == 0x02 && d.dataType[2] == 0x80 && d.dataType[1] == 0x01 {
255 | value, _ := strconv.Atoi(d.bcdValue)
256 | return float64(value) * 0.001
257 | }
258 | if d.dataType[3] == 0x02 && d.dataType[2] == 0x80 && d.dataType[1] == 0x02 {
259 | value, _ := strconv.Atoi(d.bcdValue)
260 | return float64(value) * 0.01
261 | }
262 | if d.dataType[3] == 0x02 && d.dataType[2] == 0x80 && d.dataType[1] == 0x03 {
263 | value, _ := strconv.Atoi(d.bcdValue)
264 | return float64(value) * 0.001
265 | }
266 | if d.dataType[3] == 0x02 && d.dataType[2] == 0x80 && d.dataType[1] == 0x04 {
267 | value, _ := strconv.Atoi(d.bcdValue)
268 | return float64(value) * 0.001
269 | }
270 | if d.dataType[3] == 0x02 && d.dataType[2] == 0x80 && d.dataType[1] == 0x05 {
271 | value, _ := strconv.Atoi(d.bcdValue)
272 | return float64(value) * 0.001
273 | }
274 | if d.dataType[3] == 0x02 && d.dataType[2] == 0x80 && d.dataType[1] == 0x06 {
275 | value, _ := strconv.Atoi(d.bcdValue)
276 | return float64(value) * 0.001
277 | }
278 | if d.dataType[3] == 0x02 && d.dataType[2] == 0x80 && d.dataType[1] == 0x07 {
279 | value, _ := strconv.Atoi(d.bcdValue)
280 | return float64(value) * 0.1
281 | }
282 | if d.dataType[3] == 0x02 && d.dataType[2] == 0x80 && d.dataType[1] == 0x08 {
283 | value, _ := strconv.Atoi(d.bcdValue)
284 | return float64(value) * 0.01
285 | }
286 | if d.dataType[3] == 0x02 && d.dataType[2] == 0x80 && d.dataType[1] == 0x09 {
287 | value, _ := strconv.Atoi(d.bcdValue)
288 | return float64(value) * 0.01
289 | }
290 | if d.dataType[3] == 0x02 && d.dataType[2] == 0x80 && d.dataType[1] == 0x09 {
291 | value, _ := strconv.Atoi(d.bcdValue)
292 | return float64(value) * 0.01
293 | }
294 | if d.dataType[3] == 0x02 && d.dataType[2] == 0x80 && d.dataType[1] == 0x0A {
295 | value, _ := strconv.Atoi(d.bcdValue)
296 | return float64(value)
297 | }
298 | if d.dataType[3] == 0x02 && d.dataType[2] == 0x80 && d.dataType[1] == 0x0B {
299 | value, _ := strconv.Atoi(d.bcdValue)
300 | return float64(value) * 0.0001
301 | }
302 | return 0
303 | }
304 |
305 | func (d ReadData) GetValue() string {
306 | return d.bcdValue
307 | }
308 |
309 | func (d ReadData) Encode(buffer *bytes.Buffer) error {
310 | //写入数据域 已经反转过了
311 | for index, b := range d.dataType {
312 | d.dataType[index] = b + 0x33
313 | }
314 | if err := binary.Write(buffer, binary.LittleEndian, d.dataType); err != nil {
315 | return err
316 | }
317 | if d.bcdValue != "" {
318 | //写入数据
319 | bcd := Number2bcd(d.bcdValue)
320 | //翻转
321 | for i, j := 0, len(bcd)-1; i < j; i, j = i+1, j-1 {
322 | bcd[i], bcd[j] = bcd[j], bcd[i]
323 | }
324 | if err := binary.Write(buffer, binary.LittleEndian, &bcd); err != nil {
325 | return err
326 | }
327 | }
328 | return nil
329 | }
330 |
331 | func (d ReadData) GetLen() byte {
332 | if d.bcdValue == "" {
333 | return 4
334 | }
335 | return 4 + byte(len(Number2bcd(d.bcdValue)))
336 | }
337 |
338 | //ReadRequest 读数据
339 | func ReadRequest(address Address, itemCode int32) *Protocol {
340 | c := NewControl()
341 | c.SetState(Read)
342 | d := NewReadData(itemCode, "")
343 | return NewProtocol(address, d, c)
344 |
345 | }
346 |
347 | //ReadRequestWithBlock 读数据
348 | func ReadRequestWithBlock(address Address, data ReadRequestData) *Protocol {
349 | c := NewControl()
350 | c.SetState(Read)
351 | return NewProtocol(address, data, c)
352 |
353 | }
354 |
355 | //ReadResponse 创建读响应
356 | func ReadResponse(address Address, itemCode int32, control *Control, rawValue string) *Protocol {
357 | return &Protocol{
358 | Start: Start,
359 | Start2: Start,
360 | End: End,
361 | Address: address,
362 | Control: control,
363 | DataLength: 0x04,
364 | Data: NewReadData(itemCode, rawValue),
365 | }
366 |
367 | }
368 |
369 | func (r ReadRequestData) Encode(buffer *bytes.Buffer) error {
370 | //写入数据域 已经反转过了
371 | var err error
372 | for _, b := range r.dataType {
373 | err = WriteWithOffSet(buffer, b)
374 | }
375 | if r.recordNum != 0 {
376 | err = WriteWithOffSet(buffer, r.recordNum)
377 |
378 | }
379 | if r.withTime {
380 | err = WriteWithOffSet(buffer, r.min)
381 | err = WriteWithOffSet(buffer, r.hours)
382 | err = WriteWithOffSet(buffer, r.day)
383 | err = WriteWithOffSet(buffer, r.month)
384 | err = WriteWithOffSet(buffer, r.year)
385 | }
386 | return err
387 | }
388 |
389 | func (r ReadRequestData) GetLen() byte {
390 | var dataLen byte = 4
391 | if r.withTime {
392 | dataLen += 5
393 | }
394 | if r.recordNum != 0 {
395 | dataLen += 1
396 | }
397 | return dataLen
398 | }
399 |
--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
1 | # go dlt645-2007
2 |
3 |
4 |
5 |
6 |
7 |
8 | 用go语言实现的dlt645解析
9 | ```shell
10 | go get github.com/zcx1218029121/go645
11 | ```
12 |
13 | 1. 读请求
14 | ```go
15 | c := go645.NewClient(go645.NewRTUClientProvider(go645.WithEnableLogger(), go645.WithSerialConfig(serial.Config{
16 | Address: "/dev/ttyUSB3",
17 | BaudRate: 19200,
18 | DataBits: 8,
19 | StopBits: 1,
20 | Parity: "E",
21 | Timeout: time.Second * 8,
22 | })))
23 |
24 | for {
25 | time.Sleep(time.Second)
26 | pr, err := c.Read(go645.NewAddress("3a2107000481", go645.LittleEndian), 0x00_01_00_00)
27 | if err != nil {
28 | log.Print(err.Error())
29 | } else {
30 | println(pr.GetValue())
31 | }
32 |
33 | }
34 | ```
35 |
--------------------------------------------------------------------------------
/rtuClient.go:
--------------------------------------------------------------------------------
1 | package go645
2 |
3 | import (
4 | "bytes"
5 | "errors"
6 | "io"
7 | "log"
8 | "time"
9 | )
10 |
11 | var _ ClientProvider = (*RTUClientProvider)(nil)
12 |
13 | type RTUClientProvider struct {
14 | serialPort
15 | logger
16 | PrefixHandler
17 | }
18 |
19 | func (sf *RTUClientProvider) setPrefixHandler(handler PrefixHandler) {
20 | sf.PrefixHandler = handler
21 | }
22 |
23 | //SendAndRead 发送数据并读取返回值
24 | func (sf *RTUClientProvider) SendAndRead(p *Protocol) (aduResponse []byte, err error) {
25 | bf := bytes.NewBuffer(make([]byte, 0))
26 | err = sf.EncodePrefix(bf)
27 | if err != nil {
28 | return nil, err
29 | }
30 |
31 | err = p.Encode(bf)
32 | if err != nil {
33 | return nil, err
34 | }
35 | return sf.SendRawFrameAndRead(bf.Bytes())
36 | }
37 | func (sf *RTUClientProvider) Send(p *Protocol) (err error) {
38 | bf := bytes.NewBuffer(make([]byte, 0))
39 | err = sf.EncodePrefix(bf)
40 | if err != nil {
41 | return err
42 | }
43 | err = p.Encode(bf)
44 | if err != nil {
45 | return err
46 | }
47 | return sf.SendRawFrame(bf.Bytes())
48 | }
49 |
50 | //ReadRawFrame 读取返回数据
51 | func (sf *RTUClientProvider) ReadRawFrame() (aduResponse []byte, err error) {
52 | fe, err := sf.DecodePrefix(sf.port)
53 | if err != nil {
54 | log.Printf(err.Error())
55 | return nil, err
56 | }
57 | head := make([]byte, 10)
58 | size, err := io.ReadAtLeast(sf.port, head, 10)
59 | if err != nil || size != 10 {
60 | return nil, err
61 | }
62 | //数据域+2
63 | expLen := head[9] + 2
64 | playLoad := make([]byte, expLen)
65 | if _, err := io.ReadAtLeast(sf.port, playLoad, int(expLen)); err != nil {
66 | return nil, err
67 | }
68 | //拆包器重新实现
69 | content := append(head, playLoad...)
70 |
71 | sf.Debugf("rec <==[% x]", append(fe, content...))
72 | var cs uint8
73 | for _, v := range content[:len(content)-2] {
74 | cs += v
75 | }
76 | if cs != content[len(content)-2] {
77 | return content, errors.New("cs errors")
78 | }
79 | return content, nil
80 | }
81 | func (sf *RTUClientProvider) SendRawFrameAndRead(aduRequest []byte) (aduResponse []byte, err error) {
82 | sf.mu.Lock()
83 | defer sf.mu.Unlock()
84 | err = sf.serialPort.port.Flush()
85 | if err != nil {
86 | return nil, err
87 | }
88 | if err = sf.connect(); err != nil {
89 | return
90 | }
91 | err = sf.SendRawFrame(aduRequest)
92 | if err != nil {
93 | log.Printf(err.Error())
94 | _ = sf.close()
95 | return
96 | }
97 | return sf.ReadRawFrame()
98 | }
99 | func (sf *RTUClientProvider) SendRawFrame(aduRequest []byte) (err error) {
100 | if err = sf.connect(); err != nil {
101 | return
102 | }
103 | // Send the request
104 | sf.Debugf("sending ==> [% x]", aduRequest)
105 | //发送数据
106 | _, err = sf.port.Write(aduRequest)
107 | return err
108 | }
109 |
110 | // NewRTUClientProvider allocates and initializes a RTUClientProvider.
111 | // it will use default /dev/ttyS0 19200 8 1 N and timeout 1000
112 | func NewRTUClientProvider(opts ...ClientProviderOption) *RTUClientProvider {
113 | p := &RTUClientProvider{
114 | logger: newLogger("645RTUMaster => "),
115 | PrefixHandler: &DefaultPrefix{},
116 | }
117 | for _, opt := range opts {
118 | opt(p)
119 | }
120 | return p
121 | }
122 |
123 | // calculateDelay roughly calculates time needed for the next frame.
124 | // See MODBUS over Serial Line - Specification and Implementation Guide (page 13).
125 | func (sf *RTUClientProvider) calculateDelay(chars int) time.Duration {
126 | var characterDelay, frameDelay int // us
127 |
128 | if sf.Baud <= 0 || sf.Baud > 19200 {
129 | characterDelay = 750
130 | frameDelay = 1750
131 | } else {
132 | characterDelay = 15000000 / sf.Baud
133 | frameDelay = 35000000 / sf.Baud
134 | }
135 | return time.Duration(characterDelay*chars+frameDelay) * time.Microsecond
136 | }
137 |
--------------------------------------------------------------------------------
/serial.go:
--------------------------------------------------------------------------------
1 | package go645
2 |
3 | import (
4 | "sync"
5 | "time"
6 |
7 | "github.com/tarm/serial"
8 | )
9 |
10 | // SerialDefaultTimeout Serial Default timeout
11 | const SerialDefaultTimeout = 2 * time.Second
12 |
13 | // serialPort has configuration and I/O controller.
14 | type serialPort struct {
15 | // Serial port configuration.
16 | serial.Config
17 | mu sync.Mutex
18 | port *serial.Port
19 | }
20 |
21 | // Connect try to connect the remote server
22 | func (sf *serialPort) Connect() (err error) {
23 | sf.mu.Lock()
24 | err = sf.connect()
25 | sf.mu.Unlock()
26 | return
27 | }
28 |
29 | // Caller must hold the mutex before calling this method.
30 | func (sf *serialPort) connect() error {
31 | if sf.port == nil {
32 | port, err := serial.OpenPort(&sf.Config)
33 | if err != nil {
34 | return err
35 | }
36 | sf.port = port
37 | }
38 | return nil
39 | }
40 |
41 | // IsConnected returns a bool signifying whether the client is connected or not.
42 | func (sf *serialPort) IsConnected() (b bool) {
43 | sf.mu.Lock()
44 | b = sf.port != nil
45 | sf.mu.Unlock()
46 | return b
47 | }
48 |
49 | // setSerialConfig set serial config
50 | func (sf *serialPort) setSerialConfig(config serial.Config) {
51 | sf.Config = config
52 | }
53 |
54 | func (sf *serialPort) setTCPTimeout(time.Duration) {}
55 |
56 | func (sf *serialPort) close() (err error) {
57 | if sf.port != nil {
58 | err = sf.port.Close()
59 | sf.port = nil
60 | }
61 | return err
62 | }
63 |
64 | // Close close current connection.
65 | func (sf *serialPort) Close() (err error) {
66 | sf.mu.Lock()
67 | err = sf.close()
68 | sf.mu.Unlock()
69 | return
70 | }
71 |
--------------------------------------------------------------------------------
/value.go:
--------------------------------------------------------------------------------
1 | package go645
2 |
3 | type Value struct {
4 | value float64
5 | }
6 |
--------------------------------------------------------------------------------