Components/Hosted/include/Hosted/Client.h
Go to the documentation of this file.
1 /****
2  * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development.
3  * Created 2015 by Skurydin Alexey
4  * http://github.com/SmingHub/Sming
5  * All files of the Sming Core are provided under the LGPL v3 license.
6  *
7  * Client.h
8  *
9  * @author 2021 Slavey Karadzhov <slav@attachix.com>
10  *
11  *
12  ****/
13 
14 #pragma once
15 
16 #ifndef ARCH_HOST
17 #error "Hosted::Client can be used only on the Host architecture!"
18 #endif
19 
20 #include <Stream.h>
21 #include <WHashMap.h>
22 #include <WString.h>
23 #include <simpleRPC.h>
24 #include <simpleRPC/parser.h>
25 #include <hostlib/emu.h>
26 #include <hostlib/hostmsg.h>
27 #include "Util.h"
28 
29 namespace Hosted
30 {
31 constexpr int COMMAND_NOT_FOUND = -1;
32 
33 class Client : private simpleRPC::ParserCallbacks
34 {
35 public:
37 
38  Client(Stream& stream, char methodEndsWith = ':') : stream(stream), methodEndsWith(methodEndsWith)
39  {
40  }
41 
58  template <typename... Args> bool send(const String& functionName, Args... args)
59  {
60  int functionId = getFunctionId(functionName);
61  if(functionId == COMMAND_NOT_FOUND) {
62  return false;
63  }
64 
65  simpleRPC::rpcPrint(stream, uint8_t(functionId), args...);
66  stream.flush();
67 
68  return true;
69  }
70 
75  template <typename R> R wait()
76  {
77  size_t neededBytes = sizeof(R);
78 
79  while(stream.available() < int(neededBytes)) {
80  stream.flush();
82  }
83 
84  R result{};
85  stream.readBytes(reinterpret_cast<char*>(&result), sizeof(result));
86  return result;
87  }
88 
95  {
96  if(fetchCommands) {
98  }
99 
100  if(name.indexOf('(') != -1 && name.indexOf(')') != -1) {
101  // most probably we have a name with a signature
102  name = convertFQN(name);
103  }
104 
105  int id = commands.indexOf(name);
106  if(id < 0) {
107  return COMMAND_NOT_FOUND;
108  }
109 
110  return commands[name];
111  }
112 
118  {
119  host_debug_i("Getting remote RPC commands \033[5m...\033[0m");
120 
121  using namespace simpleRPC;
122 
123  uint8_t head = 0xff;
124  stream.write(&head, 1);
125  char buffer[512];
126  ParserSettings settings{*this};
127 
128  do {
129  stream.flush();
130  host_main_loop();
131 
132  size_t length = stream.readBytes(buffer, 512);
133  if(!length) {
134  continue;
135  }
136 
137  ParserResult result = parse(settings, buffer, length, methodEndsWith);
138  if(result == ParserResult::finished) {
139  break;
140  }
141 
142  if(result == ParserResult::error) {
143  host_debug_e("Invalid header");
144  return false;
145  }
146  } while(true);
147 
148  host_debug_i("Available commands:");
149 
150  for(auto cmd : commands) {
151  host_debug_i("\t%s => %d", cmd.key().c_str(), cmd.value());
152  }
153 
154  host_debug_i("Connected. Starting application");
155 
156  return true;
157  }
158 
159 private:
160  Stream& stream;
161  bool fetchCommands{true};
162  RemoteCommands commands;
163  uint8_t methodPosition = 0;
164  String name;
165  String signature;
166  char methodEndsWith;
167 
168  void startMethods() override
169  {
170  methodPosition = 0;
171  commands.clear();
172  }
173 
174  void startMethod() override
175  {
176  name = "";
177  signature = "";
178  }
179 
180  void methodSignature(char ch) override
181  {
182  signature += ch;
183  }
184 
185  void methodName(char ch) override
186  {
187  name += ch;
188  }
189 
190  void endMethod() override
191  {
192  if(!commands.contains(name) || signature == ":") {
193  commands[name] = methodPosition;
194  }
195  commands[name + "(" + signature + ")"] = methodPosition++;
196  }
197 
198  void endMethods() override
199  {
200  fetchCommands = false;
201  }
202 };
203 
204 } // namespace Hosted
int indexOf(const K &key) const
Definition: WHashMap.h:320
Definition: Components/Hosted/include/Hosted/Client.h:34
Client(Stream &stream, char methodEndsWith=':')
Definition: Components/Hosted/include/Hosted/Client.h:38
HashMap< String, uint8_t > RemoteCommands
Definition: Components/Hosted/include/Hosted/Client.h:36
bool getRemoteCommands()
Gets list of remote command names and their ids.
Definition: Components/Hosted/include/Hosted/Client.h:117
bool send(const String &functionName, Args... args)
Method to send commands to the remote server.
Definition: Components/Hosted/include/Hosted/Client.h:58
int getFunctionId(String name)
Fetches a list of commands supported on the RPC server and gives back the id of the desired command.
Definition: Components/Hosted/include/Hosted/Client.h:94
R wait()
This method will block the execution until a message is detected.
Definition: Components/Hosted/include/Hosted/Client.h:75
Base Stream class.
Definition: Wiring/Stream.h:33
virtual int available()=0
virtual void flush()=0
virtual size_t readBytes(char *buffer, size_t length)
Read chars from stream into buffer.
The String class.
Definition: WString.h:137
int host_main_loop()
Executing this function will run once the main emulator loop.
#define host_debug_i(fmt,...)
Definition: hostmsg.h:51
#define host_debug_e(fmt,...)
Definition: hostmsg.h:49
Definition: Components/Hosted/include/Hosted/Client.h:30
constexpr int COMMAND_NOT_FOUND
Definition: Components/Hosted/include/Hosted/Client.h:31
String convertFQN(const String &name)