Skip to content

Commit 72113e9

Browse files
committed
feat: add exponential backoff for connection retries
- Implement exponential backoff strategy with jitter for socket reconnection attempts - Use base delay (5s) for first 10 attempts, then exponential increase up to max delay (5min) - Track retry count and interval using SocketConnection struct fields - Add proper interval calculation and timeline reloading for backoff phases Improves connection reliability by preventing aggressive retry storms and allowing gradual backoff when devices are temporarily unavailable.
1 parent ced1d30 commit 72113e9

File tree

1 file changed

+111
-17
lines changed

1 file changed

+111
-17
lines changed

src/mWolfVisionVisualizer.axs

Lines changed: 111 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ MODULE_NAME='mWolfVisionVisualizer' (
1313
#include 'NAVFoundation.StringUtils.axi'
1414
#include 'NAVFoundation.TimelineUtils.axi'
1515
#include 'NAVFoundation.ErrorLogUtils.axi'
16+
#include 'NAVFoundation.NetUtils.axi'
17+
#include 'NAVFoundation.Regex.axi'
1618

1719
/*
1820
_ _ _ ___ __
@@ -58,12 +60,9 @@ DEFINE_CONSTANT
5860
constant integer IP_PORT = 50915
5961

6062
constant long TL_DRIVE = 1
61-
constant long TL_IP_CHECK = 2
62-
constant long TL_HEARTBEAT = 3
63+
constant long TL_SOCKET_CHECK = 2
6364

6465
constant long TL_DRIVE_INTERVAL[] = { 200 }
65-
constant long TL_IP_CHECK_INTERVAL[] = { 3000 }
66-
constant long TL_HEARTBEAT_INTERVAL[] = { 20000 }
6766

6867
constant integer GET_POWER = 1
6968

@@ -172,12 +171,20 @@ define_function Drive() {
172171
}
173172

174173

175-
define_function MaintainIpConnection() {
174+
define_function MaintainSocketConnection() {
176175
if (module.Device.SocketConnection.IsConnected) {
177176
return
178177
}
179178

180-
NAVClientSocketOpen(dvPort.PORT,
179+
if (!length_array(module.Device.SocketConnection.Address)) {
180+
return
181+
}
182+
183+
if (module.Device.SocketConnection.Port <= 0) {
184+
return
185+
}
186+
187+
NAVClientSocketOpen(module.Device.SocketConnection.Socket,
181188
module.Device.SocketConnection.Address,
182189
module.Device.SocketConnection.Port,
183190
IP_TCP)
@@ -198,25 +205,76 @@ define_function CommunicationTimeOut(integer timeout) {
198205

199206

200207
define_function Reset() {
208+
cancel_wait 'TimeOut'
209+
201210
module.Device.SocketConnection.IsConnected = false
202211
module.Device.IsCommunicating = false
203212
module.Device.IsInitialized = false
204213
UpdateFeedback()
205214

206-
NAVTimelineStop(TL_HEARTBEAT)
207215
NAVTimelineStop(TL_DRIVE)
208216
}
209217

210218

219+
define_function SocketConnectionReset() {
220+
NAVTimelineStop(TL_SOCKET_CHECK)
221+
222+
if (module.Device.SocketConnection.IsConnected) {
223+
NAVClientSocketClose(module.Device.SocketConnection.Socket)
224+
}
225+
226+
// Always reset retry count for clean state
227+
module.Device.SocketConnection.RetryCount = 0
228+
module.Device.SocketConnection.Interval[1] = NAVSocketGetConnectionInterval(module.Device.SocketConnection.RetryCount)
229+
230+
if (!length_array(module.Device.SocketConnection.Address)) {
231+
return
232+
}
233+
234+
NAVTimelineStart(TL_SOCKET_CHECK,
235+
module.Device.SocketConnection.Interval,
236+
TIMELINE_ABSOLUTE,
237+
TIMELINE_REPEAT)
238+
}
239+
240+
211241
define_function NAVModulePropertyEventCallback(_NAVModulePropertyEvent event) {
212242
switch (event.Name) {
213243
case NAV_MODULE_PROPERTY_EVENT_IP_ADDRESS: {
214-
module.Device.SocketConnection.Address = event.Args[1]
215-
module.Device.SocketConnection.Port = IP_PORT
216-
NAVTimelineStart(TL_IP_CHECK,
217-
TL_IP_CHECK_INTERVAL,
218-
TIMELINE_ABSOLUTE,
219-
TIMELINE_REPEAT)
244+
stack_var _NAVIP ip
245+
stack_var char address[255]
246+
247+
address = NAVTrimString(event.Args[1])
248+
249+
// Handle empty address - clear and reset connection
250+
if (!length_array(address)) {
251+
module.Device.SocketConnection.Address = ''
252+
NAVErrorLog(NAV_LOG_LEVEL_INFO,
253+
"'mWolfVisionVisualizer => Clearing IP address'")
254+
255+
SocketConnectionReset()
256+
return
257+
}
258+
259+
if (!NAVNetParseIP(address, ip)) {
260+
if (NAVRegexTest('/^[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(\.[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/', address)) {
261+
module.Device.SocketConnection.Address = address
262+
NAVErrorLog(NAV_LOG_LEVEL_INFO,
263+
"'mWolfVisionVisualizer => Using hostname: ', address")
264+
}
265+
else {
266+
module.Device.SocketConnection.Address = ''
267+
NAVErrorLog(NAV_LOG_LEVEL_ERROR,
268+
"'mWolfVisionVisualizer => Invalid IP address or hostname: ', event.Args[1]")
269+
}
270+
}
271+
else {
272+
module.Device.SocketConnection.Address = ip.Address
273+
NAVErrorLog(NAV_LOG_LEVEL_INFO,
274+
"'mWolfVisionVisualizer => Using IP address: ', ip.Address")
275+
}
276+
277+
SocketConnectionReset()
220278
}
221279
}
222280
}
@@ -265,12 +323,41 @@ define_function UpdateFeedback() {
265323
}
266324

267325

326+
define_function HandleSocketError(tdata data) {
327+
module.Device.SocketConnection.RetryCount++
328+
329+
NAVErrorLog(NAV_LOG_LEVEL_ERROR,
330+
"'mWolfVisionVisualizer => Socket connection error: ', NAVGetSocketError(type_cast(data.number))")
331+
NAVErrorLog(NAV_LOG_LEVEL_WARNING,
332+
"'mWolfVisionVisualizer => Socket connection failed (attempt ', itoa(module.Device.SocketConnection.RetryCount), ')'")
333+
334+
if (module.Device.SocketConnection.RetryCount <= NAV_MAX_SOCKET_CONNECTION_RETRIES) {
335+
// Still in base retry phase - timeline already running at base interval
336+
NAVErrorLog(NAV_LOG_LEVEL_INFO,
337+
"'mWolfVisionVisualizer => Next retry in ', itoa(module.Device.SocketConnection.Interval[1]), 'ms'")
338+
return
339+
}
340+
341+
// Calculate new exponential backoff interval
342+
module.Device.SocketConnection.Interval[1] = NAVSocketGetConnectionInterval(module.Device.SocketConnection.RetryCount)
343+
344+
NAVErrorLog(NAV_LOG_LEVEL_INFO,
345+
"'mWolfVisionVisualizer => Next retry in ', itoa(module.Device.SocketConnection.Interval[1]), 'ms'")
346+
347+
// Restart timeline with new interval
348+
NAVTimelineReload(TL_SOCKET_CHECK, module.Device.SocketConnection.Interval)
349+
}
350+
351+
268352
(***********************************************************)
269353
(* STARTUP CODE GOES BELOW *)
270354
(***********************************************************)
271355
DEFINE_START {
272356
NAVModuleInit(module)
273357
create_buffer dvPort, module.RxBuffer.Data
358+
module.Device.SocketConnection.Socket = dvPort.PORT
359+
module.Device.SocketConnection.Port = IP_PORT
360+
module.Device.SocketConnection.Interval[1] = NAVSocketGetConnectionInterval(module.Device.SocketConnection.RetryCount)
274361
}
275362

276363
(***********************************************************)
@@ -290,13 +377,19 @@ data_event[dvPort] {
290377

291378
if (data.device.number == 0) {
292379
module.Device.SocketConnection.IsConnected = true
380+
381+
// Reset retry count on successful connection
382+
module.Device.SocketConnection.RetryCount = 0
383+
module.Device.SocketConnection.Interval[1] = NAVSocketGetConnectionInterval(module.Device.SocketConnection.RetryCount)
384+
NAVTimelineReload(TL_SOCKET_CHECK, module.Device.SocketConnection.Interval)
385+
293386
UpdateFeedback()
294387
}
295388

296389
NAVTimelineStart(TL_DRIVE,
297-
TL_DRIVE_INTERVAL,
298-
TIMELINE_ABSOLUTE,
299-
TIMELINE_REPEAT)
390+
TL_DRIVE_INTERVAL,
391+
TIMELINE_ABSOLUTE,
392+
TIMELINE_REPEAT)
300393
}
301394
offline: {
302395
if (data.device.number == 0) {
@@ -306,6 +399,7 @@ data_event[dvPort] {
306399
}
307400
onerror: {
308401
if (data.device.number == 0) {
402+
HandleSocketError(data)
309403
Reset()
310404
}
311405
}
@@ -414,7 +508,7 @@ channel_event[vdvObject, 0]{
414508
}
415509

416510

417-
timeline_event[TL_IP_CHECK] { MaintainIPConnection() }
511+
timeline_event[TL_SOCKET_CHECK] { MaintainSocketConnection() }
418512

419513

420514
timeline_event[TL_DRIVE] { Drive() }

0 commit comments

Comments
 (0)