Main Page | Packages | Class Hierarchy | Class List | Directories | File List | Namespace Members | Class Members | File Members | Related Pages

Project.java

Go to the documentation of this file.
00001 /*
00002  * Project.java
00003  *
00004  * Copyright (C) 2005 Project SQUID, http://www.cs.helsinki.fi/group/squid/
00005  *
00006  * This file is part of Ikayaki.
00007  *
00008  * Ikayaki is free software; you can redistribute it and/or modify
00009  * it under the terms of the GNU General Public License as published by
00010  * the Free Software Foundation; either version 2 of the License, or
00011  * (at your option) any later version.
00012  *
00013  * Ikayaki is distributed in the hope that it will be useful,
00014  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00015  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
00016  * GNU General Public License for more details.
00017  *
00018  * You should have received a copy of the GNU General Public License
00019  * along with Ikayaki; if not, write to the Free Software
00020  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
00021  */
00022 
00023 package ikayaki;
00024 
00025 import ikayaki.squid.Squid;
00026 import ikayaki.util.DocumentUtilities;
00027 import ikayaki.util.LastExecutor;
00028 import org.w3c.dom.Document;
00029 import org.w3c.dom.Element;
00030 import org.w3c.dom.NodeList;
00031 
00032 import javax.swing.*;
00033 import javax.swing.event.EventListenerList;
00034 import javax.vecmath.Matrix3d;
00035 import javax.xml.parsers.DocumentBuilderFactory;
00036 import javax.xml.parsers.ParserConfigurationException;
00037 import java.io.*;
00038 import java.text.DecimalFormat;
00039 import java.text.DecimalFormatSymbols;
00040 import java.text.FieldPosition;
00041 import java.util.*;
00042 
00043 import static ikayaki.MeasurementStep.State.READY;
00044 import static java.lang.Math.sin;
00045 import static java.lang.Math.cos;
00046 import static ikayaki.MeasurementEvent.Type.*;
00047 import static ikayaki.MeasurementResult.Type.*;
00048 import static ikayaki.Project.Normalization.*;
00049 import static ikayaki.Project.Orientation.*;
00050 import static ikayaki.Project.State.*;
00051 import static ikayaki.Project.SampleType.*;
00052 import static ikayaki.Project.Type.*;
00053 import static ikayaki.ProjectEvent.Type.*;
00054 
00066 public class Project {
00067 
00068     private static final boolean DEBUG = false;      // TODO: used for testing the measurements without a Squid
00069 
00070     /* Property names for saving values to Project */
00071     public static final String MEASUREMENT_TYPE_PROPERTY = "measurementType";
00072     public static final String MEASUREMENT_TYPE_AUTO_VALUE = "AUTO";
00073     public static final String MEASUREMENT_TYPE_MANUAL_VALUE = "MANUAL";
00074     public static final String OPERATOR_PROPERTY = "operator";
00075     public static final String DATE_PROPERTY = "date";
00076     public static final String ROCK_TYPE_PROPERTY = "rockType";
00077     public static final String AREA_PROPERTY = "area";
00078     public static final String SITE_PROPERTY = "site";
00079     public static final String COMMENT_PROPERTY = "comment";
00080     public static final String LATITUDE_PROPERTY = "latitude";
00081     public static final String LONGITUDE_PROPERTY = "longitude";
00082 
00087     private static final Hashtable<File, Project> projectCache = new Hashtable<File, Project>();
00088 
00093     private static final Hashtable<File, Object> projectTypeCache = new Hashtable<File, Object>();
00094 
00098     private final File file;
00102     private final Type type;
00103 
00108     private State state = IDLE;
00109 
00113     private boolean closed = false;
00114 
00118     private Squid squid = null;
00119 
00124     private final Properties properties = new Properties();
00125 
00130     private MeasurementSequence sequence = new MeasurementSequence();   // this instance will be dumped by the constructor Project(File,Document)
00131 
00135     private double strike = 0.0;
00136 
00140     private double dip = 0.0;
00141 
00145     private SampleType sampleType = HAND;
00146 
00150     private Orientation orientation = MINUS_Z;
00151 
00155     private Normalization normalization = VOLUME;
00156 
00161     private Matrix3d transform = new Matrix3d();
00162 
00166     private double mass = -1.0;
00167 
00171     private double volume = -1.0;
00172 
00176     private double susceptibility = -1.0;
00177 
00181     private MeasurementStep currentStep = null;
00182 
00186     private final EventListenerList listenerList = new EventListenerList();
00187 
00191     private boolean modified = false;
00192 
00196     private final LastExecutor autosaveQueue = new LastExecutor(500, true);
00197 
00201     private Runnable autosaveRunnable = new Runnable() {
00202         public void run() {
00203             saveNow();
00204         }
00205     };
00206 
00214     public static Project createCalibrationProject(File file) {
00215         return createProject(file, CALIBRATION);
00216     }
00217 
00225     public static Project createAFProject(File file) {
00226         return createProject(file, AF);
00227     }
00228 
00236     public static Project createThellierProject(File file) {
00237         return createProject(file, THELLIER);
00238     }
00239 
00247     public static Project createThermalProject(File file) {
00248         return createProject(file, THERMAL);
00249     }
00250 
00260     public static synchronized Project createProject(File file, Type type) {
00261         if (file == null || type == null) {
00262             throw new NullPointerException();
00263         }
00264 
00265         // must use only canonical file paths. otherwise the cache could include the same file twise.
00266         try {
00267             file = file.getCanonicalFile();
00268         } catch (IOException e) {
00269             e.printStackTrace();
00270             return null;
00271         }
00272 
00273         // create a new file, do not overwrite an old one
00274         try {
00275             if (!file.createNewFile()) {
00276                 return null;
00277             }
00278         } catch (IOException e) {
00279             return null;
00280         }
00281 
00282         // create project, write to file and add to cache
00283         Project project = new Project(file, type);
00284         if (!project.saveNow()) {
00285             return null;
00286         }
00287         projectCache.put(file, project);
00288         projectTypeCache.put(file, project.getType());
00289         return project;
00290     }
00291 
00300     public static synchronized Project loadProject(File file) {
00301         if (file == null) {
00302             throw new NullPointerException();
00303         }
00304         if (!file.canRead() || !file.isFile()) {
00305             return null;
00306         }
00307 
00308         // must use only canonical file paths. otherwise the cache could include the same file twise.
00309         try {
00310             file = file.getCanonicalFile();
00311         } catch (IOException e) {
00312             e.printStackTrace();
00313             return null;
00314         }
00315 
00316         // check cache
00317         Project project = projectCache.get(file);
00318         if (project != null) {
00319             return project;
00320         }
00321 
00322         // load file and add to cache
00323         try {
00324             Document document = DocumentUtilities.loadFromXML(file);
00325             if (document == null) {
00326                 return null;
00327             }
00328             project = new Project(file, document);
00329             projectCache.put(file, project);
00330             projectTypeCache.put(file, project.getType());
00331 
00332         } catch (IllegalArgumentException e) {
00333             e.printStackTrace();
00334             return null;
00335         }
00336         return project;
00337     }
00338 
00352     public static synchronized boolean closeProject(Project project) {
00353         if (project == null) {
00354             throw new NullPointerException();
00355         }
00356 
00357         synchronized (project) {
00358             // save the project to file and remove it from cache
00359             if (project.isClosed()) {
00360                 new Exception("closeProject success: the project is already closed!").printStackTrace();
00361                 return true;
00362             }
00363             if (project.getState() != IDLE) {
00364                 System.err.println("closeProject failed: the project's state is " + project.getState());
00365                 return false;
00366             }
00367             if (!project.setSquid(null) || !project.saveNow()) {
00368                 System.err.println("closeProject failed: unable to detatch the squid or save the file");
00369                 return false;
00370             }
00371             projectCache.remove(project.getFile());
00372 
00373             // mark the project as closed
00374             project.closed = true;
00375             project.autosaveRunnable = new Runnable() {
00376                 public void run() {
00377                     assert false;
00378                     throw new IllegalStateException("Tried to save a closed project!");
00379                 }
00380             };
00381         }
00382         return true;
00383     }
00384 
00388     public static synchronized Project[] getCachedProjects() {
00389         return projectCache.values().toArray(new Project[0]);
00390     }
00391 
00401     public static Type getType(File file) {
00402         if (file == null) {
00403             throw new NullPointerException();
00404         }
00405 
00406         // check the cache
00407         Object value = projectTypeCache.get(file);
00408         if (value != null) {
00409             if (value instanceof Type) {
00410                 return (Type) value;
00411             } else {
00412                 return null;
00413             }
00414         }
00415         // NOTE: Should the cache be emptied at some point? I suppose not. In normal use it won't get very big.
00416 
00417         Type type = null;
00418         BufferedReader reader = null;
00419         try {
00420             reader = new BufferedReader(new FileReader(file));
00421 
00422             // check that it is a XML file
00423             String line = reader.readLine();
00424             if (line == null || line.indexOf("<?xml") < 0) {
00425                 return null;
00426             }
00427 
00428             // the second line of the file should be something like:
00429             // <project type="TYPE" version="1.0">
00430             line = reader.readLine();
00431             if (line == null) {
00432                 return null;
00433             }
00434             int start = line.indexOf("<project type=\"");
00435             if (start < 0) {
00436                 return null;
00437             }
00438             start += 15;
00439             int end = line.indexOf("\"", start);
00440             if (end < 0) {
00441                 return null;
00442             }
00443             type = Type.valueOf(line.substring(start, end));
00444         } catch (FileNotFoundException e) {
00445             e.printStackTrace();
00446         } catch (IOException e) {
00447             e.printStackTrace();
00448         } catch (IllegalArgumentException e) {
00449             e.printStackTrace();
00450         } finally {
00451 
00452             // close the file
00453             if (reader != null) {
00454                 try {
00455                     reader.close();
00456                 } catch (IOException e) {
00457                     e.printStackTrace();
00458                 }
00459             }
00460 
00461             // save the results to cache
00462             if (type != null) {
00463                 projectTypeCache.put(file, type);
00464             } else {
00465                 projectTypeCache.put(file, new Object());
00466             }
00467         }
00468         return type;
00469     }
00470 
00480     private Project(File file, Type type) {
00481         if (file == null || type == null) {
00482             throw new NullPointerException();
00483         }
00484         this.file = file.getAbsoluteFile();
00485         this.type = type;
00486         updateTransforms();
00487         modified = true;
00488     }
00489 
00500     private Project(File file, Document document) {
00501         if (file == null || document == null) {
00502             throw new NullPointerException();
00503         }
00504         this.file = file.getAbsoluteFile();
00505         String s = null;
00506 
00507         synchronized (this) {
00508             try {
00509 
00510                 // verify project file's version
00511                 Element root = document.getDocumentElement();
00512                 if (!root.getTagName().equals("project")) {
00513                     throw new IllegalArgumentException("Invalid tag name: " + root.getTagName());
00514                 }
00515                 String version = root.getAttribute("version");
00516 
00517                 if (version.equals("1.0")) {
00518 
00519                     /* Begin importing version 1.0 */
00520 
00521                     // get type
00522                     s = root.getAttribute("type");
00523                     try {
00524                         type = Type.valueOf(s);
00525                     } catch (IllegalArgumentException e) {
00526                         throw new IllegalArgumentException("Unknown project type: " + s, e);
00527                     }
00528 
00529                     // get properties element
00530                     NodeList propertiesList = root.getElementsByTagName("properties");
00531                     if (propertiesList.getLength() != 1) {
00532                         throw new IllegalArgumentException(
00533                                 "One properties required, found " + propertiesList.getLength());
00534                     }
00535                     Element properties = (Element) propertiesList.item(0);
00536 
00537                     // get default properties
00538                     s = properties.getAttribute("strike");
00539                     try {
00540                         strike = Double.parseDouble(s);
00541                     } catch (NumberFormatException e) {
00542                         throw new IllegalArgumentException("Invalid strike: " + s, e);
00543                     }
00544                     s = properties.getAttribute("dip");
00545                     try {
00546                         dip = Double.parseDouble(s);
00547                     } catch (NumberFormatException e) {
00548                         throw new IllegalArgumentException("Invalid dip: " + s, e);
00549                     }
00550                     s = properties.getAttribute("mass");
00551                     try {
00552                         mass = Double.parseDouble(s);
00553                     } catch (NumberFormatException e) {
00554                         throw new IllegalArgumentException("Invalid mass: " + s, e);
00555                     }
00556                     s = properties.getAttribute("volume");
00557                     try {
00558                         volume = Double.parseDouble(s);
00559                     } catch (NumberFormatException e) {
00560                         throw new IllegalArgumentException("Invalid volume: " + s, e);
00561                     }
00562                     s = properties.getAttribute("susceptibility");
00563                     try {
00564                         susceptibility = Double.parseDouble(s);
00565                     } catch (NumberFormatException e) {
00566                         throw new IllegalArgumentException("Invalid susceptibility: " + s, e);
00567                     }
00568                     s = properties.getAttribute("sampletype");
00569                     try {
00570                         sampleType = SampleType.valueOf(s);
00571                     } catch (IllegalArgumentException e) {
00572                         throw new IllegalArgumentException("Invalid sampletype: " + s, e);
00573                     }
00574                     s = properties.getAttribute("orientation");
00575                     try {
00576                         orientation = Orientation.valueOf(s);
00577                     } catch (IllegalArgumentException e) {
00578                         throw new IllegalArgumentException("Invalid orientation: " + s, e);
00579                     }
00580                     s = properties.getAttribute("normalization");
00581                     try {
00582                         normalization = Normalization.valueOf(s);
00583                     } catch (IllegalArgumentException e) {
00584                         throw new IllegalArgumentException("Invalid normalization: " + s, e);
00585                     }
00586 
00587                     // get custom properties
00588                     NodeList propertyList = properties.getElementsByTagName("property");
00589                     for (int i = 0; i < propertyList.getLength(); i++) {
00590                         Element property = (Element) propertyList.item(i);
00591                         this.properties.put(property.getAttribute("key"), property.getAttribute("value"));
00592                     }
00593 
00594                     // get sequence
00595                     NodeList sequenceList = root.getElementsByTagName("sequence");
00596                     if (sequenceList.getLength() != 1) {
00597                         throw new IllegalArgumentException("One sequence required, found " + sequenceList.getLength());
00598                     }
00599                     updateTransforms();     // transforms must be updated before running MeasurementSequence's constructor
00600                     sequence = new MeasurementSequence((Element) sequenceList.item(0), this);
00601 
00602                     // check from the measurement step's timestamps and states that the steps are in the right order
00603                     Date lastTimestamp = new Date(0);
00604                     MeasurementStep.State lastState = MeasurementStep.State.DONE;
00605                     for (int i = 0; i < sequence.getSteps(); i++) {
00606                         MeasurementStep step = sequence.getStep(i);
00607                         Date currentTimestamp = step.getTimestamp();
00608                         MeasurementStep.State currentState = step.getState();
00609 
00610                         // check the order of the timestamps
00611                         if (lastTimestamp != null && currentTimestamp != null
00612                                 && currentTimestamp.before(lastTimestamp)) {
00613                             throw new IllegalArgumentException("The timestamp of step " + i + " is too early.");
00614                         }
00615                         if (lastTimestamp == null && currentTimestamp != null) {
00616                             throw new IllegalArgumentException(
00617                                     "The non-null timestamp of step " + i + " follows a null timestamp.");
00618                         }
00619 
00620                         // check the order of the states
00621                         switch (currentState) {
00622                         case DONE_RECENTLY:
00623                         case MEASURING:
00624                             // the state of a just opened step can not be DONE_RECENTLY or MEASURING
00625                             throw new IllegalArgumentException("The state of step " + i + " is " + currentState);
00626                         case DONE:
00627                             if (lastState == READY) {
00628                                 throw new IllegalArgumentException("The state of step " + i + " is "
00629                                         + currentState + " after a " + READY);
00630                             }
00631                             break;
00632                         case READY:
00633                             // lastState is DONE or READY, so everything is OK
00634                             break;
00635                         default:
00636                             throw new IllegalArgumentException(
00637                                     "The step " + i + " has an unknown state: " + currentState);
00638                         }
00639 
00640                         lastTimestamp = currentTimestamp;
00641                         lastState = currentState;
00642                     }
00643 
00644                     /* End of importing version 1.0 */
00645 
00646 //              } else if (version.equals("x.y")) {
00647 //                  ... importing of file version x.y ...
00648                     /*
00649                      * FUTURE IMPLEMENTATION NOTE:
00650                      *
00651                      * It is recommended to import old versions so that the Document object is first modified to the
00652                      * format of the latest project file version, after which the importing of the latest version is
00653                      * used. This avoids the need to change the importing of older versions.
00654                      *
00655                      */
00656                 } else {
00657                     throw new IllegalArgumentException("Unknown version: " + version);
00658                 }
00659 
00660             } catch (RuntimeException e) {
00661                 /*
00662                  * Catch and rethrow any exceptions, so that the finally block would prevent
00663                  * the overwriting of a project file whose loading failed.
00664                  */
00665                 throw e;
00666             } finally {
00667                 modified = false;   // prevent the automatic save() operations that the importing created
00668             }
00669         }
00670     }
00671 
00677     public synchronized Document getDocument() {
00678         Document document = null;
00679         try {
00680             document = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
00681         } catch (ParserConfigurationException e) {
00682             return null;
00683         }
00684 
00685         // TODO: use a DTD for the document
00686 
00687         // create document's root element
00688         Element root = document.createElement("project");
00689         root.setAttribute("version", "1.0");
00690         root.setAttribute("type", type.name());
00691 
00692         // create default properties
00693         Element properties = document.createElement("properties");
00694         properties.setAttribute("strike", Double.toString(strike));
00695         properties.setAttribute("dip", Double.toString(dip));
00696         properties.setAttribute("mass", Double.toString(mass));
00697         properties.setAttribute("volume", Double.toString(volume));
00698         properties.setAttribute("susceptibility", Double.toString(susceptibility));
00699         properties.setAttribute("sampletype", sampleType.name());
00700         properties.setAttribute("orientation", orientation.name());
00701         properties.setAttribute("normalization", normalization.name());
00702 
00703         // create custom properties
00704         Set<Map.Entry<Object, Object>> entries = this.properties.entrySet();
00705         for (Map.Entry<Object, Object> entry : entries) {
00706             Element property = document.createElement("property");
00707             property.setAttribute("key", entry.getKey().toString());
00708             property.setAttribute("value", entry.getValue().toString());
00709             properties.appendChild(property);
00710         }
00711 
00712         // create sequence
00713         Element sequence = this.sequence.getElement(document);
00714 
00715         // put all together
00716         root.appendChild(properties);
00717         root.appendChild(sequence);
00718         document.appendChild(root);
00719         return document;
00720     }
00721 
00725     public synchronized boolean isModified() {
00726         return modified;
00727     }
00728 
00735     public synchronized void save() {
00736         if (isClosed()) {
00737             throw new IllegalStateException("The project is closed");
00738         }
00739         modified = true;
00740         autosaveQueue.execute(autosaveRunnable);
00741     }
00742 
00750     public boolean saveNow() {
00751         if (isClosed()) {
00752             throw new IllegalStateException("The project is closed");
00753         }
00754         File file;
00755         Document document;
00756         synchronized (this) {
00757             // clear any delaying autosave operations
00758             autosaveQueue.clear();
00759 
00760             // do not save if this has already been saved
00761             if (!isModified()) {
00762                 return true;
00763             }
00764             file = getFile();
00765             document = getDocument();
00766         }
00767         if (DocumentUtilities.storeToXML(file, document)) {
00768             modified = false;
00769             fireProjectEvent(FILE_SAVED);
00770             return true;
00771         } else {
00772             return false;
00773         }
00774     }
00775 
00784     private static String pad(String s, int length, int alignment) {
00785         while (s.length() < length) {
00786             if (alignment < 0) {
00787                 // left align
00788                 s = s + " ";
00789             } else if (alignment > 0) {
00790                 // right align
00791                 s = " " + s;
00792             } else {
00793                 // center
00794                 if (s.length() % 2 == 0) {
00795                     s = s + " ";
00796                 } else {
00797                     s = " " + s;
00798                 }
00799             }
00800         }
00801         return s;
00802     }
00803 
00811     public boolean exportToDAT(File file) {
00812         if (file == null) {
00813             throw new NullPointerException();
00814         }
00815         PrintStream out = null;
00816         try {
00817             out = new PrintStream(file, "ISO-8859-1");
00818             double d;
00819             Double dd;
00820             String s;
00821 
00822             // locales and formatters for numbers
00823             Locale locale = new Locale("en");
00824             DecimalFormat format2Frac = new DecimalFormat("###0.00", new DecimalFormatSymbols(locale));
00825             DecimalFormat format3Frac = new DecimalFormat("##0.000", new DecimalFormatSymbols(locale));
00826             DecimalFormat format5Numb = new DecimalFormat("###0.0000", new DecimalFormatSymbols(locale)) {
00827                 @Override public StringBuffer format(double number, StringBuffer result, FieldPosition fieldPosition) {
00828                     StringBuffer sb = super.format(number, result, fieldPosition);
00829                     if (number < 0) {
00830                         sb.delete(7, sb.length());
00831                     } else {
00832                         sb.delete(6, sb.length());
00833                     }
00834                     return sb;
00835                 }
00836             };
00837 
00838             // generic headers
00839             out.print(pad("SQUID", 11, -1));
00840             out.println(Ikayaki.APP_NAME + " " + Ikayaki.APP_VERSION);
00841 
00842             out.print(pad("Name", 10, -1));
00843             out.print(":");
00844             out.println(getName());
00845 
00846             out.print(pad("Rocktype", 10, -1));
00847             out.print(":");
00848             out.println(getProperty(ROCK_TYPE_PROPERTY, ""));
00849 
00850             out.print(pad("Site", 10, -1));
00851             out.print(":");
00852             s = getProperty(AREA_PROPERTY, "");
00853             if (s.length() > 0) {
00854                 s += "/";
00855             }
00856             s += getProperty(SITE_PROPERTY, "");
00857             out.println(s);
00858 
00859             out.print(pad("Sampletype", 10, -1));
00860             out.print(":");
00861             if (getSampleType() == CORE) {
00862                 out.println("core sample");
00863             } else {
00864                 out.println("hand sample");
00865             }
00866 
00867             out.print(pad("Comment", 10, -1));
00868             out.print(":");
00869             out.println(getProperty(COMMENT_PROPERTY, "").replaceAll("\\s", " "));
00870 
00871             // value headers
00872             String header = "";
00873             String values = "";
00874 
00875             try {
00876                 d = Double.parseDouble(getProperty(LATITUDE_PROPERTY, "0.0"));
00877             } catch (NumberFormatException e) {
00878                 d = 0.0;
00879             }
00880             header += pad("Lat ", 8, 1);
00881             values += pad(" " + format2Frac.format(d), 8, 1);
00882 
00883             try {
00884                 d = Double.parseDouble(getProperty(LONGITUDE_PROPERTY, "0.0"));
00885             } catch (NumberFormatException e) {
00886                 d = 0.0;
00887             }
00888             header += pad("Lon ", 8, 1);
00889             values += pad(" " + format2Frac.format(d), 8, 1);
00890 
00891             d = getStrike();
00892             header += pad("Str ", 8, 1);
00893             values += pad(" " + format2Frac.format(d), 8, 1);
00894 
00895             d = getDip();
00896             header += pad("Dip ", 8, 1);
00897             values += pad(" " + format2Frac.format(d), 8, 1);
00898 
00899             d = 0.0;
00900             header += pad("Bstr", 8, 1);
00901             values += pad(" " + format2Frac.format(d), 8, 1);
00902 
00903             d = 0.0;
00904             header += pad("Bdip", 8, 1);
00905             values += pad(" " + format2Frac.format(d), 8, 1);
00906 
00907             d = Math.min(getVolume(), 0.0);
00908             header += pad("Vol  ", 8, 1);
00909             values += pad(" " + format3Frac.format(d), 8, 1);
00910 
00911             d = Math.min(getMass(), 0.0);
00912             header += pad("Mass ", 8, 1);
00913             values += pad(" " + format3Frac.format(d), 8, 1);
00914 
00915             out.println(header);
00916             out.println(values);
00917 
00918             // measurement headers
00919             if (getType() == THELLIER || getType() == THERMAL) {
00920                 out.print("TH");
00921             } else {
00922                 out.print("AF");
00923             }
00924             out.println("      Dec    Inc       Int       Sus    T63       Xkomp      Ykomp      Zkomp");
00925 
00926             for (int i = 0; i < getCompletedSteps(); i++) {
00927                 MeasurementStep step = getStep(i);
00928 
00929                 // AF/TF
00930                 if (getType() == THELLIER || getType() == THERMAL) {
00931                     s = "" + Math.max((int) Math.round(step.getStepValue()), 0);
00932                 } else {
00933                     s = "" + Math.max((int) Math.round(step.getStepValue() * 10), 0);
00934                 }
00935                 out.print(pad(s, 4, 1));
00936 
00937                 // Dec
00938                 dd = MeasurementValue.DECLINATION.getValue(step);
00939                 d = dd != null ? dd : 0.0;
00940                 s = format2Frac.format(d);
00941                 out.print(pad(" " + s, 8, 1));
00942 
00943                 // Inc
00944                 dd = MeasurementValue.INCLINATION.getValue(step);
00945                 d = dd != null ? dd : 0.0;
00946                 s = format2Frac.format(d);
00947                 out.print(pad(" " + s, 7, 1));
00948 
00949                 // Int
00950                 dd = MeasurementValue.MAGNETIZATION.getValue(step);
00951                 d = dd != null ? dd : 0.0;
00952                 s = format5Numb.format(d);
00953                 out.print(pad(" " + s, 11, 1));
00954 
00955                 // Sus
00956                 d = step.getSusceptibility();
00957                 d = d >= 0.0 ? d : Math.max(getSusceptibility(), 0.0);
00958                 s = format5Numb.format(d);
00959                 out.print(pad(" " + s, 9, 1));
00960 
00961                 // T63
00962                 dd = MeasurementValue.THETA63.getValue(step);
00963                 d = dd != null ? dd : 0.0;
00964                 s = format2Frac.format(d);
00965                 out.print(pad(" " + s, 7, 1));
00966 
00967                 // Xkomp
00968                 dd = MeasurementValue.SAMPLE_X.getValue(step);
00969                 d = dd != null ? dd : 0.0;
00970                 s = format5Numb.format(d);
00971                 out.print(pad(" " + s, 11, 1));
00972 
00973                 // Ykomp
00974                 dd = MeasurementValue.SAMPLE_Y.getValue(step);
00975                 d = dd != null ? dd : 0.0;
00976                 s = format5Numb.format(d);
00977                 out.print(pad(" " + s, 11, 1));
00978 
00979                 // Zkomp
00980                 dd = MeasurementValue.SAMPLE_Z.getValue(step);
00981                 d = dd != null ? dd : 0.0;
00982                 s = format5Numb.format(d);
00983                 out.print(pad(" " + s, 11, 1));
00984 
00985                 out.println();
00986             }
00987 
00988             // exporting finished
00989             return true;
00990 
00991         } catch (FileNotFoundException e) {
00992             e.printStackTrace();
00993         } catch (UnsupportedEncodingException e) {
00994             e.printStackTrace();
00995         } finally {
00996             if (out != null) {
00997                 out.close();
00998             }
00999         }
01000         return false;
01001     }
01002 
01010     public boolean exportToSRM(File file) {
01011         if (file == null) {
01012             throw new NullPointerException();
01013         }
01014         return false; // TODO
01015     }
01016 
01024     public boolean exportToTDT(File file) {
01025         if (file == null) {
01026             throw new NullPointerException();
01027         }
01028         PrintStream out = null;
01029         try {
01030             out = new PrintStream(file, "ISO-8859-1");
01031             double d;
01032             Double dd;
01033             String s;
01034 
01035             // locales and formatters for numbers
01036             Locale locale = new Locale("en");
01037             DecimalFormat format0Frac = new DecimalFormat("######0", new DecimalFormatSymbols(locale));
01038             DecimalFormat format2Frac = new DecimalFormat("###0.00", new DecimalFormatSymbols(locale));
01039             DecimalFormat format3Frac = new DecimalFormat("##0.000", new DecimalFormatSymbols(locale));
01040 
01041             // begin header
01042             out.println("Thellier-tdt");
01043 
01044             // the applied field value in mT
01045             s = format3Frac.format(0.0);
01046             out.print(pad(s, Math.max(getName().length(), 8), 1));      // TODO: how should this be calculated?
01047 
01048             // strike
01049             s = format2Frac.format(getStrike());
01050             out.print(pad(" " + s, 8, 1));
01051 
01052             // dip
01053             s = format2Frac.format(getDip());
01054             out.print(pad(" " + s, 8, 1));
01055 
01056             // latitude
01057             try {
01058                 d = Double.parseDouble(getProperty(LATITUDE_PROPERTY, "0.0"));
01059             } catch (NumberFormatException e) {
01060                 d = 0.0;
01061             }
01062             s = format2Frac.format(d);
01063             out.print(pad(" " + s, 8, 1));
01064 
01065             // longitude
01066             try {
01067                 d = Double.parseDouble(getProperty(LONGITUDE_PROPERTY, "0.0"));
01068             } catch (NumberFormatException e) {
01069                 d = 0.0;
01070             }
01071             out.print(pad(format2Frac.format(d), 8, 1));
01072             out.println();      // end header
01073 
01074             for (int i = 0; i < getCompletedSteps(); i++) {
01075                 MeasurementStep step = getStep(i);
01076 
01077                 // specimen name
01078                 out.print(pad(getName(), 8, -1));
01079 
01080                 // temperatures and steps (magnetizing vs. demagnetizing)
01081                 d = Math.max(step.getStepValue(), 0.0);
01082                 if (getType() == THELLIER) {
01083                     // with Thellier the decimals are ".00", ".11", ".12", ".13" or ".14"
01084                     s = format2Frac.format(d);
01085                 } else {
01086                     // with Thermal the decimals are always ".00"
01087                     s = format0Frac.format(d) + ".00";
01088                 }
01089                 out.print(pad(" " + s, 8, 1));
01090 
01091                 // intensity
01092                 dd = MeasurementValue.MAGNETIZATION.getValue(step);
01093                 d = dd != null ? dd : 0.0;
01094                 s = format0Frac.format(d);
01095                 out.print(pad(" " + s, 8, 1));
01096 
01097                 // declination
01098                 dd = MeasurementValue.DECLINATION.getValue(step);
01099                 d = dd != null ? dd : 0.0;
01100                 s = format0Frac.format(d);
01101                 out.print(pad(" " + s, 8, 1));
01102 
01103                 // inclination
01104                 dd = MeasurementValue.INCLINATION.getValue(step);
01105                 d = dd != null ? dd : 0.0;
01106                 s = format0Frac.format(d);
01107                 out.print(pad(" " + s, 8, 1));
01108 
01109                 out.println();
01110             }
01111 
01112             // exporting finished
01113             return true;
01114 
01115         } catch (FileNotFoundException e) {
01116             e.printStackTrace();
01117         } catch (UnsupportedEncodingException e) {
01118             e.printStackTrace();
01119         } finally {
01120             if (out != null) {
01121                 out.close();
01122             }
01123         }
01124         return false;
01125     }
01126 
01130     public synchronized File getFile() {
01131         return file;
01132     }
01133 
01137     public synchronized Type getType() {
01138         return type;
01139     }
01140 
01144     public synchronized boolean isHolderCalibration() {
01145         if (getType() == CALIBRATION && getFile().equals(Settings.getHolderCalibrationFile())) {
01146             return true;
01147         } else {
01148             return false;
01149         }
01150     }
01151 
01157     public synchronized State getState() {
01158         return state;
01159     }
01160 
01166     private void setState(State state) {
01167         this.state = state;
01168         fireProjectEvent(STATE_CHANGED);
01169     }
01170 
01175     public boolean isClosed() {
01176         return closed;
01177     }
01178 
01182     public synchronized String getName() {
01183         String name = getFile().getName();
01184         if (name.endsWith(Ikayaki.FILE_TYPE)) {
01185             name = name.substring(0, name.length() - Ikayaki.FILE_TYPE.length());
01186         }
01187         return name;
01188     }
01189 
01196     public synchronized Date getTimestamp() {
01197         for (int i = sequence.getSteps() - 1; i >= 0; i--) {
01198             Date d = sequence.getStep(i).getTimestamp();
01199             if (d != null) {
01200                 return d;
01201             }
01202         }
01203         return null;
01204     }
01205 
01212     public synchronized Squid getSquid() {
01213         return squid;
01214     }
01215 
01228     public synchronized boolean setSquid(Squid squid) {
01229         // detach the squid from this project
01230         if (squid == null) {
01231             if (getSquid() == null) {
01232                 return true;        // already detached
01233             }
01234             if (getState() == IDLE && getSquid().setOwner(null)) {
01235                 this.squid = null;
01236                 fireProjectEvent(STATE_CHANGED);
01237                 return true;
01238             }
01239             return false;       // a measurement is running - can not detach
01240         }
01241 
01242         // attach the squid to this project
01243         synchronized (squid) {
01244             if (squid.getOwner() == this) {
01245                 return true;        // already attached
01246             }
01247             if (squid.getOwner() != null) {
01248                 squid.getOwner().setSquid(null);        // try to detach from the old project
01249             }
01250             if (squid.getOwner() == null && squid.setOwner(this)) {
01251                 this.squid = squid;
01252                 fireProjectEvent(STATE_CHANGED);
01253                 return true;
01254             }
01255             return false;       // the old project has a measurement running - can not attach to this one
01256         }
01257     }
01258 
01265     public synchronized String getProperty(String key) {
01266         return properties.getProperty(key);
01267     }
01268 
01276     public synchronized String getProperty(String key, String defaultValue) {
01277         return properties.getProperty(key, defaultValue);
01278     }
01279 
01286     public synchronized void setProperty(String key, String value) {
01287         properties.setProperty(key, value);
01288         save();
01289     }
01290 
01294     public synchronized double getStrike() {
01295         return strike;
01296     }
01297 
01301     public synchronized void setStrike(double strike) {
01302         this.strike = strike;
01303         updateTransforms();
01304         fireProjectEvent(DATA_CHANGED);
01305         save();
01306     }
01307 
01311     public synchronized double getDip() {
01312         return dip;
01313     }
01314 
01318     public synchronized void setDip(double dip) {
01319         this.dip = dip;
01320         updateTransforms();
01321         fireProjectEvent(DATA_CHANGED);
01322         save();
01323     }
01324 
01328     public synchronized SampleType getSampleType() {
01329         return sampleType;
01330     }
01331 
01337     public synchronized void setSampleType(SampleType sampleType) {
01338         if (sampleType == null) {
01339             throw new NullPointerException();
01340         }
01341         this.sampleType = sampleType;
01342         updateTransforms();
01343         fireProjectEvent(DATA_CHANGED);
01344         save();
01345     }
01346 
01350     public synchronized Orientation getOrientation() {
01351         return orientation;
01352     }
01353 
01359     public synchronized void setOrientation(Orientation orientation) {
01360         if (orientation == null) {
01361             throw new NullPointerException();
01362         }
01363         this.orientation = orientation;
01364         updateTransforms();
01365         fireProjectEvent(DATA_CHANGED);
01366         save();
01367     }
01368 
01372     public Normalization getNormalization() {
01373         return normalization;
01374     }
01375 
01381     public void setNormalization(Normalization normalization) {
01382         if (normalization == null) {
01383             throw new NullPointerException();
01384         }
01385         this.normalization = normalization;
01386         fireProjectEvent(DATA_CHANGED);
01387         save();
01388     }
01389 
01399     protected synchronized Matrix3d getTransform() {
01400         return transform;
01401     }
01402 
01407     private synchronized void updateTransforms() {
01408         double s;
01409         double d;
01410         s = Math.toRadians(getStrike());
01411         d = Math.toRadians(getDip());
01412 
01413         if (sampleType == CORE) {
01414             // core sample: sample -> geographic
01415             transform.setRow(0, sin(d) * cos(s), -sin(s), cos(s) * cos(d));
01416             transform.setRow(1, sin(s) * sin(d), cos(s), cos(d) * sin(s));
01417             transform.setRow(2, -cos(d), 0, sin(d));
01418         } else if (sampleType == HAND) {
01419             // hand sample: sample -> geographic
01420             transform.setRow(0, cos(s), -sin(s) * cos(d), sin(s) * sin(d));
01421             transform.setRow(1, sin(s), cos(s) * cos(d), -sin(d) * cos(s));
01422             transform.setRow(2, 0, sin(d), cos(d));
01423         } else {
01424             assert false;
01425         }
01426 
01427         for (int i = 0; i < sequence.getSteps(); i++) {
01428             sequence.getStep(i).updateTransforms();
01429         }
01430     }
01431 
01437     public synchronized double getMass() {
01438         return mass;
01439     }
01440 
01446     public synchronized void setMass(double mass) {
01447         if (mass < 0.0) {
01448             mass = -1.0;
01449         }
01450         this.mass = mass;
01451         fireProjectEvent(DATA_CHANGED);
01452         save();
01453     }
01454 
01460     public synchronized double getVolume() {
01461         return volume;
01462     }
01463 
01469     public synchronized void setVolume(double volume) {
01470         if (volume < 0.0) {
01471             volume = -1.0;
01472         }
01473         this.volume = volume;
01474         fireProjectEvent(DATA_CHANGED);
01475         save();
01476     }
01477 
01483     public synchronized double getSusceptibility() {
01484         return susceptibility;
01485     }
01486 
01492     public synchronized void setSusceptibility(double susceptibility) {
01493         if (susceptibility < 0.0) {
01494             susceptibility = -1.0;
01495         }
01496         this.susceptibility = susceptibility;
01497         fireProjectEvent(DATA_CHANGED);
01498         save();
01499     }
01500 
01506     public synchronized void addProjectListener(ProjectListener l) {
01507         listenerList.add(ProjectListener.class, l);
01508     }
01509 
01515     public synchronized void removeProjectListener(ProjectListener l) {
01516         listenerList.remove(ProjectListener.class, l);
01517     }
01518 
01524     protected synchronized void fireProjectEvent(ProjectEvent.Type type) {
01525         final ProjectEvent event = new ProjectEvent(this, type);
01526         final ProjectListener[] listeners = listenerList.getListeners(ProjectListener.class);
01527         SwingUtilities.invokeLater(new Runnable() {
01528             public void run() {
01529                 for (ProjectListener l : listeners) {
01530                     try {
01531                         l.projectUpdated(event);
01532                     } catch (Throwable t) {
01533                         t.printStackTrace();
01534                     }
01535                 }
01536             }
01537         });
01538     }
01539 
01545     public synchronized void addMeasurementListener(MeasurementListener l) {
01546         listenerList.add(MeasurementListener.class, l);
01547     }
01548 
01554     public synchronized void removeMeasurementListener(MeasurementListener l) {
01555         listenerList.remove(MeasurementListener.class, l);
01556     }
01557 
01564     protected synchronized void fireMeasurementEvent(MeasurementStep step, MeasurementEvent.Type type) {
01565         final MeasurementEvent event = new MeasurementEvent(this, step, type);
01566         final MeasurementListener[] listeners = listenerList.getListeners(MeasurementListener.class);
01567         SwingUtilities.invokeLater(new Runnable() {
01568             public void run() {
01569                 for (MeasurementListener l : listeners) {
01570                     try {
01571                         l.measurementUpdated(event);
01572                     } catch (Throwable t) {
01573                         t.printStackTrace();
01574                     }
01575                 }
01576             }
01577         });
01578     }
01579 
01590     public synchronized boolean addSequence(MeasurementSequence append) {
01591         if (append == null) {
01592             throw new NullPointerException();
01593         }
01594         if (!isSequenceEditEnabled()) {
01595             assert false;
01596             return false;   // TODO: maybe its better to throw an exception, so that it would be easier to find bugs
01597         }
01598         for (int i = 0; i < append.getSteps(); i++) {
01599             MeasurementStep step = new MeasurementStep(this);
01600             step.setStepValue(append.getStep(i).getStepValue());
01601             sequence.addStep(step);
01602         }
01603         fireProjectEvent(DATA_CHANGED);
01604         save();
01605         return true;
01606     }
01607 
01617     public synchronized MeasurementSequence copySequence(int start, int end) {
01618         if (start < 0 || end >= getSteps()) {
01619             throw new IndexOutOfBoundsException();
01620         }
01621         MeasurementSequence copy = new MeasurementSequence();
01622         for (int i = start; i <= end; i++) {
01623             MeasurementStep step = new MeasurementStep();
01624             step.setStepValue(sequence.getStep(i).getStepValue());
01625             copy.addStep(step);
01626         }
01627         return copy;
01628     }
01629 
01639     public synchronized MeasurementSequence copySequence(int... indices) {
01640         MeasurementSequence copy = new MeasurementSequence();
01641         for (int i : indices) {
01642             MeasurementStep step = new MeasurementStep();
01643             step.setStepValue(sequence.getStep(i).getStepValue());
01644             copy.addStep(step);
01645         }
01646         return copy;
01647     }
01648 
01657     public synchronized boolean addStep(MeasurementStep step) {
01658         if (step == null) {
01659             throw new NullPointerException();
01660         }
01661         double stepValue = step.getStepValue();
01662         step = new MeasurementStep(this);
01663         step.setStepValue(stepValue);
01664         sequence.addStep(step);
01665 
01666         fireProjectEvent(DATA_CHANGED);
01667         save();
01668         return true;
01669     }
01670 
01686     public synchronized boolean addStep(int index, MeasurementStep step) {
01687         if (step == null) {
01688             throw new NullPointerException();
01689         }
01690         if (index < getCompletedSteps() || index > getSteps()) {
01691             throw new IndexOutOfBoundsException();
01692         }
01693         if (!isSequenceEditEnabled()) {
01694             assert false;
01695             return false;    // TODO: maybe its better to throw an exception, so that it would be easier to find bugs
01696         }
01697         double stepValue = step.getStepValue();
01698         step = new MeasurementStep(this);
01699         step.setStepValue(stepValue);
01700         sequence.addStep(index, step);
01701 
01702         fireProjectEvent(DATA_CHANGED);
01703         save();
01704         return true;
01705     }
01706 
01717     public synchronized boolean removeStep(int index) {
01718         if (index < getCompletedSteps() || index >= getSteps()) {
01719             throw new IndexOutOfBoundsException();
01720         }
01721         if (!isSequenceEditEnabled()) {
01722             assert false;
01723             return false; // TODO: maybe its better to throw an exception, so that it would be easier to find bugs
01724         }
01725         sequence.removeStep(index);
01726 
01727         fireProjectEvent(DATA_CHANGED);
01728         save();
01729         return true;
01730     }
01731 
01743     public synchronized boolean removeStep(int start, int end) {
01744         if (start < getCompletedSteps() || end >= getSteps()) {
01745             throw new IndexOutOfBoundsException();
01746         }
01747         if (!isSequenceEditEnabled()) {
01748             assert false;
01749             return false;   // TODO: maybe its better to throw an exception, so that it would be easier to find bugs
01750         }
01751         for (int i = end; i >= start; i--) {
01752             sequence.removeStep(i);
01753         }
01754         fireProjectEvent(DATA_CHANGED);
01755         save();
01756         return true;
01757     }
01758 
01762     public synchronized int getSteps() {
01763         return sequence.getSteps();
01764     }
01765 
01770     public synchronized int getCompletedSteps() {
01771         int i;
01772         for (i = sequence.getSteps() - 1; i >= 0; i--) {
01773             MeasurementStep.State state = sequence.getStep(i).getState();
01774             if (state == READY) {
01775                 continue;
01776             } else {
01777                 break;
01778             }
01779         }
01780         return i + 1;
01781     }
01782 
01790     public synchronized MeasurementStep getStep(int index) {
01791         return sequence.getStep(index);
01792     }
01793 
01799     public synchronized MeasurementStep getCurrentStep() {
01800         return currentStep;
01801     }
01802 
01813     public synchronized <A> A getValue(int index, MeasurementValue<A> algorithm) {
01814         return algorithm.getValue(sequence.getStep(index));
01815     }
01816 
01824     private void runMeasurement() {
01825         if (getSquid() == null && !DEBUG) {
01826             throw new IllegalStateException("Unable to run measurement, squid is: " + getSquid());
01827         }
01828         if (getState() == IDLE) {
01829             throw new IllegalStateException("Unable to run measurement, state is: " + getState());
01830         }
01831 
01832         try {
01833             if (DEBUG) {
01834                 synchronized (DummyMeasurement.class) {
01835                     new DummyMeasurement().run();
01836                 }
01837             } else {
01838                 synchronized (Measurement.class) {
01839                     new Measurement().run();
01840                 }
01841             }
01842         } catch (Exception e) {
01843             e.printStackTrace();
01844         }
01845         setState(IDLE);
01846     }
01847 
01852     public synchronized boolean isDegaussingEnabled() {
01853         if (type == CALIBRATION || type == THELLIER || type == THERMAL) {
01854             return false;
01855         } else if (type == AF) {
01856             return true;
01857         } else {
01858             return false;
01859         }
01860     }
01861 
01866     public synchronized boolean isSequenceEditEnabled() {
01867         if (type == CALIBRATION) {
01868             return false;
01869         } else if (type == AF || type == THELLIER || type == THERMAL) {
01870             return true;
01871         } else {
01872             return false;
01873         }
01874     }
01875 
01880     public synchronized boolean isManualControlEnabled() {
01881         if (getSquid() == null) {
01882             return false;
01883         }
01884         if (getState() == IDLE) {
01885             return true;
01886         } else {
01887             return false;
01888         }
01889     }
01890 
01895     public synchronized boolean isAutoStepEnabled() {
01896         if (getSquid() == null && !DEBUG) {
01897             return false;
01898         }
01899         if (type == CALIBRATION || type == THELLIER || type == THERMAL) {
01900             return false;
01901         } else if (type == AF) {
01902             if (getState() == IDLE) {
01903                 return true;
01904             } else {
01905                 return false;
01906             }
01907         } else {
01908             return false;
01909         }
01910     }
01911 
01916     public synchronized boolean isSingleStepEnabled() {
01917         if (getSquid() == null && !DEBUG) {
01918             return false;
01919         }
01920         if (type == CALIBRATION || type == AF || type == THELLIER || type == THERMAL) {
01921             if (getState() == IDLE) {
01922                 return true;
01923             } else {
01924                 return false;
01925             }
01926         } else {
01927             return false;
01928         }
01929     }
01930 
01935     public synchronized boolean isPauseEnabled() {
01936         if (type == CALIBRATION || type == THELLIER || type == THERMAL) {
01937             return false;
01938         } else if (type == AF) {
01939             if (getState() == MEASURING) {
01940                 return true;
01941             } else {
01942                 return false;
01943             }
01944         } else {
01945             return false;
01946         }
01947     }
01948 
01953     public synchronized boolean isAbortEnabled() {
01954         if (getState() == MEASURING || getState() == PAUSED) {
01955             return true;
01956         } else {
01957             return false;
01958         }
01959     }
01960 
01970     public synchronized boolean doAutoStep() {
01971         if (getSquid() == null && !DEBUG) {
01972             return false;
01973         }
01974         if (getState() == IDLE) {
01975             if (isAutoStepEnabled()) {
01976                 setState(MEASURING);
01977             } else if (isSingleStepEnabled()) {
01978                 setState(PAUSED);
01979             } else {
01980                 return false;
01981             }
01982 
01983             // if there are no unmeasured steps, add one for a measurement without demagnetization
01984             if (getSteps() == getCompletedSteps()) {
01985                 addStep(new MeasurementStep(this));
01986             }
01987 
01988             new Thread() {
01989                 @Override public void run() {
01990                     runMeasurement();
01991                 }
01992             }.start();
01993             return true;
01994         } else {
01995             return false;
01996         }
01997     }
01998 
02006     public synchronized boolean doSingleStep() {
02007         if (!isSingleStepEnabled()) {
02008             return false;
02009         }
02010 
02011         if (doAutoStep()) {
02012             return doPause();
02013         } else {
02014             return false;
02015         }
02016     }
02017 
02027     public synchronized boolean doPause() {
02028 //        if (!isPauseEnabled()) {
02029 //            return false; // will cause problems when singlestepping non-AF projects
02030 //        }
02031         if (getState() == IDLE) {
02032             return false;
02033         } else if (getState() == MEASURING) {
02034             setState(PAUSED);
02035             return true;
02036         } else if (getState() == PAUSED) {
02037             return true;
02038         } else {
02039             return false;
02040         }
02041     }
02042 
02051     public synchronized boolean doAbort() {
02052         if (!isAbortEnabled()) {
02053             return false;
02054         }
02055         if (getState() == IDLE) {
02056             return false;
02057         } else {
02058             setState(ABORTED);
02059             return true;
02060         }
02061     }
02062 
02071     private synchronized boolean doManualMove(ManualMovePosition position) {
02072         if (!isManualControlEnabled()) {
02073             return false;
02074         }
02075         setState(PAUSED);
02076         new Thread(new ManualMove(position)).start();
02077         return true;
02078     }
02079 
02087     public synchronized boolean doManualMoveDegausserY() {
02088         return doManualMove(ManualMovePosition.DEGAUSSER_Y);
02089     }
02090 
02098     public synchronized boolean doManualMoveDegausserZ() {
02099         return doManualMove(ManualMovePosition.DEGAUSSER_Z);
02100     }
02101 
02109     public synchronized boolean doManualMoveBackground() {
02110         return doManualMove(ManualMovePosition.BACKGROUND);
02111     }
02112 
02120     public synchronized boolean doManualMoveMeasurement() {
02121         return doManualMove(ManualMovePosition.MEASUREMENT);
02122     }
02123 
02132     public synchronized boolean doManualMoveHome() {
02133         return doManualMove(ManualMovePosition.HOME);
02134     }
02135 
02144     public synchronized boolean doManualMoveRightLimit() {
02145         return doManualMove(ManualMovePosition.RIGHT_LIMIT);
02146     }
02147 
02156     public synchronized boolean doManualMoveLeftLimit() {
02157         return doManualMove(ManualMovePosition.LEFT_LIMIT);
02158     }
02159 
02160 
02169     public synchronized boolean doManualRotate(int angle) {
02170         if (!isManualControlEnabled()) {
02171             return false;
02172         }
02173         setState(PAUSED);
02174         new Thread(new ManualRotate(angle)).start();
02175         return true;
02176     }
02177 
02186     public synchronized boolean doManualMeasure() {
02187         if (!isManualControlEnabled()) {
02188             return false;
02189         }
02190         setState(PAUSED);
02191         new Thread(new ManualMeasure()).start();
02192         return true;
02193     }
02194 
02195     // TODO: is this comment even close?
02203     public synchronized boolean doManualReset() {
02204         if (!isManualControlEnabled()) {
02205             return false;
02206         }
02207         setState(PAUSED);
02208         new Thread(new Runnable() {
02209             public void run() {
02210                 if (getSquid() == null) {
02211                     throw new IllegalStateException();
02212                 }
02213                 // TODO: these should be in one Magnetometer method
02214                 getSquid().getMagnetometer().pulseReset('A');
02215                 getSquid().getMagnetometer().clearFlux('A');
02216                 setState(IDLE);
02217             }
02218         }).start();
02219         return true;
02220     }
02221 
02231     public synchronized boolean doManualDemagZ(double amplitude) {
02232         if (!isManualControlEnabled()) {
02233             return false;
02234         }
02235         setState(PAUSED);
02236         new Thread(new ManualDemag(ManualDemagAxel.Z, amplitude)).start();
02237         return true;
02238     }
02239 
02249     public synchronized boolean doManualDemagY(double amplitude) {
02250         if (!isManualControlEnabled()) {
02251             return false;
02252         }
02253         setState(PAUSED);
02254         new Thread(new ManualDemag(ManualDemagAxel.Y, amplitude)).start();
02255         return true;
02256     }
02257 
02265     public synchronized boolean doManualStepDone() {
02266         if (!isManualControlEnabled()) {
02267             return false;
02268         }
02269         if (currentStep != null) {
02270             currentStep.setDone();
02271             fireMeasurementEvent(currentStep, STEP_END);
02272             currentStep = null;
02273         }
02274         return true;
02275     }
02276 
02280     public enum Type {
02281         CALIBRATION("Calibration"), AF("AF"), THELLIER("Thellier"), THERMAL("Thermal");
02282 
02283         private String name;
02284 
02285         private Type(String name) {
02286             this.name = name;
02287         }
02288 
02289         @Override public String toString() {
02290             return name;
02291         }
02292     }
02293 
02297     public enum State {
02298         IDLE, MEASURING, PAUSED, ABORTED
02299     }
02300 
02304     public enum SampleType {
02305         CORE, HAND
02306     }
02307 
02311     public enum Orientation {
02312         PLUS_Z, MINUS_Z
02313     }
02314 
02318     public enum Normalization {
02319         VOLUME, MASS
02320     }
02321 
02326     private class Measurement implements Runnable {
02327         public void run() {
02328             if (getState() == IDLE) {
02329                 throw new IllegalStateException();
02330             }
02331             if (getSquid() == null) {
02332                 throw new IllegalStateException();
02333             }
02334 
02335             for (int i = getCompletedSteps(); i < getSteps(); i++) {
02336 
02337                 // begin measuring the first uncomplete step
02338                 currentStep = getStep(i);
02339                 currentStep.setMeasuring();
02340                 fireMeasurementEvent(currentStep, STEP_START);
02341 
02342                 try {
02343                     // reset the equipment
02344                     if (getSquid().getHandler().getRotation() != 0) {
02345                         getSquid().getHandler().rotateTo(0);
02346                         getSquid().getHandler().join();
02347                     }
02348                     checkAborted();
02349 
02350                     // demagnetizing
02351                     if (currentStep.getStepValue() > 0.05 && isDegaussingEnabled()) {
02352 
02353                         // demagnetize Z
02354                         getSquid().getHandler().moveToDegausserZ();
02355                         fireMeasurementEvent(currentStep, HANDLER_MOVE);
02356                         getSquid().getHandler().join();
02357                         fireMeasurementEvent(currentStep, HANDLER_STOP);
02358                         checkAborted();
02359                         fireMeasurementEvent(currentStep, DEMAGNETIZE_START);
02360                         if (!getSquid().getDegausser().demagnetizeZ(currentStep.getStepValue())) {
02361                             throw new InterruptedException("demagnetizeZ = false");
02362                         }
02363                         fireMeasurementEvent(currentStep, DEMAGNETIZE_END);
02364                         checkAborted();
02365 
02366                         // demagnetize Y
02367                         getSquid().getHandler().moveToDegausserY();
02368                         fireMeasurementEvent(currentStep, HANDLER_MOVE);
02369                         getSquid().getHandler().join();
02370                         fireMeasurementEvent(currentStep, HANDLER_STOP);
02371                         checkAborted();
02372                         fireMeasurementEvent(currentStep, DEMAGNETIZE_START);
02373                         if (!getSquid().getDegausser().demagnetizeY(currentStep.getStepValue())) {
02374                             throw new InterruptedException("demagnetizeY = false");
02375                         }
02376                         fireMeasurementEvent(currentStep, DEMAGNETIZE_END);
02377                         checkAborted();
02378 
02379                         // demagnetize X
02380                         getSquid().getHandler().rotateTo(90);
02381                         fireMeasurementEvent(currentStep, HANDLER_ROTATE);
02382                         getSquid().getHandler().join();
02383                         fireMeasurementEvent(currentStep, HANDLER_STOP);
02384                         checkAborted();
02385                         fireMeasurementEvent(currentStep, DEMAGNETIZE_START);
02386                         if (!getSquid().getDegausser().demagnetizeY(currentStep.getStepValue())) {
02387                             throw new InterruptedException("demagnetizeY = false");
02388                         }
02389                         fireMeasurementEvent(currentStep, DEMAGNETIZE_END);
02390                         checkAborted();
02391                         getSquid().getHandler().rotateTo(0);
02392                         fireMeasurementEvent(currentStep, HANDLER_ROTATE);
02393                         getSquid().getHandler().join();
02394                         fireMeasurementEvent(currentStep, HANDLER_STOP);
02395                         checkAborted();
02396                     }
02397 
02398                     // measure first background noise
02399                     getSquid().getHandler().moveToBackground();
02400                     fireMeasurementEvent(currentStep, HANDLER_MOVE);
02401                     getSquid().getHandler().join();
02402                     fireMeasurementEvent(currentStep, HANDLER_STOP);
02403                     checkAborted();
02404                     // Begin by pulsing feedback loop for each axis And by clearing flux counter for each axis
02405                     // TODO: these should be in one Magnetometer method
02406                     getSquid().getMagnetometer().pulseReset('A');
02407                     getSquid().getMagnetometer().clearFlux('A');
02408                     double[] results = getSquid().getMagnetometer().readData();
02409                     currentStep.addResult(new MeasurementResult(NOISE, 0, results[0], results[1], results[2]));
02410                     fireMeasurementEvent(currentStep, VALUE_MEASURED);
02411                     checkAborted();
02412 
02413                     // begin measuring the sample
02414                     getSquid().getHandler().moveToMeasurement();
02415                     fireMeasurementEvent(currentStep, HANDLER_MOVE);
02416                     getSquid().getHandler().join();
02417                     fireMeasurementEvent(currentStep, HANDLER_STOP);
02418                     checkAborted();
02419 
02420                     // measure with the set amount of handler rotations
02421                     int rotations = Settings.getMeasurementRotations();
02422                     if (rotations == 0) {
02423 
02424                         // quick measure with no rotations
02425                         results = getSquid().getMagnetometer().readData();
02426                         currentStep.addResult(new MeasurementResult(SAMPLE, 0, results[0], results[1], results[2]));
02427                         fireMeasurementEvent(currentStep, VALUE_MEASURED);
02428                         checkAborted();
02429 
02430                     } else {
02431 
02432                         // accurate measure with rotations
02433                         for (int j = 0; j < rotations; j++) {
02434 
02435                             // measure at 0 degrees
02436                             results = getSquid().getMagnetometer().readData();
02437                             currentStep.addResult(new MeasurementResult(SAMPLE, 0, results[0], results[1], results[2]));
02438                             fireMeasurementEvent(currentStep, VALUE_MEASURED);
02439                             checkAborted();
02440 
02441                             // measure at 90 degrees
02442                             getSquid().getHandler().rotateTo(90);
02443                             fireMeasurementEvent(currentStep, HANDLER_ROTATE);
02444                             getSquid().getHandler().join();
02445                             fireMeasurementEvent(currentStep, HANDLER_STOP);
02446                             checkAborted();
02447                             results = getSquid().getMagnetometer().readData();
02448                             currentStep.addResult(
02449                                     new MeasurementResult(SAMPLE, 90, results[0], results[1], results[2]));
02450                             fireMeasurementEvent(currentStep, VALUE_MEASURED);
02451                             checkAborted();
02452 
02453                             // measure at 180 degrees
02454                             getSquid().getHandler().rotateTo(180);
02455                             fireMeasurementEvent(currentStep, HANDLER_ROTATE);
02456                             getSquid().getHandler().join();
02457                             fireMeasurementEvent(currentStep, HANDLER_STOP);
02458                             checkAborted();
02459                             results = getSquid().getMagnetometer().readData();
02460                             currentStep.addResult(
02461                                     new MeasurementResult(SAMPLE, 180, results[0], results[1], results[2]));
02462                             fireMeasurementEvent(currentStep, VALUE_MEASURED);
02463                             checkAborted();
02464 
02465                             // measure at 270 degrees
02466                             getSquid().getHandler().rotateTo(270);
02467                             fireMeasurementEvent(currentStep, HANDLER_ROTATE);
02468                             getSquid().getHandler().join();
02469                             fireMeasurementEvent(currentStep, HANDLER_STOP);
02470                             checkAborted();
02471                             results = getSquid().getMagnetometer().readData();
02472                             currentStep.addResult(
02473                                     new MeasurementResult(SAMPLE, 270, results[0], results[1], results[2]));
02474                             fireMeasurementEvent(currentStep, VALUE_MEASURED);
02475                             checkAborted();
02476 
02477                             // rotate the handler back to 0 degrees
02478                             getSquid().getHandler().rotateTo(0);
02479                             fireMeasurementEvent(currentStep, HANDLER_ROTATE);
02480                             getSquid().getHandler().join();
02481                             fireMeasurementEvent(currentStep, HANDLER_STOP);
02482                             checkAborted();
02483                         }
02484                     }
02485 
02486                     // measure second background noise
02487                     getSquid().getHandler().moveToBackground();
02488                     fireMeasurementEvent(currentStep, HANDLER_MOVE);
02489                     getSquid().getHandler().join();
02490                     fireMeasurementEvent(currentStep, HANDLER_STOP);
02491                     checkAborted();
02492                     results = getSquid().getMagnetometer().readData();
02493                     currentStep.addResult(new MeasurementResult(NOISE, 0, results[0], results[1], results[2]));
02494                     fireMeasurementEvent(currentStep, VALUE_MEASURED);
02495                     checkAborted();
02496 
02497                 } catch (InterruptedException e) {
02498 
02499                     // the measurement was aborted or some error occurred
02500                     if (getState() == ABORTED) {
02501                         System.err.println(e.getMessage());
02502                     } else {
02503                         e.printStackTrace();
02504                     }
02505                 } catch (IllegalStateException e) {
02506                     e.printStackTrace();
02507                 } finally {
02508 
02509                     // complete the step
02510                     currentStep.setDone();
02511                     fireMeasurementEvent(currentStep, STEP_END);
02512                     currentStep = null;
02513                 }
02514 
02515                 if (getState() == PAUSED || getState() == ABORTED) {
02516                     setState(IDLE);
02517                     return;
02518                 }
02519             }
02520             setState(IDLE);
02521         }
02522 
02529         private void checkAborted() throws InterruptedException {
02530             if (getState() == ABORTED) {
02531                 fireMeasurementEvent(currentStep, STEP_ABORTED);
02532                 throw new InterruptedException("Measurement aborted");
02533             }
02534         }
02535     }
02536 
02541     private class DummyMeasurement implements Runnable {
02542         public void run() {
02543             if (getState() == IDLE) {
02544                 throw new IllegalStateException();
02545             }
02546 
02547             System.out.println("Measurement started");
02548             for (int i = getCompletedSteps(); i < getSteps(); i++) {
02549 
02550                 System.out.println("Measuring step " + i + "...");
02551                 currentStep = getStep(i);
02552                 currentStep.setMeasuring();
02553                 fireMeasurementEvent(currentStep, STEP_START);
02554 
02555                 try {
02556                     // measure BG (1)
02557                     Thread.sleep(500);
02558                     if (getState() == ABORTED) {
02559                         System.out.println("Measurement aborted");
02560                         setState(IDLE);
02561                         return;
02562                     }
02563                     currentStep.addResult(new MeasurementResult(NOISE, 0,
02564                             Math.random() * 0.000001, Math.random() * 0.000001, Math.random() * 0.000001));
02565                     System.out.println("Result added");
02566                     fireMeasurementEvent(currentStep, VALUE_MEASURED);
02567 
02568                     // measure DEG0
02569                     Thread.sleep(500);
02570                     if (getState() == ABORTED) {
02571                         System.out.println("Measurement aborted");
02572                         setState(IDLE);
02573                         currentStep.setDone();
02574                         return;
02575                     }
02576                     currentStep.addResult(new MeasurementResult(SAMPLE, 0,
02577                             Math.random() * 0.0001, Math.random() * 0.0001, Math.random() * 0.0001));
02578                     System.out.println("Result added");
02579                     fireMeasurementEvent(currentStep, VALUE_MEASURED);
02580 
02581                     // measure DEG90
02582                     Thread.sleep(500);
02583                     if (getState() == ABORTED) {
02584                         System.out.println("Measurement aborted");
02585                         setState(IDLE);
02586                         currentStep.setDone();
02587                         return;
02588                     }
02589                     currentStep.addResult(new MeasurementResult(SAMPLE, 90,
02590                             Math.random() * 0.0001, Math.random() * 0.0001, Math.random() * 0.0001));
02591                     System.out.println("Result added");
02592                     fireMeasurementEvent(currentStep, VALUE_MEASURED);
02593 
02594                     // measure DEG180
02595                     Thread.sleep(500);
02596                     if (getState() == ABORTED) {
02597                         System.out.println("Measurement aborted");
02598                         setState(IDLE);
02599                         currentStep.setDone();
02600                         return;
02601                     }
02602                     currentStep.addResult(new MeasurementResult(SAMPLE, 180,
02603                             Math.random() * 0.0001, Math.random() * 0.0001, Math.random() * 0.0001));
02604                     System.out.println("Result added");
02605                     fireMeasurementEvent(currentStep, VALUE_MEASURED);
02606 
02607                     // measure DEG270
02608                     Thread.sleep(500);
02609                     if (getState() == ABORTED) {
02610                         System.out.println("Measurement aborted");
02611                         setState(IDLE);
02612                         currentStep.setDone();
02613                         return;
02614                     }
02615                     currentStep.addResult(new MeasurementResult(SAMPLE, 270,
02616                             Math.random() * 0.0001, Math.random() * 0.0001, Math.random() * 0.0001));
02617                     System.out.println("Result added");
02618                     fireMeasurementEvent(currentStep, VALUE_MEASURED);
02619 
02620                     // measure BG (2)
02621                     Thread.sleep(500);
02622                     if (getState() == ABORTED) {
02623                         System.out.println("Measurement aborted");
02624                         setState(IDLE);
02625                         currentStep.setDone();
02626                         return;
02627                     }
02628                     currentStep.addResult(new MeasurementResult(NOISE, 0,
02629                             Math.random() * 0.000001, Math.random() * 0.000001, Math.random() * 0.000001));
02630                     System.out.println("Result added");
02631                     fireMeasurementEvent(currentStep, VALUE_MEASURED);
02632 
02633                 } catch (InterruptedException e) {
02634                     e.printStackTrace();
02635                 } finally {
02636                     // complete step
02637                     currentStep.setDone();
02638                     System.out.println("Step " + i + " completed");
02639                     fireMeasurementEvent(currentStep, STEP_END);
02640                     currentStep = null;
02641                 }
02642 
02643                 if (getState() == PAUSED) {
02644                     System.out.println("Measurement ended (paused)");
02645                     setState(IDLE);
02646                     return;
02647                 }
02648             }
02649             System.out.println("Measurement ended");
02650             setState(IDLE);
02651         }
02652     }
02653 
02654     private enum ManualMovePosition {
02655         DEGAUSSER_Y,
02656         DEGAUSSER_Z,
02657         BACKGROUND,
02658         MEASUREMENT,
02659         HOME,
02660         RIGHT_LIMIT,
02661         LEFT_LIMIT
02662     }
02663 
02667     private class ManualMove implements Runnable {
02668 
02669         private ManualMovePosition pos;
02670 
02671         public ManualMove(ManualMovePosition pos) {
02672             this.pos = pos;
02673         }
02674 
02675         public void run() {
02676             if (getState() == IDLE) {
02677                 throw new IllegalStateException();
02678             }
02679             if (getSquid() == null) {
02680                 throw new IllegalStateException();
02681             }
02682 
02683             switch (pos) {
02684             case DEGAUSSER_Y:
02685                 getSquid().getHandler().moveToDegausserY();
02686                 break;
02687             case DEGAUSSER_Z:
02688                 getSquid().getHandler().moveToDegausserZ();
02689                 break;
02690             case BACKGROUND:
02691                 getSquid().getHandler().moveToBackground();
02692                 break;
02693             case MEASUREMENT:
02694                 getSquid().getHandler().moveToMeasurement();
02695                 break;
02696             case HOME:
02697                 getSquid().getHandler().moveToSampleLoad();
02698                 break;
02699             case RIGHT_LIMIT:
02700                 getSquid().getHandler().moveToRightLimit();
02701                 break;
02702             case LEFT_LIMIT:
02703                 getSquid().getHandler().moveToLeftLimit();
02704                 break;
02705             default:
02706                 System.err.println("Invalid pos: " + pos);
02707                 assert false;
02708                 break;
02709             }
02710             fireMeasurementEvent(null, HANDLER_MOVE);
02711 
02712             try {
02713                 getSquid().getHandler().join();
02714             } catch (InterruptedException e) {
02715                 e.printStackTrace();
02716             }
02717             fireMeasurementEvent(null, HANDLER_STOP);
02718             setState(IDLE);
02719         }
02720     }
02721 
02725     private class ManualRotate implements Runnable {
02726 
02727         private int angle;
02728 
02729         public ManualRotate(int angle) {
02730             this.angle = angle;
02731         }
02732 
02733         public void run() {
02734             if (getState() == IDLE) {
02735                 throw new IllegalStateException();
02736             }
02737             if (getSquid() == null) {
02738                 throw new IllegalStateException();
02739             }
02740 
02741             getSquid().getHandler().rotateTo(angle);
02742             fireMeasurementEvent(null, HANDLER_ROTATE);
02743 
02744             try {
02745                 getSquid().getHandler().join();
02746             } catch (InterruptedException e) {
02747                 e.printStackTrace();
02748             }
02749             fireMeasurementEvent(null, HANDLER_STOP);
02750 
02751             setState(IDLE);
02752         }
02753     }
02754 
02759     private class ManualMeasure implements Runnable {
02760         public void run() {
02761             if (getState() == IDLE) {
02762                 throw new IllegalStateException();
02763             }
02764             if (getSquid() == null) {
02765                 throw new IllegalStateException();
02766             }
02767 
02768             // select the first unfinished step or add a new one
02769             if (currentStep == null) {
02770                 int i = getCompletedSteps();
02771                 if (i == getSteps()) {
02772                     addStep(new MeasurementStep(Project.this));
02773                 }
02774                 currentStep = getStep(i);
02775                 currentStep.setMeasuring();
02776                 fireMeasurementEvent(currentStep, STEP_START);
02777             }
02778 
02779             double[] results = getSquid().getMagnetometer().readData();
02780 
02781             // check where we are
02782             MeasurementResult.Type resultType;
02783             int rotation;
02784             if (getSquid().getHandler().getPosition() == Settings.getHandlerMeasurementPosition()) {
02785                 resultType = SAMPLE;
02786                 rotation = getSquid().getHandler().getRotation();
02787             } else {
02788                 resultType = NOISE;
02789                 rotation = 0;
02790             }
02791             currentStep.addResult(new MeasurementResult(resultType, rotation, results[0], results[1], results[2]));
02792             fireMeasurementEvent(currentStep, VALUE_MEASURED);
02793 
02794             setState(IDLE);
02795         }
02796     }
02797 
02798     private enum ManualDemagAxel {
02799         Z,
02800         Y
02801     }
02802 
02807     private class ManualDemag implements Runnable {
02808 
02809         private ManualDemagAxel axel;
02810         private double amplitude;
02811 
02812         public ManualDemag(ManualDemagAxel axel, double amplitude) {
02813             if (axel == null) {
02814                 throw new NullPointerException();
02815             }
02816             this.axel = axel;
02817             this.amplitude = amplitude;
02818         }
02819 
02820         public void run() {
02821             if (getState() == IDLE) {
02822                 throw new IllegalStateException("getState() == IDLE");
02823             }
02824             if (getSquid() == null) {
02825                 throw new IllegalStateException("getSquid() == null");
02826             }
02827             if (!isDegaussingEnabled()) {
02828                 throw new IllegalStateException("!isDegaussingEnabled()");
02829             }
02830 
02831             amplitude = Math.max(amplitude, Settings.getDegausserMinimumField());
02832             amplitude = Math.min(amplitude, Settings.getDegausserMaximumField());
02833 
02834             // finish any previous measurement
02835             if (currentStep != null) {
02836                 currentStep.setDone();
02837                 fireMeasurementEvent(currentStep, STEP_END);
02838                 currentStep = null;
02839             }
02840 
02841             // select the first unfinished step or add a new one
02842             int i = getCompletedSteps();
02843             if (i == getSteps()) {
02844                 addStep(new MeasurementStep(Project.this));
02845             }
02846             currentStep = getStep(i);
02847             currentStep.setStepValue(amplitude);
02848             currentStep.setMeasuring();
02849             fireMeasurementEvent(currentStep, STEP_START);
02850 
02851             // demagnetize the sample
02852             fireMeasurementEvent(currentStep, DEMAGNETIZE_START);
02853             switch (axel) {
02854             case Y:
02855                 getSquid().getDegausser().demagnetizeY(amplitude);
02856                 break;
02857             case Z:
02858                 getSquid().getDegausser().demagnetizeZ(amplitude);
02859                 break;
02860             default:
02861                 System.err.println("Invalid axel: " + axel);
02862                 assert false;
02863                 break;
02864             }
02865             fireMeasurementEvent(currentStep, DEMAGNETIZE_END);
02866 
02867             setState(IDLE);
02868         }
02869     }
02870 }

Generated on Fri May 6 16:00:33 2005 for Squid by  doxygen 1.4.1