FrelonSerialLine.cpp 6.88 KB
Newer Older
1
2
#include "FrelonSerialLine.h"
#include "RegEx.h"
ahoms's avatar
ahoms committed
3
#include "MiscUtils.h"
4
#include <sstream>
5

6
using namespace lima;
7
8
9
using namespace lima::Frelon;
using namespace std;

10
11
12
13
const double SerialLine::TimeoutSingle    = 0.5;
const double SerialLine::TimeoutNormal    = 2.0;
const double SerialLine::TimeoutMultiLine = 3.0;
const double SerialLine::TimeoutReset     = 5.0;
14
15
16
17
18
19
20


SerialLine::SerialLine(Espia::SerialLine& espia_ser_line)
	: m_espia_ser_line(espia_ser_line)
{
	m_espia_ser_line.setLineTerm("\r\n");
	m_espia_ser_line.setTimeout(TimeoutNormal);
21
22
23

	m_last_warn = 0;

24
25
26
27
	m_curr_op = None;
	m_curr_cache = false;
	m_cache_act = true;

28
	flush();
29
30
}

31
32
33
34
35
Espia::SerialLine& SerialLine::getEspiaSerialLine()
{
	return m_espia_ser_line;
}

36
void SerialLine::write(const string& buffer, bool no_wait)
37
38
39
40
41
42
43
44
45
46
47
48
{
	AutoMutex l = lock(AutoMutex::Locked);

	while (m_curr_op != None)
		m_cond.wait();

	writeCmd(buffer, no_wait);

	l.leaveLocked();
}

void SerialLine::writeCmd(const string& buffer, bool no_wait)
49
{
ahoms's avatar
ahoms committed
50
	MsgPartStrMapType msg_parts;
51
52
53
	splitMsg(buffer, msg_parts);
	const string& cmd = msg_parts[MsgCmd];

54
55
56
57
	if (cmd == CmdStrMap[Reset]) {
		m_curr_op = DoReset;
		m_reg_cache.clear();
	} else {
ahoms's avatar
ahoms committed
58
		MultiLineCmdStrMapType::const_iterator it, end;
59
		end = MultiLineCmdStrMap.end();
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
		it = FindMapValue(MultiLineCmdStrMap, cmd);
		if (it != end)
			m_curr_op = MultiRead;
	}

	bool reg_found = false;
	if (m_curr_op == None) {
		RegStrMapType::const_iterator it, end = RegStrMap.end();
		it = FindMapValue(RegStrMap, cmd);
		reg_found = (it != end);
		if (reg_found)
			m_curr_reg = it->first;
	}

	m_curr_cache = false;
75
	if (reg_found && isRegCacheable(m_curr_reg)) {
76
77
78
79
80
81
82
83
84
		bool is_req = !msg_parts[MsgReq].empty();
		m_curr_op = is_req ? ReadReg : WriteReg;
		const string& cache_val = m_reg_cache[m_curr_reg];
		if (is_req) {
			m_curr_resp = cache_val;
			m_curr_cache = !m_curr_resp.empty();
		} else {
			m_curr_resp = msg_parts[MsgVal];
			m_curr_cache = (m_curr_resp == cache_val);
85
		}
86
87
		if (m_curr_cache)
			return;
88
89
	}

90
91
92
93
94
	if (m_curr_op == None)
		m_curr_op = DoCmd;

	string sync = msg_parts[MsgSync].empty() ? ">" : "";
	string term = msg_parts[MsgTerm].empty() ? "\r\n" : "";
95
96
97
98
99
100
101
102
	string msg = sync + buffer + term;

	m_espia_ser_line.write(msg, no_wait);
}

void SerialLine::read(string& buffer, int max_len, double timeout)
{
	m_espia_ser_line.read(buffer, max_len, timeout);
103
104
}

105
106
void SerialLine::readStr(string& buffer, int max_len, 
			 const string& term, double timeout)
107
{
108
	m_espia_ser_line.readStr(buffer, max_len, term, timeout);
109
110
}

111
void SerialLine::readLine(string& buffer, int max_len, double timeout)
112
{
113
	AutoMutex l = lock(AutoMutex::PrevLocked);
114

115
116
117
118
119
120
121
122
123
	readResp(buffer, max_len, timeout);
}

void SerialLine::readResp(string& buffer, int max_len, double timeout)
{
	if (m_curr_op == None)
		throw LIMA_HW_EXC(Error, "readLine without previous write");

	if ((m_curr_op == MultiRead) && (timeout == TimeoutDefault))
124
125
126
		readMultiLine(buffer, max_len);
	else
		readSingleLine(buffer, max_len, timeout);
127
128
129

	m_curr_op = None;
	m_cond.signal();
130
131
132
133
}

void SerialLine::readSingleLine(string& buffer, int max_len, double timeout)
{
134
135
136
137
138
139
140
141
142
143
	bool is_req = (m_curr_op == ReadReg);
	if (m_curr_cache) {
		ostringstream os;
		os << "!OK";
		if (is_req)
			os << ":" << m_curr_resp;
		os << "\r\n";
		buffer = os.str();
		m_curr_fmt_resp = m_curr_resp;
		return;
144
	}
145
146
147

	if ((m_curr_op == DoReset) && (timeout == TimeoutDefault))
		timeout = TimeoutReset;
148
	m_espia_ser_line.readLine(buffer, max_len, timeout);
149
150
151
152

	decodeFmtResp(buffer, m_curr_fmt_resp);
	
	bool reg_op = ((m_curr_op == WriteReg) || (m_curr_op == ReadReg));
153
	if (!reg_op || !isRegCacheable(m_curr_reg))
154
155
156
157
		return;

	string& cache_val = m_reg_cache[m_curr_reg];
	cache_val = is_req ? m_curr_fmt_resp : m_curr_resp;
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
}

void SerialLine::readMultiLine(string& buffer, int max_len)
{
	Timestamp timeout = Timestamp::now() + Timestamp(TimeoutMultiLine);

	buffer.clear();

	while (Timestamp::now() < timeout) {
		string ans;
		int len = max_len - buffer.size();
		try {
			m_espia_ser_line.readLine(ans, len, TimeoutSingle);
			buffer += ans;
		} catch (Exception e) {
			if (!buffer.empty())
				break;
		}
	}

	if (buffer.empty())
		throw LIMA_HW_EXC(Error, "Timeout reading Frelon multi line");
}

182
183
184
185
186
187
188
189
190
bool SerialLine::isRegCacheable(Reg reg)
{
	if (!m_cache_act)
		return false;

	const RegListType& list = NonCacheableRegList;
	return (find(list.begin(), list.end(), reg) == list.end());
}

191
192
193
void SerialLine::flush()
{
	m_espia_ser_line.flush();
194
195
}

196
void SerialLine::getNbAvailBytes(int &avail)
197
{
198
	m_espia_ser_line.getNbAvailBytes(avail);
199
200
201
202
203
204
}

void SerialLine::setTimeout(double timeout)
{
	m_espia_ser_line.setTimeout(timeout);
}
205

206
207
208
void SerialLine::getTimeout(double& timeout) const
{
	m_espia_ser_line.getTimeout(timeout);
209
210
}

211

212
void SerialLine::splitMsg(const string& msg, 
ahoms's avatar
ahoms committed
213
			  MsgPartStrMapType& msg_parts) const
214
215
216
{
	msg_parts.clear();

217
218
219
220
	const static RegEx re("^(?P<sync>>)?"
			      "(?P<cmd>[A-Z]+)"
			      "((?P<req>\\?)|(?P<val>[0-9]+))?"
			      "(?P<term>[\r\n]+)?$");
221
222
223
224
225
226
227
228
229
230
231

	RegEx::FullNameMatchType match;
	if (!re.matchName(msg, match))
		throw LIMA_HW_EXC(InvalidValue, "Invalid Frelon message");

	typedef pair<MsgPart, string> KeyPair;
	static const KeyPair key_list[] = {
		KeyPair(MsgSync, "sync"), KeyPair(MsgCmd, "cmd"), 
		KeyPair(MsgVal,  "val"),  KeyPair(MsgReq, "req"),  
		KeyPair(MsgTerm, "term"),
	};
ahoms's avatar
ahoms committed
232
	const KeyPair *it, *end = C_LIST_END(key_list);
233
234
235
236
237
238
239
	for (it = key_list; it != end; ++it) {
		const MsgPart& key = it->first;
		const string&  grp = it->second;
		msg_parts[key] = string(match[grp].start, match[grp].end);
	}
}

240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
void SerialLine::decodeFmtResp(const string& ans, string& fmt_resp)
{
	fmt_resp.clear();

	const static RegEx re("!(OK(:(?P<resp>[^\r]+))?|"
			        "W\a?:(?P<warn>[^\r]+)|"
			        "E\a?:(?P<err>[^\r]+))\r\n");

	RegEx::FullNameMatchType match;
	if (!re.matchName(ans, match))
		throw LIMA_HW_EXC(Error, "Invalid Frelon answer");

	RegEx::SingleMatchType& err = match["err"];
	if (err) {
		string err_str(err.start, err.end);
		string err_desc = string("Frelon Error: ") + err_str;
		throw LIMA_HW_EXC(Error, err_desc);
	}

	RegEx::SingleMatchType& warn = match["warn"];
	if (warn) {
		string warn_str(err.start, err.end);
		istringstream is(warn_str);
		is >> m_last_warn;
		return;
	}

	RegEx::SingleMatchType& resp = match["resp"];
	fmt_resp = string(resp.start, resp.end);
}

void SerialLine::sendFmtCmd(const string& cmd, string& resp)
{
273
274
275
276
277
	AutoMutex l = lock(AutoMutex::Locked);

	m_curr_fmt_resp.clear();

	writeCmd(cmd);
278
	string ans;
279
280
281
	readResp(ans);

	resp = m_curr_fmt_resp;
282
283
284
285
286
287
288
289
}

int SerialLine::getLastWarning()
{
	int last_warn = m_last_warn;
	m_last_warn = 0;
	return last_warn;
}
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309

void SerialLine::clearCache()
{
	AutoMutex l = lock(AutoMutex::Locked);
	m_reg_cache.clear();
}

void SerialLine::setCacheActive(bool cache_act)
{
	AutoMutex l = lock(AutoMutex::Locked);
	if (cache_act && !m_cache_act)
		m_reg_cache.clear();
	m_cache_act = cache_act;
}

void SerialLine::getCacheActive(bool& cache_act)
{
	AutoMutex l = lock(AutoMutex::Locked);
	cache_act = m_cache_act;
}