Ricks Email Reply.
Ricks message:
"Well, I'm not sure which part would be most instructive, but here is the main loop from Minute Counter. It should get you started pretty well, although I'm sure there are lots of file format differences between the 6035 and the i300! So, have you learned the record layout from the i300 files? If so, would you mind sharing that? I'd like to branch out at some point in the future and at least support the upcoming i330, I'm assuming it'll be similar.
Let me know if you need anything else.
-- first some snippets from the .H file
typedef struct {
UInt8 CallType; // 1 = incoming, 2 = outgoing, 3 = missed, 7 = data
UInt8 Unknown1;
UInt32 TimeCallPlaced; // seconds since 1/1/1904
UInt32 LengthOfCall; // seconds
UInt8 Unknown2[8];
Char PhoneNumber[52];
} CallHistoryDBRecordType;
#define CALLTYPE_INCOMING 1
#define CALLTYPE_OUTGOING 2
#define CALLTYPE_MISSED 3
#define CALLTYPE_CALLWAITING 5 // incoming type
#define CALLTYPE_DATA 7
#define CALLTYPE_DATA2 8
#define CallHistoryDBName "CallHistoryDB"
-- and the main loop
void UpdateMainDisplay(void) {
FormPtr frm;
UInt16 objectIndex;
UInt32 cntr;
Char newLabelText[15], daysLeftText[3];
LocalID dbID;
DmOpenRef callDB;
UInt32 numRecords;
MemHandle recordH;
CallHistoryDBRecordType* recordP;
DateTimeType BillingEnd, CallTime;
UInt32 secondsStart, secondsEnd, secondsCall;
UInt16 peakCallsOut, peakCallsIn, peakCallsData, peakMinOut, peakMinIn, peakMinData;
UInt16 offpeakCallsOut, offpeakCallsIn, offpeakCallsData,
offpeakMinOut, offpeakMinIn, offpeakMinData,
peakCallsOther, peakMinOther, offpeakCallsOther, offpeakMinOther ;
RectangleType rect;
ControlType* ctl;
Char *str;
Boolean isOffPeak = false;
// first toggle off-peak labels on/off based on prefs
UInt16 objectList[] = { MainOffPeak1Label,
MainOffPeak2Label,
MainOffPeak3Label,
MainOffPeak4Label,
MainOffPeak5Label,
MainOffPeak6Label,
MainOffPeak7Label,
MainOffPeakInPlanLabel,
MainOffPeakOutGoingCallsLabel,
MainOffPeakOutGoingTotalLabel,
MainOffPeakIncomingCallsLabel,
MainOffPeakIncomingTotalLabel,
MainOffPeakDataCallsLabel,
MainOffPeakDataTotalLabel,
MainOffPeakOtherCallsLabel,
MainOffPeakOtherTotalLabel,
MainOffPeakAllTotalLabel,
MainOffPeakRemainingSelTrigger };
frm = FrmGetActiveForm();
for (cntr = 0; cntr < (sizeof(objectList) / sizeof(objectList[0])); cntr++)
{
objectIndex = FrmGetObjectIndex(frm, objectList[cntr]);
if (gPreferences.UseOffPeak)
FrmShowObject(frm, objectIndex);
else
FrmHideObject(frm, objectIndex);
}
CalcStatusBox(true);
// this will set the global flag for in current period or not, called with
// false parameter so period is not actually reset to current
CheckForMonthReset(false);
// this is why we're here. Do the calculations, update the labels
dbID = DmFindDatabase(0, CallHistoryDBName);
DmDatabaseSize(0, dbID, &numRecords, NULL, NULL);
callDB = DmOpenDatabase(0, dbID, dmModeReadOnly);
// end of billing period is 1 month from start - 1 minute ealier in time
// also zero out seconds
gPreferences.BillingStart.second = 0;
secondsStart = TimDateTimeToSeconds(&gPreferences.BillingStart);
BillingEnd = gPreferences.BillingStart;
BillingEnd.month++;
if (BillingEnd.month == 13) {
BillingEnd.month = 1;
BillingEnd.year++;
}
secondsEnd = TimDateTimeToSeconds(&BillingEnd) - 60;
// set all the counters to 0
peakCallsOut = peakCallsIn = peakCallsData = peakMinOut = peakMinIn = peakMinData
=
offpeakCallsOut = offpeakCallsIn = offpeakCallsData = offpeakMinOut = offpeakMinIn
=
offpeakMinData = peakCallsOther = peakMinOther = offpeakCallsOther = offpeakMinOther
= 0;
gTotalM2M = gTotalFree = gTotalExceededM2M = 0;
// scan all the Call History records
for (cntr = 0; cntr < numRecords; cntr++) {
recordH = DmQueryRecord(callDB, cntr);
recordP = MemHandleLock(recordH);
// first test each record to see if inside billing period and not a free system
call
if (recordP->TimeCallPlaced >= secondsStart &&
recordP->TimeCallPlaced <= secondsEnd) {
// get seconds reounded up to the nearest full minute
secondsCall = RoundedUpSeconds(recordP->LengthOfCall + gPreferences.AddSeconds);
// check against exclusions list. If true, skip the rest of loop
if (CheckExclusions(recordP->PhoneNumber, secondsCall)) {
MemHandleUnlock(recordH);
continue;
}
// test for first incoming minute free, take it off if so
if (gPreferences.FIMF && secondsCall >= 60 &&
(recordP->CallType == CALLTYPE_INCOMING ||
recordP->CallType == CALLTYPE_CALLWAITING) )
secondsCall -= 60;
TimSecondsToDateTime(recordP->TimeCallPlaced, &CallTime);
// test for off-peak
if (gPreferences.UseOffPeak != OFFPEAK_NONE)
isOffPeak = IsOffPeak(CallTime);
if (isOffPeak) {
switch (recordP->CallType) {
case CALLTYPE_INCOMING:
case CALLTYPE_CALLWAITING:
offpeakCallsIn++;
offpeakMinIn += secondsCall / 60;
break;
case CALLTYPE_OUTGOING:
offpeakCallsOut++;
offpeakMinOut += secondsCall / 60;
break;
case CALLTYPE_DATA:
case CALLTYPE_DATA2:
offpeakCallsData++;
offpeakMinData += secondsCall / 60;
break;
default:
peakCallsOther++;
peakMinOther += secondsCall / 60;
} // switch calltype
} // if test for offpeak = true
else {
switch (recordP->CallType) {
case CALLTYPE_INCOMING:
case CALLTYPE_CALLWAITING:
peakCallsIn++;
peakMinIn += secondsCall / 60;
break;
case CALLTYPE_OUTGOING:
peakCallsOut++;
peakMinOut += secondsCall / 60;
break;
case CALLTYPE_DATA:
case CALLTYPE_DATA2:
peakCallsData++;
peakMinData += secondsCall / 60;
break;
default:
offpeakCallsOther++;
offpeakMinOther += secondsCall / 60;
} // switch calltype
} // if test for offpeak = false
} // if test for billing period
MemHandleUnlock(recordH);
} // for
DmCloseDatabase(callDB);
CalcStatusBox(false);
// update all peak data labels
StrIToA(newLabelText, gPreferences.PeakMinutes);
FrmCopyLabel(frm, MainPeakInPlanLabel, newLabelText);
StrIToA(newLabelText, peakCallsOut);
FrmCopyLabel(frm, MainPeakOutGoingCallsLabel, newLabelText);
StrIToA(newLabelText, peakMinOut);
FrmCopyLabel(frm, MainPeakOutGoingTotalLabel, newLabelText);
StrIToA(newLabelText, peakCallsIn);
FrmCopyLabel(frm, MainPeakIncomingCallsLabel, newLabelText);
StrIToA(newLabelText, peakMinIn);
FrmCopyLabel(frm, MainPeakIncomingTotalLabel, newLabelText);
StrIToA(newLabelText, peakCallsData);
FrmCopyLabel(frm, MainPeakDataCallsLabel, newLabelText);
StrIToA(newLabelText, peakMinData);
FrmCopyLabel(frm, MainPeakDataTotalLabel, newLabelText);
StrIToA(newLabelText, peakCallsOther);
FrmCopyLabel(frm, MainPeakOtherCallsLabel, newLabelText);
StrIToA(newLabelText, peakMinOther);
FrmCopyLabel(frm, MainPeakOtherTotalLabel, newLabelText);
gTotalPeak = peakMinOut + peakMinIn + peakMinData + peakMinOther;
StrIToA(newLabelText, gTotalPeak);
FrmCopyLabel(frm, MainPeakAllTotalLabel, newLabelText);
ctl = GetObjectPtr(MainPeakRemainingSelTrigger);
str = (Char *) CtlGetLabel(ctl);
StrIToA(str, (Int16) (gPreferences.PeakMinutes - (gTotalPeak)));
CtlSetLabel(ctl, str);
if (gPreferences.UseOffPeak) {
// update all off-peak data labels, if option is on
if (gPreferences.OffPeakMinutes == 0)
StrCopy(newLabelText,"Unlim");
else
StrIToA(newLabelText, gPreferences.OffPeakMinutes);
FrmCopyLabel(frm, MainOffPeakInPlanLabel, newLabelText);
StrIToA(newLabelText, offpeakCallsOut);
FrmCopyLabel(frm, MainOffPeakOutGoingCallsLabel, newLabelText);
StrIToA(newLabelText, offpeakMinOut);
FrmCopyLabel(frm, MainOffPeakOutGoingTotalLabel, newLabelText);
StrIToA(newLabelText, offpeakCallsIn);
FrmCopyLabel(frm, MainOffPeakIncomingCallsLabel, newLabelText);
StrIToA(newLabelText, offpeakMinIn);
FrmCopyLabel(frm, MainOffPeakIncomingTotalLabel, newLabelText);
StrIToA(newLabelText, offpeakCallsData);
FrmCopyLabel(frm, MainOffPeakDataCallsLabel, newLabelText);
StrIToA(newLabelText, offpeakMinData);
FrmCopyLabel(frm, MainOffPeakDataTotalLabel, newLabelText);
StrIToA(newLabelText, offpeakCallsOther);
FrmCopyLabel(frm, MainOffPeakOtherCallsLabel, newLabelText);
StrIToA(newLabelText, offpeakMinOther);
FrmCopyLabel(frm, MainOffPeakOtherTotalLabel, newLabelText);
gTotalOffPeak = offpeakMinOut + offpeakMinIn + offpeakMinData + offpeakMinOther;
StrIToA(newLabelText, gTotalOffPeak);
FrmCopyLabel(frm, MainOffPeakAllTotalLabel, newLabelText);
ctl = GetObjectPtr(MainOffPeakRemainingSelTrigger);
str = (Char *) CtlGetLabel(ctl);
if (gPreferences.OffPeakMinutes == 0)
StrCopy(str,"Unlim");
else
StrIToA(str, (Int16) (gPreferences.OffPeakMinutes - (gTotalOffPeak)));
CtlSetLabel(ctl, str);
}
// finish up with Month (days left) label
StrCopy(newLabelText, gMonthStrings[gPreferences.BillingStart.month - 1]);
if (gInCurrentPeriod) {
StrIToA(daysLeftText, gDaysLeftInPeriod);
StrCat(newLabelText," (");
StrCat(newLabelText,daysLeftText);
StrCat(newLabelText,")");
}
rect.topLeft.x = 95; rect.topLeft.y = 0; rect.extent.x = 64; rect.extent.y =
FntLineHeight();
WinEraseRectangle(&rect, 0);
WinDrawChars(newLabelText, StrLen(newLabelText), 95, 0);
} // UpdateMainDisplay
"
At 09:25 PM 7/25/2002 -0700, you wrote:
Hello Rick
First of all, thank you so much for your minutes program for the 6035, I am an avid user. I m writing because myself and a few others from a discussion board are attempting to create a similar application for the i300 (freeware, of course). Can you give me any tips, links, comments to help us in our endeavors? We have discovered that unlike the 6035 the call data on an SPH-I300 is stored in 3 databases. Any chance of a sample of code showing how you accessed the db on the i300? Thank you very much. If you don t have time to reply, that s fine, I wanted to thank you for the 6035 program anyways. Have a great day. John