def Read(self): if debug.on(debug.Ble): logfile.Write("BleInterface.Read() ...") rtn = False self.jsondata = None if self.interface: #----------------------------------------------------------- # Read from bluetooth interface #----------------------------------------------------------- try: r = requests.get(f'http://{self.host}:{self.port}/ant') except Exception as e: logfile.Console("... requests.get() error " + str(e)) else: #------------------------------------------------------- # Now we have a response object r # Which should contain a JSON object #------------------------------------------------------- if r.ok and r.text != "": try: self.jsondata = json.loads(r.text) except Exception as e: logfile.Console("... json.loads() error " + str(e)) else: rtn = True if debug.on(debug.Ble): logfile.Write("... returns: %s (%s)" % (rtn, self.jsondata)) return rtn
def CheckShutdown(self, FortiusAntGui=None): repeat = 7 # timeout = n * .25 seconds # 5 blinks is enough # 7 because the first two cycles are blink to allow # for a "short button press" without messages. rtn = True # Assume button will remain pressed # If we don't use leds/buttons ==> False ResetLeds = False if OnRaspberry and not IsShutdownRequested(): # ------------------------------------------------------------------ # Switch off shutdown led, just in case (only local) # ------------------------------------------------------------------ self._Shutdown(False) # ------------------------------------------------------------------ # Blink the (red) Shutdown led while button pressed # ------------------------------------------------------------------ while repeat: if self.ShutdownButtonIsHeld(): if repeat <= 5: self.SetLeds(False, False, False, True, False) if FortiusAntGui != None: FortiusAntGui.SetLeds(False, False, False, True, False) logfile.Console('Raspberry will be shutdown ... %s ' % repeat) ResetLeds = True time.sleep(.25) else: rtn = False break repeat -= 1 # ------------------------------------------------------------------ # Final warning # ------------------------------------------------------------------ if rtn: self.PowerupTest() rtn = self.ShutdownButtonIsHeld() # ------------------------------------------------------------------ # Now it's sure we will shutdown # The application has to do it, though. # ------------------------------------------------------------------ if rtn: logfile.Console('Raspberry will shutdown') PrepareShutdown() # ------------------------------------------------------------------ # If leds were touched, switch off all - application must set again # ------------------------------------------------------------------ if not rtn and ResetLeds: self.SetLeds(False, False, False, False, False) if FortiusAntGui != None: FortiusAntGui.SetLeds(False, False, False, False, False) # ---------------------------------------------------------------------- # Return True/False; may be of previous shutdown-request! # ---------------------------------------------------------------------- return IsShutdownRequested()
def FortiusAntChild(clv, conn): # -------------------------------------------------------------------------- # Initialize the child process, create our own logfile # -------------------------------------------------------------------------- debug.activate(clv.debug) if debug.on(debug.Any): logfile.Open('FortiusAntGUI') logfile.Console('FortiusAnt GUI started in child-process') FortiusAntBody.Initialize(clv) # -------------------------------------------------------------------------- # Start the user-interface # -------------------------------------------------------------------------- app = wx.App(0) frame = frmFortiusAntChild(None, conn, clv) app.SetTopWindow(frame) frame.Show() if clv.autostart: frame.Autostart() app.MainLoop() # -------------------------------------------------------------------------- # Signal parent that we're done # -------------------------------------------------------------------------- frame.GuiMessageToMain(cmd_EndExecution, False) if debug.on(debug.Any): logfile.Console('FortiusAnt GUI ended')
def SetMessages(self, Tacx=None, Dongle=None, HRM=None): if Tacx != None: logfile.Console ("Tacx - " + Tacx) if Dongle != None: logfile.Console ("Dongle - " + Dongle) if HRM != None: logfile.Console ("AntHRM - " + HRM)
def LocateHW(self): if testMode: print('') logfile.Console ('Checking for HW') time.sleep(1) logfile.Console ('Done') rtn = True else: rtn = FortiusAntBody.LocateHW(self) return rtn
def Tacx2Dongle(self): if testMode: print('') while self.RunningSwitch: logfile.Console ('Translate Tacx 2 Dongle') self.SetMessages('Translate Tacx 2 Dongle', datetime.utcnow().strftime('%Y-%m-%d %H-%M-%S'), str(time.gmtime().tm_sec)) self.SetValues(0,1,time.gmtime().tm_sec,3,4,5,6,7,8,9,10) time.sleep(1) logfile.Console ('Tacx2Dongle done') rtn = True else: rtn = FortiusAntBody.Tacx2Dongle(self) return rtn
def Runoff(self): if testMode: print('') while self.RunningSwitch: logfile.Console('Doing runoff') self.SetMessages('Doing runoff', datetime.utcnow().strftime('%Y-%m-%d %H-%M-%S'), str(time.gmtime().tm_sec)) self.SetValues(0, 1, time.gmtime().tm_sec, 3, 4, 5, 6, 7, 8) time.sleep(1) logfile.Console('Runoff done') rtn = True else: rtn = FortiusAntBody.Runoff(self) return rtn
def SetValues(self, fSpeed, iRevs, iPower, iTargetMode, iTargetPower, fTargetGrade, iTacx, iHeartRate, iTeeth, extra=None): # ---------------------------------------------------------------------- # Console: Update current readings, once per second # ---------------------------------------------------------------------- delta = time.time() - self.LastTime # Delta time since previous if delta >= 1 and (not clv.gui or debug.on(debug.Application)): self.LastTime = time.time() # Time in seconds if iTargetMode == gui.mode_Power: sTarget = "%3.0fW" % iTargetPower elif iTargetMode == gui.mode_Grade: sTarget = "%3.1f%%" % fTargetGrade if iTargetPower > 0: # 2020-01-22 sTarget += "(%iW)" % iTargetPower # Target power added for reference else: sTarget = "None" msg = "Target=%s Speed=%4.1fkmh hr=%3.0f Current=%3.0fW Cad=%3.0f r=%4.0f T=%3.0f" % \ ( sTarget, fSpeed, iHeartRate, iPower, iRevs, iTacx, int(iTeeth) ) logfile.Console(msg)
def GuiMessageToMain(self, command, wait=True, p1=None, p2=None): # ---------------------------------------------------------------------- # Step 1. GUI sends a command to main # ---------------------------------------------------------------------- if debug.on(debug.MultiProcessing) and not (command == cmd_Idle): logfile.Write("mp-GuiMessageToMain(conn, %s, %s, %s, %s)" % (command, wait, p1, p2)) self.gui_conn.send((command, p1, p2)) rtn = True while wait: # ------------------------------------------------------------------ # Check if requested command is ended # OR that information is received from Main # ------------------------------------------------------------------ # Will be more efficient than self.gui_conn.poll() / sleep loop... # ------------------------------------------------------------------ # Step 4. GUI receives the response (command, rtn) # ------------------------------------------------------------------ msg = self.gui_conn.recv() cmd = msg[0] rtn = msg[1] if debug.on(debug.MultiProcessing) and not (command == cmd_Idle and rtn == 0): logfile.Write( "mp-GuiAnswerFromMain(conn) returns (%s, %s)" % (cmd, rtn)) # ------------------------------------------------------------------ # We wait for the response on the command # and in the meantime receive data to displayed # # cmd_StopButton is treated differently, since that command is sent # while we are waiting for the response on cmd_Runoff or cmd_Tacx2Dongle # we ignore the response here and cmd_StopButton does not start wait-loop # to avoid some sort of nesting or so. # ------------------------------------------------------------------ if cmd == command: break # command is ready elif cmd == cmd_StopButton: pass elif cmd == cmd_SetValues: self.SetValues(rtn[0], rtn[1], rtn[2], rtn[3], rtn[4], rtn[5], rtn[6], rtn[7], rtn[8], rtn[9], rtn[10]) # rtn is tuple elif cmd == cmd_SetMessages: self.SetMessages( rtn[0], rtn[1], rtn[2]) # rtn is (Tacx, Dongle, HRM) tuple elif cmd == cmd_PedalStrokeAnalysis: self.PedalStrokeAnalysis( rtn[0], rtn[1]) # rtn is (info, Cadence) tuple pass else: logfile.Console( '%s active but unknown response received (%s, %s); the message is ignored.' % (command, cmd, rtn)) break return rtn
def Settings(self, pRestartApplication, pclv): global RestartApplication, clv RestartApplication = pRestartApplication clv = pclv if debug.on(debug.Function): logfile.Write ("FortiusAnt.Settings(%s, %s)" % (pRestartApplication, pclv.debug)) if testMode: print('') logfile.Console ('Transfer settings') time.sleep(1) logfile.Console ('Done') rtn = True else: rtn = FortiusAntBody.Settings(self, pRestartApplication, pclv) return rtn
def ListenToChild(self): # ---------------------------------------------------------------------- # Poll the GUI what we are expected to do # Note that we never end (on our initiative)!! # ---------------------------------------------------------------------- while True: gui_command, gui_p1, gui_p2 = self.MainCommandFromGui() if gui_command == cmd_EndExecution: break elif gui_command == cmd_Settings: rtn = Settings(self, gui_p1, gui_p2) self.MainRespondToGUI(cmd_Settings, rtn) elif gui_command == cmd_Idle: rtn = IdleFunction(self) self.MainRespondToGUI(cmd_Idle, rtn) elif gui_command == cmd_LocateHW: rtn = LocateHW(self) self.MainRespondToGUI(cmd_LocateHW, rtn) elif gui_command == cmd_Runoff: self.RunningSwitch = True thread = threading.Thread(target=self.RunoffThread) thread.start() elif gui_command == cmd_Tacx2Dongle: self.RunningSwitch = True thread = threading.Thread(target=self.Tacx2DongleThread) thread.start() elif gui_command == cmd_StopButton: if testMode: print('') logfile.Console('Stop button pressed') self.RunningSwitch = False self.MainRespondToGUI(cmd_StopButton, True) else: logfile.Console('Unexpected command from GUI: %s' % gui_command) rtn = False
def Open(self): self.OK = False self.Message = ", Bluetooth interface cannot be opened" if debug.on(debug.Ble): logfile.Write("BleInterface.Open() ...") if self.interface: if debug.on(debug.Ble): logfile.Write('... already open') else: #----------------------------------------------------------- # path to find ./node (in the parent of the directory where # 'we' are located) assuming directory structure: # ./node # ./pythoncode # ./WindowsExecutable #----------------------------------------------------------- if getattr(sys, 'frozen', False): dirname = os.path.realpath( sys.argv[0]) # the started executable else: dirname = str(lib_programname.get_path_executed_script() ) # type: pathlib.Path dirname = os.path.dirname( dirname) # Remove /filename.py or /filename.exe dirname = dirname.replace('\\', '/') # Unify separator = / dirname = dirname[0:dirname.rfind('/')] # Remove last level #----------------------------------------------------------- # Create interface as sub-process #----------------------------------------------------------- command = ["node", "server.js"] directory = dirname + "/node" if debug.on(debug.Ble): logfile.Write("... Popen(%s,%s)" % (directory, command)) try: if debug.on(debug.Any): self.interface = subprocess.Popen( command, cwd=directory, stdout=logfile.fLogfile, stderr=logfile.fLogfile) else: self.interface = subprocess.Popen(command, cwd=directory) except Exception as e: self.Message += "; " + str(e) if debug.on(debug.Ble): logfile.Write("... " + str(e)) else: if debug.on(debug.Ble): logfile.Write('... completed') self.Message = ", Bluetooth interface open" self.OK = True if self.OK: logfile.Console( "FortiusANT exchanges data with a bluetooth Cycling Training Program" ) return self.OK
def Write(self, data): rtn = False if debug.on(debug.Ble): logfile.Write('BleInterface.Write(%s)' % data) if self.interface: url = f'http://{self.host}:{self.port}/ant' try: r = requests.post(url, data=data) except Exception as e: logfile.Console("... requests.post() error " + str(e)) else: rtn = r.ok return rtn
def SetValues(self, fSpeed, iRevs, iPower, iTargetMode, iTargetPower, \ fTargetGrade, iTacx, iHeartRate, iCrancksetIndex, iCassetteIndex, fReduction): global clv # ---------------------------------------------------------------------- # Console: Update current readings, once per second # ---------------------------------------------------------------------- delta = time.time() - self.LastTime # Delta time since previous if delta >= 1 and (not clv.gui or debug.on(debug.Application)): self.LastTime = time.time() # Time in seconds if clv.imperial: s1 = fSpeed / mile s2 = "mph" else: s1 = fSpeed s2 = "km/h" if iTargetMode == mode_Power: sTarget = "%3.0fW" % iTargetPower elif iTargetMode == mode_Grade: sTarget = "%3.1f%%" % fTargetGrade if iTargetPower > 0: # 2020-01-22 sTarget += "(%iW)" % iTargetPower # Target power added for reference else: sTarget = "None" if clv.hrm == -1: h = "" else: h = "hr=%3.0f " % iHeartRate all = False self.leds = "" if all or True: # Led 1 = Tacx trainer; USB, ANT or Simulated self.leds += "t" if self.StatusLeds[0] else "-" if all or OnRaspberry: # Led 2 = on raspberry only self.leds += "s" if self.StatusLeds[1] else "-" if all or clv.Tacx_Cadence: # Led 3 = Cadence sensor (black because backgroup is white) self.leds += "c" if self.StatusLeds[2] else "-" if all or clv.ble: # Led 4 = Bluetooth CTP self.leds += "b" if self.StatusLeds[3] else "-" if all or clv.antDeviceID != -1: # Led 5 = ANT CTP self.leds += "a" if self.StatusLeds[4] else "-" msg = "Target=%s %4.1f%s %sCurrent=%3.0fW Cad=%3.0f r=%4.0f %3s%% %s" % \ (sTarget, s1,s2, h, iPower, iRevs, iTacx, int(fReduction*100), self.leds) logfile.Console (msg)
def print(self): try: v = debug.on( debug.Any ) # Verbose: print all command-line variables with values if self.autostart: logfile.Console("-a") if self.SimulateTrainer: logfile.Console("-s") if v or self.args.debug: logfile.Console("-d %s (%s)" % (self.debug, bin(self.debug))) if v or self.args.dongle: logfile.Console("-D %s (%s)" % (self.dongle, hex(self.dongle))) if v or self.args.hrm: logfile.Console("-H %s (%s)" % (self.hrm, hex(self.hrm))) if v or self.args.fe: logfile.Console("-F %s (%s)" % (self.fe, hex(self.fe))) if v or self.args.scs: logfile.Console("-S %s (%s)" % (self.scs, hex(self.scs))) if v or self.args.vtx: logfile.Console("-V %s (%s)" % (self.vtx, hex(self.vtx))) except: pass # May occur when incorrect command line parameters, error already given before
def Write(self, data): rtn = False retry = 10 if debug.on(debug.Ble): logfile.Write('BleInterface.Write(%s)' % data) while retry and not rtn and self.interface: url = f'http://{self.host}:{self.port}/ant' try: r = requests.post(url, data=data) except Exception as e: msg = "... requests.post() error " + str(e) if retry and '[Errno 111]' in msg: retry -= 1 time.sleep(0.250) logfile.Write(msg) else: retry = 0 logfile.Console(msg) else: rtn = r.ok return rtn
def Open(self): self.OK = False self.Message = ", Bluetooth interface cannot be opened" if debug.on(debug.Ble): logfile.Write("BleInterface.Open() ...") if self.interface: if debug.on(debug.Ble): logfile.Write('... already open') else: #----------------------------------------------------------- # Create interface as sub-process #----------------------------------------------------------- command = ["node", "server.js"] directory = Path.cwd().parent / "node" if debug.on(debug.Ble): logfile.Write("... Popen(%s,%s)" % (directory, command)) try: if debug.on(debug.Any): self.interface = subprocess.Popen( command, cwd=directory, stdout=logfile.fLogfile, stderr=logfile.fLogfile) else: self.interface = subprocess.Popen(command, cwd=directory) except Exception as e: self.Message += "; " + str(e) if debug.on(debug.Ble): logfile.Write("... " + str(e)) else: if debug.on(debug.Ble): logfile.Write('... completed') self.Message = ", Bluetooth interface opened to CTP" self.OK = True if self.OK: logfile.Console( "FortiusANT exchanges data with a bluetooth Cycling Training Program" ) return self.OK
def print(self): try: v = debug.on( debug.Any ) # Verbose: print all command-line variables with values if self.autostart: logfile.Console("-a") if self.PedalStrokeAnalysis: logfile.Console("-A") if v or self.args.debug: logfile.Console("-d %s (%s)" % (self.debug, bin(self.debug))) if self.gui: logfile.Console("-g") if v or self.args.hrm: logfile.Console("-H %s" % self.hrm) if self.manual: logfile.Console("-m") if self.manualGrade: logfile.Console("-M") if not self.args.calibrate: logfile.Console("-n") if v or self.args.factor: logfile.Console("-p %s" % self.PowerFactor) if self.args.PowerMode: logfile.Console("-P") if self.args.simulate: logfile.Console("-s") #scs if self.args.scs: logfile.Console("-S %s" % self.scs ) if self.args.TacxType: logfile.Console("-t %s" % self.TacxType) if self.uphill: logfile.Console("-u") #------------------------------------------------------------------- # Deprecated #------------------------------------------------------------------- # if v or self.args.bicycle: logfile.Console("-b %s,%s/%s,%s/%s" % (self.tyre, self.fL, self.fS, self.rS, self.rL)) # if v or self.args.ftp: logfile.Console("-f %s" % self.ftp ) # if v or self.args.resistance: logfile.Console("-r %s/%s" % (self.ResistanceH, self.ResistanceL)) except: pass # May occur when incorrect command line parameters, error already given before
def mainProgram(): global RestartApplication, clv # -------------------------------------------------------------------------- # Initialize # -------------------------------------------------------------------------- debug.deactivate() if not RestartApplication: clv = cmd.CommandLineVariables() debug.activate(clv.debug) FortiusAntBody.Initialize(clv) if debug.on(debug.Any): logfile.Open() logfile.Console("FortiusANT started") logfile.Write (' Restart=%s debug=%s' % (RestartApplication, clv.debug)) clv.print() logfile.Console("------------------") RestartApplication = False #------------------------------------------------------------------------------- # Component info #------------------------------------------------------------------------------- if debug.on(debug.Any): # ---------------------------------------------------------------------- if getattr(sys, 'frozen', False): logfile.Write('Windows executable started') else: logfile.Write('Python version started') # ---------------------------------------------------------------------- logfile.Write('Version info for the components' ) logfile.Write(githubWindowTitle()) s = " %20s = %s" logfile.Write(s % ('FortiusAnt', __version__ )) logfile.Write(s % ('antDongle', ant.__version__ )) logfile.Write(s % ('antFE', fe.__version__ )) logfile.Write(s % ('antHRM', hrm.__version__ )) logfile.Write(s % ('antPWR', pwr.__version__ )) logfile.Write(s % ('antSCS', scs.__version__ )) logfile.Write(s % ('bleDongle', bleDongle.__version__ )) logfile.Write(s % ('constants', constants.__version__ )) logfile.Write(s % ('debug', debug.__version__ )) logfile.Write(s % ('FortiusAntBody', FortiusAntBody.__version__ )) logfile.Write(s % ('FortiusAntCommand', cmd.__version__ )) if UseGui: logfile.Write(s % ('FortiusAntGui', gui.__version__ )) logfile.Write(s % ('logfile', logfile.__version__ )) if UseGui: logfile.Write(s % ('RadarGraph', RadarGraph.__version__ )) logfile.Write(s % ('settings', settings.__version__ )) logfile.Write(s % ('structConstants', sc.__version__ )) logfile.Write(s % ('TCXexport', TCXexport.__version__ )) logfile.Write(s % ('usbTrainer', usbTrainer.__version__ )) logfile.Write(s % ('argparse', argparse.__version__ )) # logfile.Write(s % ('binascii', binascii.__version__ )) # logfile.Write(s % ('math', math.__version__ )) logfile.Write(s % ('numpy', numpy.__version__ )) logfile.Write(s % ('os', os.name )) if os.name == 'nt': v = sys.getwindowsversion() logfile.Write((s + '.%s') % ('windows', v.major, v.minor)) logfile.Write(s % ('pickle', pickle.format_version )) logfile.Write(s % ('platform', platform.__version__ )) # logfile.Write(s % ('glob', glob.__version__ )) # logfile.Write(s % ('random', random.__version__ )) logfile.Write(s % ('sys (python)', sys.version )) # logfile.Write(s % ('struct', struct.__version__ )) # logfile.Write(s % ('threading', threading.__version__ )) # logfile.Write(s % ('time', time.__version__ )) logfile.Write(s % ('usb', usb.__version__ )) if UseGui: logfile.Write(s % ('wx', wx.__version__ )) logfile.Write('FortiusANT code flags') logfile.Write(s % ('UseMultiProcessing', UseMultiProcessing)) logfile.Write(s % ('UseGui', UseGui)) logfile.Write(s % ('UseBluetooth', UseBluetooth)) logfile.Write("------------------") if not clv.gui: # -------------------------------------------------------------------------- # Console only, no multiprocessing required to separate GUI # -------------------------------------------------------------------------- Console = clsFortiusAntConsole() Console.Autostart() elif not UseMultiProcessing: # -------------------------------------------------------------------------- # No multiprocessing wanted, start GUI immediatly # -------------------------------------------------------------------------- clv.PedalStrokeAnalysis = False app = wx.App(0) frame = frmFortiusAnt(None, clv) app.SetTopWindow(frame) frame.Show() if clv.autostart: frame.Autostart() app.MainLoop() else: # -------------------------------------------------------------------------- # Multiprocessing wanted, start GUI in it's own process # -------------------------------------------------------------------------- # https://docs.python.org/3/library/multiprocessing.html # Create queue and sub-process # -------------------------------------------------------------------------- app_conn, gui_conn = multiprocessing.Pipe(True) pChild = multiprocessing.Process(target=FortiusAntChild, args=(clv, gui_conn) ) pChild.start() # -------------------------------------------------------------------------- # Poll child-process untill done # -------------------------------------------------------------------------- parent = clsFortiusAntParent(app_conn) # The child process has the GUI parent.ListenToChild() # -------------------------------------------------------------------------- # Wait for child-process to complete # -------------------------------------------------------------------------- pChild.join() # ------------------------------------------------------------------------------ # We're done # ------------------------------------------------------------------------------ if debug.on(debug.Any): logfile.Console('FortiusAnt ended') logfile.Close()
def __init__(self, clv): global MySelf # ---------------------------------------------------------------------- # Initialize # ---------------------------------------------------------------------- self.clv = clv self.OK = OnRaspberry MySelf = self # ---------------------------------------------------------------------- # Activate leds, if -l defined # Reason for -l is that usage of GPIO might be conflicting with other # applications on the Raspberry # Activate display, if -O defined # ---------------------------------------------------------------------- if self.OK: if OnRaspberry: self.StatusLeds = clv.StatusLeds # boolean if OnRaspberry: self.OutputDisplay = clv.OutputDisplay and UseOutputDisplay # string self.OK = self.StatusLeds or self.OutputDisplay # ---------------------------------------------------------------------- # Create 5 leds on these Pins as outputs. # The numbers are the numbers of the IO-Pins of the Raspi # Don't forget to add the series resistor of 470 Ohm # ---------------------------------------------------------------------- if self.StatusLeds: self.LedTacx = gpiozero.LED(clv.rpiTacx) # Orange self.LedShutdown = gpiozero.LED(clv.rpiShutdown) # Red self.LedCadence = gpiozero.LED(clv.rpiCadence) # White self.LedBLE = gpiozero.LED(clv.rpiBLE) # Blue self.LedANT = gpiozero.LED(clv.rpiANT) # Green self.BtnShutdown = gpiozero.Button(clv.rpiButton) # ---------------------------------------------------------------------- # Initialize OLED display # If NO display, the callable functions do nothing! # ---------------------------------------------------------------------- # Currently the module is only written for st77889 240x240 pixels # future requests to implement other displays will reveal how much # must be changed to accomodate. # ---------------------------------------------------------------------- self.DisplayState = self._DisplayStateConsole # If invalid, on console self.SetValues = self._SetValuesConsole # If invalid, on console self.DrawLeds = self._DrawLedsConsole # If invalid, on console if clv.OutputDisplay == False: # Not specified, no output self.DisplayState = self._DisplayStateNone self.SetValues = self._SetValuesNone self.DrawLeds = self._DrawLedsNone elif clv.OutputDisplay == 'console': # Test output on console pass elif clv.OutputDisplay in ('st7789', 'st7789b'): # TFT mini OLED Display self.rotation = clv.OutputDisplayR if self._SetupDisplaySt7789(clv.OutputDisplay): self.DisplayState = self._DisplayStateSt7789 self.SetValues = self._SetValuesSt7789 self.DrawLeds = self._DrawLedsSt7789 else: logfile.Console('Unexpected value for -O %s' % clv.OutputDisplay) self.DisplayState() # ---------------------------------------------------------------------- # Show leds for power-up # ---------------------------------------------------------------------- if self.StatusLeds: self.PowerupTest()
def ReceiveFromTrainer(devTrainer): global trainer_type Axis = 0 Buttons = 0 Cadence = 0 CurrentPower = 0 CurrentResistance = 0 Error = "" HeartRate = 0 PedalEcho = 0 Speed = 0 TargetResistance = 0 #----------------------------------------------------------------------------- # Read from trainer #----------------------------------------------------------------------------- data = "" try: data = devTrainer.read(0x82, 64, 30) except TimeoutError: pass except Exception as e: if "timeout error" in str(e) or "timed out" in str( e): # trainer did not return any data pass else: logfile.Console("ReceiveFromTrainer: USB READ ERROR: " + str(e)) if debug.on(debug.Data2): logfile.Write("Trainer recv data=%s (len=%s)" % (logfile.HexSpace(data), len(data))) #----------------------------------------------------------------------------- # Handle data when > 40 bytes (boundary as derived from antifier) #----------------------------------------------------------------------------- if len(data) > 40 and LegacyProtocol == False: #--------------------------------------------------------------------------- # Define buffer format #--------------------------------------------------------------------------- nDeviceSerial = 0 # 0...1 fDeviceSerial = sc.unsigned_short fFiller2_7 = sc.pad * (7 - 1) # 2...7 nYearProduction = 1 # 8 fYearProduction = sc.unsigned_char fFiller9_11 = sc.pad * (11 - 8) # 9...11 nHeartRate = 2 # 12 fHeartRate = sc.unsigned_char nButtons = 3 # 13 fButtons = sc.unsigned_char nHeartDetect = 4 # 14 fHeartDetect = sc.unsigned_char nErrorCount = 5 # 15 fErrorCount = sc.unsigned_char nAxis0 = 6 # 16-17 fAxis0 = sc.unsigned_short nAxis1 = 7 # 18-19 fAxis1 = sc.unsigned_short nAxis2 = 8 # 20-21 fAxis2 = sc.unsigned_short nAxis3 = 9 # 22-23 fAxis3 = sc.unsigned_short nHeader = 10 # 24-27 fHeader = sc.unsigned_int nDistance = 11 # 28-31 fDistance = sc.unsigned_int nSpeed = 12 # 32, 33 Wheel speed (Speed = WheelSpeed / SpeedScale in km/h) fSpeed = sc.unsigned_short fFiller34_35 = sc.pad * 2 # 34...35 Increases if you accellerate? fFiller36_37 = sc.pad * 2 # 36...37 Average power? nCurrentResistance = 13 # 38, 39 fCurrentResistance = sc.short nTargetResistance = 14 # 40, 41 fTargetResistance = sc.short nEvents = 15 # 42 fEvents = sc.unsigned_char fFiller43 = sc.pad # 43 nCadence = 16 # 44 fCadence = sc.unsigned_char fFiller45 = sc.pad # 45 nModeEcho = 17 # 46 fModeEcho = sc.unsigned_char nChecksumLSB = 18 # 47 fChecksumLSB = sc.unsigned_char nChecksumMSB = 19 # 48 fChecksumMSB = sc.unsigned_char fFiller49_63 = sc.pad * (63 - 48) # 49...63 format = sc.no_alignment + fDeviceSerial + fFiller2_7 + fYearProduction + \ fFiller9_11 + fHeartRate + fButtons + fHeartDetect + fErrorCount + \ fAxis0 + fAxis1 + fAxis2 + fAxis3 + fHeader + fDistance + fSpeed + \ fFiller34_35 + fFiller36_37 + fCurrentResistance + fTargetResistance + \ fEvents + fFiller43 + fCadence + fFiller45 + fModeEcho + \ fChecksumLSB + fChecksumMSB + fFiller49_63 #--------------------------------------------------------------------------- # Buffer must be 64 characters (struct.calcsize(format)), # Note that tt_FortiusSB returns 48 bytes only; append with dummy #--------------------------------------------------------------------------- for v in range(64 - len(data)): data.append(0) #--------------------------------------------------------------------------- # Parse buffer #--------------------------------------------------------------------------- tuple = struct.unpack(format, data) Cadence = tuple[nCadence] HeartRate = tuple[nHeartRate] PedalEcho = tuple[nEvents] TargetResistance = tuple[nTargetResistance] CurrentResistance = tuple[nCurrentResistance] Speed = Wheel2Speed(tuple[nSpeed]) CurrentPower = Resistance2Power(tuple[nCurrentResistance], Speed) Buttons = tuple[nButtons] Axis = tuple[nAxis1] elif LegacyProtocol == True: #--------------------------------------------------------------------------- # Define buffer format #--------------------------------------------------------------------------- nStatusAndCursors = 0 # 0 fStatusAndCursors = sc.unsigned_char nSpeed = 1 # 1, 2 Wheel speed (Speed = WheelSpeed / SpeedScale in km/h) fSpeed = sc.unsigned_short nCadence = 2 # 3 fCadence = sc.unsigned_char nHeartRate = 3 # 4 fHeartRate = sc.unsigned_char nStopWatch = 4 fStopWatch = sc.unsigned_int # 5,6,7,8 nCurrentResistance = 5 # 9 fCurrentResistance = sc.unsigned_char nPedalSensor = 6 # 10 fPedalSensor = sc.unsigned_char nAxis0 = 7 # 11 fAxis0 = sc.unsigned_char nAxis1 = 8 # 12 fAxis1 = sc.unsigned_char nAxis2 = 9 # 13 fAxis2 = sc.unsigned_char nAxis3 = 10 # 14 fAxis3 = sc.unsigned_char nCounter = 11 # 15 fCounter = sc.unsigned_char nWheelCount = 12 # 16 fWheelCount = sc.unsigned_char nYearProduction = 13 # 17 fYearProduction = sc.unsigned_char nDeviceSerial = 14 # 18, 19 fDeviceSerial = sc.unsigned_short nFirmwareVersion = 15 # 20 fFirmwareVersion = sc.unsigned_char #--------------------------------------------------------------------------- # Parse buffer # Note that the button-bits have an inversed logic: # 1=not pushed, 0=pushed. Hence the xor. #--------------------------------------------------------------------------- format = sc.no_alignment + fStatusAndCursors + fSpeed + fCadence + fHeartRate + fStopWatch + fCurrentResistance + \ fPedalSensor + fAxis0 + fAxis1 + fAxis2 + fAxis3 + fCounter + fWheelCount + \ fYearProduction + fDeviceSerial + fFirmwareVersion tuple = struct.unpack(format, data) Cadence = tuple[nCadence] HeartRate = tuple[nHeartRate] PedalEcho = tuple[nPedalSensor] CurrentResistance = tuple[nCurrentResistance] TargetResistance = CurrentResistance # Is not separately returned # is displayed by FortiusANT! Speed = Wheel2Speed(tuple[nSpeed]) Buttons = ((tuple[nStatusAndCursors] & 0xf0) >> 4) ^ 0x0f Axis = tuple[nAxis1] CurrentPower = Resistance2Power(CurrentResistance, Speed) else: Error = "Not Found" if debug.on(debug.Function): logfile.Write ("ReceiveFromTrainer() = hr=%s Cadence=%s Speed=%s TargetRes=%s CurrentRes=%s CurrentPower=%s, pe=%s %s" % \ ( HeartRate, Cadence, Speed, TargetResistance, CurrentResistance, CurrentPower, PedalEcho, Error) \ ) return Error, Speed, PedalEcho, HeartRate, CurrentPower, Cadence, TargetResistance, CurrentResistance, Buttons, Axis
def print(self): try: v = self.debug # Verbose: print all command-line variables with values if self.autostart: logfile.Console("-a") if self.PedalStrokeAnalysis: logfile.Console("-A") if self.ble: logfile.Console("-b") if v or self.args.DeviceNumberBase: logfile.Console("-B %s" % self.DeviceNumberBase) if v or self.args.CalibrateRR: logfile.Console("-c %s" % self.CalibrateRR) if v or self.CTRL_SerialL or self.CTRL_SerialR: logfile.Console("-C %s/%s" % (self.CTRL_SerialL, self.CTRL_SerialR)) if v or self.args.debug: logfile.Console("-d %s (%s)" % (self.debug, bin(self.debug))) if v or self.args.antDeviceID: logfile.Console("-D %s" % self.antDeviceID) if v or self.args.GradeAdjust: if self.GradeAdjust == 1: logfile.Console("-G defines Grade = antGrade * %s" \ % (self.GradeFactor ) ) if self.GradeAdjust == 2: logfile.Console("-G defines Grade = antGrade * %s [* %s (downhill)]" \ % (self.GradeFactor, self.GradeFactorDH) ) if self.GradeAdjust == 3: logfile.Console("-G defines Grade = (antGrade - %s) * %s [* %s (downhill)]" \ % (self.GradeShift, self.GradeFactor, self.GradeFactorDH) ) if self.gui: logfile.Console("-g") if self.homeTrainer: logfile.Console("-e") if v or self.args.hrm: logfile.Console("-H %s" % self.hrm) if self.StatusLeds: logfile.Console("-l") if OnRaspberry and (v or self.args.gpioLayout): logfile.Console( "-L %s/%s/%s/%s/%s/%s" % (self.rpiButton, self.rpiTacx, self.rpiShutdown, self.rpiCadence, self.rpiBLE, self.rpiANT)) if self.manual: logfile.Console("-m") if self.manualGrade: logfile.Console("-M") if self.imperial: logfile.Console("-i") if not self.args.calibrate: logfile.Console("-n") if v or self.args.factor: logfile.Console("-p %s" % self.PowerFactor) if v or self.args.OutputDisplay: logfile.Console("-O %s" % self.OutputDisplay) if self.args.PowerMode: logfile.Console("-P") if self.args.Resistance: logfile.Console("-r") if v or self.args.Runoff: logfile.Console("-R defines Runoff: maxSpeed=%s dip=%s minSpeed=%s targetTime=%s power=%s" % \ (self.RunoffMaxSpeed, self.RunoffDip, self.RunoffMinSpeed, self.RunoffTime, self.RunoffPower) ) if self.args.simulate: logfile.Console("-s") #scs if v or self.args.scs: logfile.Console("-S %s" % self.scs ) if v or self.args.TacxType: logfile.Console("-t %s" % self.TacxType) # if v or self.args.Transmission != constants.Transmission: if v or self.args.Transmission: logfile.Console('-T %s x %s (start=%sx%s)' % \ (self.Cranckset, self.Cassette, \ self.Cranckset[self.CrancksetStart], \ self.Cassette [self.CassetteStart]) ) if self.exportTCX: logfile.Console("-x") except: pass # May occur when incorrect command line parameters, error already given before
# Description: Show all dongles available # Open defined dongle # Start listening what's going on in the air # # Output: Console/logfile # # Returns: None # ------------------------------------------------------------------------------ debug.deactivate() clv = cmd.CommandLineVariables() debug.activate(clv.debug) if True or debug.on(debug.Any): logfile.Open('ExplorANT') logfile.Console("ExplorANT started") s = " %17s = %s" logfile.Console(s % ('ExplorANT', __version__)) logfile.Console(s % ('antDongle', ant.__version__)) clv.print() logfile.Console("--------------------") # ------------------------------------------------------------------------------ # First enumerate all dongles # ------------------------------------------------------------------------------ ant.EnumerateAll() # ------------------------------------------------------------------------------ # Open dongle; either the defined one or default
def _SetupDisplaySt7789(self, clv_OutputDisplay): rtn = True # ---------------------------------------------------------------------- # Create the ST7789 display (this is 240x240 version): # ---------------------------------------------------------------------- cs_pin = digitalio.DigitalInOut(board.CE0) dc_pin = digitalio.DigitalInOut(board.D25) reset_pin = None BAUDRATE = 64000000 # Default max is 24Mhz try: spi = board.SPI() # Setup SPI bus using hardware SPI except Exception as e: logfile.Console("OLED display st7789 cannot be initialized: %s" % e) rtn = False else: # ------------------------------------------------------------------ # Now initialize the display # ------------------------------------------------------------------ self.st7789 = st7789.ST7789( spi, cs=cs_pin, dc=dc_pin, rst=reset_pin, baudrate=BAUDRATE, width=240, height=240, x_offset=0, y_offset=80, ) # ------------------------------------------------------------------ # As copied from https://learn.adafruit.com # For testing # ------------------------------------------------------------------ self.backlight = digitalio.DigitalInOut(board.D22) self.backlight.switch_to_output() self.backlight.value = True assert (clv_OutputDisplay in ('st7789', 'st7789b')) if clv_OutputDisplay == 'st7789': self.buttonA = digitalio.DigitalInOut(board.D23) self.buttonB = digitalio.DigitalInOut(board.D24) elif clv_OutputDisplay == 'st7789b': # Add Waveshare 1.3 LCD self.buttonA = digitalio.DigitalInOut(board.D20) self.buttonB = digitalio.DigitalInOut(board.D16) # -------------------------------------------------------------- # The Waveshare is missing the reset circuit from the Adafruit # display, the reset_pin needs to be defined. # -------------------------------------------------------------- cs_pin = digitalio.DigitalInOut(board.CE0) dc_pin = digitalio.DigitalInOut(board.D25) reset_pin = digitalio.DigitalInOut(board.D27) self.buttonA.switch_to_input(digitalio.Pull.UP) self.buttonB.switch_to_input(digitalio.Pull.UP) self.ButtonDefaultValue = self.buttonA.value # ------------------------------------------------------------------ # Startup image is in directory of the .py [or embedded in .exe] # ------------------------------------------------------------------ dirname = os.path.dirname(__file__) FortiusAnt_jpg = os.path.join(dirname, "FortiusAnt.jpg") self.faImage = Image.open(FortiusAnt_jpg) # ------------------------------------------------------------------ # Scale the image to the smaller screen dimension: # ------------------------------------------------------------------ image_ratio = self.faImage.width / self.faImage.height screen_ratio = self.st7789.width / self.st7789.height if screen_ratio < image_ratio: scaled_width = self.faImage.width * self.st7789.height // self.faImage.height scaled_height = self.st7789.height else: scaled_width = self.st7789.width scaled_height = self.faImage.height * self.st7789.width // self.faImage.width self.faImage = self.faImage.resize((scaled_width, scaled_height), Image.BICUBIC) # ------------------------------------------------------------------ # Crop and center the image: # ------------------------------------------------------------------ x_jpg = scaled_width // 2 - self.st7789.width // 2 y_jpg = scaled_height // 2 - self.st7789.height // 2 self.faImage = self.faImage.crop( (x_jpg, y_jpg, x_jpg + self.st7789.width, y_jpg + self.st7789.height)) #------------------------------------------------------------------- # Load a TTF font - other good fonts available from: http://www.dafont.com/bitmap.php #------------------------------------------------------------------- self.fontS = ImageFont.truetype( "/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf", 22) self.fontLb = ImageFont.truetype( "/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf", 36) # ------------------------------------------------------------------ # Display image, for at least three seconds # ------------------------------------------------------------------ self._ShowImage() time.sleep(3) #------------------------------------------------------------------- # Part 2: display control text # Create blank image for drawing with mode 'RGB' for full color # Get drawing object to draw on image #------------------------------------------------------------------- self.image = Image.new("RGB", (self.st7789.width, self.st7789.height)) self.draw = ImageDraw.Draw(self.image) return rtn
def __init__(self): global clv clv = self #----------------------------------------------------------------------- # Define and process command line # Ref: https://docs.python.org/3/library/argparse.html #----------------------------------------------------------------------- parser = argparse.ArgumentParser( description= 'FortiusAnt exchanges data between USB or ANT+ Tacx trainer with an ANT+ or BLE Cycling Training Program' ) parser.add_argument('-a', dest='autostart', help=constants.help_a, required=False, action='store_true') if UseGui: parser.add_argument('-A', dest='PedalStrokeAnalysis', help=constants.help_A, required=False, action='store_true') else: parser.add_argument('-A', dest='A_IgnoredIfDefined', help=argparse.SUPPRESS, required=False, action='store_true') if UseBluetooth: parser.add_argument('-b', dest='ble', help=constants.help_b, required=False, action='store_true') else: pass # If -b is requested but not available, then an error is appropriate parser.add_argument('-B', dest='DeviceNumberBase', metavar='0...65535', help=constants.help_B, required=False, default=False, type=int) parser.add_argument('-c', dest='CalibrateRR', metavar='0...100', help=constants.help_c, required=False, default=False, type=int) # parser.add_argument ('-C', dest='CtrlCommand', metavar='ANT+DeviceID', help=constants.help_C, required=False, default=False, type=int) parser.add_argument('-C', dest='CtrlCommand', metavar='ANT+DeviceID', help=argparse.SUPPRESS, required=False, default=False, type=int) parser.add_argument('-d', dest='debug', metavar='0...255', help=constants.help_d, required=False, default=False, type=int) parser.add_argument('-D', dest='antDeviceID', metavar='USB-DeviceID', help=constants.help_D, required=False, default=False, type=int) parser.add_argument('-e', dest='homeTrainer', help=constants.help_e, required=False, action='store_true') # -h help!! if UseGui: parser.add_argument('-g', dest='gui', help=constants.help_g, required=False, action='store_true') else: parser.add_argument('-g', dest='g_IgnoredIfDefined', help=argparse.SUPPRESS, required=False, action='store_true') parser.add_argument('-G', dest='GradeAdjust', metavar='% / % / %', help=constants.help_G, required=False, default=False) parser.add_argument('-H', dest='hrm', metavar='ANT+DeviceID', help=constants.help_H, required=False, default=False, type=int) parser.add_argument('-i', dest='imperial', help=constants.help_i, required=False, action='store_true') if UseGui or OnRaspberry: parser.add_argument('-l', dest='StatusLeds', help=constants.help_l, required=False, action='store_true') else: parser.add_argument('-l', dest='l_IgnoredIfDefined', help=argparse.SUPPRESS, required=False, action='store_true') if OnRaspberry: parser.add_argument('-L', dest='gpioLayout', metavar='gpioLayout', help=constants.help_L, required=False, default=False) else: parser.add_argument('-L', dest='-L_IgnoredIfDefined', help=argparse.SUPPRESS, required=False, default=False) parser.add_argument('-m', dest='manual', help=constants.help_m, required=False, action='store_true') parser.add_argument('-M', dest='manualGrade', help=constants.help_M, required=False, action='store_true') parser.add_argument('-n', dest='calibrate', help=constants.help_n, required=False, action='store_false') if OnRaspberry: parser.add_argument('-O', dest='OutputDisplay', metavar='see text', help=constants.help_O, required=False, default=False) else: parser.add_argument('-O', dest='O_IgnoredIfDefined', help=argparse.SUPPRESS, required=False, default=False) parser.add_argument('-p', dest='factor', metavar='%', help=constants.help_p, required=False, default=False, type=int) parser.add_argument('-P', dest='PowerMode', help=constants.help_P, required=False, action='store_true') parser.add_argument('-r', dest='Resistance', help=constants.help_r, required=False, action='store_true') parser.add_argument('-R', dest='Runoff', metavar='see text', help=constants.help_R, required=False, default=False) parser.add_argument('-s', dest='simulate', help=constants.help_s, required=False, action='store_true') #scs parser.add_argument ('-S', dest='scs', metavar='ANT+ DeviceID',help=constants.help_S, required=False, default=False, type=int) parser.add_argument('-T', dest='Transmission', metavar='see text', help=constants.help_T, required=False, default=False) self.ant_tacx_models = [ 'Bushido', 'Genius', 'Vortex', 'Magneticbrake', 'Motorbrake' ] parser.add_argument ('-t', dest='TacxType', help=constants.help_t, required=False, default=False, \ choices=self.ant_tacx_models + ['i-Vortex']) # i-Vortex is still allowed for compatibility parser.add_argument('-x', dest='exportTCX', help=constants.help_x, required=False, action='store_true') #----------------------------------------------------------------------- # Parse command line # Overwrite from json file if present #----------------------------------------------------------------------- self.args = parser.parse_args() jsonLoaded = settings.ReadJsonFile(self.args) #----------------------------------------------------------------------- # If nothing specified at all, use sensible defaults #----------------------------------------------------------------------- if len(sys.argv) == 1 and not jsonLoaded: pgm = max(sys.argv[0].rfind('/'), sys.argv[0].rfind('\\')) + 1 pgm = sys.argv[0][pgm:] print( '---------------------------------------------------------------' ) print('Hello!') print( 'You have started FortiusANT without command-line parameters.') print(' ') print('Therefore we start with a best-practice setting:') print(' %s -a -g -H0 -A -l' % pgm) print(' ') print('If you want to start without the graphical user interface:') print(' %s -a' % pgm) print(' ') print('For more info, please refer to the wiki on github.') print('Success!') self.args.autostart = True self.args.gui = UseGui # Show gui self.args.hrm = 0 # Pair with HRM self.args.PedalStrokeAnalysis = UseGui # Show it self.args.StatusLeds = UseGui # Show it #----------------------------------------------------------------------- # Display welcome message #----------------------------------------------------------------------- print( '---------------------------------------------------------------') print('FortiusANT is open source and can be used freely.') print('') print('Just for the fun of knowing where you all are training,') print('put yourself on the FortiusANT map by making yourself known') print('by leaving a message with name/location/trainer on') print('https://github.com/WouterJD/FortiusANT/issues/14') print('') print( 'or visit the sponsoring page https://github.com/sponsors/WouterJD' ) print( '---------------------------------------------------------------') #----------------------------------------------------------------------- # Booleans; either True or False #----------------------------------------------------------------------- self.autostart = self.args.autostart if UseBluetooth: self.ble = self.args.ble if UseGui: self.gui = self.args.gui self.homeTrainer = self.args.homeTrainer # Exersize Bike self.StatusLeds = self.args.StatusLeds self.imperial = self.args.imperial self.manual = self.args.manual self.manualGrade = self.args.manualGrade self.calibrate = self.args.calibrate self.PowerMode = self.args.PowerMode if UseGui: self.PedalStrokeAnalysis = self.args.PedalStrokeAnalysis self.Resistance = self.args.Resistance self.SimulateTrainer = self.args.simulate self.exportTCX = self.args.exportTCX or self.homeTrainer or self.manual or self.manualGrade i = 0 if self.homeTrainer: i += 1 if self.manual: i += 1 if self.manualGrade: i += 1 if i > 1: logfile.Console( "Only one of -h, -m and -M may be specified; manual power selected" ) self.homeTrainer = False self.manual = True self.manualGrade = False if (self.homeTrainer or self.manual or self.manualGrade) and self.SimulateTrainer: logfile.Console( "-e/-m/-M and -s both specified, most likely for program test purpose" ) #----------------------------------------------------------------------- # Get DeviceNumberBase #----------------------------------------------------------------------- if self.args.DeviceNumberBase: try: self.DeviceNumberBase = int(self.args.DeviceNumberBase) except: logfile.Console( 'Command line error; -B incorrect DeviceNumber Base=%s' % self.args.DeviceNumberBase) #----------------------------------------------------------------------- # Get calibration of Rolling Resistance # Not limitted to a range here, because can be different for different # types of brakes, although initially only used for Magnetic Brake #----------------------------------------------------------------------- if self.args.CalibrateRR: try: self.CalibrateRR = float( self.args.CalibrateRR.replace(',', '.')) except: logfile.Console( 'Command line error; -c incorrect calibration of Rolling Resistance=%s' % self.args.CalibrateRR) #----------------------------------------------------------------------- # Get CtrlCommand = Serial#1/Serial#2 #----------------------------------------------------------------------- if self.args.CtrlCommand: s = self.args.CtrlCommand.split("/") try: assert (len(s) <= 2) if len(s) >= 1: self.CTRL_SerialL = int(s[0]) if len(s) >= 2: self.CTRL_SerialR = int(s[1]) assert (self.CTRL_SerialL >= 0) assert (self.CTRL_SerialR >= 0) except: logfile.Console( 'Command line error; -C incorrect SerialNumber in %s' % self.args.CtrlCommand) #----------------------------------------------------------------------- # Get OutputDisplay #----------------------------------------------------------------------- if OnRaspberry and self.args.OutputDisplay: s = self.args.OutputDisplay.split("/") try: assert (len(s) <= 2) if len(s) >= 1: self.OutputDisplay = s[0] if len(s) >= 2: self.OutputDisplayR = int(s[1]) # Rotation if self.OutputDisplay == 'display': self.OutputDisplay = 'st7789' # Default device driver # As long as no more devices supported, no reason to bother user # Like this we're ready for other devices in future assert (self.OutputDisplay in ('console', 'st7789')) assert (self.OutputDisplayR in (0, 90, 180, 270)) assert (self.CTRL_SerialL >= 0) assert (self.CTRL_SerialR >= 0) except: logfile.Console( 'Command line error; -O incorrect OutputDisplay in %s' % self.args.CtrlCommand) #--------------------------------------------------------------- # OutputDisplay uses first 24 pins, so our button moves # Refer to pinlayout in raspberry.py #--------------------------------------------------------------- if self.OutputDisplay == 'st7789': self.rpiButton = 16 #----------------------------------------------------------------------- # Get Raspberry GPIO Layout = Button/Tacx/Shutdown/Cadence/BLE/ANT #----------------------------------------------------------------------- if OnRaspberry and self.args.gpioLayout: s = self.args.gpioLayout.split("/") try: e = 'Too many GPIO pins defined' assert (len(s) <= 6) e = 'Pin number not numeric' if len(s) >= 1: self.rpiButton = int(s[0]) if len(s) >= 2: self.rpiTacx = int(s[1]) if len(s) >= 3: self.rpiShutdown = int(s[2]) if len(s) >= 4: self.rpiCadence = int(s[3]) if len(s) >= 5: self.rpiBLE = int(s[4]) if len(s) >= 6: self.rpiANT = int(s[5]) # https://gpiozero.readthedocs.io/en/stable/recipes.html#pin-numbering e = 'Pin number not in range 2...27' assert (self.rpiButton in range(2, 28)) assert (self.rpiTacx in range(2, 28)) assert (self.rpiShutdown in range(2, 28)) assert (self.rpiCadence in range(2, 28)) assert (self.rpiBLE in range(2, 28)) assert (self.rpiANT in range(2, 28)) e = 'Pin number used twice' assert (self.rpiButton not in (self.rpiTacx, self.rpiShutdown, self.rpiCadence, self.rpiBLE, self.rpiANT)) assert (self.rpiTacx not in (self.rpiButton, self.rpiShutdown, self.rpiCadence, self.rpiBLE, self.rpiANT)) assert (self.rpiShutdown not in (self.rpiButton, self.rpiTacx, self.rpiCadence, self.rpiBLE, self.rpiANT)) assert (self.rpiCadence not in (self.rpiButton, self.rpiTacx, self.rpiShutdown, self.rpiBLE, self.rpiANT)) assert (self.rpiBLE not in (self.rpiButton, self.rpiTacx, self.rpiShutdown, self.rpiCadence, self.rpiANT)) assert (self.rpiANT not in (self.rpiButton, self.rpiTacx, self.rpiShutdown, self.rpiCadence, self.rpiBLE)) except: logfile.Console( 'Command line error; -L incorrect pin-number in %s; %s' % (self.args.gpioLayout, e)) #----------------------------------------------------------------------- # Get debug-flags, used in debug module #----------------------------------------------------------------------- if self.args.debug: try: self.debug = int(self.args.debug) except: logfile.Console( 'Command line error; -d incorrect debugging flags=%s' % self.args.debug) #----------------------------------------------------------------------- # Get antDeviceID #----------------------------------------------------------------------- if self.args.antDeviceID: try: self.antDeviceID = int(self.args.antDeviceID) except: logfile.Console( 'Command line error; -D incorrect antDeviceID=%s' % self.args.antDeviceID) #----------------------------------------------------------------------- # Get HRM # - None: read HRM from Tacx Fortius and broadcast as HRM master device # - -1 : no master and no slave device # - 0 : pair with the first ANT+ HRM that is found # - next: pair with the defined ANT+ HRM monitor # the number can be found with ExplorANT #----------------------------------------------------------------------- if self.args.hrm: try: self.hrm = int(self.args.hrm) except: logfile.Console('Command line error; -H incorrect HRM=%s' % self.args.hrm) #----------------------------------------------------------------------- # Get SCS # - None: No Speed Cadence Sensor # - 0 : pair with the first ANT+ SCS that is found # - next: pair with the defined ANT+ SCS # the number can be found with ExplorANT #----------------------------------------------------------------------- #scs if self.args.scs: #scs try: #scs self.scs = int(self.args.scs) #scs except: #scs logfile.Console('Command line error; -S incorrect SCS=%s' % self.args.scs) #----------------------------------------------------------------------- # Get powerfactor #----------------------------------------------------------------------- if self.args.factor: try: self.PowerFactor = int(self.args.factor) / 100 # I would expect +/- 15% would be enough for compensation, but # reactions from "the field" have reported 35% difference. # So the range became 50% ... 150% # # If the power is good and you specify 50% then you will report # 500 Watt when producing 250Watt and ... you're cheating... # # HomeTrainer users may want a low resistance but a reasonable # power on Zwift or Strava, otherwise the mountain becomes # endless. So the lower range is set to 20%. # When producing 50 Watts, Rouvy will receive 250 Watt and you # can cycle uphill. self.PowerFactor = max(0.2, min(1.5, self.PowerFactor)) except: logfile.Console( 'Command line error; -p incorrect power factor=%s' % self.args.factor) #----------------------------------------------------------------------- # Get GradeAdjust = shift/factor # Motor Brake default = -G 100/100 # Magnetic Brake suggested (Rouvy) = -G 50/100 #----------------------------------------------------------------------- if self.args.GradeAdjust: s = self.args.GradeAdjust.split("/") self.GradeAdjust = len(s) #------------------------------------------------------------------- # parameter1: factor (default = 1, allowed = 0...100%) # The target slope is divided by this number. # Factor = 50 means: requested slope = 20% --> 10% #------------------------------------------------------------------- # parameter2: factor (default = 1, allowed = 0...100%) # The target slope is divided by this number (if negative). # Factor = 50 means: requested slope = -20% --> -10% #------------------------------------------------------------------- # parameter3: shift percentage (default = 0, allowed = 0...20) # The target slope is incremented by this number. # Shift = 10 means: requested slope = -10% --> 0% #------------------------------------------------------------------- try: assert (len(s) <= 3) if len(s) >= 1 and s[0]: self.GradeFactor = max(0, min(1, int(s[0]) / 100)) if len(s) >= 2 and s[1]: self.GradeFactorDH = max(0, min(1, int(s[1]) / 100)) if len(s) >= 3 and s[2]: self.GradeShift = max(0, min(20, int(s[2]))) except: logfile.Console( 'Command line error; -G incorrect Grade Adjust=%s' % self.args.GradeAdjust) self.GradeAdjust = 0 #----------------------------------------------------------------------- # Get RunOff definition # All defined as int (float seems not useful) with exception of Time #----------------------------------------------------------------------- if self.args.Runoff: s = self.args.Runoff.split("/") try: assert (len(s) <= 5) if len(s) >= 1 and s[0]: self.RunoffMaxSpeed = max(20, min(50, int(s[0]))) # km/hr if len(s) >= 2 and s[1]: self.RunoffDip = max(0, min(5, int(s[1]))) # km/hr if len(s) >= 3 and s[2]: self.RunoffMinSpeed = max(0, min(10, int(s[2]))) # km/hr if len(s) >= 4 and s[3]: self.RunoffTime = max(0, min(10, float(s[3]))) # seconds if len(s) >= 5 and s[4]: self.RunoffPower = max(0, min(500, int(s[4]))) # Watt assert (self.RunoffMinSpeed <= self.RunoffMaxSpeed - self.RunoffDip * 2) except: logfile.Console( 'Command line error; -R incorrect Runoff definition %s' % self.args.Runoff) #----------------------------------------------------------------------- # Get TacxType #----------------------------------------------------------------------- AntRequired = False if self.args.TacxType: self.TacxType = self.args.TacxType if 'Vortex' in self.TacxType: self.Tacx_Vortex = True self.Tacx_Cadence = False AntRequired = True elif 'Genius' in self.TacxType: self.Tacx_Genius = True self.Tacx_Cadence = False AntRequired = True elif 'Bushido' in self.TacxType: self.Tacx_Bushido = True self.Tacx_Cadence = False AntRequired = True elif 'Magneticbrake' in self.TacxType: self.Tacx_Magneticbrake = True elif 'Motorbrake' in self.TacxType: self.Tacx_MotorBrake = True else: logfile.Console('Command line error; -t incorrect value=%s' % self.args.TacxType) self.args.TacxType = False if AntRequired and self.antDeviceID == -1: logfile.Console( 'You have selected an ANT-trainer (-t %s) and de-selected ANT-dongle (-D-1); -D-1 ignored.' % self.TacxType) self.antDeviceID = None #----------------------------------------------------------------------- # Get Transmission # Default = "34-50 x 34-30-27-25-23-21-19-17-15-13-11" # MTB single = "32 x 50-42-36-32-28-24-21-18-16-14-12-10" # MTB double = "24-36 x 36-32-28-24-21-19-17-15-13-11" # MTB triple = "22-34-44 x 36-32-28-24-21-19-17-15-13-11" # MTB 3 x 13 = "22-34-44 x 50-42-36-32-28-24-21-18-16-14-12-10" #----------------------------------------------------------------------- self.Transmission = self.args.Transmission while True: #------------------------------------------------------------------- # Use command-line value, if fails - use default #------------------------------------------------------------------- try: self.Cranckset = [] self.CrancksetStart = 0 # The initial value of index self.CrancksetMax = 0 # Corresponds to full WH of the drawing area self.Cassette = [] self.CassetteStart = 0 # The initial value of index self.CassetteMax = 0 # Corresponds to full WH of the drawing area self.Transmission = self.Transmission.replace(' ', '') #--------------------------------------------------------------- # Split in crackset and cassette #--------------------------------------------------------------- s1 = self.Transmission.split("x") assert (len(s1) == 2) #--------------------------------------------------------------- # Split cranckset into chainrings (max 3) #--------------------------------------------------------------- s2 = s1[0].split("-") for i in range(0, len(s2)): chainring = s2[i] if (chainring.find('*') != -1): self.CrancksetStart = i chainring = chainring.replace('*', '') self.Cranckset.append(int(chainring)) if int(chainring) > self.CrancksetMax: self.CrancksetMax = int(chainring) if i == 2: break #--------------------------------------------------------------- # Split cassette into sprockets (max 13) #--------------------------------------------------------------- s2 = s1[1].split("-") for i in range(0, len(s2)): sprocket = s2[i] if (sprocket.find('*') != -1): self.CassetteStart = i sprocket = sprocket.replace('*', '') self.Cassette.append(int(sprocket)) if int(sprocket) > self.CassetteMax: self.CassetteMax = int(sprocket) if i == 12: break except: if self.Transmission: logfile.Console( 'Command line error; -T incorrect Transmission=%s' % self.args.Transmission) self.Transmission = constants.Transmission else: break #----------------------------------------------------------------------- # If no start defined, take middle position #----------------------------------------------------------------------- if self.CrancksetStart == 0: self.CrancksetStart = int(round(len(self.Cranckset) / 2 - 0.5)) if self.CassetteStart == 0: self.CassetteStart = int(round(len(self.Cassette) / 2 - 0.5)) #----------------------------------------------------------------------- # Check pedal stroke analysis #----------------------------------------------------------------------- if self.PedalStrokeAnalysis and (not self.args.gui or self.Tacx_Vortex or self.Tacx_Genius or self.Tacx_Bushido): logfile.Console( "Pedal stroke analysis is not possible in console mode or this Tacx type" ) self.PedalStrokeAnalysis = False
def print(self): try: v = debug.on(debug.Any) # Verbose: print all command-line variables with values if self.autostart: logfile.Console("-a") if self.PedalStrokeAnalysis:logfile.Console("-A") if v or self.args.CalibrateRR: logfile.Console("-c %s" % self.CalibrateRR ) if v or self.args.debug: logfile.Console("-d %s (%s)" % (self.debug, bin(self.debug) ) ) if v or self.args.antDeviceID: logfile.Console("-D %s" % self.antDeviceID ) if v or self.args.GradeAdjust: if self.GradeAdjust == 1: logfile.Console("-G defines Grade = antGrade * %s" \ % (self.GradeFactor ) ) if self.GradeAdjust == 2: logfile.Console("-G defines Grade = antGrade * %s [* %s (downhill)]" \ % (self.GradeFactor, self.GradeFactorDH) ) if self.GradeAdjust == 3: logfile.Console("-G defines Grade = (antGrade - %s) * %s [* %s (downhill)]" \ % (self.GradeShift, self.GradeFactor, self.GradeFactorDH) ) if self.gui: logfile.Console("-g") if v or self.args.hrm: logfile.Console("-H %s" % self.hrm ) if self.manual: logfile.Console("-m") if self.manualGrade: logfile.Console("-M") if not self.args.calibrate: logfile.Console("-n") if v or self.args.factor: logfile.Console("-p %s" % self.PowerFactor ) if self.args.PowerMode: logfile.Console("-P") if self.args.Resistance: logfile.Console("-r") if self.args.simulate: logfile.Console("-s") #scs if v or self.args.scs: logfile.Console("-S %s" % self.scs ) if v or self.args.TacxType: logfile.Console("-t %s" % self.TacxType) if self.exportTCX: logfile.Console("-x") except: pass # May occur when incorrect command line parameters, error already given before
def __init__(self): global clv clv = self #----------------------------------------------------------------------- # Define and process command line #----------------------------------------------------------------------- parser = argparse.ArgumentParser(description='Program to broadcast data from USB Tacx Fortius trainer, and to receive resistance data for the trainer') parser.add_argument('-a','--autostart', help='Automatically start', required=False, action='store_true') parser.add_argument('-A','--PedalStrokeAnalysis', help='Pedal Stroke Analysis', required=False, action='store_true') parser.add_argument('-c','--CalibrateRR',help='calibrate Rolling Resistance for Magnetic Brake', required=False, default=False) parser.add_argument('-d','--debug', help='Show debugging data', required=False, default=False) parser.add_argument('-D','--antDeviceID',help='Use this antDongle type only', required=False, default=False) parser.add_argument('-g','--gui', help='Run with graphical user interface', required=False, action='store_true') parser.add_argument('-G','--GradeAdjust',help='Adjust slope%% in GradeMode (factor/factorDownhill)',required=False, default=False) parser.add_argument('-H','--hrm', help='Pair this ANT+ Heart Rate Monitor (0: any, -1: none); Tacx HRM is used if not specified', required=False, default=False) parser.add_argument('-m','--manual', help='Run manual power (ignore target from ANT+ Dongle)', required=False, action='store_true') parser.add_argument('-M','--manualGrade',help='Run manual grade (ignore target from ANT+ Dongle)', required=False, action='store_true') parser.add_argument('-n','--calibrate', help='Do not calibrate before start', required=False, action='store_false') parser.add_argument('-p','--factor', help='Adjust target Power by multiplying by this factor for static calibration', required=False, default=False) parser.add_argument('-P','--PowerMode', help='Power mode has preference over Resistance mode (for 30 seconds)', required=False, action='store_true') parser.add_argument('-r','--Resistance',help='Target Resistance = Target Power (to create power curve)', required=False, action='store_true') parser.add_argument('-s','--simulate', help='Simulated trainer to test ANT+ connectivity', required=False, action='store_true') #scs parser.add_argument('-S','--scs', help='Pair this Speed Cadence Sensor (0: default device)', required=False, default=False) parser.add_argument('-t','--TacxType', help='Specify Tacx Type; e.g. i-Vortex, default=autodetect',required=False, default=False) parser.add_argument('-x','--exportTCX', help='Export TCX file', required=False, action='store_true') #----------------------------------------------------------------------- # Parse #----------------------------------------------------------------------- args = parser.parse_args() self.args = args #----------------------------------------------------------------------- # Booleans; either True or False #----------------------------------------------------------------------- self.autostart = args.autostart self.gui = args.gui self.manual = args.manual self.manualGrade = args.manualGrade self.calibrate = args.calibrate self.PowerMode = args.PowerMode self.PedalStrokeAnalysis = args.PedalStrokeAnalysis self.Resistance = args.Resistance self.SimulateTrainer = args.simulate self.exportTCX = args.exportTCX or self.manual or self.manualGrade if self.manual and self.manualGrade: logfile.Console("-m and -M are mutually exclusive; manual power selected") self.manualGrade = False # Mutually exclusive if (self.manual or self.manualGrade) and self.SimulateTrainer: logfile.Console("-m/-M and -s both specified, most likely for program test purpose") #----------------------------------------------------------------------- # Get calibration of Rolling Resistance #----------------------------------------------------------------------- if args.CalibrateRR: try: self.CalibrateRR = float(args.CalibrateRR.replace(',', '.')) except: logfile.Console('Command line error; -c incorrect calibration of Rolling Resistance=%s' % args.CalibrateRR) #----------------------------------------------------------------------- # Get debug-flags, used in debug module #----------------------------------------------------------------------- if args.debug: try: self.debug = int(args.debug) except: logfile.Console('Command line error; -d incorrect debugging flags=%s' % args.debug) #----------------------------------------------------------------------- # Get antDeviceID #----------------------------------------------------------------------- if args.antDeviceID: try: self.antDeviceID = int(args.antDeviceID) except: logfile.Console('Command line error; -D incorrect antDeviceID=%s' % args.antDeviceID) #----------------------------------------------------------------------- # Get HRM # - None: read HRM from Tacx Fortius and broadcast as HRM master device # - -1 : no master and no slave device # - 0 : pair with the first ANT+ HRM that is found # - next: pair with the defined ANT+ HRM monitor # the number can be found with ExplorANT #----------------------------------------------------------------------- if args.hrm: try: self.hrm = int(args.hrm) except: logfile.Console('Command line error; -H incorrect HRM=%s' % args.hrm) #----------------------------------------------------------------------- # Get SCS # - None: No Speed Cadence Sensor # - 0 : pair with the first ANT+ SCS that is found # - next: pair with the defined ANT+ SCS # the number can be found with ExplorANT #----------------------------------------------------------------------- #scs if args.scs: #scs try: #scs self.scs = int(args.scs) #scs except: #scs logfile.Console('Command line error; -S incorrect SCS=%s' % args.scs) #----------------------------------------------------------------------- # Get powerfactor #----------------------------------------------------------------------- if args.factor: try: self.PowerFactor = int(args.factor)/100 except: logfile.Console('Command line error; -f incorrect power factor=%s' % args.factor) #----------------------------------------------------------------------- # Get GradeAdjust = shift/factor # Motor Brake default = -G 100/100 # Magnetic Brake suggested (Rouvy) = -G 50/100 #----------------------------------------------------------------------- if args.GradeAdjust: s = args.GradeAdjust.split("/") self.GradeAdjust = len(s) #------------------------------------------------------------------- # parameter1: factor (default = 1, allowed = 0...100%) # The target slope is divided by this number. # Factor = 5 means: requested slope = 20% --> 4% #------------------------------------------------------------------- if len(s) >= 1: try: self.GradeFactor = int(s[0]) / 100 self.GradeFactor = max( 0, self.GradeFactor) self.GradeFactor = min( 1, self.GradeFactor) except: logfile.Console('Command line error; -G incorrect Grade Adjust=%s' % args.GradeAdjust) self.GradeAdjust = 0 #------------------------------------------------------------------- # parameter2: factor (default = 1, allowed = 0...100%) # The target slope is divided by this number. # Factor = 5 means: requested slope = 20% --> 4% #------------------------------------------------------------------- if len(s) >= 2: try: self.GradeFactorDH = int(s[1]) / 100 self.GradeFactorDH = max( 0, self.GradeFactorDH) self.GradeFactorDH = min( 1, self.GradeFactorDH) except: logfile.Console('Command line error; -G incorrect Grade Adjust=%s' % args.GradeAdjust) self.GradeAdjust = 0 #------------------------------------------------------------------- # parameter3: shift percentage (default = 0, allowed = 0...20) # The target slope is incremented by this number. # Shift = 10 means: requested slope = -10% --> 0% #------------------------------------------------------------------- if len(s) >= 3: try: self.GradeShift = int(s[2]) self.GradeShift = max( 0, self.GradeShift) self.GradeShift = min(20, self.GradeShift) except: logfile.Console('Command line error; -G incorrect Grade Adjust=%s' % args.GradeAdjust) self.GradeAdjust = 0 #----------------------------------------------------------------------- # Get TacxType #----------------------------------------------------------------------- if args.TacxType: self.TacxType = args.TacxType if self.TacxType in ('i-Vortex'): self.Tacx_iVortex = True else: logfile.Console('Command line error; -t incorrect value=%s' % args.TacxType) args.TacxType = False #----------------------------------------------------------------------- # Check pedal stroke analysis #----------------------------------------------------------------------- if args.PedalStrokeAnalysis and (not args.gui or self.Tacx_iVortex): logfile.Console("Pedal stroke analysis is not possible in console mode or this Tacx type") self.PedalStrokeAnalysis = False #----------------------------------------------------------------------- # If nothing specified at all, help the poor windows-users #----------------------------------------------------------------------- if len(sys.argv) == 1: pgm = max(sys.argv[0].rfind('/'), sys.argv[0].rfind('\\')) + 1 pgm = sys.argv[0][pgm:] print('---------------------------------------------------------------') print('Hello!') print('You have started FortiusANT without command-line parameters.') print(' ') print('Therefore we start with a best-practice setting:') print(' %s -a -g -H0 -A' % pgm) print(' ') print('If you want to start without the graphical user interface:') print(' %s -a' % pgm) print(' ') print('For more info, please refer to the wiki on github.') print('Succes!') print('---------------------------------------------------------------') print('FortiusANT is open source and can freely be used.') print('') print('A free gift would be appreciated:') print('Put yourself on the FortiusANT map by making yourself known') print('by leaving a message with name/location/trainer on') print('https://github.com/WouterJD/FortiusANT/issues/14') print('') print('Just for the fun of knowing where we are training.') print('---------------------------------------------------------------') self.autostart = True self.gui = True # Show gui self.hrm = 0 # Pair with HRM self.PedalStrokeAnalysis = True # Show it
# ============================================================================== if __name__ == "__main__": multiprocessing.freeze_support() global clv # -------------------------------------------------------------------------- # Initialize # -------------------------------------------------------------------------- debug.deactivate() clv = cmd.CommandLineVariables() debug.activate(clv.debug) FortiusAntBody.Initialize(clv) if debug.on(debug.Any): logfile.Open() logfile.Console("FortiusANT started") clv.print() logfile.Console("------------------") #------------------------------------------------------------------------------- # Component info #------------------------------------------------------------------------------- if debug.on(debug.Any): logfile.Write('Version info for the components') s = " %20s = %s" logfile.Write(s % ('FortiusAnt', __version__)) logfile.Write(s % ('antDongle', ant.__version__)) logfile.Write(s % ('antHRM', hrm.__version__)) logfile.Write(s % ('antFE', fe.__version__)) logfile.Write(s % ('debug', debug.__version__)) logfile.Write(s % ('FortiusAntBody', FortiusAntBody.__version__))
def __init__(self): global clv clv = self #----------------------------------------------------------------------- # Define and process command line #----------------------------------------------------------------------- parser = argparse.ArgumentParser( description= 'Program to broadcast data from USB Tacx Fortius trainer, and to receive resistance data for the trainer' ) parser.add_argument('-a', '--autostart', help='Automatically start', required=False, action='store_true') parser.add_argument('-A', '--PedalStrokeAnalysis', help='Pedal Stroke Analysis', required=False, action='store_true') parser.add_argument('-d', '--debug', help='Show debugging data', required=False, default=False) parser.add_argument('-g', '--gui', help='Run with graphical user interface', required=False, action='store_true') parser.add_argument( '-H', '--hrm', help='Pair this Heart Rate Monitor (0: any, -1: none)', required=False, default=False) parser.add_argument( '-m', '--manual', help='Run manual power (ignore target from ANT+ Dongle)', required=False, action='store_true') parser.add_argument( '-M', '--manualGrade', help='Run manual grade (ignore target from ANT+ Dongle)', required=False, action='store_true') parser.add_argument('-n', '--calibrate', help='Do not calibrate before start', required=False, action='store_false') parser.add_argument( '-p', '--factor', help= 'Adjust target Power by multiplying by this factor for static calibration', required=False, default=False) parser.add_argument( '-P', '--PowerMode', help= 'Power mode has preference over Resistance mode (for 30 seconds)', required=False, action='store_true') parser.add_argument('-s', '--simulate', help='Simulated trainer to test ANT+ connectivity', required=False, action='store_true') #scs parser.add_argument('-S','--scs', help='Pair this Speed Cadence Sensor (0: default device)', required=False, default=False) parser.add_argument( '-t', '--TacxType', help='Specify Tacx Type; e.g. i-Vortex, default=autodetect', required=False, default=False) parser.add_argument('-u', '--uphill', help='Uphill only; negative grade is ignored', required=False, action='store_true') #----------------------------------------------------------------------- # Deprecated #----------------------------------------------------------------------- # s = '%s,%s/%s,%s/%s' % (self.__tyre__, self.__fL__, self.__fS__, self.__rS__, self.__rL__) # parser.add_argument('-b','--bicycle', help='Bicycle definition, default=' + s, required=False, default=False) # parser.add_argument('-f','--ftp', help='FTP of the rider, default=%s' % self.__ftp__, required=False, default=False) # parser.add_argument('-r','--resistance',help='FTP percentages for resistance mode, default=150/100',required=False, default=False) #----------------------------------------------------------------------- # Parse #----------------------------------------------------------------------- args = parser.parse_args() self.args = args #----------------------------------------------------------------------- # Booleans; either True or False #----------------------------------------------------------------------- self.autostart = args.autostart self.gui = args.gui self.manual = args.manual self.manualGrade = args.manualGrade self.calibrate = args.calibrate self.PowerMode = args.PowerMode self.PedalStrokeAnalysis = args.PedalStrokeAnalysis self.SimulateTrainer = args.simulate self.uphill = args.uphill if self.manual and self.manualGrade: logfile.Console( "-m and -M are mutually exclusive; manual power selected") self.manualGrade = False # Mutually exclusive if (self.manual or self.manualGrade) and self.SimulateTrainer: logfile.Console( "-m/-M and -s both specified, most likely for program test purpose" ) #----------------------------------------------------------------------- # Bicycle definition to be parsed; three parameters # format=tyre,chainring,cassette, e.g. "2.096,50/34,15/25" #----------------------------------------------------------------------- # if args.bicycle: # b = args.bicycle.split(",") # #------------------------------------------------------------------- # # parameter1: Tyre defined? # #------------------------------------------------------------------- # if len(b) >= 1: # try: # self.tyre = float(b[0]) # if self.tyre < 1: self.tyre = self.__tyre__ # except: # logfile.Console('Command line error; -b incorrect tyre=%s' % b[0]) # self.tyre = self.__tyre__ # #------------------------------------------------------------------- # # parameter2: Chainring, large/small separated by / # # format=large/small, e.g. 50/34 # # If one value is specified, e.g. "50" 50/50 is assumed # #------------------------------------------------------------------- # if len(b) >= 2: # s = b[1].split('/') # if len(s) >= 0: # try: # self.fL = int(s[0]) # except: # logfile.Console('Command line error; -b incorrect large chainring=%s' % s[0]) # self.fS = self.fL # Default is single chainring # if len(s) >= 1: # try: # self.fS = int(s[1]) # except: # logfile.Console('Command line error; -b incorrect small chainring=%s' % s[1]) # #------------------------------------------------------------------- # # parameter3: Cassette, small/large separated by / # # If one value is specified, e.g. "15" 15/15 is assumed # #------------------------------------------------------------------- # if len(b) >= 3: # s = b[2].split('/') # if len(s) >= 0: # try: # self.rS = int(s[0]) # except: # logfile.Console('Command line error; -b incorrect small cassette=%s' % s[0]) # self.rL = self.rS # Default is single speed cassette # if len(s) >= 1: # try: # self.rL = int(s[1]) # except: # logfile.Console('Command line error; -b incorrect large cassette=%s' % s[1]) #----------------------------------------------------------------------- # Get debug-flags, used in debug module #----------------------------------------------------------------------- if args.debug: try: self.debug = int(args.debug) except: logfile.Console( 'Command line error; -d incorrect debugging flags=%s' % args.debug) #----------------------------------------------------------------------- # Get riders FTP #----------------------------------------------------------------------- # if args.ftp: # try: # self.ftp = int(args.ftp) # if self.ftp < 50: self.ftp = self.__ftp__ # except: # logfile.Console('Command line error; -f incorrect ftp=%s' % args.ftp) #----------------------------------------------------------------------- # Get HRM # - None: read HRM from Tacx Fortius and broadcast as HRM master device # - -1 : no master and no slave device # - 0 : pair with the first ANT+ HRM that is found # - next: pair with the defined ANT+ HRM monitor # the number can be found with ExplorANT #----------------------------------------------------------------------- if args.hrm: try: self.hrm = int(args.hrm) except: logfile.Console('Command line error; -H incorrect HRM=%s' % args.hrm) #----------------------------------------------------------------------- # Get SCS # - None: No Speed Cadence Sensor # - 0 : pair with the first ANT+ SCS that is found # - next: pair with the defined ANT+ SCS # the number can be found with ExplorANT #----------------------------------------------------------------------- #scs if args.scs: #scs try: #scs self.scs = int(args.scs) #scs except: #scs logfile.Console('Command line error; -S incorrect SCS=%s' % args.scs) #----------------------------------------------------------------------- # Get powerfactor #----------------------------------------------------------------------- if args.factor: try: self.PowerFactor = float(args.factor) except: logfile.Console( 'Command line error; -f incorrect power factor=%s' % args.factor) #----------------------------------------------------------------------- # Parse Resistance #----------------------------------------------------------------------- # if args.resistance: # s = args.resistance.split('/') # if len(s) >= 0: # try: # self.ResistanceH = int(s[0]) # except: # logfile.Console('Command line error; -r incorrect high resistance=%s' % s[0]) # if len(s) >= 1: # try: # self.ResistanceL = int(s[1]) # except: # logfile.Console('Command line error; -r incorrect low resistance=%s' % s[1]) #----------------------------------------------------------------------- # Get TacxType #----------------------------------------------------------------------- if args.TacxType: self.TacxType = args.TacxType if self.TacxType in ('i-Vortex'): self.Tacx_iVortex = True else: logfile.Console('Command line error; -t incorrect value=%s' % args.TacxType) args.TacxType = False #----------------------------------------------------------------------- # Check pedal stroke analysis #----------------------------------------------------------------------- if args.PedalStrokeAnalysis and (not args.gui or self.Tacx_iVortex): logfile.Console( "Pedal stroke analysis is not possible in console mode or this Tacx type" ) self.PedalStrokeAnalysis = False #----------------------------------------------------------------------- # If nothing specified at all, help the poor windows-users #----------------------------------------------------------------------- if len(sys.argv) == 1: pgm = max(sys.argv[0].rfind('/'), sys.argv[0].rfind('\\')) + 1 pgm = sys.argv[0][pgm:] print( '---------------------------------------------------------------' ) print('Hello!') print( 'You have started FortiusANT without command-line parameters.') print(' ') print('Therefore we start with a best-practice setting:') print(' %s -a -g -H0 -A' % pgm) print(' ') print('If you want to start without the graphical user interface:') print(' %s -a' % pgm) print(' ') print('For more info, please refer to the wiki on github.') print('Succes!') print( '---------------------------------------------------------------' ) print('FortiusANT is open source and can freely be used.') print('') print('A free gift would be appreciated:') print( 'Put yourself on the FortiusANT map by making yourself known') print('by leaving a message with name/location/trainer on') print('https://github.com/WouterJD/FortiusANT/issues/14') print('') print('Just for the fun of knowing where we are training.') print( '---------------------------------------------------------------' ) self.autostart = True self.gui = True # Show gui self.hrm = 0 # Pair with HRM self.PedalStrokeAnalysis = True # Show it
def __init__(self): #----------------------------------------------------------------------- # Define and process command line #----------------------------------------------------------------------- parser = argparse.ArgumentParser(description='Program to broadcast data from USB Tacx Fortius trainer, and to receive resistance data for the trainer') parser.add_argument('-a','--autostart', help='Automatically start', required=False, action='store_true') s = '%s,%s/%s,%s/%s' % (self.__tyre__, self.__fL__, self.__fS__, self.__rS__, self.__rL__) parser.add_argument('-b','--bicycle', help='Bicycle definition, default=' + s, required=False, default=False) parser.add_argument('-d','--debug', help='Show debugging data', required=False, default=False) parser.add_argument('-f','--ftp', help='FTP of the rider, default=%s' % self.__ftp__, required=False, default=False) parser.add_argument('-g','--gui', help='Run with graphical user interface', required=False, action='store_true') parser.add_argument('-H','--hrm', help='Use this Heart Rate Monitor (0: default, -1: none)', required=False, default=False) parser.add_argument('-m','--manual', help='Run manual power (ignore target from antDongle)', required=False, action='store_true') parser.add_argument('-M','--manualGrade',help='Run manual grade (ignore target from antDongle)', required=False, action='store_true') parser.add_argument('-n','--calibrate', help='Do not calibrate before start', required=False, action='store_false') parser.add_argument('-p','--factor', help='Adjust target Power by multiplying by this factor for static calibration', required=False, default=False) parser.add_argument('-P','--PowerMode', help='Power mode has preference over Resistance mode (for 30 seconds)', required=False, action='store_true') parser.add_argument('-r','--resistance',help='FTP percentages for resistance mode, default=150/100',required=False, default=False) parser.add_argument('-s','--simulate', help='Simulated trainer to test ANT+ connectivity', required=False, action='store_true') #scs parser.add_argument('-S','--scs', help='Use this Speed Cadence Sensor (0: default device)', required=False, default=False) args = parser.parse_args() self.args = args #----------------------------------------------------------------------- # Booleans; either True or False #----------------------------------------------------------------------- self.autostart = args.autostart self.gui = args.gui self.manual = args.manual self.manualGrade = args.manualGrade self.calibrate = args.calibrate self.PowerMode = args.PowerMode self.SimulateTrainer = args.simulate if self.manual and self.manualGrade: logfile.Console("-m and -M are mutually exclusive; manual power selected") self.manualGrade = False # Mutually exclusive if (self.manual or self.manualGrade) and self.SimulateTrainer: logfile.Console("-m/-M and -s both specified, most likely for program test purpose") #----------------------------------------------------------------------- # Bicycle definition to be parsed; three parameters # format=tyre,chainring,cassette, e.g. "2.096,50/34,15/25" #----------------------------------------------------------------------- if args.bicycle: b = args.bicycle.split(",") #------------------------------------------------------------------- # parameter1: Tyre defined? #------------------------------------------------------------------- if len(b) >= 1: try: self.tyre = float(b[0]) if self.tyre < 1: self.tyre = self.__tyre__ except: logfile.Console('Command line error; -b incorrect tyre=%s' % b[0]) self.tyre = self.__tyre__ #------------------------------------------------------------------- # parameter2: Chainring, large/small separated by / # format=large/small, e.g. 50/34 # If one value is specified, e.g. "50" 50/50 is assumed #------------------------------------------------------------------- if len(b) >= 2: s = b[1].split('/') if len(s) >= 0: try: self.fL = int(s[0]) except: logfile.Console('Command line error; -b incorrect large chainring=%s' % s[0]) self.fS = self.fL # Default is single chainring if len(s) >= 1: try: self.fS = int(s[1]) except: logfile.Console('Command line error; -b incorrect small chainring=%s' % s[1]) #------------------------------------------------------------------- # parameter3: Cassette, small/large separated by / # If one value is specified, e.g. "15" 15/15 is assumed #------------------------------------------------------------------- if len(b) >= 3: s = b[2].split('/') if len(s) >= 0: try: self.rS = int(s[0]) except: logfile.Console('Command line error; -b incorrect small cassette=%s' % s[0]) self.rL = self.rS # Default is single speed cassette if len(s) >= 1: try: self.rL = int(s[1]) except: logfile.Console('Command line error; -b incorrect large cassette=%s' % s[1]) #----------------------------------------------------------------------- # Get debug-flags, used in debug module #----------------------------------------------------------------------- if args.debug: try: self.debug = int(args.debug) except: logfile.Console('Command line error; -d incorrect debugging flags=%s' % args.debug) #----------------------------------------------------------------------- # Get riders FTP #----------------------------------------------------------------------- if args.ftp: try: self.ftp = int(args.ftp) if self.ftp < 50: self.ftp = self.__ftp__ except: logfile.Console('Command line error; -f incorrect ftp=%s' % args.ftp) #----------------------------------------------------------------------- # Get HRM # - None: read HRM from Tacx Fortius and broadcast as HRM master device # - -1 : no master and no slave device # - 0 : pair with the first ANT+ HRM that is found # - next: pair with the defined ANT+ HRM monitor # the number can be found with ExplorANT #----------------------------------------------------------------------- if args.hrm: try: self.hrm = int(args.hrm) except: logfile.Console('Command line error; -H incorrect HRM=%s' % args.hrm) #----------------------------------------------------------------------- # Get SCS # - None: No Speed Cadence Sensor # - 0 : pair with the first ANT+ SCS that is found # - next: pair with the defined ANT+ SCS # the number can be found with ExplorANT #----------------------------------------------------------------------- #scs if args.scs: #scs try: #scs self.scs = int(args.scs) #scs except: #scs logfile.Console('Command line error; -S incorrect SCS=%s' % args.scs) #----------------------------------------------------------------------- # Get powerfactor #----------------------------------------------------------------------- if args.factor: try: self.PowerFactor = float(args.factor) except: logfile.Console('Command line error; -f incorrect power factor=%s' % args.factor) #----------------------------------------------------------------------- # Parse Resistance #----------------------------------------------------------------------- if args.resistance: s = args.resistance.split('/') if len(s) >= 0: try: self.ResistanceH = int(s[0]) except: logfile.Console('Command line error; -r incorrect high resistance=%s' % s[0]) if len(s) >= 1: try: self.ResistanceL = int(s[1]) except: logfile.Console('Command line error; -r incorrect low resistance=%s' % s[1])