Data logger: Sensors -> Barometer & temperature (BMP180), Humidity & temp. (RHT03), Sunshine (Cds): Display -> 20 chracters x 4 lines: Strage -> EEPROM (AT24C1024): Special functions -> Enter sleep mode to save current, reading the logging data via serial line
Dependencies: AT24C1024 BMP180 M41T62 RHT03 TextLCD WakeUp mbed
Fork of LPC1114_barometer_with_data_logging by
mon.cpp
00001 /* 00002 * mbed Application program 00003 * Data logging & Monitor program for only LPC1114FN28 00004 * 00005 * Copyright (c) 2010-2014 Kenji Arai / JH1PJL 00006 * http://www.page.sannet.ne.jp/kenjia/index.html 00007 * http://mbed.org/users/kenjiArai/ 00008 * Created: May 15th, 2010 00009 * Spareted: June 25th, 2014 mon() & mon_hw() 00010 * Revised: August 12th, 2014 00011 * 00012 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 00013 * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE 00014 * AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 00015 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 00016 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 00017 */ 00018 00019 // Include --------------------------------------------------------------------------------------- 00020 #include "mbed.h" 00021 #include "m41t62_rtc.h" // Own lib. / RTC control 00022 #include "AT24C1024.h" // Own lib. / EEPROM control 00023 #include "dt_log.h" 00024 00025 // Object ---------------------------------------------------------------------------------------- 00026 Serial pc(dp16,dp15); 00027 extern I2C xi2c(dp5,dp27); // SDA, SCL 00028 extern M41T62 xm41t62(xi2c); // STmicro RTC(M41T62) 00029 AT24C1024 at24c1024(xi2c); // Atmel 1Mbit EE-PROM 00030 00031 // Definition ------------------------------------------------------------------------------------ 00032 #define BAUD(x) pc.baud(x) 00033 #define GETC(x) pc.getc(x) 00034 #define PUTC(x) pc.putc(x) 00035 #define PRINTF(...) pc.printf(__VA_ARGS__) 00036 #define READABLE(x) pc.readable(x) 00037 00038 // EEPROM 00039 #define EEP_TOP 0x0 00040 00041 // RAM ------------------------------------------------------------------------------------------- 00042 char linebuf[64]; 00043 int buf_size = sizeof(linebuf); 00044 00045 // for EEPROM control 00046 int16_t read_pointer; 00047 00048 typedef struct { 00049 uint16_t head; 00050 uint16_t tail; 00051 } ring_t; 00052 00053 union _inf{ 00054 uint8_t buf_pointer[PTR_SIZE]; 00055 ring_t log_inf; 00056 }inf; 00057 00058 typedef struct { 00059 uint32_t time; 00060 uint16_t vcc; 00061 uint16_t baro; 00062 int16_t b_temp; 00063 uint16_t humi; 00064 int16_t h_temp; 00065 uint16_t lux; 00066 }one_log; // 16 bytes total 00067 00068 union _one{ 00069 uint8_t bf[PKT_SIZE]; 00070 one_log lg; 00071 }one; 00072 00073 extern float baro; 00074 extern float baro_temp; 00075 extern float cal_vcc; 00076 extern float lux; 00077 extern float humidity; 00078 extern float humidity_temp; 00079 extern float lux; 00080 00081 // ROM / Constant data --------------------------------------------------------------------------- 00082 static char *const mon_msg = "Monitor for mbed system, created on "__DATE__""; 00083 00084 // $, 2014/6/29,12:43:16,3.293,1004.5,+29.3,45.8,+29.2,1234,* 00085 char *const log_head = "$,YYYY/MM/DD,HH:MM:SS,Vcc ,Press ,Temp ,Humi,Temp ,Lux ,*"; 00086 char *const msg_emty = "Data empty"; 00087 char *const msg_end = "\r\nreach to end"; 00088 00089 // Function prototypes --------------------------------------------------------------------------- 00090 extern void mon_hw(void); 00091 00092 //------------------------------------------------------------------------------------------------- 00093 // Control Program 00094 //------------------------------------------------------------------------------------------------- 00095 // Put \r\n 00096 void put_rn ( void ){ PUTC('\r'); PUTC('\n');} 00097 00098 // Put \r 00099 void put_r ( void ){ PUTC('\r');} 00100 00101 // Put ", " 00102 void put_lin ( void ){ PRINTF(", ");} 00103 00104 // Put space n 00105 void put_spc( uint8_t n){ for(;n > 0; n--){ PUTC(' '); }} 00106 00107 // Change string -> integer 00108 int xatoi (char **str, unsigned long *res){ 00109 unsigned long val; 00110 unsigned char c, radix, s = 0; 00111 00112 while ((c = **str) == ' ') (*str)++; 00113 if (c == '-') { 00114 s = 1; 00115 c = *(++(*str)); 00116 } 00117 if (c == '0') { 00118 c = *(++(*str)); 00119 if (c <= ' ') { *res = 0; return 1; } 00120 if (c == 'x') { 00121 radix = 16; 00122 c = *(++(*str)); 00123 } else { 00124 if (c == 'b') { 00125 radix = 2; 00126 c = *(++(*str)); 00127 } else { 00128 if ((c >= '0')&&(c <= '9')){ radix = 8; 00129 } else { return 0;} 00130 } 00131 } 00132 } else { 00133 if ((c < '1')||(c > '9')){ return 0;} 00134 radix = 10; 00135 } 00136 val = 0; 00137 while (c > ' ') { 00138 if (c >= 'a') c -= 0x20; 00139 c -= '0'; 00140 if (c >= 17) { 00141 c -= 7; 00142 if (c <= 9) return 0; 00143 } 00144 if (c >= radix) return 0; 00145 val = val * radix + c; 00146 c = *(++(*str)); 00147 } 00148 if (s) val = -val; 00149 *res = val; 00150 return 1; 00151 } 00152 00153 //------------------------------------------------------------------------------------------------- 00154 // Data Logging / Save into EEPROM 00155 //------------------------------------------------------------------------------------------------- 00156 /* 00157 head = H, tail =T 00158 state 1: H=1(RING_TOP),T=1(RING_TOP) -> just after Clear command 00159 state 2: H=1,T=n -> n = 2 to RING_TAIL-1 (not filled yet) 00160 state 3: H=1,T=RING_TAIL -> need to check!!!! (just filled) 00161 state 4: H=2,T=1(RING_TOP) -> start ringed state 00162 state 5: H=n,T=n-1 -> n = 2 to RING_TAIL-1 (ringed) 00163 state 6: H=RING_TAIL,T=RING_TAIL-1 -> need to check!!!!! 00164 state 7: H=1(RING_TOP),T=RING_TAIL -> need to check!!!!! 00165 state 8: same as "state 5" 00166 -> Need to check state 3,6,7 00167 */ 00168 // Make one data packet data structure 00169 void dtlog_data_pack(void){ 00170 struct tm t; 00171 00172 xm41t62.read_rtc_std(&t); 00173 one.lg.time = mktime(&t); 00174 one.lg.vcc = (uint16_t)(cal_vcc * 1000); 00175 one.lg.baro = (uint16_t)(baro * 10); 00176 one.lg.b_temp = (int16_t)(baro_temp * 10); 00177 one.lg.humi = (uint16_t)(humidity * 10); 00178 one.lg.h_temp = (int16_t)(humidity_temp * 10); 00179 one.lg.lux = (uint16_t)lux; 00180 } 00181 00182 // Print one packet as normalized data 00183 void print_one_block_data(void){ 00184 struct tm *t; 00185 time_t seconds; 00186 uint16_t dt0; 00187 int16_t dt1; 00188 00189 put_rn(); 00190 PUTC( '$' ); 00191 //--- Time 00192 seconds = one.lg.time; 00193 t = localtime(&seconds); 00194 PRINTF(",%04d/%02d/%02d,%02d:%02d:%02d,", 00195 t->tm_year + 1900, t->tm_mon + 1, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec); 00196 //--- Vcc 00197 dt0 = one.lg.vcc; 00198 PRINTF("%01d.%03d,", dt0/1000, dt0%1000); 00199 //--- Pressure 00200 dt0 = one.lg.baro; 00201 PRINTF("%04d.%01d,", dt0/10, dt0%10 ); 00202 //--- Temp. 00203 dt1 = one.lg.b_temp; 00204 PRINTF("%\+-03d.%01d,", dt1/10, abs(dt1)%10 ); 00205 //--- Humidity 00206 dt0 = one.lg.humi; 00207 PRINTF("%02d.%01d,", dt0/10, dt0%10 ); 00208 //--- Temp. 00209 dt1 = one.lg.h_temp; 00210 PRINTF("%\+-03d.%01d,", dt1/10, abs(dt1)%10 ); 00211 //--- Lux 00212 dt0 = one.lg.lux; 00213 PRINTF("%05d,*", dt0); 00214 } 00215 00216 // Read buffer pointer 00217 static void dtlog_pointer_read(void){ 00218 uint8_t i; 00219 uint8_t *addr; 00220 00221 /* Read EEPROM and save to buf_pointer[] */ 00222 for ( i = 0; i < PTR_SIZE; i++ ){ 00223 addr = (uint8_t *)(EEP_TOP + i); 00224 inf.buf_pointer[i] = at24c1024.read((int)addr); 00225 wait(0.002); 00226 } 00227 } 00228 00229 // Write one packet 00230 void dtlog_one_write(void){ 00231 uint8_t i; 00232 uint8_t *addr; 00233 00234 // Read EEPROM buffer pointer to RAM 00235 for ( i = 0; i < PTR_SIZE; i++ ){ 00236 addr = (uint8_t *)(EEP_TOP + i); 00237 inf.buf_pointer[i] = at24c1024.read((int)addr); 00238 wait(0.002); 00239 } 00240 // Write data_pack[] into EEPROM 00241 for (i = 0; i < PKT_SIZE; i++){ 00242 addr = (uint8_t *)(EEP_TOP + (inf.log_inf.tail * PTR_SIZE) + i); 00243 at24c1024.write((int)addr, one.bf[i]); 00244 wait(0.008); 00245 } 00246 // Increment buffer pointer in RAM 00247 if (inf.log_inf.head == RING_TOP){ // check state 1,2,3 00248 if (inf.log_inf.tail == RING_TAIL){ // check state 3 00249 inf.log_inf.tail = RING_TOP; // set state 4 00250 inf.log_inf.head = 2; // missing one oldest data 00251 } else { 00252 inf.log_inf.tail++; // set state 2 00253 } 00254 } else { // check state 4,5,6,7 00255 if (inf.log_inf.head == RING_TAIL){ // check state 6 00256 inf.log_inf.head = RING_TOP; // set state 7 00257 inf.log_inf.tail = RING_TAIL; 00258 } else if (inf.log_inf.tail == inf.log_inf.head - 1){ // check state 4,5 00259 ++inf.log_inf.tail; // continue state 5 00260 ++inf.log_inf.head; 00261 } 00262 } 00263 // Write buffer pointer into EEPROM 00264 for (i = 0; i < PTR_SIZE; i++){ 00265 addr = (uint8_t *)(EEP_TOP + i); 00266 at24c1024.write((int)addr, inf.buf_pointer[i]); 00267 wait(0.008); 00268 } 00269 } 00270 00271 // Read some block from buffer 00272 void dtlog_block_read(int16_t *pt, uint16_t n){ 00273 uint8_t i; 00274 uint8_t *addr; 00275 uint16_t num; 00276 00277 dtlog_pointer_read(); 00278 if (inf.log_inf.tail == inf.log_inf.head){ // Check pointer 00279 PRINTF(msg_emty); 00280 put_rn(); 00281 return; 00282 } 00283 PRINTF("Head:%d, Tail:%d, Start pointer:%d, Number of data:%d\r\n", 00284 inf.log_inf.head, inf.log_inf.tail, *pt, n); 00285 PRINTF( log_head ); 00286 for (num = 0; num < n; num++){ 00287 /* Read EEPROM and save to data_pack[] */ 00288 for (i = 0; i < PKT_SIZE; i++){ 00289 addr = (uint8_t *)(EEP_TOP + (*pt * PTR_SIZE) + i); 00290 one.bf[i] =at24c1024.read((int)addr); 00291 wait(0.002); 00292 } 00293 print_one_block_data(); 00294 if (READABLE()){ GETC(); break;} 00295 wait(0.001); 00296 if (inf.log_inf.head == RING_TOP){ // check state 1,2,3 00297 *pt += 1; 00298 if (*pt >= inf.log_inf.tail){ // check state 2,3 00299 PRINTF(msg_end); 00300 break; 00301 } 00302 } else { // state 4,5,6,7 00303 if (inf.log_inf.head == RING_TAIL){ // check state 6 00304 if (inf.log_inf.tail == RING_TAIL -1){ // check state 6 00305 if (*pt == RING_TAIL){ // same as :pt += 1 00306 *pt = RING_TOP; 00307 } else { // next read 00308 *pt += 1; 00309 if (*pt >= inf.log_inf.tail){ 00310 PRINTF(msg_end); 00311 break; 00312 } 00313 } 00314 } 00315 } else if (inf.log_inf.tail == inf.log_inf.head - 1){ // check state 5 00316 *pt += 1; 00317 if (*pt > RING_TAIL){ // same as :pt += 1 00318 *pt = RING_TOP; 00319 } else if (*pt == inf.log_inf.tail){ // reach to end 00320 PRINTF(msg_end); 00321 break; 00322 } 00323 } 00324 } 00325 } 00326 put_rn(); 00327 } 00328 00329 // Clear all buffer 00330 void dtlog_clear_all_buff(void){ 00331 uint8_t i; 00332 uint8_t *addr; 00333 00334 /* Set initial data */ 00335 inf.log_inf.head = inf.log_inf.tail = RING_TOP; 00336 /* Write buffer pointer */ 00337 for (i = 0; i < PTR_SIZE; i++){ 00338 addr = (uint8_t *)(EEP_TOP + i); 00339 at24c1024.write((int)addr, inf.buf_pointer[i]); 00340 wait(0.008); 00341 } 00342 } 00343 00344 // EEPROM buffer occupation 00345 uint16_t dtlog_buf_occupation(void){ 00346 uint16_t i = 0; 00347 uint16_t dt = 0; 00348 uint8_t *addr; 00349 00350 // Read EEPROM buffer pointer to RAM 00351 for ( i = 0; i < PTR_SIZE; i++ ){ 00352 addr = (uint8_t *)(EEP_TOP + i); 00353 inf.buf_pointer[i] = at24c1024.read((int)addr); 00354 wait(0.002); 00355 } 00356 // check buffer pointer 00357 if (inf.log_inf.head == inf.log_inf.tail){ 00358 PRINTF(msg_emty); 00359 put_rn(); 00360 return 0; 00361 } 00362 if (inf.log_inf.head == RING_TOP){ // check state 1,2,3 00363 dt = inf.log_inf.tail - inf.log_inf.head; 00364 } else { // state 4,5,6,7 00365 if (inf.log_inf.head == RING_TAIL){ // check state 6 00366 if (inf.log_inf.tail == RING_TAIL - 1){ // check state 6 00367 dt = inf.log_inf.tail - RING_TOP + 1; 00368 } 00369 } else if (inf.log_inf.tail == inf.log_inf.head - 1){ // check state 4,5 00370 dt = RING_TAIL; 00371 } else { // error 00372 dt = 0; 00373 } 00374 } 00375 return dt; 00376 } 00377 00378 // Read block number 00379 void dtlog_num_of_block(void){ 00380 uint16_t dt; 00381 00382 dt = dtlog_buf_occupation(); 00383 if (dt == 0){ 00384 PRINTF(msg_emty); 00385 } else { 00386 PRINTF("Number of data = %d", dt); 00387 put_rn(); 00388 dt = (uint16_t)(((uint32_t)dt * 1000 )/ (BLK_NO - 2)); 00389 PRINTF("EEPROM Occupation = %d.%01d%%", dt / 10, dt % 10); 00390 } 00391 put_rn(); 00392 } 00393 00394 //------------------------------------------------------------------------------------------------- 00395 // Monitor 00396 //------------------------------------------------------------------------------------------------- 00397 // Help Massage 00398 void msg_hlp (void){ 00399 PRINTF(mon_msg); put_rn(); 00400 PRINTF("d - Data logger"); put_rn(); 00401 PRINTF("t - Check and set RTC"); put_rn(); 00402 #if 0 00403 PRINTF("x - Goto HW monitor"); put_rn(); 00404 #endif 00405 PRINTF("q - Return to main"); put_rn(); 00406 } 00407 00408 // Get key input data 00409 void get_line (char *buff, int len){ 00410 char c; 00411 int idx = 0; 00412 00413 for (;;) { 00414 c = GETC(); 00415 // Added by Kenji Arai / JH1PJL May 9th, 2010 00416 if (c == '\r') { 00417 buff[idx++] = c; 00418 break; 00419 } 00420 if ((c == '\b') && idx) { 00421 idx--; 00422 PUTC(c); 00423 PUTC(' '); 00424 PUTC(c); 00425 } 00426 if (((uint8_t)c >= ' ') && (idx < len - 1)) { 00427 buff[idx++] = c; 00428 PUTC(c); 00429 } 00430 } 00431 buff[idx] = 0; 00432 PUTC('\n'); 00433 } 00434 00435 00436 // RTC related subroutines 00437 void chk_and_set_time(char *ptr){ 00438 unsigned long p1; 00439 struct tm t; 00440 00441 if (xatoi(&ptr, &p1)){ 00442 t.tm_year = (uint8_t)p1 + 100; 00443 PRINTF("Year:%d ",p1); 00444 xatoi( &ptr, &p1 ); 00445 t.tm_mon = (uint8_t)p1 - 1; 00446 PRINTF("Month:%d ",p1); 00447 xatoi( &ptr, &p1 ); 00448 t.tm_mday = (uint8_t)p1; 00449 PRINTF("Day:%d ",p1); 00450 xatoi( &ptr, &p1 ); 00451 t.tm_hour = (uint8_t)p1; 00452 PRINTF("Hour:%d ",p1); 00453 xatoi( &ptr, &p1 ); 00454 t.tm_min = (uint8_t)p1; 00455 PRINTF("Min:%d ",p1); 00456 xatoi( &ptr, &p1 ); 00457 t.tm_sec = (uint8_t)p1; 00458 PRINTF("Sec: %d \r\n",p1); 00459 xm41t62.write_rtc_std(&t); 00460 } 00461 xm41t62.read_rtc_std(&t); 00462 // Show Time with several example 00463 // ex.1 00464 PRINTF("Date: %04d/%02d/%02d, %02d:%02d:%02d\r\n", 00465 t.tm_year + 1900, t.tm_mon + 1, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec); 00466 #if 0 00467 time_t seconds; 00468 char buf[40]; 00469 00470 seconds = mktime(&t); 00471 // ex.2 00472 strftime(buf, 40, "%x %X", localtime(&seconds)); 00473 PRINTF("Date: %s\r\n", buf); 00474 // ex.3 00475 strftime(buf, 40, "%I:%M:%S %p (%Y/%m/%d)", localtime(&seconds)); 00476 PRINTF("Date: %s\r\n", buf); 00477 // ex.4 00478 strftime(buf, 40, "%B %d,'%y, %H:%M:%S", localtime(&seconds)); 00479 PRINTF("Date: %s\r\n", buf); 00480 #endif 00481 } 00482 00483 // Data Logger / Check status and Output data 00484 static void data_logger(char *ptr){ 00485 char c; 00486 unsigned long dt; 00487 uint16_t n; 00488 char *const Msg = "Data Logger Mode, ?[Help]"; 00489 00490 PRINTF(Msg); 00491 put_rn(); 00492 /* Get EEPROM resource */ 00493 dtlog_pointer_read(); 00494 dt = inf.log_inf.head; 00495 while (1){ 00496 /* Get EEPROM resource */ 00497 dtlog_pointer_read(); 00498 PRINTF("DL>"); 00499 ptr = linebuf; 00500 get_line(ptr, buf_size); 00501 switch (*ptr++) { 00502 case 'a' : 00503 put_r(); 00504 read_pointer = inf.log_inf.head; 00505 n = dtlog_buf_occupation(); 00506 dtlog_block_read(&read_pointer, n); 00507 break; 00508 case 'c' : // Clear data 00509 put_r(); 00510 PRINTF("Delete all data?"); 00511 put_rn(); 00512 PRINTF("Enter y/n (n-cancel)"); 00513 put_rn(); 00514 c = GETC(); 00515 PUTC(c); 00516 put_rn(); 00517 if (c == 'y'){ 00518 PRINTF("Cleared all logging data"); 00519 dtlog_clear_all_buff(); 00520 } else { 00521 PRINTF("Canceled"); 00522 } 00523 put_rn(); 00524 break; 00525 case 'd' : // d <pointer> [<count>] - Dump buffer 00526 put_r(); 00527 if (xatoi(&ptr, &dt)){ read_pointer = (uint16_t)dt; 00528 } else { read_pointer = inf.log_inf.head; } 00529 if (xatoi(&ptr, &dt)){ n = (uint8_t)dt; 00530 } else { n = BLK_SIZE; } 00531 if (read_pointer == 0){ read_pointer = 1;} 00532 dtlog_block_read(&read_pointer, n); 00533 break; 00534 case 0x0d : // CR 00535 put_r(); 00536 dtlog_block_read(&read_pointer, BLK_SIZE); 00537 break; 00538 case 'b' : // Back 00539 put_r(); 00540 read_pointer -= (BLK_SIZE * 2); 00541 if (read_pointer <= 0){ read_pointer = 1;} 00542 dtlog_block_read(&read_pointer, n); 00543 break; 00544 case 'n' : 00545 case 's' : // Status 00546 put_r(); 00547 dtlog_num_of_block(); 00548 break; 00549 case 'q' : // exit 00550 linebuf[0] = 0; 00551 return; 00552 case '?' : 00553 put_r(); 00554 PRINTF("d - <pointer> [<count>] Dump one block data"); put_rn(); 00555 PRINTF("a - Dump all log data"); put_rn(); 00556 PRINTF("c - Clear log data"); put_rn(); 00557 PRINTF("s - Logger status"); put_rn(); 00558 PRINTF("q - Exit DL mode"); put_rn(); 00559 break; 00560 default: 00561 put_r(); 00562 PUTC('?'); 00563 put_rn(); 00564 break; 00565 } 00566 } 00567 } 00568 00569 // ---------- Program starts here! --------------------------------------------------------------- 00570 int mon(void) { 00571 char *ptr; 00572 00573 BAUD(9600); 00574 put_rn(); 00575 put_rn(); 00576 PRINTF("%s [Help:'?' key]", mon_msg); 00577 put_rn(); 00578 for (;;) { 00579 put_r(); 00580 PUTC('>'); 00581 ptr = linebuf; 00582 get_line(ptr, sizeof(linebuf)); 00583 switch (*ptr++) { 00584 //--------------------------------------------------------------------------------- 00585 // check and set RTC 00586 //--------------------------------------------------------------------------------- 00587 case 't' : 00588 put_r(); 00589 chk_and_set_time(ptr); 00590 break; 00591 //--------------------------------------------------------------------------------- 00592 // check EEPROM status 00593 //--------------------------------------------------------------------------------- 00594 case 'd' : 00595 put_r(); 00596 data_logger(ptr); 00597 break; 00598 //--------------------------------------------------------------------------------- 00599 // help 00600 //--------------------------------------------------------------------------------- 00601 case '?' : 00602 put_r(); 00603 msg_hlp(); 00604 break; 00605 #if 0 00606 //--------------------------------------------------------------------------------- 00607 // Go to special command 00608 //--------------------------------------------------------------------------------- 00609 case 'x' : // 00610 mon_hw(); 00611 PRINTF("->Came back monitor\r\n"); 00612 break; 00613 #endif 00614 //--------------------------------------------------------------------------------- 00615 // Go back to main() 00616 //--------------------------------------------------------------------------------- 00617 case 'q' : // Quit 00618 PRINTF("\rReturn to main\r\n"); 00619 PRINTF("cannot control anymore from here\r\n"); 00620 return 0; 00621 } 00622 } 00623 }
Generated on Sun Jul 17 2022 04:27:41 by 1.7.2