Proivdes data log data structure for FRAM, EPROM chip with functions to read chip and send back on serial data string.
Dependencies: W25Q80BV multi-serial-command-listener
Dependents: xj-data-log-test-and-example
Data Logging Data structure
Both Read and write seem to be working fine but testing has been limited.
Motivation
I needed a flexible data log structure that could tolerate evolving data structures as I discovered more things that needed to be measured. I also wanted something that is mostly human readable while remaining sufficiently concise to make efficient use of expensive storage resources.
I found it challenging to track everything needed to perform after the fact analysis we need to improve our state machine. In addition what I wanted to measure changed with time and I needed a robust way to log this data so we could analyze it latter. without breaking or converting all the old data. A self describing data format like JSON or XML would work but FRAM is expensive so I wanted something flexible but still concise.
I am working on A2WH which is a electronic controller for a sophisticated product that balances many sensors, battery charging from photo voltaic panels, controlling speed of many different fans, humidity and environmental data. Our main challenge is we never have enough battery power to run everything so we have to make decisions about what to run in an effort to produce the maximum amount of water from the available solar power resource. Our 2nd challenge is that balancing system actions such as increasing or decreasing fan speeds is driven by a complex internal prediction model that attempts balance many competing thermodynamic requirements. To get all this right requires substantial after the fact analysis and that requires logging a large amount of evolving data.
Design Notes
See: data-log-read.me.txt in the same project
Sample Use and Basic Test
Serial Command Interface
COMMANDS readall= send entire contents of log readlast 999 999 = number of bytes from tail of log to retrieve tread 333 444 333 = starting offset to start reading log 444 = number of bytes to retrieve from log erase = erase log and start a new one help = display this help
Other Chips
For legacy reasons I am using the library for "W25Q80BV.h" simply because I started with it. The actual FRAM chip I am using is 2 MBit FRAM MB85RS2MTPH-G-JNE I also tested it with SRAM 23LCV1024-I/P
Simplifying Design Decision
I made a simplifying assumption that every-time we generate a log entry I record the offset of the next write at a specific location in the chip. This works and is fast but it causes lots of updates against a single location. I prefer FRAM because this would rapidly fatigue FLASH chips like the W25Q80BV. Storing this pointer data in the CPU has the same fatigue problem.
Another other option would be to store this offset and our other critical configuration data in the clock chip but it is susceptible to loosing power and loosing this critical data.
One reason I don't log directly to the micro-sd is for the same fatigue problem but it is mostly for power management.
The FRAM chip provides adequate durability and data retention through power outage. The power outage retention is critical because the A2WH systems can be buried under feet of snow in the winter and solar panels do not provide much recharge under that condition.
One design option I have considered but not yet implemented is using a much smaller FRAM chip critical configuration data and rapid update data and then log directly to a larger and less expensive FLASH chip .
Journaling to micro-SD
I latter decided to add features to allow after the fact copying of the data to micro-sd cards to obtain larger log storage without soldering in more chips. I found the micro-sd consume quite a lot of power so I still want to log direct to the FRAM then copy to the micro-sd when I have surplus power available. Still thinking about consolidation tactics to allow re-use of FRAM after the data has been copied ot micro-sd.
Future
- Support fast indexing by date to only pull back log entries between two dates.
- Record most recent record headers for each record types where they are fast to access so we can send them with the data when only sending back portions of the data.
- Support wrap around use of data log to re-use storage on chip.
- Copy Data to micro SD card and consolidate FRAM chip for re-use.
License
By Joseph Ellsworth CTO of A2WH Take a look at A2WH.com Producing Water from Air using Solar Energy March-2016 License: https://developer.mbed.org/handbook/MIT-Licence Please contact us http://a2wh.com for help with custom design projects.
dataLog.h@6:5368ef5fced0, 2016-03-31 (annotated)
- Committer:
- joeata2wh
- Date:
- Thu Mar 31 22:05:08 2016 +0000
- Revision:
- 6:5368ef5fced0
- Parent:
- 5:286459acee56
- Child:
- 8:f73745332754
adding command listener support to data_log
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
joeata2wh | 1:b2e12bf6b4aa | 1 | /* DataLog.h - Data logger for logging enviornmental |
joeata2wh | 1:b2e12bf6b4aa | 2 | data to EPROM, SRAM or FRAM chip without a file system |
joeata2wh | 3:5550814cc21c | 3 | inteface. Supports multiple and evolving records types |
joeata2wh | 3:5550814cc21c | 4 | and trnsport back to serial |
joeata2wh | 3:5550814cc21c | 5 | |
joeata2wh | 3:5550814cc21c | 6 | See data-log-text.txt for detailed design and layout notes |
joeata2wh | 3:5550814cc21c | 7 | See: xj-data-log-test-and-example.c for example use. |
joeata2wh | 4:fa5bbe31a039 | 8 | https://developer.mbed.org/users/joeata2wh/code/xj-data-log-test-and-example/wiki/Homepage |
joeata2wh | 4:fa5bbe31a039 | 9 | |
joeata2wh | 1:b2e12bf6b4aa | 10 | By Joseph Ellsworth CTO of A2WH |
joeata2wh | 1:b2e12bf6b4aa | 11 | Take a look at A2WH.com Producing Water from Air using Solar Energy |
joeata2wh | 1:b2e12bf6b4aa | 12 | March-2016 License: https://developer.mbed.org/handbook/MIT-Licence |
joeata2wh | 1:b2e12bf6b4aa | 13 | Please contact us http://a2wh.com for help with custom design projects. |
joeata2wh | 3:5550814cc21c | 14 | |
joeata2wh | 1:b2e12bf6b4aa | 15 | Before you complain about not using proper C++ classes, I intend to |
joeata2wh | 1:b2e12bf6b4aa | 16 | port the entire A2WH project to PSoc where I may or may not be able to |
joeata2wh | 1:b2e12bf6b4aa | 17 | use the full set of C++ features. I am using techniques that should be |
joeata2wh | 1:b2e12bf6b4aa | 18 | easier to port to a ANSI C enviornment. You may think this is a wierd |
joeata2wh | 1:b2e12bf6b4aa | 19 | decision but the PSoC enviornments gives me transparant support for |
joeata2wh | 1:b2e12bf6b4aa | 20 | differential ADC and very low power analog comparators that remain active |
joeata2wh | 1:b2e12bf6b4aa | 21 | when CPU is in deep sleep and which can wake the CPU up from deep sleep |
joeata2wh | 1:b2e12bf6b4aa | 22 | which makes very low power designs easier. mBed is still weak for this |
joeata2wh | 1:b2e12bf6b4aa | 23 | kind of advanced peripherial support. |
joeata2wh | 1:b2e12bf6b4aa | 24 | |
joeata2wh | 1:b2e12bf6b4aa | 25 | */ |
joeata2wh | 1:b2e12bf6b4aa | 26 | #ifndef DataLog_H |
joeata2wh | 1:b2e12bf6b4aa | 27 | #define DataLog_H |
joeata2wh | 1:b2e12bf6b4aa | 28 | #include "mbed.h" |
joeata2wh | 6:5368ef5fced0 | 29 | #include "multi-serial-command-listener.h" |
joeata2wh | 3:5550814cc21c | 30 | |
joeata2wh | 1:b2e12bf6b4aa | 31 | #include "W25Q80BV.h" // Note: We are using this library because we started with it but |
joeata2wh | 1:b2e12bf6b4aa | 32 | // the actual chip we are using is a 2 MBit FRAM MB85RS2MTPH-G-JNE |
joeata2wh | 1:b2e12bf6b4aa | 33 | // also tested with SRAM 23LCV1024-I/P Prefer SRAM or FRAM because |
joeata2wh | 1:b2e12bf6b4aa | 34 | // we made a simplifying assumption that we could write next log |
joeata2wh | 1:b2e12bf6b4aa | 35 | // address to the same position over and over. Without wear leveling |
joeata2wh | 1:b2e12bf6b4aa | 36 | // this could rapidly wear out a e-prom chip. Could have written |
joeata2wh | 1:b2e12bf6b4aa | 37 | // this to the clock chip which uses SRAM but FRAM has such high write |
joeata2wh | 1:b2e12bf6b4aa | 38 | // durability that we don't have to. |
joeata2wh | 1:b2e12bf6b4aa | 39 | |
joeata2wh | 1:b2e12bf6b4aa | 40 | #define DataLogChipType W25Q80BV |
joeata2wh | 1:b2e12bf6b4aa | 41 | |
joeata2wh | 1:b2e12bf6b4aa | 42 | // TODO: Add the SDIO link here to copy data from dlog chip to |
joeata2wh | 1:b2e12bf6b4aa | 43 | // SD card when available. |
joeata2wh | 1:b2e12bf6b4aa | 44 | |
joeata2wh | 2:8d06af2f1fcc | 45 | |
joeata2wh | 1:b2e12bf6b4aa | 46 | const long dlChipMaxAddr = 250000; // 2 mBit 2000000 / 8 |
joeata2wh | 1:b2e12bf6b4aa | 47 | #define dlChipFullErr -2 |
joeata2wh | 1:b2e12bf6b4aa | 48 | #define dlAddressTooLarge -3 |
joeata2wh | 1:b2e12bf6b4aa | 49 | #define dlMaxOperationSize -4; |
joeata2wh | 1:b2e12bf6b4aa | 50 | // Chip DLog Chip Memory Layout |
joeata2wh | 1:b2e12bf6b4aa | 51 | const long dlAddrInitByte = 1500; // data before this is assumed to be used for system config variables |
joeata2wh | 4:fa5bbe31a039 | 52 | const char dlInitByteValue = 214; |
joeata2wh | 1:b2e12bf6b4aa | 53 | const int dlMaxReadWriteSize = 32000; // limit imposed by streaming interface for the chip |
joeata2wh | 5:286459acee56 | 54 | const long dlAddrNextWritePos = dlAddrInitByte + 1; |
joeata2wh | 4:fa5bbe31a039 | 55 | const long dlNextWritePosSize = 4; |
joeata2wh | 4:fa5bbe31a039 | 56 | const long dlAddrCurrYDay = dlAddrNextWritePos + dlNextWritePosSize; |
joeata2wh | 4:fa5bbe31a039 | 57 | const long dlCurrYDaySize = 2; |
joeata2wh | 4:fa5bbe31a039 | 58 | const long dlAddrHeaders = dlAddrCurrYDay + dlCurrYDaySize ; |
joeata2wh | 1:b2e12bf6b4aa | 59 | const long dlHeadersLen = 256; |
joeata2wh | 5:286459acee56 | 60 | const long dlAddrDateIndex = dlAddrHeaders + dlHeadersLen + 1; |
joeata2wh | 5:286459acee56 | 61 | const long dlDateIndexLen = 1000; |
joeata2wh | 5:286459acee56 | 62 | const long dlAddrFirstLogEntry= dlAddrDateIndex + dlDateIndexLen + 1; |
joeata2wh | 1:b2e12bf6b4aa | 63 | const char dlEmpty[] = {0,0,0,0,0,0,0}; |
joeata2wh | 5:286459acee56 | 64 | const long dlMaxLogSize = dlChipMaxAddr - dlAddrFirstLogEntry; |
joeata2wh | 1:b2e12bf6b4aa | 65 | #define MIN(X,Y) X <? Y |
joeata2wh | 1:b2e12bf6b4aa | 66 | #define MAX(X,Y) X >? Y |
joeata2wh | 1:b2e12bf6b4aa | 67 | |
joeata2wh | 1:b2e12bf6b4aa | 68 | struct DLOG { |
joeata2wh | 1:b2e12bf6b4aa | 69 | DataLogChipType *chip; |
joeata2wh | 1:b2e12bf6b4aa | 70 | long nextWritePos; |
joeata2wh | 1:b2e12bf6b4aa | 71 | int currYDay; // tm_yday from gmtime only log date date when date changes |
joeata2wh | 1:b2e12bf6b4aa | 72 | char *buff; |
joeata2wh | 1:b2e12bf6b4aa | 73 | int buffLen; |
joeata2wh | 6:5368ef5fced0 | 74 | Serial *sio; |
joeata2wh | 6:5368ef5fced0 | 75 | struct SCMD *cmdProc; |
joeata2wh | 6:5368ef5fced0 | 76 | |
joeata2wh | 1:b2e12bf6b4aa | 77 | }; |
joeata2wh | 1:b2e12bf6b4aa | 78 | |
joeata2wh | 6:5368ef5fced0 | 79 | void dlCommandProc(char *cmd, void *dwrk); |
joeata2wh | 6:5368ef5fced0 | 80 | |
joeata2wh | 5:286459acee56 | 81 | long dlLen(struct DLOG *wrk) { |
joeata2wh | 5:286459acee56 | 82 | return wrk->nextWritePos - dlAddrFirstLogEntry; |
joeata2wh | 5:286459acee56 | 83 | } |
joeata2wh | 1:b2e12bf6b4aa | 84 | // save the current nextWritePos to the chip so we hav it |
joeata2wh | 1:b2e12bf6b4aa | 85 | // just in case of a reboot |
joeata2wh | 1:b2e12bf6b4aa | 86 | void dlSaveNextWritePos(struct DLOG *wrk) { |
joeata2wh | 4:fa5bbe31a039 | 87 | wrk->chip->writeStream(dlAddrNextWritePos,(char *) &wrk->nextWritePos,dlNextWritePosSize); // write next write postion |
joeata2wh | 1:b2e12bf6b4aa | 88 | } |
joeata2wh | 1:b2e12bf6b4aa | 89 | |
joeata2wh | 1:b2e12bf6b4aa | 90 | long dlReadNextWritePos(struct DLOG *wrk) { |
joeata2wh | 4:fa5bbe31a039 | 91 | wrk->chip->readStream(dlAddrNextWritePos, (char *) &wrk->nextWritePos, dlNextWritePosSize); |
joeata2wh | 4:fa5bbe31a039 | 92 | //printf("dlReadNextWritePos wrk->nextWritePos=%ld\n\r",wrk->nextWritePos); |
joeata2wh | 1:b2e12bf6b4aa | 93 | return wrk->nextWritePos; |
joeata2wh | 1:b2e12bf6b4aa | 94 | } |
joeata2wh | 1:b2e12bf6b4aa | 95 | |
joeata2wh | 1:b2e12bf6b4aa | 96 | int dlReadCurrYDay(struct DLOG *wrk) { |
joeata2wh | 5:286459acee56 | 97 | wrk->chip->readStream(dlAddrCurrYDay, (char *) &wrk->currYDay, dlCurrYDaySize); |
joeata2wh | 4:fa5bbe31a039 | 98 | //printf("dlReadCurrYDay wrk->currYDay=%d\r\n", wrk->currYDay); |
joeata2wh | 1:b2e12bf6b4aa | 99 | return wrk->currYDay; |
joeata2wh | 1:b2e12bf6b4aa | 100 | } |
joeata2wh | 1:b2e12bf6b4aa | 101 | |
joeata2wh | 1:b2e12bf6b4aa | 102 | void dlUpdateCurrDate(struct DLOG *wrk, int newYDay) { |
joeata2wh | 1:b2e12bf6b4aa | 103 | wrk->currYDay = newYDay; |
joeata2wh | 4:fa5bbe31a039 | 104 | wrk->chip->writeStream(dlAddrCurrYDay,(char *) &wrk->currYDay, dlCurrYDaySize); // write next write postion |
joeata2wh | 4:fa5bbe31a039 | 105 | } |
joeata2wh | 4:fa5bbe31a039 | 106 | |
joeata2wh | 4:fa5bbe31a039 | 107 | // Erase Log from Chip but do not touch |
joeata2wh | 4:fa5bbe31a039 | 108 | // data on chip outside of log space. |
joeata2wh | 4:fa5bbe31a039 | 109 | void dlEraseLog(struct DLOG *wrk) { |
joeata2wh | 4:fa5bbe31a039 | 110 | //printf("dlEraseLogStart\r\n"); |
joeata2wh | 5:286459acee56 | 111 | wrk->nextWritePos = dlAddrFirstLogEntry; |
joeata2wh | 4:fa5bbe31a039 | 112 | wrk->currYDay = -99; |
joeata2wh | 5:286459acee56 | 113 | memset(wrk->buff,0,wrk->buffLen); |
joeata2wh | 4:fa5bbe31a039 | 114 | wrk->chip->writeStream(dlAddrInitByte, (char *) &dlInitByteValue,1); // write init byte |
joeata2wh | 5:286459acee56 | 115 | wrk->chip->writeStream(dlAddrNextWritePos,(char *) &wrk->nextWritePos,dlNextWritePosSize); // reset the next write postion |
joeata2wh | 5:286459acee56 | 116 | wrk->chip->writeStream(dlAddrCurrYDay, (char *) &wrk->currYDay, dlCurrYDaySize); // Reset the currYDay |
joeata2wh | 4:fa5bbe31a039 | 117 | wrk->chip->writeStream(dlAddrHeaders,wrk->buff,MIN(wrk->buffLen,dlHeadersLen)); // nulls over the header region |
joeata2wh | 5:286459acee56 | 118 | wrk->chip->writeStream(dlAddrDateIndex,wrk->buff,MIN(wrk->buffLen,dlDateIndexLen)); // nulls over the header region |
joeata2wh | 4:fa5bbe31a039 | 119 | wrk->chip->writeStream(wrk->nextWritePos,wrk->buff,wrk->buffLen); // nulls first of the log |
joeata2wh | 4:fa5bbe31a039 | 120 | //printf("dlEraseLogDone\r\n"); |
joeata2wh | 1:b2e12bf6b4aa | 121 | } |
joeata2wh | 1:b2e12bf6b4aa | 122 | |
joeata2wh | 1:b2e12bf6b4aa | 123 | // New data log chip detected write data to initialize it. |
joeata2wh | 1:b2e12bf6b4aa | 124 | long dlInitializeChip(struct DLOG *wrk) { |
joeata2wh | 4:fa5bbe31a039 | 125 | dlEraseLog(wrk); |
joeata2wh | 1:b2e12bf6b4aa | 126 | return wrk->nextWritePos; |
joeata2wh | 1:b2e12bf6b4aa | 127 | } |
joeata2wh | 1:b2e12bf6b4aa | 128 | |
joeata2wh | 1:b2e12bf6b4aa | 129 | |
joeata2wh | 4:fa5bbe31a039 | 130 | |
joeata2wh | 1:b2e12bf6b4aa | 131 | /* read a initialization byte from chip. If the byte |
joeata2wh | 1:b2e12bf6b4aa | 132 | doesn't contain the expected value then write one |
joeata2wh | 1:b2e12bf6b4aa | 133 | and assume that we are starting our log ad the beginning */ |
joeata2wh | 1:b2e12bf6b4aa | 134 | long dlCheckChipInit(struct DLOG *wrk){ |
joeata2wh | 1:b2e12bf6b4aa | 135 | wrk->buff[0] = 0; |
joeata2wh | 4:fa5bbe31a039 | 136 | wrk->chip->readStream(dlAddrInitByte, wrk->buff, 1); |
joeata2wh | 1:b2e12bf6b4aa | 137 | if (wrk->buff[0] != dlInitByteValue) |
joeata2wh | 4:fa5bbe31a039 | 138 | { |
joeata2wh | 4:fa5bbe31a039 | 139 | //printf("Found empty chip running init"); |
joeata2wh | 1:b2e12bf6b4aa | 140 | return dlInitializeChip(wrk); |
joeata2wh | 4:fa5bbe31a039 | 141 | } |
joeata2wh | 1:b2e12bf6b4aa | 142 | else { |
joeata2wh | 4:fa5bbe31a039 | 143 | //printf("Found existing log\r\n"); |
joeata2wh | 1:b2e12bf6b4aa | 144 | dlReadCurrYDay(wrk); |
joeata2wh | 1:b2e12bf6b4aa | 145 | return dlReadNextWritePos(wrk); |
joeata2wh | 1:b2e12bf6b4aa | 146 | } |
joeata2wh | 1:b2e12bf6b4aa | 147 | } |
joeata2wh | 1:b2e12bf6b4aa | 148 | |
joeata2wh | 1:b2e12bf6b4aa | 149 | |
joeata2wh | 6:5368ef5fced0 | 150 | |
joeata2wh | 1:b2e12bf6b4aa | 151 | // make and instance of our dlog structure fill it in |
joeata2wh | 1:b2e12bf6b4aa | 152 | // in and load any current data such as next write postion |
joeata2wh | 1:b2e12bf6b4aa | 153 | // already loaded in the chip. |
joeata2wh | 6:5368ef5fced0 | 154 | struct DLOG *dlMake(DataLogChipType *dataLogMem, char *buff, short buffLen, Serial *sio) { |
joeata2wh | 1:b2e12bf6b4aa | 155 | struct DLOG *tout = (struct DLOG *) malloc(sizeof(struct DLOG)); |
joeata2wh | 1:b2e12bf6b4aa | 156 | tout->chip = dataLogMem; |
joeata2wh | 5:286459acee56 | 157 | tout->nextWritePos = dlAddrFirstLogEntry; |
joeata2wh | 1:b2e12bf6b4aa | 158 | tout->buff = buff; |
joeata2wh | 1:b2e12bf6b4aa | 159 | tout->buffLen = buffLen; |
joeata2wh | 6:5368ef5fced0 | 160 | tout->sio = sio; |
joeata2wh | 6:5368ef5fced0 | 161 | tout->cmdProc = scMake(sio, dlCommandProc, tout) ; |
joeata2wh | 1:b2e12bf6b4aa | 162 | dlCheckChipInit(tout); |
joeata2wh | 1:b2e12bf6b4aa | 163 | return tout; |
joeata2wh | 1:b2e12bf6b4aa | 164 | } |
joeata2wh | 1:b2e12bf6b4aa | 165 | |
joeata2wh | 4:fa5bbe31a039 | 166 | |
joeata2wh | 1:b2e12bf6b4aa | 167 | // writes log stream entry to chip and updates the next write |
joeata2wh | 1:b2e12bf6b4aa | 168 | // position. Also adds a null terminator to data on chip |
joeata2wh | 1:b2e12bf6b4aa | 169 | // log entries should not contain null characters because we |
joeata2wh | 1:b2e12bf6b4aa | 170 | // eventually plant to delay flush of nextWritePos and use |
joeata2wh | 1:b2e12bf6b4aa | 171 | // scan forware to find the end when a crash occurs. |
joeata2wh | 1:b2e12bf6b4aa | 172 | // returns -2 if the write request would go beyond chip size. |
joeata2wh | 3:5550814cc21c | 173 | long dlWrite(struct DLOG *wrk, char *aStr) { |
joeata2wh | 1:b2e12bf6b4aa | 174 | int slen = strlen(aStr); |
joeata2wh | 1:b2e12bf6b4aa | 175 | if ((wrk->nextWritePos + slen) >= dlChipMaxAddr) { |
joeata2wh | 1:b2e12bf6b4aa | 176 | return dlChipFullErr; |
joeata2wh | 1:b2e12bf6b4aa | 177 | } |
joeata2wh | 4:fa5bbe31a039 | 178 | wrk->chip->writeStream(wrk->nextWritePos, aStr,slen); |
joeata2wh | 1:b2e12bf6b4aa | 179 | wrk->nextWritePos += slen; |
joeata2wh | 1:b2e12bf6b4aa | 180 | dlSaveNextWritePos(wrk); // WARNING THIS IS THE LINE THAT WILL KILL EPROM CHIPS |
joeata2wh | 1:b2e12bf6b4aa | 181 | // with over-write fatigue. |
joeata2wh | 1:b2e12bf6b4aa | 182 | // TODO: add err check read first and last bytes. |
joeata2wh | 1:b2e12bf6b4aa | 183 | // compare to what was written. |
joeata2wh | 1:b2e12bf6b4aa | 184 | return wrk->nextWritePos; |
joeata2wh | 1:b2e12bf6b4aa | 185 | } |
joeata2wh | 4:fa5bbe31a039 | 186 | char dlLFEmpty[] = "\n\000"; |
joeata2wh | 4:fa5bbe31a039 | 187 | //record log file entry with time stamp and header |
joeata2wh | 3:5550814cc21c | 188 | long dlLog(struct DLOG *wrk, char *recType, char *str) { |
joeata2wh | 4:fa5bbe31a039 | 189 | |
joeata2wh | 4:fa5bbe31a039 | 190 | time_t seconds; |
joeata2wh | 4:fa5bbe31a039 | 191 | time(&seconds); |
joeata2wh | 4:fa5bbe31a039 | 192 | //printf("dlLog ctime(&seconds)=%s\r\n", ctime(&seconds)); |
joeata2wh | 4:fa5bbe31a039 | 193 | struct tm *ptm = localtime( &seconds ); |
joeata2wh | 5:286459acee56 | 194 | //printf("dlLog asctime=%s\r\n", asctime(ptm)); |
joeata2wh | 5:286459acee56 | 195 | //printf("dlLog ptm->tm_yday=%d\r\n", ptm->tm_yday ); |
joeata2wh | 1:b2e12bf6b4aa | 196 | if (ptm->tm_yday != wrk->currYDay) { |
joeata2wh | 4:fa5bbe31a039 | 197 | int year = 1900 + ptm->tm_year; |
joeata2wh | 4:fa5bbe31a039 | 198 | int month= ptm->tm_mon + 1; |
joeata2wh | 4:fa5bbe31a039 | 199 | |
joeata2wh | 1:b2e12bf6b4aa | 200 | dlUpdateCurrDate(wrk, ptm->tm_yday); |
joeata2wh | 4:fa5bbe31a039 | 201 | sprintf(wrk->buff,"\n00:00:00 DATE\t%04d-%02d-%02d", year, month, ptm->tm_mday); |
joeata2wh | 3:5550814cc21c | 202 | dlWrite(wrk, wrk->buff); |
joeata2wh | 5:286459acee56 | 203 | //printf("update date dateRec=%s\r\n", wrk->buff); |
joeata2wh | 1:b2e12bf6b4aa | 204 | } |
joeata2wh | 4:fa5bbe31a039 | 205 | |
joeata2wh | 4:fa5bbe31a039 | 206 | memset(wrk->buff,wrk->buffLen,0); |
joeata2wh | 4:fa5bbe31a039 | 207 | sprintf(wrk->buff,"\n%02d:%02d:%02d %s\t", ptm->tm_hour, ptm->tm_min, ptm->tm_sec, recType); |
joeata2wh | 4:fa5bbe31a039 | 208 | dlWrite(wrk, wrk->buff); |
joeata2wh | 4:fa5bbe31a039 | 209 | //dlWrite(wrk, dlLFEmpty); // add terminating lineFeed |
joeata2wh | 5:286459acee56 | 210 | //printf ("recHead=%s\r\n", wrk->buff); |
joeata2wh | 4:fa5bbe31a039 | 211 | return dlWrite(wrk, str); |
joeata2wh | 1:b2e12bf6b4aa | 212 | } |
joeata2wh | 1:b2e12bf6b4aa | 213 | |
joeata2wh | 1:b2e12bf6b4aa | 214 | // read a block of bytes from log starting at offset |
joeata2wh | 1:b2e12bf6b4aa | 215 | // for len bytes placed in buffer. If offset is > |
joeata2wh | 1:b2e12bf6b4aa | 216 | // log size then return dlAddressTooLarge if len would |
joeata2wh | 1:b2e12bf6b4aa | 217 | // be greate than log size only return that available. |
joeata2wh | 3:5550814cc21c | 218 | long dlRead(struct DLOG *wrk, char *buff, long offset, int len) { |
joeata2wh | 5:286459acee56 | 219 | long addr = dlAddrFirstLogEntry + offset; |
joeata2wh | 1:b2e12bf6b4aa | 220 | if ((addr + len) >= dlChipMaxAddr) |
joeata2wh | 1:b2e12bf6b4aa | 221 | return dlAddressTooLarge; |
joeata2wh | 1:b2e12bf6b4aa | 222 | wrk->chip->readStream(offset, buff, len); |
joeata2wh | 1:b2e12bf6b4aa | 223 | return 1; |
joeata2wh | 1:b2e12bf6b4aa | 224 | } |
joeata2wh | 1:b2e12bf6b4aa | 225 | |
joeata2wh | 3:5550814cc21c | 226 | long dlReadSend(struct DLOG *wrk, Serial *dest, long offset, long len) { |
joeata2wh | 5:286459acee56 | 227 | long addr = dlAddrFirstLogEntry + offset; |
joeata2wh | 1:b2e12bf6b4aa | 228 | long maxAddr = MIN(addr + len, dlChipMaxAddr); // no overflow past end of chip |
joeata2wh | 1:b2e12bf6b4aa | 229 | maxAddr = MIN(maxAddr, wrk->nextWritePos); // no overlow pas end of log |
joeata2wh | 4:fa5bbe31a039 | 230 | int chunkSize = wrk->buffLen -1; |
joeata2wh | 5:286459acee56 | 231 | long endAdd = MIN(addr + len, maxAddr); |
joeata2wh | 5:286459acee56 | 232 | //printf("\r\ndlReadSend addr=%ld offset=%ld len=%ld maxAddr=%ld chunkSize=%d endAdd=%ld nextWritePos=%ld\r\n", |
joeata2wh | 5:286459acee56 | 233 | // addr, offset, len, maxAddr, chunkSize, endAdd, wrk->nextWritePos); |
joeata2wh | 1:b2e12bf6b4aa | 234 | if (addr >= maxAddr) |
joeata2wh | 1:b2e12bf6b4aa | 235 | return dlAddressTooLarge; |
joeata2wh | 5:286459acee56 | 236 | int chunkCnt=0; |
joeata2wh | 1:b2e12bf6b4aa | 237 | do { |
joeata2wh | 5:286459acee56 | 238 | memset(wrk->buff, wrk->buffLen, 0); |
joeata2wh | 1:b2e12bf6b4aa | 239 | if (addr + chunkSize > endAdd) |
joeata2wh | 1:b2e12bf6b4aa | 240 | chunkSize = endAdd - addr; |
joeata2wh | 5:286459acee56 | 241 | //printf("\r\n\tdlReadSend addr=%ld endAdd=%ld chunkSize=%ld chunkCnt=%d\r\n", addr, endAdd, chunkSize, chunkCnt); |
joeata2wh | 1:b2e12bf6b4aa | 242 | wrk->chip->readStream(addr, wrk->buff, chunkSize); |
joeata2wh | 5:286459acee56 | 243 | wrk->buff[chunkSize] = 0; |
joeata2wh | 1:b2e12bf6b4aa | 244 | dest->printf("%s", wrk->buff); |
joeata2wh | 5:286459acee56 | 245 | int tlen = strlen(wrk->buff); |
joeata2wh | 5:286459acee56 | 246 | //printf("\r\n\tdlReadSend tlen=%d\r\n", tlen); |
joeata2wh | 1:b2e12bf6b4aa | 247 | addr += chunkSize; |
joeata2wh | 5:286459acee56 | 248 | wait(0.05); |
joeata2wh | 5:286459acee56 | 249 | chunkCnt++; |
joeata2wh | 1:b2e12bf6b4aa | 250 | } while (addr < endAdd); |
joeata2wh | 5:286459acee56 | 251 | return 1; |
joeata2wh | 1:b2e12bf6b4aa | 252 | } |
joeata2wh | 1:b2e12bf6b4aa | 253 | |
joeata2wh | 5:286459acee56 | 254 | long dlReadSendAll(struct DLOG *wrk, Serial *dest) { |
joeata2wh | 5:286459acee56 | 255 | return dlReadSend(wrk, dest, 0, wrk->nextWritePos); |
joeata2wh | 5:286459acee56 | 256 | } |
joeata2wh | 5:286459acee56 | 257 | |
joeata2wh | 5:286459acee56 | 258 | long dlReadSendLast(struct DLOG *wrk, Serial *dest, long numSend) { |
joeata2wh | 5:286459acee56 | 259 | long logLen = dlLen(wrk); |
joeata2wh | 5:286459acee56 | 260 | long calcLen = MIN(numSend, logLen); |
joeata2wh | 5:286459acee56 | 261 | long startPos = logLen - calcLen; |
joeata2wh | 5:286459acee56 | 262 | startPos = MAX(0, startPos); |
joeata2wh | 5:286459acee56 | 263 | //printf("dlReadSendLast logLen=%ld startPos=%ld calcLen=%ld nextWritePos=%ld\r\n ", logLen, startPos,calcLen, wrk->nextWritePos); |
joeata2wh | 5:286459acee56 | 264 | return dlReadSend(wrk, dest, startPos, calcLen); |
joeata2wh | 5:286459acee56 | 265 | } |
joeata2wh | 1:b2e12bf6b4aa | 266 | |
joeata2wh | 6:5368ef5fced0 | 267 | |
joeata2wh | 6:5368ef5fced0 | 268 | void dlHelp(struct DLOG *wrk) { |
joeata2wh | 6:5368ef5fced0 | 269 | Serial *pc = wrk->sio; |
joeata2wh | 6:5368ef5fced0 | 270 | pc->printf("\r\nCOMMANDS\r\n"); |
joeata2wh | 6:5368ef5fced0 | 271 | pc->printf(" readall= send entire contents of log\r\n"); |
joeata2wh | 6:5368ef5fced0 | 272 | pc->printf(" readlast 999\r\n"); |
joeata2wh | 6:5368ef5fced0 | 273 | pc->printf(" 999 = number of bytes from tail of log to retrieve\r\n"); |
joeata2wh | 6:5368ef5fced0 | 274 | pc->printf(" tread 333 444\r\n"); |
joeata2wh | 6:5368ef5fced0 | 275 | pc->printf(" 333 = starting offset to start reading log\r\n"); |
joeata2wh | 6:5368ef5fced0 | 276 | pc->printf(" 444 = number of bytes to retrieve from log\r\n"); |
joeata2wh | 6:5368ef5fced0 | 277 | pc->printf(" erase = erase log and start a new one\r\n"); |
joeata2wh | 6:5368ef5fced0 | 278 | pc->printf(" help = display this help\r\n"); |
joeata2wh | 6:5368ef5fced0 | 279 | } |
joeata2wh | 6:5368ef5fced0 | 280 | |
joeata2wh | 6:5368ef5fced0 | 281 | // recieve callback from serial console |
joeata2wh | 6:5368ef5fced0 | 282 | // parse those commands and take approapriate |
joeata2wh | 6:5368ef5fced0 | 283 | // actions like sending back part of the data |
joeata2wh | 6:5368ef5fced0 | 284 | // erasing the log etc. |
joeata2wh | 6:5368ef5fced0 | 285 | void dlCommandProc(char *cmd, void *dwrk) { |
joeata2wh | 6:5368ef5fced0 | 286 | if (dwrk == NULL) return; |
joeata2wh | 6:5368ef5fced0 | 287 | struct DLOG *wrk = (struct DLOG *) dwrk; |
joeata2wh | 6:5368ef5fced0 | 288 | char cmdPrefix[10]; |
joeata2wh | 6:5368ef5fced0 | 289 | long part2 = 0; |
joeata2wh | 6:5368ef5fced0 | 290 | long part3 = 0; |
joeata2wh | 6:5368ef5fced0 | 291 | sscanf (cmd,"%s %ld %ld",cmdPrefix, &part2, &part3); |
joeata2wh | 6:5368ef5fced0 | 292 | printf("cmd=%s cmdPref=%s part2=%ld part3=%ld\r\n", cmd, cmdPrefix, part2, part3); |
joeata2wh | 6:5368ef5fced0 | 293 | |
joeata2wh | 6:5368ef5fced0 | 294 | if (strcmp(cmdPrefix,"readall") == 0) { |
joeata2wh | 6:5368ef5fced0 | 295 | dlReadSendAll(wrk, wrk->sio); |
joeata2wh | 6:5368ef5fced0 | 296 | } |
joeata2wh | 6:5368ef5fced0 | 297 | else if (strcmp(cmdPrefix, "readlast") == 0) { |
joeata2wh | 6:5368ef5fced0 | 298 | dlReadSendLast(wrk, wrk->sio, part2); |
joeata2wh | 6:5368ef5fced0 | 299 | } |
joeata2wh | 6:5368ef5fced0 | 300 | else if (strcmp(cmdPrefix, "read") == 0) { |
joeata2wh | 6:5368ef5fced0 | 301 | dlReadSend(wrk, wrk->sio, part2, part3); |
joeata2wh | 6:5368ef5fced0 | 302 | } |
joeata2wh | 6:5368ef5fced0 | 303 | else if (strcmp(cmdPrefix, "erase") == 0) { |
joeata2wh | 6:5368ef5fced0 | 304 | dlEraseLog(wrk); |
joeata2wh | 6:5368ef5fced0 | 305 | } |
joeata2wh | 6:5368ef5fced0 | 306 | else if (strcmp(cmdPrefix, "help") == 0) { |
joeata2wh | 6:5368ef5fced0 | 307 | dlHelp(wrk); |
joeata2wh | 6:5368ef5fced0 | 308 | } |
joeata2wh | 6:5368ef5fced0 | 309 | else { |
joeata2wh | 6:5368ef5fced0 | 310 | wrk->sio->printf("ERR no matching command cmd=%s\r\n", cmd); // no command matched |
joeata2wh | 6:5368ef5fced0 | 311 | dlHelp(wrk); |
joeata2wh | 6:5368ef5fced0 | 312 | } |
joeata2wh | 6:5368ef5fced0 | 313 | } |
joeata2wh | 6:5368ef5fced0 | 314 | |
joeata2wh | 1:b2e12bf6b4aa | 315 | #endif |