static int Main(string[] args) { /* install global Exception handler * Won't do much more than pretify'ing the error messages but better * fail gracefully that terribly */ System.AppDomain.CurrentDomain.UnhandledException += UnhandledExceptionTrapper; bool errors_encountered = false; CommandLineParser cmd_parser = new CommandLineParser(); if (cmd_parser.parse_command_line(args) == false) { return(RETURN_INVALID_CMDLINE); } /****** SEED EROS Bdrige mode enable/disable requests */ if (cmd_parser.bootloader_operations.Contains(CommandLineParser.en_bootloader_operations.SEEDEROS_BRIDGEENABLE)) { return(EROS_EnableBridgeMode(ref cmd_parser)); } if (cmd_parser.bootloader_operations.Contains(CommandLineParser.en_bootloader_operations.SEEDEROS_BRIDGEDISABLE)) { return(EROS_DisableBridgeMode(ref cmd_parser)); } /****** Emergency Erase Request * Emergency Erase restores bootloader access in case of lost password, by wiping * the full FLASH and EEPROM memory areas */ if (cmd_parser.bootloader_operations.Contains(CommandLineParser.en_bootloader_operations.EMERGENCY_ERASE)) { enEmergencyEraseConfirmResult eresult = ConfirmEmergencyErase(ref cmd_parser); if (eresult == enEmergencyEraseConfirmResult.ER_CONFIRMED) { TSBInterfacing tsb = new TSBInterfacing(UpdateStatusOnScreen, AskQuestion_ToUser); if (!tsb.EmergencyErase(cmd_parser.port_name, cmd_parser.baudrate_bps, cmd_parser.prewait_ms, cmd_parser.replytimeout_ms)) { Console.WriteLine(); Console.WriteLine("> Error while performing Emergency Erase operation."); return(RETURN_ERRORS_ENCOUNTERED); } else { return(0); } } else if (eresult == enEmergencyEraseConfirmResult.ER_ERROR) { return(RETURN_ERRORS_ENCOUNTERED); } else { return(0); } } /********** Main cycle for multi-device bootloader operations * We will cycle through the list of passwords supplied * (or use empty password if none supplied) * * On every iteration we will run the same operations but we will collect user * options on each iteration (bc user may want different options for each * device) */ if (cmd_parser.bootloader_passwords.Count() == 0) { /* add a bogus entry with an empty password. Assuming user intends to start the session * with no password. */ cmd_parser.bootloader_passwords.Add(""); } /* only advance to TSB if we have more commands lined up and requested at the command line */ if (cmd_parser.bootloader_operations.Count == 0) { Console.WriteLine(); Console.WriteLine("> No bootloader actions specified. Ending session"); return(0); } foreach (string pwd in cmd_parser.bootloader_passwords) { TSBInterfacing tsb = new TSBInterfacing(UpdateStatusOnScreen, AskQuestion_ToUser); bool activation_result = false; /* from TSB versions of 2020 onwards, we now have an operation timeout * which means the commands must get their complete input in a limited timespan * * This means that, for setting password, timeout and/or magic bytes * we should ask the user for them beforehand so that they'r ready to go * If we wait for the user input while the sesison is active, the session * will likely timeout. */ TSBInterfacing.str_tsb_session_data new_user_data = default(TSBInterfacing.str_tsb_session_data); new_user_data.magic_bytes = new byte[2]; // we need to explicit allocate space for this if (pwd.Length > 0) { if (cmd_parser.activation_mode == CommandLineParser.en_bootloader_activation_mode.COLD_BOOT) { Console.WriteLine(); Console.WriteLine("> Preparing to activate bootloader for device with password: {0}", pwd); CollectUserOptions_AndUpdateCachedSessionData(ref cmd_parser, ref new_user_data); activation_result = tsb.ActivateBootloaderFromColdStart(cmd_parser.port_name, cmd_parser.baudrate_bps, cmd_parser.prewait_ms, cmd_parser.replytimeout_ms, pwd, cmd_parser.verbose_output); } else { Console.WriteLine("ERROR: Bootloader passwords can only be specified when activating in 'cold' [boot] mode."); return(RETURN_ERRORS_ENCOUNTERED); } } else if (cmd_parser.activation_mode == CommandLineParser.en_bootloader_activation_mode.COLD_BOOT) { Console.WriteLine(); Console.WriteLine("> Preparing to activate bootloader (no device password specified)"); CollectUserOptions_AndUpdateCachedSessionData(ref cmd_parser, ref new_user_data); activation_result = tsb.ActivateBootloaderFromColdStart(cmd_parser.port_name, cmd_parser.baudrate_bps, cmd_parser.prewait_ms, cmd_parser.replytimeout_ms, pwd, cmd_parser.verbose_output); } else if (cmd_parser.activation_mode == CommandLineParser.en_bootloader_activation_mode.LIVE_VIA_DYNAMIXEL) { if (cmd_parser.dynid < 0 || cmd_parser.dynid > 253) { Console.WriteLine("ERROR: 'live' bootloader activation mode was selected but the '-dynid' parameter was not given or is invalid. Please set a valid '-dynid' for 'live' activation mode."); return(RETURN_ERRORS_ENCOUNTERED); } Console.WriteLine(); Console.WriteLine("> Preparing to activate bootloader 'live', on device with DynID {0}", cmd_parser.dynid); CollectUserOptions_AndUpdateCachedSessionData(ref cmd_parser, ref new_user_data); // baud after activation should not be hard coded as we may need to speak at higher bauds if going through the EROS board. // timeout for bootloader activation must exceed the time set in the servo firmware. activation_result = tsb.ActivateBootloaderFromDynamixel(cmd_parser.port_name, cmd_parser.baudrate_bps, 9600, (byte)cmd_parser.dynid, 3500, 6000, TSBInterfacing.en_dyn_protocol_version.DYNAMIXEL_1, cmd_parser.verbose_output); } if (activation_result == true) { /* Bootloader is active. Print all bootloader information */ PrintDeviceInfo(tsb); /* check if the user asked to tag the file names. * This MUST be done here for the cases where we use multiple * passwords; in that case, we have sessins starting at different times * (initiated here) and also different passwords */ string tag = string.Format("_{0:yyMMdd}_{0:HHmmss}", DateTime.Now); if (pwd.Length > 0) { tag += string.Format("_{0}", pwd); } string eep_filename = cmd_parser.eeprom_file_name; string flash_filename = cmd_parser.flash_file_name; if (cmd_parser.tag_eepromfilename_withdatetimepwd && !string.IsNullOrEmpty(cmd_parser.eeprom_file_name)) { eep_filename = AddTagToFilename(cmd_parser.eeprom_file_name, tag); } if (cmd_parser.tag_flashfilename_withdatetimepwd && !string.IsNullOrEmpty(cmd_parser.flash_file_name)) { flash_filename = AddTagToFilename(cmd_parser.flash_file_name, tag); } bool request_last_page_write = false; /* loop through the various operations requested */ foreach (CommandLineParser.en_bootloader_operations bootloader_op in cmd_parser.bootloader_operations) { switch (bootloader_op) { case CommandLineParser.en_bootloader_operations.DISPLAY_DEVICE_INFO: /* do nothing; when we activated TSB it already displayed the device info */ break; case CommandLineParser.en_bootloader_operations.EEP_ERASE: if (!tsb.EEProm_Erase()) { Console.WriteLine(); Console.WriteLine("> Error Erasing EEProm."); errors_encountered = true; } break; case CommandLineParser.en_bootloader_operations.EEP_WRITE: if (!tsb.EEProm_Write(eep_filename)) { Console.WriteLine(); Console.WriteLine("> Error Writing EEPROM."); errors_encountered = true; } break; case CommandLineParser.en_bootloader_operations.EEP_VERIFY: if (!tsb.EEProm_Verify(eep_filename)) { Console.WriteLine(); Console.WriteLine("> Error Verifying EEPROM."); errors_encountered = true; } break; case CommandLineParser.en_bootloader_operations.EEP_READ: if (!tsb.EEProm_Read(eep_filename)) { Console.WriteLine(); Console.WriteLine("> Error Reading EEPROM."); errors_encountered = true; } break; case CommandLineParser.en_bootloader_operations.FW_ERASE: if (!tsb.Flash_Erase()) { Console.WriteLine(); Console.WriteLine("> Error Erasing Flash."); errors_encountered = true; } break; case CommandLineParser.en_bootloader_operations.FW_WRITE: if (!tsb.Flash_Write(flash_filename)) { Console.WriteLine(); Console.WriteLine("> Error Writing Flash."); errors_encountered = true; } break; case CommandLineParser.en_bootloader_operations.FW_VERIFY: if (!tsb.Flash_Verify(flash_filename)) { Console.WriteLine(); Console.WriteLine("> Error Verifying Flash."); errors_encountered = true; } break; case CommandLineParser.en_bootloader_operations.FW_READ: if (!tsb.Flash_Read(flash_filename)) { Console.WriteLine(); Console.WriteLine("> Error Reading Flash."); errors_encountered = true; } break; case CommandLineParser.en_bootloader_operations.PASSWORD_CHANGE: tsb.session_data_.password = new_user_data.password; request_last_page_write = true; break; case CommandLineParser.en_bootloader_operations.TIMEOUT_CHANGE: tsb.session_data_.timeout = new_user_data.timeout; request_last_page_write = true; break; case CommandLineParser.en_bootloader_operations.WRITE_MAGIC_BYTES: tsb.session_data_.magic_bytes[0] = new_user_data.magic_bytes[0]; tsb.session_data_.magic_bytes[1] = new_user_data.magic_bytes[1]; request_last_page_write = true; break; case CommandLineParser.en_bootloader_operations.PATCH_DAISY_CHAIN_BUG: request_last_page_write = true; // a simple last page write, applies the patch break; default: Console.WriteLine("ERROR: Unknown Bootloader operation in function main()"); return(RETURN_UNKOWN_OPTION_IN_CASE); } } // check if there are updated settings in last page, and commit them if (request_last_page_write) { if (!tsb.LastPage_Write()) { Console.WriteLine(); Console.WriteLine("> Error updating bootloader configuration data."); Console.WriteLine("> (Daisy chain patch, Timeout, password and/or Magic bytes might not have been updated)"); errors_encountered = true; } request_last_page_write = false; } tsb.DeactivateBootloader(); } else { Console.WriteLine(); Console.WriteLine("> Could not activate bootloader for the selected device."); Console.WriteLine("> Hint: Is password correct? Is BAUD rate correct?"); tsb.DeactivateBootloader(); errors_encountered = true; } } if (errors_encountered) { Console.WriteLine(); Console.WriteLine("> Requested operations completed with errors. Please review the error messages above."); return(RETURN_ERRORS_ENCOUNTERED); } else { Console.WriteLine(); Console.WriteLine("> All operations complete."); return(0); } }
static void CollectUserOptions_AndUpdateCachedSessionData(ref CommandLineParser cmd_parser, ref TSBInterfacing.str_tsb_session_data new_user_data) { Console.WriteLine("> Collecting user options"); if (cmd_parser.bootloader_operations.Contains(CommandLineParser.en_bootloader_operations.PASSWORD_CHANGE)) { string new_pwd; while (true) { Console.WriteLine("Please enter the _new_ Password (max. {0} chars): ", TSBInterfacing.max_pwd_length); new_pwd = Console.ReadLine(); if (string.IsNullOrEmpty(new_pwd)) { new_pwd = ""; Console.WriteLine("No password specified. The bootloader will be accessible without password."); break; } else if (new_pwd.Length > Tsbloader_adv.TSBInterfacing.max_pwd_length) { Console.WriteLine("ERROR: Password is too long. Maximum password length supported by this tool is {0} characters.", Tsbloader_adv.TSBInterfacing.max_pwd_length); } else { Console.WriteLine("New password will be set to: {0}", new_pwd); Console.WriteLine("(if you lose your password, you may use the Emergency Erase option '-XXX')"); break; } } new_user_data.password = new_pwd; Console.WriteLine(); } if (cmd_parser.bootloader_operations.Contains(CommandLineParser.en_bootloader_operations.TIMEOUT_CHANGE)) { int new_timeout_setting; while (true) { Console.WriteLine("Please enter new Timeout setting: "); string new_timeout = Console.ReadLine(); if (string.IsNullOrEmpty(new_timeout)) { Console.WriteLine("Invalid timeout specified. Timeout must be number between {0} and 255.", MINIMUM_TIMEOUT_SETTING); } else { if (!int.TryParse(new_timeout, out new_timeout_setting)) { Console.WriteLine("Invalid timeout specified. Timeout must be a number, between {0} and 255.", MINIMUM_TIMEOUT_SETTING); } else if (new_timeout_setting < MINIMUM_TIMEOUT_SETTING || new_timeout_setting > 255) { Console.WriteLine("Invalid timeout value specified. Timeout must be between {0} and 255.", MINIMUM_TIMEOUT_SETTING); } else { new_user_data.timeout = (byte)new_timeout_setting; break; } } } Console.WriteLine(); } if (cmd_parser.bootloader_operations.Contains(CommandLineParser.en_bootloader_operations.WRITE_MAGIC_BYTES)) { /* ask the user for the new magic bytes */ string[] s_magic_bytes_names = { "First", "Second" }; //Console.WriteLine("Magic Bytes currently set to [0x{0:X02}] [0x{1:X02}]", tsb.session_data_.magic_bytes[0], tsb.session_data_.magic_bytes[1]); for (byte b = 0; b < 2; b++) { while (true) { Console.WriteLine("Enter the {0} Magic Byte, in HEX format (ie. for '0xFE', just enter 'FE'): 0x", s_magic_bytes_names[b]); string magic_byte = Console.ReadLine(); if (string.IsNullOrWhiteSpace(magic_byte)) { Console.WriteLine("Empty Magic Byte specified; will be set to 0xFF"); magic_byte = "FF"; } int mb; try { // based on tips for parsing HEX here https://theburningmonk.com/2010/02/converting-hex-to-int-in-csharp/ mb = int.Parse(magic_byte, System.Globalization.NumberStyles.HexNumber); if (mb > 255) { Console.WriteLine("ERROR: HEX number too big. Magic bytes are of BYTE type in range 0x0 to 0xFF."); } else { new_user_data.magic_bytes[b] = (byte)mb; break; } } catch (Exception ex) { Console.WriteLine("ERROR: Invalid HEX value. Magic bytes are set in HEX. Example: to set to 0xA7, type 'A7' and ommit the '0x' part."); } } Console.WriteLine(); } Console.WriteLine("Magic Bytes will be set to: [0x{0:X02}] [0x{1:X02}]", new_user_data.magic_bytes[0], new_user_data.magic_bytes[1]); Console.WriteLine(); } Console.WriteLine("> Proceeding to Activate Bootloader."); }