├── 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 | --------------------------------------------------------------------------------