Skip to content

Commit 7ef7de2

Browse files
authored
Merge pull request #447 from WouterJD/USB-recovery
Issue #446 USB-error recovery, version 1 successfully recovers dropped connections from Fortius head unit T1932
2 parents 8ee2a5a + c9cef20 commit 7ef7de2

File tree

2 files changed

+136
-53
lines changed

2 files changed

+136
-53
lines changed

pythoncode/FortiusAntTitle.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
#-------------------------------------------------------------------------------
22
# Version info
33
#-------------------------------------------------------------------------------
4-
WindowTitle = "Fortius Antifier v6.7" # Double quotes, see below!
4+
WindowTitle = "Fortius Antifier v6.8" # Double quotes, see below!
5+
# 2023-12-01 Version 6.8 #446 Continuously message "insufficient data"
56
# 2022-12-28 Version 6.7 #404 BLE does not work when debug=0
67
# #404 PowerFactor setting always reset to 20
78
# 2022-08-24 Version 6.7 #341 Refactoring of ANT reading loop

pythoncode/usbTrainer.py

+134-52
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,12 @@
11
#-------------------------------------------------------------------------------
22
# Version info
33
#-------------------------------------------------------------------------------
4-
__version__ = "2023-04-06"
4+
__version__ = "2023-12-01"
5+
# 2023-12-01 Improvement so that reconnect DOES NOT occur when succesful reads
6+
# follow on unsuccesful reads.
7+
# 2023-10-30 Error recovery on the USB-device improved, because of issue #446
8+
# If reading (including retry) fails multiple times, the USB-device is
9+
# re-initialized.
510
# 2023-04-06 If UserAndBikeWeight is set below the minimum, a sensible value is set.
611
# 2022-08-22 Steering only active when -S wired specified.
712
# 2022-08-10 Steering merged from marcoveeneman and switchable's code
@@ -380,19 +385,65 @@ def GetTrainer(clv, AntDevice=None):
380385
if clv.Tacx_Genius: return clsTacxAntGeniusTrainer(clv, AntDevice)
381386
if clv.Tacx_Bushido: return clsTacxAntBushidoTrainer(clv, AntDevice)
382387

388+
389+
#-----------------------------------------------------------------------
390+
# Let's see whether there is a USB-headunit connected...
391+
#-----------------------------------------------------------------------
392+
msg, hu, dev, LegacyProtocol = clsTacxTrainer.InitializeUSB(0)
393+
394+
#-----------------------------------------------------------------------
395+
# Done
396+
#-----------------------------------------------------------------------
397+
logfile.Console(msg)
398+
if debug.on(debug.Function):
399+
logfile.Write ("GetTrainer() returns, trainertype=" + hex(hu))
400+
401+
#-----------------------------------------------------------------------
402+
# Return the correct Object
403+
#-----------------------------------------------------------------------
404+
if dev != False:
405+
if LegacyProtocol:
406+
return clsTacxLegacyUsbTrainer(clv, msg, hu, dev)
407+
else:
408+
return clsTacxNewUsbTrainer(clv, msg, hu, dev)
409+
else:
410+
return clsTacxTrainer (clv, msg) # where .OK = False
411+
412+
#---------------------------------------------------------------------------
413+
# I n i t i a l i z e U S B
414+
#---------------------------------------------------------------------------
415+
# Input previousHU, is used to reconnect on error-recovery
416+
#
417+
# Function Find USB-connected headunit and perform initialization
418+
# This function was part of GetTrainer().
419+
# Issue #446 reports disconnect of the USB headunit which does
420+
# not recover.
421+
# InitializeUSB() is used to reconnect when erros occur,
422+
# practice must reveal whether this works.
423+
#
424+
# Output msg, hu, dev, LegacyProtocol
425+
#---------------------------------------------------------------------------
426+
@staticmethod
427+
def InitializeUSB(previousHu):
428+
logfile.Console ("Find and initialise USB head unit")
383429
#-----------------------------------------------------------------------
384430
# So we are going to initialize USB
385431
# This may be either 'Legacy interface' or 'New interface'
386432
#-----------------------------------------------------------------------
387433
msg = "No Tacx trainer found"
388-
hu = None # TrainerType
434+
hu = 0 # TrainerType
389435
dev = False
390436
LegacyProtocol = False
391437

392438
#-----------------------------------------------------------------------
393439
# Find supported trainer (actually we talk to a headunit)
394440
#-----------------------------------------------------------------------
395-
for hu in [hu1902, hu1902_nfw, hu1904, hu1932, hu1942, hue6be_nfw]:
441+
if previousHu == 0:
442+
HuList = [hu1902, hu1902_nfw, hu1904, hu1932, hu1942, hue6be_nfw]
443+
else:
444+
HuList = [previousHu]
445+
446+
for hu in HuList:
396447
try:
397448
if debug.on(debug.Function):
398449
logfile.Write ("GetTrainer - Check for trainer %s" % (hex(hu)))
@@ -481,22 +532,9 @@ def GetTrainer(clv, AntDevice=None):
481532
dev.write(0x02,data)
482533

483534
#-----------------------------------------------------------------------
484-
# Done
535+
# Return results
485536
#-----------------------------------------------------------------------
486-
logfile.Console(msg)
487-
if debug.on(debug.Function):
488-
logfile.Write ("GetTrainer() returns, trainertype=" + hex(hu))
489-
490-
#-----------------------------------------------------------------------
491-
# Return the correct Object
492-
#-----------------------------------------------------------------------
493-
if dev != False:
494-
if LegacyProtocol:
495-
return clsTacxLegacyUsbTrainer(clv, msg, hu, dev)
496-
else:
497-
return clsTacxNewUsbTrainer(clv, msg, hu, dev)
498-
else:
499-
return clsTacxTrainer (clv, msg) # where .OK = False
537+
return msg, hu, dev, LegacyProtocol
500538

501539
#---------------------------------------------------------------------------
502540
# Functions from external to provide data
@@ -2358,6 +2396,7 @@ def DisplayStateTable(self, FortiusAntState):
23582396
# c l s T a c x U s b T r a i n e r
23592397
#-------------------------------------------------------------------------------
23602398
class clsTacxUsbTrainer(clsTacxTrainer):
2399+
USB_ReadErrorCount = 0
23612400
#---------------------------------------------------------------------------
23622401
# Convert WheelSpeed --> Speed in km/hr
23632402
# SpeedScale must be defined in sub-class
@@ -2464,48 +2503,91 @@ def USB_Read(self):
24642503
#
24652504
# function Same plus:
24662505
# At least 40 bytes must be returned, retry 4 times
2506+
#
2507+
# If multiple USB_Read_retry4x40() fail, the USB device is
2508+
# reattached.
2509+
#
2510+
# One might argue this error-recovery should also happen in
2511+
# USB_write(). USB_Write does not fail unless the USB cable is
2512+
# actually disconnected. Since both functions are in the main loop
2513+
# reconnecting here works fine.
24672514
#---------------------------------------------------------------------------
24682515
def USB_Read_retry4x40(self, expectedHeader = USB_ControlResponse):
2469-
retry = 4
2516+
#-----------------------------------------------------------------------
2517+
# If multiple reads fail, we try to reconnect.
2518+
# This is not necessary when succesfull reads occur in the meantime.
2519+
# This is quite arbitrary, practice must reveal that no unneccessary
2520+
# reconnects occur.
2521+
#
2522+
# A reconnect occurs:
2523+
# fail - fail - fail - fail USB_ReadErrorCount = 4
2524+
# fail - fail - ok - fail - fail - fail USB_ReadErrorCount = 4
2525+
# A reconnect DOES NOT occur:
2526+
# fail - ok - fail - ok - fail - ok - fail USB_ReadErrorCount = 1
2527+
#-----------------------------------------------------------------------
2528+
self.USB_ReadErrorCount = max(self.USB_ReadErrorCount - 1, 0)
24702529

2471-
while True:
2472-
data = self.USB_Read()
2530+
#-----------------------------------------------------------------------
2531+
# Let's go
2532+
#-----------------------------------------------------------------------
2533+
while True: # Recover through InitializeUSB()
2534+
retry = 4
2535+
while True: # Retry reading through USB_Read()
2536+
data = self.USB_Read()
2537+
2538+
#---------------------------------------------------------------
2539+
# Retry if no correct buffer received
2540+
#---------------------------------------------------------------
2541+
if retry and (len(data) < 40 or self.Header != expectedHeader):
2542+
if debug.on(debug.Any):
2543+
logfile.Write ( \
2544+
'Retry because short buffer (len=%s) or incorrect header received (expected: %s received: %s)' % \
2545+
(len(data), hex(expectedHeader), hex(self.Header)))
2546+
time.sleep(0.1) # 2020-09-29 short delay @RogerPleijers
2547+
retry -= 1
2548+
else:
2549+
break
24732550

24742551
#-------------------------------------------------------------------
2475-
# Retry if no correct buffer received
2552+
# Inform when there's something unexpected
24762553
#-------------------------------------------------------------------
2477-
if retry and (len(data) < 40 or self.Header != expectedHeader):
2478-
if debug.on(debug.Any):
2479-
logfile.Write ( \
2480-
'Retry because short buffer (len=%s) or incorrect header received (expected: %s received: %s)' % \
2481-
(len(data), hex(expectedHeader), hex(self.Header)))
2482-
time.sleep(0.1) # 2020-09-29 short delay @RogerPleijers
2483-
retry -= 1
2484-
else:
2485-
break
2554+
if len(data) < 40:
2555+
self.tacxEvent = False
2556+
self.USB_ReadErrorCount += 1
2557+
# 2020-09-29 the buffer is ignored when too short (was processed before)
2558+
logfile.Console('Tacx head unit returns insufficient data, len=%s' % len(data))
2559+
if self.clv.PedalStrokeAnalysis:
2560+
logfile.Console('To resolve, try to run without Pedal Stroke Analysis.')
2561+
else:
2562+
logfile.Console('To resolve, check all (signal AND power) cabling for loose contacts.')
2563+
# 2021-04-29 On Raspberry Pi Zero W this also occurs when the
2564+
# system is too busy.
2565+
# When the system is less busy (FortiusAnt only active
2566+
# process) then the message disappears automatically.
2567+
# A longer timeout does not help (tried: 100ms).
2568+
2569+
elif self.Header != expectedHeader:
2570+
self.tacxEvent = False
2571+
self.USB_ReadErrorCount += 1
2572+
logfile.Console('Tacx head unit returns incorrect header %s (expected: %s)' % \
2573+
(hex(expectedHeader), hex(self.Header)))
24862574

2487-
#-----------------------------------------------------------------------
2488-
# Inform when there's something unexpected
2489-
#-----------------------------------------------------------------------
2490-
if len(data) < 40:
2491-
self.tacxEvent = False
2492-
# 2020-09-29 the buffer is ignored when too short (was processed before)
2493-
logfile.Console('Tacx head unit returns insufficient data, len=%s' % len(data))
2494-
if self.clv.PedalStrokeAnalysis:
2495-
logfile.Console('To resolve, try to run without Pedal Stroke Analysis.')
2575+
#-------------------------------------------------------------------
2576+
# If errors occurred multiple times, try to reconnect the USB device...
2577+
# There is no timeout here, because it's in a loop itself.
2578+
#
2579+
# This path has been tested by unplugging the USB-cable which produces
2580+
# a heap of errors. After connection, FortiusAnt proceeds nicely.
2581+
# Issue #446 (presumably hw-error) to be tested and confirmed.
2582+
#-------------------------------------------------------------------
2583+
if self.USB_ReadErrorCount > 4:
2584+
self.USB_ReadErrorCount = 0
2585+
logfile.Console('Try to reconnect to Tacx head unit')
2586+
msg, hu, self.UsbDevice, LegacyProtocol = clsTacxTrainer.InitializeUSB(self.Headunit)
2587+
logfile.Console (msg)
2588+
# Note that, if dev == False, msg shows what has gone wrong
24962589
else:
2497-
logfile.Console('To resolve, check all (signal AND power) cabling for loose contacts.')
2498-
# 2021-04-29 On Raspberry Pi Zero W this also occurs when the
2499-
# system is too busy.
2500-
# When the system is less busy (FortiusAnt only active
2501-
# process) then the message disappears automatically.
2502-
# A longer timeout does not help (tried: 100ms).
2503-
2504-
elif self.Header != expectedHeader:
2505-
self.tacxEvent = False
2506-
logfile.Console('Tacx head unit returns incorrect header %s (expected: %s)' % \
2507-
(hex(expectedHeader), hex(self.Header)))
2508-
2590+
break
25092591
return data
25102592

25112593
#---------------------------------------------------------------------------

0 commit comments

Comments
 (0)