// load.cpp : Guide database loader sample program
//
//
// This is a part of the Microsoft Foundation Classes C++ library.
// Copyright (C) 1997 Microsoft Corporation
// All rights reserved.
//
// This source code is only intended as a supplement to the
// Broadcast Architecture Programmer's Reference.
// See the reference for detailed information regarding
// Broadcast Architecture.
#include "stdafx.h"
#include "Load.h"
#include "tssutil.h"
/////////////////////////////////////////////////////////////////////////////
// CLoadApp
BEGIN_MESSAGE_MAP(CLoadApp, CWinApp)
//{{AFX_MSG_MAP(CLoadApp)
// NOTE - the ClassWizard will add and remove mapping macros here.
// DO NOT EDIT what you see in these blocks of generated code!
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CLoadApp construction
CLoadApp::CLoadApp()
{
// TODO: add construction code here,
// Place all significant initialization in InitInstance
}
/////////////////////////////////////////////////////////////////////////////
// The one and only CLoadApp object
CLoadApp theApp;
//
// This is the external entry point defined in the .DEF file that is used by
// the generic loader app to call into the Sample loader.
//
extern "C"
{
ExitCodeList APIENTRY
EPG_DBLoad(int &argc, _TCHAR **argv, CdbDBEngine &db, PFNFORCEQUIT pfnForceQuit)
{
AFX_MANAGE_STATE(AfxGetStaticModuleState());
ExitCodeList rc = theApp.EPG_DBLoad(argc, argv, db, pfnForceQuit);
if(theApp.m_lpfnDaoTerm)
(*theApp.m_lpfnDaoTerm)();
return rc;
}
};
CLoadApp::CLoadCommandLineProc::CLoadCommandLineProc(void)
{
m_fPartial = FALSE;
m_csRead = "";
}
void
CLoadApp::CLoadCommandLineProc::Partial(CString &csArg)
// Process the cmd line switch that allows us to be
// told to do a partial update
{
m_fPartial = TRUE;
return;
}
void
CLoadApp::CLoadCommandLineProc::Read(CString &csArg)
// Process the cmd line switch that gives a location to read loader data from
{
m_csRead = csArg;
return;
}
void
CLoadApp::CLoadCommandLineProc::Help(CString &csArg)
// Display the command line usage
{
AfxMessageBox(IDS_USAGE, MB_OK | MB_ICONSTOP);
return;
}
// This table tells the command line processor what to do for each valid argument.
CLoadApp::CLoadCommandLineProc::CArgProcTable
CLoadApp::CLoadCommandLineProc::acapArgs[] =
{
IDS_SWPARTIAL, (void (CCommandLineProc::*)(class CString &))CLoadApp::CLoadCommandLineProc::Partial,
IDS_SWREAD, (void (CCommandLineProc::*)(class CString &))CLoadApp::CLoadCommandLineProc::Read,
IDS_SWHELP, (void (CCommandLineProc::*)(class CString &))CLoadApp::CLoadCommandLineProc::Help,
-1, NULL,
};
BOOLEAN
CLoadApp::CLoadCommandLineProc::GetPositionalArgs(int &argc, _TCHAR **argv)
{
return TRUE;
}
// This is the main entry point into the loader class.
ExitCodeList
CLoadApp::EPG_DBLoad(int &argc, _TCHAR **argv, CdbDBEngine &db
, PFNFORCEQUIT pfnForceQuit)
{
ExitCodeListrc;
if(!m_clpCmds.ProcessCommandLine(IDS_SWITCHCHARS, argc, argv))
return EXIT_USAGE;
theApp.m_pDAODB = &db;
theApp.m_pfnForceQuit = pfnForceQuit;
try
{
if(EXIT_OK != (rc = InitMembers()))
return (rc);
rc = ProcessInput(m_clpCmds.m_fPartial, db);
return (rc);
}
catch (CException *e)
{
e->Delete();
return (EXIT_FAIL_CEXCEPTION);
}
catch (CdbException cdbe)
{
return (EXIT_FAIL_DBEXCEPTION);
}
catch (ExitCodeList rc)
{
return (rc);
}
catch (...)
{
return (EXIT_FAIL);
}
}
// This reinitializes certain app class members
ExitCodeList
CLoadApp::InitMembers(void)
{
DWORD rc;
// Incoming data is all gmt. Set up app class time info
// for conversions
TIME_ZONE_INFORMATION tzi;
DWORD tzrc;
tzrc = ::GetTimeZoneInformation(&tzi);
CTimeSpan bias(0, 0, tzi.Bias, 0);
switch(tzrc)
{
case TIME_ZONE_ID_UNKNOWN:
break;
case TIME_ZONE_ID_DAYLIGHT:
{
CTimeSpan temp(0, 0, tzi.DaylightBias, 0);
bias += temp;
break;
}
case TIME_ZONE_ID_STANDARD:
{
CTimeSpan temp(0, 0, tzi.StandardBias, 0);
bias += temp;
break;
}
default:
THROWASSERT(0, EXIT_FAIL_GETTIMEZONE);
break;
}
m_odtsTimeZoneAdjust.SetDateTimeSpan(bias.GetDays(), bias.GetHours(), bias.GetMinutes(), bias.GetSeconds());
m_codtGuideStartTime.SetStatus(COleDateTime::invalid);
m_codtGuideEndTime.SetStatus(COleDateTime::invalid);
DWORD dwBytes = sizeof(m_lTuningSpace);
rc = TSS_GetTuningIDs(SZDTVLOADGUID, (DWORD *)&m_lTuningSpace, dwBytes);
if((!rc) /*|| (dwBytes != sizeof(m_lTuningSpace)) GFS - How cound this be true?*/)
return EXIT_FAIL_GETTUNINGSPACE;
m_covTuningSpace = m_lTuningSpace;
//aux
rs = NULL;
r = NULL;
bp = NULL;
g = NULL;
sg = NULL;
sr = NULL;
// main
n = NULL;
s = NULL;
c = NULL;
cp = NULL;
csr = NULL;
e = NULL;
ep = NULL;
ts = NULL;
t = NULL;
return EXIT_OK;
}
ExitCodeList CLoadApp::ProcessInput(BOOLEAN fPartialUpdate, CdbDBEngine &db)
{
ExitCodeList rc = EXIT_OK;
COleDateTime m_odtStart(COleDateTime::GetCurrentTime());
m_covNow = m_odtStart + m_odtsTimeZoneAdjust;
CString csWSP;
csWSP.LoadString(IDS_LOADERWORKSPACE);
try
{
OpenTables();
db[csWSP].BeginTrans();
Handle(fPartialUpdate);
ClearOldEntries(db); // delete anything from mpg time span that wasn't refreshed.
ClearDanglingRefs(db); // cleanup unused auxiliary records
BlockCommit(db, EPGLDR_ACTIVE_COMMIT_STARTING, EPGLDR_ACTIVE_COMMIT_ENDING);
CloseTables();
}
catch (CException *e)
{
e->Delete();
db[csWSP].Rollback();
return (EXIT_FAIL_CEXCEPTION); // failed
}
catch (CdbException cdbe)
{
db[csWSP].Rollback();
return (EXIT_FAIL_DBEXCEPTION);
}
catch (ExitCodeList rc)
{
db[csWSP].Rollback();
return (rc);
}
catch (...)
{
db[csWSP].Rollback();
return (EXIT_FAIL);
}
#ifdef _DEBUG
if (afxTraceFlags & traceDatabase)
{
COleDateTime end_parse(COleDateTime::GetCurrentTime());
COleDateTimeSpan duration(end_parse - m_odtStart);
afxDump << "end parse. length = " << ((ULONG)duration.GetTotalSeconds()) << "\r\n";
}
#endif
return rc;
}
// execute deletes on each table in proper order for
// all records whose last tx hasn't been updated and
// who's attached to stuff that overlaps in the mpg's time frame
void CLoadApp::ClearOldEntries(CdbDBEngine &db)
{
CString csWSP;
csWSP.LoadString(IDS_LOADERWORKSPACE);
try
{
CdbDatabase database = db[csWSP][(LONG) 0];
db[csWSP].BeginTrans();
COleDateTime codtStartTimeMinusDay = m_codtGuideStartTime;
COleDateTimeSpan codtsDelta;
codtsDelta.SetDateTimeSpan(1, 0, 0, 0);
codtStartTimeMinusDay -= codtsDelta;
// delete old timeslots with end time < mpg start time
// this removes any showings that are finished
ExecuteActionQuery(database, IDS_DELETE_EXPIRED_TS, & (COleVariant) m_lTuningSpace, & (COleVariant) codtStartTimeMinusDay);
// delete unupdated timeslots with last update < now and start time < guide range end time
// this removes any showings that were in the mpg last time the loader ran
// but that aren't there now
ExecuteActionQuery(database, IDS_DELETE_OMITTED_TS, & (COleVariant) m_lTuningSpace,
&m_covNow, & (COleVariant) m_codtGuideStartTime, & (COleVariant) m_codtGuideEndTime);
// the previous actions may have left episode or channels records which aren't referenced
// by any time slots.
Commit(db);
#ifdef _DEBUG
if (afxTraceFlags & traceDatabase)
TRACE0("clear old entries committed\r\n");
#endif
}
catch (CException *e)
{
e->Delete();
db[csWSP].Rollback();
#ifdef _DEBUG
afxDump << "ClearOldEntries CException catch handler\r\n";
#endif
throw e;
}
catch (...)
{
db[csWSP].Rollback();
#ifdef _DEBUG
afxDump << "ClearOldEntries ... catch handler\r\n";
#endif
throw (EXIT_FAIL);
}
}
// execute deletes on each table in proper order for
// all auxiliary record which aren't used by anyone
// we now delete these dangling records and this completes the differencing portion
// of the partial update
void CLoadApp::ClearDanglingRefs(CdbDBEngine &db)
{
CString csWSP;
csWSP.LoadString(IDS_LOADERWORKSPACE);
try
{
CdbDatabase database = db[csWSP][(LONG) 0];
db[csWSP].BeginTrans();
ExecuteActionQuery(database, IDS_DELETE_DANGLING_C);
ExecuteActionQuery(database, IDS_DELETE_DANGLING_E);
ExecuteActionQuery(database, IDS_DELETE_DANGLING_S);
ExecuteActionQuery(database, IDS_DELETE_DANGLING_THEME);
Commit(db);
#ifdef _DEBUG
if (afxTraceFlags & traceDatabase) {
TRACE0("dangling deletes committed\r\n");
}
#endif
}
catch (CException *e)
{
e->Delete();
db[csWSP].Rollback();
#ifdef _DEBUG
afxDump << "ClearDanglingRefs CException catch handler\r\n";
#endif
throw e;
}
catch (...)
{
db[csWSP].Rollback();
#ifdef _DEBUG
afxDump << "ClearDanglingRefs ... catch handler\r\n";
#endif
throw (EXIT_FAIL);
}
}
// This method executes a query in the database
void
CLoadApp::ExecuteActionQuery(CdbDatabase &qd, int iStringID, COleVariant *p0
, COleVariant *p1, COleVariant *p2, COleVariant *p3)
{
CString csDelQuery;
if ((*m_pfnForceQuit)())
throw (EXIT_ABORT);
csDelQuery.LoadString(iStringID);
CdbQueryDef qdbdd;
qdbdd = qd.QueryDefs.Item((LPCTSTR) csDelQuery);
if (p0 != NULL)
qdbdd.Parameters[(LONG) 0].SetValue(*p0);
if (p1 != NULL)
qdbdd.Parameters[(LONG) 1].SetValue(*p1);
if (p2 != NULL)
qdbdd.Parameters[(LONG) 2].SetValue(*p2);
if (p3 != NULL)
qdbdd.Parameters[(LONG) 3].SetValue(*p3);
qdbdd.Execute();
return;
}
void
CLoadApp::Commit(CdbDBEngine &db, LONG lStartMessage, LONG lEndMessage)
{
UINT uiMessage = RegisterWindowMessage(SZLOADERSTUBGUID);
if(0 != lStartMessage)
::PostMessage(HWND_BROADCAST, uiMessage, lStartMessage, 0);
CString csWSP;
csWSP.LoadString(IDS_LOADERWORKSPACE);
db.Idle();
db[csWSP].CommitTrans();
if(0 != lEndMessage)
::PostMessage(HWND_BROADCAST, uiMessage, lEndMessage, 0);
}
void
CLoadApp::BlockCommit(CdbDBEngine &db, LONG lStartMessage, LONG lEndMessage)
{
UINT uiMessage = RegisterWindowMessage(SZLOADERSTUBGUID);
if(0 != lStartMessage)
::SendMessageTimeout(HWND_BROADCAST, uiMessage, lStartMessage, 0,
SMTO_NORMAL, 5 * 1000, NULL);
CString csWSP;
csWSP.LoadString(IDS_LOADERWORKSPACE);
db.Idle();
db[csWSP].CommitTrans();
if(0 != lEndMessage)
::PostMessage(HWND_BROADCAST, uiMessage, lEndMessage, 0);
}
// Open recordsets for each of the tables
// Set each recordset to proper index
void
CLoadApp::OpenTables(VOID)
{
// aux
rs = new CRatingSystemRecordset();
rs->OpenIndexed(IDS_RS_ADDKEY, dbOpenTable, NULL, 0);
r = new CRatingRecordset();
r->OpenIndexed(IDS_R_ADDKEY, dbOpenTable, NULL, 0);
bp = new CBroadcastPropertyRecordset();
bp->OpenIndexed(IDS_BP_ADDKEY, dbOpenTable, NULL, 0);
g = new CGenreRecordset();
g->OpenIndexed(IDS_G_ADDKEY, dbOpenTable, NULL, 0);
sg = new CSubGenreRecordset();
sg->OpenIndexed(IDS_SG_ADDKEY, dbOpenTable, NULL, 0);
sr = new CStreamTypeRecordset();
sr->OpenIndexed(IDS_SR_ADDKEY, dbOpenTable, NULL, 0);
// main
n = new CNetworkRecordset();
n->OpenIndexed(IDS_N_ADDKEY, dbOpenTable, NULL, 0);
s = new CStationRecordset();
s->OpenIndexed(IDS_S_ADDKEY, dbOpenTable, NULL, 0);
c = new CChannelTRecordset();
c->OpenIndexed(IDS_C_ADDKEY, dbOpenTable, NULL, 0);
cp = new CChannelPropertyRecordset();
cp->OpenIndexed(IDS_CP_ADDKEY, dbOpenTable, NULL, 0);
csr = new CChannelStreamRecordset();
csr->OpenIndexed(IDS_CSR_ADDKEY, dbOpenTable, NULL, 0);
e = new CEpisodeTRecordset();
e->OpenIndexed(IDS_E_ADDKEY, dbOpenTable, NULL, 0);
ep = new CEpisodePropertyRecordset();
ep->OpenIndexed(IDS_EP_ADDKEY, dbOpenTable, NULL, 0);
ts = new CTimeSlotRecordset();
ts->OpenIndexed(IDS_TS_ADDKEY, dbOpenTable, NULL, 0);
t = new CThemeRecordset();
t->OpenIndexed(IDS_T_ADDKEY, dbOpenTable, NULL, 0);
}
// close all of the tables
void
CLoadApp::CloseTables(void)
{
//aux
if (rs != NULL) {
rs->CloseRecordset();
delete rs;
rs = NULL;
}
if (r != NULL) {
r->CloseRecordset();
delete r;
r = NULL;
}
if (bp != NULL) {
bp->CloseRecordset();
delete bp;
bp = NULL;
}
if (g != NULL) {
g->CloseRecordset();
delete g;
g = NULL;
}
if (sg != NULL) {
sg->CloseRecordset();
delete sg;
sg = NULL;
}
if (sr != NULL) {
sr->CloseRecordset();
delete sr;
sr = NULL;
}
// main
if (n != NULL) {
n->CloseRecordset();
delete n;
n = NULL;
}
if (s != NULL) {
s->CloseRecordset();
delete s;
s = NULL;
}
if (c != NULL) {
c->CloseRecordset();
delete c;
c = NULL;
}
if (cp != NULL) {
cp->CloseRecordset();
delete cp;
cp = NULL;
}
if (csr != NULL) {
csr->CloseRecordset();
delete csr;
csr = NULL;
}
if (e != NULL) {
e->CloseRecordset();
delete e;
e = NULL;
}
if (ep != NULL) {
ep->CloseRecordset();
delete ep;
ep = NULL;
}
if (ts != NULL) {
ts->CloseRecordset();
delete ts;
ts = NULL;
}
if (t != NULL) {
t->CloseRecordset();
delete t;
t = NULL;
}
}
// Put data into the Guide Database
VOID
CLoadApp::Handle(BOOL fPartialUpdate)
{
// Add a station
CStationcs(AFX_RFX_LONG_PSEUDO_NULL, "WGFS", "Sample Station"
, 0// Network ID
, "mylogo"// Logo
, "The Sample station");// Description
s->UpdateRS(cs);
// Add a channel
COleDateTime codtDummy = COleDateTime(1999, 12, 30, 0, 0, 0);
CChannelTcct(AFX_RFX_LONG_PSEUDO_NULL
, -2// Tuning space
, 241// Channel number
, codtDummy// Start time
, codtDummy// End time
, 0// Length
, cs.StationID()// Station ID
, "The Sample Channel"// Description
, 0// Enhancement ID
, 0// Rating ID
, 0// Display mask
, 0// Payment address
, 0// Payment token
, COleDateTime::GetCurrentTime() + m_odtsTimeZoneAdjust); // Last update
c->UpdateRS(cct);
// Add an episode
CEpisodeT cet(AFX_RFX_LONG_PSEUDO_NULL
, "The Sample Show"// Title
, "Bringing you samples from around the world"// Description
, 0// Enhancement ID
, 0// Display mask
, 0// Theme ID
, 0// Rating ID
, 255// Abbreviation
, COleDateTime::GetCurrentTime() + m_odtsTimeZoneAdjust);// last update
e->UpdateRS(cet);
// Put the show in a time slot
// Start the show at the top of the next hour
COleDateTime codtStart = COleDateTime::GetCurrentTime();
codtStart.SetDateTime(codtStart.GetYear(), codtStart.GetMonth(), codtStart.GetDay()
, codtStart.GetHour()+1, 0, 0);
// Make it a half hour show
COleDateTime codtEnd = codtStart + COleDateTimeSpan(0, 0, 30, 0);
ts->UpdateRS(CTimeSlot(AFX_RFX_LONG_PSEUDO_NULL
, cct.ChannelID()// Channel ID
, cet.EpisodeID()// Eppisode ID
, codtStart// Start time
, codtEnd// End time
, 30// Length
, 0// Payment address
, 0// Payment token
, COleDateTime::GetCurrentTime() + m_odtsTimeZoneAdjust// Last update
, FALSE// Pay-per-view
, FALSE// Closed caption
, FALSE// Stereo
, FALSE// Re-run
, FALSE// Tape inhibited
, FALSE// Other properties
, FALSE// Alternate data
, FALSE));// Alternate audio
}