1 //
2 // Copyright (c) 2010-2024 Antmicro
3 //
4 // This file is licensed under the MIT License.
5 // Full license text is available in 'licenses/MIT.txt'.
6 //
7 using System;
8 using System.IO;
9 using System.Collections.Generic;
10 using Antmicro.Renode.Core;
11 using Microsoft.Scripting.Hosting;
12 using Antmicro.Renode.Logging;
13 using Antmicro.Renode.Time;
14 using Antmicro.Renode.Exceptions;
15 using Antmicro.Renode.Utilities;
16 
17 namespace Antmicro.Renode.UserInterface
18 {
19     public static class MonitorExecutorExtensions
20     {
ExecutePython(this IMachine machine, string script)21         public static void ExecutePython(this IMachine machine, string script)
22         {
23             var engine = new ExecutorPythonEngine(machine, script);
24             engine.Action();
25         }
26 
ExecutePythonFromFile(this IMachine machine, ReadFilePath filePath)27         public static void ExecutePythonFromFile(this IMachine machine, ReadFilePath filePath)
28         {
29             machine.ExecutePython(ReadScriptFromFile(filePath));
30         }
31 
ExecutePythonEvery(this IMachine machine, string name, int milliseconds, string script)32         public static void ExecutePythonEvery(this IMachine machine, string name, int milliseconds, string script)
33         {
34             var engine = new ExecutorPythonEngine(machine, script);
35             var clockEntry = new ClockEntry((ulong)milliseconds, 1000, engine.Action, machine, name);
36             machine.ClockSource.AddClockEntry(clockEntry);
37 
38             events.Add(machine, name, engine.Action);
39             machine.StateChanged += (m, s) => UnregisterEvent(m, name, s);
40         }
41 
ExecutePythonEveryFromFile(this IMachine machine, string name, int milliseconds, ReadFilePath filePath)42         public static void ExecutePythonEveryFromFile(this IMachine machine, string name, int milliseconds, ReadFilePath filePath)
43         {
44             machine.ExecutePythonEvery(name, milliseconds, ReadScriptFromFile(filePath));
45         }
46 
StopPythonExecution(this IMachine machine, string name)47         public static void StopPythonExecution(this IMachine machine, string name)
48         {
49             machine.ClockSource.TryRemoveClockEntry(events.WithdrawAction(machine, name));
50             events.Remove(machine, name);
51         }
52 
UnregisterEvent(IMachine machine, String name, MachineStateChangedEventArgs state)53         private static void UnregisterEvent(IMachine machine, String name, MachineStateChangedEventArgs state)
54         {
55             if(state.CurrentState == MachineStateChangedEventArgs.State.Disposed)
56             {
57                 events.Remove(machine, name);
58             }
59         }
60 
ReadScriptFromFile(ReadFilePath filePath)61         private static string ReadScriptFromFile(ReadFilePath filePath)
62         {
63             try
64             {
65                 return File.ReadAllText(filePath);
66             }
67             catch(Exception e)
68             {
69                 throw new RecoverableException($"Error while opening Python script file '{filePath}': {e.Message}");
70             }
71         }
72 
73         private static readonly PeriodicEventsRegister events = new PeriodicEventsRegister();
74 
75         private sealed class ExecutorPythonEngine : PythonEngine
76         {
ExecutorPythonEngine(IMachine machine, string script)77             public ExecutorPythonEngine(IMachine machine, string script)
78             {
79                 Scope.SetVariable(Machine.MachineKeyword, machine);
80                 Scope.SetVariable("self", machine);
81 
82                 var source = Engine.CreateScriptSourceFromString(script);
83                 code = Compile(source);
84                 Action = () => Execute(code);
85             }
86 
87             public Action Action { get; private set; }
88 
89             private CompiledCode code;
90         }
91 
92         private sealed class PeriodicEventsRegister
93         {
Add(IMachine machine, string name, Action action)94             public void Add(IMachine machine, string name, Action action)
95             {
96                 lock(periodicEvents)
97                 {
98                     if(HasEvent(machine, name))
99                     {
100                         throw new RecoverableException("Periodic event '{0}' already registered in this machine.".FormatWith(name));
101                     }
102                     periodicEvents.Add(Tuple.Create(machine, name), action);
103                 }
104             }
105 
WithdrawAction(IMachine machine, string name)106             public Action WithdrawAction(IMachine machine, string name)
107             {
108                 lock(periodicEvents)
109                 {
110                     var action = periodicEvents[Tuple.Create(machine, name)];
111                     Remove(machine, name);
112                     return action;
113                 }
114             }
115 
Remove(IMachine machine, String name)116             public void Remove(IMachine machine, String name)
117             {
118                 lock(periodicEvents)
119                 {
120                     periodicEvents.Remove(Tuple.Create(machine, name));
121                 }
122             }
123 
HasEvent(IMachine machine, String name)124             public bool HasEvent(IMachine machine, String name)
125             {
126                 lock(periodicEvents)
127                 {
128                     return periodicEvents.ContainsKey(Tuple.Create(machine, name));
129                 }
130             }
131 
132             private static readonly Dictionary<Tuple<IMachine, string>, Action> periodicEvents = new Dictionary<Tuple<IMachine, string>, Action>();
133         }
134     }
135 }
136