All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.embeddedunveiled.serial.SerialComXModem Maven / Gradle / Ivy

/**
 * Author : Rishi Gupta
 * 
 * This file is part of 'serial communication manager' library.
 *
 * The 'serial communication manager' is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by the Free Software 
 * Foundation, either version 3 of the License, or (at your option) any later version.
 *
 * The 'serial communication manager' is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 
 * PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with serial communication manager. If not, see .
 */

package com.embeddedunveiled.serial;

import java.io.File;
import java.io.FileInputStream;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

/**
 *
 */
public final class SerialComXModem {
	
	private static final byte SOH = 0x01; // Start of header character
	private static final byte EOT = 0x04; // End-of-transmission character
	private static final byte ACK = 0x06; // Acknowledge byte character
	private static final byte NAK = 0x15; // Negative-acknowledge character
	private static final byte SUB = 0x1A; // Substitute/CTRL+Z
		
	private SerialComManager scm = null;
	private long handle = 0;
	private File fileToProcess = null;
	
	private int blockNumber = -1;
	private byte[] block = new byte[132];       // 132 bytes xmodem block/packet
	private BufferedInputStream inStream = null;
	private BufferedOutputStream outStream = null;
	private boolean noMoreData = false;
	
	
	/**
	 * 

The constructor, joins instance of this class to the instance of scm.

* * @param scm SerialComManager instance associated with this handle * @param handle of the port on which file is to be sent * @param fileToProcess File instance representing file to be sent */ public SerialComXModem(SerialComManager scm, long handle, File fileToProcess) { this.scm = scm; this.handle = handle; this.fileToProcess = fileToProcess; } /** *

For internal use only.

*

Represents actions to execute in state machine to implement xmodem protocol for sending files.

*/ public boolean sendFileX() throws SecurityException, IOException, SerialComException { // Finite state machine final int CONNECT = 0; final int BEGINSEND = 1; final int WAITACK = 2; final int RESEND = 3; final int SENDNEXT = 4; final int ENDTX = 5; final int ABORT = 6; boolean nakReceived = false; boolean eotAckReceptionTimerInitialized = false; String errMsg = null; int retryCount = 0; int state = -1; byte[] data = null; long responseWaitTimeOut = 0; long eotAckWaitTimeOutValue = 0; inStream = new BufferedInputStream(new FileInputStream(fileToProcess)); state = CONNECT; while(true) { switch(state) { case CONNECT: responseWaitTimeOut = System.currentTimeMillis() + 60000; while(nakReceived != true) { try { data = scm.readBytes(handle); } catch (SerialComException exp) { inStream.close(); throw exp; } if(data.length > 0) { /* Instead of purging receive buffer and then waiting for NAK, receive all data because * this approach might be faster. The other side might have opened first time and may * have flushed garbage data. So receive buffer may contain garbage + NAK character. */ for(int x=0; x < data.length; x++) { if(NAK == data[x]) { nakReceived = true; state = BEGINSEND; break; } } }else { try { Thread.sleep(800); // delay before next attempt to check NAK arrival } catch (InterruptedException e) { } // abort if timedout while waiting for NAK character if((nakReceived != true) && (System.currentTimeMillis() >= responseWaitTimeOut)) { errMsg = SerialComErrorMapper.ERR_TIMEOUT_RECEIVER_CONNECT; state = ABORT; break; } } } break; case BEGINSEND: blockNumber = 1; // Block numbering starts with 1 for the first block sent, not 0. assembleBlock(); try { scm.writeBytes(handle, block); } catch (SerialComException exp) { inStream.close(); throw exp; } state = WAITACK; break; case RESEND: if(retryCount > 10) { errMsg = SerialComErrorMapper.ERR_MAX_RETRY_REACHED; state = ABORT; break; } try { scm.writeBytes(handle, block); } catch (SerialComException exp) { inStream.close(); throw exp; } state = WAITACK; break; case WAITACK: responseWaitTimeOut = System.currentTimeMillis() + 60000; // 1 minute while(true) { // delay before next attempt to read from serial port try { if(noMoreData != true) { Thread.sleep(150); }else { Thread.sleep(1500); } } catch (InterruptedException e) { } // try to read data from serial port try { data = scm.readBytes(handle); } catch (SerialComException exp) { inStream.close(); throw exp; } /* if data received process it. if long timeout occurred abort otherwise retry reading from serial port. * if nothing received at all abort. */ if(data.length > 0) { break; }else { if(noMoreData == true) { state = ENDTX; break; } if(System.currentTimeMillis() >= responseWaitTimeOut) { if(noMoreData == true) { errMsg = SerialComErrorMapper.ERR_TIMEOUT_ACKNOWLEDGE_EOT; }else { errMsg = SerialComErrorMapper.ERR_TIMEOUT_ACKNOWLEDGE_BLOCK; } state = ABORT; break; } } } if((state != ABORT) && (state != ENDTX)) { if(noMoreData != true) { if(data[0] == ACK) { state = SENDNEXT; }else if(data[0] == NAK) { retryCount++; state = RESEND; }else{ errMsg = SerialComErrorMapper.ERR_KNOWN_ERROR_OCCURED; state = ABORT; } }else { if(data[0] == ACK) { inStream.close(); return true; // successfully sent file, let's go back home happily }else{ if(System.currentTimeMillis() >= eotAckWaitTimeOutValue) { errMsg = SerialComErrorMapper.ERR_TIMEOUT_ACKNOWLEDGE_EOT; state = ABORT; }else { state = ENDTX; } } } } break; case SENDNEXT: retryCount = 0; blockNumber++; assembleBlock(); if(noMoreData == true) { state = ENDTX; break; } try { scm.writeBytes(handle, block); } catch (SerialComException exp) { inStream.close(); throw exp; } state = WAITACK; break; case ENDTX: if(eotAckReceptionTimerInitialized != true) { eotAckWaitTimeOutValue = System.currentTimeMillis() + 60000; // 1 minute eotAckReceptionTimerInitialized = true; } try { scm.writeSingleByte(handle, EOT); } catch (SerialComException exp) { inStream.close(); throw exp; } state = WAITACK; break; case ABORT: /* if ioexception occurs, control will not reach here instead exception would have been * thrown already. */ inStream.close(); throw new SerialComTimeOutException("sendFile()", errMsg); } } } // prepares xmodem block <255-blk #><--128 data bytes--> private void assembleBlock() throws IOException { int data = 0; int x = 0; int blockChecksum = 0; if(blockNumber > 0xFF) { blockNumber = 0x00; } block[0] = SOH; block[1] = (byte) blockNumber; block[2] = (byte) ~blockNumber; for(x=x+3; x<128+4; x++) { data = inStream.read(); if(data < 0) { if(x != 3) { // assembling last block with padding for(x=x+0; x<128+4; x++) { block[x] = SUB; } }else { noMoreData = true; return; } }else { block[x] = (byte) data; } } for(x=3; x<131; x++) { blockChecksum = (byte)blockChecksum + block[x]; } block[131] = (byte) (blockChecksum % 256); } /** *

For internal use only.

*

Represents actions to execute in state machine to implement xmodem protocol for receiving files.

* * @throws SerialComException * @throws FileNotFoundException */ public boolean receiveFileX() throws SerialComException, FileNotFoundException, IOException { // Finite state machine final int CONNECT = 0; final int RECEIVEDATA = 1; final int VERIFY = 2; final int REPLY = 3; final int ABORT = 4; int a = 0; int x = 0; int z = 0; int retryCount = 0; int state = -1; int blockNumber = 1; //TODO CHK VALID BLK NO int blockChecksum = -1; int bufferIndex = 0; boolean readComplete = false; boolean firstBlock = false; boolean isCorrupted = false; boolean rxDone = false; byte[] block = new byte[132]; byte[] data = null; String errMsg = null; outStream = new BufferedOutputStream(new FileOutputStream(fileToProcess)); // Clear receive buffer before start try { scm.clearPortIOBuffers(handle, true, false); } catch (SerialComException e) { outStream.close(); throw e; } state = CONNECT; while(true) { System.out.println("state : " + state); switch(state) { case CONNECT: if(retryCount > 10) { state = ABORT; errMsg = SerialComErrorMapper.ERR_TIMEOUT_TRANSMITTER_CONNECT; break; } try { scm.writeSingleByte(handle, NAK); // send NAK to begin tx firstBlock = true; state = RECEIVEDATA; } catch (SerialComException exp) { outStream.close(); throw exp; } break; case RECEIVEDATA: if(firstBlock == false) { // TODO }else { /* If anything is not received from port with in 10 seconds (i.e. transmitter has not started sending data) * go back to CONNECT state to send NAK again otherwise as data is received goto REPLY or VERIFY state * based on EOT or data received. */ for(a=0; a<34; a++) { try { Thread.sleep(300); // 300*34 = 10.2 seconds } catch (InterruptedException e) { } try { data = scm.readBytes(handle); System.out.println("length : " + data.length); } catch (SerialComException exp) { outStream.close(); throw exp; } if(data.length > 0) { retryCount = 0; // reset retry count if(data[0] == EOT){ // transmitter sent EOT as very first data ! state = REPLY; isCorrupted = false; rxDone = true; break; }else { if(data.length == 132) { // complete block read for(int i=0; i < 132; i++) { block[i] = data[i]; } state = VERIFY; break; }else { // partial block read readComplete = false; bufferIndex = data.length; for(int d=0; d < bufferIndex; d++) { block[d] = data[d]; } while(true) { try { Thread.sleep(50); } catch (InterruptedException e) { } try { z = 0; data = scm.readBytes(handle); if(data.length > 0) { for(bufferIndex = bufferIndex + 0; bufferIndex < data.length; bufferIndex++) { block[bufferIndex] = data[z]; z++; } if(bufferIndex > 131) { firstBlock = false; break; } } } catch (SerialComException exp) { outStream.close(); throw exp; } } } } } } if(a > 33){ state = CONNECT; retryCount++; } } break; case VERIFY: blockChecksum = 0; isCorrupted = false; // verify block number if(block[1] != ~block[2]){ isCorrupted = true; state = REPLY; break; } // verify checksum for(x=3; x<131; x++) { blockChecksum = (byte)blockChecksum + block[x]; } blockChecksum = (byte) (blockChecksum % 256); if(blockChecksum != block[131]){ isCorrupted = true; state = REPLY; } state = REPLY; break; case REPLY: try { if(isCorrupted == false) { scm.writeSingleByte(handle, ACK); }else { scm.writeSingleByte(handle, NAK); } } catch (SerialComException exp) { outStream.close(); throw exp; } if(rxDone == false) { state = RECEIVEDATA; }else { outStream.close(); return true; // file reception successfully finished, let us go back home } break; case ABORT: /* if ioexception occurs, control will not reach here instead exception would have been * thrown already. */ outStream.close(); throw new SerialComTimeOutException("receiveFile()", errMsg); } } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy