cec: added -f and --log-file parameters to cec-client
[deb_libcec.git] / src / testclient / main.cpp
CommitLineData
abbca718
LOK
1/*
2 * This file is part of the libCEC(R) library.
3 *
4 * libCEC(R) is Copyright (C) 2011 Pulse-Eight Limited. All rights reserved.
5 * libCEC(R) is an original work, containing original code.
6 *
7 * libCEC(R) is a trademark of Pulse-Eight Limited.
8 *
9 * This program is dual-licensed; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
22 *
23 *
24 * Alternatively, you can license this library under a commercial license,
25 * please contact Pulse-Eight Licensing for more information.
26 *
27 * For more information contact:
28 * Pulse-Eight Licensing <license@pulse-eight.com>
29 * http://www.pulse-eight.com/
30 * http://www.pulse-eight.net/
31 */
32
761dcc45 33#include <cec.h>
acec5f48 34
abbca718
LOK
35#include <cstdio>
36#include <fcntl.h>
37#include <iostream>
e6d2161b 38#include <fstream>
abbca718 39#include <string>
b9187cc6 40#include <sstream>
acec5f48
LOK
41#include "../lib/platform/threads.h"
42#include "../lib/util/StdString.h"
abbca718
LOK
43
44using namespace CEC;
45using namespace std;
46
8b7e5ff6 47#define CEC_TEST_CLIENT_VERSION 8
abbca718 48
761dcc45 49#include <cecloader.h>
bdd433cb 50int g_cecLogLevel = CEC_LOG_ALL;
e6d2161b 51ofstream g_logOutput;
b9187cc6 52
8bca69de 53inline bool HexStrToInt(const std::string& data, uint8_t& value)
b9187cc6 54{
8bca69de
LOK
55 int iTmp(0);
56 if (sscanf(data.c_str(), "%x", &iTmp) == 1)
57 {
58 if (iTmp > 256)
59 value = 255;
60 else if (iTmp < 0)
61 value = 0;
62 else
63 value = (uint8_t) iTmp;
b9187cc6 64
8bca69de
LOK
65 return true;
66 }
67
68 return false;
69}
b9187cc6
LOK
70
71//get the first word (separated by whitespace) from string data and place that in word
72//then remove that word from string data
73bool GetWord(string& data, string& word)
74{
75 stringstream datastream(data);
76 string end;
77
78 datastream >> word;
79 if (datastream.fail())
80 {
81 data.clear();
82 return false;
83 }
84
85 size_t pos = data.find(word) + word.length();
86
87 if (pos >= data.length())
88 {
89 data.clear();
90 return true;
91 }
92
93 data = data.substr(pos);
94
95 datastream.clear();
96 datastream.str(data);
97
98 datastream >> end;
99 if (datastream.fail())
100 data.clear();
101
102 return true;
103}
104
2abe74eb 105void flush_log(ICECAdapter *cecParser)
abbca718
LOK
106{
107 cec_log_message message;
108 while (cecParser && cecParser->GetNextLogMessage(&message))
109 {
bdd433cb 110 if ((message.level & g_cecLogLevel) == message.level)
abbca718 111 {
e6d2161b 112 CStdString strLevel;
bdd433cb
LOK
113 switch (message.level)
114 {
115 case CEC_LOG_ERROR:
e6d2161b 116 strLevel = "ERROR: ";
bdd433cb
LOK
117 break;
118 case CEC_LOG_WARNING:
e6d2161b 119 strLevel = "WARNING: ";
bdd433cb
LOK
120 break;
121 case CEC_LOG_NOTICE:
e6d2161b 122 strLevel = "NOTICE: ";
bdd433cb
LOK
123 break;
124 case CEC_LOG_TRAFFIC:
e6d2161b 125 strLevel = "TRAFFIC: ";
bdd433cb
LOK
126 break;
127 case CEC_LOG_DEBUG:
e6d2161b 128 strLevel = "DEBUG: ";
bdd433cb
LOK
129 break;
130 default:
131 break;
132 }
40339852 133
bdd433cb
LOK
134 CStdString strMessageTmp;
135 strMessageTmp.Format("[%16lld]\t%s", message.time, message.message);
e6d2161b
LOK
136 cout << strLevel.c_str() << strMessageTmp.c_str() << endl;
137
138 if (g_logOutput.is_open())
139 g_logOutput << strLevel.c_str() << strMessageTmp.c_str() << endl;
bdd433cb 140 }
abbca718
LOK
141 }
142}
143
2abe74eb 144void list_devices(ICECAdapter *parser)
abbca718 145{
25701fa6
LOK
146 cec_adapter *devices = new cec_adapter[10];
147 uint8_t iDevicesFound = parser->FindAdapters(devices, 10, NULL);
abbca718
LOK
148 if (iDevicesFound <= 0)
149 {
25701fa6 150 cout << "Found devices: NONE" << endl;
abbca718
LOK
151 }
152 else
153 {
25701fa6
LOK
154 CStdString strLog;
155 strLog.Format("Found devices: %d", iDevicesFound);
156 cout << strLog.c_str() << endl;
157 for (unsigned int iDevicePtr = 0; iDevicePtr < iDevicesFound; iDevicePtr++)
abbca718
LOK
158 {
159 CStdString strDevice;
25701fa6 160 strDevice.Format("device: %d\npath: %s\ncom port: %s", iDevicePtr + 1, devices[iDevicePtr].path, devices[iDevicePtr].comm);
abbca718
LOK
161 cout << endl << strDevice.c_str() << endl;
162 }
163 }
164}
165
166void show_help(const char* strExec)
167{
168 cout << endl <<
169 strExec << " {-h|--help|-l|--list-devices|[COM PORT]}" << endl <<
170 endl <<
171 "parameters:" << endl <<
172 "\t-h --help Shows this help text" << endl <<
173 "\t-l --list-devices List all devices on this system" << endl <<
e6d2161b 174 "\t-f --log-file {file} Writes all libCEC log message to a file" << endl <<
825ddb96
LOK
175 "\t[COM PORT] The com port to connect to. If no COM port is given, the client tries to connect to the first device that is detected" << endl <<
176 endl <<
177 "Type 'h' or 'help' and press enter after starting the client to display all available commands" << endl;
178}
179
180void show_console_help(void)
181{
182 cout << endl <<
183 "================================================================================" << endl <<
184 "Available commands:" << endl <<
185 endl <<
186 "tx {bytes} transfer bytes over the CEC line." << endl <<
faa6a23b 187 "txn {bytes} transfer bytes and don't wait for an ACK reply." << endl <<
825ddb96
LOK
188 "[tx 40 00 FF 11 22 33] sends bytes 0x40 0x00 0xFF 0x11 0x22 0x33" << endl <<
189 endl <<
88f45c9b
LOK
190 "on {address} power on the device with the given logical address." << endl <<
191 "[on 5] power on a connected audio system" << endl <<
192 endl <<
193 "standby {address} put the device with the given address in standby mode." << endl <<
194 "[standby 0] powers off the TV" << endl <<
195 endl <<
825ddb96
LOK
196 "la {logical_address} change the logical address of the CEC adapter." << endl <<
197 "[la 4] logical address 4" << endl <<
198 endl <<
a424ebac
LOK
199 "pa {physical_address} change the physical address of the CEC adapter." << endl <<
200 "[pa 10 00] physical address 1.0.0.0" << endl <<
201 endl <<
1969b140
LOK
202 "osd {addr} {string} set OSD message on the specified device." << endl <<
203 "[osd 0 Test Message] displays 'Test Message' on the TV" << endl <<
204 endl <<
8b7e5ff6 205 "[mon] {1|0} enable or disable CEC bus monitoring." << endl <<
bdd433cb 206 "[log] {1 - 31} change the log level. see cectypes.h for values." << endl <<
825ddb96 207 "[ping] send a ping command to the CEC adapter." << endl <<
88f45c9b
LOK
208 "[bl] to let the adapter enter the bootloader, to upgrade" << endl <<
209 " the flash rom." << endl <<
210 "[r] reconnect to the CEC adapter." << endl <<
825ddb96 211 "[h] or [help] show this help." << endl <<
88f45c9b
LOK
212 "[q] or [quit] to quit the CEC test client and switch off all" << endl <<
213 " connected CEC devices." << endl <<
825ddb96 214 "================================================================================" << endl;
abbca718
LOK
215}
216
217int main (int argc, char *argv[])
218{
d54c6c91 219 ICECAdapter *parser = LoadLibCec("CECTester");
acec5f48 220 if (!parser || parser->GetMinVersion() > CEC_TEST_CLIENT_VERSION)
abbca718 221 {
acec5f48
LOK
222#ifdef __WINDOWS__
223 cout << "Cannot load libcec.dll" << endl;
224#else
225 cout << "Cannot load libcec.so" << endl;
226#endif
abbca718
LOK
227 return 1;
228 }
229 CStdString strLog;
230 strLog.Format("CEC Parser created - libcec version %d", parser->GetLibVersion());
231 cout << strLog.c_str() << endl;
232
233 //make stdin non-blocking
234#ifndef __WINDOWS__
235 int flags = fcntl(0, F_GETFL, 0);
236 flags |= O_NONBLOCK;
237 fcntl(0, F_SETFL, flags);
238#endif
239
240 string strPort;
e6d2161b
LOK
241 int iArgPtr = 0;
242 if (argc >= 3)
243 {
244 if (!strcmp(argv[1], "-f"))
245 {
246 g_logOutput.open(argv[2]);
247 iArgPtr = 2;
248 }
249 else if (!strcmp(argv[1], "--file"))
250 {
251 g_logOutput.open(argv[2]);
252 iArgPtr = 2;
253 }
254 }
255
256 if (argc < 2 + iArgPtr)
abbca718
LOK
257 {
258 cout << "no serial port given. trying autodetect: ";
25701fa6
LOK
259 cec_adapter devices[10];
260 uint8_t iDevicesFound = parser->FindAdapters(devices, 10, NULL);
abbca718
LOK
261 if (iDevicesFound <= 0)
262 {
263 cout << "FAILED" << endl;
264 UnloadLibCec(parser);
265 return 1;
266 }
267 else
268 {
269 cout << endl << " path: " << devices[0].path << endl <<
270 " com port: " << devices[0].comm << endl << endl;
271 strPort = devices[0].comm;
272 }
273 }
e6d2161b 274 else if (!strcmp(argv[1 + iArgPtr], "--list-devices") || !strcmp(argv[1 + iArgPtr], "-l"))
abbca718
LOK
275 {
276 list_devices(parser);
277 UnloadLibCec(parser);
278 return 0;
279 }
e6d2161b 280 else if (!strcmp(argv[1 + iArgPtr], "--help") || !strcmp(argv[1 + iArgPtr], "-h"))
abbca718
LOK
281 {
282 show_help(argv[0]);
283 return 0;
284 }
285 else
286 {
e6d2161b 287 strPort = argv[1 + iArgPtr];
abbca718
LOK
288 }
289
e6d2161b 290
abbca718
LOK
291 if (!parser->Open(strPort.c_str()))
292 {
293 cout << "unable to open the device on port " << strPort << endl;
294 flush_log(parser);
295 UnloadLibCec(parser);
296 return 1;
297 }
298
299 cout << "cec device opened" << endl;
abbca718 300
8829d291 301 parser->PowerOnDevices(CECDEVICE_TV);
abbca718
LOK
302 flush_log(parser);
303
304 parser->SetActiveView();
305 flush_log(parser);
306
307 bool bContinue(true);
308 cout << "waiting for input" << endl;
309 while (bContinue)
310 {
311 flush_log(parser);
312
313 string input;
314 getline(cin, input);
315 cin.clear();
316
317 if (!input.empty())
318 {
319 string command;
320 if (GetWord(input, command))
321 {
faa6a23b 322 if (command == "tx" || command == "txn")
abbca718
LOK
323 {
324 string strvalue;
8bca69de 325 uint8_t ivalue;
9dee1670 326 cec_command bytes;
25701fa6
LOK
327 bytes.clear();
328
abbca718 329 while (GetWord(input, strvalue) && HexStrToInt(strvalue, ivalue))
b2da2768 330 bytes.push_back(ivalue);
abbca718 331
faa6a23b 332 parser->Transmit(bytes, command == "tx");
abbca718 333 }
88f45c9b
LOK
334 else if (command == "on")
335 {
336 string strValue;
337 uint8_t iValue = 0;
338 if (GetWord(input, strValue) && HexStrToInt(strValue, iValue) && iValue <= 0xF)
339 {
340 parser->PowerOnDevices((cec_logical_address) iValue);
341 }
342 else
343 {
344 cout << "invalid destination" << endl;
345 }
346 }
347 else if (command == "standby")
348 {
349 string strValue;
350 uint8_t iValue = 0;
351 if (GetWord(input, strValue) && HexStrToInt(strValue, iValue) && iValue <= 0xF)
352 {
353 parser->StandbyDevices((cec_logical_address) iValue);
354 }
355 else
356 {
357 cout << "invalid destination" << endl;
358 }
359 }
6dfe9213
LOK
360 else if (command == "la")
361 {
362 string strvalue;
6dfe9213
LOK
363 if (GetWord(input, strvalue))
364 {
365 parser->SetLogicalAddress((cec_logical_address) atoi(strvalue.c_str()));
abbca718
LOK
366 }
367 }
a424ebac
LOK
368 else if (command == "pa")
369 {
370 string strB1, strB2;
371 uint8_t iB1, iB2;
372 if (GetWord(input, strB1) && HexStrToInt(strB1, iB1) &&
373 GetWord(input, strB2) && HexStrToInt(strB2, iB2))
374 {
375 uint16_t iPhysicalAddress = ((uint16_t)iB1 << 8) + iB2;
376 parser->SetPhysicalAddress(iPhysicalAddress);
377 }
378 }
1969b140
LOK
379 else if (command == "osd")
380 {
381 bool bFirstWord(false);
382 string strAddr, strMessage, strWord;
383 uint8_t iAddr;
384 if (GetWord(input, strAddr) && HexStrToInt(strAddr, iAddr) && iAddr < 0xF)
385 {
386 while (GetWord(input, strWord))
387 {
388 if (bFirstWord)
389 {
390 bFirstWord = false;
391 strMessage.append(" ");
392 }
393 strMessage.append(strWord);
394 }
395 parser->SetOSDString((cec_logical_address) iAddr, CEC_DISPLAY_CONTROL_DISPLAY_FOR_DEFAULT_TIME, strMessage.c_str());
396 }
397 }
abbca718
LOK
398 else if (command == "ping")
399 {
2abe74eb 400 parser->PingAdapter();
abbca718 401 }
8b7e5ff6
LOK
402 else if (command == "mon")
403 {
404 CStdString strEnable;
405 if (GetWord(input, strEnable) && (strEnable.Equals("0") || strEnable.Equals("1")))
406 {
407 parser->SwitchMonitoring(strEnable.Equals("1"));
408 }
409 }
abbca718
LOK
410 else if (command == "bl")
411 {
412 parser->StartBootloader();
413 }
12027dbe
LOK
414 else if (command == "r")
415 {
25701fa6 416 cout << "closing the connection" << endl;
12027dbe 417 parser->Close();
25701fa6
LOK
418 flush_log(parser);
419
420 cout << "opening a new connection" << endl;
12027dbe 421 parser->Open(strPort.c_str());
25701fa6
LOK
422 flush_log(parser);
423
424 cout << "setting active view" << endl;
12027dbe
LOK
425 parser->SetActiveView();
426 }
825ddb96
LOK
427 else if (command == "h" || command == "help")
428 {
429 show_console_help();
430 }
abbca718
LOK
431 else if (command == "q" || command == "quit")
432 {
433 bContinue = false;
434 }
bdd433cb
LOK
435 else if (command == "log")
436 {
437 CStdString strLevel;
438 if (GetWord(input, strLevel))
439 {
440 int iNewLevel = atoi(strLevel);
441 if (iNewLevel >= CEC_LOG_ERROR && iNewLevel <= CEC_LOG_ALL)
442 {
443 g_cecLogLevel = iNewLevel;
444 cout << "log level changed to " << strLevel.c_str() << endl;
445 }
446 }
447 }
abbca718
LOK
448 }
449 if (bContinue)
450 cout << "waiting for input" << endl;
451 }
452 CCondition::Sleep(50);
453 }
454
2abe74eb 455 parser->StandbyDevices(CECDEVICE_BROADCAST);
5f39c4d8 456 parser->Close();
abbca718
LOK
457 flush_log(parser);
458 UnloadLibCec(parser);
e6d2161b
LOK
459
460 if (g_logOutput.is_open())
461 g_logOutput.close();
462
abbca718
LOK
463 return 0;
464}