Here's a challenge for you ( Mr. DFS ).
Given this input ( when my monitor was off ):
http://Jeff-Relf.Me/SleepLog.TXT
Produce this output:
A table showing every HalfHour of the day OffLine, last 15 months:
http://Jeff-Relf.Me/SleepPat.HTM
http://Jeff-Relf.Me/SleepPat.PNG
Here's how _I did it:
void SleepPat() { LnP HardOut =
L" My 118 by 235 block of text is omitted here, for room. \n"
L" See 'HardOut' in X.CPP in '
http://Jeff-Relf.Me/X.ZIP' \n";
L" Help/Settings: '
http://Jeff-Relf.Me/X.HTM' \n";
// "TurnHr" is when the day begins, 3 am:
const int TurnHr = 3, DaySecs = 24*60*60, Rows = 48,
TopLns = 57, LnLen = 236, Cols = LnLen - 8, Hrs = 48 ;
int rv, _Row, Day, xAxisDrawn, Bot, Dur, DateLine,
Col, StartngRow, H, M ;
float fDur ; LnA LL ; LnP P, Tok, Date, _Out ;
i64 aDate, TopDate ; tm DayRec = {}, aRec; char _Month[8];
wchar Hex, ReHex, Ch, Ch2, sDay[8];
if ( Diff_Load = 1, !Load_File( LocDir, L"SleepLog.TXT" ) ) {
Sh( L"-- Couldn't READ \"%s\".", TheOpenFile ); return; }
DayRec.tm_isdst = -1, TopDate = xAxisDrawn = 0;
// "_B_Sh" is a OneMeg+ dynamic buffer I use, occationally.
_Out = _B_Sh, strCpy( _Out, HardOut );
// "fpLines" is the dynamic array of lines, from "Load_File()", above.
{ LoopPP( Ln, fpLines ) {
if ( LL = PP + 3, LL >= EE || !Eq( B + 19, L"offline:" ) )
// Get ( the line after ) the "offline:" line.
continue;
// Get the Month, Day, and Year OffLine info:
B = *LL, Date = B += 12 - B[-1], _Str( _Month, "%.3S", Date );
LoopJ(12) if ( *pInt( _Month ) == Mos[ J ] ) break ;
// _Only tm's Month, Day, and Year fields are set/used:
DayRec.tm_mon = J, DayRec.tm_mday = AtoI( Date + 4 );
DayRec.tm_year = AtoI( Date + 18 ) - 1900, aDate = mktime( &DayRec );
if ( !xAxisDrawn ) {
// "TopDate" ( i64 seconds since 1900 ) is the most recent date.
xAxisDrawn = 1, aRec = DayRec, TopDate = mktime( &aRec );
LoopBot(2) { LoopBacCol( Cols ) {
// Set the xAxis DayOfTheMonth labels:
mktime( &aRec ), Day = --aRec.tm_mday, Str( sDay, L"%d ", Day + 1 );
wmemmove( &Out( Rows + 1 + ( Day <= 27 ? Day%4 : 4 + Day - 28 ),
Col + 1 ), sDay, 2 );
if ( !Day ) // Set the Month Year labels:
P = &Out( Rows, ER( 0, Col - 3 ) ),
P += Str( P, L" %s %d", MonthNyms[ aRec.tm_mon ], aRec.tm_year + 1900 ),
*P = 32 ; } } }
// Get the Hours/Mintues OffLine info:
Date += 4, P = Date += *Date == 32 ; To32;
DateLen = P - Date, *P++ = 0, P++ ; To!32;
Tok = P ; LoopP( Ch != ':' ); *P++ = 0;
H = AtoI( Tok ); To!32; Tok = P ;
if ( isDigit( *Tok ) ) { To32; *P++ = 0; }
M = AtoI( Tok ); if ( *P == 'P' ) H += 12 ;
// "Cols" is the far right column, the most recent day.
// "Col" is "Cols - DaysAgo"; one column per day.
//
// Normally, Midnight signals a new day, a new Col.
//
// We correct for this by decrementing Col for 12am to 3am
// data, and Row is decremented 3 hours; i.e. cut 3 hours
// off the top of the table and paste it to the bottom,
// shifted a day left.
Col = Cols - ( TopDate - aDate )/DaySecs - ( H < TurnHr ),
// Bot is true when using the bottom table.
Bot = Col <= 0, Col += Bot*Cols ;
if ( Col <= 0 )
// Only two tables, !Bot and Bot; so we're done.
break ;
// "H - 3", the hour of the day, is -3 to 20.
// "Hrs" is 48, 2*24, one Row for each HalfHour.
StartngRow = Hrs + 2*( H - TurnHr ) + M/30, StartngRow %= Hrs;
fDur = AtoF( LL[-1] ), Dur = Round( fDur );
// Convert "Dur" ( Hours OffLine ) to a Hex digit.
Hex = NtoHex( Dur ), ReHex = 0 ;
{ LoopRow( Round( 2*fDur ) + 2 ) { _Row = StartngRow + Row ;
// The char at the Row, Col; Col increments on overflow.
wchar &Ch = Out( _Row%Rows, Col + ( _Row >= Rows ) );
if ( ( ReHex = isHex( Ch ) ) || Row >= eRow ) {
// "ReHex" joins adjacent runs, just for run.
ReHex = !ReHex ? 0 : NtoHex( HexToN( Ch ) + Dur - ( Row < eRow ) );
break; }
// Put the digit into the table.
Ch = Hex ; } }
if ( _Row = StartngRow - 1, ReHex ) LOOP {
// Join adjacent runs, give them a higher digit.
++_Row >= Rows && ( _Row = 0, Col++ );
wchar &Ch = Out( _Row, Col );
if ( !isHex( Ch ) ) break ; Ch = ReHex ; } } }
StartReport, Sh( L"%s\n%s\n%s", RptHdr, L"'C' (Hex) means my monitor"
L" was off for 12 hours, starting at the yAxis time, on the xAxis Date."
L" Days start at 3am.\n", _Out ); SaveShowReport( L"SleepPat.HTM" ); }
Globals:
typedef __int64 i64 ; typedef int *pInt ;
typedef wchar_t wchar ; typedef wchar *LnP ; typedef LnP *LnA ;
#define Eq !strCmp
#define strCpy wcscpy
#define MkInt( M ) *pInt( #M )
LnP MonthNyms[]= { L"Jan", L"Feb", L"Mar", L"Apr", L"May", L"Jun",
L"Jul", L"Aug", L"Sep", L"Oct", L"Nov", L"Dec" };
const int Mos[]= { MkInt( Jan ), MkInt( Feb ), MkInt( Mar ),
MkInt( Apr ), MkInt( May ), MkInt( Jun ), MkInt( Jul ),
MkInt( Aug ), MkInt( Sep ), MkInt( Oct ), MkInt( Nov ), MkInt( Dec ) };
// Access " _Out ", a One Dimensional array,
// as if it were a couple of 2D tables, top and bottom.
#define Out( Row, Col ) _Out[ ( ( Row ) + Bot*TopLns )*LnLen + ( Col ) ]
// Special Named loops, for clarity:
#define LoopBot( N ) int eBot = N, Bot = -1 ; eBot-- ; while ( ++Bot <= eBot )
#define LoopBacCol( N ) int Col = N ; while ( --Col >= 0 )
// "LoopP()" loops P through a null terminated line.
// When "Bool" allows, it loops up to, and including, the null,
// setting Ch0, Ch, and Ch2 ( for Bool ), as it goes.
#define LoopP( Bool ) Ch = 1, Ch2 = 0, P-- ; \
while( ( Ch0 = Ch ) && ( Ch = *++P, Ch2 = !Ch ? 0 : P[1], Bool ) )
// "LoopPP()" loops PP through a (dynamic) array of lines,
// setting B and P (pointers) as it goes.
#define LoopPP( Tt, Xx ) Tt##P P = 0, B = 0 ; \
Tt##A BB = (Xx).BB, EE = (Xx).PP + 1, PP = BB - 1 ; \
if ( BB ) while( ++PP < EE ? ( B = P = *PP, 1 ) : 0 )
#define LoopJ( N ) int J = -1, eJ = ( N ) - 1 ; while ( ++J <= eJ )
#define LoopRow( N ) int Row = -1, eRow = ( N ) - 1 ; while ( ++Row <= eRow )
// Sh() is like printf(), but to my custom console/Shell.
int Sh( LnP S, ... )
// Marks off console lines, and saves them to a file:
StartReport
SaveShowReport( LnP S );
// Return the bigER one.
int ER( int X, int Y ) { return X > Y ? X : Y ; }
#define Raise_Ch towupper
inline int RaisC( LnP P ) { return *P = Raise_Ch( *P ); }
inline int HexToN( wchar C ) { if ( isDigit( C ) ) return C - '0' ;
if ( RaisC( & C ) >= 'A' && C <= 'F' ) return 10 + C - 'A' ; return -1 ; }
inline wchar NtoHex( int N ) { return N > 9 ? 'A' + N - 10 : '0' + N ; }