package de.uniluebeck.itm.wsn.drivers.jennic; 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.ChipType; import de.uniluebeck.itm.wsn.drivers.core.MacAddress; import de.uniluebeck.itm.wsn.drivers.core.exception.*; import de.uniluebeck.itm.wsn.drivers.core.operation.*; 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; import java.io.IOException; public class JennicProgramOperation extends AbstractProgramOperation { /** * Logger for this class. */ private static final Logger log = LoggerFactory.getLogger(JennicProgramOperation.class); private static final float FRACTION_GET_CHIP_TYPE = 0.01f; private static final float FRACTION_READ_MAC_FROM_DEVICE = 0.02f; private static final float FRACTION_PROGRAM_WRITE_IMAGE = 0.92f; private static final float FRACTION_RESET = 0.03f; private final JennicHelper helper; private final OperationFactory operationFactory; @Inject public JennicProgramOperation(final TimeLimiter timeLimiter, final JennicHelper 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; } @Override @SerialPortProgrammingMode protected Void callInternal() throws Exception { GetChipTypeOperation getChipTypeOperation = operationFactory.createGetChipTypeOperation(10000, null); ChipType chipType = runSubOperation(getChipTypeOperation, FRACTION_GET_CHIP_TYPE); JennicBinaryImage binaryImage = new JennicBinaryImage(getBinaryImage()); assertImageCompatible(binaryImage, chipType); final byte[] macAddressBeforeBytes = readMacAddressBytes(chipType); final MacAddress macAddressBefore = new MacAddress(macAddressBeforeBytes); if (isBrokenMacAddress(macAddressBefore)) { throw new MacAddressBrokenException("Device MAC address (" + macAddressBefore + ") is broken!"); } writeMacAddressToImage(macAddressBeforeBytes, binaryImage); while (!isCanceled() && !helper.waitForConnection()) { log.debug("Waiting for a connection..."); } if (isCanceled()) { return null; } eraseSectors(chipType); writeBinaryImage(binaryImage); final MacAddress macAddressAfter = readMacAddress(chipType); // if MAC address is broken after flashing try to rewrite the old MAC address that was there before if (isBrokenMacAddress(macAddressAfter)) { // write old MAC address runSubOperation(operationFactory.createWriteMacAddressOperation(macAddressBefore, 2000, null), 0f); // if MAC address is still broken, abort if (isBrokenMacAddress(readMacAddress(chipType))) { throw new MacAddressBrokenException( "After flashing the MAC address seems to be " + macAddressAfter + " which may result in unexpected behavior!" ); } } runSubOperation(operationFactory.createResetOperation(1000, null), FRACTION_RESET); return null; } private boolean isBrokenMacAddress(final MacAddress macAddress) throws Exception { return MacAddress.HIGHEST_MAC_ADDRESS.equals(macAddress); } private void writeBinaryImage(final JennicBinaryImage binaryImage) throws IOException, TimeoutException, UnexpectedResponseException, InvalidChecksumException, FlashProgramFailedException { BinaryImageBlock block; int blockNr = 0; int blockCount = binaryImage.getBlockCount(); while ((block = binaryImage.getNextBlock()) != null) { blockNr++; if (log.isTraceEnabled()) { log.trace("Writing block {} of {}", blockNr, blockCount); } helper.writeFlash(block.getAddress(), block.getData()); final float progressBefore = FRACTION_GET_CHIP_TYPE + FRACTION_READ_MAC_FROM_DEVICE; progress(progressBefore + (FRACTION_PROGRAM_WRITE_IMAGE * ((float) blockNr / (float) blockCount))); } } private void eraseSectors(final ChipType chipType) throws Exception { helper.configureFlash(chipType); helper.eraseFlash(Sector.FIRST); helper.eraseFlash(Sector.SECOND); helper.eraseFlash(Sector.THIRD); } private void writeMacAddressToImage(final byte[] macAddressBytes, final JennicBinaryImage binaryImage) throws ProgramChipMismatchException { binaryImage.insertHeader(macAddressBytes); } private MacAddress readMacAddress(final ChipType chipType) throws Exception { return new MacAddress(readMacAddressBytes(chipType)); } private byte[] readMacAddressBytes(final ChipType chipType) throws Exception { return readDeviceFlashHeader(chipType.getHeaderStart(), chipType.getHeaderLength()); } private byte[] readDeviceFlashHeader(final int address, final int length) throws Exception { ReadFlashOperation subOperation = operationFactory.createReadFlashOperation(address, length, 120000, null); return runSubOperation(subOperation, FRACTION_READ_MAC_FROM_DEVICE); } private void assertImageCompatible(final JennicBinaryImage binaryImage, final ChipType chipType) throws Exception { if (!binaryImage.isCompatible(chipType)) { log.error("Device chip type ({}) and image chip type ({}) mismatch!", chipType, binaryImage.getChipType()); throw new ProgramChipMismatchException(chipType, binaryImage.getChipType()); } } }