-
Notifications
You must be signed in to change notification settings - Fork 15
/
Copy pathpath.go
100 lines (87 loc) · 2.93 KB
/
path.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
package gologix
// based on code from https://github.com/loki-os/go-ethernet-ip
import (
"bytes"
"fmt"
"strconv"
"strings"
)
// The path is formatted like this.
// byte 0: number of 16 bit words
// byte 1: 000. .... path segment type (port segment = 0)
// byte 1: ...0 .... extended link address (0 = false)
// byte 1: .... 0001 port (backplane = 1)
// byte 2: n/a
// byte 3: 001. .... path segment type (logical segment = 1)
// byte 3: ...0 00.. logical segment type class ID (0)
// byte 3: .... ..00 logical segment format: 8-bit (0)
// byte 4: path segment 0x20
// byte 5: 001. .... path segment type (logical segment = 1)
// byte 5: ...0 01.. logical segment type: Instance ID = 1
// byte 5: .... ..00 logical segment format: 8-bit (0)
// byte 6: path segment instance 0x01
// so on...
//msg.Path = [6]byte{0x01, 0x00, 0x20, 0x02, 0x24, 0x01}
// bits 5,6,7 (counting from 0) are the segment type
type segmentType byte
const (
segmentTypeExtendedSymbolic segmentType = 0x91
)
// represents the port number on a CIP device
//
// If you're going to serialize this class to bytes for transimssion be sure to use one of the gologix
// serialization functions or call Bytes() to get the properly formatted data.
type CIPPort struct {
PortNo byte
ExtensionLen byte
}
func (p CIPPort) Len() int {
return 2
}
func (p CIPPort) Bytes() []byte {
if p.ExtensionLen != 0 {
return []byte{p.PortNo, p.ExtensionLen}
}
return []byte{p.PortNo}
}
// This function takes a CIP path in the format of 0,1,192.168.2.1,0,1 and converts it into the proper equivalent byte slice.
//
// The most common use is probably setting up the communication path on a new client.
func ParsePath(path string) (*bytes.Buffer, error) {
if path == "" {
return new(bytes.Buffer), nil
}
// get rid of any spaces and square brackets
path = strings.ReplaceAll(path, " ", "")
path = strings.ReplaceAll(path, "[", "")
path = strings.ReplaceAll(path, "]", "")
// split on commas
parts := strings.Split(path, ",")
byte_path := make([]byte, 0, len(parts))
for _, part := range parts {
// first see if this looks like an IP address.
is_ip := strings.Contains(part, ".")
if is_ip {
// for some god forsaken reason the path doesn't use the ip address as actual bytes but as an ascii string.
// we first have to set bit 5 in the previous byte to say we're using an extended address for this part.
last_pos := len(byte_path) - 1
last_byte := byte_path[last_pos]
byte_path[last_pos] = last_byte | 1<<4
l := len(part)
byte_path = append(byte_path, byte(l))
string_bytes := []byte(part)
byte_path = append(byte_path, string_bytes...)
continue
}
// not an IP address
val, err := strconv.Atoi(part)
if err != nil {
return nil, fmt.Errorf("problem converting %v to number. %w", part, err)
}
if val < 0 || val > 255 {
return nil, fmt.Errorf("number out of range. %v", part)
}
byte_path = append(byte_path, byte(val))
}
return bytes.NewBuffer(byte_path), nil
}