sbgEComTransfer.c
Go to the documentation of this file.
1 #include "sbgEComTransfer.h"
2 #include "../sbgEComCmdCommon.h"
5 
6 //----------------------------------------------------------------------//
7 //- Internal transfer method definitions -//
8 //----------------------------------------------------------------------//
9 
18 SbgErrorCode sbgEComTransferSendInit(SbgEComHandle *pHandle, uint8 msgClass, uint8 msg, size_t size)
19 {
20  SbgErrorCode errorCode = SBG_NO_ERROR;
21  SbgStreamBuffer streamBuffer;
22  uint8 outputBuffer[SBG_ECOM_MAX_PAYLOAD_SIZE];
23  uint32 i;
24 
25  //
26  // Check input parameter
27  //
28  SBG_ASSERT(pHandle);
29 
30  //
31  // Initialize stream buffer that will contain payload
32  //
33  sbgStreamBufferInitForWrite(&streamBuffer, outputBuffer, sizeof(outputBuffer));
34 
35  //
36  // Build transfer payload (a SBG_ECOM_TRANSFER_START command and the total size of the upload)
37  //
39  sbgStreamBufferWriteSizeT32LE(&streamBuffer, size);
40 
41  //
42  // Send command (multiple times in case of failures)
43  //
44  for (i = 0; i < 3; i++)
45  {
46  //
47  // Send transfer payload encapsulated in ECom protocol
48  //
49  errorCode = sbgEComProtocolSend(&pHandle->protocolHandle, msgClass, msg, sbgStreamBufferGetLinkedBuffer(&streamBuffer), sbgStreamBufferGetLength(&streamBuffer));
50 
51  if (errorCode == SBG_NO_ERROR)
52  {
53  //
54  // If the device accepts the transfer, it returns an ack, wait for the answer.
55  //
56  errorCode = sbgEComWaitForAck(pHandle, msgClass, msg, pHandle->cmdDefaultTimeOut);
57 
58  //
59  // Test if the response is positive from device
60  //
61  if (errorCode == SBG_NO_ERROR)
62  {
63  //
64  // Ack received, no need for other trial.
65  //
66  break;
67  }
68  }
69  }
70 
71  return errorCode;
72 }
73 
84 SbgErrorCode sbgEComTransferSendData(SbgEComHandle *pHandle, uint8 msgClass, uint8 msg, const void *pBuffer, size_t offset, size_t packetSize)
85 {
86  SbgErrorCode errorCode = SBG_NO_ERROR;
87  SbgStreamBuffer streamBuffer;
88  uint8 outputBuffer[SBG_ECOM_MAX_PAYLOAD_SIZE];
89  uint32 i;
90 
91  //
92  // Check input parameters
93  //
94  SBG_ASSERT(pHandle);
95  SBG_ASSERT(pBuffer);
96  SBG_ASSERT(packetSize > 0);
97 
98  //
99  // Initialize stream buffer for output
100  //
101  sbgStreamBufferInitForWrite(&streamBuffer, outputBuffer, sizeof(outputBuffer));
102 
103  //
104  // Build payload: a SBG_ECOM_TRANSFER_DATA command, the offset from the start of the transfer, and the data
105  //
107  sbgStreamBufferWriteSizeT32LE(&streamBuffer, offset);
108  sbgStreamBufferWriteBuffer(&streamBuffer, pBuffer, packetSize);
109 
110  //
111  // Send command (multiple times in case of failures)
112  //
113  for (i = 0; i < 3; i++)
114  {
115  //
116  // Send transfer payload encapsulated in a ECom protocol frame
117  //
118  errorCode = sbgEComProtocolSend(&pHandle->protocolHandle, msgClass, msg, sbgStreamBufferGetLinkedBuffer(&streamBuffer), sbgStreamBufferGetLength(&streamBuffer));
119 
120  if (errorCode == SBG_NO_ERROR)
121  {
122  //
123  // If the device receives the frame successfully received, it responds with an ACK, wait for the answer
124  //
125  errorCode = sbgEComWaitForAck(pHandle, msgClass, msg, pHandle->cmdDefaultTimeOut);
126 
127  //
128  // Test if the response is positive from device
129  //
130  if (errorCode == SBG_NO_ERROR)
131  {
132  //
133  // Ack received, no need for other trial
134  //
135  break;
136  }
137  }
138  }
139 
140  return errorCode;
141 }
142 
151 {
152  SbgErrorCode errorCode = SBG_NO_ERROR;
153  SbgStreamBuffer outStreamBuffer;
154  uint8 outputBuffer[sizeof(uint16)];
155  uint32 i;
156 
157  //
158  // Check input parameter
159  //
160  SBG_ASSERT(pHandle);
161 
162  //
163  // Initialize stream buffer for output
164  //
165  sbgStreamBufferInitForWrite(&outStreamBuffer, outputBuffer, sizeof(outStreamBuffer));
166 
167  //
168  // Build payload, only a SBG_ECOM_TRANSFER_END cmd
169  //
171 
172  //
173  // Send command (multiple times in case of failures)
174  //
175  for (i = 0; i < 3; i++)
176  {
177  //
178  // Send upload end payload encapsulated in a ECom protocol frame
179  //
180  errorCode = sbgEComProtocolSend(&pHandle->protocolHandle, msgClass, msg, sbgStreamBufferGetLinkedBuffer(&outStreamBuffer), sbgStreamBufferGetLength(&outStreamBuffer));
181 
182  if (errorCode == SBG_NO_ERROR)
183  {
184  //
185  // If the device finishes the sequence successfully, it responds with an ACK, wait for answer
186  //
187  errorCode = sbgEComWaitForAck(pHandle, msgClass, msg, pHandle->cmdDefaultTimeOut);
188 
189  //
190  // Test if the response is positive from device
191  //
192  if (errorCode == SBG_NO_ERROR)
193  {
194  //
195  // ACK received, no need for other trial
196  //
197  break;
198  }
199  }
200  }
201 
202  return errorCode;
203 }
204 
213 SbgErrorCode sbgEComTransferReceiveInit(SbgEComHandle *pHandle, uint8 msgClass, uint8 msg, size_t *pSize)
214 {
215  SbgErrorCode errorCode = SBG_NO_ERROR;
216  SbgStreamBuffer outStreamBuffer;
217  SbgStreamBuffer inStreamBuffer;
218  uint8 inputBuffer[SBG_ECOM_MAX_PAYLOAD_SIZE];
219  uint8 outputBuffer[sizeof(uint16)];
220  uint8 receivedMsgClass;
221  uint8 receivedMsg;
222  uint16 transferCmd;
223  size_t inputSize;
224  size_t transferSize;
225  uint32 i;
226 
227  //
228  // Check input parameter
229  //
230  SBG_ASSERT(pHandle);
231 
232  //
233  // Initialize stream buffer for output
234  //
235  sbgStreamBufferInitForWrite(&outStreamBuffer, outputBuffer, sizeof(outStreamBuffer));
236 
237  //
238  // Build payload, only a SBG_ECOM_TRANSFER_START cmd
239  //
241 
242  //
243  // Send command (multiple times in case of failures)
244  //
245  for (i = 0; i < 3; i++)
246  {
247  //
248  // Send transfer payload encapsulated in an ECom protocol frame
249  //
250  errorCode = sbgEComProtocolSend(&pHandle->protocolHandle, msgClass, msg, sbgStreamBufferGetLinkedBuffer(&outStreamBuffer), sbgStreamBufferGetLength(&outStreamBuffer));
251 
252  if (errorCode == SBG_NO_ERROR)
253  {
254  //
255  // Wait for reponse, the device should respond with a ECOM_TRANSFER_START command and the transfer size
256  // If it can not initiate the transfer, it will respond with a NACK
257  //
258  errorCode = sbgEComReceiveAnyCmd(pHandle, &receivedMsgClass, &receivedMsg, inputBuffer, &inputSize, SBG_ECOM_MAX_PAYLOAD_SIZE, pHandle->cmdDefaultTimeOut);
259 
260  if (errorCode == SBG_NO_ERROR)
261  {
262  //
263  // Test if the command received is the one expected
264  //
265  if ((receivedMsgClass == msgClass) && (receivedMsg == msg))
266  {
267  //
268  // Init stream buffer on received payload to process it
269  //
270  sbgStreamBufferInitForRead(&inStreamBuffer, inputBuffer, inputSize);
271 
272  //
273  // Retrieve parameters, the first one is the transfer command
274  // The second one is the total transfer size
275  //
276  transferCmd = sbgStreamBufferReadUint16LE(&inStreamBuffer);
277  transferSize = sbgStreamBufferReadSizeT32LE(&inStreamBuffer);
278 
279  //
280  // The device should have answered with SBG_ECOM_TRANSFER_START transfer command
281  //
282  if (transferCmd == SBG_ECOM_TRANSFER_START)
283  {
284  //
285  // Update output variable with the transfer size
286  //
287  *pSize = transferSize;
288 
289  //
290  // No need for other trials, exit loop/
291  //
292  break;
293  }
294  else
295  {
296  //
297  // Invalid transfer command response
298  //
299  errorCode = SBG_ERROR;
300  }
301  }
302  else
303  {
304  //
305  // This is not the command expected
306  //
307  errorCode = SBG_ERROR;
308  }
309  }
310  }
311  }
312 
313  return errorCode;
314 }
315 
326 SbgErrorCode sbgEComTransferReceiveData(SbgEComHandle *pHandle, uint8 msgClass, uint8 msg, void *pBuffer, size_t offset, size_t packetSize)
327 {
328  SbgErrorCode errorCode = SBG_NO_ERROR;
329  SbgStreamBuffer outStreamBuffer;
330  SbgStreamBuffer inStreamBuffer;
331  uint8 outputBuffer[SBG_ECOM_MAX_PAYLOAD_SIZE];
332  uint8 inputBuffer[SBG_ECOM_MAX_PAYLOAD_SIZE];
333  uint16 transferCmd;
334  uint8 receivedMsgClass;
335  uint8 receivedMsg;
336  size_t rcvdOffset;
337  size_t inputSize;
338  uint32 i;
339 
340  //
341  // Check input parameters
342  //
343  SBG_ASSERT(pHandle);
344  SBG_ASSERT(pBuffer);
345  SBG_ASSERT(packetSize > 0);
346 
347  //
348  // Initialize stream buffer for output
349  //
350  sbgStreamBufferInitForWrite(&outStreamBuffer, outputBuffer, sizeof(outputBuffer));
351 
352  //
353  // Build payload: an SBG_ECOM_TRANSFER_DATA transfer command, the offset from the start of the transfer, the size of the packet the device must send
354  //
356  sbgStreamBufferWriteSizeT32LE(&outStreamBuffer, offset);
357  sbgStreamBufferWriteSizeT32LE(&outStreamBuffer, packetSize);
358 
359  //
360  // Send command (multiple times in case of failures)
361  //
362  for (i = 0; i < 3; i++)
363  {
364  //
365  // Send transfer payload encapsulated in an ECom protocol frame
366  //
367  errorCode = sbgEComProtocolSend(&pHandle->protocolHandle, msgClass, msg, sbgStreamBufferGetLinkedBuffer(&outStreamBuffer), sbgStreamBufferGetLength(&outStreamBuffer));
368 
369  if (errorCode == SBG_NO_ERROR)
370  {
371  //
372  // Wait for reponse, the device should respond with a ECOM_TRANSFER_DATA, the offset from the start of the transfer and the data payload
373  // If it can not provide the data, it will respond with a NACK
374  //
375  errorCode = sbgEComReceiveAnyCmd(pHandle, &receivedMsgClass, &receivedMsg, inputBuffer, &inputSize, SBG_ECOM_MAX_PAYLOAD_SIZE, pHandle->cmdDefaultTimeOut);
376 
377  if (errorCode == SBG_NO_ERROR)
378  {
379  //
380  // Test if this is the protocol command expected
381  //
382  if ((receivedMsgClass == msgClass) && (receivedMsg == msg))
383  {
384  //
385  // Initialize stream buffer for read on input buffer
386  //
387  sbgStreamBufferInitForRead(&inStreamBuffer, inputBuffer, inputSize);
388 
389  //
390  // Read response fields, first is the transfer command, second is the offset
391  //
392  transferCmd = sbgStreamBufferReadUint16LE(&inStreamBuffer);
393  rcvdOffset = sbgStreamBufferReadSizeT32LE(&inStreamBuffer);
394 
395  //
396  // Test that it's a SBG_ECOM_TRANSFER_DATA command
397  // The data is at the offset asked
398  // And the size corresponds
399  //
400  if ( (transferCmd == SBG_ECOM_TRANSFER_DATA) && (offset == rcvdOffset) && (packetSize == (inputSize - (sizeof(uint16) + sizeof(uint32)))) )
401  {
402  //
403  // Read then all the buffer
404  //
405  sbgStreamBufferReadBuffer(&inStreamBuffer, pBuffer, inputSize - (sizeof(uint16) + sizeof(uint32)));
406 
407  //
408  // No need for other trials, exit loop
409  //
410  break;
411  }
412  }
413  else
414  {
415  //
416  // Not the command expected
417  //
418  errorCode = SBG_ERROR;
419  }
420  }
421  }
422  }
423 
424  return errorCode;
425 }
426 
435 {
436  SbgErrorCode errorCode = SBG_NO_ERROR;
437  SbgStreamBuffer outStreamBuffer;
438  uint8 outputBuffer[sizeof(uint16)];
439  uint32 i;
440 
441  //
442  // Check input parameter
443  //
444  SBG_ASSERT(pHandle);
445 
446  //
447  // Initialize stream buffer for output
448  //
449  sbgStreamBufferInitForWrite(&outStreamBuffer, outputBuffer, sizeof(outStreamBuffer));
450 
451  //
452  // Build payload, only a SBG_ECOM_TRANSFER_END cmd
453  //
455 
456  //
457  // Send command (multiple times in case of failures)
458  //
459  for (i = 0; i < 3; i++)
460  {
461  //
462  // Send upload end payload encapsulated in a ECom protocol frame
463  //
464  errorCode = sbgEComProtocolSend(&pHandle->protocolHandle, msgClass, msg, sbgStreamBufferGetLinkedBuffer(&outStreamBuffer), sbgStreamBufferGetLength(&outStreamBuffer));
465 
466  if (errorCode == SBG_NO_ERROR)
467  {
468  //
469  // If the device is able to finish transfer sequence, it responds with an ACK
470  //
471  errorCode = sbgEComWaitForAck(pHandle, msgClass, msg, pHandle->cmdDefaultTimeOut);
472 
473  //
474  // Test if the response is positive from device
475  //
476  if (errorCode == SBG_NO_ERROR)
477  {
478  //
479  // No need for other trial, exit loop
480  //
481  break;
482  }
483  }
484  }
485 
486  return errorCode;
487 }
488 
489 //----------------------------------------------------------------------//
490 //- Public transfer method definitions -//
491 //----------------------------------------------------------------------//
492 
502 SbgErrorCode sbgEComTransferSend(SbgEComHandle *pHandle, uint8 msgClass, uint8 msg, const void *pBuffer, size_t size)
503 {
504  SbgErrorCode errorCode = SBG_NO_ERROR;
505  SbgSplitBuffer splitBuffer;
506  size_t i;
507 
508  //
509  // Check input parameters
510  //
511  SBG_ASSERT(pHandle);
512  SBG_ASSERT(pBuffer);
513  SBG_ASSERT(size > 0);
514 
515  //
516  // Make sure we are not trying to send a buffer that is too large
517  //
518  if (size <= SBG_ECOM_TRANSFER_MAX_SIZE)
519  {
520  //
521  // Initiate data transfer
522  //
523  errorCode = sbgEComTransferSendInit(pHandle, msgClass, msg, size);
524 
525  //
526  // Check that the transfer was correctly initialized
527  //
528  if (errorCode == SBG_NO_ERROR)
529  {
530  //
531  // Initialize split buffer that will help with splitting up provided buffer
532  //
533  sbgSplitBufferInitForRead(&splitBuffer, pBuffer, size, SBG_ECOM_TRANSFER_PACKET_SIZE);
534 
535  //
536  // Transfer sub buffer one by one
537  //
538  for (i = 0; i < sbgSplitBufferGetSubBufferNbr(&splitBuffer); i++)
539  {
540  //
541  // Send a sub buffer
542  //
543  errorCode = sbgEComTransferSendData(pHandle, msgClass, msg, sbgSplitBufferGetSubBuffer(&splitBuffer, i), sbgSplitBufferGetSubBufferOffset(&splitBuffer, i), sbgSplitBufferGetSubBufferSize(&splitBuffer, i));
544 
545  //
546  // Test if the sub buffer has been sent
547  //
548  if (errorCode != SBG_NO_ERROR)
549  {
550  //
551  // Unable to send a sub buffer, abort send operation.
552  //
553  break;
554  }
555  }
556 
557  //
558  // Test if any error occurred during data transfer
559  //
560  if (errorCode == SBG_NO_ERROR)
561  {
562  //
563  // End data transfer
564  //
565  errorCode = sbgEComTransferSendEnd(pHandle, msgClass, msg);
566  }
567  }
568  }
569  else
570  {
571  //
572  // Trying to send a buffer that is too large
573  //
574  errorCode = SBG_INVALID_PARAMETER;
575  }
576 
577  return errorCode;
578 }
579 
590 SbgErrorCode sbgEComTransferReceive(SbgEComHandle *pHandle, uint8 msgClass, uint8 msg, void *pBuffer, size_t *pActualSize, size_t bufferSize)
591 {
592  SbgErrorCode errorCode = SBG_NO_ERROR;
593  SbgSplitBuffer splitBuffer;
594  size_t transferSize;
595  size_t i;
596 
597  //
598  // Check input parameters
599  //
600  SBG_ASSERT(pHandle);
601  SBG_ASSERT(pBuffer);
602  SBG_ASSERT(pActualSize);
603  SBG_ASSERT(bufferSize > 0);
604 
605  //
606  // initiate data transfer
607  //
608  errorCode = sbgEComTransferReceiveInit(pHandle, msgClass, msg, &transferSize);
609 
610  //
611  // Make sure the receive transfer has been correctly initialized
612  //
613  if (errorCode == SBG_NO_ERROR)
614  {
615  //
616  // Test that the provided buffer is large enough to receive all data
617  //
618  if (transferSize <= bufferSize)
619  {
620  //
621  // Initialize Split buffer to help with sub buffer receive
622  //
623  sbgSplitBufferInitForWrite(&splitBuffer, pBuffer, transferSize, SBG_ECOM_TRANSFER_PACKET_SIZE);
624 
625  //
626  // Receive buffers one by one
627  //
628  for (i = 0; i < sbgSplitBufferGetSubBufferNbr(&splitBuffer); i++)
629  {
630  //
631  // Receive a sub buffer
632  //
633  errorCode = sbgEComTransferReceiveData(pHandle, msgClass, msg, sbgSplitBufferGetSubBuffer(&splitBuffer, i), sbgSplitBufferGetSubBufferOffset(&splitBuffer, i), sbgSplitBufferGetSubBufferSize(&splitBuffer, i));
634 
635  //
636  // Make sure that the sub buffer has been correctly received
637  //
638  if (errorCode != SBG_NO_ERROR)
639  {
640  //
641  // An error occurred, abort data transfer
642  //
643  break;
644  }
645  }
646 
647  //
648  // Test if any error occurred during transfer
649  //
650  if (errorCode == SBG_NO_ERROR)
651  {
652  //
653  // End data transfer
654  //
655  errorCode = sbgEComTransferReceiveEnd(pHandle, msgClass, msg);
656 
657  //
658  // Make sure that the transfer has been correctly ended
659  //
660  if (errorCode == SBG_NO_ERROR)
661  {
662  //
663  // Since the transfer was successful update output variable pActualSize
664  //
665  *pActualSize = transferSize;
666  }
667  }
668  }
669  else
670  {
671  //
672  // Provided buffer is too small
673  //
674  errorCode = SBG_INVALID_PARAMETER;
675  }
676  }
677 
678  return errorCode;
679 }
SBG_INLINE size_t sbgSplitBufferGetSubBufferNbr(const SbgSplitBuffer *pSplitBuffer)
SBG_INLINE SbgErrorCode sbgStreamBufferInitForRead(SbgStreamBuffer *pHandle, const void *pLinkedBuffer, size_t bufferSize)
SBG_INLINE size_t sbgStreamBufferReadSizeT32LE(SbgStreamBuffer *pHandle)
unsigned int uint32
Definition: sbgTypes.h:58
Helper methods used to handle a splittable buffer.
SbgErrorCode sbgEComReceiveAnyCmd(SbgEComHandle *pHandle, uint8 *pMsgClass, uint8 *pMsg, void *pData, size_t *pSize, size_t maxSize, uint32 timeOut)
Used to read/write data from/to a memory buffer stream.
SBG_INLINE SbgErrorCode sbgStreamBufferWriteSizeT32LE(SbgStreamBuffer *pHandle, size_t value)
SbgErrorCode sbgEComTransferSend(SbgEComHandle *pHandle, uint8 msgClass, uint8 msg, const void *pBuffer, size_t size)
SBG_INLINE size_t sbgSplitBufferGetSubBufferOffset(const SbgSplitBuffer *pSplitBuffer, size_t subBufferIdx)
SBG_INLINE uint16 sbgStreamBufferReadUint16LE(SbgStreamBuffer *pHandle)
SbgErrorCode sbgEComWaitForAck(SbgEComHandle *pHandle, uint8 msgClass, uint8 msg, uint32 timeOut)
Handle large send/receive transfer for specific ECom Protocol commands.
#define SBG_ECOM_TRANSFER_MAX_SIZE
SbgEComProtocol protocolHandle
Definition: sbgECom.h:82
SBG_INLINE SbgErrorCode sbgStreamBufferInitForWrite(SbgStreamBuffer *pHandle, void *pLinkedBuffer, size_t bufferSize)
SbgErrorCode sbgEComTransferSendEnd(SbgEComHandle *pHandle, uint8 msgClass, uint8 msg)
SbgErrorCode sbgEComTransferReceiveEnd(SbgEComHandle *pHandle, uint8 msgClass, uint8 msg)
#define SBG_ECOM_TRANSFER_PACKET_SIZE
SbgErrorCode sbgEComTransferReceiveInit(SbgEComHandle *pHandle, uint8 msgClass, uint8 msg, size_t *pSize)
SbgErrorCode sbgEComTransferSendData(SbgEComHandle *pHandle, uint8 msgClass, uint8 msg, const void *pBuffer, size_t offset, size_t packetSize)
SBG_INLINE SbgErrorCode sbgStreamBufferReadBuffer(SbgStreamBuffer *pHandle, void *pBuffer, size_t numBytesToRead)
SbgErrorCode sbgEComTransferReceiveData(SbgEComHandle *pHandle, uint8 msgClass, uint8 msg, void *pBuffer, size_t offset, size_t packetSize)
SbgErrorCode sbgEComProtocolSend(SbgEComProtocol *pHandle, uint8 msgClass, uint8 msg, const void *pData, size_t size)
SBG_INLINE void * sbgStreamBufferGetLinkedBuffer(SbgStreamBuffer *pHandle)
SBG_INLINE size_t sbgSplitBufferGetSubBufferSize(const SbgSplitBuffer *pSplitBuffer, size_t subBufferIdx)
SBG_INLINE void * sbgSplitBufferGetSubBuffer(const SbgSplitBuffer *pSplitBuffer, size_t subBufferIdx)
SBG_INLINE SbgErrorCode sbgStreamBufferWriteBuffer(SbgStreamBuffer *pHandle, const void *pBuffer, size_t numBytesToWrite)
SBG_INLINE void sbgSplitBufferInitForWrite(SbgSplitBuffer *pSplitBuffer, void *pBuffer, size_t bufferSize, size_t subBufferSize)
uint32 cmdDefaultTimeOut
Definition: sbgECom.h:87
SbgErrorCode sbgEComTransferSendInit(SbgEComHandle *pHandle, uint8 msgClass, uint8 msg, size_t size)
SBG_INLINE SbgErrorCode sbgStreamBufferWriteUint16LE(SbgStreamBuffer *pHandle, uint16 value)
unsigned char uint8
Definition: sbgTypes.h:56
#define SBG_ASSERT(expression)
Definition: sbgDebug.h:52
SBG_INLINE void sbgSplitBufferInitForRead(SbgSplitBuffer *pSplitBuffer, const void *pBuffer, size_t bufferSize, size_t subBufferSize)
SBG_INLINE size_t sbgStreamBufferGetLength(SbgStreamBuffer *pHandle)
unsigned short uint16
Definition: sbgTypes.h:57
#define SBG_ECOM_MAX_PAYLOAD_SIZE
enum _SbgErrorCode SbgErrorCode
SbgErrorCode sbgEComTransferReceive(SbgEComHandle *pHandle, uint8 msgClass, uint8 msg, void *pBuffer, size_t *pActualSize, size_t bufferSize)


sbg_driver
Author(s):
autogenerated on Sun Jan 27 2019 03:42:20