forked from lorenzodonini/ocpp-go
/
state.go
219 lines (196 loc) · 6.9 KB
/
state.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
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
package ocppj
import (
"sync"
"github.com/lorenzodonini/ocpp-go/ocpp"
)
// Contains the pending request state for messages, associated to a single client-server channel.
// It is used to separate endpoint logic from state management.
type ClientState interface {
// Sets a Request as pending on the endpoint. Requests are considered pending until a response was received.
// The function expects a unique message ID and the Request.
// If an element with the same requestID exists, the new one will be ignored.
AddPendingRequest(requestID string, req ocpp.Request)
// Retrieves a pending Request, using the message ID.
// If no request for the passed message ID is found, a false flag is returned.
GetPendingRequest(requestID string) (ocpp.Request, bool)
// Deletes a pending Request from the endpoint, using the message ID.
// If no such message is currently stored as pending, the call has no effect.
DeletePendingRequest(requestID string)
// Clears all currently pending requests. Any confirmation/error,
// received as a response to a cleared request, will be ignored.
ClearPendingRequests()
// Returns true if there currently is at least one pending request, false otherwise.
HasPendingRequest() bool
}
// ----------------------------
// Request State implementation
// ----------------------------
// Simple implementation of ClientState.
// Supports a single pending request. To add a new pending request, the previous one needs to be deleted.
//
// Uses a mutex internally for concurrent access to the data struct.
type clientState struct {
requestID string
pendingRequest pendingRequest
mutex sync.RWMutex
}
// Creates a simple struct implementing ClientState, to be used by client/server dispatchers.
func NewClientState() ClientState {
return &clientState{}
}
func (s *clientState) AddPendingRequest(requestID string, req ocpp.Request) {
s.mutex.Lock()
defer s.mutex.Unlock()
if requestID != "" && s.requestID == "" {
s.requestID = requestID
s.pendingRequest = pendingRequest{
request: req,
}
}
}
func (s *clientState) GetPendingRequest(requestID string) (ocpp.Request, bool) {
s.mutex.Lock()
defer s.mutex.Unlock()
if s.requestID != requestID {
return nil, false
}
return s.pendingRequest.request, true
}
func (s *clientState) DeletePendingRequest(requestID string) {
s.mutex.Lock()
defer s.mutex.Unlock()
if s.requestID != requestID {
return
}
s.requestID = ""
}
func (s *clientState) ClearPendingRequests() {
s.mutex.Lock()
defer s.mutex.Unlock()
s.requestID = ""
}
func (s *clientState) HasPendingRequest() bool {
s.mutex.Lock()
defer s.mutex.Unlock()
return s.requestID != ""
}
// Contains the pending request state for messages associated to all client-server channels.
// It is used to separate endpoint logic from state management.
type ServerState interface {
// Sets a Request as pending on the endpoint, for a specific client.
// Requests are considered pending until a response was received.
// The function expects a client ID, a unique message ID and the Request itself.
// If an element with the same clientID/requestID exists, the new one will be ignored.
AddPendingRequest(clientID string, requestID string, req ocpp.Request)
// Deletes a pending Request from the endpoint, for a specific client, using the message ID.
// If no such message is currently stored as pending, the call has no effect.
DeletePendingRequest(clientID string, requestID string)
// Retrieves a ClientState object, associated to a specific client.
// If no such state exists, an empty state is returned.
GetClientState(clientID string) ClientState
// Returns true if there currently are pending requests for a client, false otherwise.
HasPendingRequest(clientID string) bool
// Returns true if there currently is at least one pending request, false otherwise.
HasPendingRequests() bool
// Clears currently pending requests for a client. Any confirmation/error,
// received as a response to a cleared request, will be ignored.
ClearClientPendingRequest(clientID string)
// Clears all currently pending requests inside the map. Any confirmation/error,
// received as a response to a previously sent request, will be ignored.
//
// Does not perform a deep deletion; is references to client state objects
// are stored elsewhere, those will remain unaffected and become invalid.
ClearAllPendingRequests()
}
// --------------------------------
// Request State Map implementation
// --------------------------------
// Simple implementation of ServerState, using a map.
// Supports any amount of clients and stores the pending requests for each client in a
// clientState struct.
//
// Client data is not deleted automatically; it should be deleted after a client session has ended.
//
// May internally use a mutex for concurrent access to the data struct.
// See NewServerState for more info.
type serverState struct {
pendingRequestState map[string]ClientState
mutex *sync.RWMutex
}
// Creates a simple struct implementing ServerState, to be used by server dispatchers.
//
// If no mutex is passed, then atomic access to the data struct is not guaranteed, and race conditions may arise.
func NewServerState(m *sync.RWMutex) ServerState {
return &serverState{
pendingRequestState: map[string]ClientState{},
mutex: m,
}
}
func (d *serverState) AddPendingRequest(clientID string, requestID string, req ocpp.Request) {
if d.mutex != nil {
d.mutex.Lock()
defer d.mutex.Unlock()
}
state := d.getOrCreateState(clientID)
state.AddPendingRequest(requestID, req)
}
func (d *serverState) DeletePendingRequest(clientID string, requestID string) {
if d.mutex != nil {
d.mutex.Lock()
defer d.mutex.Unlock()
}
state, exists := d.pendingRequestState[clientID]
if !exists {
return
}
state.DeletePendingRequest(requestID)
}
func (d *serverState) GetClientState(clientID string) ClientState {
if d.mutex != nil {
d.mutex.Lock()
defer d.mutex.Unlock()
}
return d.getOrCreateState(clientID)
}
func (d *serverState) HasPendingRequest(clientID string) bool {
if d.mutex != nil {
d.mutex.Lock()
defer d.mutex.Unlock()
}
state, exists := d.pendingRequestState[clientID]
return exists && state.HasPendingRequest()
}
func (d *serverState) HasPendingRequests() bool {
if d.mutex != nil {
d.mutex.Lock()
defer d.mutex.Unlock()
}
for _, s := range d.pendingRequestState {
if s.HasPendingRequest() {
return true
}
}
return false
}
func (d *serverState) ClearClientPendingRequest(clientID string) {
if d.mutex != nil {
d.mutex.Lock()
defer d.mutex.Unlock()
}
delete(d.pendingRequestState, clientID)
}
func (d *serverState) ClearAllPendingRequests() {
if d.mutex != nil {
d.mutex.Lock()
defer d.mutex.Unlock()
}
d.pendingRequestState = map[string]ClientState{}
}
func (d *serverState) getOrCreateState(clientID string) ClientState {
state, exists := d.pendingRequestState[clientID]
if !exists {
state = NewClientState()
d.pendingRequestState[clientID] = state
}
return state
}