Package net.geco.control

Source Code of net.geco.control.SIReaderHandler$SerialPort

/**
* Copyright (c) 2009 Simon Denier
* Released under the MIT License (see LICENSE file)
*/
package net.geco.control;

import gnu.io.CommPortIdentifier;

import java.util.ArrayList;
import java.util.Date;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Properties;
import java.util.Vector;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import net.geco.basics.Announcer;
import net.geco.basics.GecoRequestHandler;
import net.geco.basics.GecoResources;
import net.geco.basics.TimeManager;
import net.geco.basics.WindowsRegistryQuery;
import net.geco.model.Punch;
import net.geco.model.Runner;
import net.geco.model.RunnerRaceData;
import net.geco.model.Stage;
import net.geco.model.Status;

import org.martin.sireader.common.PunchObject;
import org.martin.sireader.common.PunchRecordData;
import org.martin.sireader.common.ResultData;
import org.martin.sireader.server.IPunchObject;
import org.martin.sireader.server.IResultData;
import org.martin.sireader.server.PortMessage;
import org.martin.sireader.server.SIPortHandler;
import org.martin.sireader.server.SIReaderListener;


/**
* @author Simon Denier
* @since Oct 8, 2009
*
*/
public class SIReaderHandler extends Control
  implements Announcer.StageListener, SIReaderListener<PunchObject,PunchRecordData> {
 
  private static final boolean DEBUGMODE = false;
 

  private GecoRequestHandler requestHandler;
 
  private SIPortHandler portHandler;

  private SerialPort siPort;
 
  private Vector<SerialPort> serialPorts;
 
  private int nbTry;
 
  private boolean starting;

 
  public static class SerialPort {
    private String port;
    private String friendlyName;
    public SerialPort(String port, String friendlyName){
      this.port = port;
      this.friendlyName = friendlyName;
    }
    public String name(){
      return port;
    }
    public String toString(){
      return friendlyName;
    }
  }
 
 
  /**
   * @param factory
   * @param stage
   * @param announcer
   */
  public SIReaderHandler(GecoControl geco) {
    super(SIReaderHandler.class, geco);
    changePortName();
    geco.announcer().registerStageListener(this);
  }
 
  public void setRequestHandler(GecoRequestHandler requestHandler) {
    this.requestHandler = requestHandler;
  }

  public static String portNameProperty() {
    return "SIPortname"; //$NON-NLS-1$
  }
 
  private void changePortName() {
    serialPorts = null; // reset listPorts
    String port = stage().getProperties().getProperty(portNameProperty());
    if( port!=null ) {
      for (SerialPort serial : listPorts()) {
        if( serial.name().equals(port) ){
          setPort(serial);
          return;
        }
      }
    }
    setPort(detectSIPort());
  }
 
  public void changeZeroTime() {
    if( portHandler!=null )
      portHandler.setCourseZeroTime( stage().getZeroHour() );
  }
 
  public Vector<SerialPort> listPorts() {
    if( serialPorts==null ){
      @SuppressWarnings("rawtypes")
      Enumeration portIdentifiers = CommPortIdentifier.getPortIdentifiers();
      Vector<String> sPorts = new Vector<String>();
      if( DEBUGMODE )
        geco().debug("*** CommPort listing ***");
      while( portIdentifiers.hasMoreElements() ){
        CommPortIdentifier port = (CommPortIdentifier) portIdentifiers.nextElement();
        if( port.getPortType()==CommPortIdentifier.PORT_SERIAL ){
          if( DEBUGMODE )
            geco().debug(port.getName());
          sPorts.add(port.getName());
        }
      }
      serialPorts = createFriendlyPorts(sPorts);     
    }
    return serialPorts;
  }
  private Vector<SerialPort> createFriendlyPorts(Vector<String> serialPorts) {
    Vector<SerialPort> ports = new Vector<SerialPort>(serialPorts.size());
    ports.add(new SerialPort("", "")); // empty port //$NON-NLS-1$ //$NON-NLS-2$
    if( GecoResources.platformIsWindows() ){
      // "HKLM\\System\\CurrentControlSet\\Enum\\USB\\Vid_10c4&Pid_800a\\78624 /v FriendlyName";
      String[] reg =
        WindowsRegistryQuery.listRegistryEntries("HKLM\\System\\CurrentControlSet\\Enum").split("\n"); //$NON-NLS-1$ //$NON-NLS-2$
      HashMap<String,String> friendlyNames = new HashMap<String,String>();
      Pattern comPattern = Pattern.compile("FriendlyName.+REG_SZ(.+)\\((COM\\d+)\\)"); //$NON-NLS-1$
      if( DEBUGMODE )
        geco().debug("*** Registry listing ***");
      for (String string : reg) {
        Matcher match = comPattern.matcher(string);
        if( match.find() ){
          String com = match.group(2);
          String fname = com + ": " + match.group(1).trim(); //$NON-NLS-1$
          friendlyNames.put(com, fname);
          if( DEBUGMODE )
            geco().debug(fname);
        }
        // The following did not always match well?
//        if( string.contains("FriendlyName") && string.contains("COM") ){ //$NON-NLS-1$ //$NON-NLS-2$
//          int s = string.indexOf("COM"); //$NON-NLS-1$
//          int e = string.indexOf(')', s);
//          if( e>=0 ){
//            String com = string.substring(s, e); // expect (COMx) format
//            String fname = com + ": " //$NON-NLS-1$
//                    + string.substring(string.lastIndexOf("\t") + 1, s - 1).trim(); //$NON-NLS-1$
//            friendlyNames.put(com, fname);           
//          } // else we match something which is not like COMx)
//        }
      }
      if( DEBUGMODE )
        geco().debug("*** Match ***");
      for (String port : serialPorts) {
        String friendlyName = friendlyNames.get(port);
        ports.add(new SerialPort(port, (friendlyName==null) ? port : friendlyName ));
        if( DEBUGMODE )
          geco().debug(port + " -> " + friendlyName);
      }
    } else {
      for (String port : serialPorts) {
        ports.add(new SerialPort(port, port));
      }
    }
    return ports;
  }

  private SerialPort detectSIPort() {
    String match;
    if( GecoResources.platformIsWindows() ){
      match = "SPORTident"; //$NON-NLS-1$
    } else { // Linux, Mac
      match = "/dev/tty.SLAB_USBtoUART"; //$NON-NLS-1$
    }
    for (SerialPort serial : listPorts()) {
      if( serial.toString().contains(match) ){
        return serial;
      }
    }
    return serialPorts.firstElement();
  }
 
  public SerialPort getPort() {
    return siPort;
  }

  public void setPort(SerialPort port) {
    this.siPort = port;
  }

  private void configure() {
    portHandler = new SIPortHandler(new ResultData());
    portHandler.addListener(this);
    portHandler.setPortName(getPort().name());
    portHandler.setDebugDir(stage().getBaseDir());
    changeZeroTime();
  }

  public void start() {
    configure();
    nbTry = 0;
    starting = true;
    if (!portHandler.isAlive())
      portHandler.start();
    PortMessage m = new PortMessage(SIPortHandler.START);
    portHandler.sendMessage(m);
  }
 
  public void stop() {
    // TODO: announce station status
    if( portHandler==null )
      return;
    try {
      portHandler.interrupt();
      portHandler.join();
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
  }
 
  @Override
  public void newCardRead(IResultData<PunchObject,PunchRecordData> card) {
    Runner runner = registry().findRunnerByEcard(card.getSiIdent());
    if( runner!=null ) {
      RunnerRaceData runnerData = registry().findRunnerData(runner);
      if( runnerData.hasData() ) {
        geco().log("READING AGAIN " + card.getSiIdent()); //$NON-NLS-1$
        String returnedCard = requestHandler.requestMergeExistingRunner(handleNewData(card), runner);
        if( returnedCard!=null ) {
          geco().announcer().announceCardReadAgain(returnedCard);
        }
      } else {
        handleData(runnerData, card);
        if( runner.rentedEcard() ){
          geco().announcer().announceRentedCard(card.getSiIdent());
        }
      }
    } else {
      geco().log("READING UNKNOWN " + card.getSiIdent()); //$NON-NLS-1$
      String returnedCard = requestHandler.requestMergeUnknownRunner(handleNewData(card), card.getSiIdent());
      if( returnedCard!=null ) {
        geco().announcer().announceUnknownCardRead(returnedCard);
      }
    }
   
  }
 
  private RunnerRaceData handleNewData(IResultData<PunchObject,PunchRecordData> card) {
    RunnerRaceData newData = factory().createRunnerRaceData();
    newData.setResult(factory().createRunnerResult());
    updateRaceDataWith(newData, card);
    // do not do any announcement here since the case is handled in the Merge dialog after that and depends on user decision
    return newData;
  }
 
  /**
   * @param runnerData
   * @param card
   */
  private void handleData(RunnerRaceData runnerData, IResultData<PunchObject,PunchRecordData> card) {
    updateRaceDataWith(runnerData, card);
    Status oldStatus = runnerData.getResult().getStatus();
    geco().checker().check(runnerData);
    geco().log("READING " + runnerData.infoString()); //$NON-NLS-1$
    if( runnerData.getResult().is(Status.MP) ) {
      geco().announcer().dataInfo(runnerData.getResult().formatMpTrace() + " (" + runnerData.getResult().getNbMPs() + " MP)"); //$NON-NLS-1$ //$NON-NLS-2$
    }
    geco().announcer().announceCardRead(runnerData.getRunner().getEcard());
    geco().announcer().announceStatusChange(runnerData, oldStatus);
  }

  /**
   * @param runnerData
   * @param card
   */
  private void updateRaceDataWith(RunnerRaceData runnerData, IResultData<PunchObject,PunchRecordData> card) {
    runnerData.stampReadtime();
    runnerData.setErasetime(safeTime(card.getClearTime()));
    runnerData.setControltime(safeTime(card.getCheckTime()));   
    handleStarttime(runnerData, card);
    handleFinishtime(runnerData, card);
    handlePunches(runnerData, card.getPunches());
  }
  private Date safeTime(long siTime) {
    if( siTime>PunchObject.INVALID ) {
      return new Date(siTime);
    } else {
      return TimeManager.NO_TIME;
    }
  }
  private void handleStarttime(RunnerRaceData runnerData, IResultData<PunchObject, PunchRecordData> card) {
    Date startTime = safeTime(card.getStartTime());
    runnerData.setStarttime(startTime); // raw time
    if( startTime.equals(TimeManager.NO_TIME) // no start time on card
        && runnerData.getRunner()!=null ){
      // retrieve registered start time for next check to be accurate
      startTime = runnerData.getRunner().getRegisteredStarttime();
    }
    if( startTime.equals(TimeManager.NO_TIME) ) {
      geco().log("MISSING start time for " + card.getSiIdent()); //$NON-NLS-1$
    }
  }
  private void handleFinishtime(RunnerRaceData runnerData, IResultData<PunchObject, PunchRecordData> card) {
    Date finishTime = safeTime(card.getFinishTime());
    runnerData.setFinishtime(finishTime);
    if( finishTime.equals(TimeManager.NO_TIME) ) {
      geco().log("MISSING finish time for " + card.getSiIdent()); //$NON-NLS-1$
    }
  }


  /**
   * @param runnerData
   * @param punches
   */
  private void handlePunches(RunnerRaceData runnerData, ArrayList<PunchObject> punchArray) {
    Punch[] punches = new Punch[punchArray.size()];
    for(int i=0; i< punches.length; i++) {
      IPunchObject punchObject = punchArray.get(i);
      punches[i] = factory().createPunch();
      punches[i].setCode(punchObject.getCode());
      punches[i].setTime(new Date(punchObject.getTime()));
    }
    runnerData.setPunches(punches);
  }

  @Override
  public void portStatusChanged(String status) {
    if( status.equals("     Open      ") && starting ){ //$NON-NLS-1$
      geco().announcer().announceStationStatus("Ready"); //$NON-NLS-1$
      starting = false;
    }
    if( status.equals("     Connecting") ){ //$NON-NLS-1$
      nbTry++;
    }
    if( nbTry>=2 ) { // catch any tentative to re-connect after a deconnexion
      portHandler.interrupt(); // one last try, after interruption
      if( starting ) { // wrong port
        geco().announcer().announceStationStatus("NotFound"); //$NON-NLS-1$
      } else { // station was disconnected?
        geco().announcer().announceStationStatus("Failed"); //$NON-NLS-1$
      }
    }
  }
 
 
  @Override
  public void changed(Stage previous, Stage next) {
//    stop();
    changePortName();
    changeZeroTime();
  }

  @Override
  public void saving(Stage stage, Properties properties) {
    properties.setProperty(portNameProperty(), getPort().name());
  }

  @Override
  public void closing(Stage stage) {
    stop();
  }
 
}
TOP

Related Classes of net.geco.control.SIReaderHandler$SerialPort

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.