-
Notifications
You must be signed in to change notification settings - Fork 14
/
Copy pathdisconnect.go
121 lines (109 loc) · 3.46 KB
/
disconnect.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
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
package gologix
import (
"fmt"
"log/slog"
"time"
)
// You will want to defer this after a successful Connect() to make sure you free up the controller resources
// to disconnect we send two items - a null item and an unconnected data item for the unregister service
func (client *Client) Disconnect() error {
if client.connecting {
client.Logger.Debug("waiting for client to finish connecting before disconnecting")
for client.connecting {
time.Sleep(time.Millisecond * 10)
}
}
if !client.connected || client.disconnecting {
return nil
}
client.disconnecting = true
defer func() { client.disconnecting = false }()
client.connected = false
var err error
client.Logger.Info("starting disconnection")
if client.keepAliveRunning {
close(client.cancel_keepalive)
}
items := make([]CIPItem, 2)
items[0] = CIPItem{} // null item
items[1] = CIPItem{Header: cipItemHeader{ID: cipItem_UnconnectedData}}
path, err := Serialize(
client.Controller.Path,
CipObject_MessageRouter,
CIPInstance(1),
)
if err != nil {
client.Logger.Error("Error serializing path", slog.Any("err", err))
return fmt.Errorf("error serializing path: %w", err)
}
msg := msgCipUnRegister{
Service: CIPService_ForwardClose,
CipPathSize: 0x02,
ClassType: cipClass_8bit,
Class: 0x06,
InstanceType: cipInstance_8bit,
Instance: 0x01,
Priority: 0x0A,
TimeoutTicks: 0x0E,
ConnectionSerialNumber: client.ConnectionSerialNumber,
VendorID: client.VendorId,
OriginatorSerialNumber: client.SerialNumber,
PathSize: byte(path.Len() / 2),
Reserved: 0x00,
}
items[1].Serialize(msg)
items[1].Serialize(path)
itemData, err := serializeItems(items)
if err != nil {
client.Logger.Error(
"unable to serialize itemData. Forcing connection closed",
slog.Any("err", err),
)
} else {
header, data, err := client.send_recv_data(cipCommandSendRRData, itemData)
if err != nil {
client.Logger.Error(
"error sending disconnect request",
slog.Any("err", err),
)
} else {
_, err = client.parseResponse(&header, data)
if err != nil {
client.Logger.Error(
"error parsing disconnect response",
slog.Any("err", err),
)
}
}
}
err = client.conn.Close()
if err != nil {
client.Logger.Error("error closing connection", slog.Any("err", err))
}
client.Logger.Info("successfully disconnected from controller")
return nil
}
// Cancels keepalive if KeepAliveAutoStart is false. Use force to cancel keepalive regardless.
// If forced, the keepalive will not resume unless the client is reconnected or KeepAlive is triggered
func (client *Client) KeepAliveCancel(force bool) error {
if client.KeepAliveAutoStart && !force {
return fmt.Errorf("unable to cancel keepalive due to AutoKeepAlive == true")
}
close(client.cancel_keepalive)
return nil
}
type msgCipUnRegister struct {
Service CIPService
CipPathSize byte
ClassType cipClassSize
Class byte
InstanceType cipInstanceSize
Instance byte
Priority byte
TimeoutTicks byte
ConnectionSerialNumber uint16
VendorID uint16
OriginatorSerialNumber uint32
PathSize uint8
Reserved byte // Always 0x00
}