globals.c
Go to the documentation of this file.
1 /*
2 MIT LICENSE
3 
4 Copyright 2014-2019 Inertial Sense, Inc. - http://inertialsense.com
5 
6 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files(the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions :
7 
8 The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
9 
10 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT, IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
11 */
12 
13 #include <flash_efc.h>
15 #include "../../../hw-libs/drivers/d_flash.h"
16 #include "globals.h"
17 
18 
29 //uint32_t g_CANbaud_kbps = 500;
30 //uint32 g_can_receive_address = 0;
31 bool g_gpsTimeSync = false;
32 uint32_t g_comm_time_ms = 0;
33 bool g_loggerEnabled = false;
34 uint32_t g_uInsBootloaderEnableTimeMs = 0; // 0 = disabled
35 bool g_enRtosStats = 0;
36 
37 
38 void globals_init(void)
39 {
40  // Init Device Info struct
41  g_evbDevInfo.hardwareVer[0] = 2;
42  g_evbDevInfo.hardwareVer[1] = 0;
43  switch(g_hdw_detect)
44  {
45  case HDW_DETECT_VER_EVB_2_0_0: g_evbDevInfo.hardwareVer[2] = 0; break;
46  default: /* HDW_DETECT_VER_EVB_2_0_1 */ g_evbDevInfo.hardwareVer[2] = 1; break;
47  }
48  g_evbDevInfo.hardwareVer[3] = 0;
49 
50 // g_evbDevInfo.firmwareVer[0] = FIRMWARE_VERSION_CHAR0; // Major
51 // g_evbDevInfo.firmwareVer[1] = FIRMWARE_VERSION_CHAR1; // Minor
52 // g_evbDevInfo.firmwareVer[2] = FIRMWARE_VERSION_CHAR2; // Revision
53 // g_evbDevInfo.firmwareVer[3] = FIRMWARE_VERSION_CHAR3; // firmware specific revision
54 
55  g_evbDevInfo.protocolVer[0] = PROTOCOL_VERSION_CHAR0; // in com_manager.h
56  g_evbDevInfo.protocolVer[1] = PROTOCOL_VERSION_CHAR1;
57  g_evbDevInfo.protocolVer[2] = PROTOCOL_VERSION_CHAR2; // in data_sets.h
58  g_evbDevInfo.protocolVer[3] = PROTOCOL_VERSION_CHAR3;
59 
60 // g_evbDevInfo.repoRevision = REPO_HEAD_COUNT;
61 // g_evbDevInfo.buildNumber = BUILD_NUMBER;
62 
63 #if defined(DEBUG)
64  g_evbDevInfo.buildDate[0] = 'd'; // Debug
65 #else
66  g_evbDevInfo.buildDate[0] = 'r'; // Release
67 #endif
68 // g_evbDevInfo.buildDate[1] = BUILD_DATE_YEAR-2000;
69 // g_evbDevInfo.buildDate[2] = BUILD_DATE_MONTH;
70 // g_evbDevInfo.buildDate[3] = BUILD_DATE_DAY;
71 
72 // g_evbDevInfo.buildTime[0] = BUILD_TIME_HOUR;
73 // g_evbDevInfo.buildTime[1] = BUILD_TIME_MINUTE;
74 // g_evbDevInfo.buildTime[2] = BUILD_TIME_SECOND;
75 // g_evbDevInfo.buildTime[3] = (uint8_t)BUILD_TIME_MILLISECOND;
76 //
77  strncpy(g_evbDevInfo.manufacturer, "Inertial Sense INC", DEVINFO_MANUFACTURER_STRLEN);
78 }
79 
80 
81 void com_bridge_apply_preset_uins_com(evb_flash_cfg_t* cfg, uint8_t uinsComPort);
82 void com_bridge_apply_preset_uins_com(evb_flash_cfg_t* cfg, uint8_t uinsComPort)
83 {
84  if(uinsComPort >= EVB2_PORT_COUNT)
85  {
86  return;
87  }
88 
89  if(uinsComPort != EVB2_PORT_USB)
90  {
91  cfg->cbf[uinsComPort] |= (1<<EVB2_PORT_USB);
92  cfg->cbf[EVB2_PORT_USB] |= (1<<uinsComPort);
93  }
94 
95  if(uinsComPort != EVB2_PORT_SP330)
96  {
97  cfg->cbf[uinsComPort] |= (1<<EVB2_PORT_SP330);
98  cfg->cbf[EVB2_PORT_SP330] |= (1<<uinsComPort);
99  }
100 }
101 
102 
103 void com_bridge_apply_preset_uins_aux(evb_flash_cfg_t* cfg, uint8_t uinsAuxPort);
104 void com_bridge_apply_preset_uins_aux(evb_flash_cfg_t* cfg, uint8_t uinsAuxPort)
105 {
106 // if (cfg->uinsComPort == uinsAuxPort)
107 // { // Using UINS-AUX as primary communication link. Skip this section.
108 // return;
109 // }
110 
111  if (uinsAuxPort >= EVB2_PORT_COUNT)
112  { // Port disabled
113  return;
114  }
115 
116  // XRadio Forwarding
117  if (uinsAuxPort != EVB2_PORT_XRADIO)
118  {
119  switch(cfg->cbPreset)
120  {
124  cfg->cbf[uinsAuxPort] |= (1<<EVB2_PORT_XRADIO);
125  cfg->cbf[EVB2_PORT_XRADIO] |= (1<<uinsAuxPort);
126  }
127  }
128 
129  // XBee Forwarding
130  if ((uinsAuxPort != EVB2_PORT_XBEE) &&
132  {
133  cfg->cbf[uinsAuxPort] |= (1<<EVB2_PORT_XBEE);
134  cfg->cbf[EVB2_PORT_XBEE] |= (1<<uinsAuxPort);
135  }
136 
137  // WiFi Forwarding
138  if ((uinsAuxPort != EVB2_PORT_WIFI) &&
140  {
141  cfg->cbf[uinsAuxPort] |= (1<<EVB2_PORT_WIFI);
142  cfg->cbf[EVB2_PORT_WIFI] |= (1<<uinsAuxPort);
143  }
144 }
145 
146 
148 {
149  if(cfg->cbPreset==EVB2_CB_PRESET_NA)
150  { // Do nothing
151  return;
152  }
153 
154  // Comm Bridge
155  memset(cfg->cbf, 0, sizeof(cfg->cbf)); // Clear com bridge settings
156  switch(cfg->cbPreset)
157  {
158  case EVB2_CB_PRESET_RS232:
161  com_bridge_apply_preset_uins_com(cfg, cfg->uinsComPort); // UINS communication port used for SD logging.
162  com_bridge_apply_preset_uins_aux(cfg, cfg->uinsAuxPort); // UINS auxiliary port used for RTK corrections.
163  break;
164 
166  // SPI mode must be on port UINS1
167  cfg->cbf[EVB2_PORT_UINS1] |= (1<<EVB2_PORT_USB);
168  cfg->cbf[EVB2_PORT_USB] |= (1<<EVB2_PORT_UINS1);
169 
170  cfg->cbf[EVB2_PORT_UINS1] |= (1<<EVB2_PORT_SP330);
171  cfg->cbf[EVB2_PORT_SP330] |= (1<<EVB2_PORT_UINS1);
172  break;
173 
175  cfg->cbf[EVB2_PORT_USB] |= (1<<EVB2_PORT_XBEE);
176  cfg->cbf[EVB2_PORT_XBEE] |= (1<<EVB2_PORT_USB);
177 
178  cfg->cbf[EVB2_PORT_USB] |= (1<<EVB2_PORT_SP330);
179  cfg->cbf[EVB2_PORT_SP330] |= (1<<EVB2_PORT_USB);
180 
181  cfg->cbf[EVB2_PORT_USB] |= (1<<EVB2_PORT_GPIO_H8);
182  cfg->cbf[EVB2_PORT_GPIO_H8] |= (1<<EVB2_PORT_USB);
183 
184  cfg->cbf[EVB2_PORT_USB] |= (1<<EVB2_PORT_XRADIO);
185  cfg->cbf[EVB2_PORT_XRADIO] |= (1<<EVB2_PORT_USB);
186  break;
187 
189  cfg->cbf[EVB2_PORT_USB] |= (1<<EVB2_PORT_SP330);
190  cfg->cbf[EVB2_PORT_SP330] |= (1<<EVB2_PORT_USB);
191 
192  cfg->cbf[EVB2_PORT_USB] |= (1<<EVB2_PORT_GPIO_H8);
193  cfg->cbf[EVB2_PORT_GPIO_H8] |= (1<<EVB2_PORT_USB);
194 
195  cfg->cbf[EVB2_PORT_USB] |= (1<<EVB2_PORT_XRADIO);
196  cfg->cbf[EVB2_PORT_XRADIO] |= (1<<EVB2_PORT_USB);
197 
198 // cfg->cbf[EVB2_PORT_USB] |= (1<<EVB2_PORT_BLE);
199 // cfg->cbf[EVB2_PORT_BLE] |= (1<<EVB2_PORT_USB);
200  break;
201 
202 #ifdef CONF_BOARD_CAN1
203  case EVB2_CB_PRESET_CAN:
204  cfg->cbf[EVB2_PORT_UINS0] |= (1<<EVB2_PORT_USB);
205  cfg->cbf[EVB2_PORT_UINS0] |= (1<<EVB2_PORT_SP330);
206 
207  cfg->cbf[EVB2_PORT_USB] |= (1<<EVB2_PORT_UINS0);
208  cfg->cbf[EVB2_PORT_SP330] |= (1<<EVB2_PORT_UINS0);
209 
210  cfg->cbf[EVB2_PORT_UINS1] |= (1<<EVB2_PORT_CAN);
211  cfg->cbf[EVB2_PORT_CAN] |= (1<<EVB2_PORT_USB);
212  break;
213 #endif
214  }
215 
216  // Clear existing
217  cfg->cbOptions = 0;
218 
219  // Tri-state uINS
220  switch(cfg->cbPreset)
221  {
222  case EVB2_CB_PRESET_NA:
228  break;
229  }
230 
231  // SP330 Options
232  switch(cfg->cbPreset)
233  {
237  break;
238  }
239 
240  // XBee and WiFi Power Options
241  switch(cfg->cbPreset)
242  {
246  break;
247 
250  break;
251  }
252 
253  // Enable SPI example
254 #ifdef CONF_BOARD_SPI_UINS
255  switch(cfg->cbPreset)
256  {
259  break;
260  }
261 #endif
262 
263  // Enable CAN
264 #ifdef CONF_BOARD_CAN1
265  switch(cfg->cbPreset)
266  {
267  case EVB2_CB_PRESET_CAN:
269  break;
270  }
271 #endif
272 }
273 
274 
276 {
277  evb_flash_cfg_t defaults;
278  memset(&defaults, 0, sizeof(evb_flash_cfg_t));
279 
280  reset_config_defaults(&defaults);
281 
282  if (cfg->checksum != flashChecksum32(cfg, sizeof(evb_flash_cfg_t)) || cfg->key != defaults.key)
283  { // Reset to defaults
284  *cfg = defaults;
285  g_nvr_manage_config.flash_write_needed = true;
286  g_nvr_manage_config.flash_write_enable = true;
287  }
288 
289  // Disable cbPresets is necessary
290 #ifndef CONF_BOARD_SPI_ATWINC_WIFI
292 #endif
293 #ifndef CONF_BOARD_SPI_UINS
295 #endif
296 
297 }
298 
299 
300 void nvr_init(void)
301 {
302  // Initialize flash: 6 wait states for flash writing
303  uint32_t rc = flash_init(FLASH_ACCESS_MODE_128, 6);
304  configASSERT(rc == FLASH_RC_OK);
305 
306  g_flashCfg = &(g_userPage.g0.m);
307 
308  // Update RAM from FLASH
310 
311  // Reset to defaults if checksum or keys don't match
312  nvr_validate_config_integrity(g_flashCfg);
313 
314  // Ensure values are within valid ranges
315  error_check_config(g_flashCfg);
316 
317  // Disable flash writes. We require the user to initiate each write with this option.
318  g_nvr_manage_config.flash_write_enable = 0;
319 }
320 
321 
323 {
324  if(g_nvr_manage_config.flash_write_enable==0)
325  {
326  return;
327  }
328 
329  if (g_nvr_manage_config.flash_write_needed)
330  {
331  // Ensure flash config isn't larger than block
333 
334 #if 0
335 
336  // copy contents, set migration marker and re-calculate checksums inside critical section
338 
340 
341  // Copy config data, zero pad, update checksum
342  memcpy(copyOfRam, &g_userPage, sizeof(g_userPage));
343  memset((uint8_t*)copyOfRam + sizeof(g_userPage), 0, BOOTLOADER_FLASH_BLOCK_SIZE - sizeof(g_userPage));
344  copyOfRam->g0.m.checksum = flashChecksum32(copyOfRam, sizeof(evb_flash_cfg_t));
345 
346  // Update userPage config
347  memcpy(&g_userPage, copyOfRam, sizeof(g_userPage));
348 
350 
351  uint32_t address = BOOTLOADER_FLASH_CONFIG_BASE_ADDRESS;
352  uint32_t memsize = BOOTLOADER_FLASH_BLOCK_SIZE;
353 // uint32_t memsize = IFLASH_PAGE_SIZE;
354 
355 #if 0
356 
357  if (flash_unlock(address, address + memsize - 1, 0, 0) == FLASH_RC_OK)
358  {
359  if (flash_erase_page(address, IFLASH_ERASE_PAGES_16) == FLASH_RC_OK)
360  {
361  if (flash_write(address, &copyOfRam, memsize, 0) == FLASH_RC_OK)
362  {
363  if( flash_lock(address, address + memsize - 1, 0, 0) == FLASH_RC_OK)
364  { // indicate attempted flash write
365  g_nvr_manage_config.flash_write_count++;
366  }
367  }
368  }
369  }
370 
371 #else
372  // write to primary location
373  if( flash_update_block(address, copyOfRam, BOOTLOADER_FLASH_BLOCK_SIZE, 0) != FLASH_RC_OK)
374  {
375  // error
376  int j=0;
377  j++;
378  }
379 
380 #endif
381 
382  FREE(copyOfRam);
383 
384 #else
385 
387 
388  // calculate checksum
389  g_userPage.g0.m.size = sizeof(evb_flash_cfg_t);
390  g_userPage.g0.m.checksum = flashChecksum32(&g_userPage.g0.m, sizeof(evb_flash_cfg_t));
391 
392 // flash_update_block(BOOTLOADER_FLASH_CONFIG_BASE_ADDRESS, &g_userPage, sizeof(nvm_config_t), 0);
394 
396 
397 #endif
398 
399  g_nvr_manage_config.flash_write_needed = 0;
400  // Disable following each flash write. We require users to re-enable for each write.
401  g_nvr_manage_config.flash_write_enable = 0;
402  }
403 }
404 
405 
406 // Returns 0 on success
408 {
409  if(cfg == NULLPTR)
410  {
411  return -1;
412  }
413 
414  int failure = 0;
415 
416  if (cfg->radioPID > 9 ||
417  cfg->radioNID > 0x7FFF ||
418  cfg->radioPowerLevel > 2 )
419  {
420  failure = 1;
421  }
423  {
425  failure = 1;
426  }
428  {
430  failure = 1;
431  }
432 
433  return failure;
434 }
435 
436 
438 {
439  if (cfg == NULL)
440  return;
441 
442  memset(cfg, 0, sizeof(evb_flash_cfg_t));
443  cfg->size = sizeof(evb_flash_cfg_t);
444  cfg->key = 8; // increment key to force config to revert to defaults (overwrites customer's settings)
445 
447 
448  cfg->uinsComPort = EVB2_PORT_UINS0; // Default port for uINS communications and SD card logging
449  cfg->uinsAuxPort = EVB2_PORT_UINS0; // Default port for RTK corrections and wireless interface
450  cfg->portOptions = EVB2_PORT_OPTIONS_DEFAULT; // Enable radio RTK filter
451  cfg->h3sp330BaudRate = 921600;
452  cfg->h4xRadioBaudRate = 115200;
453  cfg->h8gpioBaudRate = 921600;
454 
455  cfg->radioPID = 2; // 0x0 to 0x9
456  cfg->radioNID = 72; // 0x0 to 0x7FFF
457  cfg->radioPowerLevel = 0; // 0=20dBm use low power so radio can run from USB supply. 1=27dBm and 2=30dBm are too powerful for USB supply.
458 
459  cfg->server[0].ipAddr.u32 = nmi_inet_addr((void*)"69.167.49.43");
460  cfg->server[0].port = 7778;
461  cfg->server[1].ipAddr.u32 = nmi_inet_addr((void*)"192.168.1.144");
462  cfg->server[1].port = 2000;
463 // cfg->encoderTickToWheelRad = 0.0179999f; // Husqvarna lawnmower
464  cfg->encoderTickToWheelRad = 0.054164998f; // Husqvarna lawnmower
465 
467 
468  cfg->checksum = flashChecksum32(cfg, sizeof(evb_flash_cfg_t));
469 }
uint32_t bits
Definition: data_sets.h:3154
Operation OK.
Definition: flash_efc.h:64
nvm_config_t g_userPage
Definition: globals.c:24
bool g_gpsTimeSync
Definition: globals.c:31
uint32_t h3sp330BaudRate
Definition: data_sets.h:3193
uint32_t cbf[EVB2_PORT_COUNT]
Definition: data_sets.h:3148
#define FLASH_ACCESS_MODE_128
Definition: flash_efc.h:93
void nvr_validate_config_integrity(evb_flash_cfg_t *cfg)
Definition: globals.c:275
#define DEVINFO_MANUFACTURER_STRLEN
Definition: data_sets.h:149
void reset_config_defaults(evb_flash_cfg_t *cfg)
Definition: globals.c:437
uint32_t radioNID
Definition: data_sets.h:3160
uint32_t flash_init(uint32_t ul_mode, uint32_t ul_fws)
Initialize the flash service.
Definition: flash_efc.c:313
Embedded Flash service for SAM.
void globals_init(void)
Definition: globals.c:38
bool g_enRtosStats
Definition: globals.c:35
debug_array_t g_debug
Definition: globals.c:26
#define NUM_WIFI_PRESETS
Definition: data_sets.h:3121
#define PROTOCOL_VERSION_CHAR1
Definition: ISComm.h:123
#define PROTOCOL_VERSION_CHAR3
Definition: data_sets.h:157
#define NULL
Definition: nm_bsp.h:52
uint32_t h4xRadioBaudRate
Definition: data_sets.h:3196
NMI_API uint32 nmi_inet_addr(char *pcIpAddr)
Definition: socket.c:983
evb_rtos_info_t g_rtos
Definition: globals.c:27
uint32_t flash_write_count
Definition: globals.h:52
uint32_t port
Definition: data_sets.h:3106
int error_check_config(evb_flash_cfg_t *cfg)
Definition: globals.c:407
wheel_encoder_t g_wheelEncoder
Definition: globals.c:20
uint32_t flash_lock(uint32_t ul_start, uint32_t ul_end, uint32_t *pul_actual_start, uint32_t *pul_actual_end)
Lock all the regions in the given address range. The actual lock range is reported through two output...
Definition: flash_efc.c:703
void com_bridge_apply_preset_uins_aux(evb_flash_cfg_t *cfg, uint8_t uinsAuxPort)
Definition: globals.c:104
uint32_t flash_write_needed
Definition: globals.h:51
uint32_t checksum
Definition: data_sets.h:3136
uint32_t u32
Definition: data_sets.h:3101
uint32_t radioPowerLevel
Definition: data_sets.h:3163
#define NULLPTR
Definition: ISConstants.h:426
uint32_t flash_update_block(uint32_t address, const void *newData, int dataSize, int noPageErase)
Definition: d_flash.c:23
float encoderTickToWheelRad
Definition: data_sets.h:3172
void nvr_slow_maintenance(void)
Definition: globals.c:322
void com_bridge_apply_preset(evb_flash_cfg_t *cfg)
Definition: globals.c:147
#define FREE(m)
Definition: ISConstants.h:138
#define EVB2_CB_PRESET_DEFAULT
Definition: data_sets.h:3236
#define EVB_CFG_BITS_SET_IDX_SERVER(bits, idx)
Definition: data_sets.h:3123
#define configASSERT(x)
Definition: FreeRTOS.h:235
#define END_CRITICAL_SECTION
Definition: rtos.h:34
evb_status_t g_status
Definition: globals.c:21
#define BOOTLOADER_FLASH_BLOCK_SIZE
void nvr_init(void)
Definition: globals.c:300
uint32_t cbOptions
Definition: data_sets.h:3151
uint8_t hardwareVer[4]
Definition: data_sets.h:445
uint8_t cbPreset
Definition: data_sets.h:3142
uint32_t g_uInsBootloaderEnableTimeMs
Definition: globals.c:34
uint32_t g_comm_time_ms
Definition: globals.c:32
#define HDW_DETECT_VER_EVB_2_0_0
Definition: user_board.h:78
uint8_t buildDate[4]
Definition: data_sets.h:463
uint32_t flashChecksum32(const void *data, int size)
Definition: data_sets.c:557
char manufacturer[DEVINFO_MANUFACTURER_STRLEN]
Definition: data_sets.h:460
#define MALLOC(m)
Definition: ISConstants.h:136
dev_info_t g_evbDevInfo
Definition: globals.c:19
uint32_t flash_write(uint32_t ul_address, const void *p_buffer, uint32_t ul_size, uint32_t ul_erase_flag)
Write a data buffer on flash.
Definition: flash_efc.c:607
#define BEGIN_CRITICAL_SECTION
Definition: rtos.h:33
date_time_t g_gps_date_time
Definition: globals.c:28
uint32_t key
Definition: data_sets.h:3139
union evb_server_t::@40 ipAddr
uint8_t g_hdw_detect
evb_server_t server[NUM_WIFI_PRESETS]
Definition: data_sets.h:3169
#define EVB_CFG_BITS_IDX_SERVER(bits)
Definition: data_sets.h:3125
evb_flash_cfg_t * g_flashCfg
Definition: globals.c:22
uint32_t size
Definition: data_sets.h:3133
uint32_t flash_write_enable
Definition: globals.h:53
#define STATIC_ASSERT(exp)
Definition: ISConstants.h:416
bool g_loggerEnabled
Definition: globals.c:33
void com_bridge_apply_preset_uins_com(evb_flash_cfg_t *cfg, uint8_t uinsComPort)
Definition: globals.c:82
uint8_t protocolVer[4]
Definition: data_sets.h:454
PUSH_PACK_1 struct PACKED nvm_config_t
uint32_t portOptions
Definition: data_sets.h:3190
nvr_manage_t g_nvr_manage_config
Definition: globals.c:23
BSD compatible socket interface.
uint32_t radioPID
Definition: data_sets.h:3157
uint8_t uinsComPort
Definition: data_sets.h:3181
#define PROTOCOL_VERSION_CHAR2
Definition: data_sets.h:156
evb_msg_t g_msg
Definition: globals.c:25
#define PROTOCOL_VERSION_CHAR0
Definition: ISComm.h:119
#define EVB_CFG_BITS_IDX_WIFI(bits)
Definition: data_sets.h:3124
#define EVB_CFG_BITS_SET_IDX_WIFI(bits, idx)
Definition: data_sets.h:3122
uint32_t flash_unlock(uint32_t ul_start, uint32_t ul_end, uint32_t *pul_actual_start, uint32_t *pul_actual_end)
Unlock all the regions in the given address range. The actual unlock range is reported through two ou...
Definition: flash_efc.c:752
uint32_t h8gpioBaudRate
Definition: data_sets.h:3199
uint8_t uinsAuxPort
Definition: data_sets.h:3184
#define BOOTLOADER_FLASH_CONFIG_BASE_ADDRESS


inertial_sense_ros
Author(s):
autogenerated on Sun Feb 28 2021 03:17:57