GoodToGo and changing license plates.

I have a good to go pass for driving across the 520 bridge.

I decided the get personalized license plates for my car.

They issued me a temporary paper license plate with the new plate, as well as a new registration for my car.

Should I remove the old metal plates from the car now, since my paper plate is visible in the back window?

Should I change my Good2Go account to reflect the new plates now?

What would happen if I drive the bridge and had removed my transponder?

Posted from WordPress for Windows Phone

I wrote the above while I was thinking about stuff last night. Today I went to the https://mygoodtogo.com/ web site and looked into changing the plate name associated with my tag. It has options to reference a temporary tag, along, which I’m fairly certain might take care of the questions that I came up with.
I decided that I wasn’t interested in looking up my VIN number and would wait to switch the GoodToGo system till I got the new plates in the mail. I can’t remember the legalities of if I need to leave the old plates on the car along with the temporary plate anyway, and decided it’s best not to mess with things.

Advertisements

Moving iTunes Library in two steps using COM interface and RoboCopy

Several years ago I migrated from storing my data on on server to another. I had all of my music stored in an hierarchy under the UNC sharename of \\Via\Music and moved it to a different server with the sharename of \\FortMeade\Music.

My music collection is around 60 gigabytes, and my experience with apple software in windows has not been very good. Telling the itunes application itself to do the moving did not seem like the best of plans if I wanted to be able to use my computer any time soon.

I decided that the most reliable method of transfering the files was to use the robocopy command and the windows command prompt. This took several hours over gigabit ethernet but got the data copied over.

I wanted to maintain as much of the data as possible about the individual tracks, and decided I could write a program using the COM interface to control iTunes and copy the entries from the old tracks and put them in for the new location.

I wrote a program in C++ using Microsoft Visual Studio, and successfully maintained the last played date and the number of plays. Unfortunately the location data of a track seems to be read only, otherwise I’d simply have changed that and been able to maintain all of the data.

Several years later, and I want to move to another server. Now when I modify my program and attempt to run it with the new names, it adds the tracks in the new location, but when it tries to modify the PlayedCount value, it traps a COM error (0xA0040203), and the error doesn’t seem to have a description.


// iManipulate.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include "iManipulate.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#endif

#undef GetFreeSpace /* macros are evil */
#import "C:/Program Files (x86)/iTunes/iTunes.exe"
using namespace iTunesLib;

// The one and only application object
CWinApp theApp;

int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
{
  int nRetCode = 0;
  // initialize MFC and print and error on failure
  if (!AfxWinInit(::GetModuleHandle(NULL), NULL, ::GetCommandLine(), 0))
  {
    _tprintf(_T("Fatal Error: MFC initialization failed\n"));
    nRetCode = 1;
  }
  else
  {
    // initialize COM libraries
    HRESULT hr = CoInitialize(NULL);
    if (SUCCEEDED(hr))
    {
      CComPtr<IiTunes> iTunes;
      if (S_OK == iTunes.CoCreateInstance(__uuidof(iTunesApp)))
      {
        CComPtr<IITLibraryPlaylist> MyLibrary = iTunes->LibraryPlaylist;
        CComPtr<IITTrackCollection> MyTrackCollection = MyLibrary->Tracks;
        for (long index = 1; index <= MyTrackCollection->Count ; index++)
        {
          CComPtr<IITTrack> MyTrack = MyTrackCollection->Item[index];
          if (MyTrack->Kind == ITTrackKindFile)
          {
            CComPtr<IITFileOrCDTrack> MyFileOrCDTrack;
            if (S_OK == MyTrack->QueryInterface(&MyFileOrCDTrack))
            {
              _tprintf(_T("%d"), index);
              BSTR MyTrackLocation = MyFileOrCDTrack->Location;
              _tprintf(_T("\t%s"), MyTrackLocation);
              CString NewLocation(MyTrackLocation);
              CString NewLocationAllLowerCase(NewLocation);
              NewLocationAllLowerCase.MakeLower();
              CString TextToReplace(_T("\\\\fortmeade\\music"));
              CString NewText(_T("\\\\Acid\\Music"));
              int ReplaceStart = NewLocationAllLowerCase.Find(TextToReplace);
              if (ReplaceStart >= 0)
              {
                NewLocation.Delete(ReplaceStart,TextToReplace.GetLength());
                NewLocation.Insert(0,NewText);
               if (-1 != _taccess(NewLocation,0))
               {
                 try
                 {
                   CComBSTR FileToAdd(NewLocation);
                   CComPtr<IITOperationStatus> MyOperationStatus = MyLibrary->AddFile(FileToAdd.m_str);
                   while ( VARIANT_TRUE == MyOperationStatus->GetInProgress())
                     Sleep(100);
                   CComPtr<IITTrackCollection> MyAddedTrackCollection = MyOperationStatus->GetTracks();
                   if (1 == MyAddedTrackCollection->Count)
                   {
                     CComPtr<IITTrack> MyAddedTrack = MyAddedTrackCollection->GetItem(1);
                     //MyAddedTrack->DateAdded = MyFileOrCDTrack->DateAdded;
                     if (MyFileOrCDTrack->Rating > 0)
                       MyAddedTrack->PutRating(MyFileOrCDTrack->Rating);
                     if (MyFileOrCDTrack->PlayedCount > 0)
                     {
                       MyAddedTrack->PutPlayedCount(MyFileOrCDTrack->GetPlayedCount());
                       MyAddedTrack->PutPlayedDate(MyFileOrCDTrack->GetPlayedDate());
                     }
                     MyFileOrCDTrack->Delete();
                     _tprintf(_T("\t%s\n"), FileToAdd);
                   }
                 }
                 catch(_com_error &e)
                 {
                   _tprintf(_T("\t(COM ERROR:%#lx %s, %s)\n"), e.Error(), e.Source(), e.Description());
                 }
               }
             }
             else
               _tprintf(_T("\r"));
           }
         }
       }
     }
   }
   CoUninitialize();
 }
 return nRetCode;
}

I’m finally getting around to experimenting with blogging

I expect to use this blog for posting most of the random programming questions that I’ve not come up with answers for elsewhere. I also hope to post answers to solutions I came up with that I thought were clever at the time.

I program primarily in C/C++ across various platforms. I use Windows and MS Visual Studio as my primary environment, but also program on the TI TMS320C6713 DSP using Code Composer Studio, and the BeagleBoard running Linux using gcc.

Pseudo real time clock on TI TMS320C6713 DSP

I needed to keep track of time in my program written in C/C++ running directly on the TI DSP platform where I did not have external RTC hardware. My solution was to override a couple of library functions from the C library and take advantage of the built in library routines for time management.

I’ve overridden the clock() and time() functions from the library. What I mean by overriding them is not in the traditional C++ sense, but simply in that by using my own functions that are declared locally, they get linked instead of the functions in the library. I also declare the _tz data structure that gets used for setting the correct timezone information for some of the standard output functions. I’m only running my system configured as UCT.

When the system starts, it calls the RTCStart() function which configures one of the two timer resources in the DSP to call my interrupt service routine (isr) clock_isr() every 1/1000th of a second.

My isr increments a volatile long integer. The clock() and time() functions simply access that long integer.

Over time I have some drift of my real time. Because I’m running on a tiny platform that normally only communicates over an RS422 serial port and doesn’t have significant RAM or storage I don’t want to add much but I’ve been wondering if there is some significantly better way that I should be attacking this problem?

TIMER_Handle	hTimer1 = (TIMER_Handle) INV;
//////////////////////////////////////////////////////////////////////////////
//// Clock Stuff /////////////////////////////////////////////////////////////
volatile unsigned long int DSP_clock_counter = 0;
volatile time_t time_offset = 0;
extern "C" {
	interrupt void clock_isr(void)
	{
		DSP_clock_counter++;
		if (DSP_clock_counter > 0xFFFFFFFFFFFFFFF) 
			DSP_clock_counter = 0;
	}
	_CODE_ACCESS clock_t clock(void) // Including this function overrides the function in the default library, allowing all of the standard time routines to be used.
	{
		return(DSP_clock_counter);
	}
	_DATA_ACCESS TZ _tz =
	{
		0,		/* daylight */
		0,		/* timezone */
		"UCT",	/* tzname   */
		"DST",	/* dstname  */
	};
	_CODE_ACCESS time_t time(time_t *timer) // Including this function overrides the function in the default library, allowing all of the standard time routines to be used.
	{
	   time_t result = (DSP_clock_counter / 1000) + time_offset;
	   if(timer)
	       *timer = result;
	   return(result);
	}
} // end of extern "C" 
time_t settime(time_t timer)
{
	time_offset = timer - (DSP_clock_counter / 1000);
	return(time_offset);
}
time_t clock2time(clock_t clock)
{
	return((clock / 1000) + time_offset);
}
void RTCStart(void)
{
	if (hTimer1 == INV)
		hTimer1 = TIMER_open(TIMER_DEV1, TIMER_OPEN_RESET);	// open timer and set to default 
	if (hTimer1 != INV)
		TIMER_configArgs(hTimer1,
		    0x00000281,        /*  Control Register (CTL) */ // CLKSRC=1 CP=0 HLD=1 GO=0 FUNC=1
		    0x0000C350,        /*  Period Register (PRD)  */
		    0x00000000         /*  Counter Register (CNT) */
			);
	IRQ_enable(IRQ_EVT_TINT1);
	install_interrupt(CPU_INT15, clock_isr);      // clock period interrupt (Timer1 Interrupt)
}
void RTCStop(void)
{
	if (hTimer1 != INV)
	{
		TIMER_close(hTimer1);				// timer: wait
		hTimer1 = (TIMER_Handle) INV;
		install_interrupt(CPU_INT15, NULL);	// measurement period interrupt (Timer1 Interrupt)
	}
}

Then there are some support functions that I’ve got for simple output that are generic and not related to the DSP.

void GetISO8601Time(char ISO8601Time[25], clock_t theTime)
{
	time_t timer = clock2time(theTime);
	int milliseconds = theTime % 1000;
	struct tm * UTC = gmtime(&timer);
	sprintf(ISO8601Time,"%04d-%02d-%02dT%02d:%02d:%02d.%03dZ",
		UTC->tm_year+1900,
		UTC->tm_mon+1,
		UTC->tm_mday,
		UTC->tm_hour,
		UTC->tm_min,
		UTC->tm_sec,
		milliseconds);
}
void GetISO8601Time(char ISO8601Time[25])
{
	GetISO8601Time(ISO8601Time, clock());
}

Here’s an example of code that sets the time offset

int ReaderDevicesetTimeUTC(char * InputBuffer, int InputBufferSize, const unsigned short LastSample, uart_init_struct &std_uart, FILE * tx)
{
	struct tm UTC;
	UTC.tm_year = 0;
	UTC.tm_mon = 0;
	UTC.tm_mday = 1;
	UTC.tm_hour = 0;
	UTC.tm_min = 0;
	UTC.tm_sec = 0;
	UTC.tm_wday = 0;
	UTC.tm_yday = 0;
	UTC.tm_isdst = 0;
	int millisec = 0;
	if (2 < sscanf(InputBuffer,"ReaderDevice.setTimeUTC:%04d-%02d-%02dT%02d:%02d:%02d.%03dZ",
		&(UTC.tm_year),
		&(UTC.tm_mon),
		&(UTC.tm_mday),
		&(UTC.tm_hour),
		&(UTC.tm_min),
		&(UTC.tm_sec),
		&millisec))
	{
		UTC.tm_year -= 1900;
		UTC.tm_mon -= 1;
		settime(mktime(&UTC));
	}
	if (tx != NULL)
	{
		char ISO8601Time[25];
		clock_t theTime = clock();
		GetISO8601Time(ISO8601Time, theTime);
		unsigned long int TimeTicks = theTime;
		fprintf(tx,"ReaderDevice.setTimeUTC:%24s TimeTicks:%lu\n", ISO8601Time, TimeTicks);
	}
	return(0);
}