Mercurial > hg > piper-cpp
comparison utilities/vampipe-convert.cpp @ 24:533ca5ca3404
More on reading and writing json messages
author | Chris Cannam <c.cannam@qmul.ac.uk> |
---|---|
date | Mon, 23 May 2016 16:09:25 +0100 |
parents | d678cd00e593 |
children | 5b9690d18241 |
comparison
equal
deleted
inserted
replaced
23:d678cd00e593 | 24:533ca5ca3404 |
---|---|
8 | 8 |
9 using namespace std; | 9 using namespace std; |
10 using namespace json11; | 10 using namespace json11; |
11 using namespace vampipe; | 11 using namespace vampipe; |
12 | 12 |
13 // Accepting JSON objects with two fields, "type" and "payload". The | 13 // Accepting JSON objects with two fields, "type" and "content". The |
14 // "type" string corresponds to the JSON schema filename | 14 // "type" string corresponds to the JSON schema filename |
15 // (e.g. "outputdescriptor") and the "payload" is the JSON object | 15 // (e.g. "outputdescriptor") and the "content" is the JSON object |
16 // encoded with that schema. | 16 // encoded with that schema. |
17 | |
18 Json | |
19 json_input(string input) | |
20 { | |
21 string err; | |
22 Json j = Json::parse(input, err); | |
23 if (err != "") { | |
24 throw VampJson::Failure("invalid json: " + err); | |
25 } | |
26 if (!j.is_object()) { | |
27 throw VampJson::Failure("object expected at top level"); | |
28 } | |
29 if (!j["type"].is_string()) { | |
30 throw VampJson::Failure("string expected for type field"); | |
31 } | |
32 if (!j["payload"].is_object()) { | |
33 throw VampJson::Failure("object expected for payload field"); | |
34 } | |
35 return j; | |
36 } | |
37 | 17 |
38 class PreservingPluginHandleMapper : public PluginHandleMapper | 18 class PreservingPluginHandleMapper : public PluginHandleMapper |
39 { | 19 { |
40 public: | 20 public: |
41 PreservingPluginHandleMapper() : m_handle(0), m_plugin(0) { } | 21 PreservingPluginHandleMapper() : m_handle(0), m_plugin(0) { } |
54 private: | 34 private: |
55 int32_t m_handle; | 35 int32_t m_handle; |
56 Vamp::Plugin *m_plugin; | 36 Vamp::Plugin *m_plugin; |
57 }; | 37 }; |
58 | 38 |
59 void | |
60 handle_input(::capnp::MallocMessageBuilder &message, string input) | |
61 { | |
62 string err; | |
63 | |
64 Json j = json_input(input); | |
65 string type = j["type"].string_value(); | |
66 Json payload = j["payload"]; | |
67 | |
68 if (type == "configurationrequest") { | |
69 auto req = message.initRoot<ConfigurationRequest>(); | |
70 PreservingPluginHandleMapper mapper; | |
71 VampnProto::buildConfigurationRequest | |
72 (req, VampJson::toConfigurationRequest(payload, mapper), mapper); | |
73 | |
74 } else if (type == "configurationresponse") { | |
75 auto resp = message.initRoot<ConfigurationResponse>(); | |
76 VampnProto::buildConfigurationResponse | |
77 (resp, VampJson::toConfigurationResponse(payload)); | |
78 | |
79 } else if (type == "feature") { | |
80 auto f = message.initRoot<Feature>(); | |
81 VampnProto::buildFeature | |
82 (f, VampJson::toFeature(payload)); | |
83 | |
84 } else if (type == "featureset") { | |
85 auto fs = message.initRoot<FeatureSet>(); | |
86 VampnProto::buildFeatureSet | |
87 (fs, VampJson::toFeatureSet(payload)); | |
88 | |
89 } else if (type == "loadrequest") { | |
90 auto req = message.initRoot<LoadRequest>(); | |
91 VampnProto::buildLoadRequest | |
92 (req, VampJson::toLoadRequest(payload)); | |
93 | |
94 } else if (type == "loadresponse") { | |
95 auto resp = message.initRoot<LoadResponse>(); | |
96 PreservingPluginHandleMapper mapper; | |
97 VampnProto::buildLoadResponse | |
98 (resp, VampJson::toLoadResponse(payload, mapper), mapper); | |
99 | |
100 } else if (type == "outputdescriptor") { | |
101 auto od = message.initRoot<OutputDescriptor>(); | |
102 VampnProto::buildOutputDescriptor | |
103 (od, VampJson::toOutputDescriptor(payload)); | |
104 | |
105 } else if (type == "parameterdescriptor") { | |
106 auto pd = message.initRoot<ParameterDescriptor>(); | |
107 VampnProto::buildParameterDescriptor | |
108 (pd, VampJson::toParameterDescriptor(payload)); | |
109 | |
110 } else if (type == "pluginconfiguration") { | |
111 auto pc = message.initRoot<PluginConfiguration>(); | |
112 auto config = VampJson::toPluginConfiguration(payload); | |
113 VampnProto::buildPluginConfiguration(pc, config); | |
114 | |
115 } else if (type == "pluginstaticdata") { | |
116 auto pc = message.initRoot<PluginStaticData>(); | |
117 auto sd = VampJson::toPluginStaticData(payload); | |
118 VampnProto::buildPluginStaticData(pc, sd); | |
119 | |
120 } else if (type == "processrequest") { | |
121 auto p = message.initRoot<ProcessRequest>(); | |
122 PreservingPluginHandleMapper mapper; | |
123 VampnProto::buildProcessRequest | |
124 (p, VampJson::toProcessRequest(payload, mapper), mapper); | |
125 | |
126 } else if (type == "realtime") { | |
127 auto b = message.initRoot<RealTime>(); | |
128 VampnProto::buildRealTime | |
129 (b, VampJson::toRealTime(payload)); | |
130 | |
131 } else { | |
132 throw VampJson::Failure("unknown or unsupported JSON schema type " + | |
133 type); | |
134 } | |
135 } | |
136 | |
137 void usage() | 39 void usage() |
138 { | 40 { |
139 string myname = "vampipe-convert"; | 41 string myname = "vampipe-convert"; |
140 cerr << "\n" << myname << | 42 cerr << "\n" << myname << |
141 ": Convert Vamp request and response messages between formats\n\n" | 43 ": Validate and convert Vamp request and response messages\n\n" |
142 " Usage: " << myname << " -i <informat> -o <outformat>\n\n" | 44 " Usage: " << myname << " [-i <informat>] [-o <outformat>] request\n" |
143 "Where <informat> and <outformat> may be \"json\" or \"capnp\".\n" | 45 " " << myname << " [-i <informat>] [-o <outformat>] response\n\n" |
144 "Messages are read from stdin and written to stdout.\n" << endl; | 46 " where\n" |
47 " <informat>: the format to read from stdin\n" | |
48 " (\"json\" or \"capnp\", default is \"json\")\n" | |
49 " <outformat>: the format to convert to and write to stdout\n" | |
50 " (\"json\" or \"capnp\", default is \"json\")\n" | |
51 " request|response: whether to expect Vamp request or response messages\n\n" | |
52 "If <informat> and <outformat> differ, convert from <informat> to <outformat>.\n" | |
53 "If <informat> and <outformat> are the same, just check validity of incoming\n" | |
54 "messages and pass them to output.\n\n"; | |
55 | |
145 exit(2); | 56 exit(2); |
146 } | 57 } |
147 | 58 |
148 class RequestOrResponse | 59 class RequestOrResponse |
149 { | 60 { |
150 public: | 61 public: |
62 enum Direction { | |
63 Request, Response | |
64 }; | |
151 enum Type { | 65 enum Type { |
152 List, Load, Configure, Process, Finish, Eof | 66 List, Load, Configure, Process, Finish, Eof |
153 }; | 67 }; |
68 | |
69 static Type typeForName(string name) { | |
70 if (name == "list") return List; | |
71 else if (name == "load") return Load; | |
72 else if (name == "configure") return Configure; | |
73 else if (name == "process") return Process; | |
74 else if (name == "finish") return Finish; | |
75 else if (name == "eof") return Eof; | |
76 else { | |
77 throw runtime_error("unknown or unexpected request/response type \"" + | |
78 name + "\""); | |
79 } | |
80 } | |
81 | |
154 RequestOrResponse() : // nothing by default | 82 RequestOrResponse() : // nothing by default |
83 direction(Request), | |
155 type(Eof), | 84 type(Eof), |
156 success(false), | 85 success(false), |
157 finishPlugin(0) { } | 86 finishPlugin(0) { } |
158 | 87 |
88 Direction direction; | |
159 Type type; | 89 Type type; |
160 bool success; | 90 bool success; |
161 string errorText; | 91 string errorText; |
162 | 92 |
163 PreservingPluginHandleMapper mapper; | 93 PreservingPluginHandleMapper mapper; |
164 | 94 |
95 vector<Vamp::HostExt::PluginStaticData> listResponse; | |
165 Vamp::HostExt::LoadRequest loadRequest; | 96 Vamp::HostExt::LoadRequest loadRequest; |
166 Vamp::HostExt::LoadResponse loadResponse; | 97 Vamp::HostExt::LoadResponse loadResponse; |
167 Vamp::HostExt::ConfigurationRequest configurationRequest; | 98 Vamp::HostExt::ConfigurationRequest configurationRequest; |
168 Vamp::HostExt::ConfigurationResponse configurationResponse; | 99 Vamp::HostExt::ConfigurationResponse configurationResponse; |
169 Vamp::HostExt::ProcessRequest processRequest; | 100 Vamp::HostExt::ProcessRequest processRequest; |
170 Vamp::HostExt::ProcessResponse processResponse; | 101 Vamp::HostExt::ProcessResponse processResponse; |
171 Vamp::Plugin *finishPlugin; | 102 Vamp::Plugin *finishPlugin; |
172 Vamp::HostExt::ProcessResponse finishResponse; | 103 Vamp::HostExt::ProcessResponse finishResponse; |
173 | |
174 }; | 104 }; |
175 | 105 |
106 Json | |
107 convertRequestJson(string input) | |
108 { | |
109 string err; | |
110 Json j = Json::parse(input, err); | |
111 if (err != "") { | |
112 throw VampJson::Failure("invalid json: " + err); | |
113 } | |
114 if (!j.is_object()) { | |
115 throw VampJson::Failure("object expected at top level"); | |
116 } | |
117 if (!j["type"].is_string()) { | |
118 throw VampJson::Failure("string expected for type field"); | |
119 } | |
120 if (!j["content"].is_object()) { | |
121 throw VampJson::Failure("object expected for content field"); | |
122 } | |
123 return j; | |
124 } | |
125 | |
126 Json | |
127 convertResponseJson(string input) | |
128 { | |
129 string err; | |
130 Json j = Json::parse(input, err); | |
131 if (err != "") { | |
132 throw VampJson::Failure("invalid json: " + err); | |
133 } | |
134 if (!j.is_object()) { | |
135 throw VampJson::Failure("object expected at top level"); | |
136 } | |
137 if (!j["success"].is_bool()) { | |
138 throw VampJson::Failure("bool expected for success field"); | |
139 } | |
140 if (!j["content"].is_object()) { | |
141 throw VampJson::Failure("object expected for content field"); | |
142 } | |
143 return j; | |
144 } | |
145 | |
176 RequestOrResponse | 146 RequestOrResponse |
177 readInputJson() | 147 readRequestJson() |
178 { | 148 { |
179 RequestOrResponse rr; | 149 RequestOrResponse rr; |
150 rr.direction = RequestOrResponse::Request; | |
151 | |
180 string input; | 152 string input; |
181 if (!getline(cin, input)) { | 153 if (!getline(cin, input)) { |
182 rr.type = RequestOrResponse::Eof; | 154 rr.type = RequestOrResponse::Eof; |
183 return rr; | 155 return rr; |
184 } | 156 } |
185 | 157 |
186 Json j = json_input(input); | 158 Json j = convertRequestJson(input); |
187 string type = j["type"].string_value(); | 159 string type = j["type"].string_value(); |
188 | 160 rr.type = RequestOrResponse::typeForName(type); |
189 if (type == "list") { | 161 |
190 rr.type = RequestOrResponse::List; | 162 if (rr.type == RequestOrResponse::Load) { |
191 | 163 rr.loadRequest = VampJson::toVampRequest_Load(j); |
192 } else if (type == "load") { | 164 |
193 //!!! ah, we need a way to know whether we're dealing with a request or response here | 165 } else if (rr.type == RequestOrResponse::Configure) { |
194 rr.type = RequestOrResponse::Load; | 166 rr.configurationRequest = VampJson::toVampRequest_Configure(j, rr.mapper); |
195 rr.loadRequest = VampJson::toLoadRequest(j["content"]); | 167 |
196 | 168 } else if (rr.type == RequestOrResponse::Process) { |
197 } else if (type == "configure") { | 169 rr.processRequest = VampJson::toVampRequest_Process(j, rr.mapper); |
198 rr.type = RequestOrResponse::Configure; | 170 |
199 rr.configurationRequest = | 171 } else if (rr.type == RequestOrResponse::Finish) { |
200 VampJson::toConfigurationRequest(j["content"], rr.mapper); | 172 rr.finishPlugin = VampJson::toVampRequest_Finish(j, rr.mapper); |
201 | |
202 } else if (type == "process") { | |
203 rr.type = RequestOrResponse::Process; | |
204 rr.processRequest = | |
205 VampJson::toProcessRequest(j["content"], rr.mapper); | |
206 | |
207 } else if (type == "finish") { | |
208 rr.type = RequestOrResponse::Finish; | |
209 //!!! VampJsonify | |
210 rr.finishPlugin = rr.mapper.handleToPlugin(j["content"]["pluginHandle"].int_value()); | |
211 | |
212 } else { | |
213 throw runtime_error("unknown or unexpected request/response type \"" + | |
214 type + "\""); | |
215 } | 173 } |
216 | 174 |
217 return rr; | 175 return rr; |
218 } | 176 } |
219 | 177 |
178 void | |
179 writeRequestJson(RequestOrResponse &rr) | |
180 { | |
181 Json j; | |
182 | |
183 if (rr.type == RequestOrResponse::List) { | |
184 j = VampJson::fromVampRequest_List(); | |
185 | |
186 } else if (rr.type == RequestOrResponse::Load) { | |
187 j = VampJson::fromVampRequest_Load(rr.loadRequest); | |
188 | |
189 } else if (rr.type == RequestOrResponse::Configure) { | |
190 j = VampJson::fromVampRequest_Configure(rr.configurationRequest, rr.mapper); | |
191 | |
192 } else if (rr.type == RequestOrResponse::Process) { | |
193 j = VampJson::fromVampRequest_Process(rr.processRequest, rr.mapper); | |
194 | |
195 } else if (rr.type == RequestOrResponse::Finish) { | |
196 j = VampJson::fromVampRequest_Finish(rr.finishPlugin, rr.mapper); | |
197 } | |
198 | |
199 cout << j.dump() << endl; | |
200 } | |
201 | |
220 RequestOrResponse | 202 RequestOrResponse |
221 readInput(string format) | 203 readResponseJson() |
204 { | |
205 RequestOrResponse rr; | |
206 rr.direction = RequestOrResponse::Response; | |
207 | |
208 string input; | |
209 if (!getline(cin, input)) { | |
210 rr.type = RequestOrResponse::Eof; | |
211 return rr; | |
212 } | |
213 | |
214 Json j = convertResponseJson(input); | |
215 string type = j["type"].string_value(); | |
216 rr.type = RequestOrResponse::typeForName(type); | |
217 | |
218 if (rr.type == RequestOrResponse::List) { | |
219 rr.listResponse = VampJson::toVampResponse_List(j); | |
220 | |
221 } else if (rr.type == RequestOrResponse::Load) { | |
222 rr.loadResponse = VampJson::toVampResponse_Load(j, rr.mapper); | |
223 | |
224 } else if (rr.type == RequestOrResponse::Configure) { | |
225 rr.configurationResponse = VampJson::toVampResponse_Configure(j); | |
226 | |
227 } else if (rr.type == RequestOrResponse::Process) { | |
228 rr.processResponse = VampJson::toVampResponse_Process(j); | |
229 | |
230 } else if (rr.type == RequestOrResponse::Finish) { | |
231 rr.finishResponse = VampJson::toVampResponse_Finish(j); | |
232 } | |
233 | |
234 return rr; | |
235 } | |
236 | |
237 void | |
238 writeResponseJson(RequestOrResponse &rr) | |
239 { | |
240 Json j; | |
241 | |
242 if (rr.type == RequestOrResponse::List) { | |
243 j = VampJson::fromVampResponse_List("", rr.listResponse); | |
244 | |
245 } else if (rr.type == RequestOrResponse::Load) { | |
246 j = VampJson::fromVampResponse_Load(rr.loadResponse, rr.mapper); | |
247 } | |
248 | |
249 cout << j.dump() << endl; | |
250 } | |
251 | |
252 RequestOrResponse | |
253 readInput(string format, RequestOrResponse::Direction direction) | |
222 { | 254 { |
223 if (format == "json") { | 255 if (format == "json") { |
224 return readInputJson(); | 256 if (direction == RequestOrResponse::Request) { |
257 return readRequestJson(); | |
258 } else { | |
259 return readResponseJson(); | |
260 } | |
225 } else { | 261 } else { |
226 throw runtime_error("unknown or unimplemented format \"" + format + "\""); | 262 throw runtime_error("unknown or unimplemented format \"" + format + "\""); |
227 } | 263 } |
228 } | 264 } |
229 | 265 |
230 void | 266 void |
231 writeOutput(string format, const RequestOrResponse &rr) | 267 writeOutput(string format, RequestOrResponse &rr) |
232 { | 268 { |
233 throw runtime_error("writeOutput not implemented yet"); | 269 if (format == "json") { |
270 if (rr.direction == RequestOrResponse::Request) { | |
271 writeRequestJson(rr); | |
272 } else { | |
273 writeResponseJson(rr); | |
274 } | |
275 } else { | |
276 throw runtime_error("unknown or unimplemented format \"" + format + "\""); | |
277 } | |
278 } | |
279 | |
280 int main(int argc, char **argv) | |
281 { | |
282 if (argc < 2) { | |
283 usage(); | |
284 } | |
285 | |
286 string informat = "json", outformat = "json"; | |
287 RequestOrResponse::Direction direction; | |
288 bool haveDirection = false; | |
234 | 289 |
235 } | 290 for (int i = 1; i < argc; ++i) { |
236 | |
237 int main(int argc, char **argv) | |
238 { | |
239 if (argc != 5) { | |
240 usage(); | |
241 } | |
242 | |
243 string informat, outformat; | |
244 | |
245 for (int i = 1; i + 1 < argc; ++i) { | |
246 | 291 |
247 string arg = argv[i]; | 292 string arg = argv[i]; |
293 bool final = (i + 1 == argc); | |
248 | 294 |
249 if (arg == "-i") { | 295 if (arg == "-i") { |
250 if (informat != "") usage(); | 296 if (informat != "" || final) usage(); |
251 else informat = argv[++i]; | 297 else informat = argv[++i]; |
252 | 298 |
253 } else if (arg == "-o") { | 299 } else if (arg == "-o") { |
254 if (outformat != "") usage(); | 300 if (outformat != "" || final) usage(); |
255 else outformat = argv[++i]; | 301 else outformat = argv[++i]; |
256 | 302 |
303 } else if (arg == "request") { | |
304 direction = RequestOrResponse::Request; | |
305 haveDirection = true; | |
306 | |
307 } else if (arg == "response") { | |
308 direction = RequestOrResponse::Response; | |
309 haveDirection = true; | |
310 | |
257 } else { | 311 } else { |
258 usage(); | 312 usage(); |
259 } | 313 } |
260 } | 314 } |
261 | 315 |
262 if (informat == "" || outformat == "") { | 316 if (informat == "" || outformat == "" || !haveDirection) { |
263 usage(); | 317 usage(); |
264 } | 318 } |
265 | 319 |
266 while (true) { | 320 while (true) { |
267 | 321 |
268 try { | 322 try { |
269 | 323 |
270 RequestOrResponse rr = readInput(informat); | 324 RequestOrResponse rr = readInput(informat, direction); |
271 if (rr.type == RequestOrResponse::Eof) break; | 325 if (rr.type == RequestOrResponse::Eof) break; |
272 writeOutput(outformat, rr); | 326 writeOutput(outformat, rr); |
273 | 327 |
274 } catch (std::exception &e) { | 328 } catch (std::exception &e) { |
275 cerr << "Error: " << e.what() << endl; | 329 cerr << "Error: " << e.what() << endl; |