Subversion Repositories ESP8266_P1_Meter

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 raymond 1
/*
2
  Asynchronous TCP library for Espressif MCUs
3
 
4
  Copyright (c) 2016 Hristo Gochkov. All rights reserved.
5
  This file is part of the esp8266 core for Arduino environment.
6
 
7
  This library is free software; you can redistribute it and/or
8
  modify it under the terms of the GNU Lesser General Public
9
  License as published by the Free Software Foundation; either
10
  version 2.1 of the License, or (at your option) any later version.
11
 
12
  This library is distributed in the hope that it will be useful,
13
  but WITHOUT ANY WARRANTY; without even the implied warranty of
14
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15
  Lesser General Public License for more details.
16
 
17
  You should have received a copy of the GNU Lesser General Public
18
  License along with this library; if not, write to the Free Software
19
  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
20
*/
21
#include "Arduino.h"
22
#include "SyncClient.h"
23
#include "ESPAsyncTCP.h"
24
#include "cbuf.h"
25
#include <interrupts.h>
26
 
27
#define DEBUG_ESP_SYNC_CLIENT
28
#if defined(DEBUG_ESP_SYNC_CLIENT) && !defined(SYNC_CLIENT_DEBUG)
29
#define SYNC_CLIENT_DEBUG( format, ...) DEBUG_GENERIC_P("[SYNC_CLIENT]", format, ##__VA_ARGS__)
30
#endif
31
#ifndef SYNC_CLIENT_DEBUG
32
#define SYNC_CLIENT_DEBUG(...) do { (void)0;} while(false)
33
#endif
34
 
35
/*
36
  Without LWIP_NETIF_TX_SINGLE_PBUF, all tcp_writes default to "no copy".
37
  Referenced data must be preserved and free-ed from the specified tcp_sent()
38
  callback. Alternative, tcp_writes need to use the TCP_WRITE_FLAG_COPY
39
  attribute.
40
*/
41
static_assert(LWIP_NETIF_TX_SINGLE_PBUF, "Required, tcp_write() must always copy.");
42
 
43
SyncClient::SyncClient(size_t txBufLen)
44
  : _client(NULL)
45
  , _tx_buffer(NULL)
46
  , _tx_buffer_size(txBufLen)
47
  , _rx_buffer(NULL)
48
  , _ref(NULL)
49
{
50
  ref();
51
}
52
 
53
SyncClient::SyncClient(AsyncClient *client, size_t txBufLen)
54
  : _client(client)
55
  , _tx_buffer(new (std::nothrow) cbuf(txBufLen))
56
  , _tx_buffer_size(txBufLen)
57
  , _rx_buffer(NULL)
58
  , _ref(NULL)
59
{
60
  if(ref() > 0 && _client != NULL)
61
    _attachCallbacks();
62
}
63
 
64
SyncClient::~SyncClient(){
65
  if (0 == unref())
66
    _release();
67
}
68
 
69
void SyncClient::_release(){
70
  if(_client != NULL){
71
    _client->onData(NULL, NULL);
72
    _client->onAck(NULL, NULL);
73
    _client->onPoll(NULL, NULL);
74
    _client->abort();
75
    _client = NULL;
76
  }
77
  if(_tx_buffer != NULL){
78
    cbuf *b = _tx_buffer;
79
    _tx_buffer = NULL;
80
    delete b;
81
  }
82
  while(_rx_buffer != NULL){
83
    cbuf *b = _rx_buffer;
84
    _rx_buffer = _rx_buffer->next;
85
    delete b;
86
  }
87
}
88
 
89
int SyncClient::ref(){
90
  if(_ref == NULL){
91
    _ref = new (std::nothrow) int;
92
    if(_ref != NULL)
93
      *_ref = 0;
94
    else
95
      return -1;
96
  }
97
  return (++*_ref);
98
}
99
 
100
int SyncClient::unref(){
101
  int count = -1;
102
  if (_ref != NULL) {
103
    count = --*_ref;
104
    if (0 == count) {
105
      delete _ref;
106
      _ref = NULL;
107
    }
108
  }
109
  return count;
110
}
111
 
112
#if ASYNC_TCP_SSL_ENABLED
113
int SyncClient::_connect(const IPAddress& ip, uint16_t port, bool secure){
114
#else
115
int SyncClient::_connect(const IPAddress& ip, uint16_t port){
116
#endif
117
  if(connected())
118
    return 0;
119
  if(_client != NULL)
120
    delete _client;
121
 
122
  _client = new (std::nothrow) AsyncClient();
123
  if (_client == NULL)
124
    return 0;
125
 
126
  _client->onConnect([](void *obj, AsyncClient *c){ ((SyncClient*)(obj))->_onConnect(c); }, this);
127
  _attachCallbacks_Disconnect();
128
#if ASYNC_TCP_SSL_ENABLED
129
  if(_client->connect(ip, port, secure)){
130
#else
131
  if(_client->connect(ip, port)){
132
#endif
133
    while(_client != NULL && !_client->connected() && !_client->disconnecting())
134
      delay(1);
135
    return connected();
136
  }
137
  return 0;
138
}
139
 
140
#if ASYNC_TCP_SSL_ENABLED
141
int SyncClient::connect(const char *host, uint16_t port, bool secure){
142
#else
143
int SyncClient::connect(const char *host, uint16_t port){
144
#endif
145
  if(connected())
146
    return 0;
147
  if(_client != NULL)
148
    delete _client;
149
 
150
  _client = new (std::nothrow) AsyncClient();
151
  if (_client == NULL)
152
    return 0;
153
 
154
  _client->onConnect([](void *obj, AsyncClient *c){ ((SyncClient*)(obj))->_onConnect(c); }, this);
155
  _attachCallbacks_Disconnect();
156
#if ASYNC_TCP_SSL_ENABLED
157
  if(_client->connect(host, port, secure)){
158
#else
159
  if(_client->connect(host, port)){
160
#endif
161
    while(_client != NULL && !_client->connected() && !_client->disconnecting())
162
      delay(1);
163
    return connected();
164
  }
165
  return 0;
166
}
167
//#define SYNCCLIENT_NEW_OPERATOR_EQUAL
168
#ifdef SYNCCLIENT_NEW_OPERATOR_EQUAL
169
/*
170
  New behavior for operator=
171
 
172
  Allow for the object to be placed on a queue and transfered to a new container
173
  with buffers still in tact. Avoiding receive data drops. Transfers rx and tx
174
  buffers. Supports return by value.
175
 
176
  Note, this is optional, the old behavior is the default.
177
 
178
*/
179
SyncClient & SyncClient::operator=(const SyncClient &other){
180
  int *rhsref = other._ref;
181
  ++*rhsref; // Just in case the left and right side are the same object with different containers
182
  if (0 == unref())
183
    _release();
184
  _ref = other._ref;
185
  ref();
186
  --*rhsref;
187
  // Why do I not test _tx_buffer for != NULL and free?
188
  // I allow for the lh target container, to be a copy of an active
189
  // connection. Thus we are just reusing the container.
190
  // The above unref() handles releaseing the previous client of the container.
191
  _tx_buffer_size = other._tx_buffer_size;
192
  _tx_buffer = other._tx_buffer;
193
  _client = other._client;
194
  if (_client != NULL && _tx_buffer == NULL)
195
    _tx_buffer = new (std::nothrow) cbuf(_tx_buffer_size);
196
 
197
  _rx_buffer = other._rx_buffer;
198
  if(_client)
199
    _attachCallbacks();
200
  return *this;
201
}
202
#else   // ! SYNCCLIENT_NEW_OPERATOR_EQUAL
203
// This is the origianl logic with null checks
204
SyncClient & SyncClient::operator=(const SyncClient &other){
205
  if(_client != NULL){
206
    _client->abort();
207
    _client->free();
208
    _client = NULL;
209
  }
210
  _tx_buffer_size = other._tx_buffer_size;
211
  if(_tx_buffer != NULL){
212
    cbuf *b = _tx_buffer;
213
    _tx_buffer = NULL;
214
    delete b;
215
  }
216
  while(_rx_buffer != NULL){
217
    cbuf *b = _rx_buffer;
218
    _rx_buffer = b->next;
219
    delete b;
220
  }
221
  if(other._client != NULL)
222
    _tx_buffer = new (std::nothrow) cbuf(other._tx_buffer_size);
223
 
224
  _client = other._client;
225
  if(_client)
226
    _attachCallbacks();
227
 
228
  return *this;
229
}
230
#endif
231
 
232
void SyncClient::setTimeout(uint32_t seconds){
233
  if(_client != NULL)
234
    _client->setRxTimeout(seconds);
235
}
236
 
237
uint8_t SyncClient::status(){
238
  if(_client == NULL)
239
    return 0;
240
  return _client->state();
241
}
242
 
243
uint8_t SyncClient::connected(){
244
  return (_client != NULL && _client->connected());
245
}
246
 
247
bool SyncClient::stop(unsigned int maxWaitMs){
248
  (void)maxWaitMs;
249
  if(_client != NULL)
250
    _client->close(true);
251
  return true;
252
}
253
 
254
size_t SyncClient::_sendBuffer(){
255
  if(_client == NULL || _tx_buffer == NULL)
256
    return 0;
257
  size_t available = _tx_buffer->available();
258
  if(!connected() || !_client->canSend() || available == 0)
259
    return 0;
260
  size_t sendable = _client->space();
261
  if(sendable < available)
262
    available= sendable;
263
  char *out = new (std::nothrow) char[available];
264
  if(out == NULL)
265
    return 0;
266
 
267
  _tx_buffer->read(out, available);
268
  size_t sent = _client->write(out, available);
269
  delete[] out;
270
  return sent;
271
}
272
 
273
void SyncClient::_onData(void *data, size_t len){
274
  _client->ackLater();
275
  cbuf *b = new (std::nothrow) cbuf(len+1);
276
  if(b != NULL){
277
    b->write((const char *)data, len);
278
    if(_rx_buffer == NULL)
279
      _rx_buffer = b;
280
    else {
281
      cbuf *p = _rx_buffer;
282
      while(p->next != NULL)
283
        p = p->next;
284
      p->next = b;
285
    }
286
  } else {
287
    // We ran out of memory. This fail causes lost receive data.
288
    // The connection should be closed in a manner that conveys something
289
    // bad/abnormal has happened to the connection. Hence, we abort the
290
    // connection to avoid possible data corruption.
291
    // Note, callbacks maybe called.
292
    _client->abort();
293
  }
294
}
295
 
296
void SyncClient::_onDisconnect(){
297
  if(_client != NULL){
298
    _client = NULL;
299
  }
300
  if(_tx_buffer != NULL){
301
    cbuf *b = _tx_buffer;
302
    _tx_buffer = NULL;
303
    delete b;
304
  }
305
}
306
 
307
void SyncClient::_onConnect(AsyncClient *c){
308
  _client = c;
309
  if(_tx_buffer != NULL){
310
    cbuf *b = _tx_buffer;
311
    _tx_buffer = NULL;
312
    delete b;
313
  }
314
  _tx_buffer = new (std::nothrow) cbuf(_tx_buffer_size);
315
  _attachCallbacks_AfterConnected();
316
}
317
 
318
void SyncClient::_attachCallbacks(){
319
  _attachCallbacks_Disconnect();
320
  _attachCallbacks_AfterConnected();
321
}
322
 
323
void SyncClient::_attachCallbacks_AfterConnected(){
324
  _client->onAck([](void *obj, AsyncClient* c, size_t len, uint32_t time){ (void)c; (void)len; (void)time; ((SyncClient*)(obj))->_sendBuffer(); }, this);
325
  _client->onData([](void *obj, AsyncClient* c, void *data, size_t len){ (void)c; ((SyncClient*)(obj))->_onData(data, len); }, this);
326
  _client->onTimeout([](void *obj, AsyncClient* c, uint32_t time){ (void)obj; (void)time; c->close(); }, this);
327
}
328
 
329
void SyncClient::_attachCallbacks_Disconnect(){
330
  _client->onDisconnect([](void *obj, AsyncClient* c){ ((SyncClient*)(obj))->_onDisconnect(); delete c; }, this);
331
}
332
 
333
size_t SyncClient::write(uint8_t data){
334
  return write(&data, 1);
335
}
336
 
337
size_t SyncClient::write(const uint8_t *data, size_t len){
338
  if(_tx_buffer == NULL || !connected()){
339
    return 0;
340
  }
341
  size_t toWrite = 0;
342
  size_t toSend = len;
343
  while(_tx_buffer->room() < toSend){
344
    toWrite = _tx_buffer->room();
345
    _tx_buffer->write((const char*)data, toWrite);
346
    while(connected() && !_client->canSend())
347
      delay(0);
348
    if(!connected())
349
      return 0;
350
    _sendBuffer();
351
    toSend -= toWrite;
352
  }
353
  _tx_buffer->write((const char*)(data+(len - toSend)), toSend);
354
  if(connected() && _client->canSend())
355
    _sendBuffer();
356
  return len;
357
}
358
 
359
int SyncClient::available(){
360
  if(_rx_buffer == NULL) return 0;
361
  size_t a = 0;
362
  cbuf *b = _rx_buffer;
363
  while(b != NULL){
364
    a += b->available();
365
    b = b->next;
366
  }
367
  return a;
368
}
369
 
370
int SyncClient::peek(){
371
  if(_rx_buffer == NULL) return -1;
372
  return _rx_buffer->peek();
373
}
374
 
375
int SyncClient::read(uint8_t *data, size_t len){
376
  if(_rx_buffer == NULL) return -1;
377
 
378
  size_t readSoFar = 0;
379
  while(_rx_buffer != NULL && (len - readSoFar) >= _rx_buffer->available()){
380
    cbuf *b = _rx_buffer;
381
    _rx_buffer = _rx_buffer->next;
382
    size_t toRead = b->available();
383
    readSoFar += b->read((char*)(data+readSoFar), toRead);
384
    if(connected()){
385
        _client->ack(b->size() - 1);
386
    }
387
    delete b;
388
  }
389
  if(_rx_buffer != NULL && readSoFar < len){
390
    readSoFar += _rx_buffer->read((char*)(data+readSoFar), (len - readSoFar));
391
  }
392
  return readSoFar;
393
}
394
 
395
int SyncClient::read(){
396
  uint8_t res = 0;
397
  if(read(&res, 1) != 1)
398
    return -1;
399
  return res;
400
}
401
 
402
bool SyncClient::flush(unsigned int maxWaitMs){
403
  (void)maxWaitMs;
404
  if(_tx_buffer == NULL || !connected())
405
    return false;
406
  if(_tx_buffer->available()){
407
    while(connected() && !_client->canSend())
408
      delay(0);
409
    if(_client == NULL || _tx_buffer == NULL)
410
      return false;
411
    _sendBuffer();
412
  }
413
  return true;
414
}