package de.uniluebeck.itm.wsn.drivers.pacemate;
import com.google.common.util.concurrent.TimeLimiter;
import com.google.inject.Inject;
import com.google.inject.assistedinject.Assisted;
import de.uniluebeck.itm.wsn.drivers.core.exception.InvalidChecksumException;
import de.uniluebeck.itm.wsn.drivers.core.operation.AbstractProgramOperation;
import de.uniluebeck.itm.wsn.drivers.core.operation.OperationFactory;
import de.uniluebeck.itm.wsn.drivers.core.operation.OperationListener;
import de.uniluebeck.itm.wsn.drivers.core.serialport.SerialPortProgrammingMode;
import de.uniluebeck.itm.wsn.drivers.core.util.BinaryImageBlock;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.annotation.Nullable;
public class PacemateProgramOperation extends AbstractProgramOperation {
private static final Logger log = LoggerFactory.getLogger(PacemateProgramOperation.class);
private final PacemateHelper helper;
private final OperationFactory operationFactory;
@Inject
public PacemateProgramOperation(final TimeLimiter timeLimiter,
final PacemateHelper helper,
final OperationFactory operationFactory,
@Assisted byte[] binaryImage,
@Assisted final long timeoutMillis,
@Assisted @Nullable final OperationListener<Void> operationCallback) {
super(timeLimiter, binaryImage, timeoutMillis, operationCallback);
this.helper = helper;
this.operationFactory = operationFactory;
}
private static final float PROGRESS_FRACTION_PROGRAM = 0.875f;
private void program() throws Exception {
// Return with success if the user has requested to cancel this operation
if (isCanceled()) {
return;
}
// Create pacemate image
final PacemateBinaryImage binaryImage = new PacemateBinaryImage(getBinaryImage());
// Calc CRC and write it to the flash
final int flashCRC = binaryImage.calcCRC();
log.debug("CRC: " + flashCRC);
helper.writeCRCtoFlash(flashCRC);
// Write program to flash
BinaryImageBlock block;
int blockCount = 3;
int blockNumber = 3; // blockNumber != blockCount because block 8 & 9 ==
int blocksWritten = 0;
// 32 kb all other 4 kb
while ((block = binaryImage.getNextBlock()) != null) {
final byte[] data = block.getData();
final int address = block.getAddress();
try {
helper.writeToRAM(PacemateHelper.START_ADDRESS_IN_RAM, data.length);
} catch (Exception e) {
log.error("Error while write to RAM! Program Operation will be cancelled!", e);
throw e;
}
int counter = 0;
int lineCounter = 0;
byte[] line;
// each block is sent in parts of 20 lines a 45 bytes
while (counter < data.length) {
int offset = 0;
int bytesNotYetApproved = 0;
if (counter + 45 < data.length) {
line = new byte[PacemateBinaryImage.LINESIZE]; // a line with 45
// bytes
System.arraycopy(data, counter, line, 0, PacemateBinaryImage.LINESIZE);
counter = counter + PacemateBinaryImage.LINESIZE;
bytesNotYetApproved = bytesNotYetApproved + PacemateBinaryImage.LINESIZE;
} else {
if (((data.length - counter) % 3) == 1) {
offset = 2;
} else if (((data.length - counter) % 3) == 2) {
offset = 1;
}
line = new byte[data.length - counter + offset];
line[line.length - 1] = 0;
line[line.length - 2] = 0;
System.arraycopy(data, counter, line, 0, data.length - counter);
counter = counter + (data.length - counter);
bytesNotYetApproved = bytesNotYetApproved + (data.length - counter);
}
try {
helper.sendDataMessage(binaryImage.encode(line, line.length - offset));
} catch (Exception e) {
log.error("Error while writing flash! OperationRunnable will be cancelled!", e);
throw e;
}
lineCounter++;
if ((lineCounter == 20) || (counter >= data.length)) {
try {
helper.sendChecksum(binaryImage.crc);
} catch (InvalidChecksumException e) {
log.debug("Invalid Checksum - resend last part");
// so resending the last 20 lines
counter = counter - bytesNotYetApproved;
} catch (Exception e) {
log.debug("Error while writing flash! OperationRunnable will be cancelled!", e);
throw e;
}
lineCounter = 0;
binaryImage.crc = 0;
}
}
try {
// if block is completed copy data from RAM to Flash
log.debug("Prepare Flash and Copy Ram to Flash " + blockCount + " " + blockNumber + " " + address);
helper.configureFlash(blockNumber, blockNumber);
if (data.length > 1024) {
helper.copyRAMToFlash(address, PacemateHelper.START_ADDRESS_IN_RAM, 4096);
} else if (data.length > 512) {
helper.copyRAMToFlash(address, PacemateHelper.START_ADDRESS_IN_RAM, 1024);
} else if (data.length > 512) {
helper.copyRAMToFlash(address, PacemateHelper.START_ADDRESS_IN_RAM, 512);
} else {
helper.copyRAMToFlash(address, PacemateHelper.START_ADDRESS_IN_RAM, 256);
}
} catch (Exception e) {
log.error("Error while copy RAM to Flash! OperationRunnable will be cancelled!", e);
throw e;
}
// Notify listeners of the new status
blocksWritten++;
progress(0.125f + PROGRESS_FRACTION_PROGRAM * (1.0f / binaryImage.getBlockCount()) * blocksWritten);
// Return with success if the user has requested to cancel this
// operation
if (isCanceled()) {
return;
}
blockCount++;
if ((blockCount > 0) && (8 >= blockCount)) // Sector 0-7 4kb
{
blockNumber++;
} else if (blockCount == 16) // Sector 8 32kb
{
blockNumber++;
} else if (blockCount == 24) // Sector 9 32kb
{
blockNumber++;
} else if (blockCount == 32) // Sector 10 32kb
{
blockNumber++;
} else if (blockCount == 40) // Sector 11 32kb
{
blockNumber++;
} else if (blockCount == 48) // Sector 12 32kb
{
blockNumber++;
} else if (blockCount == 56) // Sector 13 32kb
{
blockNumber++;
} else if (blockCount == 64) // Sector 14 32kb
{
blockNumber++;
}
}
progress(0.125f + PROGRESS_FRACTION_PROGRAM * 1.0f);
}
@Override
@SerialPortProgrammingMode
protected Void callInternal() throws Exception {
log.trace("Program operation executing...");
runSubOperation(operationFactory.createEraseFlashOperation(120000, null), 0.125f);
program();
log.trace("Program operation finished");
return null;
}
}