/*******************************************************************************
* Copyright (c) 2008, 2011 Thomas Holland (thomas@innot.de) and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Thomas Holland - initial API and implementation
*******************************************************************************/
package de.innot.avreclipse.core.properties;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.cdt.managedbuilder.core.IConfiguration;
import org.osgi.service.prefs.BackingStoreException;
import org.osgi.service.prefs.Preferences;
import de.innot.avreclipse.core.avrdude.AVRDudeAction;
import de.innot.avreclipse.core.avrdude.AVRDudeActionFactory;
import de.innot.avreclipse.core.avrdude.BaseBytesProperties;
import de.innot.avreclipse.core.avrdude.FuseBytesProperties;
import de.innot.avreclipse.core.avrdude.LockbitBytesProperties;
import de.innot.avreclipse.core.avrdude.ProgrammerConfig;
import de.innot.avreclipse.core.avrdude.ProgrammerConfigManager;
import de.innot.avreclipse.core.toolinfo.AVRDude;
import de.innot.avreclipse.core.toolinfo.fuses.FuseType;
/**
* Container for all AVRDude specific properties of a project.
* <p>
* Upon instantiation, the properties are loaded from the given preference store. All changes are
* local to the object until the {@link #save()} method is called.
* </p>
* <p>
* <code>AVRDudeProperties</code> objects do not reflect changes made to other
* <code>AVRDudeProperties</code> for the same Project/Configuration, so they should not be held
* on to and be reloaded every time the current values are required.
* </p>
*
* @author Thomas Holland
* @since 2.2
*/
public class AVRDudeProperties {
/** Reference to the parent properties */
private final AVRProjectProperties fParent;
/** The currently selected <code>ProgrammerConfig</code> */
private ProgrammerConfig fProgrammer;
/** ID of the currently selected <code>ProgrammerConfig</code> */
private String fProgrammerId;
private static final String KEY_PROGRAMMER = "ProgrammerID";
/** Current JTAG BitClock value. Contains a float value or be empty */
private String fBitclock;
private static final String KEY_BITCLOCK = "Bitclock";
private static final String DEFAULT_BITCLOCK = "";
/** Current BitBanger bit change delay. Contains an int value or be empty */
private String fBitBangDelay;
private static final String KEY_BITBANGDELAY = "BitBangDelay";
private static final String DEFAULT_BITBANGDELAY = "";
/** No Signature Check flag. <code>true</code> disables the signature check */
private boolean fNoSigCheck;
private static final String KEY_NOSIGCHECK = "NoSigCheck";
private static final boolean DEFAULT_NOSIGCHECK = false;
/** No Verify flag. <code>true</code> disables the automatic verify */
private boolean fNoVerify;
private static final String KEY_NOVERIFY = "NoVerify";
private static final boolean DEFAULT_NOVERIFY = false;
/** No Write Mode flag. <code>true</code> inhibits most write actions */
private boolean fNoWrite;
private static final String KEY_NOWRITE = "NoWrite";
private static final boolean DEFAULT_NOWRITE = false;
/** No auto chip erase flag. <code>true</code> disables chip erase when writing flash memory. */
private boolean fNoChipErase;
private static final String KEY_NOCHIPERASE = "NoChipErase";
private static final boolean DEFAULT_NOCHIPERASE = false;
/** Use Erase Cycle Counter flags. <code>true</code> enables the counter */
private boolean fUseCounter;
private static final String KEY_USECOUNTER = "UseCounter";
private static final boolean DEFAULT_USECOUNTER = false;
/** Write Flash Image flag. <code>true</code> to upload an image file */
private boolean fWriteFlash;
private static final String KEY_WRITEFLASH = "WriteFlash";
private static final boolean DEFAULT_WRITEFLASH = true;
/**
* Use Build Config Image flag. <code>true</code> to get the name of the flash image file from
* the current <code>IConfiguration</code>
*/
private boolean fFlashFromConfig;
private static final String KEY_FLASHFROMCONFIG = "FlashFromConfig";
private static final boolean DEFAULT_FLASHFROMCONFIG = true;
/**
* Name of the Flash image file. Only used when <code>fFlashFromConfig</code> is
* <code>false</code>
*/
private String fFlashFile;
private static final String KEY_FLASHFILE = "FlashFile";
private static final String DEFAULT_FLASHFILE = "";
/** Write EEPROM Image flag. <code>true</code> to upload an image file */
private boolean fWriteEEPROM;
private static final String KEY_WRITEEEPROM = "WriteEEPROM";
private static final boolean DEFAULT_WRITEEEPROM = false;
/**
* Use Build Config Image flag. <code>true</code> to get the name of the eeprom image file
* from the current <code>IConfiguration</code>
*/
private boolean fEEPROMFromConfig;
private static final String KEY_EEPROMFROMCONFIG = "EEPROMFromConfig";
private static final boolean DEFAULT_EEPROMFROMCONFIG = true;
/**
* Name of the EEPROM image file. Only used when <code>fEEPROMFromConfig</code> is
* <code>false</code>
*/
private String fEEPROMFile;
private static final String KEY_EEPROMFILE = "EEPROMFile";
private static final String DEFAULT_EEPROMFILE = "";
/** The <code>FuseBytesProperties</code> with all fuse bytes related settings. */
private FuseBytesProperties fFuseBytes;
private static final String NODE_FUSES = "Fuses";
/** The <code>LockbitBytesProperties</code> with all lock byte related settings. */
private LockbitBytesProperties fLockbits;
private static final String NODE_LOCKS = "Locks";
/** Other avrdude options. Free text for avrdude options not directly supported by the plugin. */
private String fOtherOptions;
private static final String KEY_OTHEROPTIONS = "OtherOptions";
private static final String DEFAULT_OTHEROPTIONS = "";
// Unused for now
// private static final String NODE_CALIBRATION = "CalibrationBytes";
// private CalibrationBytes fAVRDudeCalibration;
/**
* The source/target Preferences for the properties or <code>null</code> if default properties
* are represented.
*/
private final Preferences fPrefs;
/** Flag if any properties have been changed */
private boolean fDirty;
/**
* Create a new AVRDudeProperties object and load the properties from the Preferences.
* <p>
* If the given Preferences has no saved properties yet, the default values are used.
* </p>
*
* @param prefs
* <code>Preferences</code> to read the properties from.
* @param parent
* Reference to the <code>AVRProjectProperties</code> parent object.
*/
public AVRDudeProperties(Preferences prefs, AVRProjectProperties parent) {
fPrefs = prefs;
fParent = parent;
loadData();
}
/**
* Copy constructor.
* <p>
* Create a new AVRDudeProperties object and copy the values from the given AVRDudeProperties
* object.
* </p>
* <p>
* All values from the source are copied, except for the source Preferences and the Parent.
* </p>
*
* @param prefs
* <code>Preferences</code> to read the properties from.
* @param parent
* Reference to the <code>AVRProjectProperties</code> parent object.
* @param source
* <code>AVRDudeProperties</code> object to copy.
*/
public AVRDudeProperties(Preferences prefs, AVRProjectProperties parent,
AVRDudeProperties source) {
fParent = parent;
fPrefs = prefs;
fProgrammer = source.fProgrammer;
fProgrammerId = source.fProgrammerId;
fBitclock = source.fBitclock;
fBitBangDelay = source.fBitBangDelay;
fNoSigCheck = source.fNoSigCheck;
fNoVerify = source.fNoVerify;
fNoWrite = source.fNoWrite;
fNoChipErase = source.fNoChipErase;
fUseCounter = source.fUseCounter;
fWriteFlash = source.fWriteFlash;
fFlashFromConfig = source.fFlashFromConfig;
fFlashFile = source.fFlashFile;
fWriteEEPROM = source.fWriteEEPROM;
fEEPROMFromConfig = source.fEEPROMFromConfig;
fEEPROMFile = source.fEEPROMFile;
fFuseBytes = new FuseBytesProperties(prefs.node(NODE_FUSES), this, source.fFuseBytes);
fLockbits = new LockbitBytesProperties(prefs.node(NODE_LOCKS), this, source.fLockbits);
fOtherOptions = source.fOtherOptions;
// fAVRDudeCalibration = new
// CalibrationBytes(source.fAVRDudeCalibration);
fDirty = source.fDirty;
}
/**
* Get a reference to the parent properties.
*
* @return <code>AVRProjectProperties</code>
*/
public AVRProjectProperties getParent() {
return fParent;
}
public ProgrammerConfig getProgrammer() {
if (fProgrammer == null) {
return ProgrammerConfigManager.getDefault().getConfig(fProgrammerId);
}
return fProgrammer;
}
public void setProgrammer(ProgrammerConfig progcfg) {
if (!progcfg.equals(fProgrammer)) {
fProgrammer = progcfg;
fProgrammerId = progcfg.getId();
fDirty = false;
}
}
public String getProgrammerId() {
return fProgrammerId;
}
public void setProgrammerId(String programmerid) {
if (!fProgrammerId.equals(programmerid)) {
fProgrammerId = programmerid;
fProgrammer = null;
fDirty = true;
}
}
public String getBitclock() {
return fBitclock;
}
public void setBitclock(String bitclock) {
if (!fBitclock.equals(bitclock)) {
fBitclock = bitclock;
fDirty = true;
}
}
public String getBitBangDelay() {
return fBitBangDelay;
}
public void setBitBangDelay(String bitbangdelay) {
if (!fBitBangDelay.equals(bitbangdelay)) {
fBitBangDelay = bitbangdelay;
fDirty = true;
}
}
public boolean getNoSigCheck() {
return fNoSigCheck;
}
public void setNoSigCheck(boolean nosigcheck) {
if (fNoSigCheck != nosigcheck) {
fNoSigCheck = nosigcheck;
fDirty = true;
}
}
public boolean getNoVerify() {
return fNoVerify;
}
public void setNoVerify(boolean noverify) {
if (fNoVerify != noverify) {
fNoVerify = noverify;
fDirty = true;
}
}
public boolean getNoWrite() {
return fNoWrite;
}
public void setNoWrite(boolean nowrite) {
if (fNoWrite != nowrite) {
fNoWrite = nowrite;
fDirty = true;
}
}
public boolean getNoChipErase() {
return fNoChipErase;
}
public void setNoChipErase(boolean nochiperase) {
if (fNoChipErase != nochiperase) {
fNoChipErase = nochiperase;
fDirty = true;
}
}
public boolean getUseCounter() {
return fUseCounter;
}
public void setUseCounter(boolean usecounter) {
if (fUseCounter != usecounter) {
fUseCounter = usecounter;
fDirty = true;
}
}
public boolean getWriteFlash() {
return fWriteFlash;
}
public void setWriteFlash(boolean enabled) {
if (fWriteFlash != enabled) {
fWriteFlash = enabled;
fDirty = true;
}
}
public boolean getFlashFromConfig() {
return fFlashFromConfig;
}
public void setFlashFromConfig(boolean useconfig) {
if (fFlashFromConfig != useconfig) {
fFlashFromConfig = useconfig;
fDirty = true;
}
}
public String getFlashFile() {
return fFlashFile;
}
public void setFlashFile(String filename) {
if (!fFlashFile.equals(filename)) {
fFlashFile = filename;
fDirty = true;
}
}
public boolean getWriteEEPROM() {
return fWriteEEPROM;
}
public void setWriteEEPROM(boolean enabled) {
if (fWriteEEPROM != enabled) {
fWriteEEPROM = enabled;
fDirty = true;
}
}
public boolean getEEPROMFromConfig() {
return fEEPROMFromConfig;
}
public void setEEPROMFromConfig(boolean useconfig) {
if (fEEPROMFromConfig != useconfig) {
fEEPROMFromConfig = useconfig;
fDirty = true;
}
}
public String getEEPROMFile() {
return fEEPROMFile;
}
public void setEEPROMFile(String filename) {
if (!fEEPROMFile.equals(filename)) {
fEEPROMFile = filename;
fDirty = true;
}
}
/**
* Get the Fuse byte properties container object.
* <p>
* The <code>IConfiguration</code> parameter is only used to resolve filenames for optional
* fuses files. It can be <code>null</code>, but then the filename of a fuse file will be
* used as is and no macro expansion takes place.
* </p>
*
* @param buildconfig
* The current build configuration
* @return The <code>FuseByteProperties</code> container object.
*/
public FuseBytesProperties getFuseBytes(IConfiguration buildconfig) {
// informing the FuseByteProperties about the current build configuration here is somewhat
// kludgy, but this seems to be the only point where it can be set.
fFuseBytes.setBuildConfig(buildconfig);
return fFuseBytes;
}
/**
* Get the Lockbits byte properties container object.
* <p>
* The <code>IConfiguration</code> parameter is only used to resolve filenames for optional
* locks files. It can be <code>null</code>, but then the filename of a locks file will be
* used as is and no macro expansion takes place.
* </p>
*
* @param buildconfig
* The current build configuration
* @return The <code>FuseByteProperties</code> container object.
*/
public LockbitBytesProperties getLockbitBytes(IConfiguration buildconfig) {
// informing the LockbitByteProperties about the current build configuration here is
// somewhat
// kludgy, but this seems to be the only point where it can be set.
fLockbits.setBuildConfig(buildconfig);
return fLockbits;
}
public BaseBytesProperties getBytesProperties(FuseType type, IConfiguration buildconfig) {
switch (type) {
case FUSE:
return getFuseBytes(buildconfig);
case LOCKBITS:
return getLockbitBytes(buildconfig);
}
// there are no other FuseTypes than those two, so we never come here
return null;
}
public String getOtherOptions() {
return fOtherOptions;
}
public void setOtherOptions(String otheroptions) {
if (!fOtherOptions.equals(otheroptions)) {
fOtherOptions = otheroptions;
fDirty = true;
}
}
/**
* Gets the avrdude command arguments as defined by the properties.
*
* @return <code>List<String></code> with the avrdude options, one per list entry.
*/
public List<String> getArguments() {
List<String> arguments = new ArrayList<String>();
// Convert the mcu id to the avrdude format and add it
String mcuid = fParent.getMCUId();
String avrdudemcuid = AVRDude.getDefault().getMCUInfo(mcuid);
arguments.add("-p" + avrdudemcuid);
// Add the options from the programmer configuration
ProgrammerConfig progcfg = getProgrammer();
if (progcfg != null) {
arguments.addAll(progcfg.getArguments());
}
// add the bitclock value
if (fBitclock.length() != 0) {
arguments.add("-B" + fBitclock);
}
// add the BitBang delay value
if (fBitBangDelay.length() != 0) {
arguments.add("-i" + fBitBangDelay);
}
// add the No Signature Check flag
if (fNoSigCheck) {
arguments.add("-F");
}
// add the Simulation / no-write flag
if (fNoWrite) {
arguments.add("-n");
// Add the "no Verify" flag to suppress nuisance error messages
// (if not already set)
if (!fNoVerify)
arguments.add("-V");
}
// add the No Verify flag
if (fNoVerify) {
arguments.add("-V");
}
// ad the No Chip Erase flag
if (fNoChipErase) {
arguments.add("-D");
}
// add the Use Erase Cycle Counter flag
if (fUseCounter) {
arguments.add("-y");
}
// Disable safe mode when Fuses are written, otherwise the fuses will be
// restored after the write.
// Safemode is *not* disabled in Simulation mode, because even with the
// no-write flag, fuse bytes may change values accidentally and should
// be restored.
if (fFuseBytes.getWrite() && !fNoWrite) {
arguments.add("-u");
}
// add the other options field
if (fOtherOptions.length() > 0) {
String[] options = fOtherOptions.split("\\s+");
for (String option : options) {
arguments.add(option);
}
}
return arguments;
}
/**
* Get the list of avrdude action options according to the current properties.
* <p>
* Currently the following actions are supported:
* <ul>
* <li>write flash image</li>
* <li>write eeprom image</li>
* <li>write fuse bytes</li>
* <li>write lockbits</li>
* </ul>
* Only for sections enabled with the <code>setWriteXXXX(true)</code> method will avrdude
* actions be created.
* </p>
* <p>
* Macros in the filenames for the flash and eeprom image files are not resolved. Use
* {@link #getActionArguments(IConfiguration, boolean)} to get the arguments with all macros
* resolved.
* </p>
* <p>
* This is a convenience method for <code>getArguments(buildcfg, true)</code>
* </p>
*
* @return <code>List<String></code> with avrdude action options.
*/
public List<String> getActionArguments(IConfiguration buildcfg) {
return getActionArguments(buildcfg, false);
}
public List<String> getActionArguments(IConfiguration buildcfg, boolean resolve) {
List<String> arguments = new ArrayList<String>();
AVRDudeAction action = null;
if (fWriteFlash) {
if (fFlashFromConfig) {
action = AVRDudeActionFactory.writeFlashAction(buildcfg);
} else {
action = AVRDudeActionFactory.writeFlashAction(fFlashFile);
}
if (action != null) {
String argument;
if (resolve) {
argument = action.getArgument(buildcfg);
} else {
argument = action.getArgument();
}
arguments.add(argument);
}
}
if (fWriteEEPROM) {
if (fEEPROMFromConfig) {
action = AVRDudeActionFactory.writeEEPROMAction(buildcfg);
} else {
action = AVRDudeActionFactory.writeEEPROMAction(fEEPROMFile);
}
if (action != null) {
String argument;
if (resolve) {
argument = action.getArgument(buildcfg);
} else {
argument = action.getArgument();
}
arguments.add(argument);
}
}
if (fFuseBytes.getWrite()) {
arguments.addAll(fFuseBytes.getArguments(fParent.getMCUId()));
}
if (fLockbits.getWrite()) {
arguments.addAll(fLockbits.getArguments(fParent.getMCUId()));
}
return arguments;
}
/**
* Load all options from the preferences.
*/
protected void loadData() {
fProgrammerId = fPrefs.get(KEY_PROGRAMMER, "");
fBitclock = fPrefs.get(KEY_BITCLOCK, DEFAULT_BITCLOCK);
fBitBangDelay = fPrefs.get(KEY_BITBANGDELAY, DEFAULT_BITBANGDELAY);
fNoSigCheck = fPrefs.getBoolean(KEY_NOSIGCHECK, DEFAULT_NOSIGCHECK);
fNoVerify = fPrefs.getBoolean(KEY_NOVERIFY, DEFAULT_NOVERIFY);
fNoWrite = fPrefs.getBoolean(KEY_NOWRITE, DEFAULT_NOWRITE);
fNoChipErase = fPrefs.getBoolean(KEY_NOCHIPERASE, DEFAULT_NOCHIPERASE);
fUseCounter = fPrefs.getBoolean(KEY_USECOUNTER, DEFAULT_USECOUNTER);
fWriteFlash = fPrefs.getBoolean(KEY_WRITEFLASH, DEFAULT_WRITEFLASH);
fFlashFromConfig = fPrefs.getBoolean(KEY_FLASHFROMCONFIG, DEFAULT_FLASHFROMCONFIG);
fFlashFile = fPrefs.get(KEY_FLASHFILE, DEFAULT_FLASHFILE);
fWriteEEPROM = fPrefs.getBoolean(KEY_WRITEEEPROM, DEFAULT_WRITEEEPROM);
fEEPROMFromConfig = fPrefs.getBoolean(KEY_EEPROMFROMCONFIG, DEFAULT_EEPROMFROMCONFIG);
fEEPROMFile = fPrefs.get(KEY_EEPROMFILE, DEFAULT_EEPROMFILE);
fFuseBytes = new FuseBytesProperties(fPrefs.node(NODE_FUSES), this);
fLockbits = new LockbitBytesProperties(fPrefs.node(NODE_LOCKS), this);
fOtherOptions = fPrefs.get(KEY_OTHEROPTIONS, DEFAULT_OTHEROPTIONS);
// fAVRDudeCalibration = new
// CalibrationBytes(fPrefs.node(NODE_CALIBRATION));
fDirty = false;
}
/**
* Save the modified properties to the persistent storage.
*
* @throws BackingStoreException
*/
public void save() throws BackingStoreException {
try {
if (fDirty) {
fDirty = false;
fPrefs.put(KEY_PROGRAMMER, fProgrammerId);
fPrefs.put(KEY_BITCLOCK, fBitclock);
fPrefs.put(KEY_BITBANGDELAY, fBitBangDelay);
fPrefs.putBoolean(KEY_NOSIGCHECK, fNoSigCheck);
fPrefs.putBoolean(KEY_NOVERIFY, fNoVerify);
fPrefs.putBoolean(KEY_NOWRITE, fNoWrite);
fPrefs.putBoolean(KEY_NOCHIPERASE, fNoChipErase);
fPrefs.putBoolean(KEY_USECOUNTER, fUseCounter);
fPrefs.putBoolean(KEY_WRITEFLASH, fWriteFlash);
fPrefs.putBoolean(KEY_FLASHFROMCONFIG, fFlashFromConfig);
fPrefs.put(KEY_FLASHFILE, fFlashFile);
fPrefs.putBoolean(KEY_WRITEEEPROM, fWriteEEPROM);
fPrefs.putBoolean(KEY_EEPROMFROMCONFIG, fEEPROMFromConfig);
fPrefs.put(KEY_EEPROMFILE, fEEPROMFile);
fPrefs.put(KEY_OTHEROPTIONS, fOtherOptions);
fPrefs.flush();
if (fProgrammer != null) {
ProgrammerConfigManager.getDefault().saveConfig(fProgrammer);
}
}
fFuseBytes.save();
fLockbits.save();
// fAVRDudeCalibration.save();
} catch (IllegalStateException ise) {
// This should not happen, but just in case we ignore this unchecked
// exception
ise.printStackTrace();
}
}
@Override
public String toString() {
StringBuffer sb = new StringBuffer();
sb.append(fDirty ? "*" : " ");
sb.append("[");
sb.append("ProgrammerID=" + fProgrammerId);
sb.append("]");
return sb.toString();
}
}