/[svn]/linuxsampler/trunk/src/shell/LSCPClient.cpp
ViewVC logotype

Contents of /linuxsampler/trunk/src/shell/LSCPClient.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2515 - (show annotations) (download)
Wed Feb 5 20:45:18 2014 UTC (10 years, 2 months ago) by schoenebeck
File size: 4837 byte(s)
* WIP: Introducing the LSCP shell: for now, providing color
  highlighting while typing (indicating correct part bold white,
  incorrect part red, and turning green when the command is
  complete. The shell application is implemented as thin client,
  that is the parser work is performed on sampler side and the
  shell application is just providing output formatting.
* Bumped version (1.0.0.svn28).

1 /*
2 * LSCP Shell
3 *
4 * Copyright (c) 2014 Christian Schoenebeck
5 *
6 * This program is part of LinuxSampler and released under the same terms.
7 */
8
9 #include "LSCPClient.h"
10
11 #include <strings.h>
12
13 LSCPClient::LSCPClient() :
14 Thread(false, false, 1, -1),
15 hSocket(-1), m_callback(NULL),
16 m_multiLineExpected(false), m_multiLineComplete(false)
17 {
18 }
19
20 LSCPClient::~LSCPClient() {
21 disconnect();
22 }
23
24 bool LSCPClient::connect(String host, int port) {
25 m_lineBuffer.clear();
26 m_lines.clear();
27 // resolve given host name
28 hostent* server = ::gethostbyname(host.c_str());
29 if (!server) {
30 std::cerr << "Error: Could not resolve host \"" << host << "\".\n";
31 return false;
32 }
33 // create local TCP socket
34 hSocket = ::socket(AF_INET, SOCK_STREAM, 0);
35 if (hSocket < 0) {
36 std::cerr << "Error: Could not create local socket.\n";
37 return false;
38 }
39 // TCP connect to server
40 sockaddr_in addr;
41 bzero((char*)&addr, sizeof(addr));
42 addr.sin_family = AF_INET;
43 bcopy((char*)server->h_addr, (char*)&addr.sin_addr.s_addr, server->h_length);
44 addr.sin_port = htons(port);
45 if (::connect(hSocket, (sockaddr*)&addr, sizeof(addr)) < 0) {
46 std::cerr << "Error: Could not connect to host \"" << host << "\".\n";
47 disconnect();
48 return false;
49 }
50 StartThread();
51 return true; // success
52 }
53
54 void LSCPClient::disconnect() {
55 if (hSocket >= 0) {
56 StopThread();
57 ::close(hSocket);
58 hSocket = -1;
59 }
60 }
61
62 bool LSCPClient::isConnected() const {
63 return hSocket >= 0;
64 }
65
66 bool LSCPClient::send(char c) {
67 String s;
68 s += c;
69 return send(s);
70 }
71
72 bool LSCPClient::send(String s) {
73 if (!isConnected()) return false;
74 int n = ::write(hSocket, &s[0], s.size());
75 return n == s.size();
76 }
77
78 String LSCPClient::sendCommandSync(String s) {
79 m_linesMutex.Lock();
80 m_lines.clear();
81 m_linesMutex.Unlock();
82
83 m_sync.Set(true);
84 if (!send(s + "\n")) return "";
85 m_sync.WaitIf(true);
86
87 m_linesMutex.Lock();
88 String sResponse = m_lines.back();
89 m_lines.clear();
90 m_linesMutex.Unlock();
91
92 return sResponse;
93 }
94
95 optional<String> LSCPClient::popLine() {
96 String s;
97 LockGuard guard(m_linesMutex);
98 if (m_lines.empty()) return optional<String>::nothing;
99 s = m_lines.front();
100 m_lines.pop_front();
101 //FIXME: this is incorrect when having multiple complete messages in the queue
102 if (m_multiLineExpected && s.substr(0, 1) == ".")
103 m_multiLineExpected = m_multiLineComplete = false;
104 return s;
105 }
106
107 optional<String> LSCPClient::lookAheadLine(int index) {
108 LockGuard guard(m_linesMutex);
109 if (index < 0 || index >= m_lines.size())
110 return optional<String>::nothing;
111 std::list<String>::iterator it = m_lines.begin();
112 for (int i = 0; i < index; ++i) ++it;
113 String s = *it;
114 return s;
115 }
116
117 bool LSCPClient::messageComplete() {
118 if (!lineAvailable()) return false;
119 LockGuard guard(m_linesMutex);
120 if (!m_multiLineExpected) return true;
121 return m_multiLineComplete;
122 }
123
124 bool LSCPClient::multiLine() {
125 //FIXME: returns falsely "true" in case the first message is single-line response but there is already a multi-line response in the FIFO
126 return m_multiLineExpected;
127 }
128
129 bool LSCPClient::lineAvailable() const {
130 return !m_lines.empty(); // is thread safe
131 }
132
133 void LSCPClient::setCallback(Callback_t fn) {
134 m_callback = fn;
135 }
136
137 optional<String> LSCPClient::receiveLine() {
138 if (!isConnected()) return optional<String>::nothing;
139 for (char c; true; ) {
140 int n = ::read(hSocket, &c, 1);
141 if (n < 1) return optional<String>::nothing;
142 if (c == '\r') continue;
143 if (c == '\n') {
144 String s = m_lineBuffer;
145 m_lineBuffer.clear();
146 return s;
147 }
148 //printf("->%c\n", c);
149 m_lineBuffer += c;
150 }
151 return optional<String>::nothing;
152 }
153
154 int LSCPClient::Main() {
155 static const String multiLineKey = LSCP_SHK_EXPECT_MULTI_LINE;
156 while (true) {
157 optional<String> pLine = receiveLine();
158 if (pLine) {
159 String s = *pLine;
160 //printf("->line '%s'\n", s.c_str());
161 m_linesMutex.Lock();
162 if (s.substr(0, multiLineKey.length()) == multiLineKey) {
163 m_multiLineExpected = true;
164 m_multiLineComplete = false;
165 } else {
166 if (m_multiLineExpected && s.substr(0, 1) == ".") {
167 m_multiLineComplete = true;
168 }
169 m_lines.push_back(s);
170 }
171 m_linesMutex.Unlock();
172
173 if (m_sync.GetUnsafe()) m_sync.Set(false);
174 else if (m_callback) (*m_callback)(this);
175 }
176 TestCancel();
177 }
178 return 0; // just to avoid a warning with some old compilers
179 }

  ViewVC Help
Powered by ViewVC