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.Collections.Generic;
8 using System.Linq;
9 using System.Text;
10 using Antmicro.Renode.Debugging;
11 using Antmicro.Renode.Utilities;
12 
13 namespace Antmicro.Renode.Network.ExternalControl
14 {
15     public class Response
16     {
CommandFailed(Command command, string reason)17         public static Response CommandFailed(Command command, string reason)
18         {
19             return new Response(ReturnCode.CommandFailed, command, text: reason);
20         }
21 
FatalError(string reason)22         public static Response FatalError(string reason)
23         {
24             return new Response(ReturnCode.FatalError, text: reason);
25         }
26 
InvalidCommand(Command command)27         public static Response InvalidCommand(Command command)
28         {
29             return new Response(ReturnCode.InvalidCommand, command);
30         }
31 
Success(Command command)32         public static Response Success(Command command)
33         {
34             return new Response(ReturnCode.SuccessWithoutData, command);
35         }
36 
Success(Command command, IEnumerable<byte> data)37         public static Response Success(Command command, IEnumerable<byte> data)
38         {
39             return new Response(ReturnCode.SuccessWithData, command, data);
40         }
41 
Success(Command command, string text)42         public static Response Success(Command command, string text)
43         {
44             return new Response(ReturnCode.SuccessWithData, command, text);
45         }
46 
SuccessfulHandshake()47         public static Response SuccessfulHandshake()
48         {
49             return new Response(ReturnCode.SuccessfulHandshake);
50         }
51 
Event(Command command, int eventDescriptor, IEnumerable<byte> data)52         public static Response Event(Command command, int eventDescriptor, IEnumerable<byte> data)
53         {
54             return new Response(ReturnCode.AsyncEvent, command, eventDescriptor, data.ToArray());
55         }
56 
Response(ReturnCode returnCode)57         private Response(ReturnCode returnCode)
58             : this(returnCode, null, null, null)
59         {
60         }
61 
Response(ReturnCode returnCode, string text)62         private Response(ReturnCode returnCode, string text)
63             : this(returnCode, null, text)
64         {
65         }
66 
Response(ReturnCode returnCode, Command? command, string text)67         private Response(ReturnCode returnCode, Command? command, string text)
68             : this(returnCode, command, null, Encoding.ASCII.GetBytes(text))
69         {
70             dataIsText = true;
71         }
72 
Response(ReturnCode returnCode, Command? command, IEnumerable<byte> data)73         private Response(ReturnCode returnCode, Command? command, IEnumerable<byte> data)
74             : this(returnCode, command, null, data.ToArray())
75         {
76         }
77 
Response(ReturnCode returnCode, Command? command, int? eventDescriptor = null, byte[] data = null)78         private Response(ReturnCode returnCode, Command? command, int? eventDescriptor = null, byte[] data = null)
79         {
80             // Command can be null only for FatalError and SuccessfulHandshake
81             DebugHelper.Assert(command != null || returnCode == ReturnCode.FatalError || returnCode == ReturnCode.SuccessfulHandshake);
82             // eventDescriptor can be null for all return codes but AsyncEvent
83             DebugHelper.Assert(eventDescriptor != null || returnCode != ReturnCode.AsyncEvent);
84 
85             this.returnCode = returnCode;
86             this.command = command;
87             this.eventDescriptor = eventDescriptor;
88             this.data = data;
89             dataIsText = false;
90         }
91 
GetBytes()92         public IEnumerable<byte> GetBytes()
93         {
94             var response = new List<byte> { (byte)returnCode };
95 
96             if(command.HasValue)
97             {
98                 response.Add((byte)command);
99             }
100 
101             if(eventDescriptor.HasValue)
102             {
103                 response.AddRange(((int)eventDescriptor).AsRawBytes());
104             }
105 
106             if(data != null && data.Any())
107             {
108                 response.AddRange(checked((uint)data.Length).AsRawBytes());
109                 response.AddRange(data);
110             }
111 
112             return response;
113         }
114 
ToString()115         public override string ToString()
116         {
117             var result = new StringBuilder("Response(")
118                 .Append(returnCode);
119 
120             if(command.HasValue)
121             {
122                 result
123                     .Append(", command: ")
124                     .Append(command);
125             }
126 
127             if(eventDescriptor != null)
128             {
129                 result
130                     .Append(", eventDescriptor: ")
131                     .Append(eventDescriptor);
132             }
133 
134             if(data != null)
135             {
136                 result
137                     .Append(", data: ")
138                     .Append(dataIsText ? Encoding.ASCII.GetString(data) : Misc.PrettyPrintCollectionHex(data));
139             }
140 
141             return result
142                 .Append(')')
143                 .ToString();
144         }
145 
146         private readonly bool dataIsText;
147         private readonly Command? command;
148         private readonly int? eventDescriptor;
149         private readonly byte[] data;
150         private readonly ReturnCode returnCode;
151 
152         // matches the return_code_t enum in tools/external_control_client/lib/renode_api.c
153         private enum ReturnCode : byte
154         {
155             CommandFailed,
156             FatalError,
157             InvalidCommand,
158             SuccessWithData,
159             SuccessWithoutData,
160             SuccessfulHandshake,
161             AsyncEvent,
162         }
163     }
164 }
165