/*  TT8 specific includes  */
#include        <TT8.h>                 /* Tattletale Model 8 Definitions */
#include        <tat332.h>              /* 68332 Tattletale (7,8) Hardware Definitions */
#include        <sim332.h>              /* 68332 System Integration Module Definitions */
#include        <qsm332.h>              /* 68332 Queued Serial Module Definitions */
#include        <dio332.h>              /* 68332 Digital I/O Port Pin Definitions */
#include        <tt8lib.h>              /* definitions and prototypes for Model 8 library */

/*  general C includes  */
#include        <stdio.h>
#include        <stdlib.h>
#include		<string.h>

/*  includes for our particular program  */
#include		"assert.h"
#include		"memcheck.h"
#include		"science.h"
#include		"engineer.h"
#include		"logtime.h"
#include		"logdisk.h"
#include		"text_log.h"
#include		"misc.h"
#include		"MacTrimp.h"
#include		"MAX186.h"
#include		"criterr.h"


const long ENG_LOGGER_MAX_N_POINTS = ((64L*1024L - sizeof(struct MacTrimpi))/sizeof(short));

//  defined in main.c.  Sets the minimum system clock speed required
//  for an engineering data read (which pauses the AD7716 processing)
extern const long MIN_ENG_READ_FREQ;

//  The structs and routines declared here instead of in the .h file
//  are to be used only by the subroutines in this file.  All interactions
//  with the engineering data logger are through the subroutines defined
//  in the .h file.  The data structure written to disk is declared in the
//	.h file.
//
//  Control structure with information describing each channel which
//  is allocated.  This is information which is not part of the main
//  data block which is written to disk.
struct eng_data_record  {
	time_tt first_point_time;	//  time at which first data point in the data BUFFER was collected
	time_tt collect_time;		//  time at which we should collect the next data point
	time_tt last_disk_write;	//  time at which data was last written to disk	
	long delta_t;				//  time between data points.
	long n_written;				//  number of data points written to disk
	short n_data;				//  number of data points in the buffer now
	char fname[64];				//  name of file to which the data is being written
	short data[207];			//  actual data; rounds out record to 1024 bytes
};
const short eng_logger_max_n_data = 207;
//const short eng_logger_max_n_data = sizeof (struct eng_data_record->data) / sizeof (short);
struct MacTrimpi eng_logger_header;


//
//  This is the control struct for engineering data collection.
struct engineer_logger  {
	struct eng_data_record* channel_buff[N_ENG_CHANNELS];
	int engineer_open;			//  This variable is set when the open routine is called,
								//  and is zero when the engineering logger is closed.

} eng_logger;

//  Routines used only within this file; reads one data point and adds
//  to buffer for that channel
void eng_logger_collect_chan (int chan);
int eng_logger_write_buffer (int chan);
int eng_logger_write_header (int chan);



//  eng_logger_power_on_init (void)
//
//	Inits which should be executed at power-on.  Set pointers to NULL, and
//  set the open flag to indicate closed.
//
void eng_logger_power_on_init (void)  {
	int n;
	//  no particular reason for this, its just what I expect
	ASSERT (sizeof (struct eng_data_record) == 512);
	ASSERT (ENG_LOGGER_MAX_N_POINTS >= 256);

	//printf ("Max points per engineering data file is %ld\n", (long)ENG_LOGGER_MAX_N_POINTS);	
	eng_logger.engineer_open = 0;
	for (n = 0;  n < N_ENG_CHANNELS;  n++)  {
		//  REVIEW
		//  Suppose the program resets for some reason (power glitch?)
		//  and the program restarts.  How does this reset the memory
		//  allocations routines?  how should this affect what we do
		//  here?
		eng_logger.channel_buff[n] = NULL;
	}	

	//  twiddle the hardware
	MAX186_shutdown (-1);
	MAX186_power_up ();
	MAX186_power_down ();
	MAX186_shutdown (0);
}



//  eng_logger_open (void)
//
//  This routine should always be called by the user once before any
//  other engineering logger functions are called.  It used to do work
//  now it doesn't do anything.  Maybe it will be needed in future.
//
void eng_logger_open (void)  {
	time_tt now;
	int n;
	
	//  REVIEW
	//  eng_logger.engineer_open is probably zero at this point, but it may
	//  not be if we have reset the program in the middle due to
	//  some exception or other disastorous error.  Set it to a value
	//  which is unlikely to occur by accident.
	ASSERT (eng_logger.engineer_open == 0);
	eng_logger.engineer_open = ENGINEER_INCLUDED;
}


//  eng_logger_close (void)
//
//  Main purpose is to deallocate memory.  There is no way to fail.
//
void eng_logger_close (void)  {
	time_tt start_time;
	int n;

	//  don't crash if we call this routine even when we aren't open
	if (eng_logger.engineer_open != ENGINEER_INCLUDED)  {
		ASSERT (eng_logger.engineer_open == 0);
		return;
	}	

	//  free any allocated buffers; because of possible bug in Aztec memory
	//  routines we are careful to free in the reverse of the allocation order
	for (n = N_ENG_CHANNELS-1;  n >= 0;  n--)  {
		if (eng_logger.channel_buff[n] != NULL)  {
			free (eng_logger.channel_buff[n]);
			eng_logger.channel_buff[n] = NULL;
		}
	}
	eng_logger.engineer_open = 0;
}



        
//  eng_logger_channel_setup ()
//
//  Sets up a particular engineering channel.  Allocates memory for the
//  buffer; inits variables, including the time variable which tells the
//  collection routine when it is time to take data on this channel next
//
//  Channel numbers run from 0 up
//
//	Returns: 0 if OK, -1 on failure
//  Failures: only if out of memory
//
int eng_logger_channel_setup (int chan, long delay)  {
	int n;
	time_tt now;
	ASSERT ((chan >= 0) && (chan < N_ENG_CHANNELS));

	//  don't do anything unless the logger is open
	if (eng_logger.engineer_open != ENGINEER_INCLUDED)  {
		ASSERT (eng_logger.engineer_open == 0);
		return 0;
	}

	//  If delay is not positive, then this channel should be turned off.
	//  Deallocate memory if it was allocated.
	//  REVIEW
	//	This prevents us from freeing memory in the reverse of the allocation
	//  order later on.  This may screw up the Aztec memory routines.
	if (delay <= 0)  {
		if (eng_logger.channel_buff[chan] != NULL)  {
			free (eng_logger.channel_buff[chan]);
			eng_logger.channel_buff[chan] = NULL;
		}
		return 0;
	}

	//  allocate space for the data buffer/control structure, if
	//  necessary.
	if (eng_logger.channel_buff[chan] == NULL)  {
		eng_logger.channel_buff[chan] = (struct eng_data_record*)
										malloc (sizeof (struct eng_data_record));
		if (eng_logger.channel_buff[chan] == NULL)
			return -1;
	}  else  {
		//  when we reset a channel rate on an active channel, we just throw out
		//  the old data.
		if (eng_logger.channel_buff[chan]->n_data != 0)  {
			printf ("%d old data points at the old rate were lost.\n",
									eng_logger.channel_buff[chan]->n_data);
		}
	}
	eng_logger.channel_buff[chan]->delta_t = delay;
	eng_logger.channel_buff[chan]->n_written = 0;
	eng_logger.channel_buff[chan]->n_data = 0;
	eng_logger.channel_buff[chan]->last_disk_write = EARLIEST_LOGGER_TIME;	
	eng_logger.channel_buff[chan]->first_point_time = LATEST_LOGGER_TIME;

	//  Now compute time at which we should collect the first data point.
	//  The first collection is set for a couple of seconds after the
	//  current time.
	now = logger_time_get_time ();
	now.secs += 5;
	eng_logger.channel_buff[chan]->collect_time = now;

	ASSERT (eng_logger_max_n_data*sizeof(short) == sizeof(eng_logger.channel_buff[chan]->data));
	#ifdef DEBUG
		for (n = 0;  n < eng_logger_max_n_data;  n++)
			//  Set data points to an unexpected value so that we can have
			//  an extra chance to catch screwups if the program fails and
			//  marks uncollected points as collected
			eng_logger.channel_buff[chan]->data[n] = 0xABCD;
	#endif		
}	
	



//  eng_logger_collect ()
//
//  Loops through all eight engineering channels, checks to see if it is
//  time to collect data from that channel yet, and does so if appropriate
//	
//  Returns: 0; could be number of channels collected
//  Failures: none currently
//
int eng_logger_collect (void)  {
	time_tt now;
	int chan;
	long orig_freq;

	//  don't do anything unless the logger is open
	if (eng_logger.engineer_open != ENGINEER_INCLUDED)  {
		ASSERT (eng_logger.engineer_open == 0);
		return 0;
	}
	
	//  record our initial clock speed in case we need to up it later for
	//  data collection
	orig_freq = SimGetFSys ();

	//  collect data from each engineering channel in turn as necessary
	now = logger_time_get_time ();
	for (chan = 0;  chan < N_ENG_CHANNELS;  chan++)  {
		if (eng_logger.channel_buff[chan] == NULL)
			continue;
		if (ttmcmp (now, eng_logger.channel_buff[chan]->collect_time) < 0)
			continue;
		
		//  time to collect more data from this channel.  But first,
		//	up the clock speed so that we can process the data as
		//  quickly as possible
			
		//  REVIEW - need to consider how changing frequency will affect
		//  the baud rates, and adjust as needed.  This frequency change
		//  is temporarily disabled - during debug the logger is not slowed
		//  below the max anyway;  note also that this may inject noise into
		//  the A/D converters
//		SimSetFSys (16e6);
		eng_logger_collect_chan (chan);
	}
	//  reset clock speed back to original
	SimSetFSys (orig_freq);
	//  REVIEW - are we done yet???
}




//  eng_logger_collect_chan ()
//
//  Does the actual work of collecting a data point from a particular
//  channel and adding it to the buffer.  See code for details on how
//	this routine coordinates with the AD7716
//
//  Channel number run from 0 up
//  
void eng_logger_collect_chan (chan)  {
	long count;

	//  don't do anything unless the logger is open
	if (eng_logger.engineer_open != ENGINEER_INCLUDED)  {
		ASSERT (eng_logger.engineer_open == 0);
		return;
	}
	ASSERT (eng_logger.channel_buff[chan] != NULL);
	
	//  make sure we have room in the local buffer for the data
	if (eng_logger.channel_buff[chan]->n_data >= eng_logger_max_n_data)  {
		//  programming failure; the data buffer filled up and was not written
		//  to disk before this routine was next called.
		log_printf ("eng_logger_collect_chan: not reading channel %d because buffer is full.\n", chan);
		#ifdef DEBUG
			printf ("Programming error in eng_logger_collect_chan!\n");
		#endif	
		//  skip this collection point; set up to collect data
		//  from this channel next time round
		eng_logger.channel_buff[chan]->collect_time.secs +=
								eng_logger.channel_buff[chan]->delta_t;
		return;
	}		
	
	//  make sure we have room in the file for the data
	if (eng_logger.channel_buff[chan]->n_data + eng_logger.channel_buff[chan]->n_written >=
																		ENG_LOGGER_MAX_N_POINTS)  {
		//  programming failure; any additional data will overflow the expected maximum file
		//  size
		log_printf ("eng_logger_collect_chan: not reading channel %d because file is full.\n", chan);
		#ifdef DEBUG
			printf ("Programming error 2 in eng_logger_collect_chan!\n");
		#endif	
		//  skip this collection point; set up to collect data
		//  from this channel next time round
		eng_logger.channel_buff[chan]->collect_time.secs +=
								eng_logger.channel_buff[chan]->delta_t;
		return;
	}		

	//  get the data
	eng_logger.channel_buff[chan]->data[eng_logger.channel_buff[chan]->n_data] = 
													eng_logger_read_channel (chan, 10);

	//  if this is the very first point collected, then we set the first_point_time
	if (eng_logger.channel_buff[chan]->n_data ==    0)  {
		eng_logger.channel_buff[chan]->first_point_time =
				eng_logger.channel_buff[chan]->collect_time;	
//printf ("eng chan %d: first point time set.\n", chan);				
	}
	eng_logger.channel_buff[chan]->n_data++;
//printf ("e chan %d: %d (n_data %d, n_written %ld)\n", chan,
//				eng_logger.channel_buff[chan]->data[eng_logger.channel_buff[chan]->n_data-1],
//				eng_logger.channel_buff[chan]->n_data,
//				eng_logger.channel_buff[chan]->n_written);
	
	if (eng_logger.channel_buff[chan]->n_data >= eng_logger_max_n_data)  {
		//  this data record is full
		printf ("Oops - just filled up data record.\n");
		log_printf ("Oops - just filled up data record.\n");
	}	
	
	//  set up the next time to collect data from this channel
	eng_logger.channel_buff[chan]->collect_time.secs +=
								eng_logger.channel_buff[chan]->delta_t;
}




//  eng_logger_read_channel ()
//
//  Reads an engineering logger channel
//  The channel numbers input to this routine run from
//
int eng_logger_read_channel (int chan, int n_to_average)  {
	int value, count;
	long total;
	long freq, baud;
	ASSERT (n_to_average > 0);


	//  Make sure the clock rate is set at some minimum before we
	//  wait for the interrupt.  The query time routine will print,
	//  so we also adjust the baud rate.  If the current clock
	//  exceed the min, don't bother changing
	freq = SimGetFSys ();
	if (freq < MIN_ENG_READ_FREQ)  {
		baud = SerGetBaud (0, 0);
		SimSetFSys (MIN_ENG_READ_FREQ);
		SerSetBaud (baud, 0);
	}  else
		baud = -1;	
	
	MAX186_shutdown (-1);
	sci_logger_pause_data ();
	MAX186_power_up ();
	sci_logger_resume_data ();

	total = 0;	
	for (count = 0;  count < n_to_average;  count++)  {
		//  Pause the science logger so that we can take control of the SPI
		//  interface.  The pause routine takes care of all the timing so as
		//  to maximize the time available to us here before the science logger
		//  loses data
		sci_logger_pause_data ();

		//  Collect data here
		//  REVIEW - we probably ought to pause briefly after powering up the
		//  186 to allow it to stabilize.
//		value = AtoDReadWord (chan);
		value = MAX186_read_channel (chan);
		sci_logger_resume_data ();

//		printf ("  %d\n", value);
		if (value == 0x8000)  {
			//  if we get an error on any one read, return an error
			total = n_to_average*0x8000;
			break;
		}
		total += value;
	}
	sci_logger_pause_data ();
	MAX186_power_down ();
	MAX186_shutdown (0);
	sci_logger_resume_data ();

	if (baud > 0)  {
		SimSetFSys (freq);
		SerSetBaud (baud, 0);
	}
	return ((short)(total/n_to_average));
}	



//  eng_logger_disk_switch (void)
//
//  Resets variables after a disk switch so that the next write includes
//  all the header information.
//
void eng_logger_disk_switch (void)  {
	int chan;
	//printf ("eng_logger_disk_switch: starting\n");	
	for (chan = 0;  chan < N_ENG_CHANNELS;  chan++)  {
		if (eng_logger.channel_buff[chan] != NULL)
			eng_logger.channel_buff[chan]->n_written = 0;
	}		
	//printf ("eng_logger_disk_switch: ending\n");	
}



//  eng_logger_disk_request ()
//
//	Returns non-zero if the engineering logger would like the disk to be
//  spun up and the writing subroutine called.  The only time critical point
//  is when a channel's buffer fills up.  The program must then write the
//  data to disk before the data logging routine next tries to record a
//  point, or that point will be skipped.
//
//	This routine will request a disk access if there is any data more than
//  one day old, or if a buffer is full.
//	
int eng_logger_disk_request (void)  {
	int request;
	int chan;
	long age;
	
	//  don't do anything unless the logger is open
	if (eng_logger.engineer_open != ENGINEER_INCLUDED)  {
		ASSERT (eng_logger.engineer_open == 0);
		return 0;
	}
	
	request = 0;
	for (chan = 0;  chan < N_ENG_CHANNELS;  chan++)  {
		if (eng_logger.channel_buff[chan] == NULL)
			//  channel not active - skip it
			continue;
			
		//  channel is active; check for full and check for stale data
		//  check if oldest data is more than one day old
		age = eng_logger.channel_buff[chan]->n_data * eng_logger.channel_buff[chan]->delta_t;
		if (age > 86400L)  {
//			printf ("  e_logger has old data.\n");		
			request++;
		}	
			
		//  request disk access if buffer is full		
		if (eng_logger.channel_buff[chan]->n_data >= eng_logger_max_n_data)  {
//			printf ("  e_logger buffer is full.\n");			
			request++;
		}	
		
		//  request disk access if we have just enough points to make a full
		//  length file.
		ASSERT (eng_logger.channel_buff[chan]->n_data+eng_logger.channel_buff[chan]->n_written <= ENG_LOGGER_MAX_N_POINTS);
		if (eng_logger.channel_buff[chan]->n_data + eng_logger.channel_buff[chan]->n_written >=
																		ENG_LOGGER_MAX_N_POINTS)  {
//			printf ("  e_logger has just enough data to fill a file.\n");			
			request++;
		}	
	}		
	return request;
}	



//  eng_logger_write_disk (void)
//
//  Writes engineering data to disk.  This routine should be called
//  any time the disk is spun up so as to empty any waiting data.
//
//  Returns: 0 if OK
//			 non-zero on error
int eng_logger_write_disk (void)  {
	long age;
	int chan;
	dword blocks_total, blocks_free;

	#ifdef DEBUG
	printf ("eng_logger_write_disk(): starting\n");
	#endif

	//  don't do anything unless the logger is open
	if (eng_logger.engineer_open != ENGINEER_INCLUDED)  {
		ASSERT (eng_logger.engineer_open == 0);
		return 0;
	}

	for (chan = 0;  chan < N_ENG_CHANNELS;  chan++)  {
		#ifdef DEBUG
		printf ("eng_logger_write_disk: Chan %d\n", chan);	
		#endif
		if (eng_logger.channel_buff[chan] == NULL)
			//  channel not active - skip it
			continue;
			
		if (eng_logger.channel_buff[chan]->n_data <= 0)
			//  no data to write - skip it
			continue;	
//		printf ("e_logger writing channel %d\n", chan);		
		
		//  if no points have yet been written, write the header
		if (eng_logger.channel_buff[chan]->n_written == 0)  {
			if (eng_logger_write_header (chan) < 0)
				return -1;
		}		
			
		//  write the data only
		if (eng_logger_write_buffer (chan) < 0)
			return -1;
	}		
	return 0;
}	




//  eng_logger_write_header
//
//  Creates and write to disk a header based on the information in the control
//  structure.  Creates the file name and stores it in the control structure
//
int eng_logger_write_header (int chan)  {
	char filename[64];
	int result, offset, fd;
	long msec;
	struct tm* tm;
	struct stat stat;
	extern const float SOFTWARE_VERSION;

	ASSERT (chan >= 0);
	ASSERT (chan < N_ENG_CHANNELS);
//	printf ("e_logger write header\n");
	//  start by zeroing the header
	memset (&eng_logger_header, 0, sizeof (eng_logger_header));

	//  now init the variables one by one
	strcpy (eng_logger_header.magicstring, "DATA");
	sprintf (eng_logger_header.totalhdrs, "1");
	sprintf (eng_logger_header.abbrev, "GEOSens");
	sprintf (eng_logger_header.title, "GEOSense Logger Engineering Data");
	sprintf (eng_logger_header.sampling_period, "%.7f",
				(float)eng_logger.channel_buff[chan]->delta_t);

	//  The number of bits is set at 15 because of the way which the MAX186 returns
	//  data to us.  Only the upper 12 are significant.  The lower 3 should be zero				
	strcpy (eng_logger_header.samplebits, "16");
	strcpy (eng_logger_header.wordsize, "2");

	//  channel number is stored as the station code
	sprintf (eng_logger_header.stationcode, "%d", chan);
	eng_logger_header.typemark = AMPLITUDE;
	eng_logger_header.swapping = UNSWAPPED;
	eng_logger_header.signing = SIGNED;
	eng_logger_header.caltype = CALIBRATED;

	//  min and max are set by the MAX186 input range.
	//  REVIEW - what is the gain of the input buffers?
	sprintf (eng_logger_header.calmin, "%.8f", -4.096F);
	sprintf (eng_logger_header.calmax, "%.8f", 4.096F);
	sprintf (eng_logger_header.calunits, "Volts");
	sprintf (eng_logger_header.recordsize, "512");
	sprintf (eng_logger_header.sourcevers, "%.4f", SOFTWARE_VERSION);

	//  now compute the time.  Don't let the msec exceed 999.
	msec = 1000L * eng_logger.channel_buff[chan]->first_point_time.ticks / GetTickRate ();
	ASSERT (msec <= 1000);
	if (msec >= 1000)  {
		msec = 0;
		eng_logger.channel_buff[chan]->first_point_time.ticks = 0;
		eng_logger.channel_buff[chan]->first_point_time.secs += 1;
	}	

	//  convert time in seconds to the tm structure time
	tm = localtime (&(eng_logger.channel_buff[chan]->first_point_time.secs));
	if (tm->tm_year < 70)
		tm->tm_year += 2000;
	else
		tm->tm_year += 1900;

	//  and write data to the MacTrimpi header
	sprintf (eng_logger_header.year, "%d", tm->tm_year);
	sprintf (eng_logger_header.month, "%02d", 1+tm->tm_mon);
	sprintf (eng_logger_header.day, "%02d", tm->tm_mday);
	sprintf (eng_logger_header.year, "%04d", tm->tm_year);
	sprintf (eng_logger_header.hours, "%02d", tm->tm_hour);
	sprintf (eng_logger_header.minutes, "%02d", tm->tm_min);
	sprintf (eng_logger_header.seconds, "%02d", tm->tm_sec);
	sprintf (eng_logger_header.msec, "%03ld", msec);
	
	//  The header information has been filled in.  Now write it to disk.
	//  Start by creating the file name.  This includes some code to look
	//  for a new filename if this one already exists, even though this
	//  should never happen.
	for (offset = 0;  offset < 10;  offset++)  {
		//  save the file name for later use; don't save the parts that are easy
		//  to recreate
		sprintf (&eng_logger.channel_buff[chan]->fname[0], "\\E%d_%04d\\%02d%02d%02d%02d.ENG", 
					chan, tm->tm_year,
					1+tm->tm_mon, tm->tm_mday, tm->tm_hour, tm->tm_min+offset);
		strcpy (filename, &eng_logger.channel_buff[chan]->fname[0]);
		#ifdef DEBUG
		printf ("eng_logger_write_header: opening %s\n", filename);
		#endif
		fd = logger_disk_open (filename, (PO_BINARY|PO_CREAT|PO_WRONLY), PS_IWRITE);
		if (fd < 0)  {
			//  If we failed to open, then we assume that other 'offsets' will also
			//  fail.  Don't bother with additional attempts.
			log_printf ("eng_logger_write_header: file ('%s') open failure.\n", filename);
			log_printf ("  Error %d => %s.\n", get_errno (), get_errno_string(-1));
			return -1;
		}
		critical_error_reset ();
		if (pc_fstat (fd, &stat) != 0)  {
			//  If we stat failed, then we assume that other 'offsets' will also
			//  fail.  Don't bother with additional attempts.
			log_printf ("eng_logger_write_header: file ('%s') fstat failure.\n", filename);
			log_printf ("  Error %d => %s.\n", get_errno (), get_errno_string(-1));
			critical_error_reset ();
			po_close (fd);
			return -1;
		}
		//  If size is 0, then all is well - go to write portion of code
		if (stat.st_size == 0)
			break;
			
		//  If not zero, try next offset after closing this file			
		critical_error_reset ();
		po_close (fd);
		log_printf ("eng_logger_write_header: Filename %s already exists\n", filename);
	}
	if (offset >= 10)  {
		//  all opened files have already been closed
		log_printf ("eng_logger_write_header: Aborting after repeated open attempts.\n");
		return -1;
	}
	
	ASSERT (sizeof (eng_logger_header) == 512);
	critical_error_reset ();
	result = po_write (fd, (byte*)&eng_logger_header, sizeof (eng_logger_header));
	critical_error_reset ();
	po_flush (fd);
	critical_error_reset ();
	po_close (fd);

	if (result != sizeof (eng_logger_header))  {
		//  error writing
		log_printf ("eng_logger_write_header: file ('%s') write failure.\n", filename);
		log_printf ("  Tried to write %d, got %d\n", sizeof (eng_logger_header), result);
		log_printf ("  Error %d => %s.\n", get_errno (), get_errno_string(-1));
		return -1;
	}
	return 0;
}



//  eng_logger_write_buffer (int chan)
//
//	Writes the specified channel buffer to disk.  The disk should already
//  be powered up.
//
int eng_logger_write_buffer (int chan)  {
	int fd, year, result, n_bytes;
	char filename[64];
	
//	printf ("e_logger write buffer\n");	
	strcpy (filename, eng_logger.channel_buff[chan]->fname);

	//  we shouldn't be creating the file at this stage.  It MUST already
	//  exist because we MUST have written the header already.  We still
	//  use the normal disk_open routine because it allows us to add
	//  the disk partition letter.
	//  REVIEW - this can fail is a previous write failure causes us to
	//  switch partitions in the middle of a file.
	fd = logger_disk_open (filename,  (PO_BINARY|PO_WRONLY), PS_IWRITE);
	if (fd < 0)  {
		log_printf ("eng_logger_write_buffer: file ('%s') open failure.\n", filename);
		log_printf ("  Error %d => %s.\n", get_errno (), get_errno_string(-1));
		return fd;
	}

	//  seek to the end of the file so we don't overwrite any previous data
	critical_error_reset ();
	if (po_lseek (fd, 0L, PSEEK_END) < 0)  {
		log_printf ("eng_logger_write_buffer: file ('%s') lseek failure.\n", filename);
		log_printf ("  Error %d => %s.\n", get_errno (), get_errno_string(-1));
		critical_error_reset ();
		po_close (fd);
		return -1;
	}
	
	n_bytes = sizeof(short) * eng_logger.channel_buff[chan]->n_data;
//	printf ("e_logger write buffer: Writing %d byte block of data\n", n_bytes);
	critical_error_reset ();
	result = po_write (fd, (byte*)&eng_logger.channel_buff[chan]->data[0], n_bytes);
	critical_error_reset ();
	po_flush (fd);
	critical_error_reset ();
	po_close (fd);

	if (result != n_bytes)  {
		log_printf ("eng_logger_write_buffer: file ('%s') write failure.\n", filename);
		log_printf ("  Error %d => %s.\n", get_errno (), get_errno_string(-1));
		log_printf ("  Tried to write %d, got %d\n", n_bytes, result);
		return -1;
	}

	eng_logger.channel_buff[chan]->last_disk_write = logger_time_get_time ();
	eng_logger.channel_buff[chan]->n_written += eng_logger.channel_buff[chan]->n_data;
	eng_logger.channel_buff[chan]->n_data = 0;
	//  if this file has reached the maximum length, then force the start of a new file.
	#ifdef DEBUG
	printf ("Wrote %ld points total to '%s'\n", eng_logger.channel_buff[chan]->n_written, filename);
	#endif
	if (eng_logger.channel_buff[chan]->n_written >= ENG_LOGGER_MAX_N_POINTS)  {
		eng_logger.channel_buff[chan]->n_written = 0;
	}		
	return 0;
}





//  eng_logger_print_file
//
//	The engineering logger files are currently written in the MacTrimpi format
//
int eng_logger_print_file (int fd)  {

	return MacTrimpi_print_file (fd);
}



