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.operation.AbstractWriteMacAddressOperation;
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 org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.annotation.Nullable;
import static de.uniluebeck.itm.util.StringUtils.toHexString;
public class JennicWriteMacAddressOperation extends AbstractWriteMacAddressOperation {
private static final Logger log = LoggerFactory.getLogger(JennicWriteMacAddressOperation.class);
private static final int BLOCK_SIZE = 128;
private static final float FRACTION_READ_FIRST_SECTOR = 0.49f;
private static final float FRACTION_WRITE_FIRST_SECTOR = 0.49f;
private static final float FRACTION_GET_CHIP_TYPE = 0.02f;
private final JennicHelper helper;
private final OperationFactory operationFactory;
@Inject
public JennicWriteMacAddressOperation(final TimeLimiter timeLimiter,
final JennicHelper helper,
final OperationFactory operationFactory,
@Assisted final MacAddress macAddress,
@Assisted final long timeoutMillis,
@Nullable @Assisted final OperationListener<Void> operationCallback) {
super(timeLimiter, macAddress, timeoutMillis, operationCallback);
this.helper = helper;
this.operationFactory = operationFactory;
}
private void writeMacAddress(ChipType chipType) throws Exception {
// Wait for a helper
while (!isCanceled() && !helper.waitForConnection()) {
log.debug("Still waiting for a connection");
}
// Return with success if the user has requested to cancel this
// operation
if (isCanceled()) {
return;
}
// Read the first sector
byte[][] blocksFirstSector = readSector(Sector.FIRST);
progress(FRACTION_GET_CHIP_TYPE + FRACTION_READ_FIRST_SECTOR);
// Check if this operation has been cancelled
if (isCanceled()) {
return;
}
// Copy address into the header of the first sector
byte[] macAddressBytes = macAddress.toByteArray();
if (log.isTraceEnabled()) {
log.trace("Copy MAC address bytes ({}) to address {}, length: {}",
toHexString(macAddressBytes),
chipType.getHeaderStart(),
macAddressBytes.length
);
}
System.arraycopy(macAddressBytes, 0, blocksFirstSector[0], chipType.getHeaderStart(), macAddressBytes.length);
helper.configureFlash(chipType);
helper.eraseFlash(Sector.FIRST);
writeSector(Sector.FIRST, blocksFirstSector);
progress(
FRACTION_GET_CHIP_TYPE + FRACTION_READ_FIRST_SECTOR + FRACTION_WRITE_FIRST_SECTOR
);
}
protected byte[][] readSector(final Sector index) throws Exception {
final int start = index.getStart();
final int length = index.getEnd() - start;
// Calculate number of blocks to read
final int totalBlocks = length / BLOCK_SIZE;
final int residue = length - totalBlocks * BLOCK_SIZE;
log.trace(String.format("length = %d, totalBlocks = %d, residue = %d", length, totalBlocks, residue));
// Prepare byte array
final byte[][] sector = new byte[totalBlocks + (residue > 0 ? 1 : 0)][BLOCK_SIZE];
// Read block after block
int address = start;
for (int readBlocks = 0; readBlocks < totalBlocks; readBlocks++) {
sector[readBlocks] = helper.readFlash(address, BLOCK_SIZE);
address += BLOCK_SIZE;
progress(
FRACTION_GET_CHIP_TYPE + (FRACTION_READ_FIRST_SECTOR * ((float) readBlocks / (float) totalBlocks))
);
// Check if the user has cancelled the operation
if (isCanceled()) {
log.debug("Sector read has been cancelled");
return null;
}
}
// Read residue
if (residue > 0) {
sector[sector.length - 1] = helper.readFlash(address, residue);
}
return sector;
}
private void writeSector(final Sector sector, final byte[][] blocks) throws Exception {
int address = sector.getStart();
for (int block = 0; block < blocks.length; ++block) {
log.trace("Writing {} sector, block {}", sector, block);
helper.writeFlash(address, blocks[block]);
address += blocks[block].length;
progress(
FRACTION_GET_CHIP_TYPE + FRACTION_READ_FIRST_SECTOR + (FRACTION_WRITE_FIRST_SECTOR * ((float) block / (float) blocks.length))
);
}
}
@Override
@SerialPortProgrammingMode
protected Void callInternal() throws Exception {
log.trace("Writing mac address...");
ChipType chipType = runSubOperation(
operationFactory.createGetChipTypeOperation(1000, null),
FRACTION_GET_CHIP_TYPE
);
writeMacAddress(chipType);
log.trace("Done, written MAC Address: " + getMacAddress());
return null;
}
}