1 // 2 // Copyright (c) 2010-2025 Antmicro 3 // Copyright (c) 2011-2015 Realtime Embedded 4 // 5 // This file is licensed under the MIT License. 6 // Full license text is available in 'licenses/MIT.txt'. 7 // 8 using System; 9 using Antmicro.Renode.Logging; 10 using Antmicro.Renode.Logging.Backends; 11 using Antmicro.Renode.Core; 12 using Antmicro.Renode.UserInterface; 13 using Antmicro.Renode.UserInterface.Tokenizer; 14 using Antmicro.Renode.Backends.Terminals; 15 using AntShell; 16 using AntShell.Terminal; 17 using Antmicro.Renode.Exceptions; 18 using Antmicro.Renode.Utilities; 19 using Antmicro.Renode.Peripherals.UART; 20 using System.Linq; 21 using Antmicro.OptionsParser; 22 using System.IO; 23 using System.Diagnostics; 24 using Antmicro.Renode.Analyzers; 25 using Antmicro.Renode.Extensions.Analyzers.Video; 26 using Antmicro.Renode.Backends.Video; 27 28 namespace Antmicro.Renode.UI 29 { 30 public static class CommandLineInterface 31 { Run(Options options, Action<ObjectCreator.Context> beforeRun = null)32 public static void Run(Options options, Action<ObjectCreator.Context> beforeRun = null) 33 { 34 AppDomain.CurrentDomain.UnhandledException += (sender, e) => CrashHandler.HandleCrash((Exception)e.ExceptionObject); 35 36 if(options.KeepTemporaryFiles) 37 { 38 EmulationManager.DisableEmulationFilesCleanup = true; 39 } 40 41 if(!options.HideLog) 42 { 43 Logger.AddBackend(ConsoleBackend.Instance, "console"); 44 if(options.Plain) 45 { 46 //This is set in Program.cs already, but we leave it here in case CommandLineInterface is reused, 47 //to prevent hard to trace bugs 48 ConsoleBackend.Instance.PlainMode = true; 49 } 50 } 51 52 Logger.AddBackend(new MemoryBackend(), "memory"); 53 Emulator.ShowAnalyzers = !options.HideAnalyzers; 54 XwtProvider xwt = null; 55 if(options.PidFile != null) 56 { 57 var pid = Process.GetCurrentProcess().Id; 58 File.WriteAllText(options.PidFile, pid.ToString()); 59 } 60 61 if(!options.DisableXwt || options.RobotDebug) 62 { 63 xwt = XwtProvider.Create(new WindowedUserInterfaceProvider()); 64 } 65 66 if(xwt == null && options.RobotFrameworkRemoteServerPort == -1 && !options.Console) 67 { 68 if(options.Port == -1) 69 { 70 options.Port = 1234; 71 } 72 73 if(!options.DisableXwt) 74 { 75 Logger.Log(LogLevel.Warning, "Couldn't start UI - falling back to console mode"); 76 options.Console = true; 77 } 78 } 79 80 using(var context = ObjectCreator.Instance.OpenContext()) 81 { 82 var monitor = new Antmicro.Renode.UserInterface.Monitor(); 83 context.RegisterSurrogate(typeof(Antmicro.Renode.UserInterface.Monitor), monitor); 84 85 // we must initialize plugins AFTER registering monitor surrogate 86 // as some plugins might need it for construction 87 TypeManager.Instance.PluginManager.Init("CLI"); 88 89 EmulationManager.Instance.ProgressMonitor.Handler = new CLIProgressMonitor(); 90 91 var uartAnalyzerType = (xwt == null || options.RobotDebug) ? typeof(LoggingUartAnalyzer) : typeof(ConsoleWindowBackendAnalyzer); 92 var videoAnalyzerType = (xwt == null || options.RobotDebug) ? typeof(DummyVideoAnalyzer) : typeof(VideoAnalyzer); 93 94 EmulationManager.Instance.CurrentEmulation.BackendManager.SetPreferredAnalyzer(typeof(UARTBackend), uartAnalyzerType); 95 EmulationManager.Instance.CurrentEmulation.BackendManager.SetPreferredAnalyzer(typeof(VideoBackend), videoAnalyzerType); 96 EmulationManager.Instance.EmulationChanged += () => 97 { 98 EmulationManager.Instance.CurrentEmulation.BackendManager.SetPreferredAnalyzer(typeof(UARTBackend), uartAnalyzerType); 99 EmulationManager.Instance.CurrentEmulation.BackendManager.SetPreferredAnalyzer(typeof(VideoBackend), videoAnalyzerType); 100 }; 101 102 var shell = PrepareShell(options, monitor); 103 new System.Threading.Thread(x => shell.Start(true)) 104 { 105 IsBackground = true, 106 Name = "Shell thread" 107 }.Start(); 108 109 Emulator.BeforeExit += () => 110 { 111 Emulator.DisposeAll(); 112 xwt?.Dispose(); 113 xwt = null; 114 }; 115 116 if(beforeRun != null) 117 { 118 beforeRun(context); 119 } 120 121 if(options.RobotDebug) 122 { 123 ConsoleWindowBackendAnalyzer terminal = null; 124 125 Emulator.EnableGUI += () => 126 { 127 Logger.AddBackend(ConsoleBackend.Instance, "console", true); 128 terminal = new ConsoleWindowBackendAnalyzer(true); 129 terminal.Show(); 130 shell.Terminal = new NavigableTerminalEmulator(terminal.IO); 131 shell.Terminal.PlainMode = options.Plain; 132 133 new System.Threading.Thread(x => shell.Start(true)) 134 { 135 IsBackground = true, 136 Name = "Shell thread" 137 }.Start(); 138 }; 139 140 Emulator.DisableGUI += () => 141 { 142 if(options.HideLog) 143 { 144 Logger.RemoveBackend(ConsoleBackend.Instance); 145 } 146 terminal?.Hide(); 147 terminal = null; 148 }; 149 } 150 151 Emulator.WaitForExit(); 152 } 153 } 154 PrepareShell(Options options, Monitor monitor)155 private static Shell PrepareShell(Options options, Monitor monitor) 156 { 157 Shell shell = null; 158 if(options.Console) 159 { 160 var io = new IOProvider() 161 { 162 Backend = new ConsoleIOSource() 163 }; 164 shell = ShellProvider.GenerateShell(monitor, true); 165 shell.Terminal = new NavigableTerminalEmulator(io, true); 166 } 167 else if(options.Port >= 0) 168 { 169 var io = new IOProvider() 170 { 171 Backend = new SocketIOSource(options.Port) 172 }; 173 shell = ShellProvider.GenerateShell(monitor, true); 174 shell.Terminal = new NavigableTerminalEmulator(io, true); 175 176 Logger.Log(LogLevel.Info, "Monitor available in telnet mode on port {0}", options.Port); 177 } 178 else 179 { 180 ConsoleWindowBackendAnalyzer terminal = null; 181 IOProvider io; 182 if(options.HideMonitor) 183 { 184 io = new IOProvider { Backend = new DummyIOSource() }; 185 } 186 else 187 { 188 terminal = new ConsoleWindowBackendAnalyzer(true); 189 io = terminal.IO; 190 } 191 192 // forcing vcursor is necessary, because calibrating will never end if the window is not shown 193 shell = ShellProvider.GenerateShell(monitor, forceVCursor: options.HideMonitor); 194 shell.Terminal = new NavigableTerminalEmulator(io, options.HideMonitor); 195 196 if(terminal != null) 197 { 198 try 199 { 200 Emulator.BeforeExit += shell.Stop; 201 terminal.Quitted += Emulator.Exit; 202 terminal.Show(); 203 } 204 catch(InvalidOperationException ex) 205 { 206 Console.ForegroundColor = ConsoleColor.Red; 207 Console.Error.WriteLine(ex.Message); 208 Emulator.Exit(); 209 } 210 } 211 } 212 monitor.Quitted += shell.Stop; 213 shell.Quitted += Emulator.Exit; 214 215 monitor.Interaction = shell.Writer; 216 monitor.MachineChanged += emu => shell.SetPrompt(emu != null ? new Prompt(string.Format("({0}) ", emu), ConsoleColor.DarkYellow) : null); 217 218 if(!string.IsNullOrEmpty(options.FilePath)) 219 { 220 var filePath = string.Format("{0}{1}", 221 Uri.IsWellFormedUriString(options.FilePath, UriKind.Absolute) || Path.IsPathRooted(options.FilePath) ? "@" : "$CWD/", 222 options.FilePath); 223 String commandToInject; 224 switch(Path.GetExtension(filePath)) 225 { 226 case ".save": 227 case ".gz": 228 commandToInject = string.Format("Load {0}\n", filePath); 229 break; 230 default: 231 commandToInject = string.Format("i {0}\n", filePath); 232 break; 233 } 234 shell.Started += s => s.InjectInput(commandToInject); 235 } 236 if(options.Execute != null) 237 { 238 shell.Started += s => s.InjectInput(string.Format("{0}\n", string.Join("; ", options.Execute))); 239 } 240 241 shell.Terminal.PlainMode = options.Plain; 242 243 return shell; 244 } 245 } 246 } 247