├── go.sum
├── ab
└── df1.go
├── fuji
└── spb.go
├── delta
└── delta.go
├── fatek
└── program.go
├── keyence
└── melsec.go
├── siemens
├── mpi.go
├── fetchwrite.go
├── ppi.go
├── handshake.go
├── manifest.go
├── s7.go
├── addr.go
├── simatic.go
└── s7package.go
├── mitsubishi
├── a1c.go
├── qna2c.go
├── qna4c.go
├── qna4e.go
├── utils.go
├── manifest.go
├── address.go
├── a1e.go
├── qna3e_binary.go
├── fx_program.go
├── fx_special.go
├── a3c1.go
└── qna3e.go
├── panasonic
├── melsec.go
└── newtocol.go
├── go.mod
├── protocol
├── address.go
├── protocol.go
├── manifest.go
├── socket.go
└── messenger.go
├── .gitignore
├── helper
├── check.go
├── hex.go
└── bytes.go
├── modbus
├── manifest.go
├── crc16.go
├── const.go
├── addr.go
├── tcp.go
├── rtu.go
└── parallel-tcp.go
├── LICENSE
├── README.md
└── omron
├── hostlink.go
├── addr.go
├── udp.go
├── fins.go
└── packet.go
/go.sum:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/ab/df1.go:
--------------------------------------------------------------------------------
1 | package ab
2 |
--------------------------------------------------------------------------------
/fuji/spb.go:
--------------------------------------------------------------------------------
1 | package fuji
2 |
--------------------------------------------------------------------------------
/delta/delta.go:
--------------------------------------------------------------------------------
1 | package delta
2 |
--------------------------------------------------------------------------------
/fatek/program.go:
--------------------------------------------------------------------------------
1 | package fatek
2 |
--------------------------------------------------------------------------------
/keyence/melsec.go:
--------------------------------------------------------------------------------
1 | package keyence
2 |
--------------------------------------------------------------------------------
/siemens/mpi.go:
--------------------------------------------------------------------------------
1 | package siemens
2 |
--------------------------------------------------------------------------------
/mitsubishi/a1c.go:
--------------------------------------------------------------------------------
1 | package mitsubishi
2 |
--------------------------------------------------------------------------------
/mitsubishi/qna2c.go:
--------------------------------------------------------------------------------
1 | package mitsubishi
2 |
--------------------------------------------------------------------------------
/mitsubishi/qna4c.go:
--------------------------------------------------------------------------------
1 | package mitsubishi
2 |
--------------------------------------------------------------------------------
/mitsubishi/qna4e.go:
--------------------------------------------------------------------------------
1 | package mitsubishi
2 |
--------------------------------------------------------------------------------
/mitsubishi/utils.go:
--------------------------------------------------------------------------------
1 | package mitsubishi
2 |
--------------------------------------------------------------------------------
/panasonic/melsec.go:
--------------------------------------------------------------------------------
1 | package panasonic
2 |
--------------------------------------------------------------------------------
/panasonic/newtocol.go:
--------------------------------------------------------------------------------
1 | package panasonic
2 |
--------------------------------------------------------------------------------
/siemens/fetchwrite.go:
--------------------------------------------------------------------------------
1 | package siemens
2 |
--------------------------------------------------------------------------------
/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/zgwit/go-plc
2 |
3 | go 1.14
4 |
--------------------------------------------------------------------------------
/protocol/address.go:
--------------------------------------------------------------------------------
1 | package protocol
2 |
3 | type AddressResolver func(area string, addr string) (Addr, error)
4 |
5 | type Addr interface {
6 | String() string
7 | Diff(from Addr) (int, bool)
8 | }
9 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Binaries for programs and plugins
2 | *.exe
3 | *.exe~
4 | *.dll
5 | *.so
6 | *.dylib
7 |
8 | # Test binary, built with `go test -c`
9 | *.test
10 |
11 | # Output of the go coverage tool, specifically when used with LiteIDE
12 | *.out
13 |
14 | # Dependency directories (remove the comment below to include it)
15 | # vendor/
16 | /.idea
17 |
--------------------------------------------------------------------------------
/helper/check.go:
--------------------------------------------------------------------------------
1 | package helper
2 |
3 | // Sum 和
4 | func Sum(buf []byte) byte {
5 | var sum byte = 0
6 | l := len(buf)
7 | for i := 0; i < l; i++ {
8 | sum += buf[i]
9 | }
10 | return sum
11 | }
12 |
13 | // Xor 异或
14 | func Xor(buf []byte) byte {
15 | var xor byte = buf[0]
16 | l := len(buf)
17 | for i := 1; i < l; i++ {
18 | xor ^= buf[i]
19 | }
20 | return xor
21 | }
22 |
--------------------------------------------------------------------------------
/siemens/ppi.go:
--------------------------------------------------------------------------------
1 | package siemens
2 |
3 | import (
4 | "github.com/zgwit/go-plc/protocol"
5 | )
6 |
7 | // PPI 协议
8 | type PPI struct {
9 | link protocol.Messenger
10 | }
11 |
12 | // Read 读到数据
13 | func (t *PPI) Read(station int, address protocol.Addr, length int) ([]byte, error) {
14 | return nil, nil
15 | }
16 |
17 | // Write 写入数据
18 | func (t *PPI) Write(station int, address protocol.Addr, values []byte) error {
19 | return nil
20 | }
21 |
--------------------------------------------------------------------------------
/mitsubishi/manifest.go:
--------------------------------------------------------------------------------
1 | package mitsubishi
2 |
3 | import (
4 | "github.com/zgwit/go-plc/protocol"
5 | "io"
6 | )
7 |
8 | var ManifestFxProgram = protocol.Manifest{
9 | Name: "Fx-Program",
10 | Version: "1.0",
11 | Label: "Fx-Program",
12 | Resolver: ParseFxProgramAddress,
13 | Factory: func(link io.ReadWriter, opts string) protocol.Protocol {
14 | return &FxProgram{
15 | link: protocol.Messenger{Conn: link},
16 | }
17 | },
18 | }
19 |
--------------------------------------------------------------------------------
/protocol/protocol.go:
--------------------------------------------------------------------------------
1 | package protocol
2 |
3 | import (
4 | "io"
5 | "time"
6 | )
7 |
8 | type CreateFactory func(conn io.ReadWriter, opts string) Protocol
9 |
10 | // Protocol 协议接口
11 | type Protocol interface {
12 |
13 | //Read 读数据
14 | Read(station int, add Addr, size int) ([]byte, error)
15 |
16 | //Write 写数据
17 | Write(station int, add Addr, data []byte) error
18 |
19 | //Attach(Conn io.ReadWriter) error
20 | }
21 |
22 | type transport interface {
23 | Send(request []byte, timeout time.Duration) (response []byte, err error)
24 | }
25 |
--------------------------------------------------------------------------------
/protocol/manifest.go:
--------------------------------------------------------------------------------
1 | package protocol
2 |
3 | type Parser func(code string, addr string) (Addr, error)
4 |
5 | type Area struct {
6 | Name string `json:"name"`
7 | Label string `json:"label"`
8 | //regex ??
9 | }
10 |
11 | type Manifest struct {
12 | Name string `json:"name"`
13 | Label string `json:"label"`
14 | Version string `json:"version"`
15 | Areas []Area `json:"areas"`
16 | Station bool `json:"station"`
17 | Resolver AddressResolver `json:"-"`
18 | Factory CreateFactory `json:"-"`
19 | }
20 |
--------------------------------------------------------------------------------
/modbus/manifest.go:
--------------------------------------------------------------------------------
1 | package modbus
2 |
3 | import "github.com/zgwit/go-plc/protocol"
4 |
5 | var Areas = []protocol.Area{
6 | {"C", "01 线圈"},
7 | {"D", "02 离散输入"},
8 | {"H", "03 保持寄存器"},
9 | {"I", "04 输入寄存器"},
10 | }
11 |
12 | var ManifestRTU = protocol.Manifest{
13 | Name: "ModbusRTU",
14 | Version: "1.0",
15 | Label: "Modbus RTU",
16 | Areas: Areas,
17 | Resolver: ResolveAddress,
18 | Factory: NewRTU,
19 | }
20 |
21 | var ManifestTCP = protocol.Manifest{
22 | Name: "ModbusTCP",
23 | Version: "1.1",
24 | Label: "Modbus TCP",
25 | Areas: Areas,
26 | Resolver: ResolveAddress,
27 | Factory: NewTCP,
28 | }
29 |
--------------------------------------------------------------------------------
/protocol/socket.go:
--------------------------------------------------------------------------------
1 | package protocol
2 |
3 | import (
4 | "net"
5 | "time"
6 | )
7 |
8 | type Socket struct {
9 | Conn net.Conn
10 | Timeout time.Duration
11 | }
12 |
13 | func (s *Socket) Read(b []byte) (int, error) {
14 | err := s.Conn.SetReadDeadline(time.Now().Add(s.Timeout))
15 | if err != nil {
16 | return 0, err
17 | }
18 | return s.Conn.Read(b)
19 | }
20 |
21 | func (s *Socket) Write(b []byte) (int, error) {
22 | err := s.Conn.SetWriteDeadline(time.Now().Add(s.Timeout))
23 | if err != nil {
24 | return 0, err
25 | }
26 | return s.Conn.Read(b)
27 | }
28 |
29 | func (s *Socket) Close() error {
30 | return s.Conn.Close()
31 | }
32 |
--------------------------------------------------------------------------------
/protocol/messenger.go:
--------------------------------------------------------------------------------
1 | package protocol
2 |
3 | import (
4 | "io"
5 | "sync"
6 | )
7 |
8 | type Messenger struct {
9 | mu sync.Mutex
10 | Conn io.ReadWriter
11 | }
12 |
13 | func (m *Messenger) Ask(request []byte, response []byte) (int, error) {
14 | m.mu.Lock()
15 | defer m.mu.Unlock()
16 |
17 | _, err := m.Conn.Write(request)
18 | if err != nil {
19 | return 0, err
20 | }
21 | return m.Conn.Read(response)
22 | }
23 |
24 | func (m *Messenger) AskAtLeast(request []byte, response []byte, min int) (int, error) {
25 | m.mu.Lock()
26 | defer m.mu.Unlock()
27 |
28 | _, err := m.Conn.Write(request)
29 | if err != nil {
30 | return 0, err
31 | }
32 | return io.ReadAtLeast(m.Conn, response, min)
33 | }
34 |
--------------------------------------------------------------------------------
/modbus/crc16.go:
--------------------------------------------------------------------------------
1 | package modbus
2 |
3 | // Cyclical Redundancy Checking.
4 | var (
5 | crtTable []uint16
6 | )
7 |
8 | // CRC16 Calculate Cyclical Redundancy Checking.
9 | func CRC16(bs []byte) uint16 {
10 | val := uint16(0xFFFF)
11 | for _, v := range bs {
12 | val = (val >> 8) ^ crtTable[(val^uint16(v))&0x00FF]
13 | }
14 | return val
15 | }
16 |
17 | // init 初始化表.
18 | func init() {
19 | crcPoly16 := uint16(0xa001)
20 | crtTable = make([]uint16, 256)
21 |
22 | for i := uint16(0); i < 256; i++ {
23 | crc := uint16(0)
24 | b := i
25 |
26 | for j := uint16(0); j < 8; j++ {
27 | if ((crc ^ b) & 0x0001) > 0 {
28 | crc = (crc >> 1) ^ crcPoly16
29 | } else {
30 | crc >>= 1
31 | }
32 | b >>= 1
33 | }
34 | crtTable[i] = crc
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 zGwit
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 |
--------------------------------------------------------------------------------
/helper/hex.go:
--------------------------------------------------------------------------------
1 | package helper
2 |
3 | import "encoding/hex"
4 |
5 | var hexNumbers = []byte("0123456789ABCDEF")
6 |
7 | // ByteToHex 编码
8 | func ByteToHex(value byte) []byte {
9 | buf := make([]byte, 2)
10 | buf[0] = hexNumbers[value>>4]
11 | buf[1] = hexNumbers[value&0x0F]
12 | return buf
13 | }
14 |
15 | // WriteByteHex 编码
16 | func WriteByteHex(buf []byte, value uint8) {
17 | buf[0] = hexNumbers[value>>4]
18 | buf[1] = hexNumbers[value&0x0F]
19 | }
20 |
21 | // WriteUint8Hex 编码
22 | func WriteUint8Hex(buf []byte, value uint8) {
23 | buf[0] = hexNumbers[value>>4]
24 | buf[1] = hexNumbers[value&0x0F]
25 | }
26 |
27 | // WriteUint16Hex 编码
28 | func WriteUint16Hex(buf []byte, value uint16) {
29 | h, l := value>>8, value&0xF
30 | buf[0] = hexNumbers[h>>4]
31 | buf[1] = hexNumbers[h&0x0F]
32 | buf[2] = hexNumbers[l>>4]
33 | buf[3] = hexNumbers[l&0x0F]
34 |
35 | }
36 |
37 | // ToHex 编码
38 | func ToHex(values []byte) []byte {
39 | length := len(values)
40 | buf := make([]byte, length<<1) //length * 2
41 | for i := 0; i < length; i++ {
42 | value := values[i]
43 | j := i << 1 //i * 2
44 | buf[j] = hexNumbers[value>>4]
45 | buf[j+1] = hexNumbers[value&0x0F]
46 | }
47 | return buf
48 | }
49 |
50 | // FromHex 解码
51 | func FromHex(values []byte) []byte {
52 | buf := make([]byte, len(values)>>1) //length / 2
53 | _, _ = hex.Decode(buf, values)
54 | return buf
55 | }
56 |
--------------------------------------------------------------------------------
/modbus/const.go:
--------------------------------------------------------------------------------
1 | package modbus
2 |
3 | // Function Code
4 | const (
5 | // Bits access
6 | FuncCodeReadDiscreteInputs = 2
7 | FuncCodeReadCoils = 1
8 | FuncCodeWriteSingleCoil = 5
9 | FuncCodeWriteMultipleCoils = 15
10 |
11 | // 16-bit access
12 | FuncCodeReadInputRegisters = 4
13 | FuncCodeReadHoldingRegisters = 3
14 | FuncCodeWriteSingleRegister = 6
15 | FuncCodeWriteMultipleRegisters = 16
16 | FuncCodeReadWriteMultipleRegisters = 23
17 | FuncCodeMaskWriteRegister = 22
18 | FuncCodeReadFIFOQueue = 24
19 | FuncCodeOtherReportSlaveID = 17
20 | // FuncCodeDiagReadException = 7
21 | // FuncCodeDiagDiagnostic = 8
22 | // FuncCodeDiagGetComEventCnt = 11
23 | // FuncCodeDiagGetComEventLog = 12
24 |
25 | )
26 |
27 | // Exception Code
28 | const (
29 | ExceptionCodeIllegalFunction = 1
30 | ExceptionCodeIllegalDataAddress = 2
31 | ExceptionCodeIllegalDataValue = 3
32 | ExceptionCodeServerDeviceFailure = 4
33 | ExceptionCodeAcknowledge = 5
34 | ExceptionCodeServerDeviceBusy = 6
35 | ExceptionCodeNegativeAcknowledge = 7
36 | ExceptionCodeMemoryParityError = 8
37 | ExceptionCodeGatewayPathUnavailable = 10
38 | ExceptionCodeGatewayTargetDeviceFailedToRespond = 11
39 | )
40 |
--------------------------------------------------------------------------------
/modbus/addr.go:
--------------------------------------------------------------------------------
1 | package modbus
2 |
3 | import (
4 | "github.com/zgwit/go-plc/protocol"
5 | "strconv"
6 | )
7 |
8 | type Address struct {
9 | //Slave uint8 `json:"slave"`
10 | Code uint8 `json:"code"`
11 | Offset uint16 `json:"offset"`
12 | }
13 |
14 | func (a *Address) String() string {
15 | code := ""
16 | switch a.Code {
17 | case 1:
18 | code = "C"
19 | case 2:
20 | code = "D"
21 | case 3:
22 | code = "H"
23 | case 4:
24 | code = "I"
25 | }
26 | return code + strconv.Itoa(int(a.Offset))
27 | }
28 |
29 | func (a *Address) Diff(from protocol.Addr) (int, bool) {
30 | base := from.(*Address)
31 | if base.Code != a.Code {
32 | return 0, false
33 | }
34 | cursor := int(a.Offset - base.Offset)
35 | //Modbus是以双字
36 | if a.Code == FuncCodeReadHoldingRegisters || a.Code == FuncCodeReadInputRegisters {
37 | cursor *= 2
38 | }
39 |
40 | if cursor < 0 {
41 | return 0, false
42 | }
43 | return cursor, true
44 | }
45 |
46 | func ResolveAddress(name string, addr string) (protocol.Addr, error) {
47 | var code uint8 = 1
48 | switch name {
49 | case "C":
50 | code = 1
51 | case "D", "DI":
52 | code = 2
53 | case "H":
54 | code = 3
55 | case "I":
56 | code = 4
57 | }
58 | offset, err := strconv.ParseUint(addr, 10, 16)
59 | if err != nil {
60 | return nil, err
61 | }
62 | //offset, _ := strconv.Atoi(ss[2])
63 |
64 | return &Address{
65 | Code: code,
66 | Offset: uint16(offset),
67 | }, nil
68 | }
69 |
--------------------------------------------------------------------------------
/siemens/handshake.go:
--------------------------------------------------------------------------------
1 | package siemens
2 |
3 | import (
4 | "bytes"
5 | "github.com/zgwit/go-plc/helper"
6 | )
7 |
8 | const handshake1_200_smart = "03 00 00 16 11 E0 00 00 00 01 00 C1 02 10 00 C2 02 03 00 C0 01 0A"
9 | const handshake1_200 = "03 00 00 16 11 E0 00 00 00 01 00 C1 02 4D 57 C2 02 4D 57 C0 01 09"
10 | const handshake1_300 = "03 00 00 16 11 E0 00 00 00 01 00 C0 01 0A C1 02 01 02 C2 02 01 02"
11 | const handshake1_400 = "03 00 00 16 11 E0 00 00 00 01 00 C0 01 0A C1 02 01 00 C2 02 01 03"
12 | const handshake1_1200 = "03 00 00 16 11 E0 00 00 00 01 00 C0 01 0A C1 02 01 02 C2 02 01 00"
13 | const handshake1_1500 = "03 00 00 16 11 E0 00 00 00 01 00 C0 01 0A C1 02 01 02 C2 02 01 00"
14 |
15 | const handshake2_200_smart = "03 00 00 19 02 F0 80 32 01 00 00 CC C1 00 08 00 00 F0 00 00 01 00 01 03 C0"
16 | const handshake2_200 = "03 00 00 19 02 F0 80 32 01 00 00 00 00 00 08 00 00 F0 00 00 01 00 01 03 C0"
17 | const handshake2_300 = "03 00 00 19 02 F0 80 32 01 00 00 04 00 00 08 00 00 F0 00 00 01 00 01 01 E0"
18 | const handshake2_400 = "03 00 00 19 02 F0 80 32 01 00 00 04 00 00 08 00 00 F0 00 00 01 00 01 01 E0"
19 | const handshake2_1200 = "03 00 00 19 02 F0 80 32 01 00 00 04 00 00 08 00 00 F0 00 00 01 00 01 01 E0"
20 | const handshake2_1500 = "03 00 00 19 02 F0 80 32 01 00 00 04 00 00 08 00 00 F0 00 00 01 00 01 01 E0"
21 |
22 | func parseHex(str string) []byte {
23 | //删除空格
24 | buf := bytes.ReplaceAll([]byte(str), []byte{' '}, []byte{})
25 | return helper.FromHex(buf)
26 | //str = strings.ReplaceAll(str, " ", "")
27 | //buf := helper.FromHex([]byte(str))
28 | //return buf
29 | }
30 |
31 | func setRackSlot(handshake1 []byte, rack, slot uint8) {
32 | handshake1[21] = rack*0x20 + slot
33 | }
34 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
GO PLC
3 |
4 |
5 | Native Go implementation of PLC protocols for iot-master and others.
6 |
7 | [github.com/zgwit/iot-master](https://github.com/zgwit/iot-master)
8 |
9 | ## Attention
10 |
11 | This repo is not available for real project!!!
12 |
13 | Join us pls if you want.
14 |
15 | ## Quickstart
16 |
17 | ```sh
18 | # make sure you have go1.17 or higher
19 |
20 | # install library
21 | go get -u github.com/zgwit/go-plc
22 |
23 | ```
24 |
25 | ## Plan
26 |
27 | | NAME | COMPLETE | TESTED | COMMENT |
28 | |----------------------|----------|--------|---------|
29 | | Modbus RTU | ✔ | ✔ | |
30 | | Modbus TCP | ✔ | ✔ | |
31 | | Modbus ASCII | ❌ | | Won't |
32 | | Omron Fins | ✔ | | |
33 | | Omron Hostlink | ✔ | | |
34 | | Siemens PPI | ❌ | | |
35 | | Siemens FetchWrite | ❌ | | |
36 | | Siemens S7 | ✔ | ✔ | |
37 | | Mitsubishi FxProgram | ✔ | | |
38 | | Mitsubishi FxSpecial | ❌ | | |
39 | | Mitsubishi A1C | ❌ | | |
40 | | Mitsubishi A1E | ❌ | | |
41 | | Mitsubishi Q2C | ❌ | | |
42 | | Mitsubishi Q3E | ❌ | | |
43 | | Mitsubishi Q4C | ❌ | | |
44 | | Mitsubishi Q4E | ❌ | | |
45 |
46 | ## Authors
47 |
48 | The [zgwit team](https://github.com/zgwit/go-plc/graphs/contributors).
49 |
50 | If you need to get in touch with us directly you may find us on [zgwit.com](https://zgwit.com)
51 | but try to create an issue first.
52 |
53 | ## How to join
54 |
55 | Just Fork & PR!
56 |
57 | ## License
58 |
59 | [MIT](https://github.com/zgwit/go-plc/blob/master/LICENSE)
--------------------------------------------------------------------------------
/omron/hostlink.go:
--------------------------------------------------------------------------------
1 | package omron
2 |
3 | import (
4 | "errors"
5 | "fmt"
6 | helper2 "github.com/zgwit/go-plc/helper"
7 | "github.com/zgwit/go-plc/protocol"
8 | "io"
9 | )
10 |
11 | type FinsHostLink struct {
12 | frame UdpFrame
13 | link protocol.Messenger
14 | buf []byte
15 | }
16 |
17 | func NewHostLink(link io.ReadWriter, opts string) protocol.Protocol {
18 | fins := &Fins{
19 | link: protocol.Messenger{Conn: link},
20 | buf: make([]byte, 256)}
21 | return fins
22 | }
23 |
24 | func (f *FinsHostLink) execute(cmd []byte) ([]byte, error) {
25 | //发送请求
26 | l, err := f.link.Ask(cmd, f.buf)
27 | if err != nil {
28 | return nil, err
29 | }
30 |
31 | //解析数据
32 | if l < 23 {
33 | return nil, errors.New("长度不够")
34 | }
35 |
36 | //@ [单元号] [F A] [0 0] [4 0 ICF][0 0 DA2][0 0 SA2][ SID ]
37 | //[命令码 4字节] [状态码 4字节] [ ...data... ]
38 | //[FCS][* CR]
39 | recv := helper2.FromHex(f.buf[15 : l-4])
40 |
41 | //记录响应的SID
42 | //t.frame.SID = FromHex(payload[13:15])[0]
43 |
44 | return recv, nil
45 | }
46 |
47 | func (f *FinsHostLink) Read(station int, address protocol.Addr, size int) ([]byte, error) {
48 |
49 | //构建读命令
50 | buf, e := buildReadCommand(address, size)
51 | if e != nil {
52 | return nil, e
53 | }
54 |
55 | //打包命令
56 | cmd := packAsciiCommand(&f.frame, buf)
57 |
58 | //发送请求
59 | recv, err := f.execute(cmd)
60 | if err != nil {
61 | return nil, err
62 | }
63 |
64 | //[命令码 1 1] [结束码 0 0] , data
65 | code := helper2.ParseUint16(recv[2:])
66 | if code != 0 {
67 | return nil, fmt.Errorf("错误码: %d", code)
68 | }
69 |
70 | return recv[4:], nil
71 | }
72 |
73 | func (f *FinsHostLink) Write(station int, address protocol.Addr, values []byte) error {
74 | //构建写命令
75 | buf, e := buildWriteCommand(address, values)
76 | if e != nil {
77 | return e
78 | }
79 |
80 | //打包命令
81 | cmd := packAsciiCommand(&f.frame, buf)
82 |
83 | //发送请求
84 | recv, err := f.execute(cmd)
85 | if err != nil {
86 | return err
87 | }
88 |
89 | //[命令码 1 1] [结束码 0 0]
90 | code := helper2.ParseUint16(recv[2:])
91 | if code != 0 {
92 | return fmt.Errorf("错误码: %d", code)
93 | }
94 |
95 | return nil
96 | }
97 |
--------------------------------------------------------------------------------
/mitsubishi/address.go:
--------------------------------------------------------------------------------
1 | package mitsubishi
2 |
3 | import (
4 | "errors"
5 | "strconv"
6 | "strings"
7 | )
8 |
9 | // Command 命令
10 | type Command struct {
11 | Code byte
12 | IsBit bool
13 | Base int
14 | //Code uint64
15 | }
16 |
17 | type Address struct {
18 | Command
19 | Name string
20 | Addr uint64
21 | }
22 |
23 | var commands = map[string]Command{
24 | "X": {0x9C, true, 16}, //X输入继电器
25 | "Y": {0x9D, true, 16}, //Y输出继电器
26 | "M": {0x90, true, 10}, //M中间继电器
27 | "D": {0xA8, false, 10}, //D数据寄存器
28 | "W": {0xB4, false, 16}, //D数据寄存器
29 | "L": {0x92, true, 10}, //L锁存继电器
30 | "F": {0x93, true, 10}, //F报警器
31 | "V": {0x94, false, 10}, //V边沿继电器
32 | "B": {0xA0, true, 16}, //B链接继电器
33 | "R": {0xAF, false, 10}, //R文件寄存器
34 | "S": {0x98, true, 10}, //S步进继电器
35 | "Z": {0xCC, false, 10}, //变址寄存器
36 | "ZR": {0xB0, false, 16}, //文件寄存器ZR区
37 | "TC": {0xC0, true, 10}, //定时器的线圈
38 | "TS": {0xC1, true, 10}, //定时器的触点
39 | "TN": {0xC2, false, 10}, //定时器的当前值
40 | "CC": {0xC3, true, 16}, //计数器的线圈
41 | "CS": {0xC4, true, 10}, //计数器的触点
42 | "CN": {0xC5, false, 10}, //计数器的当前值
43 | "SC": {0xC6, true, 10}, //累计定时器的线圈
44 | "SS": {0xC7, false, 10}, //累计定时器的触点
45 | "SN": {0xC8, false, 10}, //累计定时器的当前值
46 | }
47 |
48 | var a1eCommands = map[string]Command{
49 | "X": {'X', true, 8}, //X输入继电器
50 | "Y": {'Y', true, 8}, //Y输出继电器
51 | "M": {'M', true, 10}, //M中间继电器
52 | "D": {'D', false, 10}, //D数据寄存器
53 | "R": {'R', false, 10}, //R文件寄存器
54 | "S": {'S', true, 10}, //S步进继电器
55 | }
56 |
57 | func ParseAddress(address string) (addr Address, err error) {
58 |
59 | //先检查两字节
60 | k := strings.ToUpper(address[:2])
61 | if cmd, ok := commands[k]; ok {
62 | addr.Command = cmd
63 | addr.Name = k
64 | addr.Addr, err = strconv.ParseUint(address[2:], cmd.Base, 16)
65 | return
66 | }
67 |
68 | //检测单字节
69 | k = strings.ToUpper(address[:1])
70 | if cmd, ok := commands[k]; ok {
71 | addr.Command = cmd
72 | addr.Name = k + "*"
73 | addr.Addr, err = strconv.ParseUint(address[1:], cmd.Base, 16)
74 | return
75 | }
76 |
77 | err = errors.New("未知消息")
78 | return
79 | }
80 |
81 | func ParseA1EAddress(address string) (addr Address, err error) {
82 | //检测单字节
83 | k := strings.ToUpper(address[:1])
84 | if cmd, ok := a1eCommands[k]; ok {
85 | addr.Command = cmd
86 | addr.Name = k + "*"
87 | addr.Addr, err = strconv.ParseUint(address[1:], cmd.Base, 16)
88 | return
89 | }
90 |
91 | err = errors.New("未知消息")
92 | return
93 | }
94 |
--------------------------------------------------------------------------------
/omron/addr.go:
--------------------------------------------------------------------------------
1 | package omron
2 |
3 | import (
4 | "fmt"
5 | "github.com/zgwit/go-plc/protocol"
6 | "regexp"
7 | "strconv"
8 | )
9 |
10 | type Command struct {
11 | BitCode byte
12 | WordCode byte
13 | }
14 |
15 | var commands = map[string]Command{
16 | //DM Area
17 | "D": {0x02, 0x82},
18 | //CIO Area
19 | "C": {0x30, 0xB0},
20 | //Work Area
21 | "W": {0x31, 0xB1},
22 | //Holding Bits Area
23 | "H": {0x32, 0xB2},
24 | //Auxiliary Bits Area
25 | "A": {0x33, 0xB3},
26 | }
27 |
28 | type Address struct {
29 | Code byte
30 | Offset uint16
31 | Bits uint8
32 | IsBit bool
33 | }
34 |
35 | func (a *Address) String() string {
36 | code := ""
37 | switch a.Code {
38 | case 0x02, 0x82:
39 | code = "D"
40 | case 0x30, 0xB0:
41 | code = "C"
42 | case 0x31, 0xB1:
43 | code = "W"
44 | case 0x32, 0xB2:
45 | code = "H"
46 | case 0x33, 0xB3:
47 | code = "A"
48 | }
49 | return code + strconv.Itoa(int(a.Offset))
50 | }
51 |
52 | func (a *Address) Diff(from protocol.Addr) (int, bool) {
53 | base := from.(*Address)
54 | if base.Code != a.Code {
55 | return 0, false
56 | }
57 | if base.IsBit {
58 | cursor := int(a.Offset)*16 + int(a.Bits) - int(base.Offset)*16 - int(base.Bits)
59 | if cursor < 0 {
60 | return 0, false
61 | }
62 | //TODO 此处应该明确数据格式
63 | return cursor, true
64 | } else {
65 | cursor := int(a.Offset-base.Offset) * 2
66 | if cursor < 0 {
67 | return 0, false
68 | }
69 | return cursor, true
70 | }
71 | }
72 |
73 | var addrRegexp *regexp.Regexp
74 |
75 | func init() {
76 | addrRegexp = regexp.MustCompile(`^(D|C|W|H|A)(\d+)(.\d+)?$`)
77 | }
78 |
79 | func ResolveAddress(area string, addr string) (protocol.Addr, error) {
80 | ss := addrRegexp.FindStringSubmatch(addr)
81 | if ss == nil || len(ss) < 3 {
82 | return nil, fmt.Errorf("欧姆龙地址格式错误: %s", addr)
83 | }
84 | var code uint8 = 1
85 | switch area {
86 | case "D":
87 | code = 0x82
88 | case "C":
89 | code = 0xB0
90 | case "W":
91 | code = 0xB1
92 | case "H":
93 | code = 0xB2
94 | case "A":
95 | code = 0xB3
96 | }
97 | offset, _ := strconv.ParseUint(ss[2], 10, 16)
98 | //offset, _ := strconv.Atoi(ss[2])
99 | address := &Address{
100 | Code: code,
101 | Offset: uint16(offset),
102 | }
103 | if len(ss) == 4 {
104 | //TODO 判断bit,只能在 0~15之间
105 | bits, _ := strconv.ParseUint(ss[3][1:], 10, 8)
106 | address.Bits = uint8(bits)
107 | address.IsBit = true
108 | address.Code -= 0x80
109 | }
110 | return address, nil
111 | }
112 |
--------------------------------------------------------------------------------
/mitsubishi/a1e.go:
--------------------------------------------------------------------------------
1 | package mitsubishi
2 |
3 | import "io"
4 |
5 | // A1eAdapter A1E协议
6 | type A1eAdapter struct {
7 | PlcNumber byte
8 | link io.ReadWriter
9 | }
10 |
11 | func NewA1eAdapter() *A1eAdapter {
12 | a := A1eAdapter{}
13 | a.PlcNumber = 0xFF
14 | return &a
15 | }
16 |
17 | // Read 读取数据
18 | func (t *A1eAdapter) Read(address string, length int) ([]byte, error) {
19 |
20 | //解析地址
21 | addr, e := ParseA1EAddress(address)
22 | if e != nil {
23 | return nil, e
24 | }
25 |
26 | //副标题
27 | var subTitle byte = 0x01
28 | if addr.IsBit {
29 | subTitle = 0x00
30 | }
31 |
32 | //构建命令
33 | buf := make([]byte, 12)
34 | buf[0] = subTitle //副标题,0:bit 1:byte
35 | buf[1] = t.PlcNumber //PLC号
36 | buf[2] = 0x0A // CPU监视定时器(L)这里设置为0x00,0x0A,等待CPU返回的时间为10*250ms=2.5秒
37 | buf[3] = 0x00 // CPU监视定时器(H)
38 | buf[4] = byte(addr.Addr) // 起始软元件(开始读取的地址)
39 | buf[5] = byte(addr.Addr >> 8)
40 | buf[6] = byte(addr.Addr >> 16)
41 | buf[7] = byte(addr.Addr >> 24)
42 | buf[8] = 0x20 // 软元件代码(L)
43 | buf[9] = addr.Code // 软元件代码(H)
44 | buf[10] = byte(length) // 软元件点数
45 | buf[11] = 0x00
46 |
47 | //发送命令
48 |
49 | //接收响应
50 | recv := make([]byte, 2+length)
51 | //if _, err := t.link.Read(recv); err != nil {
52 | // return nil, err
53 | //}
54 |
55 | // 80/81 00 ....
56 | return recv[2:], nil
57 | }
58 |
59 | // Write 写入数据
60 | func (t *A1eAdapter) Write(address string, values []byte) error {
61 |
62 | length := len(values)
63 |
64 | //解析地址
65 | addr, e := ParseAddress(address)
66 | if e != nil {
67 | return e
68 | }
69 |
70 | //副标题
71 | var subTitle byte = 0x03
72 | if addr.IsBit {
73 | subTitle = 0x02
74 | }
75 |
76 | //构建命令
77 | buf := make([]byte, 12+length)
78 | buf[0] = subTitle //副标题,2:bit 3:byte
79 | buf[1] = t.PlcNumber //PLC号
80 | buf[2] = 0x0A // CPU监视定时器(L)这里设置为0x00,0x0A,等待CPU返回的时间为10*250ms=2.5秒
81 | buf[3] = 0x00 // CPU监视定时器(H)
82 | buf[4] = byte(addr.Addr) // 起始软元件(开始读取的地址)
83 | buf[5] = byte(addr.Addr >> 8)
84 | buf[6] = byte(addr.Addr >> 16)
85 | buf[7] = byte(addr.Addr >> 24)
86 | buf[8] = 0x20 // 软元件代码(L)
87 | buf[9] = addr.Code // 软元件代码(H)
88 | buf[10] = byte(length) // 软元件点数
89 | buf[11] = 0x00
90 |
91 | //附加数据
92 | copy(buf[12:], values)
93 |
94 | //发送命令
95 |
96 | //接收响应
97 | //recv := make([]byte, 2)
98 | //if _, err := t.link.Read(recv); err != nil {
99 | // return err
100 | //}
101 |
102 | // 82/83 00
103 | return nil
104 | }
105 |
--------------------------------------------------------------------------------
/omron/udp.go:
--------------------------------------------------------------------------------
1 | package omron
2 |
3 | import (
4 | "errors"
5 | "fmt"
6 | "github.com/zgwit/go-plc/helper"
7 | "github.com/zgwit/go-plc/protocol"
8 | "io"
9 | )
10 |
11 | type UdpFrame struct {
12 | // 信息控制字段,默认0x80
13 | ICF byte // 0x80
14 |
15 | // 系统使用的内部信息
16 | RSV byte // 0x00
17 |
18 | // 网络层信息,默认0x02,如果有八层消息,就设置为0x07
19 | GCT byte // 0x02
20 |
21 | // PLC的网络号地址,默认0x00
22 | DNA byte // 0x00
23 |
24 | // PLC的节点地址,这个值在配置了ip地址之后是默认赋值的,默认为Ip地址的最后一位
25 | DA1 byte // 0x13
26 |
27 | // PLC的单元号地址
28 | DA2 byte // 0x00
29 |
30 | // 上位机的网络号地址
31 | SNA byte // 0x00
32 |
33 | // 上位机的节点地址,假如你的电脑的Ip地址为192.168.0.13,那么这个值就是13
34 | SA1 byte
35 |
36 | // 上位机的单元号地址
37 | SA2 byte
38 |
39 | // 设备的标识号
40 | SID byte // 0x00
41 | }
42 |
43 | type FinsUdp struct {
44 | frame UdpFrame
45 | link protocol.Messenger
46 | buf []byte
47 | }
48 |
49 | func NewFinsUDP(link io.ReadWriter, opts string) protocol.Protocol {
50 | fins := &FinsUdp{
51 | link: protocol.Messenger{Conn: link},
52 | buf: make([]byte, 256)}
53 | return fins
54 | }
55 |
56 | func (f *FinsUdp) execute(cmd []byte) ([]byte, error) {
57 | //发送请求
58 | l, err := f.link.Ask(cmd, f.buf)
59 | if err != nil {
60 | return nil, err
61 | }
62 |
63 | //解析数据
64 | if l < 10 {
65 | return nil, errors.New("长度不够")
66 | }
67 |
68 | //[UDP 10字节]
69 |
70 | //记录响应的SID
71 | f.frame.SID = f.buf[9]
72 |
73 | return f.buf[10:l], nil
74 | }
75 |
76 | func (f *FinsUdp) Read(station int, address protocol.Addr, size int) ([]byte, error) {
77 |
78 | //构建读命令
79 | buf, e := buildReadCommand(address, size)
80 | if e != nil {
81 | return nil, e
82 | }
83 |
84 | //打包命令
85 | cmd := packUDPCommand(&f.frame, buf)
86 |
87 | //发送请求
88 | recv, err := f.execute(cmd)
89 | if err != nil {
90 | return nil, err
91 | }
92 |
93 | //[命令码 1 1] [结束码 0 0] , data
94 | code := helper.ParseUint16(recv[2:])
95 | if code != 0 {
96 | return nil, fmt.Errorf("错误码: %d", code)
97 | }
98 |
99 | return recv[4:], nil
100 | }
101 |
102 | func (f *FinsUdp) Write(station int, address protocol.Addr, values []byte) error {
103 | //构建写命令
104 | buf, e := buildWriteCommand(address, values)
105 | if e != nil {
106 | return e
107 | }
108 |
109 | //打包命令
110 | cmd := packUDPCommand(&f.frame, buf)
111 |
112 | //发送请求
113 | recv, err := f.execute(cmd)
114 | if err != nil {
115 | return err
116 | }
117 | //[命令码 1 1] [结束码 0 0]
118 | code := helper.ParseUint16(recv[2:])
119 | if code != 0 {
120 | return fmt.Errorf("错误码: %d", code)
121 | }
122 |
123 | return nil
124 | }
125 |
--------------------------------------------------------------------------------
/omron/fins.go:
--------------------------------------------------------------------------------
1 | package omron
2 |
3 | import (
4 | "errors"
5 | "fmt"
6 | "github.com/zgwit/go-plc/helper"
7 | "github.com/zgwit/go-plc/protocol"
8 | "io"
9 | )
10 |
11 | type Fins struct {
12 | frame UdpFrame
13 | link protocol.Messenger
14 | buf []byte
15 | }
16 |
17 | func NewFinsTCP(link io.ReadWriter, opts string) protocol.Protocol {
18 | fins := &Fins{
19 | link: protocol.Messenger{Conn: link},
20 | buf: make([]byte, 256)}
21 | return fins
22 | }
23 |
24 | func (f *Fins) execute(cmd []byte) ([]byte, error) {
25 | //发送请求
26 | l, err := f.link.Ask(cmd, f.buf)
27 | if err != nil {
28 | return nil, err
29 | }
30 |
31 | //解析数据
32 | if l < 16 {
33 | return nil, errors.New("长度不够")
34 | }
35 |
36 | //头16字节:FINS + 长度 + 命令 + 错误码
37 | status := helper.ParseUint32(f.buf[12:])
38 | if status != 0 {
39 | return nil, fmt.Errorf("TCP状态错误: %d", status)
40 | }
41 |
42 | length := helper.ParseUint32(f.buf[4:])
43 | //判断剩余长度
44 | if int(length)+8 < l {
45 | return nil, fmt.Errorf("长度错误: %d", length)
46 | }
47 |
48 | return f.buf[16:l], nil
49 | }
50 |
51 | func (f *Fins) Handshake() error {
52 |
53 | // 节点号
54 | handshake := []byte{0x00, 0x00, 0x00, 0x01}
55 |
56 | cmd := packTCPCommand(0, handshake)
57 |
58 | //发送请求
59 | buf, e := f.execute(cmd)
60 | if e != nil {
61 | return e
62 | }
63 |
64 | //0x00, 0x00, 0x00, 0x01, // 客户端节点号
65 | //0x00, 0x00, 0x00, 0x01, // PLC端节点号
66 |
67 | //客户端节点
68 | //f.SA1 = buf[3]
69 | //服务端节点
70 | f.frame.DA1 = buf[7]
71 |
72 | return nil
73 | }
74 |
75 | func (f *Fins) Read(station int, address protocol.Addr, size int) ([]byte, error) {
76 |
77 | //构建读命令
78 | buf, e := buildReadCommand(address, size)
79 | if e != nil {
80 | return nil, e
81 | }
82 |
83 | //打包命令
84 | cmd := packTCPCommand(2, packUDPCommand(&f.frame, buf))
85 |
86 | //发送请求
87 | recv, err := f.execute(cmd)
88 | if err != nil {
89 | return nil, err
90 | }
91 |
92 | //[UDP 10字节] [命令码 1 1] [结束码 0 0] , data
93 |
94 | code := helper.ParseUint16(recv[12:])
95 | if code != 0 {
96 | return nil, fmt.Errorf("错误码: %d", code)
97 | }
98 |
99 | //记录响应的SID
100 | f.frame.SID = recv[9]
101 |
102 | return recv[14:], nil
103 | }
104 |
105 | func (f *Fins) Write(station int, address protocol.Addr, values []byte) error {
106 | //构建写命令
107 | buf, e := buildWriteCommand(address, values)
108 | if e != nil {
109 | return e
110 | }
111 |
112 | //打包命令
113 | cmd := packTCPCommand(2, packUDPCommand(&f.frame, buf))
114 |
115 | //发送请求
116 | recv, err := f.execute(cmd)
117 | if err != nil {
118 | return err
119 | }
120 |
121 | //[UDP 10字节] [命令码 1 1] [结束码 0 0]
122 | code := helper.ParseUint32(recv[12:])
123 | if code != 0 {
124 | return fmt.Errorf("错误码: %d", code)
125 | }
126 |
127 | //记录响应的SID
128 | f.frame.SID = recv[9]
129 |
130 | return nil
131 | }
132 |
--------------------------------------------------------------------------------
/omron/packet.go:
--------------------------------------------------------------------------------
1 | package omron
2 |
3 | import (
4 | "github.com/zgwit/go-plc/helper"
5 | "github.com/zgwit/go-plc/protocol"
6 | )
7 |
8 | func buildReadCommand(station int, address protocol.Addr, length int) ([]byte, error) {
9 | //解析地址
10 | addr := address.(*Address)
11 |
12 | buf := make([]byte, 8)
13 |
14 | //命令
15 | buf[0] = 0x01 //MRC 读取存储区数据
16 | buf[1] = 0x01 //SRC
17 | buf[2] = addr.Code
18 | // 地址
19 | helper.WriteUint16(buf[3:], uint16(addr.Offset))
20 | // 位地址
21 | buf[5] = addr.Bits
22 | // 长度
23 | helper.WriteUint16(buf[6:], uint16(length))
24 |
25 | return buf, nil
26 | }
27 |
28 | func buildWriteCommand(station int, address protocol.Addr, values []byte) ([]byte, error) {
29 | //解析地址
30 | addr := address.(*Address)
31 |
32 | length := len(values)
33 |
34 | buf := make([]byte, 8+length)
35 |
36 | buf[0] = 0x01 //MRC 读取存储区数据
37 | buf[1] = 0x02 //SRC
38 | buf[2] = addr.Code
39 |
40 | // 地址
41 | helper.WriteUint16(buf[3:], uint16(addr.Offset))
42 | buf[5] = addr.Bits
43 |
44 | if addr.IsBit {
45 | length = length / 2 // 一个word是双字节
46 | }
47 | // 长度
48 | helper.WriteUint16(buf[6:], uint16(length))
49 |
50 | //数据
51 | copy(buf[8:], values)
52 |
53 | return buf, nil
54 | }
55 |
56 | func packTCPCommand(cmd uint32, payload []byte) []byte {
57 | length := len(payload)
58 | buf := make([]byte, 16+length)
59 |
60 | //copy(buf, "FINS")
61 | buf[0] = 0x46 //FINS
62 | buf[1] = 0x49
63 | buf[2] = 0x4e
64 | buf[3] = 0x53
65 |
66 | //长度
67 | helper.WriteUint32(buf[4:], uint32(length))
68 |
69 | //命令码 读写时为2
70 | helper.WriteUint32(buf[8:], uint32(cmd))
71 |
72 | //错误码
73 | helper.WriteUint32(buf[12:], 0)
74 |
75 | //附加数据
76 | copy(buf[16:], payload)
77 |
78 | return buf
79 | }
80 |
81 | func packUDPCommand(uf *UdpFrame, payload []byte) []byte {
82 | length := len(payload)
83 | buf := make([]byte, 10+length)
84 |
85 | //UDP头
86 | buf[0] = uf.ICF
87 | buf[1] = uf.RSV
88 | buf[2] = uf.GCT
89 | buf[3] = uf.DNA
90 | buf[4] = uf.DA1
91 | buf[5] = uf.DA2
92 | buf[6] = uf.SNA
93 | buf[7] = uf.SA1
94 | buf[8] = uf.SA2
95 | buf[9] = uf.SID
96 |
97 | //附加数据
98 | copy(buf[10:], payload)
99 |
100 | return buf
101 | }
102 |
103 | func packAsciiCommand(uf *UdpFrame, payload []byte) []byte {
104 | cmd := helper.ToHex(payload)
105 |
106 | length := len(cmd)
107 |
108 | buf := make([]byte, 18+length)
109 |
110 | buf[0] = '@'
111 | helper.WriteByteHex(buf[1:], uf.DA1) //PLC设备号
112 | buf[3] = 'F' //识别码
113 | buf[4] = 'A'
114 | buf[5] = 0x30 //响应等待时间 x 15ms
115 | helper.WriteByteHex(buf[6:], uf.ICF)
116 | helper.WriteByteHex(buf[8:], uf.DA2)
117 | helper.WriteByteHex(buf[10:], uf.SA2)
118 | helper.WriteByteHex(buf[12:], uf.SID)
119 | copy(buf[14:], cmd)
120 |
121 | //计算FCS
122 | tmp := buf[0]
123 | for i := 1; i < length+14; i++ {
124 | tmp = tmp ^ buf[i]
125 | }
126 | helper.WriteByteHex(buf[length+14:], tmp)
127 | buf[length+16] = '*'
128 | buf[length+17] = 0x0D //CR
129 |
130 | return buf
131 | }
132 |
--------------------------------------------------------------------------------
/siemens/manifest.go:
--------------------------------------------------------------------------------
1 | package siemens
2 |
3 | import (
4 | "github.com/zgwit/go-plc/protocol"
5 | "io"
6 | )
7 |
8 | var Codes = []protocol.Area{
9 | {"I", "输入"},
10 | {"Q", "输出"},
11 | {"M", "内部标志"},
12 | {"DB", "数据块"},
13 | {"DI", "背景数据块"},
14 | {"L", "局部变量"},
15 | {"V", "全局变量"},
16 | {"C", "计数器"},
17 | {"T", "定时器"},
18 | }
19 |
20 | var ManifestS7_200 = protocol.Manifest{
21 | Name: "S7-S7-200",
22 | Version: "1.0",
23 | Label: "S7 S7-200",
24 | Areas: Codes,
25 | Resolver: ParseAddress,
26 | Factory: func(link io.ReadWriter, opts string) protocol.Protocol {
27 | return &S7{
28 | handshake1: parseHex(handshake1_200),
29 | handshake2: parseHex(handshake2_200),
30 | link: protocol.Messenger{Conn: link},
31 | }
32 | },
33 | }
34 |
35 | var ManifestS7_200_Smart = protocol.Manifest{
36 | Name: "S7-S7-200-Smart",
37 | Version: "1.0",
38 | Label: "S7 S7-200 Smart",
39 | Areas: Codes,
40 | Resolver: ParseAddress,
41 | Factory: func(link io.ReadWriter, opts string) protocol.Protocol {
42 | s7 := &S7{
43 | handshake1: parseHex(handshake1_200_smart),
44 | handshake2: parseHex(handshake2_200_smart),
45 | link: protocol.Messenger{Conn: link},
46 | }
47 | return s7
48 | },
49 | }
50 |
51 | var ManifestS7_300 = protocol.Manifest{
52 | Name: "S7-S7-300",
53 | Version: "1.0",
54 | Label: "S7 S7-300",
55 | Areas: Codes,
56 | Resolver: ParseAddress,
57 | Factory: func(link io.ReadWriter, opts string) protocol.Protocol {
58 | return &S7{
59 | handshake1: parseHex(handshake1_300),
60 | handshake2: parseHex(handshake2_300),
61 | link: protocol.Messenger{Conn: link},
62 | }
63 | },
64 | }
65 |
66 | var ManifestS7_400 = protocol.Manifest{
67 | Name: "S7-S7-400",
68 | Version: "1.0",
69 | Label: "S7 S7-400",
70 | Areas: Codes,
71 | Resolver: ParseAddress,
72 | Factory: func(link io.ReadWriter, opts string) protocol.Protocol {
73 | //TODO 设置机架和槽号
74 | //setRackSlot()
75 | return &S7{
76 | handshake1: parseHex(handshake1_400),
77 | handshake2: parseHex(handshake2_400),
78 | link: protocol.Messenger{Conn: link},
79 | }
80 | },
81 | }
82 |
83 | var ManifestS7_1200 = protocol.Manifest{
84 | Name: "S7-S7-1200",
85 | Version: "1.0",
86 | Label: "S7 S7-1200",
87 | Areas: Codes,
88 | Resolver: ParseAddress,
89 | Factory: func(link io.ReadWriter, opts string) protocol.Protocol {
90 | return &S7{
91 | handshake1: parseHex(handshake1_1200),
92 | handshake2: parseHex(handshake2_1200),
93 | link: protocol.Messenger{Conn: link},
94 | }
95 | },
96 | }
97 |
98 | var ManifestS7_1500 = protocol.Manifest{
99 | Name: "S7-S7-1500",
100 | Version: "1.0",
101 | Label: "S7 S7-1500",
102 | Areas: Codes,
103 | Resolver: ParseAddress,
104 | Factory: func(link io.ReadWriter, opts string) protocol.Protocol {
105 | return &S7{
106 | handshake1: parseHex(handshake1_1500),
107 | handshake2: parseHex(handshake2_1500),
108 | link: protocol.Messenger{Conn: link},
109 | }
110 | },
111 | }
112 |
--------------------------------------------------------------------------------
/siemens/s7.go:
--------------------------------------------------------------------------------
1 | package siemens
2 |
3 | import (
4 | "fmt"
5 | "github.com/zgwit/go-plc/protocol"
6 | )
7 |
8 | type S7 struct {
9 | handshake1 []byte
10 | handshake2 []byte
11 |
12 | link protocol.Messenger
13 | buf []byte
14 | }
15 |
16 | func (s *S7) HandShake() error {
17 | _, err := s.link.Ask(s.handshake1, s.buf)
18 | if err != nil {
19 | return err
20 | }
21 | //TODO 检查结果
22 | _, err = s.link.Ask(s.handshake2, s.buf)
23 | if err != nil {
24 | return err
25 | }
26 | //TODO 检查结果
27 | return nil
28 | }
29 |
30 | func (s *S7) Read(station int, address protocol.Addr, size int) ([]byte, error) {
31 | addr := address.(*Address)
32 |
33 | var vt uint8 = VariableTypeWord
34 | offset := addr.Offset
35 | if addr.HasBit {
36 | vt = VariableTypeBit
37 | offset = offset*8 + uint32(addr.Bits)
38 | }
39 |
40 | pack := S7Package{
41 | Type: MessageTypeJobRequest,
42 | Reference: 0,
43 | param: S7Parameter{
44 | Code: ParameterTypeRead,
45 | Count: 1,
46 | Type: vt,
47 | Areas: []S7ParameterArea{
48 | {
49 | Code: addr.Code,
50 | DB: addr.DB,
51 | Size: uint16(size),
52 | Offset: offset,
53 | },
54 | },
55 | },
56 | }
57 |
58 | cmd := pack.encode()
59 |
60 | n, err := s.link.Ask(cmd, s.buf)
61 | if err != nil {
62 | return nil, err
63 | }
64 |
65 | //解析数据
66 | var resp S7Package
67 | err = resp.decode(s.buf[:n])
68 | if err != nil {
69 | return nil, err
70 | }
71 |
72 | return resp.data[0].Data, nil
73 | }
74 |
75 | func (s *S7) Write(station int, address protocol.Addr, data []byte) error {
76 | addr := address.(*Address)
77 | length := len(data)
78 |
79 | var vt uint8 = VariableTypeWord
80 | offset := addr.Offset
81 | if addr.HasBit {
82 | vt = VariableTypeBit
83 | offset = offset*8 + uint32(addr.Bits)
84 | }
85 |
86 | pack := S7Package{
87 | Type: MessageTypeJobRequest,
88 | Reference: 1,
89 | param: S7Parameter{
90 | Code: ParameterTypeWrite,
91 | Count: 1,
92 | Type: vt,
93 | Areas: []S7ParameterArea{
94 | {
95 | Code: addr.Code,
96 | DB: addr.DB,
97 | Size: uint16(length),
98 | Offset: offset,
99 | },
100 | },
101 | },
102 | data: []S7Data{{
103 | Type: vt + 2, //transport size 3 bit 4 byte/word/qword
104 | Count: uint16(length),
105 | Data: data,
106 | }},
107 | }
108 |
109 | cmd := pack.encode()
110 |
111 | n, err := s.link.Ask(cmd, s.buf)
112 | if err != nil {
113 | return err
114 | }
115 |
116 | //解析结果
117 | var resp S7Package
118 | err = resp.decode(s.buf[:n])
119 | if err != nil {
120 | return err
121 | }
122 | code := resp.data[0].Code
123 | if code != 0xff {
124 | return fmt.Errorf("错误码 %d", code)
125 | }
126 |
127 | /*
128 | 0x00 Reserved 未定义,预留
129 | 0x01 Hardware error 硬件错误
130 | 0x03 Accessing the object not allowed 对象不允许访问
131 | 0x05 Invalid addr 无效地址,所需的地址超出此PLC的极限
132 | 0x06 Data type not supported 数据类型不支持
133 | 0x07 Data type inconsistent 日期类型不一致
134 | 0x0a Object does not exist 对象不存在
135 | 0xff Success 成功
136 | */
137 |
138 | return nil
139 | }
140 |
--------------------------------------------------------------------------------
/siemens/addr.go:
--------------------------------------------------------------------------------
1 | package siemens
2 |
3 | import (
4 | "fmt"
5 | "github.com/zgwit/go-plc/protocol"
6 | "regexp"
7 | "strconv"
8 | "strings"
9 | )
10 |
11 | type Address struct {
12 | Code byte
13 | DB uint16
14 | Offset uint32
15 | HasBit bool
16 | Bits uint8
17 | }
18 |
19 | func (a *Address) String() string {
20 | code := ""
21 | switch a.Code {
22 | case 0x81:
23 | code = "I"
24 | case 0x82:
25 | code = "Q"
26 | case 0x83:
27 | code = "M"
28 | case 0x84:
29 | code = "DB"
30 | case 0x85:
31 | code = "DI"
32 | case 0x86:
33 | code = "L"
34 | case 0x87:
35 | code = "V"
36 | case 0x1D:
37 | code = "T"
38 | case 0x1C:
39 | code = "C"
40 | }
41 | return code + strconv.Itoa(int(a.Offset))
42 | }
43 |
44 | func (a *Address) Diff(from protocol.Addr) (int, bool) {
45 | base := from.(*Address)
46 | if base.Code != a.Code || base.DB != a.DB {
47 | return 0, false
48 | }
49 | if base.HasBit {
50 | if a.HasBit {
51 | cursor := int(a.Offset-base.Offset)*8 + int(a.Bits) - int(base.Bits)
52 | if cursor < 0 {
53 | return 0, false
54 | }
55 | return cursor, true
56 | } else {
57 | return 0, false
58 | }
59 | } else {
60 | cursor := int(a.Offset - base.Offset)
61 | if cursor < 0 {
62 | return 0, false
63 | }
64 | if a.HasBit {
65 | //return data[cursor]&(0x01< 0, true
66 | return 0, false
67 | } else {
68 | return cursor, true
69 | }
70 | }
71 | }
72 |
73 | var addrRegexp1 *regexp.Regexp
74 | var addrRegexp2 *regexp.Regexp
75 | var addrRegexp3 *regexp.Regexp
76 |
77 | func init() {
78 | addrRegexp1 = regexp.MustCompile(`^\d+$`)
79 | addrRegexp2 = regexp.MustCompile(`^\d+.\d+$`)
80 | addrRegexp3 = regexp.MustCompile(`^\d+(.\d+)?$`)
81 | }
82 |
83 | func ParseAddress(name, addr string) (protocol.Addr, error) {
84 | var code uint8 = 1
85 | switch name {
86 | case "I":
87 | code = 0x81
88 | case "Q":
89 | code = 0x82
90 | case "M":
91 | code = 0x83
92 | case "DB":
93 | code = 0x84
94 | case "DI":
95 | code = 0x85
96 | case "L":
97 | code = 0x86
98 | case "V":
99 | code = 0x87
100 | case "C":
101 | code = 0x1C
102 | case "T":
103 | code = 0x1D
104 | }
105 | //addrRegexp.MatchString(addr)
106 |
107 | if name == "DB" {
108 | if !addrRegexp2.MatchString(addr) {
109 | return nil, fmt.Errorf("%s 不是有效的DB格式", addr)
110 | }
111 | ss := strings.Split(addr, ".")
112 | db, _ := strconv.ParseUint(ss[0], 10, 16)
113 | offset, _ := strconv.ParseUint(ss[1], 10, 24)
114 | return &Address{
115 | Code: 0x84,
116 | DB: uint16(db),
117 | Offset: uint32(offset),
118 | }, nil
119 | }
120 | if name == "T" || name == "C" {
121 | if !addrRegexp1.MatchString(addr) {
122 | return nil, fmt.Errorf("%s 不是有效的整数格式", addr)
123 | }
124 | offset, _ := strconv.ParseUint(addr, 10, 24)
125 | return &Address{
126 | Code: code,
127 | Offset: uint32(offset),
128 | }, nil
129 | }
130 |
131 | if !addrRegexp3.MatchString(addr) {
132 | return nil, fmt.Errorf("%s 不是有效的地址格式", addr)
133 | }
134 |
135 | //i q m v
136 | address := &Address{Code: code}
137 | ss := strings.Split(addr, ".")
138 | offset, _ := strconv.ParseUint(ss[0], 10, 24)
139 | address.Offset = uint32(offset)
140 | if len(ss) > 1 {
141 | address.HasBit = true
142 | bits, _ := strconv.ParseUint(ss[1], 10, 16)
143 | address.Bits = uint8(bits)
144 | }
145 | return address, nil
146 | }
147 |
--------------------------------------------------------------------------------
/modbus/tcp.go:
--------------------------------------------------------------------------------
1 | package modbus
2 |
3 | import (
4 | "errors"
5 | "fmt"
6 | "github.com/zgwit/go-plc/helper"
7 | "github.com/zgwit/go-plc/protocol"
8 | "io"
9 | )
10 |
11 | // TCP Modbus-TCP协议
12 | type TCP struct {
13 | link protocol.Messenger
14 | buf []byte
15 | }
16 |
17 | func NewTCP(link io.ReadWriter, opts string) protocol.Protocol {
18 | tcp := &TCP{
19 | link: protocol.Messenger{Conn: link},
20 | buf: make([]byte, 260),
21 | }
22 | return tcp
23 | }
24 |
25 | func (m *TCP) execute(cmd []byte) ([]byte, error) {
26 | helper.WriteUint16(cmd, 0x0A0A) //写入事务ID
27 |
28 | //下发指令
29 | l, err := m.link.AskAtLeast(cmd, m.buf, 10)
30 | if err != nil {
31 | return nil, err
32 | }
33 | buf := m.buf[:l]
34 |
35 | length := helper.ParseUint16(buf[4:])
36 | packLen := int(length) + 6
37 | if packLen > l {
38 | return nil, errors.New("长度不够")
39 | }
40 |
41 | //slave := buf[6]
42 | fc := buf[7]
43 | //解析错误码
44 | if fc&0x80 > 0 {
45 | return nil, fmt.Errorf("错误码:%d", buf[2])
46 | }
47 |
48 | //解析数据
49 | //length := 4
50 | count := int(buf[8])
51 | switch fc {
52 | case FuncCodeReadDiscreteInputs,
53 | FuncCodeReadCoils:
54 | //数组解压
55 | bb := helper.ExpandBool(buf[9:], count)
56 | return bb, nil
57 | case FuncCodeReadInputRegisters,
58 | FuncCodeReadHoldingRegisters,
59 | FuncCodeReadWriteMultipleRegisters:
60 | return helper.Dup(buf[9:]), nil
61 | case FuncCodeWriteSingleCoil, FuncCodeWriteMultipleCoils,
62 | FuncCodeWriteSingleRegister, FuncCodeWriteMultipleRegisters:
63 | //写指令不处理
64 | return nil, nil
65 | default:
66 | return nil, fmt.Errorf("错误功能码:%d", fc)
67 | }
68 | }
69 |
70 | func (m *TCP) Read(station int, address protocol.Addr, size int) ([]byte, error) {
71 | addr := address.(*Address)
72 | b := make([]byte, 12)
73 | //helper.WriteUint16(b, id)
74 | helper.WriteUint16(b[2:], 0) //协议版本
75 | helper.WriteUint16(b[4:], 6) //剩余长度
76 | b[6] = uint8(station)
77 | b[7] = addr.Code
78 | helper.WriteUint16(b[8:], addr.Offset)
79 | helper.WriteUint16(b[10:], uint16(size))
80 |
81 | return m.execute(b)
82 | }
83 |
84 | func (m *TCP) Write(station int, address protocol.Addr, buf []byte) error {
85 | addr := address.(*Address)
86 | length := len(buf)
87 | //如果是线圈,需要Shrink
88 | code := addr.Code
89 | switch code {
90 | case FuncCodeReadCoils:
91 | if length == 1 {
92 | code = 5
93 | //数据 转成 0x0000 0xFF00
94 | if buf[0] > 0 {
95 | buf = []byte{0xFF, 0}
96 | } else {
97 | buf = []byte{0, 0}
98 | }
99 | } else {
100 | code = 15 //0x0F
101 | //数组压缩
102 | b := helper.ShrinkBool(buf)
103 | count := len(b)
104 | buf = make([]byte, 3+count)
105 | helper.WriteUint16(buf, uint16(length))
106 | buf[2] = uint8(count)
107 | copy(buf[3:], b)
108 | }
109 | case FuncCodeReadHoldingRegisters:
110 | if length == 2 {
111 | code = 6
112 | } else {
113 | code = 16 //0x10
114 | b := make([]byte, 3+length)
115 | helper.WriteUint16(b, uint16(length/2))
116 | b[2] = uint8(length)
117 | copy(b[3:], buf)
118 | buf = b
119 | }
120 | default:
121 | return errors.New("功能码不支持")
122 | }
123 |
124 | l := 10 + len(buf)
125 | b := make([]byte, l)
126 | //helper.WriteUint16(b, id)
127 | helper.WriteUint16(b[2:], 0) //协议版本
128 | helper.WriteUint16(b[4:], 6) //剩余长度
129 | b[6] = uint8(station)
130 | b[7] = code
131 | helper.WriteUint16(b[8:], addr.Offset)
132 | copy(b[10:], buf)
133 |
134 | _, err := m.execute(b)
135 | return err
136 | }
137 |
--------------------------------------------------------------------------------
/modbus/rtu.go:
--------------------------------------------------------------------------------
1 | package modbus
2 |
3 | import (
4 | "errors"
5 | "fmt"
6 | "github.com/zgwit/go-plc/helper"
7 | "github.com/zgwit/go-plc/protocol"
8 | "io"
9 | )
10 |
11 | // RTU Modbus-RTU协议
12 | type RTU struct {
13 | link protocol.Messenger
14 | buf []byte
15 | }
16 |
17 | func NewRTU(link io.ReadWriter, opts string) protocol.Protocol {
18 | //TODO parse opts(yaml)
19 | rtu := &RTU{
20 | link: protocol.Messenger{Conn: link},
21 | //slave: opts["slave"].(uint8),
22 | buf: make([]byte, 256),
23 | }
24 |
25 | return rtu
26 | }
27 |
28 | func (m *RTU) execute(cmd []byte) ([]byte, error) {
29 |
30 | l, err := m.link.AskAtLeast(cmd, m.buf, 5)
31 | if err != nil {
32 | return nil, err
33 | }
34 |
35 | crc := helper.ParseUint16LittleEndian(m.buf[l-2:])
36 |
37 | if crc != CRC16(m.buf[:l-2]) {
38 | //检验错误
39 | return nil, errors.New("校验错误")
40 | }
41 |
42 | //slave := buf[0]
43 | fc := m.buf[1]
44 |
45 | //解析错误码
46 | if fc&0x80 > 0 {
47 | return nil, fmt.Errorf("错误码:%d", m.buf[2])
48 | }
49 |
50 | //解析数据
51 | length := 4
52 | count := int(m.buf[2])
53 | switch fc {
54 | case FuncCodeReadDiscreteInputs,
55 | FuncCodeReadCoils:
56 | length += 1 + count/8
57 | if count%8 != 0 {
58 | length++
59 | }
60 |
61 | if l < length {
62 | //长度不够
63 | return nil, errors.New("长度不足")
64 | }
65 | b := m.buf[3 : l-2]
66 | //数组解压
67 | bb := helper.ExpandBool(b, count)
68 | return bb, nil
69 | case FuncCodeReadInputRegisters,
70 | FuncCodeReadHoldingRegisters,
71 | FuncCodeReadWriteMultipleRegisters:
72 | length += 1 + count
73 | if l < length {
74 | //长度不够
75 | return nil, errors.New("长度不足")
76 | }
77 | b := m.buf[3 : l-2]
78 | return helper.Dup(b), nil
79 | case FuncCodeWriteSingleCoil, FuncCodeWriteMultipleCoils,
80 | FuncCodeWriteSingleRegister, FuncCodeWriteMultipleRegisters:
81 | //写指令不处理
82 | return nil, nil
83 | default:
84 | return nil, errors.New("不支持的指令")
85 | }
86 | }
87 |
88 | func (m *RTU) Read(station int, address protocol.Addr, size int) ([]byte, error) {
89 | addr := address.(*Address)
90 | b := make([]byte, 8)
91 | b[0] = uint8(station)
92 | b[1] = addr.Code
93 | helper.WriteUint16(b[2:], addr.Offset)
94 | helper.WriteUint16(b[4:], uint16(size))
95 | helper.WriteUint16LittleEndian(b[6:], CRC16(b[:6]))
96 |
97 | return m.execute(b)
98 | }
99 |
100 | func (m *RTU) Write(station int, address protocol.Addr, buf []byte) error {
101 | addr := address.(*Address)
102 | length := len(buf)
103 | //如果是线圈,需要Shrink
104 | code := addr.Code
105 | switch code {
106 | case FuncCodeReadCoils:
107 | if length == 1 {
108 | code = 5
109 | //数据 转成 0x0000 0xFF00
110 | if buf[0] > 0 {
111 | buf = []byte{0xFF, 0}
112 | } else {
113 | buf = []byte{0, 0}
114 | }
115 | } else {
116 | code = 15 //0x0F
117 | //数组压缩
118 | b := helper.ShrinkBool(buf)
119 | count := len(b)
120 | buf = make([]byte, 3+count)
121 | helper.WriteUint16(buf, uint16(length))
122 | buf[2] = uint8(count)
123 | copy(buf[3:], b)
124 | }
125 | case FuncCodeReadHoldingRegisters:
126 | if length == 2 {
127 | code = 6
128 | } else {
129 | code = 16 //0x10
130 | b := make([]byte, 3+length)
131 | helper.WriteUint16(b, uint16(length/2))
132 | b[2] = uint8(length)
133 | copy(b[3:], buf)
134 | buf = b
135 | }
136 | default:
137 | return errors.New("功能码不支持")
138 | }
139 |
140 | l := 6 + len(buf)
141 | b := make([]byte, l)
142 | b[0] = uint8(station)
143 | b[1] = code
144 | helper.WriteUint16(b[2:], addr.Offset)
145 | copy(b[4:], buf)
146 | helper.WriteUint16LittleEndian(b[l-2:], CRC16(b[:l-2]))
147 |
148 | _, err := m.execute(b)
149 | return err
150 | }
151 |
--------------------------------------------------------------------------------
/mitsubishi/qna3e_binary.go:
--------------------------------------------------------------------------------
1 | package mitsubishi
2 |
3 | import (
4 | "errors"
5 | "fmt"
6 | "github.com/zgwit/go-plc/helper"
7 | "io"
8 | )
9 |
10 | // A3EBinaryAdapter 协议
11 | type A3EBinaryAdapter struct {
12 | StationNumber byte //站编号
13 | NetworkNumber byte //网络编号
14 | PlcNumber byte //PLC编号
15 | IoNumber byte //IO编号
16 |
17 | link io.ReadWriter
18 | }
19 |
20 | func NewA3EBinaryAdapter() *A3EBinaryAdapter {
21 | a := A3EBinaryAdapter{}
22 | a.StationNumber = 0
23 | a.NetworkNumber = 0
24 | a.PlcNumber = 0xFF
25 | a.IoNumber = 0xFF
26 | return &a
27 | }
28 |
29 | func (t *A3EBinaryAdapter) request(cmd []byte) ([]byte, error) {
30 |
31 | // 副标题 D0 00 网络号 00 PLC号 FF IO编号 FF 03 站号 00 应答长度 L H 结束代码 L H
32 | //
33 | buf := make([]byte, 16)
34 | //_, e := t.link.Read(buf)
35 | //if e != nil {
36 | // return nil, e
37 | //}
38 |
39 | status := helper.ParseUint32(buf[12:])
40 | if status != 0 {
41 | return nil, errors.New(fmt.Sprintf("TCP状态错误: %d", status))
42 | }
43 |
44 | length := helper.ParseUint32(buf[4:]) - 8
45 |
46 | payload := make([]byte, length)
47 | //t.link.Read(payload)
48 |
49 | return payload, nil
50 | }
51 |
52 | func (t *A3EBinaryAdapter) BuildCommand(cmd []byte) []byte {
53 | length := len(cmd)
54 |
55 | buf := make([]byte, 11+length)
56 | buf[0] = 0x50 //副标题
57 | buf[1] = 0x00
58 | buf[2] = t.NetworkNumber //网络号
59 | buf[3] = t.PlcNumber //PLC编号
60 | buf[4] = t.IoNumber //目标IO编号 L
61 | buf[5] = 0x03 //目标IO编号 H
62 | buf[6] = t.StationNumber //站编号
63 | helper.WriteUint16LittleEndian(buf[7:], uint16(length)) //请求数据长度
64 | helper.WriteUint16LittleEndian(buf[9:], 10) //CPU监视定时器
65 | //命令
66 | copy(buf[11:], cmd)
67 |
68 | return buf
69 | }
70 |
71 | // Read 读取数据
72 | func (t *A3EBinaryAdapter) Read(address string, length int) ([]byte, error) {
73 |
74 | //解析地址
75 | addr, e := ParseAddress(address)
76 | if e != nil {
77 | return nil, e
78 | }
79 |
80 | //构建命令
81 | buf := make([]byte, 10)
82 | buf[0] = 0x01 // 批量读取数据命令
83 | buf[1] = 0x04
84 | // 以点为单位还是字为单位成批读取
85 | if addr.IsBit {
86 | buf[2] = 0x01
87 | } else {
88 | buf[2] = 0x00
89 | }
90 | buf[3] = 0x00
91 | helper.WriteUint24LittleEndian(buf[4:], uint32(addr.Addr)) // 起始地址的地位
92 | buf[7] = addr.Code // 指明读取的数据
93 | helper.WriteUint16LittleEndian(buf[8:], uint16(length)) //软元件的长度
94 |
95 | //构建命令
96 | cmd := t.BuildCommand(buf)
97 |
98 | recv, err := t.request(cmd)
99 | if err != nil {
100 | return nil, err
101 | }
102 |
103 | //TODO 解压位, 0x10 10 => 0x01 00 01 00
104 |
105 | return recv, nil
106 |
107 | }
108 |
109 | func (t *A3EBinaryAdapter) Write(address string, values []byte) error {
110 | //解析地址
111 | addr, e := ParseAddress(address)
112 | if e != nil {
113 | return e
114 | }
115 |
116 | length := len(values)
117 |
118 | //TODO 压缩位, 0x01 00 01 00 => 0x10 10,读取的时候,也要解压
119 |
120 | //构建命令
121 | buf := make([]byte, 10+length)
122 | buf[0] = 0x01 // 批量写入数据命令
123 | buf[1] = 0x14
124 | // 以点为单位还是字为单位成批读取
125 | if addr.IsBit {
126 | buf[2] = 0x01
127 | } else {
128 | buf[2] = 0x00
129 | }
130 | buf[3] = 0x00
131 | helper.WriteUint24LittleEndian(buf[4:], uint32(addr.Addr)) // 起始地址的地位
132 | buf[7] = addr.Code // 指明写入的数据
133 | helper.WriteUint16LittleEndian(buf[8:], uint16(length)) //软元件的长度
134 |
135 | copy(buf[10:], values)
136 |
137 | //构建命令
138 | cmd := t.BuildCommand(buf)
139 |
140 | //发送命令
141 | _, err := t.request(cmd)
142 | return err
143 | }
144 |
--------------------------------------------------------------------------------
/mitsubishi/fx_program.go:
--------------------------------------------------------------------------------
1 | package mitsubishi
2 |
3 | import (
4 | "encoding/hex"
5 | "errors"
6 | "fmt"
7 | "github.com/zgwit/go-plc/helper"
8 | "github.com/zgwit/go-plc/protocol"
9 | "strconv"
10 | "strings"
11 | )
12 |
13 | type FxProgramCommand struct {
14 | Code uint16
15 | IsBit bool
16 | Base int
17 | }
18 |
19 | type FxProgramAddress struct {
20 | Code string
21 | Addr uint16
22 | }
23 |
24 | func (a *FxProgramAddress) String() string {
25 | return fmt.Sprintf("%s %d", a.Code, a.Addr)
26 | }
27 |
28 | func (a *FxProgramAddress) Diff(from protocol.Addr) (int, bool) {
29 |
30 | return 0, false
31 | }
32 |
33 | var fxProgramCommands = map[string]FxProgramCommand{
34 | "X": {0x0080, true, 8}, //X输入继电器
35 | "Y": {0x00A0, true, 8}, //Y输出继电器
36 | "M": {0x0100, true, 10}, //M中间继电器
37 | "D": {0x1000, false, 10}, //D数据寄存器
38 | "S": {0x0000, true, 10}, //S步进继电器
39 | "TS": {0x00C0, true, 10}, //定时器的触点
40 | "TC": {0x02C0, true, 10}, //定时器的线圈
41 | "TN": {0x0800, false, 10}, //定时器的当前值 ?
42 | "CS": {0x01C0, true, 10}, //计数器的触点
43 | "CC": {0x03C0, true, 10}, //计数器的线圈
44 | "CN": {0x0A00, false, 10}, //计数器的当前值 ?
45 | }
46 |
47 | func ParseFxProgramAddress(code string, address string) (protocol.Addr, error) {
48 | var addr FxProgramAddress
49 |
50 | cmd, ok := fxProgramCommands[code]
51 | if !ok {
52 | return nil, fmt.Errorf("不支持的区域 %s", code)
53 | }
54 | addr.Code = code
55 | v, err := strconv.ParseUint(address[2:], cmd.Base, 16)
56 | if cmd.IsBit {
57 | addr.Addr = cmd.Code + uint16(int(v)/8)
58 | } else {
59 | addr.Addr = cmd.Code + uint16(v)*2
60 | }
61 | return &addr, err
62 | }
63 |
64 | // FxProgram FX协议
65 | type FxProgram struct {
66 | link protocol.Messenger
67 | }
68 |
69 | // NewFxProgram 新建
70 | func NewFxProgram() *FxProgram {
71 | return &FxProgram{}
72 | }
73 |
74 | // Read 解析
75 | func (t *FxProgram) Read(station int, address protocol.Addr, length int) ([]byte, error) {
76 | addr := address.(*FxProgramAddress)
77 |
78 | buf := make([]byte, 11)
79 | buf[0] = 0x02 // STX
80 | buf[1] = 0x30 // 命令 Read
81 | helper.WriteUint16Hex(buf[2:], addr.Addr) // 偏移地址
82 | helper.WriteUint8Hex(buf[6:], uint8(length)) // 读取长度
83 | buf[8] = 0x03 // ETX
84 |
85 | // 计算和校验
86 | var sum uint8 = 0
87 | for i := 1; i < len(buf)-2; i++ {
88 | sum += buf[i]
89 | }
90 |
91 | //最后两位是校验
92 | helper.WriteUint8Hex(buf[len(buf)-2:], sum)
93 |
94 | fmt.Println("FxProgram read buff = ", hex.EncodeToString(buf))
95 | var recv [256]byte
96 | n, err := t.link.Ask(buf, recv[:])
97 | fmt.Println("FxProgram recv buff", hex.EncodeToString(recv[:n]))
98 | if err != nil {
99 | return nil, err
100 | }
101 |
102 | if recv[0] == 0x15 {
103 | return nil, errors.New("返回错误")
104 | }
105 |
106 | ret, err := hex.DecodeString(string(recv[1 : n-3]))
107 |
108 | if err != nil {
109 | return nil, err
110 | }
111 | //ret := helper.FromHex(recv[1 : len(recv)-3])
112 |
113 | return ret, nil
114 | }
115 |
116 | // Write 写
117 | func (t *FxProgram) Write(station int, address protocol.Addr, values []byte) error {
118 | addr := address.(*FxProgramAddress)
119 |
120 | //先转成十六进制
121 | length := len(values)
122 |
123 | values = []byte(strings.ToUpper(hex.EncodeToString(values)))
124 |
125 | buf := make([]byte, 11+(length*2))
126 | buf[0] = 0x02 // STX
127 | buf[1] = 0x31 // 命令 Write
128 | helper.WriteUint16Hex(buf[2:], addr.Addr) // 偏移地址
129 | helper.WriteUint8Hex(buf[6:], uint8(length)) // 写入长度
130 | copy(buf[8:], values) // 写入内容
131 | buf[len(buf)-3] = 0x03 // ETX
132 |
133 | // 计算和校验
134 | var sum uint8 = 0
135 | for i := 1; i < len(buf)-2; i++ {
136 | sum += buf[i]
137 | }
138 | //最后两位是校验
139 | helper.WriteUint8Hex(buf[len(buf)-2:], sum)
140 |
141 | fmt.Println("FxProgram write buff = ", hex.EncodeToString(buf))
142 | var recv [256]byte
143 | n, err := t.link.Ask(buf, recv[:])
144 | fmt.Println("FxProgram recv buff", hex.EncodeToString(recv[:n]))
145 | if err != nil {
146 | return err
147 | }
148 | if recv[0] == 0x15 {
149 | return errors.New("错误")
150 | } else {
151 | return nil
152 | }
153 | }
154 |
--------------------------------------------------------------------------------
/siemens/simatic.go:
--------------------------------------------------------------------------------
1 | package siemens
2 |
3 | import (
4 | "github.com/zgwit/go-plc/helper"
5 | "github.com/zgwit/go-plc/protocol"
6 | )
7 |
8 | type Simatic struct {
9 | handshake1 []byte
10 | handshake2 []byte
11 |
12 | link protocol.Messenger
13 | buf []byte
14 | }
15 |
16 | func (s *Simatic) HandShake() error {
17 | _, err := s.link.Ask(s.handshake1, s.buf)
18 | if err != nil {
19 | return err
20 | }
21 | //TODO 检查结果
22 | _, err = s.link.Ask(s.handshake2, s.buf)
23 | if err != nil {
24 | return err
25 | }
26 | //TODO 检查结果
27 | return nil
28 | }
29 |
30 | func (s *Simatic) Read(station int, addr protocol.Addr, size int) ([]byte, error) {
31 | address := addr.(*Address)
32 |
33 | buf := make([]byte, 14)
34 | buf[0] = 0x04 // 4读 5写
35 | buf[1] = 1 // 读取块数
36 | buf[2] = 0x12 //specification type 指定有效值类型
37 | buf[3] = 0x0A //length 接下来本次地址访问长度
38 | buf[4] = 0x10 //syntax id 语法标记,ANY
39 | buf[5] = 0x02 //variable type 1 bit 2 word 3 dint 4 real 5 counter???
40 | helper.WriteUint16(buf[6:], uint16(size)) // 访问数据的个数
41 | helper.WriteUint16(buf[8:], address.DB) //db number DB块编号,如果访问的是DB块的话
42 | buf[10] = address.Code //area 访问数据类型
43 | helper.WriteUint24(buf[11:], address.Offset) //address 偏移位置
44 |
45 | cmd := packCommand(buf)
46 |
47 | n, err := s.link.Ask(cmd, s.buf)
48 | if err != nil {
49 | return nil, err
50 | }
51 |
52 | //TODO 解析数据
53 |
54 | return s.buf[:n], nil
55 | }
56 |
57 | func (s *Simatic) Poll(station int, addr protocol.Addr, size int) ([]byte, error) {
58 | return s.Read(station, addr, size)
59 | }
60 |
61 | func (s *Simatic) Write(station int, addr protocol.Addr, data []byte) error {
62 | address := addr.(*Address)
63 | length := len(data)
64 |
65 | buf := make([]byte, 14)
66 | buf[0] = 0x05 // 4读 5写
67 | buf[1] = 1 // 读取块数
68 | buf[2] = 0x12 // 指定有效值类型
69 | buf[3] = 0x0A // 接下来本次地址访问长度
70 | buf[4] = 0x10 // 语法标记,ANY
71 | buf[5] = 0x02 // 按字为单位,1 位 2 字
72 | helper.WriteUint16(buf[6:], uint16(length)) // 访问数据的个数
73 | helper.WriteUint16(buf[8:], address.DB) // DB块编号,如果访问的是DB块的话
74 | buf[10] = address.Code // 访问数据类型
75 | helper.WriteUint24(buf[11:], address.Offset) // 偏移位置
76 | // 按字写入
77 | buf[14] = 0x00
78 | buf[15] = 0x04
79 | helper.WriteUint16(buf[16:], uint16(length*8)) // 按位计算的长度
80 |
81 | //添加数据
82 | copy(buf[18:], data)
83 |
84 | cmd := packCommand(buf)
85 |
86 | _, err := s.link.Ask(cmd, s.buf)
87 | if err != nil {
88 | return err
89 | }
90 |
91 | //TODO 解析结果
92 |
93 | return nil
94 | }
95 |
96 | // packCommand 打包命令
97 | func packCommand(cmd []byte) []byte {
98 | length := len(cmd)
99 |
100 | buf := make([]byte, length+17)
101 | //TPKT
102 | buf[0] = 0x03
103 | buf[1] = 0x00
104 | helper.WriteUint16(buf[2:], uint16(length+17)) // 长度
105 | //ISO-COTP
106 | buf[4] = 0x02 // 固定
107 | buf[5] = 0xF0
108 | buf[6] = 0x80
109 |
110 | //S7 communication
111 | buf[7] = 0x32 //Desc ID 协议ID,固定为32
112 | buf[8] = 0x01 //Message Type(ROSCTR) 1 Job Request 2 Ack 3 Ack-Data 7 Userdata
113 | buf[9] = 0x0 //Reserved
114 | buf[10] = 0x0
115 | helper.WriteUint16LittleEndian(buf[11:], 0) // PDU ref 标识序列号(可以像Modbus TCP一样使用)
116 | helper.WriteUint16(buf[13:], uint16(length)) // Param length
117 | helper.WriteUint16(buf[15:], 0) // Data length
118 |
119 | //发送请求Request job
120 | //buf[17]功能码 04读 05写
121 | //buf[18]块数
122 |
123 | //仅出现在Ack-Data消息中
124 | //buf[17] Error class 0x00无 0x81应用程序关系错误 0x82对象定义错误 0x83无效资源可用错误 0x84服务处理错误 0x85请求错误 0x87访问错误
125 | //buf[18] Error Area
126 |
127 | //Parameter区
128 | //读取或写入
129 | //buf[19]0x12 读写是固定的
130 | //buf[20]此项剩余长度
131 | //buf[21]常量0x10 语法标记
132 | //buf[22]数据类型 1 2
133 | //buf[23 24]读取长度
134 | //buf[25 26]DB号
135 | //buf[27]Area
136 | //buf[28 29 30]地址
137 |
138 | //Data数据区
139 | //读取的结果
140 | //buf[19] Return Area 0xff 代表成功
141 | //buf[20] Variable type 数据类型
142 | //buf[21 22] Count
143 | //buf[23+]数据,长度是len(variable)*count
144 |
145 | //写入的内容 和 结果
146 | //buf[31] Return Area 0x00 固定
147 | //buf[32] Variable type 数据类型
148 | //buf[33 34] Count
149 | //buf[35+]数据,长度是len(variable)*count
150 |
151 | copy(buf[17:], cmd)
152 |
153 | return buf
154 | }
155 |
--------------------------------------------------------------------------------
/mitsubishi/fx_special.go:
--------------------------------------------------------------------------------
1 | package mitsubishi
2 |
3 | import (
4 | "errors"
5 | "fmt"
6 | helper2 "github.com/zgwit/go-plc/helper"
7 | "io"
8 | "strconv"
9 | "strings"
10 | )
11 |
12 | type fxSpecialCommand struct {
13 | IsBit bool
14 | Base int
15 | }
16 |
17 | type fxSpecialAddr struct {
18 | fxSpecialCommand
19 | Address string
20 | }
21 |
22 | var fxSpecialCommands = map[string]fxSpecialCommand{
23 | "X": {true, 8}, //X输入继电器
24 | "Y": {true, 8}, //Y输出继电器
25 | "M": {true, 10}, //M中间继电器
26 | "D": {false, 10}, //D数据寄存器
27 | "R": {false, 10}, //R文件寄存器
28 | "S": {true, 10}, //S步进继电器
29 | "TS": {true, 10}, //定时器的触点
30 | "TN": {false, 10}, //定时器的当前值
31 | "CS": {true, 10}, //计数器的触点
32 | "CN": {false, 10}, //计数器的当前值
33 | }
34 |
35 | func parseFxSpecialAddress(address string) (addr fxSpecialAddr, err error) {
36 | var v uint64
37 |
38 | //先检查两字节
39 | k := strings.ToUpper(address[:2])
40 | if cmd, ok := fxSpecialCommands[k]; ok {
41 | v, err = strconv.ParseUint(address[2:], cmd.Base, 16)
42 | if err == nil {
43 | addr.fxSpecialCommand = cmd
44 | addr.Address = k + fmt.Sprintf("%03d", v)
45 | }
46 | return
47 | }
48 |
49 | //检测单字节
50 | k = strings.ToUpper(address[:1])
51 | if cmd, ok := fxSpecialCommands[k]; ok {
52 | v, err = strconv.ParseUint(address[1:], cmd.Base, 16)
53 | if err == nil {
54 | addr.fxSpecialCommand = cmd
55 | addr.Address = k + fmt.Sprintf("%04d", v)
56 | }
57 | return
58 | }
59 |
60 | err = errors.New("未知消息")
61 | return
62 | }
63 |
64 | // FxSpecial FX协议
65 | type FxSpecial struct {
66 | StationNumber byte //站编号 00
67 | PlcNumber byte //PLC编号 FF
68 |
69 | CheckSum bool //默认true
70 | Delay uint8
71 |
72 | link io.ReadWriter
73 | }
74 |
75 | func NewFxSpecial() *FxSpecial {
76 | return &FxSpecial{}
77 | }
78 |
79 | func (t *FxSpecial) Read(address string, length int) ([]byte, error) {
80 |
81 | recvLength := length
82 |
83 | addr, err := parseFxSpecialAddress(address)
84 | if err != nil {
85 | return nil, err
86 | }
87 |
88 | buf := make([]byte, 23)
89 | buf[0] = 0x05 //ENQ
90 | helper2.WriteUint8Hex(buf[1:], t.StationNumber) //站号
91 | helper2.WriteByteHex(buf[3:], t.PlcNumber) //PLC号
92 | if addr.IsBit {
93 | buf[10] = 'B' //位
94 | } else {
95 | buf[10] = 'W' //字
96 | recvLength = recvLength << 1 // recvLength*2 字是双字节
97 | }
98 | buf[11] = 'R' //读取
99 | helper2.WriteUint8Hex(buf[12:], t.Delay) //延迟
100 | copy(buf[14:], addr.Address) // 偏移地址
101 | copy(buf[19:], fmt.Sprintf("%02d", length)) //读取长度
102 |
103 | if t.CheckSum {
104 | //最后两位是和校验
105 | helper2.WriteUint8Hex(buf[len(buf)-2:], helper2.Sum(buf[1:len(buf)-2]))
106 | } else {
107 | buf = buf[:21]
108 | }
109 |
110 | //发送请求
111 |
112 | recv := make([]byte, recvLength+8)
113 | //_, err = t.link.Read(recv)
114 | //if err != nil {
115 | // return nil, err
116 | //}
117 |
118 | //STX 站号 PLC号 数据 ETX 和校验
119 | ret := recv[5 : len(recv)-3]
120 | if addr.IsBit {
121 | //布尔数组
122 | ret = helper2.AsciiToBool(ret)
123 | } else {
124 | //转十六进制
125 | ret = helper2.FromHex(ret)
126 | }
127 | return ret, nil
128 | }
129 |
130 | func (t *FxSpecial) Write(address string, values []byte) error {
131 |
132 | addr, err := parseFxSpecialAddress(address)
133 | if err != nil {
134 | return err
135 | }
136 |
137 | if addr.IsBit {
138 | //布尔数组
139 | values = helper2.BoolToAscii(values)
140 | } else {
141 | //转十六进制
142 | values = helper2.ToHex(values)
143 | }
144 |
145 | length := len(values)
146 |
147 | buf := make([]byte, 23+length)
148 | buf[0] = 0x05 //ENQ
149 | helper2.WriteUint8Hex(buf[1:], t.StationNumber) //站号
150 | helper2.WriteByteHex(buf[3:], t.PlcNumber) //PLC号
151 | if addr.IsBit {
152 | buf[10] = 'B' //位
153 | } else {
154 | buf[10] = 'W' //字
155 | length = length >> 1 // length/2 字是双字节
156 | }
157 | buf[11] = 'W' //写入
158 | helper2.WriteUint8Hex(buf[12:], t.Delay) //延迟
159 | copy(buf[14:], addr.Address) // 偏移地址
160 | copy(buf[19:], fmt.Sprintf("%02d", length)) //读取长度
161 | copy(buf[21:], values) //写入数据
162 |
163 | if t.CheckSum {
164 | //最后两位是和校验
165 | helper2.WriteUint8Hex(buf[len(buf)-2:], helper2.Sum(buf[1:len(buf)-2]))
166 | } else {
167 | buf = buf[:21+length]
168 | }
169 |
170 | //发送请求
171 |
172 | //recv := make([]byte, 5)
173 | //_, err = t.link.Read(recv)
174 | //if err != nil {
175 | // return err
176 | //}
177 |
178 | //ACK 站号 PLC号
179 | return nil
180 | }
181 |
--------------------------------------------------------------------------------
/mitsubishi/a3c1.go:
--------------------------------------------------------------------------------
1 | package mitsubishi
2 |
3 | import (
4 | "fmt"
5 | "github.com/zgwit/go-plc/helper"
6 | "io"
7 | "strconv"
8 | )
9 |
10 | // A3C1 A3C1协议
11 | type A3C1 struct {
12 | StationNumber byte //站编号
13 | NetworkNumber byte //网络编号
14 | PlcNumber byte //PLC编号
15 | UpperNumber byte //上位机编号
16 |
17 | link io.ReadWriter
18 | }
19 |
20 | func NewA3C1() *A3C1 {
21 | a := A3C1{}
22 | a.StationNumber = 0
23 | a.NetworkNumber = 0
24 | a.PlcNumber = 0xFF
25 | a.UpperNumber = 0
26 | return &a
27 |
28 | }
29 |
30 | func (t *A3C1) BuildCommand(cmd []byte) []byte {
31 | length := len(cmd)
32 |
33 | buf := make([]byte, 13+length)
34 | buf[0] = 0x05 //ENQ
35 | buf[1] = 0x46 //F 帧识别号H
36 | buf[2] = 0x39 //9 帧识别号L
37 | helper.WriteByteHex(buf[3:], t.StationNumber) //站编号
38 | helper.WriteByteHex(buf[5:], t.NetworkNumber) //网络编号
39 | helper.WriteByteHex(buf[7:], t.PlcNumber) //PLC编号
40 | helper.WriteByteHex(buf[9:], t.UpperNumber) //上位机编号
41 |
42 | //命令
43 | copy(buf[11:], cmd)
44 |
45 | // 计算和校验
46 | var sum byte = 0
47 | for i := 1; i < len(buf)-3; i++ {
48 | sum += buf[i]
49 | }
50 |
51 | //最后两位是校验
52 | helper.WriteByteHex(buf[len(buf)-2:], sum)
53 |
54 | return buf
55 |
56 | }
57 |
58 | func (t *A3C1) Read(address string, length int) ([]byte, error) {
59 |
60 | //解析地址
61 | addr, e := ParseAddress(address)
62 | if e != nil {
63 | return nil, e
64 | }
65 |
66 | buf := make([]byte, 20)
67 | copy(buf, []byte("0401")) //命令 读取
68 | if addr.IsBit {
69 | copy(buf[4:], []byte("0001")) //子命令 按位
70 | } else {
71 | copy(buf[4:], []byte("0000")) //子命令 按字
72 | }
73 | copy(buf[8:], addr.Name) // 软元件类型
74 | if addr.Base == 10 {
75 | copy(buf[10:], fmt.Sprintf("%d6", addr.Addr)) // 起始地址的地位
76 | } else {
77 | copy(buf[10:], fmt.Sprintf("%X6", addr.Addr)) // 起始地址的地位
78 | }
79 | copy(buf[16:], fmt.Sprintf("%X4", length)) // 软元件点数
80 |
81 | //构建命令
82 | t.BuildCommand(buf)
83 |
84 | //发送命令
85 |
86 | //如果不是位,需要纠正长度
87 | if !addr.IsBit {
88 | length = length * 4
89 | }
90 |
91 | //接收响应
92 | recv := make([]byte, 11+length)
93 | //if _, err := t.link.Read(recv); err != nil {
94 | // return nil, err
95 | //}
96 |
97 | // 正确
98 | // STX 帧识别号 站编号 网络编号 PLC编号 上位站编号 ---内容--- ETX 和校验
99 | // 0x20 F 9 0 0 0 0 F F 0 0 .... 0x03 0 0
100 | data := recv[11:length]
101 |
102 | if addr.IsBit {
103 | //0x31 => 0x01, 0x30 => 0
104 | r := make([]byte, length)
105 | for i, v := range data {
106 | if v == '1' {
107 | r[i] = 1
108 | } else {
109 | r[i] = 0
110 | }
111 | }
112 | } else {
113 | //每4字节,表示一个十六进制
114 | r := make([]byte, length/2)
115 | for i := 0; i < length; i += 4 {
116 | d, _ := strconv.ParseUint(string(data[i:i+4]), 16, 32)
117 | //TODO 大小端需要再去确认
118 | r[i*2] = byte(d << 8)
119 | r[i*2+1] = byte(d)
120 | }
121 | }
122 |
123 | // 错误
124 | // NAK 帧识别号 站编号 网络编号 PLC编号 上位站编号 ---内容--- 错误码
125 | // 0x15 F 9 0 0 0 0 F F 0 0 0 0 0 0
126 |
127 | return buf, nil
128 | }
129 |
130 | func (t *A3C1) Write(address string, values []byte) error {
131 | //解析地址
132 | addr, e := ParseAddress(address)
133 | if e != nil {
134 | return e
135 | }
136 |
137 | length := len(values)
138 |
139 | var value []byte
140 |
141 | //数据预处理,位数据要转成0和1,字数据
142 | if addr.IsBit {
143 | value = make([]byte, length)
144 | for k, v := range values {
145 | if v != 0 {
146 | value[k] = '1'
147 | } else {
148 | value[k] = '0'
149 | }
150 | }
151 | } else {
152 | //uint16 数组转字符串
153 | value := make([]byte, length*2)
154 | for i := 0; i < length/2; i++ {
155 | d := values[i]<<8 + values[i+1]
156 | if addr.Base == 10 {
157 | copy(value[i*4:], fmt.Sprintf("%d4", d))
158 | } else {
159 | copy(value[i*4:], fmt.Sprintf("%X4", d))
160 | }
161 | }
162 |
163 | length = len(value) //length * 2
164 | }
165 |
166 | buf := make([]byte, 20+length)
167 | copy(buf, []byte("1401")) //命令 读取
168 | if addr.IsBit {
169 | copy(buf[4:], []byte("0001")) //子命令 按位
170 | } else {
171 | copy(buf[4:], []byte("0000")) //子命令 按字
172 | }
173 | copy(buf[8:], addr.Name) // 软元件类型
174 | if addr.Base == 10 {
175 | copy(buf[10:], fmt.Sprintf("%d6", addr.Addr)) // 起始地址的地位
176 | } else {
177 | copy(buf[10:], fmt.Sprintf("%X6", addr.Addr)) // 起始地址的地位
178 | }
179 | if addr.IsBit {
180 | copy(buf[16:], fmt.Sprintf("%X4", length)) // 软元件点数
181 | } else {
182 | copy(buf[16:], fmt.Sprintf("%X4", length/4)) // 软元件点数
183 | }
184 |
185 | //附加数据
186 | copy(buf[20:], value)
187 |
188 | //构建命令
189 | t.BuildCommand(buf)
190 |
191 | //发送命令
192 |
193 | //接收响应
194 | //recv := make([]byte, 15)
195 | //if _, err := t.link.Read(recv); err != nil {
196 | // return err
197 | //}
198 |
199 | // 正确
200 | // ACK 帧识别号 站编号 网络编号 PLC编号 上位站编号
201 | // 0x06 F 9 0 0 0 0 F F 0 0
202 |
203 | // 错误
204 | // NAK 帧识别号 站编号 网络编号 PLC编号 上位站编号 错误码
205 | // 0x15 F 9 0 0 0 0 F F 0 0 0 0 0 0
206 |
207 | return nil
208 | }
209 |
--------------------------------------------------------------------------------
/modbus/parallel-tcp.go:
--------------------------------------------------------------------------------
1 | package modbus
2 |
3 | import (
4 | "errors"
5 | "fmt"
6 | "github.com/zgwit/go-plc/helper"
7 | "github.com/zgwit/go-plc/protocol"
8 | "io"
9 | "sync"
10 | "time"
11 | )
12 |
13 | type response struct {
14 | buf []byte
15 | err error
16 | }
17 |
18 | type request struct {
19 | cmd []byte
20 | resp chan response //out
21 | }
22 |
23 | // ParallelTCP Modbus-TCP协议
24 | type ParallelTCP struct {
25 | link io.ReadWriter
26 | queue chan interface{} //in
27 |
28 | requests sync.Map
29 | increment uint16
30 | }
31 |
32 | func NewParallelTCP(link io.ReadWriter, opts string) protocol.Protocol {
33 | concurrency := 10
34 |
35 | tcp := &ParallelTCP{
36 | link: link,
37 | queue: make(chan interface{}, concurrency),
38 | increment: 0x0A0A, //避免首字节为0,有些DTU可能会异常
39 | }
40 | return tcp
41 | }
42 |
43 | func (m *ParallelTCP) execute(cmd []byte, immediate bool) ([]byte, error) {
44 | req := &request{
45 | cmd: cmd,
46 | resp: make(chan response, 1),
47 | }
48 |
49 | if !immediate {
50 | //排队等待
51 | m.queue <- nil
52 | }
53 |
54 | id := m.increment
55 | helper.WriteUint16(cmd, id) //写入事务ID
56 | m.increment++
57 | if m.increment < 0x0A0A {
58 | m.increment = 0x0A0A
59 | }
60 | m.requests.Store(id, req)
61 |
62 | //TODO 下发指令
63 | _, err := m.link.Write(cmd)
64 | if err != nil {
65 | if !immediate {
66 | //释放队列
67 | <-m.queue
68 | }
69 | return nil, err
70 | }
71 |
72 | //等待结果
73 | select {
74 | case <-time.After(5 * time.Second):
75 | if !immediate {
76 | <-m.queue //清空
77 | }
78 | return nil, errors.New("timeout")
79 | case resp := <-req.resp:
80 | return resp.buf, resp.err
81 | }
82 | }
83 |
84 | func (m *ParallelTCP) OnData(buf []byte) {
85 | //取出请求,并让出队列,可以开始下一个请示了
86 | if len(m.queue) > 0 {
87 | <-m.queue
88 | }
89 |
90 | //解析数据
91 | l := len(buf)
92 | if l < 10 {
93 | return //长度不够
94 | }
95 |
96 | length := helper.ParseUint16(buf[4:])
97 | packLen := int(length) + 6
98 |
99 | if l < packLen {
100 | //TODO 缓存包,下次再处理???
101 | return //长度不够
102 | }
103 |
104 | //处理数据包
105 | m.handlePack(buf[:packLen])
106 |
107 | //粘包处理
108 | //如果有剩余内容,可能是下一个响应包,需要继续处理
109 | //此代码会导致后包比前包先处理
110 | if l > packLen {
111 | m.OnData(buf[packLen:])
112 | }
113 | }
114 |
115 | func (m *ParallelTCP) handlePack(buf []byte) {
116 | id := helper.ParseUint16(buf)
117 | r, ok := m.requests.LoadAndDelete(id)
118 | if !ok {
119 | return
120 | }
121 | req := r.(*request)
122 |
123 | //slave := buf[6]
124 | fc := buf[7]
125 | //解析错误码
126 | if fc&0x80 > 0 {
127 | req.resp <- response{err: fmt.Errorf("错误码:%d", buf[2])}
128 | return
129 | }
130 |
131 | //解析数据
132 | //length := 4
133 | count := int(buf[8])
134 | switch fc {
135 | case FuncCodeReadDiscreteInputs,
136 | FuncCodeReadCoils:
137 | //数组解压
138 | bb := helper.ExpandBool(buf[9:], count)
139 | req.resp <- response{buf: bb}
140 | case FuncCodeReadInputRegisters,
141 | FuncCodeReadHoldingRegisters,
142 | FuncCodeReadWriteMultipleRegisters:
143 | req.resp <- response{buf: helper.Dup(buf[9:])}
144 | case FuncCodeWriteSingleCoil, FuncCodeWriteMultipleCoils,
145 | FuncCodeWriteSingleRegister, FuncCodeWriteMultipleRegisters:
146 | //写指令不处理
147 | default:
148 | req.resp <- response{err: fmt.Errorf("错误功能码:%d", fc)}
149 | }
150 | }
151 |
152 | func (m *ParallelTCP) Read(station int, address protocol.Addr, size int) ([]byte, error) {
153 | addr := address.(*Address)
154 |
155 | b := make([]byte, 12)
156 | //helper.WriteUint16(b, id)
157 | helper.WriteUint16(b[2:], 0) //协议版本
158 | helper.WriteUint16(b[4:], 6) //剩余长度
159 | b[6] = byte(station)
160 | b[7] = addr.Code
161 | helper.WriteUint16(b[8:], addr.Offset)
162 | helper.WriteUint16(b[10:], uint16(size))
163 |
164 | return m.execute(b, true)
165 | }
166 |
167 | func (m *ParallelTCP) Write(station int, address protocol.Addr, buf []byte) error {
168 | addr := address.(*Address)
169 | code := addr.Code
170 |
171 | length := len(buf)
172 | switch code {
173 | case FuncCodeReadCoils:
174 | if length == 1 {
175 | code = 5
176 | //数据 转成 0x0000 0xFF00
177 | if buf[0] > 0 {
178 | buf = []byte{0xFF, 0}
179 | } else {
180 | buf = []byte{0, 0}
181 | }
182 | } else {
183 | code = 15 //0x0F
184 | //数组压缩
185 | b := helper.ShrinkBool(buf)
186 | count := len(b)
187 | buf = make([]byte, 3+count)
188 | helper.WriteUint16(buf, uint16(length))
189 | buf[2] = uint8(count)
190 | copy(buf[3:], b)
191 | }
192 | case FuncCodeReadHoldingRegisters:
193 | if length == 2 {
194 | code = 6
195 | } else {
196 | code = 16 //0x10
197 | b := make([]byte, 3+length)
198 | helper.WriteUint16(b, uint16(length/2))
199 | b[2] = uint8(length)
200 | copy(b[3:], buf)
201 | buf = b
202 | }
203 | default:
204 | return errors.New("功能码不支持")
205 | }
206 |
207 | l := 10 + len(buf)
208 | b := make([]byte, l)
209 | //helper.WriteUint16(b, id)
210 | helper.WriteUint16(b[2:], 0) //协议版本
211 | helper.WriteUint16(b[4:], 6) //剩余长度
212 | b[6] = byte(station)
213 | b[7] = code
214 | helper.WriteUint16(b[8:], addr.Offset)
215 | copy(b[10:], buf)
216 |
217 | _, err := m.execute(b, true)
218 | return err
219 | }
220 |
--------------------------------------------------------------------------------
/mitsubishi/qna3e.go:
--------------------------------------------------------------------------------
1 | package mitsubishi
2 |
3 | import (
4 | "errors"
5 | "fmt"
6 | helper2 "github.com/zgwit/go-plc/helper"
7 | "io"
8 | "strconv"
9 | )
10 |
11 | // A3EAdapter 协议
12 | type A3EAdapter struct {
13 | StationNumber byte //站编号
14 | NetworkNumber byte //网络编号
15 | PlcNumber byte //PLC编号
16 | IoNumber byte //IO编号
17 |
18 | link io.ReadWriter
19 | }
20 |
21 | func NewA3EAdapter() *A3EAdapter {
22 | a := A3EAdapter{}
23 | a.StationNumber = 0
24 | a.NetworkNumber = 0
25 | a.PlcNumber = 0xFF
26 | a.IoNumber = 0xFF
27 | return &a
28 | }
29 |
30 | func (t *A3EAdapter) request(cmd []byte) ([]byte, error) {
31 |
32 | // 副标题 D 0 0 0 网络号 0 0 PLC号 F F IO编号 0 3 F F 站号 0 0 应答长度 H . . L 结束代码 H . . L
33 | //
34 | buf := make([]byte, 22)
35 | //_, e := t.link.Read(buf)
36 | //if e != nil {
37 | // return nil, e
38 | //}
39 |
40 | status := helper2.ParseUint32(buf[12:])
41 | if status != 0 {
42 | return nil, errors.New(fmt.Sprintf("TCP状态错误: %d", status))
43 | }
44 |
45 | length := helper2.ParseUint32(buf[4:]) - 8
46 |
47 | payload := make([]byte, length)
48 | //t.link.Read(payload)
49 |
50 | return payload, nil
51 | }
52 |
53 | func (t *A3EAdapter) BuildCommand(cmd []byte) []byte {
54 | length := len(cmd)
55 |
56 | buf := make([]byte, 22+length)
57 |
58 | copy(buf[:3], []byte("5000")) //副标题 读取
59 |
60 | helper2.WriteByteHex(buf[4:], t.NetworkNumber) //网络编号
61 | helper2.WriteByteHex(buf[6:], t.PlcNumber) //PLC编号
62 | helper2.WriteByteHex(buf[8:], t.IoNumber) //目标模块IO编号
63 | helper2.WriteByteHex(buf[10:], 0xFF)
64 | helper2.WriteByteHex(buf[12:], t.StationNumber) //站编号
65 | helper2.WriteUint16Hex(buf[14:], uint16(length+4)) // 请求数据长度
66 | copy(buf[18:], []byte("0010")) // CPU监视定时器
67 |
68 | //命令
69 | copy(buf[22:], cmd)
70 |
71 | return buf
72 | }
73 |
74 | // Read 读取数据
75 | func (t *A3EAdapter) Read(address string, length int) ([]byte, error) {
76 |
77 | //解析地址
78 | addr, e := ParseAddress(address)
79 | if e != nil {
80 | return nil, e
81 | }
82 |
83 | buf := make([]byte, 20)
84 | copy(buf, []byte("0401")) //命令 读取
85 | if addr.IsBit {
86 | copy(buf[4:], []byte("0001")) //子命令 按位
87 | } else {
88 | copy(buf[4:], []byte("0000")) //子命令 按字
89 | }
90 | copy(buf[8:], addr.Name) // 软元件类型
91 | if addr.Base == 10 {
92 | copy(buf[10:], fmt.Sprintf("%d6", addr.Addr)) // 起始地址的地位
93 | } else {
94 | copy(buf[10:], fmt.Sprintf("%X6", addr.Addr)) // 起始地址的地位
95 | }
96 | copy(buf[16:], fmt.Sprintf("%X4", length)) // 软元件点数
97 |
98 | //构建命令
99 | t.BuildCommand(buf)
100 |
101 | //发送命令
102 |
103 | //如果不是位,需要纠正长度
104 | if !addr.IsBit {
105 | length = length * 4
106 | }
107 |
108 | //接收响应
109 | recv := make([]byte, 11+length)
110 | //if _, err := t.link.Read(recv); err != nil {
111 | // return nil, err
112 | //}
113 |
114 | // 正确
115 | // STX 帧识别号 站编号 网络编号 PLC编号 上位站编号 ---内容--- ETX 和校验
116 | // 0x20 F 9 0 0 0 0 F F 0 0 .... 0x03 0 0
117 | data := recv[11:length]
118 |
119 | if addr.IsBit {
120 | //0x31 => 0x01, 0x30 => 0
121 | r := make([]byte, length)
122 | for i, v := range data {
123 | if v == '1' {
124 | r[i] = 1
125 | } else {
126 | r[i] = 0
127 | }
128 | }
129 | } else {
130 | //每4字节,表示一个十六进制
131 | r := make([]byte, length/2)
132 | for i := 0; i < length; i += 4 {
133 | d, _ := strconv.ParseUint(string(data[i:i+4]), 16, 32)
134 | //TODO 大小端需要再去确认
135 | r[i*2] = byte(d << 8)
136 | r[i*2+1] = byte(d)
137 | }
138 | }
139 |
140 | // 错误
141 | // NAK 帧识别号 站编号 网络编号 PLC编号 上位站编号 ---内容--- 错误码
142 | // 0x15 F 9 0 0 0 0 F F 0 0 0 0 0 0
143 |
144 | return buf, nil
145 | }
146 |
147 | func (t *A3EAdapter) Write(address string, values []byte) error {
148 | //解析地址
149 | addr, e := ParseAddress(address)
150 | if e != nil {
151 | return e
152 | }
153 |
154 | length := len(values)
155 |
156 | var value []byte
157 |
158 | //数据预处理,位数据要转成0和1,字数据
159 | if addr.IsBit {
160 | value = helper2.BoolToAscii(values)
161 | } else {
162 | //uint16 数组转字符串
163 | value := make([]byte, length*2)
164 | for i := 0; i < length/2; i++ {
165 | d := uint16(values[i])<<8 + uint16(values[i+1])
166 | if addr.Base == 10 {
167 | copy(value[i*4:], fmt.Sprintf("%d4", d))
168 | } else {
169 | copy(value[i*4:], fmt.Sprintf("%X4", d))
170 | }
171 | }
172 |
173 | length = len(value) //length * 2
174 | }
175 |
176 | buf := make([]byte, 20+length)
177 | copy(buf, []byte("1401")) //命令 读取
178 | if addr.IsBit {
179 | copy(buf[4:], []byte("0001")) //子命令 按位
180 | } else {
181 | copy(buf[4:], []byte("0000")) //子命令 按字
182 | }
183 | copy(buf[8:], addr.Name) // 软元件类型
184 | if addr.Base == 10 {
185 | copy(buf[10:], fmt.Sprintf("%d6", addr.Addr)) // 起始地址的地位
186 | } else {
187 | copy(buf[10:], fmt.Sprintf("%X6", addr.Addr)) // 起始地址的地位
188 | }
189 | if addr.IsBit {
190 | copy(buf[16:], fmt.Sprintf("%X4", length)) // 软元件点数
191 | } else {
192 | copy(buf[16:], fmt.Sprintf("%X4", length/4)) // 软元件点数
193 | }
194 |
195 | //附加数据
196 | copy(buf[20:], value)
197 |
198 | //构建命令
199 | t.BuildCommand(buf)
200 |
201 | //发送命令
202 |
203 | //接收响应
204 | //recv := make([]byte, 15)
205 | //if _, err := t.link.Read(recv); err != nil {
206 | // return err
207 | //}
208 |
209 | // 正确
210 | // ACK 帧识别号 站编号 网络编号 PLC编号 上位站编号
211 | // 0x06 F 9 0 0 0 0 F F 0 0
212 |
213 | // 错误
214 | // NAK 帧识别号 站编号 网络编号 PLC编号 上位站编号 错误码
215 | // 0x15 F 9 0 0 0 0 F F 0 0 0 0 0 0
216 |
217 | return nil
218 | }
219 |
--------------------------------------------------------------------------------
/siemens/s7package.go:
--------------------------------------------------------------------------------
1 | package siemens
2 |
3 | import (
4 | "fmt"
5 | "github.com/zgwit/go-plc/helper"
6 | )
7 |
8 | const (
9 | VariableTypeBit = 1
10 | VariableTypeWord = 2
11 | VariableTypeInt = 3
12 | VariableTypeReal = 4
13 | VariableTypeCounter = 5
14 |
15 | ParameterTypeRead = 4
16 | ParameterTypeWrite = 5
17 |
18 | MessageTypeJobRequest = 1
19 | MessageTypeAck = 2
20 | MessageTypeAckData = 3
21 | MessageTypeUserData = 4
22 |
23 | TransportSizeBit = 3
24 | TransportSizeByte = 4
25 | TransportSizeInt = 5
26 | TransportSizeReal = 7
27 | TransportSizeOctet = 9
28 | )
29 |
30 | //S7Parameter 固定14字节长
31 | type S7Parameter struct {
32 | Code uint8 //04 read 05 write
33 | Count uint8
34 | Type uint8 //数据类型 1 bit 2 word 3 dint 4 real 5 counter
35 |
36 | Areas []S7ParameterArea
37 | }
38 |
39 | type S7ParameterArea struct {
40 | Code uint8
41 | DB uint16
42 | Size uint16
43 | Offset uint32
44 | }
45 |
46 | func (p *S7Parameter) encode() []byte {
47 | blocks := uint8(len(p.Areas))
48 | buf := make([]byte, blocks*8+6)
49 |
50 | buf[0] = p.Code // 4读 5写
51 | buf[1] = blocks // 读取块数
52 | buf[2] = 0x12 //specification type 指定有效值类型
53 | buf[3] = blocks*8 + 2 //length 接下来本次地址访问长度
54 | buf[4] = 0x10 //syntax id 语法标记,ANY
55 | buf[5] = p.Type //variable type 1 bit 2 word 3 dint 4 real 5 counter???
56 |
57 | cursor := 6
58 | for i, area := range p.Areas {
59 | helper.WriteUint16(buf[cursor:], area.Size) // 访问数据的个数
60 | helper.WriteUint16(buf[cursor+2:], area.DB) //db number DB块编号,如果访问的是DB块的话
61 | buf[cursor+4] = area.Code //area 访问数据类型
62 | helper.WriteUint24(buf[cursor+5:], area.Offset) //address 偏移位置
63 | cursor += i * 8
64 | }
65 | return buf
66 | }
67 |
68 | func (p *S7Parameter) decode(buf []byte) error {
69 | p.Code = buf[0]
70 | p.Count = buf[1]
71 | //返回内容 只有以上肉个字节
72 |
73 | return nil
74 | }
75 |
76 | type S7Data struct {
77 | Code uint8 //0xff代表成功
78 | Type uint8
79 | Count uint16
80 | Data []byte
81 | }
82 |
83 | func (p *S7Data) encode() []byte {
84 | data := p.Data
85 | if p.Type == VariableTypeBit {
86 | data = helper.ShrinkBool(data)
87 | p.Count = uint16(len(data))
88 | }
89 |
90 | l := uint8(len(data))
91 | buf := make([]byte, l+4)
92 |
93 | buf[0] = p.Code
94 | buf[1] = p.Type
95 | helper.WriteUint16(buf[2:], p.Count)
96 | copy(buf[4:], data)
97 | return buf
98 | }
99 |
100 | func (p *S7Data) decode(buf []byte) (int, error) {
101 | p.Code = buf[0]
102 | //写入返回
103 | if len(buf) < 4 {
104 | return 0, fmt.Errorf("长度太短")
105 | }
106 |
107 | p.Type = buf[1]
108 | p.Count = helper.ParseUint16(buf[2:])
109 | length := int(p.Count)
110 | p.Data = buf[4 : length+4]
111 | if p.Type == VariableTypeBit {
112 | p.Data = helper.ExpandBool(buf, length*8)
113 | }
114 | return length + 4, nil
115 | }
116 |
117 | type S7Package struct {
118 | Type uint8 // 1 Job Request 2 Ack 3 Ack-Data 7 Userdata
119 | Reference uint16 //序列号
120 |
121 | param S7Parameter
122 | data []S7Data
123 | //ErrorClass uint8
124 | //ErrorCode uint8
125 | }
126 |
127 | func (p *S7Package) encode() []byte {
128 |
129 | parameter := p.param.encode()
130 | paramLength := len(parameter)
131 |
132 | dataLength := 0
133 | datum := make([][]byte, 0)
134 | for _, p := range p.data {
135 | buf := p.encode()
136 | datum = append(datum, buf)
137 | dataLength += len(buf)
138 | }
139 |
140 | size := paramLength + dataLength + 17
141 |
142 | buf := make([]byte, size)
143 | //TPKT
144 | buf[0] = 0x03
145 | buf[1] = 0x00
146 | helper.WriteUint16(buf[2:], uint16(size)) // 长度
147 | //ISO-COTP
148 | buf[4] = 0x02 // 固定
149 | buf[5] = 0xF0
150 | buf[6] = 0x80
151 | //S7 communication
152 | buf[7] = 0x32 //Desc ID 协议ID,固定为32
153 | buf[8] = p.Type //Message Type(ROSCTR) 1 Job Request 2 Ack 3 Ack-Data 7 Userdata
154 | buf[9] = 0x0 //Reserved
155 | buf[10] = 0x0
156 | helper.WriteUint16LittleEndian(buf[11:], p.Reference) // PDU ref 标识序列号(可以像Modbus TCP一样使用)
157 | helper.WriteUint16(buf[13:], uint16(paramLength)) // Param length
158 | helper.WriteUint16(buf[15:], uint16(dataLength)) // Data length
159 |
160 | //复制参数
161 | cursor := 17
162 | if paramLength > 0 {
163 | copy(buf[cursor:], parameter)
164 | cursor += paramLength
165 | }
166 |
167 | //复制数据
168 | if dataLength > 0 {
169 | for _, p := range datum {
170 | copy(buf[cursor:], p)
171 | cursor += len(p)
172 | }
173 | }
174 |
175 | return buf
176 | }
177 |
178 | func (p *S7Package) decode(buf []byte) error {
179 | length := helper.ParseUint16(buf[2:])
180 | if len(buf) < int(length) {
181 | return fmt.Errorf("长度不够 %d %d", length, len(buf))
182 | }
183 |
184 | p.Type = buf[8]
185 | p.Reference = helper.ParseUint16LittleEndian(buf[11:])
186 | paramLength := helper.ParseUint16(buf[13:])
187 | dataLength := helper.ParseUint16(buf[15:])
188 |
189 | //仅当 ack-data
190 | ErrorClass := buf[17]
191 | ErrorCode := buf[18]
192 | if ErrorClass != 0 || ErrorCode != 0 {
193 | return fmt.Errorf("错误码:%d %d", ErrorClass, ErrorCode)
194 | }
195 |
196 | err := p.param.decode(buf[19:])
197 | if err != nil {
198 | return err
199 | }
200 |
201 | if dataLength == 0 {
202 | return nil
203 | }
204 |
205 | p.data = make([]S7Data, 0)
206 |
207 | cursor := int(paramLength) + 19
208 | remain := int(length) - cursor
209 | if p.Type == MessageTypeAckData && p.param.Code == ParameterTypeWrite {
210 | for remain > 0 {
211 | var d S7Data
212 | d.Code = buf[cursor]
213 | p.data = append(p.data, d)
214 | cursor++
215 | remain--
216 | }
217 | } else {
218 | for remain > 0 {
219 | var d S7Data
220 | cnt, err := d.decode(buf[cursor:])
221 | if err != nil {
222 | return err
223 | }
224 | p.data = append(p.data, d)
225 | cursor += cnt
226 | remain -= cnt
227 | }
228 | }
229 |
230 | return nil
231 | }
232 |
--------------------------------------------------------------------------------
/helper/bytes.go:
--------------------------------------------------------------------------------
1 | package helper
2 |
3 | import "math"
4 |
5 | // ParseUint64 解析
6 | func ParseUint64(b []byte) uint64 {
7 | return (uint64(b[0]) << 56) |
8 | (uint64(b[1]) << 48) |
9 | (uint64(b[2]) << 40) |
10 | (uint64(b[3]) << 32) |
11 | (uint64(b[4]) << 24) |
12 | (uint64(b[5]) << 16) |
13 | (uint64(b[6]) << 8) |
14 | uint64(b[7])
15 | }
16 |
17 | // ParseUint64LittleEndian 解析
18 | func ParseUint64LittleEndian(b []byte) uint64 {
19 | return (uint64(b[7]) << 56) |
20 | (uint64(b[6]) << 48) |
21 | (uint64(b[5]) << 40) |
22 | (uint64(b[4]) << 32) |
23 | (uint64(b[3]) << 24) |
24 | (uint64(b[2]) << 16) |
25 | (uint64(b[1]) << 8) |
26 | uint64(b[0])
27 | }
28 |
29 | // ParseUint32 解析
30 | func ParseUint32(buf []byte) uint32 {
31 | return uint32(buf[0])<<24 +
32 | uint32(buf[1])<<16 +
33 | uint32(buf[2])<<8 +
34 | uint32(buf[3])
35 | }
36 |
37 | // ParseUint32LittleEndian 解析
38 | func ParseUint32LittleEndian(buf []byte) uint32 {
39 | return uint32(buf[3])<<24 +
40 | uint32(buf[2])<<16 +
41 | uint32(buf[1])<<8 +
42 | uint32(buf[0])
43 | }
44 |
45 | // ParseUint16 解析
46 | func ParseUint16(buf []byte) uint16 {
47 | return uint16(buf[0])<<8 + uint16(buf[1])
48 | }
49 |
50 | // ParseUint16LittleEndian 解析
51 | func ParseUint16LittleEndian(buf []byte) uint16 {
52 | return uint16(buf[1])<<8 + uint16(buf[0])
53 | }
54 |
55 | // ParseFloat32 解析
56 | func ParseFloat32(buf []byte) float32 {
57 | val := ParseUint32(buf)
58 | return math.Float32frombits(val)
59 | }
60 |
61 | // ParseFloat32LittleEndian 解析
62 | func ParseFloat32LittleEndian(buf []byte) float32 {
63 | val := ParseUint32LittleEndian(buf)
64 | return math.Float32frombits(val)
65 | }
66 |
67 | // ParseFloat64 解析
68 | func ParseFloat64(buf []byte) float64 {
69 | val := ParseUint64(buf)
70 | return math.Float64frombits(val)
71 | }
72 |
73 | // ParseFloat64LittleEndian 解析
74 | func ParseFloat64LittleEndian(buf []byte) float64 {
75 | val := ParseUint64LittleEndian(buf)
76 | return math.Float64frombits(val)
77 | }
78 |
79 | // Uint32ToBytes 编码
80 | func Uint32ToBytes(value uint32) []byte {
81 | buf := make([]byte, 4)
82 | buf[0] = byte(value >> 24)
83 | buf[1] = byte(value >> 16)
84 | buf[2] = byte(value >> 8)
85 | buf[3] = byte(value)
86 | return buf
87 | }
88 |
89 | // Uint32ToBytesLittleEndian 编码
90 | func Uint32ToBytesLittleEndian(value uint32) []byte {
91 | buf := make([]byte, 4)
92 | buf[3] = byte(value >> 24)
93 | buf[2] = byte(value >> 16)
94 | buf[1] = byte(value >> 8)
95 | buf[0] = byte(value)
96 | return buf
97 | }
98 |
99 | // Uint16ToBytes 编码
100 | func Uint16ToBytes(value uint16) []byte {
101 | buf := make([]byte, 2)
102 | buf[0] = byte(value >> 8)
103 | buf[1] = byte(value)
104 | return buf
105 | }
106 |
107 | // Uint16ToBytesLittleEndian 编码
108 | func Uint16ToBytesLittleEndian(value uint16) []byte {
109 | buf := make([]byte, 2)
110 | buf[1] = byte(value >> 8)
111 | buf[0] = byte(value)
112 | return buf
113 | }
114 |
115 | // WriteUint64 编码
116 | func WriteUint64(buf []byte, value uint64) {
117 | buf[0] = byte(value >> 56)
118 | buf[1] = byte(value >> 48)
119 | buf[2] = byte(value >> 40)
120 | buf[3] = byte(value >> 32)
121 | buf[4] = byte(value >> 24)
122 | buf[5] = byte(value >> 16)
123 | buf[6] = byte(value >> 8)
124 | buf[7] = byte(value)
125 | }
126 |
127 | // WriteUint64LittleEndian 编码
128 | func WriteUint64LittleEndian(buf []byte, value uint64) {
129 | buf[7] = byte(value >> 56)
130 | buf[6] = byte(value >> 48)
131 | buf[5] = byte(value >> 40)
132 | buf[4] = byte(value >> 32)
133 | buf[3] = byte(value >> 24)
134 | buf[2] = byte(value >> 16)
135 | buf[1] = byte(value >> 8)
136 | buf[0] = byte(value)
137 | }
138 |
139 | // WriteUint32 编码
140 | func WriteUint32(buf []byte, value uint32) {
141 | buf[0] = byte(value >> 24)
142 | buf[1] = byte(value >> 16)
143 | buf[2] = byte(value >> 8)
144 | buf[3] = byte(value)
145 | }
146 |
147 | // WriteUint32LittleEndian 编码
148 | func WriteUint32LittleEndian(buf []byte, value uint32) {
149 | buf[3] = byte(value >> 24)
150 | buf[2] = byte(value >> 16)
151 | buf[1] = byte(value >> 8)
152 | buf[0] = byte(value)
153 | }
154 |
155 | // WriteUint24 编码
156 | func WriteUint24(buf []byte, value uint32) {
157 | buf[0] = byte(value >> 16)
158 | buf[1] = byte(value >> 8)
159 | buf[2] = byte(value)
160 | }
161 |
162 | // WriteUint24LittleEndian 编码
163 | func WriteUint24LittleEndian(buf []byte, value uint32) {
164 | buf[2] = byte(value >> 16)
165 | buf[1] = byte(value >> 8)
166 | buf[0] = byte(value)
167 | }
168 |
169 | // WriteUint16 编码
170 | func WriteUint16(buf []byte, value uint16) {
171 | buf[0] = byte(value >> 8)
172 | buf[1] = byte(value)
173 | }
174 |
175 | // WriteUint16LittleEndian 编码
176 | func WriteUint16LittleEndian(buf []byte, value uint16) {
177 | buf[1] = byte(value >> 8)
178 | buf[0] = byte(value)
179 | }
180 |
181 | // WriteFloat32 编码
182 | func WriteFloat32(buf []byte, value float32) {
183 | val := math.Float32bits(value)
184 | WriteUint32(buf, val)
185 | }
186 |
187 | // WriteFloat32LittleEndian 编码
188 | func WriteFloat32LittleEndian(buf []byte, value float32) {
189 | val := math.Float32bits(value)
190 | WriteUint32LittleEndian(buf, val)
191 | }
192 |
193 | // WriteFloat64 编码
194 | func WriteFloat64(buf []byte, value float64) {
195 | val := math.Float64bits(value)
196 | WriteUint64(buf, val)
197 | }
198 |
199 | // WriteFloat64LittleEndian 编码
200 | func WriteFloat64LittleEndian(buf []byte, value float64) {
201 | val := math.Float64bits(value)
202 | WriteUint64LittleEndian(buf, val)
203 | }
204 |
205 | // BoolToAscii 编码
206 | func BoolToAscii(buf []byte) []byte {
207 | length := len(buf)
208 | ret := make([]byte, length)
209 | for i := 0; i < length; i++ {
210 | if buf[i] == 0 {
211 | ret[i] = '0'
212 | } else {
213 | ret[i] = '1'
214 | }
215 | }
216 | return ret
217 | }
218 |
219 | // AsciiToBool 编码
220 | func AsciiToBool(buf []byte) []byte {
221 | length := len(buf)
222 | ret := make([]byte, length)
223 | for i := 0; i < length; i++ {
224 | if buf[i] == '0' {
225 | ret[i] = 0
226 | } else {
227 | ret[i] = 1
228 | }
229 | }
230 | return ret
231 | }
232 |
233 | // Dup 复制
234 | func Dup(buf []byte) []byte {
235 | b := make([]byte, len(buf))
236 | copy(b, buf)
237 | return b
238 | }
239 |
240 | // BoolToByte 编码
241 | func BoolToByte(buf []bool) []byte {
242 | r := make([]byte, len(buf))
243 | for i, v := range buf {
244 | if v {
245 | r[i] = 1
246 | }
247 | }
248 | return r
249 | }
250 |
251 | // ByteToBool 编码
252 | func ByteToBool(buf []byte) []bool {
253 | r := make([]bool, len(buf))
254 | for i, v := range buf {
255 | if v > 0 {
256 | r[i] = true
257 | }
258 | }
259 | return r
260 | }
261 |
262 | // ShrinkBool 压缩布尔类型
263 | func ShrinkBool(buf []byte) []byte {
264 | length := len(buf)
265 | //length = length % 8 == 0 ? length / 8 : length / 8 + 1;
266 | ln := length >> 3 // length/8
267 | if length&0x07 > 0 { // length%8
268 | ln++
269 | }
270 |
271 | b := make([]byte, ln)
272 |
273 | for i := 0; i < length; i++ {
274 | if buf[i] > 0 {
275 | //b[i/8] += 1 << (i % 8)
276 | b[i>>3] += 1 << (i & 0x07)
277 | }
278 | }
279 |
280 | return b
281 | }
282 |
283 | // ExpandBool 展开布尔类型
284 | func ExpandBool(buf []byte, count int) []byte {
285 | length := len(buf)
286 | ln := length << 3 // length * 8
287 | if count > ln {
288 | count = ln
289 | }
290 | b := make([]byte, count)
291 |
292 | for i := 0; i < length; i++ {
293 | //b[i] = buf[i/8] & (1 << (i % 8))
294 | if buf[i>>3]&(1<<(i&0x07)) > 0 {
295 | b[i] = 1
296 | }
297 | }
298 |
299 | return b
300 | }
301 |
--------------------------------------------------------------------------------