 1Content-type: application/x-Unknown; charset=UTF8
package nmc.sidekick;
import javax.comm.SerialPort;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.lang.Exception;
import nmc.common.TimestampedData;
import java.util.Date;
import java.lang.String;

import nmc.common.*;

// This driver has not been tested since the multi-threaded
// driver model was implemented.

public class asimetHRH extends StdInstrumentSensorDriver {

    // ASIMET commands need to be built dynamically
    // because they use the logical address of the
    // instrument, which may change during the course
    // of a deployment if instruments are swapped.
    // ASIMET commands have the format
        
    // #TTTNNc
     
    // where
    // TTT : module type (BPR|HRH|LWR|PRC|SST|SWR|WND)
    // NN  : module 'serial number' (address) (00-32 w/o repeaters)
    // c   : command (may be more than one character)

    // type and serial number should not be hard
    // coded to enable the driver to accomodate
    // changes of instruments in the field and
    // so that one driver may be used for 
    // different ASIMET modules        
    String _type;		// (BPR|HRH|LWR|PRC|SST|SWR|WND)
    String _sn;		// (00-32)
    String _cmdPrefix;	// e.g. '#'
    String _last_type;  
    String _last_sn;	
    String _new_type;  
    String _new_sn;	
    
        
    // Communication Configuration Options
    String _commMode;// ("RS485"|"RS232")
    boolean _isMultidrop; 
        
    // Note: ASIMET modules do not wait for end-of-line
    // to execute a command, and doesn't echo crlf

    // ASIMET Commands (may vary by sensor):

    // Data may be returned as (R)aw, (C)alibrated, or (B)oth
    // This should also be a driver configuration option.
    // We may also want to include options to select any
    // meta-data that might be returned.
    String _sampleCmd;		// ("R"|"C"|"B")
    String _addressAck;		// ID check
    byte[] _addressAckReply;
    String _setDate;
    String _getIDInfo;
    String _getCalInfo;
    String _setCfgInfo;

    // Configuration
    String _modAdr; // module address
    String _cal1A;  // Calibration constant 1A
    String _cal1B;  // Calibration constant 1B
    String _cal1C;  // Calibration constant 1C
    String _cal1D;  // Calibration constant 1D
    String _cal1E;  // Calibration constant 1E
    String _cal2A;  // Calibration constant 2A
    String _cal2B;  // Calibration constant 2B
    String _cal2C;  // Calibration constant 2C
    String _cal2D;  // Calibration constant 2D
    String _cal2E;  // Calibration constant 2E
    
    byte[] _cfgMenuPrompt;	// Configuration menu prompt
    byte[] _cfgWriteChanges;	// Configuration menu write change command
    byte[] _cfgSelModAdr;	// Configuration menu Select Modify Address command
    byte[] _cfgSelCalCon;	// Configuration menu Select Calibration Constants
    int _pace;			// Milliseconds of inter-character pacing
    int _maxIDBytes;		// Maximum bytes for ID info
    int _maxCalBytes;		// Maximum bytes for Calibration info
    
    // Configuration Change State Variables
    boolean	 _addrChangeRequested;
    boolean	 _addrChangeAccepted;
    boolean  _cal1AChangeRequested;
    boolean  _cal1BChangeRequested;
    boolean  _cal1CChangeRequested;
    boolean  _cal1DChangeRequested;
    boolean  _cal1EChangeRequested;
    boolean  _cal2AChangeRequested;
    boolean  _cal2BChangeRequested;
    boolean  _cal2CChangeRequested;
    boolean  _cal2DChangeRequested;
    boolean  _cal2EChangeRequested;

    boolean  _doPowerCycle;

    
    asimetHRH ( ) {

        _name		= "TheASIMET_HRHDriverRev1.0";   
        _enter		= "\r"      .getBytes();
        _type		= "HRH";
        _sn			= "02";
        _last_sn		= _sn;
        _last_type	= _type;
        _new_sn		= _sn;
        _new_type		= _type;
        
        // ASIMET's 'prompt' is the ETX character (0x03)
        _prompt         = "\r\n\003"  .getBytes();
        _cmdPrefix	= "#";
        
        // Communication Configuration Options
        // (for future use)
        _commMode = "RS485";
        _isMultidrop = false;
        
        // Note: ASIMET modules do not wait for end-of-line
        // to execute a command, and doesn't send crlf before
        // returning results

        // Commands (may vary by sensor):
        _sampleCmd	 = "B";   // (R)aw|(C)alibrated|(B)oth
	  _addressAck      = "A";   // (_cmdBase+"A")         .getBytes();
	  _addressAckReply = (_type+_sn+"\r\n\003") .getBytes();
	  _setDate         = "D";   // (_cmdBase+"D")         .getBytes();
	  _getIDInfo       = "I";   // (_cmdBase+"I")         .getBytes();
	  _getCalInfo      = "L";   // (_cmdBase+"L")         .getBytes();
	  _setCfgInfo      = "UOK"; // (_cmdBase+"UOK")       .getBytes();
	  
        _cfgEol          = "\r\n"                 .getBytes();
 	  _cfgMenuPrompt   = "Enter selection ->"   .getBytes();
 	  _cfgWriteChanges = "9\r" 			  .getBytes();
 	  _cfgSelModAdr    = "1\r" 			  .getBytes();
 	  _cfgSelCalCon    = "8\r" 			  .getBytes();
 	  
	  // C: These seem like they should be arguments to the 
	  //    methods that use them
        _currentLimit		= 1200; // 11 steps rounded down (120 mA/step) = 1400 mA
        _timeout			= 15000;
        _maxTries        		= 3;
        _maxAttnBytes     		= 250;
        _maxSampleBytes   	= 1000;
        _initMaxTries     		= 3;
        _maxSkipBytes     	= 512;
        _maxIDBytes	 	= 512;	// ID info buffer size
        _maxCalBytes      		= 512;	// Cal info buffer size
        _cfgBufSize       		= 512;	// Config menu buffer size
	  _pace             		= 200;	// Character pacing delay (ms)
       _powerPolicy                      = POWER_WHEN_SAMPLING;

    }//end asimetHRH()


    /** Make command using current ID*/
    private byte[] mkCmd(String cmdSuffix) throws Exception{
    	byte[] command=(_cmdPrefix+_type+_sn+cmdSuffix).getBytes();
    	return command;
    }
    
    /** Read a data sample from device communications interface. */
    public void initialize(){
    	// This should put it into lowest power state according
    	// to the powerPolicy set in the ctor
	super.initialize();
	if(_dpaChannel != null){
	   DebugMessage.println("Instrument specific initialization for  "+_name);
	   DebugMessage.println("Setting current limit = "+_currentLimit);
	   _dpaChannel.setCurrentLimit(_currentLimit);
			   	   
	}else{
	   DebugMessage.println("Driver: "+ _name+" initialize(): DPA channel is NULL");	
	}
	DebugMessage.println("Done with initialization");

    }// end initialize()

    /** Read a data sample from device communications interface. */
    //public TimestampedData poll() throws Exception {
    public void poll(PollCmd m) throws Exception{
        int tries = 0;
        TimestampedData retVal;

       // turn on communications power
       managePowerWake();

        while (tries < _maxTries) {
            tries++;
            try {
                byte[] theSample = getHRHSample();
                TimestampedData retval = new TimestampedData(theSample);
	        // Now we put the message into an outbound MessageQ, 
	        // instead of returning the TimestampedData.
	        // We may need some Message packetizing here...
                
                PolledDataReply pdr = new PolledDataReply(m,retval);
                _outbound.put(pdr);

               // turn off communications power
               managePowerSleep();

                return;

            } catch (TimeoutException e) {
                DebugMessage.println("Driver: '"
                                   + _name
                                   + "' got timeout");
            } // other Exceptions fly past into the caller
        }
        // turn off communications power
        managePowerSleep();
        
       // throw new Exception("Retry limit exceeded (retries=" + tries + ")");
        DebugMessage.println(_name+": Retry limit exceeded (retries=" + tries + ")");
        byte[] retryMsg=(_name+": Retry limit exceeded (retries=" + tries + ")").getBytes();
        TimestampedData retryOut=new TimestampedData(retryMsg);
        PolledDataReply edr = new PolledDataReply(m,retryOut);
        _outbound.put(edr);
    }//end poll()

    /** Device specific sample acquisition */
    private byte[] getHRHSample() throws Exception {
    	
    	// If the driver does it's own serial setup, do it here...
    	// _commPort.setSerialPortParams(baud,byteSize,stopBits,parity);
	// _commPort.setFlowControlMode();


	// If RS485 multidrop, get config or probe for instruments...	
	
	// Verify connection...
	//System.out.println("Verifying Connection...");
	getSane();
	
	// Get data sample
	System.out.println("Requesting Sample...");
      write( _output, mkCmd(_sampleCmd) );
      byte[] sampleBuf = new byte[_maxSampleBytes];
      int bytesCaptured = readUntil( _input, sampleBuf, _prompt );

	// Do any data processing here...

      // copy the sample into the exactly-right-sized byte array
      byte[] retval = new byte[bytesCaptured];
      for (int i = 0; i < bytesCaptured; i++) {
          retval[i] = sampleBuf[i];
       }
	
	System.out.println(_name+" Returning...");
      
      return retval;
    }//end getHRHSample()


	// (klh)
    /** Read instrument configuration from device communications interface. */
    public TimestampedData getConfiguration() throws Exception {
        int tries = 0;
        TimestampedData retVal;

        while (tries < _maxTries) {
            tries++;
            try {
                byte[] theSample = getInstrumentConfiguration();
                TimestampedData retval = new TimestampedData(theSample);
                return retval;
            } catch (TimeoutException e) {
                System.out.println("getInstrumentStatus: ('"
                                   + _name
                                   + "') got timeout");
            } // other Exceptions fly past into the caller
        }
        
        throw new Exception("Retry limit exceeded (retries=" + tries + ")");
    }//end getConfiguration()

	// (klh)
    private byte[] getInstrumentConfiguration() throws Exception {
    	
	// This method should return any available configuration
	// information that is available from the instrument
	
	// Verify connection...
	//System.out.println("Verifying Connection...");
	getSane();

	// Get config info...
	System.out.println("Requesting ID Info...");
      write( _output, mkCmd(_getIDInfo) );
      byte[] IDBuf = new byte[_maxIDBytes];
      int IDBytes = readUntil( _input, IDBuf, _prompt );

	// Get cal info...
	System.out.println("Requesting Cal Info...");
      write( _output, mkCmd(_getCalInfo) );
      byte[] calBuf = new byte[_maxCalBytes];
      int calBytes = readUntil( _input, calBuf, _prompt );
      
      // Get driver config info...
      String drvConfig="";
	drvConfig+= "\n_sampleCmd:"+_sampleCmd+"\n";      	
	drvConfig+="_sn:"+_sn+"\n";
	drvConfig+="_type:"+_type+"\n";
	
      // copy the sample into the exactly-right-sized byte array
      byte[] retval = new byte[IDBytes+calBytes+drvConfig.length()];
      for (int i = 0; i < IDBytes; i++) {
          retval[i] = IDBuf[i];
       }
      for ( int i = 0; i < calBytes; i++) {
          retval[IDBytes+i] = calBuf[i];
       }
      for ( int i = 0; i < drvConfig.length(); i++) {
          retval[IDBytes+calBytes+i] = (byte)drvConfig.charAt(i);
       }
       
	
	System.out.println(_name+" Returning...");
      
      return retval;
    }//end getInstrumentConfiguration()

    /** Configure instrument */
    public TimestampedData configure(byte[] cfgData) throws Exception {
        System.out.println("We are in the driver \""
                           + _name
                           + "\" with configuration data="
                           + new String(cfgData));

        byte[] tempBuf = new byte[ _cfgBufSize ];

        ByteArrayOutputStream cfgRecord = new ByteArrayOutputStream();

        ByteArrayInputStream cfgSource = new ByteArrayInputStream( cfgData );

	  // The ASIMET poses a configuration challenge, since
	  // it uses a menu to do configuration, which does not
	  // fit into the StdInstrumentDriver model of sending
	  // single instructions to the instrument.
	  //
	  // Here, it is assumed that the configuration byte array
	  // contains parameter="value" pairs (one per line). The configure method
	  // parses these out and uses them to configure the instrument.
	  // The configure method manages the navigation through the menu.
	  
	  // For now, the configuration is not verified, but the information
	  // will be read back and returned to the caller, offering an
	  // incremental improvement over the StdInstrumentDriver, which
	  // sends back only its commands.
	  
	  // The basic strategy is to parse all of the parameters that need
	  // to be set, then make one pass through the configuration
	  // menu. Once all parameters have all been set successfully,
	  // the changes are written to the BB_RAM area.
	  
        // Warning: This parsing is pretty fast and loose at this stage.
        // Notes:
        // - The ASIMET seems to drop characters at times; have only seen
        //   it on the HRH02UOK command so far. Even with 200 ms pacing
        //   delay, this command is sometimes missed!


        int count;
        boolean setCalConstants=false;
	  _doPowerCycle=false; // Configuration changes to BB_RAM require power cycle 

        _modAdr="";
        _cal1A="";
        _cal1B="";
        _cal1C="";
        _cal1D="";
        _cal1E="";
        _cal2A="";
        _cal2B="";
        _cal2C="";
        _cal2D="";
        _cal2E="";

	  _addrChangeRequested=false;
	  _cal1AChangeRequested=false;
	  _cal1BChangeRequested=false;
	  _cal1CChangeRequested=false;
	  _cal1DChangeRequested=false;
	  _cal1EChangeRequested=false;
	  _cal2AChangeRequested=false;
	  _cal2BChangeRequested=false;
	  _cal2CChangeRequested=false;
	  _cal2DChangeRequested=false;
	  _cal2EChangeRequested=false;
	  
	  
        while ( cfgSource.available() > 0 ) {

        	
        	// get one configuration line
            count = readUntil( cfgSource, tempBuf, _cfgEol );
            System.out.println("forming string from tempBuf contents...");
            
            // look for param="value" pairs 
            // (assumes one per line, value quoting strictly enforced)
            // (address, cal constants supported)
            String s = new String( tempBuf, 0, count );
            s.trim();
            
		int iEqual=s.indexOf('=');
            int iOpenQuote=s.indexOf("\"",0);
            int iCloseQuote=s.lastIndexOf('\"');
            String param;
            String value;
            
            // is there a StringTokenizer we could use?
	if(iEqual>=1 && iOpenQuote>iEqual && (iCloseQuote>(iOpenQuote+1)) ){
              param = new String(s.substring(0,iEqual).toUpperCase());
              value = new String(s.substring( iOpenQuote+1 , iCloseQuote ));
	}else{
		  param = new String("no param");
		  value = new String("no value");
	}
	String tmpValue="";

            try{		  
		    // Change sample command
                if(param.equals("SMPCMD")){
		      tmpValue = value;
		      System.out.println("got SMPCMD: "+ tmpValue+" "+tmpValue.length());
		      // Should verify that it is a valid value
			if(tmpValue.charAt(0)=='B') 
			  _sampleCmd="B";
			if(tmpValue.charAt(0)=='R') 
			  _sampleCmd="R";
			if(tmpValue.charAt(0)=='C') 
			  _sampleCmd="C";
  	            System.out.println("new _sampleCmd = "+ _sampleCmd);		      

                }// end SMPCMD

		    // Change module address (e.g. HRH01)
                if(param.equals("MODADR")){
                	
                	if(value.length()==5){
		        // Should verify that it is a valid value     
		        _new_type = value.substring(0,3);
		        _new_sn = value.substring(3,5);
		        int i = Integer.parseInt(_new_sn);
		        
		        if(_new_type.equals("HRH") && i>=0 && i<=32){
		          _modAdr = value;
		          _addrChangeRequested = true;
		          System.out.println("got MODADR: "+ _modAdr+" "+_modAdr.length());
		          System.out.println("new Type = "+ _new_type +" new SN = "+ _new_sn);		      
		        }else{
		          System.out.println("invalid modAdr value "+value);
		        }
                	}else{
                	   System.out.println("invalid modAdr length "+value+" "+value.length());
                	}  
		      
                }// end MODADR

	    // Change calibration constants 
	    // Two (1,2) sets of 5 (A-E) constants 
                if(param.equals("CAL_1A")){
            	_cal1A = value;
		System.out.println("got CAL_1A: "+_cal1A);
		 // Should verify that it is a valid value...
		 // Need String->Float parse routine
		 if( isDouble(_cal1A) ){
		   setCalConstants=true;
		   _cal1AChangeRequested=true;
		 }else{
		   System.out.println("Invalid calibration constant "+_cal1A);
		 }
                }// end CAL_1A

                if(param.equals("CAL_1B")){
                  _cal1B = value;
	      System.out.println("got CAL_1B: "+_cal1B);
	      // Should verify that it is a valid value...
	      // Need String->Float parse routine
	      
	      if( isDouble(_cal1B) ){
	        setCalConstants=true;
	        _cal1BChangeRequested=true;
	      }else{
	        System.out.println("Invalid calibration constant "+_cal1B);
	      }
		      
	      setCalConstants=true;
	      _cal1BChangeRequested=true;
                }// end CAL_1B
              
                if(param.equals("CAL_1C")){
                  _cal1C = value;
	      System.out.println("got CAL_1C: "+_cal1C);
	      // Should verify that it is a valid value...
	      // Need String->Float parse routine
		      
	      if( isDouble(_cal1C) ){
	        setCalConstants=true;
	        _cal1CChangeRequested=true;
	      }else{
	        System.out.println("Invalid calibration constant "+_cal1C);
	      }
	      
	      setCalConstants=true;
	      _cal1CChangeRequested=true;
                }// end CAL_1C

                if(param.equals("CAL_1D")){
            	_cal1D = value;
		      System.out.println("got CAL_1D: "+_cal1D);
		      // Should verify that it is a valid value...
		      // Need String->Float parse routine

	      if( isDouble(_cal1D) ){
	        setCalConstants=true;
	        _cal1DChangeRequested=true;
	      }else{
	        System.out.println("Invalid calibration constant "+_cal1D);
	      }
		      
		      setCalConstants=true;
		      _cal1DChangeRequested=true;
                }// end CAL_1D

                if(param.equals("CAL_1E")){
            	_cal1E = value;
		      System.out.println("got CAL_1E: "+_cal1E);
		      // Should verify that it is a valid value...
		      // Need String->Float parse routine
		      
	      if( isDouble(_cal1E) ){
	        setCalConstants=true;
	        _cal1EChangeRequested=true;
	      }else{
	        System.out.println("Invalid calibration constant "+_cal1E);
	      }
		      setCalConstants=true;
		      _cal1EChangeRequested=true;
                }// end CAL_1E

                if(param.equals("CAL_2A")){
            	_cal2A = value;
		      System.out.println("got CAL_2A: "+_cal2A);
		      // Should verify that it is a valid value...
		      // Need String->Float parse routine

	      if( isDouble(_cal2A) ){
	        setCalConstants=true;
	        _cal2AChangeRequested=true;
	      }else{
	        System.out.println("Invalid calibration constant "+_cal2A);
	      }
		      
		      setCalConstants=true;
		      _cal2AChangeRequested=true;
                }// end CAL_2A

                if(param.equals("CAL_2B")){
            	_cal2B = value;
		      System.out.println("got CAL_2B: "+_cal2B);
		      // Should verify that it is a valid value...
		      // Need String->Float parse routine
	      if( isDouble(_cal2B) ){
	        setCalConstants=true;
	        _cal2BChangeRequested=true;
	      }else{
	        System.out.println("Invalid calibration constant "+_cal2B);
	      }
		      
		      setCalConstants=true;
		      _cal2BChangeRequested=true;
                }// end CAL_2B
              
                if(param.equals("CAL_2C")){
            	_cal2C = value;
		      System.out.println("got CAL_2C: "+_cal2C);
		      // Should verify that it is a valid value...
		      // Need String->Float parse routine
	      if( isDouble(_cal2C) ){
	        setCalConstants=true;
	        _cal2CChangeRequested=true;
	      }else{
	        System.out.println("Invalid calibration constant "+_cal2C);
	      }
		      
		      setCalConstants=true;
		      _cal2CChangeRequested=true;
                }// end CAL_2C

                if(param.equals("CAL_2D")){
            	_cal2D = value;
		      System.out.println("got CAL_2D: "+_cal2D);
		      // Should verify that it is a valid value...
		      // Need String->Float parse routine
	      if( isDouble(_cal2D) ){
	        setCalConstants=true;
	        _cal2DChangeRequested=true;
	      }else{
	        System.out.println("Invalid calibration constant "+_cal2D);
	      }
		      
		      setCalConstants=true;
		      _cal2DChangeRequested=true;
                }// end CAL_2D

                if(param.equals("CAL_2E")){
            	_cal2E = value;
		      System.out.println("got CAL_2E: "+_cal2E);
		      // Should verify that it is a valid value...
		      // Need String->Float parse routine
	      if( isDouble(_cal2E) ){
	        setCalConstants=true;
	        _cal2EChangeRequested=true;
	      }else{
	        System.out.println("Invalid calibration constant "+_cal2E);
	      }
		      
		      setCalConstants=true;
		      _cal2EChangeRequested=true;
                }// end CAL_2E
            }catch(Exception e){
              	System.out.println("Parse error in Configure (parsing config strings)");
              	System.out.println(e.toString());
              	//Date t = new Date(System.currentTimeMillis());
              	//System.out.println("Error occured at "+t.toString());
              	return null;
            }//end catch

            /* We need to do more than return the
               commands we sent...more below
               
            String s = new String( tempBuf, 0, count );
            System.out.println("returned (1) from readUntil with tempBuf=" + s);
            write( _output, tempBuf, count);
            System.out.println("wrote tempBuf to _output");
            write( _output, _enter );
            System.out.println("wrote _enter to _output");
            count = readUntil( _input, tempBuf, _prompt );
            String s2 = new String(tempBuf, 0, count);
            System.out.println("readUntil (2) returned with tempBuf=" + s2);
            write( cfgRecord, tempBuf, count + _prompt.length );
            */
        } //end while
        
        // Now actually set parameters that are stored in BB_RAM
    	  try{
	  	
	    // Note: added method writePace(), which wraps
	    // the other write methods, adding delay between
	    // characters, since ASIMET seems to have trouble
	    // keeping up.

	    // Verify Connection...
	    getSane();
	    
          // Enter the configuration menu
          System.out.println("set cfg Info menu...."+new String(_setCfgInfo));
 
          writePace(_output,mkCmd(_setCfgInfo),_pace);
          skipUntil(_input,_cfgMenuPrompt);
              
          // Navigate Menu
          // Should return to menu after setting each parameter
          
          // Note: Something needed to get instrument to a known
          //       state, even if deep in the menus...
              
          // Set Module Address...
          if(_addrChangeRequested==true){
          	
            System.out.println("setting MODADR...");
            
            _addrChangeAccepted=false;
            
            flushInput();
            writePace(_output,_cfgSelModAdr,_pace);
            skipUntil(_input,"new address ->".getBytes());
            flushInput();
            writePace(_output,_modAdr.getBytes(),_pace);
            writePace(_output,"\r".getBytes(),_pace);
            skipUntil(_input,"accept ->".getBytes());
                
		flushInput();
		writePace(_output,"Y".getBytes(),_pace);
		skipUntil(_input,"accepted".getBytes());
		    
		  
		//Return to the Config Menu Prompt...
		flushInput();
		writePace(_output,"\r".getBytes(),_pace);
		skipUntil(_input,_cfgMenuPrompt);
		
		_last_type = _type;
		_last_sn   = _sn;    		    
		
		_addrChangeAccepted=true;
		_doPowerCycle=true;
          }
              
          // Make one trip through the cal constants menu
          // Each cal constant should return to the
          // Cal Constant Sub-Menu
          if(setCalConstants){
              	
	      // Select Cal constant sub-menu
	      flushInput();
	      writePace(_output,_cfgSelCalCon,_pace);
	      skipUntil(_input,"RETURN to exit ->".getBytes());
              	
            // Set Calibration Constants...
            if(_cal1AChangeRequested==true){
	        System.out.println("setting CAL1A...");
              setCalConstant("1\r","Cal constant set #1","1\r",_cal1A);
/*
		  // Select Cal constant set sub-menu
		  flushInput();
		  writePace(_output,"1\r".getBytes(),_pace);
		  skipUntil(_input,"Cal constant set #1".getBytes());
		  skipUntil(_input,"RETURN to exit ->".getBytes());

		  // Select Cal constant
		  flushInput();
		  writePace(_output,"1\r".getBytes(),_pace);
		  skipUntil(_input,"New constant value =".getBytes());

		  // Set Cal constant
		  flushInput();
		  writePace(_output,(_cal1A+"\r").getBytes(),_pace);
		  skipUntil(_input,"RETURN to exit ->".getBytes());
		    
		  // Return to Cal constant sub-menu
		  flushInput();
		  writePace(_output,"\r".getBytes(),_pace);
		  skipUntil(_input,"Enter cal constant".getBytes());
		  skipUntil(_input,"RETURN to exit ->".getBytes());
*/		  
		  _doPowerCycle=true;
            }// end if cal1A
            
            if(_cal1BChangeRequested==true){
	        System.out.println("setting CAL1B...");
              setCalConstant("1\r","Cal constant set #1","2\r",_cal1B);
		  _doPowerCycle=true;
            }// end if cal1B

            if(_cal1CChangeRequested==true){
	        System.out.println("setting CAL1C...");
              setCalConstant("1\r","Cal constant set #1","3\r",_cal1C);
		  _doPowerCycle=true;
            }// end if cal1C

            if(_cal1DChangeRequested==true){
	        System.out.println("setting CAL1D...");
              setCalConstant("1\r","Cal constant set #1","4\r",_cal1D);
		  _doPowerCycle=true;
            }// end if cal1D

              	
            if(_cal1EChangeRequested==true){
		  System.out.println("setting CAL1E...");
              setCalConstant("1\r","Cal constant set #1","5\r",_cal1E);
		  _doPowerCycle=true;
            }// end if cal1E

            if(_cal2AChangeRequested==true){
	        System.out.println("setting CAL2A...");
              setCalConstant("2\r","Cal constant set #2","1\r",_cal2A);
		  _doPowerCycle=true;
            }// end if cal2A

            if(_cal2BChangeRequested==true){
	        System.out.println("setting CAL2B...");
              setCalConstant("2\r","Cal constant set #2","2\r",_cal2B);
		  _doPowerCycle=true;
            }// end if cal2B

            if(_cal2CChangeRequested==true){
	        System.out.println("setting CAL2C...");
              setCalConstant("2\r","Cal constant set #2","3\r",_cal2C);
		  _doPowerCycle=true;
            }// end if cal2C

            if(_cal2DChangeRequested==true){
	        System.out.println("setting CAL2D...");
              setCalConstant("2\r","Cal constant set #2","4\r",_cal2D);
		  _doPowerCycle=true;
            }// end if cal2D

            if(_cal2EChangeRequested==true){
	        System.out.println("setting CAL2E...");
              setCalConstant("2\r","Cal constant set #2","5\r",_cal2E);
		  _doPowerCycle=true;
            }// end if cal2E


	      // Return to Configuration menu
	      flushInput();
	      writePace(_output,"\r".getBytes(),_pace);
	      skipUntil(_input,"Enter selection ->".getBytes());
          }// end if setCalConstants
              
              
          // Now all options have been set...
          // Assuming we are at the config menu prompt,
          // write the changes to BB_RAM
          System.out.println("writing changes...");
	    flushInput();
          write(_output,_cfgWriteChanges);
	    skipUntil(_input,_prompt);
		  
	    // For changes to BB_RAM to take effect,
	    // the power must be cycled
	    
	    // Save the current settings...
	    _last_sn = _sn;
	    _last_type = _type;
	    System.out.println("last_sn: "+_last_sn+" last_type: "+_last_type);
	    
	    if(_doPowerCycle){
	    	System.out.println("Cycling Power...");	    	  
	      // Cycle Power...
	      // (where's the on/off switch?)
     	    	System.out.println("Power Cycle Complete");	    	  
	    }
	    
	    if(_addrChangeAccepted){
	      // Set new values... 
	      _type = _new_type;
	      _sn = _new_sn;
	      _addressAckReply = (_type+_sn+"\r\n\003") .getBytes();

	    	System.out.println("Verifying new address "+_type+" "+_sn);
		  
	      // Check new address is valid...
	      if(getPrompt()){
 	  	  System.out.println("Address Change Complete");
    	      }else{
 	   	  System.out.println("Address Change error, trying last good address...");
	        // If not, try last good address
	        _type = _last_type;
	        _sn = _last_sn;
	        _addressAckReply = (_type+_sn+"\r\n\003") .getBytes();
		  
	        // If success, restore old address and flag error
	        // If failure, we are well and truly lost...
 	  	  if(getPrompt()){
 	  	    System.out.println("Still using the old sn and type: "+_type+" "+_sn);
 	  	  }else{
  	  	    System.out.println("I don't know who I am anymore: "+_type+" "+_sn);	  	
 	  	  }// end else
    	      }// end else
	    }// end if addrChangeAccepted	  		
	  }catch(Exception e){
	    System.out.println("Parse error in Configure (setting config options)");
          Date t = new Date(System.currentTimeMillis());
          System.out.println("Error occured at "+t.toString());
          byte[] errBytes="Configure(): Error setting config params".getBytes();
          cfgRecord.close();
          cfgSource.close();
          TimestampedData errval = new TimestampedData( errBytes );
          return errval;
        }//end catch
        
        //byte[] result = cfgRecord.toByteArray();
        
        // Get instrument status information; this is 
        // what we'll return for now, to verify that our
        // changes have taken place
        byte[] result = getInstrumentConfiguration();
        cfgRecord.close();
        cfgSource.close();
        TimestampedData retval = new TimestampedData( result );
        return retval;
      }// end Configure()

    
    /** Write string to instrument, insert delay between chars */
    protected void writePace ( OutputStream ostream, byte[] src, int msDelay ) 
        throws IOException {
	  byte[] b1="\0".getBytes();
	  long now;
	  for(int i=0;i<src.length;i++){
	    b1[0]=src[i];
          write( ostream, b1, b1.length );
          for(now=System.currentTimeMillis();(System.currentTimeMillis()-now)<msDelay;);
	  }
	  return;
    }// end writePace()
    
    // Attempt to get instrument to a known state...
    protected void getSane ( ) throws Exception {
    	System.out.println("going sane...");
    	
    	// Try to get prompt; Hopefully this won't do
    	// anything bad if in some menu....
    	
    	if(getPrompt()){
    	  System.out.println("sanity check ok");
	  return;
    	}
    	
    	// Eat any recent output
    	flushInput();
    	
    	// A series of (7) carriage returns
    	// followed by zero are used to bring it up
    	// from anywhere in the configuration menu.
	writePace(_output,"\r\r\r\r\r\r0".getBytes(),400);
	
    	if(getPrompt()){
    	  System.out.println("sanity check ok");
    	}else{
 	  System.out.println("certifiable!");    	
    	} 
	
	return;
    }// end getSane()
    
    /** Get prompt */
    protected boolean getPrompt(){

      try{    	
    	  // Consume any recent input...
        flushInput();
      
	  // Do a sanity check by doing an AddressAck	
        write( _output, mkCmd(_addressAck) );
      
	  int s=skipUntil( _input,_addressAckReply );
    	
      	if(s>=0){
 	        System.out.println("prompt ok");
		  return true;
    	  }else{
 	  	System.out.println("no prompt");
		return false;
    	  } 
      }catch(Exception e){
         System.out.println("getPrompt: "+e.toString());
         return false;
      }// end catch
       
    } // end getPrompt()
    
    /** Set calibration constant (assumed to be in correct menu state) */
    protected void setCalConstant(String set, String prompt, String con, String val) throws Exception{
    	
	// Select Cal constant set sub-menu
	flushInput();
	writePace(_output,set.getBytes(),_pace);
	skipUntil(_input,prompt.getBytes());
	skipUntil(_input,"RETURN to exit ->".getBytes());

	// Select Cal constant
	flushInput();
	writePace(_output,con.getBytes(),_pace);
	skipUntil(_input,"New constant value =".getBytes());

	// Set Cal constant
	flushInput();
	writePace(_output,(val+"\r").getBytes(),_pace);
	skipUntil(_input,"RETURN to exit ->".getBytes());
		    
	// Return to Cal constant sub-menu
	flushInput();
	writePace(_output,"\r".getBytes(),_pace);
	skipUntil(_input,"Enter cal constant".getBytes());
	skipUntil(_input,"RETURN to exit ->".getBytes());

      return;
    
    } //end setCalConstant()

}// end class asimetHRH

